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