diff --git a/Nuake/Engine.cpp b/Nuake/Engine.cpp index cea54337..e212817b 100644 --- a/Nuake/Engine.cpp +++ b/Nuake/Engine.cpp @@ -53,22 +53,20 @@ namespace Nuake s_CurrentWindow->Update(s_TimeStep); // Play mode update all the entities, Editor does not. - if (Engine::IsPlayMode()) - { - s_FixedUpdateDifference += s_TimeStep; - - // Fixed update - while (s_FixedUpdateDifference >= s_FixedUpdateRate) - { - s_CurrentWindow->FixedUpdate(s_FixedUpdateDifference); - - s_FixedUpdateDifference -= s_FixedUpdateRate; - } - } - else + if (!Engine::IsPlayMode()) { GetCurrentScene()->EditorUpdate(s_TimeStep); } + + s_FixedUpdateDifference += s_TimeStep; + + // Fixed update + while (s_FixedUpdateDifference >= s_FixedUpdateRate) + { + s_CurrentWindow->FixedUpdate(s_FixedUpdateDifference); + + s_FixedUpdateDifference -= s_FixedUpdateRate; + } } Input::Update(); diff --git a/Nuake/src/Rendering/SceneRenderer.cpp b/Nuake/src/Rendering/SceneRenderer.cpp index db19889a..bafbe7cc 100644 --- a/Nuake/src/Rendering/SceneRenderer.cpp +++ b/Nuake/src/Rendering/SceneRenderer.cpp @@ -3,9 +3,11 @@ #include "src/Scene/Components/BSPBrushComponent.h" #include "src/Scene/Components/SpriteComponent.h" +#include "src/Scene/Components/ParticleEmitterComponent.h" #include + namespace Nuake { void SceneRenderer::Init() @@ -231,6 +233,7 @@ namespace Nuake Renderer::SubmitMesh(sprite.SpriteMesh, finalQuadTransform, (uint32_t)e); } + Renderer::Flush(shader, true); } } @@ -286,7 +289,7 @@ namespace Nuake // Sprites auto spriteView = scene.m_Registry.view(); - for (auto e : spriteView) + for (auto& e : spriteView) { auto [transform, sprite, visibility] = spriteView.get(e); @@ -316,6 +319,34 @@ namespace Nuake Renderer::SubmitMesh(sprite.SpriteMesh, finalQuadTransform, (uint32_t)e); } Renderer::Flush(gBufferShader, false); + + // Particles + auto particleEmitterView = scene.m_Registry.view(); + for (auto& e : particleEmitterView) + { + auto [transform, emitterComponent, visibility] = particleEmitterView.get(e); + + if (!visibility.Visible) + continue; + + auto initialTransform = transform.GetGlobalTransform(); + for (auto& p : emitterComponent.Emitter.Particles) + { + Matrix4 particleTransform = initialTransform; + particleTransform = glm::inverse(mView); + + // Translation + const Vector3& particleGlobalPosition = transform.GetGlobalPosition() + p.Position; + particleTransform[3] = Vector4(particleGlobalPosition, 1.0f); + + // Scale + particleTransform = glm::scale(particleTransform, transform.GetGlobalScale()); + + Renderer::SubmitMesh(Renderer::QuadMesh, particleTransform, (uint32_t)e); + } + } + + Renderer::Flush(gBufferShader, false); } } diff --git a/Nuake/src/Scene/Components/ParticleEmitterComponent.h b/Nuake/src/Scene/Components/ParticleEmitterComponent.h index 7f46ccd2..f7fc44a6 100644 --- a/Nuake/src/Scene/Components/ParticleEmitterComponent.h +++ b/Nuake/src/Scene/Components/ParticleEmitterComponent.h @@ -1,7 +1,10 @@ #pragma once #include "src/Core/Core.h" +#include "src/Core/Maths.h" #include "src/Resource/Serializable.h" -#include + +#include "src/Scene/Systems/ParticleEmitter.h" + namespace Nuake { @@ -21,6 +24,8 @@ namespace Nuake // For now use a radius, later should use shape. float Radius; + ParticleEmitter Emitter; + public: json Serialize(); bool Deserialize(const std::string& str); diff --git a/Nuake/src/Scene/Systems/Particle.h b/Nuake/src/Scene/Systems/Particle.h new file mode 100644 index 00000000..59f93989 --- /dev/null +++ b/Nuake/src/Scene/Systems/Particle.h @@ -0,0 +1,13 @@ +#pragma once +#include "src/Core/Maths.h" + +namespace Nuake +{ + struct Particle + { + Vector3 Position; + Vector3 Velocity; + Color Color; + float Life; + }; +} diff --git a/Nuake/src/Scene/Systems/ParticleEmitter.cpp b/Nuake/src/Scene/Systems/ParticleEmitter.cpp new file mode 100644 index 00000000..5cf21798 --- /dev/null +++ b/Nuake/src/Scene/Systems/ParticleEmitter.cpp @@ -0,0 +1,63 @@ +#include "ParticleEmitter.h" + +namespace Nuake +{ + ParticleEmitter::ParticleEmitter() + { + m_MT = std::mt19937(std::random_device()()); + m_Random = std::uniform_real_distribution(-Radius, Radius); + } + + void ParticleEmitter::SpawnParticle() + { + const bool canSpawnParticle = Particles.size() < Amount; + if (!canSpawnParticle) + { + return; + } + + const auto initialPosition = Vector3(m_Random(m_MT), m_Random(m_MT), m_Random(m_MT)); + const auto initialVelocity = Vector3(); + const auto initialColor = Color(1, 0, 0, 1); // TODO: Use color. + const float initialLife = Life; + + Particles.push_back({ + initialPosition, + initialVelocity, + initialColor, + initialLife + }); + } + + void ParticleEmitter::Update(Timestep ts) + { + std::vector deletionQueue; + int i = 0; + for (auto& p : Particles) + { + p.Life -= ts; + + if (p.Life <= 0.0f) // The particle has died. + { + deletionQueue.push_back(i); + } + + i++; + } + + // Delete all dead particles. + int shiftOffset = 0; + for (int d = deletionQueue.size() - 1; d > 0; d--) + { + // Erase shifts the elements + const auto it = d; + Particles.erase(Particles.begin() + it); + } + + for (auto& p : Particles) + { + p.Velocity += Gravity * static_cast(ts); + p.Position += p.Velocity * static_cast(ts); + } + } +} \ No newline at end of file diff --git a/Nuake/src/Scene/Systems/ParticleEmitter.h b/Nuake/src/Scene/Systems/ParticleEmitter.h new file mode 100644 index 00000000..9aa3e0dc --- /dev/null +++ b/Nuake/src/Scene/Systems/ParticleEmitter.h @@ -0,0 +1,34 @@ +#pragma once +#include "Particle.h" + +#include "src/Core/Core.h" +#include "src/Core/Maths.h" +#include "src/Core/Timestep.h" + +#include + +namespace Nuake +{ + class ParticleEmitter + { + public: + float Amount = 0.0f; + float Life = 5.0f; + Vector3 Gravity = Vector3(0, 0, 0); + float GravityRandom = 0.0f; + float Radius = 1.0f; + + private: + std::mt19937 m_MT; + std::uniform_real_distribution m_Random; + + public: + ParticleEmitter(); + ~ParticleEmitter() = default; + + void SpawnParticle(); + void Update(Timestep ts); + + std::vector Particles; + }; +} \ No newline at end of file diff --git a/Nuake/src/Scene/Systems/ParticleSystem.cpp b/Nuake/src/Scene/Systems/ParticleSystem.cpp index 52ca6eff..a730f7a6 100644 --- a/Nuake/src/Scene/Systems/ParticleSystem.cpp +++ b/Nuake/src/Scene/Systems/ParticleSystem.cpp @@ -1,7 +1,9 @@ #include "ParticleSystem.h" #include "src/Scene/Scene.h" -#include "src/Scene/Components/QuakeMap.h" +#include "src/Scene/Entities/Entity.h" +#include "src/Scene/Components/ParticleEmitterComponent.h" + namespace Nuake { @@ -17,12 +19,35 @@ namespace Nuake void ParticleSystem::Update(Timestep ts) { + auto& view = m_Scene->m_Registry.view(); + for (auto& e : view) + { + auto [transformComponent, emitterComponent] = view.get(e); + Entity ent = Entity({ e, m_Scene }); + // Copy emitter settings from component to real emitter + auto& emitter = emitterComponent.Emitter; + emitter.Amount = emitterComponent.Amount; + emitter.Gravity = emitterComponent.Gravity; + emitter.GravityRandom = emitterComponent.GravityRandom; + emitter.Radius = emitterComponent.Radius; + emitter.Life = emitterComponent.Life; + + // Spawn particle if possible + emitterComponent.Emitter.SpawnParticle(); + } } void ParticleSystem::FixedUpdate(Timestep ts) { + auto& view = m_Scene->m_Registry.view(); + for (auto& e : view) + { + auto [transformComponent, emitterComponent] = view.get(e); + Entity ent = Entity({ e, m_Scene }); + emitterComponent.Emitter.Update(ts); + } } void ParticleSystem::EditorUpdate() diff --git a/Nuake/src/Scene/Systems/ParticleSystem.h b/Nuake/src/Scene/Systems/ParticleSystem.h index 6d44f96e..11678299 100644 --- a/Nuake/src/Scene/Systems/ParticleSystem.h +++ b/Nuake/src/Scene/Systems/ParticleSystem.h @@ -1,5 +1,9 @@ #pragma once #include +#include + +#include + namespace Nuake { @@ -13,7 +17,5 @@ namespace Nuake void EditorUpdate() override; void FixedUpdate(Timestep ts) override; void Exit() override; - - private: }; } diff --git a/Nuake/src/Scene/Systems/ScriptingSystem.cpp b/Nuake/src/Scene/Systems/ScriptingSystem.cpp index c41f1bff..4b9d3578 100644 --- a/Nuake/src/Scene/Systems/ScriptingSystem.cpp +++ b/Nuake/src/Scene/Systems/ScriptingSystem.cpp @@ -56,6 +56,9 @@ namespace Nuake { void ScriptingSystem::FixedUpdate(Timestep ts) { + if (!Engine::IsPlayMode()) + return; + auto entities = m_Scene->m_Registry.view(); for (auto& e : entities) {