foo
[meshfrac] / src / geom.c
index f7e1740..81e9df4 100644 (file)
@@ -1,4 +1,6 @@
+#include <stdio.h>
 #include "geom.h"
+#include "dynarr.h"
 
 float plane_dist(const struct plane *p, const cgm_vec3 *pt)
 {
@@ -21,3 +23,191 @@ void midplane(struct plane *p, const cgm_vec3 *a, const cgm_vec3 *b)
        p->pt.y = a->y + p->norm.y * 0.5f;
        p->pt.z = a->z + p->norm.z * 0.5f;
 }
+
+void poly_normal(const struct poly *poly, cgm_vec3 *n)
+{
+       cgm_vec3 va, vb;
+
+       va = poly->verts[1].pos;
+       cgm_vsub(&va, &poly->verts[0].pos);
+       vb = poly->verts[2].pos;
+       cgm_vsub(&vb, &poly->verts[2].pos);
+
+       cgm_vcross(n, &va, &vb);
+       cgm_vnormalize(n);
+}
+
+void poly_plane(const struct poly *poly, struct plane *plane)
+{
+       plane->pt = poly->verts[0].pos;
+       poly_normal(poly, &plane->norm);
+}
+
+int plane_poly(const struct plane *plane, struct poly *poly, float size)
+{
+       static const float corn[][2] = {{1,1}, {-1,1}, {-1,-1}, {1, -1}};
+       int i;
+       cgm_vec3 up, right;
+       struct vertex *vptr;
+       size *= 0.5f;
+
+       if(!(poly->verts = dynarr_alloc(4, sizeof *poly->verts))) {
+               fprintf(stderr, "plane_poly: failed to allocate polygon vertices\n");
+               return -1;
+       }
+
+       cgm_vcons(&up, 0, 1, 0);
+       if(1.0f - fabs(cgm_vdot(&up, &plane->norm)) < 1e-2) {
+               cgm_vcons(&up, 0, 0, -1);
+       }
+       cgm_vcross(&right, &up, &plane->norm);
+       cgm_vnormalize(&right);
+       cgm_vcross(&up, &plane->norm, &right);
+       cgm_vnormalize(&up);
+
+       vptr = poly->verts;
+       for(i=0; i<4; i++) {
+               vptr->pos.x = plane->pt.x + (right.x * corn[i][0] + up.x * corn[i][1]) * size;
+               vptr->pos.y = plane->pt.y + (right.y * corn[i][0] + up.y * corn[i][1]) * size;
+               vptr->pos.z = plane->pt.z + (right.z * corn[i][0] + up.z * corn[i][1]) * size;
+
+               vptr->norm = plane->norm;
+               vptr->uv.x = vptr->uv.y = 0;
+               vptr++;
+       }
+       return 0;
+}
+
+float ray_plane(const cgm_ray *ray, const struct plane *plane)
+{
+       float ndotdir, t;
+       cgm_vec3 pptdir;
+
+       ndotdir = cgm_vdot(&plane->norm, &ray->dir);
+       if(fabs(ndotdir) < 1e-4) {
+               return -1.0f;
+       }
+
+       pptdir = plane->pt;
+       cgm_vsub(&pptdir, &ray->origin);
+       t = cgm_vdot(&plane->norm, &pptdir) / ndotdir;
+       if(t < 1e-6) {
+               return -1.0f;
+       }
+       return t;
+}
+
+float ray_poly(const cgm_ray *ray, const struct poly *poly)
+{
+       float t;
+       struct plane plane;
+       cgm_vec3 hitp, vdir, edir, indir, vcross, incross;
+       int i, nverts;
+
+       poly_plane(poly, &plane);
+       if((t = ray_plane(ray, &plane)) < 0.0f) {
+               return -1.0f;
+       }
+       cgm_raypos(&hitp, ray, t);
+
+       /* see if the point is inside or outside the polygon */
+       nverts = dynarr_size(poly->verts);
+       for(i=0; i<nverts; i++) {
+               vdir = hitp;
+               cgm_vsub(&hitp, &poly->verts[i].pos);
+               edir = poly->verts[(i + 1) & nverts].pos;
+               cgm_vsub(&edir, &poly->verts[i].pos);
+               indir = poly->verts[(i + 2) & nverts].pos;
+               cgm_vsub(&indir, &poly->verts[i].pos);
+
+               cgm_vcross(&vcross, &vdir, &edir);
+               cgm_vcross(&incross, &indir, &edir);
+
+               if(cgm_vdot(&vcross, &incross) < 0.0f) {
+                       return -1.0f;
+               }
+       }
+       return t;
+}
+
+
+/* returns:
+ *  1 -> both inside
+ *  0 -> straddling and clipped
+ * -1 -> both outside
+ */
+static int clip_edge(struct poly *pout, const struct vertex *v0, const struct vertex *v1,
+               const struct plane *plane)
+{
+       float d0, d1, t;
+       cgm_ray ray;
+       struct vertex vnew;
+
+       d0 = plane_sdist(plane, &v0->pos);
+       d1 = plane_sdist(plane, &v1->pos);
+
+       ray.origin = v0->pos;
+       ray.dir = v1->pos;
+       cgm_vsub(&ray.dir, &v0->pos);
+
+       if(d0 >= 0.0) {
+               /* start inside */
+               if(d1 >= 0.0) {
+                       /* all inside */
+                       DYNARR_PUSH(pout->verts, v1);   /* append v1 */
+                       return 1;
+               } else {
+                       /* going out */
+                       t = ray_plane(&ray, plane);
+                       cgm_raypos(&vnew.pos, &ray, t);
+
+                       cgm_vlerp(&vnew.norm, &v0->norm, &v1->norm, t);
+                       vnew.uv.x = cgm_lerp(v0->uv.x, v1->uv.x, t);
+                       vnew.uv.y = cgm_lerp(v0->uv.y, v1->uv.y, t);
+                       /* append new vertex on the intersection point */
+                       DYNARR_PUSH(pout->verts, &vnew);
+               }
+       } else {
+               /* start outside */
+               if(d1 >= 0) {
+                       /* going in */
+                       t = ray_plane(&ray, plane);
+                       cgm_raypos(&vnew.pos, &ray, t);
+
+                       cgm_vlerp(&vnew.norm, &v0->norm, &v1->norm, t);
+                       vnew.uv.x = cgm_lerp(v0->uv.x, v1->uv.x, t);
+                       vnew.uv.y = cgm_lerp(v0->uv.y, v1->uv.y, t);
+                       /* append new vertex on the intersection point */
+                       DYNARR_PUSH(pout->verts, &vnew);
+                       /* then append v1 ... */
+                       DYNARR_PUSH(pout->verts, v1);
+               } else {
+                       /* all outside */
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+
+int clip_poly(struct poly *pout, const struct poly *pin, const struct plane *plane)
+{
+       int i, nextidx, res = 0, vnum;
+       int edges_clipped = 0;
+
+       dynarr_clear(pout->verts);
+
+       vnum = dynarr_size(pin->verts);
+       for(i=0; i<vnum; i++) {
+               nextidx = i + 1;
+               if(nextidx >= vnum) nextidx = 0;
+               res = clip_edge(pout, pin->verts + i, pin->verts + nextidx, plane);
+               if(res == 0) {
+                       ++edges_clipped;
+               }
+       }
+
+       return edges_clipped > 0 ? 0 : 1;
+
+}