aboutsummaryrefslogtreecommitdiff
path: root/mp/src/public/disp_tesselate.h
blob: d2d3caf2058886077c4c86820bc195dc780351b6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
//========= 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