//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CRecastMgr::Update( float dt ) { VPROF_BUDGET( "CRecastMgr::Update", "RecastNav" ); #ifndef CLIENT_DLL UpdateRebuildPartial(); #endif // CLIENT_DLL for ( int i = m_Meshes.First(); i != m_Meshes.InvalidIndex(); i = m_Meshes.Next(i ) ) { CRecastMesh *pMesh = m_Meshes[ i ]; pMesh->Update( dt ); } }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CRecastMgr::InsertMesh( const char *name, float agentRadius, float agentHeight, float agentMaxClimb, float agentMaxSlope ) { CRecastMesh *pMesh = GetMesh( name ); if( pMesh ) { Warning( "CRecastMgr::InsertMesh: %s already exists!\n", name ); return false; } pMesh = new CRecastMesh(); pMesh->Init( name, agentRadius, agentHeight, agentMaxClimb, agentMaxSlope ); m_Meshes.Insert( pMesh->GetName(), pMesh ); return true; }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CRecastMgr::BuildMesh( CMapMesh *pMapMesh, const char *name ) { CRecastMesh *pMesh = GetMesh( name ); if( !pMesh ) { pMesh = new CRecastMesh(); pMesh->Init( name ); m_Meshes.Insert( pMesh->GetName(), pMesh ); } if( pMesh->Build( pMapMesh ) ) { return true; } return false; }
bool CRecastMgr::InitDefaultMeshes() { // Ensures default meshes exists, even if they don't have a mesh loaded. for( int i = 0; i < ARRAYSIZE( s_DefaultMeshNames ); i++ ) { CRecastMesh *pMesh = GetMesh( s_DefaultMeshNames[i] ); if( !pMesh ) { pMesh = new CRecastMesh(); pMesh->Init( s_DefaultMeshNames[i] ); m_Meshes.Insert( pMesh->GetName(), pMesh ); } } return true; }
//----------------------------------------------------------------------------- // Purpose: Adds an obstacle based on bounds and height for entity // TODO: //----------------------------------------------------------------------------- bool CRecastMgr::AddEntBoxObstacle( CBaseEntity *pEntity, const Vector &mins, const Vector &maxs, float height ) { if( !pEntity ) return false; matrix3x4_t transform; // model to world transformation AngleMatrix( QAngle(0, pEntity->GetAbsAngles()[YAW], 0), pEntity->GetAbsOrigin(), transform ); //AngleMatrix( pEntity->GetAbsAngles(), pEntity->GetAbsOrigin(), transform ); Vector convexHull[4]; NavObstacleArray_t &obstacle = FindOrCreateObstacle( pEntity ); bool bSuccess = true; for ( int i = m_Meshes.First(); i != m_Meshes.InvalidIndex(); i = m_Meshes.Next(i ) ) { CRecastMesh *pMesh = m_Meshes[ i ]; // TODO: better check. Needs to filter out obstacles for air mesh if( pMesh->GetAgentHeight() - 50.0f > height ) continue; float erodeDist = pMesh->GetAgentRadius() + 8.0f; VectorTransform( mins + Vector(-erodeDist, -erodeDist, 0), transform, convexHull[0] ); VectorTransform( Vector(mins.x, maxs.y, mins.z) + Vector(-erodeDist, erodeDist, 0), transform, convexHull[1] ); VectorTransform( Vector(maxs.x, maxs.y, mins.z) + Vector(erodeDist, erodeDist, 0), transform, convexHull[2] ); VectorTransform( Vector(maxs.x, mins.y, mins.z) + Vector(erodeDist, -erodeDist, 0), transform, convexHull[3] ); /*for( int j = 0; j < 4; j++ ) { int next = (j+1) == 4 ? 0 : j + 1; NDebugOverlay::Line( convexHull[j], convexHull[next], 0, 255, 0, true, 10.0f ); }*/ obstacle.obs.AddToTail(); obstacle.obs.Tail().meshIndex = i; obstacle.obs.Tail().ref = pMesh->AddTempObstacle( pEntity->GetAbsOrigin(), convexHull, 4, height ); if( obstacle.obs.Tail().ref == 0 ) bSuccess = false; } return bSuccess; }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CRecastMgr::Reset() { m_bLoaded = false; for ( int i = m_Meshes.First(); i != m_Meshes.InvalidIndex(); i = m_Meshes.Next(i ) ) { CRecastMesh *pMesh = m_Meshes[ i ]; pMesh->Reset(); } m_Meshes.PurgeAndDeleteElements(); #ifndef CLIENT_DLL m_pendingPartialMeshUpdates.Purge(); if( m_pMapMesh ) { delete m_pMapMesh; m_pMapMesh = NULL; } #endif // CLIENT_DLL }
//----------------------------------------------------------------------------- // Purpose: Saves the generated navigation meshes //----------------------------------------------------------------------------- void CRecastMgr::DebugRender() { int idx = m_Meshes.Find( recast_debug_mesh.GetString() ); if( idx == m_Meshes.InvalidIndex() ) { // Might be visualizing a server mesh that does not exist on the client // Insert dummy mesh on the fly. IRecastMgr *pRecastMgr = warsextension->GetRecastMgr(); if( pRecastMgr->GetNavMesh( recast_debug_mesh.GetString() ) ) { CRecastMesh *pMesh = new CRecastMesh(); pMesh->Init( recast_debug_mesh.GetString() ); idx = m_Meshes.Insert( pMesh->GetName(), pMesh ); } } if( m_Meshes.IsValidIndex( idx ) ) { m_Meshes[idx]->DebugRender(); } }
//----------------------------------------------------------------------------- // Purpose: Determines best nav mesh radius/height //----------------------------------------------------------------------------- int CRecastMgr::FindBestMeshForRadiusHeight( float radius, float height ) { int bestIdx = -1; float fBestRadiusDiff = 0; float fBestHeightDiff = 0; for ( int i = m_Meshes.First(); i != m_Meshes.InvalidIndex(); i = m_Meshes.Next(i ) ) { CRecastMesh *pMesh = m_Meshes[ i ]; if( !pMesh->IsLoaded() ) { continue; } // Only consider fitting meshes if( radius > pMesh->GetAgentRadius() || height > pMesh->GetAgentHeight() ) { continue; } // From these meshes, pick the best fitting one float fRadiusDiff = fabs( pMesh->GetAgentRadius() - radius ); float fHeightDiff = fabs( pMesh->GetAgentHeight() - height ); if( bestIdx == -1 || (fRadiusDiff + fHeightDiff <= fBestRadiusDiff + fBestHeightDiff) ) { bestIdx = i; fBestRadiusDiff = fRadiusDiff; fBestHeightDiff = fHeightDiff; } } return bestIdx; }
//----------------------------------------------------------------------------- // Purpose: Adds an obstacle with radius and height for entity //----------------------------------------------------------------------------- bool CRecastMgr::AddEntRadiusObstacle( CBaseEntity *pEntity, float radius, float height ) { if( !pEntity ) return false; NavObstacleArray_t &obstacle = FindOrCreateObstacle( pEntity ); bool bSuccess = true; for ( int i = m_Meshes.First(); i != m_Meshes.InvalidIndex(); i = m_Meshes.Next(i ) ) { CRecastMesh *pMesh = m_Meshes[ i ]; // TODO: better check. Needs to filter out obstacles for air mesh if( pMesh->GetAgentHeight() - 50.0f > height ) continue; obstacle.obs.AddToTail(); obstacle.obs.Tail().meshIndex = i; obstacle.obs.Tail().ref = pMesh->AddTempObstacle( pEntity->GetAbsOrigin(), radius + 1.0f, height ); if( obstacle.obs.Tail().ref == 0 ) bSuccess = false; } return bSuccess; }
//----------------------------------------------------------------------------- // Purpose: Removes any obstacle associated with the entity //----------------------------------------------------------------------------- bool CRecastMgr::RemoveEntObstacles( CBaseEntity *pEntity ) { if( !pEntity ) return false; bool bSuccess = true; int idx = m_Obstacles.Find( pEntity ); if( m_Obstacles.IsValidIndex( idx ) ) { for( int i = 0; i < m_Obstacles[idx].obs.Count(); i++ ) { if( m_Meshes.IsValidIndex( m_Obstacles[idx].obs[i].meshIndex ) ) { CRecastMesh *pMesh = m_Meshes[ m_Obstacles[idx].obs[i].meshIndex ]; if( !pMesh->RemoveObstacle( m_Obstacles[idx].obs[i].ref ) ) bSuccess = false; } } m_Obstacles.RemoveAt( idx ); pEntity->SetNavObstacleRef( m_Obstacles.InvalidIndex() ); } return true; }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- const char *CRecastMgr::FindBestMeshNameForEntity( CBaseEntity *pEntity ) { int idx = FindBestMeshForEntity( pEntity ); CRecastMesh *pMesh = GetMesh( idx ); return pMesh ? pMesh->GetName() : NULL; }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- const char *CRecastMgr::FindBestMeshNameForRadiusHeight( float radius, float height ) { int idx = FindBestMeshForRadiusHeight( radius, height ); CRecastMesh *pMesh = GetMesh( idx ); return pMesh ? pMesh->GetName() : NULL; }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CRecastMgr::IsMeshLoaded( const char *name ) { CRecastMesh *pMesh = GetMesh( name ); return pMesh != NULL && pMesh->IsLoaded(); }