diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 5f229265..f139a319 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -1,4 +1,4 @@ -name: Linux Build Tests +name: Linux Platform Build on: [pull_request] @@ -9,11 +9,8 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Install LLVM - run: wget https://apt.llvm.org/llvm.sh && chmod +x llvm.sh && sudo ./llvm.sh - - name: Install LLVM's C++ Standard Library - run: sudo apt install libc++-17-dev libc++abi-17-dev + run: sudo apt-get install libc++-20-dev libc++abi-20-dev - name: Installing pipx run: sudo apt install pipx @@ -21,24 +18,33 @@ jobs: - name: Installing Prerequisites for Linux run: | sudo apt-get update - sudo apt install lsb-release wget software-properties-common gnupg libgtk2.0-dev libgl1-mesa-dev -y - # sudo apt install libx11-dev libx11-xcb-dev libfontenc-dev libice-dev libsm-dev libxau-dev libxaw7-dev libxt-dev libxtst-dev libxrender-dev libxrandr-dev libxi-dev -y - sudo apt-get install -y libx11-dev libx11-xcb-dev libfontenc-dev libice-dev libsm-dev libxau-dev libxaw7-dev libxt-dev libxtst-dev libxrender-dev libxrandr-dev libxi-dev sudo apt install software-properties-common -y sudo add-apt-repository ppa:deadsnakes/ppa + - name: Install Vulkan SDK + uses: humbletim/setup-vulkan-sdk@v1.2.1 + with: + vulkan-query-version: 1.4.304.1 + vulkan-components: Vulkan-Headers, Vulkan-Loader + vulkan-use-cache: true + - name: Installing Conan - run: pipx install "conan>=2.10.1" + run: pipx install "conan>=2.18.1" - name: Setting up Conan Profiles run: conan config install -sf profiles/x86_64/linux/ -tf profiles https://github.com/engine3d-dev/conan-config.git - - name: Installing project dependencies - run: conan remote add engine3d-conan https://libhal.jfrog.io/artifactory/api/conan/engine3d-conan - - - name: Cloning Atlas repository - run: git clone https://github.com/engine3d-dev/TheAtlasEngine - - - name: Building Atlas - run: conan build . -b missing -c tools.system.package_manager:sudo=True -c tools.system.package_manager:mode=install + - name: Setting up Conan + run: | + conan config install https://github.com/engine3d-dev/conan-config.git + conan atlas setup + + - name: Creating Release build for TheAtlasEngine + run: conan atlas create . -s build_type=Release + + - name: Creating Debug build for TheAtlasEngine + run: conan atlas create . -s build_type=Debug + + - name: Creating MinSizeRel build for TheAtlasEngine + run: conan atlas create . -s build_type=MinSizeRel diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml index c8d17e03..2c52c1f0 100644 --- a/.github/workflows/mac.yml +++ b/.github/workflows/mac.yml @@ -1,4 +1,4 @@ -name: Macos Build Tests +name: Macos Platform Build on: [pull_request] @@ -14,10 +14,20 @@ jobs: - name: Install LLVM run: | - brew install python pipx llvm@17 + brew install python pipx + + - name: Install Vulkan SDK + uses: humbletim/setup-vulkan-sdk@v1.2.1 + with: + vulkan-query-version: 1.4.304.1 + vulkan-components: Vulkan-Headers, Vulkan-Loader + vulkan-use-cache: true + - name: Installing conan - run: pipx install "conan>=2.10.2" + run: | + pipx install "conan>=2.18.2" + pipx upgrade conan - name: Installing clang-tidy run: sudo ln -s $(brew --prefix llvm)/bin/clang-tidy /usr/local/bin/ @@ -25,14 +35,16 @@ jobs: - name: Install Rosetta run: /usr/sbin/softwareupdate --install-rosetta --agree-to-license - - name: Setting up Conan profile - run: conan config install -sf profiles/armv8/mac/ -tf profiles https://github.com/engine3d-dev/conan-config.git - - - name: Installing Atlas repositories - run: conan remote add engine3d-conan https://libhal.jfrog.io/artifactory/api/conan/engine3d-conan + - name: Setting up Conan configuration + run: | + conan config install https://github.com/engine3d-dev/conan-config.git + conan atlas setup - - name: Cloning Atlas repository - run: git clone https://github.com/engine3d-dev/TheAtlasEngine + - name: Creating Release build for TheAtlasEngine + run: conan atlas create . -s build_type=Release + + - name: Creating Debug build for TheAtlasEngine + run: conan atlas create . -s build_type=Debug - - name: Building the project - run: conan build . -b missing + - name: Creating MinSizeRel build for TheAtlasEngine + run: conan atlas create . -s build_type=MinSizeRel \ No newline at end of file diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 46be8767..3f7701c4 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -1,4 +1,4 @@ -name: Windows Build Tests +name: Windows Platform Build on: [pull_request, workflow_dispatch] @@ -8,44 +8,40 @@ jobs: runs-on: windows-latest steps: - uses: actions/checkout@v4 - - # - name: Setting up C++ on Windows - # shell: pwsh - # run: winget install Microsoft.VisualStudio.2022.BuildTools --override "--add Microsoft.VisualStudio.Workload.VCTools --includeRecommended -p --installWhileDownloading" - name: Installing Choco shell: pwsh run: Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1')) - - name: Install tooling with Choco + - name: Installing Git shell: pwsh - run: | - choco install cmake.install --version=3.31.6 - cmake --version - choco install llvm --version=20.1.4 - clang++ --version - choco install git make mingw - - # - name: Installing python - # run: choco install python --version=3.12.2 + run: choco install git + + - name: Install Vulkan SDK + uses: humbletim/setup-vulkan-sdk@v1.2.1 + with: + vulkan-query-version: 1.4.304.1 + vulkan-components: Vulkan-Headers, Vulkan-Loader + vulkan-use-cache: true - name: Pip installing conan shell: pwsh run: pip install conan - - # - name: Installing vulkan - # shell: pwsh - # run: winget install --id=KhronosGroup.VulkanSDK -e - - name: Setting up conan profiles + - name: Setting up Conan configuration shell: pwsh - run: conan config install -sf profiles/x86_64/Windows/ -tf profiles https://github.com/engine3d-dev/conan-config.git + run: | + conan config install https://github.com/engine3d-dev/conan-config.git + conan atlas setup - - name: Setting up packages + - name: Creating Release build for TheAtlasEngine shell: pwsh - run: conan remote add engine3d-conan https://libhal.jfrog.io/artifactory/api/conan/engine3d-conan + run: conan atlas create . -s build_type=Release - - name: Running Test Cases + - name: Creating Debug build for TheAtlasEngine shell: pwsh - run: conan build . -b missing - + run: conan atlas create . -s build_type=Debug + + - name: Creating MinSizeRel build for TheAtlasEngine + shell: pwsh + run: conan atlas create . -s build_type=MinSizeRel \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d56cfc1..80f3a972 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,11 @@ -cmake_minimum_required(VERSION 3.27) -project(atlas C CXX) -set(ENGINE_INCLUDE_DIR ${CMAKE_CURRENT_LIST_DIR}/atlas) +cmake_minimum_required(VERSION 4.0) + +# Generate compile commands for anyone using our libraries. +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +set(CMAKE_COLOR_DIAGNOSTICS ON) + +project(atlas LANGUAGES CXX) + # TODO: Remove this once shaderc in the github CI is fixed option(USE_SHADERC "[${PROJECT_NAME}] Enabling shaderc" OFF) @@ -25,32 +30,203 @@ else() message(STATUS "ENABLE_TESTS_ONLY macro not enabled") endif() -build_core_library( - DIRECTORIES src +# The variable CLANG_FORMAT_PATH is now defined automatically by conan_toolchain.cmake +if(EXISTS "${CLANG_FORMAT_PATH}") + message(STATUS "${CLANG_FORMAT_PATH} found!") +else() + message(WARNING "${CLANG_FORMAT_PATH} not found!") +endif() + +if(EXISTS "${CLANG_TIDY_PATH}") + message(STATUS "${CLANG_TIDY_PATH} found!") +else() + message(WARNING "${CLANG_TIDY_PATH} not found!") +endif() + +if(EXISTS "${CMAKE_PATH}") + message(STATUS "${CMAKE_PATH} found!") +else() + message(WARNING "${CMAKE_PATH} not found!") +endif() + +add_library(${PROJECT_NAME} STATIC) +# add_subdirectory(editor) - ENABLE_TESTS ${ENABLE_TESTS_ONLY} +if(${ENABLE_TESTS_ONLY}) + build_unit_test( + TEST_SOURCES + tests/main.test.cpp + tests/basic_add.test.cpp + tests/entity_component_system.test.cpp + tests/math.test.cpp + tests/scene.test.cpp - UNIT_TEST_SOURCES + LINK_PACKAGES + atlas + ) +endif() - tests/main.test.cpp - tests/basic_add.test.cpp - tests/entity_component_system.test.cpp - tests/math.test.cpp - tests/scene.test.cpp - tests/jolt_type_conversion.test.cpp - tests/jolt_engine.test.cpp +set_packages( PACKAGES + glfw3 + Vulkan + glm + spdlog + imguidocking + Jolt + yaml-cpp + stb + flecs + tinyobjloader ${SHADERC_PACKAGE} watcher vulkan-cpp + nfd LINK_PACKAGES + glfw + Vulkan::Vulkan + glm::glm + spdlog::spdlog + imguidocking::imguidocking + + Jolt::Jolt + yaml-cpp + stb::stb + flecs::flecs_static tinyobjloader::tinyobjloader ${SHADERC_LINK_PACKAGE} watcher::watcher - vulkan-cpp::vulkan-cpp + vulkan-cpp + nfd::nfd ) -install(TARGETS ${PROJECT_NAME}) +if(APPLE) +target_compile_options( + ${PROJECT_NAME} + PUBLIC + -g -Wall -Wextra -Wno-missing-field-initializers -Wshadow +) +else() +target_compile_options( + ${PROJECT_NAME} + PUBLIC + -g -Wall -Wextra -Wno-missing-field-initializers -Wshadow -msse4.1 -mavx +) +endif() + +target_sources(${PROJECT_NAME} PUBLIC + FILE_SET CXX_MODULES + TYPE CXX_MODULES + FILES + + # core + # atlas/atlas.cppm + atlas/core/api.cppm + atlas/core/common.cppm + atlas/core/utilities/types.cppm + atlas/core/utilities/utilities.cppm + atlas/core/core.cppm + atlas/core/logger.cppm + atlas/core/application.cppm + atlas/core/window.cppm + + # math + atlas/core/math/types.cppm + atlas/core/math/math.cppm + + # scenes + atlas/core/scene/components.cppm + atlas/core/scene/game_object.cppm + atlas/core/scene/scene.cppm + atlas/core/scene/world.cppm + atlas/core/scene/system_registry.cppm + + atlas/core/utilities/state.cppm + atlas/core/utilities/poll_state.cppm + + # event + atlas/core/event/event.cppm + atlas/core/event/bus.cppm + atlas/core/event/listener.cppm + atlas/core/event/keys.cppm + atlas/core/event/mouse_codes.cppm + atlas/core/event/joystick_codes.cppm + atlas/core/event/types.cppm + + # editor + atlas/core/editor/dockspace.cppm + atlas/core/editor/menu_item.cppm + atlas/core/ui/widgets.cppm + atlas/core/filesystem/file_dialog.cppm + + # serialize + atlas/core/serialize/types.cppm + atlas/core/serialize/serializer.cppm + + # renderer + atlas/renderer/context_loader.cppm + atlas/renderer/renderer.cppm + + atlas/drivers/drivers.cppm + atlas/drivers/graphics_context.cppm + + atlas/drivers/renderer_system.cppm + + + atlas/drivers/vulkan/hash.cppm + atlas/drivers/vulkan/mesh.cppm + atlas/drivers/vulkan/shader_resource_group.cppm + atlas/drivers/vulkan/render_system.cppm + atlas/drivers/vulkan/uniforms.cppm + + atlas/drivers/vulkan/imgui_context.cppm + + # physics + atlas/drivers/physics_context.cppm + atlas/physics/types.cppm + atlas/physics/physics_engine.cppm + + # jolt_cpp + atlas/drivers/jolt_cpp/math.cppm + atlas/drivers/jolt_cpp/broad_phase.cppm + atlas/drivers/jolt_cpp/contact_listener.cppm + atlas/drivers/jolt_cpp/context.cppm + + # drivers + # atlas/drivers/window_context.cppm + # atlas/drivers/vulkan/vulkan.cppm + atlas/drivers/vulkan/utilities.cppm + atlas/drivers/vulkan/instance_context.cppm + atlas/drivers/vulkan/window_context.cppm + atlas/drivers/vulkan/physical_device.cppm + atlas/drivers/vulkan/device.cppm + atlas/drivers/vulkan/swapchain.cppm +) + +target_sources(${PROJECT_NAME} PUBLIC + FILE_SET CXX_MODULES + TYPE CXX_MODULES + PRIVATE atlas/core/entry_point/main.cpp +) + +target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_23) + +# install(TARGETS ${PROJECT_NAME}) +install( + TARGETS ${PROJECT_NAME} + EXPORT atlas_targets + FILE_SET CXX_MODULES DESTINATION "." + LIBRARY DESTINATION "lib" + ARCHIVE DESTINATION "lib" + CXX_MODULES_BMI DESTINATION "bmi" +) + +install( + EXPORT atlas_targets + FILE "atlas-config.cmake" + DESTINATION "lib/cmake" + CXX_MODULES_DIRECTORY "cxx-modules" +) diff --git a/README.md b/README.md index 93d5da00..e67be14d 100644 --- a/README.md +++ b/README.md @@ -5,40 +5,35 @@ [![GitHub forks](https://img.shields.io/github/forks/engine3d-dev/TheAtlasEngine.svg)](https://github.com/engine3d-dev/TheAtlasEngine/network) [![GitHub issues](https://img.shields.io/github/issues/engine3d-dev/TheAtlasEngine.svg)](https://github.com/engine3d-dev/TheAtlasEngine/issues) -Open-source custom C++ 3D game engine which uses Vulkan for building games. +An open-source 3D game engine using C++ and Vulkan with native C++20 module support. # Setup Development Environment -TheAtlasEngine uses Conan the C++ package manager to manage our dependencies. - -Before you build the project, make sure to check the [getting start](https://engine3d-dev.github.io/0.1/getting_started/) page. +New here? We use the C++ package manager, Conan. Follow the [Getting Started Guide](https://engine3d-dev.github.io/0.1/getting_started/) to setup your environment. ## How to Build the Editor -Since there is not a way to set for building the editor. In the CMakeLists.txt file. +Since there isn't a way to officially to building the editor. In the CMakeLists.txt file. -Just add `editor` into your CMakeLists.txt file as shown in this example to build the editor. +Just uncomment the `editor` into your CMakeLists.txt file as shown in this example to build the editor. ```Cmake -build_core_library( - DIRECTORIES src editor - # ... -) +add_subdirectory(editor) ``` ## Building the Project > [!TIP] -> `-b missing` is to install any missing dependencies necessary to build TheAtlasEngine successfully. -> `-s build_type=Debug` is to compile as a debug build when developing on the project. +> `-s build_type=Debug` is recommended to compile the project as a Debug build for development. +> For development to disable the testing environment. ``` -conan build . -b missing -s build_type=Debug +conan atlas build . -s build_type=Debug -o enable_tests_only=False ``` ## Executable Location -After building TheAtlasEngine and the dependencies have been built successful. These are three locations where the editor executable is located at. +After building TheAtlasEngine has been successful. Your executable will be executed via the following path layout. * Debug build will follow with, `./build/Debug/` * Release build will follow with, `./build/Release/` @@ -67,7 +62,7 @@ This was the first game that we made with TheAtlasEngine when the project first ## Wallace and Grommit Rocket Game ### Description -Part of the SFSU Game Dev Club's Game Jam of Spring 2025. We developed a flappy-bird-like game mechanic rocket game. This is inspired by a few youtubers who also made a similar game. One of the YouTuber's named TheCherno. +Developed during the SFSU Game Dev Club Spring 2025 Game Jam, this project serves as a technical demonstration of TheAtlasEngine core capabilities. Inspired by similar projects from creators like TheCherno -- to test the game engine's capabilities. * [itch.io link](https://niccoll-dyson.itch.io/wallace-and-grommit-spaceship-game) to out game jam submission * GitHub [repository link](https://github.com/SpinnerX/3d-rocket-game) diff --git a/atlas/core/api.cppm b/atlas/core/api.cppm new file mode 100644 index 00000000..0b01700b --- /dev/null +++ b/atlas/core/api.cppm @@ -0,0 +1,12 @@ +module; + +#include + +export module atlas.graphics_api; + + +export namespace atlas { + enum class graphics_api : uint8_t { + vulkan, dx11, fx12, undefined + }; +}; \ No newline at end of file diff --git a/atlas/core/api.hpp b/atlas/core/api.hpp deleted file mode 100644 index 986c853f..00000000 --- a/atlas/core/api.hpp +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -namespace atlas { - enum api { vulkan, dx11, fx12, undefined }; - -}; \ No newline at end of file diff --git a/atlas/core/application.cppm b/atlas/core/application.cppm new file mode 100644 index 00000000..b4d8db49 --- /dev/null +++ b/atlas/core/application.cppm @@ -0,0 +1,328 @@ +module; + +#include +#include +#define GLM_ENABLE_EXPERIMENTAL +#include +#include +#include +#include +#include +#include +#include +#include + +export module atlas.application; + +import atlas.core.utilities; +import atlas.core.utilities.poll_state; +import atlas.window; +import atlas.drivers; +import atlas.renderer.context_loader; +import atlas.core.event; +import atlas.drivers.renderer_system; +import atlas.renderer; +import atlas.drivers.vulkan.instance_context; +import atlas.core.scene; +import atlas.core.scene.world; +import atlas.core.scene.system_registry; +import atlas.core.scene.components; + +import atlas.core.math; +import atlas.drivers.vulkan.imgui_context; +import vk; +import atlas.drivers.graphics_context; + +export namespace atlas { + + /** + * @brief application properties settings for the window + */ + struct application_settings { + std::string name = "Undefined"; + uint32_t width = 0; + uint32_t height = 0; + glm::vec4 background_color = { 1.f, 0.5f, 0.5f, 1.f }; + }; + + /** + * @brief represents a single application that gets created by the engine + * internally + * + * There is only ever going to be one application tied to the engine's + * runtime. As the application is given responsibilities of preloading, + * pre-initialization any sort of utilities required by the engine, and any + * form of post-cleanup when the user requests the application to close. + * + */ + class application { + public: + /** + * @brief constructs a new application + * @param p_settings is the specific application settings to configure + * how the application may be setup + */ + application(ref p_context, const application_settings& p_params) { + console_log_info("application(const application_settings&) initialized!!!"); + + window_params params = { + .width = p_params.width, + .height = p_params.height, + .name = p_params.name, + }; + m_window = initialize_window(p_context, params, graphics_api::vulkan); + event::set_window_size(static_cast(*m_window)); + + m_renderer = initialize_renderer(p_context, graphics_api::vulkan, params, m_window->current_swapchain().image_size(), "Renderer"); + m_renderer->set_background_color(p_params.background_color); + + m_ui_context = vulkan::imgui_context(p_context->handle(), m_window->current_swapchain(), *m_window); + + // vulkan::instance_context::submit_resource_free([this](){ + // m_ui_context.destroy(); + // }); + + p_context->submit_resource_free([this](){ + m_ui_context.destroy(); + }); + + s_instance = this; + } + + ~application() { + m_window->close(); + } + + /** + * @return the delta time as a float for giving you the timestep every + * frame + */ + static float delta_time() { + return s_instance->m_delta_time; + } + + /** + * @brief Explicitly is used to execute the application's mainloop + */ + void execute() { + console_log_info("Executing game mainloop!!!"); + + auto start_time = std::chrono::high_resolution_clock::now(); + m_renderer->preload(m_window->current_swapchain().swapchain_renderpass()); + + invoke_start(); + + ref current_world = system_registry::get_world("Editor World"); + ref current_scene = current_world->get_scene("LevelScene"); + flecs::world current_world_scope = *current_scene; + + /* + - flecs::system is how your able to schedule changes for given + portions of data in this case the projection/view matrices are only + being changed when flecs::world::progress(g_delta_time) is being + invoked within the mainloop + current_world_scope.system() + + - When users do object->add>(), this automatically gets invoked by the + .system<...> that gets invoked by the mainloop. + */ + current_world_scope + .system, + transform, + perspective_camera>() + .each([&](flecs::pair p_pair, + transform& p_transform, + perspective_camera& p_camera) { + float aspect_ratio = m_window->aspect_ratio(); + if (!p_camera.is_active) { + return; + } + + p_pair->projection = glm::mat4(1.f); + + p_pair->projection = + glm::perspective(glm::radians(p_camera.field_of_view), + aspect_ratio, + p_camera.plane.x, + p_camera.plane.y); + p_pair->projection[1][1] *= -1; + p_pair->view = glm::mat4(1.f); + + // This is converting a glm::highp_vec4 to a glm::quat + glm::quat quaternion = to_quat(p_transform.quaternion); + + p_pair->view = + glm::translate(p_pair->view, p_transform.position) * + glm::mat4_cast(quaternion); + + p_pair->view = glm::inverse(p_pair->view); + }); + + /* + - Currently how this works is we query with anything that has a + flecs::pair + - This tells the ecs flecs what to do query for in regards to + specific objects that are a camera + - in the tag:: namespace, this is to imply components that are empty + and just represent tags, to specify their uses. + */ + auto query_camera_objects = + current_scene + ->query_builder, + perspective_camera>() + .build(); + + + + while(m_window->available()) { + auto current_time = std::chrono::high_resolution_clock::now(); + m_delta_time = std::chrono::duration(current_time - start_time).count(); + start_time = current_time; + + event::flush_events(); + + // Progresses the flecs::world by one tick (or replaced with using + // the delta time) + // This also invokes the following system call before the + // mainloop + current_world_scope.progress(m_delta_time); + + m_current_frame_index = m_window->acquired_next_frame(); + + // Current commands that are going to be iterated through + // Prevents things like stalling so the CPU doesnt have to wait for + // the GPU to fully complete before starting on the next frame + // Command buffer uses this to track the frames to process its commands. + // current_frame = (acquired_next_frame + 1) % frames_in_flight; + auto current_frame = (m_current_frame_index + 1) % 2; + + // TODO: Going to need to figure out where to put this + // Added this here because to ensure the handlers being used by the + // renderer is in sync when swapchain is resized + ::vk::command_buffer currently_active = m_window->active_command(current_frame); + + invoke_physics_update(); + + invoke_on_update(m_delta_time); + + invoke_defer_update(); + // We want this to be called after late update + // This queries all camera objects within the camera system + // Update -- going to be removing camera system in replacement of + // just simply using flecs::system to keep it simple for the time + query_camera_objects.each( + [&](flecs::entity, + flecs::pair p_pair, + perspective_camera& p_camera) { + if (!p_camera.is_active) { + return; + } + + m_proj_view = p_pair->projection * p_pair->view; + }); + + // TODO: Introduce scene renderer that will make use of the + // begin/end semantics for setting up tasks during pre-frame + // operations + // renderer begin to indicate when a start of the frame to start + // processing specific tasks that either need to be computed or + // pre-defined before the renderer does something with it. + // TODO: Add scene_manager to coordinate what to process + // before frame preparation + auto current_framebuffer =m_window->current_swapchain().active_framebuffer(m_current_frame_index); + + m_renderer->begin_frame( + currently_active, + m_window->current_swapchain().settings(), + m_window->current_swapchain().swapchain_renderpass(), + current_framebuffer, + m_proj_view, + m_current_frame_index); + + // TODO: vk:imgui_context will have its own renderpass, command + // buffers, and framebuffers specifically for UI-widgets + viewport + m_ui_context.begin(currently_active, m_current_frame_index); + + // execute UI logic + invoke_ui_update(); + + m_ui_context.end(); + m_renderer->end_frame(); + + /* + TODO -- have m_window present this to the screen, eventually + m_renderer should just fetch the images in the order to offload + to the swapchain for rendering. + + Where each image has gone through different phases of the + renderpass onto the final image + */ + + std::array commands = { + currently_active, + }; + m_window->current_swapchain().submit(commands); + + // Presents to the swapchain to display to screen + m_window->present(m_current_frame_index); + + } + + } + + /** + * @brief Performs any post cleanup when user requests the application + * to close + */ + void post_destroy() { + console_log_info("Executing post cleanup!!!"); + } + + /** + * @brief we only ever have one window + * + * This static function was a means to getting access to the window to + * perform any operations or request any data the window may have to + * provide + */ + // static window& get_window() { return *s_instance->m_window; } + + /* Retrieves the current selected graphics API */ + /** + * @return the currently specified API. + */ + static graphics_api current_api() { + return graphics_api::vulkan; + } + + /* Returns the currently selected swapchain */ + /** + * @brief gives you the current swapchain handle + * + * TODO: This is not actually needed, and should be removed + */ + VkSwapchainKHR get_current_swapchain() { + return m_window->current_swapchain(); + } + + protected: + [[nodiscard]] ref renderer_instance() const { + return m_renderer; + } + + private: + float m_delta_time = 0.f; + ref m_window; + // vulkan::instance_context m_instance_handle_test; + // std::optional m_instance_handle_test; + ref m_renderer = nullptr; + glm::mat4 m_proj_view; + uint32_t m_current_frame_index = -1; + vulkan::imgui_context m_ui_context; + static application* s_instance; + }; + + application* application::s_instance = nullptr; +}; \ No newline at end of file diff --git a/atlas/core/application.hpp b/atlas/core/application.hpp deleted file mode 100644 index 840ef1d7..00000000 --- a/atlas/core/application.hpp +++ /dev/null @@ -1,145 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include - -namespace atlas { - - /** - * @brief application properties settings for the window - */ - struct application_settings { - std::string name = "Undefined"; - uint32_t width = 0; - uint32_t height = 0; - glm::vec4 background_color = { 1.f, 0.5f, 0.5f, 1.f }; - }; - - /** - * @brief represents a single application that gets created by the engine - * internally - * - * There is only ever going to be one application tied to the engine's - * runtime. As the application is given responsibilities of preloading, - * pre-initialization any sort of utilities required by the engine, and any - * form of post-cleanup when the user requests the application to close. - * - */ - class application { - public: - /** - * @brief constructs a new application - * @param p_settings is the specific application settings to configure - * how the application may be setup - */ - application(const application_settings& p_settings); - - ~application(); - - /** - * @return the delta time as a float for giving you the timestep every - * frame - */ - static float delta_time(); - - /** - * TODO: Need to remove this as it is not needed here - * - * Originally was used to handle the fixed physics timestep, but that - * can be handled else where. - */ - static float physics_step(); - - /** - * @brief Explicitly is used to execute the application's mainloop - */ - void execute(); - - /** - * @brief Performs any post cleanup when user requests the application - * to close - */ - void post_destroy(); - - /** - * @brief we only ever have one window - * - * This static function was a means to getting access to the window to - * perform any operations or request any data the window may have to - * provide - */ - static window& get_window() { return *s_instance->m_window; } - - /* Retrieves the current selected graphics API */ - /** - * @return the currently specified API. - */ - static api current_api(); - - /* Returns the currently selected swapchain */ - /** - * @brief gives you the current swapchain handle - * - * TODO: This is not actually needed, and should be removed - */ - VkSwapchainKHR get_current_swapchain(); - - /** - * @brief destroys the application completely - * - * TODO: Not make this static because you should not allow for this to - * be a direct calls users can have access to - */ - static void destroy(); - - /** - * @brief gives you the current aspect ratio based on the dimensions of - * the window - * - * @return a float which is just a static_cast(width / height); - */ - static float aspect_ratio(); - - /** - * @brief Gives you the current frame index which is used for the Vulkan - * renderer - * - * Provides information such as what is the current frame index to - * correcly index the commands that get submitted to the GPU for - * processing commands (tasks) - * - * @return uint32_t - */ - static uint32_t current_frame(); - - /** - * @brief Intended to get the image size so when you use current_frame() - * to get thje frame index, that you are not making an attempt at - * accessing anything outside of the frame. - * - * @return uint32_t - */ - static uint32_t image_size(); - - protected: - [[nodiscard]] ref renderer_instance() const { - return m_renderer; - } - - private: - void set_current_api(api api); - - private: - float m_delta_time = 0.f; - ref m_window; - ref m_renderer = nullptr; - glm::mat4 m_proj_view; - uint32_t m_current_frame_index = -1; - vk::imgui_context m_ui_context; - static application* s_instance; - }; - - ref initialize_application(); -}; \ No newline at end of file diff --git a/atlas/core/core.hpp b/atlas/core/common.cppm similarity index 58% rename from atlas/core/core.hpp rename to atlas/core/common.cppm index f71ceb87..d4a18e84 100644 --- a/atlas/core/core.hpp +++ b/atlas/core/common.cppm @@ -1,39 +1,41 @@ -#pragma once -#include +module; + #include -#include +#include + +export module atlas.common; -namespace atlas { +export namespace atlas { /** * @brief alias to atlas::memory::strong_ptr */ - template - using strong_ref = memory::strong_ptr; + // template + // using strong_ref = memory::strong_ptr; - /** - * @brief construct strong_ptr through - * atlas::memory::create_strong_ptr(...); - * - * @tparam ...Args is a template pack for packing in parameters - * @tparam T is the type of object to construct strong_ptr with - * @param p_allocator is a polymorphic allocator when creating strong_ptr's. - * @param ...Args is an argument pack for packing to deduce the types that - * is specified by object of type T - */ - template - strong_ref create_strong_ref( - std::pmr::polymorphic_allocator<> p_allocator, - Args&&... args) { - return memory::make_strong_ptr(p_allocator, - std::forward(args)...); - } + // /** + // * @brief construct strong_ptr through + // * atlas::memory::create_strong_ptr(...); + // * + // * @tparam ...Args is a template pack for packing in parameters + // * @tparam T is the type of object to construct strong_ptr with + // * @param p_allocator is a polymorphic allocator when creating strong_ptr's. + // * @param ...Args is an argument pack for packing to deduce the types that + // * is specified by object of type T + // */ + // template + // strong_ref create_strong_ref( + // std::pmr::polymorphic_allocator<> p_allocator, + // Args&&... args) { + // return memory::make_strong_ptr(p_allocator, + // std::forward(args)...); + // } /** * @brief alias to memory::optional_ptr */ - template - using optional_ref = memory::optional_ptr; + // template + // using optional_ref = memory::optional_ptr; /** * @brief alias to std::shared_ptr @@ -72,5 +74,4 @@ namespace atlas { constexpr scope create_scope(Args&&... args) { return std::make_unique(std::forward(args)...); } - }; \ No newline at end of file diff --git a/atlas/core/common.hpp b/atlas/core/common.hpp deleted file mode 100644 index 32407a51..00000000 --- a/atlas/core/common.hpp +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -// core includes -#include -#include -#include -#include -#include - -#include - -// imgui UI widgets -#include - -#define GLM_ENABLE_EXPERIMENTAL -#include - -#include -#include -#include - -#include - -#include -#include -#include \ No newline at end of file diff --git a/atlas/core/core.cppm b/atlas/core/core.cppm new file mode 100644 index 00000000..52e1229f --- /dev/null +++ b/atlas/core/core.cppm @@ -0,0 +1,9 @@ +export module core; + +// export import :common; +export import atlas.common; +// export import :application; +// export import :logger; + +export namespace atlas { +}; \ No newline at end of file diff --git a/atlas/core/editor/dockspace.cppm b/atlas/core/editor/dockspace.cppm new file mode 100644 index 00000000..ced6c3b5 --- /dev/null +++ b/atlas/core/editor/dockspace.cppm @@ -0,0 +1,68 @@ +module; + +#include +#include +#include + +#include + +export module atlas.core.editor.dockspace; + +export namespace atlas::ui { + /** + * @brief dockspace window using imgui + */ + class dockspace { + public: + dockspace() = default; + + void fullscreen(bool p_fullscreen) { + m_fullscreen_enabled = p_fullscreen; + } + + void dockspace_open(bool p_dockspace_open) { + m_is_dockspace_open = p_dockspace_open; + } + + void begin() { + ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_None; + ImGuiWindowFlags window_flags = + ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking; + + if (m_fullscreen_enabled) { + ImGuiViewport* viewport = ImGui::GetMainViewport(); + ImGui::SetNextWindowPos(viewport->Pos); + ImGui::SetNextWindowSize(viewport->Size); + ImGui::SetNextWindowViewport(viewport->ID); + window_flags |= ImGuiWindowFlags_NoTitleBar | + ImGuiWindowFlags_NoCollapse | + ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove; + window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | + ImGuiWindowFlags_NoNavFocus; + } + + if (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode) { + window_flags |= ImGuiWindowFlags_NoBackground; + } + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); + ImGui::Begin("Dockspace Demo", &m_is_dockspace_open, window_flags); + ImGui::PopStyleVar(); + + // Dockspace + ImGuiIO& io = ImGui::GetIO(); + if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable) { + ImGuiID dockspace_id = ImGui::GetID("MyDockspace"); + ImGui::DockSpace(dockspace_id, ImVec2(0.f, 0.f), dockspace_flags); + } + } + + void end() { + ImGui::End(); + } + + private: + bool m_fullscreen_enabled = false; + bool m_is_dockspace_open = false; + }; + +}; \ No newline at end of file diff --git a/atlas/core/editor/dockspace.hpp b/atlas/core/editor/dockspace.hpp deleted file mode 100644 index d00ab1a2..00000000 --- a/atlas/core/editor/dockspace.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once -#include - -namespace atlas::ui { - - /** - * @brief dockspace window using imgui - */ - class dockspace { - public: - dockspace() = default; - - void fullscreen(bool p_fullscreen) { - m_fullscreen_enabled = p_fullscreen; - } - - void dockspace_open(bool p_dockspace_open) { - m_is_dockspace_open = p_dockspace_open; - } - - void begin(); - - void end(); - - private: - bool m_fullscreen_enabled = false; - bool m_is_dockspace_open = false; - }; -}; \ No newline at end of file diff --git a/atlas/core/editor/menu_item.hpp b/atlas/core/editor/menu_item.cppm similarity index 54% rename from atlas/core/editor/menu_item.hpp rename to atlas/core/editor/menu_item.cppm index 355efb42..5a47c81d 100644 --- a/atlas/core/editor/menu_item.hpp +++ b/atlas/core/editor/menu_item.cppm @@ -1,8 +1,14 @@ -#pragma once +module; + #include #include +#include +#include +#include + +export module atlas.core.editor.menu_item; -namespace atlas::ui { +export namespace atlas::ui { struct block { const char* data = nullptr; }; @@ -17,6 +23,7 @@ namespace atlas::ui { private: block m_data; }; + /** * @brief UI wrapper around setting up a menu item group * @@ -40,11 +47,21 @@ namespace atlas::ui { public: menu_item() = default; - void begin(); + void begin() { + if (!ImGui::BeginMenuBar()) { + throw menu_bar_exception("ImGui::BeginMenuBar failed!"); + } + } - void end(); + void end() { + ImGui::EndMenuBar(); + } - void add_child(const std::string& p_name, - const std::function& p_callback); + void add_child(const std::string& p_name, const std::function& p_callback) { + if (ImGui::MenuItem(p_name.c_str())) { + p_callback(); + } + ImGui::Separator(); + } }; }; \ No newline at end of file diff --git a/atlas/core/entry_point/main.cpp b/atlas/core/entry_point/main.cpp new file mode 100644 index 00000000..f2d952b6 --- /dev/null +++ b/atlas/core/entry_point/main.cpp @@ -0,0 +1,38 @@ + +#include +import atlas.application; +import atlas.common; +import atlas.logger; +import atlas.graphics_api; +import atlas.drivers.graphics_context; +import atlas.renderer.context_loader; +import atlas.core.scene.system_registry; + +// Defined in the user-application side +[[nodiscard]] atlas::ref initialize_application( + atlas::ref p_context); + +int +main() { + atlas::console_log_manager manager = atlas::console_log_manager(); + + if (!glfwInit()) { + console_log_fatal("GLFW: Initialization failed!!"); + return -1; + } + + // TODO: Level streamer is going to be replacing system_registry + atlas::ref system = + atlas::create_ref("system"); + atlas::ref context = + atlas::initialize_context("vulkan", atlas::graphics_api::vulkan); + + atlas::ref app = initialize_application(context); + + app->execute(); + + app->post_destroy(); + + context->destroy(); + return 0; +} \ No newline at end of file diff --git a/atlas/core/event/event_bus.hpp b/atlas/core/event/bus.cppm similarity index 90% rename from atlas/core/event/event_bus.hpp rename to atlas/core/event/bus.cppm index 901e14df..9bed5ecd 100644 --- a/atlas/core/event/event_bus.hpp +++ b/atlas/core/event/bus.cppm @@ -1,18 +1,23 @@ -#pragma once -#include +module; + + #include -#include #include +#include +#include + +export module atlas.core.event:bus; -namespace atlas::event { +import :listener; +export namespace atlas::event { /** * @brief Event bus that holds the responsibility to reroute events to the * subscribers of those particular event. */ - class event_bus { + class bus { public: - event_bus() = default; + bus() = default; template void create_listener() { diff --git a/atlas/core/event/event.cppm b/atlas/core/event/event.cppm new file mode 100644 index 00000000..60201d8f --- /dev/null +++ b/atlas/core/event/event.cppm @@ -0,0 +1,211 @@ +module; + +#include +#include +#include +#include +#include + +export module atlas.core.event; + +export import :keys; +export import :mouse_codes; +export import :types; +export import :listener; +export import :bus; + +namespace atlas { + + /** + * @name event.hpp + * @note Actual input polling system to poll in differeny sets of key/mouse + * actions + * @param UpdateEvents handles making sure that all of our events we handle + * have been successfully updated. + * @param GetMousePos just returns the position of our mouse cursor + */ + enum input_state { None, Idle, Pressed, Released }; + + struct joystick_button { + int ID = -1; + std::string Name = ""; + input_state ButtonState = input_state::None; + input_state PreviousButtonState = input_state::None; + }; + + struct joystick_info { + int ID = -1; + std::string JoystickName = "Default"; + std::map Buttons; + std::map ButtonsDown; + std::map AxesOfController; + }; + + static std::map s_controllers; + + using button_id = int; + using controller_id = int; + + // TODO: Probably make this into std::map s_window_viewports? + static GLFWwindow* s_window_address; + + export namespace event { + + bool is_key_pressed(int p_key) { + + // GLFWwindow* window = application::get_window(); + GLFWwindow* window = s_window_address; + + auto state = glfwGetKey(window, static_cast(p_key)); + return (state == GLFW_PRESS); + } + + bool is_key_released(int p_key) { + GLFWwindow* window = s_window_address; + + auto state = glfwGetKey(window, static_cast(p_key)); + return (state == GLFW_RELEASE); + } + + bool is_mouse_pressed(int p_mouse_code) { + GLFWwindow* window = s_window_address; + + auto state = glfwGetMouseButton(window, static_cast(p_mouse_code)); + return (state == GLFW_PRESS); + } + + bool is_mouse_released(int p_mouse_code) { + GLFWwindow* window = s_window_address; + + auto state = glfwGetMouseButton(window, static_cast(p_mouse_code)); + return (state == GLFW_RELEASE); + } + + glm::vec2 cursor_position() { + GLFWwindow* window = s_window_address; + + double x_pos, y_pos; + glfwGetCursorPos(window, &x_pos, &y_pos); + + return { x_pos, y_pos }; + } + + // joystic-specific functions + bool is_joystic_present(int p_controller_id) { + return s_controllers.contains(p_controller_id); + } + + const char* is_joystick_guid(int p_controller_id) { + return glfwGetJoystickGUID(p_controller_id); + } + + float get_joystic_axis(int p_controller_id, int p_button) { + int count; + const float axes = glfwGetJoystickAxes(p_controller_id, &count)[p_button]; + + if (count < p_button) { + return 0.0f; + } + else { + return axes; + } + } + + // bool is_button_pressed(int p_button_id, int p_controller_id) { + // auto controller = s_controllers[p_controller_id]; + // return (controller.Buttons[p_button_id].ButtonState == GLFW_RELEASE); + // } + + // bool is_button_released(button_id p_button, controller_id p_controller) { + // auto selected_controller = s_controllers[p_controller]; + // return (selected_controller.Buttons[p_button].ButtonState == + // input_state::Pressed); + // } + + //! @note FIXME: Make button later + bool is_joystick_button_pressed(int p_button) { + return p_button == GLFW_PRESS; + } + + bool is_joystick_button_released(int p_button) { + return p_button == GLFW_RELEASE; + } + + void wait_for_events() {} + + void set_window_size(GLFWwindow* p_window) { + s_window_address = p_window; + } + + // specfying listening for events + void flush_events() { + glfwPollEvents(); + + // updating joysticks here + // .... + //! @note Must be called per input updated events. In the case either + //! game console disconnects or reconnects + //! @note This will continously check. + //! @note By default GLFW check's up to a total of 16 joystick ID's that + //! are available + //! @note We iterate all 16 joysticks, only using the joystic ID's that + //! are connected + //! @note Then checking for any events from the connected joystick has + //! occurred + // 1 is the first joystick. + // 16 is the last joystick + for (int joystick_id = 0; joystick_id < 16; joystick_id++) { + if (glfwJoystickPresent(joystick_id) == GLFW_TRUE) { + auto& joystick = s_controllers[joystick_id]; + joystick.ID = joystick_id; + joystick.JoystickName = glfwGetJoystickName(joystick_id); + + //! @note We always check how many buttons the joysticks that + //! are connected contain. + int amount_of_buttons = -1; + const unsigned char* buttons = + glfwGetJoystickButtons(joystick_id, &amount_of_buttons); + + // ConsoleLogWarn("Button Size = {}", amount_of_buttons); + + for (int i = 0; i < amount_of_buttons; i++) { + // ConsoleLogFatal("Button {} is ===> {}", i, buttons[i]); + // if(buttons[i] == GLFW_PRESS && !joystick.ButtonsDown[i]){ + if (is_joystick_button_pressed(buttons[i]) && + !joystick.ButtonsDown[i]) { + joystick.Buttons[i].ButtonState = input_state::Pressed; + } + // else if(buttons[i] == GLFW_RELEASE and + // joystick.ButtonsDown[i]){ + else if (is_joystick_button_released(buttons[i]) and + joystick.ButtonsDown[i]) { + joystick.Buttons[i].ButtonState = input_state::Released; + } + + // joystick.ButtonsDown[i] = (buttons[i] == GLFW_PRESS); + joystick.ButtonsDown[i] = + is_joystick_button_pressed(buttons[i]); + } + + int amount_of_axes = -1; + const float* axes = + glfwGetJoystickAxes(joystick_id, &amount_of_axes); + joystick.AxesOfController[joystick_id] = axes; + // ConsoleLogFatal("Axes at for-loop i = {} and Axes value = + // {:.3f}", 0, axes[0]); ConsoleLogFatal("Axes at for-loop i = + // {} and Axes value = {:.3f}", 1, axes[1]); for(int i = 0; i < + // amount_of_axes; i++){ + // ConsoleLogFatal("Axes at for-loop i = {} and Axes value = + // {:.3f}", i, axes[i]); + // } + } + else { + if (is_joystic_present(joystick_id)) { + s_controllers.erase(joystick_id); + } + } + } + } + }; + +}; \ No newline at end of file diff --git a/atlas/core/event/event.hpp b/atlas/core/event/event.hpp deleted file mode 100644 index 9e500658..00000000 --- a/atlas/core/event/event.hpp +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include - -namespace atlas::event { - /** - * @name event.hpp - * @note Actual input polling system to poll in differeny sets of key/mouse - * actions - * @param UpdateEvents handles making sure that all of our events we handle - * have been successfully updated. - * @param GetMousePos just returns the position of our mouse cursor - */ - enum input_state { None, Idle, Pressed, Released }; - - struct joystick_button { - int ID = -1; - std::string Name = ""; - input_state ButtonState = input_state::None; - input_state PreviousButtonState = input_state::None; - }; - - struct joystick_info { - int ID = -1; - std::string JoystickName = "Default"; - std::map Buttons; - std::map ButtonsDown; - std::map AxesOfController; - }; - - bool is_key_pressed(int p_key); - - bool is_key_released(int p_key); - - bool is_mouse_pressed(int p_mouse_code); - bool is_mouse_released(int p_mouse_code); - - glm::vec2 cursor_position(); - - // joystic-specific functions - - bool is_joystic_present(int p_controller_id); - - const char* is_joystick_guid(int p_controller_id); - - float get_joystic_axis(int p_controller_id, int p_button); - - // bool is_button_pressed(int p_button_id, int p_controller_id); - - // bool is_button_released(int p_button_id, int p_controller_id); - - //! @note FIXME: Make button later - bool is_joystick_button_pressed(int p_button); - - bool is_joystick_button_released(int p_button); - - // specific for listening events - void update_events(); - - void wait_for_events(); -}; // namespace atlas::event \ No newline at end of file diff --git a/atlas/core/event/joystick_codes.hpp b/atlas/core/event/joystick_codes.cppm similarity index 97% rename from atlas/core/event/joystick_codes.hpp rename to atlas/core/event/joystick_codes.cppm index 07d08f3e..074820da 100644 --- a/atlas/core/event/joystick_codes.hpp +++ b/atlas/core/event/joystick_codes.cppm @@ -1,6 +1,9 @@ -#pragma once +module; + #include +export module atlas.core.event:joystick_codes; + namespace atlas::event { enum JoystickCodes : int { Joystick1 = 0, diff --git a/atlas/core/event/key_codes.hpp b/atlas/core/event/keys.cppm similarity index 98% rename from atlas/core/event/key_codes.hpp rename to atlas/core/event/keys.cppm index 57046a0b..6db80291 100644 --- a/atlas/core/event/key_codes.hpp +++ b/atlas/core/event/keys.cppm @@ -1,7 +1,11 @@ -#pragma once -#include +module; -namespace atlas::event { +#include + +export module atlas.core.event:keys; + + +export namespace atlas::event { enum Key : uint32_t { // From glfw3.h Space = 32, @@ -139,6 +143,8 @@ namespace atlas::event { // From glfw3.h + +export { inline constexpr uint32_t key_space = ::atlas::event::Key::Space; inline constexpr uint32_t key_apostrophe = ::atlas::event::Key::Apostrophe; /* ' */ @@ -271,4 +277,5 @@ inline constexpr uint32_t key_right_shift = ::atlas::event::Key::RightShift; inline constexpr uint32_t key_right_control = ::atlas::event::Key::RightControl; inline constexpr uint32_t key_right_alt = ::atlas::event::Key::RightAlt; inline constexpr uint32_t key_right_super = ::atlas::event::Key::Rightsuper; -inline constexpr uint32_t key_menu = ::atlas::event::Key::Menu; \ No newline at end of file +inline constexpr uint32_t key_menu = ::atlas::event::Key::Menu; +}; \ No newline at end of file diff --git a/atlas/core/event/event_listener.hpp b/atlas/core/event/listener.cppm similarity index 91% rename from atlas/core/event/event_listener.hpp rename to atlas/core/event/listener.cppm index fefe9d16..f6560408 100644 --- a/atlas/core/event/event_listener.hpp +++ b/atlas/core/event/listener.cppm @@ -1,12 +1,12 @@ -#pragma once +module; + #include -#include -#include #include -namespace atlas::event { +export module atlas.core.event:listener; - /** +export namespace atlas::event { + /** * @brief Generic event listener of event type UEvent * * Listener that can have a different representation of a specific type of diff --git a/atlas/core/event/mouse_codes.hpp b/atlas/core/event/mouse_codes.cppm similarity index 92% rename from atlas/core/event/mouse_codes.hpp rename to atlas/core/event/mouse_codes.cppm index 2533d773..e1addddc 100644 --- a/atlas/core/event/mouse_codes.hpp +++ b/atlas/core/event/mouse_codes.cppm @@ -1,6 +1,9 @@ -#pragma once +module; + #include +export module atlas.core.event:mouse_codes; + namespace atlas::event { enum Mouse : uint32_t { // From glfw3.h @@ -21,6 +24,7 @@ namespace atlas::event { } // namespace atlas +export { inline constexpr uint32_t mouse_button_0 = ::atlas::event::Mouse::Button0; inline constexpr uint32_t mouse_button_1 = ::atlas::event::Mouse::Button1; inline constexpr uint32_t mouse_button_2 = ::atlas::event::Mouse::Button2; @@ -34,4 +38,6 @@ inline constexpr uint32_t mouse_button_left = ::atlas::event::Mouse::ButtonLeft; inline constexpr uint32_t mouse_button_right = ::atlas::event::Mouse::ButtonRight; inline constexpr uint32_t mouse_button_middle = - ::atlas::event::Mouse::ButtonMiddle; \ No newline at end of file + ::atlas::event::Mouse::ButtonMiddle; + +}; \ No newline at end of file diff --git a/atlas/core/event/types.hpp b/atlas/core/event/types.cppm similarity index 88% rename from atlas/core/event/types.hpp rename to atlas/core/event/types.cppm index a1167e1b..b86b9503 100644 --- a/atlas/core/event/types.hpp +++ b/atlas/core/event/types.cppm @@ -1,7 +1,10 @@ -#pragma once +module; + #include -namespace atlas::event { +export module atlas.core.event:types; + +export namespace atlas::event { struct collision_enter { uint64_t entity1; diff --git a/src/atlas/core/filesystem/file_dialog.cpp b/atlas/core/filesystem/file_dialog.cppm similarity index 73% rename from src/atlas/core/filesystem/file_dialog.cpp rename to atlas/core/filesystem/file_dialog.cppm index a89d349f..083b0cca 100644 --- a/src/atlas/core/filesystem/file_dialog.cpp +++ b/atlas/core/filesystem/file_dialog.cppm @@ -1,11 +1,17 @@ -#include -#include +module; + #include +#include +#include + +export module atlas.core.filesystem.file_dialog; -namespace atlas::filesystem { +import atlas.logger; + +export namespace atlas::filesystem { std::string load_from_file_dialog( const std::string& p_filter, - const std::filesystem::path& p_default_path) { + const std::filesystem::path& p_default_path = "Untitled.engine") { char* output_path = nullptr; nfdresult_t result = NFD_OpenDialog( @@ -21,10 +27,10 @@ namespace atlas::filesystem { } std::string save_to_file(const std::string& p_filter, - const std::filesystem::path& p_default) { + const std::filesystem::path& p_default_path = "Untitled.engine") { char* output_path = nullptr; nfdresult_t result = NFD_SaveDialog( - p_filter.c_str(), p_default.string().c_str(), &output_path); + p_filter.c_str(), p_default_path.string().c_str(), &output_path); // if(result == NFD_ERROR){ // return std::string(); diff --git a/atlas/core/filesystem/file_dialog.hpp b/atlas/core/filesystem/file_dialog.hpp deleted file mode 100644 index 10d32359..00000000 --- a/atlas/core/filesystem/file_dialog.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once -#include -#include - -namespace atlas::filesystem { - /** - * @param filter - * @note Defines the filter of items to pick from the file dialog - * @example passing "obj;jpg" will only allow to select these files with - * those extensions - */ - std::string load_from_file_dialog( - const std::string& p_filter, - const std::filesystem::path& p_default_path = "Untitled.engine"); - - std::string save_to_file( - const std::string& p_filter, - const std::filesystem::path& p_default_path = "Untitled.engine"); -}; // namespace atlas::filesystem \ No newline at end of file diff --git a/atlas/core/geometry/mesh.hpp b/atlas/core/geometry/mesh.hpp deleted file mode 100644 index 7c724278..00000000 --- a/atlas/core/geometry/mesh.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once -#include - -namespace atlas { - class mesh { - public: - mesh() = default; - // TODO: Change this because UNUSED!!! - mesh(const std::string& p_filepath); - }; -}; \ No newline at end of file diff --git a/atlas/core/image/stb_image.hpp b/atlas/core/image/stb_image.hpp deleted file mode 100644 index 7125f333..00000000 --- a/atlas/core/image/stb_image.hpp +++ /dev/null @@ -1,2 +0,0 @@ -#pragma once -#include \ No newline at end of file diff --git a/atlas/core/engine_logger.hpp b/atlas/core/logger.cppm similarity index 66% rename from atlas/core/engine_logger.hpp rename to atlas/core/logger.cppm index d6089889..4bb1c093 100644 --- a/atlas/core/engine_logger.hpp +++ b/atlas/core/logger.cppm @@ -1,12 +1,19 @@ -#pragma once -#include -#include -#include -#include -#include +module; + +#include +#include +#include +#include + #include +#include +#include +#include -namespace atlas { +export module atlas.logger; +export import atlas.common; + +export namespace atlas { /** * @brief logger for logging messages to stdout on the console * @@ -15,26 +22,30 @@ namespace atlas { */ class console_log_manager { public: - /** - * @brief initializes the console_log_manager - * - * TODO: Revisit the logger and do some refactoring because the way this - * works should be changed, as I'd prob do this differently now. - */ - static void initialize_logger_manager( - const std::string& pattern = "%^[%T] %n: %v%$"); + console_log_manager(const std::string& p_pattern = "%^[%n] [%T]: %v%$") { + std::println("Constructing console_log_manager!"); + + //! @note Setting up logs for different log stdout's + //! @note Logs for p_tag is logs specific to the game. + m_loggers.insert({ "atlas", spdlog::stdout_color_mt("atlas") }); + m_loggers.insert({ "physics", spdlog::stdout_color_mt("physics") }); + m_loggers.insert({ "vulkan", spdlog::stdout_color_mt("vulkan") }); + m_loggers.insert( + { "assert", spdlog::stdout_color_mt("core assertion") }); - /** - * @brief sets what the current logger to write to the console with - */ - static void set_current_logger( - const std::string& p_tag = "Undefined g_Tag in console_logger"); + m_loggers["atlas"]->set_pattern(p_pattern); + m_loggers["atlas"]->set_level(spdlog::level::trace); - /** - * @brief constructs a new spdlog::logger to write to the console - */ - static void create_new_logger( - const std::string& p_tag = "Undefined Tag"); + m_loggers["physics"]->set_level(spdlog::level::trace); + m_loggers["physics"]->set_pattern(p_pattern); + + m_loggers["vulkan"]->set_level(spdlog::level::trace); + m_loggers["vulkan"]->set_pattern(p_pattern); + + m_loggers["assert"]->set_level(spdlog::level::trace); + m_loggers["assert"]->set_pattern(p_pattern); + s_instance = this; + } /** * @brief retrieves that specific logger if it has been constructed @@ -44,25 +55,30 @@ namespace atlas { * TODO: Should have this throw an exception rather then returning * nullptr */ - static ref get(const std::string& p_tag); + static std::shared_ptr get(const std::string& p_tag) { + return s_instance->m_loggers[p_tag]; + } private: + static console_log_manager* s_instance; // Using an unordered_map to specify through a string what logger to // retrieve to log messages - static std::unordered_map> s_loggers; + std::unordered_map> m_loggers; }; + + // std::unordered_map> console_log_manager::s_loggers; + console_log_manager* console_log_manager::s_instance = nullptr; }; +export { //! @note Console Loggers (These are loggers that write specifically to the //! console, terminal console) -//! @note TODO --- Specify that engine3d will have it's own console terminal -//! that these will be written to. template inline void console_log_trace([[maybe_unused]] spdlog::format_string_t fmt, [[maybe_unused]] T&&... args) { #ifndef ENABLE_TESTS_ONLY - atlas::console_log_manager::get("engine3d") + atlas::console_log_manager::get("atlas") ->trace(fmt, std::forward(args)...); #endif } @@ -72,7 +88,7 @@ inline void console_log_warn([[maybe_unused]] spdlog::format_string_t fmt, [[maybe_unused]] T&&... args) { #ifndef ENABLE_TESTS_ONLY - atlas::console_log_manager::get("engine3d") + atlas::console_log_manager::get("atlas") ->warn(fmt, std::forward(args)...); #endif } @@ -82,7 +98,7 @@ inline void console_log_info([[maybe_unused]] spdlog::format_string_t fmt, [[maybe_unused]] T&&... args) { #ifndef ENABLE_TESTS_ONLY - atlas::console_log_manager::get("engine3d") + atlas::console_log_manager::get("atlas") ->info(fmt, std::forward(args)...); #endif } @@ -92,7 +108,7 @@ inline void console_log_error([[maybe_unused]] spdlog::format_string_t fmt, [[maybe_unused]] T&&... args) { #ifndef ENABLE_TESTS_ONLY - atlas::console_log_manager::get("engine3d") + atlas::console_log_manager::get("atlas") ->error(fmt, std::forward(args)...); #endif } @@ -102,7 +118,7 @@ inline void console_log_fatal([[maybe_unused]] spdlog::format_string_t fmt, [[maybe_unused]] T&&... args) { #ifndef ENABLE_TESTS_ONLY - atlas::console_log_manager::get("engine3d") + atlas::console_log_manager::get("atlas") ->critical(fmt, std::forward(args)...); #endif } @@ -159,4 +175,5 @@ console_log_fatal_tagged([[maybe_unused]] const std::string& p_tag, atlas::console_log_manager::get(p_tag)->critical(fmt, std::forward(args)...); #endif +} } \ No newline at end of file diff --git a/src/atlas/core/math/utilities.cpp b/atlas/core/math/math.cppm similarity index 66% rename from src/atlas/core/math/utilities.cpp rename to atlas/core/math/math.cppm index 8947db55..580bb924 100644 --- a/src/atlas/core/math/utilities.cpp +++ b/atlas/core/math/math.cppm @@ -1,9 +1,13 @@ -#include -#include +module; + +#define GLM_ENABLE_EXPERIMENTAL #include -namespace atlas { +export module atlas.core.math; + +export namespace atlas { + //! @brief converts vec4 to quaterion glm::quat to_quat(const glm::vec4& p_values) { return glm::quat({ p_values.w, @@ -13,6 +17,7 @@ namespace atlas { }); } + //! @brief converts vec3 to quaterion glm::highp_vec4 from_quat(const glm::vec3& p_values) { // converts glm::vec3 rotation to a quaternion returning the // quaternion-converted values to glm::highp_vec4 @@ -21,6 +26,7 @@ namespace atlas { { quaternion.x, quaternion.y, quaternion.z, quaternion.w }); } + //! @brief converts glm::vec3 to glm::vec4 glm::quat to_quat(const glm::vec3& p_values) { return glm::quat(p_values); } @@ -35,42 +41,50 @@ namespace atlas { } namespace math { - + //! @brief shorthand for glm::vec3(0.f, 0.f, 0.f) glm::vec3 zeroes() { return glm::vec3(0.f); } + //! @brief shorthand for glm::vec3(1.f, 1.f, 1.f) glm::vec3 ones() { return glm::vec3(1.f); } + //! @brief rotation equivalent of a zero-vector + // sets to glm::quat(1.f, 0.f, 0.f, 0.f) glm::quat identity() { return glm::quat_identity(); } + //! @brief shorthand for glm::vec3(0.f, 0.f, 1.f) glm::vec3 forward() { return glm::vec3(0.f, 0.f, 1.f); } + //! @brief shorthand for glm::vec3(0.f, 0.f, -1.f) + glm::vec3 backward() { + return glm::vec3(0.f, 0.f, -1.f); + } + + //! @brief shorthand for glm::vec3(1.f, 0.f, 0.f) glm::vec3 right() { return glm::vec3(1.f, 0.f, 0.f); } + //! @brief shorthand for glm::vec3(-1.f, 0.f, 0.f) glm::vec3 left() { return glm::vec3(-1.f, 0.f, 0.f); } - glm::vec3 backward() { - return glm::vec3(0.f, 0.f, -1.f); - } - + //! @brief shorthand for glm::vec3(0.f, 1.f, 0.f) glm::vec3 up() { return glm::vec3(0.f, 1.f, 0.f); } + //! @brief shorthand for glm::vec3(0.f, -1.f, 0.f) glm::vec3 down() { return glm::vec3(0.f, -1.f, 0.f); } - }; - + } }; \ No newline at end of file diff --git a/atlas/core/math/math.hpp b/atlas/core/math/math.hpp deleted file mode 100644 index aab50a39..00000000 --- a/atlas/core/math/math.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#include -#include -#include - -namespace atlas { - - class interpolation { - public: - template - static T linear_interpolate(T start, - T end, - const std::function& function, - float t) { - float l_adjusted_time = 0.0f; - if (!function) { - l_adjusted_time = t; - } - else { - const float f = function(t); - l_adjusted_time = f; - } - if (l_adjusted_time < 0) { - l_adjusted_time = 0.0f; - } - if (l_adjusted_time > 1.0f) { - l_adjusted_time = 1.0f; - } - - float time_dif = 1.0f - l_adjusted_time; - - return start * time_dif + end * l_adjusted_time; - } - - private: - interpolation() = default; - }; -}; // namespace atlas \ No newline at end of file diff --git a/atlas/core/math/types.hpp b/atlas/core/math/types.cppm similarity index 98% rename from atlas/core/math/types.hpp rename to atlas/core/math/types.cppm index e8b58849..330b18bc 100644 --- a/atlas/core/math/types.hpp +++ b/atlas/core/math/types.cppm @@ -1,8 +1,11 @@ -#pragma once +module; + #include #include -namespace atlas { +export module atlas.core.math.types; + +export namespace atlas { /** * @brief vector2 is to define as a centralized mathematical diff --git a/atlas/core/math/utilities.hpp b/atlas/core/math/utilities.hpp deleted file mode 100644 index e5b4cd16..00000000 --- a/atlas/core/math/utilities.hpp +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once -#include - -namespace atlas { - - //! @brief converts vec4 to quaterion - glm::quat to_quat(const glm::vec4& p_values); - - //! @brief converts vec3 to quaterion - glm::quat to_quat(const glm::vec3& p_values); - - //! @brief converts glm::vec3 to glm::vec4 - glm::highp_vec4 from_quat(const glm::vec3& p_values); - - glm::quat to_quathp(const glm::highp_vec4& p_values); - - namespace math { - //! @brief shorthand for glm::vec3(0.f, 0.f, 0.f) - glm::vec3 zeroes(); - - //! @brief shorthand for glm::vec3(1.f, 1.f, 1.f) - glm::vec3 ones(); - - //! @brief rotation equivalent of a zero-vector - // sets to glm::quat(1.f, 0.f, 0.f, 0.f) - glm::quat identity(); - - //! @brief shorthand for glm::vec3(0.f, 0.f, 1.f) - glm::vec3 forward(); - - //! @brief shorthand for glm::vec3(0.f, 0.f, -1.f) - glm::vec3 backward(); - - //! @brief shorthand for glm::vec3(1.f, 0.f, 0.f) - glm::vec3 right(); - - //! @brief shorthand for glm::vec3(-1.f, 0.f, 0.f) - glm::vec3 left(); - - //! @brief shorthand for glm::vec3(0.f, 1.f, 0.f) - glm::vec3 up(); - - //! @brief shorthand for glm::vec3(0.f, -1.f, 0.f) - glm::vec3 down(); - } -}; \ No newline at end of file diff --git a/atlas/core/scene/components.hpp b/atlas/core/scene/components.cppm similarity index 65% rename from atlas/core/scene/components.hpp rename to atlas/core/scene/components.cppm index eaccdeda..1760f81c 100644 --- a/atlas/core/scene/components.hpp +++ b/atlas/core/scene/components.cppm @@ -1,11 +1,16 @@ -#pragma once +module; + #include #include #include #include -#include -namespace atlas { + +export module atlas.core.scene.components; + +import atlas.core.math; + +export namespace atlas { struct transform { glm::highp_vec3 position{ 0.f }; @@ -121,4 +126,71 @@ namespace atlas { glm::mat4 view; }; + /** + * @brief static is represented as fixed + */ + enum body_type : uint8_t { + fixed = 0, + kinematic = 1, + dynamic = 2, + bodynum, + }; + + /** + * @param non_moving is used for static objects that saves for not using the + * collider component + * @param moving is used for dynamic, kinematic, and character objects that + * will be used + */ + enum body_layer : uint8_t { + non_moving = 0, + moving = 1, + layer_num, + }; + + enum activation : uint8_t { activate, deactivate }; + + /** + * @brief physics body data-driven representative + * + * TODO: Add parameters for force, impulse, and torque + */ + struct physics_body { + glm::vec3 linear_velocity = glm::vec3(0.0); + glm::vec3 angular_velocity = glm::vec3(0.0f); + + glm::vec3 force = glm::vec3(0.0f); + glm::vec3 impulse = glm::vec3(0.0f); + glm::vec3 torque = glm::vec3(0.0f); + + float mass_factor = 1.0f; + glm::vec3 center_mass_position = glm::vec3(0.0f); + float linear_damping = 0.0f; + float angular_damping = 0.0f; + + float gravity_factor = 1.0f; + float friction = 0.8f; + float restitution = 0.2f; + + //! @brief body_type::fixed means this physics body is static + uint8_t body_movement_type = body_type::fixed; + + //! @brief body_layer (object layers) refer to the rules of the + //! collision system specified in JoltPhysics + uint8_t body_layer_type = body_layer::moving; + }; + + struct box_collider { + glm::vec3 half_extent = glm::vec3(0.5f); + }; + + struct capsule_collider { + float radius = 0.5f; + float half_height = 0.5f; + }; + + struct sphere_collider { + float radius = 0.5f; + }; + }; // namespace atlas \ No newline at end of file diff --git a/atlas/core/scene/exceptions.hpp b/atlas/core/scene/exceptions.hpp deleted file mode 100644 index bbacc037..00000000 --- a/atlas/core/scene/exceptions.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once -#include - -namespace atlas { - struct exception_block { - const char* data = nullptr; - }; - - class invalid_access_exception : public std::exception { - public: - invalid_access_exception() = default; - invalid_access_exception(const char* p_data) - : m_block(p_data) {} - - //! @return message given when this exception gets triggered - [[nodiscard]] const char* what() const noexcept override { - return m_block.data; - } - - private: - exception_block m_block; - }; -}; \ No newline at end of file diff --git a/atlas/core/scene/game_object.hpp b/atlas/core/scene/game_object.cppm similarity index 82% rename from atlas/core/scene/game_object.hpp rename to atlas/core/scene/game_object.cppm index 972a33b0..5d32cab8 100644 --- a/atlas/core/scene/game_object.hpp +++ b/atlas/core/scene/game_object.cppm @@ -1,9 +1,12 @@ -#pragma once +module; + #include #include -namespace atlas { +export module atlas.core.scene.game_object; +import atlas.core.scene.components; +export namespace atlas { /** * @brief Creates a pointer wrapper which extends capabilities of * flecs::entity @@ -18,11 +21,17 @@ namespace atlas { //! flecs::world game_object() = delete; - game_object(flecs::world_t* p_registry, flecs::entity_t p_id); + game_object(flecs::world_t* p_registry, flecs::entity_t p_id) : flecs::entity(p_registry, p_id) { + add(); + } - game_object(const flecs::entity& p_base); + game_object(const flecs::entity& p_base) : flecs::entity(p_base) { + add(); + } - explicit game_object(flecs::entity& p_base); + explicit game_object(flecs::entity& p_base) : flecs::entity(p_base) { + add(); + } /** * @brief sets the entity to be a parent of the specified entity @@ -45,7 +54,9 @@ namespace atlas { * ``` * */ - void child_of(const std::optional& p_parent); + void child_of(const std::optional& p_parent) { + add(flecs::ChildOf, p_parent.value()); + } /** * @brief iterates through all children entities if the given entity is diff --git a/atlas/core/scene/scene.hpp b/atlas/core/scene/scene.cppm similarity index 86% rename from atlas/core/scene/scene.hpp rename to atlas/core/scene/scene.cppm index 68f67083..2620e0c4 100644 --- a/atlas/core/scene/scene.hpp +++ b/atlas/core/scene/scene.cppm @@ -1,12 +1,18 @@ -#pragma once -#include +module; + +#include #include -#include -#include -namespace atlas { +export module atlas.core.scene; + +import atlas.core.utilities; +import atlas.core.event; +import atlas.core.scene.game_object; + + - /** +export namespace atlas { + /** * @brief Constructs a scene that defines an area where game objects are * part of contained within an atlas::world * @@ -28,7 +34,8 @@ namespace atlas { * @param p_bus is the globalized event bus that is given access to the * scene to subscribe events to it. */ - scene(const std::string& p_name, event::event_bus& p_bus); + scene(const std::string& p_name, event::bus& p_bus) : m_name(p_name), m_bus(&p_bus) { + } virtual ~scene() = default; @@ -38,7 +45,9 @@ namespace atlas { * * @param p_name is a string to set the name of the entity */ - game_object entity(std::string_view p_name); + game_object entity(std::string_view p_name) { + return game_object(m_registry.entity(p_name.data())); + } /** * @brief Retrieves if an entity already exists within the registry, @@ -47,7 +56,9 @@ namespace atlas { * @param p_entity_id is the ID to retrieve an entity if it exists, * otherwise returns a new entity. */ - game_object entity(uint64_t p_id); + game_object entity(uint64_t p_id) { + return game_object(m_registry.entity(p_id)); + } /** * @brief subscribes an event to the event::bus to get invoked when @@ -123,7 +134,9 @@ namespace atlas { * * ``` */ - uint32_t children_count(const game_object& p_parent); + uint32_t children_count(const game_object& p_parent) { + return query_builder().with(flecs::ChildOf, p_parent).build().count(); + } /** * @brief Defer operations until end of frame. @@ -153,7 +166,7 @@ namespace atlas { [[nodiscard]] std::string name() const { return m_name; } //! @return the event::bus handle for subscribing events - [[nodiscard]] event::event_bus* event_handle() const { return m_bus; } + [[nodiscard]] event::bus* event_handle() const { return m_bus; } /** * @brief Requires to return flecs::world is returned by reference to @@ -164,6 +177,6 @@ namespace atlas { private: flecs::world m_registry; std::string m_name; - event::event_bus* m_bus = nullptr; + event::bus* m_bus = nullptr; }; -}; // namespace atlas \ No newline at end of file +}; \ No newline at end of file diff --git a/atlas/core/system/registry.hpp b/atlas/core/scene/system_registry.cppm similarity index 71% rename from atlas/core/system/registry.hpp rename to atlas/core/scene/system_registry.cppm index 5333f255..44239cd2 100644 --- a/atlas/core/system/registry.hpp +++ b/atlas/core/scene/system_registry.cppm @@ -1,8 +1,14 @@ -#pragma once +module; + #include -#include +#include + +export module atlas.core.scene.system_registry; + +import atlas.common; +import atlas.core.scene.world; -namespace atlas { +export namespace atlas { /** * @brief system registry acts as a utility for managing creation of game * worlds initially @@ -23,9 +29,12 @@ namespace atlas { * * @param p_name is for specifying a name to give this registry */ - system_registry(const std::string& p_name); + system_registry(const std::string& p_name) : m_tag(p_name) { - ~system_registry(); + s_instance = this; + } + + ~system_registry() = default; /** * @note system_registry does the following: @@ -45,7 +54,9 @@ namespace atlas { * This was used for getting world to be maintained implicitly by * system_registry, though this will be changing. */ - static ref create_world(const std::string& p_tag); + static ref create_world(const std::string& p_tag) { + return s_instance->append_world_and_get(create_ref(p_tag)); + } //! @brief Searches and returns world if found //! @brief Returns nullptr if world not found @@ -57,18 +68,27 @@ namespace atlas { * @return nullptr if not found, otherwise return * shared_ptr */ - static ref get_world(const std::string& p_tag); + static ref get_world(const std::string& p_tag) { + return s_instance->m_world_registered[p_tag]; + } private: - ref search_world(const std::string& p_tag); + ref search_world(const std::string& p_tag) { + return m_world_registered[p_tag]; + } // void append_world(const ref& p_world); - ref append_world_and_get(const ref& p_world); + ref append_world_and_get(const ref& p_world) { + m_world_registered.emplace(p_world->name(), p_world); + return m_world_registered[p_world->name()]; + } private: static system_registry* s_instance; std::string m_tag = "Undefined"; std::map> m_world_registered; }; + + system_registry* system_registry::s_instance = nullptr; }; \ No newline at end of file diff --git a/atlas/core/scene/world.hpp b/atlas/core/scene/world.cppm similarity index 85% rename from atlas/core/scene/world.hpp rename to atlas/core/scene/world.cppm index f8fd96fb..14221834 100644 --- a/atlas/core/scene/world.hpp +++ b/atlas/core/scene/world.cppm @@ -1,12 +1,17 @@ -#pragma once -#include +module; + #include -#include #include -#include +#include +#include + -namespace atlas { +export module atlas.core.scene.world; +import atlas.common; +import atlas.core.scene; + +export namespace atlas { /** * @brief world represents a larger scope of areas that manages the scene * contexts @@ -32,7 +37,8 @@ namespace atlas { * @brief construct a new world with a specified name associated * with it */ - world(const std::string& p_name); + world(const std::string& p_name) : m_name(p_name) { + } virtual ~world() = default; @@ -46,7 +52,9 @@ namespace atlas { * scenes as this is quite problematic. Should direct attention to this * soon. */ - void add_scene(const ref& p_scene_context); + void add_scene(const ref& p_scene_context) { + m_scene_container.emplace(p_scene_context->name(), p_scene_context); + } /** * @brief get_scene allows for specifically querying for current scenes @@ -70,4 +78,4 @@ namespace atlas { ref m_world_shared_instance; std::string m_name = "Undefined Tag"; }; -}; // namespace atlas \ No newline at end of file +}; \ No newline at end of file diff --git a/src/atlas/core/serialize/serializer.cpp b/atlas/core/serialize/serializer.cppm similarity index 59% rename from src/atlas/core/serialize/serializer.cpp rename to atlas/core/serialize/serializer.cppm index 83f4d3a5..c77f2425 100644 --- a/src/atlas/core/serialize/serializer.cpp +++ b/atlas/core/serialize/serializer.cppm @@ -1,13 +1,24 @@ -#include -#include +module; + +// #include +// #include +#include +#include +#include +#include +#include +#include #include -#include -#include -#include -#include -namespace atlas { +export module atlas.core.serialize; + +import atlas.common; +import atlas.core.scene; +import atlas.core.serialize.types; +import atlas.core.scene.components; + +namespace atlas { // used to serialize entities // TODO -- expand on this to stream_reader and stream_writer static void serialize_entity(YAML::Emitter& output, @@ -147,67 +158,99 @@ namespace atlas { } } - serializer::serializer(const ref& p_scene_ctx) - : m_current_scene_ctx(p_scene_ctx) {} - - void serializer::save(const std::filesystem::path& p_filepath) { - YAML::Emitter output; - output << YAML::BeginMap; - output << YAML::Key << "Scene" << YAML::Value - << m_current_scene_ctx->name(); - output << YAML::Key << "Entities" << YAML::Value << YAML::BeginSeq; - - //! @note Queries in flecs the ecs framework are how we can query all - //! entities that the engine (user creates through our API) - // ref world_object = - // system_registry::get_world("Editor World"); - // ref current_scene = - // world_object->get_scene("LevelScene"); - - // flecs::query<> q = - // current_scene->query_builder().with().build(); - - // query all entities with a serialized tag specified - // while specifying to not query entities that also have the tag::editor - // specified - flecs::query<> q = m_current_scene_ctx->query_builder() - .with() - .without() - .build(); - - q.each([&output](flecs::entity p_entity_id) { - serialize_entity(output, p_entity_id); - }); - - std::ofstream output_file(p_filepath.string()); - output_file << output.c_str(); - } - - bool serializer::load(const std::filesystem::path& p_filepath, - const flecs::world& p_registry) { - std::ifstream ins(p_filepath.string()); - std::stringstream ss; - ss << ins.rdbuf(); - - YAML::Node data = YAML::Load(ss.str()); + /** + * @brief serializer is responsible for saving/loading scenes + * + * This class excepts a scene context with the purpose of serializing with + * the entities that reside within that specific scene context + * + * Which also provide an API for loading in a scene from disk for + * deserialization. + */ + export class serializer { + public: + serializer() = default; + + /** + * @brief constructs a new serializer with a requirement to specify a + * scene to serialize + * @param p_scene_ctx is the current scene to perform + * serialization/deserialization to + */ + serializer(const ref& p_scene_ctx) : m_current_scene_ctx(p_scene_ctx) {} + + /** + * @param p_filepath is the specified path to save the file + */ + void save(const std::filesystem::path& p_filepath) { + YAML::Emitter output; + output << YAML::BeginMap; + output << YAML::Key << "Scene" << YAML::Value + << m_current_scene_ctx->name(); + output << YAML::Key << "Entities" << YAML::Value << YAML::BeginSeq; + + //! @note Queries in flecs the ecs framework are how we can query all + //! entities that the engine (user creates through our API) + // ref world_object = + // system_registry::get_world("Editor World"); + // ref current_scene = + // world_object->get_scene("LevelScene"); + + // flecs::query<> q = + // current_scene->query_builder().with().build(); + + // query all entities with a serialized tag specified + // while specifying to not query entities that also have the tag::editor + // specified + flecs::query<> q = m_current_scene_ctx->query_builder() + .with() + .without() + .build(); + + q.each([&output](flecs::entity p_entity_id) { + serialize_entity(output, p_entity_id); + }); - if (!data["Scene"]) { - return false; + std::ofstream output_file(p_filepath.string()); + output_file << output.c_str(); } - YAML::Node entity_objects = data["Entities"]; + /** + * @param p_filepath is the specified path to loading in the saved file + * from + * @param p_registry is the current scene registry to load/create the + * entities through that registry + * @return true if loading was successful, otherwise will return false + */ + bool load(const std::filesystem::path& p_filepath, const flecs::world& p_registry) { + std::ifstream ins(p_filepath.string()); + std::stringstream ss; + ss << ins.rdbuf(); + + YAML::Node data = YAML::Load(ss.str()); + + if (!data["Scene"]) { + return false; + } + + YAML::Node entity_objects = data["Entities"]; - if (entity_objects) { - for (YAML::iterator::value_type entity : entity_objects) { - std::string name = entity["Entity"].as(); - flecs::entity deserialize_to_object = - p_registry.entity(name.c_str()); + if (entity_objects) { + for (YAML::iterator::value_type entity : entity_objects) { + std::string name = entity["Entity"].as(); + flecs::entity deserialize_to_object = + p_registry.entity(name.c_str()); - // Deserialize atlas::transform component - deserialize_entity(entity, deserialize_to_object); + // Deserialize atlas::transform component + deserialize_entity(entity, deserialize_to_object); + } } + + return true; } - return true; - } -}; \ No newline at end of file + private: + ref m_current_scene_ctx; + }; + +}; // namespace atlas \ No newline at end of file diff --git a/atlas/core/serialize/serializer.hpp b/atlas/core/serialize/serializer.hpp deleted file mode 100644 index ac5c09ba..00000000 --- a/atlas/core/serialize/serializer.hpp +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once -#include -#include -#include -#include - -namespace atlas { - /** - * @brief serializer is responsible for saving/loading scenes - * - * This class excepts a scene context with the purpose of serializing with - * the entities that reside within that specific scene context - * - * Which also provide an API for loading in a scene from disk for - * deserialization. - */ - class serializer { - public: - serializer() = default; - - /** - * @brief constructs a new serializer with a requirement to specify a - * scene to serialize - * @param p_scene_ctx is the current scene to perform - * serialization/deserialization to - */ - serializer(const ref& p_scene_ctx); - - /** - * @param p_filepath is the specified path to save the file - */ - void save(const std::filesystem::path& p_filepath); - - /** - * @param p_filepath is the specified path to loading in the saved file - * from - * @param p_registry is the current scene registry to load/create the - * entities through that registry - * @return true if loading was successful, otherwise will return false - */ - bool load(const std::filesystem::path& p_filepath, - const flecs::world& p_registry); - - private: - ref m_current_scene_ctx; - }; - -}; // namespace atlas \ No newline at end of file diff --git a/atlas/core/serialize/types.hpp b/atlas/core/serialize/types.cppm similarity index 66% rename from atlas/core/serialize/types.hpp rename to atlas/core/serialize/types.cppm index f7c3b46d..67dc3be6 100644 --- a/atlas/core/serialize/types.hpp +++ b/atlas/core/serialize/types.cppm @@ -1,8 +1,11 @@ -#pragma once +module; + #include #include -#include -#include + +export module atlas.core.serialize.types; +import atlas.core.scene.components; + namespace YAML { /** @@ -338,48 +341,153 @@ namespace YAML { }; }; -namespace atlas { +export namespace atlas { //! @brief from yaml-cpp, saving glm::highp_vec2 values to disk - YAML::Emitter& operator<<(YAML::Emitter& p_out, - const glm::highp_vec2& p_values); + YAML::Emitter& operator<<(YAML::Emitter& p_out, const glm::highp_vec2& p_values) { + p_out << YAML::Flow; + p_out << YAML::BeginSeq << p_values.x << p_values.y << YAML::EndSeq; + return p_out; + } //! @brief from yaml-cpp, saving glm::highp_vec3 values to disk - YAML::Emitter& operator<<(YAML::Emitter& p_out, - const glm::highp_vec3& p_values); + YAML::Emitter& operator<<(YAML::Emitter& p_out, const glm::highp_vec3& p_values) { + p_out << YAML::Flow; + p_out << YAML::BeginSeq << p_values.x << p_values.y << p_values.z + << YAML::EndSeq; + return p_out; + } //! @brief from yaml-cpp, saving glm::highp_vec4 values to disk - YAML::Emitter& operator<<(YAML::Emitter& p_out, - const glm::highp_vec4& p_values); + YAML::Emitter& operator<<(YAML::Emitter& p_out, const glm::highp_vec4& p_values) { + p_out << YAML::Flow; + p_out << YAML::BeginSeq << p_values.x << p_values.y << p_values.z + << p_values.w << YAML::EndSeq; + return p_out; + } //! @brief from yaml-cpp, saving transform values to disk - YAML::Emitter& operator<<(YAML::Emitter& p_output, - const transform* p_transform); + YAML::Emitter& operator<<(YAML::Emitter& p_output, const transform* p_transform) { + p_output << YAML::Key << "Transform"; + + p_output << YAML::BeginMap; + p_output << YAML::Key << "Position" << YAML::Value + << p_transform->position; + p_output << YAML::Key << "Scale" << YAML::Value << p_transform->scale; + p_output << YAML::Key << "Rotation" << YAML::Value + << p_transform->rotation; + p_output << YAML::Key << "Quaternion" << YAML::Value + << p_transform->quaternion; + p_output << YAML::EndMap; + return p_output; + } //! @brief from yaml-cpp, saving perspective_camera values to disk - YAML::Emitter& operator<<(YAML::Emitter& p_output, - const perspective_camera* p_camera); + YAML::Emitter& operator<<(YAML::Emitter& p_output, const perspective_camera* p_camera) { + p_output << YAML::Key << "PerspectiveCamera"; + + p_output << YAML::BeginMap; + p_output << YAML::Key << "Plane" << YAML::Value << p_camera->plane; + p_output << YAML::Key << "Active" << YAML::Value << p_camera->is_active; + p_output << YAML::Key << "Field of View" << YAML::Value + << p_camera->field_of_view; + p_output << YAML::EndMap; + return p_output; + } //! @brief from yaml-cpp, saving mesh_source values to disk - YAML::Emitter& operator<<(YAML::Emitter& p_output, - const mesh_source* p_material); + YAML::Emitter& operator<<(YAML::Emitter& p_output, const mesh_source* p_material) { + p_output << YAML::Key << "Mesh Source"; + + p_output << YAML::BeginMap; + p_output << YAML::Key << "Model Path" << YAML::Value + << p_material->model_path; + p_output << YAML::Key << "Diffuse" << YAML::Value + << p_material->diffuse; + p_output << YAML::Key << "Specular" << YAML::Value + << p_material->specular; + p_output << YAML::EndMap; + return p_output; + } //! @brief from yaml-cpp, saving mesh_source values to disk - YAML::Emitter& operator<<(YAML::Emitter& p_output, - const point_light* p_material); + YAML::Emitter& operator<<(YAML::Emitter& p_output, const point_light* p_material) { + p_output << YAML::Key << "Point Light"; + + p_output << YAML::BeginMap; + p_output << YAML::Key << "Color" << YAML::Value << p_material->color; + p_output << YAML::Key << "Attenuation" << YAML::Value + << p_material->attenuation; + p_output << YAML::Key << "Ambient" << YAML::Value + << p_material->ambient; + p_output << YAML::Key << "Diffuse" << YAML::Value + << p_material->diffuse; + p_output << YAML::Key << "Specular" << YAML::Value + << p_material->specular; + p_output << YAML::EndMap; + return p_output; + } //! @brief from yaml-cpp, saving physics_body values to disk - YAML::Emitter& operator<<(YAML::Emitter& p_output, - const physics_body* p_body); + YAML::Emitter& operator<<(YAML::Emitter& p_output, const physics_body* p_body) { + p_output << YAML::Key << "Physics Body"; + + p_output << YAML::BeginMap; + p_output << YAML::Key << "Linear Velocity" << YAML::Value + << p_body->linear_velocity; + p_output << YAML::Key << "Angular Velocity" << YAML::Value + << p_body->angular_velocity; + p_output << YAML::Key << "Force" << YAML::Value << p_body->force; + p_output << YAML::Key << "Impulse" << YAML::Value << p_body->impulse; + p_output << YAML::Key << "Torque" << YAML::Value << p_body->torque; + p_output << YAML::Key << "Mass Factor" << YAML::Value + << p_body->mass_factor; + p_output << YAML::Key << "Center Mass Position" << YAML::Value + << p_body->center_mass_position; + p_output << YAML::Key << "Friction" << YAML::Value << p_body->friction; + p_output << YAML::Key << "Restitution" << YAML::Value + << p_body->restitution; + p_output << YAML::Key << "Body Movement Type" << YAML::Value + << static_cast(p_body->body_movement_type); + p_output << YAML::Key << "Body Layer Type" << YAML::Value + << static_cast(p_body->body_layer_type); + p_output << YAML::EndMap; + return p_output; + } //! @brief from yaml-cpp, saving box_collider values to disk - YAML::Emitter& operator<<(YAML::Emitter& p_output, - const box_collider* p_body); + YAML::Emitter& operator<<(YAML::Emitter& p_output, const box_collider* p_body) { + // Tag this specific serialization values to the box collider + p_output << YAML::Key << "Box Collider"; + + p_output << YAML::BeginMap; + p_output << YAML::Key << "Half Extent" << YAML::Value + << p_body->half_extent; + p_output << YAML::EndMap; + + return p_output; + } //! @brief from yaml-cpp, saving sphere_collider values to disk - YAML::Emitter& operator<<(YAML::Emitter& p_output, - const sphere_collider* p_body); + YAML::Emitter& operator<<(YAML::Emitter& p_output, const sphere_collider* p_body) { + p_output << YAML::Key << "Sphere Collider"; + + p_output << YAML::BeginMap; + p_output << YAML::Key << "Radius" << YAML::Value << p_body->radius; + + p_output << YAML::EndMap; + + return p_output; + } //! @brief from yaml-cpp, saving capsule_collider values to disk - YAML::Emitter& operator<<(YAML::Emitter& p_output, - const capsule_collider* p_body); + YAML::Emitter& operator<<(YAML::Emitter& p_output, const capsule_collider* p_body) { + p_output << YAML::Key << "Capsule Collider"; + p_output << YAML::BeginMap; + p_output << YAML::Key << "Radius" << YAML::Value << p_body->radius; + p_output << YAML::Key << "Half Height" << YAML::Value + << p_body->half_height; + p_output << YAML::EndMap; + + return p_output; + } }; \ No newline at end of file diff --git a/src/atlas/core/ui/widgets.cpp b/atlas/core/ui/widgets.cppm similarity index 57% rename from src/atlas/core/ui/widgets.cpp rename to atlas/core/ui/widgets.cppm index 9a134699..a89f4992 100644 --- a/src/atlas/core/ui/widgets.cpp +++ b/atlas/core/ui/widgets.cppm @@ -1,9 +1,39 @@ -#include +module; + #include // Used to include "PushMultiItemsWidths" +#include +#include +#include +#include +#include +#include + +export module atlas.core.ui.widgets; + +import atlas.core.filesystem.file_dialog; +import atlas.core.scene.components; -namespace atlas::ui { +export namespace atlas::ui { using ::ImGui::InputText; + /** + * @brief This is a free-standing function around + * ImGui::BeginPopupContextWindow + * + * It is primarily used to check if your mouse cursor is hovering over any + * items and to opening items based on if the mouse is specifically hovering + * over that particular item + * + * + * @param p_name is the string ID that is needed to be specified by imgui + * @param p_mb is the mouse button identifier that is an int to represent if + * the presses were (0=left, 1=right, 2=middle) + * @param p_over_items is a boolean to check if the mouse cursor has + * hoivered over that particular item specified by the string ID + * + * @return true if successful, otherwise return false + * + */ bool begin_popup_context_window(const char* str_id, ImGuiMouseButton mb, bool over_items) { @@ -11,9 +41,16 @@ namespace atlas::ui { str_id, mb | (over_items ? 0 : ImGuiPopupFlags_NoOpenOverItems)); } + /** + * @brief UI render glm::vec3 values to imgui + * + * @param p_name is the specified name ID to associate with + * @param p_value is the value parameter to modify through this widget + * @param p_reset_value is the reset value to set the glm::vec3 + */ void draw_vec3(const std::string& p_tag, glm::vec3& p_position, - float p_reset_value) { + float p_reset_value=0.f) { // ImGuiIO& io = ImGui::GetIO(); ImGui::PushID(p_tag.c_str()); @@ -95,9 +132,16 @@ namespace atlas::ui { ImGui::PopID(); } + /** + * @brief UI render glm::vec4 values to imgui + * + * @param p_name is the specified name ID to associate with + * @param p_value is the value parameter to modify through this widget + * @param p_reset_value is the reset value to set the glm::vec3 + */ void draw_vec4(const std::string& p_tag, glm::vec4& p_value, - float p_reset_value) { + float p_reset_value=0.f) { // ImGuiIO& io = ImGui::GetIO(); ImGui::PushID(p_tag.c_str()); @@ -179,9 +223,16 @@ namespace atlas::ui { ImGui::PopID(); } + /** + * @brief UI render glm::vec4 values to imgui + * + * @param p_name is the specified name ID to associate with + * @param p_value is the value parameter to modify through this widget + * @param p_reset_value is the reset value to set the glm::vec3 + */ void draw_float(const std::string& p_tag, float& p_value, - float reset_value) { + float reset_value=0.f) { ImGui::PushID(p_tag.c_str()); float column_width = 100.0f; @@ -222,6 +273,13 @@ namespace atlas::ui { ImGui::PopID(); } + /** + * @brief UI rendering input text + * + * + * @param p_dst is the destination string to be changed + * @param p_src is the original string that was previously given + */ void draw_input_text(std::string& p_dst, std::string& p_src) { std::string input_buffer = p_src; @@ -247,10 +305,92 @@ namespace atlas::ui { } } + /** + * @brief For UI rendering text to display and not input + * + * @param p_value is specified string for drawing text to imgui's UI + */ void draw_text(const std::string& p_value) { ImGui::Text("%s", p_value.data()); } + /** + * @brief Creates a panel that can be used to attach list of properties or + * other widgets too + * + * @tparam UComponent is the type of component or type to assoociate with + * this panel too + * @tparam UFunction is the callback that contains an arbitrary task related + * to the specified UComponent + * @param p_name is the name to give this panel + * @param p_entity is the entity that is being modified by this specific + * panel + * @param p_callback is a callback that is given an arbitrary task + * + * + * Example Usage: + * + * ```C++ + * draw_panel_component("transform", [](atlas::transform* + * p_transform){ + * // do some drawing with that transform + * }); + * ``` + */ + template + void draw_panel_component(const std::string& p_name, + flecs::entity& p_entity, + const UFunction& p_callback) { + const ImGuiTreeNodeFlags tree_node_flags = + ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Framed | + ImGuiTreeNodeFlags_SpanAvailWidth | + ImGuiTreeNodeFlags_AllowItemOverlap | ImGuiTreeNodeFlags_FramePadding; + + ImVec2 content_region = ImGui::GetContentRegionAvail(); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2{ 4, 4 }); + + float line_height = + ImGui::GetFontSize() + ImGui::GetStyle().FramePadding.y * 2.0f; + ImGui::Separator(); + + bool opened = ImGui::TreeNodeEx((void*)typeid(UComponent).hash_code(), + tree_node_flags, + "%s", + p_name.c_str()); + ImGui::PopStyleVar(); + + ImGui::SameLine(content_region.x - line_height * 0.05f); + + if (ImGui::Button("+", ImVec2(line_height, line_height))) { + ImGui::OpenPopup("ComponentSettings"); + } + + bool remove_component = false; // @note for deferring when to + // delete component. + if (ImGui::BeginPopup("ComponentSettings")) { + if (ImGui::MenuItem("Remove Component")) { + remove_component = true; + } + + ImGui::EndPopup(); + } + + if (remove_component) { + p_entity.remove(); + } + + if (opened) { + p_callback(p_entity.get_mut()); + ImGui::TreePop(); + } + } + + /** + * @brief used for creating a main dockspace for the editor + * + * TODO: This should be removed because this will be added into a separate + * atlas/editor/ toolchain module + */ void dockspace_window(GLFWwindow* p_window) { bool dockspace_open = true; @@ -303,9 +443,82 @@ namespace atlas::ui { ImGui::End(); } + /** + draw panel component + + ImGui Renders individual sections that is per-component section + @param T is the specified component + @param p_entity is the entity to do operations with + @param UFunction is callback to logic for setting up those specific data + properties + */ + template + void draw_component(const std::string& p_tag, + flecs::entity& p_entity, + const UFunction& p_callable) { + const ImGuiTreeNodeFlags tree_node_flags = + ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Framed | + ImGuiTreeNodeFlags_SpanAvailWidth | + ImGuiTreeNodeFlags_AllowItemOverlap | ImGuiTreeNodeFlags_FramePadding; + + if (!p_entity.has()) { + return; + } + + T* component = p_entity.get_mut(); + + ImVec2 content_region = ImGui::GetContentRegionAvail(); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2{ 4, 4 }); + + float line_height = + ImGui::GetFontSize() + ImGui::GetStyle().FramePadding.y * 2.0f; + ImGui::Separator(); + + bool opened = ImGui::TreeNodeEx( + (void*)typeid(T).hash_code(), tree_node_flags, "%s", p_tag.c_str()); + ImGui::PopStyleVar(); + + ImGui::SameLine(content_region.x - line_height * 0.05f); + + if (ImGui::Button("+", ImVec2(line_height, line_height))) { + ImGui::OpenPopup("ComponentSettings"); + } + + bool remove_component = false; // @note for deferring when to + // delete component. + if (ImGui::BeginPopup("ComponentSettings")) { + if (ImGui::MenuItem("Remove Component")) { + remove_component = true; + } + + ImGui::EndPopup(); + } + + if (opened) { + p_callable(component); + ImGui::TreePop(); + } + + if (remove_component and !std::is_same_v) { + p_entity.remove(); + } + } + + /** + * @brief opens up a file dialog that uses imgui's button widget to make + * this happen + * + * This uses ImGui::Button to allow for when a click occurs it opens up the + * platform-specific file dialog to save or load in a particular file + * + * @param p_name is the specified name ID to pass into imgui + * @param p_filepath is the path of the file + * @param p_filter is the specific filter for file extensions to allow + * displaying in the fild dialog + */ void button_open_file_dialog(const std::string& p_tag, std::string& p_filename, - const std::string& p_filter) { + const std::string& p_filter = "obj;glftf;fbx") { if (ImGui::Button(p_tag.c_str())) { p_filename = filesystem::load_from_file_dialog(p_filter); } diff --git a/atlas/core/ui/widgets.hpp b/atlas/core/ui/widgets.hpp deleted file mode 100644 index 3043670c..00000000 --- a/atlas/core/ui/widgets.hpp +++ /dev/null @@ -1,237 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include - -namespace atlas::ui { - - /** - * @brief This is a free-standing function around - * ImGui::BeginPopupContextWindow - * - * It is primarily used to check if your mouse cursor is hovering over any - * items and to opening items based on if the mouse is specifically hovering - * over that particular item - * - * - * @param p_name is the string ID that is needed to be specified by imgui - * @param p_mb is the mouse button identifier that is an int to represent if - * the presses were (0=left, 1=right, 2=middle) - * @param p_over_items is a boolean to check if the mouse cursor has - * hoivered over that particular item specified by the string ID - * - * @return true if successful, otherwise return false - * - */ - bool begin_popup_context_window(const char* p_name, - ImGuiMouseButton p_mb, - bool p_over_items); - - /** - * @brief UI render glm::vec3 values to imgui - * - * @param p_name is the specified name ID to associate with - * @param p_value is the value parameter to modify through this widget - * @param p_reset_value is the reset value to set the glm::vec3 - */ - void draw_vec3(const std::string& p_name, - glm::vec3& p_value, - float p_reset_value = 0.f); - - /** - * @brief UI render glm::vec4 values to imgui - * - * @param p_name is the specified name ID to associate with - * @param p_value is the value parameter to modify through this widget - * @param p_reset_value is the reset value to set the glm::vec3 - */ - void draw_vec4(const std::string& p_name, - glm::vec4& p_value, - float p_reset_value = 0.f); - - /** - * @brief UI render glm::vec4 values to imgui - * - * @param p_name is the specified name ID to associate with - * @param p_value is the value parameter to modify through this widget - * @param p_reset_value is the reset value to set the glm::vec3 - */ - void draw_float(const std::string& p_tag, - float& p_value, - float p_reset_value = 0.f); - - /** - * @brief UI rendering input text - * - * - * @param p_dst is the destination string to be changed - * @param p_src is the original string that was previously given - */ - void draw_input_text(std::string& p_dst, std::string& p_src); - - /** - * @brief For UI rendering text to display and not input - * - * @param p_value is specified string for drawing text to imgui's UI - */ - void draw_text(const std::string& p_value); - - /** - * @brief Creates a panel that can be used to attach list of properties or - * other widgets too - * - * @tparam UComponent is the type of component or type to assoociate with - * this panel too - * @tparam UFunction is the callback that contains an arbitrary task related - * to the specified UComponent - * @param p_name is the name to give this panel - * @param p_entity is the entity that is being modified by this specific - * panel - * @param p_callback is a callback that is given an arbitrary task - * - * - * Example Usage: - * - * ```C++ - * draw_panel_component("transform", [](atlas::transform* - * p_transform){ - * // do some drawing with that transform - * }); - * ``` - */ - template - static void draw_panel_component(const std::string& p_name, - flecs::entity& p_entity, - const UFunction& p_callback) { - const ImGuiTreeNodeFlags tree_node_flags = - ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Framed | - ImGuiTreeNodeFlags_SpanAvailWidth | - ImGuiTreeNodeFlags_AllowItemOverlap | ImGuiTreeNodeFlags_FramePadding; - - ImVec2 content_region = ImGui::GetContentRegionAvail(); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2{ 4, 4 }); - - float line_height = - ImGui::GetFontSize() + ImGui::GetStyle().FramePadding.y * 2.0f; - ImGui::Separator(); - - bool opened = ImGui::TreeNodeEx((void*)typeid(UComponent).hash_code(), - tree_node_flags, - "%s", - p_name.c_str()); - ImGui::PopStyleVar(); - - ImGui::SameLine(content_region.x - line_height * 0.05f); - - if (ImGui::Button("+", ImVec2(line_height, line_height))) { - ImGui::OpenPopup("ComponentSettings"); - } - - bool remove_component = false; // @note for deferring when to - // delete component. - if (ImGui::BeginPopup("ComponentSettings")) { - if (ImGui::MenuItem("Remove Component")) { - remove_component = true; - } - - ImGui::EndPopup(); - } - - if (remove_component) { - p_entity.remove(); - } - - if (opened) { - p_callback(p_entity.get_mut()); - ImGui::TreePop(); - } - } - - /** - draw panel component - - ImGui Renders individual sections that is per-component section - @param T is the specified component - @param p_entity is the entity to do operations with - @param UFunction is callback to logic for setting up those specific data - properties - */ - template - static void draw_component(const std::string& p_tag, - flecs::entity& p_entity, - const UFunction& p_callable) { - const ImGuiTreeNodeFlags tree_node_flags = - ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Framed | - ImGuiTreeNodeFlags_SpanAvailWidth | - ImGuiTreeNodeFlags_AllowItemOverlap | ImGuiTreeNodeFlags_FramePadding; - - if (!p_entity.has()) { - return; - } - - T* component = p_entity.get_mut(); - - ImVec2 content_region = ImGui::GetContentRegionAvail(); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2{ 4, 4 }); - - float line_height = - ImGui::GetFontSize() + ImGui::GetStyle().FramePadding.y * 2.0f; - ImGui::Separator(); - - bool opened = ImGui::TreeNodeEx( - (void*)typeid(T).hash_code(), tree_node_flags, "%s", p_tag.c_str()); - ImGui::PopStyleVar(); - - ImGui::SameLine(content_region.x - line_height * 0.05f); - - if (ImGui::Button("+", ImVec2(line_height, line_height))) { - ImGui::OpenPopup("ComponentSettings"); - } - - bool remove_component = false; // @note for deferring when to - // delete component. - if (ImGui::BeginPopup("ComponentSettings")) { - if (ImGui::MenuItem("Remove Component")) { - remove_component = true; - } - - ImGui::EndPopup(); - } - - if (opened) { - p_callable(component); - ImGui::TreePop(); - } - - if (remove_component and !std::is_same_v) { - p_entity.remove(); - } - } - - /** - * @brief used for creating a main dockspace for the editor - * - * TODO: This should be removed because this will be added into a separate - * atlas/editor/ toolchain module - */ - void dockspace_window(GLFWwindow* p_window); - - /** - * @brief opens up a file dialog that uses imgui's button widget to make - * this happen - * - * This uses ImGui::Button to allow for when a click occurs it opens up the - * platform-specific file dialog to save or load in a particular file - * - * @param p_name is the specified name ID to pass into imgui - * @param p_filepath is the path of the file - * @param p_filter is the specific filter for file extensions to allow - * displaying in the fild dialog - */ - void button_open_file_dialog(const std::string& p_name, - std::string& p_filepath, - const std::string& p_filter = "obj;glftf;fbx"); -}; \ No newline at end of file diff --git a/atlas/core/utilities/hash.hpp b/atlas/core/utilities/hash.hpp deleted file mode 100644 index 9bc1fa5f..00000000 --- a/atlas/core/utilities/hash.hpp +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once -#include -#include - -namespace atlas { - template - void hash_combine(size_t& seed, const T& v, const Rest&... rest) { - seed ^= std::hash()(v) + 0x9e3779b9 + (seed << 6) + (seed << 2); - (hash_combine(seed, rest), ...); - } - -}; \ No newline at end of file diff --git a/atlas/core/utilities/memory.hpp b/atlas/core/utilities/memory.hpp deleted file mode 100644 index e5df5eed..00000000 --- a/atlas/core/utilities/memory.hpp +++ /dev/null @@ -1,1665 +0,0 @@ -/* - Copied from - https://github.com/libhal/libhal/blob/main/include/libhal/pointers.hpp - - [atlas] modifications - 1.) File renamed to memory - 2.) In the atlas:: namespace. -*/ -#include -#include -#include -#include -#include -#include -#include -#include - -namespace atlas::memory { - // Forward declarations - template - class strong_ptr; - - template - class weak_ptr; - - namespace detail { - /** - * @brief Control block for reference counting - type erased. - * - * This structure manages the lifetime of reference-counted objects by - * tracking strong and weak references. It also stores the memory - * allocator and destroy function used to clean up the object when no - * more references exist. - */ - struct ref_info { - /** - * @brief Destroy function for ref counted object - * - * Always returns the total size of the object wrapped in a ref - * count object. Thus the size should normally be greater than - * sizeof(T). Expect sizeof(T) + sizeof(ref_info) and anything else - * the ref count object may contain. - * - * If a nullptr is passed to the destroy function, it returns the - * object size but does not destroy the object. - */ - using destroy_fn_t = size_t(void const*); - - /// Initialize to 1 since creation implies a reference - std::pmr::polymorphic_allocator<> allocator; - destroy_fn_t* destroy; - std::atomic strong_count = 1; - std::atomic weak_count = 0; - }; - - /** - * @brief Add strong reference to control block - * - * @param p_info Pointer to the control block - */ - inline void ptr_add_ref(ref_info* p_info) { - p_info->strong_count.fetch_add(1, std::memory_order_relaxed); - } - - /** - * @brief Release strong reference from control block - * - * If this was the last strong reference, the pointed-to object will be - * destroyed. If there are no remaining weak references, the memory - * will also be deallocated. - * - * @param p_info Pointer to the control block - */ - inline void ptr_release(ref_info* p_info) { - if (p_info->strong_count.fetch_sub(1, std::memory_order_acq_rel) == - 1) { - // No more strong references, destroy the object but keep - // control block if there are weak references - - // Call the destroy function which will: - // 1. Call the destructor of the object - // 2. Return the size of the rc for deallocation when needed - size_t const object_size = p_info->destroy(p_info); - - // If there are no weak references, deallocate memory - if (p_info->weak_count.load(std::memory_order_acquire) == 0) { - // Save allocator for deallocating - auto alloc = p_info->allocator; - - // Deallocate memory - alloc.deallocate_bytes(p_info, object_size); - } - } - } - - /** - * @brief Add weak reference to control block - * - * @param p_info Pointer to the control block - */ - inline void ptr_add_weak(ref_info* p_info) { - p_info->weak_count.fetch_add(1, std::memory_order_relaxed); - } - - /** - * @brief Release weak reference from control block - * - * If this was the last weak reference and there are no remaining - * strong references, the memory will be deallocated. - * - * @param p_info Pointer to the control block - */ - inline void ptr_release_weak(ref_info* p_info) { - if (p_info->weak_count.fetch_sub(1, std::memory_order_acq_rel) == - 0) { - // No more weak references, check if we can deallocate - if (p_info->strong_count.load(std::memory_order_acquire) == 0) { - // No strong references either, get the size from the - // destroy function - auto const object_size = p_info->destroy(nullptr); - - // Save allocator for deallocating - auto alloc = p_info->allocator; - - // Deallocate memory - alloc.deallocate_bytes(p_info, object_size); - } - } - } - - /** - * @brief A wrapper that contains both the ref_info and the actual - * object - * - * This structure keeps the control block and managed object together in - * memory. - * - * @tparam T The type of the managed object - */ - template - struct rc { - ref_info m_info; - T m_object; - - // Constructor that forwards arguments to the object - template - rc(std::pmr::polymorphic_allocator<> p_alloc, Args&&... args) - : m_info{ .allocator = p_alloc, .destroy = &destroy_function } - , m_object(std::forward(args)...) {} - - // Static function to destroy an instance and return its size - static size_t destroy_function(void const* p_object) { - if (p_object != nullptr) { - auto const* obj = static_cast const*>(p_object); - // Call destructor for the object only - obj->m_object.~T(); - } - // Return size for future deallocation - return sizeof(rc); - } - }; - // Check if a type is an array or std::array - template - struct is_array_like : std::false_type {}; - - // NOLINTBEGIN(modernize-avoid-c-arrays) - // Specialization for C-style arrays - template - struct is_array_like : std::true_type {}; - // NOLINTEND(modernize-avoid-c-arrays) - - // Specialization for std::array - template - struct is_array_like> : std::true_type {}; - - // Helper variable template - template - inline constexpr bool is_array_like_v = is_array_like::value; - - // Concept for array-like types - template - concept array_like = is_array_like_v; - - // Concept for non-array-like types - template - concept non_array_like = !array_like; - } // namespace detail - - /** - * @brief A non-nullable strong reference counted pointer - * - * strong_ptr is a smart pointer that maintains shared ownership of an - * object through a reference count. It is similar to std::shared_ptr but - * with these key differences: - * - * 1. Cannot be null - must always point to a valid object - * 2. Can only be created via make_strong_ptr, not from raw pointers - * 3. More memory efficient implementation - * - * Use strong_ptr when you need shared ownership semantics and can guarantee - * the pointer will never be null. For nullable references, use - * optional_ptr. - * - * Example usage: - * - * ```C++ - * // Create a strong_ptr to an object - * auto ptr = hal::make_strong_ptr(allocator, arg1, arg2); - * - * // Use the object using dereference (*) operator - * (*ptr).configure({ .clock_rate = 250_kHz }); - * - * // OR use the object using arrow (->) operator - * ptr->configure({ .clock_rate = 250_kHz }); - * - * // Share ownership with another driver or object - * auto my_imu = hal::make_strong_ptr(allocator, ptr, 0x13); - * ``` - * - * @tparam T The type of the managed object - */ - template - class strong_ptr { - public: - using element_type = T; - - /// Delete default constructor - strong_ptr must always be valid - strong_ptr() = delete; - - /// Delete nullptr constructor - strong_ptr must always be valid - strong_ptr(std::nullptr_t) = delete; - - /** - * @brief Copy constructor - * - * Creates a new strong reference to the same object. - * - * @param p_other The strong_ptr to copy from - */ - strong_ptr(strong_ptr const& p_other) noexcept - : m_ctrl(p_other.m_ctrl) - , m_ptr(p_other.m_ptr) { - ptr_add_ref(m_ctrl); - } - - /** - * @brief Converting copy constructor - * - * Creates a new strong reference to the same object, converting from - * a derived type U to base type T. - * - * @tparam U A type convertible to T - * @param p_other The strong_ptr to copy from - */ - template - strong_ptr(strong_ptr const& p_other) noexcept - requires(std::is_convertible_v) - : m_ctrl(p_other.m_ctrl) - , m_ptr(p_other.m_ptr) { - ptr_add_ref(m_ctrl); - } - - /** - * @brief Move constructor that intentionally behaves like a copy - * constructor for safety - * - * This move constructor deliberately performs a full copy operation - * rather than transferring ownership. This is a safety feature to - * prevent potential undefined behavior that could occur if code - * accidentally accessed a moved-from strong_ptr. - * - * After this operation, both the source and destination objects remain - * in valid states, and the reference count is incremented by 1. This - * ensures that even if code incorrectly continues to use the source - * object after a move, no undefined behavior will occur. - * - * @param p_other The strong_ptr to "move" from (actually copied for - * safety) - */ - strong_ptr(strong_ptr&& p_other) noexcept - : m_ctrl(p_other.m_ctrl) - , m_ptr(p_other.m_ptr) { - ptr_add_ref(m_ctrl); - } - - /** - * @brief Move assignment operator that behaves like a copy assignment - * for safety - * - * This move assignment operator deliberately performs a full copy - * operation rather than transferring ownership. This is a safety - * feature to prevent potential undefined behavior that could occur if - * code accidentally accessed a moved-from strong_ptr. - * - * After this operation, both the source and destination objects remain - * in valid states, and the reference count is incremented by 1. This - * ensures that even if code incorrectly continues to use the source - * object after a move, no undefined behavior will occur. - * - * @param p_other The strong_ptr to "move" from (actually copied for - * safety) - * @return Reference to *this - */ - strong_ptr& operator=(strong_ptr&& p_other) noexcept { - if (this != &p_other) { - release(); - m_ctrl = p_other.m_ctrl; - m_ptr = p_other.m_ptr; - ptr_add_ref(m_ctrl); - } - return *this; - } - - /** - * @brief Compile time error message for bad alias value - * - * `std::shared_ptr` provides an alias constructor that accepts any - * `void*` which is UB if that `void*` doesn't have the same lifetime as - * the object referenced by the `std::shared_ptr`. Users attempting to - * do this will get a list of constructors that failed to fit. This is - * not a good error message for users. Instead, we provide a - * static_assert message in plain english that explains why this - * overload fails at compile time. - * - * @tparam U - some type for the strong_ptr. - */ - template - strong_ptr(strong_ptr const&, void const*) noexcept { - // NOTE: The conditional used here is to prevent the compiler from - // jumping-the-gun and emitting the static assert error during - // template instantiation of the class. With this conditional, the - // error only appears when this constructor is used. - static_assert( - std::is_same_v && !std::is_same_v, - "Aliasing constructor only works with pointers-to-members " - "and does not work with arbitrary pointers like std::shared_ptr " - "allows."); - } - - /** - * @brief Safe aliasing constructor for object members - * - * This constructor creates a strong_ptr that points to a member of an - * object managed by another strong_ptr. The resulting strong_ptr shares - * ownership with the original strong_ptr, keeping the entire parent - * object alive. - * - * This version is only enabled for non-array members to prevent - * potential undefined behavior when accessing array elements directly. - * Use the array-specific versions instead. - * - * Example usage: - * ``` - * struct container { - * component part; - * }; - * - * // Create a strong_ptr to the container - * auto container_ptr = make_strong_ptr(allocator); - * - * // Create a strong_ptr to just the component - * auto component_ptr = strong_ptr(container_ptr, - * &container::part); - * ``` - * - * @tparam U Type of the parent object - * @tparam M Type of the member - * @param p_other The strong_ptr to the parent object - * @param p_member_ptr Pointer-to-member identifying which member to - * reference - */ - template - strong_ptr(strong_ptr const& p_other, - // clang-format off - M U::* p_member_ptr - // clang-format on - ) noexcept - - : m_ctrl(p_other.m_ctrl) - , m_ptr(&((*p_other).*p_member_ptr)) { - ptr_add_ref(m_ctrl); - } - - /** - * @brief Safe aliasing constructor for std::array members - * - * This constructor creates a strong_ptr that points to an element of an - * array member in an object managed by another strong_ptr. It performs - * bounds checking to ensure the index is valid. - * - * Example usage: - * ``` - * struct array_container { - * std::array elements; - * }; - * - * auto container_ptr = make_strong_ptr(allocator); - * - * // Get strong_ptr to the 2nd element - * auto element_ptr = strong_ptr( - * container_ptr, - * &array_container::elements, - * 2 // Index to access - * ); - * ``` - * - * @tparam U Type of the parent object - * @tparam E Type of the array element - * @tparam N Size of the array - * @param p_other The strong_ptr to the parent object - * @param p_array_ptr Pointer-to-member identifying the array member - * @param p_index Index of the element to reference - * @throws hal::out_of_range if index is out of bounds - */ - template - strong_ptr(strong_ptr const& p_other, - // clang-format off - std::array U::* p_array_ptr, - // clang-format on - std::size_t p_index) { - static_assert(std::is_convertible_v, - "Array element type must be convertible to T"); - throw_if_out_of_bounds(N, p_index); - m_ctrl = p_other.m_ctrl; - m_ptr = &((*p_other).*p_array_ptr)[p_index]; - ptr_add_ref(m_ctrl); - } - - // NOLINTBEGIN(modernize-avoid-c-arrays) - /** - * @brief Safe aliasing constructor for C-array members - * - * This constructor creates a strong_ptr that points to an element of a - * C-style array member in an object managed by another strong_ptr. It - * performs bounds checking to ensure the index is valid. - * - * Example usage: - * ``` - * struct c_array_container { - * element elements[5]; - * }; - * - * auto container_ptr = make_strong_ptr(allocator); - * - * // Get strong_ptr to the 2nd element - * auto element_ptr = strong_ptr( - * container_ptr, - * &c_array_container::elements, - * 2 // Index to access - * ); - * ``` - * - * @tparam U Type of the parent object - * @tparam E Type of the array element - * @tparam N Size of the array - * @param p_other The strong_ptr to the parent object - * @param p_array_ptr Pointer-to-member identifying the array member - * @param p_index Index of the element to reference - * @throws hal::out_of_range if index is out of bounds - */ - template - strong_ptr(strong_ptr const& p_other, - E (U::*p_array_ptr)[N], - std::size_t p_index) { - static_assert(std::is_convertible_v, - "Array element type must be convertible to T"); - throw_if_out_of_bounds(N, p_index); - m_ctrl = p_other.m_ctrl; - m_ptr = &((*p_other).*p_array_ptr)[p_index]; - ptr_add_ref(m_ctrl); - } - // NOLINTEND(modernize-avoid-c-arrays) - - /** - * @brief Destructor - * - * Decrements the reference count and destroys the managed object - * if this was the last strong reference. - */ - ~strong_ptr() { release(); } - - /** - * @brief Copy assignment operator - * - * Replaces the managed object with the one managed by p_other. - * - * @param p_other The strong_ptr to copy from - * @return Reference to *this - */ - strong_ptr& operator=(strong_ptr const& p_other) noexcept { - if (this != &p_other) { - release(); - m_ctrl = p_other.m_ctrl; - m_ptr = p_other.m_ptr; - ptr_add_ref(m_ctrl); - } - return *this; - } - - /** - * @brief Converting copy assignment operator - * - * Replaces the managed object with the one managed by p_other, - * converting from type U to type T. - * - * @tparam U A type convertible to T - * @param p_other The strong_ptr to copy from - * @return Reference to *this - */ - template - strong_ptr& operator=(strong_ptr const& p_other) noexcept - requires(std::is_convertible_v) - { - release(); - m_ctrl = p_other.m_ctrl; - m_ptr = p_other.m_ptr; - ptr_add_ref(m_ctrl); - return *this; - } - - /** - * @brief Swap the contents of this strong_ptr with another - * - * @param p_other The strong_ptr to swap with - */ - void swap(strong_ptr& p_other) noexcept { - std::swap(m_ctrl, p_other.m_ctrl); - std::swap(m_ptr, p_other.m_ptr); - } - - /** - * @brief Disable dereferencing for r-values (temporaries) - */ - T& operator*() && = delete; - - /** - * @brief Disable member access for r-values (temporaries) - */ - T* operator->() && = delete; - - /** - * @brief Dereference operator to access the managed object - * - * @return Reference to the managed object - */ - [[nodiscard]] T& operator*() const& noexcept { return *m_ptr; } - - /** - * @brief Member access operator to access the managed object - * - * @return Pointer to the managed object - */ - [[nodiscard]] T* operator->() const& noexcept { return m_ptr; } - - /** - * @brief Get the current reference count - * - * This is primarily for testing purposes. - * - * @return The number of strong references to the managed object - */ - [[nodiscard]] auto use_count() const noexcept { - return m_ctrl ? m_ctrl->strong_count.load(std::memory_order_relaxed) - : 0; - } - - private: - template - friend strong_ptr make_strong_ptr(std::pmr::polymorphic_allocator<>, - Args&&...); - - template - friend class strong_ptr; - - template - friend class weak_ptr; - - template - friend class optional_ptr; - - inline void throw_if_out_of_bounds(size_t p_size, size_t p_index) { - if (p_index >= p_size) { - throw std::out_of_range( - this, { .m_index = p_index, .m_capacity = p_size }); - } - } - - // Internal constructor with control block and pointer - used by make() - // and aliasing - strong_ptr(detail::ref_info* p_ctrl, T* p_ptr) noexcept - : m_ctrl(p_ctrl) - , m_ptr(p_ptr) {} - - void release() { - if (m_ctrl) { - ptr_release(m_ctrl); - } - } - - detail::ref_info* m_ctrl = nullptr; - T* m_ptr = nullptr; - }; - - /** - * @brief CRTP mixin to enable objects to create strong_ptr instances to - * themselves - * - * Similar to `std::enable_shared_from_this`, this class allows an object to - * safely obtain a strong_ptr to itself. The object must inherit from this - * class and be managed by a strong_ptr created via make_strong_ptr. - * - * Example usage: - * ```cpp - * class my_driver : public enable_strong_from_this { - * public: - * void register_callback() { - * // Get a strong_ptr to ourselves - * auto self = strong_from_this(); - * some_async_system.register_callback([self](){ - * self->handle_callback(); - * }); - * } - * }; - * - * auto obj = make_strong_ptr(allocator); - * obj->register_callback(); // Safe to get strong_ptr to self - * ``` - * - * @tparam T The derived class type - */ - template - class enable_strong_from_this { - public: - /** - * @brief Get a strong_ptr to this object - * - * @return strong_ptr pointing to this object - * @throws hal::bad_weak_ptr if this object is not managed by a - * strong_ptr - */ - [[nodiscard]] strong_ptr strong_from_this() { - auto locked = m_weak_this.lock(); - if (!locked) { - throw std::bad_weak_ptr(&weak_from_this); - } - return locked.value(); - } - - /** - * @brief Get a strong_ptr to this object (const version) - * - * @return strong_ptr pointing to this object - * @throws hal::bad_weak_ptr if this object is not managed by a - * strong_ptr - */ - [[nodiscard]] strong_ptr strong_from_this() const { - auto locked = m_weak_this.lock(); - if (!locked) { - throw std::bad_weak_ptr(&weak_from_this); - } - // Cast the strong_ptr to strong_ptr - return strong_ptr(locked.value()); - } - - /** - * @brief Get a weak_ptr to this object - * - * @return weak_ptr pointing to this object - */ - [[nodiscard]] weak_ptr weak_from_this() noexcept { - return m_weak_this; - } - - /** - * @brief Get a weak_ptr to this object (const version) - * - * @return weak_ptr pointing to this object - */ - [[nodiscard]] weak_ptr weak_from_this() const noexcept { - return weak_ptr(m_weak_this); - } - - protected: - /** - * @brief Protected constructor to prevent direct instantiation - */ - enable_strong_from_this() = default; - - /** - * @brief Protected copy constructor - * - * Note: The weak_ptr is not copied - each object gets its own weak - * reference - */ - enable_strong_from_this(enable_strong_from_this const&) noexcept { - // Intentionally don't copy m_weak_this - } - - /** - * @brief Protected assignment operator - * - * Note: The weak_ptr is not assigned - each object keeps its own weak - * reference - */ - enable_strong_from_this& operator=( - enable_strong_from_this const&) noexcept { - // Intentionally don't assign m_weak_this - return *this; - } - - /** - * @brief Protected destructor - */ - ~enable_strong_from_this() = default; - - private: - template - friend strong_ptr make_strong_ptr(std::pmr::polymorphic_allocator<>, - Args&&...); - - /** - * @brief Initialize the weak reference (called by make_strong_ptr) - * - * @param p_self The strong_ptr that manages this object - */ - void init_weak_this(strong_ptr const& p_self) noexcept { - m_weak_this = p_self; - } - - mutable weak_ptr m_weak_this; - }; - - template - class optional_ptr; - - /** - * @brief A weak reference to a strong_ptr - * - * weak_ptr provides a non-owning reference to an object managed by - * strong_ptr. It can be used to break reference cycles or to create an - * optional_ptr. - * - * A weak_ptr doesn't increase the strong reference count, so it doesn't - * prevent the object from being destroyed when the last strong_ptr goes - * away. - * - * Example usage: - * ``` - * // Create a strong_ptr to an object - * auto ptr = hal::make_strong_ptr(allocator, args...); - * - * // Create a weak reference - * weak_ptr weak = ptr; - * - * // Later, try to get a strong reference - * if (auto locked = weak.lock()) { - * // Use the object via locked - * locked->doSomething(); - * } else { - * // Object has been destroyed - * } - * ``` - * - * @tparam T The type of the referenced object - */ - template - class weak_ptr { - public: - template - friend class strong_ptr; - - template - friend class weak_ptr; - - using element_type = T; - - /** - * @brief Default constructor creates empty weak_ptr - */ - weak_ptr() noexcept = default; - - /** - * @brief Create weak_ptr from strong_ptr - * - * @param p_strong The strong_ptr to create a weak reference to - */ - weak_ptr(strong_ptr const& p_strong) noexcept - : m_ctrl(p_strong.m_ctrl) - , m_ptr(p_strong.m_ptr) { - if (m_ctrl) { - ptr_add_weak(m_ctrl); - } - } - - /** - * @brief Copy constructor - * - * @param p_other The weak_ptr to copy from - */ - weak_ptr(weak_ptr const& p_other) noexcept - : m_ctrl(p_other.m_ctrl) - , m_ptr(p_other.m_ptr) { - if (m_ctrl) { - ptr_add_weak(m_ctrl); - } - } - - /** - * @brief Move constructor - * - * @param p_other The weak_ptr to move from - */ - weak_ptr(weak_ptr&& p_other) noexcept - : m_ctrl(p_other.m_ctrl) - , m_ptr(p_other.m_ptr) { - p_other.m_ctrl = nullptr; - p_other.m_ptr = nullptr; - } - - /** - * @brief Converting copy constructor - * - * Creates a weak_ptr of T from a weak_ptr of U where U is convertible - * to T. - * - * @tparam U A type convertible to T - * @param p_other The weak_ptr to copy from - */ - template - weak_ptr(weak_ptr const& p_other) noexcept - requires(std::is_convertible_v) - : m_ctrl(p_other.m_ctrl) - , m_ptr(static_cast(p_other.m_ptr)) { - if (m_ctrl) { - ptr_add_weak(m_ctrl); - } - } - - /** - * @brief Converting move constructor - * - * Moves a weak_ptr of U to a weak_ptr T where U is convertible to T. - * - * @tparam U A type convertible to T - * @param p_other The weak_ptr to move from - */ - template - weak_ptr(weak_ptr&& p_other) noexcept - requires(std::is_convertible_v) - : m_ctrl(p_other.m_ctrl) - , m_ptr(static_cast(p_other.m_ptr)) { - p_other.m_ctrl = nullptr; - p_other.m_ptr = nullptr; - } - - /** - * @brief Converting constructor from strong_ptr - * - * Creates a weak_ptr from a strong_ptr where U is convertible to T. - * - * @tparam U A type convertible to T - * @param p_other The strong_ptr to create a weak reference to - */ - template - weak_ptr(strong_ptr const& p_other) noexcept - requires(std::is_convertible_v) - : m_ctrl(p_other.m_ctrl) - , m_ptr(static_cast(p_other.m_ptr)) { - if (m_ctrl) { - ptr_add_weak(m_ctrl); - } - } - - /** - * @brief Destructor - * - * Decrements the weak reference count and potentially deallocates - * memory if this was the last reference. - */ - ~weak_ptr() { - if (m_ctrl) { - ptr_release_weak(m_ctrl); - } - } - - /** - * @brief Copy assignment operator - * - * @param p_other The weak_ptr to copy from - * @return Reference to *this - */ - weak_ptr& operator=(weak_ptr const& p_other) noexcept { - weak_ptr(p_other).swap(*this); - return *this; - } - - /** - * @brief Move assignment operator - * - * @param p_other The weak_ptr to move from - * @return Reference to *this - */ - weak_ptr& operator=(weak_ptr&& p_other) noexcept { - weak_ptr(std::move(p_other)).swap(*this); - return *this; - } - - /** - * @brief Assignment from strong_ptr - * - * @param p_strong The strong_ptr to create a weak reference to - * @return Reference to *this - */ - weak_ptr& operator=(strong_ptr const& p_strong) noexcept { - weak_ptr(p_strong).swap(*this); - return *this; - } - - /** - * @brief Swap the contents of this weak_ptr with another - * - * @param p_other The weak_ptr to swap with - */ - void swap(weak_ptr& p_other) noexcept { - std::swap(m_ctrl, p_other.m_ctrl); - std::swap(m_ptr, p_other.m_ptr); - } - - /** - * @brief Check if the referenced object has been destroyed - * - * @return true if the object has been destroyed, false otherwise - */ - [[nodiscard]] bool expired() const noexcept { - return !m_ctrl || - m_ctrl->strong_count.load(std::memory_order_relaxed) == 0; - } - - /** - * @brief Attempt to obtain a strong_ptr to the referenced object - * - * If the object still exists, returns an optional_ptr containing - * a strong_ptr to it. Otherwise, returns an empty optional_ptr. - * - * @return An optional_ptr that is either empty or contains a strong_ptr - */ - [[nodiscard]] optional_ptr lock() const noexcept; - - /** - * @brief Get the current strong reference count - * - * This is primarily for testing purposes. - * - * @return The number of strong references to the managed object - */ - [[nodiscard]] auto use_count() const noexcept { - return m_ctrl ? m_ctrl->strong_count.load(std::memory_order_relaxed) - : 0; - } - - private: - detail::ref_info* m_ctrl = nullptr; - T* m_ptr = nullptr; - }; - - /** - * @brief Optional, nullable, smart pointer that works with - * `hal::strong_ptr`. - * - * optional_ptr provides a way to represent a strong_ptr that may or may not - * be present. Unlike strong_ptr, which is always valid, optional_ptr can be - * in a "disengaged" state where it doesn't reference any object. - * - * Use optional_ptr when you need a nullable reference to a - * reference-counted object, such as: - * - Representing the absence of a value - * - Return values from functions that may fail - * - Results of locking a weak_ptr - * - * Example usage: - * ``` - * // Create an empty optional_ptr - * optional_ptr opt1; - * - * // Create an optional_ptr from a strong_ptr - * auto ptr = make_strong_ptr(allocator, args...); - * optional_ptr opt2 = ptr; - * - * // Check if the optional_ptr is engaged - * if (opt2) { - * // Use the contained object - * opt2->doSomething(); - * } - * - * // Reset to disengage - * opt2.reset(); - * ``` - * - * @tparam T - The type pointed to by strong_ptr - */ - template - class optional_ptr { - public: - /** - * @brief Default constructor creates a disengaged optional - */ - constexpr optional_ptr() noexcept {} - - /** - * @brief Constructor for nullptr (creates a disengaged optional) - */ - constexpr optional_ptr(std::nullptr_t) noexcept {} - - /** - * @brief Move constructor is deleted - */ - constexpr optional_ptr(optional_ptr&& p_other) noexcept = delete; - - /** - * @brief Construct from a strong_ptr lvalue - * - * @param value The strong_ptr to wrap - */ - constexpr optional_ptr(strong_ptr const& value) noexcept - : m_value(value) {} - - /** - * @brief Copy constructor - * - * @param p_other The optional_ptr to copy from - */ - constexpr optional_ptr(optional_ptr const& p_other) { *this = p_other; } - - /** - * @brief Converting constructor from a strong_ptr - * - * @tparam U A type convertible to T - * @param p_value The strong_ptr to wrap - */ - template - constexpr optional_ptr(strong_ptr const& p_value) - requires(std::is_convertible_v) - { - *this = p_value; - } - - /** - * @brief Move assignment operator is deleted - */ - constexpr optional_ptr& operator=(optional_ptr&& other) noexcept = - delete; - - /** - * @brief Copy assignment operator - * - * @param other The optional_ptr to copy from - * @return Reference to *this - */ - constexpr optional_ptr& operator=(optional_ptr const& other) { - if (this != &other) { - if (is_engaged() && other.is_engaged()) { - m_value = other.m_value; - } - else if (is_engaged() && not other.is_engaged()) { - reset(); - } - else if (not is_engaged() && other.is_engaged()) { - new (&m_value) strong_ptr(other.m_value); - } - } - return *this; - } - - /** - * @brief Assignment from a strong_ptr - * - * @param value The strong_ptr to wrap - * @return Reference to *this - */ - constexpr optional_ptr& operator=(strong_ptr const& value) noexcept { - if (is_engaged()) { - m_value = value; - } - else { - new (&m_value) strong_ptr(value); - } - return *this; - } - - /** - * @brief Converting assignment from a strong_ptr - * - * @tparam U A type convertible to T - * @param p_value The strong_ptr to wrap - * @return Reference to *this - */ - template - constexpr optional_ptr& operator=(strong_ptr const& p_value) noexcept - requires(std::is_convertible_v) - { - if (is_engaged()) { - m_value = p_value; - } - else { - new (&m_value) strong_ptr(p_value); - } - return *this; - } - - /** - * @brief Assignment from nullptr (resets to disengaged state) - * - * @return Reference to *this - */ - constexpr optional_ptr& operator=(std::nullptr_t) noexcept { - reset(); - return *this; - } - - /** - * @brief Destructor - * - * Properly destroys the contained strong_ptr if engaged. - */ - ~optional_ptr() { - if (is_engaged()) { - m_value.~strong_ptr(); - } - } - - /** - * @brief Check if the optional_ptr is engaged - * - * @return true if the optional_ptr contains a value, false otherwise - */ - [[nodiscard]] constexpr bool has_value() const noexcept { - return is_engaged(); - } - - /** - * @brief Check if the optional_ptr is engaged - * - * @return true if the optional_ptr contains a value, false otherwise - */ - constexpr explicit operator bool() const noexcept { - return is_engaged(); - } - - /** - * @brief Access the contained value, throw if not engaged - * - * @return A copy of the contained strong_ptr - * @throws hal::bad_optional_ptr_access if *this is disengaged - */ - [[nodiscard]] constexpr strong_ptr& value() { - if (!is_engaged()) { - throw std::bad_optional_access(); - } - return m_value; - } - - /** - * @brief Access the contained value, throw if not engaged (const - * version) - * - * @return A copy of the contained strong_ptr - * @throws hal::bad_optional_ptr_access if *this is disengaged - */ - [[nodiscard]] constexpr strong_ptr const& value() const { - if (!is_engaged()) { - throw std::bad_optional_access(); - } - return m_value; - } - - /** - * @brief Implicitly convert to a strong_ptr - * - * This allows optional_ptr to be used anywhere a strong_ptr is expected - * when the optional_ptr is engaged. - * - * @return A copy of the contained strong_ptr - * @throws hal::bad_optional_ptr_access if *this is disengaged - */ - [[nodiscard]] constexpr operator strong_ptr() { return value(); } - - /** - * @brief Implicitly convert to a strong_ptr (const version) - * - * @return A copy of the contained strong_ptr - * @throws hal::bad_optional_ptr_access if *this is disengaged - */ - [[nodiscard]] constexpr operator strong_ptr() const { - return value(); - } - - /** - * @brief Implicitly convert to a strong_ptr for polymorphic types - * - * This allows optional_ptr to be converted to strong_ptr - * when Derived is convertible to Base. - * - * @tparam U The target type (must be convertible from T) - * @return A copy of the contained strong_ptr, converted to the target - * type - * @throws hal::bad_optional_ptr_access if *this is disengaged - */ - template - [[nodiscard]] constexpr operator strong_ptr() - requires(std::is_convertible_v && !std::is_same_v) - { - if (!is_engaged()) { - throw std::bad_optional_access(); - } - // strong_ptr handles the polymorphic conversion - return strong_ptr(m_value); - } - - /** - * @brief Implicitly convert to a strong_ptr for polymorphic types - * (const version) - * - * @tparam U The target type (must be convertible from T) - * @return A copy of the contained strong_ptr, converted to the target - * type - * @throws hal::bad_optional_ptr_access if *this is disengaged - */ - template - [[nodiscard]] constexpr operator strong_ptr() const - requires(std::is_convertible_v && !std::is_same_v) - { - if (!is_engaged()) { - // hal::safe_throw(hal::bad_optional_ptr_access(this)); - throw std::bad_optional_access(); - } - // strong_ptr handles the polymorphic conversion - return strong_ptr(m_value); - } - - /** - * @brief Arrow operator for accessing members of the contained object - * - * @return Pointer to the object managed by the contained strong_ptr - */ - [[nodiscard]] constexpr auto* operator->() { - auto& ref = *(this->value()); - return &ref; - } - - /** - * @brief Arrow operator for accessing members of the contained object - * (const version) - * - * @return Pointer to the object managed by the contained strong_ptr - */ - [[nodiscard]] constexpr auto* operator->() const { - auto& ref = *(this->value()); - return &ref; - } - - /** - * @brief Dereference operator for accessing the contained object - * - * @return Reference to the object managed by the contained strong_ptr - */ - [[nodiscard]] constexpr auto& operator*() { - auto& ref = *(this->value()); - return ref; - } - - /** - * @brief Dereference operator for accessing the contained object (const - * version) - * - * @return Reference to the object managed by the contained strong_ptr - */ - [[nodiscard]] constexpr auto& operator*() const { - auto& ref = *(this->value()); - return ref; - } - - /** - * @brief Reset the optional to a disengaged state - */ - constexpr void reset() noexcept { - if (is_engaged()) { - m_value.~strong_ptr(); - m_raw_ptrs[0] = nullptr; - m_raw_ptrs[1] = nullptr; - } - } - - /** - * @brief Emplace a new value - * - * Reset the optional and construct a new strong_ptr in-place. - * - * @tparam Args Types of arguments to forward to the constructor - * @param args Arguments to forward to the constructor - * @return Reference to the newly constructed strong_ptr - */ - template - constexpr strong_ptr& emplace(Args&&... args) { - reset(); - new (&m_value) strong_ptr(std::forward(args)...); - return m_value; - } - - /** - * @brief Swap the contents of this optional_ptr with another - * - * @param other The optional_ptr to swap with - */ - constexpr void swap(optional_ptr& other) noexcept { - if (is_engaged() && other.is_engaged()) { - std::swap(m_value, other.m_value); - } - else if (is_engaged() && !other.is_engaged()) { - new (&other.m_value) strong_ptr(std::move(m_value)); - reset(); - } - else if (!is_engaged() && other.is_engaged()) { - new (&m_value) strong_ptr(std::move(other.m_value)); - other.reset(); - } - } - - private: - /** - * @brief Use the strong_ptr's memory directly through a union - * - * This allows us to detect whether the optional_ptr is engaged - * by checking if the internal pointers are non-null. - */ - union { - std::array m_raw_ptrs = { nullptr, nullptr }; - strong_ptr m_value; - }; - - // Ensure the strong_ptr layout matches our expectations - static_assert(sizeof(m_value) == sizeof(m_raw_ptrs), - "strong_ptr must be exactly the size of two pointers"); - - /** - * @brief Helper to check if the optional is engaged - * - * @return true if the optional_ptr contains a value, false otherwise - */ - [[nodiscard]] constexpr bool is_engaged() const noexcept { - return m_raw_ptrs[0] != nullptr || m_raw_ptrs[1] != nullptr; - } - }; - - /** - * @brief Implement weak_ptr::lock() now that optional_ptr is defined - * - * This function attempts to obtain a strong_ptr from a weak_ptr. - * If the referenced object still exists, it returns an optional_ptr - * containing a strong_ptr to it. Otherwise, it returns an empty - * optional_ptr. - * - * @tparam T The type of the referenced object - * @return An optional_ptr that is either empty or contains a strong_ptr - */ - template - [[nodiscard]] inline optional_ptr weak_ptr::lock() const noexcept { - if (expired()) { - return nullptr; - } - - // Try to increment the strong count - auto current_count = - m_ctrl->strong_count.load(std::memory_order_relaxed); - while (current_count > 0) { - // TODO(kammce): Consider if this is dangerous - if (m_ctrl->strong_count.compare_exchange_weak( - current_count, - current_count + 1, - std::memory_order_acq_rel, - std::memory_order_relaxed)) { - // Successfully incremented - auto obj = strong_ptr(m_ctrl, m_ptr); - return obj; - } - } - - // Strong count is now 0 - return nullptr; - } - - /** - * @brief Non-member swap for strong_ptr - * - * @tparam T The type of the managed object - * @param p_lhs First strong_ptr to swap - * @param p_rhs Second strong_ptr to swap - */ - template - void swap(strong_ptr& p_lhs, strong_ptr& p_rhs) noexcept { - p_lhs.swap(p_rhs); - } - - /** - * @brief Non-member swap for weak_ptr - * - * @tparam T The type of the referenced object - * @param p_lhs First weak_ptr to swap - * @param p_rhs Second weak_ptr to swap - */ - template - void swap(weak_ptr& p_lhs, weak_ptr& p_rhs) noexcept { - p_lhs.swap(p_rhs); - } - - /** - * @brief Equality operator for strong_ptr - * - * Compares if two strong_ptr instances point to the same object. - * - * @tparam T The type of the first strong_ptr - * @tparam U The type of the second strong_ptr - * @param p_lhs First strong_ptr to compare - * @param p_rhs Second strong_ptr to compare - * @return true if both point to the same object, false otherwise - */ - template - bool operator==(strong_ptr const& p_lhs, - strong_ptr const& p_rhs) noexcept { - return p_lhs.operator->() == p_rhs.operator->(); - } - - /** - * @brief Inequality operator for strong_ptr - * - * Compares if two strong_ptr instances point to different objects. - * - * @tparam T The type of the first strong_ptr - * @tparam U The type of the second strong_ptr - * @param p_lhs First strong_ptr to compare - * @param p_rhs Second strong_ptr to compare - * @return true if they point to different objects, false otherwise - */ - template - bool operator!=(strong_ptr const& p_lhs, - strong_ptr const& p_rhs) noexcept { - return !(p_lhs == p_rhs); - } - /** - * @brief Equality operator for optional_ptr - * - * Compares if two optional_ptr instances are equal - they are equal if: - * 1. Both are disengaged (both empty) - * 2. Both are engaged and point to the same object - * - * @tparam T The type of the first optional_ptr - * @tparam U The type of the second optional_ptr - * @param p_lhs First optional_ptr to compare - * @param p_rhs Second optional_ptr to compare - * @return true if both are equal according to the rules above - */ - template - [[nodiscard]] bool operator==(optional_ptr const& p_lhs, - optional_ptr const& p_rhs) noexcept { - // If both are disengaged, they're equal - if (!p_lhs.has_value() && !p_rhs.has_value()) { - return true; - } - - // If one is engaged and the other isn't, they're not equal - if (p_lhs.has_value() != p_rhs.has_value()) { - return false; - } - - // Both are engaged, compare the underlying pointers - return p_lhs.value().operator->() == p_rhs.value().operator->(); - } - - /** - * @brief Inequality operator for optional_ptr - * - * Returns the opposite of the equality operator. - * - * @tparam T The type of the first optional_ptr - * @tparam U The type of the second optional_ptr - * @param p_lhs First optional_ptr to compare - * @param p_rhs Second optional_ptr to compare - * @return true if they are not equal - */ - template - [[nodiscard]] bool operator!=(optional_ptr const& p_lhs, - optional_ptr const& p_rhs) noexcept { - return !(p_lhs == p_rhs); - } - - /** - * @brief Equality operator between optional_ptr and nullptr - * - * An optional_ptr equals nullptr if it's disengaged. - * - * @tparam T The type of the optional_ptr - * @param p_lhs The optional_ptr to compare - * @return true if the optional_ptr is disengaged - */ - template - [[nodiscard]] bool operator==(optional_ptr const& p_lhs, - std::nullptr_t) noexcept { - return !p_lhs.has_value(); - } - - /** - * @brief Equality operator between nullptr and optional_ptr - * - * nullptr equals an optional_ptr if it's disengaged. - * - * @tparam T The type of the optional_ptr - * @param p_rhs The optional_ptr to compare - * @return true if the optional_ptr is disengaged - */ - template - [[nodiscard]] bool operator==(std::nullptr_t, - optional_ptr const& p_rhs) noexcept { - return !p_rhs.has_value(); - } - - /** - * @brief Inequality operator between optional_ptr and nullptr - * - * An optional_ptr does not equal nullptr if it's engaged. - * - * @tparam T The type of the optional_ptr - * @param p_lhs The optional_ptr to compare - * @return true if the optional_ptr is engaged - */ - template - [[nodiscard]] bool operator!=(optional_ptr const& p_lhs, - std::nullptr_t) noexcept { - return p_lhs.has_value(); - } - - /** - * @brief Inequality operator between nullptr and optional_ptr - * - * nullptr does not equal an optional_ptr if it's engaged. - * - * @tparam T The type of the optional_ptr - * @param p_rhs The optional_ptr to compare - * @return true if the optional_ptr is engaged - */ - template - [[nodiscard]] bool operator!=(std::nullptr_t, - optional_ptr const& p_rhs) noexcept { - return p_rhs.has_value(); - } - - /** - * @brief A construction token that can only be created by make_strong_ptr - * - * Make the first parameter of your class's constructor(s) in order to limit - * that constructor to only be used via `make_strong_ptr`. - */ - class strong_ptr_only_token { - private: - strong_ptr_only_token() = default; - - template - friend strong_ptr make_strong_ptr(std::pmr::polymorphic_allocator<>, - Args&&...); - }; - - /** - * @brief Factory function to create a strong_ptr with automatic - * construction detection - * - * This is the primary way to create a new strong_ptr. It automatically - * detects whether the target type requires token-based construction (for - * classes that should only be managed by strong_ptr) or supports normal - * construction. - * - * The function performs the following operations: - * 1. Allocates memory for both the object and its control block together - * 2. Detects at compile time if the type expects a strong_ptr_only_token - * 3. Constructs the object with appropriate parameters - * 4. Initializes enable_strong_from_this support if the type inherits from - * it - * 5. Returns a strong_ptr managing the newly created object - * - * **Token-based construction**: If a class constructor takes - * `strong_ptr_only_token` as its first parameter, this function - * automatically provides that token, ensuring the class can only be - * constructed via make_strong_ptr. - * - * **Normal construction**: For regular classes, construction proceeds - * normally with the provided arguments. - * - * **enable_strong_from_this support**: If the constructed type inherits - * from `enable_strong_from_this`, the weak reference is automatically - * initialized to enable `strong_from_this()` and `weak_from_this()` - * functionality. - * - * Example usage: - * - * ```cpp - * // Token-protected class (can only be created via make_strong_ptr) - * class protected_driver { - * public: - * protected_driver(strong_ptr_only_token, driver_config config); - * }; - * auto driver = make_strong_ptr(allocator, config{}); - * - * // Regular class - * class regular_class { - * public: - * regular_class(int value); - * }; - * auto obj = make_strong_ptr(allocator, 42); - * - * // Class with enable_strong_from_this support - * class self_aware : public enable_strong_from_this { - * public: - * self_aware(std::string name); - * void register_callback() { - * auto self = strong_from_this(); // This works automatically - * } - * }; - * auto obj = make_strong_ptr(allocator, "example"); - * ``` - * - * @tparam T The type of object to create - * @tparam Args Types of arguments to forward to the constructor - * @param p_alloc Allocator to use for memory allocation - * @param p_args Arguments to forward to the constructor - * @return A strong_ptr managing the newly created object - * @throws Any exception thrown by the object's constructor - * @throws std::bad_alloc if memory allocation fails - */ - template - [[nodiscard]] inline strong_ptr make_strong_ptr( - std::pmr::polymorphic_allocator<> p_alloc, - Args&&... p_args) { - using rc_t = detail::rc; - - rc_t* obj = nullptr; - - if constexpr (std:: - is_constructible_v) { - // Type expects token as first parameter - obj = p_alloc.new_object( - p_alloc, strong_ptr_only_token{}, std::forward(p_args)...); - } - else { - // Normal type, construct without token - obj = - p_alloc.new_object(p_alloc, std::forward(p_args)...); - } - - strong_ptr result(&obj->m_info, &obj->m_object); - - // Initialize enable_strong_from_this if the type inherits from it - if constexpr (std::is_base_of_v, T>) { - result->init_weak_this(result); - } - - return result; - } -} // namespace atlas::memory diff --git a/atlas/core/utilities/poll_state.cppm b/atlas/core/utilities/poll_state.cppm new file mode 100644 index 00000000..62152d26 --- /dev/null +++ b/atlas/core/utilities/poll_state.cppm @@ -0,0 +1,107 @@ +module; + +#include +#include + +export module atlas.core.utilities.poll_state; + + +export namespace atlas { + inline std::unordered_map> s_update{}; + inline std::unordered_map> + s_defer_update{}; + inline std::unordered_map> s_ui_update{}; + inline std::unordered_map> + s_physica_update{}; + inline std::unordered_map> s_start{}; + + // TODO: Look into a different way of doing this + void poll_update(void* p_address,const std::function& p_callback) { + s_update.emplace(p_address, p_callback); + } + + void poll_defer_update(void* p_address, const std::function& p_callback) { + s_defer_update.emplace(p_address, p_callback); + } + + void poll_physics_update(void* p_address, const std::function& p_callback) { + s_physica_update.emplace(p_address, p_callback); + } + + void poll_ui_update(void* p_address, const std::function& p_callback) { + s_ui_update.emplace(p_address, p_callback); + } + + void poll_start(void* p_address, const std::function& p_callback) { + s_start.emplace(p_address, p_callback); + } + + // TEMP: This is a temporary solution, should look into doing this + // differently + void remove_update(void* p_address) { + s_update.erase(p_address); + } + + void remove_defer_update(void* p_address) { + s_defer_update.erase(p_address); + } + + void remove_physics_update(void* p_address) { + s_physica_update.erase(p_address); + } + + void remove_ui_update(void* p_address) { + s_ui_update.erase(p_address); + } + + void remove_start(void* p_address) { + s_start.erase(p_address); + } + + /** + * @brief detail namespace is used for any internals that should not be + * accessed by the user + * + * @note When switching to C++'s modules, hopefully this removes the + * needs for having to represent namespaces in this way. + * + * Any invoke_* function is an internal detail that handles where those + * behaviorial state callbacks get handled into their respective state + * queue's + * + * Where the state queue will call those arbitrary callbacks at their + * respective point in each of the frame. + * + * As thesse are intended for invoking those queue's directly. + * + */ + void invoke_on_update(float p_delta_time) { + for (auto& [address, on_update] : s_update) { + on_update(p_delta_time); + } + } + + void invoke_defer_update() { + for (auto& [address, on_update] : s_defer_update) { + on_update(); + } + } + + void invoke_physics_update() { + for (auto& [address, on_update] : s_physica_update) { + on_update(); + } + } + + void invoke_ui_update() { + for (auto& [address, on_update] : s_ui_update) { + on_update(); + } + } + + void invoke_start() { + for (auto& [address, on_update] : s_start) { + on_update(); + } + } +}; \ No newline at end of file diff --git a/atlas/core/utilities/state.hpp b/atlas/core/utilities/state.cppm similarity index 72% rename from atlas/core/utilities/state.hpp rename to atlas/core/utilities/state.cppm index 6183f6db..214e976c 100644 --- a/atlas/core/utilities/state.hpp +++ b/atlas/core/utilities/state.cppm @@ -1,63 +1,13 @@ -#pragma once -#include -#include - -namespace atlas { - - namespace detail { - /** - * @brief detail namespace is used for any internals that should not be - * accessed by the user - * - * @note When switching to C++'s modules, hopefully this removes the - * needs for having to represent namespaces in this way. - * - * Any invoke_* function is an internal detail that handles where those - * behaviorial state callbacks get handled into their respective state - * queue's - * - * Where the state queue will call those arbitrary callbacks at their - * respective point in each of the frame. - * - * As thesse are intended for invoking those queue's directly. - * - */ - void invoke_on_update(); - void invoke_defer_update(); - void invoke_physics_update(); - void invoke_ui_update(); - void invoke_start(); - - // TODO: Look into a different way of doing this - void poll_update(void* p_address, - const std::function& p_callback); - - void poll_defer_update(void* p_address, - const std::function& p_callback); - - void poll_physics_update(void* p_address, - const std::function& p_callback); - - void poll_ui_update(void* p_address, - const std::function& p_callback); +module; - void poll_start(void* p_address, - const std::function& p_callback); - - // TEMP: This is a temporary solution, should look into doing this - // differently - void remove_update(void* p_address); - - void remove_defer_update(void* p_address); - - void remove_physics_update(void* p_address); - - void remove_ui_update(void* p_address); +#include +#include - void remove_start(void* p_address); +export module atlas.core.utilities.state; - }; +import atlas.core.utilities.poll_state; +export namespace atlas { /** * @brief preloading any behavior that may be required of the users game * objects, such as pre-loading assets or any metadata after construction of @@ -88,7 +38,7 @@ namespace atlas { static_assert(std::is_member_pointer_v, "Cannot register a function that is not a member " "function of a class object"); - detail::poll_start(p_instance, [p_instance, p_callable]() { + poll_start(p_instance, [p_instance, p_callable]() { (p_instance->*p_callable)(); }); } @@ -120,8 +70,8 @@ namespace atlas { static_assert(std::is_member_pointer_v, "Cannot register a function that is not a member " "function of a class object"); - detail::poll_update(p_instance, [p_instance, p_callable]() { - (p_instance->*p_callable)(); + poll_update(p_instance, [p_instance, p_callable](float p_delta_time) { + (p_instance->*p_callable)(p_delta_time); }); } @@ -150,7 +100,7 @@ namespace atlas { static_assert(std::is_member_pointer_v, "Cannot register a function that is not a member " "function of a class object"); - detail::poll_physics_update(p_instance, [p_instance, p_callable]() { + poll_physics_update(p_instance, [p_instance, p_callable]() { (p_instance->*p_callable)(); }); } @@ -182,7 +132,7 @@ namespace atlas { static_assert(std::is_member_pointer_v, "Cannot register a function that is not a member " "function of a class object"); - detail::poll_defer_update(p_instance, [p_instance, p_callable]() { + poll_defer_update(p_instance, [p_instance, p_callable]() { (p_instance->*p_callable)(); }); } @@ -216,9 +166,8 @@ namespace atlas { static_assert(std::is_member_pointer_v, "Cannot register a function that is not a member " "function of a class object"); - detail::poll_ui_update(p_instance, [p_instance, p_callable]() { + poll_ui_update(p_instance, [p_instance, p_callable]() { (p_instance->*p_callable)(); }); } - }; \ No newline at end of file diff --git a/atlas/core/utilities/types.cppm b/atlas/core/utilities/types.cppm new file mode 100644 index 00000000..e62206fd --- /dev/null +++ b/atlas/core/utilities/types.cppm @@ -0,0 +1,22 @@ +module; + +#include +#include +#include + +export module atlas.core.utilities.types; + + +export namespace atlas { + struct window_params { + uint32_t width; + uint32_t height; + std::string name=""; + }; + + // TODO: Move this into atlas.drivers.vulkan.utilities + struct surface_properties { + VkSurfaceCapabilitiesKHR surface_capabilities; + VkSurfaceFormatKHR surface_format; + }; +}; \ No newline at end of file diff --git a/atlas/core/utilities/types.hpp b/atlas/core/utilities/types.hpp deleted file mode 100644 index a2fb6192..00000000 --- a/atlas/core/utilities/types.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once -#include -#include - -namespace atlas { - /** - * @brief settings for specification for atlas::window - */ - struct window_settings { - uint32_t width = -1; - uint32_t height = -1; - std::string name = ""; - uint32_t frames_in_flight = 2; - }; -}; \ No newline at end of file diff --git a/atlas/core/utilities/utilities.cppm b/atlas/core/utilities/utilities.cppm new file mode 100644 index 00000000..0261aff2 --- /dev/null +++ b/atlas/core/utilities/utilities.cppm @@ -0,0 +1,7 @@ +export module atlas.core.utilities; + +export import atlas.logger; +export import atlas.common; +export import atlas.graphics_api; +export import atlas.core.utilities.types; +export import atlas.core.utilities.state; diff --git a/atlas/core/window.hpp b/atlas/core/window.cppm similarity index 52% rename from atlas/core/window.hpp rename to atlas/core/window.cppm index 0bc188b0..a04421c0 100644 --- a/atlas/core/window.hpp +++ b/atlas/core/window.cppm @@ -1,43 +1,41 @@ -#pragma once +module; + #include -#include -#include -#include -#include -#include - -namespace atlas { - - /** - * @brief Represent an entire window that lives throughout the entire - * duration of the application - * - * There should only be one window that is living throughout the - * applications lifetime - */ +#include +#include + +export module atlas.window; + +// import atlas.common; +// import atlas.graphics_api; +// import atlas.core.utilities.types; +import atlas.core.utilities; +import atlas.drivers.vulkan.swapchain; +import vk; + +export namespace atlas { + class window { public: virtual ~window() = default; - /** - * @brief Returns the width dimension of the window - */ - [[nodiscard]] uint32_t width() const; - - /** - * @brief Returns the height dimension of the window - */ - [[nodiscard]] uint32_t height() const; + [[nodiscard]] window_params data() const { + return get_params(); + } /** * @brief Checks if window is available to close */ - [[nodiscard]] bool available() const; + [[nodiscard]] bool available() const { + return !glfwWindowShouldClose(native_window()); + } /** * @brief Returns the aspect ratio of the current window */ - [[nodiscard]] float aspect_ratio() const; + [[nodiscard]] float aspect_ratio() const { + return static_cast(get_params().width) / static_cast(get_params().height); + } /** * @brief gives you the next presentable image to use and the index to @@ -52,7 +50,7 @@ namespace atlas { /** * @brief Returns the window's currently selected swapchain */ - [[nodiscard]] vk::vk_swapchain current_swapchain() const { + [[nodiscard]] vulkan::swapchain current_swapchain() const { return window_swapchain(); } @@ -63,7 +61,7 @@ namespace atlas { * * @return command buffer to actively record commands to */ - ::vk::command_buffer active_command(uint32_t p_frame_index) { + vk::command_buffer active_command(uint32_t p_frame_index) { return current_active_command(p_frame_index); } @@ -82,7 +80,9 @@ namespace atlas { /** * @brief Closing the window operation */ - void close(); + void close() { + glfwSetWindowShouldClose(native_window(), true); + } /** * @brief does the presentation operation that is operated internally @@ -91,29 +91,18 @@ namespace atlas { * @param p_current_frame_idx is current frame index to currently * process an image in the current frame */ - void present(const uint32_t& p_current_frame_idx); + void present(const uint32_t& p_current_frame_idx) { + return present_frame(p_current_frame_idx); + } - private: - [[nodiscard]] virtual window_settings settings() const = 0; + protected: + [[nodiscard]] virtual window_params get_params() const = 0; [[nodiscard]] virtual GLFWwindow* native_window() const = 0; [[nodiscard]] virtual uint32_t read_acquired_next_frame() = 0; - [[nodiscard]] virtual vk::vk_swapchain window_swapchain() const = 0; + [[nodiscard]] virtual vulkan::swapchain window_swapchain() const = 0; - [[nodiscard]] virtual ::vk::command_buffer current_active_command( - uint32_t p_frame_idx) = 0; + [[nodiscard]] virtual vk::command_buffer current_active_command(uint32_t p_frame_idx) = 0; - virtual void presentation_process(const uint32_t& p_current_frame) = 0; + virtual void present_frame(const uint32_t& p_current_frame) = 0; }; - - /** - * @brief constructs an atlas::window - * - * There should only ever be one window constructed throughout the entire - * application - * - * @param p_settings is the window settings to construct the window with - * - * @return shared_ptr - */ - ref create_window(const window_settings& p_settings); -}; +}; \ No newline at end of file diff --git a/atlas/drivers/drivers.cppm b/atlas/drivers/drivers.cppm new file mode 100644 index 00000000..8a849984 --- /dev/null +++ b/atlas/drivers/drivers.cppm @@ -0,0 +1,37 @@ +module; + +#include + +export module atlas.drivers; + +import atlas.core.utilities; +import atlas.window; +import atlas.drivers.graphics_context; +import atlas.drivers.vulkan.window_context; + +/** + * @brief This drivers.cppm will contain API-agnostic implementation that may be widely implemented differently. + * + * Such as Window contexts, graphics API-agnostic implementation, renderers, etc. +*/ + +export namespace atlas { + /** + * @brief constructs an atlas::window + * + * There should only ever be one window constructed throughout the entire + * application + * + * @param p_settings is the window settings to construct the window with + * + * @return shared_ptr + */ + ref initialize_window(ref p_context, const window_params& p_params, graphics_api p_api) { + switch(p_api) { + case graphics_api::vulkan: + return create_ref(p_context, p_params); + default: + return nullptr; + } + } +}; \ No newline at end of file diff --git a/atlas/drivers/graphics_context.cppm b/atlas/drivers/graphics_context.cppm new file mode 100644 index 00000000..8a966947 --- /dev/null +++ b/atlas/drivers/graphics_context.cppm @@ -0,0 +1,36 @@ +module; + +#include +#include + +export module atlas.drivers.graphics_context; + + + + +export namespace atlas { + + class graphics_context { + public: + virtual ~graphics_context() = default; + + void submit_resource_free(const std::function& p_resource) { + return context_submit_resource_free(p_resource); + } + + [[nodiscard]] VkInstance handle() const { + return context_handle(); + } + + void destroy() { + return destroy_context(); + } + + protected: + virtual void destroy_context() = 0; + virtual void context_submit_resource_free(const std::function& p_resource) = 0; + + [[nodiscard]] virtual VkInstance context_handle() const = 0; + }; + +}; \ No newline at end of file diff --git a/atlas/drivers/graphics_context.hpp b/atlas/drivers/graphics_context.hpp deleted file mode 100644 index cb652d54..00000000 --- a/atlas/drivers/graphics_context.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once -#include - -namespace atlas { - /** - * @brief graphics API-agnostic that is centralized to the graphics API - * themselves - */ - class graphics_context { - public: - virtual ~graphics_context() = default; - - /** - * @brief explicit cleanup of the graphics API context - */ - void destroy() { return destroy_context(); } - - private: - virtual void destroy_context() = 0; - }; - - /** - * @brief construct a new graphics context and initializes that API - * @return shared_ptr - */ - ref initialize_context(const std::string& p_tag); - -}; \ No newline at end of file diff --git a/atlas/drivers/jolt-cpp/jolt-imports.hpp b/atlas/drivers/jolt-cpp/jolt-imports.hpp deleted file mode 100644 index 0b8e097f..00000000 --- a/atlas/drivers/jolt-cpp/jolt-imports.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include - -#include - -// jolt's math includes -#include -#include -#include \ No newline at end of file diff --git a/atlas/drivers/jolt-cpp/jolt_components.hpp b/atlas/drivers/jolt-cpp/jolt_components.hpp deleted file mode 100644 index 51070d50..00000000 --- a/atlas/drivers/jolt-cpp/jolt_components.hpp +++ /dev/null @@ -1,82 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace atlas::physics { - enum thread_type : uint8_t { - default_system = 0, - job_system = 1, - }; - - /** - * @brief Used to keep global data for player access and use. - * Tells how physics bodies should act within a given scene by - * default. - */ - struct jolt_config { - // Global gravity vector for all in scene - glm::vec3 gravity = glm::vec3(0.0f, -9.80665f, 0.0f); - - //! @brief In seconds - float time_before_sleep = 5.0f; - - // What 1 unit refers to in meters - float world_unit_scale = 1.0f; - - // Helps stop the lauching of objects during numerical/flaoting point - // errors when collision happen bertween to objects. - float contact_bias_factor = 0.2f; - float restitution_threshold = 1.0f; - - bool enable_constraints = true; - bool enable_collision_callbacks = true; - }; - - /** - * @brief Jolt-specific context configurations - * These are going to be internally integrated to jolt_context - * - * As these parameters currently are going to be specific to Jolt. - * - * These parameters are also only specific to the construction-level, not - * initiation level of the API's - * - * @remark Min and max world bounds are values that are artbitrary (in other - * words limit the simulation space) as JoltPhysics has a limit on distance - * for its limitation in simulation space - */ - struct jolt_settings { - - uint32_t allocation_amount = 10 * 1024 * 1024; - - //! @brief Specifying which threading system to use for Jolt. - thread_type thread_type = thread_type::default_system; - - uint32_t physics_threads = - std::max(1u, std::thread::hardware_concurrency() - 2); - - uint32_t max_jobs_power = 10; - uint32_t max_barriers = physics_threads * 16; - bool enable_multithread = true; - - // Max memory size per scene - uint32_t max_bodies = 16384; - uint32_t max_body_pairs = 32768; - uint32_t max_contact_constraints = 8192; - }; - - // This might be able to be generalized eventually but we will have to - // create our own manifold before that happens. - struct contact_event { - uint64_t entity_a = 0; - uint64_t entity_b = 0; - JPH::ContactManifold manifold; - JPH::ContactSettings settings; - }; - -}; \ No newline at end of file diff --git a/atlas/drivers/jolt-cpp/jolt_contact_listener.hpp b/atlas/drivers/jolt-cpp/jolt_contact_listener.hpp deleted file mode 100644 index 2c9b5362..00000000 --- a/atlas/drivers/jolt-cpp/jolt_contact_listener.hpp +++ /dev/null @@ -1,84 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -namespace atlas::physics { - /** - * @brief implementation of Jolt's contact listener for collisions - * - * contact_listener gets set to Jolt's Physics System to allow for - * collisions to happen - */ - class contact_listener : public JPH::ContactListener { - public: - contact_listener(event::event_bus& p_bus); - - private: - /** - * @brief This allows us to make sure that the contacts are valid and - * not something that went wrong. It plays the role of both sanity check - * and saftey gaurd since if collisions were to be wrong you would not - * want that to crash the whole game. - * - * @param in_body1 is ID of the object1 who collided - * @param in_body2 is ID of object2 who object1 collided with - * @param in_base_offset for telling how far apart the center of the - * objects are - * @param in_collision_result The details about the collision - * @return JPH::ValidateResult - */ - JPH::ValidateResult OnContactValidate( - const JPH::Body& in_body1, - const JPH::Body& in_body2, - JPH::RVec3Arg in_base_offset, - const JPH::CollideShapeResult& in_collision_result) override; - - /** - * @brief This gets triggered each time a collision comes into contact. - * It is only called once and then removed. - * - * @param body1 Body that called - * @param body2 Target body - * @param manifold This class has a lot of stuff including heights, - * directions, offset etc... - * @param settings This has some of the body settings these objects - * have. - */ - void OnContactAdded(const JPH::Body& body1, - const JPH::Body& body2, - const JPH::ContactManifold& manifold, - JPH::ContactSettings& settings) override; - - /** - * @brief This gets called multiple times. It is not in use yet. - * - * @param in_body1 Body that called - * @param in_body2 Target body - * @param in_manifold This class has a lot of stuff including heights, - * directions, offset etc... - * @param io_settings This specifically descibes the settings that - * contacts should have. - * FIXME: Still needs to be implemented - */ - void OnContactPersisted(const JPH::Body& in_body1, - const JPH::Body& in_body2, - const JPH::ContactManifold& in_manifold, - JPH::ContactSettings& io_settings) override; - /** - * @brief This is used to clean to shapes and call exiting functions for - * contact. - * - * @param in_sub_shape_pair The pair of shapes that no longer touch. - * FIXME: Still needs to be implemented - */ - void OnContactRemoved( - const JPH::SubShapeIDPair& in_sub_shape_pair) override; - - private: - event::event_bus* m_bus; - }; -}; \ No newline at end of file diff --git a/atlas/drivers/jolt-cpp/jolt_context.hpp b/atlas/drivers/jolt-cpp/jolt_context.hpp deleted file mode 100644 index 02972348..00000000 --- a/atlas/drivers/jolt-cpp/jolt_context.hpp +++ /dev/null @@ -1,136 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include - -namespace atlas::physics { - /** - * @brief jolt_context is the backend implementation of physics context - * - * This is the jolt specific implementation of the physics context - * - * Represents the external abstraction that is defined through the the - * engine-specific parameters. - */ - class jolt_context : public physics_context { - public: - /** - * @brief construct a new physics context with jolt implemented as its - * backend - * - * @param p_settings are the configuration parameters for initiating - * JoltPhysics - * @param p_bus is the event::bus that allows for publishing physics - * events to the subscribers of those said events - */ - jolt_context(const jolt_settings& p_settings, event::event_bus& p_bus); - ~jolt_context() override = default; - - private: - /** - * @brief Performs any specific cleanup needed by Jolt - */ - void destroy_bodies() override; - - void prepare_and_finalize() override; - - void update_simulation(float p_delta_time) override; - - protected: - void emplace_box_collider(uint32_t p_entity_id, - const transform* p_transform, - const physics_body* p_body, - const box_collider* p_collider) override; - - void emplace_sphere_collider( - uint32_t p_entity_id, - const transform* p_transform, - const physics_body* p_body, - const sphere_collider* p_collider) override; - - void emplace_capsule_collider( - uint32_t p_entity_id, - const transform* p_transform, - const physics_body* p_body, - const capsule_collider* p_collider) override; - - // void set_position_rotation(flecs::entity p_entity, const - // physics_body* p_body, const box_collider* p_collider, const - // transform* p_transform) override; - transform context_read_transform(uint32_t p_id) override; - - physics_body context_read_physics_body(uint32_t p_id) override; - - void linear_velocity(uint64_t p_id, - const glm::vec3& p_linear_velocity) override; - - void angular_velocity(uint64_t p_id, - const glm::vec3& p_angular_velocity) override; - - void force(uint64_t p_id, const glm::vec3& p_cumulative_force) override; - - void add_force_and_torque(uint64_t p_id, - const glm::vec3& p_force, - const glm::vec3& p_torque) override; - - void add_impulse(uint64_t p_id, const glm::vec3& p_impulse) override; - - private: - //! @note Must be defined before physics can be initialized otherwise - //! jolt cannot be created properly. - jolt_settings m_settings; - - /** - * @brief Creates a static allocation of all data - * - */ - ref m_temp_allocator; - - /** - * @brief Sets up a thread system, either jolts or custom based on - * thread settings in m_settings - * - */ - scope m_thread_system; - - /** - * @brief Creates filtering for the quad tree in terms of movement - * - */ - ref m_broad_phase_layer_interface; - - /** - * @brief Creates a filter for the quad tree in terms of objects types - * - */ - ref m_object_vs_broadphase_filter; - - /** - * @brief Creates a filter for pairs of collisions - * - */ - ref m_object_layer_pair_filter; - - /** - * @brief Creates a way to recognize collisions - * - */ - // ref m_contact_listener; - contact_listener m_contact_listener; - - /** - * @brief Gives access to the physics system. Is given to jolt_api as - * well. This is to allow us to give seperation of concerns. However, - * may change now do to Jolt conflict and unhandled dangling pointers - * within jolt_api. - * - */ - ref m_physics_system; - - std::map m_cached_body_ids; - }; -}; \ No newline at end of file diff --git a/atlas/drivers/jolt-cpp/types.hpp b/atlas/drivers/jolt-cpp/types.hpp deleted file mode 100644 index f1dabc7b..00000000 --- a/atlas/drivers/jolt-cpp/types.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include - -namespace atlas { - template<> - struct vector3 { - vector3() = default; - - vector3(const JPH::Vec3& p_other) { - m_value = { p_other.GetX(), p_other.GetY(), p_other.GetZ() }; - } - - operator glm::vec3() { return m_value; } - - glm::vec3 operator=(const JPH::Vec3& p_other) { - return { p_other.GetX(), p_other.GetY(), p_other.GetZ() }; - } - - bool operator==(const glm::vec3& p_other) { - return (m_value.x == p_other.x and m_value.y == p_other.y and - m_value.z == p_other.z); - } - - private: - glm::vec3 m_value; - }; - - namespace jolt { - JPH::RVec3 to_rvec3(const glm::vec3& p_value); - - JPH::Vec3 to_vec3(const glm::vec3& p_value); - - JPH::Quat to_quat(const glm::vec4& q); - - JPH::Quat to_quat(glm::quat& p_value); - }; - - glm::vec3 to_vec3(const JPH::Vec3& p_value); - - glm::quat to_quat(const JPH::Quat& p_value); - - glm::vec4 to_vec4(const JPH::Quat& p_value); -}; \ No newline at end of file diff --git a/atlas/drivers/jolt-cpp/jolt_broad_phase.hpp b/atlas/drivers/jolt_cpp/broad_phase.cppm similarity index 82% rename from atlas/drivers/jolt-cpp/jolt_broad_phase.hpp rename to atlas/drivers/jolt_cpp/broad_phase.cppm index 4d1e245e..3d126942 100644 --- a/atlas/drivers/jolt-cpp/jolt_broad_phase.hpp +++ b/atlas/drivers/jolt_cpp/broad_phase.cppm @@ -1,8 +1,42 @@ -#pragma once +module; -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include -namespace atlas::physics { +#include + +#include +#include +#include +#include +#include +#include + +#include + +// jolt's math includes +#include +#include +#include + +export module atlas.drivers.jolt_cpp.broad_phase; + +import atlas.common; + +export namespace atlas::physics { /** * @brief This contains a few important comparisons having to do with @@ -158,4 +192,4 @@ namespace atlas::physics { } }; -} +} \ No newline at end of file diff --git a/atlas/drivers/jolt_cpp/contact_listener.cppm b/atlas/drivers/jolt_cpp/contact_listener.cppm new file mode 100644 index 00000000..9acaa324 --- /dev/null +++ b/atlas/drivers/jolt_cpp/contact_listener.cppm @@ -0,0 +1,136 @@ +module; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include + +// jolt's math includes +#include +#include +#include + +export module atlas.drivers.jolt_cpp.contact_listener; +import atlas.logger; +// import atlas.core.event.types; +import atlas.core.event; + +export namespace atlas::physics { + /** + * @brief implementation of Jolt's contact listener for collisions + * + * contact_listener gets set to Jolt's Physics System to allow for + * collisions to happen + */ + class contact_listener : public JPH::ContactListener { + public: + contact_listener(event::bus& p_bus) : m_bus(&p_bus) {} + + private: + /** + * @brief This allows us to make sure that the contacts are valid and + * not something that went wrong. It plays the role of both sanity check + * and saftey gaurd since if collisions were to be wrong you would not + * want that to crash the whole game. + * + * @param in_body1 is ID of the object1 who collided + * @param in_body2 is ID of object2 who object1 collided with + * @param in_base_offset for telling how far apart the center of the + * objects are + * @param in_collision_result The details about the collision + * @return JPH::ValidateResult + */ + JPH::ValidateResult OnContactValidate( + const JPH::Body&, + const JPH::Body&, + JPH::RVec3Arg, + const JPH::CollideShapeResult&) override { + return JPH::ValidateResult::AcceptAllContactsForThisBodyPair; + } + + /** + * @brief This gets triggered each time a collision comes into contact. + * It is only called once and then removed. + * + * @param body1 Body that called + * @param body2 Target body + * @param manifold This class has a lot of stuff including heights, + * directions, offset etc... + * @param settings This has some of the body settings these objects + * have. + */ + void OnContactAdded(const JPH::Body& p_body1, + const JPH::Body& p_body2, + const JPH::ContactManifold&, + JPH::ContactSettings&) override { + event::collision_enter begin_event = { + .entity1 = static_cast(p_body1.GetUserData()), + .entity2 = static_cast(p_body2.GetUserData()) + }; + + // Publishes to all subscribers that this collision_enter event has + // occurred + m_bus->publish(begin_event); + } + + /** + * @brief This gets called multiple times. It is not in use yet. + * + * @param in_body1 Body that called + * @param in_body2 Target body + * @param in_manifold This class has a lot of stuff including heights, + * directions, offset etc... + * @param io_settings This specifically descibes the settings that + * contacts should have. + * FIXME: Still needs to be implemented + */ + void OnContactPersisted(const JPH::Body& p_body1, + const JPH::Body& p_body2, + const JPH::ContactManifold&, + JPH::ContactSettings&) override { + event::collision_persisted persisted_event = { + .entity1 = static_cast(p_body1.GetUserData()), + .entity2 = static_cast(p_body2.GetUserData()) + }; + + m_bus->publish(persisted_event); + } + + /** + * @brief This is used to clean to shapes and call exiting functions for + * contact. + * + * @param in_sub_shape_pair The pair of shapes that no longer touch. + * FIXME: Still needs to be implemented + */ + void OnContactRemoved(const JPH::SubShapeIDPair&) override { + console_log_info("Collisions Removed!"); + // For Event system to handle when collision ends + } + + private: + event::bus* m_bus; + }; +}; \ No newline at end of file diff --git a/atlas/drivers/jolt_cpp/context.cppm b/atlas/drivers/jolt_cpp/context.cppm new file mode 100644 index 00000000..d96b8d54 --- /dev/null +++ b/atlas/drivers/jolt_cpp/context.cppm @@ -0,0 +1,553 @@ +module; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include + +// jolt's math includes +#include +#include +#include +#include +#include +#include + +export module atlas.drivers.jolt_cpp.context; + +// import atlas.logger; +// import atlas.common; +import atlas.core.utilities; +import atlas.core.event; +import atlas.core.scene.components; + +import atlas.drivers.jolt_cpp.broad_phase; +import atlas.drivers.jolt_cpp.contact_listener; + +import atlas.drivers.jolt_cpp.types; +import atlas.drivers.physics_context; +import atlas.drivers.jolt_cpp.types; + +namespace atlas::physics { + + enum thread_type : uint8_t { + default_system = 0, + job_system = 1, + }; + + // This might be able to be generalized eventually but we will have to + // create our own manifold before that happens. + struct contact_event { + uint64_t entity_a = 0; + uint64_t entity_b = 0; + JPH::ContactManifold manifold; + JPH::ContactSettings settings; + }; + + /** + * @brief Jolt-specific context configurations + * These are going to be internally integrated to jolt_context + * + * As these parameters currently are going to be specific to Jolt. + * + * These parameters are also only specific to the construction-level, not + * initiation level of the API's + * + * @remark Min and max world bounds are values that are artbitrary (in other + * words limit the simulation space) as JoltPhysics has a limit on distance + * for its limitation in simulation space + */ + struct jolt_settings { + + uint32_t allocation_amount = 10 * 1024 * 1024; + + //! @brief Specifying which threading system to use for Jolt. + thread_type thread_type = thread_type::default_system; + + uint32_t physics_threads = + std::max(1u, std::thread::hardware_concurrency() - 2); + + uint32_t max_jobs_power = 10; + uint32_t max_barriers = physics_threads * 16; + bool enable_multithread = true; + + // Max memory size per scene + uint32_t max_bodies = 16384; + uint32_t max_body_pairs = 32768; + uint32_t max_contact_constraints = 8192; + }; + + static void trace_impl(const char* p_in_fmt, ...) { + va_list list; + va_start(list, p_in_fmt); + char buffer[1024]; + vsnprintf(buffer, sizeof(buffer), p_in_fmt, list); + va_end(list); + console_log_error("{}", buffer); + } + + [[maybe_unused]] static bool assert_failed_impl(const char* p_in_expression, + const char* p_in_message, + const char* p_in_file, + unsigned int p_in_line) { + + console_log_error("{}:{}: ({}) {}", + p_in_file, + p_in_line, + p_in_expression, + (p_in_message != nullptr ? p_in_message : "")); + + return true; + }; + + /** + * @brief jolt_context is the backend implementation of physics context + * + * This is the jolt specific implementation of the physics context + * + * Represents the external abstraction that is defined through the the + * engine-specific parameters. + */ + export class jolt_context : public physics_context { + public: + /** + * @brief construct a new physics context with jolt implemented as its + * backend + * + * @param p_settings are the configuration parameters for initiating + * JoltPhysics + * @param p_bus is the event::bus that allows for publishing physics + * events to the subscribers of those said events + */ + jolt_context(event::bus& p_bus) : m_contact_listener(p_bus) { + jolt_settings settings = {}; + JPH::RegisterDefaultAllocator(); + + JPH::Trace = trace_impl; + JPH_IF_ENABLE_ASSERTS(JPH::AssertFailed = assert_failed_impl;) + + JPH::Factory::sInstance = new JPH::Factory(); + JPH::RegisterTypes(); + + m_temp_allocator = + create_ref(settings.allocation_amount); + + // This just sets up the JoltPhysics system and any listeners + m_physics_system = create_ref(); + m_broad_phase_layer_interface = + create_ref(); + m_object_vs_broadphase_filter = + create_ref(); + m_object_layer_pair_filter = create_ref(); + + if (settings.thread_type == thread_type::default_system) { + + m_thread_system = create_scope( + // Max jobs must be a power of 2, otherwise jph crashes. + // Bianary tree must be fully balanced + std::pow(2, settings.max_jobs_power), + settings.max_barriers, + settings.physics_threads); + } + else { + console_log_error("Unsupported custom job system"); + assert(false); + } + + m_physics_system->Init(settings.max_bodies, + 0, + settings.max_body_pairs, + settings.max_contact_constraints, + *m_broad_phase_layer_interface, + *m_object_vs_broadphase_filter, + *m_object_layer_pair_filter); + + // Default contact listener impl and can change during runtime + m_physics_system->SetContactListener(&m_contact_listener); + } + + ~jolt_context() override = default; + + private: + void prepare_and_finalize() override { + using namespace JPH; + + //! @brief We actually do not need to pass in the body ID's into + //! std::vector. Though we may need ways to store JPH::BodyID for + //! modifying specific bodies + JPH::BodyIDVector all_body_ids; + m_physics_system->GetBodies(all_body_ids); + + auto& body_interface = m_physics_system->GetBodyInterface(); + auto state = body_interface.AddBodiesPrepare( + all_body_ids.data(), static_cast(all_body_ids.size())); + body_interface.AddBodiesFinalize(all_body_ids.data(), + static_cast(all_body_ids.size()), + state, + JPH::EActivation::Activate); + } + + void update_simulation(float p_delta_time) override { + float fixed_time_step = 1.0f / 60.0f; + int time_step = 1 + (int)(60 * fixed_time_step); + m_physics_system->Update(p_delta_time, + time_step, + m_temp_allocator.get(), + m_thread_system.get()); + } + + /** + * @brief Performs any specific cleanup needed by Jolt + */ + void destroy_bodies() override { + auto& body_interface = m_physics_system->GetBodyInterface(); + + // Retrieve all body ID's to ensure that we do proper deactivation and + // post cleanup for the physics simulation + JPH::BodyIDVector all_body_ids; + m_physics_system->GetBodies(all_body_ids); + + if (!all_body_ids.empty()) { + + body_interface.DeactivateBodies( + all_body_ids.data(), static_cast(all_body_ids.size())); + + body_interface.RemoveBodies(all_body_ids.data(), + static_cast(all_body_ids.size())); + + body_interface.DestroyBodies(all_body_ids.data(), + static_cast(all_body_ids.size())); + + m_cached_body_ids.clear(); + } + } + + protected: + void emplace_box_collider(uint32_t p_entity_id, + const transform* p_transform, + const physics_body* p_body, + const box_collider* p_collider) override { + using namespace JPH; + auto& body_interface = m_physics_system->GetBodyInterface(); + + // Creating our box shape and specifying half_extent that is a glm::vec3 + // conversion to JPH::Vec3 half_extents must be 0.5f or else it can get + // an invalid convex radius + BoxShapeSettings shape_settings(jolt::to_vec3(p_collider->half_extent)); + auto result = shape_settings.Create(); + + if (result.HasError()) { + console_log_error("Box shape creation error: {}", + result.GetError()); + return; + } + EMotionType motion_type = EMotionType::Static; + switch (p_body->body_movement_type) { + case body_type::fixed: + motion_type = EMotionType::Static; + break; + case body_type::dynamic: + motion_type = EMotionType::Dynamic; + break; + case body_type::kinematic: + motion_type = EMotionType::Kinematic; + break; + } + + auto& box = result.Get(); + BodyCreationSettings body_settings( + box, + jolt::to_vec3(p_transform->position), + jolt::to_quat(p_transform->quaternion), + motion_type, + p_body->body_layer_type); + + // NOTE TO SELF ------ This is setting some pointer to the entity ID + // WE CAN USE THIS TO TELL THE EVENT SYSTEM WHICH FLECS ENTITY COLLIDED + // WITH EACH OTHER!!!!!!!! Because each contact listener allows you to + // take a pointer from the physics bodies that are just blocks of + // data!!! + body_settings.mUserData = static_cast(p_entity_id); + body_settings.mFriction = p_body->friction; + body_settings.mRestitution = p_body->restitution; + body_settings.mLinearVelocity = jolt::to_vec3(p_body->linear_velocity); + body_settings.mAngularVelocity = + jolt::to_vec3(p_body->angular_velocity); + + Body* body = body_interface.CreateBody(body_settings); + m_cached_body_ids.emplace(p_entity_id, body->GetID()); + } + + void emplace_sphere_collider( + uint32_t p_entity_id, + const transform* p_transform, + const physics_body* p_body, + const sphere_collider* p_collider) override { + using namespace JPH; + auto& body_interface = m_physics_system->GetBodyInterface(); + // ensure that the half_extent of the box shape always matches the + // object and reference that information through the transform + SphereShapeSettings shape_settings(p_collider->radius); + auto result = shape_settings.Create(); + + if (result.HasError()) { + console_log_error("Sphere shape creation error: {}", + result.GetError()); + return; + } + EMotionType motion_type = EMotionType::Static; + switch (p_body->body_movement_type) { + case body_type::fixed: { + motion_type = EMotionType::Static; + } break; + case body_type::dynamic: { + motion_type = EMotionType::Dynamic; + } break; + case body_type::kinematic: { + motion_type = EMotionType::Kinematic; + } break; + } + + auto& box = result.Get(); + BodyCreationSettings body_settings( + box, + jolt::to_vec3(p_transform->position), + jolt::to_quat(p_transform->quaternion), + motion_type, + p_body->body_layer_type); + + // Assigning the entity ID as the user data + // Fetched when collision happens + body_settings.mUserData = static_cast(p_entity_id); + body_settings.mFriction = p_body->friction; + body_settings.mRestitution = p_body->restitution; + body_settings.mLinearVelocity = jolt::to_vec3(p_body->linear_velocity); + body_settings.mAngularVelocity = + jolt::to_vec3(p_body->angular_velocity); + Body* body = body_interface.CreateBody(body_settings); + + // body_interface.AddForce(body->GetID(), + // jolt::to_vec3(p_body->cumulative_force)); + m_cached_body_ids.emplace(p_entity_id, body->GetID()); + } + + void emplace_capsule_collider( + uint32_t p_entity_id, + const transform* p_transform, + const physics_body* p_body, + const capsule_collider* p_collider) override { + using namespace JPH; + auto& body_interface = m_physics_system->GetBodyInterface(); + // ensure that the half_extent of the box shape always matches the + // object and reference that information through the transform + CapsuleShapeSettings shape_settings(p_collider->half_height, + p_collider->radius); + auto result = shape_settings.Create(); + + if (result.HasError()) { + console_log_error("Capsule shape creation error: {}", + result.GetError()); + return; + } + EMotionType motion_type = EMotionType::Static; + switch (p_body->body_movement_type) { + case body_type::fixed: { + motion_type = EMotionType::Static; + } break; + case body_type::dynamic: { + motion_type = EMotionType::Dynamic; + } break; + case body_type::kinematic: { + motion_type = EMotionType::Kinematic; + } break; + } + + auto& box = result.Get(); + BodyCreationSettings body_settings( + box, + jolt::to_vec3(p_transform->position), + jolt::to_quat(p_transform->quaternion), + motion_type, + p_body->body_layer_type); + body_settings.mFriction = p_body->friction; + body_settings.mRestitution = p_body->restitution; + body_settings.mLinearVelocity = jolt::to_vec3(p_body->linear_velocity); + body_settings.mAngularVelocity = + jolt::to_vec3(p_body->angular_velocity); + body_settings.mUserData = static_cast(p_entity_id); + + Body* body = body_interface.CreateBody(body_settings); + m_cached_body_ids.emplace(p_entity_id, body->GetID()); + } + + // void set_position_rotation(flecs::entity p_entity, const + // physics_body* p_body, const box_collider* p_collider, const + // transform* p_transform) override; + transform context_read_transform(uint32_t p_id) override { + using namespace JPH; + transform new_transform{}; + auto& body_interface = m_physics_system->GetBodyInterface(); + + BodyID body_id = m_cached_body_ids[p_id]; + JPH::Vec3 pos = body_interface.GetPosition(body_id); + JPH::Quat rot = body_interface.GetRotation(body_id); + JPH::Vec3 rot_euler = rot.GetEulerAngles(); + + new_transform.position = to_vec3(pos); + new_transform.quaternion = to_vec4(rot); + new_transform.rotation = to_vec3(rot_euler); + + return new_transform; + } + + physics_body context_read_physics_body(uint32_t p_id) override { + using namespace JPH; + auto& body_interface = m_physics_system->GetBodyInterface(); + + // TODO: Will need to change this because if this entity doesn't exist + // then it will be set to zeroes, gotta be careful about this + if (!m_cached_body_ids.contains(p_id)) { + return {}; + } + + auto body_id = m_cached_body_ids.at(p_id); + + physics_body body = { + .linear_velocity = + to_vec3(body_interface.GetLinearVelocity(body_id)), + .angular_velocity = + to_vec3(body_interface.GetAngularVelocity(body_id)), + .center_mass_position = + to_vec3(body_interface.GetCenterOfMassPosition(body_id)), + .gravity_factor = body_interface.GetGravityFactor(body_id), + .friction = body_interface.GetFriction(body_id), + .restitution = body_interface.GetRestitution(body_id), + }; + + return body; + } + + void linear_velocity(uint64_t p_id, + const glm::vec3& p_linear_velocity) override { + using namespace JPH; + auto& body_interface = m_physics_system->GetBodyInterface(); + + body_interface.SetLinearVelocity(m_cached_body_ids.at(p_id), + jolt::to_vec3(p_linear_velocity)); + } + + void angular_velocity(uint64_t p_id, + const glm::vec3& p_angular_velocity) override { + using namespace JPH; + auto& body_interface = m_physics_system->GetBodyInterface(); + BodyID body_id(p_id); + + body_interface.SetAngularVelocity(m_cached_body_ids.at(p_id), + jolt::to_vec3(p_angular_velocity)); + } + + void force(uint64_t p_id, const glm::vec3& p_force) override { + using namespace JPH; + auto& body_interface = m_physics_system->GetBodyInterface(); + BodyID body_id(p_id); + + body_interface.AddForce(m_cached_body_ids.at(p_id), + jolt::to_vec3(p_force)); + } + + void add_force_and_torque(uint64_t p_id, + const glm::vec3& p_force, + const glm::vec3& p_torque) override { + using namespace JPH; + auto& body_interface = m_physics_system->GetBodyInterface(); + + body_interface.AddForceAndTorque(m_cached_body_ids.at(p_id), + jolt::to_vec3(p_force), + jolt::to_vec3(p_torque)); + } + + void add_impulse(uint64_t p_id, const glm::vec3& p_impulse) override { + using namespace JPH; + auto& body_interface = m_physics_system->GetBodyInterface(); + + body_interface.AddImpulse(m_cached_body_ids.at(p_id), + jolt::to_vec3(p_impulse)); + } + + private: + //! @note Must be defined before physics can be initialized otherwise + //! jolt cannot be created properly. + jolt_settings m_settings; + + /** + * @brief Creates a static allocation of all data + * + */ + ref m_temp_allocator; + + /** + * @brief Sets up a thread system, either jolts or custom based on + * thread settings in m_settings + * + */ + scope m_thread_system; + + /** + * @brief Creates filtering for the quad tree in terms of movement + * + */ + ref m_broad_phase_layer_interface; + + /** + * @brief Creates a filter for the quad tree in terms of objects types + * + */ + ref m_object_vs_broadphase_filter; + + /** + * @brief Creates a filter for pairs of collisions + * + */ + ref m_object_layer_pair_filter; + + /** + * @brief Creates a way to recognize collisions + * + */ + // ref m_contact_listener; + contact_listener m_contact_listener; + + /** + * @brief Gives access to the physics system. Is given to jolt_api as + * well. This is to allow us to give seperation of concerns. However, + * may change now do to Jolt conflict and unhandled dangling pointers + * within jolt_api. + * + */ + ref m_physics_system; + + std::map m_cached_body_ids; + }; +}; \ No newline at end of file diff --git a/atlas/drivers/jolt_cpp/math.cppm b/atlas/drivers/jolt_cpp/math.cppm new file mode 100644 index 00000000..828b3a56 --- /dev/null +++ b/atlas/drivers/jolt_cpp/math.cppm @@ -0,0 +1,70 @@ +module; + +#include +// jolt's math includes +#include +#include +#include + +#include +#define GLM_ENABLE_EXPERIMENTAL +#include +export module atlas.drivers.jolt_cpp.types; +import atlas.core.math.types; + +export namespace atlas { + template<> + struct vector3 { + vector3() = default; + + vector3(const JPH::Vec3& p_other) { + m_value = { p_other.GetX(), p_other.GetY(), p_other.GetZ() }; + } + + operator glm::vec3() { return m_value; } + + glm::vec3 operator=(const JPH::Vec3& p_other) { + return { p_other.GetX(), p_other.GetY(), p_other.GetZ() }; + } + + bool operator==(const glm::vec3& p_other) { + return (m_value.x == p_other.x and m_value.y == p_other.y and + m_value.z == p_other.z); + } + + private: + glm::vec3 m_value; + }; + + namespace jolt { + JPH::RVec3 to_rvec3(const glm::vec3& p_values) { + return { p_values.x, p_values.y, p_values.z }; + } + + JPH::Vec3 to_vec3(const glm::vec3& p_values) { + return { p_values.x, p_values.y, p_values.z }; + } + + JPH::Quat to_quat(const glm::vec4& q) { + return { q.x, q.y, q.z, q.w }; + } + + JPH::Quat to_quat(glm::quat& p_values) { + return JPH::Quat(p_values.w, p_values.x, p_values.y, p_values.z); + } + }; + + glm::quat to_quat(const JPH::Quat& p_values) { + return glm::quat( + p_values.GetW(), p_values.GetX(), p_values.GetY(), p_values.GetZ()); + } + + glm::vec3 to_vec3(const JPH::Vec3& p_values) { + return vector3(p_values); + } + + glm::vec4 to_vec4(const JPH::Quat& p_values) { + return glm::vec4( + p_values.GetX(), p_values.GetY(), p_values.GetZ(), p_values.GetW()); + } +}; \ No newline at end of file diff --git a/atlas/physics/physics_context.hpp b/atlas/drivers/physics_context.cppm similarity index 91% rename from atlas/physics/physics_context.hpp rename to atlas/drivers/physics_context.cppm index a1ffb6f6..1bbcb0c3 100644 --- a/atlas/physics/physics_context.hpp +++ b/atlas/drivers/physics_context.cppm @@ -1,14 +1,18 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include +module; + +#include +#include + +export module atlas.drivers.physics_context; + +import atlas.common; +import atlas.core.scene.components; +import atlas.core.event; + + namespace atlas::physics { + /** * @brief The context is the way to interact with specific backend context * implementation such as JoltPhysics as our specific implementation-backend @@ -17,7 +21,7 @@ namespace atlas::physics { * its simulation * */ - class physics_context { + export class physics_context { public: virtual ~physics_context() = default; @@ -178,11 +182,4 @@ namespace atlas::physics { virtual void add_impulse(uint64_t p_id, const glm::vec3& p_impulse) = 0; }; - - //! @brief initializes the physics backend. SHOULD have an API associated - //! with but for now, we assume we only have JoltPhysics as our only physics - //! backend - ref initialize_physics_context( - const jolt_settings& p_settings, - event::event_bus& p_bus); }; \ No newline at end of file diff --git a/atlas/drivers/renderer_context.hpp b/atlas/drivers/renderer_system.cppm similarity index 69% rename from atlas/drivers/renderer_context.hpp rename to atlas/drivers/renderer_system.cppm index 56e75374..852c2831 100644 --- a/atlas/drivers/renderer_context.hpp +++ b/atlas/drivers/renderer_system.cppm @@ -1,10 +1,18 @@ -#pragma once -#include -#include -#include -#include +module; -namespace atlas { +#include +#include +#include + +export module atlas.drivers.renderer_system; + +// import atlas.core.utilities.types; +// import atlas.common; +import atlas.core.utilities; +import atlas.core.scene; +import vk; + +export namespace atlas { /** * @brief is an interface that defines a graphics APi-agnostic renderer * @@ -15,9 +23,9 @@ namespace atlas { * atlas::graphics_context for setting up the agnostic-graphics API's * directly */ - class render_context { + class renderer_system { public: - virtual ~render_context() = default; + virtual ~renderer_system() = default; /** * @brief responsibility is to preload any data that is necessary to be @@ -43,13 +51,14 @@ namespace atlas { * @param p_proj_view is the (proj * view) camera matrices that is used * by the game objects being rendered and passed as a shader uniform */ - void begin_frame(const ::vk::command_buffer& p_current, - const window_settings& p_settings, + void begin_frame(const vk::command_buffer& p_current, + const window_params& p_params, const VkRenderPass& p_renderpass, const VkFramebuffer& p_framebuffer, - const glm::mat4& p_proj_view) { + const glm::mat4& p_proj_view, + uint32_t p_current_frame) { return start_frame( - p_current, p_settings, p_renderpass, p_framebuffer, p_proj_view); + p_current, p_params, p_renderpass, p_framebuffer, p_proj_view, p_current_frame); } /** @@ -62,7 +71,7 @@ namespace atlas { * @brief sets the background color and request that change to the * graphics API */ - void set_background_color(const std::array& p_color) { + void set_background_color(const glm::vec4& p_color) { return background_color(p_color); } @@ -73,20 +82,16 @@ namespace atlas { private: virtual void preload_assets(const VkRenderPass& p_renderpass) = 0; - virtual void start_frame(const ::vk::command_buffer& p_current, - const window_settings& p_settings, + virtual void start_frame(const vk::command_buffer& p_current, + const window_params& p_params, const VkRenderPass& p_renderpass, const VkFramebuffer& p_framebuffer, - const glm::mat4& p_proj_view) = 0; + const glm::mat4& p_proj_view, + uint32_t p_current_frame) = 0; virtual void post_frame() = 0; - virtual void background_color(const std::array& p_color) = 0; + virtual void background_color(const glm::vec4& p_color) = 0; virtual void current_scene(ref) = 0; }; - - ref initialize_renderer( - const window_settings& p_window_extent, - uint32_t p_image_size, - const std::string& p_tag); -}; +}; \ No newline at end of file diff --git a/atlas/drivers/vulkan-cpp/mesh.hpp b/atlas/drivers/vulkan-cpp/mesh.hpp deleted file mode 100644 index b67b3ca9..00000000 --- a/atlas/drivers/vulkan-cpp/mesh.hpp +++ /dev/null @@ -1,79 +0,0 @@ -#pragma once -#define GLM_ENABLE_EXPERIMENTAL -#include -#include -#include -#include -#include -#include -#include -#include - -namespace atlas::vk { - - /** - * @brief mesh class specifically defined with vulkan implementations for - * specific primitives - * TODO: Whenever we load in a texture that will be laucnhed asyncronously - * - * @brief mesh class will contain metadata needed by vulkan specifications - * Ways to communicate through vulkan by only supplying information needed - * to update this mesh - * - * @brief Represents a renderable object -- supporting various material - * types, etc - * TODO - For now we have a map, this should - * be expanded to a proper material system for blending various materials - */ - class mesh { - public: - mesh() = default; - mesh(std::span<::vk::vertex_input> p_vertices, - std::span p_indices); - mesh(const std::filesystem::path& p_filename, bool p_flip = false); - - //! @brief Reload mesh vertices and indices when requested - void reload_mesh(const std::filesystem::path& p_path); - - void draw(const VkCommandBuffer& p_command_buffer); - - void destroy(); - - //! @brief Loading single texture with specified std::filesystem::path - void add_diffuse(const std::filesystem::path& p_path); - - void add_specular(const std::filesystem::path& p_path); - - [[nodiscard]] ::vk::sample_image diffuse() const { - return m_diffuse.image(); - } - [[nodiscard]] ::vk::sample_image specular() const { - return m_specular.image(); - } - - //! @return true if mesh geometry model loaded succesfully - [[nodiscard]] bool loaded() const { return m_model_loaded; } - - [[nodiscard]] bool diffuse_loaded() const { return m_diffuse.loaded(); } - [[nodiscard]] bool specular_loaded() const { - return m_specular.loaded(); - } - - void set_flip(bool p_flip) { m_flip = p_flip; } - - private: - void load_obj(const std::filesystem::path& p_filename); - - private: - vk_physical_driver m_physical; - VkDevice m_device = nullptr; - ::vk::texture m_diffuse; - ::vk::texture m_specular; - ::vk::vertex_buffer m_vbo{}; - ::vk::index_buffer m_ibo{}; - ::vk::uniform_buffer m_geoemtry_ubo; - ::vk::uniform_buffer m_material_ubo; - bool m_model_loaded = false; - bool m_flip = false; - }; -}; diff --git a/atlas/drivers/vulkan-cpp/shader_resource_group.hpp b/atlas/drivers/vulkan-cpp/shader_resource_group.hpp deleted file mode 100644 index 23cd101a..00000000 --- a/atlas/drivers/vulkan-cpp/shader_resource_group.hpp +++ /dev/null @@ -1,128 +0,0 @@ -#pragma once -#include -#include -#include -// #include - -namespace atlas::vk { - /** - * @brief resource group for loading shader sources that give you back - * VkShaderModule handles - * - * Responsibility is to load stages of shader sources whether that be - * through precompiler .spv files or through shaderc runtime shader - * compilation - * - * Responsibility is loading and streaming the amount of bytes from the - * compiled shader sources into the vulkan shader module handles - * - * resource groups up the creation and management of vulkan shader modules. - */ - class shader_resource_group { - public: - shader_resource_group() = default; - /** - * @brief constructs a new shader_resource_group - * - * @param p_device is the logical device required to creating the vulkan - * shader module - * @param p_info has the properties such as specified shader sources to - * load/compile - */ - shader_resource_group(const VkDevice& p_device, - const ::vk::shader_resource_info& p_info); - ~shader_resource_group() = default; - - /** - * @return true if resources are valid, otherwise return false - */ - [[nodiscard]] bool valid() const { return m_resource_valid; } - - /** - * @brief sets the vertex attributes with the shader modules that gets - * used by ::vk::pipeline (graphics pipeline) - * - * @param p_attributes is the high-level specifications for setting up - * vertex attributes that correspond with these shaders - */ - void vertex_attributes( - std::span p_attributes); - - /** - * @brief this gives you back the shader module handles along with each - * of their stages they have been compiled with - * - * Returns the vector to retain the shader modules that are needed by - * the graphics pipeline. - * - * It is required by vulkan specs the graphics pipeline to contain valid - * shader modules of the compiled shaders - * - * @return vector<::vk::shader_handle> - */ - [[nodiscard]] std::vector<::vk::shader_handle> handles() const { - return map_to_vector(); - } - - /** - * @return span - */ - [[nodiscard]] std::span - vertex_attributes() const { - return m_vertex_attributes; - } - - /** - * @return span - */ - [[nodiscard]] std::span - vertex_bind_attributes() const { - return m_vertex_binding_attributes; - } - - /** - * @brief explicit cleanup to the VkShaderModule handles created with - * this particular resource group - */ - void destroy(); - - /** - * @brief ideally used for requesting for reload - * - * Planning to use this for invalidation when for runtime shader - * hot-reloading - * - * @note this is not used at the moment as shader runtime hot reloading - * is currently not supported. - */ - [[nodiscard]] bool reload_requested() const { - return m_reload_requested; - } - - private: - /** - * converts unordered_map to - * vector - */ - [[nodiscard]] std::vector<::vk::shader_handle> map_to_vector() const; - - void create_module(std::span p_blob, - const ::vk::shader_source& p_source); - - void create_module(std::span p_blob, - const ::vk::shader_source& p_source); - - void reload_shader(const ::vk::shader_source& p_source); - - private: - VkDevice m_device = nullptr; - std::vector m_vertex_attributes; - std::vector - m_vertex_binding_attributes; - bool m_resource_valid = false; - // shader module handles - std::unordered_map m_modules; - // ref m_watcher; - bool m_reload_requested = false; - }; -}; \ No newline at end of file diff --git a/atlas/drivers/vulkan-cpp/shader_source_reader.hpp b/atlas/drivers/vulkan-cpp/shader_source_reader.hpp deleted file mode 100644 index cfc48efd..00000000 --- a/atlas/drivers/vulkan-cpp/shader_source_reader.hpp +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -namespace atlas::vk {}; \ No newline at end of file diff --git a/atlas/drivers/vulkan-cpp/utilities.hpp b/atlas/drivers/vulkan-cpp/utilities.hpp deleted file mode 100644 index 2f537b39..00000000 --- a/atlas/drivers/vulkan-cpp/utilities.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once -#include -#include -#include - -namespace atlas::vk { - - /** - * @param p_result checks if the result of a vulkan handler was created - * correctly - * @param p_name used for debugging of which handler failed - * @param p_source is the location of the call-site that invoked vk_check - */ - void vk_check( - const VkResult& p_result, - const std::string& p_name, - const std::source_location& p_source = std::source_location::current()); - -}; \ No newline at end of file diff --git a/atlas/drivers/vulkan-cpp/vk_context.hpp b/atlas/drivers/vulkan-cpp/vk_context.hpp deleted file mode 100644 index e93d7d0c..00000000 --- a/atlas/drivers/vulkan-cpp/vk_context.hpp +++ /dev/null @@ -1,77 +0,0 @@ -#pragma once -#include -#include -#include -#include - -namespace atlas::vk { - /** - * @brief vulkan-specific implementation that is the backend of the graphics - * contextd - */ - class vk_context : public graphics_context { - public: - vk_context(const std::string& p_tag); - - /** - * @brief used for providing a way to submit all vulkan metaobjects - * before the destruction of the vulkan logical device - * - * Per vulkan specification, it is required to have all object handles - * created with the logical device to be destroyed before the logical - * device itself gets destroyed during post cleanup - * - * This function was a means to ensure that the destruction of those - * vulkan child objects are handled in that order correctly - */ - static void submit_resource_free(std::function&& p_resource); - - /** - * @brief returns function pointer to allow for setting debug object - * name - * - * - * This allows for utilizing vkSetDebugUtilsObjectNameEXT during debug - * builds - * - * This allows for setting up object names that is useful to the - * programmer when a validation layer error message occurs unexpectedly - * - */ - static PFN_vkSetDebugUtilsObjectNameEXT get_debug_object_name() { - return s_instance->m_vk_set_debug_utils_object_name_ext; - } - - /** - * @brief Gives you direct access to the vulkan instance - */ - static VkInstance handler(); - - /** - * @brief direct access to the vulkan physical device - */ - static vk_physical_driver physical_driver() { - return s_instance->m_physical; - } - - /** - * @brief direct access to the vulkan logical device - */ - static vk_driver driver_context() { return s_instance->m_driver; } - - private: - void resource_free(std::function&& p_resource); - - private: - void destroy_context() override; - - private: - static vk_context* s_instance; - VkInstance m_instance_handler = nullptr; - vk_physical_driver m_physical{}; - vk_driver m_driver{}; - std::deque> m_resources_free{}; - - PFN_vkSetDebugUtilsObjectNameEXT m_vk_set_debug_utils_object_name_ext; - }; -}; \ No newline at end of file diff --git a/atlas/drivers/vulkan-cpp/vk_driver.hpp b/atlas/drivers/vulkan-cpp/vk_driver.hpp deleted file mode 100644 index 9ec69bb7..00000000 --- a/atlas/drivers/vulkan-cpp/vk_driver.hpp +++ /dev/null @@ -1,83 +0,0 @@ -#pragma once -#include -#include - -namespace atlas::vk { - - /** - * @brief logical device implementation wrapper around the VkDevice - * - * This class was a wrapper around VKDevice, that provided you with other - * API's to do with that particular logical device. - * - * Logical devices are representation of virtual software ways to interact - * with the GPU through Vulkan specifications - * - * TODO: Out-of-date. Using vulkan-cpp ::vk::device class to provide API's - * that allow you to do more queries on specific attributes the logical - * device gives you. - */ - class vk_driver { - struct device_queue_family { - VkQueue graphics_queue; - VkQueue transfer_queue; - VkQueue compute_queue; - }; - - public: - vk_driver() = default; - - /** - * @brief construct a new logical device - * @param p_physical is the physical device required for the creation of - * the logical device - */ - vk_driver(const vk_physical_driver& p_physical); - ~vk_driver() = default; - - /** - * @brief returns the specified graphics queue from this logical device - */ - [[nodiscard]] VkQueue graphics_queue() const { - return m_device_queues.graphics_queue; - } - - /** - * @return -1 if there are no flags available/compatible/valid - */ - uint32_t select_memory_type(uint32_t p_type_filter, - VkMemoryPropertyFlags p_property_flag); - - void destroy(); - - /** - * @brief gives you the depth format from the logical device - * - * @return VkFormat - */ - [[nodiscard]] VkFormat depth_format() const; - - /** - * @brief allows to treat vk_driver as a VkDevice handle - * - * For vulkan API's that accept only taking in VKDevice, this simplifies - * the need to not have a getter API - */ - operator VkDevice() const { return m_driver; } - - /** - * @brief allows to treat vk_driver as a VkDevice handle - * - * For vulkan API's that accept only taking in VKDevice, this simplifies - * the need to not have a getter API - */ - operator VkDevice() { return m_driver; } - - private: - vk_physical_driver m_physical{}; - VkDevice m_driver = nullptr; - device_queue_family m_device_queues{}; - VkFormat m_depth_format_selected; - }; - -}; \ No newline at end of file diff --git a/atlas/drivers/vulkan-cpp/vk_imgui.hpp b/atlas/drivers/vulkan-cpp/vk_imgui.hpp deleted file mode 100644 index d1baa163..00000000 --- a/atlas/drivers/vulkan-cpp/vk_imgui.hpp +++ /dev/null @@ -1,89 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace atlas::vk { - struct hud_data { - float playerHealth = 100.0f; - int playerScore = 0; - float fps = 0.0f; - std::string currentWeapon = "Assault Rifle"; - // Add more data as needed - }; - - class imgui_context { - public: - imgui_context() = default; - imgui_context(const ref& p_window_ctx); - - void create(GLFWwindow* p_window_handler, - const uint32_t& p_image_size, - const VkRenderPass& p_current_renderpass); - - void begin(const VkCommandBuffer& p_current, - const uint32_t& p_current_frame_idx); - - void end(); - - // HUD Example, using this to test if imgui initialization works - // note: experiemental, for testing imgui with new vulkan implementation - // Proper API I want to have this be in atlas::ui::hud namespace - // API Usage: ui::hud::health_bar(float) - // If users have custom components they want to register to the UI, then - // do: - /* - template - void custom_hud_component(UComponent* p_component,, UType p_type) { - // then prob do something lik this: use template to associate - struct component and the data, with type specified - imgui::progress_bar((p_component)->*p_data); - } - - User-side would be the following: - struct custom_component_heatlh { - uint32_t health_data = 10; - }; - some_entity->set({}); - const custom_component_health* player_hp = - some_entity.get(); - - // Through this specifications the UI will now apply this to the hud - // Questions that needs to be talked here are the following points: - 1.) How will users specify UI component uses? - * Position of HUD - * Handling alignment of the HUD - 2.) Ease of usability with minimal specifications on user-side for - getting HUD's to work 3.) Making a class be optional, meaning have - this be through imgui's API's as a function 4.) Asking if its needed - to be represented as a class atlas::ui::hud::health_bar(player_hp, - &custom_component_health::health_data) - */ - void draw_hud(const hud_data& p_test, - const window_settings& p_settings); - - [[nodiscard]] ::vk::command_buffer imgui_active_command() const { - return m_viewport_command_buffers[m_current_frame_index]; - } - void destroy(); - - private: - VkInstance m_instance = nullptr; - VkPhysicalDevice m_physical = nullptr; - vk_driver m_driver{}; - uint32_t m_current_frame_index = 0; - VkSwapchainKHR m_current_swapchain_handler = nullptr; - VkDescriptorPool m_desc_pool = nullptr; - VkCommandBuffer m_current = nullptr; - std::vector<::vk::command_buffer> m_viewport_command_buffers; - }; -}; \ No newline at end of file diff --git a/atlas/drivers/vulkan-cpp/vk_physical_driver.hpp b/atlas/drivers/vulkan-cpp/vk_physical_driver.hpp deleted file mode 100644 index 1548d99a..00000000 --- a/atlas/drivers/vulkan-cpp/vk_physical_driver.hpp +++ /dev/null @@ -1,97 +0,0 @@ -#pragma once -#include -#include - -namespace atlas::vk { - - struct surface_properties { - VkSurfaceCapabilitiesKHR surface_capabilities; - VkSurfaceFormatKHR surface_format; - }; - - /** - * @brief vulkan-specific implementation wrapper around VkPhysicalDevice - * - * Wrapper that constructs a single physical device that provides API's that - * can be used to query specific information for your specific physical - * device - * - * A physical device represents your current hardware GPU and allows for the - * ability to enumerate information to check for compatibility on the - * current GPU - */ - class vk_physical_driver { - struct queue_family_indices { - uint32_t graphics = -1; - uint32_t compute = -1; - uint32_t transfer = -1; - }; - - public: - vk_physical_driver() = default; - - /** - * @brief constructs a new vulkan physical device - * - * @param p_instance requires a VkInstance to create a VkPhysicalDevice - * handle - */ - vk_physical_driver(const VkInstance& p_instance); - - ~vk_physical_driver(); - - /** - * @brief gives you the queue families that are supported - * - * @return queue_family_indices are the indices of the specific queue's - * that are compatible on current hardware specifications - */ - [[nodiscard]] queue_family_indices read_queue_family_indices() const { - return m_queue_indices; - } - - [[nodiscard]] VkPhysicalDeviceMemoryProperties memory_properties() - const; - - /** - * @return uint32_t is the index to the presentation index of the - * specific presentation queue - */ - [[nodiscard]] uint32_t read_presentation_index( - const VkSurfaceKHR& p_surface); - - /** - * @brief querying surface properties based on the currently specified - * VkSurfaceKHR handle created - */ - [[nodiscard]] surface_properties get_surface_properties( - const VkSurfaceKHR& p_surface); - - /** - * @brief Allows for treating vk_physical_device as a VkPhysicalDevice - * handle - * - * Simplifies using this same class for creating other vulkan - * metaobjects - */ - operator VkPhysicalDevice() { return m_physical_driver; } - - /** - * @brief Allows for treating vk_physical_device as a VkPhysicalDevice - * handle - * - * Simplifies using this same class for creating other vulkan - * metaobjects - */ - operator VkPhysicalDevice() const { return m_physical_driver; } - - private: - queue_family_indices select_queue_family_indices(); - - private: - VkPhysicalDevice m_physical_driver = nullptr; - queue_family_indices m_queue_indices{}; - std::vector m_queue_family_properties{}; - surface_properties m_surface_properties{}; - }; -}; \ No newline at end of file diff --git a/atlas/drivers/vulkan-cpp/vk_renderer.hpp b/atlas/drivers/vulkan-cpp/vk_renderer.hpp deleted file mode 100644 index cb59f8a9..00000000 --- a/atlas/drivers/vulkan-cpp/vk_renderer.hpp +++ /dev/null @@ -1,96 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace atlas::vk { - /** - * @brief Something to consider for mesh loading. - * - * How will meshes get loaded? - * - * There are a few ways for considering for the base approach, which are: - * TODO: These are things to think about how this may happen because I want - * to make this lightweight in the sense the data isnt continuously being - * modified. Taking a looking at minimizing the loading state of vulkan - * implementation-specific meshes - * * While also making the way how mesh components are being added the - * same as before - * - Something to avoid is the entities containing the geometry data itself - * but being able to reference to their respective geometry data that are - * getting submitted to the GPU - * - * * Batching ID's into hash table that contains the actual geometry data - * * Using ID to search up the mesh loaded and cached into the map, then - * reuse that geometry whenever the uniforms are changed - * * This way we aren't reloading in the same mesh multiple times, treating - * the hash table as a slot of the mesh contained within that scene - * * Potentially std::map> m_geometries - * * Idea is the std::string is the geometries within this scene, the - * data format is: > - */ - class vk_renderer : public render_context { - public: - vk_renderer(const window_settings& p_settings, - uint32_t p_image_size, - const std::string& p_tag); - - ~vk_renderer() override = default; - - private: - void preload_assets(const VkRenderPass& p_renderpass) override; - - void start_frame(const ::vk::command_buffer& p_current, - const window_settings& p_settings, - const VkRenderPass& p_renderpass, - const VkFramebuffer& p_framebuffer, - const glm::mat4& p_proj_view) override; - void background_color(const std::array& p_color) override; - - void post_frame() override; - - void current_scene(ref) override; - - private: - VkDevice m_device = nullptr; - vk_physical_driver m_physical; - glm::mat4 m_proj_view; - VkRenderPass m_final_renderpass = nullptr; - window_settings m_window_extent; - ::vk::command_buffer m_current_command_buffer{}; - VkClearColorValue m_color; - - uint32_t m_image_count = 0; - shader_resource_group m_shader_group; - ::vk::pipeline m_main_pipeline; - ::vk::descriptor_resource m_global_descriptors; - std::vector m_sets_layouts; - - std::map m_cached_meshes; - ::vk::uniform_buffer m_global_uniforms; - ::vk::uniform_buffer m_point_light_uniforms; - - // game object-specific meshes - std::map m_mesh_geometry_set; - // TODO: Make this into a material system, eventually - std::map m_mesh_material_set; - std::map> - m_mesh_descriptors; - uint32_t m_current_frame = 0; - glm::mat4 m_model = { 1.f }; - - ::vk::texture m_white_texture; - - ref m_current_scene; - }; -}; diff --git a/atlas/drivers/vulkan-cpp/vk_swapchain.hpp b/atlas/drivers/vulkan-cpp/vk_swapchain.hpp deleted file mode 100644 index 50f9ef0e..00000000 --- a/atlas/drivers/vulkan-cpp/vk_swapchain.hpp +++ /dev/null @@ -1,102 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace atlas::vk { - /** - * @name vk_swapchain - * @brief High-level abstraction of a vulkan swapchain - * @brief ref will return the vk_swapchain directly - * @brief ref returning vk_swapchain will need to be changed - */ - class vk_swapchain { - public: - vk_swapchain() = default; - - /** - * @brief Constructs a new vulkan swapchain with a window settings to - * apply to this swapchain - * @param p_surface passing in the surface handler for swapchain uses - */ - vk_swapchain(const VkSurfaceKHR& p_surface, - const window_settings& p_settings); - - //! @return uint32_t the next available image to present acquired - uint32_t read_acquired_image(); - - //! @return current active command buffer being processed - [[nodiscard]] ::vk::command_buffer active_command( - uint32_t p_frame_index) { - return m_swapchain_command_buffers[p_frame_index]; - } - - [[nodiscard]] VkFramebuffer active_framebuffer(uint32_t p_frame) const { - return m_swapchain_framebuffers[p_frame]; - } - - [[nodiscard]] VkRenderPass swapchain_renderpass() const { - return m_final_renderpass; - } - - [[nodiscard]] window_settings settings() const { - return m_window_settings; - } - - [[nodiscard]] uint32_t image_size() const { return m_image_size; } - - [[nodiscard]] surface_properties data() const { - return m_surface_properties; - } - - void destroy(); - - void submit(std::span p_command); - - [[nodiscard]] ::vk::sample_image active_image(uint32_t p_index) const { - return m_swapchain_images[p_index]; - } - - operator VkSwapchainKHR() const { return m_swapchain_handler; } - - operator VkSwapchainKHR() { return m_swapchain_handler; } - - void present(const uint32_t& p_current_frame); - - private: - void invalidate(); - void create(); - - uint32_t select_images_size(const VkSurfaceCapabilitiesKHR&); - - private: - vk_physical_driver m_physical{}; - vk_driver m_driver{}; - VkSurfaceKHR m_current_surface_handler = nullptr; - VkSwapchainKHR m_swapchain_handler = nullptr; - VkExtent2D m_swapchain_extent{}; - window_settings m_window_settings{}; - - uint32_t m_image_size = 0; - - VkSurfaceKHR m_current_surface = nullptr; - surface_properties m_surface_properties{}; - std::vector<::vk::command_buffer> m_swapchain_command_buffers{}; - std::vector<::vk::framebuffer> m_swapchain_framebuffers; - - //! @brief setting up images - std::vector<::vk::sample_image> m_swapchain_images; - std::vector<::vk::sample_image> m_swapchain_depth_images; - - ::vk::renderpass m_final_renderpass; - - ::vk::device_present_queue m_present_to_queue; - }; - -}; diff --git a/atlas/drivers/vulkan-cpp/vk_window.hpp b/atlas/drivers/vulkan-cpp/vk_window.hpp deleted file mode 100644 index 2138bdb3..00000000 --- a/atlas/drivers/vulkan-cpp/vk_window.hpp +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once -#include -#include -#include - -namespace atlas::vk { - class vk_window : public window { - public: - vk_window(const window_settings& p_settings); - ~vk_window() override; - - private: - void center_window(); - - private: - [[nodiscard]] window_settings settings() const override; - - [[nodiscard]] vk_swapchain window_swapchain() const override { - return m_swapchain; - } - - [[nodiscard]] uint32_t read_acquired_next_frame() override; - - [[nodiscard]] GLFWwindow* native_window() const override; - - void presentation_process(const uint32_t& p_current_frame) override; - - [[nodiscard]] ::vk::command_buffer current_active_command( - uint32_t p_frame_idx) override { - return m_swapchain.active_command(p_frame_idx); - } - - private: - VkInstance m_instance_handler = nullptr; - GLFWwindow* m_window_handler = nullptr; - VkSurfaceKHR m_window_surface = nullptr; - window_settings m_settings{}; - - vk_swapchain m_swapchain{}; - static vk_window* s_instance; - }; -}; diff --git a/atlas/drivers/vulkan-cpp/vulkan-imports.hpp b/atlas/drivers/vulkan-cpp/vulkan-imports.hpp deleted file mode 100644 index 91d4652a..00000000 --- a/atlas/drivers/vulkan-cpp/vulkan-imports.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#define GLFW_INCLUDE_VULKAN -#if _WIN32 -#define VK_USE_PLATFORM_WIN32_KHR -#include -#define GLFW_EXPOSE_NATIVE_WIN32 -#include -#include -#else -#include -#include -#endif \ No newline at end of file diff --git a/atlas/drivers/vulkan/device.cppm b/atlas/drivers/vulkan/device.cppm new file mode 100644 index 00000000..c05f3202 --- /dev/null +++ b/atlas/drivers/vulkan/device.cppm @@ -0,0 +1,206 @@ +module; + +#include + +#define GLFW_INCLUDE_VULKAN +#if _WIN32 +#define VK_USE_PLATFORM_WIN32_KHR +#include +#define GLFW_EXPOSE_NATIVE_WIN32 +#include +#include +#else +#include +#include +#endif + +#include +#include + +export module atlas.drivers.vulkan.device; + +import atlas.drivers.vulkan.utilities; +import atlas.drivers.vulkan.physical_device; + +namespace atlas::vulkan { + static VkFormat search_supported_depth_format( + const VkPhysicalDevice& p_physical, + std::span p_formats, + VkImageTiling p_tiling, + VkFormatFeatureFlags p_feature_flag) { + VkFormat format = VK_FORMAT_UNDEFINED; + + for (size_t i = 0; i < p_formats.size(); i++) { + VkFormat current_format = p_formats[i]; + VkFormatProperties format_properties; + vkGetPhysicalDeviceFormatProperties( + p_physical, current_format, &format_properties); + + if (p_tiling == VK_IMAGE_TILING_LINEAR) { + if (format_properties.linearTilingFeatures & p_feature_flag) { + format = current_format; + } + } + else if (p_tiling == VK_IMAGE_TILING_OPTIMAL and + format_properties.optimalTilingFeatures & p_feature_flag) { + format = current_format; + } + } + + return format; + } + + static VkFormat search_depth_format(const VkPhysicalDevice& p_physical) { + std::vector candidate_formats = { + VK_FORMAT_D32_SFLOAT, + VK_FORMAT_D32_SFLOAT_S8_UINT, + VK_FORMAT_D24_UNORM_S8_UINT + }; + + VkFormat format = search_supported_depth_format( + p_physical, + candidate_formats, + VK_IMAGE_TILING_OPTIMAL, + VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT); + return format; + } + + /** + * @brief logical device implementation wrapper around the VkDevice + * + * This class was a wrapper around VKDevice, that provided you with other + * API's to do with that particular logical device. + * + * Logical devices are representation of virtual software ways to interact + * with the GPU through Vulkan specifications + * + * TODO: Out-of-date. Using vulkan-cpp ::vk::device class to provide API's + * that allow you to do more queries on specific attributes the logical + * device gives you. + */ + export class device { + struct device_queue_family { + VkQueue graphics_queue; + VkQueue transfer_queue; + VkQueue compute_queue; + }; + + public: + device() = default; + + /** + * @brief construct a new logical device + * @param p_physical is the physical device required for the creation of + * the logical device + */ + device(const physical_device& p_physical) : m_physical(p_physical) { + m_depth_format_selected = search_depth_format(m_physical); + + float queue_priority[1] = { 0.0f }; + + std::vector device_extension = { + VK_KHR_SWAPCHAIN_EXTENSION_NAME + }; + + uint32_t graphics_index = + m_physical.read_queue_family_indices().graphics; + + VkDeviceQueueCreateInfo queue_create_info = { + .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .queueFamilyIndex = graphics_index, + .queueCount = 1, + .pQueuePriorities = queue_priority, + }; + + VkDeviceCreateInfo create_info = { + .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .queueCreateInfoCount = 1, + .pQueueCreateInfos = &queue_create_info, + .enabledLayerCount = 0, + .ppEnabledLayerNames = nullptr, + .enabledExtensionCount = + static_cast(device_extension.size()), + .ppEnabledExtensionNames = device_extension.data(), + }; + + VkPhysicalDeviceFeatures features; + vkGetPhysicalDeviceFeatures(m_physical, &features); + features.robustBufferAccess = false; + create_info.pEnabledFeatures = &features; + + vk_check(vkCreateDevice(m_physical, &create_info, nullptr, &m_driver), + "vkCreateDevice"); + + vkGetDeviceQueue( + m_driver, graphics_index, 0, &m_device_queues.graphics_queue); + } + + // Use .destroy to explicitly invoke when to do proper vulkan cleanup + ~device() = default; + + /** + * @brief returns the specified graphics queue from this logical device + */ + [[nodiscard]] VkQueue graphics_queue() const { + return m_device_queues.graphics_queue; + } + + /** + * @return -1 if there are no flags available/compatible/valid + */ + uint32_t select_memory_type(uint32_t p_type_filter, VkMemoryPropertyFlags p_property_flag) { + VkPhysicalDeviceMemoryProperties mem_props; + vkGetPhysicalDeviceMemoryProperties(m_physical, &mem_props); + + for (uint32_t i = 0; i < mem_props.memoryTypeCount; i++) { + if ((p_type_filter & (1 << i)) and + (mem_props.memoryTypes[i].propertyFlags & p_property_flag) == + p_property_flag) { + return i; + } + } + + return -1; + } + + void destroy() { + vkDeviceWaitIdle(m_driver); + vkDestroyDevice(m_driver, nullptr); + } + + /** + * @brief gives you the depth format from the logical device + * + * @return VkFormat + */ + [[nodiscard]] VkFormat depth_format() const { + return m_depth_format_selected; + } + + /** + * @brief allows to treat vk_driver as a VkDevice handle + * + * For vulkan API's that accept only taking in VKDevice, this simplifies + * the need to not have a getter API + */ + operator VkDevice() const { return m_driver; } + + /** + * @brief allows to treat vk_driver as a VkDevice handle + * + * For vulkan API's that accept only taking in VKDevice, this simplifies + * the need to not have a getter API + */ + operator VkDevice() { return m_driver; } + + private: + physical_device m_physical{}; + VkDevice m_driver = nullptr; + device_queue_family m_device_queues{}; + VkFormat m_depth_format_selected; + }; +}; \ No newline at end of file diff --git a/atlas/drivers/vulkan-cpp/hash.hpp b/atlas/drivers/vulkan/hash.cppm similarity index 79% rename from atlas/drivers/vulkan-cpp/hash.hpp rename to atlas/drivers/vulkan/hash.cppm index 678810d7..20435c12 100644 --- a/atlas/drivers/vulkan-cpp/hash.hpp +++ b/atlas/drivers/vulkan/hash.cppm @@ -1,10 +1,13 @@ -#pragma once -#include +module; + #define GLM_ENABLE_EXPERIMENTAL #include -#include -namespace atlas { +export module atlas.drivers.vulkan.hash; + +import vk; + +export namespace atlas { template void hash_combine(size_t& seed, const T& v, const Rest&... rest) { seed ^= std::hash()(v) + 0x9e3779b9 + (seed << 6) + (seed << 2); @@ -13,7 +16,7 @@ namespace atlas { }; -namespace std { +export namespace std { // template<> // struct hash { @@ -26,8 +29,8 @@ namespace std { // } // }; template<> - struct hash<::vk::vertex_input> { - size_t operator()(const ::vk::vertex_input& vertex) const { + struct hash { + size_t operator()(const vk::vertex_input& vertex) const { size_t seed = 0; atlas::hash_combine( seed, vertex.position, vertex.color, vertex.normals, vertex.uv); diff --git a/atlas/drivers/vulkan/imgui_context.cppm b/atlas/drivers/vulkan/imgui_context.cppm new file mode 100644 index 00000000..9fc56aff --- /dev/null +++ b/atlas/drivers/vulkan/imgui_context.cppm @@ -0,0 +1,221 @@ +module; + +#include +#include +#include +#include +#include +#include +#include + +export module atlas.drivers.vulkan.imgui_context; + +import atlas.common; +import vk; + +import atlas.drivers.vulkan.instance_context; +import atlas.drivers.vulkan.physical_device; +import atlas.drivers.vulkan.device; +import atlas.drivers.vulkan.swapchain; + +namespace atlas::vulkan { + static void im_gui_layout_color_modification() { + auto& colors = ImGui::GetStyle().Colors; // @note Colors is ImVec4 + + colors[ImGuiCol_WindowBg] = ImVec4{ 0.1f, 0.105f, 0.11f, 1.0f }; + + // Headers + colors[ImGuiCol_Header] = ImVec4{ 0.2f, 0.205f, 0.21f, 1.0f }; + colors[ImGuiCol_HeaderHovered] = ImVec4{ 0.3f, 0.305f, 0.31f, 1.0f }; + colors[ImGuiCol_HeaderActive] = ImVec4{ 0.15f, 0.1505f, 0.151f, 1.0f }; + + // Buttons + colors[ImGuiCol_Button] = ImVec4{ 0.2f, 0.205f, 0.21f, 1.0f }; + colors[ImGuiCol_ButtonHovered] = ImVec4{ 0.3f, 0.305f, 0.31f, 1.0f }; + colors[ImGuiCol_ButtonActive] = ImVec4{ 0.15f, 0.1505f, 0.151f, 1.0f }; + + // Frame BG + colors[ImGuiCol_FrameBg] = ImVec4{ 0.2f, 0.205f, 0.21f, 1.0f }; + colors[ImGuiCol_FrameBgHovered] = ImVec4{ 0.3f, 0.305f, 0.31f, 1.0f }; + colors[ImGuiCol_FrameBgActive] = ImVec4{ 0.15f, 0.1505f, 0.151f, 1.0f }; + + // Tabs + colors[ImGuiCol_Tab] = ImVec4{ 0.15f, 0.1505f, 0.151f, 1.0f }; + colors[ImGuiCol_TabHovered] = ImVec4{ 0.38f, 0.3805f, 0.381f, 1.0f }; + colors[ImGuiCol_TabActive] = ImVec4{ 0.28f, 0.2805f, 0.281f, 1.0f }; + colors[ImGuiCol_TabUnfocused] = ImVec4{ 0.15f, 0.1505f, 0.15f, 1.0f }; + colors[ImGuiCol_TabUnfocusedActive] = + ImVec4{ 0.2f, 0.205f, 0.21f, 1.0f }; + + // Titles + colors[ImGuiCol_TitleBg] = ImVec4{ 0.15f, 0.1505f, 0.151f, 1.0f }; + colors[ImGuiCol_TitleBgActive] = ImVec4{ 0.15f, 0.1505f, 0.15f, 1.0f }; + colors[ImGuiCol_TitleBgCollapsed] = + ImVec4{ 0.1f, 0.150f, 0.951f, 1.0f }; + } + + export class imgui_context { + public: + imgui_context() = default; + + imgui_context(const VkInstance& p_instance, const swapchain& p_swapchain_ctx, GLFWwindow* p_window_ctx) { + // m_instance = instance_context::handle(); + m_instance = p_instance; + m_physical = instance_context::physical_driver(); + m_driver = instance_context::logical_device(); + + m_current_swapchain_handler = p_swapchain_ctx; + + // Setting up imgui + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); + + io.ConfigFlags |= + ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + // io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable + // Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking + io.ConfigFlags |= + ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform + // Windows + + // io.ConfigViewportsNoAutoMerge = true; + // io.ConfigViewportsNoAutoMerge = true; + // io.ConfigViewportsNoTaskBarIcon = true; + + // Setup Dear ImGui style + // ImGui::StyleColorsDark(); + // ImGui::StyleColorsClassic(); + im_gui_layout_color_modification(); + + ImGuiStyle& style = ImGui::GetStyle(); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { + style.WindowRounding = 0.0f; + style.Colors[ImGuiCol_WindowBg].w = 1.0f; + } + + m_viewport_command_buffers.resize( + p_swapchain_ctx.image_size()); + + for (size_t i = 0; i < m_viewport_command_buffers.size(); i++) { + ::vk::command_params settings = { + .levels = ::vk::command_levels::primary, + // .queue_index = enumerate_swapchain_settings.present_index, + .queue_index = 0, + .flags = ::vk::command_pool_flags::reset, + }; + m_viewport_command_buffers[i] = + ::vk::command_buffer(m_driver, settings); + } + + // ::vk::descriptor_res + // m_imgui_descriptor = ::vk::descriptor_resource(m_driver, {}); + // 1: create descriptor pool for IMGUI + // the size of the pool is very oversize, but it's copied from imgui + // demo itself. + std::array pool_sizes = { + VkDescriptorPoolSize{ VK_DESCRIPTOR_TYPE_SAMPLER, 100 }, + VkDescriptorPoolSize{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + 100 }, + VkDescriptorPoolSize{ VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 100 }, + VkDescriptorPoolSize{ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 100 }, + VkDescriptorPoolSize{ VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, + 100 }, + VkDescriptorPoolSize{ VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, + 100 }, + VkDescriptorPoolSize{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 100 }, + VkDescriptorPoolSize{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 100 }, + VkDescriptorPoolSize{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, + 100 }, + VkDescriptorPoolSize{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, + 100 }, + VkDescriptorPoolSize{ VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 100 } + }; + + VkDescriptorPoolCreateInfo desc_pool_create_info = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + .pNext = nullptr, + .flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, + .maxSets = static_cast(1000 * pool_sizes.size()), + // .poolSizeCount = (uint32_t)std::size(pool_sizes), + .poolSizeCount = static_cast(pool_sizes.size()), + .pPoolSizes = pool_sizes.data() + }; + + // VkDescriptorPool imgui_pool; + vk::vk_check(vkCreateDescriptorPool( + m_driver, &desc_pool_create_info, nullptr, &m_desc_pool), + "vkCreateDescriptorPool"); + + create(p_window_ctx, + p_swapchain_ctx.image_size(), + p_swapchain_ctx.swapchain_renderpass()); + } + + void create(GLFWwindow* p_window_handler, const uint32_t& p_image_size, const VkRenderPass& p_current_renderpass) { + ImGui_ImplGlfw_InitForVulkan(p_window_handler, true); + ImGui_ImplVulkan_InitInfo init_info = {}; + init_info.Instance = m_instance; + init_info.PhysicalDevice = m_physical; + init_info.Device = m_driver; + init_info.Queue = m_driver.graphics_queue(); + init_info.RenderPass = p_current_renderpass; + init_info.PipelineCache = nullptr; + init_info.DescriptorPool = m_desc_pool; + init_info.MinImageCount = 2; + init_info.ImageCount = p_image_size; + init_info.UseDynamicRendering = false; + init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT; + ImGui_ImplVulkan_Init(&init_info); + } + + void begin(const VkCommandBuffer& p_current, const uint32_t& p_frame_index) { + ImGui_ImplVulkan_NewFrame(); + ImGui_ImplGlfw_NewFrame(); + ImGui::NewFrame(); + + m_current_frame_index = p_frame_index; + m_current = p_current; + } + + void end() { + ImGui::Render(); + + ImDrawData* draw_data = ImGui::GetDrawData(); + ImGui_ImplVulkan_RenderDrawData(draw_data, m_current); + + ImGuiIO& io = ImGui::GetIO(); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + } + } + + [[nodiscard]] ::vk::command_buffer imgui_active_command() const { + return m_viewport_command_buffers[m_current_frame_index]; + } + + void destroy() { + ImGui_ImplVulkan_Shutdown(); + vkDestroyDescriptorPool(m_driver, m_desc_pool, nullptr); + + for (auto& command_buffer : m_viewport_command_buffers) { + command_buffer.destroy(); + } + + ImGui_ImplGlfw_Shutdown(); + ImGui::DestroyContext(); + } + + private: + VkInstance m_instance = nullptr; + VkPhysicalDevice m_physical = nullptr; + device m_driver{}; + uint32_t m_current_frame_index = 0; + VkSwapchainKHR m_current_swapchain_handler = nullptr; + VkDescriptorPool m_desc_pool = nullptr; + VkCommandBuffer m_current = nullptr; + std::vector<::vk::command_buffer> m_viewport_command_buffers; + }; +}; \ No newline at end of file diff --git a/atlas/drivers/vulkan/instance_context.cppm b/atlas/drivers/vulkan/instance_context.cppm new file mode 100644 index 00000000..80b828fd --- /dev/null +++ b/atlas/drivers/vulkan/instance_context.cppm @@ -0,0 +1,247 @@ +module; + +#include +#include +#include +#include + +#define GLFW_INCLUDE_VULKAN +#if _WIN32 +#define VK_USE_PLATFORM_WIN32_KHR +#include +#define GLFW_EXPOSE_NATIVE_WIN32 +#include +#include +#else +#include +#include +#endif + +export module atlas.drivers.vulkan.instance_context; + +import atlas.logger; + +import atlas.drivers.graphics_context; +import atlas.drivers.vulkan.utilities; +import atlas.drivers.vulkan.physical_device; +import atlas.drivers.vulkan.device; + +namespace atlas::vulkan { + static std::vector initialize_instance_extensions() { + std::vector extension_names; + + extension_names.emplace_back(VK_KHR_SURFACE_EXTENSION_NAME); + extension_names.emplace_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + + // An additional surface extension needs to be loaded. This extension is + // platform-specific so needs to be selected based on the platform the + // example is going to be deployed to. Preprocessor directives are used + // here to select the correct platform. +#ifdef VK_USE_PLATFORM_WIN32_KHR + extension_names.emplace_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); +#endif +#ifdef VK_USE_PLATFORM_XLIB_KHR + extensionNames.emplace_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME); +#endif +#ifdef VK_USE_PLATFORM_XCB_KHR + extensionNames.emplace_back(VK_KHR_XCB_SURFACE_EXTENSION_NAME); +#endif +#ifdef VK_USE_PLATFORM_ANDROID_KHR + extensionNames.emplace_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME); +#endif +#ifdef VK_USE_PLATFORM_WAYLAND_KHR + extensionNames.emplace_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME); +#endif +#ifdef VK_USE_PLATFORM_MACOS_MVK + extensionNames.emplace_back(VK_MVK_MACOS_SURFACE_EXTENSION_NAME); +#endif +#ifdef USE_PLATFORM_NULLWS + extensionNames.emplace_back(VK_KHR_DISPLAY_EXTENSION_NAME); +#endif + + return extension_names; + } + +#ifdef _DEBUG + const std::vector validation_layers = { + "VK_LAYER_KHRONOS_validation", + "VK_LAYER_KHRONOS_synchronization2" + }; + + static VKAPI_ATTR VkBool32 VKAPI_CALL debug_callback( + [[maybe_unused]] VkDebugUtilsMessageSeverityFlagBitsEXT + p_message_severity, + [[maybe_unused]] VkDebugUtilsMessageTypeFlagsEXT p_message_type, + const VkDebugUtilsMessengerCallbackDataEXT* p_callback_data, + [[maybe_unused]] void* p_user_data) { + console_log_trace("validation layer:\t\t{}", p_callback_data->pMessage); + return false; + } +#endif + + export class instance_context : public graphics_context { + public: + instance_context(const std::string&) { + VkApplicationInfo app_info = { + .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, + .pNext = nullptr, + .applicationVersion = 1, + .pEngineName = "TheAtlasEngine", + .engineVersion = 1, + .apiVersion = VK_API_VERSION_1_3, + }; + + VkInstanceCreateInfo create_info = { + .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .pApplicationInfo = &app_info + }; + + //! @note Setting up the required extensions for vulkan + std::vector extensions = initialize_instance_extensions(); + #if _DEBUG + extensions.push_back("VK_EXT_debug_utils"); + #endif + create_info.enabledExtensionCount = + static_cast(extensions.size()); + create_info.ppEnabledExtensionNames = extensions.data(); + #ifdef _DEBUG + // by default we enable validation layers used for debugging! + create_info.enabledLayerCount = + static_cast(validation_layers.size()); + create_info.ppEnabledLayerNames = validation_layers.data(); + + // printing out available validation layers + uint32_t layer_count; + std::vector available_validation_layers; + vkEnumerateInstanceLayerProperties(&layer_count, nullptr); + + available_validation_layers.resize(layer_count); + vkEnumerateInstanceLayerProperties(&layer_count, + available_validation_layers.data()); + + console_log_trace("================================================"); + console_log_trace("\tValidation Layers Available"); + console_log_trace("================================================"); + for (VkLayerProperties properties : available_validation_layers) { + console_log_trace("Validation Layer:\t\t{}", properties.layerName); + console_log_trace("Description\t\t{}", properties.description); + console_log_trace("Version\t\t\t{}", (int)properties.specVersion); + } + + console_log_trace("\n"); + console_log_trace("================================================\n"); + + VkDebugUtilsMessengerCreateInfoEXT debug_create_info = { + .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, + .messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT, + .messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT, + .pfnUserCallback = debug_callback, + }; + + create_info.pNext = + (VkDebugUtilsMessengerCreateInfoEXT*)&debug_create_info; + #else + create_info.enabledLayerCount = 0; + create_info.ppEnabledLayerNames = nullptr; + create_info.pNext = nullptr; + #endif + vk_check(vkCreateInstance(&create_info, nullptr, &m_instance_handler), + "vkCreateInstance"); + + #if _DEBUG + // This needs to be created after the VkInstance is or else it wont be + // applied the debug information during validation layer error message + // execution + m_vk_set_debug_utils_object_name_ext = + reinterpret_cast( + vkGetInstanceProcAddr(m_instance_handler, + "vkSetDebugUtilsObjectNameEXT")); + #endif + + m_physical = physical_device(m_instance_handler); + m_device = device(m_physical); + + s_instance = this; + } + + ~instance_context() override { + vkDestroyInstance(m_instance_handler, nullptr); + } + + /** + * @brief returns function pointer to allow for setting debug object + * name + * + * + * This allows for utilizing vkSetDebugUtilsObjectNameEXT during debug + * builds + * + * This allows for setting up object names that is useful to the + * programmer when a validation layer error message occurs unexpectedly + * + */ + static PFN_vkSetDebugUtilsObjectNameEXT get_debug_object_name() { + return s_instance->m_vk_set_debug_utils_object_name_ext; + } + + /** + * @brief used for providing a way to submit all vulkan metaobjects + * before the destruction of the vulkan logical device + * + * Per vulkan specification, it is required to have all object handles + * created with the logical device to be destroyed before the logical + * device itself gets destroyed during post cleanup + * + * This function was a means to ensure that the destruction of those + * vulkan child objects are handled in that order correctly + */ + static void submit_resource_free(std::function&& p_resource) { + s_instance->m_resources_free.push_back(p_resource); + } + + + static physical_device physical_driver() { + return s_instance->m_physical; + } + + static device logical_device() { + return s_instance->m_device; + } + + protected: + [[nodiscard]] VkInstance context_handle() const override { + return m_instance_handler; + } + + void context_submit_resource_free(const std::function& p_resource) override { + m_resources_free.push_back(p_resource); + } + + void destroy_context() override { + console_log_info("destroy_context!"); + for (auto& callback : m_resources_free) { + callback(); + } + + m_device.destroy(); + } + + private: + + private: + static instance_context* s_instance; + VkInstance m_instance_handler=nullptr; + std::deque> m_resources_free{}; + PFN_vkSetDebugUtilsObjectNameEXT m_vk_set_debug_utils_object_name_ext; + physical_device m_physical; + device m_device; + }; + + instance_context* instance_context::s_instance = nullptr; +}; \ No newline at end of file diff --git a/atlas/drivers/vulkan/mesh.cppm b/atlas/drivers/vulkan/mesh.cppm new file mode 100644 index 00000000..23e63b0e --- /dev/null +++ b/atlas/drivers/vulkan/mesh.cppm @@ -0,0 +1,255 @@ +module; + +#include +#include +#define STB_IMAGE_IMPLEMENTATION +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include +#include +#include +#include +#include + +export module atlas.drivers.vulkan.mesh; +import vk; + +import atlas.logger; +import atlas.drivers.vulkan.instance_context; +import atlas.drivers.vulkan.physical_device; +import atlas.drivers.vulkan.device; +import atlas.drivers.vulkan.hash; + + +export namespace atlas::vulkan { + /** + * @brief mesh class specifically defined with vulkan implementations for + * specific primitives + * TODO: Whenever we load in a texture that will be laucnhed asyncronously + * + * @brief mesh class will contain metadata needed by vulkan specifications + * Ways to communicate through vulkan by only supplying information needed + * to update this mesh + * + * @brief Represents a renderable object -- supporting various material + * types, etc + * TODO - For now we have a map, this should + * be expanded to a proper material system for blending various materials + */ + class mesh { + public: + mesh() = default; + mesh(std::span p_vertices, std::span p_indices) { + m_physical = instance_context::physical_driver(); + m_device = instance_context::logical_device(); + + vk::vertex_params vbo_settings = { .phsyical_memory_properties = + m_physical.memory_properties(), + .vertices = p_vertices }; + vk::index_params ibo_settings = { .phsyical_memory_properties = + m_physical.memory_properties(), + .indices = p_indices }; + m_vbo = vk::vertex_buffer(m_device, vbo_settings); + m_ibo = vk::index_buffer(m_device, ibo_settings); + } + + mesh(const std::filesystem::path& p_filename, bool p_flip = false) : m_flip(p_flip) { + m_physical = instance_context::physical_driver(); + m_device = instance_context::logical_device(); + reload_mesh(p_filename); + } + + //! @brief Reload mesh vertices and indices when requested + void reload_mesh(const std::filesystem::path& p_filename) { + load_obj(p_filename); + } + + void draw(const VkCommandBuffer& p_command_buffer) { + m_vbo.bind(p_command_buffer); + if (m_ibo.size() > 0) { + m_ibo.bind(p_command_buffer); + vkCmdDrawIndexed(p_command_buffer, m_ibo.size(), 1, 0, 0, 0); + } + else { + vkCmdDraw(p_command_buffer, m_vbo.size(), 1, 0, 0); + } + } + + void destroy() { + m_vbo.destroy(); + m_ibo.destroy(); + + m_diffuse.destroy(); + m_specular.destroy(); + m_geoemtry_ubo.destroy(); + m_material_ubo.destroy(); + } + + //! @brief Loading single texture with specified std::filesystem::path + void add_diffuse(const std::filesystem::path& p_path) { + ::vk::texture_info config_texture = { + .phsyical_memory_properties = m_physical.memory_properties(), + .filepath = p_path, + }; + m_diffuse = ::vk::texture(m_device, config_texture); + + if (!m_diffuse.loaded()) { + console_log_info("Diffuse Texture {} is NOT loaded!!!", + p_path.string()); + return; + } + } + + void add_specular(const std::filesystem::path& p_path) { + vk::texture_info config_texture = { + .phsyical_memory_properties = m_physical.memory_properties(), + .filepath = p_path, + }; + m_specular = vk::texture(m_device, config_texture); + + if (!m_specular.loaded()) { + console_log_error("Specular Texture {} is NOT loaded!!!", + p_path.string()); + return; + } + } + + [[nodiscard]] ::vk::sample_image diffuse() const { + return m_diffuse.image(); + } + [[nodiscard]] ::vk::sample_image specular() const { + return m_specular.image(); + } + + //! @return true if mesh geometry model loaded succesfully + [[nodiscard]] bool loaded() const { return m_model_loaded; } + + [[nodiscard]] bool diffuse_loaded() const { return m_diffuse.loaded(); } + + [[nodiscard]] bool specular_loaded() const { + return m_specular.loaded(); + } + + void set_flip(bool p_flip) { m_flip = p_flip; } + + private: + void load_obj(const std::filesystem::path& p_filename) { + tinyobj::attrib_t attrib; + std::vector shapes; + std::vector materials; + std::string warn, err; + + //! @note If we return the constructor then we can check if the mesh + //! loaded successfully + //! @note We also receive hints if the loading is successful! + //! @note Return default constructor automatically returns false means + //! that mesh will return the boolean as false because it wasnt + //! successful + if (!tinyobj::LoadObj(&attrib, + &shapes, + &materials, + &warn, + &err, + p_filename.string().c_str())) { + console_log_warn("Could not load model from path {}", + p_filename.string()); + m_model_loaded = false; + return; + } + + std::vector vertices; + std::vector indices; + std::unordered_map unique_vertices{}; + + // for (const auto& shape : shapes) { + for (size_t i = 0; i < shapes.size(); i++) { + auto shape = shapes[i]; + // for (const auto& index : shape.mesh.indices) { + for (size_t j = 0; j < shape.mesh.indices.size(); j++) { + auto index = shape.mesh.indices[j]; + vk::vertex_input vertex{}; + + if (!unique_vertices.contains(vertex)) { + unique_vertices[vertex] = + static_cast(vertices.size()); + vertices.push_back(vertex); + } + + if (index.vertex_index >= 0) { + vertex.position = { + attrib.vertices[3 * index.vertex_index + 0], + attrib.vertices[3 * index.vertex_index + 1], + attrib.vertices[3 * index.vertex_index + 2] + }; + + vertex.color = { + attrib.colors[3 * index.vertex_index + 0], + attrib.colors[3 * index.vertex_index + 1], + attrib.colors[3 * index.vertex_index + 2] + }; + } + + if (!attrib.normals.empty()) { + vertex.normals = { + attrib.normals[3 * index.normal_index + 0], + attrib.normals[3 * index.normal_index + 1], + attrib.normals[3 * index.normal_index + 2] + }; + } + if (!attrib.texcoords.empty()) { + glm::vec2 flipped_uv = { + attrib.texcoords + [static_cast(index.texcoord_index) * 2], + 1.0f - attrib.texcoords[static_cast( + index.texcoord_index) * + 2 + + 1], + }; + + glm::vec2 original_uv = { + attrib.texcoords + [static_cast(index.texcoord_index) * 2], + attrib.texcoords + [static_cast(index.texcoord_index) * 2 + + 1], + }; + + vertex.uv = m_flip ? flipped_uv : original_uv; + } + else { + vertex.uv = glm::vec2(0.f, 0.f); + } + + if (!unique_vertices.contains(vertex)) { + unique_vertices[vertex] = + static_cast(vertices.size()); + vertices.push_back(vertex); + } + + indices.push_back(unique_vertices[vertex]); + } + } + + ::vk::vertex_params vbo_settings = { .phsyical_memory_properties = + m_physical.memory_properties(), + .vertices = vertices }; + ::vk::index_params ibo_settings = { .phsyical_memory_properties = + m_physical.memory_properties(), + .indices = indices }; + m_vbo = ::vk::vertex_buffer(m_device, vbo_settings); + m_ibo = ::vk::index_buffer(m_device, ibo_settings); + m_model_loaded = true; + } + + private: + physical_device m_physical; + VkDevice m_device = nullptr; + vk::texture m_diffuse; + vk::texture m_specular; + vk::vertex_buffer m_vbo{}; + vk::index_buffer m_ibo{}; + vk::uniform_buffer m_geoemtry_ubo; + vk::uniform_buffer m_material_ubo; + bool m_model_loaded = false; + bool m_flip = false; + }; +}; \ No newline at end of file diff --git a/atlas/drivers/vulkan/physical_device.cppm b/atlas/drivers/vulkan/physical_device.cppm new file mode 100644 index 00000000..03956e7d --- /dev/null +++ b/atlas/drivers/vulkan/physical_device.cppm @@ -0,0 +1,216 @@ +module; + +#include +#include + +#define GLFW_INCLUDE_VULKAN +#if _WIN32 +#define VK_USE_PLATFORM_WIN32_KHR +#include +#define GLFW_EXPOSE_NATIVE_WIN32 +#include +#include +#else +#include +#include +#endif + +export module atlas.drivers.vulkan.physical_device; + +import atlas.logger; +import atlas.drivers.vulkan.utilities; + +export namespace atlas::vulkan { + struct surface_properties { + VkSurfaceCapabilitiesKHR surface_capabilities; + VkSurfaceFormatKHR surface_format; + }; + + /** + * @brief vulkan-specific implementation wrapper around VkPhysicalDevice + * + * Wrapper that constructs a single physical device that provides API's that + * can be used to query specific information for your specific physical + * device + * + * A physical device represents your current hardware GPU and allows for the + * ability to enumerate information to check for compatibility on the + * current GPU + */ + class physical_device { + struct queue_family_indices { + uint32_t graphics = -1; + uint32_t compute = -1; + uint32_t transfer = -1; + }; + + public: + physical_device() = default; + + /** + * @brief constructs a new vulkan physical device + * + * @param p_instance requires a VkInstance to create a VkPhysicalDevice + * handle + */ + physical_device(const VkInstance& p_instance) { + uint32_t device_count = 0; + vkEnumeratePhysicalDevices(p_instance, &device_count, nullptr); + + if (device_count == 0) { + console_log_fatal("Device Count is {} and no devices found!!!", + device_count); + return; + } + + std::vector physical_drivers(device_count); + vkEnumeratePhysicalDevices( + p_instance, &device_count, physical_drivers.data()); + + for (const auto& device : physical_drivers) { + VkPhysicalDeviceProperties device_properties; + VkPhysicalDeviceFeatures device_features; + vkGetPhysicalDeviceProperties(device, &device_properties); + vkGetPhysicalDeviceFeatures(device, &device_features); + if (device_properties.deviceType == + VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) { + m_physical_driver = device; + break; + } + } + + uint32_t queue_family_count = 0; + vkGetPhysicalDeviceQueueFamilyProperties( + m_physical_driver, &queue_family_count, nullptr); + m_queue_family_properties.resize(queue_family_count); + + vkGetPhysicalDeviceQueueFamilyProperties( + m_physical_driver, + &queue_family_count, + m_queue_family_properties.data()); + + m_queue_indices = select_queue_family_indices(); + } + + ~physical_device() = default; + + /** + * @brief gives you the queue families that are supported + * + * @return queue_family_indices are the indices of the specific queue's + * that are compatible on current hardware specifications + */ + [[nodiscard]] queue_family_indices read_queue_family_indices() const { + return m_queue_indices; + } + + [[nodiscard]] VkPhysicalDeviceMemoryProperties memory_properties() const { + VkPhysicalDeviceMemoryProperties physical_memory_properties; + vkGetPhysicalDeviceMemoryProperties(m_physical_driver, + &physical_memory_properties); + return physical_memory_properties; + } + + /** + * @return uint32_t is the index to the presentation index of the + * specific presentation queue + */ + [[nodiscard]] uint32_t read_presentation_index(const VkSurfaceKHR& p_surface) { + uint32_t presentation_index = -1; + VkBool32 compatible = VK_FALSE; + uint32_t i = 0; + for (const auto& queue_family : m_queue_family_properties) { + if (queue_family.queueFlags & VK_QUEUE_GRAPHICS_BIT) { + vk_check(vkGetPhysicalDeviceSurfaceSupportKHR( + m_physical_driver, i, p_surface, &compatible), + "vkGetPhysicalDeviceSurfaceSupportKHR"); + + if (compatible) { + presentation_index = i; + } + } + i++; + } + + return presentation_index; + } + + /** + * @brief querying surface properties based on the currently specified + * VkSurfaceKHR handle created + */ + [[nodiscard]] surface_properties get_surface_properties(const VkSurfaceKHR& p_surface) { + vk_check(vkGetPhysicalDeviceSurfaceCapabilitiesKHR( + m_physical_driver, + p_surface, + &m_surface_properties.surface_capabilities), + "vkGetPhysicalDeviceSurfaceCapabilitiesKHR"); + + uint32_t format_count = 0; + std::vector formats; + vk_check(vkGetPhysicalDeviceSurfaceFormatsKHR( + m_physical_driver, p_surface, &format_count, nullptr), + "vkGetPhysicalDeviceSurfaceFormatsKHR"); + + formats.resize(format_count); + + vk_check(vkGetPhysicalDeviceSurfaceFormatsKHR( + m_physical_driver, p_surface, &format_count, formats.data()), + "vkGetPhysicalDeviceSurfaceFormatsKHR"); + + for (const auto& format : formats) { + if (format.format == VK_FORMAT_B8G8R8A8_SRGB && + format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { + m_surface_properties.surface_format = format; + } + } + + m_surface_properties.surface_format = formats[0]; + + return m_surface_properties; + } + + /** + * @brief Allows for treating vk_physical_device as a VkPhysicalDevice + * handle + * + * Simplifies using this same class for creating other vulkan + * metaobjects + */ + operator VkPhysicalDevice() { return m_physical_driver; } + + /** + * @brief Allows for treating vk_physical_device as a VkPhysicalDevice + * handle + * + * Simplifies using this same class for creating other vulkan + * metaobjects + */ + operator VkPhysicalDevice() const { return m_physical_driver; } + + private: + queue_family_indices select_queue_family_indices() { + VkPhysicalDeviceMemoryProperties physical_device_memory_properties; + vkGetPhysicalDeviceMemoryProperties(m_physical_driver, + &physical_device_memory_properties); + physical_device::queue_family_indices indices; + int i = 0; + + for (const auto& queue_family : m_queue_family_properties) { + if (queue_family.queueFlags & VK_QUEUE_GRAPHICS_BIT) { + indices.graphics = i; + break; + } + i++; + } + + return indices; + } + + private: + VkPhysicalDevice m_physical_driver = nullptr; + queue_family_indices m_queue_indices{}; + std::vector m_queue_family_properties{}; + surface_properties m_surface_properties{}; + }; +}; \ No newline at end of file diff --git a/atlas/drivers/vulkan/render_system.cppm b/atlas/drivers/vulkan/render_system.cppm new file mode 100644 index 00000000..678c7192 --- /dev/null +++ b/atlas/drivers/vulkan/render_system.cppm @@ -0,0 +1,675 @@ +module; + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +export module atlas.drivers.vulkan.render_system; +import atlas.drivers.renderer_system; + +import atlas.core.utilities; +import vk; +import atlas.core.scene; +import atlas.drivers.graphics_context; +import atlas.drivers.vulkan.instance_context; +import atlas.drivers.vulkan.physical_device; +import atlas.drivers.vulkan.device; +import atlas.drivers.vulkan.mesh; +import atlas.drivers.vulkan.shader_resource_group; +import atlas.core.scene.world; +import atlas.core.scene; +import atlas.drivers.vulkan.uniforms; +import atlas.drivers.vulkan.mesh; +import atlas.core.scene.components; + +export namespace atlas::vulkan { + /** + * @brief Something to consider for mesh loading. + * + * How will meshes get loaded? + * + * There are a few ways for considering for the base approach, which are: + * TODO: These are things to think about how this may happen because I want + * to make this lightweight in the sense the data isnt continuously being + * modified. Taking a looking at minimizing the loading state of vulkan + * implementation-specific meshes + * * While also making the way how mesh components are being added the + * same as before + * - Something to avoid is the entities containing the geometry data itself + * but being able to reference to their respective geometry data that are + * getting submitted to the GPU + * + * * Batching ID's into hash table that contains the actual geometry data + * * Using ID to search up the mesh loaded and cached into the map, then + * reuse that geometry whenever the uniforms are changed + * * This way we aren't reloading in the same mesh multiple times, treating + * the hash table as a slot of the mesh contained within that scene + * * Potentially std::map> m_geometries + * * Idea is the std::string is the geometries within this scene, the + * data format is: > + */ + class render_system : public renderer_system { + public: + render_system(ref p_context, + const window_params& p_params, + uint32_t p_image_size, + const std::string&) { + m_physical = instance_context::physical_driver(); + m_device = instance_context::logical_device(); + + m_window_extent = p_params; + m_image_count = p_image_size; + +#ifdef USE_SHADERC + console_log_info("shaderc enabled!!"); + std::array shader_sources = { + vk::shader_source{ + "builtin.shaders/test.vert", + vk::shader_stage::vertex, + }, + vk::shader_source { + "builtin.shaders/test.frag", + vk::shader_stage::fragment, + } + }; +#else + console_log_info("shaderc disabled!!"); + std::array shader_sources = { + vk::shader_source{ + "builtin.shaders/test.vert.spv", + vk::shader_stage::vertex, + }, + vk::shader_source{ + "builtin.shaders/test.frag.spv", + vk::shader_stage::fragment, + } + }; +#endif + std::array attribute_entries = { + vk::vertex_attribute_entry{ + .location = 0, + .format = vk::format::rgb32_sfloat, + .stride = offsetof(vk::vertex_input, position), + }, + vk::vertex_attribute_entry{ + .location = 1, + .format = vk::format::rgb32_sfloat, + .stride = offsetof(vk::vertex_input, color), + }, + vk::vertex_attribute_entry{ + .location = 2, + .format = vk::format::rgb32_sfloat, + .stride = offsetof(vk::vertex_input, normals), + }, + vk::vertex_attribute_entry{ + .location = 3, + .format = vk::format::rg32_sfloat, + .stride = offsetof(vk::vertex_input, uv), + } + }; + + std::array attribute = { + vk::vertex_attribute{ + // layout (set = 0, binding = 0) + .binding = 0, + .entries = attribute_entries, + .stride = sizeof(vk::vertex_input), + .input_rate = vk::input_rate::vertex, + }, + }; + + vk::shader_resource_info shader_info = { + .sources = shader_sources, + }; + + try { + m_shader_group = shader_resource_group(m_device, shader_info); + m_shader_group.vertex_attributes(attribute); + } + catch (std::runtime_error& e) { + console_log_error("Compilation Error!!"); + console_log_error("{}", e.what()); + } + // Setting global descriptor set 0 + std::vector set0_entries = { + vk::descriptor_entry{ + // specifies "layout (set = 0, binding = 0) uniform GlobalUbo" + .type = vk::buffer::uniform, + .binding_point = { + .binding = 0, + .stage = vk::shader_stage::vertex, + }, + .descriptor_count = 1, + }, + vk::descriptor_entry{ + // specifies "layout (set = 0, binding = 1) uniform light_ubo" + .type = vk::buffer::uniform, + .binding_point = { + .binding = 1, + .stage = vk::shader_stage::fragment, + }, + .descriptor_count = 1, + }, + }; + + // uint32_t image_count = image_count; + vk::descriptor_layout set0_layout = { + .slot = 0, + .max_sets = m_image_count, + .entries = set0_entries, + }; + m_global_descriptors = + vk::descriptor_resource(m_device, set0_layout); + + vk::uniform_params global_info = { + .phsyical_memory_properties = m_physical.memory_properties(), + .size_bytes = sizeof(global_ubo), + .debug_name = "\nm_global_uniforms\n", + .vkSetDebugUtilsObjectNameEXT = + instance_context::get_debug_object_name() + }; + m_global_uniforms = vk::uniform_buffer(m_device, global_info); + + // setting up our light uniforms as the global uniforms rather then + // per-object basis + vk::uniform_params light_ubo_params = { + .phsyical_memory_properties = m_physical.memory_properties(), + .size_bytes = sizeof(light_scene_ubo), + }; + m_point_light_uniforms = + vk::uniform_buffer(m_device, light_ubo_params); + + std::array binding0_uniforms = { + vk::write_buffer{ + .buffer = m_global_uniforms, + .offset = 0, + .range = m_global_uniforms.size_bytes(), + }, + }; + + std::array binding1_uniforms = { + vk::write_buffer{ + .buffer = m_point_light_uniforms, + .offset = 0, + .range = m_point_light_uniforms.size_bytes(), + }, + }; + + std::array set0_write_buffers = { + vk::write_buffer_descriptor{ + .dst_binding = 0, + .uniforms = binding0_uniforms, + }, + vk::write_buffer_descriptor{ + .dst_binding = 1, + .uniforms = binding1_uniforms, + } + }; + m_global_descriptors.update(set0_write_buffers); + + m_sets_layouts = { + m_global_descriptors.layout(), + }; + + vk::image_extent extent = { + .width = 1, + .height = 1, + }; + m_white_texture = + vk::texture(m_device, extent, m_physical.memory_properties()); + + p_context->submit_resource_free([this]() { + console_log_info("vulkan::render_system destructin invoked!!"); + m_white_texture.destroy(); + m_shader_group.destroy(); + m_global_descriptors.destroy(); + m_global_uniforms.destroy(); + m_point_light_uniforms.destroy(); + for (auto& [id, mesh] : m_cached_meshes) { + console_log_trace( + "Entity \"{}\" Destroyed in vk_renderer!!!", id); + mesh.destroy(); + } + + for (auto& [id, uniform] : m_mesh_geometry_set) { + uniform.destroy(); + } + + for (auto& [id, material_uniform] : m_mesh_material_set) { + material_uniform.destroy(); + } + + for (auto& [key, descriptor_map] : m_mesh_descriptors) { + for (auto& [descriptor_type, descriptor] : descriptor_map) { + descriptor.destroy(); + } + } + m_main_pipeline.destroy(); + }); + + console_log_info( + "Vulkan-specific implementation constructed successfully!!!"); + } + + ~render_system() override = default; + + private: + void preload_assets(const VkRenderPass& p_renderpass) override { + m_final_renderpass = p_renderpass; + // set 1 -- material uniforms + // ref current_world = system_registry::get_world("Editor + // World"); ref current_scene = + // current_world->get_scene("LevelScene"); + + if (m_current_scene == nullptr) { + console_log_error("m_current_scene == nullptr!"); + return; + } + else { + console_log_error("m_current_scene != nullptr!"); + } + + flecs::query<> caching = + m_current_scene->query_builder().build(); + + caching.each([this](flecs::entity p_entity) { + const mesh_source* target = p_entity.get(); + mesh new_mesh(std::filesystem::path(target->model_path), + target->flip); + + // we do a check if the geometry uniform associated with this + // game object is valid + if (!m_mesh_geometry_set.contains(p_entity.id())) { + vk::uniform_params geo_info = { + .phsyical_memory_properties = + m_physical.memory_properties(), + .size_bytes = sizeof(material_uniform), + }; + m_mesh_geometry_set[p_entity.id()] = + vk::uniform_buffer(m_device, geo_info); + } + + // check if material is already associated with this particular + // game object + if (!m_mesh_material_set.contains(p_entity.id())) { + vk::uniform_params mat_info = { + .phsyical_memory_properties = + m_physical.memory_properties(), + .size_bytes = sizeof(material_metadata), + }; + m_mesh_material_set[p_entity.id()] = + vk::uniform_buffer(m_device, mat_info); + } + + new_mesh.add_diffuse(std::filesystem::path(target->diffuse)); + new_mesh.add_specular(std::filesystem::path(target->specular)); + + if (new_mesh.loaded()) { + m_cached_meshes.emplace(p_entity.id(), new_mesh); + + std::vector set1_entries = { + vk::descriptor_entry{ + // specifies "layout (set = 1, binding = 0) uniform geometry_uniform" + .type = vk::buffer::uniform, + .binding_point = { + .binding = 0, + .stage = vk::shader_stage::vertex, + }, + .descriptor_count = 1, + }, + vk::descriptor_entry{ + // specifies "layout (set = 1, binding = 1) uniform sampler2D diffuse_texture" + .type = vk::buffer::combined_image_sampler, + .binding_point = { + .binding = 1, + .stage = vk::shader_stage::fragment, + }, + .descriptor_count = 1, + }, + vk::descriptor_entry{ + // specifies "layout (set = 1, binding = 2) uniform sampler2D specular_texture" + .type = vk::buffer::combined_image_sampler, + .binding_point = { + .binding = 2, + .stage = vk::shader_stage::fragment, + }, + .descriptor_count = 1, + }, + vk::descriptor_entry{ + // specifies "layout (set = 1, binding = 3) uniform sampler2D material_ubo" + .type = vk::buffer::uniform, + .binding_point = { + .binding = 3, + .stage = vk::shader_stage::fragment, + }, + .descriptor_count = 1, + }, + }; + + vk::descriptor_layout set1_layout = { + .slot = 1, + .max_sets = m_image_count, + .entries = set1_entries, + }; + + m_mesh_descriptors[p_entity.id()].emplace( + "materials", + vk::descriptor_resource(m_device, set1_layout)); + + // specify to the vk::write_descriptor_buffer + std::array binding0_buffers = { + vk::write_buffer{ + .buffer = m_mesh_geometry_set[p_entity.id()], + .offset = 0, + .range = + m_mesh_geometry_set[p_entity.id()].size_bytes(), + } + }; + + std::array binding3_buffers = { + vk::write_buffer{ + .buffer = m_mesh_material_set[p_entity.id()], + .offset = 0, + .range = + m_mesh_material_set[p_entity.id()].size_bytes(), + } + }; + + std::vector + material_uniforms = { + // layout(set= 1, binding = 0) geometry_ubo + vk::write_buffer_descriptor{ + .dst_binding = 0, + .uniforms = binding0_buffers, + }, + // layout(set= 1, binding = 3) material_ubo + vk::write_buffer_descriptor{ + .dst_binding = 3, + .uniforms = binding3_buffers, + }, + }; + + // layout(set = 1, binding = 1) + // If the texture loaded successfully then we use that + // texture, otherwise utilize the default white texture + vk::sample_image diffuse = + m_cached_meshes[p_entity.id()].diffuse_loaded() + ? m_cached_meshes[p_entity.id()].diffuse() + : m_white_texture.image(); + + // layout(set = 1, binding = 2) + vk::sample_image specular = + m_cached_meshes[p_entity.id()].specular_loaded() + ? m_cached_meshes[p_entity.id()].specular() + : m_white_texture.image(); + + // writes to texture at layout(set = 1, binding = 1) + std::array binding1_images = { + vk::write_image{ + .sampler = diffuse.sampler(), + .view = diffuse.image_view(), + // .image_layout = + // VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + .layout = vk::image_layout::shader_read_only_optimal, + }, + }; + + // writes to texture at layout(set = 1, binding = 2) + std::array binding2_images = { + vk::write_image{ + .sampler = specular.sampler(), + .view = specular.image_view(), + // .image_layout = + // VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + .layout = vk::image_layout::shader_read_only_optimal, + }, + }; + + // vulkan image descriptors are for writing textures + std::vector + material_textures = { + // layout(set = 1, binding = 1) uniform sampler2D + vk::write_image_descriptor{ + .dst_binding = 1, + .sample_images = binding1_images, + }, + // layout(set = 1, binding = 2) uniform sampler2D + vk::write_image_descriptor{ + .dst_binding = 2, + .sample_images = binding2_images, + }, + }; + + m_mesh_descriptors[p_entity.id()]["materials"].update( + material_uniforms, material_textures); + + m_sets_layouts.push_back( + m_mesh_descriptors[p_entity.id()]["materials"].layout()); + } + }); + + std::vector modules = m_shader_group.handles(); + + vk::pipeline_settings pipeline_configuration = { + .renderpass = m_final_renderpass, + .shader_modules = modules, + .vertex_attributes = m_shader_group.vertex_attributes(), + .vertex_bind_attributes = + m_shader_group.vertex_bind_attributes(), + .descriptor_layouts = m_sets_layouts + }; + m_main_pipeline = vk::pipeline(m_device, pipeline_configuration); + + console_log_warn("graphics pipeline = {}", m_main_pipeline.alive()); + } + + void start_frame(const vk::command_buffer& p_current, + const window_params& p_settings, + const VkRenderPass& p_renderpass, + const VkFramebuffer& p_framebuffer, + const glm::mat4& p_proj_view, + uint32_t p_current_frame) override { + m_proj_view = p_proj_view; + // m_current_frame = application::current_frame(); + m_current_frame = p_current_frame; + m_final_renderpass = p_renderpass; + + std::array clear_values = {}; + + clear_values[0].color = m_color; + clear_values[1].depthStencil = { 1.f, 0 }; + m_window_extent = p_settings; + + VkRenderPassBeginInfo renderpass_begin_info = { + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, + .pNext = nullptr, + .renderPass = p_renderpass, + .renderArea = { + .offset = { + .x = 0, + .y = 0 + }, + .extent = { + .width = p_settings.width, + .height = p_settings.height + }, + }, + .clearValueCount = static_cast(clear_values.size()), + .pClearValues = clear_values.data() + }; + + m_current_command_buffer = p_current; + m_current_command_buffer.begin( + vk::command_usage::simulatneous_use_bit); + + VkViewport viewport = { + .x = 0.0f, + .y = 0.0f, + .width = static_cast(m_window_extent.width), + .height = static_cast(m_window_extent.height), + .minDepth = 0.0f, + .maxDepth = 1.0f, + }; + + vkCmdSetViewport(m_current_command_buffer, 0, 1, &viewport); + + VkRect2D scissor = { + .offset = { 0, 0 }, + .extent = { m_window_extent.width, m_window_extent.height }, + }; + + vkCmdSetScissor(m_current_command_buffer, 0, 1, &scissor); + + renderpass_begin_info.framebuffer = p_framebuffer; + + vkCmdBeginRenderPass(m_current_command_buffer, + &renderpass_begin_info, + VK_SUBPASS_CONTENTS_INLINE); + } + + void background_color(const glm::vec4& p_color) override { + m_color = { + { p_color.x, p_color.y, p_color.z, p_color.w }, + }; + } + + void post_frame() override { + // For now, using this. Will need to remove this before vulkan + // integration merging into dev This is for testing and to hopefully + // have a global_ubo for globalized uniforms + global_ubo global_frame_ubo = { .mvp = m_proj_view }; + + // TODO: Make to_bytes be part of utilities. This can be useful in + // sending the amount of bytes in batches for batch-rendering + // std::span bytes_data = to_bytes(global_frame_ubo); + // m_global_uniforms.update(bytes_data.data()); + m_global_uniforms.update(&global_frame_ubo); + + // ref current_world = system_registry::get_world("Editor + // World"); ref current_scene = + // current_world->get_scene("LevelScene"); + + // query all entities that have a point light + flecs::query query_point_lights = + m_current_scene->query_builder().build(); + + light_scene_ubo test_light = {}; + uint32_t index = 0; + query_point_lights.each( + [&index, &test_light](flecs::entity p_entity, + point_light& p_light) { + const transform* t = p_entity.get(); + p_light.position = t->position; + + test_light.light_sources[index] = { + .position = glm::vec4(p_light.position, 1.f), + .color = p_light.color, + .attenuation = p_light.attenuation, + .constant = p_light.constant, + .linear = p_light.linear, + .quadratic = p_light.quadratic, + .ambient = p_light.ambient, + .diffuse = p_light.diffuse, + .specular = p_light.specular, + }; + index += 1; + }); + test_light.num_lights = index; + + m_point_light_uniforms.update(&test_light); + + // query all objects with a specified 3d mesh source + flecs::query<> query_targets = + m_current_scene->query_builder().build(); + + m_main_pipeline.bind(m_current_command_buffer); + + // Bind global camera data here + m_global_descriptors.bind(m_current_command_buffer, + m_main_pipeline.layout()); + query_targets.each([this](flecs::entity p_entity) { + const transform* transform_component = + p_entity.get(); + const mesh_source* material_component = + p_entity.get(); + m_model = glm::mat4(1.f); + m_model = + glm::translate(m_model, transform_component->position); + m_model = glm::scale(m_model, transform_component->scale); + glm::mat4 rotation_mat4 = + glm::mat4(glm::quat(transform_component->rotation)); + + m_model *= rotation_mat4; + + // Mesh used for viking_room - replaced with std::map equivalent + geometry_uniform mesh_ubo = { .model = m_model, + .color = + material_component->color }; + + if (m_cached_meshes[p_entity.id()].loaded()) { + m_mesh_geometry_set[p_entity.id()].update(&mesh_ubo); + + material_metadata data = {}; + + if (p_entity.has()) { + data = *p_entity.get(); + } + m_mesh_material_set[p_entity.id()].update(&data); + + m_mesh_descriptors[p_entity.id()]["materials"].bind( + m_current_command_buffer, m_main_pipeline.layout()); + + m_cached_meshes[p_entity.id()].draw( + m_current_command_buffer); + } + }); + + vkCmdEndRenderPass(m_current_command_buffer); + m_current_command_buffer.end(); + } + + void current_scene(ref p_scene_ctx) override { + m_current_scene = p_scene_ctx; + } + + private: + VkDevice m_device = nullptr; + physical_device m_physical; + glm::mat4 m_proj_view; + VkRenderPass m_final_renderpass = nullptr; + window_params m_window_extent; + vk::command_buffer m_current_command_buffer{}; + VkClearColorValue m_color; + + uint32_t m_image_count = 0; + shader_resource_group m_shader_group; + vk::pipeline m_main_pipeline; + vk::descriptor_resource m_global_descriptors; + std::vector m_sets_layouts; + + std::map m_cached_meshes; + vk::uniform_buffer m_global_uniforms; + vk::uniform_buffer m_point_light_uniforms; + + // game object-specific meshes + std::map m_mesh_geometry_set; + // TODO: Make this into a material system, eventually + std::map m_mesh_material_set; + std::map> + m_mesh_descriptors; + uint32_t m_current_frame = 0; + glm::mat4 m_model = { 1.f }; + + vk::texture m_white_texture; + + ref m_current_scene; + }; +}; \ No newline at end of file diff --git a/atlas/drivers/vulkan/shader_resource_group.cppm b/atlas/drivers/vulkan/shader_resource_group.cppm new file mode 100644 index 00000000..ff5c23d5 --- /dev/null +++ b/atlas/drivers/vulkan/shader_resource_group.cppm @@ -0,0 +1,430 @@ +module; + +#include +#include +#include +#include +#include + +export module atlas.drivers.vulkan.shader_resource_group; + +import atlas.logger; +import vk; + +namespace atlas::vulkan { + // Reading the raw .spv binaries + static std::vector read_raw_spirv(const std::string& p_file) { + std::vector out_buffer; + std::ifstream ins(p_file, std::ios::ate | std::ios::binary); + + if (!ins) { + throw std::runtime_error("Cannot load in .spv files!!"); + } + + uint32_t file_size = (uint32_t)ins.tellg(); + out_buffer.resize(file_size); + ins.seekg(0); + ins.read(out_buffer.data(), file_size); + return out_buffer; + } + + //! @brief Ensure file reads are valid before reading raw .spv binaries + static std::vector compile_binary_shader_source( + const ::vk::shader_source& p_shader_source) { + + if (!std::filesystem::is_regular_file(p_shader_source.filename)) { + throw std::runtime_error("Cannot load .spv file"); + } + + return read_raw_spirv(p_shader_source.filename); + } + +#if ENABLE_SHADERC + static std::string read_shader_source_code(const std::string& p_filename) { + std::ifstream ins(p_filename, std::ios::ate | std::ios::binary); + + if (!ins.is_open()) { + console_log_warn("Could not open filename = {}", p_filename); + return { 'a' }; + } + + size_t file_size = (size_t)ins.tellg(); + std::string output; + output.resize(file_size); + ins.seekg(0); + ins.read(output.data(), static_cast(file_size)); + + return output; + } +#endif + + /** + * compiles source code from the shader directly without needing manual + * recompilation + * + * shaderc requires these parameters to compile + * text_source_code: the std::string version of the entire source code to + * compile type: shader stage this shader corresponds to filename: input + * filename text entry_point: the entry point to this shader options: + * compiler-specific options to enable when compiling the shader sources + */ +#if ENABLE_SHADERC + static std::vector compile_source_from_file( + const ::vk::shader_source& p_shader_source) { + shaderc::CompileOptions options; + options.SetTargetEnvironment(shaderc_target_env_vulkan, + shaderc_env_version_vulkan_1_3); + options.SetWarningsAsErrors(); + + shaderc_shader_kind type; + + switch (p_shader_source.stage) { + case ::vk::shader_stage::vertex: + type = shaderc_glsl_vertex_shader; + break; + case ::vk::shader_stage::fragment: + type = shaderc_glsl_fragment_shader; + break; + default: + throw std::runtime_error("shader_stage unspecified!~!!"); + } + + shaderc::Compiler compiler; + std::string text_source_code = + read_shader_source_code(p_shader_source.filename); + + // Prints out the text of the shader source code + // console_log_warn("Source Text Code!!!"); + // console_log_info("{}", text_source_code); + shaderc::CompilationResult result = + compiler.CompileGlslToSpv(text_source_code, + type, + p_shader_source.filename.c_str(), + "main", + options); + + std::vector blob; + + if (result.GetCompilationStatus() != + shaderc_compilation_status_success) { + throw std::runtime_error( + std::format("Shader Compilation Error! Failed with reason {}\n{}", + p_shader_source.filename, + result.GetErrorMessage()) + .c_str()); + } + + for (auto blob_chunk : result) { + blob.push_back(blob_chunk); + } + + return blob; + } +#endif + /** + * @brief resource group for loading shader sources that give you back + * VkShaderModule handles + * + * Responsibility is to load stages of shader sources whether that be + * through precompiler .spv files or through shaderc runtime shader + * compilation + * + * Responsibility is loading and streaming the amount of bytes from the + * compiled shader sources into the vulkan shader module handles + * + * resource groups up the creation and management of vulkan shader modules. + */ + export class shader_resource_group { + public: + shader_resource_group() = default; + /** + * @brief constructs a new shader_resource_group + * + * @param p_device is the logical device required to creating the vulkan + * shader module + * @param p_info has the properties such as specified shader sources to + * load/compile + */ + shader_resource_group(const VkDevice& p_device, const ::vk::shader_resource_info& p_info) : m_device(p_device) { + + // We go through all of the specified shader source and their specific + // stage Compile them through shader compiler or if provided a .spv, + // then we compile and read in the stream of bytes directly + for (size_t i = 0; i < p_info.sources.size(); i++) { + const vk::shader_source shader_src = p_info.sources[i]; + std::filesystem::path filepath = + std::filesystem::path(shader_src.filename); +#ifndef ENABLE_SHADERC + if (filepath.extension().string() == ".spv") { + std::vector blob = + compile_binary_shader_source(shader_src); + + if (blob.empty()) { + m_resource_valid = false; + throw std::runtime_error("Cannot load in vector " + "blob of compiled down data!!!"); + } + + create_module(blob, shader_src); + } +#endif + +#ifdef ENABLE_SHADERC + if (filepath.extension().string() != ".spv") { + std::string text_source_code = + read_shader_source_code(filepath.string()); + std::vector blob = + compile_source_from_file(shader_src); + create_module(blob, shader_src); + } +#endif + } + + } + + ~shader_resource_group() = default; + + /** + * @return true if resources are valid, otherwise return false + */ + [[nodiscard]] bool valid() const { return m_resource_valid; } + + /** + * @brief sets the vertex attributes with the shader modules that gets + * used by ::vk::pipeline (graphics pipeline) + * + * @param p_attributes is the high-level specifications for setting up + * vertex attributes that correspond with these shaders + */ + void vertex_attributes(std::span p_attributes) { + /* + -- These comments are a reminder to myself -- + - this function simplifies the need to separately define vertex + attributes and the vertex binding attributes as shown below: + + - vertex attributes specify the types of data within the vertex + + - vertex binding attribute specifies the rate of reading that data + layout specified by the vertex attributes + + - Interpret the following vertex attributes below with this shader + code with `layout(location = n)` specified where by default these are + set to binding zero by the shader + + layout(location = 0) in vec3 inPosition; + layout(location = 1) in vec3 inColor; + layout(location = 2) in vec3 inNormals; + layout(location = 3) in vec2 inTexCoords; + + m_shader_group.set_vertex_attributes(VkVertexInputAttributeDescription{ + { .location = 0, .binding = 0, .format = + VK_FORMAT_R32G32B32_SFLOAT, .offset = offsetof(vk::vertex, position), + }, { .location = 1, .binding = 0, .format = + VK_FORMAT_R32G32B32_SFLOAT, .offset = offsetof(vk::vertex, color), }, + { .location = 2, .binding = 0, .format = + VK_FORMAT_R32G32B32_SFLOAT, .offset = offsetof(vk::vertex, normals), + }, { .location = 3, .binding = 0, .format = VK_FORMAT_R32G32_SFLOAT, + .offset = offsetof(vk::vertex, uv), }, + }); + + m_shader_group.set_vertex_bind_attributes(VkVertexInputBindingDescription{ + {.binding = 0, .stride = sizeof(vk::vertex), .inputRate = + VK_VERTEX_INPUT_RATE_VERTEX,}, + }); + + Which gets handled in specifying the following below + */ + + m_vertex_binding_attributes.resize(p_attributes.size()); + + for (size_t i = 0; i < m_vertex_binding_attributes.size(); i++) { + // setting up vertex binding + const ::vk::vertex_attribute attribute = p_attributes[i]; + m_vertex_attributes.resize(attribute.entries.size()); + m_vertex_binding_attributes[i] = { .binding = attribute.binding, + .stride = attribute.stride, + .inputRate = to_input_rate( + attribute.input_rate) }; + + // then setting up the vertex attributes for the vertex data layouts + for (size_t j = 0; j < attribute.entries.size(); j++) { + const ::vk::vertex_attribute_entry entry = attribute.entries[j]; + m_vertex_attributes[j] = { .location = entry.location, + .binding = attribute.binding, + .format = static_cast( + entry.format), + .offset = entry.stride }; + } + } + } + + /** + * @brief this gives you back the shader module handles along with each + * of their stages they have been compiled with + * + * Returns the vector to retain the shader modules that are needed by + * the graphics pipeline. + * + * It is required by vulkan specs the graphics pipeline to contain valid + * shader modules of the compiled shaders + * + * @return vector<::vk::shader_handle> + */ + [[nodiscard]] std::vector<::vk::shader_handle> handles() const { + return map_to_vector(); + } + + /** + * @return span + */ + [[nodiscard]] std::span + vertex_attributes() const { + return m_vertex_attributes; + } + + /** + * @return span + */ + [[nodiscard]] std::span + vertex_bind_attributes() const { + return m_vertex_binding_attributes; + } + + /** + * @brief explicit cleanup to the VkShaderModule handles created with + * this particular resource group + */ + void destroy() { + for (auto& [filename, shader_handle] : m_modules) { + if (shader_handle.module != nullptr) { + vkDestroyShaderModule(m_device, shader_handle.module, nullptr); + } + } + } + + /** + * @brief ideally used for requesting for reload + * + * Planning to use this for invalidation when for runtime shader + * hot-reloading + * + * @note this is not used at the moment as shader runtime hot reloading + * is currently not supported. + */ + [[nodiscard]] bool reload_requested() const { + return m_reload_requested; + } + + private: + /** + * converts unordered_map to + * vector + */ + [[nodiscard]] std::vector<::vk::shader_handle> map_to_vector() const { + // Using C++'s std::views to extract all of the values in + // unordered_map to a vector + // that gets passed to graphics pipeline + // TEMP: Removing this. Will add this back in later. + // return (m_modules | std::views::values | + // std::ranges::to()); + std::vector<::vk::shader_handle> result; + + result.reserve(m_modules.size()); + + for (auto const& [name, handle] : m_modules) { + result.push_back(handle); + } + + return result; + } + + void create_module(std::span p_blob, const ::vk::shader_source& p_source) { + VkShaderModuleCreateInfo shader_module_ci = { + .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, + .pNext = nullptr, + .codeSize = p_blob.size(), + .pCode = reinterpret_cast(p_blob.data()) + }; + + std::filesystem::path filepath(p_source.filename); + std::string filename = filepath.filename().string(); + + // Setting m_shader_module_handlers[i]'s stage and the VkShaderModule + // handle altogether construct this beforehand and then we are going set + // that shader module + m_modules.emplace(filename, ::vk::shader_handle{}); + ::vk::vk_check( + vkCreateShaderModule( + m_device, &shader_module_ci, nullptr, &m_modules[filename].module), + "vkCreateShaderModule"); + m_modules[filename].stage = p_source.stage; + } + + void create_module(std::span p_blob, const ::vk::shader_source& p_source) { + VkShaderModuleCreateInfo shader_module_ci = { + .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, + .pNext = nullptr, + .codeSize = p_blob.size_bytes(), + .pCode = p_blob.data() + }; + + // console_log_info("map key = {}", p_source.filename); + std::filesystem::path filepath(p_source.filename); + + std::string filename = filepath.filename().string(); + + console_log_info("Key = {}", filename); + + // Setting m_shader_module_handlers[i]'s stage and the VkShaderModule + // handle altogether construct this beforehand and then we are going set + // that shader module + m_modules.emplace(filename, ::vk::shader_handle{}); + ::vk::vk_check( + vkCreateShaderModule( + m_device, &shader_module_ci, nullptr, &m_modules[filename].module), + "vkCreateShaderModule"); + m_modules[filename].stage = p_source.stage; + } + + /* + // TODO: Re-add this when shaderc works again. + void reload_shader(const ::vk::shader_source& p_source) { + console_log_info("p_source.filename = {}", p_source.filename); + if (m_modules[p_source.filename].module != nullptr) { + vkDestroyShaderModule( + m_device, m_modules[p_source.filename].module, nullptr); + } + + auto& handle = m_modules[p_source.filename]; + + std::filesystem::path filepath(p_source.filename); + std::string text_source_code = + read_shader_source_code(filepath.string()); + std::vector blob = compile_source_from_file(p_source); + std::span view_blob(blob.data(), blob.size()); + // create_module(blob, p_source); + VkShaderModuleCreateInfo shader_module_ci = { + .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, + .pNext = nullptr, + .codeSize = view_blob.size_bytes(), + .pCode = view_blob.data() + }; + + ::vk::vk_check(vkCreateShaderModule( + m_device, &shader_module_ci, nullptr, &handle.module), + "vkCreateShaderModule"); + } + */ + + private: + VkDevice m_device = nullptr; + std::vector m_vertex_attributes; + std::vector + m_vertex_binding_attributes; + bool m_resource_valid = false; + // shader module handles + std::unordered_map m_modules; + // ref m_watcher; + bool m_reload_requested = false; + }; +}; \ No newline at end of file diff --git a/atlas/drivers/vulkan/swapchain.cppm b/atlas/drivers/vulkan/swapchain.cppm new file mode 100644 index 00000000..21733e36 --- /dev/null +++ b/atlas/drivers/vulkan/swapchain.cppm @@ -0,0 +1,338 @@ +module; + +#include +#include +#include + +export module atlas.drivers.vulkan.swapchain; + +import vk; + +// import atlas.logger; +// import atlas.core.utilities.types; +import atlas.core.utilities; +import atlas.drivers.vulkan.instance_context; +import atlas.drivers.vulkan.physical_device; +import atlas.drivers.vulkan.device; +import atlas.drivers.vulkan.utilities; + +export namespace atlas::vulkan { + /** + * @name vk_swapchain + * @brief High-level abstraction of a vulkan swapchain + * @brief ref will return the vk_swapchain directly + * @brief ref returning vk_swapchain will need to be changed + */ + class swapchain { + public: + swapchain() = default; + + /** + * @brief Constructs a new vulkan swapchain with a window settings to + * apply to this swapchain + * @param p_surface passing in the surface handler for swapchain uses + */ + swapchain(const VkSurfaceKHR& p_surface, const window_params& p_params) : m_current_surface_handler(p_surface), m_window_params(p_params), m_current_surface(p_surface) { + m_physical = instance_context::physical_driver(); + m_driver = instance_context::logical_device(); + + create(); + + console_log_info_tagged("vulkan", "swapchain created successfully!!!"); + } + + //! @return uint32_t the next available image to present acquired + uint32_t read_acquired_image() { + m_present_to_queue.wait_idle(); + + // uint32_t frame_idx = m_present_to_queue.acquired_frame(); + uint32_t frame_idx = m_present_to_queue.acquire_next_image(); + if (m_present_to_queue.out_of_date()) { + invalidate(); + m_present_to_queue.out_of_date(false); + frame_idx = m_present_to_queue.acquire_next_image(); + } + + return frame_idx; + } + + //! @return current active command buffer being processed + [[nodiscard]] vk::command_buffer active_command(uint32_t p_frame_index) { + return m_swapchain_command_buffers[p_frame_index]; + } + + [[nodiscard]] VkFramebuffer active_framebuffer(uint32_t p_frame) const { + return m_swapchain_framebuffers[p_frame]; + } + + [[nodiscard]] VkRenderPass swapchain_renderpass() const { + return m_final_renderpass; + } + + [[nodiscard]] window_params settings() const { + return m_window_params; + } + + [[nodiscard]] uint32_t image_size() const { return m_image_size; } + + [[nodiscard]] surface_properties data() const { + return m_surface_properties; + } + + void destroy() { + vkDeviceWaitIdle(m_driver); + + for (size_t i = 0; i < m_swapchain_framebuffers.size(); i++) { + m_swapchain_framebuffers[i].destroy(); + } + + m_final_renderpass.destroy(); + + m_present_to_queue.destroy(); + + for (size_t i = 0; i < m_swapchain_command_buffers.size(); i++) { + m_swapchain_command_buffers[i].destroy(); + } + + for (uint32_t i = 0; i < m_swapchain_depth_images.size(); i++) { + m_swapchain_depth_images[i].destroy(); + } + + for (uint32_t i = 0; i < m_swapchain_images.size(); i++) { + m_swapchain_images[i].destroy(); + } + + vkDestroySwapchainKHR(m_driver, m_swapchain_handler, nullptr); + } + + void submit(std::span p_commands) { + m_present_to_queue.submit_async(p_commands); + } + + [[nodiscard]] ::vk::sample_image active_image(uint32_t p_index) const { + return m_swapchain_images[p_index]; + } + + operator VkSwapchainKHR() const { return m_swapchain_handler; } + + operator VkSwapchainKHR() { return m_swapchain_handler; } + + void present(const uint32_t& p_current_frame) { + m_present_to_queue.present_frame(p_current_frame); + if (m_present_to_queue.out_of_date()) { + invalidate(); + m_present_to_queue.out_of_date(false); + } + } + + private: + void invalidate() { + destroy(); + create(); + } + + void create() { + // surface properties are always updated from the physical device + // so they are valid should the swapchain be recreated + m_surface_properties = + m_physical.get_surface_properties(m_current_surface); + m_window_params.width = + m_surface_properties.surface_capabilities.currentExtent.width; + m_window_params.height = + m_surface_properties.surface_capabilities.currentExtent.height; + + //! @note Setting up presentation stuff + // request what our minimum image count is + uint32_t request_min_image_count = + select_images_size(m_surface_properties.surface_capabilities); + + m_swapchain_extent = + m_surface_properties.surface_capabilities.currentExtent; + + // setting our presentation properties + uint32_t present_index = + m_physical.read_presentation_index(m_current_surface_handler); + + VkSwapchainCreateInfoKHR swapchain_ci = { + .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, + .surface = m_current_surface_handler, + .minImageCount = request_min_image_count, + .imageFormat = m_surface_properties.surface_format.format, + .imageColorSpace = m_surface_properties.surface_format.colorSpace, + // use physical device surface formats to getting the right formats + // in vulkan + .imageExtent = m_swapchain_extent, + .imageArrayLayers = 1, + .imageUsage = (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | + VK_IMAGE_USAGE_TRANSFER_DST_BIT), + .queueFamilyIndexCount = 1, + .pQueueFamilyIndices = &present_index, + .preTransform = + m_surface_properties.surface_capabilities.currentTransform, + .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, + .presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR, + .clipped = true + }; + + vk_check(vkCreateSwapchainKHR( + m_driver, &swapchain_ci, nullptr, &m_swapchain_handler), + "vkCreateSwapchainKHR"); + + //!@brief querying images available + uint32_t image_count = 0; + vkGetSwapchainImagesKHR(m_driver, + m_swapchain_handler, + &image_count, + nullptr); // used to get the amount of images + std::vector images(image_count); + vkGetSwapchainImagesKHR(m_driver, + m_swapchain_handler, + &image_count, + images.data()); // used to store in the images + + // Creating Images + m_swapchain_images.resize(image_count); + m_image_size = image_count; + m_swapchain_depth_images.resize(image_count); + + VkFormat depth_format = m_driver.depth_format(); + + for (uint32_t i = 0; i < m_swapchain_images.size(); i++) { + ::vk::image_params color_image_config = { + .extent = { m_swapchain_extent.width, + m_swapchain_extent.height, }, + .format = m_surface_properties.surface_format.format, + .aspect = ::vk::image_aspect_flags::color_bit, + .usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + .mip_levels = 1, + .layer_count = 1, + .phsyical_memory_properties = m_physical.memory_properties(), + + }; + + m_swapchain_images[i] = + ::vk::sample_image(m_driver, images[i], color_image_config); + + ::vk::image_params depth_image_config = { + .extent = { m_swapchain_extent.width, + m_swapchain_extent.height, }, + .format = depth_format, + .aspect = ::vk::image_aspect_flags::depth_bit, + .usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, + .mip_levels = 1, + .layer_count = 1, + .phsyical_memory_properties = m_physical.memory_properties(), + }; + + m_swapchain_depth_images[i] = + ::vk::sample_image(m_driver, depth_image_config); + } + + // command buffers + + m_swapchain_command_buffers.resize(image_count); + + for (size_t i = 0; i < m_swapchain_command_buffers.size(); i++) { + ::vk::command_params settings = { + .levels = ::vk::command_levels::primary, + // .queue_index = enumerate_swapchain_settings.present_index, + .queue_index = 0, + .flags = ::vk::command_pool_flags::reset, + }; + + m_swapchain_command_buffers[i] = + ::vk::command_buffer(m_driver, settings); + } + + std::array<::vk::attachment, 2> renderpass_attachments = { + ::vk::attachment{ + .format = m_surface_properties.surface_format.format, + .layout = ::vk::image_layout::color_optimal, + .samples = ::vk::sample_bit::count_1, + .load = ::vk::attachment_load::clear, + .store = ::vk::attachment_store::store, + .stencil_load = ::vk::attachment_load::clear, + .stencil_store = ::vk::attachment_store::dont_care, + .initial_layout = ::vk::image_layout::undefined, + .final_layout = ::vk::image_layout::present_src_khr, + }, + ::vk::attachment{ + .format = depth_format, + .layout = ::vk::image_layout::depth_stencil_optimal, + .samples = ::vk::sample_bit::count_1, + .load = ::vk::attachment_load::clear, + .store = ::vk::attachment_store::dont_care, + .stencil_load = ::vk::attachment_load::dont_care, + .stencil_store = ::vk::attachment_store::dont_care, + .initial_layout = ::vk::image_layout::undefined, + .final_layout = ::vk::image_layout::depth_stencil_optimal, + }, + }; + m_final_renderpass = ::vk::renderpass(m_driver, renderpass_attachments); + + // creating framebuffers + m_swapchain_framebuffers.resize(m_swapchain_images.size()); + + for (uint32_t i = 0; i < m_swapchain_images.size(); i++) { + + std::array + image_attachments = { m_swapchain_images[i].image_view(), + m_swapchain_depth_images[i].image_view() }; + + vk::framebuffer_params framebuffer_info = { + .renderpass = m_final_renderpass, + .views = image_attachments, + .extent = m_swapchain_extent + }; + m_swapchain_framebuffers[i] = + vk::framebuffer(m_driver, framebuffer_info); + } + + vk::queue_params present_queue_params{ + .family = 0, + .index = 0, + }; + m_present_to_queue = ::vk::device_present_queue(m_driver, m_swapchain_handler, present_queue_params); + console_log_info("m_present_to_queue initialized!!!"); + console_log_info("m_presesnt_to_queue.alive() = {}", (m_present_to_queue != nullptr)); + } + + uint32_t select_images_size(const VkSurfaceCapabilitiesKHR& p_surface_capabilities) { + uint32_t requested_images = p_surface_capabilities.minImageCount + 1; + uint32_t final_image_count = 0; + + if ((p_surface_capabilities.maxImageCount > 0) and + (requested_images > p_surface_capabilities.maxImageCount)) { + final_image_count = p_surface_capabilities.maxImageCount; + } + else { + final_image_count = requested_images; + } + + return final_image_count; + } + + private: + physical_device m_physical{}; + device m_driver{}; + VkSurfaceKHR m_current_surface_handler = nullptr; + VkSwapchainKHR m_swapchain_handler = nullptr; + VkExtent2D m_swapchain_extent{}; + window_params m_window_params{}; + + uint32_t m_image_size = 0; + + VkSurfaceKHR m_current_surface = nullptr; + surface_properties m_surface_properties{}; + std::vector m_swapchain_command_buffers{}; + std::vector m_swapchain_framebuffers; + + //! @brief setting up images + std::vector m_swapchain_images; + std::vector m_swapchain_depth_images; + + vk::renderpass m_final_renderpass; + + vk::device_present_queue m_present_to_queue; + }; +}; \ No newline at end of file diff --git a/atlas/drivers/vulkan-cpp/uniforms.hpp b/atlas/drivers/vulkan/uniforms.cppm similarity index 63% rename from atlas/drivers/vulkan-cpp/uniforms.hpp rename to atlas/drivers/vulkan/uniforms.cppm index a9172551..a5f27c1e 100644 --- a/atlas/drivers/vulkan-cpp/uniforms.hpp +++ b/atlas/drivers/vulkan/uniforms.cppm @@ -1,7 +1,12 @@ -#pragma once -#include +module; -namespace atlas::vk { +#include + +export module atlas.drivers.vulkan.uniforms; + +import vk; + +export namespace atlas::vulkan { struct vertex_input { glm::vec3 position; glm::vec3 color; @@ -46,7 +51,26 @@ namespace atlas::vk { */ struct material_uniform { float shininess = 1.f; - ::vk::texture diffuse; - ::vk::texture specular; + vk::texture diffuse; + vk::texture specular; + }; + + struct point_light_ubo { + glm::vec4 position; // this is provided by the transform + glm::vec4 color = { 1.f, 1.f, 1.f, 1.f }; + float attenuation = 1.f; + float constant = 1.f; + float linear = 1.f; + float quadratic = 1.f; + + glm::vec4 ambient = glm::vec4(1.f); + glm::vec4 diffuse = glm::vec4(1.f); + glm::vec4 specular = glm::vec4(1.f); + }; + + struct light_scene_ubo { + alignas(16) uint32_t num_lights; + // alignas(16) std::array light_sources; + alignas(16) point_light_ubo light_sources[10]; }; }; \ No newline at end of file diff --git a/atlas/drivers/vulkan/utilities.cppm b/atlas/drivers/vulkan/utilities.cppm new file mode 100644 index 00000000..3611e637 --- /dev/null +++ b/atlas/drivers/vulkan/utilities.cppm @@ -0,0 +1,19 @@ +module; + +#include +#include + +export module atlas.drivers.vulkan.utilities; +import atlas.logger; + +export namespace atlas { + namespace vulkan { + + void vk_check(const VkResult& p_result, + const std::string& p_name) { + if (p_result != VK_SUCCESS) { + console_log_error_tagged("vulkan", "{} VkResult returned: {}", p_name, (int)p_result); + } + } + }; +}; \ No newline at end of file diff --git a/atlas/drivers/vulkan/window_context.cppm b/atlas/drivers/vulkan/window_context.cppm new file mode 100644 index 00000000..18aee443 --- /dev/null +++ b/atlas/drivers/vulkan/window_context.cppm @@ -0,0 +1,125 @@ +module; + +#include + +#define GLFW_INCLUDE_VULKAN +#if _WIN32 +#define VK_USE_PLATFORM_WIN32_KHR +#include +#define GLFW_EXPOSE_NATIVE_WIN32 +#include +#include +#else +#include +#include +#endif + +export module atlas.drivers.vulkan.window_context; + +// import atlas.logger; +// import atlas.core.utilities.types; +import atlas.core.utilities; +import vk; + +import atlas.window; +import atlas.drivers.graphics_context; +// import atlas.drivers.vulkan.instance_context; +import atlas.drivers.vulkan.utilities; +import atlas.drivers.vulkan.swapchain; + + +export namespace atlas { + namespace vulkan { + /** + * @brief vulkan-backend implementation of the application-window + */ + class window_context : public window { + public: + window_context(ref p_context, const window_params& p_params) : m_params(p_params) { + + console_log_info("window_context constructed!!!"); + + if(!glfwVulkanSupported()) { + console_log_error("GLFW: Vulkan is not supported!!!"); + console_log_error("GLFW: Vulkan Supported = {}", static_cast(glfwVulkanSupported())); + return; + } + + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); + + // m_instance = instance_context::handle(); + m_instance = p_context->handle(); + + m_window_handle = glfwCreateWindow(static_cast(m_params.width), static_cast(m_params.height), m_params.name.c_str(), nullptr, nullptr); + + glfwMakeContextCurrent(m_window_handle); + + console_log_info("m_instance = {}", (m_instance == nullptr)); + + vk_check( + glfwCreateWindowSurface( + m_instance, m_window_handle, nullptr, &m_window_surface), + "glfwCreateWindowSurface"); + + center_window(); + + m_window_swapchain = swapchain(m_window_surface, m_params); + + p_context->submit_resource_free([this](){ + console_log_info("vulkan::window_context submit_resource_free invokation!"); + m_window_swapchain.destroy(); + }); + } + + virtual ~window_context() { + if(m_window_surface != nullptr) { + vkDestroySurfaceKHR(m_instance, m_window_surface, nullptr); + } + glfwDestroyWindow(m_window_handle); + } + + + protected: + [[nodiscard]] window_params get_params() const override { + return m_window_swapchain.settings(); + } + + [[nodiscard]] GLFWwindow* native_window() const override { + return m_window_handle; + } + + [[nodiscard]] uint32_t read_acquired_next_frame() override { + return m_window_swapchain.read_acquired_image(); + } + + [[nodiscard]] vulkan::swapchain window_swapchain() const override{ + return m_window_swapchain; + } + + [[nodiscard]] vk::command_buffer current_active_command(uint32_t p_frame_idx) override { + return m_window_swapchain.active_command(p_frame_idx); + } + + void present_frame(const uint32_t& p_current_frame) override { + m_window_swapchain.present(p_current_frame); + } + + private: + void center_window() { + GLFWmonitor* monitor = glfwGetPrimaryMonitor(); + const GLFWvidmode* mode = glfwGetVideoMode(monitor); + uint32_t width = (mode->width / 2) - (m_params.width / 2); + uint32_t height = (mode->height / 2) - (m_params.height / 2); + glfwSetWindowPos(m_window_handle, static_cast(width), static_cast(height)); + } + + private: + GLFWwindow* m_window_handle=nullptr; + VkSurfaceKHR m_window_surface=nullptr; + window_params m_params; + VkInstance m_instance=nullptr; + swapchain m_window_swapchain; + }; + }; +}; \ No newline at end of file diff --git a/atlas/physics/components.hpp b/atlas/physics/components.hpp deleted file mode 100644 index e1301629..00000000 --- a/atlas/physics/components.hpp +++ /dev/null @@ -1,72 +0,0 @@ -#pragma once -#include - -namespace atlas { - /** - * @brief static is represented as fixed - */ - enum body_type : uint8_t { - fixed = 0, - kinematic = 1, - dynamic = 2, - bodynum, - }; - - /** - * @param non_moving is used for static objects that saves for not using the - * collider component - * @param moving is used for dynamic, kinematic, and character objects that - * will be used - */ - enum body_layer : uint8_t { - non_moving = 0, - moving = 1, - layer_num, - }; - - enum activation : uint8_t { activate, deactivate }; - - /** - * @brief physics body data-driven representative - * - * TODO: Add parameters for force, impulse, and torque - */ - struct physics_body { - glm::vec3 linear_velocity = glm::vec3(0.0); - glm::vec3 angular_velocity = glm::vec3(0.0f); - - glm::vec3 force = glm::vec3(0.0f); - glm::vec3 impulse = glm::vec3(0.0f); - glm::vec3 torque = glm::vec3(0.0f); - - float mass_factor = 1.0f; - glm::vec3 center_mass_position = glm::vec3(0.0f); - float linear_damping = 0.0f; - float angular_damping = 0.0f; - - float gravity_factor = 1.0f; - float friction = 0.8f; - float restitution = 0.2f; - - //! @brief body_type::fixed means this physics body is static - uint8_t body_movement_type = body_type::fixed; - - //! @brief body_layer (object layers) refer to the rules of the - //! collision system specified in JoltPhysics - uint8_t body_layer_type = body_layer::moving; - }; - - struct box_collider { - glm::vec3 half_extent = glm::vec3(0.5f); - }; - - struct capsule_collider { - float radius = 0.5f; - float half_height = 0.5f; - }; - - struct sphere_collider { - float radius = 0.5f; - }; - -}; \ No newline at end of file diff --git a/atlas/physics/physics_engine.cppm b/atlas/physics/physics_engine.cppm new file mode 100644 index 00000000..64385d8c --- /dev/null +++ b/atlas/physics/physics_engine.cppm @@ -0,0 +1,202 @@ +module; + +#include +#include + +export module atlas.physics.engine; + +import atlas.common; +import atlas.core.scene.components; +import atlas.core.event; +import atlas.drivers.physics_context; +import atlas.drivers.jolt_cpp.context; + +namespace atlas::physics { + + //! @brief initializes the physics backend. SHOULD have an API associated + //! with but for now, we assume we only have JoltPhysics as our only physics + //! backend + ref initialize_physics_context(event::bus& p_bus) { + return create_ref(p_bus); + } + + /** + * @brief The manager class for all physics engines. Manages the physics + * contexts and the collision engines. + * + */ + export class engine { + public: + // Required by maps but should not be used in anyother circumstance. + engine() = default; + engine(flecs::world& p_registry, + event::bus& p_bus) : m_registry(&p_registry), m_bus(&p_bus) { + m_physics_context = initialize_physics_context(*m_bus); + + // This may change, but for now we want to ensure that we only want to + // create a single physics body with a specific collider Rather then + // having multiple colliders be associated to a single physics body + // itself. + m_query_box_collider = + m_registry->query_builder() + .without() + .build(); + m_query_sphere_collider = + m_registry->query_builder() + .without() + .build(); + m_query_capsule_collider = + m_registry->query_builder() + .without() + .build(); + + m_physics_bodies = m_registry->query_builder().build(); + } + + /** + * @brief The runtime is specifically the way to start the physics, + * utilizing defined data from level_scene and eventually any scene to + * create a physics scene and manage physics step. + * + */ + void start() { + // At the start of every simulation we create physics bodies to run + // simulation on those physics bodies with the specific colliders + // associated with them + m_query_box_collider.each([this](flecs::entity p_entity, + transform& p_transform, + physics_body& p_body, + box_collider& p_collider) { + m_physics_context->add_box_collider( + p_entity.id(), &p_transform, &p_body, &p_collider); + }); + + m_query_sphere_collider.each([this](flecs::entity p_entity, + transform& p_transform, + physics_body& p_body, + sphere_collider& p_collider) { + m_physics_context->add_sphere_collider( + p_entity.id(), &p_transform, &p_body, &p_collider); + }); + + m_query_capsule_collider.each([this](flecs::entity p_entity, + transform& p_transform, + physics_body& p_body, + capsule_collider& p_collider) { + m_physics_context->add_capsule_collider( + p_entity.id(), &p_transform, &p_body, &p_collider); + }); + m_physics_context->prepare(); + } + + /** + * @brief Runs a single frame of the simulation. Should at minimum be + * called 60 fps. If below, it must be called twice. + * + */ + void update(float p_delta_time) { + using namespace JPH; + + m_physics_bodies.each( + [this](flecs::entity p_entity, physics_body& p_body) { + m_physics_context->set_force_and_torque( + p_entity.id(), p_body.force, p_body.torque); + m_physics_context->set_linear_velocity(p_entity.id(), + p_body.linear_velocity); + m_physics_context->set_angular_velocity(p_entity.id(), + p_body.angular_velocity); + m_physics_context->set_impulse(p_entity.id(), p_body.impulse); + }); + // This will ensure all physics bodies with which colliders they are + // associated with are update with the simulation, and their parameters + // are modified + m_physics_context->update(p_delta_time); + + m_query_box_collider.each([&](flecs::entity p_entity, + transform& p_transform, + physics_body& p_body, + box_collider&) { + // updating transform + transform t = m_physics_context->read_transform(p_entity.id()); + p_transform.position = t.position; + p_transform.rotation = t.rotation; + p_transform.quaternion = t.quaternion; + + // physics bodies parameters + auto body = m_physics_context->read_physics_body(p_entity.id()); + p_body.linear_damping = body.linear_damping; + p_body.linear_velocity = body.linear_velocity; + p_body.angular_velocity = body.angular_velocity; + p_body.gravity_factor = body.gravity_factor; + p_body.center_mass_position = body.center_mass_position; + p_body.friction = body.friction; + p_body.restitution = body.restitution; + p_body.angular_velocity = body.angular_velocity; + p_body.linear_velocity = body.linear_velocity; + }); + + // updating sphere collider + m_query_sphere_collider.each([this](flecs::entity p_entity, + transform& p_transform, + physics_body& p_body, + sphere_collider&) { + // updating transform + transform t = m_physics_context->read_transform(p_entity.id()); + p_transform.position = t.position; + p_transform.rotation = t.rotation; + p_transform.quaternion = t.quaternion; + + // updating physics body + auto body = m_physics_context->read_physics_body(p_entity.id()); + p_body.linear_damping = body.linear_damping; + p_body.linear_velocity = body.linear_velocity; + p_body.angular_velocity = body.angular_velocity; + p_body.gravity_factor = body.gravity_factor; + p_body.center_mass_position = body.center_mass_position; + p_body.friction = body.friction; + p_body.restitution = body.restitution; + }); + + // updating capsule collider + m_query_capsule_collider.each([this](flecs::entity p_entity, + transform& p_transform, + physics_body& p_body, + capsule_collider&) { + transform t = m_physics_context->read_transform(p_entity.id()); + p_transform.position = t.position; + p_transform.rotation = t.rotation; + p_transform.quaternion = t.quaternion; + + auto body = m_physics_context->read_physics_body(p_entity.id()); + p_body.linear_damping = body.linear_damping; + p_body.linear_velocity = body.linear_velocity; + p_body.angular_velocity = body.angular_velocity; + p_body.gravity_factor = body.gravity_factor; + p_body.center_mass_position = body.center_mass_position; + p_body.friction = body.friction; + p_body.restitution = body.restitution; + }); + } + + /** + * @brief Deletes all physics bodies and shapes. Preps itself for + * runtime to be called again. + * + */ + void stop() { + m_physics_context->destroy(); + } + + private: + flecs::world* m_registry; + ref m_physics_context; + flecs::query m_physics_bodies; + flecs::query + m_query_box_collider; + flecs::query + m_query_sphere_collider; + flecs::query + m_query_capsule_collider; + event::bus* m_bus; + }; +}; \ No newline at end of file diff --git a/atlas/physics/physics_engine.hpp b/atlas/physics/physics_engine.hpp deleted file mode 100644 index 9a67da5e..00000000 --- a/atlas/physics/physics_engine.hpp +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once -#include -#include -#include -#include - -namespace atlas::physics { - - /** - * @brief The manager class for all physics engines. Manages the physics - * contexts and the collision engines. - * - */ - class physics_engine { - public: - // Required by maps but should not be used in anyother circumstance. - physics_engine() = default; - physics_engine(const jolt_settings& p_settings, - flecs::world& p_registry, - event::event_bus& p_bus); - - /** - * @brief The runtime is specifically the way to start the physics, - * utilizing defined data from level_scene and eventually any scene to - * create a physics scene and manage physics step. - * - */ - void start(); - - /** - * @brief Runs a single frame of the simulation. Should at minimum be - * called 60 fps. If below, it must be called twice. - * - */ - void update(float p_delta_time); - - /** - * @brief Deletes all physics bodies and shapes. Preps itself for - * runtime to be called again. - * - */ - void stop(); - - private: - flecs::world* m_registry; - ref m_physics_context; - flecs::query m_physics_bodies; - flecs::query - m_query_box_collider; - flecs::query - m_query_sphere_collider; - flecs::query - m_query_capsule_collider; - event::event_bus* m_bus; - }; -}; \ No newline at end of file diff --git a/atlas/physics/math_types.hpp b/atlas/physics/types.cppm similarity index 77% rename from atlas/physics/math_types.hpp rename to atlas/physics/types.cppm index 2098a0f9..8640ab85 100644 --- a/atlas/physics/math_types.hpp +++ b/atlas/physics/types.cppm @@ -1,15 +1,42 @@ -#pragma once - -#include +module; #include - -/** - * @brief Types are still be filled out. When this is completed to_jph() can be - * removed. - * - */ - -namespace atlas::physics { +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include + +// jolt's math includes +#include +#include +#include + +export module atlas.physics.types; + +export namespace atlas::physics { template struct vector3; @@ -102,7 +129,6 @@ namespace atlas::physics { glm::dvec3 m_value; }; - // === VECTOR4 ADAPTER === template struct vector4; @@ -188,4 +214,4 @@ namespace atlas::physics { private: glm::mat4 m_value; }; -}; +}; \ No newline at end of file diff --git a/atlas/renderer/context_loader.cppm b/atlas/renderer/context_loader.cppm new file mode 100644 index 00000000..769f0a0d --- /dev/null +++ b/atlas/renderer/context_loader.cppm @@ -0,0 +1,28 @@ +module; + +#include +#include + +export module atlas.renderer.context_loader; + +import atlas.core.utilities; + +import atlas.drivers.graphics_context; + +import atlas.drivers.vulkan.instance_context; + + +export namespace atlas { + /** + * @brief construct a new graphics context and initializes that API + * @return shared_ptr + */ + ref initialize_context(const std::string& p_name, graphics_api p_api) { + switch(p_api) { + case graphics_api::vulkan: + return create_ref(p_name); + default: + return nullptr; + } + } +}; \ No newline at end of file diff --git a/atlas/renderer/renderer.cppm b/atlas/renderer/renderer.cppm new file mode 100644 index 00000000..9fd6fd12 --- /dev/null +++ b/atlas/renderer/renderer.cppm @@ -0,0 +1,25 @@ +module; + +#include +#include + +export module atlas.renderer; + +import atlas.common; +import atlas.drivers.renderer_system; +import atlas.drivers.vulkan.render_system; +import atlas.common; +import atlas.graphics_api; +import atlas.drivers.graphics_context; +import atlas.core.utilities.types; + +export namespace atlas { + ref initialize_renderer(ref p_context, graphics_api p_api, const window_params& p_window_extent, uint32_t p_image_size, const std::string& p_name) { + switch(p_api) { + case graphics_api::vulkan: + return create_ref(p_context, p_window_extent, p_image_size, p_name); + default: + return nullptr; + } + } +}; \ No newline at end of file diff --git a/atlas/renderer/renderer.hpp b/atlas/renderer/renderer.hpp deleted file mode 100644 index aef247d5..00000000 --- a/atlas/renderer/renderer.hpp +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once -#include -#include -#include -#include - -namespace atlas { - /** - * @name Renderer - * @brief Submits high-level tasks that may get executed based on - * specifications for workloads - * @brief Provides API's for direct immediate execution to the backend - * context - * @brief Direct API's for performing certain rendering optimizations - * implementations - */ - class renderer { - public: - renderer() = default; - renderer(const window_settings& p_window_extent, - uint32_t p_image_size, - const std::string& p_tag = "Renderer"); - - void preload(const VkRenderPass& p_renderpass); - - void current_scene(ref); - - /** - * @brief Indicates to the renderer is at the start of the next frame to - * prepare workloads before next frame is processeed - */ - void begin(const ::vk::command_buffer& p_current, - const window_settings& p_settings, - const VkRenderPass& p_renderpass, - const VkFramebuffer& p_framebuffer, - const glm::mat4& p_proj_view); - - /** - * @brief Indications when the renderer has reached the end of the frame - */ - void end(); - - void set_background_color(const std::array& p_color); - - private: - ref m_render_context = nullptr; - }; -}; diff --git a/atlas/renderer/uniforms.hpp b/atlas/renderer/uniforms.hpp deleted file mode 100644 index 087d54ec..00000000 --- a/atlas/renderer/uniforms.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once -#include -#include - -namespace atlas { - - struct point_light_ubo { - glm::vec4 position; // this is provided by the transform - glm::vec4 color = { 1.f, 1.f, 1.f, 1.f }; - float attenuation = 1.f; - float constant = 1.f; - float linear = 1.f; - float quadratic = 1.f; - - glm::vec4 ambient = glm::vec4(1.f); - glm::vec4 diffuse = glm::vec4(1.f); - glm::vec4 specular = glm::vec4(1.f); - }; - - struct light_scene_ubo { - alignas(16) uint32_t num_lights; - // alignas(16) std::array light_sources; - alignas(16) point_light_ubo light_sources[10]; - }; -}; \ No newline at end of file diff --git a/builtin.shaders/compile.bat b/builtin.shaders/compile.bat new file mode 100644 index 00000000..d6fd90fe --- /dev/null +++ b/builtin.shaders/compile.bat @@ -0,0 +1,4 @@ +glslc.exe builtin.shaders/test.vert -o builtin.shaders/test.vert.spv +glslc.exe builtin.shaders/test.frag -o builtin.shaders/test.frag.spv + +echo Finished Compiling GLSL Shaders \ No newline at end of file diff --git a/experimental-shaders/test.frag b/builtin.shaders/test.frag similarity index 100% rename from experimental-shaders/test.frag rename to builtin.shaders/test.frag diff --git a/builtin.shaders/test.frag.spv b/builtin.shaders/test.frag.spv new file mode 100644 index 00000000..d3341913 Binary files /dev/null and b/builtin.shaders/test.frag.spv differ diff --git a/experimental-shaders/test.vert b/builtin.shaders/test.vert similarity index 100% rename from experimental-shaders/test.vert rename to builtin.shaders/test.vert diff --git a/builtin.shaders/test.vert.spv b/builtin.shaders/test.vert.spv new file mode 100644 index 00000000..e7b11cca Binary files /dev/null and b/builtin.shaders/test.vert.spv differ diff --git a/conanfile.py b/conanfile.py index e3006948..6cf03b46 100644 --- a/conanfile.py +++ b/conanfile.py @@ -2,11 +2,13 @@ from conan.tools.cmake import CMakeToolchain, CMake, cmake_layout, CMakeDeps from conan.tools.files import copy import os +from pathlib import Path import shutil +from conan.tools.files import save class AtlasRecipe(ConanFile): name = "atlas" - version = "0.4" + version = "0.5" package_type = "library" license = "Apache-2.0" homepage = "https://github.com/engine3d-dev/TheAtlasEngine" @@ -18,19 +20,19 @@ class AtlasRecipe(ConanFile): # Specifying our build_type is only Debug and Release options = {"shared": [True, False], "fPIC": [True, False], "enable_tests_only": [True, False], "enable_shaderc": [True, False]} - default_options = {"shared": False, "fPIC": True, "enable_tests_only": True, "enable_shaderc": True} - + default_options = {"shared": False, "fPIC": True, "enable_tests_only": True, "enable_shaderc": False} + exports_sources = "atlas/*", "tests/*", "CMakeLists.txt", "LICENSE" def build_requirements(self): - self.tool_requires("make/4.4.1") self.tool_requires("cmake/4.1.1") - self.tool_requires("engine3d-cmake-utils/4.0") + self.tool_requires("ninja/1.13.1") + self.tool_requires("engine3d-cmake-utils/5.0") def requirements(self): self.requires("joltphysics/5.2.0") if self.options.enable_shaderc: self.requires("shaderc/2025.3") - self.requires("imguidocking/2.0") + self.requires("imguidocking/3.0") self.requires("flecs/4.0.4") self.requires("glfw/3.4") self.requires("spdlog/1.16.0") @@ -38,23 +40,13 @@ def requirements(self): self.requires("yaml-cpp/0.8.0") # Vulkan-related headers and includes packages - self.requires("vulkan-headers/1.3.290.0") - self.requires("vulkan-cpp/3.0") + self.requires("vulkan-cpp/4.0") self.requires("tinyobjloader/2.0.0-rc10") self.requires("stb/cci.20230920") - self.requires("nfd/1.0") + self.requires("nfd/3.0") self.requires("watcher/0.12.0") self.requires("boost-ext-ut/2.3.1") - - def export_sources(self): - copy(self,"CMakeLists.txt", self.recipe_folder, self.export_sources_folder) - copy(self,"src/CMakeLists.txt", self.recipe_folder, self.export_sources_folder) - copy(self,"*.hpp", self.recipe_folder, self.export_sources_folder) - copy(self,"*.h", self.recipe_folder, self.export_sources_folder) - copy(self,"*.h", self.recipe_folder, self.export_sources_folder) - copy(self,"*.cpp", self.recipe_folder, self.export_sources_folder) - copy(self, "shader_ubo_tutorial", self.recipe_folder, self.export_sources_folder) def config_options(self): if self.settings.os == "Windows": @@ -72,13 +64,23 @@ def layout(self): cmake_layout(self) def generate(self): + llvm_path = self.dependencies.build["llvm-toolchain"].package_folder + cmake_path = self.dependencies.build["cmake"].package_folder + cmake_binary_location = f"{cmake_path.replace("\\", "/")}/bin/cmake.exe" + clang_format_path = f"{llvm_path}/bin/clang-format.exe" + clang_tidy_path = f"{llvm_path}/bin/clang-tidy.exe" + deps = CMakeDeps(self) deps.generate() tc = CMakeToolchain(self) # TODO: Remove this once fixing shaderc issue in the CI is resolved # These are options that can be enabled/disabled with the `-o` parameter when compiling with `conan` command + tc.generator = "Ninja" tc.variables["USE_SHADERC"] = self.options.enable_shaderc tc.variables["ENABLE_TESTS_ONLY"] = self.options.enable_tests_only + tc.variables["CLANG_FORMAT_PATH"] = clang_format_path.replace("\\", "/") + tc.variables["CLANG_TIDY_PATH"] = clang_tidy_path.replace("\\", "/") + tc.variables["CMAKE_PATH"] = cmake_binary_location tc.generate() def build(self): @@ -106,17 +108,15 @@ def build(self): def package(self): - copy(self, "LICENSE", src=self.source_folder, dst=os.path.join(self.package_folder, "licenses")) - copy(self, pattern="*.hpp", src=os.path.join(self.source_folder, "atlas"), dst=os.path.join(self.package_folder, "atlas")) - copy(self, pattern="*.a", src=self.build_folder, dst=os.path.join(self.package_folder, "lib"), keep_path=False) - copy(self, pattern="*.so", src=self.build_folder, dst=os.path.join(self.package_folder, "lib"), keep_path=False) - copy(self, pattern="*.lib", src=self.build_folder, dst=os.path.join(self.package_folder, "lib"), keep_path=False) - copy(self, pattern="*.dll", src=self.build_folder, dst=os.path.join(self.package_folder, "bin"), keep_path=False) - copy(self, pattern="*.dylib", src=self.build_folder, dst=os.path.join(self.package_folder, "lib"), keep_path=False) cmake = CMake(self) cmake.install() + copy(self, "LICENSE", + dst=Path(self.package_folder) / "licenses", + src=self.source_folder) + def package_info(self): - self.cpp_info.set_property("cmake_target_name", "atlas::atlas") - self.cpp_info.libs = ["atlas"] - self.cpp_info.includedirs = ['./', './atlas'] # Ordered list of include paths + # DISABLE Conan's config file generation + self.cpp_info.set_property("cmake_find_mode", "none") + # Tell CMake to include this directory in its search path + self.cpp_info.builddirs.append("lib/cmake") diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index ccd36417..520e5015 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -1,16 +1,37 @@ cmake_minimum_required(VERSION 3.27) project(editor CXX) -build_application( - SOURCES - application.cpp +# build_application( +# SOURCES +# application.cpp - editor_world.hpp - editor_world.cpp +# PACKAGES +# glfw3 +# Vulkan +# glm +# spdlog - level_scene.hpp - level_scene.cpp - LINK_PACKAGES - atlas +# LINK_PACKAGES +# atlas +# glfw +# Vulkan::Vulkan +# glm::glm +# spdlog::spdlog +# ) + +add_executable(${PROJECT_NAME} application.cpp) + +find_package(glfw3 REQUIRED) +find_package(Vulkan REQUIRED) +find_package(glm REQUIRED) +find_package(spdlog REQUIRED) +target_link_libraries(${PROJECT_NAME} PUBLIC glfw Vulkan::Vulkan spdlog::spdlog glm::glm atlas) + +target_sources(${PROJECT_NAME} PUBLIC + FILE_SET CXX_MODULES + TYPE CXX_MODULES + FILES + editor_world.cppm + level_scene.cppm ) \ No newline at end of file diff --git a/editor/application.cpp b/editor/application.cpp index 1c23ddac..ede95a60 100644 --- a/editor/application.cpp +++ b/editor/application.cpp @@ -1,43 +1,38 @@ -#include -#include "editor_world.hpp" -#include -#include +import atlas.application; +import atlas.common; -/** - * This represents our editor's application - * - * TODO: Add a preload API to preload the application. For providing user - * preferences, project settings, or other configuration settings that are - * projects-related - */ -class editor_application : public atlas::application { +import editor_world; +import atlas.core.scene.system_registry; + +import atlas.drivers.graphics_context; + +class test_application : public atlas::application { public: - editor_application(const atlas::application_settings& p_settings) - : application(p_settings) { - std::pmr::monotonic_buffer_resource resource{ 4096 }; - m_allocator.construct(&resource); + test_application(atlas::ref p_context, + const atlas::application_settings& p_settings) + : atlas::application(p_context, p_settings) { + // atlas::register_update(this, + // &test_application::on_update);std::pmr::monotonic_buffer_resource + // resource{ 4096 }; m_allocator.construct(&resource); // TODO -- this is going to be changed with the use of the level // streamer API - m_world = atlas::create_strong_ref( - m_allocator, "Editor World", renderer_instance()); + m_world = + atlas::create_ref("Editor World", renderer_instance()); } private: - std::pmr::polymorphic_allocator m_allocator; - atlas::optional_ref m_world; + // std::pmr::polymorphic_allocator m_allocator; + atlas::ref m_world; }; -namespace atlas { - ref initialize_application() { - application_settings settings = { - .name = "Editor", - .width = 1200, - .height = 800, - .background_color = { 0.f, 0.f, 0.f, 0.f }, - }; - - return create_ref(settings); - } - -} // end of namespace atlas \ No newline at end of file +atlas::ref +initialize_application(atlas::ref p_contetxt) { + atlas::application_settings settings = { + .name = "Editor", + .width = 1200, + .height = 800, + .background_color = { 0.f, 0.f, 0.f, 0.f }, + }; + return create_ref(p_contetxt, settings); +} \ No newline at end of file diff --git a/editor/conanfile.py b/editor/conanfile.py deleted file mode 100644 index e7480010..00000000 --- a/editor/conanfile.py +++ /dev/null @@ -1,33 +0,0 @@ -from conan import ConanFile -from conan.tools.cmake import CMake, cmake_layout - -class GameDemo(ConanFile): - name = "game-demo" - version = "1.0" - settings = "os", "compiler", "build_type", "arch" - generators = "CMakeDeps", "CMakeToolchain" - export_source = "CMakeLists.txt", "application.cpp" - - # Putting all of your build-related dependencies here - def build_requirements(self): - self.tool_requires("make/4.4.1") - self.tool_requires("cmake/3.27.1") - self.tool_requires("engine3d-cmake-utils/4.0") - - # Putting all of your packages here - # To build engine3d/1.0 locally do the following: - # conan create . --name=engine3d --version=0.1.0 --user=local --channel=12345 - def requirements(self): - self.requires("atlas/0.1") - - def build(self): - cmake = CMake(self) - cmake.configure() - cmake.build() - - def package(self): - cmake = CMake(self) - cmake.install() - - def layout(self): - cmake_layout(self) \ No newline at end of file diff --git a/editor/editor_world.cpp b/editor/editor_world.cpp deleted file mode 100644 index f25e936c..00000000 --- a/editor/editor_world.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "editor_world.hpp" -#include -#include -#include "level_scene.hpp" - -editor_world::editor_world( - const std::string& p_tag, - /*NOLINT*/ atlas::ref p_renderer_instance) - : m_renderer(/*NOLINT*/ p_renderer_instance) { - m_main_world = atlas::system_registry::create_world(p_tag); - - m_bus.create_listener(); - m_bus.create_listener(); - m_bus.create_listener(); - - atlas::ref first_scene = - atlas::create_ref("LevelScene", m_bus); - m_renderer->current_scene(first_scene); - m_main_world->add_scene(first_scene); -} \ No newline at end of file diff --git a/editor/editor_world.cppm b/editor/editor_world.cppm new file mode 100644 index 00000000..9e8929ab --- /dev/null +++ b/editor/editor_world.cppm @@ -0,0 +1,39 @@ +module; + +#include + +export module editor_world; + +// import atlas.common; +// import atlas.logger; +import atlas.core.utilities; +import atlas.core.scene.world; +// import atlas.core.event.bus; +// import atlas.core.event.types; +import atlas.core.event; +import atlas.core.scene.system_registry; +import atlas.drivers.renderer_system; +import level_scene; + +export class editor_world { +public: + editor_world(const std::string& p_tag, atlas::ref p_renderer_instance) : m_renderer(p_renderer_instance) { + m_main_world = atlas::system_registry::create_world(p_tag); + + m_bus.create_listener(); + m_bus.create_listener(); + m_bus.create_listener(); + + atlas::ref first_scene = atlas::create_ref("LevelScene", m_bus); + m_renderer->current_scene_context(first_scene); + m_main_world->add_scene(first_scene); + + console_log_error("editor_world initialized successfully!~!!"); + } + +private: + atlas::ref m_main_world; + + atlas::event::bus m_bus; + atlas::ref m_renderer; +}; \ No newline at end of file diff --git a/editor/editor_world.hpp b/editor/editor_world.hpp deleted file mode 100644 index 50a06310..00000000 --- a/editor/editor_world.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once -#include -#include -#include -#include - -class editor_world { -public: - editor_world(const std::string& p_tag, - atlas::ref p_renderer_instance); - -private: - atlas::ref m_main_world; - - atlas::event::event_bus m_bus; - atlas::ref m_renderer; -}; \ No newline at end of file diff --git a/editor/level_scene.cpp b/editor/level_scene.cpp deleted file mode 100644 index edd96f46..00000000 --- a/editor/level_scene.cpp +++ /dev/null @@ -1,728 +0,0 @@ -#include "level_scene.hpp" -#include -#include -#include -#include -#include -#include - -level_scene::level_scene(const std::string& p_name, - atlas::event::event_bus& p_bus) - : atlas::scene(p_name, p_bus) { - auto editor_camera = entity("Editor Camera"); - editor_camera - .add>(); - editor_camera.set({ - .position = { 3.50f, 4.90f, 36.40f }, - .scale{ 1.f }, - }); - editor_camera.set({ - .plane = { 0.1f, 5000.f }, - .is_active = true, - .field_of_view = 45.f, - }); - - atlas::game_object bob_object = entity("Bob"); - - bob_object.add(); - - atlas::game_object viking_room = entity("Viking Room"); - viking_room.add(); - viking_room.set({ - .position = { -2.70f, 2.70, -8.30f }, - .rotation = { 2.30f, 95.90f, 91.80f }, - .scale{ 1.f }, - }); - - viking_room.set({ - .radius = 1.0f, - }); - - viking_room.set({ - .friction = 15.f, - .restitution = 0.3f, - .body_movement_type = atlas::dynamic, - }); - - atlas::game_object cube = entity("Aircraft"); - - cube.set({ - .position = { 0.f, 2.10f, -7.30f }, - .scale = { 0.9f, 0.9f, 0.9f }, - }); - - cube.set({ - .color = { 1.f, 1.f, 1.f, 1.f }, - // .model_path = "assets/models/E 45 Aircraft_obj.obj", - .model_path = "assets/backpack/backpack.obj", - .diffuse = "assets/backpack/diffuse.jpg", - .specular = "assets/backpack/specular.jpg" - // .diffuse = "assets/models/E-45-steel detail_2_col.jpg", - }); - - atlas::game_object robot_model = entity("Cube"); - robot_model.add(); - // robot_model.add(); - robot_model.set({ - .position = { -2.70, 3.50f, 4.10f }, - .scale = { 1.f, 1.f, 1.f }, - }); - - robot_model.set( - { .color = { 1.f, 1.f, 1.f, 1.f }, - .model_path = "assets/models/cube.obj", - .diffuse = "assets/models/container_diffuse.png", - .specular = "assets/models/container_specular.png" }); - - robot_model.set({ - .half_extent = { 1.f, 1.f, 1.f }, - }); - robot_model.set({ - // .restitution = 1.f, - .body_movement_type = atlas::dynamic, - }); - - atlas::game_object platform = entity("Platform"); - - platform.set({ - .scale = { 15.f, 0.30f, 10.0f }, - }); - platform.set({ - .model_path = "assets/models/cube.obj", - .diffuse = "assets/models/wood.png", - }); - platform.set({ - .body_movement_type = atlas::fixed, - }); - platform.set({ - .half_extent = { 15.f, 0.30f, 10.0f }, - }); - - atlas::game_object point_light = entity("Point Light 1"); - point_light.set({ - .position = { 0.f, 2.10f, -7.30f }, - .scale = { 0.9f, 0.9f, 0.9f }, - }); - - point_light.set({ - .model_path = "assets/models/cube.obj", - .diffuse = "assets/models/wood.png", - }); - point_light.add(); - - // benchmark - - // auto start = std::chrono::high_resolution_clock::now(); - // TEMP Code - // [[maybe_unused]] atlas::game_object point_light_test = entity("Point - // Light 1"); auto end = std::chrono::high_resolution_clock::now(); auto - // duration = (end - start); - - // auto seconds = - // std::chrono::duration_cast(duration).count(); auto - // nanoseconds = - // std::chrono::duration_cast(duration).count(); - // auto microseconds = - // std::chrono::duration_cast(duration).count(); - - // console_log_fatal("Seconds = {:.1f}", static_cast(seconds)); - // console_log_fatal("Nanoseconds = {:.1f}", - // static_cast(nanoseconds)); console_log_fatal("Microseconds = - // {:.1f}", static_cast(microseconds)); - - // for(size_t i = 0; i < 26; i++) { - // auto obj = entity(std::format("Object #{}", i)); - // obj->set({ - // .restitution = 1.25f, - // .body_movement_type = atlas::dynamic, - // }); - - // obj->set( - // { - // .radius = 1.0f, - // }); - - // glm::vec3 pos = {float(0*1.4),float(0 * 1.4),float(0 * -3) }; - - // obj->set({ - // .position = pos, - // .rotation = {.3f, 0.0f, 0.0f}, - // }); - - // obj->set({ - // .model_path = "assets/models/Ball OBJ.obj", - // .diffuse = "assets/models/clear.png", - // }); - // } - - atlas::game_object gerald = entity("Gerald"); - gerald.add(); - - // TODO: Move this outside of level_scene - m_deserializer_test = atlas::serializer(); - - subscribe(this, - &level_scene::collision_enter); - - atlas::register_start(this, &level_scene::start); - atlas::register_physics(this, &level_scene::physics_update); - atlas::register_update(this, &level_scene::on_update); - atlas::register_ui(this, &level_scene::on_ui_update); -} - -void -level_scene::collision_enter(atlas::event::collision_enter& p_event) { - console_log_warn("collision_enter event!!!"); - atlas::game_object e1 = entity(p_event.entity1); - atlas::game_object e2 = entity(p_event.entity2); - - console_log_warn("Entity1 = {}", e1.name().c_str()); - console_log_warn("Entity2 = {}", e2.name().c_str()); -} - -void -level_scene::collision_persisted(atlas::event::collision_persisted& p_event) { - console_log_warn("collision_persisted(p_event) invoked!!"); - atlas::game_object e1 = entity(p_event.entity1); - atlas::game_object e2 = entity(p_event.entity2); - - console_log_warn("Entity1 = {}", e1.name().c_str()); - console_log_warn("Entity2 = {}", e2.name().c_str()); -} - -void -level_scene::runtime_start() { - // runs the physics simulation - m_physics_runtime = true; - - m_physics_engine.start(); -} - -void -level_scene::runtime_stop() { - m_physics_runtime = false; - - m_physics_engine.stop(); - - reset_objects(); -} - -void -level_scene::reset_objects() { - - if (!m_deserializer_test.load("LevelScene", *this)) { - console_log_error("Could not load yaml file LevelScene!!!"); - } -} - -void -ui_component_list(flecs::entity& p_selected_entity) { - std::string entity_name = p_selected_entity.name().c_str(); - std::string new_entity_name = ""; - atlas::ui::draw_input_text(new_entity_name, entity_name); - - p_selected_entity.set_name(new_entity_name.c_str()); - - ImGui::SameLine(); - ImGui::PushItemWidth(-1); - if (ImGui::Button("Add Component")) { - ImGui::OpenPopup("Add Component"); - } - - if (ImGui::BeginPopup("Add Component")) { - if (!p_selected_entity.has()) { - if (ImGui::MenuItem("Perspective Camera")) { - p_selected_entity.add< - flecs::pair>(); - p_selected_entity.add(); - ImGui::CloseCurrentPopup(); - } - } - - if (!p_selected_entity.has()) { - if (ImGui::MenuItem("Mesh Source")) { - p_selected_entity.add(); - ImGui::CloseCurrentPopup(); - } - } - - if (!p_selected_entity.has()) { - if (ImGui::MenuItem("Point Light")) { - p_selected_entity.add(); - ImGui::CloseCurrentPopup(); - } - } - - if (!p_selected_entity.has()) { - if (ImGui::MenuItem("Serialize")) { - p_selected_entity.add(); - ImGui::CloseCurrentPopup(); - } - } - - if (!p_selected_entity.has()) { - if (ImGui::MenuItem("Physics Body")) { - p_selected_entity.add(); - ImGui::CloseCurrentPopup(); - } - } - - if (!p_selected_entity.has()) { - if (ImGui::MenuItem("Box Collider")) { - p_selected_entity.add(); - ImGui::CloseCurrentPopup(); - } - } - - if (!p_selected_entity.has()) { - if (ImGui::MenuItem("Sphere Collider")) { - p_selected_entity.add(); - ImGui::CloseCurrentPopup(); - } - } - - if (!p_selected_entity.has()) { - if (ImGui::MenuItem("Capsule Collider")) { - p_selected_entity.add(); - ImGui::CloseCurrentPopup(); - } - } - ImGui::EndPopup(); - } - - ImGui::PopItemWidth(); -} - -void -level_scene::on_ui_update() { - - // setting up the dockspace UI widgets at the window toolbar - m_editor_dockspace.begin(); - - try { - m_editor_menu.begin(); - } - catch (const atlas::ui::menu_bar_exception& e) { - } - - if (ImGui::BeginMenu("File")) { - if (ImGui::MenuItem("Save")) { - // m_deserializer_test.save("LevelScene"); - } - - ImGui::Separator(); - - if (ImGui::MenuItem("Exit")) { - glfwSetWindowShouldClose(atlas::application::get_window(), true); - } - - ImGui::EndMenu(); - } - - m_editor_menu.end(); - - if (ImGui::Begin("Viewport")) { - glm::vec2 viewport_panel_size = - glm::vec2{ atlas::application::get_window().width(), - atlas::application::get_window().height() }; - - ImGui::End(); - } - - defer_begin(); - auto query_builder = this->query_builder().build(); - - if (ImGui::Begin("Scene Heirarchy")) { - // @note right click on blank space - // @param string_id - // @param popup_flags - will be the mouse flag (0=right, 1=left) - if (atlas::ui::begin_popup_context_window(nullptr, 1, false)) { - if (ImGui::MenuItem("Create Empty Entity")) { - m_current_entity = entity("Empty Entity"); - } - ImGui::EndPopup(); - } - - query_builder.each([&](flecs::entity p_entity, atlas::transform&) { - // We set the imgui flags for our scene heirarchy panel - // TODO -- Make the scene heirarchy panel a separate class that is - // used for specify the layout and other UI elements here - ImGuiTreeNodeFlags flags = - ((m_selected_entity == p_entity) ? ImGuiTreeNodeFlags_Selected - : 0) | - ImGuiTreeNodeFlags_OpenOnArrow; - flags |= ImGuiTreeNodeFlags_SpanAvailWidth; - flags |= ImGuiWindowFlags_Popup; - flags |= ImGuiTreeNodeFlags_AllowItemOverlap; - - bool opened = ImGui::TreeNodeEx(p_entity.name().c_str(), flags); - - if (ImGui::IsItemClicked()) { - m_selected_entity = p_entity; - } - - bool delete_entity = false; - if (ImGui::BeginPopupContextItem()) { - if (ImGui::MenuItem("Delete Entity")) { - delete_entity = true; - } - ImGui::EndPopup(); - } - - if (delete_entity) { - m_selected_entity.destruct(); - } - - ImGui::SameLine(); - ImGui::TextDisabled("(%llu)", p_entity.id()); - - if (opened) { - flags = ImGuiTreeNodeFlags_OpenOnArrow | - ImGuiTreeNodeFlags_SpanAvailWidth; - auto query_children_builder = - this->query_builder().with(flecs::ChildOf, p_entity).build(); - int32_t child_count = query_children_builder.count(); - - // // Only show children in scene heirarchy panel if there are - // children entities - if (child_count > 0) { - m_selected_entity.children([&](flecs::entity p_child) { - opened = - ImGui::TreeNodeEx(p_child.name().c_str(), flags); - if (opened) { - if (ImGui::IsItemClicked()) { - m_selected_entity = p_child; - } - ImGui::TreePop(); - } - }); - } - - ImGui::TreePop(); - } - }); - - defer_end(); - ImGui::End(); - } - - if (ImGui::Begin("Properties")) { - if (m_selected_entity.is_alive()) { - ui_component_list(m_selected_entity); - - atlas::ui::draw_component( - "transform", - m_selected_entity, - [](atlas::transform* p_transform) { - atlas::ui::draw_vec3("Position", p_transform->position); - atlas::ui::draw_vec3("Scale", p_transform->scale); - atlas::ui::draw_vec3("Rotation", p_transform->rotation); - }); - - atlas::ui::draw_component( - "camera", - m_selected_entity, - [this](atlas::perspective_camera* p_camera) { - atlas::ui::draw_float("field of view", - p_camera->field_of_view); - ImGui::Checkbox("is_active", &p_camera->is_active); - ImGui::DragFloat("Speed", &m_movement_speed); - }); - - atlas::ui::draw_component( - "atlas::mesh_source", - m_selected_entity, - [](atlas::mesh_source* p_source) { - std::string mesh_src = p_source->model_path; - atlas::ui::draw_input_text(p_source->model_path, mesh_src); - atlas::ui::draw_vec4("Color", p_source->color); - }); - - atlas::ui::draw_component( - "material", - m_selected_entity, - [](atlas::material_metadata* p_source) { - float speed = 0.01f; - ImGui::DragFloat4( - "Ambient", glm::value_ptr(p_source->ambient), speed); - ImGui::DragFloat4( - "Diffuse", glm::value_ptr(p_source->diffuse), speed); - ImGui::DragFloat4( - "Specular", glm::value_ptr(p_source->specular), speed); - atlas::ui::draw_float("Shininess", p_source->shininess); - }); - - /* - atlas::ui::draw_component("Directional - Light", m_selected_entity, [](atlas::directional_light* - p_dir_light){ ImGui::DragFloat4("Direction", - glm::value_ptr(p_dir_light->direction)); ImGui::DragFloat4("View - Pos", glm::value_ptr(p_dir_light->view_position)); - ImGui::DragFloat4("Color", glm::value_ptr(p_dir_light->color)); - ImGui::DragFloat4("Ambient", - glm::value_ptr(p_dir_light->ambient)); ImGui::DragFloat4("Diffuse", - glm::value_ptr(p_dir_light->diffuse)); ImGui::DragFloat4("Specular", - glm::value_ptr(p_dir_light->specular)); - }); - */ - - atlas::ui::draw_component( - "Point Light", - m_selected_entity, - [](atlas::point_light* p_dir_light) { - ImGui::DragFloat4( - "Color", glm::value_ptr(p_dir_light->color), 0.01); - ImGui::DragFloat( - "Attenuation", &p_dir_light->attenuation, 0.001); - ImGui::DragFloat4( - "Ambient", glm::value_ptr(p_dir_light->ambient), 0.01); - ImGui::DragFloat4( - "Diffuse", glm::value_ptr(p_dir_light->diffuse), 0.01); - ImGui::DragFloat4( - "Specular", glm::value_ptr(p_dir_light->specular), 0.01); - ImGui::DragFloat("Constant", &p_dir_light->constant, 0.01); - ImGui::DragFloat("Linear", &p_dir_light->linear, 0.01); - ImGui::DragFloat("Quadratic", &p_dir_light->quadratic, 0.01); - }); - - atlas::ui::draw_component( - "Physics Body", - m_selected_entity, - [](atlas::physics_body* p_body) { - std::array items = { - "Static", - "Kinematic", - "Dynamic", - }; - std::string combo_preview = items[p_body->body_movement_type]; - - // Begin the combo box - if (ImGui::BeginCombo("Body Type", combo_preview.data())) { - for (int n = 0; n < 3; n++) { - // Check if the current item is selected - const bool is_selected = - (p_body->body_movement_type == n); - if (ImGui::Selectable(items[n].data(), is_selected)) { - // Update the current type when a new item is - // selected - p_body->body_movement_type = - static_cast(n); - } - - // Set the initial focus when the combo box is first - // opened - if (is_selected) { - ImGui::SetItemDefaultFocus(); - } - } - ImGui::EndCombo(); - } - - // physics body parameters - atlas::ui::draw_vec3("Linear Velocity", - p_body->linear_velocity); - atlas::ui::draw_vec3("Angular Velocity", - p_body->angular_velocity); - atlas::ui::draw_vec3("Force", p_body->force); - atlas::ui::draw_vec3("Impulse", p_body->impulse); - atlas::ui::draw_vec3("Torque", p_body->torque); - atlas::ui::draw_vec3("Center Mass", - p_body->center_mass_position); - }); - - atlas::ui::draw_component( - "Box Collider", - m_selected_entity, - [](atlas::box_collider* p_collider) { - atlas::ui::draw_vec3("Half Extent", p_collider->half_extent); - }); - - atlas::ui::draw_component( - "Box Collider", - m_selected_entity, - [](atlas::sphere_collider* p_collider) { - atlas::ui::draw_float("Radius", p_collider->radius); - }); - - atlas::ui::draw_component( - "Box Collider", - m_selected_entity, - [](atlas::capsule_collider* p_collider) { - atlas::ui::draw_float("Half Height", p_collider->half_height); - atlas::ui::draw_float("Radius", p_collider->radius); - }); - - atlas::ui::draw_component( - "Serialize", - m_selected_entity, - [](atlas::tag::serialize* p_serialize) { - ImGui::Checkbox("Enable", &p_serialize->enable); - }); - } - - ImGui::End(); - - // Note --- just added this temporarily for testing - // auto time = atlas::application::delta_time(); - - // if((int)(time * 10.0f) % 8 > 4) { - // m_blink = !m_blink; - // } - - // auto width = atlas::application::get_window().width(); - // auto height = atlas::application::get_window().height(); - - // ImGui::SetNextWindowPos(ImVec2(static_cast(width) * 0.5f, - // static_cast(height) * 0.5f), ImGuiCond_Always, ImVec2(0.5f, - // 0.5f)); ImGui::SetNextWindowSize(ImVec2(200, 20), ImGuiCond_Always); - // ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration | - // ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoInputs; - // ImGui::SetNextWindowBgAlpha(0.f); - - // if(ImGui::Begin("Testing", nullptr, flags)) { - // ImGui::ProgressBar(10.f); - - // auto pos = ImGui::GetWindowPos(); - // pos.x += (float)width * 0.5f - 300.0f; - // pos.y += 50.0f; - // if(m_blink){ - // ImGui::GetForegroundDrawList()->AddText(m_font, 120.0f, - // pos, 0xffffffff, "Click to Play!"); - // } - - // ImGui::End(); - // } - } - - m_editor_dockspace.end(); -} - -void -level_scene::start() { - m_deserializer_test = atlas::serializer(); - - if (!m_deserializer_test.load("LevelScene", *this)) { - console_log_error("Could not load yaml file LevelScene!!!"); - } - - atlas::game_object viking_room = entity("Viking Room"); - atlas::mesh_source* src = viking_room.get_mut(); - src->flip = true; - - // Initiating physics system - atlas::physics::jolt_settings settings = {}; - flecs::world registry = *this; - m_physics_engine = - atlas::physics::physics_engine(settings, registry, *event_handle()); - - // Note -- just added for temporary - // ImGuiIO io = ImGui::GetIO(); - // m_font = io.Fonts->AddFontFromFileTTF("assets/OpenSans-Regular.ttf", - // 120.0f); -} - -void -level_scene::on_update() { - - auto query_cameras = - query_builder().build(); - - query_cameras.each([this](atlas::perspective_camera& p_camera, - atlas::transform& p_transform) { - if (!p_camera.is_active) { - return; - } - - float dt = atlas::application::delta_time(); - float default_speed = 10.f; // current default movement speed that does - // not applied modified speed - float rotation_speed = 1.f; - float velocity = default_speed * dt; - if (atlas::event::is_mouse_pressed(mouse_button_middle)) { - velocity = m_movement_speed * dt; - } - float rotation_velocity = rotation_speed * dt; - - glm::quat to_quaternion = atlas::to_quat(p_transform.quaternion); - - glm::vec3 up = glm::rotate(to_quaternion, atlas::math::up()); - glm::vec3 forward = glm::rotate(to_quaternion, atlas::math::backward()); - glm::vec3 right = glm::rotate(to_quaternion, atlas::math::right()); - - if (atlas::event::is_key_pressed(key_left_shift)) { - p_transform.position += up * velocity; - } - - if (atlas::event::is_key_pressed(key_space)) { - p_transform.position -= up * velocity; - } - - if (atlas::event::is_key_pressed(key_w)) { - p_transform.position += forward * velocity; - } - if (atlas::event::is_key_pressed(key_s)) { - p_transform.position -= forward * velocity; - } - - if (atlas::event::is_key_pressed(key_d)) { - p_transform.position += right * velocity; - } - if (atlas::event::is_key_pressed(key_a)) { - p_transform.position -= right * velocity; - } - - if (atlas::event::is_key_pressed(key_q)) { - p_transform.rotation.y += rotation_velocity; - } - if (atlas::event::is_key_pressed(key_e)) { - p_transform.rotation.y -= rotation_velocity; - } - - p_transform.set_rotation(p_transform.rotation); - }); -} - -void -level_scene::physics_update() { - float dt = atlas::application::delta_time(); - if (atlas::event::is_key_pressed(key_r) and !m_physics_runtime) { - runtime_start(); - } - - auto viking_room = entity("Viking Room"); - - atlas::physics_body* sphere_body = - viking_room.get_mut(); - // U = +up - // J = -up - // H = +left - // L = -Left - if (atlas::event::is_key_pressed(key_space)) { - glm::vec3 linear_velocity = { 0.f, 10.0f, 0.f }; - sphere_body->linear_velocity = linear_velocity; - sphere_body->impulse = linear_velocity; - } - - if (atlas::event::is_key_pressed(key_j)) { - glm::vec3 angular_vel = { -10.f, 0.f, 0.f }; - sphere_body->angular_velocity = angular_vel; - } - - if (atlas::event::is_key_pressed(key_h)) { - glm::vec3 angular_vel = { 10.f, 0.f, 0.f }; - sphere_body->angular_velocity = angular_vel; - } - - if (atlas::event::is_key_pressed(key_l)) { - glm::vec3 angular_vel = { -0.1f, 0.f, 0.f }; - sphere_body->angular_velocity = angular_vel; - } - - if (m_physics_runtime) { - m_physics_engine.update(dt); - } - - if (atlas::event::is_key_pressed(key_l) and m_physics_runtime) { - runtime_stop(); - } -} diff --git a/editor/level_scene.cppm b/editor/level_scene.cppm new file mode 100644 index 00000000..69dd2384 --- /dev/null +++ b/editor/level_scene.cppm @@ -0,0 +1,738 @@ +module; + +#include +#include +#include +#define GLM_ENABLE_EXPERIMENTAL +#include + +#include +#include +#include + +#include +#include +#include +#include + +export module level_scene; + +// import atlas.logger; +import atlas.core.utilities; +import atlas.application; +import atlas.core.scene; +import atlas.core.scene.game_object; +// import atlas.core.event.bus; +// import atlas.core.event.keys; +// import atlas.core.event.mouse_codes; +// import atlas.core.event.types; +import atlas.core.event; +import atlas.core.scene.components; +import atlas.core.utilities.state; +import atlas.core.math; +import atlas.core.editor.dockspace; +import atlas.application; +import atlas.core.ui.widgets; +import atlas.core.editor.menu_item; +import atlas.core.serialize; +import atlas.physics.engine; + +static void ui_component_list(flecs::entity& p_selected_entity) { + std::string entity_name = p_selected_entity.name().c_str(); + std::string new_entity_name = ""; + atlas::ui::draw_input_text(new_entity_name, entity_name); + + p_selected_entity.set_name(new_entity_name.c_str()); + + ImGui::SameLine(); + ImGui::PushItemWidth(-1); + if (ImGui::Button("Add Component")) { + ImGui::OpenPopup("Add Component"); + } + + if (ImGui::BeginPopup("Add Component")) { + if (!p_selected_entity.has()) { + if (ImGui::MenuItem("Perspective Camera")) { + p_selected_entity.add< + flecs::pair>(); + p_selected_entity.add(); + ImGui::CloseCurrentPopup(); + } + } + + if (!p_selected_entity.has()) { + if (ImGui::MenuItem("Mesh Source")) { + p_selected_entity.add(); + ImGui::CloseCurrentPopup(); + } + } + + if (!p_selected_entity.has()) { + if (ImGui::MenuItem("Point Light")) { + p_selected_entity.add(); + ImGui::CloseCurrentPopup(); + } + } + + if (!p_selected_entity.has()) { + if (ImGui::MenuItem("Serialize")) { + p_selected_entity.add(); + ImGui::CloseCurrentPopup(); + } + } + + if (!p_selected_entity.has()) { + if (ImGui::MenuItem("Physics Body")) { + p_selected_entity.add(); + ImGui::CloseCurrentPopup(); + } + } + + if (!p_selected_entity.has()) { + if (ImGui::MenuItem("Box Collider")) { + p_selected_entity.add(); + ImGui::CloseCurrentPopup(); + } + } + + if (!p_selected_entity.has()) { + if (ImGui::MenuItem("Sphere Collider")) { + p_selected_entity.add(); + ImGui::CloseCurrentPopup(); + } + } + + if (!p_selected_entity.has()) { + if (ImGui::MenuItem("Capsule Collider")) { + p_selected_entity.add(); + ImGui::CloseCurrentPopup(); + } + } + ImGui::EndPopup(); + } + + ImGui::PopItemWidth(); +} + +export class level_scene final : public atlas::scene { +public: + level_scene(const std::string& p_name, atlas::event::bus& p_bus) : atlas::scene(p_name, p_bus) { + auto editor_camera = entity("Editor Camera"); + editor_camera.add>(); + editor_camera.set({ + .position = { 3.50f, 4.90f, 36.40f }, + .scale{ 1.f }, + }); + editor_camera.set({ + .plane = { 0.1f, 5000.f }, + .is_active = true, + .field_of_view = 45.f, + }); + + atlas::game_object bob_object = entity("Bob"); + bob_object.add(); + + // @brief For now adding this in + // because we do not have a way to handle empty scenes + // so we are adding this in just to have a specific mesh associated with the renderer for the time being, just to make sure it works. + atlas::game_object viking_room = entity("Viking Room"); + viking_room.add(); + viking_room.set({ + .position = { -2.70f, 2.70, -8.30f }, + .rotation = { 2.30f, 95.90f, 91.80f }, + .scale{ 1.f }, + }); + + viking_room.set({ + .radius = 1.0f, + }); + + viking_room.set({ + .friction = 15.f, + .restitution = 0.3f, + .body_movement_type = atlas::dynamic, + }); + + atlas::game_object cube = entity("Aircraft"); + + cube.set({ + .position = { 0.f, 2.10f, -7.30f }, + .scale = { 0.9f, 0.9f, 0.9f }, + }); + + cube.set({ + .color = { 1.f, 1.f, 1.f, 1.f }, + // .model_path = "assets/models/E 45 Aircraft_obj.obj", + .model_path = "assets/backpack/backpack.obj", + .diffuse = "assets/backpack/diffuse.jpg", + .specular = "assets/backpack/specular.jpg" + // .diffuse = "assets/models/E-45-steel detail_2_col.jpg", + }); + + atlas::game_object robot_model = entity("Cube"); + robot_model.add(); + // robot_model.add(); + robot_model.set({ + .position = { -2.70, 3.50f, 4.10f }, + .scale = { 1.f, 1.f, 1.f }, + }); + + robot_model.set( + { .color = { 1.f, 1.f, 1.f, 1.f }, + .model_path = "assets/models/cube.obj", + .diffuse = "assets/models/container_diffuse.png", + .specular = "assets/models/container_specular.png" }); + + robot_model.set({ + .half_extent = { 1.f, 1.f, 1.f }, + }); + robot_model.set({ + // .restitution = 1.f, + .body_movement_type = atlas::dynamic, + }); + + atlas::game_object platform = entity("Platform"); + + platform.set({ + .scale = { 15.f, 0.30f, 10.0f }, + }); + platform.set({ + .model_path = "assets/models/cube.obj", + .diffuse = "assets/models/wood.png", + }); + platform.set({ + .body_movement_type = atlas::fixed, + }); + platform.set({ + .half_extent = { 15.f, 0.30f, 10.0f }, + }); + + atlas::game_object point_light = entity("Point Light 1"); + point_light.set({ + .position = { 0.f, 2.10f, -7.30f }, + .scale = { 0.9f, 0.9f, 0.9f }, + }); + point_light.add(); + + // for(size_t i = 0; i < 26; i++) { + // auto obj = entity(std::format("Object #{}", i)); + // obj.set({ + // .restitution = 1.25f, + // .body_movement_type = atlas::dynamic, + // }); + + // obj.set( + // { + // .radius = 1.0f, + // }); + + // glm::vec3 pos = {float(0*1.4),float(0 * 1.4),float(0 * -3) }; + + // obj.set({ + // .position = pos, + // .rotation = {.3f, 0.0f, 0.0f}, + // }); + + // obj.set({ + // .model_path = "assets/models/Ball OBJ.obj", + // .diffuse = "assets/models/clear.png", + // }); + // } + + atlas::game_object gerald = entity("Gerald"); + gerald.add(); + + // TODO: Move this outside of level_scene + m_deserializer_test = atlas::serializer(); + + subscribe(this, + &level_scene::collision_enter); + + atlas::register_start(this, &level_scene::start); + atlas::register_physics(this, &level_scene::physics_update); + atlas::register_update(this, &level_scene::on_update); + atlas::register_ui(this, &level_scene::on_ui_update); + } + + ~level_scene() override = default; + + void start() { + m_deserializer_test = atlas::serializer(); + + if (!m_deserializer_test.load("LevelScene", *this)) { + console_log_error("Could not load yaml file LevelScene!!!"); + } + + flecs::world registry = *this; + m_physics_engine = atlas::physics::engine(registry, *event_handle()); + } + + void on_update(float p_delta_time) { + auto query_cameras = query_builder().build(); + float dt = p_delta_time; + + query_cameras.each([this, dt](atlas::perspective_camera& p_camera, + atlas::transform& p_transform) { + if (!p_camera.is_active) { + return; + } + + float default_speed = 10.f; // current default movement speed that does + // not applied modified speed + float rotation_speed = 1.f; + float velocity = default_speed * dt; + if (atlas::event::is_mouse_pressed(mouse_button_middle)) { + velocity = m_movement_speed * dt; + } + float rotation_velocity = rotation_speed * dt; + + glm::quat to_quaternion = atlas::to_quat(p_transform.quaternion); + + glm::vec3 up = glm::rotate(to_quaternion, atlas::math::up()); + glm::vec3 forward = glm::rotate(to_quaternion, atlas::math::backward()); + glm::vec3 right = glm::rotate(to_quaternion, atlas::math::right()); + + if (atlas::event::is_key_pressed(key_left_shift)) { + p_transform.position += up * velocity; + } + + if (atlas::event::is_key_pressed(key_space)) { + p_transform.position -= up * velocity; + } + + if (atlas::event::is_key_pressed(key_w)) { + p_transform.position += forward * velocity; + } + if (atlas::event::is_key_pressed(key_s)) { + p_transform.position -= forward * velocity; + } + + if (atlas::event::is_key_pressed(key_d)) { + p_transform.position += right * velocity; + } + if (atlas::event::is_key_pressed(key_a)) { + p_transform.position -= right * velocity; + } + + if (atlas::event::is_key_pressed(key_q)) { + p_transform.rotation.y += rotation_velocity; + } + if (atlas::event::is_key_pressed(key_e)) { + p_transform.rotation.y -= rotation_velocity; + } + + p_transform.set_rotation(p_transform.rotation); + }); + + if (m_physics_runtime) { + m_physics_engine.update(dt); + } + + if (atlas::event::is_key_pressed(key_l) and m_physics_runtime) { + runtime_stop(); + } + } + + void on_ui_update() { + // setting up the dockspace UI widgets at the window toolbar + m_editor_dockspace.begin(); + + try { + m_editor_menu.begin(); + } + catch (const atlas::ui::menu_bar_exception& e) { + } + + if (ImGui::BeginMenu("File")) { + if (ImGui::MenuItem("Save")) { + // m_deserializer_test.save("LevelScene"); + } + + ImGui::Separator(); + + if (ImGui::MenuItem("Exit")) { + // glfwSetWindowShouldClose(atlas::application::close(), true); + } + + ImGui::EndMenu(); + } + + m_editor_menu.end(); + + if (ImGui::Begin("Viewport")) { + // TODO: Consider doing this a different way, but not with static. + // glm::vec2 viewport_panel_size = + // glm::vec2{ atlas::application::params().width, + // atlas::application::params().height }; + + ImGui::End(); + } + + defer_begin(); + auto query_builder = this->query_builder().build(); + + if (ImGui::Begin("Scene Heirarchy")) { + // @note right click on blank space + // @param string_id + // @param popup_flags - will be the mouse flag (0=right, 1=left) + if (atlas::ui::begin_popup_context_window(nullptr, 1, false)) { + if (ImGui::MenuItem("Create Empty Entity")) { + m_current_entity = entity("Empty Entity"); + } + ImGui::EndPopup(); + } + + query_builder.each([&](flecs::entity p_entity, atlas::transform&) { + // We set the imgui flags for our scene heirarchy panel + // TODO -- Make the scene heirarchy panel a separate class that is + // used for specify the layout and other UI elements here + ImGuiTreeNodeFlags flags = + ((m_selected_entity == p_entity) ? ImGuiTreeNodeFlags_Selected + : 0) | + ImGuiTreeNodeFlags_OpenOnArrow; + flags |= ImGuiTreeNodeFlags_SpanAvailWidth; + flags |= ImGuiWindowFlags_Popup; + flags |= ImGuiTreeNodeFlags_AllowItemOverlap; + + bool opened = ImGui::TreeNodeEx(p_entity.name().c_str(), flags); + + if (ImGui::IsItemClicked()) { + m_selected_entity = p_entity; + } + + bool delete_entity = false; + if (ImGui::BeginPopupContextItem()) { + if (ImGui::MenuItem("Delete Entity")) { + delete_entity = true; + } + ImGui::EndPopup(); + } + + if (delete_entity) { + m_selected_entity.destruct(); + } + + ImGui::SameLine(); + ImGui::TextDisabled("(%llu)", p_entity.id()); + + if (opened) { + flags = ImGuiTreeNodeFlags_OpenOnArrow | + ImGuiTreeNodeFlags_SpanAvailWidth; + auto query_children_builder = + this->query_builder().with(flecs::ChildOf, p_entity).build(); + int32_t child_count = query_children_builder.count(); + + // // Only show children in scene heirarchy panel if there are + // children entities + if (child_count > 0) { + m_selected_entity.children([&](flecs::entity p_child) { + opened = + ImGui::TreeNodeEx(p_child.name().c_str(), flags); + if (opened) { + if (ImGui::IsItemClicked()) { + m_selected_entity = p_child; + } + ImGui::TreePop(); + } + }); + } + + ImGui::TreePop(); + } + }); + + defer_end(); + ImGui::End(); + } + + if (ImGui::Begin("Properties")) { + if (m_selected_entity.is_alive()) { + ui_component_list(m_selected_entity); + + atlas::ui::draw_component( + "transform", + m_selected_entity, + [](atlas::transform* p_transform) { + atlas::ui::draw_vec3("Position", p_transform->position); + atlas::ui::draw_vec3("Scale", p_transform->scale); + atlas::ui::draw_vec3("Rotation", p_transform->rotation); + }); + + atlas::ui::draw_component( + "camera", + m_selected_entity, + [this](atlas::perspective_camera* p_camera) { + atlas::ui::draw_float("field of view", + p_camera->field_of_view); + ImGui::Checkbox("is_active", &p_camera->is_active); + ImGui::DragFloat("Speed", &m_movement_speed); + }); + + atlas::ui::draw_component( + "atlas::mesh_source", + m_selected_entity, + [](atlas::mesh_source* p_source) { + std::string mesh_src = p_source->model_path; + atlas::ui::draw_input_text(p_source->model_path, mesh_src); + atlas::ui::draw_vec4("Color", p_source->color); + }); + + atlas::ui::draw_component( + "material", + m_selected_entity, + [](atlas::material_metadata* p_source) { + float speed = 0.01f; + ImGui::DragFloat4( + "Ambient", glm::value_ptr(p_source->ambient), speed); + ImGui::DragFloat4( + "Diffuse", glm::value_ptr(p_source->diffuse), speed); + ImGui::DragFloat4( + "Specular", glm::value_ptr(p_source->specular), speed); + atlas::ui::draw_float("Shininess", p_source->shininess); + }); + + /* + atlas::ui::draw_component("Directional + Light", m_selected_entity, [](atlas::directional_light* + p_dir_light){ ImGui::DragFloat4("Direction", + glm::value_ptr(p_dir_light->direction)); ImGui::DragFloat4("View + Pos", glm::value_ptr(p_dir_light->view_position)); + ImGui::DragFloat4("Color", glm::value_ptr(p_dir_light->color)); + ImGui::DragFloat4("Ambient", + glm::value_ptr(p_dir_light->ambient)); ImGui::DragFloat4("Diffuse", + glm::value_ptr(p_dir_light->diffuse)); ImGui::DragFloat4("Specular", + glm::value_ptr(p_dir_light->specular)); + }); + */ + + atlas::ui::draw_component( + "Point Light", + m_selected_entity, + [](atlas::point_light* p_dir_light) { + ImGui::DragFloat4( + "Color", glm::value_ptr(p_dir_light->color), 0.01); + ImGui::DragFloat( + "Attenuation", &p_dir_light->attenuation, 0.001); + ImGui::DragFloat4( + "Ambient", glm::value_ptr(p_dir_light->ambient), 0.01); + ImGui::DragFloat4( + "Diffuse", glm::value_ptr(p_dir_light->diffuse), 0.01); + ImGui::DragFloat4( + "Specular", glm::value_ptr(p_dir_light->specular), 0.01); + ImGui::DragFloat("Constant", &p_dir_light->constant, 0.01); + ImGui::DragFloat("Linear", &p_dir_light->linear, 0.01); + ImGui::DragFloat("Quadratic", &p_dir_light->quadratic, 0.01); + }); + + atlas::ui::draw_component( + "Physics Body", + m_selected_entity, + [](atlas::physics_body* p_body) { + std::array items = { + "Static", + "Kinematic", + "Dynamic", + }; + std::string combo_preview = items[p_body->body_movement_type]; + + // Begin the combo box + if (ImGui::BeginCombo("Body Type", combo_preview.data())) { + for (int n = 0; n < 3; n++) { + // Check if the current item is selected + const bool is_selected = + (p_body->body_movement_type == n); + if (ImGui::Selectable(items[n].data(), is_selected)) { + // Update the current type when a new item is + // selected + p_body->body_movement_type = + static_cast(n); + } + + // Set the initial focus when the combo box is first + // opened + if (is_selected) { + ImGui::SetItemDefaultFocus(); + } + } + ImGui::EndCombo(); + } + + // physics body parameters + atlas::ui::draw_vec3("Linear Velocity", + p_body->linear_velocity); + atlas::ui::draw_vec3("Angular Velocity", + p_body->angular_velocity); + atlas::ui::draw_vec3("Force", p_body->force); + atlas::ui::draw_vec3("Impulse", p_body->impulse); + atlas::ui::draw_vec3("Torque", p_body->torque); + atlas::ui::draw_vec3("Center Mass", + p_body->center_mass_position); + }); + + atlas::ui::draw_component( + "Box Collider", + m_selected_entity, + [](atlas::box_collider* p_collider) { + atlas::ui::draw_vec3("Half Extent", p_collider->half_extent); + }); + + atlas::ui::draw_component( + "Box Collider", + m_selected_entity, + [](atlas::sphere_collider* p_collider) { + atlas::ui::draw_float("Radius", p_collider->radius); + }); + + atlas::ui::draw_component( + "Box Collider", + m_selected_entity, + [](atlas::capsule_collider* p_collider) { + atlas::ui::draw_float("Half Height", p_collider->half_height); + atlas::ui::draw_float("Radius", p_collider->radius); + }); + + atlas::ui::draw_component( + "Serialize", + m_selected_entity, + [](atlas::tag::serialize* p_serialize) { + ImGui::Checkbox("Enable", &p_serialize->enable); + }); + } + + ImGui::End(); + + // Note --- just added this temporarily for testing + // auto time = atlas::application::delta_time(); + + // if((int)(time * 10.0f) % 8 > 4) { + // m_blink = !m_blink; + // } + + // auto width = atlas::application::get_window().width(); + // auto height = atlas::application::get_window().height(); + + // ImGui::SetNextWindowPos(ImVec2(static_cast(width) * 0.5f, + // static_cast(height) * 0.5f), ImGuiCond_Always, ImVec2(0.5f, + // 0.5f)); ImGui::SetNextWindowSize(ImVec2(200, 20), ImGuiCond_Always); + // ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration | + // ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoInputs; + // ImGui::SetNextWindowBgAlpha(0.f); + + // if(ImGui::Begin("Testing", nullptr, flags)) { + // ImGui::ProgressBar(10.f); + + // auto pos = ImGui::GetWindowPos(); + // pos.x += (float)width * 0.5f - 300.0f; + // pos.y += 50.0f; + // if(m_blink){ + // ImGui::GetForegroundDrawList()->AddText(m_font, 120.0f, + // pos, 0xffffffff, "Click to Play!"); + // } + + // ImGui::End(); + // } + } + + m_editor_dockspace.end(); + } + + void physics_update() { + // TODO: Replace delta_time with physics fixed-timestep instead + float dt = atlas::application::delta_time(); + if (atlas::event::is_key_pressed(key_r) and !m_physics_runtime) { + runtime_start(); + } + + auto viking_room = entity("Viking Room"); + + atlas::physics_body* sphere_body = + viking_room.get_mut(); + // U = +up + // J = -up + // H = +left + // L = -Left + if (atlas::event::is_key_pressed(key_space)) { + glm::vec3 linear_velocity = { 0.f, 10.0f, 0.f }; + sphere_body->linear_velocity = linear_velocity; + sphere_body->impulse = linear_velocity; + } + + if (atlas::event::is_key_pressed(key_j)) { + glm::vec3 angular_vel = { -10.f, 0.f, 0.f }; + sphere_body->angular_velocity = angular_vel; + } + + if (atlas::event::is_key_pressed(key_h)) { + glm::vec3 angular_vel = { 10.f, 0.f, 0.f }; + sphere_body->angular_velocity = angular_vel; + } + + if (atlas::event::is_key_pressed(key_l)) { + glm::vec3 angular_vel = { -0.1f, 0.f, 0.f }; + sphere_body->angular_velocity = angular_vel; + } + + if (m_physics_runtime) { + m_physics_engine.update(dt); + } + + if (atlas::event::is_key_pressed(key_l) and m_physics_runtime) { + runtime_stop(); + } + } + + void runtime_start() { + m_physics_runtime = true; + m_physics_engine.start(); + } + + void runtime_stop() { + m_physics_runtime = false; + m_physics_engine.stop(); + reset_objects(); + } + + void reset_objects() { + if (!m_deserializer_test.load("LevelScene", *this)) { + console_log_error("Could not load yaml file LevelScene!!!"); + } + } + +private: + void collision_enter(atlas::event::collision_enter& p_event) { + console_log_warn("collision_enter event!!!"); + atlas::game_object e1 = entity(p_event.entity1); + atlas::game_object e2 = entity(p_event.entity2); + + console_log_warn("Entity1 = {}", e1.name().c_str()); + console_log_warn("Entity2 = {}", e2.name().c_str()); + } + + void collision_persisted(atlas::event::collision_persisted& p_event) { + console_log_warn("collision_persisted(p_event) invoked!!"); + atlas::game_object e1 = entity(p_event.entity1); + atlas::game_object e2 = entity(p_event.entity2); + + console_log_warn("Entity1 = {}", e1.name().c_str()); + console_log_warn("Entity2 = {}", e2.name().c_str()); + } + +private: + atlas::serializer m_deserializer_test; + flecs::entity m_selected_entity; + + atlas::game_object_optional m_current_entity; + float m_movement_speed = 10.f; + + // Setting physics system + // TODO -- when refactoring this would be at atlas::world level + atlas::physics::engine m_physics_engine; + + bool m_physics_runtime = false; + + atlas::ui::dockspace m_editor_dockspace; + atlas::ui::menu_item m_editor_menu; + + // Note -- Added this temporarily + // ImFont* m_font; +}; \ No newline at end of file diff --git a/editor/level_scene.hpp b/editor/level_scene.hpp deleted file mode 100644 index 5891afec..00000000 --- a/editor/level_scene.hpp +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/** - * @brief Implementation of a custom scene - * - * Represent a scene with an associated game objects that correspond to this - * game object. - * - */ - -class level_scene final : public atlas::scene { -public: - level_scene(const std::string& p_tag, atlas::event::event_bus& p_bus); - - ~level_scene() override = default; - - void start(); - - void on_update(); - - void on_ui_update(); - - void physics_update(); - - void runtime_start(); - - void runtime_stop(); - - void reset_objects(); - -private: - void collision_enter(atlas::event::collision_enter& p_event); - - void collision_persisted(atlas::event::collision_persisted& p_event); - -private: - atlas::serializer m_deserializer_test; - flecs::entity m_selected_entity; - - atlas::game_object_optional m_current_entity; - float m_movement_speed = 10.f; - - // Setting physics system - // TODO -- when refactoring this would be at atlas::world level - atlas::physics::physics_engine m_physics_engine; - - bool m_physics_runtime = false; - - atlas::ui::dockspace m_editor_dockspace; - atlas::ui::menu_item m_editor_menu; - - // Note -- Added this temporarily - // ImFont* m_font; -}; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt deleted file mode 100644 index 96322730..00000000 --- a/src/CMakeLists.txt +++ /dev/null @@ -1,75 +0,0 @@ -set(INCLUDE_DIR ../atlas) -set(SRC_DIR atlas) - -set(VULKAN_INCLUDE_DIR ${INCLUDE_DIR}/drivers/vulkan-cpp) -set(VULKAN_SRC_DIR ${SRC_DIR}/drivers/vulkan-cpp) - -set( - all_src - ${SRC_DIR}/core/application.cpp - ${SRC_DIR}/core/event/event.cpp - - # ${SRC_DIR}/core/image/stb_image.cpp - - ${SRC_DIR}/core/engine_logger.cpp - ${SRC_DIR}/core/window.cpp - ${SRC_DIR}/core/utilities/state.cpp - - ${SRC_DIR}/core/platforms/main.cpp - ${SRC_DIR}/core/platforms/win32.cpp - - # System-related stuff - ${SRC_DIR}/core/scene/scene.cpp - ${SRC_DIR}/core/scene/game_object.cpp - ${SRC_DIR}/core/system/registry.cpp - - # math sources - ${SRC_DIR}/core/math/utilities.cpp - - # Filesystem stuff - ${SRC_DIR}/core/filesystem/file_dialog.cpp - - # serializer stuff - ${SRC_DIR}/core/serialize/serializer.cpp - ${SRC_DIR}/core/serialize/types.cpp - - # physics stuff - ${SRC_DIR}/physics/physics_engine.cpp - ${SRC_DIR}/physics/physics_context.cpp - - # jolt - ${SRC_DIR}/drivers/jolt-cpp/types.cpp - ${SRC_DIR}/drivers/jolt-cpp/jolt_context.cpp - ${SRC_DIR}/drivers/jolt-cpp/jolt_contact_listener.cpp - - # Scene stuff - ${SRC_DIR}/core/scene/world.cpp - - # Renderer-stuff - ${SRC_DIR}/renderer/renderer.cpp - ${SRC_DIR}/drivers/render_context.cpp - ${SRC_DIR}/drivers/graphics_context.cpp - - ${SRC_DIR}/core/ui/widgets.cpp - - # editor-level stuff - ${SRC_DIR}/core/editor/dockspace.cpp - ${SRC_DIR}/core/editor/menu_item.cpp - - - # imgui stuff - ${VULKAN_SRC_DIR}/vk_imgui.cpp - - ## Vulkan Driver stuff - ${VULKAN_SRC_DIR}/vk_window.cpp - ${VULKAN_SRC_DIR}/utilities.cpp - ${VULKAN_SRC_DIR}/vk_context.cpp - ${VULKAN_SRC_DIR}/vk_physical_driver.cpp - ${VULKAN_SRC_DIR}/vk_driver.cpp - ${VULKAN_SRC_DIR}/vk_swapchain.cpp - ${VULKAN_SRC_DIR}/mesh.cpp - ${VULKAN_SRC_DIR}/shader_resource_group.cpp - ${VULKAN_SRC_DIR}/vk_renderer.cpp -) - -add_library(${PROJECT_NAME} ${all_headers} ${all_src}) diff --git a/src/atlas/core/application.cpp b/src/atlas/core/application.cpp deleted file mode 100644 index c1304963..00000000 --- a/src/atlas/core/application.cpp +++ /dev/null @@ -1,256 +0,0 @@ -#include -#include -#include -#include -#include - -namespace atlas { - application* application::s_instance = nullptr; - - // For now we set this globally readable to other graphics-api agnostic - // API's Not able to be modified, rather only read. - static api g_graphics_backend_api = api::vulkan; - - application::application(const application_settings& p_settings) { - console_log_manager::set_current_logger(p_settings.name); - set_current_api(api::vulkan); - window_settings settings = { - .width = p_settings.width, - .height = p_settings.height, - .name = p_settings.name, - }; - m_window = create_window(settings); - - m_renderer = create_ref( - settings, m_window->current_swapchain().image_size(), "Renderer"); - m_renderer->set_background_color({ - p_settings.background_color.x, - p_settings.background_color.y, - p_settings.background_color.z, - p_settings.background_color.w, - }); - - // vulkan-specific imgui context that allows us to control our backend - // through vulkan - m_ui_context = vk::imgui_context(m_window); - - vk::vk_context::submit_resource_free( - [this]() { m_ui_context.destroy(); }); - s_instance = this; - } - - uint32_t application::current_frame() { - return s_instance->m_current_frame_index; - } - - application::~application() { - destroy(); - } - - void application::set_current_api(api api) { - g_graphics_backend_api = api; - } - - // NOTE: only good for immediate usage, - // this will not work for long-term storage due to the likelyhood - // of the handle being invalidated - VkSwapchainKHR application::get_current_swapchain() { - return m_window->current_swapchain(); - } - - api application::current_api() { - return g_graphics_backend_api; - } - - void application::destroy() { - s_instance->get_window().close(); - } - - float application::delta_time() { - return s_instance->m_delta_time; - } - - float application::physics_step() { - return 0.f; - } - - void application::execute() { - auto start_time = std::chrono::high_resolution_clock::now(); - - detail::invoke_start(); - - m_renderer->preload( - m_window->current_swapchain().swapchain_renderpass()); - - ref current_world = system_registry::get_world("Editor World"); - ref current_scene = current_world->get_scene("LevelScene"); - - flecs::world current_world_scope = *current_scene; - - /* - - flecs::system is how your able to schedule changes for given - portions of data in this case the projection/view matrices are only - being changed when flecs::world::progress(g_delta_time) is being - invoked within the mainloop - current_world_scope.system() - - - When users do object->add>(), this automatically gets invoked by the - .system<...> that gets invoked by the mainloop. - */ - current_world_scope - .system, - transform, - perspective_camera>() - .each([&](flecs::pair p_pair, - transform& p_transform, - perspective_camera& p_camera) { - float aspect_ratio = m_window->aspect_ratio(); - if (!p_camera.is_active) { - return; - } - - p_pair->projection = glm::mat4(1.f); - - p_pair->projection = - glm::perspective(glm::radians(p_camera.field_of_view), - aspect_ratio, - p_camera.plane.x, - p_camera.plane.y); - p_pair->projection[1][1] *= -1; - p_pair->view = glm::mat4(1.f); - - // This is converting a glm::highp_vec4 to a glm::quat - glm::quat quaternion = to_quat(p_transform.quaternion); - - p_pair->view = - glm::translate(p_pair->view, p_transform.position) * - glm::mat4_cast(quaternion); - - p_pair->view = glm::inverse(p_pair->view); - }); - - /* - - Currently how this works is we query with anything that has a - flecs::pair - - This tells the ecs flecs what to do query for in regards to - specific objects that are a camera - - in the tag:: namespace, this is to imply components that are empty - and just represent tags, to specify their uses. - */ - auto query_camera_objects = - current_scene - ->query_builder, - perspective_camera>() - .build(); - - while (m_window->available()) { - auto current_time = std::chrono::high_resolution_clock::now(); - m_delta_time = - std::chrono::duration( - current_time - start_time) - .count(); - start_time = current_time; - event::update_events(); - - // Progresses the flecs::world by one tick (or replaced with using - // the delta time) - // This also invokes the following system call before the - // mainloop - current_world_scope.progress(m_delta_time); - - m_current_frame_index = m_window->acquired_next_frame(); - - // Current commands that are going to be iterated through - // Prevents things like stalling so the CPU doesnt have to wait for - // the GPU to fully complete before starting on the next frame - // Command buffer uses this to track the frames to process its - // commands currently_active_frame = (m_current_frame_index + 1) % - // m_window->current_swapchain().settings().frames_in_flight; - // TODO: Going to need to figure out where to put this - // Added this here because to ensure the handlers being used by the - // renderer is in sync when swapchain is resized - ::vk::command_buffer currently_active = - m_window->active_command(m_current_frame_index); - - detail::invoke_physics_update(); - - detail::invoke_on_update(); - - detail::invoke_defer_update(); - - // We want this to be called after late update - // This queries all camera objects within the camera system - // Update -- going to be removing camera system in replacement of - // just simply using flecs::system to keep it simple for the time - query_camera_objects.each( - [&](flecs::entity, - flecs::pair p_pair, - perspective_camera& p_camera) { - if (!p_camera.is_active) { - return; - } - - m_proj_view = p_pair->projection * p_pair->view; - }); - - // TODO: Introduce scene renderer that will make use of the - // begin/end semantics for setting up tasks during pre-frame - // operations - // renderer begin to indicate when a start of the frame to start - // processing specific tasks that either need to be computed or - // pre-defined before the renderer does something with it. - // TODO: Add scene_manager to coordinate what to process - // before frame preparation - auto current_framebuffer = - m_window->current_swapchain().active_framebuffer( - m_current_frame_index); - m_renderer->begin( - currently_active, - m_window->current_swapchain().settings(), - m_window->current_swapchain().swapchain_renderpass(), - current_framebuffer, - m_proj_view); - - // TODO: vk:imgui_context will have its own renderpass, command - // buffers, and framebuffers specifically for UI-widgets + viewport - m_ui_context.begin(currently_active, m_current_frame_index); - - detail::invoke_ui_update(); - - m_ui_context.end(); - - m_renderer->end(); - - /* - TODO -- have m_window present this to the screen, eventually - m_renderer should just fetch the images in the order to offload - to the swapchain for rendering. - - Where each image has gone through different phases of the - renderpass onto the final image - */ - - std::array commands = { - currently_active, - }; - m_window->current_swapchain().submit(commands); - // Presents to the swapchain to display to screen - // m_renderer->present(m_current_frame_index); - m_window->present(m_current_frame_index); - } - } - - void application::post_destroy() { - m_window->close(); - } - - float application::aspect_ratio() { - return s_instance->m_window->aspect_ratio(); - } - - uint32_t application::image_size() { - return s_instance->m_window->current_swapchain().image_size(); - } -}; diff --git a/src/atlas/core/editor/dockspace.cpp b/src/atlas/core/editor/dockspace.cpp deleted file mode 100644 index 08aaa3a9..00000000 --- a/src/atlas/core/editor/dockspace.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include -#include - -namespace atlas::ui { - - void dockspace::begin() { - - ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_None; - ImGuiWindowFlags window_flags = - ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking; - - if (m_fullscreen_enabled) { - ImGuiViewport* viewport = ImGui::GetMainViewport(); - ImGui::SetNextWindowPos(viewport->Pos); - ImGui::SetNextWindowSize(viewport->Size); - ImGui::SetNextWindowViewport(viewport->ID); - window_flags |= ImGuiWindowFlags_NoTitleBar | - ImGuiWindowFlags_NoCollapse | - ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove; - window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | - ImGuiWindowFlags_NoNavFocus; - } - - if (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode) { - window_flags |= ImGuiWindowFlags_NoBackground; - } - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); - ImGui::Begin("Dockspace Demo", &m_is_dockspace_open, window_flags); - ImGui::PopStyleVar(); - - // Dockspace - ImGuiIO& io = ImGui::GetIO(); - if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable) { - ImGuiID dockspace_id = ImGui::GetID("MyDockspace"); - ImGui::DockSpace(dockspace_id, ImVec2(0.f, 0.f), dockspace_flags); - } - } - - void dockspace::end() { - ImGui::End(); - } -}; \ No newline at end of file diff --git a/src/atlas/core/editor/menu_item.cpp b/src/atlas/core/editor/menu_item.cpp deleted file mode 100644 index f1c65cfe..00000000 --- a/src/atlas/core/editor/menu_item.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include -#include - -namespace atlas::ui { - - void menu_item::begin() { - if (!ImGui::BeginMenuBar()) { - throw menu_bar_exception("ImGui::BeginMenuBar failed!"); - } - } - - void menu_item::end() { - ImGui::EndMenuBar(); - } - - void menu_item::add_child(const std::string& p_name, - const std::function& p_callback) { - if (ImGui::MenuItem(p_name.c_str())) { - p_callback(); - } - ImGui::Separator(); - } -}; \ No newline at end of file diff --git a/src/atlas/core/engine_logger.cpp b/src/atlas/core/engine_logger.cpp deleted file mode 100644 index 07a2c8e8..00000000 --- a/src/atlas/core/engine_logger.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include -#include -#include - -namespace atlas { - - std::string g_current_pattern_for_logs = "Undefined Pattern Specified"; - std::unordered_map> - console_log_manager::s_loggers; - - void console_log_manager::initialize_logger_manager( - const std::string& pattern) { - g_current_pattern_for_logs = pattern; - - //! @note Setting up logs for different log stdout's - //! @note Logs for p_tag is logs specific to the game. - s_loggers.insert({ "engine3d", spdlog::stdout_color_mt("engine3d") }); - s_loggers.insert({ "physics", spdlog::stdout_color_mt("physics") }); - s_loggers.insert({ "vulkan", spdlog::stdout_color_mt("vulkan") }); - s_loggers.insert( - { "assert", spdlog::stdout_color_mt("core assertion") }); - - s_loggers["engine3d"]->set_pattern(pattern); - s_loggers["engine3d"]->set_level(spdlog::level::trace); - - s_loggers["physics"]->set_level(spdlog::level::trace); - s_loggers["physics"]->set_pattern(pattern); - - s_loggers["vulkan"]->set_level(spdlog::level::trace); - s_loggers["vulkan"]->set_pattern(pattern); - - s_loggers["assert"]->set_level(spdlog::level::trace); - s_loggers["assert"]->set_pattern(pattern); - } - - void console_log_manager::create_new_logger( - [[maybe_unused]] const std::string& p_tag) { -#ifndef ENABLE_TESTS_ONLY - s_loggers[p_tag] = spdlog::stdout_color_mt(p_tag); - s_loggers[p_tag]->set_level(spdlog::level::trace); - s_loggers[p_tag]->set_pattern(g_current_pattern_for_logs); -#endif - } - - void console_log_manager::set_current_logger( - [[maybe_unused]] const std::string& p_tag) { -#ifndef ENABLE_TESTS_ONLY - //! @note Setting up logs for different log stdout's - //! @note Logs for p_tag is logs specific to the game - s_loggers[p_tag] = spdlog::stdout_color_mt(p_tag); - s_loggers[p_tag]->set_level(spdlog::level::trace); - s_loggers[p_tag]->set_pattern(g_current_pattern_for_logs); -#endif - } - - ref console_log_manager::get(const std::string& p_tag) { - return s_loggers[p_tag]; - } -}; \ No newline at end of file diff --git a/src/atlas/core/event/event.cpp b/src/atlas/core/event/event.cpp deleted file mode 100644 index 58a51f34..00000000 --- a/src/atlas/core/event/event.cpp +++ /dev/null @@ -1,163 +0,0 @@ -#include -#include -#include - -namespace atlas::event { - static std::map s_controllers; - - using button_id = int; - using controller_id = int; - - bool is_key_pressed(int p_key) { - GLFWwindow* window = application::get_window(); - - auto state = glfwGetKey(window, static_cast(p_key)); - return (state == GLFW_PRESS); - } - - bool is_key_released(int p_key) { - GLFWwindow* window = application::get_window(); - - auto state = glfwGetKey(window, static_cast(p_key)); - return (state == GLFW_RELEASE); - } - - bool is_mouse_pressed(int p_mouse_code) { - GLFWwindow* window = application::get_window(); - - auto state = - glfwGetMouseButton(window, static_cast(p_mouse_code)); - return (state == GLFW_PRESS); - } - - bool is_mouse_released(int p_mouse_code) { - GLFWwindow* window = application::get_window(); - - auto state = - glfwGetMouseButton(window, static_cast(p_mouse_code)); - return (state == GLFW_RELEASE); - } - - glm::vec2 cursor_position() { - GLFWwindow* window = application::get_window(); - - double x_pos, y_pos; - glfwGetCursorPos(window, &x_pos, &y_pos); - - return { x_pos, y_pos }; - } - - // joystic-specific functions - - bool is_joystic_present(int p_controller_id) { - return s_controllers.contains(p_controller_id); - } - - const char* is_joystick_guid(int p_controller_id) { - return glfwGetJoystickGUID(p_controller_id); - } - - float get_joystic_axis(int p_controller_id, int p_button) { - int count; - const float axes = - glfwGetJoystickAxes(p_controller_id, &count)[p_button]; - - if (count < p_button) { - return 0.0f; - } - else { - return axes; - } - } - - // bool is_button_released(button_id p_button, controller_id p_controller) { - // auto selected_controller = s_controllers[p_controller]; - // return (selected_controller.Buttons[p_button].ButtonState == - // input_state::Pressed); - // } - - // bool is_button_pressed(int p_button_id, int p_controller_id) { - // auto controller = s_controllers[p_controller_id]; - // return (controller.Buttons[p_button_id].ButtonState == GLFW_RELEASE); - // } - - bool is_joystick_button_pressed(int p_button) { - return p_button == GLFW_PRESS; - } - - bool is_joystick_button_released(int p_button) { - return p_button == GLFW_RELEASE; - } - - // specific for listening events - void update_events() { - glfwPollEvents(); - - // updating joysticks here - // .... - //! @note Must be called per input updated events. In the case either - //! game console disconnects or reconnects - //! @note This will continously check. - //! @note By default GLFW check's up to a total of 16 joystick ID's that - //! are available - //! @note We iterate all 16 joysticks, only using the joystic ID's that - //! are connected - //! @note Then checking for any events from the connected joystick has - //! occurred - // 1 is the first joystick. - // 16 is the last joystick - for (int joystick_id = 0; joystick_id < 16; joystick_id++) { - if (glfwJoystickPresent(joystick_id) == GLFW_TRUE) { - auto& joystick = s_controllers[joystick_id]; - joystick.ID = joystick_id; - joystick.JoystickName = glfwGetJoystickName(joystick_id); - - //! @note We always check how many buttons the joysticks that - //! are connected contain. - int amount_of_buttons = -1; - const unsigned char* buttons = - glfwGetJoystickButtons(joystick_id, &amount_of_buttons); - - // ConsoleLogWarn("Button Size = {}", amount_of_buttons); - - for (int i = 0; i < amount_of_buttons; i++) { - // ConsoleLogFatal("Button {} is ===> {}", i, buttons[i]); - // if(buttons[i] == GLFW_PRESS && !joystick.ButtonsDown[i]){ - if (is_joystick_button_pressed(buttons[i]) && - !joystick.ButtonsDown[i]) { - joystick.Buttons[i].ButtonState = input_state::Pressed; - } - // else if(buttons[i] == GLFW_RELEASE and - // joystick.ButtonsDown[i]){ - else if (is_joystick_button_released(buttons[i]) and - joystick.ButtonsDown[i]) { - joystick.Buttons[i].ButtonState = input_state::Released; - } - - // joystick.ButtonsDown[i] = (buttons[i] == GLFW_PRESS); - joystick.ButtonsDown[i] = - is_joystick_button_pressed(buttons[i]); - } - - int amount_of_axes = -1; - const float* axes = - glfwGetJoystickAxes(joystick_id, &amount_of_axes); - joystick.AxesOfController[joystick_id] = axes; - // ConsoleLogFatal("Axes at for-loop i = {} and Axes value = - // {:.3f}", 0, axes[0]); ConsoleLogFatal("Axes at for-loop i = - // {} and Axes value = {:.3f}", 1, axes[1]); for(int i = 0; i < - // amount_of_axes; i++){ - // ConsoleLogFatal("Axes at for-loop i = {} and Axes value = - // {:.3f}", i, axes[i]); - // } - } - else { - if (is_joystic_present(joystick_id)) { - s_controllers.erase(joystick_id); - } - } - } - } - - void wait_for_events() {} -}; \ No newline at end of file diff --git a/src/atlas/core/image/stb_image.cpp b/src/atlas/core/image/stb_image.cpp deleted file mode 100644 index 37ea05db..00000000 --- a/src/atlas/core/image/stb_image.cpp +++ /dev/null @@ -1,4 +0,0 @@ -#ifndef STB_IMAGE_IMPLEMENTATION -#define STB_IMAGE_IMPLEMENTATION -#include -#endif \ No newline at end of file diff --git a/src/atlas/core/platforms/main.cpp b/src/atlas/core/platforms/main.cpp deleted file mode 100644 index 94840934..00000000 --- a/src/atlas/core/platforms/main.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include -#include -extern int win_main(); - -int -main() { - //! @note pre-init phase - atlas::console_log_manager::initialize_logger_manager(); - //! @note Ensures that glfw gets initiated during the pre-init phases - if (!glfwInit()) { - console_log_warn("GLFW: Failed to initialize"); - return -1; - } - - return win_main(); -} \ No newline at end of file diff --git a/src/atlas/core/platforms/win32.cpp b/src/atlas/core/platforms/win32.cpp deleted file mode 100644 index ef789510..00000000 --- a/src/atlas/core/platforms/win32.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include -#include -#include -#include -extern atlas::ref atlas::initialize_application(); - -int -win_main() { - // atlas::system_registry::initialize(); - atlas::ref graphic_context = - atlas::initialize_context("vulkan_context"); - - atlas::ref system = - atlas::create_ref("system"); - atlas::ref app = atlas::initialize_application(); - app->execute(); - graphic_context->destroy(); - app->post_destroy(); - return 0; -} diff --git a/src/atlas/core/scene/game_object.cpp b/src/atlas/core/scene/game_object.cpp deleted file mode 100644 index 397f2072..00000000 --- a/src/atlas/core/scene/game_object.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include -#include -#include -#include - -namespace atlas { - game_object::game_object(flecs::world_t* p_registry, flecs::entity_t p_id) - : flecs::entity(p_registry, p_id) { - add(); - } - - game_object::game_object(const flecs::entity& p_base) - : flecs::entity(p_base) { - add(); - } - - game_object::game_object(flecs::entity& p_base) - : flecs::entity(p_base) { - add(); - } - - void game_object::child_of(const std::optional& p_parent) { - add(flecs::ChildOf, p_parent.value()); - } -}; \ No newline at end of file diff --git a/src/atlas/core/scene/scene.cpp b/src/atlas/core/scene/scene.cpp deleted file mode 100644 index 6cb57848..00000000 --- a/src/atlas/core/scene/scene.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include - -namespace atlas { - scene::scene(const std::string& p_name, event::event_bus& p_bus) - : m_name(p_name) - , m_bus(&p_bus) {} - - game_object scene::entity(std::string_view p_name) { - return game_object(m_registry.entity(p_name.data())); - } - - game_object scene::entity(uint64_t p_id) { - return game_object(m_registry.entity(p_id)); - } - - uint32_t scene::children_count(const game_object& p_parent) { - return query_builder().with(flecs::ChildOf, p_parent).build().count(); - } - -}; \ No newline at end of file diff --git a/src/atlas/core/scene/world.cpp b/src/atlas/core/scene/world.cpp deleted file mode 100644 index 62d43aff..00000000 --- a/src/atlas/core/scene/world.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include -#include -#include - -namespace atlas { - - world::world(const std::string& p_name) - : m_name(p_name) {} - - /** - * @brief Currently how to pass in the scene context to the world - * TODO: Have a way of allowing creation and management of those - * created-scenes be done through world - */ - void world::add_scene(const ref& p_scene_context) { - m_scene_container.emplace(p_scene_context->name(), p_scene_context); - } -}; \ No newline at end of file diff --git a/src/atlas/core/serialize/types.cpp b/src/atlas/core/serialize/types.cpp deleted file mode 100644 index ffd47c12..00000000 --- a/src/atlas/core/serialize/types.cpp +++ /dev/null @@ -1,157 +0,0 @@ -#include - -namespace atlas { - - YAML::Emitter& operator<<(YAML::Emitter& p_out, - const glm::highp_vec2& p_values) { - p_out << YAML::Flow; - p_out << YAML::BeginSeq << p_values.x << p_values.y << YAML::EndSeq; - return p_out; - } - - YAML::Emitter& operator<<(YAML::Emitter& p_out, - const glm::highp_vec3& p_values) { - p_out << YAML::Flow; - p_out << YAML::BeginSeq << p_values.x << p_values.y << p_values.z - << YAML::EndSeq; - return p_out; - } - - YAML::Emitter& operator<<(YAML::Emitter& p_out, - const glm::highp_vec4& p_values) { - p_out << YAML::Flow; - p_out << YAML::BeginSeq << p_values.x << p_values.y << p_values.z - << p_values.w << YAML::EndSeq; - return p_out; - } - - // Serializing atlas::transform component to yaml format - YAML::Emitter& operator<<(YAML::Emitter& p_output, - const transform* p_transform) { - p_output << YAML::Key << "Transform"; - - p_output << YAML::BeginMap; - p_output << YAML::Key << "Position" << YAML::Value - << p_transform->position; - p_output << YAML::Key << "Scale" << YAML::Value << p_transform->scale; - p_output << YAML::Key << "Rotation" << YAML::Value - << p_transform->rotation; - p_output << YAML::Key << "Quaternion" << YAML::Value - << p_transform->quaternion; - p_output << YAML::EndMap; - return p_output; - } - - // Serialize perspective camera component into yaml format - YAML::Emitter& operator<<(YAML::Emitter& p_output, - const perspective_camera* p_camera) { - p_output << YAML::Key << "PerspectiveCamera"; - - p_output << YAML::BeginMap; - p_output << YAML::Key << "Plane" << YAML::Value << p_camera->plane; - p_output << YAML::Key << "Active" << YAML::Value << p_camera->is_active; - p_output << YAML::Key << "Field of View" << YAML::Value - << p_camera->field_of_view; - p_output << YAML::EndMap; - return p_output; - } - - YAML::Emitter& operator<<(YAML::Emitter& p_output, - const mesh_source* p_material) { - p_output << YAML::Key << "Mesh Source"; - - p_output << YAML::BeginMap; - p_output << YAML::Key << "Model Path" << YAML::Value - << p_material->model_path; - p_output << YAML::Key << "Diffuse" << YAML::Value - << p_material->diffuse; - p_output << YAML::Key << "Specular" << YAML::Value - << p_material->specular; - p_output << YAML::EndMap; - return p_output; - } - - YAML::Emitter& operator<<(YAML::Emitter& p_output, - const point_light* p_material) { - p_output << YAML::Key << "Point Light"; - - p_output << YAML::BeginMap; - p_output << YAML::Key << "Color" << YAML::Value << p_material->color; - p_output << YAML::Key << "Attenuation" << YAML::Value - << p_material->attenuation; - p_output << YAML::Key << "Ambient" << YAML::Value - << p_material->ambient; - p_output << YAML::Key << "Diffuse" << YAML::Value - << p_material->diffuse; - p_output << YAML::Key << "Specular" << YAML::Value - << p_material->specular; - p_output << YAML::EndMap; - return p_output; - } - - YAML::Emitter& operator<<(YAML::Emitter& p_output, - const physics_body* p_body) { - p_output << YAML::Key << "Physics Body"; - - p_output << YAML::BeginMap; - p_output << YAML::Key << "Linear Velocity" << YAML::Value - << p_body->linear_velocity; - p_output << YAML::Key << "Angular Velocity" << YAML::Value - << p_body->angular_velocity; - p_output << YAML::Key << "Force" << YAML::Value << p_body->force; - p_output << YAML::Key << "Impulse" << YAML::Value << p_body->impulse; - p_output << YAML::Key << "Torque" << YAML::Value << p_body->torque; - p_output << YAML::Key << "Mass Factor" << YAML::Value - << p_body->mass_factor; - p_output << YAML::Key << "Center Mass Position" << YAML::Value - << p_body->center_mass_position; - p_output << YAML::Key << "Friction" << YAML::Value << p_body->friction; - p_output << YAML::Key << "Restitution" << YAML::Value - << p_body->restitution; - p_output << YAML::Key << "Body Movement Type" << YAML::Value - << static_cast(p_body->body_movement_type); - p_output << YAML::Key << "Body Layer Type" << YAML::Value - << static_cast(p_body->body_layer_type); - p_output << YAML::EndMap; - return p_output; - } - - YAML::Emitter& operator<<(YAML::Emitter& p_output, - const box_collider* p_body) { - // Tag this specific serialization values to the box collider - p_output << YAML::Key << "Box Collider"; - - p_output << YAML::BeginMap; - p_output << YAML::Key << "Half Extent" << YAML::Value - << p_body->half_extent; - p_output << YAML::EndMap; - - return p_output; - } - - YAML::Emitter& operator<<(YAML::Emitter& p_output, - const sphere_collider* p_body) { - // - p_output << YAML::Key << "Sphere Collider"; - - p_output << YAML::BeginMap; - p_output << YAML::Key << "Radius" << YAML::Value << p_body->radius; - - p_output << YAML::EndMap; - - return p_output; - } - - YAML::Emitter& operator<<(YAML::Emitter& p_output, - const capsule_collider* p_body) { - // - p_output << YAML::Key << "Capsule Collider"; - p_output << YAML::BeginMap; - p_output << YAML::Key << "Radius" << YAML::Value << p_body->radius; - p_output << YAML::Key << "Half Height" << YAML::Value - << p_body->half_height; - p_output << YAML::EndMap; - - return p_output; - } -}; \ No newline at end of file diff --git a/src/atlas/core/system/registry.cpp b/src/atlas/core/system/registry.cpp deleted file mode 100644 index c0ccf02a..00000000 --- a/src/atlas/core/system/registry.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include -#include -#include - -namespace atlas { - system_registry* system_registry::s_instance = nullptr; - - system_registry::system_registry(const std::string& p_tag) - : m_tag(p_tag) { - s_instance = this; - } - - system_registry::~system_registry() = default; - - ref system_registry::create_world(const std::string& p_tag) { - return s_instance->append_world_and_get(create_ref(p_tag)); - } - - ref system_registry::get_world(const std::string& p_tag) { - return s_instance->search_world(p_tag); - } - - ref system_registry::search_world(const std::string& p_tag) { - return m_world_registered[p_tag]; - } - - ref system_registry::append_world_and_get( - const ref& p_world) { - m_world_registered.emplace(p_world->name(), p_world); - return m_world_registered[p_world->name()]; - } -}; \ No newline at end of file diff --git a/src/atlas/core/timer.cpp b/src/atlas/core/timer.cpp deleted file mode 100644 index 3e420505..00000000 --- a/src/atlas/core/timer.cpp +++ /dev/null @@ -1,4 +0,0 @@ -/* -- Delete this file along with core/timer.hpp if not done so -- Only exists to prevent merge conflicts in CMakeLists.txt -*/ \ No newline at end of file diff --git a/src/atlas/core/utilities/state.cpp b/src/atlas/core/utilities/state.cpp deleted file mode 100644 index 24f45316..00000000 --- a/src/atlas/core/utilities/state.cpp +++ /dev/null @@ -1,98 +0,0 @@ -#include -#include - -namespace atlas { - namespace detail { - // inline std::deque> s_update{}; - // inline std::deque> s_defer_update{}; - // inline std::deque> s_ui_update{}; - // inline std::deque> s_physica_update{}; - // inline std::deque> s_start{}; - - // TODO: This should be done in a better way - // Potential replace this approach with using std::hive from C++26 - inline std::unordered_map> s_update{}; - inline std::unordered_map> - s_defer_update{}; - inline std::unordered_map> s_ui_update{}; - inline std::unordered_map> - s_physica_update{}; - inline std::unordered_map> s_start{}; - - void poll_update(void* p_address, - const std::function& p_callable) { - // s_update.emplace_back(p_callback); - s_update.emplace(p_address, p_callable); - } - - void poll_defer_update(void* p_address, - const std::function& p_callback) { - s_defer_update.emplace(p_address, p_callback); - } - - void poll_physics_update(void* p_address, - const std::function& p_callback) { - s_physica_update.emplace(p_address, p_callback); - } - - void poll_ui_update(void* p_address, - const std::function& p_callback) { - s_ui_update.emplace(p_address, p_callback); - } - - void poll_start(void* p_address, - const std::function& p_callback) { - s_start.emplace(p_address, p_callback); - } - - void remove_update(void* p_address) { - s_update.erase(p_address); - } - - void remove_defer_update(void* p_address) { - s_defer_update.erase(p_address); - } - - void remove_physics_update(void* p_address) { - s_physica_update.erase(p_address); - } - - void remove_ui_update(void* p_address) { - s_ui_update.erase(p_address); - } - - void remove_start(void* p_address) { - s_start.erase(p_address); - } - - void invoke_on_update() { - for (auto& [address, on_update] : s_update) { - on_update(); - } - } - - void invoke_defer_update() { - for (auto& [address, on_update] : s_defer_update) { - on_update(); - } - } - - void invoke_physics_update() { - for (auto& [address, on_update] : s_physica_update) { - on_update(); - } - } - - void invoke_ui_update() { - for (auto& [address, on_update] : s_ui_update) { - on_update(); - } - } - - void invoke_start() { - for (auto& [address, on_update] : s_start) { - on_update(); - } - } - }; -}; \ No newline at end of file diff --git a/src/atlas/core/window.cpp b/src/atlas/core/window.cpp deleted file mode 100644 index b202cc86..00000000 --- a/src/atlas/core/window.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include -#include -#include -#include -#include - -namespace atlas { - ref create_window(const window_settings& p_settings) { - switch (application::current_api()) { - case api::vulkan: - return create_ref(p_settings); - default: - console_log_error( - "API that was input was not specifiying valid backend!"); - return nullptr; - } - - return nullptr; - } - - uint32_t window::width() const { - return settings().width; - } - - uint32_t window::height() const { - return settings().height; - } - - bool window::available() const { - return !glfwWindowShouldClose(native_window()); - } - - void window::close() { - glfwSetWindowShouldClose(native_window(), true); - } - - void window::present(const uint32_t& p_current_frame_idx) { - return presentation_process(p_current_frame_idx); - } - - float window::aspect_ratio() const { - return (float)width() / (float)height(); - } -}; \ No newline at end of file diff --git a/src/atlas/drivers/graphics_context.cpp b/src/atlas/drivers/graphics_context.cpp deleted file mode 100644 index 7d236703..00000000 --- a/src/atlas/drivers/graphics_context.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include -#include -#include - -namespace atlas { - ref initialize_context(const std::string& p_tag) { - switch (application::current_api()) { - case api::vulkan: - return create_ref(p_tag); - default: - return nullptr; - } - } -}; \ No newline at end of file diff --git a/src/atlas/drivers/jolt-cpp/jolt_contact_listener.cpp b/src/atlas/drivers/jolt-cpp/jolt_contact_listener.cpp deleted file mode 100644 index a6c7aff0..00000000 --- a/src/atlas/drivers/jolt-cpp/jolt_contact_listener.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include -#include -#include -#include -#include - -namespace atlas::physics { - - contact_listener::contact_listener(event::event_bus& p_bus) - : m_bus(&p_bus) {} - - JPH::ValidateResult contact_listener::OnContactValidate( - const JPH::Body&, - const JPH::Body&, - JPH::RVec3Arg, - const JPH::CollideShapeResult&) { - return JPH::ValidateResult::AcceptAllContactsForThisBodyPair; - } - - void contact_listener::OnContactAdded(const JPH::Body& p_body1, - const JPH::Body& p_body2, - const JPH::ContactManifold&, - JPH::ContactSettings&) { - event::collision_enter begin_event = { - .entity1 = static_cast(p_body1.GetUserData()), - .entity2 = static_cast(p_body2.GetUserData()) - }; - - // Publishes to all subscribers that this collision_enter event has - // occurred - m_bus->publish(begin_event); - } - - void contact_listener::OnContactPersisted(const JPH::Body& p_body1, - const JPH::Body& p_body2, - const JPH::ContactManifold&, - JPH::ContactSettings&) { - event::collision_persisted persisted_event = { - .entity1 = static_cast(p_body1.GetUserData()), - .entity2 = static_cast(p_body2.GetUserData()) - }; - - m_bus->publish(persisted_event); - } - - void contact_listener::OnContactRemoved(const JPH::SubShapeIDPair&) { - console_log_info("Collisions Removed!"); - // For Event system to handle when collision ends - } - -} \ No newline at end of file diff --git a/src/atlas/drivers/jolt-cpp/jolt_context.cpp b/src/atlas/drivers/jolt-cpp/jolt_context.cpp deleted file mode 100644 index 5b732e1e..00000000 --- a/src/atlas/drivers/jolt-cpp/jolt_context.cpp +++ /dev/null @@ -1,376 +0,0 @@ -#include -#include -#include -#include -#include - -namespace atlas::physics { - static void trace_impl(const char* p_in_fmt, ...) { - va_list list; - va_start(list, p_in_fmt); - char buffer[1024]; - vsnprintf(buffer, sizeof(buffer), p_in_fmt, list); - va_end(list); - console_log_error("{}", buffer); - } - - [[maybe_unused]] static bool assert_failed_impl(const char* p_in_expression, - const char* p_in_message, - const char* p_in_file, - unsigned int p_in_line) { - - console_log_error("{}:{}: ({}) {}", - p_in_file, - p_in_line, - p_in_expression, - (p_in_message != nullptr ? p_in_message : "")); - - return true; - }; - - jolt_context::jolt_context(const jolt_settings& p_settings, - event::event_bus& p_bus) - : m_contact_listener(p_bus) { - JPH::RegisterDefaultAllocator(); - - JPH::Trace = trace_impl; - JPH_IF_ENABLE_ASSERTS(JPH::AssertFailed = assert_failed_impl;) - - JPH::Factory::sInstance = new JPH::Factory(); - JPH::RegisterTypes(); - - m_temp_allocator = - create_ref(p_settings.allocation_amount); - - // This just sets up the JoltPhysics system and any listeners - m_physics_system = create_ref(); - m_broad_phase_layer_interface = - create_ref(); - m_object_vs_broadphase_filter = - create_ref(); - m_object_layer_pair_filter = create_ref(); - - if (p_settings.thread_type == thread_type::default_system) { - - m_thread_system = create_scope( - // Max jobs must be a power of 2, otherwise jph crashes. - // Bianary tree must be fully balanced - std::pow(2, p_settings.max_jobs_power), - p_settings.max_barriers, - p_settings.physics_threads); - } - else { - console_log_error("Unsupported custom job system"); - assert(false); - } - - m_physics_system->Init(p_settings.max_bodies, - 0, - p_settings.max_body_pairs, - p_settings.max_contact_constraints, - *m_broad_phase_layer_interface, - *m_object_vs_broadphase_filter, - *m_object_layer_pair_filter); - - // Default contact listener impl and can change during runtime - m_physics_system->SetContactListener(&m_contact_listener); - } - - void jolt_context::emplace_box_collider(uint32_t p_entity_id, - const transform* p_transform, - const physics_body* p_body, - const box_collider* p_collider) { - using namespace JPH; - auto& body_interface = m_physics_system->GetBodyInterface(); - - // Creating our box shape and specifying half_extent that is a glm::vec3 - // conversion to JPH::Vec3 half_extents must be 0.5f or else it can get - // an invalid convex radius - BoxShapeSettings shape_settings(jolt::to_vec3(p_collider->half_extent)); - auto result = shape_settings.Create(); - - if (result.HasError()) { - console_log_error("Box shape creation error: {}", - result.GetError()); - return; - } - EMotionType motion_type = EMotionType::Static; - switch (p_body->body_movement_type) { - case body_type::fixed: - motion_type = EMotionType::Static; - break; - case body_type::dynamic: - motion_type = EMotionType::Dynamic; - break; - case body_type::kinematic: - motion_type = EMotionType::Kinematic; - break; - } - - auto& box = result.Get(); - BodyCreationSettings body_settings( - box, - jolt::to_vec3(p_transform->position), - jolt::to_quat(p_transform->quaternion), - motion_type, - p_body->body_layer_type); - - // NOTE TO SELF ------ This is setting some pointer to the entity ID - // WE CAN USE THIS TO TELL THE EVENT SYSTEM WHICH FLECS ENTITY COLLIDED - // WITH EACH OTHER!!!!!!!! Because each contact listener allows you to - // take a pointer from the physics bodies that are just blocks of - // data!!! - body_settings.mUserData = static_cast(p_entity_id); - body_settings.mFriction = p_body->friction; - body_settings.mRestitution = p_body->restitution; - body_settings.mLinearVelocity = jolt::to_vec3(p_body->linear_velocity); - body_settings.mAngularVelocity = - jolt::to_vec3(p_body->angular_velocity); - - Body* body = body_interface.CreateBody(body_settings); - m_cached_body_ids.emplace(p_entity_id, body->GetID()); - } - - void jolt_context::emplace_sphere_collider( - uint32_t p_entity_id, - const transform* p_transform, - const physics_body* p_body, - const sphere_collider* p_collider) { - using namespace JPH; - auto& body_interface = m_physics_system->GetBodyInterface(); - // ensure that the half_extent of the box shape always matches the - // object and reference that information through the transform - SphereShapeSettings shape_settings(p_collider->radius); - auto result = shape_settings.Create(); - - if (result.HasError()) { - console_log_error("Sphere shape creation error: {}", - result.GetError()); - return; - } - EMotionType motion_type = EMotionType::Static; - switch (p_body->body_movement_type) { - case body_type::fixed: { - motion_type = EMotionType::Static; - } break; - case body_type::dynamic: { - motion_type = EMotionType::Dynamic; - } break; - case body_type::kinematic: { - motion_type = EMotionType::Kinematic; - } break; - } - - auto& box = result.Get(); - BodyCreationSettings body_settings( - box, - jolt::to_vec3(p_transform->position), - jolt::to_quat(p_transform->quaternion), - motion_type, - p_body->body_layer_type); - - // Assigning the entity ID as the user data - // Fetched when collision happens - body_settings.mUserData = static_cast(p_entity_id); - body_settings.mFriction = p_body->friction; - body_settings.mRestitution = p_body->restitution; - body_settings.mLinearVelocity = jolt::to_vec3(p_body->linear_velocity); - body_settings.mAngularVelocity = - jolt::to_vec3(p_body->angular_velocity); - Body* body = body_interface.CreateBody(body_settings); - - // body_interface.AddForce(body->GetID(), - // jolt::to_vec3(p_body->cumulative_force)); - m_cached_body_ids.emplace(p_entity_id, body->GetID()); - } - - void jolt_context::emplace_capsule_collider( - uint32_t p_entity_id, - const transform* p_transform, - const physics_body* p_body, - const capsule_collider* p_collider) { - using namespace JPH; - auto& body_interface = m_physics_system->GetBodyInterface(); - // ensure that the half_extent of the box shape always matches the - // object and reference that information through the transform - CapsuleShapeSettings shape_settings(p_collider->half_height, - p_collider->radius); - auto result = shape_settings.Create(); - - if (result.HasError()) { - console_log_error("Capsule shape creation error: {}", - result.GetError()); - return; - } - EMotionType motion_type = EMotionType::Static; - switch (p_body->body_movement_type) { - case body_type::fixed: { - motion_type = EMotionType::Static; - } break; - case body_type::dynamic: { - motion_type = EMotionType::Dynamic; - } break; - case body_type::kinematic: { - motion_type = EMotionType::Kinematic; - } break; - } - - auto& box = result.Get(); - BodyCreationSettings body_settings( - box, - jolt::to_vec3(p_transform->position), - jolt::to_quat(p_transform->quaternion), - motion_type, - p_body->body_layer_type); - body_settings.mFriction = p_body->friction; - body_settings.mRestitution = p_body->restitution; - body_settings.mLinearVelocity = jolt::to_vec3(p_body->linear_velocity); - body_settings.mAngularVelocity = - jolt::to_vec3(p_body->angular_velocity); - body_settings.mUserData = static_cast(p_entity_id); - - Body* body = body_interface.CreateBody(body_settings); - m_cached_body_ids.emplace(p_entity_id, body->GetID()); - } - - transform jolt_context::context_read_transform(uint32_t p_id) { - using namespace JPH; - transform new_transform{}; - auto& body_interface = m_physics_system->GetBodyInterface(); - - BodyID body_id = m_cached_body_ids[p_id]; - JPH::Vec3 pos = body_interface.GetPosition(body_id); - JPH::Quat rot = body_interface.GetRotation(body_id); - JPH::Vec3 rot_euler = rot.GetEulerAngles(); - - new_transform.position = to_vec3(pos); - new_transform.quaternion = to_vec4(rot); - new_transform.rotation = to_vec3(rot_euler); - - return new_transform; - } - - physics_body jolt_context::context_read_physics_body(uint32_t p_id) { - using namespace JPH; - auto& body_interface = m_physics_system->GetBodyInterface(); - - // TODO: Will need to change this because if this entity doesn't exist - // then it will be set to zeroes, gotta be careful about this - if (!m_cached_body_ids.contains(p_id)) { - return {}; - } - - auto body_id = m_cached_body_ids.at(p_id); - - physics_body body = { - .linear_velocity = - to_vec3(body_interface.GetLinearVelocity(body_id)), - .angular_velocity = - to_vec3(body_interface.GetAngularVelocity(body_id)), - .center_mass_position = - to_vec3(body_interface.GetCenterOfMassPosition(body_id)), - .gravity_factor = body_interface.GetGravityFactor(body_id), - .friction = body_interface.GetFriction(body_id), - .restitution = body_interface.GetRestitution(body_id), - }; - - return body; - } - - void jolt_context::destroy_bodies() { - auto& body_interface = m_physics_system->GetBodyInterface(); - - // Retrieve all body ID's to ensure that we do proper deactivation and - // post cleanup for the physics simulation - JPH::BodyIDVector all_body_ids; - m_physics_system->GetBodies(all_body_ids); - - if (!all_body_ids.empty()) { - - body_interface.DeactivateBodies( - all_body_ids.data(), static_cast(all_body_ids.size())); - - body_interface.RemoveBodies(all_body_ids.data(), - static_cast(all_body_ids.size())); - - body_interface.DestroyBodies(all_body_ids.data(), - static_cast(all_body_ids.size())); - - m_cached_body_ids.clear(); - } - } - - void jolt_context::linear_velocity(uint64_t p_id, - const glm::vec3& p_linear_velocity) { - using namespace JPH; - auto& body_interface = m_physics_system->GetBodyInterface(); - - body_interface.SetLinearVelocity(m_cached_body_ids.at(p_id), - jolt::to_vec3(p_linear_velocity)); - } - - void jolt_context::angular_velocity(uint64_t p_id, - const glm::vec3& p_angular_velocity) { - using namespace JPH; - auto& body_interface = m_physics_system->GetBodyInterface(); - BodyID body_id(p_id); - - body_interface.SetAngularVelocity(m_cached_body_ids.at(p_id), - jolt::to_vec3(p_angular_velocity)); - } - - void jolt_context::force(uint64_t p_id, const glm::vec3& p_force) { - using namespace JPH; - auto& body_interface = m_physics_system->GetBodyInterface(); - BodyID body_id(p_id); - - body_interface.AddForce(m_cached_body_ids.at(p_id), - jolt::to_vec3(p_force)); - } - - void jolt_context::add_force_and_torque(uint64_t p_id, - const glm::vec3& p_force, - const glm::vec3& p_torque) { - using namespace JPH; - auto& body_interface = m_physics_system->GetBodyInterface(); - - body_interface.AddForceAndTorque(m_cached_body_ids.at(p_id), - jolt::to_vec3(p_force), - jolt::to_vec3(p_torque)); - } - - void jolt_context::add_impulse(uint64_t p_id, const glm::vec3& p_impulse) { - using namespace JPH; - auto& body_interface = m_physics_system->GetBodyInterface(); - - body_interface.AddImpulse(m_cached_body_ids.at(p_id), - jolt::to_vec3(p_impulse)); - } - - void jolt_context::prepare_and_finalize() { - using namespace JPH; - - //! @brief We actually do not need to pass in the body ID's into - //! std::vector. Though we may need ways to store JPH::BodyID for - //! modifying specific bodies - JPH::BodyIDVector all_body_ids; - m_physics_system->GetBodies(all_body_ids); - - auto& body_interface = m_physics_system->GetBodyInterface(); - auto state = body_interface.AddBodiesPrepare( - all_body_ids.data(), static_cast(all_body_ids.size())); - body_interface.AddBodiesFinalize(all_body_ids.data(), - static_cast(all_body_ids.size()), - state, - JPH::EActivation::Activate); - } - - void jolt_context::update_simulation(float p_delta_time) { - float fixed_time_step = 1.0f / 60.0f; - int time_step = 1 + (int)(60 * fixed_time_step); - m_physics_system->Update(p_delta_time, - time_step, - m_temp_allocator.get(), - m_thread_system.get()); - } -} diff --git a/src/atlas/drivers/jolt-cpp/types.cpp b/src/atlas/drivers/jolt-cpp/types.cpp deleted file mode 100644 index 861e769b..00000000 --- a/src/atlas/drivers/jolt-cpp/types.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include -#include - -namespace atlas { - namespace jolt { - JPH::RVec3 to_rvec3(const glm::vec3& p_value) { - return { p_value.x, p_value.y, p_value.z }; - } - - JPH::Vec3 to_vec3(const glm::vec3& p_value) { - return { p_value.x, p_value.y, p_value.z }; - } - - JPH::Quat to_quat(const glm::vec4& q) { - return { q.x, q.y, q.z, q.w }; - } - - JPH::Quat to_quat(glm::quat& p_value) { - return JPH::Quat(p_value.w, p_value.x, p_value.y, p_value.z); - } - } - - glm::quat to_quat(const JPH::Quat& p_value) { - return glm::quat( - p_value.GetW(), p_value.GetX(), p_value.GetY(), p_value.GetZ()); - } - - glm::vec3 to_vec3(const JPH::Vec3& p_value) { - return vector3(p_value); - } - - glm::vec4 to_vec4(const JPH::Quat& p_value) { - return glm::vec4( - p_value.GetX(), p_value.GetY(), p_value.GetZ(), p_value.GetW()); - } -}; \ No newline at end of file diff --git a/src/atlas/drivers/render_context.cpp b/src/atlas/drivers/render_context.cpp deleted file mode 100644 index 7eff5ed6..00000000 --- a/src/atlas/drivers/render_context.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include -#include -#include - -namespace atlas { - - ref initialize_renderer( - const window_settings& p_window_extent, - uint32_t p_image_size, - const std::string& p_tag) { - switch (application::current_api()) { - case api::vulkan: - return create_ref( - p_window_extent, p_image_size, p_tag); - default: - return nullptr; - } - } -}; diff --git a/src/atlas/drivers/vulkan-cpp/mesh.cpp b/src/atlas/drivers/vulkan-cpp/mesh.cpp deleted file mode 100644 index 60d661aa..00000000 --- a/src/atlas/drivers/vulkan-cpp/mesh.cpp +++ /dev/null @@ -1,192 +0,0 @@ -#include -#include -#include -#define STB_IMAGE_IMPLEMENTATION -#define STB_IMAGE_WRITE_IMPLEMENTATION -#include -#include -#include - -namespace atlas::vk { - mesh::mesh(std::span<::vk::vertex_input> p_vertices, - std::span p_indices) { - m_physical = vk_context::physical_driver(); - m_device = vk_context::driver_context(); - ::vk::vertex_params vbo_settings = { .phsyical_memory_properties = - m_physical.memory_properties(), - .vertices = p_vertices }; - ::vk::index_params ibo_settings = { .phsyical_memory_properties = - m_physical.memory_properties(), - .indices = p_indices }; - m_vbo = ::vk::vertex_buffer(m_device, vbo_settings); - m_ibo = ::vk::index_buffer(m_device, ibo_settings); - } - - mesh::mesh(const std::filesystem::path& p_filename, bool p_flip) - : m_flip(p_flip) { - m_physical = vk_context::physical_driver(); - m_device = vk_context::driver_context(); - reload_mesh(p_filename); - } - - void mesh::reload_mesh(const std::filesystem::path& p_filename) { - console_log_info("Loading .obj file!!!"); - load_obj(p_filename); - } - - void mesh::load_obj(const std::filesystem::path& p_filename) { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - std::string warn, err; - - //! @note If we return the constructor then we can check if the mesh - //! loaded successfully - //! @note We also receive hints if the loading is successful! - //! @note Return default constructor automatically returns false means - //! that mesh will return the boolean as false because it wasnt - //! successful - if (!tinyobj::LoadObj(&attrib, - &shapes, - &materials, - &warn, - &err, - p_filename.string().c_str())) { - console_log_warn("Could not load model from path {}", - p_filename.string()); - m_model_loaded = false; - return; - } - - std::vector<::vk::vertex_input> vertices; - std::vector indices; - std::unordered_map<::vk::vertex_input, uint32_t> unique_vertices{}; - - // for (const auto& shape : shapes) { - for (size_t i = 0; i < shapes.size(); i++) { - auto shape = shapes[i]; - // for (const auto& index : shape.mesh.indices) { - for (size_t j = 0; j < shape.mesh.indices.size(); j++) { - auto index = shape.mesh.indices[j]; - ::vk::vertex_input vertex{}; - - if (!unique_vertices.contains(vertex)) { - unique_vertices[vertex] = - static_cast(vertices.size()); - vertices.push_back(vertex); - } - - if (index.vertex_index >= 0) { - vertex.position = { - attrib.vertices[3 * index.vertex_index + 0], - attrib.vertices[3 * index.vertex_index + 1], - attrib.vertices[3 * index.vertex_index + 2] - }; - - vertex.color = { - attrib.colors[3 * index.vertex_index + 0], - attrib.colors[3 * index.vertex_index + 1], - attrib.colors[3 * index.vertex_index + 2] - }; - } - - if (!attrib.normals.empty()) { - vertex.normals = { - attrib.normals[3 * index.normal_index + 0], - attrib.normals[3 * index.normal_index + 1], - attrib.normals[3 * index.normal_index + 2] - }; - } - if (!attrib.texcoords.empty()) { - glm::vec2 flipped_uv = { - attrib.texcoords - [static_cast(index.texcoord_index) * 2], - 1.0f - attrib.texcoords[static_cast( - index.texcoord_index) * - 2 + - 1], - }; - - glm::vec2 original_uv = { - attrib.texcoords - [static_cast(index.texcoord_index) * 2], - attrib.texcoords - [static_cast(index.texcoord_index) * 2 + - 1], - }; - - vertex.uv = m_flip ? flipped_uv : original_uv; - } - else { - vertex.uv = glm::vec2(0.f, 0.f); - } - - if (!unique_vertices.contains(vertex)) { - unique_vertices[vertex] = - static_cast(vertices.size()); - vertices.push_back(vertex); - } - - indices.push_back(unique_vertices[vertex]); - } - } - - ::vk::vertex_params vbo_settings = { .phsyical_memory_properties = - m_physical.memory_properties(), - .vertices = vertices }; - ::vk::index_params ibo_settings = { .phsyical_memory_properties = - m_physical.memory_properties(), - .indices = indices }; - m_vbo = ::vk::vertex_buffer(m_device, vbo_settings); - m_ibo = ::vk::index_buffer(m_device, ibo_settings); - m_model_loaded = true; - } - - void mesh::add_diffuse(const std::filesystem::path& p_path) { - ::vk::texture_info config_texture = { - .phsyical_memory_properties = m_physical.memory_properties(), - .filepath = p_path, - }; - m_diffuse = ::vk::texture(m_device, config_texture); - - if (!m_diffuse.loaded()) { - console_log_info("Diffuse Texture {} is NOT loaded!!!", - p_path.string()); - return; - } - } - - void mesh::add_specular(const std::filesystem::path& p_path) { - ::vk::texture_info config_texture = { .phsyical_memory_properties = - m_physical.memory_properties(), - .filepath = p_path }; - m_specular = ::vk::texture(m_device, config_texture); - - if (!m_specular.loaded()) { - console_log_error("Specular Texture {} is NOT loaded!!!", - p_path.string()); - return; - } - } - - void mesh::draw(const VkCommandBuffer& p_current) { - m_vbo.bind(p_current); - if (m_ibo.size() > 0) { - m_ibo.bind(p_current); - vkCmdDrawIndexed(p_current, m_ibo.size(), 1, 0, 0, 0); - } - else { - vkCmdDraw(p_current, m_vbo.size(), 1, 0, 0); - } - } - - void mesh::destroy() { - m_vbo.destroy(); - m_ibo.destroy(); - - m_diffuse.destroy(); - m_specular.destroy(); - m_geoemtry_ubo.destroy(); - m_material_ubo.destroy(); - } -}; \ No newline at end of file diff --git a/src/atlas/drivers/vulkan-cpp/shader_resource_group.cpp b/src/atlas/drivers/vulkan-cpp/shader_resource_group.cpp deleted file mode 100644 index aaa4fef8..00000000 --- a/src/atlas/drivers/vulkan-cpp/shader_resource_group.cpp +++ /dev/null @@ -1,346 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -namespace atlas::vk { - - // Reading the raw .spv binaries - static std::vector read_raw_spirv(const std::string& p_file) { - std::vector out_buffer; - std::ifstream ins(p_file, std::ios::ate | std::ios::binary); - - if (!ins) { - throw std::runtime_error("Cannot load in .spv files!!"); - } - - uint32_t file_size = (uint32_t)ins.tellg(); - out_buffer.resize(file_size); - ins.seekg(0); - ins.read(out_buffer.data(), file_size); - return out_buffer; - } - - //! @brief Ensure file reads are valid before reading raw .spv binaries - static std::vector compile_binary_shader_source( - const ::vk::shader_source& p_shader_source) { - - if (!std::filesystem::is_regular_file(p_shader_source.filename)) { - throw std::runtime_error("Cannot load .spv file"); - } - - return read_raw_spirv(p_shader_source.filename); - } - - static std::string read_shader_source_code(const std::string& p_filename) { - std::ifstream ins(p_filename, std::ios::ate | std::ios::binary); - - if (!ins.is_open()) { - console_log_warn("Could not open filename = {}", p_filename); - return { 'a' }; - } - - size_t file_size = (size_t)ins.tellg(); - std::string output; - output.resize(file_size); - ins.seekg(0); - ins.read(output.data(), static_cast(file_size)); - - return output; - } - - /** - * compiles source code from the shader directly without needing manual - * recompilation - * - * shaderc requires these parameters to compile - * text_source_code: the std::string version of the entire source code to - * compile type: shader stage this shader corresponds to filename: input - * filename text entry_point: the entry point to this shader options: - * compiler-specific options to enable when compiling the shader sources - */ - static std::vector compile_source_from_file( - const ::vk::shader_source& p_shader_source) { - shaderc::CompileOptions options; - options.SetTargetEnvironment(shaderc_target_env_vulkan, - shaderc_env_version_vulkan_1_3); - options.SetWarningsAsErrors(); - - shaderc_shader_kind type; - - switch (p_shader_source.stage) { - case ::vk::shader_stage::vertex: - type = shaderc_glsl_vertex_shader; - break; - case ::vk::shader_stage::fragment: - type = shaderc_glsl_fragment_shader; - break; - default: - throw std::runtime_error("shader_stage unspecified!~!!"); - } - - shaderc::Compiler compiler; - std::string text_source_code = - read_shader_source_code(p_shader_source.filename); - - // Prints out the text of the shader source code - // console_log_warn("Source Text Code!!!"); - // console_log_info("{}", text_source_code); - shaderc::CompilationResult result = - compiler.CompileGlslToSpv(text_source_code, - type, - p_shader_source.filename.c_str(), - "main", - options); - - std::vector blob; - - if (result.GetCompilationStatus() != - shaderc_compilation_status_success) { - throw std::runtime_error( - std::format("Shader Compilation Error! Failed with reason {}\n{}", - p_shader_source.filename, - result.GetErrorMessage()) - .c_str()); - } - - for (auto blob_chunk : result) { - blob.push_back(blob_chunk); - } - - return blob; - } - - shader_resource_group::shader_resource_group( - const VkDevice& p_device, - const ::vk::shader_resource_info& p_info) - : m_device(p_device) { - - // We go through all of the specified shader source and their specific - // stage Compile them through shader compiler or if provided a .spv, - // then we compile and read in the stream of bytes directly - for (size_t i = 0; i < p_info.sources.size(); i++) { - const ::vk::shader_source shader_src = p_info.sources[i]; - std::filesystem::path filepath = - std::filesystem::path(shader_src.filename); - - if (filepath.extension().string() == ".spv") { - std::vector blob = - compile_binary_shader_source(shader_src); - - if (blob.empty()) { - m_resource_valid = false; - throw std::runtime_error("Cannot load in vector " - "blob of compiled down data!!!"); - } - - create_module(blob, shader_src); - } - else { - std::string text_source_code = - read_shader_source_code(filepath.string()); - std::vector blob = - compile_source_from_file(shader_src); - create_module(blob, shader_src); - } - } - - // This is a testing example for getting shaders hot-reloading - // m_watcher = create_ref("./experimental-shaders", - // [this](const wtr::event& e){ - // console_log_info("File {} Reload!!!", - // e.path_name.filename().string()); if (e.effect_type == - // wtr::event::effect_type::modify and e.path_type == - // wtr::event::path_type::file) { - // console_log_info("File has been modified!!"); - // // std::string fmt = std::format("experimental-shaders/{}", - // e.path_name.filename().string()); - // console_log_info("({}).contains = {}", - // e.path_name.filename().string(), - // m_modules.contains(e.path_name.filename().string())); - // if(m_modules.contains(e.path_name.filename().string())) { - // console_log_info("Reload has been requested!!!"); - // m_reload_requested = true; - // // reload - // ::vk::shader_source reload_src = { - // .filename = e.path_name.filename().string(), - // .stage = - // m_modules.at(e.path_name.filename().string()).stage - // }; - // reload_shader(reload_src); - - // } - // } - // else { - // console_log_info("File {} Could Not Be Reloaded!!!", - // e.path_name.filename().string()); - // } - // }); - - m_resource_valid = true; - } - - void shader_resource_group::reload_shader( - const ::vk::shader_source& p_source) { - console_log_info("p_source.filename = {}", p_source.filename); - if (m_modules[p_source.filename].module != nullptr) { - vkDestroyShaderModule( - m_device, m_modules[p_source.filename].module, nullptr); - } - - auto& handle = m_modules[p_source.filename]; - - std::filesystem::path filepath(p_source.filename); - std::string text_source_code = - read_shader_source_code(filepath.string()); - std::vector blob = compile_source_from_file(p_source); - std::span view_blob(blob.data(), blob.size()); - // create_module(blob, p_source); - VkShaderModuleCreateInfo shader_module_ci = { - .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, - .pNext = nullptr, - .codeSize = view_blob.size_bytes(), - .pCode = view_blob.data() - }; - - ::vk::vk_check(vkCreateShaderModule( - m_device, &shader_module_ci, nullptr, &handle.module), - "vkCreateShaderModule"); - } - - void shader_resource_group::create_module( - std::span p_blob, - const ::vk::shader_source& p_source) { - VkShaderModuleCreateInfo shader_module_ci = { - .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, - .pNext = nullptr, - .codeSize = p_blob.size(), - .pCode = reinterpret_cast(p_blob.data()) - }; - - std::filesystem::path filepath(p_source.filename); - std::string filename = filepath.filename().string(); - - // Setting m_shader_module_handlers[i]'s stage and the VkShaderModule - // handle altogether construct this beforehand and then we are going set - // that shader module - m_modules.emplace(filename, ::vk::shader_handle{}); - ::vk::vk_check( - vkCreateShaderModule( - m_device, &shader_module_ci, nullptr, &m_modules[filename].module), - "vkCreateShaderModule"); - m_modules[filename].stage = p_source.stage; - } - - void shader_resource_group::create_module( - std::span p_blob, - const ::vk::shader_source& p_source) { - VkShaderModuleCreateInfo shader_module_ci = { - .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, - .pNext = nullptr, - .codeSize = p_blob.size_bytes(), - .pCode = p_blob.data() - }; - - // console_log_info("map key = {}", p_source.filename); - std::filesystem::path filepath(p_source.filename); - - std::string filename = filepath.filename().string(); - - console_log_info("Key = {}", filename); - - // Setting m_shader_module_handlers[i]'s stage and the VkShaderModule - // handle altogether construct this beforehand and then we are going set - // that shader module - m_modules.emplace(filename, ::vk::shader_handle{}); - ::vk::vk_check( - vkCreateShaderModule( - m_device, &shader_module_ci, nullptr, &m_modules[filename].module), - "vkCreateShaderModule"); - m_modules[filename].stage = p_source.stage; - } - - std::vector<::vk::shader_handle> shader_resource_group::map_to_vector() - const { - // Using C++'s std::views to extract all of the values in - // unordered_map to a vector - // that gets passed to graphics pipeline - return (m_modules | std::views::values | - std::ranges::to()); - } - - void shader_resource_group::vertex_attributes( - std::span p_attributes) { - /* - -- These comments are a reminder to myself -- - - this function simplifies the need to separately define vertex - attributes and the vertex binding attributes as shown below: - - - vertex attributes specify the types of data within the vertex - - - vertex binding attribute specifies the rate of reading that data - layout specified by the vertex attributes - - - Interpret the following vertex attributes below with this shader - code with `layout(location = n)` specified where by default these are - set to binding zero by the shader - - layout(location = 0) in vec3 inPosition; - layout(location = 1) in vec3 inColor; - layout(location = 2) in vec3 inNormals; - layout(location = 3) in vec2 inTexCoords; - - m_shader_group.set_vertex_attributes(VkVertexInputAttributeDescription{ - { .location = 0, .binding = 0, .format = - VK_FORMAT_R32G32B32_SFLOAT, .offset = offsetof(vk::vertex, position), - }, { .location = 1, .binding = 0, .format = - VK_FORMAT_R32G32B32_SFLOAT, .offset = offsetof(vk::vertex, color), }, - { .location = 2, .binding = 0, .format = - VK_FORMAT_R32G32B32_SFLOAT, .offset = offsetof(vk::vertex, normals), - }, { .location = 3, .binding = 0, .format = VK_FORMAT_R32G32_SFLOAT, - .offset = offsetof(vk::vertex, uv), }, - }); - - m_shader_group.set_vertex_bind_attributes(VkVertexInputBindingDescription{ - {.binding = 0, .stride = sizeof(vk::vertex), .inputRate = - VK_VERTEX_INPUT_RATE_VERTEX,}, - }); - - Which gets handled in specifying the following below - */ - - m_vertex_binding_attributes.resize(p_attributes.size()); - - for (size_t i = 0; i < m_vertex_binding_attributes.size(); i++) { - // setting up vertex binding - const ::vk::vertex_attribute attribute = p_attributes[i]; - m_vertex_attributes.resize(attribute.entries.size()); - m_vertex_binding_attributes[i] = { .binding = attribute.binding, - .stride = attribute.stride, - .inputRate = to_input_rate( - attribute.input_rate) }; - - // then setting up the vertex attributes for the vertex data layouts - for (size_t j = 0; j < attribute.entries.size(); j++) { - const ::vk::vertex_attribute_entry entry = attribute.entries[j]; - m_vertex_attributes[j] = { .location = entry.location, - .binding = attribute.binding, - .format = static_cast( - entry.format), - .offset = entry.stride }; - } - } - } - - void shader_resource_group::destroy() { - - for (auto& [filename, shader_handle] : m_modules) { - if (shader_handle.module != nullptr) { - vkDestroyShaderModule(m_device, shader_handle.module, nullptr); - } - } - } -}; \ No newline at end of file diff --git a/src/atlas/drivers/vulkan-cpp/utilities.cpp b/src/atlas/drivers/vulkan-cpp/utilities.cpp deleted file mode 100644 index 8ed7c863..00000000 --- a/src/atlas/drivers/vulkan-cpp/utilities.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include -#include -#include -#include -#include - -namespace atlas::vk { - - void vk_check(const VkResult& p_result, - const std::string& p_name, - const std::source_location& p_source) { - if (p_result != VK_SUCCESS) { - console_log_error_tagged( - "vulkan", - "File {} on line {} failed VkResult check", - std::filesystem::relative(p_source.file_name()).string(), - p_source.line()); - console_log_error_tagged("vulkan", - "Current Function Location = {}", - p_source.function_name()); - console_log_error_tagged( - "vulkan", "{} VkResult returned: {}", p_name, (int)p_result); - } - } -}; \ No newline at end of file diff --git a/src/atlas/drivers/vulkan-cpp/vk_context.cpp b/src/atlas/drivers/vulkan-cpp/vk_context.cpp deleted file mode 100644 index 06e6e952..00000000 --- a/src/atlas/drivers/vulkan-cpp/vk_context.cpp +++ /dev/null @@ -1,170 +0,0 @@ -#include -#include -#include -#include - -namespace atlas::vk { - std::vector initialize_instance_extensions() { - std::vector extension_names; - - extension_names.emplace_back(VK_KHR_SURFACE_EXTENSION_NAME); - extension_names.emplace_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); - - // An additional surface extension needs to be loaded. This extension is - // platform-specific so needs to be selected based on the platform the - // example is going to be deployed to. Preprocessor directives are used - // here to select the correct platform. -#ifdef VK_USE_PLATFORM_WIN32_KHR - extension_names.emplace_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); -#endif -#ifdef VK_USE_PLATFORM_XLIB_KHR - extensionNames.emplace_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME); -#endif -#ifdef VK_USE_PLATFORM_XCB_KHR - extensionNames.emplace_back(VK_KHR_XCB_SURFACE_EXTENSION_NAME); -#endif -#ifdef VK_USE_PLATFORM_ANDROID_KHR - extensionNames.emplace_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME); -#endif -#ifdef VK_USE_PLATFORM_WAYLAND_KHR - extensionNames.emplace_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME); -#endif -#ifdef VK_USE_PLATFORM_MACOS_MVK - extensionNames.emplace_back(VK_MVK_MACOS_SURFACE_EXTENSION_NAME); -#endif -#ifdef USE_PLATFORM_NULLWS - extensionNames.emplace_back(VK_KHR_DISPLAY_EXTENSION_NAME); -#endif - - return extension_names; - } - -#ifdef _DEBUG - const std::vector validation_layers = { - "VK_LAYER_KHRONOS_validation", - "VK_LAYER_KHRONOS_synchronization2" - }; - - static VKAPI_ATTR VkBool32 VKAPI_CALL debug_callback( - [[maybe_unused]] VkDebugUtilsMessageSeverityFlagBitsEXT - p_message_severity, - [[maybe_unused]] VkDebugUtilsMessageTypeFlagsEXT p_message_type, - const VkDebugUtilsMessengerCallbackDataEXT* p_callback_data, - [[maybe_unused]] void* p_user_data) { - console_log_trace("validation layer:\t\t{}", p_callback_data->pMessage); - return false; - } -#endif - - vk_context* vk_context::s_instance = nullptr; - - vk_context::vk_context(const std::string& p_tag) { - console_log_manager::create_new_logger(p_tag); - - VkApplicationInfo app_info = { - .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, - .pNext = nullptr, - .applicationVersion = 1, - .pEngineName = "TheAtlasEngine", - .engineVersion = 1, - .apiVersion = VK_API_VERSION_1_3, - }; - - VkInstanceCreateInfo create_info = { - .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .pApplicationInfo = &app_info - }; - - //! @note Setting up the required extensions for vulkan - std::vector extensions = initialize_instance_extensions(); -#if _DEBUG - extensions.push_back("VK_EXT_debug_utils"); -#endif - create_info.enabledExtensionCount = - static_cast(extensions.size()); - create_info.ppEnabledExtensionNames = extensions.data(); -#ifdef _DEBUG - // by default we enable validation layers used for debugging! - create_info.enabledLayerCount = - static_cast(validation_layers.size()); - create_info.ppEnabledLayerNames = validation_layers.data(); - - // printing out available validation layers - uint32_t layer_count; - std::vector available_validation_layers; - vkEnumerateInstanceLayerProperties(&layer_count, nullptr); - - available_validation_layers.resize(layer_count); - vkEnumerateInstanceLayerProperties(&layer_count, - available_validation_layers.data()); - - console_log_trace("================================================"); - console_log_trace("\tValidation Layers Available"); - console_log_trace("================================================"); - for (VkLayerProperties properties : available_validation_layers) { - console_log_trace("Validation Layer:\t\t{}", properties.layerName); - console_log_trace("Description\t\t{}", properties.description); - console_log_trace("Version\t\t\t{}", (int)properties.specVersion); - } - - console_log_trace("\n"); - console_log_trace("================================================\n"); - - VkDebugUtilsMessengerCreateInfoEXT debug_create_info = { - .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, - .messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | - VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | - VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT, - .messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | - VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | - VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT, - .pfnUserCallback = debug_callback, - }; - - create_info.pNext = - (VkDebugUtilsMessengerCreateInfoEXT*)&debug_create_info; -#else - create_info.enabledLayerCount = 0; - create_info.ppEnabledLayerNames = nullptr; - create_info.pNext = nullptr; -#endif - vk_check(vkCreateInstance(&create_info, nullptr, &m_instance_handler), - "vkCreateInstance"); - -#if _DEBUG - // This needs to be created after the VkInstance is or else it wont be - // applied the debug information during validation layer error message - // execution - m_vk_set_debug_utils_object_name_ext = - reinterpret_cast( - vkGetInstanceProcAddr(m_instance_handler, - "vkSetDebugUtilsObjectNameEXT")); -#endif - s_instance = this; - - m_physical = vk_physical_driver(m_instance_handler); - m_driver = vk_driver(m_physical); - } - - VkInstance vk_context::handler() { - return s_instance->m_instance_handler; - } - - void vk_context::resource_free(std::function&& p_resource) { - m_resources_free.push_back(p_resource); - } - - void vk_context::submit_resource_free(std::function&& p_resource) { - s_instance->m_resources_free.push_back(p_resource); - } - - void vk_context::destroy_context() { - for (auto& callback : m_resources_free) { - callback(); - } - - m_driver.destroy(); - } -}; diff --git a/src/atlas/drivers/vulkan-cpp/vk_driver.cpp b/src/atlas/drivers/vulkan-cpp/vk_driver.cpp deleted file mode 100644 index 5207712f..00000000 --- a/src/atlas/drivers/vulkan-cpp/vk_driver.cpp +++ /dev/null @@ -1,123 +0,0 @@ -#include -#include -#include -#include -#include - -namespace atlas::vk { - static VkFormat search_supported_depth_format( - const VkPhysicalDevice& p_physical, - std::span p_formats, - VkImageTiling p_tiling, - VkFormatFeatureFlags p_feature_flag) { - VkFormat format = VK_FORMAT_UNDEFINED; - - for (size_t i = 0; i < p_formats.size(); i++) { - VkFormat current_format = p_formats[i]; - VkFormatProperties format_properties; - vkGetPhysicalDeviceFormatProperties( - p_physical, current_format, &format_properties); - - if (p_tiling == VK_IMAGE_TILING_LINEAR) { - if (format_properties.linearTilingFeatures & p_feature_flag) { - format = current_format; - } - } - else if (p_tiling == VK_IMAGE_TILING_OPTIMAL and - format_properties.optimalTilingFeatures & p_feature_flag) { - format = current_format; - } - } - - return format; - } - - static VkFormat search_depth_format(const VkPhysicalDevice& p_physical) { - std::vector candidate_formats = { - VK_FORMAT_D32_SFLOAT, - VK_FORMAT_D32_SFLOAT_S8_UINT, - VK_FORMAT_D24_UNORM_S8_UINT - }; - - VkFormat format = search_supported_depth_format( - p_physical, - candidate_formats, - VK_IMAGE_TILING_OPTIMAL, - VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT); - return format; - } - - vk_driver::vk_driver(const vk_physical_driver& p_physical) - : m_physical(p_physical) { - m_depth_format_selected = search_depth_format(m_physical); - - float queue_priority[1] = { 0.0f }; - - std::vector device_extension = { - VK_KHR_SWAPCHAIN_EXTENSION_NAME - }; - - uint32_t graphics_index = - m_physical.read_queue_family_indices().graphics; - - VkDeviceQueueCreateInfo queue_create_info = { - .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .queueFamilyIndex = graphics_index, - .queueCount = 1, - .pQueuePriorities = queue_priority, - }; - - VkDeviceCreateInfo create_info = { - .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .queueCreateInfoCount = 1, - .pQueueCreateInfos = &queue_create_info, - .enabledLayerCount = 0, - .ppEnabledLayerNames = nullptr, - .enabledExtensionCount = - static_cast(device_extension.size()), - .ppEnabledExtensionNames = device_extension.data(), - }; - - VkPhysicalDeviceFeatures features; - vkGetPhysicalDeviceFeatures(m_physical, &features); - features.robustBufferAccess = false; - create_info.pEnabledFeatures = &features; - - vk_check(vkCreateDevice(m_physical, &create_info, nullptr, &m_driver), - "vkCreateDevice"); - - vkGetDeviceQueue( - m_driver, graphics_index, 0, &m_device_queues.graphics_queue); - } - - //! @note Returns -1 if there are no flags available/compatible/valid - uint32_t vk_driver::select_memory_type( - uint32_t p_type_filter, - VkMemoryPropertyFlags p_property_flag) { - VkPhysicalDeviceMemoryProperties mem_props; - vkGetPhysicalDeviceMemoryProperties(m_physical, &mem_props); - - for (uint32_t i = 0; i < mem_props.memoryTypeCount; i++) { - if ((p_type_filter & (1 << i)) and - (mem_props.memoryTypes[i].propertyFlags & p_property_flag) == - p_property_flag) { - return i; - } - } - - return -1; - } - - VkFormat vk_driver::depth_format() const { - return m_depth_format_selected; - } - - void vk_driver::destroy() { - vkDeviceWaitIdle(m_driver); - vkDestroyDevice(m_driver, nullptr); - } -}; \ No newline at end of file diff --git a/src/atlas/drivers/vulkan-cpp/vk_imgui.cpp b/src/atlas/drivers/vulkan-cpp/vk_imgui.cpp deleted file mode 100644 index 18deb51a..00000000 --- a/src/atlas/drivers/vulkan-cpp/vk_imgui.cpp +++ /dev/null @@ -1,294 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -namespace atlas::vk { - - static void im_gui_layout_color_modification() { - auto& colors = ImGui::GetStyle().Colors; // @note Colors is ImVec4 - - colors[ImGuiCol_WindowBg] = ImVec4{ 0.1f, 0.105f, 0.11f, 1.0f }; - - // Headers - colors[ImGuiCol_Header] = ImVec4{ 0.2f, 0.205f, 0.21f, 1.0f }; - colors[ImGuiCol_HeaderHovered] = ImVec4{ 0.3f, 0.305f, 0.31f, 1.0f }; - colors[ImGuiCol_HeaderActive] = ImVec4{ 0.15f, 0.1505f, 0.151f, 1.0f }; - - // Buttons - colors[ImGuiCol_Button] = ImVec4{ 0.2f, 0.205f, 0.21f, 1.0f }; - colors[ImGuiCol_ButtonHovered] = ImVec4{ 0.3f, 0.305f, 0.31f, 1.0f }; - colors[ImGuiCol_ButtonActive] = ImVec4{ 0.15f, 0.1505f, 0.151f, 1.0f }; - - // Frame BG - colors[ImGuiCol_FrameBg] = ImVec4{ 0.2f, 0.205f, 0.21f, 1.0f }; - colors[ImGuiCol_FrameBgHovered] = ImVec4{ 0.3f, 0.305f, 0.31f, 1.0f }; - colors[ImGuiCol_FrameBgActive] = ImVec4{ 0.15f, 0.1505f, 0.151f, 1.0f }; - - // Tabs - colors[ImGuiCol_Tab] = ImVec4{ 0.15f, 0.1505f, 0.151f, 1.0f }; - colors[ImGuiCol_TabHovered] = ImVec4{ 0.38f, 0.3805f, 0.381f, 1.0f }; - colors[ImGuiCol_TabActive] = ImVec4{ 0.28f, 0.2805f, 0.281f, 1.0f }; - colors[ImGuiCol_TabUnfocused] = ImVec4{ 0.15f, 0.1505f, 0.15f, 1.0f }; - colors[ImGuiCol_TabUnfocusedActive] = - ImVec4{ 0.2f, 0.205f, 0.21f, 1.0f }; - - // Titles - colors[ImGuiCol_TitleBg] = ImVec4{ 0.15f, 0.1505f, 0.151f, 1.0f }; - colors[ImGuiCol_TitleBgActive] = ImVec4{ 0.15f, 0.1505f, 0.15f, 1.0f }; - colors[ImGuiCol_TitleBgCollapsed] = - ImVec4{ 0.1f, 0.150f, 0.951f, 1.0f }; - } - - imgui_context::imgui_context(const ref& p_window_ctx) { - m_instance = vk_context::handler(); - m_physical = vk_context::physical_driver(); - m_driver = vk_context::driver_context(); - - m_current_swapchain_handler = p_window_ctx->current_swapchain(); - - // Setting up imgui - IMGUI_CHECKVERSION(); - ImGui::CreateContext(); - ImGuiIO& io = ImGui::GetIO(); - - io.ConfigFlags |= - ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls - // io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable - // Gamepad Controls - io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking - io.ConfigFlags |= - ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform - // Windows - - // io.ConfigViewportsNoAutoMerge = true; - // io.ConfigViewportsNoAutoMerge = true; - // io.ConfigViewportsNoTaskBarIcon = true; - - // Setup Dear ImGui style - // ImGui::StyleColorsDark(); - // ImGui::StyleColorsClassic(); - im_gui_layout_color_modification(); - - ImGuiStyle& style = ImGui::GetStyle(); - if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { - style.WindowRounding = 0.0f; - style.Colors[ImGuiCol_WindowBg].w = 1.0f; - } - - m_viewport_command_buffers.resize( - p_window_ctx->current_swapchain().image_size()); - - for (size_t i = 0; i < m_viewport_command_buffers.size(); i++) { - ::vk::command_params settings = { - .levels = ::vk::command_levels::primary, - // .queue_index = enumerate_swapchain_settings.present_index, - .queue_index = 0, - .flags = ::vk::command_pool_flags::reset, - }; - m_viewport_command_buffers[i] = - ::vk::command_buffer(m_driver, settings); - } - - // ::vk::descriptor_res - // m_imgui_descriptor = ::vk::descriptor_resource(m_driver, {}); - // 1: create descriptor pool for IMGUI - // the size of the pool is very oversize, but it's copied from imgui - // demo itself. - std::array pool_sizes = { - VkDescriptorPoolSize{ VK_DESCRIPTOR_TYPE_SAMPLER, 100 }, - VkDescriptorPoolSize{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - 100 }, - VkDescriptorPoolSize{ VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 100 }, - VkDescriptorPoolSize{ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 100 }, - VkDescriptorPoolSize{ VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, - 100 }, - VkDescriptorPoolSize{ VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, - 100 }, - VkDescriptorPoolSize{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 100 }, - VkDescriptorPoolSize{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 100 }, - VkDescriptorPoolSize{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, - 100 }, - VkDescriptorPoolSize{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, - 100 }, - VkDescriptorPoolSize{ VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 100 } - }; - - VkDescriptorPoolCreateInfo desc_pool_create_info = { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, - .pNext = nullptr, - .flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, - .maxSets = static_cast(1000 * pool_sizes.size()), - // .poolSizeCount = (uint32_t)std::size(pool_sizes), - .poolSizeCount = static_cast(pool_sizes.size()), - .pPoolSizes = pool_sizes.data() - }; - - // VkDescriptorPool imgui_pool; - vk::vk_check(vkCreateDescriptorPool( - m_driver, &desc_pool_create_info, nullptr, &m_desc_pool), - "vkCreateDescriptorPool"); - - create(*p_window_ctx, - p_window_ctx->current_swapchain().image_size(), - p_window_ctx->current_swapchain().swapchain_renderpass()); - } - - void imgui_context::create(GLFWwindow* p_window_handler, - const uint32_t& p_image_size, - const VkRenderPass& p_current_renderpass) { - ImGui_ImplGlfw_InitForVulkan(p_window_handler, true); - ImGui_ImplVulkan_InitInfo init_info = {}; - init_info.Instance = m_instance; - init_info.PhysicalDevice = m_physical; - init_info.Device = m_driver; - init_info.Queue = m_driver.graphics_queue(); - init_info.RenderPass = p_current_renderpass; - init_info.PipelineCache = nullptr; - init_info.DescriptorPool = m_desc_pool; - init_info.MinImageCount = 2; - init_info.ImageCount = p_image_size; - init_info.UseDynamicRendering = false; - init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT; - ImGui_ImplVulkan_Init(&init_info); - } - - void imgui_context::begin(const VkCommandBuffer& p_current, - const uint32_t& p_frame_index) { - ImGui_ImplVulkan_NewFrame(); - ImGui_ImplGlfw_NewFrame(); - ImGui::NewFrame(); - - m_current_frame_index = p_frame_index; - m_current = p_current; - } - - void imgui_context::end() { - ImGui::Render(); - - ImDrawData* draw_data = ImGui::GetDrawData(); - ImGui_ImplVulkan_RenderDrawData(draw_data, m_current); - - ImGuiIO& io = ImGui::GetIO(); - if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { - ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindowsDefault(); - } - } - - void imgui_context::draw_hud(const hud_data& p_hud_data, - const window_settings& p_settings) { - ImGuiIO& io = ImGui::GetIO(); - - // --- Top-Left HUD (FPS and Weapon) --- - ImGui::SetNextWindowPos(ImVec2(10, 10), - ImGuiCond_Always); // Fixed position - ImGui::SetNextWindowBgAlpha(0.0f); // Transparent background - - ImGuiWindowFlags hud_flags = - ImGuiWindowFlags_NoDecoration | // No title bar, borders, etc. - ImGuiWindowFlags_NoMove | // Cannot be moved by user - ImGuiWindowFlags_NoResize | // Cannot be resized by user - ImGuiWindowFlags_NoSavedSettings | // Don't save position/size to .ini - ImGuiWindowFlags_NoInputs; // Does not block mouse/keyboard input - - if (ImGui::Begin("TopLeftHUD", nullptr, hud_flags)) { - ImGui::Text("FPS: %.1f", p_hud_data.fps); - ImGui::Text("Weapon: %s", p_hud_data.currentWeapon.c_str()); - // ImGui::Text("Health:"); - // Use a progress bar for health - // ImGui::ProgressBar(p_hud_data.playerHealth / 100.0f, ImVec2(-1, - // 0), "#Health"); // -1 width fills available space - ImGui::Text("Health: %.1f", p_hud_data.playerHealth); - ImGui::ProgressBar(p_hud_data.playerHealth / 100.0f, - ImVec2(-1, 0)); // No overlay text here - } - ImGui::End(); - - // --- Health Bar (Bottom-Left) --- - ImGui::SetNextWindowPos(ImVec2(10, (float)p_settings.width - 50), - ImGuiCond_Always); - ImGui::SetNextWindowSize(ImVec2(200, 40), - ImGuiCond_Always); // Fixed size - ImGui::SetNextWindowBgAlpha(0.0f); - - // if (ImGui::Begin("HealthBar", nullptr, hud_flags)) { - // ImGui::Text("Health:"); - // // Use a progress bar for health - // ImGui::ProgressBar(p_hud_data.playerHealth / 100.0f, ImVec2(-1, - // 0), "HP"); // -1 width fills available space - // } - // ImGui::End(); - - // --- Score (Top-Right) --- - // Position it relative to the right edge - ImGui::SetNextWindowPos(ImVec2((float)p_settings.width - 150, 10), - ImGuiCond_Always); - ImGui::SetNextWindowSize( - ImVec2(140, 0), - ImGuiCond_Always); // 0 height means auto-size vertically - ImGui::SetNextWindowBgAlpha(0.0f); - - if (ImGui::Begin("ScoreDisplay", nullptr, hud_flags)) { - ImGui::SetCursorPosX( - ImGui::GetWindowSize().x - - ImGui::CalcTextSize( - std::to_string(p_hud_data.playerScore).c_str()) - .x - - ImGui::GetStyle().WindowPadding.x); - ImGui::Text("Score: %d", p_hud_data.playerScore); - } - ImGui::End(); - - // --- Crosshair (Custom Drawing - Optional) --- - // If you want a more custom crosshair, you might use ImDrawList. - // This is more advanced and often handled by your game's rendering - // pipeline directly, but ImGui can draw simple shapes. To draw on the - // main viewport, you can get the foreground draw list of the main - // viewport. - ImDrawList* draw_list = - ImGui::GetBackgroundDrawList(); // Or ImGui::GetForegroundDrawList() - if (draw_list) { - ImVec2 center = - ImVec2(io.DisplaySize.x * 0.5f, io.DisplaySize.y * 0.5f); - float crosshair_size = 10.0f; - float gap = 2.0f; - ImU32 crosshair_color = IM_COL32(255, 255, 255, 255); // White - - // Horizontal lines - draw_list->AddLine( - ImVec2(center.x - crosshair_size - gap, center.y), - ImVec2(center.x - gap, center.y), - crosshair_color); - draw_list->AddLine( - ImVec2(center.x + gap, center.y), - ImVec2(center.x + crosshair_size + gap, center.y), - crosshair_color); - - // Vertical lines - draw_list->AddLine( - ImVec2(center.x, center.y - crosshair_size - gap), - ImVec2(center.x, center.y - gap), - crosshair_color); - draw_list->AddLine( - ImVec2(center.x, center.y + gap), - ImVec2(center.x, center.y + crosshair_size + gap), - crosshair_color); - } - } - - void imgui_context::destroy() { - ImGui_ImplVulkan_Shutdown(); - vkDestroyDescriptorPool(m_driver, m_desc_pool, nullptr); - - for (auto& command_buffer : m_viewport_command_buffers) { - command_buffer.destroy(); - } - - ImGui_ImplGlfw_Shutdown(); - ImGui::DestroyContext(); - } -}; \ No newline at end of file diff --git a/src/atlas/drivers/vulkan-cpp/vk_physical_driver.cpp b/src/atlas/drivers/vulkan-cpp/vk_physical_driver.cpp deleted file mode 100644 index 3dc6945d..00000000 --- a/src/atlas/drivers/vulkan-cpp/vk_physical_driver.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include -#include -#include - -namespace atlas::vk { - - vk_physical_driver::vk_physical_driver(const VkInstance& p_instance) { - - uint32_t device_count = 0; - vkEnumeratePhysicalDevices(p_instance, &device_count, nullptr); - - if (device_count == 0) { - console_log_fatal("Device Count is {} and no devices found!!!", - device_count); - return; - } - - std::vector physical_drivers(device_count); - vkEnumeratePhysicalDevices( - p_instance, &device_count, physical_drivers.data()); - - for (const auto& device : physical_drivers) { - VkPhysicalDeviceProperties device_properties; - VkPhysicalDeviceFeatures device_features; - vkGetPhysicalDeviceProperties(device, &device_properties); - vkGetPhysicalDeviceFeatures(device, &device_features); - if (device_properties.deviceType == - VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) { - m_physical_driver = device; - break; - } - } - - uint32_t queue_family_count = 0; - vkGetPhysicalDeviceQueueFamilyProperties( - m_physical_driver, &queue_family_count, nullptr); - m_queue_family_properties.resize(queue_family_count); - - vkGetPhysicalDeviceQueueFamilyProperties( - m_physical_driver, - &queue_family_count, - m_queue_family_properties.data()); - - m_queue_indices = select_queue_family_indices(); - } - - vk_physical_driver::~vk_physical_driver() = default; - - surface_properties vk_physical_driver::get_surface_properties( - const VkSurfaceKHR& p_surface) { - vk_check(vkGetPhysicalDeviceSurfaceCapabilitiesKHR( - m_physical_driver, - p_surface, - &m_surface_properties.surface_capabilities), - "vkGetPhysicalDeviceSurfaceCapabilitiesKHR"); - - uint32_t format_count = 0; - std::vector formats; - vk_check(vkGetPhysicalDeviceSurfaceFormatsKHR( - m_physical_driver, p_surface, &format_count, nullptr), - "vkGetPhysicalDeviceSurfaceFormatsKHR"); - - formats.resize(format_count); - - vk_check(vkGetPhysicalDeviceSurfaceFormatsKHR( - m_physical_driver, p_surface, &format_count, formats.data()), - "vkGetPhysicalDeviceSurfaceFormatsKHR"); - - for (const auto& format : formats) { - if (format.format == VK_FORMAT_B8G8R8A8_SRGB && - format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { - m_surface_properties.surface_format = format; - } - } - - m_surface_properties.surface_format = formats[0]; - - return m_surface_properties; - } - - vk_physical_driver::queue_family_indices - vk_physical_driver::select_queue_family_indices() { - VkPhysicalDeviceMemoryProperties physical_device_memory_properties; - vkGetPhysicalDeviceMemoryProperties(m_physical_driver, - &physical_device_memory_properties); - vk_physical_driver::queue_family_indices indices; - int i = 0; - - for (const auto& queue_family : m_queue_family_properties) { - if (queue_family.queueFlags & VK_QUEUE_GRAPHICS_BIT) { - indices.graphics = i; - break; - } - i++; - } - - return indices; - } - - uint32_t vk_physical_driver::read_presentation_index( - const VkSurfaceKHR& p_surface) { - uint32_t presentation_index = -1; - VkBool32 compatible = VK_FALSE; - uint32_t i = 0; - for (const auto& queue_family : m_queue_family_properties) { - if (queue_family.queueFlags & VK_QUEUE_GRAPHICS_BIT) { - vk_check(vkGetPhysicalDeviceSurfaceSupportKHR( - m_physical_driver, i, p_surface, &compatible), - "vkGetPhysicalDeviceSurfaceSupportKHR"); - - if (compatible) { - presentation_index = i; - } - } - i++; - } - - return presentation_index; - } - - VkPhysicalDeviceMemoryProperties vk_physical_driver::memory_properties() - const { - VkPhysicalDeviceMemoryProperties physical_memory_properties; - vkGetPhysicalDeviceMemoryProperties(m_physical_driver, - &physical_memory_properties); - return physical_memory_properties; - } -}; \ No newline at end of file diff --git a/src/atlas/drivers/vulkan-cpp/vk_renderer.cpp b/src/atlas/drivers/vulkan-cpp/vk_renderer.cpp deleted file mode 100644 index bc79f807..00000000 --- a/src/atlas/drivers/vulkan-cpp/vk_renderer.cpp +++ /dev/null @@ -1,561 +0,0 @@ -#include -#include -#include -#include -#include -#include - -namespace atlas::vk { - - vk_renderer::vk_renderer(const window_settings& p_settings, - uint32_t p_image_size, - const std::string& p_tag) { - console_log_manager::create_new_logger(p_tag); - m_device = vk_context::driver_context(); - m_physical = vk_context::physical_driver(); - m_window_extent = p_settings; - - m_image_count = p_image_size; -#ifdef USE_SHADERC - std::array<::vk::shader_source, 2> shader_sources = { - ::vk::shader_source{ - "experimental-shaders/test.vert", - ::vk::shader_stage::vertex, - }, - ::vk::shader_source{ - "experimental-shaders/test.frag", - ::vk::shader_stage::fragment, - } - }; -#else - std::array<::vk::shader_source, 2> shader_sources = { - ::vk::shader_source{ - "experimental-shaders/test.vert.spv", - ::vk::shader_stage::vertex, - }, - ::vk::shader_source{ - "experimental-shaders/test.frag.spv", - ::vk::shader_stage::fragment, - } - }; -#endif - - std::array<::vk::vertex_attribute_entry, 4> attribute_entries = { - ::vk::vertex_attribute_entry{ - .location = 0, - .format = ::vk::format::rgb32_sfloat, - .stride = offsetof(vk::vertex_input, position), - }, - ::vk::vertex_attribute_entry{ - .location = 1, - .format = ::vk::format::rgb32_sfloat, - .stride = offsetof(vk::vertex_input, color), - }, - ::vk::vertex_attribute_entry{ - .location = 2, - .format = ::vk::format::rgb32_sfloat, - .stride = offsetof(vertex_input, normals), - }, - ::vk::vertex_attribute_entry{ - .location = 3, - .format = ::vk::format::rg32_sfloat, - .stride = offsetof(vertex_input, uv), - } - }; - - std::array<::vk::vertex_attribute, 1> attribute = { - ::vk::vertex_attribute{ - // layout (set = 0, binding = 0) - .binding = 0, - .entries = attribute_entries, - .stride = sizeof(vk::vertex_input), - .input_rate = ::vk::input_rate::vertex, - }, - }; - - ::vk::shader_resource_info shader_info = { - .sources = shader_sources, - }; - - try { - m_shader_group = shader_resource_group(m_device, shader_info); - m_shader_group.vertex_attributes(attribute); - } - catch (std::runtime_error& e) { - console_log_error("Compilation Error!!"); - console_log_error("{}", e.what()); - } - // Setting global descriptor set 0 - std::vector<::vk::descriptor_entry> set0_entries = { - ::vk::descriptor_entry{ - // specifies "layout (set = 0, binding = 0) uniform GlobalUbo" - .type = ::vk::buffer::uniform, - .binding_point = { - .binding = 0, - .stage = ::vk::shader_stage::vertex, - }, - .descriptor_count = 1, - }, - ::vk::descriptor_entry{ - // specifies "layout (set = 0, binding = 1) uniform light_ubo" - .type = ::vk::buffer::uniform, - .binding_point = { - .binding = 1, - .stage = ::vk::shader_stage::fragment, - }, - .descriptor_count = 1, - }, - }; - - // uint32_t image_count = image_count; - ::vk::descriptor_layout set0_layout = { - .slot = 0, - .max_sets = m_image_count, - .entries = set0_entries, - }; - m_global_descriptors = ::vk::descriptor_resource(m_device, set0_layout); - - ::vk::uniform_params global_info = { - .phsyical_memory_properties = m_physical.memory_properties(), - .size_bytes = sizeof(global_ubo), - .debug_name = "\nm_global_uniforms\n", - .vkSetDebugUtilsObjectNameEXT = vk_context::get_debug_object_name() - }; - m_global_uniforms = ::vk::uniform_buffer(m_device, global_info); - - // setting up our light uniforms as the global uniforms rather then - // per-object basis - ::vk::uniform_params light_ubo_params = { - .phsyical_memory_properties = m_physical.memory_properties(), - .size_bytes = sizeof(light_scene_ubo), - }; - m_point_light_uniforms = - ::vk::uniform_buffer(m_device, light_ubo_params); - - std::array<::vk::write_buffer, 1> binding0_uniforms = { - ::vk::write_buffer{ - .buffer = m_global_uniforms, - .offset = 0, - .range = m_global_uniforms.size_bytes(), - }, - }; - - std::array<::vk::write_buffer, 1> binding1_uniforms = { - ::vk::write_buffer{ - .buffer = m_point_light_uniforms, - .offset = 0, - .range = m_point_light_uniforms.size_bytes(), - }, - }; - - std::array<::vk::write_buffer_descriptor, 2> set0_write_buffers = { - ::vk::write_buffer_descriptor{ - .dst_binding = 0, - .uniforms = binding0_uniforms, - }, - ::vk::write_buffer_descriptor{ - .dst_binding = 1, - .uniforms = binding1_uniforms, - } - }; - m_global_descriptors.update(set0_write_buffers); - - m_sets_layouts = { - m_global_descriptors.layout(), - }; - - ::vk::image_extent extent = { - .width = 1, - .height = 1, - }; - m_white_texture = - ::vk::texture(m_device, extent, m_physical.memory_properties()); - - vk_context::submit_resource_free([this]() { - m_white_texture.destroy(); - m_shader_group.destroy(); - m_global_descriptors.destroy(); - m_global_uniforms.destroy(); - m_point_light_uniforms.destroy(); - for (auto& [id, mesh] : m_cached_meshes) { - console_log_trace("Entity \"{}\" Destroyed in vk_renderer!!!", - id); - mesh.destroy(); - } - - for (auto& [id, uniform] : m_mesh_geometry_set) { - uniform.destroy(); - } - - for (auto& [id, material_uniform] : m_mesh_material_set) { - material_uniform.destroy(); - } - - for (auto& [key, descriptor_map] : m_mesh_descriptors) { - for (auto& [descriptor_type, descriptor] : descriptor_map) { - descriptor.destroy(); - } - } - m_main_pipeline.destroy(); - }); - } - - void vk_renderer::current_scene(ref p_scene) { - m_current_scene = p_scene; - } - - void vk_renderer::background_color(const std::array& p_color) { - m_color = { - { p_color.at(0), p_color.at(1), p_color.at(2), p_color.at(3) } - }; - } - - void vk_renderer::preload_assets(const VkRenderPass& p_renderpass) { - m_final_renderpass = p_renderpass; - // set 1 -- material uniforms - // ref current_world = system_registry::get_world("Editor - // World"); ref current_scene = - // current_world->get_scene("LevelScene"); - - flecs::query<> caching = - m_current_scene->query_builder().build(); - - caching.each([this](flecs::entity p_entity) { - const mesh_source* target = p_entity.get(); - mesh new_mesh(std::filesystem::path(target->model_path), - target->flip); - - // we do a check if the geometry uniform associated with this game - // object is valid - if (!m_mesh_geometry_set.contains(p_entity.id())) { - ::vk::uniform_params geo_info = { - .phsyical_memory_properties = - m_physical.memory_properties(), - .size_bytes = sizeof(material_uniform), - }; - m_mesh_geometry_set[p_entity.id()] = - ::vk::uniform_buffer(m_device, geo_info); - } - - // check if material is already associated with this particular game - // object - if (!m_mesh_material_set.contains(p_entity.id())) { - ::vk::uniform_params mat_info = { - .phsyical_memory_properties = - m_physical.memory_properties(), - .size_bytes = sizeof(material_metadata), - }; - m_mesh_material_set[p_entity.id()] = - ::vk::uniform_buffer(m_device, mat_info); - } - - new_mesh.add_diffuse(std::filesystem::path(target->diffuse)); - new_mesh.add_specular(std::filesystem::path(target->specular)); - - if (new_mesh.loaded()) { - m_cached_meshes.emplace(p_entity.id(), new_mesh); - - std::vector<::vk::descriptor_entry> set1_entries = { - ::vk::descriptor_entry{ - // specifies "layout (set = 1, binding = 0) uniform geometry_uniform" - .type = ::vk::buffer::uniform, - .binding_point = { - .binding = 0, - .stage = ::vk::shader_stage::vertex, - }, - .descriptor_count = 1, - }, - ::vk::descriptor_entry{ - // specifies "layout (set = 1, binding = 1) uniform sampler2D diffuse_texture" - .type = ::vk::buffer::combined_image_sampler, - .binding_point = { - .binding = 1, - .stage = ::vk::shader_stage::fragment, - }, - .descriptor_count = 1, - }, - ::vk::descriptor_entry{ - // specifies "layout (set = 1, binding = 2) uniform sampler2D specular_texture" - .type = ::vk::buffer::combined_image_sampler, - .binding_point = { - .binding = 2, - .stage = ::vk::shader_stage::fragment, - }, - .descriptor_count = 1, - }, - ::vk::descriptor_entry{ - // specifies "layout (set = 1, binding = 3) uniform sampler2D material_ubo" - .type = ::vk::buffer::uniform, - .binding_point = { - .binding = 3, - .stage = ::vk::shader_stage::fragment, - }, - .descriptor_count = 1, - }, - }; - - ::vk::descriptor_layout set1_layout = { - .slot = 1, - .max_sets = m_image_count, - .entries = set1_entries, - }; - - m_mesh_descriptors[p_entity.id()].emplace( - "materials", - ::vk::descriptor_resource(m_device, set1_layout)); - - // specify to the vk::write_descriptor_buffer - std::array<::vk::write_buffer, 1> binding0_buffers = { - ::vk::write_buffer{ - .buffer = m_mesh_geometry_set[p_entity.id()], - .offset = 0, - .range = m_mesh_geometry_set[p_entity.id()].size_bytes(), - } - }; - - std::array<::vk::write_buffer, 1> binding3_buffers = { - ::vk::write_buffer{ - .buffer = m_mesh_material_set[p_entity.id()], - .offset = 0, - .range = m_mesh_material_set[p_entity.id()].size_bytes(), - } - }; - - std::vector<::vk::write_buffer_descriptor> material_uniforms = { - // layout(set= 1, binding = 0) geometry_ubo - ::vk::write_buffer_descriptor{ - .dst_binding = 0, - .uniforms = binding0_buffers, - }, - // layout(set= 1, binding = 3) material_ubo - ::vk::write_buffer_descriptor{ - .dst_binding = 3, - .uniforms = binding3_buffers, - }, - }; - - // layout(set = 1, binding = 1) - // If the texture loaded successfully then we use that texture, - // otherwise utilize the default white texture - ::vk::sample_image diffuse = - m_cached_meshes[p_entity.id()].diffuse_loaded() - ? m_cached_meshes[p_entity.id()].diffuse() - : m_white_texture.image(); - - // layout(set = 1, binding = 2) - ::vk::sample_image specular = - m_cached_meshes[p_entity.id()].specular_loaded() - ? m_cached_meshes[p_entity.id()].specular() - : m_white_texture.image(); - - // writes to texture at layout(set = 1, binding = 1) - std::array<::vk::write_image, 1> - binding1_images = { ::vk::write_image{ - .sampler = diffuse.sampler(), - .view = diffuse.image_view(), - // .image_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - .layout = ::vk::image_layout::shader_read_only_optimal, - } }; - - // writes to texture at layout(set = 1, binding = 2) - std::array<::vk::write_image, 1> - binding2_images = { ::vk::write_image{ - .sampler = specular.sampler(), - .view = specular.image_view(), - // .image_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - .layout = ::vk::image_layout::shader_read_only_optimal, - } }; - - // vulkan image descriptors are for writing textures - std::vector<::vk::write_image_descriptor> material_textures = { - // layout(set = 1, binding = 1) uniform sampler2D - ::vk::write_image_descriptor{ - .dst_binding = 1, - .sample_images = binding1_images, - }, - // layout(set = 1, binding = 2) uniform sampler2D - ::vk::write_image_descriptor{ - .dst_binding = 2, - .sample_images = binding2_images, - }, - }; - - m_mesh_descriptors[p_entity.id()]["materials"].update( - material_uniforms, material_textures); - - m_sets_layouts.push_back( - m_mesh_descriptors[p_entity.id()]["materials"].layout()); - } - }); - - std::vector<::vk::shader_handle> modules = m_shader_group.handles(); - - ::vk::pipeline_settings pipeline_configuration = { - .renderpass = m_final_renderpass, - .shader_modules = modules, - .vertex_attributes = m_shader_group.vertex_attributes(), - .vertex_bind_attributes = m_shader_group.vertex_bind_attributes(), - .descriptor_layouts = m_sets_layouts - }; - m_main_pipeline = ::vk::pipeline(m_device, pipeline_configuration); - } - - void vk_renderer::start_frame(const ::vk::command_buffer& p_current, - const window_settings& p_settings, - const VkRenderPass& p_renderpass, - const VkFramebuffer& p_framebuffer, - const glm::mat4& p_proj_view) { - m_proj_view = p_proj_view; - m_current_frame = application::current_frame(); - m_final_renderpass = p_renderpass; - - std::array clear_values = {}; - - clear_values[0].color = m_color; - clear_values[1].depthStencil = { 1.f, 0 }; - m_window_extent = p_settings; - - VkRenderPassBeginInfo renderpass_begin_info = { - .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, - .pNext = nullptr, - .renderPass = p_renderpass, - .renderArea = { - .offset = { - .x = 0, - .y = 0 - }, - .extent = { - .width = p_settings.width, - .height = p_settings.height - }, - }, - .clearValueCount = static_cast(clear_values.size()), - .pClearValues = clear_values.data() - }; - - m_current_command_buffer = p_current; - m_current_command_buffer.begin( - ::vk::command_usage::simulatneous_use_bit); - - VkViewport viewport = { - .x = 0.0f, - .y = 0.0f, - .width = static_cast(m_window_extent.width), - .height = static_cast(m_window_extent.height), - .minDepth = 0.0f, - .maxDepth = 1.0f, - }; - - vkCmdSetViewport(m_current_command_buffer, 0, 1, &viewport); - - VkRect2D scissor = { - .offset = { 0, 0 }, - .extent = { m_window_extent.width, m_window_extent.height }, - }; - - vkCmdSetScissor(m_current_command_buffer, 0, 1, &scissor); - - renderpass_begin_info.framebuffer = p_framebuffer; - - vkCmdBeginRenderPass(m_current_command_buffer, - &renderpass_begin_info, - VK_SUBPASS_CONTENTS_INLINE); - } - - // This just returns the arbitrary amount of bytes of the object - // TODO: Move this into core/utilities.hpp - template - std::span to_bytes(T p_data) { - return std::span(reinterpret_cast(&p_data), - sizeof(p_data)); - } - - void vk_renderer::post_frame() { - - // For now, using this. Will need to remove this before vulkan - // integration merging into dev This is for testing and to hopefully - // have a global_ubo for globalized uniforms - global_ubo global_frame_ubo = { .mvp = m_proj_view }; - - // TODO: Make to_bytes be part of utilities. This can be useful in - // sending the amount of bytes in batches for batch-rendering - // std::span bytes_data = to_bytes(global_frame_ubo); - // m_global_uniforms.update(bytes_data.data()); - m_global_uniforms.update(&global_frame_ubo); - - ref current_world = system_registry::get_world("Editor World"); - ref current_scene = current_world->get_scene("LevelScene"); - - // query all entities that have a point light - flecs::query query_point_lights = - current_scene->query_builder().build(); - - light_scene_ubo test_light = {}; - uint32_t index = 0; - query_point_lights.each( - [&index, &test_light](flecs::entity p_entity, point_light& p_light) { - const transform* t = p_entity.get(); - p_light.position = t->position; - - test_light.light_sources[index] = { - .position = glm::vec4(p_light.position, 1.f), - .color = p_light.color, - .attenuation = p_light.attenuation, - .constant = p_light.constant, - .linear = p_light.linear, - .quadratic = p_light.quadratic, - .ambient = p_light.ambient, - .diffuse = p_light.diffuse, - .specular = p_light.specular, - }; - index += 1; - }); - test_light.num_lights = index; - - m_point_light_uniforms.update(&test_light); - - // query all objects with a specified 3d mesh source - flecs::query<> query_targets = - current_scene->query_builder().build(); - - m_main_pipeline.bind(m_current_command_buffer); - - // Bind global camera data here - m_global_descriptors.bind(m_current_command_buffer, - m_main_pipeline.layout()); - query_targets.each([this](flecs::entity p_entity) { - const transform* transform_component = p_entity.get(); - const mesh_source* material_component = p_entity.get(); - m_model = glm::mat4(1.f); - m_model = glm::translate(m_model, transform_component->position); - m_model = glm::scale(m_model, transform_component->scale); - glm::mat4 rotation_mat4 = - glm::mat4(glm::quat(transform_component->rotation)); - - m_model *= rotation_mat4; - - // Mesh used for viking_room - replaced with std::map equivalent - geometry_uniform mesh_ubo = { .model = m_model, - .color = material_component->color }; - - if (m_cached_meshes[p_entity.id()].loaded()) { - m_mesh_geometry_set[p_entity.id()].update(&mesh_ubo); - - material_metadata data = {}; - - if (p_entity.has()) { - data = *p_entity.get(); - } - m_mesh_material_set[p_entity.id()].update(&data); - - m_mesh_descriptors[p_entity.id()]["materials"].bind( - m_current_command_buffer, m_main_pipeline.layout()); - - m_cached_meshes[p_entity.id()].draw(m_current_command_buffer); - } - }); - - vkCmdEndRenderPass(m_current_command_buffer); - m_current_command_buffer.end(); - } -}; diff --git a/src/atlas/drivers/vulkan-cpp/vk_swapchain.cpp b/src/atlas/drivers/vulkan-cpp/vk_swapchain.cpp deleted file mode 100644 index f5b953a0..00000000 --- a/src/atlas/drivers/vulkan-cpp/vk_swapchain.cpp +++ /dev/null @@ -1,261 +0,0 @@ -#include -#include -#include -#include -#include -#include - -namespace atlas::vk { - - vk_swapchain::vk_swapchain(const VkSurfaceKHR& p_surface, - const window_settings& p_settings) - : m_current_surface_handler(p_surface) - , m_window_settings(p_settings) - , m_current_surface(p_surface) { - m_physical = vk_context::physical_driver(); - m_driver = vk_context::driver_context(); - - create(); - } - - void vk_swapchain::create() { - - // surface properties are always updated from the physical device - // so they are valid should the swapchain be recreated - m_surface_properties = - m_physical.get_surface_properties(m_current_surface); - m_window_settings.width = - m_surface_properties.surface_capabilities.currentExtent.width; - m_window_settings.height = - m_surface_properties.surface_capabilities.currentExtent.height; - - //! @note Setting up presentation stuff - // request what our minimum image count is - uint32_t request_min_image_count = - select_images_size(m_surface_properties.surface_capabilities); - - m_swapchain_extent = - m_surface_properties.surface_capabilities.currentExtent; - - // setting our presentation properties - uint32_t present_index = - m_physical.read_presentation_index(m_current_surface_handler); - - VkSwapchainCreateInfoKHR swapchain_ci = { - .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, - .surface = m_current_surface_handler, - .minImageCount = request_min_image_count, - .imageFormat = m_surface_properties.surface_format.format, - .imageColorSpace = m_surface_properties.surface_format.colorSpace, - // use physical device surface formats to getting the right formats - // in vulkan - .imageExtent = m_swapchain_extent, - .imageArrayLayers = 1, - .imageUsage = (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | - VK_IMAGE_USAGE_TRANSFER_DST_BIT), - .queueFamilyIndexCount = 1, - .pQueueFamilyIndices = &present_index, - .preTransform = - m_surface_properties.surface_capabilities.currentTransform, - .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, - .presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR, - .clipped = true - }; - - vk_check(vkCreateSwapchainKHR( - m_driver, &swapchain_ci, nullptr, &m_swapchain_handler), - "vkCreateSwapchainKHR"); - - //!@brief querying images available - uint32_t image_count = 0; - vkGetSwapchainImagesKHR(m_driver, - m_swapchain_handler, - &image_count, - nullptr); // used to get the amount of images - std::vector images(image_count); - vkGetSwapchainImagesKHR(m_driver, - m_swapchain_handler, - &image_count, - images.data()); // used to store in the images - - // Creating Images - m_swapchain_images.resize(image_count); - m_image_size = image_count; - m_swapchain_depth_images.resize(image_count); - - VkFormat depth_format = m_driver.depth_format(); - - for (uint32_t i = 0; i < m_swapchain_images.size(); i++) { - ::vk::image_params color_image_config = { - .extent = { m_swapchain_extent.width, - m_swapchain_extent.height, }, - .format = m_surface_properties.surface_format.format, - .aspect = ::vk::image_aspect_flags::color_bit, - .usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, - .mip_levels = 1, - .layer_count = 1, - .phsyical_memory_properties = m_physical.memory_properties(), - - }; - - m_swapchain_images[i] = - ::vk::sample_image(m_driver, images[i], color_image_config); - - ::vk::image_params depth_image_config = { - .extent = { m_swapchain_extent.width, - m_swapchain_extent.height, }, - .format = depth_format, - .aspect = ::vk::image_aspect_flags::depth_bit, - .usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, - .mip_levels = 1, - .layer_count = 1, - .phsyical_memory_properties = m_physical.memory_properties(), - }; - - m_swapchain_depth_images[i] = - ::vk::sample_image(m_driver, depth_image_config); - } - - // command buffers - - m_swapchain_command_buffers.resize(image_count); - - for (size_t i = 0; i < m_swapchain_command_buffers.size(); i++) { - ::vk::command_params settings = { - .levels = ::vk::command_levels::primary, - // .queue_index = enumerate_swapchain_settings.present_index, - .queue_index = 0, - .flags = ::vk::command_pool_flags::reset, - }; - - m_swapchain_command_buffers[i] = - ::vk::command_buffer(m_driver, settings); - } - - std::array<::vk::attachment, 2> renderpass_attachments = { - ::vk::attachment{ - .format = m_surface_properties.surface_format.format, - .layout = ::vk::image_layout::color_optimal, - .samples = ::vk::sample_bit::count_1, - .load = ::vk::attachment_load::clear, - .store = ::vk::attachment_store::store, - .stencil_load = ::vk::attachment_load::clear, - .stencil_store = ::vk::attachment_store::dont_care, - .initial_layout = ::vk::image_layout::undefined, - .final_layout = ::vk::image_layout::present_src_khr, - }, - ::vk::attachment{ - .format = depth_format, - .layout = ::vk::image_layout::depth_stencil_optimal, - .samples = ::vk::sample_bit::count_1, - .load = ::vk::attachment_load::clear, - .store = ::vk::attachment_store::dont_care, - .stencil_load = ::vk::attachment_load::dont_care, - .stencil_store = ::vk::attachment_store::dont_care, - .initial_layout = ::vk::image_layout::undefined, - .final_layout = ::vk::image_layout::depth_stencil_optimal, - }, - }; - m_final_renderpass = ::vk::renderpass(m_driver, renderpass_attachments); - - // creating framebuffers - m_swapchain_framebuffers.resize(m_swapchain_images.size()); - - for (uint32_t i = 0; i < m_swapchain_images.size(); i++) { - - std::array - image_attachments = { m_swapchain_images[i].image_view(), - m_swapchain_depth_images[i].image_view() }; - - ::vk::framebuffer_params framebuffer_info = { - .renderpass = m_final_renderpass, - .views = image_attachments, - .extent = m_swapchain_extent - }; - m_swapchain_framebuffers[i] = - ::vk::framebuffer(m_driver, framebuffer_info); - } - - ::vk::queue_params present_queue_params{ - .family = 0, - .index = 0, - }; - m_present_to_queue = ::vk::device_present_queue( - m_driver, m_swapchain_handler, present_queue_params); - } - - void vk_swapchain::invalidate() { - destroy(); - create(); - } - - uint32_t vk_swapchain::select_images_size( - const VkSurfaceCapabilitiesKHR& p_surface_capabilities) { - uint32_t requested_images = p_surface_capabilities.minImageCount + 1; - - uint32_t final_image_count = 0; - - if ((p_surface_capabilities.maxImageCount > 0) and - (requested_images > p_surface_capabilities.maxImageCount)) { - final_image_count = p_surface_capabilities.maxImageCount; - } - else { - final_image_count = requested_images; - } - - return final_image_count; - } - - uint32_t vk_swapchain::read_acquired_image() { - m_present_to_queue.wait_idle(); - - // uint32_t frame_idx = m_present_to_queue.acquired_frame(); - uint32_t frame_idx = m_present_to_queue.acquire_next_image(); - if (m_present_to_queue.out_of_date()) { - invalidate(); - m_present_to_queue.out_of_date(false); - frame_idx = m_present_to_queue.acquire_next_image(); - } - - return frame_idx; - } - - void vk_swapchain::present(const uint32_t& p_current_frame) { - m_present_to_queue.present_frame(p_current_frame); - if (m_present_to_queue.out_of_date()) { - invalidate(); - m_present_to_queue.out_of_date(false); - } - } - - void vk_swapchain::submit(std::span p_commands) { - // m_present_to_queue.submit_immediate_async(p_command); - m_present_to_queue.submit_async(p_commands); - } - - void vk_swapchain::destroy() { - vkDeviceWaitIdle(m_driver); - - for (size_t i = 0; i < m_swapchain_framebuffers.size(); i++) { - m_swapchain_framebuffers[i].destroy(); - } - - m_final_renderpass.destroy(); - - m_present_to_queue.destroy(); - - for (size_t i = 0; i < m_swapchain_command_buffers.size(); i++) { - m_swapchain_command_buffers[i].destroy(); - } - - for (uint32_t i = 0; i < m_swapchain_depth_images.size(); i++) { - m_swapchain_depth_images[i].destroy(); - } - - for (uint32_t i = 0; i < m_swapchain_images.size(); i++) { - m_swapchain_images[i].destroy(); - } - - vkDestroySwapchainKHR(m_driver, m_swapchain_handler, nullptr); - } -}; diff --git a/src/atlas/drivers/vulkan-cpp/vk_window.cpp b/src/atlas/drivers/vulkan-cpp/vk_window.cpp deleted file mode 100644 index 92fc063e..00000000 --- a/src/atlas/drivers/vulkan-cpp/vk_window.cpp +++ /dev/null @@ -1,78 +0,0 @@ -#include -#include -#include -#include -#include - -namespace atlas::vk { - vk_window* vk_window::s_instance = nullptr; - vk_window::vk_window(const window_settings& p_settings) - : m_settings(p_settings) { - - if (!glfwVulkanSupported()) { - console_log_warn("GLFW: Vulkan is not supported!"); - return; - } - - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); - glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); - - m_instance_handler = vk_context::handler(); - - m_window_handler = glfwCreateWindow((int)p_settings.width, - (int)p_settings.height, - p_settings.name.c_str(), - nullptr, - nullptr); - - glfwMakeContextCurrent(m_window_handler); - - vk_check( - glfwCreateWindowSurface( - m_instance_handler, m_window_handler, nullptr, &m_window_surface), - "glfwCreateWindowSurface"); - - center_window(); - - m_swapchain = vk_swapchain(m_window_surface, m_settings); - - vk_context::submit_resource_free([this]() { - console_log_fatal("vk_window submit freed resources!!!"); - m_swapchain.destroy(); - }); - - glfwSetWindowUserPointer(m_window_handler, this); - - s_instance = this; - } - - vk_window::~vk_window() { - vkDestroySurfaceKHR(m_instance_handler, m_window_surface, nullptr); - vkDestroyInstance(m_instance_handler, nullptr); - glfwDestroyWindow(m_window_handler); - } - - void vk_window::center_window() { - GLFWmonitor* monitor = glfwGetPrimaryMonitor(); - const GLFWvidmode* mode = glfwGetVideoMode(monitor); - uint32_t width = (mode->width / 2) - (m_settings.width / 2); - uint32_t height = (mode->height / 2) - (m_settings.height / 2); - glfwSetWindowPos(m_window_handler, (int)width, (int)height); - } - - void vk_window::presentation_process(const uint32_t& p_current_frame) { - m_swapchain.present(p_current_frame); - } - - window_settings vk_window::settings() const { - return m_swapchain.settings(); - } - - uint32_t vk_window::read_acquired_next_frame() { - return m_swapchain.read_acquired_image(); - } - - GLFWwindow* vk_window::native_window() const { - return m_window_handler; - } -}; diff --git a/src/atlas/physics/physics_context.cpp b/src/atlas/physics/physics_context.cpp deleted file mode 100644 index 78f80e8a..00000000 --- a/src/atlas/physics/physics_context.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include -#include -#include -#include -#include - -namespace atlas::physics { - - ref initialize_physics_context( - const jolt_settings& p_settings, - event::event_bus& p_bus) { - return create_ref(p_settings, p_bus); - } - -} \ No newline at end of file diff --git a/src/atlas/physics/physics_engine.cpp b/src/atlas/physics/physics_engine.cpp deleted file mode 100644 index c2d6dad6..00000000 --- a/src/atlas/physics/physics_engine.cpp +++ /dev/null @@ -1,154 +0,0 @@ -#include -#include -#include -#include -#include -#include - -namespace atlas::physics { - - physics_engine::physics_engine(const jolt_settings& p_settings, - flecs::world& p_registry, - event::event_bus& p_bus) - : m_registry(&p_registry) - , m_bus(&p_bus) { - m_physics_context = initialize_physics_context(p_settings, *m_bus); - // This may change, but for now we want to ensure that we only want to - // create a single physics body with a specific collider Rather then - // having multiple colliders be associated to a single physics body - // itself. - m_query_box_collider = - m_registry->query_builder() - .without() - .build(); - m_query_sphere_collider = - m_registry->query_builder() - .without() - .build(); - m_query_capsule_collider = - m_registry->query_builder() - .without() - .build(); - - m_physics_bodies = m_registry->query_builder().build(); - }; - - void physics_engine::start() { - // At the start of every simulation we create physics bodies to run - // simulation on those physics bodies with the specific colliders - // associated with them - m_query_box_collider.each([this](flecs::entity p_entity, - transform& p_transform, - physics_body& p_body, - box_collider& p_collider) { - m_physics_context->add_box_collider( - p_entity.id(), &p_transform, &p_body, &p_collider); - }); - - m_query_sphere_collider.each([this](flecs::entity p_entity, - transform& p_transform, - physics_body& p_body, - sphere_collider& p_collider) { - m_physics_context->add_sphere_collider( - p_entity.id(), &p_transform, &p_body, &p_collider); - }); - - m_query_capsule_collider.each([this](flecs::entity p_entity, - transform& p_transform, - physics_body& p_body, - capsule_collider& p_collider) { - m_physics_context->add_capsule_collider( - p_entity.id(), &p_transform, &p_body, &p_collider); - }); - m_physics_context->prepare(); - } - - void physics_engine::update(float p_delta_time) { - using namespace JPH; - - m_physics_bodies.each( - [this](flecs::entity p_entity, physics_body& p_body) { - m_physics_context->set_force_and_torque( - p_entity.id(), p_body.force, p_body.torque); - m_physics_context->set_linear_velocity(p_entity.id(), - p_body.linear_velocity); - m_physics_context->set_angular_velocity(p_entity.id(), - p_body.angular_velocity); - m_physics_context->set_impulse(p_entity.id(), p_body.impulse); - }); - // This will ensure all physics bodies with which colliders they are - // associated with are update with the simulation, and their parameters - // are modified - m_physics_context->update(p_delta_time); - - m_query_box_collider.each([&](flecs::entity p_entity, - transform& p_transform, - physics_body& p_body, - box_collider&) { - // updating transform - transform t = m_physics_context->read_transform(p_entity.id()); - p_transform.position = t.position; - p_transform.rotation = t.rotation; - p_transform.quaternion = t.quaternion; - - // physics bodies parameters - auto body = m_physics_context->read_physics_body(p_entity.id()); - p_body.linear_damping = body.linear_damping; - p_body.linear_velocity = body.linear_velocity; - p_body.angular_velocity = body.angular_velocity; - p_body.gravity_factor = body.gravity_factor; - p_body.center_mass_position = body.center_mass_position; - p_body.friction = body.friction; - p_body.restitution = body.restitution; - p_body.angular_velocity = body.angular_velocity; - p_body.linear_velocity = body.linear_velocity; - }); - - // updating sphere collider - m_query_sphere_collider.each([this](flecs::entity p_entity, - transform& p_transform, - physics_body& p_body, - sphere_collider&) { - // updating transform - transform t = m_physics_context->read_transform(p_entity.id()); - p_transform.position = t.position; - p_transform.rotation = t.rotation; - p_transform.quaternion = t.quaternion; - - // updating physics body - auto body = m_physics_context->read_physics_body(p_entity.id()); - p_body.linear_damping = body.linear_damping; - p_body.linear_velocity = body.linear_velocity; - p_body.angular_velocity = body.angular_velocity; - p_body.gravity_factor = body.gravity_factor; - p_body.center_mass_position = body.center_mass_position; - p_body.friction = body.friction; - p_body.restitution = body.restitution; - }); - - // updating capsule collider - m_query_capsule_collider.each([this](flecs::entity p_entity, - transform& p_transform, - physics_body& p_body, - capsule_collider&) { - transform t = m_physics_context->read_transform(p_entity.id()); - p_transform.position = t.position; - p_transform.rotation = t.rotation; - p_transform.quaternion = t.quaternion; - - auto body = m_physics_context->read_physics_body(p_entity.id()); - p_body.linear_damping = body.linear_damping; - p_body.linear_velocity = body.linear_velocity; - p_body.angular_velocity = body.angular_velocity; - p_body.gravity_factor = body.gravity_factor; - p_body.center_mass_position = body.center_mass_position; - p_body.friction = body.friction; - p_body.restitution = body.restitution; - }); - } - - void physics_engine::stop() { - m_physics_context->destroy(); - } - -} \ No newline at end of file diff --git a/src/atlas/renderer/renderer.cpp b/src/atlas/renderer/renderer.cpp deleted file mode 100644 index 95b81109..00000000 --- a/src/atlas/renderer/renderer.cpp +++ /dev/null @@ -1,37 +0,0 @@ -#include -#include - -namespace atlas { - - renderer::renderer(const window_settings& p_window_extent, - uint32_t p_image_size, - const std::string& p_tag) { - m_render_context = - initialize_renderer(p_window_extent, p_image_size, p_tag); - } - - void renderer::preload(const VkRenderPass& p_renderpass) { - m_render_context->preload(p_renderpass); - } - - void renderer::begin(const ::vk::command_buffer& p_current, - const window_settings& p_settings, - const VkRenderPass& p_renderpass, - const VkFramebuffer& p_framebuffer, - const glm::mat4& p_proj_view) { - return m_render_context->begin_frame( - p_current, p_settings, p_renderpass, p_framebuffer, p_proj_view); - } - - void renderer::end() { - return m_render_context->end_frame(); - } - - void renderer::set_background_color(const std::array& p_color) { - m_render_context->set_background_color(p_color); - } - - void renderer::current_scene(ref p_scene) { - m_render_context->current_scene_context(std::move(p_scene)); - } -}; diff --git a/tests/entity_component_system.test.cpp b/tests/entity_component_system.test.cpp index 4e5b6ec5..49883113 100644 --- a/tests/entity_component_system.test.cpp +++ b/tests/entity_component_system.test.cpp @@ -1,10 +1,16 @@ #include -#include +// #include #include #include #include -#include +// #include +import atlas.core.scene; +import atlas.core.scene.game_object; +import atlas.core.math.types; +import atlas.core.event; + +#include namespace atlas { @@ -82,7 +88,7 @@ namespace atlas { //! @note Each scene will define flecs::world typically //! @note flecs::world is how flecs (ECS) stores entities and components - atlas::event::event_bus test_event_bus; + atlas::event::bus test_event_bus; atlas::scene test_scene = atlas::scene("Mock Scene", test_event_bus); "create_entity::add"_test = [&test_scene] { diff --git a/tests/jolt_engine.test.cpp b/tests/jolt_engine.test.cpp deleted file mode 100644 index c20aa02d..00000000 --- a/tests/jolt_engine.test.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -namespace atlas { - - /** - * @brief The physics is fully reliant on the ecs working. As it utilizes - * queries as well as scene to navigate location of entities. - * - */ - boost::ut::suite<"physics_engine_integration"> engine_test = [] { - using namespace boost::ut; - - // Setup to do testing and assume we have a specified current scene that - // gets provided to you during that runtime - atlas::event::event_bus bus; - atlas::scene test_environment_scene = atlas::scene("Mock 1", bus); - - // Do some testing if these two collides or something like that - // atlas::game_object obj1 = test_environment_scene.entity("Entity - // 1"); atlas::game_object obj2 = - // test_environment_scene.entity("Entity 2"); - - "on_collision_enter"_test = [] {}; - }; -}; \ No newline at end of file diff --git a/tests/jolt_type_conversion.test.cpp b/tests/jolt_type_conversion.test.cpp deleted file mode 100644 index 76f52f79..00000000 --- a/tests/jolt_type_conversion.test.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -namespace atlas { - - bool approx_equal(float a, float b, float epsilon = 1e-6f) { - return std::fabs(a - b) < epsilon; - } - - boost::ut::suite<"jolt_helper"> jolt_helper_tests = [] { - using namespace boost::ut; - - "to_jph::vec3 conversion"_test = [] { - glm::vec3 glm_vec{ 1.0f, 2.0f, 3.0f }; - JPH::Vec3 jph_vec = jolt::to_vec3(glm_vec); - - expect(approx_equal(jph_vec.GetX(), 1.0f)); - expect(approx_equal(jph_vec.GetY(), 2.0f)); - expect(approx_equal(jph_vec.GetZ(), 3.0f)); - }; - - "to_jph::quat conversion"_test = [] { - glm::quat glm_quat{ 0.707f, 0.0f, 0.707f, 0.0f }; - glm::vec4 quat_vec = - glm::vec4(glm_quat.x, glm_quat.y, glm_quat.z, glm_quat.w); - - JPH::Quat jph_quat = jolt::to_quat(quat_vec); - - expect(approx_equal(jph_quat.GetX(), 0.0f)); - expect(approx_equal(jph_quat.GetY(), 0.707f)); - expect(approx_equal(jph_quat.GetZ(), 0.0f)); - expect(approx_equal(jph_quat.GetW(), 0.707f)); - }; - }; -}; \ No newline at end of file diff --git a/tests/math.test.cpp b/tests/math.test.cpp index 74a13be4..bb5f109b 100644 --- a/tests/math.test.cpp +++ b/tests/math.test.cpp @@ -1,8 +1,8 @@ -#include - +#include #include #include #include +import atlas.core.math.types; namespace atlas { /** diff --git a/tests/object.test.cpp b/tests/object.test.cpp deleted file mode 100644 index a93b0fa3..00000000 --- a/tests/object.test.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include - -boost::ut::suite<"::object_test"> object_test = []() { - using namespace boost::ut; -}; \ No newline at end of file diff --git a/tests/scene.test.cpp b/tests/scene.test.cpp index 32e567ec..c5eb1ea0 100644 --- a/tests/scene.test.cpp +++ b/tests/scene.test.cpp @@ -1,11 +1,12 @@ #include -#include -#include -#include +import atlas.core.scene; +import atlas.core.scene.game_object; +import atlas.core.scene.components; +import atlas.core.event; boost::ut::suite<"::scene"> scene_test = []() { using namespace boost::ut; - atlas::event::event_bus test_event_bus; + atlas::event::bus test_event_bus; atlas::scene test_scene = atlas::scene("Mock Scene 1", test_event_bus); "create_object"_test = [&test_scene]() {