///
/// 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
}