Commits

Anonymous committed ad13d15

add 2d and raytrace programs that output directly to framebuffer

Comments (0)

Files changed (2)

+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <linux/fb.h>
+
+#define MAX(a, b) ((a) > (b) ?  (a) : (b))
+#define MIN(a, b) ((a) < (b) ?  (a) : (b))
+#define ABS(a)    ((a) <  0  ? -(a) : (a))
+
+typedef union {
+    uint32_t val;
+    struct {
+        uint8_t b, g, r, t;
+    };
+} Pixel;
+
+typedef struct {
+    Pixel *buf;
+    int xres, yres;
+} Buffer;
+
+typedef struct {
+    int x, y;
+} Point;
+
+void push(void *fbm, Buffer buf)
+{
+    memcpy(fbm, buf.buf, buf.xres * buf.yres * sizeof(*buf.buf));
+}
+
+void draw_point(Buffer buf, Point p, Pixel color)
+{
+    if (p.y < 0 || p.y > buf.yres ||
+        p.x < 0 || p.x > buf.xres)
+        return;
+    *(buf.buf + p.y * buf.xres + p.x) = color;
+}
+
+int Point_eq(Point a, Point b)
+{
+    return a.x == b.x && a.y == b.y;
+}
+
+void draw_line(Buffer buf, Point a, Point b, Pixel color)
+{
+    Point d = { ABS(b.x - a.x)      , ABS(b.y - a.y)       };
+    Point s = { (a.x < b.x ? 1 : -1), (a.y < b.y ? 1 : -1) };
+    int err = d.x - d.y;
+    int e2;
+
+    for (;;) {
+        draw_point(buf, a, color);
+        if (Point_eq(a, b)) break;
+        e2 = 2 * err;
+        if (e2 > -d.y) { err -= d.y; a.x += s.x; }
+        if (e2 <  d.x) { err += d.x; a.y += s.y; }
+    }
+}
+
+int main(void)
+{
+    int fd;
+    uint8_t *fbm;
+    struct fb_var_screeninfo vi;
+    struct fb_fix_screeninfo fi;
+    Buffer buf;
+
+    if ((fd = open("/dev/fb0", O_RDWR))     < 0) return 1;
+    if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) return 2;
+    if (ioctl(fd, FBIOGET_VSCREENINFO, &vi) < 0) return 3;
+    if (vi.bits_per_pixel != 32)                 return 4;
+    if ((fbm = mmap(NULL, vi.xres * vi.yres * sizeof(Pixel), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) return 5;
+
+    buf.xres = vi.xres;
+    buf.yres = vi.yres;
+    buf.buf  = calloc(sizeof(Pixel), buf.xres * buf.yres);
+
+    draw_line(buf, (Point){ buf.xres, buf.yres }, (Point){ 0, 0 }, (Pixel){ 0xffffff0 });
+    push(fbm, buf);
+
+    munmap(fbm, sizeof(buf));
+    close(fd);
+
+    return 0;
+}
+#include <fcntl.h>
+#include <math.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <linux/fb.h>
+
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#define SWAP(a, b, t) do{ (t) = (a); (a) = (b); (b) = (t); }while(0)
+#define FAR_PLANE  100.
+#define NEAR_PLANE   1.1
+#define M_PI         3.14159265358979323846
+
+typedef union {
+    uint32_t val;
+    struct {
+        uint8_t b, g, r, t; // watch out for Endianness...
+    };
+} Color;
+
+typedef struct {
+    double x, y, z;
+} Point;
+
+typedef Point Vector;
+
+typedef struct {
+    Point  origin;
+    Vector direction;
+    double tmin;
+    double tmax;
+} Ray;
+
+typedef struct {
+    Point  center;
+    Vector normal;
+} Plane;
+
+typedef struct {
+    Point  center;
+    double radius;
+} Sphere;
+
+typedef union {
+    Sphere sphere;
+    Plane  plane;
+} Shape;
+
+typedef struct {
+    Shape shape;
+    Color color;
+    int (*intersect)(Shape, Ray*);
+} Object;
+
+typedef struct {
+    Color *buf;
+    int xres;
+    int yres;
+    double fov; // radians
+    Point  position;
+    Vector direction;
+} Camera;
+
+double dot            (Vector a, Vector b) { return a.x * b.x + a.y * b.y + a.z * b.z;                  }
+double magnitude      (Vector a)           { return sqrt(dot(a, a));                                    }
+Vector scalar_multiply(double s, Vector a) { return (Vector){ a.x * s  , a.y * s  , a.z * s   };        }
+Vector add            (Vector a, Vector b) { return (Vector){ a.x + b.x, a.y + b.y, a.z + b.z };        }
+Vector normalize      (Vector a) { double m; return (m = magnitude(a)) ? scalar_multiply(1 / m, a) : a; }
+
+double radians(double degrees)
+{
+    return degrees * M_PI / 180;
+}
+
+Ray new_primary_ray(Camera c, int x, int y)
+{
+    double aspect_ratio = (double)c.xres / c.yres;
+    double t = tan(c.fov / 2);
+    Vector pixel;
+
+    pixel.x = (-1 + 2 * (x + 0.5) / c.xres) * t * aspect_ratio;
+    pixel.y = ( 1 - 2 * (y + 0.5) / c.yres) * t;
+    pixel.z =   1;
+    pixel = normalize(pixel);
+
+    //TODO: transform pixel to camera coordinates
+    return (Ray){ c.position, pixel, NEAR_PLANE, FAR_PLANE };
+}
+
+int qroots(double a, double b, double c, double *r1, double *r2)
+{
+    double q, d = b * b - 4 * a * c;
+
+    if (d <  0) return 0;
+    if (d == 0) {
+        *r1 = *r2 = -0.5 * b / a;
+        return 1;
+    }
+    q = (b > 0) ? -0.5 * (b + sqrt(d)) : -0.5 * (b - sqrt(d));
+    *r1 = q / a;
+    *r2 = c / q;
+
+    if (*r1 > *r2)
+        SWAP(*r1, *r2, q);
+    return 2;
+}
+
+int intersect_sphere(Shape s, Ray *r)
+{
+    double t0, t1;
+    Vector ro = add(r->origin, scalar_multiply(-1, s.sphere.center));
+    double a  = 1; // direction is normalized. if not: a = dot(r->direction, r->direction);
+    double b  = dot(r->direction, ro) * 2;
+    double c  = dot(ro, ro) - s.sphere.radius * s.sphere.radius;
+
+    if (!qroots(a, b, c, &t0, &t1))       return 0;
+    if (t0 > r->tmax || t0 < r->tmin) return 0;
+    r->tmax = t0;
+    return 1;
+}
+
+int intersect_plane(Shape p, Ray *r)
+{
+    double d = dot(p.plane.normal, r->direction);
+    Vector v = add(p.plane.center, scalar_multiply(-1, r->origin));
+
+    if (fabs(d) < 1E-6) return 0;
+    d = dot(v, p.plane.normal) / d;
+    if (d < r->tmin || d > r->tmax) return 0;
+    r->tmax = d;
+    return 1;
+}
+
+Color shade(Ray r, Object o)
+{
+    return o.color;
+}
+
+Color trace(Ray r, Object *o, Color bg)
+{
+    Object *p, *hit = NULL;
+
+    for (p = o; p->intersect; p++)
+        if (p->intersect(p->shape, &r))
+            hit = p;
+
+    return hit ? shade(r, *hit) : bg;
+}
+
+void render(Camera c, Object *o, Color bg)
+{
+    int x, y;
+    Object *hit;
+
+    for (y = 0; y < c.yres; y++)
+        for (x = 0; x < c.xres; x++)
+            *(c.buf + y * c.xres + x) = trace(new_primary_ray(c, x, y), o, bg);
+}
+
+int main(void)
+{
+    int fd;
+    uint8_t *fbm;
+    struct fb_var_screeninfo vi;
+    struct fb_fix_screeninfo fi;
+
+    //Sphere s = { { 0, 0, 10 }, 2 };
+    Camera c = { NULL, 0, 0, radians(90), { 0 }, { 0, 0, 1 } };
+    Object objects[] = {
+        { { .plane  = { {   0,   0, 20 }, {  0,  0, -1 } } }, { .val = 0x0000ffffUL }, intersect_plane  },
+        { { .plane  = { { -10,   0,  0 }, {  1,  0,  0 } } }, { .val = 0x00ff0000UL }, intersect_plane  },
+        { { .plane  = { {  10,   0,  0 }, { -1,  0,  0 } } }, { .val = 0x00ffff00UL }, intersect_plane  },
+        { { .plane  = { {   0,  10,  0 }, {  0, -1,  0 } } }, { .val = 0x00ffffffUL }, intersect_plane  },
+        { { .plane  = { {   0, -10,  0 }, {  0,  1,  0 } } }, { .val = 0x00000000UL }, intersect_plane  },
+        { { .sphere = { {   0,   0, 10 }, 2              } }, { .val = 0x000000ffUL }, intersect_sphere },
+        { { .sphere = { {   1,   1,  9 }, 1.5            } }, { .val = 0x0000ff00UL }, intersect_sphere },
+        { 0 }
+    };
+
+    if ((fd = open("/dev/fb0", O_RDWR))     < 0) return 1;
+    if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) return 2;
+    if (ioctl(fd, FBIOGET_VSCREENINFO, &vi) < 0) return 3;
+    if (vi.bits_per_pixel != 32)                 return 4;
+    if ((c.buf = mmap(NULL, vi.xres * vi.yres * sizeof(Color), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
+        perror("mmap failed");
+        return 5;
+    }
+    c.xres = vi.xres;
+    c.yres = vi.yres;
+
+    render(c, objects, (Color){ .val = 0x00ffffff });
+
+    munmap(c.buf, c.xres * c.yres * sizeof(Color));
+    close(fd);
+
+    return 0;
+}