//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ // //=============================================================================// #include #include #include "FaceEditSheet.h" #include "MainFrm.h" #include "GlobalFunctions.h" #include "MapDisp.h" #include "MapFace.h" #include "utlvector.h" #include "disp_tesselate.h" // memdbgon must be the last include file in a .cpp file!!! #include //============================================================================ // // e1 // c1------c2 // | | // e0 | | e2 // | | // c0------c3 // e3 // // Note: edges refer to internal edge points only, corners "contain" all surfaces // touching the corner (surfaces that only touch the corner, as well as those // "edges" that end/begin at the corner(s)) // #define DISPSEW_POINT_TOLERANCE 1.0f // one unit #define DISPSEW_NULL_INDEX -99999 #define DISPSEW_EDGE_NORMAL 0 #define DISPSEW_EDGE_TJSTART 1 #define DISPSEW_EDGE_TJEND 2 #define DISPSEW_EDGE_TJ 3 #define DISPSEW_FACES_AT_EDGE 3 #define DISPSEW_FACES_AT_CORNER 16 #define DISPSEW_FACES_AT_TJUNC 8 struct SewEdgeData_t { int faceCount; // number of faces contributing to the edge sew CMapFace *pFaces[DISPSEW_FACES_AT_EDGE]; // the faces contributing to the edge sew int ndxEdges[DISPSEW_FACES_AT_EDGE]; // the faces' edge indices contributing to the edge sew int type[DISPSEW_FACES_AT_EDGE]; // the type of edge t-junction, match t-junction start, etc.... }; struct SewCornerData_t { int faceCount; // number of faces contributing to the corner sew CMapFace *pFaces[DISPSEW_FACES_AT_CORNER]; // the faces contributing to the corner sew int ndxCorners[DISPSEW_FACES_AT_CORNER]; // the faces' corner indices contributing to the corner sew }; struct SewTJuncData_t { int faceCount; // number of faces contributing to the t-junction sew CMapFace *pFaces[DISPSEW_FACES_AT_TJUNC]; // the faces contributing to the t-junction sew int ndxCorners[DISPSEW_FACES_AT_TJUNC]; // the faces' corner indices contributing to the t-junction sew int ndxEdges[DISPSEW_FACES_AT_TJUNC]; // the faces' edge (midpoint) indices contributing to the t-junction sew }; static CUtlVector s_EdgeData; static CUtlVector s_CornerData; static CUtlVector s_TJData; static CUtlVector m_aCoreDispInfos; // local functions void SewCorner_Build( void ); void SewCorner_Resolve( void ); void SewCorner_Destroy( SewCornerData_t *pCornerData ); void SewTJunc_Build( void ); void SewTJunc_Resolve( void ); void SewTJunc_Destroy( SewTJuncData_t *pTJData ); void SewEdge_Build( void ); void SewEdge_Resolve( void ); void SewEdge_Destroy( SewEdgeData_t *pEdgeData ); void PlanarizeDependentVerts( void ); //----------------------------------------------------------------------------- // Purpose: compare two point positions to see if they are equivolent given a // tolerance //----------------------------------------------------------------------------- bool PointCompareWithTolerance( Vector const& pt1, Vector const& pt2, float tolerance ) { for( int i = 0 ; i < 3 ; i++ ) { if( fabs( pt1[i] - pt2[i] ) > tolerance ) return false; } return true; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- bool EdgeCompare( Vector *pEdgePts1, Vector *pEdgePts2, int &edgeType1, int &edgeType2 ) { Vector edge1[3]; Vector edge2[3]; // // create edges and midpoints // edge1[0] = pEdgePts1[0]; edge1[1] = ( pEdgePts1[0] + pEdgePts1[1] ) * 0.5f; edge1[2] = pEdgePts1[1]; edge2[0] = pEdgePts2[0]; edge2[1] = ( pEdgePts2[0] + pEdgePts2[1] ) * 0.5f; edge2[2] = pEdgePts2[1]; // assume edge type to be normal (will get overridden if otherwise) edgeType1 = DISPSEW_EDGE_NORMAL; edgeType2 = DISPSEW_EDGE_NORMAL; // // compare points and determine how many are shared between the two edges // int overlapCount = 0; int ndxEdge1[2]; int ndxEdge2[2]; for( int ndx1 = 0; ndx1 < 3; ndx1++ ) { for( int ndx2 = 0; ndx2 < 3; ndx2++ ) { if( PointCompareWithTolerance( edge1[ndx1], edge2[ndx2], DISPSEW_POINT_TOLERANCE ) ) { // no midpoint to midpoint sharing allowed (midpoints are odd index values) if( ( ndx1%2 != 0 ) && ( ndx2%2 != 0 ) ) continue; // sanity check assert( overlapCount >= 0 ); assert( overlapCount < 2 ); ndxEdge1[overlapCount] = ndx1; ndxEdge2[overlapCount] = ndx2; overlapCount++; break; } } } if( overlapCount != 2 ) return false; // handle edge1 as t-junction edge if( ndxEdge1[0]%2 != 0 ) { edgeType1 = DISPSEW_EDGE_TJ; if( ndxEdge1[1] == 0 ) { edgeType2 = DISPSEW_EDGE_TJSTART; } else if( ndxEdge1[1] == 2 ) { edgeType2 = DISPSEW_EDGE_TJEND; } } else if( ndxEdge1[1]%2 != 0 ) { edgeType1 = DISPSEW_EDGE_TJ; if( ndxEdge1[0] == 0 ) { edgeType2 = DISPSEW_EDGE_TJSTART; } else if( ndxEdge1[0] == 2 ) { edgeType2 = DISPSEW_EDGE_TJEND; } } // handle edge2 as t-junction edge if( ndxEdge2[0]%2 != 0 ) { edgeType2 = DISPSEW_EDGE_TJ; if( ndxEdge2[1] == 0 ) { edgeType1 = DISPSEW_EDGE_TJSTART; } else if( ndxEdge2[1] == 2 ) { edgeType1 = DISPSEW_EDGE_TJEND; } } else if( ndxEdge2[1]%2 != 0 ) { edgeType2 = DISPSEW_EDGE_TJ; if( ndxEdge2[0] == 0 ) { edgeType1 = DISPSEW_EDGE_TJSTART; } else if( ndxEdge2[0] == 2 ) { edgeType1 = DISPSEW_EDGE_TJEND; } } return true; } //----------------------------------------------------------------------------- // Purpose: get a point from the surface at the given index, will get the point // from the displacement surface if it exists, it will get it from the // base face otherwise //----------------------------------------------------------------------------- inline void GetPointFromSurface( CMapFace *pFace, int ndxPt, Vector &pt ) { EditDispHandle_t dispHandle = pFace->GetDisp(); if( dispHandle != EDITDISPHANDLE_INVALID ) { CMapDisp *pDisp = EditDispMgr()->GetDisp( dispHandle ); pDisp->GetSurfPoint( ndxPt, pt ); } else { pFace->GetPoint( pt, ndxPt ); } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- int GetEdgePointIndex( CMapDisp *pDisp, int edgeIndex, int edgePtIndex, bool bCCW ) { int height = pDisp->GetHeight(); int width = pDisp->GetWidth(); if( bCCW ) { switch( edgeIndex ) { case 0: { return ( edgePtIndex * height ); } case 1: { return ( ( ( height - 1 ) * width ) + edgePtIndex ); } case 2: { return ( ( height * width - 1 ) - ( edgePtIndex * height ) ); } case 3: { return ( ( width - 1 ) - edgePtIndex ); } default: { return -1; } } } else { switch( edgeIndex ) { case 0: { return ( ( ( height - 1 ) * width ) - ( edgePtIndex * height ) ); } case 1: { return ( ( height * width - 1 ) - edgePtIndex ); } case 2: { return ( ( width - 1 ) + ( edgePtIndex * height ) ); } case 3: { return ( edgePtIndex ); } default: { return -1; } } } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- int GetCornerPointIndex( CMapDisp *pDisp, int cornerIndex ) { int width = pDisp->GetWidth(); int height = pDisp->GetHeight(); switch( cornerIndex ) { case 0: { return 0; } case 1: { return ( ( height - 1 ) * width ); } case 2: { return ( height * width - 1 ); } case 3: { return ( width - 1 ); } default: { return -1; } } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- int GetTJuncIndex( CMapDisp *pDisp, int ndxEdge ) { int width = pDisp->GetWidth(); int height = pDisp->GetHeight(); switch( ndxEdge ) { case 0: { return( height * ( width / 2 ) ); } case 1: { return( ( ( height - 1 ) * width ) + ( width / 2 ) ); } case 2: { return( ( height * ( width / 2 ) ) + ( width - 1 ) ); } case 3: { return( width / 2 ); } default: { return -1; } } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void AverageVectorFieldData( CMapDisp *pDisp1, int ndx1, CMapDisp *pDisp2, int ndx2 ) { // // average the positions at each index // position = dispVector * dispDist // float dist1 = pDisp1->GetFieldDistance( ndx1 ); float dist2 = pDisp2->GetFieldDistance( ndx2 ); Vector v1, v2; pDisp1->GetFieldVector( ndx1, v1 ); pDisp2->GetFieldVector( ndx2, v2 ); v1 *= dist1; v2 *= dist2; Vector vAvg; vAvg = ( v1 + v2 ) * 0.5f; float distAvg = VectorNormalize( vAvg ); pDisp1->SetFieldDistance( ndx1, distAvg ); pDisp2->SetFieldDistance( ndx2, distAvg ); pDisp1->SetFieldVector( ndx1, vAvg ); pDisp2->SetFieldVector( ndx2, vAvg ); // Check to see if the materials match and blend alphas if they do. CMapFace *pFace1 = static_cast( pDisp1->GetParent() ); CMapFace *pFace2 = static_cast( pDisp2->GetParent() ); char szMatName1[128]; char szMatName2[128]; pFace1->GetTexture()->GetShortName( szMatName1 ); pFace2->GetTexture()->GetShortName( szMatName2 ); if ( !strcmpi( szMatName1, szMatName2 ) ) { // Grab the alphas at the points and average them. float flAlpha1, flAlpha2; flAlpha1 = pDisp1->GetAlpha( ndx1 ); flAlpha2 = pDisp2->GetAlpha( ndx2 ); float flAlphaBlend = ( flAlpha1 + flAlpha1 ) * 0.5f; pDisp1->SetAlpha( ndx1, flAlphaBlend ); pDisp2->SetAlpha( ndx2, flAlphaBlend ); } // // average the subdivion positions and normals // pDisp1->GetSubdivPosition( ndx1, v1 ); pDisp2->GetSubdivPosition( ndx2, v2 ); vAvg = ( v1 + v2 ) * 0.5f; pDisp1->SetSubdivPosition( ndx1, vAvg ); pDisp2->SetSubdivPosition( ndx2, vAvg ); pDisp1->GetSubdivNormal( ndx1, v1 ); pDisp2->GetSubdivNormal( ndx2, v2 ); vAvg = v1 + v2; VectorNormalize( vAvg ); pDisp1->SetSubdivNormal( ndx1, vAvg ); pDisp2->SetSubdivNormal( ndx2, vAvg ); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void BlendVectorFieldData( CMapDisp *pDisp1, int ndxSrc1, int ndxDst1, CMapDisp *pDisp2, int ndxSrc2, int ndxDst2, float blendFactor ) { // // to blend positions -- calculate the positions at the end points // find the new point along the parameterized line and calculate the // new field vector direction and distance (position) // float dist1 = pDisp1->GetFieldDistance( ndxSrc1 ); float dist2 = pDisp2->GetFieldDistance( ndxSrc2 ); Vector v1, v2; pDisp1->GetFieldVector( ndxSrc1, v1 ); pDisp2->GetFieldVector( ndxSrc2, v2 ); v1 *= dist1; v2 *= dist2; Vector vBlend; vBlend = v1 + ( v2 - v1 ) * blendFactor; float distBlend = VectorNormalize( vBlend ); pDisp1->SetFieldDistance( ndxDst1, distBlend ); pDisp2->SetFieldDistance( ndxDst2, distBlend ); pDisp1->SetFieldVector( ndxDst1, vBlend ); pDisp2->SetFieldVector( ndxDst2, vBlend ); // Check to see if the materials match and blend alphas if they do. CMapFace *pFace1 = static_cast( pDisp1->GetParent() ); CMapFace *pFace2 = static_cast( pDisp2->GetParent() ); char szMatName1[128]; char szMatName2[128]; pFace1->GetTexture()->GetShortName( szMatName1 ); pFace2->GetTexture()->GetShortName( szMatName2 ); if ( !strcmpi( szMatName1, szMatName2 ) ) { float flAlpha1, flAlpha2; flAlpha1 = pDisp1->GetAlpha( ndxDst1 ); flAlpha2 = pDisp2->GetAlpha( ndxDst2 ); float flAlphaBlend = flAlpha1 + ( flAlpha2 - flAlpha1 ) * blendFactor; pDisp1->SetAlpha( ndxDst1, flAlphaBlend ); pDisp2->SetAlpha( ndxDst2, flAlphaBlend ); } // // blend subdivision positions and normals as before, // this isn't truly accurate, but I am not sure what these // values mean in the edge sewing case anyway??? // pDisp1->GetSubdivPosition( ndxSrc1, v1 ); pDisp2->GetSubdivPosition( ndxSrc2, v2 ); vBlend = v1 + ( v2 - v1 ) * blendFactor; pDisp1->SetSubdivPosition( ndxDst1, vBlend ); pDisp2->SetSubdivPosition( ndxDst2, vBlend ); pDisp1->GetSubdivNormal( ndxSrc1, v1 ); pDisp2->GetSubdivNormal( ndxSrc2, v2 ); vBlend = v1 + ( v2 - v1 ) * blendFactor; pDisp1->SetSubdivNormal( ndxDst1, vBlend ); pDisp2->SetSubdivNormal( ndxDst2, vBlend ); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- inline bool Face_IsSolid( CMapFace *pFace ) { return ( pFace->GetDisp() == EDITDISPHANDLE_INVALID ); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void Faces_Update( void ) { // // get the "faces" selection list (contains displaced and non-displaced faces) // CFaceEditSheet *pSheet = GetMainWnd()->GetFaceEditSheet(); if( !pSheet ) return; // // for each face in list // int faceCount = pSheet->GetFaceListCount(); for( int ndxFace = 0; ndxFace < faceCount; ndxFace++ ) { // get the current face CMapFace *pFace = pSheet->GetFaceListDataFace( ndxFace ); if( !pFace ) continue; // only update displacement surfaces EditDispHandle_t dispHandle = pFace->GetDisp(); if( dispHandle == EDITDISPHANDLE_INVALID ) continue; CMapDisp *pDisp = EditDispMgr()->GetDisp( dispHandle ); pDisp->UpdateData(); } } //----------------------------------------------------------------------------- // Purpose: Build temporary edge/midpoint/corner info for sewing. //----------------------------------------------------------------------------- void PreFaceListSew( void ) { // Build edge/midpoint/corner data. SewCorner_Build(); SewTJunc_Build(); SewEdge_Build(); } //----------------------------------------------------------------------------- // Purpose: Destroy temporary edge/midpoint/corner info for sewing and // update the effected displacements. //----------------------------------------------------------------------------- void PostFaceListSew( void ) { // Destroy all corners, midpoint, edges. int count = s_CornerData.Size(); for( int i = 0; i < count; i++ ) { SewCorner_Destroy( s_CornerData.Element( i ) ); } count = s_TJData.Size(); for( int i = 0; i < count; i++ ) { SewTJunc_Destroy( s_TJData.Element( i ) ); } count = s_EdgeData.Size(); for( int i = 0; i < count; i++ ) { SewEdge_Destroy( s_EdgeData.Element( i ) ); } // Flush all of the sewing data buffers. s_CornerData.Purge(); s_TJData.Purge(); s_EdgeData.Purge(); // Update the faces. Faces_Update(); } //----------------------------------------------------------------------------- // Purpose: given a face with a displacement surface, "sew" all edges to all // neighboring displacement and non-displacement surfaces // found in the selection set //----------------------------------------------------------------------------- void FaceListSewEdges( void ) { // Setup. PreFaceListSew(); // Resolve/Planarize unusable verts. PlanarizeDependentVerts(); // Resolve sewing. SewCorner_Resolve(); SewTJunc_Resolve(); SewEdge_Resolve(); // Update and clean-up. PostFaceListSew(); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- SewCornerData_t *SewCorner_Create( void ) { SewCornerData_t *pCornerData = new SewCornerData_t; if( pCornerData ) { // initialize the data pCornerData->faceCount = 0; return pCornerData; } return NULL; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void SewCorner_Destroy( SewCornerData_t *pCornerData ) { if( pCornerData ) { delete pCornerData; pCornerData = NULL; } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- bool SewCorner_IsSolid( SewCornerData_t *pCornerData ) { for( int i = 0; i < pCornerData->faceCount; i++ ) { if( Face_IsSolid( pCornerData->pFaces[i] ) ) return true; } return false; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void SewCorner_Add( SewCornerData_t *pCornerData, CMapFace *pFace, int ndx ) { if ( pCornerData->faceCount >= DISPSEW_FACES_AT_CORNER ) { AfxMessageBox( "Warning: Too many displacement faces at corner!\n" ); return; } pCornerData->pFaces[pCornerData->faceCount] = pFace; pCornerData->ndxCorners[pCornerData->faceCount] = ndx; pCornerData->faceCount++; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void SewCorner_AddToList( SewCornerData_t *pCornerData ) { // get the current corner point Vector pt; GetPointFromSurface( pCornerData->pFaces[0], pCornerData->ndxCorners[0], pt ); // // check to see if the corner point already exists in the corner data list // int cornerCount = s_CornerData.Size(); for( int i = 0; i < cornerCount; i++ ) { // // get the compare corner point // SewCornerData_t *pCmpData = s_CornerData.Element( i ); if( !pCmpData ) continue; Vector cmpPt; GetPointFromSurface( pCmpData->pFaces[0], pCmpData->ndxCorners[0], cmpPt ); // compare the points - return if found if( PointCompareWithTolerance( pt, cmpPt, DISPSEW_POINT_TOLERANCE ) ) { SewCorner_Destroy( pCornerData ); return; } } // unique corner point -- add it to the list s_CornerData.AddToTail( pCornerData ); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void SewCorner_Build( void ) { // // get the "faces" selection list (contains displaced and non-displaced faces) // CFaceEditSheet *pSheet = GetMainWnd()->GetFaceEditSheet(); if( !pSheet ) return; // // for each face in list // int faceCount = pSheet->GetFaceListCount(); for( int ndxFace = 0; ndxFace < faceCount; ndxFace++ ) { // get the current face CMapFace *pFace = pSheet->GetFaceListDataFace( ndxFace ); if( !pFace ) continue; // // for each face point // int ptCount = pFace->GetPointCount(); for( int ndxPt = 0; ndxPt < ptCount; ndxPt++ ) { // get the current point Vector pt; GetPointFromSurface( pFace, ndxPt, pt ); // allocate new corner point SewCornerData_t *pCornerData = SewCorner_Create(); if( !pCornerData ) return; // // compare this point to all of the other points on all the other faces in the list // for( int ndxFace2 = 0; ndxFace2 < faceCount; ndxFace2++ ) { // don't compare to itself if( ndxFace == ndxFace2 ) continue; // get the current compare face CMapFace *pFace2 = pSheet->GetFaceListDataFace( ndxFace2 ); if( !pFace2 ) continue; // // for each compare face point // int ptCount2 = pFace2->GetPointCount(); for( int ndxPt2 = 0; ndxPt2 < ptCount2; ndxPt2++ ) { // get the current compare point Vector pt2; GetPointFromSurface( pFace2, ndxPt2, pt2 ); // compare pt1 and pt2 if( PointCompareWithTolerance( pt, pt2, DISPSEW_POINT_TOLERANCE ) ) { SewCorner_Add( pCornerData, pFace2, ndxPt2 ); } } } // had neighbors -- add base point and add it to corner list if( pCornerData->faceCount > 0 ) { SewCorner_Add( pCornerData, pFace, ndxPt ); SewCorner_AddToList( pCornerData ); } // no neighbors -- de-allocate else { SewCorner_Destroy( pCornerData ); } } } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void SewCorner_ResolveDisp( SewCornerData_t *pCornerData ) { // the field data accumulators float avgDist = 0.0f; Vector vAvgField( 0.0f, 0.0f, 0.0f ); Vector vAvgSubdivPos( 0.0f, 0.0f, 0.0f ); Vector vAvgSubdivNormal( 0.0f, 0.0f, 0.0f ); float flAvgAlpha = 0.0f; // Blend the alpha? bool bBlendAlpha = true; char szMatName1[128]; char szMatName2[128]; bool bInitMat = false; for( int i = 0; i < pCornerData->faceCount; i++ ) { // get the current corner face CMapFace *pFace = pCornerData->pFaces[i]; if( !pFace ) continue; // get the current displacement surface to reset, if solid = done! EditDispHandle_t dispHandle = pFace->GetDisp(); if( dispHandle == EDITDISPHANDLE_INVALID ) continue; if ( !bInitMat ) { pFace->GetTexture()->GetShortName( szMatName1 ); bInitMat = true; continue; } else { pFace->GetTexture()->GetShortName( szMatName2 ); if ( strcmpi( szMatName1, szMatName2 ) ) { bBlendAlpha = false; break; } } } // for all the faces at the corner for( int i = 0; i < pCornerData->faceCount; i++ ) { // get the current corner face CMapFace *pFace = pCornerData->pFaces[i]; if( !pFace ) continue; // get the current displacement surface to reset, if solid = done! EditDispHandle_t dispHandle = pFace->GetDisp(); if( dispHandle == EDITDISPHANDLE_INVALID ) continue; CMapDisp *pDisp = EditDispMgr()->GetDisp( dispHandle ); // get the corner index int ndxPt = GetCornerPointIndex( pDisp, pCornerData->ndxCorners[i] ); if( ndxPt == -1 ) continue; Vector vecPos; pDisp->GetVert( ndxPt, vecPos ); float dist = pDisp->GetFieldDistance( ndxPt ); avgDist += dist; Vector vTmp; pDisp->GetFieldVector( ndxPt, vTmp ); vAvgField += vTmp; pDisp->GetSubdivPosition( ndxPt, vTmp ); vAvgSubdivPos += vTmp; pDisp->GetSubdivNormal( ndxPt, vTmp ); vAvgSubdivNormal += vTmp; if ( bBlendAlpha ) { flAvgAlpha += pDisp->GetAlpha( ndxPt ); } } // calculate the average avgDist /= pCornerData->faceCount; vAvgField /= pCornerData->faceCount; vAvgSubdivPos /= pCornerData->faceCount; vAvgSubdivNormal /= pCornerData->faceCount; if ( bBlendAlpha ) { flAvgAlpha /= pCornerData->faceCount; } for( int i = 0; i < pCornerData->faceCount; i++ ) { // get the current corner face CMapFace *pFace = pCornerData->pFaces[i]; if( !pFace ) continue; // get the current displacement surface to reset, if solid = done! EditDispHandle_t dispHandle = pFace->GetDisp(); if( dispHandle == EDITDISPHANDLE_INVALID ) continue; CMapDisp *pDisp = EditDispMgr()->GetDisp( dispHandle ); // get the corner index int ndxPt = GetCornerPointIndex( pDisp, pCornerData->ndxCorners[i] ); if( ndxPt == -1 ) continue; // set the averaged values pDisp->SetFieldDistance( ndxPt, avgDist ); pDisp->SetFieldVector( ndxPt, vAvgField ); pDisp->SetSubdivPosition( ndxPt, vAvgSubdivPos ); pDisp->SetSubdivNormal( ndxPt, vAvgSubdivNormal ); if ( bBlendAlpha ) { pDisp->SetAlpha( ndxPt, flAvgAlpha ); } } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void SewCorner_ResolveSolid( SewCornerData_t *pCornerData ) { // create a clear vector - to reset the offset vector Vector vClear( 0.0f, 0.0f, 0.0f ); // for all the faces at the corner for( int i = 0; i < pCornerData->faceCount; i++ ) { // get the current corner face CMapFace *pFace = pCornerData->pFaces[i]; if( !pFace ) continue; // get the current displacement surface to reset, if solid = done! EditDispHandle_t dispHandle = pFace->GetDisp(); if( dispHandle == EDITDISPHANDLE_INVALID ) continue; CMapDisp *pDisp = EditDispMgr()->GetDisp( dispHandle ); // get the face normal -- to reset the field vector Vector vNormal; pDisp->GetSurfNormal( vNormal ); // get the corner index int ndxPt = GetCornerPointIndex( pDisp, pCornerData->ndxCorners[i] ); if( ndxPt == -1 ) continue; // // reset all neighbor surface data - field vector, distance, and offset // pDisp->SetFieldDistance( ndxPt, 0.0f ); pDisp->SetFieldVector( ndxPt, vNormal ); pDisp->SetSubdivPosition( ndxPt, vClear ); pDisp->SetSubdivNormal( ndxPt, vNormal ); pDisp->SetAlpha( ndxPt, 0.0f ); } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void SewCorner_Resolve( void ) { // get the number of corners in the corner list int cornerCount = s_CornerData.Size(); // resolve each corner for( int i = 0; i < cornerCount; i++ ) { // get the current corner data struct SewCornerData_t *pCornerData = s_CornerData.Element( i ); if( !pCornerData ) continue; // determine if any of the faces is solid bool bSolid = SewCorner_IsSolid( pCornerData ); // solid at corner -- reset corner data if( bSolid ) { SewCorner_ResolveSolid( pCornerData ); } // all disps at corner -- average else { SewCorner_ResolveDisp( pCornerData ); } } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- SewTJuncData_t *SewTJunc_Create( void ) { SewTJuncData_t *pTJData = new SewTJuncData_t; if( pTJData ) { // initialize the data pTJData->faceCount = 0; return pTJData; } return NULL; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void SewTJunc_Destroy( SewTJuncData_t *pTJData ) { if( pTJData ) { delete pTJData; pTJData = NULL; } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- bool SewTJunc_IsSolid( SewTJuncData_t *pTJData ) { for( int i = 0; i < pTJData->faceCount; i++ ) { if( Face_IsSolid( pTJData->pFaces[i] ) ) return true; } return false; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void SewTJunc_Add( SewTJuncData_t *pTJData, CMapFace *pFace, int ndxCorner, int ndxEdge ) { if ( pTJData->faceCount >= DISPSEW_FACES_AT_TJUNC ) { AfxMessageBox( "Warning: Too many displacement faces at t-junction!\n" ); return; } pTJData->pFaces[pTJData->faceCount] = pFace; pTJData->ndxCorners[pTJData->faceCount] = ndxCorner; pTJData->ndxEdges[pTJData->faceCount] = ndxEdge; pTJData->faceCount++; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void SewTJunc_AddToList( SewTJuncData_t *pTJData ) { // get the current t-junction point Vector pt; GetPointFromSurface( pTJData->pFaces[0], pTJData->ndxCorners[0], pt ); // // check to see if the t-junction point already exists in the t-junction data list // int tjCount = s_TJData.Size(); for( int i = 0; i < tjCount; i++ ) { // get the compare t-junction point SewTJuncData_t *pCmpData = s_TJData.Element( i ); if( !pCmpData ) continue; Vector cmpPt; GetPointFromSurface( pCmpData->pFaces[0], pCmpData->ndxCorners[0], cmpPt ); // compare the points - return if found if( PointCompareWithTolerance( pt, cmpPt, DISPSEW_POINT_TOLERANCE ) ) { SewTJunc_Destroy( pTJData ); return; } } // unique t-junction point -- add it to the list s_TJData.AddToTail( pTJData ); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void SewTJunc_Build( void ) { // // get the "faces" selection list (contains displaced and non-displaced faces) // CFaceEditSheet *pSheet = GetMainWnd()->GetFaceEditSheet(); if( !pSheet ) return; // // for each face in list // int faceCount = pSheet->GetFaceListCount(); for( int ndxFace = 0; ndxFace < faceCount; ndxFace++ ) { // get the current face CMapFace *pFace = pSheet->GetFaceListDataFace( ndxFace ); if( !pFace ) continue; // // for each face point // int ptCount = pFace->GetPointCount(); for( int ndxPt = 0; ndxPt < ptCount; ndxPt++ ) { // get the current t-junction point Vector pt, tmpPt1, tmpPt2; GetPointFromSurface( pFace, ndxPt, tmpPt1 ); GetPointFromSurface( pFace, (ndxPt+1)%ptCount, tmpPt2 ); pt = ( tmpPt1 + tmpPt2 ) * 0.5f; // allocate new corner point SewTJuncData_t *pTJData = SewTJunc_Create(); if( !pTJData ) return; // // compare this point to all of the other points on all the other faces in the list // for( int ndxFace2 = 0; ndxFace2 < faceCount; ndxFace2++ ) { // don't compare to itself if( ndxFace == ndxFace2 ) continue; // get the current compare face CMapFace *pFace2 = pSheet->GetFaceListDataFace( ndxFace2 ); if( !pFace2 ) continue; // // for each compare face point // int ptCount2 = pFace2->GetPointCount(); for( int ndxPt2 = 0; ndxPt2 < ptCount2; ndxPt2++ ) { // get the current compare point Vector pt2; GetPointFromSurface( pFace2, ndxPt2, pt2 ); // compare pt1 and pt2 if( PointCompareWithTolerance( pt, pt2, DISPSEW_POINT_TOLERANCE ) ) { SewTJunc_Add( pTJData, pFace2, ndxPt2, -1 ); } } } // had neighbors -- add base point and add it to corner list if( pTJData->faceCount > 0 ) { SewTJunc_Add( pTJData, pFace, -1, ndxPt ); SewTJunc_AddToList( pTJData ); } // no neighbors -- de-allocate else { SewTJunc_Destroy( pTJData ); } } } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void SewTJunc_ResolveDisp( SewTJuncData_t *pTJData ) { // the field data accumulators float avgDist = 0.0f; Vector vAvgField( 0.0f, 0.0f, 0.0f ); Vector vAvgSubdivPos( 0.0f, 0.0f, 0.0f ); Vector vAvgSubdivNormal( 0.0f, 0.0f, 0.0f ); float flAvgAlpha = 0.0f; // for all the faces at the t-junction for( int i = 0; i < pTJData->faceCount; i++ ) { // get the current t-junction face CMapFace *pFace = pTJData->pFaces[i]; if( !pFace ) continue; // get the current displacement surface to reset, if solid = done! EditDispHandle_t dispHandle = pFace->GetDisp(); if( dispHandle == EDITDISPHANDLE_INVALID ) continue; CMapDisp *pDisp = EditDispMgr()->GetDisp( dispHandle ); // get the t-junction index int ndxPt = -1; if( pTJData->ndxCorners[i] != -1 ) { ndxPt = GetCornerPointIndex( pDisp, pTJData->ndxCorners[i] ); } else if( pTJData->ndxEdges[i] != -1 ) { int ndxEdgePt = pDisp->GetWidth() / 2; ndxPt = GetEdgePointIndex( pDisp, pTJData->ndxEdges[i], ndxEdgePt, true ); } if( ndxPt == -1 ) continue; float dist = pDisp->GetFieldDistance( ndxPt ); avgDist += dist; Vector vTmp; pDisp->GetFieldVector( ndxPt, vTmp ); vAvgField += vTmp; pDisp->GetSubdivPosition( ndxPt, vTmp ); vAvgSubdivPos += vTmp; pDisp->GetSubdivNormal( ndxPt, vTmp ); vAvgSubdivNormal += vTmp; flAvgAlpha += pDisp->GetAlpha( ndxPt ); } // calculate the average avgDist /= pTJData->faceCount; vAvgField /= pTJData->faceCount; vAvgSubdivPos /= pTJData->faceCount; vAvgSubdivNormal /= pTJData->faceCount; flAvgAlpha /= pTJData->faceCount; for( int i = 0; i < pTJData->faceCount; i++ ) { // get the current t-junction face CMapFace *pFace = pTJData->pFaces[i]; if( !pFace ) continue; // get the current displacement surface to reset, if solid = done! EditDispHandle_t dispHandle = pFace->GetDisp(); if( dispHandle == EDITDISPHANDLE_INVALID ) continue; CMapDisp *pDisp = EditDispMgr()->GetDisp( dispHandle ); // get the t-junction index int ndxPt = -1; if( pTJData->ndxCorners[i] != -1 ) { ndxPt = GetCornerPointIndex( pDisp, pTJData->ndxCorners[i] ); } else if( pTJData->ndxEdges[i] != -1 ) { int ndxEdgePt = pDisp->GetWidth() / 2; ndxPt = GetEdgePointIndex( pDisp, pTJData->ndxEdges[i], ndxEdgePt, true ); } if( ndxPt == -1 ) continue; // set the averaged values pDisp->SetFieldDistance( ndxPt, avgDist ); pDisp->SetFieldVector( ndxPt, vAvgField ); pDisp->SetSubdivPosition( ndxPt, vAvgSubdivPos ); pDisp->SetSubdivNormal( ndxPt, vAvgSubdivNormal ); pDisp->SetAlpha( ndxPt, flAvgAlpha ); } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void SewTJunc_ResolveSolid( SewTJuncData_t *pTJData ) { // create a clear vector - to reset the offset vector Vector vClear( 0.0f, 0.0f, 0.0f ); // for all the faces at the t-junction for( int i = 0; i < pTJData->faceCount; i++ ) { // get the current t-junction face CMapFace *pFace = pTJData->pFaces[i]; if( !pFace ) continue; // get the current displacement surface to reset, if solid = done! EditDispHandle_t dispHandle = pFace->GetDisp(); if( dispHandle == EDITDISPHANDLE_INVALID ) continue; CMapDisp *pDisp = EditDispMgr()->GetDisp( dispHandle ); // get the face normal -- to reset the field vector Vector vNormal; pDisp->GetSurfNormal( vNormal ); // get the t-junction index int ndxPt = -1; if( pTJData->ndxCorners[i] != -1 ) { ndxPt = GetCornerPointIndex( pDisp, pTJData->ndxCorners[i] ); } else if( pTJData->ndxEdges[i] != -1 ) { int ndxEdgePt = pDisp->GetWidth() / 2; ndxPt = GetEdgePointIndex( pDisp, pTJData->ndxEdges[i], ndxEdgePt, true ); } if( ndxPt == -1 ) continue; // // reset all neighbor surface data - field vector, distance, and offset // pDisp->SetFieldDistance( ndxPt, 0.0f ); pDisp->SetFieldVector( ndxPt, vNormal ); pDisp->SetSubdivPosition( ndxPt, vClear ); pDisp->SetSubdivNormal( ndxPt, vNormal ); pDisp->SetAlpha( ndxPt, 0.0f ); } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void SewTJunc_Resolve( void ) { // get the number of t-junctions in the t-junction list int tjCount = s_TJData.Size(); // resolve each t-junction for( int i = 0; i < tjCount; i++ ) { // get the current t-junction data struct SewTJuncData_t *pTJData = s_TJData.Element( i ); if( !pTJData ) continue; // determine if any of the faces is solid bool bSolid = SewTJunc_IsSolid( pTJData ); // solid at t-junction -- reset t-junction data if( bSolid ) { SewTJunc_ResolveSolid( pTJData ); } // all disps at t-junction -- average else { SewTJunc_ResolveDisp( pTJData ); } } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- SewEdgeData_t *SewEdge_Create( void ) { SewEdgeData_t *pEdgeData = new SewEdgeData_t; if( pEdgeData ) { // initialize the data pEdgeData->faceCount = 0; return pEdgeData; } return NULL; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void SewEdge_Destroy( SewEdgeData_t *pEdgeData ) { if( pEdgeData ) { delete pEdgeData; pEdgeData = NULL; } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- inline bool SewEdge_IsSolidNormal( SewEdgeData_t *pEdgeData ) { for( int i = 0; i < pEdgeData->faceCount; i++ ) { if( Face_IsSolid( pEdgeData->pFaces[i] ) ) return true; } return false; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- inline int SewEdge_TJIndex( SewEdgeData_t *pEdgeData, int type ) { for( int i = 0; i < pEdgeData->faceCount; i++ ) { if( pEdgeData->type[i] == type ) return i; } return -1; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- inline bool SewEdge_IsSolidTJunc( SewEdgeData_t *pEdgeData, int type ) { for( int i = 0; i < pEdgeData->faceCount; i++ ) { if( pEdgeData->type[i] != type ) continue; if( Face_IsSolid( pEdgeData->pFaces[i] ) ) return true; } return false; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- bool SewEdge_Add( SewEdgeData_t *pEdgeData, CMapFace *pFace, int ndxEdge, int type ) { if ( pEdgeData->faceCount >= DISPSEW_FACES_AT_EDGE ) { return false; } // Add face to edge. pEdgeData->pFaces[pEdgeData->faceCount] = pFace; pEdgeData->ndxEdges[pEdgeData->faceCount] = ndxEdge; pEdgeData->type[pEdgeData->faceCount] = type; pEdgeData->faceCount++; return true; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- bool SewEdge_AddToListMerge( SewEdgeData_t *pEdgeData, SewEdgeData_t *pCmpData ) { bool bReturn = true; for( int i = 0; i < pEdgeData->faceCount; i++ ) { // t-junction edges already exist in both (skip it!) if( pEdgeData->type[i] == DISPSEW_EDGE_TJ ) continue; int j; for( j = 0; j < pCmpData->faceCount; j++ ) { // t-junction edges already exist in both (skip it!) if( pCmpData->type[j] == DISPSEW_EDGE_TJ ) continue; if( pEdgeData->type[i] == pCmpData->type[j] ) break; } // no match found -- add it if( j == pCmpData->faceCount ) { if (!SewEdge_Add( pCmpData, pEdgeData->pFaces[i], pEdgeData->ndxEdges[i], pEdgeData->type[i] )) { bReturn = false; } } } return bReturn; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- bool SewEdge_AddToListTJunc( SewEdgeData_t *pEdgeData ) { // find the t-junction edge int ndxTJ = SewEdge_TJIndex( pEdgeData, DISPSEW_EDGE_TJ ); if( ndxTJ == -1 ) return true; // get the t-junction edge point count int ptCount = pEdgeData->pFaces[ndxTJ]->GetPointCount(); // get the current t-junction edge (edge points) Vector edgePts[2]; GetPointFromSurface( pEdgeData->pFaces[ndxTJ], pEdgeData->ndxEdges[ndxTJ], edgePts[0] ); GetPointFromSurface( pEdgeData->pFaces[ndxTJ], (pEdgeData->ndxEdges[ndxTJ]+1)%ptCount, edgePts[1] ); // // check to see if the edge already exists in the edge data list // int edgeCount = s_EdgeData.Size(); for( int i = 0; i < edgeCount; i++ ) { // get the edge points to compare against SewEdgeData_t *pCmpData = s_EdgeData.Element( i ); if( !pCmpData ) continue; // get the compare t-junction edge int ndxCmp = SewEdge_TJIndex( pCmpData, DISPSEW_EDGE_TJ ); if( ndxCmp == -1 ) continue; // get the compare face point count int ptCount2 = pCmpData->pFaces[ndxCmp]->GetPointCount(); Vector edgePts2[2]; GetPointFromSurface( pCmpData->pFaces[ndxCmp], pCmpData->ndxEdges[ndxCmp], edgePts2[0] ); GetPointFromSurface( pCmpData->pFaces[ndxCmp], (pCmpData->ndxEdges[ndxCmp]+1)%ptCount2, edgePts2[1] ); // compare the edges -- return if found int edgeType1, edgeType2; if( EdgeCompare( edgePts, edgePts2, edgeType1, edgeType2 ) ) { bool bReturn = SewEdge_AddToListMerge( pEdgeData, pCmpData ); SewEdge_Destroy( pEdgeData ); return bReturn; } } // unique edge -- add it to the list s_EdgeData.AddToTail( pEdgeData ); return true; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void SewEdge_AddToListNormal( SewEdgeData_t *pEdgeData ) { // get the face point count int ptCount = pEdgeData->pFaces[0]->GetPointCount(); // get the current edge (edge points) Vector edgePts[2]; GetPointFromSurface( pEdgeData->pFaces[0], pEdgeData->ndxEdges[0], edgePts[0] ); GetPointFromSurface( pEdgeData->pFaces[0], (pEdgeData->ndxEdges[0]+1)%ptCount, edgePts[1] ); // // check to see if the edge already exists in the edge data list // int edgeCount = s_EdgeData.Size(); for( int i = 0; i < edgeCount; i++ ) { // get the edge points to compare against SewEdgeData_t *pCmpData = s_EdgeData.Element( i ); if( !pCmpData ) continue; // compare against each edge (all colinear) in struct for( int j = 0; j < pCmpData->faceCount; j++ ) { // get the compare face point count int ptCount2 = pCmpData->pFaces[j]->GetPointCount(); Vector edgePts2[2]; GetPointFromSurface( pCmpData->pFaces[j], pCmpData->ndxEdges[j], edgePts2[0] ); GetPointFromSurface( pCmpData->pFaces[j], (pCmpData->ndxEdges[j]+1)%ptCount2, edgePts2[1] ); // compare the edges -- return if found int edgeType1, edgeType2; if( EdgeCompare( edgePts, edgePts2, edgeType1, edgeType2 ) ) { SewEdge_Destroy( pEdgeData ); return; } } } // unique edge -- add it to the list s_EdgeData.AddToTail( pEdgeData ); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- bool SewEdge_AddToList( SewEdgeData_t *pEdgeData ) { // if this is a "normal" edge - handle it if( pEdgeData->type[0] == DISPSEW_EDGE_NORMAL ) { SewEdge_AddToListNormal( pEdgeData ); return true; } // this is a "t-junction" edge - handle it return SewEdge_AddToListTJunc( pEdgeData ); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void SewEdge_Build( void ) { // // get the "faces" selection list (contains displaced and non-displaced faces) // CFaceEditSheet *pSheet = GetMainWnd()->GetFaceEditSheet(); if( !pSheet ) return; bool bError = false; // // for each face in list // int faceCount = pSheet->GetFaceListCount(); for( int ndxFace = 0; ndxFace < faceCount; ndxFace++ ) { // get the current face CMapFace *pFace = pSheet->GetFaceListDataFace( ndxFace ); if( !pFace ) continue; // // for each face edge // int ptCount = pFace->GetPointCount(); for( int ndxPt = 0; ndxPt < ptCount; ndxPt++ ) { // get the current edge points int type1_keep = 0; Vector edgePts[2]; GetPointFromSurface( pFace, ndxPt, edgePts[0] ); GetPointFromSurface( pFace, (ndxPt+1)%ptCount, edgePts[1] ); // allocate new edge SewEdgeData_t *pEdgeData = SewEdge_Create(); if( !pEdgeData ) return; // // compare this edge to all of the other edges on all the other faces in the list // for( int ndxFace2 = 0; ndxFace2 < faceCount; ndxFace2++ ) { // don't compare to itself if( ndxFace == ndxFace2 ) continue; // get the current compare face CMapFace *pFace2 = pSheet->GetFaceListDataFace( ndxFace2 ); if( !pFace2 ) continue; // // for each compare face edge // int ptCount2 = pFace2->GetPointCount(); for( int ndxPt2 = 0; ndxPt2 < ptCount2; ndxPt2++ ) { // get the current compare edge point Vector edgePts2[2]; GetPointFromSurface( pFace2, ndxPt2, edgePts2[0] ); GetPointFromSurface( pFace2, (ndxPt2+1)%ptCount2, edgePts2[1] ); // compare pt1 and pt2 int type1, type2; if( EdgeCompare( edgePts, edgePts2, type1, type2 ) ) { if (!SewEdge_Add( pEdgeData, pFace2, ndxPt2, type2 )) { bError = true; } type1_keep = type1; } } } // had neighbors -- add base point and add it to corner list if( pEdgeData->faceCount > 0 ) { if (!SewEdge_Add( pEdgeData, pFace, ndxPt, type1_keep )) { bError = true; } if (!SewEdge_AddToList( pEdgeData )) { bError = true; } } // no neighbors -- de-allocate else { SewEdge_Destroy( pEdgeData ); } } } if (bError) { AfxMessageBox("Not all selected faces could be sewn because too many selected faces share a single edge.\n\nLook for places where 3 or more selected faces (displacement or non-displacement) all share an edge."); } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void SewEdge_ResolveDispTJunc( SewEdgeData_t *pEdgeData, int ndxTJ, int ndxTJNeighbor, bool bStart ) { // // handle displacement sewing to displacement t-junction edge // EditDispHandle_t tjEdgeHandle = pEdgeData->pFaces[ndxTJ]->GetDisp(); EditDispHandle_t edgeHandle = pEdgeData->pFaces[ndxTJNeighbor]->GetDisp(); if( ( tjEdgeHandle == EDITDISPHANDLE_INVALID ) || ( edgeHandle == EDITDISPHANDLE_INVALID ) ) return; CMapDisp *pTJEdgeDisp = EditDispMgr()->GetDisp( tjEdgeHandle ); CMapDisp *pEdgeDisp = EditDispMgr()->GetDisp( edgeHandle ); // // get the t-junction edge interval (or half of it) // int tjWidth = pTJEdgeDisp->GetWidth(); int tjInterval = pTJEdgeDisp->GetWidth() / 2; // // get edge interval // int edgeWidth = pEdgeDisp->GetWidth(); int edgeInterval = pEdgeDisp->GetWidth(); int ratio = ( edgeInterval - 1 ) / tjInterval; bool bFlip = ( ratio < 1 ); if( bFlip ) { ratio = tjInterval / ( edgeInterval - 1 ); } // // average the "like" points // if( bStart ) { if( bFlip ) { for( int i = 1, j = ratio; i < edgeInterval; i++, j += ratio ) { int ndxTJPt, ndxEdgePt; ndxTJPt = GetEdgePointIndex( pTJEdgeDisp, pEdgeData->ndxEdges[ndxTJ], j, true ); ndxEdgePt = GetEdgePointIndex( pEdgeDisp, pEdgeData->ndxEdges[ndxTJNeighbor], i, false ); // average AverageVectorFieldData( pEdgeDisp, ndxEdgePt, pTJEdgeDisp, ndxTJPt ); } } else { for( int i = 1, j = ratio; i < tjInterval; i++, j += ratio ) { int ndxTJPt, ndxEdgePt; ndxTJPt = GetEdgePointIndex( pTJEdgeDisp, pEdgeData->ndxEdges[ndxTJ], i, true ); ndxEdgePt = GetEdgePointIndex( pEdgeDisp, pEdgeData->ndxEdges[ndxTJNeighbor], j, false ); // average AverageVectorFieldData( pEdgeDisp, ndxEdgePt, pTJEdgeDisp, ndxTJPt ); } } } else { if( bFlip ) { for( int i = 1, j = ratio; i < edgeWidth; i++, j += ratio ) { int ndxTJPt, ndxEdgePt; ndxTJPt = GetEdgePointIndex( pTJEdgeDisp, pEdgeData->ndxEdges[ndxTJ], j, false ); ndxEdgePt = GetEdgePointIndex( pEdgeDisp, pEdgeData->ndxEdges[ndxTJNeighbor], i, true ); // average AverageVectorFieldData( pEdgeDisp, ndxEdgePt, pTJEdgeDisp, ndxTJPt ); } } else { for( int i = ( tjInterval + 1 ), j = ratio; i < ( tjWidth - 1 ); i++, j += ratio ) { int ndxTJPt, ndxEdgePt; ndxTJPt = GetEdgePointIndex( pTJEdgeDisp, pEdgeData->ndxEdges[ndxTJ], i, true ); ndxEdgePt = GetEdgePointIndex( pEdgeDisp, pEdgeData->ndxEdges[ndxTJNeighbor], j, false ); // average AverageVectorFieldData( pEdgeDisp, ndxEdgePt, pTJEdgeDisp, ndxTJPt ); } } } // // linearly interpolate the "unlike" points // float blendRatio = 1.0f / ratio; if( bFlip ) { for( int i = 0; i < ( tjWidth - ratio ); i += ratio ) { int ndxStart = i; int ndxEnd = ( i + ratio ); int ndxStartPt = GetEdgePointIndex( pTJEdgeDisp, pEdgeData->ndxEdges[ndxTJ], ndxStart, true ); int ndxEndPt = GetEdgePointIndex( pTJEdgeDisp, pEdgeData->ndxEdges[ndxTJ], ndxEnd, true ); for( int j = ( ndxStart + 1 ); j < ndxEnd; j++ ) { float blend = blendRatio * ( j - ndxStart ); int ndxDst = GetEdgePointIndex( pTJEdgeDisp, pEdgeData->ndxEdges[ndxTJ], j, true ); BlendVectorFieldData( pTJEdgeDisp, ndxStartPt, ndxDst, pTJEdgeDisp, ndxEndPt, ndxDst, blend ); } } } else { for( int i = 0; i < ( edgeWidth - ratio ); i += ratio ) { int ndxStart = i; int ndxEnd = ( i + ratio ); int ndxStartPt = GetEdgePointIndex( pEdgeDisp, pEdgeData->ndxEdges[ndxTJNeighbor], ndxStart, true ); int ndxEndPt = GetEdgePointIndex( pEdgeDisp, pEdgeData->ndxEdges[ndxTJNeighbor], ndxEnd, true ); for( int j = ( ndxStart + 1 ); j < ndxEnd; j++ ) { float blend = blendRatio * ( j - ndxStart ); int ndxDst = GetEdgePointIndex( pEdgeDisp, pEdgeData->ndxEdges[ndxTJNeighbor], j, true ); BlendVectorFieldData( pEdgeDisp, ndxStartPt, ndxDst, pEdgeDisp, ndxEndPt, ndxDst, blend ); } } } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void SewEdge_ResolveSolidTJunc( SewEdgeData_t *pEdgeData, int type, bool bStart ) { // create an empty vector to reset the offset with Vector vClear( 0.0f, 0.0f, 0.0f ); for( int i = 0; i < pEdgeData->faceCount; i++ ) { if( pEdgeData->type[i] != type ) continue; // get the displacement surface associated with the face EditDispHandle_t dispHandle = pEdgeData->pFaces[i]->GetDisp(); if( dispHandle == EDITDISPHANDLE_INVALID ) continue; CMapDisp *pDisp = EditDispMgr()->GetDisp( dispHandle ); // get surface normal, to reset vector field to base state Vector vNormal; pDisp->GetSurfNormal( vNormal ); // reset tjstart and tjend if( type != DISPSEW_EDGE_TJ ) { // // for all points along the edge -- reset // int width = pDisp->GetWidth(); for( int j = 1; j < ( width - 1 ); j++ ) { // get the edge point index int ndxPt = GetEdgePointIndex( pDisp, pEdgeData->ndxEdges[i], j, true ); if( ndxPt == -1 ) continue; // // reset displacement data (dist, field vector, and offset vector) // pDisp->SetFieldDistance( ndxPt, 0.0f ); pDisp->SetFieldVector( ndxPt, vNormal ); pDisp->SetSubdivPosition( ndxPt, vClear ); pDisp->SetSubdivNormal( ndxPt, vNormal ); pDisp->SetAlpha( ndxPt, 0.0f ); } } // reset tj (upper and lower) else { // // for all points along the edge -- reset // int width = pDisp->GetWidth(); int widthDiv2 = width / 2; if( bStart ) { for( int j = 1; j < widthDiv2; j++ ) { // get the edge point index int ndxPt = GetEdgePointIndex( pDisp, pEdgeData->ndxEdges[i], j, true ); if( ndxPt == -1 ) continue; // // reset displacement data (dist, field vector, and offset vector) // pDisp->SetFieldDistance( ndxPt, 0.0f ); pDisp->SetFieldVector( ndxPt, vNormal ); pDisp->SetSubdivPosition( ndxPt, vClear ); pDisp->SetSubdivNormal( ndxPt, vNormal ); pDisp->SetAlpha( ndxPt, 0.0f ); } } else { for( int j = ( widthDiv2 + 1 ); j < ( width - 1 ); j++ ) { // get the edge point index int ndxPt = GetEdgePointIndex( pDisp, pEdgeData->ndxEdges[i], j, true ); if( ndxPt == -1 ) continue; // // reset displacement data (dist, field vector, and offset vector) // pDisp->SetFieldDistance( ndxPt, 0.0f ); pDisp->SetFieldVector( ndxPt, vNormal ); pDisp->SetSubdivPosition( ndxPt, vClear ); pDisp->SetSubdivNormal( ndxPt, vNormal ); pDisp->SetAlpha( ndxPt, 0.0f ); } } } } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void SewEdge_ResolveDispNormal( SewEdgeData_t *pEdgeData ) { // // get displacement surfaces -- if any // EditDispHandle_t handle1 = pEdgeData->pFaces[0]->GetDisp(); EditDispHandle_t handle2 = pEdgeData->pFaces[1]->GetDisp(); if( ( handle1 == EDITDISPHANDLE_INVALID ) || ( handle2 == EDITDISPHANDLE_INVALID ) ) return; CMapDisp *pEdgeDisp1 = EditDispMgr()->GetDisp( handle1 ); CMapDisp *pEdgeDisp2 = EditDispMgr()->GetDisp( handle2 ); // // sew displacement edges // // // find displacement with smallest/largest interval // CMapDisp *pSmDisp, *pLgDisp; int smInterval, lgInterval; int ndxSmEdge, ndxLgEdge; if( pEdgeDisp1->GetWidth() > pEdgeDisp2->GetWidth() ) { pSmDisp = pEdgeDisp2; ndxSmEdge = pEdgeData->ndxEdges[1]; smInterval = pEdgeDisp2->GetWidth(); pLgDisp = pEdgeDisp1; ndxLgEdge = pEdgeData->ndxEdges[0]; lgInterval = pEdgeDisp1->GetWidth(); } else { pSmDisp = pEdgeDisp1; ndxSmEdge = pEdgeData->ndxEdges[0]; smInterval = pEdgeDisp1->GetWidth(); pLgDisp = pEdgeDisp2; ndxLgEdge = pEdgeData->ndxEdges[1]; lgInterval = pEdgeDisp2->GetWidth(); } // calculate the ratio int ratio = ( lgInterval - 1 ) / ( smInterval - 1 ); // // average "like" points // for( int ndxSm = 1, ndxLg = ratio; ndxSm < ( smInterval - 1 ); ndxSm++, ndxLg += ratio ) { int ndxSmPt = GetEdgePointIndex( pSmDisp, ndxSmEdge, ndxSm, true ); int ndxLgPt = GetEdgePointIndex( pLgDisp, ndxLgEdge, ndxLg, false ); // average AverageVectorFieldData( pSmDisp, ndxSmPt, pLgDisp, ndxLgPt ); } // // linearly interpolate the "unlike" points // float blendRatio = 1.0f / ratio; for( int ndxLg = 0; ndxLg < ( lgInterval - 1 ); ndxLg += ratio ) { int ndxStart = ndxLg; int ndxEnd = ( ndxLg + ratio ); int ndxStartPt = GetEdgePointIndex( pLgDisp, ndxLgEdge, ndxStart, true ); int ndxEndPt = GetEdgePointIndex( pLgDisp, ndxLgEdge, ndxEnd, true ); for( int ndx = ( ndxStart + 1 ); ndx < ndxEnd; ndx++ ) { float blend = blendRatio * ( ndx - ndxStart ); int ndxDst = GetEdgePointIndex( pLgDisp, ndxLgEdge, ndx, true ); BlendVectorFieldData( pLgDisp, ndxStartPt, ndxDst, pLgDisp, ndxEndPt, ndxDst, blend ); } } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void SewEdge_ResolveSolidNormal( SewEdgeData_t *pEdgeData ) { // create an empty vector to reset the offset with Vector vClear( 0.0f, 0.0f, 0.0f ); for( int i = 0; i < pEdgeData->faceCount; i++ ) { // get the displacement surface associated with the face EditDispHandle_t handle = pEdgeData->pFaces[i]->GetDisp(); if( handle == EDITDISPHANDLE_INVALID ) continue; CMapDisp *pDisp = EditDispMgr()->GetDisp( handle ); // get surface normal, to reset vector field to base state Vector vNormal; pDisp->GetSurfNormal( vNormal ); // // for all points along the edge -- reset // int width = pDisp->GetWidth(); for( int j = 0; j < width; j++ ) { // get the edge point index int ndxPt = GetEdgePointIndex( pDisp, pEdgeData->ndxEdges[i], j, true ); if( ndxPt == -1 ) continue; // // reset displacement data (dist, field vector, and offset vector) // pDisp->SetFieldDistance( ndxPt, 0.0f ); pDisp->SetFieldVector( ndxPt, vClear ); pDisp->SetSubdivPosition( ndxPt, vClear ); pDisp->SetSubdivNormal( ndxPt, vNormal ); pDisp->SetAlpha( ndxPt, 0.0f ); } } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void SewEdge_Resolve( void ) { // get the number of edges in the edge list int edgeCount = s_EdgeData.Size(); // resolve each edge for( int i = 0; i < edgeCount; i++ ) { // get the current edge data struct SewEdgeData_t *pEdgeData = s_EdgeData.Element( i ); if( !pEdgeData ) continue; // handle "normal" edge if( pEdgeData->type[0] == DISPSEW_EDGE_NORMAL ) { // solid "normal" edge if( SewEdge_IsSolidNormal( pEdgeData ) ) { SewEdge_ResolveSolidNormal( pEdgeData ); } // disps "normal" edge else { SewEdge_ResolveDispNormal( pEdgeData ); } } // handle "t-junction" edge else { int ndxTJ = SewEdge_TJIndex( pEdgeData, DISPSEW_EDGE_TJ ); int ndxTJStart = SewEdge_TJIndex( pEdgeData, DISPSEW_EDGE_TJSTART ); int ndxTJEnd = SewEdge_TJIndex( pEdgeData, DISPSEW_EDGE_TJEND ); if( SewEdge_IsSolidTJunc( pEdgeData, DISPSEW_EDGE_TJ ) ) { // reset both start and end t-junction edges if they exist if( ndxTJStart != -1 ) { SewEdge_ResolveSolidTJunc( pEdgeData, DISPSEW_EDGE_TJSTART, false ); } if( ndxTJEnd != -1 ) { SewEdge_ResolveSolidTJunc( pEdgeData, DISPSEW_EDGE_TJEND, false ); } continue; } // handle start edge if( ndxTJStart != -1 ) { if( SewEdge_IsSolidTJunc( pEdgeData, DISPSEW_EDGE_TJSTART ) ) { SewEdge_ResolveSolidTJunc( pEdgeData, DISPSEW_EDGE_TJ, true ); } else { SewEdge_ResolveDispTJunc( pEdgeData, ndxTJ, ndxTJStart, true ); } } // handle end edge if( ndxTJEnd != -1 ) { if( SewEdge_IsSolidTJunc( pEdgeData, DISPSEW_EDGE_TJEND ) ) { SewEdge_ResolveSolidTJunc( pEdgeData, DISPSEW_EDGE_TJ, false ); } else { SewEdge_ResolveDispTJunc( pEdgeData, ndxTJ, ndxTJEnd, false ); } } } } } //----------------------------------------------------------------------------- // Purpose: Convert the edge/midpoint/corner data for shared code, //----------------------------------------------------------------------------- bool PrePlanarizeDependentVerts( void ) { // Create a list of all the selected displacement cores. CFaceEditSheet *pSheet = GetMainWnd()->GetFaceEditSheet(); if( !pSheet ) return false; int nFaceCount = pSheet->GetFaceListCount(); for( int iFace = 0; iFace < nFaceCount; ++iFace ) { CMapFace *pFace = pSheet->GetFaceListDataFace( iFace ); if( !pFace || !pFace->HasDisp() ) continue; CMapDisp *pDisp = EditDispMgr()->GetDisp( pFace->GetDisp() ); Assert( pDisp ); int iDisp = m_aCoreDispInfos.AddToTail(); pDisp->GetCoreDispInfo()->SetListIndex( iDisp ); m_aCoreDispInfos[iDisp] = pDisp->GetCoreDispInfo(); } // Add the list to the displacements -- this is a bit hacky!! for ( int iDisp = 0; iDisp < m_aCoreDispInfos.Count(); ++iDisp ) { m_aCoreDispInfos[iDisp]->SetDispUtilsHelperInfo( m_aCoreDispInfos.Base(), m_aCoreDispInfos.Count() ); } // Build neighboring info. FindNeighboringDispSurfs( m_aCoreDispInfos.Base(), m_aCoreDispInfos.Count() ); return true; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- class CHammerTesselateHelper : public CBaseTesselateHelper { public: void EndTriangle() { m_pIndices->AddToTail( m_TempIndices[0] ); m_pIndices->AddToTail( m_TempIndices[1] ); m_pIndices->AddToTail( m_TempIndices[2] ); } DispNodeInfo_t& GetNodeInfo( int iNodeBit ) { // Hammer doesn't care about these. Give it back something to play with. static DispNodeInfo_t dummy; return dummy; } public: CUtlVector *m_pIndices; }; //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- bool FindEnclosingTri( const Vector2D &vert, CUtlVector &vertCoords, CUtlVector &indices, int *pStartVert, float bcCoords[3] ) { for ( int i = 0; i < indices.Count(); i += 3 ) { GetBarycentricCoords2D( vertCoords[indices[i+0]], vertCoords[indices[i+1]], vertCoords[indices[i+2]], vert, bcCoords ); if ( bcCoords[0] >= 0 && bcCoords[0] <= 1 && bcCoords[1] >= 0 && bcCoords[1] <= 1 && bcCoords[2] >= 0 && bcCoords[2] <= 1 ) { *pStartVert = i; return true; } } return false; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void SnapDependentVertsToSurface( CCoreDispInfo *pCoreDisp ) { // Don't really want to do this, but....... CUtlVector indices; CHammerTesselateHelper helper; helper.m_pIndices = &indices; helper.m_pActiveVerts = pCoreDisp->GetAllowedVerts().Base(); helper.m_pPowerInfo = pCoreDisp->GetPowerInfo(); TesselateDisplacement( &helper ); // Find allowed/non-allowed verts. CUtlVector vertsTouched; vertsTouched.SetSize( pCoreDisp->GetSize() ); memset( vertsTouched.Base(), 0, sizeof( bool ) * vertsTouched.Count() ); for ( int iVert = 0; iVert < indices.Count(); ++iVert ) { vertsTouched[indices[iVert]] = true; } // Generate 2D floating point coordinates for each vertex. We use these to generate // barycentric coordinates, and the scale doesn't matter. CUtlVector vertCoords; vertCoords.SetSize( pCoreDisp->GetSize() ); for ( int iHgt = 0; iHgt < pCoreDisp->GetHeight(); ++iHgt ) { for ( int iWid = 0; iWid < pCoreDisp->GetWidth(); ++iWid ) { vertCoords[iHgt*pCoreDisp->GetWidth()+iWid].Init( iWid, iHgt ); } } // Now, for each vert not touched, snap its position to the main surface. for ( int iHgt = 0; iHgt < pCoreDisp->GetHeight(); ++iHgt ) { for ( int iWid = 0; iWid < pCoreDisp->GetWidth(); ++iWid ) { int nIndex = iHgt * pCoreDisp->GetWidth() + iWid; if ( !( vertsTouched[nIndex] ) ) { float flBCoords[3]; int iStartVert = -1; if ( FindEnclosingTri( vertCoords[nIndex], vertCoords, indices, &iStartVert, flBCoords ) ) { const Vector &A = pCoreDisp->GetVert( indices[iStartVert+0] ); const Vector &B = pCoreDisp->GetVert( indices[iStartVert+1] ); const Vector &C = pCoreDisp->GetVert( indices[iStartVert+2] ); Vector vNewPos = A*flBCoords[0] + B*flBCoords[1] + C*flBCoords[2]; // Modify the CCoreDispInfo vert (although it probably won't be used later). pCoreDisp->Position_Update( nIndex, vNewPos ); } else { // This shouldn't happen because it would mean that the triangulation that // disp_tesselation.h produced was missing a chunk of the space that the // displacement covers. // It also could indicate a floating-point epsilon error.. check to see if // FindEnclosingTri finds a triangle that -almost- encloses the vert. Assert( false ); } } } } } //----------------------------------------------------------------------------- // Purpose: Get allowed verts bits and planarize cleared verts and purge disp // infos. //----------------------------------------------------------------------------- void PostPlanarizeDependentVerts( void ) { // Snap dependents verts to the displacement surface. for ( int iDispCore = 0; iDispCore < m_aCoreDispInfos.Count(); ++iDispCore ) { SnapDependentVertsToSurface( m_aCoreDispInfos[iDispCore] ); } // Clear out the displacement info list. m_aCoreDispInfos.Purge(); } //----------------------------------------------------------------------------- // Purpose: Planarize vertices that are removed because of dependencies with // neighboring displacements. //----------------------------------------------------------------------------- void PlanarizeDependentVerts( void ) { // Setup. if ( !PrePlanarizeDependentVerts() ) return; SetupAllowedVerts( m_aCoreDispInfos.Base(), m_aCoreDispInfos.Count() ); // Update and clean-up. PostPlanarizeDependentVerts(); }