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