//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ // //=============================================================================// #include #include "DispSubdiv.h" #include "MapDisp.h" #include "UtlLinkedList.h" #include "UtlVector.h" #include "GlobalFunctions.h" // memdbgon must be the last include file in a .cpp file!!! #include //============================================================================= // // Editable Displacement Subdivision Mesh Implementation // class CEditDispSubdivMesh : public IEditDispSubdivMesh { public: // functions void Init( void ); void Shutdown( void ); void AddDispTo( CMapDisp *pDisp ); void GetDispFrom( CMapDisp *pDisp ); void DoCatmullClarkSubdivision( void ); public: // typedefs, enums, structs enum { EDITDISP_QUADSIZE = 4 }; // should be in mapdisp (general define) private: // typedefs, enums, structs typedef int SubdivPointHandle_t; typedef int SubdivEdgeHandle_t; typedef int SubdivQuadHandle_t; enum { NUM_SUBDIV_LEVELS = 4 }; // number of subdivision levels enum { SUBDIV_DISPPOINTS = 512, SUBDIV_DISPEDGES = 1024, SUBDIV_DISPQUADS = 512 }; enum { SUBDIV_POINTORDINARY = 0, SUBDIV_POINTCORNER = 1, SUBDIV_POINTCREASE = 2 }; struct SubdivPoint_t { Vector m_vPoint; Vector m_vNormal; Vector m_vNewPoint; Vector m_vNewNormal; unsigned short m_uType; unsigned short m_uValence; SubdivEdgeHandle_t m_EdgeHandles[EDITDISP_QUADSIZE*4]; }; struct SubdivEdge_t { Vector m_vNewEdgePoint; Vector m_vNewEdgeNormal; SubdivPointHandle_t m_PointHandles[2]; SubdivQuadHandle_t m_QuadHandles[2]; float m_flSharpness; bool m_bActive; }; struct SubdivQuad_t { // generated Vector m_vCentroid; // quad center Vector m_vNormal; // quad normal // linkage SubdivQuadHandle_t m_ndxParent; // parent quad index SubdivQuadHandle_t m_ndxChild[EDITDISP_QUADSIZE]; // chilren (4 of them) indices // quad data SubdivPointHandle_t m_PointHandles[EDITDISP_QUADSIZE]; // point indices - unique list SubdivEdgeHandle_t m_EdgeHandles[EDITDISP_QUADSIZE]; // edge indices - unique list // disp/quad mapping EditDispHandle_t m_EditDispHandle; short m_Level; // level of quad in the hierarchy (tree) short m_QuadIndices[EDITDISP_QUADSIZE]; // quad indices (in the X x X displacement surface) }; private: // functions SubdivPoint_t *GetPoint( SubdivPointHandle_t ptHandle ); SubdivEdge_t *GetEdge( SubdivEdgeHandle_t edgeHandle ); SubdivQuad_t *GetQuad( SubdivQuadHandle_t quadHandle ); void Point_Init( SubdivPointHandle_t ptHandle ); void Point_CalcNewPoint( SubdivPointHandle_t ptHandle ); void Point_PointOrdinary( SubdivPoint_t *pPoint ); void Point_PointCorner( SubdivPoint_t *pPoint ); void Point_PointCrease( SubdivPoint_t *pPoint ); void Edge_Init( SubdivEdgeHandle_t edgeHandle ); void Edge_CalcNewPoint( SubdivEdgeHandle_t edgeHandle ); void Quad_Init( SubdivQuadHandle_t quadHandle ); void Quad_CalcCentroid( SubdivQuadHandle_t quadHandle ); void Quad_CalcNormal( SubdivQuadHandle_t quadHandle ); bool CompareSubdivPoints( Vector const &pt1, Vector const &pt2, float flTolerance ); bool CompareSubdivEdges( SubdivPointHandle_t ptEdge0Handle0, SubdivPointHandle_t ptEdge0Handle1, SubdivPointHandle_t ptEdge1Handle0, SubdivPointHandle_t ptEdge1Handle1 ); SubdivPointHandle_t BuildSubdivPoint( Vector const &vPoint, Vector const &vNormal ); SubdivEdgeHandle_t BuildSubdivEdge( int ndxEdge, SubdivQuadHandle_t quadHandle, SubdivQuadHandle_t parentHandle, int ndxChild ); SubdivQuadHandle_t BuildSubdivQuad( int ndxChild, SubdivQuadHandle_t parentHandle ); void CatmullClarkSubdivision( void ); void UpdateSubdivisionHierarchy( int ndxLevel ); private: // variables CUtlLinkedList m_Points; CUtlLinkedList m_Edges; CUtlLinkedList m_Quads; }; //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- IEditDispSubdivMesh *CreateEditDispSubdivMesh( void ) { return new CEditDispSubdivMesh; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void DestroyEditDispSubdivMesh( IEditDispSubdivMesh **pSubdivMesh ) { if ( *pSubdivMesh ) { delete *pSubdivMesh; *pSubdivMesh = NULL; } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CEditDispSubdivMesh::SubdivPoint_t *CEditDispSubdivMesh::GetPoint( SubdivPointHandle_t ptHandle ) { if ( !m_Points.IsValidIndex( ptHandle ) ) return NULL; return &m_Points.Element( ptHandle ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CEditDispSubdivMesh::SubdivEdge_t *CEditDispSubdivMesh::GetEdge( SubdivEdgeHandle_t edgeHandle ) { if ( !m_Edges.IsValidIndex( edgeHandle ) ) return NULL; return &m_Edges.Element( edgeHandle ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CEditDispSubdivMesh::SubdivQuad_t *CEditDispSubdivMesh::GetQuad( SubdivQuadHandle_t quadHandle ) { if ( !m_Quads.IsValidIndex( quadHandle ) ) return NULL; return &m_Quads.Element( quadHandle ); } //============================================================================= // // Subdivision Edit Displacement Point Functions // //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CEditDispSubdivMesh::Point_Init( SubdivPointHandle_t ptHandle ) { SubdivPoint_t *pPoint = GetPoint( ptHandle ); if ( pPoint ) { VectorClear( pPoint->m_vPoint ); VectorClear( pPoint->m_vNormal ); VectorClear( pPoint->m_vNewPoint ); VectorClear( pPoint->m_vNewNormal ); pPoint->m_uType = (unsigned short)-1; pPoint->m_uValence = 0; for ( int ndxEdge = 0; ndxEdge < ( EDITDISP_QUADSIZE*2 ); ndxEdge++ ) { pPoint->m_EdgeHandles[ndxEdge] = m_Edges.InvalidIndex(); } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CEditDispSubdivMesh::Point_CalcNewPoint( SubdivPointHandle_t ptHandle ) { // get the point to act on SubdivPoint_t *pPoint = GetPoint( ptHandle ); if ( !pPoint ) return; switch ( pPoint->m_uType ) { case SUBDIV_POINTORDINARY: { Point_PointOrdinary( pPoint ); break; } case SUBDIV_POINTCORNER: { Point_PointCorner( pPoint ); break; } case SUBDIV_POINTCREASE: { Point_PointCrease( pPoint ); break; } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CEditDispSubdivMesh::Point_PointOrdinary( SubdivPoint_t *pPoint ) { // // accumulate the edge data and multiply by the valence (coincident edge) // ratio (squared) // Vector edgeAccumPoint( 0.0f, 0.0f, 0.0f ); Vector edgeAccumNormal( 0.0f, 0.0f, 0.0f ); for ( int ndxEdge = 0; ndxEdge < pPoint->m_uValence; ndxEdge++ ) { SubdivEdge_t *pEdge = GetEdge( pPoint->m_EdgeHandles[ndxEdge] ); if ( pEdge ) { VectorAdd( edgeAccumPoint, pEdge->m_vNewEdgePoint, edgeAccumPoint ); VectorAdd( edgeAccumNormal, pEdge->m_vNewEdgeNormal, edgeAccumNormal ); } } float ratio = 1.0f / ( float )( pPoint->m_uValence * pPoint->m_uValence ); VectorScale( edgeAccumPoint, ratio, edgeAccumPoint ); VectorScale( edgeAccumNormal, ratio, edgeAccumNormal ); // // accumlate the centroid data from all neighboring quads and multiply by // the valence (coincident edge) ratio (squared) // int quadListCount = 0; SubdivQuadHandle_t quadList[32]; for ( int ndxEdge = 0; ndxEdge < pPoint->m_uValence; ndxEdge++ ) { SubdivEdge_t *pEdge = GetEdge( pPoint->m_EdgeHandles[ndxEdge] ); if ( pEdge ) { for ( int ndxQuad = 0; ndxQuad < 2; ndxQuad++ ) { if ( pEdge->m_QuadHandles[ndxQuad] != m_Quads.InvalidIndex() ) { int ndxList; for ( ndxList = 0; ndxList < quadListCount; ndxList++ ) { if( pEdge->m_QuadHandles[ndxQuad] == quadList[ndxList] ) break; } if( ndxList == quadListCount ) { quadList[quadListCount] = pEdge->m_QuadHandles[ndxQuad]; quadListCount++; } } } } } Vector centroidAccum( 0.0f, 0.0f, 0.0f ); for ( int ndxQuad = 0; ndxQuad < quadListCount; ndxQuad++ ) { SubdivQuadHandle_t quadHandle = quadList[ndxQuad]; Quad_CalcCentroid( quadHandle ); SubdivQuad_t *pQuad = GetQuad( quadHandle ); VectorAdd( centroidAccum, pQuad->m_vCentroid, centroidAccum ); } VectorScale( centroidAccum, ratio, centroidAccum ); // // // ratio = ( ( float )pPoint->m_uValence - 2.0f ) / ( float )pPoint->m_uValence; VectorScale( pPoint->m_vPoint, ratio, pPoint->m_vNewPoint ); VectorAdd( pPoint->m_vNewPoint, edgeAccumPoint, pPoint->m_vNewPoint ); VectorAdd( pPoint->m_vNewPoint, centroidAccum, pPoint->m_vNewPoint ); VectorScale( pPoint->m_vNormal, ratio, pPoint->m_vNewNormal ); VectorAdd( pPoint->m_vNewNormal, edgeAccumNormal, pPoint->m_vNewNormal ); VectorAdd( pPoint->m_vNewNormal, centroidAccum, pPoint->m_vNewNormal ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CEditDispSubdivMesh::Point_PointCorner( SubdivPoint_t *pPoint ) { VectorCopy( pPoint->m_vPoint, pPoint->m_vNewPoint ); VectorCopy( pPoint->m_vNormal, pPoint->m_vNewNormal ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CEditDispSubdivMesh::Point_PointCrease( SubdivPoint_t *pPoint ) { // // accumulate the edge data and multiply by the valence (coincident edge) // ratio (squared) // Vector edgeAccumPoint( 0.0f, 0.0f, 0.0f ); Vector edgeAccumNormal( 0.0f, 0.0f, 0.0f ); for ( int ndxEdge = 0; ndxEdge < pPoint->m_uValence; ndxEdge++ ) { SubdivEdge_t *pEdge = GetEdge( pPoint->m_EdgeHandles[ndxEdge] ); if ( pEdge && ( pEdge->m_flSharpness > 0.0f ) ) { VectorAdd( edgeAccumPoint, pEdge->m_vNewEdgePoint, edgeAccumPoint ); VectorAdd( edgeAccumNormal, pEdge->m_vNewEdgeNormal, edgeAccumNormal ); } } // // // VectorScale( pPoint->m_vPoint, 6.0f, pPoint->m_vNewPoint ); VectorAdd( pPoint->m_vNewPoint, edgeAccumPoint, pPoint->m_vNewPoint ); VectorScale( pPoint->m_vNewPoint, 0.125f, pPoint->m_vNewPoint ); VectorScale( pPoint->m_vNormal, 6.0f, pPoint->m_vNewNormal ); VectorAdd( pPoint->m_vNewNormal, edgeAccumNormal, pPoint->m_vNewNormal ); VectorScale( pPoint->m_vNewNormal, 0.125f, pPoint->m_vNewNormal ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CEditDispSubdivMesh::Edge_Init( SubdivEdgeHandle_t edgeHandle ) { SubdivEdge_t *pEdge = GetEdge( edgeHandle ); if ( pEdge ) { VectorClear( pEdge->m_vNewEdgePoint ); VectorClear( pEdge->m_vNewEdgeNormal ); pEdge->m_flSharpness = 1.0f; pEdge->m_bActive = false; for ( int ndx = 0; ndx < 2; ndx++ ) { pEdge->m_PointHandles[ndx] = m_Points.InvalidIndex(); pEdge->m_QuadHandles[ndx] = m_Quads.InvalidIndex(); } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CEditDispSubdivMesh::Edge_CalcNewPoint( SubdivEdgeHandle_t edgeHandle ) { SubdivEdge_t *pEdge = GetEdge( edgeHandle ); if ( !pEdge ) return; if ( !pEdge->m_bActive ) return; // // get edge points // SubdivPoint_t *pPoint0 = GetPoint( pEdge->m_PointHandles[0] ); SubdivPoint_t *pPoint1 = GetPoint( pEdge->m_PointHandles[1] ); if ( !pPoint0 || !pPoint1 ) return; // // calculate the "sharp" new edge point // Vector vSharpPoint( 0.0f, 0.0f, 0.0f ); VectorAdd( pPoint0->m_vPoint, pPoint1->m_vPoint, vSharpPoint ); VectorScale( vSharpPoint, 0.5f, vSharpPoint ); Vector vSharpNormal( 0.0f, 0.0f, 0.0f ); VectorAdd( pPoint0->m_vNormal, pPoint1->m_vNormal, vSharpNormal ); VectorNormalize( vSharpNormal ); // // calculate the "smooth" new edge point (if necessary) // Vector vSmoothPoint( 0.0f, 0.0f, 0.0f ); Vector vSmoothNormal( 0.0f, 0.0f, 0.0f ); if ( ( pEdge->m_QuadHandles[1] != m_Edges.InvalidIndex() ) && ( pEdge->m_flSharpness != 1.0f ) ) { Quad_CalcCentroid( pEdge->m_QuadHandles[0] ); Quad_CalcCentroid( pEdge->m_QuadHandles[1] ); Quad_CalcNormal( pEdge->m_QuadHandles[0] ); Quad_CalcNormal( pEdge->m_QuadHandles[1] ); SubdivQuad_t *pQuad0 = GetQuad( pEdge->m_QuadHandles[0] ); SubdivQuad_t *pQuad1 = GetQuad( pEdge->m_QuadHandles[1] ); VectorAdd( pPoint0->m_vPoint, pPoint1->m_vPoint, vSmoothPoint ); VectorAdd( vSmoothPoint, pQuad0->m_vCentroid, vSmoothPoint ); VectorAdd( vSmoothPoint, pQuad1->m_vCentroid, vSmoothPoint ); VectorScale( vSmoothPoint, 0.25f, vSmoothPoint ); VectorAdd( pPoint0->m_vNormal, pPoint1->m_vNormal, vSmoothNormal ); VectorAdd( vSmoothNormal, pQuad0->m_vNormal, vSmoothNormal ); VectorAdd( vSmoothNormal, pQuad1->m_vNormal, vSmoothNormal ); VectorNormalize( vSmoothNormal ); } else { pEdge->m_flSharpness = 1.0f; Quad_CalcCentroid( pEdge->m_QuadHandles[0] ); Quad_CalcNormal( pEdge->m_QuadHandles[0] ); } // // calculate the new edge point // // ( 1 - edge(sharpness) ) * vSmooth + edge(sharpness) * vSharp // VectorScale( vSmoothPoint, ( 1.0f - pEdge->m_flSharpness ), vSmoothPoint ); VectorScale( vSharpPoint, pEdge->m_flSharpness, vSharpPoint ); VectorAdd( vSmoothPoint, vSharpPoint, pEdge->m_vNewEdgePoint ); VectorScale( vSmoothNormal, ( 1.0f - pEdge->m_flSharpness ), vSmoothNormal ); VectorScale( vSharpNormal, pEdge->m_flSharpness, vSharpNormal ); VectorAdd( vSmoothNormal, vSharpNormal, pEdge->m_vNewEdgeNormal ); VectorNormalize( pEdge->m_vNewEdgeNormal ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CEditDispSubdivMesh::Quad_Init( SubdivQuadHandle_t quadHandle ) { SubdivQuad_t *pQuad = GetQuad( quadHandle ); if ( pQuad ) { VectorClear( pQuad->m_vCentroid ); VectorClear( pQuad->m_vNormal ); pQuad->m_ndxParent = m_Quads.InvalidIndex(); pQuad->m_EditDispHandle = EDITDISPHANDLE_INVALID; pQuad->m_Level = -1; for ( int ndx = 0; ndx < EDITDISP_QUADSIZE; ndx++ ) { pQuad->m_ndxChild[ndx] = m_Quads.InvalidIndex(); pQuad->m_PointHandles[ndx] = m_Points.InvalidIndex(); pQuad->m_EdgeHandles[ndx] = m_Edges.InvalidIndex(); pQuad->m_QuadIndices[ndx] = -1; } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CEditDispSubdivMesh::Quad_CalcCentroid( SubdivQuadHandle_t quadHandle ) { SubdivQuad_t *pQuad = GetQuad( quadHandle ); if ( pQuad ) { VectorClear( pQuad->m_vCentroid ); for ( int ndxPt = 0; ndxPt < EDITDISP_QUADSIZE; ndxPt++ ) { SubdivPoint_t *pPoint = GetPoint( pQuad->m_PointHandles[ndxPt] ); VectorAdd( pQuad->m_vCentroid, pPoint->m_vPoint, pQuad->m_vCentroid ); } VectorScale( pQuad->m_vCentroid, 0.25f, pQuad->m_vCentroid ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CEditDispSubdivMesh::Quad_CalcNormal( SubdivQuadHandle_t quadHandle ) { SubdivQuad_t *pQuad = GetQuad( quadHandle ); if ( pQuad ) { SubdivPoint_t *pPoints[3]; Vector edges[2]; pPoints[0] = GetPoint( pQuad->m_PointHandles[0] ); pPoints[1] = GetPoint( pQuad->m_PointHandles[1] ); pPoints[2] = GetPoint( pQuad->m_PointHandles[2] ); VectorSubtract( pPoints[1]->m_vPoint, pPoints[0]->m_vPoint, edges[0] ); VectorSubtract( pPoints[2]->m_vPoint, pPoints[0]->m_vPoint, edges[1] ); CrossProduct( edges[1], edges[0], pQuad->m_vNormal ); VectorNormalize( pQuad->m_vNormal ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CEditDispSubdivMesh::CompareSubdivPoints( Vector const &pt1, Vector const &pt2, float flTolerance ) { for ( int axis = 0 ; axis < 3 ; axis++ ) { if ( fabs( pt1[axis] - pt2[axis] ) > flTolerance ) return false; } return true; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CEditDispSubdivMesh::CompareSubdivEdges( SubdivPointHandle_t ptEdge0Handle0, SubdivPointHandle_t ptEdge0Handle1, SubdivPointHandle_t ptEdge1Handle0, SubdivPointHandle_t ptEdge1Handle1 ) { if ( ( ( ptEdge0Handle0 == ptEdge1Handle0 ) && ( ptEdge0Handle1 == ptEdge1Handle1 ) ) || ( ( ptEdge0Handle0 == ptEdge1Handle1 ) && ( ptEdge0Handle1 == ptEdge1Handle0 ) ) ) return true; return false; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CEditDispSubdivMesh::SubdivPointHandle_t CEditDispSubdivMesh::BuildSubdivPoint( Vector const &vPoint, Vector const &vPointNormal ) { // // build a "unique" point // SubdivPointHandle_t ptHandle; for ( ptHandle = m_Points.Head(); ptHandle != m_Points.InvalidIndex(); ptHandle = m_Points.Next( ptHandle ) ) { SubdivPoint_t *pPoint = GetPoint( ptHandle ); if ( pPoint ) { // compare (positions) if ( CompareSubdivPoints( vPoint, pPoint->m_vPoint, 0.1f ) ) return ptHandle; } } ptHandle = m_Points.AddToTail(); Point_Init( ptHandle ); SubdivPoint_t *pPoint = GetPoint( ptHandle ); VectorCopy( vPoint, pPoint->m_vPoint ); VectorCopy( vPointNormal, pPoint->m_vNormal ); return ptHandle; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CEditDispSubdivMesh::SubdivEdgeHandle_t CEditDispSubdivMesh::BuildSubdivEdge( int ndxEdge, SubdivQuadHandle_t quadHandle, SubdivQuadHandle_t parentHandle, int ndxChild ) { // get the quad SubdivQuad_t *pQuad = GetQuad( quadHandle ); if ( !pQuad ) return m_Edges.InvalidIndex(); // // define a unique edge (m_PointHandlesX2, m_QuadHandle) // SubdivEdgeHandle_t edgeHandle; for ( edgeHandle = m_Edges.Head(); edgeHandle != m_Edges.InvalidIndex(); edgeHandle = m_Edges.Next( edgeHandle ) ) { SubdivEdge_t *pEdge = GetEdge( edgeHandle ); if ( pEdge ) { // compare (point handles) if ( CompareSubdivEdges( pQuad->m_PointHandles[ndxEdge], pQuad->m_PointHandles[(ndxEdge+1)%4], pEdge->m_PointHandles[0], pEdge->m_PointHandles[1] ) ) { // check to see if the quad is quad 0 or 1 (or if it needs to be quad 1) if ( ( pEdge->m_QuadHandles[0] != quadHandle ) && ( pEdge->m_QuadHandles[1] == m_Quads.InvalidIndex() ) ) { pEdge->m_QuadHandles[1] = quadHandle; pEdge->m_flSharpness = 0.0f; // smooth edge (between two subdiv quads) } return edgeHandle; } } } edgeHandle = m_Edges.AddToTail(); Edge_Init( edgeHandle ); SubdivEdge_t *pEdge = GetEdge( edgeHandle ); pEdge->m_PointHandles[0] = pQuad->m_PointHandles[ndxEdge]; pEdge->m_PointHandles[1] = pQuad->m_PointHandles[(ndxEdge+1)%4]; pEdge->m_QuadHandles[0] = quadHandle; pEdge->m_bActive = true; // extra data for children (get edge sharpness from parent or // it may be an internal edge and its sharpness will be 0) if( ndxChild != -1 ) { if ( ( ndxEdge == ndxChild ) || ( ndxEdge == ( (ndxChild+3)%4 ) ) ) { SubdivQuad_t *pParentQuad = GetQuad( parentHandle ); if ( pParentQuad ) { SubdivEdge_t *pParentEdge = GetEdge( pParentQuad->m_EdgeHandles[ndxEdge] ); pEdge->m_flSharpness = pParentEdge->m_flSharpness; } } else { pEdge->m_flSharpness = 0.0f; } } return edgeHandle; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CEditDispSubdivMesh::SubdivQuadHandle_t CEditDispSubdivMesh::BuildSubdivQuad( int ndxChild, SubdivQuadHandle_t parentHandle ) { // get parent quad SubdivQuad_t *pParentQuad = GetQuad( parentHandle ); if( !pParentQuad ) return m_Quads.InvalidIndex(); // allocate a new quad SubdivQuadHandle_t quadHandle = m_Quads.AddToTail(); Quad_Init( quadHandle ); SubdivQuad_t *pQuad = GetQuad( quadHandle ); pQuad->m_ndxParent = parentHandle; pQuad->m_EditDispHandle = pParentQuad->m_EditDispHandle; pQuad->m_Level = pParentQuad->m_Level + 1; switch ( ndxChild ) { case 0: { // displacement quad indices pQuad->m_QuadIndices[0] = pParentQuad->m_QuadIndices[0]; pQuad->m_QuadIndices[1] = ( pParentQuad->m_QuadIndices[0] + pParentQuad->m_QuadIndices[1] ) * 0.5f; pQuad->m_QuadIndices[2] = ( pParentQuad->m_QuadIndices[0] + pParentQuad->m_QuadIndices[2] ) * 0.5f; pQuad->m_QuadIndices[3] = ( pParentQuad->m_QuadIndices[0] + pParentQuad->m_QuadIndices[3] ) * 0.5f; // new verts SubdivEdge_t *pEdge0 = GetEdge( pParentQuad->m_EdgeHandles[0] ); SubdivEdge_t *pEdge3 = GetEdge( pParentQuad->m_EdgeHandles[3] ); if ( pEdge0 && pEdge3 ) { pQuad->m_PointHandles[0] = pParentQuad->m_PointHandles[0]; pQuad->m_PointHandles[1] = BuildSubdivPoint( pEdge0->m_vNewEdgePoint, pEdge0->m_vNewEdgeNormal ); pQuad->m_PointHandles[2] = BuildSubdivPoint( pParentQuad->m_vCentroid, pParentQuad->m_vNormal ); pQuad->m_PointHandles[3] = BuildSubdivPoint( pEdge3->m_vNewEdgePoint, pEdge3->m_vNewEdgeNormal ); } break; } case 1: { // displacement quad indices pQuad->m_QuadIndices[0] = ( pParentQuad->m_QuadIndices[0] + pParentQuad->m_QuadIndices[1] ) * 0.5f; pQuad->m_QuadIndices[1] = pParentQuad->m_QuadIndices[1]; pQuad->m_QuadIndices[2] = ( pParentQuad->m_QuadIndices[1] + pParentQuad->m_QuadIndices[2] ) * 0.5f; pQuad->m_QuadIndices[3] = ( pParentQuad->m_QuadIndices[0] + pParentQuad->m_QuadIndices[2] ) * 0.5f; // new verts SubdivEdge_t *pEdge0 = GetEdge( pParentQuad->m_EdgeHandles[0] ); SubdivEdge_t *pEdge1 = GetEdge( pParentQuad->m_EdgeHandles[1] ); if ( pEdge0 && pEdge1 ) { pQuad->m_PointHandles[0] = BuildSubdivPoint( pEdge0->m_vNewEdgePoint, pEdge0->m_vNewEdgeNormal ); pQuad->m_PointHandles[1] = pParentQuad->m_PointHandles[1]; pQuad->m_PointHandles[2] = BuildSubdivPoint( pEdge1->m_vNewEdgePoint, pEdge1->m_vNewEdgeNormal ); pQuad->m_PointHandles[3] = BuildSubdivPoint( pParentQuad->m_vCentroid, pParentQuad->m_vNormal ); } break; } case 2: { // displacement quad indices pQuad->m_QuadIndices[0] = ( pParentQuad->m_QuadIndices[0] + pParentQuad->m_QuadIndices[2] ) * 0.5f; pQuad->m_QuadIndices[1] = ( pParentQuad->m_QuadIndices[1] + pParentQuad->m_QuadIndices[2] ) * 0.5f; pQuad->m_QuadIndices[2] = pParentQuad->m_QuadIndices[2]; pQuad->m_QuadIndices[3] = ( pParentQuad->m_QuadIndices[2] + pParentQuad->m_QuadIndices[3] ) * 0.5f; // new verts SubdivEdge_t *pEdge1 = GetEdge( pParentQuad->m_EdgeHandles[1] ); SubdivEdge_t *pEdge2 = GetEdge( pParentQuad->m_EdgeHandles[2] ); if ( pEdge1 && pEdge2 ) { pQuad->m_PointHandles[0] = BuildSubdivPoint( pParentQuad->m_vCentroid, pParentQuad->m_vNormal ); pQuad->m_PointHandles[1] = BuildSubdivPoint( pEdge1->m_vNewEdgePoint, pEdge1->m_vNewEdgeNormal ); pQuad->m_PointHandles[2] = pParentQuad->m_PointHandles[2]; pQuad->m_PointHandles[3] = BuildSubdivPoint( pEdge2->m_vNewEdgePoint, pEdge2->m_vNewEdgeNormal ); } break; } case 3: { // displacement quad indices pQuad->m_QuadIndices[0] = ( pParentQuad->m_QuadIndices[0] + pParentQuad->m_QuadIndices[3] ) * 0.5f; pQuad->m_QuadIndices[1] = ( pParentQuad->m_QuadIndices[0] + pParentQuad->m_QuadIndices[2] ) * 0.5f; pQuad->m_QuadIndices[2] = ( pParentQuad->m_QuadIndices[2] + pParentQuad->m_QuadIndices[3] ) * 0.5f; pQuad->m_QuadIndices[3] = pParentQuad->m_QuadIndices[3]; // new verts SubdivEdge_t *pEdge2 = GetEdge( pParentQuad->m_EdgeHandles[2] ); SubdivEdge_t *pEdge3 = GetEdge( pParentQuad->m_EdgeHandles[3] ); if ( pEdge2 && pEdge3 ) { pQuad->m_PointHandles[0] = BuildSubdivPoint( pEdge3->m_vNewEdgePoint, pEdge3->m_vNewEdgeNormal ); pQuad->m_PointHandles[1] = BuildSubdivPoint( pParentQuad->m_vCentroid, pParentQuad->m_vNormal ); pQuad->m_PointHandles[2] = BuildSubdivPoint( pEdge2->m_vNewEdgePoint, pEdge2->m_vNewEdgeNormal ); pQuad->m_PointHandles[3] = pParentQuad->m_PointHandles[3]; } break; } } // // buidl new quad edges // for ( int ndxEdge = 0; ndxEdge < 4; ndxEdge++ ) { pQuad->m_EdgeHandles[ndxEdge] = BuildSubdivEdge( ndxEdge, quadHandle, parentHandle, ndxChild ); } return quadHandle; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CEditDispSubdivMesh::Init( void ) { // ensure capacity on all lists IWorldEditDispMgr *pDispMgr = GetActiveWorldEditDispManager(); if( !pDispMgr ) return; int selectCount = pDispMgr->SelectCount(); m_Points.EnsureCapacity( SUBDIV_DISPPOINTS * selectCount ); m_Edges.EnsureCapacity( SUBDIV_DISPEDGES * selectCount ); m_Quads.EnsureCapacity( SUBDIV_DISPQUADS * selectCount ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CEditDispSubdivMesh::Shutdown( void ) { // clear all lists m_Points.Purge(); m_Edges.Purge(); m_Quads.Purge(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CEditDispSubdivMesh::AddDispTo( CMapDisp *pDisp ) { // add a quad to the subdivision mesh SubdivQuadHandle_t quadHandle = m_Quads.AddToTail(); Quad_Init( quadHandle ); SubdivQuad_t *pQuad = &m_Quads.Element( quadHandle ); // this is the parent! pQuad->m_ndxParent = m_Quads.InvalidIndex(); pQuad->m_EditDispHandle = pDisp->GetEditHandle(); pQuad->m_Level = 0; // // get displacement data // int dispWidth = pDisp->GetWidth(); int dispHeight = pDisp->GetHeight(); // // setup mapping between the displacement size and initial quad indices // pQuad->m_QuadIndices[0] = 0; pQuad->m_QuadIndices[1] = dispWidth * ( dispHeight - 1 ); pQuad->m_QuadIndices[2] = ( dispWidth * dispHeight ) - 1; pQuad->m_QuadIndices[3] = ( dispWidth - 1 ); // // find point normals and neighbors -- "smooth" // NOTE: this is slow -- should write a faster version (is offline process, do later) // IWorldEditDispMgr *pDispMgr = GetActiveWorldEditDispManager(); if( !pDispMgr ) return; Vector vPoints[4]; Vector vPointNormals[4]; for( int ndxPt = 0; ndxPt < EDITDISP_QUADSIZE; ndxPt++ ) { // get the base face normal of all surfaces touching this point! pDisp->GetSurfNormal( vPointNormals[ndxPt] ); // get the point to compare to neighbors pDisp->GetSurfPoint( ndxPt, vPoints[ndxPt] ); int count = pDispMgr->SelectCount(); for( int ndxSelect = 0; ndxSelect < count; ndxSelect++ ) { CMapDisp *pSelectDisp = pDispMgr->GetFromSelect( ndxSelect ); if( !pSelectDisp || ( pSelectDisp == pDisp ) ) continue; for( int ndxPt2 = 0; ndxPt2 < EDITDISP_QUADSIZE; ndxPt2++ ) { Vector vPoint; pSelectDisp->GetSurfPoint( ndxPt2, vPoint ); if( CompareSubdivPoints( vPoints[ndxPt], vPoint, 0.01f ) ) { Vector vNormal; pSelectDisp->GetSurfNormal( vNormal ); VectorAdd( vPointNormals[ndxPt], vNormal, vPointNormals[ndxPt] ); } } } VectorNormalize( vPointNormals[ndxPt] ); } // build subdivision points for( int ndxPt = 0; ndxPt < EDITDISP_QUADSIZE; ndxPt++ ) { pQuad->m_PointHandles[ndxPt] = BuildSubdivPoint( vPoints[ndxPt], vPointNormals[ndxPt] ); } // build subdivision edges for( int ndxEdge = 0; ndxEdge < EDITDISP_QUADSIZE; ndxEdge++ ) { pQuad->m_EdgeHandles[ndxEdge] = BuildSubdivEdge( ndxEdge, quadHandle, m_Quads.InvalidIndex(), -1 ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CEditDispSubdivMesh::GetDispFrom( CMapDisp *pDisp ) { // // find the parent quad with the id of the displacement // for ( SubdivQuadHandle_t quadHandle = m_Quads.Head(); quadHandle != m_Quads.InvalidIndex(); quadHandle = m_Quads.Next( quadHandle ) ) { SubdivQuad_t *pQuad = GetQuad( quadHandle ); if ( pQuad ) { // find children quads that "belong" to this displacement if( pQuad->m_EditDispHandle != pDisp->GetEditHandle() ) continue; // get the data at the appropriate level -- (based on the size of the displacement) if ( pQuad->m_Level != pDisp->GetPower() ) continue; // // fill in subdivision positions and normals // for ( int ndxPt = 0; ndxPt < 4; ndxPt++ ) { SubdivPoint_t *pPoint = GetPoint( pQuad->m_PointHandles[ndxPt] ); if ( pPoint ) { Vector vFlatVert, vSubVert; pDisp->GetFlatVert( pQuad->m_QuadIndices[ndxPt], vFlatVert ); VectorSubtract( pPoint->m_vPoint, vFlatVert, vSubVert ); pDisp->UpdateVertPositionForSubdiv( pQuad->m_QuadIndices[ndxPt], vSubVert ); pDisp->SetSubdivNormal( pQuad->m_QuadIndices[ndxPt], pPoint->m_vNormal ); } } } } // tell the dispalcemet to update itself pDisp->UpdateData(); // reset subdivision/subdivided flags pDisp->SetReSubdivision( false ); pDisp->SetSubdivided( true ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CEditDispSubdivMesh::DoCatmullClarkSubdivision( void ) { for ( int ndxLevel = 0; ndxLevel < NUM_SUBDIV_LEVELS; ndxLevel++ ) { // subdivide CatmullClarkSubdivision(); // update the subdivision hierarchy (tree) UpdateSubdivisionHierarchy( ndxLevel ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CEditDispSubdivMesh::CatmullClarkSubdivision( void ) { // // step 1: calculate the "new edge points" for all edges // for ( SubdivEdgeHandle_t edgeHandle = m_Edges.Head(); edgeHandle != m_Edges.InvalidIndex(); edgeHandle = m_Edges.Next( edgeHandle ) ) { Edge_CalcNewPoint( edgeHandle ); } // // step 2: calculate the valence and edge list // for ( SubdivPointHandle_t ptHandle = m_Points.Head(); ptHandle != m_Points.InvalidIndex(); ptHandle = m_Points.Next( ptHandle ) ) { for ( SubdivEdgeHandle_t edgeHandle = m_Edges.Head(); edgeHandle != m_Edges.InvalidIndex(); edgeHandle = m_Edges.Next( edgeHandle ) ) { SubdivEdge_t *pEdge = GetEdge( edgeHandle ); if ( !pEdge->m_bActive ) continue; if ( ( ptHandle == pEdge->m_PointHandles[0] ) || ( ptHandle == pEdge->m_PointHandles[1] ) ) { SubdivPoint_t *pPoint = GetPoint( ptHandle ); if ( pPoint->m_uValence < ( EDITDISP_QUADSIZE*4 ) ) { pPoint->m_EdgeHandles[pPoint->m_uValence] = edgeHandle; pPoint->m_uValence++; } } } } // // step 3: determine the point's Type (Oridinary, Corner, Crease) // for ( SubdivPointHandle_t ptHandle = m_Points.Head(); ptHandle != m_Points.InvalidIndex(); ptHandle = m_Points.Next( ptHandle ) ) { SubdivPoint_t *pPoint = GetPoint( ptHandle ); if ( pPoint ) { int sharpCount = 0; int sharpThreshold = pPoint->m_uValence - 1; bool bHasNeighbors = false; // initialize as oridinary -- determine otherwise pPoint->m_uType = SUBDIV_POINTORDINARY; for ( int ndxEdge = 0; ndxEdge < pPoint->m_uValence; ndxEdge++ ) { SubdivEdge_t *pEdge = GetEdge( pPoint->m_EdgeHandles[ndxEdge] ); if ( pEdge ) { if ( pEdge->m_flSharpness > 0.0f ) { sharpCount++; } if ( pEdge->m_QuadHandles[1] != m_Quads.InvalidIndex() ) { bHasNeighbors = true; } } } if ( !bHasNeighbors || ( sharpCount >= sharpThreshold ) ) { pPoint->m_uType = SUBDIV_POINTCORNER; } else if( sharpCount > 1 ) { pPoint->m_uType = SUBDIV_POINTCREASE; } } } // // step 4: calculate the "new points" for all points // for ( SubdivPointHandle_t ptHandle = m_Points.Head(); ptHandle != m_Points.InvalidIndex(); ptHandle = m_Points.Next( ptHandle ) ) { Point_CalcNewPoint( ptHandle ); } // // step 5: copy all "new point" data to point data // for ( SubdivPointHandle_t ptHandle = m_Points.Head(); ptHandle != m_Points.InvalidIndex(); ptHandle = m_Points.Next( ptHandle ) ) { SubdivPoint_t *pPoint = GetPoint( ptHandle ); VectorCopy( pPoint->m_vNewPoint, pPoint->m_vPoint ); VectorCopy( pPoint->m_vNewNormal, pPoint->m_vNewNormal ); VectorClear( pPoint->m_vNewPoint ); VectorClear( pPoint->m_vNewNormal ); // reset valence pPoint->m_uValence = 0; } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CEditDispSubdivMesh::UpdateSubdivisionHierarchy( int ndxLevel ) { int quadCount = m_Quads.Count(); SubdivQuadHandle_t quadHandle = m_Quads.Head(); int ndxQuad = 0; while ( ( quadHandle != m_Quads.InvalidIndex() ) && ( ndxQuad < quadCount ) ) { SubdivQuad_t *pQuad = GetQuad( quadHandle ); if ( pQuad ) { // skip parent quads if ( pQuad->m_ndxChild[0] != m_Quads.InvalidIndex() ) { ndxQuad++; quadHandle = m_Quads.Next( quadHandle ); continue; } for( int ndxChild = 0; ndxChild < 4; ndxChild++ ) { pQuad->m_ndxChild[ndxChild] = BuildSubdivQuad( ndxChild, quadHandle ); } // de-activate all edges (children's edges are active now!) for ( int ndxEdge = 0; ndxEdge < 4; ndxEdge++ ) { SubdivEdge_t *pEdge = GetEdge( pQuad->m_EdgeHandles[ndxEdge] ); if ( pEdge ) { pEdge->m_bActive = false; } } } ndxQuad++; quadHandle = m_Quads.Next( quadHandle ); } }