📄 quake3bsp.cpp
字号:
// Read in the number of leaf brushes and allocate memory for it
m_numOfLeafBrushes = lumps[kLeafBrushes].length / sizeof(int);
m_pLeafBrushes = new int [m_numOfLeafBrushes];
// Finally, read in the leaf brushes for traversing the bsp tree with brushes
fseek(fp, lumps[kLeafBrushes].offset, SEEK_SET);
fread(m_pLeafBrushes, m_numOfLeafBrushes, sizeof(int), fp);
// Close the file
fclose(fp);
// Here we allocate enough bits to store all the faces for our bitset
m_FacesDrawn.Resize(m_numOfFaces);
// Return a success
return true;
}
//////////////////////////// FIND LEAF \\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This returns the leaf our camera is in
/////
//////////////////////////// FIND LEAF \\\\\\\\\\\\\\\\\\\\\\\\\\\*
int CQuake3BSP::FindLeaf(const CVector3 &vPos)
{
int i = 0;
float distance = 0.0f;
// Continue looping until we find a negative index
while(i >= 0)
{
// Get the current node, then find the slitter plane from that
// node's plane index. Notice that we use a constant reference
// to store the plane and node so we get some optimization.
const tBSPNode& node = m_pNodes[i];
const tBSPPlane& plane = m_pPlanes[node.plane];
// Use the Plane Equation (Ax + by + Cz + D = 0) to find if the
// camera is in front of or behind the current splitter plane.
distance = plane.vNormal.x * vPos.x +
plane.vNormal.y * vPos.y +
plane.vNormal.z * vPos.z - plane.d;
// If the camera is in front of the plane
if(distance >= 0)
{
// Assign the current node to the node in front of itself
i = node.front;
}
// Else if the camera is behind the plane
else
{
// Assign the current node to the node behind itself
i = node.back;
}
}
// Return the leaf index (same thing as saying: return -(i + 1)).
return ~i; // Binary operation
}
//////////////////////////// IS CLUSTER VISIBLE \\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This tells us if the "current" cluster can see the "test" cluster
/////
//////////////////////////// IS CLUSTER VISIBLE \\\\\\\\\\\\\\\\\\\\\\\\\\\*
inline int CQuake3BSP::IsClusterVisible(int current, int test)
{
// Make sure we have valid memory and that the current cluster is > 0.
// If we don't have any memory or a negative cluster, return a visibility (1).
if(!m_clusters.pBitsets || current < 0) return 1;
// Use binary math to get the 8 bit visibility set for the current cluster
byte visSet = m_clusters.pBitsets[(current*m_clusters.bytesPerCluster) + (test / 8)];
// Now that we have our vector (bitset), do some bit shifting to find if
// the "test" cluster is visible from the "current" cluster, according to the bitset.
int result = visSet & (1 << ((test) & 7));
// Return the result ( either 1 (visible) or 0 (not visible) )
return ( result );
}
/////////////////////////////////// DOT \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This computers the dot product of 2 vectors
/////
/////////////////////////////////// DOT \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
float Dot(CVector3 vVector1, CVector3 vVector2)
{
// (V1.x * V2.x + V1.y * V2.y + V1.z * V2.z)
return ( (vVector1.x * vVector2.x) + (vVector1.y * vVector2.y) + (vVector1.z * vVector2.z) );
}
/////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
/////////////////////////////////// TRY TO STEP \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This checks a bunch of different heights to see if we can step up
/////
/////////////////////////////////// TRY TO STEP \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
CVector3 CQuake3BSP::TryToStep(CVector3 vStart, CVector3 vEnd)
{
// In this function we loop until we either found a reasonable height
// that we can step over, or find out that we can't step over anything.
// We check 10 times, each time increasing the step size to check for
// a collision. If we don't collide, then we climb over the step.
// Go through and check different heights to step up
for(float height = 1.0f; height <= kMaxStepHeight; height++)
{
// Reset our variables for each loop interation
m_bCollided = false;
m_bTryStep = false;
// Here we add the current height to our y position of a new start and end.
// If these 2 new start and end positions are okay, we can step up.
CVector3 vStepStart = CVector3(vStart.x, vStart.y + height, vStart.z);
CVector3 vStepEnd = CVector3(vEnd.x, vStart.y + height, vEnd.z);
// Test to see if the new position we are trying to step collides or not
CVector3 vStepPosition = Trace(vStepStart, vStepEnd);
// If we didn't collide, we can step!
if(!m_bCollided)
{
// Here we get the current view, then increase the y value by the current height.
// This makes it so when we are walking up the stairs, our view follows our step
// height and doesn't sag down as we walk up the stairs.
CVector3 vNewView = g_Camera.View();
g_Camera.SetView(CVector3(vNewView.x, vNewView.y + height, vNewView.z));
// Return the current position since we stepped up somewhere
return vStepPosition;
}
}
// If we can't step, then we just return the original position of the collision
return vStart;
}
/////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
/////////////////////////////////// TRACE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This takes a start and end position (general) to test against the BSP brushes
/////
/////////////////////////////////// TRACE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
CVector3 CQuake3BSP::Trace(CVector3 vStart, CVector3 vEnd)
{
// Initially we set our trace ratio to 1.0f, which means that we don't have
// a collision or intersection point, so we can move freely.
m_traceRatio = 1.0f;
// We start out with the first node (0), setting our start and end ratio to 0 and 1.
// We will recursively go through all of the nodes to see which brushes we should check.
CheckNode(0, 0.0f, 1.0f, vStart, vEnd);
// If the traceRatio is STILL 1.0f, then we never collided and just return our end position
if(m_traceRatio == 1.0f)
{
return vEnd;
}
else // Else COLLISION!!!!
{
// Set our new position to a position that is right up to the brush we collided with
CVector3 vNewPosition = vStart + ((vEnd - vStart) * m_traceRatio);
// Get the distance from the end point to the new position we just got
CVector3 vMove = vEnd - vNewPosition;
// Get the distance we need to travel backwards to the new slide position.
// This is the distance of course along the normal of the plane we collided with.
float distance = Dot(vMove, m_vCollisionNormal);
// Get the new end position that we will end up (the slide position).
CVector3 vEndPosition = vEnd - m_vCollisionNormal*distance;
// Since we got a new position for our sliding vector, we need to check
// to make sure that new sliding position doesn't collide with anything else.
vNewPosition = Trace(vNewPosition, vEndPosition);
/////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
//
if(m_vCollisionNormal.y > 0.2f || m_bGrounded)
m_bGrounded = true;
else
m_bGrounded = false;
/////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
// Return the new position to be used by our camera (or player)
return vNewPosition;
}
}
/////////////////////////////////// TRACE RAY \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This takes a start and end position (ray) to test against the BSP brushes
/////
/////////////////////////////////// TRACE RAY \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
CVector3 CQuake3BSP::TraceRay(CVector3 vStart, CVector3 vEnd)
{
// We don't use this function, but we set it up to allow us to just check a
// ray with the BSP tree brushes. We do so by setting the trace type to TYPE_RAY.
m_traceType = TYPE_RAY;
// Run the normal Trace() function with our start and end
// position and return a new position
return Trace(vStart, vEnd);
}
/////////////////////////////////// TRACE SPHERE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This tests a sphere around our movement vector against the BSP brushes for collision
/////
/////////////////////////////////// TRACE SPHERE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
CVector3 CQuake3BSP::TraceSphere(CVector3 vStart, CVector3 vEnd, float radius)
{
// Here we initialize the type of trace (SPHERE) and initialize other data
m_traceType = TYPE_SPHERE;
m_bCollided = false;
/////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
// Here we initialize our variables for a new round of collision checks
m_bTryStep = false;
m_bGrounded = false;
/////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
m_traceRadius = radius;
// Get the new position that we will return to the camera or player
CVector3 vNewPosition = Trace(vStart, vEnd);
/////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
// Let's check to see if we collided with something and we should try to step up
if(m_bCollided && m_bTryStep)
{
// Try and step up what we collided with
vNewPosition = TryToStep(vNewPosition, vEnd);
}
/////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
// Return the new position to be changed for the camera or player
return vNewPosition;
}
/////////////////////////////////// TRACE BOX \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This takes a start and end position to test a AABB (box) against the BSP brushes
/////
/////////////////////////////////// TRACE BOX \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
CVector3 CQuake3BSP::TraceBox(CVector3 vStart, CVector3 vEnd, CVector3 vMin, CVector3 vMax)
{
m_traceType = TYPE_BOX; // Set the trace type to a BOX
m_vTraceMaxs = vMax; // Set the max value of our AABB
m_vTraceMins = vMin; // Set the min value of our AABB
m_bCollided = false; // Reset the collised flag
/////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
// Here we initialize our variables for a new round of collision checks
m_bTryStep = false;
m_bGrounded = false;
/////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
// Grab the extend of our box (the largest size for each x, y, z axis)
m_vExtents = CVector3(-m_vTraceMins.x > m_vTraceMaxs.x ? -m_vTraceMins.x : m_vTraceMaxs.x,
-m_vTraceMins.y > m_vTraceMaxs.y ? -m_vTraceMins.y : m_vTraceMaxs.y,
-m_vTraceMins.z > m_vTraceMaxs.z ? -m_vTraceMins.z : m_vTraceMaxs.z);
// Check if our movement collided with anything, then get back our new position
CVector3 vNewPosition = Trace(vStart, vEnd);
/////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
// Let's check to see if we collided with something and we should try to step up
if(m_bCollided && m_bTryStep)
{
// Try and step up what we collided with
vNewPosition = TryToStep(vNewPosition, vEnd);
}
/////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
// Return our new position
return vNewPosition;
}
/////////////////////////////////// CHECK NODE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This traverses the BSP to find the brushes closest to our position
/////
/////////////////////////////////// CHECK NODE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
void CQuake3BSP::CheckNode(int nodeIndex, float startRatio, float endRatio, CVector3 vStart, CVector3 vEnd)
{
// Check if the next node is a leaf
if(nodeIndex < 0)
{
// If this node in the BSP is a leaf, we need to negate and add 1 to offset
// the real node index into the m_pLeafs[] array. You could also do [~nodeIndex].
tBSPLeaf *pLeaf = &m_pLeafs[-(nodeIndex + 1)];
// We have a leaf, so let's go through all of the brushes for that leaf
for(int i = 0; i < pLeaf->numOfLeafBrushes; i++)
{
// Get the current brush that we going to check
tBSPBrush *pBrush = &m_pBrushes[m_pLeafBrushes[pLeaf->leafBrush + i]];
// Check if we have brush sides and the current brush is solid and collidable
if((pBrush->numOfBrushSides > 0) && (m_pTextures[pBrush->textureID].textureType & 1))
{
// Now we delve into the dark depths of the real calculations for collision.
// We can now check the movement vector against our brush planes.
CheckBrush(pBrush, vStart, vEnd);
}
}
// Since we found the brushes, we can go back up and stop recursing at this level
return;
}
// Grad the next node to work with and grab this node's plane data
tBSPNode *pNode = &m_pNodes[nodeIndex];
tBSPPlane *pPlane = &m_pPlanes[pNode->plane];
// Here we use the plane equation to find out where our initial start position is
// according the the node that we are checking. We then grab the same info for the end pos.
float startDistance = Dot(vStart, pPlane->vNormal) - pPlane->d;
float endDistance = Dot(vEnd, pPlane->vNormal) - pPlane->d;
float offset = 0.0f;
// If we are doing sphere collision, include an offset for our collision tests below
if(m_traceType == TYPE_SPHERE)
offset = m_traceRadius;
// Here we check to see if we are working with a BOX or not
else if(m_traceType == TYPE_BOX)
{
// Get the distance our AABB is from the current splitter plane
offset = (float)(fabs( m_vExtents.x * pPlane->vNormal.x ) +
fabs( m_vExtents.y * pPlane->vNormal.y ) +
fabs( m_vExtents.z * pPlane->vNormal.z ) );
}
// Here we check to see if the start and end point are both in front of the current node.
// If so, we want to check all of the nodes in front of this current splitter plane.
if(startDistance >= offset && endDistance >= offset)
{
// Traverse the BSP tree on all the nodes in front of this current splitter plane
CheckNode(pNode->front, startDistance, endDistance, vStart, vEnd);
}
// If both points are behind the current splitter plane, traverse down the back nodes
else if(startDistance < -offset && endDistance < -offset)
{
// Traverse the BSP tree on all the nodes in back of this current splitter plane
CheckNode(pNode->back, startDistance, endDistance, vStart, vEnd);
}
else
{
// If we get here, then our ray needs to be split in half to check the nodes
// on both sides of the current splitter plane. Thus we create 2 ratios.
float Ratio1 = 1.0f, Ratio2 = 0.0f, middleRatio = 0.0f;
CVector3 vMiddle; // This stores the middle point for our split ray
// Start of the side as the front side to check
int side = pNode->front;
// Here we check to see if the start point is in back of the plane (negative)
if(startDistance < endDistance)
{
// Since the start position is in back, let's check the back nodes
side = pNode->back;
// Here we create 2 ratios that hold a distance from the start to the
// extent closest to the start (take into account a sphere and epsilon).
float inverseDistance = 1.0f / (startDistance - endDistance);
Ratio1 = (startDistance - offset - kEpsilon) * inverseDistance;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -