diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9773e6a8..3f5f5f66 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -115,6 +115,16 @@ jobs: CMAKE_C_COMPILER: ${{ matrix.compiler == 'clang' && steps.set-up-clang.outputs.clang || matrix.compiler == 'gcc' && steps.set-up-gcc.outputs.gcc || '' }} CMAKE_CXX_COMPILER: ${{ matrix.compiler == 'clang' && steps.set-up-clang.outputs.clangxx || matrix.compiler == 'gcc' && steps.set-up-gcc.outputs.gxx || '' }} + - name: Install Mesa for Windows + shell: cmd + if: ${{ matrix.os == 'windows-latest' }} + run: | + curl.exe -L --output mesa.7z --url https://github.com/pal1000/mesa-dist-win/releases/download/20.3.2/mesa3d-20.3.2-release-msvc.7z + "C:\Program Files\7-Zip\7z.exe" x mesa.7z + mklink opengl32.dll "x64\opengl32.dll" + mklink libglapi.dll "x64\libglapi.dll" + working-directory: 'build' + - name: Run headless test uses: coactions/setup-xvfb@v1 with: diff --git a/CMakeLists.txt b/CMakeLists.txt index 9d061156..ff683420 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ set(CMAKE_CXX_STANDARD 20) set(NEXO_COVERAGE OFF CACHE BOOL "Enable coverage for binaries") set(NEXO_GIT_SUBMODULE OFF CACHE BOOL "Enable git submodules init and update") set(NEXO_BOOTSTRAP_VCPKG OFF CACHE BOOL "Enable vcpkg bootstrap") -set(NEXO_BUILD_TESTS ${BUILD_TESTING} CACHE BOOL "Enable tests") +set(NEXO_BUILD_TESTS ON CACHE BOOL "Enable tests") set(NEXO_BUILD_EXAMPLES OFF CACHE BOOL "Enable examples") set(NEXO_GRAPHICS_API "OpenGL" CACHE STRING "Graphics API to use") @@ -32,6 +32,10 @@ add_compile_options( "$<$:${NEXO_COMPILER_FLAGS_RELEASE}>" ) +# Prevent Visual Studio (or other build tools) from creating per config sub-directories (e.g. Debug, Release) +# Useful to look for resource files relative to the executable path +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY $<1:${CMAKE_BINARY_DIR}>) + if (NEXO_COVERAGE) message(STATUS "Coverage enabled, adding flags: ${NEXO_COVERAGE_FLAGS}") add_compile_options("$<$:${NEXO_COVERAGE_FLAGS}>") diff --git a/README.md b/README.md index 6e194410..eb457959 100644 --- a/README.md +++ b/README.md @@ -139,6 +139,10 @@ cd build ctest -C Debug ``` +## Troubleshooting + +If you encounter any issues, please refer to our [Troubleshooting Guide](docs/troubleshooting/troubleshooting.md). + ## The Team NEXO Engine is brought to life by a dedicated team of fourth-year students from EPITECH Strasbourg: diff --git a/config/default-layout.ini b/config/default-layout.ini index f1a5e280..8dc6d1dd 100644 --- a/config/default-layout.ini +++ b/config/default-layout.ini @@ -1,115 +1,95 @@ +[Window][DockSpaceViewport_11111111] +Pos=0,24 +Size=1920,1056 +Collapsed=0 + [Window][Debug##Default] -Pos=60,60 +Pos=118,38 Size=400,400 Collapsed=0 -[Window][3D View] -Pos=368,24 -Size=609,645 +[Window][Scene Tree] +Pos=1499,45 +Size=421,1035 Collapsed=0 -DockId=0x00000005,0 +DockId=0x00000002,0 [Window][Dear ImGui Demo] Pos=0,42 Size=365,1038 Collapsed=0 -DockId=0x00000003,0 - -[Window][Example: Console] -Pos=374,648 -Size=1080,352 -Collapsed=0 +DockId=0x00000005,0 -[Window][Example: Custom rendering] -Pos=197,282 -Size=531,414 +[Window][Console] +Pos=0,643 +Size=1496,437 Collapsed=0 +DockId=0x0000000A,0 -[Window][Dear ImGui Style Editor] -Pos=0,766 -Size=1460,234 +[Window][Default scene] +Pos=0,45 +Size=1496,595 Collapsed=0 +DockId=0x00000009,0 -[Window][Dear ImGui ID Stack Tool] -Pos=601,19 -Size=796,467 +[Window][WindowOverViewport_11111111] +Pos=0,45 +Size=1920,1035 Collapsed=0 -[Window][Example: Log] -Pos=347,534 -Size=500,400 +[Window][Example: Assets Browser] +Pos=60,60 +Size=800,480 Collapsed=0 [Window][Example: Property editor] -Pos=60,60 +Pos=156,340 Size=430,450 -Collapsed=1 +Collapsed=0 -[Window][Example: Auto-resizing window] -Pos=828,476 -Size=377,369 +[Window][Example: Property editor/##tree_73339443] +IsChild=1 +Size=300,410 + +[Window][DockSpace Demo] +Pos=0,24 +Size=3000,1852 Collapsed=0 -[Window][Example: Long text display] +[Window][Example: Console] Pos=60,60 Size=520,600 Collapsed=0 -[Window][Example: Constrained Resize] -Pos=295,346 -Size=421,401 +[Window][Example: Simple layout] +Pos=60,60 +Size=500,440 Collapsed=0 -[Window][Dear ImGui Demo/ResizableChild_D5443E47] +[Window][Example: Simple layout/left pane_AED60EF8] IsChild=1 -Size=484,192 +Size=150,376 -[Window][Dear ImGui Demo/Red_1D4E05CE] -IsChild=1 -Size=200,100 - -[Window][Dear ImGui Debug Log] +[Window][Example: Auto-resizing window] Pos=60,60 -Size=470,216 +Size=398,320 Collapsed=0 -[Window][Example: Documents] -Pos=155,83 -Size=616,1050 +[Window][Create New Scene] +Pos=732,456 +Size=540,200 Collapsed=0 -[Window][Scene Tree] -Pos=1499,45 -Size=421,1035 -Collapsed=0 -DockId=0x00000002,0 - -[Window][Properties] -Pos=979,405 -Size=421,495 +[Window][test] +Pos=802,42 +Size=838,721 Collapsed=0 DockId=0x00000008,0 -[Window][DockSpaceViewport_11111111] -Pos=0,24 -Size=1400,876 -Collapsed=0 - -[Window][DockSpace Demo] -Pos=0,24 -Size=1920,985 -Collapsed=0 - -[Window][Dear ImGui Metrics/Debugger] -Pos=598,209 -Size=780,651 -Collapsed=0 - -[Window][Console] -Pos=0,643 -Size=1496,437 +[Window][Dear ImGui ID Stack Tool] +Pos=605,120 +Size=839,766 Collapsed=0 -DockId=0x0000000A,0 [Window][Import] Pos=76,121 @@ -133,19 +113,23 @@ Collapsed=0 [Window][ImGui in Raylib Example] Pos=421,64 -Size=800,800 -Collapsed=0 - -[Window][WindowOverViewport_11111111] -Pos=0,45 Size=1920,1035 Collapsed=0 -[Window][Default scene] -Pos=0,45 -Size=1496,595 +[Window][Asset Manager] +Pos=0,643 +Size=1496,437 Collapsed=0 -DockId=0x00000009,0 +DockId=0x0000000A,1 + +[Table][0xDDC24BCA,2] +RefScale=18 +Column 0 Sort=0v + +[Table][0xAC9A836A,2] +RefScale=18 +Column 0 Width=4 +Column 1 Weight=2.0000 [Table][0xC9935533,3] Column 0 Weight=1.0000 @@ -284,7 +268,7 @@ DockSpace ID=0x11111111 Window=0xA87D555D Pos=0,45 Size=1920,1035 Split=X DockNode ID=0x00000004 Parent=0x11111111 SizeRef=1552,1038 Split=X DockNode ID=0x00000001 Parent=0x00000004 SizeRef=1496,1038 Split=Y DockNode ID=0x00000009 Parent=0x00000001 SizeRef=1128,598 CentralNode=1 Selected=0xDBE0A5BF - DockNode ID=0x0000000A Parent=0x00000001 SizeRef=1128,437 Selected=0x49278EEE + DockNode ID=0x0000000A Parent=0x00000001 SizeRef=1128,437 Selected=0x0E238246 DockNode ID=0x00000002 Parent=0x00000004 SizeRef=421,1038 Selected=0x3576480E DockSpace ID=0x8B93E3BD Pos=0,24 Size=1400,876 Split=X Selected=0x3576480E DockNode ID=0x00000007 Parent=0x8B93E3BD SizeRef=977,876 Split=Y diff --git a/docs/troubleshooting/troubleshooting.md b/docs/troubleshooting/troubleshooting.md new file mode 100644 index 00000000..8cc9b277 --- /dev/null +++ b/docs/troubleshooting/troubleshooting.md @@ -0,0 +1,86 @@ +\page troubleshooting Troubleshooting + +# Build Troubleshooting + +## Table of Contents + +- [Common Issues](#common-issues) + - [Couldn't create symlink](#couldnt-create-symlink) + - [CPackDeb: file utility is not available](#cpackdeb-file-utility-is-not-available) + - [CMake configure/build infinite loop](#cmake-configurebuild-infinite-loop) +- [Still having issues? Any other questions?](#still-having-issues-any-other-questions) + +## Common Issues + +### Couldn't create symlink + +If you encounter the following error message when packing with `cpack -G DEB`: + +```bash +NEXO Engine symlink couldn't be created. Required for the DEB package generator. See README's troubleshooting section. +``` + +To create an installer using the cpack DEB generator, our scripts try to create +a symlink to NEXO's binary. This symlink is +required for installing NEXO in the path of a DEB compatible linux distribution. + +#### Solution + +If you are running Windows: +- Either ignore this message, since you may not want to generate a DEB package. +- If you still want to generate a DEB package, you should enable Windows Developer Mode, + see [Microsoft Documentaion](https://learn.microsoft.com/en-us/windows/apps/get-started/enable-your-device-for-development). + And rerun the cmake configure, build and cpack commands. + +> [!WARNING] +> If you are running Windows, you may not want to generate a DEB package. It may not be supported. + +If you are running another OS, it is unexpected, so please submit an issue [here](https://github.com/NexoEngine/game-engine/issues) +with the details of your OS and the error message; we will help you from there. + +### CPackDeb: file utility is not available + +If you encounter the following error message when packing with `cpack -G DEB`: + +```bash +CPackDeb: file utility is not available. CPACK_DEBIAN_PACKAGE_SHLIBDEPS + and CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS options are not available. +``` + +The DEB generator couldn't find the `file` command. + +#### Solution + +You need to install the `file` command on your system. + +On Ubuntu/Debian, you can install it with the following command: + +```bash +sudo apt update && sudo apt install file +``` + +On Windows (using chocolatey), you can install it with the following command on an elevated PowerShell: + +```bash +choco install file +``` + +> [!WARNING] +> If you are running Windows, you may not want to generate a DEB package. It may not be supported. + +### CMake configure/build infinite loop + +If you encounter an infinite loop when running the `cmake` command, it may be due to various reasons. + +#### Solution + +- Check if date and time are set correctly on your system. +- Something is modifying cmake files after each run. Check if you have any scripts or tools that modify the CMake files. +- Try to delete the `build` (might be another name) directory and rerun the `cmake` command. + +## Still having issues? Any other questions? + +If you are still having issues, please submit an issue [here](https://github.com/NexoEngine/game-engine/issues) +with the details of your problem. Thank you! + + diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index ba7dd20e..5859ccef 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -8,18 +8,19 @@ set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED True) set(SRCS - common/Exception.cpp - common/math/Matrix.cpp - editor/main.cpp - editor/src/backends/ImGuiBackend.cpp - editor/src/backends/opengl/openglImGuiBackend.cpp - editor/src/Editor.cpp - editor/src/SceneManagerBridge.cpp - editor/src/DocumentWindows/ConsoleWindow.cpp - editor/src/DocumentWindows/MainScene.cpp - editor/src/DocumentWindows/SceneTreeWindow.cpp - editor/src/DocumentWindows/PopupManager.cpp - editor/src/DocumentWindows/SceneViewManager.cpp + common/Exception.cpp + common/math/Matrix.cpp + editor/main.cpp + editor/src/backends/ImGuiBackend.cpp + editor/src/backends/opengl/openglImGuiBackend.cpp + editor/src/Editor.cpp + editor/src/SceneManagerBridge.cpp + editor/src/DocumentWindows/ConsoleWindow.cpp + editor/src/DocumentWindows/MainScene.cpp + editor/src/DocumentWindows/SceneTreeWindow.cpp + editor/src/DocumentWindows/PopupManager.cpp + editor/src/DocumentWindows/SceneViewManager.cpp + editor/src/DocumentWindows/AssetManagerWindow.cpp ) # Windows App Icon @@ -79,6 +80,9 @@ target_link_libraries(nexoEditor PRIVATE tinyfiledialogs::tinyfiledialogs) find_package(Boost CONFIG REQUIRED COMPONENTS dll) target_link_libraries(nexoEditor PRIVATE Boost::dll) +# Boost UUID +find_package(boost_uuid CONFIG REQUIRED) + include_directories(include) if(NEXO_GRAPHICS_API STREQUAL "OpenGL") diff --git a/editor/main.cpp b/editor/main.cpp index d988f7f9..661e17c1 100644 --- a/editor/main.cpp +++ b/editor/main.cpp @@ -6,7 +6,7 @@ // zzz zzz zzz z zzzz zzzz zzzz zzzz // zzz zzz zzzzzzzzzzzzz zzzz zzz zzzzzzz zzzzz // -// Author: Mehdy MORVAN +// Author: Guillaume HEIN // Date: 10/11/2024 // Description: Main file for the nexo editor // @@ -16,6 +16,7 @@ #include "src/DocumentWindows/ConsoleWindow.hpp" #include "src/DocumentWindows/MainScene.hpp" #include "src/DocumentWindows/SceneTreeWindow.hpp" +#include "src/DocumentWindows/AssetManagerWindow.hpp" #include #include @@ -29,8 +30,9 @@ int main(int argc, char **argv) const auto &sceneViewManager = nexo::editor::SceneViewManager::getInstance(); sceneViewManager->addNewScene("Default scene", std::make_shared("Default scene", true)); editor.registerWindow("Scene Tree", std::make_shared()); - editor.registerWindow("Scene view manager", sceneViewManager); + editor.registerWindow("Scene View Manager", sceneViewManager); editor.registerWindow("Console", std::make_shared(editor)); + editor.registerWindow("Asset Manager", std::make_shared()); editor.init(); while (editor.isOpen()) diff --git a/editor/src/ADocumentWindow.hpp b/editor/src/ADocumentWindow.hpp index 8b6f1465..7f8a847e 100644 --- a/editor/src/ADocumentWindow.hpp +++ b/editor/src/ADocumentWindow.hpp @@ -6,7 +6,7 @@ // zzz zzz zzz z zzzz zzzz zzzz zzzz // zzz zzz zzzzzzzzzzzzz zzzz zzz zzzzzzz zzzzz // -// Author: Mehdy MORVAN +// Author: Guillaume HEIN // Date: 10/11/2024 // Description: Header file for the document window abstract class // @@ -48,4 +48,4 @@ namespace nexo::editor { std::shared_ptr m_sceneManagerBridge; }; -} \ No newline at end of file +} diff --git a/editor/src/DocumentWindows/AssetManagerWindow.cpp b/editor/src/DocumentWindows/AssetManagerWindow.cpp new file mode 100644 index 00000000..36edec40 --- /dev/null +++ b/editor/src/DocumentWindows/AssetManagerWindow.cpp @@ -0,0 +1,178 @@ +//// AssetManagerWindow.hpp /////////////////////////////////////////////////// +// +// zzzzz zzz zzzzzzzzzzzzz zzzz zzzz zzzzzz zzzzz +// zzzzzzz zzz zzzz zzzz zzzz zzzz +// zzz zzz zzz zzzzzzzzzzzzz zzzz zzzz zzz +// zzz zzz zzz z zzzz zzzz zzzz zzzz +// zzz zzz zzzzzzzzzzzzz zzzz zzz zzzzzzz zzzzz +// +// Author: Guillaume HEIN +// Date: 18/11/2024 +// Description: Source file for the AssetManagerWindow class +// +/////////////////////////////////////////////////////////////////////////////// + +#include "AssetManagerWindow.hpp" +#include + +// AssetManagerWindow.hpp /////////////////////////////////////////////////// +// +// zzzzz zzz zzzzzzzzzzzzz zzzz zzzz zzzzzz zzzzz +// zzzzzzz zzz zzzz zzzz zzzz zzzz +// zzz zzz zzz zzzzzzzzzzzzz zzzz zzzz zzz +// zzz zzz zzz z zzzz zzzz zzzz zzzz +// zzz zzz zzzzzzzzzzzzz zzzz zzz zzzzzzz zzzzz +// +// Author: Guillaume HEIN +// Date: 18/11/2024 +// Description: Source file for the AssetManagerWindow class +// +/////////////////////////////////////////////////////////////////////////////// + +#include "AssetManagerWindow.hpp" +#include + +namespace nexo::editor { + + void AssetManagerWindow::setup() { + // Initialize assets + for (int i = 0; i < 100; ++i) { + assets.push_back({ "Asset " + std::to_string(i), i % 3 }); // Alternate types + } + } + + void AssetManagerWindow::shutdown() { + assets.clear(); + } + + void AssetManagerWindow::show() { + ImGui::SetNextWindowSize(ImVec2(800, 600), ImGuiCond_FirstUseEver); + if (!ImGui::Begin("Asset Manager", nullptr, ImGuiWindowFlags_MenuBar)) { + ImGui::End(); + return; + } + + drawMenuBar(); + + float availWidth = ImGui::GetContentRegionAvail().x; + calculateLayout(availWidth); + + drawAssetsGrid(); + + ImGui::End(); + } + + void AssetManagerWindow::update() { + // Update logic if necessary + } + + void AssetManagerWindow::calculateLayout(float availWidth) { + // Sizes + layout.size.columnCount = std::max(static_cast(availWidth / (layout.size.iconSize + layout.size.iconSpacing)), 1); + layout.size.itemSize = ImVec2(layout.size.iconSize + ImGui::GetFontSize() * 1.5f, layout.size.iconSize + ImGui::GetFontSize() * 1.7f); + layout.size.itemStep = ImVec2(layout.size.itemSize.x + layout.size.iconSpacing, layout.size.itemSize.y + layout.size.iconSpacing); + + // Colors + layout.color.thumbnailBg = ImGui::GetColorU32(ImGuiCol_Button); + layout.color.thumbnailBgHovered = ImGui::GetColorU32(ImGuiCol_ButtonHovered); + layout.color.thumbnailBgSelected = ImGui::GetColorU32(ImGuiCol_Header); + layout.color.thumbnailBgSelectedHovered = ImGui::GetColorU32(ImGuiCol_HeaderHovered); + + layout.color.titleBg = ImGui::GetColorU32(ImGuiCol_Header); + layout.color.titleBgHovered = ImGui::GetColorU32(ImGuiCol_HeaderHovered); + layout.color.titleBgSelected = ImGui::GetColorU32(ImGuiCol_Header); + layout.color.titleBgSelectedHovered = ImGui::GetColorU32(ImGuiCol_HeaderHovered); + + layout.color.titleText = ImGui::GetColorU32(ImGuiCol_Text); + } + + void AssetManagerWindow::drawMenuBar() { + if (ImGui::BeginMenuBar()) { + if (ImGui::BeginMenu("Options")) { + ImGui::SliderFloat("Icon Size", &layout.size.iconSize, 32.0f, 128.0f, "%.0f"); + ImGui::SliderInt("Icon Spacing", &layout.size.iconSpacing, 0, 32); + ImGui::EndMenu(); + } + ImGui::EndMenuBar(); + } + } + + void AssetManagerWindow::drawAssetsGrid() { + ImDrawList* drawList = ImGui::GetWindowDrawList(); + ImVec2 startPos = ImGui::GetCursorScreenPos(); + + ImGuiListClipper clipper; + clipper.Begin(assets.size(), layout.size.itemStep.y); + while (clipper.Step()) { + for (int lineIdx = clipper.DisplayStart; lineIdx < clipper.DisplayEnd; ++lineIdx) { + int startIdx = lineIdx * layout.size.columnCount; + int endIdx = std::min(startIdx + layout.size.columnCount, static_cast(assets.size())); + + for (int i = startIdx; i < endIdx; ++i) { + ImVec2 itemPos = ImVec2(startPos.x + (i % layout.size.columnCount) * layout.size.itemStep.x, + startPos.y + (i / layout.size.columnCount) * layout.size.itemStep.y); + drawAsset(assets[i], i, itemPos, layout.size.itemSize); + } + } + } + clipper.End(); + } + + void AssetManagerWindow::drawAsset(const Asset& asset, int index, const ImVec2& itemPos, const ImVec2& itemSize) { + ImDrawList* drawList = ImGui::GetWindowDrawList(); + ImVec2 itemEnd = ImVec2(itemPos.x + itemSize.x, itemPos.y + itemSize.y); + + ImGui::PushID(index); + + // Highlight selection + bool isSelected = std::find(selectedAssets.begin(), selectedAssets.end(), index) != selectedAssets.end(); + ImU32 bgColor = isSelected ? layout.color.thumbnailBgSelected : layout.color.thumbnailBg; + drawList->AddRectFilled(itemPos, itemEnd, bgColor, layout.size.cornerRadius); + + // Draw thumbnail + ImVec2 thumbnailEnd = ImVec2(itemPos.x + itemSize.x, itemPos.y + itemSize.y * layout.size.thumbnailHeightRatio); + drawList->AddRectFilled(itemPos, thumbnailEnd, layout.color.thumbnailBg); + + // Draw type overlay + ImVec2 overlayPos = ImVec2(thumbnailEnd.x - layout.size.overlayPadding, itemPos.y + layout.size.overlayPadding); + ImU32 overlayColor = getAssetTypeOverlayColor(asset.type); + drawList->AddRectFilled(overlayPos, ImVec2(overlayPos.x + layout.size.overlaySize, overlayPos.y + layout.size.overlaySize), overlayColor); + + // Draw title + ImVec2 textPos = ImVec2(itemPos.x + (itemSize.x - ImGui::CalcTextSize(asset.name.c_str()).x) * 0.5f, + thumbnailEnd.y + layout.size.titlePadding); + // Background rectangle for text + drawList->AddRectFilled(ImVec2(itemPos.x, thumbnailEnd.y), ImVec2(itemEnd.x, itemEnd.y), layout.color.titleBg); + drawList->AddText(textPos, layout.color.titleText, asset.name.c_str()); + + // Handle input + if (ImGui::InvisibleButton("##item", itemSize)) { + handleSelection(index, isSelected); + } + + ImGui::PopID(); + } + + void AssetManagerWindow::handleSelection(int index, bool isSelected) { + LOG(NEXO_INFO, "Asset {} {}", index, isSelected ? "deselected" : "selected"); + if (ImGui::GetIO().KeyCtrl) { + if (isSelected) + selectedAssets.erase(std::remove(selectedAssets.begin(), selectedAssets.end(), index), selectedAssets.end()); + else + selectedAssets.push_back(index); + } + else { + selectedAssets.clear(); + selectedAssets.push_back(index); + } + } + + ImU32 AssetManagerWindow::getAssetTypeOverlayColor(int type) const { + switch (type) { + case 1: return IM_COL32(200, 70, 70, 255); + case 2: return IM_COL32(70, 170, 70, 255); + default: return IM_COL32(0, 0, 0, 0); + } + } + +} // namespace nexo::editor diff --git a/editor/src/DocumentWindows/AssetManagerWindow.hpp b/editor/src/DocumentWindows/AssetManagerWindow.hpp new file mode 100644 index 00000000..c237664a --- /dev/null +++ b/editor/src/DocumentWindows/AssetManagerWindow.hpp @@ -0,0 +1,79 @@ +//// AssetManagerWindow.hpp /////////////////////////////////////////////////// +// +// zzzzz zzz zzzzzzzzzzzzz zzzz zzzz zzzzzz zzzzz +// zzzzzzz zzz zzzz zzzz zzzz zzzz +// zzz zzz zzz zzzzzzzzzzzzz zzzz zzzz zzz +// zzz zzz zzz z zzzz zzzz zzzz zzzz +// zzz zzz zzzzzzzzzzzzz zzzz zzz zzzzzzz zzzzz +// +// Author: Guillaume HEIN +// Date: 18/11/2024 +// Description: Header file for the AssetManagerWindow class +// +/////////////////////////////////////////////////////////////////////////////// + +#pragma once +#include +#include +#include +#include + +namespace nexo::editor { + + class AssetManagerWindow final : public ADocumentWindow { + public: + AssetManagerWindow() = default; + + void setup() override; + void shutdown() override; + void show() override; + void update() override; + + private: + struct Asset { + std::string name; + int type; // 0: default, 1: red overlay, 2: green overlay + }; + + struct LayoutSettings { + struct LayoutSizes { + float iconSize = 64.0f; + int iconSpacing = 8; + ImVec2 itemSize; + ImVec2 itemStep; + int columnCount; + float thumbnailHeightRatio = 0.8f; + float titlePadding = 5.0f; + float overlaySize = 6.0f; + float overlayPadding = 5.0f; + float cornerRadius = 5.0f; + } size; + + struct LayoutColors { + ImU32 thumbnailBg; + ImU32 thumbnailBgHovered; + ImU32 thumbnailBgSelected; + ImU32 thumbnailBgSelectedHovered; + + ImU32 titleBg; + ImU32 titleBgHovered; + ImU32 titleBgSelected; + ImU32 titleBgSelectedHovered; + + ImU32 titleText; + } color; + }; + + std::vector assets; + std::vector selectedAssets; + LayoutSettings layout; + + void calculateLayout(float availWidth); + void drawMenuBar(); + void drawAssetsGrid(); + void drawAsset(const Asset& asset, int index, const ImVec2& itemPos, const ImVec2& itemSize); + void handleSelection(int index, bool isSelected); + ImU32 getAssetTypeOverlayColor(int type) const; + }; + +} // namespace nexo::editor diff --git a/editor/src/DocumentWindows/ConsoleWindow.cpp b/editor/src/DocumentWindows/ConsoleWindow.cpp index ae3daca4..bcf05847 100644 --- a/editor/src/DocumentWindows/ConsoleWindow.cpp +++ b/editor/src/DocumentWindows/ConsoleWindow.cpp @@ -6,7 +6,7 @@ // zzz zzz zzz z zzzz zzzz zzzz zzzz // zzz zzz zzzzzzzzzzzzz zzzz zzz zzzzzzz zzzzz // -// Author: Mehdy MORVAN +// Author: Guillaume HEIN // Date: 10/11/2024 // Description: Source file for the console window class // diff --git a/editor/src/DocumentWindows/ConsoleWindow.hpp b/editor/src/DocumentWindows/ConsoleWindow.hpp index d865edfb..e6d48e30 100644 --- a/editor/src/DocumentWindows/ConsoleWindow.hpp +++ b/editor/src/DocumentWindows/ConsoleWindow.hpp @@ -6,7 +6,7 @@ // zzz zzz zzz z zzzz zzzz zzzz zzzz // zzz zzz zzzzzzzzzzzzz zzzz zzz zzzzzzz zzzzz // -// Author: Mehdy MORVAN +// Author: Guillaume HEIN // Date: 10/11/2024 // Description: Header file for the console window class // @@ -61,4 +61,4 @@ namespace nexo::editor { void calcLogPadding(); }; -} \ No newline at end of file +} diff --git a/editor/src/DocumentWindows/MainScene.cpp b/editor/src/DocumentWindows/MainScene.cpp index 42748bce..57a1e483 100644 --- a/editor/src/DocumentWindows/MainScene.cpp +++ b/editor/src/DocumentWindows/MainScene.cpp @@ -6,7 +6,7 @@ // zzz zzz zzz z zzzz zzzz zzzz zzzz // zzz zzz zzzzzzzzzzzzz zzzz zzz zzzzzzz zzzzz // -// Author: Mehdy MORVAN +// Author: Guillaume HEIN // Date: 10/11/2024 // Description: Source for the main scene document window // diff --git a/editor/src/DocumentWindows/MainScene.hpp b/editor/src/DocumentWindows/MainScene.hpp index 3a84e0a8..6bb85ee4 100644 --- a/editor/src/DocumentWindows/MainScene.hpp +++ b/editor/src/DocumentWindows/MainScene.hpp @@ -6,7 +6,7 @@ // zzz zzz zzz z zzzz zzzz zzzz zzzz // zzz zzz zzzzzzzzzzzzz zzzz zzz zzzzzzz zzzzz // -// Author: Mehdy MORVAN +// Author: Guillaume HEIN // Date: 10/11/2024 // Description: Header file for the main document window // @@ -135,4 +135,4 @@ namespace nexo::editor { void rayPicking() const; }; -} \ No newline at end of file +} diff --git a/editor/src/DocumentWindows/SceneTreeWindow.cpp b/editor/src/DocumentWindows/SceneTreeWindow.cpp index 93d04e86..b582778e 100644 --- a/editor/src/DocumentWindows/SceneTreeWindow.cpp +++ b/editor/src/DocumentWindows/SceneTreeWindow.cpp @@ -6,7 +6,7 @@ // zzz zzz zzz z zzzz zzzz zzzz zzzz // zzz zzz zzzzzzzzzzzzz zzzz zzz zzzzzzz zzzzz // -// Author: Mehdy MORVAN +// Author: Guillaume HEIN // Date: 13/11/2024 // Description: Source for the scene tree document window // diff --git a/editor/src/DocumentWindows/SceneTreeWindow.hpp b/editor/src/DocumentWindows/SceneTreeWindow.hpp index 066b943d..889fe076 100644 --- a/editor/src/DocumentWindows/SceneTreeWindow.hpp +++ b/editor/src/DocumentWindows/SceneTreeWindow.hpp @@ -6,7 +6,7 @@ // zzz zzz zzz z zzzz zzzz zzzz zzzz // zzz zzz zzzzzzzzzzzzz zzzz zzz zzzzzzz zzzzz // -// Author: Mehdy MORVAN +// Author: Guillaume HEIN // Date: 13/11/2024 // Description: Header file for the scene tree document window // diff --git a/editor/src/Editor.cpp b/editor/src/Editor.cpp index d18b9960..53f53edc 100644 --- a/editor/src/Editor.cpp +++ b/editor/src/Editor.cpp @@ -6,7 +6,7 @@ // zzz zzz zzz z zzzz zzzz zzzz zzzz // zzz zzz zzzzzzzzzzzzz zzzz zzz zzzzzzz zzzzz // -// Author: Mehdy MORVAN +// Author: Guillaume HEIN // Date: 09/11/2024 // Description: Source file for the main editor class // diff --git a/editor/src/Editor.hpp b/editor/src/Editor.hpp index c5fa9beb..623a6691 100644 --- a/editor/src/Editor.hpp +++ b/editor/src/Editor.hpp @@ -6,7 +6,7 @@ // zzz zzz zzz z zzzz zzzz zzzz zzzz // zzz zzz zzzzzzzzzzzzz zzzz zzz zzzzzzz zzzzz // -// Author: Mehdy MORVAN +// Author: Guillaume HEIN // Date: 09/11/2024 // Description: Header file for the main editor class // diff --git a/editor/src/IDocumentWindow.hpp b/editor/src/IDocumentWindow.hpp index 5fa12100..35bdda4a 100644 --- a/editor/src/IDocumentWindow.hpp +++ b/editor/src/IDocumentWindow.hpp @@ -6,7 +6,7 @@ // zzz zzz zzz z zzzz zzzz zzzz zzzz // zzz zzz zzzzzzzzzzzzz zzzz zzz zzzzzzz zzzzz // -// Author: Mehdy MORVAN +// Author: Guillaume HEIN // Date: 09/11/2024 // Description: Header file for the document window interface // @@ -31,4 +31,4 @@ namespace nexo::editor { [[nodiscard]] virtual bool isOpened() const = 0; [[nodiscard]] virtual bool &getOpened() = 0; }; -} \ No newline at end of file +} diff --git a/editor/src/SceneManagerBridge.cpp b/editor/src/SceneManagerBridge.cpp index a1a09304..6399216a 100644 --- a/editor/src/SceneManagerBridge.cpp +++ b/editor/src/SceneManagerBridge.cpp @@ -6,7 +6,7 @@ // zzz zzz zzz z zzzz zzzz zzzz zzzz // zzz zzz zzzzzzzzzzzzz zzzz zzz zzzzzzz zzzzz // -// Author: Mehdy MORVAN +// Author: Guillaume HEIN // Date: 10/11/2024 // Description: Source file for the scene manager class // @@ -116,4 +116,4 @@ namespace nexo::editor { } } -} \ No newline at end of file +} diff --git a/editor/src/SceneManagerBridge.hpp b/editor/src/SceneManagerBridge.hpp index 7d8d9293..c6027cc5 100644 --- a/editor/src/SceneManagerBridge.hpp +++ b/editor/src/SceneManagerBridge.hpp @@ -6,7 +6,7 @@ // zzz zzz zzz z zzzz zzzz zzzz zzzz // zzz zzz zzzzzzzzzzzzz zzzz zzz zzzzzzz zzzzz // -// Author: Mehdy MORVAN +// Author: Guillaume HEIN // Date: 10/11/2024 // Description: Header file for the scene manager bridge class // @@ -99,4 +99,4 @@ namespace nexo::editor { bool m_isEntitySelected = false; }; -} \ No newline at end of file +} diff --git a/editor/src/backends/ImGuiBackend.cpp b/editor/src/backends/ImGuiBackend.cpp index 104c4f1b..31ac3928 100644 --- a/editor/src/backends/ImGuiBackend.cpp +++ b/editor/src/backends/ImGuiBackend.cpp @@ -75,4 +75,4 @@ namespace nexo::editor { THROW_EXCEPTION(BackendRendererApiNotSupported, "UNKNOWN"); } -} \ No newline at end of file +} diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 0345a0ff..011a9a5a 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -9,55 +9,62 @@ set(CMAKE_CXX_STANDARD_REQUIRED True) # Add source files set(COMMON_SOURCES - common/Exception.cpp - engine/src/Nexo.cpp - engine/src/EntityFactory2D.cpp - engine/src/EntityFactory3D.cpp - engine/src/Application.cpp - engine/src/Application.cpp - engine/src/renderer/Window.cpp - engine/src/core/event/Event.cpp - engine/src/core/event/Input.cpp - engine/src/core/event/SignalEvent.cpp - engine/src/core/event/opengl/InputOpenGl.cpp - engine/src/core/event/WindowEvent.cpp - engine/src/renderer/Buffer.cpp - engine/src/renderer/Shader.cpp - engine/src/renderer/VertexArray.cpp - engine/src/renderer/RendererAPI.cpp - engine/src/renderer/Renderer.cpp - engine/src/renderer/RenderCommand.cpp - engine/src/renderer/Texture.cpp - engine/src/renderer/SubTexture2D.cpp - engine/src/renderer/Renderer2D.cpp - engine/src/renderer/Renderer3D.cpp - engine/src/renderer/Framebuffer.cpp - engine/src/core/camera/PerspectiveCamera.cpp - engine/src/core/camera/OrthographicCamera.cpp - engine/src/core/camera/PerspectiveCameraController.cpp - engine/src/core/camera/OrthographicCameraController.cpp - engine/src/core/camera/Camera.cpp - engine/src/core/layer/Layer.cpp - engine/src/core/layer/LayerStack.cpp - engine/src/core/scene/Scene.cpp - engine/src/core/scene/SceneManager.cpp - engine/src/ecs/Entity.cpp - engine/src/ecs/Components.cpp - engine/src/ecs/Coordinator.cpp - engine/src/ecs/System.cpp - engine/src/systems/OnSceneDeletedSystem.cpp + common/Exception.cpp +) + +list(APPEND COMMON_SOURCES + engine/src/Nexo.cpp + engine/src/EntityFactory2D.cpp + engine/src/EntityFactory3D.cpp + engine/src/Application.cpp + engine/src/Application.cpp + engine/src/renderer/Window.cpp + engine/src/assets/Asset.cpp + engine/src/assets/AssetManager.cpp + engine/src/assets/AssetRef.cpp + engine/src/core/event/Event.cpp + engine/src/core/event/Input.cpp + engine/src/core/event/SignalEvent.cpp + engine/src/core/event/opengl/InputOpenGl.cpp + engine/src/core/event/WindowEvent.cpp + engine/src/renderer/Buffer.cpp + engine/src/renderer/Shader.cpp + engine/src/renderer/VertexArray.cpp + engine/src/renderer/RendererAPI.cpp + engine/src/renderer/Renderer.cpp + engine/src/renderer/RenderCommand.cpp + engine/src/renderer/Texture.cpp + engine/src/renderer/SubTexture2D.cpp + engine/src/renderer/Renderer2D.cpp + engine/src/renderer/Renderer3D.cpp + engine/src/renderer/Framebuffer.cpp + engine/src/core/camera/PerspectiveCamera.cpp + engine/src/core/camera/OrthographicCamera.cpp + engine/src/core/camera/PerspectiveCameraController.cpp + engine/src/core/camera/OrthographicCameraController.cpp + engine/src/core/camera/Camera.cpp + engine/src/core/layer/Layer.cpp + engine/src/core/layer/LayerStack.cpp + engine/src/core/scene/Scene.cpp + engine/src/core/scene/SceneManager.cpp + engine/src/ecs/Entity.cpp + engine/src/ecs/Components.cpp + engine/src/ecs/Coordinator.cpp + engine/src/ecs/System.cpp + engine/src/systems/OnSceneDeletedSystem.cpp + engine/src/assets/AssetLocation.cpp ) # Add API-specific sources if(NEXO_GRAPHICS_API STREQUAL "OpenGL") list(APPEND COMMON_SOURCES - engine/src/renderer/opengl/OpenGlBuffer.cpp - engine/src/renderer/opengl/OpenGlWindow.cpp - engine/src/renderer/opengl/OpenGlVertexArray.cpp - engine/src/renderer/opengl/OpenGlTexture2D.cpp - engine/src/renderer/opengl/OpenGlShader.cpp - engine/src/renderer/opengl/OpenGlRendererApi.cpp - engine/src/renderer/opengl/OpenGlFramebuffer.cpp + engine/src/renderer/GraphicsBackends/opengl/OpenGlBuffer.cpp + engine/src/renderer/GraphicsBackends/opengl/OpenGlWindow.cpp + engine/src/renderer/GraphicsBackends/opengl/OpenGlVertexArray.cpp + engine/src/renderer/GraphicsBackends/opengl/OpenGlTexture2D.cpp + engine/src/renderer/GraphicsBackends/opengl/OpenGlShader.cpp + engine/src/renderer/GraphicsBackends/opengl/OpenGlRendererApi.cpp + engine/src/renderer/GraphicsBackends/opengl/OpenGlFramebuffer.cpp ) endif() diff --git a/engine/src/assets/Asset.cpp b/engine/src/assets/Asset.cpp new file mode 100644 index 00000000..f3d51258 --- /dev/null +++ b/engine/src/assets/Asset.cpp @@ -0,0 +1,19 @@ +//// Asset.cpp //////////////////////////////////////////////////////////////// +// +// zzzzz zzz zzzzzzzzzzzzz zzzz zzzz zzzzzz zzzzz +// zzzzzzz zzz zzzz zzzz zzzz zzzz +// zzz zzz zzz zzzzzzzzzzzzz zzzz zzzz zzz +// zzz zzz zzz z zzzz zzzz zzzz zzzz +// zzz zzz zzzzzzzzzzzzz zzzz zzz zzzzzzz zzzzz +// +// Author: Guillaume HEIN +// Date: 18/11/2024 +// Description: Source file for the Asset class, base class for all assets +// +/////////////////////////////////////////////////////////////////////////////// + +#include "Asset.hpp" + +namespace nexo::assets { + +} // namespace nexo::editor diff --git a/engine/src/assets/Asset.hpp b/engine/src/assets/Asset.hpp new file mode 100644 index 00000000..4438f650 --- /dev/null +++ b/engine/src/assets/Asset.hpp @@ -0,0 +1,118 @@ +//// Asset.hpp //////////////////////////////////////////////////////////////// +// +// zzzzz zzz zzzzzzzzzzzzz zzzz zzzz zzzzzz zzzzz +// zzzzzzz zzz zzzz zzzz zzzz zzzz +// zzz zzz zzz zzzzzzzzzzzzz zzzz zzzz zzz +// zzz zzz zzz z zzzz zzzz zzzz zzzz +// zzz zzz zzzzzzzzzzzzz zzzz zzz zzzzzzz zzzzz +// +// Author: Guillaume HEIN +// Date: 18/11/2024 +// Description: Header file for the Asset class, base class for all assets +// +/////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "AssetRef.hpp" + +namespace nexo::assets { + + enum class AssetType { + TEXTURE, + MODEL, + SOUND, + MUSIC, + FONT, + SHADER, + SCRIPT + }; + + using AssetID = boost::uuids::uuid; + + enum class AssetStatus { + LOADED, + UNLOADED, + ERROR + }; + + class AssetManager; + + struct AssetMetadata { + AssetType type; //< Asset type + AssetStatus status; //< Asset status + uint64_t referenceCount; //< Number of references to the asset + AssetID id; //< Unique identifier + AssetManager *manager; //< Pointer to the asset manager + }; + + class IAsset { + friend class AssetManager; + friend class AssetRefBase; + public: + IAsset() = delete; + virtual ~IAsset() = default; + + [[nodiscard]] virtual const AssetMetadata& getMetadata() const { return m_metadata; } + [[nodiscard]] virtual AssetType getType() const { return getMetadata().type; } + [[nodiscard]] virtual AssetID getID() const { return getMetadata().id; } + [[nodiscard]] virtual AssetStatus getStatus() const { return getMetadata().status; } + protected: + explicit IAsset(AssetType type) + : m_metadata({ + .type = type, + .status = AssetStatus::UNLOADED, + .referenceCount = 0, + .id = boost::uuids::nil_uuid(), + .manager = nullptr + }) + { + } + + private: + AssetMetadata m_metadata; + + /** + * @brief Get the metadata of the asset (for modification) + */ + [[nodiscard]] AssetMetadata& getMetadata() { return m_metadata; } + + virtual AssetStatus load() = 0; + virtual AssetStatus unload() = 0; + + }; + + template + class Asset : public IAsset { + friend class AssetManager; + + friend class AssetRef; + public: + virtual ~Asset() = default; + + [[nodiscard]] const AssetMetadata& getMetadata() const { return m_metadata; } + [[nodiscard]] AssetType getType() const { return getMetadata().type; } + [[nodiscard]] AssetID getID() const { return getMetadata().id; } + [[nodiscard]] AssetStatus getStatus() const { return getMetadata().status; } + protected: + explicit Asset(AssetType type) : IAsset(type) + { + } + + private: + TAssetData m_data; + + virtual AssetStatus load() = 0; + virtual AssetStatus unload() = 0; + + }; + +} // namespace nexo::editor diff --git a/engine/src/assets/AssetCatalog.hpp b/engine/src/assets/AssetCatalog.hpp new file mode 100644 index 00000000..983d346c --- /dev/null +++ b/engine/src/assets/AssetCatalog.hpp @@ -0,0 +1,49 @@ +//// AssetCatalog.hpp ///////////////////////////////////////////////////////// +// +// zzzzz zzz zzzzzzzzzzzzz zzzz zzzz zzzzzz zzzzz +// zzzzzzz zzz zzzz zzzz zzzz zzzz +// zzz zzz zzz zzzzzzzzzzzzz zzzz zzzz zzz +// zzz zzz zzz z zzzz zzzz zzzz zzzz +// zzz zzz zzzzzzzzzzzzz zzzz zzz zzzzzzz zzzzz +// +// Author: Guillaume HEIN +// Date: 03/12/2024 +// Description: Header file for the AssetCatalog class +// +/////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "AssetImporter.hpp" + +namespace nexo::assets { + + + /** + * @class AssetCatalog + * + * @brief Singleton class that holds all the assets in the engine. + */ + class AssetCatalog { + public: + AssetCatalog() = default; + ~AssetCatalog() = default; + + /*void addAsset(AssetID id, AssetRef asset); + void removeAsset(AssetID id); + void removeAsset(AssetRef asset); + + [[nodiscard]] AssetRef getAsset(AssetID id) const; + [[nodiscard]] AssetRef getAsset(const std::string &name) const; + + [[nodiscard]] bool hasAsset(AssetID id) const; + [[nodiscard]] bool hasAsset(const std::string &name) const; + + [[nodiscard]] std::size_t size() const; + + private: + std::unordered_map> m_assets;*/ + }; + + +} // namespace nexo::assets diff --git a/engine/src/assets/AssetConcepts.hpp b/engine/src/assets/AssetConcepts.hpp new file mode 100644 index 00000000..1aa0f44f --- /dev/null +++ b/engine/src/assets/AssetConcepts.hpp @@ -0,0 +1,24 @@ +//// AssetConcepts.hpp //////////////////////////////////////////////////////// +// +// zzzzz zzz zzzzzzzzzzzzz zzzz zzzz zzzzzz zzzzz +// zzzzzzz zzz zzzz zzzz zzzz zzzz +// zzz zzz zzz zzzzzzzzzzzzz zzzz zzzz zzz +// zzz zzz zzz z zzzz zzzz zzzz zzzz +// zzz zzz zzzzzzzzzzzzz zzzz zzz zzzzzzz zzzzz +// +// Author: Guillaume HEIN +// Date: 25/11/2024 +// Description: Header file for concepts related to assets. +// +/////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include + +namespace nexo::assets { + /*class Asset; + + template + concept DerivedFromAsset = std::derived_from;*/ +} // namespace nexo::assets diff --git a/engine/src/assets/AssetImporter.hpp b/engine/src/assets/AssetImporter.hpp new file mode 100644 index 00000000..bb04fc6e --- /dev/null +++ b/engine/src/assets/AssetImporter.hpp @@ -0,0 +1,40 @@ +//// AssetImporter.hpp //////////////////////////////////////////////////////// +// +// zzzzz zzz zzzzzzzzzzzzz zzzz zzzz zzzzzz zzzzz +// zzzzzzz zzz zzzz zzzz zzzz zzzz +// zzz zzz zzz zzzzzzzzzzzzz zzzz zzzz zzz +// zzz zzz zzz z zzzz zzzz zzzz zzzz +// zzz zzz zzzzzzzzzzzzz zzzz zzz zzzzzzz zzzzz +// +// Author: Guillaume HEIN +// Date: 05/12/2024 +// Description: Header file for the AssetImporter class +// +/////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "Asset.hpp" + +namespace nexo::assets { + + /** + * @class AssetImporter + * + * @brief Interface for importing assets into the engine. + */ + class AssetImporter { + public: + AssetImporter() = default; + virtual ~AssetImporter() = default; + + /*/** + * @brief Imports an asset from a file. + * + * @param path The path to the file to import. + * @return The imported asset. + #1# + virtual AssetRef import(std::filesystem::path path) = 0;*/ + }; + +} // namespace nexo::assets diff --git a/engine/src/assets/AssetLocation.cpp b/engine/src/assets/AssetLocation.cpp new file mode 100644 index 00000000..dd47173c --- /dev/null +++ b/engine/src/assets/AssetLocation.cpp @@ -0,0 +1,29 @@ +//// AssetLocation.cpp //////////////////////////////////////////////////////// +// +// zzzzz zzz zzzzzzzzzzzzz zzzz zzzz zzzzzz zzzzz +// zzzzzzz zzz zzzz zzzz zzzz zzzz +// zzz zzz zzz zzzzzzzzzzzzz zzzz zzzz zzz +// zzz zzz zzz z zzzz zzzz zzzz zzzz +// zzz zzz zzzzzzzzzzzzz zzzz zzz zzzzzzz zzzzz +// +// Author: Guillaume HEIN +// Date: 07/12/2024 +// Description: Source file for the AssetLocation class +// +/////////////////////////////////////////////////////////////////////////////// + +#include "AssetLocation.hpp" + +namespace nexo::assets { + + void AssetLocation::setLocation( + const AssetName& name, + const std::string& path, + const std::optional>& packName + ) + { + _name = name; + _path = path; + _packName = packName; + } +} // namespace nexo::assets diff --git a/engine/src/assets/AssetLocation.hpp b/engine/src/assets/AssetLocation.hpp new file mode 100644 index 00000000..dd6b38e9 --- /dev/null +++ b/engine/src/assets/AssetLocation.hpp @@ -0,0 +1,121 @@ +//// AssetLocation.hpp //////////////////////////////////////////////////////// +// +// zzzzz zzz zzzzzzzzzzzzz zzzz zzzz zzzzzz zzzzz +// zzzzzzz zzz zzzz zzzz zzzz zzzz +// zzz zzz zzz zzzzzzzzzzzzz zzzz zzzz zzz +// zzz zzz zzz z zzzz zzzz zzzz zzzz +// zzz zzz zzzzzzzzzzzzz zzzz zzz zzzzzzz zzzzz +// +// Author: Guillaume HEIN +// Date: 07/12/2024 +// Description: AssetLocation is a wrapper of std::string to represent the +// location of an asset. It is used to apply our own rules on +// naming. +// +/////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include + +#include "AssetName.hpp" +#include "AssetPackName.hpp" + +namespace nexo::assets { + + class InvalidAssetLocation final : public Exception { + public: + explicit InvalidAssetLocation( + std::string_view assetLocation, + std::string_view message, + const std::source_location loc = std::source_location::current() + ) : Exception(std::format("Invalid asset location '{}': {}", assetLocation, message), loc) {}; + }; + + class AssetLocation { + public: + explicit AssetLocation(const std::string& fullLocation) + { + setLocation(fullLocation); + } + + [[nodiscard]] std::optional> getPackName() const { return _packName; } + [[nodiscard]] const AssetName& getAssetName() const { return _name; } + [[nodiscard]] const std::string& getPath() const { return _path; } + + [[nodiscard]] std::string getFullLocation() const + { + std::string fullLocation; + if (_packName) + fullLocation += _packName->getName() + "::"; + fullLocation += _name.getName(); + if (!_path.empty()) + fullLocation += "@" + _path; + return fullLocation; + } + + void setLocation( + const AssetName& name, + const std::string& path, + const std::optional>& packName = std::nullopt + ); + + void setLocation(const std::string& fullLocation) + { + std::string extractedPackName; + std::string extractedAssetName; + std::string extractedPath; + + parseFullLocation(fullLocation, extractedAssetName, extractedPath, extractedPackName); + + try { + _name = AssetName(extractedAssetName); + if (!extractedPackName.empty()) + _packName = AssetPackName(extractedPackName); + } catch (const InvalidName& e) { + THROW_EXCEPTION(InvalidAssetLocation, fullLocation, e.getMessage()); + } + _path = extractedPath; + } + + /** + * @brief Parse a full asset location string into its components + * @param fullLocation The full location string to parse + * @param extractedAssetName The extracted asset name + * @param extractedPath The extracted path + * @param extractedPackName The extracted package name + * @note This function is static and can be used to parse a full location string into its components + * @warning Does not validate the extracted names + */ + static void parseFullLocation( + std::string_view fullLocation, + std::string& extractedAssetName, + std::string& extractedPath, + std::string& extractedPackName + ) + { + if (auto packNameEndPos = fullLocation.find("::"); packNameEndPos != std::string::npos) { + extractedPackName = fullLocation.substr(0, packNameEndPos); + fullLocation.remove_prefix(packNameEndPos + 2); + } else { + extractedPackName.clear(); + } + if (auto pathStartPos = fullLocation.find('@'); pathStartPos != std::string::npos) { + extractedPath = fullLocation.substr(pathStartPos + 1); + fullLocation.remove_suffix(fullLocation.size() - pathStartPos); + } else { + extractedPath.clear(); + } + extractedAssetName = fullLocation; + } + + private: + AssetName _name{"Unnamed"}; //< The name of the asset + std::optional _packName; //< The package containing the asset + std::string _path; //< The path to the asset + }; + + +} // namespace nexo::assets diff --git a/engine/src/assets/AssetManager.cpp b/engine/src/assets/AssetManager.cpp new file mode 100644 index 00000000..8dbd3cac --- /dev/null +++ b/engine/src/assets/AssetManager.cpp @@ -0,0 +1,55 @@ +//// AssetManager.cpp ///////////////////////////////////////////////////////// +// +// zzzzz zzz zzzzzzzzzzzzz zzzz zzzz zzzzzz zzzzz +// zzzzzzz zzz zzzz zzzz zzzz zzzz +// zzz zzz zzz zzzzzzzzzzzzz zzzz zzzz zzz +// zzz zzz zzz z zzzz zzzz zzzz zzzz +// zzz zzz zzzzzzzzzzzzz zzzz zzz zzzzzzz zzzzz +// +// Author: Guillaume HEIN +// Date: 18/11/2024 +// Description: Source file for the AssetManager class +// +/////////////////////////////////////////////////////////////////////////////// + +#include "AssetManager.hpp" + +namespace nexo::assets { + + /* + void AssetManager::loadAsset(const AssetID id) + { + const auto& asset = m_assetMap.at(id); + loadAsset(asset); + } + + void AssetManager::loadAsset(const AssetRef asset) + { + asset->m_metadata.status = asset->load(); + } + + void AssetManager::unloadAsset(const AssetID id) + { + const auto& asset = m_assetMap.at(id); + unloadAsset(asset); + } + + void AssetManager::unloadAsset(const AssetRef asset) + { + asset->m_metadata.status = asset->unload(); + } + + void AssetManager::removeAsset(const AssetRef asset) + { + // TODO: Implement deleting assets + removeAsset(asset->getID()); + } + + void AssetManager::removeAsset(const AssetID uuid) + { + m_assetMap.erase(uuid); + } + */ + + +} // namespace nexo::editor diff --git a/engine/src/assets/AssetManager.hpp b/engine/src/assets/AssetManager.hpp new file mode 100644 index 00000000..98a12679 --- /dev/null +++ b/engine/src/assets/AssetManager.hpp @@ -0,0 +1,68 @@ +//// AssetManager.hpp ///////////////////////////////////////////////////////// +// +// zzzzz zzz zzzzzzzzzzzzz zzzz zzzz zzzzzz zzzzz +// zzzzzzz zzz zzzz zzzz zzzz zzzz +// zzz zzz zzz zzzzzzzzzzzzz zzzz zzzz zzz +// zzz zzz zzz z zzzz zzzz zzzz zzzz +// zzz zzz zzzzzzzzzzzzz zzzz zzz zzzzzzz zzzzz +// +// Author: Guillaume HEIN +// Date: 18/11/2024 +// Description: Header file for the AssetManager class +// +/////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include +#include + +#include "Asset.hpp" +#include "AssetRef.hpp" +#include "AssetConcepts.hpp" +#include "AssetCatalog.hpp" + +namespace nexo::assets { + + class AssetManager { + public: + // to construct an asset manager you should provide via template the type of the AssetRefBase + AssetManager() + { + + } + ~AssetManager() = default; + + /*template + AssetRef createAsset(Args&&... args) { + auto asset = new TAssetData(std::forward(args)...); + auto assetRef = AssetRef(asset); + + auto& metadata = assetRef->getMetadata(); + auto assetId = m_uuidGenerator(); + metadata.id = assetId; + metadata.manager = this; + + m_assetMap[assetId] = assetRef; + + return assetRef; + } + + void loadAsset(AssetID id); + void loadAsset(AssetRef asset); + + void unloadAsset(AssetID id); + void unloadAsset(AssetRef asset); + + void removeAsset(AssetID uuid); + void removeAsset(AssetRef asset); + + AssetRef getAsset(AssetID id); + private: + std::unordered_map> m_assetMap; + boost::uuids::random_generator m_uuidGenerator;*/ + + }; +} // namespace nexo::editor diff --git a/engine/src/assets/AssetName.hpp b/engine/src/assets/AssetName.hpp new file mode 100644 index 00000000..645d9c02 --- /dev/null +++ b/engine/src/assets/AssetName.hpp @@ -0,0 +1,28 @@ +//// AssetName.hpp //////////////////////////////////////////////////////////// +// +// zzzzz zzz zzzzzzzzzzzzz zzzz zzzz zzzzzz zzzzz +// zzzzzzz zzz zzzz zzzz zzzz zzzz +// zzz zzz zzz zzzzzzzzzzzzz zzzz zzzz zzz +// zzz zzz zzz z zzzz zzzz zzzz zzzz +// zzz zzz zzzzzzzzzzzzz zzzz zzz zzzzzzz zzzzz +// +// Author: Guillaume HEIN +// Date: 07/12/2024 +// Description: AssetName is a wrapper of std::string to represent the name +// of an asset. It is used to apply our own rules on naming. +// +/////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "ValidatedName.hpp" +#include "FilenameValidator.hpp" +#include + +namespace nexo::assets { + + struct AssetNameValidator : FilenameValidator {}; + + using AssetName = ValidatedName; + +} // namespace nexo::assets diff --git a/engine/src/assets/AssetPackName.hpp b/engine/src/assets/AssetPackName.hpp new file mode 100644 index 00000000..e23ca24f --- /dev/null +++ b/engine/src/assets/AssetPackName.hpp @@ -0,0 +1,27 @@ +//// AssetPackName.hpp //////////////////////////////////////////////////////// +// +// zzzzz zzz zzzzzzzzzzzzz zzzz zzzz zzzzzz zzzzz +// zzzzzzz zzz zzzz zzzz zzzz zzzz +// zzz zzz zzz zzzzzzzzzzzzz zzzz zzzz zzz +// zzz zzz zzz z zzzz zzzz zzzz zzzz +// zzz zzz zzzzzzzzzzzzz zzzz zzz zzzzzzz zzzzz +// +// Author: Guillaume HEIN +// Date: 07/12/2024 +// Description: AssetPackName is a wrapper of std::string, derived from +// AssetName. It is used to apply our own rules on naming. +// +/////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "ValidatedName.hpp" +#include "AssetName.hpp" + +namespace nexo::assets { + + struct AssetPackNameValidator : FilenameValidator {}; + + using AssetPackName = ValidatedName; + +} // namespace nexo::assets diff --git a/engine/src/assets/AssetRef.cpp b/engine/src/assets/AssetRef.cpp new file mode 100644 index 00000000..c5e02e83 --- /dev/null +++ b/engine/src/assets/AssetRef.cpp @@ -0,0 +1,20 @@ +//// AssetRef.cpp ///////////////////////////////////////////////////////////// +// +// zzzzz zzz zzzzzzzzzzzzz zzzz zzzz zzzzzz zzzzz +// zzzzzzz zzz zzzz zzzz zzzz zzzz +// zzz zzz zzz zzzzzzzzzzzzz zzzz zzzz zzz +// zzz zzz zzz z zzzz zzzz zzzz zzzz +// zzz zzz zzzzzzzzzzzzz zzzz zzz zzzzzzz zzzzz +// +// Author: Guillaume HEIN +// Date: 24/11/2024 +// Description: Source file for the AssetRef class +// +/////////////////////////////////////////////////////////////////////////////// + +#include "AssetRef.hpp" + +namespace nexo::assets { + + +} // namespace nexo::assets diff --git a/engine/src/assets/AssetRef.hpp b/engine/src/assets/AssetRef.hpp new file mode 100644 index 00000000..9f37c96b --- /dev/null +++ b/engine/src/assets/AssetRef.hpp @@ -0,0 +1,178 @@ +/// AssetRef.hpp ///////////////////////////////////////////////////////////// +// +// zzzzz zzz zzzzzzzzzzzzz zzzz zzzz zzzzzz zzzzz +// zzzzzzz zzz zzzz zzzz zzzz zzzz +// zzz zzz zzz zzzzzzzzzzzzz zzzz zzzz zzz +// zzz zzz zzz z zzzz zzzz zzzz zzzz +// zzz zzz zzzzzzzzzzzzz zzzz zzz zzzzzzz zzzzz +// +// Author: Guillaume HEIN +// Date: 24/11/2024 +// Description: Header file for the AssetRef class. +// An AssetRef is a reference to an asset, allowing for easy +// access to the asset's data. The AssetData memory is handled +// by the AssetManager, and the AssetRef is a lightweight +// reference to the asset. +// +/////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include + +#include "AssetConcepts.hpp" + +namespace nexo::assets { + + template + class Asset; + + class AssetManager; + + /** + * @brief Template class representing a lightweight reference to an asset. + * @tparam TAsset The type of asset data being referenced + * + * AssetRef provides a lightweight wrapper around asset data that is managed by the AssetManager. + * It offers convenient access to the asset data while ensuring proper resource management. + * The class focuses on making the common case fast by providing direct pointer access to + * asset data, while still maintaining safety through the AssetManager. + */ + template + class AssetRef { + using TAsset = Asset; + + public: + /** + * @brief Constructs an AssetRef with the given asset data pointer + * @param data Pointer to the asset data + */ + explicit AssetRef(TAsset* data) : m_asset(data) + { + if (!data) + return; + ++m_asset->getMetadata().referenceCount; + } + + ~AssetRef() + { + if (!m_asset) + return; + --m_asset->getMetadata().referenceCount; + } + + /** @brief Copy constructor */ + AssetRef(const AssetRef& other) + { + m_asset = other.m_asset; + ++m_asset->m_metadata.referenceCount; + } + + /** @brief Copy assignment operator */ + AssetRef& operator=(const AssetRef& other) + { + if (this == &other) + return *this; + + if (m_asset) + --m_asset->m_metadata.referenceCount; + + m_asset = other.m_asset; + ++m_asset->m_metadata.referenceCount; + + return *this; + } + + /** @brief Move constructor */ + AssetRef(AssetRef&& other) noexcept + { + m_asset = other.m_asset; + other.m_asset = nullptr; + } + + /** @brief Move assignment operator */ + AssetRef& operator=(AssetRef&& other) noexcept + { + if (this == &other) + return *this; + + m_asset = other.m_asset; + other.m_asset = nullptr; + + return *this; + } + + /** + * @brief Gets the raw pointer to the asset data + * @return Pointer to the asset data + */ + [[nodiscard]] TAssetData* get() const noexcept + { + return m_asset; + } + + /** + * @brief Arrow operator for accessing asset data members + * @return Pointer to the asset data + */ + TAsset* operator->() const noexcept + { + return this->get(); + } + + /** + * @brief Dereference operator for accessing asset data members + * @return Reference to the asset data + */ + TAsset& operator*() const noexcept + { + return *this->get(); + } + + /** + * @brief Checks if the asset reference is valid + * @return true if the reference points to valid data, false otherwise + */ + [[nodiscard]] bool isValid() const + { + return m_asset != nullptr; + } + + /** + * @brief Checks if the asset is fully loaded + * @return true if the asset is loaded, false otherwise + */ + [[nodiscard]] bool isLoaded() const; + + /** @brief Requests the AssetManager to reload the asset */ + void reload(); + + /** @brief Requests the AssetManager to unload the asset but maintain the reference */ + void unload(); + + /** + * @brief Creates a null asset reference + * @return An AssetRef instance with a null data pointer + */ + [[nodiscard]] static AssetRef null() + { + return AssetRef(nullptr); + } + + /** + * @brief Boolean conversion operator + * @return true if the reference is valid, false otherwise + */ + explicit operator bool() const + { + return isValid(); + } + + private: + TAsset* m_asset; /**< Pointer to the actual asset */ + }; + + class GenericAssetRef : public AssetRef {}; + +} // namespace nexo::assets diff --git a/engine/src/assets/FilenameValidator.hpp b/engine/src/assets/FilenameValidator.hpp new file mode 100644 index 00000000..5ab22457 --- /dev/null +++ b/engine/src/assets/FilenameValidator.hpp @@ -0,0 +1,46 @@ +//// FilenameValidator.hpp //////////////////////////////////////////////////// +// +// zzzzz zzz zzzzzzzzzzzzz zzzz zzzz zzzzzz zzzzz +// zzzzzzz zzz zzzz zzzz zzzz zzzz +// zzz zzz zzz zzzzzzzzzzzzz zzzz zzzz zzz +// zzz zzz zzz z zzzz zzzz zzzz zzzz +// zzz zzz zzzzzzzzzzzzz zzzz zzz zzzzzzz zzzzz +// +// Author: Guillaume HEIN +// Date: 13/12/2024 +// Description: FilenameValidator is a struct that provides a static method to +// validate a filename (most OS compatible). +// +/////////////////////////////////////////////////////////////////////////////// + +#pragma once + +namespace nexo::assets { + struct FilenameValidator { + static inline const auto ValidationRegex = std::regex("^[a-zA-Z0-9._-]*$"); + static constexpr auto MaxLength = 255; + static constexpr auto ForbiddenKeywords = { + "CON", "PRN", "AUX", "NUL", + "COM1", "COM2", "COM3", "COM4", + "COM5", "COM6", "COM7", "COM8", + "COM9", "LPT1", "LPT2", "LPT3", + "LPT4", "LPT5", "LPT6", "LPT7", + "LPT8", "LPT9" + }; + + [[nodiscard]] static std::optional validate(std::string_view name) + { + if (name.empty()) + return "Cannot be empty."; + if (name.size() > MaxLength) + return "Cannot exceed 255 characters."; + if (!std::regex_match(name.data(), ValidationRegex)) + return "Allowed characters are 0-9, a-z, A-Z, '.', '_', and '-'."; + for (const auto& keyword : ForbiddenKeywords) { + if (name == keyword) + return "Cannot be a reserved keyword."; + } + return std::nullopt; // Valid name + } + }; +} diff --git a/engine/src/assets/ValidatedName.cpp b/engine/src/assets/ValidatedName.cpp new file mode 100644 index 00000000..403f129c --- /dev/null +++ b/engine/src/assets/ValidatedName.cpp @@ -0,0 +1,23 @@ +//// ValidatedName.cpp //////////////////////////////////////////////////////// +// +// zzzzz zzz zzzzzzzzzzzzz zzzz zzzz zzzzzz zzzzz +// zzzzzzz zzz zzzz zzzz zzzz zzzz +// zzz zzz zzz zzzzzzzzzzzzz zzzz zzzz zzz +// zzz zzz zzz z zzzz zzzz zzzz zzzz +// zzz zzz zzzzzzzzzzzzz zzzz zzz zzzzzzz zzzzz +// +// Author: Guillaume HEIN +// Date: 11/12/2024 +// Description: Source file for the ValidatedName class +// +/////////////////////////////////////////////////////////////////////////////// + +#include "ValidatedName.hpp" + +namespace nexo::assets { + + + + + +} // namespace nexo::assets diff --git a/engine/src/assets/ValidatedName.hpp b/engine/src/assets/ValidatedName.hpp new file mode 100644 index 00000000..008af53a --- /dev/null +++ b/engine/src/assets/ValidatedName.hpp @@ -0,0 +1,137 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "Exception.hpp" + +namespace nexo::assets { + + /** + * @class InvalidName + * + * @brief Exception thrown when a name fails validation. + */ + class InvalidName final : public Exception { + public: + explicit InvalidName( + std::string_view name, + std::string_view message, + const std::source_location loc = std::source_location::current() + ) : Exception(std::format("Invalid name '{}': {}", name, message), loc) {} + }; + + template + concept Validator = requires(std::string_view name) { + { T::validate(name) } -> std::same_as>; //< The validation method. + }; + + /** + * @class ValidatedName + * + * @brief Base class for validated names. + */ + template + class ValidatedName { + public: + virtual ~ValidatedName() = default; + + // Constructors + explicit ValidatedName(const std::string_view name) : _value(name) { + if (auto errorMessage = TValidator::validate(name); errorMessage.has_value()) { + _value = "Unnamed"; + THROW_EXCEPTION(InvalidName, name, errorMessage.value()); + } + } + explicit(false) ValidatedName(const std::string& name) : ValidatedName(std::string_view(name)) {} + explicit(false) ValidatedName(const char* name) : ValidatedName(std::string_view(name)) {} + + + + + /** + * @brief Returns the underlying name as a string. + */ + [[nodiscard]] const std::string& getName() const { return _value; } + + /** + * @brief Returns the size of the name. + */ + [[nodiscard]] std::size_t size() const { return _value.size(); } + + /** + * @brief Implicit conversions for convenience. + */ + + explicit operator std::string() const { return _value; } + explicit operator std::string_view() const { return _value; } + explicit operator const char*() const { return _value.c_str(); } + + [[nodiscard]] const std::string& data() const { return _value; } + [[nodiscard]] const char* c_str() const { return _value.c_str(); } + + + /** + * @brief Equality and inequality operators. + */ + bool operator==(const ValidatedName& other) const { return _value == other._value; } + bool operator!=(const ValidatedName& other) const { return !(*this == other); } + + ValidatedName& operator=(const ValidatedName& other) = default; + ValidatedName& operator=(std::string_view name); + ValidatedName& operator=(const std::string& name); + + ValidatedName& operator=(const char* name); + + std::optional rename(std::string_view name); + + /** + * @brief Validates a name. + * @param name The name to validate. + * @return An error message if the name is invalid, or std::nullopt if it is valid. + * @note This function is static and can be used to validate a name without creating an instance. + */ + static std::optional validate(std::string_view name) { + return TValidator::validate(name); + } + + private: + + std::string _value; //< The validated name value. + }; + + template + ValidatedName& ValidatedName::operator=(std::string_view name) + { + if (auto errorMessage = validate(name); errorMessage.has_value()) + THROW_EXCEPTION(InvalidName, name, errorMessage.value()); + _value = name; + return *this; + } + + template + ValidatedName& ValidatedName::operator=(const std::string& name) + { + *this = std::string_view(name); + return *this; + } + + template + ValidatedName& ValidatedName::operator=(const char* name) + { + this->operator=(std::string_view(name)); + return *this; + } + + template + std::optional ValidatedName::rename(std::string_view name) + { + if (auto errorMessage = validate(name); errorMessage.has_value()) + return errorMessage; + _value = name; + return std::nullopt; + } +} // namespace nexo::assets diff --git a/engine/src/renderer/Buffer.cpp b/engine/src/renderer/Buffer.cpp index 62fa1752..87d45eb3 100644 --- a/engine/src/renderer/Buffer.cpp +++ b/engine/src/renderer/Buffer.cpp @@ -14,7 +14,7 @@ #include "Buffer.hpp" #include "renderer/RendererExceptions.hpp" #ifdef GRAPHICS_API_OPENGL - #include "opengl/OpenGlBuffer.hpp" + #include "GraphicsBackends/opengl/OpenGlBuffer.hpp" #endif diff --git a/engine/src/renderer/Framebuffer.cpp b/engine/src/renderer/Framebuffer.cpp index 6024c3c1..f83427ec 100644 --- a/engine/src/renderer/Framebuffer.cpp +++ b/engine/src/renderer/Framebuffer.cpp @@ -14,7 +14,7 @@ #include "Framebuffer.hpp" #include "renderer/RendererExceptions.hpp" #ifdef GRAPHICS_API_OPENGL - #include "opengl/OpenGlFramebuffer.hpp" + #include "GraphicsBackends/opengl/OpenGlFramebuffer.hpp" #endif namespace nexo::renderer { diff --git a/engine/src/renderer/opengl/OpenGlBuffer.cpp b/engine/src/renderer/GraphicsBackends/opengl/OpenGlBuffer.cpp similarity index 100% rename from engine/src/renderer/opengl/OpenGlBuffer.cpp rename to engine/src/renderer/GraphicsBackends/opengl/OpenGlBuffer.cpp diff --git a/engine/src/renderer/opengl/OpenGlBuffer.hpp b/engine/src/renderer/GraphicsBackends/opengl/OpenGlBuffer.hpp similarity index 100% rename from engine/src/renderer/opengl/OpenGlBuffer.hpp rename to engine/src/renderer/GraphicsBackends/opengl/OpenGlBuffer.hpp diff --git a/engine/src/renderer/opengl/OpenGlFramebuffer.cpp b/engine/src/renderer/GraphicsBackends/opengl/OpenGlFramebuffer.cpp similarity index 100% rename from engine/src/renderer/opengl/OpenGlFramebuffer.cpp rename to engine/src/renderer/GraphicsBackends/opengl/OpenGlFramebuffer.cpp diff --git a/engine/src/renderer/opengl/OpenGlFramebuffer.hpp b/engine/src/renderer/GraphicsBackends/opengl/OpenGlFramebuffer.hpp similarity index 100% rename from engine/src/renderer/opengl/OpenGlFramebuffer.hpp rename to engine/src/renderer/GraphicsBackends/opengl/OpenGlFramebuffer.hpp diff --git a/engine/src/renderer/opengl/OpenGlRendererAPI.hpp b/engine/src/renderer/GraphicsBackends/opengl/OpenGlRendererAPI.hpp similarity index 100% rename from engine/src/renderer/opengl/OpenGlRendererAPI.hpp rename to engine/src/renderer/GraphicsBackends/opengl/OpenGlRendererAPI.hpp diff --git a/engine/src/renderer/opengl/OpenGlRendererApi.cpp b/engine/src/renderer/GraphicsBackends/opengl/OpenGlRendererApi.cpp similarity index 100% rename from engine/src/renderer/opengl/OpenGlRendererApi.cpp rename to engine/src/renderer/GraphicsBackends/opengl/OpenGlRendererApi.cpp diff --git a/engine/src/renderer/opengl/OpenGlShader.cpp b/engine/src/renderer/GraphicsBackends/opengl/OpenGlShader.cpp similarity index 100% rename from engine/src/renderer/opengl/OpenGlShader.cpp rename to engine/src/renderer/GraphicsBackends/opengl/OpenGlShader.cpp diff --git a/engine/src/renderer/opengl/OpenGlShader.hpp b/engine/src/renderer/GraphicsBackends/opengl/OpenGlShader.hpp similarity index 100% rename from engine/src/renderer/opengl/OpenGlShader.hpp rename to engine/src/renderer/GraphicsBackends/opengl/OpenGlShader.hpp diff --git a/engine/src/renderer/opengl/OpenGlTexture2D.cpp b/engine/src/renderer/GraphicsBackends/opengl/OpenGlTexture2D.cpp similarity index 100% rename from engine/src/renderer/opengl/OpenGlTexture2D.cpp rename to engine/src/renderer/GraphicsBackends/opengl/OpenGlTexture2D.cpp diff --git a/engine/src/renderer/opengl/OpenGlTexture2D.hpp b/engine/src/renderer/GraphicsBackends/opengl/OpenGlTexture2D.hpp similarity index 100% rename from engine/src/renderer/opengl/OpenGlTexture2D.hpp rename to engine/src/renderer/GraphicsBackends/opengl/OpenGlTexture2D.hpp diff --git a/engine/src/renderer/opengl/OpenGlVertexArray.cpp b/engine/src/renderer/GraphicsBackends/opengl/OpenGlVertexArray.cpp similarity index 100% rename from engine/src/renderer/opengl/OpenGlVertexArray.cpp rename to engine/src/renderer/GraphicsBackends/opengl/OpenGlVertexArray.cpp diff --git a/engine/src/renderer/opengl/OpenGlVertexArray.hpp b/engine/src/renderer/GraphicsBackends/opengl/OpenGlVertexArray.hpp similarity index 100% rename from engine/src/renderer/opengl/OpenGlVertexArray.hpp rename to engine/src/renderer/GraphicsBackends/opengl/OpenGlVertexArray.hpp diff --git a/engine/src/renderer/opengl/OpenGlWindow.cpp b/engine/src/renderer/GraphicsBackends/opengl/OpenGlWindow.cpp similarity index 100% rename from engine/src/renderer/opengl/OpenGlWindow.cpp rename to engine/src/renderer/GraphicsBackends/opengl/OpenGlWindow.cpp diff --git a/engine/src/renderer/opengl/OpenGlWindow.hpp b/engine/src/renderer/GraphicsBackends/opengl/OpenGlWindow.hpp similarity index 100% rename from engine/src/renderer/opengl/OpenGlWindow.hpp rename to engine/src/renderer/GraphicsBackends/opengl/OpenGlWindow.hpp diff --git a/engine/src/renderer/RenderCommand.cpp b/engine/src/renderer/RenderCommand.cpp index 3199a626..3490bb22 100644 --- a/engine/src/renderer/RenderCommand.cpp +++ b/engine/src/renderer/RenderCommand.cpp @@ -14,7 +14,7 @@ #include "RenderCommand.hpp" #include "renderer/RendererExceptions.hpp" #ifdef GRAPHICS_API_OPENGL - #include "opengl/OpenGlRendererAPI.hpp" + #include "GraphicsBackends/opengl/OpenGlRendererAPI.hpp" #endif namespace nexo::renderer { diff --git a/engine/src/renderer/Renderer2D.cpp b/engine/src/renderer/Renderer2D.cpp index b4190da1..4c2c6095 100644 --- a/engine/src/renderer/Renderer2D.cpp +++ b/engine/src/renderer/Renderer2D.cpp @@ -61,8 +61,7 @@ namespace nexo::renderer { for (int i = 0; i < static_cast(Renderer2DStorage::maxTextureSlots); ++i) samplers[i] = i; - try - { + try { m_storage->textureShader = Shader::create( Path::resolvePathRelativeToExe("../assets/shaders/texture.glsl").string()); m_storage->textureShader->bind(); diff --git a/engine/src/renderer/Shader.cpp b/engine/src/renderer/Shader.cpp index cf5de581..b0dfea2f 100644 --- a/engine/src/renderer/Shader.cpp +++ b/engine/src/renderer/Shader.cpp @@ -15,7 +15,7 @@ #include "renderer/RendererExceptions.hpp" #include "Logger.hpp" #ifdef GRAPHICS_API_OPENGL - #include "opengl/OpenGlShader.hpp" + #include "GraphicsBackends/opengl/OpenGlShader.hpp" #endif #include diff --git a/engine/src/renderer/Texture.cpp b/engine/src/renderer/Texture.cpp index 4732d221..acc6c816 100644 --- a/engine/src/renderer/Texture.cpp +++ b/engine/src/renderer/Texture.cpp @@ -16,7 +16,7 @@ #include "Renderer.hpp" #include "renderer/RendererExceptions.hpp" #ifdef GRAPHICS_API_OPENGL - #include "opengl/OpenGlTexture2D.hpp" + #include "GraphicsBackends/opengl/OpenGlTexture2D.hpp" #endif namespace nexo::renderer { diff --git a/engine/src/renderer/VertexArray.cpp b/engine/src/renderer/VertexArray.cpp index 6c0bc499..e9855b42 100644 --- a/engine/src/renderer/VertexArray.cpp +++ b/engine/src/renderer/VertexArray.cpp @@ -14,7 +14,7 @@ #include "VertexArray.hpp" #include "renderer/RendererExceptions.hpp" #ifdef GRAPHICS_API_OPENGL - #include "opengl/OpenGlVertexArray.hpp" + #include "GraphicsBackends/opengl/OpenGlVertexArray.hpp" #endif namespace nexo::renderer { diff --git a/engine/src/renderer/Window.cpp b/engine/src/renderer/Window.cpp index 94c0bab5..fe4e2b5c 100644 --- a/engine/src/renderer/Window.cpp +++ b/engine/src/renderer/Window.cpp @@ -15,7 +15,7 @@ #include "Window.hpp" #include "renderer/RendererExceptions.hpp" #ifdef GRAPHICS_API_OPENGL - #include "renderer/opengl/OpenGlWindow.hpp" + #include "GraphicsBackends/opengl/OpenGlWindow.hpp" #endif namespace nexo::renderer { diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 747a2652..77701501 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.17) include(${CMAKE_CURRENT_SOURCE_DIR}/examples/ecs/CMakeLists.txt) +message(STATUS "NEXO_BUILD_EXAMPLES: ${NEXO_BUILD_EXAMPLES}") if(NOT NEXO_BUILD_EXAMPLES) message(STATUS "Excluding examples from the 'ALL' target") set_target_properties(ecsExample PROPERTIES EXCLUDE_FROM_ALL TRUE) diff --git a/scripts/CMakeCPackOptions.cmake.in b/scripts/CMakeCPackOptions.cmake.in index 59885e98..20d23469 100755 --- a/scripts/CMakeCPackOptions.cmake.in +++ b/scripts/CMakeCPackOptions.cmake.in @@ -41,7 +41,7 @@ if (CPACK_GENERATOR MATCHES "DEB") set(CPACK_PACKAGING_INSTALL_PREFIX "/usr/share/nexo-engine") message(STATUS "Setting CPACK_PACKAGING_INSTALL_PREFIX to ${CPACK_PACKAGING_INSTALL_PREFIX}") if("@NEXO_GRAPHICS_API@" STREQUAL "OpenGL") - set(CPACK_DEBIAN_PACKAGE_DEPENDS "mesa-utils, libglfw3") + set(CPACK_DEBIAN_PACKAGE_DEPENDS "mesa-utils, libglfw3, libxrandr2 (>= 2:1.2.0), libxrender1") else() message(WARNING "Unknown graphics API: @NEXO_GRAPHICS_API@, cannot set dependencies") endif() diff --git a/scripts/install.cmake b/scripts/install.cmake index 3866c20a..778a3626 100755 --- a/scripts/install.cmake +++ b/scripts/install.cmake @@ -14,9 +14,6 @@ cmake_minimum_required(VERSION 3.28) -# Set output directories -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) - # Installation rules install(TARGETS nexoEditor RUNTIME DESTINATION bin diff --git a/scripts/linux/deb_config.cmake b/scripts/linux/deb_config.cmake index ed3419b4..de2f2cd4 100755 --- a/scripts/linux/deb_config.cmake +++ b/scripts/linux/deb_config.cmake @@ -32,9 +32,23 @@ configure_file(${CMAKE_SOURCE_DIR}/scripts/linux/nexo-engine.desktop.in FILE_PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ ) -# Create symlink for the executable -set(DEB_TEMP_GENERATED_SYMLINK_PATH "${CMAKE_BINARY_DIR}/nexoEditor-symlink") -execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink "../share/nexo-engine/bin/nexoEditor" ${DEB_TEMP_GENERATED_SYMLINK_PATH}) +# Check if the symlink can be created +if(WIN32) + include(${CMAKE_SOURCE_DIR}/scripts/windows/check_dev_mode.cmake) + is_win_developer_mode_enabled(WINDOWS_DEVELOPER_MODE_ENABLED) + set(CAN_CREATE_SYMLINK ${WINDOWS_DEVELOPER_MODE_ENABLED}) + message(VERBOSE "Windows Developer Mode enabled: ${WINDOWS_DEVELOPER_MODE_ENABLED}") +else() + set(CAN_CREATE_SYMLINK ON) +endif() + +if(CAN_CREATE_SYMLINK) + # Create symlink for the executable + set(DEB_TEMP_GENERATED_SYMLINK_PATH "${CMAKE_BINARY_DIR}/nexoEditor-symlink") + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink "../share/nexo-engine/bin/nexoEditor" ${DEB_TEMP_GENERATED_SYMLINK_PATH}) +else () + message(VERBOSE "NEXO Engine symlink couldn't be created. Required for the DEB package generator. See README's troubleshooting section.") +endif() # Install the desktop entry set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA @@ -47,7 +61,18 @@ set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA install(FILES ${DEB_TEMP_GENERATED_SYMLINK_PATH} DESTINATION /usr/bin RENAME "nexoEditor" COMPONENT deb-symlink EXCLUDE_FROM_ALL # EXCLUDE_FROM_ALL is important to avoid installing the symlink in the default component + OPTIONAL # OPTIONAL if symlink couldn't be created ) + +# Check if the symlink couldn't be created, display a warning on install time +if(NOT CAN_CREATE_SYMLINK) + # Create an install code to fatal error if the symlink couldn't be created + # for component deb-symlink + install(CODE "message(FATAL_ERROR \"NEXO Engine symlink couldn't be created. Required for the DEB package generator. See README's troubleshooting section.\")" + COMPONENT deb-symlink EXCLUDE_FROM_ALL + ) +endif() + # Component deb-icon install(FILES "${CMAKE_SOURCE_DIR}/assets/nexo.png" DESTINATION /usr/share/icons/hicolor/256x256/apps diff --git a/scripts/windows/check_dev_mode.cmake b/scripts/windows/check_dev_mode.cmake new file mode 100644 index 00000000..9879d674 --- /dev/null +++ b/scripts/windows/check_dev_mode.cmake @@ -0,0 +1,18 @@ +function(is_win_developer_mode_enabled result) + if (WIN32) + cmake_host_system_information(RESULT result_var + QUERY WINDOWS_REGISTRY "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\AppModelUnlock" + VALUE "AllowDevelopmentWithoutDevLicense" + ERROR_VARIABLE error_var) + + if (${result_var}) + set(${result} TRUE PARENT_SCOPE) + else () + message(DEBUG "Developer mode is not enabled on Windows: ${error_var}") + set(${result} FALSE PARENT_SCOPE) + endif () + else () + message(WARNING "Developer mode is only supported on Windows") + set(${result} FALSE PARENT_SCOPE) + endif () +endfunction() diff --git a/src/game_engine/ecs/CMakeLists.txt b/src/game_engine/ecs/CMakeLists.txt index 9c4f1dad..7c5f18a8 100644 --- a/src/game_engine/ecs/CMakeLists.txt +++ b/src/game_engine/ecs/CMakeLists.txt @@ -7,10 +7,6 @@ set(ROOT "../../..") include_directories(${ROOT}/include) -set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ../${ROOT}/lib) -set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ../${ROOT}/lib) -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ../${ROOT}/bin) - add_library(controls STATIC systems/Controls.cpp) add_library(draw STATIC systems/Draw.cpp) add_library(physics STATIC systems/Physics.cpp) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index df175435..485fa9a1 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -26,6 +26,10 @@ include(GoogleTest) # Find gtest package find_package(GTest CONFIG REQUIRED) +set(TEST_MAIN_FILES + ${CMAKE_CURRENT_LIST_DIR}/test_main.cpp +) + include(${CMAKE_CURRENT_LIST_DIR}/engine/CMakeLists.txt) include(${CMAKE_CURRENT_LIST_DIR}/common/CMakeLists.txt) include(${CMAKE_CURRENT_LIST_DIR}/renderer/CMakeLists.txt) @@ -34,7 +38,6 @@ include(${CMAKE_CURRENT_LIST_DIR}/ecs/CMakeLists.txt) # Add tests gtest_add_tests(TARGET engine_tests TEST_LIST engineTestsList - ) gtest_add_tests(TARGET common_tests TEST_LIST commonTestsList @@ -56,6 +59,7 @@ set_tests_properties(${rendererTestsList} PROPERTIES LABELS "renderer") set_tests_properties(${ecsTestsList} PROPERTIES LABELS "ecs") # Exclude tests from the "ALL" target +message(STATUS "NEXO_BUILD_TESTS: ${NEXO_BUILD_TESTS}") if(NOT NEXO_BUILD_TESTS) message(STATUS "Excluding tests from the 'ALL' target") set_target_properties(engine_tests PROPERTIES EXCLUDE_FROM_ALL TRUE) diff --git a/tests/common/CMakeLists.txt b/tests/common/CMakeLists.txt index f5426986..b73a1115 100644 --- a/tests/common/CMakeLists.txt +++ b/tests/common/CMakeLists.txt @@ -27,6 +27,7 @@ set(COMMON_SOURCES ) add_executable(common_tests + ${TEST_MAIN_FILES} ${COMMON_SOURCES} ${BASEDIR}/Matrix.test.cpp ${BASEDIR}/Path.test.cpp @@ -41,4 +42,4 @@ find_package(Boost CONFIG REQUIRED COMPONENTS dll) target_link_libraries(common_tests PRIVATE Boost::dll) # Link gtest and engine (renderer) libraries -target_link_libraries(common_tests PRIVATE GTest::gtest GTest::gtest_main) +target_link_libraries(common_tests PRIVATE GTest::gtest) diff --git a/tests/ecs/CMakeLists.txt b/tests/ecs/CMakeLists.txt index 5c16d7f1..195dedfd 100644 --- a/tests/ecs/CMakeLists.txt +++ b/tests/ecs/CMakeLists.txt @@ -35,6 +35,7 @@ set(ECS_SOURCES ) add_executable(ecs_tests + ${TEST_MAIN_FILES} ${COMMON_SOURCES} ${ECS_SOURCES} ${BASEDIR}/Components.test.cpp @@ -53,4 +54,4 @@ find_package(Boost CONFIG REQUIRED COMPONENTS dll) target_link_libraries(ecs_tests PRIVATE Boost::dll) # Link gtest and engine (renderer) libraries -target_link_libraries(ecs_tests PRIVATE GTest::gtest GTest::gtest_main GTest::gmock) +target_link_libraries(ecs_tests PRIVATE GTest::gtest GTest::gmock) diff --git a/tests/engine/CMakeLists.txt b/tests/engine/CMakeLists.txt index f4b92847..8e0e0318 100644 --- a/tests/engine/CMakeLists.txt +++ b/tests/engine/CMakeLists.txt @@ -24,6 +24,7 @@ include_directories("./common") # Add engine test source files add_executable(engine_tests + ${TEST_MAIN_FILES} ${BASEDIR}/camera/OrthographicCamera.test.cpp ${BASEDIR}/camera/PerspectiveCamera.test.cpp ${BASEDIR}/event/EventManager.test.cpp @@ -33,8 +34,10 @@ add_executable(engine_tests ${BASEDIR}/layer/Layer.test.cpp ${BASEDIR}/scene/Scene.test.cpp ${BASEDIR}/scene/SceneManager.test.cpp + ${BASEDIR}/assets/AssetName.test.cpp + ${BASEDIR}/assets/AssetLocation.test.cpp # Add other engine test files here ) # Link gtest and engine (renderer) libraries -target_link_libraries(engine_tests GTest::gtest GTest::gtest_main GTest::gmock nexoRenderer) +target_link_libraries(engine_tests GTest::gtest GTest::gmock nexoRenderer) diff --git a/tests/engine/assets/AssetLocation.test.cpp b/tests/engine/assets/AssetLocation.test.cpp new file mode 100644 index 00000000..829ee7d3 --- /dev/null +++ b/tests/engine/assets/AssetLocation.test.cpp @@ -0,0 +1,155 @@ +//// AssetLocation.test.cpp /////////////////////////////////////////////////// +// +// zzzzz zzz zzzzzzzzzzzzz zzzz zzzz zzzzzz zzzzz +// zzzzzzz zzz zzzz zzzz zzzz zzzz +// zzz zzz zzz zzzzzzzzzzzzz zzzz zzzz zzz +// zzz zzz zzz z zzzz zzzz zzzz zzzz +// zzz zzz zzzzzzzzzzzzz zzzz zzz zzzzzzz zzzzz +// +// Author: Guillaume HEIN +// Date: 07/12/2024 +// Description: TODO: desc +// +/////////////////////////////////////////////////////////////////////////////// + +#include +#include "assets/AssetLocation.hpp" +#include "assets/AssetName.hpp" +#include "assets/AssetPackName.hpp" + +namespace nexo::assets { + + TEST(AssetLocationTest, ValidFullLocationWithPackAndPath) + { + std::string fullLocation = "myPack::myAsset@path/to/asset"; + AssetLocation location(fullLocation); + + ASSERT_TRUE(location.getPackName().has_value()); + EXPECT_EQ(location.getPackName()->get().getName(), "myPack"); + EXPECT_EQ(location.getAssetName().getName(), "myAsset"); + EXPECT_EQ(location.getPath(), "path/to/asset"); + EXPECT_EQ(location.getFullLocation(), fullLocation); + } + + TEST(AssetLocationTest, ValidFullLocationWithoutPack) + { + std::string fullLocation = "myAsset@path/to/asset"; + AssetLocation location(fullLocation); + + EXPECT_FALSE(location.getPackName().has_value()); + EXPECT_EQ(location.getAssetName().getName(), "myAsset"); + EXPECT_EQ(location.getPath(), "path/to/asset"); + EXPECT_EQ(location.getFullLocation(), fullLocation); + } + + TEST(AssetLocationTest, ValidFullLocationWithoutPath) + { + std::string fullLocation = "myPack::myAsset"; + AssetLocation location(fullLocation); + + ASSERT_TRUE(location.getPackName().has_value()); + EXPECT_EQ(location.getPackName()->get().getName(), "myPack"); + EXPECT_EQ(location.getAssetName().getName(), "myAsset"); + EXPECT_EQ(location.getPath(), ""); + EXPECT_EQ(location.getFullLocation(), fullLocation); + } + + TEST(AssetLocationTest, InvalidLocationEmpty) + { + EXPECT_THROW(AssetLocation(""), InvalidAssetLocation); + } + + TEST(AssetLocationTest, InvalidLocationWithInvalidName) + { + GTEST_SKIP() << "TODO: Implement validation for path, this test should pass later"; + const std::string invalidLocation = "myPack::my@Asset@path/to/asset"; + EXPECT_THROW({ + AssetLocation cannotCreate(invalidLocation); + }, InvalidAssetLocation); + } + + TEST(AssetLocationTest, ParseFullLocationStaticFunctionValid) + { + const std::string fullLocation = "myPack::myAsset@path/to/asset"; + std::string extractedAssetName, extractedPath, extractedPackName; + + AssetLocation::parseFullLocation(fullLocation, extractedAssetName, extractedPath, extractedPackName); + + EXPECT_EQ(extractedPackName, "myPack"); + EXPECT_EQ(extractedAssetName, "myAsset"); + EXPECT_EQ(extractedPath, "path/to/asset"); + } + + TEST(AssetLocationTest, ParseFullLocationStaticFunctionWithoutPack) + { + const std::string fullLocation = "myAsset@path/to/asset"; + std::string extractedAssetName, extractedPath, extractedPackName; + + AssetLocation::parseFullLocation(fullLocation, extractedAssetName, extractedPath, extractedPackName); + + EXPECT_EQ(extractedPackName, ""); + EXPECT_EQ(extractedAssetName, "myAsset"); + EXPECT_EQ(extractedPath, "path/to/asset"); + } + + TEST(AssetLocationTest, ParseFullLocationStaticFunctionWithoutPath) + { + const std::string fullLocation = "myPack::myAsset"; + std::string extractedAssetName, extractedPath, extractedPackName; + + AssetLocation::parseFullLocation(fullLocation, extractedAssetName, extractedPath, extractedPackName); + + EXPECT_EQ(extractedPackName, "myPack"); + EXPECT_EQ(extractedAssetName, "myAsset"); + EXPECT_EQ(extractedPath, ""); + } + + TEST(AssetLocationTest, SetLocationExplicitly) + { + const AssetName name("myAsset"); + const std::string path = "path/to/asset"; + AssetPackName packName("myPack"); + + AssetLocation location("test"); + EXPECT_EQ(location.getFullLocation(), "test"); + location.setLocation(name, path, packName); + + ASSERT_TRUE(location.getPackName().has_value()); + EXPECT_EQ(location.getPackName()->get().getName(), "myPack"); + EXPECT_EQ(location.getAssetName().getName(), "myAsset"); + EXPECT_EQ(location.getPath(), "path/to/asset"); + EXPECT_EQ(location.getFullLocation(), "myPack::myAsset@path/to/asset"); + } + + TEST(AssetLocationTest, SetLocationExplicitlyWithoutPack) + { + const AssetName name("myAsset"); + const std::string path = "path/to/asset"; + + AssetLocation location("test"); + EXPECT_EQ(location.getFullLocation(), "test"); + location.setLocation(name, path); + + EXPECT_FALSE(location.getPackName().has_value()); + EXPECT_EQ(location.getAssetName().getName(), "myAsset"); + EXPECT_EQ(location.getPath(), "path/to/asset"); + EXPECT_EQ(location.getFullLocation(), "myAsset@path/to/asset"); + } + + TEST(AssetLocationTest, SetLocationExplicitlyWithoutPath) + { + const AssetName name("myAsset"); + AssetPackName packName("myPack"); + + AssetLocation location("test"); + EXPECT_EQ(location.getFullLocation(), "test"); + location.setLocation(name, "", packName); + + ASSERT_TRUE(location.getPackName().has_value()); + EXPECT_EQ(location.getPackName()->get().getName(), "myPack"); + EXPECT_EQ(location.getAssetName().getName(), "myAsset"); + EXPECT_EQ(location.getPath(), ""); + EXPECT_EQ(location.getFullLocation(), "myPack::myAsset"); + } + +} // namespace nexo::assets diff --git a/tests/engine/assets/AssetName.test.cpp b/tests/engine/assets/AssetName.test.cpp new file mode 100644 index 00000000..0f969f60 --- /dev/null +++ b/tests/engine/assets/AssetName.test.cpp @@ -0,0 +1,153 @@ +//// AssetName.test.cpp /////////////////////////////////////////////////////// +// +// zzzzz zzz zzzzzzzzzzzzz zzzz zzzz zzzzzz zzzzz +// zzzzzzz zzz zzzz zzzz zzzz zzzz +// zzz zzz zzz zzzzzzzzzzzzz zzzz zzzz zzz +// zzz zzz zzz z zzzz zzzz zzzz zzzz +// zzz zzz zzzzzzzzzzzzz zzzz zzz zzzzzzz zzzzz +// +// Author: Guillaume HEIN +// Date: 07/12/2024 +// Description: Test file for the AssetName class +// +/////////////////////////////////////////////////////////////////////////////// + +#include +#include "assets/AssetName.hpp" + +namespace nexo::assets { + + TEST(AssetNameTest, ValidAssetNames) + { + EXPECT_NO_THROW(AssetName("ValidName")); + EXPECT_NO_THROW(AssetName("Valid_Name")); + EXPECT_NO_THROW(AssetName("Valid123")); + EXPECT_NO_THROW(AssetName("Valid-Name")); + EXPECT_NO_THROW(AssetName("Valid.Name")); + } + + TEST(AssetNameTest, InvalidEmptyName) + { + EXPECT_THROW(AssetName(""), InvalidName); + } + + TEST(AssetNameTest, InvalidTooLongName) + { + const std::string longName(AssetNameValidator::MaxLength + 1, 'a'); + EXPECT_THROW({ + AssetName test(longName); + }, InvalidName); + } + + TEST(AssetNameTest, InvalidCharactersInName) + { + EXPECT_THROW(AssetName("Invalid@Name"), InvalidName); + EXPECT_THROW(AssetName("Invalid/Name"), InvalidName); + EXPECT_THROW(AssetName("Invalid:Name"), InvalidName); + EXPECT_THROW(AssetName("Invalid?Name"), InvalidName); + } + + TEST(AssetNameTest, ReservedKeywords) + { + for (const auto& keyword : AssetNameValidator::ForbiddenKeywords) { + EXPECT_THROW({ + AssetName test(keyword); + }, InvalidName); + } + } + + TEST(AssetNameTest, StaticValidateNameValidCases) + { + EXPECT_EQ(AssetNameValidator::validate("ValidName"), std::nullopt); + EXPECT_EQ(AssetNameValidator::validate("Valid_Name"), std::nullopt); + EXPECT_EQ(AssetNameValidator::validate("Valid123"), std::nullopt); + } + + TEST(AssetNameTest, StaticValidateNameInvalidCases) + { + EXPECT_EQ(AssetNameValidator::validate(""), "Cannot be empty."); + EXPECT_EQ(AssetNameValidator::validate("Invalid@Name"), "Allowed characters are 0-9, a-z, A-Z, '.', '_', and '-'."); + EXPECT_EQ(AssetNameValidator::validate(std::string(AssetNameValidator::MaxLength + 1, 'a')), "Cannot exceed 255 characters."); + } + + TEST(AssetNameTest, StaticIsValidNameFunction) + { + EXPECT_TRUE(AssetNameValidator::validate("ValidName") == std::nullopt); + EXPECT_FALSE(AssetNameValidator::validate("") == std::nullopt); + EXPECT_FALSE(AssetNameValidator::validate("Invalid@Name") == std::nullopt); + EXPECT_FALSE(AssetNameValidator::validate(std::string(256, 'a')) == std::nullopt); + } + + TEST(AssetNameTest, StringConversion) + { + const AssetName name("ValidName"); + + const std::string str = std::string(name); + const std::string_view sv = std::string_view(name); + const char* cstr = name.c_str(); + EXPECT_EQ(str, "ValidName"); + EXPECT_EQ(sv, "ValidName"); + EXPECT_STREQ(cstr, "ValidName"); + } + + TEST(AssetNameTest, EqualityAndInequalityOperators) + { + AssetName name1("Name1"); + AssetName name2("Name1"); + AssetName name3("Name3"); + + EXPECT_TRUE(name1 == name2); + EXPECT_FALSE(name1 != name2); + EXPECT_FALSE(name1 == name3); + EXPECT_TRUE(name1 != name3); + } + + TEST(AssetNameTest, TestDataAndCstr) + { + AssetName name("Name"); + EXPECT_EQ(name, "Name"); + EXPECT_STREQ(name.c_str(), "Name"); + EXPECT_EQ(std::string(name), "Name"); + EXPECT_EQ(name.data(), "Name"); + } + + TEST(AssetNameTest, Renaming) + { + AssetName name("InitialName"); + EXPECT_EQ(name, "InitialName"); + EXPECT_NO_THROW(name.rename("NewName")); + EXPECT_EQ(name.getName(), "NewName"); + + EXPECT_EQ(name.rename("Invalid@Name"), "Allowed characters are 0-9, a-z, A-Z, '.', '_', and '-'."); + EXPECT_EQ(name.getName(), "NewName"); // Should remain unchanged + } + + TEST(AssetNameTest, InvalidNameExceptionMessage) + { + try { + AssetName name("Invalid@Name"); + } catch (const InvalidName& e) { + EXPECT_EQ(e.getMessage(), "Invalid name 'Invalid@Name': Allowed characters are 0-9, a-z, A-Z, '.', '_', and '-'."); + } + } + + TEST(AssetNameTest, AllInvalidNameExceptions) + { + auto testException = [](const std::string_view name, const std::string& expectedMessage) + { + try { + AssetName assetName(name); + } catch (const InvalidName& e) { + EXPECT_EQ(e.getMessage(), expectedMessage); + } + }; + + testException("", "Invalid name '': Cannot be empty."); + testException("Invalid@Name", "Invalid name 'Invalid@Name': Allowed characters are 0-9, a-z, A-Z, '.', '_', and '-'."); + testException(std::string(AssetNameValidator::MaxLength + 1, 'a'), "Invalid name '" + std::string(AssetNameValidator::MaxLength + 1, 'a') + "': Cannot exceed 255 characters."); + for (const auto& keyword : AssetNameValidator::ForbiddenKeywords) { + testException(keyword, "Invalid name '" + std::string(keyword) + "': Cannot be a reserved keyword."); + } + } + +} // namespace nexo::assets diff --git a/tests/renderer/Buffer.test.cpp b/tests/renderer/Buffer.test.cpp index 0939d22e..7e5084fa 100644 --- a/tests/renderer/Buffer.test.cpp +++ b/tests/renderer/Buffer.test.cpp @@ -17,7 +17,7 @@ #include #include "Buffer.hpp" -#include "opengl/OpenGlBuffer.hpp" +#include "GraphicsBackends/opengl/OpenGlBuffer.hpp" #include "contexts/opengl.hpp" namespace nexo::renderer { diff --git a/tests/renderer/CMakeLists.txt b/tests/renderer/CMakeLists.txt index a3d24010..defecf21 100644 --- a/tests/renderer/CMakeLists.txt +++ b/tests/renderer/CMakeLists.txt @@ -40,16 +40,17 @@ set(RENDERER_SOURCES engine/src/renderer/Renderer2D.cpp engine/src/renderer/Renderer3D.cpp engine/src/renderer/Framebuffer.cpp - engine/src/renderer/opengl/OpenGlBuffer.cpp - engine/src/renderer/opengl/OpenGlWindow.cpp - engine/src/renderer/opengl/OpenGlVertexArray.cpp - engine/src/renderer/opengl/OpenGlTexture2D.cpp - engine/src/renderer/opengl/OpenGlShader.cpp - engine/src/renderer/opengl/OpenGlRendererApi.cpp - engine/src/renderer/opengl/OpenGlFramebuffer.cpp + engine/src/renderer/GraphicsBackends/opengl/OpenGlBuffer.cpp + engine/src/renderer/GraphicsBackends/opengl/OpenGlWindow.cpp + engine/src/renderer/GraphicsBackends/opengl/OpenGlVertexArray.cpp + engine/src/renderer/GraphicsBackends/opengl/OpenGlTexture2D.cpp + engine/src/renderer/GraphicsBackends/opengl/OpenGlShader.cpp + engine/src/renderer/GraphicsBackends/opengl/OpenGlRendererApi.cpp + engine/src/renderer/GraphicsBackends/opengl/OpenGlFramebuffer.cpp ) add_executable(renderer_tests + ${TEST_MAIN_FILES} ${COMMON_SOURCES} ${RENDERER_SOURCES} ${BASEDIR}/Buffer.test.cpp @@ -83,4 +84,4 @@ target_link_libraries(renderer_tests PRIVATE Boost::dll) #target_link_libraries(renderer_tests PRIVATE OpenGL::GL glfw glad::glad) # Link gtest and engine (renderer) libraries -target_link_libraries(renderer_tests PRIVATE GTest::gtest GTest::gtest_main GTest::gmock OpenGL::GL glfw glad::glad) +target_link_libraries(renderer_tests PRIVATE GTest::gtest GTest::gmock OpenGL::GL glfw glad::glad) diff --git a/tests/renderer/Framebuffer.test.cpp b/tests/renderer/Framebuffer.test.cpp index 547d979a..b259f3d3 100644 --- a/tests/renderer/Framebuffer.test.cpp +++ b/tests/renderer/Framebuffer.test.cpp @@ -17,9 +17,9 @@ #include #include -#include "opengl/OpenGlVertexArray.hpp" -#include "opengl/OpenGlBuffer.hpp" -#include "opengl/OpenGlFramebuffer.hpp" +#include "GraphicsBackends/opengl/OpenGlVertexArray.hpp" +#include "GraphicsBackends/opengl/OpenGlBuffer.hpp" +#include "GraphicsBackends/opengl/OpenGlFramebuffer.hpp" #include "contexts/opengl.hpp" #include "RendererExceptions.hpp" diff --git a/tests/renderer/RendererAPI.test.cpp b/tests/renderer/RendererAPI.test.cpp index 4b09f862..3e74e6f1 100644 --- a/tests/renderer/RendererAPI.test.cpp +++ b/tests/renderer/RendererAPI.test.cpp @@ -14,10 +14,10 @@ #include #include #include -#include +#include "GraphicsBackends/opengl/OpenGlVertexArray.hpp" #include "contexts/opengl.hpp" -#include "opengl/OpenGlRendererAPI.hpp" +#include "GraphicsBackends/opengl/OpenGlRendererAPI.hpp" #include "renderer/RendererExceptions.hpp" namespace nexo::renderer { @@ -119,4 +119,4 @@ namespace nexo::renderer { EXPECT_THROW(rendererApi.drawIndexed(nullptr), InvalidValue); } -} \ No newline at end of file +} diff --git a/tests/renderer/Shader.test.cpp b/tests/renderer/Shader.test.cpp index 825110a5..62ab90bb 100644 --- a/tests/renderer/Shader.test.cpp +++ b/tests/renderer/Shader.test.cpp @@ -20,7 +20,7 @@ #include "../utils/comparison.hpp" #include "contexts/opengl.hpp" -#include "opengl/OpenGlShader.hpp" +#include "GraphicsBackends/opengl/OpenGlShader.hpp" #include "renderer/RendererExceptions.hpp" namespace nexo::renderer { diff --git a/tests/renderer/Texture.test.cpp b/tests/renderer/Texture.test.cpp index 4a76c57b..3f1d826d 100644 --- a/tests/renderer/Texture.test.cpp +++ b/tests/renderer/Texture.test.cpp @@ -17,7 +17,7 @@ #include #include -#include "opengl/OpenGlTexture2D.hpp" +#include "GraphicsBackends/opengl/OpenGlTexture2D.hpp" #include "renderer/Texture.hpp" #include "contexts/opengl.hpp" #include "flattenedAssets/testLogo.hpp" diff --git a/tests/renderer/VertexArray.test.cpp b/tests/renderer/VertexArray.test.cpp index f42e039d..09cde295 100644 --- a/tests/renderer/VertexArray.test.cpp +++ b/tests/renderer/VertexArray.test.cpp @@ -17,8 +17,8 @@ #include #include -#include "opengl/OpenGlVertexArray.hpp" -#include "opengl/OpenGlBuffer.hpp" +#include "GraphicsBackends/opengl/OpenGlVertexArray.hpp" +#include "GraphicsBackends/opengl/OpenGlBuffer.hpp" #include "contexts/opengl.hpp" #include "RendererExceptions.hpp" diff --git a/tests/test_main.cpp b/tests/test_main.cpp index 76f841f1..d5b6e7b0 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -1,6 +1,15 @@ #include +// Init tests +// Use this main file instead of GTest::gtest_main in linking +// See: https://github.com/google/googletest/issues/2157 int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); + const int rv = RUN_ALL_TESTS(); + GTEST_LOG_(INFO) << "Ran " << ::testing::UnitTest::GetInstance()->test_to_run_count() << " tests"; + if (::testing::UnitTest::GetInstance()->test_to_run_count() == 0) { + GTEST_LOG_(FATAL) << "No tests were run. This might be due to a wrong linking configuration"; + return 1; + } + return rv; } diff --git a/vcpkg.json b/vcpkg.json index c025acb5..e3b31d86 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -17,6 +17,7 @@ {"name": "imguizmo", "version>=": "2024-05-29#1"}, {"name": "boost-preprocessor", "version>=": "1.86.0#0"}, {"name": "boost-dll", "version>=": "1.86.0#0"}, + {"name": "boost-uuid", "version>=": "1.86.0#0"}, {"name": "joltphysics", "version>=": "5.1.0#2"}, {"name": "loguru", "version>=": "2.1.0#4"}, {"name": "glm", "version>=": "1.0.1#3"},