|
|
//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $Workfile: $
// $Date: $
// $NoKeywords: $
//=============================================================================//
#include <stdafx.h>
#include "UtlLinkedList.h"
//#include "DispManager.h"
#include "MapFace.h"
#include "MapDisp.h"
#include "DispSubdiv.h"
#include "History.h"
#include "tier0/minidump.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
//=============================================================================
//
// Global Displacement Manager
//
class CEditDispMgr : public IEditDispMgr { public: // functions
CEditDispMgr(); virtual ~CEditDispMgr();
EditDispHandle_t Create( void ); void Destroy( EditDispHandle_t handle );
CMapDisp *GetDisp( EditDispHandle_t handle );
private: // variables
CUtlLinkedList<CMapDisp, EditDispHandle_t> m_AllocList; };
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
IEditDispMgr* EditDispMgr( void ) { static CEditDispMgr s_EditDispMgr; return &s_EditDispMgr; }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CEditDispMgr::CEditDispMgr() { }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CEditDispMgr::~CEditDispMgr() { m_AllocList.Purge(); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
EditDispHandle_t CEditDispMgr::Create( void ) { EditDispHandle_t handle = m_AllocList.AddToTail(); if( handle != EDITDISPHANDLE_INVALID ) { CMapDisp *pDisp = &m_AllocList.Element( handle ); pDisp->SetEditHandle( handle ); }
return handle; }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEditDispMgr::Destroy( EditDispHandle_t handle ) { if ( m_AllocList.IsValidIndex( handle ) ) { m_AllocList.Remove( handle ); } else { static bool bNoToAll = false; if ( !bNoToAll ) { int result = AfxMessageBox( "CEditDispMgr::Destroy - invalid handle.\n" "Write minidump?\n", MB_YESNO ); if ( result == IDYES ) { // Generate a minidump.
WriteMiniDump(); } else { bNoToAll = true; } } } }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CMapDisp *CEditDispMgr::GetDisp( EditDispHandle_t handle ) { if( m_AllocList.IsValidIndex( handle ) ) { return &m_AllocList.Element( handle ); }
return NULL; }
//=============================================================================
//
// World Displacement Manager(s)
//
class CWorldEditDispMgr : public IWorldEditDispMgr { public: // functions
// construction/deconstruction
CWorldEditDispMgr(); virtual ~CWorldEditDispMgr();
// world list functionals
int WorldCount( void ); CMapDisp *GetFromWorld( int iWorldList ); CMapDisp *GetFromWorld( EditDispHandle_t handle );
void AddToWorld( EditDispHandle_t handle ); void RemoveFromWorld( EditDispHandle_t handle ); void FindWorldNeighbors( EditDispHandle_t handle );
// selection list functions
int SelectCount( void ); void SelectClear( void ); CMapDisp *GetFromSelect( int iSelectList );
void AddToSelect( EditDispHandle_t handle ); void RemoveFromSelect( EditDispHandle_t handle ); bool IsInSelect( EditDispHandle_t handle );
void CatmullClarkSubdivide( void );
void PreUndo( const char *pszMarkName ); void Undo( EditDispHandle_t handle, bool bAddNeighbors ); void PostUndo( void );
virtual int NumSharedPoints( CMapDisp *pDisp, CMapDisp *pNeighborDisp, int *edge1, int *edge2 );
private: // functions
void TestNeighbors( CMapDisp *pDisp, CMapDisp *pNeighborDisp ); int GetCornerIndex( int index ); int GetEdgeIndex( int *edge );
bool IsInKeptList( CMapClass *pObject );
private: // variables
CUtlVector<EditDispHandle_t> m_WorldList; CUtlVector<EditDispHandle_t> m_SelectList;
IEditDispSubdivMesh *m_pSubdivMesh; // pointer to the subdivision mesh
CUtlVector<CMapClass*> m_aKeptList; };
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
IWorldEditDispMgr *CreateWorldEditDispMgr( void ) { return new CWorldEditDispMgr; }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void DestroyWorldEditDispMgr( IWorldEditDispMgr **pDispMgr ) { if( *pDispMgr ) { delete *pDispMgr; *pDispMgr = NULL; } }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CWorldEditDispMgr::CWorldEditDispMgr() { // allocate the subdivision mesh
m_pSubdivMesh = CreateEditDispSubdivMesh(); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CWorldEditDispMgr::~CWorldEditDispMgr() { // clear the displacement manager lists
m_WorldList.Purge(); m_SelectList.Purge();
// de-allocate the subdivision mesh
DestroyEditDispSubdivMesh( &m_pSubdivMesh );
m_aKeptList.Purge(); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CWorldEditDispMgr::WorldCount( void ) { return m_WorldList.Count(); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CMapDisp *CWorldEditDispMgr::GetFromWorld( int iWorldList ) { // no assert because the .Element( ) takes care of that!
EditDispHandle_t handle = m_WorldList.Element( iWorldList ); return EditDispMgr()->GetDisp( handle ); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CMapDisp *CWorldEditDispMgr::GetFromWorld( EditDispHandle_t handle ) { int ndx = m_WorldList.Find( handle ); if( ndx != -1 ) { return EditDispMgr()->GetDisp( handle ); }
return NULL; }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWorldEditDispMgr::AddToWorld( EditDispHandle_t handle ) { int ndx = m_WorldList.Find( handle ); if( ndx == -1 ) { ndx = m_WorldList.AddToTail(); m_WorldList[ndx] = handle; }
// Update itself when it gets added to the world.
CMapDisp *pDisp = EditDispMgr()->GetDisp( handle ); if ( pDisp ) { pDisp->UpdateData(); } }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWorldEditDispMgr::RemoveFromWorld( EditDispHandle_t handle ) { int ndx = m_WorldList.Find( handle ); if( ndx != -1 ) { m_WorldList.Remove( ndx ); } }
//-----------------------------------------------------------------------------
// Purpose:
// NOTE: this will be in the common code soon!!!!!!!!!
//-----------------------------------------------------------------------------
void CWorldEditDispMgr::FindWorldNeighbors( EditDispHandle_t handle ) { // get the current displacement
CMapDisp *pDisp = GetFromWorld( handle ); if( !pDisp ) return;
//
// compare against all of the displacements in the world
//
int count = WorldCount(); for( int ndx = 0; ndx < count; ndx++ ) { // get the potential neighbor surface
CMapDisp *pNeighborDisp = GetFromWorld( ndx );
// check for valid neighbor and don't compare against self
if( !pNeighborDisp || ( pNeighborDisp == pDisp ) ) continue;
// displacements at different resolutions are not considered neighbors
// regardless of edge connectivity
if( pDisp->GetPower() != pNeighborDisp->GetPower() ) continue;
// test for neighboring edge/corner properties
TestNeighbors( pDisp, pNeighborDisp ); } }
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CWorldEditDispMgr::TestNeighbors( CMapDisp *pDisp, CMapDisp *pNeighborDisp ) { //
// find the number of shared points between the two displacements (corners, edges)
// NOTE: should use only 2, but face may be right on top of one another
//
int edge1[4], edge2[4]; int sharedPointCount = NumSharedPoints( pDisp, pNeighborDisp, edge1, edge2 );
//
// set the neighboring info
//
if( sharedPointCount == 1 ) { int cornerIndex = GetCornerIndex( edge1[0] ); int neighborCornerIndex = GetCornerIndex( edge2[0] );
if ( ( cornerIndex != -1 ) && ( neighborCornerIndex != -1 ) ) { CMapFace *pNeighborFace = ( CMapFace* )pNeighborDisp->GetParent(); pDisp->AddCornerNeighbor( cornerIndex, pNeighborFace->GetDisp(), neighborCornerIndex ); } } else if( sharedPointCount == 2 ) { //
// get edge indices
//
int edgeIndex = GetEdgeIndex( edge1 ); int neighborEdgeIndex = GetEdgeIndex( edge2 );
if ( ( edgeIndex != -1 ) && ( neighborEdgeIndex != -1 ) ) { CMapFace *pNeighborFace = ( CMapFace* )pNeighborDisp->GetParent(); pDisp->SetEdgeNeighbor( edgeIndex, pNeighborFace->GetDisp(), neighborEdgeIndex ); } } }
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool ComparePoints( const Vector& v1, const Vector& v2, float tolerance ) { for( int axis = 0; axis < 3; axis++ ) { if( fabs( v1[axis] - v2[axis] ) > tolerance ) return false; } return true; }
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int CWorldEditDispMgr::NumSharedPoints( CMapDisp *pDisp, CMapDisp *pNeighborDisp, int *edge1, int *edge2 ) { int ptCount = 0;
for( int i = 0; i < 4; i++ ) { int j; for( j = 0; j < 4; j++ ) { Vector pt1, pt2; pDisp->GetSurfPoint( i, pt1 ); pNeighborDisp->GetSurfPoint( j, pt2 ); if( ComparePoints( pt1, pt2, 0.01f ) ) break; }
if( j == 4 ) continue;
edge1[ptCount] = i; edge2[ptCount] = j; ptCount++; }
return ptCount; }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CWorldEditDispMgr::GetCornerIndex( int index ) { switch( index ) { case 0: return 0; case 1: return 2; case 2: return 3; case 3: return 1; default: return -1; } }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CWorldEditDispMgr::GetEdgeIndex( int *edge ) { if( ( edge[0] == 0 && edge[1] == 1 ) || ( edge[0] == 1 && edge[1] == 0 ) ) return 0;
if( ( edge[0] == 1 && edge[1] == 2 ) || ( edge[0] == 2 && edge[1] == 1 ) ) return 1; if( ( edge[0] == 2 && edge[1] == 3 ) || ( edge[0] == 3 && edge[1] == 2 ) ) return 2;
if( ( edge[0] == 3 && edge[1] == 0 ) || ( edge[0] == 0 && edge[1] == 3 ) ) return 3;
return -1; }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CWorldEditDispMgr::SelectCount( void ) { return m_SelectList.Count(); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWorldEditDispMgr::SelectClear( void ) { m_SelectList.RemoveAll(); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CMapDisp *CWorldEditDispMgr::GetFromSelect( int iSelectList ) { // no assert because the .Element( ) takes care of that!
EditDispHandle_t handle = m_SelectList.Element( iSelectList ); return EditDispMgr()->GetDisp( handle ); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWorldEditDispMgr::AddToSelect( EditDispHandle_t handle ) { int ndx = m_SelectList.Find( handle ); if( ndx == -1 ) { ndx = m_SelectList.AddToTail(); m_SelectList[ndx] = handle; } } //-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWorldEditDispMgr::RemoveFromSelect( EditDispHandle_t handle ) { int ndx = m_SelectList.Find( handle ); if( ndx != -1 ) { m_SelectList.Remove( handle ); } }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CWorldEditDispMgr::IsInSelect( EditDispHandle_t handle ) { int ndx = m_SelectList.Find( handle ); return ( ndx != -1 ); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWorldEditDispMgr::CatmullClarkSubdivide( void ) { // change the mouse to hourglass, so level designers know something is
// happening
HCURSOR oldCursor = SetCursor( LoadCursor( NULL, IDC_WAIT ) );
//
// add all of the displacements in the selection list into the UNDO
// system
//
PreUndo( "Subdivision" );
int selectCount = m_SelectList.Count(); for( int ndxSelect = 0; ndxSelect < selectCount; ndxSelect++ ) { // get the current displacement surface
CMapDisp *pDisp = GetFromSelect( ndxSelect ); if( pDisp ) { Undo( pDisp->GetEditHandle(), false ); } }
PostUndo();
// initialize the subdivision mesh
m_pSubdivMesh->Init(); //
// add all of the displacements in the selection list into the
// subdivision mesh
//
for( int ndxSelect = 0; ndxSelect < selectCount; ndxSelect++ ) { // get the current displacement surface
CMapDisp *pDisp = GetFromSelect( ndxSelect ); if( pDisp ) { m_pSubdivMesh->AddDispTo( pDisp ); } }
// subdivision
m_pSubdivMesh->DoCatmullClarkSubdivision();
//
// get back subdivided data for all displacement surfaces in the
// selection list
//
for( int ndxSelect = 0; ndxSelect < selectCount; ndxSelect++ ) { // get the current displacement surface
CMapDisp *pDisp = GetFromSelect( ndxSelect ); if( pDisp ) { m_pSubdivMesh->GetDispFrom( pDisp ); } }
m_pSubdivMesh->Shutdown();
// set the cursor back
SetCursor( oldCursor ); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CWorldEditDispMgr::IsInKeptList( CMapClass *pObject ) { if ( m_aKeptList.Find( pObject ) == -1 ) return false;
return true; }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWorldEditDispMgr::PreUndo( const char *pszMarkName ) { GetHistory()->MarkUndoPosition( NULL, pszMarkName ); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWorldEditDispMgr::Undo( EditDispHandle_t hDisp, bool bAddNeighbors ) { // Check the handle.
Assert( hDisp != EDITDISPHANDLE_INVALID ); if( hDisp == EDITDISPHANDLE_INVALID ) return;
// Get the map class object that contains the displacement surface.
CMapDisp *pDisp = EditDispMgr()->GetDisp( hDisp ); if ( !pDisp ) return;
CMapFace *pFace = ( CMapFace* )pDisp->GetParent(); CMapSolid *pSolid = ( CMapSolid* )pFace->GetParent(); CMapClass *pObject = ( CMapClass* )pSolid; if ( !pObject ) return;
// Keep the map class object for undo.
if ( !IsInKeptList( pObject ) ) { m_aKeptList.AddToTail( pObject ); GetHistory()->Keep( pObject ); }
// Keep the map class (displacement parent) neighbor objects for undo.
if ( bAddNeighbors ) { int nNeighborOrient; EditDispHandle_t hNeighbor;
for ( int iNeighbor = 0; iNeighbor < 4; ++iNeighbor ) { pDisp = EditDispMgr()->GetDisp( hDisp ); if ( pDisp ) { //
// Edge Neighbors.
//
pDisp->GetEdgeNeighbor( iNeighbor, hNeighbor, nNeighborOrient ); if( hNeighbor != EDITDISPHANDLE_INVALID ) { CMapDisp *pNeighborDisp = EditDispMgr()->GetDisp( hNeighbor ); CMapFace *pNeighborFace = ( CMapFace* )pNeighborDisp->GetParent(); CMapSolid *pNeighborSolid = ( CMapSolid* )pNeighborFace->GetParent(); CMapClass *pNeighborObject = ( CMapClass* )pNeighborSolid; if ( !IsInKeptList( pNeighborObject ) ) { m_aKeptList.AddToTail( pNeighborObject ); GetHistory()->Keep( pNeighborObject ); } } } pDisp = EditDispMgr()->GetDisp( hDisp ); if ( pDisp ) { //
// Corner Neighbors.
//
int nCornerCount = pDisp->GetCornerNeighborCount( iNeighbor ); for( int iCorner = 0; iCorner < nCornerCount; ++iCorner ) { pDisp = EditDispMgr()->GetDisp( hDisp ); if ( pDisp ) { pDisp->GetCornerNeighbor( iNeighbor, iCorner, hNeighbor, nNeighborOrient ); CMapDisp *pNeighborDisp = EditDispMgr()->GetDisp( hNeighbor ); if ( pNeighborDisp ) { CMapFace *pNeighborFace = ( CMapFace* )pNeighborDisp->GetParent(); CMapSolid *pNeighborSolid = ( CMapSolid* )pNeighborFace->GetParent(); CMapClass *pNeighborObject = ( CMapClass* )pNeighborSolid; if ( !IsInKeptList( pNeighborObject ) ) { m_aKeptList.AddToTail( pNeighborObject ); GetHistory()->Keep( pNeighborObject ); } } } } } } } }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWorldEditDispMgr::PostUndo( void ) { // Clear the kept list.
m_aKeptList.RemoveAll(); }
|