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