diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /hammer/mapdisp.cpp | |
| download | archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.tar.xz archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.zip | |
Diffstat (limited to 'hammer/mapdisp.cpp')
| -rw-r--r-- | hammer/mapdisp.cpp | 4365 |
1 files changed, 4365 insertions, 0 deletions
diff --git a/hammer/mapdisp.cpp b/hammer/mapdisp.cpp new file mode 100644 index 0000000..b748f1c --- /dev/null +++ b/hammer/mapdisp.cpp @@ -0,0 +1,4365 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include <stdafx.h> +#include "bitmap/tgaloader.h" +#include "ChunkFile.h" +#include "MapDefs.h" +#include "MapDisp.h" +#include "MapDoc.h" +#include "MapFace.h" +#include "MapSolid.h" +#include "MapWorld.h" +#include "MainFrm.h" +#include "GlobalFunctions.h" +#include "SaveInfo.h" +#include "TextureSystem.h" +#include "materialsystem/imesh.h" +#include "Material.h" +#include "CollisionUtils.h" +#include "CModel.h" +#include "History.h" +#include "ToolDisplace.h" +#include "ToolManager.h" +#include "mathlib/mathlib.h" +#include "dispshore.h" +#include "Color.h" +#include "render2d.h" +#include "faceeditsheet.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +#define OVERLAY_CHECK_BLOAT 16.0f + +bool CMapDisp::m_bSelectMask = false; +bool CMapDisp::m_bGridMask = false; + +//----------------------------------------------------------------------------- +// Purpose : CMapDisp constructor +//----------------------------------------------------------------------------- +CMapDisp::CMapDisp() +{ + // clear neighbor data + ResetNeighbors(); + + // + // initialize the hit indices + // + ResetTexelHitIndex(); + ResetDispMapHitIndex(); + + m_bHasMappingAxes = false; + VectorClear( m_MapAxes[0] ); + VectorClear( m_MapAxes[1] ); + + m_Scale = 1.0f; + + m_bSubdiv = false; + m_bReSubdiv = false; + + m_CoreDispInfo.InitDispInfo( 4, 0, 0, NULL, NULL, NULL ); + Paint_Init( DISPPAINT_CHANNEL_POSITION ); + + m_CoreDispInfo.AllowedVerts_Clear(); +} + +//----------------------------------------------------------------------------- +// Purpose : CMapDisp deconstructor +//----------------------------------------------------------------------------- +CMapDisp::~CMapDisp() +{ + m_aWalkableVerts.Purge(); + m_aWalkableIndices.Purge(); + m_aForcedWalkableIndices.Purge(); + + m_aBuildableVerts.Purge(); + m_aBuildableIndices.Purge(); + m_aForcedBuildableIndices.Purge(); + + m_aRemoveVerts.Purge(); + m_aRemoveIndices.Purge(); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CMapDisp::InitDispSurfaceData( CMapFace *pFace, bool bGenerateStartPoint ) +{ + // + // verify face is a "quad" + // + int pointCount = pFace->GetPointCount(); + if( pointCount != 4 ) + return false; + + // get the displacement surface + CCoreDispSurface *pSurf = m_CoreDispInfo.GetSurface(); + + // + // set face point data - pos, normal, texture, etc.... + // + Vector v3; + Vector2D v2; + pSurf->SetPointCount( 4 ); + for( int i = 0; i < 4; i++ ) + { + // position + pFace->GetPoint( v3, i ); + pSurf->SetPoint( i, v3 ); + + // normal + pFace->GetFaceNormal( v3 ); + pSurf->SetPointNormal( i, v3 ); + + // texture coords + pFace->GetTexCoord( v2, i ); + pSurf->SetTexCoord( i, v2 ); + } + + // + // get displacement surface point start index + // + int pointStartIndex = pSurf->GetPointStartIndex(); + if( m_bHasMappingAxes && ( pointStartIndex == -1 ) ) + { + pSurf->GeneratePointStartIndexFromMappingAxes( m_MapAxes[0], m_MapAxes[1] ); + } + else + { + if( bGenerateStartPoint ) + { + pSurf->GenerateSurfPointStartIndex(); + } + else + { + pSurf->FindSurfPointStartIndex(); + } + } + pSurf->AdjustSurfPointData(); + + // Luxel coords. + int nLightmapScale = pFace->texture.nLightmapScale; + pSurf->CalcLuxelCoords( nLightmapScale, false, pFace->texture.UAxis.AsVector3D(), pFace->texture.VAxis.AsVector3D() ); + + // Set the lightmap coordinates. + for ( int iLuxelCoord = 0; iLuxelCoord < 4; ++iLuxelCoord ) + { + Vector2D vecCoord; + pSurf->GetLuxelCoord( 0, iLuxelCoord, vecCoord ); + pFace->SetLightmapCoord( vecCoord, iLuxelCoord ); + } + + // reset the has mapping axes flag (surface has been created! - use new method now) + m_bHasMappingAxes = false; + + // set the s and t texture mapping axes so that tangent spaces can be calculated + pSurf->SetSAxis( pFace->texture.UAxis.AsVector3D() ); + pSurf->SetTAxis( pFace->texture.VAxis.AsVector3D() ); + + // successful init + return true; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapDisp::ResetFieldData( void ) +{ + ResetFieldVectors(); + ResetFieldDistances(); + ResetSubdivPositions(); + ResetSubdivNormals(); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapDisp::InitData( int power ) +{ + // set surface "power" (defines size) + SetPower( power ); + + // clear vector field distances, subdiv positions and normals + ResetFieldData(); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CMapDisp::Create( void ) +{ + if ( m_CoreDispInfo.CreateWithoutLOD() ) + { + PostCreate(); + return true; + } + + return false; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapDisp::PostCreate( void ) +{ + UpdateBoundingBox(); + UpdateNeighborDependencies( false ); + UpdateLightmapExtents(); + UpdateWalkable(); + UpdateBuildable(); + + // Get the current face and create/update any detail objects + CMapFace *pFace = static_cast<CMapFace*>( GetParent() ); + if ( pFace ) + DetailObjects::BuildAnyDetailObjects(pFace); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +CMapDisp *CMapDisp::CopyFrom( CMapDisp *pMapDisp, bool bUpdateDependencies ) +{ + // + // check for valid displacement to copy from + // + if( !pMapDisp ) + return NULL; + + // + // copy the base surface data - positions, normals, texture coords, etc... + // + CCoreDispSurface *pFromSurf = pMapDisp->m_CoreDispInfo.GetSurface(); + CCoreDispSurface *pToSurf = m_CoreDispInfo.GetSurface(); + + int pointCount = pFromSurf->GetPointCount(); + pToSurf->SetPointCount( pointCount ); + + Vector2D v2; + Vector v3; + for( int i = 0; i < pointCount; i++ ) + { + pFromSurf->GetPoint( i, v3 ); + pToSurf->SetPoint( i, v3 ); + + pFromSurf->GetPointNormal( i, v3 ); + pToSurf->SetPointNormal( i, v3 ); + + pFromSurf->GetTexCoord( i, v2 ); + pToSurf->SetTexCoord( i, v2 ); + + pFromSurf->GetLuxelCoord( 0, i, v2 ); + pToSurf->SetLuxelCoord( 0, i, v2 ); + } + + pToSurf->SetFlags( pFromSurf->GetFlags() ); + pToSurf->SetContents( pFromSurf->GetContents() ); + pToSurf->SetPointStartIndex( pFromSurf->GetPointStartIndex() ); + + // + // copy displacement surface data + // + SetPower( pMapDisp->GetPower() ); + SetElevation( pMapDisp->GetElevation() ); + + // save the scale -- don't want to rescale!! + m_Scale = pMapDisp->GetScale(); + + int size = GetSize(); + for( int i = 0; i < size; i++ ) + { + pMapDisp->GetFieldVector( i, v3 ); + SetFieldVector( i, v3 ); + + pMapDisp->GetSubdivPosition( i, v3 ); + SetSubdivPosition( i, v3 ); + + pMapDisp->GetSubdivNormal( i, v3 ); + SetSubdivNormal( i, v3 ); + + SetFieldDistance( i, pMapDisp->GetFieldDistance( i ) ); + + pMapDisp->GetVert( i, v3 ); + SetVert( i, v3 ); + + pMapDisp->GetFlatVert( i, v3 ); + SetFlatVert( i, v3 ); + + SetAlpha( i, pMapDisp->GetAlpha( i ) ); + } + + int renderCount = pMapDisp->m_CoreDispInfo.GetRenderIndexCount(); + m_CoreDispInfo.SetRenderIndexCount( renderCount ); + for( int i = 0; i < renderCount; i++ ) + { + m_CoreDispInfo.SetRenderIndex( i, pMapDisp->m_CoreDispInfo.GetRenderIndex( i ) ); + } + + // Copy the triangle data. + int nTriCount = GetTriCount(); + for ( int iTri = 0; iTri < nTriCount; ++iTri ) + { + unsigned short triIndices[3]; + pMapDisp->GetTriIndices( iTri, triIndices[0], triIndices[1], triIndices[2] ); + m_CoreDispInfo.SetTriIndices( iTri, triIndices[0], triIndices[1], triIndices[2] ); + + unsigned short triValue = pMapDisp->m_CoreDispInfo.GetTriTagValue( iTri ); + m_CoreDispInfo.SetTriTagValue( iTri, triValue ); + } + + // + // copy editor specific data + // + m_bSubdiv = pMapDisp->IsSubdivided(); + m_bReSubdiv = pMapDisp->NeedsReSubdivision(); + + ResetTexelHitIndex(); + ResetDispMapHitIndex(); + ResetTouched(); + m_CoreDispInfo.AllowedVerts_Clear(); + + // + // re-build the surface??? an undo, etc... + // + if( bUpdateDependencies ) + { + UpdateData(); + CheckAndUpdateOverlays( true ); + } + + return this; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapDisp::UpdateSurfData( CMapFace *pFace ) +{ + InitDispSurfaceData( pFace, false ); + Create(); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapDisp::UpdateSurfDataAndVectorField( CMapFace *pFace ) +{ + InitDispSurfaceData( pFace, false ); + +// ResetFieldVectors(); + ResetSubdivPositions(); + ResetSubdivNormals(); + + Create(); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapDisp::UpdateData( void ) +{ + Create(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapDisp::UpdateDataAndNeighborData( void ) +{ + // update itself + Create(); + + // update neighbors + for( int i = 0; i < 4; i++ ) + { + EditDispHandle_t handle = GetEdgeNeighbor( i ); + if( handle != EDITDISPHANDLE_INVALID ) + { + CMapDisp *pNeighborDisp = EditDispMgr()->GetDisp( handle ); + pNeighborDisp->UpdateData(); + } + + int cornerCount = GetCornerNeighborCount( i ); + if( cornerCount > 0 ) + { + for( int j = 0; j < cornerCount; j++ ) + { + handle = GetCornerNeighbor( i, j ); + if( handle != EDITDISPHANDLE_INVALID ) + { + CMapDisp *pNeighborDisp = EditDispMgr()->GetDisp( handle ); + pNeighborDisp->UpdateData(); + } + } + } + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapDisp::UpdateLightmapExtents( void ) +{ + // Get the parent face. + CMapFace *pFace = ( CMapFace* )GetParent(); + if( !pFace ) + return; + + // Check for valid lightmap size and correct if need be. + ValidLightmapSize(); +} + +//----------------------------------------------------------------------------- +// Purpose: Returns TRUE if the lightmap scale on this face is within the acceptable range. +//----------------------------------------------------------------------------- +bool CMapDisp::ValidLightmapSize( void ) +{ + // Get the current face and lightmap scale. + CMapFace *pFace = static_cast<CMapFace*>( GetParent() ); + if ( !pFace ) + return false; + + int nLightmapScale = pFace->texture.nLightmapScale; + + // Get the surface points. + Vector vecPoints[4]; + for ( int iPoint = 0; iPoint < 4; ++iPoint ) + { + GetSurfPoint( iPoint, vecPoints[iPoint] ); + } + + // Find the largest edge. + float flMaxLength = 0.0f; + for ( int iPoint = 0; iPoint < 4; ++iPoint ) + { + float flLength = ( vecPoints[(iPoint+1)%4] - vecPoints[iPoint] ).Length(); + if ( flLength > flMaxLength ) + { + flMaxLength = flLength; + } + } + + float flOOLightmapScale = 1.0f / static_cast<float>( nLightmapScale ); + float flSize = static_cast<float>( static_cast<int>( flMaxLength * flOOLightmapScale ) + 1 ); + if ( flSize > MAX_DISP_LIGHTMAP_DIM_WITHOUT_BORDER ) + { + while ( flSize > MAX_DISP_LIGHTMAP_DIM_WITHOUT_BORDER ) + { + nLightmapScale++; + flOOLightmapScale = 1.0f / static_cast<float>( nLightmapScale ); + flSize = static_cast<float>( static_cast<int>( flMaxLength * flOOLightmapScale ) + 1 ); + } + + // Save the next to last. + pFace->texture.nLightmapScale = nLightmapScale; + + // Re-calculate texture coordinates now. + pFace->CalcTextureCoords(); + + CFaceEditSheet *pSheet = GetMainWnd()->GetFaceEditSheet(); + if( pSheet ) + { + pSheet->m_MaterialPage.UpdateDialogData(); + } + } + + return true; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CMapDisp::EntityInBoundingBox( Vector const &vOrigin ) +{ + Vector vMin, vMax; + + for( int axis = 0; axis < 3; axis++ ) + { + vMin[axis] = m_BBox[0][axis] - OVERLAY_CHECK_BLOAT; + vMax[axis] = m_BBox[1][axis] + OVERLAY_CHECK_BLOAT; + } + + if( ( vOrigin.x < vMin.x ) || ( vOrigin.x > vMax.x ) || + ( vOrigin.y < vMin.y ) || ( vOrigin.y > vMax.y ) || + ( vOrigin.z < vMin.z ) || ( vOrigin.z > vMax.z ) ) + return false; + + return true; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapDisp::CheckAndUpdateOverlays( bool bFull ) +{ + CMapFace *pFace = ( CMapFace* )GetParent(); + if ( pFace ) + { + CMapSolid *pSolid = ( CMapSolid* )pFace->GetParent(); + if ( pSolid ) + { + if ( !bFull ) + { + pSolid->PostUpdate(Notify_Rebuild); + } + else + { + pSolid->PostUpdate(Notify_Rebuild_Full); + } + } + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapDisp::UpSample( int oldPower ) +{ + // + // allocate temporary memory to hold new displacement distances + // + int width = GetWidth(); + int height = GetHeight(); + + float *dists = new float[height*width]; + float *alphas = new float[height*width]; + Vector *dispVectors = new Vector[height*width]; + Vector *subdivPositions = new Vector[height*width]; + Vector *subdivNormals = new Vector[height*width]; + + if( !dists || !alphas || !dispVectors || !subdivPositions || !subdivNormals ) + { + delete [] dists; + delete [] alphas; + delete [] dispVectors; + delete [] subdivPositions; + delete [] subdivNormals; + return; + } + + // + // get old width and height + // + int oldWidth = ( ( 1 << oldPower ) + 1 ); + int oldHeight = ( ( 1 << oldPower ) + 1 ); + + for( int oh = 0, nh = 0; oh < oldHeight; oh++, nh += 2 ) + { + for( int ow = 0, nw = 0; ow < oldWidth; ow++, nw += 2 ) + { + bool bRight = false; + bool bUp = false; + + int oldIndex = oh * oldHeight + ow; + int newIndex = nh * height + nw; + + int x = oldIndex % oldWidth; + int y = oldIndex / oldHeight; + + float dist = GetFieldDistance( oldIndex ); + dists[newIndex] = dist; + + float alpha = GetAlpha( oldIndex ); + alphas[newIndex] = alpha; + + Vector dVector[2], subPVector[2], subNVector[2]; + GetFieldVector( oldIndex, dVector[0] ); + GetSubdivPosition( oldIndex, subPVector[0] ); + GetSubdivNormal( oldIndex, subNVector[0] ); + dispVectors[newIndex] = dVector[0]; + subdivPositions[newIndex] = subPVector[0]; + subdivNormals[newIndex] = subNVector[0]; + + if( ( x + 1 ) < oldWidth ) + { + dist = ( GetFieldDistance( oldIndex ) + GetFieldDistance( oldIndex + 1 ) ) * 0.5f; + dists[newIndex+1] = dist; + + alpha = ( GetAlpha( oldIndex ) + GetAlpha( oldIndex + 1 ) ) * 0.5f; + alphas[newIndex+1] = alpha; + + GetFieldVector( oldIndex, dVector[0] ); + GetFieldVector( oldIndex + 1, dVector[1] ); + dispVectors[newIndex+1] = ( dVector[0] + dVector[1] ) * 0.5f; + + GetSubdivPosition( oldIndex, subPVector[0] ); + GetSubdivPosition( oldIndex + 1, subPVector[1] ); + subdivPositions[newIndex+1] = ( subPVector[0] + subPVector[1] ) * 0.5f; + + GetSubdivNormal( oldIndex, subNVector[0] ); + GetSubdivNormal( oldIndex + 1, subNVector[1] ); + subdivNormals[newIndex+1] = ( subNVector[0] + subNVector[1] ) * 0.5f; + + bRight = true; + } + + if( ( y + 1 ) < oldHeight ) + { + dist = ( GetFieldDistance( oldIndex ) + GetFieldDistance( oldIndex + oldHeight ) ) * 0.5f; + dists[newIndex+height] = dist; + + alpha = ( GetAlpha( oldIndex ) + GetAlpha( oldIndex + oldHeight ) ) * 0.5f; + alphas[newIndex+height] = alpha; + + GetFieldVector( oldIndex, dVector[0] ); + GetFieldVector( oldIndex + oldHeight, dVector[1] ); + dispVectors[newIndex+height] = ( dVector[0] + dVector[1] ) * 0.5f; + + GetSubdivPosition( oldIndex, subPVector[0] ); + GetSubdivPosition( oldIndex + oldHeight, subPVector[1] ); + subdivPositions[newIndex+height] = ( subPVector[0] + subPVector[1] ) * 0.5f; + + GetSubdivNormal( oldIndex, subNVector[0] ); + GetSubdivNormal( oldIndex + oldHeight, subNVector[1] ); + subdivNormals[newIndex+height] = ( subNVector[0] + subNVector[1] ) * 0.5f; + + bUp = true; + } + + if( bRight && bUp ) + { + dist = ( GetFieldDistance( oldIndex + 1 ) + GetFieldDistance( oldIndex + oldHeight ) ) * 0.5f; + dists[newIndex+height+1] = dist; + + alpha = ( GetAlpha( oldIndex + 1 ) + GetAlpha( oldIndex + oldHeight ) ) * 0.5f; + alphas[newIndex+height+1] = alpha; + + GetFieldVector( oldIndex + 1, dVector[0] ); + GetFieldVector( oldIndex + oldHeight, dVector[1] ); + dispVectors[newIndex+height+1] = ( dVector[0] + dVector[1] ) * 0.5f; + + GetSubdivPosition( oldIndex + 1, subPVector[0] ); + GetSubdivPosition( oldIndex + oldHeight, subPVector[1] ); + subdivPositions[newIndex+height+1] = ( subPVector[0] + subPVector[1] ) * 0.5f; + + GetSubdivNormal( oldIndex + 1, subNVector[0] ); + GetSubdivNormal( oldIndex + oldHeight, subNVector[1] ); + subdivNormals[newIndex+height+1] = ( subNVector[0] + subNVector[1] ) * 0.5f; + } + } + } + + // + // copy sampled list + // + int size = GetSize(); + for( int i = 0; i < size; i++ ) + { + SetAlpha( i, alphas[i] ); + SetFieldVector( i, dispVectors[i] ); + SetFieldDistance( i, dists[i] ); + SetSubdivPosition( i, subdivPositions[i] ); + SetSubdivNormal( i, subdivNormals[i] ); + } + + // + // free temporary memory + // + delete [] dists; + delete [] alphas; + delete [] dispVectors; + delete [] subdivPositions; + delete [] subdivNormals; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapDisp::SamplePoints( int index, int width, int height, bool *pValidPoints, + float *pValue, float *pAlpha, Vector& newDispVector, + Vector& newSubdivPos, Vector &newSubdivNormal ) +{ + // + // set initial sample values + // + Vector vField, vSPos, vSNormal; + int value = GetFieldDistance( index ); + float alpha = GetAlpha( index ); + GetFieldVector( index, vField ); + GetSubdivPosition( index, vSPos ); + GetSubdivNormal( index, vSNormal ); + + int count = 1; + + // + // accumulate other sample values from around the given index + // + int ndx; + Vector vTmp; + for( int i = 0; i < 8; i++ ) + { + if( !pValidPoints[i] ) + continue; + + switch( i ) + { + case 0: { ndx = index - height - 1; break; } // down and left + case 1: { ndx = index - 1; break; } // left + case 2: { ndx = index + height - 1; break; } // up and left + case 3: { ndx = index + height; break; } // up + case 4: { ndx = index + height + 1; break; } // up and right + case 5: { ndx = index + 1; break; } // right + case 6: { ndx = index - height + 1; break; } // down and right + case 7: { ndx = index - height; break; } // down + default: continue; + } + + value += GetFieldDistance( ndx ); + alpha += GetAlpha( ndx ); + + GetFieldVector( ndx, vTmp ); + vField += vTmp; + + GetSubdivPosition( ndx, vTmp ); + vSPos += vTmp; + + GetSubdivNormal( ndx, vTmp ); + vSNormal += vTmp; + + // increment count + count++; + } + + // average + *pValue = value / ( float )count; + *pAlpha = alpha / ( float )count; + newDispVector = vField / ( float )count; + newSubdivPos = vSPos / ( float )count; + newSubdivNormal = vSNormal / ( float )count; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapDisp::GetValidSamplePoints( int index, int width, int height, bool *pValidPoints ) +{ + int x = index % width; + int y = index / height; + + // down and left + if( ( ( x - 1 ) >= 0 ) && ( ( y - 1 ) >= 0 ) ) { pValidPoints[0] = true; } + + // left + if( ( x - 1 ) >= 0 ) { pValidPoints[1] = true; } + + // up and left + if( ( ( x - 1 ) >= 0 ) && ( ( y + 1 ) < height ) ) { pValidPoints[2] = true; } + + // up + if( ( y + 1 ) < height ) { pValidPoints[3] = true; } + + // up and right + if( ( ( x + 1 ) < width ) && ( ( y + 1 ) < height ) ) { pValidPoints[4] = true; } + + // right + if( ( x + 1 ) < width ) { pValidPoints[5] = true; } + + // down and right + if( ( ( x + 1 ) < width ) && ( ( y - 1 ) >= 0 ) ) { pValidPoints[6] = true; } + + // down + if( ( y - 1 ) >= 0 ) { pValidPoints[7] = true; } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapDisp::DownSample( int oldPower ) +{ + // + // allocate temporary memory to hold new displacement distances + // + int width = GetWidth(); + int height = GetHeight(); + + float *dists = new float[height*width]; + float *alphas = new float[height*width]; + Vector *dispVectors = new Vector[height*width]; + Vector *subdivPos = new Vector[height*width]; + Vector *subdivNormals = new Vector[height*width]; + + if( !dists || !alphas || !dispVectors || !subdivPos || !subdivNormals ) + { + delete [] dists; + delete [] alphas; + delete [] dispVectors; + delete [] subdivPos; + delete [] subdivNormals; + return; + } + + // + // get old width and height + // + int oldWidth = ( ( 1 << oldPower ) + 1 ); + int oldHeight = ( ( 1 << oldPower ) + 1 ); + + for( int oh = 0, nh = 0; oh < oldHeight; oh += 2, nh++ ) + { + for( int ow = 0, nw = 0; ow < oldWidth; ow += 2, nw++ ) + { + int oldIndex = oh * oldHeight + ow; + int newIndex = nh * height + nw; + + // + // clear valid point list and gather valid sample points + // + bool validPoints[8]; + for( int i = 0; i < 8; i++ ) { validPoints[i] = false; } + GetValidSamplePoints( oldIndex, oldWidth, oldHeight, validPoints ); + + // + // sample the points, vector field vectors, and offset vectors + // + float newValue; + float newAlpha; + Vector newDispVector; + Vector newSubdivPos; + Vector newSubdivNormal; + SamplePoints( oldIndex, oldWidth, oldHeight, validPoints, &newValue, &newAlpha, + newDispVector, newSubdivPos, newSubdivNormal ); + + // + // save sampled values + // + dists[newIndex] = newValue; + alphas[newIndex] = newAlpha; + dispVectors[newIndex] = newDispVector; + subdivPos[newIndex] = newSubdivPos; + subdivNormals[newIndex] = newSubdivNormal; + } + } + + // + // copy sampled list + // + int size = GetSize(); + for( int i = 0; i < size; i++ ) + { + SetAlpha( i, alphas[i] ); + SetFieldDistance( i, dists[i] ); + SetFieldVector( i, dispVectors[i] ); + SetSubdivPosition( i, subdivPos[i] ); + SetSubdivNormal( i, subdivNormals[i] ); + } + + // + // free temporary memory + // + delete [] dists; + delete [] alphas; + delete [] dispVectors; + delete [] subdivPos; + delete [] subdivNormals; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMapDisp::InvertAlpha( void ) +{ + int nVertCount = GetSize(); + for ( int iVert = 0; iVert < nVertCount; ++iVert ) + { + float flAlpha = GetAlpha( iVert ); + float flInvAlpha = 255.0f - flAlpha; + SetAlpha( iVert, flInvAlpha ); + } + + // Update the surface with new data. + UpdateData(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapDisp::Resample( int power ) +{ + // + // save old power for resampling, update to new power + // + int oldPower = GetPower(); + if( oldPower > power ) + { + int delta = oldPower - power; + for( int i = 0; i < delta; i++ ) + { + SetPower( oldPower - ( i + 1 ) ); + DownSample( oldPower - i ); + } + } + else + { + int delta = power - oldPower; + for( int i = 0; i < delta; i++ ) + { + SetPower( oldPower + ( i + 1 ) ); + UpSample( oldPower + i ); + } + } + + // update the surface with the new data + UpdateData(); + CheckAndUpdateOverlays( true ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapDisp::Elevate( float elevation ) +{ + // set the new elevation + SetElevation( elevation ); + + // update the displacement + UpdateData(); +} + +//----------------------------------------------------------------------------- +// Purpose: Resample a displacement map to be a clipped version of this surface. +// Called when we split a face with a displacement surface. +// NOTE: The new surface must be a quad as well, otherwise return false; +// hBuilderDisp - The displacement surface to receive the new clipped data. +//----------------------------------------------------------------------------- +void CMapDisp::Split( EditDispHandle_t hBuilderDisp ) +{ +#define SPLIT_EPSILON 0.001f + + CMapDisp *pBuilderDisp = EditDispMgr()->GetDisp( hBuilderDisp ); + + static Vector vecSurfPoints[4]; + for ( int iPoint = 0; iPoint < 4; ++iPoint ) + { + GetSurfPoint( iPoint, vecSurfPoints[iPoint] ); + } + + // Prepare the destination surface for painting. + pBuilderDisp->Paint_Init( DISPPAINT_CHANNEL_POSITION ); + + int nVertCount = pBuilderDisp->GetSize(); + for ( int iVert = 0; iVert < nVertCount; ++iVert ) + { + Vector vecVert; + pBuilderDisp->GetVert( iVert, vecVert ); + + Vector2D vecDispUV; + PointInQuadToBarycentric( vecSurfPoints[0], vecSurfPoints[3], vecSurfPoints[2], vecSurfPoints[1], vecVert, vecDispUV ); + + // A little clean-up here. + for ( int iComp = 0; iComp < 2; ++iComp ) + { + vecDispUV[iComp] = clamp( vecDispUV[iComp], 0.0f, 1.0f ); + } + + Vector vecNewVert, vecNewNormal; + float flNewAlpha; + m_CoreDispInfo.DispUVToSurf( vecDispUV, vecNewVert, &vecNewNormal, &flNewAlpha ); + + pBuilderDisp->SetAlpha( iVert, flNewAlpha ); + pBuilderDisp->Paint_SetValue(iVert, vecNewVert ); + } + + pBuilderDisp->Paint_Update( true ); + +#undef SPLIT_EPSILON +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CMapDisp::ComparePoints( const Vector& pt1, const Vector& pt2, const float tolerance ) +{ + for( int i = 0 ; i < 3 ; i++ ) + { + if( fabs( pt1[i] - pt2[i] ) > tolerance ) + return false; + } + + return true; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +float CMapDisp::CollideWithTriangles( const Vector& RayStart, const Vector& RayEnd, Tri_t *pTris, int triCount, + Vector& surfNormal ) +{ + // create a ray + Ray_t ray; + ray.m_Start = RayStart; + ray.m_Delta = RayEnd - RayStart; + ray.m_IsRay = true; + + Vector vNormal; + float minFraction = 1.0f; + for( int ndxTri = 0; ndxTri < triCount; ndxTri++ ) + { + Tri_t &tri = pTris[ndxTri]; + float fraction = IntersectRayWithTriangle( ray, tri.v[0], tri.v[2], tri.v[1], true ); + if( fraction == -1 ) + continue; + + if( fraction < minFraction ) + { + minFraction = fraction; + + // calculate the triangle normal + Vector edge1, edge2; + VectorSubtract( tri.v[2], tri.v[0], edge1 ); + VectorSubtract( tri.v[1], tri.v[0], edge2 ); + CrossProduct( edge1, edge2, surfNormal ); + VectorNormalize( surfNormal ); + } + } + + return minFraction; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapDisp::CreatePlanesFromBoundingBox( Plane_t *planes, const Vector& bbMin, const Vector& bbMax ) +{ + for( int i = 0; i < 6; i++ ) + { + VectorClear( planes[i].normal ); + } + + // + // use pads to store minor axes + // + planes[0].normal[0] = -1; planes[0].dist = -bbMin[0]; + planes[1].normal[0] = 1; planes[1].dist = bbMax[0]; + + planes[2].normal[1] = -1; planes[2].dist = -bbMin[1]; + planes[3].normal[1] = 1; planes[3].dist = bbMax[1]; + + planes[4].normal[2] = -1; planes[4].dist = -bbMin[2]; + planes[5].normal[2] = 1; planes[5].dist = bbMax[2]; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapDisp::CollideWithBoundingBoxes( const Vector& rayStart, const Vector& rayEnd, + BBox_t *pBBox, int bboxCount, Tri_t *pTris, int *triCount ) +{ + const float DIST_EPSILON = 0.01f; + + // + // collide against all bounding boxes + // + for( int i = 0; i < bboxCount; i++ ) + { + // + // make copy of vectors so they can be cut up + // + Vector start, end; + start = rayStart; + end = rayEnd; + + // + // make planes for bbox + // + Plane_t planes[6]; + CreatePlanesFromBoundingBox( planes, pBBox[i].min, pBBox[i].max ); + + // + // collide against bounding box planes + // + int j; + for( j = 0; j < 6; j++ ) + { + float dist1 = DotProduct( planes[j].normal, start ) - planes[j].dist; + float dist2 = DotProduct( planes[j].normal, end ) - planes[j].dist; + + // + // entry intersection point - move ray start up to intersection + // + if( ( dist1 > DIST_EPSILON ) && ( dist2 < -DIST_EPSILON ) ) + { + float fraction = ( dist1 / ( dist1 - dist2 ) ); + Vector segment, addOn; + VectorSubtract( end, start, segment ); + VectorScale( segment, fraction, addOn ); + VectorNormalize( segment ); + VectorAdd( addOn, segment, addOn ); + VectorAdd( start, addOn, start ); + } + else if( ( dist1 > DIST_EPSILON ) && ( dist2 > DIST_EPSILON ) ) + { + break; + } + } + + // + // collision add triangles to list + // + if( j == 6 ) + { + // gross! shouldn't know value (64) and handle error better + if( *triCount >= 256 ) + { + // error!!!!! + return; + } + + int postSpacing = m_CoreDispInfo.GetPostSpacing(); + int index = i + ( i / ( postSpacing - 1 ) ); + + m_CoreDispInfo.GetVert( index, pTris[*triCount].v[0] ); + m_CoreDispInfo.GetVert( index+postSpacing, pTris[*triCount].v[1] ); + m_CoreDispInfo.GetVert( index+1, pTris[*triCount].v[2] ); + *triCount += 1; + + m_CoreDispInfo.GetVert( index+1, pTris[*triCount].v[0] ); + m_CoreDispInfo.GetVert( index+postSpacing, pTris[*triCount].v[1] ); + m_CoreDispInfo.GetVert( index+postSpacing+1, pTris[*triCount].v[2] ); + *triCount += 1; + } + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapDisp::CreateBoundingBoxes( BBox_t *pBBox, int count, float bloat ) +{ + // + // initialize the bounding boxes + // + for( int i = 0; i < count; i++ ) + { + VectorFill( pBBox[i].min, COORD_NOTINIT ); + VectorFill( pBBox[i].max, -COORD_NOTINIT ); + } + + // get the width and height of the displacement surface + int postSpacing = m_CoreDispInfo.GetPostSpacing(); + + // + // find bounding box of every two consecutive triangles + // + int bboxIndex = 0; + int index = 0; + for( int i = 0; i < ( postSpacing - 1 ); i++ ) + { + for( int j = 0; j < ( postSpacing - 1 ); j++ ) + { + for( int k = 0; k < 4; k++ ) + { + switch( k ) + { + case 0: { index = ( postSpacing * i ) + j; break; } + case 1: { index = ( postSpacing * ( i + 1 ) ) + j; break; } + case 2: { index = ( postSpacing * i ) + ( j + 1 ); break; } + case 3: { index = ( postSpacing * ( i + 1 ) ) + ( j + 1 ); break; } + } + + Vector v; + m_CoreDispInfo.GetVert( index, v ); + if( v[0] < pBBox[bboxIndex].min[0] ) { pBBox[bboxIndex].min[0] = v[0]; } + if( v[1] < pBBox[bboxIndex].min[1] ) { pBBox[bboxIndex].min[1] = v[1]; } + if( v[2] < pBBox[bboxIndex].min[2] ) { pBBox[bboxIndex].min[2] = v[2]; } + + if( v[0] > pBBox[bboxIndex].max[0] ) { pBBox[bboxIndex].max[0] = v[0]; } + if( v[1] > pBBox[bboxIndex].max[1] ) { pBBox[bboxIndex].max[1] = v[1]; } + if( v[2] > pBBox[bboxIndex].max[2] ) { pBBox[bboxIndex].max[2] = v[2]; } + } + + // bloat all the boxes a little + for( int axis = 0; axis < 3; axis++ ) + { + pBBox[bboxIndex].min[axis] -= bloat; + pBBox[bboxIndex].max[axis] += bloat; + } + + bboxIndex++; + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +// NOTE: Performance, look into it here. This is doing way more work than +// necessary. We should probably update a collision representation, a +// simple one at least whenever we update a displacement and use it as +// a first level cull here. But for now....it works...ship, ship, ship. +//----------------------------------------------------------------------------- +bool CMapDisp::TraceLine( Vector &vecHitPos, Vector &vecHitNormal, Vector const &vecRayStart, Vector const &vecRayEnd ) +{ + // Just do the slow thing for now. + float flFraction; + int iTri = CollideWithDispTri( vecRayStart, vecRayEnd, flFraction ); + if ( iTri == -1 ) + return false; + + // Get hit position and normal. + Vector vecRay = vecRayEnd - vecRayStart; + vecRay = vecRay * flFraction; + vecHitPos = vecRayStart + vecRay; + + Vector vecTriPoints[3]; + GetTriPos( iTri, vecTriPoints[0], vecTriPoints[1], vecTriPoints[2] ); + Vector vecEdge1 = vecTriPoints[2] - vecTriPoints[0]; + Vector vecEdge2 = vecTriPoints[1] - vecTriPoints[0]; + vecHitNormal = CrossProduct( vecEdge1, vecEdge2 ); + VectorNormalize( vecHitNormal ); + + return true; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CMapDisp::TraceLineSnapTo( Vector &HitPos, Vector &HitNormal, + Vector const &RayStart, Vector const &RayEnd ) +{ +#define LOWER_TOLERANCE -0.1f +#define UPPER_TOLERANCE 1.1f + + // get width and height + int width = GetWidth(); + int height = GetHeight(); + + // build the ray + Ray_t ray; + ray.m_Start = RayStart; + ray.m_Delta = RayEnd - RayStart; + ray.m_IsRay = true; + + float u, v; + Tri_t tri; + + // test edge 0 + for( int ndx = 0; ndx < ( width - 1 ); ndx++ ) + { + GetVert( ndx, tri.v[0] ); + GetVert( ndx + width, tri.v[1] ); + GetVert( ndx + 1, tri.v[2] ); + + ComputeIntersectionBarycentricCoordinates( ray, tri.v[0], tri.v[1], tri.v[2], u, v ); + + // along edge (0.0 < v < 1.0) and below (u < 0.0) + if( ( v >= LOWER_TOLERANCE ) && ( v <= UPPER_TOLERANCE ) && ( u < 0.0f ) ) + { + v = clamp( v, 0.0f, 1.0f ); + + // snap u (u = 0.0) + HitPos = tri.v[0] + ( tri.v[2] - tri.v[0] ) * v; + return true; + } + + // special corner 0 + if( ( ndx == 0 ) && ( v < 0.0f ) && ( u < 0.0f ) ) + { + HitPos = tri.v[0]; + return true; + } + } + + // test edge 1 + for( int ndx = 0; ndx < ( height - 1 ); ndx++ ) + { + GetVert( ndx * width, tri.v[0] ); + GetVert( ( ndx * width )+ width, tri.v[1] ); + GetVert( ( ndx * width ) + 1, tri.v[2] ); + + ComputeIntersectionBarycentricCoordinates( ray, tri.v[0], tri.v[1], tri.v[2], u, v ); + + // along edge (0.0 < u < 1.0) and left (v < 0.0) + if( ( u >= LOWER_TOLERANCE ) && ( u <= UPPER_TOLERANCE ) && ( v < 0.0f ) ) + { + u = clamp( u, 0.0f, 1.0f ); + + // snap v (v = 0.0) + HitPos = tri.v[0] + ( tri.v[1] - tri.v[0] ) * u; + return true; + } + + // special corner 1 + if( ( ndx == ( height - 2 ) ) && ( u > 1.0f ) && ( v < 0.0f ) ) + { + HitPos = tri.v[1]; + return true; + } + } + + // test edge 2 + for( int ndx = 0; ndx < ( width - 1 ); ndx++ ) + { + GetVert( ( ( height - 1 ) * width ) + ndx + 1, tri.v[0] ); + GetVert( ( ( height - 2 ) * width ) + ndx + 1, tri.v[1] ); + GetVert( ( ( height - 1 ) * width ) + ndx, tri.v[2] ); + + ComputeIntersectionBarycentricCoordinates( ray, tri.v[0], tri.v[1], tri.v[2], u, v ); + + // along edge (0.0 < v < 1.0) and above (u < 0.0) + if( ( v >= LOWER_TOLERANCE ) && ( v <= UPPER_TOLERANCE ) && ( u < 0.0f ) ) + { + v = clamp( v, 0.0f, 1.0f ); + + // snap u (u = 0.0) + HitPos = tri.v[0] + ( tri.v[2] - tri.v[0] ) * v; + return true; + } + + // special corner 2 + if( ( ndx == ( width - 2 ) ) && ( v < 0.0f ) && ( u < 0.0f ) ) + { + HitPos = tri.v[0]; + return true; + } + } + + // test edge 3 + for( int ndx = 0; ndx < ( height - 1 ); ndx++ ) + { + GetVert( ( ndx * width ) + ( ( 2 * width ) - 1 ), tri.v[0] ); + GetVert( ( ndx * width ) + ( width - 1 ), tri.v[1] ); + GetVert( ( ndx * width ) + ( ( 2 * width ) - 2 ), tri.v[2] ); + + ComputeIntersectionBarycentricCoordinates( ray, tri.v[0], tri.v[1], tri.v[2], u, v ); + + // along edge (0.0 < u < 1.0) and right (v < 0.0) + if( ( u >= LOWER_TOLERANCE ) && ( u <= UPPER_TOLERANCE ) && ( v < 0.0f ) ) + { + u = clamp( u, 0.0f, 1.0f ); + + // snap v (v = 0.0) + HitPos = tri.v[0] + ( tri.v[1] - tri.v[0] ) * u; + return true; + } + + // special corner 3 + if( ( ndx == 0 ) && ( u > 1.0f ) && ( v < 0.0f ) ) + { + HitPos = tri.v[1]; + return true; + } + } + + return false; + +#undef LOWER_TOLERANCE +#undef UPPER_TOLERANCE +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapDisp::Flip( int flipType ) +{ + int width = GetWidth(); + int height = GetHeight(); + + switch( flipType ) + { + case FLIP_HORIZONTAL: + { + return; + } + case FLIP_VERTICAL: + { + return; + } + case FLIP_TRANSPOSE: + { + for( int ndxHeight = 0; ndxHeight < height; ndxHeight++ ) + { + for( int ndxWidth = ndxHeight; ndxWidth < width; ndxWidth++ ) + { + float dist1 = GetFieldDistance( ( ndxHeight * width ) + ndxWidth ); + float dist2 = GetFieldDistance( ( ndxWidth * height ) + ndxHeight ); + SetFieldDistance( ( ndxHeight * width ) + ndxWidth, dist2 ); + SetFieldDistance( ( ndxWidth * height ) + ndxHeight, dist1 ); + + Vector v1, v2; + GetFieldVector( ( ndxHeight * width ) + ndxWidth, v1 ); + GetFieldVector( ( ndxWidth * height ) + ndxHeight, v2 ); + SetFieldVector( ( ndxHeight * width ) + ndxWidth, v2 ); + SetFieldVector( ( ndxWidth * height ) + ndxHeight, v1 ); + + GetSubdivPosition( ( ndxHeight * width ) + ndxWidth, v1 ); + GetSubdivPosition( ( ndxWidth * height ) + ndxHeight, v2 ); + SetSubdivPosition( ( ndxHeight * width ) + ndxWidth, v2 ); + SetSubdivPosition( ( ndxWidth * height ) + ndxHeight, v1 ); + + GetSubdivNormal( ( ndxHeight * width ) + ndxWidth, v1 ); + GetSubdivNormal( ( ndxWidth * height ) + ndxHeight, v2 ); + SetSubdivNormal( ( ndxHeight * width ) + ndxWidth, v2 ); + SetSubdivNormal( ( ndxWidth * height ) + ndxHeight, v1 ); + + float alpha1 = GetAlpha( ( ndxHeight * width ) + ndxWidth ); + float alpha2 = GetAlpha( ( ndxWidth * height ) + ndxHeight ); + SetAlpha( ( ndxHeight * width ) + ndxWidth, alpha2 ); + SetAlpha( ( ndxWidth * height ) + ndxHeight, alpha1 ); + } + } + return; + } + default: + { + return; + } + } +} + + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapDisp::UpdateWalkable( void ) +{ + // Set the walkable tag. + int nTriCount = GetTriCount(); + for ( int iTri = 0; iTri < nTriCount; ++iTri ) + { + Vector v1, v2, v3; + GetTriPos( iTri, v1, v2, v3 ); + + Vector vecEdge1, vecEdge2; + vecEdge1 = v2 - v1; + vecEdge2 = v3 - v1; + + Vector vecTriNormal; + CrossProduct( vecEdge2, vecEdge1, vecTriNormal ); + VectorNormalize( vecTriNormal ); + + ResetTriTag( iTri, COREDISPTRI_TAG_WALKABLE ); + if ( vecTriNormal.z >= WALKABLE_NORMAL_VALUE ) + { + SetTriTag( iTri, COREDISPTRI_TAG_WALKABLE ); + } + } + + // Create the walkable render list. + m_aWalkableVerts.RemoveAll(); + m_aWalkableIndices.RemoveAll(); + m_aForcedWalkableIndices.RemoveAll(); + + for ( int iTri = 0; iTri < nTriCount; ++iTri ) + { + if ( !IsTriWalkable( iTri ) ) + { + unsigned short triIndices[3]; + unsigned short newTriIndices[3]; + GetTriIndices( iTri, triIndices[0], triIndices[1], triIndices[2] ); + + newTriIndices[0] = m_aWalkableVerts.AddToTail( m_CoreDispInfo.GetDispVert( triIndices[0] ) ); + newTriIndices[1] = m_aWalkableVerts.AddToTail( m_CoreDispInfo.GetDispVert( triIndices[1] ) ); + newTriIndices[2] = m_aWalkableVerts.AddToTail( m_CoreDispInfo.GetDispVert( triIndices[2] ) ); + + if ( IsTriTag( iTri, COREDISPTRI_TAG_FORCE_WALKABLE_BIT ) ) + { + m_aForcedWalkableIndices.AddToTail( newTriIndices[0] ); + m_aForcedWalkableIndices.AddToTail( newTriIndices[1] ); + m_aForcedWalkableIndices.AddToTail( newTriIndices[2] ); + } + else + { + m_aWalkableIndices.AddToTail( newTriIndices[0] ); + m_aWalkableIndices.AddToTail( newTriIndices[1] ); + m_aWalkableIndices.AddToTail( newTriIndices[2] ); + } + } + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapDisp::UpdateBuildable( void ) +{ + // Set the buildable tag. + int nTriCount = GetTriCount(); + for ( int iTri = 0; iTri < nTriCount; ++iTri ) + { + Vector v1, v2, v3; + GetTriPos( iTri, v1, v2, v3 ); + + Vector vecEdge1, vecEdge2; + vecEdge1 = v2 - v1; + vecEdge2 = v3 - v1; + + Vector vecTriNormal; + CrossProduct( vecEdge2, vecEdge1, vecTriNormal ); + VectorNormalize( vecTriNormal ); + + ResetTriTag( iTri, COREDISPTRI_TAG_BUILDABLE ); + if ( vecTriNormal.z >= BUILDABLE_NORMAL_VALUE ) + { + SetTriTag( iTri, COREDISPTRI_TAG_BUILDABLE ); + } + } + + // Create the buildable render list. + m_aBuildableVerts.RemoveAll(); + m_aBuildableIndices.RemoveAll(); + m_aForcedBuildableIndices.RemoveAll(); + + for ( int iTri = 0; iTri < nTriCount; ++iTri ) + { + if ( !IsTriBuildable( iTri ) ) + { + unsigned short triIndices[3]; + unsigned short newTriIndices[3]; + GetTriIndices( iTri, triIndices[0], triIndices[1], triIndices[2] ); + + newTriIndices[0] = m_aBuildableVerts.AddToTail( m_CoreDispInfo.GetDispVert( triIndices[0] ) ); + newTriIndices[1] = m_aBuildableVerts.AddToTail( m_CoreDispInfo.GetDispVert( triIndices[1] ) ); + newTriIndices[2] = m_aBuildableVerts.AddToTail( m_CoreDispInfo.GetDispVert( triIndices[2] ) ); + + if ( IsTriTag( iTri, COREDISPTRI_TAG_FORCE_BUILDABLE_BIT ) ) + { + m_aForcedBuildableIndices.AddToTail( newTriIndices[0] ); + m_aForcedBuildableIndices.AddToTail( newTriIndices[1] ); + m_aForcedBuildableIndices.AddToTail( newTriIndices[2] ); + } + else + { + m_aBuildableIndices.AddToTail( newTriIndices[0] ); + m_aBuildableIndices.AddToTail( newTriIndices[1] ); + m_aBuildableIndices.AddToTail( newTriIndices[2] ); + } + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMapDisp::UpdateTriRemove( void ) +{ + // Create the remove render list. + m_aRemoveVerts.RemoveAll(); + m_aRemoveIndices.RemoveAll(); + + int nTriCount = GetTriCount(); + for ( int iTri = 0; iTri < nTriCount; ++iTri ) + { + if ( IsTriRemove( iTri ) ) + { + unsigned short triIndices[3]; + unsigned short newTriIndices[3]; + GetTriIndices( iTri, triIndices[0], triIndices[1], triIndices[2] ); + + newTriIndices[0] = m_aRemoveVerts.AddToTail( m_CoreDispInfo.GetDispVert( triIndices[0] ) ); + newTriIndices[1] = m_aRemoveVerts.AddToTail( m_CoreDispInfo.GetDispVert( triIndices[1] ) ); + newTriIndices[2] = m_aRemoveVerts.AddToTail( m_CoreDispInfo.GetDispVert( triIndices[2] ) ); + + m_aRemoveIndices.AddToTail( newTriIndices[0] ); + m_aRemoveIndices.AddToTail( newTriIndices[1] ); + m_aRemoveIndices.AddToTail( newTriIndices[2] ); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMapDisp::CreateShoreOverlays( CMapFace *pFace, Shoreline_t *pShoreline ) +{ + // Do the bounds volumes intersect? + Vector vecDispMin, vecDispMax; + GetBoundingBox( vecDispMin, vecDispMax ); + Vector vecSolidMin, vecSolidMax; + CMapSolid *pSolid = static_cast<CMapSolid*>( pFace->GetParent() ); + pSolid->GetCullBox( vecSolidMin, vecSolidMax ); + if ( !IsBoxIntersectingBox( vecDispMin, vecDispMax, vecSolidMin, vecSolidMax ) ) + return; + + int nTriangleCount = TriangleCount(); + for ( int iTri = 0; iTri < nTriangleCount; ++iTri ) + { + unsigned short i[3]; + GetTriIndices( iTri, i[0], i[1], i[2] ); + Vector v[3]; + GetVert( i[0], v[0] ); + GetVert( i[1], v[1] ); + GetVert( i[2], v[2] ); + + Vector vU, vV; + VectorSubtract( v[1], v[0], vU ); + VectorSubtract( v[2], v[0], vV ); + + Vector2D vecIntersect[2]; + Vector4D plane; + plane.Init( pFace->plane.normal.x, pFace->plane.normal.y, pFace->plane.normal.z, pFace->plane.dist ); + int nCount = IntersectTriangleWithPlaneBarycentric( v[0], vU, vV, plane, vecIntersect ); + if ( nCount != 2 ) + continue; + + // Find the normal pointing toward the shore. + Vector vecPoints[2]; + vecPoints[0] = v[0] + ( vU * vecIntersect[0].x ) + ( vV * vecIntersect[0].y ); + vecPoints[1] = v[0] + ( vU * vecIntersect[1].x ) + ( vV * vecIntersect[1].y ); + + // Create shore edge normal. + Vector vecEdge, vecNormal; + VectorSubtract( vecPoints[1], vecPoints[0], vecEdge ); + VectorNormalize( vecEdge ); + CrossProduct( vecEdge, pFace->plane.normal, vecNormal ); + float flEdgeDist = DotProduct( vecNormal, vecPoints[0] ); + + for ( int iVert = 0; iVert < 3; ++iVert ) + { + float flDist = DotProduct( vecNormal, v[iVert] ) - flEdgeDist; + if ( flDist > 0.0f ) + { + float flDist2 = DotProduct( pFace->plane.normal, v[iVert] ) - pFace->plane.dist; + if ( flDist2 < 0.0f ) + { + vecNormal.Negate(); + break; + } + } + } + + if ( !VectorsAreEqual( vecPoints[0], vecPoints[1], 0.1f ) ) + { + pShoreline->AddSegment( vecPoints[0], vecPoints[1], vecNormal, pFace->plane.dist, pFace, GetEditHandle() ); + } + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +static void RenderDisplacementNormals( CCoreDispInfo& coreDispInfo, int numVerts ) +{ + Vector points[4], normal; + + CMeshBuilder meshBuilder; + CMatRenderContextPtr pRenderContext( MaterialSystemInterface() ); + IMesh* pMesh = pRenderContext->GetDynamicMesh(); + + meshBuilder.Begin( pMesh, MATERIAL_LINES, numVerts ); + + for( int i = 0; i < numVerts; i++ ) + { + coreDispInfo.GetVert( i, points[0] ); + coreDispInfo.GetNormal( i, normal ); + + meshBuilder.Color3f( 0.0f, 1.0f, 0.0f ); + meshBuilder.Position3f( points[0][0], points[0][1], points[0][2] ); + meshBuilder.AdvanceVertex(); + + meshBuilder.Color3f( 0.0f, 1.0f, 0.0f ); + meshBuilder.Position3f( points[0][0] + ( normal[0] * 10.0f ), + points[0][1] + ( normal[1] * 10.0f ), + points[0][2] + ( normal[2] * 10.0f ) ); + meshBuilder.AdvanceVertex(); + } + meshBuilder.End(); + pMesh->Draw(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +static void RenderDisplacementTangentsS( CCoreDispInfo &coreDispInfo, int numVerts ) +{ + Vector points[4], tangentS; + + CMeshBuilder meshBuilder; + CMatRenderContextPtr pRenderContext( MaterialSystemInterface() ); + IMesh *pMesh = pRenderContext->GetDynamicMesh(); + + meshBuilder.Begin( pMesh, MATERIAL_LINES, numVerts ); + + for( int i = 0; i < numVerts; i++ ) + { + coreDispInfo.GetVert( i, points[0] ); + coreDispInfo.GetTangentS( i, tangentS ); + + meshBuilder.Color3f( 1.0f, 0.0f, 0.0f ); + meshBuilder.Position3f( points[0][0], points[0][1], points[0][2] ); + meshBuilder.AdvanceVertex(); + + meshBuilder.Color3f( 1.0f, 0.0f, 0.0f ); + meshBuilder.Position3f( points[0][0] + ( tangentS[0] * 10.0f ), + points[0][1] + ( tangentS[1] * 10.0f ), + points[0][2] + ( tangentS[2] * 10.0f ) ); + meshBuilder.AdvanceVertex(); + } + + meshBuilder.End(); + pMesh->Draw(); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +static void RenderDisplacementTangentsT( CCoreDispInfo &coreDispInfo, int numVerts ) +{ + Vector points[4], tangentT; + + CMeshBuilder meshBuilder; + CMatRenderContextPtr pRenderContext( MaterialSystemInterface() ); + IMesh *pMesh = pRenderContext->GetDynamicMesh(); + + meshBuilder.Begin( pMesh, MATERIAL_LINES, numVerts ); + + for( int i = 0; i < numVerts; i++ ) + { + coreDispInfo.GetVert( i, points[0] ); + coreDispInfo.GetTangentT( i, tangentT ); + + meshBuilder.Color3f( 0.0f, 0.0f, 1.0f ); + meshBuilder.Position3f( points[0][0], points[0][1], points[0][2] ); + meshBuilder.AdvanceVertex(); + + meshBuilder.Color3f( 0.0f, 0.0f, 1.0f ); + meshBuilder.Position3f( points[0][0] + ( tangentT[0] * 10.0f ), + points[0][1] + ( tangentT[1] * 10.0f ), + points[0][2] + ( tangentT[2] * 10.0f ) ); + meshBuilder.AdvanceVertex(); + } + + meshBuilder.End(); + pMesh->Draw(); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +static void RenderFaceVertexNormals( CCoreDispInfo& coreDispInfo ) +{ + Vector points[4], normal; + + CMeshBuilder meshBuilder; + CMatRenderContextPtr pRenderContext( MaterialSystemInterface() ); + IMesh *pMesh = pRenderContext->GetDynamicMesh(); + meshBuilder.Begin( pMesh, MATERIAL_LINES, 4 ); + + CCoreDispSurface *pSurf = coreDispInfo.GetSurface(); + for( int i = 0; i < 4; i++ ) + { + pSurf->GetPoint( i, points[0] ); + pSurf->GetPointNormal( i, normal ); + + meshBuilder.Color3f( 1.0f, 0.0f, 0.0f ); + meshBuilder.Position3f( points[0][0], points[0][1], points[0][2] ); + meshBuilder.AdvanceVertex(); + + meshBuilder.Color3f( 1.0f, 0.0f, 0.0f ); + meshBuilder.Position3f( points[0][0] + ( normal[0] * 25.0f ), + points[0][1] + ( normal[1] * 25.0f ), + points[0][2] + ( normal[2] * 25.0f ) ); + meshBuilder.AdvanceVertex(); + } + meshBuilder.End(); + pMesh->Draw(); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +static void RenderDisplacementVectorField( CCoreDispInfo& coreDispInfo, int numVerts ) +{ + Vector points[4], normal; + + CMeshBuilder meshBuilder; + CMatRenderContextPtr pRenderContext( MaterialSystemInterface() ); + IMesh *pMesh = pRenderContext->GetDynamicMesh(); + meshBuilder.Begin( pMesh, MATERIAL_LINES, numVerts ); + + for( int i = 0; i < numVerts; i++ ) + { + coreDispInfo.GetVert( i, points[0] ); + coreDispInfo.GetFieldVector( i, normal ); + + meshBuilder.Color3f( 1.0f, 1.0f, 0.0f ); + meshBuilder.Position3f( points[0][0], points[0][1], points[0][2] ); + meshBuilder.AdvanceVertex(); + + meshBuilder.Color3f( 1.0f, 1.0f, 0.0f ); + meshBuilder.Position3f( points[0][0] + ( normal[0] * 50.0f ), + points[0][1] + ( normal[1] * 50.0f ), + points[0][2] + ( normal[2] * 50.0f ) ); + meshBuilder.AdvanceVertex(); + } + meshBuilder.End(); + pMesh->Draw(); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +static void RenderSubdivPositions( CCoreDispInfo& coreDispInfo, int numVerts ) +{ + Vector pt, normal; + + CMeshBuilder meshBuilder; + CMatRenderContextPtr pRenderContext( MaterialSystemInterface() ); + IMesh *pMesh = pRenderContext->GetDynamicMesh(); + meshBuilder.Begin( pMesh, MATERIAL_LINES, numVerts ); + + for( int i = 0; i < numVerts; i++ ) + { + coreDispInfo.GetFlatVert( i, pt ); + coreDispInfo.GetSubdivPosition( i, normal ); + + meshBuilder.Position3f( pt[0], pt[1], pt[2] ); + meshBuilder.Color3f( 1.0f, 0.0f, 1.0f ); + meshBuilder.AdvanceVertex(); + + meshBuilder.Position3f( pt[0] + normal[0], pt[1] + normal[1], pt[2] + normal[2] ); + meshBuilder.Color3f( 1.0f, 0.0f, 1.0f ); + meshBuilder.AdvanceVertex(); + } + meshBuilder.End(); + pMesh->Draw(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +static void RenderDisplacementEdges( CCoreDispInfo& coreDispInfo ) +{ + Vector points[4]; + + CMeshBuilder meshBuilder; + CMatRenderContextPtr pRenderContext( MaterialSystemInterface() ); + IMesh *pMesh = pRenderContext->GetDynamicMesh(); + meshBuilder.Begin( pMesh, MATERIAL_LINES, 4 ); + + CCoreDispSurface *pSurf = coreDispInfo.GetSurface(); + pSurf->GetPoint( 0, points[0] ); + pSurf->GetPoint( 1, points[1] ); + pSurf->GetPoint( 2, points[2] ); + pSurf->GetPoint( 3, points[3] ); + + meshBuilder.Color3f( 1.0f, 0.0f, 0.0f ); + meshBuilder.Position3f( points[0][0], points[0][1], points[0][2] ); + meshBuilder.AdvanceVertex(); + meshBuilder.Color3f( 1.0f, 0.0f, 0.0f ); + meshBuilder.Position3f( points[1][0], points[1][1], points[1][2] ); + meshBuilder.AdvanceVertex(); + + meshBuilder.Color3f( 0.0f, 1.0f, 0.0f ); + meshBuilder.Position3f( points[1][0], points[1][1], points[1][2] ); + meshBuilder.AdvanceVertex(); + meshBuilder.Color3f( 0.0f, 1.0f, 0.0f ); + meshBuilder.Position3f( points[2][0], points[2][1], points[2][2] ); + meshBuilder.AdvanceVertex(); + + meshBuilder.Color3f( 0.0f, 0.0f, 1.0f ); + meshBuilder.Position3f( points[2][0], points[2][1], points[2][2] ); + meshBuilder.AdvanceVertex(); + meshBuilder.Color3f( 0.0f, 0.0f, 1.0f ); + meshBuilder.Position3f( points[3][0], points[3][1], points[3][2] ); + meshBuilder.AdvanceVertex(); + + meshBuilder.Color3f( 1.0f, 0.0f, 1.0f ); + meshBuilder.Position3f( points[3][0], points[3][1], points[3][2] ); + meshBuilder.AdvanceVertex(); + meshBuilder.Color3f( 1.0f, 0.0f, 1.0f ); + meshBuilder.Position3f( points[0][0], points[0][1], points[0][2] ); + meshBuilder.AdvanceVertex(); + + meshBuilder.End(); + pMesh->Draw(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapDisp::RenderDisAllowedVerts( CRender3D *pRender ) +{ + CBitVec<MAX_DISPVERTS> &allowedVerts = m_CoreDispInfo.GetAllowedVerts(); + + int nVertCount = GetSize(); + for ( int iVert = 0; iVert < nVertCount; ++iVert ) + { + if ( allowedVerts.Get( iVert ) == 0 ) + { + Vector vecPos; + GetVert( iVert, vecPos ); + + // Draw a box at this point! + Vector vecPointMin, vecPointMax; + for ( int iAxis = 0; iAxis < 3; ++iAxis ) + { + vecPointMin[iAxis] = vecPos[iAxis] - 5.0f; + vecPointMax[iAxis] = vecPos[iAxis] + 5.0f; + } + pRender->RenderBox( vecPointMin, vecPointMax, 255, 0, 255, SELECT_NONE ); + } + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapDisp::Render3DDebug( CRender3D *pRender, bool isSelected ) +{ +#if 0 + pRender->SetRenderMode( RENDER_MODE_WIREFRAME ); + RenderDisplacementNormals( m_CoreDispInfo, MAPDISP_MAX_VERTS ); + RenderDisplacementTangentsS( m_CoreDispInfo, MAPDISP_MAX_VERTS ); + RenderDisplacementTangentsT( m_CoreDispInfo, MAPDISP_MAX_VERTS ); + +// RenderFaceVertexNormals( m_CoreDispInfo ); + RenderDisplacementVectorField( m_CoreDispInfo, MAPDISP_MAX_VERTS ); + RenderSubdivPositions( m_CoreDispInfo, GetSize() ); +// RenderDisplacementEdges( m_CoreDispInfo ); +#endif +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapDisp::CalcColor( CRender3D *pRender, bool bIsSelected, + SelectionState_t faceSelectionState, + Color &pColor ) +{ + // Get the current render mode. + EditorRenderMode_t renderMode = pRender->GetCurrentRenderMode(); + + switch ( renderMode ) + { + case RENDER_MODE_TEXTURED: + case RENDER_MODE_TEXTURED_SHADED: + case RENDER_MODE_LIGHT_PREVIEW2: + case RENDER_MODE_LIGHT_PREVIEW_RAYTRACED: + { + break; + } + case RENDER_MODE_SELECTION_OVERLAY: + { + if ( faceSelectionState == SELECT_MULTI_PARTIAL ) + { + pColor[2] = 100; + pColor[3] = 64; + } + else if ( ( faceSelectionState == SELECT_NORMAL ) || bIsSelected ) + { + SelectFaceColor( pColor ); + pColor[3] = 64; + } + break; + } + case RENDER_MODE_LIGHTMAP_GRID: + { + CMapFace *pFace = ( CMapFace* )GetParent(); + if ( bIsSelected ) + { + SelectFaceColor( pColor ); + } + else if (pFace->texture.nLightmapScale > DEFAULT_LIGHTMAP_SCALE) + { + pColor[0] = 150; + } + else if (pFace->texture.nLightmapScale < DEFAULT_LIGHTMAP_SCALE) + { + pColor[2] = 100; + } + break; + } + case RENDER_MODE_TRANSLUCENT_FLAT: + case RENDER_MODE_FLAT: + { + if ( bIsSelected ) + { + SelectFaceColor( pColor ); + } + + break; + } + case RENDER_MODE_WIREFRAME: + { + if ( bIsSelected ) + { + SelectEdgeColor( pColor ); + } + break; + } + case RENDER_MODE_SMOOTHING_GROUP: + { + // Render the non-smoothing group faces in white, yellow for the others. + CMapDoc *pDoc = CMapDoc::GetActiveMapDoc(); + if ( pDoc ) + { + CMapFace *pFace = ( CMapFace* )GetParent(); + int iGroup = pDoc->GetSmoothingGroupVisual(); + if ( pFace->InSmoothingGroup( iGroup ) ) + { + pColor[2] = 0; + } + } + + break; + } + default: + { + assert( 0 ); + break; + } + } +} + +void CMapDisp::Render2D(CRender2D *pRender, bool bIsSelected, SelectionState_t faceSelectionState ) +{ + pRender->PushRenderMode( RENDER_MODE_WIREFRAME ); + + pRender->DrawDisplacement( &m_CoreDispInfo ); + + pRender->PopRenderMode(); +} + +void CMapDisp::AddShadowingTriangles( CUtlVector<Vector> &tri_list ) +{ + // add lighting preview triangles + CoreDispVert_t *pVert = m_CoreDispInfo.GetDispVertList(); + unsigned short *pIndex = m_CoreDispInfo.GetRenderIndexList(); + int numIndices = m_CoreDispInfo.GetRenderIndexCount(); + for ( int i = 0; i < numIndices; i += 3 ) + { + for( int v = 0; v < 3; v++ ) + tri_list.AddToTail( pVert[pIndex[i+v]].m_Vert ); + } +} + +//----------------------------------------------------------------------------- +// NOTE: most of the rendering mode is set in the parent face render call!!! +//----------------------------------------------------------------------------- +void CMapDisp::Render3D( CRender3D *pRender, bool bIsSelected, SelectionState_t faceSelectionState ) +{ + // Get the current rendermode. + EditorRenderMode_t renderMode = pRender->GetCurrentRenderMode(); + + if ( renderMode == RENDER_MODE_SELECTION_OVERLAY ) + { + RenderOverlaySurface( pRender, bIsSelected, faceSelectionState ); + } + else + { + RenderSurface( pRender, bIsSelected, faceSelectionState ); + + // Note: This will cause the wireframe to render twice in selection due to + // the multiplass operations at the solid and face levels (the render + // portion of the hammer code needs to be reworked there). + if ( renderMode != RENDER_MODE_WIREFRAME && bIsSelected ) + { + // This renders wireframe twice in selection! + RenderWireframeSurface( pRender, bIsSelected, faceSelectionState ); + } + } + + // Note: the rendermode == textured is so that this only gets rendered + // once per frame. + bool bDispWalkableMode = CMapDoc::GetActiveMapDoc()->IsDispDrawWalkable(); + if ( bDispWalkableMode && RenderingModeIsTextured(renderMode)) + { + RenderWalkableSurface( pRender, bIsSelected, faceSelectionState ); + } + + // Note: the rendermode == textured is so that this only gets rendered + // once per frame. + bool bDispBuildableMode = CMapDoc::GetActiveMapDoc()->IsDispDrawBuildable(); + if ( bDispBuildableMode && RenderingModeIsTextured( renderMode )) + { + RenderBuildableSurface( pRender, bIsSelected, faceSelectionState ); + } + + // Note: the rendermode == textured is so that this only gets rendered + // once per frame. + bool bDispRemoveMode = CMapDoc::GetActiveMapDoc()->IsDispDrawRemove(); + if ( bDispRemoveMode && RenderingModeIsTextured( renderMode )) + { + RenderRemoveSurface( pRender, bIsSelected, faceSelectionState ); + } + + bool bDispRemovedVertMode = CMapDoc::GetActiveMapDoc()->IsDispDrawRemovedVerts(); + if ( bDispRemovedVertMode && RenderingModeIsTextured( renderMode ) ) + { + RenderDisAllowedVerts( pRender ); + } + + // Render debug information. +// Render3DDebug( pRender, bIsSelected ); +} + +//----------------------------------------------------------------------------- +// Purpose: Render the displacement surface. +//----------------------------------------------------------------------------- +void CMapDisp::RenderOverlaySurface( CRender3D *pRender, bool bIsSelected, SelectionState_t faceSelectionState ) +{ + if ( HasSelectMask() ) + return; + + Color color( 255, 255, 255, 255 ); + CalcColor( pRender, bIsSelected, faceSelectionState, color ); + + int nVertCount = m_CoreDispInfo.GetSize(); + int nIndexCount = m_CoreDispInfo.GetRenderIndexCount(); + + CMeshBuilder meshBuilder; + CMatRenderContextPtr pRenderContext( MaterialSystemInterface() ); + IMesh *pMesh = pRenderContext->GetDynamicMesh(); + meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, nVertCount, nIndexCount ); + + CoreDispVert_t *pVert = m_CoreDispInfo.GetDispVertList(); + for (int i = 0; i < nVertCount; ++i ) + { + meshBuilder.Position3fv( pVert[i].m_Vert.Base() ); + meshBuilder.Color4ub( color[0], color[1], color[2], color[3] ); + meshBuilder.Normal3fv( pVert[i].m_Normal.Base() ); + meshBuilder.AdvanceVertex(); + } + + unsigned short *pIndex = m_CoreDispInfo.GetRenderIndexList(); + for ( int i = 0; i < nIndexCount; ++i ) + { + meshBuilder.Index( pIndex[i] ); + meshBuilder.AdvanceIndex(); + } + + meshBuilder.End(); + pMesh->Draw(); +} + +//----------------------------------------------------------------------------- +// Purpose: Render the displacement surface with a vertex alpha (blending). +//----------------------------------------------------------------------------- +void CMapDisp::RenderSurface( CRender3D *pRender, bool bIsSelected, SelectionState_t faceSelectionState ) +{ + Color color( 255, 255, 255, 255 ); + CalcColor( pRender, bIsSelected, faceSelectionState, color ); + + int numVerts = m_CoreDispInfo.GetSize(); + int numIndices = m_CoreDispInfo.GetRenderIndexCount(); + + CMeshBuilder meshBuilder; + CMatRenderContextPtr pRenderContext( MaterialSystemInterface() ); + IMesh *pMesh = pRenderContext->GetDynamicMesh(); + meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, numVerts, numIndices ); + + CoreDispVert_t *pVert = m_CoreDispInfo.GetDispVertList(); + for (int i = 0; i < numVerts; ++i ) + { + meshBuilder.Position3fv( pVert[i].m_Vert.Base() ); + meshBuilder.Color4ub( color[0], color[1], color[2], 255 - ( unsigned char )( pVert[i].m_Alpha ) ); + meshBuilder.Normal3fv( pVert[i].m_Normal.Base() ); + meshBuilder.TangentS3fv( pVert[i].m_TangentS.Base() ); + meshBuilder.TangentT3fv( pVert[i].m_TangentT.Base() ); + meshBuilder.TexCoord2fv( 0, pVert[i].m_TexCoord.Base() ); + meshBuilder.TexCoord2fv( 1, pVert[i].m_LuxelCoords[0].Base() ); + meshBuilder.AdvanceVertex(); + } + + unsigned short *pIndex = m_CoreDispInfo.GetRenderIndexList(); + int nTriCount = numIndices / 3; + for ( int nTri = 0; nTri < nTriCount; ++nTri ) + { + if ( !IsTriRemove( nTri ) ) + { + int nIndex = nTri * 3; + meshBuilder.Index( pIndex[nIndex] ); + meshBuilder.AdvanceIndex(); + meshBuilder.Index( pIndex[nIndex+1] ); + meshBuilder.AdvanceIndex(); + meshBuilder.Index( pIndex[nIndex+2] ); + meshBuilder.AdvanceIndex(); + } + } + + meshBuilder.End(); + pMesh->Draw(); +} + +//----------------------------------------------------------------------------- +// Purpose: Render the displacement surface with walkable data. +//----------------------------------------------------------------------------- +void CMapDisp::RenderWalkableSurface( CRender3D *pRender, bool bIsSelected, SelectionState_t faceSelectionState ) +{ + // Normal + for ( int iPass = 0; iPass < 2; ++iPass ) + { + Color color; + if ( iPass == 0 ) + { + pRender->PushRenderMode( RENDER_MODE_TRANSLUCENT_FLAT ); + color.SetColor( 255, 255, 0, 64 ); + CalcColor( pRender, false, faceSelectionState, color ); + } + else + { + pRender->PushRenderMode( RENDER_MODE_WIREFRAME ); + color.SetColor( 255, 255, 0, 255 ); + CalcColor( pRender, false, faceSelectionState, color ); + } + + int nVertCount = m_aWalkableVerts.Count(); + int nIndexCount = m_aWalkableIndices.Count(); + + CMeshBuilder meshBuilder; + CMatRenderContextPtr pRenderContext( MaterialSystemInterface() ); + IMesh *pMesh = pRenderContext->GetDynamicMesh(); + meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, nVertCount, nIndexCount ); + + CoreDispVert_t **ppVerts = m_aWalkableVerts.Base(); + for (int i = 0; i < nVertCount; ++i ) + { + CoreDispVert_t *pVert = ppVerts[i]; + + meshBuilder.Position3fv( pVert->m_Vert.Base() ); + meshBuilder.Color4ub( color[0], color[1], color[2], color[3] ); + meshBuilder.Normal3fv( pVert->m_Normal.Base() ); + meshBuilder.AdvanceVertex(); + } + + unsigned short *pIndex = m_aWalkableIndices.Base(); + for ( int i = 0; i < nIndexCount; ++i ) + { + meshBuilder.Index( pIndex[i] ); + meshBuilder.AdvanceIndex(); + } + + meshBuilder.End(); + pMesh->Draw(); + + pRender->PopRenderMode(); + } + + // Forced + for ( int iPass = 0; iPass < 2; ++iPass ) + { + Color color; + if ( iPass == 0 ) + { + pRender->PushRenderMode( RENDER_MODE_TRANSLUCENT_FLAT ); + color.SetColor( 0, 255, 0, 64 ); + CalcColor( pRender, false, faceSelectionState, color ); + } + else + { + pRender->PushRenderMode( RENDER_MODE_WIREFRAME ); + color.SetColor( 0, 255, 0, 255 ); + CalcColor( pRender, false, faceSelectionState, color ); + } + + int nVertCount = m_aWalkableVerts.Count(); + int nIndexCount = m_aForcedWalkableIndices.Count(); + + CMeshBuilder meshBuilder; + CMatRenderContextPtr pRenderContext( MaterialSystemInterface() ); + IMesh *pMesh = pRenderContext->GetDynamicMesh(); + meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, nVertCount, nIndexCount ); + + CoreDispVert_t **ppVerts = m_aWalkableVerts.Base(); + for (int i = 0; i < nVertCount; ++i ) + { + CoreDispVert_t *pVert = ppVerts[i]; + + meshBuilder.Position3fv( pVert->m_Vert.Base() ); + meshBuilder.Color4ub( color[0], color[1], color[2], color[3] ); + meshBuilder.Normal3fv( pVert->m_Normal.Base() ); + meshBuilder.AdvanceVertex(); + } + + unsigned short *pIndex = m_aForcedWalkableIndices.Base(); + for ( int i = 0; i < nIndexCount; ++i ) + { + meshBuilder.Index( pIndex[i] ); + meshBuilder.AdvanceIndex(); + } + + meshBuilder.End(); + pMesh->Draw(); + + pRender->PopRenderMode(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Render the displacement surface with buildable data. +//----------------------------------------------------------------------------- +void CMapDisp::RenderBuildableSurface( CRender3D *pRender, bool bIsSelected, SelectionState_t faceSelectionState ) +{ + // Normal + for ( int iPass = 0; iPass < 2; ++iPass ) + { + Color color; + if ( iPass == 0 ) + { + pRender->PushRenderMode( RENDER_MODE_TRANSLUCENT_FLAT ); + color.SetColor( 255, 100, 25, 64 ); + CalcColor( pRender, false, faceSelectionState, color ); + } + else + { + pRender->PushRenderMode( RENDER_MODE_WIREFRAME ); + color.SetColor( 255, 255, 0, 255 ); + CalcColor( pRender, false, faceSelectionState, color ); + } + + int nVertCount = m_aBuildableVerts.Count(); + int nIndexCount = m_aBuildableIndices.Count(); + + CMeshBuilder meshBuilder; + CMatRenderContextPtr pRenderContext( MaterialSystemInterface() ); + IMesh *pMesh = pRenderContext->GetDynamicMesh(); + meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, nVertCount, nIndexCount ); + + CoreDispVert_t **ppVerts = m_aBuildableVerts.Base(); + for (int i = 0; i < nVertCount; ++i ) + { + CoreDispVert_t *pVert = ppVerts[i]; + + meshBuilder.Position3fv( pVert->m_Vert.Base() ); + meshBuilder.Color4ub( color[0], color[1], color[2], color[3] ); + meshBuilder.Normal3fv( pVert->m_Normal.Base() ); + meshBuilder.AdvanceVertex(); + } + + unsigned short *pIndex = m_aBuildableIndices.Base(); + for ( int i = 0; i < nIndexCount; ++i ) + { + meshBuilder.Index( pIndex[i] ); + meshBuilder.AdvanceIndex(); + } + + meshBuilder.End(); + pMesh->Draw(); + + pRender->PopRenderMode(); + } + + // Forced + for ( int iPass = 0; iPass < 2; ++iPass ) + { + Color color; + if ( iPass == 0 ) + { + pRender->PushRenderMode( RENDER_MODE_TRANSLUCENT_FLAT ); + color.SetColor( 0, 0, 255, 64 ); + CalcColor( pRender, false, faceSelectionState, color ); + } + else + { + pRender->PushRenderMode( RENDER_MODE_WIREFRAME ); + color.SetColor( 0, 0, 255, 255 ); + CalcColor( pRender, false, faceSelectionState, color ); + } + + int nVertCount = m_aBuildableVerts.Count(); + int nIndexCount = m_aForcedBuildableIndices.Count(); + + CMeshBuilder meshBuilder; + CMatRenderContextPtr pRenderContext( MaterialSystemInterface() ); + IMesh *pMesh = pRenderContext->GetDynamicMesh(); + meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, nVertCount, nIndexCount ); + + CoreDispVert_t **ppVerts = m_aBuildableVerts.Base(); + for (int i = 0; i < nVertCount; ++i ) + { + CoreDispVert_t *pVert = ppVerts[i]; + + meshBuilder.Position3fv( pVert->m_Vert.Base() ); + meshBuilder.Color4ub( color[0], color[1], color[2], color[3] ); + meshBuilder.Normal3fv( pVert->m_Normal.Base() ); + meshBuilder.AdvanceVertex(); + } + + unsigned short *pIndex = m_aForcedBuildableIndices.Base(); + for ( int i = 0; i < nIndexCount; ++i ) + { + meshBuilder.Index( pIndex[i] ); + meshBuilder.AdvanceIndex(); + } + + meshBuilder.End(); + pMesh->Draw(); + + pRender->PopRenderMode(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Render the displacement surface removed faces. +//----------------------------------------------------------------------------- +void CMapDisp::RenderRemoveSurface( CRender3D *pRender, bool bIsSelected, SelectionState_t faceSelectionState ) +{ + // Remove Faces + for ( int iPass = 0; iPass < 2; ++iPass ) + { + Color color; + if ( iPass == 0 ) + { + pRender->PushRenderMode( RENDER_MODE_TRANSLUCENT_FLAT ); + color.SetColor( 0, 0, 255, 64 ); + CalcColor( pRender, false, faceSelectionState, color ); + } + else + { + pRender->PushRenderMode( RENDER_MODE_WIREFRAME ); + color.SetColor( 0, 0, 255, 255 ); + CalcColor( pRender, false, faceSelectionState, color ); + } + + int nVertCount = m_aRemoveVerts.Count(); + int nIndexCount = m_aRemoveIndices.Count(); + + CMeshBuilder meshBuilder; + CMatRenderContextPtr pRenderContext( MaterialSystemInterface() ); + IMesh *pMesh = pRenderContext->GetDynamicMesh(); + meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, nVertCount, nIndexCount ); + + CoreDispVert_t **ppVerts = m_aRemoveVerts.Base(); + for (int i = 0; i < nVertCount; ++i ) + { + CoreDispVert_t *pVert = ppVerts[i]; + + meshBuilder.Position3fv( pVert->m_Vert.Base() ); + meshBuilder.Color4ub( color[0], color[1], color[2], color[3] ); + meshBuilder.Normal3fv( pVert->m_Normal.Base() ); + meshBuilder.AdvanceVertex(); + } + + unsigned short *pIndex = m_aRemoveIndices.Base(); + for ( int i = 0; i < nIndexCount; ++i ) + { + meshBuilder.Index( pIndex[i] ); + meshBuilder.AdvanceIndex(); + } + + meshBuilder.End(); + pMesh->Draw(); + + pRender->PopRenderMode(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Render the white wireframe overlay. +//----------------------------------------------------------------------------- +void CMapDisp::RenderWireframeSurface( CRender3D *pRender, bool bIsSelected, SelectionState_t faceSelectionState ) +{ + if ( HasGridMask() ) + return; + + pRender->PushRenderMode( RENDER_MODE_WIREFRAME ); + + Color color( 255, 255, 255, 255 ); + CalcColor( pRender, bIsSelected, faceSelectionState, color ); + + int numVerts = m_CoreDispInfo.GetSize(); + int numIndices = m_CoreDispInfo.GetRenderIndexCount(); + + CMeshBuilder meshBuilder; + CMatRenderContextPtr pRenderContext( MaterialSystemInterface() ); + IMesh *pMesh = pRenderContext->GetDynamicMesh(); + meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, numVerts, numIndices ); + + CoreDispVert_t *pVert = m_CoreDispInfo.GetDispVertList(); + for (int i = 0; i < numVerts; ++i ) + { + meshBuilder.Position3fv( pVert[i].m_Vert.Base() ); + meshBuilder.Color3ub( 255, 255, 255 ); + meshBuilder.TexCoord2fv( 0, pVert[i].m_TexCoord.Base() ); + meshBuilder.TexCoord2fv( 1, pVert[i].m_LuxelCoords[0].Base() ); + meshBuilder.AdvanceVertex(); + } + + unsigned short *pIndex = m_CoreDispInfo.GetRenderIndexList(); + for ( int i = 0; i < numIndices; ++i ) + { + meshBuilder.Index( pIndex[i] ); + meshBuilder.AdvanceIndex(); + } + + meshBuilder.End(); + pMesh->Draw(); + + // Reset the render mode. + pRender->PopRenderMode(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapDisp::UpdateNeighborDependencies( bool bDestroy ) +{ + if( !bDestroy ) + { + // reset and find new neighbors + ResetNeighbors(); + FindNeighbors(); + } + else + { + // + // update edge neighbors + // + for( int i = 0; i < 4; i++ ) + { + EditDispHandle_t handle = GetEdgeNeighbor( i ); + if( handle == EDITDISPHANDLE_INVALID ) + continue; + + CMapDisp *pNeighborDisp = EditDispMgr()->GetDisp( handle ); + pNeighborDisp->UpdateNeighborDependencies( false ); + } + + // + // update corner neighbors + // + for( int i = 0; i < 4; i++ ) + { + int cornerCount = GetCornerNeighborCount( i ); + for( int j = 0; j < cornerCount; j++ ) + { + EditDispHandle_t handle = GetCornerNeighbor( i, j ); + if( handle != EDITDISPHANDLE_INVALID ) + { + CMapDisp *pNeighborDisp = EditDispMgr()->GetDisp( handle ); + pNeighborDisp->UpdateNeighborDependencies( false ); + } + } + } + } +} + +void CMapDisp::UpdateNeighborsOfDispsIntersectingBox( const Vector &bbMin, const Vector &bbMax, float flPadding ) +{ + IWorldEditDispMgr *pDispMgr = GetActiveWorldEditDispManager(); + if( !pDispMgr ) + return; + + Vector bbPaddedMin = bbMin - Vector( flPadding, flPadding, flPadding ); + Vector bbPaddedMax = bbMax + Vector( flPadding, flPadding, flPadding ); + + int count = pDispMgr->WorldCount(); + for ( int i=0; i < count; i++ ) + { + CMapDisp *pDisp = pDispMgr->GetFromWorld( i ); + + // Do the bbox test. + Vector testbbmin, testbbmax; + pDisp->GetBoundingBox( testbbmin, testbbmax ); + if ( QuickBoxIntersectTest( testbbmin, testbbmax, bbPaddedMin, bbPaddedMax ) ) + { + pDisp->ResetNeighbors(); + pDispMgr->FindWorldNeighbors( pDisp->GetEditHandle() ); + } + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapDisp::FindNeighbors( void ) +{ + // + // find the current neighbors to "this" displacement + // + IWorldEditDispMgr *pDispMgr = GetActiveWorldEditDispManager(); + if( !pDispMgr ) + return; + + pDispMgr->FindWorldNeighbors( m_EditHandle ); + + // + // generate the vector field for neighboring surfaces (edges and corners) + // + for( int i = 0; i < NUM_EDGES_CORNERS; i++ ) + { + EditDispHandle_t handle = m_EdgeNeighbors[i]; + if( handle != EDITDISPHANDLE_INVALID ) + { + CMapDisp *pNeighborDisp = EditDispMgr()->GetDisp( handle ); + pNeighborDisp->ResetNeighbors(); + pDispMgr->FindWorldNeighbors( pNeighborDisp->GetEditHandle() ); + } + + int cornerCount = m_CornerNeighborCounts[i]; + if( cornerCount != 0 ) + { + for( int j = 0; j < cornerCount; j++ ) + { + handle = m_CornerNeighbors[i][j]; + if( handle != EDITDISPHANDLE_INVALID ) + { + CMapDisp *pNeighborDisp = EditDispMgr()->GetDisp( handle ); + pNeighborDisp->ResetNeighbors(); + pDispMgr->FindWorldNeighbors( pNeighborDisp->GetEditHandle() ); + } + } + } + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapDisp::UpdateBoundingBox( void ) +{ + Vector v; + + VectorFill( m_BBox[0], COORD_NOTINIT ); + VectorFill( m_BBox[1], -COORD_NOTINIT ); + + int size = GetSize(); + for( int i = 0; i < size; i++ ) + { + m_CoreDispInfo.GetVert( i, v ); + + if( v[0] < m_BBox[0][0] ) { m_BBox[0][0] = v[0]; } + if( v[1] < m_BBox[0][1] ) { m_BBox[0][1] = v[1]; } + if( v[2] < m_BBox[0][2] ) { m_BBox[0][2] = v[2]; } + + if( v[0] > m_BBox[1][0] ) { m_BBox[1][0] = v[0]; } + if( v[1] > m_BBox[1][1] ) { m_BBox[1][1] = v[1]; } + if( v[2] > m_BBox[1][2] ) { m_BBox[1][2] = v[2]; } + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapDisp::Scale( float scale ) +{ + // check for a change in scale + if( scale == m_Scale ) + return; + + int size = GetSize(); + + // scale the surface back to its original state and re-scale with the new + // value + if( m_Scale != 1.0f ) + { + float adj = 1.0f / m_Scale; + + for( int i = 0; i < size; i++ ) + { + // scale the vector field distance + float dist = GetFieldDistance( i ); + dist *= adj; + SetFieldDistance( i, dist ); + + // scale the subdivision pos + Vector vPos; + GetSubdivPosition( i, vPos ); + vPos *= adj; + SetSubdivPosition( i, vPos ); + } + } + + for( int i = 0; i < size; i++ ) + { + // scale the vector field distance + float dist = GetFieldDistance( i ); + dist *= scale; + SetFieldDistance( i, dist ); + + // scale the subdivision pos + Vector vPos; + GetSubdivPosition( i, vPos ); + vPos *= scale; + SetSubdivPosition( i, vPos ); + } + + m_Scale = scale; + + UpdateData(); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapDisp::ApplyNoise( float min, float max, float rockiness ) +{ + if( min == max ) + return; + + // initialize the paint data + Paint_Init( DISPPAINT_CHANNEL_POSITION ); + + // + // clamp rockiness value between 0.0 and 1.0 + // + if( rockiness < 0.0f ) { rockiness = 0.0f; } + if( rockiness > 1.0f ) { rockiness = 1.0f; } + + float delta = max - min; + float deltaBy2 = delta / 2.0f; + + int size = GetSize(); + for( int i = 0; i < size; i++ ) + { + // + // get a noise value based on the points position + // + Vector v; + GetVert( i, v ); + + float noiseX = v.x + v.z; + float noiseY = v.y + v.z; + float noise = PerlinNoise2D( noiseX, noiseY, rockiness ); + + // + // clamp noise (can go a little higher and lower due to precision) + // + if( noise < -1.0f ) { noise = -1.0f; } + if( noise > 1.0f ) { noise = 1.0f; } + + noise *= deltaBy2; + noise += ( deltaBy2 + min ); + + // apply noise to the subdivision normal direction + Vector vNoise; + + GetFieldVector( i, vNoise ); + if( ( vNoise.x == 0 ) && ( vNoise.y == 0 ) && ( vNoise.z == 0 ) ) + { + GetSubdivNormal( i, vNoise ); + } + vNoise *= noise; + vNoise += v; + + // set the paint value + Paint_SetValue( i, vNoise ); + } + + Paint_Update( false ); +} + + +//============================================================================= +// +// Load/Save Functions +// + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapDisp::PostLoad( void ) +{ + Vector v; + + // + // check the subdivision normals -- clean them up (old files) + // + bool bUpdateSubdivNormals = false; + + int size = GetSize(); + for( int i = 0; i < size; i++ ) + { + GetSubdivNormal( i, v ); + if( ( v.x == 0.0f ) && ( v.y == 0.0f ) && ( v.z == 0.0f ) ) + { + bUpdateSubdivNormals = true; + break; + } + } + + if( bUpdateSubdivNormals ) + { + Vector vNormal; + GetSurfNormal( vNormal ); + + for( int i = 0; i < size; i++ ) + { + SetSubdivNormal( i, vNormal ); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pFile - +// *pDisp - +// Output : ChunkFileResult_t +//----------------------------------------------------------------------------- +ChunkFileResult_t CMapDisp::LoadDispDistancesCallback(CChunkFile *pFile, CMapDisp *pDisp) +{ + return(pFile->ReadChunk((KeyHandler_t)LoadDispDistancesKeyCallback, pDisp)); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : szKey - +// szValue - +// pDisp - +// Output : ChunkFileResult_t +//----------------------------------------------------------------------------- +ChunkFileResult_t CMapDisp::LoadDispDistancesKeyCallback(const char *szKey, const char *szValue, CMapDisp *pDisp) +{ + float dispDistance; + + if (!strnicmp(szKey, "row", 3)) + { + char szBuf[MAX_KEYVALUE_LEN]; + strcpy(szBuf, szValue); + + int nCols = (1 << pDisp->GetPower()) + 1; + int nRow = atoi(&szKey[3]); + + char *pszNext = strtok(szBuf, " "); + int nIndex = nRow * nCols; + + while (pszNext != NULL) + { + dispDistance = (float)atof(pszNext); + pDisp->m_CoreDispInfo.SetFieldDistance( nIndex, dispDistance ); + pszNext = strtok(NULL, " "); + nIndex++; + } + } + + return(ChunkFile_Ok); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pFile - +// *pDisp - +// Output : ChunkFileResult_t +//----------------------------------------------------------------------------- +ChunkFileResult_t CMapDisp::LoadDispOffsetsCallback(CChunkFile *pFile, CMapDisp *pDisp) +{ + return(pFile->ReadChunk((KeyHandler_t)LoadDispOffsetsKeyCallback, pDisp)); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : szKey - +// szValue - +// pDisp - +// Output : ChunkFileResult_t +//----------------------------------------------------------------------------- +ChunkFileResult_t CMapDisp::LoadDispOffsetsKeyCallback(const char *szKey, const char *szValue, CMapDisp *pDisp) +{ + Vector subdivVector; + + if( !strnicmp( szKey, "row", 3 ) ) + { + char szBuf[MAX_KEYVALUE_LEN]; + strcpy( szBuf, szValue ); + + int nCols = ( 1 << pDisp->GetPower() ) + 1; + int nRow = atoi( &szKey[3] ); + + char *pszNext0 = strtok( szBuf, " " ); + char *pszNext1 = strtok( NULL, " " ); + char *pszNext2 = strtok( NULL, " " ); + + int nIndex = nRow * nCols; + + while( ( pszNext0 != NULL ) && ( pszNext1 != NULL ) && ( pszNext2 != NULL ) ) + { + subdivVector[0] = ( float )atof( pszNext0 ); + subdivVector[1] = ( float )atof( pszNext1 ); + subdivVector[2] = ( float )atof( pszNext2 ); + + pDisp->m_CoreDispInfo.SetSubdivPosition( nIndex, subdivVector ); + + pszNext0 = strtok( NULL, " " ); + pszNext1 = strtok( NULL, " " ); + pszNext2 = strtok( NULL, " " ); + + nIndex++; + } + } + + return( ChunkFile_Ok ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pFile - +// *pDisp - +// Output : ChunkFileResult_t +//----------------------------------------------------------------------------- +ChunkFileResult_t CMapDisp::LoadDispOffsetNormalsCallback(CChunkFile *pFile, CMapDisp *pDisp) +{ + return(pFile->ReadChunk((KeyHandler_t)LoadDispOffsetNormalsKeyCallback, pDisp )); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : szKey - +// szValue - +// pDisp - +// Output : ChunkFileResult_t +//----------------------------------------------------------------------------- +ChunkFileResult_t CMapDisp::LoadDispOffsetNormalsKeyCallback(const char *szKey, const char *szValue, + CMapDisp *pDisp) +{ + Vector normalVector; + + if( !strnicmp( szKey, "row", 3 ) ) + { + char szBuf[MAX_KEYVALUE_LEN]; + strcpy( szBuf, szValue ); + + int nCols = ( 1 << pDisp->GetPower() ) + 1; + int nRow = atoi( &szKey[3] ); + + char *pszNext0 = strtok( szBuf, " " ); + char *pszNext1 = strtok( NULL, " " ); + char *pszNext2 = strtok( NULL, " " ); + + int nIndex = nRow * nCols; + + while( ( pszNext0 != NULL ) && ( pszNext1 != NULL ) && ( pszNext2 != NULL ) ) + { + normalVector[0] = ( float )atof( pszNext0 ); + normalVector[1] = ( float )atof( pszNext1 ); + normalVector[2] = ( float )atof( pszNext2 ); + + pDisp->m_CoreDispInfo.SetSubdivNormal( nIndex, normalVector ); + + pszNext0 = strtok( NULL, " " ); + pszNext1 = strtok( NULL, " " ); + pszNext2 = strtok( NULL, " " ); + + nIndex++; + } + } + + return( ChunkFile_Ok ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : szKey - +// szValue - +// pWorld - +// Output : +//----------------------------------------------------------------------------- +ChunkFileResult_t CMapDisp::LoadDispKeyCallback(const char *szKey, const char *szValue, CMapDisp *pDisp) +{ + if (!stricmp(szKey, "power")) + { + int power; + CChunkFile::ReadKeyValueInt( szValue, power ); + pDisp->SetPower( power ); + } + else if (!stricmp(szKey, "uaxis")) + { + Vector mapAxis; + CChunkFile::ReadKeyValueVector3( szValue, mapAxis ); + pDisp->SetHasMappingAxes( true ); + pDisp->m_MapAxes[0] = mapAxis; + } + else if (!stricmp(szKey, "vaxis")) + { + Vector mapAxis; + CChunkFile::ReadKeyValueVector3( szValue, mapAxis ); + pDisp->SetHasMappingAxes( true ); + pDisp->m_MapAxes[1] = mapAxis; + } + else if( !stricmp( szKey, "startposition" ) ) + { + Vector startPosition; + CChunkFile::ReadKeyValueVector3( szValue, startPosition ); + CCoreDispSurface *pSurf = pDisp->m_CoreDispInfo.GetSurface(); + pSurf->SetPointStart( startPosition ); + } + else if (!stricmp(szKey, "flags")) + { + int nFlags; + CChunkFile::ReadKeyValueInt( szValue, nFlags ); + pDisp->SetFlags( nFlags ); + } +#if 0 + else if (!stricmp(szKey, "mintess")) + { + int minTess; + CChunkFile::ReadKeyValueInt( szValue, minTess ); + pDisp->SetMinTess( minTess ); + } + else if (!stricmp(szKey, "smooth")) + { + float smoothingAngle; + CChunkFile::ReadKeyValueFloat( szValue, smoothingAngle ); + pDisp->SetSmoothingAngle( smoothingAngle ); + } + else if( !stricmp( szKey, "alpha" ) ) + { + Vector4D alphaValues; + CChunkFile::ReadKeyValueVector4( szValue, alphaValues ); + + for( int i = 0; i < 4; i++ ) + { + pDisp->m_CoreDispInfo.SetSurfPointAlpha( i, alphaValues[i] ); + } + } +#endif + else if( !stricmp( szKey, "elevation" ) ) + { + float elevation; + CChunkFile::ReadKeyValueFloat( szValue, elevation ); + pDisp->SetElevation( elevation ); + } + else if( !stricmp( szKey, "subdiv" ) ) + { + int bSubdivided; + CChunkFile::ReadKeyValueInt( szValue, bSubdivided ); + bool bSubdiv = ( bSubdivided != 0 ); + pDisp->SetSubdivided( bSubdiv ); + } + + return(ChunkFile_Ok); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pFile - +// *pDisp - +// Output : ChunkFileResult_t +//----------------------------------------------------------------------------- +ChunkFileResult_t CMapDisp::LoadDispAlphasCallback(CChunkFile *pFile, CMapDisp *pDisp) +{ + return(pFile->ReadChunk((KeyHandler_t)LoadDispAlphasKeyCallback, pDisp)); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pFile - +// *pDisp - +// Output : ChunkFileResult_t +//----------------------------------------------------------------------------- +ChunkFileResult_t CMapDisp::LoadDispAlphasKeyCallback(const char *szKey, const char *szValue, CMapDisp *pDisp) +{ + float alpha; + + if (!strnicmp(szKey, "row", 3)) + { + char szBuf[MAX_KEYVALUE_LEN]; + strcpy(szBuf, szValue); + + int nCols = (1 << pDisp->GetPower()) + 1; + int nRow = atoi(&szKey[3]); + + char *pszNext = strtok(szBuf, " "); + + int nIndex = nRow * nCols; + + while (pszNext != NULL) + { + alpha = (float)atof(pszNext); + + pDisp->m_CoreDispInfo.SetAlpha( nIndex, alpha ); + + pszNext = strtok(NULL, " "); + + nIndex++; + } + } + + return(ChunkFile_Ok); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +ChunkFileResult_t CMapDisp::LoadDispTriangleTagsCallback(CChunkFile *pFile, CMapDisp *pDisp) +{ + return(pFile->ReadChunk((KeyHandler_t)LoadDispTriangleTagsKeyCallback, pDisp)); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +ChunkFileResult_t CMapDisp::LoadDispTriangleTagsKeyCallback(const char *szKey, const char *szValue, CMapDisp *pDisp) +{ + unsigned short nTriTag; + + if ( !strnicmp( szKey, "row", 3 ) ) + { + char szBuf[MAX_KEYVALUE_LEN]; + strcpy( szBuf, szValue ); + + int nCols = ( 1 << pDisp->GetPower() ); + int nRow = atoi( &szKey[3] ); + + char *pszNext = strtok( szBuf, " " ); + + int nIndex = nRow * nCols; + int iTri = nIndex * 2; + + while ( pszNext != NULL ) + { + nTriTag = ( unsigned int )atoi( pszNext ); + pDisp->m_CoreDispInfo.SetTriTagValue( iTri, nTriTag ); + pszNext = strtok( NULL, " " ); + iTri++; + } + } + + return( ChunkFile_Ok ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +ChunkFileResult_t CMapDisp::LoadDispAllowedVertsCallback( CChunkFile *pFile, CMapDisp *pDisp ) +{ + return( pFile->ReadChunk( ( KeyHandler_t )LoadDispAllowedVertsKeyCallback, pDisp ) ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +ChunkFileResult_t CMapDisp::LoadDispAllowedVertsKeyCallback( const char *szKey, const char *szValue, CMapDisp *pDisp ) +{ + if ( !strnicmp( szKey, "10", 2 ) ) + { + char szBuf[MAX_KEYVALUE_LEN]; + strcpy( szBuf, szValue ); + + int iValue = 0; + + char *pszNext = strtok( szBuf, " " ); + while ( pszNext != NULL ) + { + unsigned int nValue = ( unsigned int )atoi( pszNext ); + unsigned long ulValue = ( unsigned long )nValue; + pDisp->m_CoreDispInfo.AllowedVerts_SetDWord( iValue, ulValue ); + pszNext = strtok( NULL, " " ); + iValue++; + } + } + + return( ChunkFile_Ok ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pFile - +// *pDisp - +// Output : ChunkFileResult_t +//----------------------------------------------------------------------------- +ChunkFileResult_t CMapDisp::LoadDispNormalsCallback(CChunkFile *pFile, CMapDisp *pDisp) +{ + return(pFile->ReadChunk((KeyHandler_t)LoadDispNormalsKeyCallback, pDisp)); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *szKey - +// *szValue - +// *pDisp - +// Output : ChunkFileResult_t +//----------------------------------------------------------------------------- +ChunkFileResult_t CMapDisp::LoadDispNormalsKeyCallback(const char *szKey, const char *szValue, CMapDisp *pDisp) +{ + Vector vectorFieldVector; + + if (!strnicmp(szKey, "row", 3)) + { + char szBuf[MAX_KEYVALUE_LEN]; + strcpy(szBuf, szValue); + + int nCols = (1 << pDisp->GetPower()) + 1; + int nRow = atoi(&szKey[3]); + + char *pszNext0 = strtok(szBuf, " "); + char *pszNext1 = strtok(NULL, " "); + char *pszNext2 = strtok(NULL, " "); + + int nIndex = nRow * nCols; + + while ((pszNext0 != NULL) && (pszNext1 != NULL) && (pszNext2 != NULL)) + { + vectorFieldVector[0] = (float)atof(pszNext0); + vectorFieldVector[1] = (float)atof(pszNext1); + vectorFieldVector[2] = (float)atof(pszNext2); + + pDisp->m_CoreDispInfo.SetFieldVector( nIndex, vectorFieldVector ); + + pszNext0 = strtok(NULL, " "); + pszNext1 = strtok(NULL, " "); + pszNext2 = strtok(NULL, " "); + + nIndex++; + } + } + + return(ChunkFile_Ok); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pFile - +// Output : ChunkFileResult_t +//----------------------------------------------------------------------------- +ChunkFileResult_t CMapDisp::LoadVMF(CChunkFile *pFile) +{ + // + // Set up handlers for the subchunks that we are interested in. + // + CChunkHandlerMap Handlers; + Handlers.AddHandler("normals", (ChunkHandler_t)LoadDispNormalsCallback, this); + Handlers.AddHandler("distances", (ChunkHandler_t)LoadDispDistancesCallback, this); + Handlers.AddHandler("offsets", (ChunkHandler_t)LoadDispOffsetsCallback, this); + Handlers.AddHandler("offset_normals", (ChunkHandler_t)LoadDispOffsetNormalsCallback, this); + Handlers.AddHandler("alphas", (ChunkHandler_t)LoadDispAlphasCallback, this); + Handlers.AddHandler("triangle_tags", (ChunkHandler_t)LoadDispTriangleTagsCallback, this ); + Handlers.AddHandler("allowed_verts", (ChunkHandler_t)LoadDispAllowedVertsCallback, this ); + + pFile->PushHandlers(&Handlers); + ChunkFileResult_t eResult = pFile->ReadChunk((KeyHandler_t)LoadDispKeyCallback, this); + pFile->PopHandlers(); + + return(eResult); +} + + +//----------------------------------------------------------------------------- +// Purpose: Saves the displacement info into a special chunk in the MAP file. +// Input : *pFile - +// Output : ChunkFileResult_t +//----------------------------------------------------------------------------- +ChunkFileResult_t CMapDisp::SaveVMF(CChunkFile *pFile, CSaveInfo *pSaveInfo) +{ + ChunkFileResult_t eResult = pFile->BeginChunk("dispinfo"); + + int power = GetPower(); + float elevation = GetElevation(); + int nFlags = GetFlags(); + + Vector startPosition; + CCoreDispSurface *pSurf = m_CoreDispInfo.GetSurface(); + pSurf->GetPoint( 0, startPosition ); + + int bSubdivided = ( int )IsSubdivided(); + +#if 0 // old + Vector4D alphaValues; + for( int i = 0; i < 4; i++ ) + { + alphaValues[i] = m_CoreDispInfo.GetSurfPointAlpha( i ); + } +#endif + + if (eResult == ChunkFile_Ok) + { + eResult = pFile->WriteKeyValueInt("power", power); + } + +#if 0 // old + if (eResult == ChunkFile_Ok) + { + eResult = pFile->WriteKeyValueVector3("uaxis", m_BDSurf.uAxis); + } + + if (eResult == ChunkFile_Ok) + { + eResult = pFile->WriteKeyValueVector3("vaxis", m_BDSurf.vAxis); + } +#endif + + if( eResult == ChunkFile_Ok ) + { + eResult = pFile->WriteKeyValueVector3( "startposition", startPosition ); + } + + if (eResult == ChunkFile_Ok) + { + eResult = pFile->WriteKeyValueInt("flags", nFlags ); + } + +#if 0 + // old + if (eResult == ChunkFile_Ok) + { + eResult = pFile->WriteKeyValueInt("mintess", minTess); + } + + if (eResult == ChunkFile_Ok) + { + eResult = pFile->WriteKeyValueFloat("smooth", smoothingAngle); + } + // + // save the corner alpha values + // + if( eResult == ChunkFile_Ok ) + { + eResult = pFile->WriteKeyValueVector4( "alpha", alphaValues ); + } +#endif + + if( eResult == ChunkFile_Ok ) + { + eResult = pFile->WriteKeyValueFloat( "elevation", elevation ); + } + + if (eResult == ChunkFile_Ok) + { + eResult = pFile->WriteKeyValueInt( "subdiv", bSubdivided ); + } + + // + // Save displacement map normals. + // + if (eResult == ChunkFile_Ok) + { + Vector vectorFieldVector; + + eResult = pFile->BeginChunk("normals"); + if (eResult == ChunkFile_Ok) + { + char szBuf[MAX_KEYVALUE_LEN]; + char szTemp[80]; + + int nRows = (1 << power) + 1;; + int nCols = nRows; + + for (int nRow = 0; nRow < nRows; nRow++) + { + bool bFirst = true; + szBuf[0] = '\0'; + + for (int nCol = 0; nCol < nCols; nCol++) + { + int nIndex = nRow * nCols + nCol; + + if (!bFirst) + { + strcat(szBuf, " "); + } + + bFirst = false; + m_CoreDispInfo.GetFieldVector( nIndex, vectorFieldVector ); + sprintf(szTemp, "%g %g %g", (double)vectorFieldVector[0], (double)vectorFieldVector[1], (double)vectorFieldVector[2]); + strcat(szBuf, szTemp); + } + + char szKey[10]; + sprintf(szKey, "row%d", nRow); + eResult = pFile->WriteKeyValue(szKey, szBuf); + } + } + + if (eResult == ChunkFile_Ok) + { + eResult = pFile->EndChunk(); + } + } + + // + // Save displacement map distances. + // + if (eResult == ChunkFile_Ok) + { + float dispDistance; + + eResult = pFile->BeginChunk("distances"); + if (eResult == ChunkFile_Ok) + { + char szBuf[MAX_KEYVALUE_LEN]; + char szTemp[80]; + + int nRows = (1 << power) + 1; + int nCols = nRows; + + for (int nRow = 0; nRow < nRows; nRow++) + { + bool bFirst = true; + szBuf[0] = '\0'; + + for (int nCol = 0; nCol < nCols; nCol++) + { + int nIndex = nRow * nCols + nCol; + + if (!bFirst) + { + strcat(szBuf, " "); + } + + bFirst = false; + dispDistance = m_CoreDispInfo.GetFieldDistance( nIndex ); + sprintf(szTemp, "%g", (double)dispDistance); + strcat(szBuf, szTemp); + } + + char szKey[10]; + sprintf(szKey, "row%d", nRow); + eResult = pFile->WriteKeyValue(szKey, szBuf); + } + } + + if (eResult == ChunkFile_Ok) + { + eResult = pFile->EndChunk(); + } + } + + // + // Save displacement map offset. + // + if (eResult == ChunkFile_Ok) + { + Vector subdivPos; + + eResult = pFile->BeginChunk( "offsets" ); + if( eResult == ChunkFile_Ok ) + { + char szBuf[MAX_KEYVALUE_LEN]; + char szTemp[80]; + + int nRows = (1 << power) + 1; + int nCols = nRows; + + for (int nRow = 0; nRow < nRows; nRow++) + { + bool bFirst = true; + szBuf[0] = '\0'; + + for (int nCol = 0; nCol < nCols; nCol++) + { + int nIndex = nRow * nCols + nCol; + + if (!bFirst) + { + strcat(szBuf, " "); + } + + bFirst = false; + m_CoreDispInfo.GetSubdivPosition( nIndex, subdivPos ); + sprintf(szTemp, "%g %g %g", (double)subdivPos[0], (double)subdivPos[1], (double)subdivPos[2]); + strcat(szBuf, szTemp); + } + + char szKey[10]; + sprintf(szKey, "row%d", nRow); + eResult = pFile->WriteKeyValue(szKey, szBuf); + } + } + + if (eResult == ChunkFile_Ok) + { + eResult = pFile->EndChunk(); + } + } + + // + // Save displacement subdivision normals + // + if (eResult == ChunkFile_Ok) + { + Vector subdivNormal; + + eResult = pFile->BeginChunk( "offset_normals" ); + if( eResult == ChunkFile_Ok ) + { + char szBuf[MAX_KEYVALUE_LEN]; + char szTemp[80]; + + int nRows = (1 << power) + 1; + int nCols = nRows; + + for (int nRow = 0; nRow < nRows; nRow++) + { + bool bFirst = true; + szBuf[0] = '\0'; + + for (int nCol = 0; nCol < nCols; nCol++) + { + int nIndex = nRow * nCols + nCol; + + if (!bFirst) + { + strcat(szBuf, " "); + } + + bFirst = false; + m_CoreDispInfo.GetSubdivNormal( nIndex, subdivNormal ); + sprintf(szTemp, "%g %g %g", (double)subdivNormal[0], (double)subdivNormal[1], (double)subdivNormal[2]); + strcat(szBuf, szTemp); + } + + char szKey[10]; + sprintf(szKey, "row%d", nRow); + eResult = pFile->WriteKeyValue(szKey, szBuf); + } + } + + if (eResult == ChunkFile_Ok) + { + eResult = pFile->EndChunk(); + } + } + + // + // Save displacement alphas + // + if (eResult == ChunkFile_Ok) + { + float alpha; + + eResult = pFile->BeginChunk( "alphas" ); + if( eResult == ChunkFile_Ok ) + { + char szBuf[MAX_KEYVALUE_LEN]; + char szTemp[80]; + + int nRows = (1 << power) + 1; + int nCols = nRows; + + for (int nRow = 0; nRow < nRows; nRow++) + { + bool bFirst = true; + szBuf[0] = '\0'; + + for (int nCol = 0; nCol < nCols; nCol++) + { + int nIndex = nRow * nCols + nCol; + + if (!bFirst) + { + strcat(szBuf, " "); + } + + bFirst = false; + alpha = m_CoreDispInfo.GetAlpha( nIndex ); + sprintf(szTemp, "%g", (double)alpha); + strcat(szBuf, szTemp); + } + + char szKey[10]; + sprintf(szKey, "row%d", nRow); + eResult = pFile->WriteKeyValue(szKey, szBuf); + } + } + + if (eResult == ChunkFile_Ok) + { + eResult = pFile->EndChunk(); + } + } + + // Save Triangle data. + if (eResult == ChunkFile_Ok) + { + unsigned short nTriTag; + + eResult = pFile->BeginChunk( "triangle_tags" ); + if( eResult == ChunkFile_Ok ) + { + char szBuf[MAX_KEYVALUE_LEN]; + char szTemp[80]; + + int nRows = ( 1 << power ); // ( 1 << power ) + 1 - 1 + int nCols = nRows; + + for ( int iRow = 0; iRow < nRows; ++iRow ) + { + bool bFirst = true; + szBuf[0] = '\0'; + + for ( int iCol = 0; iCol < nCols; ++iCol ) + { + int nIndex = iRow * nCols + iCol; + int iTri = nIndex * 2; + + if ( !bFirst ) + { + strcat( szBuf, " " ); + } + bFirst = false; + + nTriTag = m_CoreDispInfo.GetTriTagValue( iTri ); + sprintf( szTemp, "%d", (int)nTriTag ); + strcat( szBuf, szTemp ); + + nTriTag = m_CoreDispInfo.GetTriTagValue( iTri + 1 ); + sprintf( szTemp, " %d", (int)nTriTag ); + strcat( szBuf, szTemp ); + } + + char szKey[10]; + sprintf( szKey, "row%d", iRow ); + eResult = pFile->WriteKeyValue( szKey, szBuf ); + } + } + + if (eResult == ChunkFile_Ok) + { + eResult = pFile->EndChunk(); + } + } + + // Save allowed vert data. + if ( eResult == ChunkFile_Ok ) + { + eResult = pFile->BeginChunk( "allowed_verts" ); + if ( eResult == ChunkFile_Ok ) + { + char szBuf[MAX_KEYVALUE_LEN]; + char szTemp[80]; + + szBuf[0] = '\0'; + + int nCount = m_CoreDispInfo.AllowedVerts_GetNumDWords(); + for ( int iCount = 0; iCount < nCount; ++iCount ) + { + if ( iCount != 0 ) + { + strcat( szBuf, " " ); + } + + unsigned long ulValue = m_CoreDispInfo.AllowedVerts_GetDWord( iCount ); + sprintf( szTemp, "%d", ( int )ulValue ); + strcat( szBuf, szTemp ); + } + + char szKey[8]; + sprintf( szKey, "%d", nCount ); + eResult = pFile->WriteKeyValue( szKey, szBuf ); + } + + if (eResult == ChunkFile_Ok) + { + eResult = pFile->EndChunk(); + } + } + + if (eResult == ChunkFile_Ok) + { + eResult = pFile->EndChunk(); + } + + return(eResult); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CMapDisp::SerializedLoadMAP( std::fstream &file, CMapFace *pFace, UINT version ) +{ + int power; + float maxData = 1.0f; + int minTess; + float smoothingAngle; + Vector vectorFieldVector; + float distance; + + // + // read off the first line -- burn it!!! and get the second + // + static char buf[256]; + file.getline( buf, 256 ); + file.getline( buf, 256 ); + + if( version < 350 ) + { + sscanf( buf, "%d [ %f %f %f ] [ %f %f %f ] %f %d %f", + &power, + &m_MapAxes[0][0], &m_MapAxes[0][1], &m_MapAxes[0][2], + &m_MapAxes[1][0], &m_MapAxes[1][1], &m_MapAxes[1][2], + &maxData, + &minTess, + &smoothingAngle ); + } + else + { + sscanf( buf, "%d [ %f %f %f ] [ %f %f %f ] %d %f", + &power, + &m_MapAxes[0][0], &m_MapAxes[0][1], &m_MapAxes[0][2], + &m_MapAxes[1][0], &m_MapAxes[1][1], &m_MapAxes[1][2], + &minTess, + &smoothingAngle ); + } + + m_CoreDispInfo.SetPower( power ); + + m_bHasMappingAxes = true; + + // + // displacement normals + // + int size = GetSize(); + for( int i = 0; i < size; i++ ) + { + file >> vectorFieldVector[0]; + file >> vectorFieldVector[1]; + file >> vectorFieldVector[2]; + + m_CoreDispInfo.SetFieldVector( i, vectorFieldVector ); + } + file.getline( buf, 256 ); + + // + // displacement distances + // + for( int i = 0; i < size; i++ ) + { + if( version < 350 ) + { + file >> distance; + distance *= maxData; + } + else + { + file >> distance; + } + + m_CoreDispInfo.SetFieldDistance( i, distance ); + } + file.getline( buf, 256 ); + + // finish the last bit of the "chunk" + file.getline( buf, 256 ); + + // save the parent info + SetParent( pFace ); + + return true; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CMapDisp::SerializedLoadRMF( std::fstream &file, CMapFace *pFace, float version ) +{ + int power; + int minTess; + float smoothingAngle; + Vector vectorFieldVectors[MAPDISP_MAX_VERTS]; + float distances[MAPDISP_MAX_VERTS]; + + // + // get displacement information + // + file.read( ( char* )&power, sizeof( int ) ); + file.read( ( char* )m_MapAxes[0].Base(), 3 * sizeof( float ) ); + file.read( ( char* )m_MapAxes[1].Base(), 3 * sizeof( float ) ); + file.read( ( char* )&minTess, sizeof( int ) ); + file.read( ( char* )&smoothingAngle, sizeof( float ) ); + + m_CoreDispInfo.SetPower( power ); + + m_bHasMappingAxes = true; + + // + // get displacement map normals and distances + // + int size = GetSize(); + int i; + for ( i = 0; i < size; ++i) + { + file.read( ( char* )&vectorFieldVectors[i], 3 * sizeof( float ) ); + } + file.read( ( char* )distances, size * sizeof( float ) ); + + for( i = 0; i < size; i++ ) + { + m_CoreDispInfo.SetFieldVector( i, vectorFieldVectors[i] ); + m_CoreDispInfo.SetFieldDistance( i, distances[i] ); + } + + // set the parent + SetParent( pFace ); + + // displacement info loaded + return true; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int CMapDisp::GetEndIndexFromLevel( int levelIndex ) +{ + switch( levelIndex ) + { + case 2: { return 20; } + case 3: { return 84; } + case 4: { return 340; } + default: { return 0; } + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int CMapDisp::GetStartIndexFromLevel( int levelIndex ) +{ + switch( levelIndex ) + { + case 2: { return 5; } + case 3: { return 21; } + case 4: { return 85; } + default: { return 0; } + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapDisp::DoTransform(const VMatrix &matrix) +{ + // get the face + CCoreDispSurface *pSurf = m_CoreDispInfo.GetSurface(); + CMapFace *pFace = ( CMapFace* )GetParent(); + + if( !pFace || !pSurf ) + return; + + Assert( pFace->GetPointCount() == 4 ); + + bool bFlip = (matrix[0][0]*matrix[1][1]*matrix[2][2]) < 0; + + if ( bFlip ) + { + // get the displacement starting point, relative to the newly "flipped" points + // NOTE: this seems a bit hacky -- if flip goes NUTS later -- look here!!! + + int iStartIndex = pSurf->GetPointStartIndex(); + pSurf->SetPointStartIndex( 3-iStartIndex ); + Flip( FLIP_TRANSPOSE ); + } + + Vector v; + int size = GetSize(); + for( int i = 0; i < size; i++ ) + { + GetFieldVector( i, v ); + TransformPoint( matrix, v ); + SetFieldVector( i, v ); + + GetSubdivPosition( i, v ); + TransformPoint( matrix, v ); + SetSubdivPosition( i, v ); + + GetSubdivNormal( i, v ); + TransformPoint( matrix, v ); + SetSubdivNormal( i, v ); + } + + UpdateSurfData( pFace ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool SphereTriEdgePlanesIntersection( Vector const &ptCenter, float radius, cplane_t *pPlanes ) +{ + // check all planes + for( int ndxPlane = 0; ndxPlane < 3; ndxPlane++ ) + { + float dist = pPlanes[ndxPlane].normal.Dot( ptCenter ) - pPlanes[ndxPlane].dist; + if( dist > radius ) + return false; + } + + return true; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CMapDisp::PointSurfIntersection( Vector const &ptCenter, float radius, float &distMin, + Vector &ptMin ) +{ + // initialize the min data + distMin = radius; + ptMin.Init(); + + // + // get the render list size -- created triangles + // + unsigned short *pTriList = m_CoreDispInfo.GetRenderIndexList(); + int listSize = m_CoreDispInfo.GetRenderIndexCount(); + for( int i = 0; i < listSize; i += 3 ) + { + // get the triangle + Vector v[3]; + GetVert( pTriList[i], v[0] ); + GetVert( pTriList[i+1], v[1] ); + GetVert( pTriList[i+2], v[2] ); + + // + // create a triangle plane + // + Vector seg0, seg1; + seg0 = v[1] - v[0]; + seg1 = v[2] - v[0]; + cplane_t triPlane; + triPlane.normal = seg1.Cross( seg0 ); + VectorNormalize( triPlane.normal ); + triPlane.dist = triPlane.normal.Dot( v[0] ); + + // + // plane sphere intersection + // + float dist = triPlane.normal.Dot( ptCenter ) - triPlane.dist; + if( fabs( dist ) < distMin ) + { + // + // create edge plane data + // + cplane_t edgePlanes[3]; + Vector edges[3]; + edges[0] = v[1] - v[0]; + edges[1] = v[2] - v[1]; + edges[2] = v[0] - v[2]; + + for( int j = 0; j < 3; j++ ) + { + edgePlanes[j].normal = triPlane.normal.Cross( edges[j] ); + VectorNormalize( edgePlanes[j].normal ); + edgePlanes[j].dist = edgePlanes[j].normal.Dot( v[j] ); + + // check normal facing + float distPt = edgePlanes[j].normal.Dot( v[(j+2)%3] ) - edgePlanes[j].dist; + if( distPt > 0.0f ) + { + edgePlanes[j].normal.Negate(); + edgePlanes[j].dist = -edgePlanes[j].dist; + } + } + + // intersect sphere with triangle + bool bSphereIntersect = SphereTriEdgePlanesIntersection( ptCenter, distMin, edgePlanes ); + + // + // check to see if the center lies behind all the edge planes + // + if( bSphereIntersect ) + { + bool bPointInside = SphereTriEdgePlanesIntersection( ptCenter, 0.0f, edgePlanes ); + + if( bPointInside ) + { + distMin = fabs( dist ); + ptMin = ptCenter - ( triPlane.normal * dist ); + } + else + { + // check distance to points + for( int k = 0; k < 3; k++ ) + { + Vector vTmp; + vTmp = ptCenter - v[k]; + float distPt = ( float )sqrt( vTmp.Dot( vTmp ) ); + if( distPt < distMin ) + { + distMin = distPt; + ptMin = v[k]; + } + } + } + } + } + } + + if( distMin != radius ) + return true; + + return false; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +EditDispHandle_t CMapDisp::GetHitDispMap( void ) +{ + if( m_HitDispIndex == -1 ) + { + CMapFace *pFace = ( CMapFace* )GetParent(); + return pFace->GetDisp(); + } + + if( m_HitDispIndex <= 3 ) + { + return m_EdgeNeighbors[m_HitDispIndex]; + } + + return m_CornerNeighbors[m_HitDispIndex-4][2]; +} + + +//----------------------------------------------------------------------------- +// Purpose: UNDO is messy to begin with, and now with handles it gets even +// more fun!!! Call through here to setup undo!! +//----------------------------------------------------------------------------- +void EditDisp_ForUndo( EditDispHandle_t editHandle, char *pszPositionName, + bool bNeighborsUndo ) +{ + // sanity check on handle + if( editHandle == EDITDISPHANDLE_INVALID ) + return; + + // get the current displacement given the handle + CMapDisp *pDisp = EditDispMgr()->GetDisp( editHandle ); + + // + // set the undo name if necessary + // + if( pszPositionName ) + { + GetHistory()->MarkUndoPosition( NULL, pszPositionName ); + } + + // + // get the solid (face) for the UNDO history + // + CMapFace *pFace = ( CMapFace* )pDisp->GetParent(); + CMapSolid *pSolid = ( CMapSolid* )pFace->GetParent(); + GetHistory()->Keep( ( CMapClass* )pSolid ); + + // + // neighbors in undo as well + // + if ( bNeighborsUndo ) + { + for ( int ndxNeighbor = 0; ndxNeighbor < 4; ndxNeighbor++ ) + { + // displacement pointer could have changed due to the undo/copyfrom above + pDisp = EditDispMgr()->GetDisp( editHandle ); + + // + // edge neighbors + // + int neighborOrient; + EditDispHandle_t neighborHandle; + pDisp->GetEdgeNeighbor( ndxNeighbor, neighborHandle, neighborOrient ); + if( neighborHandle != EDITDISPHANDLE_INVALID ) + { + CMapDisp *pNeighborDisp = EditDispMgr()->GetDisp( neighborHandle ); + CMapFace *pNeighborFace = ( CMapFace* )pNeighborDisp->GetParent(); + CMapSolid *pNeighborSolid = ( CMapSolid* )pNeighborFace->GetParent(); + GetHistory()->Keep( ( CMapClass* )pNeighborSolid ); + + // displacement pointer could have changed due to the undo/copyfrom above + pDisp = EditDispMgr()->GetDisp( editHandle ); + } + + // + // corner neighbors + // + int cornerCount = pDisp->GetCornerNeighborCount( ndxNeighbor ); + if( cornerCount > 0 ) + { + for( int ndxCorner = 0; ndxCorner < cornerCount; ndxCorner++ ) + { + pDisp->GetCornerNeighbor( ndxNeighbor, ndxCorner, neighborHandle, neighborOrient ); + CMapDisp *pNeighborDisp = EditDispMgr()->GetDisp( neighborHandle ); + CMapFace *pNeighborFace = ( CMapFace* )pNeighborDisp->GetParent(); + CMapSolid *pNeighborSolid = ( CMapSolid* )pNeighborFace->GetParent(); + GetHistory()->Keep( ( CMapClass* )pNeighborSolid ); + + // displacement pointer could have changed due to the undo/copyfrom above + pDisp = EditDispMgr()->GetDisp( editHandle ); + } + } + } + } +} + + +//============================================================================= +// +// Painting Functions +// + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapDisp::Paint_Init( int nType ) +{ + m_Canvas.m_nType = nType; + m_Canvas.m_bDirty = false; + + int nVertCount = GetSize(); + for( int iVert = 0; iVert < nVertCount; iVert++ ) + { + m_Canvas.m_Values[iVert].Init(); + m_Canvas.m_bValuesDirty[iVert] = false; + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapDisp::Paint_InitSelfAndNeighbors( int nType ) +{ + // Initialiuze self. + Paint_Init( nType ); + + // Initialize neighbors. + for( int iEdge = 0; iEdge < 4; iEdge++ ) + { + EditDispHandle_t handle = GetEdgeNeighbor( iEdge ); + if( handle != EDITDISPHANDLE_INVALID ) + { + CMapDisp *pNeighborDisp = EditDispMgr()->GetDisp( handle ); + pNeighborDisp->Paint_Init( nType ); + } + + int nCornerCount = GetCornerNeighborCount( iEdge ); + if( nCornerCount > 0 ) + { + for( int iCorner = 0; iCorner < nCornerCount; iCorner++ ) + { + handle = GetCornerNeighbor( iEdge, iCorner ); + if( handle != EDITDISPHANDLE_INVALID ) + { + CMapDisp *pNeighborDisp = EditDispMgr()->GetDisp( handle ); + pNeighborDisp->Paint_Init( nType ); + } + } + } + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapDisp::Paint_SetValue( int iVert, Vector const &vPaint ) +{ + Assert( iVert >= 0 ); + Assert( iVert < MAPDISP_MAX_VERTS ); + + VectorCopy( vPaint, m_Canvas.m_Values[iVert] ); + m_Canvas.m_bValuesDirty[iVert] = true; + m_Canvas.m_bDirty = true; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapDisp::PaintAlpha_Update( int iVert ) +{ + SetAlpha( iVert, m_Canvas.m_Values[iVert].x ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapDisp::PaintPosition_Update( int iVert ) +{ + Vector vSPos, vFlat; + GetFlatVert( iVert, vFlat ); + GetSubdivPosition( iVert, vSPos ); + + Vector vSeg; + vSeg = m_Canvas.m_Values[iVert] - vFlat; + vSeg -= vSPos; + + // Subtract out the elevation. + float elev = GetElevation(); + if( elev != 0.0 ) + { + Vector vNormal; + GetSurfNormal( vNormal ); + vNormal *= elev; + + vSeg -= vNormal; + } + + float flDistance = VectorNormalize( vSeg ); + + SetFieldVector( iVert, vSeg ); + SetFieldDistance( iVert, flDistance ); +} + +void CMapDisp::UpdateVertPositionForSubdiv( int iVert, const Vector &vecNewSubdivPos ) +{ + Vector vecSubdivPos, vecFlatPos, vecPos; + GetFlatVert( iVert, vecFlatPos ); + GetSubdivPosition( iVert, vecSubdivPos ); + GetVert( iVert, vecPos ); + + Vector vecSegment1; + vecPos -= vecSubdivPos; + vecSegment1 = vecPos - vecFlatPos; + + // Subtract out the elevation. + float flElevation = GetElevation(); + Vector vecFaceNormal( 0.0f, 0.0f, 0.0f ); + if( flElevation != 0.0 ) + { + GetSurfNormal( vecFaceNormal ); + vecFaceNormal *= flElevation; + vecSegment1 -= vecFaceNormal; + } + + float flDistance = VectorNormalize( vecSegment1 ); + + SetFieldVector( iVert, vecSegment1 ); + SetFieldDistance( iVert, flDistance ); + + SetSubdivPosition( iVert, vecNewSubdivPos ); + + // Have to update in place. + Vector vecNewPos = vecFlatPos; + vecNewPos += ( vecFaceNormal * flElevation ); + vecNewPos += vecNewSubdivPos; + vecNewPos += ( vecSegment1 * flDistance ); + SetVert( iVert, vecNewPos ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapDisp::Paint_Update( bool bSplit ) +{ + // Check for changes to the canvas. + if ( !m_Canvas.m_bDirty ) + return; + + int nVertCount = GetSize(); + for ( int iVert = 0; iVert < nVertCount; iVert++ ) + { + // Check for changes at the vertex. + if ( m_Canvas.m_bValuesDirty[iVert] ) + { + if ( m_Canvas.m_nType == DISPPAINT_CHANNEL_POSITION ) + { + PaintPosition_Update( iVert ); + } + else if ( m_Canvas.m_nType == DISPPAINT_CHANNEL_ALPHA ) + { + PaintAlpha_Update( iVert ); + } + } + } + + // Update the displacement surface. + UpdateData(); + + if ( !bSplit ) + { + CheckAndUpdateOverlays( false ); + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapDisp::Paint_UpdateSelfAndNeighbors( bool bSplit ) +{ + // Update self. + Paint_Update( bSplit ); + + // Update neighbors. + for( int iEdge = 0; iEdge < 4; iEdge++ ) + { + EditDispHandle_t handle = GetEdgeNeighbor( iEdge ); + if( handle != EDITDISPHANDLE_INVALID ) + { + CMapDisp *pNeighborDisp = EditDispMgr()->GetDisp( handle ); + pNeighborDisp->Paint_Update( bSplit ); + } + + int nCornerCount = GetCornerNeighborCount( iEdge ); + if( nCornerCount > 0 ) + { + for( int iCorner = 0; iCorner < nCornerCount; iCorner++ ) + { + handle = GetCornerNeighbor( iEdge, iCorner ); + if( handle != EDITDISPHANDLE_INVALID ) + { + CMapDisp *pNeighborDisp = EditDispMgr()->GetDisp( handle ); + pNeighborDisp->Paint_Update( bSplit ); + } + } + } + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapDisp::SetSelectMask( bool bSelectMask ) +{ + m_bSelectMask = bSelectMask; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CMapDisp::HasSelectMask( void ) +{ + return m_bSelectMask; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapDisp::SetGridMask( bool bGridMask ) +{ + m_bGridMask = bGridMask; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CMapDisp::HasGridMask( void ) +{ + return m_bGridMask; +} + +//----------------------------------------------------------------------------- +// Purpose: Do the slow thing first and optimize later?? +//----------------------------------------------------------------------------- +int CMapDisp::CollideWithDispTri( const Vector &rayStart, const Vector &rayEnd, float &flFraction, bool OneSided ) +{ + int iTriangle = -1; + flFraction = 1.0f; + + int nTriCount = GetTriCount(); + for ( int iTri = 0; iTri < nTriCount; ++iTri ) + { + unsigned short v1, v2, v3; + GetTriIndices( iTri, v1, v2, v3 ); + Vector vec1, vec2, vec3; + GetVert( v1, vec1 ); + GetVert( v2, vec2 ); + GetVert( v3, vec3 ); + + Ray_t ray; + ray.Init( rayStart, rayEnd, Vector( 0.0f, 0.0f, 0.0f ), Vector ( 0.0f, 0.0f, 0.0f ) ); + + float flFrac = IntersectRayWithTriangle( ray, vec1, vec2, vec3, OneSided ); + if ( flFrac == -1.0f ) + continue; + + if ( flFrac < flFraction ) + { + flFraction = flFrac; + iTriangle = iTri; + } + } + + return iTriangle; +} + +bool CMapDisp::SaveDXF(ExportDXFInfo_s *pInfo) +{ + char szName[128]; + sprintf(szName, "OBJECT%03d", pInfo->nObject); + + // count number of triangulated faces + int nVertCount = GetSize(); + int nTriFaces = TriangleCount(); + + fprintf(pInfo->fp,"0\nPOLYLINE\n8\n%s\n66\n1\n70\n64\n71\n%u\n72\n%u\n", + szName, nVertCount, nTriFaces); + fprintf(pInfo->fp,"62\n50\n"); + + // Write out vertices... + int i; + for (i = 0; i < nVertCount; i++) + { + Vector pos; + GetVert( i, pos ); + fprintf(pInfo->fp, "0\nVERTEX\n8\n%s\n10\n%.6f\n20\n%.6f\n30\n%.6f\n70\n192\n", szName, pos[0], pos[1], pos[2]); + } + + // triangulate each face and write + int nWidth = GetWidth(); + int nHeight = GetHeight(); + for (i = 0; i < nHeight - 1; ++i) + { + for (int j = 0; j < nWidth - 1; ++j) + { + // DXF files are 1 based, not 0 based. That's what the extra 1 is for + int idx = i * nHeight + j + 1; + + fprintf(pInfo->fp, "0\nVERTEX\n8\n%s\n10\n0\n20\n0\n30\n" + "0\n70\n128\n71\n%d\n72\n%d\n73\n%d\n", szName, + idx, idx + nHeight, idx + nHeight + 1 ); + + fprintf(pInfo->fp, "0\nVERTEX\n8\n%s\n10\n0\n20\n0\n30\n" + "0\n70\n128\n71\n%d\n72\n%d\n73\n%d\n", szName, + idx, idx + nHeight + 1, idx + 1 ); + } + } + + fprintf(pInfo->fp, "0\nSEQEND\n8\n%s\n", szName); + + return true; +} |