//========= 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]; }