📄 quake3bsp.cpp
字号:
Ratio2 = (startDistance + offset + kEpsilon) * inverseDistance;
}
// Check if the starting point is greater than the end point (positive)
else if(startDistance > endDistance)
{
// This means that we are going to recurse down the front nodes first.
// We do the same thing as above and get 2 ratios for split ray.
float inverseDistance = 1.0f / (startDistance - endDistance);
Ratio1 = (startDistance + offset + kEpsilon) * inverseDistance;
Ratio2 = (startDistance - offset - kEpsilon) * inverseDistance;
}
// Make sure that we have valid numbers and not some weird float problems.
// This ensures that we have a value from 0 to 1 as a good ratio should be :)
if (Ratio1 < 0.0f) Ratio1 = 0.0f;
else if (Ratio1 > 1.0f) Ratio1 = 1.0f;
if (Ratio2 < 0.0f) Ratio2 = 0.0f;
else if (Ratio2 > 1.0f) Ratio2 = 1.0f;
// Just like we do in the Trace() function, we find the desired middle
// point on the ray, but instead of a point we get a middleRatio percentage.
middleRatio = startRatio + ((endRatio - startRatio) * Ratio1);
vMiddle = vStart + ((vEnd - vStart) * Ratio1);
// Now we recurse on the current side with only the first half of the ray
CheckNode(side, startRatio, middleRatio, vStart, vMiddle);
// Now we need to make a middle point and ratio for the other side of the node
middleRatio = startRatio + ((endRatio - startRatio) * Ratio2);
vMiddle = vStart + ((vEnd - vStart) * Ratio2);
// Depending on which side should go last, traverse the bsp with the
// other side of the split ray (movement vector).
if(side == pNode->back)
CheckNode(pNode->front, middleRatio, endRatio, vMiddle, vEnd);
else
CheckNode(pNode->back, middleRatio, endRatio, vMiddle, vEnd);
}
}
/////////////////////////////////// CHECK BRUSH \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This checks our movement vector against all the planes of the brush
/////
/////////////////////////////////// CHECK BRUSH \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
void CQuake3BSP::CheckBrush(tBSPBrush *pBrush, CVector3 vStart, CVector3 vEnd)
{
float startRatio = -1.0f; // Like in BrushCollision.htm, start a ratio at -1
float endRatio = 1.0f; // Set the end ratio to 1
bool startsOut = false; // This tells us if we starting outside the brush
// Go through all of the brush sides and check collision against each plane
for(int i = 0; i < pBrush->numOfBrushSides; i++)
{
// Here we grab the current brush side and plane in this brush
tBSPBrushSide *pBrushSide = &m_pBrushSides[pBrush->brushSide + i];
tBSPPlane *pPlane = &m_pPlanes[pBrushSide->plane];
// Let's store a variable for the offset (like for sphere collision)
float offset = 0.0f;
// If we are testing sphere collision we need to add the sphere radius
if(m_traceType == TYPE_SPHERE)
offset = m_traceRadius;
// Test the start and end points against the current plane of the brush side.
// Notice that we add an offset to the distance from the origin, which makes
// our sphere collision work.
float startDistance = Dot(vStart, pPlane->vNormal) - (pPlane->d + offset);
float endDistance = Dot(vEnd, pPlane->vNormal) - (pPlane->d + offset);
// Store the offset that we will check against the plane
CVector3 vOffset = CVector3(0, 0, 0);
// If we are using AABB collision
if(m_traceType == TYPE_BOX)
{
// Grab the closest corner (x, y, or z value) that is closest to the plane
vOffset.x = (pPlane->vNormal.x < 0) ? m_vTraceMaxs.x : m_vTraceMins.x;
vOffset.y = (pPlane->vNormal.y < 0) ? m_vTraceMaxs.y : m_vTraceMins.y;
vOffset.z = (pPlane->vNormal.z < 0) ? m_vTraceMaxs.z : m_vTraceMins.z;
// Use the plane equation to grab the distance our start position is from the plane.
startDistance = Dot(vStart + vOffset, pPlane->vNormal) - pPlane->d;
// Get the distance our end position is from this current brush plane
endDistance = Dot(vEnd + vOffset, pPlane->vNormal) - pPlane->d;
}
// Make sure we start outside of the brush's volume
if(startDistance > 0) startsOut = true;
// Stop checking since both the start and end position are in front of the plane
if(startDistance > 0 && endDistance > 0)
return;
// Continue on to the next brush side if both points are behind or on the plane
if(startDistance <= 0 && endDistance <= 0)
continue;
// If the distance of the start point is greater than the end point, we have a collision!
if(startDistance > endDistance)
{
// This gets a ratio from our starting point to the approximate collision spot
float Ratio1 = (startDistance - kEpsilon) / (startDistance - endDistance);
// If this is the first time coming here, then this will always be true,
if(Ratio1 > startRatio)
{
// Set the startRatio (currently the closest collision distance from start)
startRatio = Ratio1;
m_bCollided = true; // Let us know we collided!
// Store the normal of plane that we collided with for sliding calculations
m_vCollisionNormal = pPlane->vNormal;
/////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
// This checks first tests if we actually moved along the x or z-axis,
// meaning that we went in a direction somewhere. The next check makes
// sure that we don't always check to step every time we collide. If
// the normal of the plane has a Y value of 1, that means it's just the
// flat ground and we don't need to check if we can step over it, it's flat!
if((vStart.x != vEnd.x || vStart.z != vEnd.z) && pPlane->vNormal.y != 1)
{
// We can try and step over the wall we collided with
m_bTryStep = true;
}
// Here we make sure that we don't slide slowly down walls when we
// jump and collide into them. We only want to say that we are on
// the ground if we actually have stopped from falling. A wall wouldn't
// have a high y value for the normal, it would most likely be 0.
if(m_vCollisionNormal.y >= 0.2f)
m_bGrounded = true;
/////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
}
}
else
{
// Get the ratio of the current brush side for the endRatio
float Ratio = (startDistance + kEpsilon) / (startDistance - endDistance);
// If the ratio is less than the current endRatio, assign a new endRatio.
// This will usually always be true when starting out.
if(Ratio < endRatio)
endRatio = Ratio;
}
}
// If we didn't start outside of the brush we don't want to count this collision - return;
if(startsOut == false)
{
return;
}
// If our startRatio is less than the endRatio there was a collision!!!
if(startRatio < endRatio)
{
// Make sure the startRatio moved from the start and check if the collision
// ratio we just got is less than the current ratio stored in m_traceRatio.
// We want the closest collision to our original starting position.
if(startRatio > -1 && startRatio < m_traceRatio)
{
// If the startRatio is less than 0, just set it to 0
if(startRatio < 0)
startRatio = 0;
// Store the new ratio in our member variable for later
m_traceRatio = startRatio;
}
}
}
//////////////////////////// RENDER FACE \\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This renders a face, determined by the passed in index
/////
//////////////////////////// RENDER FACE \\\\\\\\\\\\\\\\\\\\\\\\\\\*
void CQuake3BSP::RenderFace(int faceIndex)
{
// Here we grab the face from the index passed in
tBSPFace *pFace = &m_pFaces[faceIndex];
// Assign our array of face vertices for our vertex arrays and enable vertex arrays
glVertexPointer(3, GL_FLOAT, sizeof(tBSPVertex), &(m_pVerts[pFace->startVertIndex].vPosition));
glEnableClientState(GL_VERTEX_ARRAY);
// If we want to render the textures
if(g_bTextures)
{
// Set the current pass as the first texture (For multi-texturing)
glActiveTextureARB(GL_TEXTURE0_ARB);
// Give OpenGL the texture coordinates for the first texture, and enable that texture
glClientActiveTextureARB(GL_TEXTURE0_ARB);
glTexCoordPointer(2, GL_FLOAT, sizeof(tBSPVertex),
&(m_pVerts[pFace->startVertIndex].vTextureCoord));
// Set our vertex array client states for allowing texture coordinates
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
// Turn on texture arrays for the first pass
glClientActiveTextureARB(GL_TEXTURE0_ARB);
// Turn on texture mapping and bind the face's texture map
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, m_textures[pFace->textureID]);
}
if(g_bLightmaps)
{
// Set the current pass as the second lightmap texture_
glActiveTextureARB(GL_TEXTURE1_ARB);
// Turn on texture arrays for the second lightmap pass
glClientActiveTextureARB(GL_TEXTURE1_ARB);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
// Next, we need to specify the UV coordinates for our lightmaps. This is done
// by switching to the second texture and giving OpenGL our lightmap array.
glClientActiveTextureARB(GL_TEXTURE1_ARB);
glTexCoordPointer(2, GL_FLOAT, sizeof(tBSPVertex),
&(m_pVerts[pFace->startVertIndex].vLightmapCoord));
// Turn on texture mapping and bind the face's lightmap over the texture
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, m_lightmaps[pFace->lightmapID]);
}
// Render our current face to the screen with vertex arrays
glDrawElements(GL_TRIANGLES, pFace->numOfIndices, GL_UNSIGNED_INT, &(m_pIndices[pFace->startIndex]) );
}
//////////////////////////// RENDER LEVEL \\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// Goes through all of the faces and draws them if the type is FACE_POLYGON
/////
//////////////////////////// RENDER LEVEL \\\\\\\\\\\\\\\\\\\\\\\\\\\*
void CQuake3BSP::RenderLevel(const CVector3 &vPos)
{
// Reset our bitset so all the slots are zero.
m_FacesDrawn.ClearAll();
// Grab the leaf index that our camera is in
int leafIndex = FindLeaf(vPos);
// Grab the cluster that is assigned to the leaf
int cluster = m_pLeafs[leafIndex].cluster;
// Initialize our counter variables (start at the last leaf and work down)
int i = m_numOfLeafs;
g_VisibleFaces = 0;
// Go through all the leafs and check their visibility
while(i--)
{
// Get the current leaf that is to be tested for visibility from our camera's leaf
tBSPLeaf *pLeaf = &(m_pLeafs[i]);
// If the current leaf can't be seen from our cluster, go to the next leaf
if(!IsClusterVisible(cluster, pLeaf->cluster))
continue;
// If the current leaf is not in the camera's frustum, go to the next leaf
if(!g_Frustum.BoxInFrustum((float)pLeaf->min.x, (float)pLeaf->min.y, (float)pLeaf->min.z,
(float)pLeaf->max.x, (float)pLeaf->max.y, (float)pLeaf->max.z))
continue;
// If we get here, the leaf we are testing must be visible in our camera's view.
// Get the number of faces that this leaf is in charge of.
int faceCount = pLeaf->numOfLeafFaces;
// Loop through and render all of the faces in this leaf
while(faceCount--)
{
// Grab the current face index from our leaf faces array
int faceIndex = m_pLeafFaces[pLeaf->leafface + faceCount];
// Before drawing this face, make sure it's a normal polygon
if(m_pFaces[faceIndex].type != FACE_POLYGON) continue;
// Since many faces are duplicated in other leafs, we need to
// make sure this face already hasn't been drawn.
if(!m_FacesDrawn.On(faceIndex))
{
// Increase the rendered face count to display for fun
g_VisibleFaces++;
// Set this face as drawn and render it
m_FacesDrawn.Set(faceIndex);
RenderFace(faceIndex);
}
}
}
}
//////////////////////////// DESTROY \\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This cleans up our object and frees allocated memory
/////
//////////////////////////// DESTROY \\\\\\\\\\\\\\\\\\\\\\\\\\\*
void CQuake3BSP::Destroy()
{
// If we still have valid memory for our vertices, free them
if(m_pVerts)
{
delete [] m_pVerts; m_pVerts = NULL;
}
// If we still have valid memory for our faces, free them
if(m_pFaces)
{
delete [] m_pFaces; m_pFaces = NULL;
}
// If we still have valid memory for our indices, free them
if(m_pIndices)
{
delete [] m_pIndices;
m_pIndices = NULL;
}
// If we still have valid memory for our nodes, free them
if(m_pNodes)
{
delete [] m_pNodes; m_pNodes = NULL;
}
// If we still have valid memory for our leafs, free them
if(m_pLeafs)
{
delete [] m_pLeafs; m_pLeafs = NULL;
}
// If we still have valid memory for our leaf faces, free them
if(m_pLeafFaces)
{
delete [] m_pLeafFaces; m_pLeafFaces = NULL;
}
// If we still have valid memory for our planes, free them
if(m_pPlanes)
{
delete [] m_pPlanes; m_pPlanes = NULL;
}
// If we still have valid memory for our clusters, free them
if(m_clusters.pBitsets)
{
delete [] m_clusters.pBitsets; m_clusters.pBitsets = NULL;
}
// If we still have valid memory for our brushes, free them
if(m_pBrushes)
{
delete [] m_pBrushes; m_pBrushes = NULL;
}
// If we still have valid memory for our brush sides, free them
if(m_pBrushSides)
{
delete [] m_pBrushSides; m_pBrushSides = NULL;
}
// If we still have valid memory for our leaf brushes, free them
if(m_pLeafBrushes)
{
delete [] m_pLeafBrushes; m_pLeafBrushes = NULL;
}
// If we still have valid memory for our BSP texture info, free it
if(m_pTextures)
{
delete [] m_pTextures; m_pTextures = NULL;
}
// Free all the textures
glDeleteTextures(m_numOfTextures, m_textures);
// Delete the lightmap textures
glDeleteTextures(m_numOfLightmaps, m_lightmaps);
}
//////////////////////////// ~CQUAKE3BSP \\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This is our deconstructor that is called when the object is destroyed
/////
//////////////////////////// ~CQUAKE3BSP \\\\\\\\\\\\\\\\\\\\\\\\\\\*
CQuake3BSP::~CQuake3BSP()
{
// Call our destroy function
Destroy();
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -