mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-07-20 02:09:19 +02:00
WIP
This commit is contained in:
@@ -36,14 +36,21 @@ void ECScene::Unload()
|
||||
|
||||
void ECScene::Update(float dt)
|
||||
{
|
||||
if (!m_loaded){
|
||||
|
||||
if (!m_loaded)
|
||||
return;
|
||||
}
|
||||
|
||||
m_systems.Update(m_world, dt);
|
||||
OnUpdate(dt);
|
||||
}
|
||||
|
||||
void ECScene::Render()
|
||||
{
|
||||
if (!m_loaded)
|
||||
return;
|
||||
|
||||
m_systems.Render(m_world);
|
||||
}
|
||||
|
||||
|
||||
ECWorld& ECScene::World()
|
||||
{
|
||||
|
||||
@@ -21,6 +21,7 @@ public:
|
||||
void Load();
|
||||
void Unload();
|
||||
void Update(float dt);
|
||||
void Render();
|
||||
|
||||
ECWorld& World();
|
||||
const ECWorld& World() const;
|
||||
|
||||
@@ -52,3 +52,9 @@ const entt::registry& ECWorld::Registry() const
|
||||
{
|
||||
return m_registry;
|
||||
}
|
||||
|
||||
ECWorld& GetComponentSystem()
|
||||
{
|
||||
static ECWorld instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
@@ -66,6 +66,24 @@ public:
|
||||
}, components);
|
||||
}
|
||||
}
|
||||
|
||||
// Same as Each but skips entities that have any of the Excluded components.
|
||||
// Usage: world.Each<A, B>(entt::exclude<Disabled>, lambda)
|
||||
template<typename... Components, typename... Excluded, typename Func>
|
||||
void Each(entt::exclude_t<Excluded...>, Func&& func)
|
||||
{
|
||||
auto view = m_registry.view<Components...>(entt::exclude<Excluded...>);
|
||||
|
||||
for (auto entity : view)
|
||||
{
|
||||
auto components = std::forward_as_tuple(view.get<Components>(entity)...);
|
||||
|
||||
std::apply([&](Components&... comps)
|
||||
{
|
||||
func(entity, comps...);
|
||||
}, components);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<entt::entity> GetEntities() const
|
||||
{
|
||||
@@ -96,11 +114,16 @@ T& ECWorld::AddComponent(entt::entity entity, Args&&... args)
|
||||
"Component must be constructible with provided arguments");
|
||||
|
||||
if (m_registry.all_of<T>(entity))
|
||||
{
|
||||
return m_registry.replace<T>(entity, std::forward<Args>(args)...);
|
||||
}
|
||||
m_registry.replace<T>(entity, std::forward<Args>(args)...);
|
||||
else
|
||||
m_registry.emplace<T>(entity, std::forward<Args>(args)...);
|
||||
|
||||
return m_registry.emplace<T>(entity, std::forward<Args>(args)...);
|
||||
if constexpr (std::is_empty_v<T>) {
|
||||
static T dummy{};
|
||||
return dummy;
|
||||
} else {
|
||||
return m_registry.get<T>(entity);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
|
||||
@@ -16,8 +16,6 @@ SceneManager::~SceneManager()
|
||||
void SceneManager::SetScene(std::unique_ptr<ECScene> scene)
|
||||
{
|
||||
m_pendingScene = std::move(scene);
|
||||
if (m_currentScene)
|
||||
m_currentScene->Load();
|
||||
}
|
||||
|
||||
void SceneManager::Update(float dt)
|
||||
|
||||
@@ -8,10 +8,12 @@
|
||||
#include "glm/vec3.hpp"
|
||||
#include "glm/gtc/quaternion.hpp"
|
||||
#include "registry/FName.h"
|
||||
#include "utils/uuid.hpp"
|
||||
#include "utilities/uuid.hpp"
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
class TDynamicObject;
|
||||
|
||||
namespace ECSComponent
|
||||
{
|
||||
///< summary>
|
||||
@@ -45,6 +47,15 @@ struct Identification
|
||||
struct Parent
|
||||
{
|
||||
entt::entity value{entt::null};
|
||||
glm::dvec3 localOffset{0.0}; // position offset relative to parent
|
||||
glm::quat localRotation{1.f, 0.f, 0.f, 0.f}; // rotation relative to parent
|
||||
};
|
||||
|
||||
// Links an ECS entity to its corresponding simulation vehicle.
|
||||
// VehicleSyncSystem reads this to keep Transform in sync with vehicle world position.
|
||||
struct VehicleRef
|
||||
{
|
||||
TDynamicObject* vehicle = nullptr;
|
||||
};
|
||||
|
||||
struct SoundComponent
|
||||
|
||||
@@ -4,26 +4,23 @@
|
||||
#include "registry/FName.h"
|
||||
#include "rendering/geometrybank.h"
|
||||
|
||||
class TAnimModel;
|
||||
|
||||
namespace ECSComponent
|
||||
{
|
||||
/// <summary>
|
||||
/// Component for entities that can be rendered.
|
||||
/// </summary>
|
||||
/// <remarks>Currently empty
|
||||
/// TODO: Add component members
|
||||
/// </remarks>
|
||||
struct MeshRenderer
|
||||
{
|
||||
gfx::geometry_handle meshHandle;
|
||||
TAnimModel* modelInstance = nullptr;
|
||||
bool visible = true;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Component for entities that can cast shadows.
|
||||
/// </summary>
|
||||
/// <remarks>Currently empty
|
||||
/// TODO: Add component members
|
||||
/// </remarks>
|
||||
struct SpotLight
|
||||
{
|
||||
glm::vec3 Color{ 1.0f, 1.0f, 1.0f };
|
||||
@@ -38,16 +35,70 @@ struct SpotLight
|
||||
bool Enabled = true;
|
||||
};
|
||||
|
||||
// TODO: AreaLight like blender
|
||||
// TODO: SunLight for scene
|
||||
|
||||
|
||||
struct ParticleEmitter
|
||||
/// <summary>
|
||||
///
|
||||
///</summary>
|
||||
struct AreaLight
|
||||
{
|
||||
FName particleEffectPath = ("");
|
||||
bool active = true;
|
||||
glm::vec3 Color{ 1.0f, 1.0f, 1.0f };
|
||||
float Intensity = 1.0f;
|
||||
float Width = 1.0f;
|
||||
float Height = 1.0f;
|
||||
bool CastShadows = false;
|
||||
bool Enabled = true;
|
||||
};
|
||||
|
||||
struct SunLight
|
||||
{
|
||||
glm::vec3 Color{ 1.0f, 1.0f, 1.0f };
|
||||
float Intensity = 1.0f;
|
||||
bool CastShadows = false;
|
||||
bool Enabled = true;
|
||||
};
|
||||
|
||||
|
||||
struct Particle {
|
||||
glm::vec3 position;
|
||||
glm::vec3 velocity;
|
||||
glm::vec4 color;
|
||||
float size;
|
||||
float age;
|
||||
float maxAge;
|
||||
};
|
||||
|
||||
struct ParticleEmitter {
|
||||
|
||||
// --- Kontener i Stan ---
|
||||
std::vector<Particle> particles;
|
||||
uint32_t maxParticles = 1000;
|
||||
bool isActive = true;
|
||||
float spawnAccumulator = 0.0f;
|
||||
|
||||
// --- Konfiguracja Emisji ---
|
||||
float spawnRate = 20.0f; // cząstek na sekundę
|
||||
float particleLifetime = 2.0f; // bazowy czas życia
|
||||
glm::vec3 gravity{ 0.0f, 0.0f, 0.0f }; // np. dym: 0.5, deszcz: -9.81
|
||||
float airResistance = 0.1f; // tłumienie prędkości
|
||||
|
||||
// --- Zakresy (losowanie) ---
|
||||
glm::vec3 minStartVelocity{ -0.5f, 1.0f, -0.5f };
|
||||
glm::vec3 maxStartVelocity{ 0.5f, 2.0f, 0.5f };
|
||||
|
||||
// --- Ewolucja w czasie (Modyfikatory) ---
|
||||
float sizeGrowth = 0.5f; // zmiana rozmiaru na sekundę
|
||||
glm::vec4 colorFade = { 0.0f, 0.0f, 0.0f, -0.5f }; // np. znikanie (alpha)
|
||||
|
||||
// --- Pozycja emitera ---
|
||||
glm::vec3 emitterLocation{ 0.0f, 0.0f, 0.0f };
|
||||
|
||||
// --- Flag Funkcjonalnych ---
|
||||
bool hasCollision = false; // czy sprawdzać kolizje
|
||||
float bounceFactor = 0.3f; // energia po odbiciu (0.0 - 1.0)
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Component for entities that can be rendered as decals (e.g. graffiti on wagons, dirties on wall).
|
||||
/// </summary>
|
||||
struct Decal
|
||||
{
|
||||
FName decalTexturePath = ("");
|
||||
@@ -55,6 +106,10 @@ struct Decal
|
||||
bool active = true;
|
||||
};
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Component for entities that can be rendered as billboards (e.g. particles, sprites).
|
||||
/// </summary>
|
||||
struct Billboard
|
||||
{
|
||||
FName texturePath = ("");
|
||||
@@ -62,6 +117,9 @@ struct Billboard
|
||||
bool active = true;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Component for entities that can be rendered as lines.
|
||||
/// </summary>
|
||||
struct Line {
|
||||
glm::vec3 start{ 0.0f };
|
||||
glm::vec3 end{ 1.0f, 0.0f, 0.0f };
|
||||
|
||||
@@ -4,32 +4,96 @@
|
||||
|
||||
#include "GameScene.h"
|
||||
|
||||
|
||||
#include "entitysystem/components/BasicComponents.h"
|
||||
#include "entitysystem/components/RenderComponents.h"
|
||||
#include "entitysystem/systems/MovementSystem.h"
|
||||
#include "entitysystem/systems/ParticlesSystem.h"
|
||||
#include "entitysystem/systems/MeshRenderSystem.h"
|
||||
#include "entitysystem/systems/LODSystem.h"
|
||||
#include "entitysystem/systems/SoundSystem.h"
|
||||
#include "entitysystem/systems/LightSystem.h"
|
||||
#include "entitysystem/systems/HierarchySystem.h"
|
||||
#include "entitysystem/systems/LineSystem.h"
|
||||
#include "entitysystem/systems/VehicleSyncSystem.h"
|
||||
#include "entitysystem/systems/BillboardSystem.h"
|
||||
#include "utilities/Logs.h"
|
||||
|
||||
GameScene::GameScene() = default;
|
||||
GameScene::~GameScene() = default ;
|
||||
GameScene::~GameScene() = default;
|
||||
|
||||
void GameScene::OnLoad()
|
||||
{
|
||||
m_systems.AddSystem<VehicleSyncSystem>(); // must be first — updates Transform from live vehicle
|
||||
m_systems.AddSystem<MovementSystem>();
|
||||
|
||||
m_systems.AddSystem<HierarchySystem>();
|
||||
m_systems.AddSystem<LightSystem>();
|
||||
m_systems.AddSystem<ParticlesSystem>();
|
||||
m_systems.AddSystem<SoundSystem>();
|
||||
m_systems.AddSystem<LODSystem>();
|
||||
m_systems.AddSystem<MeshRenderSystem>();
|
||||
m_systems.AddSystem<LineSystem>();
|
||||
m_systems.AddSystem<BillboardSystem>();
|
||||
|
||||
#ifdef _DEBUG
|
||||
CreateTestEntities();
|
||||
#endif
|
||||
}
|
||||
|
||||
void GameScene::OnUpdate(float dt)
|
||||
{
|
||||
|
||||
//auto& world = World();
|
||||
|
||||
//auto view = world.View<ECSComponent::Transform>();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void GameScene::OnUnload()
|
||||
{
|
||||
World().Clear();
|
||||
}
|
||||
|
||||
void GameScene::CreateTestEntities()
|
||||
{
|
||||
auto& world = World();
|
||||
|
||||
// smoke emitter at world origin for visual testing of ParticlesSystem
|
||||
{
|
||||
auto entity = world.CreateEntity();
|
||||
|
||||
auto& id = world.AddComponent<ECSComponent::Identification>(entity);
|
||||
id.Name = "TestSmokeEmitter";
|
||||
|
||||
auto& transform = world.AddComponent<ECSComponent::Transform>(entity);
|
||||
transform.Position = glm::dvec3(0.0, 2.0, 0.0);
|
||||
|
||||
auto& emitter = world.AddComponent<ECSComponent::ParticleEmitter>(entity);
|
||||
emitter.emitterLocation = glm::vec3(0.0f, 2.0f, 0.0f);
|
||||
emitter.isActive = true;
|
||||
emitter.spawnRate = 15.0f;
|
||||
emitter.particleLifetime = 3.0f;
|
||||
emitter.gravity = glm::vec3(0.0f, 0.3f, 0.0f);
|
||||
emitter.airResistance = 0.2f;
|
||||
emitter.colorFade = glm::vec4(0.0f, 0.0f, 0.0f, -0.4f);
|
||||
|
||||
WriteLog("[ECS] Created test entity: TestSmokeEmitter");
|
||||
}
|
||||
|
||||
// moving entity to test MovementSystem + LOD culling
|
||||
{
|
||||
auto entity = world.CreateEntity();
|
||||
|
||||
auto& id = world.AddComponent<ECSComponent::Identification>(entity);
|
||||
id.Name = "TestMovingBox";
|
||||
|
||||
auto& transform = world.AddComponent<ECSComponent::Transform>(entity);
|
||||
transform.Position = glm::dvec3(10.0, 0.5, 0.0);
|
||||
|
||||
auto& velocity = world.AddComponent<ECSComponent::Velocity>(entity);
|
||||
velocity.Value = glm::vec3(-1.0f, 0.0f, 0.0f);
|
||||
|
||||
auto& lod = world.AddComponent<ECSComponent::LODController>(entity);
|
||||
lod.RangeMin = 0.0;
|
||||
lod.RangeMax = 200.0;
|
||||
|
||||
auto& mesh = world.AddComponent<ECSComponent::MeshRenderer>(entity);
|
||||
mesh.visible = true;
|
||||
|
||||
WriteLog("[ECS] Created test entity: TestMovingBox");
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ public:
|
||||
virtual void OnDestroy(ECWorld& world) {}
|
||||
|
||||
virtual void Update(ECWorld& world, float dt) {}
|
||||
virtual void Render(ECWorld& world) {}
|
||||
};
|
||||
|
||||
|
||||
|
||||
116
entitysystem/systems/BillboardSystem.cpp
Normal file
116
entitysystem/systems/BillboardSystem.cpp
Normal file
@@ -0,0 +1,116 @@
|
||||
#include "stdafx.h"
|
||||
#include "BillboardSystem.h"
|
||||
|
||||
#include "entitysystem/ECWorld.h"
|
||||
#include "entitysystem/components/BasicComponents.h"
|
||||
#include "entitysystem/components/RenderComponents.h"
|
||||
#include "utilities/Globals.h"
|
||||
#include "utilities/Logs.h"
|
||||
|
||||
#include "gl/buffer.h"
|
||||
#include "gl/vao.h"
|
||||
#include "gl/shader.h"
|
||||
|
||||
struct BillboardSystem::RenderState
|
||||
{
|
||||
std::optional<gl::buffer> instanceBuffer;
|
||||
std::optional<gl::vao> vao;
|
||||
std::unique_ptr<gl::program> shader;
|
||||
std::size_t bufferCapacity { 0 };
|
||||
};
|
||||
|
||||
BillboardSystem::BillboardSystem() = default;
|
||||
BillboardSystem::~BillboardSystem() = default;
|
||||
|
||||
void BillboardSystem::Render(ECWorld& world)
|
||||
{
|
||||
// Group billboard instances by texture path
|
||||
std::unordered_map<std::string, std::vector<BillboardInstance>> groups;
|
||||
|
||||
const glm::vec3 camPos{
|
||||
static_cast<float>(Global.pCamera.Pos.x),
|
||||
static_cast<float>(Global.pCamera.Pos.y),
|
||||
static_cast<float>(Global.pCamera.Pos.z)
|
||||
};
|
||||
|
||||
world.Each<ECSComponent::Billboard, ECSComponent::Transform>(
|
||||
entt::exclude<ECSComponent::Disabled>,
|
||||
[&](entt::entity, ECSComponent::Billboard& bill, ECSComponent::Transform& t)
|
||||
{
|
||||
if (!bill.active) return;
|
||||
std::string key = bill.texturePath.ToString();
|
||||
groups[key].push_back({
|
||||
glm::vec3(t.Position) - camPos,
|
||||
bill.size
|
||||
});
|
||||
});
|
||||
|
||||
if (groups.empty()) return;
|
||||
|
||||
// Lazy-init shared GL resources
|
||||
if (!m_renderState)
|
||||
m_renderState = std::make_unique<RenderState>();
|
||||
|
||||
auto& rs = *m_renderState;
|
||||
|
||||
if (!rs.instanceBuffer)
|
||||
rs.instanceBuffer.emplace();
|
||||
|
||||
if (!rs.shader) {
|
||||
try {
|
||||
gl::shader vert("ecs_billboard.vert");
|
||||
gl::shader frag("ecs_billboard.frag");
|
||||
rs.shader = std::unique_ptr<gl::program>(new gl::program({vert, frag}));
|
||||
} catch (const gl::shader_exception& e) {
|
||||
WriteLog("[ECS] BillboardSystem: shader compile failed: " + std::string(e.what()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glDepthMask(GL_FALSE);
|
||||
rs.shader->bind();
|
||||
|
||||
for (auto& [texPath, instances] : groups) {
|
||||
// Grow VBO if needed
|
||||
if (instances.size() > rs.bufferCapacity) {
|
||||
rs.bufferCapacity = instances.size() + 64;
|
||||
rs.instanceBuffer->allocate(
|
||||
gl::buffer::ARRAY_BUFFER,
|
||||
static_cast<int>(rs.bufferCapacity * sizeof(BillboardInstance)),
|
||||
GL_DYNAMIC_DRAW);
|
||||
rs.vao.reset(); // rebuild after realloc
|
||||
}
|
||||
|
||||
rs.instanceBuffer->upload(
|
||||
gl::buffer::ARRAY_BUFFER,
|
||||
instances.data(), 0,
|
||||
static_cast<int>(instances.size() * sizeof(BillboardInstance)));
|
||||
|
||||
if (!rs.vao) {
|
||||
rs.vao.emplace();
|
||||
constexpr int stride = sizeof(BillboardInstance);
|
||||
rs.vao->setup_attrib(*rs.instanceBuffer, 0, 3, GL_FLOAT, stride, 0); // position
|
||||
glVertexAttribDivisor(0, 1);
|
||||
rs.vao->setup_attrib(*rs.instanceBuffer, 1, 1, GL_FLOAT, stride, 12); // size
|
||||
glVertexAttribDivisor(1, 1);
|
||||
rs.vao->unbind();
|
||||
rs.instanceBuffer->unbind(gl::buffer::ARRAY_BUFFER);
|
||||
}
|
||||
|
||||
// Bind texture (fallback: skip group if path is empty)
|
||||
if (!texPath.empty()) {
|
||||
auto texHandle = GfxRenderer->Fetch_Texture(texPath, true);
|
||||
GfxRenderer->Bind_Texture(0, texHandle);
|
||||
}
|
||||
|
||||
rs.vao->bind();
|
||||
glDrawArraysInstanced(GL_TRIANGLES, 0, 6, static_cast<GLsizei>(instances.size()));
|
||||
rs.vao->unbind();
|
||||
}
|
||||
|
||||
gl::program::unbind();
|
||||
glDepthMask(GL_TRUE);
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
25
entitysystem/systems/BillboardSystem.h
Normal file
25
entitysystem/systems/BillboardSystem.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "BaseSystem.h"
|
||||
#include "rendering/renderer.h"
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
|
||||
// Renders Billboard components as camera-facing textured quads.
|
||||
// Instances sharing the same texture are batched into one draw call.
|
||||
class BillboardSystem : public BaseSystem
|
||||
{
|
||||
public:
|
||||
BillboardSystem();
|
||||
~BillboardSystem();
|
||||
void Render(ECWorld& world) override;
|
||||
|
||||
private:
|
||||
struct BillboardInstance {
|
||||
glm::vec3 position; // camera-relative
|
||||
float size;
|
||||
};
|
||||
|
||||
struct RenderState;
|
||||
std::unique_ptr<RenderState> m_renderState;
|
||||
};
|
||||
27
entitysystem/systems/HierarchySystem.cpp
Normal file
27
entitysystem/systems/HierarchySystem.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#include "stdafx.h"
|
||||
#include "HierarchySystem.h"
|
||||
|
||||
#include "entitysystem/ECWorld.h"
|
||||
#include "entitysystem/components/BasicComponents.h"
|
||||
|
||||
void HierarchySystem::Update(ECWorld& world, float dt)
|
||||
{
|
||||
world.Each<ECSComponent::Parent, ECSComponent::Transform>(
|
||||
entt::exclude<ECSComponent::Disabled>,
|
||||
[&](entt::entity, ECSComponent::Parent& parent, ECSComponent::Transform& childTransform)
|
||||
{
|
||||
if (parent.value == entt::null || !world.IsAlive(parent.value))
|
||||
return;
|
||||
|
||||
const auto* parentTransform = world.GetComponent<ECSComponent::Transform>(parent.value);
|
||||
if (!parentTransform)
|
||||
return;
|
||||
|
||||
// Rotate local offset by parent rotation then add parent world position.
|
||||
glm::dvec3 rotatedOffset = glm::dvec3(parentTransform->Rotation * glm::vec3(parent.localOffset));
|
||||
childTransform.Position = parentTransform->Position + rotatedOffset;
|
||||
|
||||
// Compose rotations: child world = parent world * child local.
|
||||
childTransform.Rotation = parentTransform->Rotation * parent.localRotation;
|
||||
});
|
||||
}
|
||||
14
entitysystem/systems/HierarchySystem.h
Normal file
14
entitysystem/systems/HierarchySystem.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "BaseSystem.h"
|
||||
|
||||
// Propagates parent world Transform to children.
|
||||
// Runs after MovementSystem so children always inherit the parent's
|
||||
// already-updated position in the same frame.
|
||||
// Child's Transform.Position is stored as a LOCAL offset relative to
|
||||
// the parent and this system overwrites it with the computed world position.
|
||||
class HierarchySystem : public BaseSystem
|
||||
{
|
||||
public:
|
||||
void Update(ECWorld& world, float dt) override;
|
||||
};
|
||||
33
entitysystem/systems/LODSystem.cpp
Normal file
33
entitysystem/systems/LODSystem.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#include "stdafx.h"
|
||||
#include "LODSystem.h"
|
||||
|
||||
#include "entitysystem/ECWorld.h"
|
||||
#include "entitysystem/components/BasicComponents.h"
|
||||
#include "entitysystem/components/RenderComponents.h"
|
||||
#include "utilities/Globals.h"
|
||||
#include "model/AnimModel.h"
|
||||
|
||||
void LODSystem::Update(ECWorld& world, float dt)
|
||||
{
|
||||
const glm::dvec3 camPos{
|
||||
Global.pCamera.Pos.x,
|
||||
Global.pCamera.Pos.y,
|
||||
Global.pCamera.Pos.z
|
||||
};
|
||||
|
||||
world.Each<ECSComponent::LODController, ECSComponent::Transform>(
|
||||
entt::exclude<ECSComponent::Disabled>,
|
||||
[&](entt::entity entity,
|
||||
ECSComponent::LODController& lod,
|
||||
ECSComponent::Transform& transform)
|
||||
{
|
||||
double dist = glm::length(camPos - transform.Position);
|
||||
bool inRange = (dist >= lod.RangeMin) && (dist <= lod.RangeMax);
|
||||
|
||||
if (auto* mesh = world.GetComponent<ECSComponent::MeshRenderer>(entity)) {
|
||||
mesh->visible = inRange;
|
||||
if (mesh->modelInstance)
|
||||
mesh->modelInstance->visible(inRange);
|
||||
}
|
||||
});
|
||||
}
|
||||
11
entitysystem/systems/LODSystem.h
Normal file
11
entitysystem/systems/LODSystem.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "BaseSystem.h"
|
||||
|
||||
// Reads LODController + Transform on each entity and toggles MeshRenderer.visible
|
||||
// based on distance to camera. Runs every Update tick.
|
||||
class LODSystem : public BaseSystem
|
||||
{
|
||||
public:
|
||||
void Update(ECWorld& world, float dt) override;
|
||||
};
|
||||
62
entitysystem/systems/LightSystem.cpp
Normal file
62
entitysystem/systems/LightSystem.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
#include "stdafx.h"
|
||||
#include "LightSystem.h"
|
||||
|
||||
#include "entitysystem/ECWorld.h"
|
||||
#include "entitysystem/components/BasicComponents.h"
|
||||
#include "entitysystem/components/RenderComponents.h"
|
||||
#include "utilities/Globals.h"
|
||||
#include "rendering/lightarray.h"
|
||||
#include "simulation/simulation.h"
|
||||
|
||||
void LightSystem::Update(ECWorld& world, float dt)
|
||||
{
|
||||
// Sync SunLight intensity with global overcast value.
|
||||
// Overcast 0 = clear sky (full sun), 2 = heavy overcast (dim).
|
||||
const float sunIntensity = glm::clamp( 1.0f - Global.Overcast * 0.4f, 0.1f, 1.0f );
|
||||
world.Each<ECSComponent::SunLight>(
|
||||
entt::exclude<ECSComponent::Disabled>,
|
||||
[sunIntensity](entt::entity, ECSComponent::SunLight& sun)
|
||||
{
|
||||
if (sun.Enabled)
|
||||
sun.Intensity = sunIntensity;
|
||||
});
|
||||
|
||||
// SpotLights: cull distant lights and rebuild the free_lights list for the renderer.
|
||||
const glm::dvec3 camPos{
|
||||
Global.pCamera.Pos.x,
|
||||
Global.pCamera.Pos.y,
|
||||
Global.pCamera.Pos.z
|
||||
};
|
||||
simulation::Lights.free_lights.clear();
|
||||
world.Each<ECSComponent::SpotLight, ECSComponent::Transform>(
|
||||
entt::exclude<ECSComponent::Disabled>,
|
||||
[&](entt::entity, ECSComponent::SpotLight& spot, ECSComponent::Transform& transform)
|
||||
{
|
||||
double dist = glm::length(camPos - transform.Position);
|
||||
if (dist > static_cast<double>(spot.Range) * 4.0) {
|
||||
spot.Enabled = false;
|
||||
return;
|
||||
}
|
||||
if (spot.Range > 0.0f)
|
||||
spot.Enabled = true;
|
||||
|
||||
if (!spot.Enabled) return;
|
||||
|
||||
light_array::free_light_record rec;
|
||||
rec.position = transform.Position;
|
||||
rec.direction = glm::vec3(0.f, -1.f, 0.f); // default downward; rotation support can be added later
|
||||
rec.color = spot.Color;
|
||||
rec.intensity = spot.Intensity;
|
||||
rec.range = spot.Range;
|
||||
rec.inner_cutoff = glm::cos(glm::radians(spot.InnerAngle));
|
||||
rec.outer_cutoff = glm::cos(glm::radians(spot.OuterAngle));
|
||||
simulation::Lights.free_lights.push_back(rec);
|
||||
});
|
||||
}
|
||||
|
||||
void LightSystem::Render(ECWorld& world)
|
||||
{
|
||||
// Rendering of ECS lights is handled by the main renderer pipeline,
|
||||
// which queries SpotLight/AreaLight/SunLight components via World views.
|
||||
// Nothing to do here for now.
|
||||
}
|
||||
13
entitysystem/systems/LightSystem.h
Normal file
13
entitysystem/systems/LightSystem.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "BaseSystem.h"
|
||||
|
||||
// Manages ECS light components (SpotLight, AreaLight, SunLight).
|
||||
// Updates light position/direction from Transform each tick.
|
||||
// Actual renderer integration is handled by the rendering pipeline reading these components.
|
||||
class LightSystem : public BaseSystem
|
||||
{
|
||||
public:
|
||||
void Update(ECWorld& world, float dt) override;
|
||||
void Render(ECWorld& world) override;
|
||||
};
|
||||
96
entitysystem/systems/LineSystem.cpp
Normal file
96
entitysystem/systems/LineSystem.cpp
Normal file
@@ -0,0 +1,96 @@
|
||||
#include "stdafx.h"
|
||||
#include "LineSystem.h"
|
||||
|
||||
#include "entitysystem/ECWorld.h"
|
||||
#include "entitysystem/components/BasicComponents.h"
|
||||
#include "entitysystem/components/RenderComponents.h"
|
||||
#include "utilities/Globals.h"
|
||||
#include "utilities/Logs.h"
|
||||
|
||||
#include "gl/buffer.h"
|
||||
#include "gl/vao.h"
|
||||
#include "gl/shader.h"
|
||||
|
||||
struct LineSystem::RenderState
|
||||
{
|
||||
std::optional<gl::buffer> vertexBuffer;
|
||||
std::optional<gl::vao> vao;
|
||||
std::unique_ptr<gl::program> shader;
|
||||
std::size_t bufferCapacity { 0 };
|
||||
};
|
||||
|
||||
LineSystem::LineSystem() = default;
|
||||
LineSystem::~LineSystem() = default;
|
||||
|
||||
void LineSystem::Render(ECWorld& world)
|
||||
{
|
||||
std::vector<LineVertex> vertices;
|
||||
vertices.reserve(256);
|
||||
|
||||
const glm::vec3 camPos{
|
||||
static_cast<float>(Global.pCamera.Pos.x),
|
||||
static_cast<float>(Global.pCamera.Pos.y),
|
||||
static_cast<float>(Global.pCamera.Pos.z)
|
||||
};
|
||||
|
||||
world.Each<ECSComponent::Line, ECSComponent::Transform>(
|
||||
entt::exclude<ECSComponent::Disabled>,
|
||||
[&](entt::entity, ECSComponent::Line& line, ECSComponent::Transform& t)
|
||||
{
|
||||
if (!line.active) return;
|
||||
|
||||
glm::vec3 worldOrigin = glm::vec3(t.Position);
|
||||
vertices.push_back({ worldOrigin + line.start - camPos, line.color });
|
||||
vertices.push_back({ worldOrigin + line.end - camPos, line.color });
|
||||
});
|
||||
|
||||
if (vertices.empty()) return;
|
||||
|
||||
if (!m_renderState)
|
||||
m_renderState = std::make_unique<RenderState>();
|
||||
|
||||
auto& rs = *m_renderState;
|
||||
|
||||
if (!rs.vertexBuffer)
|
||||
rs.vertexBuffer.emplace();
|
||||
|
||||
if (vertices.size() > rs.bufferCapacity) {
|
||||
rs.bufferCapacity = vertices.size() + 64;
|
||||
rs.vertexBuffer->allocate(
|
||||
gl::buffer::ARRAY_BUFFER,
|
||||
static_cast<int>(rs.bufferCapacity * sizeof(LineVertex)),
|
||||
GL_DYNAMIC_DRAW);
|
||||
rs.vao.reset();
|
||||
}
|
||||
|
||||
rs.vertexBuffer->upload(
|
||||
gl::buffer::ARRAY_BUFFER,
|
||||
vertices.data(), 0,
|
||||
static_cast<int>(vertices.size() * sizeof(LineVertex)));
|
||||
|
||||
if (!rs.vao) {
|
||||
rs.vao.emplace();
|
||||
constexpr int stride = sizeof(LineVertex);
|
||||
rs.vao->setup_attrib(*rs.vertexBuffer, 0, 3, GL_FLOAT, stride, 0); // position
|
||||
rs.vao->setup_attrib(*rs.vertexBuffer, 1, 3, GL_FLOAT, stride, 12); // color
|
||||
rs.vao->unbind();
|
||||
rs.vertexBuffer->unbind(gl::buffer::ARRAY_BUFFER);
|
||||
}
|
||||
|
||||
if (!rs.shader) {
|
||||
try {
|
||||
gl::shader vert("vbocolor.vert");
|
||||
gl::shader frag("ecs_line.frag");
|
||||
rs.shader = std::unique_ptr<gl::program>(new gl::program({vert, frag}));
|
||||
} catch (const gl::shader_exception& e) {
|
||||
WriteLog("[ECS] LineSystem: shader compile failed: " + std::string(e.what()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
rs.shader->bind();
|
||||
rs.vao->bind();
|
||||
glDrawArrays(GL_LINES, 0, static_cast<GLsizei>(vertices.size()));
|
||||
rs.vao->unbind();
|
||||
gl::program::unbind();
|
||||
}
|
||||
22
entitysystem/systems/LineSystem.h
Normal file
22
entitysystem/systems/LineSystem.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "BaseSystem.h"
|
||||
|
||||
// Renders Line components as GL_LINES using camera-relative world positions.
|
||||
// Uses vbocolor.vert + ecs_line.frag.
|
||||
class LineSystem : public BaseSystem
|
||||
{
|
||||
public:
|
||||
LineSystem();
|
||||
~LineSystem();
|
||||
void Render(ECWorld& world) override;
|
||||
|
||||
private:
|
||||
struct LineVertex {
|
||||
glm::vec3 position; // camera-relative world position
|
||||
glm::vec3 color;
|
||||
};
|
||||
|
||||
struct RenderState;
|
||||
std::unique_ptr<RenderState> m_renderState;
|
||||
};
|
||||
16
entitysystem/systems/MeshRenderSystem.cpp
Normal file
16
entitysystem/systems/MeshRenderSystem.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#include "stdafx.h"
|
||||
#include "MeshRenderSystem.h"
|
||||
|
||||
#include "entitysystem/ECWorld.h"
|
||||
#include "entitysystem/components/BasicComponents.h"
|
||||
#include "entitysystem/components/RenderComponents.h"
|
||||
|
||||
// gfx_renderer does not expose per-handle draw calls — geometry is drawn
|
||||
// by the legacy pipeline (simulation::Region → GfxRenderer->Render()).
|
||||
// MeshRenderer.visible is the authoritative visibility flag; LODSystem writes
|
||||
// it and the legacy scene node reads it when the renderer traverses the scene.
|
||||
// When a custom ECS draw path is wired into the renderer this system will
|
||||
// issue the actual draw calls.
|
||||
void MeshRenderSystem::Render(ECWorld& world)
|
||||
{
|
||||
}
|
||||
9
entitysystem/systems/MeshRenderSystem.h
Normal file
9
entitysystem/systems/MeshRenderSystem.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "BaseSystem.h"
|
||||
|
||||
class MeshRenderSystem : public BaseSystem
|
||||
{
|
||||
public:
|
||||
void Render(ECWorld& world) override;
|
||||
};
|
||||
@@ -13,6 +13,7 @@ void MovementSystem::Update(ECWorld& world, float dt)
|
||||
constexpr double damping = 0.90; // 0.0 = instant stop, 1.0 = without damping
|
||||
|
||||
world.Each<ECSComponent::Transform, ECSComponent::Velocity>(
|
||||
entt::exclude<ECSComponent::Disabled>,
|
||||
[dt](auto entity, auto& transform, auto& velocity)
|
||||
{
|
||||
|
||||
@@ -22,7 +23,7 @@ void MovementSystem::Update(ECWorld& world, float dt)
|
||||
|
||||
if (glm::length(velocity.Value) < 0.001)
|
||||
{
|
||||
velocity.Value = glm::dvec3(0.0);
|
||||
velocity.Value = glm::vec3(0.0f);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
196
entitysystem/systems/ParticlesSystem.cpp
Normal file
196
entitysystem/systems/ParticlesSystem.cpp
Normal file
@@ -0,0 +1,196 @@
|
||||
//
|
||||
// Created by Daniu
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "ParticlesSystem.h"
|
||||
|
||||
#include "entitysystem/components/BasicComponents.h"
|
||||
#include "entitysystem/components/RenderComponents.h"
|
||||
#include "utilities/Globals.h"
|
||||
|
||||
#include "gl/buffer.h"
|
||||
#include "gl/vao.h"
|
||||
#include "gl/shader.h"
|
||||
|
||||
#include "utilities/Logs.h"
|
||||
#include <algorithm>
|
||||
#include <random>
|
||||
|
||||
// ---- OpenGL resource bundle (pimpl) ----------------------------------------
|
||||
|
||||
struct ParticlesSystem::RenderState
|
||||
{
|
||||
std::optional<gl::buffer> instanceBuffer;
|
||||
std::optional<gl::vao> vao;
|
||||
std::unique_ptr<gl::program> shader;
|
||||
std::size_t bufferCapacity { 0 };
|
||||
};
|
||||
|
||||
ParticlesSystem::ParticlesSystem() = default;
|
||||
ParticlesSystem::~ParticlesSystem() = default;
|
||||
|
||||
// ---- Update ----------------------------------------------------------------
|
||||
|
||||
void ParticlesSystem::Update(ECWorld &world, float dt)
|
||||
{
|
||||
world.Each<ECSComponent::ParticleEmitter>(
|
||||
entt::exclude<ECSComponent::Disabled>,
|
||||
[&](entt::entity entity, ECSComponent::ParticleEmitter& emitter)
|
||||
{
|
||||
if (!emitter.isActive && emitter.particles.empty()) return;
|
||||
|
||||
for (size_t i = 0; i < emitter.particles.size(); )
|
||||
{
|
||||
auto& p = emitter.particles[i];
|
||||
p.age += dt;
|
||||
|
||||
if (p.age >= p.maxAge || p.color.a <= 0.0f) {
|
||||
p = emitter.particles.back();
|
||||
emitter.particles.pop_back();
|
||||
continue;
|
||||
}
|
||||
|
||||
p.velocity += emitter.gravity * dt;
|
||||
p.velocity *= (1.0f - emitter.airResistance * dt);
|
||||
|
||||
glm::vec3 nextPos = p.position + p.velocity * dt;
|
||||
if (emitter.hasCollision && nextPos.y < 0.0f) {
|
||||
p.velocity.y *= -emitter.bounceFactor;
|
||||
p.velocity.x *= 0.8f;
|
||||
nextPos.y = 0.01f;
|
||||
}
|
||||
p.position = nextPos;
|
||||
|
||||
p.size += emitter.sizeGrowth * dt;
|
||||
p.color += emitter.colorFade * dt;
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
if (emitter.isActive) {
|
||||
emitter.spawnAccumulator += dt;
|
||||
float spawnInterval = 1.0f / emitter.spawnRate;
|
||||
|
||||
while (emitter.spawnAccumulator >= spawnInterval) {
|
||||
if (emitter.particles.size() < emitter.maxParticles)
|
||||
SpawnParticle(emitter);
|
||||
emitter.spawnAccumulator -= spawnInterval;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ParticlesSystem::SpawnParticle(ECSComponent::ParticleEmitter& emitter)
|
||||
{
|
||||
static thread_local std::mt19937 gen(std::random_device{}());
|
||||
std::uniform_real_distribution<float> dist(0.0f, 1.0f);
|
||||
|
||||
ECSComponent::Particle p{};
|
||||
p.position = emitter.emitterLocation;
|
||||
p.velocity.x = emitter.minStartVelocity.x + dist(gen) * (emitter.maxStartVelocity.x - emitter.minStartVelocity.x);
|
||||
p.velocity.y = emitter.minStartVelocity.y + dist(gen) * (emitter.maxStartVelocity.y - emitter.minStartVelocity.y);
|
||||
p.velocity.z = emitter.minStartVelocity.z + dist(gen) * (emitter.maxStartVelocity.z - emitter.minStartVelocity.z);
|
||||
p.color = glm::vec4(1.0f);
|
||||
p.size = 0.1f;
|
||||
p.age = 0.0f;
|
||||
p.maxAge = emitter.particleLifetime;
|
||||
|
||||
emitter.particles.push_back(p);
|
||||
}
|
||||
|
||||
// ---- Render ----------------------------------------------------------------
|
||||
|
||||
void ParticlesSystem::Render(ECWorld& world)
|
||||
{
|
||||
// Collect instance data for every live particle across all emitters
|
||||
std::vector<GPUInstanceData> instances;
|
||||
instances.reserve(4096);
|
||||
|
||||
const glm::vec3 camPos{
|
||||
static_cast<float>(Global.pCamera.Pos.x),
|
||||
static_cast<float>(Global.pCamera.Pos.y),
|
||||
static_cast<float>(Global.pCamera.Pos.z)
|
||||
};
|
||||
|
||||
world.Each<ECSComponent::ParticleEmitter>(
|
||||
entt::exclude<ECSComponent::Disabled>,
|
||||
[&](entt::entity, ECSComponent::ParticleEmitter& emitter)
|
||||
{
|
||||
for (const auto& p : emitter.particles) {
|
||||
if (p.color.a <= 0.0f) continue;
|
||||
instances.push_back({
|
||||
p.position - camPos, // camera-relative world position
|
||||
std::max(p.size, 0.01f),
|
||||
p.color
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (instances.empty()) return;
|
||||
|
||||
// Lazy-init OpenGL resources
|
||||
if (!m_renderState)
|
||||
m_renderState = std::make_unique<RenderState>();
|
||||
|
||||
auto& rs = *m_renderState;
|
||||
|
||||
// Grow VBO if needed
|
||||
if (!rs.instanceBuffer)
|
||||
rs.instanceBuffer.emplace();
|
||||
|
||||
if (instances.size() > rs.bufferCapacity) {
|
||||
rs.bufferCapacity = instances.size() + 1024; // headroom
|
||||
rs.instanceBuffer->allocate(
|
||||
gl::buffer::ARRAY_BUFFER,
|
||||
static_cast<int>(rs.bufferCapacity * sizeof(GPUInstanceData)),
|
||||
GL_DYNAMIC_DRAW);
|
||||
// VAO needs rebuild after buffer reallocation
|
||||
rs.vao.reset();
|
||||
}
|
||||
|
||||
rs.instanceBuffer->upload(
|
||||
gl::buffer::ARRAY_BUFFER,
|
||||
instances.data(), 0,
|
||||
static_cast<int>(instances.size() * sizeof(GPUInstanceData)));
|
||||
|
||||
// Build VAO once (or after buffer realloc)
|
||||
if (!rs.vao) {
|
||||
rs.vao.emplace();
|
||||
constexpr int stride = sizeof(GPUInstanceData);
|
||||
rs.vao->setup_attrib(*rs.instanceBuffer, 0, 3, GL_FLOAT, stride, 0); // position
|
||||
glVertexAttribDivisor(0, 1);
|
||||
rs.vao->setup_attrib(*rs.instanceBuffer, 1, 1, GL_FLOAT, stride, 12); // size
|
||||
glVertexAttribDivisor(1, 1);
|
||||
rs.vao->setup_attrib(*rs.instanceBuffer, 2, 4, GL_FLOAT, stride, 16); // color
|
||||
glVertexAttribDivisor(2, 1);
|
||||
rs.vao->unbind();
|
||||
rs.instanceBuffer->unbind(gl::buffer::ARRAY_BUFFER);
|
||||
}
|
||||
|
||||
// Load shader once
|
||||
if (!rs.shader) {
|
||||
try {
|
||||
gl::shader vert("ecs_particle.vert");
|
||||
gl::shader frag("ecs_particle.frag");
|
||||
rs.shader = std::unique_ptr<gl::program>(new gl::program({vert, frag}));
|
||||
} catch (const gl::shader_exception& e) {
|
||||
WriteLog("[ECS] ParticlesSystem: shader compile failed: " + std::string(e.what()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Render
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glDepthMask(GL_FALSE);
|
||||
|
||||
rs.shader->bind();
|
||||
rs.vao->bind();
|
||||
glDrawArraysInstanced(GL_TRIANGLES, 0, 6, static_cast<GLsizei>(instances.size()));
|
||||
rs.vao->unbind();
|
||||
gl::program::unbind();
|
||||
|
||||
glDepthMask(GL_TRUE);
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
35
entitysystem/systems/ParticlesSystem.h
Normal file
35
entitysystem/systems/ParticlesSystem.h
Normal file
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// Created by Daniu
|
||||
//
|
||||
|
||||
#ifndef EU07_PARTICLESSYSTEM_H
|
||||
#define EU07_PARTICLESSYSTEM_H
|
||||
|
||||
#include "BaseSystem.h"
|
||||
#include "entitysystem/ECWorld.h"
|
||||
#include "entitysystem/components/RenderComponents.h"
|
||||
|
||||
class ParticlesSystem : public BaseSystem
|
||||
{
|
||||
public:
|
||||
ParticlesSystem();
|
||||
~ParticlesSystem();
|
||||
void Update(ECWorld& world, float dt) override;
|
||||
void Render(ECWorld& world) override;
|
||||
|
||||
private:
|
||||
void SpawnParticle(ECSComponent::ParticleEmitter& emitter);
|
||||
|
||||
// Per-instance data uploaded to GPU (one entry per live particle)
|
||||
struct GPUInstanceData {
|
||||
glm::vec3 position; // camera-relative world position
|
||||
float size;
|
||||
glm::vec4 color;
|
||||
};
|
||||
|
||||
// OpenGL resources — allocated lazily on first Render() call
|
||||
struct RenderState;
|
||||
std::unique_ptr<RenderState> m_renderState;
|
||||
};
|
||||
|
||||
#endif // EU07_PARTICLESSYSTEM_H
|
||||
26
entitysystem/systems/SoundSystem.cpp
Normal file
26
entitysystem/systems/SoundSystem.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#include "stdafx.h"
|
||||
#include "SoundSystem.h"
|
||||
|
||||
#include "entitysystem/ECWorld.h"
|
||||
#include "entitysystem/components/BasicComponents.h"
|
||||
|
||||
void SoundSystem::Update(ECWorld& world, float dt)
|
||||
{
|
||||
world.Each<ECSComponent::SoundComponent, ECSComponent::Transform>(
|
||||
entt::exclude<ECSComponent::Disabled>,
|
||||
[](entt::entity,
|
||||
ECSComponent::SoundComponent& sc,
|
||||
ECSComponent::Transform& transform)
|
||||
{
|
||||
sc.sound.offset( glm::vec3( transform.Position ) );
|
||||
sc.sound.gain( sc.volume );
|
||||
sc.sound.pitch( sc.pitch );
|
||||
|
||||
if (sc.isPlaying && !sc.sound.is_playing()) {
|
||||
int flags = sc.loop ? sound_flags::looping : 0;
|
||||
sc.sound.play( flags );
|
||||
} else if (!sc.isPlaying && sc.sound.is_playing()) {
|
||||
sc.sound.stop();
|
||||
}
|
||||
});
|
||||
}
|
||||
11
entitysystem/systems/SoundSystem.h
Normal file
11
entitysystem/systems/SoundSystem.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "BaseSystem.h"
|
||||
|
||||
// Drives SoundComponent: starts/stops sound_source playback and keeps
|
||||
// position, volume and pitch in sync with the entity's Transform.
|
||||
class SoundSystem : public BaseSystem
|
||||
{
|
||||
public:
|
||||
void Update(ECWorld& world, float dt) override;
|
||||
};
|
||||
@@ -27,7 +27,12 @@ void SystemManager::Destroy(ECWorld& world)
|
||||
|
||||
void SystemManager::Update(ECWorld& world, float dt)
|
||||
{
|
||||
|
||||
for (auto& system : m_systems)
|
||||
system->Update(world, dt);
|
||||
}
|
||||
|
||||
void SystemManager::Render(ECWorld& world)
|
||||
{
|
||||
for (auto& system : m_systems)
|
||||
system->Render(world);
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ public:
|
||||
void Destroy(ECWorld& world);
|
||||
|
||||
void Update(ECWorld& world, float dt);
|
||||
void Render(ECWorld& world);
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<BaseSystem>> m_systems;
|
||||
|
||||
30
entitysystem/systems/VehicleSyncSystem.cpp
Normal file
30
entitysystem/systems/VehicleSyncSystem.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#include "stdafx.h"
|
||||
#include "VehicleSyncSystem.h"
|
||||
|
||||
#include "entitysystem/ECWorld.h"
|
||||
#include "entitysystem/components/BasicComponents.h"
|
||||
#include "vehicle/DynObj.h"
|
||||
|
||||
void VehicleSyncSystem::Update(ECWorld& world, float dt)
|
||||
{
|
||||
world.Each<ECSComponent::VehicleRef, ECSComponent::Transform>(
|
||||
entt::exclude<ECSComponent::Disabled>,
|
||||
[](entt::entity,
|
||||
ECSComponent::VehicleRef& ref,
|
||||
ECSComponent::Transform& transform)
|
||||
{
|
||||
if (!ref.vehicle) return;
|
||||
|
||||
auto const &pos = ref.vehicle->GetPosition();
|
||||
transform.Position = glm::dvec3(pos.x, pos.y, pos.z);
|
||||
|
||||
// Extract rotation from the vehicle's render matrix (column vectors = basis)
|
||||
auto const *m = ref.vehicle->mMatrix.getArray();
|
||||
// mMatrix is column-major 4x4; extract upper-left 3x3 into glm
|
||||
glm::mat3 rot(
|
||||
m[0], m[1], m[2],
|
||||
m[4], m[5], m[6],
|
||||
m[8], m[9], m[10]);
|
||||
transform.Rotation = glm::quat_cast(rot);
|
||||
});
|
||||
}
|
||||
11
entitysystem/systems/VehicleSyncSystem.h
Normal file
11
entitysystem/systems/VehicleSyncSystem.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "BaseSystem.h"
|
||||
|
||||
// Keeps ECS Transform in sync with the vehicle's actual world position each frame.
|
||||
// Must run before all other systems that read Transform (e.g. LOD, Sound, Hierarchy).
|
||||
class VehicleSyncSystem : public BaseSystem
|
||||
{
|
||||
public:
|
||||
void Update(ECWorld& world, float dt) override;
|
||||
};
|
||||
Reference in New Issue
Block a user