csgo-2018-source/hammer/mapdisp.cpp
2021-07-24 21:11:47 -07:00

4709 lines
126 KiB
C++

//========= Copyright © 1996-2005, 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"
#include "..\FoW\FoW.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, 0, NULL );
Paint_Init( DISPPAINT_CHANNEL_POSITION );
m_CoreDispInfo.AllowedVerts_Clear();
m_FoWTriSoupID = -1;
}
//-----------------------------------------------------------------------------
// Purpose : CMapDisp deconstructor
//-----------------------------------------------------------------------------
CMapDisp::~CMapDisp()
{
m_aWalkableVerts.Purge();
m_aWalkableIndices.Purge();
m_aForcedWalkableIndices.Purge();
m_aBuildableVerts.Purge();
m_aBuildableIndices.Purge();
m_aForcedBuildableIndices.Purge();
if ( m_FoWTriSoupID != -1 )
{
CFoW *pFoW = CMapDoc::GetActiveMapDoc()->GetFoW();
if ( pFoW )
{
pFoW->RemoveTriSoup( m_FoWTriSoupID );
}
m_FoWTriSoupID = -1;
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
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);
CFoW *pFoW = CMapDoc::GetActiveMapDoc()->GetFoW();
CMapFace *pParent = NULL;
CMapSolid *pParent2 = NULL;
pParent = dynamic_cast< CMapFace * >( GetParent() );
if ( pParent )
{
pParent2 = dynamic_cast< CMapSolid * >( pParent->GetParent() );
}
if ( pFoW && pParent2 && pParent2->IsVisible() )
{
if ( m_FoWTriSoupID == -1 )
{
m_FoWTriSoupID = pFoW->AddTriSoup();
}
pFoW->ClearTriSoup( m_FoWTriSoupID );
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] );
pFoW->AddTri( m_FoWTriSoupID, v[ 0 ], v[ 1 ], v[ 2 ] );
}
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
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;
Vector4D vBlend, vAlphaBlend;
Vector vColor1, vColor2, vColor3, vColor4;
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 ) );
pMapDisp->GetMultiBlend( i, vBlend, vAlphaBlend, vColor1, vColor2, vColor3, vColor4 );
SetMultiBlend( i, vBlend, vAlphaBlend, vColor1, vColor2, vColor3, vColor4 );
}
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::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() );
}
}
}
void CMapDisp::PostUpdate(Notify_Dependent_t eNotifyType)
{
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
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 );
}
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 );
Vector test( 1.0f, 0.0f, 0.0f );
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], ( 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() );
// multiblend uses these
meshBuilder.TexCoord4fv( 3, pVert[ i ].m_AlphaBlend.Base() );
Vector4D temp;
temp.Init( pVert[ i ].m_vBlendColors[ 0 ], pVert[ i ].m_MultiBlend.x );
meshBuilder.TexCoord4fv( 4, temp.Base() );
temp.Init( pVert[ i ].m_vBlendColors[ 1 ], pVert[ i ].m_MultiBlend.y );
meshBuilder.TexCoord4fv( 5, temp.Base() );
temp.Init( pVert[ i ].m_vBlendColors[ 2 ], pVert[ i ].m_MultiBlend.z );
meshBuilder.TexCoord4fv( 6, temp.Base() );
temp.Init( pVert[ i ].m_vBlendColors[ 3 ], pVert[ i ].m_MultiBlend.w );
meshBuilder.TexCoord4fv( 7, temp.Base() );
// lightmapped_4wayblend uses this
meshBuilder.Specular4fv( pVert[ i ].m_MultiBlend.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();
}
//-----------------------------------------------------------------------------
// 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 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);
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pFile -
// *pDisp -
// Output : ChunkFileResult_t
//-----------------------------------------------------------------------------
ChunkFileResult_t CMapDisp::LoadDispMultiBlendCallback(CChunkFile *pFile, CMapDisp *pDisp)
{
return(pFile->ReadChunk((KeyHandler_t)LoadDispMultiBlendKeyCallback, pDisp));
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pFile -
// *pDisp -
// Output : ChunkFileResult_t
//-----------------------------------------------------------------------------
ChunkFileResult_t CMapDisp::LoadDispMultiBlendKeyCallback(const char *szKey, const char *szValue, CMapDisp *pDisp)
{
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)
{
Vector4D vMultiBlend;
vMultiBlend.x = ( float )atof( pszNext );
pszNext = strtok(NULL, " ");
vMultiBlend.y = ( float )atof( pszNext );
pszNext = strtok(NULL, " ");
vMultiBlend.z = ( float )atof( pszNext );
pszNext = strtok(NULL, " ");
vMultiBlend.w = ( float )atof( pszNext );
pszNext = strtok(NULL, " ");
pDisp->m_CoreDispInfo.SetMultiBlend( nIndex, vMultiBlend );
nIndex++;
}
}
return(ChunkFile_Ok);
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pFile -
// *pDisp -
// Output : ChunkFileResult_t
//-----------------------------------------------------------------------------
ChunkFileResult_t CMapDisp::LoadDispAlphaBlendCallback(CChunkFile *pFile, CMapDisp *pDisp)
{
return(pFile->ReadChunk((KeyHandler_t)LoadDispAlphaBlendKeyCallback, pDisp));
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pFile -
// *pDisp -
// Output : ChunkFileResult_t
//-----------------------------------------------------------------------------
ChunkFileResult_t CMapDisp::LoadDispAlphaBlendKeyCallback(const char *szKey, const char *szValue, CMapDisp *pDisp)
{
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)
{
Vector4D vMultiBlend;
vMultiBlend.x = ( float )atof( pszNext );
pszNext = strtok(NULL, " ");
vMultiBlend.y = ( float )atof( pszNext );
pszNext = strtok(NULL, " ");
vMultiBlend.z = ( float )atof( pszNext );
pszNext = strtok(NULL, " ");
vMultiBlend.w = ( float )atof( pszNext );
pszNext = strtok(NULL, " ");
pDisp->m_CoreDispInfo.SetAlphaBlend( nIndex, vMultiBlend );
nIndex++;
}
}
return(ChunkFile_Ok);
}
static int nMultiBlendColorIndex = 0;
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pFile -
// *pDisp -
// Output : ChunkFileResult_t
//-----------------------------------------------------------------------------
ChunkFileResult_t CMapDisp::LoadDispMultiBlendColorCallback0(CChunkFile *pFile, CMapDisp *pDisp)
{
nMultiBlendColorIndex = 0;
return(pFile->ReadChunk((KeyHandler_t)LoadDispMultiBlendColorKeyCallback, pDisp));
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pFile -
// *pDisp -
// Output : ChunkFileResult_t
//-----------------------------------------------------------------------------
ChunkFileResult_t CMapDisp::LoadDispMultiBlendColorCallback1(CChunkFile *pFile, CMapDisp *pDisp)
{
nMultiBlendColorIndex = 1;
return(pFile->ReadChunk((KeyHandler_t)LoadDispMultiBlendColorKeyCallback, pDisp));
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pFile -
// *pDisp -
// Output : ChunkFileResult_t
//-----------------------------------------------------------------------------
ChunkFileResult_t CMapDisp::LoadDispMultiBlendColorCallback2(CChunkFile *pFile, CMapDisp *pDisp)
{
nMultiBlendColorIndex = 2;
return(pFile->ReadChunk((KeyHandler_t)LoadDispMultiBlendColorKeyCallback, pDisp));
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pFile -
// *pDisp -
// Output : ChunkFileResult_t
//-----------------------------------------------------------------------------
ChunkFileResult_t CMapDisp::LoadDispMultiBlendColorCallback3(CChunkFile *pFile, CMapDisp *pDisp)
{
nMultiBlendColorIndex = 3;
return(pFile->ReadChunk((KeyHandler_t)LoadDispMultiBlendColorKeyCallback, pDisp));
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pFile -
// *pDisp -
// Output : ChunkFileResult_t
//-----------------------------------------------------------------------------
ChunkFileResult_t CMapDisp::LoadDispMultiBlendColorKeyCallback(const char *szKey, const char *szValue, CMapDisp *pDisp)
{
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)
{
Vector vMultiBlendColor;
vMultiBlendColor.x = ( float )atof( pszNext );
pszNext = strtok(NULL, " ");
vMultiBlendColor.y = ( float )atof( pszNext );
pszNext = strtok(NULL, " ");
vMultiBlendColor.z = ( float )atof( pszNext );
pszNext = strtok(NULL, " ");
pDisp->m_CoreDispInfo.SetMultiBlendColor( nIndex, nMultiBlendColorIndex, vMultiBlendColor );
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 );
Handlers.AddHandler("multiblend", (ChunkHandler_t)LoadDispMultiBlendCallback, this );
Handlers.AddHandler("alphablend", (ChunkHandler_t)LoadDispAlphaBlendCallback, this );
Assert( MAX_MULTIBLEND_CHANNELS == 4 );
Handlers.AddHandler("multiblend_color_0", (ChunkHandler_t)LoadDispMultiBlendColorCallback0, this );
Handlers.AddHandler("multiblend_color_1", (ChunkHandler_t)LoadDispMultiBlendColorCallback1, this );
Handlers.AddHandler("multiblend_color_2", (ChunkHandler_t)LoadDispMultiBlendColorCallback2, this );
Handlers.AddHandler("multiblend_color_3", (ChunkHandler_t)LoadDispMultiBlendColorCallback3, 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();
}
}
//
// Save multi blends
//
if ( eResult == ChunkFile_Ok )
{
int nRows = (1 << power) + 1;
int nCols = nRows;
bool bHasMultiBlend = false;
for (int nRow = 0; nRow < nRows; nRow++)
{
for (int nCol = 0; nCol < nCols; nCol++)
{
int nIndex = nRow * nCols + nCol;
Vector4D vMultiBlend;
m_CoreDispInfo.GetMultiBlend( nIndex, vMultiBlend );
if ( vMultiBlend != Vector4D( 0.0f, 0.0f, 0.0f, 0.0f ) )
{
bHasMultiBlend = true;
break;
}
}
}
if ( bHasMultiBlend == true )
{
eResult = pFile->BeginChunk( "multiblend" );
if( eResult == ChunkFile_Ok )
{
char szBuf[ MAX_KEYVALUE_LEN ];
char szTemp[ 256 ];
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 == false )
{
strcat( szBuf, " " );
}
bFirst = false;
Vector4D vMultiBlend;
m_CoreDispInfo.GetMultiBlend( nIndex, vMultiBlend );
Assert( MAX_MULTIBLEND_CHANNELS == 4 );
sprintf( szTemp, "%g %g %g %g", vMultiBlend.x, vMultiBlend.y, vMultiBlend.z, vMultiBlend.w );
strcat( szBuf, szTemp );
}
char szKey[ 10 ];
sprintf( szKey, "row%d", nRow );
eResult = pFile->WriteKeyValue( szKey, szBuf );
}
}
if (eResult == ChunkFile_Ok)
{
eResult = pFile->EndChunk();
}
eResult = pFile->BeginChunk( "alphablend" );
if( eResult == ChunkFile_Ok )
{
char szBuf[ MAX_KEYVALUE_LEN ];
char szTemp[ 256 ];
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 == false )
{
strcat( szBuf, " " );
}
bFirst = false;
Vector4D vAlphaBlend;
m_CoreDispInfo.GetAlphaBlend( nIndex, vAlphaBlend );
Assert( MAX_MULTIBLEND_CHANNELS == 4 );
sprintf( szTemp, "%g %g %g %g", vAlphaBlend.x, vAlphaBlend.y, vAlphaBlend.z, vAlphaBlend.w );
strcat( szBuf, szTemp );
}
char szKey[ 10 ];
sprintf( szKey, "row%d", nRow );
eResult = pFile->WriteKeyValue( szKey, szBuf );
}
}
if (eResult == ChunkFile_Ok)
{
eResult = pFile->EndChunk();
}
for( int i = 0; i < MAX_MULTIBLEND_CHANNELS; i++ )
{
char temp[ 128 ];
sprintf( temp, "multiblend_color_%d", i );
eResult = pFile->BeginChunk( temp );
if( eResult == ChunkFile_Ok )
{
char szBuf[ MAX_KEYVALUE_LEN ];
char szTemp[ 256 ];
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 == false )
{
strcat( szBuf, " " );
}
bFirst = false;
Vector4D vMultiBlend, vAlphaBlend;
Vector vColorBlend[ MAX_MULTIBLEND_CHANNELS ];
m_CoreDispInfo.GetMultiBlend( nIndex, vMultiBlend, vAlphaBlend, vColorBlend[ 0 ], vColorBlend[ 1 ], vColorBlend[ 2 ], vColorBlend[ 3 ] );
sprintf( szTemp, "%g %g %g", vColorBlend[ i ].x, vColorBlend[ i ].y, vColorBlend[ i ].z );
strcat( szBuf, szTemp );
}
char szKey[ 10 ];
sprintf( szKey, "row%d", nRow );
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;
}