mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-01-03 11:58:24 -05:00
This commit imports the C# engine code and game files, excluding C++ source code. [Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
245 lines
9.1 KiB
C#
245 lines
9.1 KiB
C#
using HalfEdgeMesh;
|
|
|
|
namespace Sandbox;
|
|
|
|
public partial class PolygonMesh
|
|
{
|
|
private void SubdividePatch( FaceHandle hFace, int nNumDivisions, int nCurrentLevel, VertexHandle hVert0, VertexHandle hVert1, VertexHandle hVert2, VertexHandle hVert3 )
|
|
{
|
|
var originalVertices = new VertexHandle[4] { hVert0, hVert1, hVert2, hVert3 };
|
|
|
|
SubdivideFace( hFace, nNumDivisions, nCurrentLevel + 1, originalVertices, 4, out _ );
|
|
}
|
|
|
|
private VertexHandle SubdivideFace( FaceHandle hFace )
|
|
{
|
|
Topology.GetVerticesConnectedToFace( hFace, out var vertices );
|
|
|
|
SubdivideFace( hFace, 1, 0, vertices, vertices.Length, out var hCenterVertex );
|
|
return hCenterVertex;
|
|
}
|
|
|
|
private void SubdivideFace( FaceHandle hFace, int nNumDivisions, int nCurrentLevel, IReadOnlyList<VertexHandle> pOriginalVertices, int nNumOriginalVertices, out VertexHandle pOutCenterVertex )
|
|
{
|
|
pOutCenterVertex = VertexHandle.Invalid;
|
|
|
|
if ( (nNumOriginalVertices < 3) || (nCurrentLevel >= nNumDivisions) )
|
|
return;
|
|
|
|
Assert.True( (nCurrentLevel == 0) || (nNumOriginalVertices == 4) );
|
|
if ( (nCurrentLevel != 0) && (nNumOriginalVertices != 4) )
|
|
return;
|
|
|
|
if ( IsFaceInMesh( hFace ) == false )
|
|
return;
|
|
|
|
// Validate the original vertices
|
|
for ( int iVertex = 0; iVertex < nNumOriginalVertices; ++iVertex )
|
|
{
|
|
if ( IsVertexInMesh( pOriginalVertices[iVertex] ) == false )
|
|
return;
|
|
}
|
|
|
|
// Add the new edge vertices or find the existing vertices that are in the correct location
|
|
var pMidpointVertices = new VertexHandle[nNumOriginalVertices];
|
|
for ( int nCurrentVertex = nNumOriginalVertices - 1, nNextVertex = 0; nNextVertex < nNumOriginalVertices; nCurrentVertex = nNextVertex++ )
|
|
{
|
|
var hVertexA = pOriginalVertices[nCurrentVertex];
|
|
var hVertexB = pOriginalVertices[nNextVertex];
|
|
AddVertexToFaceEdgeBetweenVertices( hFace, hVertexA, hVertexB, 0.5f, out pMidpointVertices[nCurrentVertex] );
|
|
}
|
|
|
|
var hSharedFace = FaceHandle.Invalid;
|
|
|
|
// Create the edges connecting the midpoint vertices
|
|
var pCenterVertices = new VertexHandle[nNumOriginalVertices];
|
|
for ( int nCurrentVertex = nNumOriginalVertices - 1, nNextVertex = 0; nNextVertex < nNumOriginalVertices; nCurrentVertex = nNextVertex++ )
|
|
{
|
|
var hVertexA = pMidpointVertices[nCurrentVertex];
|
|
var hVertexB = pMidpointVertices[nNextVertex];
|
|
hSharedFace = Topology.FindFaceSharedByVertices( hVertexA, hVertexB );
|
|
if ( hSharedFace == FaceHandle.Invalid )
|
|
continue;
|
|
|
|
AddEdgeToFace( hSharedFace, hVertexA, hVertexB, out _ );
|
|
AddVertexToEdge( hVertexA, hVertexB, 0.5f, out pCenterVertices[nNextVertex] );
|
|
}
|
|
|
|
// Create the edges connecting the center vertices
|
|
var pCenterEdges = new HalfEdgeHandle[nNumOriginalVertices];
|
|
for ( int nCurrentVertex = nNumOriginalVertices - 1, nNextVertex = 0; nNextVertex < nNumOriginalVertices; nCurrentVertex = nNextVertex++ )
|
|
{
|
|
var hVertexA = pCenterVertices[nCurrentVertex];
|
|
var hVertexB = pCenterVertices[nNextVertex];
|
|
|
|
hSharedFace = Topology.FindFaceSharedByVertices( hVertexA, hVertexB );
|
|
if ( hSharedFace == FaceHandle.Invalid )
|
|
continue;
|
|
|
|
AddEdgeToFace( hSharedFace, hVertexA, hVertexB, out pCenterEdges[nNextVertex] );
|
|
}
|
|
|
|
// Find the center face by finding the face common to the first two center edges
|
|
Topology.GetFacesConnectedToFullEdge( pCenterEdges[0], out var hFaceA1, out var hFaceA2 );
|
|
Topology.GetFacesConnectedToFullEdge( pCenterEdges[1], out var hFaceB1, out var hFaceB2 );
|
|
|
|
hSharedFace = FaceHandle.Invalid;
|
|
if ( (hFaceA1 == hFaceB1) || (hFaceA1 == hFaceB2) )
|
|
{
|
|
hSharedFace = hFaceA1;
|
|
}
|
|
else if ( (hFaceA2 == hFaceB1) || (hFaceA2 == hFaceB2) )
|
|
{
|
|
hSharedFace = hFaceA2;
|
|
}
|
|
|
|
// Collapse the center face, turning it into a single vertex
|
|
if ( CollapseFace( hSharedFace, out var hCenterVertex ) == false )
|
|
return;
|
|
|
|
pOutCenterVertex = hCenterVertex;
|
|
|
|
// Recursively subdivide the resulting faces
|
|
|
|
var nNumPatchSteps = (1 << (nNumDivisions - 1));
|
|
var nNumPatchFaceVertices = nNumPatchSteps * nNumPatchSteps * 4;
|
|
|
|
int nOutFaceVerticesOffset = 0;
|
|
|
|
if ( nCurrentLevel > 0 )
|
|
{
|
|
Assert.True( nNumOriginalVertices == 4 );
|
|
|
|
var hChildFace = Topology.FindFaceSharedByVertices( hCenterVertex, pOriginalVertices[0] );
|
|
SubdividePatch( hChildFace, nNumDivisions, nCurrentLevel, pOriginalVertices[0], pMidpointVertices[0], hCenterVertex, pMidpointVertices[3] );
|
|
|
|
hChildFace = Topology.FindFaceSharedByVertices( hCenterVertex, pOriginalVertices[1] );
|
|
SubdividePatch( hChildFace, nNumDivisions, nCurrentLevel, pMidpointVertices[0], pOriginalVertices[1], pMidpointVertices[1], hCenterVertex );
|
|
|
|
hChildFace = Topology.FindFaceSharedByVertices( hCenterVertex, pOriginalVertices[3] );
|
|
SubdividePatch( hChildFace, nNumDivisions, nCurrentLevel, pMidpointVertices[3], hCenterVertex, pMidpointVertices[2], pOriginalVertices[3] );
|
|
|
|
hChildFace = Topology.FindFaceSharedByVertices( hCenterVertex, pOriginalVertices[2] );
|
|
SubdividePatch( hChildFace, nNumDivisions, nCurrentLevel, hCenterVertex, pMidpointVertices[1], pOriginalVertices[2], pMidpointVertices[2] );
|
|
}
|
|
else
|
|
{
|
|
var childFaceOriginalVertices = new VertexHandle[4];
|
|
|
|
for ( int iVertex = 0, iPrevVertex = nNumOriginalVertices - 1; iVertex < nNumOriginalVertices; iPrevVertex = iVertex++ )
|
|
{
|
|
childFaceOriginalVertices[0] = pOriginalVertices[iVertex];
|
|
childFaceOriginalVertices[1] = pMidpointVertices[iVertex];
|
|
childFaceOriginalVertices[2] = hCenterVertex;
|
|
childFaceOriginalVertices[3] = pMidpointVertices[iPrevVertex];
|
|
|
|
var hChildFace = Topology.FindFaceSharedByVertices( hCenterVertex, pOriginalVertices[iVertex] );
|
|
|
|
SubdivideFace( hChildFace, nNumDivisions, nCurrentLevel + 1, childFaceOriginalVertices, 4, out _ );
|
|
|
|
nOutFaceVerticesOffset += nNumPatchFaceVertices;
|
|
}
|
|
}
|
|
}
|
|
|
|
private bool AddVertexToFaceEdgeBetweenVertices( FaceHandle hFace, VertexHandle hEdgeVertexA, VertexHandle hEdgeVertexB, float flParam, out VertexHandle pOutNewVertex )
|
|
{
|
|
const float flTolerance = 0.01f;
|
|
|
|
pOutNewVertex = VertexHandle.Invalid;
|
|
var hNewVertex = VertexHandle.Invalid;
|
|
|
|
// Find the face vertices connected to the specified vertices in the specified face
|
|
var hFaceVertexA = FindFaceVertexConnectedToVertex( hEdgeVertexA, hFace );
|
|
var hFaceVertexB = FindFaceVertexConnectedToVertex( hEdgeVertexB, hFace );
|
|
if ( (hFaceVertexA == HalfEdgeHandle.Invalid) || (hFaceVertexB == HalfEdgeHandle.Invalid) )
|
|
return false;
|
|
|
|
// Compute the length of the entire edge
|
|
int nNumSegments = 0;
|
|
float flEdgeLength = 0;
|
|
var hCurrentFaceVertex = hFaceVertexA;
|
|
while ( hCurrentFaceVertex != hFaceVertexB )
|
|
{
|
|
var hNextFaceVertex = GetNextVertexInFace( hCurrentFaceVertex );
|
|
var hSegmentVertexA = GetVertexConnectedToFaceVertex( hCurrentFaceVertex );
|
|
var hSegmentVertexB = GetVertexConnectedToFaceVertex( hNextFaceVertex );
|
|
var vPositionA = GetVertexPosition( hSegmentVertexA );
|
|
var vPositionB = GetVertexPosition( hSegmentVertexB );
|
|
|
|
float flSegmentLength = vPositionA.Distance( vPositionB );
|
|
flEdgeLength += flSegmentLength;
|
|
++nNumSegments;
|
|
|
|
hCurrentFaceVertex = hNextFaceVertex;
|
|
}
|
|
|
|
// If the edge is zero length, we will just say each segment is 1 unit
|
|
// so the total length equals the number of units is 1.0f * nNumSegmets;
|
|
bool bZeroLengthEdge = false;
|
|
if ( flEdgeLength <= 0.0f )
|
|
{
|
|
flEdgeLength = (float)nNumSegments;
|
|
bZeroLengthEdge = true;
|
|
}
|
|
|
|
// Compute the distance of the new vertex along the edge
|
|
float flNewVertexDistance = flEdgeLength * flParam;
|
|
|
|
// Find the segment to which the new vertex should be added
|
|
float flCurrentLength = 0.0f;
|
|
hCurrentFaceVertex = hFaceVertexA;
|
|
while ( hCurrentFaceVertex != hFaceVertexB )
|
|
{
|
|
var hNextFaceVertex = GetNextVertexInFace( hCurrentFaceVertex );
|
|
var hCurrentVertex = GetVertexConnectedToFaceVertex( hCurrentFaceVertex );
|
|
var hNextVertex = GetVertexConnectedToFaceVertex( hNextFaceVertex );
|
|
var vPositionA = GetVertexPosition( hCurrentVertex );
|
|
var vPositionB = GetVertexPosition( hNextVertex );
|
|
|
|
float flSegmentLength = vPositionA.Distance( vPositionB );
|
|
if ( bZeroLengthEdge )
|
|
{
|
|
flSegmentLength = 1.0f;
|
|
}
|
|
|
|
if ( flSegmentLength > 0.0f )
|
|
{
|
|
float flSegmentParameter = (flNewVertexDistance - flCurrentLength) / flSegmentLength;
|
|
Assert.True( flSegmentParameter >= 0.0f );
|
|
|
|
// If the segment parameter is less than or equal to 1.0 this is the segment the vertex is on
|
|
if ( flSegmentParameter <= 1.0f )
|
|
{
|
|
if ( flSegmentParameter < flTolerance )
|
|
{
|
|
// The new vertex position is the position of the first vertex of the current segment
|
|
hNewVertex = hCurrentVertex;
|
|
}
|
|
else if ( flSegmentParameter > (1.0f - flTolerance) )
|
|
{
|
|
// The new vertex position is the position of the second vertex of the current segment
|
|
hNewVertex = hNextVertex;
|
|
}
|
|
else
|
|
{
|
|
// Add the new vertex to the segment
|
|
if ( AddVertexToEdge( hCurrentVertex, hNextVertex, flSegmentParameter, out hNewVertex ) == false )
|
|
return false;
|
|
}
|
|
|
|
// The new vertex was found or added to this segment no need to check the others.
|
|
break;
|
|
}
|
|
}
|
|
|
|
flCurrentLength += flSegmentLength;
|
|
hCurrentFaceVertex = hNextFaceVertex;
|
|
}
|
|
|
|
pOutNewVertex = hNewVertex;
|
|
|
|
return hNewVertex != VertexHandle.Invalid;
|
|
}
|
|
}
|