diff --git a/conanfile.py b/conanfile.py index 78dd04b9a0..1366023c98 100644 --- a/conanfile.py +++ b/conanfile.py @@ -25,6 +25,7 @@ def requirements(self): self.requires("rapidjson/cci.20211112", force=True) self.requires("socketw/3.11.0@anotherfoxguy/stable") + self.requires("jasper/4.2.4", override=True) self.requires("libpng/1.6.39", override=True) self.requires("libwebp/1.3.2", override=True) self.requires("zlib/1.2.13", override=True) diff --git a/source/main/ForwardDeclarations.h b/source/main/ForwardDeclarations.h index 723f0ec84e..0750a3c42c 100644 --- a/source/main/ForwardDeclarations.h +++ b/source/main/ForwardDeclarations.h @@ -69,9 +69,12 @@ namespace RoR typedef int FlareID_t; //!< Index into `Actor::ar_flares`, use `RoR::FLAREID_INVALID` as empty value static const FlareID_t FLAREID_INVALID = -1; - typedef int ExhaustID_t; //!< Index into `Actor::exhausts`, use `RoR::EXHAUSTID_INVALID` as empty value + typedef int ExhaustID_t; //!< Index into `GfxActor::m_exhausts`, use `RoR::EXHAUSTID_INVALID` as empty value static const ExhaustID_t EXHAUSTID_INVALID = -1; + typedef int CParticleID_t; //!< Index into `GfxActor::m_cparticles`, use `RoR::CPARTICLEID_INVALID` as empty value + static const CParticleID_t CPARTICLEID_INVALID = -1; + typedef int CommandkeyID_t; //!< Index into `Actor::ar_commandkeys` (BEWARE: indexed 1-MAX_COMMANDKEYS, 0 is invalid value, negative subscript of any size is acceptable, see `class CmdKeyArray` ). static const CommandkeyID_t COMMANDKEYID_INVALID = 0; @@ -187,8 +190,8 @@ namespace RoR struct rotator_t; struct flare_t; struct rope_t; - struct exhaust_t; - struct cparticle_t; + struct Exhaust; + struct CParticle; struct collision_box_t; struct tie_t; struct hook_t; diff --git a/source/main/audio/SoundScriptManager.cpp b/source/main/audio/SoundScriptManager.cpp index 6894caa870..7948ec6f0d 100644 --- a/source/main/audio/SoundScriptManager.cpp +++ b/source/main/audio/SoundScriptManager.cpp @@ -305,14 +305,14 @@ void SoundScriptManager::modulate(int actor_id, int mod, float value, int linkTy } } -void SoundScriptManager::update(float dt_sec) +void SoundScriptManager::update(float dt) { if (App::sim_state->getEnum() == SimState::RUNNING || App::sim_state->getEnum() == SimState::EDITOR_MODE) { Ogre::SceneNode* cam_node = App::GetCameraManager()->GetCameraNode(); static Vector3 lastCameraPosition; - Vector3 cameraSpeed = (cam_node->getPosition() - lastCameraPosition) / dt_sec; + Vector3 cameraSpeed = (cam_node->getPosition() - lastCameraPosition) / dt; lastCameraPosition = cam_node->getPosition(); Ogre::Vector3 upVector = App::GetCameraManager()->GetCameraNode()->getOrientation() * Ogre::Vector3::UNIT_Y; // Direction points down -Z by default (adapted from Ogre::Camera) diff --git a/source/main/audio/SoundScriptManager.h b/source/main/audio/SoundScriptManager.h index fe557fe5ab..1d849f16a3 100644 --- a/source/main/audio/SoundScriptManager.h +++ b/source/main/audio/SoundScriptManager.h @@ -331,7 +331,7 @@ class SoundScriptManager : public Ogre::ScriptLoader bool isDisabled() { return disabled; } - void update(float dt_sec); + void update(float dt); SoundManager* getSoundManager() { return sound_manager; } diff --git a/source/main/gfx/DustPool.cpp b/source/main/gfx/DustPool.cpp index 146e51c1d2..95520632c8 100644 --- a/source/main/gfx/DustPool.cpp +++ b/source/main/gfx/DustPool.cpp @@ -24,6 +24,7 @@ #include "Application.h" #include "GameContext.h" +#include "GfxScene.h" #include "Terrain.h" #include "Water.h" @@ -195,6 +196,7 @@ void DustPool::update() { for (int i = 0; i < allocated; i++) { + App::GetGfxScene()->AdjustParticleSystemTimeFactor(pss[i]); ParticleEmitter* emit = pss[i]->getEmitter(0); Vector3 ndir = velocities[i]; Real vel = ndir.length(); diff --git a/source/main/gfx/DustPool.h b/source/main/gfx/DustPool.h index 956e54889c..9ddc6d836b 100644 --- a/source/main/gfx/DustPool.h +++ b/source/main/gfx/DustPool.h @@ -80,7 +80,7 @@ class DustPool Ogre::SceneNode* sns[MAX_DUSTS]; Ogre::SceneNode* parent_snode; Ogre::Vector3 positions[MAX_DUSTS]; - Ogre::Vector3 velocities[MAX_DUSTS]; + Ogre::Vector3 velocities[MAX_DUSTS]; //!< Velocity in wall time, ignoring the time scale. float rates[MAX_DUSTS]; int allocated; int size; diff --git a/source/main/gfx/GfxActor.cpp b/source/main/gfx/GfxActor.cpp index 5b7a76d55c..bbf88ec676 100644 --- a/source/main/gfx/GfxActor.cpp +++ b/source/main/gfx/GfxActor.cpp @@ -283,6 +283,54 @@ RoR::GfxActor::~GfxActor() HandleGenericException(WhereFrom(this, "destroying renderdash"), HANDLEGENERICEXCEPTION_LOGFILE); } } + + // delete exhausts + for (std::vector::iterator it = m_exhausts.begin(); it != m_exhausts.end(); it++) + { + try + { + if (it->smokeNode) + { + it->smokeNode->removeAndDestroyAllChildren(); + App::GetGfxScene()->GetSceneManager()->destroySceneNode(it->smokeNode); + } + if (it->smoker) + { + it->smoker->removeAllAffectors(); + it->smoker->removeAllEmitters(); + App::GetGfxScene()->GetSceneManager()->destroyParticleSystem(it->smoker); + } + } + catch (...) + { + HandleGenericException(fmt::format("GfxActor::~GfxActor(); instanceID:{}, streamID:{}, filename:{}; deleting exhaust {}/{}.", + m_actor->ar_instance_id, m_actor->ar_net_stream_id, m_actor->ar_filename, std::distance(m_exhausts.begin(), it), m_exhausts.size()), HANDLEGENERICEXCEPTION_LOGFILE); + } + } + + // delete custom particles + for (int i = 0; i < (int)m_cparticles.size(); i++) + { + try + { + if (m_cparticles[i].snode) + { + m_cparticles[i].snode->removeAndDestroyAllChildren(); + App::GetGfxScene()->GetSceneManager()->destroySceneNode(m_cparticles[i].snode); + } + if (m_cparticles[i].psys) + { + m_cparticles[i].psys->removeAllAffectors(); + m_cparticles[i].psys->removeAllEmitters(); + App::GetGfxScene()->GetSceneManager()->destroyParticleSystem(m_cparticles[i].psys); + } + } + catch (...) + { + HandleGenericException(fmt::format("Actor::dispose(); instanceID:{}, streamID:{}, filename:{}; deleting custom particle {}/{}.", + m_actor->ar_instance_id, m_actor->ar_net_stream_id, m_actor->ar_filename, i, m_cparticles.size()), HANDLEGENERICEXCEPTION_LOGFILE); + } + } } ActorPtr RoR::GfxActor::GetActor() @@ -419,7 +467,7 @@ void RoR::GfxActor::SetVideoCamState(VideoCamState state) m_vidcam_state = state; } -void RoR::GfxActor::UpdateVideoCameras(float dt_sec) +void RoR::GfxActor::UpdateVideoCameras(float dt) { if (m_vidcam_state != VideoCamState::VCSTATE_ENABLED_ONLINE) return; @@ -539,7 +587,7 @@ void RoR::GfxActor::UpdateVideoCameras(float dt_sec) } } -void RoR::GfxActor::UpdateParticles(float dt_sec) +void RoR::GfxActor::UpdateParticles(float dt) { float water_height = 0.f; // Unused if terrain has no water if (App::GetGameContext()->GetTerrain()->getWater() != nullptr) @@ -563,7 +611,7 @@ void RoR::GfxActor::UpdateParticles(float dt_sec) // Dripping water? if (nfx.nx_wet_time_sec != -1) { - nfx.nx_wet_time_sec += dt_sec; + nfx.nx_wet_time_sec += dt; if (nfx.nx_wet_time_sec > 5.f) // Dries off in 5 sec { nfx.nx_wet_time_sec = -1.f; @@ -1844,6 +1892,7 @@ void RoR::GfxActor::UpdateSimDataBuffer() m_simbuf.simbuf_clutch = m_actor->ar_engine->GetClutch(); m_simbuf.simbuf_num_gears = m_actor->ar_engine->getNumGears(); m_simbuf.simbuf_engine_max_rpm = m_actor->ar_engine->getMaxRPM(); + m_simbuf.simbuf_engine_smoke = m_actor->ar_engine->GetSmoke(); } if (m_actor->m_num_wheel_diffs > 0) { @@ -1858,6 +1907,7 @@ void RoR::GfxActor::UpdateSimDataBuffer() m_simbuf.simbuf_lightmask = m_actor->m_lightmask; m_simbuf.simbuf_smoke_enabled = m_actor->getSmokeEnabled(); m_simbuf.simbuf_parking_brake = m_actor->ar_parking_brake; + m_simbuf.simbuf_cparticles_active = m_actor->ar_cparticles_active; // Aerial m_simbuf.simbuf_hydro_aileron_state = m_actor->ar_hydro_aileron_state; @@ -1986,19 +2036,45 @@ void RoR::GfxActor::UpdateAirbrakes() } } -// TODO: Also move the data structure + setup code to GfxActor ~ only_a_ptr, 05/2018 void RoR::GfxActor::UpdateCParticles() { - //update custom particle systems - for (int i = 0; i < m_actor->ar_num_custom_particles; i++) + for (CParticle& cparticle: m_cparticles) { - Ogre::Vector3 pos = m_simbuf.simbuf_nodes[m_actor->ar_custom_particles[i].emitterNode].AbsPosition; - Ogre::Vector3 dir = pos - m_simbuf.simbuf_nodes[m_actor->ar_custom_particles[i].directionNode].AbsPosition; - dir = fast_normalise(dir); - m_actor->ar_custom_particles[i].snode->setPosition(pos); - for (int j = 0; j < m_actor->ar_custom_particles[i].psys->getNumEmitters(); j++) + App::GetGfxScene()->AdjustParticleSystemTimeFactor(cparticle.psys); + const Ogre::Vector3 pos = m_simbuf.simbuf_nodes[cparticle.emitterNode].AbsPosition; + const Ogre::Vector3 dir = fast_normalise(pos - m_simbuf.simbuf_nodes[cparticle.directionNode].AbsPosition); + cparticle.snode->setPosition(pos); + + for (unsigned short j = 0; j < cparticle.psys->getNumEmitters(); j++) + { + cparticle.psys->getEmitter(j)->setEnabled(m_simbuf.simbuf_cparticles_active); + cparticle.psys->getEmitter(j)->setDirection(dir); + } + } +} + +void RoR::GfxActor::UpdateExhausts() +{ + if (!m_simbuf.simbuf_has_engine) + return; + + for (Exhaust& exhaust: m_exhausts) + { + if (!exhaust.smoker) // This remains `nullptr` if removed via `addonpart_unwanted_exhaust` or Tuning UI. + continue; + + App::GetGfxScene()->AdjustParticleSystemTimeFactor(exhaust.smoker); + const Ogre::Vector3 pos = m_simbuf.simbuf_nodes[exhaust.emitterNode].AbsPosition; + const Ogre::Vector3 dir = pos - m_simbuf.simbuf_nodes[exhaust.directionNode].AbsPosition; + exhaust.smokeNode->setPosition(pos); + + const bool active = m_simbuf.simbuf_smoke_enabled && m_simbuf.simbuf_engine_smoke != -1.f; + exhaust.smoker->getEmitter(0)->setEnabled(active); + if (active) // `setTimeToLive()` assert()s that argument is not negative. { - m_actor->ar_custom_particles[i].psys->getEmitter(j)->setDirection(dir); + exhaust.smoker->getEmitter(0)->setColour(Ogre::ColourValue(0.0, 0.0, 0.0, 0.02 + m_simbuf.simbuf_engine_smoke * 0.06)); + exhaust.smoker->getEmitter(0)->setTimeToLive((0.02 + m_simbuf.simbuf_engine_smoke * 0.06) / 0.04); + exhaust.smoker->getEmitter(0)->setParticleVelocity(1.0 + m_simbuf.simbuf_engine_smoke * 2.0, 2.0 + m_simbuf.simbuf_engine_smoke * 3.0); } } } @@ -3182,7 +3258,7 @@ bool ShouldEnableLightSource(FlareType type, bool is_player) } } -void RoR::GfxActor::UpdateFlares(float dt_sec, bool is_player) +void RoR::GfxActor::UpdateFlares(float dt, bool is_player) { // Flare states are determined in simulation, this function only applies them to OGRE objects // ------------------------------------------------------------------------------------------ diff --git a/source/main/gfx/GfxActor.h b/source/main/gfx/GfxActor.h index 3ea6a3c409..ac4c4a81b8 100644 --- a/source/main/gfx/GfxActor.h +++ b/source/main/gfx/GfxActor.h @@ -66,6 +66,13 @@ class GfxActor void RegisterCabMesh(Ogre::Entity* ent, Ogre::SceneNode* snode, FlexObj* flexobj); void SortFlexbodies(); + // Retrieving elements + + std::vector& GetFlexbodies() { return m_flexbodies; }; + std::vector& getProps() { return m_props; } + std::vector& getCParticles() { return m_cparticles; } + std::vector& getExhausts() { return m_exhausts; } + // Visual changes void SetMaterialFlareOn(int flare_index, bool state_on); @@ -95,8 +102,8 @@ class GfxActor // Visual updates - void UpdateVideoCameras(float dt_sec); - void UpdateParticles(float dt_sec); + void UpdateVideoCameras(float dt); + void UpdateParticles(float dt); void UpdateRods(); void UpdateWheelVisuals(); void UpdateFlexbodies(); @@ -108,9 +115,10 @@ class GfxActor void UpdatePropAnimations(float dt); void UpdateAirbrakes(); void UpdateCParticles(); + void UpdateExhausts(); void UpdateAeroEngines(); void UpdateNetLabels(float dt); - void UpdateFlares(float dt_sec, bool is_player); + void UpdateFlares(float dt, bool is_player); void UpdateRenderdashRTT (); // SimBuffers @@ -132,17 +140,15 @@ class GfxActor void CalculateDriverPos(Ogre::Vector3& out_pos, Ogre::Quaternion& out_rot); int GetActorId() const; int GetActorState() const; - std::vector& GetFlexbodies() { return m_flexbodies; }; - Ogre::MaterialPtr& GetCabTransMaterial() { return m_cab_mat_visual_trans; } VideoCamState GetVideoCamState() const { return m_vidcam_state; } DebugViewType GetDebugView() const { return m_debug_view; } Ogre::String GetResourceGroup() { return m_custom_resource_group; } ActorPtr GetActor(); // Watch out for multithreading with this! + Ogre::MaterialPtr& GetCabTransMaterial() { return m_cab_mat_visual_trans; } Ogre::TexturePtr GetHelpTex() { return m_help_tex; } Ogre::MaterialPtr GetHelpMat() { return m_help_mat; } bool HasDriverSeatProp() const { return m_driverseat_prop_index != -1; } void CalcPropAnimation(PropAnim& anim, float& cstate, int& div, float dt); - std::vector& getProps() { return m_props; } size_t getNumVideoCameras() const { return m_videocameras.size(); } SurveyMapEntity& getSurveyMapEntity() { return m_surveymap_entity; } WheelSide getWheelSide(WheelID_t wheel_id) { return (wheel_id >= 0 && (size_t)wheel_id < m_wheels.size()) ? m_wheels[wheel_id].wx_side : WheelSide::INVALID; } @@ -193,6 +199,8 @@ class GfxActor DustPool* m_particles_ripple = nullptr; DustPool* m_particles_sparks = nullptr; DustPool* m_particles_clump = nullptr; + std::vector m_cparticles; + std::vector m_exhausts; // Cab mesh ('submesh' in truck fileformat) FlexObj* m_cab_mesh = nullptr; diff --git a/source/main/gfx/GfxData.h b/source/main/gfx/GfxData.h index d548fa6d8d..ce779ef613 100644 --- a/source/main/gfx/GfxData.h +++ b/source/main/gfx/GfxData.h @@ -316,6 +316,23 @@ struct FlareMaterial // materialflares Ogre::ColourValue emissive_color; }; +struct Exhaust +{ + NodeNum_t emitterNode = NODENUM_INVALID; + NodeNum_t directionNode = NODENUM_INVALID; + Ogre::SceneNode *smokeNode = nullptr; + Ogre::ParticleSystem* smoker = nullptr; //!< This remains `nullptr` if removed via `addonpart_unwanted_exhaust` or Tuning UI. + std::string particleSystemName; //!< Name in .particle file ~ for display in Tuning UI. +}; + +struct CParticle +{ + NodeNum_t emitterNode = NODENUM_INVALID; + NodeNum_t directionNode = NODENUM_INVALID; + Ogre::SceneNode *snode = nullptr; + Ogre::ParticleSystem* psys = nullptr; +}; + /// @} // addtogroup Gfx } // namespace RoR diff --git a/source/main/gfx/GfxScene.cpp b/source/main/gfx/GfxScene.cpp index 9f514cbbb7..02f527dbb1 100644 --- a/source/main/gfx/GfxScene.cpp +++ b/source/main/gfx/GfxScene.cpp @@ -87,8 +87,11 @@ void GfxScene::Init() m_skidmark_conf.LoadDefaultSkidmarkDefs(); } -void GfxScene::UpdateScene(float dt_sec) +void GfxScene::UpdateScene(float dt) { + // NOTE: The `dt` parameter here is simulation time (0 when paused), not real time! + // ================================================================================ + // Actors - start threaded tasks for (GfxActor* gfx_actor: m_live_gfx_actors) { @@ -114,13 +117,14 @@ void GfxScene::UpdateScene(float dt_sec) // Particles if (App::gfx_particles_mode->getInt() == 1) { + // Generate particles as needed for (GfxActor* gfx_actor: m_all_gfx_actors) { - if (!m_simbuf.simbuf_sim_paused && !gfx_actor->GetSimDataBuffer().simbuf_physics_paused) - { - gfx_actor->UpdateParticles(m_simbuf.simbuf_sim_speed * dt_sec); - } + float dt_actor = (!gfx_actor->GetSimDataBuffer().simbuf_physics_paused) ? dt : 0.f; + gfx_actor->UpdateParticles(dt_actor); } + + // Update particle movement for (auto itor : m_dustpools) { itor.second->update(); @@ -136,7 +140,7 @@ void GfxScene::UpdateScene(float dt_sec) } // Terrain - animated meshes and paged geometry - App::GetGameContext()->GetTerrain()->getObjectManager()->UpdateTerrainObjects(dt_sec); + App::GetGameContext()->GetTerrain()->getObjectManager()->UpdateTerrainObjects(dt); // Terrain - lightmap; TODO: ported as-is from Terrain::update(), is it needed? ~ only_a_ptr, 05/2018 App::GetGameContext()->GetTerrain()->getGeometryManager()->UpdateMainLightPosition(); // TODO: Is this necessary? I'm leaving it here just in case ~ only_a_ptr, 04/2017 @@ -153,7 +157,7 @@ void GfxScene::UpdateScene(float dt_sec) { water->SetReflectionPlaneHeight(water->GetStaticWaterHeight()); } - water->FrameStepWater(dt_sec); + water->FrameStepWater(dt); } // Terrain - sky @@ -168,7 +172,7 @@ void GfxScene::UpdateScene(float dt_sec) SkyXManager* skyx_man = App::GetGameContext()->GetTerrain()->getSkyXManager(); if (skyx_man != nullptr) { - skyx_man->update(dt_sec); // Light update + skyx_man->update(dt); // Light update } // GUI - race @@ -197,7 +201,7 @@ void GfxScene::UpdateScene(float dt_sec) // HUD - network labels (always update) for (GfxActor* gfx_actor: m_all_gfx_actors) { - gfx_actor->UpdateNetLabels(m_simbuf.simbuf_sim_speed * dt_sec); + gfx_actor->UpdateNetLabels(dt); } // Player avatars @@ -209,6 +213,7 @@ void GfxScene::UpdateScene(float dt_sec) // Actors - update misc visuals for (GfxActor* gfx_actor: m_all_gfx_actors) { + float dt_actor = (!gfx_actor->GetSimDataBuffer().simbuf_physics_paused) ? dt : 0.f; if (gfx_actor->IsActorLive()) { gfx_actor->UpdateRods(); @@ -216,18 +221,20 @@ void GfxScene::UpdateScene(float dt_sec) gfx_actor->UpdateWingMeshes(); gfx_actor->UpdateAirbrakes(); gfx_actor->UpdateCParticles(); + gfx_actor->UpdateExhausts(); gfx_actor->UpdateAeroEngines(); - gfx_actor->UpdatePropAnimations(dt_sec); + gfx_actor->UpdatePropAnimations(dt_actor); gfx_actor->UpdateRenderdashRTT(); } // Beacon flares must always be updated - gfx_actor->UpdateProps(dt_sec, (gfx_actor == player_gfx_actor)); + gfx_actor->UpdateProps(dt_actor, (gfx_actor == player_gfx_actor)); // Blinkers (turn signals) must always be updated - gfx_actor->UpdateFlares(dt_sec, (gfx_actor == player_gfx_actor)); + gfx_actor->UpdateFlares(dt_actor, (gfx_actor == player_gfx_actor)); } if (player_gfx_actor != nullptr) { - player_gfx_actor->UpdateVideoCameras(dt_sec); + float dt_actor = (!player_gfx_actor->GetSimDataBuffer().simbuf_physics_paused) ? dt : 0.f; + player_gfx_actor->UpdateVideoCameras(dt_actor); // The old-style render-to-texture dashboard (based on OGRE overlays) if (m_simbuf.simbuf_player_actor->ar_driveable == TRUCK && m_simbuf.simbuf_player_actor->ar_engine != nullptr) @@ -397,3 +404,13 @@ void GfxScene::DrawNetLabel(Ogre::Vector3 scene_pos, float cam_dist, std::string #endif // USE_SOCKETW } +void GfxScene::AdjustParticleSystemTimeFactor(Ogre::ParticleSystem* psys) +{ + float speed_factor = 0.f; + if (App::sim_state->getEnum() == SimState::RUNNING && !App::GetGameContext()->GetActorManager()->IsSimulationPaused()) + { + speed_factor = m_simbuf.simbuf_sim_speed; + } + + psys->setSpeedFactor(speed_factor); +} \ No newline at end of file diff --git a/source/main/gfx/GfxScene.h b/source/main/gfx/GfxScene.h index fced6a3431..9912285442 100644 --- a/source/main/gfx/GfxScene.h +++ b/source/main/gfx/GfxScene.h @@ -47,11 +47,15 @@ class GfxScene public: void Init(); + + // Particles: void CreateDustPools(); DustPool* GetDustPool(const char* name); + void AdjustParticleSystemTimeFactor(Ogre::ParticleSystem* psys); + void SetParticlesVisible(bool visible); void DrawNetLabel(Ogre::Vector3 pos, float cam_dist, std::string const& nick, int colornum); - void UpdateScene(float dt_sec); + void UpdateScene(float dt); void ClearScene(); void RegisterGfxActor(RoR::GfxActor* gfx_actor); void RemoveGfxActor(RoR::GfxActor* gfx_actor); diff --git a/source/main/gfx/SimBuffers.h b/source/main/gfx/SimBuffers.h index 45bb7b9f1b..ecbf64620c 100644 --- a/source/main/gfx/SimBuffers.h +++ b/source/main/gfx/SimBuffers.h @@ -155,6 +155,7 @@ struct ActorSB float simbuf_clutch = 0; int simbuf_num_gears = 0; //!< Gearbox float simbuf_engine_max_rpm = 0; + float simbuf_engine_smoke = 0; // Tyre pressure float simbuf_tyre_pressure = 0; @@ -164,6 +165,7 @@ struct ActorSB BitMask_t simbuf_lightmask = 0; bool simbuf_smoke_enabled = false; bool simbuf_parking_brake = false; + bool simbuf_cparticles_active = false; // Aerial float simbuf_hydro_aileron_state = 0; diff --git a/source/main/gfx/hydrax/Hydrax.cpp b/source/main/gfx/hydrax/Hydrax.cpp index 3374793439..cc2b0a09f7 100644 --- a/source/main/gfx/hydrax/Hydrax.cpp +++ b/source/main/gfx/hydrax/Hydrax.cpp @@ -292,6 +292,7 @@ namespace Hydrax { if (mCreated && mModule && mVisible) { + mMaterialManager->updateAnimatedTextures(timeSinceLastFrame); mModule->update(timeSinceLastFrame); mDecalsManager->update(); _checkUnderwater(timeSinceLastFrame); diff --git a/source/main/gfx/hydrax/MaterialManager.cpp b/source/main/gfx/hydrax/MaterialManager.cpp index 5998cd9c7d..2135c22bd1 100644 --- a/source/main/gfx/hydrax/MaterialManager.cpp +++ b/source/main/gfx/hydrax/MaterialManager.cpp @@ -1528,9 +1528,11 @@ namespace Hydrax { FP_Parameters->setNamedConstant("uCaustics", 0); } - Ogre::TextureUnitState *TUS_Caustics = DM_Technique0_Pass0->createTextureUnitState("Caustics.bmp"); - TUS_Caustics->setTextureAddressingMode(Ogre::TextureUnitState::TAM_WRAP); - TUS_Caustics->setAnimatedTextureName("Caustics.bmp", 32, 1.5); + Ogre::TextureUnitState* TUS_Caustics = DM_Technique0_Pass0->createTextureUnitState("Caustics.bmp"); + TUS_Caustics->setTextureAddressingMode(Ogre::TextureUnitState::TAM_WRAP); + // To account for variable sim. time, we must update animation manually. + TUS_Caustics->setAnimatedTextureName("Caustics.bmp", 32); + mCausticsAnimTexVec.push_back(TUS_Caustics); } DepthMaterial->setReceiveShadows(false); @@ -1539,6 +1541,31 @@ namespace Hydrax return true; } + const float CAUSTICS_FRAME_DURATION = 1.5f / 32.f; // 1.5sec is the original hardcoded total duration. + const unsigned int CAUSTICS_NUM_FRAMES = 32; + + void MaterialManager::updateAnimatedTextures(float dt) + { + mCausticsAnimCurrentFrameElapsedTime += dt; + while (mCausticsAnimCurrentFrameElapsedTime > CAUSTICS_FRAME_DURATION) + { + // advance anim frame + mCausticsAnimCurrentFrame++; + if (mCausticsAnimCurrentFrame >= CAUSTICS_NUM_FRAMES) + { + mCausticsAnimCurrentFrame = 0; + } + // update time + mCausticsAnimCurrentFrameElapsedTime -= CAUSTICS_FRAME_DURATION; + } + + // Update frame on registered anims + for (Ogre::TextureUnitState* tus : mCausticsAnimTexVec) + { + tus->setCurrentFrame(mCausticsAnimCurrentFrame); + } + } + bool MaterialManager::_createDepthTextureGPUPrograms(const HydraxComponent &Components, const Options &Options, const Ogre::String& AlphaChannel) { const bool cCaustics = _isComponent(Components, HYDRAX_COMPONENT_CAUSTICS); @@ -3409,10 +3436,12 @@ namespace Hydrax { FP_Parameters->setNamedConstant("uCaustics", 0); } - Ogre::TextureUnitState *TUS_Caustics = DM_Technique_Pass0->createTextureUnitState("Caustics.bmp"); - TUS_Caustics->setName("Caustics"); - TUS_Caustics->setTextureAddressingMode(Ogre::TextureUnitState::TAM_WRAP); - TUS_Caustics->setAnimatedTextureName("Caustics.bmp", 32, 1.5); + Ogre::TextureUnitState* TUS_Caustics = DM_Technique_Pass0->createTextureUnitState("Caustics.bmp"); + TUS_Caustics->setName("Caustics"); + TUS_Caustics->setTextureAddressingMode(Ogre::TextureUnitState::TAM_WRAP); + // To account for variable sim. time, we must update animation manually. + TUS_Caustics->setAnimatedTextureName("Caustics.bmp", 32); + mCausticsAnimTexVec.push_back(TUS_Caustics); } if (AutoUpdate) @@ -3472,7 +3501,9 @@ namespace Hydrax Ogre::TextureUnitState *TUS_Caustics = DM_Technique_Pass0->createTextureUnitState("Caustics.bmp"); TUS_Caustics->setName("Caustics"); TUS_Caustics->setTextureAddressingMode(Ogre::TextureUnitState::TAM_WRAP); - TUS_Caustics->setAnimatedTextureName("Caustics.bmp", 32, 1.5); + // To account for variable sim. time, we must update animation manually. + TUS_Caustics->setAnimatedTextureName("Caustics.bmp", 32); + mCausticsAnimTexVec.push_back(TUS_Caustics); } if(mOptions.SM == SM_GLSL) diff --git a/source/main/gfx/hydrax/MaterialManager.h b/source/main/gfx/hydrax/MaterialManager.h index 1c272ecbdf..d747a469ef 100644 --- a/source/main/gfx/hydrax/MaterialManager.h +++ b/source/main/gfx/hydrax/MaterialManager.h @@ -311,6 +311,10 @@ namespace Hydrax */ void setGpuProgramParameter(const GpuProgram &GpuP, const MaterialType &MType, const Ogre::String &Name, const Ogre::Vector3 &Value); + /** Animated textures must be updated manually to account for variable simulation time. + */ + void updateAnimatedTextures(float dt); + private: /** Is component in the given list? @param List Components list @@ -373,6 +377,11 @@ namespace Hydrax UnderwaterCompositorListener mUnderwaterCompositorListener; /// Hydrax main pointer Hydrax *mHydrax; + /// Caustics animated texture, for manual updating. + std::vector mCausticsAnimTexVec; + unsigned int mCausticsAnimCurrentFrame = 0; + /// Time spent on current animation frame, cumulative. + float mCausticsAnimCurrentFrameElapsedTime = 0.f; }; }; diff --git a/source/main/gui/panels/GUI_SurveyMap.h b/source/main/gui/panels/GUI_SurveyMap.h index 63d8a8fadd..8ecb44ae5b 100644 --- a/source/main/gui/panels/GUI_SurveyMap.h +++ b/source/main/gui/panels/GUI_SurveyMap.h @@ -69,7 +69,7 @@ class SurveyMap }; void setMapZoom(float zoom); - void setMapZoomRelative(float dt_sec); + void setMapZoomRelative(float dt); const char* getTypeByDriveable(const ActorPtr& actor); const char* getAIType(const ActorPtr& actor); diff --git a/source/main/gui/panels/GUI_TopMenubar.cpp b/source/main/gui/panels/GUI_TopMenubar.cpp index 5548b1f1b0..100065a429 100644 --- a/source/main/gui/panels/GUI_TopMenubar.cpp +++ b/source/main/gui/panels/GUI_TopMenubar.cpp @@ -1858,12 +1858,12 @@ void TopMenubar::Draw(float dt) } // Draw exhausts - size_t total_exhausts = tuning_actor->exhausts.size(); + size_t total_exhausts = tuning_actor->GetGfxActor()->getExhausts().size(); std::string exhausts_title = fmt::format(_LC("Tuning", "Exhausts ({})"), total_exhausts); if (ImGui::CollapsingHeader(exhausts_title.c_str())) { // Draw all exhausts (those removed by addonparts are also present as placeholders) - for (ExhaustID_t exhaustid = 0; exhaustid < (int)tuning_actor->exhausts.size(); exhaustid++) + for (ExhaustID_t exhaustid = 0; exhaustid < (int)total_exhausts; exhaustid++) { ImGui::PushID(exhaustid); ImGui::AlignTextToFramePadding(); @@ -1872,7 +1872,7 @@ void TopMenubar::Draw(float dt) this->DrawTuningForceRemoveControls( exhaustid, - tuning_actor->exhausts[exhaustid].particleSystemName, + tuning_actor->GetGfxActor()->getExhausts()[exhaustid].particleSystemName, tuneup_def && tuneup_def->isExhaustUnwanted(exhaustid), tuneup_def && tuneup_def->isExhaustForceRemoved(exhaustid), ModifyProjectRequestType::TUNEUP_FORCEREMOVE_EXHAUST_SET, diff --git a/source/main/gui/panels/GUI_VehicleInfoTPanel.cpp b/source/main/gui/panels/GUI_VehicleInfoTPanel.cpp index 522f3d528d..3b8e7c9e33 100644 --- a/source/main/gui/panels/GUI_VehicleInfoTPanel.cpp +++ b/source/main/gui/panels/GUI_VehicleInfoTPanel.cpp @@ -770,7 +770,7 @@ void VehicleInfoTPanel::DrawVehicleBasicsUI(RoR::GfxActor* actorx) } } - const int num_cparticles = actorx->GetActor()->ar_num_custom_particles; + const int num_cparticles = (int)actorx->getCParticles().size(); const size_t num_videocams = actorx->getNumVideoCameras(); ImGui::TextDisabled("View:"); if (num_cparticles) diff --git a/source/main/main.cpp b/source/main/main.cpp index a23121ce0b..b531a1d493 100644 --- a/source/main/main.cpp +++ b/source/main/main.cpp @@ -1754,6 +1754,13 @@ int main(int argc, char *argv[]) App::GetGfxScene()->BufferSimulationData(); } + // Calculate elapsed simulation time (taking simulation speed and pause into account) + float dt_sim = 0.f; + if (App::sim_state->getEnum() == SimState::RUNNING && !App::GetGameContext()->GetActorManager()->IsSimulationPaused()) + { + dt_sim = dt * App::GetGameContext()->GetActorManager()->GetSimulationSpeed(); + } + // Advance simulation if (App::sim_state->getEnum() == SimState::RUNNING) { @@ -1767,7 +1774,7 @@ int main(int argc, char *argv[]) } else if (App::app_state->getEnum() == AppState::SIMULATION) { - App::GetGfxScene()->UpdateScene(dt); // Draws GUI as well + App::GetGfxScene()->UpdateScene(dt_sim); // Draws GUI as well } // Render! diff --git a/source/main/physics/Actor.cpp b/source/main/physics/Actor.cpp index 01e54eb29b..b3cf3635a5 100644 --- a/source/main/physics/Actor.cpp +++ b/source/main/physics/Actor.cpp @@ -265,54 +265,6 @@ void Actor::dispose() } this->ar_flares.clear(); - // delete exhausts - for (std::vector::iterator it = exhausts.begin(); it != exhausts.end(); it++) - { - try - { - if (it->smokeNode) - { - it->smokeNode->removeAndDestroyAllChildren(); - App::GetGfxScene()->GetSceneManager()->destroySceneNode(it->smokeNode); - } - if (it->smoker) - { - it->smoker->removeAllAffectors(); - it->smoker->removeAllEmitters(); - App::GetGfxScene()->GetSceneManager()->destroyParticleSystem(it->smoker); - } - } - catch (...) - { - HandleGenericException(fmt::format("Actor::dispose(); instanceID:{}, streamID:{}, filename:{}; deleting exhaust {}/{}.", - ar_instance_id, ar_net_stream_id, ar_filename, std::distance(exhausts.begin(), it), exhausts.size()), HANDLEGENERICEXCEPTION_LOGFILE); - } - } - - // delete custom particles - for (int i = 0; i < ar_num_custom_particles; i++) - { - try - { - if (ar_custom_particles[i].snode) - { - ar_custom_particles[i].snode->removeAndDestroyAllChildren(); - App::GetGfxScene()->GetSceneManager()->destroySceneNode(ar_custom_particles[i].snode); - } - if (ar_custom_particles[i].psys) - { - ar_custom_particles[i].psys->removeAllAffectors(); - ar_custom_particles[i].psys->removeAllEmitters(); - App::GetGfxScene()->GetSceneManager()->destroyParticleSystem(ar_custom_particles[i].psys); - } - } - catch (...) - { - HandleGenericException(fmt::format("Actor::dispose(); instanceID:{}, streamID:{}, filename:{}; deleting custom particle {}/{}.", - ar_instance_id, ar_net_stream_id, ar_filename, i, ar_num_custom_particles), HANDLEGENERICEXCEPTION_LOGFILE); - } - } - // delete Rails for (std::vector::iterator it = m_railgroups.begin(); it != m_railgroups.end(); it++) { @@ -685,7 +637,7 @@ void Actor::calcNetwork() } // set particle cannon - if (((flagmask & NETMASK_PARTICLE) != 0) != m_custom_particles_enabled) + if (((flagmask & NETMASK_PARTICLE) != 0) != ar_cparticles_active) toggleCustomParticles(); m_antilockbrake = flagmask & NETMASK_ALB_ACTIVE; @@ -3191,15 +3143,7 @@ void Actor::toggleCustomParticles() if (ar_state == ActorState::DISPOSED) return; - m_custom_particles_enabled = !m_custom_particles_enabled; - for (int i = 0; i < ar_num_custom_particles; i++) - { - ar_custom_particles[i].active = !ar_custom_particles[i].active; - for (int j = 0; j < ar_custom_particles[i].psys->getNumEmitters(); j++) - { - ar_custom_particles[i].psys->getEmitter(j)->setEnabled(ar_custom_particles[i].active); - } - } + ar_cparticles_active = !ar_cparticles_active; //ScriptEvent - Particle Toggle TRIGGER_EVENT_ASYNC(SE_TRUCK_CPARTICLES_TOGGLE, ar_instance_id); @@ -3242,34 +3186,6 @@ void Actor::updateVisual(float dt) } #endif //openAL - // update exhausts - // TODO: Move to GfxActor, don't forget dt*m_simulation_speed - if (ar_engine && exhausts.size() > 0) - { - std::vector::iterator it; - for (it = exhausts.begin(); it != exhausts.end(); it++) - { - if (!it->smoker) - continue; - Vector3 dir = ar_nodes[it->emitterNode].AbsPosition - ar_nodes[it->directionNode].AbsPosition; - // dir.normalise(); - ParticleEmitter* emit = it->smoker->getEmitter(0); - it->smokeNode->setPosition(ar_nodes[it->emitterNode].AbsPosition); - emit->setDirection(dir); - if (!m_disable_smoke && ar_engine->GetSmoke() != -1.0) - { - emit->setEnabled(true); - emit->setColour(ColourValue(0.0, 0.0, 0.0, 0.02 + ar_engine->GetSmoke() * 0.06)); - emit->setTimeToLive((0.02 + ar_engine->GetSmoke() * 0.06) / 0.04); - } - else - { - emit->setEnabled(false); - } - emit->setParticleVelocity(1.0 + ar_engine->GetSmoke() * 2.0, 2.0 + ar_engine->GetSmoke() * 3.0); - } - } - // Wings (only physics, graphics are updated in GfxActor) float autoaileron = 0; float autorudder = 0; @@ -4456,7 +4372,7 @@ Actor::Actor( , m_water_contact(false) , m_water_contact_old(false) , m_has_command_beams(false) - , m_custom_particles_enabled(false) + , ar_cparticles_active(false) , m_beam_break_debug_enabled(false) , m_beam_deform_debug_enabled(false) , m_trigger_debug_enabled(false) @@ -4565,7 +4481,7 @@ BlinkType Actor::getBlinkType() bool Actor::getCustomParticleMode() { - return m_custom_particles_enabled; + return ar_cparticles_active; } Ogre::Real Actor::getMinimalCameraRadius() diff --git a/source/main/physics/Actor.h b/source/main/physics/Actor.h index f1e811eeef..e0d7195cd7 100644 --- a/source/main/physics/Actor.h +++ b/source/main/physics/Actor.h @@ -296,7 +296,6 @@ class Actor : public RefCountingObject wing_t* ar_wings = nullptr; int ar_num_wings = 0; std::vector authors; - std::vector exhausts; std::vector ar_ropes; std::vector ar_ropables; std::vector ar_ties; @@ -323,8 +322,6 @@ class Actor : public RefCountingObject int ar_num_contacters = 0; //!< Total number of nodes which can selfcontact cabs wheel_t ar_wheels[MAX_WHEELS] = {}; int ar_num_wheels = 0; - cparticle_t ar_custom_particles[MAX_CPARTICLES] = {}; - int ar_num_custom_particles = 0; soundsource_t ar_soundsources[MAX_SOUNDSCRIPTS_PER_TRUCK] = {}; int ar_num_soundsources = 0; AeroEngine* ar_aeroengines[MAX_AEROENGINES] = {}; @@ -483,6 +480,7 @@ class Actor : public RefCountingObject bool ar_toggle_ropes:1; //!< Sim state bool ar_toggle_ties:1; //!< Sim state bool ar_physics_paused:1; //!< Sim state + bool ar_cparticles_active:1;//!< Gfx state private: @@ -627,7 +625,6 @@ class Actor : public RefCountingObject bool m_water_contact:1; //!< Scripting state bool m_water_contact_old:1; //!< Scripting state bool m_has_command_beams:1; //!< Physics attr; - bool m_custom_particles_enabled:1; //!< Gfx state bool m_preloaded_with_terrain:1; //!< Spawn context (TODO: remove!) bool m_beam_break_debug_enabled:1; //!< Logging state bool m_beam_deform_debug_enabled:1; //!< Logging state diff --git a/source/main/physics/ActorSpawner.cpp b/source/main/physics/ActorSpawner.cpp index 46b410b730..30d351ab85 100644 --- a/source/main/physics/ActorSpawner.cpp +++ b/source/main/physics/ActorSpawner.cpp @@ -278,9 +278,6 @@ void ActorSpawner::InitializeRig() m_actor->ar_minimass.resize(req.num_nodes); - m_actor->exhausts.clear(); - memset(m_actor->ar_custom_particles, 0, sizeof(cparticle_t) * MAX_CPARTICLES); - m_actor->ar_num_custom_particles = 0; memset(m_actor->ar_soundsources, 0, sizeof(soundsource_t) * MAX_SOUNDSCRIPTS_PER_TRUCK); m_actor->ar_num_soundsources = 0; memset(m_actor->ar_collcabs, 0, sizeof(int) * MAX_CABS); @@ -1282,8 +1279,8 @@ void ActorSpawner::ProcessExhaust(RigDef::Exhaust & def) return; } - const ExhaustID_t exhaust_id = (ExhaustID_t)m_actor->exhausts.size(); - exhaust_t exhaust; + const ExhaustID_t exhaust_id = (ExhaustID_t)m_actor->m_gfx_actor->m_exhausts.size(); + Exhaust exhaust; exhaust.emitterNode = this->GetNodeIndexOrThrow(def.reference_node); exhaust.directionNode = this->GetNodeIndexOrThrow(def.direction_node); @@ -1296,7 +1293,7 @@ void ActorSpawner::ProcessExhaust(RigDef::Exhaust & def) if (!TuneupUtil::isExhaustAnyhowRemoved(m_actor->getWorkingTuneupDef(), exhaust_id)) { - std::string name = this->ComposeName(template_name.c_str(), (int)m_actor->exhausts.size()); + std::string name = this->ComposeName(template_name.c_str(), (int)m_actor->m_gfx_actor->m_exhausts.size()); exhaust.smoker = this->CreateParticleSystem(name, template_name); if (exhaust.smoker == nullptr) { @@ -1306,7 +1303,7 @@ void ActorSpawner::ProcessExhaust(RigDef::Exhaust & def) return; } - exhaust.smokeNode = m_particles_parent_scenenode->createChildSceneNode(this->ComposeName("exhaust", (int)m_actor->exhausts.size())); + exhaust.smokeNode = m_particles_parent_scenenode->createChildSceneNode(this->ComposeName("exhaust", (int)m_actor->m_gfx_actor->m_exhausts.size())); exhaust.smokeNode->attachObject(exhaust.smoker); exhaust.smokeNode->setPosition(m_actor->ar_nodes[exhaust.emitterNode].AbsPosition); @@ -1314,7 +1311,7 @@ void ActorSpawner::ProcessExhaust(RigDef::Exhaust & def) m_actor->m_gfx_actor->SetNodeHot(exhaust.directionNode, true); } - m_actor->exhausts.push_back(exhaust); + m_actor->m_gfx_actor->m_exhausts.push_back(exhaust); } std::string ActorSpawner::GetSubmeshGroundmodelName() @@ -2896,13 +2893,13 @@ void ActorSpawner::ProcessParticle(RigDef::Particle & def) return; } - int particle_index = m_actor->ar_num_custom_particles; - cparticle_t & particle = m_actor->ar_custom_particles[particle_index]; + CParticleID_t particle_id = static_cast(m_actor->m_gfx_actor->m_cparticles.size()); + CParticle particle; particle.emitterNode = GetNodeIndexOrThrow(def.emitter_node); particle.directionNode = GetNodeIndexOrThrow(def.reference_node); - std::string name = this->ComposeName(def.particle_system_name.c_str(), particle_index); + std::string name = this->ComposeName(def.particle_system_name.c_str(), particle_id); particle.psys = this->CreateParticleSystem(name, def.particle_system_name); if (particle.psys == nullptr) { @@ -2912,18 +2909,17 @@ void ActorSpawner::ProcessParticle(RigDef::Particle & def) return; } - particle.snode = m_particles_parent_scenenode->createChildSceneNode(this->ComposeName("cparticles", m_actor->ar_num_custom_particles)); + particle.snode = m_particles_parent_scenenode->createChildSceneNode(this->ComposeName("cparticles", particle_id)); particle.snode->attachObject(particle.psys); particle.snode->setPosition(m_actor->ar_nodes[particle.emitterNode].AbsPosition); /* Shut down the emitters */ - particle.active = false; for (unsigned int i = 0; i < particle.psys->getNumEmitters(); i++) { particle.psys->getEmitter(i)->setEnabled(false); } - ++m_actor->ar_num_custom_particles; + m_actor->m_gfx_actor->m_cparticles.push_back(particle); } void ActorSpawner::ProcessRopable(RigDef::Ropable & def) @@ -6055,12 +6051,13 @@ void ActorSpawner::AddExhaust( NodeNum_t direction_node_idx ) { - exhaust_t exhaust; + const ExhaustID_t exhaust_id = (ExhaustID_t)m_actor->m_gfx_actor->m_exhausts.size(); + Exhaust exhaust; exhaust.emitterNode = emitter_node_idx; exhaust.directionNode = direction_node_idx; exhaust.smoker = App::GetGfxScene()->GetSceneManager()->createParticleSystem( - this->ComposeName("exhaust", (int)m_actor->exhausts.size()), + this->ComposeName("exhaust", exhaust_id), /*quota=*/500, // Default value m_custom_resource_group); @@ -6074,14 +6071,14 @@ void ActorSpawner::AddExhaust( Ogre::MaterialPtr mat = this->FindOrCreateCustomizedMaterial("tracks/Smoke", m_custom_resource_group); exhaust.smoker->setMaterialName(mat->getName(), mat->getGroup()); - exhaust.smokeNode = m_particles_parent_scenenode->createChildSceneNode(this->ComposeName("exhaust", (int)m_actor->exhausts.size())); + exhaust.smokeNode = m_particles_parent_scenenode->createChildSceneNode(this->ComposeName("exhaust", exhaust_id)); exhaust.smokeNode->attachObject(exhaust.smoker); exhaust.smokeNode->setPosition(m_actor->ar_nodes[exhaust.emitterNode].AbsPosition); m_actor->m_gfx_actor->SetNodeHot(exhaust.emitterNode, true); m_actor->m_gfx_actor->SetNodeHot(exhaust.directionNode, true); - m_actor->exhausts.push_back(exhaust); + m_actor->m_gfx_actor->m_exhausts.push_back(exhaust); } void ActorSpawner::ProcessCinecam(RigDef::Cinecam & def) @@ -6161,18 +6158,6 @@ void ActorSpawner::ProcessGlobals(RigDef::Globals & def) // Limits. /* -------------------------------------------------------------------------- */ -bool ActorSpawner::CheckParticleLimit(unsigned int count) -{ - if ((m_actor->ar_num_custom_particles + count) > MAX_CPARTICLES) - { - std::stringstream msg; - msg << "Particle limit (" << MAX_CPARTICLES << ") exceeded"; - AddMessage(Message::TYPE_ERROR, msg.str()); - return false; - } - return true; -} - bool ActorSpawner::CheckAxleLimit(unsigned int count) { if ((m_actor->m_num_wheel_diffs + count) > MAX_WHEELS/2) diff --git a/source/main/physics/ActorSpawner.h b/source/main/physics/ActorSpawner.h index ecd9ecd2d9..1270bb66f7 100644 --- a/source/main/physics/ActorSpawner.h +++ b/source/main/physics/ActorSpawner.h @@ -362,7 +362,6 @@ class ActorSpawner /// @name Limit checks /// @{ - bool CheckParticleLimit(unsigned int count); bool CheckAxleLimit(unsigned int count); bool CheckSubmeshLimit(unsigned int count); bool CheckTexcoordLimit(unsigned int count); diff --git a/source/main/physics/Savegame.cpp b/source/main/physics/Savegame.cpp index 7baf958379..6a172d54e2 100644 --- a/source/main/physics/Savegame.cpp +++ b/source/main/physics/Savegame.cpp @@ -545,7 +545,7 @@ bool ActorManager::SaveScene(Ogre::String filename) j_entry.AddMember("wheel_speed", actor->ar_wheel_speed, j_doc.GetAllocator()); j_entry.AddMember("wheel_spin", actor->ar_wheel_spin, j_doc.GetAllocator()); - j_entry.AddMember("custom_particles", actor->m_custom_particles_enabled, j_doc.GetAllocator()); + j_entry.AddMember("custom_particles", actor->ar_cparticles_active, j_doc.GetAllocator()); // Flares j_entry.AddMember("lights", (int)actor->getHeadlightsVisible(), j_doc.GetAllocator()); @@ -831,7 +831,7 @@ void ActorManager::RestoreSavedState(ActorPtr actor, rapidjson::Value const& j_e actor->ar_wheel_speed = j_entry["wheel_speed"].GetFloat(); actor->ar_wheel_spin = j_entry["wheel_spin"].GetFloat(); - if (actor->m_custom_particles_enabled != j_entry["custom_particles"].GetBool()) + if (actor->ar_cparticles_active != j_entry["custom_particles"].GetBool()) { actor->toggleCustomParticles(); } diff --git a/source/main/physics/SimData.h b/source/main/physics/SimData.h index 07dde3705f..9fb671fe49 100644 --- a/source/main/physics/SimData.h +++ b/source/main/physics/SimData.h @@ -635,25 +635,6 @@ struct flare_t SimpleInertia inertia; //!< Only 'flares3' }; -struct exhaust_t -{ - NodeNum_t emitterNode = NODENUM_INVALID; - NodeNum_t directionNode = NODENUM_INVALID; - Ogre::SceneNode *smokeNode = nullptr; - Ogre::ParticleSystem* smoker = nullptr; //!< This remains `nullptr` if removed via `addonpart_unwanted_exhaust` or Tuning UI. - std::string particleSystemName; //!< Name in .particle file ~ for display in Tuning UI. -}; - - -struct cparticle_t -{ - NodeNum_t emitterNode = NODENUM_INVALID; - NodeNum_t directionNode = NODENUM_INVALID; - bool active; - Ogre::SceneNode *snode; - Ogre::ParticleSystem* psys; -}; - /// User input state for animated props with 'source:event'. struct PropAnimKeyState { diff --git a/source/main/physics/air/TurboJet.cpp b/source/main/physics/air/TurboJet.cpp index 07d0957f1e..925052b44d 100644 --- a/source/main/physics/air/TurboJet.cpp +++ b/source/main/physics/air/TurboJet.cpp @@ -183,6 +183,7 @@ void TurbojetVisual::UpdateVisuals(RoR::GfxActor* gfx_actor) if (m_smoke_particle && gfx_actor->GetSimDataBuffer().simbuf_smoke_enabled) { + App::GetGfxScene()->AdjustParticleSystemTimeFactor(m_smoke_particle); m_smoke_scenenode->setPosition(node_buf[m_node_back].AbsPosition); ParticleEmitter* emit = m_smoke_particle->getEmitter(0); emit->setDirection(-laxis); diff --git a/source/main/physics/air/TurboProp.cpp b/source/main/physics/air/TurboProp.cpp index 4b2f72b757..0ad08e4cde 100644 --- a/source/main/physics/air/TurboProp.cpp +++ b/source/main/physics/air/TurboProp.cpp @@ -175,6 +175,7 @@ void Turboprop::updateVisuals(RoR::GfxActor* gfx_m_actor) //smoke if (smokeNode) { + App::GetGfxScene()->AdjustParticleSystemTimeFactor(smokePS); smokeNode->setPosition(node_buf[nodeback].AbsPosition); ParticleEmitter* emit = smokePS->getEmitter(0); Vector3 dir = node_buf[nodeback].AbsPosition - node_buf[noderef].AbsPosition; diff --git a/source/main/terrain/TerrainObjectManager.cpp b/source/main/terrain/TerrainObjectManager.cpp index 20697d6533..8aab36208c 100644 --- a/source/main/terrain/TerrainObjectManager.cpp +++ b/source/main/terrain/TerrainObjectManager.cpp @@ -764,6 +764,11 @@ bool TerrainObjectManager::LoadTerrainObject(const Ogre::String& name, const Ogr SceneNode* sn = tenode->createChildSceneNode(); sn->attachObject(pParticleSys); sn->pitch(Degree(90)); + + ParticleEffectObject peo; + peo.node = sn; + peo.psys = pParticleSys; + m_particle_effect_objects.push_back(peo); } if (!odef->mat_name.empty()) @@ -953,10 +958,10 @@ bool TerrainObjectManager::LoadTerrainScript(const Ogre::String& filename) return result != SCRIPTUNITID_INVALID; } -bool TerrainObjectManager::UpdateAnimatedObjects(float dt) +void TerrainObjectManager::UpdateAnimatedObjects(float dt) { if (m_animated_objects.size() == 0) - return true; + return; std::vector::iterator it; @@ -968,7 +973,17 @@ bool TerrainObjectManager::UpdateAnimatedObjects(float dt) it->anim->addTime(time); } } - return true; +} + +void TerrainObjectManager::UpdateParticleEffectObjects() +{ + for (ParticleEffectObject& peo : m_particle_effect_objects) + { + if (peo.psys) + { + App::GetGfxScene()->AdjustParticleSystemTimeFactor(peo.psys); + } + } } void TerrainObjectManager::LoadTelepoints() @@ -1009,6 +1024,7 @@ bool TerrainObjectManager::UpdateTerrainObjects(float dt) } #endif //USE_PAGED this->UpdateAnimatedObjects(dt); + this->UpdateParticleEffectObjects(); return true; } diff --git a/source/main/terrain/TerrainObjectManager.h b/source/main/terrain/TerrainObjectManager.h index 8d6df52c1c..c6e589a66d 100644 --- a/source/main/terrain/TerrainObjectManager.h +++ b/source/main/terrain/TerrainObjectManager.h @@ -124,6 +124,12 @@ class TerrainObjectManager float speedfactor; }; + struct ParticleEffectObject + { + Ogre::ParticleSystem* psys = nullptr; + Ogre::SceneNode* node = nullptr; + }; + struct PredefinedActor { float px; @@ -149,9 +155,10 @@ class TerrainObjectManager RoR::ODefFile* FetchODef(std::string const & odef_name); void ProcessODefCollisionBoxes(StaticObject* obj, ODefFile* odef, const EditorObject& params, bool race_event); - // Misc functions + // Update functions - bool UpdateAnimatedObjects(float dt); + void UpdateAnimatedObjects(float dt); + void UpdateParticleEffectObjects(); // Variables @@ -161,6 +168,7 @@ class TerrainObjectManager std::vector m_editor_objects; std::vector m_predefined_actors; std::vector m_animated_objects; + std::vector m_particle_effect_objects; std::vector m_mesh_objects; SurveyMapEntityVec m_map_entities; Terrain* terrainManager;