181 lines
6.9 KiB
C++
181 lines
6.9 KiB
C++
//========= Copyright Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//
|
|
// $NoKeywords: $
|
|
//=============================================================================//
|
|
|
|
#ifndef DISP_TESSELATE_H
|
|
#define DISP_TESSELATE_H
|
|
#ifdef _WIN32
|
|
#pragma once
|
|
#endif
|
|
|
|
#include "disp_powerinfo.h"
|
|
|
|
inline int InternalVertIndex(const CPowerInfo *pInfo, const CVertIndex &vert) {
|
|
return vert.y * pInfo->m_SideLength + vert.x;
|
|
}
|
|
|
|
template <class TesselateHelper>
|
|
inline void InternalEndTriangle(TesselateHelper *pHelper,
|
|
CVertIndex const &nodeIndex, int &iCurTriVert) {
|
|
// End our current triangle here.
|
|
Assert(iCurTriVert == 2);
|
|
|
|
// Finish the triangle.
|
|
pHelper->m_TempIndices[2] =
|
|
(unsigned short)InternalVertIndex(pHelper->m_pPowerInfo, nodeIndex);
|
|
|
|
pHelper->EndTriangle();
|
|
|
|
// Add on the last vertex to join to the next triangle.
|
|
pHelper->m_TempIndices[0] = pHelper->m_TempIndices[1];
|
|
iCurTriVert = 1;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Tesselates a single node, doesn't deal with hierarchy
|
|
//-----------------------------------------------------------------------------
|
|
template <class TesselateHelper>
|
|
inline void TesselateDisplacementNode(TesselateHelper *pHelper,
|
|
CVertIndex const &nodeIndex, int iLevel,
|
|
int *pActiveChildren) {
|
|
int iPower = pHelper->m_pPowerInfo->m_Power - iLevel;
|
|
int vertInc = 1 << (iPower - 1);
|
|
|
|
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);
|
|
|
|
int iVertNode = pWinding->m_Verts[iVert].m_iNode;
|
|
bool bNode = (iVertNode != -1) && pActiveChildren[iVertNode];
|
|
if (bNode) {
|
|
if (iCurTriVert == 2)
|
|
InternalEndTriangle(pHelper, nodeIndex, iCurTriVert);
|
|
|
|
iCurTriVert = 0;
|
|
} else {
|
|
int iVertBit = InternalVertIndex(pHelper->m_pPowerInfo, sideVert);
|
|
if (pHelper->m_pActiveVerts[iVertBit >> 5] &
|
|
(1 << (iVertBit & 31))) {
|
|
// Ok, add a vert here.
|
|
pHelper->m_TempIndices[iCurTriVert] =
|
|
(unsigned short)InternalVertIndex(pHelper->m_pPowerInfo,
|
|
sideVert);
|
|
iCurTriVert++;
|
|
if (iCurTriVert == 2)
|
|
InternalEndTriangle(pHelper, nodeIndex, iCurTriVert);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Tesselates in a *breadth first* fashion
|
|
//-----------------------------------------------------------------------------
|
|
template <class T>
|
|
inline void TesselateDisplacement_R(T *pHelper, const CVertIndex &nodeIndex,
|
|
int iNodeBitIndex, int iLevel) {
|
|
// Here's the node info for our current node
|
|
Assert(iNodeBitIndex < pHelper->m_pPowerInfo->m_NodeCount);
|
|
DispNodeInfo_t &nodeInfo = pHelper->GetNodeInfo(iNodeBitIndex);
|
|
|
|
// Store off the current number of indices
|
|
int oldIndexCount = pHelper->m_nIndices;
|
|
|
|
// Go through each quadrant. If there is an active child node, recurse down.
|
|
int bActiveChildren[4];
|
|
if (iLevel >= pHelper->m_pPowerInfo->m_Power - 1) {
|
|
// This node has no children.
|
|
bActiveChildren[0] = bActiveChildren[1] = bActiveChildren[2] =
|
|
bActiveChildren[3] = false;
|
|
} else {
|
|
int iNodeIndex = InternalVertIndex(pHelper->m_pPowerInfo, nodeIndex);
|
|
|
|
int iChildNodeBit = iNodeBitIndex + 1;
|
|
for (int iChild = 0; iChild < 4; iChild++) {
|
|
CVertIndex const &childNode =
|
|
pHelper->m_pPowerInfo->m_pChildVerts[iNodeIndex]
|
|
.m_Verts[iChild];
|
|
|
|
// Make sure we really can tesselate here (a smaller neighbor
|
|
// displacement could have inactivated certain edge verts.
|
|
int iVertBit = InternalVertIndex(pHelper->m_pPowerInfo, childNode);
|
|
bActiveChildren[iChild] = (pHelper->m_pActiveVerts[iVertBit >> 5] &
|
|
(1 << (iVertBit & 31)));
|
|
|
|
if (bActiveChildren[iChild]) {
|
|
TesselateDisplacement_R(pHelper, childNode, iChildNodeBit,
|
|
iLevel + 1);
|
|
} else {
|
|
// Make sure the triangle counts are cleared on this one because
|
|
// it may visit this node in GenerateDecalFragments_R if
|
|
// nodeInfo's CHILDREN_HAVE_TRIANGLES flag is set.
|
|
DispNodeInfo_t &childInfo = pHelper->GetNodeInfo(iChildNodeBit);
|
|
childInfo.m_Count = 0;
|
|
childInfo.m_Flags = 0;
|
|
}
|
|
|
|
iChildNodeBit +=
|
|
pHelper->m_pPowerInfo->m_NodeIndexIncrements[iLevel];
|
|
}
|
|
}
|
|
|
|
// Set the child field
|
|
if (pHelper->m_nIndices != oldIndexCount) {
|
|
nodeInfo.m_Flags = DispNodeInfo_t::CHILDREN_HAVE_TRIANGLES;
|
|
oldIndexCount = pHelper->m_nIndices;
|
|
} else {
|
|
nodeInfo.m_Flags = 0;
|
|
}
|
|
|
|
// Now tesselate the node itself...
|
|
TesselateDisplacementNode(pHelper, nodeIndex, iLevel, bActiveChildren);
|
|
|
|
// Now that we've tesselated, figure out how many indices we've added at
|
|
// this node
|
|
nodeInfo.m_Count = pHelper->m_nIndices - oldIndexCount;
|
|
nodeInfo.m_FirstTesselationIndex = oldIndexCount;
|
|
Assert(nodeInfo.m_Count % 3 == 0);
|
|
}
|
|
|
|
class CBaseTesselateHelper {
|
|
public:
|
|
// Functions your derived class must implement:
|
|
// void EndTriangle(); // (the 3 indices are in
|
|
// m_TempIndices). DispNodeInfo_t& GetNodeInfo( int iNodeBit );
|
|
|
|
// Set these before calling TesselateDisplacement.
|
|
uint32 *m_pActiveVerts; // These bits control the tesselation.
|
|
const CPowerInfo *m_pPowerInfo; // Lots of precalculated data about a
|
|
// displacement this size.
|
|
|
|
// Used internally by TesselateDisplacement.
|
|
int m_nIndices; // After calling TesselateDisplacement, this is set to the
|
|
// # of indices generated.
|
|
unsigned short m_TempIndices[6];
|
|
};
|
|
|
|
// This interface is shared betwixt VBSP and the engine. VBSP uses it to build
|
|
// the physics mesh and the engine uses it to render.
|
|
//
|
|
// To use this function, derive a class from CBaseTesselateHelper that supports
|
|
// the TesselateHelper functions.
|
|
template <class TesselateHelper>
|
|
inline void TesselateDisplacement(TesselateHelper *pHelper) {
|
|
pHelper->m_nIndices = 0;
|
|
|
|
TesselateDisplacement_R<TesselateHelper>(
|
|
pHelper, pHelper->m_pPowerInfo->m_RootNode,
|
|
0, // node bit indexing CDispDecal::m_NodeIntersects
|
|
0);
|
|
}
|
|
|
|
#endif // DISP_TESSELATE_H
|