diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index b83f5ed..bd0c0d0 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -3,18 +3,21 @@ option(ENGINE_BUILD_SHARED "Build the Engine library as a shared library" ON) set(SOURCES src/IO/parser.cpp src/IO/file_manager.cpp + src/renderer/debug.cpp src/window/window.cpp - src/components/batch.cpp + src/scene/scene.cpp - src/renderer/debug.cpp + src/components/batch.cpp src/renderer/mesh.cpp src/renderer/shader.cpp src/renderer/texture.cpp - src/renderer/core.cpp src/renderer/renderer.cpp + src/renderer/wavefront.cpp + + src/renderer/core.cpp ) if(ENGINE_BUILD_SHARED) diff --git a/engine/include/engine/app/app.h b/engine/include/engine/app/app.h index f57b6fa..14c4fa4 100644 --- a/engine/include/engine/app/app.h +++ b/engine/include/engine/app/app.h @@ -1,20 +1,19 @@ #ifndef APPLICATION_H_ #define APPLICATION_H_ -#include "engine/window/events/window.h" +#include "engine/scene/scene.h" +#include "engine/window/event.h" #include "engine/export.h" class ENGINE_API IApplication { public: virtual ~IApplication() = default; - virtual void OnInit() {}; + virtual void OnInit(std::shared_ptr scene) {}; virtual void OnUpdate() {}; - virtual void OnRender() {}; virtual void OnShutdown() {}; - virtual void OnEvent() {}; - virtual void OnWindowResized(const WindowResized& e) {}; + virtual void OnEvent(const Event& event) {}; }; #endif // APPLICATION_H_ \ No newline at end of file diff --git a/engine/include/engine/components/batch.h b/engine/include/engine/components/batch.h index afe609f..eea8305 100644 --- a/engine/include/engine/components/batch.h +++ b/engine/include/engine/components/batch.h @@ -1,7 +1,7 @@ #ifndef COMPONENT_BATCH_H_ #define COMPONENT_BATCH_H_ -#include "engine/renderer/renderer.h" +#include #include "engine/export.h" // requires mesh component diff --git a/engine/include/engine/renderer/basics.h b/engine/include/engine/renderer/basics.h index f1a7301..29e0820 100644 --- a/engine/include/engine/renderer/basics.h +++ b/engine/include/engine/renderer/basics.h @@ -3,8 +3,6 @@ #include -#include "engine/renderer/mesh.h" - class Vertex { friend class Mesh; private: @@ -14,8 +12,6 @@ private: public: Vertex(glm::vec3 position, glm::vec3 normal, glm::vec2 texCoord) : m_position(position), m_normal(normal), m_texCoord(texCoord) {} -public: - static void DefineAttrib(); }; #endif // RENDERER_BASICS_H \ No newline at end of file diff --git a/engine/include/engine/renderer/core.h b/engine/include/engine/renderer/core.h index 134f1ce..99a9e80 100644 --- a/engine/include/engine/renderer/core.h +++ b/engine/include/engine/renderer/core.h @@ -7,6 +7,9 @@ #include "engine/window/window.h" #include "engine/window/events/window.h" +#include "engine/renderer/renderer.h" +#include "engine/scene/scene.h" + #include "engine/app/app.h" #include "engine/export.h" @@ -16,6 +19,8 @@ public: private: static std::unique_ptr s_app; static std::shared_ptr s_window; + static std::unique_ptr s_renderer; + static std::shared_ptr s_scene; static bool s_running; }; diff --git a/engine/include/engine/renderer/renderer.h b/engine/include/engine/renderer/renderer.h index 69d3410..89abeba 100644 --- a/engine/include/engine/renderer/renderer.h +++ b/engine/include/engine/renderer/renderer.h @@ -2,8 +2,8 @@ #define RENDERER_H_ #include -#include +#include "engine/scene/scene.h" #include "engine/renderer/shader.h" #include "engine/export.h" #include "engine/components/light.h" @@ -11,23 +11,23 @@ // TODO: make static or singleton class ENGINE_API Renderer { public: - Renderer(entt::registry& registry); + Renderer(std::shared_ptr scene); void Render(); void Init(); - void GenerateShadowMaps(); void OnWindowResized(int w, int h); private: void ApplyLights(Shader &shader); void UpdateView(); void RenderScene(Shader &shader); + void GenerateShadowMaps(); void EnsureShadowResources(light& l); private: Shader m_shader; Shader m_depthShader; - entt::registry& m_registry; + std::shared_ptr m_scene; // unsigned int m_depth_fbo; // unsigned int m_depthMap; diff --git a/engine/include/engine/scene/scene.h b/engine/include/engine/scene/scene.h new file mode 100644 index 0000000..295398c --- /dev/null +++ b/engine/include/engine/scene/scene.h @@ -0,0 +1,15 @@ +#ifndef ENGINE_SCENE_H_ +#define ENGINE_SCENE_H_ + +#include + +class Scene { +public: + Scene(); +private: + entt::registry m_registry; + friend class Renderer; + friend class Game; +}; + +#endif // ENGINE_SCENE_H_ \ No newline at end of file diff --git a/engine/include/engine/window/event.h b/engine/include/engine/window/event.h index 64356a9..c97cd10 100644 --- a/engine/include/engine/window/event.h +++ b/engine/include/engine/window/event.h @@ -7,6 +7,30 @@ #include #include +enum class EventType { + WINDOW_RESIZE, + WINDOW_CLOSE, +}; + +class Event { +public: + enum EventCategory { + WINDOW, + // KEYBOARD ... + }; + + Event(EventCategory category) : m_category(category) {} + virtual ~Event() {} + + Event(const Event& event) = default; + + inline const EventCategory GetCategory() const { return m_category; } + + inline const virtual EventType GetType() const = 0; +private: + EventCategory m_category; +}; + class EventDispatcher { using Type = std::type_index; using RawFn = std::function; diff --git a/engine/include/engine/window/events/window.h b/engine/include/engine/window/events/window.h index cc9d64e..6dee04c 100644 --- a/engine/include/engine/window/events/window.h +++ b/engine/include/engine/window/events/window.h @@ -1,7 +1,30 @@ #ifndef WINDOW_EVENTS_H_ #define WINDOW_EVENTS_H_ -struct WindowResized { int w, h; }; -struct WindowCloseRequested {}; +#include "engine/window/event.h" + +class WindowEvent : public Event { +public: + WindowEvent() : Event(Event::EventCategory::WINDOW) {} + + inline const EventType GetType() const override { return EventType::WINDOW_CLOSE; } +}; + +class WindowResizeEvent : public WindowEvent { +public: + WindowResizeEvent(unsigned int w, unsigned int h) : m_width(w), m_height(h) {} + + inline const EventType GetType() const override { return EventType::WINDOW_RESIZE; } + + inline const unsigned int GetWidth() const { return m_width; } + inline const unsigned int GetHeight() const { return m_height; } +private: + unsigned int m_width, m_height; +}; + +class WindowCloseEvent : public WindowEvent { +public: + WindowCloseEvent() {} +}; #endif // WINDOW_EVENTS_H_ \ No newline at end of file diff --git a/engine/src/renderer/core.cpp b/engine/src/renderer/core.cpp index e8564f2..a07e4cd 100644 --- a/engine/src/renderer/core.cpp +++ b/engine/src/renderer/core.cpp @@ -1,27 +1,34 @@ #include #include "engine/renderer/core.h" -#include "engine/window/event.h" +#include "engine/window/event.h" #include "engine/renderer/wavefront.h" std::unique_ptr Engine::s_app = nullptr; std::shared_ptr Engine::s_window = nullptr; +std::unique_ptr Engine::s_renderer = nullptr; +std::shared_ptr Engine::s_scene = nullptr; bool Engine::s_running = false; void Engine::Run(std::unique_ptr app) { - s_app = std::move(app); + s_scene = std::make_shared(); + s_renderer = std::make_unique(s_scene); s_window = Window::GetInstance(); + s_app = std::move(app); s_running = true; - s_app->OnInit(); + s_app->OnInit(s_scene); + s_renderer->Init(); - s_window->Subscribe([](const WindowCloseRequested& e) { - Engine::s_running = false; + s_window->Subscribe([&](const WindowCloseEvent& e) { + s_running = false; + s_app->OnEvent(e); }); - s_window->Subscribe([](const WindowResized& e) { - Engine::s_app->OnWindowResized(e); + s_window->Subscribe([&](const WindowResizeEvent& e) { + s_renderer->OnWindowResized(e.GetWidth(), e.GetHeight()); + s_app->OnEvent(e); }); while (s_running) { @@ -29,7 +36,7 @@ void Engine::Run(std::unique_ptr app) { s_app->OnUpdate(); - s_app->OnRender(); + s_renderer->Render(); s_window->SwapBuffers(); } diff --git a/engine/src/renderer/renderer.cpp b/engine/src/renderer/renderer.cpp index 5b1285b..5dace4f 100644 --- a/engine/src/renderer/renderer.cpp +++ b/engine/src/renderer/renderer.cpp @@ -18,7 +18,7 @@ #include "engine/components/mesh.h" #include "engine/components/batch.h" -Renderer::Renderer(entt::registry& registry) : m_registry(registry) +Renderer::Renderer(std::shared_ptr scene) : m_scene(scene) { m_proj = glm::perspective( static_cast(M_PI_2), @@ -44,15 +44,7 @@ Renderer::Renderer(entt::registry& registry) : m_registry(registry) } void Renderer::Init() { - // auto view = m_registry.view(); - // for (auto [_, b, m] : m_registry.view().each()) { - // unsigned int items = 0; - // for (auto [entt, item] : m_registry.view().each()) { - // if (item.batchId == b.id()) ++items; - // } - // b.prepare() - // m.object->EnableBatch(b.m_instance_vbo); - // } + GenerateShadowMaps(); } void Renderer::OnWindowResized(int w, int h) { @@ -65,13 +57,13 @@ void Renderer::OnWindowResized(int w, int h) { } void Renderer::ApplyLights(Shader &shader) { - auto lights = m_registry.view(); + auto lights = m_scene->m_registry.view(); // TODO: Pass Lights Data to depth shader as well shader.setInt("lightsCount", static_cast(lights.size())); size_t lightIndex = 0; for (auto entity : lights) { - auto &l = m_registry.get(entity); - auto &transf = m_registry.get(entity); + auto &l = m_scene->m_registry.get(entity); + auto &transf = m_scene->m_registry.get(entity); shader.setInt("lights[" + std::to_string(lightIndex) + "].type", static_cast(l.type)); shader.setVec3("lights[" + std::to_string(lightIndex) + "].position", transf.position); @@ -113,24 +105,27 @@ void Renderer::EnsureShadowResources(light& l) { } void Renderer::UpdateView() { - auto cam = m_registry.view().back(); - auto camTransform = m_registry.get(cam); + auto camView = m_scene->m_registry.view(); + auto camTransform = camView.size() > 0 ? + m_scene->m_registry.get(camView.back()) : + transform {glm::vec3(0.f, 0.f, 0.f), glm::vec3(0.f, 0.f, 0.f), glm::vec3(0.f, 0.f, 0.f)}; m_view = glm::lookAt( camTransform.position, camTransform.position + camTransform.rotation, glm::vec3(0.f, 1.f, 0.f) ); - m_shader.setMat4("u_view", m_view); - m_shader.setMat4("u_projection", m_proj); m_shader.setVec3("viewPos", camTransform.position); + + m_shader.setMat4("u_view", m_view); + m_shader.setMat4("u_projection", m_proj); } void Renderer::RenderScene(Shader &shader) { std::unordered_map> batches; - for (auto [entt, item] : m_registry.view().each()) { + for (auto [entt, item] : m_scene->m_registry.view().each()) { if (batches.find(item.batchId) == batches.end()) batches.insert(std::make_pair(item.batchId, std::vector())); @@ -140,7 +135,7 @@ void Renderer::RenderScene(Shader &shader) { shader.setBool("u_isInstanced", true); shader.setBool("isLight", false); shader.setVec3("currentLightColor", glm::vec3(0.f)); - for (auto [entt, b, m] : m_registry.view().each()) { + for (auto [entt, b, m] : m_scene->m_registry.view().each()) { // check if have items for batch render if (batches.find(b.id()) == batches.end()) continue; @@ -150,7 +145,7 @@ void Renderer::RenderScene(Shader &shader) { models.reserve(batchItems.size()); for (auto item : batchItems) { - auto &t = m_registry.get(item); + auto &t = m_scene->m_registry.get(item); glm::mat4 rotation = glm::yawPitchRoll(t.rotation.y, t.rotation.x, t.rotation.z); auto itemModel = glm::translate(glm::mat4(1.f), t.position) * rotation; models.push_back(itemModel); @@ -166,14 +161,14 @@ void Renderer::RenderScene(Shader &shader) { } shader.setBool("u_isInstanced", false); - for (auto [entity, transf, mesh] : m_registry.view(entt::exclude).each()) { + for (auto [entity, transf, mesh] : m_scene->m_registry.view(entt::exclude).each()) { if (mesh.object == nullptr) { std::cerr << "WARN: Entity doesn't have a mesh to render" << std::endl; return; } - if (m_registry.all_of(entity)) { - auto &l = m_registry.get(entity); + if (m_scene->m_registry.all_of(entity)) { + auto &l = m_scene->m_registry.get(entity); shader.setBool("isLight", true); shader.setVec3("currentLightColor", l.color); } else { @@ -193,7 +188,7 @@ void Renderer::RenderScene(Shader &shader) { void Renderer::GenerateShadowMaps() { m_depthShader.use(); - auto lights = m_registry.view(); + auto lights = m_scene->m_registry.view(); for (auto [_, l] : lights.each()) { // TODO: support other light types when ready @@ -207,7 +202,7 @@ void Renderer::Render() { glCullFace(GL_FRONT); - const auto lights = m_registry.view(); + const auto lights = m_scene->m_registry.view(); for (auto [_, l, t] : lights.each()) { // TODO: support other light types when ready diff --git a/engine/src/renderer/wavefront.cpp b/engine/src/renderer/wavefront.cpp index d6f1c5f..fb8b5dc 100644 --- a/engine/src/renderer/wavefront.cpp +++ b/engine/src/renderer/wavefront.cpp @@ -5,9 +5,10 @@ #include #include +#include "engine/renderer/wavefront.h" + #include "engine/IO/parser.h" #include "engine/renderer/mesh.h" -#include "engine/renderer/wavefront.h" #define DEFAULT_MATERIAL_NAME "default" diff --git a/engine/src/scene/scene.cpp b/engine/src/scene/scene.cpp new file mode 100644 index 0000000..1b5af07 --- /dev/null +++ b/engine/src/scene/scene.cpp @@ -0,0 +1,4 @@ +#include "engine/scene/scene.h" + +Scene::Scene() : m_registry() { +} diff --git a/engine/src/window/window.cpp b/engine/src/window/window.cpp index ba66723..f040632 100644 --- a/engine/src/window/window.cpp +++ b/engine/src/window/window.cpp @@ -100,11 +100,11 @@ void Window::ProcessEvents() { switch (event.type) { case SDL_EVENT_WINDOW_CLOSE_REQUESTED: case SDL_EVENT_QUIT: - Dispatch(WindowCloseRequested()); + Dispatch(WindowCloseEvent()); break; case SDL_EVENT_KEY_DOWN: if (event.key.scancode == SDL_SCANCODE_ESCAPE) { - Dispatch(WindowCloseRequested()); + Dispatch(WindowCloseEvent()); } if (event.key.scancode == SDL_SCANCODE_F11) { bool isFullscreen = SDL_GetWindowFlags(m_handle) & SDL_WINDOW_FULLSCREEN; @@ -122,7 +122,7 @@ void Window::ProcessEvents() { 0, width, height); - Dispatch(WindowResized{ m_width, m_height }); + Dispatch(WindowResizeEvent(static_cast(m_width), static_cast(m_height))); SDL_SetWindowRelativeMouseMode(m_handle, true); SDL_Rect boundaries = {0, 0, m_width, m_height}; SDL_SetWindowMouseRect(m_handle, &boundaries); diff --git a/sandbox/src/main.cpp b/sandbox/src/main.cpp index 5909727..33a73ea 100644 --- a/sandbox/src/main.cpp +++ b/sandbox/src/main.cpp @@ -9,7 +9,6 @@ #endif #include "engine/renderer/wavefront.h" -#include "engine/renderer/renderer.h" #include "engine/app/app.h" @@ -24,55 +23,57 @@ class Game : public IApplication { public: - Game() : m_renderer(m_registry) { + Game() {} + ~Game() override {} + + void OnInit(std::shared_ptr scene) override { + m_scene = scene; + Object* lightObj = Object::LoadFile("./assets/common/sphere/sphere.obj"); - const auto lght = m_registry.create(); - m_registry.emplace(lght, glm::vec3(5.f, 5.f, 5.f), glm::vec3(0.f)); - m_registry.emplace(lght, light::LightType::DIRECTIONAL, glm::vec3(1.f, 1.f, 1.f), 1.5f); - m_registry.emplace(lght, std::shared_ptr(lightObj)); + const auto lght = scene->m_registry.create(); + scene->m_registry.emplace(lght, glm::vec3(5.f, 5.f, 5.f), glm::vec3(0.f)); + scene->m_registry.emplace(lght, light::LightType::DIRECTIONAL, glm::vec3(1.f, 1.f, 1.f), 1.5f); + scene->m_registry.emplace(lght, std::shared_ptr(lightObj)); - const auto cameraEntity = m_registry.create(); - m_registry.emplace(cameraEntity, glm::vec3(0.f, 2.f, 2.f)); - m_registry.emplace(cameraEntity); + const auto cameraEntity = scene->m_registry.create(); + scene->m_registry.emplace(cameraEntity, glm::vec3(0.f, 2.f, 2.f)); + scene->m_registry.emplace(cameraEntity); - Object* targetObj = Object::LoadFile("./assets/car/car.obj"); - const auto targetEntity = m_registry.create(); - m_registry.emplace(targetEntity, glm::vec3(0.f, 0.0f, 0.f)); - m_registry.emplace(targetEntity, std::shared_ptr(targetObj)); - m_registry.emplace(targetEntity); + Object* targetObj = Object::LoadFile("./assets/wizard/wizard.obj"); + const auto targetEntity = scene->m_registry.create(); + scene->m_registry.emplace(targetEntity, glm::vec3(0.f, 0.0f, 0.f)); + scene->m_registry.emplace(targetEntity, std::shared_ptr(targetObj)); + scene->m_registry.emplace(targetEntity); Object* grass = Object::LoadFile("./assets/common/cube/cube.obj"); - const auto cubeEntity = m_registry.create(); - m_registry.emplace(cubeEntity, glm::vec3(-1.5f, 0.4f, 0.f)); - m_registry.emplace(cubeEntity, std::shared_ptr(grass)); + const auto cubeEntity = scene->m_registry.create(); + scene->m_registry.emplace(cubeEntity, glm::vec3(-1.5f, 0.4f, 0.f)); + scene->m_registry.emplace(cubeEntity, std::shared_ptr(grass)); // Cube template (use shared object to avoid reloading 1000 times) std::shared_ptr cubeObj = std::shared_ptr(Object::LoadFile("./assets/grass_block/grass_block.obj")); - const auto batchEntt = m_registry.create(); - m_registry.emplace(batchEntt); - m_registry.emplace(batchEntt, cubeObj); - auto cubeBatch = m_registry.get(batchEntt); + const auto batchEntt = scene->m_registry.create(); + scene->m_registry.emplace(batchEntt); + scene->m_registry.emplace(batchEntt, cubeObj); + auto cubeBatch = scene->m_registry.get(batchEntt); // Generate 1000 random cubes for (int i = 0; i < 1000; ++i) { - const auto cubeEntity = m_registry.create(); + const auto cubeEntity = scene->m_registry.create(); float x = static_cast(rand()) / RAND_MAX * 200.f - 100.f; // range [-100, 100] float y = static_cast(rand()) / RAND_MAX * 10.f; // range [0, 10] float z = static_cast(rand()) / RAND_MAX * 200.f - 100.f; // range [-100, 100] - m_registry.emplace(cubeEntity, glm::vec3(x, y, z)); - m_registry.emplace(cubeEntity); - m_registry.emplace(cubeEntity, cubeBatch.id()); + scene->m_registry.emplace(cubeEntity, glm::vec3(x, y, z)); + scene->m_registry.emplace(cubeEntity); + scene->m_registry.emplace(cubeEntity, cubeBatch.id()); } Object* floorObj = Object::LoadFile("./assets/common/plane/plane.obj"); - const auto floorEntt = m_registry.create(); - m_registry.emplace(floorEntt, glm::vec3(0.f)); - m_registry.emplace(floorEntt, std::shared_ptr(floorObj)); - } - ~Game() override {} + const auto floorEntt = scene->m_registry.create(); + scene->m_registry.emplace(floorEntt, glm::vec3(0.f)); + scene->m_registry.emplace(floorEntt, std::shared_ptr(floorObj)); - void OnInit() override { std::cout << "Game initialized" << std::endl; m_angle = 3.45f; @@ -86,13 +87,6 @@ public: // FPS tracking m_startTicks = SDL_GetTicks(); m_frameCount = 0; - - m_renderer.Init(); - m_renderer.GenerateShadowMaps(); - } - - void OnWindowResized(const WindowResized& event) override { - m_renderer.OnWindowResized(event.w, event.h); } void OnUpdate() override { @@ -136,7 +130,7 @@ public: if (state[SDL_SCANCODE_SPACE]) velocity.y += 1.f; if (state[SDL_SCANCODE_LSHIFT]) velocity.y -= 1.f; - auto view = m_registry.view(); + auto view = m_scene->m_registry.view(); for (auto [cam, camTransform] : view.each()) { camTransform.position += velocity * deltaTime * 2.5f; // speed is e.g. 2.5f camTransform.rotation = cameraViewDirection; @@ -173,7 +167,7 @@ public: glm::vec3 sunColor = glm::mix(dayColor, sunsetColor, sunsetFactor); // Update the directional light in the registry - auto lightsView = m_registry.view(); + auto lightsView = m_scene->m_registry.view(); for (auto [entity, l, t] : lightsView.each()) { if (l.type == light::LightType::DIRECTIONAL) { // "position" for directional light often stores direction vector @@ -184,17 +178,13 @@ public: } } - auto rotateEntts = m_registry.view(); + auto rotateEntts = m_scene->m_registry.view(); for (auto [entity, t] : rotateEntts.each()) { // auto targetTransform = rotateEntts.get(entity); - if (!m_registry.all_of(entity)) { + if (!m_scene->m_registry.all_of(entity)) { t.rotation.y = m_angle; } } - } - - void OnRender() override { - m_renderer.Render(); m_frameCount++; m_currentTicks = SDL_GetTicks(); @@ -207,9 +197,18 @@ public: m_startTicks = m_currentTicks; } } + + void OnEvent(const Event& event) override { + if (event.GetType() == EventType::WINDOW_RESIZE) { + auto resizeEvent = static_cast(event); + std::cout << "[DEBUG] Window resized to " << resizeEvent.GetWidth() << "x" << resizeEvent.GetHeight() << std::endl; + } + else if (event.GetType() == EventType::WINDOW_CLOSE) { + std::cout << "[DEBUG] Window closing" << std::endl; + } + } private: - Renderer m_renderer; - entt::registry m_registry; + std::shared_ptr m_scene; float m_angle; Uint64 m_lastTicks;