//////////////////////////////////////////////////////////////////////////////// // Scorched3D (c) 2000-2004 // // This file is part of Scorched3D. // // Scorched3D is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // Scorched3D is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Scorched3D; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA //////////////////////////////////////////////////////////////////////////////// /* * Copyright (C) 1996, Christopher John Kline * Electronic mail: ckline@acm.org * * This software may be freely copied, modified, and redistributed * for academic purposes and by not-for-profit organizations, provided that * this copyright notice is preserved on all copies, and that the source * code is included or notice is given informing the end-user that the source * code is publicly available under the terms described here. * * Persons or organizations wishing to use this code or any modified version * of this code in a commercial and/or for-profit manner must contact the * author via electronic mail (preferred) or other method to arrange the terms * of usage. These terms may be as simple as giving the author visible credit * in the final product. * * There is no warranty or other guarantee of fitness for this software, * it is provided solely "as is". Bug reports or fixes may be sent * to the author, who may or may not act on them as he desires. * * If you use this software the author politely requests that you inform him via * electronic mail. * */ #include "Obstacle.h" #include #include #include #include //---------------- CLASS OBSTACLE ------------------------------ //---------------- CLASS BoidPolygon ------------------------------ int operator==(const BoidPolygon &a, const BoidPolygon &b) { int i, test; if (a.numvertices != b.numvertices) return 0; // definitely different // Check if all vertices are the same vertices. for (i=0, test=1; test && (i < a.numvertices); i++) test = test && (a.vertex[i]==b.vertex[i]); return(test); } BoidPolygon::BoidPolygon(const BoidPolygon &p) { d = p.d; i1 = p.i1; i2 = p.i2; normal = p.normal; // copy all the vertices over numvertices = p.numvertices; vertex = new BoidVector[numvertices]; for (int i = 0; i < numvertices; i++) vertex[i] = p.vertex[i]; } BoidPolygon::BoidPolygon(int numverts, const BoidVector v0, const BoidVector v1, const BoidVector v2, const BoidVector v3) { BoidVector p; int i; assert(numverts == 4); // Set the number of vertices in the BoidPolygon and allocate the array // of vertices numvertices = numverts; vertex = new BoidVector[numvertices]; // set up the first 3 required vertices vertex[0] = v0; vertex[1] = v1; vertex[2] = v2; // cross product provides normal to poly normal = (vertex[1] - vertex[0]) % (vertex[2] - vertex[0]); normal.Normalize(); // plane of BoidPolygon is: (normal dot vertex[0]) + d = 0 d = float(-vertex[0] * normal); // Find find dominant axis of normal BoidVector, and put the indices // of the plane perpendicular to that axis into i1 and i2. // This tells us what the closest major plane to the BoidPolygon is. // (i.e., i1==0 and i2==2 means closest plane is xz-plane) if (fabs(normal.x) > fabs(normal.y) && fabs(normal.x) > fabs(normal.z)) { i1 = 1; i2 = 2; } else if (fabs(normal.y) >= fabs(normal.x) && fabs(normal.y) >= fabs(normal.z)) { i1 = 0; i2 = 2; } else { i1 = 0; i2 = 1; } // set up the rest of them for (i=3; i= 0.0) && (beta <= 1.0)) { alpha = (v0 - beta*v2)/v1; inside = ((alpha >= 0.0) && (alpha+beta <= 1.0)); } } else { beta = (v0*u1 - u0*v1)/(v2*u1 - u2*v1); if ((beta >= 0.0)&&(beta <= 1.0)) { alpha = (u0 - beta*u2)/u1; inside = ((alpha >= 0.0)&&((alpha+beta) <= 1.0)); } } i++; } while ((inside == 0) && (i < numvertices)); // Ok, so there was an intersection with the BoidPolygon's plane, // but we only care if the intersection was inside the BoidPolygon data.intersectionflag = inside; // make sure to return the normal of the poly that there the ray // might be intersecting. data.normal = normal; return(data); } //---------------- CLASS BOX------------------------------ Box::Box(const Box &b) { for (int i= 0; i < 6; i++) { side[i] = new BoidPolygon(*(b.side[i])); } tlb = b.tlb; brf = b.brf; boundingSphere = new Sphere(*(b.boundingSphere)); } Box::Box(const BoidVector &topLeftBackCorner, const BoidVector &bottomRightFrontCorner) { // When constructing the box, imagine that it is originally positioned // with the bottomRightFrontCorner at (0, 0, 0) and the topLeftBackCorner // at (width, height, length). The top and bottom sides are parallel to // the x-z plane; the left and right are parallel to the y-z plane, and // the back and front are parallel to the x-y plane. // Then it is translated by the amount (x, y, z) where x, y, and z are the // respective components of the bottomRightFrontCorner. tlb = topLeftBackCorner; brf = bottomRightFrontCorner; double width = tlb.x - brf.x; double height = tlb.y - brf.y; double length = tlb.z - brf.z; #ifdef DEBUG_OBSTACLES cerr << "Constructor for Box" << endl; cerr << "length = " << length << endl; cerr << "width = " << width << endl; cerr << "height = " << height << endl; cerr << "tlb = " << tlb << endl; cerr << "brf = " << brf << endl; #endif // Top side[0] = new BoidPolygon(4, brf + BoidVector(0, height, 0), brf + BoidVector(0, height, length), brf + BoidVector(width, height, length), brf + BoidVector(width, height, 0)); // Bottom side[1] = new BoidPolygon(4, brf, brf + BoidVector(0, 0, length), brf + BoidVector(width, 0, length), brf + BoidVector(width, 0, 0)); // Left side[2] = new BoidPolygon(4, brf + BoidVector(width, 0, 0), brf + BoidVector(width, height, 0), tlb, brf + BoidVector(width, 0, length)); // Right side[3] = new BoidPolygon(4, brf + BoidVector(0, 0, 0), brf + BoidVector(0, height, 0), tlb + BoidVector(-width, 0, 0), brf + BoidVector(0, 0, length)); // Front side[4] = new BoidPolygon(4, brf, brf + BoidVector(0, height, 0), brf + BoidVector(width, height, 0), brf + BoidVector(width, 0, 0)); // Back side[5] = new BoidPolygon(4, brf + BoidVector(0, 0, length), brf + BoidVector(0, height, length), brf + BoidVector(width, height, length), brf + BoidVector(width, 0, length)); #ifdef DEBUG_OBSTACLES for (int i = 0; i < 6; i++) cerr << "side[" << i<< "] = " << *side[i] << endl; #endif BoidVector center(brf.x + width/2, brf.y + height/2, brf.z + length/2); double radius = Magnitude(tlb - center); double temp = Magnitude(brf - center); if (temp > radius) radius = temp; boundingSphere = new Sphere(center, radius); #ifdef DEBUG_OBSTACLES cerr << "Bounding sphere = " << *boundingSphere << endl; #endif } ISectData Box::IntersectionWithRay(const BoidVector &raydirection, const BoidVector &rayorigin) const { // Do a quick check to see if the ray intersected the bounding sphere of // this box. If not, then no need to check the sides of the box // individually. ISectData data = boundingSphere->DoesRayIntersect(raydirection, rayorigin); if (data.intersectionflag == 0) { #ifdef DEBUG_OBSTACLES cerr << "Missed Bounding Sphere\n"; #endif return data; } ISectData closestIntersect; double distToClosestIntersect = DBL_MAX; closestIntersect.intersectionflag = 0; // Attempt to find the closest intersection with a side of the box, if // there actually is an intersection. for (int i = 0; i < 6; i++) { #ifdef DEBUG_OBSTACLES cerr << "Checking Poly: " << *side[i] << endl; #endif data = side[i]->DoesRayIntersect(raydirection, rayorigin); if (data.intersectionflag == 1) { #ifdef DEBUG_OBSTACLES cerr << "Found Isect in box\n"; #endif // Find closest intersection, since the ray could intersect multiple // sides. double temp = Magnitude(data.point - rayorigin); if (temp < distToClosestIntersect) { distToClosestIntersect = temp; closestIntersect = data; #ifdef DEBUG_OBSTACLES cerr << "Closest intersect at " << closestIntersect.point << " Normal " << closestIntersect.normal << "\n"; #endif } } } return closestIntersect; } //---------------- CLASS SPHERE------------------------------ ISectData Sphere::IntersectionWithRay(const BoidVector &raydirection, const BoidVector &rayorigin) const { ISectData data; BoidVector rdirection = Direction(raydirection); // only want a unit BoidVector // If the ray starts at the sphere's origin, then calculations // are trivial if (rayorigin == origin) { data.intersectionflag = 1; // The ray MUST intersect the sphere! data.point = radius*rdirection; data.normal = Direction(origin - data.point); return data; // bail out early and save time } data.intersectionflag = 0; // no intersection as default // If ray has no direction, then try to handle this // gracefully by returning with no intersection. // This situation shouldn't happen, though if (rdirection == BoidVector(0,0,0)) return data; // The stuff below is Adapted by Chris Kline 11/28/95 from : // // Intersection Of A Ray With A Sphere // by James Hultquist // from "Graphics Gems", Academic Press, 1990 // // Modified to handle special case when the rayorigin is // inside the sphere, which Hultquist didn't consider (because // he was only interested in raytracing the outside of spheres). // Find component of the ray from rayorigin to the // sphere's origin in the direction specified by // rdirection double v = (origin - rayorigin) * rdirection; // Find the square of the distance from the ray-sphere // intersection to the point on the ray closest to the // sphere's origin double d = pow(radius, 2) - pow(Magnitude(origin - rayorigin), 2) + pow(v, 2); // If d < 0 then there is no intersection if (d < 0) return data; // Otherwise find the intersection point if (Magnitude(rayorigin - origin) < radius) data.point = (v + sqrt(d)) * rdirection + rayorigin; /* inside sphere */ else data.point = (v - sqrt(d)) * rdirection + rayorigin; /* outside sphere */ // An intersection with the ray occured data.intersectionflag = 1; // set the normal at the point of ray-sphere intersection // (the outward-pointing normal) data.normal = Direction(data.point - origin); return data; }