+
+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);
+}
+
+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;
+}