//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $Workfile: $ // $Date: $ // $NoKeywords: $ //=============================================================================// //#include #include #include #include "builddisp.h" #include "collisionutils.h" #include "tier1/strtools.h" #include "tier0/dbg.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" // // Node Functions (friend functions) // //----------------------------------------------------------------------------- // should make this more programatic and extensible! //----------------------------------------------------------------------------- int GetNodeLevel( int index ) { // root if( index == 0 ) return 1; // [1...4] if( index < 5 ) return 2; // [5....20] if( index < 21 ) return 3; // [21....84] if( index < 85 ) return 4; // error!!! return -1; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- int GetNodeCount( int power ) { return ( ( 1 << ( power << 1 ) ) / 3 ); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- int GetNodeParent( int index ) { // ( index - 1 ) / 4 return ( ( index - 1 ) >> 2 ); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- int GetNodeChild( int power, int index, int direction ) { // ( index * 4 ) + direction return ( ( index << 2 ) + ( direction - 3 ) ); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- int GetNodeMinNodeAtLevel( int level ) { switch( level ) { case 1: return 0; case 2: return 1; case 3: return 5; case 4: return 21; default: return -99999; } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void GetComponentsFromNodeIndex( int index, int *x, int *y ) { *x = 0; *y = 0; for( int shift = 0; index != 0; shift++ ) { *x |= ( index & 1 ) << shift; index >>= 1; *y |= ( index & 1 ) << shift; index >>= 1; } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- int GetNodeIndexFromComponents( int x, int y ) { int index = 0; // Interleave bits from the x and y values to create the index: int shift; for( shift = 0; x != 0; shift += 2, x >>= 1 ) { index |= ( x & 1 ) << shift; } for( shift = 1; y != 0; shift += 2, y >>= 1 ) { index |= ( y & 1 ) << shift; } return index; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- bool CalcBarycentricCooefs( Vector const &v0, Vector const &v1, Vector const &v2, Vector const &pt, float &c0, float &c1, float &c2 ) { Vector vSeg0, vSeg1, vCross; vSeg0 = v1 - v0; vSeg1 = v2 - v0; // get the area of the triangle vCross = vSeg0.Cross( vSeg1 ); float totalArea = vCross.Length() * 0.5f; float ooTotalArea = totalArea ? 1.0f / totalArea : 0.0f; // get the area for cooeficient 0 (pt, v1, v2) vSeg0 = v1 - pt; vSeg1 = v2 - pt; vCross = vSeg0.Cross( vSeg1 ); float subArea = vCross.Length() * 0.5f; c0 = subArea * ooTotalArea; // get the area for cooeficient 1 (v0, pt, v2) vSeg0 = v2 - pt; vSeg1 = v0 - pt; vCross = vSeg0.Cross( vSeg1 ); subArea = vCross.Length() * 0.5f; c1 = subArea * ooTotalArea; // get the area for cooeficient 2 (v0, v1, pt) vSeg0 = v0 - pt; vSeg1 = v1 - pt; vCross = vSeg0.Cross( vSeg1 ); subArea = vCross.Length() * 0.5f; c2 = subArea * ooTotalArea; float cTotal = c0 + c1 + c2; if ( FloatMakePositive( 1.0f - cTotal ) < 1e-3 ) return true; return false; } // For some reason, the global optimizer screws up the recursion here. disable the global optimizations to fix this. // IN VC++ 6.0 #pragma optimize( "g", off ) CCoreDispSurface::CCoreDispSurface() { Init(); } //============================================================================= // // CDispSurface Functions // //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CCoreDispSurface::Init( void ) { m_Index = -1; m_PointCount = 0; int i; for( i = 0; i < QUAD_POINT_COUNT; i++ ) { VectorClear( m_Points[i] ); VectorClear( m_Normals[i] ); Vector2DClear( m_TexCoords[i] ); for( int j = 0; j < NUM_BUMP_VECTS+1; j++ ) { Vector2DClear( m_LuxelCoords[i][j] ); } m_Alphas[i] = 1.0f; } m_PointStartIndex = -1; VectorClear( m_PointStart ); VectorClear( sAxis ); VectorClear( tAxis ); for( i = 0; i < 4; i++ ) { m_EdgeNeighbors[i].SetInvalid(); m_CornerNeighbors[i].SetInvalid(); } m_Flags = 0; m_Contents = 0; } void CCoreDispSurface::SetNeighborData( const CDispNeighbor edgeNeighbors[4], const CDispCornerNeighbors cornerNeighbors[4] ) { for ( int i=0; i < 4; i++ ) { m_EdgeNeighbors[i] = edgeNeighbors[i]; m_CornerNeighbors[i] = cornerNeighbors[i]; } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CCoreDispSurface::GeneratePointStartIndexFromMappingAxes( Vector const &sAxis_, Vector const &tAxis_ ) { if( m_PointStartIndex != -1 ) return; int numIndices = 0; int indices[4]; int offsetIndex; // // project all points on to the v-axis first and find the minimum // float minValue = DotProduct( tAxis_, m_Points[0] ); indices[numIndices] = 0; numIndices++; int i; for( i = 1; i < m_PointCount; i++ ) { float value = DotProduct( tAxis_, m_Points[i] ); float delta = ( value - minValue ); delta = FloatMakePositive( delta ); if( delta < 0.1 ) { indices[numIndices] = i; numIndices++; } else if( value < minValue ) { minValue = value; indices[0] = i; numIndices = 1; } } // // break ties with the u-axis projection // minValue = DotProduct( sAxis_, m_Points[indices[0]] ); offsetIndex = indices[0]; for( i = 1; i < numIndices; i++ ) { float value = DotProduct( sAxis_, m_Points[indices[i]] ); if( ( value < minValue ) ) { minValue = value; offsetIndex = indices[i]; } } m_PointStartIndex = offsetIndex; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- int CCoreDispSurface::GenerateSurfPointStartIndex( void ) { // // get the minimum surface component values // Vector bMin; VectorFill( bMin, 99999.0f ); int i; for( i = 0; i < QUAD_POINT_COUNT; i++ ) { for( int j = 0; j < 3; j++ ) { if( m_Points[i][j] < bMin[j] ) { bMin[j] = m_Points[i][j]; } } } // // find the point closest to the minimum, that is the start point // int minIndex = -1; float minDistance = 999999999.0f; for( i = 0; i < QUAD_POINT_COUNT; i++ ) { Vector segment; segment = m_Points[i] - bMin; float distanceSq = segment.LengthSqr(); if( distanceSq < minDistance ) { minDistance = distanceSq; minIndex = i; } } m_PointStartIndex = minIndex; return minIndex; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- int CCoreDispSurface::FindSurfPointStartIndex( void ) { if( m_PointStartIndex != -1 ) return m_PointStartIndex; int minIndex = -1; float minDistance = 999999999.0f; for( int i = 0; i < QUAD_POINT_COUNT; i++ ) { Vector segment; VectorSubtract( m_PointStart, m_Points[i], segment ); float distanceSq = segment.LengthSqr(); if( distanceSq < minDistance ) { minDistance = distanceSq; minIndex = i; } } m_PointStartIndex = minIndex; return minIndex; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CCoreDispSurface::AdjustSurfPointData( void ) { Vector tmpPoints[4]; Vector tmpNormals[4]; Vector2D tmpTexCoords[4]; float tmpAlphas[4]; int i; for( i = 0; i < QUAD_POINT_COUNT; i++ ) { VectorCopy( m_Points[i], tmpPoints[i] ); VectorCopy( m_Normals[i], tmpNormals[i] ); Vector2DCopy( m_TexCoords[i], tmpTexCoords[i] ); tmpAlphas[i] = m_Alphas[i]; } for( i = 0; i < QUAD_POINT_COUNT; i++ ) { VectorCopy( tmpPoints[(i+m_PointStartIndex)%4], m_Points[i] ); VectorCopy( tmpNormals[(i+m_PointStartIndex)%4], m_Normals[i] ); Vector2DCopy( tmpTexCoords[(i+m_PointStartIndex)%4], m_TexCoords[i] ); m_Alphas[i] = tmpAlphas[i]; } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CCoreDispSurface::LongestInU( const Vector &vecU, const Vector &vecV ) { Vector vecNormU = vecU; Vector vecNormV = vecV; VectorNormalize( vecNormU ); VectorNormalize( vecNormV ); float flDistU[4]; float flDistV[4]; for ( int iPoint = 0; iPoint < 4; ++iPoint ) { flDistU[iPoint] = vecNormU.Dot( m_Points[iPoint] ); flDistV[iPoint] = vecNormV.Dot( m_Points[iPoint] ); } float flULength = 0.0f; float flVLength = 0.0f; for ( int iPoint = 0; iPoint < 4; ++iPoint ) { float flTestDist = fabs( flDistU[(iPoint+1)%4] - flDistU[iPoint] ); if ( flTestDist > flULength ) { flULength = flTestDist; } flTestDist = fabs( flDistV[(iPoint+1)%4] - flDistV[iPoint] ); if ( flTestDist > flVLength ) { flVLength = flTestDist; } } if ( flULength < flVLength ) { return false; } return true; } //----------------------------------------------------------------------------- // Purpose: // Input : - //----------------------------------------------------------------------------- bool CCoreDispSurface::CalcLuxelCoords( int nLuxels, bool bAdjust, const Vector &vecU, const Vector &vecV ) { // Valid value? if ( nLuxels <= 0.0f ) return false; // Get the start point offset. int iOffset = 0; if ( bAdjust ) { iOffset = GetPointStartIndex(); } // Does projecting along U or V create the longest edge? bool bLongU = LongestInU( vecU, vecV ); float flLengthTemp = 0.0f; float flULength = ( m_Points[(3+iOffset)%4] - m_Points[(0+iOffset)%4] ).Length(); flLengthTemp = ( m_Points[(2+iOffset)%4] - m_Points[(1+iOffset)%4] ).Length(); if ( flLengthTemp > flULength ) { flULength = flLengthTemp; } // Find the largest edge in V. float flVLength = ( m_Points[(1+iOffset)%4] - m_Points[(0+iOffset)%4] ).Length(); flLengthTemp = ( m_Points[(2+iOffset)%4] - m_Points[(3+iOffset)%4] ).Length(); if ( flLengthTemp > flVLength ) { flVLength = flLengthTemp; } float flOOLuxelScale = 1.0f / static_cast( nLuxels ); float flUValue = static_cast( static_cast( flULength * flOOLuxelScale ) + 1 ); if ( flUValue > MAX_DISP_LIGHTMAP_DIM_WITHOUT_BORDER ) { flUValue = MAX_DISP_LIGHTMAP_DIM_WITHOUT_BORDER; } float flVValue = static_cast( static_cast( flVLength * flOOLuxelScale ) + 1 ); if ( flVValue > MAX_DISP_LIGHTMAP_DIM_WITHOUT_BORDER ) { flVValue = MAX_DISP_LIGHTMAP_DIM_WITHOUT_BORDER; } // Swap if necessary. bool bSwapped = false; if ( bLongU ) { if ( flVValue > flUValue ) { bSwapped = true; } } else { if ( flUValue > flVValue ) { bSwapped = true; } } m_nLuxelU = static_cast( flUValue ); m_nLuxelV = static_cast( flVValue ); // Generate luxel coordinates. for( int iBump = 0; iBump < NUM_BUMP_VECTS+1; ++iBump ) { m_LuxelCoords[iBump][(0+iOffset)%4].Init( 0.5f, 0.5f ); m_LuxelCoords[iBump][(1+iOffset)%4].Init( 0.5f, flVValue + 0.5 ); m_LuxelCoords[iBump][(2+iOffset)%4].Init( flUValue + 0.5, flVValue + 0.5 ); m_LuxelCoords[iBump][(3+iOffset)%4].Init( flUValue + 0.5, 0.5f ); } return bSwapped; } //============================================================================= // // CDispNode Functions // //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CCoreDispNode::Init( void ) { VectorClear( m_BBox[0] ); VectorClear( m_BBox[1] ); m_ErrorTerm = 0.0f; m_VertIndex = -1; int j; for( j = 0; j < MAX_NEIGHBOR_NODE_COUNT; j++ ) { m_NeighborVertIndices[j] = -1; } for( j = 0; j < MAX_SURF_AT_NODE_COUNT; j++ ) { VectorClear( m_SurfBBoxes[j][0] ); VectorClear( m_SurfBBoxes[j][1] ); VectorClear( m_SurfPlanes[j].normal ); m_SurfPlanes[j].dist = 0.0f; } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void GetDispNodeTriVerts( CCoreDispInfo *pDisp, int nodeIndex, int triIndex, Vector& v1, Vector& v2, Vector& v3 ) { // get the node CCoreDispNode *pNode = pDisp->GetNode( nodeIndex ); switch( triIndex ) { case 0: { pDisp->GetVert( pNode->GetNeighborVertIndex( 4 ), v1 ); pDisp->GetVert( pNode->GetNeighborVertIndex( 0 ), v2 ); pDisp->GetVert( pNode->GetNeighborVertIndex( 3 ), v3 ); return; } case 1: { pDisp->GetVert( pNode->GetNeighborVertIndex( 3 ), v1 ); pDisp->GetVert( pNode->GetNeighborVertIndex( 0 ), v2 ); pDisp->GetVert( pNode->GetCenterVertIndex(), v3 ); return; } case 2: { pDisp->GetVert( pNode->GetNeighborVertIndex( 3 ), v1 ); pDisp->GetVert( pNode->GetCenterVertIndex(), v2 ); pDisp->GetVert( pNode->GetNeighborVertIndex( 5 ), v3 ); return; } case 3: { pDisp->GetVert( pNode->GetNeighborVertIndex( 5 ), v1 ); pDisp->GetVert( pNode->GetCenterVertIndex(), v2 ); pDisp->GetVert( pNode->GetNeighborVertIndex( 2 ), v3 ); return; } case 4: { pDisp->GetVert( pNode->GetNeighborVertIndex( 0 ), v1 ); pDisp->GetVert( pNode->GetNeighborVertIndex( 6 ), v2 ); pDisp->GetVert( pNode->GetCenterVertIndex(), v3 ); return; } case 5: { pDisp->GetVert( pNode->GetCenterVertIndex(), v1 ); pDisp->GetVert( pNode->GetNeighborVertIndex( 6 ), v2 ); pDisp->GetVert( pNode->GetNeighborVertIndex( 1 ), v3 ); return; } case 6: { pDisp->GetVert( pNode->GetCenterVertIndex(), v1 ); pDisp->GetVert( pNode->GetNeighborVertIndex( 1 ), v2 ); pDisp->GetVert( pNode->GetNeighborVertIndex( 2 ), v3 ); return; } case 7: { pDisp->GetVert( pNode->GetNeighborVertIndex( 2 ), v1 ); pDisp->GetVert( pNode->GetNeighborVertIndex( 1 ), v2 ); pDisp->GetVert( pNode->GetNeighborVertIndex( 7 ), v3 ); return; } default: { return; } } } //============================================================================= // // CCoreDispInfo Functions // //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- CCoreDispInfo::CCoreDispInfo() { m_pVerts = NULL; m_RenderIndices = NULL; m_Nodes = NULL; m_pTris = NULL; // initialize the base surface data m_Surf.Init(); // // initialize the disp info // m_Power = 0; m_Elevation = 0.0f; m_RenderIndexCount = 0; m_RenderCounter = 0; m_bTouched = false; m_pNext = NULL; m_ppListBase = NULL; m_ListSize = 0; m_nListIndex = -1; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- CCoreDispInfo::~CCoreDispInfo() { if (m_pVerts) delete [] m_pVerts; if (m_RenderIndices) delete [] m_RenderIndices; if (m_Nodes) delete [] m_Nodes; if (m_pTris) delete [] m_pTris; } #if 0 //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CCoreDispInfo::InitSurf( int parentIndex, Vector points[4], Vector normals[4], Vector2D texCoords[4], Vector2D lightCoords[4][4], int contents, int flags, bool bGenerateSurfPointStart, Vector& startPoint, bool bHasMappingAxes, Vector& uAxis, Vector& vAxis ) { // save the "parent" index m_Surf.m_Index = parentIndex; // // save the surface points and point normals, texture coordinates, and // lightmap coordinates // m_Surf.m_PointCount = CSurface::QUAD_POINT_COUNT; for( int i = 0; i < CSurface::QUAD_POINT_COUNT; i++ ) { VectorCopy( points[i], m_Surf.m_Points[i] ); if( normals ) { VectorCopy( normals[i], m_Surf.m_pVerts[i].m_Normal ); } if( texCoords ) { Vector2DCopy( texCoords[i], m_Surf.m_TexCoords[i] ); } if( lightCoords ) { Assert( NUM_BUMP_VECTS == 3 ); Vector2DCopy( lightCoords[0][i], m_Surf.m_LightCoords[i][0] ); Vector2DCopy( lightCoords[1][i], m_Surf.m_LightCoords[i][1] ); Vector2DCopy( lightCoords[2][i], m_Surf.m_LightCoords[i][2] ); Vector2DCopy( lightCoords[3][i], m_Surf.m_LightCoords[i][3] ); } } // save the starting point if( startPoint ) { VectorCopy( startPoint, m_Surf.m_PointStart ); } // // save the surface contents and flags // m_Contents = contents; m_Flags = flags; // // adjust surface points, texture coordinates, etc.... // if( bHasMappingAxes && ( m_Surf.m_PointStartIndex == -1 ) ) { GeneratePointStartIndexFromMappingAxes( uAxis, vAxis ); } else { // // adjust the surf data // if( bGenerateSurfPointStart ) { GenerateSurfPointStartIndex(); } else { FindSurfPointStartIndex(); } } AdjustSurfPointData(); } #endif //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CCoreDispInfo::InitDispInfo( int power, int minTess, float smoothingAngle, float *alphas, Vector *dispVectorField, float *dispDistances ) { Assert( power >= MIN_MAP_DISP_POWER && power <= MAX_MAP_DISP_POWER ); // // general displacement data // m_Power = power; if ( ( minTess & 0x80000000 ) != 0 ) { // If the high bit is set, this represents FLAGS (SURF_NOPHYSICS_COLL, etc.) flags. int nFlags = minTess; nFlags &= ~0x80000000; GetSurface()->SetFlags( nFlags ); } // Allocate + initialize verts int size = GetSize(); m_pVerts = new CoreDispVert_t[size]; int nIndexCount = size * 2 * 3; m_RenderIndices = new unsigned short[nIndexCount]; int nNodeCount = GetNodeCount(power); m_Nodes = new CCoreDispNode[nNodeCount]; int i; for( i = 0; i < size; i++ ) { m_pVerts[i].m_FieldVector.Init(); m_pVerts[i].m_SubdivPos.Init(); m_pVerts[i].m_SubdivNormal.Init(); m_pVerts[i].m_FieldDistance = 0.0f; m_pVerts[i].m_Vert.Init(); m_pVerts[i].m_FlatVert.Init(); m_pVerts[i].m_Normal.Init(); m_pVerts[i].m_TangentS.Init(); m_pVerts[i].m_TangentT.Init(); m_pVerts[i].m_TexCoord.Init(); for( int j = 0; j < ( NUM_BUMP_VECTS + 1 ); j++ ) { m_pVerts[i].m_LuxelCoords[j].Init(); } m_pVerts[i].m_Alpha = 0.0f; } for( i = 0; i < nIndexCount; i++ ) { m_RenderIndices[i] = 0; } for( i = 0; i < nNodeCount; i++ ) { m_Nodes[i].Init(); } // // save the displacement vector field and distances within the field // offset have been combined with fieldvectors at this point!!! // if (alphas && dispVectorField && dispDistances) { for( i = 0; i < size; i++ ) { VectorCopy( dispVectorField[i], m_pVerts[i].m_FieldVector ); m_pVerts[i].m_FieldDistance = dispDistances[i]; m_pVerts[i].m_Alpha = alphas[i]; } } // Init triangle information. int nTriCount = GetTriCount(); if ( nTriCount != 0 ) { m_pTris = new CoreDispTri_t[nTriCount]; if ( m_pTris ) { InitTris(); } } } void CCoreDispInfo::InitDispInfo( int power, int minTess, float smoothingAngle, const CDispVert *pVerts, const CDispTri *pTris ) { Vector vectors[MAX_DISPVERTS]; float dists[MAX_DISPVERTS]; float alphas[MAX_DISPVERTS]; int nVerts = NUM_DISP_POWER_VERTS( power ); for ( int i=0; i < nVerts; i++ ) { vectors[i] = pVerts[i].m_vVector; dists[i] = pVerts[i].m_flDist; alphas[i] = pVerts[i].m_flAlpha; } InitDispInfo( power, minTess, smoothingAngle, alphas, vectors, dists ); int nTris = NUM_DISP_POWER_TRIS( power ); for ( int iTri = 0; iTri < nTris; ++iTri ) { m_pTris[iTri].m_uiTags = pTris[iTri].m_uiTags; } } void CCoreDispInfo::SetDispUtilsHelperInfo( CCoreDispInfo **ppListBase, int listSize ) { m_ppListBase = ppListBase; m_ListSize = listSize; } const CPowerInfo* CCoreDispInfo::GetPowerInfo() const { return ::GetPowerInfo( GetPower() ); } CDispNeighbor* CCoreDispInfo::GetEdgeNeighbor( int index ) { return GetSurface()->GetEdgeNeighbor( index ); } CDispCornerNeighbors* CCoreDispInfo::GetCornerNeighbors( int index ) { return GetSurface()->GetCornerNeighbors( index ); } CDispUtilsHelper* CCoreDispInfo::GetDispUtilsByIndex( int index ) { Assert( m_ppListBase ); return index == 0xFFFF ? 0 : m_ppListBase[index]; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CCoreDispInfo::BuildTriTLtoBR( int ndx ) { // get width and height of displacement maps int nWidth = ( ( 1 << m_Power ) + 1 ); m_RenderIndices[m_RenderIndexCount] = ndx; m_RenderIndices[m_RenderIndexCount+1] = ndx + nWidth; m_RenderIndices[m_RenderIndexCount+2] = ndx + 1; m_RenderIndexCount += 3; m_RenderIndices[m_RenderIndexCount] = ndx + 1; m_RenderIndices[m_RenderIndexCount+1] = ndx + nWidth; m_RenderIndices[m_RenderIndexCount+2] = ndx + nWidth + 1; m_RenderIndexCount += 3; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CCoreDispInfo::BuildTriBLtoTR( int ndx ) { // get width and height of displacement maps int nWidth = ( ( 1 << m_Power ) + 1 ); m_RenderIndices[m_RenderIndexCount] = ndx; m_RenderIndices[m_RenderIndexCount+1] = ndx + nWidth; m_RenderIndices[m_RenderIndexCount+2] = ndx + nWidth + 1; m_RenderIndexCount += 3; m_RenderIndices[m_RenderIndexCount] = ndx; m_RenderIndices[m_RenderIndexCount+1] = ndx + nWidth + 1; m_RenderIndices[m_RenderIndexCount+2] = ndx + 1; m_RenderIndexCount += 3; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CCoreDispInfo::GenerateCollisionSurface( void ) { // get width and height of displacement maps int nWidth = ( ( 1 << m_Power ) + 1 ); int nHeight = ( ( 1 << m_Power ) + 1 ); // // generate a fan tesselated (at quadtree node) rendering index list // m_RenderIndexCount = 0; for ( int iV = 0; iV < ( nHeight - 1 ); iV++ ) { for ( int iU = 0; iU < ( nWidth - 1 ); iU++ ) { int ndx = ( iV * nWidth ) + iU; // test whether or not the index is odd bool bOdd = ( ( ndx %2 ) == 1 ); // Top Left to Bottom Right if( bOdd ) { BuildTriTLtoBR( ndx ); } // Bottom Left to Top Right else { BuildTriBLtoTR( ndx ); } } } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CCoreDispInfo::GenerateCollisionData( void ) { GenerateCollisionSurface(); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CCoreDispInfo::CalcTriSurfPlanes( int nodeIndex, int indices[8][3] ) { // // calculate plane info for each face // for( int i = 0; i < 8; i++ ) { Vector v[3]; VectorCopy( m_pVerts[indices[i][0]].m_Vert, v[0] ); VectorCopy( m_pVerts[indices[i][1]].m_Vert, v[1] ); VectorCopy( m_pVerts[indices[i][2]].m_Vert, v[2] ); Vector seg[2]; VectorSubtract( v[1], v[0], seg[0] ); VectorSubtract( v[2], v[0], seg[1] ); Vector normal; CrossProduct( seg[1], seg[0], normal ); VectorNormalize( normal ); float dist = DotProduct( v[0], normal ); m_Nodes[nodeIndex].SetTriPlane( i, normal, dist ); } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CCoreDispInfo::CalcRayBoundingBoxes( int nodeIndex, int indices[8][3] ) { Vector triMin, triMax; for( int i = 0; i < 4; i++ ) { triMin[0] = triMax[0] = m_pVerts[indices[(i*2)][0]].m_Vert[0]; triMin[1] = triMax[1] = m_pVerts[indices[(i*2)][0]].m_Vert[1]; triMin[2] = triMax[2] = m_pVerts[indices[(i*2)][0]].m_Vert[2]; for( int j = 0; j < 3; j++ ) { // // minimum // if( triMin[0] > m_pVerts[indices[(i*2)][j]].m_Vert[0] ) triMin[0] = m_pVerts[indices[(i*2)][j]].m_Vert[0]; if( triMin[0] > m_pVerts[indices[(i*2+1)][j]].m_Vert[0] ) triMin[0] = m_pVerts[indices[(i*2+1)][j]].m_Vert[0]; if( triMin[1] > m_pVerts[indices[(i*2)][j]].m_Vert[1] ) triMin[1] = m_pVerts[indices[(i*2)][j]].m_Vert[1]; if( triMin[1] > m_pVerts[indices[(i*2+1)][j]].m_Vert[1] ) triMin[1] = m_pVerts[indices[(i*2+1)][j]].m_Vert[1]; if( triMin[2] > m_pVerts[indices[(i*2)][j]].m_Vert[2] ) triMin[2] = m_pVerts[indices[(i*2)][j]].m_Vert[2]; if( triMin[2] > m_pVerts[indices[(i*2+1)][j]].m_Vert[2] ) triMin[2] = m_pVerts[indices[(i*2+1)][j]].m_Vert[2]; // // maximum // if( triMax[0] < m_pVerts[indices[(i*2)][j]].m_Vert[0] ) triMax[0] = m_pVerts[indices[(i*2)][j]].m_Vert[0]; if( triMax[0] < m_pVerts[indices[(i*2+1)][j]].m_Vert[0] ) triMax[0] = m_pVerts[indices[(i*2+1)][j]].m_Vert[0]; if( triMax[1] < m_pVerts[indices[(i*2)][j]].m_Vert[1] ) triMax[1] = m_pVerts[indices[(i*2)][j]].m_Vert[1]; if( triMax[1] < m_pVerts[indices[(i*2+1)][j]].m_Vert[1] ) triMax[1] = m_pVerts[indices[(i*2+1)][j]].m_Vert[1]; if( triMax[2] < m_pVerts[indices[(i*2)][j]].m_Vert[2] ) triMax[2] = m_pVerts[indices[(i*2)][j]].m_Vert[2]; if( triMax[2] < m_pVerts[indices[(i*2+1)][j]].m_Vert[2] ) triMax[2] = m_pVerts[indices[(i*2+1)][j]].m_Vert[2]; } m_Nodes[nodeIndex].SetRayBoundingBox( i, triMin, triMax ); } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CCoreDispInfo::CalcTriSurfBoundingBoxes( int nodeIndex, int indices[8][3] ) { Vector triMin, triMax; for( int i = 0; i < 8; i++ ) { m_Nodes[nodeIndex].GetTriBoundingBox( i, triMin, triMax ); for( int j = 0; j < 3; j++ ) { // // minimum // if( triMin[0] > m_pVerts[indices[i][j]].m_Vert[0] ) triMin[0] = m_pVerts[indices[i][j]].m_Vert[0]; if( triMin[1] > m_pVerts[indices[i][j]].m_Vert[1] ) triMin[1] = m_pVerts[indices[i][j]].m_Vert[1]; if( triMin[2] > m_pVerts[indices[i][j]].m_Vert[2] ) triMin[2] = m_pVerts[indices[i][j]].m_Vert[2]; // // maximum // if( triMax[0] < m_pVerts[indices[i][j]].m_Vert[0] ) triMax[0] = m_pVerts[indices[i][j]].m_Vert[0]; if( triMax[1] < m_pVerts[indices[i][j]].m_Vert[1] ) triMax[1] = m_pVerts[indices[i][j]].m_Vert[1]; if( triMax[2] < m_pVerts[indices[i][j]].m_Vert[2] ) triMax[2] = m_pVerts[indices[i][j]].m_Vert[2]; } m_Nodes[nodeIndex].SetTriBoundingBox( i, triMin, triMax ); } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CCoreDispInfo::CalcTriSurfIndices( int nodeIndex, int indices[8][3] ) { indices[0][0] = m_Nodes[nodeIndex].GetNeighborVertIndex( 4 ); indices[0][1] = m_Nodes[nodeIndex].GetNeighborVertIndex( 0 ); indices[0][2] = m_Nodes[nodeIndex].GetNeighborVertIndex( 3 ); indices[1][0] = m_Nodes[nodeIndex].GetNeighborVertIndex( 3 ); indices[1][1] = m_Nodes[nodeIndex].GetNeighborVertIndex( 0 ); indices[1][2] = m_Nodes[nodeIndex].GetCenterVertIndex(); indices[2][0] = m_Nodes[nodeIndex].GetNeighborVertIndex( 3 ); indices[2][1] = m_Nodes[nodeIndex].GetCenterVertIndex(); indices[2][2] = m_Nodes[nodeIndex].GetNeighborVertIndex( 5 ); indices[3][0] = m_Nodes[nodeIndex].GetNeighborVertIndex( 5 ); indices[3][1] = m_Nodes[nodeIndex].GetCenterVertIndex(); indices[3][2] = m_Nodes[nodeIndex].GetNeighborVertIndex( 2 ); indices[4][0] = m_Nodes[nodeIndex].GetNeighborVertIndex( 0 ); indices[4][1] = m_Nodes[nodeIndex].GetNeighborVertIndex( 6 ); indices[4][2] = m_Nodes[nodeIndex].GetCenterVertIndex(); indices[5][0] = m_Nodes[nodeIndex].GetCenterVertIndex(); indices[5][1] = m_Nodes[nodeIndex].GetNeighborVertIndex( 6 ); indices[5][2] = m_Nodes[nodeIndex].GetNeighborVertIndex( 1 ); indices[6][0] = m_Nodes[nodeIndex].GetCenterVertIndex(); indices[6][1] = m_Nodes[nodeIndex].GetNeighborVertIndex( 1 ); indices[6][2] = m_Nodes[nodeIndex].GetNeighborVertIndex( 2 ); indices[7][0] = m_Nodes[nodeIndex].GetNeighborVertIndex( 2 ); indices[7][1] = m_Nodes[nodeIndex].GetNeighborVertIndex( 1 ); indices[7][2] = m_Nodes[nodeIndex].GetNeighborVertIndex( 7 ); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CCoreDispInfo::CalcTriSurfInfoAtNode( int nodeIndex ) { int indices[8][3]; CalcTriSurfIndices( nodeIndex, indices ); CalcTriSurfBoundingBoxes( nodeIndex, indices ); CalcRayBoundingBoxes( nodeIndex, indices ); CalcTriSurfPlanes( nodeIndex, indices ); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CCoreDispInfo::CalcMinMaxBoundingBoxAtNode( int nodeIndex, Vector& bMin, Vector& bMax ) { // get the child node index int childNodeIndex = GetNodeChild( m_Power, nodeIndex, 4 ); // get initial bounding box values m_Nodes[childNodeIndex].GetBoundingBox( bMin, bMax ); Vector nodeMin, nodeMax; for( int i = 1, j = 5; i < 4; i++, j++ ) { // // get the child node bounding box // childNodeIndex = GetNodeChild( m_Power, nodeIndex, j ); m_Nodes[childNodeIndex].GetBoundingBox( nodeMin, nodeMax ); // minimum if( bMin[0] > nodeMin[0] ) bMin[0] = nodeMin[0]; if( bMin[1] > nodeMin[1] ) bMin[1] = nodeMin[1]; if( bMin[2] > nodeMin[2] ) bMin[2] = nodeMin[2]; // maximum if( bMax[0] < nodeMax[0] ) bMax[0] = nodeMax[0]; if( bMax[1] < nodeMax[1] ) bMax[1] = nodeMax[1]; if( bMax[2] < nodeMax[2] ) bMax[2] = nodeMax[2]; } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CCoreDispInfo::CalcBoundingBoxAtNode( int nodeIndex ) { Vector bMin, bMax; // // initialize the minimum and maximum values for the bounding box // int level = GetNodeLevel( nodeIndex ); int vertIndex = m_Nodes[nodeIndex].GetCenterVertIndex(); if( level == m_Power ) { VectorCopy( m_pVerts[vertIndex].m_Vert, bMin ); VectorCopy( m_pVerts[vertIndex].m_Vert, bMax ); } else { CalcMinMaxBoundingBoxAtNode( nodeIndex, bMin, bMax ); if( bMin[0] > m_pVerts[vertIndex].m_Vert[0] ) bMin[0] = m_pVerts[vertIndex].m_Vert[0]; if( bMin[1] > m_pVerts[vertIndex].m_Vert[1] ) bMin[1] = m_pVerts[vertIndex].m_Vert[1]; if( bMin[2] > m_pVerts[vertIndex].m_Vert[2] ) bMin[2] = m_pVerts[vertIndex].m_Vert[2]; if( bMax[0] < m_pVerts[vertIndex].m_Vert[0] ) bMax[0] = m_pVerts[vertIndex].m_Vert[0]; if( bMax[1] < m_pVerts[vertIndex].m_Vert[1] ) bMax[1] = m_pVerts[vertIndex].m_Vert[1]; if( bMax[2] < m_pVerts[vertIndex].m_Vert[2] ) bMax[2] = m_pVerts[vertIndex].m_Vert[2]; } for( int i = 0; i < 8; i++ ) { int neighborVertIndex = m_Nodes[nodeIndex].GetNeighborVertIndex( i ); // // minimum // if( bMin[0] > m_pVerts[neighborVertIndex].m_Vert[0] ) bMin[0] = m_pVerts[neighborVertIndex].m_Vert[0]; if( bMin[1] > m_pVerts[neighborVertIndex].m_Vert[1] ) bMin[1] = m_pVerts[neighborVertIndex].m_Vert[1]; if( bMin[2] > m_pVerts[neighborVertIndex].m_Vert[2] ) bMin[2] = m_pVerts[neighborVertIndex].m_Vert[2]; // // maximum // if( bMax[0] < m_pVerts[neighborVertIndex].m_Vert[0] ) bMax[0] = m_pVerts[neighborVertIndex].m_Vert[0]; if( bMax[1] < m_pVerts[neighborVertIndex].m_Vert[1] ) bMax[1] = m_pVerts[neighborVertIndex].m_Vert[1]; if( bMax[2] < m_pVerts[neighborVertIndex].m_Vert[2] ) bMax[2] = m_pVerts[neighborVertIndex].m_Vert[2]; } m_Nodes[nodeIndex].SetBoundingBox( bMin, bMax ); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- float CCoreDispInfo::GetMaxErrorFromChildren( int nodeIndex, int level ) { // // check for children nodes // if( level == m_Power ) return 0.0f; // // get the child's error term and save the greatest error -- SW, SE, NW, NE // float errorTerm = 0.0f; for( int i = 4; i < 8; i++ ) { int childIndex = GetNodeChild( m_Power, nodeIndex, i ); float nodeErrorTerm = m_Nodes[childIndex].GetErrorTerm(); if( errorTerm < nodeErrorTerm ) { errorTerm = nodeErrorTerm; } } return errorTerm; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CCoreDispInfo::CalcErrorTermAtNode( int nodeIndex, int level ) { if( level == m_Power ) return; // // get the vertex indices // int neighborVertIndices[9]; for( int i = 0; i < 8; i++ ) { neighborVertIndices[i] = m_Nodes[nodeIndex].GetNeighborVertIndex( i ); } neighborVertIndices[8] = m_Nodes[nodeIndex].GetCenterVertIndex(); // // calculate the error terms // Vector segment; Vector v; VectorAdd( m_pVerts[neighborVertIndices[5]].m_Vert, m_pVerts[neighborVertIndices[4]].m_Vert, v ); VectorScale( v, 0.5f, v ); VectorSubtract( m_pVerts[neighborVertIndices[0]].m_Vert, v, segment ); float errorTerm = ( float )VectorLength( segment ); VectorAdd( m_pVerts[neighborVertIndices[5]].m_Vert, m_pVerts[neighborVertIndices[6]].m_Vert, v ); VectorScale( v, 0.5f, v ); VectorSubtract( m_pVerts[neighborVertIndices[1]].m_Vert, v, segment ); if( errorTerm < ( float )VectorLength( segment ) ) errorTerm = ( float )VectorLength( segment ); VectorAdd( m_pVerts[neighborVertIndices[6]].m_Vert, m_pVerts[neighborVertIndices[7]].m_Vert, v ); VectorScale( v, 0.5f, v ); VectorSubtract( m_pVerts[neighborVertIndices[2]].m_Vert, v, segment ); if( errorTerm < ( float )VectorLength( segment ) ) errorTerm = ( float )VectorLength( segment ); VectorAdd( m_pVerts[neighborVertIndices[7]].m_Vert, m_pVerts[neighborVertIndices[4]].m_Vert, v ); VectorScale( v, 0.5f, v ); VectorSubtract( m_pVerts[neighborVertIndices[3]].m_Vert, v, segment ); if( errorTerm < ( float )VectorLength( segment ) ) errorTerm = ( float )VectorLength( segment ); VectorAdd( m_pVerts[neighborVertIndices[4]].m_Vert, m_pVerts[neighborVertIndices[6]].m_Vert, v ); VectorScale( v, 0.5f, v ); VectorSubtract( m_pVerts[neighborVertIndices[8]].m_Vert, v, segment ); if( errorTerm < ( float )VectorLength( segment ) ) errorTerm = ( float )VectorLength( segment ); VectorAdd( m_pVerts[neighborVertIndices[5]].m_Vert, m_pVerts[neighborVertIndices[7]].m_Vert, v ); VectorScale( v, 0.5f, v ); VectorSubtract( m_pVerts[neighborVertIndices[8]].m_Vert, v, segment ); if( errorTerm < ( float )VectorLength( segment ) ) errorTerm = ( float )VectorLength( segment ); // // add the max child's error term // errorTerm += GetMaxErrorFromChildren( nodeIndex, level ); // set the error term m_Nodes[nodeIndex].SetErrorTerm( errorTerm ); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CCoreDispInfo::CalcNeighborVertIndicesAtNode( int nodeIndex, int level ) { // calculate the shift in direction in the matrix int shift = ( 1 << ( m_Power - level ) ); // calculate the width, height of the displacement surface (are uniform) int extent = ( ( 1 << m_Power ) + 1 ); // // get the neighbor vertex indices (defining the surface at the node level) // for( int direction = 0; direction < 8; direction++ ) { // // get the parent vertex index in component form // int posX = m_Nodes[nodeIndex].GetCenterVertIndex() % extent; int posY = m_Nodes[nodeIndex].GetCenterVertIndex() / extent; // // calculate the neighboring vertex indices for surface rendering // bool bError = false; switch( direction ) { case WEST: { posX -= shift; break; } case NORTH: { posY += shift; break; } case EAST: { posX += shift; break; } case SOUTH: { posY -= shift; break; } case SOUTHWEST: { posX -= shift; posY -= shift; break; } case SOUTHEAST: { posX += shift; posY -= shift; break; } case NORTHWEST: { posX -= shift; posY += shift; break; } case NORTHEAST: { posX += shift; posY += shift; break; } default: { bError = true; break; } } if( bError ) { m_Nodes[nodeIndex].SetNeighborVertIndex( direction, -99999 ); } else { m_Nodes[nodeIndex].SetNeighborVertIndex( direction, ( ( posY * extent ) + posX ) ); } } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CCoreDispInfo::CalcNodeInfo( int nodeIndex, int terminationLevel ) { // get the level of the current node int level = GetNodeLevel( nodeIndex ); // // get the node data at the termination level // if( level == terminationLevel ) { // get the neighbor vertex indices (used to create surface at node level) CalcNeighborVertIndicesAtNode( nodeIndex, level ); // get the neighbor node indices //CalcNeighborNodeIndicesAtNode( nodeIndex, level ); // calculate the error term at the node CalcErrorTermAtNode( nodeIndex, level ); // calcluate the axial-aligned bounding box at the node CalcBoundingBoxAtNode( nodeIndex ); // calculate the triangular surface info at the node CalcTriSurfInfoAtNode( nodeIndex ); return; } // // continue recursion (down to nodes "children") // for( int i = 4; i < 8; i++ ) { int childIndex = GetNodeChild( m_Power, nodeIndex, i ); CalcNodeInfo( childIndex, terminationLevel ); } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- int CCoreDispInfo::GetNodeVertIndexFromParentIndex( int level, int parentVertIndex, int direction ) { // calculate the "shift" int shift = ( 1 << ( m_Power - ( level + 1 ) ) ); // calculate the width and height of displacement (is uniform) int extent = ( ( 1 << m_Power ) + 1 ); // get the parent vertex index in component form int posX = parentVertIndex % extent; int posY = parentVertIndex / extent; // // calculate the child index based on the parent index and child // direction // switch( direction ) { case SOUTHWEST: { posX -= shift; posY -= shift; break; } case SOUTHEAST: { posX += shift; posY -= shift; break; } case NORTHWEST: { posX -= shift; posY += shift; break; } case NORTHEAST: { posX += shift; posY += shift; break; } default: return -99999; } // return the child vertex index return ( ( posY * extent ) + posX ); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CCoreDispInfo::CalcVertIndicesAtNodes( int nodeIndex ) { // // check for recursion termination ( node level = power ) // int level = GetNodeLevel( nodeIndex ); if( level == m_Power ) return; // // get the children indices - SW, SE, NW, NE // int childIndices[4]; int i, j; for( i = 0, j = 4; i < 4; i++, j++ ) { childIndices[i] = GetNodeChild( m_Power, nodeIndex, j ); int centerIndex = GetNodeVertIndexFromParentIndex( level, m_Nodes[nodeIndex].GetCenterVertIndex(), j ); m_Nodes[childIndices[i]].SetCenterVertIndex( centerIndex ); } // // calculate the children's node vertex indices // for( i = 0; i < 4; i++ ) { CalcVertIndicesAtNodes( childIndices[i] ); } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CCoreDispInfo::GenerateLODTree( void ) { // // calculate the displacement surface's vertex index at each quad-tree node // centroid // int size = GetSize(); int initialIndex = ( ( size - 1 ) >> 1 ); m_Nodes[0].SetCenterVertIndex( initialIndex ); CalcVertIndicesAtNodes( 0 ); // // calculate the error terms, bounding boxes, and neighboring vertex indices // at each node // for( int i = m_Power; i > 0; i-- ) { CalcNodeInfo( 0, i ); } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CCoreDispInfo::CalcDispSurfCoords( bool bLightMap, int lightmapID ) { // // get base surface texture coords // Vector2D texCoords[4]; Vector2D luxelCoords[4]; CCoreDispSurface *pSurf = GetSurface(); int i; for( i = 0; i < 4; i++ ) { pSurf->GetTexCoord( i, texCoords[i] ); pSurf->GetLuxelCoord( lightmapID, i, luxelCoords[i] ); } // // get images width and intervals along the edge // int postSpacing = GetPostSpacing(); float ooInt = ( 1.0f / ( float )( postSpacing - 1 ) ); // // calculate the parallel edge intervals // Vector2D edgeInt[2]; if( !bLightMap ) { Vector2DSubtract( texCoords[1], texCoords[0], edgeInt[0] ); Vector2DSubtract( texCoords[2], texCoords[3], edgeInt[1] ); } else { Vector2DSubtract( luxelCoords[1], luxelCoords[0], edgeInt[0] ); Vector2DSubtract( luxelCoords[2], luxelCoords[3], edgeInt[1] ); } Vector2DMultiply( edgeInt[0], ooInt, edgeInt[0] ); Vector2DMultiply( edgeInt[1], ooInt, edgeInt[1] ); // // calculate the displacement points // for( i = 0; i < postSpacing; i++ ) { // // position along parallel edges (start and end for a perpendicular segment) // Vector2D endPts[2]; Vector2DMultiply( edgeInt[0], ( float )i, endPts[0] ); Vector2DMultiply( edgeInt[1], ( float )i, endPts[1] ); if( !bLightMap ) { Vector2DAdd( endPts[0], texCoords[0], endPts[0] ); Vector2DAdd( endPts[1], texCoords[3], endPts[1] ); } else { Vector2DAdd( endPts[0], luxelCoords[0], endPts[0] ); Vector2DAdd( endPts[1], luxelCoords[3], endPts[1] ); } // // interval length for perpendicular edge // Vector2D seg, segInt; Vector2DSubtract( endPts[1], endPts[0], seg ); Vector2DMultiply( seg, ooInt, segInt ); // // calculate the material (texture or light) coordinate at each point // for( int j = 0; j < postSpacing; j++ ) { Vector2DMultiply( segInt, ( float )j, seg ); if( !bLightMap ) { Vector2DAdd( endPts[0], seg, m_pVerts[i*postSpacing+j].m_TexCoord ); } else { Vector2DAdd( endPts[0], seg, m_pVerts[i*postSpacing+j].m_LuxelCoords[lightmapID] ); } } } } #if 0 //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CCoreDispInfo::CalcDispSurfAlphas( void ) { // // get images width and intervals along the edge // int postSpacing = GetPostSpacing(); float ooInt = ( 1.0f / ( float )( postSpacing - 1 ) ); // // calculate the parallel edge intervals // float edgeInt[2]; edgeInt[0] = m_Surf.m_Alpha[1] - m_Surf.m_Alpha[0]; edgeInt[1] = m_Surf.m_Alpha[2] - m_Surf.m_Alpha[3]; edgeInt[0] *= ooInt; edgeInt[1] *= ooInt; // // calculate the displacement points // for( int i = 0; i < postSpacing; i++ ) { // // position along parallel edges (start and end for a perpendicular segment) // float endValues[2]; endValues[0] = edgeInt[0] * ( float )i; endValues[1] = edgeInt[1] * ( float )i; endValues[0] += m_Surf.m_Alpha[0]; endValues[1] += m_Surf.m_Alpha[3]; // // interval length for perpendicular edge // float seg, segInt; seg = endValues[1] - endValues[0]; segInt = seg * ooInt; // // calculate the alpha value at each point // for( int j = 0; j < postSpacing; j++ ) { seg = segInt * ( float )j; m_Alphas[i*postSpacing+j] = endValues[0] + seg; } } } #endif //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CCoreDispInfo::GenerateDispSurfTangentSpaces( void ) { // // get texture axes from base surface // CCoreDispSurface *pSurf = GetSurface(); Vector sAxis, tAxis; pSurf->GetSAxis( sAxis ); pSurf->GetTAxis( tAxis ); // // calculate the tangent spaces // int size = GetSize(); for( int i = 0; i < size; i++ ) { // // create the axes - normals, tangents, and binormals // VectorCopy( tAxis, m_pVerts[i].m_TangentT ); VectorNormalize( m_pVerts[i].m_TangentT ); CrossProduct( m_pVerts[i].m_Normal, m_pVerts[i].m_TangentT, m_pVerts[i].m_TangentS ); VectorNormalize( m_pVerts[i].m_TangentS ); CrossProduct( m_pVerts[i].m_TangentS, m_pVerts[i].m_Normal, m_pVerts[i].m_TangentT ); VectorNormalize( m_pVerts[i].m_TangentT ); Vector tmpVect; Vector planeNormal; pSurf->GetNormal( planeNormal ); CrossProduct( sAxis, tAxis, tmpVect ); if( DotProduct( planeNormal, tmpVect ) > 0.0f ) { VectorScale( m_pVerts[i].m_TangentS, -1.0f, m_pVerts[i].m_TangentS ); } } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CCoreDispInfo::CalcNormalFromEdges( int indexRow, int indexCol, bool bIsEdge[4], Vector& normal ) { // get the post spacing (size/interval of displacement surface) int postSpacing = ( ( 1 << m_Power ) + 1 ); // initialize the normal accumulator - counter Vector accumNormal; int normalCount = 0; VectorClear( accumNormal ); Vector tmpVect[2]; Vector tmpNormal; // // check quadrant I (posX, posY) // if( bIsEdge[1] && bIsEdge[2] ) { // tri i VectorSubtract( m_pVerts[(indexCol+1)*postSpacing+indexRow].m_Vert, m_pVerts[indexCol*postSpacing+indexRow].m_Vert, tmpVect[0] ); VectorSubtract( m_pVerts[indexCol*postSpacing+(indexRow+1)].m_Vert, m_pVerts[indexCol*postSpacing+indexRow].m_Vert, tmpVect[1] ); CrossProduct( tmpVect[1], tmpVect[0], tmpNormal ); VectorNormalize( tmpNormal ); VectorAdd( accumNormal, tmpNormal, accumNormal ); normalCount++; // tri 2 VectorSubtract( m_pVerts[(indexCol+1)*postSpacing+indexRow].m_Vert, m_pVerts[indexCol*postSpacing+(indexRow+1)].m_Vert, tmpVect[0] ); VectorSubtract( m_pVerts[(indexCol+1)*postSpacing+(indexRow+1)].m_Vert, m_pVerts[indexCol*postSpacing+(indexRow+1)].m_Vert, tmpVect[1] ); CrossProduct( tmpVect[1], tmpVect[0], tmpNormal ); VectorNormalize( tmpNormal ); VectorAdd( accumNormal, tmpNormal, accumNormal ); normalCount++; } // // check quadrant II (negX, posY) // if( bIsEdge[0] && bIsEdge[1] ) { // tri i VectorSubtract( m_pVerts[(indexCol+1)*postSpacing+(indexRow-1)].m_Vert, m_pVerts[indexCol*postSpacing+(indexRow-1)].m_Vert, tmpVect[0] ); VectorSubtract( m_pVerts[indexCol*postSpacing+indexRow].m_Vert, m_pVerts[indexCol*postSpacing+(indexRow-1)].m_Vert, tmpVect[1] ); CrossProduct( tmpVect[1], tmpVect[0], tmpNormal ); VectorNormalize( tmpNormal ); VectorAdd( accumNormal, tmpNormal, accumNormal ); normalCount++; // tri 2 VectorSubtract( m_pVerts[(indexCol+1)*postSpacing+(indexRow-1)].m_Vert, m_pVerts[indexCol*postSpacing+indexRow].m_Vert, tmpVect[0] ); VectorSubtract( m_pVerts[(indexCol+1)*postSpacing+indexRow].m_Vert, m_pVerts[indexCol*postSpacing+indexRow].m_Vert, tmpVect[1] ); CrossProduct( tmpVect[1], tmpVect[0], tmpNormal ); VectorNormalize( tmpNormal ); VectorAdd( accumNormal, tmpNormal, accumNormal ); normalCount++; } // // check quadrant III (negX, negY) // if( bIsEdge[0] && bIsEdge[3] ) { // tri i VectorSubtract( m_pVerts[indexCol*postSpacing+(indexRow-1)].m_Vert, m_pVerts[(indexCol-1)*postSpacing+(indexRow-1)].m_Vert, tmpVect[0] ); VectorSubtract( m_pVerts[(indexCol-1)*postSpacing+indexRow].m_Vert, m_pVerts[(indexCol-1)*postSpacing+(indexRow-1)].m_Vert, tmpVect[1] ); CrossProduct( tmpVect[1], tmpVect[0], tmpNormal ); VectorNormalize( tmpNormal ); VectorAdd( accumNormal, tmpNormal, accumNormal ); normalCount++; // tri 2 VectorSubtract( m_pVerts[indexCol*postSpacing+(indexRow-1)].m_Vert, m_pVerts[(indexCol-1)*postSpacing+indexRow].m_Vert, tmpVect[0] ); VectorSubtract( m_pVerts[indexCol*postSpacing+indexRow].m_Vert, m_pVerts[(indexCol-1)*postSpacing+indexRow].m_Vert, tmpVect[1] ); CrossProduct( tmpVect[1], tmpVect[0], tmpNormal ); VectorNormalize( tmpNormal ); VectorAdd( accumNormal, tmpNormal, accumNormal ); normalCount++; } // // check quadrant IV (posX, negY) // if( bIsEdge[2] && bIsEdge[3] ) { // tri i VectorSubtract( m_pVerts[indexCol*postSpacing+indexRow].m_Vert, m_pVerts[(indexCol-1)*postSpacing+indexRow].m_Vert, tmpVect[0] ); VectorSubtract( m_pVerts[(indexCol-1)*postSpacing+(indexRow+1)].m_Vert, m_pVerts[(indexCol-1)*postSpacing+indexRow].m_Vert, tmpVect[1] ); CrossProduct( tmpVect[1], tmpVect[0], tmpNormal ); VectorNormalize( tmpNormal ); VectorAdd( accumNormal, tmpNormal, accumNormal ); normalCount++; // tri 2 VectorSubtract( m_pVerts[indexCol*postSpacing+indexRow].m_Vert, m_pVerts[(indexCol-1)*postSpacing+(indexRow+1)].m_Vert, tmpVect[0] ); VectorSubtract( m_pVerts[indexCol*postSpacing+(indexRow+1)].m_Vert, m_pVerts[(indexCol-1)*postSpacing+(indexRow+1)].m_Vert, tmpVect[1] ); CrossProduct( tmpVect[1], tmpVect[0], tmpNormal ); VectorNormalize( tmpNormal ); VectorAdd( accumNormal, tmpNormal, accumNormal ); normalCount++; } VectorScale( accumNormal, ( 1.0f / ( float )normalCount ), normal ); } //----------------------------------------------------------------------------- // Purpose: This function determines if edges exist in each of the directions // off of the given point (given in component form). We know ahead of // time that there are only 4 possibilities. // // 1 "directions" // 0 + 2 // 3 // // Input: indexRow - row position // indexCol - col position // direction - the direction (edge) currently being evaluated // postSpacing - the number of intervals in the row and col directions // Output: the edge existed? (true/false) //----------------------------------------------------------------------------- bool CCoreDispInfo::DoesEdgeExist( int indexRow, int indexCol, int direction, int postSpacing ) { switch( direction ) { case 0: // left edge if( ( indexRow - 1 ) < 0 ) return false; return true; case 1: // top edge if( ( indexCol + 1 ) > ( postSpacing - 1 ) ) return false; return true; case 2: // right edge if( ( indexRow + 1 ) > ( postSpacing - 1 ) ) return false; return true; case 3: // bottom edge if( ( indexCol - 1 ) < 0 ) return false; return true; default: return false; } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CCoreDispInfo::GenerateDispSurfNormals( void ) { // get the post spacing (size/interval of displacement surface) int postSpacing = GetPostSpacing(); // // generate the normals at each displacement surface vertex // for( int i = 0; i < postSpacing; i++ ) { for( int j = 0; j < postSpacing; j++ ) { bool bIsEdge[4]; // edges for( int k = 0; k < 4; k++ ) { bIsEdge[k] = DoesEdgeExist( j, i, k, postSpacing ); } Vector normal; CalcNormalFromEdges( j, i, bIsEdge, normal ); // save generated normal VectorCopy( normal, m_pVerts[i*postSpacing+j].m_Normal ); } } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CCoreDispInfo::GenerateDispSurf( void ) { int i; CCoreDispSurface *pSurf = GetSurface(); Vector points[4]; for( i = 0; i < 4; i++ ) { pSurf->GetPoint( i, points[i] ); } // // get the spacing (interval = width/height, are equal because it is uniform) along the edge // int postSpacing = GetPostSpacing(); float ooInt = 1.0f / ( float )( postSpacing - 1 ); // // calculate the opposite edge intervals // Vector edgeInt[2]; VectorSubtract( points[1], points[0], edgeInt[0] ); VectorScale( edgeInt[0], ooInt, edgeInt[0] ); VectorSubtract( points[2], points[3], edgeInt[1] ); VectorScale( edgeInt[1], ooInt, edgeInt[1] ); Vector elevNormal; elevNormal.Init(); if( m_Elevation != 0.0f ) { pSurf->GetNormal( elevNormal ); VectorScale( elevNormal, m_Elevation, elevNormal ); } // // calculate the displaced vertices // for( i = 0; i < postSpacing; i++ ) { // // calculate segment interval between opposite edges // Vector endPts[2]; VectorScale( edgeInt[0], ( float )i, endPts[0] ); VectorAdd( endPts[0], points[0], endPts[0] ); VectorScale( edgeInt[1], ( float )i, endPts[1] ); VectorAdd( endPts[1], points[3], endPts[1] ); Vector seg, segInt; VectorSubtract( endPts[1], endPts[0], seg ); VectorScale( seg, ooInt, segInt ); // // calculate the surface vertices // for( int j = 0; j < postSpacing; j++ ) { int ndx = i * postSpacing + j; CoreDispVert_t *pVert = &m_pVerts[ndx]; // calculate the flat surface position -- saved separately pVert->m_FlatVert = endPts[0] + ( segInt * ( float )j ); // start with the base surface position pVert->m_Vert = pVert->m_FlatVert; // add the elevation vector -- if it exists if( m_Elevation != 0.0f ) { pVert->m_Vert += elevNormal; } // add the subdivision surface position pVert->m_Vert += pVert->m_SubdivPos; // add the displacement field direction(normalized) and distance pVert->m_Vert += pVert->m_FieldVector * pVert->m_FieldDistance; } } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- //bool CCoreDispInfo::Create( int creationFlags ) bool CCoreDispInfo::Create( void ) { // sanity check CCoreDispSurface *pSurf = GetSurface(); if( pSurf->GetPointCount() != 4 ) return false; // generate the displacement surface GenerateDispSurf(); GenerateDispSurfNormals(); GenerateDispSurfTangentSpaces(); CalcDispSurfCoords( false, 0 ); for( int bumpID = 0; bumpID < ( NUM_BUMP_VECTS + 1 ); bumpID++ ) { CalcDispSurfCoords( true, bumpID ); } GenerateLODTree(); GenerateCollisionData(); CreateTris(); return true; } //----------------------------------------------------------------------------- // Purpose: Create a displacement surface without generating the LOD for it. //----------------------------------------------------------------------------- bool CCoreDispInfo::CreateWithoutLOD( void ) { // sanity check CCoreDispSurface *pSurf = GetSurface(); if( pSurf->GetPointCount() != 4 ) return false; GenerateDispSurf(); GenerateDispSurfNormals(); GenerateDispSurfTangentSpaces(); CalcDispSurfCoords( false, 0 ); for( int bumpID = 0; bumpID < ( NUM_BUMP_VECTS + 1 ); bumpID++ ) { CalcDispSurfCoords( true, bumpID ); } GenerateCollisionData(); CreateTris(); return true; } //----------------------------------------------------------------------------- // Purpose: This function calculates the neighbor node index given the base // node and direction of the neighbor node in the tree. // Input: power - the size in one dimension of the displacement map (2^power + 1 ) // index - the "base" node index // direction - the direction of the neighbor { W = 1, N = 2, E = 3, S = 4 } // Output: returns the index of the neighbor node //----------------------------------------------------------------------------- int GetNodeNeighborNode( int power, int index, int direction, int level ) { // adjust the index to range [0...?] int minNodeIndex = GetNodeMinNodeAtLevel( level ); // get node extent (uniform: height = width) int nodeExtent = ( 1 << ( level - 1 ) ); // // get node's component positions in quad-tree // int posX, posY; GetComponentsFromNodeIndex( ( index - minNodeIndex ), &posX, &posY ); // // find the neighbor in the "direction" // switch( direction ) { case CCoreDispInfo::WEST: { if( ( posX - 1 ) < 0 ) { return -( CCoreDispInfo::WEST + 1 ); } else { return ( GetNodeIndexFromComponents( ( posX - 1 ), posY ) + minNodeIndex ); } } case CCoreDispInfo::NORTH: { if( ( posY + 1 ) == nodeExtent ) { return -( CCoreDispInfo::NORTH + 1 ); } else { return ( GetNodeIndexFromComponents( posX, ( posY + 1 ) ) + minNodeIndex ); } } case CCoreDispInfo::EAST: { if( ( posX + 1 ) == nodeExtent ) { return -( CCoreDispInfo::EAST + 1 ); } else { return ( GetNodeIndexFromComponents( ( posX + 1 ), posY ) + minNodeIndex ); } } case CCoreDispInfo::SOUTH: { if( ( posY - 1 ) < 0 ) { return -( CCoreDispInfo::SOUTH + 1 ); } else { return ( GetNodeIndexFromComponents( posX, ( posY - 1 ) ) + minNodeIndex ); } } default: { return -99999; } } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- int GetNodeNeighborNodeFromNeighborSurf( int power, int index, int direction, int level, int neighborOrient ) { // adjust the index to range [0...?] int minNodeIndex = GetNodeMinNodeAtLevel( level ); // get node extent (uniform: height = width) int nodeExtent = ( 1 << ( level - 1 ) ); // // get node's component positions in quad-tree // int posX, posY; GetComponentsFromNodeIndex( ( index - minNodeIndex ), &posX, &posY ); switch( direction ) { case CCoreDispInfo::WEST: { switch( neighborOrient ) { case CCoreDispInfo::WEST: return -( ( GetNodeIndexFromComponents( posX, ( ( nodeExtent - 1 ) - posY ) ) ) + minNodeIndex ); case CCoreDispInfo::NORTH: return -( ( GetNodeIndexFromComponents( ( nodeExtent - 1 ) - posY, ( nodeExtent - 1 ) ) ) + minNodeIndex ); case CCoreDispInfo::EAST: return -( ( GetNodeIndexFromComponents( ( nodeExtent - 1 ), posY ) ) + minNodeIndex ); case CCoreDispInfo::SOUTH: return -( ( GetNodeIndexFromComponents( posY, posX ) ) + minNodeIndex ); default: return -99999; } } case CCoreDispInfo::NORTH: { switch( neighborOrient ) { case CCoreDispInfo::WEST: return -( ( GetNodeIndexFromComponents( ( ( nodeExtent - 1 ) - posY ), ( ( nodeExtent - 1 ) - posX ) ) ) + minNodeIndex ); case CCoreDispInfo::NORTH: return -( ( GetNodeIndexFromComponents( ( ( nodeExtent - 1 ) - posX ), posY ) ) + minNodeIndex ); case CCoreDispInfo::EAST: return -( ( GetNodeIndexFromComponents( posY, posX ) ) + minNodeIndex ); case CCoreDispInfo::SOUTH: return -( ( GetNodeIndexFromComponents( posX, ( ( nodeExtent - 1 ) - posY ) ) ) + minNodeIndex ); default: return -99999; } } case CCoreDispInfo::EAST: { switch( neighborOrient ) { case CCoreDispInfo::WEST: return -( ( GetNodeIndexFromComponents( ( ( nodeExtent - 1 ) - posX ), posY ) ) + minNodeIndex ); case CCoreDispInfo::NORTH: return -( ( GetNodeIndexFromComponents( posY, posX ) ) + minNodeIndex ); case CCoreDispInfo::EAST: return -( ( GetNodeIndexFromComponents( posX, ( ( nodeExtent - 1 ) - posY ) ) ) + minNodeIndex ); case CCoreDispInfo::SOUTH: return -( ( GetNodeIndexFromComponents( ( ( nodeExtent - 1 ) - posY ), ( ( nodeExtent - 1 ) - posX ) ) ) + minNodeIndex ); default: return -99999; } } case CCoreDispInfo::SOUTH: { switch( neighborOrient ) { case CCoreDispInfo::WEST: return -( ( GetNodeIndexFromComponents( posY, posX ) ) + minNodeIndex ); case CCoreDispInfo::NORTH: return -( ( GetNodeIndexFromComponents( posX, ( nodeExtent - 1 ) ) ) + minNodeIndex ); case CCoreDispInfo::EAST: return -( ( GetNodeIndexFromComponents( ( nodeExtent - 1 ), ( ( nodeExtent - 1 ) - posX ) ) ) + minNodeIndex ); case CCoreDispInfo::SOUTH: return -( ( GetNodeIndexFromComponents( ( ( nodeExtent - 1 ) - posX ), posY ) ) + minNodeIndex ); default: return -99999; } } default: { return -99999; } } } // Turn the optimizer back on #pragma optimize( "", on ) //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CCoreDispInfo::GetPositionOnSurface( float u, float v, Vector &vPos, Vector *pNormal, float *pAlpha ) { Vector2D dispUV( u, v ); DispUVToSurf( dispUV, vPos, pNormal, pAlpha ); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CCoreDispInfo::BaseFacePlaneToDispUV( Vector const &planePt, Vector2D &dispUV ) { // Get the base surface points. CCoreDispSurface *pSurf = GetSurface(); Vector vecPoints[4]; for( int iPoint = 0; iPoint < 4; ++iPoint ) { pSurf->GetPoint( iPoint, vecPoints[iPoint] ); } PointInQuadToBarycentric( vecPoints[0], vecPoints[3], vecPoints[2], vecPoints[1], planePt, dispUV ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCoreDispInfo::DispUVToSurf_TriTLToBR_1( const Vector &vecIntersectPoint, int nSnapU, int nNextU, int nSnapV, int nNextV, Vector &vecPoint, Vector *pNormal, float *pAlpha, bool bBackup ) { int nWidth = GetWidth(); int nIndices[3]; nIndices[0] = nNextV * nWidth + nSnapU; nIndices[1] = nNextV * nWidth + nNextU; nIndices[2] = nSnapV * nWidth + nNextU; Vector vecFlatVerts[3], vecVerts[3]; float flAlphas[3]; for ( int iVert = 0; iVert < 3; ++iVert ) { vecFlatVerts[iVert] = m_pVerts[nIndices[iVert]].m_FlatVert; vecVerts[iVert] = m_pVerts[nIndices[iVert]].m_Vert; flAlphas[iVert] = m_pVerts[nIndices[iVert]].m_Alpha; } if ( nSnapU == nNextU ) { if ( nSnapV == nNextV ) { vecPoint = vecVerts[0]; *pAlpha = flAlphas[0]; } else { float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[2] - vecFlatVerts[0] ).Length(); vecPoint = vecVerts[0] + ( flFrac * ( vecVerts[2] - vecVerts[0] ) ); if ( pAlpha ) { *pAlpha = flAlphas[0] + ( flFrac * ( flAlphas[2] - flAlphas[0] ) ); } } if( pNormal ) { Vector edgeU = vecVerts[0] - vecVerts[1]; Vector edgeV = vecVerts[2] - vecVerts[1]; *pNormal = CrossProduct( edgeU, edgeV ); VectorNormalize( *pNormal ); } } else if ( nSnapV == nNextV ) { if ( nSnapU == nNextU ) { vecPoint = vecVerts[0]; *pAlpha = flAlphas[0]; } else { float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[2] - vecFlatVerts[0] ).Length(); vecPoint = vecVerts[0] + ( flFrac * ( vecVerts[2] - vecVerts[0] ) ); if ( pAlpha ) { *pAlpha = flAlphas[0] + ( flFrac * ( flAlphas[2] - flAlphas[0] ) ); } } if( pNormal ) { Vector edgeU = vecVerts[0] - vecVerts[1]; Vector edgeV = vecVerts[2] - vecVerts[1]; *pNormal = CrossProduct( edgeU, edgeV ); VectorNormalize( *pNormal ); } } else { float flCfs[3]; if ( CalcBarycentricCooefs( vecFlatVerts[0], vecFlatVerts[1], vecFlatVerts[2], vecIntersectPoint, flCfs[0], flCfs[1], flCfs[2] ) ) { vecPoint = ( vecVerts[0] * flCfs[0] ) + ( vecVerts[1] * flCfs[1] ) + ( vecVerts[2] * flCfs[2] ); if( pAlpha ) { *pAlpha = ( flAlphas[0] * flCfs[0] ) + ( flAlphas[1] * flCfs[1] ) + ( flAlphas[2] * flCfs[2] ); } if( pNormal ) { Vector edgeU = vecVerts[0] - vecVerts[1]; Vector edgeV = vecVerts[2] - vecVerts[1]; *pNormal = CrossProduct( edgeU, edgeV ); VectorNormalize( *pNormal ); } } else { if ( !bBackup ) { DispUVToSurf_TriTLToBR_2( vecIntersectPoint, nSnapU, nNextU, nSnapV, nNextV, vecPoint, pNormal, pAlpha, true ); } } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCoreDispInfo::DispUVToSurf_TriTLToBR_2( const Vector &vecIntersectPoint, int nSnapU, int nNextU, int nSnapV, int nNextV, Vector &vecPoint, Vector *pNormal, float *pAlpha, bool bBackup ) { int nWidth = GetWidth(); int nIndices[3]; nIndices[0] = nSnapV * nWidth + nSnapU; nIndices[1] = nNextV * nWidth + nSnapU; nIndices[2] = nSnapV * nWidth + nNextU; Vector vecFlatVerts[3], vecVerts[3]; float flAlphas[3]; for ( int iVert = 0; iVert < 3; ++iVert ) { vecFlatVerts[iVert] = m_pVerts[nIndices[iVert]].m_FlatVert; vecVerts[iVert] = m_pVerts[nIndices[iVert]].m_Vert; flAlphas[iVert] = m_pVerts[nIndices[iVert]].m_Alpha; } if ( nSnapU == nNextU ) { if ( nSnapV == nNextV ) { vecPoint = vecVerts[0]; *pAlpha = flAlphas[0]; } else { float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[1] - vecFlatVerts[0] ).Length(); vecPoint = vecVerts[0] + ( flFrac * ( vecVerts[1] - vecVerts[0] ) ); if ( pAlpha ) { *pAlpha = flAlphas[0] + ( flFrac * ( flAlphas[1] - flAlphas[0] ) ); } } if( pNormal ) { Vector edgeU = vecVerts[2] - vecVerts[0]; Vector edgeV = vecVerts[1] - vecVerts[0]; *pNormal = CrossProduct( edgeU, edgeV ); VectorNormalize( *pNormal ); } } else if ( nSnapV == nNextV ) { if ( nSnapU == nNextU ) { vecPoint = vecVerts[0]; *pAlpha = flAlphas[0]; } else { float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[2] - vecFlatVerts[0] ).Length(); vecPoint = vecVerts[0] + ( flFrac * ( vecVerts[2] - vecVerts[0] ) ); if ( pAlpha ) { *pAlpha = flAlphas[0] + ( flFrac * ( flAlphas[2] - flAlphas[0] ) ); } } if( pNormal ) { Vector edgeU = vecVerts[2] - vecVerts[0]; Vector edgeV = vecVerts[1] - vecVerts[0]; *pNormal = CrossProduct( edgeU, edgeV ); VectorNormalize( *pNormal ); } } else { float flCfs[3]; if ( CalcBarycentricCooefs( vecFlatVerts[0], vecFlatVerts[1], vecFlatVerts[2], vecIntersectPoint, flCfs[0], flCfs[1], flCfs[2] ) ) { vecPoint = ( vecVerts[0] * flCfs[0] ) + ( vecVerts[1] * flCfs[1] ) + ( vecVerts[2] * flCfs[2] ); if( pAlpha ) { *pAlpha = ( flAlphas[0] * flCfs[0] ) + ( flAlphas[1] * flCfs[1] ) + ( flAlphas[2] * flCfs[2] ); } if( pNormal ) { Vector edgeU = vecVerts[2] - vecVerts[0]; Vector edgeV = vecVerts[1] - vecVerts[0]; *pNormal = CrossProduct( edgeU, edgeV ); VectorNormalize( *pNormal ); } } else { if ( !bBackup ) { DispUVToSurf_TriTLToBR_1( vecIntersectPoint, nSnapU, nNextU, nSnapV, nNextV, vecPoint, pNormal, pAlpha, true ); } } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCoreDispInfo::DispUVToSurf_TriTLToBR( Vector &vecPoint, Vector *pNormal, float *pAlpha, float flU, float flV, const Vector &vecIntersectPoint ) { const float TRIEDGE_EPSILON = 0.00001f; int nWidth = GetWidth(); int nHeight = GetHeight(); int nSnapU = static_cast( flU ); int nSnapV = static_cast( flV ); int nNextU = nSnapU + 1; int nNextV = nSnapV + 1; if ( nNextU == nWidth) { --nNextU; } if ( nNextV == nHeight ) { --nNextV; } float flFracU = flU - static_cast( nSnapU ); float flFracV = flV - static_cast( nSnapV ); if ( ( flFracU + flFracV ) >= ( 1.0f + TRIEDGE_EPSILON ) ) { DispUVToSurf_TriTLToBR_1( vecIntersectPoint, nSnapU, nNextU, nSnapV, nNextV, vecPoint, pNormal, pAlpha, false ); } else { DispUVToSurf_TriTLToBR_2( vecIntersectPoint, nSnapU, nNextU, nSnapV, nNextV, vecPoint, pNormal, pAlpha, false ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCoreDispInfo::DispUVToSurf_TriBLToTR_1( const Vector &vecIntersectPoint, int nSnapU, int nNextU, int nSnapV, int nNextV, Vector &vecPoint, Vector *pNormal, float *pAlpha, bool bBackup ) { int nWidth = GetWidth(); int nIndices[3]; nIndices[0] = nSnapV * nWidth + nSnapU; nIndices[1] = nNextV * nWidth + nSnapU; nIndices[2] = nNextV * nWidth + nNextU; Vector vecFlatVerts[3], vecVerts[3]; float flAlphas[3]; for ( int iVert = 0; iVert < 3; ++iVert ) { vecFlatVerts[iVert] = m_pVerts[nIndices[iVert]].m_FlatVert; vecVerts[iVert] = m_pVerts[nIndices[iVert]].m_Vert; flAlphas[iVert] = m_pVerts[nIndices[iVert]].m_Alpha; } if ( nSnapU == nNextU ) { if ( nSnapV == nNextV ) { vecPoint = vecVerts[0]; *pAlpha = flAlphas[0]; } else { float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[2] - vecFlatVerts[0] ).Length(); vecPoint = vecVerts[0] + ( flFrac * ( vecVerts[2] - vecVerts[0] ) ); if ( pAlpha ) { *pAlpha = flAlphas[0] + ( flFrac * ( flAlphas[2] - flAlphas[0] ) ); } } if( pNormal ) { Vector edgeU = vecVerts[2] - vecVerts[1]; Vector edgeV = vecVerts[0] - vecVerts[1]; *pNormal = CrossProduct( edgeU, edgeV ); VectorNormalize( *pNormal ); } } else if ( nSnapV == nNextV ) { if ( nSnapU == nNextU ) { vecPoint = vecVerts[0]; *pAlpha = flAlphas[0]; } else { float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[2] - vecFlatVerts[0] ).Length(); vecPoint = vecVerts[0] + ( flFrac * ( vecVerts[2] - vecVerts[0] ) ); if ( pAlpha ) { *pAlpha = flAlphas[0] + ( flFrac * ( flAlphas[2] - flAlphas[0] ) ); } } if( pNormal ) { Vector edgeU = vecVerts[2] - vecVerts[1]; Vector edgeV = vecVerts[0] - vecVerts[1]; *pNormal = CrossProduct( edgeV, edgeU ); VectorNormalize( *pNormal ); } } else { float flCfs[3]; if ( CalcBarycentricCooefs( vecFlatVerts[0], vecFlatVerts[1], vecFlatVerts[2], vecIntersectPoint, flCfs[0], flCfs[1], flCfs[2] ) ) { vecPoint = ( vecVerts[0] * flCfs[0] ) + ( vecVerts[1] * flCfs[1] ) + ( vecVerts[2] * flCfs[2] ); if( pAlpha ) { *pAlpha = ( flAlphas[0] * flCfs[0] ) + ( flAlphas[1] * flCfs[1] ) + ( flAlphas[2] * flCfs[2] ); } if( pNormal ) { Vector edgeU = vecVerts[2] - vecVerts[1]; Vector edgeV = vecVerts[0] - vecVerts[1]; *pNormal = CrossProduct( edgeV, edgeU ); VectorNormalize( *pNormal ); } } else { if ( !bBackup ) { DispUVToSurf_TriBLToTR_2( vecIntersectPoint, nSnapU, nNextU, nSnapV, nNextV, vecPoint, pNormal, pAlpha, true ); } } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCoreDispInfo::DispUVToSurf_TriBLToTR_2( const Vector &vecIntersectPoint, int nSnapU, int nNextU, int nSnapV, int nNextV, Vector &vecPoint, Vector *pNormal, float *pAlpha, bool bBackup ) { int nWidth = GetWidth(); int nIndices[3]; nIndices[0] = nSnapV * nWidth + nSnapU; nIndices[1] = nNextV * nWidth + nNextU; nIndices[2] = nSnapV * nWidth + nNextU; Vector vecFlatVerts[3], vecVerts[3]; float flAlphas[3]; for ( int iVert = 0; iVert < 3; ++iVert ) { vecFlatVerts[iVert] = m_pVerts[nIndices[iVert]].m_FlatVert; vecVerts[iVert] = m_pVerts[nIndices[iVert]].m_Vert; flAlphas[iVert] = m_pVerts[nIndices[iVert]].m_Alpha; } if ( nSnapU == nNextU ) { if ( nSnapV == nNextV ) { vecPoint = vecVerts[0]; *pAlpha = flAlphas[0]; } else { float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[1] - vecFlatVerts[0] ).Length(); vecPoint = vecVerts[0] + ( flFrac * ( vecVerts[1] - vecVerts[0] ) ); if ( pAlpha ) { *pAlpha = flAlphas[0] + ( flFrac * ( flAlphas[1] - flAlphas[0] ) ); } } if( pNormal ) { Vector edgeU = vecVerts[0] - vecVerts[2]; Vector edgeV = vecVerts[1] - vecVerts[2]; *pNormal = CrossProduct( edgeV, edgeU ); VectorNormalize( *pNormal ); } } else if ( nSnapV == nNextV ) { if ( nSnapU == nNextU ) { vecPoint = vecVerts[0]; *pAlpha = flAlphas[0]; } else { float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[2] - vecFlatVerts[0] ).Length(); vecPoint = vecVerts[0] + ( flFrac * ( vecVerts[2] - vecVerts[0] ) ); if ( pAlpha ) { *pAlpha = flAlphas[0] + ( flFrac * ( flAlphas[2] - flAlphas[0] ) ); } } if( pNormal ) { Vector edgeU = vecVerts[0] - vecVerts[2]; Vector edgeV = vecVerts[1] - vecVerts[2]; *pNormal = CrossProduct( edgeV, edgeU ); VectorNormalize( *pNormal ); } } else { float flCfs[3]; if ( CalcBarycentricCooefs( vecFlatVerts[0], vecFlatVerts[1], vecFlatVerts[2], vecIntersectPoint, flCfs[0], flCfs[1], flCfs[2] ) ) { vecPoint = ( vecVerts[0] * flCfs[0] ) + ( vecVerts[1] * flCfs[1] ) + ( vecVerts[2] * flCfs[2] ); if( pAlpha ) { *pAlpha = ( flAlphas[0] * flCfs[0] ) + ( flAlphas[1] * flCfs[1] ) + ( flAlphas[2] * flCfs[2] ); } if( pNormal ) { Vector edgeU = vecVerts[0] - vecVerts[2]; Vector edgeV = vecVerts[1] - vecVerts[2]; *pNormal = CrossProduct( edgeV, edgeU ); VectorNormalize( *pNormal ); } } else { if ( !bBackup ) { DispUVToSurf_TriBLToTR_1( vecIntersectPoint, nSnapU, nNextU, nSnapV, nNextV, vecPoint, pNormal, pAlpha, true ); } } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCoreDispInfo::DispUVToSurf_TriBLToTR( Vector &vecPoint, Vector *pNormal, float *pAlpha, float flU, float flV, const Vector &vecIntersectPoint ) { int nWidth = GetWidth(); int nHeight = GetHeight(); int nSnapU = static_cast( flU ); int nSnapV = static_cast( flV ); int nNextU = nSnapU + 1; int nNextV = nSnapV + 1; if ( nNextU == nWidth) { --nNextU; } if ( nNextV == nHeight ) { --nNextV; } float flFracU = flU - static_cast( nSnapU ); float flFracV = flV - static_cast( nSnapV ); if( flFracU < flFracV ) { DispUVToSurf_TriBLToTR_1( vecIntersectPoint, nSnapU, nNextU, nSnapV, nNextV, vecPoint, pNormal, pAlpha, false ); } else { DispUVToSurf_TriBLToTR_2( vecIntersectPoint, nSnapU, nNextU, nSnapV, nNextV, vecPoint, pNormal, pAlpha, false ); } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CCoreDispInfo::DispUVToSurf( Vector2D const &dispUV, Vector &vecPoint, Vector *pNormal, float *pAlpha ) { // Check to see that the point is on the surface. if ( dispUV.x < 0.0f || dispUV.x > 1.0f || dispUV.y < 0.0f || dispUV.y > 1.0f ) return; // Get the base surface points. Vector vecIntersectPoint; CCoreDispSurface *pSurf = GetSurface(); PointInQuadFromBarycentric( pSurf->GetPoint( 0 ), pSurf->GetPoint( 3 ), pSurf->GetPoint( 2 ), pSurf->GetPoint( 1 ), dispUV, vecIntersectPoint ); // Get the displacement power. int nWidth = GetWidth(); int nHeight = GetHeight(); // Scale the U, V coordinates to the displacement grid size. float flU = dispUV.x * ( static_cast( nWidth ) - 1.000001f ); float flV = dispUV.y * ( static_cast( nHeight ) - 1.000001f ); // Find the base U, V. int nSnapU = static_cast( flU ); int nSnapV = static_cast( flV ); // Use this to get the triangle orientation. bool bOdd = ( ( ( nSnapV * nWidth ) + nSnapU ) % 2 == 1 ); // Top Left to Bottom Right if( bOdd ) { DispUVToSurf_TriTLToBR( vecPoint, pNormal, pAlpha, flU, flV, vecIntersectPoint ); } // Bottom Left to Top Right else { DispUVToSurf_TriBLToTR( vecPoint, pNormal, pAlpha, flU, flV, vecIntersectPoint ); } } //----------------------------------------------------------------------------- // Purpose: Create bounding boxes around pairs of triangles (in a grid-like) // fashion; used for culling //----------------------------------------------------------------------------- void CCoreDispInfo::CreateBoundingBoxes( CoreDispBBox_t *pBBox, int count ) { // // Initialize the bounding boxes. // int iBox; for( iBox = 0; iBox < count; ++iBox ) { pBBox[iBox].vMin.Init( FLT_MAX, FLT_MAX, FLT_MAX ); pBBox[iBox].vMax.Init( FLT_MIN, FLT_MIN, FLT_MIN ); } // Get the width and height of the displacement surface. int nHeight = GetHeight(); int nWidth = GetWidth(); // Find bounding box of every two consecutive triangles iBox = 0; int nIndex = 0; for( int iHgt = 0; iHgt < ( nHeight - 1 ); ++iHgt ) { for( int iWid = 0; iWid < ( nWidth - 1 ); ++iWid ) { for( int iPoint = 0; iPoint < 4; ++iPoint ) { switch( iPoint ) { case 0: { nIndex = ( nHeight * iHgt ) + iWid; break; } case 1: { nIndex = ( nHeight * ( iHgt + 1 ) ) + iWid; break; } case 2: { nIndex = ( nHeight * ( iHgt + 1 ) ) + ( iWid + 1 ); break; } case 3: { nIndex = ( nHeight * iHgt ) + ( iWid + 1 ); break; } default: { break; } } Vector vecPoint; GetVert( nIndex, vecPoint ); if( vecPoint[0] < pBBox[iBox].vMin[0] ) { pBBox[iBox].vMin[0] = vecPoint[0]; } if( vecPoint[1] < pBBox[iBox].vMin[1] ) { pBBox[iBox].vMin[1] = vecPoint[1]; } if( vecPoint[2] < pBBox[iBox].vMin[2] ) { pBBox[iBox].vMin[2] = vecPoint[2]; } if( vecPoint[0] > pBBox[iBox].vMax[0] ) { pBBox[iBox].vMax[0] = vecPoint[0]; } if( vecPoint[1] > pBBox[iBox].vMax[1] ) { pBBox[iBox].vMax[1] = vecPoint[1]; } if( vecPoint[2] > pBBox[iBox].vMax[2] ) { pBBox[iBox].vMax[2] = vecPoint[2]; } } iBox++; } } // Verify. Assert( iBox == count ); // Bloat. for ( iBox = 0; iBox < count; ++iBox ) { for( int iAxis = 0; iAxis < 3; ++iAxis ) { pBBox[iBox].vMin[iAxis] -= 1.0f; pBBox[iBox].vMax[iAxis] += 1.0f; } } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- inline bool PointInDispBBox( CoreDispBBox_t *pBox, const Vector &vecPoint ) { // Check to see if point lies in box if( ( vecPoint.x < pBox->vMin.x ) || ( vecPoint.x > pBox->vMax.x ) ) return false; if( ( vecPoint.y < pBox->vMin.y ) || ( vecPoint.y > pBox->vMax.y ) ) return false; if( ( vecPoint.z < pBox->vMin.z ) || ( vecPoint.z > pBox->vMax.z ) ) return false; return true; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CCoreDispInfo::GetTriangleIndicesForDispBBox( int nIndex, int nTris[2][3] ) { // Test to see whether or not the index is odd. bool bOdd = ( ( nIndex % 2 ) == 1 ); int nWidth = GetWidth(); // Tris for TLtoBR if ( bOdd ) { nTris[0][0] = nIndex; nTris[0][1] = nIndex + nWidth; nTris[0][2] = nIndex + 1; nTris[1][0] = nIndex + 1; nTris[1][1] = nIndex + nWidth; nTris[1][2] = nIndex + nWidth + 1; } // Tris for BLtoTR else { nTris[0][0] = nIndex; nTris[0][1] = nIndex + nWidth; nTris[0][2] = nIndex + nWidth + 1; nTris[1][0] = nIndex; nTris[1][1] = nIndex + nWidth + 1; nTris[1][2] = nIndex + 1; } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- bool CCoreDispInfo::SurfToBaseFacePlane( Vector const &surfPt, Vector &planePt ) { // Create bounding boxes int nBoxCount = ( GetHeight() - 1 ) * ( GetWidth() - 1 ); CoreDispBBox_t *pBBox = new CoreDispBBox_t[nBoxCount]; CreateBoundingBoxes( pBBox, nBoxCount ); // Use the boxes as a first-pass culling mechanism. for( int iBox = 0; iBox < nBoxCount; ++iBox ) { // Get the current displacement triangle-pair bounding-box. CoreDispBBox_t *pBox = &pBBox[iBox]; if( !pBox ) continue; // Check the point against the current displacement bounding-box. if ( !PointInDispBBox( pBox, surfPt ) ) continue; // Point lies within the bounding box. int nIndex = iBox + ( iBox / ( GetWidth() - 1 ) ); // Get the triangle coordinates for this box. int aTris[2][3]; GetTriangleIndicesForDispBBox( nIndex, aTris ); // Barycentrically test the triangles on the displacement surface. Vector vecPoints[3]; for ( int iTri = 0; iTri < 2; ++iTri ) { for ( int iVert = 0; iVert < 3; ++iVert ) { GetVert( aTris[iTri][iVert], vecPoints[iVert] ); } float c[3]; if ( CalcBarycentricCooefs( vecPoints[0], vecPoints[1], vecPoints[2], surfPt, c[0], c[1], c[2] ) ) { Vector vecFlatPoints[3]; for ( int iVert = 0; iVert < 3; ++iVert ) { GetFlatVert( aTris[iTri][iVert], vecFlatPoints[iVert] ); } planePt = ( vecFlatPoints[0] * c[0] ) + ( vecFlatPoints[1] * c[1] ) + ( vecFlatPoints[2] * c[2] ); // Delete temporary memory. delete [] pBBox; return true; } } } // Delete temporary memory delete [] pBBox; return false; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int CCoreDispInfo::GetTriCount( void ) { return ( ( GetHeight() - 1 ) * ( GetWidth() -1 ) * 2 ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCoreDispInfo::GetTriIndices( int iTri, unsigned short &v1, unsigned short &v2, unsigned short &v3 ) { // Verify we have the correct data (only build when collision data is built). if ( !m_pTris || ( iTri < 0 ) || ( iTri >= GetTriCount() ) ) { Assert( iTri >= 0 ); Assert( iTri < GetTriCount() ); Assert( m_pTris ); return; } CoreDispTri_t *pTri = &m_pTris[iTri]; v1 = pTri->m_iIndex[0]; v2 = pTri->m_iIndex[1]; v3 = pTri->m_iIndex[2]; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCoreDispInfo::SetTriIndices( int iTri, unsigned short v1, unsigned short v2, unsigned short v3 ) { // Verify we have the correct data (only build when collision data is built). if ( !m_pTris || ( iTri < 0 ) || ( iTri >= GetTriCount() ) ) { Assert( iTri >= 0 ); Assert( iTri < GetTriCount() ); Assert( m_pTris ); return; } CoreDispTri_t *pTri = &m_pTris[iTri]; pTri->m_iIndex[0] = v1; pTri->m_iIndex[1] = v2; pTri->m_iIndex[2] = v3; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCoreDispInfo::GetTriPos( int iTri, Vector &v1, Vector &v2, Vector &v3 ) { // Verify we have the correct data (only build when collision data is built). if ( !m_pTris || ( iTri < 0 ) || ( iTri >= GetTriCount() ) ) { Assert( iTri >= 0 ); Assert( iTri < GetTriCount() ); Assert( m_pTris ); return; } CoreDispTri_t *pTri = &m_pTris[iTri]; v1 = m_pVerts[pTri->m_iIndex[0]].m_Vert; v2 = m_pVerts[pTri->m_iIndex[1]].m_Vert; v3 = m_pVerts[pTri->m_iIndex[2]].m_Vert; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCoreDispInfo::InitTris( void ) { // Verify we have the correct data (only build when collision data is built). if ( !m_pTris ) { Assert( m_pTris ); return; } int nTriCount = GetTriCount(); for ( int iTri = 0; iTri < nTriCount; ++iTri ) { m_pTris[iTri].m_uiTags = 0; } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCoreDispInfo::CreateTris( void ) { // Verify we have the correct data (only build when collision data is built). if ( !m_pTris ) { Assert( m_pTris ); return; } // Extra sanity check if wanted! Assert( GetTriCount() == ( m_RenderIndexCount / 3 ) ); int nTriCount = GetTriCount(); for ( int iTri = 0, iRender = 0; iTri < nTriCount; ++iTri, iRender += 3 ) { m_pTris[iTri].m_iIndex[0] = m_RenderIndices[iRender]; m_pTris[iTri].m_iIndex[1] = m_RenderIndices[iRender+1]; m_pTris[iTri].m_iIndex[2] = m_RenderIndices[iRender+2]; } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CCoreDispInfo::IsTriWalkable( int iTri ) { if ( IsTriTag( iTri, COREDISPTRI_TAG_FORCE_WALKABLE_BIT ) ) { return IsTriTag( iTri, COREDISPTRI_TAG_FORCE_WALKABLE_VAL ); } return IsTriTag( iTri, COREDISPTRI_TAG_WALKABLE ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CCoreDispInfo::IsTriBuildable( int iTri ) { if ( IsTriTag( iTri, COREDISPTRI_TAG_FORCE_BUILDABLE_BIT ) ) { return IsTriTag( iTri, COREDISPTRI_TAG_FORCE_BUILDABLE_VAL ); } return IsTriTag( iTri, COREDISPTRI_TAG_BUILDABLE ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CCoreDispInfo::IsTriRemove( int iTri ) { return IsTriTag( iTri, COREDISPTRI_TAG_FORCE_REMOVE_BIT ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCoreDispInfo::Position_Update( int iVert, Vector vecPos ) { Vector vSPos, vFlat; GetFlatVert( iVert, vFlat ); GetSubdivPosition( iVert, vSPos ); Vector vSeg; vSeg = vecPos - vFlat; vSeg -= vSPos; // Subtract out the elevation. float elev = GetElevation(); if( elev != 0.0 ) { Vector vNormal; GetSurface()->GetNormal( vNormal ); vNormal *= elev; vSeg -= vNormal; } float flDistance = VectorNormalize( vSeg ); SetFieldVector( iVert, vSeg ); SetFieldDistance( iVert, flDistance ); }