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.
+
-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 | [](https://github.com/Thomas-Chqt/Game-Engine/actions/workflows/macos-metal.yml) | [](https://github.com/Thomas-Chqt/Game-Engine/actions/workflows/macos-vulkan.yml) |
+| Windows | N/A | [](https://github.com/Thomas-Chqt/Game-Engine/actions/workflows/windows.yml) |
+| Linux | N/A | [](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