using aabb planes as mirror planes
[laserbrain_demo] / src / geom.cc
index 890d975..1813eea 100644 (file)
@@ -1,6 +1,8 @@
-#include <algorithm>
+#include <stdlib.h>
 #include <float.h>
+#include <algorithm>
 #include "geom.h"
+#include "app.h"
 
 #define SPHERE(ptr)    ((Sphere*)ptr)
 #define AABOX(ptr)     ((AABox*)ptr)
@@ -16,6 +18,24 @@ GeomObject::~GeomObject()
 {
 }
 
+bool GeomObject::valid() const
+{
+       return true;
+}
+
+void GeomObject::invalidate()
+{
+}
+
+float GeomObject::distance(const Vec3 &v) const
+{
+       return sqrt(distance_sq(v));
+}
+
+float GeomObject::signed_distance(const Vec3 &v) const
+{
+       return sqrt(signed_distance_sq(v));
+}
 
 Sphere::Sphere()
 {
@@ -30,6 +50,17 @@ Sphere::Sphere(const Vec3 &cent, float radius)
        this->radius = radius;
 }
 
+bool Sphere::valid() const
+{
+       return radius >= 0.0f;
+}
+
+void Sphere::invalidate()
+{
+       center = Vec3(0, 0, 0);
+       radius = -1;
+}
+
 bool Sphere::intersect(const Ray &ray, HitPoint *hit) const
 {
        float a = dot(ray.dir, ray.dir);
@@ -73,14 +104,14 @@ bool Sphere::contains(const Vec3 &pt) const
        return length_sq(pt - center) <= radius * radius;
 }
 
-float Sphere::distance(const Vec3 &v) const
+float Sphere::distance_sq(const Vec3 &v) const
 {
-       return std::max(length(v - center) - radius, 0.0f);
+       return std::max(length_sq(v - center) - radius * radius, 0.0f);
 }
 
-float Sphere::signed_distance(const Vec3 &v) const
+float Sphere::signed_distance_sq(const Vec3 &v) const
 {
-       return length(v - center) - radius;
+       return length_sq(v - center) - radius * radius;
 }
 
 AABox::AABox()
@@ -94,19 +125,63 @@ AABox::AABox(const Vec3 &vmin, const Vec3 &vmax)
        type = GOBJ_AABOX;
 }
 
+bool AABox::valid() const
+{
+       return min.x <= max.x && min.y <= max.y && min.z <= max.z;
+}
+
+void AABox::invalidate()
+{
+       min = Vec3(FLT_MAX, FLT_MAX, FLT_MAX);
+       max = Vec3(-FLT_MAX, -FLT_MAX, -FLT_MAX);
+}
+
 Vec3 AABox::get_corner(int idx) const
 {
-       static const Vec3 v[] = {
-               Vec3(-0.5, -0.5, -0.5), Vec3(0.5, -0.5, -0.5), Vec3(0.5, -0.5, 0.5), Vec3(-0.5, -0.5, 0.5),
-               Vec3(-0.5, 0.5, -0.5), Vec3(0.5, 0.5, -0.5), Vec3(0.5, 0.5, 0.5), Vec3(-0.5, 0.5, 0.5)
-       };
-       return v[idx] * Vec3(max - min);
+       Vec3 v[] = {min, max};
+       static const int xidx[] = {0, 1, 1, 0, 0, 1, 1, 0};
+       static const int yidx[] = {0, 0, 0, 0, 1, 1, 1, 1};
+       static const int zidx[] = {0, 0, 1, 1, 0, 0, 1, 1};
+       return Vec3(v[xidx[idx]].x, v[yidx[idx]].y, v[zidx[idx]].z);
+}
+
+Plane AABox::get_plane(int pidx) const
+{
+       switch(pidx) {
+       case AABOX_PLANE_PX:
+               return Plane(Vec3(max.x, 0, 0), Vec3(1, 0, 0));
+       case AABOX_PLANE_NX:
+               return Plane(Vec3(min.x, 0, 0), Vec3(-1, 0, 0));
+       case AABOX_PLANE_PY:
+               return Plane(Vec3(0, max.x, 0), Vec3(0, 1, 0));
+       case AABOX_PLANE_NY:
+               return Plane(Vec3(0, min.x, 0), Vec3(0, -1, 0));
+       case AABOX_PLANE_PZ:
+               return Plane(Vec3(0, 0, max.z), Vec3(0, 0, 1));
+       case AABOX_PLANE_NZ:
+               return Plane(Vec3(0, 0, min.z), Vec3(0, 0, -1));
+       default:
+               break;
+       }
+       abort();
+       return Plane();
 }
 
 bool AABox::intersect(const Ray &ray, HitPoint *hit) const
 {
        Vec3 param[2] = {min, max};
+#ifndef NDEBUG
+       Vec3 inv_dir;
+       if(fpexcept_enabled) {
+               inv_dir.x = ray.dir.x == 0.0f ? 1.0f : 1.0f / ray.dir.x;
+               inv_dir.y = ray.dir.y == 0.0f ? 1.0f : 1.0f / ray.dir.y;
+               inv_dir.z = ray.dir.z == 0.0f ? 1.0f : 1.0f / ray.dir.z;
+       } else {
+               inv_dir = Vec3(1.0 / ray.dir.x, 1.0 / ray.dir.y, 1.0 / ray.dir.z);
+       }
+#else
        Vec3 inv_dir(1.0 / ray.dir.x, 1.0 / ray.dir.y, 1.0 / ray.dir.z);
+#endif
        int sign[3] = {inv_dir.x < 0, inv_dir.y < 0, inv_dir.z < 0};
 
        float tmin = (param[sign[0]].x - ray.origin.x) * inv_dir.x;
@@ -174,28 +249,68 @@ bool AABox::contains(const Vec3 &v) const
                v.x <= max.x && v.y <= max.y && v.z <= max.z;
 }
 
