|
|
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "disp_powerinfo.h"
#include "disp_common.h"
#include "commonmacros.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
// ------------------------------------------------------------------------ //
// Internal classes.
// ------------------------------------------------------------------------ //
// These point at the vertices connecting to each of the [north,south,east,west] vertices.
class CVertCorners { public: short m_Corner1[2]; short m_Corner2[2]; };
// ------------------------------------------------------------------------ //
// Globals.
// ------------------------------------------------------------------------ //
// This points at vertices to the side of a node (north, south, east, west).
static short g_SideVertMul[4][2] = { {1,0}, {0,1}, {-1,0}, {0,-1} };
static CVertCorners g_SideVertCorners[4] = { { {1,-1}, {1,1} }, { {1,1}, {-1,1} }, { {-1,1}, {-1,-1} }, { {-1,-1}, {1,-1} } };
// This is used in loops on child nodes. The indices point at the nodes:
// 0 = upper-right
// 1 = upper-left
// 2 = lower-left
// 3 = lower-right
static CVertIndex g_ChildNodeIndexMul[4] = { CVertIndex(1,1), CVertIndex(-1,1), CVertIndex(-1,-1), CVertIndex(1,-1) };
// These are multipliers on vertMul (not nodeMul).
static CVertIndex g_ChildNodeDependencies[4][2] = { { CVertIndex(1,0), CVertIndex(0,1) }, { CVertIndex(0,1), CVertIndex(-1,0) }, { CVertIndex(-1,0), CVertIndex(0,-1) }, { CVertIndex(0,-1), CVertIndex(1,0) } };
// 2x2 rotation matrices for each orientation.
static int g_OrientationRotations[4][2][2] = { {{1, 0}, // CCW_0
{0, 1}},
{{0, 1}, // CCW_90
{-1,0}},
{{-1,0}, // CCW_180
{0,-1}},
{{0, -1}, // CCW_270
{1, 0}} };
// ------------------------------------------------------------------------ //
// Helper functions.
// ------------------------------------------------------------------------ //
// Apply a 2D rotation to the specified CVertIndex around the specified centerpoint.
static CVertIndex Transform2D( int const mat[2][2], CVertIndex const &vert, CVertIndex const ¢erPoint ) { CVertIndex translated = vert - centerPoint; CVertIndex transformed( translated.x*mat[0][0] + translated.y*mat[0][1], translated.x*mat[1][0] + translated.y*mat[1][1] ); return transformed + centerPoint; }
// Rotate a given CVertIndex with a specified orientation.
// Do this with a lookup table eventually!
static void GetEdgeVertIndex( int sideLength, int iEdge, int iVert, CVertIndex &out ) { if( iEdge == NEIGHBOREDGE_RIGHT ) { out.x = sideLength - 1; out.y = iVert; } else if( iEdge == NEIGHBOREDGE_TOP ) { out.x = iVert; out.y = sideLength - 1; } else if( iEdge == NEIGHBOREDGE_LEFT ) { out.x = 0; out.y = iVert; } else { out.x = iVert; out.y = 0; } }
// Generate an index given a CVertIndex and the size of the displacement it resides in.
static int VertIndex( CVertIndex const &vert, int iMaxPower ) { return vert.y * ((1 << iMaxPower) + 1) + vert.x; }
static CVertIndex WrapVertIndex( CVertIndex const &in, int sideLength ) { int out[2];
for( int i=0; i < 2; i++ ) { if( in[i] < 0 ) out[i] = sideLength - 1 - (-in[i] % sideLength); else if( in[i] >= sideLength ) out[i] = in[i] % sideLength; else out[i] = in[i]; }
return CVertIndex( out[0], out[1] ); }
static int GetFreeDependency( CVertDependency *pDep, int nElements ) { for( int i=0; i < nElements; i++ ) { if( !pDep[i].IsValid() ) return i; }
Assert( false ); return 0; }
static void AddDependency( CVertInfo *dependencies, int sideLength, CVertIndex const &nodeIndex, CVertIndex const &dependency, int iMaxPower, bool bCheckNeighborDependency, bool bAddReverseDependency ) { int iNodeIndex = VertIndex( nodeIndex, iMaxPower ); CVertInfo *pNode = &dependencies[iNodeIndex];
int iDep = GetFreeDependency( pNode->m_Dependencies, sizeof(pNode->m_Dependencies)/sizeof(pNode->m_Dependencies[0]) ); pNode->m_Dependencies[iDep].m_iVert = dependency; pNode->m_Dependencies[iDep].m_iNeighbor = -1;
if( bAddReverseDependency ) { CVertInfo *pDep = &dependencies[VertIndex( dependency, iMaxPower )]; iDep = GetFreeDependency( pDep->m_ReverseDependencies, CVertInfo::NUM_REVERSE_DEPENDENCIES ); pDep->m_ReverseDependencies[iDep].m_iVert = nodeIndex; pDep->m_ReverseDependencies[iDep].m_iNeighbor = -1; }
// Edge verts automatically add a dependency for the neighbor.
// Internal verts wind up in here twice anyway so it doesn't need to
if( bCheckNeighborDependency ) { int iConnection = GetEdgeIndexFromPoint( nodeIndex, iMaxPower ); if( iConnection != -1 ) { Assert( !pNode->m_Dependencies[1].IsValid() );
CVertIndex delta( nodeIndex.x - dependency.x, nodeIndex.y - dependency.y ); CVertIndex newIndex( nodeIndex.x + delta.x, nodeIndex.y + delta.y ); int fullSideLength = (1 << iMaxPower) + 1; pNode->m_Dependencies[1].m_iVert = WrapVertIndex( CVertIndex( newIndex.x, newIndex.y ), fullSideLength ); pNode->m_Dependencies[1].m_iNeighbor = iConnection; } } }
// --------------------------------------------------------------------------------- //
// CTesselateWinding stuff.
// --------------------------------------------------------------------------------- //
CTesselateVert::CTesselateVert( CVertIndex const &index, int iNode ) : m_Index( index ) { m_iNode = iNode; }
CVertInfo::CVertInfo() { int i; for( i=0; i < sizeof(m_Dependencies)/sizeof(m_Dependencies[0]); i++ ) { m_Dependencies[i].m_iVert = CVertIndex( -1, -1 ); m_Dependencies[i].m_iNeighbor = -1; }
for( i=0; i < sizeof(m_ReverseDependencies)/sizeof(m_ReverseDependencies[0]); i++ ) { m_ReverseDependencies[i].m_iVert = CVertIndex( -1, -1 ); m_ReverseDependencies[i].m_iNeighbor = -1; } m_iParent.x = m_iParent.y = -1; m_iNodeLevel = -1; }
CTesselateVert g_TesselateVerts[] = { CTesselateVert( CVertIndex(1,-1), CHILDNODE_LOWER_RIGHT), CTesselateVert( CVertIndex(0,-1), -1), CTesselateVert( CVertIndex(-1,-1), CHILDNODE_LOWER_LEFT), CTesselateVert( CVertIndex(-1, 0), -1), CTesselateVert( CVertIndex(-1, 1), CHILDNODE_UPPER_LEFT), CTesselateVert( CVertIndex(0, 1), -1), CTesselateVert( CVertIndex(1, 1), CHILDNODE_UPPER_RIGHT), CTesselateVert( CVertIndex(1, 0), -1), CTesselateVert( CVertIndex(1,-1), CHILDNODE_LOWER_RIGHT) };
CTesselateWinding g_TWinding = { g_TesselateVerts, sizeof( g_TesselateVerts ) / sizeof( g_TesselateVerts[0] ) };
// --------------------------------------------------------------------------------- //
// CPowerInfo stuff.
// --------------------------------------------------------------------------------- //
// Precalculated info about each particular displacement size.
#define DECLARE_TABLES( size ) \
static CVertInfo g_VertInfo_##size##x##size[ size*size ]; \ static CFourVerts g_SideVerts_##size##x##size[ size*size ]; \ static CFourVerts g_ChildVerts_##size##x##size[ size*size ]; \ static CFourVerts g_SideVertCorners_##size##x##size[ size*size ]; \ static CTwoUShorts g_ErrorEdges_##size##x##size[ size*size ]; \ static CTriInfo g_TriInfos_##size##x##size[ (size-1)*(size-1)*2 ]; \ static CPowerInfo g_PowerInfo_##size##x##size( \ g_VertInfo_##size##x##size, \ g_SideVerts_##size##x##size, \ g_ChildVerts_##size##x##size, \ g_SideVertCorners_##size##x##size,\ g_ErrorEdges_##size##x##size, \ g_TriInfos_##size##x##size \ )
#define POWERINFO_ENTRY( size ) \
(&g_PowerInfo_##size##x##size)
DECLARE_TABLES( 5 ); DECLARE_TABLES( 9 ); DECLARE_TABLES( 17 );
// Index by m_Power.
CPowerInfo *g_PowerInfos[NUM_POWERINFOS] = { NULL, NULL, POWERINFO_ENTRY(5), POWERINFO_ENTRY(9), POWERINFO_ENTRY(17) };
CPowerInfo::CPowerInfo( CVertInfo *pVertInfo, CFourVerts *pSideVerts, CFourVerts *pChildVerts, CFourVerts *pSideVertCorners, CTwoUShorts *pErrorEdges, CTriInfo *pTriInfos ) { m_pVertInfo = pVertInfo; m_pSideVerts = pSideVerts; m_pChildVerts = pChildVerts; m_pSideVertCorners = pSideVertCorners; m_pErrorEdges = pErrorEdges; m_pTriInfos = pTriInfos; }
static void InitPowerInfoTriInfos_R( CPowerInfo *pInfo, CVertIndex const &nodeIndex, CTriInfo* &pTriInfo, int iMaxPower, int iLevel ) { int iNodeIndex = VertIndex( nodeIndex, iMaxPower );
if( iLevel+1 < iMaxPower ) { // Recurse into children.
for( int iChild=0; iChild < 4; iChild++ ) { InitPowerInfoTriInfos_R( pInfo, pInfo->m_pChildVerts[iNodeIndex].m_Verts[iChild], pTriInfo, iMaxPower, iLevel+1 ); } } else { unsigned short indices[3]; int vertInc = 1 << ((iMaxPower - iLevel) - 1);
// We're at a leaf, generate the tris.
CTesselateWinding *pWinding = &g_TWinding;
// Starting at the bottom-left, wind clockwise picking up vertices and
// generating triangles.
int iCurTriVert = 0; for( int iVert=0; iVert < pWinding->m_nVerts; iVert++ ) { CVertIndex sideVert = BuildOffsetVertIndex( nodeIndex, pWinding->m_Verts[iVert].m_Index, vertInc ); if( iCurTriVert == 1 ) { // Add this vert and finish the tri.
pTriInfo->m_Indices[0] = indices[0]; pTriInfo->m_Indices[1] = VertIndex( sideVert, iMaxPower ); pTriInfo->m_Indices[2] = iNodeIndex; ++pTriInfo; }
indices[0] = VertIndex( sideVert, iMaxPower ); iCurTriVert = 1; } } }
static void InitPowerInfo_R( CPowerInfo *pPowerInfo, int iMaxPower, CVertIndex const &nodeIndex, CVertIndex const &dependency1, CVertIndex const &dependency2, CVertIndex const &nodeEdge1, CVertIndex const &nodeEdge2, CVertIndex const &iParent, int iLevel ) { int sideLength = ((1 << iMaxPower) + 1); int iNodeIndex = VertIndex( nodeIndex, iMaxPower ); pPowerInfo->m_pVertInfo[iNodeIndex].m_iParent = iParent; pPowerInfo->m_pVertInfo[iNodeIndex].m_iNodeLevel = iLevel + 1;
pPowerInfo->m_pErrorEdges[iNodeIndex].m_Values[0] = (unsigned short)(VertIndex( nodeEdge1, iMaxPower )); pPowerInfo->m_pErrorEdges[iNodeIndex].m_Values[1] = (unsigned short)(VertIndex( nodeEdge2, iMaxPower )); // Add this node's dependencies.
AddDependency( pPowerInfo->m_pVertInfo, sideLength, nodeIndex, dependency1, iMaxPower, false, true ); AddDependency( pPowerInfo->m_pVertInfo, sideLength, nodeIndex, dependency2, iMaxPower, false, true );
// The 4 side vertices depend on this node.
int iPower = iMaxPower - iLevel; int vertInc = 1 << (iPower - 1);
for( int iSide=0; iSide < 4; iSide++ ) { // Store the side vert index.
CVertIndex sideVert( nodeIndex.x + g_SideVertMul[iSide][0]*vertInc, nodeIndex.y + g_SideVertMul[iSide][1]*vertInc ); int iSideVert = VertIndex( sideVert, iMaxPower );
pPowerInfo->m_pSideVerts[iNodeIndex].m_Verts[iSide] = sideVert;
// Store the side vert corners.
CVertIndex sideVertCorner0 = CVertIndex( nodeIndex.x + g_SideVertCorners[iSide].m_Corner1[0]*vertInc, nodeIndex.y + g_SideVertCorners[iSide].m_Corner1[1]*vertInc ); CVertIndex sideVertCorner1 = CVertIndex( nodeIndex.x + g_SideVertCorners[iSide].m_Corner2[0]*vertInc, nodeIndex.y + g_SideVertCorners[iSide].m_Corner2[1]*vertInc );
pPowerInfo->m_pSideVertCorners[iNodeIndex].m_Verts[iSide] = sideVertCorner0;
// Write the side vert corners into the error-edges list.
pPowerInfo->m_pErrorEdges[iSideVert].m_Values[0] = (unsigned short)VertIndex( sideVertCorner0, iMaxPower ); pPowerInfo->m_pErrorEdges[iSideVert].m_Values[1] = (unsigned short)VertIndex( sideVertCorner1, iMaxPower );
AddDependency( pPowerInfo->m_pVertInfo, sideLength, sideVert, nodeIndex, iMaxPower, true, true ); }
// Recurse into the children.
int nodeInc = vertInc >> 1; if( nodeInc ) { for( int iChild=0; iChild < 4; iChild++ ) { CVertIndex childVert( nodeIndex.x + g_ChildNodeIndexMul[iChild].x * nodeInc, nodeIndex.y + g_ChildNodeIndexMul[iChild].y * nodeInc );
pPowerInfo->m_pChildVerts[iNodeIndex].m_Verts[iChild] = childVert;
InitPowerInfo_R( pPowerInfo, iMaxPower, childVert, CVertIndex(nodeIndex.x + g_ChildNodeDependencies[iChild][0].x*vertInc, nodeIndex.y + g_ChildNodeDependencies[iChild][0].y*vertInc), CVertIndex(nodeIndex.x + g_ChildNodeDependencies[iChild][1].x*vertInc, nodeIndex.y + g_ChildNodeDependencies[iChild][1].y*vertInc), nodeIndex, CVertIndex( nodeIndex.x + g_ChildNodeIndexMul[iChild].x * vertInc, nodeIndex.y + g_ChildNodeIndexMul[iChild].y * vertInc ), nodeIndex, iLevel + 1 ); } } }
void InitPowerInfo( CPowerInfo *pInfo, int iMaxPower ) { int sideLength = (1 << iMaxPower) + 1;
// Precalculate the dependency graph.
CVertIndex nodeDependency1( sideLength-1, sideLength-1 ); CVertIndex nodeDependency2( 0, 0 );
pInfo->m_RootNode = CVertIndex( sideLength/2, sideLength/2 ); pInfo->m_SideLength = sideLength; pInfo->m_SideLengthM1 = sideLength - 1; pInfo->m_MidPoint = sideLength / 2; pInfo->m_MaxVerts = sideLength * sideLength;
// Setup the corner indices.
pInfo->m_CornerPointIndices[CORNER_LOWER_LEFT].Init( 0, 0 ); pInfo->m_CornerPointIndices[CORNER_UPPER_LEFT].Init( 0, sideLength-1 ); pInfo->m_CornerPointIndices[CORNER_UPPER_RIGHT].Init( sideLength-1, sideLength-1 ); pInfo->m_CornerPointIndices[CORNER_LOWER_RIGHT].Init( sideLength-1, 0 ); InitPowerInfo_R( pInfo, iMaxPower, pInfo->m_RootNode, nodeDependency1, // dependencies
nodeDependency2, CVertIndex(0,0), // error edge
CVertIndex(sideLength-1, sideLength-1), CVertIndex(-1,-1), // parent
0 );
pInfo->m_Power = iMaxPower; CTriInfo *pTriInfo = pInfo->m_pTriInfos; InitPowerInfoTriInfos_R( pInfo, pInfo->m_RootNode, pTriInfo, iMaxPower, 0 );
for( int iEdge=0; iEdge < 4; iEdge++ ) { // Figure out the start vert and increment.
CVertIndex nextVert; GetEdgeVertIndex( sideLength, iEdge, 0, pInfo->m_EdgeStartVerts[iEdge] ); GetEdgeVertIndex( sideLength, iEdge, 1, nextVert ); pInfo->m_EdgeIncrements[iEdge] = nextVert - pInfo->m_EdgeStartVerts[iEdge];
// Now get the neighbor's start vert and increment.
CVertIndex nbStartVert, nbNextVert, nbDelta; GetEdgeVertIndex( sideLength, (iEdge+2)&3, 0, nbStartVert ); GetEdgeVertIndex( sideLength, (iEdge+2)&3, 1, nbNextVert ); nbDelta = nbNextVert - nbStartVert;
// Rotate it for each orientation.
for( int orient=0; orient < 4; orient++ ) { pInfo->m_NeighborStartVerts[iEdge][orient] = Transform2D( g_OrientationRotations[orient], nbStartVert, CVertIndex( sideLength/2, sideLength/2 ) );
pInfo->m_NeighborIncrements[iEdge][orient] = Transform2D( g_OrientationRotations[orient], nbDelta, CVertIndex(0,0) ); } }
// Init the node index increments.
int curPowerOf4 = 1; int curTotal = 0; for( int i=0; i < iMaxPower-1; i++ ) { curTotal += curPowerOf4;
pInfo->m_NodeIndexIncrements[iMaxPower-i-2] = curTotal;
curPowerOf4 *= 4; }
// Store off the total node count
pInfo->m_NodeCount = curTotal + curPowerOf4;
pInfo->m_nTriInfos = Square( 1 << iMaxPower ) * 2; }
class CPowerInfoInitializer { public: CPowerInfoInitializer() { Assert( MAX_MAP_DISP_POWER+1 == NUM_POWERINFOS );
for( int i=0; i <= MAX_MAP_DISP_POWER; i++ ) { if( g_PowerInfos[i] ) { InitPowerInfo( g_PowerInfos[i], i ); } } } };
static CPowerInfoInitializer g_PowerInfoInitializer;
const CPowerInfo* GetPowerInfo( int iPower ) { Assert( iPower >= 0 && iPower < ARRAYSIZE( g_PowerInfos ) ); Assert( g_PowerInfos[iPower] ); return g_PowerInfos[iPower]; }
// ------------------------------------------------------------------------------------------------ //
// CPowerInfo member function initialization.
// ------------------------------------------------------------------------------------------------ //
const CVertIndex& CPowerInfo::GetCornerPointIndex( int iCorner ) const { Assert( iCorner >= 0 && iCorner < 4 ); return m_CornerPointIndices[iCorner]; }
|