/// /// The box face enumeration indicates which face of the bounds object /// is intersected with in a ray intersect test /// public enum BoxFace { None, PositiveX, PositiveY, PositiveZ, NegativeX, NegativeY, NegativeZ } /// /// The bounds class is a bounding box used for various dynamics and simulation calculations. /// For example, it serves as the collision box for a player in an FPS style game. It is also used to /// define the space contained by an OctreeNode. /// public class Bounds : Node { /* * CONSTANTS */ /// /// cosine of 45 degrees /// const float COSINE_OF_45 = 0.70710678f; /// /// invsere squear root of three /// const float INVERSE_SQRT_OF_THREE = 0.57735027f; /// /// array of vertex normals for any axis-aligned rectilinear volume /// public static Vector3[] vertexNormals = new Vector3[8] { new Vector3(-Bounds.INVERSE_SQRT_OF_THREE,-Bounds.INVERSE_SQRT_OF_THREE,-Bounds.INVERSE_SQRT_OF_THREE), //min vertex new Vector3( Bounds.INVERSE_SQRT_OF_THREE, Bounds.INVERSE_SQRT_OF_THREE, Bounds.INVERSE_SQRT_OF_THREE), new Vector3( Bounds.INVERSE_SQRT_OF_THREE,-Bounds.INVERSE_SQRT_OF_THREE,-Bounds.INVERSE_SQRT_OF_THREE), new Vector3(-Bounds.INVERSE_SQRT_OF_THREE, Bounds.INVERSE_SQRT_OF_THREE, Bounds.INVERSE_SQRT_OF_THREE), new Vector3( Bounds.INVERSE_SQRT_OF_THREE, Bounds.INVERSE_SQRT_OF_THREE,-Bounds.INVERSE_SQRT_OF_THREE), new Vector3(-Bounds.INVERSE_SQRT_OF_THREE,-Bounds.INVERSE_SQRT_OF_THREE, Bounds.INVERSE_SQRT_OF_THREE), new Vector3(-Bounds.INVERSE_SQRT_OF_THREE, Bounds.INVERSE_SQRT_OF_THREE,-Bounds.INVERSE_SQRT_OF_THREE), new Vector3( Bounds.INVERSE_SQRT_OF_THREE,-Bounds.INVERSE_SQRT_OF_THREE, Bounds.INVERSE_SQRT_OF_THREE) //max vertex }; /// /// array of edge normals for any axis-aligned rectilinear volume /// public static Vector3[] edgeNormals = new Vector3[12] { new Vector3(-Bounds.COSINE_OF_45, 0, -Bounds.COSINE_OF_45), new Vector3(0, Bounds.COSINE_OF_45, -Bounds.COSINE_OF_45), new Vector3(Bounds.COSINE_OF_45, 0, -Bounds.COSINE_OF_45), new Vector3(0, -Bounds.COSINE_OF_45, -Bounds.COSINE_OF_45), new Vector3(0, Bounds.COSINE_OF_45, Bounds.COSINE_OF_45), new Vector3(-Bounds.COSINE_OF_45, 0, Bounds.COSINE_OF_45), new Vector3(0, -Bounds.COSINE_OF_45, Bounds.COSINE_OF_45), new Vector3( Bounds.COSINE_OF_45, 0, Bounds.COSINE_OF_45), new Vector3(-Bounds.COSINE_OF_45,-Bounds.COSINE_OF_45, 0), new Vector3(-Bounds.COSINE_OF_45, Bounds.COSINE_OF_45, 0), new Vector3( Bounds.COSINE_OF_45, Bounds.COSINE_OF_45, 0), new Vector3( Bounds.COSINE_OF_45,-Bounds.COSINE_OF_45, 0) }; /// /// face normals for any axis-aligned rectilinear volume /// public static Vector3[] faceNormals = new Vector3[6] { new Vector3(1,0,0), //X face new Vector3(0,1,0), //Y face new Vector3(0,0,1), //Z face new Vector3(-1,0,0), //-X face new Vector3(0,-1,0), //-Y face new Vector3(0,0,-1) //-Z face }; /* * CLASS PROPERTIES */ /// /// the bounding box's minimum point /// protected Vector3 min; /// /// the bounding box's maximum point /// protected Vector3 max; /// /// the dimensions of the box /// protected Vector3 delta; /// /// indicates if the delta or center needs to be updated /// protected bool needsUpdate; /// /// the center point of the bounding box /// protected Vector3 centerPosition; /* * CONSTRUCTOR */ /// /// constructor from min/max vectors /// /// minimum position /// maximum position public Bounds(TwelveCylinderGame newGame, Vector3 min, Vector3 max) : base(newGame) { this.Min = min; this.Max = max; init(); } /// /// initializes the index buffer for rendering the box /// private void init() { needsUpdate = true; } //additional constructors omitted //static constructors omitted //some encapsulators omitted /* * BOX DATA */ /// /// gets the volume of this box /// public float Volume { get { if(needsUpdate) updateData(); //update delta and center position //prevent potential negative volume... return Math.Max(0, delta.X * delta.Y * delta.Z); } } /// /// get the bounding box center point /// public Vector3 Center { get { if (needsUpdate) updateData(); return centerPosition; } } /// /// updates the center point definition /// private void updateData() { delta = max - min; centerPosition = min + (0.5f * delta); needsUpdate = false; } /* * METHODS */ /// /// adds a point to the bounding box /// extending it if needed /// /// the point to add public virtual void AddPoint(Vector3 p) { if (p.X >= max.X) max.X = p.X; if (p.Y >= max.Y) max.Y = p.Y; if (p.Z >= max.Z) max.Z = p.Z; if (p.X <= min.X) min.X = p.X; if (p.Y <= min.Y) min.Y = p.Y; if (p.Z <= min.Z) min.Z = p.Z; needsUpdate = true; } /// /// configures the bounding box to surround all the vertices in the specified array /// /// an array of vertices to be bounded public void BoundVertices(VertexPositionNormalTexture[] verts) { max = -Vector3.Max; min = Vector3.Min; for (int i = 0; i < verts.Length; i++) AddPoint(verts[i].Position); } /// /// inflates the size of the bounding box by the specified buffer /// /// distance to push out each face of the bounding box public virtual void AddBuffer(float buffer) { min.X -= buffer; min.Y -= buffer; min.Z -= buffer; max.X += buffer; max.Y += buffer; max.Z += buffer; needsUpdate = true; } /// /// checks to see if two bounding boxs overlap any volume of space /// /// bounding box to check against this one /// true if any volume of space is shared by the two boxes public bool BoxOverlap(Bounds bb) { //verifty that the two boxes don't miss in any dimension... return (max.X >= bb.min.X && min.X <= bb.max.X && max.Y >= bb.min.Y && min.Y <= bb.max.Y && max.Z >= bb.min.Z && min.Z <= bb.max.Z); } /// /// Calculates and returns the volume of the overlap /// region between two bounding boxes. /// /// the first bounds /// the second bounds /// the volume of the overlap public static float GetVolumeOfOverlap(Bounds b1, Bounds b2) { float[] volumeComp = new float[3]{0,0,0}; float innerMax, innerMin; float min1, min2, max1, max2; //loops through the three dimensions of a vector for(int i = 0; i < 3; i++) { min1 = XNAHelper.GetComponent(b1.Min, i); min2 = XNAHelper.GetComponent(b2.Min, i); max1 = XNAHelper.GetComponent(b1.Max, i); max2 = XNAHelper.GetComponent(b2.Max, i); //determine inner (larger) minimum position if(min1 < min2) innerMin = min2; else innerMin = min1; //determine inner (smaller) maximum position if(max1 < max2) innerMax = max1; else innerMax = max2; if (innerMax < innerMin) return 0; //volumes don't overlap in a dimension //thus the overlap has no volume //overlap dimension is the differene between //the inner two delimitors of the bounds //bounding box volumeComp[i] = innerMax - innerMin; } return (volumeComp[0] * volumeComp[1] * volumeComp[2]); } /// /// check if a point in inside the bounding box /// /// point to test /// true if the point is inside public bool PointInside(Vector3 p) { return (p.X > min.X && p.X <= max.X && p.Y > min.Y && p.Y <= max.Y && p.Z > min.Z && p.Z <= max.Z); } /// /// Tests for ray intersection with this box. /// /// the intersection ray to test /// (must be in the same coordinate space as the box) /// the output distance from the ray /// origin to the first collision point /// the output distance from the ray /// origin to the second collision point /// the box face which the ray intersects with or "none" public BoxFace RayIntersect(OptimizedRay localRay, out float tnear, out float tfar) { tnear = -float.MaxValue; tfar = float.MaxValue; float paramT1, paramT2, temp; int face, nearFace = -1, farFace = -1; float rayDirectionComp, rayWorldDivDComp, rayPositionComp, bmin, bmax; //for each component of a vector (X,Y,Z) for (int vComp = 0; vComp < 3; vComp++) { //vector components rayDirectionComp = XNAHelper.GetComponent(localRay.Direction, vComp); //component from ray direction rayWorldDivDComp = XNAHelper.GetComponent(localRay.WorldDivD, vComp); //component from ray (1 / direction) rayPositionComp = XNAHelper.GetComponent(localRay.Position, vComp); //component from ray origin bmin = XNAHelper.GetComponent(this.min, vComp); //bounding box minimum point position bmax = XNAHelper.GetComponent(this.max, vComp); //bounding box maximum point position if (Math.Abs(rayDirectionComp) < 0.00001f) { //ray is perpendicular to axis if (rayPositionComp < bmin || rayPositionComp > bmax) //ray start is outside of bounding box return BoxFace.None; } else { paramT1 = (bmin - rayPositionComp) * rayWorldDivDComp; paramT2 = (bmax - rayPositionComp) * rayWorldDivDComp; if (paramT1 > paramT2) { temp = paramT1; paramT1 = paramT2; paramT2 = temp; face = vComp; //positive axis direction face } else face = vComp + 3; //negative axis direction face if (paramT1 > tnear) { //near face tnear = paramT1; nearFace = face; } if (paramT2 < tfar) { //far face tfar = paramT2; if (face > 2) farFace = face - 3; else farFace = face + 3; } if (tnear > tfar || tfar < 0.00001f) return BoxFace.None ; //box is behind the ray } } if (tnear > tfar || tfar < 0.00001f) return BoxFace.None; if (tnear < 0.0f) return (BoxFace)Enum.ToObject(typeof(BoxFace), farFace); //intersection with the box from inside else return (BoxFace)Enum.ToObject(typeof(BoxFace), nearFace); //intersection with the box from outside } //bounds rendering omitted }