diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml deleted file mode 100644 index 0183294..0000000 --- a/.github/workflows/CI.yml +++ /dev/null @@ -1,76 +0,0 @@ -name: Continuous Integration -run-name: Multi platform build for ${{ github.ref }} by ${{ github.actor }} -on: - pull_request: - workflow_dispatch: - -jobs: - Build-and-Test: - strategy: - fail-fast: false - matrix: - os: [ macos-latest, ubuntu-latest, windows-latest ] - compiler: [ clang, gcc, cl ] - - exclude: - - os: macos-latest - compiler: cl - - os: macos-latest - compiler: gcc - - os: ubuntu-latest - compiler: cl - - os: windows-latest - compiler: clang - - os: windows-latest - compiler: gcc - - include: - - os: macos-latest - compiler: clang - c_compiler: clang - cpp_compiler: clang++ - - - os: ubuntu-latest - compiler: clang - c_compiler: clang-18 - cpp_compiler: clang++-18 - - - os: ubuntu-latest - compiler: gcc - c_compiler: gcc-14 - cpp_compiler: g++-14 - - - os: windows-latest - c_compiler: cl - cpp_compiler: cl - - - runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@v4 - - - name: Install dependencies - if: ${{ matrix.os == 'ubuntu-latest' }} - shell: bash - run: | - sudo apt update - sudo apt install -y xorg-dev - - - name: Configure CMake - run: > - cmake -B build -S ${{ github.workspace }} - -DCMAKE_BUILD_TYPE=Release - -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} - -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} - -DGE_BUILD_TESTS=ON - -DGE_BUILD_EXAMPLES=ON - - - name: Build - run: cmake --build build --config Release - - - name: Test - working-directory: build - run: ctest -j 8 --output-on-failure - env: - GTEST_COLOR: 1 - diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml new file mode 100644 index 0000000..58042fc --- /dev/null +++ b/.github/workflows/linux.yml @@ -0,0 +1,39 @@ +name: Build Linux +run-name: ${{ github.event.pull_request && format('{0} -> {1} by {2} (PR {3})', github.head_ref, github.base_ref, github.actor, github.event.pull_request.number) || github.event_name == 'workflow_dispatch' && format('{0} by {1} [manual]', github.ref_name, github.actor) || format('{0} by {1}', github.ref_name, github.actor) }} + +on: + pull_request: + workflow_dispatch: + +jobs: + build: + strategy: + fail-fast: false + matrix: + build-type: [ Debug, Release ] + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y gcc-14 g++-14 xorg-dev + + - name: Generate + run: > + cmake -S ${{ github.workspace }} -B build + -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} + -DCMAKE_C_COMPILER=gcc-14 + -DCMAKE_CXX_COMPILER=g++-14 + -DGE_BUILD_VULKAN=ON + -DGE_BUILD_TESTS=ON + -DGE_BUILD_EXAMPLES=ON + + - name: Build + run: cmake --build build --config ${{ matrix.build-type }} --parallel + + - name: Test + run: ctest --test-dir build -C ${{ matrix.build-type }} --output-on-failure diff --git a/.github/workflows/macos-metal.yml b/.github/workflows/macos-metal.yml new file mode 100644 index 0000000..75ad9bf --- /dev/null +++ b/.github/workflows/macos-metal.yml @@ -0,0 +1,33 @@ +name: Build macOS - Metal +run-name: ${{ github.event.pull_request && format('{0} -> {1} by {2} (PR {3})', github.head_ref, github.base_ref, github.actor, github.event.pull_request.number) || github.event_name == 'workflow_dispatch' && format('{0} by {1} [manual]', github.ref_name, github.actor) || format('{0} by {1}', github.ref_name, github.actor) }} + +on: + pull_request: + workflow_dispatch: + +jobs: + build: + strategy: + fail-fast: false + matrix: + build-type: [ Debug, Release ] + + runs-on: macos-latest + + steps: + - uses: actions/checkout@v4 + + - name: Generate + run: > + cmake -S ${{ github.workspace }} -B build + -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} + -DGE_BUILD_METAL=ON + -DGE_BUILD_VULKAN=OFF + -DGE_BUILD_TESTS=ON + -DGE_BUILD_EXAMPLES=ON + + - name: Build + run: cmake --build build --config ${{ matrix.build-type }} --parallel + + - name: Test + run: ctest --test-dir build -C ${{ matrix.build-type }} --output-on-failure diff --git a/.github/workflows/macos-vulkan.yml b/.github/workflows/macos-vulkan.yml new file mode 100644 index 0000000..cfce2db --- /dev/null +++ b/.github/workflows/macos-vulkan.yml @@ -0,0 +1,33 @@ +name: Build macOS - Vulkan +run-name: ${{ github.event.pull_request && format('{0} -> {1} by {2} (PR {3})', github.head_ref, github.base_ref, github.actor, github.event.pull_request.number) || github.event_name == 'workflow_dispatch' && format('{0} by {1} [manual]', github.ref_name, github.actor) || format('{0} by {1}', github.ref_name, github.actor) }} + +on: + pull_request: + workflow_dispatch: + +jobs: + build: + strategy: + fail-fast: false + matrix: + build-type: [ Debug, Release ] + + runs-on: macos-latest + + steps: + - uses: actions/checkout@v4 + + - name: Generate + run: > + cmake -S ${{ github.workspace }} -B build + -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} + -DGE_BUILD_METAL=OFF + -DGE_BUILD_VULKAN=ON + -DGE_BUILD_TESTS=ON + -DGE_BUILD_EXAMPLES=ON + + - name: Build + run: cmake --build build --config ${{ matrix.build-type }} --parallel + + - name: Test + run: ctest --test-dir build -C ${{ matrix.build-type }} --output-on-failure diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml new file mode 100644 index 0000000..6f0b579 --- /dev/null +++ b/.github/workflows/macos.yml @@ -0,0 +1,33 @@ +name: Build macOS +run-name: ${{ github.event.pull_request && format('{0} -> {1} by {2} (PR {3})', github.head_ref, github.base_ref, github.actor, github.event.pull_request.number) || github.event_name == 'workflow_dispatch' && format('{0} by {1} [manual]', github.ref_name, github.actor) || format('{0} by {1}', github.ref_name, github.actor) }} + +on: + pull_request: + workflow_dispatch: + +jobs: + build: + strategy: + fail-fast: false + matrix: + build-type: [ Debug, Release ] + + runs-on: macos-latest + + steps: + - uses: actions/checkout@v4 + + - name: Generate + run: > + cmake -S ${{ github.workspace }} -B build + -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} + -DGE_BUILD_METAL=ON + -DGE_BUILD_VULKAN=ON + -DGE_BUILD_TESTS=ON + -DGE_BUILD_EXAMPLES=ON + + - name: Build + run: cmake --build build --config ${{ matrix.build-type }} --parallel + + - name: Test + run: ctest --test-dir build -C ${{ matrix.build-type }} --output-on-failure diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml new file mode 100644 index 0000000..8a7331f --- /dev/null +++ b/.github/workflows/windows.yml @@ -0,0 +1,32 @@ +name: Build Windows +run-name: ${{ github.event.pull_request && format('{0} -> {1} by {2} (PR {3})', github.head_ref, github.base_ref, github.actor, github.event.pull_request.number) || github.event_name == 'workflow_dispatch' && format('{0} by {1} [manual]', github.ref_name, github.actor) || format('{0} by {1}', github.ref_name, github.actor) }} + +on: + pull_request: + workflow_dispatch: + +jobs: + build: + strategy: + fail-fast: false + matrix: + build-type: [ Debug, Release ] + + runs-on: windows-latest + + steps: + - uses: actions/checkout@v4 + + - name: Generate + run: > + cmake -S ${{ github.workspace }} -B build + -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} + -DGE_BUILD_VULKAN=ON + -DGE_BUILD_TESTS=ON + -DGE_BUILD_EXAMPLES=${{ matrix.build-examples }} + + - name: Build + run: cmake --build build --config ${{ matrix.build-type }} --parallel + + - name: Test + run: ctest --test-dir build -C ${{ matrix.build-type }} --output-on-failure diff --git a/.gitignore b/.gitignore index 4eb330f..6d6a193 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ default.profraw graph.drawio.svg **/.DS_Store examples/project1/project1_scriptLib* +__cmake_systeminformation diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 4c31544..0000000 --- a/.gitmodules +++ /dev/null @@ -1,9 +0,0 @@ -[submodule "dependencies/UtilsCPP"] - path = dependencies/UtilsCPP - url = https://github.com/Thomas-Chqt/UtilsCPP -[submodule "dependencies/Math"] - path = dependencies/Math - url = https://github.com/Thomas-Chqt/Math -[submodule "dependencies/Graphics"] - path = dependencies/Graphics - url = https://github.com/Thomas-Chqt/Graphics diff --git a/CMakeLists.txt b/CMakeLists.txt index 4a9e94e..c13d01b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,32 +7,60 @@ cmake_minimum_required(VERSION 3.22) -include(FetchContent) +include(cmake/FetchDependencies.cmake) -enable_testing() set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) -set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build using shared libraries" FORCE) +project(Game-Engine) -option(GE_BUILD_TESTS "Build test executable" OFF) -option(GE_BUILD_EXAMPLES "Build example scripts" OFF) +option(BUILD_SHARED_LIBS "Build using shared libraries" OFF) if (APPLE) - option(GE_BUILD_METAL "Build with metal enable" ON) - option(GE_BUILD_OPENGL "Build with OpenGL enable" ON) -else() - option(GE_BUILD_OPENGL "Build with OpenGL enable" ON) + option(GE_BUILD_METAL "Build the metal backend" ON) endif() +option(GE_BUILD_VULKAN "Build the vulkan backend" ON) +option(GE_BUILD_TESTS "Build test executable" OFF) +option(GE_BUILD_EXAMPLES "Build examples" OFF) +option(GE_INSTALL "Enable the install command" ON) + +enable_language(CXX) + +fetch_dependencies() -if (NOT GE_BUILD_METAL AND NOT GE_BUILD_OPENGL) - message(FATAL_ERROR "One graphic api must be enable") +add_library(Game-Engine ${GE_LIB_TYPE}) + +set_target_properties(Game-Engine PROPERTIES + CXX_VISIBILITY_PRESET hidden + VISIBILITY_INLINES_HIDDEN YES +) + +target_compile_features(Game-Engine PUBLIC cxx_std_23) +target_compile_definitions(Game-Engine PRIVATE GE_DLL_EXPORT) +target_compile_definitions(Game-Engine PUBLIC "GE_SHARED_LIBRARY_EXTENSION=\"${CMAKE_SHARED_LIBRARY_SUFFIX}\"") + +if(MSVC) + target_compile_options(Game-Engine PRIVATE /W4 /wd4251) +else() + target_compile_options(Game-Engine PRIVATE -Wall -Wextra -Wpedantic) + target_compile_options(Game-Engine PRIVATE $<$:-Wno-missing-field-initializers>) endif() -project(Game-Engine) -enable_language(CXX) +file(GLOB_RECURSE GE_SRC "include/*.hpp" "include/*.inl" "src/*.cpp" "src/*.hpp") +target_sources(Game-Engine PRIVATE ${GE_SRC} ${GFX_INC}) + +target_include_directories(Game-Engine + PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" + PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src" "${CMAKE_CURRENT_SOURCE_DIR}") + +# target_precompile_headers(Graphics PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/pch.hpp") + +target_link_libraries(Game-Engine PUBLIC Graphics glfw glm::glm imgui stb_image dlLoad assimp::assimp yaml-cpp) + +add_subdirectory("shaders") + +add_dependencies(Game-Engine flat_color_shader) +target_compile_definitions(Game-Engine PRIVATE "SHADER_DIR=\"${SHADER_DIR}\"") -add_subdirectory("dependencies") -add_subdirectory("engine") add_subdirectory("editor") if(GE_BUILD_EXAMPLES) @@ -40,5 +68,6 @@ if(GE_BUILD_EXAMPLES) endif() if (GE_BUILD_TESTS) + enable_testing() # need to be in the top level cmakelists add_subdirectory("tests") -endif() \ No newline at end of file +endif() diff --git a/README.md b/README.md index b1fe60b..2ba2d8f 100644 --- a/README.md +++ b/README.md @@ -1,90 +1,85 @@ -> This project is currently undergoing refactoring (refer to the [refactor branch](https://github.com/Thomas-Chqt/Game-Engine/tree/refactor)) to incorporate the latest version of the graphics library and adopt more modern C++ features. +# Game-Engine -Game-Engine -=========== +`Game-Engine` is a small C++23 game engine and editor built as a learning project around rendering, tooling, and runtime architecture. It includes a reusable engine library, a desktop editor, YAML-based project and scene serialization, and hot-reloadable C++ gameplay scripts. -**Game-Engine** is an ongoing project focused on building a simple, low-level game engine. The primary goal is to deepen my understanding of game development by creating a versatile set of tools that can be used to build various types of games and 3D applications. +![viewport](viewport.png) -Current Features ----------------- +## Current Features -- **Basic Renderer**: A simple rendering pipeline for drawing objects on the screen. -- **Entity Component System (ECS)**: A flexible system for composing game objects with various components. -- **Editor**: A built-in editor linked to the engine for editing, saving, and running projects. -- **External Scripting**: Projects can include scripts that are loaded at runtime through shared libraries, allowing scripts to be rebuilt and reloaded without closing the editor. +- Frame-graph-based renderer +- Metal backend on macOS and Vulkan backend where enabled +- Entity Component System +- Scene hierarchy +- ImGui-based editor with viewport, scene graph, inspector, project properties, and resource management panels +- Runtime game mode inside the editor +- Hot-reloadable gameplay scripts written in C++ +- Reflected script fields exposed to the editor and serialization +- YAML project, scene, input, and component serialization +- Input system +- Mesh importing with `assimp` +- Texture loading with `stb_image` +- Automated tests for core engine systems -### Example Project +## Example Project -A basic example project is included to demonstrate the engine’s current capabilities. It consists of four entities: +The repository includes `examples/project1`, a sample project that demonstrates the current engine and editor workflow. It contains: -1. **Player**: An entity with a mesh component and a script enabling player movement. -2. **Camera**: A camera component attached as a child of the player, ensuring it moves along with the player. -3. **Chess Set**: A static entity with a mesh component. -4. **Light**: A point light component illuminating the scene. +- A chess-set scene with imported meshes, textures, lighting, and camera setup +- A set of inputs and there mappers +- A Player controller script attached to the player entity -To run the game, use the **Project > Run** menu option. +It can be opened from the project root with `./path/to/editor examples/project1/project1.geproj`.

- - -

+ + +

-Support ------- +## Build -| Platform | Supported | Graphics API | -|----------|-----------|----------------| -| Windows | Yes | OpenGL | -| Linux | Yes | OpenGL | -| macOS | Yes | Metal / OpenGL | +### Supported Platforms -Building the Project --------------------- +| Platform | Metal | Vulkan | +|----------|-------|--------| +| macOS | [![macOS-Metal](https://github.com/Thomas-Chqt/Game-Engine/actions/workflows/macos-metal.yml/badge.svg)](https://github.com/Thomas-Chqt/Game-Engine/actions/workflows/macos-metal.yml) | [![macOS-Vulkan](https://github.com/Thomas-Chqt/Game-Engine/actions/workflows/macos-vulkan.yml/badge.svg)](https://github.com/Thomas-Chqt/Game-Engine/actions/workflows/macos-vulkan.yml) | +| Windows | N/A | [![Windows](https://github.com/Thomas-Chqt/Game-Engine/actions/workflows/windows.yml/badge.svg)](https://github.com/Thomas-Chqt/Game-Engine/actions/workflows/windows.yml) | +| Linux | N/A | [![Linux](https://github.com/Thomas-Chqt/Game-Engine/actions/workflows/linux.yml/badge.svg)](https://github.com/Thomas-Chqt/Game-Engine/actions/workflows/linux.yml) | -The repository must be **cloned** (instead of downloaded as a ZIP) to correctly resolve submodules when configuring the project with CMake. Recursive cloning is not required, as everything is managed through CMake. +### Requirements -### Build Instructions +- CMake +- A C++23 compiler (out of the box on macos and windows, require gcc-14 on ubuntu) +- see [`Graphics`](https://github.com/Thomas-Chqt/Graphics) for backends requirements + +### Configure and Build ```sh -mkdir build cmake -S . -B build cmake --build build -``` - -All dependencies are embedded in the project using either **Git submodules** or **CMake's FetchContent**, so it should work out of the box. However, if compilation fails, ensure that your system meets the requirements for each dependency. - -### CMake Options - -| Option | Default | Description | -|---------------------|---------|--------------------------------------| -| `GE_BUILD_EXAMPLES` | `OFF` | Build example projects | -| `GE_BUILD_TESTS` | `OFF` | Build unit tests | -| `GE_BUILD_METAL` | `ON` | Build the Metal backend (macOS only) | -| `GE_BUILD_OPENGL` | `ON` | Build the OpenGL backend | - -Libraries ---------- - -This project utilizes a combination of **custom-built** and **open-source** libraries. - -### Custom Libraries - -I’ve developed the following libraries : - -- **[UtilsCPP](https://github.com/Thomas-Chqt/UtilsCPP)** – A utility library with various data structures and algorithms. -- **[Math](https://github.com/Thomas-Chqt/Math)** – A math library providing vector and matrix operations for graphics. -- **[Graphics](https://github.com/Thomas-Chqt/Graphics)** – An abstraction layer over multiple graphics APIs, enabling cross-platform compatibility with the most native API per platform. - -### Open-Source Libraries - -The engine also integrates several open-source libraries: - -- **[GLFW](https://github.com/glfw/glfw)** – Handles window creation and input events (used internally by **Graphics**). -- **[GLAD](https://github.com/Thomas-Chqt/GLAD)** – Loads OpenGL functions for cross-platform rendering (used internally by **Graphics**). -- **[assimp](https://github.com/assimp/assimp)** – Imports 3D models and meshes for asset loading. -- **[stb_image](https://github.com/Thomas-Chqt/stb_image)** – A lightweight image-loading library for textures. -- **[imgui](https://github.com/Thomas-Chqt/imgui)** – A UI framework for building efficient in-engine interfaces. -- **[tinyfiledialogs](http://tinyfiledialogs.sourceforge.net)** – Provides a native file picker for all platforms. -- **[nlohmann/json](https://github.com/nlohmann/json)** – Parses and writes JSON files. - -Additionally, unit tests are built using **[GoogleTest](https://github.com/google/googletest)**. +``` + +This builds the `Game-Engine` library, the `GE-Editor` application, and the required shader compilation targets. +To build the example project, add the `-DGE_BUILD_EXAMPLES=ON` flag + +### Useful CMake Options + +| Option | Default | Description | +| ------------------- | ------------- | ----------------------------------------------------------- | +| `BUILD_SHARED_LIBS` | `OFF` | Build libraries as shared instead of static where supported | +| `GE_BUILD_METAL` | `ON` on macOS | Enable the Metal backend | +| `GE_BUILD_VULKAN` | `ON` | Enable the Vulkan backend | +| `GE_BUILD_TESTS` | `OFF` | Build the test target | +| `GE_BUILD_EXAMPLES` | `OFF` | Build the example project script library | +| `GE_INSTALL` | `ON` | Enable install rules in dependencies that support them | + +## Libraries Used + +- [`Graphics`](https://github.com/Thomas-Chqt/Graphics): rendering abstraction layer and shader toolchain +- [`GLFW`](https://github.com/glfw/glfw): windowing and input +- [`GLM`](https://github.com/g-truc/glm): math types and transforms +- [`imgui`](https://github.com/Thomas-Chqt/imgui): editor UI +- [`stb_image`](https://github.com/Thomas-Chqt/stb_image): image loading +- [`assimp`](https://github.com/assimp/assimp): mesh import +- [`yaml-cpp`](https://github.com/jbeder/yaml-cpp): YAML serialization +- [`dlLoad`](https://github.com/Thomas-Chqt/dlLoad): dynamic library loading for scripts +- [`GoogleTest`](https://github.com/google/googletest): unit tests diff --git a/cmake/FetchDependencies.cmake b/cmake/FetchDependencies.cmake new file mode 100644 index 0000000..ec8ada8 --- /dev/null +++ b/cmake/FetchDependencies.cmake @@ -0,0 +1,178 @@ +# --------------------------------------------------- +# FetchDependencies.cmake +# +# Centralizes FetchContent-based dependencies for the Game-Engine project. +# Call `fetch_dependencies()` from the root `CMakeLists.txt`. +# --------------------------------------------------- + +include_guard(GLOBAL) + +include(FetchContent) + +function(fetch_dependencies) + set(FETCHCONTENT_QUIET OFF) + set(ENV{GIT_LFS_SKIP_SMUDGE} 1) + + # ----------------------------- + # Graphics + # ----------------------------- + FetchContent_Declare(Graphics + GIT_REPOSITORY https://github.com/Thomas-Chqt/Graphics.git + GIT_TAG c85bcc401cdeaa461458ac1078ca441ad200b65f + GIT_SHALLOW 1 + GIT_PROGRESS TRUE + FIND_PACKAGE_ARGS + ) + set(GFX_BUILD_METAL ${GE_BUILD_METAL}) + set(GFX_BUILD_VULKAN ${GE_BUILD_VULKAN}) + set(GFX_ENABLE_IMGUI ON) + set(GFX_ENABLE_GLFW ON) + set(GFX_BUILD_EXAMPLES OFF) + set(GFX_BUILD_TESTS OFF) + set(GFX_BUILD_TRACY OFF) + set(GFX_INSTALL ${GE_INSTALL}) + FetchContent_MakeAvailable(Graphics) + set_target_properties(Graphics PROPERTIES FOLDER "dependencies/Graphics") + set_target_properties(gfxsc PROPERTIES FOLDER "dependencies/Graphics") + + # ----------------------------- + # GLFW + # ----------------------------- + FetchContent_Declare(glfw3 + GIT_REPOSITORY https://github.com/glfw/glfw.git + GIT_TAG 3.4 + GIT_SHALLOW 1 + GIT_PROGRESS TRUE + FIND_PACKAGE_ARGS + ) + set(GLFW_BUILD_TESTS OFF) + set(GLFW_BUILD_DOCS OFF) + set(GLFW_INSTALL OFF) + set(GLFW_BUILD_EXAMPLES OFF) + if(NOT GLFW_BUILD_WAYLAND) + set(GLFW_BUILD_WAYLAND OFF) + endif() + if (APPLE) + enable_language(OBJC) + endif() + FetchContent_MakeAvailable(glfw3) + if (glfw3_SOURCE_DIR) + if (TARGET update_mappings) + set_target_properties(glfw PROPERTIES FOLDER "dependencies/GLFW3") + set_target_properties(update_mappings PROPERTIES FOLDER "dependencies/GLFW3") + else() + set_target_properties(glfw PROPERTIES FOLDER "dependencies") + endif() + endif() + + # ----------------------------- + # GLM + # ----------------------------- + FetchContent_Declare(glm + GIT_REPOSITORY https://github.com/g-truc/glm.git + GIT_TAG 1.0.1 + GIT_SHALLOW 1 + GIT_PROGRESS TRUE + FIND_PACKAGE_ARGS + ) + FetchContent_MakeAvailable(glm) + if (glm_SOURCE_DIR) + set_target_properties(glm PROPERTIES FOLDER "dependencies") + endif() + + # ----------------------------- + # ImGui + # ----------------------------- + FetchContent_Declare(imgui + GIT_REPOSITORY https://github.com/Thomas-Chqt/imgui.git + GIT_TAG 4d8f55183c2e974916daf6336ee2c4b9c8e72891 + GIT_SHALLOW 1 + GIT_PROGRESS TRUE + FIND_PACKAGE_ARGS + ) + set(IM_BUILD_GLFW ON) + FetchContent_MakeAvailable(imgui) + target_compile_definitions(imgui PRIVATE "GLFW_INCLUDE_NONE") + target_link_libraries(imgui PUBLIC glfw) + if (imgui_SOURCE_DIR) + set_target_properties(imgui PROPERTIES FOLDER "dependencies") + endif() + + # ----------------------------- + # stb_image + # ----------------------------- + FetchContent_Declare(stb_image + GIT_REPOSITORY https://github.com/Thomas-Chqt/stb_image.git + GIT_TAG a09b799a2ba95c14f511493db4ea59811b2fcc97 + GIT_SHALLOW 1 + GIT_PROGRESS TRUE + FIND_PACKAGE_ARGS + ) + FetchContent_MakeAvailable(stb_image) + if (stb_image_SOURCE_DIR) + set_target_properties(stb_image PROPERTIES FOLDER "dependencies") + endif() + + # ----------------------------- + # dlLoad + # ----------------------------- + FetchContent_Declare(dlLoad + GIT_REPOSITORY https://github.com/Thomas-Chqt/dlLoad.git + GIT_TAG fd860b8200983b89d3ca58b371f6ec25b9972bd3 + GIT_SHALLOW 1 + GIT_PROGRESS TRUE + FIND_PACKAGE_ARGS + ) + set(DL_BUILD_TESTS OFF) + set(DL_INSTALL ${GE_INSTALL}) + FetchContent_MakeAvailable(dlLoad) + set_target_properties(dlLoad PROPERTIES FOLDER "dependencies") + + # ----------------------------- + # assimp + # ----------------------------- + FetchContent_Declare(assimp + GIT_REPOSITORY https://github.com/assimp/assimp.git + GIT_TAG v5.4.1 + GIT_SHALLOW 1 + GIT_PROGRESS TRUE + FIND_PACKAGE_ARGS + ) + set(ASSIMP_BUILD_TESTS OFF) + set(ASSIMP_INSTALL OFF) + set(ASSIMP_NO_EXPORT ON) + set(ASSIMP_BUILD_ALL_IMPORTERS_BY_DEFAULT OFF) + set(ASSIMP_BUILD_OBJ_IMPORTER ON) + set(ASSIMP_BUILD_FBX_IMPORTER ON) + set(ASSIMP_BUILD_GLTF_IMPORTER ON) + set(CMAKE_POLICY_DEFAULT_CMP0175 OLD) + FetchContent_MakeAvailable(assimp) + unset(CMAKE_POLICY_DEFAULT_CMP0175) + if (assimp_SOURCE_DIR) + if(TARGET zlibstatic OR TARGET UpdateAssimpLibsDebugSymbolsAndDLLs) + set_target_properties(assimp PROPERTIES FOLDER "dependencies/assimp") + if(TARGET zlibstatic) + set_target_properties(zlibstatic PROPERTIES FOLDER "dependencies/assimp") + endif() + if(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs) + set_target_properties(UpdateAssimpLibsDebugSymbolsAndDLLs PROPERTIES FOLDER "dependencies/assimp") + endif() + else() + set_target_properties(assimp PROPERTIES FOLDER "dependencies") + endif() + endif() + + # ----------------------------- + # yaml-cpp + # ----------------------------- + FetchContent_Declare(yaml-cpp + GIT_REPOSITORY https://github.com/jbeder/yaml-cpp.git + GIT_TAG yaml-cpp-0.9.0 + GIT_SHALLOW 1 + GIT_PROGRESS TRUE + FIND_PACKAGE_ARGS + ) + set(YAML_CPP_BUILD_TOOLS OFF) + FetchContent_MakeAvailable(yaml-cpp) + set_target_properties(yaml-cpp PROPERTIES FOLDER "dependencies") +endfunction() diff --git a/cmake/ScriptLibrary.cmake b/cmake/ScriptLibrary.cmake new file mode 100644 index 0000000..c1bf08b --- /dev/null +++ b/cmake/ScriptLibrary.cmake @@ -0,0 +1,69 @@ +# --------------------------------------------------- +# ScriptLibrary.cmake +# +# Author: Thomas Choquet +# --------------------------------------------------- +# Script library helper for user project shared libraries. + +function(ge_add_script_library target_name) + set(export_source "${CMAKE_CURRENT_BINARY_DIR}/${target_name}_ScriptSharedLibraryExports.cpp") + file(WRITE "${export_source}" + [=[ + #include "Game-Engine/Script.hpp" + + #if defined(_WIN32) + #define GE_SCRIPT_LIBRARY_EXPORT __declspec(dllexport) + #else + #define GE_SCRIPT_LIBRARY_EXPORT __attribute__((visibility("default"))) + #endif + + extern "C" + { + + GE_SCRIPT_LIBRARY_EXPORT void listScriptNames(const char*** names, size_t* count) + { + return GE::ScriptRegistry::instance().listScriptNames(names, count); + } + + GE_SCRIPT_LIBRARY_EXPORT void listScriptParameterNames(const char* scriptName, const char*** names, size_t* count) + { + return GE::ScriptRegistry::instance().listScriptParameterNames(scriptName, names, count); + } + + GE_SCRIPT_LIBRARY_EXPORT const char* getScriptParameterTypeName(const char* scriptName, const char* parameterName) + { + return GE::ScriptRegistry::instance().getScriptParameterTypeName(scriptName, parameterName); + } + + GE_SCRIPT_LIBRARY_EXPORT void getScriptDefaultParameterValue(const char* scriptName, const char* parameterName, void* data) + { + return GE::ScriptRegistry::instance().getScriptDefaultParameterValue(scriptName, parameterName, data); + } + + GE_SCRIPT_LIBRARY_EXPORT GE::Script* makeScriptInstance(const char* name) + { + return GE::ScriptRegistry::instance().makeScriptInstance(name); + } + + GE_SCRIPT_LIBRARY_EXPORT void destroyScriptInstance(GE::Script* instance) + { + return GE::ScriptRegistry::instance().destroyScriptInstance(instance); + } + + GE_SCRIPT_LIBRARY_EXPORT void setScriptParameter(const char* scriptName, const char* parameterName, GE::Script* instance, const void* data) + { + return GE::ScriptRegistry::instance().setScriptParameter(scriptName, parameterName, instance, data); + } + + } + ]=] + ) + + add_library(${target_name} SHARED ${ARGN}) + target_sources(${target_name} PRIVATE "${export_source}") + target_compile_features(${target_name} PRIVATE cxx_std_23) + + if(APPLE) + target_link_options(${target_name} PRIVATE "-undefined" "dynamic_lookup") + endif() +endfunction() diff --git a/dependencies/CMakeLists.txt b/dependencies/CMakeLists.txt deleted file mode 100644 index 42d8243..0000000 --- a/dependencies/CMakeLists.txt +++ /dev/null @@ -1,110 +0,0 @@ -# --------------------------------------------------- -# CMakeLists.txt -# -# Author: Thomas Choquet -# Date: 2024/06/20 16:24:26 -# --------------------------------------------------- - -if(NOT TARGET UtilsCPP) - find_program(GIT_PATH git REQUIRED) - execute_process(COMMAND ${GIT_PATH} submodule update --init UtilsCPP WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) - set(UTILSCPP_BUILD_TESTS OFF) - set(UTILSCPP_API_EXPORT ON) - add_subdirectory(UtilsCPP) -endif() -set_target_properties(UtilsCPP PROPERTIES FOLDER "dependencies") - -if(NOT TARGET Math) - find_program(GIT_PATH git REQUIRED) - execute_process(COMMAND ${GIT_PATH} submodule update --init Math WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) - set(MATH_BUILD_TESTS OFF) - set(MATH_API_EXPORT ON) - add_subdirectory(Math) -endif() -set_target_properties(Math PROPERTIES FOLDER "dependencies") - -if(NOT TARGET Graphics) - find_program(GIT_PATH git REQUIRED) - execute_process(COMMAND ${GIT_PATH} submodule update --init Graphics WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) - set(GFX_BUILD_IMGUI ON) - set(GFX_BUILD_EXEMPLES OFF) - set(GFX_BUILD_METAL ${GE_BUILD_METAL}) - set(GFX_BUILD_OPENGL ${GE_BUILD_OPENGL}) - add_subdirectory(Graphics) -endif() -set_target_properties(Graphics PROPERTIES FOLDER "dependencies") - -FetchContent_Declare(assimp - GIT_REPOSITORY https://github.com/assimp/assimp.git - GIT_TAG v5.4.1 - GIT_SHALLOW 1 -) -set(ASSIMP_BUILD_TESTS OFF) -set(ASSIMP_INSTALL OFF) -set(ASSIMP_NO_EXPORT ON) -set(ASSIMP_BUILD_ALL_IMPORTERS_BY_DEFAULT OFF) -set(ASSIMP_BUILD_OBJ_IMPORTER ON) -set(ASSIMP_BUILD_FBX_IMPORTER ON) -set(ASSIMP_BUILD_GLTF_IMPORTER ON) -set(CMAKE_POLICY_DEFAULT_CMP0175 OLD) -FetchContent_MakeAvailable(assimp) -unset(CMAKE_POLICY_DEFAULT_CMP0175) -if(TARGET zlibstatic OR TARGET UpdateAssimpLibsDebugSymbolsAndDLLs) - set_target_properties(assimp PROPERTIES FOLDER "dependencies/assimp") - if(TARGET zlibstatic) - set_target_properties(zlibstatic PROPERTIES FOLDER "dependencies/assimp") - endif() - if(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs) - set_target_properties(UpdateAssimpLibsDebugSymbolsAndDLLs PROPERTIES FOLDER "dependencies/assimp") - endif() -else() - set_target_properties(assimp PROPERTIES FOLDER "dependencies") -endif() - - -FetchContent_Declare(json - GIT_REPOSITORY https://github.com/nlohmann/json.git - GIT_TAG v3.11.3 - GIT_SHALLOW 1 -) -set(JSON_BuildTests OFF) -set(JSON_Install OFF) -FetchContent_MakeAvailable(json) - -FetchContent_Declare(tinyfiledialogs - GIT_REPOSITORY https://github.com/Thomas-Chqt/tinyfiledialogs.git - GIT_TAG 0806552c78bf70658628e0ca4e3b962ca163ac70 -) -set(TFD_BUILD_EXAMPLES OFF) -set(TFD_INSTALL OFF) -FetchContent_MakeAvailable(tinyfiledialogs) -set_target_properties(tinyfiledialogs PROPERTIES FOLDER "dependencies") - -FetchContent_Declare(stduuid - GIT_REPOSITORY https://github.com/mariusbancila/stduuid.git - GIT_TAG v1.2.3 - GIT_SHALLOW 1 -) -set(UUID_BUILD_TESTS OFF) -set(UUID_SYSTEM_GENERATOR OFF) -set(UUID_TIME_GENERATOR OFF) -set(UUID_USING_CXX20_SPAN ON) -set(UUID_ENABLE_INSTALL OFF) -FetchContent_MakeAvailable(stduuid) -set_target_properties(stduuid PROPERTIES FOLDER "dependencies") - -FetchContent_Declare(stb_image - GIT_REPOSITORY https://github.com/Thomas-Chqt/stb_image.git - GIT_SHALLOW 1 -) -FetchContent_MakeAvailable(stb_image) -set_target_properties(stb_image PROPERTIES FOLDER "dependencies") - -FetchContent_Declare(dlLoad - GIT_REPOSITORY https://github.com/Thomas-Chqt/dlLoad.git - GIT_SHALLOW 1 -) -set(DL_BUILD_TESTS OFF) -set(DL_INSTALL OFF) -FetchContent_MakeAvailable(dlLoad) -set_target_properties(dlLoad PROPERTIES FOLDER "dependencies") \ No newline at end of file diff --git a/dependencies/Graphics b/dependencies/Graphics deleted file mode 160000 index fc08a81..0000000 --- a/dependencies/Graphics +++ /dev/null @@ -1 +0,0 @@ -Subproject commit fc08a8196a5d9669e4459161b4feabd0417adb9d diff --git a/dependencies/Math b/dependencies/Math deleted file mode 160000 index af70345..0000000 --- a/dependencies/Math +++ /dev/null @@ -1 +0,0 @@ -Subproject commit af70345dc20df20ca42276dfc6ec36e1433d8f42 diff --git a/dependencies/UtilsCPP b/dependencies/UtilsCPP deleted file mode 160000 index 088b32c..0000000 --- a/dependencies/UtilsCPP +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 088b32ca55a5a1e03f3d47b723a2c585370c0482 diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index 403154c..360606d 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -6,21 +6,46 @@ # --------------------------------------------------- add_executable(GE-Editor) -set_target_properties(GE-Editor - PROPERTIES CXX_STANDARD 20 - CXX_STANDARD_REQUIRED ON - ENABLE_EXPORTS ON +set_target_properties(GE-Editor PROPERTIES + ENABLE_EXPORTS ON + CXX_VISIBILITY_PRESET hidden + VISIBILITY_INLINES_HIDDEN YES ) file(GLOB_RECURSE GE_EDITOR_SRC "*.cpp" "*.hpp") target_sources(GE-Editor PRIVATE ${GE_EDITOR_SRC}) +if(MSVC) + target_sources(GE-Editor PRIVATE "ge-editor.def") +endif() target_include_directories(GE-Editor PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) -target_compile_definitions(GE-Editor PRIVATE "RESSOURCES_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}/ressources\"") -target_compile_definitions(GE-Editor INTERFACE "UTILSCPP_API_IMPORT" "MATH_API_IMPORT" "GAME_ENGINE_API_IMPORT") +target_compile_definitions(GE-Editor PRIVATE "RESOURCE_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}/resources\"") target_link_libraries(GE-Editor PRIVATE Game-Engine) target_include_directories(GE-Editor INTERFACE $) -target_link_libraries(GE-Editor PRIVATE tinyfiledialogs) \ No newline at end of file +if(APPLE) + target_link_options(GE-Editor PRIVATE "LINKER:-force_load,$") +elseif(NOT MSVC) + target_link_options(GE-Editor PRIVATE "LINKER:--whole-archive,$,--no-whole-archive") +endif() + +if(APPLE AND NOT CMAKE_GENERATOR STREQUAL "Xcode") + set(CODESIGN_IDENTITY "-" CACHE STRING "Codesigning identity") + + set(GE_EDITOR_ENTITLEMENTS_FILE "${CMAKE_CURRENT_SOURCE_DIR}/ge-editor.entitlements") + if(EXISTS "${GE_EDITOR_ENTITLEMENTS_FILE}") + add_custom_command(TARGET GE-Editor POST_BUILD + COMMAND /bin/sh -c "codesign --sign \"${CODESIGN_IDENTITY}\" --force --entitlements \"${GE_EDITOR_ENTITLEMENTS_FILE}\" \"$\" >/dev/null 2>&1" + COMMENT "Codesigning GE-Editor (with entitlements) [identity: ${CODESIGN_IDENTITY}]" + VERBATIM + ) + else() + add_custom_command(TARGET GE-Editor POST_BUILD + COMMAND /bin/sh -c "codesign --sign \"${CODESIGN_IDENTITY}\" --force \"$\" >/dev/null 2>&1" + COMMENT "Codesigning GE-Editor [identity: ${CODESIGN_IDENTITY}]" + VERBATIM + ) + endif() +endif() diff --git a/editor/Editor.cpp b/editor/Editor.cpp index 7bfa1ea..a4bfb40 100644 --- a/editor/Editor.cpp +++ b/editor/Editor.cpp @@ -8,348 +8,256 @@ */ #include "Editor.hpp" -#include "ECS/Components.hpp" -#include "ECS/ECSView.hpp" -#include "ECS/Entity.hpp" -#include "EditorCamera.hpp" -#include "Graphics/Event.hpp" -#include "InputManager/RawInput.hpp" -#include "InputManager/Mapper.hpp" #include "Project.hpp" -#include "Renderer/Renderer.hpp" -#include "Scene.hpp" -#include "Script.hpp" -#include "UI/ContentBrowserPanel.hpp" -#include "UI/EntityInspectorPanel.hpp" -#include "UI/FileOpenDialog.hpp" -#include "UI/FileSaveDialog.hpp" -#include "UI/MainMenuBar.hpp" -#include "UI/ProjectPropertiesModal.hpp" -#include "UI/ViewportPanel.hpp" -#include "UI/SceneGraphPanel.hpp" -#include "UtilsCPP/Func.hpp" -#include "UtilsCPP/String.hpp" -#include "UtilsCPP/Types.hpp" -#include "UtilsCPP/UniquePtr.hpp" -#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + #include #include -#include -#include "ViewportFrameBuff.hpp" -#include "imgui.h" -#include -#include +#include +#include +#include +#include +#include -using json = nlohmann::json; -namespace fs = std::filesystem; +std::unique_ptr createApplication(int argc, char* argv[]) +{ + return std::make_unique(argc, argv); +} + +namespace GE_Editor +{ -namespace GE +namespace { -Editor::Editor() : m_vpFrameBuff(window(), renderer().graphicAPI()) +Project loadProjectFile(const std::filesystem::path& path) { - ImGui::GetIO().IniFilename = nullptr; - - Mapper::Descriptor inputMapperDesc; - - ActionInput& quitEditorIpt = m_editorInputContext.newInput("quit_editor"); - quitEditorIpt.callback = utils::Func(*(Application*)this, &Application::terminate); - auto quitEditorIptMapper = utils::makeUnique>(KeyboardButton::esc, quitEditorIpt); - quitEditorIpt.mappers[0] = quitEditorIptMapper.staticCast(); - quitEditorIpt.mappers[1].clear(); - - Range2DInput& editorCamMoveIpt = m_editorInputContext.newInput("editor_cam_move"); - editorCamMoveIpt.callback = utils::Func(m_editorCamera, &EditorCamera::move); - inputMapperDesc.xPos = KeyboardButton::d; - inputMapperDesc.xNeg = KeyboardButton::a; - inputMapperDesc.yPos = KeyboardButton::w; - inputMapperDesc.yNeg = KeyboardButton::s; - auto editorCamMoveIptMapper = utils::makeUnique>(inputMapperDesc, editorCamMoveIpt); - editorCamMoveIpt.mappers[0] = editorCamMoveIptMapper.staticCast(); - editorCamMoveIpt.mappers[1].clear(); - - Range2DInput& editorCamRotateIpt = m_editorInputContext.newInput("editor_cam_rotate"); - editorCamRotateIpt.callback = utils::Func(m_editorCamera, &EditorCamera::rotate); - inputMapperDesc.xPos = KeyboardButton::down; - inputMapperDesc.xNeg = KeyboardButton::up; - inputMapperDesc.yPos = KeyboardButton::right; - inputMapperDesc.yNeg = KeyboardButton::left; - auto editorCamRotateIptMapper = utils::makeUnique>(inputMapperDesc, editorCamRotateIpt); - editorCamRotateIpt.mappers[0] = editorCamRotateIptMapper.staticCast(); - editorCamRotateIpt.mappers[1].clear(); - - newProject(); + Project project; + + std::ifstream file(path); + if (file.is_open() == false) + throw std::runtime_error(std::format("unable to open file : {}", path.string())); + + YAML::Node projectNode = YAML::Load(file); + + if (YAML::convert::decode(projectNode, project) == false) + throw std::runtime_error(std::format("unable to load project file : {}", path.string())); + + return project; } -void Editor::onUpdate() +void makeEditorInputs(GE::InputContext& context) { - if (m_projectNeedReload) - { - reloadProject(); - m_projectNeedReload = false; - } + GE::Range2DInput editorCameraMoveInput; + editorCameraMoveInput.setMapper(GE::InputMapper::Descriptor{ + .xPos = GE::KeyboardButton::d, .xNeg = GE::KeyboardButton::a, .yPos = GE::KeyboardButton::w, .yNeg = GE::KeyboardButton::s, + }); + context.addInput("camera_move", editorCameraMoveInput); + + GE::Range2DInput editorCameraRotationInput; + editorCameraRotationInput.setMapper(GE::InputMapper::Descriptor{ + .xPos = GE::KeyboardButton::up, .xNeg = GE::KeyboardButton::down, .yPos = GE::KeyboardButton::right, .yNeg = GE::KeyboardButton::left, + }); + context.addInput("camera_rotate", editorCameraRotationInput); +} - m_vpFrameBuff.onUpdate(); - processDroppedFiles(); +} - if (m_game && m_game->isRunning()) - { - ECSView(m_game->activeScene().ecsWorld()).onEach([&](Entity entt, ScriptComponent& scriptComponent){ - if (scriptComponent.instance) - scriptComponent.instance->onUpdate(); - }); - } +Editor::Editor(int argc, char* argv[]) + : m_projectFilePath(argc == 2 ? std::filesystem::path(argv[1]) : std::filesystem::path()) + , m_project(m_projectFilePath.empty() ? Project() : loadProjectFile(m_projectFilePath)) + , m_editedScene{m_project.startScene().first, GE::Scene(&assetManager(), m_project.startScene().second)} +{ + makeEditorInputs(m_editorInputContext); - if (m_game && m_game->isRunning() == false) - m_game.clear(); + m_editorInputContext.setInputCallback("camera_move", [editorCamera=&m_editorCamera](const glm::vec2& value) { editorCamera->onMoveInput(value); }) ; + m_editorInputContext.setInputCallback("camera_rotate", [editorCamera=&m_editorCamera](const glm::vec2& value) { editorCamera->onRotationInput(value); }); - if (!m_game) - { - assert(m_editedScene != nullptr); - renderer().beginScene(m_editorCamera.getRendererCam(), m_vpFrameBuff); - { - renderer().addRenderables(m_editedScene->ecsWorld(), m_editedScene->assetManager()); - renderer().addLights(m_editedScene->ecsWorld()); - } - renderer().endScene(); + pushInputContext(&m_editorInputContext); + pushInputContext(&m_imguiInputContext); - addInputContext(&m_editorInputContext); - setDispatchedInputContext(&m_editorInputContext); - } - else + rebuildFrameGraph(); + + ImGui::LoadIniSettingsFromMemory(m_project.imguiSettings().c_str()); + + if (std::filesystem::exists(m_project.scriptLib())) + reloadScriptLib(); +} + +void Editor::onUpdate() +{ + processDropedFiles(); + + if (m_game.has_value()) { - assert(m_game->activeScene().activeCamera()); - assert(m_game->activeScene().activeCamera().has()); - assert(m_game->activeScene().activeCamera().has()); - - Renderer::Camera rendererCamera = { - m_game->activeScene().activeCamera().worldTransform_noScale().inversed(), - m_game->activeScene().activeCamera().get().projectionMatrix() - }; - - renderer().beginScene(rendererCamera, m_vpFrameBuff); + // game update + // TODO ? maybe move into game class + for (auto [scriptComponent] : m_game->activeScene().ecsWorld() | GE::ECSView()) { - renderer().addRenderables(m_game->activeScene().ecsWorld(), m_editedScene->assetManager()); - renderer().addLights(m_game->activeScene().ecsWorld()); + if (scriptComponent.instance) + scriptComponent.instance->onUpdate(); } - renderer().endScene(); - - addInputContext(&m_editorInputContext); - addInputContext(&m_game->inputContext()); - setDispatchedInputContext(&m_game->inputContext()); } -} -void Editor::onImGuiRender() -{ - static bool isProjectPropertiesModalPresented = false; - static bool isFileOpenDialogPresented = false; - static bool isFileSaveDialogPresented = false; - - ImGui::BeginDisabled(isFileOpenDialogPresented || isFileSaveDialogPresented); - - ImGui::DockSpaceOverViewport(); - - MainMenuBar() - .on_File_New(utils::Func(*this, &Editor::newProject)) - .on_File_Open([](){ isFileOpenDialogPresented = true; }) - .on_File_Save(m_projectSavePath.empty() ? [&](){ (void)(isFileSaveDialogPresented = true); } : utils::Func(*this, &Editor::saveProject)) - .on_Project_ReloadScriptLib(!(m_game && m_game->isRunning()) && !m_project.scriptLib().empty() ? utils::Func(*this, &Editor::reloadScriptLib) : utils::Func()) - .on_Project_Properties([](){ isProjectPropertiesModalPresented = true; }) - .on_Scene_Add_EmptyEntity([&](){ m_editedScene->newEntity("new_empty_entity"); }) - .on_Project_Run(!(m_game && m_game->isRunning()) ? utils::Func(*this, &Editor::runGame) : utils::Func()) - .on_Project_Stop((m_game && m_game->isRunning()) ? utils::Func(*this, &Editor::stopGame) : utils::Func()) - .render(); - - ViewportPanel(*static_cast&>(m_vpFrameBuff)->colorTexture()) - .onResize(utils::Func(m_vpFrameBuff, &ViewportFrameBuff::resize)) - .render(); - - SceneGraphPanel(m_editedScene, m_selectedEntity) - .onEntitySelect([&](const Entity& e){ m_selectedEntity = e; }) - .render(); - - EntityInspectorPanel(m_editedScene, m_selectedEntity) - .onEntityDelete([&](){ - m_selectedEntity.destroy(); - m_selectedEntity = Entity(); - }) - .render(); - - ProjectPropertiesModal(isProjectPropertiesModalPresented, m_project, m_projectSavePath) - .onOk(utils::Func(*this, &Editor::reloadScriptLib)) - .render(); - - ContentBrowserPanel(m_project, m_editedScene, m_getScriptNames) - .render(); - - ImGui::EndDisabled(); - - FileOpenDialog("Open project", isFileOpenDialogPresented) - .onSelection(utils::Func(*this, &Editor::openProject)) - .render(); - - FileSaveDialog(m_project.name() + ".geproj", isFileSaveDialogPresented) - .onSelection([&](const fs::path& path){ - m_projectSavePath = path; - saveProject(); - }) - .render(); + renderImgui(); if (ImGui::GetIO().WantSaveIniSettings) { - m_project.saveIniSettingsToMemory(); + m_project.setImguiSettings(ImGui::SaveIniSettingsToMemory()); ImGui::GetIO().WantSaveIniSettings = false; } - - if (ImGui::GetIO().WantCaptureKeyboard) - setDispatchedInputContext(nullptr); } -void Editor::onWindowRequestCloseEvent(gfx::WindowRequestCloseEvent&) +void Editor::onEvent(GE::Event& event) { - terminate(); + if (event.dispatch([&](auto&) { terminate(); })) return; + if (event.dispatch([&](auto&) { rebuildFrameGraph(); })) return; } -void Editor::newProject() +void Editor::loadProject(const std::filesystem::path& path) { - m_projectSavePath = fs::path(); - m_projectNeedReload = true; -} + m_projectFilePath = path; // if we allow file loading error (not terminating the program on load error) + // this will need to go after the yaml parsing + m_project = loadProjectFile(path); -void Editor::openProject(const fs::path& filePath) -{ - assert(fs::is_regular_file(filePath)); - assert(filePath.is_absolute()); - - m_projectSavePath = filePath; - m_projectNeedReload = true; -} + m_editedScene = { + m_project.startScene().first, + GE::Scene(&assetManager(), m_project.startScene().second) + }; -void Editor::reloadProject() -{ - if (m_projectSavePath.empty()) - m_project = Project(); - else - { - assert(fs::is_regular_file(m_projectSavePath)); - assert(m_projectSavePath.is_absolute()); - m_project = json::parse(std::ifstream(m_projectSavePath)); - } + ImGui::LoadIniSettingsFromMemory(m_project.imguiSettings().c_str()); - reloadScriptLib(); + if (std::filesystem::exists(m_project.scriptLib())) + reloadScriptLib(); - m_editedScene = nullptr; - editScene(m_project.startScene()); + m_selectedEntity = {}; + m_editorCamera = {}; +} - m_project.loadIniSettingsFromMemory(); +void Editor::saveEditedScene() +{ + m_project.setScene(m_editedScene.first, m_editedScene.second.makeDescriptor()); } void Editor::saveProject() { - assert(fs::is_directory(fs::path(m_projectSavePath).remove_filename())); - std::ofstream(m_projectSavePath) << json(m_project).dump(4); + saveEditedScene(); + if (m_projectFilePath.has_parent_path()) + std::filesystem::create_directories(m_projectFilePath.parent_path()); + + YAML::Emitter out; + out << YAML::convert::encode(m_project); + if (!out.good()) + throw std::runtime_error("failed to serialize project"); + + std::ofstream file(m_projectFilePath); + if (!file.is_open()) + throw std::runtime_error("failed to open project file for writing"); + + file << out.c_str(); + if (!file.good()) + throw std::runtime_error("failed to write project file"); } void Editor::reloadScriptLib() { - if (m_scriptLibHandle != nullptr) + assert(m_game.has_value() == false); + assert(std::ranges::all_of( + m_editedScene.second.ecsWorld() | GE::ECSView(), + [](const auto& e) -> bool { return e.template get<0>().instance == nullptr; } + )); + assert(std::filesystem::exists(m_project.scriptLib())); + + if (m_project.scriptLib().empty()) { - dlFree(m_scriptLibHandle); - m_scriptLibHandle = nullptr; - m_getScriptNames = nullptr; - m_makeScriptInstance = nullptr; + m_scriptLibrary.reset(); + return; } - if (m_project.scriptLib().empty() == false) - { - fs::path scriptLibPath = fs::path(m_projectSavePath).remove_filename() / m_project.scriptLib(); - #ifdef _WIN32 - scriptLibPath.replace_extension(".dll"); - #endif - assert(scriptLibPath.is_absolute()); - if (fs::is_regular_file(scriptLibPath) == false) - return; - m_scriptLibHandle = dlLoad(scriptLibPath.string().c_str()); - if (m_scriptLibHandle != nullptr) - { - m_getScriptNames = (GetScriptNamesFn)getSym(m_scriptLibHandle, "getScriptNames"); - m_makeScriptInstance = (MakeScriptInstanceFn)getSym(m_scriptLibHandle, "makeScriptInstance"); - if (m_getScriptNames == nullptr || m_makeScriptInstance == nullptr) - { - dlFree(m_scriptLibHandle); - m_scriptLibHandle = nullptr; - } - } - } + m_scriptLibrary.emplace(m_project.scriptLib()); } -void Editor::editScene(Scene* scene) +void Editor::startGame() { - if (m_editedScene) - { - assert(m_editedScene->assetManager().isLoaded()); - m_editedScene->assetManager().unloadAssets(); - } - - m_editedScene = scene; - m_editedScene->assetManager().loadAssets(renderer().graphicAPI(), fs::path(m_projectSavePath).remove_filename()); - - m_selectedEntity = Entity(); - m_editorCamera = EditorCamera(); + assert(m_game.has_value() == false); + saveEditedScene(); + m_game.emplace(&assetManager(), m_scriptLibrary ? &m_scriptLibrary.value() : nullptr, m_project.makeGameDescriptor()); + setPrimaryInputContext(m_game->inputContext()); } -void Editor::runGame() +void Editor::stopGame() { - Game::Descriptor gameDescriptor; - gameDescriptor.scenes = m_project.scenes(); - gameDescriptor.inputContext = m_project.inputContext(); - gameDescriptor.graphicAPI = &renderer().graphicAPI(); - gameDescriptor.baseDir = m_projectSavePath.remove_filename(); - gameDescriptor.makeScriptInstance = m_makeScriptInstance; - - m_game = utils::makeUnique(gameDescriptor); - m_game->setActiveScene(m_project.startScene()->name()); + assert(m_game.has_value()); + setPrimaryInputContext(m_editorInputContext); + m_game.reset(); } -void Editor::stopGame() +void Editor::setPrimaryInputContext(GE::InputContext& inputContext) { - m_game->stop(); + popInputContext(); + popInputContext(); + + pushInputContext(&inputContext); + pushInputContext(&m_imguiInputContext); } -Editor::~Editor() +void Editor::processDropedFiles() { - if (m_scriptLibHandle != nullptr) + while (const std::optional droppedFile = window().popDroppedFile()) { - dlFree(m_scriptLibHandle); - m_scriptLibHandle = nullptr; + if (std::filesystem::is_regular_file(*droppedFile) && droppedFile->extension() == ".geproj") + loadProject(*droppedFile); } } -void Editor::processDroppedFiles() +void Editor::rebuildFrameGraph() { - fs::path path; - while (window().popDroppedFile(path)) - { - if (json::accept(std::ifstream(path))) - { - json jsn = json::parse(std::ifstream(path)); - if (jsn.find("scenes") != jsn.end() && jsn.find("imguiSettings") != jsn.end()) - { - openProject(path); - continue; - } + std::function getScene = [this]() -> GE::Scene* { + if (m_game.has_value()) + return &m_game->activeScene(); + return &m_editedScene.second; + }; + + std::function getCamera = [this]() -> GE::ICamera*{ + if (m_game.has_value()) + return nullptr; + return &m_editorCamera; + }; + + m_frameGraph = GE::FrameGraph(GE::FrameGraph::Descriptor{ + .backBufferName = "windowBackBuffer", + .textures = { + { .name = "viewportBackBuffer", .size = m_viewportSize, .pixelFormat = gfx::PixelFormat::BGRA8Unorm }, + { .name = "depthBuffer", .size = m_viewportSize, .pixelFormat = gfx::PixelFormat::Depth32Float }, + { .name = "windowBackBuffer", .size = window().frameBufferSize(), .pixelFormat = gfx::PixelFormat::BGRA8Unorm }, + }, + .passes = { + GE::FlatGeometryPassBuilder(std::move(getScene), std::move(getCamera)) + .setColorAttachment("viewportBackBuffer") + .setDepthAttachment("depthBuffer"), + GE::ImguiPassBuilder() + .setColorAttachment("windowBackBuffer") + .addSampledTexture("viewportBackBuffer") } - - if (stbi_info(path.string().c_str(), nullptr, nullptr, nullptr) == 1) - continue; - - if (m_editedScene != nullptr) - { - m_editedScene->assetManager().registerMesh(path); - continue; - } - } + }); } -} \ No newline at end of file +} // namespace GE_Editor diff --git a/editor/Editor.hpp b/editor/Editor.hpp index 1f989fe..8203a4c 100644 --- a/editor/Editor.hpp +++ b/editor/Editor.hpp @@ -10,81 +10,79 @@ #ifndef EDITOR_HPP #define EDITOR_HPP -#include "Application.hpp" -#include "ECS/Entity.hpp" #include "EditorCamera.hpp" -#include "Game.hpp" -#include "InputManager/InputContext.hpp" +#include "ImGuiInputContext.hpp" #include "Project.hpp" -#include "Scene.hpp" -#include "ViewportFrameBuff.hpp" + +#include +#include +#include +#include +#include +#include + +#include + +#include #include -#include "Script.hpp" -#include "dlLoad/dlLoad.h" +#include +#include -namespace GE +namespace GE_Editor { -class Editor final : public Application +class Editor : public GE::Application { public: - Editor(); + Editor() = delete; Editor(const Editor&) = delete; - Editor(Editor&&) = delete; + Editor(Editor&&) = delete; + + Editor(int argc, char* argv[]); void onUpdate() override; - void onImGuiRender() override; - inline void onWindowResizeEvent(gfx::WindowResizeEvent&) override {} - void onWindowRequestCloseEvent(gfx::WindowRequestCloseEvent&) override; - - // * Functions that directly match user action - // * Some are also used by other functions - void newProject(); - void openProject(const std::filesystem::path&); - void reloadProject(); + void onEvent(GE::Event& event) override; + + inline const GE::FrameGraph& frameGraph() override { return m_frameGraph; } + + ~Editor() override = default; + +private: + void loadProject(const std::filesystem::path&); + void saveEditedScene(); void saveProject(); void reloadScriptLib(); - void editScene(Scene*); - void runGame(); + void startGame(); void stopGame(); - ~Editor(); - -private: - // * helper functions - void processDroppedFiles(); + void setPrimaryInputContext(GE::InputContext&); + void processDropedFiles(); - // * absolut path of the project file - // * (can be empty if the project hasnt been saved yet) - std::filesystem::path m_projectSavePath; - bool m_projectNeedReload = false; + void rebuildFrameGraph(); + void renderImgui(); - // * project data (saved to disk) + std::filesystem::path m_projectFilePath; Project m_project; + std::pair m_editedScene; - // * data derived from the project file (created at runtime, not saved) - DlHandle m_scriptLibHandle = nullptr; - GetScriptNamesFn m_getScriptNames = nullptr; - MakeScriptInstanceFn m_makeScriptInstance = nullptr; - - // * editor state - // TODO save this data in the project so the last state can be preserved - Scene* m_editedScene = nullptr; - Entity m_selectedEntity; + GE::Entity m_selectedEntity; EditorCamera m_editorCamera; - InputContext m_editorInputContext; - // * instance used when the game is running - utils::UniquePtr m_game; + GE::InputContext m_editorInputContext; + ImGuiInputContext m_imguiInputContext; + + std::optional m_game; + + std::pair m_viewportSize = {0, 0}; + GE::FrameGraph m_frameGraph; // TODO move into render (something like render->setGraph()) - // * UI reladed data - ViewportFrameBuff m_vpFrameBuff; + std::optional m_scriptLibrary; public: - Editor& operator = (const Editor&) = delete; - Editor& operator = (Editor&&) = delete; + Editor& operator=(const Editor&) = delete; + Editor& operator=(Editor&&) = delete; }; -} +} // namespace GE -#endif // EDITOR_HPP \ No newline at end of file +#endif // EDITOR_HPP diff --git a/editor/EditorCamera.cpp b/editor/EditorCamera.cpp index 23b4b1e..dc6721d 100644 --- a/editor/EditorCamera.cpp +++ b/editor/EditorCamera.cpp @@ -3,49 +3,60 @@ * EditorCamera.cpp * * Author: Thomas Choquet - * Date: 2024/08/24 13:57:19 * --------------------------------------------------- */ #include "EditorCamera.hpp" -#include "Math/Constants.hpp" -#include "Math/Vector.hpp" -#include "Renderer/Renderer.hpp" -#include -namespace GE -{ +#include +#include + +#include +#include +#include + +#include -void EditorCamera::rotate(math::vec2f value) +namespace GE_Editor { - m_rotation += math::vec3f{ value.x, value.y, 0.0F }.normalized() * 0.05; -} -void EditorCamera::move(math::vec2f value) +namespace { - m_position += math::mat3x3::rotation(m_rotation) * math::vec3f{ value.x, 0, value.y }.normalized() * 0.15; + +constexpr float kMoveStepPerFrame = 5.0f / 60.0f; +constexpr float kRotateStepPerFrame = 1.5f / 60.0f; + } -Renderer::Camera EditorCamera::getRendererCam() +glm::mat4 EditorCamera::viewProjectionMatrix(float aspectRatio) const { - Renderer::Camera cam; + auto rotationMat = rotationMatrix(); - float zs = 10000.0F / (10000.0F - 0.01F); - float ys = 1.0F / std::tan((float)(60 * (PI / 180.0F)) * 0.5F); - float xs = ys; // (ys / aspectRatio) + glm::vec3 pos = m_position; + glm::vec3 dir = rotationMat * glm::vec4(0.0f, 0.0f, -1.0f, 0.0f); + glm::vec3 up = rotationMat * glm::vec4(0.0f, 1.0f, 0.0f, 0.0f); - cam.projectionMatrix = math::mat4x4(xs, 0, 0, 0, - 0, ys, 0, 0, - 0, 0, zs, -0.01F * zs, - 0, 0, 1, 0); + return glm::perspective(m_fov, aspectRatio, m_zNear, m_zFar) * glm::lookAt(pos, pos + dir, up); +} - cam.viewMatrix = (math::mat4x4::translation(m_position) * math::mat4x4::rotation(m_rotation)).inversed(); - return cam; +void EditorCamera::onMoveInput(const glm::vec2& value) +{ + m_position += glm::vec3(rotationMatrix() * glm::vec4(glm::vec3{ value.x, 0.0f, -value.y } * kMoveStepPerFrame, 0.0f)); } +void EditorCamera::onRotationInput(const glm::vec2& value) +{ + m_rotation.y -= value.y * kRotateStepPerFrame; + m_rotation.x += value.x * kRotateStepPerFrame; +} -EditorCamera::~EditorCamera() +glm::mat4 EditorCamera::rotationMatrix() const { + auto rotationMat = glm::mat4x4(1.0f); + rotationMat = glm::rotate(rotationMat, m_rotation.y, glm::vec3(0, 1, 0)); + rotationMat = glm::rotate(rotationMat, m_rotation.x, glm::vec3(1, 0, 0)); + rotationMat = glm::rotate(rotationMat, m_rotation.z, glm::vec3(0, 0, 1)); + return rotationMat; } -} \ No newline at end of file +} diff --git a/editor/EditorCamera.hpp b/editor/EditorCamera.hpp index 0f3f973..b132b41 100644 --- a/editor/EditorCamera.hpp +++ b/editor/EditorCamera.hpp @@ -3,42 +3,47 @@ * EditorCamera.hpp * * Author: Thomas Choquet - * Date: 2024/08/24 13:51:34 * --------------------------------------------------- */ #ifndef EDITORCAMERA_HPP #define EDITORCAMERA_HPP -#include "Math/Vector.hpp" -#include "Renderer/Renderer.hpp" +#include +#include -namespace GE +#include + +namespace GE_Editor { -class EditorCamera +class EditorCamera : public GE::ICamera { public: - EditorCamera() = default; - EditorCamera(const EditorCamera&) = default; - EditorCamera(EditorCamera&&) = default; + EditorCamera() = default; + EditorCamera(const EditorCamera&) = delete; + EditorCamera(EditorCamera&&) = default; - void rotate(math::vec2f); - void move(math::vec2f); + inline glm::vec3 position() const override { return m_position; } + glm::mat4 viewProjectionMatrix(float aspectRatio) const override; - Renderer::Camera getRendererCam(); - - ~EditorCamera(); + void onMoveInput(const glm::vec2& value); + void onRotationInput(const glm::vec2& value); private: - math::vec3f m_position; - math::vec3f m_rotation; + glm::mat4 rotationMatrix() const; + + glm::vec3 m_position = { 0.0f, 0.0f, 0.0f }; + glm::vec3 m_rotation = { 0.0f, 0.0f, 0.0f }; + float m_fov = glm::radians(60.0f); + float m_zFar = 1000.0f; + float m_zNear = 0.1f; public: - EditorCamera& operator = (const EditorCamera&) = default; - EditorCamera& operator = (EditorCamera&&) = default; + EditorCamera& operator=(const EditorCamera&) = delete; + EditorCamera& operator=(EditorCamera&&) = default; }; -} +} // namespace GE_Editor -#endif // EDITORCAMERA_HPP \ No newline at end of file +#endif diff --git a/editor/ImGuiInputContext.cpp b/editor/ImGuiInputContext.cpp new file mode 100644 index 0000000..2d89c15 --- /dev/null +++ b/editor/ImGuiInputContext.cpp @@ -0,0 +1,196 @@ +/* + * --------------------------------------------------- + * ImGuiInputContext.cpp + * + * Author: Thomas Choquet + * --------------------------------------------------- + */ + +#include "ImGuiInputContext.hpp" + +#include + +#include + +#include + +namespace +{ + +ImGuiKey glfwKeyToImGuiKey(int keycode) +{ + switch (keycode) + { + case GLFW_KEY_TAB: return ImGuiKey_Tab; + case GLFW_KEY_LEFT: return ImGuiKey_LeftArrow; + case GLFW_KEY_RIGHT: return ImGuiKey_RightArrow; + case GLFW_KEY_UP: return ImGuiKey_UpArrow; + case GLFW_KEY_DOWN: return ImGuiKey_DownArrow; + case GLFW_KEY_PAGE_UP: return ImGuiKey_PageUp; + case GLFW_KEY_PAGE_DOWN: return ImGuiKey_PageDown; + case GLFW_KEY_HOME: return ImGuiKey_Home; + case GLFW_KEY_END: return ImGuiKey_End; + case GLFW_KEY_INSERT: return ImGuiKey_Insert; + case GLFW_KEY_DELETE: return ImGuiKey_Delete; + case GLFW_KEY_BACKSPACE: return ImGuiKey_Backspace; + case GLFW_KEY_SPACE: return ImGuiKey_Space; + case GLFW_KEY_ENTER: return ImGuiKey_Enter; + case GLFW_KEY_ESCAPE: return ImGuiKey_Escape; + case GLFW_KEY_APOSTROPHE: return ImGuiKey_Apostrophe; + case GLFW_KEY_COMMA: return ImGuiKey_Comma; + case GLFW_KEY_MINUS: return ImGuiKey_Minus; + case GLFW_KEY_PERIOD: return ImGuiKey_Period; + case GLFW_KEY_SLASH: return ImGuiKey_Slash; + case GLFW_KEY_SEMICOLON: return ImGuiKey_Semicolon; + case GLFW_KEY_EQUAL: return ImGuiKey_Equal; + case GLFW_KEY_LEFT_BRACKET: return ImGuiKey_LeftBracket; + case GLFW_KEY_BACKSLASH: return ImGuiKey_Backslash; + case GLFW_KEY_WORLD_1: return ImGuiKey_Oem102; + case GLFW_KEY_WORLD_2: return ImGuiKey_Oem102; + case GLFW_KEY_RIGHT_BRACKET: return ImGuiKey_RightBracket; + case GLFW_KEY_GRAVE_ACCENT: return ImGuiKey_GraveAccent; + case GLFW_KEY_CAPS_LOCK: return ImGuiKey_CapsLock; + case GLFW_KEY_SCROLL_LOCK: return ImGuiKey_ScrollLock; + case GLFW_KEY_NUM_LOCK: return ImGuiKey_NumLock; + case GLFW_KEY_PRINT_SCREEN: return ImGuiKey_PrintScreen; + case GLFW_KEY_PAUSE: return ImGuiKey_Pause; + case GLFW_KEY_KP_0: return ImGuiKey_Keypad0; + case GLFW_KEY_KP_1: return ImGuiKey_Keypad1; + case GLFW_KEY_KP_2: return ImGuiKey_Keypad2; + case GLFW_KEY_KP_3: return ImGuiKey_Keypad3; + case GLFW_KEY_KP_4: return ImGuiKey_Keypad4; + case GLFW_KEY_KP_5: return ImGuiKey_Keypad5; + case GLFW_KEY_KP_6: return ImGuiKey_Keypad6; + case GLFW_KEY_KP_7: return ImGuiKey_Keypad7; + case GLFW_KEY_KP_8: return ImGuiKey_Keypad8; + case GLFW_KEY_KP_9: return ImGuiKey_Keypad9; + case GLFW_KEY_KP_DECIMAL: return ImGuiKey_KeypadDecimal; + case GLFW_KEY_KP_DIVIDE: return ImGuiKey_KeypadDivide; + case GLFW_KEY_KP_MULTIPLY: return ImGuiKey_KeypadMultiply; + case GLFW_KEY_KP_SUBTRACT: return ImGuiKey_KeypadSubtract; + case GLFW_KEY_KP_ADD: return ImGuiKey_KeypadAdd; + case GLFW_KEY_KP_ENTER: return ImGuiKey_KeypadEnter; + case GLFW_KEY_KP_EQUAL: return ImGuiKey_KeypadEqual; + case GLFW_KEY_LEFT_SHIFT: return ImGuiKey_LeftShift; + case GLFW_KEY_LEFT_CONTROL: return ImGuiKey_LeftCtrl; + case GLFW_KEY_LEFT_ALT: return ImGuiKey_LeftAlt; + case GLFW_KEY_LEFT_SUPER: return ImGuiKey_LeftSuper; + case GLFW_KEY_RIGHT_SHIFT: return ImGuiKey_RightShift; + case GLFW_KEY_RIGHT_CONTROL: return ImGuiKey_RightCtrl; + case GLFW_KEY_RIGHT_ALT: return ImGuiKey_RightAlt; + case GLFW_KEY_RIGHT_SUPER: return ImGuiKey_RightSuper; + case GLFW_KEY_MENU: return ImGuiKey_Menu; + case GLFW_KEY_0: return ImGuiKey_0; + case GLFW_KEY_1: return ImGuiKey_1; + case GLFW_KEY_2: return ImGuiKey_2; + case GLFW_KEY_3: return ImGuiKey_3; + case GLFW_KEY_4: return ImGuiKey_4; + case GLFW_KEY_5: return ImGuiKey_5; + case GLFW_KEY_6: return ImGuiKey_6; + case GLFW_KEY_7: return ImGuiKey_7; + case GLFW_KEY_8: return ImGuiKey_8; + case GLFW_KEY_9: return ImGuiKey_9; + case GLFW_KEY_A: return ImGuiKey_A; + case GLFW_KEY_B: return ImGuiKey_B; + case GLFW_KEY_C: return ImGuiKey_C; + case GLFW_KEY_D: return ImGuiKey_D; + case GLFW_KEY_E: return ImGuiKey_E; + case GLFW_KEY_F: return ImGuiKey_F; + case GLFW_KEY_G: return ImGuiKey_G; + case GLFW_KEY_H: return ImGuiKey_H; + case GLFW_KEY_I: return ImGuiKey_I; + case GLFW_KEY_J: return ImGuiKey_J; + case GLFW_KEY_K: return ImGuiKey_K; + case GLFW_KEY_L: return ImGuiKey_L; + case GLFW_KEY_M: return ImGuiKey_M; + case GLFW_KEY_N: return ImGuiKey_N; + case GLFW_KEY_O: return ImGuiKey_O; + case GLFW_KEY_P: return ImGuiKey_P; + case GLFW_KEY_Q: return ImGuiKey_Q; + case GLFW_KEY_R: return ImGuiKey_R; + case GLFW_KEY_S: return ImGuiKey_S; + case GLFW_KEY_T: return ImGuiKey_T; + case GLFW_KEY_U: return ImGuiKey_U; + case GLFW_KEY_V: return ImGuiKey_V; + case GLFW_KEY_W: return ImGuiKey_W; + case GLFW_KEY_X: return ImGuiKey_X; + case GLFW_KEY_Y: return ImGuiKey_Y; + case GLFW_KEY_Z: return ImGuiKey_Z; + case GLFW_KEY_F1: return ImGuiKey_F1; + case GLFW_KEY_F2: return ImGuiKey_F2; + case GLFW_KEY_F3: return ImGuiKey_F3; + case GLFW_KEY_F4: return ImGuiKey_F4; + case GLFW_KEY_F5: return ImGuiKey_F5; + case GLFW_KEY_F6: return ImGuiKey_F6; + case GLFW_KEY_F7: return ImGuiKey_F7; + case GLFW_KEY_F8: return ImGuiKey_F8; + case GLFW_KEY_F9: return ImGuiKey_F9; + case GLFW_KEY_F10: return ImGuiKey_F10; + case GLFW_KEY_F11: return ImGuiKey_F11; + case GLFW_KEY_F12: return ImGuiKey_F12; + case GLFW_KEY_F13: return ImGuiKey_F13; + case GLFW_KEY_F14: return ImGuiKey_F14; + case GLFW_KEY_F15: return ImGuiKey_F15; + case GLFW_KEY_F16: return ImGuiKey_F16; + case GLFW_KEY_F17: return ImGuiKey_F17; + case GLFW_KEY_F18: return ImGuiKey_F18; + case GLFW_KEY_F19: return ImGuiKey_F19; + case GLFW_KEY_F20: return ImGuiKey_F20; + case GLFW_KEY_F21: return ImGuiKey_F21; + case GLFW_KEY_F22: return ImGuiKey_F22; + case GLFW_KEY_F23: return ImGuiKey_F23; + case GLFW_KEY_F24: return ImGuiKey_F24; + default: return ImGuiKey_None; + } +} + +} + +namespace GE_Editor +{ + +void ImGuiInputContext::onInputEvent(GE::InputEvent& event) +{ + ImGuiIO& io = ImGui::GetIO(); + + event.dispatch([&](GE::KeyDownEvent& keyDownEvent) { + if (keyDownEvent.isRepeat()) + return; + io.AddKeyEvent(glfwKeyToImGuiKey(keyDownEvent.keyCode()), true); + if (io.WantCaptureKeyboard) + event.markAsProcessed(); + }); + event.dispatch([&](GE::KeyUpEvent& keyUpEvent) { + io.AddKeyEvent(glfwKeyToImGuiKey(keyUpEvent.keyCode()), false); + if (io.WantCaptureKeyboard) + event.markAsProcessed(); + }); + event.dispatch([&](GE::CharEvent& charEvent) { + io.AddInputCharacter(charEvent.codePoint()); + if (io.WantCaptureKeyboard) + event.markAsProcessed(); + }); + event.dispatch([&](GE::MouseDownEvent& mouseDownEvent) { + io.AddMouseButtonEvent(mouseDownEvent.mouseCode(), true); + if (io.WantCaptureMouse) + event.markAsProcessed(); + }); + event.dispatch([&](GE::MouseUpEvent& mouseUpEvent) { + io.AddMouseButtonEvent(mouseUpEvent.mouseCode(), false); + if (io.WantCaptureMouse) + event.markAsProcessed(); + }); + event.dispatch([&](GE::MouseMoveEvent& mouseMoveEvent) { + auto [x, y] = mouseMoveEvent.mousePos(); + io.AddMousePosEvent(static_cast(x), static_cast(y)); + if (io.WantCaptureMouse) + event.markAsProcessed(); + }); + event.dispatch([&](GE::ScrollEvent& scrollEvent) { + io.AddMouseWheelEvent(static_cast(scrollEvent.offsetX()), static_cast(scrollEvent.offsetY())); + if (io.WantCaptureMouse) + event.markAsProcessed(); + }); +} + +} diff --git a/editor/ImGuiInputContext.hpp b/editor/ImGuiInputContext.hpp new file mode 100644 index 0000000..b91661b --- /dev/null +++ b/editor/ImGuiInputContext.hpp @@ -0,0 +1,25 @@ +/* + * --------------------------------------------------- + * ImGuiInputContext.hpp + * + * Author: Thomas Choquet + * --------------------------------------------------- + */ + +#ifndef IMGUIINPUTCONTEXT_HPP +#define IMGUIINPUTCONTEXT_HPP + +#include + +namespace GE_Editor +{ + +class ImGuiInputContext : public GE::InputContext +{ +public: + void onInputEvent(GE::InputEvent& event) override; +}; + +} + +#endif diff --git a/editor/Project.cpp b/editor/Project.cpp index 153b24c..36e93e1 100644 --- a/editor/Project.cpp +++ b/editor/Project.cpp @@ -3,123 +3,115 @@ * Project.cpp * * Author: Thomas Choquet - * Date: 2024/10/27 18:41:09 * --------------------------------------------------- */ #include "Project.hpp" -#include "ECS/Components.hpp" -#include "Scene.hpp" -#include -#include - -#define DEFAULT_IMGUI_INI \ - "[Window][WindowOverViewport_11111111]\nPos=0,19\nSize=1280,701\nCollapsed=0\n\n" \ - "[Window][Debug##Default]\nPos=60,60\nSize=400,400\nCollapsed=0\n\n" \ - "[Window][Entity inspector]\nPos=1015,405\nSize=265,315\nCollapsed=0\nDockId=0x00000004,0\n\n" \ - "[Window][Scene graph]\nPos=1015,19\nSize=265,384\nCollapsed=0\nDockId=0x00000003,0\n\n" \ - "[Window][viewport]\nPos=0,19\nSize=1013,554\nCollapsed=0\nDockId=0x00000007,0\n\n" \ - "[Window][File explorer]\nPos=0,544\nSize=1013,176\nCollapsed=0\nDockId=0x00000006,0\n\n" \ - "[Window][Project properties]\nPos=435,288\nSize=361,77\nCollapsed=0\n\n" \ - "[Window][Scenes]\nPos=445,309\nSize=268,102\nCollapsed=0\n\n" \ - "[Window][Content browser]\nPos=0,575\nSize=1013,145\nCollapsed=0\nDockId=0x00000008,0\n\n" \ - "[Docking][Data]\n" \ - "DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=100,196 Size=1280,701 Split=X Selected=0x0BA3B4F3\n" \ - " DockNode ID=0x00000001 Parent=0x7C6B3D9B SizeRef=1428,701 Split=Y Selected=0x0BA3B4F3\n" \ - " DockNode ID=0x00000005 Parent=0x00000001 SizeRef=1013,523 Split=Y Selected=0x0BA3B4F3\n" \ - " DockNode ID=0x00000007 Parent=0x00000005 SizeRef=1013,554 CentralNode=1 Selected=0x0BA3B4F3\n" \ - " DockNode ID=0x00000008 Parent=0x00000005 SizeRef=1013,145 Selected=0x3995E0EF\n" \ - " DockNode ID=0x00000006 Parent=0x00000001 SizeRef=1013,176 Selected=0xD2F73F3F\n" \ - " DockNode ID=0x00000002 Parent=0x7C6B3D9B SizeRef=265,701 Split=Y Selected=0xF5BE1C77\n" \ - " DockNode ID=0x00000003 Parent=0x00000002 SizeRef=168,583 Selected=0xF5BE1C77\n" \ - " DockNode ID=0x00000004 Parent=0x00000002 SizeRef=168,479 Selected=0xD3D12213\n\n" \ - -using json = nlohmann::json; -namespace fs = std::filesystem; -namespace GE -{ +#include +#include +#include +#include +#include +#include -Project::Project() - : m_name("new_project"), - m_imguiSettings(DEFAULT_IMGUI_INI) -{ - auto& defautScene = *m_scenes.insert(Scene("default_scene")); - - auto cube = defautScene.newEntity("cube"); - cube.emplace(); - cube.emplace(BUILT_IN_CUBE_ASSET_ID); +#include - auto light = defautScene.newEntity("light"); - light.emplace().position = math::vec3f{-1.5, 1.5, -1.5}; - light.emplace(); - - auto camera = defautScene.newEntity("camera"); - camera.emplace().position = math::vec3f{0.0F, 0.0F, -3.0F}; - camera.emplace(); - - defautScene.setActiveCamera(camera); - - m_startScene = "default_scene"; +#include +#include +#include +#include +#include + +namespace { + constexpr std::string_view DEFAULT_IMGUI_SETTINGS = + "[Window][WindowOverViewport_11111111]\n" + "Pos=0,19\nSize=1280,701\nCollapsed=0\n\n" + "[Window][Debug##Default]\n" + "Pos=60,60\nSize=400,400\nCollapsed=0\n\n[Window][viewport]\n" + "Pos=0,19\nSize=1004,525\nCollapsed=0\nDockId=0x00000001,0\n\n" + "[Window][Scene graph]\n" + "Pos=1006,19\nSize=274,273\nCollapsed=0\nDockId=0x00000005,0\n\n" + "[Window][Entity inspector]\n" + "Pos=1006,294\nSize=274,426\nCollapsed=0\nDockId=0x00000006,0\n\n" + "[Window][Scenes]\n" + "Pos=0,546\nSize=1004,174\nCollapsed=0\nDockId=0x00000002,0\n\n" + "[Window][Project properties]\n" + "Pos=435,238\nSize=409,244\nCollapsed=0\n\n" + "[Docking][Data]\n" + "DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=0,19 Size=1280,701 Split=X\n" + " DockNode ID=0x00000003 Parent=0x08BD597D SizeRef=1004,701 Split=Y\n" + " DockNode ID=0x00000001 Parent=0x00000003 SizeRef=1280,525 CentralNode=1 Selected=0xF6034841\n" + " DockNode ID=0x00000002 Parent=0x00000003 SizeRef=1280,174 Selected=0xC73B27C0\n" + " DockNode ID=0x00000004 Parent=0x08BD597D SizeRef=274,701 Split=Y Selected=0x848E2614\n" + " DockNode ID=0x00000005 Parent=0x00000004 SizeRef=142,273 Selected=0x2C1AB1E4\n" + " DockNode ID=0x00000006 Parent=0x00000004 SizeRef=142,426 Selected=0x848E2614\n" + "\n"; } -void Project::deleteScene(const utils::String& name) +namespace GE_Editor { - assert(m_startScene != name); - m_scenes.remove(m_scenes.find(name)); -} -Scene* Project::startScene() +Project::Project() + : m_name("Untitled Project") + , m_startScene(0) + , m_imguiSettings(DEFAULT_IMGUI_SETTINGS) + , m_resourceDir("resources") { - if (m_startScene.isEmpty()) - return nullptr; - auto it = m_scenes.find(m_startScene); - assert(it != m_scenes.end()); - return &*it; + auto [it, inserted] = m_scenes.emplace(0, GE::Scene::Descriptor()); + assert(inserted); + GE::Scene::Descriptor& desc = it->second; + + desc.name = "default_scene"; + desc.activeCamera = 1; + desc.registredAssets = { + { GE::AssetPath(), GE::BUILT_IN_CUBE_ASSET_ID } + }; + desc.entities = { + { + 0, { + GE::NameComponent{"cube"}, + GE::TransformComponent{}, + GE::MeshComponent{GE::BUILT_IN_CUBE_ASSET_ID} + } + }, + { + 1, { + GE::NameComponent{"camera"}, + GE::HierarchyComponent{ .firstChild = 2 }, + GE::TransformComponent{ + .position = { 3.0f, 3.0f, 5.0f }, + .rotation = { -0.5f, 0.5f, 0.0f }, + .scale = { 1.0f, 1.0f, 1.0f } + }, + GE::CameraComponent{} + } + }, + { + 2, { + GE::NameComponent{"light"}, + GE::HierarchyComponent{ .parent = 1 }, + GE::TransformComponent{}, + GE::LightComponent{} + } + } + }; + + m_startScene = it->first; } -void to_json(json& jsn, const Project& project) +GE::Game::Descriptor Project::makeGameDescriptor() const { - jsn["name"] = std::string(project.m_name); - jsn["scriptsLib"] = project.m_scriptLib.string(); - - jsn["imguiSettings"] = std::string(project.m_imguiSettings); - - json scenesJsn = json::array(); - for (const auto& scene : project.m_scenes) - scenesJsn.emplace_back(scene); - jsn["scenes"] = scenesJsn; - - jsn["startScene"] = project.m_startScene; + return { + .scenes = m_scenes + | std::views::values + | std::views::transform([](const GE::Scene::Descriptor& sceneDescriptor) { + return std::make_pair(sceneDescriptor.name, sceneDescriptor); + }) + | std::ranges::to>(), + .activeScene = startScene().second.name, + .inputContext = m_inputContext + }; } -void from_json(const json& jsn, Project& project) -{ - auto nameIt = jsn.find("name"); - project.m_name = nameIt != jsn.end() ? utils::String(nameIt->template get().c_str()) : utils::String(""); - - auto scriptsLibIt = jsn.find("scriptsLib"); - project.m_scriptLib = scriptsLibIt != jsn.end() ? scriptsLibIt->template get() : fs::path(); - - auto imguiSettIt = jsn.find("imguiSettings"); - if (imguiSettIt != jsn.end()) - project.m_imguiSettings = utils::String(imguiSettIt->template get().c_str()); - else - project.m_imguiSettings = ImGui::SaveIniSettingsToMemory(); - - project.m_scenes.clear(); - auto scenesIt = jsn.find("scenes"); - if (scenesIt != jsn.end()) - { - for (auto& scene : *scenesIt) - project.m_scenes.insert(scene.template get()); - } - - auto startSceneNameIt = jsn.find("startScene"); - if (startSceneNameIt != jsn.end() && project.m_scenes.contain(utils::String(startSceneNameIt->template get().c_str()))) - project.m_startScene = utils::String(startSceneNameIt->template get().c_str()); - else - project.m_startScene = ""; } - -} \ No newline at end of file diff --git a/editor/Project.hpp b/editor/Project.hpp index 60c0725..efebf3f 100644 --- a/editor/Project.hpp +++ b/editor/Project.hpp @@ -3,82 +3,127 @@ * Project.hpp * * Author: Thomas Choquet - * Date: 2024/10/27 18:40:31 * --------------------------------------------------- */ #ifndef PROJECT_HPP #define PROJECT_HPP -#include "Scene.hpp" -#include "UtilsCPP/Set.hpp" -#include "UtilsCPP/String.hpp" -#include +#include +#include + +#include +#include +#include #include -#include "InputManager/InputContext.hpp" +#include +#include +#include -namespace GE +namespace GE_Editor { -/* - * Mirror of the project file with a convenient set of getters and setters. - * No complex behavior should be inserted here (like saving) and no other data than the JSON file. - */ - class Project { public: - Project(); - Project(const Project&) = default; - Project(Project&&) = default; - - inline const utils::String& name() const { return m_name; } - inline void setName(const utils::String& s) { m_name = s; } - - inline const std::filesystem::path& scriptLib() const { return m_scriptLib; } - inline void setScriptLib(const std::filesystem::path& p) { m_scriptLib = p; } + Project(); // new project with default scene + Project(const Project&) = delete; + Project(Project&&) = default; - inline void loadIniSettingsFromMemory() const { ImGui::LoadIniSettingsFromMemory(m_imguiSettings); } - inline void saveIniSettingsToMemory() { m_imguiSettings = ImGui::SaveIniSettingsToMemory(); } + inline const std::string& name() const { return m_name; } + inline void setName(const std::string& name) { m_name = name; } - inline const utils::Set& scenes() const { return m_scenes; } - - inline Scene& scene(const utils::String& name) { return *m_scenes.find(name); } - inline const Scene& scene(const utils::String& name) const { return const_cast(this)->scene(name); } + inline const std::map& scenes() const { return m_scenes; } + inline void setScene(uint32_t id, const GE::Scene::Descriptor& desc) { m_scenes.insert_or_assign(id, desc); } - inline Scene* addScene(const Scene& s) { return &*m_scenes.insert(s); } - inline Scene* addScene(Scene&& s) { return &*m_scenes.insert(std::move(s)); } + inline std::pair startScene() const { return *m_scenes.find(m_startScene); } + inline void setStartScene(uint32_t id) { assert(m_scenes.contains(id)); m_startScene = id; } - void deleteScene(const utils::String& name); - inline void deleteScene(const Scene& s) { deleteScene(s.name()); } + inline const std::string& imguiSettings() const { return m_imguiSettings; } + inline void setImguiSettings(std::string imguiSettings) { m_imguiSettings = std::move(imguiSettings); } - Scene* startScene(); - inline const Scene* startScene() const { return const_cast(this)->startScene(); } + inline const std::filesystem::path& scriptLib() const { return m_scriptLib; } + inline void setScriptLib(std::filesystem::path scriptLib) { m_scriptLib = std::move(scriptLib); } - inline void setStartScene(const utils::String& name) { m_startScene = name; } - inline void setStartScene(const Scene& s) { setStartScene(s.name()); } + inline auto& inputContext(this auto&& self) { return self.m_inputContext; } - inline InputContext& inputContext() { return m_inputContext; } - inline const InputContext& inputContext() const { return const_cast(this)->inputContext(); } + inline std::filesystem::path resourceDir() const { return m_resourceDir; } + inline void setResourceDir(std::filesystem::path p) { m_resourceDir = std::move(p); } - ~Project() = default; + GE::Game::Descriptor makeGameDescriptor() const; private: - utils::String m_name; + std::string m_name; + std::map m_scenes; + uint32_t m_startScene; + std::string m_imguiSettings; std::filesystem::path m_scriptLib; - utils::String m_imguiSettings; - utils::Set m_scenes; - utils::String m_startScene; - InputContext m_inputContext; // TODO add to json + GE::InputContext m_inputContext; + std::filesystem::path m_resourceDir; public: - Project& operator = (const Project&) = default; - Project& operator = (Project&&) = default; + Project& operator = (const Project&) = delete; + Project& operator = (Project&&) = default; - friend void to_json(nlohmann::json&, const Project&); - friend void from_json(const nlohmann::json&, Project&); + friend struct YAML::convert; +}; + +} + +namespace YAML +{ + +template<> +struct convert +{ + static Node encode(const GE_Editor::Project& rhs) + { + Node node; + node["name"] = rhs.m_name; + node["imguiSettings"] = rhs.m_imguiSettings; + node["scriptsLib"] = (rhs.m_scriptLib.extension() == GE_SHARED_LIBRARY_EXTENSION + ? std::filesystem::path(rhs.m_scriptLib).replace_extension() + : rhs.m_scriptLib).string(); + node["inputContext"] = rhs.m_inputContext; + node["resourceDir"] = rhs.m_resourceDir.string(); + for (const auto& [_, scene] : rhs.m_scenes) + node["scenes"].push_back(scene); + node["startScene"] = rhs.startScene().second.name; + return node; + } + + static bool decode(const Node& node, GE_Editor::Project& rhs) + { + if (!node.IsMap() || !node["name"] || !node["startScene"]) + return false; + if (!node["scenes"] || !node["scenes"].IsSequence() || node["scenes"].size() == 0) + return false; + + rhs.m_name = node["name"].as(); + rhs.m_imguiSettings = node["imguiSettings"] ? node["imguiSettings"].as() : std::string(); + rhs.m_scriptLib = node["scriptsLib"] ? std::filesystem::path(node["scriptsLib"].as()) : std::filesystem::path(); + if (!rhs.m_scriptLib.empty() && rhs.m_scriptLib.extension() != GE_SHARED_LIBRARY_EXTENSION) + rhs.m_scriptLib.replace_extension(GE_SHARED_LIBRARY_EXTENSION); + rhs.m_inputContext = node["inputContext"] ? node["inputContext"].as() : GE::InputContext(); + rhs.m_resourceDir = node["resourceDir"] ? std::filesystem::path(node["resourceDir"].as()) : std::filesystem::path(); + + rhs.m_scenes.clear(); + uint32_t sceneId = 0; + for (const Node& sceneNode : node["scenes"]) + rhs.m_scenes.emplace(sceneId++, sceneNode.as()); + + const std::string startSceneName = node["startScene"].as(); + auto startScene = std::ranges::find_if(rhs.m_scenes, [&](auto& scene) -> bool { + return scene.second.name == startSceneName; + }); + if (startScene == rhs.m_scenes.end()) + return false; + rhs.m_startScene = startScene->first; + + return true; + } }; } -#endif // PROJECT_HPP \ No newline at end of file +#endif // PROJECT_HPP diff --git a/editor/UI/ContentBrowserPanel.cpp b/editor/UI/ContentBrowserPanel.cpp deleted file mode 100644 index a2ccc8a..0000000 --- a/editor/UI/ContentBrowserPanel.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/* - * --------------------------------------------------- - * ContentBrowserPanel.cpp - * - * Author: Thomas Choquet - * Date: 2024/11/06 14:48:45 - * --------------------------------------------------- - */ - -#include "UI/ContentBrowserPanel.hpp" -#include "Script.hpp" -#include - -#define ELEMENT_SIZE 60.0F - -namespace GE -{ - -ContentBrowserPanel::ContentBrowserPanel(Project& project, const Scene* scene, GetScriptNamesFn getScriptNames) - : m_project(project), m_scene(scene), m_getScriptNames(getScriptNames) -{ -} - -void ContentBrowserPanel::render() -{ - if (ImGui::Begin("Content browser")) - { - renderScenes(); - renderAssets(); - renderScripts(); - } - ImGui::End(); -} - -void ContentBrowserPanel::renderElement(const utils::String& name, const char *payloadType, const void* payload, utils::uint64 payloadSize) -{ - if (ImGui::BeginChild(name + "childId", ImVec2(ELEMENT_SIZE, ELEMENT_SIZE + ImGui::GetFrameHeightWithSpacing()))) - { - ImGui::Button(name, ImVec2(ELEMENT_SIZE, ELEMENT_SIZE)); - if (ImGui::BeginDragDropSource()) - { - ImGui::SetDragDropPayload(payloadType, payload, payloadSize); - ImGui::Text("%s", (const char*)name); - ImGui::EndDragDropSource(); - } - ImGui::Text("%s", (const char*)name); - } - ImGui::EndChild(); - - m_lineWith += ELEMENT_SIZE + 7.5F; - if (m_lineWith < ImGui::GetContentRegionAvail().x - ELEMENT_SIZE) - ImGui::SameLine(); - else - m_lineWith = 0.0F; - -} - -void ContentBrowserPanel::renderScenes() -{ - for (auto& scene : m_project.scenes()) - renderElement(scene.name(), "scene_dnd", (void*)(const char*)scene.name(), scene.name().length()); -} - -void ContentBrowserPanel::renderAssets() -{ - if (m_scene == nullptr) - return; - - for (auto& [path, id] : m_scene->assetManager().registeredMeshes()) - renderElement(path.filename().string().c_str(), "mesh_dnd", &id, sizeof(id)); -} - -void ContentBrowserPanel::renderScripts() -{ - if (m_getScriptNames == nullptr) - return; - - const char** scriptNames; - utils::uint64 scriptCount; - m_getScriptNames(&scriptNames, &scriptCount); - - for (utils::uint64 i = 0; i < scriptCount; i++) - renderElement(scriptNames[i], "script_dnd", scriptNames[i], strlen(scriptNames[i])); -} - - -} \ No newline at end of file diff --git a/editor/UI/ContentBrowserPanel.hpp b/editor/UI/ContentBrowserPanel.hpp deleted file mode 100644 index 8a1e3f4..0000000 --- a/editor/UI/ContentBrowserPanel.hpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * --------------------------------------------------- - * ContentBrowserPanel.hpp - * - * Author: Thomas Choquet - * Date: 2024/11/06 14:16:00 - * --------------------------------------------------- - */ - -#ifndef CONTENTBROWSERPANEL_HPP -#define CONTENTBROWSERPANEL_HPP - -#include "Project.hpp" -#include "Scene.hpp" -#include "UtilsCPP/String.hpp" -#include "UtilsCPP/Types.hpp" -#include "Script.hpp" - -namespace GE -{ - -class ContentBrowserPanel -{ -public: - ContentBrowserPanel() = delete; - ContentBrowserPanel(const ContentBrowserPanel&) = delete; - ContentBrowserPanel(ContentBrowserPanel&&) = delete; - - ContentBrowserPanel(Project&, const Scene*, GetScriptNamesFn); - - void render(); - - ~ContentBrowserPanel() = default; - -private: - void renderElement(const utils::String& name, const char *payloadType, const void* payload, utils::uint64 payloadSize); - - void renderScenes(); - void renderAssets(); - void renderScripts(); - - Project& m_project; - const Scene* m_scene; - GetScriptNamesFn m_getScriptNames; - - float m_lineWith = 0.0F; - -public: - ContentBrowserPanel& operator = (const ContentBrowserPanel&) = delete; - ContentBrowserPanel& operator = (ContentBrowserPanel&&) = delete; -}; - -} - -#endif // CONTENTBROWSERPANEL_HPP \ No newline at end of file diff --git a/editor/UI/EditorUI.cpp b/editor/UI/EditorUI.cpp deleted file mode 100644 index 64e733e..0000000 --- a/editor/UI/EditorUI.cpp +++ /dev/null @@ -1,652 +0,0 @@ -/* - * --------------------------------------------------- - * EditorUI.cpp - * - * Author: Thomas Choquet - * Date: 2024/10/10 12:54:44 - * --------------------------------------------------- - */ - -#if 0 -struct OpenProjectFileDialog -{ - bool isPresented = false; - std::future result; - - void present() - { - isPresented = true; - result = std::async(tinyfd_openFileDialog, "Open project", "", 0, nullptr, nullptr, 0); - } -}; - -struct SaveProjectFileDialog -{ - bool isPresented = false; - std::future result; - - void present(const utils::String& projectName) - { - isPresented = true; - result = std::async(tinyfd_saveFileDialog, "Choose destination", projectName + ".geproj", 0, nullptr, nullptr); - } -}; - -void Editor::onImGuiRender() -{ - static bool showProjectProperties = false; - static bool showProjectScenes = false; - static bool showDemoWindow = false; - static bool showMetricsWindow = false; - - static char projectNameBuff[32]; - static char projectRessourceDirBuff[1024]; - static char projectScriptsDirBuff[1024]; - static char projectBuildDirBuff[1024]; - - static OpenProjectFileDialog openProjectFileDialog; - static SaveProjectFileDialog saveProjectFileDialog; - - ImGui::DockSpaceOverViewport(); - - ImGui::BeginDisabled(openProjectFileDialog.isPresented || saveProjectFileDialog.isPresented); - - if (ImGui::BeginMainMenuBar()) - { - if (ImGui::BeginMenu("File")) - { - ImGui::BeginDisabled(); - if (ImGui::MenuItem("New")) - ; - ImGui::EndDisabled(); - - if (ImGui::MenuItem("Open")) - openProjectFileDialog.present(); - - if (ImGui::MenuItem("Save")) - { - if (m_projectFilePath.empty()) - saveProjectFileDialog.present(m_projectName); - else - saveProject(); - } - - ImGui::EndMenu(); - } - - if (ImGui::BeginMenu("Project")) - { - if (ImGui::MenuItem("Properties")) - { - std::strncpy(projectNameBuff, (const char*)m_projectName, sizeof(projectNameBuff)); - std::strncpy(projectRessourceDirBuff, m_projectRessourcesDir.c_str(), sizeof(projectRessourceDirBuff)); - std::strncpy(projectScriptsDirBuff, m_projectScriptsDir.c_str(), sizeof(projectScriptsDirBuff)); - std::strncpy(projectBuildDirBuff, m_projectBuildDir.c_str(), sizeof(projectBuildDirBuff)); - showProjectProperties = true; - } - if (ImGui::MenuItem("Scene")) - showProjectScenes = true; - - ImGui::BeginDisabled(); - if (ImGui::MenuItem("Run")) - ; - - if (ImGui::MenuItem("Stop")) - ; - ImGui::EndDisabled(); - - ImGui::EndMenu(); - } - - ImGui::BeginDisabled(m_editedScene == nullptr); - if (ImGui::BeginMenu("Scene")) - { - if (ImGui::BeginMenu("New")) - { - if (ImGui::MenuItem("Empty entity")) - m_selectedEntity = m_editedScene->newEntity("new_entity"); - if (ImGui::MenuItem("Cube")) - { - m_selectedEntity = m_editedScene->newEntity("cube"); - m_selectedEntity.emplace(); - m_selectedEntity.emplace(BUILT_IN_CUBE_ASSET_ID); - } - if (ImGui::MenuItem("Light")) - { - m_selectedEntity = m_editedScene->newEntity("light"); - m_selectedEntity.emplace(); - m_selectedEntity.emplace(); - } - ImGui::EndMenu(); - } - ImGui::EndMenu(); - } - ImGui::EndDisabled(); - - if (ImGui::BeginMenu("Debug")) - { - if (ImGui::MenuItem("Show demo window")) - showDemoWindow = true; - if (ImGui::MenuItem("show metrics window")) - showMetricsWindow = true; - ImGui::BeginDisabled(m_scriptLibHandle == nullptr || m_editedScene == nullptr); - if (ImGui::MenuItem("start edited scene")) - { - using makeScriptInstanceFunc = GE::Script* (*)(char*); - - m_runningScene = *m_editedScene; - ECSView(m_runningScene.ecsWorld()).onEach([&](Entity entt, ScriptComponent& scriptComponent){ - auto makeScriptInstance = (makeScriptInstanceFunc)dlsym(m_scriptLibHandle, "makeScriptInstance"); - assert(makeScriptInstance); - scriptComponent.instance = utils::SharedPtr