In the code that I will give you, I want you to implement double buffering for the framebuffer of my 3D rasterizer in C. That way we will write the whole frame in one pass or call.
```c
/* 
   Simple 3D Rasterizer in Pure C with Lighting System
   - Revised to render using the Linux framebuffer instead of the terminal.
   - Renders triangles directly to the framebuffer with proper scaling.
   - Supports perspective projection with a field of view (FOV) of 90 degrees.
   - Allows global and local transformations (translation, rotation, scaling) for triangles.
   - Includes camera transformations (translation and rotation).
   - Continuously rotates the first triangle around its Z-axis in a loop.
   - Implements a basic lighting system based on distance from a light source.

   Compile with:
       gcc -o rasterizer_fb rasterizer_fb.c -lm
*/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <unistd.h>     // For usleep()
#include <fcntl.h>      // For open()
#include <sys/mman.h>   // For mmap()
#include <linux/fb.h>   // For framebuffer information
#include <sys/ioctl.h>  // For ioctl()
#include <stdint.h>     // For fixed-width integer types
#include <signal.h>     // For signal handling

/* Default Framebuffer path */
#define FRAMEBUFFER_DEVICE "/dev/fb0"

/* Type Definitions */
typedef unsigned char u8_t;
typedef float f32_t;
typedef int i32_t;
typedef unsigned int u32_t;

typedef struct { u8_t r, g, b; } col_t;
typedef struct { f32_t x, y, z; } vec3_t;
typedef struct { f32_t x, y, z, w; } vec4_t;
typedef struct { f32_t m[4][4]; } mat4x4_t;

typedef struct { vec3_t p; col_t c; } vert_t;
typedef struct { vert_t a, b, c; } tri_t;
typedef struct { i32_t x, y; f32_t z; } screen_vert_t;
typedef struct { screen_vert_t a, b, c; } screen_tri_t;

/* Camera Structure */
typedef struct {
    vec3_t position;
    f32_t pitch; // Rotation around X-axis (in radians)
    f32_t yaw;   // Rotation around Y-axis (in radians)
    f32_t roll;  // Rotation around Z-axis (in radians)
} camera_t;

/* Light Structure */
typedef struct {
    vec3_t position;    // Position of the light in world space
    col_t color;        // Color of the light
    f32_t intensity;    // Intensity of the light
    f32_t range;        // Range of the light
} light_t;

/* Framebuffer Information Structure */
typedef struct {
    int fb_fd;                 // File descriptor for framebuffer
    struct fb_var_screeninfo vinfo; // Variable screen information
    struct fb_fix_screeninfo finfo; // Fixed screen information
    long int screensize;       // Size of the framebuffer memory
    uint8_t *fbp;              // Pointer to framebuffer memory
} framebuffer_t;

/* Global Framebuffer Structure */
framebuffer_t framebuffer = {0};

/* Screen dimensions for rasterizer (logical resolution) */
#define LOGICAL_WIDTH  1024   // Adjust to desired resolution
#define LOGICAL_HEIGHT 768

/* Color Depth Information */
#define BITS_PER_PIXEL 32     // Assuming 32 bits per pixel (RGBA)

/* Depth range */
#define ZNEAR 0.1f     // Near clipping plane
#define ZFAR 1000.0f   // Far clipping plane

/* Field of view in degrees */
#define FOV 90.0f

/* Aspect ratio correction */
#define ASPECT_RATIO_CORRECTION 1.0f   // Updated for pixel-based rendering

/* Precompute FOV scaling factor */
#define FOV_RAD (FOV * (M_PI / 180.0f))
#define F_SCALE (1.0f / tanf(FOV_RAD / 2.0f))

/* Function Prototypes */

/* Framebuffer Operations */
int init_framebuffer(framebuffer_t *fb);
void close_framebuffer(framebuffer_t *fb);
void put_pixel(framebuffer_t *fb, int x, int y, col_t color);

/* Matrix Operations */
void mat4x4_identity(mat4x4_t *mat);
void mat4x4_multiply(mat4x4_t *result, const mat4x4_t *a, const mat4x4_t *b);
void mat4x4_translate(mat4x4_t *mat, const vec3_t *t);
void mat4x4_rotate_x(mat4x4_t *mat, f32_t angle);
void mat4x4_rotate_y(mat4x4_t *mat, f32_t angle);
void mat4x4_rotate_z(mat4x4_t *mat, f32_t angle);
void mat4x4_scale(mat4x4_t *mat, const vec3_t *s);
vec3_t mat4x4_multiply_vec3(const mat4x4_t *mat, const vec3_t *v);

/* Transformation Functions for Triangles */
void translate_triangle(tri_t *tri, vec3_t t);
void rotate_triangle(tri_t *tri, f32_t angle_x, f32_t angle_y, f32_t angle_z);
void rotate_triangle_deg(tri_t *tri, f32_t angle_x_deg, f32_t angle_y_deg, f32_t angle_z_deg);
void scale_triangle(tri_t *tri, const vec3_t s);
vec3_t compute_centroid(const tri_t *tri);
void rotate_triangle_local(tri_t *tri, f32_t angle_x, f32_t angle_y, f32_t angle_z);
void rotate_triangle_local_deg(tri_t *tri, f32_t angle_x_deg, f32_t angle_y_deg, f32_t angle_z_deg);
void scale_triangle_local(tri_t *tri, const vec3_t s);
void scale_triangle_local_uniform(tri_t *tri, f32_t scale_factor);

/* Camera Transformation Functions */
void translate_camera(camera_t *cam, const vec3_t *t);
void rotate_camera(camera_t *cam, f32_t angle_x, f32_t angle_y, f32_t angle_z);
void rotate_camera_deg(camera_t *cam, f32_t angle_x_deg, f32_t angle_y_deg, f32_t angle_z_deg);
vec3_t apply_camera_transform(const camera_t *cam, const vec3_t *p);
screen_vert_t project_vertex(const camera_t *cam, const vec3_t *p_world);

/* Projection and Rasterization */
void compute_barycentric(int px, int py, screen_tri_t tri, f32_t *u, f32_t *v, f32_t *w);
void rasterize_triangle(const tri_t *tri, const camera_t *cam, const vec3_t *light_cam_space_pos, col_t *fb, f32_t *zb_buffer, f32_t *distance_buffer);

/* Rendering Functions */
void clear_fb_buffer(col_t *fb, f32_t *zb_buffer, f32_t *distance_buffer);
void render_fb_distance(const col_t *fb, const float *distance_buffer, const light_t *light, framebuffer_t *fb_device);

/* Helper Functions */
f32_t deg_to_rad(f32_t degrees);

/* Signal Handler for Cleanup */
void handle_signal(int sig) {
    printf("\nReceived signal %d, exiting...\n", sig);
    close_framebuffer(&framebuffer);
    exit(0);
}

/* Function Implementations */

/* Helper function to convert degrees to radians */
f32_t deg_to_rad(f32_t degrees) {
    return degrees * (M_PI / 180.0f);
}

/* Framebuffer Initialization */
int init_framebuffer(framebuffer_t *fb) {
    /* Open the framebuffer device */
    fb->fb_fd = open(FRAMEBUFFER_DEVICE, O_RDWR);
    if (fb->fb_fd == -1) {
        perror("Error: cannot open framebuffer device");
        return -1;
    }

    /* Get fixed screen information */
    if (ioctl(fb->fb_fd, FBIOGET_FSCREENINFO, &fb->finfo) == -1) {
        perror("Error reading fixed information");
        close(fb->fb_fd);
        return -1;
    }

    /* Get variable screen information */
    if (ioctl(fb->fb_fd, FBIOGET_VSCREENINFO, &fb->vinfo) == -1) {
        perror("Error reading variable information");
        close(fb->fb_fd);
        return -1;
    }

    /* Calculate the size of the screen in bytes */
    fb->screensize = fb->vinfo.yres_virtual * fb->finfo.line_length;

    /* Map the framebuffer device to memory */
    fb->fbp = (uint8_t *)mmap(0, fb->screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fb->fb_fd, 0);
    if ((int)fb->fbp == -1) {
        perror("Error: failed to map framebuffer device to memory");
        close(fb->fb_fd);
        return -1;
    }

    /* Clear the framebuffer */
    memset(fb->fbp, 0, fb->screensize);

    return 0;
}

/* Close Framebuffer */
void close_framebuffer(framebuffer_t *fb) {
    if (fb->fbp && fb->fbp != MAP_FAILED) {
        munmap(fb->fbp, fb->screensize);
    }
    if (fb->fb_fd != -1) {
        close(fb->fb_fd);
    }
}

/* Function to put a pixel on the framebuffer */
void put_pixel(framebuffer_t *fb, int x, int y, col_t color) {
    /* Ensure coordinates are within screen bounds */
    if (x < 0 || x >= fb->vinfo.xres || y < 0 || y >= fb->vinfo.yres) {
        return;
    }

    /* Calculate the location in the framebuffer memory */
    long location = (x + fb->vinfo.xoffset) * (fb->vinfo.bits_per_pixel / 8) +
                    (y + fb->vinfo.yoffset) * fb->finfo.line_length;

    /* Depending on bits_per_pixel, format the color appropriately */
    if (fb->vinfo.bits_per_pixel == 32) { // Assuming ARGB
        *(fb->fbp + location)     = color.b;        // Blue
        *(fb->fbp + location + 1) = color.g;        // Green
        *(fb->fbp + location + 2) = color.r;        // Red
        *(fb->fbp + location + 3) = 0x00;           // Padding or Alpha
    } else if (fb->vinfo.bits_per_pixel == 24) { // Assuming RGB
        *(fb->fbp + location)     = color.b;
        *(fb->fbp + location + 1) = color.g;
        *(fb->fbp + location + 2) = color.r;
    }
    // Add more formats if necessary
}

/* Matrix Operations Implementations */

void mat4x4_identity(mat4x4_t *mat) {
    memset(mat, 0, sizeof(mat4x4_t));
    for(int i = 0; i < 4; i++) {
        mat->m[i][i] = 1.0f;
    }
}

void mat4x4_multiply(mat4x4_t *result, const mat4x4_t *a, const mat4x4_t *b) {
    mat4x4_t temp;
    for(int i = 0; i < 4; i++) {
        for(int j = 0; j < 4; j++) {
            temp.m[i][j] = 0.0f;
            for(int k = 0; k < 4; k++) {
                temp.m[i][j] += a->m[i][k] * b->m[k][j];
            }
        }
    }
    *result = temp;
}

void mat4x4_translate(mat4x4_t *mat, const vec3_t *t) {
    mat4x4_identity(mat);
    mat->m[0][3] = t->x;
    mat->m[1][3] = t->y;
    mat->m[2][3] = t->z;
}

void mat4x4_rotate_x(mat4x4_t *mat, f32_t angle) {
    mat4x4_identity(mat);
    f32_t c = cosf(angle);
    f32_t s = sinf(angle);
    mat->m[1][1] = c;
    mat->m[1][2] = -s;
    mat->m[2][1] = s;
    mat->m[2][2] = c;
}

void mat4x4_rotate_y(mat4x4_t *mat, f32_t angle) {
    mat4x4_identity(mat);
    f32_t c = cosf(angle);
    f32_t s = sinf(angle);
    mat->m[0][0] = c;
    mat->m[0][2] = s;
    mat->m[2][0] = -s;
    mat->m[2][2] = c;
}

void mat4x4_rotate_z(mat4x4_t *mat, f32_t angle) {
    mat4x4_identity(mat);
    f32_t c = cosf(angle);
    f32_t s = sinf(angle);
    mat->m[0][0] = c;
    mat->m[0][1] = -s;
    mat->m[1][0] = s;
    mat->m[1][1] = c;
}

void mat4x4_scale(mat4x4_t *mat, const vec3_t *s) {
    mat4x4_identity(mat);
    mat->m[0][0] = s->x;
    mat->m[1][1] = s->y;
    mat->m[2][2] = s->z;
}

vec3_t mat4x4_multiply_vec3(const mat4x4_t *mat, const vec3_t *v) {
    vec4_t result;
    result.x = mat->m[0][0]*v->x + mat->m[0][1]*v->y + mat->m[0][2]*v->z + mat->m[0][3]*1.0f;
    result.y = mat->m[1][0]*v->x + mat->m[1][1]*v->y + mat->m[1][2]*v->z + mat->m[1][3]*1.0f;
    result.z = mat->m[2][0]*v->x + mat->m[2][1]*v->y + mat->m[2][2]*v->z + mat->m[2][3]*1.0f;
    result.w = mat->m[3][0]*v->x + mat->m[3][1]*v->y + mat->m[3][2]*v->z + mat->m[3][3]*1.0f;
    
    // Perform perspective divide if w is not 1
    if(result.w != 0.0f && result.w != 1.0f) {
        result.x /= result.w;
        result.y /= result.w;
        result.z /= result.w;
    }
    
    vec3_t final = { result.x, result.y, result.z };
    return final;
}

/* Transformation Functions for Triangles */

/* Function to translate a triangle globally */
void translate_triangle(tri_t *tri, vec3_t t) {
    tri->a.p.x += t.x;
    tri->a.p.y += t.y;
    tri->a.p.z += t.z;

    tri->b.p.x += t.x;
    tri->b.p.y += t.y;
    tri->b.p.z += t.z;

    tri->c.p.x += t.x;
    tri->c.p.y += t.y;
    tri->c.p.z += t.z;
}

/* Function to compute the normal vector of a triangle */
vec3_t compute_normal(const tri_t *tri) {
    // Calculate vectors AB and AC
    vec3_t ab = { tri->b.p.x - tri->a.p.x, tri->b.p.y - tri->a.p.y, tri->b.p.z - tri->a.p.z };
    vec3_t ac = { tri->c.p.x - tri->a.p.x, tri->c.p.y - tri->a.p.y, tri->c.p.z - tri->a.p.z };

    // Cross product AB x AC
    vec3_t cross = {
        ab.y * ac.z - ab.z * ac.y,
        ab.z * ac.x - ab.x * ac.z,
        ab.x * ac.y - ab.y * ac.x
    };

    // Normalize the vector
    f32_t length = sqrtf(cross.x * cross.x + cross.y * cross.y + cross.z * cross.z);
    if(length == 0.0f) return (vec3_t){0.0f, 0.0f, 0.0f};
    cross.x /= length;
    cross.y /= length;
    cross.z /= length;

    return cross;
}

/* Function to compute the centroid of a triangle */
vec3_t compute_centroid(const tri_t *tri) {
    vec3_t centroid;
    centroid.x = (tri->a.p.x + tri->b.p.x + tri->c.p.x) / 3.0f;
    centroid.y = (tri->a.p.y + tri->b.p.y + tri->c.p.y) / 3.0f;
    centroid.z = (tri->a.p.z + tri->b.p.z + tri->c.p.z) / 3.0f;
    return centroid;
}

/* Function to rotate a triangle globally using radians */
void rotate_triangle(tri_t *tri, f32_t angle_x, f32_t angle_y, f32_t angle_z) {
    mat4x4_t rot_x, rot_y, rot_z, temp, rot_total;

    /* Rotate around X-axis */
    mat4x4_rotate_x(&rot_x, angle_x);
    
    /* Rotate around Y-axis */
    mat4x4_rotate_y(&rot_y, angle_y);
    
    /* Rotate around Z-axis */
    mat4x4_rotate_z(&rot_z, angle_z);
    
    /* Combine rotations: Rz * Ry * Rx */
    mat4x4_multiply(&temp, &rot_y, &rot_x);
    mat4x4_multiply(&rot_total, &rot_z, &temp);
    
    /* Apply rotation to each vertex */
    tri->a.p = mat4x4_multiply_vec3(&rot_total, &tri->a.p);
    tri->b.p = mat4x4_multiply_vec3(&rot_total, &tri->b.p);
    tri->c.p = mat4x4_multiply_vec3(&rot_total, &tri->c.p);
}

/* Function to rotate a triangle globally using degrees */
void rotate_triangle_deg(tri_t *tri, f32_t angle_x_deg, f32_t angle_y_deg, f32_t angle_z_deg) {
    f32_t angle_x_rad = deg_to_rad(angle_x_deg);
    f32_t angle_y_rad = deg_to_rad(angle_y_deg);
    f32_t angle_z_rad = deg_to_rad(angle_z_deg);
    rotate_triangle(tri, angle_x_rad, angle_y_rad, angle_z_rad);
}

/* Function to rotate a triangle around its centroid using radians */
void rotate_triangle_local(tri_t *tri, f32_t angle_x, f32_t angle_y, f32_t angle_z) {
    // Step 1: Compute the centroid
    vec3_t centroid = compute_centroid(tri);
    
    // Step 2: Translate the triangle so that the centroid is at the origin
    translate_triangle(tri, (vec3_t){ -centroid.x, -centroid.y, -centroid.z });
    
    // Step 3: Apply rotation
    rotate_triangle(tri, angle_x, angle_y, angle_z);
    
    // Step 4: Translate the triangle back to its original position
    translate_triangle(tri, centroid);
}

/* Function to rotate a triangle around its centroid using degrees */
void rotate_triangle_local_deg(tri_t *tri, f32_t angle_x_deg, f32_t angle_y_deg, f32_t angle_z_deg) {
    f32_t angle_x_rad = deg_to_rad(angle_x_deg);
    f32_t angle_y_rad = deg_to_rad(angle_y_deg);
    f32_t angle_z_rad = deg_to_rad(angle_z_deg);
    rotate_triangle_local(tri, angle_x_rad, angle_y_rad, angle_z_rad);
}

/* Function to scale a triangle globally */
void scale_triangle(tri_t *tri, const vec3_t s) {
    tri->a.p.x *= s.x;
    tri->a.p.y *= s.y;
    tri->a.p.z *= s.z;

    tri->b.p.x *= s.x;
    tri->b.p.y *= s.y;
    tri->b.p.z *= s.z;

    tri->c.p.x *= s.x;
    tri->c.p.y *= s.y;
    tri->c.p.z *= s.z;
}

/* Function to scale a triangle relative to its centroid using scaling factors */
void scale_triangle_local(tri_t *tri, const vec3_t s) {
    // Step 1: Compute the centroid
    vec3_t centroid = compute_centroid(tri);
    
    // Step 2: Translate the triangle so that the centroid is at the origin
    translate_triangle(tri, (vec3_t){ -centroid.x, -centroid.y, -centroid.z });
    
    // Step 3: Apply scaling
    scale_triangle(tri, s);
    
    // Step 4: Translate the triangle back to its original position
    translate_triangle(tri, centroid);
}

/* Function to scale a triangle uniformly relative to its centroid */
void scale_triangle_local_uniform(tri_t *tri, f32_t scale_factor) {
    vec3_t scale = { scale_factor, scale_factor, scale_factor };
    scale_triangle_local(tri, scale);
}

/* Camera Transformation Functions */

/* Function to translate the camera */
void translate_camera(camera_t *cam, const vec3_t *t) {
    cam->position.x += t->x;
    cam->position.y += t->y;
    cam->position.z += t->z;
}

/* Function to rotate the camera using radians */
void rotate_camera(camera_t *cam, f32_t angle_x, f32_t angle_y, f32_t angle_z) {
    cam->pitch += angle_x;
    cam->yaw   += angle_y;
    cam->roll  += angle_z;
}

/* Function to rotate the camera using degrees */
void rotate_camera_deg(camera_t *cam, f32_t angle_x_deg, f32_t angle_y_deg, f32_t angle_z_deg) {
    f32_t angle_x_rad = deg_to_rad(angle_x_deg);
    f32_t angle_y_rad = deg_to_rad(angle_y_deg);
    f32_t angle_z_rad = deg_to_rad(angle_z_deg);
    rotate_camera(cam, angle_x_rad, angle_y_rad, angle_z_rad);
}

/* Function to apply camera transformation to a point (world space to camera space) */
vec3_t apply_camera_transform(const camera_t *cam, const vec3_t *p) {
    // Translate
    vec3_t translated = {
        p->x - cam->position.x,
        p->y - cam->position.y,
        p->z - cam->position.z
    };

    // Apply inverse rotations (pitch, yaw, roll)
    // Rotation order: Roll -> Pitch -> Yaw
    // Inverse rotations: Negate the angles

    // Rotate around Z-axis (Roll)
    f32_t cos_z = cosf(-cam->roll);
    f32_t sin_z = sinf(-cam->roll);
    f32_t x1 = translated.x * cos_z - translated.y * sin_z;
    f32_t y1 = translated.x * sin_z + translated.y * cos_z;
    f32_t z1 = translated.z;

    // Rotate around X-axis (Pitch)
    f32_t cos_x = cosf(-cam->pitch);
    f32_t sin_x = sinf(-cam->pitch);
    f32_t x2 = x1;
    f32_t y2 = y1 * cos_x - z1 * sin_x;
    f32_t z2 = y1 * sin_x + z1 * cos_x;

    // Rotate around Y-axis (Yaw)
    f32_t cos_y = cosf(-cam->yaw);
    f32_t sin_y = sinf(-cam->yaw);
    f32_t x3 = x2 * cos_y + z2 * sin_y;
    f32_t y3 = y2;
    f32_t z3 = -x2 * sin_y + z2 * cos_y;

    vec3_t final = { x3, y3, z3 };
    return final;
}

/* Projection Function: Project a 3D point to 2D screen space using perspective projection and camera transform */
screen_vert_t project_vertex(const camera_t *cam, const vec3_t *p_world) {
    screen_vert_t sv;
    
    // Transform the point to camera space
    vec3_t p_cam = apply_camera_transform(cam, p_world);
    
    // Prevent division by zero and handle points behind the camera
    if (p_cam.z <= ZNEAR) {
        p_cam.z = ZNEAR;
    }

    // Apply perspective projection
    f32_t x_proj = (p_cam.x * F_SCALE) / p_cam.z;
    f32_t y_proj = (p_cam.y * F_SCALE) / p_cam.z;

    // Adjust for aspect ratio correction
    y_proj /= ASPECT_RATIO_CORRECTION;

    // Map normalized device coordinates [-1, 1] to screen coordinates [0, WIDTH] and [0, HEIGHT]
    // Assuming LOGICAL_WIDTH x LOGICAL_HEIGHT as screen space
    sv.x = (int)((x_proj + 1.0f) * 0.5f * LOGICAL_WIDTH);
    sv.y = (int)((1.0f - (y_proj + 1.0f) * 0.5f) * LOGICAL_HEIGHT);
    sv.z = p_cam.z;

    return sv;
}

/* Function to compute barycentric coordinates */
void compute_barycentric(int px, int py, screen_tri_t tri, f32_t *u, f32_t *v, f32_t *w) {
    f32_t denom = (f32_t)((tri.b.y - tri.c.y)*(tri.a.x - tri.c.x) + (tri.c.x - tri.b.x)*(tri.a.y - tri.c.y));
    if (fabsf(denom) < 1e-6f) { // Degenerate triangle
        *u = *v = *w = -1.0f;
        return;
    }
    *u = ((tri.b.y - tri.c.y)*(px - tri.c.x) + (tri.c.x - tri.b.x)*(py - tri.c.y)) / denom;
    *v = ((tri.c.y - tri.a.y)*(px - tri.c.x) + (tri.a.x - tri.c.x)*(py - tri.c.y)) / denom;
    *w = 1.0f - (*u) - (*v);
}

/* Rasterization Function: Rasterize a single triangle with lighting based on light distance */
void rasterize_triangle(const tri_t *tri, const camera_t *cam, const vec3_t *light_cam_space_pos, col_t *fb_buffer, f32_t *zb_buffer, f32_t *distance_buffer) {
    // Project vertices to screen space
    screen_tri_t st = {
        .a = project_vertex(cam, &tri->a.p),
        .b = project_vertex(cam, &tri->b.p),
        .c = project_vertex(cam, &tri->c.p)
    };

    // Compute bounding box
    int minX = st.a.x < st.b.x ? (st.a.x < st.c.x ? st.a.x : st.c.x) : (st.b.x < st.c.x ? st.b.x : st.c.x);
    int maxX = st.a.x > st.b.x ? (st.a.x > st.c.x ? st.a.x : st.c.x) : (st.b.x > st.c.x ? st.b.x : st.c.x);
    int minY = st.a.y < st.b.y ? (st.a.y < st.c.y ? st.a.y : st.c.y) : (st.b.y < st.c.y ? st.b.y : st.c.y);
    int maxY = st.a.y > st.b.y ? (st.a.y > st.c.y ? st.a.y : st.c.y) : (st.b.y > st.c.y ? st.b.y : st.c.y);

    // Clamp to screen dimensions
    if(minX < 0) minX = 0;
    if(maxX >= LOGICAL_WIDTH) maxX = LOGICAL_WIDTH -1;
    if(minY < 0) minY = 0;
    if(maxY >= LOGICAL_HEIGHT) maxY = LOGICAL_HEIGHT -1;

    // Iterate over the bounding box
    for(int y = minY; y <= maxY; y++) {
        for(int x = minX; x <= maxX; x++) {
            f32_t u, v, w;
            compute_barycentric(x + 0.5f, y + 0.5f, st, &u, &v, &w);

            // Check if inside the triangle
            if(u >= 0.0f && v >= 0.0f && w >= 0.0f) {
                // Interpolate depth (z)
                f32_t depth = u * st.a.z + v * st.b.z + w * st.c.z;

                // Z-buffer test
                int index = y * LOGICAL_WIDTH + x;
                if(depth < zb_buffer[index]) {
                    zb_buffer[index] = depth;

                    // Interpolate 3D position in camera space
                    vec3_t pos_a = apply_camera_transform(cam, &tri->a.p);
                    vec3_t pos_b = apply_camera_transform(cam, &tri->b.p);
                    vec3_t pos_c = apply_camera_transform(cam, &tri->c.p);

                    vec3_t pos = {
                        u * pos_a.x + v * pos_b.x + w * pos_c.x,
                        u * pos_a.y + v * pos_b.y + w * pos_c.y,
                        u * pos_a.z + v * pos_b.z + w * pos_c.z
                    };

                    // Compute distance from light
                    f32_t dx = pos.x - light_cam_space_pos->x;
                    f32_t dy = pos.y - light_cam_space_pos->y;
                    f32_t dz = pos.z - light_cam_space_pos->z;
                    f32_t distance = sqrtf(dx * dx + dy * dy + dz * dz);
                    distance_buffer[index] = distance;

                    // Interpolate color
                    col_t color = {
                        .r = (u * tri->a.c.r) + (v * tri->b.c.r) + (w * tri->c.c.r),
                        .g = (u * tri->a.c.g) + (v * tri->b.c.g) + (w * tri->c.c.g),
                        .b = (u * tri->a.c.b) + (v * tri->b.c.b) + (w * tri->c.c.b),
                    };
                    fb_buffer[index] = color;
                }
            }
        }
    }
}

/* Rendering Functions */

/* Clear the frame buffer, Z-buffer, and Distance buffer */
void clear_fb_buffer(col_t *fb_buffer, f32_t *zb_buffer, f32_t *distance_buffer) {
    for(int i = 0; i < LOGICAL_WIDTH * LOGICAL_HEIGHT; i++) {
        fb_buffer[i] = (col_t){0, 0, 0};             // Black background
        zb_buffer[i] = ZFAR;                         // Initialize Z-buffer to farthest depth
        distance_buffer[i] = 1e9f;                    // Initialize Distance buffer to a large number
    }
}

/* Render the frame buffer with distance-based shading from the light source */
void render_fb_distance(const col_t *fb_buffer, const float *distance_buffer, const light_t *light, framebuffer_t *fb_device) {
    for(int y = 0; y < LOGICAL_HEIGHT; y++) {
        for(int x = 0; x < LOGICAL_WIDTH; x++) {
            int index = y * LOGICAL_WIDTH + x;
            col_t color = fb_buffer[index];

            f32_t brightness = 1.0f;

            if (light) { // Apply lighting if light exists
                f32_t distance = distance_buffer[index];

                // Normalize distance between 0 and the range of the light
                f32_t normalized = distance / light->range;

                // Clamp the normalized value between 0 and 1
                if(normalized < 0.0f) normalized = 0.0f;
                if(normalized > 1.0f) normalized = 1.0f;

                // Invert the normalized distance for brightness (closer objects are brighter)
                brightness = 1.0f - normalized;

                // Apply light intensity
                brightness *= light->intensity;

                // Modulate the object's color with the light's color and brightness
                color.r = (u8_t)(color.r * brightness * (light->color.r / 255.0f));
                color.g = (u8_t)(color.g * brightness * (light->color.g / 255.0f));
                color.b = (u8_t)(color.b * brightness * (light->color.b / 255.0f));
            }

            /* Map the logical framebuffer to the actual screen resolution */
            // Calculate scaling factors
            float scale_x = (float)framebuffer.vinfo.xres / LOGICAL_WIDTH;
            float scale_y = (float)framebuffer.vinfo.yres / LOGICAL_HEIGHT;

            // Scale coordinates
            int screen_x = x * scale_x;
            int screen_y = y * scale_y;

            // Draw a block of pixels to represent the single logical pixel
            for(int dy = 0; dy < scale_y; dy++) {
                for(int dx = 0; dx < scale_x; dx++) {
                    put_pixel(&framebuffer, screen_x + dx, screen_y + dy, color);
                }
            }
        }
    }
}

/* Main Function */
int main() {
    /* Handle SIGINT for graceful exit */
    signal(SIGINT, handle_signal);
    signal(SIGTERM, handle_signal);

    /* Initialize framebuffer */
    if(init_framebuffer(&framebuffer) != 0) {
        fprintf(stderr, "Failed to initialize framebuffer.\n");
        exit(EXIT_FAILURE);
    }

    /* Set logical resolution to match physical resolution if desired */
    // Alternatively, adjust LOGICAL_WIDTH and LOGICAL_HEIGHT as needed
    // For simplicity, keeping LOGICAL_WIDTH x LOGICAL_HEIGHT as a virtual resolution

    /* Initialize frame buffer, Z-buffer, and Distance buffer */
    col_t *fb_buffer = malloc(sizeof(col_t) * LOGICAL_WIDTH * LOGICAL_HEIGHT);
    if (!fb_buffer) {
        perror("Failed to allocate framebuffer buffer");
        close_framebuffer(&framebuffer);
        exit(EXIT_FAILURE);
    }

    f32_t *zb_buffer = malloc(sizeof(f32_t) * LOGICAL_WIDTH * LOGICAL_HEIGHT);
    if (!zb_buffer) {
        perror("Failed to allocate Z-buffer");
        free(fb_buffer);
        close_framebuffer(&framebuffer);
        exit(EXIT_FAILURE);
    }

    f32_t *distance_buffer = malloc(sizeof(f32_t) * LOGICAL_WIDTH * LOGICAL_HEIGHT);
    if (!distance_buffer) {
        perror("Failed to allocate Distance buffer");
        free(fb_buffer);
        free(zb_buffer);
        close_framebuffer(&framebuffer);
        exit(EXIT_FAILURE);
    }

    /* Example Triangles */
    tri_t t1 = {
        .a = { .p = { -2.0f, -2.0f, 5.0f }, .c = { 255, 0, 0 } },   // Red
        .b = { .p = {  2.0f, -2.0f, 5.0f }, .c = { 0, 255, 0 } },   // Green
        .c = { .p = {  0.0f,  2.0f, 5.0f }, .c = { 0, 0, 255 } }    // Blue
    };

    tri_t t2 = {
        .a = { .p = { -3.0f, -3.0f, 5.0f }, .c = { 255, 255, 0 } },   // Red
        .b = { .p = {  3.0f, -3.0f, 5.0f }, .c = { 0, 255, 255 } },   // Green
        .c = { .p = {  0.0f,  3.0f, 5.0f }, .c = { 255, 0, 255 } }    // Blue
    };


    /* Initialize Camera */
    camera_t camera = {
        .position = {0.0f, 0.0f, 0.0f},
        .pitch = 0.0f,
        .yaw = 0.0f,
        .roll = 0.0f
    };

    /* Initialize Light */
    light_t light = {
        .position = {0.0f, 0.0f, 3.0f},  // Position of the light in world space
        .color = {255, 255, 255},       // White light
        .intensity = 1.0f,              // Full intensity
        .range = 5.0f                   // Range of the light
    };

    f32_t counter = 0;

    /* Main Rendering Loop */
    while(1) {
        /* Clear the buffers */
        clear_fb_buffer(fb_buffer, zb_buffer, distance_buffer);

        /* Apply Local Transformation: Rotate Triangle t1 around its centroid by 1 degree on all axes */
        rotate_triangle_local_deg(&t1, 1.0f, 1.0f, 1.0f); // Rotate 1 degree around X, Y, and Z axes
	rotate_triangle_local_deg(&t2, -2.0f, -2.0f, -2.0f); // Rotate 1 degree around X, Y, and Z axes



        /* Update Light Position (Optional: Animate the light) */
        light.position.y = 3.0f * sinf(counter);
        light.position.x = 3.0f * cosf(counter);
        counter += 0.01f;

        /* Transform light position to camera space */
        vec3_t light_cam_space = apply_camera_transform(&camera, &light.position);

        /* Rasterize the triangles with lighting */
        rasterize_triangle(&t1, &camera, &light_cam_space, fb_buffer, zb_buffer, distance_buffer); 
        rasterize_triangle(&t2, &camera, &light_cam_space, fb_buffer, zb_buffer, distance_buffer);


        /* Render the frame buffer to the framebuffer device with lighting */
        render_fb_distance(fb_buffer, distance_buffer, 0 /*&light*/, &framebuffer);

        /* Sleep for 16 ms (~60 FPS) */
        usleep(12000);
    }

    /* Cleanup (unreachable in this example, but good practice) */
    free(fb_buffer);
    free(zb_buffer);
    free(distance_buffer);
    close_framebuffer(&framebuffer);

    return 0;
}

```