From 8152429acd4aae506799c97bb934376be471918b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= <49535803+damacaa@users.noreply.github.com> Date: Sat, 8 Aug 2026 20:48:13 +0200 Subject: [PATCH 01/21] core/assert: introduce WEIRD_ASSERT runtime diagnostics system --- CMakeLists.txt | 15 +++++++++++++++ include/weird-engine/Assert.h | 31 +++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 include/weird-engine/Assert.h diff --git a/CMakeLists.txt b/CMakeLists.txt index fdd20ec..a59fc5c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,6 +39,20 @@ if(WEIRD_TEST_HOOKS) add_compile_definitions(WEIRD_TEST_HOOKS) endif() +# Runtime assertions: enabled by default in debug-like configurations +# (Debug, RelWithDebInfo), disabled in Release/MinSizeRel. Games that build +# the engine via add_subdirectory inherit this through the PUBLIC definition. +set(WEIRD_ENGINE_ENABLE_ASSERTS_DEFAULT OFF) +if(CMAKE_BUILD_TYPE MATCHES "^(Debug|RelWithDebInfo)$" + OR CMAKE_CONFIGURATION_TYPES MATCHES "(^|;)Debug(;|$)" + OR CMAKE_CONFIGURATION_TYPES MATCHES "(^|;)RelWithDebInfo(;|$)") + set(WEIRD_ENGINE_ENABLE_ASSERTS_DEFAULT ON) +endif() +option(WEIRD_ENGINE_ENABLE_ASSERTS "Enable runtime assertions (WEIRD_ASSERT)" ${WEIRD_ENGINE_ENABLE_ASSERTS_DEFAULT}) +if(WEIRD_ENGINE_ENABLE_ASSERTS) + target_compile_definitions(${PROJECT_NAME} PUBLIC WEIRD_ENABLE_ASSERTS=1) +endif() + if(NOT WEIRD_DISABLE_IMGUI) # ImGui setup set(IMGUI_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third-party/imgui) @@ -222,6 +236,7 @@ if(WEIRD_ENGINE_BUILD_EXAMPLES) add_subdirectory(examples/3d-experiments) add_subdirectory(examples/opengl-experiments) add_subdirectory(examples/sample-scenes) + add_subdirectory(examples/empty-project) endif() # Tools diff --git a/include/weird-engine/Assert.h b/include/weird-engine/Assert.h new file mode 100644 index 0000000..d60ed1b --- /dev/null +++ b/include/weird-engine/Assert.h @@ -0,0 +1,31 @@ +#pragma once + +// Weird Engine runtime assertions. +// +// Enabled when the WEIRD_ENABLE_ASSERTS compile definition is set (done by the +// engine's CMake for Debug and RelWithDebInfo builds by default). These remain +// active in RelWithDebInfo even though CMake defines NDEBUG there, which is +// why this is not a wrapper around the standard assert(). +// +// When disabled, the macro compiles away entirely and imposes zero overhead. +#if defined(WEIRD_ENABLE_ASSERTS) + +#include +#include + +#define WEIRD_ASSERT(condition, message) \ + do \ + { \ + if (!(condition)) \ + { \ + std::fprintf(stderr, "WEIRD ASSERT FAILED: %s\n %s\n at %s:%d\n", #condition, message, __FILE__, \ + __LINE__); \ + std::abort(); \ + } \ + } while (false) + +#else + +#define WEIRD_ASSERT(condition, message) ((void)0) + +#endif From 9408a8410e103aeb9108fa189d91cd24ed698139 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= <49535803+damacaa@users.noreply.github.com> Date: Sat, 8 Aug 2026 20:48:17 +0200 Subject: [PATCH 02/21] physics: enforce thread execution boundaries and batch snapshot readback --- .../weird-engine/systems/PhysicsSystem2D.h | 22 ++++- include/weird-physics/PhysicsSettings.h | 1 + include/weird-physics/Simulation2D.h | 37 +++++++- src/weird-physics/Simulation2D.cpp | 91 +++++++++++++++++-- .../molecule-editor/include/MoleculeEditor.h | 1 - 5 files changed, 135 insertions(+), 17 deletions(-) diff --git a/include/weird-engine/systems/PhysicsSystem2D.h b/include/weird-engine/systems/PhysicsSystem2D.h index 7f373bd..a589fcc 100644 --- a/include/weird-engine/systems/PhysicsSystem2D.h +++ b/include/weird-engine/systems/PhysicsSystem2D.h @@ -23,6 +23,8 @@ namespace WeirdEngine inline void update(ECSManager& ecs, Simulation2D& simulation) { + // Pass 1: ECS -> physics. Writes are queued as commands and the + // physics thread applies them on its next step. ecs.forEach( [&](Entity entity, RigidBody2D& rb, Transform& transform) { @@ -56,8 +58,6 @@ namespace WeirdEngine simulation.setContinuousForce(rb.simulationId, rb.pendingContinuousForce); rb.pendingContinuousForce = glm::vec2(0.0f); } - - simulation.updateTransform(transform, rb.simulationId); }); ecs.forEach( @@ -122,6 +122,24 @@ namespace WeirdEngine // initialization commands (position, velocity, mass, etc.) // have been queued ahead of this in m_pendingCommands. simulation.activatePendingBodies(); + + // Pass 2: physics -> ECS readback. The published buffers are + // copied into a reusable snapshot under a short lock; the ECS + // iteration then runs without holding the simulation mutex, so + // the physics thread can keep stepping meanwhile. + static Simulation2D::ReadBufferSnapshot readSnapshot; + + simulation.copyReadBuffers(readSnapshot); + + ecs.forEach( + [&](Entity entity, RigidBody2D& rb, Transform& transform) + { + vec2 position = readSnapshot.positions[rb.simulationId]; + transform.position.x = position.x; + transform.position.y = position.y; + + rb.velocity = readSnapshot.velocities[rb.simulationId]; + }); } } // namespace PhysicsSystem2D } // namespace ECS diff --git a/include/weird-physics/PhysicsSettings.h b/include/weird-physics/PhysicsSettings.h index 7a0ff2f..4f1a47d 100644 --- a/include/weird-physics/PhysicsSettings.h +++ b/include/weird-physics/PhysicsSettings.h @@ -8,5 +8,6 @@ namespace WeirdEngine float damping = 0.001f; float simulationFrequency = 100.0f; int relaxationSteps = 10; + bool runSimulationInThread = true; }; } // namespace WeirdEngine diff --git a/include/weird-physics/Simulation2D.h b/include/weird-physics/Simulation2D.h index 22ba11c..87d16fb 100644 --- a/include/weird-physics/Simulation2D.h +++ b/include/weird-physics/Simulation2D.h @@ -151,14 +151,41 @@ namespace WeirdEngine return m_stats; } - // Retrieve results + // Retrieve published results. Safe from any thread, including physics + // callbacks (they just pay a per-call lock). For reading many bodies + // at once on the main thread, copy them into a ReadBufferSnapshot via + // copyReadBuffers() instead. vec2 getPosition(SimulationID id); void setPosition(SimulationID id, vec2 pos); vec2 getVelocity(SimulationID id); void setVelocity(SimulationID id, vec2 vel); - void updateTransform(Transform& transform, SimulationID id); void setMass(SimulationID id, float mass); + // Published physics state copied out under a brief lock. Fill it once + // per frame with copyReadBuffers(), then iterate the ECS without + // holding the simulation mutex. + struct ReadBufferSnapshot + { + std::vector positions; + std::vector velocities; + }; + + // Copies the published positions/velocities into the snapshot under a + // short lock. The snapshot's buffers grow as needed but keep their + // capacity across calls. Main thread only; must NOT be called from + // physics execution (WEIRD_ASSERT enforces this in debug builds). + void copyReadBuffers(ReadBufferSnapshot& snapshot); + + // Current working physics state. PHYSICS EXECUTION ONLY: call these + // from onPhysicsStep/onCollision/onShapeCollision callbacks, never + // from the main thread (WEIRD_ASSERT enforces this in debug builds). + vec2 getPhysicsPosition(SimulationID id) const; + vec2 getPhysicsVelocity(SimulationID id) const; + + // True while inside a physics step (physics thread in threaded mode, + // main thread in single-threaded mode). + static bool isPhysicsExecutionContext(); + void setSDFs(std::vector>& sdfs); std::shared_ptr getSpatialGridSnapshot() @@ -335,9 +362,9 @@ namespace WeirdEngine float m_fixedDeltaTimeF; int m_relaxationSteps; - bool m_isPaused; - bool m_simulating; - double m_simulationDelay; + std::atomic m_isPaused{false}; + std::atomic m_simulating{false}; + std::atomic m_simulationDelay{0.0}; std::atomic m_simulationTime{0.0}; bool m_useSimdOperations; diff --git a/src/weird-physics/Simulation2D.cpp b/src/weird-physics/Simulation2D.cpp index 52619a2..14d7043 100644 --- a/src/weird-physics/Simulation2D.cpp +++ b/src/weird-physics/Simulation2D.cpp @@ -3,11 +3,38 @@ #include #include "glm/gtx/norm.hpp" +#include "weird-engine/Assert.h" #include "weird-engine/Logger.h" namespace WeirdEngine { + namespace + { + // True on whatever thread currently runs physics steps. Lets + // Simulation2D::isPhysicsExecutionContext() and the getPhysics*() + // assertions work in both threaded and single-threaded simulation. + thread_local bool g_inPhysicsExecution = false; + + class PhysicsExecutionScope + { + public: + PhysicsExecutionScope() + : m_previous(g_inPhysicsExecution) + { + g_inPhysicsExecution = true; + } + + ~PhysicsExecutionScope() + { + g_inPhysicsExecution = m_previous; + } + + private: + bool m_previous; + }; + } // namespace + #define MEASURE_PERFORMANCE false #define INTEGRATION_METHOD 1 @@ -17,8 +44,7 @@ namespace WeirdEngine const float EPSILON = 0.0001f; Simulation2D::Simulation2D(size_t size, const PhysicsSettings& settings) - : m_isPaused(false) - , m_positions(new vec2[size]) + : m_positions(new vec2[size]) , m_positionsRead(new vec2[size]) , m_positionsAux(new vec2[size]) , m_previousPositions(new vec2[size]) @@ -35,8 +61,6 @@ namespace WeirdEngine , m_maxSize(size) , m_size(0) , m_allocated(0) - , m_simulationDelay(0) - , m_simulationTime(0) , m_substeps(1) , m_simulationFrequency(settings.simulationFrequency) , m_fixedDeltaTime(1.0 / static_cast(settings.simulationFrequency)) @@ -45,7 +69,6 @@ namespace WeirdEngine , m_gravity(settings.gravity) , m_push(10.0f * settings.simulationFrequency) , m_damping(settings.damping) - , m_simulating(false) , m_collisionDetectionMethod(MethodNaive) , m_useSimdOperations(false) , m_diameter(1.0f) @@ -122,6 +145,8 @@ namespace WeirdEngine void Simulation2D::process() { + PhysicsExecutionScope physicsExecution; + int steps = 0; while (m_simulationDelay >= m_fixedDeltaTime && steps < MAX_STEPS) @@ -954,6 +979,7 @@ namespace WeirdEngine SimulationID Simulation2D::generateSimulationID() { std::lock_guard lock(m_structuralMutex); + std::lock_guard readLock(m_readMutex); SimulationID id = static_cast(m_allocated); @@ -989,7 +1015,7 @@ namespace WeirdEngine void Simulation2D::removeObject(SimulationID id) { - std::scoped_lock lock(m_structuralMutex, m_externalForcesMutex, m_fixMutex); + std::scoped_lock lock(m_structuralMutex, m_externalForcesMutex, m_fixMutex, m_readMutex); if (m_size == 0 || id >= m_size) { @@ -1221,6 +1247,9 @@ namespace WeirdEngine return std::find(m_fixedObjects.begin(), m_fixedObjects.end(), id) != m_fixedObjects.end(); } + // Safe from any thread (main thread, or physics callbacks while the + // physics thread runs). Prefer copyReadBuffers() when reading many + // bodies at once on the main thread. vec2 Simulation2D::getPosition(SimulationID entity) { std::lock_guard lock(m_readMutex); @@ -1243,8 +1272,12 @@ namespace WeirdEngine } } + // Safe from any thread (main thread, or physics callbacks while the + // physics thread runs). Prefer copyReadBuffers() when reading many + // bodies at once on the main thread. vec2 Simulation2D::getVelocity(SimulationID id) { + std::lock_guard lock(m_readMutex); return m_velocitiesRead[id]; } @@ -1264,11 +1297,51 @@ namespace WeirdEngine } } - void Simulation2D::updateTransform(Transform& transform, SimulationID id) + void Simulation2D::copyReadBuffers(ReadBufferSnapshot& snapshot) { + // Reading the published buffers while physics is executing would race + // against the read-buffer swap that happens at the end of every step. + WEIRD_ASSERT(!isPhysicsExecutionContext(), "copyReadBuffers() may not be called from physics execution " + "(onPhysicsStep/onCollision/onShapeCollision). " + "Use simulation.getPhysicsPosition()/getPhysicsVelocity() instead."); + std::lock_guard lock(m_readMutex); - transform.position.x = m_positionsRead[id].x; - transform.position.y = m_positionsRead[id].y; + + // Copy every allocated slot, not just the active bodies: bodies + // created this frame have ids in [m_size, m_allocated) until their + // ActivatePending command is processed, and the readback must be able + // to look up those ids. + size_t count = m_allocated; + if (snapshot.positions.size() < count) + snapshot.positions.resize(count); + if (snapshot.velocities.size() < count) + snapshot.velocities.resize(count); + + std::copy(m_positionsRead, m_positionsRead + count, snapshot.positions.begin()); + std::copy(m_velocitiesRead, m_velocitiesRead + count, snapshot.velocities.begin()); + } + + bool Simulation2D::isPhysicsExecutionContext() + { + return g_inPhysicsExecution; + } + + vec2 Simulation2D::getPhysicsPosition(SimulationID id) const + { + WEIRD_ASSERT(isPhysicsExecutionContext(), "getPhysicsPosition() may only be called from physics execution " + "(onPhysicsStep/onCollision/onShapeCollision callbacks). " + "Use simulation.getPosition() from the main thread instead."); + + return m_positions[id]; + } + + vec2 Simulation2D::getPhysicsVelocity(SimulationID id) const + { + WEIRD_ASSERT(isPhysicsExecutionContext(), "getPhysicsVelocity() may only be called from physics execution " + "(onPhysicsStep/onCollision/onShapeCollision callbacks). " + "Use simulation.getVelocity() from the main thread instead."); + + return m_velocities[id]; } void Simulation2D::setMass(SimulationID id, float mass) diff --git a/tools/molecule-editor/include/MoleculeEditor.h b/tools/molecule-editor/include/MoleculeEditor.h index c170cc9..06114f2 100644 --- a/tools/molecule-editor/include/MoleculeEditor.h +++ b/tools/molecule-editor/include/MoleculeEditor.h @@ -1162,6 +1162,5 @@ class MoleculeEditor : public Scene2D void onEntityShapeCollision(ECSManager& ecs, WeirdEngine::EntityShapeCollisionEvent& event) override { m_tempEcs = &ecs; - event.raw.friction *= 100.0f; } }; From ff7ce79a343671d585703568ce49e40ea1d319e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= <49535803+damacaa@users.noreply.github.com> Date: Sat, 8 Aug 2026 20:48:20 +0200 Subject: [PATCH 03/21] scene: add onDestroy lifecycle hook and fix SceneManager index state --- AGENTS.md | 1 + examples/3d-experiments/include/Classic.h | 2 +- examples/3d-experiments/include/CornellBox.h | 20 ++++++------- .../3d-experiments/include/MaterialShowcase.h | 18 +++++------ examples/empty-project/src/main.cpp | 27 ++++++++--------- examples/opengl-experiments/include/Fire.h | 6 ++-- examples/opengl-experiments/include/Lines.h | 2 +- examples/opengl-experiments/include/Water.h | 4 +-- .../sample-scenes/include/AquariumScene.h | 2 -- examples/sample-scenes/include/DestroyScene.h | 2 -- include/weird-engine.h | 5 ++-- include/weird-engine/Scene.h | 24 +++++++++++---- include/weird-engine/SceneManager.h | 2 -- include/weird-engine/math/MathExpressions.h | 7 +++-- src/weird-engine/Scene.cpp | 4 +-- src/weird-engine/SceneManager.cpp | 30 +++++++++++++++---- src/weird-renderer/core/Renderer.cpp | 2 +- 17 files changed, 92 insertions(+), 66 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 4b6e9cb..3688777 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -27,6 +27,7 @@ There are no tests. CI runs `ctest` but no test targets are defined. | `WEIRD_DISABLE_IMGUI` | `OFF` | Strip ImGui (used for muOS build) | | `WEIRD_TEST_HOOKS` | `OFF` | Enables `WEIRD_AUTO_QUIT_SECONDS` / `WEIRD_SCREENSHOT_FRAME` env vars | | `WEIRD_ENGINE_ENABLE_ASAN` | `OFF` | AddressSanitizer | +| `WEIRD_ENGINE_ENABLE_ASSERTS` | Debug/RelWithDebInfo | Enables `WEIRD_ASSERT` runtime assertions (abort on violation); disabled in Release | | `WEIRD_USE_FBDEV_EGL` | `OFF` | fbdev EGL backend for Mali devices (no GBM/KMS) | | `WEIRD_ENGINE_USE_RUNTIME_ASSETS` | `OFF` | Load shaders/fonts from `./shaders/` `./fonts/` instead of source tree | diff --git a/examples/3d-experiments/include/Classic.h b/examples/3d-experiments/include/Classic.h index 27eb98a..effc589 100644 --- a/examples/3d-experiments/include/Classic.h +++ b/examples/3d-experiments/include/Classic.h @@ -64,7 +64,7 @@ class ClassicScene : public Scene3D Entity start = addShape(DefaultShapes3D::PLANE, vars1, floorMaterial, CombinationType::Addition, false); } - getLigths().push_back(Light{0, glm::vec3(0.0f, 3.0f, 0.0f), 0, glm::vec3(0.35f, 0.45f, 0.5f), + getLights().push_back(Light{0, glm::vec3(0.0f, 3.0f, 0.0f), 0, glm::vec3(0.35f, 0.45f, 0.5f), glm::vec4(1.0f, 0.95f, 0.9f, 2.0f)}); ecs.getComponent(m_mainCamera).position = vec3(0, 2, 10); diff --git a/examples/3d-experiments/include/CornellBox.h b/examples/3d-experiments/include/CornellBox.h index 064fde3..0d773bc 100644 --- a/examples/3d-experiments/include/CornellBox.h +++ b/examples/3d-experiments/include/CornellBox.h @@ -100,16 +100,16 @@ class CornellBox : public Scene3D } // Sun - getLigths().push_back(Light{0, glm::vec3(0.0f, 0.0f, 0.0f), 0, normalize(glm::vec3(0.0f, 0.0f, 0.0f)), + getLights().push_back(Light{0, glm::vec3(0.0f, 0.0f, 0.0f), 0, normalize(glm::vec3(0.0f, 0.0f, 0.0f)), glm::vec4(1.0f, 1.0f, 1.0f, 0.0f)}); - getLigths().push_back(Light{1, glm::vec3(0.0f, (2.0f * 2.6f) + 0.25f, 0.0f), 0, glm::vec3(0.0f, 0.0f, 0.0f), + getLights().push_back(Light{1, glm::vec3(0.0f, (2.0f * 2.6f) + 0.25f, 0.0f), 0, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec4(1.0f, 1.0f, 1.0f, 3.0f)}); - // getLigths().push_back( + // getLights().push_back( // Light{1, glm::vec3(0.0f, 0.0f, 0.0f), 0, glm::vec3(0.35f, 0.45f, 0.5f), glm::vec4(0.0f, 1.0f, 0.0f, 1.0f)}); - // getLigths().push_back( + // getLights().push_back( // Light{2, glm::vec3(0.0f, 0.0f, 0.0f), 0, glm::vec3(0.0f, 1.0f, 0.0f), glm::vec4(0.0f, 0.0f, 2.0f, 10.0f)}); ecs.getComponent(m_mainCamera).position = vec3(0, 2.6f, 12.0f); @@ -124,12 +124,12 @@ class CornellBox : public Scene3D auto& cameraTransform = ecs.getComponent(m_mainCamera); - // getLigths()[0].position.x = cameraTransform.position.x; - // getLigths()[0].position.y = cameraTransform.position.y; - // getLigths()[0].position.z = cameraTransform.position.z; + // getLights()[0].position.x = cameraTransform.position.x; + // getLights()[0].position.y = cameraTransform.position.y; + // getLights()[0].position.z = cameraTransform.position.z; - // getLigths()[0].rotation.x = -cameraTransform.rotation.x; - // getLigths()[0].rotation.y = -cameraTransform.rotation.y; - // getLigths()[0].rotation.z = -cameraTransform.rotation.z; + // getLights()[0].rotation.x = -cameraTransform.rotation.x; + // getLights()[0].rotation.y = -cameraTransform.rotation.y; + // getLights()[0].rotation.z = -cameraTransform.rotation.z; } }; diff --git a/examples/3d-experiments/include/MaterialShowcase.h b/examples/3d-experiments/include/MaterialShowcase.h index fbc7d88..328c83d 100644 --- a/examples/3d-experiments/include/MaterialShowcase.h +++ b/examples/3d-experiments/include/MaterialShowcase.h @@ -155,13 +155,13 @@ class MaterialShowcaseScene : public Scene3D Entity start = addShape(boxId, vars1, mirrorMaterial, CombinationType::Addition, false); } - getLigths().push_back(Light{0, glm::vec3(0.0f, 0.0f, 0.0f), 0, normalize(glm::vec3(0.0f, 0.4f, 1.0f)), + getLights().push_back(Light{0, glm::vec3(0.0f, 0.0f, 0.0f), 0, normalize(glm::vec3(0.0f, 0.4f, 1.0f)), glm::vec4(1.0f, 1.0f, 1.0f, 0.5f)}); - // getLigths().push_back( + // getLights().push_back( // Light{1, glm::vec3(0.0f, 0.0f, 0.0f), 0, glm::vec3(0.35f, 0.45f, 0.5f), glm::vec4(0.0f, 1.0f, 0.0f, 1.0f)}); - // getLigths().push_back( + // getLights().push_back( // Light{2, glm::vec3(0.0f, 0.0f, 0.0f), 0, glm::vec3(0.0f, 1.0f, 0.0f), glm::vec4(0.0f, 0.0f, 2.0f, 10.0f)}); auto& cameraTransform = ecs.getComponent(m_mainCamera); @@ -178,12 +178,12 @@ class MaterialShowcaseScene : public Scene3D auto& cameraTransform = ecs.getComponent(m_mainCamera); - // getLigths()[0].position.x = cameraTransform.position.x; - // getLigths()[0].position.y = cameraTransform.position.y; - // getLigths()[0].position.z = cameraTransform.position.z; + // getLights()[0].position.x = cameraTransform.position.x; + // getLights()[0].position.y = cameraTransform.position.y; + // getLights()[0].position.z = cameraTransform.position.z; - // getLigths()[0].rotation.x = -cameraTransform.rotation.x; - // getLigths()[0].rotation.y = -cameraTransform.rotation.y; - // getLigths()[0].rotation.z = -cameraTransform.rotation.z; + // getLights()[0].rotation.x = -cameraTransform.rotation.x; + // getLights()[0].rotation.y = -cameraTransform.rotation.y; + // getLights()[0].rotation.z = -cameraTransform.rotation.z; } }; diff --git a/examples/empty-project/src/main.cpp b/examples/empty-project/src/main.cpp index e76cab1..50e49f0 100644 --- a/examples/empty-project/src/main.cpp +++ b/examples/empty-project/src/main.cpp @@ -1,26 +1,23 @@ -#include // Only needed for debug output, can remove if unused #include // Main engine include using namespace WeirdEngine; -// Example scene demonstrating how to create a rope of connected circles using springs. +// Starter template for a new game. Register scenes in main() and the engine +// takes care of the rest. Override the callbacks you need: +// onCreate - once, before the physics thread starts +// onStart(ecs[, tags])- after the ECS is ready and the physics thread runs +// onUpdate(dt, ecs) - game logic, once per frame (pure virtual) +// onRender(target) - extra 3D rendering +// onImGuiRender - debug UI +// onCollision / onShapeCollision - physics thread, no ECS access +// onEntityCollision / onEntityShapeCollision - main thread, ECS safe +// onDestroy - before the scene is replaced during a transition class EmptyScene : public Scene2D { -public: - EmptyScene() - : Scene() - { - } - private: - void onStart() override {} + void onStart(ECSManager& ecs) override {} - void onUpdate(float delta) override {} - void onCreate() override {} - void onRender(WeirdRenderer::RenderTarget& renderTarget) override {} - void onCollision(WeirdEngine::CollisionEvent& event) override {} - void onShapeCollision(WeirdEngine::ShapeCollisionEvent& event) override {} - void onDestroy() override {} + void onUpdate(float delta, ECSManager& ecs) override {} }; int main(int argc, char* argv[]) diff --git a/examples/opengl-experiments/include/Fire.h b/examples/opengl-experiments/include/Fire.h index 96dc280..be84bd7 100644 --- a/examples/opengl-experiments/include/Fire.h +++ b/examples/opengl-experiments/include/Fire.h @@ -58,8 +58,8 @@ class FireScene : public Scene3D m_heatDistortionShader = Shader(SHADERS_PATH "3d/geometry.vert", ASSETS_PATH "fire/shaders/heatDistortion.frag"); - getLigths().push_back(Light{0, glm::vec3(0.0f), 0, glm::vec3(0.0f), glm::vec4(0.0f)}); - getLigths().push_back( + getLights().push_back(Light{0, glm::vec3(0.0f), 0, glm::vec3(0.0f), glm::vec4(0.0f)}); + getLights().push_back( Light{1, glm::vec3(0.0f, 1.0f, 0.0f), 0, glm::vec3(0.0f), glm::vec4(1.0f, 0.95f, 0.9f, 2.0f)}); // Load meshes @@ -321,7 +321,7 @@ class FireScene : public Scene3D m_litShader.setUniform("u_far", sceneCamera.farPlane); // Pass light rotation - auto& lights = getLigths(); + auto& lights = getLights(); glm::vec3 position = lights[1].position; m_litShader.setUniform("u_lightPos", position); glm::vec3 direction = lights[1].rotation; diff --git a/examples/opengl-experiments/include/Lines.h b/examples/opengl-experiments/include/Lines.h index b4ce3e7..2557a16 100644 --- a/examples/opengl-experiments/include/Lines.h +++ b/examples/opengl-experiments/include/Lines.h @@ -54,7 +54,7 @@ class LinesScene : public Scene3D void onStart(ECSManager& ecs) override { m_debugFly = false; - getLigths().push_back(Light{}); + getLights().push_back(Light{}); { Entity entity = ecs.createEntity(); diff --git a/examples/opengl-experiments/include/Water.h b/examples/opengl-experiments/include/Water.h index 781cfdd..b1b3e71 100644 --- a/examples/opengl-experiments/include/Water.h +++ b/examples/opengl-experiments/include/Water.h @@ -49,7 +49,7 @@ class WaterScene : public Scene3D m_waterShader = Shader(ASSETS_PATH "water/shaders/water.vert", ASSETS_PATH "water/shaders/water.frag"); - getLigths().push_back(Light{0, glm::vec3(0.0f, 0.0f, 0.0f), 0, normalize(glm::vec3(0.0f, 0.4f, 1.0f)), + getLights().push_back(Light{0, glm::vec3(0.0f, 0.0f, 0.0f), 0, normalize(glm::vec3(0.0f, 0.4f, 1.0f)), glm::vec4(1.0f, 1.0f, 1.0f, 0.5f)}); m_waterPlane.build(); @@ -147,7 +147,7 @@ class WaterScene : public Scene3D WeirdRenderer::Camera& sceneCamera = getCamera(); float time = getTime(); - auto& lights = getLigths(); + auto& lights = getLights(); // ── Snapshot the current scene colour + depth ──────────────────────── // We need to read from these textures while drawing the water plane, diff --git a/examples/sample-scenes/include/AquariumScene.h b/examples/sample-scenes/include/AquariumScene.h index aaf604e..c5a3805 100644 --- a/examples/sample-scenes/include/AquariumScene.h +++ b/examples/sample-scenes/include/AquariumScene.h @@ -673,8 +673,6 @@ class AquariumScene : public Scene2D void onEntityShapeCollision(ECSManager& ecs, WeirdEngine::EntityShapeCollisionEvent& event) override { - event.raw.friction *= 50.0f; - if (std::rand() % 8 == 0) { playSound({0.015f, 150.0f + (std::rand() % 150), true, vec3(event.raw.position, 0.0f), 1}); diff --git a/examples/sample-scenes/include/DestroyScene.h b/examples/sample-scenes/include/DestroyScene.h index 731b274..d794572 100644 --- a/examples/sample-scenes/include/DestroyScene.h +++ b/examples/sample-scenes/include/DestroyScene.h @@ -171,8 +171,6 @@ class DestroyScene : public Scene2D void onEntityShapeCollision(ECSManager& ecs, WeirdEngine::EntityShapeCollisionEvent& event) override { - event.raw.friction *= 100.0f; - if (std::rand() % 20 == 0) { Entity e = event.entity; diff --git a/include/weird-engine.h b/include/weird-engine.h index 0bcc52c..a107f23 100644 --- a/include/weird-engine.h +++ b/include/weird-engine.h @@ -248,8 +248,9 @@ namespace WeirdEngine #endif } // namespace Detail - void start(SceneManager& sceneManager, DisplaySettings displaySettings = {}, PhysicsSettings physicsSettings = {}, - AudioSettings audioSettings = {}, int argc = 0, char** argv = nullptr) + inline void start(SceneManager& sceneManager, DisplaySettings displaySettings = {}, + PhysicsSettings physicsSettings = {}, AudioSettings audioSettings = {}, int argc = 0, + char** argv = nullptr) { WeirdEngine::Logger::log("Starting Weird Engine..."); diff --git a/include/weird-engine/Scene.h b/include/weird-engine/Scene.h index 49d28af..2b76a44 100644 --- a/include/weird-engine/Scene.h +++ b/include/weird-engine/Scene.h @@ -25,14 +25,18 @@ namespace WeirdEngine struct EntityCollisionEvent { - CollisionEvent& raw; + // Raw event data from the physics thread. Read-only: the physics + // response has already been applied by the time this is dispatched. + const CollisionEvent& raw; Entity entityA; Entity entityB; }; struct EntityShapeCollisionEvent { - ShapeCollisionEvent& raw; + // Raw event data from the physics thread. Read-only: the physics + // response has already been applied by the time this is dispatched. + const ShapeCollisionEvent& raw; Entity entity; }; @@ -66,7 +70,7 @@ namespace WeirdEngine void getUIData(vec4*& uiData, uint32_t& size, uint32_t& customShapeCount); WeirdRenderer::Camera& getCamera(); - std::vector& getLigths(); + std::vector& getLights(); Simulation2D& getSimulation2D() { @@ -138,6 +142,11 @@ namespace WeirdEngine RaymarchResult raymarch(glm::vec2 origin, glm::vec2 direction, float epsilon = 0.001f, float maxDistance = 150.0f); + // Called by SceneManager right before this scene is destroyed during + // a scene transition. Runs on the main thread; the physics thread may + // still be stepping, so keep the same thread rules as the callbacks below. + virtual void onDestroy() {}; + void renderImGui(); void renderPhysicsStatsUI(); @@ -152,15 +161,18 @@ namespace WeirdEngine virtual void onRender(WeirdRenderer::RenderTarget& renderTarget) {}; virtual void onImGuiRender() {}; - // Physics thread callbacks (No m_ecs access recommended!) + // Physics thread callbacks. Fire on the physics thread mid-step; no + // ECS access here. Use them for simulation-coupled logic only (contact + // tuning, immediate impulses). Everything else belongs in the main + // thread callbacks below. virtual void onPhysicsStep(Simulation2D& simulation) {}; virtual void onCollision(Simulation2D& simulation, WeirdEngine::CollisionEvent& event) {}; virtual void onShapeCollision(Simulation2D& simulation, WeirdEngine::ShapeCollisionEvent& event) {}; - // Main thread callbacks (m_ecs is safe to use here) // ARE YOU SURE??? + // Main thread callbacks (m_ecs is safe to use here; the physics + // thread only ever touches Simulation2D internals, never the ECS) virtual void onEntityCollision(ECSManager& ecs, WeirdEngine::EntityCollisionEvent& event) {}; virtual void onEntityShapeCollision(ECSManager& ecs, WeirdEngine::EntityShapeCollisionEvent& event) {}; - virtual void onDestroy() {}; void setSceneComplete(std::string nextScene = "") { diff --git a/include/weird-engine/SceneManager.h b/include/weird-engine/SceneManager.h index 6c555ec..abcc21a 100644 --- a/include/weird-engine/SceneManager.h +++ b/include/weird-engine/SceneManager.h @@ -16,8 +16,6 @@ namespace WeirdEngine public: ~SceneManager(); - void loadProject(std::string projectDir); - Scene* getCurrentScene(); void setPhysicsSettings(const PhysicsSettings& settings) diff --git a/include/weird-engine/math/MathExpressions.h b/include/weird-engine/math/MathExpressions.h index eb7befb..6277ba9 100644 --- a/include/weird-engine/math/MathExpressions.h +++ b/include/weird-engine/math/MathExpressions.h @@ -215,8 +215,8 @@ namespace WeirdEngine } }; - // Substract - struct Substraction : TwoFloatOperation + // Subtraction + struct Subtraction : TwoFloatOperation { using TwoFloatOperation::TwoFloatOperation; @@ -233,6 +233,9 @@ namespace WeirdEngine } }; + // Deprecated alias of Subtraction + using Substraction = Subtraction; + // Multiplication struct Multiplication : TwoFloatOperation { diff --git a/src/weird-engine/Scene.cpp b/src/weird-engine/Scene.cpp index 05331fe..30a86bf 100644 --- a/src/weird-engine/Scene.cpp +++ b/src/weird-engine/Scene.cpp @@ -45,7 +45,7 @@ namespace WeirdEngine Scene::Scene() : m_simulation2D(MAX_ENTITIES, SceneManager::getInstance().getPhysicsSettings()) - , m_runSimulationInThread(true) + , m_runSimulationInThread(SceneManager::getInstance().getPhysicsSettings().runSimulationInThread) { } @@ -348,7 +348,7 @@ namespace WeirdEngine return m_drawQueue; } - std::vector& Scene::getLigths() + std::vector& Scene::getLights() { return m_lights; } diff --git a/src/weird-engine/SceneManager.cpp b/src/weird-engine/SceneManager.cpp index ec4e05c..d6c1cdb 100644 --- a/src/weird-engine/SceneManager.cpp +++ b/src/weird-engine/SceneManager.cpp @@ -1,5 +1,7 @@ #include "weird-engine/SceneManager.h" +#include + namespace WeirdEngine { SceneManager::SceneManager() {} @@ -23,15 +25,31 @@ namespace WeirdEngine void SceneManager::loadScene(const std::string& sceneName) { - if (sceneFactories.find(sceneName) != sceneFactories.end()) + if (sceneFactories.find(sceneName) == sceneFactories.end()) + return; + + // Keep the current/target indices in sync so a later loadNextScene() + // cycles from the scene that is actually loaded. + auto it = std::find(names.begin(), names.end(), sceneName); + if (it != names.end()) + { + currentSceneIdx = static_cast(it - names.begin()); + targetSceneIdx = currentSceneIdx; + } + + if (currentScene) { - currentScene = nullptr; - currentScene = sceneFactories[sceneName](); // Instantiate the scene - currentScene->start(); + // Main-thread cleanup hook. The physics thread may still be + // stepping, so only touch ECS/sim state from the main thread here. + currentScene->onDestroy(); + } + + currentScene = nullptr; + currentScene = sceneFactories[sceneName](); // Instantiate the scene + currentScene->start(); #ifndef NDEBUG - WeirdEngine::Logger::log("Changed to " + sceneName + " scene"); + WeirdEngine::Logger::log("Changed to " + sceneName + " scene"); #endif - } } void SceneManager::loadScene(int idx) diff --git a/src/weird-renderer/core/Renderer.cpp b/src/weird-renderer/core/Renderer.cpp index ec5669a..4db4be3 100644 --- a/src/weird-renderer/core/Renderer.cpp +++ b/src/weird-renderer/core/Renderer.cpp @@ -768,7 +768,7 @@ namespace WeirdEngine { PROFILE_SCOPE("3D Render", enable2D); - auto& lights = scene.getLigths(); + auto& lights = scene.getLights(); // --- 1. GBuffer pass: render mesh geometry first --- // Depth testing and culling must be enabled for correct GBuffer writes. From 8b66ad2170b1ed31c944e1058fc4f27f4035ff5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= <49535803+damacaa@users.noreply.github.com> Date: Mon, 10 Aug 2026 16:43:10 +0200 Subject: [PATCH 04/21] physics: add BodyUserData for attaching custom data to rigidbodies --- include/weird-physics/BodyUserData.h | 18 +++++++++ include/weird-physics/Simulation2D.h | 48 ++++++++++++++++++++++++ src/weird-physics/Simulation2D.cpp | 56 ++++++++++++++++++++++++++++ 3 files changed, 122 insertions(+) create mode 100644 include/weird-physics/BodyUserData.h diff --git a/include/weird-physics/BodyUserData.h b/include/weird-physics/BodyUserData.h new file mode 100644 index 0000000..2ca1cbe --- /dev/null +++ b/include/weird-physics/BodyUserData.h @@ -0,0 +1,18 @@ +#pragma once + +namespace WeirdEngine +{ + // Base class for per-body user data attached to rigid bodies via + // Simulation2D::setUserData(). The simulation only stores the pointer and + // never interprets it: derive from this, set `type` to a game-specific + // discriminator, and use Simulation2D::getUserDataAs() (which checks + // `T::TYPE` against `type` before casting) or check `type` manually. + // + // The pointer must be heap-allocated with `new`. The simulation takes + // ownership: it deletes the data when the body is removed and deletes all + // remaining data when the simulation is destroyed (scene teardown). + struct BodyUserData + { + int type = 0; + }; +} // namespace WeirdEngine diff --git a/include/weird-physics/Simulation2D.h b/include/weird-physics/Simulation2D.h index 87d16fb..81df857 100644 --- a/include/weird-physics/Simulation2D.h +++ b/include/weird-physics/Simulation2D.h @@ -20,6 +20,7 @@ #include "weird-engine/vec.h" #include "PhysicsSettings.h" +#include "weird-physics/BodyUserData.h" namespace WeirdEngine { @@ -210,6 +211,49 @@ namespace WeirdEngine m_damping = damping; } + // Per-body user data, keyed by SimulationID (entity-free: the ECS maps + // simulation IDs back to entities via the RigidBody2D component array). + // Must be heap-allocated: the simulation owns the pointer and deletes + // it when the body is removed (removeObject) and when the simulation + // is destroyed. setUserData() is main-thread only; getUserData()/ + // getUserDataAs()/forEachUserData() are safe from the physics + // callbacks without locks. + void setUserData(SimulationID id, BodyUserData* data); + BodyUserData* getUserData(SimulationID id); + + // Type-checked cast: returns nullptr unless the attached data exists + // and its `type` matches T::TYPE. + template T* getUserDataAs(SimulationID id) + { + BodyUserData* data = getUserData(id); + if (!data || data->type != T::TYPE) + return nullptr; + return static_cast(data); + } + + // Calls fn(SimulationID, BodyUserData&) for every active body that has + // user data attached. Lock-free from physics callbacks (the step + // already holds the structural mutex); serialized on the main thread. + template void forEachUserData(Fn&& fn) + { + if (isPhysicsExecutionContext()) + { + for (SimulationID id = 0; id < m_size; ++id) + { + if (m_userData[id]) + fn(id, *m_userData[id]); + } + return; + } + + std::lock_guard lock(m_structuralMutex); + for (SimulationID id = 0; id < m_size; ++id) + { + if (m_userData[id]) + fn(id, *m_userData[id]); + } + } + // Constraint structs (public for serialization) struct DistanceConstraint { @@ -393,6 +437,10 @@ namespace WeirdEngine float* m_mass; float* m_invMass; + // Per-body user data, parallel to the body arrays. Swapped in + // removeObject() so the data follows the body through renumbering. + BodyUserData** m_userData; + const float m_diameter; const float m_diameterSquared; const float m_radious; diff --git a/src/weird-physics/Simulation2D.cpp b/src/weird-physics/Simulation2D.cpp index 14d7043..4e0e56e 100644 --- a/src/weird-physics/Simulation2D.cpp +++ b/src/weird-physics/Simulation2D.cpp @@ -58,6 +58,7 @@ namespace WeirdEngine , m_continuousForcesWrite(new vec2[size]) , m_mass(new float[size]) , m_invMass(new float[size]) + , m_userData(new BodyUserData*[size]) , m_maxSize(size) , m_size(0) , m_allocated(0) @@ -92,6 +93,7 @@ namespace WeirdEngine m_mass[i] = 1000.0f; m_invMass[i] = 0.001f; + m_userData[i] = nullptr; } m_sdfs = std::make_shared>>(); @@ -99,6 +101,14 @@ namespace WeirdEngine Simulation2D::~Simulation2D() { + // Free any user data still attached to live bodies (the simulation + // owns these pointers; removed bodies free theirs in removeObject). + for (size_t i = 0; i < m_allocated; ++i) + { + delete m_userData[i]; + m_userData[i] = nullptr; + } + delete[] m_positions; delete[] m_positionsRead; delete[] m_positionsAux; @@ -112,6 +122,7 @@ namespace WeirdEngine delete[] m_continuousForcesWrite; delete[] m_mass; delete[] m_invMass; + delete[] m_userData; } void Simulation2D::pause() @@ -317,6 +328,38 @@ namespace WeirdEngine // std::lock_guard lock(g_simulationTimeMutex); return m_simulationTime; } + void Simulation2D::setUserData(SimulationID id, BodyUserData* data) + { + WEIRD_ASSERT(!isPhysicsExecutionContext(), "setUserData() may not be called from physics execution context"); + + std::lock_guard lock(m_structuralMutex); + + // Bounds-check against m_allocated, not m_size: bodies can carry user + // data before they are activated (ActivatePending) later in the frame. + if (id >= m_allocated) + return; + + m_userData[id] = data; + } + + BodyUserData* Simulation2D::getUserData(SimulationID id) + { + // Inside a physics step the structural mutex is already held, so the + // read is lock-free; on the main thread it is serialized against + // structural changes (removeObject renumbering). + if (isPhysicsExecutionContext()) + { + if (id >= m_allocated) + return nullptr; + return m_userData[id]; + } + + std::lock_guard lock(m_structuralMutex); + + if (id >= m_allocated) + return nullptr; + return m_userData[id]; + } void Simulation2D::startSimulationThread() { @@ -999,6 +1042,7 @@ namespace WeirdEngine m_mass[id] = 1.0f; m_invMass[id] = 1.0f; m_collisionMap[id] = false; + m_userData[id] = nullptr; m_allocated++; return id; @@ -1027,6 +1071,12 @@ namespace WeirdEngine if (toId != fromId) { + // The simulation owns user data: free the removed body's data and + // move the swapped body's data along with it. + delete m_userData[toId]; + m_userData[toId] = m_userData[fromId]; + m_userData[fromId] = nullptr; + m_positions[toId] = m_positions[fromId]; m_positionsRead[toId] = m_positionsRead[fromId]; m_positionsAux[toId] = m_positionsAux[fromId]; @@ -1046,6 +1096,12 @@ namespace WeirdEngine m_collisionMap[toId] = m_collisionMap[fromId]; } } + else + { + // Removing the last body: just free its user data. + delete m_userData[toId]; + m_userData[toId] = nullptr; + } // Fix constraints (potentially slow...) From 422a0d34750843dd9862149057a358befd084572 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= <49535803+damacaa@users.noreply.github.com> Date: Mon, 10 Aug 2026 16:43:23 +0200 Subject: [PATCH 05/21] core: refactor Scene API to use ServiceProvider and improve naming --- include/weird-engine/Scene.h | 258 ++++---- .../weird-engine/services/ServiceProvider.h | 587 ++++++++++++++++++ src/weird-engine/Scene.cpp | 91 ++- src/weird-engine/SceneManager.cpp | 2 +- 4 files changed, 794 insertions(+), 144 deletions(-) create mode 100644 include/weird-engine/services/ServiceProvider.h diff --git a/include/weird-engine/Scene.h b/include/weird-engine/Scene.h index 2b76a44..9706a08 100644 --- a/include/weird-engine/Scene.h +++ b/include/weird-engine/Scene.h @@ -12,6 +12,7 @@ #include "weird-engine/Background.h" #include "weird-engine/Material3D.h" +#include "weird-engine/services/ServiceProvider.h" #include "weird-physics/PhysicsSettings.h" #include "weird-physics/Simulation2D.h" @@ -40,45 +41,74 @@ namespace WeirdEngine Entity entity; }; - constexpr int SOUND_QUEUE_SIZE = 16; - // Forward declaration – full definition in SceneSerializer.h class SceneSerializer; class Scene { + // Serialization and the service provider reach into the scene's + // private state (storage lives here; the provider is a facade). friend class SceneSerializer; + friend class ServiceProvider; + friend struct SerializationService; public: + // ---- Types /// Map from tag name (std::string) to the entity that owns it. - using TagMap = std::unordered_map; + using TagMap = ::WeirdEngine::TagMap; + + using RenderMode = ::WeirdEngine::RenderMode; + + using RaymarchResult = ::WeirdEngine::RaymarchResult; + // ---- Lifecycle (engine-driven) Scene(); virtual ~Scene(); void start(); - void renderExtra(WeirdRenderer::RenderTarget& renderTarget); + // Called by the engine once per frame with the variable frame delta. + void update(double delta, double time); + + // Called by SceneManager right before this scene is destroyed during + // a scene transition. Runs on the main thread; the physics thread may + // still be stepping, so keep the same thread rules as the callbacks. + void destroy() + { + onDestroy(m_ecs, m_services); + } + // ---- Rendering pipeline (engine-driven) + void renderExtra(WeirdRenderer::RenderTarget& renderTarget); void update2DWorldShader(WeirdRenderer::Shader& shader); void update3DWorldShader(WeirdRenderer::Shader& shader); void updateUIShader(WeirdRenderer::Shader& shader); void forceShaderRefresh(); - void update(double delta, double time); - void get2DShapesData(vec4*& data, uint32_t& size, uint32_t& customShapeCount); void get3DShapesData(vec4*& data, uint32_t& size, uint32_t& customShapeCount); void getUIData(vec4*& uiData, uint32_t& size, uint32_t& customShapeCount); + void renderImGui(); + void renderPhysicsStatsUI(); + // ---- Scene state access (engine-driven) WeirdRenderer::Camera& getCamera(); std::vector& getLights(); + const std::vector& getDrawQueue() const; + AudioRingBuffer& getAudioQueue(); + float getFrictionSound(); - Simulation2D& getSimulation2D() + BackgroundParams& getBackground() { - return m_simulation2D; + return m_background; } - Material3D& createMaterial(); + const BackgroundParams& getBackground() const + { + return m_background; + } + RenderMode getRenderMode() const; + float getTime(); + Material3D& createMaterial(); Material3D& getMaterial(int index) { return m_materials[index]; @@ -88,30 +118,7 @@ namespace WeirdEngine return m_materials; } - BackgroundParams& getBackground() - { - return m_background; - } - const BackgroundParams& getBackground() const - { - return m_background; - } - - float getTime(); - - enum class RenderMode - { - RayMarching3D, - RayMarching2D, - RayMarchingBoth - }; - - RenderMode getRenderMode() const; - - float getFrictionSound(); - const std::vector& getDrawQueue() const; - AudioRingBuffer& getAudioQueue(); - + // ---- Scene control bool isSceneComplete() const { return m_isSceneComplete; @@ -121,73 +128,63 @@ namespace WeirdEngine return m_nextScene; }; - static ShapeId registerDefaultSDF(std::shared_ptr sdf); - static const std::vector>& getGlobalSDFs(); - - ShapeId registerSDF(std::shared_ptr sdf); - // Set the path to a .weird file to load when the scene starts void setSceneFilePath(const std::string& path) { m_sceneFilePath = path; } - struct RaymarchResult - { - float distance; - Entity entity; - }; - - // Physics queries - RaymarchResult raymarch(glm::vec2 origin, glm::vec2 direction, float epsilon = 0.001f, - float maxDistance = 150.0f); - - // Called by SceneManager right before this scene is destroyed during - // a scene transition. Runs on the main thread; the physics thread may - // still be stepping, so keep the same thread rules as the callbacks below. - virtual void onDestroy() {}; - - void renderImGui(); - void renderPhysicsStatsUI(); + // ---- Global SDF registry (engine-level, shared across scenes) + static ShapeId registerDefaultSDF(std::shared_ptr sdf); + static const std::vector>& getGlobalSDFs(); protected: - virtual void onCreate() {}; - virtual void onStart(ECSManager& ecs, const TagMap& tags) - { - onStart(ecs); - } - virtual void onStart(ECSManager& ecs) {} - virtual void onUpdate(float delta, ECSManager& ecs) = 0; + // Internal constructor: sets the render mode for Scene2D/Scene3D/SceneBoth. + Scene(RenderMode mode); + + // ---- Lifecycle callbacks + virtual void onCreate(ECSManager& ecs, ServiceProvider& services) {}; + virtual void onStart(ECSManager& ecs, ServiceProvider& services) {} + virtual void onUpdate(ECSManager& ecs, ServiceProvider& services) {}; + virtual void onDestroy(ECSManager& ecs, ServiceProvider& services) {}; virtual void onRender(WeirdRenderer::RenderTarget& renderTarget) {}; - virtual void onImGuiRender() {}; - - // Physics thread callbacks. Fire on the physics thread mid-step; no - // ECS access here. Use them for simulation-coupled logic only (contact - // tuning, immediate impulses). Everything else belongs in the main - // thread callbacks below. + virtual void onImGuiRender(ECSManager& ecs, ServiceProvider& services) {}; + + // ---- Main thread collision callbacks (onEntity* family). Fire after + // the physics response has been applied; the events are read-only. + // m_ecs is safe to use here (the physics thread only ever touches + // Simulation2D internals). See the onPhysics* callbacks below for the + // pre-response, mutable equivalents. + virtual void onEntityCollision(ECSManager& ecs, ServiceProvider& services, + WeirdEngine::EntityCollisionEvent& event) {}; + virtual void onEntityShapeCollision(ECSManager& ecs, ServiceProvider& services, + WeirdEngine::EntityShapeCollisionEvent& event) {}; + + // ---- Physics thread callbacks (onPhysics* family). Fire on the + // physics thread mid-step; no ECS access here. The collision events + // are pre-response and mutable: tune the event fields (friction, + // absorption), queue impulses, or call fix() to alter the solver's + // behavior. Everything else belongs in the onEntity* main-thread + // callbacks above (post-response, read-only). virtual void onPhysicsStep(Simulation2D& simulation) {}; - virtual void onCollision(Simulation2D& simulation, WeirdEngine::CollisionEvent& event) {}; - virtual void onShapeCollision(Simulation2D& simulation, WeirdEngine::ShapeCollisionEvent& event) {}; - - // Main thread callbacks (m_ecs is safe to use here; the physics - // thread only ever touches Simulation2D internals, never the ECS) - virtual void onEntityCollision(ECSManager& ecs, WeirdEngine::EntityCollisionEvent& event) {}; - virtual void onEntityShapeCollision(ECSManager& ecs, WeirdEngine::EntityShapeCollisionEvent& event) {}; + virtual void onPhysicsRigidBodyCollision(Simulation2D& simulation, WeirdEngine::CollisionEvent& event) {}; + virtual void onPhysicsShapeCollision(Simulation2D& simulation, WeirdEngine::ShapeCollisionEvent& event) {}; - void setSceneComplete(std::string nextScene = "") + // ---- Scene control helper + void goToNextScene(std::string nextScene = "") { m_isSceneComplete = true; m_nextScene = nextScene; }; - Entity m_mainCamera; - ResourceManager m_resourceManager; - - Material3D m_materials[16]; - uint16_t m_materialCount = 0; - - std::vector> m_sdfs; + // Deprecated alias of goToNextScene. + void setSceneComplete(std::string nextScene = "") + { + goToNextScene(nextScene); + }; + // ---- Shape creation & SDF registration + ShapeId registerSDF(std::shared_ptr sdf); Entity addShape(ShapeId shapeId, float* variables, uint16_t material, CombinationType combination = CombinationType::Addition, bool hasCollision = true, int group = 0); @@ -207,16 +204,7 @@ namespace WeirdEngine } UIShape& addUIShape(ShapeId shapeId, float* variables, Entity& entity, int group = 0); - void lookAt(Entity entity); - - // Entities in this set will be skipped during scene serialization - std::unordered_set m_serializationBlacklist; - void blacklistEntity(Entity e) - { - m_serializationBlacklist.insert(e); - } - - // Tag management + // ---- Entity helpers // Assign a unique tag to an entity. If the tag is already owned by // another entity, it is moved to this one. An empty name is treated // as a removal request (equivalent to calling removeTag). @@ -228,20 +216,13 @@ namespace WeirdEngine // Return the entity that owns a tag, or MAX_ENTITIES if none. Entity getEntityByTag(const std::string& name) const; - SDFRenderSystemContext m_2DWorldRenderContext; - SDFRenderSystemContext m_3DWorldRenderContext; - SDFRenderSystemContext m_UIRenderContext; - // Resolve a physics SimulationID to the owning entity. - Entity getEntityForSimulationId(SimulationID simulationId, - std::shared_ptr> rigidBodies); - - bool m_debugFly = false; - bool m_debugInput = false; - - RenderMode m_renderMode = RenderMode::RayMarching2D; - - void playSound(const WeirdRenderer::SimpleAudioRequest& audio); + // Entities in this set will be skipped during scene serialization + void blacklistEntity(Entity e) + { + m_serializationBlacklist.insert(e); + } + // ---- Persistence & physics queries // Save the current scene state to a .weird JSON file void saveScene(const std::string& filename); @@ -251,49 +232,76 @@ namespace WeirdEngine // Returns a map of tag names to their corresponding entities. TagMap loadWeirdFile(const std::string& path, bool blacklistEntities = false); - // Path to a .weird file to load when the scene starts (set via setSceneFilePath or registerScene) - std::string m_sceneFilePath; + RaymarchResult raymarch(glm::vec2 origin, glm::vec2 direction, float epsilon = 0.001f, + float maxDistance = 150.0f); - BackgroundParams m_background; + // ---- Shared state (available to derived scenes) + Entity m_mainCamera; + ResourceManager m_resourceManager; + std::vector> m_sdfs; + bool m_debugFly = false; + bool m_debugInput = false; private: + // ---- Internal helpers + static void handlePhysicsStep(void* userData); + static void handleCollision(CollisionEvent& event, void* userData); + static void handleShapeCollision(ShapeCollisionEvent& event, void* userData); // Load scene state from a .weird JSON file void loadFromWeirdFile(const std::string& path); + void playSound(const WeirdRenderer::SimpleAudioRequest& audio); + // Resolve a physics SimulationID to the owning entity. + Entity getEntityForSimulationId(SimulationID simulationId, + std::shared_ptr> rigidBodies); + // ---- Simulation + ECSManager m_ecs; + Simulation2D m_simulation2D; bool m_runSimulationInThread; + bool m_simulationIsPaused = false; + // ---- Collision queues (physics thread pushes, main thread drains) + std::mutex m_collisionQueueMutex; + std::vector m_queuedCollisions; + std::vector m_queuedShapeCollisions; + + // ---- Audio, draw queue, lights AudioRingBuffer m_audioQueue; float m_frictionSoundLevel{0.0f}; std::atomic m_frictionSoundLevelRead{0.0f}; - std::vector m_drawQueue; std::vector m_lights; - static void handlePhysicsStep(void* userData); - static void handleCollision(CollisionEvent& event, void* userData); - static void handleShapeCollision(ShapeCollisionEvent& event, void* userData); + // ---- Serialization & visuals + std::unordered_set m_serializationBlacklist; + std::string m_sceneFilePath; + BackgroundParams m_background; + Material3D m_materials[16]; + uint16_t m_materialCount = 0; + SDFRenderSystemContext m_2DWorldRenderContext; + SDFRenderSystemContext m_3DWorldRenderContext; + SDFRenderSystemContext m_UIRenderContext; + RenderMode m_renderMode = RenderMode::RayMarching2D; + // ---- Scene control state std::string m_nextScene; bool m_isSceneComplete = false; - ECSManager m_ecs; - Simulation2D m_simulation2D; - bool m_simulationIsPaused = false; - - std::mutex m_collisionQueueMutex; - std::vector m_queuedCollisions; - std::vector m_queuedShapeCollisions; - - // Entity tag storage (bidirectional maps kept in sync) + // ---- Entity tag storage (bidirectional maps kept in sync) TagMap m_tagToEntity; std::unordered_map m_entityToTag; + + // ---- Service provider binding (must be the last declared member: it + // holds references to the members above). + float m_lastDelta = 0.0f; + ServiceProvider m_services; }; class Scene2D : public Scene { public: Scene2D() + : Scene(RenderMode::RayMarching2D) { - m_renderMode = RenderMode::RayMarching2D; } }; @@ -301,8 +309,8 @@ namespace WeirdEngine { public: Scene3D() + : Scene(RenderMode::RayMarching3D) { - m_renderMode = RenderMode::RayMarching3D; } }; @@ -310,8 +318,8 @@ namespace WeirdEngine { public: SceneBoth() + : Scene(RenderMode::RayMarchingBoth) { - m_renderMode = RenderMode::RayMarchingBoth; } }; } // namespace WeirdEngine diff --git a/include/weird-engine/services/ServiceProvider.h b/include/weird-engine/services/ServiceProvider.h new file mode 100644 index 0000000..cbaf8d4 --- /dev/null +++ b/include/weird-engine/services/ServiceProvider.h @@ -0,0 +1,587 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "weird-engine/Background.h" +#include "weird-engine/ecs/ECS.h" +#include "weird-engine/Material3D.h" +#include "weird-engine/ResourceManager.h" +#include "weird-engine/systems/SDFRenderSystem.h" +#include "weird-engine/vec.h" +#include "weird-physics/components/RigidBody.h" +#include "weird-physics/Simulation2D.h" +#include "weird-renderer/audio/AudioRingBuffer.h" +#include "weird-renderer/audio/SimpleAudioRequest.h" +#include "weird-renderer/components/Camera.h" +#include "weird-renderer/components/CustomShape.h" +#include "weird-renderer/scene/Light.h" + +namespace WeirdEngine +{ + class Scene; + + constexpr int SOUND_QUEUE_SIZE = 16; + + /// Map from tag name (std::string) to the entity that owns it. + using TagMap = std::unordered_map; + + struct RaymarchResult + { + float distance; + Entity entity; + }; + + enum class RenderMode + { + RayMarching3D, + RayMarching2D, + RayMarchingBoth + }; + + // Shared raymarch implementation used by both Scene::raymarch and + // PhysicsService::raymarch. Defined in Scene.cpp. + RaymarchResult raymarchScene(ECSManager& ecs, std::vector>& sdfs, + Simulation2D& simulation, float time, glm::vec2 origin, glm::vec2 direction, + float epsilon, float maxDistance); + + struct TimeService + { + Simulation2D& simulation; + const float& delta; + + float time() const + { + return static_cast(simulation.getSimulationTime()); + } + + float deltaTime() const + { + return delta; + } + + double fixedDeltaTime() const + { + return simulation.getDeltaTime(); + } + }; + + struct PhysicsService + { + ECSManager& ecs; + Simulation2D& simulation; + std::vector>& sdfs; + + Simulation2D& sim() + { + return simulation; + } + + void setGravity(float gravity) + { + simulation.setGravity(gravity); + } + + void setDamping(float damping) + { + simulation.setDamping(damping); + } + + void pause() + { + simulation.pause(); + } + + void resume() + { + simulation.resume(); + } + + bool isPaused() const + { + return simulation.isPaused(); + } + + Entity entityForSimulationId(SimulationID simulationId) const + { + auto rigidBodies = ecs.getComponentArray(); + if (simulationId >= static_cast(rigidBodies->getSize())) + return INVALID_ENTITY; + + return rigidBodies->getEntityAtIdx(static_cast(simulationId)); + } + + // Per-body user data. Set the data right after adding the RigidBody2D + // component (read rb.simulationId from it). The pointer must be + // heap-allocated: the simulation owns it and deletes it when the body + // is removed or when the simulation is destroyed. + void setUserData(SimulationID id, BodyUserData* data) + { + simulation.setUserData(id, data); + } + + BodyUserData* getUserData(SimulationID id) + { + return simulation.getUserData(id); + } + + template T* getUserDataAs(SimulationID id) + { + return simulation.getUserDataAs(id); + } + + template void forEachUserData(Fn&& fn) + { + simulation.forEachUserData(std::forward(fn)); + } + + RaymarchResult raymarch(glm::vec2 origin, glm::vec2 direction, float epsilon = 0.001f, + float maxDistance = 150.0f) + { + return raymarchScene(ecs, sdfs, simulation, static_cast(simulation.getSimulationTime()), origin, + direction, epsilon, maxDistance); + } + }; + + struct ShapeService + { + ECSManager& ecs; + Simulation2D& simulation; + std::vector>& sdfs; + + static ShapeId registerDefaultSDF(std::shared_ptr sdf); + + ShapeId registerSDF(std::shared_ptr sdf) + { + sdfs.push_back(std::move(sdf)); + simulation.setSDFs(sdfs); + + return static_cast(sdfs.size() - 1); + } + + const std::vector>& getSDFs() const + { + return sdfs; + } + + Entity addShape(ShapeId shapeId, float* variables, uint16_t material, + CombinationType combination = CombinationType::Addition, bool hasCollision = true, + int group = 0) + { + Entity entity = ecs.createEntity(); + CustomShape& shape = ecs.addComponent(entity); + shape.distanceFieldId = shapeId; + shape.combination = combination; + shape.hasCollisions = hasCollision; + shape.groupIdx = group; + shape.material = material; + std::copy(variables, variables + 8, shape.parameters); + + return entity; + } + + Entity addShape(ShapeId shapeId, float* variables, const Material3D& material, + CombinationType combination = CombinationType::Addition, bool hasCollision = true, + int group = 0) + { + return addShape(shapeId, variables, material.id, combination, hasCollision, group); + } + + Entity addUIShape(ShapeId shapeId, float* variables, uint16_t material, + CombinationType combination = CombinationType::Addition, int group = 0) + { + Entity entity = ecs.createEntity(); + UIShape& shape = ecs.addComponent(entity); + shape.distanceFieldId = shapeId; + shape.combination = combination; + shape.groupIdx = group; + shape.material = material; + std::copy(variables, variables + 8, shape.parameters); + + return entity; + } + + Entity addUIShape(ShapeId shapeId, float* variables, const Material3D& material, + CombinationType combination = CombinationType::Addition, int group = 0) + { + return addUIShape(shapeId, variables, material.id, combination, group); + } + + UIShape& addUIShape(ShapeId shapeId, float* variables, Entity& entity, int group = 0) + { + entity = ecs.createEntity(); + UIShape& component = ecs.addComponent(entity); + component.distanceFieldId = shapeId; + component.groupIdx = group; + component.smoothFactor = 100.0f; + std::copy(variables, variables + 8, component.parameters); + + return component; + } + }; + + struct RenderService + { + ECSManager& ecs; + Entity& cameraEntity; + SDFRenderSystemContext& context2D; + SDFRenderSystemContext& context3D; + SDFRenderSystemContext& contextUI; + std::vector& lights; + BackgroundParams& background; + RenderMode& renderMode; + + WeirdRenderer::Camera& camera() + { + return ecs.getComponent(cameraEntity).camera; + } + + Entity getCameraEntity() const + { + return cameraEntity; + } + + std::vector& getLights() + { + return lights; + } + + BackgroundParams& getBackground() + { + return background; + } + + const BackgroundParams& getBackground() const + { + return background; + } + + RenderMode getRenderMode() const + { + return renderMode; + } + + SDFRenderSystemContext& getContext2D() + { + return context2D; + } + + SDFRenderSystemContext& getContext3D() + { + return context3D; + } + + SDFRenderSystemContext& getContextUI() + { + return contextUI; + } + + // Force the shader to regenerate the next frame. Materials are baked + // into the shader, so changing a shape's material (or any parameter + // that affects shader generation) requires a refresh. Use the + // granular variants to refresh only the affected render target. + void forceShaderRefresh2D() + { + context2D.shapesNeedUpdate = true; + } + + void forceShaderRefresh3D() + { + context3D.shapesNeedUpdate = true; + } + + void forceShaderRefreshUI() + { + contextUI.shapesNeedUpdate = true; + } + + void forceShaderRefresh() + { + forceShaderRefresh2D(); + forceShaderRefresh3D(); + forceShaderRefreshUI(); + } + }; + + struct MaterialService + { + Material3D (&materials)[16]; + uint16_t& count; + + Material3D& createMaterial() + { + if (count >= 16) + { + // Max materials reached, return the last one + return materials[15]; + } + + Material3D& mat = materials[count]; + mat.id = count; + count++; + + return mat; + } + + Material3D& getMaterial(int index) + { + return materials[index]; + } + + const Material3D* getMaterials() const + { + return materials; + } + + uint16_t getMaterialCount() const + { + return count; + } + }; + + struct AudioService + { + AudioRingBuffer& queue; + const std::atomic& frictionSoundLevel; + + void playSound(const WeirdRenderer::SimpleAudioRequest& audio) + { + queue.push(audio); + } + + float getFrictionSound() const + { + return frictionSoundLevel.load(std::memory_order_acquire); + } + + AudioRingBuffer& audioQueue() + { + return queue; + } + }; + + struct TagService + { + TagMap& tagToEntity; + std::unordered_map& entityToTag; + + // Assign a unique tag to an entity. If the tag is already owned by + // another entity, it is moved to this one. An empty name is treated + // as a removal request (equivalent to calling removeTag). + void tag(Entity entity, const std::string& name) + { + if (name.empty()) + { + removeTag(entity); + return; + } + + // If the tag is already owned by another entity, remove it from that entity + auto existingOwner = tagToEntity.find(name); + if (existingOwner != tagToEntity.end() && existingOwner->second != entity) + { + entityToTag.erase(existingOwner->second); + } + + // Remove any previous tag this entity had + auto existingTag = entityToTag.find(entity); + if (existingTag != entityToTag.end() && existingTag->second != name) + { + tagToEntity.erase(existingTag->second); + } + + tagToEntity[name] = entity; + entityToTag[entity] = name; + } + + void removeTag(Entity entity) + { + auto it = entityToTag.find(entity); + if (it == entityToTag.end()) + return; + tagToEntity.erase(it->second); + entityToTag.erase(it); + } + + std::string getEntityTag(Entity entity) const + { + auto it = entityToTag.find(entity); + if (it == entityToTag.end()) + return ""; + return it->second; + } + + Entity getEntityByTag(const std::string& name) const + { + auto it = tagToEntity.find(name); + if (it == tagToEntity.end()) + return MAX_ENTITIES; + return it->second; + } + }; + + struct SerializationService + { + Scene& scene; + std::unordered_set& blacklist; + std::string& sceneFilePath; + + // Save the current scene state to a .weird JSON file + void saveScene(const std::string& filename); + + // Dynamically load a .weird file and add its contents to the scene. + // If blacklistEntities is true, all entities created by the load will be + // excluded from future scene serialization. + // Returns a map of tag names to their corresponding entities. + TagMap loadWeirdFile(const std::string& path, bool blacklistEntities = false); + + void blacklistEntity(Entity entity) + { + blacklist.insert(entity); + } + + // Set the path to a .weird file to load when the scene starts + void setSceneFilePath(const std::string& path) + { + sceneFilePath = path; + } + }; + + struct SceneControlService + { + bool& isComplete; + std::string& nextScene; + + void goToNextScene(std::string next = "") + { + isComplete = true; + nextScene = std::move(next); + } + }; + + struct ResourceService + { + ResourceManager& resourceManager; + + ResourceManager& resources() + { + return resourceManager; + } + }; + + struct DebugService + { + bool& fly; + bool& input; + + bool debugFly() const + { + return fly; + } + + void setDebugFly(bool value) + { + fly = value; + } + + bool debugInput() const + { + return input; + } + + void setDebugInput(bool value) + { + input = value; + } + }; + + // Central access point for all non-ECS scene functionality. Systems take + // a ServiceProvider& (plus the ECSManager&) and use it instead of reaching + // into Scene internals. Owned by Scene, which binds it to its storage. + class ServiceProvider + { + public: + explicit ServiceProvider(Scene& scene); + + ECSManager& ecs() + { + return m_ecs; + } + + TimeService& time() + { + return m_time; + } + + PhysicsService& physics() + { + return m_physics; + } + + ShapeService& shapes() + { + return m_shapes; + } + + RenderService& render() + { + return m_render; + } + + MaterialService& materials() + { + return m_materials; + } + + AudioService& audio() + { + return m_audio; + } + + TagService& tags() + { + return m_tags; + } + + SerializationService& serialization() + { + return m_serialization; + } + + SceneControlService& sceneControl() + { + return m_sceneControl; + } + + ResourceService& resources() + { + return m_resources; + } + + DebugService& debug() + { + return m_debug; + } + + private: + ECSManager& m_ecs; + TimeService m_time; + PhysicsService m_physics; + ShapeService m_shapes; + RenderService m_render; + MaterialService m_materials; + AudioService m_audio; + TagService m_tags; + SerializationService m_serialization; + SceneControlService m_sceneControl; + ResourceService m_resources; + DebugService m_debug; + }; +} // namespace WeirdEngine diff --git a/src/weird-engine/Scene.cpp b/src/weird-engine/Scene.cpp index 30a86bf..9614d0a 100644 --- a/src/weird-engine/Scene.cpp +++ b/src/weird-engine/Scene.cpp @@ -46,9 +46,16 @@ namespace WeirdEngine Scene::Scene() : m_simulation2D(MAX_ENTITIES, SceneManager::getInstance().getPhysicsSettings()) , m_runSimulationInThread(SceneManager::getInstance().getPhysicsSettings().runSimulationInThread) + , m_services(*this) { } + Scene::Scene(RenderMode mode) + : Scene() + { + m_renderMode = mode; + } + Scene::~Scene() { m_simulation2D.stopSimulationThread(); @@ -59,6 +66,8 @@ namespace WeirdEngine void Scene::start() { + onCreate(m_ecs, m_services); + // Custom component managers std::shared_ptr rbManager = std::make_shared(m_simulation2D); m_ecs.registerComponent(rbManager); @@ -120,18 +129,14 @@ namespace WeirdEngine t.rotation = vec3(0, 0, -1.0f); ECS::Camera& c = m_ecs.addComponent(m_mainCamera); - onCreate(); - // If a .weird file path was provided (via setSceneFilePath / registerScene), // restore saved scene state before the derived class's onStart() runs. - TagMap loadedTags; if (!m_sceneFilePath.empty()) { SceneSerializer::load(*this, m_sceneFilePath); - loadedTags = m_tagToEntity; } - onStart(m_ecs, loadedTags); + onStart(m_ecs, m_services); switch (m_renderMode) { @@ -158,6 +163,8 @@ namespace WeirdEngine { PROFILE_SCOPE("Scene Update"); + m_lastDelta = static_cast(delta); + if (Input::GetKey(Input::LeftCtrl) && Input::GetKeyDown((Input::R))) { forceShaderRefresh(); @@ -209,13 +216,13 @@ namespace WeirdEngine { EntityCollisionEvent entityEvent{ev, getEntityForSimulationId(ev.bodyA, rigidBodies), getEntityForSimulationId(ev.bodyB, rigidBodies)}; - onEntityCollision(m_ecs, entityEvent); + onEntityCollision(m_ecs, m_services, entityEvent); } for (auto& ev : shapeCollisions) { EntityShapeCollisionEvent entityEvent{ev, getEntityForSimulationId(ev.body, rigidBodies)}; - onEntityShapeCollision(m_ecs, entityEvent); + onEntityShapeCollision(m_ecs, m_services, entityEvent); const float m_soundFalloff = 0.1f; bool spatialAudio = false; @@ -246,7 +253,7 @@ namespace WeirdEngine { PROFILE_SCOPE("OnUpdate"); - onUpdate(static_cast(delta), m_ecs); + onUpdate(m_ecs, m_services); } { @@ -271,7 +278,7 @@ namespace WeirdEngine void Scene::handleCollision(CollisionEvent& event, void* userData) { Scene* self = static_cast(userData); - self->onCollision(self->m_simulation2D, event); + self->onPhysicsRigidBodyCollision(self->m_simulation2D, event); std::lock_guard lock(self->m_collisionQueueMutex); self->m_queuedCollisions.push_back(event); @@ -280,7 +287,7 @@ namespace WeirdEngine void Scene::handleShapeCollision(ShapeCollisionEvent& event, void* userData) { Scene* self = static_cast(userData); - self->onShapeCollision(self->m_simulation2D, event); + self->onPhysicsShapeCollision(self->m_simulation2D, event); { std::lock_guard lock(self->m_collisionQueueMutex); @@ -474,11 +481,51 @@ namespace WeirdEngine SceneSerializer::load(*this, path); } + // ServiceProvider + + ServiceProvider::ServiceProvider(Scene& scene) + : m_ecs(scene.m_ecs) + , m_time(scene.m_simulation2D, scene.m_lastDelta) + , m_physics(scene.m_ecs, scene.m_simulation2D, scene.m_sdfs) + , m_shapes(scene.m_ecs, scene.m_simulation2D, scene.m_sdfs) + , m_render(scene.m_ecs, scene.m_mainCamera, scene.m_2DWorldRenderContext, scene.m_3DWorldRenderContext, + scene.m_UIRenderContext, scene.m_lights, scene.m_background, scene.m_renderMode) + , m_materials(scene.m_materials, scene.m_materialCount) + , m_audio(scene.m_audioQueue, scene.m_frictionSoundLevelRead) + , m_tags(scene.m_tagToEntity, scene.m_entityToTag) + , m_serialization(scene, scene.m_serializationBlacklist, scene.m_sceneFilePath) + , m_sceneControl(scene.m_isSceneComplete, scene.m_nextScene) + , m_resources(scene.m_resourceManager) + , m_debug(scene.m_debugFly, scene.m_debugInput) + { + } + + ShapeId ShapeService::registerDefaultSDF(std::shared_ptr sdf) + { + return Scene::registerDefaultSDF(std::move(sdf)); + } + + void SerializationService::saveScene(const std::string& filename) + { + scene.saveScene(filename); + } + + TagMap SerializationService::loadWeirdFile(const std::string& path, bool blacklistEntities) + { + return scene.loadWeirdFile(path, blacklistEntities); + } + Scene::RaymarchResult Scene::raymarch(glm::vec2 origin, glm::vec2 direction, float epsilon, float maxDistance) + { + return raymarchScene(m_ecs, m_sdfs, m_simulation2D, getTime(), origin, direction, epsilon, maxDistance); + } + + RaymarchResult raymarchScene(ECSManager& ecs, std::vector>& sdfs, + Simulation2D& simulation, float time, glm::vec2 origin, glm::vec2 direction, + float epsilon, float maxDistance) { float traveled = 0.0f; - float time = getTime(); - auto gridSnapshot = m_simulation2D.getSpatialGridSnapshot(); + auto gridSnapshot = simulation.getSpatialGridSnapshot(); if (epsilon <= 0.0f) { @@ -503,9 +550,9 @@ namespace WeirdEngine groups.reserve(16); // Cache the rigid bodies component array to avoid repeated lookups in the ECS during the raymarching loop - auto rigidBodies = m_ecs.getComponentArray(); + auto rigidBodies = ecs.getComponentArray(); - auto shapeArray = m_ecs.getComponentArray(); + auto shapeArray = ecs.getComponentArray(); for (size_t j = 0; j < shapeArray->getSize(); j++) { auto& shape = shapeArray->getDataAtIdx(j); @@ -513,7 +560,7 @@ namespace WeirdEngine if (!shape.hasCollisions) continue; - if (shape.distanceFieldId >= m_sdfs.size()) + if (shape.distanceFieldId >= sdfs.size()) continue; float parameters[11]; @@ -522,7 +569,7 @@ namespace WeirdEngine parameters[9] = p.x; parameters[10] = p.y; - float dist = m_sdfs[shape.distanceFieldId]->getValue(parameters); + float dist = sdfs[shape.distanceFieldId]->getValue(parameters); float currentMinDistance = d; Entity currentEntity = INVALID_ENTITY; @@ -613,6 +660,14 @@ namespace WeirdEngine float minRigidbodyDist = 1000.0f; Entity closestRbEntity = INVALID_ENTITY; + auto entityForSimulationId = [&](SimulationID simulationId) -> Entity + { + if (simulationId >= static_cast(rigidBodies->getSize())) + return INVALID_ENTITY; + + return rigidBodies->getEntityAtIdx(static_cast(simulationId)); + }; + int gx = static_cast(std::floor(p.x * gridSnapshot->invCellSize)); int gy = static_cast(std::floor(p.y * gridSnapshot->invCellSize)); const int TABLE_SIZE = 8191; // Must match Simulation2D.cpp @@ -643,7 +698,7 @@ namespace WeirdEngine if (dist < minRigidbodyDist) { minRigidbodyDist = dist; - closestRbEntity = getEntityForSimulationId(rbIndex, rigidBodies); + closestRbEntity = entityForSimulationId(rbIndex); } rbIndex = gridSnapshot->next[rbIndex]; @@ -712,7 +767,7 @@ namespace WeirdEngine ImGui::Separator(); - onImGuiRender(); + onImGuiRender(m_ecs, m_services); ImGui::PopID(); } diff --git a/src/weird-engine/SceneManager.cpp b/src/weird-engine/SceneManager.cpp index d6c1cdb..284459a 100644 --- a/src/weird-engine/SceneManager.cpp +++ b/src/weird-engine/SceneManager.cpp @@ -41,7 +41,7 @@ namespace WeirdEngine { // Main-thread cleanup hook. The physics thread may still be // stepping, so only touch ECS/sim state from the main thread here. - currentScene->onDestroy(); + currentScene->destroy(); } currentScene = nullptr; From 19deda090adb895929ea2b7e37657657be8c4e9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= <49535803+damacaa@users.noreply.github.com> Date: Mon, 10 Aug 2026 16:43:23 +0200 Subject: [PATCH 06/21] examples: update tools and example scenes to new Scene API --- examples/3d-experiments/include/Classic.h | 6 +- examples/3d-experiments/include/CornellBox.h | 6 +- .../3d-experiments/include/MaterialShowcase.h | 6 +- examples/empty-project/src/main.cpp | 4 +- examples/opengl-experiments/include/Fire.h | 11 +- examples/opengl-experiments/include/Lines.h | 9 +- examples/opengl-experiments/include/Water.h | 11 +- .../sample-scenes/include/AquariumScene.h | 26 +- .../sample-scenes/include/CollisionHandling.h | 8 +- examples/sample-scenes/include/DestroyScene.h | 17 +- examples/sample-scenes/include/ImageScene.h | 6 +- examples/sample-scenes/include/LifeScene.h | 7 +- .../include/MouseCollisionScene.h | 12 +- examples/sample-scenes/include/RopeScene.h | 7 +- .../include/ServiceShowcaseScene.h | 580 ++++++++++++++++++ .../include/ShapesCombinations.h | 7 +- examples/sample-scenes/include/TextScene.h | 6 +- examples/sample-scenes/include/WalkScene.h | 23 +- examples/sample-scenes/src/main.cpp | 2 + .../molecule-editor/include/MoleculeEditor.h | 9 +- tools/scene-editor/include/SceneEditor.h | 6 +- 21 files changed, 686 insertions(+), 83 deletions(-) create mode 100644 examples/sample-scenes/include/ServiceShowcaseScene.h diff --git a/examples/3d-experiments/include/Classic.h b/examples/3d-experiments/include/Classic.h index effc589..aec1e70 100644 --- a/examples/3d-experiments/include/Classic.h +++ b/examples/3d-experiments/include/Classic.h @@ -12,7 +12,7 @@ class ClassicScene : public Scene3D Entity m_ball; // Inherited via Scene - void onStart(ECSManager& ecs) override + void onStart(ECSManager& ecs, ServiceProvider& services) override { m_debugFly = true; @@ -70,11 +70,11 @@ class ClassicScene : public Scene3D ecs.getComponent(m_mainCamera).position = vec3(0, 2, 10); } - void onUpdate(float delta, ECSManager& ecs) override + void onUpdate(ECSManager& ecs, ServiceProvider& services) override { if (Input::GetKeyDown(Input::Q)) { - setSceneComplete(); + goToNextScene(); } Transform& cameraTransform = ecs.getComponent(m_mainCamera); diff --git a/examples/3d-experiments/include/CornellBox.h b/examples/3d-experiments/include/CornellBox.h index 0d773bc..bc20e27 100644 --- a/examples/3d-experiments/include/CornellBox.h +++ b/examples/3d-experiments/include/CornellBox.h @@ -15,7 +15,7 @@ class CornellBox : public Scene3D private: // Inherited via Scene - void onStart(ECSManager& ecs) override + void onStart(ECSManager& ecs, ServiceProvider& services) override { m_debugFly = true; @@ -115,11 +115,11 @@ class CornellBox : public Scene3D ecs.getComponent(m_mainCamera).position = vec3(0, 2.6f, 12.0f); } - void onUpdate(float delta, ECSManager& ecs) override + void onUpdate(ECSManager& ecs, ServiceProvider& services) override { if (Input::GetKeyDown(Input::Q)) { - setSceneComplete(); + goToNextScene(); } auto& cameraTransform = ecs.getComponent(m_mainCamera); diff --git a/examples/3d-experiments/include/MaterialShowcase.h b/examples/3d-experiments/include/MaterialShowcase.h index 328c83d..3c8ec89 100644 --- a/examples/3d-experiments/include/MaterialShowcase.h +++ b/examples/3d-experiments/include/MaterialShowcase.h @@ -15,7 +15,7 @@ class MaterialShowcaseScene : public Scene3D private: // Inherited via Scene - void onStart(ECSManager& ecs) override + void onStart(ECSManager& ecs, ServiceProvider& services) override { m_debugFly = true; @@ -169,11 +169,11 @@ class MaterialShowcaseScene : public Scene3D cameraTransform.rotation.x = -0.95f; } - void onUpdate(float delta, ECSManager& ecs) override + void onUpdate(ECSManager& ecs, ServiceProvider& services) override { if (Input::GetKeyDown(Input::Q)) { - setSceneComplete(); + goToNextScene(); } auto& cameraTransform = ecs.getComponent(m_mainCamera); diff --git a/examples/empty-project/src/main.cpp b/examples/empty-project/src/main.cpp index 50e49f0..74efd1b 100644 --- a/examples/empty-project/src/main.cpp +++ b/examples/empty-project/src/main.cpp @@ -15,9 +15,9 @@ using namespace WeirdEngine; class EmptyScene : public Scene2D { private: - void onStart(ECSManager& ecs) override {} + void onStart(ECSManager& ecs, ServiceProvider& services) override {} - void onUpdate(float delta, ECSManager& ecs) override {} + void onUpdate(ECSManager& ecs, ServiceProvider& services) override {} }; int main(int argc, char* argv[]) diff --git a/examples/opengl-experiments/include/Fire.h b/examples/opengl-experiments/include/Fire.h index be84bd7..eb88895 100644 --- a/examples/opengl-experiments/include/Fire.h +++ b/examples/opengl-experiments/include/Fire.h @@ -37,7 +37,7 @@ class FireScene : public Scene3D RenderPlane m_renderPlane; - void onCreate() override + void onCreate(ECSManager& ecs, ServiceProvider& services) override { // Base shaders @@ -166,7 +166,7 @@ class FireScene : public Scene3D m_bloomRenderTarget->bindColorTextureToFrameBuffer(*m_brightPassTexture); } - void onDestroy() override + void onDestroy(ECSManager& ecs, ServiceProvider& services) override { m_flameShader.free(); m_particlesShader.free(); @@ -207,17 +207,18 @@ class FireScene : public Scene3D } // Inherited via Scene - void onStart(ECSManager& ecs) override + void onStart(ECSManager& ecs, ServiceProvider& services) override { m_debugFly = false; } float m_time = 3.1416f; - void onUpdate(float delta, ECSManager& ecs) override + void onUpdate(ECSManager& ecs, ServiceProvider& services) override { + float delta = services.time().deltaTime(); if (Input::GetKeyDown(Input::Q)) { - setSceneComplete(); + goToNextScene(); } if (m_debugFly) diff --git a/examples/opengl-experiments/include/Lines.h b/examples/opengl-experiments/include/Lines.h index 2557a16..013b1ec 100644 --- a/examples/opengl-experiments/include/Lines.h +++ b/examples/opengl-experiments/include/Lines.h @@ -24,7 +24,7 @@ class LinesScene : public Scene3D Entity m_monkey; - void onCreate() override + void onCreate(ECSManager& ecs, ServiceProvider& services) override { { @@ -51,7 +51,7 @@ class LinesScene : public Scene3D } // Inherited via Scene - void onStart(ECSManager& ecs) override + void onStart(ECSManager& ecs, ServiceProvider& services) override { m_debugFly = false; getLights().push_back(Light{}); @@ -73,11 +73,12 @@ class LinesScene : public Scene3D } } - void onUpdate(float delta, ECSManager& ecs) override + void onUpdate(ECSManager& ecs, ServiceProvider& services) override { + float delta = services.time().deltaTime(); if (Input::GetKeyDown(Input::Q)) { - setSceneComplete(); + goToNextScene(); } Transform& cameraTransform = ecs.getComponent(m_mainCamera); diff --git a/examples/opengl-experiments/include/Water.h b/examples/opengl-experiments/include/Water.h index b1b3e71..6a2a814 100644 --- a/examples/opengl-experiments/include/Water.h +++ b/examples/opengl-experiments/include/Water.h @@ -44,7 +44,7 @@ class WaterScene : public Scene3D // ------------------------------------------------------------------------- - void onCreate() override + void onCreate(ECSManager& ecs, ServiceProvider& services) override { m_waterShader = Shader(ASSETS_PATH "water/shaders/water.vert", ASSETS_PATH "water/shaders/water.frag"); @@ -55,7 +55,7 @@ class WaterScene : public Scene3D m_waterPlane.build(); } - void onDestroy() override + void onDestroy(ECSManager& ecs, ServiceProvider& services) override { m_waterShader.free(); @@ -76,7 +76,7 @@ class WaterScene : public Scene3D float buoyancy = 10.0f; // how strongly this entity is affected by the water surface }; - void onStart(ECSManager& ecs) override + void onStart(ECSManager& ecs, ServiceProvider& services) override { m_debugFly = true; @@ -109,11 +109,12 @@ class WaterScene : public Scene3D float m_time = 0.0f; - void onUpdate(float delta, ECSManager& ecs) override + void onUpdate(ECSManager& ecs, ServiceProvider& services) override { + float delta = services.time().deltaTime(); if (Input::GetKeyDown(Input::Q)) { - setSceneComplete(); + goToNextScene(); } m_time += delta; diff --git a/examples/sample-scenes/include/AquariumScene.h b/examples/sample-scenes/include/AquariumScene.h index c5a3805..810d74c 100644 --- a/examples/sample-scenes/include/AquariumScene.h +++ b/examples/sample-scenes/include/AquariumScene.h @@ -72,15 +72,16 @@ class AquariumScene : public Scene2D static constexpr float TANK_W = TANK_RIGHT - TANK_LEFT; static constexpr float TANK_H = TANK_TOP - TANK_BOTTOM; - void onStart(ECSManager& ecs) override + void onStart(ECSManager& ecs, ServiceProvider& services) override { m_debugInput = true; m_debugFly = true; - m_background.type = BackgroundType::Sky; - m_background.primaryColor = vec4(98, 129, 240, 255) / 255.0f; - m_background.secondaryColor = vec4(86, 208, 197, 255) / 255.0f; - m_background.scale = 0.15f; + auto& background = getBackground(); + background.type = BackgroundType::Sky; + background.primaryColor = vec4(98, 129, 240, 255) / 255.0f; + background.secondaryColor = vec4(86, 208, 197, 255) / 255.0f; + background.scale = 0.15f; Entity globalSettingsEnt = ecs.createEntity(); auto& settings = ecs.addComponent(globalSettingsEnt); @@ -248,13 +249,14 @@ class AquariumScene : public Scene2D } } - void onUpdate(float delta, ECSManager& ecs) override + void onUpdate(ECSManager& ecs, ServiceProvider& services) override { + float delta = services.time().deltaTime(); g_cameraPositon = ecs.getComponent(m_mainCamera).position; if (Input::GetKeyDown(Input::Q) || Input::GetGamepadButtonDown(Input::GamepadButton::North)) { - setSceneComplete(); + goToNextScene(); } m_time += delta; @@ -671,11 +673,12 @@ class AquariumScene : public Scene2D } } - void onEntityShapeCollision(ECSManager& ecs, WeirdEngine::EntityShapeCollisionEvent& event) override + void onEntityShapeCollision(ECSManager& ecs, ServiceProvider& services, + WeirdEngine::EntityShapeCollisionEvent& event) override { if (std::rand() % 8 == 0) { - playSound({0.015f, 150.0f + (std::rand() % 150), true, vec3(event.raw.position, 0.0f), 1}); + services.audio().playSound({0.015f, 150.0f + (std::rand() % 150), true, vec3(event.raw.position, 0.0f), 1}); } auto eelArray = ecs.getComponentArray(); @@ -696,7 +699,8 @@ class AquariumScene : public Scene2D } } - void onEntityCollision(ECSManager& ecs, WeirdEngine::EntityCollisionEvent& event) override + void onEntityCollision(ECSManager& ecs, ServiceProvider& services, + WeirdEngine::EntityCollisionEvent& event) override { Entity a = event.entityA; Entity b = event.entityB; @@ -706,7 +710,7 @@ class AquariumScene : public Scene2D if (std::rand() % 10 == 0) { - playSound({0.01f, 300.0f + (std::rand() % 200), true, vec3(0.0f), 1}); + services.audio().playSound({0.01f, 300.0f + (std::rand() % 200), true, vec3(0.0f), 1}); } if (ecs.hasComponent(a) && ecs.hasComponent(b)) diff --git a/examples/sample-scenes/include/CollisionHandling.h b/examples/sample-scenes/include/CollisionHandling.h index 489949e..01ea886 100644 --- a/examples/sample-scenes/include/CollisionHandling.h +++ b/examples/sample-scenes/include/CollisionHandling.h @@ -12,7 +12,7 @@ class CollisionHandlingScene : public Scene2D private: // Inherited via Scene - void onStart(ECSManager& ecs) override + void onStart(ECSManager& ecs, ServiceProvider& services) override { m_debugInput = true; m_debugFly = true; @@ -58,16 +58,16 @@ class CollisionHandlingScene : public Scene2D ecs.getComponent(m_mainCamera).position = g_cameraPositon; } - void onUpdate(float delta, ECSManager& ecs) override + void onUpdate(ECSManager& ecs, ServiceProvider& services) override { if (Input::GetKeyDown(Input::Q) || Input::GetGamepadButtonDown(Input::GamepadButton::North)) { - setSceneComplete(); + goToNextScene(); } } float m_lastTime = 0.0f; - void onCollision(Simulation2D& simulation, WeirdEngine::CollisionEvent& event) override + void onPhysicsRigidBodyCollision(Simulation2D& simulation, WeirdEngine::CollisionEvent& event) override { float t = getTime(); if (t - m_lastTime < 0.1f) diff --git a/examples/sample-scenes/include/DestroyScene.h b/examples/sample-scenes/include/DestroyScene.h index d794572..b419765 100644 --- a/examples/sample-scenes/include/DestroyScene.h +++ b/examples/sample-scenes/include/DestroyScene.h @@ -24,7 +24,7 @@ class DestroyScene : public Scene2D float m_timer = 0.0f; // Inherited via Scene - void onStart(ECSManager& ecs) override + void onStart(ECSManager& ecs, ServiceProvider& services) override { m_debugInput = true; m_debugFly = true; @@ -32,13 +32,14 @@ class DestroyScene : public Scene2D ecs.getComponent(m_mainCamera).position = g_cameraPositon; } - void onUpdate(float delta, ECSManager& ecs) override + void onUpdate(ECSManager& ecs, ServiceProvider& services) override { + float delta = services.time().deltaTime(); g_cameraPositon = ecs.getComponent(m_mainCamera).position; if (Input::GetKeyDown(Input::Q) || Input::GetGamepadButtonDown(Input::GamepadButton::North)) { - setSceneComplete(); + goToNextScene(); } m_timer += delta; @@ -152,7 +153,8 @@ class DestroyScene : public Scene2D } } - void onEntityCollision(ECSManager& ecs, WeirdEngine::EntityCollisionEvent& event) override + void onEntityCollision(ECSManager& ecs, ServiceProvider& services, + WeirdEngine::EntityCollisionEvent& event) override { if (std::rand() % 5 != 0) return; @@ -166,10 +168,11 @@ class DestroyScene : public Scene2D ecs.getComponent(a).collisionCount++; } - playSound({0.02f, 400.0f + (std::rand() % 200), false, vec3(0.0f), 1}); + services.audio().playSound({0.02f, 400.0f + (std::rand() % 200), false, vec3(0.0f), 1}); } - void onEntityShapeCollision(ECSManager& ecs, WeirdEngine::EntityShapeCollisionEvent& event) override + void onEntityShapeCollision(ECSManager& ecs, ServiceProvider& services, + WeirdEngine::EntityShapeCollisionEvent& event) override { if (std::rand() % 20 == 0) { @@ -195,7 +198,7 @@ class DestroyScene : public Scene2D if (std::rand() % 5 == 0) { - playSound({0.02f, 200.0f + (std::rand() % 100), false, vec3(event.raw.position, 0.0f), 1}); + services.audio().playSound({0.02f, 200.0f + (std::rand() % 100), false, vec3(event.raw.position, 0.0f), 1}); } } }; diff --git a/examples/sample-scenes/include/ImageScene.h b/examples/sample-scenes/include/ImageScene.h index 8fddbb9..8580bea 100644 --- a/examples/sample-scenes/include/ImageScene.h +++ b/examples/sample-scenes/include/ImageScene.h @@ -18,7 +18,7 @@ class ImageScene : public Scene2D std::string imagePath = ASSETS_PATH "jimmy.jpg"; // Inherited via Scene - void onStart(ECSManager& ecs) override + void onStart(ECSManager& ecs, ServiceProvider& services) override { m_debugInput = true; m_debugFly = true; @@ -167,11 +167,11 @@ class ImageScene : public Scene2D return closestIndex; } - void onUpdate(float delta, ECSManager& ecs) override + void onUpdate(ECSManager& ecs, ServiceProvider& services) override { if (Input::GetKeyDown(Input::Q) || Input::GetGamepadButtonDown(Input::GamepadButton::North)) { - setSceneComplete(); + goToNextScene(); } // Get colors diff --git a/examples/sample-scenes/include/LifeScene.h b/examples/sample-scenes/include/LifeScene.h index 8f7c2b4..9a6717e 100644 --- a/examples/sample-scenes/include/LifeScene.h +++ b/examples/sample-scenes/include/LifeScene.h @@ -27,7 +27,7 @@ class LifeScene : public Scene2D private: // Inherited via Scene - void onStart(ECSManager& ecs) override + void onStart(ECSManager& ecs, ServiceProvider& services) override { m_debugInput = true; m_debugFly = true; @@ -89,13 +89,14 @@ class LifeScene : public Scene2D ecs.getComponent(m_mainCamera).position = g_cameraPositon; } - void onUpdate(float delta, ECSManager& ecs) override + void onUpdate(ECSManager& ecs, ServiceProvider& services) override { + float delta = services.time().deltaTime(); g_cameraPositon = ecs.getComponent(m_mainCamera).position; if (Input::GetKeyDown(Input::Q) || Input::GetGamepadButtonDown(Input::GamepadButton::North)) { - setSceneComplete(); + goToNextScene(); } updateHeads(delta, ecs); diff --git a/examples/sample-scenes/include/MouseCollisionScene.h b/examples/sample-scenes/include/MouseCollisionScene.h index 3d05d5f..fe0c773 100644 --- a/examples/sample-scenes/include/MouseCollisionScene.h +++ b/examples/sample-scenes/include/MouseCollisionScene.h @@ -20,7 +20,7 @@ class MouseCollisionScene : public Scene2D Entity m_cursorShape; // Inherited via Scene - void onStart(ECSManager& ecs) override + void onStart(ECSManager& ecs, ServiceProvider& services) override { m_debugInput = true; m_debugFly = true; @@ -78,13 +78,13 @@ class MouseCollisionScene : public Scene2D ecs.getComponent(m_mainCamera).position = g_cameraPositon; } - void onUpdate(float delta, ECSManager& ecs) override + void onUpdate(ECSManager& ecs, ServiceProvider& services) override { g_cameraPositon = ecs.getComponent(m_mainCamera).position; if (Input::GetKeyDown(Input::Q) || Input::GetGamepadButtonDown(Input::GamepadButton::North)) { - setSceneComplete(); + goToNextScene(); } // Move wall to mouse @@ -103,7 +103,8 @@ class MouseCollisionScene : public Scene2D } } - void onEntityCollision(ECSManager& ecs, WeirdEngine::EntityCollisionEvent& event) override + void onEntityCollision(ECSManager& ecs, ServiceProvider& services, + WeirdEngine::EntityCollisionEvent& event) override { if (ecs.hasComponent(event.entityA)) { @@ -138,7 +139,8 @@ class MouseCollisionScene : public Scene2D } } - void onEntityShapeCollision(ECSManager& ecs, WeirdEngine::EntityShapeCollisionEvent& event) override + void onEntityShapeCollision(ECSManager& ecs, ServiceProvider& services, + WeirdEngine::EntityShapeCollisionEvent& event) override { if (ecs.hasComponent(event.entity)) { diff --git a/examples/sample-scenes/include/RopeScene.h b/examples/sample-scenes/include/RopeScene.h index 9247159..a1c9597 100644 --- a/examples/sample-scenes/include/RopeScene.h +++ b/examples/sample-scenes/include/RopeScene.h @@ -18,7 +18,7 @@ class RopeScene : public Scene2D std::vector m_balls; - void onStart(ECSManager& ecs) override + void onStart(ECSManager& ecs, ServiceProvider& services) override { m_debugInput = true; m_debugFly = true; @@ -150,13 +150,14 @@ class RopeScene : public Scene2D m_lastSpawnTime = getTime(); } - void onUpdate(float delta, ECSManager& ecs) override + void onUpdate(ECSManager& ecs, ServiceProvider& services) override { + float delta = services.time().deltaTime(); g_cameraPositon = ecs.getComponent(m_mainCamera).position; if (Input::GetKeyDown(Input::Q) || Input::GetGamepadButtonDown(Input::GamepadButton::North)) { - setSceneComplete(); + goToNextScene(); } // Animate custom shape over time diff --git a/examples/sample-scenes/include/ServiceShowcaseScene.h b/examples/sample-scenes/include/ServiceShowcaseScene.h new file mode 100644 index 0000000..196b73b --- /dev/null +++ b/examples/sample-scenes/include/ServiceShowcaseScene.h @@ -0,0 +1,580 @@ +#pragma once + +#include +#include +#include +#include + +#include + +#include "globals.h" +#include "weird-engine/math/Default2DSDFs.h" +#include "weird-renderer/core/Display.h" + +using namespace WeirdEngine; + +// ============================================================================ +// Service showcase scene. +// +// Demonstrates the new system + ServiceProvider API: most Scene callbacks are +// thin wrappers that delegate to a plain free function (a "system") of the +// form: +// +// void system(ECSManager& ecs, ServiceProvider& services, ...); +// +// (onRender and onImGuiRender are inlined in the scene instead, see below.) +// +// Systems never touch Scene internals: everything they need is either on the +// ECSManager& or on the ServiceProvider& passed to the callback. Even the +// scene's own state lives in the ECS (see State below): a single "state" +// entity owns it, and systems reach it through the component array. +// +// Callbacks covered: onCreate, onStart, onUpdate (4 systems), onRender, +// onImGuiRender, onPhysicsStep, onPhysicsRigidBodyCollision, onPhysicsShapeCollision, +// onEntityCollision, onEntityShapeCollision, onDestroy. +// +// Controls: +// Left click spawn a ball at the cursor +// Space pause / resume the physics simulation +// Up / Down gravity up / down (live) +// Left / Right damping down / up (live) +// Ctrl+S save the scene to assets/scenes/service_showcase.weird +// Ctrl+L load the saved scene into the current one +// Q go to the next scene +// ============================================================================ + +namespace ServiceShowcase +{ + // Game-defined per-body user data: derives from BodyUserData and sets a + // type discriminator so the physics callbacks can cast safely (see + // getUserDataAs). + struct CharacterData : BodyUserData + { + static constexpr int TYPE = 1; + float restitution = 1.2f; + }; + + // Scene state as an ECS component: attached to a single "state" entity + // created by onCreateSystem. This is the ECS-native way for systems to + // share state instead of passing a struct around. + struct State + { + Entity timeText = INVALID_ENTITY; + Entity entitiesText = INVALID_ENTITY; + Entity collisionsText = INVALID_ENTITY; + Entity hintsText = INVALID_ENTITY; + + float spawnTimer = 0.0f; + float leaderAngle = 0.0f; + int ballsSpawned = 0; + float gravity = -9.8f; + float damping = 0.001f; + float initialTime = 0.0f; + + // Heap-allocated (see onCreateSystem): the simulation owns per-body + // user data and deletes it when the body is removed or the scene ends. + CharacterData* characterData = nullptr; + + // Each counter is only touched from the main thread (collision + // callbacks); plain ints are fine. Atomicity would require a custom + // component manager since components must be copyable for the ECS + // storage. + int entityCollisions = 0; + int shapeCollisions = 0; + }; + + // State entity lookup: there is exactly one State component in the scene + // (created by onCreateSystem), so it always lives at index 0 of the State + // component array. + inline State& getState(ECSManager& ecs, ServiceProvider& services) + { + return ecs.getComponentArray()->getDataAtIdx(0); + } + + inline Entity spawnBall(ECSManager& ecs, vec2 position) + { + Entity entity = ecs.createEntity(); + auto& t = ecs.addComponent(entity); + t.position = vec3(position, 0.0f); + + auto& dot = ecs.addComponent(entity); + dot.materialId = DisplaySettings::LightGray; + + auto& rb = ecs.addComponent(entity); + rb.velocity = vec2((std::rand() % 200 - 100) / 40.0f, 0.0f); + ecs.setComponentDirty(rb); + + return entity; + } + + // ---------------------------------------------------------------- onCreate + // Runs after the ECS, materials and camera exist, before any scene file is + // loaded and before onStart. Creates the "state" entity that owns the + // scene's State component. + inline void onCreateSystem(ECSManager& ecs, ServiceProvider& services) + { + Entity stateEntity = ecs.createEntity(); + ecs.addComponent(stateEntity); + services.tags().tag(stateEntity, "state"); + services.serialization().blacklistEntity(stateEntity); + + State& state = getState(ecs, services); + state.initialTime = services.time().time(); + state.characterData = new CharacterData(); + std::cout << "[ServiceShowcase] onCreate at simulation time " << state.initialTime << "s" << std::endl; + } + + // ----------------------------------------------------------------- onStart + inline void onStartSystem(ECSManager& ecs, ServiceProvider& services) + { + State& state = getState(ecs, services); + + // Debug flags through the provider + services.debug().setDebugFly(true); + services.debug().setDebugInput(true); + + // Materials through the provider + Material3D& floorMaterial = services.materials().createMaterial(); + floorMaterial.color = vec4(0.2f, 0.5f, 0.9f, 1.0f); + floorMaterial.metallic = 0.5f; + + Material3D& ringMaterial = services.materials().createMaterial(); + ringMaterial.color = vec4(0.9f, 0.3f, 0.2f, 1.0f); + + // Register a custom SDF: a ring (outer circle minus inner circle) + ShapeId ringShape; + { + auto x = std::make_shared(0); + auto y = std::make_shared(1); + auto outerRadius = std::make_shared(2); + auto innerRadius = std::make_shared(3); + + auto outer = std::make_shared(x, y, outerRadius); + auto inner = + std::make_shared(-1.0f, std::make_shared(x, y, innerRadius)); + auto ring = std::make_shared(outer, inner); + + ringShape = services.shapes().registerSDF(ring); + + float vars[8] = {15.0f, 20.0f, 5.0f, 4.0f}; + Entity ringEntity = + services.shapes().addShape(ringShape, vars, ringMaterial, CombinationType::Addition, true, 0); + ecs.getComponent(ringEntity).smoothFactor = 2.0f; + } + + // Floor + { + float vars[8] = {15.0f, -50.0f, 250.0f, 50.0f}; + Entity floor = + services.shapes().addShape(DefaultShapes::BOX, vars, floorMaterial, CombinationType::SmoothAddition); + services.tags().tag(floor, "floor"); + ecs.getComponent(floor).smoothFactor = 3.0f; + } + + // Pit: a subtraction shape; balls that roll into it fall through + { + float vars[8] = {30.0f, 5.0f, 4.0f}; + services.shapes().addShape(DefaultShapes::CIRCLE, vars, 0, CombinationType::Subtraction, true, + CustomShape::GLOBAL_GROUP); + } + + // Camera + ecs.getComponent(services.render().getCameraEntity()).position = g_cameraPositon; + + // Leader ball: orbits a point (moved by FollowSystem through the + // physics simulation) + { + Entity leader = ecs.createEntity(); + auto& t = ecs.addComponent(leader); + t.position = vec3(15.0f, 12.0f, 0.0f); + + auto& dot = ecs.addComponent(leader); + dot.materialId = DisplaySettings::Yellow; + + ecs.addComponent(leader); + services.tags().tag(leader, "leader"); + } + + // Character ball: carries per-body user data so the physics callbacks + // can identify and tune it without touching the ECS (the physics + // thread must not access the ECS). The data pointer is read fresh from + // the RigidBody2D component at spawn time. + { + Entity character = ecs.createEntity(); + auto& t = ecs.addComponent(character); + t.position = vec3(15.0f, 15.0f, 0.0f); + + auto& dot = ecs.addComponent(character); + dot.materialId = DisplaySettings::Orange; + + auto& rb = ecs.addComponent(character); + services.tags().tag(character, "character"); + services.physics().setUserData(rb.simulationId, state.characterData); + } + + // Initial ball pile + for (int i = 0; i < 12; ++i) + { + float x = 8.0f + (i % 4) * 3.0f; + float y = 28.0f + (i / 4) * 4.0f; + spawnBall(ecs, vec2(x, y)); + } + + // UI text (screen space; blacklisted so it is never serialized) + { + auto makeText = [&](const char* initial, vec2 screenPosition, Entity& outEntity, int material) + { + outEntity = ecs.createEntity(); + services.serialization().blacklistEntity(outEntity); + + auto& t = ecs.addComponent(outEntity); + t.position = vec3(screenPosition, 0.0f); + + auto& text = ecs.addComponent(outEntity); + text.text = initial; + text.material = material; + text.horizontalAlignment = TextRenderer::HorizontalAlignment::Left; + text.verticalAlignment = TextRenderer::VerticalAlignment::Bottom; + }; + + makeText("time 0.0s", vec2(10.0f, static_cast(Display::height) - 10.0f), state.timeText, + DisplaySettings::LightGreen); + makeText("entities 0", vec2(10.0f, static_cast(Display::height) - 22.0f), state.entitiesText, + DisplaySettings::Cyan); + makeText("collisions 0 / 0", vec2(10.0f, static_cast(Display::height) - 34.0f), state.collisionsText, + DisplaySettings::Magenta); + makeText("click: spawn | space: pause | arrows: gravity/damping | ctrl+s: save | ctrl+l: load | q: next", + vec2(10.0f, 10.0f), state.hintsText, DisplaySettings::Orange); + } + } + + // ----------------------------------------------------- update: spawn system + // Periodically drops a new ball from the top of the world. + inline void spawnSystem(ECSManager& ecs, ServiceProvider& services) + { + State& state = getState(ecs, services); + + state.spawnTimer += services.time().deltaTime(); + if (state.spawnTimer > 0.35f && ecs.getEntityCount() < 160) + { + state.spawnTimer = 0.0f; + float x = 3.0f + static_cast(std::rand() % 240) / 10.0f; + spawnBall(ecs, vec2(x, 35.0f)); + state.ballsSpawned++; + } + } + + // ----------------------------------------------------- update: input system + inline void inputSystem(ECSManager& ecs, ServiceProvider& services) + { + State& state = getState(ecs, services); + + // Scene transition through the provider + if (Input::GetKeyDown(Input::Q) || Input::GetGamepadButtonDown(Input::GamepadButton::North)) + { + services.sceneControl().goToNextScene(); + } + + // Pause / resume through the provider + if (Input::GetKeyDown(Input::Space)) + { + if (services.physics().isPaused()) + services.physics().resume(); + else + services.physics().pause(); + } + + // Real-time physics settings through the provider + if (Input::GetKeyDown(Input::Up)) + { + state.gravity = std::clamp(state.gravity + 1.0f, -30.0f, 0.0f); + services.physics().setGravity(state.gravity); + } + if (Input::GetKeyDown(Input::Down)) + { + state.gravity = std::clamp(state.gravity - 1.0f, -30.0f, 0.0f); + services.physics().setGravity(state.gravity); + } + if (Input::GetKeyDown(Input::Left)) + { + state.damping = std::max(0.0f, state.damping - 0.05f); + services.physics().setDamping(state.damping); + } + if (Input::GetKeyDown(Input::Right)) + { + state.damping += 0.05f; + services.physics().setDamping(state.damping); + } + + // Spawn a ball where the mouse points + if (Input::GetMouseButtonDown(Input::LeftClick) && !Input::isUIClick()) + { + auto& cameraTransform = ecs.getComponent(services.render().getCameraEntity()); + vec2 mouseWorld = ECS::Camera::screenPositionToWorldPosition2D( + cameraTransform, vec2(Input::GetMouseX(), Input::GetMouseY())); + spawnBall(ecs, mouseWorld); + state.ballsSpawned++; + } + + // Serialization through the provider + if (Input::GetKeyDown(Input::S) && Input::GetKey(Input::LeftCtrl)) + { + services.serialization().saveScene(ASSETS_PATH "scenes/service_showcase.weird"); + std::cout << "[ServiceShowcase] scene saved" << std::endl; + } + if (Input::GetKeyDown(Input::L) && Input::GetKey(Input::LeftCtrl)) + { + // blacklistEntities = true: entities loaded from disk are excluded + // from future saves, so saving again does not duplicate them. + TagMap loaded = services.serialization().loadWeirdFile(ASSETS_PATH "scenes/service_showcase.weird", true); + for (const auto& [name, entity] : loaded) + std::cout << "[ServiceShowcase] loaded tag '" << name << "' -> entity " << entity << std::endl; + } + } + + // ----------------------------------------------------- update: follow system + // Orbits the "leader" ball around a point by writing its velocity straight + // into the physics simulation through the provider. + inline void followSystem(ECSManager& ecs, ServiceProvider& services) + { + State& state = getState(ecs, services); + + Entity leader = services.tags().getEntityByTag("leader"); + if (leader == INVALID_ENTITY) + return; + + state.leaderAngle += services.time().deltaTime() * 1.5f; + + glm::vec2 center(15.0f, 18.0f); + glm::vec2 target = center + 6.0f * glm::vec2(std::cos(state.leaderAngle), std::sin(state.leaderAngle)); + + auto& rb = ecs.getComponent(leader); + glm::vec2 current = glm::vec2(ecs.getComponent(leader).position); + services.physics().sim().setVelocity(rb.simulationId, (target - current) * 2.0f); + } + + // -------------------------------------------------------- update: ui system + inline void uiSystem(ECSManager& ecs, ServiceProvider& services) + { + State& state = getState(ecs, services); + + char buffer[64]; + + auto& timeText = ecs.getComponent(state.timeText); + std::snprintf(buffer, sizeof(buffer), "time %.1fs", services.time().time()); + timeText.text = buffer; + ecs.setComponentDirty(timeText); + + auto& entitiesText = ecs.getComponent(state.entitiesText); + std::snprintf(buffer, sizeof(buffer), "entities %d (balls spawned: %d)", services.ecs().getEntityCount(), + state.ballsSpawned); + entitiesText.text = buffer; + ecs.setComponentDirty(entitiesText); + + auto& collisionsText = ecs.getComponent(state.collisionsText); + std::snprintf(buffer, sizeof(buffer), "collisions %d body / %d shape", state.entityCollisions, + state.shapeCollisions); + collisionsText.text = buffer; + ecs.setComponentDirty(collisionsText); + } + + // ------------------------------------------------------------ onPhysicsStep + // Physics thread. Simulation-coupled logic only: no ECS access here (the + // engine does not allow touching the ECS from the physics thread), so the + // step counter is a local static rather than a component. + // Applies a gentle "wind" to every body every 60 steps. Physics-thread + // systems only receive the Simulation2D& (no ECS, no ServiceProvider). + inline void onPhysicsStepSystem(Simulation2D& simulation) + { + static int stepCounter = 0; + if (++stepCounter % 60 != 0) + return; + + for (SimulationID id = 0; id < simulation.getSize(); ++id) + simulation.addImpulseForce(id, vec2(0.5f, 0.0f)); + + // Per-body user data: iterate only the bodies that opted in, and + // branch on the type discriminator. No ECS access needed here. + simulation.forEachUserData( + [&](SimulationID id, BodyUserData& data) + { + if (data.type == CharacterData::TYPE) + { + // Give the character a little extra lift each gust + simulation.addImpulseForce(id, vec2(0.0f, 4.0f)); + } + }); + } + + // -------------------------------------------------------------- onPhysicsRigidBodyCollision + // Physics thread. Body-body collisions: push the pair apart based on their + // relative velocity. + inline void onPhysicsRigidBodyCollisionSystem(Simulation2D& simulation, CollisionEvent& event) + { + vec2 va = simulation.getPhysicsVelocity(event.bodyA); + vec2 vb = simulation.getPhysicsVelocity(event.bodyB); + + vec2 separation = (va - vb) * 0.05f; + simulation.addImpulseForce(event.bodyA, -separation); + simulation.addImpulseForce(event.bodyB, separation); + } + + // --------------------------------------------------------- onPhysicsShapeCollision + // Physics thread. Shape collisions: bounce the body off the shape normal + // when the penetration is deep enough. The character (identified via its + // per-body user data) gets a bouncier, more slippery response by tuning + // the event before the solver reads it. + inline void onPhysicsShapeCollisionSystem(Simulation2D& simulation, ShapeCollisionEvent& event) + { + if (event.state == CollisionState::START && event.penetration > 0.1f) + { + simulation.addImpulseForce(event.body, event.normal * (2.0f + event.penetration * 10.0f)); + } + + // Type-checked cast: nullptr unless this body carries CharacterData + if (auto* data = simulation.getUserDataAs(event.body)) + { + event.absortion *= 0.5f; // less normal damping = bouncier + event.friction *= 0.5f; // slippery character + } + } + + // ------------------------------------------------------- onEntityCollision + // Main thread. Body-body collisions mapped to entities: count them, flash + // the colliding ball and play a sound through the provider. + inline void onEntityCollisionSystem(ECSManager& ecs, ServiceProvider& services, EntityCollisionEvent& event) + { + State& state = getState(ecs, services); + state.entityCollisions++; + + if (event.entityA != INVALID_ENTITY && ecs.hasComponent(event.entityA)) + { + auto& dot = ecs.getComponent(event.entityA); + dot.materialId = DisplaySettings::Orange; + ecs.setComponentDirty(dot); + } + + services.audio().playSound({0.05f, 300.0f, false, vec3(0.0f), 1}); + } + + // --------------------------------------------------- onEntityShapeCollision + // Main thread. Shape collisions mapped to entities: count them and play a + // spatial sound at the contact point. + inline void onEntityShapeCollisionSystem(ECSManager& ecs, ServiceProvider& services, + EntityShapeCollisionEvent& event) + { + State& state = getState(ecs, services); + state.shapeCollisions++; + + if (event.entity != INVALID_ENTITY) + { + float frequency = 200.0f + static_cast(state.shapeCollisions % 40) * 5.0f; + services.audio().playSound({0.04f, frequency, true, vec3(event.raw.position, 0.0f), 1}); + } + } + + // ---------------------------------------------------------------- onDestroy + inline void onDestroySystem(ECSManager& ecs, ServiceProvider& services) + { + std::cout << "[ServiceShowcase] scene destroyed at " << services.time().time() << "s" << std::endl; + + State& state = getState(ecs, services); + state.ballsSpawned = 0; + state.entityCollisions = 0; + state.shapeCollisions = 0; + } +} // namespace ServiceShowcase + +class ServiceShowcaseScene : public Scene2D +{ +public: + ServiceShowcaseScene() = default; + +private: + // Most callbacks are thin wrappers around a system. The ServiceProvider + // only reaches callbacks through their `services` parameter (getServices() + // is private); the scene owns no state at all (it lives in the State + // component on the "state" entity). onRender and onImGuiRender are + // inlined below instead of using systems. + + void onCreate(ECSManager& ecs, ServiceProvider& services) override + { + ServiceShowcase::onCreateSystem(ecs, services); + } + + void onStart(ECSManager& ecs, ServiceProvider& services) override + { + ServiceShowcase::onStartSystem(ecs, services); + } + + void onUpdate(ECSManager& ecs, ServiceProvider& services) override + { + ServiceShowcase::spawnSystem(ecs, services); + ServiceShowcase::inputSystem(ecs, services); + ServiceShowcase::followSystem(ecs, services); + ServiceShowcase::uiSystem(ecs, services); + } + + void onEntityCollision(ECSManager& ecs, ServiceProvider& services, EntityCollisionEvent& event) override + { + ServiceShowcase::onEntityCollisionSystem(ecs, services, event); + } + + void onEntityShapeCollision(ECSManager& ecs, ServiceProvider& services, EntityShapeCollisionEvent& event) override + { + ServiceShowcase::onEntityShapeCollisionSystem(ecs, services, event); + } + + void onDestroy(ECSManager& ecs, ServiceProvider& services) override + { + ServiceShowcase::onDestroySystem(ecs, services); + } + + // Don't use systems for these callbacks (they are inlined below). + // Note the firing rules: onRender only fires for 3D / both render modes, + // while onImGuiRender fires for every scene, 2D and 3D alike (it is just + // the debug UI). + void onRender(WeirdRenderer::RenderTarget& renderTarget) override + { + static bool logged = false; + if (!logged) + { + logged = true; + std::cout << "[ServiceShowcase] onRender (3D render path)" << std::endl; + } + } + + void onImGuiRender(ECSManager& ecs, ServiceProvider& services) override + { + auto& state = ServiceShowcase::getState(ecs, services); + + ImGui::Text("Time: %.2fs", services.time().time()); + ImGui::Text("Entities: %d", services.ecs().getEntityCount()); + ImGui::Text("Gravity: %.1f | Damping: %.2f | Physics %s", state.gravity, state.damping, + services.physics().isPaused() ? "paused" : "running"); + ImGui::Text("Collisions: %d body / %d shape", state.entityCollisions, state.shapeCollisions); + ImGui::Text("Balls spawned: %d", state.ballsSpawned); + + ImGui::Separator(); + ImGui::Text("Left click: spawn ball | Space: pause/resume"); + ImGui::Text("Up/Down: gravity | Left/Right: damping"); + ImGui::Text("Ctrl+S: save scene | Ctrl+L: load scene | Q: next scene"); + } + + // Physics thread callbacks. Fire on the physics thread mid-step; no ECS + // access here. They delegate to Simulation2D-only systems. + void onPhysicsStep(Simulation2D& simulation) override + { + ServiceShowcase::onPhysicsStepSystem(simulation); + } + + void onPhysicsRigidBodyCollision(Simulation2D& simulation, CollisionEvent& event) override + { + ServiceShowcase::onPhysicsRigidBodyCollisionSystem(simulation, event); + } + + void onPhysicsShapeCollision(Simulation2D& simulation, ShapeCollisionEvent& event) override + { + ServiceShowcase::onPhysicsShapeCollisionSystem(simulation, event); + } +}; diff --git a/examples/sample-scenes/include/ShapesCombinations.h b/examples/sample-scenes/include/ShapesCombinations.h index 958aefe..250bbc9 100644 --- a/examples/sample-scenes/include/ShapesCombinations.h +++ b/examples/sample-scenes/include/ShapesCombinations.h @@ -20,7 +20,7 @@ class ShapeCombinatiosScene : public Scene2D std::vector m_uiPoints; - void onStart(ECSManager& ecs) override + void onStart(ECSManager& ecs, ServiceProvider& services) override { m_debugInput = true; m_debugFly = true; @@ -89,13 +89,14 @@ class ShapeCombinatiosScene : public Scene2D ecs.getComponent(m_mainCamera).position = g_cameraPositon; } - void onUpdate(float delta, ECSManager& ecs) override + void onUpdate(ECSManager& ecs, ServiceProvider& services) override { + float delta = services.time().deltaTime(); g_cameraPositon = ecs.getComponent(m_mainCamera).position; if (Input::GetKeyDown(Input::Q) || Input::GetGamepadButtonDown(Input::GamepadButton::North)) { - setSceneComplete(); + goToNextScene(); } auto& cameraTransform = ecs.getComponent(m_mainCamera); diff --git a/examples/sample-scenes/include/TextScene.h b/examples/sample-scenes/include/TextScene.h index 7774a40..feae72a 100644 --- a/examples/sample-scenes/include/TextScene.h +++ b/examples/sample-scenes/include/TextScene.h @@ -24,7 +24,7 @@ class TextScene : public Scene2D int m_counter = 0; int m_lastResolutionHash = 0; - void onStart(ECSManager& ecs) override + void onStart(ECSManager& ecs, ServiceProvider& services) override { m_debugInput = true; m_debugFly = true; @@ -123,11 +123,11 @@ class TextScene : public Scene2D } } - void onUpdate(float delta, ECSManager& ecs) override + void onUpdate(ECSManager& ecs, ServiceProvider& services) override { if (Input::GetKeyDown(Input::Q) || Input::GetGamepadButtonDown(Input::GamepadButton::North)) { - setSceneComplete(); + goToNextScene(); } m_counter++; diff --git a/examples/sample-scenes/include/WalkScene.h b/examples/sample-scenes/include/WalkScene.h index d0c76fa..bf5c3a2 100644 --- a/examples/sample-scenes/include/WalkScene.h +++ b/examples/sample-scenes/include/WalkScene.h @@ -30,15 +30,16 @@ class WalkScene : public Scene2D Entity m_head; // Inherited via Scene - void onStart(ECSManager& ecs) override + void onStart(ECSManager& ecs, ServiceProvider& services) override { m_debugInput = true; m_debugFly = true; - m_background.type = BackgroundType::Sky; - m_background.primaryColor = vec4(0.2f, 0.55f, 0.9f, 1.0f); - m_background.secondaryColor = vec4(0.4f, 0.75f, 0.85f, 1.0f); - m_background.scale = 0.2f; + auto& background = getBackground(); + background.type = BackgroundType::Sky; + background.primaryColor = vec4(0.2f, 0.55f, 0.9f, 1.0f); + background.secondaryColor = vec4(0.4f, 0.75f, 0.85f, 1.0f); + background.scale = 0.2f; auto tags = loadWeirdFile(ASSETS_PATH "man.weird"); @@ -72,13 +73,15 @@ class WalkScene : public Scene2D ecs.setComponentDirty(settings); } - void onUpdate(float delta, ECSManager& ecs) override + void onUpdate(ECSManager& ecs, ServiceProvider& services) override { + float delta = services.time().deltaTime(); + g_cameraPositon = ecs.getComponent(m_mainCamera).position; if (Input::GetKeyDown(Input::Q) || Input::GetGamepadButtonDown(Input::GamepadButton::North)) { - setSceneComplete(); + goToNextScene(); } updatePhysics(delta, ecs); @@ -167,7 +170,8 @@ class WalkScene : public Scene2D m_feetTouching = false; } - void onEntityCollision(ECSManager& ecs, WeirdEngine::EntityCollisionEvent& event) override + void onEntityCollision(ECSManager& ecs, ServiceProvider& services, + WeirdEngine::EntityCollisionEvent& event) override { Entity entityA = event.entityA; Entity entityB = event.entityB; @@ -178,7 +182,8 @@ class WalkScene : public Scene2D } } - void onEntityShapeCollision(ECSManager& ecs, WeirdEngine::EntityShapeCollisionEvent& event) override + void onEntityShapeCollision(ECSManager& ecs, ServiceProvider& services, + WeirdEngine::EntityShapeCollisionEvent& event) override { Entity entity = event.entity; if (ecs.hasComponent(entity)) diff --git a/examples/sample-scenes/src/main.cpp b/examples/sample-scenes/src/main.cpp index 19f07eb..9e31fa0 100644 --- a/examples/sample-scenes/src/main.cpp +++ b/examples/sample-scenes/src/main.cpp @@ -8,6 +8,7 @@ #include "LifeScene.h" #include "MouseCollisionScene.h" #include "RopeScene.h" +#include "ServiceShowcaseScene.h" #include "ShapesCombinations.h" #include "TextScene.h" #include "WalkScene.h" @@ -21,6 +22,7 @@ int main(int argc, char* argv[]) { SceneManager& sceneManager = SceneManager::getInstance(); + sceneManager.registerScene("service-showcase"); sceneManager.registerScene("shapes"); sceneManager.registerScene("rope"); sceneManager.registerScene("text"); diff --git a/tools/molecule-editor/include/MoleculeEditor.h b/tools/molecule-editor/include/MoleculeEditor.h index 06114f2..9a50682 100644 --- a/tools/molecule-editor/include/MoleculeEditor.h +++ b/tools/molecule-editor/include/MoleculeEditor.h @@ -125,7 +125,7 @@ class MoleculeEditor : public Scene2D static constexpr float TAG_INNER_RADIUS = 25.0f; static constexpr int TAG_RING_GROUP = 8; - void onStart(ECSManager& ecs) override + void onStart(ECSManager& ecs, ServiceProvider& services) override { m_tempEcs = &ecs; m_debugFly = true; @@ -158,14 +158,14 @@ class MoleculeEditor : public Scene2D } } - void onUpdate(float delta, ECSManager& ecs) override + void onUpdate(ECSManager& ecs, ServiceProvider& services) override { m_tempEcs = &ecs; g_cameraPositon = m_tempEcs->getComponent(m_mainCamera).position; if (Input::GetKeyDown(Input::Q) || Input::GetGamepadButtonDown(Input::GamepadButton::North)) { - setSceneComplete(); + goToNextScene(); return; } @@ -1159,7 +1159,8 @@ class MoleculeEditor : public Scene2D WeirdEngine::Logger::log(loadMsg); } - void onEntityShapeCollision(ECSManager& ecs, WeirdEngine::EntityShapeCollisionEvent& event) override + void onEntityShapeCollision(ECSManager& ecs, ServiceProvider& services, + WeirdEngine::EntityShapeCollisionEvent& event) override { m_tempEcs = &ecs; } diff --git a/tools/scene-editor/include/SceneEditor.h b/tools/scene-editor/include/SceneEditor.h index 5bf99ac..0ea38b4 100644 --- a/tools/scene-editor/include/SceneEditor.h +++ b/tools/scene-editor/include/SceneEditor.h @@ -86,7 +86,7 @@ class SceneEditor : public Scene2D // ===================================================================== // Lifecycle // ===================================================================== - void onStart(ECSManager& ecs, const TagMap& tags) override + void onStart(ECSManager& ecs, ServiceProvider& services) override { m_tempEcs = &ecs; m_debugInput = true; @@ -99,13 +99,13 @@ class SceneEditor : public Scene2D buildParamPanel(); } - void onUpdate(float delta, ECSManager& ecs) override + void onUpdate(ECSManager& ecs, ServiceProvider& services) override { m_tempEcs = &ecs; g_cameraPositon = m_tempEcs->getComponent(m_mainCamera).position; if (Input::GetKeyDown(Input::Q) || Input::GetGamepadButtonDown(Input::GamepadButton::North)) - setSceneComplete(); + goToNextScene(); if (Input::GetKey(Input::LeftCtrl) && Input::GetKeyDown(Input::S)) saveScene(ASSETS_PATH "example.weird"); From ba5853d96fb49629f54670f705ef7b91572e6f62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= <49535803+damacaa@users.noreply.github.com> Date: Mon, 10 Aug 2026 18:01:08 +0200 Subject: [PATCH 07/21] Refactor Scene API to enforce ServiceProvider pattern and inject assets path --- include/weird-engine.h | 4 +- include/weird-engine/Scene.h | 74 +-------- include/weird-engine/SceneManager.h | 6 + .../weird-engine/services/ServiceProvider.h | 146 ++++++++++++++++++ include/weird-renderer/core/Renderer.h | 2 + include/weird-renderer/resources/Shader.h | 4 + include/weird-renderer/resources/Texture.h | 4 + src/weird-engine/Scene.cpp | 146 ++---------------- src/weird-engine/SceneManager.cpp | 1 + src/weird-engine/SceneSerializer.cpp | 4 +- src/weird-renderer/audio/AudioEngine.cpp | 3 - src/weird-renderer/core/Renderer.cpp | 57 ++++--- 12 files changed, 222 insertions(+), 229 deletions(-) diff --git a/include/weird-engine.h b/include/weird-engine.h index a107f23..0c34245 100644 --- a/include/weird-engine.h +++ b/include/weird-engine.h @@ -165,6 +165,7 @@ namespace WeirdEngine else { Input::handleEvent(event); + ctx.renderer.handleEvent(event); } } } @@ -250,8 +251,9 @@ namespace WeirdEngine inline void start(SceneManager& sceneManager, DisplaySettings displaySettings = {}, PhysicsSettings physicsSettings = {}, AudioSettings audioSettings = {}, int argc = 0, - char** argv = nullptr) + char** argv = nullptr, const std::string& assetsPath = ASSETS_PATH) { + sceneManager.setAssetsPath(assetsPath); WeirdEngine::Logger::log("Starting Weird Engine..."); std::string startupScene; diff --git a/include/weird-engine/Scene.h b/include/weird-engine/Scene.h index 9706a08..913dd13 100644 --- a/include/weird-engine/Scene.h +++ b/include/weird-engine/Scene.h @@ -43,11 +43,13 @@ namespace WeirdEngine // Forward declaration – full definition in SceneSerializer.h class SceneSerializer; + class SceneManager; class Scene { // Serialization and the service provider reach into the scene's // private state (storage lives here; the provider is a facade). + friend class SceneManager; friend class SceneSerializer; friend class ServiceProvider; friend struct SerializationService; @@ -147,7 +149,7 @@ namespace WeirdEngine virtual void onStart(ECSManager& ecs, ServiceProvider& services) {} virtual void onUpdate(ECSManager& ecs, ServiceProvider& services) {}; virtual void onDestroy(ECSManager& ecs, ServiceProvider& services) {}; - virtual void onRender(WeirdRenderer::RenderTarget& renderTarget) {}; + virtual void onRender(ECSManager& ecs, WeirdRenderer::RenderTarget& renderTarget, ServiceProvider& services) {}; virtual void onImGuiRender(ECSManager& ecs, ServiceProvider& services) {}; // ---- Main thread collision callbacks (onEntity* family). Fire after @@ -170,71 +172,9 @@ namespace WeirdEngine virtual void onPhysicsRigidBodyCollision(Simulation2D& simulation, WeirdEngine::CollisionEvent& event) {}; virtual void onPhysicsShapeCollision(Simulation2D& simulation, WeirdEngine::ShapeCollisionEvent& event) {}; - // ---- Scene control helper - void goToNextScene(std::string nextScene = "") - { - m_isSceneComplete = true; - m_nextScene = nextScene; - }; - - // Deprecated alias of goToNextScene. - void setSceneComplete(std::string nextScene = "") - { - goToNextScene(nextScene); - }; - - // ---- Shape creation & SDF registration - ShapeId registerSDF(std::shared_ptr sdf); - Entity addShape(ShapeId shapeId, float* variables, uint16_t material, - CombinationType combination = CombinationType::Addition, bool hasCollision = true, - int group = 0); - Entity addShape(ShapeId shapeId, float* variables, const Material3D& material, - CombinationType combination = CombinationType::Addition, bool hasCollision = true, - int group = 0) - { - return addShape(shapeId, variables, material.id, combination, hasCollision, group); - } - - Entity addUIShape(ShapeId shapeId, float* variables, uint16_t material, - CombinationType combination = CombinationType::Addition, int group = 0); - Entity addUIShape(ShapeId shapeId, float* variables, const Material3D& material, - CombinationType combination = CombinationType::Addition, int group = 0) - { - return addUIShape(shapeId, variables, material.id, combination, group); - } - UIShape& addUIShape(ShapeId shapeId, float* variables, Entity& entity, int group = 0); - - // ---- Entity helpers - // Assign a unique tag to an entity. If the tag is already owned by - // another entity, it is moved to this one. An empty name is treated - // as a removal request (equivalent to calling removeTag). - void tag(Entity entity, const std::string& name); - // Remove any tag currently assigned to an entity. - void removeTag(Entity entity); - // Return the tag of an entity, or "" if none. - std::string getEntityTag(Entity entity) const; - // Return the entity that owns a tag, or MAX_ENTITIES if none. - Entity getEntityByTag(const std::string& name) const; - - // Entities in this set will be skipped during scene serialization - void blacklistEntity(Entity e) - { - m_serializationBlacklist.insert(e); - } - - // ---- Persistence & physics queries - // Save the current scene state to a .weird JSON file - void saveScene(const std::string& filename); - - // Dynamically load a .weird file and add its contents to the scene. - // If blacklistEntities is true, all entities created by the load will be - // excluded from future scene serialization. - // Returns a map of tag names to their corresponding entities. - TagMap loadWeirdFile(const std::string& path, bool blacklistEntities = false); - - RaymarchResult raymarch(glm::vec2 origin, glm::vec2 direction, float epsilon = 0.001f, - float maxDistance = 150.0f); + ServiceProvider m_services; + private: // ---- Shared state (available to derived scenes) Entity m_mainCamera; ResourceManager m_resourceManager; @@ -242,7 +182,6 @@ namespace WeirdEngine bool m_debugFly = false; bool m_debugInput = false; - private: // ---- Internal helpers static void handlePhysicsStep(void* userData); static void handleCollision(CollisionEvent& event, void* userData); @@ -291,10 +230,7 @@ namespace WeirdEngine TagMap m_tagToEntity; std::unordered_map m_entityToTag; - // ---- Service provider binding (must be the last declared member: it - // holds references to the members above). float m_lastDelta = 0.0f; - ServiceProvider m_services; }; class Scene2D : public Scene { diff --git a/include/weird-engine/SceneManager.h b/include/weird-engine/SceneManager.h index abcc21a..08860a4 100644 --- a/include/weird-engine/SceneManager.h +++ b/include/weird-engine/SceneManager.h @@ -27,6 +27,11 @@ namespace WeirdEngine return m_physicsSettings; } + void setAssetsPath(const std::string& path) + { + m_assetsPath = path; + } + static SceneManager& getInstance() { static SceneManager _instance; @@ -48,6 +53,7 @@ namespace WeirdEngine int currentSceneIdx = 0; int targetSceneIdx = 0; PhysicsSettings m_physicsSettings; + std::string m_assetsPath; }; // ChatGPT: Template method declarations and definitions are usually placed in header files. diff --git a/include/weird-engine/services/ServiceProvider.h b/include/weird-engine/services/ServiceProvider.h index cbaf8d4..efc73c5 100644 --- a/include/weird-engine/services/ServiceProvider.h +++ b/include/weird-engine/services/ServiceProvider.h @@ -12,9 +12,11 @@ #include "weird-engine/Background.h" #include "weird-engine/ecs/ECS.h" +#include "weird-engine/Input.h" #include "weird-engine/Material3D.h" #include "weird-engine/ResourceManager.h" #include "weird-engine/systems/SDFRenderSystem.h" +#include "weird-engine/Utils.h" #include "weird-engine/vec.h" #include "weird-physics/components/RigidBody.h" #include "weird-physics/Simulation2D.h" @@ -466,14 +468,152 @@ namespace WeirdEngine } }; + struct InputService + { + bool getKey(Input::KeyCode key) const + { + return Input::GetKey(key); + } + bool getKeyDown(Input::KeyCode key) const + { + return Input::GetKeyDown(key); + } + bool getKeyUp(Input::KeyCode key) const + { + return Input::GetKeyUp(key); + } + + float getMouseX() const + { + return Input::GetMouseX(); + } + float getMouseY() const + { + return Input::GetMouseY(); + } + float getMouseDeltaX() const + { + return Input::GetMouseDeltaX(); + } + float getMouseDeltaY() const + { + return Input::GetMouseDeltaY(); + } + float getMouseDeltaXRaw() const + { + return Input::GetMouseDeltaXRaw(); + } + float getMouseDeltaYRaw() const + { + return Input::GetMouseDeltaYRaw(); + } + bool getMouseButton(Input::MouseButton button) const + { + return Input::GetMouseButton(button); + } + bool getMouseButtonDown(Input::MouseButton button) const + { + return Input::GetMouseButtonDown(button); + } + bool getMouseButtonUp(Input::MouseButton button) const + { + return Input::GetMouseButtonUp(button); + } + void setMousePosition(float x, float y) + { + Input::SetMousePosition(x, y); + } + void showMouse() + { + Input::ShowMouse(); + } + void hideMouse() + { + Input::HideMouse(); + } + bool isUIClick() const + { + return Input::isUIClick(); + } + void flagUIClick() + { + Input::flagUIClick(); + } + + bool getGamepadButton(Input::GamepadButton button) const + { + return Input::GetGamepadButton(button); + } + bool getGamepadButtonDown(Input::GamepadButton button) const + { + return Input::GetGamepadButtonDown(button); + } + bool getGamepadButtonUp(Input::GamepadButton button) const + { + return Input::GetGamepadButtonUp(button); + } + float getGamepadAxis(Input::GamepadAxis axis) const + { + return Input::GetGamepadAxis(axis); + } + + void suppressMouseInput() + { + Input::suppressMouseInput(); + } + void suppressKeyboardInput() + { + Input::suppressKeyboardInput(); + } + }; + struct ResourceService { ResourceManager& resourceManager; + std::string assetsBasePath; ResourceManager& resources() { return resourceManager; } + + void setAssetsBasePath(const std::string& path) + { + assetsBasePath = path; + } + + std::string assetPath(const std::string& relative) const + { + return assetsBasePath + relative; + } + + MeshID getMeshId(const std::string& path, Entity entity, bool instancing = false) + { + return resourceManager.getMeshId(assetPath(path).c_str(), entity, instancing); + } + + std::string readTextFile(const std::string& path) const + { + return get_file_contents(path.c_str()); + } + + void writeTextFile(const std::string& path, const std::string& content) const + { + saveToFile(path.c_str(), content); + } + + bool fileExists(const std::string& path) const + { + return checkIfFileExists(path.c_str()); + } + + void ensureDirectory(const std::string& path) const + { + if (!std::filesystem::exists(path)) + { + std::filesystem::create_directory(path); + } + } }; struct DebugService @@ -570,6 +710,11 @@ namespace WeirdEngine return m_debug; } + InputService& input() + { + return m_input; + } + private: ECSManager& m_ecs; TimeService m_time; @@ -583,5 +728,6 @@ namespace WeirdEngine SceneControlService m_sceneControl; ResourceService m_resources; DebugService m_debug; + InputService m_input; }; } // namespace WeirdEngine diff --git a/include/weird-renderer/core/Renderer.h b/include/weird-renderer/core/Renderer.h index 430ee39..b18a398 100644 --- a/include/weird-renderer/core/Renderer.h +++ b/include/weird-renderer/core/Renderer.h @@ -31,6 +31,7 @@ namespace WeirdEngine void render(Scene& scene, const double time, const double delta); void setWindowTitle(const char* name); void setWindowSize(unsigned int width, unsigned int height); + void handleEvent(const SDL_Event& event); SDL_Window* getWindow(); @@ -78,6 +79,7 @@ namespace WeirdEngine std::string m_lastScreenshotPath; // Stats UI (F4) + bool m_showDebugUI = false; bool m_showStatsUI = false; static constexpr int STATS_HISTORY_SIZE = 128; float m_frametimeHistory[STATS_HISTORY_SIZE] = {}; diff --git a/include/weird-renderer/resources/Shader.h b/include/weird-renderer/resources/Shader.h index a6f21f1..5cba4c8 100644 --- a/include/weird-renderer/resources/Shader.h +++ b/include/weird-renderer/resources/Shader.h @@ -22,6 +22,10 @@ namespace WeirdEngine GLuint ID = -1; // Constructor that build the Shader Program from 2 different shaders Shader(const char* vertexFile, const char* fragmentFile); + Shader(const std::string& vertexFile, const std::string& fragmentFile) + : Shader(vertexFile.c_str(), fragmentFile.c_str()) + { + } Shader() {}; // Activates the Shader Program diff --git a/include/weird-renderer/resources/Texture.h b/include/weird-renderer/resources/Texture.h index 841e0e1..e828e35 100644 --- a/include/weird-renderer/resources/Texture.h +++ b/include/weird-renderer/resources/Texture.h @@ -35,6 +35,10 @@ namespace WeirdEngine , height(0) {}; Texture(const char* image); + Texture(const std::string& image) + : Texture(image.c_str()) + { + } Texture(glm::vec4 color); diff --git a/src/weird-engine/Scene.cpp b/src/weird-engine/Scene.cpp index 9614d0a..35971c4 100644 --- a/src/weird-engine/Scene.cpp +++ b/src/weird-engine/Scene.cpp @@ -124,7 +124,7 @@ namespace WeirdEngine // Create camera m_mainCamera = m_ecs.createEntity(); - tag(m_mainCamera, "mainCamera"); + m_services.tags().tag(m_mainCamera, "mainCamera"); Transform& t = m_ecs.addComponent(m_mainCamera); t.rotation = vec3(0, 0, -1.0f); ECS::Camera& c = m_ecs.addComponent(m_mainCamera); @@ -364,20 +364,12 @@ namespace WeirdEngine { if (m_renderMode == RenderMode::RayMarching3D || m_renderMode == RenderMode::RayMarchingBoth) { - onRender(renderTarget); + onRender(m_ecs, renderTarget, m_services); } } // SDFs - ShapeId Scene::registerSDF(std::shared_ptr sdf) - { - m_sdfs.push_back(sdf); - m_simulation2D.setSDFs(m_sdfs); - - return static_cast(m_sdfs.size() - 1); - } - // AUDIO AudioRingBuffer& Scene::getAudioQueue() @@ -397,57 +389,6 @@ namespace WeirdEngine // Serialization - void Scene::tag(Entity entity, const std::string& name) - { - if (name.empty()) - { - removeTag(entity); - return; - } - - // If the tag is already owned by another entity, remove it from that entity - auto existingOwner = m_tagToEntity.find(name); - if (existingOwner != m_tagToEntity.end() && existingOwner->second != entity) - { - m_entityToTag.erase(existingOwner->second); - } - - // Remove any previous tag this entity had - auto existingTag = m_entityToTag.find(entity); - if (existingTag != m_entityToTag.end() && existingTag->second != name) - { - m_tagToEntity.erase(existingTag->second); - } - - m_tagToEntity[name] = entity; - m_entityToTag[entity] = name; - } - - void Scene::removeTag(Entity entity) - { - auto it = m_entityToTag.find(entity); - if (it == m_entityToTag.end()) - return; - m_tagToEntity.erase(it->second); - m_entityToTag.erase(it); - } - - std::string Scene::getEntityTag(Entity entity) const - { - auto it = m_entityToTag.find(entity); - if (it == m_entityToTag.end()) - return ""; - return it->second; - } - - Entity Scene::getEntityByTag(const std::string& name) const - { - auto it = m_tagToEntity.find(name); - if (it == m_tagToEntity.end()) - return MAX_ENTITIES; - return it->second; - } - Entity Scene::getEntityForSimulationId(SimulationID simulationId, std::shared_ptr> rigidBodies) { @@ -457,25 +398,6 @@ namespace WeirdEngine return rigidBodies->getEntityAtIdx(static_cast(simulationId)); } - void Scene::saveScene(const std::string& filename) - { - SceneSerializer::save(*this, filename); - } - - Scene::TagMap Scene::loadWeirdFile(const std::string& path, bool blacklistEntities) - { - TagMap loadedTags; - Entity firstNewEntity = m_ecs.getEntityCount(); - SceneSerializer::load(*this, path, &loadedTags); - if (blacklistEntities) - { - Entity lastNewEntity = m_ecs.getEntityCount(); - for (Entity entity = firstNewEntity; entity < lastNewEntity; ++entity) - m_serializationBlacklist.insert(entity); - } - return loadedTags; - } - void Scene::loadFromWeirdFile(const std::string& path) { SceneSerializer::load(*this, path); @@ -495,8 +417,9 @@ namespace WeirdEngine , m_tags(scene.m_tagToEntity, scene.m_entityToTag) , m_serialization(scene, scene.m_serializationBlacklist, scene.m_sceneFilePath) , m_sceneControl(scene.m_isSceneComplete, scene.m_nextScene) - , m_resources(scene.m_resourceManager) + , m_resources{scene.m_resourceManager, ""} , m_debug(scene.m_debugFly, scene.m_debugInput) + , m_input() { } @@ -507,17 +430,21 @@ namespace WeirdEngine void SerializationService::saveScene(const std::string& filename) { - scene.saveScene(filename); + SceneSerializer::save(scene, filename); } TagMap SerializationService::loadWeirdFile(const std::string& path, bool blacklistEntities) { - return scene.loadWeirdFile(path, blacklistEntities); - } - - Scene::RaymarchResult Scene::raymarch(glm::vec2 origin, glm::vec2 direction, float epsilon, float maxDistance) - { - return raymarchScene(m_ecs, m_sdfs, m_simulation2D, getTime(), origin, direction, epsilon, maxDistance); + TagMap loadedTags; + Entity firstNewEntity = scene.m_ecs.getEntityCount(); + SceneSerializer::load(scene, path, &loadedTags); + if (blacklistEntities) + { + Entity lastNewEntity = scene.m_ecs.getEntityCount(); + for (Entity entity = firstNewEntity; entity < lastNewEntity; ++entity) + scene.m_serializationBlacklist.insert(entity); + } + return loadedTags; } RaymarchResult raymarchScene(ECSManager& ecs, std::vector>& sdfs, @@ -785,7 +712,7 @@ namespace WeirdEngine if (componentIDs.empty()) continue; - std::string tag = getEntityTag(e); + std::string tag = m_services.tags().getEntityTag(e); std::string label = tag.empty() ? ("Entity " + std::to_string(e)) : (tag + " (ID: " + std::to_string(e) + ")"); @@ -822,47 +749,6 @@ namespace WeirdEngine #endif } - Entity Scene::addShape(ShapeId shapeId, float* variables, uint16_t material, CombinationType combination, - bool hasCollision, int group) - { - Entity entity = m_ecs.createEntity(); - CustomShape& shape = m_ecs.addComponent(entity); - shape.distanceFieldId = shapeId; - shape.combination = combination; - shape.hasCollisions = hasCollision; - shape.groupIdx = group; - shape.material = material; - std::copy(variables, variables + 8, shape.parameters); - - return entity; - } - - Entity Scene::addUIShape(ShapeId shapeId, float* variables, uint16_t material, CombinationType combination, - int group) - { - Entity entity = m_ecs.createEntity(); - UIShape& shape = m_ecs.addComponent(entity); - shape.distanceFieldId = shapeId; - shape.combination = combination; - shape.groupIdx = group; - shape.material = material; - std::copy(variables, variables + 8, shape.parameters); - - return entity; - } - - UIShape& Scene::addUIShape(ShapeId shapeId, float* variables, Entity& entity, int group) - { - entity = m_ecs.createEntity(); - UIShape& component = m_ecs.addComponent(entity); - component.distanceFieldId = shapeId; - component.groupIdx = group; - component.smoothFactor = 100.0f; - std::copy(variables, variables + 8, component.parameters); - - return component; - } - Material3D& Scene::createMaterial() { if (m_materialCount >= 16) diff --git a/src/weird-engine/SceneManager.cpp b/src/weird-engine/SceneManager.cpp index 284459a..14c0779 100644 --- a/src/weird-engine/SceneManager.cpp +++ b/src/weird-engine/SceneManager.cpp @@ -46,6 +46,7 @@ namespace WeirdEngine currentScene = nullptr; currentScene = sceneFactories[sceneName](); // Instantiate the scene + currentScene->m_services.resources().setAssetsBasePath(m_assetsPath); currentScene->start(); #ifndef NDEBUG WeirdEngine::Logger::log("Changed to " + sceneName + " scene"); diff --git a/src/weird-engine/SceneSerializer.cpp b/src/weird-engine/SceneSerializer.cpp index c2773e3..8e100bc 100644 --- a/src/weird-engine/SceneSerializer.cpp +++ b/src/weird-engine/SceneSerializer.cpp @@ -418,8 +418,8 @@ namespace WeirdEngine } else { - // Use scene's tag() method to keep both maps in sync - scene.tag(newEntity, name); + // Use service provider to keep both maps in sync + scene.m_services.tags().tag(newEntity, name); } } } diff --git a/src/weird-renderer/audio/AudioEngine.cpp b/src/weird-renderer/audio/AudioEngine.cpp index de7439c..587deae 100644 --- a/src/weird-renderer/audio/AudioEngine.cpp +++ b/src/weird-renderer/audio/AudioEngine.cpp @@ -175,9 +175,6 @@ namespace WeirdEngine if (m_mute) return; - if (Input::GetKeyDown(Input::C)) - playSineSound(getPleasantFrequency(200.0f), 1.0f, 0.1f); - float frictionValue = scene.getFrictionSound(); setFrictionLevel(frictionValue); diff --git a/src/weird-renderer/core/Renderer.cpp b/src/weird-renderer/core/Renderer.cpp index 4db4be3..288be3b 100644 --- a/src/weird-renderer/core/Renderer.cpp +++ b/src/weird-renderer/core/Renderer.cpp @@ -171,34 +171,12 @@ namespace WeirdEngine #ifndef WEIRD_DISABLE_IMGUI { PROFILE_SCOPE("ImGui"); - static bool showDebugUI = false; - static bool showStatsUI = false; - - if (Input::GetKeyDown(Input::F3)) - { - showDebugUI = !showDebugUI; - } - - if (Input::GetKeyDown(Input::F4)) - { - showStatsUI = !showStatsUI; - if (showStatsUI) - Profiler::get().enableRealtime(); - else - Profiler::get().disableRealtime(); - } - - if (Input::GetKeyDown(Input::F11)) - { - bool isFullscreen = (SDL_GetWindowFlags(m_window) & SDL_WINDOW_FULLSCREEN) != 0; - SDL_SetWindowFullscreen(m_window, !isFullscreen); - } ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplSDL3_NewFrame(); ImGui::NewFrame(); - if (showDebugUI) + if (m_showDebugUI) { ImGui::Begin("Engine Settings"); @@ -309,7 +287,7 @@ namespace WeirdEngine ImGui::End(); } - if (showStatsUI) + if (m_showStatsUI) { drawStatsUI(scene, delta); } @@ -852,4 +830,35 @@ namespace WeirdEngine } } // namespace WeirdRenderer +} // namespace WeirdEngine +namespace WeirdEngine +{ + namespace WeirdRenderer + { + void Renderer::handleEvent(const SDL_Event& event) + { + if (event.type == SDL_EVENT_KEY_DOWN && !event.key.repeat) + { + switch (event.key.key) + { + case SDLK_F3: + m_showDebugUI = !m_showDebugUI; + break; + case SDLK_F4: + m_showStatsUI = !m_showStatsUI; + if (m_showStatsUI) + Profiler::get().enableRealtime(); + else + Profiler::get().disableRealtime(); + break; + case SDLK_F11: + { + bool isFullscreen = (SDL_GetWindowFlags(m_window) & SDL_WINDOW_FULLSCREEN) != 0; + SDL_SetWindowFullscreen(m_window, !isFullscreen); + break; + } + } + } + } + } // namespace WeirdRenderer } // namespace WeirdEngine \ No newline at end of file From f07454c7490892fd6a8eecc09d019873bbd89812 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= <49535803+damacaa@users.noreply.github.com> Date: Mon, 10 Aug 2026 18:01:17 +0200 Subject: [PATCH 08/21] Migrate examples and tools to the updated ServiceProvider Scene API --- examples/3d-experiments/include/Classic.h | 18 +-- examples/3d-experiments/include/CornellBox.h | 27 ++-- .../3d-experiments/include/MaterialShowcase.h | 21 +-- examples/opengl-experiments/include/Fire.h | 39 +++--- examples/opengl-experiments/include/Lines.h | 24 ++-- examples/opengl-experiments/include/Water.h | 15 ++- .../sample-scenes/include/AquariumScene.h | 44 +++--- .../sample-scenes/include/CollisionHandling.h | 16 +-- examples/sample-scenes/include/DestroyScene.h | 15 ++- examples/sample-scenes/include/ImageScene.h | 21 +-- examples/sample-scenes/include/LifeScene.h | 16 +-- .../include/MouseCollisionScene.h | 26 ++-- examples/sample-scenes/include/RopeScene.h | 49 +++---- .../include/ServiceShowcaseScene.h | 27 ++-- .../include/ShapesCombinations.h | 41 +++--- examples/sample-scenes/include/TextScene.h | 21 +-- examples/sample-scenes/include/WalkScene.h | 18 +-- .../molecule-editor/include/MoleculeEditor.h | 127 +++++++++--------- tools/scene-editor/include/SceneEditor.h | 73 +++++----- 19 files changed, 333 insertions(+), 305 deletions(-) diff --git a/examples/3d-experiments/include/Classic.h b/examples/3d-experiments/include/Classic.h index aec1e70..808abca 100644 --- a/examples/3d-experiments/include/Classic.h +++ b/examples/3d-experiments/include/Classic.h @@ -14,7 +14,7 @@ class ClassicScene : public Scene3D // Inherited via Scene void onStart(ECSManager& ecs, ServiceProvider& services) override { - m_debugFly = true; + services.debug().setDebugFly(true); auto& redMat = createMaterial(); redMat.color = vec4(.8f, 0.2f, 0.2f, 1.0f); @@ -36,7 +36,7 @@ class ClassicScene : public Scene3D MeshRenderer& mr = ecs.addComponent(entity); - auto id = m_resourceManager.getMeshId(ASSETS_PATH "monkey/demo.gltf", entity, true); + auto id = services.resources().getMeshId(services.resources().assetPath("monkey/demo.gltf"), entity, true); mr.mesh = id; // mr.materialIndex = floorMaterial.id; @@ -56,28 +56,30 @@ class ClassicScene : public Scene3D { float vars1[8] = {25.0f, 10.0f, 5.0f, 0.5f, 13.0f, 0.0f}; // Custom shape - Entity start = addShape(DefaultShapes::STAR, vars1, orangeMat, CombinationType::Addition, true, 0); + Entity start = + services.shapes().addShape(DefaultShapes::STAR, vars1, orangeMat, CombinationType::Addition, true, 0); } { float vars1[8] = {}; // Custom shape - Entity start = addShape(DefaultShapes3D::PLANE, vars1, floorMaterial, CombinationType::Addition, false); + Entity start = services.shapes().addShape(DefaultShapes3D::PLANE, vars1, floorMaterial, + CombinationType::Addition, false); } getLights().push_back(Light{0, glm::vec3(0.0f, 3.0f, 0.0f), 0, glm::vec3(0.35f, 0.45f, 0.5f), glm::vec4(1.0f, 0.95f, 0.9f, 2.0f)}); - ecs.getComponent(m_mainCamera).position = vec3(0, 2, 10); + ecs.getComponent(services.render().getCameraEntity()).position = vec3(0, 2, 10); } void onUpdate(ECSManager& ecs, ServiceProvider& services) override { - if (Input::GetKeyDown(Input::Q)) + if (services.input().getKeyDown(Input::Q)) { - goToNextScene(); + services.sceneControl().goToNextScene(); } - Transform& cameraTransform = ecs.getComponent(m_mainCamera); + Transform& cameraTransform = ecs.getComponent(services.render().getCameraEntity()); return; diff --git a/examples/3d-experiments/include/CornellBox.h b/examples/3d-experiments/include/CornellBox.h index bc20e27..5163a17 100644 --- a/examples/3d-experiments/include/CornellBox.h +++ b/examples/3d-experiments/include/CornellBox.h @@ -17,7 +17,7 @@ class CornellBox : public Scene3D // Inherited via Scene void onStart(ECSManager& ecs, ServiceProvider& services) override { - m_debugFly = true; + services.debug().setDebugFly(true); auto& ballMat = createMaterial(); ballMat.color = vec4(1.0f); @@ -55,47 +55,48 @@ class CornellBox : public Scene3D { std::shared_ptr box = std::make_shared(); - auto boxId = registerSDF(box); + auto boxId = services.shapes().registerSDF(box); // Left { float vars1[8] = {-2.0f * 2.6f, 2.6f, 0.0f, 2.6f, 2.6f, 2.6f}; // Custom shape - Entity start = addShape(boxId, vars1, redMat, CombinationType::Addition, false); + Entity start = services.shapes().addShape(boxId, vars1, redMat, CombinationType::Addition, false); } // Right { float vars1[8] = {2.0f * 2.6f, 2.6f, 0.0f, 2.6f, 2.6f, 2.6f}; // Custom shape - Entity start = addShape(boxId, vars1, greenMat, CombinationType::Addition, false); + Entity start = services.shapes().addShape(boxId, vars1, greenMat, CombinationType::Addition, false); } // Back { float vars1[8] = {0.0f, 2.6f, -2.0f * 2.6f, 3.0f * 2.6f, 2.6f, 2.6f}; // Custom shape - Entity start = addShape(boxId, vars1, whiteMat, CombinationType::Addition, false); + Entity start = services.shapes().addShape(boxId, vars1, whiteMat, CombinationType::Addition, false); } // Top { float vars1[8] = {0.0f, 3.0f * 2.6f, -2.6f, 3.0f * 2.6f, 2.6f, 2.0f * 2.6f}; // Custom shape - Entity start = addShape(boxId, vars1, whiteMat, CombinationType::Addition, false); + Entity start = services.shapes().addShape(boxId, vars1, whiteMat, CombinationType::Addition, false); } // Light hole { float vars1[8] = {0.0f, 2.0f * 2.6f, 0.0f, 0.5f, 1.0f, 0.5f}; // Custom shape - Entity start = addShape(boxId, vars1, whiteMat, CombinationType::Subtraction, false); + Entity start = services.shapes().addShape(boxId, vars1, whiteMat, CombinationType::Subtraction, false); } // Floor { float vars1[8] = {0.0f, -1.0f * 2.6f, -2.6f, 3.0f * 2.6f, 2.6f, 2.0f * 2.6f}; // Custom shape - Entity start = addShape(boxId, vars1, whiteMat, CombinationType::Addition, false); + Entity start = services.shapes().addShape(boxId, vars1, whiteMat, CombinationType::Addition, false); } // { // float vars1[8] = {0.0f, 2.6f, 0.0f, 2.7f, 2.7f, 2.7f}; // Custom shape - // Entity start = addShape(boxId, vars1, DisplaySettings::White, CombinationType::Intersection, false); + // Entity start = services.shapes().addShape(boxId, vars1, DisplaySettings::White, + // CombinationType::Intersection, false); // } } @@ -112,17 +113,17 @@ class CornellBox : public Scene3D // getLights().push_back( // Light{2, glm::vec3(0.0f, 0.0f, 0.0f), 0, glm::vec3(0.0f, 1.0f, 0.0f), glm::vec4(0.0f, 0.0f, 2.0f, 10.0f)}); - ecs.getComponent(m_mainCamera).position = vec3(0, 2.6f, 12.0f); + ecs.getComponent(services.render().getCameraEntity()).position = vec3(0, 2.6f, 12.0f); } void onUpdate(ECSManager& ecs, ServiceProvider& services) override { - if (Input::GetKeyDown(Input::Q)) + if (services.input().getKeyDown(Input::Q)) { - goToNextScene(); + services.sceneControl().goToNextScene(); } - auto& cameraTransform = ecs.getComponent(m_mainCamera); + auto& cameraTransform = ecs.getComponent(services.render().getCameraEntity()); // getLights()[0].position.x = cameraTransform.position.x; // getLights()[0].position.y = cameraTransform.position.y; diff --git a/examples/3d-experiments/include/MaterialShowcase.h b/examples/3d-experiments/include/MaterialShowcase.h index 3c8ec89..5af5e02 100644 --- a/examples/3d-experiments/include/MaterialShowcase.h +++ b/examples/3d-experiments/include/MaterialShowcase.h @@ -17,7 +17,7 @@ class MaterialShowcaseScene : public Scene3D // Inherited via Scene void onStart(ECSManager& ecs, ServiceProvider& services) override { - m_debugFly = true; + services.debug().setDebugFly(true); { Entity entity = ecs.createEntity(); @@ -130,7 +130,8 @@ class MaterialShowcaseScene : public Scene3D floorMaterial.secondaryColor = floorMaterial.color * 0.8f; float vars[8] = {3}; - Entity floor = addShape(DefaultShapes3D::PLANE, vars, floorMaterial, CombinationType::Addition, false); + Entity floor = services.shapes().addShape(DefaultShapes3D::PLANE, vars, floorMaterial, + CombinationType::Addition, false); } auto& mirrorMaterial = createMaterial(); @@ -141,18 +142,18 @@ class MaterialShowcaseScene : public Scene3D { std::shared_ptr box = std::make_shared(); - auto boxId = registerSDF(box); + auto boxId = services.shapes().registerSDF(box); float vars1[8] = {-5.0f, -2.0f, 0.0f, 0.1f, 1.0f, 3.0f}; // Custom shape - Entity start = addShape(boxId, vars1, mirrorMaterial, CombinationType::Addition, false); + Entity start = services.shapes().addShape(boxId, vars1, mirrorMaterial, CombinationType::Addition, false); } { std::shared_ptr box = std::make_shared(); - auto boxId = registerSDF(box); + auto boxId = services.shapes().registerSDF(box); float vars1[8] = {20.0f, -2.0f, 0.0f, 0.1f, 1.0f, 3.0f}; // Custom shape - Entity start = addShape(boxId, vars1, mirrorMaterial, CombinationType::Addition, false); + Entity start = services.shapes().addShape(boxId, vars1, mirrorMaterial, CombinationType::Addition, false); } getLights().push_back(Light{0, glm::vec3(0.0f, 0.0f, 0.0f), 0, normalize(glm::vec3(0.0f, 0.4f, 1.0f)), @@ -164,19 +165,19 @@ class MaterialShowcaseScene : public Scene3D // getLights().push_back( // Light{2, glm::vec3(0.0f, 0.0f, 0.0f), 0, glm::vec3(0.0f, 1.0f, 0.0f), glm::vec4(0.0f, 0.0f, 2.0f, 10.0f)}); - auto& cameraTransform = ecs.getComponent(m_mainCamera); + auto& cameraTransform = ecs.getComponent(services.render().getCameraEntity()); cameraTransform.position = vec3(12, -1, 12); cameraTransform.rotation.x = -0.95f; } void onUpdate(ECSManager& ecs, ServiceProvider& services) override { - if (Input::GetKeyDown(Input::Q)) + if (services.input().getKeyDown(Input::Q)) { - goToNextScene(); + services.sceneControl().goToNextScene(); } - auto& cameraTransform = ecs.getComponent(m_mainCamera); + auto& cameraTransform = ecs.getComponent(services.render().getCameraEntity()); // getLights()[0].position.x = cameraTransform.position.x; // getLights()[0].position.y = cameraTransform.position.y; diff --git a/examples/opengl-experiments/include/Fire.h b/examples/opengl-experiments/include/Fire.h index eb88895..8476be8 100644 --- a/examples/opengl-experiments/include/Fire.h +++ b/examples/opengl-experiments/include/Fire.h @@ -43,20 +43,21 @@ class FireScene : public Scene3D // Base shaders m_backgroundShader = Shader(SHADERS_PATH "common/screen_plane.vert", SHADERS_PATH "misc/background_spherical_grid.frag"); - m_litShader = Shader(SHADERS_PATH "3d/geometry.vert", ASSETS_PATH "fire/shaders/lit.frag"); + m_litShader = Shader(SHADERS_PATH "3d/geometry.vert", services.resources().assetPath("fire/shaders/lit.frag")); m_bloomShader = Shader(SHADERS_PATH "common/screen_plane.vert", SHADERS_PATH "postprocess/bloom.frag"); m_blurShader = Shader(SHADERS_PATH "common/screen_plane.vert", SHADERS_PATH "postprocess/blur.frag"); m_brightFilterShader = Shader(SHADERS_PATH "common/screen_plane.vert", SHADERS_PATH "postprocess/bright_filter.frag"); // Custom shaders - m_flameShader = Shader(SHADERS_PATH "3d/geometry.vert", ASSETS_PATH "fire/shaders/flame.frag"); - m_particlesShader = - Shader(ASSETS_PATH "fire/shaders/fireParticles.vert", ASSETS_PATH "fire/shaders/fireParticles.frag"); - m_smokeShader = - Shader(ASSETS_PATH "fire/shaders/smokeParticles.vert", ASSETS_PATH "fire/shaders/smokeParticles.frag"); + m_flameShader = + Shader(SHADERS_PATH "3d/geometry.vert", services.resources().assetPath("fire/shaders/flame.frag")); + m_particlesShader = Shader(services.resources().assetPath("fire/shaders/fireParticles.vert"), + services.resources().assetPath("fire/shaders/fireParticles.frag")); + m_smokeShader = Shader(services.resources().assetPath("fire/shaders/smokeParticles.vert"), + services.resources().assetPath("fire/shaders/smokeParticles.frag")); m_heatDistortionShader = - Shader(SHADERS_PATH "3d/geometry.vert", ASSETS_PATH "fire/shaders/heatDistortion.frag"); + Shader(SHADERS_PATH "3d/geometry.vert", services.resources().assetPath("fire/shaders/heatDistortion.frag")); getLights().push_back(Light{0, glm::vec3(0.0f), 0, glm::vec3(0.0f), glm::vec4(0.0f)}); getLights().push_back( @@ -144,8 +145,8 @@ class FireScene : public Scene3D } // Fire textures - m_noiseTexture = new Texture(ASSETS_PATH "fire/fire.jpg"); - m_flameShape = new Texture(ASSETS_PATH "fire/flame.png"); + m_noiseTexture = new Texture(services.resources().assetPath("fire/fire.jpg")); + m_flameShape = new Texture(services.resources().assetPath("fire/flame.png")); m_sceneTextureBeforeFire = new Texture(Display::rWidth, Display::rHeight, Texture::TextureType::Data); m_postProcessTextureFront = new Texture(Display::rWidth, Display::rHeight, Texture::TextureType::Data); @@ -209,27 +210,27 @@ class FireScene : public Scene3D // Inherited via Scene void onStart(ECSManager& ecs, ServiceProvider& services) override { - m_debugFly = false; + services.debug().setDebugFly(false); } float m_time = 3.1416f; void onUpdate(ECSManager& ecs, ServiceProvider& services) override { float delta = services.time().deltaTime(); - if (Input::GetKeyDown(Input::Q)) + if (services.input().getKeyDown(Input::Q)) { - goToNextScene(); + services.sceneControl().goToNextScene(); } - if (m_debugFly) + if (services.debug().debugFly()) { return; } - if (!Input::GetKey(Input::Space)) + if (!services.input().getKey(Input::Space)) { static float speed = 0.15f; - if (Input::GetKey(Input::R)) + if (services.input().getKey(Input::R)) { m_time -= delta * speed; } @@ -239,7 +240,7 @@ class FireScene : public Scene3D } } - Transform& cameraTransform = ecs.getComponent(m_mainCamera); + Transform& cameraTransform = ecs.getComponent(services.render().getCameraEntity()); static float amplitude = 10.0f; @@ -292,7 +293,7 @@ class FireScene : public Scene3D glDisable(GL_BLEND); } - void onRender(WeirdRenderer::RenderTarget& renderTarget) override + void onRender(ECSManager& ecs, WeirdRenderer::RenderTarget& renderTarget, ServiceProvider& services) override { WeirdRenderer::Camera& sceneCamera = getCamera(); float time = getTime(); @@ -400,7 +401,7 @@ class FireScene : public Scene3D // Fire renderFire(sceneCamera, time); - if (Input::GetKey(Input::P)) + if (services.input().getKey(Input::P)) { return; } @@ -452,7 +453,7 @@ class FireScene : public Scene3D RenderTarget* finalTarget = m_postProcessDoubleBuffer[!horizontal]; finalTarget->getColorAttachment()->bind(1); - if (Input::GetKey(Input::B)) + if (services.input().getKey(Input::B)) { finalTarget->getColorAttachment()->bind(0); } diff --git a/examples/opengl-experiments/include/Lines.h b/examples/opengl-experiments/include/Lines.h index 013b1ec..2a3f77b 100644 --- a/examples/opengl-experiments/include/Lines.h +++ b/examples/opengl-experiments/include/Lines.h @@ -33,27 +33,29 @@ class LinesScene : public Scene3D whiteMat.pattern = MaterialPattern::Checkers; std::shared_ptr plane = std::make_shared(0.0f); - auto planeId = registerSDF(plane); + auto planeId = services.shapes().registerSDF(plane); float vars1[8] = {}; // Custom shape - Entity start = addShape(planeId, vars1, whiteMat); + Entity start = services.shapes().addShape(planeId, vars1, whiteMat); } m_renderPlane = new RenderPlane(); m_colorTextureCopy = new Texture(Display::rWidth, Display::rHeight, Texture::TextureType::Data); - m_lineShader = new Shader(SHADERS_PATH "common/screen_plane.vert", ASSETS_PATH "lines/lines.frag"); + m_lineShader = + new Shader(SHADERS_PATH "common/screen_plane.vert", services.resources().assetPath("lines/lines.frag")); m_lineRender = new RenderTarget(false); m_lineTexture = new Texture(Display::rWidth, Display::rHeight, Texture::TextureType::Data); m_lineRender->bindColorTextureToFrameBuffer(*m_lineTexture); - m_combinationShader = new Shader(SHADERS_PATH "common/screen_plane.vert", ASSETS_PATH "lines/combination.frag"); + m_combinationShader = new Shader(SHADERS_PATH "common/screen_plane.vert", + services.resources().assetPath("lines/combination.frag")); } // Inherited via Scene void onStart(ECSManager& ecs, ServiceProvider& services) override { - m_debugFly = false; + services.debug().setDebugFly(false); getLights().push_back(Light{}); { @@ -63,8 +65,8 @@ class LinesScene : public Scene3D // MeshRenderer &mr = ecs.addComponent(entity); - // auto id = m_resourceManager.getMeshId(ASSETS_PATH "monkey/demo.gltf", entity, true); - // mr.mesh = id; + // auto id = services.resources().getMeshId(services.resources().assetPath("monkey/demo.gltf"), entity, + // true); mr.mesh = id; auto& sdf = ecs.addComponent(entity); sdf.materialId = m_whiteMatId; @@ -76,12 +78,12 @@ class LinesScene : public Scene3D void onUpdate(ECSManager& ecs, ServiceProvider& services) override { float delta = services.time().deltaTime(); - if (Input::GetKeyDown(Input::Q)) + if (services.input().getKeyDown(Input::Q)) { - goToNextScene(); + services.sceneControl().goToNextScene(); } - Transform& cameraTransform = ecs.getComponent(m_mainCamera); + Transform& cameraTransform = ecs.getComponent(services.render().getCameraEntity()); cameraTransform.position.y = 5.0f; cameraTransform.position.z -= 10.0f * delta; @@ -90,7 +92,7 @@ class LinesScene : public Scene3D monkeyTransform.position.z -= 5.0f; } - void onRender(WeirdRenderer::RenderTarget& renderTarget) override + void onRender(ECSManager& ecs, WeirdRenderer::RenderTarget& renderTarget, ServiceProvider& services) override { m_lineRender->bind(); glClearColor(0, 0, 0, 0); // Set clear color diff --git a/examples/opengl-experiments/include/Water.h b/examples/opengl-experiments/include/Water.h index 6a2a814..7f0679f 100644 --- a/examples/opengl-experiments/include/Water.h +++ b/examples/opengl-experiments/include/Water.h @@ -47,7 +47,8 @@ class WaterScene : public Scene3D void onCreate(ECSManager& ecs, ServiceProvider& services) override { - m_waterShader = Shader(ASSETS_PATH "water/shaders/water.vert", ASSETS_PATH "water/shaders/water.frag"); + m_waterShader = Shader(services.resources().assetPath("water/shaders/water.vert"), + services.resources().assetPath("water/shaders/water.frag")); getLights().push_back(Light{0, glm::vec3(0.0f, 0.0f, 0.0f), 0, normalize(glm::vec3(0.0f, 0.4f, 1.0f)), glm::vec4(1.0f, 1.0f, 1.0f, 0.5f)}); @@ -78,7 +79,7 @@ class WaterScene : public Scene3D void onStart(ECSManager& ecs, ServiceProvider& services) override { - m_debugFly = true; + services.debug().setDebugFly(true); auto& redMat = createMaterial(); redMat.color = vec4(.8f, 0.2f, 0.2f, 1.0f); @@ -98,13 +99,13 @@ class WaterScene : public Scene3D t.position = vec3(3, 0, 0); MeshRenderer& mr = ecs.addComponent(entity); - auto id = m_resourceManager.getMeshId(ASSETS_PATH "monkey/demo.gltf", entity, true); + auto id = services.resources().getMeshId(services.resources().assetPath("monkey/demo.gltf"), entity, true); mr.mesh = id; ecs.addComponent(entity); } - ecs.getComponent(m_mainCamera).position = vec3(0, 3, 20); + ecs.getComponent(services.render().getCameraEntity()).position = vec3(0, 3, 20); } float m_time = 0.0f; @@ -112,9 +113,9 @@ class WaterScene : public Scene3D void onUpdate(ECSManager& ecs, ServiceProvider& services) override { float delta = services.time().deltaTime(); - if (Input::GetKeyDown(Input::Q)) + if (services.input().getKeyDown(Input::Q)) { - goToNextScene(); + services.sceneControl().goToNextScene(); } m_time += delta; @@ -143,7 +144,7 @@ class WaterScene : public Scene3D } } - void onRender(WeirdRenderer::RenderTarget& renderTarget) override + void onRender(ECSManager& ecs, WeirdRenderer::RenderTarget& renderTarget, ServiceProvider& services) override { WeirdRenderer::Camera& sceneCamera = getCamera(); float time = getTime(); diff --git a/examples/sample-scenes/include/AquariumScene.h b/examples/sample-scenes/include/AquariumScene.h index 810d74c..9914e0d 100644 --- a/examples/sample-scenes/include/AquariumScene.h +++ b/examples/sample-scenes/include/AquariumScene.h @@ -74,8 +74,8 @@ class AquariumScene : public Scene2D void onStart(ECSManager& ecs, ServiceProvider& services) override { - m_debugInput = true; - m_debugFly = true; + services.debug().setDebugInput(true); + services.debug().setDebugFly(true); auto& background = getBackground(); background.type = BackgroundType::Sky; @@ -89,10 +89,10 @@ class AquariumScene : public Scene2D settings.damping = 0.025f; ecs.setComponentDirty(settings); - createJellyfish(ecs, 0.0f, 25.0f, 4 + 0, 1.3f, 0.0f); - createJellyfish(ecs, 15.0f, 20.0f, 4 + 3, 1.6f, 1.5f); - createJellyfish(ecs, 30.0f, 28.0f, 4 + 6, 1.1f, 3.0f); - createJellyfish(ecs, 40.0f, 15.0f, 4 + 9, 1.4f, 4.5f); + createJellyfish(ecs, services, 0.0f, 25.0f, 4 + 0, 1.3f, 0.0f); + createJellyfish(ecs, services, 15.0f, 20.0f, 4 + 3, 1.6f, 1.5f); + createJellyfish(ecs, services, 30.0f, 28.0f, 4 + 6, 1.1f, 3.0f); + createJellyfish(ecs, services, 40.0f, 15.0f, 4 + 9, 1.4f, 4.5f); createEel(ecs, -10.0f, 50.0f, 20, 1.0f, 4); createEel(ecs, 40.0f, 40.0f, 18, 1.0f, 8); @@ -100,27 +100,27 @@ class AquariumScene : public Scene2D { float seaweedVars[8] = {3.0f, 1.2f, 2.5f}; - Entity seaweed = addShape(DefaultShapes::SINE, seaweedVars, DisplaySettings::Green); + Entity seaweed = services.shapes().addShape(DefaultShapes::SINE, seaweedVars, DisplaySettings::Green); auto& sw = ecs.addComponent(seaweed); sw.animationOffset = 0.0f; } { float seaweedVars[8] = {2.0f, 2.0f, 1.8f}; - Entity seaweed = addShape(DefaultShapes::SINE, seaweedVars, DisplaySettings::LightGreen); + Entity seaweed = services.shapes().addShape(DefaultShapes::SINE, seaweedVars, DisplaySettings::LightGreen); auto& sw = ecs.addComponent(seaweed); sw.animationOffset = 1.5f; } { float boxVars[8] = {TANK_CX, TANK_CY, TANK_W, TANK_H}; - Entity box = - addShape(DefaultShapes::BOX, boxVars, DisplaySettings::LightBlue, CombinationType::Intersection); + Entity box = services.shapes().addShape(DefaultShapes::BOX, boxVars, DisplaySettings::LightBlue, + CombinationType::Intersection); } { float boxVars[8] = {TANK_CX, TANK_CY, TANK_W, TANK_H, 1.0f}; - Entity box = addShape(DefaultShapes::BOX_LINE, boxVars, DisplaySettings::LightBlue); + Entity box = services.shapes().addShape(DefaultShapes::BOX_LINE, boxVars, DisplaySettings::LightBlue); } for (int i = 0; i < 40; i++) @@ -148,10 +148,11 @@ class AquariumScene : public Scene2D fishComp.perceptionRadius = 5.0f; } - ecs.getComponent(m_mainCamera).position = vec3(TANK_CX, TANK_CY, 45.0f); + ecs.getComponent(services.render().getCameraEntity()).position = vec3(TANK_CX, TANK_CY, 45.0f); } - void createJellyfish(ECSManager& ecs, float x, float y, int material, float scale, float phase) + void createJellyfish(ECSManager& ecs, ServiceProvider& services, float x, float y, int material, float scale, + float phase) { Entity bellEntity = ecs.createEntity(); auto& t = ecs.addComponent(bellEntity); @@ -161,7 +162,8 @@ class AquariumScene : public Scene2D auto& rb = ecs.addComponent(bellEntity); float bellVars[8] = {x, y, 2.5f * scale, 0.8f, 6.0f, 2.0f}; - Entity bellShape = addShape(DefaultShapes::STAR, bellVars, material, CombinationType::Addition, false); + Entity bellShape = + services.shapes().addShape(DefaultShapes::STAR, bellVars, material, CombinationType::Addition, false); auto& jf = ecs.addComponent(bellEntity); jf.bellShape = bellShape; @@ -252,20 +254,20 @@ class AquariumScene : public Scene2D void onUpdate(ECSManager& ecs, ServiceProvider& services) override { float delta = services.time().deltaTime(); - g_cameraPositon = ecs.getComponent(m_mainCamera).position; + g_cameraPositon = ecs.getComponent(services.render().getCameraEntity()).position; - if (Input::GetKeyDown(Input::Q) || Input::GetGamepadButtonDown(Input::GamepadButton::North)) + if (services.input().getKeyDown(Input::Q) || services.input().getGamepadButtonDown(Input::GamepadButton::North)) { - goToNextScene(); + services.sceneControl().goToNextScene(); } m_time += delta; - auto& cameraTransform = ecs.getComponent(m_mainCamera); - vec2 mouseWorld = - ECS::Camera::screenPositionToWorldPosition2D(cameraTransform, vec2(Input::GetMouseX(), Input::GetMouseY())); + auto& cameraTransform = ecs.getComponent(services.render().getCameraEntity()); + vec2 mouseWorld = ECS::Camera::screenPositionToWorldPosition2D( + cameraTransform, vec2(services.input().getMouseX(), services.input().getMouseY())); - if (Input::GetKeyDown(Input::E)) + if (services.input().getKeyDown(Input::E)) { constexpr int FOOD_COUNT = 30; for (int i = 0; i < FOOD_COUNT; i++) diff --git a/examples/sample-scenes/include/CollisionHandling.h b/examples/sample-scenes/include/CollisionHandling.h index 01ea886..00bebee 100644 --- a/examples/sample-scenes/include/CollisionHandling.h +++ b/examples/sample-scenes/include/CollisionHandling.h @@ -14,8 +14,8 @@ class CollisionHandlingScene : public Scene2D // Inherited via Scene void onStart(ECSManager& ecs, ServiceProvider& services) override { - m_debugInput = true; - m_debugFly = true; + services.debug().setDebugInput(true); + services.debug().setDebugFly(true); // Create a random number generator engine @@ -41,28 +41,28 @@ class CollisionHandlingScene : public Scene2D // Floor { float variables[8]{15.0f, 5.0f, 25.0f}; - addShape(DefaultShapes::CIRCLE, variables, 3); + services.shapes().addShape(DefaultShapes::CIRCLE, variables, 3); } { float variables[8]{15.0f, -50.0f, 250.0f, 50.0f}; - auto floor = addShape(DefaultShapes::BOX, variables, 3, CombinationType::SmoothAddition); + auto floor = services.shapes().addShape(DefaultShapes::BOX, variables, 3, CombinationType::SmoothAddition); ecs.getComponent(floor).smoothFactor = 3.0f; } { float variables[8]{15.0f, 5.0f, 20.0f}; - addShape(DefaultShapes::CIRCLE, variables, 3, CombinationType::Subtraction); + services.shapes().addShape(DefaultShapes::CIRCLE, variables, 3, CombinationType::Subtraction); } - ecs.getComponent(m_mainCamera).position = g_cameraPositon; + ecs.getComponent(services.render().getCameraEntity()).position = g_cameraPositon; } void onUpdate(ECSManager& ecs, ServiceProvider& services) override { - if (Input::GetKeyDown(Input::Q) || Input::GetGamepadButtonDown(Input::GamepadButton::North)) + if (services.input().getKeyDown(Input::Q) || services.input().getGamepadButtonDown(Input::GamepadButton::North)) { - goToNextScene(); + services.sceneControl().goToNextScene(); } } diff --git a/examples/sample-scenes/include/DestroyScene.h b/examples/sample-scenes/include/DestroyScene.h index b419765..23d28ef 100644 --- a/examples/sample-scenes/include/DestroyScene.h +++ b/examples/sample-scenes/include/DestroyScene.h @@ -26,20 +26,20 @@ class DestroyScene : public Scene2D // Inherited via Scene void onStart(ECSManager& ecs, ServiceProvider& services) override { - m_debugInput = true; - m_debugFly = true; + services.debug().setDebugInput(true); + services.debug().setDebugFly(true); - ecs.getComponent(m_mainCamera).position = g_cameraPositon; + ecs.getComponent(services.render().getCameraEntity()).position = g_cameraPositon; } void onUpdate(ECSManager& ecs, ServiceProvider& services) override { float delta = services.time().deltaTime(); - g_cameraPositon = ecs.getComponent(m_mainCamera).position; + g_cameraPositon = ecs.getComponent(services.render().getCameraEntity()).position; - if (Input::GetKeyDown(Input::Q) || Input::GetGamepadButtonDown(Input::GamepadButton::North)) + if (services.input().getKeyDown(Input::Q) || services.input().getGamepadButtonDown(Input::GamepadButton::North)) { - goToNextScene(); + services.sceneControl().goToNextScene(); } m_timer += delta; @@ -81,7 +81,8 @@ class DestroyScene : public Scene2D float h = (float)(std::rand() % 4 + 1); float variables[8]{w, y, x, h, 0.0f, 0.0f, 0.0f, 0.0f}; uint16_t material = std::rand() % 16; - Entity shape = addShape(DefaultShapes::BOX, variables, material, CombinationType::Addition); + Entity shape = services.shapes().addShape(DefaultShapes::BOX, variables, material, + CombinationType::Addition); m_testShapes.push_back(shape); } break; diff --git a/examples/sample-scenes/include/ImageScene.h b/examples/sample-scenes/include/ImageScene.h index 8580bea..e3843df 100644 --- a/examples/sample-scenes/include/ImageScene.h +++ b/examples/sample-scenes/include/ImageScene.h @@ -15,13 +15,14 @@ class ImageScene : public Scene2D private: std::string binaryString; std::string filePath = "cache/image.txt"; - std::string imagePath = ASSETS_PATH "jimmy.jpg"; + std::string imagePath; // Inherited via Scene void onStart(ECSManager& ecs, ServiceProvider& services) override { - m_debugInput = true; - m_debugFly = true; + services.debug().setDebugInput(true); + services.debug().setDebugFly(true); + imagePath = services.resources().assetPath("jimmy.jpg"); // Check if the folder exists if (!std::filesystem::exists("cache/")) @@ -73,22 +74,22 @@ class ImageScene : public Scene2D // Floor { float variables[8]{15, -5, 25.0f, 5.0f, 0.0f}; - addShape(DefaultShapes::BOX, variables, 3); + services.shapes().addShape(DefaultShapes::BOX, variables, 3); } // Wall right { float variables[8]{30 + 5, 20, 5.0f, 30.0f, 0.0f}; - addShape(DefaultShapes::BOX, variables, 3); + services.shapes().addShape(DefaultShapes::BOX, variables, 3); } // Wall left { float variables[8]{0 - 5, 20, 5.0f, 30.0f, 0.0f}; - addShape(DefaultShapes::BOX, variables, 3); + services.shapes().addShape(DefaultShapes::BOX, variables, 3); } - ecs.getComponent(m_mainCamera).position = g_cameraPositon; + ecs.getComponent(services.render().getCameraEntity()).position = g_cameraPositon; } vec3 getColor(const char* path, float x, float y) @@ -169,13 +170,13 @@ class ImageScene : public Scene2D void onUpdate(ECSManager& ecs, ServiceProvider& services) override { - if (Input::GetKeyDown(Input::Q) || Input::GetGamepadButtonDown(Input::GamepadButton::North)) + if (services.input().getKeyDown(Input::Q) || services.input().getGamepadButtonDown(Input::GamepadButton::North)) { - goToNextScene(); + services.sceneControl().goToNextScene(); } // Get colors - if (Input::GetKeyDown(Input::P)) + if (services.input().getKeyDown(Input::P)) { auto components = ecs.getComponentArray(); diff --git a/examples/sample-scenes/include/LifeScene.h b/examples/sample-scenes/include/LifeScene.h index 9a6717e..4817eaa 100644 --- a/examples/sample-scenes/include/LifeScene.h +++ b/examples/sample-scenes/include/LifeScene.h @@ -29,8 +29,8 @@ class LifeScene : public Scene2D // Inherited via Scene void onStart(ECSManager& ecs, ServiceProvider& services) override { - m_debugInput = true; - m_debugFly = true; + services.debug().setDebugInput(true); + services.debug().setDebugFly(true); Entity globalSettingsEnt = ecs.createEntity(); auto& settings = ecs.addComponent(globalSettingsEnt); @@ -38,7 +38,7 @@ class LifeScene : public Scene2D settings.damping = 0.1f; ecs.setComponentDirty(settings); - const std::filesystem::path organismsDir(ASSETS_PATH "Organisms"); + const std::filesystem::path organismsDir(services.resources().assetPath("Organisms")); { int i = 0; @@ -53,7 +53,7 @@ class LifeScene : public Scene2D { Entity firstCreated = static_cast(ecs.getEntityCount()); - auto tags = loadWeirdFile(entry.path().string()); + auto tags = services.serialization().loadWeirdFile(entry.path().string()); Entity lastCreated = static_cast(ecs.getEntityCount()); @@ -86,17 +86,17 @@ class LifeScene : public Scene2D } } - ecs.getComponent(m_mainCamera).position = g_cameraPositon; + ecs.getComponent(services.render().getCameraEntity()).position = g_cameraPositon; } void onUpdate(ECSManager& ecs, ServiceProvider& services) override { float delta = services.time().deltaTime(); - g_cameraPositon = ecs.getComponent(m_mainCamera).position; + g_cameraPositon = ecs.getComponent(services.render().getCameraEntity()).position; - if (Input::GetKeyDown(Input::Q) || Input::GetGamepadButtonDown(Input::GamepadButton::North)) + if (services.input().getKeyDown(Input::Q) || services.input().getGamepadButtonDown(Input::GamepadButton::North)) { - goToNextScene(); + services.sceneControl().goToNextScene(); } updateHeads(delta, ecs); diff --git a/examples/sample-scenes/include/MouseCollisionScene.h b/examples/sample-scenes/include/MouseCollisionScene.h index fe0c773..ec6edd9 100644 --- a/examples/sample-scenes/include/MouseCollisionScene.h +++ b/examples/sample-scenes/include/MouseCollisionScene.h @@ -22,8 +22,8 @@ class MouseCollisionScene : public Scene2D // Inherited via Scene void onStart(ECSManager& ecs, ServiceProvider& services) override { - m_debugInput = true; - m_debugFly = true; + services.debug().setDebugInput(true); + services.debug().setDebugFly(true); for (size_t i = 0; i < 9900; i++) { @@ -53,46 +53,46 @@ class MouseCollisionScene : public Scene2D // Floor { float variables[8]{0.0f, 1.5f, 1.0f}; - addShape(DefaultShapes::SINE, variables, 3); + services.shapes().addShape(DefaultShapes::SINE, variables, 3); } // Wall right { float variables[8]{30 + 5, 0, 5.0f, 30.0f, 0.0f}; - addShape(DefaultShapes::BOX, variables, 3); + services.shapes().addShape(DefaultShapes::BOX, variables, 3); } // Wall left { float variables[8]{-5, 0, 5.0f, 30.0f, 0.0f}; - addShape(DefaultShapes::BOX, variables, 3); + services.shapes().addShape(DefaultShapes::BOX, variables, 3); } { float variables[8]{-15.0f, 50.0f, 5.0f, 4.5f, 2.0f, 10.0f}; - Entity star = addShape(DefaultShapes::CIRCLE, variables, 7); + Entity star = services.shapes().addShape(DefaultShapes::CIRCLE, variables, 7); m_cursorShape = star; } - ecs.getComponent(m_mainCamera).position = g_cameraPositon; + ecs.getComponent(services.render().getCameraEntity()).position = g_cameraPositon; } void onUpdate(ECSManager& ecs, ServiceProvider& services) override { - g_cameraPositon = ecs.getComponent(m_mainCamera).position; + g_cameraPositon = ecs.getComponent(services.render().getCameraEntity()).position; - if (Input::GetKeyDown(Input::Q) || Input::GetGamepadButtonDown(Input::GamepadButton::North)) + if (services.input().getKeyDown(Input::Q) || services.input().getGamepadButtonDown(Input::GamepadButton::North)) { - goToNextScene(); + services.sceneControl().goToNextScene(); } // Move wall to mouse { CustomShape& cs = ecs.getComponent(m_cursorShape); - auto& cameraTransform = ecs.getComponent(m_mainCamera); - float x = Input::GetMouseX(); - float y = Input::GetMouseY(); + auto& cameraTransform = ecs.getComponent(services.render().getCameraEntity()); + float x = services.input().getMouseX(); + float y = services.input().getMouseY(); // Transform mouse coordinates to world space vec2 mousePositionInWorld = ECS::Camera::screenPositionToWorldPosition2D(cameraTransform, vec2(x, y)); diff --git a/examples/sample-scenes/include/RopeScene.h b/examples/sample-scenes/include/RopeScene.h index a1c9597..4ac0250 100644 --- a/examples/sample-scenes/include/RopeScene.h +++ b/examples/sample-scenes/include/RopeScene.h @@ -20,8 +20,8 @@ class RopeScene : public Scene2D void onStart(ECSManager& ecs, ServiceProvider& services) override { - m_debugInput = true; - m_debugFly = true; + services.debug().setDebugInput(true); + services.debug().setDebugFly(true); constexpr int rowWidth = 30; constexpr int numBalls = rowWidth * 2; @@ -112,15 +112,15 @@ class RopeScene : public Scene2D // Add base shapes (walls, ground, custom) float vars0[8] = {1.0f, 0.5f, 1.0f}; // Floor shape - addShape(DefaultShapes::SINE, vars0, 3); + services.shapes().addShape(DefaultShapes::SINE, vars0, 3); float vars1[8] = {25.0f, 10.0f, 5.0f, 0.5f, 13.0f, 5.0f}; // Custom shape - m_star = addShape(DefaultShapes::STAR, vars1, 3); + m_star = services.shapes().addShape(DefaultShapes::STAR, vars1, 3); float vars3[8] = {15.0f, -98.0f, 15.0f, 100.0f}; - addShape(DefaultShapes::BOX, vars3, 3, CombinationType::Addition); + services.shapes().addShape(DefaultShapes::BOX, vars3, 3, CombinationType::Addition); - ecs.getComponent(m_mainCamera).position = g_cameraPositon; + ecs.getComponent(services.render().getCameraEntity()).position = g_cameraPositon; } void throwBalls(ECSManager& ecs) @@ -153,11 +153,11 @@ class RopeScene : public Scene2D void onUpdate(ECSManager& ecs, ServiceProvider& services) override { float delta = services.time().deltaTime(); - g_cameraPositon = ecs.getComponent(m_mainCamera).position; + g_cameraPositon = ecs.getComponent(services.render().getCameraEntity()).position; - if (Input::GetKeyDown(Input::Q) || Input::GetGamepadButtonDown(Input::GamepadButton::North)) + if (services.input().getKeyDown(Input::Q) || services.input().getGamepadButtonDown(Input::GamepadButton::North)) { - goToNextScene(); + services.sceneControl().goToNextScene(); } // Animate custom shape over time @@ -173,17 +173,17 @@ class RopeScene : public Scene2D ecs.setComponentDirty(cs); } - if (Input::GetKey(Input::E) || Input::GetGamepadButton(Input::GamepadButton::West)) + if (services.input().getKey(Input::E) || services.input().getGamepadButton(Input::GamepadButton::West)) { throwBalls(ecs); } static vec2 boxStart; static bool createBoxInUI = true; - if (Input::GetKeyDown(Input::M)) + if (services.input().getKeyDown(Input::M)) { - auto& cam = ecs.getComponent(m_mainCamera); - vec2 screen = {Input::GetMouseX(), Input::GetMouseY()}; + auto& cam = ecs.getComponent(services.render().getCameraEntity()); + vec2 screen = {services.input().getMouseX(), services.input().getMouseY()}; if (createBoxInUI) { @@ -195,10 +195,10 @@ class RopeScene : public Scene2D boxStart = world; } } - else if (Input::GetKeyUp(Input::M)) + else if (services.input().getKeyUp(Input::M)) { - auto& cam = ecs.getComponent(m_mainCamera); - vec2 screen = {Input::GetMouseX(), Input::GetMouseY()}; + auto& cam = ecs.getComponent(services.render().getCameraEntity()); + vec2 screen = {services.input().getMouseX(), services.input().getMouseY()}; vec2 world = ECS::Camera::screenPositionToWorldPosition2D(cam, screen); vec2 boxEnd; @@ -218,23 +218,24 @@ class RopeScene : public Scene2D float vars[8] = {x, y, w, h, 1.2f}; if (createBoxInUI) - addUIShape(DefaultShapes::BOX, vars, 7, CombinationType::SmoothAddition); + services.shapes().addUIShape(DefaultShapes::BOX, vars, 7, CombinationType::SmoothAddition); else - addShape(DefaultShapes::BOX, vars, 4 + ecs.getComponentArray()->getSize() % 12, - CombinationType::SmoothAddition, true, ecs.getComponentArray()->getSize()); + services.shapes().addShape( + DefaultShapes::BOX, vars, 4 + ecs.getComponentArray()->getSize() % 12, + CombinationType::SmoothAddition, true, ecs.getComponentArray()->getSize()); } - if (Input::GetKeyDown(Input::N)) + if (services.input().getKeyDown(Input::N)) { - auto& cam = ecs.getComponent(m_mainCamera); - vec2 screen = {Input::GetMouseX(), Input::GetMouseY()}; + auto& cam = ecs.getComponent(services.render().getCameraEntity()); + vec2 screen = {services.input().getMouseX(), services.input().getMouseY()}; vec2 world = ECS::Camera::screenPositionToWorldPosition2D(cam, screen); float vars[8] = {world.x, world.y, 5.0f, 7.5f, 1.0f}; - addShape(DefaultShapes::STAR, vars, 3); + services.shapes().addShape(DefaultShapes::STAR, vars, 3); } - if (Input::GetKey(Input::R) || Input::GetGamepadButton(Input::GamepadButton::South)) + if (services.input().getKey(Input::R) || services.input().getGamepadButton(Input::GamepadButton::South)) { ecs.forEach( [&](Entity e, RigidBody2D& rb, Transform& t) diff --git a/examples/sample-scenes/include/ServiceShowcaseScene.h b/examples/sample-scenes/include/ServiceShowcaseScene.h index 196b73b..50a49af 100644 --- a/examples/sample-scenes/include/ServiceShowcaseScene.h +++ b/examples/sample-scenes/include/ServiceShowcaseScene.h @@ -270,13 +270,13 @@ namespace ServiceShowcase State& state = getState(ecs, services); // Scene transition through the provider - if (Input::GetKeyDown(Input::Q) || Input::GetGamepadButtonDown(Input::GamepadButton::North)) + if (services.input().getKeyDown(Input::Q) || services.input().getGamepadButtonDown(Input::GamepadButton::North)) { services.sceneControl().goToNextScene(); } // Pause / resume through the provider - if (Input::GetKeyDown(Input::Space)) + if (services.input().getKeyDown(Input::Space)) { if (services.physics().isPaused()) services.physics().resume(); @@ -285,48 +285,49 @@ namespace ServiceShowcase } // Real-time physics settings through the provider - if (Input::GetKeyDown(Input::Up)) + if (services.input().getKeyDown(Input::Up)) { state.gravity = std::clamp(state.gravity + 1.0f, -30.0f, 0.0f); services.physics().setGravity(state.gravity); } - if (Input::GetKeyDown(Input::Down)) + if (services.input().getKeyDown(Input::Down)) { state.gravity = std::clamp(state.gravity - 1.0f, -30.0f, 0.0f); services.physics().setGravity(state.gravity); } - if (Input::GetKeyDown(Input::Left)) + if (services.input().getKeyDown(Input::Left)) { state.damping = std::max(0.0f, state.damping - 0.05f); services.physics().setDamping(state.damping); } - if (Input::GetKeyDown(Input::Right)) + if (services.input().getKeyDown(Input::Right)) { state.damping += 0.05f; services.physics().setDamping(state.damping); } // Spawn a ball where the mouse points - if (Input::GetMouseButtonDown(Input::LeftClick) && !Input::isUIClick()) + if (services.input().getMouseButtonDown(Input::LeftClick) && !services.input().isUIClick()) { auto& cameraTransform = ecs.getComponent(services.render().getCameraEntity()); vec2 mouseWorld = ECS::Camera::screenPositionToWorldPosition2D( - cameraTransform, vec2(Input::GetMouseX(), Input::GetMouseY())); + cameraTransform, vec2(services.input().getMouseX(), services.input().getMouseY())); spawnBall(ecs, mouseWorld); state.ballsSpawned++; } // Serialization through the provider - if (Input::GetKeyDown(Input::S) && Input::GetKey(Input::LeftCtrl)) + if (services.input().getKeyDown(Input::S) && services.input().getKey(Input::LeftCtrl)) { - services.serialization().saveScene(ASSETS_PATH "scenes/service_showcase.weird"); + services.serialization().saveScene(services.resources().assetPath("scenes/service_showcase.weird")); std::cout << "[ServiceShowcase] scene saved" << std::endl; } - if (Input::GetKeyDown(Input::L) && Input::GetKey(Input::LeftCtrl)) + if (services.input().getKeyDown(Input::L) && services.input().getKey(Input::LeftCtrl)) { // blacklistEntities = true: entities loaded from disk are excluded // from future saves, so saving again does not duplicate them. - TagMap loaded = services.serialization().loadWeirdFile(ASSETS_PATH "scenes/service_showcase.weird", true); + TagMap loaded = services.serialization().loadWeirdFile( + services.resources().assetPath("scenes/service_showcase.weird"), true); for (const auto& [name, entity] : loaded) std::cout << "[ServiceShowcase] loaded tag '" << name << "' -> entity " << entity << std::endl; } @@ -534,7 +535,7 @@ class ServiceShowcaseScene : public Scene2D // Note the firing rules: onRender only fires for 3D / both render modes, // while onImGuiRender fires for every scene, 2D and 3D alike (it is just // the debug UI). - void onRender(WeirdRenderer::RenderTarget& renderTarget) override + void onRender(ECSManager& ecs, WeirdRenderer::RenderTarget& renderTarget, ServiceProvider& services) override { static bool logged = false; if (!logged) diff --git a/examples/sample-scenes/include/ShapesCombinations.h b/examples/sample-scenes/include/ShapesCombinations.h index 250bbc9..d7f2164 100644 --- a/examples/sample-scenes/include/ShapesCombinations.h +++ b/examples/sample-scenes/include/ShapesCombinations.h @@ -22,13 +22,13 @@ class ShapeCombinatiosScene : public Scene2D void onStart(ECSManager& ecs, ServiceProvider& services) override { - m_debugInput = true; - m_debugFly = true; + services.debug().setDebugInput(true); + services.debug().setDebugFly(true); // Floor shape { float vars0[8] = {0.5f, 2.5f, 1.0f}; - addShape(DefaultShapes::SINE, vars0, 2, CombinationType::Addition, true, 0); + services.shapes().addShape(DefaultShapes::SINE, vars0, 2, CombinationType::Addition, true, 0); } std::random_device rd; @@ -46,32 +46,33 @@ class ShapeCombinatiosScene : public Scene2D float y = -2.0f + distribY(gen); float vars2[8] = {x, y, 3.0f, 5.0f, 1.0f, 0.0f}; // Custom shape - addShape(DefaultShapes::BOX, vars2, 4 + i, CombinationType::Addition, true, 1); + services.shapes().addShape(DefaultShapes::BOX, vars2, 4 + i, CombinationType::Addition, true, 1); } } // Circle { float vars[8] = {15.0f, 7.5f, 5.0f}; - addShape(DefaultShapes::CIRCLE, vars, 7, CombinationType::Addition, true, 2); + services.shapes().addShape(DefaultShapes::CIRCLE, vars, 7, CombinationType::Addition, true, 2); } // Subtract star { float vars[8] = {-2.5f + 15.0f, 12.5f, 5.0f, 0.5f, 13.0f, 5.0f}; - addShape(DefaultShapes::STAR, vars, 0, CombinationType::SmoothSubtraction, true, 2); + services.shapes().addShape(DefaultShapes::STAR, vars, 0, CombinationType::SmoothSubtraction, true, 2); } // Cursor circle { float vars2[8] = {250.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f}; // Custom shape - m_circle = addShape(DefaultShapes::CIRCLE, vars2, 0, CombinationType::Subtraction, true, - CustomShape::GLOBAL_GROUP); + m_circle = services.shapes().addShape(DefaultShapes::CIRCLE, vars2, 0, CombinationType::Subtraction, true, + CustomShape::GLOBAL_GROUP); } { float vars2[8] = {15.0f, 0.0f, 30.0f, 0.0f, 0.0f, 0.0f}; // Custom shape - addShape(DefaultShapes::CIRCLE, vars2, 0, CombinationType::Intersection, true, CustomShape::GLOBAL_GROUP); + services.shapes().addShape(DefaultShapes::CIRCLE, vars2, 0, CombinationType::Intersection, true, + CustomShape::GLOBAL_GROUP); } for (int i = 0; i < 10; ++i) @@ -86,31 +87,31 @@ class ShapeCombinatiosScene : public Scene2D m_uiPoints.push_back(ee); } - ecs.getComponent(m_mainCamera).position = g_cameraPositon; + ecs.getComponent(services.render().getCameraEntity()).position = g_cameraPositon; } void onUpdate(ECSManager& ecs, ServiceProvider& services) override { float delta = services.time().deltaTime(); - g_cameraPositon = ecs.getComponent(m_mainCamera).position; + g_cameraPositon = ecs.getComponent(services.render().getCameraEntity()).position; - if (Input::GetKeyDown(Input::Q) || Input::GetGamepadButtonDown(Input::GamepadButton::North)) + if (services.input().getKeyDown(Input::Q) || services.input().getGamepadButtonDown(Input::GamepadButton::North)) { - goToNextScene(); + services.sceneControl().goToNextScene(); } - auto& cameraTransform = ecs.getComponent(m_mainCamera); - float x = Input::GetMouseX(); - float y = Input::GetMouseY(); + auto& cameraTransform = ecs.getComponent(services.render().getCameraEntity()); + float x = services.input().getMouseX(); + float y = services.input().getMouseY(); // Transform mouse coordinates to world space vec2 mousePositionInWorld = ECS::Camera::screenPositionToWorldPosition2D(cameraTransform, vec2(x, y)); - if (Input::GetMouseButtonDown(Input::RightClick)) + if (services.input().getMouseButtonDown(Input::RightClick)) { m_initialMousePositionInWorld = mousePositionInWorld; } - else if (Input::GetGamepadButtonDown(Input::GamepadButton::LeftShoulder)) + else if (services.input().getGamepadButtonDown(Input::GamepadButton::LeftShoulder)) { float halfWidth = Display::width / 2.0f; float halfHeight = Display::height / 2.0f; @@ -119,12 +120,12 @@ class ShapeCombinatiosScene : public Scene2D ECS::Camera::screenPositionToWorldPosition2D(cameraTransform, vec2(halfWidth, halfHeight)); } - if (Input::GetMouseButton(Input::RightClick)) + if (services.input().getMouseButton(Input::RightClick)) { vec2 v = mousePositionInWorld - m_initialMousePositionInWorld; m_circleRadious = (std::min)(10.0f, length(v)); } - else if (Input::GetGamepadButton(Input::GamepadButton::LeftShoulder)) + else if (services.input().getGamepadButton(Input::GamepadButton::LeftShoulder)) { m_circleRadious = (std::min)(10.0f, m_circleRadious + (10.0f * delta)); } diff --git a/examples/sample-scenes/include/TextScene.h b/examples/sample-scenes/include/TextScene.h index feae72a..f7560bf 100644 --- a/examples/sample-scenes/include/TextScene.h +++ b/examples/sample-scenes/include/TextScene.h @@ -26,15 +26,16 @@ class TextScene : public Scene2D void onStart(ECSManager& ecs, ServiceProvider& services) override { - m_debugInput = true; - m_debugFly = true; + services.debug().setDebugInput(true); + services.debug().setDebugFly(true); { float vars[8] = {15.0f, -50.0f, 250.0f, 50.0f}; - addShape(DefaultShapes::BOX, vars, DisplaySettings::LightGray, CombinationType::SmoothAddition); + services.shapes().addShape(DefaultShapes::BOX, vars, DisplaySettings::LightGray, + CombinationType::SmoothAddition); } - ecs.getComponent(m_mainCamera).position = g_cameraPositon; + ecs.getComponent(services.render().getCameraEntity()).position = g_cameraPositon; m_lastResolutionHash = Display::width + Display::height; { @@ -125,9 +126,9 @@ class TextScene : public Scene2D void onUpdate(ECSManager& ecs, ServiceProvider& services) override { - if (Input::GetKeyDown(Input::Q) || Input::GetGamepadButtonDown(Input::GamepadButton::North)) + if (services.input().getKeyDown(Input::Q) || services.input().getGamepadButtonDown(Input::GamepadButton::North)) { - goToNextScene(); + services.sceneControl().goToNextScene(); } m_counter++; @@ -137,13 +138,13 @@ class TextScene : public Scene2D ecs.setComponentDirty(text); auto& t = ecs.getComponent(m_counterText); - t.position.x = Input::GetMouseX() + 20.0f; - t.position.y = Input::GetMouseY() + 10.0f; + t.position.x = services.input().getMouseX() + 20.0f; + t.position.y = services.input().getMouseY() + 10.0f; } { - auto& cameraTransform = ecs.getComponent(m_mainCamera); - vec2 mouseScreen = vec2(Input::GetMouseX() + 20.0f, Input::GetMouseY() - 10.0f); + auto& cameraTransform = ecs.getComponent(services.render().getCameraEntity()); + vec2 mouseScreen = vec2(services.input().getMouseX() + 20.0f, services.input().getMouseY() - 10.0f); vec2 mouseWorld = ECS::Camera::screenPositionToWorldPosition2D(cameraTransform, mouseScreen); auto& t = ecs.getComponent(m_worldMouseText); diff --git a/examples/sample-scenes/include/WalkScene.h b/examples/sample-scenes/include/WalkScene.h index bf5c3a2..fa2a01c 100644 --- a/examples/sample-scenes/include/WalkScene.h +++ b/examples/sample-scenes/include/WalkScene.h @@ -32,8 +32,8 @@ class WalkScene : public Scene2D // Inherited via Scene void onStart(ECSManager& ecs, ServiceProvider& services) override { - m_debugInput = true; - m_debugFly = true; + services.debug().setDebugInput(true); + services.debug().setDebugFly(true); auto& background = getBackground(); background.type = BackgroundType::Sky; @@ -41,7 +41,7 @@ class WalkScene : public Scene2D background.secondaryColor = vec4(0.4f, 0.75f, 0.85f, 1.0f); background.scale = 0.2f; - auto tags = loadWeirdFile(ASSETS_PATH "man.weird"); + auto tags = services.serialization().loadWeirdFile(services.resources().assetPath("man.weird")); Entity firstCreated = static_cast(ecs.getEntityCount()); @@ -62,10 +62,10 @@ class WalkScene : public Scene2D m_head = tags["head"]; float boundsVars2[8]{0.0f, -24.0f, 200.0f, 20.0f}; - Entity inside = - addShape(DefaultShapes::BOX, boundsVars2, DisplaySettings::LightGreen, CombinationType::Addition); + Entity inside = services.shapes().addShape(DefaultShapes::BOX, boundsVars2, DisplaySettings::LightGreen, + CombinationType::Addition); - ecs.getComponent(m_mainCamera).position = g_cameraPositon; + ecs.getComponent(services.render().getCameraEntity()).position = g_cameraPositon; Entity globalSettingsEnt = ecs.createEntity(); auto& settings = ecs.addComponent(globalSettingsEnt); @@ -77,11 +77,11 @@ class WalkScene : public Scene2D { float delta = services.time().deltaTime(); - g_cameraPositon = ecs.getComponent(m_mainCamera).position; + g_cameraPositon = ecs.getComponent(services.render().getCameraEntity()).position; - if (Input::GetKeyDown(Input::Q) || Input::GetGamepadButtonDown(Input::GamepadButton::North)) + if (services.input().getKeyDown(Input::Q) || services.input().getGamepadButtonDown(Input::GamepadButton::North)) { - goToNextScene(); + services.sceneControl().goToNextScene(); } updatePhysics(delta, ecs); diff --git a/tools/molecule-editor/include/MoleculeEditor.h b/tools/molecule-editor/include/MoleculeEditor.h index 9a50682..7ead6d8 100644 --- a/tools/molecule-editor/include/MoleculeEditor.h +++ b/tools/molecule-editor/include/MoleculeEditor.h @@ -128,11 +128,11 @@ class MoleculeEditor : public Scene2D void onStart(ECSManager& ecs, ServiceProvider& services) override { m_tempEcs = &ecs; - m_debugFly = true; + m_services.debug().setDebugFly(true); g_cameraPositon.x = 0.0f; g_cameraPositon.y = 0.0f; - m_tempEcs->getComponent(m_mainCamera).position = g_cameraPositon; + m_tempEcs->getComponent(m_services.render().getCameraEntity()).position = g_cameraPositon; // Request neutral simulation behavior for this editor scene. Entity globalSettingsEnt = m_tempEcs->createEntity(); @@ -147,29 +147,31 @@ class MoleculeEditor : public Scene2D { float boundsVars[8]{0.0f, 0.0f, 3000.0f}; - Entity outside = addShape(DefaultShapes::CIRCLE, boundsVars, 17, CombinationType::Addition); + Entity outside = + m_services.shapes().addShape(DefaultShapes::CIRCLE, boundsVars, 17, CombinationType::Addition); float boundsVars2[8]{0.0f, 0.0f, 20.0f, 20.0f}; - Entity inside = - addShape(DefaultShapes::BOX, boundsVars2, DisplaySettings::Black, CombinationType::Subtraction); + Entity inside = m_services.shapes().addShape(DefaultShapes::BOX, boundsVars2, DisplaySettings::Black, + CombinationType::Subtraction); - blacklistEntity(outside); - blacklistEntity(inside); + m_services.serialization().blacklistEntity(outside); + m_services.serialization().blacklistEntity(inside); } } void onUpdate(ECSManager& ecs, ServiceProvider& services) override { m_tempEcs = &ecs; - g_cameraPositon = m_tempEcs->getComponent(m_mainCamera).position; + g_cameraPositon = m_tempEcs->getComponent(m_services.render().getCameraEntity()).position; - if (Input::GetKeyDown(Input::Q) || Input::GetGamepadButtonDown(Input::GamepadButton::North)) + if (m_services.input().getKeyDown(Input::Q) || + m_services.input().getGamepadButtonDown(Input::GamepadButton::North)) { - goToNextScene(); + m_services.sceneControl().goToNextScene(); return; } - if (Input::GetKey(Input::LeftCtrl) && Input::GetKeyDown(Input::S)) + if (m_services.input().getKey(Input::LeftCtrl) && m_services.input().getKeyDown(Input::S)) { WeirdEngine::Logger::log("Save scene name: "); @@ -185,11 +187,11 @@ class MoleculeEditor : public Scene2D { fileName += ".weird"; } - saveScene(ASSETS_PATH "Organisms/" + fileName); + m_services.serialization().saveScene(m_services.resources().assetPath("Organisms/") + fileName); } } - if (Input::GetKey(Input::LeftCtrl) && Input::GetKeyDown(Input::L)) + if (m_services.input().getKey(Input::LeftCtrl) && m_services.input().getKeyDown(Input::L)) { WeirdEngine::Logger::log("Load scene name: "); @@ -205,11 +207,11 @@ class MoleculeEditor : public Scene2D { fileName += ".weird"; } - loadMolecule(ASSETS_PATH "Organisms/" + fileName); + loadMolecule(m_services.resources().assetPath("Organisms/") + fileName); } } - if (Input::GetMouseButtonDown(Input::LeftClick) && !Input::isUIClick()) + if (m_services.input().getMouseButtonDown(Input::LeftClick) && !m_services.input().isUIClick()) { spawnBallAtMouse(); } @@ -310,7 +312,7 @@ class MoleculeEditor : public Scene2D float px = START_X + i * MAT_SPACING; float p[8]{px, MAT_Y, BTN_SIZE - 4.0f}; Entity e; - UIShape& sh = addUIShape(DefaultShapes::CIRCLE, p, e); + UIShape& sh = m_services.shapes().addUIShape(DefaultShapes::CIRCLE, p, e); sh.material = static_cast(i); auto& tog = m_tempEcs->addComponent(e); @@ -319,7 +321,7 @@ class MoleculeEditor : public Scene2D tog.modifierAmount = 5.0f; m_materialToggles[i] = e; - blacklistEntity(e); + m_services.serialization().blacklistEntity(e); } m_tempEcs->getComponent(m_materialToggles[m_selectedMaterial]).active = true; @@ -369,14 +371,14 @@ class MoleculeEditor : public Scene2D { float y = (Display::height - TOOL_Y_START) - (i * TOOL_SPACING); float p[8]{TOOL_X, y, TOOL_BTN_HALF, TOOL_BTN_HALF}; - Entity e = addUIShape(DefaultShapes::BOX, p, static_cast(2)); + Entity e = m_services.shapes().addUIShape(DefaultShapes::BOX, p, static_cast(2)); auto& tog = m_tempEcs->addComponent(e); tog.clickPadding = TOOL_BTN_HALF + 8.0f; tog.parameterModifierMask.set(2); tog.parameterModifierMask.set(3); tog.modifierAmount = 3.0f; m_toolToggles[i] = e; - blacklistEntity(e); + m_services.serialization().blacklistEntity(e); Entity lbl = m_tempEcs->createEntity(); auto& lt = m_tempEcs->addComponent(lbl); @@ -386,27 +388,27 @@ class MoleculeEditor : public Scene2D tx.material = 1; tx.horizontalAlignment = TextRenderer::HorizontalAlignment::Left; tx.verticalAlignment = TextRenderer::VerticalAlignment::Center; - blacklistEntity(lbl); + m_services.serialization().blacklistEntity(lbl); } m_tempEcs->getComponent(m_toolToggles[0]).active = true; float starP[8]{Display::width - GRAV_Y, Display::height - GRAV_Y, GRAV_Y * 0.5f, 5.0f, 10.0f, 0.0f}; - m_gravityToggleEntity = addUIShape(DefaultShapes::STAR, starP, static_cast(2)); + m_gravityToggleEntity = m_services.shapes().addUIShape(DefaultShapes::STAR, starP, static_cast(2)); auto& gravTog = m_tempEcs->addComponent(m_gravityToggleEntity); gravTog.clickPadding = 18.0f; // gravTog.parameterModifierMask.set(2); gravTog.parameterModifierMask.set(5); gravTog.modifierAmount = 10.0f; - blacklistEntity(m_gravityToggleEntity); + m_services.serialization().blacklistEntity(m_gravityToggleEntity); float gridP[8]{Display::width - GRAV_Y, Display::height - GRID_Y, 12.0f, 12.0f}; - m_gridToggleEntity = addUIShape(DefaultShapes::BOX, gridP, static_cast(2)); + m_gridToggleEntity = m_services.shapes().addUIShape(DefaultShapes::BOX, gridP, static_cast(2)); auto& gridTog = m_tempEcs->addComponent(m_gridToggleEntity); gridTog.clickPadding = 18.0f; gridTog.parameterModifierMask.set(2); gridTog.parameterModifierMask.set(3); gridTog.modifierAmount = 3.0f; - blacklistEntity(m_gridToggleEntity); + m_services.serialization().blacklistEntity(m_gridToggleEntity); } void syncToolbar() @@ -457,8 +459,9 @@ class MoleculeEditor : public Scene2D void spawnBallAtMouse() { - auto& cam = m_tempEcs->getComponent(m_mainCamera); - vec2 world = ECS::Camera::screenPositionToWorldPosition2D(cam, vec2(Input::GetMouseX(), Input::GetMouseY())); + auto& cam = m_tempEcs->getComponent(m_services.render().getCameraEntity()); + vec2 world = ECS::Camera::screenPositionToWorldPosition2D( + cam, vec2(m_services.input().getMouseX(), m_services.input().getMouseY())); if (m_gridMode) world = snapToGrid(world); @@ -479,8 +482,9 @@ class MoleculeEditor : public Scene2D vec2 getMouseWorldPosition() { - auto& cam = m_tempEcs->getComponent(m_mainCamera); - return ECS::Camera::screenPositionToWorldPosition2D(cam, vec2(Input::GetMouseX(), Input::GetMouseY())); + auto& cam = m_tempEcs->getComponent(m_services.render().getCameraEntity()); + return ECS::Camera::screenPositionToWorldPosition2D( + cam, vec2(m_services.input().getMouseX(), m_services.input().getMouseY())); } vec2 snapToGrid(vec2 pos, Entity exclude = static_cast(-1)) @@ -545,7 +549,7 @@ class MoleculeEditor : public Scene2D void handleRightMouseDragInput() { - bool rightDown = Input::GetMouseButton(Input::RightClick); + bool rightDown = m_services.input().getMouseButton(Input::RightClick); if (rightDown && !m_rightWasDown) { @@ -627,7 +631,7 @@ class MoleculeEditor : public Scene2D if (m_draggedBall == static_cast(-1) || m_draggedSimulationId < 0) return; - if (Input::GetKeyDown(Input::F)) + if (m_services.input().getKeyDown(Input::F)) { m_keepFixedAfterDrag = true; } @@ -691,8 +695,9 @@ class MoleculeEditor : public Scene2D Entity pickBallAtMouse() { - auto& cam = m_tempEcs->getComponent(m_mainCamera); - vec2 world = ECS::Camera::screenPositionToWorldPosition2D(cam, vec2(Input::GetMouseX(), Input::GetMouseY())); + auto& cam = m_tempEcs->getComponent(m_services.render().getCameraEntity()); + vec2 world = ECS::Camera::screenPositionToWorldPosition2D( + cam, vec2(m_services.input().getMouseX(), m_services.input().getMouseY())); float best = BALL_HIT_RADIUS; Entity bestEntity = static_cast(-1); @@ -768,13 +773,13 @@ class MoleculeEditor : public Scene2D float lineVars[8]{}; computeScreenLineParams(pa, pb, lineVars); - Entity line = addUIShape(DefaultShapes::LINE, lineVars, lineColor); + Entity line = m_services.shapes().addUIShape(DefaultShapes::LINE, lineVars, lineColor); auto& btn = m_tempEcs->addComponent(line); btn.clickPadding = 8.0f; btn.modifierAmount = 0.0f; - blacklistEntity(line); + m_services.serialization().blacklistEntity(line); m_links.push_back({a, b, idA, idB, restDistance, line, type, constraintEnt}); } @@ -805,7 +810,7 @@ class MoleculeEditor : public Scene2D void handleConstraintLineClicks() { // Detect click-down via ShapeButton state. - // ButtonSystem already calls Input::flagUIClick() when a line is clicked, + // ButtonSystem already calls m_services.input().flagUIClick() when a line is clicked, // so ball spawning is suppressed automatically. for (auto& link : m_links) { @@ -815,7 +820,7 @@ class MoleculeEditor : public Scene2D if (btn.state == ButtonState::Down) { m_draggedLink = &link; - m_linkDragStartX = Input::GetMouseX(); + m_linkDragStartX = m_services.input().getMouseX(); m_linkDragStartDist = link.restDistance; m_dragLinkSimIdA.store(link.simulationIdA, std::memory_order_relaxed); m_dragLinkSimIdB.store(link.simulationIdB, std::memory_order_relaxed); @@ -823,7 +828,7 @@ class MoleculeEditor : public Scene2D } } - if (!Input::GetMouseButton(Input::LeftClick)) + if (!m_services.input().getMouseButton(Input::LeftClick)) { m_draggedLink = nullptr; return; @@ -832,7 +837,7 @@ class MoleculeEditor : public Scene2D if (m_draggedLink == nullptr) return; - float dx = (Input::GetMouseX() - m_linkDragStartX) * 0.3f; + float dx = (m_services.input().getMouseX() - m_linkDragStartX) * 0.3f; float newDist = std::round((m_linkDragStartDist + dx) * 10.0f) / 10.0f; newDist = (std::clamp)(newDist, 1.0f, 10.0f); m_draggedLink->restDistance = newDist; @@ -843,7 +848,7 @@ class MoleculeEditor : public Scene2D void updateConstraintLines() { - auto& cam = m_tempEcs->getComponent(m_mainCamera); + auto& cam = m_tempEcs->getComponent(m_services.render().getCameraEntity()); for (auto& link : m_links) { @@ -869,7 +874,7 @@ class MoleculeEditor : public Scene2D void computeScreenLineParams(const vec2& aWorld, const vec2& bWorld, float outParams[8]) { - auto& cam = m_tempEcs->getComponent(m_mainCamera); + auto& cam = m_tempEcs->getComponent(m_services.render().getCameraEntity()); vec2 aScreen = ECS::Camera::worldPosition2DToScreenPosition(cam, aWorld); vec2 bScreen = ECS::Camera::worldPosition2DToScreenPosition(cam, bWorld); @@ -907,7 +912,7 @@ class MoleculeEditor : public Scene2D tx.horizontalAlignment = TextRenderer::HorizontalAlignment::Center; tx.verticalAlignment = TextRenderer::VerticalAlignment::Center; m_tagLabelEntity = lbl; - blacklistEntity(lbl); + m_services.serialization().blacklistEntity(lbl); } // "edit tag" button (a small box) @@ -915,11 +920,11 @@ class MoleculeEditor : public Scene2D static constexpr float BW = 40.0f; static constexpr float BH = 14.0f; float p[8]{Display::width * 0.5f, 90.0f, BW, BH}; - m_tagEditButton = addUIShape(DefaultShapes::BOX, p, static_cast(2)); + m_tagEditButton = m_services.shapes().addUIShape(DefaultShapes::BOX, p, static_cast(2)); auto& btn = m_tempEcs->addComponent(m_tagEditButton); btn.clickPadding = 6.0f; btn.modifierAmount = 0.0f; - blacklistEntity(m_tagEditButton); + m_services.serialization().blacklistEntity(m_tagEditButton); } } @@ -950,16 +955,18 @@ class MoleculeEditor : public Scene2D if (m_tagCircleOuter == static_cast(-1)) { float p[8]{}; - m_tagCircleOuter = addUIShape(DefaultShapes::CIRCLE, p, static_cast(DisplaySettings::Yellow), - CombinationType::Addition, TAG_RING_GROUP); - blacklistEntity(m_tagCircleOuter); - - m_tagCircleInner = addUIShape(DefaultShapes::CIRCLE, p, static_cast(DisplaySettings::Yellow), - CombinationType::Subtraction, TAG_RING_GROUP); - blacklistEntity(m_tagCircleInner); + m_tagCircleOuter = + m_services.shapes().addUIShape(DefaultShapes::CIRCLE, p, static_cast(DisplaySettings::Yellow), + CombinationType::Addition, TAG_RING_GROUP); + m_services.serialization().blacklistEntity(m_tagCircleOuter); + + m_tagCircleInner = + m_services.shapes().addUIShape(DefaultShapes::CIRCLE, p, static_cast(DisplaySettings::Yellow), + CombinationType::Subtraction, TAG_RING_GROUP); + m_services.serialization().blacklistEntity(m_tagCircleInner); } - auto& cam = m_tempEcs->getComponent(m_mainCamera); + auto& cam = m_tempEcs->getComponent(m_services.render().getCameraEntity()); const auto& ht = m_tempEcs->getComponent(m_tagSelectedEntity); vec2 world(ht.position.x, ht.position.y); vec2 screen = ECS::Camera::worldPosition2DToScreenPosition(cam, world); @@ -977,7 +984,7 @@ class MoleculeEditor : public Scene2D // Update the tag label text if (m_tagLabelEntity != static_cast(-1)) { - std::string currentTag = getEntityTag(m_tagSelectedEntity); + std::string currentTag = m_services.tags().getEntityTag(m_tagSelectedEntity); auto& tx = m_tempEcs->getComponent(m_tagLabelEntity); std::string newText = currentTag.empty() ? "tag: (none)" : ("tag: " + currentTag); if (tx.text != newText) @@ -1000,11 +1007,11 @@ class MoleculeEditor : public Scene2D std::getline(std::cin, newTag); if (newTag.empty()) { - removeTag(m_tagSelectedEntity); + m_services.tags().removeTag(m_tagSelectedEntity); } else { - tag(m_tagSelectedEntity, newTag); + m_services.tags().tag(m_tagSelectedEntity, newTag); } } } @@ -1050,12 +1057,12 @@ class MoleculeEditor : public Scene2D size_t prevDistCount = m_tempEcs->getComponentArray()->getSize(); // Load the file — creates new entities / rigid bodies / constraints - TagMap loadedTags = loadWeirdFile(path); + TagMap loadedTags = m_services.serialization().loadWeirdFile(path); // Apply loaded tags to the scene for (const auto& [name, entity] : loadedTags) { - tag(entity, name); + m_services.tags().tag(entity, name); } // Collect new balls: find entities with both Dot and RigidBody2D @@ -1107,12 +1114,12 @@ class MoleculeEditor : public Scene2D float lineVars[8]{}; computeScreenLineParams(pa, pb, lineVars); - Entity line = addUIShape(DefaultShapes::LINE, lineVars, lineColor); + Entity line = m_services.shapes().addUIShape(DefaultShapes::LINE, lineVars, lineColor); auto& btn = m_tempEcs->addComponent(line); btn.clickPadding = 8.0f; btn.modifierAmount = 0.0f; - blacklistEntity(line); + m_services.serialization().blacklistEntity(line); int idA = m_tempEcs->getComponent(a).simulationId; int idB = m_tempEcs->getComponent(b).simulationId; @@ -1141,12 +1148,12 @@ class MoleculeEditor : public Scene2D float lineVars[8]{}; computeScreenLineParams(pa, pb, lineVars); - Entity line = addUIShape(DefaultShapes::LINE, lineVars, lineColor); + Entity line = m_services.shapes().addUIShape(DefaultShapes::LINE, lineVars, lineColor); auto& btn = m_tempEcs->addComponent(line); btn.clickPadding = 8.0f; btn.modifierAmount = 0.0f; - blacklistEntity(line); + m_services.serialization().blacklistEntity(line); int idA = m_tempEcs->getComponent(a).simulationId; int idB = m_tempEcs->getComponent(b).simulationId; diff --git a/tools/scene-editor/include/SceneEditor.h b/tools/scene-editor/include/SceneEditor.h index 0ea38b4..172fce3 100644 --- a/tools/scene-editor/include/SceneEditor.h +++ b/tools/scene-editor/include/SceneEditor.h @@ -89,9 +89,9 @@ class SceneEditor : public Scene2D void onStart(ECSManager& ecs, ServiceProvider& services) override { m_tempEcs = &ecs; - m_debugInput = true; - m_debugFly = true; - m_tempEcs->getComponent(m_mainCamera).position = g_cameraPositon; + m_services.debug().setDebugInput(true); + m_services.debug().setDebugFly(true); + m_tempEcs->getComponent(m_services.render().getCameraEntity()).position = g_cameraPositon; buildShapeButtons(); buildCombToggles(); @@ -102,27 +102,29 @@ class SceneEditor : public Scene2D void onUpdate(ECSManager& ecs, ServiceProvider& services) override { m_tempEcs = &ecs; - g_cameraPositon = m_tempEcs->getComponent(m_mainCamera).position; + g_cameraPositon = m_tempEcs->getComponent(m_services.render().getCameraEntity()).position; - if (Input::GetKeyDown(Input::Q) || Input::GetGamepadButtonDown(Input::GamepadButton::North)) - goToNextScene(); - if (Input::GetKey(Input::LeftCtrl) && Input::GetKeyDown(Input::S)) - saveScene(ASSETS_PATH "example.weird"); + if (m_services.input().getKeyDown(Input::Q) || + m_services.input().getGamepadButtonDown(Input::GamepadButton::North)) + m_services.sceneControl().goToNextScene(); + if (m_services.input().getKey(Input::LeftCtrl) && m_services.input().getKeyDown(Input::S)) + m_services.serialization().saveScene(m_services.resources().assetPath("example.weird")); syncMaterialToggles(); syncCombToggles(); - if (Input::GetMouseButtonDown(Input::LeftClick)) + if (m_services.input().getMouseButtonDown(Input::LeftClick)) onLeftClick(); - if (Input::GetMouseButton(Input::LeftClick)) + if (m_services.input().getMouseButton(Input::LeftClick)) { - auto& cam = m_tempEcs->getComponent(m_mainCamera); - vec2 wp = ECS::Camera::screenPositionToWorldPosition2D(cam, vec2(Input::GetMouseX(), Input::GetMouseY())); + auto& cam = m_tempEcs->getComponent(m_services.render().getCameraEntity()); + vec2 wp = ECS::Camera::screenPositionToWorldPosition2D( + cam, vec2(m_services.input().getMouseX(), m_services.input().getMouseY())); spawnPhysicsEntity(wp); } - if (Input::GetMouseButtonDown(Input::RightClick)) + if (m_services.input().getMouseButtonDown(Input::RightClick)) onRightClick(); - if (Input::GetKeyDown(Input::X) && m_hasSelection) + if (m_services.input().getKeyDown(Input::X) && m_hasSelection) deleteSelected(); refreshPanel(); @@ -139,7 +141,7 @@ class SceneEditor : public Scene2D { auto& transform = transformArray->getDataAtIdx(i); Entity entity = transformArray->getEntityAtIdx(i); - if (entity == m_mainCamera) + if (entity == m_services.render().getCameraEntity()) continue; if (transform.position.y < -10.0f) @@ -164,13 +166,13 @@ class SceneEditor : public Scene2D float p[8]{}; previewParams(types[i], cx, cy, p); - Entity e = addUIShape(types[i], p, 2); + Entity e = m_services.shapes().addUIShape(types[i], p, 2); auto& b = m_tempEcs->addComponent(e); b.modifierAmount = 1.0f; b.clickPadding = 8.0f; m_shapeButtons.push_back({e, types[i]}); - blacklistEntity(e); + m_services.serialization().blacklistEntity(e); } } @@ -244,10 +246,11 @@ class SceneEditor : public Scene2D int g = COMB_GRP_BASE + i; float p1[8]{cx - off * 0.5f, cy, r}; - Entity e1 = addUIShape(DefaultShapes::CIRCLE, p1, static_cast(1), CombinationType::Addition, g); + Entity e1 = m_services.shapes().addUIShape(DefaultShapes::CIRCLE, p1, static_cast(1), + CombinationType::Addition, g); float p2[8]{cx + off * 0.5f, cy, r}; - Entity e2 = addUIShape(DefaultShapes::CIRCLE, p2, static_cast(1), ct[i], g); + Entity e2 = m_services.shapes().addUIShape(DefaultShapes::CIRCLE, p2, static_cast(1), ct[i], g); if (ct[i] == CombinationType::SmoothAddition || ct[i] == CombinationType::SmoothSubtraction) m_tempEcs->getComponent(e2).smoothFactor = 5.0f; @@ -265,9 +268,9 @@ class SceneEditor : public Scene2D tx.horizontalAlignment = TextRenderer::HorizontalAlignment::Center; m_combButtons.push_back({e1, ct[i]}); - blacklistEntity(e1); - blacklistEntity(e2); - blacklistEntity(lbl); + m_services.serialization().blacklistEntity(e1); + m_services.serialization().blacklistEntity(e2); + m_services.serialization().blacklistEntity(lbl); } m_tempEcs->getComponent(m_combButtons[0].toggleEntity).active = true; } @@ -282,7 +285,7 @@ class SceneEditor : public Scene2D float px = START_X + i * MAT_SPACING; float p[8]{px, MAT_Y, BTN_SIZE - 4.0f}; Entity e; - UIShape& sh = addUIShape(DefaultShapes::CIRCLE, p, e); + UIShape& sh = m_services.shapes().addUIShape(DefaultShapes::CIRCLE, p, e); sh.material = static_cast(i); auto& tog = m_tempEcs->addComponent(e); @@ -291,7 +294,7 @@ class SceneEditor : public Scene2D tog.modifierAmount = 5.0f; m_materialToggles[i] = e; - blacklistEntity(e); + m_services.serialization().blacklistEntity(e); } m_tempEcs->getComponent(m_materialToggles[m_selectedMaterial]).active = true; } @@ -309,14 +312,14 @@ class SceneEditor : public Scene2D auto& hdr = m_tempEcs->addComponent(m_selInfoText); hdr.material = 1; hdr.horizontalAlignment = TextRenderer::HorizontalAlignment::Right; - blacklistEntity(m_selInfoText); + m_services.serialization().blacklistEntity(m_selInfoText); for (int i = 0; i < 8; i++) { float py = PANEL_TOP_Y - i * PARAM_GAP; float bp[8]{HIDDEN, py, P_BTN_W, P_BTN_H}; - Entity be = addUIShape(DefaultShapes::BOX, bp, static_cast(3)); + Entity be = m_services.shapes().addUIShape(DefaultShapes::BOX, bp, static_cast(3)); auto& btn = m_tempEcs->addComponent(be); btn.modifierAmount = 1.0f; btn.clickPadding = 3.0f; @@ -331,8 +334,8 @@ class SceneEditor : public Scene2D tx.verticalAlignment = TextRenderer::VerticalAlignment::Center; m_paramBtns[i] = {be, te}; - blacklistEntity(be); - blacklistEntity(te); + m_services.serialization().blacklistEntity(be); + m_services.serialization().blacklistEntity(te); } } @@ -365,8 +368,9 @@ class SceneEditor : public Scene2D void onRightClick() { - auto& cam = m_tempEcs->getComponent(m_mainCamera); - vec2 wp = ECS::Camera::screenPositionToWorldPosition2D(cam, vec2(Input::GetMouseX(), Input::GetMouseY())); + auto& cam = m_tempEcs->getComponent(m_services.render().getCameraEntity()); + vec2 wp = ECS::Camera::screenPositionToWorldPosition2D( + cam, vec2(m_services.input().getMouseX(), m_services.input().getMouseY())); selectNearest(wp); } @@ -392,7 +396,7 @@ class SceneEditor : public Scene2D std::copy(std::begin(s.parameters), std::end(s.parameters), p); p[9] = pos.x; p[10] = pos.y; - float d = m_sdfs[s.distanceFieldId]->getValue(p); + float d = m_services.shapes().getSDFs()[s.distanceFieldId]->getValue(p); if (d < best) { best = d; @@ -644,7 +648,8 @@ class SceneEditor : public Scene2D { float p[8]{}; fillRandomParams(type, p); - Entity e = addShape(type, p, static_cast(m_selectedMaterial), m_selectedCombination); + Entity e = + m_services.shapes().addShape(type, p, static_cast(m_selectedMaterial), m_selectedCombination); if (m_selectedCombination == CombinationType::SmoothAddition || m_selectedCombination == CombinationType::SmoothSubtraction) m_tempEcs->getComponent(e).smoothFactor = 1.5f; @@ -660,7 +665,7 @@ class SceneEditor : public Scene2D auto& sdf = m_tempEcs->addComponent(e); sdf.materialId = static_cast(m_selectedMaterial); m_tempEcs->addComponent(e); - blacklistEntity(e); + m_services.serialization().blacklistEntity(e); } // ===================================================================== @@ -755,7 +760,7 @@ class SceneEditor : public Scene2D vec2 camCentre() { - auto& t = m_tempEcs->getComponent(m_mainCamera); + auto& t = m_tempEcs->getComponent(m_services.render().getCameraEntity()); return vec2(t.position.x, t.position.y); } From 23bdc332bc6fe1906ef67d9830a698b87799f919 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= <49535803+damacaa@users.noreply.github.com> Date: Tue, 11 Aug 2026 00:06:02 +0200 Subject: [PATCH 09/21] Refactor Scene API to use strict internal access and GBuffer struct --- .gitignore | 1 + examples/3d-experiments/include/Classic.h | 14 +++++-- examples/3d-experiments/include/CornellBox.h | 35 +++++++++++------ .../3d-experiments/include/MaterialShowcase.h | 25 ++++++------ examples/opengl-experiments/include/Fire.h | 39 +++++++++++++++---- examples/opengl-experiments/include/Lines.h | 6 ++- examples/opengl-experiments/include/Water.h | 31 ++++++++++----- .../sample-scenes/include/CollisionHandling.h | 2 +- .../include/ServiceShowcaseScene.h | 8 ++-- include/weird-engine/Scene.h | 32 +++++++++++---- include/weird-engine/ecs/ECS.h | 1 + include/weird-engine/systems/RenderSystem.h | 19 ++++++++- include/weird-physics/Simulation2D.h | 10 ++--- .../components/LightComponent.h | 18 +++++++++ .../weird-renderer/core/MeshRenderPipeline.h | 11 ++---- .../weird-renderer/core/SDF3DRenderPipeline.h | 14 +++++-- src/weird-engine/Scene.cpp | 10 ++--- src/weird-physics/Simulation2D.cpp | 6 +-- .../core/MeshRenderPipeline.cpp | 6 +-- src/weird-renderer/core/Renderer.cpp | 14 ++++--- .../core/SDF3DRenderPipeline.cpp | 16 ++++---- 21 files changed, 214 insertions(+), 104 deletions(-) create mode 100644 include/weird-renderer/components/LightComponent.h diff --git a/.gitignore b/.gitignore index 5def43c..7bceffc 100644 --- a/.gitignore +++ b/.gitignore @@ -414,3 +414,4 @@ FodyWeavers.xsd # JetBrains CLion .idea +imgui.ini diff --git a/examples/3d-experiments/include/Classic.h b/examples/3d-experiments/include/Classic.h index 808abca..c1bd538 100644 --- a/examples/3d-experiments/include/Classic.h +++ b/examples/3d-experiments/include/Classic.h @@ -36,7 +36,7 @@ class ClassicScene : public Scene3D MeshRenderer& mr = ecs.addComponent(entity); - auto id = services.resources().getMeshId(services.resources().assetPath("monkey/demo.gltf"), entity, true); + auto id = services.resources().getMeshId("monkey/demo.gltf", entity, true); mr.mesh = id; // mr.materialIndex = floorMaterial.id; @@ -66,8 +66,16 @@ class ClassicScene : public Scene3D CombinationType::Addition, false); } - getLights().push_back(Light{0, glm::vec3(0.0f, 3.0f, 0.0f), 0, glm::vec3(0.35f, 0.45f, 0.5f), - glm::vec4(1.0f, 0.95f, 0.9f, 2.0f)}); + { + Entity entity = ecs.createEntity(); + Transform& t = ecs.addComponent(entity); + t.position = glm::vec3(0.0f, 3.0f, 0.0f); + t.rotation = glm::vec3(0.35f, 0.45f, 0.5f); + + LightComponent& lc = ecs.addComponent(entity); + lc.type = LightType::Directional; + lc.color = glm::vec4(1.0f, 0.95f, 0.9f, 2.0f); + } ecs.getComponent(services.render().getCameraEntity()).position = vec3(0, 2, 10); } diff --git a/examples/3d-experiments/include/CornellBox.h b/examples/3d-experiments/include/CornellBox.h index 5163a17..7d1d43f 100644 --- a/examples/3d-experiments/include/CornellBox.h +++ b/examples/3d-experiments/include/CornellBox.h @@ -14,6 +14,7 @@ class CornellBox : public Scene3D CornellBox() {}; private: + Entity m_sunLight; // Inherited via Scene void onStart(ECSManager& ecs, ServiceProvider& services) override { @@ -101,11 +102,27 @@ class CornellBox : public Scene3D } // Sun - getLights().push_back(Light{0, glm::vec3(0.0f, 0.0f, 0.0f), 0, normalize(glm::vec3(0.0f, 0.0f, 0.0f)), - glm::vec4(1.0f, 1.0f, 1.0f, 0.0f)}); + { + m_sunLight = ecs.createEntity(); + Transform& t = ecs.addComponent(m_sunLight); + t.position = glm::vec3(0.0f, 0.0f, 0.0f); + t.rotation = normalize(glm::vec3(0.0f, 0.0f, 0.0f)); + + LightComponent& lc = ecs.addComponent(m_sunLight); + lc.type = LightType::Directional; + lc.color = glm::vec4(1.0f, 1.0f, 1.0f, 0.0f); + } + + { + Entity entity = ecs.createEntity(); + Transform& t = ecs.addComponent(entity); + t.position = glm::vec3(0.0f, (2.0f * 2.6f) + 0.25f, 0.0f); + t.rotation = glm::vec3(0.0f, 0.0f, 0.0f); - getLights().push_back(Light{1, glm::vec3(0.0f, (2.0f * 2.6f) + 0.25f, 0.0f), 0, glm::vec3(0.0f, 0.0f, 0.0f), - glm::vec4(1.0f, 1.0f, 1.0f, 3.0f)}); + LightComponent& lc = ecs.addComponent(entity); + lc.type = LightType::Point; + lc.color = glm::vec4(1.0f, 1.0f, 1.0f, 3.0f); + } // getLights().push_back( // Light{1, glm::vec3(0.0f, 0.0f, 0.0f), 0, glm::vec3(0.35f, 0.45f, 0.5f), glm::vec4(0.0f, 1.0f, 0.0f, 1.0f)}); @@ -125,12 +142,8 @@ class CornellBox : public Scene3D auto& cameraTransform = ecs.getComponent(services.render().getCameraEntity()); - // getLights()[0].position.x = cameraTransform.position.x; - // getLights()[0].position.y = cameraTransform.position.y; - // getLights()[0].position.z = cameraTransform.position.z; - - // getLights()[0].rotation.x = -cameraTransform.rotation.x; - // getLights()[0].rotation.y = -cameraTransform.rotation.y; - // getLights()[0].rotation.z = -cameraTransform.rotation.z; + auto& lightTransform = ecs.getComponent(m_sunLight); + lightTransform.position = cameraTransform.position; + lightTransform.rotation = -cameraTransform.rotation; } }; diff --git a/examples/3d-experiments/include/MaterialShowcase.h b/examples/3d-experiments/include/MaterialShowcase.h index 5af5e02..3e71fec 100644 --- a/examples/3d-experiments/include/MaterialShowcase.h +++ b/examples/3d-experiments/include/MaterialShowcase.h @@ -14,6 +14,7 @@ class MaterialShowcaseScene : public Scene3D MaterialShowcaseScene() {}; private: + Entity m_sunLight; // Inherited via Scene void onStart(ECSManager& ecs, ServiceProvider& services) override { @@ -124,7 +125,7 @@ class MaterialShowcaseScene : public Scene3D { auto& floorMaterial = createMaterial(); floorMaterial.color = vec4(1.0f, 1.0f, 1.0f, 1.0f); - floorMaterial.metallic = 0.0f; + floorMaterial.metallic = 0.1f; floorMaterial.roughness = 0.3f; floorMaterial.pattern = MaterialPattern::Checkers; floorMaterial.secondaryColor = floorMaterial.color * 0.8f; @@ -156,8 +157,16 @@ class MaterialShowcaseScene : public Scene3D Entity start = services.shapes().addShape(boxId, vars1, mirrorMaterial, CombinationType::Addition, false); } - getLights().push_back(Light{0, glm::vec3(0.0f, 0.0f, 0.0f), 0, normalize(glm::vec3(0.0f, 0.4f, 1.0f)), - glm::vec4(1.0f, 1.0f, 1.0f, 0.5f)}); + { + m_sunLight = ecs.createEntity(); + Transform& t = ecs.addComponent(m_sunLight); + t.position = glm::vec3(0.0f, 0.0f, 0.0f); + t.rotation = normalize(glm::vec3(0.0f, 0.4f, 1.0f)); + + LightComponent& lc = ecs.addComponent(m_sunLight); + lc.type = LightType::Directional; + lc.color = glm::vec4(1.0f, 1.0f, 1.0f, 0.75f); + } // getLights().push_back( // Light{1, glm::vec3(0.0f, 0.0f, 0.0f), 0, glm::vec3(0.35f, 0.45f, 0.5f), glm::vec4(0.0f, 1.0f, 0.0f, 1.0f)}); @@ -176,15 +185,5 @@ class MaterialShowcaseScene : public Scene3D { services.sceneControl().goToNextScene(); } - - auto& cameraTransform = ecs.getComponent(services.render().getCameraEntity()); - - // getLights()[0].position.x = cameraTransform.position.x; - // getLights()[0].position.y = cameraTransform.position.y; - // getLights()[0].position.z = cameraTransform.position.z; - - // getLights()[0].rotation.x = -cameraTransform.rotation.x; - // getLights()[0].rotation.y = -cameraTransform.rotation.y; - // getLights()[0].rotation.z = -cameraTransform.rotation.z; } }; diff --git a/examples/opengl-experiments/include/Fire.h b/examples/opengl-experiments/include/Fire.h index 8476be8..dc85536 100644 --- a/examples/opengl-experiments/include/Fire.h +++ b/examples/opengl-experiments/include/Fire.h @@ -9,6 +9,8 @@ class FireScene : public Scene3D FireScene() {}; private: + Entity m_light0; + Entity m_light1; Shader m_flameShader; Shader m_particlesShader; Shader m_smokeShader; @@ -59,9 +61,26 @@ class FireScene : public Scene3D m_heatDistortionShader = Shader(SHADERS_PATH "3d/geometry.vert", services.resources().assetPath("fire/shaders/heatDistortion.frag")); - getLights().push_back(Light{0, glm::vec3(0.0f), 0, glm::vec3(0.0f), glm::vec4(0.0f)}); - getLights().push_back( - Light{1, glm::vec3(0.0f, 1.0f, 0.0f), 0, glm::vec3(0.0f), glm::vec4(1.0f, 0.95f, 0.9f, 2.0f)}); + m_light0 = ecs.createEntity(); + { + Transform& t = ecs.addComponent(m_light0); + t.position = glm::vec3(0.0f); + t.rotation = glm::vec3(0.0f); + + LightComponent& lc = ecs.addComponent(m_light0); + lc.type = LightType::Directional; + lc.color = glm::vec4(0.0f); + } + m_light1 = ecs.createEntity(); + { + Transform& t = ecs.addComponent(m_light1); + t.position = glm::vec3(0.0f, 1.0f, 0.0f); + t.rotation = glm::vec3(0.0f); + + LightComponent& lc = ecs.addComponent(m_light1); + lc.type = LightType::Point; + lc.color = glm::vec4(1.0f, 0.95f, 0.9f, 2.0f); + } // Load meshes // Quad geom @@ -295,7 +314,8 @@ class FireScene : public Scene3D void onRender(ECSManager& ecs, WeirdRenderer::RenderTarget& renderTarget, ServiceProvider& services) override { - WeirdRenderer::Camera& sceneCamera = getCamera(); + WeirdRenderer::Camera& sceneCamera = + ecs.getComponent(services.render().getCameraEntity()).camera; float time = getTime(); glDepthMask(GL_FALSE); @@ -323,12 +343,15 @@ class FireScene : public Scene3D m_litShader.setUniform("u_far", sceneCamera.farPlane); // Pass light rotation - auto& lights = getLights(); - glm::vec3 position = lights[1].position; + auto& light0_t = ecs.getComponent(m_light0); + auto& light0_lc = ecs.getComponent(m_light0); + auto& light1_t = ecs.getComponent(m_light1); + auto& light1_lc = ecs.getComponent(m_light1); + glm::vec3 position = light1_t.position; m_litShader.setUniform("u_lightPos", position); - glm::vec3 direction = lights[1].rotation; + glm::vec3 direction = light1_t.rotation; m_litShader.setUniform("u_directionalLightDir", direction); - glm::vec4 color = lights[1].color; + glm::vec4 color = light1_lc.color; m_litShader.setUniform("u_lightColor", color); // bind current FBO diff --git a/examples/opengl-experiments/include/Lines.h b/examples/opengl-experiments/include/Lines.h index 2a3f77b..c657fc8 100644 --- a/examples/opengl-experiments/include/Lines.h +++ b/examples/opengl-experiments/include/Lines.h @@ -56,7 +56,11 @@ class LinesScene : public Scene3D void onStart(ECSManager& ecs, ServiceProvider& services) override { services.debug().setDebugFly(false); - getLights().push_back(Light{}); + { + Entity entity = ecs.createEntity(); + ecs.addComponent(entity); + ecs.addComponent(entity); + } { Entity entity = ecs.createEntity(); diff --git a/examples/opengl-experiments/include/Water.h b/examples/opengl-experiments/include/Water.h index 7f0679f..c7d033e 100644 --- a/examples/opengl-experiments/include/Water.h +++ b/examples/opengl-experiments/include/Water.h @@ -11,6 +11,7 @@ class WaterScene : public Scene3D WaterScene() {}; private: + Entity m_light0; Shader m_waterShader; RenderPlane m_renderPlane; @@ -50,8 +51,16 @@ class WaterScene : public Scene3D m_waterShader = Shader(services.resources().assetPath("water/shaders/water.vert"), services.resources().assetPath("water/shaders/water.frag")); - getLights().push_back(Light{0, glm::vec3(0.0f, 0.0f, 0.0f), 0, normalize(glm::vec3(0.0f, 0.4f, 1.0f)), - glm::vec4(1.0f, 1.0f, 1.0f, 0.5f)}); + m_light0 = ecs.createEntity(); + { + Transform& t = ecs.addComponent(m_light0); + t.position = glm::vec3(0.0f, 0.0f, 0.0f); + t.rotation = normalize(glm::vec3(0.0f, 0.4f, 1.0f)); + + LightComponent& lc = ecs.addComponent(m_light0); + lc.type = LightType::Directional; + lc.color = glm::vec4(1.0f, 1.0f, 1.0f, 0.5f); + } m_waterPlane.build(); } @@ -99,7 +108,7 @@ class WaterScene : public Scene3D t.position = vec3(3, 0, 0); MeshRenderer& mr = ecs.addComponent(entity); - auto id = services.resources().getMeshId(services.resources().assetPath("monkey/demo.gltf"), entity, true); + auto id = services.resources().getMeshId("monkey/demo.gltf", entity, true); mr.mesh = id; ecs.addComponent(entity); @@ -146,10 +155,12 @@ class WaterScene : public Scene3D void onRender(ECSManager& ecs, WeirdRenderer::RenderTarget& renderTarget, ServiceProvider& services) override { - WeirdRenderer::Camera& sceneCamera = getCamera(); + WeirdRenderer::Camera& sceneCamera = + ecs.getComponent(services.render().getCameraEntity()).camera; float time = getTime(); - auto& lights = getLights(); + auto& light0_t = ecs.getComponent(m_light0); + auto& light0_lc = ecs.getComponent(m_light0); // ── Snapshot the current scene colour + depth ──────────────────────── // We need to read from these textures while drawing the water plane, @@ -189,15 +200,15 @@ class WaterScene : public Scene3D m_snapshotDepth.bind(1); m_waterShader.setUniform("u_screenSize", glm::vec2((float)w, (float)h)); - int numLights = (std::min)((int)lights.size(), 8); + int numLights = 1; m_waterShader.setUniform("u_numLights", numLights); for (int i = 0; i < numLights; i++) { std::string prefix = "u_lights[" + std::to_string(i) + "]."; - m_waterShader.setUniform(prefix + "position", lights[i].position); - m_waterShader.setUniform(prefix + "direction", lights[i].rotation); - m_waterShader.setUniform(prefix + "color", lights[i].color); - m_waterShader.setUniform(prefix + "type", (int)lights[i].type); + m_waterShader.setUniform(prefix + "position", light0_t.position); + m_waterShader.setUniform(prefix + "direction", light0_t.rotation); + m_waterShader.setUniform(prefix + "color", light0_lc.color); + m_waterShader.setUniform(prefix + "type", (int)light0_lc.type); } glm::mat4 waterModel = glm::mat4(1.0f); diff --git a/examples/sample-scenes/include/CollisionHandling.h b/examples/sample-scenes/include/CollisionHandling.h index 00bebee..f4c8b0c 100644 --- a/examples/sample-scenes/include/CollisionHandling.h +++ b/examples/sample-scenes/include/CollisionHandling.h @@ -67,7 +67,7 @@ class CollisionHandlingScene : public Scene2D } float m_lastTime = 0.0f; - void onPhysicsRigidBodyCollision(Simulation2D& simulation, WeirdEngine::CollisionEvent& event) override + void onPhysicsRigidBodyCollision(Simulation2D& simulation, WeirdEngine::PhysicsCollisionEvent& event) override { float t = getTime(); if (t - m_lastTime < 0.1f) diff --git a/examples/sample-scenes/include/ServiceShowcaseScene.h b/examples/sample-scenes/include/ServiceShowcaseScene.h index 50a49af..a489509 100644 --- a/examples/sample-scenes/include/ServiceShowcaseScene.h +++ b/examples/sample-scenes/include/ServiceShowcaseScene.h @@ -410,7 +410,7 @@ namespace ServiceShowcase // -------------------------------------------------------------- onPhysicsRigidBodyCollision // Physics thread. Body-body collisions: push the pair apart based on their // relative velocity. - inline void onPhysicsRigidBodyCollisionSystem(Simulation2D& simulation, CollisionEvent& event) + inline void onPhysicsRigidBodyCollisionSystem(Simulation2D& simulation, PhysicsCollisionEvent& event) { vec2 va = simulation.getPhysicsVelocity(event.bodyA); vec2 vb = simulation.getPhysicsVelocity(event.bodyB); @@ -425,7 +425,7 @@ namespace ServiceShowcase // when the penetration is deep enough. The character (identified via its // per-body user data) gets a bouncier, more slippery response by tuning // the event before the solver reads it. - inline void onPhysicsShapeCollisionSystem(Simulation2D& simulation, ShapeCollisionEvent& event) + inline void onPhysicsShapeCollisionSystem(Simulation2D& simulation, PhysicsShapeCollisionEvent& event) { if (event.state == CollisionState::START && event.penetration > 0.1f) { @@ -569,12 +569,12 @@ class ServiceShowcaseScene : public Scene2D ServiceShowcase::onPhysicsStepSystem(simulation); } - void onPhysicsRigidBodyCollision(Simulation2D& simulation, CollisionEvent& event) override + void onPhysicsRigidBodyCollision(Simulation2D& simulation, PhysicsCollisionEvent& event) override { ServiceShowcase::onPhysicsRigidBodyCollisionSystem(simulation, event); } - void onPhysicsShapeCollision(Simulation2D& simulation, ShapeCollisionEvent& event) override + void onPhysicsShapeCollision(Simulation2D& simulation, PhysicsShapeCollisionEvent& event) override { ServiceShowcase::onPhysicsShapeCollisionSystem(simulation, event); } diff --git a/include/weird-engine/Scene.h b/include/weird-engine/Scene.h index 913dd13..7d19a16 100644 --- a/include/weird-engine/Scene.h +++ b/include/weird-engine/Scene.h @@ -28,7 +28,7 @@ namespace WeirdEngine { // Raw event data from the physics thread. Read-only: the physics // response has already been applied by the time this is dispatched. - const CollisionEvent& raw; + const PhysicsCollisionEvent& raw; Entity entityA; Entity entityB; }; @@ -37,7 +37,7 @@ namespace WeirdEngine { // Raw event data from the physics thread. Read-only: the physics // response has already been applied by the time this is dispatched. - const ShapeCollisionEvent& raw; + const PhysicsShapeCollisionEvent& raw; Entity entity; }; @@ -45,6 +45,13 @@ namespace WeirdEngine class SceneSerializer; class SceneManager; + namespace WeirdRenderer + { + class AudioEngine; + class Renderer; + class MeshRenderPipeline; + } // namespace WeirdRenderer + class Scene { // Serialization and the service provider reach into the scene's @@ -54,6 +61,9 @@ namespace WeirdEngine friend class ServiceProvider; friend struct SerializationService; + friend class WeirdRenderer::AudioEngine; + friend class WeirdRenderer::Renderer; + public: // ---- Types /// Map from tag name (std::string) to the entity that owns it. @@ -91,6 +101,7 @@ namespace WeirdEngine void renderImGui(); void renderPhysicsStatsUI(); + private: // ---- Scene state access (engine-driven) WeirdRenderer::Camera& getCamera(); std::vector& getLights(); @@ -98,6 +109,7 @@ namespace WeirdEngine AudioRingBuffer& getAudioQueue(); float getFrictionSound(); + public: BackgroundParams& getBackground() { return m_background; @@ -120,6 +132,7 @@ namespace WeirdEngine return m_materials; } + public: // ---- Scene control bool isSceneComplete() const { @@ -169,8 +182,10 @@ namespace WeirdEngine // behavior. Everything else belongs in the onEntity* main-thread // callbacks above (post-response, read-only). virtual void onPhysicsStep(Simulation2D& simulation) {}; - virtual void onPhysicsRigidBodyCollision(Simulation2D& simulation, WeirdEngine::CollisionEvent& event) {}; - virtual void onPhysicsShapeCollision(Simulation2D& simulation, WeirdEngine::ShapeCollisionEvent& event) {}; + virtual void onPhysicsRigidBodyCollision(Simulation2D& simulation, WeirdEngine::PhysicsCollisionEvent& event) { + }; + virtual void onPhysicsShapeCollision(Simulation2D& simulation, WeirdEngine::PhysicsShapeCollisionEvent& event) { + }; ServiceProvider m_services; @@ -184,8 +199,8 @@ namespace WeirdEngine // ---- Internal helpers static void handlePhysicsStep(void* userData); - static void handleCollision(CollisionEvent& event, void* userData); - static void handleShapeCollision(ShapeCollisionEvent& event, void* userData); + static void handleCollision(PhysicsCollisionEvent& event, void* userData); + static void handleShapeCollision(PhysicsShapeCollisionEvent& event, void* userData); // Load scene state from a .weird JSON file void loadFromWeirdFile(const std::string& path); void playSound(const WeirdRenderer::SimpleAudioRequest& audio); @@ -201,8 +216,8 @@ namespace WeirdEngine // ---- Collision queues (physics thread pushes, main thread drains) std::mutex m_collisionQueueMutex; - std::vector m_queuedCollisions; - std::vector m_queuedShapeCollisions; + std::vector m_queuedCollisions; + std::vector m_queuedShapeCollisions; // ---- Audio, draw queue, lights AudioRingBuffer m_audioQueue; @@ -222,6 +237,7 @@ namespace WeirdEngine SDFRenderSystemContext m_UIRenderContext; RenderMode m_renderMode = RenderMode::RayMarching2D; + public: // ---- Scene control state std::string m_nextScene; bool m_isSceneComplete = false; diff --git a/include/weird-engine/ecs/ECS.h b/include/weird-engine/ecs/ECS.h index 72964bd..39ae14f 100644 --- a/include/weird-engine/ecs/ECS.h +++ b/include/weird-engine/ecs/ECS.h @@ -311,6 +311,7 @@ namespace WeirdEngine #include "weird-renderer/components/Camera.h" #include "weird-renderer/components/CustomShape.h" #include "weird-renderer/components/InstancedMeshRenderer.h" +#include "weird-renderer/components/LightComponent.h" #include "weird-renderer/components/MeshRenderer.h" #include "weird-renderer/components/SDFRenderer.h" #include "weird-renderer/components/TextRenderer.h" diff --git a/include/weird-engine/systems/RenderSystem.h b/include/weird-engine/systems/RenderSystem.h index c5da1b3..b6ed627 100644 --- a/include/weird-engine/systems/RenderSystem.h +++ b/include/weird-engine/systems/RenderSystem.h @@ -3,6 +3,7 @@ #include "weird-engine/ResourceManager.h" #include "weird-renderer/resources/DrawCommand.h" +#include "weird-renderer/scene/Light.h" #include namespace WeirdEngine @@ -12,9 +13,11 @@ namespace WeirdEngine namespace RenderSystem { inline void update(ECSManager& ecs, ResourceManager& resourceManager, - std::vector& drawQueue) + std::vector& drawQueue, + std::vector& lights) { drawQueue.clear(); + lights.clear(); ecs.forEach( [&](Entity mOwner, MeshRenderer& mr, Transform& t) @@ -28,6 +31,18 @@ namespace WeirdEngine drawQueue.push_back(cmd); }); + + ecs.forEach( + [&](Entity mOwner, LightComponent& lc, Transform& t) + { + WeirdRenderer::Light light; + light.type = static_cast(lc.type); + light.color = lc.color; + light.position = t.position; + light.rotation = t.rotation; + + lights.push_back(light); + }); } } // namespace RenderSystem -} // namespace WeirdEngine \ No newline at end of file +} // namespace WeirdEngine diff --git a/include/weird-physics/Simulation2D.h b/include/weird-physics/Simulation2D.h index 81df857..dee2b1c 100644 --- a/include/weird-physics/Simulation2D.h +++ b/include/weird-physics/Simulation2D.h @@ -55,14 +55,14 @@ namespace WeirdEngine END }; - struct CollisionEvent + struct PhysicsCollisionEvent { // CollisionState state; SimulationID bodyA; SimulationID bodyB; }; - struct ShapeCollisionEvent + struct PhysicsShapeCollisionEvent { CollisionState state; SimulationID body; @@ -79,8 +79,8 @@ namespace WeirdEngine using StepCallbackFn = void (*)(void*); // Define the function pointer type and include a user data pointer - using CollisionCallbackFn = void (*)(CollisionEvent&, void*); - using ShapeCollisionCallbackFn = void (*)(ShapeCollisionEvent&, void*); + using CollisionCallbackFn = void (*)(PhysicsCollisionEvent&, void*); + using ShapeCollisionCallbackFn = void (*)(PhysicsShapeCollisionEvent&, void*); struct SpatialGridSnapshot { @@ -456,7 +456,7 @@ namespace WeirdEngine std::vector m_objects; std::vector m_collisionMap; - std::vector m_collisionQueue; + std::vector m_collisionQueue; float map(vec2 p); float map(vec2 p, int& closestShape); diff --git a/include/weird-renderer/components/LightComponent.h b/include/weird-renderer/components/LightComponent.h new file mode 100644 index 0000000..8034413 --- /dev/null +++ b/include/weird-renderer/components/LightComponent.h @@ -0,0 +1,18 @@ +#pragma once +#include + +namespace WeirdEngine +{ + enum class LightType : uint32_t + { + Directional = 0, + Point = 1, + Spot = 2 + }; + + struct LightComponent + { + LightType type = LightType::Directional; + glm::vec4 color = glm::vec4(1.0f); + }; +} // namespace WeirdEngine diff --git a/include/weird-renderer/core/MeshRenderPipeline.h b/include/weird-renderer/core/MeshRenderPipeline.h index 064e5b2..7ac7bf9 100644 --- a/include/weird-renderer/core/MeshRenderPipeline.h +++ b/include/weird-renderer/core/MeshRenderPipeline.h @@ -1,18 +1,13 @@ #pragma once #include "weird-renderer/core/RenderTarget.h" +#include "weird-renderer/resources/DrawCommand.h" #include "weird-renderer/resources/Shader.h" #include "weird-renderer/resources/Texture.h" #include "weird-renderer/scene/Camera.h" #include "weird-renderer/scene/Light.h" #include -// Forward declaration to avoid pulling in the full Scene header -namespace WeirdEngine -{ - class Scene; -} - namespace WeirdEngine { namespace WeirdRenderer @@ -42,8 +37,8 @@ namespace WeirdEngine // Renders 3D models into the internal GBuffer. // outputTarget is forwarded to Scene::onRender for custom per-scene rendering. - void render(Scene& scene, RenderTarget& outputTarget, const Camera& camera, - const std::vector& lights); + void render(RenderTarget& outputTarget, const std::vector& drawQueue, + const Camera& camera, const std::vector& lights); // Recreates internal textures for a new resolution. void resize(unsigned int newWidth, unsigned int newHeight); diff --git a/include/weird-renderer/core/SDF3DRenderPipeline.h b/include/weird-renderer/core/SDF3DRenderPipeline.h index 9c02834..6499a62 100644 --- a/include/weird-renderer/core/SDF3DRenderPipeline.h +++ b/include/weird-renderer/core/SDF3DRenderPipeline.h @@ -38,13 +38,21 @@ namespace WeirdEngine Shader& getShader(); + struct GBuffer + { + Texture& albedo; + Texture& worldPos; + Texture& normal; + Texture& material; + Texture& depth; + Texture& backDepth; + }; + // Renders the SDF 3D scene using ray marching with path-traced accumulation. // GBuffer textures come from MeshRenderPipeline and allow the shader to composite // mesh surfaces with SDF lighting (SDFs cast light on meshes; meshes don't affect SDFs). void render(vec4* shapeData, uint32_t dataSize, uint32_t shapeCount, const std::vector& lights, - const Camera& camera, double time, Texture& gbufferAlbedo, Texture& gbufferWorldPos, - Texture& gbufferNormal, Texture& gbufferMaterial, Texture& gbufferDepth, - Texture& gbufferBackDepth, const Material3D* materials); + const Camera& camera, double time, const GBuffer& gbuffer, const Material3D* materials); RenderTarget& getRenderTarget(); Texture& getOutputTexture(); diff --git a/src/weird-engine/Scene.cpp b/src/weird-engine/Scene.cpp index 35971c4..43f4e3a 100644 --- a/src/weird-engine/Scene.cpp +++ b/src/weird-engine/Scene.cpp @@ -200,8 +200,8 @@ namespace WeirdEngine // Process queued collisions // Static vectors retain heap capacity across frames, avoiding // repeated allocations when thousands of collisions are generated. - static std::vector collisions; - static std::vector shapeCollisions; + static std::vector collisions; + static std::vector shapeCollisions; collisions.clear(); shapeCollisions.clear(); { @@ -258,7 +258,7 @@ namespace WeirdEngine { PROFILE_SCOPE("Render Queue update"); - RenderSystem::update(m_ecs, m_resourceManager, m_drawQueue); + RenderSystem::update(m_ecs, m_resourceManager, m_drawQueue, m_lights); } m_ecs.freeRemovedComponents(); @@ -275,7 +275,7 @@ namespace WeirdEngine self->onPhysicsStep(self->m_simulation2D); } - void Scene::handleCollision(CollisionEvent& event, void* userData) + void Scene::handleCollision(PhysicsCollisionEvent& event, void* userData) { Scene* self = static_cast(userData); self->onPhysicsRigidBodyCollision(self->m_simulation2D, event); @@ -284,7 +284,7 @@ namespace WeirdEngine self->m_queuedCollisions.push_back(event); } - void Scene::handleShapeCollision(ShapeCollisionEvent& event, void* userData) + void Scene::handleShapeCollision(PhysicsShapeCollisionEvent& event, void* userData) { Scene* self = static_cast(userData); self->onPhysicsShapeCollision(self->m_simulation2D, event); diff --git a/src/weird-physics/Simulation2D.cpp b/src/weird-physics/Simulation2D.cpp index 4e0e56e..59fbdcb 100644 --- a/src/weird-physics/Simulation2D.cpp +++ b/src/weird-physics/Simulation2D.cpp @@ -501,7 +501,7 @@ namespace WeirdEngine // Check bool currentCollision = false; - ShapeCollisionEvent collisionEvent; + PhysicsShapeCollisionEvent collisionEvent; collisionEvent.body = static_cast(i); // Static shapes @@ -837,8 +837,8 @@ namespace WeirdEngine // Notify collision callback if (m_collisionCallback) { - CollisionEvent event{col.A, col.B}; - m_collisionCallback(event, m_callbackUserData); + PhysicsCollisionEvent event{col.A, col.B}; + m_collisionCallback(event, m_callbackUserData); // Why am I creating a new event and not saving it?????? } } diff --git a/src/weird-renderer/core/MeshRenderPipeline.cpp b/src/weird-renderer/core/MeshRenderPipeline.cpp index 7e93487..064ca95 100644 --- a/src/weird-renderer/core/MeshRenderPipeline.cpp +++ b/src/weird-renderer/core/MeshRenderPipeline.cpp @@ -5,8 +5,6 @@ #include #endif -#include "weird-engine/Scene.h" - namespace WeirdEngine { namespace WeirdRenderer @@ -67,7 +65,8 @@ namespace WeirdEngine return m_backDepthTexture; } - void MeshRenderPipeline::render(Scene& scene, RenderTarget& outputTarget, const Camera& camera, + void MeshRenderPipeline::render(RenderTarget& outputTarget, + const std::vector& drawQueue, const Camera& camera, const std::vector& lights) { // Set GBuffer uniforms for both shaders @@ -87,7 +86,6 @@ namespace WeirdEngine glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Render scene meshes into the GBuffer. - const auto& drawQueue = scene.getDrawQueue(); for (const auto& cmd : drawQueue) { cmd.mesh->draw(m_gbufferShader, camera, cmd.translation, cmd.rotation, cmd.scale, cmd.materialIndex); diff --git a/src/weird-renderer/core/Renderer.cpp b/src/weird-renderer/core/Renderer.cpp index 288be3b..0d207f1 100644 --- a/src/weird-renderer/core/Renderer.cpp +++ b/src/weird-renderer/core/Renderer.cpp @@ -760,7 +760,8 @@ namespace WeirdEngine glFrontFace(GL_CCW); // outputTarget (SDF render target) is forwarded to Scene::onRender callbacks - m_meshPipeline->render(scene, m_3DWorldPipeline->getRenderTarget(), sceneCamera, lights); + m_meshPipeline->render(m_3DWorldPipeline->getRenderTarget(), scene.getDrawQueue(), sceneCamera, + lights); Profiler::get().gpuSync(); } @@ -781,12 +782,13 @@ namespace WeirdEngine static vec4* data3D = nullptr; scene.get3DShapesData(data3D, dataSize3D, shapeCount3D); - m_3DWorldPipeline->render(data3D, dataSize3D, shapeCount3D, lights, sceneCamera, scene.getTime(), - m_meshPipeline->getGBufferAlbedo(), m_meshPipeline->getGBufferWorldPos(), - m_meshPipeline->getGBufferNormal(), m_meshPipeline->getGBufferMaterial(), - m_meshPipeline->getDepthTexture(), m_meshPipeline->getBackDepthTexture(), - scene.getMaterials()); + SDF3DRenderPipeline::GBuffer gbuffer = { + m_meshPipeline->getGBufferAlbedo(), m_meshPipeline->getGBufferWorldPos(), + m_meshPipeline->getGBufferNormal(), m_meshPipeline->getGBufferMaterial(), + m_meshPipeline->getDepthTexture(), m_meshPipeline->getBackDepthTexture()}; + m_3DWorldPipeline->render(data3D, dataSize3D, shapeCount3D, lights, sceneCamera, scene.getTime(), + gbuffer, scene.getMaterials()); glEnable(GL_CULL_FACE); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); diff --git a/src/weird-renderer/core/SDF3DRenderPipeline.cpp b/src/weird-renderer/core/SDF3DRenderPipeline.cpp index 779bf73..86325ca 100644 --- a/src/weird-renderer/core/SDF3DRenderPipeline.cpp +++ b/src/weird-renderer/core/SDF3DRenderPipeline.cpp @@ -56,9 +56,7 @@ namespace WeirdEngine void SDF3DRenderPipeline::render(vec4* shapeData, uint32_t dataSize, uint32_t shapeCount, const std::vector& lights, const Camera& camera, double time, - Texture& gbufferAlbedo, Texture& gbufferWorldPos, Texture& gbufferNormal, - Texture& gbufferMaterial, Texture& gbufferDepth, Texture& gbufferBackDepth, - const Material3D* materials) + const GBuffer& gbuffer, const Material3D* materials) { // Reset frame counter when path tracer is disabled (no accumulation) if (!m_config.enablePathTracer) @@ -117,7 +115,7 @@ namespace WeirdEngine m_accumTexture[previousAccumIdx].bind(0); m_sdfShader.setUniform("t_depthTexture", 1); - gbufferDepth.bind(1); + gbuffer.depth.bind(1); // Shape data buffer m_sdfShader.setUniform("t_shapeBuffer", 2); @@ -126,16 +124,16 @@ namespace WeirdEngine // GBuffer colour attachments m_sdfShader.setUniform("t_gbufferAlbedo", 3); - gbufferAlbedo.bind(3); + gbuffer.albedo.bind(3); m_sdfShader.setUniform("t_gbufferWorldPos", 4); - gbufferWorldPos.bind(4); + gbuffer.worldPos.bind(4); m_sdfShader.setUniform("t_gbufferNormal", 5); - gbufferNormal.bind(5); + gbuffer.normal.bind(5); m_sdfShader.setUniform("t_gbufferMaterial", 6); - gbufferMaterial.bind(6); + gbuffer.material.bind(6); m_sdfShader.setUniform("t_gbufferBackDepth", 7); - gbufferBackDepth.bind(7); + gbuffer.backDepth.bind(7); m_sdfShader.setUniform("u_loadedObjects", (int)dataSize); m_sdfShader.setUniform("u_customShapeCount", (int)shapeCount); From 9eef44ba9941dca3a4a4b683c6e690efa50e3f7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= <49535803+damacaa@users.noreply.github.com> Date: Tue, 11 Aug 2026 00:10:57 +0200 Subject: [PATCH 10/21] docs: add commit naming guidelines to AGENTS.md --- AGENTS.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 3688777..f1531eb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -60,3 +60,9 @@ There are no tests. CI runs `ctest` but no test targets are defined. - The `build/` and `build-muos/` directories are separate CMake trees; do not mix them. - `compile_flags.txt` exists for clangd; it does not drive the actual build. - The `.vscode/settings.json` enables `WEIRD_ENGINE_BUILD_EXAMPLES=ON` by default. + +## Commit Guidelines + +- **Naming Convention**: Prefix all commit messages with the affected module or system name, followed by a colon and a space. Keep the prefix lowercase. + - Prefix Examples: `scene:`, `core:`, `physics:`, `renderer:`, `examples:`, `tools:`, `core/assert:` + - Full Example: `physics: add BodyUserData for attaching custom data to rigidbodies` From 246121c2eec2a7cdbf572b3bd0774b0031a1d3c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= <49535803+damacaa@users.noreply.github.com> Date: Tue, 11 Aug 2026 00:22:11 +0200 Subject: [PATCH 11/21] docs: add ServiceProvider pattern documentation to AGENTS.md --- AGENTS.md | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index f1531eb..d15fa6e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -9,12 +9,6 @@ cmake --build build # Run the main example ./build/examples/sample-scenes/WeirdSamples - -# muOS cross-compile (aarch64, uses podman) -./scripts/anbernic/build-muos.sh -./scripts/anbernic/deploy-muos.sh # build + deploy via MTP -./scripts/anbernic/deploy-muos.sh --no-build # deploy only -./scripts/anbernic/fetch-logs.sh # pull log.txt + screenshots ``` There are no tests. CI runs `ctest` but no test targets are defined. @@ -42,6 +36,7 @@ There are no tests. CI runs `ctest` but no test targets are defined. - `sample-scenes` → `WeirdSamples` (main demo) - `3d-experiments`, `opengl-experiments` — other demos - `empty-project` — starter template +- **ServiceProvider pattern**: `Scene` state is highly encapsulated. Game systems should use `ServiceProvider` (passed into update/render loops) to interact with rendering, audio, or physics systems instead of accessing `Scene` internals. - Entry point for games: `WeirdEngine::start(sceneManager, ...)` in `include/weird-engine.h`. ## Dependencies From 787381cd80995c581e835b6ec5494d94888571bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= <49535803+damacaa@users.noreply.github.com> Date: Tue, 11 Aug 2026 00:26:44 +0200 Subject: [PATCH 12/21] scene: remove Simulation2D getter form the physics service --- examples/sample-scenes/include/ServiceShowcaseScene.h | 3 ++- include/weird-engine/services/ServiceProvider.h | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/examples/sample-scenes/include/ServiceShowcaseScene.h b/examples/sample-scenes/include/ServiceShowcaseScene.h index a489509..bcee596 100644 --- a/examples/sample-scenes/include/ServiceShowcaseScene.h +++ b/examples/sample-scenes/include/ServiceShowcaseScene.h @@ -351,7 +351,8 @@ namespace ServiceShowcase auto& rb = ecs.getComponent(leader); glm::vec2 current = glm::vec2(ecs.getComponent(leader).position); - services.physics().sim().setVelocity(rb.simulationId, (target - current) * 2.0f); + rb.velocity = (target - current) * 2.0f; + ecs.setComponentDirty(rb); } // -------------------------------------------------------- update: ui system diff --git a/include/weird-engine/services/ServiceProvider.h b/include/weird-engine/services/ServiceProvider.h index efc73c5..59b498a 100644 --- a/include/weird-engine/services/ServiceProvider.h +++ b/include/weird-engine/services/ServiceProvider.h @@ -81,10 +81,10 @@ namespace WeirdEngine Simulation2D& simulation; std::vector>& sdfs; - Simulation2D& sim() - { - return simulation; - } + // Simulation2D& sim() + // { + // return simulation; + // } void setGravity(float gravity) { From 5884f84bcf2677fc6fb57062c39d89e58b6fae01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= <49535803+damacaa@users.noreply.github.com> Date: Tue, 11 Aug 2026 00:50:59 +0200 Subject: [PATCH 13/21] scene: encapsulate internals and enforce ServiceProvider usage - Made Scene lifecycle and engine-driven methods private - Added friend declaration for Detail::runFrame to access Scene internals - Restored custom onRender signature (ecs, services, renderTarget) in Scene and inheriting classes - Refactored all example scenes and tools to access engine services through the ServiceProvider facade rather than direct Scene access or private variables --- examples/3d-experiments/include/Classic.h | 12 +- examples/3d-experiments/include/CornellBox.h | 8 +- .../3d-experiments/include/MaterialShowcase.h | 18 +-- examples/opengl-experiments/include/Fire.h | 4 +- examples/opengl-experiments/include/Lines.h | 4 +- examples/opengl-experiments/include/Water.h | 6 +- .../sample-scenes/include/AquariumScene.h | 2 +- .../sample-scenes/include/CollisionHandling.h | 4 +- examples/sample-scenes/include/LifeScene.h | 6 +- examples/sample-scenes/include/RopeScene.h | 12 +- .../include/ServiceShowcaseScene.h | 6 +- .../include/ShapesCombinations.h | 2 +- examples/sample-scenes/include/WalkScene.h | 2 +- include/weird-engine/Scene.h | 118 +++++++++--------- src/weird-engine/Scene.cpp | 2 +- .../molecule-editor/include/MoleculeEditor.h | 118 +++++++++--------- tools/scene-editor/include/SceneEditor.h | 73 +++++------ 17 files changed, 202 insertions(+), 195 deletions(-) diff --git a/examples/3d-experiments/include/Classic.h b/examples/3d-experiments/include/Classic.h index c1bd538..2b2466d 100644 --- a/examples/3d-experiments/include/Classic.h +++ b/examples/3d-experiments/include/Classic.h @@ -16,13 +16,13 @@ class ClassicScene : public Scene3D { services.debug().setDebugFly(true); - auto& redMat = createMaterial(); + auto& redMat = services.materials().createMaterial(); redMat.color = vec4(.8f, 0.2f, 0.2f, 1.0f); - auto& orangeMat = createMaterial(); + auto& orangeMat = services.materials().createMaterial(); orangeMat.color = vec4(.95f, 0.4f, 0.1f, 1.0f); - auto& floorMaterial = createMaterial(); + auto& floorMaterial = services.materials().createMaterial(); floorMaterial.color = vec4(1.0f, 1.0f, 1.0f, 1.0f); floorMaterial.secondaryColor = vec4(0.4f, 0.4f, 0.6f, 1.0f); floorMaterial.metallic = 0.7f; @@ -97,9 +97,9 @@ class ClassicScene : public Scene3D { Transform& t = ecs.getComponent(m_ball); - // t.position.z = 10 * sinf(getTime()); - t.position.x = 2.0f * sinf(-getTime()); - t.position.z = 2.0f * cosf(-getTime()); + // t.position.z = 10 * sinf(services.time().time()); + t.position.x = 2.0f * sinf(-services.time().time()); + t.position.z = 2.0f * cosf(-services.time().time()); } // t.position = cameraTransform.position + vec3(-10, -6, -20); diff --git a/examples/3d-experiments/include/CornellBox.h b/examples/3d-experiments/include/CornellBox.h index 7d1d43f..9d3f07c 100644 --- a/examples/3d-experiments/include/CornellBox.h +++ b/examples/3d-experiments/include/CornellBox.h @@ -20,20 +20,20 @@ class CornellBox : public Scene3D { services.debug().setDebugFly(true); - auto& ballMat = createMaterial(); + auto& ballMat = services.materials().createMaterial(); ballMat.color = vec4(1.0f); ballMat.metallic = 1.0f; ballMat.roughness = 0.005f; - auto& redMat = createMaterial(); + auto& redMat = services.materials().createMaterial(); redMat.color = vec4(.8f, 0.2f, 0.2f, 1.0f); - auto& greenMat = createMaterial(); + auto& greenMat = services.materials().createMaterial(); greenMat.color = vec4(0.1f, .95f, 0.1f, 1.0f); greenMat.metallic = 0.5f; greenMat.roughness = 0.1f; - auto& whiteMat = createMaterial(); + auto& whiteMat = services.materials().createMaterial(); whiteMat.color = vec4(1.0f, 1.0f, 1.0f, 1.0f); { diff --git a/examples/3d-experiments/include/MaterialShowcase.h b/examples/3d-experiments/include/MaterialShowcase.h index 3e71fec..125f6e9 100644 --- a/examples/3d-experiments/include/MaterialShowcase.h +++ b/examples/3d-experiments/include/MaterialShowcase.h @@ -25,7 +25,7 @@ class MaterialShowcaseScene : public Scene3D Transform& t = ecs.addComponent(entity); t.position = vec3(-0.5f, -2.0f, 0); - auto& mat = createMaterial(); + auto& mat = services.materials().createMaterial(); mat.color = vec4(1.0f); mat.metallic = 1.0f; mat.roughness = 0.0f; @@ -47,7 +47,7 @@ class MaterialShowcaseScene : public Scene3D }; { - auto& mat = createMaterial(); + auto& mat = services.materials().createMaterial(); mat.color = vec4(.95f, 0.4f, 0.1f, 1.0f); mat.metallic = 0.5f; mat.roughness = 0.1f; @@ -58,7 +58,7 @@ class MaterialShowcaseScene : public Scene3D } { - auto& mat = createMaterial(); + auto& mat = services.materials().createMaterial(); mat.color = vec4(0.5f, 1.0f, 0.5f, 1.0f); mat.metallic = 0.05f; mat.roughness = 0.99f; @@ -69,7 +69,7 @@ class MaterialShowcaseScene : public Scene3D } { - auto& mat = createMaterial(); + auto& mat = services.materials().createMaterial(); mat.color = vec4(1.0f, 0.3f, .6f, 1.0f); mat.secondaryColor = vec4(1.0f, 0.2f, 0.05f, 1.0f); @@ -81,7 +81,7 @@ class MaterialShowcaseScene : public Scene3D } { - auto& mat = createMaterial(); + auto& mat = services.materials().createMaterial(); mat.color = vec4(0.0f, 10.9f, 10.9f, 1.0f); mat.metallic = 0.05f; mat.roughness = 0.99f; @@ -92,7 +92,7 @@ class MaterialShowcaseScene : public Scene3D } { - auto& mat = createMaterial(); + auto& mat = services.materials().createMaterial(); mat.color = vec4(0.5f, 0.5f, 0.8f, 1.0f); mat.secondaryColor = vec4(1.0f, 1.0f, 1.0f, 1.0f); mat.metallic = 0.3f; @@ -104,7 +104,7 @@ class MaterialShowcaseScene : public Scene3D } { - auto& mat = createMaterial(); + auto& mat = services.materials().createMaterial(); mat.color = vec4(0.85f, 0.7f, 0.1f, 0.5f); mat.metallic = 0.5f; mat.roughness = 0.0f; @@ -123,7 +123,7 @@ class MaterialShowcaseScene : public Scene3D } { - auto& floorMaterial = createMaterial(); + auto& floorMaterial = services.materials().createMaterial(); floorMaterial.color = vec4(1.0f, 1.0f, 1.0f, 1.0f); floorMaterial.metallic = 0.1f; floorMaterial.roughness = 0.3f; @@ -135,7 +135,7 @@ class MaterialShowcaseScene : public Scene3D CombinationType::Addition, false); } - auto& mirrorMaterial = createMaterial(); + auto& mirrorMaterial = services.materials().createMaterial(); mirrorMaterial.color = vec4(1.0f); mirrorMaterial.metallic = 1.0f; mirrorMaterial.roughness = 0.0f; diff --git a/examples/opengl-experiments/include/Fire.h b/examples/opengl-experiments/include/Fire.h index dc85536..1007cd0 100644 --- a/examples/opengl-experiments/include/Fire.h +++ b/examples/opengl-experiments/include/Fire.h @@ -312,11 +312,11 @@ class FireScene : public Scene3D glDisable(GL_BLEND); } - void onRender(ECSManager& ecs, WeirdRenderer::RenderTarget& renderTarget, ServiceProvider& services) override + void onRender(ECSManager& ecs, ServiceProvider& services, WeirdRenderer::RenderTarget& renderTarget) override { WeirdRenderer::Camera& sceneCamera = ecs.getComponent(services.render().getCameraEntity()).camera; - float time = getTime(); + float time = services.time().time(); glDepthMask(GL_FALSE); glDisable(GL_DEPTH_TEST); diff --git a/examples/opengl-experiments/include/Lines.h b/examples/opengl-experiments/include/Lines.h index c657fc8..3be0b32 100644 --- a/examples/opengl-experiments/include/Lines.h +++ b/examples/opengl-experiments/include/Lines.h @@ -28,7 +28,7 @@ class LinesScene : public Scene3D { { - auto& whiteMat = createMaterial(); + auto& whiteMat = services.materials().createMaterial(); m_whiteMatId = whiteMat.id; whiteMat.pattern = MaterialPattern::Checkers; @@ -96,7 +96,7 @@ class LinesScene : public Scene3D monkeyTransform.position.z -= 5.0f; } - void onRender(ECSManager& ecs, WeirdRenderer::RenderTarget& renderTarget, ServiceProvider& services) override + void onRender(ECSManager& ecs, ServiceProvider& services, WeirdRenderer::RenderTarget& renderTarget) override { m_lineRender->bind(); glClearColor(0, 0, 0, 0); // Set clear color diff --git a/examples/opengl-experiments/include/Water.h b/examples/opengl-experiments/include/Water.h index c7d033e..4cbe00c 100644 --- a/examples/opengl-experiments/include/Water.h +++ b/examples/opengl-experiments/include/Water.h @@ -90,7 +90,7 @@ class WaterScene : public Scene3D { services.debug().setDebugFly(true); - auto& redMat = createMaterial(); + auto& redMat = services.materials().createMaterial(); redMat.color = vec4(.8f, 0.2f, 0.2f, 1.0f); { @@ -153,11 +153,11 @@ class WaterScene : public Scene3D } } - void onRender(ECSManager& ecs, WeirdRenderer::RenderTarget& renderTarget, ServiceProvider& services) override + void onRender(ECSManager& ecs, ServiceProvider& services, WeirdRenderer::RenderTarget& renderTarget) override { WeirdRenderer::Camera& sceneCamera = ecs.getComponent(services.render().getCameraEntity()).camera; - float time = getTime(); + float time = services.time().time(); auto& light0_t = ecs.getComponent(m_light0); auto& light0_lc = ecs.getComponent(m_light0); diff --git a/examples/sample-scenes/include/AquariumScene.h b/examples/sample-scenes/include/AquariumScene.h index 9914e0d..34ebdf8 100644 --- a/examples/sample-scenes/include/AquariumScene.h +++ b/examples/sample-scenes/include/AquariumScene.h @@ -77,7 +77,7 @@ class AquariumScene : public Scene2D services.debug().setDebugInput(true); services.debug().setDebugFly(true); - auto& background = getBackground(); + auto& background = services.render().getBackground(); background.type = BackgroundType::Sky; background.primaryColor = vec4(98, 129, 240, 255) / 255.0f; background.secondaryColor = vec4(86, 208, 197, 255) / 255.0f; diff --git a/examples/sample-scenes/include/CollisionHandling.h b/examples/sample-scenes/include/CollisionHandling.h index f4c8b0c..d7264d4 100644 --- a/examples/sample-scenes/include/CollisionHandling.h +++ b/examples/sample-scenes/include/CollisionHandling.h @@ -58,8 +58,10 @@ class CollisionHandlingScene : public Scene2D ecs.getComponent(services.render().getCameraEntity()).position = g_cameraPositon; } + float m_currentTime = 0.0f; void onUpdate(ECSManager& ecs, ServiceProvider& services) override { + m_currentTime = services.time().time(); if (services.input().getKeyDown(Input::Q) || services.input().getGamepadButtonDown(Input::GamepadButton::North)) { services.sceneControl().goToNextScene(); @@ -69,7 +71,7 @@ class CollisionHandlingScene : public Scene2D float m_lastTime = 0.0f; void onPhysicsRigidBodyCollision(Simulation2D& simulation, WeirdEngine::PhysicsCollisionEvent& event) override { - float t = getTime(); + float t = m_currentTime; if (t - m_lastTime < 0.1f) return; // Avoid multiple collisions in a short time diff --git a/examples/sample-scenes/include/LifeScene.h b/examples/sample-scenes/include/LifeScene.h index 4817eaa..23b12cf 100644 --- a/examples/sample-scenes/include/LifeScene.h +++ b/examples/sample-scenes/include/LifeScene.h @@ -99,12 +99,12 @@ class LifeScene : public Scene2D services.sceneControl().goToNextScene(); } - updateHeads(delta, ecs); + updateHeads(delta, ecs, services); } - void updateHeads(float delta, ECSManager& ecs) + void updateHeads(float delta, ECSManager& ecs, ServiceProvider& services) { - float animationT = std::sin(getTime() * 10.0f) * 0.5f + 0.25f; + float animationT = std::sin(services.time().time() * 10.0f) * 0.5f + 0.25f; auto headArray = ecs.getComponentArray(); diff --git a/examples/sample-scenes/include/RopeScene.h b/examples/sample-scenes/include/RopeScene.h index 4ac0250..b111f03 100644 --- a/examples/sample-scenes/include/RopeScene.h +++ b/examples/sample-scenes/include/RopeScene.h @@ -123,9 +123,9 @@ class RopeScene : public Scene2D ecs.getComponent(services.render().getCameraEntity()).position = g_cameraPositon; } - void throwBalls(ECSManager& ecs) + void throwBalls(ECSManager& ecs, ServiceProvider& services) { - if (getTime() <= m_lastSpawnTime + 0.1) + if (services.time().time() <= m_lastSpawnTime + 0.1) { return; } @@ -147,7 +147,7 @@ class RopeScene : public Scene2D rb.pendingImpulseForce += vec2(20.0f, 0.0f); } - m_lastSpawnTime = getTime(); + m_lastSpawnTime = services.time().time(); } void onUpdate(ECSManager& ecs, ServiceProvider& services) override @@ -163,8 +163,8 @@ class RopeScene : public Scene2D // Animate custom shape over time if (m_star != INVALID_ENTITY) { - // Instead of getSimulation().getSimulationTime(), we can just use getTime() if Scene provides it, or track - // delta. + // Instead of getSimulation().getSimulationTime(), we can just use services.time().time() if Scene provides + // it, or track delta. static float animTime = 0.0f; animTime += delta; auto& cs = ecs.getComponent(m_star); @@ -175,7 +175,7 @@ class RopeScene : public Scene2D if (services.input().getKey(Input::E) || services.input().getGamepadButton(Input::GamepadButton::West)) { - throwBalls(ecs); + throwBalls(ecs, services); } static vec2 boxStart; diff --git a/examples/sample-scenes/include/ServiceShowcaseScene.h b/examples/sample-scenes/include/ServiceShowcaseScene.h index bcee596..e4fe7da 100644 --- a/examples/sample-scenes/include/ServiceShowcaseScene.h +++ b/examples/sample-scenes/include/ServiceShowcaseScene.h @@ -135,8 +135,8 @@ namespace ServiceShowcase // Materials through the provider Material3D& floorMaterial = services.materials().createMaterial(); - floorMaterial.color = vec4(0.2f, 0.5f, 0.9f, 1.0f); - floorMaterial.metallic = 0.5f; + floorMaterial.color = vec4(0.8f, 0.8f, 0.8f, 1.0f); + floorMaterial.roughness = 1.0f; Material3D& ringMaterial = services.materials().createMaterial(); ringMaterial.color = vec4(0.9f, 0.3f, 0.2f, 1.0f); @@ -536,7 +536,7 @@ class ServiceShowcaseScene : public Scene2D // Note the firing rules: onRender only fires for 3D / both render modes, // while onImGuiRender fires for every scene, 2D and 3D alike (it is just // the debug UI). - void onRender(ECSManager& ecs, WeirdRenderer::RenderTarget& renderTarget, ServiceProvider& services) override + void onRender(ECSManager& ecs, ServiceProvider& services, WeirdRenderer::RenderTarget& renderTarget) override { static bool logged = false; if (!logged) diff --git a/examples/sample-scenes/include/ShapesCombinations.h b/examples/sample-scenes/include/ShapesCombinations.h index d7f2164..6884e60 100644 --- a/examples/sample-scenes/include/ShapesCombinations.h +++ b/examples/sample-scenes/include/ShapesCombinations.h @@ -154,7 +154,7 @@ class ShapeCombinatiosScene : public Scene2D for (int i = 0; i < m_uiPoints.size(); i++) { // Calculate angle: Time moves them, 'i' spreads them out - float angle = (getTime() * speed) + (i * spacing); + float angle = (services.time().time() * speed) + (i * spacing); float x = center.x + std::cos(angle) * radius; float y = center.y + std::sin(angle) * radius; diff --git a/examples/sample-scenes/include/WalkScene.h b/examples/sample-scenes/include/WalkScene.h index fa2a01c..1f97bb2 100644 --- a/examples/sample-scenes/include/WalkScene.h +++ b/examples/sample-scenes/include/WalkScene.h @@ -35,7 +35,7 @@ class WalkScene : public Scene2D services.debug().setDebugInput(true); services.debug().setDebugFly(true); - auto& background = getBackground(); + auto& background = services.render().getBackground(); background.type = BackgroundType::Sky; background.primaryColor = vec4(0.2f, 0.55f, 0.9f, 1.0f); background.secondaryColor = vec4(0.4f, 0.75f, 0.85f, 1.0f); diff --git a/include/weird-engine/Scene.h b/include/weird-engine/Scene.h index 7d19a16..6ca8e19 100644 --- a/include/weird-engine/Scene.h +++ b/include/weird-engine/Scene.h @@ -52,6 +52,12 @@ namespace WeirdEngine class MeshRenderPipeline; } // namespace WeirdRenderer + namespace Detail + { + struct RuntimeContext; + void runFrame(RuntimeContext& ctx); + } // namespace Detail + class Scene { // Serialization and the service provider reach into the scene's @@ -63,6 +69,7 @@ namespace WeirdEngine friend class WeirdRenderer::AudioEngine; friend class WeirdRenderer::Renderer; + friend void Detail::runFrame(Detail::RuntimeContext& ctx); public: // ---- Types @@ -73,17 +80,51 @@ namespace WeirdEngine using RaymarchResult = ::WeirdEngine::RaymarchResult; - // ---- Lifecycle (engine-driven) - Scene(); virtual ~Scene(); - void start(); - // Called by the engine once per frame with the variable frame delta. - void update(double delta, double time); + // ---- Global SDF registry (engine-level, shared across scenes) + static ShapeId registerDefaultSDF(std::shared_ptr sdf); + static const std::vector>& getGlobalSDFs(); + + protected: + // Internal constructors: sets the render mode for Scene2D/Scene3D/SceneBoth. + Scene(); + Scene(RenderMode mode); + + // ---- Lifecycle callbacks + virtual void onCreate(ECSManager& ecs, ServiceProvider& services) {}; + virtual void onStart(ECSManager& ecs, ServiceProvider& services) {} + virtual void onUpdate(ECSManager& ecs, ServiceProvider& services) {}; + virtual void onDestroy(ECSManager& ecs, ServiceProvider& services) {}; + virtual void onImGuiRender(ECSManager& ecs, ServiceProvider& services) {}; + virtual void onRender(ECSManager& ecs, ServiceProvider& services, WeirdRenderer::RenderTarget& renderTarget) {}; + + // ---- Main thread collision callbacks (onEntity* family). Fire after + // the physics response has been applied; the events are read-only. + // m_ecs is safe to use here (the physics thread only ever touches + // Simulation2D internals). See the onPhysics* callbacks below for the + // pre-response, mutable equivalents. + virtual void onEntityCollision(ECSManager& ecs, ServiceProvider& services, + WeirdEngine::EntityCollisionEvent& event) {}; + virtual void onEntityShapeCollision(ECSManager& ecs, ServiceProvider& services, + WeirdEngine::EntityShapeCollisionEvent& event) {}; + + // ---- Physics thread callbacks (onPhysics* family). Fire on the + // physics thread mid-step; no ECS access here. The collision events + // are pre-response and mutable: tune the event fields (friction, + // absorption), queue impulses, or call fix() to alter the solver's + // behavior. Everything else belongs in the onEntity* main-thread + // callbacks above (post-response, read-only). + virtual void onPhysicsStep(Simulation2D& simulation) {}; + virtual void onPhysicsRigidBodyCollision(Simulation2D& simulation, WeirdEngine::PhysicsCollisionEvent& event) { + }; + virtual void onPhysicsShapeCollision(Simulation2D& simulation, WeirdEngine::PhysicsShapeCollisionEvent& event) { + }; - // Called by SceneManager right before this scene is destroyed during - // a scene transition. Runs on the main thread; the physics thread may - // still be stepping, so keep the same thread rules as the callbacks. + private: + // ---- Lifecycle (engine-driven) + void start(); + void update(double delta, double time); void destroy() { onDestroy(m_ecs, m_services); @@ -101,7 +142,6 @@ namespace WeirdEngine void renderImGui(); void renderPhysicsStatsUI(); - private: // ---- Scene state access (engine-driven) WeirdRenderer::Camera& getCamera(); std::vector& getLights(); @@ -109,7 +149,6 @@ namespace WeirdEngine AudioRingBuffer& getAudioQueue(); float getFrictionSound(); - public: BackgroundParams& getBackground() { return m_background; @@ -132,7 +171,6 @@ namespace WeirdEngine return m_materials; } - public: // ---- Scene control bool isSceneComplete() const { @@ -149,54 +187,6 @@ namespace WeirdEngine m_sceneFilePath = path; } - // ---- Global SDF registry (engine-level, shared across scenes) - static ShapeId registerDefaultSDF(std::shared_ptr sdf); - static const std::vector>& getGlobalSDFs(); - - protected: - // Internal constructor: sets the render mode for Scene2D/Scene3D/SceneBoth. - Scene(RenderMode mode); - - // ---- Lifecycle callbacks - virtual void onCreate(ECSManager& ecs, ServiceProvider& services) {}; - virtual void onStart(ECSManager& ecs, ServiceProvider& services) {} - virtual void onUpdate(ECSManager& ecs, ServiceProvider& services) {}; - virtual void onDestroy(ECSManager& ecs, ServiceProvider& services) {}; - virtual void onRender(ECSManager& ecs, WeirdRenderer::RenderTarget& renderTarget, ServiceProvider& services) {}; - virtual void onImGuiRender(ECSManager& ecs, ServiceProvider& services) {}; - - // ---- Main thread collision callbacks (onEntity* family). Fire after - // the physics response has been applied; the events are read-only. - // m_ecs is safe to use here (the physics thread only ever touches - // Simulation2D internals). See the onPhysics* callbacks below for the - // pre-response, mutable equivalents. - virtual void onEntityCollision(ECSManager& ecs, ServiceProvider& services, - WeirdEngine::EntityCollisionEvent& event) {}; - virtual void onEntityShapeCollision(ECSManager& ecs, ServiceProvider& services, - WeirdEngine::EntityShapeCollisionEvent& event) {}; - - // ---- Physics thread callbacks (onPhysics* family). Fire on the - // physics thread mid-step; no ECS access here. The collision events - // are pre-response and mutable: tune the event fields (friction, - // absorption), queue impulses, or call fix() to alter the solver's - // behavior. Everything else belongs in the onEntity* main-thread - // callbacks above (post-response, read-only). - virtual void onPhysicsStep(Simulation2D& simulation) {}; - virtual void onPhysicsRigidBodyCollision(Simulation2D& simulation, WeirdEngine::PhysicsCollisionEvent& event) { - }; - virtual void onPhysicsShapeCollision(Simulation2D& simulation, WeirdEngine::PhysicsShapeCollisionEvent& event) { - }; - - ServiceProvider m_services; - - private: - // ---- Shared state (available to derived scenes) - Entity m_mainCamera; - ResourceManager m_resourceManager; - std::vector> m_sdfs; - bool m_debugFly = false; - bool m_debugInput = false; - // ---- Internal helpers static void handlePhysicsStep(void* userData); static void handleCollision(PhysicsCollisionEvent& event, void* userData); @@ -208,6 +198,15 @@ namespace WeirdEngine Entity getEntityForSimulationId(SimulationID simulationId, std::shared_ptr> rigidBodies); + ServiceProvider m_services; + + // ---- Shared state (managed via ServiceProvider) + Entity m_mainCamera; + ResourceManager m_resourceManager; + std::vector> m_sdfs; + bool m_debugFly = false; + bool m_debugInput = false; + // ---- Simulation ECSManager m_ecs; Simulation2D m_simulation2D; @@ -237,7 +236,6 @@ namespace WeirdEngine SDFRenderSystemContext m_UIRenderContext; RenderMode m_renderMode = RenderMode::RayMarching2D; - public: // ---- Scene control state std::string m_nextScene; bool m_isSceneComplete = false; diff --git a/src/weird-engine/Scene.cpp b/src/weird-engine/Scene.cpp index 43f4e3a..fa7c5c7 100644 --- a/src/weird-engine/Scene.cpp +++ b/src/weird-engine/Scene.cpp @@ -364,7 +364,7 @@ namespace WeirdEngine { if (m_renderMode == RenderMode::RayMarching3D || m_renderMode == RenderMode::RayMarchingBoth) { - onRender(m_ecs, renderTarget, m_services); + onRender(m_ecs, m_services, renderTarget); } } diff --git a/tools/molecule-editor/include/MoleculeEditor.h b/tools/molecule-editor/include/MoleculeEditor.h index 7ead6d8..4b91b73 100644 --- a/tools/molecule-editor/include/MoleculeEditor.h +++ b/tools/molecule-editor/include/MoleculeEditor.h @@ -29,6 +29,7 @@ class MoleculeEditor : public Scene2D MoleculeEditor() {} ECSManager* m_tempEcs = nullptr; + ServiceProvider* m_tempSvc = nullptr; private: enum class RightMouseMode @@ -128,11 +129,12 @@ class MoleculeEditor : public Scene2D void onStart(ECSManager& ecs, ServiceProvider& services) override { m_tempEcs = &ecs; - m_services.debug().setDebugFly(true); + m_tempSvc = &services; + m_tempSvc->debug().setDebugFly(true); g_cameraPositon.x = 0.0f; g_cameraPositon.y = 0.0f; - m_tempEcs->getComponent(m_services.render().getCameraEntity()).position = g_cameraPositon; + m_tempEcs->getComponent(m_tempSvc->render().getCameraEntity()).position = g_cameraPositon; // Request neutral simulation behavior for this editor scene. Entity globalSettingsEnt = m_tempEcs->createEntity(); @@ -148,30 +150,31 @@ class MoleculeEditor : public Scene2D { float boundsVars[8]{0.0f, 0.0f, 3000.0f}; Entity outside = - m_services.shapes().addShape(DefaultShapes::CIRCLE, boundsVars, 17, CombinationType::Addition); + m_tempSvc->shapes().addShape(DefaultShapes::CIRCLE, boundsVars, 17, CombinationType::Addition); float boundsVars2[8]{0.0f, 0.0f, 20.0f, 20.0f}; - Entity inside = m_services.shapes().addShape(DefaultShapes::BOX, boundsVars2, DisplaySettings::Black, + Entity inside = m_tempSvc->shapes().addShape(DefaultShapes::BOX, boundsVars2, DisplaySettings::Black, CombinationType::Subtraction); - m_services.serialization().blacklistEntity(outside); - m_services.serialization().blacklistEntity(inside); + m_tempSvc->serialization().blacklistEntity(outside); + m_tempSvc->serialization().blacklistEntity(inside); } } void onUpdate(ECSManager& ecs, ServiceProvider& services) override { m_tempEcs = &ecs; - g_cameraPositon = m_tempEcs->getComponent(m_services.render().getCameraEntity()).position; + m_tempSvc = &services; + g_cameraPositon = m_tempEcs->getComponent(m_tempSvc->render().getCameraEntity()).position; - if (m_services.input().getKeyDown(Input::Q) || - m_services.input().getGamepadButtonDown(Input::GamepadButton::North)) + if (m_tempSvc->input().getKeyDown(Input::Q) || + m_tempSvc->input().getGamepadButtonDown(Input::GamepadButton::North)) { - m_services.sceneControl().goToNextScene(); + m_tempSvc->sceneControl().goToNextScene(); return; } - if (m_services.input().getKey(Input::LeftCtrl) && m_services.input().getKeyDown(Input::S)) + if (m_tempSvc->input().getKey(Input::LeftCtrl) && m_tempSvc->input().getKeyDown(Input::S)) { WeirdEngine::Logger::log("Save scene name: "); @@ -187,11 +190,11 @@ class MoleculeEditor : public Scene2D { fileName += ".weird"; } - m_services.serialization().saveScene(m_services.resources().assetPath("Organisms/") + fileName); + m_tempSvc->serialization().saveScene(m_tempSvc->resources().assetPath("Organisms/") + fileName); } } - if (m_services.input().getKey(Input::LeftCtrl) && m_services.input().getKeyDown(Input::L)) + if (m_tempSvc->input().getKey(Input::LeftCtrl) && m_tempSvc->input().getKeyDown(Input::L)) { WeirdEngine::Logger::log("Load scene name: "); @@ -207,11 +210,11 @@ class MoleculeEditor : public Scene2D { fileName += ".weird"; } - loadMolecule(m_services.resources().assetPath("Organisms/") + fileName); + loadMolecule(m_tempSvc->resources().assetPath("Organisms/") + fileName); } } - if (m_services.input().getMouseButtonDown(Input::LeftClick) && !m_services.input().isUIClick()) + if (m_tempSvc->input().getMouseButtonDown(Input::LeftClick) && !m_tempSvc->input().isUIClick()) { spawnBallAtMouse(); } @@ -312,7 +315,7 @@ class MoleculeEditor : public Scene2D float px = START_X + i * MAT_SPACING; float p[8]{px, MAT_Y, BTN_SIZE - 4.0f}; Entity e; - UIShape& sh = m_services.shapes().addUIShape(DefaultShapes::CIRCLE, p, e); + UIShape& sh = m_tempSvc->shapes().addUIShape(DefaultShapes::CIRCLE, p, e); sh.material = static_cast(i); auto& tog = m_tempEcs->addComponent(e); @@ -321,7 +324,7 @@ class MoleculeEditor : public Scene2D tog.modifierAmount = 5.0f; m_materialToggles[i] = e; - m_services.serialization().blacklistEntity(e); + m_tempSvc->serialization().blacklistEntity(e); } m_tempEcs->getComponent(m_materialToggles[m_selectedMaterial]).active = true; @@ -371,14 +374,14 @@ class MoleculeEditor : public Scene2D { float y = (Display::height - TOOL_Y_START) - (i * TOOL_SPACING); float p[8]{TOOL_X, y, TOOL_BTN_HALF, TOOL_BTN_HALF}; - Entity e = m_services.shapes().addUIShape(DefaultShapes::BOX, p, static_cast(2)); + Entity e = m_tempSvc->shapes().addUIShape(DefaultShapes::BOX, p, static_cast(2)); auto& tog = m_tempEcs->addComponent(e); tog.clickPadding = TOOL_BTN_HALF + 8.0f; tog.parameterModifierMask.set(2); tog.parameterModifierMask.set(3); tog.modifierAmount = 3.0f; m_toolToggles[i] = e; - m_services.serialization().blacklistEntity(e); + m_tempSvc->serialization().blacklistEntity(e); Entity lbl = m_tempEcs->createEntity(); auto& lt = m_tempEcs->addComponent(lbl); @@ -388,27 +391,27 @@ class MoleculeEditor : public Scene2D tx.material = 1; tx.horizontalAlignment = TextRenderer::HorizontalAlignment::Left; tx.verticalAlignment = TextRenderer::VerticalAlignment::Center; - m_services.serialization().blacklistEntity(lbl); + m_tempSvc->serialization().blacklistEntity(lbl); } m_tempEcs->getComponent(m_toolToggles[0]).active = true; float starP[8]{Display::width - GRAV_Y, Display::height - GRAV_Y, GRAV_Y * 0.5f, 5.0f, 10.0f, 0.0f}; - m_gravityToggleEntity = m_services.shapes().addUIShape(DefaultShapes::STAR, starP, static_cast(2)); + m_gravityToggleEntity = m_tempSvc->shapes().addUIShape(DefaultShapes::STAR, starP, static_cast(2)); auto& gravTog = m_tempEcs->addComponent(m_gravityToggleEntity); gravTog.clickPadding = 18.0f; // gravTog.parameterModifierMask.set(2); gravTog.parameterModifierMask.set(5); gravTog.modifierAmount = 10.0f; - m_services.serialization().blacklistEntity(m_gravityToggleEntity); + m_tempSvc->serialization().blacklistEntity(m_gravityToggleEntity); float gridP[8]{Display::width - GRAV_Y, Display::height - GRID_Y, 12.0f, 12.0f}; - m_gridToggleEntity = m_services.shapes().addUIShape(DefaultShapes::BOX, gridP, static_cast(2)); + m_gridToggleEntity = m_tempSvc->shapes().addUIShape(DefaultShapes::BOX, gridP, static_cast(2)); auto& gridTog = m_tempEcs->addComponent(m_gridToggleEntity); gridTog.clickPadding = 18.0f; gridTog.parameterModifierMask.set(2); gridTog.parameterModifierMask.set(3); gridTog.modifierAmount = 3.0f; - m_services.serialization().blacklistEntity(m_gridToggleEntity); + m_tempSvc->serialization().blacklistEntity(m_gridToggleEntity); } void syncToolbar() @@ -459,9 +462,9 @@ class MoleculeEditor : public Scene2D void spawnBallAtMouse() { - auto& cam = m_tempEcs->getComponent(m_services.render().getCameraEntity()); + auto& cam = m_tempEcs->getComponent(m_tempSvc->render().getCameraEntity()); vec2 world = ECS::Camera::screenPositionToWorldPosition2D( - cam, vec2(m_services.input().getMouseX(), m_services.input().getMouseY())); + cam, vec2(m_tempSvc->input().getMouseX(), m_tempSvc->input().getMouseY())); if (m_gridMode) world = snapToGrid(world); @@ -482,9 +485,9 @@ class MoleculeEditor : public Scene2D vec2 getMouseWorldPosition() { - auto& cam = m_tempEcs->getComponent(m_services.render().getCameraEntity()); + auto& cam = m_tempEcs->getComponent(m_tempSvc->render().getCameraEntity()); return ECS::Camera::screenPositionToWorldPosition2D( - cam, vec2(m_services.input().getMouseX(), m_services.input().getMouseY())); + cam, vec2(m_tempSvc->input().getMouseX(), m_tempSvc->input().getMouseY())); } vec2 snapToGrid(vec2 pos, Entity exclude = static_cast(-1)) @@ -549,7 +552,7 @@ class MoleculeEditor : public Scene2D void handleRightMouseDragInput() { - bool rightDown = m_services.input().getMouseButton(Input::RightClick); + bool rightDown = m_tempSvc->input().getMouseButton(Input::RightClick); if (rightDown && !m_rightWasDown) { @@ -631,7 +634,7 @@ class MoleculeEditor : public Scene2D if (m_draggedBall == static_cast(-1) || m_draggedSimulationId < 0) return; - if (m_services.input().getKeyDown(Input::F)) + if (m_tempSvc->input().getKeyDown(Input::F)) { m_keepFixedAfterDrag = true; } @@ -695,9 +698,9 @@ class MoleculeEditor : public Scene2D Entity pickBallAtMouse() { - auto& cam = m_tempEcs->getComponent(m_services.render().getCameraEntity()); + auto& cam = m_tempEcs->getComponent(m_tempSvc->render().getCameraEntity()); vec2 world = ECS::Camera::screenPositionToWorldPosition2D( - cam, vec2(m_services.input().getMouseX(), m_services.input().getMouseY())); + cam, vec2(m_tempSvc->input().getMouseX(), m_tempSvc->input().getMouseY())); float best = BALL_HIT_RADIUS; Entity bestEntity = static_cast(-1); @@ -773,13 +776,13 @@ class MoleculeEditor : public Scene2D float lineVars[8]{}; computeScreenLineParams(pa, pb, lineVars); - Entity line = m_services.shapes().addUIShape(DefaultShapes::LINE, lineVars, lineColor); + Entity line = m_tempSvc->shapes().addUIShape(DefaultShapes::LINE, lineVars, lineColor); auto& btn = m_tempEcs->addComponent(line); btn.clickPadding = 8.0f; btn.modifierAmount = 0.0f; - m_services.serialization().blacklistEntity(line); + m_tempSvc->serialization().blacklistEntity(line); m_links.push_back({a, b, idA, idB, restDistance, line, type, constraintEnt}); } @@ -810,7 +813,7 @@ class MoleculeEditor : public Scene2D void handleConstraintLineClicks() { // Detect click-down via ShapeButton state. - // ButtonSystem already calls m_services.input().flagUIClick() when a line is clicked, + // ButtonSystem already calls m_tempSvc->input().flagUIClick() when a line is clicked, // so ball spawning is suppressed automatically. for (auto& link : m_links) { @@ -820,7 +823,7 @@ class MoleculeEditor : public Scene2D if (btn.state == ButtonState::Down) { m_draggedLink = &link; - m_linkDragStartX = m_services.input().getMouseX(); + m_linkDragStartX = m_tempSvc->input().getMouseX(); m_linkDragStartDist = link.restDistance; m_dragLinkSimIdA.store(link.simulationIdA, std::memory_order_relaxed); m_dragLinkSimIdB.store(link.simulationIdB, std::memory_order_relaxed); @@ -828,7 +831,7 @@ class MoleculeEditor : public Scene2D } } - if (!m_services.input().getMouseButton(Input::LeftClick)) + if (!m_tempSvc->input().getMouseButton(Input::LeftClick)) { m_draggedLink = nullptr; return; @@ -837,7 +840,7 @@ class MoleculeEditor : public Scene2D if (m_draggedLink == nullptr) return; - float dx = (m_services.input().getMouseX() - m_linkDragStartX) * 0.3f; + float dx = (m_tempSvc->input().getMouseX() - m_linkDragStartX) * 0.3f; float newDist = std::round((m_linkDragStartDist + dx) * 10.0f) / 10.0f; newDist = (std::clamp)(newDist, 1.0f, 10.0f); m_draggedLink->restDistance = newDist; @@ -848,7 +851,7 @@ class MoleculeEditor : public Scene2D void updateConstraintLines() { - auto& cam = m_tempEcs->getComponent(m_services.render().getCameraEntity()); + auto& cam = m_tempEcs->getComponent(m_tempSvc->render().getCameraEntity()); for (auto& link : m_links) { @@ -874,7 +877,7 @@ class MoleculeEditor : public Scene2D void computeScreenLineParams(const vec2& aWorld, const vec2& bWorld, float outParams[8]) { - auto& cam = m_tempEcs->getComponent(m_services.render().getCameraEntity()); + auto& cam = m_tempEcs->getComponent(m_tempSvc->render().getCameraEntity()); vec2 aScreen = ECS::Camera::worldPosition2DToScreenPosition(cam, aWorld); vec2 bScreen = ECS::Camera::worldPosition2DToScreenPosition(cam, bWorld); @@ -912,7 +915,7 @@ class MoleculeEditor : public Scene2D tx.horizontalAlignment = TextRenderer::HorizontalAlignment::Center; tx.verticalAlignment = TextRenderer::VerticalAlignment::Center; m_tagLabelEntity = lbl; - m_services.serialization().blacklistEntity(lbl); + m_tempSvc->serialization().blacklistEntity(lbl); } // "edit tag" button (a small box) @@ -920,11 +923,11 @@ class MoleculeEditor : public Scene2D static constexpr float BW = 40.0f; static constexpr float BH = 14.0f; float p[8]{Display::width * 0.5f, 90.0f, BW, BH}; - m_tagEditButton = m_services.shapes().addUIShape(DefaultShapes::BOX, p, static_cast(2)); + m_tagEditButton = m_tempSvc->shapes().addUIShape(DefaultShapes::BOX, p, static_cast(2)); auto& btn = m_tempEcs->addComponent(m_tagEditButton); btn.clickPadding = 6.0f; btn.modifierAmount = 0.0f; - m_services.serialization().blacklistEntity(m_tagEditButton); + m_tempSvc->serialization().blacklistEntity(m_tagEditButton); } } @@ -956,17 +959,17 @@ class MoleculeEditor : public Scene2D { float p[8]{}; m_tagCircleOuter = - m_services.shapes().addUIShape(DefaultShapes::CIRCLE, p, static_cast(DisplaySettings::Yellow), + m_tempSvc->shapes().addUIShape(DefaultShapes::CIRCLE, p, static_cast(DisplaySettings::Yellow), CombinationType::Addition, TAG_RING_GROUP); - m_services.serialization().blacklistEntity(m_tagCircleOuter); + m_tempSvc->serialization().blacklistEntity(m_tagCircleOuter); m_tagCircleInner = - m_services.shapes().addUIShape(DefaultShapes::CIRCLE, p, static_cast(DisplaySettings::Yellow), + m_tempSvc->shapes().addUIShape(DefaultShapes::CIRCLE, p, static_cast(DisplaySettings::Yellow), CombinationType::Subtraction, TAG_RING_GROUP); - m_services.serialization().blacklistEntity(m_tagCircleInner); + m_tempSvc->serialization().blacklistEntity(m_tagCircleInner); } - auto& cam = m_tempEcs->getComponent(m_services.render().getCameraEntity()); + auto& cam = m_tempEcs->getComponent(m_tempSvc->render().getCameraEntity()); const auto& ht = m_tempEcs->getComponent(m_tagSelectedEntity); vec2 world(ht.position.x, ht.position.y); vec2 screen = ECS::Camera::worldPosition2DToScreenPosition(cam, world); @@ -984,7 +987,7 @@ class MoleculeEditor : public Scene2D // Update the tag label text if (m_tagLabelEntity != static_cast(-1)) { - std::string currentTag = m_services.tags().getEntityTag(m_tagSelectedEntity); + std::string currentTag = m_tempSvc->tags().getEntityTag(m_tagSelectedEntity); auto& tx = m_tempEcs->getComponent(m_tagLabelEntity); std::string newText = currentTag.empty() ? "tag: (none)" : ("tag: " + currentTag); if (tx.text != newText) @@ -1007,11 +1010,11 @@ class MoleculeEditor : public Scene2D std::getline(std::cin, newTag); if (newTag.empty()) { - m_services.tags().removeTag(m_tagSelectedEntity); + m_tempSvc->tags().removeTag(m_tagSelectedEntity); } else { - m_services.tags().tag(m_tagSelectedEntity, newTag); + m_tempSvc->tags().tag(m_tagSelectedEntity, newTag); } } } @@ -1057,12 +1060,12 @@ class MoleculeEditor : public Scene2D size_t prevDistCount = m_tempEcs->getComponentArray()->getSize(); // Load the file — creates new entities / rigid bodies / constraints - TagMap loadedTags = m_services.serialization().loadWeirdFile(path); + TagMap loadedTags = m_tempSvc->serialization().loadWeirdFile(path); // Apply loaded tags to the scene for (const auto& [name, entity] : loadedTags) { - m_services.tags().tag(entity, name); + m_tempSvc->tags().tag(entity, name); } // Collect new balls: find entities with both Dot and RigidBody2D @@ -1114,12 +1117,12 @@ class MoleculeEditor : public Scene2D float lineVars[8]{}; computeScreenLineParams(pa, pb, lineVars); - Entity line = m_services.shapes().addUIShape(DefaultShapes::LINE, lineVars, lineColor); + Entity line = m_tempSvc->shapes().addUIShape(DefaultShapes::LINE, lineVars, lineColor); auto& btn = m_tempEcs->addComponent(line); btn.clickPadding = 8.0f; btn.modifierAmount = 0.0f; - m_services.serialization().blacklistEntity(line); + m_tempSvc->serialization().blacklistEntity(line); int idA = m_tempEcs->getComponent(a).simulationId; int idB = m_tempEcs->getComponent(b).simulationId; @@ -1148,12 +1151,12 @@ class MoleculeEditor : public Scene2D float lineVars[8]{}; computeScreenLineParams(pa, pb, lineVars); - Entity line = m_services.shapes().addUIShape(DefaultShapes::LINE, lineVars, lineColor); + Entity line = m_tempSvc->shapes().addUIShape(DefaultShapes::LINE, lineVars, lineColor); auto& btn = m_tempEcs->addComponent(line); btn.clickPadding = 8.0f; btn.modifierAmount = 0.0f; - m_services.serialization().blacklistEntity(line); + m_tempSvc->serialization().blacklistEntity(line); int idA = m_tempEcs->getComponent(a).simulationId; int idB = m_tempEcs->getComponent(b).simulationId; @@ -1170,5 +1173,6 @@ class MoleculeEditor : public Scene2D WeirdEngine::EntityShapeCollisionEvent& event) override { m_tempEcs = &ecs; + m_tempSvc = &services; } }; diff --git a/tools/scene-editor/include/SceneEditor.h b/tools/scene-editor/include/SceneEditor.h index 172fce3..63ea7d1 100644 --- a/tools/scene-editor/include/SceneEditor.h +++ b/tools/scene-editor/include/SceneEditor.h @@ -23,6 +23,7 @@ class SceneEditor : public Scene2D } ECSManager* m_tempEcs = nullptr; + ServiceProvider* m_tempSvc = nullptr; private: // ===================================================================== @@ -89,9 +90,10 @@ class SceneEditor : public Scene2D void onStart(ECSManager& ecs, ServiceProvider& services) override { m_tempEcs = &ecs; - m_services.debug().setDebugInput(true); - m_services.debug().setDebugFly(true); - m_tempEcs->getComponent(m_services.render().getCameraEntity()).position = g_cameraPositon; + m_tempSvc = &services; + m_tempSvc->debug().setDebugInput(true); + m_tempSvc->debug().setDebugFly(true); + m_tempEcs->getComponent(m_tempSvc->render().getCameraEntity()).position = g_cameraPositon; buildShapeButtons(); buildCombToggles(); @@ -102,29 +104,30 @@ class SceneEditor : public Scene2D void onUpdate(ECSManager& ecs, ServiceProvider& services) override { m_tempEcs = &ecs; - g_cameraPositon = m_tempEcs->getComponent(m_services.render().getCameraEntity()).position; + m_tempSvc = &services; + g_cameraPositon = m_tempEcs->getComponent(m_tempSvc->render().getCameraEntity()).position; - if (m_services.input().getKeyDown(Input::Q) || - m_services.input().getGamepadButtonDown(Input::GamepadButton::North)) - m_services.sceneControl().goToNextScene(); - if (m_services.input().getKey(Input::LeftCtrl) && m_services.input().getKeyDown(Input::S)) - m_services.serialization().saveScene(m_services.resources().assetPath("example.weird")); + if (m_tempSvc->input().getKeyDown(Input::Q) || + m_tempSvc->input().getGamepadButtonDown(Input::GamepadButton::North)) + m_tempSvc->sceneControl().goToNextScene(); + if (m_tempSvc->input().getKey(Input::LeftCtrl) && m_tempSvc->input().getKeyDown(Input::S)) + m_tempSvc->serialization().saveScene(m_tempSvc->resources().assetPath("example.weird")); syncMaterialToggles(); syncCombToggles(); - if (m_services.input().getMouseButtonDown(Input::LeftClick)) + if (m_tempSvc->input().getMouseButtonDown(Input::LeftClick)) onLeftClick(); - if (m_services.input().getMouseButton(Input::LeftClick)) + if (m_tempSvc->input().getMouseButton(Input::LeftClick)) { - auto& cam = m_tempEcs->getComponent(m_services.render().getCameraEntity()); + auto& cam = m_tempEcs->getComponent(m_tempSvc->render().getCameraEntity()); vec2 wp = ECS::Camera::screenPositionToWorldPosition2D( - cam, vec2(m_services.input().getMouseX(), m_services.input().getMouseY())); + cam, vec2(m_tempSvc->input().getMouseX(), m_tempSvc->input().getMouseY())); spawnPhysicsEntity(wp); } - if (m_services.input().getMouseButtonDown(Input::RightClick)) + if (m_tempSvc->input().getMouseButtonDown(Input::RightClick)) onRightClick(); - if (m_services.input().getKeyDown(Input::X) && m_hasSelection) + if (m_tempSvc->input().getKeyDown(Input::X) && m_hasSelection) deleteSelected(); refreshPanel(); @@ -141,7 +144,7 @@ class SceneEditor : public Scene2D { auto& transform = transformArray->getDataAtIdx(i); Entity entity = transformArray->getEntityAtIdx(i); - if (entity == m_services.render().getCameraEntity()) + if (entity == m_tempSvc->render().getCameraEntity()) continue; if (transform.position.y < -10.0f) @@ -166,13 +169,13 @@ class SceneEditor : public Scene2D float p[8]{}; previewParams(types[i], cx, cy, p); - Entity e = m_services.shapes().addUIShape(types[i], p, 2); + Entity e = m_tempSvc->shapes().addUIShape(types[i], p, 2); auto& b = m_tempEcs->addComponent(e); b.modifierAmount = 1.0f; b.clickPadding = 8.0f; m_shapeButtons.push_back({e, types[i]}); - m_services.serialization().blacklistEntity(e); + m_tempSvc->serialization().blacklistEntity(e); } } @@ -246,11 +249,11 @@ class SceneEditor : public Scene2D int g = COMB_GRP_BASE + i; float p1[8]{cx - off * 0.5f, cy, r}; - Entity e1 = m_services.shapes().addUIShape(DefaultShapes::CIRCLE, p1, static_cast(1), + Entity e1 = m_tempSvc->shapes().addUIShape(DefaultShapes::CIRCLE, p1, static_cast(1), CombinationType::Addition, g); float p2[8]{cx + off * 0.5f, cy, r}; - Entity e2 = m_services.shapes().addUIShape(DefaultShapes::CIRCLE, p2, static_cast(1), ct[i], g); + Entity e2 = m_tempSvc->shapes().addUIShape(DefaultShapes::CIRCLE, p2, static_cast(1), ct[i], g); if (ct[i] == CombinationType::SmoothAddition || ct[i] == CombinationType::SmoothSubtraction) m_tempEcs->getComponent(e2).smoothFactor = 5.0f; @@ -268,9 +271,9 @@ class SceneEditor : public Scene2D tx.horizontalAlignment = TextRenderer::HorizontalAlignment::Center; m_combButtons.push_back({e1, ct[i]}); - m_services.serialization().blacklistEntity(e1); - m_services.serialization().blacklistEntity(e2); - m_services.serialization().blacklistEntity(lbl); + m_tempSvc->serialization().blacklistEntity(e1); + m_tempSvc->serialization().blacklistEntity(e2); + m_tempSvc->serialization().blacklistEntity(lbl); } m_tempEcs->getComponent(m_combButtons[0].toggleEntity).active = true; } @@ -285,7 +288,7 @@ class SceneEditor : public Scene2D float px = START_X + i * MAT_SPACING; float p[8]{px, MAT_Y, BTN_SIZE - 4.0f}; Entity e; - UIShape& sh = m_services.shapes().addUIShape(DefaultShapes::CIRCLE, p, e); + UIShape& sh = m_tempSvc->shapes().addUIShape(DefaultShapes::CIRCLE, p, e); sh.material = static_cast(i); auto& tog = m_tempEcs->addComponent(e); @@ -294,7 +297,7 @@ class SceneEditor : public Scene2D tog.modifierAmount = 5.0f; m_materialToggles[i] = e; - m_services.serialization().blacklistEntity(e); + m_tempSvc->serialization().blacklistEntity(e); } m_tempEcs->getComponent(m_materialToggles[m_selectedMaterial]).active = true; } @@ -312,14 +315,14 @@ class SceneEditor : public Scene2D auto& hdr = m_tempEcs->addComponent(m_selInfoText); hdr.material = 1; hdr.horizontalAlignment = TextRenderer::HorizontalAlignment::Right; - m_services.serialization().blacklistEntity(m_selInfoText); + m_tempSvc->serialization().blacklistEntity(m_selInfoText); for (int i = 0; i < 8; i++) { float py = PANEL_TOP_Y - i * PARAM_GAP; float bp[8]{HIDDEN, py, P_BTN_W, P_BTN_H}; - Entity be = m_services.shapes().addUIShape(DefaultShapes::BOX, bp, static_cast(3)); + Entity be = m_tempSvc->shapes().addUIShape(DefaultShapes::BOX, bp, static_cast(3)); auto& btn = m_tempEcs->addComponent(be); btn.modifierAmount = 1.0f; btn.clickPadding = 3.0f; @@ -334,8 +337,8 @@ class SceneEditor : public Scene2D tx.verticalAlignment = TextRenderer::VerticalAlignment::Center; m_paramBtns[i] = {be, te}; - m_services.serialization().blacklistEntity(be); - m_services.serialization().blacklistEntity(te); + m_tempSvc->serialization().blacklistEntity(be); + m_tempSvc->serialization().blacklistEntity(te); } } @@ -368,9 +371,9 @@ class SceneEditor : public Scene2D void onRightClick() { - auto& cam = m_tempEcs->getComponent(m_services.render().getCameraEntity()); + auto& cam = m_tempEcs->getComponent(m_tempSvc->render().getCameraEntity()); vec2 wp = ECS::Camera::screenPositionToWorldPosition2D( - cam, vec2(m_services.input().getMouseX(), m_services.input().getMouseY())); + cam, vec2(m_tempSvc->input().getMouseX(), m_tempSvc->input().getMouseY())); selectNearest(wp); } @@ -396,7 +399,7 @@ class SceneEditor : public Scene2D std::copy(std::begin(s.parameters), std::end(s.parameters), p); p[9] = pos.x; p[10] = pos.y; - float d = m_services.shapes().getSDFs()[s.distanceFieldId]->getValue(p); + float d = m_tempSvc->shapes().getSDFs()[s.distanceFieldId]->getValue(p); if (d < best) { best = d; @@ -649,7 +652,7 @@ class SceneEditor : public Scene2D float p[8]{}; fillRandomParams(type, p); Entity e = - m_services.shapes().addShape(type, p, static_cast(m_selectedMaterial), m_selectedCombination); + m_tempSvc->shapes().addShape(type, p, static_cast(m_selectedMaterial), m_selectedCombination); if (m_selectedCombination == CombinationType::SmoothAddition || m_selectedCombination == CombinationType::SmoothSubtraction) m_tempEcs->getComponent(e).smoothFactor = 1.5f; @@ -665,7 +668,7 @@ class SceneEditor : public Scene2D auto& sdf = m_tempEcs->addComponent(e); sdf.materialId = static_cast(m_selectedMaterial); m_tempEcs->addComponent(e); - m_services.serialization().blacklistEntity(e); + m_tempSvc->serialization().blacklistEntity(e); } // ===================================================================== @@ -760,7 +763,7 @@ class SceneEditor : public Scene2D vec2 camCentre() { - auto& t = m_tempEcs->getComponent(m_services.render().getCameraEntity()); + auto& t = m_tempEcs->getComponent(m_tempSvc->render().getCameraEntity()); return vec2(t.position.x, t.position.y); } From 2eea61a706d2d8d7344a7d2d30c29f4dda3b6302 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= <49535803+damacaa@users.noreply.github.com> Date: Tue, 11 Aug 2026 13:05:00 +0200 Subject: [PATCH 14/21] physics: take BodyUserData ownership via unique_ptr in setUserData setUserData now consumes std::unique_ptr, forcing callers to hand off ownership and query the data back through getUserDataAs instead of caching a raw pointer. Out-of-bounds ids now assert instead of silently returning. Documented the type discriminator must be set in the derived constructor. --- .../weird-engine/services/ServiceProvider.h | 14 ++++++++------ include/weird-physics/BodyUserData.h | 12 +++++++++--- include/weird-physics/Simulation2D.h | 18 ++++++++++++------ src/weird-physics/Simulation2D.cpp | 9 +++++---- 4 files changed, 34 insertions(+), 19 deletions(-) diff --git a/include/weird-engine/services/ServiceProvider.h b/include/weird-engine/services/ServiceProvider.h index 59b498a..c5cbac3 100644 --- a/include/weird-engine/services/ServiceProvider.h +++ b/include/weird-engine/services/ServiceProvider.h @@ -121,12 +121,14 @@ namespace WeirdEngine } // Per-body user data. Set the data right after adding the RigidBody2D - // component (read rb.simulationId from it). The pointer must be - // heap-allocated: the simulation owns it and deletes it when the body - // is removed or when the simulation is destroyed. - void setUserData(SimulationID id, BodyUserData* data) - { - simulation.setUserData(id, data); + // component (read rb.simulationId from it). Ownership is transferred to + // the simulation (e.g. std::make_unique()): it deletes + // the data when the body is removed or when the simulation is + // destroyed. Do not retain the pointer after the call; query it back + // through getUserData()/getUserDataAs(). + void setUserData(SimulationID id, std::unique_ptr data) + { + simulation.setUserData(id, std::move(data)); } BodyUserData* getUserData(SimulationID id) diff --git a/include/weird-physics/BodyUserData.h b/include/weird-physics/BodyUserData.h index 2ca1cbe..03f684f 100644 --- a/include/weird-physics/BodyUserData.h +++ b/include/weird-physics/BodyUserData.h @@ -8,9 +8,15 @@ namespace WeirdEngine // discriminator, and use Simulation2D::getUserDataAs() (which checks // `T::TYPE` against `type` before casting) or check `type` manually. // - // The pointer must be heap-allocated with `new`. The simulation takes - // ownership: it deletes the data when the body is removed and deletes all - // remaining data when the simulation is destroyed (scene teardown). + // Set `type` in the derived class's constructor (e.g. + // `MyData() { type = TYPE; }`) so the discriminator can never drift out + // of sync with T::TYPE. + // + // Ownership is transferred to the simulation with std::unique_ptr (e.g. + // std::make_unique()): it deletes the data when the body is + // removed and deletes all remaining data when the simulation is destroyed + // (scene teardown). Do not retain the pointer after setUserData(); query + // it back via getUserData()/getUserDataAs() instead. struct BodyUserData { int type = 0; diff --git a/include/weird-physics/Simulation2D.h b/include/weird-physics/Simulation2D.h index dee2b1c..a721127 100644 --- a/include/weird-physics/Simulation2D.h +++ b/include/weird-physics/Simulation2D.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -121,6 +122,10 @@ namespace WeirdEngine size_t getSize(); // Interaction + // One-shot kick applied to a body. With massIndependent = false the + // impulse is a force scaled by the simulation frequency; with + // massIndependent = true it is applied as a direct velocity change + // (the parameter is the desired delta-v in m/s, regardless of mass). void addImpulseForce(SimulationID id, const vec2& impulse, bool massIndependent = false); void setContinuousForce(SimulationID id, const vec2& force, bool massIndependent = false); void swapContinuousForces(); @@ -213,12 +218,13 @@ namespace WeirdEngine // Per-body user data, keyed by SimulationID (entity-free: the ECS maps // simulation IDs back to entities via the RigidBody2D component array). - // Must be heap-allocated: the simulation owns the pointer and deletes - // it when the body is removed (removeObject) and when the simulation - // is destroyed. setUserData() is main-thread only; getUserData()/ - // getUserDataAs()/forEachUserData() are safe from the physics - // callbacks without locks. - void setUserData(SimulationID id, BodyUserData* data); + // Takes ownership via unique_ptr: the simulation deletes the data when + // the body is removed (removeObject) and when the simulation is + // destroyed. Do not retain the pointer after the call; read or modify + // it through getUserData()/getUserDataAs() instead. setUserData() is + // main-thread only; getUserData()/getUserDataAs()/forEachUserData() + // are safe from the physics callbacks without locks. + void setUserData(SimulationID id, std::unique_ptr data); BodyUserData* getUserData(SimulationID id); // Type-checked cast: returns nullptr unless the attached data exists diff --git a/src/weird-physics/Simulation2D.cpp b/src/weird-physics/Simulation2D.cpp index 59fbdcb..1440c74 100644 --- a/src/weird-physics/Simulation2D.cpp +++ b/src/weird-physics/Simulation2D.cpp @@ -328,7 +328,7 @@ namespace WeirdEngine // std::lock_guard lock(g_simulationTimeMutex); return m_simulationTime; } - void Simulation2D::setUserData(SimulationID id, BodyUserData* data) + void Simulation2D::setUserData(SimulationID id, std::unique_ptr data) { WEIRD_ASSERT(!isPhysicsExecutionContext(), "setUserData() may not be called from physics execution context"); @@ -336,10 +336,11 @@ namespace WeirdEngine // Bounds-check against m_allocated, not m_size: bodies can carry user // data before they are activated (ActivatePending) later in the frame. - if (id >= m_allocated) - return; + WEIRD_ASSERT(id < m_allocated, "setUserData() called with invalid simulation id"); - m_userData[id] = data; + // Free any prior data attached to this body before overwriting. + delete m_userData[id]; + m_userData[id] = data.release(); } BodyUserData* Simulation2D::getUserData(SimulationID id) From 2cca414ae05cdc869579fbc76626fbc8be7c4590 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= <49535803+damacaa@users.noreply.github.com> Date: Tue, 11 Aug 2026 13:06:24 +0200 Subject: [PATCH 15/21] scene: add system dispatcher for registering callbacks Scenes can register plain functions (systems) for create, start, update, destroy, imgui render, entity collision and entity shape collision stages. Registered systems run sequentially after the virtual callback of the same stage, keeping the old override API working. --- include/weird-engine/Scene.h | 49 ++++++++++++++++++++++++++++++++++++ src/weird-engine/Scene.cpp | 24 ++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/include/weird-engine/Scene.h b/include/weird-engine/Scene.h index 6ca8e19..d0c0214 100644 --- a/include/weird-engine/Scene.h +++ b/include/weird-engine/Scene.h @@ -16,6 +16,7 @@ #include "weird-physics/PhysicsSettings.h" #include "weird-physics/Simulation2D.h" +#include #include #include #include @@ -45,6 +46,11 @@ namespace WeirdEngine class SceneSerializer; class SceneManager; + // ---- System Signatures ---- + using CoreSystem = std::function; + using EntityCollisionSystem = std::function; + using EntityShapeCollisionSystem = std::function; + namespace WeirdRenderer { class AudioEngine; @@ -86,6 +92,36 @@ namespace WeirdEngine static ShapeId registerDefaultSDF(std::shared_ptr sdf); static const std::vector>& getGlobalSDFs(); + // ---- System Dispatcher (Register systems to be called automatically) + void addCreateSystem(CoreSystem system) + { + m_createSystems.push_back(std::move(system)); + } + void addStartSystem(CoreSystem system) + { + m_startSystems.push_back(std::move(system)); + } + void addUpdateSystem(CoreSystem system) + { + m_updateSystems.push_back(std::move(system)); + } + void addDestroySystem(CoreSystem system) + { + m_destroySystems.push_back(std::move(system)); + } + void addImGuiRenderSystem(CoreSystem system) + { + m_imguiSystems.push_back(std::move(system)); + } + void addEntityCollisionSystem(EntityCollisionSystem system) + { + m_entityCollisionSystems.push_back(std::move(system)); + } + void addEntityShapeCollisionSystem(EntityShapeCollisionSystem system) + { + m_entityShapeCollisionSystems.push_back(std::move(system)); + } + protected: // Internal constructors: sets the render mode for Scene2D/Scene3D/SceneBoth. Scene(); @@ -128,6 +164,10 @@ namespace WeirdEngine void destroy() { onDestroy(m_ecs, m_services); + for (auto& sys : m_destroySystems) + { + sys(m_ecs, m_services); + } } // ---- Rendering pipeline (engine-driven) @@ -244,6 +284,15 @@ namespace WeirdEngine TagMap m_tagToEntity; std::unordered_map m_entityToTag; + // ---- Registered Systems + std::vector m_createSystems; + std::vector m_startSystems; + std::vector m_updateSystems; + std::vector m_destroySystems; + std::vector m_imguiSystems; + std::vector m_entityCollisionSystems; + std::vector m_entityShapeCollisionSystems; + float m_lastDelta = 0.0f; }; class Scene2D : public Scene diff --git a/src/weird-engine/Scene.cpp b/src/weird-engine/Scene.cpp index fa7c5c7..c7f4bbe 100644 --- a/src/weird-engine/Scene.cpp +++ b/src/weird-engine/Scene.cpp @@ -67,6 +67,10 @@ namespace WeirdEngine void Scene::start() { onCreate(m_ecs, m_services); + for (auto& sys : m_createSystems) + { + sys(m_ecs, m_services); + } // Custom component managers std::shared_ptr rbManager = std::make_shared(m_simulation2D); @@ -137,6 +141,10 @@ namespace WeirdEngine } onStart(m_ecs, m_services); + for (auto& sys : m_startSystems) + { + sys(m_ecs, m_services); + } switch (m_renderMode) { @@ -217,12 +225,20 @@ namespace WeirdEngine EntityCollisionEvent entityEvent{ev, getEntityForSimulationId(ev.bodyA, rigidBodies), getEntityForSimulationId(ev.bodyB, rigidBodies)}; onEntityCollision(m_ecs, m_services, entityEvent); + for (auto& sys : m_entityCollisionSystems) + { + sys(m_ecs, m_services, entityEvent); + } } for (auto& ev : shapeCollisions) { EntityShapeCollisionEvent entityEvent{ev, getEntityForSimulationId(ev.body, rigidBodies)}; onEntityShapeCollision(m_ecs, m_services, entityEvent); + for (auto& sys : m_entityShapeCollisionSystems) + { + sys(m_ecs, m_services, entityEvent); + } const float m_soundFalloff = 0.1f; bool spatialAudio = false; @@ -254,6 +270,10 @@ namespace WeirdEngine { PROFILE_SCOPE("OnUpdate"); onUpdate(m_ecs, m_services); + for (auto& sys : m_updateSystems) + { + sys(m_ecs, m_services); + } } { @@ -695,6 +715,10 @@ namespace WeirdEngine ImGui::Separator(); onImGuiRender(m_ecs, m_services); + for (auto& sys : m_imguiSystems) + { + sys(m_ecs, m_services); + } ImGui::PopID(); } From dfad8c4cd3ceb00d12522d077fe080b05f553567 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= <49535803+damacaa@users.noreply.github.com> Date: Tue, 11 Aug 2026 13:06:28 +0200 Subject: [PATCH 16/21] examples: migrate ServiceShowcaseScene to the system dispatcher Replace thin virtual wrappers with add*System registrations and move scene state into the ECS. Physics-thread callbacks and onRender stay inlined: the dispatcher is not involved there. Character user data is handed to the simulation via make_unique and queried back through the physics service; restitution now drives the bounce response. --- .../include/ServiceShowcaseScene.h | 267 +++++++++--------- 1 file changed, 130 insertions(+), 137 deletions(-) diff --git a/examples/sample-scenes/include/ServiceShowcaseScene.h b/examples/sample-scenes/include/ServiceShowcaseScene.h index e4fe7da..39a42c7 100644 --- a/examples/sample-scenes/include/ServiceShowcaseScene.h +++ b/examples/sample-scenes/include/ServiceShowcaseScene.h @@ -22,16 +22,18 @@ using namespace WeirdEngine; // // void system(ECSManager& ecs, ServiceProvider& services, ...); // -// (onRender and onImGuiRender are inlined in the scene instead, see below.) +// (onRender and the physics-thread callbacks are inlined in the scene +// instead, see below: the dispatcher is deliberately not involved there.) // // Systems never touch Scene internals: everything they need is either on the // ECSManager& or on the ServiceProvider& passed to the callback. Even the // scene's own state lives in the ECS (see State below): a single "state" // entity owns it, and systems reach it through the component array. // -// Callbacks covered: onCreate, onStart, onUpdate (4 systems), onRender, -// onImGuiRender, onPhysicsStep, onPhysicsRigidBodyCollision, onPhysicsShapeCollision, -// onEntityCollision, onEntityShapeCollision, onDestroy. +// Registered as systems: onCreate, onStart, onUpdate (4 systems), +// onImGuiRender, onEntityCollision, onEntityShapeCollision, onDestroy. +// Inlined overrides: onRender and the physics-thread callbacks +// (onPhysicsStep, onPhysicsRigidBodyCollision, onPhysicsShapeCollision). // // Controls: // Left click spawn a ball at the cursor @@ -51,7 +53,16 @@ namespace ServiceShowcase struct CharacterData : BodyUserData { static constexpr int TYPE = 1; + + // The inherited `type` member must match TYPE or every + // getUserDataAs() / forEachUserData() check will fail. + CharacterData() + { + type = TYPE; + } + float restitution = 1.2f; + float jumpStrength = 5.0f; }; // Scene state as an ECS component: attached to a single "state" entity @@ -71,10 +82,6 @@ namespace ServiceShowcase float damping = 0.001f; float initialTime = 0.0f; - // Heap-allocated (see onCreateSystem): the simulation owns per-body - // user data and deletes it when the body is removed or the scene ends. - CharacterData* characterData = nullptr; - // Each counter is only touched from the main thread (collision // callbacks); plain ints are fine. Atomicity would require a custom // component manager since components must be copyable for the ECS @@ -120,7 +127,6 @@ namespace ServiceShowcase State& state = getState(ecs, services); state.initialTime = services.time().time(); - state.characterData = new CharacterData(); std::cout << "[ServiceShowcase] onCreate at simulation time " << state.initialTime << "s" << std::endl; } @@ -197,19 +203,30 @@ namespace ServiceShowcase // Character ball: carries per-body user data so the physics callbacks // can identify and tune it without touching the ECS (the physics - // thread must not access the ECS). The data pointer is read fresh from - // the RigidBody2D component at spawn time. + // thread must not access the ECS). Ownership of the data is handed off + // to the simulation; the callbacks reach it through the simulation id + // stored in the RigidBody2D component. { Entity character = ecs.createEntity(); auto& t = ecs.addComponent(character); t.position = vec3(15.0f, 15.0f, 0.0f); auto& dot = ecs.addComponent(character); - dot.materialId = DisplaySettings::Orange; + dot.materialId = DisplaySettings::Blue; auto& rb = ecs.addComponent(character); services.tags().tag(character, "character"); - services.physics().setUserData(rb.simulationId, state.characterData); + + // Configure the data before handing ownership to the simulation; + // std::move() empties the local unique_ptr, so the only way to + // reach the data afterwards is getUserDataAs(). + auto characterData = std::make_unique(); + characterData->jumpStrength = 10.0f; + services.physics().setUserData(rb.simulationId, std::move(characterData)); + + // Reading and modifying the data after the handoff: no cached + // pointer is kept, everything goes through the physics service. + services.physics().getUserDataAs(rb.simulationId)->restitution = 2.0f; } // Initial ball pile @@ -380,67 +397,6 @@ namespace ServiceShowcase ecs.setComponentDirty(collisionsText); } - // ------------------------------------------------------------ onPhysicsStep - // Physics thread. Simulation-coupled logic only: no ECS access here (the - // engine does not allow touching the ECS from the physics thread), so the - // step counter is a local static rather than a component. - // Applies a gentle "wind" to every body every 60 steps. Physics-thread - // systems only receive the Simulation2D& (no ECS, no ServiceProvider). - inline void onPhysicsStepSystem(Simulation2D& simulation) - { - static int stepCounter = 0; - if (++stepCounter % 60 != 0) - return; - - for (SimulationID id = 0; id < simulation.getSize(); ++id) - simulation.addImpulseForce(id, vec2(0.5f, 0.0f)); - - // Per-body user data: iterate only the bodies that opted in, and - // branch on the type discriminator. No ECS access needed here. - simulation.forEachUserData( - [&](SimulationID id, BodyUserData& data) - { - if (data.type == CharacterData::TYPE) - { - // Give the character a little extra lift each gust - simulation.addImpulseForce(id, vec2(0.0f, 4.0f)); - } - }); - } - - // -------------------------------------------------------------- onPhysicsRigidBodyCollision - // Physics thread. Body-body collisions: push the pair apart based on their - // relative velocity. - inline void onPhysicsRigidBodyCollisionSystem(Simulation2D& simulation, PhysicsCollisionEvent& event) - { - vec2 va = simulation.getPhysicsVelocity(event.bodyA); - vec2 vb = simulation.getPhysicsVelocity(event.bodyB); - - vec2 separation = (va - vb) * 0.05f; - simulation.addImpulseForce(event.bodyA, -separation); - simulation.addImpulseForce(event.bodyB, separation); - } - - // --------------------------------------------------------- onPhysicsShapeCollision - // Physics thread. Shape collisions: bounce the body off the shape normal - // when the penetration is deep enough. The character (identified via its - // per-body user data) gets a bouncier, more slippery response by tuning - // the event before the solver reads it. - inline void onPhysicsShapeCollisionSystem(Simulation2D& simulation, PhysicsShapeCollisionEvent& event) - { - if (event.state == CollisionState::START && event.penetration > 0.1f) - { - simulation.addImpulseForce(event.body, event.normal * (2.0f + event.penetration * 10.0f)); - } - - // Type-checked cast: nullptr unless this body carries CharacterData - if (auto* data = simulation.getUserDataAs(event.body)) - { - event.absortion *= 0.5f; // less normal damping = bouncier - event.friction *= 0.5f; // slippery character - } - } - // ------------------------------------------------------- onEntityCollision // Main thread. Body-body collisions mapped to entities: count them, flash // the colliding ball and play a sound through the provider. @@ -449,11 +405,18 @@ namespace ServiceShowcase State& state = getState(ecs, services); state.entityCollisions++; - if (event.entityA != INVALID_ENTITY && ecs.hasComponent(event.entityA)) + // Flash the colliding ball orange, but keep the character's identity + // color: it is identified through its per-body user data. + if (event.entityA != INVALID_ENTITY && ecs.hasComponent(event.entityA) && + ecs.hasComponent(event.entityA)) { - auto& dot = ecs.getComponent(event.entityA); - dot.materialId = DisplaySettings::Orange; - ecs.setComponentDirty(dot); + RigidBody2D& rb = ecs.getComponent(event.entityA); + if (services.physics().getUserDataAs(rb.simulationId) == nullptr) + { + auto& dot = ecs.getComponent(event.entityA); + dot.materialId = DisplaySettings::Orange; + ecs.setComponentDirty(dot); + } } services.audio().playSound({0.05f, 300.0f, false, vec3(0.0f), 1}); @@ -490,52 +453,42 @@ namespace ServiceShowcase class ServiceShowcaseScene : public Scene2D { public: - ServiceShowcaseScene() = default; - -private: - // Most callbacks are thin wrappers around a system. The ServiceProvider - // only reaches callbacks through their `services` parameter (getServices() - // is private); the scene owns no state at all (it lives in the State - // component on the "state" entity). onRender and onImGuiRender are - // inlined below instead of using systems. - - void onCreate(ECSManager& ecs, ServiceProvider& services) override + ServiceShowcaseScene() { - ServiceShowcase::onCreateSystem(ecs, services); - } - - void onStart(ECSManager& ecs, ServiceProvider& services) override - { - ServiceShowcase::onStartSystem(ecs, services); - } + addCreateSystem(ServiceShowcase::onCreateSystem); + addStartSystem(ServiceShowcase::onStartSystem); - void onUpdate(ECSManager& ecs, ServiceProvider& services) override - { - ServiceShowcase::spawnSystem(ecs, services); - ServiceShowcase::inputSystem(ecs, services); - ServiceShowcase::followSystem(ecs, services); - ServiceShowcase::uiSystem(ecs, services); - } + // Multiple systems for the same stage run sequentially! + addUpdateSystem(ServiceShowcase::spawnSystem); + addUpdateSystem(ServiceShowcase::inputSystem); + addUpdateSystem(ServiceShowcase::followSystem); + addUpdateSystem(ServiceShowcase::uiSystem); - void onEntityCollision(ECSManager& ecs, ServiceProvider& services, EntityCollisionEvent& event) override - { - ServiceShowcase::onEntityCollisionSystem(ecs, services, event); - } + addEntityCollisionSystem(ServiceShowcase::onEntityCollisionSystem); + addEntityShapeCollisionSystem(ServiceShowcase::onEntityShapeCollisionSystem); - void onEntityShapeCollision(ECSManager& ecs, ServiceProvider& services, EntityShapeCollisionEvent& event) override - { - ServiceShowcase::onEntityShapeCollisionSystem(ecs, services, event); - } + addDestroySystem(ServiceShowcase::onDestroySystem); - void onDestroy(ECSManager& ecs, ServiceProvider& services) override - { - ServiceShowcase::onDestroySystem(ecs, services); + addImGuiRenderSystem( + [](ECSManager& ecs, ServiceProvider& services) + { + auto& state = ServiceShowcase::getState(ecs, services); + + ImGui::Text("Time: %.2fs", services.time().time()); + ImGui::Text("Entities: %d", services.ecs().getEntityCount()); + ImGui::Text("Gravity: %.1f | Damping: %.2f | Physics %s", state.gravity, state.damping, + services.physics().isPaused() ? "paused" : "running"); + ImGui::Text("Collisions: %d body / %d shape", state.entityCollisions, state.shapeCollisions); + ImGui::Text("Balls spawned: %d", state.ballsSpawned); + + ImGui::Separator(); + ImGui::Text("Left click: spawn ball | Space: pause/resume"); + ImGui::Text("Up/Down: gravity | Left/Right: damping"); + ImGui::Text("Ctrl+S: save scene | Ctrl+L: load scene | Q: next scene"); + }); } - // Don't use systems for these callbacks (they are inlined below). - // Note the firing rules: onRender only fires for 3D / both render modes, - // while onImGuiRender fires for every scene, 2D and 3D alike (it is just - // the debug UI). +private: void onRender(ECSManager& ecs, ServiceProvider& services, WeirdRenderer::RenderTarget& renderTarget) override { static bool logged = false; @@ -546,37 +499,77 @@ class ServiceShowcaseScene : public Scene2D } } - void onImGuiRender(ECSManager& ecs, ServiceProvider& services) override - { - auto& state = ServiceShowcase::getState(ecs, services); - - ImGui::Text("Time: %.2fs", services.time().time()); - ImGui::Text("Entities: %d", services.ecs().getEntityCount()); - ImGui::Text("Gravity: %.1f | Damping: %.2f | Physics %s", state.gravity, state.damping, - services.physics().isPaused() ? "paused" : "running"); - ImGui::Text("Collisions: %d body / %d shape", state.entityCollisions, state.shapeCollisions); - ImGui::Text("Balls spawned: %d", state.ballsSpawned); - - ImGui::Separator(); - ImGui::Text("Left click: spawn ball | Space: pause/resume"); - ImGui::Text("Up/Down: gravity | Left/Right: damping"); - ImGui::Text("Ctrl+S: save scene | Ctrl+L: load scene | Q: next scene"); - } - // Physics thread callbacks. Fire on the physics thread mid-step; no ECS // access here. They delegate to Simulation2D-only systems. void onPhysicsStep(Simulation2D& simulation) override { - ServiceShowcase::onPhysicsStepSystem(simulation); + // ------------------------------------------------------------ onPhysicsStep + // Physics thread. Simulation-coupled logic only: no ECS access here (the + // engine does not allow touching the ECS from the physics thread), so the + // step counter is a local static rather than a component. + // Every 120 steps, applies a wind gust that alternates direction. + // Physics-thread systems only receive the Simulation2D& (no ECS, no + // ServiceProvider). + static int stepCounter = 0; + if (++stepCounter % 120 != 0) + return; + + static float direction = 1.0f; + direction *= -1.0f; + + for (SimulationID id = 0; id < simulation.getSize(); ++id) + simulation.addImpulseForce(id, vec2(2.5f * direction, 0.0f)); + + // Per-body user data: iterate only the bodies that opted in, and + // branch on the type discriminator. No ECS access needed here. + simulation.forEachUserData( + [&](SimulationID id, BodyUserData& data) + { + if (data.type == ServiceShowcase::CharacterData::TYPE) + { + auto characterData = simulation.getUserDataAs(id); + + // Kick the character into a visible hop each gust. + // massIndependent = true: jumpStrength is the delta-v in + // m/s, regardless of the body's mass. + simulation.addImpulseForce(id, vec2(0.0f, characterData->jumpStrength), true); + } + }); } void onPhysicsRigidBodyCollision(Simulation2D& simulation, PhysicsCollisionEvent& event) override { - ServiceShowcase::onPhysicsRigidBodyCollisionSystem(simulation, event); + // -------------------------------------------------------------- onPhysicsRigidBodyCollision + // Physics thread. Body-body collisions: push the pair apart based on their + // relative velocity. + vec2 va = simulation.getPhysicsVelocity(event.bodyA); + vec2 vb = simulation.getPhysicsVelocity(event.bodyB); + + vec2 separation = (va - vb) * 0.05f; + simulation.addImpulseForce(event.bodyA, -separation); + simulation.addImpulseForce(event.bodyB, separation); } void onPhysicsShapeCollision(Simulation2D& simulation, PhysicsShapeCollisionEvent& event) override { - ServiceShowcase::onPhysicsShapeCollisionSystem(simulation, event); + // --------------------------------------------------------- onPhysicsShapeCollision + // Physics thread. Shape collisions: bounce the body off the shape normal + // when the penetration is deep enough. The character (identified via its + // per-body user data) gets a bouncier, more slippery response by tuning + // the event before the solver reads it. + if (event.state == CollisionState::START && event.penetration > 0.1f) + { + simulation.addImpulseForce(event.body, event.normal * (2.0f + event.penetration * 10.0f)); + } + + // Type-checked cast: nullptr unless this body carries CharacterData + if (auto* data = simulation.getUserDataAs(event.body)) + { + // absortion damps the normal axis (more damping = less bounce), so + // higher restitution = bouncier. At restitution = 2.0 (set in + // onStartSystem) this equals the previous hard-coded 0.5x. + event.absortion *= 1.0f / data->restitution; + event.friction *= 0.5f; // slippery character + } } }; From 19d843d9dbcc716bea81bf4864ef9720a1ed5ede Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= <49535803+damacaa@users.noreply.github.com> Date: Tue, 11 Aug 2026 13:29:51 +0200 Subject: [PATCH 17/21] core: rename ECSManager to Registry Renames the entity+component storage class, its members (m_ecs -> m_registry), the ecs() accessor and the ecs/ECS.h header (now ecs/Registry.h). The name no longer over-promises system management: scheduling stays on Scene, which dispatches systems with a Registry& and a ServiceProvider&. The ECS namespace and ecs/ folder are untouched. --- examples/3d-experiments/include/Classic.h | 30 +-- examples/3d-experiments/include/CornellBox.h | 34 +-- .../3d-experiments/include/MaterialShowcase.h | 24 +- examples/empty-project/src/main.cpp | 8 +- examples/opengl-experiments/include/Fire.h | 34 +-- examples/opengl-experiments/include/Lines.h | 26 +-- examples/opengl-experiments/include/Water.h | 44 ++-- .../sample-scenes/include/AquariumScene.h | 194 ++++++++--------- .../sample-scenes/include/CollisionHandling.h | 20 +- examples/sample-scenes/include/DestroyScene.h | 51 ++--- examples/sample-scenes/include/ImageScene.h | 18 +- examples/sample-scenes/include/LifeScene.h | 38 ++-- .../include/MouseCollisionScene.h | 46 ++-- examples/sample-scenes/include/RopeScene.h | 78 +++---- .../include/ServiceShowcaseScene.h | 136 ++++++------ .../include/ShapesCombinations.h | 22 +- examples/sample-scenes/include/TextScene.h | 66 +++--- examples/sample-scenes/include/WalkScene.h | 53 ++--- include/weird-engine/Scene.h | 33 +-- .../weird-engine/ecs/{ECS.h => Registry.h} | 6 +- .../weird-engine/services/ServiceProvider.h | 36 +-- include/weird-engine/systems/ButtonSystem.h | 20 +- include/weird-engine/systems/CameraSystem.h | 6 +- .../systems/PhysicsInteractionSystem.h | 112 +++++----- .../weird-engine/systems/PhysicsSystem2D.h | 64 +++--- .../systems/PlayerMovementSystem.h | 20 +- include/weird-engine/systems/RenderSystem.h | 8 +- .../weird-engine/systems/SDFRenderSystem.h | 20 +- .../systems/SDFShaderGenerationSystem.h | 6 +- .../components/DistanceConstraint.h | 2 +- .../components/DistanceConstraintManager.h | 14 +- include/weird-physics/components/Spring.h | 2 +- .../weird-physics/components/SpringManager.h | 14 +- src/weird-engine/Scene.cpp | 118 +++++----- src/weird-engine/SceneSerializer.cpp | 71 +++--- .../molecule-editor/include/MoleculeEditor.h | 206 +++++++++--------- tools/scene-editor/include/SceneEditor.h | 144 ++++++------ 37 files changed, 915 insertions(+), 909 deletions(-) rename include/weird-engine/ecs/{ECS.h => Registry.h} (98%) diff --git a/examples/3d-experiments/include/Classic.h b/examples/3d-experiments/include/Classic.h index 2b2466d..7bcdfd6 100644 --- a/examples/3d-experiments/include/Classic.h +++ b/examples/3d-experiments/include/Classic.h @@ -12,7 +12,7 @@ class ClassicScene : public Scene3D Entity m_ball; // Inherited via Scene - void onStart(ECSManager& ecs, ServiceProvider& services) override + void onStart(Registry& registry, ServiceProvider& services) override { services.debug().setDebugFly(true); @@ -30,11 +30,11 @@ class ClassicScene : public Scene3D floorMaterial.pattern = MaterialPattern::Checkers; { - Entity entity = ecs.createEntity(); - Transform& t = ecs.addComponent(entity); + Entity entity = registry.createEntity(); + Transform& t = registry.addComponent(entity); t.position = vec3(0, 1, 0); - MeshRenderer& mr = ecs.addComponent(entity); + MeshRenderer& mr = registry.addComponent(entity); auto id = services.resources().getMeshId("monkey/demo.gltf", entity, true); mr.mesh = id; @@ -44,11 +44,11 @@ class ClassicScene : public Scene3D } { - Entity entity = ecs.createEntity(); - Transform& t = ecs.addComponent(entity); + Entity entity = registry.createEntity(); + Transform& t = registry.addComponent(entity); t.position = vec3(2, 3, 2); - auto& sdf = ecs.addComponent(entity); + auto& sdf = registry.addComponent(entity); sdf.materialId = redMat.id; m_ball = entity; @@ -67,36 +67,36 @@ class ClassicScene : public Scene3D } { - Entity entity = ecs.createEntity(); - Transform& t = ecs.addComponent(entity); + Entity entity = registry.createEntity(); + Transform& t = registry.addComponent(entity); t.position = glm::vec3(0.0f, 3.0f, 0.0f); t.rotation = glm::vec3(0.35f, 0.45f, 0.5f); - LightComponent& lc = ecs.addComponent(entity); + LightComponent& lc = registry.addComponent(entity); lc.type = LightType::Directional; lc.color = glm::vec4(1.0f, 0.95f, 0.9f, 2.0f); } - ecs.getComponent(services.render().getCameraEntity()).position = vec3(0, 2, 10); + registry.getComponent(services.render().getCameraEntity()).position = vec3(0, 2, 10); } - void onUpdate(ECSManager& ecs, ServiceProvider& services) override + void onUpdate(Registry& registry, ServiceProvider& services) override { if (services.input().getKeyDown(Input::Q)) { services.sceneControl().goToNextScene(); } - Transform& cameraTransform = ecs.getComponent(services.render().getCameraEntity()); + Transform& cameraTransform = registry.getComponent(services.render().getCameraEntity()); return; { - Transform& t = ecs.getComponent(m_monkey); + Transform& t = registry.getComponent(m_monkey); } { - Transform& t = ecs.getComponent(m_ball); + Transform& t = registry.getComponent(m_ball); // t.position.z = 10 * sinf(services.time().time()); t.position.x = 2.0f * sinf(-services.time().time()); t.position.z = 2.0f * cosf(-services.time().time()); diff --git a/examples/3d-experiments/include/CornellBox.h b/examples/3d-experiments/include/CornellBox.h index 9d3f07c..4d6c49d 100644 --- a/examples/3d-experiments/include/CornellBox.h +++ b/examples/3d-experiments/include/CornellBox.h @@ -16,7 +16,7 @@ class CornellBox : public Scene3D private: Entity m_sunLight; // Inherited via Scene - void onStart(ECSManager& ecs, ServiceProvider& services) override + void onStart(Registry& registry, ServiceProvider& services) override { services.debug().setDebugFly(true); @@ -37,20 +37,20 @@ class CornellBox : public Scene3D whiteMat.color = vec4(1.0f, 1.0f, 1.0f, 1.0f); { - Entity entity = ecs.createEntity(); - Transform& t = ecs.addComponent(entity); + Entity entity = registry.createEntity(); + Transform& t = registry.addComponent(entity); t.position = vec3(0.0f, 0.75f, 0.0f); - auto& sdf = ecs.addComponent(entity); + auto& sdf = registry.addComponent(entity); sdf.materialId = ballMat.id; } { - Entity entity = ecs.createEntity(); - Transform& t = ecs.addComponent(entity); + Entity entity = registry.createEntity(); + Transform& t = registry.addComponent(entity); t.position = vec3(20.0f, 20.0f, 0.0f); - auto& text = ecs.addComponent(entity); + auto& text = registry.addComponent(entity); text.text = "Cornell Box"; } @@ -103,23 +103,23 @@ class CornellBox : public Scene3D // Sun { - m_sunLight = ecs.createEntity(); - Transform& t = ecs.addComponent(m_sunLight); + m_sunLight = registry.createEntity(); + Transform& t = registry.addComponent(m_sunLight); t.position = glm::vec3(0.0f, 0.0f, 0.0f); t.rotation = normalize(glm::vec3(0.0f, 0.0f, 0.0f)); - LightComponent& lc = ecs.addComponent(m_sunLight); + LightComponent& lc = registry.addComponent(m_sunLight); lc.type = LightType::Directional; lc.color = glm::vec4(1.0f, 1.0f, 1.0f, 0.0f); } { - Entity entity = ecs.createEntity(); - Transform& t = ecs.addComponent(entity); + Entity entity = registry.createEntity(); + Transform& t = registry.addComponent(entity); t.position = glm::vec3(0.0f, (2.0f * 2.6f) + 0.25f, 0.0f); t.rotation = glm::vec3(0.0f, 0.0f, 0.0f); - LightComponent& lc = ecs.addComponent(entity); + LightComponent& lc = registry.addComponent(entity); lc.type = LightType::Point; lc.color = glm::vec4(1.0f, 1.0f, 1.0f, 3.0f); } @@ -130,19 +130,19 @@ class CornellBox : public Scene3D // getLights().push_back( // Light{2, glm::vec3(0.0f, 0.0f, 0.0f), 0, glm::vec3(0.0f, 1.0f, 0.0f), glm::vec4(0.0f, 0.0f, 2.0f, 10.0f)}); - ecs.getComponent(services.render().getCameraEntity()).position = vec3(0, 2.6f, 12.0f); + registry.getComponent(services.render().getCameraEntity()).position = vec3(0, 2.6f, 12.0f); } - void onUpdate(ECSManager& ecs, ServiceProvider& services) override + void onUpdate(Registry& registry, ServiceProvider& services) override { if (services.input().getKeyDown(Input::Q)) { services.sceneControl().goToNextScene(); } - auto& cameraTransform = ecs.getComponent(services.render().getCameraEntity()); + auto& cameraTransform = registry.getComponent(services.render().getCameraEntity()); - auto& lightTransform = ecs.getComponent(m_sunLight); + auto& lightTransform = registry.getComponent(m_sunLight); lightTransform.position = cameraTransform.position; lightTransform.rotation = -cameraTransform.rotation; } diff --git a/examples/3d-experiments/include/MaterialShowcase.h b/examples/3d-experiments/include/MaterialShowcase.h index 125f6e9..59bdad8 100644 --- a/examples/3d-experiments/include/MaterialShowcase.h +++ b/examples/3d-experiments/include/MaterialShowcase.h @@ -16,13 +16,13 @@ class MaterialShowcaseScene : public Scene3D private: Entity m_sunLight; // Inherited via Scene - void onStart(ECSManager& ecs, ServiceProvider& services) override + void onStart(Registry& registry, ServiceProvider& services) override { services.debug().setDebugFly(true); { - Entity entity = ecs.createEntity(); - Transform& t = ecs.addComponent(entity); + Entity entity = registry.createEntity(); + Transform& t = registry.addComponent(entity); t.position = vec3(-0.5f, -2.0f, 0); auto& mat = services.materials().createMaterial(); @@ -30,7 +30,7 @@ class MaterialShowcaseScene : public Scene3D mat.metallic = 1.0f; mat.roughness = 0.0f; - auto& sdf = ecs.addComponent(entity); + auto& sdf = registry.addComponent(entity); sdf.materialId = mat.id; } @@ -114,11 +114,11 @@ class MaterialShowcaseScene : public Scene3D for (size_t i = 0; i < randomMats.size(); i++) { - Entity entity = ecs.createEntity(); - Transform& t = ecs.addComponent(entity); + Entity entity = registry.createEntity(); + Transform& t = registry.addComponent(entity); t.position = vec3(1.0f + (i), -2.0f, 0); - auto& sdf = ecs.addComponent(entity); + auto& sdf = registry.addComponent(entity); sdf.materialId = randomMats[i]; } @@ -158,12 +158,12 @@ class MaterialShowcaseScene : public Scene3D } { - m_sunLight = ecs.createEntity(); - Transform& t = ecs.addComponent(m_sunLight); + m_sunLight = registry.createEntity(); + Transform& t = registry.addComponent(m_sunLight); t.position = glm::vec3(0.0f, 0.0f, 0.0f); t.rotation = normalize(glm::vec3(0.0f, 0.4f, 1.0f)); - LightComponent& lc = ecs.addComponent(m_sunLight); + LightComponent& lc = registry.addComponent(m_sunLight); lc.type = LightType::Directional; lc.color = glm::vec4(1.0f, 1.0f, 1.0f, 0.75f); } @@ -174,12 +174,12 @@ class MaterialShowcaseScene : public Scene3D // getLights().push_back( // Light{2, glm::vec3(0.0f, 0.0f, 0.0f), 0, glm::vec3(0.0f, 1.0f, 0.0f), glm::vec4(0.0f, 0.0f, 2.0f, 10.0f)}); - auto& cameraTransform = ecs.getComponent(services.render().getCameraEntity()); + auto& cameraTransform = registry.getComponent(services.render().getCameraEntity()); cameraTransform.position = vec3(12, -1, 12); cameraTransform.rotation.x = -0.95f; } - void onUpdate(ECSManager& ecs, ServiceProvider& services) override + void onUpdate(Registry& registry, ServiceProvider& services) override { if (services.input().getKeyDown(Input::Q)) { diff --git a/examples/empty-project/src/main.cpp b/examples/empty-project/src/main.cpp index 74efd1b..11dded3 100644 --- a/examples/empty-project/src/main.cpp +++ b/examples/empty-project/src/main.cpp @@ -5,8 +5,8 @@ using namespace WeirdEngine; // Starter template for a new game. Register scenes in main() and the engine // takes care of the rest. Override the callbacks you need: // onCreate - once, before the physics thread starts -// onStart(ecs[, tags])- after the ECS is ready and the physics thread runs -// onUpdate(dt, ecs) - game logic, once per frame (pure virtual) +// onStart(registry[, tags])- after the ECS is ready and the physics thread runs +// onUpdate(dt, registry) - game logic, once per frame (pure virtual) // onRender(target) - extra 3D rendering // onImGuiRender - debug UI // onCollision / onShapeCollision - physics thread, no ECS access @@ -15,9 +15,9 @@ using namespace WeirdEngine; class EmptyScene : public Scene2D { private: - void onStart(ECSManager& ecs, ServiceProvider& services) override {} + void onStart(Registry& registry, ServiceProvider& services) override {} - void onUpdate(ECSManager& ecs, ServiceProvider& services) override {} + void onUpdate(Registry& registry, ServiceProvider& services) override {} }; int main(int argc, char* argv[]) diff --git a/examples/opengl-experiments/include/Fire.h b/examples/opengl-experiments/include/Fire.h index 1007cd0..e6767cf 100644 --- a/examples/opengl-experiments/include/Fire.h +++ b/examples/opengl-experiments/include/Fire.h @@ -39,7 +39,7 @@ class FireScene : public Scene3D RenderPlane m_renderPlane; - void onCreate(ECSManager& ecs, ServiceProvider& services) override + void onCreate(Registry& registry, ServiceProvider& services) override { // Base shaders @@ -61,23 +61,23 @@ class FireScene : public Scene3D m_heatDistortionShader = Shader(SHADERS_PATH "3d/geometry.vert", services.resources().assetPath("fire/shaders/heatDistortion.frag")); - m_light0 = ecs.createEntity(); + m_light0 = registry.createEntity(); { - Transform& t = ecs.addComponent(m_light0); + Transform& t = registry.addComponent(m_light0); t.position = glm::vec3(0.0f); t.rotation = glm::vec3(0.0f); - LightComponent& lc = ecs.addComponent(m_light0); + LightComponent& lc = registry.addComponent(m_light0); lc.type = LightType::Directional; lc.color = glm::vec4(0.0f); } - m_light1 = ecs.createEntity(); + m_light1 = registry.createEntity(); { - Transform& t = ecs.addComponent(m_light1); + Transform& t = registry.addComponent(m_light1); t.position = glm::vec3(0.0f, 1.0f, 0.0f); t.rotation = glm::vec3(0.0f); - LightComponent& lc = ecs.addComponent(m_light1); + LightComponent& lc = registry.addComponent(m_light1); lc.type = LightType::Point; lc.color = glm::vec4(1.0f, 0.95f, 0.9f, 2.0f); } @@ -186,7 +186,7 @@ class FireScene : public Scene3D m_bloomRenderTarget->bindColorTextureToFrameBuffer(*m_brightPassTexture); } - void onDestroy(ECSManager& ecs, ServiceProvider& services) override + void onDestroy(Registry& registry, ServiceProvider& services) override { m_flameShader.free(); m_particlesShader.free(); @@ -227,13 +227,13 @@ class FireScene : public Scene3D } // Inherited via Scene - void onStart(ECSManager& ecs, ServiceProvider& services) override + void onStart(Registry& registry, ServiceProvider& services) override { services.debug().setDebugFly(false); } float m_time = 3.1416f; - void onUpdate(ECSManager& ecs, ServiceProvider& services) override + void onUpdate(Registry& registry, ServiceProvider& services) override { float delta = services.time().deltaTime(); if (services.input().getKeyDown(Input::Q)) @@ -259,7 +259,7 @@ class FireScene : public Scene3D } } - Transform& cameraTransform = ecs.getComponent(services.render().getCameraEntity()); + Transform& cameraTransform = registry.getComponent(services.render().getCameraEntity()); static float amplitude = 10.0f; @@ -312,10 +312,10 @@ class FireScene : public Scene3D glDisable(GL_BLEND); } - void onRender(ECSManager& ecs, ServiceProvider& services, WeirdRenderer::RenderTarget& renderTarget) override + void onRender(Registry& registry, ServiceProvider& services, WeirdRenderer::RenderTarget& renderTarget) override { WeirdRenderer::Camera& sceneCamera = - ecs.getComponent(services.render().getCameraEntity()).camera; + registry.getComponent(services.render().getCameraEntity()).camera; float time = services.time().time(); glDepthMask(GL_FALSE); @@ -343,10 +343,10 @@ class FireScene : public Scene3D m_litShader.setUniform("u_far", sceneCamera.farPlane); // Pass light rotation - auto& light0_t = ecs.getComponent(m_light0); - auto& light0_lc = ecs.getComponent(m_light0); - auto& light1_t = ecs.getComponent(m_light1); - auto& light1_lc = ecs.getComponent(m_light1); + auto& light0_t = registry.getComponent(m_light0); + auto& light0_lc = registry.getComponent(m_light0); + auto& light1_t = registry.getComponent(m_light1); + auto& light1_lc = registry.getComponent(m_light1); glm::vec3 position = light1_t.position; m_litShader.setUniform("u_lightPos", position); glm::vec3 direction = light1_t.rotation; diff --git a/examples/opengl-experiments/include/Lines.h b/examples/opengl-experiments/include/Lines.h index 3be0b32..7035c7e 100644 --- a/examples/opengl-experiments/include/Lines.h +++ b/examples/opengl-experiments/include/Lines.h @@ -24,7 +24,7 @@ class LinesScene : public Scene3D Entity m_monkey; - void onCreate(ECSManager& ecs, ServiceProvider& services) override + void onCreate(Registry& registry, ServiceProvider& services) override { { @@ -53,33 +53,33 @@ class LinesScene : public Scene3D } // Inherited via Scene - void onStart(ECSManager& ecs, ServiceProvider& services) override + void onStart(Registry& registry, ServiceProvider& services) override { services.debug().setDebugFly(false); { - Entity entity = ecs.createEntity(); - ecs.addComponent(entity); - ecs.addComponent(entity); + Entity entity = registry.createEntity(); + registry.addComponent(entity); + registry.addComponent(entity); } { - Entity entity = ecs.createEntity(); - Transform& t = ecs.addComponent(entity); + Entity entity = registry.createEntity(); + Transform& t = registry.addComponent(entity); t.position = vec3(0, 0, 0); - // MeshRenderer &mr = ecs.addComponent(entity); + // MeshRenderer &mr = registry.addComponent(entity); // auto id = services.resources().getMeshId(services.resources().assetPath("monkey/demo.gltf"), entity, // true); mr.mesh = id; - auto& sdf = ecs.addComponent(entity); + auto& sdf = registry.addComponent(entity); sdf.materialId = m_whiteMatId; m_monkey = entity; } } - void onUpdate(ECSManager& ecs, ServiceProvider& services) override + void onUpdate(Registry& registry, ServiceProvider& services) override { float delta = services.time().deltaTime(); if (services.input().getKeyDown(Input::Q)) @@ -87,16 +87,16 @@ class LinesScene : public Scene3D services.sceneControl().goToNextScene(); } - Transform& cameraTransform = ecs.getComponent(services.render().getCameraEntity()); + Transform& cameraTransform = registry.getComponent(services.render().getCameraEntity()); cameraTransform.position.y = 5.0f; cameraTransform.position.z -= 10.0f * delta; - Transform& monkeyTransform = ecs.getComponent(m_monkey); + Transform& monkeyTransform = registry.getComponent(m_monkey); monkeyTransform.position = cameraTransform.position; monkeyTransform.position.z -= 5.0f; } - void onRender(ECSManager& ecs, ServiceProvider& services, WeirdRenderer::RenderTarget& renderTarget) override + void onRender(Registry& registry, ServiceProvider& services, WeirdRenderer::RenderTarget& renderTarget) override { m_lineRender->bind(); glClearColor(0, 0, 0, 0); // Set clear color diff --git a/examples/opengl-experiments/include/Water.h b/examples/opengl-experiments/include/Water.h index 4cbe00c..412cc9c 100644 --- a/examples/opengl-experiments/include/Water.h +++ b/examples/opengl-experiments/include/Water.h @@ -45,19 +45,19 @@ class WaterScene : public Scene3D // ------------------------------------------------------------------------- - void onCreate(ECSManager& ecs, ServiceProvider& services) override + void onCreate(Registry& registry, ServiceProvider& services) override { m_waterShader = Shader(services.resources().assetPath("water/shaders/water.vert"), services.resources().assetPath("water/shaders/water.frag")); - m_light0 = ecs.createEntity(); + m_light0 = registry.createEntity(); { - Transform& t = ecs.addComponent(m_light0); + Transform& t = registry.addComponent(m_light0); t.position = glm::vec3(0.0f, 0.0f, 0.0f); t.rotation = normalize(glm::vec3(0.0f, 0.4f, 1.0f)); - LightComponent& lc = ecs.addComponent(m_light0); + LightComponent& lc = registry.addComponent(m_light0); lc.type = LightType::Directional; lc.color = glm::vec4(1.0f, 1.0f, 1.0f, 0.5f); } @@ -65,7 +65,7 @@ class WaterScene : public Scene3D m_waterPlane.build(); } - void onDestroy(ECSManager& ecs, ServiceProvider& services) override + void onDestroy(Registry& registry, ServiceProvider& services) override { m_waterShader.free(); @@ -86,7 +86,7 @@ class WaterScene : public Scene3D float buoyancy = 10.0f; // how strongly this entity is affected by the water surface }; - void onStart(ECSManager& ecs, ServiceProvider& services) override + void onStart(Registry& registry, ServiceProvider& services) override { services.debug().setDebugFly(true); @@ -94,32 +94,32 @@ class WaterScene : public Scene3D redMat.color = vec4(.8f, 0.2f, 0.2f, 1.0f); { - m_dot = ecs.createEntity(); - Transform& t = ecs.addComponent(m_dot); + m_dot = registry.createEntity(); + Transform& t = registry.addComponent(m_dot); t.position = vec3(0, 0, 0); - auto& renderer = ecs.addComponent(m_dot); + auto& renderer = registry.addComponent(m_dot); renderer.materialId = redMat.id; - ecs.addComponent(m_dot); + registry.addComponent(m_dot); } { - Entity entity = ecs.createEntity(); - Transform& t = ecs.addComponent(entity); + Entity entity = registry.createEntity(); + Transform& t = registry.addComponent(entity); t.position = vec3(3, 0, 0); - MeshRenderer& mr = ecs.addComponent(entity); + MeshRenderer& mr = registry.addComponent(entity); auto id = services.resources().getMeshId("monkey/demo.gltf", entity, true); mr.mesh = id; - ecs.addComponent(entity); + registry.addComponent(entity); } - ecs.getComponent(services.render().getCameraEntity()).position = vec3(0, 3, 20); + registry.getComponent(services.render().getCameraEntity()).position = vec3(0, 3, 20); } float m_time = 0.0f; - void onUpdate(ECSManager& ecs, ServiceProvider& services) override + void onUpdate(Registry& registry, ServiceProvider& services) override { float delta = services.time().deltaTime(); if (services.input().getKeyDown(Input::Q)) @@ -129,14 +129,14 @@ class WaterScene : public Scene3D m_time += delta; - const auto& floatables = ecs.getComponentArray(); + const auto& floatables = registry.getComponentArray(); for (int i = 0; i < floatables->getSize(); i++) { auto& floatable = floatables->getDataAtIdx(i); // Keep the dot riding the water surface - Transform& transform = ecs.getComponent(floatables->getEntityAtIdx(i)); + Transform& transform = registry.getComponent(floatables->getEntityAtIdx(i)); glm::vec2 flatPos = {transform.position.x, transform.position.z}; float centerHeight = m_waterPlane.waterHeightAt(flatPos, m_time); transform.position.y = centerHeight; @@ -153,14 +153,14 @@ class WaterScene : public Scene3D } } - void onRender(ECSManager& ecs, ServiceProvider& services, WeirdRenderer::RenderTarget& renderTarget) override + void onRender(Registry& registry, ServiceProvider& services, WeirdRenderer::RenderTarget& renderTarget) override { WeirdRenderer::Camera& sceneCamera = - ecs.getComponent(services.render().getCameraEntity()).camera; + registry.getComponent(services.render().getCameraEntity()).camera; float time = services.time().time(); - auto& light0_t = ecs.getComponent(m_light0); - auto& light0_lc = ecs.getComponent(m_light0); + auto& light0_t = registry.getComponent(m_light0); + auto& light0_lc = registry.getComponent(m_light0); // ── Snapshot the current scene colour + depth ──────────────────────── // We need to read from these textures while drawing the water plane, diff --git a/examples/sample-scenes/include/AquariumScene.h b/examples/sample-scenes/include/AquariumScene.h index 34ebdf8..d2aca49 100644 --- a/examples/sample-scenes/include/AquariumScene.h +++ b/examples/sample-scenes/include/AquariumScene.h @@ -72,7 +72,7 @@ class AquariumScene : public Scene2D static constexpr float TANK_W = TANK_RIGHT - TANK_LEFT; static constexpr float TANK_H = TANK_TOP - TANK_BOTTOM; - void onStart(ECSManager& ecs, ServiceProvider& services) override + void onStart(Registry& registry, ServiceProvider& services) override { services.debug().setDebugInput(true); services.debug().setDebugFly(true); @@ -83,32 +83,32 @@ class AquariumScene : public Scene2D background.secondaryColor = vec4(86, 208, 197, 255) / 255.0f; background.scale = 0.15f; - Entity globalSettingsEnt = ecs.createEntity(); - auto& settings = ecs.addComponent(globalSettingsEnt); + Entity globalSettingsEnt = registry.createEntity(); + auto& settings = registry.addComponent(globalSettingsEnt); settings.gravity = -10.0f; settings.damping = 0.025f; - ecs.setComponentDirty(settings); + registry.setComponentDirty(settings); - createJellyfish(ecs, services, 0.0f, 25.0f, 4 + 0, 1.3f, 0.0f); - createJellyfish(ecs, services, 15.0f, 20.0f, 4 + 3, 1.6f, 1.5f); - createJellyfish(ecs, services, 30.0f, 28.0f, 4 + 6, 1.1f, 3.0f); - createJellyfish(ecs, services, 40.0f, 15.0f, 4 + 9, 1.4f, 4.5f); + createJellyfish(registry, services, 0.0f, 25.0f, 4 + 0, 1.3f, 0.0f); + createJellyfish(registry, services, 15.0f, 20.0f, 4 + 3, 1.6f, 1.5f); + createJellyfish(registry, services, 30.0f, 28.0f, 4 + 6, 1.1f, 3.0f); + createJellyfish(registry, services, 40.0f, 15.0f, 4 + 9, 1.4f, 4.5f); - createEel(ecs, -10.0f, 50.0f, 20, 1.0f, 4); - createEel(ecs, 40.0f, 40.0f, 18, 1.0f, 8); - createEel(ecs, 10.0f, 30.0f, 22, 0.9f, 6); + createEel(registry, -10.0f, 50.0f, 20, 1.0f, 4); + createEel(registry, 40.0f, 40.0f, 18, 1.0f, 8); + createEel(registry, 10.0f, 30.0f, 22, 0.9f, 6); { float seaweedVars[8] = {3.0f, 1.2f, 2.5f}; Entity seaweed = services.shapes().addShape(DefaultShapes::SINE, seaweedVars, DisplaySettings::Green); - auto& sw = ecs.addComponent(seaweed); + auto& sw = registry.addComponent(seaweed); sw.animationOffset = 0.0f; } { float seaweedVars[8] = {2.0f, 2.0f, 1.8f}; Entity seaweed = services.shapes().addShape(DefaultShapes::SINE, seaweedVars, DisplaySettings::LightGreen); - auto& sw = ecs.addComponent(seaweed); + auto& sw = registry.addComponent(seaweed); sw.animationOffset = 1.5f; } @@ -125,20 +125,20 @@ class AquariumScene : public Scene2D for (int i = 0; i < 40; i++) { - Entity fish = ecs.createEntity(); - auto& t = ecs.addComponent(fish); + Entity fish = registry.createEntity(); + auto& t = registry.addComponent(fish); float fx = TANK_LEFT + 5.0f + static_cast(std::rand() % 60); float fy = TANK_BOTTOM + 5.0f + static_cast(std::rand() % 35); t.position = vec3(fx, fy, 0.0f); - auto& dot = ecs.addComponent(fish); + auto& dot = registry.addComponent(fish); dot.materialId = 4 + (i % 12); - auto& rb = ecs.addComponent(fish); + auto& rb = registry.addComponent(fish); rb.pendingImpulseForce += vec2(static_cast((std::rand() % 100) - 50) * 0.05f, static_cast((std::rand() % 100) - 50) * 0.05f); - auto& fishComp = ecs.addComponent(fish); + auto& fishComp = registry.addComponent(fish); float angle = static_cast(std::rand() % 628) * 0.01f; fishComp.velocity = vec2(std::cos(angle), std::sin(angle)) * 3.0f; fishComp.maxSpeed = 5.0f; @@ -148,24 +148,24 @@ class AquariumScene : public Scene2D fishComp.perceptionRadius = 5.0f; } - ecs.getComponent(services.render().getCameraEntity()).position = vec3(TANK_CX, TANK_CY, 45.0f); + registry.getComponent(services.render().getCameraEntity()).position = vec3(TANK_CX, TANK_CY, 45.0f); } - void createJellyfish(ECSManager& ecs, ServiceProvider& services, float x, float y, int material, float scale, + void createJellyfish(Registry& registry, ServiceProvider& services, float x, float y, int material, float scale, float phase) { - Entity bellEntity = ecs.createEntity(); - auto& t = ecs.addComponent(bellEntity); + Entity bellEntity = registry.createEntity(); + auto& t = registry.addComponent(bellEntity); t.position = vec3(x, y, 0.0f); - auto& dot = ecs.addComponent(bellEntity); + auto& dot = registry.addComponent(bellEntity); dot.materialId = material; - auto& rb = ecs.addComponent(bellEntity); + auto& rb = registry.addComponent(bellEntity); float bellVars[8] = {x, y, 2.5f * scale, 0.8f, 6.0f, 2.0f}; Entity bellShape = services.shapes().addShape(DefaultShapes::STAR, bellVars, material, CombinationType::Addition, false); - auto& jf = ecs.addComponent(bellEntity); + auto& jf = registry.addComponent(bellEntity); jf.bellShape = bellShape; jf.pulsePhase = phase; jf.pulseSpeed = 0.5f + static_cast(std::rand() % 100) * 0.01f; @@ -184,19 +184,19 @@ class AquariumScene : public Scene2D for (int s = 0; s < segmentsPerTentacle; s++) { - Entity segment = ecs.createEntity(); - auto& st = ecs.addComponent(segment); + Entity segment = registry.createEntity(); + auto& st = registry.addComponent(segment); st.position = vec3(x + offsetX, y - 2.5f * scale - s * 0.8f, 0.0f); - auto& sd = ecs.addComponent(segment); + auto& sd = registry.addComponent(segment); sd.materialId = material; - auto& srb = ecs.addComponent(segment); + auto& srb = registry.addComponent(segment); jf.tentacleSegments.push_back(segment); if (s > 0) { - Entity springEnt = ecs.createEntity(); - auto& spring = ecs.addComponent(springEnt); + Entity springEnt = registry.createEntity(); + auto& spring = registry.addComponent(springEnt); spring.entityA = jf.tentacleSegments[jf.tentacleSegments.size() - 2]; spring.entityB = segment; spring.stiffness = 0.8f; @@ -204,8 +204,8 @@ class AquariumScene : public Scene2D } } - Entity springEnt = ecs.createEntity(); - auto& spring = ecs.addComponent(springEnt); + Entity springEnt = registry.createEntity(); + auto& spring = registry.addComponent(springEnt); spring.entityA = bellEntity; spring.entityB = jf.tentacleSegments[t_idx * segmentsPerTentacle]; spring.stiffness = 0.5f; @@ -213,13 +213,13 @@ class AquariumScene : public Scene2D } } - void createEel(ECSManager& ecs, float x, float y, int numSegments, float spacing, int baseMaterial) + void createEel(Registry& registry, float x, float y, int numSegments, float spacing, int baseMaterial) { float angle = static_cast(std::rand() % 628) * 0.01f; vec2 dir(std::cos(angle), std::sin(angle)); - Entity eelEnt = ecs.createEntity(); - auto& eel = ecs.addComponent(eelEnt); + Entity eelEnt = registry.createEntity(); + auto& eel = registry.addComponent(eelEnt); eel.phaseOffset = static_cast(std::rand() % 100) * 0.0628f; eel.speed = 2.0f + static_cast(std::rand() % 100) * 0.02f; eel.direction = dir; @@ -228,21 +228,21 @@ class AquariumScene : public Scene2D for (int i = 0; i < numSegments; i++) { - Entity seg = ecs.createEntity(); - auto& t = ecs.addComponent(seg); + Entity seg = registry.createEntity(); + auto& t = registry.addComponent(seg); t.position = vec3(x + dir.x * i * spacing, y + dir.y * i * spacing, 0.0f); - auto& dot = ecs.addComponent(seg); + auto& dot = registry.addComponent(seg); dot.materialId = static_cast(baseMaterial + (i % 4)); - ecs.addComponent(seg); + registry.addComponent(seg); eel.segments.push_back(seg); if (i > 0) { - Entity springEnt = ecs.createEntity(); - auto& spring = ecs.addComponent(springEnt); + Entity springEnt = registry.createEntity(); + auto& spring = registry.addComponent(springEnt); spring.entityA = eel.segments[i - 1]; spring.entityB = seg; spring.stiffness = 0.25f; @@ -251,10 +251,10 @@ class AquariumScene : public Scene2D } } - void onUpdate(ECSManager& ecs, ServiceProvider& services) override + void onUpdate(Registry& registry, ServiceProvider& services) override { float delta = services.time().deltaTime(); - g_cameraPositon = ecs.getComponent(services.render().getCameraEntity()).position; + g_cameraPositon = registry.getComponent(services.render().getCameraEntity()).position; if (services.input().getKeyDown(Input::Q) || services.input().getGamepadButtonDown(Input::GamepadButton::North)) { @@ -263,7 +263,7 @@ class AquariumScene : public Scene2D m_time += delta; - auto& cameraTransform = ecs.getComponent(services.render().getCameraEntity()); + auto& cameraTransform = registry.getComponent(services.render().getCameraEntity()); vec2 mouseWorld = ECS::Camera::screenPositionToWorldPosition2D( cameraTransform, vec2(services.input().getMouseX(), services.input().getMouseY())); @@ -272,30 +272,30 @@ class AquariumScene : public Scene2D constexpr int FOOD_COUNT = 30; for (int i = 0; i < FOOD_COUNT; i++) { - Entity food = ecs.createEntity(); - auto& ft = ecs.addComponent(food); + Entity food = registry.createEntity(); + auto& ft = registry.addComponent(food); float ox = static_cast(std::rand() % 200 - 100) * 0.06f; float oy = static_cast(std::rand() % 200 - 100) * 0.06f; ft.position = vec3(mouseWorld.x + ox, mouseWorld.y + oy, 0.0f); - ecs.setComponentDirty(ft); - auto& fd = ecs.addComponent(food); + registry.setComponentDirty(ft); + auto& fd = registry.addComponent(food); fd.materialId = 8; - auto& frb = ecs.addComponent(food); + auto& frb = registry.addComponent(food); - ecs.addComponent(food); + registry.addComponent(food); } } - ecs.forEach( + registry.forEach( [&](Entity bellEntity, JellyfishComponent& jf, Transform& bellT, RigidBody2D& rb) { - auto& cs = ecs.getComponent(jf.bellShape); + auto& cs = registry.getComponent(jf.bellShape); cs.parameters[0] = bellT.position.x; cs.parameters[1] = bellT.position.y; float pulse = std::sin(m_time * jf.pulseSpeed + jf.pulsePhase); cs.parameters[3] = 1.0f + pulse * 0.8f; cs.parameters[2] = 2.5f + pulse * 0.3f; - ecs.setComponentDirty(cs); + registry.setComponentDirty(cs); float pulseUp = (pulse > 0.0f) ? pulse * pulse * 20.5f : 0.0f; rb.pendingImpulseForce += jf.direction * pulseUp; @@ -336,14 +336,14 @@ class AquariumScene : public Scene2D } }); - ecs.forEach( + registry.forEach( [&](Entity, Seaweed& sw, CustomShape& cs) { cs.parameters[3] = std::sin(m_time * 1.5f + sw.animationOffset) * 4.0f; - ecs.setComponentDirty(cs); + registry.setComponentDirty(cs); }); - ecs.forEach( + registry.forEach( [&](Entity, EelComponent& eel) { if (std::rand() % 120 == 0) @@ -357,15 +357,15 @@ class AquariumScene : public Scene2D } Entity head = eel.segments[0]; - auto& headT = ecs.getComponent(head); + auto& headT = registry.getComponent(head); vec2 headPos(headT.position); - auto& headRb = ecs.getComponent(head); + auto& headRb = registry.getComponent(head); headRb.pendingContinuousForce += eel.direction * eel.speed * 120.0f; { - auto foodArray = ecs.getComponentArray(); - auto transformArray = ecs.getComponentArray(); + auto foodArray = registry.getComponentArray(); + auto transformArray = registry.getComponentArray(); constexpr float eatRadius = 2.0f; for (size_t fi = 0; fi < foodArray->getSize(); fi++) { @@ -380,7 +380,7 @@ class AquariumScene : public Scene2D if (toFood.x * toFood.x + toFood.y * toFood.y < eatRadius * eatRadius) { ff.eaten = true; - ecs.destroyEntity(foodEntity); + registry.destroyEntity(foodEntity); Entity lastSeg = eel.segments.back(); Entity prevSeg = @@ -395,19 +395,19 @@ class AquariumScene : public Scene2D else tailDir /= tailLen; - Entity newSeg = ecs.createEntity(); - auto& nt = ecs.addComponent(newSeg); + Entity newSeg = registry.createEntity(); + auto& nt = registry.addComponent(newSeg); nt.position = vec3(lastT.position.x + tailDir.x * eel.segmentSpacing, lastT.position.y + tailDir.y * eel.segmentSpacing, 0.0f); - auto& nd = ecs.addComponent(newSeg); + auto& nd = registry.addComponent(newSeg); nd.materialId = static_cast(eel.baseMaterial + static_cast(eel.segments.size() % 4)); - ecs.addComponent(newSeg); + registry.addComponent(newSeg); - Entity springEnt = ecs.createEntity(); - auto& spring = ecs.addComponent(springEnt); + Entity springEnt = registry.createEntity(); + auto& spring = registry.addComponent(springEnt); spring.entityA = lastSeg; spring.entityB = newSeg; spring.stiffness = 0.25f; @@ -421,8 +421,8 @@ class AquariumScene : public Scene2D for (size_t i = 1; i < eel.segments.size(); i++) { - auto& segT = ecs.getComponent(eel.segments[i]); - auto& prevT = ecs.getComponent(eel.segments[i - 1]); + auto& segT = registry.getComponent(eel.segments[i]); + auto& prevT = registry.getComponent(eel.segments[i - 1]); vec2 bodyDir = vec2(segT.position) - vec2(prevT.position); float bodyLen = length(bodyDir); @@ -431,15 +431,15 @@ class AquariumScene : public Scene2D vec2 normal(bodyDir.y, -bodyDir.x); normal /= bodyLen; float wave = std::sin(m_time * 5.0f + eel.phaseOffset + static_cast(i) * 0.6f); - auto& segRb = ecs.getComponent(eel.segments[i]); + auto& segRb = registry.getComponent(eel.segments[i]); segRb.pendingContinuousForce += normal * wave * 80.0f; } } for (Entity seg : eel.segments) { - auto& segT = ecs.getComponent(seg); - auto& segRb = ecs.getComponent(seg); + auto& segT = registry.getComponent(seg); + auto& segRb = registry.getComponent(seg); segRb.pendingContinuousForce += vec2(0.0f, 5.0f); if (segT.position.x < TANK_LEFT + 5.0f) segRb.pendingImpulseForce += vec2(2.0f, 0.0f); @@ -453,17 +453,17 @@ class AquariumScene : public Scene2D }); // Boids - updateFishBoids(delta, ecs); + updateFishBoids(delta, registry); } - void updateFishBoids(float delta, ECSManager& ecs) + void updateFishBoids(float delta, Registry& registry) { PROFILE_SCOPE("Boids"); - auto fishArray = ecs.getComponentArray(); - auto foodArray = ecs.getComponentArray(); - auto transformArray = ecs.getComponentArray(); - auto rbArray = ecs.getComponentArray(); + auto fishArray = registry.getComponentArray(); + auto foodArray = registry.getComponentArray(); + auto transformArray = registry.getComponentArray(); + auto rbArray = registry.getComponentArray(); const size_t fishCount = fishArray->getSize(); if (fishCount == 0) @@ -655,7 +655,7 @@ class AquariumScene : public Scene2D { fd.energy += 1.0f; foodArray->getDataFromEntity(fc.entity).eaten = true; - ecs.destroyEntity(fc.entity); + registry.destroyEntity(fc.entity); foodCache[fi] = foodCache.back(); foodCache.pop_back(); fi--; @@ -675,7 +675,7 @@ class AquariumScene : public Scene2D } } - void onEntityShapeCollision(ECSManager& ecs, ServiceProvider& services, + void onEntityShapeCollision(Registry& registry, ServiceProvider& services, WeirdEngine::EntityShapeCollisionEvent& event) override { if (std::rand() % 8 == 0) @@ -683,7 +683,7 @@ class AquariumScene : public Scene2D services.audio().playSound({0.015f, 150.0f + (std::rand() % 150), true, vec3(event.raw.position, 0.0f), 1}); } - auto eelArray = ecs.getComponentArray(); + auto eelArray = registry.getComponentArray(); for (size_t i = 0; i < eelArray->getSize(); i++) { auto& eel = eelArray->getDataAtIdx(i); @@ -694,14 +694,14 @@ class AquariumScene : public Scene2D } } - if (ecs.hasComponent(event.entity)) + if (registry.hasComponent(event.entity)) { - auto& jf = ecs.getComponent(event.entity); + auto& jf = registry.getComponent(event.entity); jf.direction = -jf.direction; } } - void onEntityCollision(ECSManager& ecs, ServiceProvider& services, + void onEntityCollision(Registry& registry, ServiceProvider& services, WeirdEngine::EntityCollisionEvent& event) override { Entity a = event.entityA; @@ -715,10 +715,10 @@ class AquariumScene : public Scene2D services.audio().playSound({0.01f, 300.0f + (std::rand() % 200), true, vec3(0.0f), 1}); } - if (ecs.hasComponent(a) && ecs.hasComponent(b)) + if (registry.hasComponent(a) && registry.hasComponent(b)) { - auto& fishA = ecs.getComponent(a); - auto& fishB = ecs.getComponent(b); + auto& fishA = registry.getComponent(a); + auto& fishB = registry.getComponent(b); if (fishA.energy >= 3.0f && fishB.energy >= 3.0f && fishA.mateCooldown <= 0.0f && fishB.mateCooldown <= 0.0f) @@ -728,26 +728,26 @@ class AquariumScene : public Scene2D fishA.mateCooldown = 5.0f; fishB.mateCooldown = 5.0f; - auto& tA = ecs.getComponent(a); - auto& tB = ecs.getComponent(b); - auto& dotA = ecs.getComponent(a); + auto& tA = registry.getComponent(a); + auto& tB = registry.getComponent(b); + auto& dotA = registry.getComponent(a); int numOffspring = 1 + (std::rand() % 4); for (int i = 0; i < numOffspring; i++) { - Entity baby = ecs.createEntity(); - auto& bt = ecs.addComponent(baby); + Entity baby = registry.createEntity(); + auto& bt = registry.addComponent(baby); float ox = static_cast(std::rand() % 100 - 50) * 0.03f; float oy = static_cast(std::rand() % 100 - 50) * 0.03f; bt.position = (tA.position + tB.position) * 0.5f + vec3(ox, oy, 0.0f); - ecs.setComponentDirty(bt); + registry.setComponentDirty(bt); - auto& bd = ecs.addComponent(baby); + auto& bd = registry.addComponent(baby); bd.materialId = static_cast(dotA.materialId); - auto& brb = ecs.addComponent(baby); + auto& brb = registry.addComponent(baby); - auto& bFish = ecs.addComponent(baby); + auto& bFish = registry.addComponent(baby); float angle = static_cast(std::rand() % 628) * 0.01f; bFish.velocity = vec2(std::cos(angle), std::sin(angle)) * 3.0f; bFish.maxSpeed = fishA.maxSpeed; diff --git a/examples/sample-scenes/include/CollisionHandling.h b/examples/sample-scenes/include/CollisionHandling.h index d7264d4..3c26258 100644 --- a/examples/sample-scenes/include/CollisionHandling.h +++ b/examples/sample-scenes/include/CollisionHandling.h @@ -12,7 +12,7 @@ class CollisionHandlingScene : public Scene2D private: // Inherited via Scene - void onStart(ECSManager& ecs, ServiceProvider& services) override + void onStart(Registry& registry, ServiceProvider& services) override { services.debug().setDebugInput(true); services.debug().setDebugFly(true); @@ -28,14 +28,14 @@ class CollisionHandlingScene : public Scene2D float z = 0; - Entity entity = ecs.createEntity(); - Transform& t = ecs.addComponent(entity); + Entity entity = registry.createEntity(); + Transform& t = registry.addComponent(entity); t.position = vec3(x + 0.5f, y + 0.5f, z); - Dot& dot = ecs.addComponent(entity); + Dot& dot = registry.addComponent(entity); dot.materialId = material; - RigidBody2D& rb = ecs.addComponent(entity); + RigidBody2D& rb = registry.addComponent(entity); } // Floor @@ -47,7 +47,7 @@ class CollisionHandlingScene : public Scene2D { float variables[8]{15.0f, -50.0f, 250.0f, 50.0f}; auto floor = services.shapes().addShape(DefaultShapes::BOX, variables, 3, CombinationType::SmoothAddition); - ecs.getComponent(floor).smoothFactor = 3.0f; + registry.getComponent(floor).smoothFactor = 3.0f; } { @@ -55,11 +55,11 @@ class CollisionHandlingScene : public Scene2D services.shapes().addShape(DefaultShapes::CIRCLE, variables, 3, CombinationType::Subtraction); } - ecs.getComponent(services.render().getCameraEntity()).position = g_cameraPositon; + registry.getComponent(services.render().getCameraEntity()).position = g_cameraPositon; } float m_currentTime = 0.0f; - void onUpdate(ECSManager& ecs, ServiceProvider& services) override + void onUpdate(Registry& registry, ServiceProvider& services) override { m_currentTime = services.time().time(); if (services.input().getKeyDown(Input::Q) || services.input().getGamepadButtonDown(Input::GamepadButton::North)) @@ -81,8 +81,8 @@ class CollisionHandlingScene : public Scene2D simulation.setPosition(event.bodyB, vec2(15.0f, 24.45f)); simulation.addImpulseForce(event.bodyB, vec2(20.0f * sinf(10.0f * t), -30.0f)); - // Entity a = ecs.getComponentArray()->getDataAtIdx(event.bodyA).Owner; - // Transform &at = ecs.getComponent(a); + // Entity a = registry.getComponentArray()->getDataAtIdx(event.bodyA).Owner; + // Transform &at = registry.getComponent(a); // at.position.x = 15.0f + sinf(event.bodyB * 123.4565f + t); // at.position.y = 5.0f + (event.bodyA % 10) * 2.5f; // at.isDirty = true; diff --git a/examples/sample-scenes/include/DestroyScene.h b/examples/sample-scenes/include/DestroyScene.h index 23d28ef..4e51787 100644 --- a/examples/sample-scenes/include/DestroyScene.h +++ b/examples/sample-scenes/include/DestroyScene.h @@ -24,18 +24,18 @@ class DestroyScene : public Scene2D float m_timer = 0.0f; // Inherited via Scene - void onStart(ECSManager& ecs, ServiceProvider& services) override + void onStart(Registry& registry, ServiceProvider& services) override { services.debug().setDebugInput(true); services.debug().setDebugFly(true); - ecs.getComponent(services.render().getCameraEntity()).position = g_cameraPositon; + registry.getComponent(services.render().getCameraEntity()).position = g_cameraPositon; } - void onUpdate(ECSManager& ecs, ServiceProvider& services) override + void onUpdate(Registry& registry, ServiceProvider& services) override { float delta = services.time().deltaTime(); - g_cameraPositon = ecs.getComponent(services.render().getCameraEntity()).position; + g_cameraPositon = registry.getComponent(services.render().getCameraEntity()).position; if (services.input().getKeyDown(Input::Q) || services.input().getGamepadButtonDown(Input::GamepadButton::North)) { @@ -59,13 +59,13 @@ class DestroyScene : public Scene2D { for (int j = 0; j < 10; ++j) { - Entity e = ecs.createEntity(); - auto& t = ecs.addComponent(e); + Entity e = registry.createEntity(); + auto& t = registry.addComponent(e); t.position = vec3((std::rand() % 200) - 100.0f, (std::rand() % 100) - 50.0f, 0.0f); - ecs.setComponentDirty(t); - auto& ui = ecs.addComponent(e); + registry.setComponentDirty(t); + auto& ui = registry.addComponent(e); ui.materialId = 4 + (e % 12); - auto& rb = ecs.addComponent(e); + auto& rb = registry.addComponent(e); m_testBalls.push_back(e); } } @@ -95,17 +95,18 @@ class DestroyScene : public Scene2D int idx2 = std::rand() % m_testBalls.size(); if (idx1 != idx2) { - Entity constraintEnt = ecs.createEntity(); + Entity constraintEnt = registry.createEntity(); if (std::rand() % 2 == 0) { - auto& constraint = ecs.addComponent(constraintEnt); + auto& constraint = + registry.addComponent(constraintEnt); constraint.entityA = m_testBalls[idx1]; constraint.entityB = m_testBalls[idx2]; constraint.distance = 3.0f + (std::rand() % 5); } else { - auto& spring = ecs.addComponent(constraintEnt); + auto& spring = registry.addComponent(constraintEnt); spring.entityA = m_testBalls[idx1]; spring.entityB = m_testBalls[idx2]; spring.restDistance = 3.0f + (std::rand() % 5); @@ -121,7 +122,7 @@ class DestroyScene : public Scene2D if (!m_testShapes.empty()) { int idx = std::rand() % m_testShapes.size(); - ecs.destroyEntity(m_testShapes[idx]); + registry.destroyEntity(m_testShapes[idx]); m_testShapes[idx] = m_testShapes.back(); m_testShapes.pop_back(); } @@ -132,7 +133,7 @@ class DestroyScene : public Scene2D if (!m_testBalls.empty()) { int idx = std::rand() % m_testBalls.size(); - ecs.destroyEntity(m_testBalls[idx]); + registry.destroyEntity(m_testBalls[idx]); m_testBalls[idx] = m_testBalls.back(); m_testBalls.pop_back(); } @@ -143,7 +144,7 @@ class DestroyScene : public Scene2D if (!m_testConstraints.empty()) { int idx = std::rand() % m_testConstraints.size(); - ecs.destroyEntity(m_testConstraints[idx]); + registry.destroyEntity(m_testConstraints[idx]); m_testConstraints[idx] = m_testConstraints.back(); m_testConstraints.pop_back(); } @@ -154,7 +155,7 @@ class DestroyScene : public Scene2D } } - void onEntityCollision(ECSManager& ecs, ServiceProvider& services, + void onEntityCollision(Registry& registry, ServiceProvider& services, WeirdEngine::EntityCollisionEvent& event) override { if (std::rand() % 5 != 0) @@ -164,25 +165,25 @@ class DestroyScene : public Scene2D if (a != INVALID_ENTITY) { - if (!ecs.hasComponent(a)) - ecs.addComponent(a); - ecs.getComponent(a).collisionCount++; + if (!registry.hasComponent(a)) + registry.addComponent(a); + registry.getComponent(a).collisionCount++; } services.audio().playSound({0.02f, 400.0f + (std::rand() % 200), false, vec3(0.0f), 1}); } - void onEntityShapeCollision(ECSManager& ecs, ServiceProvider& services, + void onEntityShapeCollision(Registry& registry, ServiceProvider& services, WeirdEngine::EntityShapeCollisionEvent& event) override { if (std::rand() % 20 == 0) { Entity e = event.entity; - if (e != INVALID_ENTITY && ecs.hasComponent(e)) + if (e != INVALID_ENTITY && registry.hasComponent(e)) { - auto& rb = ecs.getComponent(e); + auto& rb = registry.getComponent(e); rb.isFixed = true; - ecs.setComponentDirty(rb); + registry.setComponentDirty(rb); } } @@ -190,9 +191,9 @@ class DestroyScene : public Scene2D { int idx = std::rand() % m_testConstraints.size(); Entity constraint = m_testConstraints[idx]; - if (ecs.hasComponent(constraint)) + if (registry.hasComponent(constraint)) { - auto& spring = ecs.getComponent(constraint); + auto& spring = registry.getComponent(constraint); spring.restDistance = 1.0f + (std::rand() % 10); } } diff --git a/examples/sample-scenes/include/ImageScene.h b/examples/sample-scenes/include/ImageScene.h index e3843df..4c83c82 100644 --- a/examples/sample-scenes/include/ImageScene.h +++ b/examples/sample-scenes/include/ImageScene.h @@ -18,7 +18,7 @@ class ImageScene : public Scene2D std::string imagePath; // Inherited via Scene - void onStart(ECSManager& ecs, ServiceProvider& services) override + void onStart(Registry& registry, ServiceProvider& services) override { services.debug().setDebugInput(true); services.debug().setDebugFly(true); @@ -61,14 +61,14 @@ class ImageScene : public Scene2D material = (materialId.size() > 0 && materialId.size() <= 2) ? std::stoi(materialId) : 0; - Entity entity = ecs.createEntity(); - Transform& t = ecs.addComponent(entity); + Entity entity = registry.createEntity(); + Transform& t = registry.addComponent(entity); t.position = vec3(x + 0.5f, y + 0.5f, 0); - Dot& dot = ecs.addComponent(entity); + Dot& dot = registry.addComponent(entity); dot.materialId = material; - RigidBody2D& rb = ecs.addComponent(entity); + RigidBody2D& rb = registry.addComponent(entity); } // Floor @@ -89,7 +89,7 @@ class ImageScene : public Scene2D services.shapes().addShape(DefaultShapes::BOX, variables, 3); } - ecs.getComponent(services.render().getCameraEntity()).position = g_cameraPositon; + registry.getComponent(services.render().getCameraEntity()).position = g_cameraPositon; } vec3 getColor(const char* path, float x, float y) @@ -168,7 +168,7 @@ class ImageScene : public Scene2D return closestIndex; } - void onUpdate(ECSManager& ecs, ServiceProvider& services) override + void onUpdate(Registry& registry, ServiceProvider& services) override { if (services.input().getKeyDown(Input::Q) || services.input().getGamepadButtonDown(Input::GamepadButton::North)) { @@ -178,7 +178,7 @@ class ImageScene : public Scene2D // Get colors if (services.input().getKeyDown(Input::P)) { - auto components = ecs.getComponentArray(); + auto components = registry.getComponentArray(); // Result string std::string result; @@ -187,7 +187,7 @@ class ImageScene : public Scene2D { RigidBody2D& rb = components->getDataAtIdx(i); Entity rbOwner = components->getEntityAtIdx(i); - Transform& t = ecs.getComponent(rbOwner); + Transform& t = registry.getComponent(rbOwner); int x = static_cast(floor(t.position.x)); int y = static_cast(floor(30.0f - t.position.y)); diff --git a/examples/sample-scenes/include/LifeScene.h b/examples/sample-scenes/include/LifeScene.h index 23b12cf..bc0c5fc 100644 --- a/examples/sample-scenes/include/LifeScene.h +++ b/examples/sample-scenes/include/LifeScene.h @@ -27,16 +27,16 @@ class LifeScene : public Scene2D private: // Inherited via Scene - void onStart(ECSManager& ecs, ServiceProvider& services) override + void onStart(Registry& registry, ServiceProvider& services) override { services.debug().setDebugInput(true); services.debug().setDebugFly(true); - Entity globalSettingsEnt = ecs.createEntity(); - auto& settings = ecs.addComponent(globalSettingsEnt); + Entity globalSettingsEnt = registry.createEntity(); + auto& settings = registry.addComponent(globalSettingsEnt); settings.gravity = 0.0f; settings.damping = 0.1f; - ecs.setComponentDirty(settings); + registry.setComponentDirty(settings); const std::filesystem::path organismsDir(services.resources().assetPath("Organisms")); { @@ -51,32 +51,32 @@ class LifeScene : public Scene2D for (size_t j = 0; j < 3; j++) { - Entity firstCreated = static_cast(ecs.getEntityCount()); + Entity firstCreated = static_cast(registry.getEntityCount()); auto tags = services.serialization().loadWeirdFile(entry.path().string()); - Entity lastCreated = static_cast(ecs.getEntityCount()); + Entity lastCreated = static_cast(registry.getEntityCount()); for (Entity e = 0; e < (lastCreated - firstCreated); e++) { - if (!ecs.hasComponent(firstCreated + e)) + if (!registry.hasComponent(firstCreated + e)) { continue; } - auto& t = ecs.getComponent(firstCreated + e); + auto& t = registry.getComponent(firstCreated + e); t.position += vec3(-10.0f + (float)(i * 10), -10.0f + (float)(j * 10), 0.0f); } if (tags.contains("head")) { Entity headEntity = tags["head"]; - ecs.addComponent(headEntity); + registry.addComponent(headEntity); } else { - auto& a = ecs.getComponent(firstCreated); - ecs.addComponent(firstCreated); + auto& a = registry.getComponent(firstCreated); + registry.addComponent(firstCreated); } // break; @@ -86,34 +86,34 @@ class LifeScene : public Scene2D } } - ecs.getComponent(services.render().getCameraEntity()).position = g_cameraPositon; + registry.getComponent(services.render().getCameraEntity()).position = g_cameraPositon; } - void onUpdate(ECSManager& ecs, ServiceProvider& services) override + void onUpdate(Registry& registry, ServiceProvider& services) override { float delta = services.time().deltaTime(); - g_cameraPositon = ecs.getComponent(services.render().getCameraEntity()).position; + g_cameraPositon = registry.getComponent(services.render().getCameraEntity()).position; if (services.input().getKeyDown(Input::Q) || services.input().getGamepadButtonDown(Input::GamepadButton::North)) { services.sceneControl().goToNextScene(); } - updateHeads(delta, ecs, services); + updateHeads(delta, registry, services); } - void updateHeads(float delta, ECSManager& ecs, ServiceProvider& services) + void updateHeads(float delta, Registry& registry, ServiceProvider& services) { float animationT = std::sin(services.time().time() * 10.0f) * 0.5f + 0.25f; - auto headArray = ecs.getComponentArray(); + auto headArray = registry.getComponentArray(); for (size_t i = 0; i < headArray->getSize(); i++) { auto& head = headArray->getDataAtIdx(i); Entity headEntity = headArray->getEntityAtIdx(i); - auto& rb = ecs.getComponent(headEntity); + auto& rb = registry.getComponent(headEntity); rb.pendingContinuousForce += head.forceMagnitude * head.direction * animationT; if (animationT < 0.0f && !head.directionChanged) @@ -133,7 +133,7 @@ class LifeScene : public Scene2D head.directionChanged = false; } - vec2 positon = vec2(ecs.getComponent(headEntity).position); + vec2 positon = vec2(registry.getComponent(headEntity).position); if (length(positon) > 50.0f) { head.direction = -normalize(positon); diff --git a/examples/sample-scenes/include/MouseCollisionScene.h b/examples/sample-scenes/include/MouseCollisionScene.h index ec6edd9..900772b 100644 --- a/examples/sample-scenes/include/MouseCollisionScene.h +++ b/examples/sample-scenes/include/MouseCollisionScene.h @@ -20,7 +20,7 @@ class MouseCollisionScene : public Scene2D Entity m_cursorShape; // Inherited via Scene - void onStart(ECSManager& ecs, ServiceProvider& services) override + void onStart(Registry& registry, ServiceProvider& services) override { services.debug().setDebugInput(true); services.debug().setDebugFly(true); @@ -35,19 +35,19 @@ class MouseCollisionScene : public Scene2D float z = 0; - Entity entity = ecs.createEntity(); - Transform& t = ecs.addComponent(entity); + Entity entity = registry.createEntity(); + Transform& t = registry.addComponent(entity); t.position = vec3(x + 0.5f, y + 0.5f, z); if (i < 100) { } - Dot& dot = ecs.addComponent(entity); + Dot& dot = registry.addComponent(entity); dot.materialId = 0; - RigidBody2D& rb = ecs.addComponent(entity); - CollisionCounter& counter = ecs.addComponent(entity); + RigidBody2D& rb = registry.addComponent(entity); + CollisionCounter& counter = registry.addComponent(entity); } // Floor @@ -75,12 +75,12 @@ class MouseCollisionScene : public Scene2D m_cursorShape = star; } - ecs.getComponent(services.render().getCameraEntity()).position = g_cameraPositon; + registry.getComponent(services.render().getCameraEntity()).position = g_cameraPositon; } - void onUpdate(ECSManager& ecs, ServiceProvider& services) override + void onUpdate(Registry& registry, ServiceProvider& services) override { - g_cameraPositon = ecs.getComponent(services.render().getCameraEntity()).position; + g_cameraPositon = registry.getComponent(services.render().getCameraEntity()).position; if (services.input().getKeyDown(Input::Q) || services.input().getGamepadButtonDown(Input::GamepadButton::North)) { @@ -89,8 +89,8 @@ class MouseCollisionScene : public Scene2D // Move wall to mouse { - CustomShape& cs = ecs.getComponent(m_cursorShape); - auto& cameraTransform = ecs.getComponent(services.render().getCameraEntity()); + CustomShape& cs = registry.getComponent(m_cursorShape); + auto& cameraTransform = registry.getComponent(services.render().getCameraEntity()); float x = services.input().getMouseX(); float y = services.input().getMouseY(); @@ -99,22 +99,22 @@ class MouseCollisionScene : public Scene2D cs.parameters[0] = mousePositionInWorld.x; cs.parameters[1] = mousePositionInWorld.y; - ecs.setComponentDirty(cs); + registry.setComponentDirty(cs); } } - void onEntityCollision(ECSManager& ecs, ServiceProvider& services, + void onEntityCollision(Registry& registry, ServiceProvider& services, WeirdEngine::EntityCollisionEvent& event) override { - if (ecs.hasComponent(event.entityA)) + if (registry.hasComponent(event.entityA)) { - auto& counter = ecs.getComponent(event.entityA); + auto& counter = registry.getComponent(event.entityA); counter.count++; constexpr int COLLISIONS_PER_MATERIAL = 50; if (counter.count <= 10 * COLLISIONS_PER_MATERIAL && counter.count % COLLISIONS_PER_MATERIAL == 0) { - auto& dot = ecs.getComponent(event.entityA); + auto& dot = registry.getComponent(event.entityA); dot.materialId++; if (counter.count == 10 * COLLISIONS_PER_MATERIAL) @@ -122,15 +122,15 @@ class MouseCollisionScene : public Scene2D } } - if (ecs.hasComponent(event.entityB)) + if (registry.hasComponent(event.entityB)) { - auto& counter = ecs.getComponent(event.entityB); + auto& counter = registry.getComponent(event.entityB); counter.count++; constexpr int COLLISIONS_PER_MATERIAL = 50; if (counter.count <= 10 * COLLISIONS_PER_MATERIAL && counter.count % COLLISIONS_PER_MATERIAL == 0) { - auto& dot = ecs.getComponent(event.entityB); + auto& dot = registry.getComponent(event.entityB); dot.materialId++; if (counter.count == 10 * COLLISIONS_PER_MATERIAL) @@ -139,14 +139,14 @@ class MouseCollisionScene : public Scene2D } } - void onEntityShapeCollision(ECSManager& ecs, ServiceProvider& services, + void onEntityShapeCollision(Registry& registry, ServiceProvider& services, WeirdEngine::EntityShapeCollisionEvent& event) override { - if (ecs.hasComponent(event.entity)) + if (registry.hasComponent(event.entity)) { - auto& counter = ecs.getComponent(event.entity); + auto& counter = registry.getComponent(event.entity); counter.count = 0; - auto& dot = ecs.getComponent(event.entity); + auto& dot = registry.getComponent(event.entity); dot.materialId = 0; } } diff --git a/examples/sample-scenes/include/RopeScene.h b/examples/sample-scenes/include/RopeScene.h index b111f03..81f12d3 100644 --- a/examples/sample-scenes/include/RopeScene.h +++ b/examples/sample-scenes/include/RopeScene.h @@ -18,7 +18,7 @@ class RopeScene : public Scene2D std::vector m_balls; - void onStart(ECSManager& ecs, ServiceProvider& services) override + void onStart(Registry& registry, ServiceProvider& services) override { services.debug().setDebugInput(true); services.debug().setDebugFly(true); @@ -35,15 +35,15 @@ class RopeScene : public Scene2D float y = startY - static_cast(i / rowWidth); int material = 4 + (i % 12); - Entity entity = ecs.createEntity(); + Entity entity = registry.createEntity(); - auto& t = ecs.addComponent(entity); + auto& t = registry.addComponent(entity); t.position = vec3(x + 0.5f, y + 0.5f, 0.0f); - auto& sdf = ecs.addComponent(entity); + auto& sdf = registry.addComponent(entity); sdf.materialId = material; - auto& rb = ecs.addComponent(entity); + auto& rb = registry.addComponent(entity); m_balls.push_back(entity); } @@ -57,8 +57,8 @@ class RopeScene : public Scene2D // Structural Springs (Down and Right) if (hasRowBelow) // Down { - Entity springEnt = ecs.createEntity(); - auto& spring = ecs.addComponent(springEnt); + Entity springEnt = registry.createEntity(); + auto& spring = registry.addComponent(springEnt); spring.entityA = m_balls[i]; spring.entityB = m_balls[i + rowWidth]; spring.stiffness = stiffness; @@ -67,8 +67,8 @@ class RopeScene : public Scene2D if (notRightEdge) // Right { - Entity springEnt = ecs.createEntity(); - auto& spring = ecs.addComponent(springEnt); + Entity springEnt = registry.createEntity(); + auto& spring = registry.addComponent(springEnt); spring.entityA = m_balls[i]; spring.entityB = m_balls[i + 1]; spring.stiffness = stiffness; @@ -78,8 +78,8 @@ class RopeScene : public Scene2D // Shear Springs (Diagonal) if (hasRowBelow && notRightEdge) // Bottom-Right { - Entity springEnt = ecs.createEntity(); - auto& spring = ecs.addComponent(springEnt); + Entity springEnt = registry.createEntity(); + auto& spring = registry.addComponent(springEnt); spring.entityA = m_balls[i]; spring.entityB = m_balls[i + rowWidth + 1]; spring.stiffness = stiffness; @@ -88,8 +88,8 @@ class RopeScene : public Scene2D if (hasRowBelow && notLeftEdge) // Bottom-Left { - Entity springEnt = ecs.createEntity(); - auto& spring = ecs.addComponent(springEnt); + Entity springEnt = registry.createEntity(); + auto& spring = registry.addComponent(springEnt); spring.entityA = m_balls[i]; spring.entityB = m_balls[i + rowWidth - 1]; spring.stiffness = stiffness; @@ -98,16 +98,16 @@ class RopeScene : public Scene2D } // Fix corners - ecs.getComponent(m_balls[0]).isFixed = true; - ecs.setEntityDirty(m_balls[0], true); + registry.getComponent(m_balls[0]).isFixed = true; + registry.setEntityDirty(m_balls[0], true); if (numBalls > rowWidth) { - ecs.getComponent(m_balls[rowWidth - 1]).isFixed = true; - ecs.setEntityDirty(m_balls[rowWidth - 1], true); - ecs.getComponent(m_balls[rowWidth]).isFixed = true; - ecs.setEntityDirty(m_balls[rowWidth], true); - ecs.getComponent(m_balls[(2 * rowWidth) - 1]).isFixed = true; - ecs.setEntityDirty(m_balls[(2 * rowWidth) - 1], true); + registry.getComponent(m_balls[rowWidth - 1]).isFixed = true; + registry.setEntityDirty(m_balls[rowWidth - 1], true); + registry.getComponent(m_balls[rowWidth]).isFixed = true; + registry.setEntityDirty(m_balls[rowWidth], true); + registry.getComponent(m_balls[(2 * rowWidth) - 1]).isFixed = true; + registry.setEntityDirty(m_balls[(2 * rowWidth) - 1], true); } // Add base shapes (walls, ground, custom) @@ -120,10 +120,10 @@ class RopeScene : public Scene2D float vars3[8] = {15.0f, -98.0f, 15.0f, 100.0f}; services.shapes().addShape(DefaultShapes::BOX, vars3, 3, CombinationType::Addition); - ecs.getComponent(services.render().getCameraEntity()).position = g_cameraPositon; + registry.getComponent(services.render().getCameraEntity()).position = g_cameraPositon; } - void throwBalls(ECSManager& ecs, ServiceProvider& services) + void throwBalls(Registry& registry, ServiceProvider& services) { if (services.time().time() <= m_lastSpawnTime + 0.1) { @@ -135,25 +135,25 @@ class RopeScene : public Scene2D { float y = 60.0f + (1.2f * i); - Entity entity = ecs.createEntity(); + Entity entity = registry.createEntity(); - auto& t = ecs.addComponent(entity); + auto& t = registry.addComponent(entity); t.position = vec3(0.5f, y + 0.5f, 0.0f); - auto& sdf = ecs.addComponent(entity); - sdf.materialId = 4 + ecs.getComponentArray()->getSize() % 12; + auto& sdf = registry.addComponent(entity); + sdf.materialId = 4 + registry.getComponentArray()->getSize() % 12; - auto& rb = ecs.addComponent(entity); + auto& rb = registry.addComponent(entity); rb.pendingImpulseForce += vec2(20.0f, 0.0f); } m_lastSpawnTime = services.time().time(); } - void onUpdate(ECSManager& ecs, ServiceProvider& services) override + void onUpdate(Registry& registry, ServiceProvider& services) override { float delta = services.time().deltaTime(); - g_cameraPositon = ecs.getComponent(services.render().getCameraEntity()).position; + g_cameraPositon = registry.getComponent(services.render().getCameraEntity()).position; if (services.input().getKeyDown(Input::Q) || services.input().getGamepadButtonDown(Input::GamepadButton::North)) { @@ -167,22 +167,22 @@ class RopeScene : public Scene2D // it, or track delta. static float animTime = 0.0f; animTime += delta; - auto& cs = ecs.getComponent(m_star); + auto& cs = registry.getComponent(m_star); cs.parameters[4] = static_cast((static_cast(std::floor(animTime)) % 5) + 2); cs.parameters[3] = std::sin(3.1416f * animTime); - ecs.setComponentDirty(cs); + registry.setComponentDirty(cs); } if (services.input().getKey(Input::E) || services.input().getGamepadButton(Input::GamepadButton::West)) { - throwBalls(ecs, services); + throwBalls(registry, services); } static vec2 boxStart; static bool createBoxInUI = true; if (services.input().getKeyDown(Input::M)) { - auto& cam = ecs.getComponent(services.render().getCameraEntity()); + auto& cam = registry.getComponent(services.render().getCameraEntity()); vec2 screen = {services.input().getMouseX(), services.input().getMouseY()}; if (createBoxInUI) @@ -197,7 +197,7 @@ class RopeScene : public Scene2D } else if (services.input().getKeyUp(Input::M)) { - auto& cam = ecs.getComponent(services.render().getCameraEntity()); + auto& cam = registry.getComponent(services.render().getCameraEntity()); vec2 screen = {services.input().getMouseX(), services.input().getMouseY()}; vec2 world = ECS::Camera::screenPositionToWorldPosition2D(cam, screen); @@ -221,13 +221,13 @@ class RopeScene : public Scene2D services.shapes().addUIShape(DefaultShapes::BOX, vars, 7, CombinationType::SmoothAddition); else services.shapes().addShape( - DefaultShapes::BOX, vars, 4 + ecs.getComponentArray()->getSize() % 12, - CombinationType::SmoothAddition, true, ecs.getComponentArray()->getSize()); + DefaultShapes::BOX, vars, 4 + registry.getComponentArray()->getSize() % 12, + CombinationType::SmoothAddition, true, registry.getComponentArray()->getSize()); } if (services.input().getKeyDown(Input::N)) { - auto& cam = ecs.getComponent(services.render().getCameraEntity()); + auto& cam = registry.getComponent(services.render().getCameraEntity()); vec2 screen = {services.input().getMouseX(), services.input().getMouseY()}; vec2 world = ECS::Camera::screenPositionToWorldPosition2D(cam, screen); @@ -237,7 +237,7 @@ class RopeScene : public Scene2D if (services.input().getKey(Input::R) || services.input().getGamepadButton(Input::GamepadButton::South)) { - ecs.forEach( + registry.forEach( [&](Entity e, RigidBody2D& rb, Transform& t) { vec2 force(0, -0.001f * (t.position.y * t.position.y)); diff --git a/examples/sample-scenes/include/ServiceShowcaseScene.h b/examples/sample-scenes/include/ServiceShowcaseScene.h index 39a42c7..9ea852a 100644 --- a/examples/sample-scenes/include/ServiceShowcaseScene.h +++ b/examples/sample-scenes/include/ServiceShowcaseScene.h @@ -20,13 +20,13 @@ using namespace WeirdEngine; // thin wrappers that delegate to a plain free function (a "system") of the // form: // -// void system(ECSManager& ecs, ServiceProvider& services, ...); +// void system(Registry& registry, ServiceProvider& services, ...); // // (onRender and the physics-thread callbacks are inlined in the scene // instead, see below: the dispatcher is deliberately not involved there.) // // Systems never touch Scene internals: everything they need is either on the -// ECSManager& or on the ServiceProvider& passed to the callback. Even the +// Registry& or on the ServiceProvider& passed to the callback. Even the // scene's own state lives in the ECS (see State below): a single "state" // entity owns it, and systems reach it through the component array. // @@ -93,23 +93,23 @@ namespace ServiceShowcase // State entity lookup: there is exactly one State component in the scene // (created by onCreateSystem), so it always lives at index 0 of the State // component array. - inline State& getState(ECSManager& ecs, ServiceProvider& services) + inline State& getState(Registry& registry, ServiceProvider& services) { - return ecs.getComponentArray()->getDataAtIdx(0); + return registry.getComponentArray()->getDataAtIdx(0); } - inline Entity spawnBall(ECSManager& ecs, vec2 position) + inline Entity spawnBall(Registry& registry, vec2 position) { - Entity entity = ecs.createEntity(); - auto& t = ecs.addComponent(entity); + Entity entity = registry.createEntity(); + auto& t = registry.addComponent(entity); t.position = vec3(position, 0.0f); - auto& dot = ecs.addComponent(entity); + auto& dot = registry.addComponent(entity); dot.materialId = DisplaySettings::LightGray; - auto& rb = ecs.addComponent(entity); + auto& rb = registry.addComponent(entity); rb.velocity = vec2((std::rand() % 200 - 100) / 40.0f, 0.0f); - ecs.setComponentDirty(rb); + registry.setComponentDirty(rb); return entity; } @@ -118,22 +118,22 @@ namespace ServiceShowcase // Runs after the ECS, materials and camera exist, before any scene file is // loaded and before onStart. Creates the "state" entity that owns the // scene's State component. - inline void onCreateSystem(ECSManager& ecs, ServiceProvider& services) + inline void onCreateSystem(Registry& registry, ServiceProvider& services) { - Entity stateEntity = ecs.createEntity(); - ecs.addComponent(stateEntity); + Entity stateEntity = registry.createEntity(); + registry.addComponent(stateEntity); services.tags().tag(stateEntity, "state"); services.serialization().blacklistEntity(stateEntity); - State& state = getState(ecs, services); + State& state = getState(registry, services); state.initialTime = services.time().time(); std::cout << "[ServiceShowcase] onCreate at simulation time " << state.initialTime << "s" << std::endl; } // ----------------------------------------------------------------- onStart - inline void onStartSystem(ECSManager& ecs, ServiceProvider& services) + inline void onStartSystem(Registry& registry, ServiceProvider& services) { - State& state = getState(ecs, services); + State& state = getState(registry, services); // Debug flags through the provider services.debug().setDebugFly(true); @@ -165,7 +165,7 @@ namespace ServiceShowcase float vars[8] = {15.0f, 20.0f, 5.0f, 4.0f}; Entity ringEntity = services.shapes().addShape(ringShape, vars, ringMaterial, CombinationType::Addition, true, 0); - ecs.getComponent(ringEntity).smoothFactor = 2.0f; + registry.getComponent(ringEntity).smoothFactor = 2.0f; } // Floor @@ -174,7 +174,7 @@ namespace ServiceShowcase Entity floor = services.shapes().addShape(DefaultShapes::BOX, vars, floorMaterial, CombinationType::SmoothAddition); services.tags().tag(floor, "floor"); - ecs.getComponent(floor).smoothFactor = 3.0f; + registry.getComponent(floor).smoothFactor = 3.0f; } // Pit: a subtraction shape; balls that roll into it fall through @@ -185,19 +185,19 @@ namespace ServiceShowcase } // Camera - ecs.getComponent(services.render().getCameraEntity()).position = g_cameraPositon; + registry.getComponent(services.render().getCameraEntity()).position = g_cameraPositon; // Leader ball: orbits a point (moved by FollowSystem through the // physics simulation) { - Entity leader = ecs.createEntity(); - auto& t = ecs.addComponent(leader); + Entity leader = registry.createEntity(); + auto& t = registry.addComponent(leader); t.position = vec3(15.0f, 12.0f, 0.0f); - auto& dot = ecs.addComponent(leader); + auto& dot = registry.addComponent(leader); dot.materialId = DisplaySettings::Yellow; - ecs.addComponent(leader); + registry.addComponent(leader); services.tags().tag(leader, "leader"); } @@ -207,14 +207,14 @@ namespace ServiceShowcase // to the simulation; the callbacks reach it through the simulation id // stored in the RigidBody2D component. { - Entity character = ecs.createEntity(); - auto& t = ecs.addComponent(character); + Entity character = registry.createEntity(); + auto& t = registry.addComponent(character); t.position = vec3(15.0f, 15.0f, 0.0f); - auto& dot = ecs.addComponent(character); + auto& dot = registry.addComponent(character); dot.materialId = DisplaySettings::Blue; - auto& rb = ecs.addComponent(character); + auto& rb = registry.addComponent(character); services.tags().tag(character, "character"); // Configure the data before handing ownership to the simulation; @@ -234,20 +234,20 @@ namespace ServiceShowcase { float x = 8.0f + (i % 4) * 3.0f; float y = 28.0f + (i / 4) * 4.0f; - spawnBall(ecs, vec2(x, y)); + spawnBall(registry, vec2(x, y)); } // UI text (screen space; blacklisted so it is never serialized) { auto makeText = [&](const char* initial, vec2 screenPosition, Entity& outEntity, int material) { - outEntity = ecs.createEntity(); + outEntity = registry.createEntity(); services.serialization().blacklistEntity(outEntity); - auto& t = ecs.addComponent(outEntity); + auto& t = registry.addComponent(outEntity); t.position = vec3(screenPosition, 0.0f); - auto& text = ecs.addComponent(outEntity); + auto& text = registry.addComponent(outEntity); text.text = initial; text.material = material; text.horizontalAlignment = TextRenderer::HorizontalAlignment::Left; @@ -267,24 +267,24 @@ namespace ServiceShowcase // ----------------------------------------------------- update: spawn system // Periodically drops a new ball from the top of the world. - inline void spawnSystem(ECSManager& ecs, ServiceProvider& services) + inline void spawnSystem(Registry& registry, ServiceProvider& services) { - State& state = getState(ecs, services); + State& state = getState(registry, services); state.spawnTimer += services.time().deltaTime(); - if (state.spawnTimer > 0.35f && ecs.getEntityCount() < 160) + if (state.spawnTimer > 0.35f && registry.getEntityCount() < 160) { state.spawnTimer = 0.0f; float x = 3.0f + static_cast(std::rand() % 240) / 10.0f; - spawnBall(ecs, vec2(x, 35.0f)); + spawnBall(registry, vec2(x, 35.0f)); state.ballsSpawned++; } } // ----------------------------------------------------- update: input system - inline void inputSystem(ECSManager& ecs, ServiceProvider& services) + inline void inputSystem(Registry& registry, ServiceProvider& services) { - State& state = getState(ecs, services); + State& state = getState(registry, services); // Scene transition through the provider if (services.input().getKeyDown(Input::Q) || services.input().getGamepadButtonDown(Input::GamepadButton::North)) @@ -326,10 +326,10 @@ namespace ServiceShowcase // Spawn a ball where the mouse points if (services.input().getMouseButtonDown(Input::LeftClick) && !services.input().isUIClick()) { - auto& cameraTransform = ecs.getComponent(services.render().getCameraEntity()); + auto& cameraTransform = registry.getComponent(services.render().getCameraEntity()); vec2 mouseWorld = ECS::Camera::screenPositionToWorldPosition2D( cameraTransform, vec2(services.input().getMouseX(), services.input().getMouseY())); - spawnBall(ecs, mouseWorld); + spawnBall(registry, mouseWorld); state.ballsSpawned++; } @@ -353,9 +353,9 @@ namespace ServiceShowcase // ----------------------------------------------------- update: follow system // Orbits the "leader" ball around a point by writing its velocity straight // into the physics simulation through the provider. - inline void followSystem(ECSManager& ecs, ServiceProvider& services) + inline void followSystem(Registry& registry, ServiceProvider& services) { - State& state = getState(ecs, services); + State& state = getState(registry, services); Entity leader = services.tags().getEntityByTag("leader"); if (leader == INVALID_ENTITY) @@ -366,56 +366,56 @@ namespace ServiceShowcase glm::vec2 center(15.0f, 18.0f); glm::vec2 target = center + 6.0f * glm::vec2(std::cos(state.leaderAngle), std::sin(state.leaderAngle)); - auto& rb = ecs.getComponent(leader); - glm::vec2 current = glm::vec2(ecs.getComponent(leader).position); + auto& rb = registry.getComponent(leader); + glm::vec2 current = glm::vec2(registry.getComponent(leader).position); rb.velocity = (target - current) * 2.0f; - ecs.setComponentDirty(rb); + registry.setComponentDirty(rb); } // -------------------------------------------------------- update: ui system - inline void uiSystem(ECSManager& ecs, ServiceProvider& services) + inline void uiSystem(Registry& registry, ServiceProvider& services) { - State& state = getState(ecs, services); + State& state = getState(registry, services); char buffer[64]; - auto& timeText = ecs.getComponent(state.timeText); + auto& timeText = registry.getComponent(state.timeText); std::snprintf(buffer, sizeof(buffer), "time %.1fs", services.time().time()); timeText.text = buffer; - ecs.setComponentDirty(timeText); + registry.setComponentDirty(timeText); - auto& entitiesText = ecs.getComponent(state.entitiesText); - std::snprintf(buffer, sizeof(buffer), "entities %d (balls spawned: %d)", services.ecs().getEntityCount(), + auto& entitiesText = registry.getComponent(state.entitiesText); + std::snprintf(buffer, sizeof(buffer), "entities %d (balls spawned: %d)", services.registry().getEntityCount(), state.ballsSpawned); entitiesText.text = buffer; - ecs.setComponentDirty(entitiesText); + registry.setComponentDirty(entitiesText); - auto& collisionsText = ecs.getComponent(state.collisionsText); + auto& collisionsText = registry.getComponent(state.collisionsText); std::snprintf(buffer, sizeof(buffer), "collisions %d body / %d shape", state.entityCollisions, state.shapeCollisions); collisionsText.text = buffer; - ecs.setComponentDirty(collisionsText); + registry.setComponentDirty(collisionsText); } // ------------------------------------------------------- onEntityCollision // Main thread. Body-body collisions mapped to entities: count them, flash // the colliding ball and play a sound through the provider. - inline void onEntityCollisionSystem(ECSManager& ecs, ServiceProvider& services, EntityCollisionEvent& event) + inline void onEntityCollisionSystem(Registry& registry, ServiceProvider& services, EntityCollisionEvent& event) { - State& state = getState(ecs, services); + State& state = getState(registry, services); state.entityCollisions++; // Flash the colliding ball orange, but keep the character's identity // color: it is identified through its per-body user data. - if (event.entityA != INVALID_ENTITY && ecs.hasComponent(event.entityA) && - ecs.hasComponent(event.entityA)) + if (event.entityA != INVALID_ENTITY && registry.hasComponent(event.entityA) && + registry.hasComponent(event.entityA)) { - RigidBody2D& rb = ecs.getComponent(event.entityA); + RigidBody2D& rb = registry.getComponent(event.entityA); if (services.physics().getUserDataAs(rb.simulationId) == nullptr) { - auto& dot = ecs.getComponent(event.entityA); + auto& dot = registry.getComponent(event.entityA); dot.materialId = DisplaySettings::Orange; - ecs.setComponentDirty(dot); + registry.setComponentDirty(dot); } } @@ -425,10 +425,10 @@ namespace ServiceShowcase // --------------------------------------------------- onEntityShapeCollision // Main thread. Shape collisions mapped to entities: count them and play a // spatial sound at the contact point. - inline void onEntityShapeCollisionSystem(ECSManager& ecs, ServiceProvider& services, + inline void onEntityShapeCollisionSystem(Registry& registry, ServiceProvider& services, EntityShapeCollisionEvent& event) { - State& state = getState(ecs, services); + State& state = getState(registry, services); state.shapeCollisions++; if (event.entity != INVALID_ENTITY) @@ -439,11 +439,11 @@ namespace ServiceShowcase } // ---------------------------------------------------------------- onDestroy - inline void onDestroySystem(ECSManager& ecs, ServiceProvider& services) + inline void onDestroySystem(Registry& registry, ServiceProvider& services) { std::cout << "[ServiceShowcase] scene destroyed at " << services.time().time() << "s" << std::endl; - State& state = getState(ecs, services); + State& state = getState(registry, services); state.ballsSpawned = 0; state.entityCollisions = 0; state.shapeCollisions = 0; @@ -470,12 +470,12 @@ class ServiceShowcaseScene : public Scene2D addDestroySystem(ServiceShowcase::onDestroySystem); addImGuiRenderSystem( - [](ECSManager& ecs, ServiceProvider& services) + [](Registry& registry, ServiceProvider& services) { - auto& state = ServiceShowcase::getState(ecs, services); + auto& state = ServiceShowcase::getState(registry, services); ImGui::Text("Time: %.2fs", services.time().time()); - ImGui::Text("Entities: %d", services.ecs().getEntityCount()); + ImGui::Text("Entities: %d", services.registry().getEntityCount()); ImGui::Text("Gravity: %.1f | Damping: %.2f | Physics %s", state.gravity, state.damping, services.physics().isPaused() ? "paused" : "running"); ImGui::Text("Collisions: %d body / %d shape", state.entityCollisions, state.shapeCollisions); @@ -489,7 +489,7 @@ class ServiceShowcaseScene : public Scene2D } private: - void onRender(ECSManager& ecs, ServiceProvider& services, WeirdRenderer::RenderTarget& renderTarget) override + void onRender(Registry& registry, ServiceProvider& services, WeirdRenderer::RenderTarget& renderTarget) override { static bool logged = false; if (!logged) diff --git a/examples/sample-scenes/include/ShapesCombinations.h b/examples/sample-scenes/include/ShapesCombinations.h index 6884e60..5d86291 100644 --- a/examples/sample-scenes/include/ShapesCombinations.h +++ b/examples/sample-scenes/include/ShapesCombinations.h @@ -20,7 +20,7 @@ class ShapeCombinatiosScene : public Scene2D std::vector m_uiPoints; - void onStart(ECSManager& ecs, ServiceProvider& services) override + void onStart(Registry& registry, ServiceProvider& services) override { services.debug().setDebugInput(true); services.debug().setDebugFly(true); @@ -77,30 +77,30 @@ class ShapeCombinatiosScene : public Scene2D for (int i = 0; i < 10; ++i) { - auto ee = ecs.createEntity(); - auto& t = ecs.addComponent(ee); + auto ee = registry.createEntity(); + auto& t = registry.addComponent(ee); t.position = vec3(15.0f, 15.0f, 10.0f); - auto& ui = ecs.addComponent(ee); + auto& ui = registry.addComponent(ee); ui.materialId = 4 + (i % 12); m_uiPoints.push_back(ee); } - ecs.getComponent(services.render().getCameraEntity()).position = g_cameraPositon; + registry.getComponent(services.render().getCameraEntity()).position = g_cameraPositon; } - void onUpdate(ECSManager& ecs, ServiceProvider& services) override + void onUpdate(Registry& registry, ServiceProvider& services) override { float delta = services.time().deltaTime(); - g_cameraPositon = ecs.getComponent(services.render().getCameraEntity()).position; + g_cameraPositon = registry.getComponent(services.render().getCameraEntity()).position; if (services.input().getKeyDown(Input::Q) || services.input().getGamepadButtonDown(Input::GamepadButton::North)) { services.sceneControl().goToNextScene(); } - auto& cameraTransform = ecs.getComponent(services.render().getCameraEntity()); + auto& cameraTransform = registry.getComponent(services.render().getCameraEntity()); float x = services.input().getMouseX(); float y = services.input().getMouseY(); @@ -136,12 +136,12 @@ class ShapeCombinatiosScene : public Scene2D } { - CustomShape& cs = ecs.getComponent(m_circle); + CustomShape& cs = registry.getComponent(m_circle); cs.parameters[0] = m_initialMousePositionInWorld.x; cs.parameters[1] = m_circleRadious <= 0.0f ? -1000.0f : m_initialMousePositionInWorld.y; cs.parameters[2] = m_circleRadious; - ecs.setComponentDirty(cs); + registry.setComponentDirty(cs); } float volume = AudioEngine::getInstance().getAudioData().currentVolume; @@ -159,7 +159,7 @@ class ShapeCombinatiosScene : public Scene2D float x = center.x + std::cos(angle) * radius; float y = center.y + std::sin(angle) * radius; - auto& t = ecs.getComponent(m_uiPoints[i]); + auto& t = registry.getComponent(m_uiPoints[i]); t.position = vec3(x, y, 0.0f); } } diff --git a/examples/sample-scenes/include/TextScene.h b/examples/sample-scenes/include/TextScene.h index f7560bf..6b98a7f 100644 --- a/examples/sample-scenes/include/TextScene.h +++ b/examples/sample-scenes/include/TextScene.h @@ -24,7 +24,7 @@ class TextScene : public Scene2D int m_counter = 0; int m_lastResolutionHash = 0; - void onStart(ECSManager& ecs, ServiceProvider& services) override + void onStart(Registry& registry, ServiceProvider& services) override { services.debug().setDebugInput(true); services.debug().setDebugFly(true); @@ -35,15 +35,15 @@ class TextScene : public Scene2D CombinationType::SmoothAddition); } - ecs.getComponent(services.render().getCameraEntity()).position = g_cameraPositon; + registry.getComponent(services.render().getCameraEntity()).position = g_cameraPositon; m_lastResolutionHash = Display::width + Display::height; { - m_worldText = ecs.createEntity(); - auto& t = ecs.addComponent(m_worldText); + m_worldText = registry.createEntity(); + auto& t = registry.addComponent(m_worldText); t.position = vec3(15.0f, 12.0f, 0.0f); - auto& text = ecs.addComponent(m_worldText); + auto& text = registry.addComponent(m_worldText); text.text = "WORLD TEXT"; text.material = DisplaySettings::Cyan; text.horizontalAlignment = TextRenderer::HorizontalAlignment::Center; @@ -51,11 +51,11 @@ class TextScene : public Scene2D } { - m_worldMouseText = ecs.createEntity(); - auto& t = ecs.addComponent(m_worldMouseText); + m_worldMouseText = registry.createEntity(); + auto& t = registry.addComponent(m_worldMouseText); t.position = vec3(0.0f, 0.0f, 0.0f); - auto& text = ecs.addComponent(m_worldMouseText); + auto& text = registry.addComponent(m_worldMouseText); text.text = "WORLD MOUSE"; text.material = DisplaySettings::LightBlue; text.horizontalAlignment = TextRenderer::HorizontalAlignment::Left; @@ -63,11 +63,11 @@ class TextScene : public Scene2D } { - m_counterText = ecs.createEntity(); - auto& t = ecs.addComponent(m_counterText); + m_counterText = registry.createEntity(); + auto& t = registry.addComponent(m_counterText); t.position = vec3(static_cast(Display::width) * 0.5f, 50.0f, 0.0f); - auto& text = ecs.addComponent(m_counterText); + auto& text = registry.addComponent(m_counterText); text.text = "0"; text.material = DisplaySettings::LightGreen; text.horizontalAlignment = TextRenderer::HorizontalAlignment::Left; @@ -75,11 +75,11 @@ class TextScene : public Scene2D } { - m_centerText = ecs.createEntity(); - auto& t = ecs.addComponent(m_centerText); + m_centerText = registry.createEntity(); + auto& t = registry.addComponent(m_centerText); t.position = vec3(static_cast(Display::width) * 0.5f, 20.0f, 0.0f); - auto& text = ecs.addComponent(m_centerText); + auto& text = registry.addComponent(m_centerText); text.text = "CENTERED"; text.material = DisplaySettings::Yellow; text.horizontalAlignment = TextRenderer::HorizontalAlignment::Center; @@ -87,11 +87,11 @@ class TextScene : public Scene2D } { - m_leftText = ecs.createEntity(); - auto& t = ecs.addComponent(m_leftText); + m_leftText = registry.createEntity(); + auto& t = registry.addComponent(m_leftText); t.position = vec3(10.0f, 20.0f, 0.0f); - auto& text = ecs.addComponent(m_leftText); + auto& text = registry.addComponent(m_leftText); text.text = "LEFT"; text.material = DisplaySettings::Orange; text.horizontalAlignment = TextRenderer::HorizontalAlignment::Left; @@ -99,11 +99,11 @@ class TextScene : public Scene2D } { - m_rightText = ecs.createEntity(); - auto& t = ecs.addComponent(m_rightText); + m_rightText = registry.createEntity(); + auto& t = registry.addComponent(m_rightText); t.position = vec3(static_cast(Display::width) - 10.0f, 20.0f, 0.0f); - auto& text = ecs.addComponent(m_rightText); + auto& text = registry.addComponent(m_rightText); text.text = "RIGHT"; text.material = DisplaySettings::Magenta; text.horizontalAlignment = TextRenderer::HorizontalAlignment::Right; @@ -111,12 +111,12 @@ class TextScene : public Scene2D } { - m_nonResponsiveText = ecs.createEntity(); - auto& t = ecs.addComponent(m_nonResponsiveText); + m_nonResponsiveText = registry.createEntity(); + auto& t = registry.addComponent(m_nonResponsiveText); t.position = vec3(static_cast(Display::width) - 10.0f, static_cast(Display::height) - 10.0f, 0.0f); - auto& text = ecs.addComponent(m_nonResponsiveText); + auto& text = registry.addComponent(m_nonResponsiveText); text.text = "STUCK"; text.material = DisplaySettings::Red; text.horizontalAlignment = TextRenderer::HorizontalAlignment::Right; @@ -124,7 +124,7 @@ class TextScene : public Scene2D } } - void onUpdate(ECSManager& ecs, ServiceProvider& services) override + void onUpdate(Registry& registry, ServiceProvider& services) override { if (services.input().getKeyDown(Input::Q) || services.input().getGamepadButtonDown(Input::GamepadButton::North)) { @@ -133,24 +133,24 @@ class TextScene : public Scene2D m_counter++; { - auto& text = ecs.getComponent(m_counterText); + auto& text = registry.getComponent(m_counterText); text.text = std::to_string(m_counter); - ecs.setComponentDirty(text); + registry.setComponentDirty(text); - auto& t = ecs.getComponent(m_counterText); + auto& t = registry.getComponent(m_counterText); t.position.x = services.input().getMouseX() + 20.0f; t.position.y = services.input().getMouseY() + 10.0f; } { - auto& cameraTransform = ecs.getComponent(services.render().getCameraEntity()); + auto& cameraTransform = registry.getComponent(services.render().getCameraEntity()); vec2 mouseScreen = vec2(services.input().getMouseX() + 20.0f, services.input().getMouseY() - 10.0f); vec2 mouseWorld = ECS::Camera::screenPositionToWorldPosition2D(cameraTransform, mouseScreen); - auto& t = ecs.getComponent(m_worldMouseText); + auto& t = registry.getComponent(m_worldMouseText); t.position.x = mouseWorld.x; t.position.y = mouseWorld.y; - ecs.setComponentDirty(t); + registry.setComponentDirty(t); } int hash = Display::width + Display::height; @@ -160,9 +160,9 @@ class TextScene : public Scene2D float halfW = static_cast(Display::width) * 0.5f; - ecs.getComponent(m_counterText).position = vec3(halfW, 20.0f, 0.0f); - ecs.getComponent(m_centerText).position = vec3(halfW, 40.0f, 0.0f); - ecs.getComponent(m_rightText).position = + registry.getComponent(m_counterText).position = vec3(halfW, 20.0f, 0.0f); + registry.getComponent(m_centerText).position = vec3(halfW, 40.0f, 0.0f); + registry.getComponent(m_rightText).position = vec3(static_cast(Display::width) - 10.0f, 20.0f, 0.0f); } } diff --git a/examples/sample-scenes/include/WalkScene.h b/examples/sample-scenes/include/WalkScene.h index 1f97bb2..69500ef 100644 --- a/examples/sample-scenes/include/WalkScene.h +++ b/examples/sample-scenes/include/WalkScene.h @@ -30,7 +30,7 @@ class WalkScene : public Scene2D Entity m_head; // Inherited via Scene - void onStart(ECSManager& ecs, ServiceProvider& services) override + void onStart(Registry& registry, ServiceProvider& services) override { services.debug().setDebugInput(true); services.debug().setDebugFly(true); @@ -43,21 +43,21 @@ class WalkScene : public Scene2D auto tags = services.serialization().loadWeirdFile(services.resources().assetPath("man.weird")); - Entity firstCreated = static_cast(ecs.getEntityCount()); + Entity firstCreated = static_cast(registry.getEntityCount()); - Entity lastCreated = static_cast(ecs.getEntityCount()); + Entity lastCreated = static_cast(registry.getEntityCount()); for (Entity e = 0; e < (lastCreated - firstCreated); e++) { - auto& t = ecs.getComponent(firstCreated + e); + auto& t = registry.getComponent(firstCreated + e); t.position += vec3(-10.0f, 0.0f, 0.0f); } Entity leftFootEntity = tags["foot_left"]; - ecs.addComponent(leftFootEntity); + registry.addComponent(leftFootEntity); Entity rightFootEntity = tags["foot_right"]; - ecs.addComponent(rightFootEntity); + registry.addComponent(rightFootEntity); m_head = tags["head"]; @@ -65,34 +65,34 @@ class WalkScene : public Scene2D Entity inside = services.shapes().addShape(DefaultShapes::BOX, boundsVars2, DisplaySettings::LightGreen, CombinationType::Addition); - ecs.getComponent(services.render().getCameraEntity()).position = g_cameraPositon; + registry.getComponent(services.render().getCameraEntity()).position = g_cameraPositon; - Entity globalSettingsEnt = ecs.createEntity(); - auto& settings = ecs.addComponent(globalSettingsEnt); + Entity globalSettingsEnt = registry.createEntity(); + auto& settings = registry.addComponent(globalSettingsEnt); settings.gravity = -10.0f; - ecs.setComponentDirty(settings); + registry.setComponentDirty(settings); } - void onUpdate(ECSManager& ecs, ServiceProvider& services) override + void onUpdate(Registry& registry, ServiceProvider& services) override { float delta = services.time().deltaTime(); - g_cameraPositon = ecs.getComponent(services.render().getCameraEntity()).position; + g_cameraPositon = registry.getComponent(services.render().getCameraEntity()).position; if (services.input().getKeyDown(Input::Q) || services.input().getGamepadButtonDown(Input::GamepadButton::North)) { services.sceneControl().goToNextScene(); } - updatePhysics(delta, ecs); + updatePhysics(delta, registry); } int m_currentFoot = 0; bool m_feetTouching = false; - void updatePhysics(float delta, ECSManager& ecs) + void updatePhysics(float delta, Registry& registry) { - auto componentArray = ecs.getComponentArray(); - auto rigidBodies = ecs.getComponentArray(); + auto componentArray = registry.getComponentArray(); + auto rigidBodies = registry.getComponentArray(); for (size_t i = 0; i < componentArray->getSize(); i++) { @@ -104,28 +104,29 @@ class WalkScene : public Scene2D if (foot.onFloor) { rb.isFixed = true; - ecs.setComponentDirty(rb); + registry.setComponentDirty(rb); } continue; } - auto& headRB = ecs.getComponent(m_head); + auto& headRB = registry.getComponent(m_head); headRB.pendingImpulseForce += vec2(0.0f, 1.0f); rb.isFixed = false; - ecs.setComponentDirty(rb); + registry.setComponentDirty(rb); // Start step if (!foot.stepStarted) { if (foot.onFloor) { - foot.initialPos = vec2(ecs.getComponent(componentArray->getEntityAtIdx(i)).position); + foot.initialPos = + vec2(registry.getComponent(componentArray->getEntityAtIdx(i)).position); foot.stepStarted = true; foot.t = 0.0f; // rb.position = foot.initialPos + vec2(0.0f, 0.1f); rb.isFixed = false; - ecs.setComponentDirty(rb); + registry.setComponentDirty(rb); } } else @@ -170,25 +171,25 @@ class WalkScene : public Scene2D m_feetTouching = false; } - void onEntityCollision(ECSManager& ecs, ServiceProvider& services, + void onEntityCollision(Registry& registry, ServiceProvider& services, WeirdEngine::EntityCollisionEvent& event) override { Entity entityA = event.entityA; Entity entityB = event.entityB; - if (ecs.hasComponent(entityA) && ecs.hasComponent(entityB)) + if (registry.hasComponent(entityA) && registry.hasComponent(entityB)) { m_feetTouching = true; } } - void onEntityShapeCollision(ECSManager& ecs, ServiceProvider& services, + void onEntityShapeCollision(Registry& registry, ServiceProvider& services, WeirdEngine::EntityShapeCollisionEvent& event) override { Entity entity = event.entity; - if (ecs.hasComponent(entity)) + if (registry.hasComponent(entity)) { - auto& foot = ecs.getComponent(entity); + auto& foot = registry.getComponent(entity); if (event.raw.state == CollisionState::START) { foot.onFloor = true; diff --git a/include/weird-engine/Scene.h b/include/weird-engine/Scene.h index d0c0214..ed16acf 100644 --- a/include/weird-engine/Scene.h +++ b/include/weird-engine/Scene.h @@ -1,6 +1,6 @@ #pragma once -#include "ecs/ECS.h" +#include "ecs/Registry.h" #include "ResourceManager.h" #include "weird-engine/systems/SDFRenderSystem.h" @@ -47,9 +47,9 @@ namespace WeirdEngine class SceneManager; // ---- System Signatures ---- - using CoreSystem = std::function; - using EntityCollisionSystem = std::function; - using EntityShapeCollisionSystem = std::function; + using CoreSystem = std::function; + using EntityCollisionSystem = std::function; + using EntityShapeCollisionSystem = std::function; namespace WeirdRenderer { @@ -128,21 +128,22 @@ namespace WeirdEngine Scene(RenderMode mode); // ---- Lifecycle callbacks - virtual void onCreate(ECSManager& ecs, ServiceProvider& services) {}; - virtual void onStart(ECSManager& ecs, ServiceProvider& services) {} - virtual void onUpdate(ECSManager& ecs, ServiceProvider& services) {}; - virtual void onDestroy(ECSManager& ecs, ServiceProvider& services) {}; - virtual void onImGuiRender(ECSManager& ecs, ServiceProvider& services) {}; - virtual void onRender(ECSManager& ecs, ServiceProvider& services, WeirdRenderer::RenderTarget& renderTarget) {}; + virtual void onCreate(Registry& registry, ServiceProvider& services) {}; + virtual void onStart(Registry& registry, ServiceProvider& services) {} + virtual void onUpdate(Registry& registry, ServiceProvider& services) {}; + virtual void onDestroy(Registry& registry, ServiceProvider& services) {}; + virtual void onImGuiRender(Registry& registry, ServiceProvider& services) {}; + virtual void onRender(Registry& registry, ServiceProvider& services, + WeirdRenderer::RenderTarget& renderTarget) {}; // ---- Main thread collision callbacks (onEntity* family). Fire after // the physics response has been applied; the events are read-only. - // m_ecs is safe to use here (the physics thread only ever touches + // m_registry is safe to use here (the physics thread only ever touches // Simulation2D internals). See the onPhysics* callbacks below for the // pre-response, mutable equivalents. - virtual void onEntityCollision(ECSManager& ecs, ServiceProvider& services, + virtual void onEntityCollision(Registry& registry, ServiceProvider& services, WeirdEngine::EntityCollisionEvent& event) {}; - virtual void onEntityShapeCollision(ECSManager& ecs, ServiceProvider& services, + virtual void onEntityShapeCollision(Registry& registry, ServiceProvider& services, WeirdEngine::EntityShapeCollisionEvent& event) {}; // ---- Physics thread callbacks (onPhysics* family). Fire on the @@ -163,10 +164,10 @@ namespace WeirdEngine void update(double delta, double time); void destroy() { - onDestroy(m_ecs, m_services); + onDestroy(m_registry, m_services); for (auto& sys : m_destroySystems) { - sys(m_ecs, m_services); + sys(m_registry, m_services); } } @@ -248,7 +249,7 @@ namespace WeirdEngine bool m_debugInput = false; // ---- Simulation - ECSManager m_ecs; + Registry m_registry; Simulation2D m_simulation2D; bool m_runSimulationInThread; bool m_simulationIsPaused = false; diff --git a/include/weird-engine/ecs/ECS.h b/include/weird-engine/ecs/Registry.h similarity index 98% rename from include/weird-engine/ecs/ECS.h rename to include/weird-engine/ecs/Registry.h index 39ae14f..9854399 100644 --- a/include/weird-engine/ecs/ECS.h +++ b/include/weird-engine/ecs/Registry.h @@ -37,8 +37,10 @@ namespace WeirdEngine } } // namespace internal - // ECSManager Manager - class ECSManager + // Entity + component storage. Systems are not managed here: scheduling + // lives on Scene, which dispatches them with a Registry& and + // ServiceProvider&. + class Registry { public: Entity createEntity() diff --git a/include/weird-engine/services/ServiceProvider.h b/include/weird-engine/services/ServiceProvider.h index c5cbac3..fb0c655 100644 --- a/include/weird-engine/services/ServiceProvider.h +++ b/include/weird-engine/services/ServiceProvider.h @@ -11,7 +11,7 @@ #include #include "weird-engine/Background.h" -#include "weird-engine/ecs/ECS.h" +#include "weird-engine/ecs/Registry.h" #include "weird-engine/Input.h" #include "weird-engine/Material3D.h" #include "weird-engine/ResourceManager.h" @@ -50,7 +50,7 @@ namespace WeirdEngine // Shared raymarch implementation used by both Scene::raymarch and // PhysicsService::raymarch. Defined in Scene.cpp. - RaymarchResult raymarchScene(ECSManager& ecs, std::vector>& sdfs, + RaymarchResult raymarchScene(Registry& registry, std::vector>& sdfs, Simulation2D& simulation, float time, glm::vec2 origin, glm::vec2 direction, float epsilon, float maxDistance); @@ -77,7 +77,7 @@ namespace WeirdEngine struct PhysicsService { - ECSManager& ecs; + Registry& registry; Simulation2D& simulation; std::vector>& sdfs; @@ -113,7 +113,7 @@ namespace WeirdEngine Entity entityForSimulationId(SimulationID simulationId) const { - auto rigidBodies = ecs.getComponentArray(); + auto rigidBodies = registry.getComponentArray(); if (simulationId >= static_cast(rigidBodies->getSize())) return INVALID_ENTITY; @@ -149,14 +149,14 @@ namespace WeirdEngine RaymarchResult raymarch(glm::vec2 origin, glm::vec2 direction, float epsilon = 0.001f, float maxDistance = 150.0f) { - return raymarchScene(ecs, sdfs, simulation, static_cast(simulation.getSimulationTime()), origin, + return raymarchScene(registry, sdfs, simulation, static_cast(simulation.getSimulationTime()), origin, direction, epsilon, maxDistance); } }; struct ShapeService { - ECSManager& ecs; + Registry& registry; Simulation2D& simulation; std::vector>& sdfs; @@ -179,8 +179,8 @@ namespace WeirdEngine CombinationType combination = CombinationType::Addition, bool hasCollision = true, int group = 0) { - Entity entity = ecs.createEntity(); - CustomShape& shape = ecs.addComponent(entity); + Entity entity = registry.createEntity(); + CustomShape& shape = registry.addComponent(entity); shape.distanceFieldId = shapeId; shape.combination = combination; shape.hasCollisions = hasCollision; @@ -201,8 +201,8 @@ namespace WeirdEngine Entity addUIShape(ShapeId shapeId, float* variables, uint16_t material, CombinationType combination = CombinationType::Addition, int group = 0) { - Entity entity = ecs.createEntity(); - UIShape& shape = ecs.addComponent(entity); + Entity entity = registry.createEntity(); + UIShape& shape = registry.addComponent(entity); shape.distanceFieldId = shapeId; shape.combination = combination; shape.groupIdx = group; @@ -220,8 +220,8 @@ namespace WeirdEngine UIShape& addUIShape(ShapeId shapeId, float* variables, Entity& entity, int group = 0) { - entity = ecs.createEntity(); - UIShape& component = ecs.addComponent(entity); + entity = registry.createEntity(); + UIShape& component = registry.addComponent(entity); component.distanceFieldId = shapeId; component.groupIdx = group; component.smoothFactor = 100.0f; @@ -233,7 +233,7 @@ namespace WeirdEngine struct RenderService { - ECSManager& ecs; + Registry& registry; Entity& cameraEntity; SDFRenderSystemContext& context2D; SDFRenderSystemContext& context3D; @@ -244,7 +244,7 @@ namespace WeirdEngine WeirdRenderer::Camera& camera() { - return ecs.getComponent(cameraEntity).camera; + return registry.getComponent(cameraEntity).camera; } Entity getCameraEntity() const @@ -645,16 +645,16 @@ namespace WeirdEngine }; // Central access point for all non-ECS scene functionality. Systems take - // a ServiceProvider& (plus the ECSManager&) and use it instead of reaching + // a ServiceProvider& (plus the Registry&) and use it instead of reaching // into Scene internals. Owned by Scene, which binds it to its storage. class ServiceProvider { public: explicit ServiceProvider(Scene& scene); - ECSManager& ecs() + Registry& registry() { - return m_ecs; + return m_registry; } TimeService& time() @@ -718,7 +718,7 @@ namespace WeirdEngine } private: - ECSManager& m_ecs; + Registry& m_registry; TimeService m_time; PhysicsService m_physics; ShapeService m_shapes; diff --git a/include/weird-engine/systems/ButtonSystem.h b/include/weird-engine/systems/ButtonSystem.h index e28117b..b9f0abd 100644 --- a/include/weird-engine/systems/ButtonSystem.h +++ b/include/weird-engine/systems/ButtonSystem.h @@ -1,5 +1,5 @@ #pragma once -#include "weird-engine/ecs/ECS.h" +#include "weird-engine/ecs/Registry.h" #include "weird-engine/Input.h" #include "weird-engine/math/MathExpressions.h" @@ -12,20 +12,20 @@ namespace WeirdEngine namespace ButtonSystem { - inline void updateButtons(ECSManager& ecs, std::vector>& sdfs, float time); - inline void updateToggles(ECSManager& ecs, std::vector>& sdfs, float time); + inline void updateButtons(Registry& registry, std::vector>& sdfs, float time); + inline void updateToggles(Registry& registry, std::vector>& sdfs, float time); - inline void update(ECSManager& ecs, std::vector>& sdfs, float time) + inline void update(Registry& registry, std::vector>& sdfs, float time) { - updateButtons(ecs, sdfs, time); - updateToggles(ecs, sdfs, time); + updateButtons(registry, sdfs, time); + updateToggles(registry, sdfs, time); } - inline void updateButtons(ECSManager& ecs, std::vector>& sdfs, float time) + inline void updateButtons(Registry& registry, std::vector>& sdfs, float time) { bool mouseIsClicking = Input::GetMouseButton(Input::LeftClick); - ecs.forEach( + registry.forEach( [&](Entity buttonOwner, ShapeButton& buttonComponent, UIShape& shape) { { @@ -95,11 +95,11 @@ namespace WeirdEngine }); } - inline void updateToggles(ECSManager& ecs, std::vector>& sdfs, float time) + inline void updateToggles(Registry& registry, std::vector>& sdfs, float time) { bool mouseIsClicking = Input::GetMouseButtonDown(Input::LeftClick); - ecs.forEach( + registry.forEach( [&](Entity toggleOwner, ShapeToggle& toggleComponent, UIShape& shape) { { diff --git a/include/weird-engine/systems/CameraSystem.h b/include/weird-engine/systems/CameraSystem.h index f46a634..fd1dc80 100644 --- a/include/weird-engine/systems/CameraSystem.h +++ b/include/weird-engine/systems/CameraSystem.h @@ -1,5 +1,5 @@ #pragma once -#include "weird-engine/ecs/ECS.h" +#include "weird-engine/ecs/Registry.h" #include "weird-engine/Input.h" namespace WeirdEngine @@ -8,9 +8,9 @@ namespace WeirdEngine { namespace CameraSystem { - inline void update(ECSManager& ecs) + inline void update(Registry& registry) { - ecs.forEach( + registry.forEach( [](Entity camOwner, Camera& c, Transform& t) { c.camera.position = t.position; diff --git a/include/weird-engine/systems/PhysicsInteractionSystem.h b/include/weird-engine/systems/PhysicsInteractionSystem.h index a31adab..5c1749a 100644 --- a/include/weird-engine/systems/PhysicsInteractionSystem.h +++ b/include/weird-engine/systems/PhysicsInteractionSystem.h @@ -1,5 +1,5 @@ #pragma once -#include "weird-engine/ecs/ECS.h" +#include "weird-engine/ecs/Registry.h" #include "weird-engine/Input.h" namespace WeirdEngine @@ -80,12 +80,12 @@ namespace WeirdEngine return false; } - inline vec2 getMousePositionInWorld(ECSManager& ecs); - inline void drag(ECSManager& ecs); - inline void impulse(ECSManager& ecs); - inline void fix(ECSManager& ecs); - inline void spring(ECSManager& ecs); - inline void positionConstraint(ECSManager& ecs); + inline vec2 getMousePositionInWorld(Registry& registry); + inline void drag(Registry& registry); + inline void impulse(Registry& registry); + inline void fix(Registry& registry); + inline void spring(Registry& registry); + inline void positionConstraint(Registry& registry); inline void reset() { @@ -102,23 +102,23 @@ namespace WeirdEngine m_selectedId = INVALID_ENTITY; } - inline void update(ECSManager& ecs) + inline void update(Registry& registry) { // Spawn ball if (getLeftClickDown()) { // Get mouse coordinates world space - vec2 mousePositionInWorld = getMousePositionInWorld(ecs); + vec2 mousePositionInWorld = getMousePositionInWorld(registry); // t.position = vec3(mousePositionInWorld.x + sin(time), mousePositionInWorld.y + cos(time), 0.0); - Entity entity = ecs.createEntity(); - Transform& t = ecs.addComponent(entity); + Entity entity = registry.createEntity(); + Transform& t = registry.addComponent(entity); t.position = vec3(mousePositionInWorld.x, mousePositionInWorld.y, 0.0); - auto& dot = ecs.addComponent(entity); + auto& dot = registry.addComponent(entity); dot.materialId = m_currentMaterial + 4; - RigidBody2D& rb = ecs.addComponent(entity); + RigidBody2D& rb = registry.addComponent(entity); if (!m_usingController) { rb.pendingImpulseForce += 1000.0f * vec2(Input::GetMouseDeltaX(), -Input::GetMouseDeltaY()); @@ -140,19 +140,19 @@ namespace WeirdEngine switch (m_currentInteractionMode) { case PhysicsInteractionSystem::InteractionMode::Drag: - drag(ecs); + drag(registry); break; case PhysicsInteractionSystem::InteractionMode::Impulse: - impulse(ecs); + impulse(registry); break; case PhysicsInteractionSystem::InteractionMode::Fix: - fix(ecs); + fix(registry); break; case PhysicsInteractionSystem::InteractionMode::Spring: - spring(ecs); + spring(registry); break; case PhysicsInteractionSystem::InteractionMode::DistanceConstraint: - positionConstraint(ecs); + positionConstraint(registry); break; default: break; @@ -203,12 +203,12 @@ namespace WeirdEngine // Add force to last ball if (Input::GetKey(Input::T)) { - auto rbs = ecs.getComponentArray(); + auto rbs = registry.getComponentArray(); RigidBody2D& target = rbs->getLastData(); Entity targetOwner = rbs->getEntityAtIdx(rbs->getSize() - 1); - vec3 position = ecs.getComponent(targetOwner).position; + vec3 position = registry.getComponent(targetOwner).position; vec2 v = vec2(15, 30) - vec2(position.x, position.y); target.pendingContinuousForce += 50.0f * normalize(v); @@ -230,10 +230,10 @@ namespace WeirdEngine // } } - inline vec2 getMousePositionInWorld(ECSManager& ecs) + inline vec2 getMousePositionInWorld(Registry& registry) { // Get mouse coordinates - auto& cameraTransform = ecs.getComponent(0); // m_mainCamera + auto& cameraTransform = registry.getComponent(0); // m_mainCamera float x = m_usingController ? (WeirdRenderer::Display::width / 2.0f) : Input::GetMouseX(); float y = m_usingController ? (WeirdRenderer::Display::height / 2.0f) : Input::GetMouseY(); @@ -243,21 +243,21 @@ namespace WeirdEngine return mousePositionInWorld; } - inline void drag(ECSManager& ecs) + inline void drag(Registry& registry) { if (getRightClickDown()) { - auto rbs = ecs.getComponentArray(); + auto rbs = registry.getComponentArray(); float minD2 = 1.0f; - vec2 mouseInWorld = getMousePositionInWorld(ecs); + vec2 mouseInWorld = getMousePositionInWorld(registry); for (int i = 0; i < rbs->getSize(); i++) { auto& rb = rbs->getDataAtIdx(i); Entity rbOwner = rbs->getEntityAtIdx(i); - auto& t = ecs.getComponent(rbOwner); + auto& t = registry.getComponent(rbOwner); float d2 = glm::length2(mouseInWorld - vec2(t.position.x, t.position.y)); if (d2 < minD2) @@ -269,7 +269,7 @@ namespace WeirdEngine if (m_dragId != INVALID_ENTITY) { - auto& rb = ecs.getComponent(m_dragId); + auto& rb = registry.getComponent(m_dragId); rb.isFixed = true; rbs->setEntityDirty(m_dragId, true); } @@ -279,7 +279,7 @@ namespace WeirdEngine { if (m_dragId != INVALID_ENTITY) { - auto& rb = ecs.getComponent(m_dragId); + auto& rb = registry.getComponent(m_dragId); rb.isFixed = false; if (!m_usingController) { @@ -290,7 +290,7 @@ namespace WeirdEngine rb.pendingImpulseForce += 1000.0f * vec2(Input::GetGamepadAxis(Input::GamepadAxis::RightX), -Input::GetGamepadAxis(Input::GamepadAxis::RightY)); } - ecs.getComponentArray()->setEntityDirty(m_dragId, true); + registry.getComponentArray()->setEntityDirty(m_dragId, true); m_dragId = INVALID_ENTITY; } @@ -298,17 +298,17 @@ namespace WeirdEngine if (m_dragId != INVALID_ENTITY) { - vec2 mousePositionInWorld = getMousePositionInWorld(ecs); - auto& t = ecs.getComponent(m_dragId); + vec2 mousePositionInWorld = getMousePositionInWorld(registry); + auto& t = registry.getComponent(m_dragId); t.position = vec3(mousePositionInWorld, 0.0f); - ecs.getComponentArray()->setEntityDirty(m_dragId, true); + registry.getComponentArray()->setEntityDirty(m_dragId, true); } } - inline Entity getEntityAtPosition(ECSManager& ecs, vec2 position, float radius = 0.5f) + inline Entity getEntityAtPosition(Registry& registry, vec2 position, float radius = 0.5f) { - auto rbs = ecs.getComponentArray(); + auto rbs = registry.getComponentArray(); float minD2 = radius * radius; Entity found = INVALID_ENTITY; @@ -316,7 +316,7 @@ namespace WeirdEngine { auto& rb = rbs->getDataAtIdx(i); Entity rbOwner = rbs->getEntityAtIdx(i); - auto& t = ecs.getComponent(rbOwner); + auto& t = registry.getComponent(rbOwner); float d2 = glm::length2(position - vec2(t.position.x, t.position.y)); if (d2 < minD2) @@ -328,7 +328,7 @@ namespace WeirdEngine return found; } - inline void impulse(ECSManager& ecs) + inline void impulse(Registry& registry) { if (getRightClickDown()) { @@ -336,7 +336,7 @@ namespace WeirdEngine return; m_loadingImpulse = true; - m_loadStartPosition = getMousePositionInWorld(ecs); + m_loadStartPosition = getMousePositionInWorld(registry); } if (getRightClickUp()) @@ -346,12 +346,12 @@ namespace WeirdEngine m_loadingImpulse = false; - auto m_rbManager = ecs.getComponentManager(); + auto m_rbManager = registry.getComponentManager(); auto componentArray = m_rbManager->getComponentArray(); - auto transforms = ecs.getComponentArray(); + auto transforms = registry.getComponentArray(); - vec2 mousePositionInWorld = getMousePositionInWorld(ecs); + vec2 mousePositionInWorld = getMousePositionInWorld(registry); vec2 direction = (mousePositionInWorld - m_loadStartPosition); float dragDistance = length(direction); @@ -377,26 +377,26 @@ namespace WeirdEngine } } - inline void fix(ECSManager& ecs) + inline void fix(Registry& registry) { if (getRightClickDown()) { - Entity id = getEntityAtPosition(ecs, getMousePositionInWorld(ecs)); + Entity id = getEntityAtPosition(registry, getMousePositionInWorld(registry)); if (id != INVALID_ENTITY) { - auto& rb = ecs.getComponent(id); + auto& rb = registry.getComponent(id); rb.isFixed = !rb.isFixed; - ecs.getComponentArray()->setEntityDirty(id, true); + registry.getComponentArray()->setEntityDirty(id, true); } } } - inline void spring(ECSManager& ecs) + inline void spring(Registry& registry) { if (getRightClickDown()) { - Entity id = getEntityAtPosition(ecs, getMousePositionInWorld(ecs)); + Entity id = getEntityAtPosition(registry, getMousePositionInWorld(registry)); if (id != INVALID_ENTITY) { m_firstIdInSpring = id; @@ -412,16 +412,16 @@ namespace WeirdEngine if (m_firstIdInSpring != INVALID_ENTITY) { // Check - Entity id = getEntityAtPosition(ecs, getMousePositionInWorld(ecs)); + Entity id = getEntityAtPosition(registry, getMousePositionInWorld(registry)); if (id != INVALID_ENTITY) { - Entity entity = ecs.createEntity(); - auto& spring = ecs.addComponent(entity); + Entity entity = registry.createEntity(); + auto& spring = registry.addComponent(entity); spring.entityA = id; spring.entityB = m_firstIdInSpring; spring.stiffness = 0.1f; spring.restDistance = 1.4142f; - ecs.getComponentArray()->setEntityDirty(entity, true); + registry.getComponentArray()->setEntityDirty(entity, true); } } @@ -429,12 +429,12 @@ namespace WeirdEngine } } - inline void positionConstraint(ECSManager& ecs) + inline void positionConstraint(Registry& registry) { if (getRightClickDown()) { - Entity id = getEntityAtPosition(ecs, getMousePositionInWorld(ecs)); + Entity id = getEntityAtPosition(registry, getMousePositionInWorld(registry)); if (id != INVALID_ENTITY) { m_firstIdInSpring = id; @@ -450,15 +450,15 @@ namespace WeirdEngine if (m_firstIdInSpring != INVALID_ENTITY) { // Check - Entity id = getEntityAtPosition(ecs, getMousePositionInWorld(ecs)); + Entity id = getEntityAtPosition(registry, getMousePositionInWorld(registry)); if (id != INVALID_ENTITY) { - Entity entity = ecs.createEntity(); - auto& constraint = ecs.addComponent(entity); + Entity entity = registry.createEntity(); + auto& constraint = registry.addComponent(entity); constraint.entityA = id; constraint.entityB = m_firstIdInSpring; constraint.distance = 1.0f; // Was default in simulation.addPositionConstraint - ecs.getComponentArray()->setEntityDirty(entity, true); + registry.getComponentArray()->setEntityDirty(entity, true); } } diff --git a/include/weird-engine/systems/PhysicsSystem2D.h b/include/weird-engine/systems/PhysicsSystem2D.h index a589fcc..8c036ad 100644 --- a/include/weird-engine/systems/PhysicsSystem2D.h +++ b/include/weird-engine/systems/PhysicsSystem2D.h @@ -1,6 +1,6 @@ #pragma once #pragma once -#include "weird-engine/ecs/ECS.h" +#include "weird-engine/ecs/Registry.h" #include "weird-physics/components/DistanceConstraint.h" #include "weird-physics/components/GlobalPhysicsSettings.h" #include "weird-physics/components/Spring.h" @@ -14,28 +14,28 @@ namespace WeirdEngine namespace PhysicsSystem2D { - inline void init(ECSManager& ecs, Simulation2D& simulation) + inline void init(Registry& registry, Simulation2D& simulation) { - ecs.registerComponent(); - ecs.registerComponent(); - ecs.registerComponent(); + registry.registerComponent(); + registry.registerComponent(); + registry.registerComponent(); } - inline void update(ECSManager& ecs, Simulation2D& simulation) + inline void update(Registry& registry, Simulation2D& simulation) { // Pass 1: ECS -> physics. Writes are queued as commands and the // physics thread applies them on its next step. - ecs.forEach( + registry.forEach( [&](Entity entity, RigidBody2D& rb, Transform& transform) { - if (ecs.isComponentDirty(transform)) + if (registry.isComponentDirty(transform)) { // Override simulation transform simulation.setPosition(rb.simulationId, glm::vec2(transform.position)); - ecs.setComponentDirty(transform, false); // TODO: move somewhere else + registry.setComponentDirty(transform, false); // TODO: move somewhere else } - if (ecs.isComponentDirty(rb)) + if (registry.isComponentDirty(rb)) { simulation.setVelocity(rb.simulationId, rb.velocity); @@ -44,7 +44,7 @@ namespace WeirdEngine else simulation.unFix(rb.simulationId); - ecs.setComponentDirty(rb, false); + registry.setComponentDirty(rb, false); } if (glm::length2(rb.pendingImpulseForce) > 0.0001f) @@ -60,57 +60,57 @@ namespace WeirdEngine } }); - ecs.forEach( + registry.forEach( [&](Entity entity, CustomShape& shape) { - if (ecs.isComponentDirty(shape)) + if (registry.isComponentDirty(shape)) { simulation.updateShape(entity, shape); - ecs.setComponentDirty(shape, false); + registry.setComponentDirty(shape, false); } }); - ecs.forEach( + registry.forEach( [&](Entity entity, GlobalPhysicsSettings& settings) { - if (ecs.isComponentDirty(settings)) + if (registry.isComponentDirty(settings)) { simulation.setGravity(settings.gravity); simulation.setDamping(settings.damping); - ecs.setComponentDirty(settings, false); + registry.setComponentDirty(settings, false); } }); - ecs.forEach( + registry.forEach( [&](Entity entity, Spring& spring) { - if (ecs.isComponentDirty(spring) && spring.entityA != INVALID_ENTITY && + if (registry.isComponentDirty(spring) && spring.entityA != INVALID_ENTITY && spring.entityB != INVALID_ENTITY) { - if (ecs.hasComponent(spring.entityA) && - ecs.hasComponent(spring.entityB)) + if (registry.hasComponent(spring.entityA) && + registry.hasComponent(spring.entityB)) { - auto simIdA = ecs.getComponent(spring.entityA).simulationId; - auto simIdB = ecs.getComponent(spring.entityB).simulationId; + auto simIdA = registry.getComponent(spring.entityA).simulationId; + auto simIdB = registry.getComponent(spring.entityB).simulationId; simulation.addSpring(simIdA, simIdB, spring.stiffness, spring.restDistance); - ecs.setComponentDirty(spring, false); + registry.setComponentDirty(spring, false); } } }); - ecs.forEach( + registry.forEach( [&](Entity entity, DistanceConstraint& constraint) { - if (ecs.isComponentDirty(constraint) && constraint.entityA != INVALID_ENTITY && + if (registry.isComponentDirty(constraint) && constraint.entityA != INVALID_ENTITY && constraint.entityB != INVALID_ENTITY) { - if (ecs.hasComponent(constraint.entityA) && - ecs.hasComponent(constraint.entityB)) + if (registry.hasComponent(constraint.entityA) && + registry.hasComponent(constraint.entityB)) { - auto simIdA = ecs.getComponent(constraint.entityA).simulationId; - auto simIdB = ecs.getComponent(constraint.entityB).simulationId; + auto simIdA = registry.getComponent(constraint.entityA).simulationId; + auto simIdB = registry.getComponent(constraint.entityB).simulationId; simulation.addPositionConstraint(simIdA, simIdB, constraint.distance); - ecs.setComponentDirty(constraint, false); + registry.setComponentDirty(constraint, false); } } }); @@ -131,7 +131,7 @@ namespace WeirdEngine simulation.copyReadBuffers(readSnapshot); - ecs.forEach( + registry.forEach( [&](Entity entity, RigidBody2D& rb, Transform& transform) { vec2 position = readSnapshot.positions[rb.simulationId]; diff --git a/include/weird-engine/systems/PlayerMovementSystem.h b/include/weird-engine/systems/PlayerMovementSystem.h index c395a51..684b250 100644 --- a/include/weird-engine/systems/PlayerMovementSystem.h +++ b/include/weird-engine/systems/PlayerMovementSystem.h @@ -1,5 +1,5 @@ #pragma once -#include "weird-engine/ecs/ECS.h" +#include "weird-engine/ecs/Registry.h" #include "weird-engine/Input.h" #include @@ -23,20 +23,20 @@ namespace WeirdEngine return std::clamp(delta, 0.0f, MAX_DEBUG_CAMERA_DELTA); } - inline void updateMovement2D(ECSManager& ecs, float delta); - inline void updateFly(ECSManager& ecs, float delta); + inline void updateMovement2D(Registry& registry, float delta); + inline void updateFly(Registry& registry, float delta); - inline void update(ECSManager& ecs, float delta) + inline void update(Registry& registry, float delta) { - updateMovement2D(ecs, delta); - updateFly(ecs, delta); + updateMovement2D(registry, delta); + updateFly(registry, delta); } - inline void updateMovement2D(ECSManager& ecs, float delta) + inline void updateMovement2D(Registry& registry, float delta) { float safeDelta = clampMovementDelta(delta); - ecs.forEach( + registry.forEach( [&](Entity target, FlyMovement2D& flyComponent, Transform& t, Camera& c) { vec3 targetPosition = flyComponent.targetPosition; @@ -174,11 +174,11 @@ namespace WeirdEngine }); } - inline void updateFly(ECSManager& ecs, float delta) + inline void updateFly(Registry& registry, float delta) { float safeDelta = clampMovementDelta(delta); - ecs.forEach( + registry.forEach( [&](Entity target, FlyMovement& flyComponent, Transform& t, Camera& c) { glm::vec3 forward = glm::normalize(t.rotation); diff --git a/include/weird-engine/systems/RenderSystem.h b/include/weird-engine/systems/RenderSystem.h index b6ed627..10a3689 100644 --- a/include/weird-engine/systems/RenderSystem.h +++ b/include/weird-engine/systems/RenderSystem.h @@ -1,5 +1,5 @@ #pragma once -#include "weird-engine/ecs/ECS.h" +#include "weird-engine/ecs/Registry.h" #include "weird-engine/ResourceManager.h" #include "weird-renderer/resources/DrawCommand.h" @@ -12,14 +12,14 @@ namespace WeirdEngine namespace RenderSystem { - inline void update(ECSManager& ecs, ResourceManager& resourceManager, + inline void update(Registry& registry, ResourceManager& resourceManager, std::vector& drawQueue, std::vector& lights) { drawQueue.clear(); lights.clear(); - ecs.forEach( + registry.forEach( [&](Entity mOwner, MeshRenderer& mr, Transform& t) { WeirdRenderer::DrawCommand cmd; @@ -32,7 +32,7 @@ namespace WeirdEngine drawQueue.push_back(cmd); }); - ecs.forEach( + registry.forEach( [&](Entity mOwner, LightComponent& lc, Transform& t) { WeirdRenderer::Light light; diff --git a/include/weird-engine/systems/SDFRenderSystem.h b/include/weird-engine/systems/SDFRenderSystem.h index 6779417..a47026d 100644 --- a/include/weird-engine/systems/SDFRenderSystem.h +++ b/include/weird-engine/systems/SDFRenderSystem.h @@ -1,5 +1,5 @@ #pragma once -#include "weird-engine/ecs/ECS.h" +#include "weird-engine/ecs/Registry.h" #include "weird-engine/vec.h" #include "weird-renderer/resources/Font.h" #include @@ -27,19 +27,19 @@ namespace WeirdEngine { template - inline void update(ECSManager& ecs, SDFRenderSystemContext& ctx, vec4*& data, uint32_t& size) + inline void update(Registry& registry, SDFRenderSystemContext& ctx, vec4*& data, uint32_t& size) { uint32_t normalDots = 0; - if (auto dotArray = ecs.getComponentArray()) + if (auto dotArray = registry.getComponentArray()) { normalDots = dotArray->getSize(); } uint32_t textDots = 0; - ecs.forEach( + registry.forEach( [&](Entity entity, TextClass& text) { - if (ecs.isComponentDirty(text)) + if (registry.isComponentDirty(text)) { // Update dot count text.bufferedDotCount = 0; @@ -54,7 +54,7 @@ namespace WeirdEngine ((charCount - 1) * ctx.charSpacing); text.height = ctx.font.getCharHeight() * 2 * ctx.dotRadious; - ecs.setComponentDirty(text, false); + registry.setComponentDirty(text, false); } textDots += text.bufferedDotCount; @@ -63,7 +63,7 @@ namespace WeirdEngine uint32_t dotCount = normalDots + textDots; uint32_t shapeCount = 0; - if (auto shapeArray = ecs.getComponentArray()) + if (auto shapeArray = registry.getComponentArray()) { shapeCount = shapeArray->getSize(); } @@ -89,7 +89,7 @@ namespace WeirdEngine // Process DotClass instances int dotIdx = 0; - ecs.forEach( + registry.forEach( [&](Entity entity, DotClass& dotComp, Transform& t) { data[dotIdx].x = t.position.x; @@ -103,7 +103,7 @@ namespace WeirdEngine // Text int dotIndex = 0; - ecs.forEach( + registry.forEach( [&](Entity entity, TextClass& text, Transform& t) { int charCount = static_cast(text.text.length()); @@ -161,7 +161,7 @@ namespace WeirdEngine // Process ShapeClass instances int shapeIdx = 0; - ecs.forEach( + registry.forEach( [&](Entity entity, ShapeClass& shapeComp) { // Assuming ShapeClass has m_parameters[0] through m_parameters[7] diff --git a/include/weird-engine/systems/SDFShaderGenerationSystem.h b/include/weird-engine/systems/SDFShaderGenerationSystem.h index 60c86a2..c51b6c8 100644 --- a/include/weird-engine/systems/SDFShaderGenerationSystem.h +++ b/include/weird-engine/systems/SDFShaderGenerationSystem.h @@ -1,6 +1,6 @@ #pragma once -#include "weird-engine/ecs/ECS.h" +#include "weird-engine/ecs/Registry.h" #include "weird-engine/Input.h" #include "weird-renderer/components/CustomShape.h" #include "weird-renderer/resources/Shader.h" @@ -19,10 +19,10 @@ namespace WeirdEngine::SDFShaderGenerationSystem { template - inline void update(ECSManager& ecs, RenderContext& ctx, WeirdRenderer::Shader& shader, + inline void update(Registry& registry, RenderContext& ctx, WeirdRenderer::Shader& shader, const std::vector>& sdfs) { - const auto componentArray = ecs.getComponentManager()->getComponentArray(); + const auto componentArray = registry.getComponentManager()->getComponentArray(); if (!ctx.shapesNeedUpdate) { diff --git a/include/weird-physics/components/DistanceConstraint.h b/include/weird-physics/components/DistanceConstraint.h index 101bed0..b22be96 100644 --- a/include/weird-physics/components/DistanceConstraint.h +++ b/include/weird-physics/components/DistanceConstraint.h @@ -1,6 +1,6 @@ #pragma once -#include "weird-engine/ecs/ECS.h" +#include "weird-engine/ecs/Registry.h" namespace WeirdEngine { diff --git a/include/weird-physics/components/DistanceConstraintManager.h b/include/weird-physics/components/DistanceConstraintManager.h index 7a7d352..d262082 100644 --- a/include/weird-physics/components/DistanceConstraintManager.h +++ b/include/weird-physics/components/DistanceConstraintManager.h @@ -11,12 +11,12 @@ namespace WeirdEngine { private: Simulation2D* m_simulation; - ECSManager* m_ecs; + Registry* m_registry; public: - DistanceConstraintManager(Simulation2D& simulation, ECSManager& ecs) + DistanceConstraintManager(Simulation2D& simulation, Registry& registry) : m_simulation(&simulation) - , m_ecs(&ecs) + , m_registry(®istry) { } @@ -25,11 +25,11 @@ namespace WeirdEngine auto componentArray = std::static_pointer_cast>(m_componentArray); DistanceConstraint& removedConstraint = componentArray->getDataFromEntity(entity); - if (m_ecs->hasComponent(removedConstraint.entityA) && - m_ecs->hasComponent(removedConstraint.entityB)) + if (m_registry->hasComponent(removedConstraint.entityA) && + m_registry->hasComponent(removedConstraint.entityB)) { - auto simIdA = m_ecs->getComponent(removedConstraint.entityA).simulationId; - auto simIdB = m_ecs->getComponent(removedConstraint.entityB).simulationId; + auto simIdA = m_registry->getComponent(removedConstraint.entityA).simulationId; + auto simIdB = m_registry->getComponent(removedConstraint.entityB).simulationId; m_simulation->removeDistanceConstraint(simIdA, simIdB); } } diff --git a/include/weird-physics/components/Spring.h b/include/weird-physics/components/Spring.h index 673c0b9..2c45c97 100644 --- a/include/weird-physics/components/Spring.h +++ b/include/weird-physics/components/Spring.h @@ -1,6 +1,6 @@ #pragma once -#include "weird-engine/ecs/ECS.h" +#include "weird-engine/ecs/Registry.h" namespace WeirdEngine { diff --git a/include/weird-physics/components/SpringManager.h b/include/weird-physics/components/SpringManager.h index ea60ffc..e2f6078 100644 --- a/include/weird-physics/components/SpringManager.h +++ b/include/weird-physics/components/SpringManager.h @@ -11,12 +11,12 @@ namespace WeirdEngine { private: Simulation2D* m_simulation; - ECSManager* m_ecs; + Registry* m_registry; public: - SpringManager(Simulation2D& simulation, ECSManager& ecs) + SpringManager(Simulation2D& simulation, Registry& registry) : m_simulation(&simulation) - , m_ecs(&ecs) + , m_registry(®istry) { } @@ -25,11 +25,11 @@ namespace WeirdEngine auto componentArray = std::static_pointer_cast>(m_componentArray); Spring& removedSpring = componentArray->getDataFromEntity(entity); - if (m_ecs->hasComponent(removedSpring.entityA) && - m_ecs->hasComponent(removedSpring.entityB)) + if (m_registry->hasComponent(removedSpring.entityA) && + m_registry->hasComponent(removedSpring.entityB)) { - auto simIdA = m_ecs->getComponent(removedSpring.entityA).simulationId; - auto simIdB = m_ecs->getComponent(removedSpring.entityB).simulationId; + auto simIdA = m_registry->getComponent(removedSpring.entityA).simulationId; + auto simIdB = m_registry->getComponent(removedSpring.entityB).simulationId; // DistanceConstraints and Springs are treated identically for removal in Simulation2D m_simulation->removeDistanceConstraint(simIdA, simIdB); } diff --git a/src/weird-engine/Scene.cpp b/src/weird-engine/Scene.cpp index c7f4bbe..70fc237 100644 --- a/src/weird-engine/Scene.cpp +++ b/src/weird-engine/Scene.cpp @@ -66,46 +66,46 @@ namespace WeirdEngine void Scene::start() { - onCreate(m_ecs, m_services); + onCreate(m_registry, m_services); for (auto& sys : m_createSystems) { - sys(m_ecs, m_services); + sys(m_registry, m_services); } // Custom component managers std::shared_ptr rbManager = std::make_shared(m_simulation2D); - m_ecs.registerComponent(rbManager); + m_registry.registerComponent(rbManager); if (m_renderMode == RenderMode::RayMarching2D) { std::shared_ptr shapeManager = std::make_shared(m_simulation2D, m_2DWorldRenderContext); - m_ecs.registerComponent(shapeManager); + m_registry.registerComponent(shapeManager); } else { std::shared_ptr shapeManager = std::make_shared(m_simulation2D, m_3DWorldRenderContext); - m_ecs.registerComponent(shapeManager); + m_registry.registerComponent(shapeManager); } std::shared_ptr distManager = - std::make_shared(m_simulation2D, m_ecs); - m_ecs.registerComponent(distManager); + std::make_shared(m_simulation2D, m_registry); + m_registry.registerComponent(distManager); - std::shared_ptr springManager = std::make_shared(m_simulation2D, m_ecs); - m_ecs.registerComponent(springManager); + std::shared_ptr springManager = std::make_shared(m_simulation2D, m_registry); + m_registry.registerComponent(springManager); std::shared_ptr uiShapeManager = std::make_shared(m_UIRenderContext); - m_ecs.registerComponent(uiShapeManager); + m_registry.registerComponent(uiShapeManager); // Shapes m_sdfs = Scene::getGlobalSDFs(); m_simulation2D.setSDFs(m_sdfs); // Initialize simulation - PhysicsSystem2D::init(m_ecs, m_simulation2D); + PhysicsSystem2D::init(m_registry, m_simulation2D); // Start simulation if different thread if (m_runSimulationInThread) @@ -127,11 +127,11 @@ namespace WeirdEngine defaultMaterial.roughness = 0.1f; // Create camera - m_mainCamera = m_ecs.createEntity(); + m_mainCamera = m_registry.createEntity(); m_services.tags().tag(m_mainCamera, "mainCamera"); - Transform& t = m_ecs.addComponent(m_mainCamera); + Transform& t = m_registry.addComponent(m_mainCamera); t.rotation = vec3(0, 0, -1.0f); - ECS::Camera& c = m_ecs.addComponent(m_mainCamera); + ECS::Camera& c = m_registry.addComponent(m_mainCamera); // If a .weird file path was provided (via setSceneFilePath / registerScene), // restore saved scene state before the derived class's onStart() runs. @@ -140,31 +140,31 @@ namespace WeirdEngine SceneSerializer::load(*this, m_sceneFilePath); } - onStart(m_ecs, m_services); + onStart(m_registry, m_services); for (auto& sys : m_startSystems) { - sys(m_ecs, m_services); + sys(m_registry, m_services); } switch (m_renderMode) { case WeirdEngine::Scene::RenderMode::RayMarching3D: { - FlyMovement& fly = m_ecs.addComponent(m_mainCamera); + FlyMovement& fly = m_registry.addComponent(m_mainCamera); break; } case WeirdEngine::Scene::RenderMode::RayMarching2D: case WeirdEngine::Scene::RenderMode::RayMarchingBoth: { - FlyMovement2D& fly = m_ecs.addComponent(m_mainCamera); - fly.targetPosition = m_ecs.getComponent(m_mainCamera).position; + FlyMovement2D& fly = m_registry.addComponent(m_mainCamera); + fly.targetPosition = m_registry.getComponent(m_mainCamera).position; break; } default: break; } - PhysicsSystem2D::update(m_ecs, m_simulation2D); + PhysicsSystem2D::update(m_registry, m_simulation2D); } void Scene::update(double delta, double time) @@ -182,21 +182,21 @@ namespace WeirdEngine { if (m_debugFly) { - PlayerMovementSystem::update(m_ecs, static_cast(delta)); + PlayerMovementSystem::update(m_registry, static_cast(delta)); } - CameraSystem::update(m_ecs); + CameraSystem::update(m_registry); } - ButtonSystem::update(m_ecs, m_sdfs, getTime()); + ButtonSystem::update(m_registry, m_sdfs, getTime()); { PROFILE_SCOPE("Physics synchronization"); - PhysicsSystem2D::update(m_ecs, m_simulation2D); + PhysicsSystem2D::update(m_registry, m_simulation2D); if (m_debugInput) { - PhysicsInteractionSystem::update(m_ecs); + PhysicsInteractionSystem::update(m_registry); } m_simulation2D.update(delta); @@ -218,26 +218,26 @@ namespace WeirdEngine std::swap(shapeCollisions, m_queuedShapeCollisions); } - auto rigidBodies = m_ecs.getComponentArray(); + auto rigidBodies = m_registry.getComponentArray(); for (auto& ev : collisions) { EntityCollisionEvent entityEvent{ev, getEntityForSimulationId(ev.bodyA, rigidBodies), getEntityForSimulationId(ev.bodyB, rigidBodies)}; - onEntityCollision(m_ecs, m_services, entityEvent); + onEntityCollision(m_registry, m_services, entityEvent); for (auto& sys : m_entityCollisionSystems) { - sys(m_ecs, m_services, entityEvent); + sys(m_registry, m_services, entityEvent); } } for (auto& ev : shapeCollisions) { EntityShapeCollisionEvent entityEvent{ev, getEntityForSimulationId(ev.body, rigidBodies)}; - onEntityShapeCollision(m_ecs, m_services, entityEvent); + onEntityShapeCollision(m_registry, m_services, entityEvent); for (auto& sys : m_entityShapeCollisionSystems) { - sys(m_ecs, m_services, entityEvent); + sys(m_registry, m_services, entityEvent); } const float m_soundFalloff = 0.1f; @@ -269,19 +269,19 @@ namespace WeirdEngine { PROFILE_SCOPE("OnUpdate"); - onUpdate(m_ecs, m_services); + onUpdate(m_registry, m_services); for (auto& sys : m_updateSystems) { - sys(m_ecs, m_services); + sys(m_registry, m_services); } } { PROFILE_SCOPE("Render Queue update"); - RenderSystem::update(m_ecs, m_resourceManager, m_drawQueue, m_lights); + RenderSystem::update(m_registry, m_resourceManager, m_drawQueue, m_lights); } - m_ecs.freeRemovedComponents(); + m_registry.freeRemovedComponents(); } float Scene::getTime() @@ -324,43 +324,43 @@ namespace WeirdEngine WeirdRenderer::Camera& Scene::getCamera() { - return m_ecs.getComponent(m_mainCamera).camera; + return m_registry.getComponent(m_mainCamera).camera; } void Scene::get2DShapesData(vec4*& data, uint32_t& size, uint32_t& customShapeCount) { // PROFILE_SCOPE("Fetch World Data"); - customShapeCount = m_ecs.getComponentArray()->getSize(); - SDFRenderSystem::update(m_ecs, m_2DWorldRenderContext, data, size); + customShapeCount = m_registry.getComponentArray()->getSize(); + SDFRenderSystem::update(m_registry, m_2DWorldRenderContext, data, size); } void Scene::get3DShapesData(vec4*& data, uint32_t& size, uint32_t& customShapeCount) { // PROFILE_SCOPE("Fetch 3D World Data"); - customShapeCount = m_ecs.getComponentArray()->getSize(); - SDFRenderSystem::update(m_ecs, m_3DWorldRenderContext, data, size); + customShapeCount = m_registry.getComponentArray()->getSize(); + SDFRenderSystem::update(m_registry, m_3DWorldRenderContext, data, size); } void Scene::getUIData(vec4*& uiData, uint32_t& size, uint32_t& customShapeCount) { // PROFILE_SCOPE("Fetch UI Data"); - customShapeCount = m_ecs.getComponentArray()->getSize(); - SDFRenderSystem::update(m_ecs, m_UIRenderContext, uiData, size); + customShapeCount = m_registry.getComponentArray()->getSize(); + SDFRenderSystem::update(m_registry, m_UIRenderContext, uiData, size); } void Scene::update2DWorldShader(WeirdRenderer::Shader& shader) { - SDFShaderGenerationSystem::update(m_ecs, m_2DWorldRenderContext, shader, m_sdfs); + SDFShaderGenerationSystem::update(m_registry, m_2DWorldRenderContext, shader, m_sdfs); } void Scene::update3DWorldShader(WeirdRenderer::Shader& shader) { - SDFShaderGenerationSystem::update(m_ecs, m_3DWorldRenderContext, shader, m_sdfs); + SDFShaderGenerationSystem::update(m_registry, m_3DWorldRenderContext, shader, m_sdfs); } void Scene::updateUIShader(WeirdRenderer::Shader& shader) { - SDFShaderGenerationSystem::update(m_ecs, m_UIRenderContext, shader, m_sdfs); + SDFShaderGenerationSystem::update(m_registry, m_UIRenderContext, shader, m_sdfs); } void Scene::forceShaderRefresh() @@ -384,7 +384,7 @@ namespace WeirdEngine { if (m_renderMode == RenderMode::RayMarching3D || m_renderMode == RenderMode::RayMarchingBoth) { - onRender(m_ecs, m_services, renderTarget); + onRender(m_registry, m_services, renderTarget); } } @@ -426,11 +426,11 @@ namespace WeirdEngine // ServiceProvider ServiceProvider::ServiceProvider(Scene& scene) - : m_ecs(scene.m_ecs) + : m_registry(scene.m_registry) , m_time(scene.m_simulation2D, scene.m_lastDelta) - , m_physics(scene.m_ecs, scene.m_simulation2D, scene.m_sdfs) - , m_shapes(scene.m_ecs, scene.m_simulation2D, scene.m_sdfs) - , m_render(scene.m_ecs, scene.m_mainCamera, scene.m_2DWorldRenderContext, scene.m_3DWorldRenderContext, + , m_physics(scene.m_registry, scene.m_simulation2D, scene.m_sdfs) + , m_shapes(scene.m_registry, scene.m_simulation2D, scene.m_sdfs) + , m_render(scene.m_registry, scene.m_mainCamera, scene.m_2DWorldRenderContext, scene.m_3DWorldRenderContext, scene.m_UIRenderContext, scene.m_lights, scene.m_background, scene.m_renderMode) , m_materials(scene.m_materials, scene.m_materialCount) , m_audio(scene.m_audioQueue, scene.m_frictionSoundLevelRead) @@ -456,18 +456,18 @@ namespace WeirdEngine TagMap SerializationService::loadWeirdFile(const std::string& path, bool blacklistEntities) { TagMap loadedTags; - Entity firstNewEntity = scene.m_ecs.getEntityCount(); + Entity firstNewEntity = scene.m_registry.getEntityCount(); SceneSerializer::load(scene, path, &loadedTags); if (blacklistEntities) { - Entity lastNewEntity = scene.m_ecs.getEntityCount(); + Entity lastNewEntity = scene.m_registry.getEntityCount(); for (Entity entity = firstNewEntity; entity < lastNewEntity; ++entity) scene.m_serializationBlacklist.insert(entity); } return loadedTags; } - RaymarchResult raymarchScene(ECSManager& ecs, std::vector>& sdfs, + RaymarchResult raymarchScene(Registry& registry, std::vector>& sdfs, Simulation2D& simulation, float time, glm::vec2 origin, glm::vec2 direction, float epsilon, float maxDistance) { @@ -497,9 +497,9 @@ namespace WeirdEngine groups.reserve(16); // Cache the rigid bodies component array to avoid repeated lookups in the ECS during the raymarching loop - auto rigidBodies = ecs.getComponentArray(); + auto rigidBodies = registry.getComponentArray(); - auto shapeArray = ecs.getComponentArray(); + auto shapeArray = registry.getComponentArray(); for (size_t j = 0; j < shapeArray->getSize(); j++) { auto& shape = shapeArray->getDataAtIdx(j); @@ -714,10 +714,10 @@ namespace WeirdEngine ImGui::Separator(); - onImGuiRender(m_ecs, m_services); + onImGuiRender(m_registry, m_services); for (auto& sys : m_imguiSystems) { - sys(m_ecs, m_services); + sys(m_registry, m_services); } ImGui::PopID(); @@ -729,9 +729,9 @@ namespace WeirdEngine ImGui::PushID(label2); - for (Entity e = 0; e < m_ecs.getEntityCount(); ++e) + for (Entity e = 0; e < m_registry.getEntityCount(); ++e) { - std::vector componentIDs = m_ecs.getComponentTypes(e); + std::vector componentIDs = m_registry.getComponentTypes(e); if (componentIDs.empty()) continue; @@ -746,7 +746,7 @@ namespace WeirdEngine for (size_t compID : componentIDs) { - std::string compName = m_ecs.getComponentName(compID); + std::string compName = m_registry.getComponentName(compID); ImGui::BulletText("%s", compName.c_str()); } diff --git a/src/weird-engine/SceneSerializer.cpp b/src/weird-engine/SceneSerializer.cpp index 8e100bc..a848075 100644 --- a/src/weird-engine/SceneSerializer.cpp +++ b/src/weird-engine/SceneSerializer.cpp @@ -20,7 +20,7 @@ namespace WeirdEngine // Save camera state { - auto& camTransform = scene.m_ecs.getComponent(scene.m_mainCamera); + auto& camTransform = scene.m_registry.getComponent(scene.m_mainCamera); j["camera"]["position"] = {camTransform.position.x, camTransform.position.y, camTransform.position.z}; j["camera"]["rotation"] = {camTransform.rotation.x, camTransform.rotation.y, camTransform.rotation.z}; j["camera"]["scale"] = {camTransform.scale.x, camTransform.scale.y, camTransform.scale.z}; @@ -33,12 +33,12 @@ namespace WeirdEngine auto isBlacklisted = [&](Entity e) { return e == scene.m_mainCamera || blacklist.count(e); }; { - auto transformArray = scene.m_ecs.getComponentArray(); - auto customShapeArray = scene.m_ecs.getComponentArray(); - auto uiShapeArray = scene.m_ecs.getComponentArray(); - auto dotArray = scene.m_ecs.getComponentArray(); - auto rigidBodyArray = scene.m_ecs.getComponentArray(); - auto textArray = scene.m_ecs.getComponentArray(); + auto transformArray = scene.m_registry.getComponentArray(); + auto customShapeArray = scene.m_registry.getComponentArray(); + auto uiShapeArray = scene.m_registry.getComponentArray(); + auto dotArray = scene.m_registry.getComponentArray(); + auto rigidBodyArray = scene.m_registry.getComponentArray(); + auto textArray = scene.m_registry.getComponentArray(); std::unordered_map entityMap; @@ -139,7 +139,7 @@ namespace WeirdEngine } // GlobalPhysicsSettings - auto globalSettingsArray = scene.m_ecs.getComponentArray(); + auto globalSettingsArray = scene.m_registry.getComponentArray(); if (globalSettingsArray) { for (size_t i = 0; i < globalSettingsArray->getSize(); i++) @@ -157,7 +157,7 @@ namespace WeirdEngine // Entity order is very important for correct SDF operations // BIG TODO: when entities are added/removed, ensure this order is maintained // (deleted entitiy ids can be reused) - for (Entity e = 0; e < scene.m_ecs.getEntityCount(); ++e) + for (Entity e = 0; e < scene.m_registry.getEntityCount(); ++e) { if (isBlacklisted(e)) continue; @@ -187,7 +187,7 @@ namespace WeirdEngine // Save physics constraints directly from ECS components { json distanceConstraintsJson = json::array(); - auto distConstraintArray = scene.m_ecs.getComponentArray(); + auto distConstraintArray = scene.m_registry.getComponentArray(); if (distConstraintArray) { for (size_t i = 0; i < distConstraintArray->getSize(); i++) @@ -202,7 +202,7 @@ namespace WeirdEngine } json springsJson = json::array(); - auto springArray = scene.m_ecs.getComponentArray(); + auto springArray = scene.m_registry.getComponentArray(); if (springArray) { for (size_t i = 0; i < springArray->getSize(); i++) @@ -256,12 +256,12 @@ namespace WeirdEngine // Restore camera state if (j.contains("camera")) { - auto& camTransform = scene.m_ecs.getComponent(scene.m_mainCamera); + auto& camTransform = scene.m_registry.getComponent(scene.m_mainCamera); const auto& cam = j["camera"]; if (cam.contains("position")) { camTransform.position = vec3(cam["position"][0], cam["position"][1], cam["position"][2]); - scene.m_ecs.getComponentArray()->setEntityDirty(scene.m_mainCamera, true); + scene.m_registry.getComponentArray()->setEntityDirty(scene.m_mainCamera, true); } if (cam.contains("rotation")) camTransform.rotation = vec3(cam["rotation"][0], cam["rotation"][1], cam["rotation"][2]); @@ -281,7 +281,7 @@ namespace WeirdEngine { for (const auto& ej : j["entities"]) { - Entity entity = scene.m_ecs.createEntity(); + Entity entity = scene.m_registry.createEntity(); // Track saved-id → new-entity mapping for tag remapping if (ej.contains("id")) @@ -289,7 +289,7 @@ namespace WeirdEngine if (ej.contains("transform")) { - auto& t = scene.m_ecs.addComponent(entity); + auto& t = scene.m_registry.addComponent(entity); const auto& tj = ej["transform"]; if (tj.contains("position")) t.position = vec3(tj["position"][0], tj["position"][1], tj["position"][2]); @@ -297,12 +297,12 @@ namespace WeirdEngine t.rotation = vec3(tj["rotation"][0], tj["rotation"][1], tj["rotation"][2]); if (tj.contains("scale")) t.scale = vec3(tj["scale"][0], tj["scale"][1], tj["scale"][2]); - scene.m_ecs.getComponentArray()->setEntityDirty(entity, true); + scene.m_registry.getComponentArray()->setEntityDirty(entity, true); } if (ej.contains("customShape")) { - auto& s = scene.m_ecs.addComponent(entity); + auto& s = scene.m_registry.addComponent(entity); const auto& sj = ej["customShape"]; s.distanceFieldId = static_cast(sj.value("distanceFieldId", 0)); s.combination = static_cast(sj.value("combination", 0)); @@ -315,13 +315,13 @@ namespace WeirdEngine for (int pi = 0; pi < (int)std::size(s.parameters) && pi < (int)sj["parameters"].size(); pi++) s.parameters[pi] = sj["parameters"][pi].get(); } - scene.m_ecs.getComponentArray()->setEntityDirty(entity, true); + scene.m_registry.getComponentArray()->setEntityDirty(entity, true); scene.m_2DWorldRenderContext.shapesNeedUpdate = true; } if (ej.contains("uiShape")) { - auto& s = scene.m_ecs.addComponent(entity); + auto& s = scene.m_registry.addComponent(entity); const auto& sj = ej["uiShape"]; s.distanceFieldId = static_cast(sj.value("distanceFieldId", 0)); s.combination = static_cast(sj.value("combination", 0)); @@ -338,7 +338,7 @@ namespace WeirdEngine if (ej.contains("dot")) { - auto& r = scene.m_ecs.addComponent(entity); + auto& r = scene.m_registry.addComponent(entity); const auto& rj = ej["dot"]; r.isStatic = rj.value("isStatic", false); r.materialId = static_cast(rj.value("materialId", 0)); @@ -346,27 +346,27 @@ namespace WeirdEngine if (ej.contains("textRenderer")) { - auto& tr = scene.m_ecs.addComponent(entity); + auto& tr = scene.m_registry.addComponent(entity); const auto& trj = ej["textRenderer"]; tr.text = trj.value("text", std::string{}); tr.material = static_cast(trj.value("material", 0)); tr.width = trj.value("width", 0.0f); tr.height = trj.value("height", 0.0f); - scene.m_ecs.getComponentArray()->setEntityDirty(entity, true); + scene.m_registry.getComponentArray()->setEntityDirty(entity, true); } if (ej.contains("globalPhysicsSettings")) { - auto& gs = scene.m_ecs.addComponent(entity); + auto& gs = scene.m_registry.addComponent(entity); const auto& gsj = ej["globalPhysicsSettings"]; gs.gravity = gsj.value("gravity", 0.0f); gs.damping = gsj.value("damping", 0.05f); - scene.m_ecs.getComponentArray()->setEntityDirty(entity, true); + scene.m_registry.getComponentArray()->setEntityDirty(entity, true); } if (ej.contains("rigidBody2D")) { - auto& rb = scene.m_ecs.addComponent(entity); + auto& rb = scene.m_registry.addComponent(entity); const auto& rbj = ej["rigidBody2D"]; int savedSimId = rbj.value("simulationId", -1); @@ -378,7 +378,7 @@ namespace WeirdEngine { rb.isFixed = rbj.value("isFixed", false); } - scene.m_ecs.getComponentArray()->setEntityDirty( + scene.m_registry.getComponentArray()->setEntityDirty( entity, true); // Sync velocity and fixed state to simulation if (rbj.contains("physicsPosition")) @@ -465,16 +465,17 @@ namespace WeirdEngine { if (k >= 1.0f) { - Entity constraintEnt = scene.m_ecs.createEntity(); - auto& constraint = scene.m_ecs.addComponent(constraintEnt); + Entity constraintEnt = scene.m_registry.createEntity(); + auto& constraint = + scene.m_registry.addComponent(constraintEnt); constraint.entityA = entityA; constraint.entityB = entityB; constraint.distance = dist; } else { - Entity springEnt = scene.m_ecs.createEntity(); - auto& spring = scene.m_ecs.addComponent(springEnt); + Entity springEnt = scene.m_registry.createEntity(); + auto& spring = scene.m_registry.addComponent(springEnt); spring.entityA = entityA; spring.entityB = entityB; spring.stiffness = k; @@ -493,8 +494,8 @@ namespace WeirdEngine if (entityIdMap.find(savedA) != entityIdMap.end() && entityIdMap.find(savedB) != entityIdMap.end()) { - Entity springEnt = scene.m_ecs.createEntity(); - auto& spring = scene.m_ecs.addComponent(springEnt); + Entity springEnt = scene.m_registry.createEntity(); + auto& spring = scene.m_registry.addComponent(springEnt); spring.entityA = entityIdMap[savedA]; spring.entityB = entityIdMap[savedB]; spring.stiffness = spj.value("k", 1.0f); @@ -529,11 +530,11 @@ namespace WeirdEngine if (it != simIdMap.end()) { Entity e = simIdToEntityMap[savedId]; - if (scene.m_ecs.hasComponent(e)) + if (scene.m_registry.hasComponent(e)) { - auto& rb = scene.m_ecs.getComponent(e); + auto& rb = scene.m_registry.getComponent(e); rb.isFixed = true; - scene.m_ecs.getComponentArray()->setEntityDirty(e, true); + scene.m_registry.getComponentArray()->setEntityDirty(e, true); } // The actual fix will happen in PhysicsSystem2D::update thanks to isDirty=true } diff --git a/tools/molecule-editor/include/MoleculeEditor.h b/tools/molecule-editor/include/MoleculeEditor.h index 4b91b73..1705c3e 100644 --- a/tools/molecule-editor/include/MoleculeEditor.h +++ b/tools/molecule-editor/include/MoleculeEditor.h @@ -28,7 +28,7 @@ class MoleculeEditor : public Scene2D public: MoleculeEditor() {} - ECSManager* m_tempEcs = nullptr; + Registry* m_tempRegistry = nullptr; ServiceProvider* m_tempSvc = nullptr; private: @@ -126,22 +126,22 @@ class MoleculeEditor : public Scene2D static constexpr float TAG_INNER_RADIUS = 25.0f; static constexpr int TAG_RING_GROUP = 8; - void onStart(ECSManager& ecs, ServiceProvider& services) override + void onStart(Registry& registry, ServiceProvider& services) override { - m_tempEcs = &ecs; + m_tempRegistry = ®istry; m_tempSvc = &services; m_tempSvc->debug().setDebugFly(true); g_cameraPositon.x = 0.0f; g_cameraPositon.y = 0.0f; - m_tempEcs->getComponent(m_tempSvc->render().getCameraEntity()).position = g_cameraPositon; + m_tempRegistry->getComponent(m_tempSvc->render().getCameraEntity()).position = g_cameraPositon; // Request neutral simulation behavior for this editor scene. - Entity globalSettingsEnt = m_tempEcs->createEntity(); - auto& settings = m_tempEcs->addComponent(globalSettingsEnt); + Entity globalSettingsEnt = m_tempRegistry->createEntity(); + auto& settings = m_tempRegistry->addComponent(globalSettingsEnt); settings.gravity = 0.0f; settings.damping = 1.0f; - m_tempEcs->setComponentDirty(settings); + m_tempRegistry->setComponentDirty(settings); buildMaterialPalette(); buildToolbar(); @@ -161,11 +161,11 @@ class MoleculeEditor : public Scene2D } } - void onUpdate(ECSManager& ecs, ServiceProvider& services) override + void onUpdate(Registry& registry, ServiceProvider& services) override { - m_tempEcs = &ecs; + m_tempRegistry = ®istry; m_tempSvc = &services; - g_cameraPositon = m_tempEcs->getComponent(m_tempSvc->render().getCameraEntity()).position; + g_cameraPositon = m_tempRegistry->getComponent(m_tempSvc->render().getCameraEntity()).position; if (m_tempSvc->input().getKeyDown(Input::Q) || m_tempSvc->input().getGamepadButtonDown(Input::GamepadButton::North)) @@ -252,7 +252,7 @@ class MoleculeEditor : public Scene2D continue; } - const auto& t = m_tempEcs->getComponent(b.entity); + const auto& t = m_tempRegistry->getComponent(b.entity); if (t.position.y < -1000.0f) { toDelete.push_back(b.entity); @@ -267,7 +267,7 @@ class MoleculeEditor : public Scene2D for (Entity e : toDelete) { - m_tempEcs->destroyEntity(e); + m_tempRegistry->destroyEntity(e); } m_links.erase(std::remove_if(m_links.begin(), m_links.end(), @@ -278,7 +278,7 @@ class MoleculeEditor : public Scene2D { if (m_draggedLink == &link) m_draggedLink = nullptr; - m_tempEcs->destroyEntity(link.lineEntity); + m_tempRegistry->destroyEntity(link.lineEntity); } return remove; }), @@ -318,7 +318,7 @@ class MoleculeEditor : public Scene2D UIShape& sh = m_tempSvc->shapes().addUIShape(DefaultShapes::CIRCLE, p, e); sh.material = static_cast(i); - auto& tog = m_tempEcs->addComponent(e); + auto& tog = m_tempRegistry->addComponent(e); tog.clickPadding = BTN_SIZE + 3.0f; tog.parameterModifierMask.set(2); tog.modifierAmount = 5.0f; @@ -327,7 +327,7 @@ class MoleculeEditor : public Scene2D m_tempSvc->serialization().blacklistEntity(e); } - m_tempEcs->getComponent(m_materialToggles[m_selectedMaterial]).active = true; + m_tempRegistry->getComponent(m_materialToggles[m_selectedMaterial]).active = true; } void syncMaterialPalette() @@ -335,7 +335,7 @@ class MoleculeEditor : public Scene2D int activated = -1; for (int i = 0; i < 16; i++) { - auto& t = m_tempEcs->getComponent(m_materialToggles[i]); + auto& t = m_tempRegistry->getComponent(m_materialToggles[i]); if (t.active && t.state == ButtonState::Down) { activated = i; @@ -350,7 +350,7 @@ class MoleculeEditor : public Scene2D { if (i != activated) { - m_tempEcs->getComponent(m_materialToggles[i]).active = false; + m_tempRegistry->getComponent(m_materialToggles[i]).active = false; } } } @@ -358,7 +358,7 @@ class MoleculeEditor : public Scene2D { for (int i = 0; i < 16; i++) { - if (m_tempEcs->getComponent(m_materialToggles[i]).active) + if (m_tempRegistry->getComponent(m_materialToggles[i]).active) { m_selectedMaterial = i; break; @@ -375,7 +375,7 @@ class MoleculeEditor : public Scene2D float y = (Display::height - TOOL_Y_START) - (i * TOOL_SPACING); float p[8]{TOOL_X, y, TOOL_BTN_HALF, TOOL_BTN_HALF}; Entity e = m_tempSvc->shapes().addUIShape(DefaultShapes::BOX, p, static_cast(2)); - auto& tog = m_tempEcs->addComponent(e); + auto& tog = m_tempRegistry->addComponent(e); tog.clickPadding = TOOL_BTN_HALF + 8.0f; tog.parameterModifierMask.set(2); tog.parameterModifierMask.set(3); @@ -383,21 +383,21 @@ class MoleculeEditor : public Scene2D m_toolToggles[i] = e; m_tempSvc->serialization().blacklistEntity(e); - Entity lbl = m_tempEcs->createEntity(); - auto& lt = m_tempEcs->addComponent(lbl); + Entity lbl = m_tempRegistry->createEntity(); + auto& lt = m_tempRegistry->addComponent(lbl); lt.position = vec3(TOOL_X + TOOL_BTN_HALF + 20.0f, y, 0.0f); - auto& tx = m_tempEcs->addComponent(lbl); + auto& tx = m_tempRegistry->addComponent(lbl); tx.text = labels[i]; tx.material = 1; tx.horizontalAlignment = TextRenderer::HorizontalAlignment::Left; tx.verticalAlignment = TextRenderer::VerticalAlignment::Center; m_tempSvc->serialization().blacklistEntity(lbl); } - m_tempEcs->getComponent(m_toolToggles[0]).active = true; + m_tempRegistry->getComponent(m_toolToggles[0]).active = true; float starP[8]{Display::width - GRAV_Y, Display::height - GRAV_Y, GRAV_Y * 0.5f, 5.0f, 10.0f, 0.0f}; m_gravityToggleEntity = m_tempSvc->shapes().addUIShape(DefaultShapes::STAR, starP, static_cast(2)); - auto& gravTog = m_tempEcs->addComponent(m_gravityToggleEntity); + auto& gravTog = m_tempRegistry->addComponent(m_gravityToggleEntity); gravTog.clickPadding = 18.0f; // gravTog.parameterModifierMask.set(2); gravTog.parameterModifierMask.set(5); @@ -406,7 +406,7 @@ class MoleculeEditor : public Scene2D float gridP[8]{Display::width - GRAV_Y, Display::height - GRID_Y, 12.0f, 12.0f}; m_gridToggleEntity = m_tempSvc->shapes().addUIShape(DefaultShapes::BOX, gridP, static_cast(2)); - auto& gridTog = m_tempEcs->addComponent(m_gridToggleEntity); + auto& gridTog = m_tempRegistry->addComponent(m_gridToggleEntity); gridTog.clickPadding = 18.0f; gridTog.parameterModifierMask.set(2); gridTog.parameterModifierMask.set(3); @@ -419,7 +419,7 @@ class MoleculeEditor : public Scene2D int activated = -1; for (int i = 0; i < 6; i++) { - auto& t = m_tempEcs->getComponent(m_toolToggles[i]); + auto& t = m_tempRegistry->getComponent(m_toolToggles[i]); if (t.active && t.state == ButtonState::Down) { activated = i; @@ -432,17 +432,17 @@ class MoleculeEditor : public Scene2D for (int i = 0; i < 6; i++) { if (i != activated) - m_tempEcs->getComponent(m_toolToggles[i]).active = false; + m_tempRegistry->getComponent(m_toolToggles[i]).active = false; } } - auto& gravTog = m_tempEcs->getComponent(m_gravityToggleEntity); + auto& gravTog = m_tempRegistry->getComponent(m_gravityToggleEntity); bool wantsGravity = gravTog.active; if (wantsGravity != m_gravityEnabled) { m_gravityEnabled = wantsGravity; - auto& starShape = m_tempEcs->getComponent(m_gravityToggleEntity); - auto globalSettingsArray = m_tempEcs->getComponentArray(); + auto& starShape = m_tempRegistry->getComponent(m_gravityToggleEntity); + auto globalSettingsArray = m_tempRegistry->getComponentArray(); auto& settings = globalSettingsArray->getDataAtIdx(0); if (m_gravityEnabled) { @@ -454,38 +454,38 @@ class MoleculeEditor : public Scene2D settings.gravity = 0.0f; settings.damping = 1.0f; } - m_tempEcs->setComponentDirty(settings); + m_tempRegistry->setComponentDirty(settings); } - m_gridMode = m_tempEcs->getComponent(m_gridToggleEntity).active; + m_gridMode = m_tempRegistry->getComponent(m_gridToggleEntity).active; } void spawnBallAtMouse() { - auto& cam = m_tempEcs->getComponent(m_tempSvc->render().getCameraEntity()); + auto& cam = m_tempRegistry->getComponent(m_tempSvc->render().getCameraEntity()); vec2 world = ECS::Camera::screenPositionToWorldPosition2D( cam, vec2(m_tempSvc->input().getMouseX(), m_tempSvc->input().getMouseY())); if (m_gridMode) world = snapToGrid(world); - Entity e = m_tempEcs->createEntity(); + Entity e = m_tempRegistry->createEntity(); - auto& t = m_tempEcs->addComponent(e); + auto& t = m_tempRegistry->addComponent(e); t.position = vec3(world.x, world.y, 0.0f); - m_tempEcs->setComponentDirty(t); + m_tempRegistry->setComponentDirty(t); - auto& sdf = m_tempEcs->addComponent(e); + auto& sdf = m_tempRegistry->addComponent(e); sdf.materialId = static_cast(m_selectedMaterial); - auto& rb = m_tempEcs->addComponent(e); + auto& rb = m_tempRegistry->addComponent(e); m_balls.push_back({e, static_cast(rb.simulationId)}); } vec2 getMouseWorldPosition() { - auto& cam = m_tempEcs->getComponent(m_tempSvc->render().getCameraEntity()); + auto& cam = m_tempRegistry->getComponent(m_tempSvc->render().getCameraEntity()); return ECS::Camera::screenPositionToWorldPosition2D( cam, vec2(m_tempSvc->input().getMouseX(), m_tempSvc->input().getMouseY())); } @@ -540,9 +540,9 @@ class MoleculeEditor : public Scene2D { if (b.entity == exclude) continue; - if (!m_tempEcs->hasComponent(b.entity)) + if (!m_tempRegistry->hasComponent(b.entity)) continue; - const auto& t = m_tempEcs->getComponent(b.entity); + const auto& t = m_tempRegistry->getComponent(b.entity); vec2 p(t.position.x, t.position.y); if (std::abs(p.x - cell.x) < threshold && std::abs(p.y - cell.y) < threshold) return true; @@ -617,16 +617,16 @@ class MoleculeEditor : public Scene2D m_draggedSimulationId = id; m_keepFixedAfterDrag = false; - auto& rb = m_tempEcs->getComponent(m_draggedBall); + auto& rb = m_tempRegistry->getComponent(m_draggedBall); rb.isFixed = true; - m_tempEcs->setComponentDirty(rb); + m_tempRegistry->setComponentDirty(rb); vec2 startPos = getMouseWorldPosition(); if (m_gridMode) startPos = snapToGrid(startPos, m_draggedBall); - auto& t = m_tempEcs->getComponent(m_draggedBall); + auto& t = m_tempRegistry->getComponent(m_draggedBall); t.position = vec3(startPos.x, startPos.y, 0.0f); - m_tempEcs->setComponentDirty(t); + m_tempRegistry->setComponentDirty(t); } void onRightDragUpdate() @@ -642,9 +642,9 @@ class MoleculeEditor : public Scene2D vec2 dragPos = getMouseWorldPosition(); if (m_gridMode) dragPos = snapToGrid(dragPos, m_draggedBall); - auto& t = m_tempEcs->getComponent(m_draggedBall); + auto& t = m_tempRegistry->getComponent(m_draggedBall); t.position = vec3(dragPos.x, dragPos.y, 0.0f); - m_tempEcs->setComponentDirty(t); + m_tempRegistry->setComponentDirty(t); } void onRightDragEnd() @@ -654,9 +654,9 @@ class MoleculeEditor : public Scene2D if (!m_keepFixedAfterDrag) { - auto& rb = m_tempEcs->getComponent(m_draggedBall); + auto& rb = m_tempRegistry->getComponent(m_draggedBall); rb.isFixed = false; - m_tempEcs->setComponentDirty(rb); + m_tempRegistry->setComponentDirty(rb); } m_draggedBall = static_cast(-1); @@ -698,7 +698,7 @@ class MoleculeEditor : public Scene2D Entity pickBallAtMouse() { - auto& cam = m_tempEcs->getComponent(m_tempSvc->render().getCameraEntity()); + auto& cam = m_tempRegistry->getComponent(m_tempSvc->render().getCameraEntity()); vec2 world = ECS::Camera::screenPositionToWorldPosition2D( cam, vec2(m_tempSvc->input().getMouseX(), m_tempSvc->input().getMouseY())); @@ -710,7 +710,7 @@ class MoleculeEditor : public Scene2D if (!hasTransform(b.entity)) continue; - const auto& t = m_tempEcs->getComponent(b.entity); + const auto& t = m_tempRegistry->getComponent(b.entity); vec2 p(t.position.x, t.position.y); float d = length(world - p); if (d < best) @@ -748,24 +748,24 @@ class MoleculeEditor : public Scene2D if (idA < 0 || idB < 0) return; - auto& ta = m_tempEcs->getComponent(a); - auto& tb = m_tempEcs->getComponent(b); + auto& ta = m_tempRegistry->getComponent(a); + auto& tb = m_tempRegistry->getComponent(b); vec2 pa(ta.position.x, ta.position.y); vec2 pb(tb.position.x, tb.position.y); float restDistance = length(pb - pa); restDistance = (std::max)(restDistance, 1.0f); - Entity constraintEnt = m_tempEcs->createEntity(); + Entity constraintEnt = m_tempRegistry->createEntity(); if (type == LinkType::Distance) { - auto& constraint = m_tempEcs->addComponent(constraintEnt); + auto& constraint = m_tempRegistry->addComponent(constraintEnt); constraint.entityA = a; constraint.entityB = b; constraint.distance = restDistance; } else { - auto& spring = m_tempEcs->addComponent(constraintEnt); + auto& spring = m_tempRegistry->addComponent(constraintEnt); spring.entityA = a; spring.entityB = b; spring.stiffness = SPRING_STIFFNESS; @@ -778,7 +778,7 @@ class MoleculeEditor : public Scene2D computeScreenLineParams(pa, pb, lineVars); Entity line = m_tempSvc->shapes().addUIShape(DefaultShapes::LINE, lineVars, lineColor); - auto& btn = m_tempEcs->addComponent(line); + auto& btn = m_tempRegistry->addComponent(line); btn.clickPadding = 8.0f; btn.modifierAmount = 0.0f; @@ -800,12 +800,12 @@ class MoleculeEditor : public Scene2D continue; } - m_tempEcs->destroyEntity(link.constraintEntity); + m_tempRegistry->destroyEntity(link.constraintEntity); if (m_draggedLink == &link) m_draggedLink = nullptr; - m_tempEcs->destroyEntity(link.lineEntity); + m_tempRegistry->destroyEntity(link.lineEntity); m_links.erase(m_links.begin() + i); } } @@ -819,7 +819,7 @@ class MoleculeEditor : public Scene2D { if (!hasShapeButton(link.lineEntity)) continue; - auto& btn = m_tempEcs->getComponent(link.lineEntity); + auto& btn = m_tempRegistry->getComponent(link.lineEntity); if (btn.state == ButtonState::Down) { m_draggedLink = &link; @@ -851,22 +851,22 @@ class MoleculeEditor : public Scene2D void updateConstraintLines() { - auto& cam = m_tempEcs->getComponent(m_tempSvc->render().getCameraEntity()); + auto& cam = m_tempRegistry->getComponent(m_tempSvc->render().getCameraEntity()); for (auto& link : m_links) { if (!hasTransform(link.a) || !hasTransform(link.b) || !hasUIShape(link.lineEntity)) continue; - const auto& ta = m_tempEcs->getComponent(link.a); - const auto& tb = m_tempEcs->getComponent(link.b); + const auto& ta = m_tempRegistry->getComponent(link.a); + const auto& tb = m_tempRegistry->getComponent(link.b); vec2 aWorld(ta.position.x, ta.position.y); vec2 bWorld(tb.position.x, tb.position.y); vec2 aScreen = ECS::Camera::worldPosition2DToScreenPosition(cam, aWorld); vec2 bScreen = ECS::Camera::worldPosition2DToScreenPosition(cam, bWorld); - auto& ui = m_tempEcs->getComponent(link.lineEntity); + auto& ui = m_tempRegistry->getComponent(link.lineEntity); ui.parameters[0] = aScreen.x; ui.parameters[1] = aScreen.y; ui.parameters[2] = bScreen.x; @@ -877,7 +877,7 @@ class MoleculeEditor : public Scene2D void computeScreenLineParams(const vec2& aWorld, const vec2& bWorld, float outParams[8]) { - auto& cam = m_tempEcs->getComponent(m_tempSvc->render().getCameraEntity()); + auto& cam = m_tempRegistry->getComponent(m_tempSvc->render().getCameraEntity()); vec2 aScreen = ECS::Camera::worldPosition2DToScreenPosition(cam, aWorld); vec2 bScreen = ECS::Camera::worldPosition2DToScreenPosition(cam, bWorld); @@ -906,10 +906,10 @@ class MoleculeEditor : public Scene2D { // Tag label – shows "tag: " or "tag: (none)" { - Entity lbl = m_tempEcs->createEntity(); - auto& lt = m_tempEcs->addComponent(lbl); + Entity lbl = m_tempRegistry->createEntity(); + auto& lt = m_tempRegistry->addComponent(lbl); lt.position = vec3(Display::width * 0.5f, 150.0f, 0.0f); - auto& tx = m_tempEcs->addComponent(lbl); + auto& tx = m_tempRegistry->addComponent(lbl); tx.text = ""; tx.material = 1; tx.horizontalAlignment = TextRenderer::HorizontalAlignment::Center; @@ -924,7 +924,7 @@ class MoleculeEditor : public Scene2D static constexpr float BH = 14.0f; float p[8]{Display::width * 0.5f, 90.0f, BW, BH}; m_tagEditButton = m_tempSvc->shapes().addUIShape(DefaultShapes::BOX, p, static_cast(2)); - auto& btn = m_tempEcs->addComponent(m_tagEditButton); + auto& btn = m_tempRegistry->addComponent(m_tagEditButton); btn.clickPadding = 6.0f; btn.modifierAmount = 0.0f; m_tempSvc->serialization().blacklistEntity(m_tagEditButton); @@ -938,12 +938,12 @@ class MoleculeEditor : public Scene2D { if (m_tagCircleOuter != static_cast(-1)) { - m_tempEcs->getComponent(m_tagCircleOuter).parameters[2] = 0.0f; - m_tempEcs->getComponent(m_tagCircleInner).parameters[2] = 0.0f; + m_tempRegistry->getComponent(m_tagCircleOuter).parameters[2] = 0.0f; + m_tempRegistry->getComponent(m_tagCircleInner).parameters[2] = 0.0f; } if (m_tagLabelEntity != static_cast(-1)) { - m_tempEcs->getComponent(m_tagLabelEntity).text = ""; + m_tempRegistry->getComponent(m_tagLabelEntity).text = ""; } return; } @@ -969,17 +969,17 @@ class MoleculeEditor : public Scene2D m_tempSvc->serialization().blacklistEntity(m_tagCircleInner); } - auto& cam = m_tempEcs->getComponent(m_tempSvc->render().getCameraEntity()); - const auto& ht = m_tempEcs->getComponent(m_tagSelectedEntity); + auto& cam = m_tempRegistry->getComponent(m_tempSvc->render().getCameraEntity()); + const auto& ht = m_tempRegistry->getComponent(m_tagSelectedEntity); vec2 world(ht.position.x, ht.position.y); vec2 screen = ECS::Camera::worldPosition2DToScreenPosition(cam, world); - auto& outer = m_tempEcs->getComponent(m_tagCircleOuter); + auto& outer = m_tempRegistry->getComponent(m_tagCircleOuter); outer.parameters[0] = screen.x; outer.parameters[1] = screen.y; outer.parameters[2] = TAG_OUTER_RADIUS; - auto& inner = m_tempEcs->getComponent(m_tagCircleInner); + auto& inner = m_tempRegistry->getComponent(m_tagCircleInner); inner.parameters[0] = screen.x; inner.parameters[1] = screen.y; inner.parameters[2] = TAG_INNER_RADIUS; @@ -988,12 +988,12 @@ class MoleculeEditor : public Scene2D if (m_tagLabelEntity != static_cast(-1)) { std::string currentTag = m_tempSvc->tags().getEntityTag(m_tagSelectedEntity); - auto& tx = m_tempEcs->getComponent(m_tagLabelEntity); + auto& tx = m_tempRegistry->getComponent(m_tagLabelEntity); std::string newText = currentTag.empty() ? "tag: (none)" : ("tag: " + currentTag); if (tx.text != newText) { tx.text = newText; - m_tempEcs->setComponentDirty(tx); + m_tempRegistry->setComponentDirty(tx); } } @@ -1002,7 +1002,7 @@ class MoleculeEditor : public Scene2D // as required by the design of this editor scene. if (m_tagEditButton != static_cast(-1)) { - auto& btn = m_tempEcs->getComponent(m_tagEditButton); + auto& btn = m_tempRegistry->getComponent(m_tagEditButton); if (btn.state == ButtonState::Down) { WeirdEngine::Logger::log("Enter new tag (empty to remove): "); @@ -1031,33 +1031,33 @@ class MoleculeEditor : public Scene2D if (hit == static_cast(-1)) return; - auto& dot = m_tempEcs->getComponent(hit); + auto& dot = m_tempRegistry->getComponent(hit); dot.materialId = static_cast(m_selectedMaterial); } bool hasTransform(Entity e) { - auto arr = m_tempEcs->getComponentArray(); + auto arr = m_tempRegistry->getComponentArray(); return arr->hasData(e); } bool hasUIShape(Entity e) { - auto arr = m_tempEcs->getComponentArray(); + auto arr = m_tempRegistry->getComponentArray(); return arr->hasData(e); } bool hasShapeButton(Entity e) { - auto arr = m_tempEcs->getComponentArray(); + auto arr = m_tempRegistry->getComponentArray(); return arr->hasData(e); } void loadMolecule(const std::string& path) { // Remember constraint count before loading so we can find new ones - size_t prevSpringCount = m_tempEcs->getComponentArray()->getSize(); - size_t prevDistCount = m_tempEcs->getComponentArray()->getSize(); + size_t prevSpringCount = m_tempRegistry->getComponentArray()->getSize(); + size_t prevDistCount = m_tempRegistry->getComponentArray()->getSize(); // Load the file — creates new entities / rigid bodies / constraints TagMap loadedTags = m_tempSvc->serialization().loadWeirdFile(path); @@ -1070,8 +1070,8 @@ class MoleculeEditor : public Scene2D // Collect new balls: find entities with both Dot and RigidBody2D // that are not already tracked - auto dotArray = m_tempEcs->getComponentArray(); - auto rbArray = m_tempEcs->getComponentArray(); + auto dotArray = m_tempRegistry->getComponentArray(); + auto rbArray = m_tempRegistry->getComponentArray(); // Build a set of already-tracked entities for fast lookup std::unordered_set existingBalls; @@ -1096,7 +1096,7 @@ class MoleculeEditor : public Scene2D } // Collect new constraints and create visual links - auto springArray = m_tempEcs->getComponentArray(); + auto springArray = m_tempRegistry->getComponentArray(); size_t newSprings = 0; for (size_t i = prevSpringCount; i < springArray->getSize(); i++) { @@ -1108,29 +1108,29 @@ class MoleculeEditor : public Scene2D if (!hasTransform(a) || !hasTransform(b)) continue; - if (!m_tempEcs->hasComponent(a) || !m_tempEcs->hasComponent(b)) + if (!m_tempRegistry->hasComponent(a) || !m_tempRegistry->hasComponent(b)) continue; auto lineColor = DisplaySettings::Orange; - vec2 pa(m_tempEcs->getComponent(a).position); - vec2 pb(m_tempEcs->getComponent(b).position); + vec2 pa(m_tempRegistry->getComponent(a).position); + vec2 pb(m_tempRegistry->getComponent(b).position); float lineVars[8]{}; computeScreenLineParams(pa, pb, lineVars); Entity line = m_tempSvc->shapes().addUIShape(DefaultShapes::LINE, lineVars, lineColor); - auto& btn = m_tempEcs->addComponent(line); + auto& btn = m_tempRegistry->addComponent(line); btn.clickPadding = 8.0f; btn.modifierAmount = 0.0f; m_tempSvc->serialization().blacklistEntity(line); - int idA = m_tempEcs->getComponent(a).simulationId; - int idB = m_tempEcs->getComponent(b).simulationId; + int idA = m_tempRegistry->getComponent(a).simulationId; + int idB = m_tempRegistry->getComponent(b).simulationId; m_links.push_back({a, b, idA, idB, spring.restDistance, line, LinkType::Spring, springEnt}); newSprings++; } - auto distArray = m_tempEcs->getComponentArray(); + auto distArray = m_tempRegistry->getComponentArray(); size_t newDists = 0; for (size_t i = prevDistCount; i < distArray->getSize(); i++) { @@ -1142,24 +1142,24 @@ class MoleculeEditor : public Scene2D if (!hasTransform(a) || !hasTransform(b)) continue; - if (!m_tempEcs->hasComponent(a) || !m_tempEcs->hasComponent(b)) + if (!m_tempRegistry->hasComponent(a) || !m_tempRegistry->hasComponent(b)) continue; auto lineColor = DisplaySettings::Cyan; - vec2 pa(m_tempEcs->getComponent(a).position); - vec2 pb(m_tempEcs->getComponent(b).position); + vec2 pa(m_tempRegistry->getComponent(a).position); + vec2 pb(m_tempRegistry->getComponent(b).position); float lineVars[8]{}; computeScreenLineParams(pa, pb, lineVars); Entity line = m_tempSvc->shapes().addUIShape(DefaultShapes::LINE, lineVars, lineColor); - auto& btn = m_tempEcs->addComponent(line); + auto& btn = m_tempRegistry->addComponent(line); btn.clickPadding = 8.0f; btn.modifierAmount = 0.0f; m_tempSvc->serialization().blacklistEntity(line); - int idA = m_tempEcs->getComponent(a).simulationId; - int idB = m_tempEcs->getComponent(b).simulationId; + int idA = m_tempRegistry->getComponent(a).simulationId; + int idB = m_tempRegistry->getComponent(b).simulationId; m_links.push_back({a, b, idA, idB, dist.distance, line, LinkType::Distance, distEnt}); newDists++; } @@ -1169,10 +1169,10 @@ class MoleculeEditor : public Scene2D WeirdEngine::Logger::log(loadMsg); } - void onEntityShapeCollision(ECSManager& ecs, ServiceProvider& services, + void onEntityShapeCollision(Registry& registry, ServiceProvider& services, WeirdEngine::EntityShapeCollisionEvent& event) override { - m_tempEcs = &ecs; + m_tempRegistry = ®istry; m_tempSvc = &services; } }; diff --git a/tools/scene-editor/include/SceneEditor.h b/tools/scene-editor/include/SceneEditor.h index 63ea7d1..b2c5601 100644 --- a/tools/scene-editor/include/SceneEditor.h +++ b/tools/scene-editor/include/SceneEditor.h @@ -22,7 +22,7 @@ class SceneEditor : public Scene2D { } - ECSManager* m_tempEcs = nullptr; + Registry* m_tempRegistry = nullptr; ServiceProvider* m_tempSvc = nullptr; private: @@ -87,13 +87,13 @@ class SceneEditor : public Scene2D // ===================================================================== // Lifecycle // ===================================================================== - void onStart(ECSManager& ecs, ServiceProvider& services) override + void onStart(Registry& registry, ServiceProvider& services) override { - m_tempEcs = &ecs; + m_tempRegistry = ®istry; m_tempSvc = &services; m_tempSvc->debug().setDebugInput(true); m_tempSvc->debug().setDebugFly(true); - m_tempEcs->getComponent(m_tempSvc->render().getCameraEntity()).position = g_cameraPositon; + m_tempRegistry->getComponent(m_tempSvc->render().getCameraEntity()).position = g_cameraPositon; buildShapeButtons(); buildCombToggles(); @@ -101,11 +101,11 @@ class SceneEditor : public Scene2D buildParamPanel(); } - void onUpdate(ECSManager& ecs, ServiceProvider& services) override + void onUpdate(Registry& registry, ServiceProvider& services) override { - m_tempEcs = &ecs; + m_tempRegistry = ®istry; m_tempSvc = &services; - g_cameraPositon = m_tempEcs->getComponent(m_tempSvc->render().getCameraEntity()).position; + g_cameraPositon = m_tempRegistry->getComponent(m_tempSvc->render().getCameraEntity()).position; if (m_tempSvc->input().getKeyDown(Input::Q) || m_tempSvc->input().getGamepadButtonDown(Input::GamepadButton::North)) @@ -120,7 +120,7 @@ class SceneEditor : public Scene2D onLeftClick(); if (m_tempSvc->input().getMouseButton(Input::LeftClick)) { - auto& cam = m_tempEcs->getComponent(m_tempSvc->render().getCameraEntity()); + auto& cam = m_tempRegistry->getComponent(m_tempSvc->render().getCameraEntity()); vec2 wp = ECS::Camera::screenPositionToWorldPosition2D( cam, vec2(m_tempSvc->input().getMouseX(), m_tempSvc->input().getMouseY())); spawnPhysicsEntity(wp); @@ -136,7 +136,7 @@ class SceneEditor : public Scene2D void destroyOffscreenEntities() { - auto transformArray = m_tempEcs->getComponentArray(); + auto transformArray = m_tempRegistry->getComponentArray(); if (!transformArray) return; @@ -149,7 +149,7 @@ class SceneEditor : public Scene2D if (transform.position.y < -10.0f) { - m_tempEcs->destroyEntity(entity); + m_tempRegistry->destroyEntity(entity); } } } @@ -170,7 +170,7 @@ class SceneEditor : public Scene2D previewParams(types[i], cx, cy, p); Entity e = m_tempSvc->shapes().addUIShape(types[i], p, 2); - auto& b = m_tempEcs->addComponent(e); + auto& b = m_tempRegistry->addComponent(e); b.modifierAmount = 1.0f; b.clickPadding = 8.0f; @@ -256,16 +256,16 @@ class SceneEditor : public Scene2D Entity e2 = m_tempSvc->shapes().addUIShape(DefaultShapes::CIRCLE, p2, static_cast(1), ct[i], g); if (ct[i] == CombinationType::SmoothAddition || ct[i] == CombinationType::SmoothSubtraction) - m_tempEcs->getComponent(e2).smoothFactor = 5.0f; + m_tempRegistry->getComponent(e2).smoothFactor = 5.0f; - auto& tog = m_tempEcs->addComponent(e1); + auto& tog = m_tempRegistry->addComponent(e1); tog.clickPadding = r + 10.0f; tog.parameterModifierMask.set(2); tog.modifierAmount = 3.0f; - Entity lbl = m_tempEcs->createEntity(); - m_tempEcs->addComponent(lbl).position = vec3(cx, cy - 2600.0f, 0.0f); - auto& tx = m_tempEcs->addComponent(lbl); + Entity lbl = m_tempRegistry->createEntity(); + m_tempRegistry->addComponent(lbl).position = vec3(cx, cy - 2600.0f, 0.0f); + auto& tx = m_tempRegistry->addComponent(lbl); tx.text = label[i]; tx.material = 1; tx.horizontalAlignment = TextRenderer::HorizontalAlignment::Center; @@ -275,7 +275,7 @@ class SceneEditor : public Scene2D m_tempSvc->serialization().blacklistEntity(e2); m_tempSvc->serialization().blacklistEntity(lbl); } - m_tempEcs->getComponent(m_combButtons[0].toggleEntity).active = true; + m_tempRegistry->getComponent(m_combButtons[0].toggleEntity).active = true; } // ===================================================================== @@ -291,7 +291,7 @@ class SceneEditor : public Scene2D UIShape& sh = m_tempSvc->shapes().addUIShape(DefaultShapes::CIRCLE, p, e); sh.material = static_cast(i); - auto& tog = m_tempEcs->addComponent(e); + auto& tog = m_tempRegistry->addComponent(e); tog.clickPadding = BTN_SIZE + 3.0f; tog.parameterModifierMask.set(2); tog.modifierAmount = 5.0f; @@ -299,7 +299,7 @@ class SceneEditor : public Scene2D m_materialToggles[i] = e; m_tempSvc->serialization().blacklistEntity(e); } - m_tempEcs->getComponent(m_materialToggles[m_selectedMaterial]).active = true; + m_tempRegistry->getComponent(m_materialToggles[m_selectedMaterial]).active = true; } // ===================================================================== @@ -307,12 +307,12 @@ class SceneEditor : public Scene2D // ===================================================================== void buildParamPanel() { - m_selInfoText = m_tempEcs->createEntity(); - auto& selInfoTf = m_tempEcs->addComponent(m_selInfoText); + m_selInfoText = m_tempRegistry->createEntity(); + auto& selInfoTf = m_tempRegistry->addComponent(m_selInfoText); selInfoTf.position = vec3(HIDDEN, PANEL_TOP_Y + 35.0f, 0.0f); - m_tempEcs->setComponentDirty(selInfoTf); + m_tempRegistry->setComponentDirty(selInfoTf); - auto& hdr = m_tempEcs->addComponent(m_selInfoText); + auto& hdr = m_tempRegistry->addComponent(m_selInfoText); hdr.material = 1; hdr.horizontalAlignment = TextRenderer::HorizontalAlignment::Right; m_tempSvc->serialization().blacklistEntity(m_selInfoText); @@ -323,15 +323,15 @@ class SceneEditor : public Scene2D float bp[8]{HIDDEN, py, P_BTN_W, P_BTN_H}; Entity be = m_tempSvc->shapes().addUIShape(DefaultShapes::BOX, bp, static_cast(3)); - auto& btn = m_tempEcs->addComponent(be); + auto& btn = m_tempRegistry->addComponent(be); btn.modifierAmount = 1.0f; btn.clickPadding = 3.0f; - Entity te = m_tempEcs->createEntity(); - auto& ttf = m_tempEcs->addComponent(te); + Entity te = m_tempRegistry->createEntity(); + auto& ttf = m_tempRegistry->addComponent(te); ttf.position = vec3(HIDDEN, py, 0.0f); - m_tempEcs->setComponentDirty(ttf); - auto& tx = m_tempEcs->addComponent(te); + m_tempRegistry->setComponentDirty(ttf); + auto& tx = m_tempRegistry->addComponent(te); tx.material = 0; tx.horizontalAlignment = TextRenderer::HorizontalAlignment::Right; tx.verticalAlignment = TextRenderer::VerticalAlignment::Center; @@ -349,7 +349,7 @@ class SceneEditor : public Scene2D { for (auto& sb : m_shapeButtons) { - if (m_tempEcs->getComponent(sb.entity).state == ButtonState::Down) + if (m_tempRegistry->getComponent(sb.entity).state == ButtonState::Down) { spawnShape(sb.shapeType); return; @@ -360,7 +360,7 @@ class SceneEditor : public Scene2D { for (int i = 0; i < 8; i++) { - if (m_tempEcs->getComponent(m_paramBtns[i].shapeEntity).state == ButtonState::Down) + if (m_tempRegistry->getComponent(m_paramBtns[i].shapeEntity).state == ButtonState::Down) { promptParam(i); return; @@ -371,7 +371,7 @@ class SceneEditor : public Scene2D void onRightClick() { - auto& cam = m_tempEcs->getComponent(m_tempSvc->render().getCameraEntity()); + auto& cam = m_tempRegistry->getComponent(m_tempSvc->render().getCameraEntity()); vec2 wp = ECS::Camera::screenPositionToWorldPosition2D( cam, vec2(m_tempSvc->input().getMouseX(), m_tempSvc->input().getMouseY())); selectNearest(wp); @@ -382,8 +382,8 @@ class SceneEditor : public Scene2D // ===================================================================== void selectNearest(vec2 pos) { - auto cs = m_tempEcs->getComponentArray(); - auto ui = m_tempEcs->getComponentArray(); + auto cs = m_tempRegistry->getComponentArray(); + auto ui = m_tempRegistry->getComponentArray(); float best = SEL_THRESH; Entity hit = static_cast(-1); @@ -426,7 +426,7 @@ class SceneEditor : public Scene2D void deleteSelected() { - m_tempEcs->destroyEntity(m_selectedEntity); + m_tempRegistry->destroyEntity(m_selectedEntity); doDeselect(); } @@ -438,17 +438,17 @@ class SceneEditor : public Scene2D for (int i = 0; i < 8; i++) { float py = PANEL_TOP_Y - i * PARAM_GAP; - auto& u = m_tempEcs->getComponent(m_paramBtns[i].shapeEntity); + auto& u = m_tempRegistry->getComponent(m_paramBtns[i].shapeEntity); u.parameters[0] = PANEL_X + (P_BTN_W) * 0.5f; u.parameters[1] = py; - auto& t = m_tempEcs->getComponent(m_paramBtns[i].textEntity); + auto& t = m_tempRegistry->getComponent(m_paramBtns[i].textEntity); t.position = vec3(PANEL_X - P_BTN_W - 10.0f, py, 0.0f); - m_tempEcs->setComponentDirty(t); + m_tempRegistry->setComponentDirty(t); } - auto& ht = m_tempEcs->getComponent(m_selInfoText); + auto& ht = m_tempRegistry->getComponent(m_selInfoText); ht.position = vec3(PANEL_X, PANEL_TOP_Y + 35.0f, 0.0f); - m_tempEcs->setComponentDirty(ht); + m_tempRegistry->setComponentDirty(ht); // m_UIRenderSystem.shaderNeedsUpdate() = true; } @@ -456,15 +456,15 @@ class SceneEditor : public Scene2D { for (int i = 0; i < 8; i++) { - auto& u = m_tempEcs->getComponent(m_paramBtns[i].shapeEntity); + auto& u = m_tempRegistry->getComponent(m_paramBtns[i].shapeEntity); u.parameters[0] = HIDDEN; - auto& t = m_tempEcs->getComponent(m_paramBtns[i].textEntity); + auto& t = m_tempRegistry->getComponent(m_paramBtns[i].textEntity); t.position.x = HIDDEN; - m_tempEcs->setComponentDirty(t); + m_tempRegistry->setComponentDirty(t); } - auto& ht = m_tempEcs->getComponent(m_selInfoText); + auto& ht = m_tempRegistry->getComponent(m_selInfoText); ht.position.x = HIDDEN; - m_tempEcs->setComponentDirty(ht); + m_tempRegistry->setComponentDirty(ht); // m_UIRenderSystem.shaderNeedsUpdate() = true; } @@ -478,20 +478,20 @@ class SceneEditor : public Scene2D return; } - auto& cs = m_tempEcs->getComponent(m_selectedEntity); + auto& cs = m_tempRegistry->getComponent(m_selectedEntity); - auto& hdr = m_tempEcs->getComponent(m_selInfoText); + auto& hdr = m_tempRegistry->getComponent(m_selInfoText); const char* name = shapeName(cs.distanceFieldId); if (hdr.text != name) { hdr.text = name; - m_tempEcs->setComponentDirty(hdr); + m_tempRegistry->setComponentDirty(hdr); } int pc = paramCount(cs.distanceFieldId); for (int i = 0; i < 8; i++) { - auto& tx = m_tempEcs->getComponent(m_paramBtns[i].textEntity); + auto& tx = m_tempRegistry->getComponent(m_paramBtns[i].textEntity); if (i < pc) { char buf[48]; @@ -499,23 +499,23 @@ class SceneEditor : public Scene2D if (tx.text != buf) { tx.text = buf; - m_tempEcs->setComponentDirty(tx); + m_tempRegistry->setComponentDirty(tx); } - auto& u = m_tempEcs->getComponent(m_paramBtns[i].shapeEntity); + auto& u = m_tempRegistry->getComponent(m_paramBtns[i].shapeEntity); if (u.parameters[0] < 0.0f) { u.parameters[0] = PANEL_X + (P_BTN_W) * 0.5f; u.parameters[1] = PANEL_TOP_Y - i * PARAM_GAP; } - auto& t = m_tempEcs->getComponent(m_paramBtns[i].textEntity); + auto& t = m_tempRegistry->getComponent(m_paramBtns[i].textEntity); float py = PANEL_TOP_Y - i * PARAM_GAP; float txX = PANEL_X - P_BTN_W - 10.0f; if (t.position.x < 0.0f || std::abs(t.position.y - py) > 0.001f) { t.position = vec3(txX, py, 0.0f); - m_tempEcs->setComponentDirty(t); + m_tempRegistry->setComponentDirty(t); } } else @@ -523,15 +523,15 @@ class SceneEditor : public Scene2D if (!tx.text.empty()) { tx.text.clear(); - m_tempEcs->setComponentDirty(tx); + m_tempRegistry->setComponentDirty(tx); } - auto& u = m_tempEcs->getComponent(m_paramBtns[i].shapeEntity); + auto& u = m_tempRegistry->getComponent(m_paramBtns[i].shapeEntity); if (u.parameters[0] > 0.0f) { u.parameters[0] = HIDDEN; - auto& t2 = m_tempEcs->getComponent(m_paramBtns[i].textEntity); + auto& t2 = m_tempRegistry->getComponent(m_paramBtns[i].textEntity); t2.position.x = HIDDEN; - m_tempEcs->setComponentDirty(t2); + m_tempRegistry->setComponentDirty(t2); } } } @@ -539,7 +539,7 @@ class SceneEditor : public Scene2D bool entityHasShape(Entity e) { - auto arr = m_tempEcs->getComponentArray(); + auto arr = m_tempRegistry->getComponentArray(); for (size_t i = 0; i < arr->getSize(); i++) if (arr->getEntityAtIdx(i) == e) return true; @@ -554,7 +554,7 @@ class SceneEditor : public Scene2D if (!m_hasSelection || !entityHasShape(m_selectedEntity)) return; - auto& cs = m_tempEcs->getComponent(m_selectedEntity); + auto& cs = m_tempRegistry->getComponent(m_selectedEntity); int pc = paramCount(cs.distanceFieldId); if (idx >= pc) return; @@ -570,7 +570,7 @@ class SceneEditor : public Scene2D if (cs.distanceFieldId == DefaultShapes::STAR && idx == 4) v = std::round(v); cs.parameters[idx] = v; - m_tempEcs->setComponentDirty(cs); + m_tempRegistry->setComponentDirty(cs); } else { @@ -587,7 +587,7 @@ class SceneEditor : public Scene2D int activated = -1; for (int i = 0; i < 16; i++) { - auto& t = m_tempEcs->getComponent(m_materialToggles[i]); + auto& t = m_tempRegistry->getComponent(m_materialToggles[i]); if (t.active && t.state == ButtonState::Down) { activated = i; @@ -599,12 +599,12 @@ class SceneEditor : public Scene2D m_selectedMaterial = activated; for (int i = 0; i < 16; i++) if (i != activated) - m_tempEcs->getComponent(m_materialToggles[i]).active = false; + m_tempRegistry->getComponent(m_materialToggles[i]).active = false; } else { for (int i = 0; i < 16; i++) - if (m_tempEcs->getComponent(m_materialToggles[i]).active) + if (m_tempRegistry->getComponent(m_materialToggles[i]).active) { m_selectedMaterial = i; break; @@ -617,7 +617,7 @@ class SceneEditor : public Scene2D int activated = -1; for (int i = 0; i < (int)m_combButtons.size(); i++) { - auto& t = m_tempEcs->getComponent(m_combButtons[i].toggleEntity); + auto& t = m_tempRegistry->getComponent(m_combButtons[i].toggleEntity); if (t.active && t.state == ButtonState::Down) { activated = i; @@ -630,12 +630,12 @@ class SceneEditor : public Scene2D m_selectedCombination = m_combButtons[activated].combType; for (int i = 0; i < (int)m_combButtons.size(); i++) if (i != activated) - m_tempEcs->getComponent(m_combButtons[i].toggleEntity).active = false; + m_tempRegistry->getComponent(m_combButtons[i].toggleEntity).active = false; } else { for (int i = 0; i < (int)m_combButtons.size(); i++) - if (m_tempEcs->getComponent(m_combButtons[i].toggleEntity).active) + if (m_tempRegistry->getComponent(m_combButtons[i].toggleEntity).active) { m_selectedCombIdx = i; m_selectedCombination = m_combButtons[i].combType; @@ -655,19 +655,19 @@ class SceneEditor : public Scene2D m_tempSvc->shapes().addShape(type, p, static_cast(m_selectedMaterial), m_selectedCombination); if (m_selectedCombination == CombinationType::SmoothAddition || m_selectedCombination == CombinationType::SmoothSubtraction) - m_tempEcs->getComponent(e).smoothFactor = 1.5f; + m_tempRegistry->getComponent(e).smoothFactor = 1.5f; doSelect(e); } void spawnPhysicsEntity(vec2 wp) { - Entity e = m_tempEcs->createEntity(); - auto& t = m_tempEcs->addComponent(e); + Entity e = m_tempRegistry->createEntity(); + auto& t = m_tempRegistry->addComponent(e); t.position = vec3(wp.x, wp.y, 0.0f); - m_tempEcs->setComponentDirty(t); - auto& sdf = m_tempEcs->addComponent(e); + m_tempRegistry->setComponentDirty(t); + auto& sdf = m_tempRegistry->addComponent(e); sdf.materialId = static_cast(m_selectedMaterial); - m_tempEcs->addComponent(e); + m_tempRegistry->addComponent(e); m_tempSvc->serialization().blacklistEntity(e); } @@ -763,7 +763,7 @@ class SceneEditor : public Scene2D vec2 camCentre() { - auto& t = m_tempEcs->getComponent(m_tempSvc->render().getCameraEntity()); + auto& t = m_tempRegistry->getComponent(m_tempSvc->render().getCameraEntity()); return vec2(t.position.x, t.position.y); } From dc848584302e99f8d58e46695291176d797072c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= <49535803+damacaa@users.noreply.github.com> Date: Tue, 11 Aug 2026 15:42:18 +0200 Subject: [PATCH 18/21] scene: remove stale comments from SceneManager --- include/weird-engine/SceneManager.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/include/weird-engine/SceneManager.h b/include/weird-engine/SceneManager.h index 08860a4..e9bb73e 100644 --- a/include/weird-engine/SceneManager.h +++ b/include/weird-engine/SceneManager.h @@ -56,16 +56,11 @@ namespace WeirdEngine std::string m_assetsPath; }; - // ChatGPT: Template method declarations and definitions are usually placed in header files. - // The header files are then included in the source files that use the templates. - // If the template is only in the static library and the client code doesn’t see its full definition, it won't be - // able to use it. That's why this is here... template void SceneManager::registerScene(const std::string& sceneName, const std::string& sceneFilePath) { static_assert(std::is_base_of::value, "T must derive from Scene"); - // TODO: check ECS for a similar names.push_back(sceneName); sceneFactories[sceneName] = [this, sceneFilePath]() { From 638be7bca11616ef37d6038fee9c763026dc602b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= <49535803+damacaa@users.noreply.github.com> Date: Tue, 11 Aug 2026 16:49:00 +0200 Subject: [PATCH 19/21] scene: fix SceneSerializer failing to save when parent directories do not exist --- src/weird-engine/SceneSerializer.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/weird-engine/SceneSerializer.cpp b/src/weird-engine/SceneSerializer.cpp index a848075..7494673 100644 --- a/src/weird-engine/SceneSerializer.cpp +++ b/src/weird-engine/SceneSerializer.cpp @@ -4,6 +4,7 @@ #include "weird-physics/components/DistanceConstraint.h" #include "weird-physics/components/GlobalPhysicsSettings.h" #include "weird-physics/components/Spring.h" +#include #include #include #include @@ -221,6 +222,12 @@ namespace WeirdEngine j["physics"] = {{"distanceConstraints", distanceConstraintsJson}, {"springs", springsJson}}; } + std::filesystem::path filePath(filename); + if (filePath.has_parent_path()) + { + std::filesystem::create_directories(filePath.parent_path()); + } + std::ofstream outFile(filename); if (!outFile.is_open()) { From 1b953882affff101ac8ecd09a5c31f4a86afb38d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= <49535803+damacaa@users.noreply.github.com> Date: Tue, 11 Aug 2026 17:24:43 +0200 Subject: [PATCH 20/21] examples: disabled ServiceShowcaseScene --- examples/sample-scenes/src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/sample-scenes/src/main.cpp b/examples/sample-scenes/src/main.cpp index 9e31fa0..3d7b4ba 100644 --- a/examples/sample-scenes/src/main.cpp +++ b/examples/sample-scenes/src/main.cpp @@ -22,13 +22,13 @@ int main(int argc, char* argv[]) { SceneManager& sceneManager = SceneManager::getInstance(); - sceneManager.registerScene("service-showcase"); sceneManager.registerScene("shapes"); sceneManager.registerScene("rope"); sceneManager.registerScene("text"); sceneManager.registerScene("life"); sceneManager.registerScene("cursor-collision"); sceneManager.registerScene("destroy-test"); + // sceneManager.registerScene("service-showcase"); // sceneManager.registerScene("collision-handling"); // sceneManager.registerScene("image"); From 4efef56f9a9b8d15e5dfb9eb3003e11cb75a9190 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= <49535803+damacaa@users.noreply.github.com> Date: Tue, 11 Aug 2026 20:17:16 +0200 Subject: [PATCH 21/21] core: refactor ShapeService to use ShapeConfig and fix memory safety - Introduce ShapeConfig and UIShapeConfig for designated initialization of SDFs. - Safely encapsulate parameter arrays in ShapeVariables to prevent dangling pointers. - Simplify addShape and addUIShape implementations. - Update SDF_SHAPES.md documentation. --- README.md | 281 +++++++++++++-- docs/SCENE_AND_ECS.md | 329 ++++++++++++++++++ docs/SDF_SHAPES.md | 180 ++++++++++ examples/3d-experiments/include/Classic.h | 23 +- examples/3d-experiments/include/CornellBox.h | 84 +++-- .../3d-experiments/include/MaterialShowcase.h | 49 ++- examples/opengl-experiments/include/Lines.h | 3 +- .../sample-scenes/include/AquariumScene.h | 48 ++- .../sample-scenes/include/CollisionHandling.h | 36 +- examples/sample-scenes/include/DestroyScene.h | 10 +- examples/sample-scenes/include/ImageScene.h | 30 +- .../include/MouseCollisionScene.h | 42 ++- examples/sample-scenes/include/RopeScene.h | 51 ++- .../include/ServiceShowcaseScene.h | 38 +- .../include/ShapesCombinations.h | 72 ++-- examples/sample-scenes/include/TextScene.h | 12 +- examples/sample-scenes/include/WalkScene.h | 10 +- .../weird-engine/services/ServiceProvider.h | 157 ++++++--- .../molecule-editor/include/MoleculeEditor.h | 93 +++-- tools/scene-editor/include/SceneEditor.h | 27 +- 20 files changed, 1271 insertions(+), 304 deletions(-) create mode 100644 docs/SCENE_AND_ECS.md create mode 100644 docs/SDF_SHAPES.md diff --git a/README.md b/README.md index a703e38..741510f 100644 --- a/README.md +++ b/README.md @@ -1,66 +1,275 @@ +# Weird Engine -# Weird Engine +Weird Engine is a C++20 game engine designed for 2D and 3D Signed Distance Field (SDF) rendering. -## Overview +## Features -Weird Engine is a simple yet unique game engine featuring: +- **Ray Marching Renderer**: Renders 2D and 3D Signed Distance Fields using custom OpenGL ES shaders. +- **Physics Engine**: Calculates 2D Position-Based Dynamics (PBD) with SDF collision detection. +- **Entity Component System (ECS)**: Manages entities, component storage, and system dispatching. +- **Service Architecture**: Provides decoupled engine services through a single provider interface. -- **Custom OpenGL Renderer**: Uses ray marching to render 2D Signed Distance Fields (SDFs). -- **Physics Engine**: Implements Position-Based Dynamics (PBD) with custom SDF-based collision detection. -- **ECS Architecture**: A fully custom-built Entity Component System (ECS). +--- -## Getting Started +## Getting Started -### Creating Your First Project +### Prerequisites + +Install the SDL3 development library for your operating system before building. +Refer to the [SDL3 Linux README](https://wiki.libsdl.org/SDL3/README-linux) for Linux package names. + +### Building Your Project with the Template Preset -1. Navigate to `/CMake/create-project`. -2. Place your header files (`.h`) in the `/include` directory. -3. Place your source files (`.cpp`) in the `/src` directory. - - You can create subfolders for better organization. -4. Build witch CMake and run your project. +Weird Engine provides a project template in `examples/empty-project`. +Use this template to start building a new game. -### Linux -You'll need to install SDL3 dependencies: -[SDL3 Linux README](https://wiki.libsdl.org/SDL3/README-linux) +1. Copy the `examples/empty-project` directory to your project location. +2. Open `CMakeLists.txt` in your new project directory. +3. Configure the engine source location: + - **Local Engine (Default)**: Set `USE_LOCAL_WEIRD_ENGINE` to `ON`. Set `WEIRD_ENGINE_LOCAL_PATH` to your local engine directory. + - **Automatic Download**: Set `USE_LOCAL_WEIRD_ENGINE` to `OFF`. CMake automatically downloads Weird Engine from GitHub. +4. Place your header files in `include/` and source files in `src/`. +5. Place your game assets in `assets/`. +6. Configure and build the project using CMake: -### Issues downloading SDL submodule +```bash +cmake -B build -S . +cmake --build build ``` -git rm --cached third-party/SDL -rm -rf .git/modules/third-party/SDL -rm -rf third-party/SDL -git submodule add https://github.com/libsdl-org/SDL.git third-party/SDL +7. Run the compiled executable from the build directory. + +--- + +## Engine Architecture + +For detailed guides, refer to: +- [Scene and ECS Architecture Guide](docs/SCENE_AND_ECS.md) +- [Defining Shapes with SDFs Guide](docs/SDF_SHAPES.md) + +### Creating a Scene + +Inherit from one of the scene base classes in `include/weird-engine/Scene.h`: + +- `Scene2D`: Uses 2D ray marching and 2D physics. +- `Scene3D`: Uses 3D ray marching. +- `SceneBoth`: Combines 2D and 3D ray marching paths. + +Register your scene in `main()` with the `SceneManager` instance: + +```cpp +#include + +using namespace WeirdEngine; + +class MyScene : public Scene2D +{ +public: + MyScene() + { + addStartSystem(onStartSystem); + } +}; + +int main(int argc, char* argv[]) +{ + SceneManager& sceneManager = SceneManager::getInstance(); + sceneManager.registerScene("my-scene"); + start(sceneManager, {}, {}, {}, argc, argv); +} ``` -## Anbernic muOS Deployment +### Entities and Components + +Entities are unique numerical identifiers. +The `Registry` class manages entities and stores components. + +#### Creating an Entity + +Call `registry.createEntity()` to make a new entity: + +```cpp +Entity entity = registry.createEntity(); +``` + +#### Adding Components + +Call `registry.addComponent(entity)` to attach a component to an entity: + +```cpp +auto& transform = registry.addComponent(entity); +transform.position = vec3(0.0f, 10.0f, 0.0f); + +auto& dot = registry.addComponent(entity); +dot.materialId = DisplaySettings::LightGray; +``` + +If you modify a component after creation, mark it dirty if required: + +```cpp +registry.setComponentDirty(transform); +``` + +#### Creating and Registering Custom Components + +Define custom components as C++ structures: + +```cpp +struct Health +{ + int current = 100; + int max = 100; +}; +``` + +The `Registry` automatically registers new component types when first accessed. +You can also register component types explicitly: + +```cpp +registry.registerComponent(); +``` -Weird Engine includes generic scripts for building and deploying games directly to Anbernic handheld consoles running muOS over MTP. These scripts are located in `scripts/anbernic/`. +#### Scene State Component Pattern -You can use these generic scripts to deploy *any* game built with Weird Engine without needing to copy the scripts to your game's folder. The scripts automatically detect your project's name, cross-compile it via Podman, package assets, generate launcher scripts, and push the files to the device. +Store scene variables in an ECS component instead of global variables. +Create a `State` component and attach it to a dedicated entity: + +```cpp +struct State +{ + int score = 0; + float timer = 0.0f; +}; + +void onCreateSystem(Registry& registry, ServiceProvider& services) +{ + Entity stateEntity = registry.createEntity(); + registry.addComponent(stateEntity); + services.tags().tag(stateEntity, "state"); + services.serialization().blacklistEntity(stateEntity); +} +``` + +### Systems and Logic + +Add game logic using the System Dispatcher or legacy callbacks. + +#### System Dispatcher (Recommended) + +Systems are plain free functions or lambdas with this signature: + +```cpp +void system(Registry& registry, ServiceProvider& services); +``` + +Register systems inside your scene constructor: + +```cpp +MyScene() +{ + addCreateSystem(onCreateSystem); + addStartSystem(onStartSystem); + addUpdateSystem(movementSystem); + addUpdateSystem(combatSystem); + addImGuiRenderSystem(uiSystem); + addEntityCollisionSystem(onCollisionSystem); + addEntityShapeCollisionSystem(onShapeCollisionSystem); + addDestroySystem(onDestroySystem); +} +``` + +Systems registered to the same stage run sequentially in registration order. + +#### Service Provider Interface + +Systems access engine subsystems through the `ServiceProvider` facade: + +- `services.input()`: Read keyboard, mouse, and gamepad inputs. +- `services.physics()`: Change gravity, damping, pause state, or run raycasts. +- `services.render()`: Control camera, lights, and force shader updates. +- `services.shapes()`: Register custom SDFs and add geometric shapes. +- `services.materials()`: Create and query 3D materials. +- `services.audio()`: Play sounds and check friction audio levels. +- `services.tags()`: Assign unique string tags to entities and look up entities by tag. +- `services.serialization()`: Save or load `.weird` scene files and blacklist entities. +- `services.time()`: Read frame delta time and total simulation time. +- `services.resources()`: Resolve asset paths and file input/output. +- `services.sceneControl()`: Trigger scene transitions. + +#### Legacy Scene Callbacks + +Override virtual methods in `Scene` to use legacy callbacks: + +```cpp +class MyScene : public Scene2D +{ +protected: + void onStart(Registry& registry, ServiceProvider& services) override {} + void onUpdate(Registry& registry, ServiceProvider& services) override {} + void onRender(Registry& registry, ServiceProvider& services, WeirdRenderer::RenderTarget& target) override {} +}; +``` + +Note: Use `onRender` specifically when you need custom 3D render pipeline operations. + +#### Physics Thread Callbacks + +Physics simulation steps run on a dedicated thread. +Override these virtual methods to execute logic mid-step: + +- `onPhysicsStep(Simulation2D& simulation)` +- `onPhysicsRigidBodyCollision(Simulation2D& simulation, PhysicsCollisionEvent& event)` +- `onPhysicsShapeCollision(Simulation2D& simulation, PhysicsShapeCollisionEvent& event)` + +Physics callbacks receive `Simulation2D&` only. +Physics callbacks cannot access `Registry` or `ServiceProvider` because the main thread owns the ECS. + +To associate custom data with physics bodies, derive from `BodyUserData`: + +```cpp +struct CharacterData : BodyUserData +{ + static constexpr int TYPE = 1; + CharacterData() { type = TYPE; } + float jumpStrength = 10.0f; +}; + +// Hand off ownership to the simulation: +services.physics().setUserData(rb.simulationId, std::make_unique()); + +// Query data back in physics callbacks: +if (auto* data = simulation.getUserDataAs(bodyId)) +{ + simulation.addImpulseForce(bodyId, vec2(0.0f, data->jumpStrength)); +} +``` + +--- + +## Anbernic muOS Deployment + +Weird Engine includes scripts for building and deploying games to Anbernic handhelds running muOS. +Find these scripts in `scripts/anbernic/`. ### Prerequisites -- [Podman](https://podman.io/) installed on your machine (used to safely isolate the cross-compiler toolchain). -- The device must be connected to your PC via USB and mounted via MTP (e.g., `mtp:/RG35XX-H/SD2`). +- Install [Podman](https://podman.io/) on your PC. +- Mount the console SD card over USB using MTP (for example `mtp:/RG35XX-H/SD2`). ### Deploying a Game -To build and deploy a game to the console: +Run `deploy-muos.sh` with your project path and MTP destination: ```bash -# General Usage -/path/to/weird-engine/scripts/anbernic/deploy-muos.sh - -# Example: Deploying a game from its own directory -cd my-awesome-game -../weird-engine/scripts/anbernic/deploy-muos.sh . mtp:/RG35XX-H/SD2 +/path/to/weird-engine/scripts/anbernic/deploy-muos.sh . mtp:/RG35XX-H/SD2 ``` ### Fetching Device Logs -If you need to retrieve `log.txt` or screenshots from the device after running your game: +Pull log files and screenshots from the device: ```bash -../weird-engine/scripts/anbernic/fetch-logs.sh . mtp:/RG35XX-H/SD2 +/path/to/weird-engine/scripts/anbernic/fetch-logs.sh . mtp:/RG35XX-H/SD2 ``` -Logs will be saved to a timestamped folder inside your project's `device-logs/` directory. + +Logs are saved to `device-logs/` inside your project directory. diff --git a/docs/SCENE_AND_ECS.md b/docs/SCENE_AND_ECS.md new file mode 100644 index 0000000..d8ce7c1 --- /dev/null +++ b/docs/SCENE_AND_ECS.md @@ -0,0 +1,329 @@ +# Scene and ECS Architecture Guide + +This document explains how to build scenes, manage entities and components, and write system logic in Weird Engine. +All instructions follow the ASD-STE100 Simplified Technical English standard. + +--- + +## 1. Project Setup and Building + +Weird Engine includes a pre-configured template project in `examples/empty-project`. +Use this template to create new games. + +### Build Configuration + +Open `CMakeLists.txt` in your game directory to configure engine linking options. + +#### Option A: Using a Local Engine Copy + +Set `USE_LOCAL_WEIRD_ENGINE` to `ON` (default). +Set `WEIRD_ENGINE_LOCAL_PATH` to your engine directory relative to `CMakeLists.txt`: + +```cmake +set(USE_LOCAL_WEIRD_ENGINE ON) +set(WEIRD_ENGINE_LOCAL_PATH "../weird-engine/") +``` + +#### Option B: Automatic GitHub Download + +Set `USE_LOCAL_WEIRD_ENGINE` to `OFF`. +CMake uses `FetchContent` to download Weird Engine from GitHub: + +```cmake +set(USE_LOCAL_WEIRD_ENGINE OFF) +``` + +### Compiling Your Project + +Run these CMake commands from your project root: + +```bash +cmake -B build -S . +cmake --build build +``` + +--- + +## 2. Building a Scene + +Scenes inherit from one of three base classes defined in `include/weird-engine/Scene.h`: + +- `Scene2D`: Standard 2D Signed Distance Field (SDF) ray marching and PBD physics. +- `Scene3D`: 3D SDF ray marching path. +- `SceneBoth`: Combined 2D and 3D ray marching paths. + +### Registering Scenes + +Register your scene class in `main()` with the singleton `SceneManager`: + +```cpp +#include + +using namespace WeirdEngine; + +class MainGameScene : public Scene2D +{ +public: + MainGameScene() + { + addCreateSystem(onCreateSystem); + addStartSystem(onStartSystem); + addUpdateSystem(onUpdateSystem); + } +}; + +int main(int argc, char* argv[]) +{ + SceneManager& sceneManager = SceneManager::getInstance(); + sceneManager.registerScene("main-game"); + + DisplaySettings displaySettings{}; + PhysicsSettings physicsSettings{}; + AudioSettings audioSettings{}; + + start(sceneManager, displaySettings, physicsSettings, audioSettings, argc, argv); +} +``` + +--- + +## 3. Entity Component System (ECS) + +The `Registry` class manages entity IDs and component storage. + +### Creating and Destroying Entities + +Call `registry.createEntity()` to generate an entity ID: + +```cpp +Entity entity = registry.createEntity(); +``` + +Call `registry.destroyEntity(entity)` to remove an entity and all attached components: + +```cpp +registry.destroyEntity(entity); +``` + +### Adding and Accessing Components + +Add components to an entity using `registry.addComponent(entity)`: + +```cpp +auto& transform = registry.addComponent(entity); +transform.position = vec3(10.0f, 5.0f, 0.0f); + +auto& dot = registry.addComponent(entity); +dot.materialId = DisplaySettings::LightGray; + +auto& rb = registry.addComponent(entity); +rb.velocity = vec2(0.0f, 5.0f); +``` + +Retrieve components using `registry.getComponent(entity)`: + +```cpp +auto& transform = registry.getComponent(entity); +``` + +Check component existence using `registry.hasComponent(entity)`: + +```cpp +if (registry.hasComponent(entity)) +{ + // Perform action +} +``` + +Mark modified components dirty when required by rendering or physics systems: + +```cpp +registry.setComponentDirty(rb); +``` + +### Creating Custom Component Types + +Define custom components as plain C++ structures: + +```cpp +struct Health +{ + int current = 100; + int max = 100; +}; +``` + +The `Registry` registers component types automatically during first access. +You can also register component types explicitly: + +```cpp +registry.registerComponent(); +``` + +### ECS Native Scene State Pattern + +Systems do not keep local state variables inside the scene class. +Store scene state inside an ECS component attached to a dedicated state entity: + +```cpp +struct SceneState +{ + int score = 0; + float spawnTimer = 0.0f; + Entity playerEntity = INVALID_ENTITY; +}; + +void onCreateSystem(Registry& registry, ServiceProvider& services) +{ + Entity stateEntity = registry.createEntity(); + registry.addComponent(stateEntity); + services.tags().tag(stateEntity, "state"); + services.serialization().blacklistEntity(stateEntity); +} + +inline SceneState& getState(Registry& registry, ServiceProvider& services) +{ + return registry.getComponentArray()->getDataAtIdx(0); +} +``` + +--- + +## 4. System Dispatcher Architecture + +Add scene logic by registering systems to stage dispatchers. + +### System Function Signature + +Systems are free functions or lambdas matching the `CoreSystem` signature: + +```cpp +void systemName(Registry& registry, ServiceProvider& services); +``` + +### Registering Stage Systems + +Register systems inside your Scene constructor: + +- `addCreateSystem`: Runs after ECS initialization before scene loading. +- `addStartSystem`: Runs once after initial scene setup. +- `addUpdateSystem`: Runs every frame for game logic. +- `addImGuiRenderSystem`: Runs during ImGui interface rendering. +- `addEntityCollisionSystem`: Dispatches main-thread entity collision events. +- `addEntityShapeCollisionSystem`: Dispatches main-thread entity shape collision events. +- `addDestroySystem`: Runs when replacing or exiting the scene. + +```cpp +class ShowcaseScene : public Scene2D +{ +public: + ShowcaseScene() + { + addCreateSystem(onCreateSystem); + addStartSystem(onStartSystem); + addUpdateSystem(spawnSystem); + addUpdateSystem(inputSystem); + addUpdateSystem(uiSystem); + addDestroySystem(onDestroySystem); + } +}; +``` + +Multiple systems registered to the same stage execute sequentially in registration order. + +--- + +## 5. ServiceProvider Subsystems + +The `ServiceProvider` parameter provides controlled access to engine subsystems. + +| Subsystem | Service Call | Purpose | +|---|---|---| +| Input | `services.input()` | Query keys, mouse coordinates, and gamepads | +| Physics | `services.physics()` | Control gravity, damping, pause state, and raycasts | +| Render | `services.render()` | Access camera, lights, and trigger shader updates | +| Shapes | `services.shapes()` | Register custom SDFs and spawn shapes (see [SDF Shapes Guide](SDF_SHAPES.md)) | +| Materials | `services.materials()` | Create and access 3D material definitions | +| Audio | `services.audio()` | Queue audio requests and read friction audio levels | +| Tags | `services.tags()` | Assign and query unique string tags on entities | +| Serialization | `services.serialization()` | Save and load `.weird` files and blacklist entities | +| Time | `services.time()` | Read simulation time and frame delta time | +| Resources | `services.resources()` | Resolve asset paths and file storage operations | +| Scene Control | `services.sceneControl()` | Request scene transitions | +| Debug | `services.debug()` | Toggle fly camera and input debugging options | + +--- + +## 6. Legacy Callbacks and Physics Thread Interaction + +### Legacy Virtual Callbacks + +You can override virtual callbacks in `Scene`: + +- `onStart(Registry& registry, ServiceProvider& services)` +- `onUpdate(Registry& registry, ServiceProvider& services)` +- `onRender(Registry& registry, ServiceProvider& services, WeirdRenderer::RenderTarget& target)` + +Use `onRender` when issuing direct 3D draw commands to the render target. + +### Physics Thread Callbacks + +Physics simulation steps run on a dedicated physics thread. +Override these virtual methods for mid-step physics logic: + +```cpp +void onPhysicsStep(Simulation2D& simulation) override; +void onPhysicsRigidBodyCollision(Simulation2D& simulation, PhysicsCollisionEvent& event) override; +void onPhysicsShapeCollision(Simulation2D& simulation, PhysicsShapeCollisionEvent& event) override; +``` + +Physics thread callbacks receive `Simulation2D&` only. +Physics thread callbacks must NOT access `Registry` or `ServiceProvider` because the main thread owns the ECS. + +### Physics Body User Data + +Attach custom C++ structs to physics bodies by deriving from `BodyUserData`: + +```cpp +struct CharacterData : BodyUserData +{ + static constexpr int TYPE = 1; + + CharacterData() + { + type = TYPE; + } + + float jumpStrength = 10.0f; + float restitution = 1.5f; +}; +``` + +Pass ownership of the user data to the physics simulation: + +```cpp +auto data = std::make_unique(); +data->jumpStrength = 12.0f; +services.physics().setUserData(rb.simulationId, std::move(data)); +``` + +Query user data in physics thread callbacks safely using `getUserDataAs`: + +```cpp +void onPhysicsShapeCollision(Simulation2D& simulation, PhysicsShapeCollisionEvent& event) override +{ + if (auto* data = simulation.getUserDataAs(event.body)) + { + event.absortion *= 1.0f / data->restitution; + event.friction *= 0.5f; + } +} +``` + +--- + +## 7. Sample Scene Reference + +For a complete working example demonstrating systems, `ServiceProvider`, custom SDFs, UI text, and physics callbacks, consult: + +`examples/sample-scenes/include/ServiceShowcaseScene.h` diff --git a/docs/SDF_SHAPES.md b/docs/SDF_SHAPES.md new file mode 100644 index 0000000..f61c0f6 --- /dev/null +++ b/docs/SDF_SHAPES.md @@ -0,0 +1,180 @@ +# Defining Shapes with Signed Distance Fields (SDFs) + +This document explains how to construct, register, and instantiate Signed Distance Field (SDF) shapes in Weird Engine. +All instructions follow the ASD-STE100 Simplified Technical English standard. + +--- + +## 1. Overview of SDF Shapes + +A Signed Distance Field (SDF) evaluates the shortest distance from a world position to the surface of a shape. + +- **Negative values**: Inside the shape. +- **Zero**: Exactly on the boundary. +- **Positive values**: Outside the shape. + +In Weird Engine, shape definitions inherit from `IMathExpression` (defined in `include/weird-engine/math/MathExpressions.h`). + +Each `IMathExpression` provides two functions: + +- `getValue(const float* parameters)`: Evaluates distance on the CPU for physics collisions and CPU raymarching. +- `print()`: Returns GLSL code string to generate OpenGL raymarching shaders. + +--- + +## 2. Using Default Primitive Shapes + +Weird Engine includes default shape primitives in `WeirdEngine::DefaultShapes` (defined in `include/weird-engine/math/Default2DSDFs.h`). + +### Available Default Shapes + +- `DefaultShapes::CIRCLE` +- `DefaultShapes::BOX` +- `DefaultShapes::TRIANGLE` +- `DefaultShapes::LINE` +- `DefaultShapes::RAMP` +- `DefaultShapes::SINE` +- `DefaultShapes::STAR` +- `DefaultShapes::CIRCLE_LINE` +- `DefaultShapes::BOX_LINE` + +### Adding a Default Shape to a Scene + +Use `services.shapes().addShape(...)` with a `ShapeConfig` struct to instantiate a shape entity: + +```cpp +Entity sphere = services.shapes().addShape({ + .shapeId = DefaultShapes::CIRCLE, + .variables = {{Primitives::Circle::POS_X, 15.0f}, {Primitives::Circle::POS_Y, 10.0f}, {Primitives::Circle::RADIUS, 5.0f}}, + .material = materialId, + .combination = CombinationType::Addition, + .hasCollision = true // Enable collisions +}); +``` + +### Adding a UI Shape + +For rendering shapes on the 2D user interface layer (which is screen-space and does not use physical collisions), use `addUIShape` with a `UIShapeConfig`: + +```cpp +Entity uiBox = services.shapes().addUIShape({ + .shapeId = DefaultShapes::BOX, + .variables = {Display::width * 0.5f, 90.0f, 40.0f, 14.0f}, // Inline positional array + .material = 2, + .combination = CombinationType::Addition +}); +``` + +--- + +## 3. Creating Custom SDF Shapes + +You can build custom geometric shapes by combining mathematical expressions. + +### Expression Nodes + +Build expression trees using shared pointers to `IMathExpression` nodes: + +- `FloatVariable(offset)`: Reads a float value from the parameter array at the specified index. +- `FloatConstant(value)`: Represents a fixed float constant. +- **Math Operations**: `Addition`, `Subtraction`, `Multiplication`, `Division`, `Sine`, `Abs`, `Min`, `Max`. +- **CSG Operations**: `SDFAddition`, `SDFSubtraction`, `SDFIntersection`, `SDFSmoothAddition`, `SDFSmoothSubtraction`, `SDFOnion`. +- **Primitives**: `Primitives::Circle`, `Primitives::Box`, `Primitives::Triangle`, `Primitives::Line`, `Primitives::Ramp`, `Primitives::SineWave`. + +### Defining a Custom Ring SDF + +The following example builds a ring by subtracting an inner circle from an outer circle: + +```cpp +// Define variable index bindings +auto x = std::make_shared(0); +auto y = std::make_shared(1); +auto outerRadius = std::make_shared(2); +auto innerRadius = std::make_shared(3); + +// Construct primitive circles +auto outer = std::make_shared(x, y, outerRadius); +auto inner = std::make_shared(x, y, innerRadius); + +// Subtract inner circle from outer circle using Max(outer, -inner) +auto negatedInner = std::make_shared(-1.0f, inner); +auto ringExpression = std::make_shared(outer, negatedInner); +``` + +--- + +## 4. Registering and Instantiating Custom SDFs + +### Registering the SDF + +Register your expression tree with `services.shapes().registerSDF(...)` to obtain a `ShapeId`: + +```cpp +ShapeId ringShapeId = services.shapes().registerSDF(ringExpression); +``` + +You can also register global default SDFs before scene start using `Scene::registerDefaultSDF(expression)`. + +### Adding the Custom Shape Entity + +Pass a `ShapeConfig` struct to `addShape`. You can pass variables positionally or by index offset (`{{INDEX, value}, ...}`): + +```cpp +// 1. Positional syntax +Entity ringEntity = services.shapes().addShape({ + .shapeId = ringShapeId, + .variables = { 15.0f, 20.0f, 5.0f, 4.0f }, // POS_X, POS_Y, outerRadius, innerRadius + .material = ringMaterial, + .combination = CombinationType::Addition, + .hasCollision = true, // Enable collision + .group = 0 // Group index +}); + +// 2. Indexed offset syntax using constants (e.g. Primitives::Box or custom constants) +static constexpr uint8_t POS_X = 0; +static constexpr uint8_t POS_Y = 1; +static constexpr uint8_t OUTER_R = 2; +static constexpr uint8_t INNER_R = 3; + +Entity ringEntity2 = services.shapes().addShape({ + .shapeId = ringShapeId, + .variables = {{POS_X, 15.0f}, {POS_Y, 20.0f}, {OUTER_R, 5.0f}, {INNER_R, 4.0f}}, + .material = ringMaterial +}); + +// Adjust smooth factor on the CustomShape component +registry.getComponent(ringEntity).smoothFactor = 2.0f; +``` + +--- + +## 5. Shape Combination Types + +When adding shapes to a scene, specify how the shape blends with existing scene geometry: + +- `CombinationType::Addition`: Standard CSG union (`min`). +- `CombinationType::Subtraction`: CSG subtraction (`max(a, -b)`). +- `CombinationType::SmoothAddition`: Smooth blending union. +- `CombinationType::SmoothSubtraction`: Smooth blending subtraction. + +Example of creating a subtractive pit in the floor: + +```cpp +services.shapes().addShape({ + .shapeId = DefaultShapes::CIRCLE, + .variables = {{Primitives::Circle::POS_X, 30.0f}, {Primitives::Circle::POS_Y, 5.0f}, {Primitives::Circle::RADIUS, 4.0f}}, + .material = 0, // No material required for subtraction + .combination = CombinationType::Subtraction, + .hasCollision = true, + .group = CustomShape::GLOBAL_GROUP +}); +``` + +--- + +## 6. Code Reference + +For full implementation examples of custom SDF registration, subtraction shapes, and parameter passing, see: + +`examples/sample-scenes/include/ServiceShowcaseScene.h` + diff --git a/examples/3d-experiments/include/Classic.h b/examples/3d-experiments/include/Classic.h index 7bcdfd6..3580ecc 100644 --- a/examples/3d-experiments/include/Classic.h +++ b/examples/3d-experiments/include/Classic.h @@ -54,17 +54,18 @@ class ClassicScene : public Scene3D m_ball = entity; } - { - float vars1[8] = {25.0f, 10.0f, 5.0f, 0.5f, 13.0f, 0.0f}; // Custom shape - Entity start = - services.shapes().addShape(DefaultShapes::STAR, vars1, orangeMat, CombinationType::Addition, true, 0); - } - - { - float vars1[8] = {}; // Custom shape - Entity start = services.shapes().addShape(DefaultShapes3D::PLANE, vars1, floorMaterial, - CombinationType::Addition, false); - } + services.shapes().addShape({.shapeId = DefaultShapes::STAR, + .variables = {25.0f, 10.0f, 5.0f, 0.5f, 13.0f, 0.0f}, + .material = orangeMat, + .combination = CombinationType::Addition, + .hasCollision = true, + .group = 0}); + + services.shapes().addShape({.shapeId = DefaultShapes3D::PLANE, + .variables = {}, + .material = floorMaterial, + .combination = CombinationType::Addition, + .hasCollision = false}); { Entity entity = registry.createEntity(); diff --git a/examples/3d-experiments/include/CornellBox.h b/examples/3d-experiments/include/CornellBox.h index 4d6c49d..f6412f5 100644 --- a/examples/3d-experiments/include/CornellBox.h +++ b/examples/3d-experiments/include/CornellBox.h @@ -59,40 +59,76 @@ class CornellBox : public Scene3D auto boxId = services.shapes().registerSDF(box); // Left - { - float vars1[8] = {-2.0f * 2.6f, 2.6f, 0.0f, 2.6f, 2.6f, 2.6f}; // Custom shape - Entity start = services.shapes().addShape(boxId, vars1, redMat, CombinationType::Addition, false); - } + services.shapes().addShape({.shapeId = boxId, + .variables = {{Primitives3D::Box::POS_X, -2.0f * 2.6f}, + {Primitives3D::Box::POS_Y, 2.6f}, + {Primitives3D::Box::POS_Z, 0.0f}, + {Primitives3D::Box::SIZE_X, 2.6f}, + {Primitives3D::Box::SIZE_Y, 2.6f}, + {Primitives3D::Box::SIZE_Z, 2.6f}}, + .material = redMat, + .combination = CombinationType::Addition, + .hasCollision = false}); // Right - { - float vars1[8] = {2.0f * 2.6f, 2.6f, 0.0f, 2.6f, 2.6f, 2.6f}; // Custom shape - Entity start = services.shapes().addShape(boxId, vars1, greenMat, CombinationType::Addition, false); - } + services.shapes().addShape({.shapeId = boxId, + .variables = {{Primitives3D::Box::POS_X, 2.0f * 2.6f}, + {Primitives3D::Box::POS_Y, 2.6f}, + {Primitives3D::Box::POS_Z, 0.0f}, + {Primitives3D::Box::SIZE_X, 2.6f}, + {Primitives3D::Box::SIZE_Y, 2.6f}, + {Primitives3D::Box::SIZE_Z, 2.6f}}, + .material = greenMat, + .combination = CombinationType::Addition, + .hasCollision = false}); // Back - { - float vars1[8] = {0.0f, 2.6f, -2.0f * 2.6f, 3.0f * 2.6f, 2.6f, 2.6f}; // Custom shape - Entity start = services.shapes().addShape(boxId, vars1, whiteMat, CombinationType::Addition, false); - } + services.shapes().addShape({.shapeId = boxId, + .variables = {{Primitives3D::Box::POS_X, 0.0f}, + {Primitives3D::Box::POS_Y, 2.6f}, + {Primitives3D::Box::POS_Z, -2.0f * 2.6f}, + {Primitives3D::Box::SIZE_X, 3.0f * 2.6f}, + {Primitives3D::Box::SIZE_Y, 2.6f}, + {Primitives3D::Box::SIZE_Z, 2.6f}}, + .material = whiteMat, + .combination = CombinationType::Addition, + .hasCollision = false}); // Top - { - float vars1[8] = {0.0f, 3.0f * 2.6f, -2.6f, 3.0f * 2.6f, 2.6f, 2.0f * 2.6f}; // Custom shape - Entity start = services.shapes().addShape(boxId, vars1, whiteMat, CombinationType::Addition, false); - } + services.shapes().addShape({.shapeId = boxId, + .variables = {{Primitives3D::Box::POS_X, 0.0f}, + {Primitives3D::Box::POS_Y, 3.0f * 2.6f}, + {Primitives3D::Box::POS_Z, -2.6f}, + {Primitives3D::Box::SIZE_X, 3.0f * 2.6f}, + {Primitives3D::Box::SIZE_Y, 2.6f}, + {Primitives3D::Box::SIZE_Z, 2.0f * 2.6f}}, + .material = whiteMat, + .combination = CombinationType::Addition, + .hasCollision = false}); // Light hole - { - float vars1[8] = {0.0f, 2.0f * 2.6f, 0.0f, 0.5f, 1.0f, 0.5f}; // Custom shape - Entity start = services.shapes().addShape(boxId, vars1, whiteMat, CombinationType::Subtraction, false); - } + services.shapes().addShape({.shapeId = boxId, + .variables = {{Primitives3D::Box::POS_X, 0.0f}, + {Primitives3D::Box::POS_Y, 2.0f * 2.6f}, + {Primitives3D::Box::POS_Z, 0.0f}, + {Primitives3D::Box::SIZE_X, 0.5f}, + {Primitives3D::Box::SIZE_Y, 1.0f}, + {Primitives3D::Box::SIZE_Z, 0.5f}}, + .material = whiteMat, + .combination = CombinationType::Subtraction, + .hasCollision = false}); // Floor - { - float vars1[8] = {0.0f, -1.0f * 2.6f, -2.6f, 3.0f * 2.6f, 2.6f, 2.0f * 2.6f}; // Custom shape - Entity start = services.shapes().addShape(boxId, vars1, whiteMat, CombinationType::Addition, false); - } + services.shapes().addShape({.shapeId = boxId, + .variables = {{Primitives3D::Box::POS_X, 0.0f}, + {Primitives3D::Box::POS_Y, -1.0f * 2.6f}, + {Primitives3D::Box::POS_Z, -2.6f}, + {Primitives3D::Box::SIZE_X, 3.0f * 2.6f}, + {Primitives3D::Box::SIZE_Y, 2.6f}, + {Primitives3D::Box::SIZE_Z, 2.0f * 2.6f}}, + .material = whiteMat, + .combination = CombinationType::Addition, + .hasCollision = false}); // { // float vars1[8] = {0.0f, 2.6f, 0.0f, 2.7f, 2.7f, 2.7f}; // Custom shape diff --git a/examples/3d-experiments/include/MaterialShowcase.h b/examples/3d-experiments/include/MaterialShowcase.h index 59bdad8..ac36ed1 100644 --- a/examples/3d-experiments/include/MaterialShowcase.h +++ b/examples/3d-experiments/include/MaterialShowcase.h @@ -122,18 +122,18 @@ class MaterialShowcaseScene : public Scene3D sdf.materialId = randomMats[i]; } - { - auto& floorMaterial = services.materials().createMaterial(); - floorMaterial.color = vec4(1.0f, 1.0f, 1.0f, 1.0f); - floorMaterial.metallic = 0.1f; - floorMaterial.roughness = 0.3f; - floorMaterial.pattern = MaterialPattern::Checkers; - floorMaterial.secondaryColor = floorMaterial.color * 0.8f; - - float vars[8] = {3}; - Entity floor = services.shapes().addShape(DefaultShapes3D::PLANE, vars, floorMaterial, - CombinationType::Addition, false); - } + auto& floorMaterial = services.materials().createMaterial(); + floorMaterial.color = vec4(1.0f, 1.0f, 1.0f, 1.0f); + floorMaterial.metallic = 0.1f; + floorMaterial.roughness = 0.3f; + floorMaterial.pattern = MaterialPattern::Checkers; + floorMaterial.secondaryColor = floorMaterial.color * 0.8f; + + services.shapes().addShape({.shapeId = DefaultShapes3D::PLANE, + .variables = {3}, + .material = floorMaterial, + .combination = CombinationType::Addition, + .hasCollision = false}); auto& mirrorMaterial = services.materials().createMaterial(); mirrorMaterial.color = vec4(1.0f); @@ -141,20 +141,35 @@ class MaterialShowcaseScene : public Scene3D mirrorMaterial.roughness = 0.0f; { - std::shared_ptr box = std::make_shared(); auto boxId = services.shapes().registerSDF(box); - float vars1[8] = {-5.0f, -2.0f, 0.0f, 0.1f, 1.0f, 3.0f}; // Custom shape - Entity start = services.shapes().addShape(boxId, vars1, mirrorMaterial, CombinationType::Addition, false); + services.shapes().addShape({.shapeId = boxId, + .variables = {{Primitives3D::Box::POS_X, -5.0f}, + {Primitives3D::Box::POS_Y, -2.0f}, + {Primitives3D::Box::POS_Z, 0.0f}, + {Primitives3D::Box::SIZE_X, 0.1f}, + {Primitives3D::Box::SIZE_Y, 1.0f}, + {Primitives3D::Box::SIZE_Z, 3.0f}}, + .material = mirrorMaterial, + .combination = CombinationType::Addition, + .hasCollision = false}); } { std::shared_ptr box = std::make_shared(); auto boxId = services.shapes().registerSDF(box); - float vars1[8] = {20.0f, -2.0f, 0.0f, 0.1f, 1.0f, 3.0f}; // Custom shape - Entity start = services.shapes().addShape(boxId, vars1, mirrorMaterial, CombinationType::Addition, false); + services.shapes().addShape({.shapeId = boxId, + .variables = {{Primitives3D::Box::POS_X, 20.0f}, + {Primitives3D::Box::POS_Y, -2.0f}, + {Primitives3D::Box::POS_Z, 0.0f}, + {Primitives3D::Box::SIZE_X, 0.1f}, + {Primitives3D::Box::SIZE_Y, 1.0f}, + {Primitives3D::Box::SIZE_Z, 3.0f}}, + .material = mirrorMaterial, + .combination = CombinationType::Addition, + .hasCollision = false}); } { diff --git a/examples/opengl-experiments/include/Lines.h b/examples/opengl-experiments/include/Lines.h index 7035c7e..32b26a9 100644 --- a/examples/opengl-experiments/include/Lines.h +++ b/examples/opengl-experiments/include/Lines.h @@ -35,8 +35,7 @@ class LinesScene : public Scene3D std::shared_ptr plane = std::make_shared(0.0f); auto planeId = services.shapes().registerSDF(plane); - float vars1[8] = {}; // Custom shape - Entity start = services.shapes().addShape(planeId, vars1, whiteMat); + Entity start = services.shapes().addShape({.shapeId = planeId, .variables = {}, .material = whiteMat}); } m_renderPlane = new RenderPlane(); diff --git a/examples/sample-scenes/include/AquariumScene.h b/examples/sample-scenes/include/AquariumScene.h index d2aca49..2e2eb7a 100644 --- a/examples/sample-scenes/include/AquariumScene.h +++ b/examples/sample-scenes/include/AquariumScene.h @@ -99,29 +99,41 @@ class AquariumScene : public Scene2D createEel(registry, 10.0f, 30.0f, 22, 0.9f, 6); { - float seaweedVars[8] = {3.0f, 1.2f, 2.5f}; - Entity seaweed = services.shapes().addShape(DefaultShapes::SINE, seaweedVars, DisplaySettings::Green); + Entity seaweed = services.shapes().addShape({.shapeId = DefaultShapes::SINE, + .variables = {{Primitives::SineWave::AMPLITUDE, 3.0f}, + {Primitives::SineWave::PERIOD, 1.2f}, + {Primitives::SineWave::SPEED, 2.5f}}, + .material = static_cast(DisplaySettings::Green)}); auto& sw = registry.addComponent(seaweed); sw.animationOffset = 0.0f; } { - float seaweedVars[8] = {2.0f, 2.0f, 1.8f}; - Entity seaweed = services.shapes().addShape(DefaultShapes::SINE, seaweedVars, DisplaySettings::LightGreen); + Entity seaweed = + services.shapes().addShape({.shapeId = DefaultShapes::SINE, + .variables = {{Primitives::SineWave::AMPLITUDE, 2.0f}, + {Primitives::SineWave::PERIOD, 2.0f}, + {Primitives::SineWave::SPEED, 1.8f}}, + .material = static_cast(DisplaySettings::LightGreen)}); auto& sw = registry.addComponent(seaweed); sw.animationOffset = 1.5f; } - { - float boxVars[8] = {TANK_CX, TANK_CY, TANK_W, TANK_H}; - Entity box = services.shapes().addShape(DefaultShapes::BOX, boxVars, DisplaySettings::LightBlue, - CombinationType::Intersection); - } - - { - float boxVars[8] = {TANK_CX, TANK_CY, TANK_W, TANK_H, 1.0f}; - Entity box = services.shapes().addShape(DefaultShapes::BOX_LINE, boxVars, DisplaySettings::LightBlue); - } + services.shapes().addShape({.shapeId = DefaultShapes::BOX, + .variables = {{Primitives::Box::POS_X, TANK_CX}, + {Primitives::Box::POS_Y, TANK_CY}, + {Primitives::Box::SIZE_X, TANK_W}, + {Primitives::Box::SIZE_Y, TANK_H}}, + .material = static_cast(DisplaySettings::LightBlue), + .combination = CombinationType::Intersection}); + + services.shapes().addShape({.shapeId = DefaultShapes::BOX_LINE, + .variables = {{Primitives::Box::POS_X, TANK_CX}, + {Primitives::Box::POS_Y, TANK_CY}, + {Primitives::Box::SIZE_X, TANK_W}, + {Primitives::Box::SIZE_Y, TANK_H}, + {4, 1.0f}}, + .material = static_cast(DisplaySettings::LightBlue)}); for (int i = 0; i < 40; i++) { @@ -161,9 +173,11 @@ class AquariumScene : public Scene2D dot.materialId = material; auto& rb = registry.addComponent(bellEntity); - float bellVars[8] = {x, y, 2.5f * scale, 0.8f, 6.0f, 2.0f}; - Entity bellShape = - services.shapes().addShape(DefaultShapes::STAR, bellVars, material, CombinationType::Addition, false); + Entity bellShape = services.shapes().addShape({.shapeId = DefaultShapes::STAR, + .variables = {x, y, 2.5f * scale, 0.8f, 6.0f, 2.0f}, + .material = static_cast(material), + .combination = CombinationType::Addition, + .hasCollision = false}); auto& jf = registry.addComponent(bellEntity); jf.bellShape = bellShape; diff --git a/examples/sample-scenes/include/CollisionHandling.h b/examples/sample-scenes/include/CollisionHandling.h index 3c26258..5e63452 100644 --- a/examples/sample-scenes/include/CollisionHandling.h +++ b/examples/sample-scenes/include/CollisionHandling.h @@ -39,21 +39,27 @@ class CollisionHandlingScene : public Scene2D } // Floor - { - float variables[8]{15.0f, 5.0f, 25.0f}; - services.shapes().addShape(DefaultShapes::CIRCLE, variables, 3); - } - - { - float variables[8]{15.0f, -50.0f, 250.0f, 50.0f}; - auto floor = services.shapes().addShape(DefaultShapes::BOX, variables, 3, CombinationType::SmoothAddition); - registry.getComponent(floor).smoothFactor = 3.0f; - } - - { - float variables[8]{15.0f, 5.0f, 20.0f}; - services.shapes().addShape(DefaultShapes::CIRCLE, variables, 3, CombinationType::Subtraction); - } + services.shapes().addShape({.shapeId = DefaultShapes::CIRCLE, + .variables = {{Primitives::Circle::POS_X, 15.0f}, + {Primitives::Circle::POS_Y, 5.0f}, + {Primitives::Circle::RADIUS, 25.0f}}, + .material = 3}); + + auto floor = services.shapes().addShape({.shapeId = DefaultShapes::BOX, + .variables = {{Primitives::Box::POS_X, 15.0f}, + {Primitives::Box::POS_Y, -50.0f}, + {Primitives::Box::SIZE_X, 250.0f}, + {Primitives::Box::SIZE_Y, 50.0f}}, + .material = 3, + .combination = CombinationType::SmoothAddition}); + registry.getComponent(floor).smoothFactor = 3.0f; + + services.shapes().addShape({.shapeId = DefaultShapes::CIRCLE, + .variables = {{Primitives::Circle::POS_X, 15.0f}, + {Primitives::Circle::POS_Y, 5.0f}, + {Primitives::Circle::RADIUS, 20.0f}}, + .material = 3, + .combination = CombinationType::Subtraction}); registry.getComponent(services.render().getCameraEntity()).position = g_cameraPositon; } diff --git a/examples/sample-scenes/include/DestroyScene.h b/examples/sample-scenes/include/DestroyScene.h index 4e51787..a4fd11b 100644 --- a/examples/sample-scenes/include/DestroyScene.h +++ b/examples/sample-scenes/include/DestroyScene.h @@ -79,10 +79,14 @@ class DestroyScene : public Scene2D float y = (std::rand() % 100) - 50.0f; float w = (float)(std::rand() % 4 + 1); float h = (float)(std::rand() % 4 + 1); - float variables[8]{w, y, x, h, 0.0f, 0.0f, 0.0f, 0.0f}; uint16_t material = std::rand() % 16; - Entity shape = services.shapes().addShape(DefaultShapes::BOX, variables, material, - CombinationType::Addition); + Entity shape = services.shapes().addShape({.shapeId = DefaultShapes::BOX, + .variables = {{Primitives::Box::POS_X, w}, + {Primitives::Box::POS_Y, y}, + {Primitives::Box::SIZE_X, x}, + {Primitives::Box::SIZE_Y, h}}, + .material = material, + .combination = CombinationType::Addition}); m_testShapes.push_back(shape); } break; diff --git a/examples/sample-scenes/include/ImageScene.h b/examples/sample-scenes/include/ImageScene.h index 4c83c82..8e11922 100644 --- a/examples/sample-scenes/include/ImageScene.h +++ b/examples/sample-scenes/include/ImageScene.h @@ -72,22 +72,28 @@ class ImageScene : public Scene2D } // Floor - { - float variables[8]{15, -5, 25.0f, 5.0f, 0.0f}; - services.shapes().addShape(DefaultShapes::BOX, variables, 3); - } + services.shapes().addShape({.shapeId = DefaultShapes::BOX, + .variables = {{Primitives::Box::POS_X, 15.0f}, + {Primitives::Box::POS_Y, -5.0f}, + {Primitives::Box::SIZE_X, 25.0f}, + {Primitives::Box::SIZE_Y, 5.0f}}, + .material = 3}); // Wall right - { - float variables[8]{30 + 5, 20, 5.0f, 30.0f, 0.0f}; - services.shapes().addShape(DefaultShapes::BOX, variables, 3); - } + services.shapes().addShape({.shapeId = DefaultShapes::BOX, + .variables = {{Primitives::Box::POS_X, 35.0f}, + {Primitives::Box::POS_Y, 20.0f}, + {Primitives::Box::SIZE_X, 5.0f}, + {Primitives::Box::SIZE_Y, 30.0f}}, + .material = 3}); // Wall left - { - float variables[8]{0 - 5, 20, 5.0f, 30.0f, 0.0f}; - services.shapes().addShape(DefaultShapes::BOX, variables, 3); - } + services.shapes().addShape({.shapeId = DefaultShapes::BOX, + .variables = {{Primitives::Box::POS_X, -5.0f}, + {Primitives::Box::POS_Y, 20.0f}, + {Primitives::Box::SIZE_X, 5.0f}, + {Primitives::Box::SIZE_Y, 30.0f}}, + .material = 3}); registry.getComponent(services.render().getCameraEntity()).position = g_cameraPositon; } diff --git a/examples/sample-scenes/include/MouseCollisionScene.h b/examples/sample-scenes/include/MouseCollisionScene.h index 900772b..22b7cc9 100644 --- a/examples/sample-scenes/include/MouseCollisionScene.h +++ b/examples/sample-scenes/include/MouseCollisionScene.h @@ -51,29 +51,33 @@ class MouseCollisionScene : public Scene2D } // Floor - { - float variables[8]{0.0f, 1.5f, 1.0f}; - services.shapes().addShape(DefaultShapes::SINE, variables, 3); - } + services.shapes().addShape({.shapeId = DefaultShapes::SINE, + .variables = {{Primitives::SineWave::AMPLITUDE, 0.0f}, + {Primitives::SineWave::PERIOD, 1.5f}, + {Primitives::SineWave::SPEED, 1.0f}}, + .material = 3}); // Wall right - { - float variables[8]{30 + 5, 0, 5.0f, 30.0f, 0.0f}; - services.shapes().addShape(DefaultShapes::BOX, variables, 3); - } + services.shapes().addShape({.shapeId = DefaultShapes::BOX, + .variables = {{Primitives::Box::POS_X, 35.0f}, + {Primitives::Box::POS_Y, 0.0f}, + {Primitives::Box::SIZE_X, 5.0f}, + {Primitives::Box::SIZE_Y, 30.0f}}, + .material = 3}); // Wall left - { - float variables[8]{-5, 0, 5.0f, 30.0f, 0.0f}; - services.shapes().addShape(DefaultShapes::BOX, variables, 3); - } - - { - float variables[8]{-15.0f, 50.0f, 5.0f, 4.5f, 2.0f, 10.0f}; - Entity star = services.shapes().addShape(DefaultShapes::CIRCLE, variables, 7); - - m_cursorShape = star; - } + services.shapes().addShape({.shapeId = DefaultShapes::BOX, + .variables = {{Primitives::Box::POS_X, -5.0f}, + {Primitives::Box::POS_Y, 0.0f}, + {Primitives::Box::SIZE_X, 5.0f}, + {Primitives::Box::SIZE_Y, 30.0f}}, + .material = 3}); + + m_cursorShape = services.shapes().addShape({.shapeId = DefaultShapes::CIRCLE, + .variables = {{Primitives::Circle::POS_X, -15.0f}, + {Primitives::Circle::POS_Y, 50.0f}, + {Primitives::Circle::RADIUS, 5.0f}}, + .material = 7}); registry.getComponent(services.render().getCameraEntity()).position = g_cameraPositon; } diff --git a/examples/sample-scenes/include/RopeScene.h b/examples/sample-scenes/include/RopeScene.h index 81f12d3..92195d4 100644 --- a/examples/sample-scenes/include/RopeScene.h +++ b/examples/sample-scenes/include/RopeScene.h @@ -111,14 +111,22 @@ class RopeScene : public Scene2D } // Add base shapes (walls, ground, custom) - float vars0[8] = {1.0f, 0.5f, 1.0f}; // Floor shape - services.shapes().addShape(DefaultShapes::SINE, vars0, 3); - - float vars1[8] = {25.0f, 10.0f, 5.0f, 0.5f, 13.0f, 5.0f}; // Custom shape - m_star = services.shapes().addShape(DefaultShapes::STAR, vars1, 3); - - float vars3[8] = {15.0f, -98.0f, 15.0f, 100.0f}; - services.shapes().addShape(DefaultShapes::BOX, vars3, 3, CombinationType::Addition); + services.shapes().addShape({.shapeId = DefaultShapes::SINE, + .variables = {{Primitives::SineWave::AMPLITUDE, 1.0f}, + {Primitives::SineWave::PERIOD, 0.5f}, + {Primitives::SineWave::SPEED, 1.0f}}, + .material = 3}); + + m_star = services.shapes().addShape( + {.shapeId = DefaultShapes::STAR, .variables = {25.0f, 10.0f, 5.0f, 0.5f, 13.0f, 5.0f}, .material = 3}); + + services.shapes().addShape({.shapeId = DefaultShapes::BOX, + .variables = {{Primitives::Box::POS_X, 15.0f}, + {Primitives::Box::POS_Y, -98.0f}, + {Primitives::Box::SIZE_X, 15.0f}, + {Primitives::Box::SIZE_Y, 100.0f}}, + .material = 3, + .combination = CombinationType::Addition}); registry.getComponent(services.render().getCameraEntity()).position = g_cameraPositon; } @@ -152,6 +160,7 @@ class RopeScene : public Scene2D void onUpdate(Registry& registry, ServiceProvider& services) override { + float delta = services.time().deltaTime(); g_cameraPositon = registry.getComponent(services.render().getCameraEntity()).position; @@ -215,14 +224,28 @@ class RopeScene : public Scene2D float y = (boxStart.y + boxEnd.y) / 2.0f; float w = 0.5f * std::abs(boxStart.x - boxEnd.x); float h = 0.5f * std::abs(boxStart.y - boxEnd.y); - float vars[8] = {x, y, w, h, 1.2f}; if (createBoxInUI) - services.shapes().addUIShape(DefaultShapes::BOX, vars, 7, CombinationType::SmoothAddition); + services.shapes().addUIShape({.shapeId = DefaultShapes::BOX, + .variables = {{Primitives::Box::POS_X, x}, + {Primitives::Box::POS_Y, y}, + {Primitives::Box::SIZE_X, w}, + {Primitives::Box::SIZE_Y, h}, + {4, 1.2f}}, + .material = 7, + .combination = CombinationType::SmoothAddition}); else services.shapes().addShape( - DefaultShapes::BOX, vars, 4 + registry.getComponentArray()->getSize() % 12, - CombinationType::SmoothAddition, true, registry.getComponentArray()->getSize()); + {.shapeId = DefaultShapes::BOX, + .variables = {{Primitives::Box::POS_X, x}, + {Primitives::Box::POS_Y, y}, + {Primitives::Box::SIZE_X, w}, + {Primitives::Box::SIZE_Y, h}, + {4, 1.2f}}, + .material = static_cast(4 + registry.getComponentArray()->getSize() % 12), + .combination = CombinationType::SmoothAddition, + .hasCollision = true, + .group = static_cast(registry.getComponentArray()->getSize())}); } if (services.input().getKeyDown(Input::N)) @@ -231,8 +254,8 @@ class RopeScene : public Scene2D vec2 screen = {services.input().getMouseX(), services.input().getMouseY()}; vec2 world = ECS::Camera::screenPositionToWorldPosition2D(cam, screen); - float vars[8] = {world.x, world.y, 5.0f, 7.5f, 1.0f}; - services.shapes().addShape(DefaultShapes::STAR, vars, 3); + services.shapes().addShape( + {.shapeId = DefaultShapes::STAR, .variables = {world.x, world.y, 5.0f, 7.5f, 1.0f}, .material = 3}); } if (services.input().getKey(Input::R) || services.input().getGamepadButton(Input::GamepadButton::South)) diff --git a/examples/sample-scenes/include/ServiceShowcaseScene.h b/examples/sample-scenes/include/ServiceShowcaseScene.h index 9ea852a..3912434 100644 --- a/examples/sample-scenes/include/ServiceShowcaseScene.h +++ b/examples/sample-scenes/include/ServiceShowcaseScene.h @@ -162,27 +162,35 @@ namespace ServiceShowcase ringShape = services.shapes().registerSDF(ring); - float vars[8] = {15.0f, 20.0f, 5.0f, 4.0f}; - Entity ringEntity = - services.shapes().addShape(ringShape, vars, ringMaterial, CombinationType::Addition, true, 0); + Entity ringEntity = services.shapes().addShape({.shapeId = ringShape, + .variables = {15.0f, 20.0f, 5.0f, 4.0f}, + .material = ringMaterial, + .combination = CombinationType::Addition, + .hasCollision = true, + .group = 0}); registry.getComponent(ringEntity).smoothFactor = 2.0f; } // Floor - { - float vars[8] = {15.0f, -50.0f, 250.0f, 50.0f}; - Entity floor = - services.shapes().addShape(DefaultShapes::BOX, vars, floorMaterial, CombinationType::SmoothAddition); - services.tags().tag(floor, "floor"); - registry.getComponent(floor).smoothFactor = 3.0f; - } + Entity floor = services.shapes().addShape({.shapeId = DefaultShapes::BOX, + .variables = {{Primitives::Box::POS_X, 15.0f}, + {Primitives::Box::POS_Y, -50.0f}, + {Primitives::Box::SIZE_X, 250.0f}, + {Primitives::Box::SIZE_Y, 50.0f}}, + .material = floorMaterial, + .combination = CombinationType::SmoothAddition}); + services.tags().tag(floor, "floor"); + registry.getComponent(floor).smoothFactor = 3.0f; // Pit: a subtraction shape; balls that roll into it fall through - { - float vars[8] = {30.0f, 5.0f, 4.0f}; - services.shapes().addShape(DefaultShapes::CIRCLE, vars, 0, CombinationType::Subtraction, true, - CustomShape::GLOBAL_GROUP); - } + services.shapes().addShape({.shapeId = DefaultShapes::CIRCLE, + .variables = {{Primitives::Circle::POS_X, 30.0f}, + {Primitives::Circle::POS_Y, 5.0f}, + {Primitives::Circle::RADIUS, 4.0f}}, + .material = 0, + .combination = CombinationType::Subtraction, + .hasCollision = true, + .group = CustomShape::GLOBAL_GROUP}); // Camera registry.getComponent(services.render().getCameraEntity()).position = g_cameraPositon; diff --git a/examples/sample-scenes/include/ShapesCombinations.h b/examples/sample-scenes/include/ShapesCombinations.h index 5d86291..a2b39ab 100644 --- a/examples/sample-scenes/include/ShapesCombinations.h +++ b/examples/sample-scenes/include/ShapesCombinations.h @@ -26,10 +26,14 @@ class ShapeCombinatiosScene : public Scene2D services.debug().setDebugFly(true); // Floor shape - { - float vars0[8] = {0.5f, 2.5f, 1.0f}; - services.shapes().addShape(DefaultShapes::SINE, vars0, 2, CombinationType::Addition, true, 0); - } + services.shapes().addShape({.shapeId = DefaultShapes::SINE, + .variables = {{Primitives::SineWave::AMPLITUDE, 0.5f}, + {Primitives::SineWave::PERIOD, 2.5f}, + {Primitives::SineWave::SPEED, 1.0f}}, + .material = 2, + .combination = CombinationType::Addition, + .hasCollision = true, + .group = 0}); std::random_device rd; std::mt19937 gen(rd()); @@ -45,35 +49,53 @@ class ShapeCombinatiosScene : public Scene2D float x = distrib(gen) + 15.0f; float y = -2.0f + distribY(gen); - float vars2[8] = {x, y, 3.0f, 5.0f, 1.0f, 0.0f}; // Custom shape - services.shapes().addShape(DefaultShapes::BOX, vars2, 4 + i, CombinationType::Addition, true, 1); + services.shapes().addShape({.shapeId = DefaultShapes::BOX, + .variables = {{Primitives::Box::POS_X, x}, + {Primitives::Box::POS_Y, y}, + {Primitives::Box::SIZE_X, 3.0f}, + {Primitives::Box::SIZE_Y, 5.0f}}, + .material = static_cast(4 + i), + .combination = CombinationType::Addition, + .hasCollision = true, + .group = 1}); } } // Circle - { - float vars[8] = {15.0f, 7.5f, 5.0f}; - services.shapes().addShape(DefaultShapes::CIRCLE, vars, 7, CombinationType::Addition, true, 2); - } + services.shapes().addShape({.shapeId = DefaultShapes::CIRCLE, + .variables = {{Primitives::Circle::POS_X, 15.0f}, + {Primitives::Circle::POS_Y, 7.5f}, + {Primitives::Circle::RADIUS, 5.0f}}, + .material = 7, + .combination = CombinationType::Addition, + .hasCollision = true, + .group = 2}); // Subtract star - { - float vars[8] = {-2.5f + 15.0f, 12.5f, 5.0f, 0.5f, 13.0f, 5.0f}; - services.shapes().addShape(DefaultShapes::STAR, vars, 0, CombinationType::SmoothSubtraction, true, 2); - } + services.shapes().addShape({.shapeId = DefaultShapes::STAR, + .variables = {-2.5f + 15.0f, 12.5f, 5.0f, 0.5f, 13.0f, 5.0f}, + .material = 0, + .combination = CombinationType::SmoothSubtraction, + .hasCollision = true, + .group = 2}); // Cursor circle - { - float vars2[8] = {250.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f}; // Custom shape - m_circle = services.shapes().addShape(DefaultShapes::CIRCLE, vars2, 0, CombinationType::Subtraction, true, - CustomShape::GLOBAL_GROUP); - } - - { - float vars2[8] = {15.0f, 0.0f, 30.0f, 0.0f, 0.0f, 0.0f}; // Custom shape - services.shapes().addShape(DefaultShapes::CIRCLE, vars2, 0, CombinationType::Intersection, true, - CustomShape::GLOBAL_GROUP); - } + m_circle = services.shapes().addShape( + {.shapeId = DefaultShapes::CIRCLE, + .variables = {{Primitives::Circle::POS_X, 250.0f}, {Primitives::Circle::POS_Y, 10.0f}}, + .material = 0, + .combination = CombinationType::Subtraction, + .hasCollision = true, + .group = CustomShape::GLOBAL_GROUP}); + + services.shapes().addShape({.shapeId = DefaultShapes::CIRCLE, + .variables = {{Primitives::Circle::POS_X, 15.0f}, + {Primitives::Circle::POS_Y, 0.0f}, + {Primitives::Circle::RADIUS, 30.0f}}, + .material = 0, + .combination = CombinationType::Intersection, + .hasCollision = true, + .group = CustomShape::GLOBAL_GROUP}); for (int i = 0; i < 10; ++i) { diff --git a/examples/sample-scenes/include/TextScene.h b/examples/sample-scenes/include/TextScene.h index 6b98a7f..7583f81 100644 --- a/examples/sample-scenes/include/TextScene.h +++ b/examples/sample-scenes/include/TextScene.h @@ -29,11 +29,13 @@ class TextScene : public Scene2D services.debug().setDebugInput(true); services.debug().setDebugFly(true); - { - float vars[8] = {15.0f, -50.0f, 250.0f, 50.0f}; - services.shapes().addShape(DefaultShapes::BOX, vars, DisplaySettings::LightGray, - CombinationType::SmoothAddition); - } + services.shapes().addShape({.shapeId = DefaultShapes::BOX, + .variables = {{Primitives::Box::POS_X, 15.0f}, + {Primitives::Box::POS_Y, -50.0f}, + {Primitives::Box::SIZE_X, 250.0f}, + {Primitives::Box::SIZE_Y, 50.0f}}, + .material = static_cast(DisplaySettings::LightGray), + .combination = CombinationType::SmoothAddition}); registry.getComponent(services.render().getCameraEntity()).position = g_cameraPositon; m_lastResolutionHash = Display::width + Display::height; diff --git a/examples/sample-scenes/include/WalkScene.h b/examples/sample-scenes/include/WalkScene.h index 69500ef..dfdefe5 100644 --- a/examples/sample-scenes/include/WalkScene.h +++ b/examples/sample-scenes/include/WalkScene.h @@ -61,9 +61,13 @@ class WalkScene : public Scene2D m_head = tags["head"]; - float boundsVars2[8]{0.0f, -24.0f, 200.0f, 20.0f}; - Entity inside = services.shapes().addShape(DefaultShapes::BOX, boundsVars2, DisplaySettings::LightGreen, - CombinationType::Addition); + services.shapes().addShape({.shapeId = DefaultShapes::BOX, + .variables = {{Primitives::Box::POS_X, 0.0f}, + {Primitives::Box::POS_Y, -24.0f}, + {Primitives::Box::SIZE_X, 200.0f}, + {Primitives::Box::SIZE_Y, 20.0f}}, + .material = static_cast(DisplaySettings::LightGreen), + .combination = CombinationType::Addition}); registry.getComponent(services.render().getCameraEntity()).position = g_cameraPositon; diff --git a/include/weird-engine/services/ServiceProvider.h b/include/weird-engine/services/ServiceProvider.h index fb0c655..ffc1721 100644 --- a/include/weird-engine/services/ServiceProvider.h +++ b/include/weird-engine/services/ServiceProvider.h @@ -3,13 +3,16 @@ #include #include #include +#include #include +#include #include #include #include #include #include +#include "weird-engine/Assert.h" #include "weird-engine/Background.h" #include "weird-engine/ecs/Registry.h" #include "weird-engine/Input.h" @@ -154,6 +157,104 @@ namespace WeirdEngine } }; + struct ShapeParamValue + { + size_t index = 0; + float value = 0.0f; + }; + + struct ShapeVariables + { + float data[8]{}; + + constexpr ShapeVariables() = default; + + constexpr ShapeVariables(std::initializer_list list) + { + size_t i = 0; + for (float v : list) + { + if (i >= 8) + break; + data[i++] = v; + } + } + + constexpr ShapeVariables(std::initializer_list indexedList) + { + for (const auto& pv : indexedList) + { + if (pv.index < 8) + { + data[pv.index] = pv.value; + } + } + } + + template constexpr ShapeVariables(const float (&arr)[N]) + { + const size_t count = std::min(N, 8); + for (size_t i = 0; i < count; ++i) + data[i] = arr[i]; + } + + constexpr ShapeVariables(std::span s) + { + const size_t count = std::min(s.size(), 8); + for (size_t i = 0; i < count; ++i) + data[i] = s[i]; + } + + constexpr ShapeVariables(const float* ptr, size_t n = 8) + { + const size_t count = std::min(n, 8); + for (size_t i = 0; i < count; ++i) + data[i] = ptr[i]; + } + }; + + struct ShapeMaterial + { + uint16_t id = 0; + + constexpr ShapeMaterial() = default; + constexpr ShapeMaterial(uint16_t matId) + : id(matId) + { + } + constexpr ShapeMaterial(int matId) + : id(static_cast(matId)) + { + } + ShapeMaterial(const Material3D& mat) + : id(mat.id) + { + } + constexpr operator uint16_t() const + { + return id; + } + }; + + struct ShapeConfig + { + ShapeId shapeId = 0; + ShapeVariables variables{}; + ShapeMaterial material = 0; + CombinationType combination = CombinationType::Addition; + bool hasCollision = true; + int group = 0; + }; + + struct UIShapeConfig + { + ShapeId shapeId = 0; + ShapeVariables variables{}; + ShapeMaterial material = 0; + CombinationType combination = CombinationType::Addition; + int group = 0; + }; + struct ShapeService { Registry& registry; @@ -175,59 +276,33 @@ namespace WeirdEngine return sdfs; } - Entity addShape(ShapeId shapeId, float* variables, uint16_t material, - CombinationType combination = CombinationType::Addition, bool hasCollision = true, - int group = 0) + Entity addShape(const ShapeConfig& config) { Entity entity = registry.createEntity(); CustomShape& shape = registry.addComponent(entity); - shape.distanceFieldId = shapeId; - shape.combination = combination; - shape.hasCollisions = hasCollision; - shape.groupIdx = group; - shape.material = material; - std::copy(variables, variables + 8, shape.parameters); + shape.distanceFieldId = config.shapeId; + shape.combination = config.combination; + shape.hasCollisions = config.hasCollision; + shape.groupIdx = config.group; + shape.material = config.material.id; - return entity; - } + std::copy_n(config.variables.data, 8, shape.parameters); - Entity addShape(ShapeId shapeId, float* variables, const Material3D& material, - CombinationType combination = CombinationType::Addition, bool hasCollision = true, - int group = 0) - { - return addShape(shapeId, variables, material.id, combination, hasCollision, group); + return entity; } - Entity addUIShape(ShapeId shapeId, float* variables, uint16_t material, - CombinationType combination = CombinationType::Addition, int group = 0) + Entity addUIShape(const UIShapeConfig& config) { Entity entity = registry.createEntity(); UIShape& shape = registry.addComponent(entity); - shape.distanceFieldId = shapeId; - shape.combination = combination; - shape.groupIdx = group; - shape.material = material; - std::copy(variables, variables + 8, shape.parameters); - - return entity; - } + shape.distanceFieldId = config.shapeId; + shape.combination = config.combination; + shape.groupIdx = config.group; + shape.material = config.material.id; - Entity addUIShape(ShapeId shapeId, float* variables, const Material3D& material, - CombinationType combination = CombinationType::Addition, int group = 0) - { - return addUIShape(shapeId, variables, material.id, combination, group); - } + std::copy_n(config.variables.data, 8, shape.parameters); - UIShape& addUIShape(ShapeId shapeId, float* variables, Entity& entity, int group = 0) - { - entity = registry.createEntity(); - UIShape& component = registry.addComponent(entity); - component.distanceFieldId = shapeId; - component.groupIdx = group; - component.smoothFactor = 100.0f; - std::copy(variables, variables + 8, component.parameters); - - return component; + return entity; } }; diff --git a/tools/molecule-editor/include/MoleculeEditor.h b/tools/molecule-editor/include/MoleculeEditor.h index 1705c3e..2eaa4fe 100644 --- a/tools/molecule-editor/include/MoleculeEditor.h +++ b/tools/molecule-editor/include/MoleculeEditor.h @@ -148,13 +148,15 @@ class MoleculeEditor : public Scene2D buildTagEditorUI(); { - float boundsVars[8]{0.0f, 0.0f, 3000.0f}; - Entity outside = - m_tempSvc->shapes().addShape(DefaultShapes::CIRCLE, boundsVars, 17, CombinationType::Addition); + Entity outside = m_tempSvc->shapes().addShape({.shapeId = DefaultShapes::CIRCLE, + .variables = {0.0f, 0.0f, 3000.0f}, + .material = 17, + .combination = CombinationType::Addition}); - float boundsVars2[8]{0.0f, 0.0f, 20.0f, 20.0f}; - Entity inside = m_tempSvc->shapes().addShape(DefaultShapes::BOX, boundsVars2, DisplaySettings::Black, - CombinationType::Subtraction); + Entity inside = m_tempSvc->shapes().addShape({.shapeId = DefaultShapes::BOX, + .variables = {0.0f, 0.0f, 20.0f, 20.0f}, + .material = static_cast(DisplaySettings::Black), + .combination = CombinationType::Subtraction}); m_tempSvc->serialization().blacklistEntity(outside); m_tempSvc->serialization().blacklistEntity(inside); @@ -314,9 +316,8 @@ class MoleculeEditor : public Scene2D { float px = START_X + i * MAT_SPACING; float p[8]{px, MAT_Y, BTN_SIZE - 4.0f}; - Entity e; - UIShape& sh = m_tempSvc->shapes().addUIShape(DefaultShapes::CIRCLE, p, e); - sh.material = static_cast(i); + Entity e = m_tempSvc->shapes().addUIShape( + {.shapeId = DefaultShapes::CIRCLE, .variables = p, .material = static_cast(i)}); auto& tog = m_tempRegistry->addComponent(e); tog.clickPadding = BTN_SIZE + 3.0f; @@ -374,7 +375,7 @@ class MoleculeEditor : public Scene2D { float y = (Display::height - TOOL_Y_START) - (i * TOOL_SPACING); float p[8]{TOOL_X, y, TOOL_BTN_HALF, TOOL_BTN_HALF}; - Entity e = m_tempSvc->shapes().addUIShape(DefaultShapes::BOX, p, static_cast(2)); + Entity e = m_tempSvc->shapes().addUIShape({.shapeId = DefaultShapes::BOX, .variables = p, .material = 2}); auto& tog = m_tempRegistry->addComponent(e); tog.clickPadding = TOOL_BTN_HALF + 8.0f; tog.parameterModifierMask.set(2); @@ -395,8 +396,10 @@ class MoleculeEditor : public Scene2D } m_tempRegistry->getComponent(m_toolToggles[0]).active = true; - float starP[8]{Display::width - GRAV_Y, Display::height - GRAV_Y, GRAV_Y * 0.5f, 5.0f, 10.0f, 0.0f}; - m_gravityToggleEntity = m_tempSvc->shapes().addUIShape(DefaultShapes::STAR, starP, static_cast(2)); + m_gravityToggleEntity = m_tempSvc->shapes().addUIShape( + {.shapeId = DefaultShapes::STAR, + .variables = {Display::width - GRAV_Y, Display::height - GRAV_Y, GRAV_Y * 0.5f, 5.0f, 10.0f, 0.0f}, + .material = 2}); auto& gravTog = m_tempRegistry->addComponent(m_gravityToggleEntity); gravTog.clickPadding = 18.0f; // gravTog.parameterModifierMask.set(2); @@ -404,8 +407,10 @@ class MoleculeEditor : public Scene2D gravTog.modifierAmount = 10.0f; m_tempSvc->serialization().blacklistEntity(m_gravityToggleEntity); - float gridP[8]{Display::width - GRAV_Y, Display::height - GRID_Y, 12.0f, 12.0f}; - m_gridToggleEntity = m_tempSvc->shapes().addUIShape(DefaultShapes::BOX, gridP, static_cast(2)); + m_gridToggleEntity = m_tempSvc->shapes().addUIShape( + {.shapeId = DefaultShapes::BOX, + .variables = {Display::width - GRAV_Y, Display::height - GRID_Y, 12.0f, 12.0f}, + .material = 2}); auto& gridTog = m_tempRegistry->addComponent(m_gridToggleEntity); gridTog.clickPadding = 18.0f; gridTog.parameterModifierMask.set(2); @@ -776,7 +781,8 @@ class MoleculeEditor : public Scene2D float lineVars[8]{}; computeScreenLineParams(pa, pb, lineVars); - Entity line = m_tempSvc->shapes().addUIShape(DefaultShapes::LINE, lineVars, lineColor); + Entity line = m_tempSvc->shapes().addUIShape( + {.shapeId = DefaultShapes::LINE, .variables = lineVars, .material = static_cast(lineColor)}); auto& btn = m_tempRegistry->addComponent(line); btn.clickPadding = 8.0f; @@ -904,26 +910,37 @@ class MoleculeEditor : public Scene2D void buildTagEditorUI() { - // Tag label – shows "tag: " or "tag: (none)" + // Create label text entity for the selection tag + m_tagLabelEntity = m_tempRegistry->createEntity(); + auto& lt = m_tempRegistry->addComponent(m_tagLabelEntity); + lt.position = vec3(Display::width * 0.5f, 115.0f, 0.0f); + + auto& tx = m_tempRegistry->addComponent(m_tagLabelEntity); + tx.text = ""; + tx.material = static_cast(DisplaySettings::Yellow); + tx.horizontalAlignment = TextRenderer::HorizontalAlignment::Center; + tx.verticalAlignment = TextRenderer::VerticalAlignment::Center; + m_tempSvc->serialization().blacklistEntity(m_tagLabelEntity); + + // Label above the edit button { - Entity lbl = m_tempRegistry->createEntity(); - auto& lt = m_tempRegistry->addComponent(lbl); - lt.position = vec3(Display::width * 0.5f, 150.0f, 0.0f); - auto& tx = m_tempRegistry->addComponent(lbl); - tx.text = ""; - tx.material = 1; - tx.horizontalAlignment = TextRenderer::HorizontalAlignment::Center; - tx.verticalAlignment = TextRenderer::VerticalAlignment::Center; - m_tagLabelEntity = lbl; - m_tempSvc->serialization().blacklistEntity(lbl); + Entity btnLbl = m_tempRegistry->createEntity(); + auto& blt = m_tempRegistry->addComponent(btnLbl); + blt.position = vec3(Display::width * 0.5f, 90.0f, 0.0f); + auto& btx = m_tempRegistry->addComponent(btnLbl); + btx.text = "edit tag"; + btx.material = 1; + btx.horizontalAlignment = TextRenderer::HorizontalAlignment::Center; + btx.verticalAlignment = TextRenderer::VerticalAlignment::Center; + m_tempSvc->serialization().blacklistEntity(btnLbl); } // "edit tag" button (a small box) { static constexpr float BW = 40.0f; static constexpr float BH = 14.0f; - float p[8]{Display::width * 0.5f, 90.0f, BW, BH}; - m_tagEditButton = m_tempSvc->shapes().addUIShape(DefaultShapes::BOX, p, static_cast(2)); + m_tagEditButton = m_tempSvc->shapes().addUIShape( + {.shapeId = DefaultShapes::BOX, .variables = {Display::width * 0.5f, 90.0f, BW, BH}, .material = 2}); auto& btn = m_tempRegistry->addComponent(m_tagEditButton); btn.clickPadding = 6.0f; btn.modifierAmount = 0.0f; @@ -959,13 +976,19 @@ class MoleculeEditor : public Scene2D { float p[8]{}; m_tagCircleOuter = - m_tempSvc->shapes().addUIShape(DefaultShapes::CIRCLE, p, static_cast(DisplaySettings::Yellow), - CombinationType::Addition, TAG_RING_GROUP); + m_tempSvc->shapes().addUIShape({.shapeId = DefaultShapes::CIRCLE, + .variables = p, + .material = static_cast(DisplaySettings::Yellow), + .combination = CombinationType::Addition, + .group = TAG_RING_GROUP}); m_tempSvc->serialization().blacklistEntity(m_tagCircleOuter); m_tagCircleInner = - m_tempSvc->shapes().addUIShape(DefaultShapes::CIRCLE, p, static_cast(DisplaySettings::Yellow), - CombinationType::Subtraction, TAG_RING_GROUP); + m_tempSvc->shapes().addUIShape({.shapeId = DefaultShapes::CIRCLE, + .variables = p, + .material = static_cast(DisplaySettings::Yellow), + .combination = CombinationType::Subtraction, + .group = TAG_RING_GROUP}); m_tempSvc->serialization().blacklistEntity(m_tagCircleInner); } @@ -1117,7 +1140,8 @@ class MoleculeEditor : public Scene2D float lineVars[8]{}; computeScreenLineParams(pa, pb, lineVars); - Entity line = m_tempSvc->shapes().addUIShape(DefaultShapes::LINE, lineVars, lineColor); + Entity line = m_tempSvc->shapes().addUIShape( + {.shapeId = DefaultShapes::LINE, .variables = lineVars, .material = static_cast(lineColor)}); auto& btn = m_tempRegistry->addComponent(line); btn.clickPadding = 8.0f; @@ -1151,7 +1175,8 @@ class MoleculeEditor : public Scene2D float lineVars[8]{}; computeScreenLineParams(pa, pb, lineVars); - Entity line = m_tempSvc->shapes().addUIShape(DefaultShapes::LINE, lineVars, lineColor); + Entity line = m_tempSvc->shapes().addUIShape( + {.shapeId = DefaultShapes::LINE, .variables = lineVars, .material = static_cast(lineColor)}); auto& btn = m_tempRegistry->addComponent(line); btn.clickPadding = 8.0f; diff --git a/tools/scene-editor/include/SceneEditor.h b/tools/scene-editor/include/SceneEditor.h index b2c5601..805cef7 100644 --- a/tools/scene-editor/include/SceneEditor.h +++ b/tools/scene-editor/include/SceneEditor.h @@ -169,7 +169,7 @@ class SceneEditor : public Scene2D float p[8]{}; previewParams(types[i], cx, cy, p); - Entity e = m_tempSvc->shapes().addUIShape(types[i], p, 2); + Entity e = m_tempSvc->shapes().addUIShape({.shapeId = types[i], .variables = p, .material = 2}); auto& b = m_tempRegistry->addComponent(e); b.modifierAmount = 1.0f; b.clickPadding = 8.0f; @@ -249,11 +249,15 @@ class SceneEditor : public Scene2D int g = COMB_GRP_BASE + i; float p1[8]{cx - off * 0.5f, cy, r}; - Entity e1 = m_tempSvc->shapes().addUIShape(DefaultShapes::CIRCLE, p1, static_cast(1), - CombinationType::Addition, g); + Entity e1 = m_tempSvc->shapes().addUIShape({.shapeId = DefaultShapes::CIRCLE, + .variables = p1, + .material = 1, + .combination = CombinationType::Addition, + .group = g}); float p2[8]{cx + off * 0.5f, cy, r}; - Entity e2 = m_tempSvc->shapes().addUIShape(DefaultShapes::CIRCLE, p2, static_cast(1), ct[i], g); + Entity e2 = m_tempSvc->shapes().addUIShape( + {.shapeId = DefaultShapes::CIRCLE, .variables = p2, .material = 1, .combination = ct[i], .group = g}); if (ct[i] == CombinationType::SmoothAddition || ct[i] == CombinationType::SmoothSubtraction) m_tempRegistry->getComponent(e2).smoothFactor = 5.0f; @@ -287,9 +291,8 @@ class SceneEditor : public Scene2D { float px = START_X + i * MAT_SPACING; float p[8]{px, MAT_Y, BTN_SIZE - 4.0f}; - Entity e; - UIShape& sh = m_tempSvc->shapes().addUIShape(DefaultShapes::CIRCLE, p, e); - sh.material = static_cast(i); + Entity e = m_tempSvc->shapes().addUIShape( + {.shapeId = DefaultShapes::CIRCLE, .variables = p, .material = static_cast(i)}); auto& tog = m_tempRegistry->addComponent(e); tog.clickPadding = BTN_SIZE + 3.0f; @@ -321,8 +324,8 @@ class SceneEditor : public Scene2D { float py = PANEL_TOP_Y - i * PARAM_GAP; - float bp[8]{HIDDEN, py, P_BTN_W, P_BTN_H}; - Entity be = m_tempSvc->shapes().addUIShape(DefaultShapes::BOX, bp, static_cast(3)); + Entity be = m_tempSvc->shapes().addUIShape( + {.shapeId = DefaultShapes::BOX, .variables = {HIDDEN, py, P_BTN_W, P_BTN_H}, .material = 3}); auto& btn = m_tempRegistry->addComponent(be); btn.modifierAmount = 1.0f; btn.clickPadding = 3.0f; @@ -651,8 +654,10 @@ class SceneEditor : public Scene2D { float p[8]{}; fillRandomParams(type, p); - Entity e = - m_tempSvc->shapes().addShape(type, p, static_cast(m_selectedMaterial), m_selectedCombination); + Entity e = m_tempSvc->shapes().addShape({.shapeId = type, + .variables = p, + .material = static_cast(m_selectedMaterial), + .combination = m_selectedCombination}); if (m_selectedCombination == CombinationType::SmoothAddition || m_selectedCombination == CombinationType::SmoothSubtraction) m_tempRegistry->getComponent(e).smoothFactor = 1.5f;