csgo-2018-source/meshutils/clipmesh.cpp

274 lines
8.6 KiB
C++
Raw Permalink Normal View History

2021-07-25 12:11:47 +08:00
//=========== Copyright <20> Valve Corporation, All rights reserved. ============//
//
// Purpose: Mesh clipping operations.
//
//===========================================================================//
#include "mesh.h"
#include "tier1/utlbuffer.h"
void ClipTriangle( float *pBackOut, float *pFrontOut, int *pNumBackOut, int *pNumFrontOut, int nStrideFloats,
float **ppVertsIn, Vector4D &vClipPlane )
{
int nBack = 0;
int nFront = 0;
Vector vPlaneNormal = vClipPlane.AsVector3D();
const int nVerts = 3;
for( int v=0; v<nVerts; ++v )
{
int nxtVert = ( v + 1 ) % nVerts;
Vector vPos1 = *(Vector*)ppVertsIn[ v ];
Vector vPos2 = *(Vector*)ppVertsIn[ nxtVert ];
float flDot1 = DotProduct( vPos1, vPlaneNormal ) + vClipPlane.w;
float flDot2 = DotProduct( vPos2, vPlaneNormal ) + vClipPlane.w;
// Enforce that points that lie perfectly on the plane always go to the front
if ( flDot1 == 0.0f )
{
flDot1 = 0.01f;
}
if ( flDot2 == 0.0f )
{
flDot2 = 0.01f;
}
if ( flDot1 < 0 )
{
CopyVertex( pBackOut + nBack * nStrideFloats, ppVertsIn[ v ], nStrideFloats );
nBack ++;
}
else
{
CopyVertex( pFrontOut + nFront * nStrideFloats, ppVertsIn[ v ], nStrideFloats );
nFront ++;
}
if ( flDot1 * flDot2 < 0 )
{
// Lerp verts
float flLerp = -flDot1 / ( flDot2 - flDot1 );
LerpVertex( pBackOut + nBack * nStrideFloats, ppVertsIn[ v ], ppVertsIn[ nxtVert ], flLerp, nStrideFloats );
CopyVertex( pFrontOut + nFront * nStrideFloats, pBackOut + nBack * nStrideFloats, nStrideFloats );
nBack ++;
nFront++;
}
}
*pNumBackOut = nBack;
*pNumFrontOut = nFront;
}
// Clips a mesh against a plane and returns 2 meshes, one on each side of the plane. The caller must
// check pMeshBack and pMeshFront for empty vertex or index sets implying that the mesh was entirely
// on one side of the plane or the other.
bool ClipMeshToHalfSpace( CMesh *pMeshBack, CMesh *pMeshFront, const CMesh &inputMesh, Vector4D &vClipPlane )
{
Assert( pMeshBack || pMeshFront );
// NOTE: This assumes that position is the FIRST float3 in the buffer
int nPosOffset = inputMesh.FindFirstAttributeOffset( VERTEX_ELEMENT_POSITION );
if ( nPosOffset != 0 )
return false;
int nVertexStrideFloats = inputMesh.m_nVertexStrideFloats;
int nIndexCount = inputMesh.m_nIndexCount;
uint32 *pIndices = inputMesh.m_pIndices;
float *pVertices = inputMesh.m_pVerts;
// Allocate working space for the number of vertices out on each side of the plane.
// This is a maximum of 4 vertices out when clipping a triangle to a plane.
float *pBackOut = new float[ nVertexStrideFloats * 4 ];
float *pFrontOut = new float[ nVertexStrideFloats * 4 ];
CUtlBuffer backMeshVerts;
CUtlBuffer frontMeshVerts;
for ( int i=0; i<nIndexCount; i += 3 )
{
float *ppVerts[3];
int nIndex0 = pIndices[ i ];
int nIndex1 = pIndices[ i + 1 ];
int nIndex2 = pIndices[ i + 2 ];
ppVerts[0] = pVertices + nIndex0 * nVertexStrideFloats;
ppVerts[1] = pVertices + nIndex1 * nVertexStrideFloats;
ppVerts[2] = pVertices + nIndex2 * nVertexStrideFloats;
int nBackOut = 0;
int nFrontOut = 0;
ClipTriangle( pBackOut, pFrontOut, &nBackOut, &nFrontOut, nVertexStrideFloats, ppVerts, vClipPlane );
// reconstruct triangles out of the polygon
int numBackTris = nBackOut - 2;
for ( int t=0; t<numBackTris; ++t )
{
backMeshVerts.Put( pBackOut, nVertexStrideFloats * sizeof( float ) );
backMeshVerts.Put( pBackOut + nVertexStrideFloats * ( t + 1 ), nVertexStrideFloats * sizeof( float ) );
backMeshVerts.Put( pBackOut + nVertexStrideFloats * ( t + 2 ), nVertexStrideFloats * sizeof( float ) );
}
int numFrontTris = nFrontOut - 2;
for ( int t=0; t<numFrontTris; ++t )
{
frontMeshVerts.Put( pFrontOut, nVertexStrideFloats * sizeof( float ) );
frontMeshVerts.Put( pFrontOut + nVertexStrideFloats * ( t + 1 ), nVertexStrideFloats * sizeof( float ) );
frontMeshVerts.Put( pFrontOut + nVertexStrideFloats * ( t + 2 ), nVertexStrideFloats * sizeof( float ) );
}
}
delete []pBackOut;
delete []pFrontOut;
// Turn the utlbuffers into actual meshes
if ( pMeshBack )
{
pMeshBack->m_nVertexCount = backMeshVerts.TellPut() / ( nVertexStrideFloats * sizeof( float ) );
if ( pMeshBack->m_nVertexCount )
{
pMeshBack->AllocateMesh( pMeshBack->m_nVertexCount, pMeshBack->m_nVertexCount, nVertexStrideFloats, inputMesh.m_pAttributes, inputMesh.m_nAttributeCount );
Q_memcpy( pMeshBack->m_pVerts, backMeshVerts.Base(), backMeshVerts.TellPut() );
for ( int i=0; i<pMeshBack->m_nIndexCount; ++i )
{
pMeshBack->m_pIndices[i] = i;
}
}
}
if ( pMeshFront )
{
pMeshFront->m_nVertexCount = frontMeshVerts.TellPut() / ( nVertexStrideFloats * sizeof( float ) );
if ( pMeshFront->m_nVertexCount )
{
pMeshFront->AllocateMesh( pMeshFront->m_nVertexCount, pMeshFront->m_nVertexCount, nVertexStrideFloats, inputMesh.m_pAttributes, inputMesh.m_nAttributeCount );
Q_memcpy( pMeshFront->m_pVerts, frontMeshVerts.Base(), frontMeshVerts.TellPut() );
for ( int i=0; i<pMeshFront->m_nIndexCount; ++i )
{
pMeshFront->m_pIndices[i] = i;
}
}
}
return true;
}
//--------------------------------------------------------------------------------------
void CreateGridCellsForVolume( CUtlVector< GridVolume_t > &outputVolumes, const Vector &vTotalMinBounds, const Vector &vTotalMaxBounds, const Vector &vGridSize )
{
// First, determine if we need to be split
Vector vDelta = vTotalMaxBounds - vTotalMinBounds;
int nX = vDelta.x / vGridSize.x;
int nY = vDelta.y / vGridSize.y;
int nZ = vDelta.z / vGridSize.z;
Vector vEpsilon( 0.1f, 0.1f, 0.1f );
Vector vMinBounds = vTotalMinBounds - vEpsilon;
Vector vMaxBounds = vTotalMaxBounds + vEpsilon;
if ( nX * nY * nZ < 2 )
{
GridVolume_t newbounds;
newbounds.m_vMinBounds = vMinBounds;
newbounds.m_vMaxBounds = vMaxBounds;
outputVolumes.AddToTail( newbounds );
}
else
{
Vector vStep;
vStep.z = vDelta.z / nZ;
vStep.y = vDelta.y / nY;
vStep.x = vDelta.x / nX;
// Create the split volumes
outputVolumes.EnsureCount( nX * nY * nZ );
int nVolumes = 0;
Vector vStart = vMinBounds;
for ( int z=0; z<nZ; ++z )
{
vStart.y = vMinBounds.y;
for ( int y=0; y<nY; ++y )
{
vStart.x = vMinBounds.x;
for ( int x=0; x<nX; ++x )
{
GridVolume_t newbounds;
newbounds.m_vMinBounds = vStart;
newbounds.m_vMaxBounds = vStart + vStep;
if ( x == nX - 1 )
newbounds.m_vMaxBounds.x = vMaxBounds.x;
if ( y == nY - 1 )
newbounds.m_vMaxBounds.y = vMaxBounds.y;
if ( z == nZ - 1 )
newbounds.m_vMaxBounds.z = vMaxBounds.z;
outputVolumes[ nVolumes ] = newbounds;
nVolumes ++;
vStart.x += vStep.x;
}
vStart.y += vStep.y;
}
vStart.z += vStep.z;
}
}
}
//--------------------------------------------------------------------------------------
// For a list of AABBs, find all indices in the mesh that belong to each AABB. Then
// coalesce those indices into a single buffer in order of the input AABBs and return a
// list of ranges of the indices in the output mesh that are needed in each input volume
//--------------------------------------------------------------------------------------
void CreatedGriddedIndexRangesFromMesh( CMesh *pOutputMesh, CUtlVector< IndexRange_t > &outputRanges, const CMesh &inputMesh, CUtlVector< GridVolume_t > &inputVolumes )
{
DuplicateMesh( pOutputMesh, inputMesh );
int nVolumes = inputVolumes.Count();
outputRanges.EnsureCount( nVolumes );
int nFaces = inputMesh.m_nIndexCount / 3;
uint32 *pInputIndices = inputMesh.m_pIndices;
uint32 *pOutputIndices = pOutputMesh->m_pIndices;
int nMeshIndices = 0;
// Go though each volume and assign indices to its respective range
for ( int v=0; v<nVolumes; ++v )
{
GridVolume_t &volume = inputVolumes[ v ];
IndexRange_t &range = outputRanges[ v ];
range.m_nStartIndex = nMeshIndices;
for ( int f=0; f<nFaces; ++f )
{
uint32 i0 = pInputIndices[ f * 3 ];
uint32 i1 = pInputIndices[ f * 3 + 1 ];
uint32 i2 = pInputIndices[ f * 3 + 2 ];
Vector vCenter = *(Vector*)inputMesh.GetVertex( i0 );
vCenter += *(Vector*)inputMesh.GetVertex( i1 );
vCenter += *(Vector*)inputMesh.GetVertex( i2 );
vCenter /= 3.0f;
if ( vCenter.x > volume.m_vMinBounds.x && vCenter.x <= volume.m_vMaxBounds.x &&
vCenter.y > volume.m_vMinBounds.y && vCenter.y <= volume.m_vMaxBounds.y &&
vCenter.z > volume.m_vMinBounds.z && vCenter.z <= volume.m_vMaxBounds.z )
{
// Add the whole triangle
pOutputIndices[ nMeshIndices++ ] = i0;
pOutputIndices[ nMeshIndices++ ] = i1;
pOutputIndices[ nMeshIndices++ ] = i2;
}
}
range.m_nIndexCount = nMeshIndices - range.m_nStartIndex;
}
Assert( nMeshIndices == inputMesh.m_nIndexCount );
}