-float AABox::distance(const Vec3 &v) const
+#define SQ(x)  ((x) * (x))
+float AABox::distance_sq(const Vec3 &v) const
 {
-       return 0.0;     // TODO
+       float dsq = 0.0f;
+
+       for(int i=0; i<3; i++) {
+               if(v[i] < min[i]) dsq += SQ(min[i] - v[i]);
+               if(v[i] > max[i]) dsq += SQ(v[i] - max[i]);
+       }
+       return dsq;
 }
 
-float AABox::signed_distance(const Vec3 &v) const
+float AABox::signed_distance_sq(const Vec3 &v) const
 {
-       return 0.0;     // TODO
+       if(!contains(v)) {
+               return distance_sq(v);
+       }
+
+       float dsq = 0.0f;
+       for(int i=0; i<3; i++) {
+               float dmin = v[i] - min[i];
+               float dmax = max[i] - v[i];
+               dsq -= dmin < dmax ? SQ(dmin) : SQ(dmax);
+       }
+       return dsq;
 }
 
 Box::Box()
 {
+       type = GOBJ_BOX;
+}
+
+Box::Box(const AABox &aabox, const Mat4 &xform)
+       : xform(xform)
+{
+       type = GOBJ_BOX;
+       min = aabox.min;
+       max = aabox.max;
+}
+
+void Box::invalidate()
+{
+       AABox::invalidate();
+       xform = Mat4::identity;
 }
 
 Box::Box(const Vec3 &min, const Vec3 &max)
        : AABox(min, max)
 {
+       type = GOBJ_BOX;
+}
+
+Box::Box(const Vec3 &min, const Vec3 &max, const Mat4 &xform)
+       : AABox(min, max), xform(xform)
+{
+       type = GOBJ_BOX;
 }
 
 // XXX all this shit is completely untested
 Box::Box(const Vec3 &pos, const Vec3 &vi, const Vec3 &vj, const Vec3 &vk)
 {
+       type = GOBJ_BOX;
        float ilen = length(vi);
        float jlen = length(vj);
        float klen = length(vk);
@@ -213,17 +328,13 @@ Box::Box(const Vec3 &pos, const Vec3 &vi, const Vec3 &vj, const Vec3 &vk)
 
 Box::Box(const Vec3 *varr, int vcount)
 {
+       type = GOBJ_BOX;
        calc_bounding_aabox(this, varr, vcount);
 }
 
 Vec3 Box::get_corner(int idx) const
 {
-       static const Vec3 v[] = {
-               Vec3(-0.5, -0.5, -0.5), Vec3(0.5, -0.5, -0.5), Vec3(0.5, -0.5, 0.5), Vec3(-0.5, -0.5, 0.5),
-               Vec3(-0.5, 0.5, -0.5), Vec3(0.5, 0.5, -0.5), Vec3(0.5, 0.5, 0.5), Vec3(-0.5, 0.5, 0.5)
-       };
-
-       return xform * (v[idx] * Vec3(max - min));
+       return xform * AABox::get_corner(idx);
 }
 
 bool Box::intersect(const Ray &ray, HitPoint *hit) const
@@ -249,12 +360,12 @@ bool Box::contains(const Vec3 &pt) const
        return AABox::contains(inverse(xform) * pt);
 }
 
-float Box::distance(const Vec3 &v) const
+float Box::distance_sq(const Vec3 &v) const
 {
        return 0.0f;    // TODO
 }
 
-float Box::signed_distance(const Vec3 &v) const
+float Box::signed_distance_sq(const Vec3 &v) const
 {
        return 0.0f;    // TODO
 }
