diff --git a/CMakeLists.txt b/CMakeLists.txt index 669582b..82c9241 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,7 @@ set(CMAKE_CXX_EXTENSIONS OFF) option(PRODUCTION "Enable production build flags" OFF) option(SANDBOX_SCRIPTS "Enable script sandboxing build flags" OFF) +option(CWD "Current Resources Dir" "") execute_process( @@ -42,6 +43,10 @@ if(NOT PRODUCTION) DESTINATION "${ENGINE_SRC_DEST}" PATTERN "Examples" EXCLUDE) + file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/Tests" + DESTINATION "${ENGINE_SRC_DEST}" + PATTERN "Examples" EXCLUDE) + file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/vendor" DESTINATION "${ENGINE_SRC_DEST}" PATTERN "Examples" EXCLUDE) @@ -49,12 +54,22 @@ if(NOT PRODUCTION) file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt" DESTINATION "${ENGINE_SRC_DEST}") endif() - - -file(GLOB_RECURSE GameDataSet CONFIGURE_DEPENDS "GameData/**") -cmrc_add_resource_library(GameData ${GameDataSet}) -file(GLOB_RECURSE PROD_SCRIPTS CONFIGURE_DEPENDS "Scripts/*.cpp") +message(STATUS "CWD = ${CWD}") +file(GLOB_RECURSE GameDataSet CONFIGURE_DEPENDS "${CWD}GameData/**") +cmrc_add_resource_library(GameData WHENCE "${CWD}" ${GameDataSet}) +message(STATUS "============ GameData Resources Found: =============") +foreach(res ${GameDataSet}) + message(STATUS "* " ${res}) +endforeach() +message(STATUS "================ GameData Resources ================") + +file(GLOB_RECURSE PROD_SCRIPTS CONFIGURE_DEPENDS "${CWD}Scripts/*.cpp") +message(STATUS "================== Scripts Found: ==================") +foreach(res ${PROD_SCRIPTS}) + message(STATUS "* " ${res}) +endforeach() +message(STATUS "====================== Scripts =====================") add_subdirectory(Engine) diff --git a/Engine/Core/CMakeLists.txt b/Engine/Core/CMakeLists.txt index 05c96e1..f5ba493 100644 --- a/Engine/Core/CMakeLists.txt +++ b/Engine/Core/CMakeLists.txt @@ -38,6 +38,7 @@ add_library(Core STATIC ${src} ${PROD_SCRIPTS}) target_link_libraries(Core CWindow Utils) target_link_libraries(Core GameData) target_include_directories(Core PUBLIC "Core/" "../ScriptShared") +target_include_directories(Core PUBLIC "${CMAKE_SOURCE_DIR}") target_compile_definitions(Core PRIVATE ENGINE_SRC_DEST="${ENGINE_SRC_DEST}") target_compile_definitions(Core PRIVATE PRODUCTION) diff --git a/Engine/Core/Core/Camera/Camera.cpp b/Engine/Core/Core/Camera/Camera.cpp index 61b3e59..8ee913d 100644 --- a/Engine/Core/Core/Camera/Camera.cpp +++ b/Engine/Core/Core/Camera/Camera.cpp @@ -6,6 +6,7 @@ #include "Camera.h" +#include "Objects/GameObject.h" @@ -16,7 +17,10 @@ Engine::Core::Camera::Camera() noexcept { if (!renderer || !renderer->getWindowData()) { Engine::Utils::Logger::get().warn("Engine::Core::Camera::Camera()", "renderer is nullptr"); + return; }; + + fbo = CW::Renderer::Framebuffer(fbo_size.x, fbo_size.y); }; @@ -60,6 +64,10 @@ Engine::Core::Camera::Camera(const Camera &second) noexcept orthogonal_mat_ready(second.orthogonal_mat_ready), perspective_mat(second.perspective_mat), orthogonal_mat(second.orthogonal_mat), + fbo(second.fbo), + fbo_size(second.fbo_size), + track_window_size(second.track_window_size), + fbo_size_ready(second.fbo_size_ready), default_movemement_on(second.default_movemement_on), sensitivity(second.sensitivity), velocity(second.velocity), @@ -99,6 +107,10 @@ Engine::Core::Camera &Engine::Core::Camera::operator=(const Camera &second) noex orthogonal_near_plane = second.orthogonal_near_plane; perspective_far_plane = second.perspective_far_plane; orthogonal_far_plane = second.orthogonal_far_plane; + fbo = second.fbo; + fbo_size = second.fbo_size; + track_window_size = second.track_window_size; + fbo_size_ready = second.fbo_size_ready; perspective_mat_ready = second.perspective_mat_ready; orthogonal_mat_ready = second.orthogonal_mat_ready; perspective_mat = second.perspective_mat; @@ -140,6 +152,10 @@ Engine::Core::Camera::Camera(Camera &&second) noexcept orthogonal_mat_ready(std::move(second.orthogonal_mat_ready)), perspective_mat(std::move(second.perspective_mat)), orthogonal_mat(std::move(second.orthogonal_mat)), + fbo(std::move(second.fbo)), + fbo_size(std::move(second.fbo_size)), + track_window_size(std::move(second.track_window_size)), + fbo_size_ready(std::move(second.fbo_size_ready)), default_movemement_on(std::move(second.default_movemement_on)), sensitivity(std::move(second.sensitivity)), velocity(std::move(second.velocity)), @@ -152,6 +168,8 @@ Engine::Core::Camera::Camera(Camera &&second) noexcept if (!renderer || !renderer->getWindowData()) { Engine::Utils::Logger::get().warn("Engine::Core::Camera::Camera(Camera &&second)", "renderer is nullptr"); }; + + second.renderer = nullptr; }; @@ -183,6 +201,10 @@ Engine::Core::Camera& Engine::Core::Camera::operator=(Camera &&second) noexcept orthogonal_mat_ready = std::move(second.orthogonal_mat_ready); perspective_mat = std::move(second.perspective_mat); orthogonal_mat = std::move(second.orthogonal_mat); + fbo = std::move(second.fbo); + fbo_size = std::move(second.fbo_size); + track_window_size = std::move(second.track_window_size); + fbo_size_ready = std::move(second.fbo_size_ready); default_movemement_on = std::move(second.default_movemement_on); sensitivity = std::move(second.sensitivity); velocity = std::move(second.velocity); @@ -192,6 +214,8 @@ Engine::Core::Camera& Engine::Core::Camera::operator=(Camera &&second) noexcept cursor_visible_lock = std::move(second.cursor_visible_lock); cursor_lock = std::move(second.cursor_lock); + second.renderer = nullptr; + return *this; }; @@ -476,6 +500,39 @@ void Engine::Core::Camera::setCameraMode(Engine::ScriptShared::CameraMode mode) +glm::ivec2 Engine::Core::Camera::getFboSize() const noexcept { + return fbo_size; +}; + + + +void Engine::Core::Camera::setFboSize(glm::ivec2 size) noexcept { + fbo_size = size; + if(fbo_size.x < 0) fbo_size.x = 0; + if(fbo_size.y < 0) fbo_size.y = 0; + fbo_size_ready = false; +}; + + + +CW::Renderer::Framebuffer &Engine::Core::Camera::getFbo() noexcept { + return fbo; +}; + + + +void Engine::Core::Camera::setTrackWindowSize(bool track) noexcept { + track_window_size = track; +}; + + + +bool Engine::Core::Camera::getTrackWindowSize() const noexcept { + return track_window_size; +}; + + + bool Engine::Core::Camera::getDefaultMovement() const noexcept { return default_movemement_on; }; @@ -526,6 +583,61 @@ void Engine::Core::Camera::setMouseActive(bool active) noexcept{ +//// ================ //// +//// ==== Render ==== //// +//// ================ //// +void Engine::Core::Camera::render(std::vector &objects) noexcept{ + if (!renderer || !renderer->getWindowData()) { + Engine::Utils::Logger::get().warn("Engine::Core::Camera::render(std::vector &objects)", "renderer is nullptr {skipping}"); + return; + }; + + if(track_window_size) autoSizeFboToWindow(); + if(!fbo_size_ready) { + fbo.rescale(fbo_size.x, fbo_size.y); + fbo_size_ready = true; + }; + + + fbo.bind(); + CW::Renderer::Uniform uniform; + for(Engine::Core::GameObject& object : objects) object.render(renderer, static_cast(*this), static_cast(*this), uniform); + fbo.unbind(); +}; + + + +void Engine::Core::Camera::autoSizeFboToWindow() noexcept { + if (!renderer || !renderer->getWindowData()) { + Engine::Utils::Logger::get().warn("Engine::Core::Camera::autoSizeToWindow()", "renderer is nullptr {skipping}"); + return; + }; + + if(renderer->getWindowData()->width == fbo_size.x && renderer->getWindowData()->height == fbo_size.y) return; + setFboSize({renderer->getWindowData()->width, renderer->getWindowData()->height}); +}; + + + +void Engine::Core::Camera::clearFbo() noexcept { + if (!renderer || !renderer->getWindowData()) { + Engine::Utils::Logger::get().warn("Engine::Core::Camera::clearFbo()", "renderer is nullptr {skipping}"); + return; + }; + + if(track_window_size) autoSizeFboToWindow(); + if(!fbo_size_ready) { + fbo.rescale(fbo_size.x, fbo_size.y); + fbo_size_ready = true; + }; + + fbo.bind(); + renderer->beginFrame(); + fbo.unbind(); +}; + + + //// ================== //// //// ==== Movement ==== //// //// ================== //// diff --git a/Engine/Core/Core/Camera/Camera.h b/Engine/Core/Core/Camera/Camera.h index 70d17ef..be4644a 100644 --- a/Engine/Core/Core/Camera/Camera.h +++ b/Engine/Core/Core/Camera/Camera.h @@ -7,6 +7,7 @@ #pragma once #include "Renderer.h" +#include "Renderer/OpenGL/Framebuffer.h" #define GLM_ENABLE_EXPERIMENTAL #include "../vendor/glm/glm/gtx/euler_angles.hpp" @@ -19,6 +20,12 @@ +namespace Engine::Core{ + class GameObject; +}; + + + namespace Engine::Core { class Camera : public Engine::ScriptShared::ICamera { //////// ============================================ //////// @@ -58,7 +65,16 @@ class Camera : public Engine::ScriptShared::ICamera { float orthogonal_far_plane = Engine::Config::CAMERA_ORTHO_FAR_PLANE; bool orthogonal_mat_ready = false; glm::mat4 orthogonal_mat = glm::mat4(1.0f); - + +//// ================ //// +//// ==== Render ==== //// +//// ================ //// +private: + CW::Renderer::Framebuffer fbo = CW::Renderer::Framebuffer(); + glm::ivec2 fbo_size = glm::ivec2(0); + bool track_window_size = true; + bool fbo_size_ready = true; + //// ================== //// //// ==== Movement ==== //// //// ================== //// @@ -135,6 +151,12 @@ class Camera : public Engine::ScriptShared::ICamera { Engine::ScriptShared::CameraMode getCameraMode() const noexcept; void setCameraMode(Engine::ScriptShared::CameraMode mode) noexcept; + + glm::ivec2 getFboSize() const noexcept; + void setFboSize(glm::ivec2 size) noexcept; + CW::Renderer::Framebuffer& getFbo() noexcept; + void setTrackWindowSize(bool track) noexcept; + bool getTrackWindowSize() const noexcept; bool getDefaultMovement() const noexcept; void setDefaultMovement(bool state) noexcept; @@ -146,6 +168,14 @@ class Camera : public Engine::ScriptShared::ICamera { bool getMouseActive() const noexcept; void setMouseActive(bool active) noexcept; +//// ================ //// +//// ==== Render ==== //// +//// ================ //// +public: + void render(std::vector& objects) noexcept; + void autoSizeFboToWindow() noexcept; + void clearFbo() noexcept; + //// ================== //// //// ==== Movement ==== //// //// ================== //// @@ -158,9 +188,5 @@ class Camera : public Engine::ScriptShared::ICamera { void velocityButtons(float delta_time) noexcept; void movementButtons(float delta_time, float& target_bank) noexcept; void rotationButtons(float delta_time, float& target_bank) noexcept; - - // [TODO] - // render - // culling }; }; diff --git a/Engine/Core/Core/Camera/CameraController.cpp b/Engine/Core/Core/Camera/CameraController.cpp index bcb499a..b9e3d78 100644 --- a/Engine/Core/Core/Camera/CameraController.cpp +++ b/Engine/Core/Core/Camera/CameraController.cpp @@ -32,11 +32,14 @@ Engine::Core::CameraController::~CameraController() noexcept {}; Engine::Core::CameraController::CameraController(const CameraController &second) noexcept :renderer(second.renderer), cameras(second.cameras), - active_camera(second.active_camera) + active_camera(second.active_camera) { if(renderer == nullptr){ Engine::Utils::Logger::get().warn("Engine::Core::CameraController::CameraController(const CameraController &second)", "renderer is nullptr"); }; + + active_camera_setted = cameraExists(active_camera); + if(active_camera_setted) active_camera_ref = &getCoreCamera(active_camera); }; @@ -51,7 +54,9 @@ Engine::Core::CameraController &Engine::Core::CameraController::operator=(const renderer = second.renderer; cameras = second.cameras; active_camera = second.active_camera; - + active_camera_setted = cameraExists(active_camera); + if(active_camera_setted) active_camera_ref = &getCoreCamera(active_camera); + return *this; }; @@ -61,11 +66,19 @@ Engine::Core::CameraController &Engine::Core::CameraController::operator=(const Engine::Core::CameraController::CameraController(CameraController &&second) noexcept :renderer(std::move(second.renderer)), cameras(std::move(second.cameras)), - active_camera(std::move(second.active_camera)) + active_camera(std::move(second.active_camera)) { if(renderer == nullptr){ Engine::Utils::Logger::get().warn("Engine::Core::CameraController::CameraController(CameraController &&second)", "renderer is nullptr"); }; + + active_camera_setted = cameraExists(active_camera); + if(active_camera_setted) active_camera_ref = &getCoreCamera(active_camera); + + second.renderer = nullptr; + second.active_camera_ref = nullptr; + second.active_camera_setted = false; + }; @@ -80,6 +93,12 @@ Engine::Core::CameraController &Engine::Core::CameraController::operator=(Camera renderer = std::move(second.renderer); cameras = std::move(second.cameras); active_camera = std::move(second.active_camera); + active_camera_setted = cameraExists(active_camera); + if(active_camera_setted) active_camera_ref = &getCoreCamera(active_camera); + + second.renderer = nullptr; + second.active_camera_ref = nullptr; + second.active_camera_setted = false; return *this; }; @@ -96,11 +115,13 @@ void Engine::Core::CameraController::setActiveCamera(const std::string &name) no }; active_camera = name; + active_camera_setted = cameraExists(active_camera); + active_camera_ref = &getCoreCamera(active_camera); }; -std::string Engine::Core::CameraController::getActiveCameraName() const noexcept{ +std::string Engine::Core::CameraController::getActiveCameraName() const noexcept { if(!cameraExists(active_camera)){ Engine::Utils::Logger::get().warn("Engine::Core::CameraController::getActiveCameraName()", "Camera: " + active_camera + " didn't exists (returning '')"); return ""; @@ -112,12 +133,23 @@ std::string Engine::Core::CameraController::getActiveCameraName() const noexcept Engine::ScriptShared::ICamera &Engine::Core::CameraController::getActiveCamera() { - if(!cameraExists(active_camera)){ - Engine::Utils::Logger::get().erro("Engine::Core::CameraController::getActiveCamera()", "Camera: " + active_camera + " didn't exists (throwing runtime_error)"); - throw std::runtime_error("Engine::Core::CameraController::getActiveCamera() -> Camera: " + active_camera + " didn't exists"); + if(!active_camera_setted){ + Engine::Utils::Logger::get().erro("Engine::Core::CameraController::getActiveCamera()", "Camera: " + active_camera + " didn't exists (returning nullptr)"); + throw std::runtime_error("Engine::Core::CameraController::getActiveCamera(const std::string &name) -> Camera: " + active_camera + " didn't exists"); }; - return cameras[active_camera]; + return *active_camera_ref; +}; + + + +Engine::Core::Camera &Engine::Core::CameraController::getCoreActiveCamera() { + if(!active_camera_setted){ + Engine::Utils::Logger::get().erro("Engine::Core::CameraController::getActiveCamera()", "Camera: " + active_camera + " didn't exists (returning nullptr)"); + throw std::runtime_error("Engine::Core::CameraController::getCoreActiveCamera(const std::string &name) -> Camera: " + active_camera + " didn't exists"); + }; + + return *active_camera_ref; }; @@ -131,6 +163,9 @@ void Engine::Core::CameraController::spawnCamera(const std::string &name, glm::v cameras.try_emplace(name, renderer, position, direction); Engine::Utils::Logger::get().info("Engine::Core::CameraController::spawnCamera(const std::string &name, glm::vec3 position, glm::vec3 direction)", "Spawned Camera: " + name); + + active_camera_setted = cameraExists(active_camera); + if(active_camera_setted) active_camera_ref = &getCoreCamera(active_camera); }; @@ -149,23 +184,26 @@ void Engine::Core::CameraController::deleteCamera(const std::string &name) noexc active_camera.clear(); Engine::Utils::Logger::get().info("Engine::Core::CameraController::deleteCamera(const std::string &name)", "Active Camera Reset"); }; + + active_camera_setted = cameraExists(active_camera); + if(active_camera_setted) active_camera_ref = &getCoreCamera(active_camera); }; +Engine::ScriptShared::ICamera &Engine::Core::CameraController::getCamera(const std::string &name) noexcept { + return cameras[name]; +}; + -Engine::ScriptShared::ICamera &Engine::Core::CameraController::getCamera(const std::string &name) { - if(!cameraExists(name)){ - Engine::Utils::Logger::get().erro("Engine::Core::CameraController::getCamera(const std::string &name)", "Camera: " + name + " didn't exists (throwing runtime_error)"); - throw std::runtime_error("Engine::Core::CameraController::getCamera(const std::string &name) -> Camera: " + name + " didn't exists"); - }; +Engine::Core::Camera &Engine::Core::CameraController::getCoreCamera(const std::string &name) noexcept { return cameras[name]; }; -bool Engine::Core::CameraController::cameraExists(const std::string &name) const noexcept{ +bool Engine::Core::CameraController::cameraExists(const std::string &name) const noexcept { const auto& it = cameras.find(name); if(it != cameras.end()) return true; return false; diff --git a/Engine/Core/Core/Camera/CameraController.h b/Engine/Core/Core/Camera/CameraController.h index 39897f0..5d7e246 100644 --- a/Engine/Core/Core/Camera/CameraController.h +++ b/Engine/Core/Core/Camera/CameraController.h @@ -8,15 +8,11 @@ #pragma once #include "Renderer.h" -#define GLM_ENABLE_EXPERIMENTAL -#include "../vendor/glm/glm/gtx/euler_angles.hpp" -#include "../vendor/glm/glm/gtx/quaternion.hpp" - #include "ScriptShared/CameraController.h" +#include "Camera.h" #include "Utils/config.h" #include "Utils/Logger.h" -#include "Camera.h" @@ -27,8 +23,10 @@ class CameraController : public Engine::ScriptShared::ICameraController { //////// ============================================ //////// private: CW::Renderer::Renderer* renderer = nullptr; + Engine::Core::Camera* active_camera_ref = nullptr; std::unordered_map cameras{}; std::string active_camera = ""; + bool active_camera_setted = false; @@ -56,11 +54,13 @@ class CameraController : public Engine::ScriptShared::ICameraController { void setActiveCamera(const std::string& name) noexcept; std::string getActiveCameraName() const noexcept; Engine::ScriptShared::ICamera& getActiveCamera(); + Engine::Core::Camera& getCoreActiveCamera(); void spawnCamera(const std::string& name, glm::vec3 position = {0.0f, 0.0f, 0.0f}, glm::vec3 direction = {0.0f, 0.0f, 1.0f}) noexcept; void deleteCamera(const std::string& name) noexcept; - Engine::ScriptShared::ICamera& getCamera(const std::string& name); + Engine::ScriptShared::ICamera& getCamera(const std::string& name) noexcept; + Engine::Core::Camera& getCoreCamera(const std::string& name) noexcept; bool cameraExists(const std::string& name) const noexcept; }; diff --git a/Engine/Core/Core/Core.cpp b/Engine/Core/Core/Core.cpp index 1e739a1..48437bf 100644 --- a/Engine/Core/Core/Core.cpp +++ b/Engine/Core/Core/Core.cpp @@ -74,12 +74,12 @@ void Engine::Core::Core::render(){ CW::Renderer::Framebuffer& Engine::Core::Core::get_fbo(){ -#ifndef PRODUCTION - if(scene.post_processing_on) return scene.post_fbo; - else return scene.fbo; -#else - return scene.post_fbo; -#endif +// #ifndef PRODUCTION +// if(scene.post_processing_on) return scene.post_fbo; +// else return scene.fbo; +// #else +// #endif + return scene.getFbo(); }; diff --git a/Engine/Core/Core/DataSerializer/MeshSerialization.cpp b/Engine/Core/Core/DataSerializer/MeshSerialization.cpp index 2666672..6fed058 100644 --- a/Engine/Core/Core/DataSerializer/MeshSerialization.cpp +++ b/Engine/Core/Core/DataSerializer/MeshSerialization.cpp @@ -90,7 +90,7 @@ void Engine::MeshSerialization::load(const std::string& path_to_mesh, Engine::Ut std::memcpy(vertices.data(), e.data.data(), e.data.size()); engine_mesh.addVertices(vertices, e.dimension, e.key); } else { - Engine::Utils::uploadBufferByType(engine_mesh, e.type, e.data, e.dimension, e.key); + Engine::Utils::uploadBufferByType(engine_mesh, e.data, e.dimension, e.key, e.type); }; }; @@ -126,13 +126,13 @@ void Engine::MeshSerialization::loadAll(Engine::Utils::ResourceController mesh_files; + std::vector mesh_files; #ifndef PRODUCTION if (std::filesystem::exists(meshes_root) && std::filesystem::is_directory(meshes_root)) { for (const auto& entry : std::filesystem::recursive_directory_iterator(meshes_root)) { if (entry.is_regular_file() && entry.path().extension() == Engine::Config::MESH_EXTENSION) { - mesh_files.push_back(entry.path().string()); + mesh_files.push_back(entry.path()); } } } else { @@ -148,7 +148,7 @@ void Engine::MeshSerialization::loadAll(Engine::Utils::ResourceController& scripts, Engine::ScriptShared::GameObjectData* new_data_ptr) { for (auto& record : scripts) { if (record.script) { - record.script->game_object_data = new_data_ptr; + record.script->object_data = new_data_ptr; }; }; }; diff --git a/Engine/Core/Core/Scene.cpp b/Engine/Core/Core/Scene.cpp index 3ffb2c5..d343f56 100644 --- a/Engine/Core/Core/Scene.cpp +++ b/Engine/Core/Core/Scene.cpp @@ -10,7 +10,7 @@ Engine::Core::Scene::Scene(CW::Renderer::Renderer& window) - : window(window), camera_controller(&window), light_camera(&window), fbo(1920, 1080), post_fbo(1920, 1080), shadows_fbo(1920 * 5, 1080 * 5), screen_quad("screen_quad", &Engine::Core::Resources::get().meshes) + : window(window), camera_controller(&window) #ifndef PRODUCTION , debug_camera(&window) #endif @@ -31,37 +31,10 @@ void Engine::Core::Scene::onLoad(){ Engine::Utils::Logger::get().info("Scene", "Data Loaded from DataSerializer"); - - post_uniform["u_water_height"]->set(Engine::Config::WATER_HEIGHT); - post_uniform["u_Near"]->set(Engine::Config::CAMERA_NEAR_PLANE); - post_uniform["u_Far"]->set(Engine::Config::CAMERA_ORTHO_FAR_PLANE); - post_uniform["u_FogDensity"]->set(Engine::Config::FOG_DENSITY); - post_uniform["u_FogColor"]->set(Engine::Config::FOG_COLOR); - Engine::Utils::Logger::get().info("Scene", "PostProcessing Uniforms Initialized"); - - - light_camera.setCameraMode(Engine::ScriptShared::CameraMode::PERSPECTIVE); - light_camera.setFov(110.0f); - last_light_camera_fov = light_camera.getFov(); - light_camera.setPosition(Engine::Core::Resources::get().lights[0].position); - last_light_camera_pos = light_camera.getPosition(); - light_camera.setDirection(glm::normalize(-Engine::Core::Resources::get().lights[0].position)); - last_light_camera_dir = light_camera.getDirection(); - light_space_matrix = light_camera.transformation(); - - shadows_uniform_off["u_ShadowEnabled"]->set(0); - shadows_uniform_off["u_ShadowDepthTexture"]->set(16); - shadows_uniform_off["u_LightSpaceMatrix"]->set(light_space_matrix); - shadows_uniform_on["u_ShadowEnabled"]->set(1); - shadows_uniform_on["u_ShadowDepthTexture"]->set(16); - shadows_uniform_on["u_LightSpaceMatrix"]->set(light_space_matrix); - Engine::Utils::Logger::get().info("Scene", "Shadows Camera and Uniform Initialized"); - - camera_controller.spawnCamera( "Main", - {174.780f, 26.939f, -80.027f}, - {-0.847f, -0.466f, -0.256f} + {0.0f, 0.0f, 40.0f}, + {0.0f, 0.0f, -1.0f} ); camera_controller.setActiveCamera("Main"); @@ -69,20 +42,30 @@ void Engine::Core::Scene::onLoad(){ #ifndef PRODUCTION - debug_camera.setPosition({453.198f, 250.233f, -26.842f}); - debug_camera.setDirection({-0.668f, -0.734f, -0.122f}); + debug_camera.setPosition({0.0f, 0.0f, 0.0f}); + debug_camera.setDirection({0.0f, 0.0f, -1.0f}); debug_camera.setDefaultMovement(true); Engine::Utils::Logger::get().info("Scene", "Debug Camera Initialized"); #endif - +}; + - compileShadows(); - Engine::Utils::Logger::get().info("Scene", "Shadows Compiled"); + +CW::Renderer::Framebuffer &Engine::Core::Scene::getFbo(){ +#ifndef PRODUCTION + if(debug_camera_on) return debug_camera.getFbo(); + else +#endif + return camera_controller.getCoreActiveCamera().getFbo(); }; void Engine::Core::Scene::onUpdate(float delta_time){ +#ifndef PRODUCTION + if(debug_camera_on) debug_camera.event(delta_time); + else +#endif camera_controller.getActiveCamera().event(delta_time); unsigned int size = Engine::ObjectManager::get().objects.size(); @@ -176,144 +159,25 @@ void Engine::Core::Scene::onDestroy() { -void Engine::Core::Scene::compileShadows(){ - shadows_fbo.bind(); - - if(last_light_camera_pos != light_camera.getPosition()){ - light_camera.setFov(110.0f); - last_light_camera_fov = light_camera.getFov(); - - light_camera.setPosition(Engine::Core::Resources::get().lights[0].position); - last_light_camera_pos = light_camera.getPosition(); - - light_camera.setDirection(glm::normalize(-Engine::Core::Resources::get().lights[0].position)); - last_light_camera_dir = light_camera.getDirection(); - - light_space_matrix = light_camera.transformation(); - shadows_uniform_off["u_LightSpaceMatrix"]->set(light_space_matrix); - shadows_uniform_on["u_LightSpaceMatrix"]->set(light_space_matrix); - }; - - window.beginFrame(); - - for(Engine::Core::GameObject& object : Engine::ObjectManager::get().objects) object.render(&window, light_camera, light_camera, shadows_uniform_off); - for(Engine::Core::GameObject& object : Engine::ObjectManager::get().script_objects) object.render(&window, light_camera, light_camera, shadows_uniform_off); - - shadows_fbo.unbind(); -}; - - - void Engine::Core::Scene::render(){ Engine::Core::Resources::get().lights.bind(0); Engine::Core::Resources::get().materials.bind(1); - -#ifndef PRODUCTION - if(!shadows_on){ - shadows_fbo.bind(); - window.beginFrame(); - shadows_fbo.unbind(); - } - else -#endif - compileShadows(); - - - fbo.bind(); - #ifndef PRODUCTION if(debug_camera_on) renderFrame(debug_camera); else #endif - renderFrame(this->camera_controller.getActiveCamera()); - - fbo.unbind(); - + renderFrame(this->camera_controller.getCoreActiveCamera()); Engine::Core::Resources::get().materials.unbind(); Engine::Core::Resources::get().lights.unbind(); - - - window.beginFrame(); - - - // fbo.blitToScreen(window.getWindowData()->width, window.getWindowData()->height); - // else - // #endif - -#ifndef PRODUCTION - if(post_processing_on) -#endif - postProcessing(); }; -void Engine::Core::Scene::renderFrame(Engine::ScriptShared::ICamera& camera){ - window.beginFrame(); - - glActiveTexture(GL_TEXTURE16); - glBindTexture(GL_TEXTURE_2D, shadows_fbo.getDepthTexture()); - - for(Engine::Core::GameObject& object : Engine::ObjectManager::get().script_objects) object.render(&window, this->camera_controller.getActiveCamera(), camera, shadows_uniform_on); - for(Engine::Core::GameObject& object : Engine::ObjectManager::get().objects) object.render(&window, this->camera_controller.getActiveCamera(), camera, shadows_uniform_on); - - - glActiveTexture(GL_TEXTURE16); - glBindTexture(GL_TEXTURE_2D, 0); -}; - - - -void Engine::Core::Scene::postProcessing(){ - CW::Renderer::Mesh* screen_mesh = screen_quad.get(); - if(!screen_mesh) return; - - post_fbo.bind(); - - window.beginFrame(); - - glDisable(GL_DEPTH_TEST); - - std::string shader_name = "PostProcessing"; - - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, fbo.getColorTexture()); - post_uniform["u_SceneColorTexture"]->set(0); - - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, fbo.getDepthTexture()); - post_uniform["u_SceneDepthTexture"]->set(1); - -#ifndef PRODUCTION - if(debug_camera_on){ - glm::mat4 invViewProj = glm::inverse(debug_camera.projection() * debug_camera.view()); - post_uniform["u_InvViewProj"]->set(invViewProj); - post_uniform["u_CamPos"]->set(debug_camera.getPosition()); - } - else{ -#endif - glm::mat4 invViewProj = glm::inverse(camera_controller.getActiveCamera().projection() * camera_controller.getActiveCamera().view()); - post_uniform["u_InvViewProj"]->set(invViewProj); - post_uniform["u_CamPos"]->set(camera_controller.getActiveCamera().getPosition()); -#ifndef PRODUCTION - } -#endif - - Engine::Core::Resources::get().getShader(shader_name).getUniforms().emplace_back(&post_uniform); - Engine::Core::Resources::get().getShader(shader_name).bind(); - - screen_mesh->render(); - - Engine::Core::Resources::get().getShader(shader_name).unbind(); - Engine::Core::Resources::get().getShader(shader_name).getUniforms().clear(); - - glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, 0); - glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, 0); - - glEnable(GL_DEPTH_TEST); - - post_fbo.unbind(); +void Engine::Core::Scene::renderFrame(Engine::Core::Camera& camera){ + camera.clearFbo(); + camera.render(Engine::ObjectManager::get().script_objects); + camera.render(Engine::ObjectManager::get().objects); }; diff --git a/Engine/Core/Core/Scene.h b/Engine/Core/Core/Scene.h index 27e7da7..a4a1073 100644 --- a/Engine/Core/Core/Scene.h +++ b/Engine/Core/Core/Scene.h @@ -22,8 +22,6 @@ namespace Engine::Core{ class Scene{ public: - CW::Renderer::Framebuffer post_fbo; - CW::Renderer::Framebuffer fbo; Engine::Core::CameraController camera_controller; #ifndef PRODUCTION @@ -32,31 +30,13 @@ class Scene{ private: #endif CW::Renderer::Renderer& window; - CW::Renderer::Framebuffer shadows_fbo; #ifndef PRODUCTION Engine::Core::Camera debug_camera; bool debug_camera_on = Engine::Config::DEFAULT_DEBUG_CAMERA_ON; - bool shadows_on = Engine::Config::DEFAULT_SHADOWS_ON; float save_acc = 0.0f; - -public: - bool post_processing_on = Engine::Config::DEFAULT_POST_PROCESSING_ON; - -private: #endif - - Engine::Core::Camera light_camera; - CW::Renderer::Uniform shadows_uniform_on; - CW::Renderer::Uniform shadows_uniform_off; - glm::mat4 light_space_matrix; - glm::vec3 last_light_camera_pos = glm::vec3(0.0f); - glm::vec3 last_light_camera_dir = glm::vec3(0.0f); - float last_light_camera_fov = 1.0f; - - Engine::Utils::Resource screen_quad; - CW::Renderer::Uniform post_uniform; public: Scene(CW::Renderer::Renderer& window); @@ -67,11 +47,9 @@ class Scene{ void onUpdate(float delta_time); void onFixedUpdate(float fixed_delta_time); void render(); + CW::Renderer::Framebuffer& getFbo(); private: - void postProcessing(); - void compileShadows(); - void renderFrame(Engine::ScriptShared::ICamera& camera); - + void renderFrame(Engine::Core::Camera& camera); }; }; diff --git a/Engine/Core/Core/ScriptController/ScriptController.cpp b/Engine/Core/Core/ScriptController/ScriptController.cpp index 74e5eaf..a9cd1ce 100644 --- a/Engine/Core/Core/ScriptController/ScriptController.cpp +++ b/Engine/Core/Core/ScriptController/ScriptController.cpp @@ -137,7 +137,7 @@ Engine::Core::Script::GameObjectScriptRecord& Engine::Core::Script::GameObjectSc void Engine::Core::Script::GameObjectScriptRecord::syncPointer(Engine::ScriptShared::GameObjectData* data) { - if (script) script->game_object_data = data; + if (script) script->object_data = data; }; @@ -162,11 +162,11 @@ void Engine::Core::Script::GameObjectScriptRecord::observe(Engine::ScriptShared: void Engine::Core::Script::GameObjectScriptRecord::onLoad(Engine::ScriptShared::GameObjectData* data, Engine::Core::Scene& scene) { if(script){ - script->game_object_data = data; - script->glob_res = &Engine::ScriptShared::GlobResource::get(); - script->logger = static_cast(&Engine::Utils::Logger::get()); - script->object_manager = static_cast(&Engine::ObjectManager::get()); - script->camera_controller = static_cast(&scene.camera_controller); + script->object_data = data; + script->engine.glob_res = &Engine::ScriptShared::GlobResource::get(); + script->engine.logger = static_cast(&Engine::Utils::Logger::get()); + script->engine.object_manager = static_cast(&Engine::ObjectManager::get()); + script->engine.camera_controller = static_cast(&scene.camera_controller); #ifndef PRODUCTION #ifdef SANDBOX_SCRIPTS diff --git a/Engine/Editor/Editor/UI/UI.cpp b/Engine/Editor/Editor/UI/UI.cpp index a39e586..73526fe 100644 --- a/Engine/Editor/Editor/UI/UI.cpp +++ b/Engine/Editor/Editor/UI/UI.cpp @@ -311,6 +311,7 @@ void Engine::Editor::UI::buildProject(){ // compilation stage std::filesystem::path engine_src_dir = std::filesystem::path(ENGINE_SRC_DEST); std::string generator = GENERATOR; + std::string res_folder = "-DCWD=" + (std::filesystem::current_path() / dest_folder).string(); const char* config[] = { "cmake", "-S", @@ -320,6 +321,7 @@ void Engine::Editor::UI::buildProject(){ "-G", generator.c_str(), "-DPRODUCTION=ON", + res_folder.c_str(), nullptr }; diff --git a/Engine/Editor/Editor/UI/UI_Info.cpp b/Engine/Editor/Editor/UI/UI_Info.cpp index c60fc0a..489d783 100644 --- a/Engine/Editor/Editor/UI/UI_Info.cpp +++ b/Engine/Editor/Editor/UI/UI_Info.cpp @@ -67,8 +67,8 @@ inline void Engine::Editor::UI_Info::guiInfo(){ }; }; - if(ImGui::Checkbox("Post Processing", &scene.post_processing_on)); - if(ImGui::Checkbox("Shadows", &scene.shadows_on)); + // if(ImGui::Checkbox("Post Processing", &scene.post_processing_on)); + // if(ImGui::Checkbox("Shadows", &scene.shadows_on)); }; diff --git a/Engine/ScriptShared/ScriptShared/Camera.h b/Engine/ScriptShared/ScriptShared/Camera.h index e789f75..8b5bfdb 100644 --- a/Engine/ScriptShared/ScriptShared/Camera.h +++ b/Engine/ScriptShared/ScriptShared/Camera.h @@ -54,10 +54,12 @@ class ICamera { virtual float getFarOrthogonalPlane() const noexcept = 0; virtual void setFarOrthogonalPlane(float far) noexcept = 0; - virtual Engine::ScriptShared::CameraMode getCameraMode() const noexcept = 0; virtual void setCameraMode(Engine::ScriptShared::CameraMode mode) noexcept = 0; - + + virtual void autoSizeFboToWindow() noexcept = 0; + virtual void clearFbo() noexcept = 0; + virtual bool getDefaultMovement() const noexcept = 0; virtual void setDefaultMovement(bool state) noexcept = 0; @@ -70,6 +72,5 @@ class ICamera { virtual void event(float delta_time) = 0; virtual void resetMouse() = 0; - }; }; diff --git a/Engine/ScriptShared/ScriptShared/CameraController.h b/Engine/ScriptShared/ScriptShared/CameraController.h index 3ccf7d6..7465c59 100644 --- a/Engine/ScriptShared/ScriptShared/CameraController.h +++ b/Engine/ScriptShared/ScriptShared/CameraController.h @@ -30,7 +30,7 @@ class ICameraController { virtual void spawnCamera(const std::string& name, glm::vec3 position = {0.0f, 0.0f, 0.0f}, glm::vec3 direction = {0.0f, 0.0f, 1.0f}) noexcept = 0; virtual void deleteCamera(const std::string& name) noexcept = 0; - virtual Engine::ScriptShared::ICamera& getCamera(const std::string& name) = 0; + virtual Engine::ScriptShared::ICamera& getCamera(const std::string& name) noexcept = 0; virtual bool cameraExists(const std::string& name) const noexcept = 0; }; }; diff --git a/Engine/ScriptShared/ScriptShared/GameObjectScriptInterface.h b/Engine/ScriptShared/ScriptShared/GameObjectScriptInterface.h index 84e16e4..c29520e 100644 --- a/Engine/ScriptShared/ScriptShared/GameObjectScriptInterface.h +++ b/Engine/ScriptShared/ScriptShared/GameObjectScriptInterface.h @@ -29,13 +29,17 @@ namespace Engine::ScriptShared{ -class GameObjectScriptInterface { -public: - GameObjectData* game_object_data = nullptr; +struct EngineData{ GlobResource* glob_res = nullptr; ILogger* logger = nullptr; IObjectManager* object_manager = nullptr; ICameraController* camera_controller = nullptr; +}; + +class GameObjectScriptInterface { +public: + GameObjectData* object_data = nullptr; + Engine::ScriptShared::EngineData engine; virtual ~GameObjectScriptInterface() = default; diff --git a/Engine/ScriptShared/ScriptShared/ScriptRegister.h b/Engine/ScriptShared/ScriptShared/ScriptRegister.h index e103859..425caf6 100644 --- a/Engine/ScriptShared/ScriptShared/ScriptRegister.h +++ b/Engine/ScriptShared/ScriptShared/ScriptRegister.h @@ -35,7 +35,6 @@ namespace Engine::ScriptShared { }; void registerScript(const std::string& name, ScriptFactory factory) { - printf("Reg: %s\n", name.c_str()); factories[name] = factory; }; diff --git a/Engine/Utils/Utils/utils.h b/Engine/Utils/Utils/utils.h index c158ae0..f1e151d 100644 --- a/Engine/Utils/Utils/utils.h +++ b/Engine/Utils/Utils/utils.h @@ -6,9 +6,11 @@ #pragma once +#include "Renderer.h" #include #include #include +#include #ifdef PRODUCTION #include @@ -18,6 +20,7 @@ namespace Engine::Utils { constexpr inline uint32_t hash(uint32_t x) noexcept { + x = x + 0x45d9f3b; x = ((x >> 16) ^ x) * 0x45d9f3b; x = ((x >> 16) ^ x) * 0x45d9f3b; x = (x >> 16) ^ x; @@ -28,8 +31,9 @@ constexpr inline uint32_t hash(uint32_t x) noexcept { template inline void uploadTypedBuffer(CW::Renderer::Mesh& mesh, const std::vector& buffer, unsigned int dimension, unsigned int layout, GLenum type) noexcept { - if (buffer.empty()) return; - if (buffer.size() % sizeof(T) != 0) return; + if(dimension == 0) return; + if(buffer.empty()) return; + if(buffer.size() % sizeof(T) != 0) return; size_t count = buffer.size() / sizeof(T); @@ -42,18 +46,19 @@ inline void uploadTypedBuffer(CW::Renderer::Mesh& mesh, const std::vector& buffer, unsigned int dimension, unsigned int key) noexcept { - if (buffer.empty()) return; +inline void uploadBufferByType(CW::Renderer::Mesh& mesh, const std::vector& buffer, unsigned int dimension, unsigned int layout, GLenum type) noexcept { + if(dimension == 0) return; + if(buffer.empty()) return; switch (type){ case GL_FLOAT: - uploadTypedBuffer(engine_mesh, buffer, dimension, key, type); + uploadTypedBuffer(mesh, buffer, dimension, layout, type); break; case GL_UNSIGNED_INT: - uploadTypedBuffer(engine_mesh, buffer, dimension, key, type); + uploadTypedBuffer(mesh, buffer, dimension, layout, type); break; case GL_UNSIGNED_SHORT: - uploadTypedBuffer(engine_mesh, buffer, dimension, key, type); + uploadTypedBuffer(mesh, buffer, dimension, layout, type); break; default: break; @@ -63,15 +68,39 @@ inline void uploadBufferByType(CW::Renderer::Mesh& engine_mesh, GLenum type, con #ifdef PRODUCTION -inline void scanCmrcDirectory(const cmrc::embedded_filesystem& fs, const std::string& current_path, const std::string& pattern_str, std::vector& out_mesh_files) noexcept { - std::regex pattern(pattern_str); +inline void scanCmrcDirectoryHelper(const cmrc::embedded_filesystem& fs, const std::filesystem::path& current_path, const std::regex& pattern, std::vector& out_mesh_files) { + if(current_path.empty()) return; + + const std::string path_str = current_path.generic_string(); + if (fs.is_file(path_str)){ + if (std::regex_search(current_path.filename().string(), pattern)) { + out_mesh_files.emplace_back(current_path); + }; + return; + }; - for (const auto& entry : fs.iterate_directory(current_path)) { - std::string entry_path = current_path + (current_path.back() == '/' ? "" : "/") + entry.filename(); - - if (entry.is_directory()) scanCmrcDirectory(fs, entry_path, pattern_str, out_mesh_files); - else if (entry.is_file() && std::regex_search(entry.filename(), pattern)) out_mesh_files.emplace_back(entry_path); + if (!fs.is_directory(path_str)) return; + + for (const cmrc::directory_entry& entry : fs.iterate_directory(path_str)) { + std::filesystem::path entry_path = current_path / entry.filename(); + + if (entry.is_directory()) + scanCmrcDirectoryHelper(fs, entry_path, pattern, out_mesh_files); + else if (entry.is_file() && std::regex_search(entry.filename(), pattern)) + out_mesh_files.emplace_back(std::move(entry_path)); }; }; + + + +inline void scanCmrcDirectory(const cmrc::embedded_filesystem& fs, const std::filesystem::path& current_path, const std::string& pattern_str, std::vector& out_mesh_files) { + out_mesh_files.clear(); + + if(pattern_str.empty()) return; + if(current_path.empty()) return; + + std::regex pattern(pattern_str); + scanCmrcDirectoryHelper(fs, current_path, pattern, out_mesh_files); +}; #endif }; // namespace Engine::Utils diff --git a/Examples/Game/GameData/Assets/Meshes/Default.msh b/Examples/Game/GameData/Assets/Meshes/Default.msh index fa3567a..976fac0 100644 Binary files a/Examples/Game/GameData/Assets/Meshes/Default.msh and b/Examples/Game/GameData/Assets/Meshes/Default.msh differ diff --git a/Examples/Game/GameData/Assets/Meshes/screen_quad.msh b/Examples/Game/GameData/Assets/Meshes/screen_quad.msh index 37d1898..8f71699 100644 Binary files a/Examples/Game/GameData/Assets/Meshes/screen_quad.msh and b/Examples/Game/GameData/Assets/Meshes/screen_quad.msh differ diff --git a/Examples/Game/GameData/Assets/Shaders/Enviroment/fragment.glsl b/Examples/Game/GameData/Assets/Shaders/Enviroment/fragment.glsl index 3e41710..f6126a8 100644 --- a/Examples/Game/GameData/Assets/Shaders/Enviroment/fragment.glsl +++ b/Examples/Game/GameData/Assets/Shaders/Enviroment/fragment.glsl @@ -4,8 +4,8 @@ out vec4 FragColor; in vec2 TexCoords; in vec3 FragPosition; -in vec3 v_cameraPos; in vec2 atlasSizes; +in vec2 Repeat; uniform int lightCount; uniform int u_HasNormalMap; @@ -33,7 +33,7 @@ vec2 hash22(vec2 p) { } void main() { - vec2 globalUV = TexCoords + v_cameraPos.xy / (atlasSizes * 2.0f); + vec2 globalUV = (TexCoords * atlasSizes * Repeat) / (atlasSizes * 2.0f); vec2 cellID = floor(globalUV); vec2 localUV = fract(globalUV); @@ -84,3 +84,4 @@ void main() { + diff --git a/Examples/Game/GameData/Assets/Shaders/Enviroment/vertex.glsl b/Examples/Game/GameData/Assets/Shaders/Enviroment/vertex.glsl index ba35c77..6ae7182 100644 --- a/Examples/Game/GameData/Assets/Shaders/Enviroment/vertex.glsl +++ b/Examples/Game/GameData/Assets/Shaders/Enviroment/vertex.glsl @@ -4,37 +4,36 @@ layout(location = 0) in vec3 aPos; layout(location = 1) in vec2 uvs; out vec2 TexCoords; +out vec3 Normal; out vec3 FragPosition; -out vec3 v_cameraPos; out vec2 atlasSizes; +out vec2 Repeat; -uniform vec3 cameraPosition; uniform mat4 projection; uniform mat4 view; uniform vec2 window_size; uniform vec2 sizes; -uniform vec2 repeate; +uniform vec2 repeat; uniform vec2 atlas_size; - +uniform mat4 model; void main(){ - vec3 localCamPos = cameraPosition; - localCamPos.z = -1.0f; - - vec3 worldPos = aPos + localCamPos; - - vec4 pos = projection * view * vec4(worldPos, 1.0f); - - pos.xy *= repeate.xy; + vec3 scaledPos = vec3(aPos.x * sizes.x * repeat.x / 2, aPos.y * sizes.y * repeat.y / 2, aPos.z); + vec4 worldPos = model * vec4(scaledPos, 1.0); + vec4 pos = projection * view * worldPos; + + FragPosition = worldPos.xyz; + TexCoords = uvs; atlasSizes = atlas_size; - v_cameraPos = cameraPosition; - - FragPosition = worldPos; - TexCoords = (uvs / atlas_size) * repeate; + Repeat = repeat; gl_Position = pos; } + + + + diff --git a/Examples/Game/GameData/Assets/Textures/Stairs/Albedo.png b/Examples/Game/GameData/Assets/Textures/Stairs/Albedo.png index b00762f..b6b70f2 100644 Binary files a/Examples/Game/GameData/Assets/Textures/Stairs/Albedo.png and b/Examples/Game/GameData/Assets/Textures/Stairs/Albedo.png differ diff --git a/Examples/Game/GameData/Assets/Textures/Stairs/Normals.png b/Examples/Game/GameData/Assets/Textures/Stairs/Normals.png new file mode 100644 index 0000000..29459a7 Binary files /dev/null and b/Examples/Game/GameData/Assets/Textures/Stairs/Normals.png differ diff --git a/Examples/Game/GameData/Assets/Textures/Terrain/Albedo.png b/Examples/Game/GameData/Assets/Textures/Terrain/Albedo.png index 7e45902..3f5c930 100644 Binary files a/Examples/Game/GameData/Assets/Textures/Terrain/Albedo.png and b/Examples/Game/GameData/Assets/Textures/Terrain/Albedo.png differ diff --git a/Examples/Game/GameData/Assets/Textures/Terrain/Normals.png b/Examples/Game/GameData/Assets/Textures/Terrain/Normals.png index 1131205..7ef8d50 100644 Binary files a/Examples/Game/GameData/Assets/Textures/Terrain/Normals.png and b/Examples/Game/GameData/Assets/Textures/Terrain/Normals.png differ diff --git a/Examples/Game/GameData/Lights.lit b/Examples/Game/GameData/Lights.lit index 7352c99..d34036c 100644 Binary files a/Examples/Game/GameData/Lights.lit and b/Examples/Game/GameData/Lights.lit differ diff --git a/Examples/Game/GameData/Objects.obj b/Examples/Game/GameData/Objects.obj index 58cd880..bb6e1a1 100644 Binary files a/Examples/Game/GameData/Objects.obj and b/Examples/Game/GameData/Objects.obj differ diff --git a/Examples/Game/GameData/Resources.res b/Examples/Game/GameData/Resources.res index 507d0cb..0a80cb5 100644 Binary files a/Examples/Game/GameData/Resources.res and b/Examples/Game/GameData/Resources.res differ diff --git a/Examples/Game/Scripts/Movement.cpp b/Examples/Game/Scripts/Movement.cpp index 7f69b1d..a43c96b 100644 --- a/Examples/Game/Scripts/Movement.cpp +++ b/Examples/Game/Scripts/Movement.cpp @@ -20,13 +20,18 @@ namespace Engine{ class SCRIPT_NAME : public Engine::ScriptShared::GameObjectScriptInterface { private: + + std::vector colliders = {"Stairs", "Stairs2", "Stairs3"}; + const float STAIRS_HEIGHT_COFF = 4.0f; + const float STAIRS_ENTER_EPS = 4.0f; float zoom = DEFAULT_ZOOM; + float height = 0.0f; public: ~SCRIPT_NAME() = default; void OnLoad(){ - logger->info(SCRIPT_FILE_NAME, "Loaded"); + engine.logger->info(SCRIPT_FILE_NAME, "Loaded"); }; void OnUpdate(float delta_time){ @@ -41,34 +46,122 @@ class SCRIPT_NAME : public Engine::ScriptShared::GameObjectScriptInterface { }; void OnDestroy(){ - logger->info(SCRIPT_FILE_NAME, "Destroyed"); + engine.logger->info(SCRIPT_FILE_NAME, "Destroyed"); }; + // ================= // + // ==== Physics ==== // + // ================= // + std::array getPlayerCollider(){ + glm::vec3 player_size = object_data->scale / 2.0f; + glm::vec3 player_pos = object_data->position - glm::vec3(0.0f, 1.0f, 0.0f); + + return {player_pos, player_size}; + }; + + std::array getObjectCollider(const std::string& name){ + Engine::ScriptShared::GameObjectData* collider = engine.object_manager->getGameObjectData(name); + if (collider == nullptr) { + engine.logger->erro(SCRIPT_FILE_NAME, "collider " + name + " is nullptr"); + return {glm::vec3(0.0f), glm::vec3(0.0f)}; + }; + + glm::vec3 collider_size = collider->scale; + glm::vec3 collider_pos = collider->position; + collider_pos.y += collider_size.y / 2.0f; + + return {collider_pos, collider_size}; + }; + + glm::vec3 collision(const std::string& name) { + if (object_data == nullptr) { + engine.logger->erro(SCRIPT_FILE_NAME, "object_data is nullptr"); + return glm::vec3(0.0f); + }; + + std::array player_collider = getPlayerCollider(); + std::array object_collider = getObjectCollider(name); + + float overlap_x = (player_collider[1].x + object_collider[1].x) - std::abs(player_collider[0].x - object_collider[0].x); + float overlap_y = (player_collider[1].y + object_collider[1].y) - std::abs(player_collider[0].y - object_collider[0].y); + + if (overlap_x > 0.0f && overlap_y > 0.0f) { + if (overlap_x < overlap_y) { + float sign_x = (player_collider[0].x < object_collider[0].x) ? -1.0f : 1.0f; + return glm::vec3(overlap_x * sign_x, 0.0f, 0.0f); + } else { + float sign_y = (player_collider[0].y < object_collider[0].y) ? -1.0f : 1.0f; + return glm::vec3(0.0f, overlap_y * sign_y, 0.0f); + }; + }; + + return glm::vec3(0.0f); + }; + + float stairs_height(const std::string& name){ + Engine::ScriptShared::GameObjectData* collider = engine.object_manager->getGameObjectData(name); + if(collider == nullptr){ + engine.logger->erro("height_collision", "nullptr"); + return 0.0f; + }; + + float min_height = 0.0f; + float max_height = 0.0f; + auto its1 = collider->parameters.find("min_height"); + if (its1 != collider->parameters.end()) { + if (auto* new_amount = std::get_if(&its1->second)) min_height = *new_amount; + }; + auto its2 = collider->parameters.find("max_height"); + if (its2 != collider->parameters.end()) { + if (auto* new_amount = std::get_if(&its2->second)) max_height = *new_amount; + }; + + std::array player_collider = getPlayerCollider(); + std::array object_collider = getObjectCollider(name); + + float stair_start_y = object_collider[0].y - (object_collider[1].y * 0.5f); + float progress = (player_collider[0].y - stair_start_y) / object_collider[1].y; + progress = glm::clamp(progress, 0.0f, 1.0f); + + return (min_height + progress * (max_height - min_height)) * STAIRS_HEIGHT_COFF; + }; - float height(){ - glm::vec3 position = game_object_data->position; - Engine::ScriptShared::GameObjectData* collider = object_manager->getGameObjectData("Stairs"); + void physics(){ + float height_val = 0.0f; + glm::vec3 collision_dir = glm::vec3(0.0f); - if(collider == nullptr) return 0.0f; - if(std::abs(position.x - collider->position.x) < 1.0f - && std::abs(position.y * 2.0f - collider->position.y) < 1.0f) - return 5.0f; - return 0.0f; + for(const std::string& name : colliders) { + glm::vec3 new_collision = collision(name); + collision_dir += new_collision; + + if(new_collision != glm::vec3(0.0f)){ + float new_height_val = stairs_height(name); + if(new_height_val > height_val) height_val = new_height_val; + }; + }; + + if(std::abs(height - height_val) < STAIRS_ENTER_EPS) height = height_val; + else object_data->position += collision_dir; }; + + + // ================== // + // ==== Movement ==== // + // ================== // void movement(float delta_time){ glm::vec3 delta_movement = glm::vec3(0.0f); - bool shift_pressed = glob_res->input_data->is_key_down("LSHIFT"); + bool shift_pressed = engine.glob_res->input_data->is_key_down("LSHIFT"); float target_zoom = shift_pressed ? DEFAULT_SPRINT_ZOOM : DEFAULT_ZOOM; zoom += (target_zoom - zoom) * DEFAULT_ZOOM_ACCELERATION * delta_time; - if(glob_res->input_data->is_key_down("W")) delta_movement.y += 1; - if(glob_res->input_data->is_key_down("S")) delta_movement.y -= 1; - if(glob_res->input_data->is_key_down("D")) delta_movement.x += 1; - if(glob_res->input_data->is_key_down("A")) delta_movement.x -= 1; + if(engine.glob_res->input_data->is_key_down("W")) delta_movement.y += 1; + if(engine.glob_res->input_data->is_key_down("S")) delta_movement.y -= 1; + if(engine.glob_res->input_data->is_key_down("D")) delta_movement.x += 1; + if(engine.glob_res->input_data->is_key_down("A")) delta_movement.x -= 1; if(delta_movement.x == 0.0f && delta_movement.y == 0.0f) return; glm::vec3 move_vector = glm::normalize(delta_movement); @@ -76,20 +169,20 @@ class SCRIPT_NAME : public Engine::ScriptShared::GameObjectScriptInterface { if(shift_pressed) move_vector = move_vector * DEFAULT_SPRINT_SPEED * delta_time; else move_vector = move_vector * DEFAULT_SPEED * delta_time; - game_object_data->position += move_vector; + object_data->position += move_vector; - // logger->warn(SCRIPT_FILE_NAME, std::to_string(move_vector.x) + ", " + std::to_string(move_vector.y)); + physics(); }; void cameraTracking(){ - Engine::ScriptShared::ICamera& camera = camera_controller->getActiveCamera(); + Engine::ScriptShared::ICamera& camera = engine.camera_controller->getActiveCamera(); - glm::vec3 position = game_object_data->position; + glm::vec3 position = object_data->position; position.z = 10.0f; camera.setPosition(position); - camera.setOrthoSize(zoom + height()); + camera.setOrthoSize(zoom + height); camera.setDirection(glm::vec3(0.0f, 0.0f, -1.0f)); camera.setCameraMode(Engine::ScriptShared::CameraMode::ORTHOGONAL); }; diff --git a/Examples/Game/Scripts/Template.cpp b/Examples/Game/Scripts/Template.cpp index 692ddf9..19d2067 100644 --- a/Examples/Game/Scripts/Template.cpp +++ b/Examples/Game/Scripts/Template.cpp @@ -24,7 +24,7 @@ class SCRIPT_NAME : public Engine::ScriptShared::GameObjectScriptInterface { ~SCRIPT_NAME() = default; void OnLoad(){ - logger->info(SCRIPT_FILE_NAME, "Loaded"); + engine.logger->info(SCRIPT_FILE_NAME, "Loaded"); }; void OnUpdate(float delta_time){ @@ -37,7 +37,7 @@ class SCRIPT_NAME : public Engine::ScriptShared::GameObjectScriptInterface { }; void OnDestroy(){ - logger->info(SCRIPT_FILE_NAME, "Destroyed"); + engine.logger->info(SCRIPT_FILE_NAME, "Destroyed"); }; }; }; diff --git a/Examples/Game/Scripts/Window.cpp b/Examples/Game/Scripts/Window.cpp index 5c562f3..c216515 100644 --- a/Examples/Game/Scripts/Window.cpp +++ b/Examples/Game/Scripts/Window.cpp @@ -27,14 +27,14 @@ class SCRIPT_NAME : public Engine::ScriptShared::GameObjectScriptInterface { ~SCRIPT_NAME() = default; void OnLoad(){ - glob_res->WINDOW_TITLE = base_name; - logger->info(SCRIPT_FILE_NAME, "Loaded"); + engine.glob_res->WINDOW_TITLE = base_name; + engine.logger->info(SCRIPT_FILE_NAME, "Loaded"); }; void OnUpdate(float delta_time){ if(samples > 200){ float fps = samples / acc_time; - glob_res->WINDOW_TITLE = base_name + " | " + std::to_string(floor(fps)) + " fps"; + engine.glob_res->WINDOW_TITLE = base_name + " | " + std::to_string(floor(fps)) + " fps"; acc_time = 0.0f; samples = 0; } @@ -52,8 +52,8 @@ class SCRIPT_NAME : public Engine::ScriptShared::GameObjectScriptInterface { }; void OnDestroy(){ - glob_res->WINDOW_TITLE = base_name; - logger->info(SCRIPT_FILE_NAME, "Destroyed"); + engine.glob_res->WINDOW_TITLE = base_name; + engine.logger->info(SCRIPT_FILE_NAME, "Destroyed"); }; }; }; diff --git a/README.md b/README.md index 2c95dc3..190cbeb 100644 --- a/README.md +++ b/README.md @@ -338,11 +338,12 @@ Production is designed to create one executable with no additional files require - [x] Production BTN. - [x] Camera full quaternions. - [x] Minimized Script Shared. -- [ ] Move Rendering to Camera with FBO. -- [ ] Camera Culling check. -- [ ] Camera Tests. -- [ ] CameraController Refactor. -- [ ] CameraController Tests. +- [x] Move Rendering to Camera with FBO. +- [x] Camera Tests. +- [x] CameraController Refactor. +- [x] CameraController ActiveCameraCache Avoid Hashing. +- [x] CameraController Tests. +- [ ] Issue with rotation Camera. - [ ] One Unified Scene Class. - [ ] Scene Save. - [ ] SceneController. diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 3c5961a..0d8a852 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -8,19 +8,33 @@ cmake_minimum_required(VERSION 3.15) set(tests_src - Core/Camera/CameraTests.cpp + Unit/Core/Camera/Camera.cpp + Unit/Core/Camera/CameraController.cpp + Unit/Utils/utils.cpp + Unit/Utils/utilsProd.cpp ) add_executable(unit_tests ${tests_src}) +cmrc_add_resource_library(test_mesh_resources + PREFIX "test_assets" + test_data/box.obj + test_data/character.fbx + test_data/nested/house.obj + test_data/ignored.txt +) + target_link_libraries(unit_tests PRIVATE - Core + Core + Utils + test_mesh_resources gtest_main gmock_main ) target_include_directories(unit_tests PRIVATE ${CMAKE_SOURCE_DIR}/Engine/Core + ${CMAKE_SOURCE_DIR}/Engine/Utils ) include(GoogleTest) diff --git a/Tests/Core/Camera/CameraTests.cpp b/Tests/Unit/Core/Camera/Camera.cpp similarity index 89% rename from Tests/Core/Camera/CameraTests.cpp rename to Tests/Unit/Core/Camera/Camera.cpp index 9e14cc7..c807d98 100644 --- a/Tests/Core/Camera/CameraTests.cpp +++ b/Tests/Unit/Core/Camera/Camera.cpp @@ -16,6 +16,7 @@ #define protected public #include "Core/Camera/Camera.h" +#include "Core/Objects/GameObject.h" #undef private #undef protected @@ -30,36 +31,25 @@ namespace Mock{ class Renderer : public CW::Renderer::Renderer { public: Renderer() {} - - // MOCK_METHOD(const CW::Renderer::WindowData*, getWindowData, (), (override)); - // MOCK_METHOD(const CW::Renderer::InputData*, getInputData, (), (override)); - // MOCK_METHOD(void, setKeyboardBind, (const std::string& action, char key), (override)); - - // MOCK_METHOD(void, createWindow, (), (override)); - // MOCK_METHOD(void, windowLessRenderer, (), (override)); - // MOCK_METHOD(APIWindow*, getWindow, (), (override)); - // void createRenderer() override {}; - - // MOCK_METHOD(void, beginFrame, (), (override)); - // MOCK_METHOD(void, swapBuffer, (), (override)); - // MOCK_METHOD(void, windowEvents, (), (override)); - - // MOCK_METHOD(void, setWindowMode, (CW::Renderer::WindowMode mode), (override)); - // MOCK_METHOD(void, setWindowTitle, (const std::string& title), (override)); - // MOCK_METHOD(void, setIcon, (const std::string& path), (override)); - // MOCK_METHOD(void, setVsync, (bool vsync), (override)); - // MOCK_METHOD(void, minimize, (bool minimize), (override)); - // MOCK_METHOD(void, maximize, (bool maximize), (override)); - // MOCK_METHOD(void, setPosition, (int x, int y), (override)); - // MOCK_METHOD(void, setSize, (int width, int height), (override)); - // MOCK_METHOD(void, setCursorVisibility, (bool visible), (override)); - // MOCK_METHOD(void, setCursorOn, (bool on), (override)); - // MOCK_METHOD(void, close, (), (override)); }; }; +//// ==================== //// +//// ====== Helpers ===== //// +//// ==================== //// +bool cmp_fbo(CW::Renderer::Framebuffer& first, CW::Renderer::Framebuffer& second){ + if(first.fboID != second.fboID) return false; + if(first.colorTextureID != second.colorTextureID) return false; + if(first.depthTextureID != second.depthTextureID) return false; + if(first.width != second.width) return false; + if(first.height != second.height) return false; + return true; +}; + + + //// ==================== //// //// === Constructors === //// //// ==================== //// @@ -71,6 +61,9 @@ TEST(CameraDefaultConstructors, HandlesInitialization) { EXPECT_GE(camera.ortho_size, 0); EXPECT_GE(camera.sensitivity, 0); EXPECT_GE(camera.velocity, 0); + EXPECT_EQ(camera.getFbo().fboID, 0); + EXPECT_EQ(camera.getFbo().colorTextureID, 0); + EXPECT_EQ(camera.getFbo().depthTextureID, 0); }; TEST(CameraDefaultConstructorsWithNullptr, HandlesInitialization) { @@ -83,6 +76,9 @@ TEST(CameraDefaultConstructorsWithNullptr, HandlesInitialization) { EXPECT_GE(camera.ortho_size, 0); EXPECT_GE(camera.sensitivity, 0); EXPECT_GE(camera.velocity, 0); + EXPECT_EQ(camera.getFbo().fboID, 0); + EXPECT_EQ(camera.getFbo().colorTextureID, 0); + EXPECT_EQ(camera.getFbo().depthTextureID, 0); }; TEST(CameraDefaultParamConstructors, HandlesInitialization) { @@ -139,6 +135,10 @@ TEST(CameraCopyConstructor, HandlesInitialization){ EXPECT_EQ(init_camera.orthogonal_mat_ready, construct_copy_camera.orthogonal_mat_ready); EXPECT_EQ(init_camera.perspective_mat, construct_copy_camera.perspective_mat); EXPECT_EQ(init_camera.orthogonal_mat, construct_copy_camera.orthogonal_mat); + EXPECT_TRUE(cmp_fbo(init_camera.fbo, construct_copy_camera.fbo)); + EXPECT_EQ(init_camera.fbo_size, construct_copy_camera.fbo_size); + EXPECT_EQ(init_camera.track_window_size, construct_copy_camera.track_window_size); + EXPECT_EQ(init_camera.fbo_size_ready, construct_copy_camera.fbo_size_ready); EXPECT_EQ(init_camera.default_movemement_on, construct_copy_camera.default_movemement_on); EXPECT_EQ(init_camera.sensitivity, construct_copy_camera.sensitivity); EXPECT_EQ(init_camera.velocity, construct_copy_camera.velocity); @@ -167,6 +167,10 @@ TEST(CameraCopyConstructor, HandlesInitialization){ EXPECT_EQ(init_camera.orthogonal_mat_ready, construct_copy_assign_camera.orthogonal_mat_ready); EXPECT_EQ(init_camera.perspective_mat, construct_copy_assign_camera.perspective_mat); EXPECT_EQ(init_camera.orthogonal_mat, construct_copy_assign_camera.orthogonal_mat); + EXPECT_TRUE(cmp_fbo(init_camera.fbo, construct_copy_assign_camera.fbo)); + EXPECT_EQ(init_camera.fbo_size, construct_copy_assign_camera.fbo_size); + EXPECT_EQ(init_camera.track_window_size, construct_copy_assign_camera.track_window_size); + EXPECT_EQ(init_camera.fbo_size_ready, construct_copy_assign_camera.fbo_size_ready); EXPECT_EQ(init_camera.default_movemement_on, construct_copy_assign_camera.default_movemement_on); EXPECT_EQ(init_camera.sensitivity, construct_copy_assign_camera.sensitivity); EXPECT_EQ(init_camera.velocity, construct_copy_assign_camera.velocity); @@ -199,6 +203,10 @@ TEST(CameraCopyConstructor, HandlesInitialization){ EXPECT_EQ(init_camera.orthogonal_mat_ready, construct_copy_self_camera.orthogonal_mat_ready); EXPECT_EQ(init_camera.perspective_mat, construct_copy_self_camera.perspective_mat); EXPECT_EQ(init_camera.orthogonal_mat, construct_copy_self_camera.orthogonal_mat); + EXPECT_TRUE(cmp_fbo(init_camera.fbo, construct_copy_self_camera.fbo)); + EXPECT_EQ(init_camera.fbo_size, construct_copy_self_camera.fbo_size); + EXPECT_EQ(init_camera.track_window_size, construct_copy_self_camera.track_window_size); + EXPECT_EQ(init_camera.fbo_size_ready, construct_copy_self_camera.fbo_size_ready); EXPECT_EQ(init_camera.default_movemement_on, construct_copy_self_camera.default_movemement_on); EXPECT_EQ(init_camera.sensitivity, construct_copy_self_camera.sensitivity); EXPECT_EQ(init_camera.velocity, construct_copy_self_camera.velocity); @@ -216,7 +224,7 @@ TEST(CameraMoveConstructor, HandlesInitialization){ Engine::Core::Camera init_constructor_move_camera(init_org_camera); Engine::Core::Camera construct_move_camera(std::move(init_constructor_move_camera)); - EXPECT_NE(init_constructor_move_camera.renderer, nullptr); + EXPECT_EQ(init_constructor_move_camera.renderer, nullptr); EXPECT_NE(init_org_camera.renderer, nullptr); EXPECT_EQ(init_org_camera.position, construct_move_camera.position); EXPECT_EQ(init_org_camera.direction, construct_move_camera.direction); @@ -237,6 +245,10 @@ TEST(CameraMoveConstructor, HandlesInitialization){ EXPECT_EQ(init_org_camera.orthogonal_mat_ready, construct_move_camera.orthogonal_mat_ready); EXPECT_EQ(init_org_camera.perspective_mat, construct_move_camera.perspective_mat); EXPECT_EQ(init_org_camera.orthogonal_mat, construct_move_camera.orthogonal_mat); + EXPECT_TRUE(cmp_fbo(init_org_camera.fbo, construct_move_camera.fbo)); + EXPECT_EQ(init_org_camera.fbo_size, construct_move_camera.fbo_size); + EXPECT_EQ(init_org_camera.track_window_size, construct_move_camera.track_window_size); + EXPECT_EQ(init_org_camera.fbo_size_ready, construct_move_camera.fbo_size_ready); EXPECT_EQ(init_org_camera.default_movemement_on, construct_move_camera.default_movemement_on); EXPECT_EQ(init_org_camera.sensitivity, construct_move_camera.sensitivity); EXPECT_EQ(init_org_camera.velocity, construct_move_camera.velocity); @@ -246,7 +258,7 @@ TEST(CameraMoveConstructor, HandlesInitialization){ Engine::Core::Camera init_construct_move_assign_camera(init_org_camera); Engine::Core::Camera construct_move_assign_camera = std::move(init_construct_move_assign_camera); - EXPECT_NE(init_construct_move_assign_camera.renderer, nullptr); + EXPECT_EQ(init_construct_move_assign_camera.renderer, nullptr); EXPECT_NE(construct_move_assign_camera.renderer, nullptr); EXPECT_EQ(init_org_camera.position, construct_move_assign_camera.position); EXPECT_EQ(init_org_camera.direction, construct_move_assign_camera.direction); @@ -267,6 +279,10 @@ TEST(CameraMoveConstructor, HandlesInitialization){ EXPECT_EQ(init_org_camera.orthogonal_mat_ready, construct_move_assign_camera.orthogonal_mat_ready); EXPECT_EQ(init_org_camera.perspective_mat, construct_move_assign_camera.perspective_mat); EXPECT_EQ(init_org_camera.orthogonal_mat, construct_move_assign_camera.orthogonal_mat); + EXPECT_TRUE(cmp_fbo(init_org_camera.fbo, construct_move_assign_camera.fbo)); + EXPECT_EQ(init_org_camera.fbo_size, construct_move_assign_camera.fbo_size); + EXPECT_EQ(init_org_camera.track_window_size, construct_move_assign_camera.track_window_size); + EXPECT_EQ(init_org_camera.fbo_size_ready, construct_move_assign_camera.fbo_size_ready); EXPECT_EQ(init_org_camera.default_movemement_on, construct_move_assign_camera.default_movemement_on); EXPECT_EQ(init_org_camera.sensitivity, construct_move_assign_camera.sensitivity); EXPECT_EQ(init_org_camera.velocity, construct_move_assign_camera.velocity); @@ -276,11 +292,11 @@ TEST(CameraMoveConstructor, HandlesInitialization){ Engine::Core::Camera init_construct_move_self_camera(init_org_camera); Engine::Core::Camera construct_move_self_camera(std::move(init_construct_move_self_camera)); Engine::Core::Camera* org_camera_ptr = &construct_move_self_camera; - construct_move_self_camera = std::move(init_construct_move_self_camera); + construct_move_self_camera = std::move(construct_move_self_camera); Engine::Core::Camera* new_camera_ptr = &construct_move_self_camera; EXPECT_EQ(org_camera_ptr, new_camera_ptr); - EXPECT_NE(init_construct_move_self_camera.renderer, nullptr); + EXPECT_EQ(init_construct_move_self_camera.renderer, nullptr); EXPECT_NE(construct_move_self_camera.renderer, nullptr); EXPECT_EQ(init_org_camera.position, construct_move_self_camera.position); EXPECT_EQ(init_org_camera.direction, construct_move_self_camera.direction); @@ -301,6 +317,10 @@ TEST(CameraMoveConstructor, HandlesInitialization){ EXPECT_EQ(init_org_camera.orthogonal_mat_ready, construct_move_self_camera.orthogonal_mat_ready); EXPECT_EQ(init_org_camera.perspective_mat, construct_move_self_camera.perspective_mat); EXPECT_EQ(init_org_camera.orthogonal_mat, construct_move_self_camera.orthogonal_mat); + EXPECT_TRUE(cmp_fbo(init_org_camera.fbo, construct_move_self_camera.fbo)); + EXPECT_EQ(init_org_camera.fbo_size, construct_move_self_camera.fbo_size); + EXPECT_EQ(init_org_camera.track_window_size, construct_move_self_camera.track_window_size); + EXPECT_EQ(init_org_camera.fbo_size_ready, construct_move_self_camera.fbo_size_ready); EXPECT_EQ(init_org_camera.default_movemement_on, construct_move_self_camera.default_movemement_on); EXPECT_EQ(init_org_camera.sensitivity, construct_move_self_camera.sensitivity); EXPECT_EQ(init_org_camera.velocity, construct_move_self_camera.velocity); @@ -1059,9 +1079,85 @@ TEST(CameraMovementSettersGetters, HandlesMovementAndInputFlags) { EXPECT_FLOAT_EQ(camera.getSensitivity(), 0.0f); }; +//// ================ //// +//// ==== Render ==== //// +//// ================ //// +TEST(CameraRender, HandlesModeSpecificPlanes) { + Mock::Renderer renderer; + Engine::Core::Camera camera(&renderer, glm::vec3(0.0f), glm::vec3(0.0f, 0.0f, -1.0f)); - -//// ================== //// -//// ==== Movement ==== //// -//// ================== //// -// Skiped requires MOCKS \ No newline at end of file + bool track_window_size = false; + camera.setTrackWindowSize(track_window_size); + EXPECT_EQ(track_window_size, camera.getTrackWindowSize()); + + glm::ivec2 fbo_size = glm::ivec2(200, 400); + camera.setFboSize(fbo_size); + EXPECT_EQ(fbo_size, camera.getFboSize()); + + fbo_size = glm::ivec2(-200, -400); + camera.setFboSize(fbo_size); + EXPECT_EQ(glm::ivec2(0), camera.getFboSize()); + + fbo_size = glm::ivec2(200, -400); + camera.setFboSize(fbo_size); + EXPECT_EQ(glm::ivec2(200, 0), camera.getFboSize()); + + fbo_size = glm::ivec2(-200, 400); + camera.setFboSize(fbo_size); + EXPECT_EQ(glm::ivec2(0, 400), camera.getFboSize()); + + camera.setFboSize({300, 400}); + camera.clearFbo(); + CW::Renderer::Framebuffer& fbo = camera.getFbo(); + EXPECT_NE(fbo.fboID, 0); + EXPECT_NE(fbo.colorTextureID, 0); + EXPECT_NE(fbo.depthTextureID, 0); + EXPECT_EQ(fbo.width, 300); + EXPECT_EQ(fbo.height, 400); + + camera.setFboSize({600, 100}); + std::vector objects = {}; + camera.render(objects); + fbo = camera.getFbo(); + EXPECT_NE(fbo.fboID, 0); + EXPECT_NE(fbo.colorTextureID, 0); + EXPECT_NE(fbo.depthTextureID, 0); + EXPECT_EQ(fbo.width, 600); + EXPECT_EQ(fbo.height, 100); + + camera.setFboSize({10, 10}); + objects = {}; + camera.render(objects); + fbo = camera.getFbo(); + EXPECT_NE(fbo.fboID, 0); + EXPECT_NE(fbo.colorTextureID, 0); + EXPECT_NE(fbo.depthTextureID, 0); + EXPECT_EQ(fbo.width, 10); + EXPECT_EQ(fbo.height, 10); + EXPECT_EQ(camera.getFboSize(), glm::ivec2(10, 10)); + + camera.setFboSize({200, 400}); + camera.autoSizeFboToWindow(); + EXPECT_NE(camera.getFboSize(), glm::ivec2(200, 400)); + + camera.setTrackWindowSize(true); + camera.setFboSize({500, 100}); + camera.render(objects); + fbo = camera.getFbo(); + EXPECT_NE(fbo.fboID, 0); + EXPECT_NE(fbo.colorTextureID, 0); + EXPECT_NE(fbo.depthTextureID, 0); + EXPECT_NE(fbo.width, 500); + EXPECT_NE(fbo.height, 100); + EXPECT_NE(camera.getFboSize(), glm::ivec2(500, 100)); + + camera.setFboSize({64, 64}); + camera.clearFbo(); + fbo = camera.getFbo(); + EXPECT_NE(fbo.fboID, 0); + EXPECT_NE(fbo.colorTextureID, 0); + EXPECT_NE(fbo.depthTextureID, 0); + EXPECT_NE(fbo.width, 64); + EXPECT_NE(fbo.height, 64); + EXPECT_NE(camera.getFboSize(), glm::ivec2(64, 64)); +}; diff --git a/Tests/Unit/Core/Camera/CameraController.cpp b/Tests/Unit/Core/Camera/CameraController.cpp new file mode 100644 index 0000000..d33a2b6 --- /dev/null +++ b/Tests/Unit/Core/Camera/CameraController.cpp @@ -0,0 +1,333 @@ +// Engine +// Copyright 2026 Daynlight +// Licensed under the GNU General, Version 3.0. +// See LICENSE file for details. + + + +#include +#include + +#define GLM_ENABLE_EXPERIMENTAL +#include "../vendor/glm/glm/gtx/euler_angles.hpp" +#include "../vendor/glm/glm/gtx/quaternion.hpp" + +#define private public +#define protected public + +#include "Core/Camera/Camera.h" +#include "Core/Camera/CameraController.h" + +#undef private +#undef protected + + + +//// ======================= ///// +//// ======== Mocks ======== ///// +//// ======================= ///// +namespace Mock{ + +class Renderer : public CW::Renderer::Renderer { +public: + Renderer() {}; +}; +}; + + + +//// ==================== //// +//// === Constructors === //// +//// ==================== //// +TEST(CameraControllerDefaultConstructors, HandlesInitialization) { + Engine::Core::CameraController controller; + + EXPECT_EQ(controller.renderer, nullptr); + EXPECT_EQ(controller.active_camera_ref, nullptr); + EXPECT_TRUE(controller.cameras.empty()); + EXPECT_EQ(controller.active_camera, ""); + EXPECT_FALSE(controller.active_camera_setted); +}; + +TEST(CameraControllerParamConstructors, HandlesInitialization) { + Mock::Renderer renderer; + Engine::Core::CameraController controller(&renderer); + + EXPECT_EQ(controller.renderer, &renderer); + EXPECT_EQ(controller.active_camera_ref, nullptr); + EXPECT_TRUE(controller.cameras.empty()); + EXPECT_EQ(controller.active_camera, ""); + EXPECT_FALSE(controller.active_camera_setted); +}; + +TEST(CameraControllerCopyConstructor, HandlesInitialization){ + Mock::Renderer renderer; + Engine::Core::CameraController init_controller(&renderer); + init_controller.spawnCamera("MainCamera", {0.0f, 1.0f, 5.0f}, {0.0f, 0.0f, -1.0f}); + init_controller.setActiveCamera("MainCamera"); + + // Copy Constructor + Engine::Core::CameraController construct_copy_controller(init_controller); + + EXPECT_EQ(construct_copy_controller.renderer, init_controller.renderer); + EXPECT_EQ(construct_copy_controller.cameras.size(), init_controller.cameras.size()); + EXPECT_TRUE(construct_copy_controller.cameraExists("MainCamera")); + EXPECT_EQ(construct_copy_controller.active_camera, init_controller.active_camera); + EXPECT_EQ(construct_copy_controller.active_camera_setted, init_controller.active_camera_setted); + EXPECT_NE(construct_copy_controller.active_camera_ref, init_controller.active_camera_ref); + + // Copy Assignment + Engine::Core::CameraController construct_copy_assign_controller; + construct_copy_assign_controller = init_controller; + + EXPECT_EQ(construct_copy_assign_controller.renderer, init_controller.renderer); + EXPECT_EQ(construct_copy_assign_controller.cameras.size(), init_controller.cameras.size()); + EXPECT_TRUE(construct_copy_assign_controller.cameraExists("MainCamera")); + EXPECT_EQ(construct_copy_assign_controller.active_camera, init_controller.active_camera); + EXPECT_EQ(construct_copy_assign_controller.active_camera_setted, init_controller.active_camera_setted); + EXPECT_NE(construct_copy_assign_controller.active_camera_ref, init_controller.active_camera_ref); + + // Self Assignment + Engine::Core::CameraController construct_copy_self_controller(init_controller); + Engine::Core::CameraController* org_controller_ptr = &construct_copy_self_controller; + construct_copy_self_controller = construct_copy_self_controller; + Engine::Core::CameraController* new_controller_ptr = &construct_copy_self_controller; + + EXPECT_EQ(org_controller_ptr, new_controller_ptr); + EXPECT_EQ(construct_copy_self_controller.renderer, init_controller.renderer); + EXPECT_EQ(construct_copy_self_controller.cameras.size(), init_controller.cameras.size()); + EXPECT_TRUE(construct_copy_self_controller.cameraExists("MainCamera")); + EXPECT_EQ(construct_copy_self_controller.active_camera, init_controller.active_camera); + EXPECT_EQ(construct_copy_self_controller.active_camera_setted, init_controller.active_camera_setted); + EXPECT_NE(construct_copy_self_controller.active_camera_ref, init_controller.active_camera_ref); +}; + +TEST(CameraControllerMoveConstructor, HandlesInitialization){ + Mock::Renderer renderer; + Engine::Core::CameraController init_org_controller(&renderer); + init_org_controller.spawnCamera("MainCamera", {0.0f, 1.0f, 5.0f}, {0.0f, 0.0f, -1.0f}); + init_org_controller.setActiveCamera("MainCamera"); + + // Move Constructor + Engine::Core::CameraController init_constructor_move_controller(init_org_controller); + Engine::Core::CameraController construct_move_controller(std::move(init_constructor_move_controller)); + + // Verify new object state + EXPECT_EQ(construct_move_controller.renderer, &renderer); + EXPECT_EQ(construct_move_controller.cameras.size(), 1); + EXPECT_TRUE(construct_move_controller.cameraExists("MainCamera")); + EXPECT_EQ(construct_move_controller.active_camera, "MainCamera"); + EXPECT_TRUE(construct_move_controller.active_camera_setted); + EXPECT_EQ(construct_move_controller.active_camera_ref, &construct_move_controller.getCoreActiveCamera()); + + // Verify moved-from object state reset + EXPECT_EQ(init_constructor_move_controller.renderer, nullptr); + EXPECT_EQ(init_constructor_move_controller.active_camera_ref, nullptr); + EXPECT_FALSE(init_constructor_move_controller.active_camera_setted); + EXPECT_TRUE(init_constructor_move_controller.cameras.empty()); + + // Move Assignment + Engine::Core::CameraController init_construct_move_assign_controller(init_org_controller); + Engine::Core::CameraController construct_move_assign_controller; + construct_move_assign_controller = std::move(init_construct_move_assign_controller); + + // Verify new object state + EXPECT_EQ(construct_move_assign_controller.renderer, &renderer); + EXPECT_EQ(construct_move_assign_controller.cameras.size(), 1); + EXPECT_TRUE(construct_move_assign_controller.cameraExists("MainCamera")); + EXPECT_EQ(construct_move_assign_controller.active_camera, "MainCamera"); + EXPECT_TRUE(construct_move_assign_controller.active_camera_setted); + EXPECT_EQ(construct_move_assign_controller.active_camera_ref, &construct_move_assign_controller.getCoreActiveCamera()); + + // Verify moved-from object state reset + EXPECT_EQ(init_construct_move_assign_controller.renderer, nullptr); + EXPECT_EQ(init_construct_move_assign_controller.active_camera_ref, nullptr); + EXPECT_FALSE(init_construct_move_assign_controller.active_camera_setted); + EXPECT_TRUE(init_construct_move_assign_controller.cameras.empty()); + + // Self Move Assignment + Engine::Core::CameraController init_construct_move_self_controller(init_org_controller); + Engine::Core::CameraController construct_move_self_controller(std::move(init_construct_move_self_controller)); + Engine::Core::CameraController* org_controller_ptr = &construct_move_self_controller; + + construct_move_self_controller = std::move(construct_move_self_controller); + Engine::Core::CameraController* new_controller_ptr = &construct_move_self_controller; + + EXPECT_EQ(org_controller_ptr, new_controller_ptr); + EXPECT_EQ(construct_move_self_controller.renderer, &renderer); + EXPECT_EQ(construct_move_self_controller.cameras.size(), 1); + EXPECT_TRUE(construct_move_self_controller.cameraExists("MainCamera")); + EXPECT_EQ(construct_move_self_controller.active_camera, "MainCamera"); + EXPECT_TRUE(construct_move_self_controller.active_camera_setted); + EXPECT_EQ(construct_move_self_controller.active_camera_ref, &construct_move_self_controller.getCoreActiveCamera()); +}; + + + +//// ================= //// +//// ==== Control ==== //// +//// ================= //// +TEST(CameraControllerSpawnWithParams, SpawnCameraAndCheckExists) { + Mock::Renderer renderer; + Engine::Core::CameraController controller(&renderer); + + glm::vec3 pos = {1.0f, 2.0f, 3.0f}; + glm::vec3 dir = {0.0f, -1.0f, 0.0f}; + + EXPECT_FALSE(controller.cameraExists("PlayerCamera")); + controller.spawnCamera("PlayerCamera", pos, dir); + EXPECT_TRUE(controller.cameraExists("PlayerCamera")); + + auto& cam = controller.getCoreCamera("PlayerCamera"); + EXPECT_NEAR(cam.getPosition().x, pos.x, 0.0001f); + EXPECT_NEAR(cam.getPosition().y, pos.y, 0.0001f); + EXPECT_NEAR(cam.getPosition().z, pos.z, 0.0001f); + EXPECT_NEAR(cam.getDirection().x, dir.x, 0.0001f); + EXPECT_NEAR(cam.getDirection().y, dir.y, 0.0001f); + EXPECT_NEAR(cam.getDirection().z, dir.z, 0.0001f); +}; + +TEST(CameraControllerSpawnWithPosition, SpawnCameraIgnoresDuplicates) { + Mock::Renderer renderer; + Engine::Core::CameraController controller(&renderer); + + controller.spawnCamera("Cam1", {1.0f, 1.0f, 1.0f}); + EXPECT_EQ(controller.cameras.size(), 1); + + controller.spawnCamera("Cam1", {9.0f, 9.0f, 9.0f}); + EXPECT_EQ(controller.cameras.size(), 1); + + auto& cam = controller.getCoreCamera("Cam1"); + EXPECT_NEAR(cam.getPosition().x, 1.0f, 0.0001f); +}; + +TEST(CameraControllerSpawnCamera, DeleteCamera) { + Mock::Renderer renderer; + Engine::Core::CameraController controller(&renderer); + + controller.spawnCamera("Cam1"); + controller.spawnCamera("Cam2"); + EXPECT_TRUE(controller.cameraExists("Cam1")); + EXPECT_TRUE(controller.cameraExists("Cam2")); + + controller.deleteCamera("Cam1"); + EXPECT_FALSE(controller.cameraExists("Cam1")); + EXPECT_TRUE(controller.cameraExists("Cam2")); + + EXPECT_NO_THROW(controller.deleteCamera("NonExistentCam")); +}; + +TEST(CameraControllerActiveCameraState, ActiveCameraManagement) { + Mock::Renderer renderer; + Engine::Core::CameraController controller(&renderer); + + controller.spawnCamera("Cam1"); + controller.spawnCamera("Cam2"); + + controller.setActiveCamera("Cam1"); + EXPECT_EQ(controller.getActiveCameraName(), "Cam1"); + EXPECT_TRUE(controller.active_camera_setted); + EXPECT_EQ(controller.active_camera_ref, &controller.getCoreCamera("Cam1")); + + EXPECT_NO_THROW({ + Engine::ScriptShared::ICamera& icam = controller.getActiveCamera(); + Engine::Core::Camera& ccam = controller.getCoreActiveCamera(); + }); + + controller.setActiveCamera("Cam2"); + EXPECT_EQ(controller.getActiveCameraName(), "Cam2"); + EXPECT_EQ(controller.active_camera_ref, &controller.getCoreCamera("Cam2")); + + controller.setActiveCamera("NonExistentCam"); + EXPECT_EQ(controller.getActiveCameraName(), "Cam2"); +}; + +TEST(CameraControllerActiveCamera, DeleteActiveCameraResetsState) { + Mock::Renderer renderer; + Engine::Core::CameraController controller(&renderer); + + controller.spawnCamera("Cam1"); + controller.setActiveCamera("Cam1"); + + EXPECT_EQ(controller.getActiveCameraName(), "Cam1"); + EXPECT_TRUE(controller.active_camera_setted); + + controller.deleteCamera("Cam1"); + + EXPECT_EQ(controller.getActiveCameraName(), ""); + EXPECT_FALSE(controller.active_camera_setted); + + EXPECT_THROW(controller.getActiveCamera(), std::runtime_error); + EXPECT_THROW(controller.getCoreActiveCamera(), std::runtime_error); +}; + +TEST(CameraControllerRuntimeErrors, GetActiveCameraThrowsIfNoneSetted) { + Mock::Renderer renderer; + Engine::Core::CameraController controller(&renderer); + + EXPECT_THROW(controller.getActiveCamera(), std::runtime_error); + EXPECT_THROW(controller.getCoreActiveCamera(), std::runtime_error); +}; + +TEST(CameraControllerMultipleCameras, SpawnCameraUpdatesActiveCameraRef) { + Mock::Renderer renderer; + Engine::Core::CameraController controller(&renderer); + + controller.spawnCamera("Cam1"); + controller.setActiveCamera("Cam1"); + + Engine::Core::Camera* initial_ref = &controller.getCoreCamera("Cam1"); + + for (int i = 0; i < 50; ++i) { + controller.spawnCamera("ExtraCam" + std::to_string(i)); + }; + + EXPECT_EQ(controller.getActiveCameraName(), "Cam1"); + EXPECT_TRUE(controller.active_camera_setted); + EXPECT_EQ(controller.active_camera_ref, &controller.getCoreCamera("Cam1")); + EXPECT_NO_THROW(controller.getActiveCamera()); +}; + +TEST(CameraControllerEditingCamera, EditCamerasAndVerifyActive) { + Mock::Renderer renderer; + Engine::Core::CameraController controller(&renderer); + + controller.spawnCamera("FirstCamera"); + Engine::Core::Camera& cam1 = controller.getCoreCamera("FirstCamera"); + + cam1.position = {10.0f, 20.0f, 30.0f}; + cam1.direction = glm::normalize(glm::vec3(0.0f, 1.0f, 0.0f)); + cam1.fov = 90.0f; + cam1.velocity = 15.0f; + + controller.spawnCamera("SecondCamera"); + Engine::Core::Camera& cam2 = controller.getCoreCamera("SecondCamera"); + + cam2.position = {-5.0f, -15.0f, 25.0f}; + cam2.direction = glm::normalize(glm::vec3(1.0f, 0.0f, -1.0f)); + cam2.fov = 60.0f; + cam2.velocity = 5.0f; + + Engine::Core::Camera& verify_cam1 = controller.getCoreCamera("FirstCamera"); + EXPECT_NEAR(verify_cam1.position.x, 10.0f, 0.0001f); + EXPECT_NEAR(verify_cam1.position.y, 20.0f, 0.0001f); + EXPECT_NEAR(verify_cam1.position.z, 30.0f, 0.0001f); + EXPECT_NEAR(verify_cam1.fov, 90.0f, 0.0001f); + + controller.setActiveCamera("SecondCamera"); + EXPECT_EQ(controller.getActiveCameraName(), "SecondCamera"); + EXPECT_TRUE(controller.active_camera_setted); + + Engine::Core::Camera& active_cam = controller.getCoreActiveCamera(); + + EXPECT_NEAR(active_cam.position.x, -5.0f, 0.0001f); + EXPECT_NEAR(active_cam.position.y, -15.0f, 0.0001f); + EXPECT_NEAR(active_cam.position.z, 25.0f, 0.0001f); + + EXPECT_NEAR(active_cam.direction.x, cam2.direction.x, 0.0001f); + EXPECT_NEAR(active_cam.direction.y, cam2.direction.y, 0.0001f); + EXPECT_NEAR(active_cam.direction.z, cam2.direction.z, 0.0001f); + + EXPECT_NEAR(active_cam.fov, 60.0f, 0.0001f); + EXPECT_NEAR(active_cam.velocity, 5.0f, 0.0001f); +}; \ No newline at end of file diff --git a/Tests/Unit/Utils/utils.cpp b/Tests/Unit/Utils/utils.cpp new file mode 100644 index 0000000..bb3afc4 --- /dev/null +++ b/Tests/Unit/Utils/utils.cpp @@ -0,0 +1,201 @@ +// Engine +// Copyright 2026 Daynlight +// Licensed under the GNU General, Version 3.0. +// See LICENSE file for details. + + + +#include +#include "random" + + +#define private public +#define protected public + +#include "Utils/utils.h" + +#undef private +#undef protected + + + +TEST(UtilsHash, HandlesInitialization){ + const unsigned int seed = 2123123; + const size_t random_numbers_to_test = 25; + const float min = -200.0f; + const float max = 200.0f; + + std::mt19937 gen(seed); + std::uniform_real_distribution dist(min, max); + + std::vector init_values = {0.0f, static_cast(0x45d9f3b), 16.0f}; + init_values.reserve(random_numbers_to_test); + + for (size_t i = 0; i < random_numbers_to_test; ++i) { + init_values.emplace_back(dist(gen)); + }; + + for (size_t i = 0; i < init_values.size(); ++i) { + float value = init_values[i]; + + uint32_t hashed_value1 = Engine::Utils::hash(value); + EXPECT_NE(value, hashed_value1); + + uint32_t hashed_value2 = Engine::Utils::hash(value); + EXPECT_EQ(hashed_value1, hashed_value2); + }; +}; + +TEST(UtilsUploadTypedBuffer, HandlesInitialization){ + CW::Renderer::Renderer renderer; + + const size_t random_tests = 25; + const unsigned int seed = 2123123; + const float min = -200.0f; + const float max = 200.0f; + + std::mt19937 gen(seed); + std::uniform_real_distribution dist(min, max); + + std::vector data = { glm::vec3(1.0f) }; + + for(size_t i = 0; i < random_tests; i++){ + float rand_val = dist(gen); + data.emplace_back(glm::vec3(rand_val)); + }; + + const unsigned int dimensions = 3; + const unsigned int layout = 0; + const GLenum type = GL_FLOAT; + + const auto* bytePtr = reinterpret_cast(&data); + std::vector buffer(bytePtr, bytePtr + sizeof(glm::vec3) * data.size()); + + { + CW::Renderer::Mesh mesh; + Engine::Utils::uploadTypedBuffer(mesh, buffer, dimensions, layout, GL_FLOAT); + + auto& data_reg = mesh.dataRegister; + auto it = data_reg.find(layout); + EXPECT_NE(it, data_reg.end()); + if(it != data_reg.end()) { + CW::Renderer::MeshData test_data = it->second; + EXPECT_EQ(test_data.dimension, dimensions); + EXPECT_EQ(test_data.type, type); + EXPECT_EQ(test_data.size_of_element, sizeof(float)); + EXPECT_EQ(test_data.size, sizeof(float) * dimensions * data.size()); + }; + } + + { + CW::Renderer::Mesh mesh; + Engine::Utils::uploadTypedBuffer(mesh, buffer, 0, layout, GL_FLOAT); + + auto& data_reg = mesh.dataRegister; + auto it = data_reg.find(layout); + EXPECT_EQ(it, data_reg.end()); + } + + { + CW::Renderer::Mesh mesh; + std::vector buffer_empty = {}; + Engine::Utils::uploadTypedBuffer(mesh, buffer_empty, dimensions, layout, GL_FLOAT); + + auto& data_reg = mesh.dataRegister; + auto it = data_reg.find(layout); + EXPECT_EQ(it, data_reg.end()); + } + + { + CW::Renderer::Mesh mesh; + std::vector buffer_empty = buffer; + buffer_empty.emplace_back(1); + Engine::Utils::uploadTypedBuffer(mesh, buffer_empty, dimensions, layout, GL_FLOAT); + + auto& data_reg = mesh.dataRegister; + auto it = data_reg.find(layout); + EXPECT_EQ(it, data_reg.end()); + } +}; + +TEST(UtilsuploadBufferByType, HandlesInitialization){ + CW::Renderer::Renderer renderer; + + const size_t random_tests = 25; + const unsigned int seed = 2123123; + const float min = -200.0f; + const float max = 200.0f; + + std::mt19937 gen(seed); + std::uniform_real_distribution dist(min, max); + + std::vector data = { glm::vec3(1.0f) }; + + for(size_t i = 0; i < random_tests; i++){ + float rand_val = dist(gen); + data.emplace_back(glm::vec3(rand_val)); + }; + + { + const unsigned int dimensions = 3; + const unsigned int layout = 0; + std::vector types = {GL_FLOAT, GL_UNSIGNED_INT}; + + for(GLenum type : types){ + CW::Renderer::Mesh mesh; + + const auto* bytePtr = reinterpret_cast(&data); + std::vector buffer(bytePtr, bytePtr + sizeof(glm::vec3) * data.size()); + + Engine::Utils::uploadBufferByType(mesh, buffer, dimensions, layout, type); + + auto& data_reg = mesh.dataRegister; + auto it = data_reg.find(layout); + EXPECT_NE(it, data_reg.end()); + if(it != data_reg.end()){ + CW::Renderer::MeshData test_data = it->second; + EXPECT_EQ(test_data.dimension, dimensions); + EXPECT_EQ(test_data.type, type); + EXPECT_EQ(test_data.size_of_element, sizeof(float)); + EXPECT_EQ(test_data.size, sizeof(float) * dimensions * data.size()); + }; + }; + } + + { + const unsigned int dimensions = 0; + const unsigned int layout = 0; + std::vector types = {GL_FLOAT, GL_UNSIGNED_INT}; + + for(GLenum type : types){ + CW::Renderer::Mesh mesh; + + const auto* bytePtr = reinterpret_cast(&data); + std::vector buffer(bytePtr, bytePtr + sizeof(glm::vec3) * data.size()); + + Engine::Utils::uploadBufferByType(mesh, buffer, dimensions, layout, type); + + auto& data_reg = mesh.dataRegister; + auto it = data_reg.find(layout); + EXPECT_EQ(it, data_reg.end()); + }; + } + + { + const unsigned int dimensions = 3; + const unsigned int layout = 0; + std::vector types = {GL_FLOAT, GL_UNSIGNED_INT}; + + for(GLenum type : types){ + CW::Renderer::Mesh mesh; + + std::vector buffer_empty = {}; + + Engine::Utils::uploadBufferByType(mesh, buffer_empty, dimensions, layout, type); + + auto& data_reg = mesh.dataRegister; + auto it = data_reg.find(layout); + EXPECT_EQ(it, data_reg.end()); + }; + } +}; \ No newline at end of file diff --git a/Tests/Unit/Utils/utilsProd.cpp b/Tests/Unit/Utils/utilsProd.cpp new file mode 100644 index 0000000..dd33368 --- /dev/null +++ b/Tests/Unit/Utils/utilsProd.cpp @@ -0,0 +1,127 @@ +// Engine +// Copyright 2026 Daynlight +// Licensed under the GNU General, Version 3.0. +// See LICENSE file for details. + + + +#define PRODUCTION +#include +#include + + +#define private public +#define protected public + +#include "Utils/utils.h" + +#undef private +#undef protected + + + +CMRC_DECLARE(test_mesh_resources); + +TEST(UtilsScanCmrcDirectoryProperFolders, RecursivelyFindsMatchingMeshFiles) { + auto fs = cmrc::test_mesh_resources::get_filesystem(); + std::vector discovered_meshes; + + std::string pattern = R"(\.(obj|fbx)$)"; + Engine::Utils::scanCmrcDirectory(fs, "test_assets", pattern, discovered_meshes); + + EXPECT_EQ(discovered_meshes.size(), 3); + EXPECT_THAT(discovered_meshes, ::testing::UnorderedElementsAre( + std::filesystem::path("test_assets/test_data/box.obj"), + std::filesystem::path("test_assets/test_data/character.fbx"), + std::filesystem::path("test_assets/test_data/nested/house.obj") + )); +}; + +TEST(ScanCmrcDirectoryTestEmpty, ReturnsEmptyWhenNoMatchesFound) { + auto fs = cmrc::test_mesh_resources::get_filesystem(); + std::vector discovered_meshes; + + Engine::Utils::scanCmrcDirectory(fs, "test_assets", R"(\.gltf$)", discovered_meshes); + + EXPECT_TRUE(discovered_meshes.empty()); +}; + +TEST(ScanCmrcDirectoryTestNoPattern, ReturnsEmptyWhenNoMatchesFound) { + auto fs = cmrc::test_mesh_resources::get_filesystem(); + std::vector discovered_meshes; + + std::string pattern = ""; + Engine::Utils::scanCmrcDirectory(fs, "test_assets", pattern, discovered_meshes); + + EXPECT_TRUE(discovered_meshes.empty()); +}; + +TEST(ScanCmrcDirectoryTestFile, ReturnsEmptyWhenNoMatchesFound) { + auto fs = cmrc::test_mesh_resources::get_filesystem(); + std::vector discovered_meshes; + + std::string pattern = R"(\.(obj|fbx)$)"; + Engine::Utils::scanCmrcDirectory(fs, "test_assets/test_data/box.obj", pattern, discovered_meshes); + + EXPECT_EQ(discovered_meshes.size(), 1); + EXPECT_THAT(discovered_meshes, ::testing::UnorderedElementsAre( + std::filesystem::path("test_assets/test_data/box.obj") + )); +}; + +TEST(ScanCmrcDirectoryTestFileInvalidRegex, ReturnsEmptyWhenNoMatchesFound) { + auto fs = cmrc::test_mesh_resources::get_filesystem(); + std::vector discovered_meshes; + + std::string pattern = R"(\.(obj|fbx)$)"; + Engine::Utils::scanCmrcDirectory(fs, "test_assets/test_data/box.gls", pattern, discovered_meshes); + + EXPECT_EQ(discovered_meshes.size(), 0); +}; + +TEST(ScanCmrcDirectoryTestFileNotExists, ReturnsEmptyWhenNoMatchesFound) { + auto fs = cmrc::test_mesh_resources::get_filesystem(); + std::vector discovered_meshes; + + std::string pattern = R"(\.(obj|fbx)$)"; + Engine::Utils::scanCmrcDirectory(fs, "test_assets/test_data/boxe.obj", pattern, discovered_meshes); + + EXPECT_EQ(discovered_meshes.size(), 0); +}; + +TEST(ScanCmrcDirectoryTestNoPath, ReturnsEmptyWhenNoMatchesFound) { + auto fs = cmrc::test_mesh_resources::get_filesystem(); + std::vector discovered_meshes; + + std::string pattern = R"(\.(obj|fbx)$)"; + Engine::Utils::scanCmrcDirectory(fs, "", pattern, discovered_meshes); + + EXPECT_TRUE(discovered_meshes.empty()); +}; + +TEST(UtilsScanCmrcDirectoryNotEmpty, RecursivelyFindsMatchingMeshFiles) { + auto fs = cmrc::test_mesh_resources::get_filesystem(); + std::vector discovered_meshes; + discovered_meshes.emplace_back(std::filesystem::path("Err")); + + std::string pattern = R"(\.(obj|fbx)$)"; + Engine::Utils::scanCmrcDirectory(fs, "test_assets", pattern, discovered_meshes); + + EXPECT_EQ(discovered_meshes.size(), 3); + EXPECT_THAT(discovered_meshes, ::testing::UnorderedElementsAre( + std::filesystem::path("test_assets/test_data/box.obj"), + std::filesystem::path("test_assets/test_data/character.fbx"), + std::filesystem::path("test_assets/test_data/nested/house.obj") + )); +}; + +TEST(UtilsScanCmrcDirectoryNotEmptyEarlyExit, RecursivelyFindsMatchingMeshFiles) { + auto fs = cmrc::test_mesh_resources::get_filesystem(); + std::vector discovered_meshes; + discovered_meshes.emplace_back(std::filesystem::path("Err")); + + std::string pattern = ""; + Engine::Utils::scanCmrcDirectory(fs, "test_assets", pattern, discovered_meshes); + + EXPECT_EQ(discovered_meshes.size(), 0); +}; \ No newline at end of file diff --git a/Tests/test_data/box.obj b/Tests/test_data/box.obj new file mode 100644 index 0000000..e69de29 diff --git a/Tests/test_data/character.fbx b/Tests/test_data/character.fbx new file mode 100644 index 0000000..e69de29 diff --git a/Tests/test_data/ignored.txt b/Tests/test_data/ignored.txt new file mode 100644 index 0000000..e69de29 diff --git a/Tests/test_data/nested/house.obj b/Tests/test_data/nested/house.obj new file mode 100644 index 0000000..e69de29 diff --git a/docs/Editor.gif b/docs/Editor.gif index 311db2e..f5b2269 100644 Binary files a/docs/Editor.gif and b/docs/Editor.gif differ diff --git a/docs/Editor.mp4 b/docs/Editor.mp4 index 94c0e4f..14db77b 100644 Binary files a/docs/Editor.mp4 and b/docs/Editor.mp4 differ diff --git a/docs/Prod.gif b/docs/Prod.gif index ea1635e..dae08d6 100644 Binary files a/docs/Prod.gif and b/docs/Prod.gif differ diff --git a/docs/Prod.mp4 b/docs/Prod.mp4 index f9f5cad..942c7a2 100644 Binary files a/docs/Prod.mp4 and b/docs/Prod.mp4 differ diff --git a/vendor/CWindow b/vendor/CWindow index 42123e3..eb01867 160000 --- a/vendor/CWindow +++ b/vendor/CWindow @@ -1 +1 @@ -Subproject commit 42123e3e2968feee9de4a994b73137b498ce2902 +Subproject commit eb018671b9d9cdbc5e2e0578c6209978e81e2464 diff --git a/vendor/googletest b/vendor/googletest index d89aac5..91c99b6 160000 --- a/vendor/googletest +++ b/vendor/googletest @@ -1 +1 @@ -Subproject commit d89aac5f0dd4021198d903d39de16f896726de21 +Subproject commit 91c99b6ffecb6f37ca11c25a0db84007c263c1f2