X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?p=laserbrain_demo;a=blobdiff_plain;f=src%2Fgeom.cc;h=890d9754bbc5677028ab2c426ce00eeaf58eea19;hp=23391a73444f094dbf0c639216e19180c34ff6fd;hb=ca5d732eb2b76da1e01428193effeb205ae951e9;hpb=ae60a8cb1a30e204e7f60969fe6245e510cca0ff diff --git a/src/geom.cc b/src/geom.cc index 23391a7..890d975 100644 --- a/src/geom.cc +++ b/src/geom.cc @@ -1,8 +1,17 @@ -#include -#include #include +#include #include "geom.h" +#define SPHERE(ptr) ((Sphere*)ptr) +#define AABOX(ptr) ((AABox*)ptr) +#define BOX(ptr) ((Box*)ptr) +#define PLANE(ptr) ((Plane*)ptr) + +GeomObject::GeomObject() +{ + type = GOBJ_UNKNOWN; +} + GeomObject::~GeomObject() { } @@ -10,43 +19,17 @@ GeomObject::~GeomObject() Sphere::Sphere() { + type = GOBJ_SPHERE; radius = 1.0; } Sphere::Sphere(const Vec3 ¢, float radius) : center(cent) { + type = GOBJ_SPHERE; this->radius = radius; } -void Sphere::set_union(const GeomObject *obj1, const GeomObject *obj2) -{ - const Sphere *sph1 = dynamic_cast(obj1); - const Sphere *sph2 = dynamic_cast(obj2); - - if(!sph1 || !sph2) { - fprintf(stderr, "Sphere::set_union: arguments must be spheres"); - return; - } - - float dist = length(sph1->center - sph2->center); - float surf_dist = dist - (sph1->radius + sph2->radius); - float d1 = sph1->radius + surf_dist / 2.0; - float d2 = sph2->radius + surf_dist / 2.0; - float t = d1 / (d1 + d2); - - if(t < 0.0) t = 0.0; - if(t > 1.0) t = 1.0; - - center = sph1->center * t + sph2->center * (1.0 - t); - radius = std::max(dist * t + sph2->radius, dist * (1.0f - t) + sph1->radius); -} - -void Sphere::set_intersection(const GeomObject *obj1, const GeomObject *obj2) -{ - fprintf(stderr, "Sphere::intersection undefined\n"); -} - bool Sphere::intersect(const Ray &ray, HitPoint *hit) const { float a = dot(ray.dir, ray.dir); @@ -85,53 +68,39 @@ bool Sphere::intersect(const Ray &ray, HitPoint *hit) const return true; } - -AABox::AABox() +bool Sphere::contains(const Vec3 &pt) const { + return length_sq(pt - center) <= radius * radius; } -AABox::AABox(const Vec3 &vmin, const Vec3 &vmax) - : min(vmin), max(vmax) +float Sphere::distance(const Vec3 &v) const { + return std::max(length(v - center) - radius, 0.0f); } -void AABox::set_union(const GeomObject *obj1, const GeomObject *obj2) +float Sphere::signed_distance(const Vec3 &v) const { - const AABox *box1 = dynamic_cast(obj1); - const AABox *box2 = dynamic_cast(obj2); - - if(!box1 || !box2) { - fprintf(stderr, "AABox::set_union: arguments must be AABoxes too\n"); - return; - } - - min.x = std::min(box1->min.x, box2->min.x); - min.y = std::min(box1->min.y, box2->min.y); - min.z = std::min(box1->min.z, box2->min.z); - - max.x = std::max(box1->max.x, box2->max.x); - max.y = std::max(box1->max.y, box2->max.y); - max.z = std::max(box1->max.z, box2->max.z); + return length(v - center) - radius; } -void AABox::set_intersection(const GeomObject *obj1, const GeomObject *obj2) +AABox::AABox() { - const AABox *box1 = dynamic_cast(obj1); - const AABox *box2 = dynamic_cast(obj2); - - if(!box1 || !box2) { - fprintf(stderr, "AABox::set_intersection: arguments must be AABoxes too\n"); - return; - } + type = GOBJ_AABOX; +} - for(int i=0; i<3; i++) { - min[i] = std::max(box1->min[i], box2->min[i]); - max[i] = std::min(box1->max[i], box2->max[i]); +AABox::AABox(const Vec3 &vmin, const Vec3 &vmax) + : min(vmin), max(vmax) +{ + type = GOBJ_AABOX; +} - if(max[i] < min[i]) { - max[i] = min[i]; - } - } +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); } bool AABox::intersect(const Ray &ray, HitPoint *hit) const @@ -197,7 +166,97 @@ bool AABox::intersect(const Ray &ray, HitPoint *hit) const return true; } return false; +} + +bool AABox::contains(const Vec3 &v) const +{ + return v.x >= min.x && v.y >= min.y && v.z >= min.z && + v.x <= max.x && v.y <= max.y && v.z <= max.z; +} + +float AABox::distance(const Vec3 &v) const +{ + return 0.0; // TODO +} + +float AABox::signed_distance(const Vec3 &v) const +{ + return 0.0; // TODO +} +Box::Box() +{ +} + +Box::Box(const Vec3 &min, const Vec3 &max) + : AABox(min, max) +{ +} + +// XXX all this shit is completely untested +Box::Box(const Vec3 &pos, const Vec3 &vi, const Vec3 &vj, const Vec3 &vk) +{ + float ilen = length(vi); + float jlen = length(vj); + float klen = length(vk); + + min = Vec3(-ilen, -jlen, -klen); + max = Vec3(ilen, jlen, klen); + + float si = ilen == 0.0 ? 1.0 : 1.0 / ilen; + float sj = jlen == 0.0 ? 1.0 : 1.0 / jlen; + float sk = klen == 0.0 ? 1.0 : 1.0 / klen; + + xform = Mat4(vi * si, vj * sj, vk * sk); + xform.translate(pos); +} + +Box::Box(const Vec3 *varr, int vcount) +{ + 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)); +} + +bool Box::intersect(const Ray &ray, HitPoint *hit) const +{ + Mat4 inv_xform = inverse(xform); + Mat4 dir_inv_xform = inv_xform.upper3x3(); + Mat4 dir_xform = transpose(dir_inv_xform); + Ray local_ray = Ray(inv_xform * ray.origin, dir_inv_xform * ray.dir); + + bool res = AABox::intersect(local_ray, hit); + if(!res || !hit) return res; + + hit->pos = xform * hit->pos; + hit->normal = dir_xform * hit->normal; + hit->local_ray = local_ray; + hit->ray = ray; + return true; +} + +bool Box::contains(const Vec3 &pt) const +{ + // XXX is it faster to extract 6 planes and do dot products? sounds marginal + return AABox::contains(inverse(xform) * pt); +} + +float Box::distance(const Vec3 &v) const +{ + return 0.0f; // TODO +} + +float Box::signed_distance(const Vec3 &v) const +{ + return 0.0f; // TODO } Plane::Plane() @@ -223,16 +282,6 @@ Plane::Plane(const Vec3 &normal, float dist) pt = this->normal * dist; } -void Plane::set_union(const GeomObject *obj1, const GeomObject *obj2) -{ - fprintf(stderr, "Plane::set_union undefined\n"); -} - -void Plane::set_intersection(const GeomObject *obj1, const GeomObject *obj2) -{ - fprintf(stderr, "Plane::set_intersection undefined\n"); -} - bool Plane::intersect(const Ray &ray, HitPoint *hit) const { float ndotdir = dot(normal, ray.dir); @@ -244,6 +293,7 @@ bool Plane::intersect(const Ray &ray, HitPoint *hit) const Vec3 ptdir = pt - ray.origin; float t = dot(normal, ptdir) / ndotdir; + hit->dist = t; hit->pos = ray.origin + ray.dir * t; hit->normal = normal; hit->obj = this; @@ -251,84 +301,282 @@ bool Plane::intersect(const Ray &ray, HitPoint *hit) const return true; } -float sphere_distance(const Vec3 ¢, float rad, const Vec3 &pt) +bool Plane::contains(const Vec3 &v) const +{ + return dot(v, normal) <= 0.0; +} + +float Plane::distance(const Vec3 &v) const +{ + return std::max(dot(v - pt, normal), 0.0f); +} + +float Plane::signed_distance(const Vec3 &v) const +{ + return dot(v - pt, normal); +} + + +Disc::Disc() +{ + radius = 1.0; +} + +Disc::Disc(const Vec3 &pt, const Vec3 &normal, float rad) + : Plane(pt, normal) +{ + radius = rad; +} + +Disc::Disc(const Vec3 &normal, float dist, float rad) + : Plane(normal, dist) +{ + radius = rad; +} + +bool Disc::intersect(const Ray &ray, HitPoint *hit) const +{ + HitPoint phit; + if(Plane::intersect(ray, &phit)) { + if(length_sq(phit.pos - pt) <= radius * radius) { + *hit = phit; + return true; + } + } + return false; +} + +bool Disc::contains(const Vec3 &pt) const +{ + Vec3 pj = proj_point_plane(pt, *this); + return length_sq(pj - this->pt) <= radius * radius; +} + +float Disc::distance(const Vec3 &v) const +{ + return 0.0; // TODO +} + +float Disc::signed_distance(const Vec3 &v) const +{ + return 0.0; // TODO +} + + +Vec3 proj_point_plane(const Vec3 &pt, const Plane &plane) +{ + float dist = plane.signed_distance(pt); + return pt - plane.normal * dist; +} + +// ---- bounding sphere calculations ---- + +bool calc_bounding_sphere(Sphere *sph, const GeomObject *obj) +{ + switch(obj->type) { + case GOBJ_SPHERE: + *sph = *(Sphere*)obj; + break; + + case GOBJ_AABOX: + sph->center = (AABOX(obj)->min + AABOX(obj)->max) * 0.5; + sph->radius = length(AABOX(obj)->max - AABOX(obj)->min) * 0.5; + break; + + case GOBJ_BOX: + sph->center = (BOX(obj)->min + BOX(obj)->max) * 0.5 + BOX(obj)->xform.get_translation(); + sph->radius = length(BOX(obj)->max - BOX(obj)->min) * 0.5; + break; + + case GOBJ_PLANE: + default: + return false; + } + return true; +} + +bool calc_bounding_sphere(Sphere *sph, const GeomObject *a, const GeomObject *b) { - return length(pt - cent) - rad; + Sphere bsa, bsb; + + if(!calc_bounding_sphere(&bsa, a) || !calc_bounding_sphere(&bsb, b)) { + return false; + } + + float dist = length(bsa.center - bsb.center); + float surf_dist = dist - (bsa.radius + bsb.radius); + float d1 = bsa.radius + surf_dist / 2.0; + float d2 = bsb.radius + surf_dist / 2.0; + float t = d1 / (d1 + d2); + + if(t < 0.0) t = 0.0; + if(t > 1.0) t = 1.0; + + sph->center = bsa.center * t + bsb.center * (1.0 - t); + sph->radius = std::max(dist * t + bsb.radius, dist * (1.0f - t) + bsa.radius); + return true; } -// TODO version which takes both radii into account -float capsule_distance(const Vec3 &a, float ra, const Vec3 &b, float rb, const Vec3 &pt) +bool calc_bounding_sphere(Sphere *sph, const GeomObject **objv, int num) { - Vec3 ab_dir = b - a; - float ab_len_sq = length_sq(ab_dir); + if(num <= 0) return false; + + if(!calc_bounding_sphere(sph, objv[0])) { + return false; + } - if(fabs(ab_len_sq) < 1e-5) { - // if a == b, the capsule is a sphere with radius the maximum of the capsule radii - return sphere_distance(a, std::max(ra, rb), pt); + for(int i=1; icenter = Vec3(0.0, 0.0, 0.0); + for(int i=0; icenter += xform * v[i]; } - if(t >= 1.0) { - return sphere_distance(b, rb, pt); + sph->center /= (float)num; + + float rad_sq = 0.0f; + for(int i=0; icenter; + rad_sq = std::max(rad_sq, dot(dir, dir)); } + sph->radius = sqrt(rad_sq); + return true; +} + +bool calc_bounding_aabox(AABox *box, const GeomObject *obj) +{ + switch(obj->type) { + case GOBJ_AABOX: + *box = *(AABox*)obj; + break; + + case GOBJ_BOX: + { + Vec3 v[8]; + for(int i=0; i<8; i++) { + v[i] = BOX(obj)->get_corner(i); + } + calc_bounding_aabox(box, v, 8); + } + break; - Vec3 pproj = a + ab_dir * t; - return length(pproj - pt) - ra; + 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); + } + break; + + case GOBJ_PLANE: + default: + return false; + } + return true; } -#if 0 -float capsule_distance(const Vec3 &a, float ra, const Vec3 &b, float rb, const Vec3 &pt) +bool calc_bounding_aabox(AABox *box, const GeomObject *a, const GeomObject *b) { - Vec3 ab_dir = b - a; + AABox bba, bbb; - if(fabs(length_sq(ab_dir)) < 1e-5) { - // if a == b, the capsule is a sphere with radius the maximum of the capsule radii - return sphere_distance(a, std::max(ra, rb), pt); + if(!calc_bounding_aabox(&bba, a) || !calc_bounding_aabox(&bbb, b)) { + return false; } - float ab_len = length(ab_dir); - Vec3 ap_dir = pt - a; - Vec3 rotaxis = normalize(cross(ab_dir, ap_dir)); + for(int i=0; i<3; i++) { + box->min[i] = std::min(bba.min[i], bbb.min[i]); + box->max[i] = std::max(bba.max[i], bbb.max[i]); + } + return true; +} + +bool calc_bounding_aabox(AABox *box, const GeomObject **objv, int num) +{ + if(num <= 0) return false; - Mat4 rmat; - rmat.set_rotation(rotaxis, M_PI / 2.0); - Vec3 right = rmat * ab_dir / ab_len; + if(!calc_bounding_aabox(box, objv[0])) { + return false; + } - // XXX I think this check is redundant, always false, due to the cross product order - //assert(dot(right, ab_dir) >= 0.0); - if(dot(right, ab_dir) < 0.0) { - right = -right; + for(int i=1; imin = box->max = xform * v[0]; + for(int i=1; imin[j] = std::min(box->min[j], p[j]); + box->max[j] = std::max(box->max[j], p[j]); + } } - if(t >= 1.0) { - return sphere_distance(b, rb, pt); + return true; +} + +bool intersect_sphere_sphere(Disc *result, const Sphere &a, const Sphere &b) +{ + Vec3 dir = b.center - a.center; + + float dist_sq = length_sq(dir); + if(dist_sq <= 1e-8) return false; + + float rsum = a.radius + b.radius; + float rdif = fabs(a.radius - b.radius); + if(dist_sq > rsum * rsum || dist_sq < rdif * rdif) { + return false; } - Vec3 ppt = aa + aabb_dir * t; - Vec3 norm = ppt - pt; - float dist = length(norm); + float dist = sqrt(dist_sq); + float t = (dist_sq + a.radius * a.radius - b.radius * b.radius) / (2.0 * sqrt(dist_sq)); + + result->pt = a.center + dir * t; + result->normal = dir / dist; + result->radius = sin(acos(t)) * a.radius; + return true; +} - if(dot(norm, right) < 0.0) { - // inside the cone - dist = -dist; +bool intersect_plane_plane(Ray *result, const Plane &a, const Plane &b) +{ + return false; // TODO +} + +bool intersect_sphere_plane(Sphere *result, const Sphere &s, const Plane &p) +{ + return false; // TODO +} + +bool intersect_plane_sphere(Sphere *result, const Plane &p, const Sphere &s) +{ + return false; // TODO +} + +bool intersect_aabox_aabox(AABox *res, const AABox &a, const AABox &b) +{ + for(int i=0; i<3; i++) { + res->min[i] = std::max(a.min[i], b.min[i]); + res->max[i] = std::min(a.max[i], b.max[i]); + + if(res->max[i] < res->min[i]) { + res->max[i] = res->min[i]; + } } - return dist; + return res->min.x != res->max.x && res->min.y != res->max.y && res->min.z != res->max.z; } -#endif