almost there
[csgray] / src / geom.c
diff --git a/src/geom.c b/src/geom.c
new file mode 100644 (file)
index 0000000..ee7cb7d
--- /dev/null
@@ -0,0 +1,172 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "geom.h"
+#include "matrix.h"
+
+/* TODO custom hit allocator */
+struct hit *alloc_hit(void)
+{
+       struct hit *hit = calloc(sizeof *hit, 1);
+       if(!hit) {
+               perror("failed to allocate ray hit node");
+               abort();
+       }
+       return hit;
+}
+
+void free_hit(struct hit *hit)
+{
+       free(hit);
+}
+
+void free_hit_list(struct hit *hit)
+{
+       while(hit) {
+               struct hit *tmp = hit;
+               hit = hit->next;
+               free_hit(tmp);
+       }
+}
+
+struct hit *ray_intersect(struct ray *ray, csg_object *o)
+{
+       switch(o->ob.type) {
+       case OB_SPHERE:
+               return ray_sphere(ray, o);
+       case OB_CYLINDER:
+               return ray_cylinder(ray, o);
+       case OB_PLANE:
+               return ray_plane(ray, o);
+       case OB_UNION:
+               return ray_csg_un(ray, o);
+       case OB_INTERSECTION:
+               return ray_csg_isect(ray, o);
+       case OB_SUBTRACTION:
+               return ray_csg_sub(ray, o);
+       default:
+               break;
+       }
+       return 0;
+}
+
+struct hit *ray_sphere(struct ray *ray, csg_object *o)
+{
+       float a, b, c, d, sqrt_d, t0, t1, t, sq_rad;
+       struct hit *hit;
+       struct ray locray = *ray;
+
+       if(o->sph.rad == 0.0f) {
+               return 0;
+       }
+       sq_rad = o->sph.rad * o->sph.rad;
+
+       xform_ray(&locray, o->ob.inv_xform);
+
+       a = locray.dx * locray.dx + locray.dy * locray.dy + locray.dz * locray.dz;
+       b = 2.0f * (locray.dx * locray.x + locray.dy * locray.y + locray.dz * locray.z);
+       c = (locray.x * locray.x + locray.y * locray.y + locray.z * locray.z) - sq_rad;
+
+       d = b * b - 4.0f * a * c;
+       if(d < 1e-6) return 0;
+
+       sqrt_d = sqrt(d);
+       t0 = (-b + sqrt_d) / (2.0f * a);
+       t1 = (-b - sqrt_d) / (2.0f * a);
+
+       if(t0 < 1e-6) t0 = t1;
+       if(t1 < 1e-6) t1 = t0;
+       t = t0 < t1 ? t0 : t1;
+       if(t < 1e-6) return 0;
+
+       hit = alloc_hit();
+       hit->t = t;
+       hit->x = ray->x + ray->dx * t;
+       hit->y = ray->y + ray->dy * t;
+       hit->z = ray->z + ray->dz * t;
+       hit->nx = hit->x / o->sph.rad;
+       hit->ny = hit->y / o->sph.rad;
+       hit->nz = hit->z / o->sph.rad;
+       hit->o = o;
+       return hit;
+}
+
+struct hit *ray_cylinder(struct ray *ray, csg_object *o)
+{
+       struct ray locray = *ray;
+
+       xform_ray(&locray, o->ob.inv_xform);
+       return 0;       /* TODO */
+}
+
+struct hit *ray_plane(struct ray *ray, csg_object *o)
+{
+       float vx, vy, vz, ndotv, ndotr, t;
+       struct hit *hit = 0;
+       struct ray locray = *ray;
+
+       xform_ray(&locray, o->ob.inv_xform);
+
+       vx = o->plane.nx * o->plane.d - locray.x;
+       vy = o->plane.ny * o->plane.d - locray.y;
+       vz = o->plane.nz * o->plane.d - locray.z;
+
+       ndotv = o->plane.nx * vx + o->plane.ny * vy + o->plane.nz * vz;
+       if(fabs(ndotv) < 1e-6) return 0;
+
+       ndotr = o->plane.nx * locray.dx + o->plane.ny * locray.dy + o->plane.nz * locray.dz;
+       t = ndotr / ndotv;
+
+       if(t > 1e-6) {
+               hit = alloc_hit();
+               hit->t = t;
+               hit->x = ray->x + ray->dx * t;
+               hit->y = ray->y + ray->dy * t;
+               hit->z = ray->z + ray->dz * t;
+               hit->nx = o->plane.nx;
+               hit->ny = o->plane.ny;
+               hit->nz = o->plane.nz;
+               hit->o = o;
+       }
+       return hit;
+}
+
+struct hit *ray_csg_un(struct ray *ray, csg_object *o)
+{
+       struct hit *hita, *hitb;
+
+       hita = ray_intersect(ray, o->un.a);
+       hitb = ray_intersect(ray, o->un.a);
+
+       if(!hita) return hitb;
+       if(!hitb) return hita;
+
+       if(hita->t < hitb->t) {
+               free_hit(hitb);
+               return hita;
+       }
+       free_hit(hita);
+       return hitb;
+}
+
+struct hit *ray_csg_isect(struct ray *ray, csg_object *o)
+{
+       return 0;
+}
+
+struct hit *ray_csg_sub(struct ray *ray, csg_object *o)
+{
+       return 0;
+}
+
+
+void xform_ray(struct ray *ray, float *mat)
+{
+       float m3x3[16];
+
+       mat4_copy(m3x3, mat);
+       mat4_upper3x3(m3x3);
+
+       mat4_xform3(&ray->x, mat, &ray->x);
+       mat4_xform3(&ray->dx, m3x3, &ray->dx);
+}