Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

936 lines
29 KiB

//========= Copyright � 1996-2008, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "optimize_subd.h"
#define PI 3.14159265
#define VTXIDX(vID) m_vtxList[vID].origMeshVertID
#define VTXPOS(vID) *m_vtxData->Position( m_vtxList[vID].origMeshVertID )
#define VTXNOR(vID) *m_vtxData->Normal( m_vtxList[vID].origMeshVertID )
static Vector project_and_normalize( Vector v, Vector n )
{
v = v - DotProduct(v, n)*n;
VectorNormalize(v);
return v;
}
static int MOD4[8] = {0,1,2,3,0,1,2,3};
namespace OptimizedModel
{
class NeighborCornerBitfield
{
public:
unsigned short *bitfield;
NeighborCornerBitfield(unsigned short *field): index(0), bitfield(field) { *bitfield = 0; }
void pushBit( bool bit )
{
*bitfield |= bit<<index; index++;
}
void popBit()
{
index--; *bitfield &= ~(1<<index);
}
bool getBitAt( unsigned short i )
{
return ((*bitfield & (1<<i))>>i) == 1;
}
void insertBitAt( unsigned short i, bool bit )
{
unsigned short preMask = (1<<i)-1;
*bitfield = (*bitfield & preMask) + ((*bitfield & (~preMask))<<1) + (bit<<i);
index++;
}
void removeBitAt( unsigned short i )
{
unsigned short preMask = (1<<i)-1;
unsigned short postMask = ~((1<<(i+1))-1);
*bitfield = ((*bitfield & postMask)>>1) + (*bitfield & preMask);
index--;
}
void clearBits()
{
*bitfield = 0;
}
private:
unsigned short index;
};
COptimizeSubDBuilder::COptimizeSubDBuilder(SubD_FaceList_t& subDFaceList, const SubD_VertexList_t& vertexList, const SubD_VertexData_t &vertexData, bool bIsTagged, bool bMendVertices)
: m_faceList(subDFaceList), m_vtxList(vertexList), m_vtxData(vertexData)
{
m_numPatches = (int) subDFaceList.Count();
ProcessPatches(bIsTagged,bMendVertices);
}
void dumpPatch(SubD_Face_t *patch)
{
Msg( "Patch: %d\n", patch->patchID );
Msg( " vtxIDs: %d %d %d %d\n", patch->vtxIDs[0], patch->vtxIDs[1], patch->vtxIDs[2], patch->vtxIDs[3] );
Msg( " valences: %d %d %d %d\n", patch->valences[0], patch->valences[1], patch->valences[2], patch->valences[3] );
Msg( " vtx1RingSize: %d %d %d %d\n", patch->vtx1RingSize[0], patch->vtx1RingSize[1], patch->vtx1RingSize[2], patch->vtx1RingSize[3] );
Msg( " vtx1RingCenterQuadOffset: %d %d %d %d\n", patch->vtx1RingCenterQuadOffset[0], patch->vtx1RingCenterQuadOffset[1], patch->vtx1RingCenterQuadOffset[2], patch->vtx1RingCenterQuadOffset[3] );
Msg( " bndVtx: %d %d %d %d\n", patch->bndVtx[0], patch->bndVtx[1], patch->bndVtx[2], patch->bndVtx[3] );
Msg( " cornerVtx: %d %d %d %d\n", patch->cornerVtx[0], patch->cornerVtx[1], patch->cornerVtx[2], patch->cornerVtx[3] );
Msg( " BndEdge: %d %d %d %d\n", patch->bndEdge[0], patch->bndEdge[1], patch->bndEdge[2], patch->bndEdge[3] );
Msg( " halfEdges.twin: %d/%d %d/%d %d/%d %d/%d\n",
patch->halfEdges[0].twin ? patch->halfEdges[0].twin->patch->patchID : -1, patch->halfEdges[0].twin ? patch->halfEdges[0].twin->localID: -1,
patch->halfEdges[1].twin ? patch->halfEdges[1].twin->patch->patchID : -1, patch->halfEdges[1].twin ? patch->halfEdges[1].twin->localID: -1,
patch->halfEdges[2].twin ? patch->halfEdges[2].twin->patch->patchID : -1, patch->halfEdges[2].twin ? patch->halfEdges[2].twin->localID: -1,
patch->halfEdges[3].twin ? patch->halfEdges[3].twin->patch->patchID : -1, patch->halfEdges[3].twin ? patch->halfEdges[3].twin->localID: -1 );
Msg( " halfEdges.sectorStart: %d/%d %d/%d %d/%d %d/%d\n",
patch->halfEdges[0].sectorStart ? patch->halfEdges[0].sectorStart->patch->patchID : -1, patch->halfEdges[0].sectorStart ? patch->halfEdges[0].sectorStart->localID: -1,
patch->halfEdges[1].sectorStart ? patch->halfEdges[1].sectorStart->patch->patchID : -1, patch->halfEdges[1].sectorStart ? patch->halfEdges[1].sectorStart->localID: -1,
patch->halfEdges[2].sectorStart ? patch->halfEdges[2].sectorStart->patch->patchID : -1, patch->halfEdges[2].sectorStart ? patch->halfEdges[2].sectorStart->localID: -1,
patch->halfEdges[3].sectorStart ? patch->halfEdges[3].sectorStart->patch->patchID : -1, patch->halfEdges[3].sectorStart ? patch->halfEdges[3].sectorStart->localID: -1 );
Msg( " nbCornerVtx: %x %x %x %x\n", patch->nbCornerVtx[0], patch->nbCornerVtx[1], patch->nbCornerVtx[2], patch->nbCornerVtx[3] );
Msg( " loopGapAngle: %d %d %d %d\n", patch->loopGapAngle[0], patch->loopGapAngle[1], patch->loopGapAngle[2], patch->loopGapAngle[3] );
}
void dumpPatches( SubD_FaceList_t &quads )
{
size_t nQuads = quads.Count();
for (size_t k=0; k<nQuads; k++)
{
dumpPatch(&quads[k]);
}
}
void COptimizeSubDBuilder::ProcessPatches( bool bIsTagged, bool bMendVertices )
{
// Init attributes
for ( int i=0; i<m_numPatches; ++i )
{
SubD_Face_t *pPatch = &m_faceList[i];
for (int k=0; k<4; ++k)
{
pPatch->patchID = i;
if ( !bIsTagged )
{
pPatch->bndVtx[k] = false;
pPatch->bndEdge[k] = false;
pPatch->cornerVtx[k] = false;
}
pPatch->nbCornerVtx[k] = 0;
pPatch->valences[k] = 0;
pPatch->minOneRingIndex[k] = 0;
pPatch->loopGapAngle[k] = 65535;
pPatch->edgeBias[2*k] = 16384;
pPatch->edgeBias[2*k+1] = 16384;
pPatch->halfEdges[k].twin = NULL;
pPatch->halfEdges[k].sectorStart = &pPatch->halfEdges[k]; // start one-ring with this halfedge
pPatch->halfEdges[k].localID = k;
pPatch->halfEdges[k].patch = pPatch;
}
}
RemapIndices();
BuildNeighborhoodInfo();
CheckForManifoldMesh();
ConsistentPatchOrientation();
if ( !bIsTagged )
{
TagCreases();
}
// dumpPatches(m_QuadArray);
// first pass --------------------------------------------------------------------
for ( int i=0; i<m_numPatches; i++ )
{
SubD_Face_t *pPatch = &m_faceList[i];
for ( int k=0; k<4; k++ )
{
ComputeSectorStart( pPatch, k );
ComputePerVertexInfo( pPatch, k );
ComputeSectorOneRing( pPatch, k );
ComputeSectorAngle( pPatch, k );
}
}
// dumpPatches(m_faceList);
// second pass requires all per-vertex-per-face variables to be computed ----------
for ( int i=0; i<m_numPatches; i++ )
{
SubD_Face_t *pPatch = &m_faceList[i];
for ( int k=0; k<4; k++ )
{
ComputeNbCorners( pPatch, k );
}
}
// third pass computes neighboring texcoords for watertight displacement mapping ----------
for ( int i=0; i<m_numPatches; i++ )
{
SubD_Face_t *pPatch = &m_faceList[i];
ComputeNeighborTexcoords( pPatch );
}
// Compute offsets into one-rings, necessary for subsequent evaluation consistency
SetMinOneRingIndices();
// Sort patches by regular vs extraordinary
SubD_FaceList_t regFaceList;
SubD_FaceList_t extraFaceList;
for ( int i=0; i<m_numPatches; i++ )
{
SubD_Face_t *pPatch = &m_faceList[i];
if ( FaceIsRegular( pPatch ) )
{
regFaceList.AddToTail( *pPatch );
}
else
{
extraFaceList.AddToTail( *pPatch );
}
}
// recombine
int nRegFaces = regFaceList.Count();
for ( int i=0; i<nRegFaces; i++ )
{
m_faceList[i] = regFaceList[i];
}
int nExtraFaces = extraFaceList.Count();
for ( int i=0; i<nExtraFaces; i++ )
{
m_faceList[i+nRegFaces] = extraFaceList[i];
}
// mend vertices at the end ----------
/*if ( bMendVertices )
{
for ( int i=0; i<m_numPatches; i++ )
{
SubD_Face_t *pPatch = &m_faceList[i];
for (int k=0; k<4; k++)
{
mendVertices( pPatch, k );
}
}
}*/
}
HalfEdge *COptimizeSubDBuilder::FindTwin( HalfEdge &he )
{
Vector p0 = VTXPOS( he.patch->vtxIDs[ MOD4[he.localID+0] ] );
Vector p1 = VTXPOS( he.patch->vtxIDs[ MOD4[he.localID+1] ] ); // twin face will have edge p1->p0
for (int i=0; i<m_numPatches; i++)
{
SubD_Face_t *patch = &m_faceList[i];
for ( unsigned short k=0; k<4; k++ )
{
if ( ( VTXPOS( patch->vtxIDs[ MOD4[k + 0] ] ) == p1 ) &&
( VTXPOS( patch->vtxIDs[ MOD4[k + 1] ] ) == p0 ) )
{
return &patch->halfEdges[k];
}
}
}
return NULL;
}
// Set the minimum one-ring index for each of the four vertices of a patch.
// This value is used during the mapping from vertices to Bezier control
// points in order to ensure consistent evaluation order and avoid cracks
void COptimizeSubDBuilder::SetMinOneRingIndices()
{
for ( int i=0; i<m_numPatches; i++ ) // Walk patches
{
SubD_Face_t* pPatch = &m_faceList[i];
int nFirstNeighbor = 0; // First neighbor in a given vertex's one-ring
for ( int k=0; k<4; k++ ) // For each vertex of the patch
{
int nMinNeighborIdx = m_IndexRemapTable[pPatch->oneRing[nFirstNeighbor]]; // Remapped Index
int nMinNeighborOffset = 0; // Neighbor zero is the current min
int nLastNeighbor = nFirstNeighbor + pPatch->vtx1RingSize[k] - 1; // Last neighbor
for ( int j=nFirstNeighbor; j<=nLastNeighbor; j++ ) // First neighbor to the last neighbor, inclusive
{
int nNeighborIdx = m_IndexRemapTable[pPatch->oneRing[j]]; // Use only remapped indices
if ( nNeighborIdx < nMinNeighborIdx ) // If we have a smaller remapped index
{
nMinNeighborIdx = nNeighborIdx; // Set as new min index
nMinNeighborOffset = j - nFirstNeighbor; // Offset into THIS vertex's one-ring
}
}
pPatch->minOneRingIndex[k] = nMinNeighborOffset; // Set the offset into THIS vertex's one-ring
nFirstNeighbor = nLastNeighbor + 1; // Go to next range of indices in the one-ring array
}
}
}
// Positions appear redundantly in vertex data, so we need a mapping so that SetMinOneRingIndices() can do the right thing
void COptimizeSubDBuilder::RemapIndices()
{
for ( int i=0; i<m_vtxList.Count(); i++ )
{
m_IndexRemapTable.AddToTail(i); // Set identity mapping
}
for ( int i=0; i<m_vtxList.Count(); i++ ) // Walk indices again
{
for ( int j=i+1; j<m_vtxList.Count(); j++ ) // Look at later indices
{
Vector vPosi = VTXPOS( i );
Vector vPosj = VTXPOS( j );
if ( vPosi == vPosj ) // If the positions are equivalent, set index remapping
{
m_IndexRemapTable[j] = MIN( i, j );
m_IndexRemapTable[i] = MIN( i, j );
}
}
}
/*
for ( int i=0; i<m_vtxList.Count(); i++ )
{
if ( i != m_IndexRemapTable[i] )
{
Msg( "(%d, %d) ***\n", i, m_IndexRemapTable[i] );
}
else
{
Msg( "(%d, %d)\n", i, m_IndexRemapTable[i] );
}
}
*/
}
void COptimizeSubDBuilder::BuildNeighborhoodInfo( )
{
for ( int i=0; i<m_numPatches; i++ )
{
SubD_Face_t* pPatch = &m_faceList[i];
for ( int k=0; k<4; k++ )
{
if ( !pPatch->halfEdges[k].twin )
{
HalfEdge *pTwin = FindTwin(pPatch->halfEdges[k]);
pPatch->halfEdges[k].twin = pTwin; // record twin
if ( pTwin )
{
pPatch->halfEdges[k].twin->twin = &pPatch->halfEdges[k]; // record twin's twin
}
else
{
pPatch->bndEdge[k] = true;
pPatch->bndVtx[MOD4[k+0]] = true;
pPatch->bndVtx[MOD4[k+1]] = true;
}
}
}
}
}
void COptimizeSubDBuilder::CheckForManifoldMesh( )
{
for ( int i=0; i<m_numPatches; i++ )
{
SubD_Face_t* pPatch = &m_faceList[i];
for (unsigned short k=0; k<4; ++k)
{
if (( pPatch->halfEdges[k].twin != NULL ) && (pPatch->halfEdges[k].twin->twin != &pPatch->halfEdges[k]) )
{
Msg( "Topology error at vertices %d, %d, %d\n", pPatch->vtxIDs[MOD4[k+3]], pPatch->vtxIDs[MOD4[k+0]], pPatch->vtxIDs[MOD4[k+1]] );
Vector vA = VTXPOS( pPatch->vtxIDs[MOD4[k+3]] );
Vector vB = VTXPOS( pPatch->vtxIDs[MOD4[k+0]] );
Vector vC = VTXPOS( pPatch->vtxIDs[MOD4[k+1]] );
Msg( "spaceLocator -p %.4f %.4f %.4f;\n", vA.x, vA.y, vA.z );
Msg( "spaceLocator -p %.4f %.4f %.4f;\n", vB.x, vB.y, vB.z );
Msg( "spaceLocator -p %.4f %.4f %.4f;\n", vC.x, vC.y, vC.z );
}
}
}
}
void COptimizeSubDBuilder::ComputeSectorStart(SubD_Face_t *pPatch, unsigned short k)
{
HalfEdge *sectorStart, *next = &pPatch->halfEdges[k];
do
{
sectorStart = next;
if ( next->BndEdge() )
{
next = NULL;
}
else
{
next = next->PrevByTail();
}
}
while ( ( next != NULL ) && ( next != &(pPatch->halfEdges[k]) ) );
if ( next == NULL )
{
pPatch->halfEdges[k].sectorStart = sectorStart; // only update sectorStart if we actually hit a boundary
}
}
// Propagates bndVtx to faces that do not have a BndEdge to this vertex, sets cornerVtx,
// Requires sectorStart, corrects sectorStart and bndVtx for dangling crease edges.
void COptimizeSubDBuilder::ComputePerVertexInfo(SubD_Face_t *baseQuad, unsigned short baseLocalID)
{
unsigned short nBndEdges = 0;
HalfEdge *sectorStart = baseQuad->halfEdges[ MOD4[baseLocalID] ].sectorStart, *he = sectorStart;
// Find first sector
HalfEdge *next = he->PrevByTail();
while ( ( next!=NULL ) && ( next!=sectorStart ) )
{
he = next;
next = next->PrevByTail();
}
if ( next != NULL )
{
he = sectorStart;
}
if ( he->BndEdge() )
{
nBndEdges++;
}
HalfEdge *heEnd = he->twin;
he = he->PrevInFace();
do
{
if ( he->BndEdge() )
{
nBndEdges++;
}
he = he->NextByHead();
} while (( he != NULL ) && (he != heEnd));
// Set flags
if ( nBndEdges == 1 ) // dangling BndEdge -> correct sectorStart
{
baseQuad->halfEdges[ baseLocalID ].sectorStart = &baseQuad->halfEdges[ baseLocalID ];
baseQuad->bndVtx[baseLocalID] = false;
}
else if ( nBndEdges >= 2 )
{
baseQuad->bndVtx[baseLocalID] = true;
if ( nBndEdges > 2 )
{
baseQuad->cornerVtx[baseLocalID] = true; // more than 2 BndEdges -> cornerVtx
}
}
}
//
// Writes oneRing, vtx1RingSize, vtx1RingCenterQuadOffset, valence
//
void COptimizeSubDBuilder::ComputeSectorOneRing( SubD_Face_t *baseQuad, unsigned short baseLocalID )
{
unsigned short *oneRing = baseQuad->oneRing;
for ( unsigned short k=0; k < baseLocalID; k++ )
{
oneRing += baseQuad->vtx1RingSize[k];
}
unsigned short &centerOffset = baseQuad->vtx1RingCenterQuadOffset[baseLocalID] = 1;
unsigned short &valence = baseQuad->valences[baseLocalID] = 0;
unsigned short &oneRingSize = baseQuad->vtx1RingSize[baseLocalID] = 0;
HalfEdge *heBase = &baseQuad->halfEdges[ MOD4[baseLocalID] ];
HalfEdge *he = heBase->sectorStart;
oneRing[oneRingSize++] = he->patch->vtxIDs[ MOD4[he->localID+0] ];
valence++;
oneRing[oneRingSize++] = he->patch->vtxIDs[ MOD4[he->localID+1] ];
HalfEdge *heEnd = he->twin;
he = he->PrevInFace();
do
{
oneRing[oneRingSize++] = he->patch->vtxIDs[ MOD4[he->localID+3] ];
valence++;
oneRing[oneRingSize++] = he->patch->vtxIDs[ MOD4[he->localID+0] ];
if ( he->twin == heBase )
{
centerOffset = oneRingSize - 1;
}
he = (he->BndEdge() && baseQuad->bndVtx[baseLocalID]) ? NULL : he->NextByHead(); // make sure we only step over BndEdge if it is dangling.
} while ( ( he != NULL ) && ( he != heEnd ) );
if ( ( he != NULL) && ( he == heEnd ) ) // if we closed the loop, add off-edge vertex from last quad.
{
oneRing[oneRingSize++] = he->patch->vtxIDs[ MOD4[ he->localID+3 ]];
}
}
// Depends on bndVtx, cornerVtx, valence
void COptimizeSubDBuilder::ComputeSectorAngle( SubD_Face_t *baseQuad, unsigned short baseLocalID )
{
if ( !baseQuad->bndVtx[baseLocalID] ) // If no boundary vertex, nothing needs to be done (includes dangling crease)
return;
if ( !baseQuad->cornerVtx[baseLocalID] ) // If no corner, set loopGapAngle = PI (or PI/2 for valence==2)
{
baseQuad->loopGapAngle[baseLocalID] = 65535 / ( baseQuad->valences[baseLocalID] == 2 ? 4 : 2 );
return;
}
HalfEdge *he = baseQuad->halfEdges[ MOD4[baseLocalID] ].sectorStart;
Vector center_pos = VTXPOS( he->patch->vtxIDs[ he->localID ] );
Vector center_nor = VTXNOR( he->patch->vtxIDs[ he->localID ] );
VectorNormalize(center_nor);
int debugVtxID = he->patch->vtxIDs[ MOD4[ he->localID+1 ] ];
Vector eVec1 = VTXPOS( he->patch->vtxIDs[ MOD4[ he->localID+1 ] ] ) - center_pos, eVec2;
Vector npVec1 = project_and_normalize( eVec1, center_nor ), npVec2;
float sector_angle = 0;
he = he->PrevInFace();
do
{
debugVtxID = he->patch->vtxIDs[ MOD4[ he->localID ] ];
eVec2 = VTXPOS( he->patch->vtxIDs[ MOD4[ he->localID ] ] ) - center_pos;
npVec2 = project_and_normalize( eVec2, center_nor );
sector_angle += acosf( DotProduct( npVec1, npVec2 ) );
he = he->BndEdge() ? NULL : he->NextByHead(); // make sure we only step over BndEdge if it is dangling.
npVec1 = npVec2;
} while ( he != NULL ); // only way to terminate is to hit BndEdge
VectorNormalize( eVec1 );
VectorNormalize( eVec2 );
float loopGapAngleF = acosf( DotProduct(eVec1, eVec2) ); // measure overall gap
baseQuad->loopGapAngle[baseLocalID] = (unsigned int) ( ( 65535.0 * loopGapAngleF ) / ( 2 * PI ) );
}
void COptimizeSubDBuilder::MendVertices(SubD_Face_t *baseQuad, unsigned short baseLocalID)
{
HalfEdge *he = baseQuad->halfEdges[ baseLocalID ].sectorStart;
unsigned short vtxID = baseQuad->vtxIDs[ baseLocalID ];
Vector p = VTXPOS( vtxID );
Vector n = VTXNOR( vtxID );
HalfEdge *heEnd = he->twin;
he = he->PrevInFace();
do
{
if (( VTXPOS( he->patch->vtxIDs[ MOD4[he->localID+1] ]) == p ) &&
( VTXNOR( he->patch->vtxIDs[ MOD4[he->localID+1] ]) == n ))
{
he->patch->vtxIDs[ MOD4[he->localID+1] ] = vtxID;
}
if ( (he->twin) &&
( VTXPOS( he->twin->patch->vtxIDs[ MOD4[he->twin->localID] ] ) == p) &&
( VTXNOR( he->twin->patch->vtxIDs[ MOD4[he->twin->localID] ] ) == n) )
{
he->twin->patch->vtxIDs[ MOD4[he->twin->localID] ] = vtxID;
}
he = he->NextByHead();
} while (( he != NULL ) && (he != heEnd));
}
// Computes a bitfield with bits set if the corresponding neighbor-vertex is a concave corner
// this has to go in a second pass as all per-face-per-vertex flags from the first pass to be computed beforehand
void COptimizeSubDBuilder::ComputeNbCorners( SubD_Face_t *baseQuad, unsigned short baseLocalID )
{
NeighborCornerBitfield nbCorners( &baseQuad->nbCornerVtx[baseLocalID] );
HalfEdge *he = baseQuad->halfEdges[ MOD4[baseLocalID] ].sectorStart;
nbCorners.pushBit( he->patch->cornerVtx[ MOD4[he->localID+1] ] == 2 );
HalfEdge *heEnd = he->twin;
he = he->PrevInFace();
do
{
nbCorners.pushBit( he->patch->cornerVtx[ he->localID ] == 2 );
he = ( he->BndEdge() && baseQuad->bndVtx[baseLocalID] ) ? NULL : he->NextByHead(); // make sure we only step over BndEdge if it is dangling.
} while (( he != NULL ) && (he != heEnd));
}
unsigned short COptimizeSubDBuilder::FindNeighborVertex( HalfEdge** ppOutMirrorEdge, const HalfEdge *pHalfEdge, int indexAlongEdge )
// Finds neighboring vertex along the mirror edge of pHalfEdge.
// Returns the index of the vertex.
// pOutMirrorEdge is the mirror edge we took this vertex from.
// pHalfEdge is the shared edge who's mirror we want to find.
// indexAlongEdge is the index of the vertex along the edge. ( 0 or 1 only )
{
HalfEdge* pMirrorEdge = pHalfEdge->twin;
unsigned short vertexID = (unsigned short)-1;
if ( pMirrorEdge )
{
vertexID = pMirrorEdge->patch->vtxIDs[ ( pMirrorEdge->localID + indexAlongEdge ) % 4 ] ;
}
if ( ppOutMirrorEdge )
{
*ppOutMirrorEdge = pMirrorEdge;
}
return vertexID;
}
// Computes the neighboring texcoords ( Interior, EdgeV, EdgeU, Corner ) for each vertex
// texcoords are computed in such a way that every shared edge or corner computes the same values
// this is used as a tie-breaking scheme for creating consistent texture sampling for displacement maps
void COptimizeSubDBuilder::ComputeNeighborTexcoords( SubD_Face_t *baseQuad )
{
unsigned short p = baseQuad->patchID;
unsigned short invalidNeighborValue = (unsigned short)-1;
// Loop over all 4 verts of the quad
for ( int i=0; i<4; ++i )
{
unsigned short index = baseQuad->vtxIDs[i];
// Interior point is alway the current corner
baseQuad->vUV0[i] = VTXIDX( index );
// Assert( index == baseQuad->vUV0[i] );
// Default to original texcoord values for 1 and 2
baseQuad->vUV1[i] = baseQuad->vUV0[i];
baseQuad->vUV2[i] = baseQuad->vUV0[i];
// Find the texture coordinates of our neighbors
// Only keep the texture coordinates of the neighbor with the greatest quad index
HalfEdge* pMirrorEdgeV = NULL;
// V edge ( store the UVs of the patch with the greatest ID )
unsigned short iNeighborPatchV = invalidNeighborValue;
unsigned short iNeighborV = FindNeighborVertex( &pMirrorEdgeV, &baseQuad->halfEdges[ i ], 1 );
if ( iNeighborV != invalidNeighborValue ) // hard edge test
{
iNeighborPatchV = pMirrorEdgeV->patch->patchID;
if ( iNeighborPatchV > p )
{
baseQuad->vUV1[i] = VTXIDX( iNeighborV );
}
}
HalfEdge* pMirrorEdgeU = NULL;
// U edge ( store the UVs of the patch with the greatest ID )
unsigned short iNeighborPatchU = invalidNeighborValue;
unsigned short iNeighborU = FindNeighborVertex( &pMirrorEdgeU, &baseQuad->halfEdges[ (i+3)%4 ], 0 );
if ( iNeighborU != invalidNeighborValue ) // hard edge test
{
iNeighborPatchU = pMirrorEdgeU->patch->patchID;
if ( iNeighborPatchU > p )
{
baseQuad->vUV2[i] = VTXIDX( iNeighborU );
}
}
// Corner ( store the UVs of the patch with the greatest ID ).
// Walk from NeighborV to NeighborU and store data for the largest patch ID.
// We may redundantly check NeighborPatchU here if this is a valence 3 vertex.
HalfEdge* pMirrorEdgeCorner = pMirrorEdgeV;
unsigned short iNeighborPatch = invalidNeighborValue;
unsigned short iMaxNeighborCorner = index;
unsigned short iMaxPatch = baseQuad->patchID;
if ( pMirrorEdgeCorner )
{
do
{
HalfEdge* pNextEdge = pMirrorEdgeCorner->NextInFace();
unsigned short iNeighborCorner = FindNeighborVertex( &pMirrorEdgeCorner, pNextEdge, 1 );
if ( iNeighborCorner != invalidNeighborValue ) // hard edge test
{
iNeighborPatch = pMirrorEdgeCorner->patch->patchID;
if ( pMirrorEdgeCorner->patch->patchID > iMaxPatch )
{
iMaxPatch = pMirrorEdgeCorner->patch->patchID;
iMaxNeighborCorner = iNeighborCorner;
}
}
} while( iNeighborPatch != iNeighborPatchU && pMirrorEdgeCorner );
}
// Determine whether We still need to check against U and V adjacent patches
if ( pMirrorEdgeU && ( pMirrorEdgeU->patch->patchID > iMaxPatch ) )
{
iMaxPatch = pMirrorEdgeU->patch->patchID;
iMaxNeighborCorner = iNeighborU;
}
if ( pMirrorEdgeV && ( pMirrorEdgeV->patch->patchID > iMaxPatch ) )
{
iMaxPatch = pMirrorEdgeV->patch->patchID;
iMaxNeighborCorner = iNeighborV;
}
baseQuad->vUV3[i] = VTXIDX( iMaxNeighborCorner );
}
}
void DumpPatchLite( SubD_Face_t *patch )
{
Msg( "Patch: %d\n", patch->patchID );
Msg( " vtxIDs: %d %d %d %d\n", patch->vtxIDs[0], patch->vtxIDs[1], patch->vtxIDs[2], patch->vtxIDs[3] );
Msg( " halfEdges.twin: %d/%d %d/%d %d/%d %d/%d\n",
patch->halfEdges[0].twin ? patch->halfEdges[0].twin->patch->patchID : -1, patch->halfEdges[0].twin ? patch->halfEdges[0].twin->localID: -1,
patch->halfEdges[1].twin ? patch->halfEdges[1].twin->patch->patchID : -1, patch->halfEdges[1].twin ? patch->halfEdges[1].twin->localID: -1,
patch->halfEdges[2].twin ? patch->halfEdges[2].twin->patch->patchID : -1, patch->halfEdges[2].twin ? patch->halfEdges[2].twin->localID: -1,
patch->halfEdges[3].twin ? patch->halfEdges[3].twin->patch->patchID : -1, patch->halfEdges[3].twin ? patch->halfEdges[3].twin->localID: -1 );
Msg( " halfEdges.sectorStart: %d/%d %d/%d %d/%d %d/%d\n",
patch->halfEdges[0].sectorStart ? patch->halfEdges[0].sectorStart->patch->patchID : -1, patch->halfEdges[0].sectorStart ? patch->halfEdges[0].sectorStart->localID: -1,
patch->halfEdges[1].sectorStart ? patch->halfEdges[1].sectorStart->patch->patchID : -1, patch->halfEdges[1].sectorStart ? patch->halfEdges[1].sectorStart->localID: -1,
patch->halfEdges[2].sectorStart ? patch->halfEdges[2].sectorStart->patch->patchID : -1, patch->halfEdges[2].sectorStart ? patch->halfEdges[2].sectorStart->localID: -1,
patch->halfEdges[3].sectorStart ? patch->halfEdges[3].sectorStart->patch->patchID : -1, patch->halfEdges[3].sectorStart ? patch->halfEdges[3].sectorStart->localID: -1 );
}
// Rotate a particular face one step (element N grabs from element N-1)
void COptimizeSubDBuilder::RotateOnce( SubD_Face_t *pPatch )
{
// Msg( "- Before ------------------------------------------------------------------------------------------\n" );
// DumpPatchLite( pPatch );
SubD_Face_t tmpFace;
memcpy( &tmpFace, pPatch, sizeof( SubD_Face_t ) );
HalfEdge *pTwins[4] = { NULL, NULL, NULL, NULL };
for ( int i=0; i<4; i++ )
{
pTwins[i] = pPatch->halfEdges[i].twin; // Point to each HalfEdge's twin
if ( pTwins[i] )
{
Assert( pTwins[i]->twin == &(pPatch->halfEdges[i]) ); // ith twin should be pointing back to ith HalfEdge
}
}
for ( int i=0; i<4; i++ )
{
pPatch->vtxIDs[i] = tmpFace.vtxIDs[(i+3)%4]; // Grab from n-1
pPatch->bndEdge[i] = tmpFace.bndEdge[(i+3)%4];
pPatch->bndVtx[i] = tmpFace.bndVtx[(i+3)%4];
memcpy( &(pPatch->halfEdges[i]), &(tmpFace.halfEdges[(i+3)%4]), sizeof(HalfEdge) );
pPatch->halfEdges[i].localID = i;
pPatch->halfEdges[i].sectorStart = &pPatch->halfEdges[i];
}
for ( int i=0; i<4; i++ )
{
if ( pTwins[i] )
{
pTwins[i]->twin = &(pPatch->halfEdges[(i+1)%4]); // Record twin's twin after we've rotated the local patch data
}
}
// Msg( "- After ------------------------------------------------------------------------------------------\n" );
// DumpPatchLite( pPatch );
// Msg( "---------------------------------------------------------------------------------------------------\n" );
// Msg( "---------------------------------------------------------------------------------------------------\n\n" );
}
void COptimizeSubDBuilder::RotateFace( SubD_Face_t *pPatch, int nTimesToRotate )
{
for ( int i=0; i<nTimesToRotate; i++ )
{
RotateOnce( pPatch );
}
}
int COptimizeSubDBuilder::FaceEdgeIndex( SubD_Face_t *pFace, HalfEdge *pEdge )
{
int i = 0;
while ( &(pFace->halfEdges[i]) != pEdge )
{
i++;
}
return i;
}
void COptimizeSubDBuilder::Propagate( CUtlVector<Orientation> & orientationArray, HalfEdge *pEdge, bool dir )
{
Assert( pEdge );
while( true )
{
HalfEdge *pNeighborEdge = pEdge->twin;
if ( !pNeighborEdge )
break; // Stop at mesh boundaries.
SubD_Face_t *pFace = pNeighborEdge->patch;
if ( !pFace )
break; // Stop at mesh boundaries.
int nEdgeIndex = FaceEdgeIndex( pFace, pNeighborEdge );
Orientation & faceOrientation = orientationArray[pFace->patchID];
if ( nEdgeIndex == 1 || nEdgeIndex == 3 )
{
if ( faceOrientation.uSet )
{
Assert( faceOrientation.u == ( ( nEdgeIndex == 1 ) ^ dir ) );
break;
}
faceOrientation.SetU( ( nEdgeIndex == 1 ) ^ dir );
}
else // if ( nEdgeIndex == 0 || nEdgeIndex == 2 )
{
if ( faceOrientation.vSet )
{
Assert( faceOrientation.v == ( ( nEdgeIndex == 0 ) ^ dir ) );
break;
}
faceOrientation.SetV( ( nEdgeIndex == 0 ) ^ dir );
}
pEdge = pNeighborEdge->NextInFace()->NextInFace();
}
}
static HalfEdge *FaceEdge( SubD_Face_t *pPatch, int idx )
{
int i = 0;
HalfEdge *pEdge = &pPatch->halfEdges[0];
while ( i != idx )
{
i++;
pEdge = pEdge->NextInFace();
}
return pEdge;
}
// Reorient faces in order to avoid parametric discontinuities.
void COptimizeSubDBuilder::ConsistentPatchOrientation()
{
CUtlVector<Orientation> orientationArray;
orientationArray.AddMultipleToTail( m_numPatches );
for( int f = 0; f < m_numPatches; f++ )
{
SubD_Face_t *pPatch = &m_faceList[f];
HalfEdge *pEdges = &pPatch->halfEdges[0];
if ( !orientationArray[f].uSet )
{
orientationArray[f].SetU( false );
Propagate( orientationArray, pEdges+1, false );
Propagate( orientationArray, pEdges+3, true );
}
if ( !orientationArray[f].vSet )
{
orientationArray[f].SetV( false );
Propagate( orientationArray, pEdges+0, false );
Propagate( orientationArray, pEdges+2, true );
}
}
for( int f = 0; f < m_numPatches; f++ )
{
SubD_Face_t *pPatch = &m_faceList[f];
const Orientation &o = orientationArray[f]; // Determine edge from orientation flags.
static const int nTimesToRotate[4] = {0, 1, 3, 2};
const int idx = nTimesToRotate[(o.v << 1) + o.u];
RotateFace( pPatch, idx );
}
}
void COptimizeSubDBuilder::TagCreases()
{
static int MOD4[] = {0,1,2,3,0,1,2,3};
for (unsigned short i=0; i<m_numPatches; i++)
{
SubD_Face_t *pPatch = &m_faceList[i];
for ( int k=0; k<4; k++ ) // for all vertices
{
if ( pPatch->halfEdges[k].twin != NULL )
{
HalfEdge *twin = pPatch->halfEdges[k].twin;
SubD_Face_t *nbQuad = twin->patch;
int quad0vtx0ID = pPatch->vtxIDs[ MOD4[k+0] ];
int quad1vtx0ID = nbQuad->vtxIDs[ MOD4[twin->localID+1] ];
int quad0vtx1ID = pPatch->vtxIDs[ MOD4[k+1] ];
int quad1vtx1ID = nbQuad->vtxIDs[ MOD4[twin->localID+0] ];
if ( ( VTXNOR( quad0vtx0ID ) != VTXNOR( quad1vtx0ID ) ) ||
( VTXNOR( quad0vtx1ID ) != VTXNOR( quad1vtx1ID ) ) )
{
pPatch->bndEdge[k] = true;
pPatch->bndVtx[MOD4[k+0]] = true;
pPatch->bndVtx[MOD4[k+1]] = true;
}
}
}
}
}
}; // namespace