//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: // // $Workfile: $ // $Date: $ // $NoKeywords: $ //=============================================================================// #include #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 //============================================================================= // // 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 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 m_WorldList; CUtlVector m_SelectList; IEditDispSubdivMesh *m_pSubdivMesh; // pointer to the subdivision mesh CUtlVector 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(); }