summaryrefslogtreecommitdiff
path: root/hammer/mapdisp.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /hammer/mapdisp.cpp
downloadarchived-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.cpp4365
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;
+}