//======== (C) Copyright 2001 Charles G. Cleveland All rights reserved. ========= // // The copyright to the contents herein is the property of Charles G. Cleveland. // The contents may be used and/or copied only with the written permission of // Charles G. Cleveland, or in accordance with the terms and conditions stipulated in // the agreement/contract under which the contents have been supplied. // // Purpose: // // $Workfile: AvHParticleSystemManager.cpp $ // $Date: 2002/10/28 20:36:06 $ // //------------------------------------------------------------------------------- // $Log: AvHParticleSystemManager.cpp,v $ // Revision 1.14 2002/10/28 20:36:06 Flayra // - Tweaked particle culling time to work with umbra and spores // // Revision 1.13 2002/10/24 21:34:46 Flayra // - Fixes for particle culling again! // // Revision 1.12 2002/07/10 14:44:10 Flayra // - Visibility fixes (too many PSs drawing, now they expire when not visible for awhile) // // Revision 1.11 2002/05/23 02:33:20 Flayra // - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. // //=============================================================================== #include "mod/AvHParticleSystemManager.h" #ifdef AVH_CLIENT #include "cl_dll/hud.h" #include "cl_dll/cl_util.h" // Triangle rendering apis are in gEngfuncs.pTriAPI #include "common/const.h" #include "common/entity_state.h" #include "common/cl_entity.h" #include "common/triangleapi.h" #include #include "mod/AvHParticleTemplateClient.h" #else #include "mod/AvHParticleTemplate.h" #include "mod/AvHParticleTemplateServer.h" #include "dlls/util.h" #endif #include "pm_shared/pm_debug.h" #ifdef AVH_CLIENT AvHParticleTemplateListClient gParticleTemplateList; extern DebugEntityListType gCubeDebugEntities; #else //AvHParticleTemplateList gParticleTemplateList; AvHParticleTemplateListServer gParticleTemplateList; #endif AvHParticleSystemManager* AvHParticleSystemManager::sInstance = NULL; const float kUpdateIncrement = 0.0f; AvHParticleSystemManager* AvHParticleSystemManager::Instance() { if(!sInstance) { sInstance = new AvHParticleSystemManager(); } return sInstance; } void AvHParticleSystemManager::Init() { #ifdef AVH_CLIENT this->mStarted = false; #else this->mStarted = true; #endif this->mReloadFromTemplates = false; this->mUpdatedTime = 0.0f; this->mTimePassed = 0.0f; } AvHParticleSystemManager::AvHParticleSystemManager() { this->Init(); } void AvHParticleSystemManager::CreateParticleSystem(uint32 inTemplateIndex, int inParentEntityIndex, ParticleSystemHandle inHandle) { this->mQueuedCreateList.push_back(QueuedParticleSystem(inTemplateIndex, this->GetTime(), inParentEntityIndex, inHandle)); } void AvHParticleSystemManager::CreateParticleSystem(uint32 inTemplateIndex, vec3_t inOrigin, vec3_t* inNormal) { QueuedParticleSystem theParticleSystem(inTemplateIndex, this->GetTime(), inOrigin); if(inNormal) { theParticleSystem.SetNormal(*inNormal); } this->mQueuedCreateList.push_back(theParticleSystem); } bool AvHParticleSystemManager::CreateParticleSystem(const string& inTemplateName, vec3_t inOrigin, vec3_t* inNormal) { bool theSuccess = false; // Lookup template by name uint32 theTemplateIndex = 0; if(::gParticleTemplateList.GetTemplateIndexWithName(inTemplateName, theTemplateIndex)) { this->CreateParticleSystem(theTemplateIndex, inOrigin, inNormal); theSuccess = true; } return theSuccess; } void AvHParticleSystemManager::CreateParticleSystemIfNotCreated(int inIndex, int inTemplateIndex, int inHandle) { bool theEntityHasParticles = false; // Look in list of entities, see if we've created one for this entity for(ParticleEntityList::iterator theIter = this->mParticleEntities.begin(); theIter != this->mParticleEntities.end(); theIter++) { if(*theIter == inIndex) { theEntityHasParticles = true; break; } } if(!theEntityHasParticles) { // If not, create one this->CreateParticleSystem(inTemplateIndex, inIndex, (ParticleSystemHandle)inHandle); // Add entity to list this->mParticleEntities.push_back(inIndex); } } void AvHParticleSystemManager::DestroyParticleSystemIfNotDestroyed(int inIndex, ParticleSystemHandle inHandle) { // Look in list of entities, see if we've created one for this entity for(ParticleEntityList::iterator theIter = this->mParticleEntities.begin(); theIter != this->mParticleEntities.end(); theIter++) { if(*theIter == inIndex) { // If so, destroy this particle system. Note that this particle system could have already // expired, that's ok. The list of particle entities just needs to be kept up to date. this->DestroyParticleSystem(inHandle); // Remove entity from list this->mParticleEntities.erase(theIter); break; } } } void AvHParticleSystemManager::DestroyParticleSystem(ParticleSystemHandle inHandle) { InternalDestroyParticleSystem(inHandle); } void AvHParticleSystemManager::Draw(const pVector& inView) { if(this->mStarted) { ParticleSystemList::iterator theIterator; for(theIterator = this->mParticleSystemList.begin(); theIterator != this->mParticleSystemList.end(); theIterator++) { theIterator->Draw(inView); } } } AvHParticleSystem* AvHParticleSystemManager::GetParticleSystem(ParticleSystemHandle inHandle) { AvHParticleSystem* theParticleSystem = NULL; for(ParticleSystemList::iterator theIterator = this->mParticleSystemList.begin(); theIterator != this->mParticleSystemList.end(); theIterator++) { if(theIterator->GetHandle() == inHandle) { theParticleSystem = &*theIterator; break; } } return theParticleSystem; } int AvHParticleSystemManager::GetNumberParticleSystems() { return (int)this->mParticleSystemList.size(); } #ifdef AVH_CLIENT int AvHParticleSystemManager::GetNumberVisibleParticleSystems() { int theNumVisibleSystems = 0; for(ParticleSystemList::iterator theIterator = this->mParticleSystemList.begin(); theIterator != this->mParticleSystemList.end(); theIterator++) { if(theIterator->GetIsVisible()) { theNumVisibleSystems++; } } return theNumVisibleSystems; } #endif float AvHParticleSystemManager::GetTime(void) const { float theTime = 0; #ifdef AVH_CLIENT theTime = gEngfuncs.GetClientTime(); #else theTime = gpGlobals->time; #endif return theTime; } bool AvHParticleSystemManager::InternalDestroyParticleSystem(ParticleSystemHandle inHandle) { bool theDestroyed = false; for(ParticleSystemList::iterator theIterator = this->mParticleSystemList.begin(); theIterator != this->mParticleSystemList.end(); theIterator++) { if(theIterator->GetHandle() == inHandle) { // Tell it to kill its particles and clean up theIterator->Kill(); // Remove it from the list theIterator = this->mParticleSystemList.erase(theIterator); // We're successful and done theDestroyed = true; break; } } return theDestroyed; } bool AvHParticleSystemManager::InternalCreateParticleSystem(const QueuedParticleSystem& inParticleSystem) { bool theSuccess = false; // add new particle system to list AvHParticleTemplate* theTemplate = ::gParticleTemplateList.GetTemplateAtIndex(inParticleSystem.mTemplateIndex); if(theTemplate) { AvHParticleSystem theNewParticleSystem(theTemplate, inParticleSystem.mTemplateIndex); // set it's template handle int theHandle = inParticleSystem.mHandle; if(theHandle) { theNewParticleSystem.SetHandle(theHandle); } else { // TODO: Check to be sure it has a lifetime, because it won't be able to be deleted } // Bind it to an entity if it was specified //if(inParticleSystem.mEntity) //{ // theNewParticleSystem.SetEntity(inParticleSystem.mEntity); //} theNewParticleSystem.SetPosition(inParticleSystem.mOrigin); // Set time created theNewParticleSystem.SetTimeCreated(this->GetTime()); // Set normal if specified if(inParticleSystem.mHasNormal) { theNewParticleSystem.SetNormal(inParticleSystem.mNormal); } this->mParticleSystemList.push_back(theNewParticleSystem); #ifdef AVH_CLIENT //int theGenerationEntityIndex; //if(theNewParticleSystem.GetGenerationEntity(theGenerationEntityIndex)) //{ // gCubeDebugEntities.push_back(theGenerationEntityIndex); //} //else //{ int theParentEntityIndex = inParticleSystem.GetParentEntityIndex(); if(theParentEntityIndex > 0) { gCubeDebugEntities.push_back(theParentEntityIndex); } //} #endif theSuccess = true; } return theSuccess; } void AvHParticleSystemManager::ProcessQueuedList() { // Process pending creates for(QueuedCreateList::iterator theCreateIter = this->mQueuedCreateList.begin(); theCreateIter != this->mQueuedCreateList.end(); /* no increment */) { // Run through the list, creating each queued particle system. If a system couldn't be created, we probably // don't have that particle template yet, so just skip it for now and try to create it next frame. // Add code that detects how long a queued particle system has been in? const float kTimeOut = 20.0f; float theCurrentTime = this->GetTime(); bool theCreateTimedOut = theCurrentTime - theCreateIter->mTimeQueued > kTimeOut; if(theCreateTimedOut) { //ASSERT(!theCreateTimedOut); theCreateIter = this->mQueuedCreateList.erase(theCreateIter); } else { bool theCreated = this->InternalCreateParticleSystem(*theCreateIter); if(theCreated) { theCreateIter = this->mQueuedCreateList.erase(theCreateIter); } else { theCreateIter++; } } } // Process pending destroys for(QueuedDestroyList::iterator theDestroyIter = this->mQueuedDestroyList.begin(); theDestroyIter != this->mQueuedDestroyList.end(); /* no increment */) { ParticleSystemHandle theHandle = *theDestroyIter; // Try to delete it. If it hasn't been created yet, keep the pending destruction around bool theDestroyed = this->InternalDestroyParticleSystem(theHandle); if(theDestroyed) { theDestroyIter = this->mQueuedDestroyList.erase(theDestroyIter); } else { theDestroyIter++; } } } void AvHParticleSystemManager::QueueParticleSystem(const QueuedParticleSystem& inParticleSystem) { this->mQueuedCreateList.push_back(inParticleSystem); } void AvHParticleSystemManager::ReloadFromTemplates() { this->mReloadFromTemplates = true; } void AvHParticleSystemManager::Reset() { // Clear out alive particle systems for(ParticleSystemList::iterator theIterator = this->mParticleSystemList.begin(); theIterator != this->mParticleSystemList.end(); theIterator++) { theIterator->Kill(); } this->mParticleSystemList.clear(); // Clear out queued particle systems this->mQueuedCreateList.clear(); this->mQueuedDestroyList.clear(); // Clear entities we're tracking this->mParticleEntities.clear(); // Init list again so it's just like we just built a new one this->Init(); } bool AvHParticleSystemManager::SetParticleSystemCustomData(uint16 inCustomData, ParticleSystemHandle inHandle) { bool theSuccess = false; for(ParticleSystemList::iterator theIterator = this->mParticleSystemList.begin(); theIterator != this->mParticleSystemList.end(); theIterator++) { if(theIterator->GetHandle() == inHandle) { theIterator->SetCustomData(inCustomData); theSuccess = true; break; } } return theSuccess; } void AvHParticleSystemManager::MarkParticleSystemForDeletion(int inIndex, ParticleSystemHandle inHandle) { for(ParticleEntityList::iterator theIter = this->mParticleEntities.begin(); theIter != this->mParticleEntities.end(); theIter++) { if(*theIter == inIndex) { // Remove it from the list theIter = this->mParticleEntities.erase(theIter); break; } } // Mark it for deletion for(ParticleSystemList::iterator theIterator = this->mParticleSystemList.begin(); theIterator != this->mParticleSystemList.end(); theIterator++) { if(theIterator->GetHandle() == inHandle) { theIterator->SetIsMarkedForDeletion(); break; } } } #ifdef AVH_CLIENT int AvHParticleSystemManager::GetNumVisibleParticleSystems() const { int theNumVisible = 0; for(ParticleSystemList::const_iterator theIterator = this->mParticleSystemList.begin(); theIterator != this->mParticleSystemList.end(); theIterator++) { if(theIterator->GetIsVisible()) { theNumVisible++; } } return theNumVisible; } void AvHParticleSystemManager::SetParticleSystemGenerationEntityExtents(vec3_t& inMin, vec3_t& inMax, ParticleSystemHandle inHandle) { for(ParticleSystemList::iterator theIterator = this->mParticleSystemList.begin(); theIterator != this->mParticleSystemList.end(); theIterator++) { if(theIterator->GetHandle() == inHandle) { theIterator->SetGenerationEntityExtents(inMin, inMax); break; } } } void AvHParticleSystemManager::SetParticleSystemPosition(vec3_t& inPos, ParticleSystemHandle inHandle) { for(ParticleSystemList::iterator theIterator = this->mParticleSystemList.begin(); theIterator != this->mParticleSystemList.end(); theIterator++) { if(theIterator->GetHandle() == inHandle) { theIterator->SetPosition(inPos); break; } } } void AvHParticleSystemManager::SetParticleSystemVisibility(bool inVisibility, ParticleSystemHandle inHandle) { for(ParticleSystemList::iterator theIterator = this->mParticleSystemList.begin(); theIterator != this->mParticleSystemList.end(); theIterator++) { if(theIterator->GetHandle() == inHandle) { theIterator->SetIsVisible(inVisibility, this->GetTime()); break; } } } #endif void AvHParticleSystemManager::Start() { if(!this->mStarted) { this->mStarted = true; this->mUpdatedTime = this->mTimePassed = 0.0f; } } void AvHParticleSystemManager::Update(double inTime) { if(this->mStarted) { this->ProcessQueuedList(); ASSERT(inTime >= 0.0f); this->mTimePassed += (float)inTime; if(this->mTimePassed - this->mUpdatedTime > kUpdateIncrement) { float theTimeToUpdate = this->mTimePassed - this->mUpdatedTime; // Run through list, updating each one ParticleSystemList::iterator theIterator; for(theIterator = this->mParticleSystemList.begin(); theIterator != this->mParticleSystemList.end(); /* notice: no increment */ ) { if(this->mReloadFromTemplates) { AvHParticleTemplate* theTemplate = gParticleTemplateList.GetTemplateAtIndex(theIterator->GetTemplateIndex()); ASSERT(theTemplate); theIterator->LoadFromTemplate(theTemplate); } // Always update particle systems theIterator->Update(theTimeToUpdate); // If the particle system has lived long enough, mark it for deletion float theCurrentTime = this->GetTime(); float theParticleSystemLifetime = theIterator->GetParticleSystemLifetime(); if(!theIterator->GetIsMarkedForDeletion()) { if((theParticleSystemLifetime != -1) && theIterator->GetHasGeneratedParticles()) { if(theCurrentTime > (theIterator->GetTimeCreated() + theParticleSystemLifetime)) { theIterator->SetIsMarkedForDeletion(); } } } #ifdef AVH_CLIENT // Expire visibility of particle systems. The server sends down particle entities as they're updated, but we need // a way to expire them. if(theIterator->GetIsVisible()) { const float kExpireTime = 6.0f; if((theIterator->GetLastTimeVisibilityLastSetTrue() == -1) || (this->mTimePassed > (theIterator->GetLastTimeVisibilityLastSetTrue() + kExpireTime))) { theIterator->SetIsVisible(false, inTime); } } #endif // If a particle system is marked for deletion and it has no more particles, delete it if(theIterator->GetIsMarkedForDeletion() && (theIterator->GetNumberOfParticles() == 0) ) { theIterator->Kill(); theIterator = this->mParticleSystemList.erase(theIterator); } else { theIterator++; } } this->mUpdatedTime = this->mTimePassed; } } this->mReloadFromTemplates = false; }