/////////////////////////////////////////////////////////////////////////// // // Copyright (c) 2002, Industrial Light & Magic, a division of Lucas // Digital Ltd. LLC // // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Industrial Light & Magic nor the names of // its contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // /////////////////////////////////////////////////////////////////////////// #ifndef INCLUDED_IMATHBOXALGO_H #define INCLUDED_IMATHBOXALGO_H //--------------------------------------------------------------------------- // // This file contains algorithms applied to or in conjunction // with bounding boxes (Imath::Box). These algorithms require // more headers to compile. The assumption made is that these // functions are called much less often than the basic box // functions or these functions require more support classes. // // Contains: // // T clip(const T& in, const Box& box) // // Vec3 closestPointOnBox(const Vec3&, const Box>& ) // // Vec3 closestPointInBox(const Vec3&, const Box>& ) // // void transform(Box>&, const Matrix44&) // // bool findEntryAndExitPoints(const Line &line, // const Box< Vec3 > &box, // Vec3 &enterPoint, // Vec3 &exitPoint) // // bool intersects(const Box> &box, // const Line3 &line, // Vec3 result) // // bool intersects(const Box> &box, const Line3 &line) // //--------------------------------------------------------------------------- #include #include #include #include namespace Imath { template inline T clip(const T& in, const Box& box) { // // Clip a point so that it lies inside the given bbox // T out; for (int i=0; i<(int)box.min.dimensions(); i++) { if (in[i] < box.min[i]) out[i] = box.min[i]; else if (in[i] > box.max[i]) out[i] = box.max[i]; else out[i] = in[i]; } return out; } // // Return p if p is inside the box. // template Vec3 closestPointInBox(const Vec3& p, const Box< Vec3 >& box ) { Imath::V3f b; if (p.x < box.min.x) b.x = box.min.x; else if (p.x > box.max.x) b.x = box.max.x; else b.x = p.x; if (p.y < box.min.y) b.y = box.min.y; else if (p.y > box.max.y) b.y = box.max.y; else b.y = p.y; if (p.z < box.min.z) b.z = box.min.z; else if (p.z > box.max.z) b.z = box.max.z; else b.z = p.z; return b; } template Vec3 closestPointOnBox(const Vec3& pt, const Box< Vec3 >& box ) { // // This sucker is specialized to work with a Vec3f and a box // made of Vec3fs. // Vec3 result; // trivial cases first if (box.isEmpty()) return pt; else if (pt == box.center()) { // middle of z side result[0] = (box.max[0] + box.min[0])/2.0; result[1] = (box.max[1] + box.min[1])/2.0; result[2] = box.max[2]; } else { // Find the closest point on a unit box (from -1 to 1), // then scale up. // Find the vector from center to the point, then scale // to a unit box. Vec3 vec = pt - box.center(); T sizeX = box.max[0]-box.min[0]; T sizeY = box.max[1]-box.min[1]; T sizeZ = box.max[2]-box.min[2]; T halfX = sizeX/2.0; T halfY = sizeY/2.0; T halfZ = sizeZ/2.0; if (halfX > 0.0) vec[0] /= halfX; if (halfY > 0.0) vec[1] /= halfY; if (halfZ > 0.0) vec[2] /= halfZ; // Side to snap side that has greatest magnitude in the vector. Vec3 mag; mag[0] = fabs(vec[0]); mag[1] = fabs(vec[1]); mag[2] = fabs(vec[2]); result = mag; // Check if beyond corners if (result[0] > 1.0) result[0] = 1.0; if (result[1] > 1.0) result[1] = 1.0; if (result[2] > 1.0) result[2] = 1.0; // snap to appropriate side if ((mag[0] > mag[1]) && (mag[0] > mag[2])) { result[0] = 1.0; } else if ((mag[1] > mag[0]) && (mag[1] > mag[2])) { result[1] = 1.0; } else if ((mag[2] > mag[0]) && (mag[2] > mag[1])) { result[2] = 1.0; } else if ((mag[0] == mag[1]) && (mag[0] == mag[2])) { // corner result = Vec3(1,1,1); } else if (mag[0] == mag[1]) { // edge parallel with z result[0] = 1.0; result[1] = 1.0; } else if (mag[0] == mag[2]) { // edge parallel with y result[0] = 1.0; result[2] = 1.0; } else if (mag[1] == mag[2]) { // edge parallel with x result[1] = 1.0; result[2] = 1.0; } // Now make everything point the right way for (int i=0; i < 3; i++) { if (vec[i] < 0.0) result[i] = -result[i]; } // scale back up and move to center result[0] *= halfX; result[1] *= halfY; result[2] *= halfZ; result += box.center(); } return result; } template Box< Vec3 > transform(const Box< Vec3 >& box, const Matrix44& m) { // Transforms Box3f by matrix, enlarging Box3f to contain result. // Clever method courtesy of Graphics Gems, pp. 548-550 // // This works for projection matrices as well as simple affine // transformations. Coordinates of the box are rehomogenized if there // is a projection matrix // a transformed empty box is still empty if (box.isEmpty()) return box; // If the last column is close enuf to ( 0 0 0 1 ) then we use the // fast, affine version. The tricky affine method could maybe be // extended to deal with the projection case as well, but its not // worth it right now. if (m[0][3] * m[0][3] + m[1][3] * m[1][3] + m[2][3] * m[2][3] + (1.0 - m[3][3]) * (1.0 - m[3][3]) < 0.00001) { // Affine version, use the Graphics Gems hack int i, j; Box< Vec3 > newBox; for (i = 0; i < 3; i++) { newBox.min[i] = newBox.max[i] = (S) m[3][i]; for (j = 0; j < 3; j++) { float a, b; a = (S) m[j][i] * box.min[j]; b = (S) m[j][i] * box.max[j]; if (a < b) { newBox.min[i] += a; newBox.max[i] += b; } else { newBox.min[i] += b; newBox.max[i] += a; } } } return newBox; } // This is a projection matrix. Do things the naive way. Vec3 points[8]; /* Set up the eight points at the corners of the extent */ points[0][0] = points[1][0] = points[2][0] = points[3][0] = box.min[0]; points[4][0] = points[5][0] = points[6][0] = points[7][0] = box.max[0]; points[0][1] = points[1][1] = points[4][1] = points[5][1] = box.min[1]; points[2][1] = points[3][1] = points[6][1] = points[7][1] = box.max[1]; points[0][2] = points[2][2] = points[4][2] = points[6][2] = box.min[2]; points[1][2] = points[3][2] = points[5][2] = points[7][2] = box.max[2]; Box< Vec3 > newBox; for (int i = 0; i < 8; i++) newBox.extendBy(points[i] * m); return newBox; } template Box< Vec3 > affineTransform(const Box< Vec3 > &bbox, const Matrix44 &M) { float min0, max0, min1, max1, min2, max2, a, b; float min0new, max0new, min1new, max1new, min2new, max2new; min0 = bbox.min[0]; max0 = bbox.max[0]; min1 = bbox.min[1]; max1 = bbox.max[1]; min2 = bbox.min[2]; max2 = bbox.max[2]; min0new = max0new = M[3][0]; a = M[0][0] * min0; b = M[0][0] * max0; if (a < b) { min0new += a; max0new += b; } else { min0new += b; max0new += a; } a = M[1][0] * min1; b = M[1][0] * max1; if (a < b) { min0new += a; max0new += b; } else { min0new += b; max0new += a; } a = M[2][0] * min2; b = M[2][0] * max2; if (a < b) { min0new += a; max0new += b; } else { min0new += b; max0new += a; } min1new = max1new = M[3][1]; a = M[0][1] * min0; b = M[0][1] * max0; if (a < b) { min1new += a; max1new += b; } else { min1new += b; max1new += a; } a = M[1][1] * min1; b = M[1][1] * max1; if (a < b) { min1new += a; max1new += b; } else { min1new += b; max1new += a; } a = M[2][1] * min2; b = M[2][1] * max2; if (a < b) { min1new += a; max1new += b; } else { min1new += b; max1new += a; } min2new = max2new = M[3][2]; a = M[0][2] * min0; b = M[0][2] * max0; if (a < b) { min2new += a; max2new += b; } else { min2new += b; max2new += a; } a = M[1][2] * min1; b = M[1][2] * max1; if (a < b) { min2new += a; max2new += b; } else { min2new += b; max2new += a; } a = M[2][2] * min2; b = M[2][2] * max2; if (a < b) { min2new += a; max2new += b; } else { min2new += b; max2new += a; } Box< Vec3 > xbbox; xbbox.min[0] = min0new; xbbox.max[0] = max0new; xbbox.min[1] = min1new; xbbox.max[1] = max1new; xbbox.min[2] = min2new; xbbox.max[2] = max2new; return xbbox; } template bool findEntryAndExitPoints(const Line3& line, const Box >& box, Vec3 &enterPoint, Vec3 &exitPoint) { if ( box.isEmpty() ) return false; if ( line.distanceTo(box.center()) > box.size().length()/2. ) return false; Vec3 points[8], inter, bary; Plane3 plane; int i, v0, v1, v2; bool front = false, valid, validIntersection = false; // set up the eight coords of the corners of the box for(i = 0; i < 8; i++) { points[i].setValue( i & 01 ? box.min[0] : box.max[0], i & 02 ? box.min[1] : box.max[1], i & 04 ? box.min[2] : box.max[2]); } // intersect the 12 triangles. for(i = 0; i < 12; i++) { switch(i) { case 0: v0 = 2; v1 = 1; v2 = 0; break; // +z case 1: v0 = 2; v1 = 3; v2 = 1; break; case 2: v0 = 4; v1 = 5; v2 = 6; break; // -z case 3: v0 = 6; v1 = 5; v2 = 7; break; case 4: v0 = 0; v1 = 6; v2 = 2; break; // -x case 5: v0 = 0; v1 = 4; v2 = 6; break; case 6: v0 = 1; v1 = 3; v2 = 7; break; // +x case 7: v0 = 1; v1 = 7; v2 = 5; break; case 8: v0 = 1; v1 = 4; v2 = 0; break; // -y case 9: v0 = 1; v1 = 5; v2 = 4; break; case 10: v0 = 2; v1 = 7; v2 = 3; break; // +y case 11: v0 = 2; v1 = 6; v2 = 7; break; } if((valid=intersect (line, points[v0], points[v1], points[v2], inter, bary, front)) == true) { if(front == true) { enterPoint = inter; validIntersection = valid; } else { exitPoint = inter; validIntersection = valid; } } } return validIntersection; } template bool intersects(const Box< Vec3 > &box, const Line3 &line, Vec3 &result) { /* Fast Ray-Box Intersection by Andrew Woo from "Graphics Gems", Academic Press, 1990 */ const int right = 0; const int left = 1; const int middle = 2; const Vec3 &minB = box.min; const Vec3 &maxB = box.max; const Vec3 &origin = line.pos; const Vec3 &dir = line.dir; bool inside = true; char quadrant[3]; int whichPlane; float maxT[3]; float candidatePlane[3]; /* Find candidate planes; this loop can be avoided if rays cast all from the eye(assume perpsective view) */ for (int i=0; i<3; i++) { if(origin[i] < minB[i]) { quadrant[i] = left; candidatePlane[i] = minB[i]; inside = false; } else if (origin[i] > maxB[i]) { quadrant[i] = right; candidatePlane[i] = maxB[i]; inside = false; } else { quadrant[i] = middle; } } /* Ray origin inside bounding box */ if ( inside ) { result = origin; return true; } /* Calculate T distances to candidate planes */ for (int i = 0; i < 3; i++) { if (quadrant[i] != middle && dir[i] !=0.) { maxT[i] = (candidatePlane[i]-origin[i]) / dir[i]; } else { maxT[i] = -1.; } } /* Get largest of the maxT's for final choice of intersection */ whichPlane = 0; for (int i = 1; i < 3; i++) { if (maxT[whichPlane] < maxT[i]) { whichPlane = i; } } /* Check final candidate actually inside box */ if (maxT[whichPlane] < 0.) return false; for (int i = 0; i < 3; i++) { if (whichPlane != i) { result[i] = origin[i] + maxT[whichPlane] *dir[i]; if ((quadrant[i] == right && result[i] < minB[i]) || (quadrant[i] == left && result[i] > maxB[i])) { return false; /* outside box */ } } else { result[i] = candidatePlane[i]; } } return true; } template bool intersects(const Box< Vec3 > &box, const Line3 &line) { Vec3 ignored; return intersects(box,line,ignored); } } // namespace Imath #endif