@@ -262,22 +373,26 @@ float Box::signed_distance(const Vec3 &v) const
 Plane::Plane()
        : normal(0.0, 1.0, 0.0)
 {
+       type = GOBJ_PLANE;
 }
 
 Plane::Plane(const Vec3 &p, const Vec3 &norm)
        : pt(p)
 {
+       type = GOBJ_PLANE;
        normal = normalize(norm);
 }
 
 Plane::Plane(const Vec3 &p1, const Vec3 &p2, const Vec3 &p3)
        : pt(p1)
 {
+       type = GOBJ_PLANE;
        normal = normalize(cross(p2 - p1, p3 - p1));
 }
 
 Plane::Plane(const Vec3 &normal, float dist)
 {
+       type = GOBJ_PLANE;
        this->normal = normalize(normal);
        pt = this->normal * dist;
 }
@@ -316,24 +431,49 @@ float Plane::signed_distance(const Vec3 &v) const
        return dot(v - pt, normal);
 }
 
+float Plane::distance_sq(const Vec3 &v) const
+{
+       float d = distance(v);
+       return d * d;
+}
+
+float Plane::signed_distance_sq(const Vec3 &v) const
+{
+       float d = distance(v);
+       return dot(v, normal) >= 0.0f ? d * d : -(d * d);
+}
+
 
 Disc::Disc()
 {
+       type = GOBJ_DISC;
        radius = 1.0;
 }
 
 Disc::Disc(const Vec3 &pt, const Vec3 &normal, float rad)
        : Plane(pt, normal)
 {
+       type = GOBJ_DISC;
        radius = rad;
 }
 
 Disc::Disc(const Vec3 &normal, float dist, float rad)
        : Plane(normal, dist)
 {
+       type = GOBJ_DISC;
        radius = rad;
 }
 
+bool Disc::valid() const
+{
+       return radius >= 0.0f;
+}
+
+void Disc::invalidate()
+{
+       radius = -1;
+}
+
 bool Disc::intersect(const Ray &ray, HitPoint *hit) const
 {
        HitPoint phit;
@@ -352,12 +492,12 @@ bool Disc::contains(const Vec3 &pt) const
        return length_sq(pj - this->pt) <= radius * radius;
 }
 
-float Disc::distance(const Vec3 &v) const
+float Disc::distance_sq(const Vec3 &v) const
 {
        return 0.0;     // TODO
 }
 
-float Disc::signed_distance(const Vec3 &v) const
+float Disc::signed_distance_sq(const Vec3 &v) const
 {
        return 0.0;     // TODO
 }
@@ -373,6 +513,11 @@ Vec3 proj_point_plane(const Vec3 &pt, const Plane &plane)
 
 bool calc_bounding_sphere(Sphere *sph, const GeomObject *obj)
 {
+       if(!obj->valid()) {
+               sph->invalidate();
+               return true;
+       }
+
        switch(obj->type) {
        case GOBJ_SPHERE:
                *sph = *(Sphere*)obj;
@@ -454,6 +599,11 @@ bool calc_bounding_sphere(Sphere *sph, const Vec3 *v, int num, const Mat4 &xform
 
 bool calc_bounding_aabox(AABox *box, const GeomObject *obj)
 {
+       if(!obj->valid()) {
+               box->invalidate();
+               return true;
+       }
+
        switch(obj->type) {
        case GOBJ_AABOX:
                *box = *(AABox*)obj;
@@ -531,6 +681,40 @@ bool calc_bounding_aabox(AABox *box, const Vec3 *v, int num, const Mat4 &xform)
        return true;
 }
 
+bool calc_bounding_box(Box *box, const GeomObject *obj)
+{
+       if(!obj->valid()) {
+               box->invalidate();
+               return true;
+       }
+
+       switch(obj->type) {
+       case GOBJ_BOX:
+               *box = *(Box*)obj;
+               break;
+
+       case GOBJ_AABOX:
+               box->min = BOX(obj)->min;
+               box->max = BOX(obj)->max;
+               box->xform = Mat4::identity;
+               break;
+
+       case GOBJ_SPHERE:
+               {
+                       float r = SPHERE(obj)->radius;
+                       box->min = SPHERE(obj)->center - Vec3(r, r, r);
+                       box->max = SPHERE(obj)->center + Vec3(r, r, r);
+                       box->xform = Mat4::identity;
+               }
+               break;
+
+       case GOBJ_PLANE:
+       default:
+               return false;
+       }
+       return true;
+}
+
 bool intersect_sphere_sphere(Disc *result, const Sphere &a, const Sphere &b)
 {
        Vec3 dir = b.center - a.center;
@@ -580,3 +764,8 @@ bool intersect_aabox_aabox(AABox *res, const AABox &a, const AABox &b)
        }
        return res->min.x != res->max.x && res->min.y != res->max.y && res->min.z != res->max.z;
 }
+
+bool collision_sphere_aabox(const Sphere &s, const AABox &b)
+{
+       return b.distance_sq(s.center) <= s.radius * s.radius;
+}