diff --git a/.gitmodules b/.gitmodules index e285cba7..33918741 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,21 +1,24 @@ +[submodule "Submodules/angelscript_addons_impl"] + path = Submodules/angelscript_addons_impl + url = https://github.com/NeoDoa-Collective/angelscript_addons_impl +[submodule "Submodules/detector"] + path = Submodules/detector + url = https://github.com/NeoDoa-Collective/detector [submodule "Submodules/EZEasing"] path = Submodules/EZEasing url = https://github.com/NeoDoa-Collective/EZEasing [submodule "Submodules/IconFontCppHeaders"] path = Submodules/IconFontCppHeaders url = https://github.com/NeoDoa-Collective/IconFontCppHeaders -[submodule "Submodules/detector"] - path = Submodules/detector - url = https://github.com/NeoDoa-Collective/detector -[submodule "Submodules/libtinyfiledialogs"] - path = Submodules/libtinyfiledialogs - url = https://github.com/NeoDoa-Collective/libtinyfiledialogs [submodule "Submodules/imgInspect"] path = Submodules/imgInspect url = https://github.com/NeoDoa-Collective/imgInspect +[submodule "Submodules/libtinyfiledialogs"] + path = Submodules/libtinyfiledialogs + url = https://github.com/NeoDoa-Collective/libtinyfiledialogs [submodule "Submodules/stb_impl"] path = Submodules/stb_impl url = https://github.com/NeoDoa-Collective/stb_impl -[submodule "Submodules/angelscript_addons_impl"] - path = Submodules/angelscript_addons_impl - url = https://github.com/NeoDoa-Collective/angelscript_addons_impl +[submodule "Submodules/debugbreak"] + path = Submodules/debugbreak + url = https://github.com/NeoDoa-Collective/debugbreak diff --git a/CMakeLists.txt b/CMakeLists.txt index ee55de91..3318740f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,7 @@ cmake_minimum_required(VERSION 3.26.4) +set(VCPKG_DIR "${CMAKE_CURRENT_SOURCE_DIR}/vcpkg" CACHE STRING "vcpkg dir") + function(copy_folder_post_build folder) add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory @@ -7,14 +9,9 @@ function(copy_folder_post_build folder) ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$/${folder}) endfunction() -if (MSVC AND WIN32 AND NOT MSVC_VERSION VERSION_LESS 142) - add_link_option($<$:/INCREMENTAL>) - add_compile_option($<$:/ZI>) -endif() - set_property(GLOBAL PROPERTY USE_FOLDERS ON) set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT Editor) -set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_SOURCE_DIR}/vcpkg/scripts/buildsystems/vcpkg.cmake) +set(CMAKE_TOOLCHAIN_FILE ${VCPKG_DIR}/scripts/buildsystems/vcpkg.cmake) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") @@ -22,15 +19,50 @@ set(CMAKE_COMPILE_WARNING_AS_ERROR OFF) project(NeoDoa) +message("Generating for ${CMAKE_CXX_COMPILER_ID}") +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + if(WIN32 AND NOT MSVC_VERSION VERSION_LESS 142) + add_definitions(-D_CRT_SECURE_NO_WARNINGS) + add_compile_options(/W4) + add_compile_options(/wd4456) # declaration of 'LOCAL_VARIABLE_NAME' hides previous local declaration + add_compile_options(/wd4457) # declaration of 'LOCAL_VARIABLE_NAME' hides function parameter + add_compile_options(/wd4458) # declaration of 'FUNCTION_PARAMETER_NAME' hides class member + add_compile_options(/wd4459) # declaration of 'FUNCTION_PARAMETER_NAME' hides global declaration + add_compile_options($<$:/ZI>) + add_link_options($<$:/INCREMENTAL>) + endif() +else() + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + add_compile_options(-fmax-errors=0) + add_compile_options(-Wall) + add_compile_options(-Wextra) + add_compile_options(-Wpedantic) + add_compile_options(-Wno-changes-meaning) + endif() + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + add_compile_options(-ferror-limit=0) + add_compile_options(-Wall) + add_compile_options(-Wextra) + add_compile_options(-Wpedantic) + add_compile_options(-Wno-invalid-utf8) + add_compile_options(-Wno-braced-scalar-init) + add_compile_options(-Wno-missing-field-initializers) + add_compile_options(-Wno-gnu-zero-variadic-macro-arguments) + endif() +endif() + add_compile_options("$<$:-DDEBUG>") add_compile_options("$<$:-D_DEBUG>") add_compile_options("$<$:-DNDEBUG>") add_compile_options("$<$:-D_NDEBUG>") add_compile_definitions(ASSIMP_BUILD_ZLIB=ON) add_compile_definitions(IMGUI_DEFINE_MATH_OPERATORS) +add_compile_definitions(GLFW_INCLUDE_NONE) add_compile_definitions(GLM_ENABLE_EXPERIMENTAL) +add_compile_definitions(OPENGL_4_6_SUPPORT) add_subdirectory(Submodules/angelscript_addons_impl) +add_subdirectory(Submodules/debugbreak) add_subdirectory(Submodules/detector) add_subdirectory(Submodules/EZEasing) add_subdirectory(Submodules/IconFontCppHeaders) @@ -42,6 +74,7 @@ add_subdirectory(Editor) add_subdirectory(Launcher) set_target_properties(angelscript_addons_impl PROPERTIES FOLDER Submodules) +set_target_properties(debugbreak PROPERTIES FOLDER Submodules) set_target_properties(detector PROPERTIES FOLDER Submodules) set_target_properties(EZEasing PROPERTIES FOLDER Submodules) set_target_properties(IconFontCppHeaders PROPERTIES FOLDER Submodules) diff --git a/Editor/AssetFilter.cpp b/Editor/AssetFilter.cpp new file mode 100644 index 00000000..29e3413c --- /dev/null +++ b/Editor/AssetFilter.cpp @@ -0,0 +1,69 @@ +#include + +#include + +bool AssetFilters::IncludeSceneAssets::operator()(UUID assetID, const Assets& assets) const noexcept { + AssetHandle handle = assets.FindAsset(assetID); + assert(handle.HasValue()); + return handle->IsScene(); +} +bool AssetFilters::IncludeComponentAssets::operator()(UUID assetID, const Assets& assets) const noexcept { + AssetHandle handle = assets.FindAsset(assetID); + assert(handle.HasValue()); + return handle->IsComponentDefinition(); +} +bool AssetFilters::IncludeSamplerAssets::operator()(UUID assetID, const Assets& assets) const noexcept { + AssetHandle handle = assets.FindAsset(assetID); + assert(handle.HasValue()); + return handle->IsSampler(); +} +bool AssetFilters::IncludeTextureAssets::operator()(UUID assetID, const Assets& assets) const noexcept { + AssetHandle handle = assets.FindAsset(assetID); + assert(handle.HasValue()); + return handle->IsTexture(); +} +bool AssetFilters::IncludeShaderAssets::operator()(UUID assetID, const Assets& assets) const noexcept { + AssetHandle handle = assets.FindAsset(assetID); + assert(handle.HasValue()); + return handle->IsShader(); +} +bool AssetFilters::IncludeShaderProgramAssets::operator()(UUID assetID, const Assets& assets) const noexcept { + AssetHandle handle = assets.FindAsset(assetID); + assert(handle.HasValue()); + return handle->IsShaderProgram(); +} +bool AssetFilters::IncludeMaterialAssets::operator()(UUID assetID, const Assets& assets) const noexcept { + AssetHandle handle = assets.FindAsset(assetID); + assert(handle.HasValue()); + return handle->IsMaterial(); +} +bool AssetFilters::IncludeModelAssets::operator()(UUID assetID, const Assets& assets) const noexcept { + AssetHandle handle = assets.FindAsset(assetID); + assert(handle.HasValue()); + return handle->IsModel(); +} + +bool AssetFilters::ExcludeSceneAssets::operator()(UUID assetID, const Assets& assets) const noexcept { + return !IncludeSceneAssets()(assetID, assets); +} +bool AssetFilters::ExcludeComponentAssets::operator()(UUID assetID, const Assets& assets) const noexcept { + return !IncludeComponentAssets()(assetID, assets); +} +bool AssetFilters::ExcludeSamplerAssets::operator()(UUID assetID, const Assets& assets) const noexcept { + return !IncludeSamplerAssets()(assetID, assets); +} +bool AssetFilters::ExcludeTextureAssets::operator()(UUID assetID, const Assets& assets) const noexcept { + return !IncludeTextureAssets()(assetID, assets); +} +bool AssetFilters::ExcludeShaderAssets::operator()(UUID assetID, const Assets& assets) const noexcept { + return !IncludeShaderAssets()(assetID, assets); +} +bool AssetFilters::ExcludeShaderProgramAssets::operator()(UUID assetID, const Assets& assets) const noexcept { + return !IncludeShaderProgramAssets()(assetID, assets); +} +bool AssetFilters::ExcludeMaterialAssets::operator()(UUID assetID, const Assets& assets) const noexcept { + return !IncludeMaterialAssets()(assetID, assets); +} +bool AssetFilters::ExcludeModelAssets::operator()(UUID assetID, const Assets& assets) const noexcept { + return !IncludeModelAssets()(assetID, assets); +} \ No newline at end of file diff --git a/Editor/AssetFilter.hpp b/Editor/AssetFilter.hpp new file mode 100644 index 00000000..240aa648 --- /dev/null +++ b/Editor/AssetFilter.hpp @@ -0,0 +1,90 @@ +#pragma once + +#include +#include + +#include + +struct Assets; + +template +concept AssetFilter = requires (Callable& c, UUID assetID, const Assets& assets) { + { std::invoke(c, assetID, assets) } -> std::convertible_to; +}; + +template +auto operator|(const T1& f1, const T2& f2) { + return [f1, f2](UUID assetID, const Assets& assets) { + return std::invoke(f1, assetID, assets) || std::invoke(f2, assetID, assets); + }; +} + +namespace AssetFilters { + struct IncludeSceneAssets { + bool operator()(UUID assetID, const Assets& assets) const noexcept; + }; + struct IncludeComponentAssets { + bool operator()(UUID assetID, const Assets& assets) const noexcept; + }; + struct IncludeSamplerAssets { + bool operator()(UUID assetID, const Assets& assets) const noexcept; + }; + struct IncludeTextureAssets { + bool operator()(UUID assetID, const Assets& assets) const noexcept; + }; + struct IncludeShaderAssets { + bool operator()(UUID assetID, const Assets& assets) const noexcept; + }; + struct IncludeShaderProgramAssets { + bool operator()(UUID assetID, const Assets& assets) const noexcept; + }; + struct IncludeMaterialAssets { + bool operator()(UUID assetID, const Assets& assets) const noexcept; + }; + struct IncludeModelAssets { + bool operator()(UUID assetID, const Assets& assets) const noexcept; + }; + + struct ExcludeSceneAssets { + bool operator()(UUID assetID, const Assets& assets) const noexcept; + }; + struct ExcludeComponentAssets { + bool operator()(UUID assetID, const Assets& assets) const noexcept; + }; + struct ExcludeSamplerAssets { + bool operator()(UUID assetID, const Assets& assets) const noexcept; + }; + struct ExcludeTextureAssets { + bool operator()(UUID assetID, const Assets& assets) const noexcept; + }; + struct ExcludeShaderAssets { + bool operator()(UUID assetID, const Assets& assets) const noexcept; + }; + struct ExcludeShaderProgramAssets { + bool operator()(UUID assetID, const Assets& assets) const noexcept; + }; + struct ExcludeMaterialAssets { + bool operator()(UUID assetID, const Assets& assets) const noexcept; + }; + struct ExcludeModelAssets { + bool operator()(UUID assetID, const Assets& assets) const noexcept; + }; +} + +static_assert(AssetFilter); +static_assert(AssetFilter); +static_assert(AssetFilter); +static_assert(AssetFilter); +static_assert(AssetFilter); +static_assert(AssetFilter); +static_assert(AssetFilter); +static_assert(AssetFilter); + +static_assert(AssetFilter); +static_assert(AssetFilter); +static_assert(AssetFilter); +static_assert(AssetFilter); +static_assert(AssetFilter); +static_assert(AssetFilter); +static_assert(AssetFilter); +static_assert(AssetFilter); \ No newline at end of file diff --git a/Editor/AssetManager.cpp b/Editor/AssetManager.cpp index c3e083d0..77dca69f 100644 --- a/Editor/AssetManager.cpp +++ b/Editor/AssetManager.cpp @@ -23,7 +23,7 @@ AssetManager::AssetManager(GUI& gui) noexcept : } bool AssetManager::Begin() { - const GUI& gui = this->gui; + [[maybe_unused]] const GUI& gui = this->gui; ImGui::PushID(WindowStrings::AssetManagerWindowName); bool visible = ImGui::Begin(WindowStrings::AssetManagerWindowTitleID, nullptr, ImGuiWindowFlags_MenuBar); @@ -399,7 +399,7 @@ void AssetManager::RenderSelectedFolderContent() { void AssetManager::RenderContextMenu() { GUI& gui = this->gui.get(); - Assets& assets = *gui.CORE->GetAssets(); + [[maybe_unused]] Assets& assets = *gui.CORE->GetAssets(); bool isDisabled = !hasContent; if (isDisabled) { ImGui::BeginDisabled(); } @@ -464,7 +464,14 @@ void AssetManager::RenderContextMenu() { if (ImGui::MenuItem(shaderProgram)) { gui.ShowNewShaderProgramAssetModal(*currentFolder); } - + static constexpr auto material = cat(FileIcons::MATERIAL_ICON, " ", "Material"); + if (ImGui::MenuItem(material)) { + gui.ShowNewMaterialAssetModal(*currentFolder); + } + static constexpr auto sampler = cat(FileIcons::SAMPLER_ICON, " ", "Sampler"); + if (ImGui::MenuItem(sampler)) { + gui.ShowNewSamplerAssetModal(*currentFolder); + } ImGui::EndMenu(); } @@ -515,7 +522,6 @@ void AssetManager::SetCurrentFolder(FNode* folder) { } if (!folder->IsDirectory()) { return; } - GUI& gui = this->gui; if (folder == root || folder == currentFolder->ParentNode()) { SetSelectedNode(nullptr); @@ -545,7 +551,7 @@ bool AssetManager::FileFilter::CheckVisibility(const FNode& file) const { return true; } -void AssetManager::OnProjectLoaded(Project& project) { +void AssetManager::OnProjectLoaded([[maybe_unused]] Project& project) { hasContent = true; assets = gui.get().CORE->GetAssets().get(); root = &assets->Root(); diff --git a/Editor/CMakeLists.txt b/Editor/CMakeLists.txt index 66f943ec..d8ab2fbe 100644 --- a/Editor/CMakeLists.txt +++ b/Editor/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.26.4) project(Editor LANGUAGES CXX) set(CMAKE_CXX_STANDARD 23) -set(CMAKE_CXX_STANDARD_REQUIRED True) +set(CMAKE_CXX_STANDARD_REQUIRED ON) add_executable(Editor) @@ -12,12 +12,12 @@ target_link_libraries(Editor PUBLIC Engine) find_package(argparse CONFIG REQUIRED) find_package(cppzmq CONFIG REQUIRED) -find_package(unofficial-imguizmo CONFIG REQUIRED) +find_package(imguizmo CONFIG REQUIRED) find_package(unofficial-lunasvg CONFIG REQUIRED) target_link_libraries(Editor PUBLIC argparse::argparse) target_link_libraries(Editor PUBLIC cppzmq cppzmq-static) -target_link_libraries(Editor PUBLIC unofficial::imguizmo::imguizmo) +target_link_libraries(Editor PUBLIC imguizmo::imguizmo) target_link_libraries(Editor PUBLIC unofficial::lunasvg::lunasvg) set(GROUP_LIST @@ -46,6 +46,8 @@ set(GROUP_LIST "Language/CodeGenerator.cpp" "Language/CodeGenerator.hpp" + "UI/AssetFilter/AssetFilter.cpp" + "UI/AssetFilter/AssetFilter.hpp" "UI/Colors/Colors.hpp" "UI/Components/ComponentUI.cpp" "UI/Components/ComponentUI.hpp" @@ -92,6 +94,20 @@ set(GROUP_LIST "UI/GUI/Modals/NewAssetModal.hpp" "UI/GUI/Observer/Observer.cpp" "UI/GUI/Observer/Observer.hpp" + "UI/GUI/Observer/Displays/DisplayTargetRenderer.cpp" + "UI/GUI/Observer/Displays/DisplayTargetRenderer.hpp" + "UI/GUI/Observer/Displays/SceneDisplay/SceneDisplay.cpp" + "UI/GUI/Observer/Displays/SceneDisplay/SceneDisplay.hpp" + "UI/GUI/Observer/Displays/ComponentDefinitionDisplay/ComponentDefinitionDisplay.cpp" + "UI/GUI/Observer/Displays/ComponentDefinitionDisplay/ComponentDefinitionDisplay.hpp" + "UI/GUI/Observer/Displays/SamplerDisplay/SamplerDisplay.cpp" + "UI/GUI/Observer/Displays/SamplerDisplay/SamplerDisplay.hpp" + "UI/GUI/Observer/Displays/ShaderDisplay/ShaderDisplay.cpp" + "UI/GUI/Observer/Displays/ShaderDisplay/ShaderDisplay.hpp" + "UI/GUI/Observer/Displays/ShaderProgramDisplay/ShaderProgramDisplay.cpp" + "UI/GUI/Observer/Displays/ShaderProgramDisplay/ShaderProgramDisplay.hpp" + "UI/GUI/Observer/Displays/MaterialDisplay/MaterialDisplay.cpp" + "UI/GUI/Observer/Displays/MaterialDisplay/MaterialDisplay.hpp" "UI/GUI/SceneHierarchy/SceneHierarchy.cpp" "UI/GUI/SceneHierarchy/SceneHierarchy.hpp" "UI/GUI/SceneSettings/SceneSettings.cpp" diff --git a/Editor/CodeEditor.cpp b/Editor/CodeEditor.cpp index ac898752..d67007c5 100644 --- a/Editor/CodeEditor.cpp +++ b/Editor/CodeEditor.cpp @@ -16,7 +16,7 @@ CodeEditor::CodeEditor(GUI& gui) noexcept : } bool CodeEditor::Begin() { - const GUI& gui = this->gui; + [[maybe_unused]] const GUI& gui = this->gui; ImGui::PushID(WindowStrings::CodeEditorWindowName); bool visible = ImGui::Begin(WindowStrings::CodeEditorWindowTitleID, nullptr, ImGuiWindowFlags_MenuBar); @@ -24,13 +24,13 @@ bool CodeEditor::Begin() { return visible; } void CodeEditor::Render() { - const GUI& gui = this->gui; + [[maybe_unused]] const GUI& gui = this->gui; RenderMenuBar(); ImGuiTabBarFlags tabBarFlags = ImGuiTabBarFlags_AutoSelectNewTabs | ImGuiTabBarFlags_FittingPolicyResizeDown | ImGuiTabBarFlags_Reorderable; if(ImGui::BeginTabBar("#CodeEditorOpenTabs", tabBarFlags)) { - for (auto i = 0; i < tabs.size(); i++) { + for (size_t i = 0; i < tabs.size(); i++) { if (i == removedTabIndex) { continue; } auto& tab = tabs[i]; @@ -66,14 +66,14 @@ void CodeEditor::End() { ImGui::End(); ImGui::PopID(); - if (removedTabIndex != -1) { + if (removedTabIndex != std::numeric_limits::max()) { tabs.erase(tabs.begin() + removedTabIndex); - removedTabIndex = -1; + removedTabIndex = std::numeric_limits::max(); } } void CodeEditor::RenderMenuBar() { - bool menuBarEnabled = selectedTabIndex != -1; + bool menuBarEnabled = selectedTabIndex != std::numeric_limits::max(); if (menuBarEnabled) { bool shortcutsEnabled = menuBarEnabled; if (ImGui::Shortcut(ImGuiMod_Ctrl | ImGuiKey_S) && shortcutsEnabled) { @@ -177,7 +177,7 @@ void CodeEditor::AddTab(AssetHandle assetHandle) { return element.currentAsset == assetHandle; }); if (search != tabs.end()) { - selectedTabIndex = search - tabs.begin(); + selectedTabIndex = static_cast(search - tabs.begin()); return; } @@ -185,10 +185,13 @@ void CodeEditor::AddTab(AssetHandle assetHandle) { selectedTabIndex++; } -void CodeEditor::CloseTabAt(int index) { +void CodeEditor::CloseTabAt(size_t index) { removedTabIndex = index; if (removedTabIndex <= selectedTabIndex) { - selectedTabIndex = std::max(0, selectedTabIndex--); + selectedTabIndex--; + if(selectedTabIndex == std::numeric_limits::max()) { + selectedTabIndex = 0; + } } } @@ -204,7 +207,7 @@ void CodeEditor::OnReimport(Assets& assets) { } } void CodeEditor::OnAssetDeleted(AssetHandle handle) { - for (int i = 0; i < tabs.size(); i++) { + for (size_t i = 0; i < tabs.size(); i++) { if (tabs[i].currentAsset == handle) { CloseTabAt(i); return; diff --git a/Editor/CodeEditor.hpp b/Editor/CodeEditor.hpp index 59924ceb..4b4b3c08 100644 --- a/Editor/CodeEditor.hpp +++ b/Editor/CodeEditor.hpp @@ -51,14 +51,14 @@ struct CodeEditor { bool isWhitespaceVisible{ true }; int tabSize{ 4 }; - int selectedTabIndex{ -1 }; + size_t selectedTabIndex{ std::numeric_limits::max() }; std::vector tabs{}; EditorTab emptyTab{}; - int removedTabIndex{ -1 }; + size_t removedTabIndex{ std::numeric_limits::max() }; void AddTab(AssetHandle assetHandle); - void CloseTabAt(int index); + void CloseTabAt(size_t index); void RenderMenuBar(); diff --git a/Editor/Colors.hpp b/Editor/Colors.hpp index 4ab989fb..2138b429 100644 --- a/Editor/Colors.hpp +++ b/Editor/Colors.hpp @@ -1,5 +1,12 @@ +#pragma once + #include +namespace MultiAssetWidgetColors { + static constexpr ImVec4 DeletedOrUnloadedColor{ 1.0f, 0.6f, 0.6f, 1.0f }; + static constexpr ImVec4 EmptyColor{ 1.0f, 0.5f, 0.1f, 1.0f }; +} + namespace ComponentDefinitionViewColors { static constexpr ImVec4 ERROR_COLOR{ 1.0f, 0.6f, 0.6f, 1.0f }; static constexpr ImVec4 INFO_COLOR{ 0.7f, 1.0f, 0.7f, 1.0f }; diff --git a/Editor/ComponentDefinitionDisplay.cpp b/Editor/ComponentDefinitionDisplay.cpp new file mode 100644 index 00000000..028f8f83 --- /dev/null +++ b/Editor/ComponentDefinitionDisplay.cpp @@ -0,0 +1,134 @@ +#include + +#include +#include + +#include +#include +#include +#include + +ComponentDefinitionDisplay::ComponentDefinitionDisplay(Observer& observer) noexcept : + observer(observer) { + textEditorInstance.SetColorizerEnable(true); + textEditorInstance.SetReadOnlyEnabled(true); + textEditorInstance.SetShowWhitespaces(false); + textEditorInstance.SetText(std::string(10, ' ')); + textEditorInstance.SetLanguageDefinition(TextEditor::LanguageDefinition::AngelScript()); +} + +void ComponentDefinitionDisplay::SetDisplayTarget(const AssetHandle componentDefAssetHandle) noexcept { + assert(componentDefAssetHandle->IsComponentDefinition()); + if (componentDefAsset != componentDefAssetHandle) { + componentDefAsset = componentDefAssetHandle; + if (componentDefAsset->HasDeserializedData() && !componentDefAsset->HasErrorMessages()) { + const auto& componentDef = componentDefAsset->DataAs(); + textEditorInstance.SetText(componentDef.declaration); + } + } +} + +void ComponentDefinitionDisplay::RenderMessagesTable() noexcept { + if (!componentDefAsset->HasErrorMessages() && + !componentDefAsset->HasWarningMessages() && + !componentDefAsset->HasInfoMessages()) { + return; + } + + ImGuiTableFlags flags = ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders; + + ImGui::BeginTable("compiler_logs", 2, flags); + + ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, 30); + ImGui::TableSetupColumn("Compiler Logs", ImGuiTableColumnFlags_WidthStretch); + ImGui::TableHeadersRow(); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 0, 0 }); + + ImGui::PushStyleColor(ImGuiCol_Text, ComponentDefinitionViewColors::ERROR_COLOR); + for (auto& message : componentDefAsset->ErrorMessages()) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + + float r = BeginTableColumnCenterText(ComponentDefinitionViewIcons::ERROR_ICON); + ImGui::Text(ComponentDefinitionViewIcons::ERROR_ICON); + EndTableColumnCenterText(r); + + ImGui::TableSetColumnIndex(1); + + const ComponentCompilerMessage& m{ std::any_cast(message) }; + ImGui::TextWrapped("%s", m.message.c_str()); + } + ImGui::PopStyleColor(); + + ImGui::PushStyleColor(ImGuiCol_Text, ComponentDefinitionViewColors::WARNING_COLOR); + for (auto& message : componentDefAsset->WarningMessages()) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + + float r = BeginTableColumnCenterText(ComponentDefinitionViewIcons::WARNING_ICON); + ImGui::Text(ComponentDefinitionViewIcons::WARNING_ICON); + EndTableColumnCenterText(r); + + ImGui::TableSetColumnIndex(1); + + const ComponentCompilerMessage& m{ std::any_cast(message) }; + ImGui::TextWrapped("%s", m.message.c_str()); + } + ImGui::PopStyleColor(); + + ImGui::PushStyleColor(ImGuiCol_Text, ComponentDefinitionViewColors::INFO_COLOR); + for (auto& message : componentDefAsset->InfoMessages()) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + + float r = BeginTableColumnCenterText(ComponentDefinitionViewIcons::INFO_ICON); + ImGui::Text(ComponentDefinitionViewIcons::INFO_ICON); + EndTableColumnCenterText(r); + + ImGui::TableSetColumnIndex(1); + + const ComponentCompilerMessage& m{ std::any_cast(message) }; + ImGui::TextWrapped("%s", m.message.c_str()); + } + ImGui::PopStyleColor(); + + ImGui::PopStyleVar(); + ImGui::EndTable(); +} +void ComponentDefinitionDisplay::RenderFields() noexcept { + assert(componentDefAsset->HasDeserializedData()); + assert(!componentDefAsset->HasErrorMessages()); + + const auto& componentDef = componentDefAsset->DataAs(); + + ImGuiTableFlags flags = ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders; + if (ImGui::CollapsingHeader(componentDef.name.c_str(), ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_DefaultOpen)) { + if (ImGui::BeginTable("component_fields", 2, flags)) { + ImGui::TableSetupColumn("Field", ImGuiTableColumnFlags_WidthStretch); + ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthStretch); + ImGui::TableHeadersRow(); + + for (auto& field : componentDef.fields) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted(field.name.c_str()); + ImGui::TableSetColumnIndex(1); + ImGui::TextUnformatted(field.typeName.c_str()); + } + ImGui::EndTable(); + } + } +} +void ComponentDefinitionDisplay::RenderSourceCode() noexcept { + ImGuiTableFlags flags = ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders; + ImGui::BeginTable("source_code", 1, flags); + ImGui::TableSetupColumn("Source Code", ImGuiTableColumnFlags_WidthStretch); + ImGui::TableHeadersRow(); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + + textEditorInstance.Render("###ObserverComponentDefSourceCodeViewer", false, { 0.0f, -32.0f }); + + ImGui::EndTable(); +} \ No newline at end of file diff --git a/Editor/ComponentDefinitionDisplay.hpp b/Editor/ComponentDefinitionDisplay.hpp new file mode 100644 index 00000000..74c5a790 --- /dev/null +++ b/Editor/ComponentDefinitionDisplay.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include + +#include + +#include + +struct Observer; + +struct ComponentDefinitionDisplay { + + ComponentDefinitionDisplay(Observer& observer) noexcept; + + void SetDisplayTarget(const AssetHandle componentDefAssetHandle) noexcept; + + void RenderMessagesTable() noexcept; + void RenderFields() noexcept; + void RenderSourceCode() noexcept; + +private: + std::reference_wrapper observer; + + AssetHandle componentDefAsset{}; + TextEditor textEditorInstance{}; +}; \ No newline at end of file diff --git a/Editor/ComponentUI.cpp b/Editor/ComponentUI.cpp index 2d6f680f..4725458d 100644 --- a/Editor/ComponentUI.cpp +++ b/Editor/ComponentUI.cpp @@ -11,11 +11,13 @@ #include #include #include +#include #include #include #include #include +#include #include #include @@ -55,10 +57,8 @@ void TransformComponentUI::Render(GUI& gui, const TransformComponent& transformC { glm::quat quat = transformComponent.GetLocalRotation(); glm::vec3 eulersDeg(glm::degrees(glm::eulerAngles(quat))); - glm::vec3 old(eulersDeg); if (FancyVector3Widget(UINames[nameof(TransformComponent::localRotation)], eulersDeg)) { - quat = quat * glm::quat(glm::radians(eulersDeg - old)); - gui.ExecuteCommand(transformComponent.GetEntity(), quat); + gui.ExecuteCommand(transformComponent.GetEntity(), glm::quat(glm::radians(eulersDeg))); } } // scale @@ -86,15 +86,24 @@ void ChildComponentUI::Render(const ChildComponent& childComponent) { { nameof(ChildComponent::parent), Prettify(nameof(ChildComponent::parent)) } }; - ChildComponent& child = const_cast(childComponent); + [[maybe_unused]] ChildComponent& child = const_cast(childComponent); UneditableEntityWidget(UINames[nameof(ChildComponent::parent)], childComponent.GetParent()); } +void MultiMaterialComponentUI::Render(GUI& gui, const MultiMaterialComponent& multiMaterialComponent) { + static unordered_string_map UINames = { + { nameof(MultiMaterialComponent::materials), Prettify(nameof(MultiMaterialComponent::materials)) } + }; + + MultiMaterialComponent& mmc = const_cast(multiMaterialComponent); + MultiAssetWidget(UINames[nameof(MultiMaterialComponent::materials)], mmc.GetMaterials(), *Core::GetCore()->GetAssets().get(), gui.GetMetaAssetInfoBank(), AssetFilters::IncludeMaterialAssets() | AssetFilters::IncludeTextureAssets()); +} + void OrthoCameraComponentUI::Render(const OrthoCameraComponent& orthoCameraComponent) { static unordered_string_map UINames = { { nameof(OrthoCameraComponent::isActiveAndRendering), Prettify(nameof(OrthoCameraComponent::isActiveAndRendering)) }, { nameof(OrthoCameraComponent::data), Prettify("orthoCameraProperties") }, - { nameof(FrameBuffer::ClearColor), Prettify("clearColor") }, + { nameof(ClearColor), Prettify("clearColor") }, { nameof(OrthoCameraComponent::frameBuffer), Prettify("resolution") } }; @@ -126,7 +135,7 @@ void PerspectiveCameraComponentUI::Render(const PerspectiveCameraComponent& pers static unordered_string_map UINames = { { nameof(PerspectiveCameraComponent::isActiveAndRendering), Prettify(nameof(PerspectiveCameraComponent::isActiveAndRendering)) }, { nameof(PerspectiveCameraComponent::data), Prettify("orthoCameraProperties") }, - { nameof(FrameBuffer::ClearColor), Prettify("clearColor") }, + { nameof(ClearColor), Prettify("clearColor") }, { nameof(PerspectiveCameraComponent::frameBuffer), Prettify("resolution") } }; @@ -159,7 +168,7 @@ void UserDefinedComponentStorageUI::RenderComponentInstance(const ComponentInsta AssetHandle cmpAsset{ Core::GetCore()->GetAssets()->FindAsset(instance.ComponentAssetID()) }; const auto& component{ cmpAsset->DataAs() }; if (!cmpAsset.HasValue() || cmpAsset->HasErrorMessages()) { return; } - for (int i = 0; i < component.fields.size(); i++) { + for (size_t i = 0; i < component.fields.size(); i++) { auto& field{ component.fields[i] }; const auto& type{ field.typeName }; auto& value{ instance.MemberValues()[i] }; @@ -238,6 +247,13 @@ void ComponentUI::RenderChildComponent(const Observer& observer, const ChildComp } ComponentUI::End(show); } +void ComponentUI::RenderMultiMaterialComponent(const Observer& observer, const MultiMaterialComponent& multiMaterialComponent) { + bool show = ComponentUI::Begin(observer, nameof(MultiMaterialComponent)); + if (show) { + MultiMaterialComponentUI::Render(observer.gui, multiMaterialComponent); + } + ComponentUI::End(show); +} void ComponentUI::RenderOrthoCameraComponent(const Observer& observer, const OrthoCameraComponent& orthoCameraComponent) { bool show = ComponentUI::Begin(observer, nameof(OrthoCameraComponent)); if (show) { @@ -303,4 +319,4 @@ void ComponentUI::End(bool show) { ImGui::PopFont(); } ImGui::PopFont(); -} \ No newline at end of file +} diff --git a/Editor/ComponentUI.hpp b/Editor/ComponentUI.hpp index 0a78e350..46bffa93 100644 --- a/Editor/ComponentUI.hpp +++ b/Editor/ComponentUI.hpp @@ -24,6 +24,11 @@ namespace ChildComponentUI { void Render(const ChildComponent& childComponent); }; +struct MultiMaterialComponent; +namespace MultiMaterialComponentUI { + void Render(GUI& gui, const MultiMaterialComponent& multiMaterialComponent); +}; + struct OrthoCameraComponent; namespace OrthoCameraComponentUI { void Render(const OrthoCameraComponent& orthoCameraComponent); @@ -52,6 +57,7 @@ namespace ComponentUI { void RenderTransformComponent(const Observer& observer, const TransformComponent& transformComponent); void RenderParentComponent(const Observer& observer, const ParentComponent& parentComponent); void RenderChildComponent(const Observer& observer, const ChildComponent& childComponent); + void RenderMultiMaterialComponent(const Observer& observer, const MultiMaterialComponent& multiMaterialComponent); void RenderOrthoCameraComponent(const Observer& observer, const OrthoCameraComponent& orthoCameraComponent); void RenderPerspectiveCameraComponent(const Observer& observer, const PerspectiveCameraComponent& perspectiveCameraComponent); void RenderUserDefinedComponentStorage(const Observer& observer, const UserDefinedComponentStorage& storageComponent); diff --git a/Editor/ComponentWidgets.cpp b/Editor/ComponentWidgets.cpp index dcbb3f9d..f6dacff4 100644 --- a/Editor/ComponentWidgets.cpp +++ b/Editor/ComponentWidgets.cpp @@ -6,19 +6,199 @@ bool FancyVector1Widget(const std::string& label, glm::vec1& vec, FancyVectorWidgetSettings settings) { return FancyVectorWidget(label, &vec.x, settings); } - bool FancyVector2Widget(const std::string& label, glm::vec2& vec, FancyVectorWidgetSettings settings) { return FancyVectorWidget(label, &vec.x, settings); } - bool FancyVector3Widget(const std::string& label, glm::vec3& vec, FancyVectorWidgetSettings settings) { return FancyVectorWidget(label, &vec.x, settings); } - bool FancyVector4Widget(const std::string& label, glm::vec4& vec, FancyVectorWidgetSettings settings) { return FancyVectorWidget(label, &vec.x, settings); } +bool FancyVectori1Widget(const std::string& label, glm::ivec1& vec, FancyVectorWidgetSettings settings) { + settings.speed = std::clamp(std::round(settings.speed), 1.0f, static_cast(std::numeric_limits().max())); + settings.fmt = "%.0f"; + + std::array values = { + static_cast(vec.x), + }; + bool rv = FancyVectorWidget(label, values.data(), settings); + vec.x = static_cast(std::round(values[0])); + + return rv; +} +bool FancyVectori2Widget(const std::string& label, glm::ivec2& vec, FancyVectorWidgetSettings settings) { + settings.speed = std::clamp(std::round(settings.speed), 1.0f, static_cast(std::numeric_limits().max())); + settings.fmt = "%.0f"; + + std::array values = { + static_cast(vec.x), + static_cast(vec.y) + }; + bool rv = FancyVectorWidget(label, values.data(), settings); + vec.x = static_cast(std::round(values[0])); + vec.y = static_cast(std::round(values[1])); + + return rv; +} +bool FancyVectori3Widget(const std::string& label, glm::ivec3& vec, FancyVectorWidgetSettings settings) { + settings.speed = std::clamp(std::round(settings.speed), 1.0f, static_cast(std::numeric_limits().max())); + settings.fmt = "%.0f"; + + std::array values = { + static_cast(vec.x), + static_cast(vec.y), + static_cast(vec.z) + }; + bool rv = FancyVectorWidget(label, values.data(), settings); + vec.x = static_cast(std::round(values[0])); + vec.y = static_cast(std::round(values[1])); + vec.z = static_cast(std::round(values[2])); + + return rv; +} +bool FancyVectori4Widget(const std::string& label, glm::ivec4& vec, FancyVectorWidgetSettings settings) { + settings.speed = std::clamp(std::round(settings.speed), 1.0f, static_cast(std::numeric_limits().max())); + settings.fmt = "%.0f"; + + std::array values = { + static_cast(vec.x), + static_cast(vec.y), + static_cast(vec.z), + static_cast(vec.w) + }; + bool rv = FancyVectorWidget(label, values.data(), settings); + vec.x = static_cast(std::round(values[0])); + vec.y = static_cast(std::round(values[1])); + vec.z = static_cast(std::round(values[2])); + vec.w = static_cast(std::round(values[3])); + + return rv; +} + +bool FancyVectorui1Widget(const std::string& label, glm::uvec1& vec, FancyVectorWidgetSettings settings) { + settings.speed = std::clamp(std::round(settings.speed), 1.0f, static_cast(std::numeric_limits().max())); + settings.min = 0.0f; + settings.max = static_cast(std::numeric_limits().max()); + settings.fmt = "%.0f"; + + float value = static_cast(vec.x); + bool rv = FancyVectorWidget(label, &value, settings); + vec.x = static_cast(std::round(value)); + + return rv; +} +bool FancyVectorui2Widget(const std::string& label, glm::uvec2& vec, FancyVectorWidgetSettings settings) { + settings.speed = std::clamp(std::round(settings.speed), 1.0f, static_cast(std::numeric_limits().max())); + settings.min = 0.0f; + settings.max = static_cast(std::numeric_limits().max()); + settings.fmt = "%.0f"; + + std::array values = { + static_cast(vec.x), + static_cast(vec.y) + }; + bool rv = FancyVectorWidget(label, values.data(), settings); + vec.x = static_cast(std::round(values[0])); + vec.y = static_cast(std::round(values[1])); + + return rv; +} +bool FancyVectorui3Widget(const std::string& label, glm::uvec3& vec, FancyVectorWidgetSettings settings) { + settings.speed = std::clamp(std::round(settings.speed), 1.0f, static_cast(std::numeric_limits().max())); + settings.min = 0.0f; + settings.max = static_cast(std::numeric_limits().max()); + settings.fmt = "%.0f"; + + std::array values = { + static_cast(vec.x), + static_cast(vec.y), + static_cast(vec.z) + }; + bool rv = FancyVectorWidget(label, values.data(), settings); + vec.x = static_cast(std::round(values[0])); + vec.y = static_cast(std::round(values[1])); + vec.z = static_cast(std::round(values[2])); + + return rv; +} +bool FancyVectorui4Widget(const std::string& label, glm::uvec4& vec, FancyVectorWidgetSettings settings) { + settings.speed = std::clamp(std::round(settings.speed), 1.0f, static_cast(std::numeric_limits().max())); + settings.min = 0.0f; + settings.max = static_cast(std::numeric_limits().max()); + settings.fmt = "%.0f"; + + std::array values = { + static_cast(vec.x), + static_cast(vec.y), + static_cast(vec.z), + static_cast(vec.w) + }; + bool rv = FancyVectorWidget(label, values.data(), settings); + vec.x = static_cast(std::round(values[0])); + vec.y = static_cast(std::round(values[1])); + vec.z = static_cast(std::round(values[2])); + vec.w = static_cast(std::round(values[3])); + + return rv; +} + +bool FancyVectorb1Widget(const std::string& label, glm::ivec1& vec, FancyVectorWidgetSettings settings) { + settings.min = 0.0f; + settings.max = 1.0f; + settings.displayLabelOverride[0] = "B"; + settings.displayLabelColorOverride[0] = Color(0.2f, 0.2f, 0.2f); + settings.displayLabelHoverColorOverride[0] = Color(0.3f, 0.3f, 0.3f); + + return FancyVectori1Widget(label, vec, settings); +} +bool FancyVectorb2Widget(const std::string& label, glm::ivec2& vec, FancyVectorWidgetSettings settings) { + settings.min = 0.0f; + settings.max = 1.0f; + settings.displayLabelOverride[0] = "B"; + settings.displayLabelOverride[1] = "B"; + settings.displayLabelColorOverride[0] = Color(0.2f, 0.2f, 0.2f); + settings.displayLabelColorOverride[1] = Color(0.2f, 0.2f, 0.2f); + settings.displayLabelHoverColorOverride[0] = Color(0.3f, 0.3f, 0.3f); + settings.displayLabelHoverColorOverride[1] = Color(0.3f, 0.3f, 0.3f); + + return FancyVectori2Widget(label, vec, settings); +} +bool FancyVectorb3Widget(const std::string& label, glm::ivec3& vec, FancyVectorWidgetSettings settings) { + settings.min = 0.0f; + settings.max = 1.0f; + settings.displayLabelOverride[0] = "B"; + settings.displayLabelOverride[1] = "B"; + settings.displayLabelOverride[2] = "B"; + settings.displayLabelColorOverride[0] = Color(0.2f, 0.2f, 0.2f); + settings.displayLabelColorOverride[1] = Color(0.2f, 0.2f, 0.2f); + settings.displayLabelColorOverride[2] = Color(0.2f, 0.2f, 0.2f); + settings.displayLabelHoverColorOverride[0] = Color(0.3f, 0.3f, 0.3f); + settings.displayLabelHoverColorOverride[1] = Color(0.3f, 0.3f, 0.3f); + settings.displayLabelHoverColorOverride[2] = Color(0.3f, 0.3f, 0.3f); + + return FancyVectori3Widget(label, vec, settings); +} +bool FancyVectorb4Widget(const std::string& label, glm::ivec4& vec, FancyVectorWidgetSettings settings) { + settings.min = 0.0f; + settings.max = 1.0f; + settings.displayLabelOverride[0] = "B"; + settings.displayLabelOverride[1] = "B"; + settings.displayLabelOverride[2] = "B"; + settings.displayLabelOverride[3] = "B"; + settings.displayLabelColorOverride[0] = Color(0.2f, 0.2f, 0.2f); + settings.displayLabelColorOverride[1] = Color(0.2f, 0.2f, 0.2f); + settings.displayLabelColorOverride[2] = Color(0.2f, 0.2f, 0.2f); + settings.displayLabelColorOverride[3] = Color(0.2f, 0.2f, 0.2f); + settings.displayLabelHoverColorOverride[0] = Color(0.3f, 0.3f, 0.3f); + settings.displayLabelHoverColorOverride[1] = Color(0.3f, 0.3f, 0.3f); + settings.displayLabelHoverColorOverride[2] = Color(0.3f, 0.3f, 0.3f); + settings.displayLabelHoverColorOverride[3] = Color(0.3f, 0.3f, 0.3f); + + return FancyVectori4Widget(label, vec, settings); +} + void detail::BeginWidget(const std::string& label) { float w = ImGui::GetContentRegionAvail().x; @@ -27,7 +207,7 @@ void detail::BeginWidget(const std::string& label) { ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { ImGui::GetStyle().ItemSpacing.x, 0 }); ImGui::SetColumnWidth(0, w - compFieldWidth); ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetStyle().FramePadding.y * 0.5f + 3); - ImGui::Text("%s", label.c_str()); + ImGui::TextUnformatted(label.c_str()); ImGui::NextColumn(); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 0, 0 }); ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetStyle().FramePadding.y * 0.5f); @@ -51,19 +231,18 @@ bool EnumWidget(const std::string& label, int& value, const std::vector items; - for (int i = 0; i < values.size(); i++) { + size_t selected{ std::numeric_limits::max() }; + for (size_t i = 0; i < values.size(); i++) { auto& element = values[i]; if (element.value == value) { selected = i; } - items.push_back(element.prettyName.c_str()); } + assert(selected < values.size()); bool rv{ false }; if (ImGui::BeginCombo(ss.str().c_str(), values[selected].prettyName.c_str())) { - for (int i = 0; i < values.size(); i++) { + for (size_t i = 0; i < values.size(); i++) { bool is_selected = (value == values[i].value); // You can store your selection however you want, outside or inside your objects if (ImGui::Selectable(values[i].prettyName.c_str(), is_selected)) { rv = true; @@ -94,7 +273,7 @@ bool EntityWidget(const std::string& label, Entity& value) { void UneditableEntityWidget(const std::string& label, const Entity value) { BeginWidget(label); int val = EntityTo(value); - ImGui::Text("%s", std::to_string(val).c_str()); + ImGui::TextUnformatted(std::to_string(val).c_str()); EndWidget(); } @@ -225,7 +404,7 @@ bool StringWidget(const std::string& label, std::string& value) { void UneditableStringWidget(const std::string& label, const std::string& value) { BeginWidget(label); - ImGui::Text("%s", value.c_str()); + ImGui::TextUnformatted(value.c_str()); EndWidget(); } @@ -278,15 +457,15 @@ bool ResolutionWidget(const std::string& label, Resolution& resolution) { settingsFBO.displayLabelColorOverride[1] = Color(0.66f, 0.49f, 0.65f); bool rv = FancyVector2Widget(label, res, settingsFBO); - resolution = { static_cast(res.x), static_cast(res.y) }; + resolution = { static_cast(res.x), static_cast(res.y) }; return rv; } bool OrthoCameraWidget(OrthoCamera& cameraData) { - glm::vec1 top{ cameraData._top }; - glm::vec2 leftRight{ cameraData._left, cameraData._right }; - glm::vec1 bottom{ cameraData._bottom }; + glm::vec1 top{ cameraData.TopPlane }; + glm::vec2 leftRight{ cameraData.LeftPlane, cameraData.RightPlane }; + glm::vec1 bottom{ cameraData.BottomPlane }; FancyVectorWidgetSettings settingsOrthoTop; settingsOrthoTop.resetTo = 1; @@ -318,20 +497,19 @@ bool OrthoCameraWidget(OrthoCamera& cameraData) { rv = rv | FancyVector2Widget("Left & Right", leftRight, settingsOrthoLeftRight); // no double pipe because of short circuit shadowing imgui call rv = rv | FancyVector1Widget("Bottom", bottom, settingsOrthoBottom); - cameraData._top = top.x; - cameraData._left = leftRight.x; - cameraData._right = leftRight.y; - cameraData._bottom = bottom.x; + cameraData.TopPlane = top.x; + cameraData.LeftPlane = leftRight.x; + cameraData.RightPlane = leftRight.y; + cameraData.BottomPlane = bottom.x; return rv; } bool PerspectiveCameraWidget(PerspectiveCamera& cameraData) { - glm::vec1 fov{ cameraData._fov }; - glm::vec1 aspect{ cameraData._aspect }; + glm::vec1 fov{ cameraData.FOV }; + glm::vec1 aspect{ cameraData.AspectRatio }; FancyVectorWidgetSettings fovSettings; - auto a = fovSettings.displayLabelColorOverride[0].Lighten(0.5); fovSettings.resetTo = 110; fovSettings.min = 30; fovSettings.max = 150; @@ -348,11 +526,21 @@ bool PerspectiveCameraWidget(PerspectiveCamera& cameraData) { rv = FancyVector1Widget("Field Of View", fov, fovSettings); rv = rv | FancyVector1Widget("Aspect Ratio", aspect, aspectSettings); // no double pipe because of short circuit shadowing imgui call - cameraData._fov = fov.x; + cameraData.FOV = fov.x; return rv; } +bool Image2DButtonWidget(const std::string& label, ImTextureID texture) { + BeginWidget(label); + ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - 128 - ImGui::GetStyle().FramePadding.x); + std::stringstream ss; + ss << "##" << label; + bool rv = ImGui::ImageButton(label.c_str(), texture, { 128, 128 }, { 0, 1 }, { 1, 0 }); + EndWidget(); + return rv; +} + void Space() { ImGui::NewLine(); } @@ -364,7 +552,7 @@ void Header(const std::string& label) { auto boldFont = io.Fonts->Fonts[1]; ImGui::PushFont(boldFont); - ImGui::Text("%s", label.c_str()); + ImGui::TextUnformatted(label.c_str()); ImGui::Separator(); ImGui::PopFont(); @@ -616,8 +804,6 @@ bool ImGui::NeoDoaColorPickerPopup(const char* label, float col[4], ImGuiColorEd bool value_changed_fix_hue_wrap = false; if ((flags & ImGuiColorEditFlags_NoInputs) == 0) { PushItemWidth((alpha_bar ? bar1_pos_x : bar0_pos_x) + bars_width - picker_pos.x); - ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoSmallPreview | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf; - ImGuiColorEditFlags sub_flags = (flags & sub_flags_to_forward) | ImGuiColorEditFlags_NoPicker; if (flags & ImGuiColorEditFlags_DisplayRGB || (flags & ImGuiColorEditFlags_DisplayMask_) == 0) { FancyVectorWidgetSettings settings; settings.fmt = "%g"; diff --git a/Editor/ComponentWidgets.hpp b/Editor/ComponentWidgets.hpp index 0dd68cac..3328375b 100644 --- a/Editor/ComponentWidgets.hpp +++ b/Editor/ComponentWidgets.hpp @@ -7,6 +7,12 @@ #include +#include +#include +#include +#include +#include + inline constexpr int compFieldWidth = 300; // must be divisible by both 3 and 4 (and 1 and 2, but you know... MaThS...) (x,y,z,w) enum class Display { @@ -93,9 +99,25 @@ bool FancyVector2Widget(const std::string& label, glm::vec2& vec, FancyVectorWid bool FancyVector3Widget(const std::string& label, glm::vec3& vec, FancyVectorWidgetSettings settings = defaultFancyVectorSettingsXYZ); bool FancyVector4Widget(const std::string& label, glm::vec4& vec, FancyVectorWidgetSettings settings = defaultFancyVectorSettingsXYZW); +bool FancyVectori1Widget(const std::string& label, glm::ivec1& vec, FancyVectorWidgetSettings settings = defaultFancyVectorSettingsX); +bool FancyVectori2Widget(const std::string& label, glm::ivec2& vec, FancyVectorWidgetSettings settings = defaultFancyVectorSettingsXY); +bool FancyVectori3Widget(const std::string& label, glm::ivec3& vec, FancyVectorWidgetSettings settings = defaultFancyVectorSettingsXYZ); +bool FancyVectori4Widget(const std::string& label, glm::ivec4& vec, FancyVectorWidgetSettings settings = defaultFancyVectorSettingsXYZW); + +bool FancyVectorui1Widget(const std::string& label, glm::uvec1& vec, FancyVectorWidgetSettings settings = defaultFancyVectorSettingsX); +bool FancyVectorui2Widget(const std::string& label, glm::uvec2& vec, FancyVectorWidgetSettings settings = defaultFancyVectorSettingsXY); +bool FancyVectorui3Widget(const std::string& label, glm::uvec3& vec, FancyVectorWidgetSettings settings = defaultFancyVectorSettingsXYZ); +bool FancyVectorui4Widget(const std::string& label, glm::uvec4& vec, FancyVectorWidgetSettings settings = defaultFancyVectorSettingsXYZW); + +bool FancyVectorb1Widget(const std::string& label, glm::ivec1& vec, FancyVectorWidgetSettings settings = defaultFancyVectorSettingsX); +bool FancyVectorb2Widget(const std::string& label, glm::ivec2& vec, FancyVectorWidgetSettings settings = defaultFancyVectorSettingsXY); +bool FancyVectorb3Widget(const std::string& label, glm::ivec3& vec, FancyVectorWidgetSettings settings = defaultFancyVectorSettingsXYZ); +bool FancyVectorb4Widget(const std::string& label, glm::ivec4& vec, FancyVectorWidgetSettings settings = defaultFancyVectorSettingsXYZW); + template bool FancyVectorPiece(FancyVectorWidgetSettings& settings, size_t idx, float* vec, ImFont* buttonFont = nullptr, ImVec2 buttonSize = { 0, 0 }) { bool rv{ false }; + ImGui::PushID(settings.displayLabelID[idx]); if (buttonFont == nullptr) { buttonFont = ImGui::GetIO().Fonts->Fonts[1]; @@ -120,7 +142,11 @@ bool FancyVectorPiece(FancyVectorWidgetSettings& settings, size_t idx, floa if (!settings.disabled) { if (settings.resetEnabled) { rv = true; - vec[idx] = settings.resetTo; + if (settings.resetAllToSame) { + vec[idx] = settings.resetTo; + } else { + vec[idx] = settings.resetTos[idx]; + } } } } @@ -150,6 +176,7 @@ bool FancyVectorPiece(FancyVectorWidgetSettings& settings, size_t idx, floa if (settings.disabled) { ImGui::EndDisabled(); } ImGui::PopItemWidth(); + ImGui::PopID(); return rv; } @@ -170,7 +197,42 @@ bool FancyVectorWidget(const std::string& label, float* vec, FancyVectorWidgetSe ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { ImGui::GetStyle().ItemSpacing.x, 0 }); ImGui::SetColumnWidth(0, w - compFieldWidth); ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetStyle().FramePadding.y * 0.5f); - ImGui::Text("%s", label.c_str()); + ImGui::TextUnformatted(label.c_str()); + if (ImGui::BeginPopupContextItem(label.c_str(), ImGuiPopupFlags_MouseButtonRight)) { + if (ImGui::MenuItem(cat(ComponentWidgetIcons::ContextMenu::ResetIcon, ComponentWidgetStrings::ContextMenu::Reset))) { + rv = true; + if (settings.resetAllToSame) { + vec[0] = settings.resetTo; + if constexpr (dsp == Display::XY) { + vec[1] = settings.resetTo; + } + if constexpr (dsp == Display::XYZ) { + vec[1] = settings.resetTo; + vec[2] = settings.resetTo; + } + if constexpr (dsp == Display::XYZW) { + vec[1] = settings.resetTo; + vec[2] = settings.resetTo; + vec[3] = settings.resetTo; + } + } else { + vec[0] = settings.resetTos[0]; + if constexpr (dsp == Display::XY) { + vec[1] = settings.resetTos[1]; + } + if constexpr (dsp == Display::XYZ) { + vec[1] = settings.resetTos[1]; + vec[2] = settings.resetTos[2]; + } + if constexpr (dsp == Display::XYZW) { + vec[1] = settings.resetTos[1]; + vec[2] = settings.resetTos[2]; + vec[3] = settings.resetTos[3]; + } + } + } + ImGui::EndPopup(); + } ImGui::NextColumn(); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 0, 0 }); ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetStyle().FramePadding.y * 0.5f); @@ -250,6 +312,8 @@ bool OrthoCameraWidget(OrthoCamera& cameraData); bool PerspectiveCameraWidget(PerspectiveCamera& cameraData); +bool Image2DButtonWidget(const std::string& label, ImTextureID texture); + void Space(); void Header(const std::string& label); @@ -266,8 +330,100 @@ template requires requires (T x) { std::to_string(x); } void UneditableArrayWidget(const std::string& label, const std::vector& array) { BeginWidget(label); - for (int i = 0; i < array.size(); i++) { - ImGui::Text("%s", std::to_string(array[i]).c_str()); + for (size_t i = 0; i < array.size(); i++) { + ImGui::TextUnformatted(std::to_string(array[i]).c_str()); + } + EndWidget(); +} + +template +bool MultiAssetWidget(std::string_view label, std::vector& uuids, const Assets& assets, MetaAssetInfoBank& metaBank, Filter filter) { + ImGuiIO& io = ImGui::GetIO(); + auto boldFont = io.Fonts->Fonts[1]; + float lineHeight = boldFont->FontSize + ImGui::GetStyle().FramePadding.y * 2.0f; + ImVec2 buttonSize = { lineHeight, lineHeight }; + float margin = 3.0f; + + for (size_t i = 0; i < uuids.size(); i++) { + UUID id = uuids[i]; + + BeginWidget(std::format("{}[{}]: ", label.data(), i)); + + ImVec4 textColor; + std::string_view assetIcon; + std::string_view assetName; + if (id != UUID::Empty()) { + AssetHandle handle = assets.FindAsset(id); + if (handle.HasValue()) { + textColor = ImGui::GetStyleColorVec4(ImGuiCol_Text); + assetIcon = metaBank.GetMetaInfoOf(handle->File()).fa_icon; + assetName = handle->File().Name(); + } else { + textColor = MultiAssetWidgetColors::DeletedOrUnloadedColor; + assetIcon = ICON_FA_CIRCLE_EXCLAMATION; + assetName = "Deleted or unloaded"; + } + } else { + textColor = MultiAssetWidgetColors::EmptyColor; + assetIcon = ICON_FA_TRIANGLE_EXCLAMATION; + assetName = "Empty"; + } + + std::string name{ std::format("{} {} (UUID: {})", assetIcon, assetName.data(), id.AsString()) }; + + float x = ImGui::GetCursorPosX(); + ImGui::SetNextItemWidth(compFieldWidth - margin - buttonSize.x); + float oldDisabledAlpha = ImGui::GetStyle().DisabledAlpha; + ImGui::GetStyle().DisabledAlpha = 1.0f; + ImGui::BeginDisabled(); + ImGui::PushStyleColor(ImGuiCol_Text, textColor); + ImGui::InputText("", name.data(), name.size(), ImGuiInputTextFlags_ReadOnly); // TODO accept drag-drop if (filter(dropped, assets)) + if (ImGui::BeginDragDropTarget()) { + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("DND_DEMO_CELL")) { + UUID data = *(const UUID*) payload->Data; + AssetHandle handle = assets.FindAsset(data); + assert(handle.HasValue()); + if (filter(data, assets)) { + uuids[i] = data; + } + } + ImGui::EndDragDropTarget(); + } + ImGui::PopStyleColor(); + ImGui::EndDisabled(); + ImGui::GetStyle().DisabledAlpha = oldDisabledAlpha; + x += compFieldWidth - buttonSize.x; + + ImGui::PushFont(boldFont); + ImGui::SameLine(); + ImGui::SetCursorPosX(x); + ImGui::Button(ICON_FA_BULLSEYE_POINTER, buttonSize); // TODO open popup. pass filter + + ImGui::PopFont(); + EndWidget(); } + BeginWidget(""); + ImGui::PushFont(boldFont); + float x = ImGui::GetCursorPosX() + compFieldWidth - 2 * (buttonSize.x) - margin; + + ImGui::SameLine(); + ImGui::SetCursorPosX(x); + bool isEmpty = uuids.empty(); + ImGui::BeginDisabled(isEmpty); + if (ImGui::Button(ICON_FA_MINUS, buttonSize)) { + uuids.pop_back(); + } + ImGui::EndDisabled(); + x += margin + buttonSize.x; + + ImGui::SameLine(); + ImGui::SetCursorPosX(x); + if (ImGui::Button(ICON_FA_PLUS_LARGE, buttonSize)) { + uuids.push_back(UUID::Empty()); + } + x += margin + buttonSize.x; + + ImGui::PopFont(); EndWidget(); + return true; } \ No newline at end of file diff --git a/Editor/Console.hpp b/Editor/Console.hpp index 33e6ddf8..672eb2bb 100644 --- a/Editor/Console.hpp +++ b/Editor/Console.hpp @@ -42,7 +42,7 @@ struct Console { LogSeverity selectedSeverity{ LogSeverity::TRACE }; - int oldCount{ 0 }; + unsigned oldCount{ 0 }; void RenderTopPanel(); void RenderFilterButtons(); diff --git a/Editor/DisplayTargetRenderer.cpp b/Editor/DisplayTargetRenderer.cpp new file mode 100644 index 00000000..60564b10 --- /dev/null +++ b/Editor/DisplayTargetRenderer.cpp @@ -0,0 +1,586 @@ +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +DisplayTargetRenderer::DisplayTargetRenderer(Observer& observer) noexcept : + observer(observer), + sceneDisplay(observer), + componentDefinitionDisplay(observer), + shaderDisplay(observer), + shaderProgramDisplay(observer), + materialDisplay(observer), + samplerDisplay(observer) { + GUI& gui = observer.gui; + gui.Events.OnProjectUnloaded += std::bind_front(&DisplayTargetRenderer::OnProjectUnloaded, this); + gui.Events.OnReimport += std::bind_front(&DisplayTargetRenderer::OnReimport, this); + gui.Events.OnAssetDeleted += std::bind_front(&DisplayTargetRenderer::OnAssetDeleted, this); + gui.Events.AssetManager.OnAssetFocused += std::bind_front(&DisplayTargetRenderer::OnAssetFocused, this); + gui.Events.AssetManager.OnFolderFocused += std::bind_front(&DisplayTargetRenderer::OnFolderFocused, this); + gui.Events.AssetManager.OnFocusLost += std::bind_front(&DisplayTargetRenderer::OnFocusLost, this); + gui.Events.OnSceneClosed += std::bind_front(&DisplayTargetRenderer::OnSceneClosed, this); + gui.Events.OnEntityDeleted += std::bind_front(&DisplayTargetRenderer::OnEntityDeleted, this); + gui.Events.SceneHierarchy.OnEntitySelected += std::bind_front(&DisplayTargetRenderer::OnEntitySelected, this); + gui.Events.SceneHierarchy.OnEntityDeselected += std::bind_front(&DisplayTargetRenderer::OnEntityDeselected, this); +} + +void DisplayTargetRenderer::SetDisplayTarget(Entity entity) { + static std::string hypen(" - "); + displayTarget = entity; + renderTargetTitleText = hypen + observer.get().gui.get().GetOpenScene().GetComponent(entity).GetTagRef(); +} +void DisplayTargetRenderer::SetDisplayTarget(FNode& file) { + static std::string hypen(" - "); + displayTarget = &file; + renderTargetTitleText = hypen + file.Name().data(); +} +void DisplayTargetRenderer::ResetDisplayTarget() { + displayTarget = std::monostate{}; + renderTargetTitleText = ""; +} + +void DisplayTargetRenderer::Render() { + std::visit(overloaded::lambda{ + [this](std::monostate) { + HandleTargetWhenEmpty(); + }, + [this](Entity entity) { + GUI& gui = observer.get().gui.get(); + if (!gui.HasOpenScene()) { assert(false); } + + Scene& scene = gui.GetOpenScene(); + HandleTargetWhenEntity(scene, entity); + }, + [this](FNode* file) { + HandleTargetWhenFile(*file); + } + }, displayTarget); +} + +void DisplayTargetRenderer::HandleTargetWhenEmpty() { + static constexpr const char text[] = "Nothing to display here :)"; + ImVec2 size = ImGui::GetContentRegionAvail(); + ImVec2 txtSize = ImGui::CalcTextSize(text); + for (int i = 0; i < 5; i++) { + ImGui::SameLine((size.x - txtSize.x) / 2, 0); + ImGui::Text(text); + ImGui::NewLine(); + } +} +void DisplayTargetRenderer::HandleTargetWhenEntity(Scene& scene, const Entity entt) { + const auto& idComponent = scene.GetComponent(entt); + ComponentUI::RenderIDComponent(observer, idComponent); + + const auto& transformComponent = scene.GetComponent(entt); + ComponentUI::RenderTransformComponent(observer, transformComponent); + + if (scene.HasComponent(entt)) { + const auto& parentComponent = scene.GetComponent(entt); + ComponentUI::RenderParentComponent(observer, parentComponent); + } + + if (scene.HasComponent(entt)) { + const auto& childComponent = scene.GetComponent(entt); + ComponentUI::RenderChildComponent(observer, childComponent); + } + + if (scene.HasComponent(entt)) { + const auto& orthoCameraComponent = scene.GetComponent(entt); + ComponentUI::RenderOrthoCameraComponent(observer, orthoCameraComponent); + } + + if (scene.HasComponent(entt)) { + const auto& perspectiveCameraComponent = scene.GetComponent(entt); + ComponentUI::RenderPerspectiveCameraComponent(observer, perspectiveCameraComponent); + } + + if (scene.HasComponent(entt)) { + const auto& multiMaterialComponent = scene.GetComponent(entt); + ComponentUI::RenderMultiMaterialComponent(observer, multiMaterialComponent); + } + + if (scene.HasComponent(entt)) { + const auto& storageComponent = scene.GetComponent(entt); + ComponentUI::RenderUserDefinedComponentStorage(observer, storageComponent); + } +} +void DisplayTargetRenderer::HandleTargetWhenFile(FNode& file) { + GUI& gui = observer.get().gui; + + static const ImVec2 iconSize{ 60.0f, 60.0f }; + ImGui::Columns(2); + ImGui::SetColumnWidth(0, iconSize.x * 1.25f); + ImGui::PushFont(gui.GetFontBold()); + + ImGui::PushStyleColor(ImGuiCol_Button, {}); + auto prePos = ImGui::GetCursorPos(); + auto& meta{ gui.GetMetaInfoOf(file) }; + if (ImGui::ImageButton(meta.GetSVGIcon(), iconSize)) { + ImGui::OpenPopup("assetIconCombo"); + } + auto afterPos = ImGui::GetCursorPos(); + ImGui::SetCursorPos({ prePos.x, prePos.y + iconSize.y }); + RenderIconChangePopup(file, meta); + ImGui::SetCursorPos(afterPos); + ImGui::PopStyleColor(); + + ImGui::SameLine(); + ImGui::NextColumn(); + + ImGui::TextUnformatted(file.Name().data()); + + ImGui::PopFont(); + ImGui::PushFont(gui.GetFont()); + + std::string path = file.Path().string(); + ImGui::TextUnformatted(std::string("Path: ROOT").append(sizeof(char), static_cast(std::filesystem::path::preferred_separator)).append(path).c_str()); + std::string absolutePath = file.AbsolutePath().string(); + ImGui::TextUnformatted(std::string("Absolute Path: ").append(absolutePath).c_str()); + + ImGui::PopFont(); + + ImGui::Columns(1); + ImGui::Separator(); + + if (file.IsDirectory()) { + RenderFolderView(file); + } else { + AssetHandle h = gui.CORE->GetAssets()->FindAssetAt(file); + if (h.HasValue()) { + RenderAssetView(h); + } + } +} +void DisplayTargetRenderer::RenderIconChangePopup(const FNode& file, MetaAssetInfo& meta) { + if (ImGui::BeginPopup("assetIconCombo")) { + const FileIcons::ElementType* begin{ nullptr }, * end{ nullptr }; + if (file.IsDirectory()) { + auto& items = FileIcons::DirectoryIcons; + begin = &items.front(); + end = &items.back() + 1; + } else if (Assets::IsSceneFile(file)) { + auto& items = FileIcons::SceneIcons; + begin = &items.front(); + end = &items.back() + 1; + } else if (Assets::IsComponentDefinitionFile(file)) { + auto& items = FileIcons::ComponentIcons; + begin = &items.front(); + end = &items.back() + 1; + } else if (Assets::IsShaderFile(file)) { + auto& items = FileIcons::ShaderIcons; + begin = &items.front(); + end = &items.back() + 1; + } else if (Assets::IsShaderProgramFile(file)) { + auto& items = FileIcons::ShaderProgramIcons; + begin = &items.front(); + end = &items.back() + 1; + } else if (Assets::IsMaterialFile(file)) { + auto& items = FileIcons::MaterialIcons; + begin = &items.front(); + end = &items.back() + 1; + } else if (Assets::IsSamplerFile(file)) { + auto& items = FileIcons::SamplerIcons; + begin = &items.front(); + end = &items.back() + 1; + } else if (Assets::IsTextureFile(file)) { + auto& items = FileIcons::TextureIcons; + begin = &items.front(); + end = &items.back() + 1; + } else { + auto& items = FileIcons::RegularFileIcons; + begin = &items.front(); + end = &items.back() + 1; + } + + assert(begin); + assert(end); + auto selectedItem = begin + meta.icon_index; + for (auto current = begin; current != end; current++) { + const bool isSelected{ current == selectedItem }; + if (ImGui::Selectable(current->first, isSelected)) { + selectedItem = current; + } + // Set the initial focus when opening the combo (scrolling + keyboard navigation focus) + if (isSelected) { + ImGui::SetItemDefaultFocus(); + } + } + + meta.icon_index = static_cast(selectedItem - begin); + meta.fa_icon = selectedItem->first; + meta.svg_icon_key = selectedItem->second; + ImGui::EndPopup(); + } +} + +void DisplayTargetRenderer::RenderFolderView(FNode& folder) { + assert(folder.IsDirectory()); + GUI& gui = observer.get().gui; + + ImGuiTableFlags flags = ImGuiTableFlags_RowBg | + ImGuiTableFlags_Borders | + ImGuiTableFlags_Resizable | + ImGuiTableFlags_SizingFixedFit | + ImGuiTableFlags_NoBordersInBodyUntilResize; + + if (ImGui::BeginTable("folderview", 3, flags)) { + + ImGui::TableSetupColumn("", ImGuiTableColumnFlags_NoResize); + ImGui::TableSetupColumn("Files", ImGuiTableColumnFlags_WidthStretch, 0.6f); + ImGui::TableSetupColumn("Size", ImGuiTableColumnFlags_WidthStretch, 0.3f); + ImGui::TableHeadersRow(); + + for (auto& child : folder.Children()) { + if (!child.IsDirectory() && !gui.CORE->GetAssets()->IsAssetExistsAt(child)) { continue; } + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::Image(gui.GetMetaInfoOf(child).GetSVGIcon(TextureSize::SMALL), { 16.0f, 16.0f }); + ImGui::TableSetColumnIndex(1); + ImGui::TextUnformatted(child.FullName().data()); + ImGui::TableSetColumnIndex(2); + std::string size = FormatBytes(static_cast(child.Size())); + ImGui::TextUnformatted(size.c_str()); + } + ImGui::EndTable(); + } +} +void DisplayTargetRenderer::RenderAssetView(AssetHandle h) { + if (h->IsScene()) { + RenderSceneView(h); + } else if (h->IsComponentDefinition()) { + RenderComponentDefinitionView(h); + } else if (h->IsShader()) { + RenderShaderView(h); + } else if (h->IsShaderProgram()) { + RenderShaderProgramView(h); + } else if (h->IsMaterial()) { + RenderMaterialView(h); + } else if (h->IsSampler()) { + RenderSamplerView(h); + } else if (h->IsTexture()) { + RenderTextureView(h); + } else { + RenderTextView(h); + } +} +void DisplayTargetRenderer::RenderSceneView(AssetHandle h) { + assert(h->IsScene()); + + const Scene& scene = h->DataAs(); + + sceneDisplay.RenderEntities(scene); + sceneDisplay.RenderSystems(scene); +} +void DisplayTargetRenderer::RenderComponentDefinitionView(AssetHandle h) { + assert(h->IsComponentDefinition()); + + componentDefinitionDisplay.SetDisplayTarget(h); + componentDefinitionDisplay.RenderMessagesTable(); + + if (h->HasDeserializedData()) { + if (!h->HasErrorMessages()) { + componentDefinitionDisplay.RenderFields(); + ImGui::Separator(); + componentDefinitionDisplay.RenderSourceCode(); + } + } else { + ImGui::Text("Component Definition is not deserialized..."); + } + + static int extraPadding = 16; + float lineHeight = ImGui::GetTextLineHeight() + ImGui::GetStyle().FramePadding.y * 2; + if (ImGui::GetContentRegionAvail().y > 34.0f) { + ImGui::SetCursorPosY(ImGui::GetWindowHeight() - lineHeight - extraPadding); + } + + if (ImGui::Button("Refresh", { ImGui::GetContentRegionAvail().x, 0 })) { + h->ForceDeserialize(); + } + + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); + ImGui::TextUnformatted("Forces deserialization on this component definition. All data in RAM is purged, and new data is read from disk. This operation will cause re-instantiation of user defined components without loss of data."); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } +} +void DisplayTargetRenderer::RenderShaderView(AssetHandle h) { + assert(h->IsShader()); + + shaderDisplay.SetDisplayTarget(h); + shaderDisplay.RenderMessagesTable(); + + if (h->HasDeserializedData()) { + if (!h->HasErrorMessages()) { + shaderDisplay.RenderFields(); + ImGui::Separator(); + shaderDisplay.RenderSourceCode(); + } + } else { + ImGui::Text("Shader is not deserialized..."); + } + + static int extraPadding = 16; + float lineHeight = ImGui::GetTextLineHeight() + ImGui::GetStyle().FramePadding.y * 2; + if (ImGui::GetContentRegionAvail().y > 34.0f) { + ImGui::SetCursorPosY(ImGui::GetWindowHeight() - lineHeight - extraPadding); + } + + if (ImGui::Button("Refresh", { ImGui::GetContentRegionAvail().x, 0 })) { + h->ForceDeserialize(); + } + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); + ImGui::TextUnformatted("Forces deserialization on this shader. All data in VRAM is purged, and new data is allocated."); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } +} +void DisplayTargetRenderer::RenderShaderProgramView(AssetHandle h) { + assert(h->IsShaderProgram()); + + shaderProgramDisplay.SetDisplayTarget(*observer.get().gui.get().CORE->GetAssets(), h); + shaderProgramDisplay.RenderMessagesTable(); + ImGui::Separator(); + if (h->HasDeserializedData()) { + shaderProgramDisplay.RenderShaders(); + } else { + ImGui::Text("Shader Program is not deserialized..."); + } + + static int extraPadding = 16; + float lineHeight = ImGui::GetTextLineHeight() + ImGui::GetStyle().FramePadding.y * 2; + if (ImGui::GetContentRegionAvail().y > 34.0f) { + ImGui::SetCursorPosY(ImGui::GetWindowHeight() - lineHeight - extraPadding); + } + + if (ImGui::Button("Refresh", { ImGui::GetContentRegionAvail().x, 0 })) { + h->ForceDeserialize(); + } + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); + ImGui::TextUnformatted("Forces deserialization on this shader program. All data in VRAM is purged, and new data is allocated."); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } +} +void DisplayTargetRenderer::RenderMaterialView(AssetHandle h) { + assert(h->IsMaterial()); + + materialDisplay.SetDisplayTarget(*observer.get().gui.get().CORE->GetAssets(), h); + materialDisplay.RenderMessagesTable(); + ImGui::Separator(); + if (h->HasDeserializedData()) { + materialDisplay.RenderProgramCombo(); + ImGui::Separator(); + materialDisplay.RenderShaderUniforms(); + } else { + ImGui::Text("Material is not deserialized..."); + } + + static int extraPadding = 16; + float lineHeight = ImGui::GetTextLineHeight() + ImGui::GetStyle().FramePadding.y * 2; + if (ImGui::GetContentRegionAvail().y > 34.0f) { + ImGui::SetCursorPosY(ImGui::GetWindowHeight() - lineHeight - extraPadding); + } + + if (ImGui::Button("Refresh", { ImGui::GetContentRegionAvail().x, 0 })) { + h->ForceDeserialize(); + } + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); + ImGui::TextUnformatted("Forces deserialization on material. All data in RAM is purged, and new data is allocated."); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } +} +void DisplayTargetRenderer::RenderSamplerView(AssetHandle h) { + assert(h->IsSampler()); + + samplerDisplay.SetDisplayTarget(h); + samplerDisplay.RenderMessagesTable(); + ImGui::Separator(); + if (h->HasDeserializedData() && !h->HasErrorMessages()) { + samplerDisplay.RenderSamplerParameters(); + } else { + ImGui::Text("Sampler is not deserialized..."); + } + + static int extraPadding = 16; + float lineHeight = ImGui::GetTextLineHeight() + ImGui::GetStyle().FramePadding.y * 2; + if (ImGui::GetContentRegionAvail().y > 34.0f) { + ImGui::SetCursorPosY(ImGui::GetWindowHeight() - lineHeight - extraPadding); + } + + if (ImGui::Button("Refresh", { ImGui::GetContentRegionAvail().x, 0 })) { + h->ForceDeserialize(); + } + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); + ImGui::TextUnformatted("Forces deserialization on sampler. All data in RAM is purged, and new data is allocated."); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } +} +void DisplayTargetRenderer::RenderTextureView(AssetHandle h) { + assert(h->IsTexture()); + /* channels */ + static bool r{ true }, g{ true }, b{ true }, a{ true }; + /* inspect params */ + static bool drawInspector{ true }, drawHistogram{ false }, drawNormals{ false }; + + static int bottomContentLineCount = 3; + float lineHeight = ImGui::GetTextLineHeight() + ImGui::GetStyle().FramePadding.y * 2; + static float extraPadding = 16; /* pad by an extra amount to remove scroll bar, don't pad and see the scroll bar appear on right side */ + //ImGui::SliderFloat("label", &extraPadding, 0, 50); /* was used to test padding amount, not deleted to easily re-test in future */ + float totalBottomPadding = lineHeight * bottomContentLineCount + extraPadding; + + auto [windowWidth, windowHeight] = ImGui::GetContentRegionAvail(); + windowWidth = windowWidth - ImGui::GetStyle().FramePadding.x; + windowHeight = windowHeight - totalBottomPadding; + + if (h->HasDeserializedData()) { + const GPUTexture* gpuTex = observer.get().gui.get().CORE->GetAssetGPUBridge()->GetTextures().Query(h->ID()); + assert(gpuTex); + const Texture& tex = h->DataAs(); + + float w = static_cast(tex.Width); + float h = static_cast(tex.Height); + float aspect = w / h; + + float maxWidth = windowWidth; + float maxHeight = windowHeight; + + w = maxWidth; + h = w / aspect; + + if (h > maxHeight) { + aspect = w / h; + h = maxHeight; + w = h * aspect; + } + + ImGui::Image(*gpuTex, { w, h }, { 0, 1 }, { 1, 0 }, { (float) r, (float) g, (float) b, (float) a }, { 1, 1, 0, 1 }); + + if (drawInspector) { + ImRect rc = ImRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax()); + ImVec2 mouseUVCoord = (ImGui::GetIO().MousePos - rc.Min) / rc.GetSize(); + mouseUVCoord.y = 1.f - mouseUVCoord.y; + if (mouseUVCoord.x >= 0.0f && + mouseUVCoord.y >= 0.0f && + mouseUVCoord.x <= 1.0f && + mouseUVCoord.y <= 1.0f) { + float w = static_cast(tex.Width); + float h = static_cast(tex.Height); + auto pixels = reinterpret_cast(tex.PixelData.data()); + ImageInspect::inspect(static_cast(w), static_cast(h), pixels, mouseUVCoord, { w, h }, drawNormals, drawHistogram); + } + } + } else { + ImGui::Text("Texture is not deserialized..."); + } + + ImGui::SetCursorPosY(ImGui::GetWindowHeight() - totalBottomPadding); + ImGui::AlignTextToFramePadding(); ImGui::Text("Channels"); + ImGui::SameLine(); ImGui::TextColored({ 1.0f, 0.0f, 0.0f, 1.0f }, "R:"); ImGui::SameLine(); ImGui::Checkbox("##r", &r); + ImGui::SameLine(); ImGui::TextColored({ 0.0f, 1.0f, 0.0f, 1.0f }, "G:"); ImGui::SameLine(); ImGui::Checkbox("##g", &g); + ImGui::SameLine(); ImGui::TextColored({ 0.2f, 0.2f, 1.0f, 1.0f }, "B:"); ImGui::SameLine(); ImGui::Checkbox("##b", &b); + ImGui::SameLine(); ImGui::TextColored({ 0.5f, 0.5f, 0.5f, 1.0f }, "A:"); ImGui::SameLine(); ImGui::Checkbox("##a", &a); + + ImGui::AlignTextToFramePadding(); ImGui::Text("Image Inspect:"); ImGui::SameLine(); ImGui::Checkbox("##inspect", &drawInspector); + ImGui::SameLine(); ImGui::Text("Normals:"); ImGui::SameLine(); ImGui::Checkbox("##normals", &drawNormals); + ImGui::SameLine(); ImGui::Text("Histogram:"); ImGui::SameLine(); ImGui::Checkbox("##histogram", &drawHistogram); + if (ImGui::Button("Refresh", { ImGui::GetContentRegionAvail().x, 0 })) { + h->ForceDeserialize(); + } + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); + ImGui::TextUnformatted("Forces deserialization on this texture. All data in RAM/VRAM is purged, and new data is read from disk."); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } +} +void DisplayTargetRenderer::RenderTextView(AssetHandle h) { + if (!h->File().HasContent()) { + h->File().ReadContent(); + } + std::string content{ h->File().Content() }; + + ImGuiTableFlags flags = ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders; + if (ImGui::BeginTable("content", 1, flags)) { + ImGui::TableSetupColumn("File Content", ImGuiTableColumnFlags_WidthStretch); + ImGui::TableHeadersRow(); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + + auto size = ImGui::GetContentRegionAvail(); + size.y -= 5; /* this "hides" the "1px vertical scroll-bar" in observer window */ + ImGuiInputTextFlags flags = ImGuiInputTextFlags_ReadOnly; + ImGui::InputTextMultiline("", content.data(), content.size(), size, flags); + + ImGui::EndTable(); + } +} + +void DisplayTargetRenderer::OnProjectUnloaded() { + ResetDisplayTarget(); +} +void DisplayTargetRenderer::OnReimport([[maybe_unused]] Assets& assets) { + if (!std::holds_alternative(displayTarget)) { return; } +} +void DisplayTargetRenderer::OnAssetDeleted(AssetHandle asset) { + if (std::holds_alternative(displayTarget) && + &asset->File() == std::get(displayTarget)) { + ResetDisplayTarget(); + } +} +void DisplayTargetRenderer::OnAssetFocused(AssetHandle asset) { + SetDisplayTarget(asset->File()); +} +void DisplayTargetRenderer::OnFolderFocused(FNode& folder) { + SetDisplayTarget(folder); +} +void DisplayTargetRenderer::OnFocusLost() { + ResetDisplayTarget(); +} +void DisplayTargetRenderer::OnSceneClosed() { + if (std::holds_alternative(displayTarget)) { + ResetDisplayTarget(); + } +} +void DisplayTargetRenderer::OnEntityDeleted(Entity entity) { + if (std::holds_alternative(displayTarget) && entity == std::get(displayTarget)) { + ResetDisplayTarget(); + } +} +void DisplayTargetRenderer::OnEntitySelected(Entity entity) { + SetDisplayTarget(entity); +} +void DisplayTargetRenderer::OnEntityDeselected() { + if (std::holds_alternative(displayTarget)) { + ResetDisplayTarget(); + } +} \ No newline at end of file diff --git a/Editor/DisplayTargetRenderer.hpp b/Editor/DisplayTargetRenderer.hpp new file mode 100644 index 00000000..25628d26 --- /dev/null +++ b/Editor/DisplayTargetRenderer.hpp @@ -0,0 +1,74 @@ +#pragma once + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +struct Scene; + +struct Observer; +struct MetaAssetInfo; + +#define DISPLAYABLE Entity, FNode* +using DisplayTarget = std::variant; +#undef DISPLAYABLE + +struct DisplayTargetRenderer { + + DisplayTargetRenderer(Observer& observer) noexcept; + + void SetDisplayTarget(Entity entity); + void SetDisplayTarget(FNode& file); + void ResetDisplayTarget(); + + void Render(); +private: + + std::reference_wrapper observer; + DisplayTarget displayTarget; + std::string renderTargetTitleText; + + SceneDisplay sceneDisplay; + ComponentDefinitionDisplay componentDefinitionDisplay; + ShaderDisplay shaderDisplay; + ShaderProgramDisplay shaderProgramDisplay; + MaterialDisplay materialDisplay; + SamplerDisplay samplerDisplay; + + void HandleTargetWhenEmpty(); + void HandleTargetWhenEntity(Scene& scene, const Entity entt); + void HandleTargetWhenFile(FNode& file); + void RenderIconChangePopup(const FNode& file, MetaAssetInfo& meta); + + void RenderFolderView(FNode& folder); + void RenderAssetView(AssetHandle h); + void RenderSceneView(AssetHandle h); + void RenderComponentDefinitionView(AssetHandle h); + void RenderShaderView(AssetHandle h); + void RenderShaderProgramView(AssetHandle h); + void RenderMaterialView(AssetHandle h); + void RenderSamplerView(AssetHandle h); + void RenderTextureView(AssetHandle h); + void RenderTextView(AssetHandle h); + + void OnProjectUnloaded(); + void OnReimport(Assets& assets); + void OnAssetDeleted(AssetHandle asset); + void OnAssetFocused(AssetHandle asset); + void OnFolderFocused(FNode& folder); + void OnFocusLost(); + void OnSceneClosed(); + void OnEntityDeleted(Entity entity); + void OnEntitySelected(Entity entity); + void OnEntityDeselected(); +}; \ No newline at end of file diff --git a/Editor/EditorMeta.cpp b/Editor/EditorMeta.cpp index 92d35ee1..435cdf6b 100644 --- a/Editor/EditorMeta.cpp +++ b/Editor/EditorMeta.cpp @@ -40,7 +40,7 @@ void EditorMeta::CreateMetaAssetInfoBank() noexcept { MetaAssetInfoBank::LoadFromDisk(*metaAssetInfoBank.get(), *editorMetaFolder); } -void EditorMeta::OnProjectLoaded(const Project& project) noexcept { +void EditorMeta::OnProjectLoaded([[maybe_unused]] const Project& project) noexcept { static const auto& Core = Core::GetCore(); const auto& assets = Core->GetAssets(); CreateHiddenMetaDataFolderIfNotExists(assets->Root()); @@ -51,7 +51,7 @@ void EditorMeta::OnProjectLoaded(const Project& project) noexcept { ImGui::LoadIniSettingsFromDisk(ImGuiSettingsFileName); std::filesystem::current_path(current); } -void EditorMeta::OnProjectSaved(const Project& project) noexcept { +void EditorMeta::OnProjectSaved([[maybe_unused]] const Project& project) noexcept { MetaAssetInfoBank::SaveToDisk(*metaAssetInfoBank.get(), *editorMetaFolder); } void EditorMeta::OnProjectUnloaded() noexcept { diff --git a/Editor/GUI.cpp b/Editor/GUI.cpp index 76e5ac64..f1455661 100644 --- a/Editor/GUI.cpp +++ b/Editor/GUI.cpp @@ -20,17 +20,17 @@ GUI::GUI(const CorePtr& core) noexcept : Events.OnReimport += std::bind_front(&GUI::OnReimport , this); Events.OnAssetDeleted += std::bind_front(&GUI::OnAssetDeleted, this); - //shortcutHandler.RegisterShortcut(Shortcuts::NewProjectShortcut, [this]() { ShowNewProjectModal(); }, ImGuiInputFlags_RouteGlobalLow); - //shortcutHandler.RegisterShortcut(Shortcuts::OpenProjectShortcut, [this]() { ShowOpenProjectModal(); }, ImGuiInputFlags_RouteGlobalLow); - shortcutHandler.RegisterShortcut(Shortcuts::SaveProjectShortcut, [this]() { SaveProjectToDisk(); }, ImGuiInputFlags_RouteGlobalLow); - shortcutHandler.RegisterShortcut(Shortcuts::CloseProjectShortcut, [this]() { CloseProject(); }, ImGuiInputFlags_RouteGlobalLow); + //shortcutHandler.RegisterShortcut(Shortcuts::NewProjectShortcut, [this]() { ShowNewProjectModal(); }, ImGuiInputFlags_RouteGlobal); + //shortcutHandler.RegisterShortcut(Shortcuts::OpenProjectShortcut, [this]() { ShowOpenProjectModal(); }, ImGuiInputFlags_RouteGlobal); + shortcutHandler.RegisterShortcut(Shortcuts::SaveProjectShortcut, [this]() { SaveProjectToDisk(); }, ImGuiInputFlags_RouteGlobal); + shortcutHandler.RegisterShortcut(Shortcuts::CloseProjectShortcut, [this]() { CloseProject(); }, ImGuiInputFlags_RouteGlobal); - shortcutHandler.RegisterShortcut(Shortcuts::UndoShortcut, [this]() { UndoLastCommand(); }, ImGuiInputFlags_RouteGlobalLow); - shortcutHandler.RegisterShortcut(Shortcuts::RedoShortcut, [this]() { RedoLastCommand(); }, ImGuiInputFlags_RouteGlobalLow); + shortcutHandler.RegisterShortcut(Shortcuts::UndoShortcut, [this]() { UndoLastCommand(); }, ImGuiInputFlags_RouteGlobal); + shortcutHandler.RegisterShortcut(Shortcuts::RedoShortcut, [this]() { RedoLastCommand(); }, ImGuiInputFlags_RouteGlobal); - shortcutHandler.RegisterShortcut(Shortcuts::NewSceneShortcut, [this]() { ShowNewSceneAssetModal(*am.GetCurrentFolder()); }, ImGuiInputFlags_RouteGlobalLow); - shortcutHandler.RegisterShortcut(Shortcuts::SaveSceneShortcut, [this]() { SaveScene(); }, ImGuiInputFlags_RouteGlobalLow); - shortcutHandler.RegisterShortcut(Shortcuts::CloseSceneShortcut, [this]() { CloseScene(); }, ImGuiInputFlags_RouteGlobalLow); + shortcutHandler.RegisterShortcut(Shortcuts::NewSceneShortcut, [this]() { ShowNewSceneAssetModal(*am.GetCurrentFolder()); }, ImGuiInputFlags_RouteGlobal); + shortcutHandler.RegisterShortcut(Shortcuts::SaveSceneShortcut, [this]() { SaveScene(); }, ImGuiInputFlags_RouteGlobal); + shortcutHandler.RegisterShortcut(Shortcuts::CloseSceneShortcut, [this]() { CloseScene(); }, ImGuiInputFlags_RouteGlobal); } void GUI::Prepare() { @@ -154,13 +154,13 @@ void GUI::operator() (float delta) { if (urh.Begin()) { urh.Render(); - urh.End(); } + urh.End(); - if (svcs.Begin()) { + if (svcs.Begin(sv.GetViewportCameraSettingsButtonPosition())) { svcs.Render(); - svcs.End(); } + svcs.End(); nam.Render(); @@ -214,7 +214,6 @@ void GUI::OpenProjectFromDisk(const std::string& path) { CORE->LoadProject(path); assert(HasOpenProject()); - Assets& assets = *CORE->GetAssets(); Project& project = GetOpenProject(); std::string title = defaultWindowName; @@ -237,7 +236,6 @@ void GUI::OpenProjectFromDisk(const std::string& path) { void GUI::CloseProject() { Events.OnProjectUnloaded(); - obs.ResetDisplayTarget(); CloseScene(); CORE->UnloadProject(); @@ -250,7 +248,7 @@ void GUI::CreateNewScene(FNode& folder, std::string_view name) { if (!HasOpenProject()) { return; } const Scene temporary(name); const auto data = temporary.Serialize(); - AssetHandle handle = CORE->GetAssets()->CreateAssetAt(folder, std::string(name) + Assets::SCENE_EXT, data); + AssetHandle handle = CORE->GetAssets()->CreateAssetAt(folder, std::string(name) + Assets::SceneExtension, data); assert(handle.HasValue()); DOA_LOG_INFO("Succesfully created a new scene asset named %s at %s", name.data(), folder.Path().c_str()); if (!HasOpenScene()) { @@ -320,24 +318,24 @@ ImGuiIO* GUI::IO() const { return io; } ImFont* GUI::GetFont() const { return font; } ImFont* GUI::GetFontBold() const { return fontBold; } -void* GUI::GetFolderIcon(TextureSize size) const { return SVGPathway::Get(FOLDER_ICON_KEY, TextureStyle::PADDED, size).TextureIDRaw(); } -void* GUI::GetProjectIcon(TextureSize size) const { return SVGPathway::Get(PROJECT_ICON_KEY, TextureStyle::PADDED, size).TextureIDRaw(); } -void* GUI::GetSceneIcon(TextureSize size) const { return SVGPathway::Get(SCENE_ICON_KEY, TextureStyle::PADDED, size).TextureIDRaw(); } -void* GUI::GetComponentIcon(TextureSize size) const { return SVGPathway::Get(COMPONENT_ICON_KEY, TextureStyle::PADDED, size).TextureIDRaw(); } -void* GUI::GetVertexShaderIcon(TextureSize size) const { return SVGPathway::Get(VERTEX_SHADER_ICON_KEY, TextureStyle::PADDED, size).TextureIDRaw(); } -void* GUI::GetTessellationControlShaderIcon(TextureSize size) const { return SVGPathway::Get(TESS_CTRL_SHADER_ICON_KEY, TextureStyle::PADDED, size).TextureIDRaw(); } -void* GUI::GetTessellationEvaluationShaderIcon(TextureSize size) const { return SVGPathway::Get(TESS_EVAL_SHADER_ICON_KEY, TextureStyle::PADDED, size).TextureIDRaw(); } -void* GUI::GetGeometryShaderIcon(TextureSize size) const { return SVGPathway::Get(GEOMETRY_SHADER_ICON_KEY, TextureStyle::PADDED, size).TextureIDRaw(); } -void* GUI::GetFragmentShaderIcon(TextureSize size) const { return SVGPathway::Get(FRAGMENT_SHADER_ICON_KEY, TextureStyle::PADDED, size).TextureIDRaw(); } -void* GUI::GetComputeShaderIcon(TextureSize size) const { return SVGPathway::Get(COMPUTE_SHADER_ICON_KEY, TextureStyle::PADDED, size).TextureIDRaw(); } -void* GUI::GetFileIcon(TextureSize size) const { return SVGPathway::Get(FILE_ICON_KEY, TextureStyle::PADDED, size).TextureIDRaw(); } -void* GUI::GetBackArrowIcon(TextureSize size) const { return SVGPathway::Get(BACK_ARROW_ICON_KEY, TextureStyle::PADDED, size).TextureIDRaw(); } +void* GUI::GetFolderIcon(TextureSize size) const { return SVGPathway::Get(FOLDER_ICON_KEY, TextureStyle::PADDED, size); } +void* GUI::GetProjectIcon(TextureSize size) const { return SVGPathway::Get(PROJECT_ICON_KEY, TextureStyle::PADDED, size); } +void* GUI::GetSceneIcon(TextureSize size) const { return SVGPathway::Get(SCENE_ICON_KEY, TextureStyle::PADDED, size); } +void* GUI::GetComponentIcon(TextureSize size) const { return SVGPathway::Get(COMPONENT_ICON_KEY, TextureStyle::PADDED, size); } +void* GUI::GetVertexShaderIcon(TextureSize size) const { return SVGPathway::Get(VERTEX_SHADER_ICON_KEY, TextureStyle::PADDED, size); } +void* GUI::GetTessellationControlShaderIcon(TextureSize size) const { return SVGPathway::Get(TESS_CTRL_SHADER_ICON_KEY, TextureStyle::PADDED, size); } +void* GUI::GetTessellationEvaluationShaderIcon(TextureSize size) const { return SVGPathway::Get(TESS_EVAL_SHADER_ICON_KEY, TextureStyle::PADDED, size); } +void* GUI::GetGeometryShaderIcon(TextureSize size) const { return SVGPathway::Get(GEOMETRY_SHADER_ICON_KEY, TextureStyle::PADDED, size); } +void* GUI::GetFragmentShaderIcon(TextureSize size) const { return SVGPathway::Get(FRAGMENT_SHADER_ICON_KEY, TextureStyle::PADDED, size); } +void* GUI::GetComputeShaderIcon(TextureSize size) const { return SVGPathway::Get(COMPUTE_SHADER_ICON_KEY, TextureStyle::PADDED, size); } +void* GUI::GetFileIcon(TextureSize size) const { return SVGPathway::Get(FILE_ICON_KEY, TextureStyle::PADDED, size); } +void* GUI::GetBackArrowIcon(TextureSize size) const { return SVGPathway::Get(BACK_ARROW_ICON_KEY, TextureStyle::PADDED, size); } void* GUI::FindIconForFileType(const FNode& file, TextureSize size) const { assert(HasOpenProject()); if (file.IsDirectory()) { return GetFolderIcon(size); } - if (file.Extension() == Assets::PROJ_EXT) { return GetProjectIcon(size); } /* TODO FIX THIS SHITTY EXTENSION CHECK */ + if (Assets::IsProjectFile(file)) { return GetProjectIcon(size); } assert(CORE->GetAssets()->IsAssetExistsAt(file)); AssetHandle asset = CORE->GetAssets()->FindAssetAt(file); @@ -358,9 +356,10 @@ void* GUI::FindIconForFileType(const FNode& file, TextureSize size) const { } return GetFileIcon(size); } -void* GUI::FindIconByName(const std::string_view key, TextureSize size) const { return SVGPathway::Get(std::string(key), TextureStyle::PADDED, size).TextureIDRaw(); } +void* GUI::FindIconByName(const std::string_view key, TextureSize size) const { return reinterpret_cast(static_cast(SVGPathway::Get(std::string(key), TextureStyle::PADDED, size).GLObjectID)); } MetaAssetInfo& GUI::GetMetaInfoOf(const FNode& file) { return meta.GetMetaAssetInfoBank().GetMetaInfoOf(file); } +MetaAssetInfoBank& GUI::GetMetaAssetInfoBank() noexcept { return meta.GetMetaAssetInfoBank(); } void GUI::ShowNewSceneAssetModal(FNode& currentFolder) const { nam.ShowSceneCreationModal(currentFolder); } void GUI::ShowNewComponentAssetModal(FNode& currentFolder) const { nam.ShowComponentCreationModal(currentFolder); } @@ -370,6 +369,8 @@ void GUI::ShowNewTessellationEvaluationShaderAssetModal(FNode& currentFolder) co void GUI::ShowNewGeometryShaderAssetModal(FNode& currentFolder) const { nam.ShowGeometryShaderCreationModal(currentFolder); } void GUI::ShowNewFragmentShaderAssetModal(FNode& currentFolder) const { nam.ShowFragmentShaderCreationModal(currentFolder); } void GUI::ShowNewShaderProgramAssetModal(FNode& currentFolder) const { nam.ShowShaderProgramCreationModal(currentFolder); } +void GUI::ShowNewMaterialAssetModal(FNode& currentFolder) const { nam.ShowMaterialCreationModal(currentFolder); } +void GUI::ShowNewSamplerAssetModal(FNode& currentFolder) const { nam.ShowSamplerCreationModal(currentFolder); } UndoRedoStack& GUI::GetCommandHistory() noexcept { return history; } void GUI::UndoLastCommand() noexcept { @@ -415,12 +416,13 @@ void GUI::OnSceneClosed() { scene = std::nullopt; } void GUI::OnReimport(Assets& assets) { - if (sceneUUID == UUID::Empty()) { return; } - - AssetHandle currentSceneHandle = assets.FindAsset(sceneUUID); - if (!currentSceneHandle.HasValue()) { - sceneUUID = UUID::Empty(); - scene = std::nullopt; + meta.GetMetaAssetInfoBank().Clear(); + if (sceneUUID != UUID::Empty()) { + AssetHandle currentSceneHandle = assets.FindAsset(sceneUUID); + if (!currentSceneHandle.HasValue()) { + sceneUUID = UUID::Empty(); + scene = std::nullopt; + } } } void GUI::OnAssetDeleted(AssetHandle asset) { diff --git a/Editor/GUI.hpp b/Editor/GUI.hpp index f2faaff2..8a03554c 100644 --- a/Editor/GUI.hpp +++ b/Editor/GUI.hpp @@ -97,7 +97,7 @@ struct GUI { ImGuiDockNodeFlags dockspace_flags{ ImGuiDockNodeFlags_None }; const CorePtr& CORE; - WindowPtr& window; + std::unique_ptr& window; std::string defaultWindowName{ "NeoDoa Editor" }; bool dockspaceOpen{ true }; @@ -168,6 +168,7 @@ struct GUI { void* FindIconByName(const std::string_view key, TextureSize size = TextureSize::MEDIUM) const; MetaAssetInfo& GetMetaInfoOf(const FNode& file); + MetaAssetInfoBank& GetMetaAssetInfoBank() noexcept; //- Modals -// void ShowNewSceneAssetModal(FNode& currentFolder) const; @@ -178,6 +179,8 @@ struct GUI { void ShowNewGeometryShaderAssetModal(FNode& currentFolder) const; void ShowNewFragmentShaderAssetModal(FNode& currentFolder) const; void ShowNewShaderProgramAssetModal(FNode& currentFolder) const; + void ShowNewMaterialAssetModal(FNode& currentFolder) const; + void ShowNewSamplerAssetModal(FNode& currentFolder) const; //- Undo/Redo History -// UndoRedoStack& GetCommandHistory() noexcept; @@ -230,7 +233,7 @@ struct GUI { // -- // // Open Scene UUID sceneUUID{ UUID::Empty() }; - std::optional scene{}; + std::optional scene{ std::nullopt }; void OnAssetOpened(AssetHandle asset); void OnSceneOpened(Scene& scene); void OnSceneClosed(); diff --git a/Editor/Gizmos.cpp b/Editor/Gizmos.cpp index 94060453..6cf7cda4 100644 --- a/Editor/Gizmos.cpp +++ b/Editor/Gizmos.cpp @@ -21,13 +21,12 @@ void Gizmos::Render(Scene& scene) { ImGuizmo::SetDrawlist(); if (sv.GetViewportCamera().IsPerspective()) { ImGuizmo::SetOrthographic(false); - sv.GetViewportCamera().GetPerspectiveCamera()._aspect = settings.viewportSize.Aspect(); } else if (sv.GetViewportCamera().IsOrtho()) { ImGuizmo::SetOrthographic(true); } const auto& camera = sv.GetViewportCamera().GetActiveCamera(); - glm::mat4 proj = camera._projectionMatrix; - glm::mat4 view = camera._viewMatrix; + glm::mat4 proj = camera.GetProjectionMatrix(); + glm::mat4 view = camera.GetViewMatrix(); ImGuizmo::SetRect( settings.viewportPosition.x, @@ -37,10 +36,9 @@ void Gizmos::Render(Scene& scene) { ); // Entity transform - TransformComponent& transformComponent = scene.GetComponent(selectedEntity); - glm::mat4 matrix = transformComponent.GetWorldMatrix(); + glm::mat4 matrix = TransformComponent::ComputeWorldMatrix(selectedEntity, scene); - bool snap = sv.gui.get().CORE->GetInput()->IsKeyPressed(KEY_LEFT_CONTROL); + bool snap = sv.gui.get().CORE->GetInput()->IsKeyPressed(Key::LeftControl); float snapValue = 0.5f; if (settings.type == ImGuizmo::OPERATION::ROTATE) { snapValue = 5.0f; } @@ -51,16 +49,30 @@ void Gizmos::Render(Scene& scene) { settings.mode, glm::value_ptr(matrix), nullptr, - snap ? &std::array{snapValue, snapValue, snapValue} [0] : nullptr); + snap ? &std::array{snapValue, snapValue, snapValue} [0] : nullptr + ); if (ImGuizmo::IsUsing()) { glm::vec3 translation; glm::quat rotation; glm::vec3 scale; - TransformComponent::Decompose(matrix, translation, rotation, scale); - transformComponent.SetWorldTranslation(translation); - transformComponent.SetWorldRotation(rotation); - transformComponent.SetLocalScale(scale); + TransformComponent::Decompose(matrix, &translation, &rotation, &scale); + + TransformComponent& transform = scene.GetComponent(selectedEntity); + + glm::vec3 currentWorldTranslation = TransformComponent::ComputeWorldTranslation(selectedEntity, scene); + glm::vec3 translationDifference = translation - currentWorldTranslation; + glm::vec3 currentLocalTranslation = transform.GetLocalTranslation(); + glm::vec3 newLocalTranslation = currentLocalTranslation + translationDifference; + transform.SetLocalTranslation(newLocalTranslation); + + glm::quat currentWorldRotation = TransformComponent::ComputeWorldRotation(selectedEntity, scene); + glm::quat rotationDifference = glm::inverse(currentWorldRotation) * rotation; + glm::quat currentLocalRotation = transform.GetLocalRotation(); + glm::quat newLocalRotation = currentLocalRotation * rotationDifference; + transform.SetLocalRotation(newLocalRotation); + + transform.SetLocalScale(scale); } } diff --git a/Editor/Gizmos.hpp b/Editor/Gizmos.hpp index 7f381a00..5502c165 100644 --- a/Editor/Gizmos.hpp +++ b/Editor/Gizmos.hpp @@ -27,11 +27,11 @@ struct Gizmos { void Render(Scene& scene); + Entity selectedEntity{ NULL_ENTT }; private: std::reference_wrapper sv; // -- // - Entity selectedEntity{ NULL_ENTT }; void OnEntitySelected(Entity entity); void OnEntityDeselected(); void OnEntityDeleted(Entity entity); diff --git a/Editor/Icons.hpp b/Editor/Icons.hpp index b20e7a68..62e1c875 100644 --- a/Editor/Icons.hpp +++ b/Editor/Icons.hpp @@ -23,11 +23,19 @@ namespace WindowIcons { inline constexpr const char SCENE_VIEWPORT_CAMERA_SETTINGS_WINDOW_ICON[]{ ICON_FA_CAMERA_MOVIE }; } +namespace ComponentWidgetIcons { + namespace ContextMenu { + inline constexpr const char ResetIcon[]{ ICON_FA_ARROW_ROTATE_LEFT " " }; + } +} + namespace SceneHierarchyIcons { inline constexpr const char SCENE_ICON[]{ ICON_FA_CUBES_STACKED " " }; inline constexpr const char ENTITY_ICON[]{ ICON_FA_CUBE " " }; namespace ContextMenu { + inline constexpr const char MAKE_STARTUP_SCENE_ICON[]{ ICON_FA_BADGE_CHECK " "}; + inline constexpr const char CREATE_NEW_ENTITY_ICON[]{ ICON_FA_PLUS " " }; inline constexpr const char CLOSE_SCENE_ICON[]{ ICON_FA_SQUARE_XMARK " " }; @@ -49,6 +57,7 @@ namespace SceneViewportIcons { } namespace ComponentIcons { + inline constexpr const char GENERIC_COMPONENT_ICON[] = ICON_FA_GEAR " "; inline const unordered_string_map DEFINED_COMPONENT_ICONS { { nameof(IDComponent), ICON_FA_ID_CARD " " }, @@ -111,6 +120,12 @@ namespace ObserverIcons { inline constexpr const char RESET_COMPONENT_DATA_ICON[]{ ICON_FA_ARROW_ROTATE_LEFT " " }; inline constexpr const char DETACH_COMPONENT_ICON[]{ ICON_FA_TRASH " " }; } + + namespace MaterialDisplayIcons { + namespace ContextMenu { + inline constexpr const char RESET_UNIFORM_ICON[]{ ICON_FA_ARROW_ROTATE_LEFT " " }; + } + } } namespace ComponentDefinitionViewIcons { @@ -213,6 +228,15 @@ namespace FileIcons { std::pair{ SHADER_PROGRAM_ICON, "triangle" } }; + inline constexpr const char MATERIAL_ICON[]{ ICON_FA_CIRCLE }; + inline std::array MaterialIcons{ + std::pair{ MATERIAL_ICON, "circle" } + }; + + inline constexpr const char SAMPLER_ICON[]{ ICON_FA_LOADER }; + inline std::array SamplerIcons{ + std::pair{ SAMPLER_ICON, "loader" } + }; inline constexpr const char FILE_ICON[] { ICON_FA_FILE }; inline constexpr const char FILE_ICON_MULTIPLE[] { ICON_FA_FILES }; diff --git a/Editor/ImGuiDebugPanel.cpp b/Editor/ImGuiDebugPanel.cpp index 6822a69d..aee2f8fb 100644 --- a/Editor/ImGuiDebugPanel.cpp +++ b/Editor/ImGuiDebugPanel.cpp @@ -1,6 +1,13 @@ +// This is external code. Disabling warnings. +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wsign-compare" +#endif + #if _DEBUG #include +#include void TextEditor::ImGuiDebugPanel(const std::string& panelName) { @@ -17,21 +24,21 @@ void TextEditor::ImGuiDebugPanel(const std::string& panelName) } if (ImGui::CollapsingHeader("Lines")) { for (int i = 0; i < mLines.size(); i++) { - ImGui::Text("%d", mLines[i].size()); + ImGuiFormattedText("{}", mLines[i].size()); } } if (ImGui::CollapsingHeader("Undo")) { static std::string numberOfRecordsText; numberOfRecordsText = "Number of records: " + std::to_string(mUndoBuffer.size()); - ImGui::Text("%s", numberOfRecordsText.c_str()); + ImGui::TextUnformatted(numberOfRecordsText.c_str()); ImGui::DragInt("Undo index", &mState.mCurrentCursor); for (int i = 0; i < mUndoBuffer.size(); i++) { if (ImGui::CollapsingHeader(std::to_string(i).c_str())) { ImGui::Text("Operations"); for (int j = 0; j < mUndoBuffer[i].mOperations.size(); j++) { - ImGui::Text("%s", mUndoBuffer[i].mOperations[j].mText.c_str()); - ImGui::Text(mUndoBuffer[i].mOperations[j].mType == UndoOperationType::Add ? "Add" : "Delete"); + ImGui::TextUnformatted(mUndoBuffer[i].mOperations[j].mText.c_str()); + ImGui::TextUnformatted(mUndoBuffer[i].mOperations[j].mType == UndoOperationType::Add ? "Add" : "Delete"); ImGui::DragInt2("Start", &mUndoBuffer[i].mOperations[j].mStart.mLine); ImGui::DragInt2("End", &mUndoBuffer[i].mOperations[j].mEnd.mLine); ImGui::Separator(); @@ -231,4 +238,8 @@ void TextEditor::UnitTests() { assert(!FindNextOccurrence("lalal", 4, { 3, 5 }, outStart, outEnd)); // not found } } +#endif + +#if defined(__clang__) +#pragma clang diagnostic pop #endif \ No newline at end of file diff --git a/Editor/ImGuiExtensions.cpp b/Editor/ImGuiExtensions.cpp index 51aa1da6..659bb9ba 100644 --- a/Editor/ImGuiExtensions.cpp +++ b/Editor/ImGuiExtensions.cpp @@ -11,18 +11,22 @@ void DrawRowsBackground(int row_count, ImVec4 col_even, ImVec4 col_odd) { float x1 = ImGui::GetWindowPos().x; float x2 = x1 + ImGui::GetWindowSize().x; - int row_display_start; - int row_display_end; - ImGui::CalcListClipping(row_count, line_height, &row_display_start, &row_display_end); - for (int row_n = row_display_start; row_n < row_display_end; row_n++) { - const ImVec4& col = (row_n & 1) ? col_odd : col_even; - if (col.w == 0) { - continue; + float oldCursorY = ImGui::GetCursorPosY(); + ImGuiListClipper clipper; + clipper.Begin(row_count); + while(clipper.Step()) { + for (int row_n = clipper.DisplayStart; row_n < clipper.DisplayEnd; row_n++) { + const ImVec4& col = (row_n & 1) ? col_odd : col_even; + if (col.w == 0) { + continue; + } + float y1 = y0 + (line_height * row_n); + float y2 = y1 + line_height; + draw_list->AddRectFilled(ImVec2(x1, y1), ImVec2(x2, y2), ImGui::ColorConvertFloat4ToU32(col)); + ImGui::SetCursorPosY(ImGui::GetCursorPosY() + line_height); } - float y1 = y0 + (line_height * row_n); - float y2 = y1 + line_height; - draw_list->AddRectFilled(ImVec2(x1, y1), ImVec2(x2, y2), ImGui::ColorConvertFloat4ToU32(col)); } + ImGui::SetCursorPosY(oldCursorY); } bool Splitter(bool split_vertically, float thickness, float* size1, float* size2, float min_size1, float min_size2, float splitter_long_axis_size) { diff --git a/Editor/ImGuiExtensions.hpp b/Editor/ImGuiExtensions.hpp index e3c9a839..75550e8d 100644 --- a/Editor/ImGuiExtensions.hpp +++ b/Editor/ImGuiExtensions.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -18,4 +19,9 @@ void ImRotateStart(); ImVec2 ImRotationCenter(); void ImRotateEnd(float rad = 0, ImVec2 center = ImRotationCenter()); -void FocusNextItem(); \ No newline at end of file +void FocusNextItem(); + +template +void ImGuiFormattedText(std::format_string format, Args&&... args) { + ImGui::TextUnformatted(std::format(format, std::forward(args)...).c_str()); +} \ No newline at end of file diff --git a/Editor/MaterialDisplay.cpp b/Editor/MaterialDisplay.cpp new file mode 100644 index 00000000..aa7a6306 --- /dev/null +++ b/Editor/MaterialDisplay.cpp @@ -0,0 +1,491 @@ +#include + +#include + +#include + +#include + +#include +#include +#include +#include +#include + +MaterialDisplay::MaterialDisplay(Observer& observer) noexcept : + observer(observer) {} + +void MaterialDisplay::SetDisplayTarget(Assets& assets, const AssetHandle materialAssetHandle) noexcept { + this->assets = &assets; + assert(materialAssetHandle->IsMaterial()); + if (materialAsset != materialAssetHandle) { + materialAsset = materialAssetHandle; + } +} + +void MaterialDisplay::RenderMessagesTable() noexcept { + assert(materialAsset.HasValue()); + if (!materialAsset->HasErrorMessages() && + !materialAsset->HasWarningMessages() && + !materialAsset->HasInfoMessages()) { + return; + } + + ImGuiTableFlags flags = ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders; + + ImGui::BeginTable("compiler_logs", 2, flags); + + ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, 30); + ImGui::TableSetupColumn("Material Logs", ImGuiTableColumnFlags_WidthStretch); + ImGui::TableHeadersRow(); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 0, 0 }); + + ImGui::PushStyleColor(ImGuiCol_Text, ComponentDefinitionViewColors::ERROR_COLOR); + for (auto& message : materialAsset->ErrorMessages()) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + + float r = BeginTableColumnCenterText(ComponentDefinitionViewIcons::ERROR_ICON); + ImGui::Text(ComponentDefinitionViewIcons::ERROR_ICON); + EndTableColumnCenterText(r); + + ImGui::TableSetColumnIndex(1); + + const std::string& m{ std::any_cast(message) }; + ImGui::TextWrapped("%s", m.c_str()); + } + ImGui::PopStyleColor(); + + ImGui::PushStyleColor(ImGuiCol_Text, ComponentDefinitionViewColors::WARNING_COLOR); + for (auto& message : materialAsset->WarningMessages()) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + + float r = BeginTableColumnCenterText(ComponentDefinitionViewIcons::WARNING_ICON); + ImGui::Text(ComponentDefinitionViewIcons::WARNING_ICON); + EndTableColumnCenterText(r); + + ImGui::TableSetColumnIndex(1); + + const std::string& m{ std::any_cast(message) }; + ImGui::TextWrapped("%s", m.c_str()); + } + ImGui::PopStyleColor(); + + ImGui::PushStyleColor(ImGuiCol_Text, ComponentDefinitionViewColors::INFO_COLOR); + for (auto& message : materialAsset->InfoMessages()) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + + float r = BeginTableColumnCenterText(ComponentDefinitionViewIcons::INFO_ICON); + ImGui::Text(ComponentDefinitionViewIcons::INFO_ICON); + EndTableColumnCenterText(r); + + ImGui::TableSetColumnIndex(1); + + const std::string& m{ std::any_cast(message) }; + ImGui::TextWrapped("%s", m.c_str()); + } + ImGui::PopStyleColor(); + + ImGui::PopStyleVar(); + ImGui::EndTable(); +} +void MaterialDisplay::RenderProgramCombo() noexcept { + const auto& programIDs = assets->ShaderProgramAssetIDs(); + Material& material = materialAsset->DataAs(); + + std::string comboPreviewString; + AssetHandle programHandle = assets->FindAsset(material.ShaderProgram); + if (programHandle.HasValue()) { + const ShaderProgram& programInUse = programHandle->DataAs(); + comboPreviewString = programInUse.Name; + } else { + comboPreviewString = ""; + } + + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Shader Program: "); + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::BeginCombo("###program_combo", comboPreviewString.c_str(), ImGuiComboFlags_HeightLarge)) { + for (const auto& programID : programIDs) { + AssetHandle programAsset = assets->FindAsset(programID); + assert(programAsset.HasValue()); + + const bool isSelected = (material.ShaderProgram == programAsset->ID()); + const ShaderProgram& program = programAsset->DataAs(); + if (ImGui::Selectable(program.Name.c_str(), isSelected)) { + assets->TryDeleteDependencyBetween(materialAsset->ID(), material.ShaderProgram); + material.ShaderProgram = programID; + assets->TryRegisterDependencyBetween(materialAsset->ID(), programID); + material.ClearAllUniforms(); + materialAsset->Serialize(); + materialAsset->ForceDeserialize(); + } + // Set the initial focus when opening the combo (scrolling + keyboard navigation focus) + if (isSelected) { + ImGui::SetItemDefaultFocus(); + } + } + ImGui::EndCombo(); + } +} +void MaterialDisplay::RenderShaderUniforms() noexcept { + Material& material = materialAsset->DataAs(); + if (material.ShaderProgram == UUID::Empty()) { return; } + + AssetHandle programHandle = assets->FindAsset(material.ShaderProgram); + if (!programHandle.HasValue()) { + ImGuiFormattedText("Selected program (ID: {}) is non-existant!", static_cast(material.ShaderProgram)); + return; + } + + if (!programHandle->IsShaderProgram()) { + ImGuiFormattedText("Selected program (ID: {}) is non a shader program!", static_cast(material.ShaderProgram)); + return; + } + + const ShaderProgram& program = programHandle->DataAs(); + if (!program.IsComplete()) { + ImGuiFormattedText("Selected program (ID: {}) is not complete!", static_cast(material.ShaderProgram)); + return; + } + + const GPUShaderProgram* gpuProgramPtr = assets->GPUBridge().GetShaderPrograms().Query(material.ShaderProgram); + if (!gpuProgramPtr) { + ImGuiFormattedText("Selected program (ID: {}) is not allocated on the GPU yet!", static_cast(material.ShaderProgram)); + return; + } + const GPUShaderProgram& gpuProgram = *gpuProgramPtr; + + if (CountUniformsInGroup(gpuProgram, ShaderType::Vertex) > 0) { + ImGui::SeparatorText(std::format("Vertex Shader - {} ({})", assets->FindAsset(program.VertexShader)->DataAs().Name, program.VertexShader.AsString()).c_str()); + RenderUniformGroup(material.VertexUniforms, gpuProgram, ShaderType::Vertex); + } + + if (program.TessellationControlShader != UUID::Empty() && CountUniformsInGroup(gpuProgram, ShaderType::TessellationControl) > 0) { + ImGui::SeparatorText(std::format("Tessellation Control Shader - {} ({})", assets->FindAsset(program.TessellationControlShader)->DataAs().Name, program.TessellationControlShader.AsString()).c_str()); + RenderUniformGroup(material.VertexUniforms, gpuProgram, ShaderType::TessellationControl); + } + + if (program.TessellationEvaluationShader != UUID::Empty() && CountUniformsInGroup(gpuProgram, ShaderType::TessellationEvaluation) > 0) { + ImGui::SeparatorText(std::format("Tessellation Evaluation Shader - {} ({})", assets->FindAsset(program.TessellationEvaluationShader)->DataAs().Name, program.TessellationEvaluationShader.AsString()).c_str()); + RenderUniformGroup(material.VertexUniforms, gpuProgram, ShaderType::TessellationEvaluation); + } + + if (program.GeometryShader != UUID::Empty() && CountUniformsInGroup(gpuProgram, ShaderType::Geometry) > 0) { + ImGui::SeparatorText(std::format("Geometry Shader - {} ({})", assets->FindAsset(program.GeometryShader)->DataAs().Name, program.GeometryShader.AsString()).c_str()); + RenderUniformGroup(material.VertexUniforms, gpuProgram, ShaderType::Geometry); + } + + if (CountUniformsInGroup(gpuProgram, ShaderType::Fragment) > 0) { + ImGui::SeparatorText(std::format("Fragment Shader - {} ({})", assets->FindAsset(program.FragmentShader)->DataAs().Name, program.FragmentShader.AsString()).c_str()); + RenderUniformGroup(material.VertexUniforms, gpuProgram, ShaderType::Fragment); + } + + textureView.Render(); +} + +int MaterialDisplay::CountUniformsInGroup(const GPUShaderProgram& program, ShaderType group) noexcept { + return static_cast(std::ranges::count_if(program.Uniforms, [group](const auto& uniform) { return uniform.ReferencedBy == group; })); +} +void MaterialDisplay::RenderUniformGroup(Material::Uniforms& uniforms, const GPUShaderProgram& program, ShaderType group) noexcept { + for (const auto& uniform : program.Uniforms) { + if (uniform.ReferencedBy != group) { continue; } + + if (!uniforms.TryGet(uniform.Location)) { + MaterialPostDeserialization::EmplaceUniform(uniforms, uniform.Location, uniform.TypeName, uniform.Name); + } + auto& uniformValue = uniforms.Get(uniform.Location); + + if (RenderSingleUniform(uniforms, uniformValue, uniform)) { + materialAsset->Serialize(); + materialAsset->ForceDeserialize(); + } + } +} +bool MaterialDisplay::RenderSingleUniform(Material::Uniforms& uniforms, const UniformValue& uniformValue, const GPUShaderProgram::Uniform& uniform) noexcept { + bool rv{ false }; + //ImGuiFormattedText("Name: {}, Type: {}", uniform.Name.c_str(), uniform.TypeName.c_str()); + + if (uniform.TypeName == "float") { + Uniform1f value = std::get(uniformValue.Value); + if (FancyVector1Widget(uniformValue.Name.c_str(), value)) { + uniforms.Set(uniformValue.Location, uniformValue.Name, value); + rv = true; + } + } else if (uniform.TypeName == "vec2") { + Uniform2f value = std::get(uniformValue.Value); + if (FancyVector2Widget(uniformValue.Name.c_str(), value)) { + uniforms.Set(uniformValue.Location, uniformValue.Name, value); + rv = true; + } + } else if (uniform.TypeName == "vec3") { + Uniform3f value = std::get(uniformValue.Value); + if (FancyVector3Widget(uniformValue.Name.c_str(), value)) { + uniforms.Set(uniformValue.Location, uniformValue.Name, value); + rv = true; + } + } else if (uniform.TypeName == "vec4") { + Uniform4f value = std::get(uniformValue.Value); + if (FancyVector4Widget(uniformValue.Name.c_str(), value)) { + uniforms.Set(uniformValue.Location, uniformValue.Name, value); + rv = true; + } + } + + //else if (uniform.TypeName == "double") { + //} else if (uniform.TypeName == "dvec2") { + //} else if (uniform.TypeName == "dvec3") { + //} else if (uniform.TypeName == "dvec4") { + //} + + else if (uniform.TypeName == "int") { + Uniform1i value = std::get(uniformValue.Value); + if (FancyVectori1Widget(uniformValue.Name.c_str(), value)) { + uniforms.Set(uniformValue.Location, uniformValue.Name, value); + rv = true; + } + } else if (uniform.TypeName == "ivec2") { + Uniform2i value = std::get(uniformValue.Value); + if (FancyVectori2Widget(uniformValue.Name.c_str(), value)) { + uniforms.Set(uniformValue.Location, uniformValue.Name, value); + rv = true; + } + } else if (uniform.TypeName == "ivec3") { + Uniform3i value = std::get(uniformValue.Value); + if (FancyVectori3Widget(uniformValue.Name.c_str(), value)) { + uniforms.Set(uniformValue.Location, uniformValue.Name, value); + rv = true; + } + } else if (uniform.TypeName == "ivec4") { + Uniform4i value = std::get(uniformValue.Value); + if (FancyVectori4Widget(uniformValue.Name.c_str(), value)) { + uniforms.Set(uniformValue.Location, uniformValue.Name, value); + rv = true; + } + } + + else if (uniform.TypeName == "unsigned int") { + Uniform1ui value = std::get(uniformValue.Value); + if (FancyVectorui1Widget(uniformValue.Name.c_str(), value)) { + uniforms.Set(uniformValue.Location, uniformValue.Name, value); + rv = true; + } + } else if (uniform.TypeName == "uvec2") { + Uniform2ui value = std::get(uniformValue.Value); + if (FancyVectorui2Widget(uniformValue.Name.c_str(), value)) { + uniforms.Set(uniformValue.Location, uniformValue.Name, value); + rv = true; + } + } else if (uniform.TypeName == "uvec3") { + Uniform3ui value = std::get(uniformValue.Value); + if (FancyVectorui3Widget(uniformValue.Name.c_str(), value)) { + uniforms.Set(uniformValue.Location, uniformValue.Name, value); + rv = true; + } + } else if (uniform.TypeName == "uvec4") { + Uniform4ui value = std::get(uniformValue.Value); + if (FancyVectorui4Widget(uniformValue.Name.c_str(), value)) { + uniforms.Set(uniformValue.Location, uniformValue.Name, value); + rv = true; + } + } + + else if (uniform.TypeName == "bool") { + Uniform1i value = std::get(uniformValue.Value); + if (FancyVectorb1Widget(uniformValue.Name.c_str(), value)) { + uniforms.Set(uniformValue.Location, uniformValue.Name, value); + rv = true; + } + } else if (uniform.TypeName == "bvec2") { + Uniform2i value = std::get(uniformValue.Value); + if (FancyVectorb2Widget(uniformValue.Name.c_str(), value)) { + uniforms.Set(uniformValue.Location, uniformValue.Name, value); + rv = true; + } + } else if (uniform.TypeName == "bvec3") { + Uniform3i value = std::get(uniformValue.Value); + if (FancyVectorb3Widget(uniformValue.Name.c_str(), value)) { + uniforms.Set(uniformValue.Location, uniformValue.Name, value); + rv = true; + } + } else if (uniform.TypeName == "bvec4") { + Uniform4i value = std::get(uniformValue.Value); + if (FancyVectorb4Widget(uniformValue.Name.c_str(), value)) { + uniforms.Set(uniformValue.Location, uniformValue.Name, value); + rv = true; + } + } + + else if (uniform.TypeName == "sampler1D") { + ImGui::TextUnformatted("Implementation pending..."); + } else if (uniform.TypeName == "sampler2D") { + UniformSampler2D value = std::get(uniformValue.Value); + + { // Texture + const Texture* texture{ nullptr }; + const GPUTexture* gpuTexture{ nullptr }; + AssetHandle handle = assets->FindAsset(value.textureUUID); + if (handle && handle->IsTexture()) { + texture = &handle->DataAs(); + gpuTexture = assets->GPUBridge().GetTextures().Query(handle->ID()); + assert(gpuTexture); + } else { + texture = missingTexture; + gpuTexture = &assets->GPUBridge().GetTextures().Missing(); + } + + if (Image2DButtonWidget(uniformValue.Name.c_str(), *gpuTexture)) { + textureView.Show(*texture, *gpuTexture); + } + + if (ImGui::BeginPopupContextItem(nullptr, ImGuiPopupFlags_MouseButtonRight)) { + if (ImGui::Button(cat(ObserverIcons::MaterialDisplayIcons::ContextMenu::RESET_UNIFORM_ICON, "Reset"))) { + uniforms.Set(uniform.Location, uniform.Name, UniformSampler2D{ UUID::Empty(), value.samplerUUID }); // TODO make view update on texture change!! + rv = true; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + if(ImGui::BeginDragDropTarget()) { + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("DND_DEMO_CELL")) { + UUID data = *(const UUID*) payload->Data; + AssetHandle handle = assets->FindAsset(data); + assert(handle.HasValue()); + if (handle->IsTexture()) { + uniforms.Set(uniform.Location, uniform.Name, UniformSampler2D{ data, value.samplerUUID }); + rv = true; + } + } + ImGui::EndDragDropTarget(); + } + } + { // Sampler + std::string data; + AssetHandle handle = assets->FindAsset(value.samplerUUID); + if (handle && handle->IsSampler()) { + data = std::format("{} (UUID:{})", handle->DataAs().Name, value.samplerUUID.AsString()); + } else { + data = "Optional. Drag-Drop a sampler asset to set."; + } + ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_TextDisabled)); + UneditableStringWidget(std::format("{} (Texture Sampler)", uniformValue.Name), data); + ImGui::PopStyleColor(); + + if (ImGui::BeginPopupContextItem("sampler_ctx_menu", ImGuiPopupFlags_MouseButtonRight)) { + if (ImGui::Button(cat(ObserverIcons::MaterialDisplayIcons::ContextMenu::RESET_UNIFORM_ICON, "Reset"))) { + uniforms.Set(uniform.Location, uniform.Name, UniformSampler2D{ value.textureUUID, UUID::Empty() }); + rv = true; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + if (ImGui::BeginDragDropTarget()) { + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("DND_DEMO_CELL")) { + UUID data = *(const UUID*) payload->Data; + AssetHandle handle = assets->FindAsset(data); + assert(handle.HasValue()); + if (handle->IsSampler()) { + uniforms.Set(uniform.Location, uniform.Name, UniformSampler2D{ value.textureUUID, data }); + rv = true; + } + } + ImGui::EndDragDropTarget(); + } + } + } else if (uniform.TypeName == "sampler3D") { + ImGui::TextUnformatted("Implementation pending..."); + } + /* + else if (uniform.TypeName == "mat2") { + //uniforms.Setv(uniform.Location, std::span{}); + } else if (uniform.TypeName == "mat3") { + //uniforms.Setv(uniform.Location, std::span{}); + } else if (uniform.TypeName == "mat4") { + //uniforms.Setv(uniform.Location, std::span{}); + } else if (uniform.TypeName == "mat2x3") { + //uniforms.Setv(uniform.Location, std::span{}); + } else if (uniform.TypeName == "mat2x4") { + ///uniforms.Setv(uniform.Location, std::span{}); + } else if (uniform.TypeName == "mat3x2") { + //uniforms.Setv(uniform.Location, std::span{}); + } else if (uniform.TypeName == "mat3x4") { + //uniforms.Setv(uniform.Location, std::span{}); + } else if (uniform.TypeName == "mat4x2") { + //uniforms.Setv(uniform.Location, std::span{}); + } else if (uniform.TypeName == "mat4x3") { + //uniforms.Setv(uniform.Location, std::span{}); + }*/ else { + ImGui::TextDisabled("Uniform typename %s not currently supported!", uniform.TypeName.c_str()); + } + return rv; +} + +void MaterialDisplay::TextureView::Render() noexcept { + if (!visible) { return; } + ImGui::Begin(std::format("Texture View - {}", texture->Name).c_str(), &visible); + /* channels */ + static bool r{ true }, g{ true }, b{ true }, a{ true }; + /* inspect params */ + static bool drawInspector{ true }, drawHistogram{ false }, drawNormals{ false }; + + float lineHeight = ImGui::GetTextLineHeight() + ImGui::GetStyle().FramePadding.y * 2; + float totalBottomPadding = lineHeight * 2.75f; + + auto [windowWidth, windowHeight] = ImGui::GetContentRegionAvail(); + windowWidth = windowWidth - ImGui::GetStyle().FramePadding.x; + windowHeight = windowHeight - totalBottomPadding; + + float w = static_cast(texture->Width); + float h = static_cast(texture->Height); + float aspect = w / h; + + float maxWidth = windowWidth; + float maxHeight = windowHeight; + + w = maxWidth; + h = w / aspect; + + if (h > maxHeight) { + aspect = w / h; + h = maxHeight; + w = h * aspect; + } + + ImGui::Image(*gpuTexture, { w, h }, { 0, 1 }, { 1, 0 }, { (float) r, (float) g, (float) b, (float) a }, { 1, 1, 0, 1 }); + + if (drawInspector) { + ImRect rc = ImRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax()); + ImVec2 mouseUVCoord = (ImGui::GetIO().MousePos - rc.Min) / rc.GetSize(); + mouseUVCoord.y = 1.f - mouseUVCoord.y; + if (mouseUVCoord.x >= 0.0f && + mouseUVCoord.y >= 0.0f && + mouseUVCoord.x <= 1.0f && + mouseUVCoord.y <= 1.0f) { + float w = static_cast(texture->Width); + float h = static_cast(texture->Height); + auto pixels = reinterpret_cast(texture->PixelData.data()); + ImageInspect::inspect(static_cast(w), static_cast(h), pixels, mouseUVCoord, { w, h }, drawNormals, drawHistogram); + } + } + + ImGui::SetCursorPosY(ImGui::GetWindowHeight() - totalBottomPadding); + ImGui::AlignTextToFramePadding(); ImGui::Text("Channels"); + ImGui::SameLine(); ImGui::TextColored({ 1.0f, 0.0f, 0.0f, 1.0f }, "R:"); ImGui::SameLine(); ImGui::Checkbox("##r", &r); + ImGui::SameLine(); ImGui::TextColored({ 0.0f, 1.0f, 0.0f, 1.0f }, "G:"); ImGui::SameLine(); ImGui::Checkbox("##g", &g); + ImGui::SameLine(); ImGui::TextColored({ 0.2f, 0.2f, 1.0f, 1.0f }, "B:"); ImGui::SameLine(); ImGui::Checkbox("##b", &b); + ImGui::SameLine(); ImGui::TextColored({ 0.5f, 0.5f, 0.5f, 1.0f }, "A:"); ImGui::SameLine(); ImGui::Checkbox("##a", &a); + + ImGui::AlignTextToFramePadding(); ImGui::Text("Image Inspect:"); ImGui::SameLine(); ImGui::Checkbox("##inspect", &drawInspector); + ImGui::SameLine(); ImGui::Text("Normals:"); ImGui::SameLine(); ImGui::Checkbox("##normals", &drawNormals); + ImGui::SameLine(); ImGui::Text("Histogram:"); ImGui::SameLine(); ImGui::Checkbox("##histogram", &drawHistogram); + ImGui::End(); +} +void MaterialDisplay::TextureView::Show(const Texture& texture, const GPUTexture& gpuTexture) noexcept { + visible = true; + this->texture = &texture; + this->gpuTexture = &gpuTexture; +} +void MaterialDisplay::TextureView::Hide() noexcept { visible = false; } \ No newline at end of file diff --git a/Editor/MaterialDisplay.hpp b/Editor/MaterialDisplay.hpp new file mode 100644 index 00000000..d7bef18a --- /dev/null +++ b/Editor/MaterialDisplay.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include + +struct Observer; + +struct MaterialDisplay { + + MaterialDisplay(Observer& observer) noexcept; + + void SetDisplayTarget(Assets& assets, const AssetHandle materialAssetHandle) noexcept; + + void RenderMessagesTable() noexcept; + void RenderProgramCombo() noexcept; + void RenderShaderUniforms() noexcept; + +private: + std::reference_wrapper observer; + Assets* assets{ nullptr }; + AssetHandle materialAsset{}; + const Texture* missingTexture{ &Texture::Missing() }; + + int CountUniformsInGroup(const GPUShaderProgram& program, ShaderType group) noexcept; + void RenderUniformGroup(Material::Uniforms& uniforms, const GPUShaderProgram& program, ShaderType group) noexcept; + bool RenderSingleUniform(Material::Uniforms& uniforms, const UniformValue& value, const GPUShaderProgram::Uniform& uniform) noexcept; + + struct TextureView { + void Render() noexcept; + void Show(const Texture& texture, const GPUTexture& gpuTexture) noexcept; + void Hide() noexcept; + private: + bool visible{ false }; + const Texture* texture{ nullptr }; + const GPUTexture* gpuTexture{ nullptr }; + } textureView; +}; \ No newline at end of file diff --git a/Editor/MenuBar.cpp b/Editor/MenuBar.cpp index 6c32cd9b..012a85db 100644 --- a/Editor/MenuBar.cpp +++ b/Editor/MenuBar.cpp @@ -1,10 +1,14 @@ -#include +#include + +#include #include #include #include #include +#include +#include #include #include @@ -19,7 +23,7 @@ MenuBar::MenuBar(GUI& owner) noexcept : bool MenuBar::Begin() noexcept { return true; } void MenuBar::Render() noexcept { - GUI& gui = this->gui; + [[maybe_unused]] GUI& gui = this->gui; if (ImGui::BeginMenuBar()) { if (ImGui::BeginMenu("File")) { @@ -54,9 +58,10 @@ void MenuBar::RenderFileSubMenu() noexcept { assert(currentFolder != nullptr); gui.ShowNewSceneAssetModal(*currentFolder); } - if (ImGui::BeginMenu("Open Scene...", gui.HasOpenProject())) { - const auto& assets = gui.CORE->GetAssets(); - for (const auto& uuid : assets->SceneAssetIDs()) { + const auto& assets = gui.CORE->GetAssets(); + const auto& scenes = assets->SceneAssetIDs(); + if (ImGui::BeginMenu("Open Scene...", gui.HasOpenProject() && !scenes.empty())) { + for (const auto& uuid : scenes) { AssetHandle sceneAsset = assets->FindAsset(uuid); if (ImGui::MenuItem(sceneAsset.Value().File().Name().data(), nullptr, nullptr)) { gui.OpenScene(sceneAsset); @@ -115,7 +120,7 @@ void MenuBar::RenderEditSubMenu() noexcept { } void MenuBar::RenderAssetsSubMenu() noexcept { - GUI& gui = this->gui; + [[maybe_unused]] GUI& gui = this->gui; } void MenuBar::RenderHelpSubMenu() noexcept { @@ -127,32 +132,50 @@ void MenuBar::RenderHelpSubMenu() noexcept { // Inner struct: About Section MenuBar::AboutSection::AboutSection(MenuBar& owner) noexcept : mb(owner), - neodoaBanner(Texture::CreateTexture("!!neodoa_banner!!", "Images/social.png")), licences({ - { "NeoDoa", R"(NeoDoa Public Licence + { "NeoDoa", reinterpret_cast(u8R"(# SOFTWARE LICENSE AGREEMENT +This Software License Agreement ("Agreement") is made and entered into as of 2024-02-10 ("Effective Date") by and between DoÄŸa Oruç, a private person, having its principal place of business in Türkiye ("Licensor"), and Licensee, either a private person, a registered company, or a partnership, having its principal place of business anywhere ("Licensee"). + +WHEREAS, the Licensor owns certain software that it desires to license to the Licensee; + +WHEREAS, Licensee desires to use such software under the terms and conditions set forth herein. + +NOW, THEREFORE, in consideration of the mutual promises contained herein and for other good and valuable consideration, the parties agree as follows: + +## 1. Definition of Software: +The term "Software" refers to the **NeoDoa**, including any updates, modifications, or associated documentation provided by the Licensor. + +## 2. Grant of License: +Subject to the terms and conditions of this Agreement, the Licensor grants the Licensee a non-exclusive, non-transferable license to use the Software. To apply, reach out to the Licensor. + +## 3. Derivative Works: +All modifications and derivative works must be submitted as Pull Requests to the [official **NeoDoa** GitHub repository](https://github.com/aeris170/NeoDoa). Without supervision from the Licensor, no modifications or derivative works are allowed. + +## 4. Commercial Use: +Commercial use is permitted on an application basis. To apply for commercial use, reach out to the Licensor. + +## 5. Attribution: +Licensee is required to provide attribution to Licensor in the form of a fade in/out **NeoDoa** social logo staying on the screen no shorter than 5 seconds (skippable with keypresses) first thing at the start of their program. - > Copyright(C)[2022][Doga Oruc] +## 6. Intellectual Property Rights: +All intellectual property rights in the Software shall remain the property of the Licensor. The Licensee does not acquire any rights to the Software except for the limited use rights specified in this Agreement. - > NeoDoa Public Licence - > TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +## 7. Warranty and Liability: +The Software is provided "as is" without warranty of any kind. Licensor shall not be liable for any damages arising out of or related to the use or inability to use the Software. - 1. Do whatever you like with the original work, just don't be a dick. +## 8. Termination: +This Agreement shall terminate automatically if the Licensee breaches any of its terms and conditions. Upon termination, Licensee must cease all use of the Software and destroy all copies. - Being a dick includes - but is not limited to - the following instances: +## 9. Governing Law: +This Agreement shall be governed by and construed under the laws of Türkiye. - 1a. Outright copyright infringement - Don't just copy this and change the name. - 1b. Selling the unmodified original with no work done what - so - ever, that's REALLY being a dick. - 1c. Modifying the original work to contain hidden harmful content. That would make you a PROPER dick. +IN WITNESS WHEREOF, the parties have executed this Software License Agreement as of the Effective Date. - 2. If you become rich through modifications, related works / services, or supporting the original work, - share the love. Only a dick would make loads off this work and not buy the original work's - creator(s) a pint. +Licensor: DoÄŸa Oruç - 3. Software is provided with no warranty. Asking for help won't make you a dick, but asking someone to - write your code for you makes you a DONKEY dick. If you happen to solve your problem before any help arrives, - you would submit the fix back to regain your status of non-dick.)"}, +Licensee: Licensee)")}, { "AngelScript", R"(AngelCode Scripting Library - Copyright © 2003 - 2020 Andreas Jönsson + Copyright © 2003 - 2020 Andreas Jönsson This software is provided 'as-is', without any express or implied warranty.In no event will the authors be held liable for any damages arising from the use of this software. @@ -340,7 +363,7 @@ MenuBar::AboutSection::AboutSection(MenuBar& owner) noexcept : MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.)" }, { "GLFW", R"(Copyright (c) 2002-2006 Marcus Geelnard - Copyright (c) 2006-2019 Camilla Löwy + Copyright (c) 2006-2019 Camilla Löwy This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -489,19 +512,6 @@ MenuBar::AboutSection::AboutSection(MenuBar& owner) noexcept : AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.)" }, - { "TinyFileDialogs", R"(This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution.)" }, { "TinyXML2", R"(This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. @@ -546,8 +556,30 @@ MenuBar::AboutSection::AboutSection(MenuBar& owner) noexcept : services; loss of use, data, or profits; or business interruption) In the event that YOU, the library user, accept these terms, you are free - to use this software free of charge, with or without modifications. Have fun : )" } - }) {} + to use this software free of charge, with or without modifications. Have fun : )" }}) { + + stbi_set_flip_vertically_on_load(true); + int w, h, nrChannels; + auto* readPixels = stbi_load("Images/social.png", &w, &h, &nrChannels, STBI_rgb_alpha); + + GPUTextureBuilder builder; + builder.SetName("!!neodoa_banner!!"); + if (readPixels) { + std::span pixels{ reinterpret_cast(readPixels), w * h * nrChannels * sizeof(stbi_uc) }; + builder.SetWidth(w) + .SetHeight(h) + .SetData(DataFormat::RGBA8, pixels); + } else { + const Texture& texture = Texture::Missing(); + builder.SetWidth(texture.Width) + .SetHeight(texture.Height) + .SetData(texture.Format, texture.PixelData); + } + auto [tex, _] = builder.Build(); + neodoaBanner = std::move(tex.value()); + + stbi_image_free(readPixels); +} void MenuBar::AboutSection::RenderAboutPopup() noexcept { GUI& gui = mb.get().gui; @@ -565,7 +597,7 @@ void MenuBar::AboutSection::RenderAboutPopup() noexcept { ImGui::PushFont(gui.GetFont()); ImGui::TextColored({ 0.7f, 0.7f, 0.7f, 1.0f }, PRODUCT_NAME); ImGui::PopFont(); - ImGui::Image(neodoaBanner.TextureIDRaw(), { ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().x / 2 }, { 0, 1 }, { 1, 0 }); + ImGui::Image(neodoaBanner, { ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().x / 2 }, { 0, 1 }, { 1, 0 }); ImGui::Text(PRODUCT_DESCRIPTION); ImGui::Dummy({ 0, 20 }); if (ImGui::Button(LIBS_BUTTON_TEXT, { ImGui::GetContentRegionAvail().x, 30 })) { @@ -594,7 +626,7 @@ void MenuBar::AboutSection::RenderLicenceNotices() noexcept { ImGui::PopFont(); auto title = (std::string("License###") + name); if (ImGui::CollapsingHeader(title.c_str(), ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick)) { - ImGui::TextUnformatted(licence.c_str()); + ImGui::TextWrapped("%s", licence.c_str()); } } } diff --git a/Editor/MenuBar.hpp b/Editor/MenuBar.hpp index 548b5794..c2390442 100644 --- a/Editor/MenuBar.hpp +++ b/Editor/MenuBar.hpp @@ -8,7 +8,7 @@ #include -#include +#include struct GUI; @@ -34,7 +34,7 @@ struct MenuBar { static constexpr auto PRODUCT_DESCRIPTION{ "A game engine. Build: Windows" }; std::reference_wrapper mb; - Texture neodoaBanner; + GPUTexture neodoaBanner; std::vector> licences; bool ab{ false }, ab_open{ true }, lib_open{ true }; diff --git a/Editor/MetaAssetInfo.cpp b/Editor/MetaAssetInfo.cpp index c2d5b85c..87de3782 100644 --- a/Editor/MetaAssetInfo.cpp +++ b/Editor/MetaAssetInfo.cpp @@ -10,7 +10,7 @@ #include #include -void* MetaAssetInfo::GetSVGIcon(TextureStyle style) const { return SVGPathway::Get(svg_icon_key, style).TextureIDRaw(); } +void* MetaAssetInfo::GetSVGIcon(TextureSize size, TextureStyle style) const { return SVGPathway::Get(svg_icon_key, style, size); } void MetaAssetInfoBank::SaveToDisk(const MetaAssetInfoBank& bank, const FNode& editorMetaFolder) noexcept { tinyxml2::XMLDocument doc; @@ -56,61 +56,76 @@ MetaAssetInfoBank::DataStructure::const_iterator MetaAssetInfoBank::begin() cons MetaAssetInfoBank::DataStructure::const_iterator MetaAssetInfoBank::end() const noexcept { return metaInfo.end(); } void MetaAssetInfoBank::TryEmplace(const FNode& file, const MetaAssetInfo& emplaceThisIfAbsent) { + int icon_index{ 0 }; const char* fa_icon{ nullptr }; std::string svg_icon_key{}; if (file.IsDirectory()) { - const auto& [_fa_icon, _svg_icon_key] = FileIcons::DirectoryIcons[0]; + const auto& [_fa_icon, _svg_icon_key] = FileIcons::DirectoryIcons[icon_index]; fa_icon = _fa_icon; svg_icon_key = _svg_icon_key; } else { AssetHandle handle = Core::GetCore()->GetAssets()->FindAssetAt(file); assert(handle.HasValue()); if (handle->IsScene()) { - const auto& [_fa_icon, _svg_icon_key] = FileIcons::SceneIcons[0]; + const auto& [_fa_icon, _svg_icon_key] = FileIcons::SceneIcons[icon_index]; fa_icon = _fa_icon; svg_icon_key = _svg_icon_key; } else if (handle->IsComponentDefinition()) { - const auto& [_fa_icon, _svg_icon_key] = FileIcons::ComponentIcons[0]; + const auto& [_fa_icon, _svg_icon_key] = FileIcons::ComponentIcons[icon_index]; fa_icon = _fa_icon; svg_icon_key = _svg_icon_key; } else if (handle->IsShader()) { if (Assets::IsVertexShaderFile(handle->File())) { - const auto& [_fa_icon, _svg_icon_key] = FileIcons::ShaderIcons[1]; + icon_index = 1; + const auto& [_fa_icon, _svg_icon_key] = FileIcons::ShaderIcons[icon_index]; fa_icon = _fa_icon; svg_icon_key = _svg_icon_key; } if (Assets::IsTessellationControlShaderFile(handle->File())) { - const auto& [_fa_icon, _svg_icon_key] = FileIcons::ShaderIcons[2]; + icon_index = 2; + const auto& [_fa_icon, _svg_icon_key] = FileIcons::ShaderIcons[icon_index]; fa_icon = _fa_icon; svg_icon_key = _svg_icon_key; } if (Assets::IsTessellationEvaluationShaderFile(handle->File())) { - const auto& [_fa_icon, _svg_icon_key] = FileIcons::ShaderIcons[3]; + icon_index = 3; + const auto& [_fa_icon, _svg_icon_key] = FileIcons::ShaderIcons[icon_index]; fa_icon = _fa_icon; svg_icon_key = _svg_icon_key; } if (Assets::IsGeometryShaderFile(handle->File())) { - const auto& [_fa_icon, _svg_icon_key] = FileIcons::ShaderIcons[4]; + icon_index = 4; + const auto& [_fa_icon, _svg_icon_key] = FileIcons::ShaderIcons[icon_index]; fa_icon = _fa_icon; svg_icon_key = _svg_icon_key; } if (Assets::IsFragmentShaderFile(handle->File())) { - const auto& [_fa_icon, _svg_icon_key] = FileIcons::ShaderIcons[5]; + icon_index = 5; + const auto& [_fa_icon, _svg_icon_key] = FileIcons::ShaderIcons[icon_index]; fa_icon = _fa_icon; svg_icon_key = _svg_icon_key; } if (Assets::IsComputeShaderFile(handle->File())) { - const auto& [_fa_icon, _svg_icon_key] = FileIcons::ShaderIcons[6]; + icon_index = 6; + const auto& [_fa_icon, _svg_icon_key] = FileIcons::ShaderIcons[icon_index]; fa_icon = _fa_icon; svg_icon_key = _svg_icon_key; } } else if (handle->IsShaderProgram()) { - const auto& [_fa_icon, _svg_icon_key] = FileIcons::ShaderProgramIcons[0]; + const auto& [_fa_icon, _svg_icon_key] = FileIcons::ShaderProgramIcons[icon_index]; + fa_icon = _fa_icon; + svg_icon_key = _svg_icon_key; + } else if (handle->IsMaterial()) { + const auto& [_fa_icon, _svg_icon_key] = FileIcons::MaterialIcons[icon_index]; + fa_icon = _fa_icon; + svg_icon_key = _svg_icon_key; + } else if (handle->IsSampler()) { + const auto& [_fa_icon, _svg_icon_key] = FileIcons::SamplerIcons[icon_index]; fa_icon = _fa_icon; svg_icon_key = _svg_icon_key; } else if (handle->IsTexture()) { - const auto& [_fa_icon, _svg_icon_key] = FileIcons::TextureIcons[0]; + const auto& [_fa_icon, _svg_icon_key] = FileIcons::TextureIcons[icon_index]; fa_icon = _fa_icon; svg_icon_key = _svg_icon_key; } else { @@ -121,7 +136,7 @@ void MetaAssetInfoBank::TryEmplace(const FNode& file, const MetaAssetInfo& empla MetaAssetInfo emplaceThisIfAbsentCopy = emplaceThisIfAbsent; emplaceThisIfAbsentCopy.file = &file; - emplaceThisIfAbsentCopy.icon_index = 0; + emplaceThisIfAbsentCopy.icon_index = icon_index; emplaceThisIfAbsentCopy.fa_icon = fa_icon; emplaceThisIfAbsentCopy.svg_icon_key = std::move(svg_icon_key); metaInfo.try_emplace(&file, emplaceThisIfAbsentCopy); diff --git a/Editor/MetaAssetInfo.hpp b/Editor/MetaAssetInfo.hpp index bd3a4ffa..6f19ab49 100644 --- a/Editor/MetaAssetInfo.hpp +++ b/Editor/MetaAssetInfo.hpp @@ -15,7 +15,7 @@ struct MetaAssetInfo { const char* fa_icon{ nullptr }; /* from IconsFontAwesome6Pro.h */ std::string svg_icon_key{}; /* for SVGPathway::Get */ - void* GetSVGIcon(TextureStyle style = TextureStyle::PADDED) const; + void* GetSVGIcon(TextureSize size = TextureSize::MEDIUM, TextureStyle style = TextureStyle::PADDED) const; }; struct MetaAssetInfoBank { @@ -52,7 +52,7 @@ struct MetaAssetInfoBank { DataStructure metaInfo; }; -#if DEBUG +#ifdef DEBUG #include static_assert(std::is_default_constructible_v); static_assert(std::is_copy_constructible_v); diff --git a/Editor/MetaAssetInfoDeserializer.cpp b/Editor/MetaAssetInfoDeserializer.cpp index 52f1e154..228dfdd0 100644 --- a/Editor/MetaAssetInfoDeserializer.cpp +++ b/Editor/MetaAssetInfoDeserializer.cpp @@ -22,7 +22,7 @@ MetaAssetInfo DeserializeMetaAssetInfo(const std::string& data) { tinyxml2::XMLDocument doc; tinyxml2::XMLError err = doc.Parse(data.c_str()); if (err != tinyxml2::XML_SUCCESS) { - DOA_LOG_WARNING("Couldn't deserialize meta asset info!\n\n%s", data); + DOA_LOG_WARNING("Couldn't deserialize meta asset info!\n\n%s", data.c_str()); } const auto* rootElement = doc.RootElement(); @@ -42,7 +42,7 @@ MetaAssetInfoBankDeserializationResult DeserializeMetaAssetInfoBank(const std::s tinyxml2::XMLDocument doc; tinyxml2::XMLError err = doc.Parse(data.c_str()); if (err != tinyxml2::XML_SUCCESS) { - DOA_LOG_WARNING("Couldn't deserialize meta asset info bank! All custom set icons are, sadly, gone. You must re-apply your custom asset icons. %s", data); + DOA_LOG_WARNING("Couldn't deserialize meta asset info bank! All custom set icons are, sadly, gone. You must re-apply your custom asset icons. %s", data.c_str()); return { true, {} }; } @@ -81,6 +81,26 @@ static const char* FindIconInIcons(const char* icon) { return fa_icon; } } + for (const auto& [fa_icon, _] : FileIcons::ShaderIcons) { + if (std::strcmp(fa_icon, icon) == 0) { + return fa_icon; + } + } + for (const auto& [fa_icon, _] : FileIcons::ShaderProgramIcons) { + if (std::strcmp(fa_icon, icon) == 0) { + return fa_icon; + } + } + for (const auto& [fa_icon, _] : FileIcons::MaterialIcons) { + if (std::strcmp(fa_icon, icon) == 0) { + return fa_icon; + } + } + for (const auto& [fa_icon, _] : FileIcons::SamplerIcons) { + if (std::strcmp(fa_icon, icon) == 0) { + return fa_icon; + } + } for (const auto& [fa_icon, _] : FileIcons::RegularFileIcons) { if (std::strcmp(fa_icon, icon) == 0) { return fa_icon; diff --git a/Editor/NewAssetModal.cpp b/Editor/NewAssetModal.cpp index 4095a419..b2065541 100644 --- a/Editor/NewAssetModal.cpp +++ b/Editor/NewAssetModal.cpp @@ -45,6 +45,14 @@ void NewAssetModal::ShowShaderProgramCreationModal(FNode& currentFolder) const { /* cast-away const - this modal is never created const */ const_cast(this)->Reset(currentFolder, NewAssetData::AssetType::ShaderProgram); } +void NewAssetModal::ShowMaterialCreationModal(FNode& currentFolder) const { + /* cast-away const - this modal is never created const */ + const_cast(this)->Reset(currentFolder, NewAssetData::AssetType::Material); +} +void NewAssetModal::ShowSamplerCreationModal(FNode& currentFolder) const { + /* cast-away const - this modal is never created const */ + const_cast(this)->Reset(currentFolder, NewAssetData::AssetType::Sampler); +} void NewAssetModal::Hide() const { ImGui::CloseCurrentPopup(); @@ -71,6 +79,8 @@ void NewAssetModal::Reset(FNode& currentFolder, NewAssetData::AssetType typeOfAs static auto defGeometryShaderName = "MyGeometryShader"; assert(std::strlen(defGeometryShaderName) < buf.size()); static auto defFragmentShaderName = "MyFragmentShader"; assert(std::strlen(defFragmentShaderName) < buf.size()); static auto defShaderProgramName = "MyShaderProgram"; assert(std::strlen(defShaderProgramName) < buf.size()); + static auto defMaterialName = "MyMaterial"; assert(std::strlen(defMaterialName) < buf.size()); + static auto defSamplerName = "MySampler"; assert(std::strlen(defSamplerName) < buf.size()); switch(typeOfAssetToCreate) { using enum NewAssetData::AssetType; case Scene: @@ -113,6 +123,16 @@ void NewAssetModal::Reset(FNode& currentFolder, NewAssetData::AssetType typeOfAs titleText = std::format(TITLE_TEXT, "Shader Program"); confirmText = std::format(CONFIRM_TEXT, "a Shader Program"); break; + case Material: + std::strcpy(buf.data(), defMaterialName); + titleText = std::format(TITLE_TEXT, "Material"); + confirmText = std::format(CONFIRM_TEXT, "a Material"); + break; + case Sampler: + std::strcpy(buf.data(), defSamplerName); + titleText = std::format(TITLE_TEXT, "Sampler"); + confirmText = std::format(CONFIRM_TEXT, "a Sampler"); + break; case Texture: break; case Model: @@ -236,6 +256,12 @@ void NewAssetModal::CreateAsset() { case ShaderProgram: CreateShaderProgramAsset(); break; + case Material: + CreateMaterialAsset(); + break; + case Sampler: + CreateSamplerAsset(); + break; case Texture: break; case Model: @@ -251,43 +277,57 @@ void NewAssetModal::CreateSceneAsset() { void NewAssetModal::CreateComponentAsset() { const GUI& gui = this->gui.get(); const auto sourceCode = CodeGenerator::GenerateComponentDeclaration(newAssetData.name); - gui.CORE->GetAssets()->CreateAssetAt(*newAssetData.currentFolder, newAssetData.name + Assets::COMP_EXT, sourceCode); + gui.CORE->GetAssets()->CreateAssetAt(*newAssetData.currentFolder, newAssetData.name + Assets::ComponentDefinitionExtension, sourceCode); DOA_LOG_INFO("Succesfully created a new component asset named %s at %s", newAssetData.name.c_str(), newAssetData.path.c_str()); } void NewAssetModal::CreateVertexShaderAsset() { const GUI& gui = this->gui.get(); const auto sourceCode = CodeGenerator::GenerateVertexShaderCode(); - gui.CORE->GetAssets()->CreateAssetAt(*newAssetData.currentFolder, newAssetData.name + Assets::VERTEX_SHADER_EXT, sourceCode); + gui.CORE->GetAssets()->CreateAssetAt(*newAssetData.currentFolder, newAssetData.name + Assets::VertexShaderExtension, sourceCode); DOA_LOG_INFO("Succesfully created a new vertex shader asset named %s at %s", newAssetData.name.c_str(), newAssetData.path.c_str()); } void NewAssetModal::CreateTessellationControlAsset() { const GUI& gui = this->gui.get(); const auto sourceCode = CodeGenerator::GenerateTessellationControlShaderCode(); - gui.CORE->GetAssets()->CreateAssetAt(*newAssetData.currentFolder, newAssetData.name + Assets::TESS_CTRL_SHADER_EXT, sourceCode); + gui.CORE->GetAssets()->CreateAssetAt(*newAssetData.currentFolder, newAssetData.name + Assets::TessellationControlShaderExtension, sourceCode); DOA_LOG_INFO("Succesfully created a new tessellation control shader asset named %s at %s", newAssetData.name.c_str(), newAssetData.path.c_str()); } void NewAssetModal::CreateTessellationEvaluationAsset() { const GUI& gui = this->gui.get(); const auto sourceCode = CodeGenerator::GenerateTessellationEvaluationShaderCode(); - gui.CORE->GetAssets()->CreateAssetAt(*newAssetData.currentFolder, newAssetData.name + Assets::TESS_EVAL_SHADER_EXT, sourceCode); + gui.CORE->GetAssets()->CreateAssetAt(*newAssetData.currentFolder, newAssetData.name + Assets::TessellationEvaluationShaderExtension, sourceCode); DOA_LOG_INFO("Succesfully created a new tessellation evaluation shader asset named %s at %s", newAssetData.name.c_str(), newAssetData.path.c_str()); } void NewAssetModal::CreateGeometryShaderAsset() { const GUI& gui = this->gui.get(); const auto sourceCode = CodeGenerator::GenerateGeometryShaderCode(); - gui.CORE->GetAssets()->CreateAssetAt(*newAssetData.currentFolder, newAssetData.name + Assets::GEOMETRY_SHADER_EXT, sourceCode); + gui.CORE->GetAssets()->CreateAssetAt(*newAssetData.currentFolder, newAssetData.name + Assets::GeometryShaderExtension, sourceCode); DOA_LOG_INFO("Succesfully created a new geometry shader asset named %s at %s", newAssetData.name.c_str(), newAssetData.path.c_str()); } void NewAssetModal::CreateFragmentShaderAsset() { const GUI& gui = this->gui.get(); const auto sourceCode = CodeGenerator::GenerateFragmentShaderCode(); - gui.CORE->GetAssets()->CreateAssetAt(*newAssetData.currentFolder, newAssetData.name + Assets::FRAGMENT_SHADER_EXT, sourceCode); + gui.CORE->GetAssets()->CreateAssetAt(*newAssetData.currentFolder, newAssetData.name + Assets::FragmentShaderExtension, sourceCode); DOA_LOG_INFO("Succesfully created a new fragment shader asset named %s at %s", newAssetData.name.c_str(), newAssetData.path.c_str()); } void NewAssetModal::CreateShaderProgramAsset() { const GUI& gui = this->gui.get(); ShaderProgram temporary{ .Name = newAssetData.name }; const auto data = temporary.Serialize(); - gui.CORE->GetAssets()->CreateAssetAt(*newAssetData.currentFolder, newAssetData.name + Assets::SHADER_PROGRAM_EXT, data); + gui.CORE->GetAssets()->CreateAssetAt(*newAssetData.currentFolder, newAssetData.name + Assets::ShaderProgramExtension, data); DOA_LOG_INFO("Succesfully created a new shader program asset named %s at %s", newAssetData.name.c_str(), newAssetData.path.c_str()); -} \ No newline at end of file +} +void NewAssetModal::CreateMaterialAsset() { + const GUI& gui = this->gui.get(); + Material temporary{ newAssetData.name }; + const auto data = temporary.Serialize(); + gui.CORE->GetAssets()->CreateAssetAt(*newAssetData.currentFolder, newAssetData.name + Assets::MaterialExtension, data); + DOA_LOG_INFO("Succesfully created a material asset named %s at %s", newAssetData.name.c_str(), newAssetData.path.c_str()); +} +void NewAssetModal::CreateSamplerAsset() { + const GUI& gui = this->gui.get(); + Sampler temporary{ .Name = newAssetData.name }; + const auto data = temporary.Serialize(); + gui.CORE->GetAssets()->CreateAssetAt(*newAssetData.currentFolder, newAssetData.name + Assets::SamplerExtension, data); + DOA_LOG_INFO("Succesfully created a sampler asset named %s at %s", newAssetData.name.c_str(), newAssetData.path.c_str()); +} diff --git a/Editor/NewAssetModal.hpp b/Editor/NewAssetModal.hpp index 883c9128..6f64fc19 100644 --- a/Editor/NewAssetModal.hpp +++ b/Editor/NewAssetModal.hpp @@ -23,6 +23,8 @@ struct NewAssetModal { void ShowGeometryShaderCreationModal(FNode& currentFolder) const; void ShowFragmentShaderCreationModal(FNode& currentFolder) const; void ShowShaderProgramCreationModal(FNode& currentFolder) const; + void ShowMaterialCreationModal(FNode& currentFolder) const; + void ShowSamplerCreationModal(FNode& currentFolder) const; void Hide() const; private: @@ -51,6 +53,8 @@ struct NewAssetModal { GeometryShader, FragmentShader, ShaderProgram, + Material, + Sampler, Texture, Model } type; @@ -73,6 +77,8 @@ struct NewAssetModal { void CreateGeometryShaderAsset(); void CreateFragmentShaderAsset(); void CreateShaderProgramAsset(); + void CreateMaterialAsset(); + void CreateSamplerAsset(); friend struct GUI; diff --git a/Editor/Observer.cpp b/Editor/Observer.cpp index f5a45b2e..cc201f7b 100644 --- a/Editor/Observer.cpp +++ b/Editor/Observer.cpp @@ -1,49 +1,10 @@ #include -#include -#include - -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - #include -#include -#include #include -#include -#include -#include Observer::Observer(GUI& gui) noexcept : - gui(gui) { - ComponentDefinitionDisplay::Init(); - ShaderDisplay::Init(); - ShaderProgramDisplay::Init(); - - gui.Events.OnReimport += std::bind_front(&Observer::OnReimport, this); - gui.Events.OnAssetDeleted += std::bind_front(&Observer::OnAssetDeleted, this); - gui.Events.AssetManager.OnAssetFocused += std::bind_front(&Observer::OnAssetFocused, this); - gui.Events.AssetManager.OnFolderFocused += std::bind_front(&Observer::OnFolderFocused, this); - gui.Events.AssetManager.OnFocusLost += std::bind_front(&Observer::OnFocusLost, this); - gui.Events.OnSceneClosed += std::bind_front(&Observer::OnSceneClosed, this); - gui.Events.OnEntityDeleted += std::bind_front(&Observer::OnEntityDeleted, this); - gui.Events.SceneHierarchy.OnEntitySelected += std::bind_front(&Observer::OnEntitySelected, this); - gui.Events.SceneHierarchy.OnEntityDeselected += std::bind_front(&Observer::OnEntityDeselected, this); -} + gui(gui) {} bool Observer::Begin() { ImGui::PushID(WindowStrings::ObserverWindowName); @@ -53,1205 +14,10 @@ bool Observer::Begin() { } void Observer::Render() { - GUI& gui = this->gui; - DisplayTargetRenderer::Render(*this); + displayTargetRenderer.Render(); } void Observer::End() { ImGui::End(); ImGui::PopID(); } - -void Observer::SetDisplayTarget(Entity entity) { - static std::string hypen(" - "); - displayTarget = entity; - renderTargetTitleText = hypen + gui.get().GetOpenScene().GetComponent(entity).GetTagRef(); -} -void Observer::SetDisplayTarget(FNode& file) { - static std::string hypen(" - "); - displayTarget = &file; - renderTargetTitleText = hypen + file.Name().data(); -} -void Observer::ResetDisplayTarget() { - displayTarget = std::monostate{}; - renderTargetTitleText = ""; -} - -// Inner-struct DisplayTargetRenderer -void Observer::DisplayTargetRenderer::Render(Observer& observer) { - std::visit(overloaded::lambda { - [](std::monostate) { - HandleTargetWhenEmpty(); - }, - [&observer](Entity entity) { - GUI& gui = observer.gui.get(); - if (!gui.HasOpenScene()) { assert(false); } - - Scene& scene = gui.GetOpenScene(); - HandleTargetWhenEntity(observer, scene, entity); - }, - [&observer](FNode* file) { - HandleTargetWhenFile(observer, *file); - } - }, observer.displayTarget); -} - -void Observer::DisplayTargetRenderer::HandleTargetWhenEmpty() { - static constexpr const char text[] = "Nothing to display here :)"; - ImVec2 size = ImGui::GetContentRegionAvail(); - ImVec2 txtSize = ImGui::CalcTextSize(text); - for (int i = 0; i < 5; i++) { - ImGui::SameLine((size.x - txtSize.x) / 2, 0); - ImGui::Text(text); - ImGui::NewLine(); - } -} -void Observer::DisplayTargetRenderer::HandleTargetWhenEntity(const Observer& observer, Scene& scene, const Entity entt) { - const auto& idComponent = scene.GetComponent(entt); - ComponentUI::RenderIDComponent(observer, idComponent); - - const auto& transformComponent = scene.GetComponent(entt); - ComponentUI::RenderTransformComponent(observer, transformComponent); - - if (scene.HasComponent(entt)) { - const auto& parentComponent = scene.GetComponent(entt); - ComponentUI::RenderParentComponent(observer, parentComponent); - } - - if (scene.HasComponent(entt)) { - const auto& childComponent = scene.GetComponent(entt); - ComponentUI::RenderChildComponent(observer, childComponent); - } - - if (scene.HasComponent(entt)) { - const auto& orthoCameraComponent = scene.GetComponent(entt); - ComponentUI::RenderOrthoCameraComponent(observer, orthoCameraComponent); - } - - if (scene.HasComponent(entt)) { - const auto& perspectiveCameraComponent = scene.GetComponent(entt); - ComponentUI::RenderPerspectiveCameraComponent(observer, perspectiveCameraComponent); - } - - if (scene.HasComponent(entt)) { - const auto& storageComponent = scene.GetComponent(entt); - ComponentUI::RenderUserDefinedComponentStorage(observer, storageComponent); - } -} -void Observer::DisplayTargetRenderer::HandleTargetWhenFile(const Observer& observer, FNode& file) { - GUI& gui = observer.gui; - - static const ImVec2 iconSize{ 60.0f, 60.0f }; - ImGui::Columns(2); - ImGui::SetColumnWidth(0, iconSize.x * 1.25f); - ImGui::PushFont(gui.GetFontBold()); - - ImGui::PushStyleColor(ImGuiCol_Button, {}); - auto prePos = ImGui::GetCursorPos(); - auto& meta{ gui.GetMetaInfoOf(file) }; - if (ImGui::ImageButton(meta.GetSVGIcon(), iconSize)) { - ImGui::OpenPopup("assetIconCombo"); - } - auto afterPos = ImGui::GetCursorPos(); - ImGui::SetCursorPos({ prePos.x, prePos.y + iconSize.y }); - RenderIconChangePopup(file, meta); - ImGui::SetCursorPos(afterPos); - ImGui::PopStyleColor(); - - ImGui::SameLine(); - ImGui::NextColumn(); - - ImGui::TextUnformatted(file.Name().data()); - - ImGui::PopFont(); - ImGui::PushFont(gui.GetFont()); - - std::string path = file.Path().string(); - ImGui::TextUnformatted(std::string("Path: ROOT").append(sizeof(char), static_cast(std::filesystem::path::preferred_separator)).append(path).c_str()); - std::string absolutePath = file.AbsolutePath().string(); - ImGui::TextUnformatted(std::string("Absolute Path: ").append(absolutePath).c_str()); - - ImGui::PopFont(); - - ImGui::Columns(1); - ImGui::Separator(); - - if (file.IsDirectory()) { - RenderFolderView(observer, file); - } else { - AssetHandle h = gui.CORE->GetAssets()->FindAssetAt(file); - if (h.HasValue()) { - RenderAssetView(observer, h); - } - } -} -void Observer::DisplayTargetRenderer::RenderIconChangePopup(const FNode& file, MetaAssetInfo& meta) { - if (ImGui::BeginPopup("assetIconCombo")) { - const FileIcons::ElementType *begin{ nullptr }, *end{ nullptr }; - if (file.IsDirectory()) { - auto& items = FileIcons::DirectoryIcons; - begin = &items.front(); - end = &items.back() + 1; - } else if (Assets::IsSceneFile(file)) { - auto& items = FileIcons::SceneIcons; - begin = &items.front(); - end = &items.back() + 1; - } else if (Assets::IsComponentDefinitionFile(file)) { - auto& items = FileIcons::ComponentIcons; - begin = &items.front(); - end = &items.back() + 1; - } else if (Assets::IsShaderFile(file)) { - auto& items = FileIcons::ShaderIcons; - begin = &items.front(); - end = &items.back() + 1; - } else if (Assets::IsShaderProgramFile(file)) { - auto& items = FileIcons::ShaderProgramIcons; - begin = &items.front(); - end = &items.back() + 1; - } else if (Assets::IsTextureFile(file)) { - auto& items = FileIcons::TextureIcons; - begin = &items.front(); - end = &items.back() + 1; - } else { - auto& items = FileIcons::RegularFileIcons; - begin = &items.front(); - end = &items.back() + 1; - } - - assert(begin); - assert(end); - auto selectedItem = begin + meta.icon_index; - for (auto current = begin; current != end; current++) { - const bool isSelected{ current == selectedItem }; - if (ImGui::Selectable(current->first, isSelected)) { - selectedItem = current; - } - // Set the initial focus when opening the combo (scrolling + keyboard navigation focus) - if (isSelected) { - ImGui::SetItemDefaultFocus(); - } - } - - meta.icon_index = selectedItem - begin; - meta.fa_icon = selectedItem->first; - meta.svg_icon_key = selectedItem->second; - ImGui::EndPopup(); - } -} - -void Observer::DisplayTargetRenderer::RenderFolderView(const Observer& observer, FNode& folder) { - assert(folder.IsDirectory()); - const GUI& gui = observer.gui; - - ImGuiTableFlags flags = ImGuiTableFlags_RowBg | - ImGuiTableFlags_Borders | - ImGuiTableFlags_Resizable | - ImGuiTableFlags_SizingFixedFit | - ImGuiTableFlags_NoBordersInBodyUntilResize; - - if (ImGui::BeginTable("folderview", 3, flags)) { - - ImGui::TableSetupColumn("", ImGuiTableColumnFlags_NoResize); - ImGui::TableSetupColumn("Files", ImGuiTableColumnFlags_WidthStretch, 0.6f); - ImGui::TableSetupColumn("Size", ImGuiTableColumnFlags_WidthStretch, 0.3f); - ImGui::TableHeadersRow(); - - for (auto& child : folder.Children()) { - if (!child.IsDirectory() && !gui.CORE->GetAssets()->IsAssetExistsAt(child)) { continue; } - - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - ImGui::Image(gui.FindIconForFileType(child, TextureSize::SMALL), { 16.0f, 16.0f }); - ImGui::TableSetColumnIndex(1); - ImGui::TextUnformatted(child.FullName().data()); - ImGui::TableSetColumnIndex(2); - std::string size = FormatBytes(static_cast(child.Size())); - ImGui::TextUnformatted(size.c_str()); - } - ImGui::EndTable(); - } -} -void Observer::DisplayTargetRenderer::RenderAssetView(const Observer& observer, AssetHandle h) { - if (h->IsScene()) { - RenderSceneView(observer, h); - } else if (h->IsComponentDefinition()) { - RenderComponentDefinitionView(observer, h); - } else if (h->IsShader()) { - RenderShaderView(observer, h); - } else if (h->IsShaderProgram()) { - RenderShaderProgramView(observer, h); - } else if (h->IsTexture()) { - RenderTextureView(observer, h); - } else { - RenderTextView(observer, h); - } -} -void Observer::DisplayTargetRenderer::RenderSceneView(const Observer& observer, AssetHandle h) { - assert(h->IsScene()); - - Scene& scene = h->DataAs(); - - SceneDisplay::RenderEntities(observer, scene); - SceneDisplay::RenderSystems(observer, scene); - -} -void Observer::DisplayTargetRenderer::RenderComponentDefinitionView(const Observer& observer, AssetHandle h) { - assert(h->IsComponentDefinition()); - - ComponentDefinitionDisplay::SetDisplayTarget(h); - ComponentDefinitionDisplay::RenderMessagesTable(); - - if (h->HasDeserializedData()) { - if (!h->HasErrorMessages()) { - ComponentDefinitionDisplay::RenderFields(); - ImGui::Separator(); - ComponentDefinitionDisplay::RenderSourceCode(); - } - } else { - ImGui::Text("Component Definition is not deserialized..."); - } - - static int extraPadding = 16; - float lineHeight = ImGui::GetTextLineHeight() + ImGui::GetStyle().FramePadding.y * 2; - if (ImGui::GetContentRegionAvail().y > 34.0f) { - ImGui::SetCursorPosY(ImGui::GetWindowHeight() - lineHeight - extraPadding); - } - - if (ImGui::Button("Refresh", { ImGui::GetContentRegionAvail().x, 0 })) { - h->ForceDeserialize(); - } - - if (ImGui::IsItemHovered()) { - ImGui::BeginTooltip(); - ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); - ImGui::TextUnformatted("Forces deserialization on this component definition. All data in RAM is purged, and new data is read from disk. This operation will cause re-instantiation of user defined components without loss of data."); - ImGui::PopTextWrapPos(); - ImGui::EndTooltip(); - } -} -void Observer::DisplayTargetRenderer::RenderShaderView(const Observer& observer, AssetHandle h) { - assert(h->IsShader()); - - ShaderDisplay::SetDisplayTarget(h); - ShaderDisplay::RenderMessagesTable(); - - if (h->HasDeserializedData()) { - if (!h->HasErrorMessages()) { - ShaderDisplay::RenderFields(); - ImGui::Separator(); - ShaderDisplay::RenderSourceCode(); - } - } else { - ImGui::Text("Shader is not deserialized..."); - } - - static int extraPadding = 16; - float lineHeight = ImGui::GetTextLineHeight() + ImGui::GetStyle().FramePadding.y * 2; - if (ImGui::GetContentRegionAvail().y > 34.0f) { - ImGui::SetCursorPosY(ImGui::GetWindowHeight() - lineHeight - extraPadding); - } - - if (ImGui::Button("Refresh", { ImGui::GetContentRegionAvail().x, 0 })) { - h->ForceDeserialize(); - } - if (ImGui::IsItemHovered()) { - ImGui::BeginTooltip(); - ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); - ImGui::TextUnformatted("Forces deserialization on this shader. All data in VRAM is purged, and new data is allocated."); - ImGui::PopTextWrapPos(); - ImGui::EndTooltip(); - } -} -void Observer::DisplayTargetRenderer::RenderShaderProgramView(const Observer& observer, AssetHandle h) { - assert(h->IsShaderProgram()); - - ShaderProgramDisplay::SetDisplayTarget(*observer.gui.get().CORE->GetAssets(), h); - ShaderProgramDisplay::RenderMessagesTable(); - ImGui::Separator(); - if (h->HasDeserializedData()) { - ShaderProgramDisplay::RenderShaders(); - } else { - ImGui::Text("Shader Program is not deserialized..."); - } - - static int extraPadding = 16; - float lineHeight = ImGui::GetTextLineHeight() + ImGui::GetStyle().FramePadding.y * 2; - if (ImGui::GetContentRegionAvail().y > 34.0f) { - ImGui::SetCursorPosY(ImGui::GetWindowHeight() - lineHeight - extraPadding); - } - - if (ImGui::Button("Refresh", { ImGui::GetContentRegionAvail().x, 0 })) { - h->ForceDeserialize(); - } - if (ImGui::IsItemHovered()) { - ImGui::BeginTooltip(); - ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); - ImGui::TextUnformatted("Forces deserialization on this shader program. All data in VRAM is purged, and new data is allocated."); - ImGui::PopTextWrapPos(); - ImGui::EndTooltip(); - } -} -void Observer::DisplayTargetRenderer::RenderTextureView(const Observer& observer, AssetHandle h) { - assert(h->IsTexture()); - /* channels */ - static bool r{ true }, g{ true }, b{ true }, a{ true }; - /* inspect params */ - static bool drawInspector{ true }, drawHistogram{ false }, drawNormals{ false }; - - static int bottomContentLineCount = 3; - float lineHeight = ImGui::GetTextLineHeight() + ImGui::GetStyle().FramePadding.y * 2; - static float extraPadding = 16; /* pad by an extra amount to remove scroll bar, don't pad and see the scroll bar appear on right side */ - //ImGui::SliderFloat("label", &extraPadding, 0, 50); /* was used to test padding amount, not deleted to easily re-test in future */ - float totalBottomPadding = lineHeight * bottomContentLineCount + extraPadding; - - auto [windowWidth, windowHeight] = ImGui::GetContentRegionAvail(); - windowWidth = windowWidth - ImGui::GetStyle().FramePadding.x; - windowHeight = windowHeight - totalBottomPadding; - - if (h->HasDeserializedData()) { - const auto& tex = h->DataAs(); - float w = static_cast(tex.Width()); - float h = static_cast(tex.Height()); - float aspect = w / h; - - float maxWidth = windowWidth; - float maxHeight = windowHeight; - - w = maxWidth; - h = w / aspect; - - if (h > maxHeight) { - aspect = w / h; - h = maxHeight; - w = h * aspect; - } - - ImGui::Image(tex.TextureIDRaw(), { w, h }, { 0, 1 }, { 1, 0 }, { (float) r, (float) g, (float) b, (float) a }, { 1, 1, 0, 1 }); - - if (drawInspector) { - ImRect rc = ImRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax()); - ImVec2 mouseUVCoord = (ImGui::GetIO().MousePos - rc.Min) / rc.GetSize(); - mouseUVCoord.y = 1.f - mouseUVCoord.y; - if (mouseUVCoord.x >= 0.0f && - mouseUVCoord.y >= 0.0f && - mouseUVCoord.x <= 1.0f && - mouseUVCoord.y <= 1.0f) { - float w = static_cast(tex.Width()); - float h = static_cast(tex.Height()); - auto pixels = reinterpret_cast(Texture::GetByteBufferOf(tex)); - ImageInspect::inspect(static_cast(w), static_cast(h), pixels, mouseUVCoord, { w, h }, drawNormals, drawHistogram); - } - } - } else { - ImGui::Text("Texture is not deserialized..."); - } - - ImGui::SetCursorPosY(ImGui::GetWindowHeight() - totalBottomPadding); - ImGui::AlignTextToFramePadding(); ImGui::Text("Channels"); - ImGui::SameLine(); ImGui::TextColored({ 1.0f, 0.0f, 0.0f, 1.0f }, "R:"); ImGui::SameLine(); ImGui::Checkbox("##r", &r); - ImGui::SameLine(); ImGui::TextColored({ 0.0f, 1.0f, 0.0f, 1.0f }, "G:"); ImGui::SameLine(); ImGui::Checkbox("##g", &g); - ImGui::SameLine(); ImGui::TextColored({ 0.2f, 0.2f, 1.0f, 1.0f }, "B:"); ImGui::SameLine(); ImGui::Checkbox("##b", &b); - ImGui::SameLine(); ImGui::TextColored({ 0.5f, 0.5f, 0.5f, 1.0f }, "A:"); ImGui::SameLine(); ImGui::Checkbox("##a", &a); - - ImGui::AlignTextToFramePadding(); ImGui::Text("Image Inspect:"); ImGui::SameLine(); ImGui::Checkbox("##inspect", &drawInspector); - ImGui::SameLine(); ImGui::Text("Normals:"); ImGui::SameLine(); ImGui::Checkbox("##normals", &drawNormals); - ImGui::SameLine(); ImGui::Text("Histogram:"); ImGui::SameLine(); ImGui::Checkbox("##histogram", &drawHistogram); - if (ImGui::Button("Refresh", { ImGui::GetContentRegionAvail().x, 0 })) { - h->ForceDeserialize(); - } - if (ImGui::IsItemHovered()) { - ImGui::BeginTooltip(); - ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); - ImGui::TextUnformatted("Forces deserialization on this texture. All data in RAM/VRAM is purged, and new data is read from disk."); - ImGui::PopTextWrapPos(); - ImGui::EndTooltip(); - } -} -void Observer::DisplayTargetRenderer::RenderTextView(const Observer& observer, AssetHandle h) { - if (!h->File().HasContent()) { - h->File().ReadContent(); - } - std::string content{ h->File().Content() }; - - ImGuiTableFlags flags = ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders; - if (ImGui::BeginTable("content", 1, flags)) { - ImGui::TableSetupColumn("File Content", ImGuiTableColumnFlags_WidthStretch); - ImGui::TableHeadersRow(); - - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - - auto size = ImGui::GetContentRegionAvail(); - size.y -= 5; /* this "hides" the "1px vertical scroll-bar" in observer window */ - ImGuiInputTextFlags flags = ImGuiInputTextFlags_ReadOnly; - ImGui::InputTextMultiline("", content.data(), content.size(), size, flags); - - ImGui::EndTable(); - } -} - -// Inner-struct SceneDisplay -void Observer::SceneDisplay::RenderEntities(const Observer& observer, Scene& scene) { - GUI& gui = observer.gui; - - auto flags = ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders; - if (ImGui::BeginTable("entities", 1, flags)) { - ImGui::TableSetupColumn("Entities", ImGuiTableColumnFlags_WidthStretch); - ImGui::TableHeadersRow(); - - for (Entity entt : scene.GetAllEntites()) { - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - std::string label = std::to_string(entt).append("\t\t(").append(scene.GetComponent(entt).GetTagRef()).append(")"); - if (ImGui::CollapsingHeader(label.c_str(), ImGuiTreeNodeFlags_SpanAvailWidth)) { - std::string nameWithIcon{}; - - nameWithIcon = ComponentIcons::FindIconByComponentName(nameof(IDComponent)) + nameof(IDComponent); - ImGui::TextUnformatted(nameWithIcon.c_str()); - - nameWithIcon = ComponentIcons::FindIconByComponentName(nameof(TransformComponent)) + nameof(TransformComponent); - ImGui::TextUnformatted(nameWithIcon.c_str()); - if (scene.HasComponent(entt)) { - nameWithIcon = ComponentIcons::FindIconByComponentName(nameof(ParentComponent)) + nameof(ParentComponent); - ImGui::TextUnformatted(nameWithIcon.c_str()); - } - if (scene.HasComponent(entt)) { - nameWithIcon = ComponentIcons::FindIconByComponentName(nameof(ChildComponent)) + nameof(ChildComponent); - ImGui::TextUnformatted(nameWithIcon.c_str()); - } - if (scene.HasComponent(entt)) { - nameWithIcon = ComponentIcons::FindIconByComponentName(nameof(OrthoCameraComponent)) + nameof(OrthoCameraComponent); - ImGui::TextUnformatted(nameWithIcon.c_str()); - } - if (scene.HasComponent(entt)) { - nameWithIcon = ComponentIcons::FindIconByComponentName(nameof(PerspectiveCameraComponent)) + nameof(PerspectiveCameraComponent); - ImGui::TextUnformatted(nameWithIcon.c_str()); - } - if (scene.HasComponent(entt)) { - auto& udcs{ scene.GetComponent(entt) }; - for (const auto& [name, instance] : udcs.Components()) { - if (!instance.HasError()) { - AssetHandle cmpAsset{ gui.CORE->GetAssets()->FindAsset(instance.ComponentAssetID()) }; - nameWithIcon = ComponentIcons::FindIconByComponentName(name) + name; - ImGui::TextUnformatted(nameWithIcon.c_str()); - } else { - ImGui::PushStyleColor(ImGuiCol_Text, { 1.0f, 0.5f, 0.1f, 1.0f }); - - nameWithIcon = ComponentIcons::FindIconForInstantiationError(instance.GetError()) + name; - ImGui::TextUnformatted(nameWithIcon.c_str()); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("%s", instance.ErrorString().data()); - } - - ImGui::PopStyleColor(); - } - } - } - } - } - ImGui::EndTable(); - } -} -void Observer::SceneDisplay::RenderSystems(const Observer& observer, const Scene& scene) { - auto flags = ImGuiTableFlags_RowBg | - ImGuiTableFlags_Borders | - ImGuiTableFlags_SizingFixedFit | - ImGuiTableFlags_NoBordersInBodyUntilResize; - if (ImGui::BeginTable("systems", 2, flags)) { - ImGui::TableSetupColumn("Systems", ImGuiTableColumnFlags_WidthStretch); - ImGui::TableSetupColumn("", ImGuiTableColumnFlags_NoResize); - ImGui::TableHeadersRow(); - - ImGui::EndTable(); - } -} - -// Inner-struct ComponentDefinitionDisplay -void Observer::ComponentDefinitionDisplay::Init() { - TextEditorInstance.SetColorizerEnable(true); - TextEditorInstance.SetReadOnlyEnabled(true); - TextEditorInstance.SetShowWhitespaces(false); - TextEditorInstance.SetText(std::string(10, ' ')); - TextEditorInstance.SetLanguageDefinition(TextEditor::LanguageDefinition::AngelScript()); -} -void Observer::ComponentDefinitionDisplay::SetDisplayTarget(const AssetHandle componentDefAsset) { - assert(componentDefAsset->IsComponentDefinition()); - if (ComponentDefAsset != componentDefAsset) { - ComponentDefAsset = componentDefAsset; - if (ComponentDefAsset->HasDeserializedData() && !ComponentDefAsset->HasErrorMessages()) { - const auto& componentDef = ComponentDefAsset->DataAs(); - TextEditorInstance.SetText(componentDef.declaration); - } - } -} - -void Observer::ComponentDefinitionDisplay::RenderMessagesTable() { - if(!ComponentDefAsset->HasErrorMessages() && - !ComponentDefAsset->HasWarningMessages() && - !ComponentDefAsset->HasInfoMessages()) { - return; - } - - ImGuiTableFlags flags = ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders; - - ImGui::BeginTable("compiler_logs", 2, flags); - - ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, 30); - ImGui::TableSetupColumn("Compiler Logs", ImGuiTableColumnFlags_WidthStretch); - ImGui::TableHeadersRow(); - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 0, 0 }); - - ImGui::PushStyleColor(ImGuiCol_Text, ComponentDefinitionViewColors::ERROR_COLOR); - for (auto& message : ComponentDefAsset->ErrorMessages()) { - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - - float r = BeginTableColumnCenterText(ComponentDefinitionViewIcons::ERROR_ICON); - ImGui::Text(ComponentDefinitionViewIcons::ERROR_ICON); - EndTableColumnCenterText(r); - - ImGui::TableSetColumnIndex(1); - - const ComponentCompilerMessage& m{ std::any_cast(message) }; - ImGui::TextWrapped("%s", m.message.c_str()); - } - ImGui::PopStyleColor(); - - ImGui::PushStyleColor(ImGuiCol_Text, ComponentDefinitionViewColors::WARNING_COLOR); - for (auto& message : ComponentDefAsset->WarningMessages()) { - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - - float r = BeginTableColumnCenterText(ComponentDefinitionViewIcons::WARNING_ICON); - ImGui::Text(ComponentDefinitionViewIcons::WARNING_ICON); - EndTableColumnCenterText(r); - - ImGui::TableSetColumnIndex(1); - - const ComponentCompilerMessage& m{ std::any_cast(message) }; - ImGui::TextWrapped("%s", m.message.c_str()); - } - ImGui::PopStyleColor(); - - ImGui::PushStyleColor(ImGuiCol_Text, ComponentDefinitionViewColors::INFO_COLOR); - for (auto& message : ComponentDefAsset->InfoMessages()) { - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - - float r = BeginTableColumnCenterText(ComponentDefinitionViewIcons::INFO_ICON); - ImGui::Text(ComponentDefinitionViewIcons::INFO_ICON); - EndTableColumnCenterText(r); - - ImGui::TableSetColumnIndex(1); - - const ComponentCompilerMessage& m{ std::any_cast(message) }; - ImGui::TextWrapped("%s", m.message.c_str()); - } - ImGui::PopStyleColor(); - - ImGui::PopStyleVar(); - ImGui::EndTable(); -} -void Observer::ComponentDefinitionDisplay::RenderFields() { - assert(ComponentDefAsset->HasDeserializedData()); - assert(!ComponentDefAsset->HasErrorMessages()); - - const auto& componentDef = ComponentDefAsset->DataAs(); - - ImGuiTableFlags flags = ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders; - if (ImGui::CollapsingHeader(componentDef.name.c_str(), ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_DefaultOpen)) { - if (ImGui::BeginTable("component_fields", 2, flags)) { - ImGui::TableSetupColumn("Field", ImGuiTableColumnFlags_WidthStretch); - ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthStretch); - ImGui::TableHeadersRow(); - - for (auto& field : componentDef.fields) { - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - ImGui::TextUnformatted(field.name.c_str()); - ImGui::TableSetColumnIndex(1); - ImGui::TextUnformatted(field.typeName.c_str()); - } - ImGui::EndTable(); - } - } -} -void Observer::ComponentDefinitionDisplay::RenderSourceCode() { - ImGuiTableFlags flags = ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders; - ImGui::BeginTable("source_code", 1, flags); - ImGui::TableSetupColumn("Source Code", ImGuiTableColumnFlags_WidthStretch); - ImGui::TableHeadersRow(); - - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - - TextEditorInstance.Render("###ObserverComponentDefSourceCodeViewer", false, { 0.0f, -32.0f }); - - ImGui::EndTable(); -} - -// Inner-struct ShaderDisplay -void Observer::ShaderDisplay::Init() { - TextEditorInstance.SetColorizerEnable(true); - TextEditorInstance.SetReadOnlyEnabled(true); - TextEditorInstance.SetShowWhitespaces(false); - TextEditorInstance.SetText(std::string(10, ' ')); - TextEditorInstance.SetLanguageDefinition(TextEditor::LanguageDefinition::GLSL()); -} -void Observer::ShaderDisplay::SetDisplayTarget(const AssetHandle shaderAsset) { - assert(shaderAsset->IsShader()); - if (ShaderAsset != shaderAsset) { - ShaderAsset = shaderAsset; - if (ShaderAsset->HasDeserializedData() && !ShaderAsset->HasErrorMessages()) { - const auto& shader = ShaderAsset->DataAs(); - TextEditorInstance.SetText(shader.SourceCode); - } - } -} - -void Observer::ShaderDisplay::RenderMessagesTable() { - if (!ShaderAsset->HasErrorMessages() && - !ShaderAsset->HasWarningMessages() && - !ShaderAsset->HasInfoMessages()) { - return; - } - - ImGuiTableFlags flags = ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders; - - ImGui::BeginTable("compiler_logs", 2, flags); - - ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, 30); - ImGui::TableSetupColumn("Compiler Logs", ImGuiTableColumnFlags_WidthStretch); - ImGui::TableHeadersRow(); - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 0, 0 }); - - ImGui::PushStyleColor(ImGuiCol_Text, ComponentDefinitionViewColors::ERROR_COLOR); - for (auto& message : ShaderAsset->ErrorMessages()) { - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - - float r = BeginTableColumnCenterText(ComponentDefinitionViewIcons::ERROR_ICON); - ImGui::Text(ComponentDefinitionViewIcons::ERROR_ICON); - EndTableColumnCenterText(r); - - ImGui::TableSetColumnIndex(1); - - const ShaderCompilerMessage& m{ std::any_cast(message) }; - ImGui::TextWrapped("%s", m.shortMessage.c_str()); - } - ImGui::PopStyleColor(); - - ImGui::PushStyleColor(ImGuiCol_Text, ComponentDefinitionViewColors::WARNING_COLOR); - for (auto& message : ShaderAsset->WarningMessages()) { - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - - float r = BeginTableColumnCenterText(ComponentDefinitionViewIcons::WARNING_ICON); - ImGui::Text(ComponentDefinitionViewIcons::WARNING_ICON); - EndTableColumnCenterText(r); - - ImGui::TableSetColumnIndex(1); - - const ShaderCompilerMessage& m{ std::any_cast(message) }; - ImGui::TextWrapped("%s", m.shortMessage.c_str()); - } - ImGui::PopStyleColor(); - - ImGui::PushStyleColor(ImGuiCol_Text, ComponentDefinitionViewColors::INFO_COLOR); - for (auto& message : ShaderAsset->InfoMessages()) { - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - - float r = BeginTableColumnCenterText(ComponentDefinitionViewIcons::INFO_ICON); - ImGui::Text(ComponentDefinitionViewIcons::INFO_ICON); - EndTableColumnCenterText(r); - - ImGui::TableSetColumnIndex(1); - - const ShaderCompilerMessage& m{ std::any_cast(message) }; - ImGui::TextWrapped("%s", m.shortMessage.c_str()); - } - ImGui::PopStyleColor(); - - ImGui::PopStyleVar(); - ImGui::EndTable(); -} -void Observer::ShaderDisplay::RenderFields() { - /* - ImGuiTableFlags flags = ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders; - if (ImGui::CollapsingHeader(componentDef.name.c_str(), ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_DefaultOpen)) { - if (ImGui::BeginTable("component_fields", 2, flags)) { - ImGui::TableSetupColumn("Field", ImGuiTableColumnFlags_WidthStretch); - ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthStretch); - ImGui::TableHeadersRow(); - - for (auto& field : componentDef.fields) { - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - ImGui::Text(field.name.c_str()); - ImGui::TableSetColumnIndex(1); - ImGui::Text(field.typeName.c_str()); - } - ImGui::EndTable(); - } - } - */ -} -void Observer::ShaderDisplay::RenderSourceCode() { - ImGuiTableFlags flags = ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders; - ImGui::BeginTable("source_code", 1, flags); - ImGui::TableSetupColumn("Source Code", ImGuiTableColumnFlags_WidthStretch); - ImGui::TableHeadersRow(); - - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - - TextEditorInstance.Render("###ObserverShaderSourceCodeViewer", false, { 0.0f, -32.0f }); - - ImGui::EndTable(); -} - -// Inner-struct ShaderProgramDisplay -void Observer::ShaderProgramDisplay::Init() { - VertexShaderText = std::string(128, 0); - TessCtrlShaderText = std::string(128, 0); - TessEvalShaderText = std::string(128, 0); - GeometryShaderText = std::string(128, 0); - FragmentShaderText = std::string(128, 0); -} -void Observer::ShaderProgramDisplay::SetDisplayTarget(Assets& assets, const AssetHandle shaderProgramAsset) { - Observer::ShaderProgramDisplay::assets = &assets; - assert(shaderProgramAsset->IsShaderProgram()); - if (ShaderProgramAsset != shaderProgramAsset) { - ShaderProgramAsset = shaderProgramAsset; - const ShaderProgram& program = ShaderProgramAsset->DataAs(); - - if (program.HasVertexShader()) { - UUID uuid = program.VertexShader; - VertexShaderText = std::format("{} (UUID: {})", assets.FindAsset(uuid)->File().Name(), std::to_string(uuid)); - } else { - VertexShaderText = NOT_SET_MANDATORY_TEXT; - } - - if (program.HasTessellationControlShader()) { - UUID uuid = program.TessellationControlShader; - TessCtrlShaderText = std::format("{} (UUID: {})", assets.FindAsset(uuid)->File().Name(), std::to_string(uuid)); - } else { - TessCtrlShaderText = NOT_SET_OPTIONAL_TEXT; - } - - if (program.HasTessellationEvaluationShader()) { - UUID uuid = program.TessellationEvaluationShader; - TessEvalShaderText = std::format("{} (UUID: {})", assets.FindAsset(uuid)->File().Name(), std::to_string(uuid)); - } else { - TessEvalShaderText = NOT_SET_OPTIONAL_TEXT; - } - - if (program.HasGeometryShader()) { - UUID uuid = program.GeometryShader; - GeometryShaderText = std::format("{} (UUID: {})", assets.FindAsset(uuid)->File().Name(), std::to_string(uuid)); - } else { - GeometryShaderText = NOT_SET_OPTIONAL_TEXT; - } - - if (program.HasFragmentShader()) { - UUID uuid = program.FragmentShader; - FragmentShaderText = std::format("{} (UUID: {})", assets.FindAsset(uuid)->File().Name(), std::to_string(uuid)); - } else { - FragmentShaderText = NOT_SET_MANDATORY_TEXT; - } - } -} - -void Observer::ShaderProgramDisplay::RenderMessagesTable() { - if (!ShaderProgramAsset->HasErrorMessages() && - !ShaderProgramAsset->HasWarningMessages() && - !ShaderProgramAsset->HasInfoMessages()) { - return; - } - - ImGuiTableFlags flags = ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders; - - ImGui::BeginTable("linker_logs", 2, flags); - - ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, 30); - ImGui::TableSetupColumn("Linker Logs", ImGuiTableColumnFlags_WidthStretch); - ImGui::TableHeadersRow(); - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 0, 0 }); - - ImGui::PushStyleColor(ImGuiCol_Text, ComponentDefinitionViewColors::ERROR_COLOR); - for (auto& message : ShaderProgramAsset->ErrorMessages()) { - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - - float r = BeginTableColumnCenterText(ComponentDefinitionViewIcons::ERROR_ICON); - ImGui::Text(ComponentDefinitionViewIcons::ERROR_ICON); - EndTableColumnCenterText(r); - - ImGui::TableSetColumnIndex(1); - - const std::string& m{ std::any_cast(message) }; - ImGui::TextWrapped("%s", m.c_str()); - } - ImGui::PopStyleColor(); - - ImGui::PushStyleColor(ImGuiCol_Text, ComponentDefinitionViewColors::WARNING_COLOR); - for (auto& message : ShaderProgramAsset->WarningMessages()) { - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - - float r = BeginTableColumnCenterText(ComponentDefinitionViewIcons::WARNING_ICON); - ImGui::Text(ComponentDefinitionViewIcons::WARNING_ICON); - EndTableColumnCenterText(r); - - ImGui::TableSetColumnIndex(1); - - const std::string& m{ std::any_cast(message) }; - ImGui::TextWrapped("%s", m.c_str()); - } - ImGui::PopStyleColor(); - - ImGui::PushStyleColor(ImGuiCol_Text, ComponentDefinitionViewColors::INFO_COLOR); - for (auto& message : ShaderProgramAsset->InfoMessages()) { - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - - float r = BeginTableColumnCenterText(ComponentDefinitionViewIcons::INFO_ICON); - ImGui::Text(ComponentDefinitionViewIcons::INFO_ICON); - EndTableColumnCenterText(r); - - ImGui::TableSetColumnIndex(1); - - const std::string& m{ std::any_cast(message) }; - ImGui::TextWrapped("%s", m.c_str()); - } - ImGui::PopStyleColor(); - - ImGui::PopStyleVar(); - ImGui::EndTable(); -} -void Observer::ShaderProgramDisplay::RenderShaders() { - ShaderProgram& program = ShaderProgramAsset->DataAs(); - RenderVertexShader(program); - RenderTessCtrlShader(program); - RenderTessEvalShader(program); - RenderGeometryShader(program); - RenderFragmentShader(program); -} - -void Observer::ShaderProgramDisplay::Begin(std::string_view label) { - float w = ImGui::GetContentRegionAvail().x; - - ImGui::PushID(label.data()); - ImGui::Columns(2, nullptr, false); - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { ImGui::GetStyle().ItemSpacing.x, 0 }); - ImGui::SetColumnWidth(0, 240); - ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetStyle().FramePadding.y * 0.5f + 3); - ImGui::TextDisabled("%s", label.data()); - ImGui::NextColumn(); - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 0, 0 }); - ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetStyle().FramePadding.y * 0.5f); - - float buttonWidth; - { - ImGuiIO& io = ImGui::GetIO(); - auto boldFont = io.Fonts->Fonts[1]; - buttonWidth = boldFont->FontSize + ImGui::GetStyle().FramePadding.y * 2.0f; - } - - ImGui::PushItemWidth(w - 240 - buttonWidth); - ImGuiIO& io = ImGui::GetIO(); - auto font = io.Fonts->Fonts[0]; - ImGui::PushFont(font); -} -void Observer::ShaderProgramDisplay::RenderVertexShader(ShaderProgram& program) { - ImGui::BeginDisabled(); - if (program.HasVertexShader()) { - ImGui::PushStyleColor(ImGuiCol_TextDisabled, SET_COLOR); - } else { - ImGui::PushStyleColor(ImGuiCol_TextDisabled, NOT_SET_MANDATORY_COLOR); - } - Begin("Vertex Shader"); - ImGui::InputText("###vertex_shader_text", VertexShaderText.data(), VertexShaderText.capacity(), ImGuiInputTextFlags_ReadOnly); - if (ImGui::BeginDragDropTarget()) { - if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("DND_DEMO_CELL")) { - UUID data = *(const UUID*) payload->Data; - AssetHandle handle = assets->FindAsset(data); - assert(handle.HasValue()); - if (handle->IsShader()) { - program.VertexShader = data; - ShaderProgramAsset->Serialize(); - ShaderProgramAsset->ForceDeserialize(); - VertexShaderText = std::format("{} (UUID: {})", assets->FindAsset(data)->File().Name(), std::to_string(data)); - } - } - } - ImGui::EndDragDropTarget(); - } - ImGui::PopStyleColor(); - ImGui::EndDisabled(); - - ImGui::SameLine(); - ImGuiIO& io = ImGui::GetIO(); - auto boldFont = io.Fonts->Fonts[1]; - ImGui::PushFont(boldFont); - float lineHeight = boldFont->FontSize + ImGui::GetStyle().FramePadding.y * 2.0f; - ImVec2 buttonSize = { lineHeight + 3.0f, lineHeight }; - - ImGui::PushStyleColor(ImGuiCol_Button, { .80f, .10f, .15f, 1 }); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, { .80f, .10f, .15f, 1 }); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, { .94f, .51f, .55f, 1 }); - if (ImGui::Button("X", buttonSize)) { - program.VertexShader = 0; - ShaderProgramAsset->Serialize(); - ShaderProgramAsset->ForceDeserialize(); - VertexShaderText = NOT_SET_MANDATORY_TEXT; - } - ImGui::PopStyleColor(3); - ImGui::PopFont(); - - End(); -} -void Observer::ShaderProgramDisplay::RenderTessCtrlShader(ShaderProgram& program) { - ImGui::BeginDisabled(); - if (program.HasTessellationControlShader()) { - ImGui::PushStyleColor(ImGuiCol_TextDisabled, SET_COLOR); - } else { - ImGui::PushStyleColor(ImGuiCol_TextDisabled, NOT_SET_OPTIONAL_COLOR); - } - Begin("Tessellation Control Shader"); - ImGui::InputText("###tess_ctrl_shader_text", TessCtrlShaderText.data(), TessCtrlShaderText.capacity(), ImGuiInputTextFlags_ReadOnly); - if (ImGui::BeginDragDropTarget()) { - if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("DND_DEMO_CELL")) { - UUID data = *(const UUID*) payload->Data; - AssetHandle handle = assets->FindAsset(data); - assert(handle.HasValue()); - if (handle->IsShader()) { - if (handle->DataAs().Type == Shader::ShaderType::TessellationControl) { - program.TessellationControlShader = data; - ShaderProgramAsset->Serialize(); - ShaderProgramAsset->ForceDeserialize(); - TessCtrlShaderText = std::format("{} (UUID: {})", assets->FindAsset(data)->File().Name(), std::to_string(data)); - } - } - } - ImGui::EndDragDropTarget(); - } - ImGui::PopStyleColor(); - ImGui::EndDisabled(); - - ImGui::SameLine(); - ImGuiIO& io = ImGui::GetIO(); - auto boldFont = io.Fonts->Fonts[1]; - ImGui::PushFont(boldFont); - float lineHeight = boldFont->FontSize + ImGui::GetStyle().FramePadding.y * 2.0f; - ImVec2 buttonSize = { lineHeight + 3.0f, lineHeight }; - - ImGui::PushStyleColor(ImGuiCol_Button, { .80f, .10f, .15f, 1 }); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, { .80f, .10f, .15f, 1 }); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, { .94f, .51f, .55f, 1 }); - if (ImGui::Button("X", buttonSize)) { - program.TessellationControlShader = 0; - ShaderProgramAsset->Serialize(); - ShaderProgramAsset->ForceDeserialize(); - TessCtrlShaderText = NOT_SET_OPTIONAL_TEXT; - } - ImGui::PopStyleColor(3); - ImGui::PopFont(); - - End(); -} -void Observer::ShaderProgramDisplay::RenderTessEvalShader(ShaderProgram& program) { - ImGui::BeginDisabled(); - if (program.HasTessellationEvaluationShader()) { - ImGui::PushStyleColor(ImGuiCol_TextDisabled, SET_COLOR); - } else { - ImGui::PushStyleColor(ImGuiCol_TextDisabled, NOT_SET_OPTIONAL_COLOR); - } - Begin("Tessellation Evaluation Shader"); - ImGui::InputText("###tess_eval_shader_text", TessEvalShaderText.data(), TessEvalShaderText.capacity(), ImGuiInputTextFlags_ReadOnly); - if (ImGui::BeginDragDropTarget()) { - if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("DND_DEMO_CELL")) { - UUID data = *(const UUID*) payload->Data; - AssetHandle handle = assets->FindAsset(data); - assert(handle.HasValue()); - if (handle->IsShader()) { - if (handle->DataAs().Type == Shader::ShaderType::TessellationEvaluation) { - program.TessellationEvaluationShader = data; - ShaderProgramAsset->Serialize(); - ShaderProgramAsset->ForceDeserialize(); - TessEvalShaderText = std::format("{} (UUID: {})", assets->FindAsset(data)->File().Name(), std::to_string(data)); - } - } - } - ImGui::EndDragDropTarget(); - } - ImGui::PopStyleColor(); - ImGui::EndDisabled(); - - ImGui::SameLine(); - ImGuiIO& io = ImGui::GetIO(); - auto boldFont = io.Fonts->Fonts[1]; - ImGui::PushFont(boldFont); - float lineHeight = boldFont->FontSize + ImGui::GetStyle().FramePadding.y * 2.0f; - ImVec2 buttonSize = { lineHeight + 3.0f, lineHeight }; - - ImGui::PushStyleColor(ImGuiCol_Button, { .80f, .10f, .15f, 1 }); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, { .80f, .10f, .15f, 1 }); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, { .94f, .51f, .55f, 1 }); - if (ImGui::Button("X", buttonSize)) { - program.TessellationEvaluationShader = 0; - ShaderProgramAsset->Serialize(); - ShaderProgramAsset->ForceDeserialize(); - TessEvalShaderText = NOT_SET_OPTIONAL_TEXT; - } - ImGui::PopStyleColor(3); - ImGui::PopFont(); - - End(); -} -void Observer::ShaderProgramDisplay::RenderGeometryShader(ShaderProgram& program) { - ImGui::BeginDisabled(); - if (program.HasGeometryShader()) { - ImGui::PushStyleColor(ImGuiCol_TextDisabled, SET_COLOR); - } else { - ImGui::PushStyleColor(ImGuiCol_TextDisabled, NOT_SET_OPTIONAL_COLOR); - } - Begin("Geometry Shader"); - ImGui::InputText("###geometry_text", GeometryShaderText.data(), GeometryShaderText.capacity(), ImGuiInputTextFlags_ReadOnly); - if (ImGui::BeginDragDropTarget()) { - if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("DND_DEMO_CELL")) { - UUID data = *(const UUID*) payload->Data; - AssetHandle handle = assets->FindAsset(data); - assert(handle.HasValue()); - if (handle->IsShader()) { - if (handle->DataAs().Type == Shader::ShaderType::Geometry) { - program.GeometryShader = data; - ShaderProgramAsset->Serialize(); - ShaderProgramAsset->ForceDeserialize(); - GeometryShaderText = std::format("{} (UUID: {})", assets->FindAsset(data)->File().Name(), std::to_string(data)); - } - } - } - ImGui::EndDragDropTarget(); - } - ImGui::PopStyleColor(); - ImGui::EndDisabled(); - - ImGui::SameLine(); - ImGuiIO& io = ImGui::GetIO(); - auto boldFont = io.Fonts->Fonts[1]; - ImGui::PushFont(boldFont); - float lineHeight = boldFont->FontSize + ImGui::GetStyle().FramePadding.y * 2.0f; - ImVec2 buttonSize = { lineHeight + 3.0f, lineHeight }; - - ImGui::PushStyleColor(ImGuiCol_Button, { .80f, .10f, .15f, 1 }); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, { .80f, .10f, .15f, 1 }); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, { .94f, .51f, .55f, 1 }); - if (ImGui::Button("X", buttonSize)) { - program.GeometryShader = 0; - ShaderProgramAsset->Serialize(); - ShaderProgramAsset->ForceDeserialize(); - GeometryShaderText = NOT_SET_OPTIONAL_TEXT; - } - ImGui::PopStyleColor(3); - ImGui::PopFont(); - - End(); -} -void Observer::ShaderProgramDisplay::RenderFragmentShader(ShaderProgram& program) { - ImGui::BeginDisabled(); - if (program.HasFragmentShader()) { - ImGui::PushStyleColor(ImGuiCol_TextDisabled, SET_COLOR); - } else { - ImGui::PushStyleColor(ImGuiCol_TextDisabled, NOT_SET_MANDATORY_COLOR); - } - Begin("Fragment Shader"); - ImGui::InputText("###fragment_shader_text", FragmentShaderText.data(), FragmentShaderText.capacity(), ImGuiInputTextFlags_ReadOnly); - if (ImGui::BeginDragDropTarget()) { - if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("DND_DEMO_CELL")) { - UUID data = *(const UUID*) payload->Data; - AssetHandle handle = assets->FindAsset(data); - assert(handle.HasValue()); - if (handle->IsShader()) { - if (handle->DataAs().Type == Shader::ShaderType::Fragment) { - program.FragmentShader = data; - ShaderProgramAsset->Serialize(); - ShaderProgramAsset->ForceDeserialize(); - FragmentShaderText = std::format("{} (UUID: {})", assets->FindAsset(data)->File().Name(), std::to_string(data)); - } - } - } - ImGui::EndDragDropTarget(); - } - ImGui::PopStyleColor(); - ImGui::EndDisabled(); - - ImGui::SameLine(); - ImGuiIO& io = ImGui::GetIO(); - auto boldFont = io.Fonts->Fonts[1]; - ImGui::PushFont(boldFont); - float lineHeight = boldFont->FontSize + ImGui::GetStyle().FramePadding.y * 2.0f; - ImVec2 buttonSize = { lineHeight + 3.0f, lineHeight }; - - ImGui::PushStyleColor(ImGuiCol_Button, { .80f, .10f, .15f, 1 }); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, { .80f, .10f, .15f, 1 }); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, { .94f, .51f, .55f, 1 }); - if (ImGui::Button("X", buttonSize)) { - program.FragmentShader = 0; - ShaderProgramAsset->Serialize(); - ShaderProgramAsset->ForceDeserialize(); - FragmentShaderText = NOT_SET_MANDATORY_TEXT; - } - ImGui::PopStyleColor(3); - ImGui::PopFont(); - - End(); -} -void Observer::ShaderProgramDisplay::End() { - ImGui::PopFont(); - ImGui::PopStyleVar(2); - ImGui::Columns(1); - ImGui::PopItemWidth(); - ImGui::PopID(); -} - -void Observer::OnReimport(Assets& assets) { - if (!std::holds_alternative(displayTarget)) { return; } - //TODO -} -void Observer::OnAssetDeleted(AssetHandle asset) { - if (std::holds_alternative(displayTarget) && - &asset->File() == std::get(displayTarget)) { - ResetDisplayTarget(); - } -} -void Observer::OnAssetFocused(AssetHandle asset) { - SetDisplayTarget(asset->File()); -} -void Observer::OnFolderFocused(FNode& folder) { - SetDisplayTarget(folder); -} -void Observer::OnFocusLost() { - ResetDisplayTarget(); -} -void Observer::OnSceneClosed() { - if (std::holds_alternative(displayTarget)) { - ResetDisplayTarget(); - } -} -void Observer::OnEntityDeleted(Entity entity) { - if (std::holds_alternative(displayTarget)) { - if (entity == std::get(displayTarget)) { - ResetDisplayTarget(); - } - } -} -void Observer::OnEntitySelected(Entity entity) { - SetDisplayTarget(entity); -} -void Observer::OnEntityDeselected() { - if (std::holds_alternative(displayTarget)) { - ResetDisplayTarget(); - } -} diff --git a/Editor/Observer.hpp b/Editor/Observer.hpp index aeace109..7b00702e 100644 --- a/Editor/Observer.hpp +++ b/Editor/Observer.hpp @@ -1,18 +1,8 @@ #pragma once -#include -#include #include -#include -#include -#include - -#include -#include - -#include -#include +#include struct GUI; struct Scene; @@ -21,10 +11,6 @@ struct Shader; struct Component; struct AssetHandle; -#define DISPLAYABLE Entity, FNode* -using DisplayTarget = std::variant; -#undef DISPLAYABLE - struct Observer { std::reference_wrapper gui; @@ -35,104 +21,8 @@ struct Observer { void Render(); void End(); - void SetDisplayTarget(Entity entity); - void SetDisplayTarget(FNode& file); - void ResetDisplayTarget(); - private: - DisplayTarget displayTarget; - - struct DisplayTargetRenderer { - static void Render(Observer& observer); - private: - static void HandleTargetWhenEmpty(); - static void HandleTargetWhenEntity(const Observer& observer, Scene& scene, const Entity entt); - static void HandleTargetWhenFile(const Observer& observer, FNode& file); - static void RenderIconChangePopup(const FNode& file, MetaAssetInfo& meta); - - static void RenderFolderView(const Observer& observer, FNode& folder); - static void RenderAssetView(const Observer& observer, AssetHandle h); - static void RenderSceneView(const Observer& observer, AssetHandle h); - static void RenderComponentDefinitionView(const Observer& observer, AssetHandle h); - static void RenderShaderView(const Observer& observer, AssetHandle h); - static void RenderShaderProgramView(const Observer& observer, AssetHandle h); - static void RenderTextureView(const Observer& observer, AssetHandle h); - static void RenderTextView(const Observer& observer, AssetHandle h); - }; - - struct SceneDisplay { - static void RenderEntities(const Observer& observer, Scene& scene); - static void RenderSystems(const Observer& observer, const Scene& scene); - }; - - struct ComponentDefinitionDisplay { - static void Init(); - static void SetDisplayTarget(const AssetHandle componentDefAsset); - - static void RenderMessagesTable(); - static void RenderFields(); - static void RenderSourceCode(); - - private: - static inline AssetHandle ComponentDefAsset{}; - static inline TextEditor TextEditorInstance{}; - }; - - struct ShaderDisplay { - static void Init(); - static void SetDisplayTarget(const AssetHandle shaderAsset); - - static void RenderMessagesTable(); - static void RenderFields(); - static void RenderSourceCode(); - - private: - static inline AssetHandle ShaderAsset{}; - static inline TextEditor TextEditorInstance{}; - }; - - struct ShaderProgramDisplay { - static void Init(); - static void SetDisplayTarget(Assets& assets, const AssetHandle shaderProgramAsset); - - static void RenderMessagesTable(); - static void RenderShaders(); - - private: - static constexpr ImVec4 NOT_SET_MANDATORY_COLOR{ 1, 0, 0, 1 }; - static constexpr ImVec4 NOT_SET_OPTIONAL_COLOR{ 1, 1, 0, 1 }; - static constexpr ImVec4 SET_COLOR{ 0, 1, 0, 1 }; - static constexpr std::string_view NOT_SET_MANDATORY_TEXT{ "NOT SET - MANDATORY!" }; - static constexpr std::string_view NOT_SET_OPTIONAL_TEXT{ "NOT SET - OPTIONAL!" }; - - static inline Assets* assets{ nullptr }; - static inline AssetHandle ShaderProgramAsset{}; - static inline std::string VertexShaderText; - static inline std::string TessCtrlShaderText; - static inline std::string TessEvalShaderText; - static inline std::string GeometryShaderText; - static inline std::string FragmentShaderText; - - static void Begin(std::string_view label); - static void RenderVertexShader(ShaderProgram& program); - static void RenderTessCtrlShader(ShaderProgram& program); - static void RenderTessEvalShader(ShaderProgram& program); - static void RenderGeometryShader(ShaderProgram& program); - static void RenderFragmentShader(ShaderProgram& program); - static void End(); - }; - - std::string renderTargetTitleText; - - void OnReimport(Assets& assets); - void OnAssetDeleted(AssetHandle asset); - void OnAssetFocused(AssetHandle asset); - void OnFolderFocused(FNode& folder); - void OnFocusLost(); - void OnSceneClosed(); - void OnEntityDeleted(Entity entity); - void OnEntitySelected(Entity entity); - void OnEntityDeselected(); + DisplayTargetRenderer displayTargetRenderer{ *this }; }; diff --git a/Editor/OutlineAttachment.cpp b/Editor/OutlineAttachment.cpp index 596f3dcf..a2dc9455 100644 --- a/Editor/OutlineAttachment.cpp +++ b/Editor/OutlineAttachment.cpp @@ -1,8 +1,8 @@ #include OutlineAttachment::OutlineAttachment(std::shared_ptr gui) noexcept : - gui(gui), - fbo() {} + gui(gui)/*, + fbo()*/ {} void OutlineAttachment::BeforeFrame(Project* project) {} void OutlineAttachment::AfterFrame(Project* project) { @@ -15,7 +15,7 @@ void OutlineAttachment::AfterFrame(Project* project) { //TransformComponent& transformComponent = scene.GetComponent(entt); //MeshComponent& meshComponent = scene.GetComponent(entt); - fbo.Bind(); + //fbo.Bind(); // Graphics::DrawMesh(meshComponent.mesh, transformComponent.GetWorldMatrix(), solidColorShader, ) - glDepthMask(GL_FALSE); + //glDepthMask(GL_FALSE); } \ No newline at end of file diff --git a/Editor/OutlineAttachment.hpp b/Editor/OutlineAttachment.hpp index 397a584e..8f5be2fb 100644 --- a/Editor/OutlineAttachment.hpp +++ b/Editor/OutlineAttachment.hpp @@ -5,7 +5,6 @@ #include #include -#include #include #include @@ -15,7 +14,7 @@ struct OutlineAttachment { std::shared_ptr gui; glm::vec4 OutlineColor{ 1.0f, 1.0f, 1.0f, 1.0f }; - FrameBuffer fbo; + //FrameBuffer fbo; std::weak_ptr solidColorShader; std::weak_ptr gaussianBlurShader; diff --git a/Editor/RotateEntityCommand.cpp b/Editor/RotateEntityCommand.cpp index 50ca88e6..98c1bf3f 100644 --- a/Editor/RotateEntityCommand.cpp +++ b/Editor/RotateEntityCommand.cpp @@ -26,10 +26,12 @@ void RotateEntityCommand::UnExecute() noexcept { cmp.SetLocalRotation(oldRotation); } -bool RotateEntityCommand::TryMergeWith(UndoRedoStack& history, const ICommand* command) noexcept { +bool RotateEntityCommand::TryMergeWith([[maybe_unused]] UndoRedoStack& history, const ICommand* command) noexcept { if (const RotateEntityCommand* other = dynamic_cast(command)) { - newRotation = other->newRotation; - return true; + if (entity == other->entity) { + newRotation = other->newRotation; + return true; + } } return false; } \ No newline at end of file diff --git a/Editor/RotateEntityCommand.hpp b/Editor/RotateEntityCommand.hpp index 0bc78dcd..14735a3c 100644 --- a/Editor/RotateEntityCommand.hpp +++ b/Editor/RotateEntityCommand.hpp @@ -12,7 +12,7 @@ struct RotateEntityCommand : GUICommand { void Execute() noexcept override; void UnExecute() noexcept override; - bool TryMergeWith(UndoRedoStack& history, const ICommand* command) noexcept override; + bool TryMergeWith([[maybe_unused]] UndoRedoStack& history, const ICommand* command) noexcept override; private: Entity entity; diff --git a/Editor/SVGPathway.cpp b/Editor/SVGPathway.cpp index 40cad119..f331cd9e 100644 --- a/Editor/SVGPathway.cpp +++ b/Editor/SVGPathway.cpp @@ -26,7 +26,7 @@ void SVGPathway::Initialize(std::filesystem::path&& directory, Color color) { Initialized = true; } -const Texture& SVGPathway::Get(const std::string& key, const TextureStyle style, const TextureSize size) { +const GPUTexture& SVGPathway::Get(const std::string& key, const TextureStyle style, const TextureSize size) { assert(Initialized); switch (style) { using enum TextureStyle; @@ -89,37 +89,60 @@ void SVGPathway::Load(std::string_view key) { static_cast(stoi(tokens[3])) }; + auto convertToSpan = [](uint8_t* ptr, size_t size) -> std::span { + return std::span(reinterpret_cast(ptr), size); + }; + { // Fill the "Textures" + // Small lunasvg::Bitmap bitmapScaledAspect; bitmapScaledAspect = lunadoc->renderToBitmap( static_cast((box[2] - box[0]) * SCALE_FACTOR_SMALL), static_cast((box[3] - box[1]) * SCALE_FACTOR_SMALL), RASTER_BG_COLOR ); - auto small = Texture::CreateTextureRaw("!!" + name + "_none_small!!", bitmapScaledAspect.data(), bitmapScaledAspect.width(), bitmapScaledAspect.height()); - + GPUTextureBuilder builder; + builder.SetName("!!" + name + "_none_small!!") + .SetWidth(bitmapScaledAspect.width()) + .SetHeight(bitmapScaledAspect.height()) + .SetData(DataFormat::RGBA8, convertToSpan(bitmapScaledAspect.data(), bitmapScaledAspect.width() * bitmapScaledAspect.height() * 4)); + auto [texSmall, _] = builder.Build(); + + // Medium bitmapScaledAspect = lunadoc->renderToBitmap( static_cast((box[2] - box[0]) * SCALE_FACTOR_MEDIUM), static_cast((box[3] - box[1]) * SCALE_FACTOR_MEDIUM), RASTER_BG_COLOR ); - auto medium = Texture::CreateTextureRaw("!!" + name + "_none_medium!!", bitmapScaledAspect.data(), bitmapScaledAspect.width(), bitmapScaledAspect.height()); + builder.SetName("!!" + name + "_none_medium!!") + .SetWidth(bitmapScaledAspect.width()) + .SetHeight(bitmapScaledAspect.height()) + .SetData(DataFormat::RGBA8, convertToSpan(bitmapScaledAspect.data(), bitmapScaledAspect.width() * bitmapScaledAspect.height() * 4)); + auto [texMedium, __] = builder.Build(); + // Large bitmapScaledAspect = lunadoc->renderToBitmap( static_cast((box[2] - box[0]) * SCALE_FACTOR_LARGE), static_cast((box[3] - box[1]) * SCALE_FACTOR_LARGE), RASTER_BG_COLOR ); - auto large = Texture::CreateTextureRaw("!!" + name + "_none_large!!", bitmapScaledAspect.data(), bitmapScaledAspect.width(), bitmapScaledAspect.height()); - - auto empty = Texture::Empty(); - if (small != empty && medium != empty && large != empty) { - Textures.try_emplace(name, std::move(small), std::move(medium), std::move(large)); + builder.SetName("!!" + name + "_none_large!!") + .SetWidth(bitmapScaledAspect.width()) + .SetHeight(bitmapScaledAspect.height()) + .SetData(DataFormat::RGBA8, convertToSpan(bitmapScaledAspect.data(), bitmapScaledAspect.width() * bitmapScaledAspect.height() * 4)); + auto [texLarge, ___] = builder.Build(); + + if (texSmall && texMedium && texLarge) { + Textures.try_emplace(name, std::move(texSmall.value()), std::move(texMedium.value()), std::move(texLarge.value())); } } { // Fill the "TexturesPadded" auto paddingFunction = [](std::unique_ptr& lunadoc, const std::vector& box, const std::string& name, std::string_view scaleFactorName, float scaleFactor) { + auto convertToSpan = [](uint32_t* ptr, size_t size) -> std::span { + return std::span(reinterpret_cast(ptr), size); + }; + lunasvg::Bitmap bitmapPadded; bitmapPadded = lunadoc->renderToBitmap( static_cast((box[2] - box[0]) * scaleFactor), @@ -132,11 +155,11 @@ void SVGPathway::Load(std::string_view key) { const size_t dimension = size_t(std::max(width, height)); uint32_t* paddedBuf = new uint32_t[dimension * dimension](); // zero initialized rgba buffer const auto* buffer = bitmapPadded.data(); - auto bufIdx = 0; + size_t bufIdx = 0; bool widthScaled = width != dimension; - for (int y = 0; y < dimension; y++) { - for (int x = 0; x < dimension; x++) { + for (size_t y = 0; y < dimension; y++) { + for (size_t x = 0; x < dimension; x++) { unsigned char* currentElement{ nullptr }; if (widthScaled) { // pad left-right @@ -162,35 +185,47 @@ void SVGPathway::Load(std::string_view key) { } } - auto tex = Texture::CreateTextureRaw("!!" + name + "_padded_" + scaleFactorName.data() + "!!", (unsigned char*) (paddedBuf), dimension, dimension); + GPUTextureBuilder builder; + builder.SetName("!!" + name + "_padded_" + scaleFactorName.data() + "!!") + .SetWidth(static_cast(dimension)) + .SetHeight(static_cast(dimension)) + .SetData(DataFormat::RGBA8, convertToSpan(paddedBuf, width * height * sizeof(uint32_t))); + auto [tex, _] = builder.Build(); + delete[] paddedBuf; - return tex; + return std::move(tex); }; - auto small = paddingFunction(lunadoc, box, name, "small", SCALE_FACTOR_SMALL); - auto medium = paddingFunction(lunadoc, box, name, "medium", SCALE_FACTOR_MEDIUM); - auto large = paddingFunction(lunadoc, box, name, "large", SCALE_FACTOR_LARGE); + auto smallTex = paddingFunction(lunadoc, box, name, "small", SCALE_FACTOR_SMALL); + auto mediumTex = paddingFunction(lunadoc, box, name, "medium", SCALE_FACTOR_MEDIUM); + auto largeTex = paddingFunction(lunadoc, box, name, "large", SCALE_FACTOR_LARGE); - auto empty = Texture::Empty(); - if (small != empty && medium != empty && large != empty) { - TexturesPadded.try_emplace(name, std::move(small), std::move(medium), std::move(large)); + if (smallTex && mediumTex && largeTex) { + TexturesPadded.try_emplace(name, std::move(smallTex.value()), std::move(mediumTex.value()), std::move(largeTex.value())); } } lunasvg::Bitmap bitmapScaled; { // Fill the "TexturesScaled" bitmapScaled = lunadoc->renderToBitmap(WIDTH, HEIGHT, RASTER_BG_COLOR); - auto tex = Texture::CreateTextureRaw("!!" + name + "_scaled!!", bitmapScaled.data(), bitmapScaled.width(), bitmapScaled.height()); - if (tex != Texture::Empty()) { + + GPUTextureBuilder builder; + builder.SetName("!!" + name + "_scaled!!") + .SetWidth(bitmapScaled.width()) + .SetHeight(bitmapScaled.height()) + .SetData(DataFormat::RGBA8, convertToSpan(bitmapScaled.data(), bitmapScaled.width() * bitmapScaled.height() * 4)); + auto [tex, _] = builder.Build(); + + if (tex) { /* we cannot copy (we can but why copy?), there is no "size" defined for scaled texture. if there were, it wouldn't make sense. "Hey SVG, pls give me A SMALL SCALED texture", what even is a SMALL SCALED texture? there is only one scaled texture and it is placed to the beginning of the pack. for future: ref. [1] */ - TexturesScaled.try_emplace(name, std::move(tex), Texture::Empty(), Texture::Empty()); + TexturesScaled.try_emplace(name, std::move(tex.value()), GPUTexture{}, GPUTexture{}); } } } -Texture& SVGPathway::TexturePack::operator[](size_t index) noexcept { +GPUTexture& SVGPathway::TexturePack::operator[](size_t index) noexcept { assert(index >= 0 && index < 3); switch(index){ case 0: return SmallTexture; diff --git a/Editor/SVGPathway.hpp b/Editor/SVGPathway.hpp index 4586a4c6..3161606e 100644 --- a/Editor/SVGPathway.hpp +++ b/Editor/SVGPathway.hpp @@ -8,7 +8,7 @@ #include #include -#include +#include enum class TextureStyle { NONE, @@ -28,15 +28,15 @@ struct SVGPathway { static void Initialize(const std::filesystem::path& directory, Color color = { 1, 1, 1, 1 }); static void Initialize(std::filesystem::path&& directory, Color color = { 1, 1, 1, 1 }); - static const Texture& Get(const std::string& key, const TextureStyle style = TextureStyle::NONE, const TextureSize size = TextureSize::MEDIUM); + static const GPUTexture& Get(const std::string& key, const TextureStyle style = TextureStyle::NONE, const TextureSize size = TextureSize::MEDIUM); private: struct TexturePack { - Texture SmallTexture; - Texture MediumTexture; - Texture LargeTexture; + GPUTexture SmallTexture; + GPUTexture MediumTexture; + GPUTexture LargeTexture; - Texture& operator[](size_t index) noexcept; + GPUTexture& operator[](size_t index) noexcept; }; static inline std::filesystem::path directory; diff --git a/Editor/SamplerDisplay.cpp b/Editor/SamplerDisplay.cpp new file mode 100644 index 00000000..2a758810 --- /dev/null +++ b/Editor/SamplerDisplay.cpp @@ -0,0 +1,171 @@ +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include + +SamplerDisplay::SamplerDisplay(Observer& observer) noexcept : + observer(observer) { + minFilterEnums.emplace_back("", 0, Prettify(nameof(TextureMinificationMode::Nearest))); + minFilterEnums.emplace_back("", 1, Prettify(nameof(TextureMinificationMode::Linear))); + minFilterEnums.emplace_back("", 2, Prettify(nameof(TextureMinificationMode::NearestMipmapNearest))); + minFilterEnums.emplace_back("", 3, Prettify(nameof(TextureMinificationMode::LinearMipmapNearest))); + minFilterEnums.emplace_back("", 4, Prettify(nameof(TextureMinificationMode::NearestMipmapLinear))); + minFilterEnums.emplace_back("", 5, Prettify(nameof(TextureMinificationMode::LinearMipmapLinear))); + + magFilterEnums.emplace_back("", 0, Prettify(nameof(TextureMagnificationMode::Nearest))); + magFilterEnums.emplace_back("", 1, Prettify(nameof(TextureMagnificationMode::Linear))); + + wrapModeEnums.emplace_back("", 0, Prettify(nameof(TextureWrappingMode::Repeat))); + wrapModeEnums.emplace_back("", 1, Prettify(nameof(TextureWrappingMode::MirroredRepeat))); + wrapModeEnums.emplace_back("", 2, Prettify(nameof(TextureWrappingMode::ClampToEdge))); + wrapModeEnums.emplace_back("", 3, Prettify(nameof(TextureWrappingMode::MirrorClampToEdge))); + wrapModeEnums.emplace_back("", 4, Prettify(nameof(TextureWrappingMode::ClampToBorder))); + + compareModeEnums.emplace_back("", 0, Prettify(nameof(TextureCompareMode::CompareRefToTexture))); + compareModeEnums.emplace_back("", 1, Prettify(nameof(TextureCompareMode::None))); + + compareFunctionEnums.emplace_back("", 0, Prettify(nameof(TextureCompareFunction::LessEqual))); + compareFunctionEnums.emplace_back("", 1, Prettify(nameof(TextureCompareFunction::GreaterEqual))); + compareFunctionEnums.emplace_back("", 2, Prettify(nameof(TextureCompareFunction::Less))); + compareFunctionEnums.emplace_back("", 3, Prettify(nameof(TextureCompareFunction::Greater))); + compareFunctionEnums.emplace_back("", 4, Prettify(nameof(TextureCompareFunction::Equal))); + compareFunctionEnums.emplace_back("", 5, Prettify(nameof(TextureCompareFunction::NotEqual))); + compareFunctionEnums.emplace_back("", 6, Prettify(nameof(TextureCompareFunction::Always))); + compareFunctionEnums.emplace_back("", 7, Prettify(nameof(TextureCompareFunction::Never))); +} + +void SamplerDisplay::SetDisplayTarget(const AssetHandle samplerAssetHandle) noexcept { + assert(samplerAssetHandle->IsSampler()); + if (samplerAsset != samplerAssetHandle) { + samplerAsset = samplerAssetHandle; + } +} +void SamplerDisplay::RenderMessagesTable() noexcept { + assert(samplerAsset.HasValue()); + if (!samplerAsset->HasErrorMessages() && + !samplerAsset->HasWarningMessages() && + !samplerAsset->HasInfoMessages()) { + return; + } + + ImGuiTableFlags flags = ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders; + + ImGui::BeginTable("compiler_logs", 2, flags); + + ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, 30); + ImGui::TableSetupColumn("Sampler Logs", ImGuiTableColumnFlags_WidthStretch); + ImGui::TableHeadersRow(); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 0, 0 }); + + ImGui::PushStyleColor(ImGuiCol_Text, ComponentDefinitionViewColors::ERROR_COLOR); + for (auto& message : samplerAsset->ErrorMessages()) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + + float r = BeginTableColumnCenterText(ComponentDefinitionViewIcons::ERROR_ICON); + ImGui::Text(ComponentDefinitionViewIcons::ERROR_ICON); + EndTableColumnCenterText(r); + + ImGui::TableSetColumnIndex(1); + + const std::string& m{ std::any_cast(message) }; + ImGui::TextWrapped("%s", m.c_str()); + } + ImGui::PopStyleColor(); + + ImGui::PushStyleColor(ImGuiCol_Text, ComponentDefinitionViewColors::WARNING_COLOR); + for (auto& message : samplerAsset->WarningMessages()) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + + float r = BeginTableColumnCenterText(ComponentDefinitionViewIcons::WARNING_ICON); + ImGui::Text(ComponentDefinitionViewIcons::WARNING_ICON); + EndTableColumnCenterText(r); + + ImGui::TableSetColumnIndex(1); + + const std::string& m{ std::any_cast(message) }; + ImGui::TextWrapped("%s", m.c_str()); + } + ImGui::PopStyleColor(); + + ImGui::PushStyleColor(ImGuiCol_Text, ComponentDefinitionViewColors::INFO_COLOR); + for (auto& message : samplerAsset->InfoMessages()) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + + float r = BeginTableColumnCenterText(ComponentDefinitionViewIcons::INFO_ICON); + ImGui::Text(ComponentDefinitionViewIcons::INFO_ICON); + EndTableColumnCenterText(r); + + ImGui::TableSetColumnIndex(1); + + const std::string& m{ std::any_cast(message) }; + ImGui::TextWrapped("%s", m.c_str()); + } + ImGui::PopStyleColor(); + + ImGui::PopStyleVar(); + ImGui::EndTable(); +} +void SamplerDisplay::RenderSamplerParameters() noexcept { + bool changed{ false }; + Sampler& sampler = samplerAsset->DataAs(); + + { // Min Filter + int minFilter{ static_cast(sampler.MinFilter) }; + changed |= EnumWidget(Prettify(nameof(Sampler::MinFilter)), minFilter, minFilterEnums); + sampler.MinFilter = static_cast(minFilter); + } + { // Mag Filter + int magFilter{ static_cast(sampler.MagFilter) }; + changed |= EnumWidget(Prettify(nameof(Sampler::MagFilter)), magFilter, magFilterEnums); + sampler.MagFilter = static_cast(magFilter); + } + + changed |=FloatWidget(Prettify(nameof(Sampler::MinLOD)), sampler.MinLOD); + changed |=FloatWidget(Prettify(nameof(Sampler::MaxLOD)), sampler.MaxLOD); + changed |=FloatWidget(Prettify(nameof(Sampler::LODBias)), sampler.LODBias); + + { // Wrap S + int wrapS{ static_cast(sampler.WrapS) }; + changed |= EnumWidget(Prettify(nameof(Sampler::WrapS)), wrapS, wrapModeEnums); + sampler.WrapS = static_cast(wrapS); + } + { // Wrap T + int wrapT{ static_cast(sampler.WrapT) }; + changed |= EnumWidget(Prettify(nameof(Sampler::WrapT)), wrapT, wrapModeEnums); + sampler.WrapT = static_cast(wrapT); + } + { // Wrap R + int wrapR{ static_cast(sampler.WrapR) }; + changed |= EnumWidget(Prettify(nameof(Sampler::WrapR)), wrapR, wrapModeEnums); + sampler.WrapR = static_cast(wrapR); + } + + changed |= ColorWidget(Prettify(nameof(Sampler::BorderColor)), sampler.BorderColor); + + { // Compare Mode + int compareMode{ static_cast(sampler.CompareMode) }; + changed |= EnumWidget(Prettify(nameof(Sampler::CompareMode)), compareMode, compareModeEnums); + sampler.CompareMode = static_cast(compareMode); + } + { // Compare Func + int compareFunc{ static_cast(sampler.CompareFunction) }; + changed |= EnumWidget(Prettify(nameof(Sampler::CompareFunction)), compareFunc, compareFunctionEnums); + sampler.CompareFunction = static_cast(compareFunc); + } + + changed |= FloatWidget(Prettify(nameof(Sampler::MaxAnisotropy)), sampler.MaxAnisotropy, 1.0f, 1.0f, 16.0f); + changed |= BoolWidget(Prettify(nameof(Sampler::CubemapSeamless)), sampler.CubemapSeamless); + + if (changed) { samplerAsset->Serialize(); } +} diff --git a/Editor/SamplerDisplay.hpp b/Editor/SamplerDisplay.hpp new file mode 100644 index 00000000..3494772a --- /dev/null +++ b/Editor/SamplerDisplay.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +#include +#include + +struct Observer; + +struct SamplerDisplay { + + explicit SamplerDisplay(Observer& observer) noexcept; + + void SetDisplayTarget(const AssetHandle samplerAssetHandle) noexcept; + void RenderMessagesTable() noexcept; + void RenderSamplerParameters() noexcept; + +private: + std::reference_wrapper observer; + + AssetHandle samplerAsset{}; + std::vector minFilterEnums{}; + std::vector magFilterEnums{}; + std::vector wrapModeEnums{}; + std::vector compareModeEnums{}; + std::vector compareFunctionEnums{}; +}; \ No newline at end of file diff --git a/Editor/ScaleEntityCommand.cpp b/Editor/ScaleEntityCommand.cpp index e2aae096..f127a5d4 100644 --- a/Editor/ScaleEntityCommand.cpp +++ b/Editor/ScaleEntityCommand.cpp @@ -26,10 +26,12 @@ void ScaleEntityCommand::UnExecute() noexcept { cmp.SetLocalScale(oldScale); } -bool ScaleEntityCommand::TryMergeWith(UndoRedoStack& history, const ICommand* command) noexcept { +bool ScaleEntityCommand::TryMergeWith([[maybe_unused]] UndoRedoStack& history, const ICommand* command) noexcept { if (const ScaleEntityCommand* other = dynamic_cast(command)) { - newScale = other->newScale; - return true; + if (entity == other->entity) { + newScale = other->newScale; + return true; + } } return false; } \ No newline at end of file diff --git a/Editor/ScaleEntityCommand.hpp b/Editor/ScaleEntityCommand.hpp index 34cc89a2..c2eeef21 100644 --- a/Editor/ScaleEntityCommand.hpp +++ b/Editor/ScaleEntityCommand.hpp @@ -11,7 +11,7 @@ struct ScaleEntityCommand : GUICommand { void Execute() noexcept override; void UnExecute() noexcept override; - bool TryMergeWith(UndoRedoStack& history, const ICommand* command) noexcept override; + bool TryMergeWith([[maybe_unused]] UndoRedoStack& history, const ICommand* command) noexcept override; private: Entity entity; diff --git a/Editor/SceneDisplay.cpp b/Editor/SceneDisplay.cpp new file mode 100644 index 00000000..f03dba67 --- /dev/null +++ b/Editor/SceneDisplay.cpp @@ -0,0 +1,91 @@ +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +SceneDisplay::SceneDisplay(Observer& observer) noexcept : + observer(observer) {} + +void SceneDisplay::RenderEntities(const Scene& scene) const noexcept { + GUI& gui = observer.get().gui; + + auto flags = ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders; + if (ImGui::BeginTable("entities", 1, flags)) { + ImGui::TableSetupColumn("Entities", ImGuiTableColumnFlags_WidthStretch); + ImGui::TableHeadersRow(); + + for (Entity entt : scene.GetAllEntites()) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + std::string label = std::to_string(entt).append("\t\t(").append(scene.GetComponent(entt).GetTag()).append(")"); + if (ImGui::CollapsingHeader(label.c_str(), ImGuiTreeNodeFlags_SpanAvailWidth)) { + std::string nameWithIcon{}; + + nameWithIcon = ComponentIcons::FindIconByComponentName(nameof(IDComponent)) + nameof(IDComponent); + ImGui::TextUnformatted(nameWithIcon.c_str()); + + nameWithIcon = ComponentIcons::FindIconByComponentName(nameof(TransformComponent)) + nameof(TransformComponent); + ImGui::TextUnformatted(nameWithIcon.c_str()); + if (scene.HasComponent(entt)) { + nameWithIcon = ComponentIcons::FindIconByComponentName(nameof(ParentComponent)) + nameof(ParentComponent); + ImGui::TextUnformatted(nameWithIcon.c_str()); + } + if (scene.HasComponent(entt)) { + nameWithIcon = ComponentIcons::FindIconByComponentName(nameof(ChildComponent)) + nameof(ChildComponent); + ImGui::TextUnformatted(nameWithIcon.c_str()); + } + if (scene.HasComponent(entt)) { + nameWithIcon = ComponentIcons::FindIconByComponentName(nameof(OrthoCameraComponent)) + nameof(OrthoCameraComponent); + ImGui::TextUnformatted(nameWithIcon.c_str()); + } + if (scene.HasComponent(entt)) { + nameWithIcon = ComponentIcons::FindIconByComponentName(nameof(PerspectiveCameraComponent)) + nameof(PerspectiveCameraComponent); + ImGui::TextUnformatted(nameWithIcon.c_str()); + } + if (scene.HasComponent(entt)) { + auto& udcs{ scene.GetComponent(entt) }; + for (const auto& [name, instance] : udcs.Components()) { + if (!instance.HasError()) { + nameWithIcon = ComponentIcons::FindIconByComponentName(name) + name; + ImGui::TextUnformatted(nameWithIcon.c_str()); + } else { + ImGui::PushStyleColor(ImGuiCol_Text, { 1.0f, 0.5f, 0.1f, 1.0f }); + + nameWithIcon = ComponentIcons::FindIconForInstantiationError(instance.GetError()) + name; + ImGui::TextUnformatted(nameWithIcon.c_str()); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("%s", instance.ErrorString().data()); + } + + ImGui::PopStyleColor(); + } + } + } + } + } + ImGui::EndTable(); + } +} + +void SceneDisplay::RenderSystems([[maybe_unused]] const Scene& scene) const noexcept { + auto flags = ImGuiTableFlags_RowBg | + ImGuiTableFlags_Borders | + ImGuiTableFlags_SizingFixedFit | + ImGuiTableFlags_NoBordersInBodyUntilResize; + if (ImGui::BeginTable("systems", 2, flags)) { + ImGui::TableSetupColumn("Systems", ImGuiTableColumnFlags_WidthStretch); + ImGui::TableSetupColumn("", ImGuiTableColumnFlags_NoResize); + ImGui::TableHeadersRow(); + + ImGui::EndTable(); + } +} diff --git a/Editor/SceneDisplay.hpp b/Editor/SceneDisplay.hpp new file mode 100644 index 00000000..d66f436e --- /dev/null +++ b/Editor/SceneDisplay.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include + +#include + +struct Observer; + +struct SceneDisplay { + + SceneDisplay(Observer& observer) noexcept; + + void RenderEntities(const Scene& scene) const noexcept; + void RenderSystems(const Scene& scene) const noexcept; + +private: + std::reference_wrapper observer; +}; \ No newline at end of file diff --git a/Editor/SceneHierarchy.cpp b/Editor/SceneHierarchy.cpp index 5f38c3fd..c5bd95ab 100644 --- a/Editor/SceneHierarchy.cpp +++ b/Editor/SceneHierarchy.cpp @@ -12,14 +12,15 @@ #include #include #include +#include #include #include #include -#include - +#include #include #include +#include SceneHierarchy::SceneHierarchy(GUI& gui) noexcept : gui(gui) { @@ -64,6 +65,26 @@ void SceneHierarchy::Render() { title.reserve(64); title.append(scene.Name); if (ImGui::CollapsingHeader(title.c_str(), ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_SpanAvailWidth)) { + if (ImGui::BeginPopupContextItem(0, ImGuiPopupFlags_MouseButtonRight)) { + UUID startupScene = gui.GetOpenProject().GetStartupScene(); + UUID thisScene = UUID::Empty(); + + const auto& assets = gui.CORE->GetAssets(); + for (const auto& uuid : assets->SceneAssetIDs()) { + AssetHandle sceneAsset = assets->FindAsset(uuid); + if (sceneAsset.Value().DataAs().Name == scene.Name) { + thisScene = uuid; + break; + } + } + + assert(thisScene != UUID::Empty()); + + if (ImGui::MenuItem(cat(SceneHierarchyIcons::ContextMenu::MAKE_STARTUP_SCENE_ICON, SceneHierarchyStrings::ContextMenu::MakeStartupScene), nullptr, false, startupScene != thisScene)) { + gui.GetOpenProject().SetStartupScene(thisScene); + } + ImGui::EndPopup(); + } if (ImGui::BeginDragDropTarget()) { auto* payload = ImGui::AcceptDragDropPayload("SELECTED_ENTT"); @@ -92,7 +113,7 @@ void SceneHierarchy::Render() { } } - if (ImGui::IsMouseDown(0) && ImGui::IsWindowHovered()) { + if (ImGui::IsMouseDown(ImGuiMouseButton_Left) && ImGui::IsWindowHovered()) { gui.Events.SceneHierarchy.OnEntityDeselected(); } @@ -183,9 +204,7 @@ void SceneHierarchy::RenderEntityNode(const Entity entity) { if (ImGui::BeginDragDropSource()) { ImGui::SetDragDropPayload("SELECTED_ENTT", &entity, sizeof(Entity)); - std::string txt; - txt.append("DragDrop - ").append(id.GetTag()); - ImGui::TextUnformatted(txt.c_str()); + ImGuiFormattedText("DragDrop - {}", id.GetTag().data()); ImGui::EndDragDropSource(); } @@ -231,8 +250,8 @@ void SceneHierarchy::RenderEntityNode(const Entity entity) { child.SetParent(parent.GetEntity()); parent.GetChildren().push_back(child.GetEntity()); } - ImGui::EndDragDropTarget(); } + ImGui::EndDragDropTarget(); } if (ImGui::IsItemHovered(ImGuiHoveredFlags_None) && ImGui::IsMouseReleased(ImGuiMouseButton_Left)) { @@ -269,6 +288,9 @@ void SceneHierarchy::RenderContextMenu(const Entity entity) { if (ImGui::MenuItem("Perspec")) { scene.EmplaceComponent(entity); } + if (ImGui::MenuItem("MMC")) { + scene.EmplaceComponent(entity); + } // cpp components end // script components start diff --git a/Editor/SceneSettings.cpp b/Editor/SceneSettings.cpp index df44a787..38badd9a 100644 --- a/Editor/SceneSettings.cpp +++ b/Editor/SceneSettings.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include SceneSettings::SceneSettings(GUI& gui) noexcept : @@ -35,14 +36,17 @@ void SceneSettings::DrawStats(Scene& scene) const { GUI& gui = this->gui; ImGui::BeginGroup(); + ImGui::TextUnformatted("TODO"); + /* auto stats = scene.GetRendererStats(); - ImGui::Text("Draw Calls: %d", stats.drawCalls); - ImGui::Text("Vertices: %d", stats.vertices); - ImGui::Text("Indices: %d", stats.indices); + ImGuiFormattedText("Draw Calls: {}", stats.drawCalls); + ImGuiFormattedText("Vertices: {}", stats.vertices); + ImGuiFormattedText("Indices: {}", stats.indices); auto fps = gui.IO()->Framerate; - ImGui::Text("Editor average %.3f ms/frame (%.1f FPS)", 1000.0f / fps, fps); + ImGuiFormattedText("Editor average {:.3f} ms/frame ({:.1f} FPS)", 1000.0f / fps, fps); ImGui::ColorEdit3("Clear Color", &scene.ClearColor.r); ImGui::ColorEdit3("Selection Outline Color", &scene.SelectionOutlineColor.r); + */ ImGui::EndGroup(); } diff --git a/Editor/SceneViewport.cpp b/Editor/SceneViewport.cpp index 83ca832d..c6c17514 100644 --- a/Editor/SceneViewport.cpp +++ b/Editor/SceneViewport.cpp @@ -1,5 +1,7 @@ #include +#include + #include #include @@ -8,7 +10,12 @@ #include #include #include -#include +#include +#include +#include +#include +#include +#include #include #include @@ -24,9 +31,146 @@ PerspectiveCamera& SceneViewport::ViewportCamera::GetPerspectiveCamera() { retur bool SceneViewport::ViewportCamera::IsOrtho() const { return activeCamera == &ortho; } bool SceneViewport::ViewportCamera::IsPerspective() const { return activeCamera == &perspective; } +GPUPipeline pipeline; +GPUShaderProgram prog; +GPUDescriptorSet perFrame; +GPUDescriptorSet perObject; +const GPUTexture* tex; +GPUSampler sampler; +GPUBuffer buf; +GPUBuffer perFrameUniformBuffer; +GPUBuffer perObjectUniformBuffer; SceneViewport::SceneViewport(GUI& gui) noexcept : gui(gui), - gizmos(*this) {} + gizmos(*this) { + + float vertices[] = { + // Back face + -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, // Bottom-left + 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, // top-right + 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, // bottom-right + 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, // top-right + -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, // bottom-left + -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, // top-left + // Front face + 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, // bottom-right + 0.5f, 0.5f, 0.5f, 1.0f, 1.0f, // top-right + -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, // bottom-left + 0.5f, 0.5f, 0.5f, 1.0f, 1.0f, // top-right + -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, // top-left + -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, // bottom-left + // Left face + -0.5f, 0.5f, 0.5f, 1.0f, 0.0f, // top-right + -0.5f, 0.5f, -0.5f, 1.0f, 1.0f, // top-left + -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, // bottom-left + -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, // bottom-left + -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, // bottom-right + -0.5f, 0.5f, 0.5f, 1.0f, 0.0f, // top-right + // Right face + 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, // top-left + 0.5f, -0.5f, -0.5f, 0.0f, 1.0f, // bottom-right + 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, // top-right + 0.5f, -0.5f, -0.5f, 0.0f, 1.0f, // bottom-right + 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, // top-left + 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, // bottom-left + // Bottom face + -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, // top-right + 0.5f, -0.5f, -0.5f, 1.0f, 1.0f, // top-left + 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, // bottom-left + 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, // bottom-left + -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, // bottom-right + -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, // top-right + // Top face + -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, // top-left + 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, // bottom-right + 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, // top-right + 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, // bottom-right + -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, // top-left + -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, // bottom-left + }; + std::byte* bytePtr = reinterpret_cast(vertices); + + GPUBufferBuilder bBuilder; + buf = bBuilder.SetStorage(std::span(bytePtr, 180 * sizeof(float))).Build().first.value(); + perFrameUniformBuffer = bBuilder.SetProperties(BufferProperties::DynamicStorage).SetStorage(sizeof(glm::mat4) * 2, nullptr).Build().first.value(); // proj and view + perObjectUniformBuffer = bBuilder.SetProperties(BufferProperties::DynamicStorage).SetStorage(sizeof(glm::mat4), nullptr).Build().first.value(); // model + GPUVertexAttribLayout layout; + layout.Define(3); + layout.Define(2); + + GPUShaderBuilder sBuilder; + auto v = sBuilder.SetType(ShaderType::Vertex).SetSourceCode(R"( +#version 460 core + +layout(location = 0) in vec3 vPos; +layout(location = 1) in vec2 vUV; + +out vec2 fUV; + +layout(std140, binding = 0) uniform ProjViewBuffer { + mat4 projection; + mat4 view; +}; + +layout(std140, binding = 1) uniform ModelBuffer { + mat4 model; +}; + +void main() { + gl_Position = projection * view * model * vec4(vPos, 1.0); + fUV = vUV; +} + )").Build().first; + auto f = sBuilder.SetType(ShaderType::Fragment).SetSourceCode(R"( +#version 460 core + +in vec2 fUV; + +layout (binding = 0) uniform sampler2D tex; + +out vec4 FragColor; + +void main() { + FragColor = texture(tex, fUV) * vec4(1,1,1,1); +} +)").Build().first; + + GPUShaderProgramBuilder spBuilder; + prog = spBuilder.SetVertexShader(v.value()).SetFragmentShader(f.value()).Build().first.value(); + + GPUPipelineBuilder aBuilder; + pipeline = aBuilder + .SetFaceCullEnabled(true) + .SetCullMode(CullMode::Back) + .SetArrayBuffer(0, buf, layout) + .SetTopology(TopologyType::Triangles) + .SetPolygonMode(PolygonMode::Fill) + .SetViewport({ 0, 0, viewportSize.Width, viewportSize.Height }) + .SetDepthTestEnabled(true) + .SetDepthWriteEnabled(true) + .SetMultisampleEnabled(true) + .SetShaderProgram(prog) + .Build().first.value(); + + tex = &Core::GetCore()->GetAssetGPUBridge()->GetTextures().Missing(); + + GPUSamplerBuilder saBuilder; + sampler = saBuilder + .SetMagnificationFilter(TextureMagnificationMode::Nearest) + .SetWrapS(TextureWrappingMode::ClampToEdge) + .SetWrapT(TextureWrappingMode::ClampToEdge) + .Build().first.value(); + + GPUDescriptorSetBuilder dsBuilder; + perFrame = dsBuilder + .SetUniformBufferBinding(0, perFrameUniformBuffer) + .Build().first.value(); + + perObject = dsBuilder + .SetUniformBufferBinding(1, perObjectUniformBuffer) + .SetCombinedImageSamplerBinding(0, *tex, sampler) + .Build().first.value(); +} bool SceneViewport::Begin() { @@ -50,11 +194,11 @@ void SceneViewport::Render() { ImGui::GetWindowPos().x + ImGui::GetCursorPos().x, ImGui::GetWindowPos().y + ImGui::GetCursorPos().y }; - ReallocBufferIfNeeded({ static_cast(ImGui::GetContentRegionAvail().x), static_cast(ImGui::GetContentRegionAvail().y) }); + ReallocBufferIfNeeded({ static_cast(ImGui::GetContentRegionAvail().x), static_cast(ImGui::GetContentRegionAvail().y) }); RenderSceneToBuffer(scene); ImVec2 size{ static_cast(viewportSize.Width), static_cast(viewportSize.Height) }; - ImGui::Image(reinterpret_cast(viewportFramebuffer->GetColorAttachment()), size, { 0, 1 }, { 1, 0 }); + ImGui::Image(std::get(viewportFramebuffer.ColorAttachments[0].value()), size, { 0, 1 }, { 1, 0 }); ImGui::PushClipRect({ viewportPosition.x, viewportPosition.y }, { viewportPosition.x + size.x, viewportPosition.y + size.y }, false); gizmos.settings.viewportSize = viewportSize; @@ -74,16 +218,96 @@ void SceneViewport::End() { SceneViewport::ViewportCamera& SceneViewport::GetViewportCamera() { return viewportCamera; } +ImVec2 SceneViewport::GetViewportCameraSettingsButtonPosition() const noexcept { return viewportCameraSettingsButtonPosition; } + void SceneViewport::ReallocBufferIfNeeded(Resolution size) { if (viewportSize == size) { return; } viewportSize = size; - viewportFramebuffer = std::move(FrameBufferBuilder().SetResolution(size).AddColorAttachment(OpenGL::RGB8).BuildUnique()); + viewportCamera.GetPerspectiveCamera().AspectRatio = size.Aspect(); + + { // Build Multisampled FB + GPURenderBufferBuilder rbBuilder; + auto&& color = rbBuilder + .SetLayout(viewportSize.Width, viewportSize.Height, DataFormat::RGBA16F) + .SetSamples(Multisample::x8) + .Build().first; + assert(color.has_value()); + + auto&& depthStencil = rbBuilder + .SetLayout(viewportSize.Width, viewportSize.Height, DataFormat::DEPTH32F_STENCIL8) + .SetSamples(Multisample::x8) + .Build().first; + assert(depthStencil.has_value()); + + GPUFrameBufferBuilder fbBuilder; + fbBuilder.SetName("NeoDoa Editor Scene Viewport Buffer (MS)") + .AttachColorRenderBuffer(std::move(color.value()), 0) + .AttachDepthStencilRenderBuffer(std::move(depthStencil.value())); + auto&& fb = fbBuilder.Build().first; + assert(fb.has_value()); + + viewportFramebufferMultisampled = std::move(fb.value()); + } + + { // Build FB + GPUTextureBuilder tBuilder; + auto&& color = tBuilder + .SetWidth(viewportSize.Width) + .SetHeight(viewportSize.Height) + .SetData(DataFormat::RGBA16F, {}) + .SetSamples(Multisample::None) + .Build().first; + assert(color.has_value()); + + GPURenderBufferBuilder rbBuilder; + auto&& depthStencil = rbBuilder.SetLayout(viewportSize.Width, viewportSize.Height, DataFormat::DEPTH32F_STENCIL8).Build().first; + assert(depthStencil.has_value()); + + GPUFrameBufferBuilder fbBuilder; + fbBuilder.SetName("NeoDoa Editor Scene Viewport Buffer") + .AttachColorTexture(std::move(color.value()), 0) + .AttachDepthStencilRenderBuffer(std::move(depthStencil.value())); + auto&& fb = fbBuilder.Build().first; + assert(fb.has_value()); + + viewportFramebuffer = std::move(fb.value()); + } + + pipeline.Viewport = { 0, 0, viewportSize.Width, viewportSize.Height }; } void SceneViewport::RenderSceneToBuffer(Scene& scene) { - viewportFramebuffer->Bind(); - scene.Update(gui.get().delta); - scene.Render(); - FrameBuffer::BackBuffer().Bind(); + //scene.Update(gui.get().delta); + //scene.Render(); + + std::array targets{ 0 }; + Graphics::SetRenderTarget(viewportFramebufferMultisampled, targets); + Graphics::BindPipeline(pipeline); + Graphics::ClearRenderTarget(viewportFramebufferMultisampled, { scene.ClearColor.r, scene.ClearColor.g, scene.ClearColor.b, scene.ClearColor.a }); + + // Bind per-frame uniform + viewportCamera.GetPerspectiveCamera().UpdateView(); + viewportCamera.GetPerspectiveCamera().UpdateProjection(); + glm::mat4 matrices[2] { + viewportCamera.GetPerspectiveCamera().GetProjectionMatrix(), + viewportCamera.GetPerspectiveCamera().GetViewMatrix() + }; + Graphics::BufferSubData(perFrameUniformBuffer, sizeof(matrices), reinterpret_cast(glm::value_ptr(matrices[0]))); + Graphics::BindDescriptorSet(perFrame); + // --- + + // Bind per-object uniform + for (const auto& e : scene.GetRegistry().view()) { + glm::mat4 model = TransformComponent::ComputeWorldMatrix(e, scene); + Graphics::BufferSubData(perObjectUniformBuffer, sizeof(model), reinterpret_cast(glm::value_ptr(model))); + Graphics::BindDescriptorSet(perObject); + + Graphics::Render(36); + } + // --- + Graphics::SetRenderTarget({}); + + std::array dst{ 0 }; + Graphics::BlitColor(viewportFramebufferMultisampled, viewportFramebuffer, 0, dst); } void SceneViewport::DrawViewportSettings(bool hasScene) { @@ -159,6 +383,7 @@ void SceneViewport::DrawViewportSettings(bool hasScene) { ImGui::SameLine(); ImGui::BeginDisabled(!gui.HasOpenScene()); + viewportCameraSettingsButtonPosition = ImGui::GetWindowPos() + ImGui::GetCursorPos(); if (ImGui::Button(WindowStrings::SceneViewportCameraSettingsWindowTitle)) { gui.GetSceneViewportCameraSettings().Show(); } @@ -200,25 +425,25 @@ void SceneViewport::DrawViewportSettings(bool hasScene) { void SceneViewport::DrawCubeControl() { auto& camera = viewportCamera.GetActiveCamera(); camera.UpdateView(); - glm::mat4 view = camera._viewMatrix; + glm::mat4 view = camera.GetViewMatrix(); ImGuizmo::SetDrawlist(); ImGuizmo::ViewManipulate(glm::value_ptr(view), 8, { viewportPosition.x + viewportSize.Width - 128 , viewportPosition.y }, { 128, 128 }, 0x10101080); - camera.forward = glm::normalize(glm::vec3(-view[0].z, -view[1].z, -view[2].z)); // forward is INVERTED!!! + camera.Forward = glm::normalize(glm::vec3(-view[0].z, -view[1].z, -view[2].z)); // forward is INVERTED!!! // don't change up vector, fuck space sims. up being something other than 0, 1, 0 is VERBOTEN! //ptr->_activeCamera->up = glm::normalize(glm::vec3(view[0].y, view[1].y, view[2].y)); - controls.yaw = glm::degrees(atan2(camera.forward.z, camera.forward.x)); - controls.pitch = glm::degrees(asin(camera.forward.y)); + controls.yaw = glm::degrees(atan2(camera.Forward.z, camera.Forward.x)); + controls.pitch = glm::degrees(asin(camera.Forward.y)); } void SceneViewport::HandleMouseControls() { GUI& gui = this->gui; auto& camera = viewportCamera.GetActiveCamera(); - glm::vec3& eye = camera.eye; - glm::vec3& forward = camera.forward; - glm::vec3& up = camera.up; - float& zoom = camera.zoom; + glm::vec3& eye = camera.Eye; + glm::vec3& forward = camera.Forward; + glm::vec3& up = camera.Up; + float& zoom = camera.Zoom; if (ImGui::IsItemHovered()) { zoom += ImGui::GetIO().MouseWheel / 100; @@ -256,16 +481,16 @@ void SceneViewport::HandleMouseControls() { } else if (viewportCamera.IsPerspective()) { glm::vec3 right = glm::normalize(glm::cross(forward, up)) * (controls.cameraSpeed * gui.delta); glm::vec3 fwd = glm::normalize(forward) * (controls.cameraSpeed * gui.delta); - if (gui.CORE->GetInput()->IsKeyPressed(KEY_W)) { + if (gui.CORE->GetInput()->IsKeyDepressed(Key::W)) { eye += fwd; } - if (gui.CORE->GetInput()->IsKeyPressed(KEY_A)) { + if (gui.CORE->GetInput()->IsKeyDepressed(Key::A)) { eye -= right; } - if (gui.CORE->GetInput()->IsKeyPressed(KEY_S)) { + if (gui.CORE->GetInput()->IsKeyDepressed(Key::S)) { eye -= fwd; } - if (gui.CORE->GetInput()->IsKeyPressed(KEY_D)) { + if (gui.CORE->GetInput()->IsKeyDepressed(Key::D)) { eye += right; } controls.yaw += delta.x; @@ -281,7 +506,7 @@ void SceneViewport::HandleMouseControls() { direction.z = sin(glm::radians(controls.yaw)) * cos(glm::radians(controls.pitch)); forward = glm::normalize(direction); } else { - assert(false); /* no camera? */ + std::unreachable(); } controls.prevDelta = v; } diff --git a/Editor/SceneViewport.hpp b/Editor/SceneViewport.hpp index 4edc4103..819f4837 100644 --- a/Editor/SceneViewport.hpp +++ b/Editor/SceneViewport.hpp @@ -9,7 +9,7 @@ #include #include -#include +#include #include @@ -47,10 +47,15 @@ struct SceneViewport { ViewportCamera& GetViewportCamera(); + ImVec2 GetViewportCameraSettingsButtonPosition() const noexcept; + private: glm::vec2 viewportPosition{}; Resolution viewportSize{}; - std::unique_ptr viewportFramebuffer{ nullptr }; + GPUFrameBuffer viewportFramebufferMultisampled; + GPUFrameBuffer viewportFramebuffer; + + ImVec2 viewportCameraSettingsButtonPosition; void ReallocBufferIfNeeded(Resolution size); void RenderSceneToBuffer(Scene& scene); diff --git a/Editor/SceneViewportCameraSettings.cpp b/Editor/SceneViewportCameraSettings.cpp index 3c3f2951..dc27244c 100644 --- a/Editor/SceneViewportCameraSettings.cpp +++ b/Editor/SceneViewportCameraSettings.cpp @@ -9,12 +9,26 @@ SceneViewportCameraSettings::SceneViewportCameraSettings(GUI& owner) noexcept : gui(owner) {} -bool SceneViewportCameraSettings::Begin() noexcept { +bool SceneViewportCameraSettings::Begin(ImVec2 position) noexcept { if (!isOpen) { return false; } + ImGui::SetNextWindowPos(position, ImGuiCond_Always); ImGui::SetNextWindowSize({ 480, 440 }, ImGuiCond_Always); ImGui::PushID(WindowStrings::SceneViewportCameraSettingsWindowName); - bool visible = ImGui::Begin(WindowStrings::SceneViewportCameraSettingsWindowTitleID, &isOpen, ImGuiWindowFlags_NoResize); + static auto flags = ImGuiWindowFlags_NoMove | + ImGuiWindowFlags_NoResize | + ImGuiWindowFlags_NoCollapse | + ImGuiWindowFlags_AlwaysAutoResize; + bool visible = ImGui::Begin(WindowStrings::SceneViewportCameraSettingsWindowTitleID, &isOpen, flags); + + if (ImGui::IsItemHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) { + visible = false; + isOpen = false; + } + + if (!isOpen) { + isClosing = true; + } return visible; } @@ -28,8 +42,9 @@ void SceneViewportCameraSettings::Render() noexcept { ImGui::PopFont(); int camSelection = viewportCamera.IsOrtho() ? 0 : 1; - /* | is intentional, || leads to invisible Perspective radio button */ - if (ImGui::RadioButton("Ortho", &camSelection, 0) | ImGui::RadioButton("Perspective", &camSelection, 1)) { + bool ortho = ImGui::RadioButton("Ortho", &camSelection, 0); + bool perspective = ImGui::RadioButton("Perspective", &camSelection, 1); + if (ortho || perspective) { if (camSelection == 0) { viewportCamera.SwitchToOrtho(); } else if (camSelection == 1) { @@ -41,29 +56,37 @@ void SceneViewportCameraSettings::Render() noexcept { if (viewportCamera.IsOrtho()) { auto& ortho = viewportCamera.GetOrthoCamera(); - FloatWidget("Left", ortho._left, 1); - FloatWidget("Right", ortho._right, 1); - FloatWidget("Bottom", ortho._bottom, 1); - FloatWidget("Top", ortho._top, 1); - FloatWidget("Near", ortho._near, 1); - FloatWidget("Far", ortho._far, 1); + FloatWidget(nameof(OrthoCamera::LeftPlane), ortho.LeftPlane, 1); + FloatWidget(nameof(OrthoCamera::RightPlane), ortho.RightPlane, 1); + FloatWidget(nameof(OrthoCamera::BottomPlane), ortho.BottomPlane, 1); + FloatWidget(nameof(OrthoCamera::TopPlane), ortho.TopPlane, 1); + FloatWidget(nameof(OrthoCamera::NearPlane), ortho.NearPlane, 1); + FloatWidget(nameof(OrthoCamera::FarPlane), ortho.FarPlane, 1); } else if (viewportCamera.IsPerspective()) { auto& perspective = viewportCamera.GetPerspectiveCamera(); - FloatWidget("FOV", perspective._fov, 1, 45, 135); - FloatWidget("Near", perspective._near, 1); - FloatWidget("Far", perspective._far, 1); + FloatWidget(nameof(PerspectiveCamera::FOV), perspective.FOV, 1, 45, 135); + FloatWidget(nameof(PerspectiveCamera::NearPlane), perspective.NearPlane, 1); + FloatWidget(nameof(PerspectiveCamera::FarPlane), perspective.FarPlane, 1); } ImGui::NewLine(); auto& active = viewportCamera.GetActiveCamera(); - FancyVector3Widget("Eye", active.eye); - FancyVector3Widget("Forward", active.forward); - FancyVector3Widget("Up", active.up); - FloatWidget("Zoom", active.zoom); + FancyVectorWidgetSettings settings = defaultFancyVectorSettingsXYZ; + settings.resetAllToSame = false; + FancyVector3Widget(nameof(ACamera::Eye), active.Eye); + settings.resetTos = { 0, 0, -1 }; + FancyVector3Widget(nameof(ACamera::Forward), active.Forward, settings); + settings.resetTos = { 0, 1, 0 }; + FancyVector3Widget(nameof(ACamera::Up), active.Up, settings); + FloatWidget(nameof(ACamera::Zoom), active.Zoom); ImGui::EndGroup(); } void SceneViewportCameraSettings::End() noexcept { + if (!isOpen && !isClosing) { return; } + + isClosing = false; + ImGui::End(); ImGui::PopID(); } diff --git a/Editor/SceneViewportCameraSettings.hpp b/Editor/SceneViewportCameraSettings.hpp index 72e067e2..a378e84a 100644 --- a/Editor/SceneViewportCameraSettings.hpp +++ b/Editor/SceneViewportCameraSettings.hpp @@ -2,6 +2,8 @@ #include +#include + struct GUI; struct SceneViewportCameraSettings { @@ -9,7 +11,7 @@ struct SceneViewportCameraSettings { explicit SceneViewportCameraSettings(GUI& owner) noexcept; - bool Begin() noexcept; + bool Begin(ImVec2 position) noexcept; void Render() noexcept; void End() noexcept; @@ -17,4 +19,5 @@ struct SceneViewportCameraSettings { void Hide() noexcept; private: bool isOpen{ false }; + bool isClosing{ false }; }; \ No newline at end of file diff --git a/Editor/ShaderDisplay.cpp b/Editor/ShaderDisplay.cpp new file mode 100644 index 00000000..be0fc06c --- /dev/null +++ b/Editor/ShaderDisplay.cpp @@ -0,0 +1,132 @@ +#include + +#include +#include + +#include +#include +#include +#include + +ShaderDisplay::ShaderDisplay(Observer& observer) noexcept : + observer(observer) { + textEditorInstance.SetColorizerEnable(true); + textEditorInstance.SetReadOnlyEnabled(true); + textEditorInstance.SetShowWhitespaces(false); + textEditorInstance.SetText(std::string(10, ' ')); + textEditorInstance.SetLanguageDefinition(TextEditor::LanguageDefinition::GLSL()); +} + +void ShaderDisplay::SetDisplayTarget(const AssetHandle shaderAssetHandle) noexcept { + assert(shaderAssetHandle->IsShader()); + if (shaderAsset != shaderAssetHandle || assetVersion < shaderAssetHandle->Version()) { + assetVersion = shaderAssetHandle->Version(); + shaderAsset = shaderAssetHandle; + if (shaderAsset->HasDeserializedData() && !shaderAsset->HasErrorMessages()) { + const auto& shader = shaderAsset->DataAs(); + textEditorInstance.SetText(shader.SourceCode); + } + } +} + +void ShaderDisplay::RenderMessagesTable() noexcept { + if (!shaderAsset->HasErrorMessages() && + !shaderAsset->HasWarningMessages() && + !shaderAsset->HasInfoMessages()) { + return; + } + + ImGuiTableFlags flags = ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders; + + ImGui::BeginTable("compiler_logs", 2, flags); + + ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, 30); + ImGui::TableSetupColumn("Compiler Logs", ImGuiTableColumnFlags_WidthStretch); + ImGui::TableHeadersRow(); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 0, 0 }); + + ImGui::PushStyleColor(ImGuiCol_Text, ComponentDefinitionViewColors::ERROR_COLOR); + for (auto& message : shaderAsset->ErrorMessages()) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + + float r = BeginTableColumnCenterText(ComponentDefinitionViewIcons::ERROR_ICON); + ImGui::Text(ComponentDefinitionViewIcons::ERROR_ICON); + EndTableColumnCenterText(r); + + ImGui::TableSetColumnIndex(1); + + const ShaderCompilerMessage& m{ std::any_cast(message) }; + ImGui::TextWrapped("%s", m.ShortMessage.c_str()); + } + ImGui::PopStyleColor(); + + ImGui::PushStyleColor(ImGuiCol_Text, ComponentDefinitionViewColors::WARNING_COLOR); + for (auto& message : shaderAsset->WarningMessages()) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + + float r = BeginTableColumnCenterText(ComponentDefinitionViewIcons::WARNING_ICON); + ImGui::Text(ComponentDefinitionViewIcons::WARNING_ICON); + EndTableColumnCenterText(r); + + ImGui::TableSetColumnIndex(1); + + const ShaderCompilerMessage& m{ std::any_cast(message) }; + ImGui::TextWrapped("%s", m.ShortMessage.c_str()); + } + ImGui::PopStyleColor(); + + ImGui::PushStyleColor(ImGuiCol_Text, ComponentDefinitionViewColors::INFO_COLOR); + for (auto& message : shaderAsset->InfoMessages()) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + + float r = BeginTableColumnCenterText(ComponentDefinitionViewIcons::INFO_ICON); + ImGui::Text(ComponentDefinitionViewIcons::INFO_ICON); + EndTableColumnCenterText(r); + + ImGui::TableSetColumnIndex(1); + + const ShaderCompilerMessage& m{ std::any_cast(message) }; + ImGui::TextWrapped("%s", m.ShortMessage.c_str()); + } + ImGui::PopStyleColor(); + + ImGui::PopStyleVar(); + ImGui::EndTable(); +} +void ShaderDisplay::RenderFields() noexcept { + /* + ImGuiTableFlags flags = ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders; + if (ImGui::CollapsingHeader(componentDef.name.c_str(), ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_DefaultOpen)) { + if (ImGui::BeginTable("component_fields", 2, flags)) { + ImGui::TableSetupColumn("Field", ImGuiTableColumnFlags_WidthStretch); + ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthStretch); + ImGui::TableHeadersRow(); + + for (auto& field : componentDef.fields) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::Text(field.name.c_str()); + ImGui::TableSetColumnIndex(1); + ImGui::Text(field.typeName.c_str()); + } + ImGui::EndTable(); + } + } + */ +} +void ShaderDisplay::RenderSourceCode() noexcept { + ImGuiTableFlags flags = ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders; + ImGui::BeginTable("source_code", 1, flags); + ImGui::TableSetupColumn("Source Code", ImGuiTableColumnFlags_WidthStretch); + ImGui::TableHeadersRow(); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + + textEditorInstance.Render("###ObserverShaderSourceCodeViewer", false, { 0.0f, -32.0f }); + + ImGui::EndTable(); +} \ No newline at end of file diff --git a/Editor/ShaderDisplay.hpp b/Editor/ShaderDisplay.hpp new file mode 100644 index 00000000..1583e900 --- /dev/null +++ b/Editor/ShaderDisplay.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include + +#include + +#include + +struct Observer; + +struct ShaderDisplay { + + ShaderDisplay(Observer& observer) noexcept; + + void SetDisplayTarget(const AssetHandle shaderAssetHandle) noexcept; + + void RenderMessagesTable() noexcept; + void RenderFields() noexcept; + void RenderSourceCode() noexcept; + +private: + std::reference_wrapper observer; + + uint64_t assetVersion{}; + AssetHandle shaderAsset{}; + TextEditor textEditorInstance{}; +}; \ No newline at end of file diff --git a/Editor/ShaderProgramDisplay.cpp b/Editor/ShaderProgramDisplay.cpp new file mode 100644 index 00000000..36a86e2c --- /dev/null +++ b/Editor/ShaderProgramDisplay.cpp @@ -0,0 +1,427 @@ +#include + +#include +#include +#include +#include + +ShaderProgramDisplay::ShaderProgramDisplay(Observer& observer) noexcept : + observer(observer) { + vertexShaderText = std::string(128, 0); + tessCtrlShaderText = std::string(128, 0); + tessEvalShaderText = std::string(128, 0); + geometryShaderText = std::string(128, 0); + fragmentShaderText = std::string(128, 0); +} +void ShaderProgramDisplay::SetDisplayTarget(Assets& assets, const AssetHandle shaderProgramAssetHandle) noexcept { + this->assets = &assets; + assert(shaderProgramAssetHandle->IsShaderProgram()); + if (shaderProgramAsset != shaderProgramAssetHandle) { + shaderProgramAsset = shaderProgramAssetHandle; + const ShaderProgram& program = shaderProgramAsset->DataAs(); + + if (program.HasVertexShader()) { + UUID uuid = program.VertexShader; + vertexShaderText = std::format("{} (UUID: {})", assets.FindAsset(uuid)->File().Name(), std::to_string(uuid)); + } else { + vertexShaderText = NOT_SET_MANDATORY_TEXT; + } + + if (program.HasTessellationControlShader()) { + UUID uuid = program.TessellationControlShader; + tessCtrlShaderText = std::format("{} (UUID: {})", assets.FindAsset(uuid)->File().Name(), std::to_string(uuid)); + } else { + tessCtrlShaderText = NOT_SET_OPTIONAL_TEXT; + } + + if (program.HasTessellationEvaluationShader()) { + UUID uuid = program.TessellationEvaluationShader; + tessEvalShaderText = std::format("{} (UUID: {})", assets.FindAsset(uuid)->File().Name(), std::to_string(uuid)); + } else { + tessEvalShaderText = NOT_SET_OPTIONAL_TEXT; + } + + if (program.HasGeometryShader()) { + UUID uuid = program.GeometryShader; + geometryShaderText = std::format("{} (UUID: {})", assets.FindAsset(uuid)->File().Name(), std::to_string(uuid)); + } else { + geometryShaderText = NOT_SET_OPTIONAL_TEXT; + } + + if (program.HasFragmentShader()) { + UUID uuid = program.FragmentShader; + fragmentShaderText = std::format("{} (UUID: {})", assets.FindAsset(uuid)->File().Name(), std::to_string(uuid)); + } else { + fragmentShaderText = NOT_SET_MANDATORY_TEXT; + } + } +} + +void ShaderProgramDisplay::RenderMessagesTable() noexcept { + if (!shaderProgramAsset->HasErrorMessages() && + !shaderProgramAsset->HasWarningMessages() && + !shaderProgramAsset->HasInfoMessages()) { + return; + } + + ImGuiTableFlags flags = ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders; + + ImGui::BeginTable("linker_logs", 2, flags); + + ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, 30); + ImGui::TableSetupColumn("Linker Logs", ImGuiTableColumnFlags_WidthStretch); + ImGui::TableHeadersRow(); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 0, 0 }); + + ImGui::PushStyleColor(ImGuiCol_Text, ComponentDefinitionViewColors::ERROR_COLOR); + for (auto& message : shaderProgramAsset->ErrorMessages()) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + + float r = BeginTableColumnCenterText(ComponentDefinitionViewIcons::ERROR_ICON); + ImGui::Text(ComponentDefinitionViewIcons::ERROR_ICON); + EndTableColumnCenterText(r); + + ImGui::TableSetColumnIndex(1); + + const std::string& m{ std::any_cast(message) }; + ImGui::TextWrapped("%s", m.c_str()); + } + ImGui::PopStyleColor(); + + ImGui::PushStyleColor(ImGuiCol_Text, ComponentDefinitionViewColors::WARNING_COLOR); + for (auto& message : shaderProgramAsset->WarningMessages()) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + + float r = BeginTableColumnCenterText(ComponentDefinitionViewIcons::WARNING_ICON); + ImGui::Text(ComponentDefinitionViewIcons::WARNING_ICON); + EndTableColumnCenterText(r); + + ImGui::TableSetColumnIndex(1); + + const std::string& m{ std::any_cast(message) }; + ImGui::TextWrapped("%s", m.c_str()); + } + ImGui::PopStyleColor(); + + ImGui::PushStyleColor(ImGuiCol_Text, ComponentDefinitionViewColors::INFO_COLOR); + for (auto& message : shaderProgramAsset->InfoMessages()) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + + float r = BeginTableColumnCenterText(ComponentDefinitionViewIcons::INFO_ICON); + ImGui::Text(ComponentDefinitionViewIcons::INFO_ICON); + EndTableColumnCenterText(r); + + ImGui::TableSetColumnIndex(1); + + const std::string& m{ std::any_cast(message) }; + ImGui::TextWrapped("%s", m.c_str()); + } + ImGui::PopStyleColor(); + + ImGui::PopStyleVar(); + ImGui::EndTable(); +} +void ShaderProgramDisplay::RenderShaders() noexcept { + ShaderProgram& program = shaderProgramAsset->DataAs(); + RenderVertexShader(program); + RenderTessCtrlShader(program); + RenderTessEvalShader(program); + RenderGeometryShader(program); + RenderFragmentShader(program); +} + +void ShaderProgramDisplay::Begin(std::string_view label) noexcept { + float w = ImGui::GetContentRegionAvail().x; + + ImGui::PushID(label.data()); + ImGui::Columns(2, nullptr, false); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { ImGui::GetStyle().ItemSpacing.x, 0 }); + ImGui::SetColumnWidth(0, 240); + ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetStyle().FramePadding.y * 0.5f + 3); + ImGui::TextDisabled("%s", label.data()); + ImGui::NextColumn(); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 0, 0 }); + ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetStyle().FramePadding.y * 0.5f); + + float buttonWidth; + { + ImGuiIO& io = ImGui::GetIO(); + auto boldFont = io.Fonts->Fonts[1]; + buttonWidth = boldFont->FontSize + ImGui::GetStyle().FramePadding.y * 2.0f; + } + + ImGui::PushItemWidth(w - 240 - buttonWidth); + ImGuiIO& io = ImGui::GetIO(); + auto font = io.Fonts->Fonts[0]; + ImGui::PushFont(font); +} +void ShaderProgramDisplay::RenderVertexShader(ShaderProgram& program) noexcept { + ImGui::BeginDisabled(); + if (program.HasVertexShader()) { + ImGui::PushStyleColor(ImGuiCol_TextDisabled, SET_COLOR); + } else { + ImGui::PushStyleColor(ImGuiCol_TextDisabled, NOT_SET_MANDATORY_COLOR); + } + Begin("Vertex Shader"); + ImGui::InputText("###vertex_shader_text", vertexShaderText.data(), vertexShaderText.capacity(), ImGuiInputTextFlags_ReadOnly); + if (ImGui::BeginDragDropTarget()) { + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("DND_DEMO_CELL")) { + UUID data = *(const UUID*) payload->Data; + AssetHandle handle = assets->FindAsset(data); + assert(handle.HasValue()); + if (handle->IsShader()) { + if (handle->DataAs().Type == ShaderType::Vertex) { + assets->TryDeleteDependencyBetween(shaderProgramAsset->ID(), program.VertexShader); + program.VertexShader = data; + assets->TryRegisterDependencyBetween(shaderProgramAsset->ID(), data); + shaderProgramAsset->Serialize(); + shaderProgramAsset->ForceDeserialize(); + vertexShaderText = std::format("{} (UUID: {})", assets->FindAsset(data)->File().Name(), std::to_string(data)); + } + } + } + ImGui::EndDragDropTarget(); + } + ImGui::PopStyleColor(); + ImGui::EndDisabled(); + + ImGui::SameLine(); + ImGuiIO& io = ImGui::GetIO(); + auto boldFont = io.Fonts->Fonts[1]; + ImGui::PushFont(boldFont); + float lineHeight = boldFont->FontSize + ImGui::GetStyle().FramePadding.y * 2.0f; + ImVec2 buttonSize = { lineHeight + 3.0f, lineHeight }; + + ImGui::PushStyleColor(ImGuiCol_Button, { .80f, .10f, .15f, 1 }); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, { .80f, .10f, .15f, 1 }); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, { .94f, .51f, .55f, 1 }); + if (ImGui::Button("X", buttonSize)) { + assets->TryDeleteDependencyBetween(shaderProgramAsset->ID(), program.VertexShader); + program.VertexShader = 0; + shaderProgramAsset->Serialize(); + shaderProgramAsset->ForceDeserialize(); + vertexShaderText = NOT_SET_MANDATORY_TEXT; + } + ImGui::PopStyleColor(3); + ImGui::PopFont(); + + End(); +} +void ShaderProgramDisplay::RenderTessCtrlShader(ShaderProgram& program) noexcept { + ImGui::BeginDisabled(); + if (program.HasTessellationControlShader()) { + ImGui::PushStyleColor(ImGuiCol_TextDisabled, SET_COLOR); + } else { + ImGui::PushStyleColor(ImGuiCol_TextDisabled, NOT_SET_OPTIONAL_COLOR); + } + Begin("Tessellation Control Shader"); + ImGui::InputText("###tess_ctrl_shader_text", tessCtrlShaderText.data(), tessCtrlShaderText.capacity(), ImGuiInputTextFlags_ReadOnly); + if (ImGui::BeginDragDropTarget()) { + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("DND_DEMO_CELL")) { + UUID data = *(const UUID*) payload->Data; + AssetHandle handle = assets->FindAsset(data); + assert(handle.HasValue()); + if (handle->IsShader()) { + if (handle->DataAs().Type == ShaderType::TessellationControl) { + assets->TryDeleteDependencyBetween(shaderProgramAsset->ID(), program.TessellationControlShader); + program.TessellationControlShader = data; + assets->TryRegisterDependencyBetween(shaderProgramAsset->ID(), data); + shaderProgramAsset->Serialize(); + shaderProgramAsset->ForceDeserialize(); + tessCtrlShaderText = std::format("{} (UUID: {})", assets->FindAsset(data)->File().Name(), std::to_string(data)); + } + } + } + ImGui::EndDragDropTarget(); + } + ImGui::PopStyleColor(); + ImGui::EndDisabled(); + + ImGui::SameLine(); + ImGuiIO& io = ImGui::GetIO(); + auto boldFont = io.Fonts->Fonts[1]; + ImGui::PushFont(boldFont); + float lineHeight = boldFont->FontSize + ImGui::GetStyle().FramePadding.y * 2.0f; + ImVec2 buttonSize = { lineHeight + 3.0f, lineHeight }; + + ImGui::PushStyleColor(ImGuiCol_Button, { .80f, .10f, .15f, 1 }); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, { .80f, .10f, .15f, 1 }); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, { .94f, .51f, .55f, 1 }); + if (ImGui::Button("X", buttonSize)) { + assets->TryDeleteDependencyBetween(shaderProgramAsset->ID(), program.TessellationControlShader); + program.TessellationControlShader = 0; + shaderProgramAsset->Serialize(); + shaderProgramAsset->ForceDeserialize(); + tessCtrlShaderText = NOT_SET_OPTIONAL_TEXT; + } + ImGui::PopStyleColor(3); + ImGui::PopFont(); + + End(); +} +void ShaderProgramDisplay::RenderTessEvalShader(ShaderProgram& program) noexcept { + ImGui::BeginDisabled(); + if (program.HasTessellationEvaluationShader()) { + ImGui::PushStyleColor(ImGuiCol_TextDisabled, SET_COLOR); + } else { + ImGui::PushStyleColor(ImGuiCol_TextDisabled, NOT_SET_OPTIONAL_COLOR); + } + Begin("Tessellation Evaluation Shader"); + ImGui::InputText("###tess_eval_shader_text", tessEvalShaderText.data(), tessEvalShaderText.capacity(), ImGuiInputTextFlags_ReadOnly); + if (ImGui::BeginDragDropTarget()) { + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("DND_DEMO_CELL")) { + UUID data = *(const UUID*) payload->Data; + AssetHandle handle = assets->FindAsset(data); + assert(handle.HasValue()); + if (handle->IsShader()) { + if (handle->DataAs().Type == ShaderType::TessellationEvaluation) { + assets->TryDeleteDependencyBetween(shaderProgramAsset->ID(), program.TessellationEvaluationShader); + program.TessellationEvaluationShader = data; + assets->TryRegisterDependencyBetween(shaderProgramAsset->ID(), data); + shaderProgramAsset->Serialize(); + shaderProgramAsset->ForceDeserialize(); + tessEvalShaderText = std::format("{} (UUID: {})", assets->FindAsset(data)->File().Name(), std::to_string(data)); + } + } + } + ImGui::EndDragDropTarget(); + } + ImGui::PopStyleColor(); + ImGui::EndDisabled(); + + ImGui::SameLine(); + ImGuiIO& io = ImGui::GetIO(); + auto boldFont = io.Fonts->Fonts[1]; + ImGui::PushFont(boldFont); + float lineHeight = boldFont->FontSize + ImGui::GetStyle().FramePadding.y * 2.0f; + ImVec2 buttonSize = { lineHeight + 3.0f, lineHeight }; + + ImGui::PushStyleColor(ImGuiCol_Button, { .80f, .10f, .15f, 1 }); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, { .80f, .10f, .15f, 1 }); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, { .94f, .51f, .55f, 1 }); + if (ImGui::Button("X", buttonSize)) { + assets->TryDeleteDependencyBetween(shaderProgramAsset->ID(), program.TessellationEvaluationShader); + program.TessellationEvaluationShader = 0; + shaderProgramAsset->Serialize(); + shaderProgramAsset->ForceDeserialize(); + tessEvalShaderText = NOT_SET_OPTIONAL_TEXT; + } + ImGui::PopStyleColor(3); + ImGui::PopFont(); + + End(); +} +void ShaderProgramDisplay::RenderGeometryShader(ShaderProgram& program) noexcept { + ImGui::BeginDisabled(); + if (program.HasGeometryShader()) { + ImGui::PushStyleColor(ImGuiCol_TextDisabled, SET_COLOR); + } else { + ImGui::PushStyleColor(ImGuiCol_TextDisabled, NOT_SET_OPTIONAL_COLOR); + } + Begin("Geometry Shader"); + ImGui::InputText("###geometry_text", geometryShaderText.data(), geometryShaderText.capacity(), ImGuiInputTextFlags_ReadOnly); + if (ImGui::BeginDragDropTarget()) { + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("DND_DEMO_CELL")) { + UUID data = *(const UUID*) payload->Data; + AssetHandle handle = assets->FindAsset(data); + assert(handle.HasValue()); + if (handle->IsShader()) { + if (handle->DataAs().Type == ShaderType::Geometry) { + assets->TryDeleteDependencyBetween(shaderProgramAsset->ID(), program.GeometryShader); + program.GeometryShader = data; + assets->TryRegisterDependencyBetween(shaderProgramAsset->ID(), data); + shaderProgramAsset->Serialize(); + shaderProgramAsset->ForceDeserialize(); + geometryShaderText = std::format("{} (UUID: {})", assets->FindAsset(data)->File().Name(), std::to_string(data)); + } + } + } + ImGui::EndDragDropTarget(); + } + ImGui::PopStyleColor(); + ImGui::EndDisabled(); + + ImGui::SameLine(); + ImGuiIO& io = ImGui::GetIO(); + auto boldFont = io.Fonts->Fonts[1]; + ImGui::PushFont(boldFont); + float lineHeight = boldFont->FontSize + ImGui::GetStyle().FramePadding.y * 2.0f; + ImVec2 buttonSize = { lineHeight + 3.0f, lineHeight }; + + ImGui::PushStyleColor(ImGuiCol_Button, { .80f, .10f, .15f, 1 }); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, { .80f, .10f, .15f, 1 }); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, { .94f, .51f, .55f, 1 }); + if (ImGui::Button("X", buttonSize)) { + assets->TryDeleteDependencyBetween(shaderProgramAsset->ID(), program.GeometryShader); + program.GeometryShader = 0; + shaderProgramAsset->Serialize(); + shaderProgramAsset->ForceDeserialize(); + geometryShaderText = NOT_SET_OPTIONAL_TEXT; + } + ImGui::PopStyleColor(3); + ImGui::PopFont(); + + End(); +} +void ShaderProgramDisplay::RenderFragmentShader(ShaderProgram& program) noexcept { + ImGui::BeginDisabled(); + if (program.HasFragmentShader()) { + ImGui::PushStyleColor(ImGuiCol_TextDisabled, SET_COLOR); + } else { + ImGui::PushStyleColor(ImGuiCol_TextDisabled, NOT_SET_MANDATORY_COLOR); + } + Begin("Fragment Shader"); + ImGui::InputText("###fragment_shader_text", fragmentShaderText.data(), fragmentShaderText.capacity(), ImGuiInputTextFlags_ReadOnly); + if (ImGui::BeginDragDropTarget()) { + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("DND_DEMO_CELL")) { + UUID data = *(const UUID*) payload->Data; + AssetHandle handle = assets->FindAsset(data); + assert(handle.HasValue()); + if (handle->IsShader()) { + if (handle->DataAs().Type == ShaderType::Fragment) { + assets->TryDeleteDependencyBetween(shaderProgramAsset->ID(), program.FragmentShader); + program.FragmentShader = data; + assets->TryRegisterDependencyBetween(shaderProgramAsset->ID(), data); + shaderProgramAsset->Serialize(); + shaderProgramAsset->ForceDeserialize(); + fragmentShaderText = std::format("{} (UUID: {})", assets->FindAsset(data)->File().Name(), std::to_string(data)); + } + } + } + ImGui::EndDragDropTarget(); + } + ImGui::PopStyleColor(); + ImGui::EndDisabled(); + + ImGui::SameLine(); + ImGuiIO& io = ImGui::GetIO(); + auto boldFont = io.Fonts->Fonts[1]; + ImGui::PushFont(boldFont); + float lineHeight = boldFont->FontSize + ImGui::GetStyle().FramePadding.y * 2.0f; + ImVec2 buttonSize = { lineHeight + 3.0f, lineHeight }; + + ImGui::PushStyleColor(ImGuiCol_Button, { .80f, .10f, .15f, 1 }); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, { .80f, .10f, .15f, 1 }); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, { .94f, .51f, .55f, 1 }); + if (ImGui::Button("X", buttonSize)) { + assets->TryDeleteDependencyBetween(shaderProgramAsset->ID(), program.FragmentShader); + program.FragmentShader = 0; + shaderProgramAsset->Serialize(); + shaderProgramAsset->ForceDeserialize(); + fragmentShaderText = NOT_SET_MANDATORY_TEXT; + } + ImGui::PopStyleColor(3); + ImGui::PopFont(); + + End(); +} +void ShaderProgramDisplay::End() noexcept { + ImGui::PopFont(); + ImGui::PopStyleVar(2); + ImGui::Columns(1); + ImGui::PopItemWidth(); + ImGui::PopID(); +} \ No newline at end of file diff --git a/Editor/ShaderProgramDisplay.hpp b/Editor/ShaderProgramDisplay.hpp new file mode 100644 index 00000000..d604faff --- /dev/null +++ b/Editor/ShaderProgramDisplay.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include +#include +#include + +#include + +#include +#include + +struct Observer; + +struct ShaderProgramDisplay { + + ShaderProgramDisplay(Observer& observer) noexcept; + + void SetDisplayTarget(Assets& assets, const AssetHandle shaderProgramAssetHandle) noexcept; + + void RenderMessagesTable() noexcept; + void RenderShaders() noexcept; + +private: + static constexpr ImVec4 NOT_SET_MANDATORY_COLOR{ 1, 0, 0, 1 }; + static constexpr ImVec4 NOT_SET_OPTIONAL_COLOR{ 1, 1, 0, 1 }; + static constexpr ImVec4 SET_COLOR{ 0, 1, 0, 1 }; + static constexpr std::string_view NOT_SET_MANDATORY_TEXT{ "NOT SET - MANDATORY!" }; + static constexpr std::string_view NOT_SET_OPTIONAL_TEXT{ "NOT SET - OPTIONAL!" }; + + std::reference_wrapper observer; + Assets* assets{ nullptr }; + AssetHandle shaderProgramAsset{}; + std::string vertexShaderText; + std::string tessCtrlShaderText; + std::string tessEvalShaderText; + std::string geometryShaderText; + std::string fragmentShaderText; + + void Begin(std::string_view label) noexcept; + void RenderVertexShader(ShaderProgram& program) noexcept; + void RenderTessCtrlShader(ShaderProgram& program) noexcept; + void RenderTessEvalShader(ShaderProgram& program) noexcept; + void RenderGeometryShader(ShaderProgram& program) noexcept; + void RenderFragmentShader(ShaderProgram& program) noexcept; + void End() noexcept; +}; \ No newline at end of file diff --git a/Editor/Strings.hpp b/Editor/Strings.hpp index 39af7406..eb7da815 100644 --- a/Editor/Strings.hpp +++ b/Editor/Strings.hpp @@ -48,11 +48,19 @@ namespace WindowStrings { inline constexpr auto SceneViewportCameraSettingsWindowTitleID{ cat(SceneViewportCameraSettingsWindowTitle.c, ImGuiIDDesignator, SceneViewportCameraSettingsWindowName) }; } +namespace ComponentWidgetStrings { + namespace ContextMenu { + inline constexpr const char Reset[]{ "Reset" }; + } +} + namespace SceneHierarchyStrings { inline constexpr const char SCENE_ICON[]{ ICON_FA_CUBES_STACKED " " }; inline constexpr const char ENTITY_ICON[]{ ICON_FA_CUBE " " }; namespace ContextMenu { + inline constexpr const char MakeStartupScene[]{ "Make Startup Scene" }; + inline constexpr const char CREATE_NEW_ENTITY_ICON[]{ ICON_FA_PLUS " " }; inline constexpr const char CLOSE_SCENE_ICON[]{ ICON_FA_SQUARE_XMARK " " }; diff --git a/Editor/TextEditor.cpp b/Editor/TextEditor.cpp index a6259bbb..3b9e0697 100644 --- a/Editor/TextEditor.cpp +++ b/Editor/TextEditor.cpp @@ -1,3 +1,12 @@ +// This is external code. Disabling warnings. +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreorder-ctor" +#pragma clang diagnostic ignored "-Wsign-compare" +#pragma clang diagnostic ignored "-Wlogical-op-parentheses" +#pragma clang diagnostic ignored "-Wunused-but-set-variable" +#endif + #include #include #include @@ -195,7 +204,7 @@ void TextEditor::DeleteRange(const Coordinates& aStart, const Coordinates& aEnd) auto& lastLine = mLines[aEnd.mLine]; if (aStart.mLine < aEnd.mLine) { - AddGlyphsToLine(aStart.mLine, firstLine.size(), lastLine.begin(), lastLine.end()); + AddGlyphsToLine(aStart.mLine, static_cast(firstLine.size()), lastLine.begin(), lastLine.end()); for (int c = 0; c <= mState.mCurrentCursor; c++) // move up cursors in line that is being moved up { if (mState.mCursors[c].mInteractiveEnd.mLine > aEnd.mLine) @@ -229,7 +238,6 @@ int TextEditor::InsertTextAt(Coordinates& /* inout */ aWhere, const char* aValue ++aValue; } else if (*aValue == '\n') { if (cindex < (int) mLines[aWhere.mLine].size()) { - auto& newLine = InsertLine(aWhere.mLine + 1); auto& line = mLines[aWhere.mLine]; AddGlyphsToLine(aWhere.mLine + 1, 0, line.begin() + cindex, line.end()); RemoveGlyphsFromLine(aWhere.mLine, cindex); @@ -242,7 +250,6 @@ int TextEditor::InsertTextAt(Coordinates& /* inout */ aWhere, const char* aValue ++totalLines; ++aValue; } else { - auto& line = mLines[aWhere.mLine]; auto d = UTF8CharLength(*aValue); while (d-- > 0 && *aValue != '\0') AddGlyphToLine(aWhere.mLine, cindex++, Glyph(*aValue++, PaletteIndex::Default)); @@ -283,11 +290,8 @@ TextEditor::Coordinates TextEditor::ScreenPosToCoordinates(const ImVec2& aPositi if (lineNo >= 0 && lineNo < (int) mLines.size()) { auto& line = mLines.at(lineNo); - int columnIndex = 0; std::string cumulatedString = ""; - float columnWidth = 0.0f; float columnX = 0.0f; - int delta = 0; // First we find the hovered column coord. for (size_t columnIndex = 0; columnIndex < line.size(); ++columnIndex) { @@ -634,7 +638,7 @@ void TextEditor::RemoveGlyphsFromLine(int aLine, int aStartChar, int aEndChar) { void TextEditor::AddGlyphsToLine(int aLine, int aTargetIndex, Line::iterator aSourceStart, Line::iterator aSourceEnd) { int targetColumn = GetCharacterColumn(aLine, aTargetIndex); - int charsInserted = std::distance(aSourceStart, aSourceEnd); + int charsInserted = static_cast(std::distance(aSourceStart, aSourceEnd)); auto& line = mLines[aLine]; OnLineChanged(true, aLine, targetColumn, charsInserted, false); line.insert(line.begin() + aTargetIndex, aSourceStart, aSourceEnd); @@ -955,7 +959,7 @@ void TextEditor::Render(bool aParentIsFocused) { } ImVec2 cursorScreenPos = ImGui::GetCursorScreenPos(); - auto scrollX = ImGui::GetScrollX(); + [[maybe_unused]] auto scrollX = ImGui::GetScrollX(); auto scrollY = ImGui::GetScrollY(); auto lineNo = (int) floor(scrollY / mCharAdvance.y); @@ -1472,7 +1476,7 @@ void TextEditor::EnterCharacter(ImWchar aChar, bool aShift) { const size_t whitespaceSize = newLine.size(); auto cindex = GetCharacterIndexR(coord); - AddGlyphsToLine(coord.mLine + 1, newLine.size(), line.begin() + cindex, line.end()); + AddGlyphsToLine(coord.mLine + 1, static_cast(newLine.size()), line.begin() + cindex, line.end()); RemoveGlyphsFromLine(coord.mLine, cindex); SetCursorPosition(Coordinates(coord.mLine + 1, GetCharacterColumn(coord.mLine + 1, (int) whitespaceSize)), c); } else { @@ -1553,7 +1557,7 @@ void TextEditor::SetCursorPosition(const Coordinates& aPosition, int aCursor, bo } } -void TextEditor::SetCursorPosition(int aLine, int aCharIndex, int aCursor, bool aClearSelection) { +void TextEditor::SetCursorPosition(int aLine, int aCharIndex, int aCursor, [[maybe_unused]] bool aClearSelection) { SetCursorPosition({ aLine, GetCharacterColumn(aLine, aCharIndex) }, aCursor); } @@ -1857,7 +1861,7 @@ void TextEditor::Paste() { clipTextLines.push_back({ i + 1, 0 }); } } - clipTextLines.back().second = clipText.length(); + clipTextLines.back().second = static_cast(clipText.length()); canPasteToMultipleCursors = clipTextLines.size() == mState.mCurrentCursor + 1; } @@ -1938,7 +1942,7 @@ void TextEditor::AddCursorForNextOccurrence() { std::string selectionText = GetText(currentCursor.GetSelectionStart(), currentCursor.GetSelectionEnd()); Coordinates nextStart, nextEnd; - if (!FindNextOccurrence(selectionText.c_str(), selectionText.length(), currentCursor.GetSelectionEnd(), nextStart, nextEnd)) + if (!FindNextOccurrence(selectionText.c_str(), static_cast(selectionText.length()), currentCursor.GetSelectionEnd(), nextStart, nextEnd)) return; mState.AddCursor(); @@ -2095,7 +2099,7 @@ void TextEditor::MergeCursorsIfPossible() { if (cursorsToDelete.find(c) != cursorsToDelete.end()) mState.mCursors.erase(mState.mCursors.begin() + c); } - mState.mCurrentCursor -= cursorsToDelete.size(); + mState.mCurrentCursor -= static_cast(cursorsToDelete.size()); } @@ -2445,7 +2449,7 @@ TextEditor::UndoRecord::UndoRecord( } void TextEditor::UndoRecord::Undo(TextEditor* aEditor) { - for (int i = mOperations.size() - 1; i > -1; i--) { + for (size_t i = mOperations.size() - 1; i > -1; i--) { const UndoOperation& operation = mOperations[i]; if (!operation.mText.empty()) { switch (operation.mType) { @@ -2494,4 +2498,8 @@ void TextEditor::UndoRecord::Redo(TextEditor* aEditor) { aEditor->mState = mAfter; aEditor->EnsureCursorVisible(); -} \ No newline at end of file +} + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif \ No newline at end of file diff --git a/Editor/TextEditor.hpp b/Editor/TextEditor.hpp index 035ad6ac..4ff7c38c 100644 --- a/Editor/TextEditor.hpp +++ b/Editor/TextEditor.hpp @@ -11,6 +11,7 @@ #include #include #include +#include class IMGUI_API TextEditor { public: diff --git a/Editor/TranslateEntityCommand.cpp b/Editor/TranslateEntityCommand.cpp index 31bb92cd..ac47e2d7 100644 --- a/Editor/TranslateEntityCommand.cpp +++ b/Editor/TranslateEntityCommand.cpp @@ -26,10 +26,12 @@ void TranslateEntityCommand::UnExecute() noexcept { cmp.SetLocalTranslation(oldTranslation); } -bool TranslateEntityCommand::TryMergeWith(UndoRedoStack& history, const ICommand* command) noexcept { +bool TranslateEntityCommand::TryMergeWith([[maybe_unused]] UndoRedoStack& history, const ICommand* command) noexcept { if (const TranslateEntityCommand* other = dynamic_cast(command)) { - newTranslation = other->newTranslation; - return true; + if (entity == other->entity) { + newTranslation = other->newTranslation; + return true; + } } return false; } \ No newline at end of file diff --git a/Editor/TranslateEntityCommand.hpp b/Editor/TranslateEntityCommand.hpp index e7c621ed..42ff58f3 100644 --- a/Editor/TranslateEntityCommand.hpp +++ b/Editor/TranslateEntityCommand.hpp @@ -11,7 +11,7 @@ struct TranslateEntityCommand : GUICommand { void Execute() noexcept override; void UnExecute() noexcept override; - bool TryMergeWith(UndoRedoStack& history, const ICommand* command) noexcept override; + bool TryMergeWith([[maybe_unused]] UndoRedoStack& history, const ICommand* command) noexcept override; private: Entity entity; diff --git a/Editor/UndoRedoHistory.cpp b/Editor/UndoRedoHistory.cpp index 0bc42e96..5825bdcc 100644 --- a/Editor/UndoRedoHistory.cpp +++ b/Editor/UndoRedoHistory.cpp @@ -19,6 +19,10 @@ bool UndoRedoHistory::Begin() noexcept { ImGui::PushID(WindowStrings::UndoRedoHistoryWindowName); bool visible = ImGui::Begin(WindowStrings::UndoRedoHistoryWindowTitleID, &isOpen); + if (!isOpen) { + isClosing = true; + } + return visible; } @@ -50,7 +54,7 @@ void UndoRedoHistory::Render() noexcept { if (isMouseCaught && ImGui::IsMouseDragging(ImGuiMouseButton_Left)) { float drag = ImGui::GetMouseDragDelta().y; float elemSize = ImGui::GetTextLineHeightWithSpacing(); - int newStackDelta = drag / elemSize; + int newStackDelta = static_cast(drag / elemSize); int diff = newStackDelta - stackDelta; while (diff > 0) { // Undo gui.UndoLastCommand(); @@ -75,6 +79,10 @@ void UndoRedoHistory::Render() noexcept { } void UndoRedoHistory::End() noexcept { + if (!isOpen && !isClosing) { return; } + + isClosing = false; + ImGui::End(); ImGui::PopID(); } diff --git a/Editor/UndoRedoHistory.hpp b/Editor/UndoRedoHistory.hpp index 154fbad0..75c72c87 100644 --- a/Editor/UndoRedoHistory.hpp +++ b/Editor/UndoRedoHistory.hpp @@ -18,6 +18,7 @@ struct UndoRedoHistory { private: bool isOpen{ false }; + bool isClosing{ false }; bool isMouseCaught{ false }; int stackDelta{}; }; \ No newline at end of file diff --git a/Editor/UserDefinedComponentStorageDeserializer.cpp b/Editor/UserDefinedComponentStorageDeserializer.cpp index 5d9854a4..247126c5 100644 --- a/Editor/UserDefinedComponentStorageDeserializer.cpp +++ b/Editor/UserDefinedComponentStorageDeserializer.cpp @@ -5,7 +5,7 @@ #include -void DeserializeUserDefinedComponentStorage(tinyxml2::XMLElement& componentNode, Scene& scene, Entity entity, const std::string& name) { +void DeserializeUserDefinedComponentStorage(tinyxml2::XMLElement& componentNode, Scene& scene, Entity entity, [[maybe_unused]] const std::string& name) { if (!scene.HasComponent(entity)) { scene.EmplaceComponent(entity); } diff --git a/Editor/UserDefinedComponentStorageDeserializer.hpp b/Editor/UserDefinedComponentStorageDeserializer.hpp index 4b891ec2..34abc458 100644 --- a/Editor/UserDefinedComponentStorageDeserializer.hpp +++ b/Editor/UserDefinedComponentStorageDeserializer.hpp @@ -8,4 +8,4 @@ #include #include -void DeserializeUserDefinedComponentStorage(tinyxml2::XMLElement& componentNode, Scene& scene, Entity entity, const std::string&); \ No newline at end of file +void DeserializeUserDefinedComponentStorage(tinyxml2::XMLElement& componentNode, Scene& scene, Entity entity, [[maybe_unused]] const std::string& name); \ No newline at end of file diff --git a/Editor/main.cpp b/Editor/main.cpp index 2d40e108..7fdd5740 100644 --- a/Editor/main.cpp +++ b/Editor/main.cpp @@ -1,11 +1,28 @@ #include +#include + #include #include #include #include +WindowIconPack::IconData LoadIcon(std::string_view path) noexcept { + WindowIconPack::IconData data; + int w, h, channels; + + stbi_uc* pixelData = stbi_load(path.data(), &w, &h, &channels, STBI_rgb_alpha); + if (pixelData) { + data.Size.Width = static_cast(w); + data.Size.Height = static_cast(h); + data.Pixels.assign(reinterpret_cast>(pixelData), + reinterpret_cast>(pixelData) + (w * h * STBI_rgb_alpha)); + stbi_image_free(pixelData); + } + return data; +} + int main(int argc, char* argv[]) { DOA_LOG_TRACE("NeoDoa Editor"); DOA_LOG_TRACE("//- CMD Arguments -//"); @@ -19,19 +36,43 @@ int main(int argc, char* argv[]) { program.add_argument("project_path").help("Absolute path to project file *.doa").metavar("PROJECT_PATH"); program.add_description("Launches NeoDoa Editor with a loaded project."); + std::string path; try { - program.parse_args(argc, argv); // Example: ./main --input_files config.yml System.xml + program.parse_args(argc, argv); + path = program.get("project_path"); } catch (const std::exception& err) { DOA_LOG_FATAL("FATAL ERROR: %s\n", err.what()); std::cerr << program << std::endl; std::exit(1); } - std::string path = program.get("project_path"); //- Parse Command Line Arguments -// + //- Setup params -// + WindowIconPack pack{}; + pack.Icons.emplace_back(LoadIcon("Images/neodoalogo-16_x_16.png")); + pack.Icons.emplace_back(LoadIcon("Images/neodoalogo-32_x_32.png")); + pack.Icons.emplace_back(LoadIcon("Images/neodoalogo-48_x_48.png")); + pack.Icons.emplace_back(LoadIcon("Images/neodoalogo-64_x_64.png")); + pack.Icons.emplace_back(LoadIcon("Images/neodoalogo-128_x_128.png")); + pack.Icons.emplace_back(LoadIcon("Images/neodoalogo-256_x_256.png")); + + ContextWindowCreationParams params { + .Size{ 2000, 2000 }, + .Title{ "NeoDoa Editor" }, + + .IsResizable{ true }, + .IsVisible{ true }, + .IsMaximized{ true }, + + .Samples{ 8 }, + + .IconPack{ pack }, + }; + //- Setup params -// + + // TODO change these to trace DOA_LOG_INFO("Allocating %d bytes...", sizeof(Core)); - const CorePtr& core = Core::CreateCore({ 2000, 2000 }, "NeoDoa Editor", false, "Images/neodoalogo", true); - core->GetWindow()->Maximize(); + const CorePtr& core = Core::CreateCore(GraphicsBackend::OpenGL4_6, WindowBackend::GLFW, params); ImGui::GetIO().IniFilename = NULL; DOA_LOG_INFO("Core dynamically allocated!"); @@ -50,5 +91,7 @@ int main(int argc, char* argv[]) { core->Start(); Core::DestroyCore(); + gui_ptr.reset(); + return 0; } \ No newline at end of file diff --git a/Engine/AABB.cpp b/Engine/AABB.cpp index e1283eba..6fd24dfa 100644 --- a/Engine/AABB.cpp +++ b/Engine/AABB.cpp @@ -59,9 +59,9 @@ Region AABB::CalcScreenSpaceCoordinates(const AABB& aabb, const glm::mat4& model min = glm::clamp(min, glm::vec2(), glm::vec2(viewport[2] - 1, viewport[3] - 1)); max = glm::clamp(max, glm::vec2(), glm::vec2(viewport[2] - 1, viewport[3] - 1)); - int x = static_cast(std::min(min.x, max.x)); - int y = static_cast(std::min(min.y, max.y)); - int w = static_cast(std::abs(max.x - min.x)); - int h = static_cast(std::abs(max.y - min.y)); + unsigned x = static_cast(std::min(min.x, max.x)); + unsigned y = static_cast(std::min(min.y, max.y)); + unsigned w = static_cast(std::abs(max.x - min.x)); + unsigned h = static_cast(std::abs(max.y - min.y)); return { x, y, w, h }; } \ No newline at end of file diff --git a/Engine/ACamera.cpp b/Engine/ACamera.cpp new file mode 100644 index 00000000..913a7f5e --- /dev/null +++ b/Engine/ACamera.cpp @@ -0,0 +1,7 @@ +#include + +ACamera::~ACamera() = default; + +const glm::mat4& ACamera::GetViewMatrix() const noexcept { return viewMatrix; } +const glm::mat4& ACamera::GetProjectionMatrix() const noexcept { return projectionMatrix; } +const glm::mat4& ACamera::GetViewProjectionMatrix() const noexcept { return viewProjectionMatrix; } \ No newline at end of file diff --git a/Engine/ACamera.hpp b/Engine/ACamera.hpp index cf4c6d2b..9895aebc 100644 --- a/Engine/ACamera.hpp +++ b/Engine/ACamera.hpp @@ -4,18 +4,23 @@ struct ACamera { - glm::vec3 eye{ 0, 0, 0 }; - glm::vec3 forward{ 0, 0, -1 }; - glm::vec3 up{ 0, 1, 0 }; - float zoom{ 1 }; - - glm::mat4 _viewMatrix; - glm::mat4 _projectionMatrix; - glm::mat4 _viewProjectionMatrix; - - virtual ~ACamera() = default; + virtual ~ACamera() = 0; virtual void UpdateView() = 0; virtual void UpdateProjection() = 0; virtual void UpdateViewProjection() = 0; + + const glm::mat4& GetViewMatrix() const noexcept; + const glm::mat4& GetProjectionMatrix() const noexcept; + const glm::mat4& GetViewProjectionMatrix() const noexcept; + + glm::vec3 Eye{ 0, 0, 0 }; + glm::vec3 Forward{ 0, 0, -1 }; + glm::vec3 Up{ 0, 1, 0 }; + float Zoom{ 1 }; + +protected: + glm::mat4 viewMatrix; + glm::mat4 projectionMatrix; + glm::mat4 viewProjectionMatrix; }; \ No newline at end of file diff --git a/Engine/Angel.cpp b/Engine/Angel.cpp index 4308feee..ffbc1c42 100644 --- a/Engine/Angel.cpp +++ b/Engine/Angel.cpp @@ -1,8 +1,5 @@ #include "Angel.hpp" -#include -#include - #include #include #include @@ -15,99 +12,9 @@ #include #include -#include -#include -#include - -#include - -#include - -#include -#include #include -#include -#include -#include -#include - -static void logTrace(const std::string& in) { CLI_LOG_TRACE("%s", in.c_str()); } -static void logTrace(int in) { CLI_LOG_TRACE("%d", in); } -static void logTrace(float in) { CLI_LOG_TRACE("%f", in); } -static void logTrace(double in) { CLI_LOG_TRACE("%f", in); } -static void logTrace(bool in) { CLI_LOG_TRACE("%s", in ? "true" : "false"); } -static void logTrace(const glm::vec2& in) { CLI_LOG_TRACE("%s", glm::to_string(in).c_str()); } -static void logTrace(const glm::vec3& in) { CLI_LOG_TRACE("%s", glm::to_string(in).c_str()); } -static void logTrace(const glm::vec4& in) { CLI_LOG_TRACE("%s", glm::to_string(in).c_str()); } - -static void logInfo(const std::string& in) { CLI_LOG_INFO("%s", in.c_str()); } -static void logInfo(int in) { CLI_LOG_INFO("%d", in); } -static void logInfo(float in) { CLI_LOG_INFO("%f", in); } -static void logInfo(double in) { CLI_LOG_INFO("%f", in); } -static void logInfo(bool in) { CLI_LOG_INFO("%s", in ? "true" : "false"); } -static void logInfo(const glm::vec2& in) { CLI_LOG_INFO("%s", glm::to_string(in).c_str()); } -static void logInfo(const glm::vec3& in) { CLI_LOG_INFO("%s", glm::to_string(in).c_str()); } -static void logInfo(const glm::vec4& in) { CLI_LOG_INFO("%s", glm::to_string(in).c_str()); } - -static void logWarning(const std::string& in) { CLI_LOG_WARNING("%s", in.c_str()); } -static void logWarning(int in) { CLI_LOG_WARNING("%d", in); } -static void logWarning(float in) { CLI_LOG_WARNING("%f", in); } -static void logWarning(double in) { CLI_LOG_WARNING("%f", in); } -static void logWarning(bool in) { CLI_LOG_WARNING("%s", in ? "true" : "false"); } -static void logWarning(const glm::vec2& in) { CLI_LOG_WARNING("%s", glm::to_string(in).c_str()); } -static void logWarning(const glm::vec3& in) { CLI_LOG_WARNING("%s", glm::to_string(in).c_str()); } -static void logWarning(const glm::vec4& in) { CLI_LOG_WARNING("%s", glm::to_string(in).c_str()); } -static void logError(const std::string& in) { CLI_LOG_ERROR("%s", in.c_str()); } -static void logError(int in) { CLI_LOG_ERROR("%d", in); } -static void logError(float in) { CLI_LOG_ERROR("%f", in); } -static void logError(double in) { CLI_LOG_ERROR("%f", in); } -static void logError(bool in) { CLI_LOG_ERROR("%s", in ? "true" : "false"); } -static void logError(const glm::vec2& in) { CLI_LOG_ERROR("%s", glm::to_string(in).c_str()); } -static void logError(const glm::vec3& in) { CLI_LOG_ERROR("%s", glm::to_string(in).c_str()); } -static void logError(const glm::vec4& in) { CLI_LOG_ERROR("%s", glm::to_string(in).c_str()); } - -static void logFatal(const std::string& in) { CLI_LOG_FATAL("%s", in.c_str()); } -static void logFatal(int in) { CLI_LOG_FATAL("%d", in); } -static void logFatal(float in) { CLI_LOG_FATAL("%f", in); } -static void logFatal(double in) { CLI_LOG_FATAL("%f", in); } -static void logFatal(bool in) { CLI_LOG_FATAL("%s", in ? "true" : "false"); } -static void logFatal(const glm::vec2& in) { CLI_LOG_FATAL("%s", glm::to_string(in).c_str()); } -static void logFatal(const glm::vec3& in) { CLI_LOG_FATAL("%s", glm::to_string(in).c_str()); } -static void logFatal(const glm::vec4& in) { CLI_LOG_FATAL("%s", glm::to_string(in).c_str()); } - -static void vec2Ctor(void* memory) { new(memory) glm::vec2(); } -static void vec2CtorXY(void* memory, float xy) { new(memory) glm::vec2(xy); } -static void vec2CtorX_Y(void* memory, float x, float y) { new(memory) glm::vec2(x, y); } -static void ivec2Ctor(void* memory) { new(memory) glm::ivec2(); } -static void ivec2CtorXY(void* memory, float xy) { new(memory) glm::ivec2(xy); } -static void ivec2CtorX_Y(void* memory, float x, float y) { new(memory) glm::ivec2(x, y); } -static void vec3Ctor(void* memory) { new(memory) glm::vec3(); } -static void vec3CtorXYZ(void* memory, float xyz) { new(memory) glm::vec3(xyz); } -static void vec3CtorX_Y_Z(void* memory, float x, float y, float z) { new(memory) glm::vec3(x, y, z); } -static void ivec3Ctor(void* memory) { new(memory) glm::ivec3(); } -static void ivec3CtorXYZ(void* memory, float xyz) { new(memory) glm::ivec3(xyz); } -static void ivec3CtorX_Y_Z(void* memory, float x, float y, float z) { new(memory) glm::ivec3(x, y, z); } -static void vec4Ctor(void* memory) { new(memory) glm::vec4(); } -static void vec4CtorXYZW(void* memory, float xy) { new(memory) glm::vec4(xy); } -static void vec4CtorX_Y_Z_W(void* memory, float x, float y, float z, float w) { new(memory) glm::vec4(x, y, z, w); } -static void ivec4Ctor(void* memory) { new(memory) glm::ivec4(); } -static void ivec4CtorXYZW(void* memory, float xy) { new(memory) glm::ivec4(xy); } -static void ivec4CtorX_Y_Z_W(void* memory, float x, float y, float z, float w) { new(memory) glm::ivec4(x, y, z, w); } -static void quatCtor(void* memory) { new(memory) glm::quat(); } -static void quatCtorVEC(void* memory, const glm::vec3& v) { new(memory) glm::quat(v); } -static void quatCtor_XYZW(void* memory, float w, float x, float y, float z) { new(memory) glm::quat(w, x, y, z); } - -static void vertexCtor(void* memory) { new(memory) Vertex(); } - -static void colorCtor(void* memory) { new(memory) Color(); } -static void colorCtorV(void* memory, float v) { new(memory) Color(v); } -static void colorCtorRGB(void* memory, float r, float g, float b) { new(memory) Color(r, g, b); } -static void colorCtorRGBA(void* memory, float r, float g, float b, float a) { new(memory) Color(r, g, b, a); } -static void colorCtorVec3(void* memory, const glm::vec3& color) { new(memory) Color(color); } -static void colorCtorVec4(void* memory, const glm::vec4& color) { new(memory) Color(color); } - -static void MessageCallback(const asSMessageInfo* msg, void* param) { +static void MessageCallback(const asSMessageInfo* msg, [[maybe_unused]] void* param) { if (msg->type == asMSGTYPE_INFORMATION) { DOA_LOG_INFO("%s (%d, %d) : %s", msg->section, msg->row, msg->col, msg->message); } else if (msg->type == asMSGTYPE_WARNING) { @@ -130,305 +37,10 @@ Angel::Angel() noexcept : RegisterScriptDateTime(_scriptEngine); // date-time _scriptEngine->SetEngineProperty(asEEngineProp::asEP_ALLOW_UNSAFE_REFERENCES, true); -#pragma region Register Globals int r; r = _scriptEngine->SetMessageCallback(asFUNCTION(MessageCallback), nullptr, asCALL_CDECL); assert(r >= 0); -#pragma region Input... - // TODO reexpose input into the scripts - /* - r = _scriptEngine->RegisterGlobalFunction("bool IsKeyPressed(int)", asFUNCTION(IsKeyPressed), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("bool IsKeyTyped(int)", asFUNCTION(IsKeyTyped), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("bool IsKeyReleased(int)", asFUNCTION(IsKeyReleased), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("bool IsMouseButtonPressed(int)", asFUNCTION(IsMouseButtonPressed), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("bool IsMouseButtonReleased(int button)", asFUNCTION(IsMouseButtonReleased), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("double GetMouseX()", asFUNCTION(GetMouseX), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("double GetMouseY()", asFUNCTION(GetMouseY), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("double GetMouseScrollX()", asFUNCTION(GetMouseScrollX), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("double GetMouseScrollY()", asFUNCTION(GetMouseScrollY), asCALL_CDECL); assert(r >= 0); - */ - r = _scriptEngine->RegisterGlobalProperty("const int KEY_UNKNOWN", &KEY_UNKNOWN); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_SPACE", &KEY_SPACE); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_APOSTROPHE", &KEY_APOSTROPHE); assert(r >= 0); /* ' */ - r = _scriptEngine->RegisterGlobalProperty("const int KEY_COMMA", &KEY_COMMA); assert(r >= 0); /* , */ - r = _scriptEngine->RegisterGlobalProperty("const int KEY_MINUS", &KEY_MINUS); assert(r >= 0); /* - */ - r = _scriptEngine->RegisterGlobalProperty("const int KEY_PERIOD", &KEY_PERIOD); assert(r >= 0); /* . */ - r = _scriptEngine->RegisterGlobalProperty("const int KEY_SLASH", &KEY_SLASH); assert(r >= 0); /* / */ - r = _scriptEngine->RegisterGlobalProperty("const int KEY_0", &KEY_0); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_1", &KEY_1); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_2", &KEY_2); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_3", &KEY_3); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_4", &KEY_4); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_5", &KEY_5); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_6", &KEY_6); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_7", &KEY_7); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_8", &KEY_8); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_9", &KEY_9); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_SEMICOLON", &KEY_SEMICOLON); assert(r >= 0); /* ; */ - r = _scriptEngine->RegisterGlobalProperty("const int KEY_EQUAL", &KEY_EQUAL); assert(r >= 0); /* = */ - r = _scriptEngine->RegisterGlobalProperty("const int KEY_A", &KEY_A); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_B", &KEY_B); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_C", &KEY_C); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_D", &KEY_D); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_E", &KEY_E); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_F", &KEY_F); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_G", &KEY_G); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_H", &KEY_H); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_I", &KEY_I); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_J", &KEY_J); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_K", &KEY_K); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_L", &KEY_L); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_M", &KEY_M); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_N", &KEY_N); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_O", &KEY_O); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_P", &KEY_P); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_Q", &KEY_Q); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_R", &KEY_R); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_S", &KEY_S); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_T", &KEY_T); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_U", &KEY_U); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_V", &KEY_V); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_W", &KEY_W); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_X", &KEY_X); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_Y", &KEY_Y); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_Z", &KEY_Z); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_LEFT_BRACKET", &KEY_LEFT_BRACKET); assert(r >= 0); /* [ */ - r = _scriptEngine->RegisterGlobalProperty("const int KEY_BACKSLASH", &KEY_BACKSLASH); assert(r >= 0); /* \ */ - r = _scriptEngine->RegisterGlobalProperty("const int KEY_RIGHT_BRACKET", &KEY_RIGHT_BRACKET); assert(r >= 0); /* ] */ - r = _scriptEngine->RegisterGlobalProperty("const int KEY_GRAVE_ACCENT", &KEY_GRAVE_ACCENT); assert(r >= 0); /* ` */ - r = _scriptEngine->RegisterGlobalProperty("const int KEY_WORLD_1", &KEY_WORLD_1); assert(r >= 0); /* non-US #1 */ - r = _scriptEngine->RegisterGlobalProperty("const int KEY_WORLD_2", &KEY_WORLD_2); assert(r >= 0); /* non-US #2 */ - r = _scriptEngine->RegisterGlobalProperty("const int KEY_ESCAPE", &KEY_ESCAPE); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_ENTER", &KEY_ENTER); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_TAB", &KEY_TAB); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_BACKSPACE", &KEY_BACKSPACE); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_INSERT", &KEY_INSERT); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_DELETE", &KEY_DELETE); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_RIGHT", &KEY_RIGHT); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_LEFT", &KEY_LEFT); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_DOWN", &KEY_DOWN); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_UP", &KEY_UP); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_PAGE_UP", &KEY_PAGE_UP); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_PAGE_DOWN", &KEY_PAGE_DOWN); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_HOME", &KEY_HOME); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_END", &KEY_END); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_CAPS_LOCK", &KEY_CAPS_LOCK); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_SCROLL_LOCK", &KEY_SCROLL_LOCK); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_NUM_LOCK", &KEY_NUM_LOCK); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_PRINT_SCREEN", &KEY_PRINT_SCREEN); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_PAUSE", &KEY_PAUSE); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_F1", &KEY_F1); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_F2", &KEY_F2); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_F3", &KEY_F3); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_F4", &KEY_F4); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_F5", &KEY_F5); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_F6", &KEY_F6); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_F7", &KEY_F7); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_F8", &KEY_F8); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_F9", &KEY_F9); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_F10", &KEY_F10); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_F11", &KEY_F11); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_F12", &KEY_F12); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_F13", &KEY_F13); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_F14", &KEY_F14); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_F15", &KEY_F15); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_F16", &KEY_F16); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_F17", &KEY_F17); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_F18", &KEY_F18); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_F19", &KEY_F19); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_F20", &KEY_F20); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_F21", &KEY_F21); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_F22", &KEY_F22); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_F23", &KEY_F23); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_F24", &KEY_F24); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_F25", &KEY_F25); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_KP_0", &KEY_KP_0); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_KP_1", &KEY_KP_1); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_KP_2", &KEY_KP_2); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_KP_3", &KEY_KP_3); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_KP_4", &KEY_KP_4); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_KP_5", &KEY_KP_5); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_KP_6", &KEY_KP_6); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_KP_7", &KEY_KP_7); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_KP_8", &KEY_KP_8); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_KP_9", &KEY_KP_9); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_KP_DECIMAL", &KEY_KP_DECIMAL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_KP_DIVIDE", &KEY_KP_DIVIDE); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_KP_MULTIPLY", &KEY_KP_MULTIPLY); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_KP_SUBTRACT", &KEY_KP_SUBTRACT); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_KP_ADD", &KEY_KP_ADD); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_KP_ENTER", &KEY_KP_ENTER); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_KP_EQUAL", &KEY_KP_EQUAL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_LEFT_SHIFT", &KEY_LEFT_SHIFT); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_LEFT_CONTROL", &KEY_LEFT_CONTROL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_LEFT_ALT", &KEY_LEFT_ALT); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_LEFT_SUPER", &KEY_LEFT_SUPER); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_RIGHT_SHIFT", &KEY_RIGHT_SHIFT); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_RIGHT_CONTROL", &KEY_RIGHT_CONTROL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_RIGHT_ALT", &KEY_RIGHT_ALT); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_RIGHT_SUPER", &KEY_RIGHT_SUPER); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int KEY_MENU", &KEY_MENU); assert(r >= 0); - - r = _scriptEngine->RegisterGlobalProperty("const int MOUSE_BUTTON_1", &MOUSE_BUTTON_1); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int MOUSE_BUTTON_2", &MOUSE_BUTTON_2); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int MOUSE_BUTTON_3", &MOUSE_BUTTON_3); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int MOUSE_BUTTON_4", &MOUSE_BUTTON_4); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int MOUSE_BUTTON_5", &MOUSE_BUTTON_5); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int MOUSE_BUTTON_6", &MOUSE_BUTTON_6); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int MOUSE_BUTTON_7", &MOUSE_BUTTON_7); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int MOUSE_BUTTON_8", &MOUSE_BUTTON_8); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int MOUSE_BUTTON_LEFT", &MOUSE_BUTTON_LEFT); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int MOUSE_BUTTON_RIGHT", &MOUSE_BUTTON_RIGHT); assert(r >= 0); - r = _scriptEngine->RegisterGlobalProperty("const int MOUSE_BUTTON_MIDDLE", &MOUSE_BUTTON_MIDDLE); assert(r >= 0); -#pragma endregion -#pragma region GLM Types - RegisterGLMVector("vec2"); - r = _scriptEngine->RegisterObjectBehaviour("vec2", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(vec2Ctor), asCALL_CDECL_OBJFIRST); assert(r >= 0); - r = _scriptEngine->RegisterObjectBehaviour("vec2", asBEHAVE_CONSTRUCT, "void f(float)", asFUNCTION(vec2CtorXY), asCALL_CDECL_OBJFIRST); assert(r >= 0); - r = _scriptEngine->RegisterObjectBehaviour("vec2", asBEHAVE_CONSTRUCT, "void f(float, float)", asFUNCTION(vec2CtorX_Y), asCALL_CDECL_OBJFIRST); assert(r >= 0); - r = _scriptEngine->RegisterObjectProperty("vec2", "float x", asOFFSET(glm::vec2, x)); assert(r >= 0); - r = _scriptEngine->RegisterObjectProperty("vec2", "float y", asOFFSET(glm::vec2, y)); assert(r >= 0); - - RegisterGLMVector("ivec2"); - r = _scriptEngine->RegisterObjectBehaviour("ivec2", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ivec2Ctor), asCALL_CDECL_OBJFIRST); assert(r >= 0); - r = _scriptEngine->RegisterObjectBehaviour("ivec2", asBEHAVE_CONSTRUCT, "void f(int)", asFUNCTION(ivec2CtorXY), asCALL_CDECL_OBJFIRST); assert(r >= 0); - r = _scriptEngine->RegisterObjectBehaviour("ivec2", asBEHAVE_CONSTRUCT, "void f(int, int)", asFUNCTION(ivec2CtorX_Y), asCALL_CDECL_OBJFIRST); assert(r >= 0); - r = _scriptEngine->RegisterObjectProperty("ivec2", "int x", asOFFSET(glm::ivec2, x)); assert(r >= 0); - r = _scriptEngine->RegisterObjectProperty("ivec2", "int y", asOFFSET(glm::ivec2, y)); assert(r >= 0); - - RegisterGLMVector("vec3"); - r = _scriptEngine->RegisterObjectBehaviour("vec3", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(vec3Ctor), asCALL_CDECL_OBJFIRST); assert(r >= 0); - r = _scriptEngine->RegisterObjectBehaviour("vec3", asBEHAVE_CONSTRUCT, "void f(float)", asFUNCTION(vec3CtorXYZ), asCALL_CDECL_OBJFIRST); assert(r >= 0); - r = _scriptEngine->RegisterObjectBehaviour("vec3", asBEHAVE_CONSTRUCT, "void f(float, float, float)", asFUNCTION(vec3CtorX_Y_Z), asCALL_CDECL_OBJFIRST); assert(r >= 0); - r = _scriptEngine->RegisterObjectProperty("vec3", "float x", asOFFSET(glm::vec3, x)); assert(r >= 0); - r = _scriptEngine->RegisterObjectProperty("vec3", "float y", asOFFSET(glm::vec3, y)); assert(r >= 0); - r = _scriptEngine->RegisterObjectProperty("vec3", "float z", asOFFSET(glm::vec3, z)); assert(r >= 0); - - RegisterGLMVector("ivec3"); - r = _scriptEngine->RegisterObjectBehaviour("ivec3", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ivec3Ctor), asCALL_CDECL_OBJFIRST); assert(r >= 0); - r = _scriptEngine->RegisterObjectBehaviour("ivec3", asBEHAVE_CONSTRUCT, "void f(int)", asFUNCTION(ivec3CtorXYZ), asCALL_CDECL_OBJFIRST); assert(r >= 0); - r = _scriptEngine->RegisterObjectBehaviour("ivec3", asBEHAVE_CONSTRUCT, "void f(int, int, int)", asFUNCTION(ivec3CtorX_Y_Z), asCALL_CDECL_OBJFIRST); assert(r >= 0); - r = _scriptEngine->RegisterObjectProperty("ivec3", "int x", asOFFSET(glm::ivec3, x)); assert(r >= 0); - r = _scriptEngine->RegisterObjectProperty("ivec3", "int y", asOFFSET(glm::ivec3, y)); assert(r >= 0); - r = _scriptEngine->RegisterObjectProperty("ivec3", "int z", asOFFSET(glm::ivec3, z)); assert(r >= 0); - - RegisterGLMVector("vec4"); - r = _scriptEngine->RegisterObjectBehaviour("vec4", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(vec4Ctor), asCALL_CDECL_OBJFIRST); assert(r >= 0); - r = _scriptEngine->RegisterObjectBehaviour("vec4", asBEHAVE_CONSTRUCT, "void f(float)", asFUNCTION(vec4CtorXYZW), asCALL_CDECL_OBJFIRST); assert(r >= 0); - r = _scriptEngine->RegisterObjectBehaviour("vec4", asBEHAVE_CONSTRUCT, "void f(float, float, float, float)", asFUNCTION(vec4CtorX_Y_Z_W), asCALL_CDECL_OBJFIRST); assert(r >= 0); - r = _scriptEngine->RegisterObjectProperty("vec4", "float x", asOFFSET(glm::vec4, x)); assert(r >= 0); - r = _scriptEngine->RegisterObjectProperty("vec4", "float y", asOFFSET(glm::vec4, y)); assert(r >= 0); - r = _scriptEngine->RegisterObjectProperty("vec4", "float z", asOFFSET(glm::vec4, z)); assert(r >= 0); - r = _scriptEngine->RegisterObjectProperty("vec4", "float w", asOFFSET(glm::vec4, w)); assert(r >= 0); - - RegisterGLMVector("ivec4"); - r = _scriptEngine->RegisterObjectBehaviour("ivec4", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ivec4Ctor), asCALL_CDECL_OBJFIRST); assert(r >= 0); - r = _scriptEngine->RegisterObjectBehaviour("ivec4", asBEHAVE_CONSTRUCT, "void f(int)", asFUNCTION(ivec4CtorXYZW), asCALL_CDECL_OBJFIRST); assert(r >= 0); - r = _scriptEngine->RegisterObjectBehaviour("ivec4", asBEHAVE_CONSTRUCT, "void f(int, int, int, int)", asFUNCTION(ivec4CtorX_Y_Z_W), asCALL_CDECL_OBJFIRST); assert(r >= 0); - r = _scriptEngine->RegisterObjectProperty("ivec4", "int x", asOFFSET(glm::ivec4, x)); assert(r >= 0); - r = _scriptEngine->RegisterObjectProperty("ivec4", "int y", asOFFSET(glm::ivec4, y)); assert(r >= 0); - r = _scriptEngine->RegisterObjectProperty("ivec4", "int z", asOFFSET(glm::ivec4, z)); assert(r >= 0); - r = _scriptEngine->RegisterObjectProperty("ivec4", "int w", asOFFSET(glm::ivec4, w)); assert(r >= 0); - - r = _scriptEngine->RegisterObjectType("quat", sizeof(glm::quat), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_CLASS_ALLFLOATS | asGetTypeTraits()); assert(r >= 0); - r = _scriptEngine->RegisterObjectBehaviour("quat", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(quatCtor), asCALL_CDECL_OBJFIRST); assert(r >= 0); - r = _scriptEngine->RegisterObjectBehaviour("quat", asBEHAVE_CONSTRUCT, "void f(float, float, float, float)", asFUNCTION(quatCtor_XYZW), asCALL_CDECL_OBJFIRST); assert(r >= 0); - r = _scriptEngine->RegisterObjectBehaviour("quat", asBEHAVE_CONSTRUCT, "void f(const vec3 &in)", asFUNCTION(quatCtorVEC), asCALL_CDECL_OBJFIRST); assert(r >= 0); - r = _scriptEngine->RegisterObjectProperty("quat", "float x", asOFFSET(glm::vec4, x)); assert(r >= 0); - r = _scriptEngine->RegisterObjectProperty("quat", "float y", asOFFSET(glm::vec4, y)); assert(r >= 0); - r = _scriptEngine->RegisterObjectProperty("quat", "float z", asOFFSET(glm::vec4, z)); assert(r >= 0); - r = _scriptEngine->RegisterObjectProperty("quat", "float w", asOFFSET(glm::vec4, w)); assert(r >= 0); - r = _scriptEngine->RegisterObjectMethod("quat", "quat& opAddAssign(const quat &in) const", asMETHODPR(glm::quat, operator+=, (const glm::quat&), glm::quat&), asCALL_THISCALL); assert(r >= 0); - r = _scriptEngine->RegisterObjectMethod("quat", "quat& opSubAssign(const quat &in) const", asMETHODPR(glm::quat, operator-=, (const glm::quat&), glm::quat&), asCALL_THISCALL); assert(r >= 0); - r = _scriptEngine->RegisterObjectMethod("quat", "quat& opMulAssign(const quat &in) const", asMETHODPR(glm::quat, operator*=, (const glm::quat&), glm::quat&), asCALL_THISCALL); assert(r >= 0); - r = _scriptEngine->RegisterObjectMethod("quat", "quat& opAssign(const quat &in) const", asMETHODPR(glm::quat, operator=, (const glm::quat&), glm::quat&), asCALL_THISCALL); assert(r >= 0); - r = _scriptEngine->RegisterObjectMethod("quat", "quat& opAdd(const quat &in) const", asFUNCTIONPR(glm::operator+, (const glm::quat&, const glm::quat&), glm::quat), asCALL_CDECL_OBJLAST); assert(r >= 0); - r = _scriptEngine->RegisterObjectMethod("quat", "quat& opSub(const quat &in) const", asFUNCTIONPR(glm::operator-, (const glm::quat&, const glm::quat&), glm::quat), asCALL_CDECL_OBJLAST); assert(r >= 0); - r = _scriptEngine->RegisterObjectMethod("quat", "quat& opMul(const quat &in) const", asFUNCTIONPR(glm::operator*, (const glm::quat&, const glm::quat&), glm::quat), asCALL_CDECL_OBJLAST); assert(r >= 0); - - r = _scriptEngine->RegisterGlobalFunction("vec2 toRadians(const vec2 &in)", asFUNCTIONPR(glm::radians, (const glm::vec2&), glm::vec2), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("vec3 toRadians(const vec3 &in)", asFUNCTIONPR(glm::radians, (const glm::vec3&), glm::vec3), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("vec4 toRadians(const vec4 &in)", asFUNCTIONPR(glm::radians, (const glm::vec4&), glm::vec4), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("float toRadians(float)", asFUNCTIONPR(glm::radians, (float), float), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("vec2 toDegrees(const vec2 &in)", asFUNCTIONPR(glm::degrees, (const glm::vec2&), glm::vec2), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("vec3 toDegrees(const vec3 &in)", asFUNCTIONPR(glm::degrees, (const glm::vec3&), glm::vec3), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("vec4 toDegrees(const vec4 &in)", asFUNCTIONPR(glm::degrees, (const glm::vec4&), glm::vec4), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("float toDegrees(float)", asFUNCTIONPR(glm::degrees, (float), float), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("vec3 eulerAngles(const quat &in)", asFUNCTIONPR(glm::eulerAngles, (const glm::quat&), glm::vec3), asCALL_CDECL); assert(r >= 0); -#pragma endregion -#pragma region Core Types - r = _scriptEngine->RegisterObjectType("Vertex", sizeof(Vertex), asOBJ_VALUE | asOBJ_POD | asGetTypeTraits()); assert(r >= 0); - r = _scriptEngine->RegisterObjectBehaviour("Vertex", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(vertexCtor), asCALL_CDECL_OBJFIRST); assert(r >= 0); - r = _scriptEngine->RegisterObjectProperty("Vertex", "vec3 position", asOFFSET(Vertex, Position)); assert(r >= 0); - r = _scriptEngine->RegisterObjectProperty("Vertex", "vec3 normal", asOFFSET(Vertex, Normal)); assert(r >= 0); - r = _scriptEngine->RegisterObjectProperty("Vertex", "vec4 color", asOFFSET(Vertex, Color)); assert(r >= 0); - r = _scriptEngine->RegisterObjectProperty("Vertex", "vec2 uv", asOFFSET(Vertex, TexCoords)); assert(r >= 0); - - r = _scriptEngine->RegisterObjectType("Color", sizeof(Color), asOBJ_VALUE | asOBJ_POD | asGetTypeTraits()); assert(r >= 0); - r = _scriptEngine->RegisterObjectBehaviour("Color", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(colorCtor), asCALL_CDECL_OBJFIRST); assert(r >= 0); - r = _scriptEngine->RegisterObjectBehaviour("Color", asBEHAVE_CONSTRUCT, "void f(float)", asFUNCTION(colorCtorV), asCALL_CDECL_OBJFIRST); assert(r >= 0); - r = _scriptEngine->RegisterObjectBehaviour("Color", asBEHAVE_CONSTRUCT, "void f(float, float, float)", asFUNCTION(colorCtorRGB), asCALL_CDECL_OBJFIRST); assert(r >= 0); - r = _scriptEngine->RegisterObjectBehaviour("Color", asBEHAVE_CONSTRUCT, "void f(float, float, float, float)", asFUNCTION(colorCtorRGBA), asCALL_CDECL_OBJFIRST); assert(r >= 0); - r = _scriptEngine->RegisterObjectBehaviour("Color", asBEHAVE_CONSTRUCT, "void f(const vec3 &in)", asFUNCTION(colorCtorVec3), asCALL_CDECL_OBJFIRST); assert(r >= 0); - r = _scriptEngine->RegisterObjectBehaviour("Color", asBEHAVE_CONSTRUCT, "void f(const vec4 &in)", asFUNCTION(colorCtorVec4), asCALL_CDECL_OBJFIRST); assert(r >= 0); - r = _scriptEngine->RegisterObjectProperty("Color", "float r", asOFFSET(Color, r)); assert(r >= 0); - r = _scriptEngine->RegisterObjectProperty("Color", "float g", asOFFSET(Color, g)); assert(r >= 0); - r = _scriptEngine->RegisterObjectProperty("Color", "float b", asOFFSET(Color, b)); assert(r >= 0); - r = _scriptEngine->RegisterObjectProperty("Color", "float a", asOFFSET(Color, a)); assert(r >= 0); -#pragma endregion -#pragma region Core Functions -#pragma endregion -#pragma region Print Functions - r = _scriptEngine->RegisterGlobalFunction("void trace(const string &in)", asFUNCTIONPR(logTrace, (const std::string&), void), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("void trace(int)", asFUNCTIONPR(logTrace, (int), void), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("void trace(float)", asFUNCTIONPR(logTrace, (float), void), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("void trace(double)", asFUNCTIONPR(logTrace, (double), void), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("void trace(bool)", asFUNCTIONPR(logTrace, (bool), void), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("void trace(const vec2 &in)", asFUNCTIONPR(logTrace, (const glm::vec2&), void), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("void trace(const vec3 &in)", asFUNCTIONPR(logTrace, (const glm::vec3&), void), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("void trace(const vec4 &in)", asFUNCTIONPR(logTrace, (const glm::vec4&), void), asCALL_CDECL); assert(r >= 0); - - r = _scriptEngine->RegisterGlobalFunction("void info(const string &in)", asFUNCTIONPR(logInfo, (const std::string&), void), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("void info(int)", asFUNCTIONPR(logInfo, (int), void), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("void info(float)", asFUNCTIONPR(logInfo, (float), void), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("void info(double)", asFUNCTIONPR(logInfo, (double), void), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("void info(bool)", asFUNCTIONPR(logInfo, (bool), void), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("void info(const vec2 &in)", asFUNCTIONPR(logInfo, (const glm::vec2&), void), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("void info(const vec3 &in)", asFUNCTIONPR(logInfo, (const glm::vec3&), void), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("void info(const vec4 &in)", asFUNCTIONPR(logInfo, (const glm::vec4&), void), asCALL_CDECL); assert(r >= 0); - - r = _scriptEngine->RegisterGlobalFunction("void warning(const string &in)", asFUNCTIONPR(logWarning, (const std::string&), void), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("void warning(int)", asFUNCTIONPR(logWarning, (int), void), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("void warning(float)", asFUNCTIONPR(logWarning, (float), void), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("void warning(double)", asFUNCTIONPR(logWarning, (double), void), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("void warning(bool)", asFUNCTIONPR(logWarning, (bool), void), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("void warning(const vec2 &in)", asFUNCTIONPR(logWarning, (const glm::vec2&), void), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("void warning(const vec3 &in)", asFUNCTIONPR(logWarning, (const glm::vec3&), void), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("void warning(const vec4 &in)", asFUNCTIONPR(logWarning, (const glm::vec4&), void), asCALL_CDECL); assert(r >= 0); - - r = _scriptEngine->RegisterGlobalFunction("void error(const string &in)", asFUNCTIONPR(logError, (const std::string&), void), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("void error(int)", asFUNCTIONPR(logError, (int), void), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("void error(float)", asFUNCTIONPR(logError, (float), void), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("void error(double)", asFUNCTIONPR(logError, (double), void), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("void error(bool)", asFUNCTIONPR(logError, (bool), void), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("void error(const vec2 &in)", asFUNCTIONPR(logError, (const glm::vec2&), void), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("void error(const vec3 &in)", asFUNCTIONPR(logError, (const glm::vec3&), void), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("void error(const vec4 &in)", asFUNCTIONPR(logError, (const glm::vec4&), void), asCALL_CDECL); assert(r >= 0); - - r = _scriptEngine->RegisterGlobalFunction("void fatal(const string &in)", asFUNCTIONPR(logFatal, (const std::string&), void), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("void fatal(int)", asFUNCTIONPR(logFatal, (int), void), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("void fatal(float)", asFUNCTIONPR(logFatal, (float), void), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("void fatal(double)", asFUNCTIONPR(logFatal, (double), void), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("void fatal(bool)", asFUNCTIONPR(logFatal, (bool), void), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("void fatal(const vec2 &in)", asFUNCTIONPR(logFatal, (const glm::vec2&), void), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("void fatal(const vec3 &in)", asFUNCTIONPR(logFatal, (const glm::vec3&), void), asCALL_CDECL); assert(r >= 0); - r = _scriptEngine->RegisterGlobalFunction("void fatal(const vec4 &in)", asFUNCTIONPR(logFatal, (const glm::vec4&), void), asCALL_CDECL); assert(r >= 0); -#pragma endregion -#pragma region Auxillary Types -#pragma endregion - - _scriptEngine->RegisterInterface("Component"); + r = _scriptEngine->RegisterInterface("Component"); assert(r >= 0); _componentTypeInfo = _scriptEngine->GetTypeInfoByName("Component"); -#pragma endregion _scriptCtx = _scriptEngine->CreateContext(); } @@ -438,7 +50,6 @@ Angel::~Angel() noexcept { _scriptEngine->ShutDownAndRelease(); } - asIScriptEngine& Angel::ScriptEngine() { return *_scriptEngine; } CScriptBuilder& Angel::ScriptBuilder() { return _scriptBuilder; } bool Angel::IsComponentDefinition(asITypeInfo* typeInfo) const { return typeInfo->Implements(_componentTypeInfo); } diff --git a/Engine/Angel.hpp b/Engine/Angel.hpp index fbab93df..69680502 100644 --- a/Engine/Angel.hpp +++ b/Engine/Angel.hpp @@ -29,21 +29,5 @@ struct Angel { Angel& operator=(const Angel&) = delete; Angel& operator=(const Angel&&) = delete; - template - void RegisterGLMVector(const char* typeName) { - std::string t{ typeName }; - static int r; - r = _scriptEngine->RegisterObjectType(typeName, sizeof(T), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_CLASS_ALLFLOATS | asGetTypeTraits()); assert(r >= 0); - r = _scriptEngine->RegisterObjectMethod(typeName, std::string(t + "& opAddAssign(const " + t + " &in) const").c_str(), asMETHODPR(T, operator+=, (const T&), T&), asCALL_THISCALL); assert(r >= 0); - r = _scriptEngine->RegisterObjectMethod(typeName, std::string(t + "& opSubAssign(const " + t + " &in) const").c_str(), asMETHODPR(T, operator-=, (const T&), T&), asCALL_THISCALL); assert(r >= 0); - r = _scriptEngine->RegisterObjectMethod(typeName, std::string(t + "& opMulAssign(const " + t + " &in) const").c_str(), asMETHODPR(T, operator*=, (const T&), T&), asCALL_THISCALL); assert(r >= 0); - r = _scriptEngine->RegisterObjectMethod(typeName, std::string(t + "& opDivAssign(const " + t + " &in) const").c_str(), asMETHODPR(T, operator/=, (const T&), T&), asCALL_THISCALL); assert(r >= 0); - r = _scriptEngine->RegisterObjectMethod(typeName, std::string(t + "& opAssign(const " + t + " &in) const").c_str(), asMETHODPR(T, operator=, (const T&), T&), asCALL_THISCALL); assert(r >= 0); - r = _scriptEngine->RegisterObjectMethod(typeName, std::string(t + "& opAdd(const " + t + "& in) const").c_str(), asFUNCTIONPR(glm::operator+, (const T&, const T&), T), asCALL_CDECL_OBJLAST); assert(r >= 0); - r = _scriptEngine->RegisterObjectMethod(typeName, std::string(t + "& opSub(const " + t + "& in) const").c_str(), asFUNCTIONPR(glm::operator-, (const T&, const T&), T), asCALL_CDECL_OBJLAST); assert(r >= 0); - r = _scriptEngine->RegisterObjectMethod(typeName, std::string(t + "& opMul(const " + t + "& in) const").c_str(), asFUNCTIONPR(glm::operator*, (const T&, const T&), T), asCALL_CDECL_OBJLAST); assert(r >= 0); - r = _scriptEngine->RegisterObjectMethod(typeName, std::string(t + "& opDiv(const " + t + "& in) const").c_str(), asFUNCTIONPR(glm::operator/, (const T&, const T&), T), asCALL_CDECL_OBJLAST); assert(r >= 0); - } - asITypeInfo* _componentTypeInfo; }; diff --git a/Engine/Asset.cpp b/Engine/Asset.cpp index e08d4745..6aab676a 100644 --- a/Engine/Asset.cpp +++ b/Engine/Asset.cpp @@ -1,15 +1,20 @@ #include +#include #include #include #include #include #include +#include +#include +#include +#include #include #include #include -#include -#include +#include +#include Asset::Asset() noexcept : Asset(UUID::Empty(), nullptr) {} @@ -19,7 +24,7 @@ Asset::Asset(const UUID id, FNode* file) noexcept : file(file) {} Asset::~Asset() noexcept { NotifyObservers("destructed"_hs); } Asset::Asset(Asset&& other) noexcept : ObserverPattern::Observable(std::move(other)), - id(std::move(other.id)), + id(std::exchange(other.id, UUID::Empty())), file(std::exchange(other.file, nullptr)), data(std::move(other.data)), version(std::exchange(other.version, 0)), @@ -30,7 +35,7 @@ Asset::Asset(Asset&& other) noexcept : ObserverPattern::Observable(std::move(oth } Asset& Asset::operator=(Asset&& other) noexcept { ObserverPattern::Observable::operator=(std::move(other)); - id = std::move(other.id); + id = std::exchange(other.id, UUID::Empty()); file = std::exchange(other.file, nullptr); DeleteDeserializedData(); data.swap(other.data); @@ -56,9 +61,24 @@ void Asset::Serialize() { doc.SaveFile(file->AbsolutePath().string().c_str()); return; } + if (IsSampler()) { + std::string serializedData; + serializedData = SerializeSampler(DataAs()); + file->ModifyContent(std::move(serializedData)); + file->DisposeContent(); + } if (IsTexture()) { - SerializeTexture(DataAs()); - // TODO WRITE TO FILE + EncodedTextureData serializedData; + serializedData = SerializeTexture(DataAs(), ExtToEncoding(file->Extension())); + + std::string stringified; + stringified.reserve(serializedData.EncodedData.size()); + for (const std::byte b : serializedData.EncodedData) { + stringified.push_back(static_cast(b)); + } + + file->ModifyContent(std::move(stringified)); + file->DisposeContent(); } /* * TODO others @@ -69,6 +89,12 @@ void Asset::Serialize() { file->ModifyContent(std::move(serializedData)); file->DisposeContent(); } + if (IsMaterial()) { + std::string serializedData; + serializedData = SerializeMaterial(DataAs()); + file->ModifyContent(std::move(serializedData)); + file->DisposeContent(); + } } void Asset::Deserialize() { @@ -81,19 +107,37 @@ void Asset::Deserialize() { auto result = DeserializeComponent(*file); for (auto& message : result.messages) { switch (message.messageType) { - case ComponentCompilerMessageType::INFO: + case ComponentCompilerMessageType::Info: infoList.emplace_back(std::move(message)); break; - case ComponentCompilerMessageType::WARNING: + case ComponentCompilerMessageType::Warning: warningList.emplace_back(std::move(message)); break; - case ComponentCompilerMessageType::ERROR: + case ComponentCompilerMessageType::Error: errorList.emplace_back(std::move(message)); break; } } data = std::move(result.deserializedComponent); } + if (IsSampler()) { + SamplerDeserializationResult result = DeserializeSampler(*file); + if (result.erred) { + for (auto& error : result.errors) { + errorList.emplace_back(std::move(error)); + } + } + data = std::move(result.deserializedSampler); + } + if (IsTexture()) { + TextureDeserializationResult result = DeserializeTexture(*file); + if (result.erred) { + for (auto& error : result.errors) { + errorList.emplace_back(std::move(error)); + } + } + data = std::move(result.deserializedTexture); + } if (IsShader()) { ShaderDeserializationResult result; if (Assets::IsVertexShaderFile(*file)) { @@ -114,18 +158,8 @@ void Asset::Deserialize() { if (Assets::IsComputeShaderFile(*file)) { result = DeserializeComputeShader(*file); } - for (auto& message : result.messages) { - switch (message.messageType) { - case ShaderCompilerMessageType::INFO: - infoList.emplace_back(std::move(message)); - break; - case ShaderCompilerMessageType::WARNING: - warningList.emplace_back(std::move(message)); - break; - case ShaderCompilerMessageType::ERROR: - errorList.emplace_back(std::move(message)); - break; - } + for (auto& message : result.errors) { + errorList.emplace_back(std::move(message)); } data = std::move(result.deserializedShader); } @@ -140,8 +174,12 @@ void Asset::Deserialize() { } data = std::move(result.deserializedShaderProgram); } - if (IsTexture()) { - data = DeserializeTexture(*file); + if (IsMaterial()) { + MaterialDeserializationResult result = DeserializeMaterial(*file); + for (auto& error : result.errors) { + errorList.emplace_back(std::move(error)); + } + data = std::move(result.deserializedMaterial); } /* * TODO others @@ -169,12 +207,13 @@ UUID Asset::Instantiate() const { bool Asset::IsScene() const { return Assets::IsSceneFile(*file); } bool Asset::IsComponentDefinition() const { return Assets::IsComponentDefinitionFile(*file); } -bool Asset::IsScript() const { return Assets::IsScriptFile(*file); } +bool Asset::IsSampler() const { return Assets::IsSamplerFile(*file); } bool Asset::IsTexture() const { return Assets::IsTextureFile(*file); } -bool Asset::IsModel() const { return Assets::IsModelFile(*file); } -bool Asset::IsMaterial() const { return Assets::IsMaterialFile(*file); } bool Asset::IsShader() const { return Assets::IsShaderFile(*file); } bool Asset::IsShaderProgram() const { return Assets::IsShaderProgramFile(*file); } +bool Asset::IsMaterial() const { return Assets::IsMaterialFile(*file); } +bool Asset::IsScript() const { return Assets::IsScriptFile(*file); } +bool Asset::IsModel() const { return Assets::IsModelFile(*file); } bool Asset::HasInfoMessages() const { return !infoList.empty(); } const std::vector& Asset::InfoMessages() const { return infoList; } diff --git a/Engine/Asset.hpp b/Engine/Asset.hpp index 6468d636..8467f10a 100644 --- a/Engine/Asset.hpp +++ b/Engine/Asset.hpp @@ -1,23 +1,24 @@ #pragma once -#include "UUID.hpp" -#include "FileNode.hpp" +#include +#include + +#include +#include +#include +#include #include -#include #include +#include +#include +#include +#include //#include "Script.hpp" -#include "Texture.hpp" -#include "Model.hpp" -//#include "Material.hpp" +//#include "Model.hpp" -#include -#include -#include -#include - -#define ASSET_TYPE Scene, Component, Shader, ShaderProgram, Texture, Model +#define ASSET_TYPE Scene, Component, Sampler, Texture, Shader, ShaderProgram, Material/*, Model*/ template concept AssetType = concepts::IsAnyOf && concepts::Copyable && concepts::Serializable && std::movable; using AssetData = std::variant; @@ -40,6 +41,10 @@ struct Asset final : ObserverPattern::Observable { T& DataAs() { return std::get(data); } + template + const T& DataAs() const { + return std::get(data); + } uint64_t Version() const; void Serialize(); @@ -57,12 +62,13 @@ struct Asset final : ObserverPattern::Observable { bool IsScene() const; bool IsComponentDefinition() const; - bool IsScript() const; + bool IsSampler() const; bool IsTexture() const; - bool IsModel() const; - bool IsMaterial() const; bool IsShader() const; bool IsShaderProgram() const; + bool IsMaterial() const; + bool IsScript() const; + bool IsModel() const; bool HasInfoMessages() const; const std::vector& InfoMessages() const; @@ -77,7 +83,7 @@ struct Asset final : ObserverPattern::Observable { void NotifyObservers(ObserverPattern::Notification message) final; private: - UUID id{}; + UUID id{ UUID::Empty() }; FNode* file{ nullptr }; AssetData data{ std::monostate{} }; uint64_t version{}; diff --git a/Engine/AssetBridge.cpp b/Engine/AssetBridge.cpp new file mode 100644 index 00000000..04584b18 --- /dev/null +++ b/Engine/AssetBridge.cpp @@ -0,0 +1,183 @@ +#include + +// GPUFrameBuffer +template<> +std::vector GPUFrameBuffers::Allocate(const Assets& assets, const UUID asset) noexcept { + return {}; +} + +// GPUShader +template<> +std::vector GPUShaders::Allocate(const Assets& assets, const UUID asset) noexcept { + AssetHandle handle{ assets.FindAsset(asset) }; + assert(handle && handle->IsShader()); + const Shader& shader{ handle->DataAs() }; + + GPUShaderBuilder builder; + builder.SetName(shader.Name).SetSourceCode(shader.SourceCode).SetType(shader.Type); + + auto [gpuShader, messages] = builder.Build(); + if (gpuShader.has_value()) { + database[asset] = std::move(gpuShader.value()); + } else { + DOA_LOG_ERROR("Shader allocation failed for %s (UUID: %s). Aborting.", shader.Name.c_str(), asset.AsString().c_str()); + } + return messages; +} + +// GPUShaderProgram +template<> +std::vector GPUShaderPrograms::Allocate(const Assets& assets, const UUID asset) noexcept { + AssetHandle handle{ assets.FindAsset(asset) }; + assert(handle && handle->IsShaderProgram()); + const ShaderProgram& program{ handle->DataAs() }; + + std::vector preBuildMessages; + GPUShader* vertShader = bridge.GetShaders().Query(program.VertexShader); + GPUShader* tessEvalShader = bridge.GetShaders().Query(program.TessellationEvaluationShader); + GPUShader* tessCtrlShader = bridge.GetShaders().Query(program.TessellationControlShader); + GPUShader* geomShader = bridge.GetShaders().Query(program.GeometryShader); + GPUShader* fragShader = bridge.GetShaders().Query(program.FragmentShader); + GPUShader* compShader = bridge.GetShaders().Query(program.ComputeShader); + + if (program.HasComputeShader()) { + if (!compShader) { + preBuildMessages.emplace_back("Cannot build pipeline!"); + preBuildMessages.emplace_back("Compute shader couldn't compile. Check Compute shader for compiler errors."); + } + } else { + if (program.HasVertexShader() && !vertShader) { + preBuildMessages.emplace_back("Vertex shader couldn't compile. Check Vertex shader for compiler errors."); + } + if (program.HasTessellationEvaluationShader() && !tessEvalShader) { + preBuildMessages.emplace_back("Tessellation Evaluation shader couldn't compile. Check Tessellation Evaluation shader for compiler errors."); + } + if (program.HasTessellationControlShader() && !tessCtrlShader) { + preBuildMessages.emplace_back("Tessellation Control shader couldn't compile. Check Tessellation Control shader for compiler errors."); + } + if (program.HasGeometryShader() && !geomShader) { + preBuildMessages.emplace_back("Geometry shader couldn't compile. Check Geometry shader for compiler errors."); + } + if (program.HasFragmentShader() && !fragShader) { + preBuildMessages.emplace_back("Fragment shader couldn't compile. Check Fragment shader for compiler errors."); + } + + // Prepend message if erred + if (!preBuildMessages.empty()) { + preBuildMessages.emplace(preBuildMessages.begin(), "Cannot build pipeline!"); + } + } + + if (!preBuildMessages.empty()) { + DOA_LOG_ERROR("Shader program allocation failed for %s (UUID: %s). Aborting.", program.Name.c_str(), asset.AsString().c_str()); + return preBuildMessages; + } + + GPUShaderProgramBuilder builder; + + builder.SetName(program.Name); + if (vertShader) { + builder.SetVertexShader(*vertShader); + } + if (tessEvalShader) { + builder.SetTessellationEvaluationShader(*tessEvalShader); + } + if (tessCtrlShader) { + builder.SetTessellationControlShader(*tessCtrlShader); + } + if (geomShader) { + builder.SetGeometryShader(*geomShader); + } + if (fragShader) { + builder.SetFragmentShader(*fragShader); + } + if (compShader) { + builder.SetComputeShader(*compShader); + } + + auto [gpuShaderProgram, messages] = builder.Build(); + if (gpuShaderProgram.has_value()) { + database[asset] = std::move(gpuShaderProgram.value()); + } else { + DOA_LOG_ERROR("Shader program allocation failed for %s (UUID: %s). Aborting.", program.Name.c_str(), asset.AsString().c_str()); + } + return messages; +} + +// GPUSampler +template<> +std::vector GPUSamplers::Allocate(const Assets& assets, const UUID asset) noexcept { + AssetHandle handle{ assets.FindAsset(asset) }; + assert(handle && handle->IsSampler()); + const Sampler& sampler{ handle->DataAs() }; + + GPUSamplerBuilder builder; + builder.SetName(sampler.Name) + .SetMinificationFilter(sampler.MinFilter) + .SetMagnificationFilter(sampler.MagFilter) + .SetMinLOD(sampler.MinLOD) + .SetMaxLOD(sampler.MaxLOD) + .SetLODBias(sampler.LODBias) + .SetWrapS(sampler.WrapS) + .SetWrapT(sampler.WrapT) + .SetWrapR(sampler.WrapR) + .SetBorderColor(sampler.BorderColor.r, sampler.BorderColor.g, sampler.BorderColor.b, sampler.BorderColor.a) + .SetCompareMode(sampler.CompareMode) + .SetCompareFunction(sampler.CompareFunction) + .SetMaxAnisotropy(sampler.MaxAnisotropy) + .SetCubemapSeamless(sampler.CubemapSeamless); + + auto [gpuSampler, messages] = builder.Build(); + if (gpuSampler.has_value()) { + database[asset] = std::move(gpuSampler.value()); + } else { + DOA_LOG_ERROR("Sampler allocation failed for %s (UUID: %s). Aborting.", sampler.Name.c_str(), asset.AsString().c_str()); + } + return messages; +} + +// GPUTexture +template<> +std::vector GPUTextures::Allocate(const Assets& assets, const UUID asset) noexcept { + AssetHandle handle{ assets.FindAsset(asset) }; + assert(handle && handle->IsTexture()); + const Texture& texture{ handle->DataAs() }; + + GPUTextureBuilder builder; + builder.SetName(texture.Name) + .SetWidth(texture.Width) + .SetHeight(texture.Height) + .SetDepth(1) + .SetData(texture.Format, texture.PixelData); + + auto [gpuTexture, messages] = builder.Build(); + if (gpuTexture.has_value()) { + database[asset] = std::move(gpuTexture.value()); + } else { + DOA_LOG_ERROR("Texture allocation failed for %s (UUID: %s). Aborting.", texture.Name.c_str(), asset.AsString().c_str()); + } + return messages; +} +template<> +const GPUTexture& GPUTextures::Missing() const noexcept { + static const Texture& missingTexture = Texture::Missing(); + + static GPUTexture missing = GPUTextureBuilder() + .SetName(missingTexture.Name) + .SetWidth(missingTexture.Width) + .SetHeight(missingTexture.Height) + .SetData(missingTexture.Format, missingTexture.PixelData) + .Build().first.value(); + return missing; +} + +GPUFrameBuffers& AssetGPUBridge::GetGPUFrameBuffers() noexcept { return gpuFrameBuffers; } +const GPUFrameBuffers& AssetGPUBridge::GetGPUFrameBuffers() const noexcept { return gpuFrameBuffers; } +GPUShaders& AssetGPUBridge::GetShaders() noexcept { return gpuShaders; } +const GPUShaders& AssetGPUBridge::GetShaders() const noexcept { return gpuShaders; } +GPUShaderPrograms& AssetGPUBridge::GetShaderPrograms() noexcept { return gpuShaderPrograms; } +const GPUShaderPrograms& AssetGPUBridge::GetShaderPrograms() const noexcept { return gpuShaderPrograms; } +GPUSamplers& AssetGPUBridge::GetSamplers() noexcept { return gpuSamplers; } +const GPUSamplers& AssetGPUBridge::GetSamplers() const noexcept { return gpuSamplers; } +GPUTextures& AssetGPUBridge::GetTextures() noexcept { return gpuTextures; } +const GPUTextures& AssetGPUBridge::GetTextures() const noexcept { return gpuTextures; } \ No newline at end of file diff --git a/Engine/AssetBridge.hpp b/Engine/AssetBridge.hpp new file mode 100644 index 00000000..efde1322 --- /dev/null +++ b/Engine/AssetBridge.hpp @@ -0,0 +1,115 @@ +#pragma once + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +struct AssetGPUBridge; + +template +struct GPUObjectDatabase { +#if DEBUG + using Database = std::unordered_map; +#elif NDEBUG + using Database = entt::dense_map; +#else +#error "Neither DEBUG nor NDEBUG are defined!" +#endif + + explicit GPUObjectDatabase(AssetGPUBridge& bridge) noexcept : + bridge(bridge) {} + ~GPUObjectDatabase() noexcept = default; + GPUObjectDatabase(GPUObjectDatabase&) noexcept = delete; + GPUObjectDatabase(GPUObjectDatabase&&) noexcept = delete; + GPUObjectDatabase& operator=(GPUObjectDatabase&) noexcept = delete; + GPUObjectDatabase& operator=(GPUObjectDatabase&&) noexcept = delete; + + bool Exists(const UUID asset) const noexcept { return database.contains(asset); } + T& Fetch(const UUID asset) noexcept { + assert(Exists(asset)); + return database[asset]; + } + const T& Fetch(const UUID asset) const noexcept { + assert(Exists(asset)); + return database.at(asset); + } + T* Query(const UUID asset) noexcept { + if (!Exists(asset)) { return nullptr; } + return &Fetch(asset); + } + const T* Query(const UUID asset) const noexcept { + if (!Exists(asset)) { return nullptr; } + return &Fetch(asset); + } + + std::vector Allocate(const Assets& assets, const UUID asset) noexcept { DOA_LOG_FATAL("Illegal allocator! %s", std::quoted(assets.FindAsset(asset)->File().Name())); std::unreachable(); }; + std::vector TryAllocate(const Assets& assets, const UUID asset) noexcept { + if (Exists(asset)) { return { ErrorMessageType{ std::format("Cannot allocate. Asset {} already has allocated GPU resource!", asset.AsString()) } }; } + return Allocate(assets, asset); + } + void Deallocate(const UUID asset) noexcept { + database.erase(asset); + } + + const T& Missing() const noexcept { + DOA_LOG_FATAL("Illegal missing resource!"); std::unreachable(); + } + +private: + AssetGPUBridge& bridge; + Database database{}; +}; + +#define ND_EXPLICIT_SPECIALIZE_ALLOCATOR(Name, T, ErrorMessageType) \ +using Name = GPUObjectDatabase; \ +template<> \ +std::vector Name::Allocate(const Assets& assets, const UUID asset) noexcept +#define ND_EXPLICIT_SPECIALIZE_ALLOCATOR_SPECIALIZE_MISSING(Name, T) \ +template<> \ +const T& Name::Missing() const noexcept +ND_EXPLICIT_SPECIALIZE_ALLOCATOR(GPUFrameBuffers, GPUFrameBuffer, FrameBufferAllocatorMessage); +ND_EXPLICIT_SPECIALIZE_ALLOCATOR(GPUShaders, GPUShader, ShaderCompilerMessage); +ND_EXPLICIT_SPECIALIZE_ALLOCATOR(GPUShaderPrograms, GPUShaderProgram, ShaderLinkerMessage); +ND_EXPLICIT_SPECIALIZE_ALLOCATOR(GPUSamplers, GPUSampler, SamplerAllocatorMessage); +ND_EXPLICIT_SPECIALIZE_ALLOCATOR(GPUTextures, GPUTexture, TextureAllocatorMessage); ND_EXPLICIT_SPECIALIZE_ALLOCATOR_SPECIALIZE_MISSING(GPUTextures, GPUTexture); +#undef ND_EXPLICIT_SPECIALIZE_ALLOCATOR +#undef ND_EXPLICIT_SPECIALIZE_ALLOCATOR_SPECIALIZE_MISSING + +struct AssetGPUBridge { + + GPUFrameBuffers& GetGPUFrameBuffers() noexcept; + const GPUFrameBuffers& GetGPUFrameBuffers() const noexcept; + GPUShaders& GetShaders() noexcept; + const GPUShaders& GetShaders() const noexcept; + GPUShaderPrograms& GetShaderPrograms() noexcept; + const GPUShaderPrograms& GetShaderPrograms() const noexcept; + GPUSamplers& GetSamplers() noexcept; + const GPUSamplers& GetSamplers() const noexcept; + GPUTextures& GetTextures() noexcept; + const GPUTextures& GetTextures() const noexcept; + +private: + GPUFrameBuffers gpuFrameBuffers{ *this }; + GPUShaders gpuShaders{ *this }; + GPUShaderPrograms gpuShaderPrograms{ *this }; + GPUSamplers gpuSamplers{ *this }; + GPUTextures gpuTextures{ *this }; + +public: + AssetGPUBridge() noexcept = default; + ~AssetGPUBridge() noexcept = default; + AssetGPUBridge(AssetGPUBridge&) noexcept = delete; + AssetGPUBridge(AssetGPUBridge&&) noexcept = delete; + AssetGPUBridge& operator=(AssetGPUBridge&) noexcept = delete; + AssetGPUBridge& operator=(AssetGPUBridge&&) noexcept = delete; +}; diff --git a/Engine/Assets.cpp b/Engine/Assets.cpp index dbc006c1..d9326fba 100644 --- a/Engine/Assets.cpp +++ b/Engine/Assets.cpp @@ -1,17 +1,10 @@ -#include "Assets.hpp" +#include -#include -#include #include -#include #include -#include - -#include "Core.hpp" -#include "Project.hpp" -#include "Log.hpp" -#include "SceneSerializer.hpp" +#include +#include AssetHandle::AssetHandle() noexcept : _asset(nullptr) {} @@ -26,12 +19,18 @@ bool AssetHandle::HasValue() const { return _asset != nullptr; } Asset& AssetHandle::Value() const { return *_asset; } void AssetHandle::Reset() { _asset = nullptr; } -bool Assets::IsSceneFile(const FNode& file) { return file.ext == SCENE_EXT; } -bool Assets::IsScriptFile(const FNode& file) { return file.ext == SCRIPT_EXT; } -bool Assets::IsTextureFile(const FNode& file) { return file.ext == TEXTURE_EXT_PNG || file.ext == TEXTURE_EXT_JPG || file.ext == TEXTURE_EXT_JPEG; } -bool Assets::IsModelFile(const FNode& file) { return file.ext == MODEL_EXT; } -bool Assets::IsMaterialFile(const FNode& file) { return file.ext == MATERIAL_EXT; } -bool Assets::IsShaderFile(const FNode& file) { +bool Assets::IsProjectFile(const FNode& file) noexcept { return file.ext == ProjectExtension; } +bool Assets::IsSceneFile(const FNode& file) noexcept { return file.ext == SceneExtension; } +bool Assets::IsComponentDefinitionFile(const FNode& file) noexcept { return file.ext == ComponentDefinitionExtension; } +bool Assets::IsSamplerFile(const FNode& file) noexcept { return file.ext == SamplerExtension; } +bool Assets::IsTextureFile(const FNode& file) noexcept { + return file.ext == TextureExtensionPNG || + file.ext == TextureExtensionBMP || + file.ext == TextureExtensionTGA || + file.ext == TextureExtensionJPG || + file.ext == TextureExtensionJPEG; +} +bool Assets::IsShaderFile(const FNode& file) noexcept { return IsVertexShaderFile(file) || IsTessellationControlShaderFile(file) || IsTessellationEvaluationShaderFile(file) || @@ -39,17 +38,20 @@ bool Assets::IsShaderFile(const FNode& file) { IsFragmentShaderFile(file) || IsComputeShaderFile(file); } -bool Assets::IsVertexShaderFile(const FNode& file) { return file.ext == VERTEX_SHADER_EXT; } -bool Assets::IsTessellationControlShaderFile(const FNode& file) { return file.ext == TESS_CTRL_SHADER_EXT; } -bool Assets::IsTessellationEvaluationShaderFile(const FNode& file) { return file.ext == TESS_EVAL_SHADER_EXT; } -bool Assets::IsGeometryShaderFile(const FNode& file) { return file.ext == GEOMETRY_SHADER_EXT; } -bool Assets::IsFragmentShaderFile(const FNode& file) { return file.ext == FRAGMENT_SHADER_EXT; } -bool Assets::IsComputeShaderFile(const FNode & file) { return file.ext == COMPUTE_SHADER_EXT; } -bool Assets::IsShaderProgramFile(const FNode& file) { return file.ext == SHADER_PROGRAM_EXT; } -bool Assets::IsComponentDefinitionFile(const FNode& file) { return file.ext == COMP_EXT; } - -Assets::Assets(const Project& project) noexcept : - _root({ &project, nullptr, "", "", "", true }) { +bool Assets::IsVertexShaderFile(const FNode& file) noexcept { return file.ext == VertexShaderExtension; } +bool Assets::IsTessellationControlShaderFile(const FNode& file) noexcept { return file.ext == TessellationControlShaderExtension; } +bool Assets::IsTessellationEvaluationShaderFile(const FNode& file) noexcept { return file.ext == TessellationEvaluationShaderExtension; } +bool Assets::IsGeometryShaderFile(const FNode& file) noexcept { return file.ext == GeometryShaderExtension; } +bool Assets::IsFragmentShaderFile(const FNode& file) noexcept { return file.ext == FragmentShaderExtension; } +bool Assets::IsComputeShaderFile(const FNode & file) noexcept { return file.ext == ComputeShaderExtension; } +bool Assets::IsShaderProgramFile(const FNode& file) noexcept { return file.ext == ShaderProgramExtension; } +bool Assets::IsMaterialFile(const FNode& file) noexcept { return file.ext == MaterialExtension; } +bool Assets::IsScriptFile(const FNode& file) noexcept { return file.ext == SCRIPT_EXT; } +bool Assets::IsModelFile(const FNode& file) noexcept { return file.ext == MODEL_EXT; } + +Assets::Assets(const Project& project, AssetGPUBridge& bridge) noexcept : + _root({ &project, nullptr, "", "", "", true }), + bridge(bridge) { BuildFileNodeTree(project, _root); ImportAllFiles(database, _root); } @@ -90,8 +92,10 @@ void Assets::DeleteAsset(const AssetHandle asset) { std::erase(componentDefinitionAssets, id); std::erase(shaderAssets, id); std::erase(shaderProgramAssets, id); + std::erase(materialAssets, id); std::erase(textureAssets, id); + dependencyGraph.RemoveVertex(id); //ReimportAll(); } @@ -131,7 +135,10 @@ const Assets::UUIDCollection& Assets::ComponentDefinitionAssetIDs() const { retu const Assets::UUIDCollection& Assets::ModelAssetIDs() const { return modelAssets; } const Assets::UUIDCollection& Assets::ShaderAssetIDs() const { return shaderAssets; } const Assets::UUIDCollection& Assets::ShaderProgramAssetIDs() const { return shaderProgramAssets; } -const Assets::UUIDCollection& Assets::ShaderUniformBlockAssetIDs() const { return shaderUniformBlockAssets; } +const Assets::UUIDCollection& Assets::MaterialAssetIDs() const { return materialAssets; } +const Assets::UUIDCollection& Assets::SamplerAssetIDs() const { return samplerAssets; } + +const AssetGPUBridge& Assets::GPUBridge() const { return bridge; } AssetHandle Assets::Import(const FNode& file) { return ImportFile(database, file); } void Assets::ReimportAll() { @@ -141,9 +148,12 @@ void Assets::ReimportAll() { componentDefinitionAssets.clear(); shaderAssets.clear(); shaderProgramAssets.clear(); + materialAssets.clear(); + samplerAssets.clear(); textureAssets.clear(); _root.children.clear(); + dependencyGraph.Clear(); BuildFileNodeTree(*_root.OwningProject(), _root); ImportAllFiles(database, _root); EnsureDeserialization(); @@ -153,8 +163,60 @@ void Assets::EnsureDeserialization() { Deserialize(componentDefinitionAssets); Deserialize(sceneAssets); Deserialize(textureAssets); + Deserialize(samplerAssets); Deserialize(shaderAssets); Deserialize(shaderProgramAssets); + Deserialize(materialAssets); + + ReBuildDependencyGraph(); +} + +void Assets::TryRegisterDependencyBetween(UUID dependent, UUID dependency) noexcept { + if (dependencyGraph.HasVertex(dependent) && !dependencyGraph.HasEdge(dependent, dependency)) { + dependencyGraph.AddEdge(dependent, dependency); + } +} +void Assets::TryDeleteDependencyBetween(UUID dependent, UUID dependency) noexcept { + if (dependencyGraph.HasVertex(dependent) && dependencyGraph.HasEdge(dependent, dependency)) { + dependencyGraph.RemoveEdge(dependent, dependency); + } +} + +void Assets::OnNotify(const ObserverPattern::Observable* source, ObserverPattern::Notification message) { + if (message == "deserialized"_hs) { + const Asset* asset = dynamic_cast(source); + assert(asset); // must be non-null + const UUID origin = asset->ID(); + + if (asset->IsScene()) { PerformPostDeserializationAction (origin); } + if (asset->IsComponentDefinition()) { PerformPostDeserializationAction (origin); } + if (asset->IsShader()) { PerformPostDeserializationAction (origin); } + if (asset->IsShaderProgram()) { PerformPostDeserializationAction(origin); } + if (asset->IsMaterial()) { PerformPostDeserializationAction (origin); } + if (asset->IsSampler()) { PerformPostDeserializationAction (origin); } + if (asset->IsTexture()) { PerformPostDeserializationAction (origin); } + + if (dependencyGraph.HasVertex(origin)) { + auto edgeVertices = dependencyGraph.GetIncomingEdgesOf(origin); + while (edgeVertices.HasNext()) { + const UUID& dependentID = edgeVertices.Next(); + assert(database.contains(dependentID)); + database[dependentID].ForceDeserialize(); + } + } + } + if (message == "data_deleted"_hs || message == "destructed"_hs) { + const Asset* asset = dynamic_cast(source); + assert(asset); // must be non-null + if (asset->ID() == UUID::Empty()) { return; } + if (asset->IsScene()) {} + if (asset->IsComponentDefinition()) {} + if (asset->IsShader()) { bridge.GetShaders().Deallocate(asset->ID()); } + if (asset->IsShaderProgram()) { bridge.GetShaderPrograms().Deallocate(asset->ID()); } + if (asset->IsMaterial()) {} + if (asset->IsSampler()) {} + if (asset->IsTexture()) {} + } } AssetHandle Assets::ImportFile(AssetDatabase& database, const FNode& file) { @@ -169,14 +231,15 @@ AssetHandle Assets::ImportFile(AssetDatabase& database, const FNode& file) { (this is no longer the case, as we have dependencies between assets eg. Scene depends on ComponentDefinition or Material depends on Program, Program depends on Shader etc.) * Step 8: Separate imported asset to its own subcategory (and put it into allAssets list) + * Step 9: Set ownself as imported asset's Observer and return */ - if (file.ext == PROJ_EXT) { return nullptr; } + if (IsProjectFile(file)) { return nullptr; } if (file.IsDirectory()) { return nullptr; } - if (file.ext == ID_EXT) { return nullptr; } + if (file.ext == AssetIDExtension) { return nullptr; } // Step 1 FNode importData = FNode::HollowCopy(file); - importData.ext.append(ID_EXT); - importData.fullName.append(ID_EXT); + importData.ext.append(AssetIDExtension); + importData.fullName.append(AssetIDExtension); tinyxml2::XMLDocument doc; tinyxml2::XMLError err = doc.LoadFile(importData.AbsolutePath().string().c_str()); @@ -241,10 +304,18 @@ AssetHandle Assets::ImportFile(AssetDatabase& database, const FNode& file) { if (asset.IsShaderProgram()) { shaderProgramAssets.push_back(id); } + if (asset.IsMaterial()) { + materialAssets.push_back(id); + } + if (asset.IsSampler()) { + samplerAssets.push_back(id); + } if (asset.IsTexture()) { textureAssets.push_back(id); } + asset.AddObserver(*this); + dependencyGraph.AddVertex(id); return &asset; } else { DOA_LOG_ERROR("Failed to import asset at %s do you have read/write access to the directory?", std::quoted(file.Path().c_str())); @@ -252,9 +323,13 @@ AssetHandle Assets::ImportFile(AssetDatabase& database, const FNode& file) { } } void Assets::ImportAllFiles(AssetDatabase& database, const FNode& root) { + // ImportFile mutates root.Children, therefore we can't use a range + // for loop as; if a re-allocation were to happen, pointers used in + // range for loop are invalidated. ImportFile(database, root); - for (auto& child : root.Children()) { - ImportAllFiles(database, child); + const auto children = root.Children(); + for (size_t i = 0; i < children.size(); i++) { + ImportAllFiles(database, children[i]); } } void Assets::Deserialize(const UUIDCollection& assets) { @@ -293,3 +368,339 @@ void Assets::BuildFileNodeTree(const Project& project, FNode& root) { } } } +void Assets::ReBuildDependencyGraph() noexcept { + dependencyGraph.Clear(); + for (const auto& [id, _] : database) { + dependencyGraph.AddVertex(id); + } + + for (const auto& [id, asset] : database) { + // Some asset types have no innate dependencies. + if (asset.IsScene()) {} + if (asset.IsComponentDefinition()) {} + if (asset.IsShader()) {} + if (asset.IsShaderProgram()) { + const ShaderProgram& program = asset.DataAs(); + if (program.HasVertexShader() && dependencyGraph.HasVertex(program.VertexShader)) { + dependencyGraph.AddEdge(id, program.VertexShader); + } + if (program.HasTessellationControlShader() && dependencyGraph.HasVertex(program.TessellationControlShader)) { + dependencyGraph.AddEdge(id, program.TessellationControlShader); + } + if (program.HasTessellationEvaluationShader() && dependencyGraph.HasVertex(program.TessellationEvaluationShader)) { + dependencyGraph.AddEdge(id, program.TessellationEvaluationShader); + } + if (program.HasGeometryShader() && dependencyGraph.HasVertex(program.GeometryShader)) { + dependencyGraph.AddEdge(id, program.GeometryShader); + } + if (program.HasFragmentShader() && dependencyGraph.HasVertex(program.FragmentShader)) { + dependencyGraph.AddEdge(id, program.FragmentShader); + } + } + if (asset.IsMaterial()) { + const Material& material = asset.DataAs(); + if (material.HasShaderProgram() && dependencyGraph.HasVertex(material.ShaderProgram)) { + dependencyGraph.AddEdge(id, material.ShaderProgram); + } + } + if (asset.IsSampler()) {} + if (asset.IsTexture()) {} + } +} + +template <> +void Assets::PerformPostDeserializationAction(UUID id) noexcept { + bridge.GetTextures().Deallocate(id); + std::vector messages = bridge.GetTextures().Allocate(*this, id); + + // Cast-away const. Assets are never created const. + const Asset& asset{ database[id] }; + std::vector& errorMessages = const_cast&>(asset.ErrorMessages()); + for (auto& message : messages) { + errorMessages.emplace_back(std::move(message)); + } +} +template <> +void Assets::PerformPostDeserializationAction(UUID id) noexcept { + bridge.GetShaders().Deallocate(id); + std::vector messages = bridge.GetShaders().Allocate(*this, id); + + // Cast-away const. Assets are never created const. + const Asset& asset{ database[id] }; + std::vector& infoMessages = const_cast&>(asset.InfoMessages()); + std::vector& warningMessages = const_cast&>(asset.WarningMessages()); + std::vector& errorMessages = const_cast&>(asset.ErrorMessages()); + for (auto& message : messages) { + switch (message.MessageType) { + using enum ShaderCompilerMessage::Type; + case Info: + infoMessages.emplace_back(std::move(message)); + break; + case Warning: + warningMessages.emplace_back(std::move(message)); + break; + case Error: + errorMessages.emplace_back(std::move(message)); + break; + default: + std::unreachable(); + } + } +} +template <> +void Assets::PerformPostDeserializationAction(UUID id) noexcept { + bridge.GetShaderPrograms().Deallocate(id); + std::vector messages = bridge.GetShaderPrograms().Allocate(*this, id); + + // Cast-away const. Assets are never created const. + const Asset& asset{ database[id] }; + std::vector& errorMessages = const_cast&>(asset.ErrorMessages()); + for (auto& message : messages) { + errorMessages.emplace_back(std::move(message)); + } +} + +size_t MaterialPostDeserialization::TypeNameToVariantIndex(std::string_view typeName) noexcept { + // TODO refactor this to remove the magic numbers, see https://stackoverflow.com/questions/52303316/get-index-by-type-in-stdvariant + if (typeName == "float") { return 0uLL; } + else if (typeName == "vec2") { return 1uLL; } + else if (typeName == "vec3") { return 2uLL; } + else if (typeName == "vec4") { return 3uLL; } + + //else if (typeName == "double") {} + //else if (typeName == "dvec2") {} + //else if (typeName == "dvec3") {} + //else if (typeName == "dvec4") {} + + else if (typeName == "int") { return 4uLL; } + else if (typeName == "ivec2") { return 5uLL; } + else if (typeName == "ivec3") { return 6uLL; } + else if (typeName == "ivec4") { return 7uLL; } + + else if (typeName == "unsigned int") { return 8uLL; } + else if (typeName == "uvec2") { return 9uLL; } + else if (typeName == "uvec3") { return 10uLL; } + else if (typeName == "uvec4") { return 11uLL; } + + else if (typeName == "bool") { return 4uLL; } + else if (typeName == "bvec2") { return 5uLL; } + else if (typeName == "bvec3") { return 6uLL; } + else if (typeName == "bvec4") { return 7uLL; } + + else if (typeName == "mat2") { return 12uLL; } + else if (typeName == "mat3") { return 13uLL; } + else if (typeName == "mat4") { return 14uLL; } + else if (typeName == "mat2x3") { return 15uLL; } + else if (typeName == "mat2x4") { return 16uLL; } + else if (typeName == "mat3x2") { return 17uLL; } + else if (typeName == "mat3x4") { return 18uLL; } + else if (typeName == "mat4x2") { return 19uLL; } + else if (typeName == "mat4x3") { return 20uLL; } + + else if (typeName == "sampler2D") { return 21uLL; } + else { DOA_LOG_WARNING("MaterialPostDeserialization::TypeNameToVariantIndex encountered unknown typeName %s", typeName.data()); return std::numeric_limits::max(); } // =) +} +void MaterialPostDeserialization::InsertUniform(Material::Uniforms& uniforms, int location, const UniformValue& uniform) noexcept { + if (const Uniform1f* ptr = std::get_if(&uniform.Value)) { uniforms.Set(location, uniform.Name, *ptr); } + else if (const Uniform2f* ptr = std::get_if(&uniform.Value)) { uniforms.Set(location, uniform.Name, *ptr); } + else if (const Uniform3f* ptr = std::get_if(&uniform.Value)) { uniforms.Set(location, uniform.Name, *ptr); } + else if (const Uniform4f* ptr = std::get_if(&uniform.Value)) { uniforms.Set(location, uniform.Name, *ptr); } + + else if (const Uniform1i* ptr = std::get_if(&uniform.Value)) { uniforms.Set(location, uniform.Name, *ptr); } + else if (const Uniform2i* ptr = std::get_if(&uniform.Value)) { uniforms.Set(location, uniform.Name, *ptr); } + else if (const Uniform3i* ptr = std::get_if(&uniform.Value)) { uniforms.Set(location, uniform.Name, *ptr); } + else if (const Uniform4i* ptr = std::get_if(&uniform.Value)) { uniforms.Set(location, uniform.Name, *ptr); } + + else if (const Uniform1ui* ptr = std::get_if(&uniform.Value)) { uniforms.Set(location, uniform.Name, *ptr); } + else if (const Uniform2ui* ptr = std::get_if(&uniform.Value)) { uniforms.Set(location, uniform.Name, *ptr); } + else if (const Uniform3ui* ptr = std::get_if(&uniform.Value)) { uniforms.Set(location, uniform.Name, *ptr); } + else if (const Uniform4ui* ptr = std::get_if(&uniform.Value)) { uniforms.Set(location, uniform.Name, *ptr); } + + else if (const UniformMatrix2f* ptr = std::get_if(&uniform.Value)) { uniforms.Set(location, uniform.Name, *ptr); } + else if (const UniformMatrix3f* ptr = std::get_if(&uniform.Value)) { uniforms.Set(location, uniform.Name, *ptr); } + else if (const UniformMatrix4f* ptr = std::get_if(&uniform.Value)) { uniforms.Set(location, uniform.Name, *ptr); } + else if (const UniformMatrix2x3f* ptr = std::get_if(&uniform.Value)) { uniforms.Set(location, uniform.Name, *ptr); } + else if (const UniformMatrix3x2f* ptr = std::get_if(&uniform.Value)) { uniforms.Set(location, uniform.Name, *ptr); } + else if (const UniformMatrix2x4f* ptr = std::get_if(&uniform.Value)) { uniforms.Set(location, uniform.Name, *ptr); } + else if (const UniformMatrix4x2f* ptr = std::get_if(&uniform.Value)) { uniforms.Set(location, uniform.Name, *ptr); } + else if (const UniformMatrix3x4f* ptr = std::get_if(&uniform.Value)) { uniforms.Set(location, uniform.Name, *ptr); } + else if (const UniformMatrix4x3f* ptr = std::get_if(&uniform.Value)) { uniforms.Set(location, uniform.Name, *ptr); } + + else if (const UniformSampler2D* ptr = std::get_if(&uniform.Value)) { uniforms.Set(location, uniform.Name, *ptr); } +} +void MaterialPostDeserialization::EmplaceUniform(Material::Uniforms& uniforms, int location, std::string_view typeName, std::string_view name, int arraySize) noexcept { + assert(arraySize > 0); + if (typeName == "float") { + for (int i = 0; i < arraySize; i++) { + uniforms.Set(location + i, name, Uniform1f{}); + } + } else if (typeName == "vec2") { + for (int i = 0; i < arraySize; i++) { + uniforms.Set(location, name, Uniform2f{}); + } + } else if (typeName == "vec3") { + for (int i = 0; i < arraySize; i++) { + uniforms.Set(location, name, Uniform3f{}); + } + } else if (typeName == "vec4") { + for (int i = 0; i < arraySize; i++) { + uniforms.Set(location, name, Uniform4f{}); + } + } + + else if (typeName == "double") {} + else if (typeName == "dvec2") {} + else if (typeName == "dvec3") {} + else if (typeName == "dvec4") {} + + else if (typeName == "int") { + for (int i = 0; i < arraySize; i++) { + uniforms.Set(location, name, Uniform1i{}); + } + } else if (typeName == "ivec2") { + for (int i = 0; i < arraySize; i++) { + uniforms.Set(location, name, Uniform2i{}); + } + } else if (typeName == "ivec3") { + for (int i = 0; i < arraySize; i++) { + uniforms.Set(location, name, Uniform3i{}); + } + } else if (typeName == "ivec4") { + for (int i = 0; i < arraySize; i++) { + uniforms.Set(location, name, Uniform4i{}); + } + } + + else if (typeName == "unsigned int") { + for (int i = 0; i < arraySize; i++) { + uniforms.Set(location, name, Uniform1ui{}); + } + } else if (typeName == "uvec2") { + for (int i = 0; i < arraySize; i++) { + uniforms.Set(location, name, Uniform2ui{}); + } + } else if (typeName == "uvec3") { + for (int i = 0; i < arraySize; i++) { + uniforms.Set(location, name, Uniform3ui{}); + } + } else if (typeName == "uvec4") { + for (int i = 0; i < arraySize; i++) { + uniforms.Set(location, name, Uniform4ui{}); + } + } + + else if (typeName == "bool") { + for (int i = 0; i < arraySize; i++) { + uniforms.Set(location, name, Uniform1i{}); + } + } else if (typeName == "bvec2") { + for (int i = 0; i < arraySize; i++) { + uniforms.Set(location, name, Uniform2i{}); + } + } else if (typeName == "bvec3") { + for (int i = 0; i < arraySize; i++) { + uniforms.Set(location, name, Uniform3i{}); + } + } else if (typeName == "bvec4") { + for (int i = 0; i < arraySize; i++) { + uniforms.Set(location, name, Uniform4i{}); + } + } + + else if (typeName == "mat2") { + for (int i = 0; i < arraySize; i++) { + uniforms.Set(location, name, UniformMatrix2f{}); + } + } else if (typeName == "mat3") { + for (int i = 0; i < arraySize; i++) { + uniforms.Set(location, name, UniformMatrix3f{}); + } + } else if (typeName == "mat4") { + for (int i = 0; i < arraySize; i++) { + uniforms.Set(location, name, UniformMatrix4f{}); + } + } else if (typeName == "mat2x3") { + for (int i = 0; i < arraySize; i++) { + uniforms.Set(location, name, UniformMatrix2x3f{}); + } + } else if (typeName == "mat2x4") { + for (int i = 0; i < arraySize; i++) { + uniforms.Set(location, name, UniformMatrix2x4f{}); + } + } else if (typeName == "mat3x2") { + for (int i = 0; i < arraySize; i++) { + uniforms.Set(location, name, UniformMatrix3x2f{}); + } + } else if (typeName == "mat3x4") { + for (int i = 0; i < arraySize; i++) { + uniforms.Set(location, name, UniformMatrix3x4f{}); + } + } else if (typeName == "mat4x2") { + for (int i = 0; i < arraySize; i++) { + uniforms.Set(location, name, UniformMatrix4x2f{}); + } + } else if (typeName == "mat4x3") { + for (int i = 0; i < arraySize; i++) { + uniforms.Set(location, name, UniformMatrix4x3f{}); + } + } + + else if (typeName == "sampler1D") { + DOA_LOG_WARNING("Uniform typename %s is still waiting implementation!", typeName.data()); + } else if (typeName == "sampler2D") { + for (int i = 0; i < arraySize; i++) { + uniforms.Set(location, name, UniformSampler2D{}); + } + } else if (typeName == "sampler3D") { + DOA_LOG_WARNING("Uniform typename %s is still waiting implementation!", typeName.data()); + } + else { + DOA_LOG_WARNING("Uniform typename %s is either unknown or not currently supported!", typeName.data()); + } +} + +template <> +void Assets::PerformPostDeserializationAction(UUID id) noexcept { + Material& asset = database[id].DataAs(); + if (!asset.HasShaderProgram()) { + asset.ClearAllUniforms(); + return; + } + assert(database.contains(asset.ShaderProgram)); // Asset must exist, but GPU resource may not due to compilation-linking errors. + //assert(bridge.GetShaderPrograms().Exists(asset.ShaderProgram)); + + const GPUShaderProgram* program = bridge.GetShaderPrograms().Query(asset.ShaderProgram); + if (program) { + auto&& algorithm = [](Material::Uniforms& uniforms, ShaderType group, const GPUShaderProgram& program) { + Material::Uniforms copy = std::move(uniforms); + uniforms.Clear(); + + auto& uniformList = copy.GetAll(); + + for (auto& uniform : program.Uniforms) { + if (uniform.ReferencedBy != group) { continue; } + + auto search = std::ranges::find_if(uniformList, [&uniform](auto& uniformValue) { + return uniformValue.Name == uniform.Name && uniformValue.Value.index() == MaterialPostDeserialization::TypeNameToVariantIndex(uniform.TypeName); + }); + if (search != uniformList.end()) { + MaterialPostDeserialization::InsertUniform(uniforms, uniform.Location, *search); + } else { + MaterialPostDeserialization::EmplaceUniform(uniforms, uniform.Location, uniform.TypeName, uniform.Name, uniform.ArraySize); + } + } + }; + + algorithm(asset.VertexUniforms, ShaderType::Vertex, *program); + algorithm(asset.TessellationControlUniforms, ShaderType::TessellationControl, *program); + algorithm(asset.TessellationEvaluationUniforms, ShaderType::TessellationEvaluation, *program); + algorithm(asset.GeometryUniforms, ShaderType::Geometry, *program); + algorithm(asset.FragmentUniforms, ShaderType::Fragment, *program); + } else { + // Cast-away const. Assets are never created const. + const Asset& asset{ database[id] }; + std::vector& errorMessages = const_cast&>(asset.ErrorMessages()); + errorMessages.emplace_back(std::string("Material deserialization failed.")); + errorMessages.emplace_back(std::string("Referenced shader program failed to allocate. Check for errors in program!")); + } +} \ No newline at end of file diff --git a/Engine/Assets.hpp b/Engine/Assets.hpp index d064f55c..04fb7e45 100644 --- a/Engine/Assets.hpp +++ b/Engine/Assets.hpp @@ -7,10 +7,16 @@ #include +#include +#include + +#include #include #include #include +struct AssetGPUBridge; + struct AssetHandle { AssetHandle() noexcept; @@ -30,44 +36,49 @@ struct AssetHandle { Asset* _asset; }; -struct Assets { +struct Assets : ObserverPattern::Observer { using UUIDCollection = std::vector; - inline static std::string PROJ_EXT{ ".doa" }; - inline static std::string SCENE_EXT{ ".scn" }; + inline static std::string ProjectExtension{ ".doa" }; + inline static std::string SceneExtension{ ".scn" }; + inline static std::string ComponentDefinitionExtension{ ".ncd" }; + inline static std::string SamplerExtension{ ".smplr" }; + inline static std::string TextureExtensionPNG{ ".png" }; + inline static std::string TextureExtensionBMP{ ".bmp" }; + inline static std::string TextureExtensionTGA{ ".tga" }; + inline static std::string TextureExtensionJPG{ ".jpg" }; + inline static std::string TextureExtensionJPEG{ ".jpeg" }; + inline static std::string VertexShaderExtension{ ".vert" }; + inline static std::string TessellationControlShaderExtension{ ".tesc" }; + inline static std::string TessellationEvaluationShaderExtension{ ".tese" }; + inline static std::string GeometryShaderExtension{ ".geom" }; + inline static std::string FragmentShaderExtension{ ".frag" }; + inline static std::string ComputeShaderExtension{ ".comp" }; + inline static std::string ShaderProgramExtension{ ".prog" }; + inline static std::string MaterialExtension{ ".mat" }; inline static std::string SCRIPT_EXT{ ".scrpt" }; - inline static std::string TEXTURE_EXT_PNG{ ".png" }; - inline static std::string TEXTURE_EXT_JPG{ ".jpg" }; - inline static std::string TEXTURE_EXT_JPEG{ ".jpeg" }; inline static std::string MODEL_EXT{ ".mdl" }; - inline static std::string MATERIAL_EXT{ ".mat" }; - inline static std::string VERTEX_SHADER_EXT{ ".vert" }; - inline static std::string TESS_CTRL_SHADER_EXT{ ".tesc" }; - inline static std::string TESS_EVAL_SHADER_EXT{ ".tese" }; - inline static std::string GEOMETRY_SHADER_EXT{ ".geom" }; - inline static std::string FRAGMENT_SHADER_EXT{ ".frag" }; - inline static std::string COMPUTE_SHADER_EXT{ ".comp" }; - inline static std::string SHADER_PROGRAM_EXT{ ".prog" }; - inline static std::string COMP_EXT{ ".ncd" }; - inline static std::string ID_EXT{ ".id" }; - - static bool IsSceneFile(const FNode& file); - static bool IsScriptFile(const FNode& file); - static bool IsTextureFile(const FNode& file); - static bool IsModelFile(const FNode& file); - static bool IsMaterialFile(const FNode& file); - static bool IsShaderFile(const FNode& file); - static bool IsVertexShaderFile(const FNode& file); - static bool IsTessellationControlShaderFile(const FNode& file); - static bool IsTessellationEvaluationShaderFile(const FNode& file); - static bool IsGeometryShaderFile(const FNode& file); - static bool IsFragmentShaderFile(const FNode& file); - static bool IsComputeShaderFile(const FNode& file); - static bool IsShaderProgramFile(const FNode& file); - static bool IsComponentDefinitionFile(const FNode& file); - - explicit Assets(const Project& project) noexcept; + inline static std::string AssetIDExtension{ ".id" }; + + static bool IsProjectFile(const FNode& file) noexcept; + static bool IsSceneFile(const FNode& file) noexcept; + static bool IsComponentDefinitionFile(const FNode& file) noexcept; + static bool IsSamplerFile(const FNode& file) noexcept; + static bool IsTextureFile(const FNode& file) noexcept; + static bool IsShaderFile(const FNode& file) noexcept; + static bool IsVertexShaderFile(const FNode& file) noexcept; + static bool IsTessellationControlShaderFile(const FNode& file) noexcept; + static bool IsTessellationEvaluationShaderFile(const FNode& file) noexcept; + static bool IsGeometryShaderFile(const FNode& file) noexcept; + static bool IsFragmentShaderFile(const FNode& file) noexcept; + static bool IsComputeShaderFile(const FNode& file) noexcept; + static bool IsShaderProgramFile(const FNode& file) noexcept; + static bool IsMaterialFile(const FNode& file) noexcept; + static bool IsScriptFile(const FNode& file) noexcept; + static bool IsModelFile(const FNode& file) noexcept; + + explicit Assets(const Project& project, AssetGPUBridge& bridge) noexcept; ~Assets() = default; Assets(const Assets&) = delete; Assets(Assets&&) = delete; @@ -111,13 +122,22 @@ struct Assets { const UUIDCollection& ModelAssetIDs() const; const UUIDCollection& ShaderAssetIDs() const; const UUIDCollection& ShaderProgramAssetIDs() const; - const UUIDCollection& ShaderUniformBlockAssetIDs() const; + const UUIDCollection& MaterialAssetIDs() const; + const UUIDCollection& SamplerAssetIDs() const; + + const AssetGPUBridge& GPUBridge() const; AssetHandle Import(const FNode& file); void ReimportAll(); void EnsureDeserialization(); + void TryRegisterDependencyBetween(UUID dependent, UUID dependency) noexcept; + void TryDeleteDependencyBetween(UUID dependent, UUID dependency) noexcept; + +protected: + void OnNotify(const ObserverPattern::Observable* source, ObserverPattern::Notification message) final; + private: #if DEBUG @@ -143,11 +163,37 @@ struct Assets { UUIDCollection modelAssets{}; UUIDCollection shaderAssets{}; UUIDCollection shaderProgramAssets{}; - UUIDCollection shaderUniformBlockAssets{}; + UUIDCollection materialAssets{}; + UUIDCollection samplerAssets{}; + + AdjacencyList dependencyGraph{}; + + AssetGPUBridge& bridge; AssetHandle ImportFile(AssetDatabase& database, const FNode& file); void ImportAllFiles(AssetDatabase& database, const FNode& root); void Deserialize(const UUIDCollection& assets); void BuildFileNodeTree(const Project& project, FNode& root); + void ReBuildDependencyGraph() noexcept; + + template + void PerformPostDeserializationAction([[maybe_unused]] UUID id) noexcept {} }; + +template <> +void Assets::PerformPostDeserializationAction(UUID id) noexcept; +template <> +void Assets::PerformPostDeserializationAction(UUID id) noexcept; +template <> +void Assets::PerformPostDeserializationAction(UUID id) noexcept; + +// Material Post Serialiation Helpers +namespace MaterialPostDeserialization { + size_t TypeNameToVariantIndex(std::string_view typeName) noexcept; + void InsertUniform(Material::Uniforms& uniforms, int location, const UniformValue& uniform) noexcept; + void EmplaceUniform(Material::Uniforms& uniforms, int location, std::string_view name, std::string_view typeName, int arraySize = 1) noexcept; +} + +template <> +void Assets::PerformPostDeserializationAction(UUID id) noexcept; \ No newline at end of file diff --git a/Engine/AssimpGLMConvert.hpp b/Engine/AssimpGLMConvert.hpp deleted file mode 100644 index 408a233d..00000000 --- a/Engine/AssimpGLMConvert.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -inline glm::vec4 aiColorToGLMVec(const aiColor4D& c) { - return { c.r, c.g, c.b, c.a }; -} -inline glm::vec2 aiVecToGLMVec(const aiVector2D& v) { - return { v.x, v.y }; -} -inline glm::vec3 aiVecToGLMVec(const aiVector3D& v) { - return { v.x, v.y, v.z }; -} -inline glm::mat4 aiMat3ToGLMMat3(const aiMatrix3x3& m) { - return glm::transpose(glm::make_mat3(&m.a1)); -} -inline glm::mat4 aiMat4ToGLMMat4(const aiMatrix4x4& m) { - return glm::transpose(glm::make_mat4(&m.a1)); -} -inline glm::quat aiQuatToGLMQuat(const aiQuaternion& q) { - return { q.w, q.x, q.y, q.z }; -} \ No newline at end of file diff --git a/Engine/Avatar.hpp b/Engine/Avatar.hpp deleted file mode 100644 index d1c30664..00000000 --- a/Engine/Avatar.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include -#include - -#include -#include - -#include - -#include - -struct Avatar { - struct Node { - struct Mesh { - std::string Name{}; - - std::vector Vertices{}; - std::vector Indices{}; - }; - - std::string Name{}; - glm::mat4 LocalTransformation{ glm::mat4(1) }; - Node* Parent{ nullptr }; - - unsigned ChildCount{}; - std::array Children{}; - - unordered_string_map Meshes{}; - }; - struct Bone { - int Id{}; - std::string Name{}; - glm::mat4 offsetTransform{ glm::mat4(1) }; - }; - - Node* RootNode{ nullptr }; - unordered_string_map Nodes{}; - unordered_string_map Bones{}; -}; \ No newline at end of file diff --git a/Engine/BehaviourComponent.cpp b/Engine/BehaviourComponent.cpp index 688ce6d0..ebc4fe54 100644 --- a/Engine/BehaviourComponent.cpp +++ b/Engine/BehaviourComponent.cpp @@ -8,4 +8,4 @@ BehaviourComponent::~BehaviourComponent() {} void BehaviourComponent::Init() {} -void BehaviourComponent::Execute(float deltaTime) {} \ No newline at end of file +void BehaviourComponent::Execute([[maybe_unused]] float deltaTime) {} \ No newline at end of file diff --git a/Engine/BehaviourComponent.hpp b/Engine/BehaviourComponent.hpp index 018c6e36..e4a3ffc3 100644 --- a/Engine/BehaviourComponent.hpp +++ b/Engine/BehaviourComponent.hpp @@ -19,7 +19,7 @@ struct BehaviourComponent { virtual ~BehaviourComponent() = 0; virtual void Init(); - virtual void Execute(float deltaTime); + virtual void Execute([[maybe_unused]] float deltaTime); template Component& GetComponent() { diff --git a/Engine/Buffer.cpp b/Engine/Buffer.cpp deleted file mode 100644 index e601ed3e..00000000 --- a/Engine/Buffer.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include - -#include - -Buffer::Buffer(const OpenGL::BufferData& data) noexcept : - data(data) { - AllocateGPU(); -} -Buffer::Buffer(OpenGL::BufferData&& data) noexcept : - data(std::move(data)) { - AllocateGPU(); -} -Buffer::~Buffer() noexcept { DeallocateGPU(); } -Buffer::Buffer(const Buffer& other) noexcept : - data(other.data) { - AllocateGPU(); -} -Buffer::Buffer(Buffer&& other) noexcept : - object(std::exchange(other.object, 0)), - data(std::move(other.data)) {} -Buffer& Buffer::operator=(const Buffer& other) noexcept { - DeallocateGPU(); - data = other.data; - AllocateGPU(); - return *this; -} -Buffer& Buffer::operator=(Buffer&& other) noexcept { - DeallocateGPU(); - object = std::exchange(other.object, 0); - data = std::move(other.data); - return *this; -} - -bool Buffer::HasData() const { return object != 0; } -const OpenGL::BufferData& Buffer::GetData() const { return data; } -OpenGL::BufferData::const_iterator Buffer::begin() const { return data.begin(); } -OpenGL::BufferData::const_iterator Buffer::end() const { return data.end(); } - -void Buffer::AllocateGPU() noexcept { - glCreateBuffers(1, &object); - glNamedBufferStorage(object, sizeof(OpenGL::BufferData::value_type) * data.size(), data.data(), 0); -} -void Buffer::DeallocateGPU() noexcept { - glDeleteBuffers(1, &object); -} - diff --git a/Engine/Buffer.hpp b/Engine/Buffer.hpp deleted file mode 100644 index c99ec75f..00000000 --- a/Engine/Buffer.hpp +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include -#include - -#include - -struct Buffer { - - template - static OpenGL::BufferData Accept(std::vector values) { - return Accept(std::span{ values }); - } - - template - static OpenGL::BufferData Accept(std::span values) { - auto bytes{ std::as_bytes(values) }; - return { bytes.begin(), bytes.end() }; - } - - Buffer() noexcept = default; - explicit Buffer(const OpenGL::BufferData& data) noexcept; - explicit Buffer(OpenGL::BufferData&& data) noexcept; - ~Buffer() noexcept; - Buffer(const Buffer& other) noexcept; - Buffer(Buffer&& other) noexcept; - Buffer& operator=(const Buffer& other) noexcept; - Buffer& operator=(Buffer&& other) noexcept; - - bool HasData() const; - const OpenGL::BufferData& GetData() const; - OpenGL::BufferData::const_iterator begin() const; - OpenGL::BufferData::const_iterator end() const; - -private: - - GLuint object{}; - OpenGL::BufferData data{}; - - void AllocateGPU() noexcept; - void DeallocateGPU() noexcept; - - friend struct VertexArray; -}; - diff --git a/Engine/CMakeLists.txt b/Engine/CMakeLists.txt index a9fe8a33..b3c4ed56 100644 --- a/Engine/CMakeLists.txt +++ b/Engine/CMakeLists.txt @@ -5,10 +5,12 @@ set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED ON) add_library(Engine STATIC) + target_include_directories(Engine PUBLIC ..) target_link_libraries(Engine PUBLIC Utility) target_link_libraries(Engine PUBLIC angelscript_addons_impl) +target_link_libraries(Engine PUBLIC debugbreak) target_link_libraries(Engine PUBLIC stb_impl) find_package(assimp CONFIG REQUIRED) @@ -34,16 +36,14 @@ target_link_libraries(Engine PUBLIC ICU::uc ICU::dt ICU::i18n) set(GROUP_LIST "NeoDoa.hpp" - - "TypedefsAndConstants.hpp" - + "Asset/Asset.cpp" "Asset/Asset.hpp" "Asset/Assets.cpp" "Asset/Assets.hpp" "Asset/UUID.cpp" "Asset/UUID.hpp" - + "Asset/Color/Color.hpp" "Asset/Component/Component.cpp" "Asset/Component/Component.hpp" @@ -51,9 +51,18 @@ set(GROUP_LIST "Asset/Component/Serialize/ComponentDeserializer.hpp" "Asset/Manager/FileNode.cpp" "Asset/Manager/FileNode.hpp" - "Asset/Model/Avatar.hpp" - "Asset/Model/Model.cpp" - "Asset/Model/Model.hpp" + "Asset/Material/Material.cpp" + "Asset/Material/Material.hpp" + "Asset/Material/MaterialSerializer.cpp" + "Asset/Material/MaterialSerializer.hpp" + "Asset/Material/MaterialDeserializer.cpp" + "Asset/Material/MaterialDeserializer.hpp" + "Asset/Sampler/Sampler.cpp" + "Asset/Sampler/Sampler.hpp" + "Asset/Sampler/Serialize/SamplerSerializer.cpp" + "Asset/Sampler/Serialize/SamplerSerializer.hpp" + "Asset/Sampler/Serialize/SamplerDeserializer.cpp" + "Asset/Sampler/Serialize/SamplerDeserializer.hpp" "Asset/Script/Angel.cpp" "Asset/Script/Angel.hpp" "Asset/Script/EnumValue.hpp" @@ -72,27 +81,25 @@ set(GROUP_LIST "Asset/Texture/Serialize/TextureSerializer.hpp" "Asset/Texture/Serialize/TextureDeserializer.cpp" "Asset/Texture/Serialize/TextureDeserializer.hpp" - + "Core/Core.cpp" "Core/Core.hpp" + "Core/DataTypes.cpp" + "Core/DataTypes.hpp" "Core/Input.cpp" "Core/Input.hpp" - "Core/Window.cpp" - "Core/Window.hpp" "Core/TypeSystem.hpp" - + "Core/Window/Monitor.cpp" + "Core/Window/Monitor.hpp" + "Core/Window/MonitorGLFW.cpp" + "Core/Window/MonitorGLFW.hpp" + "Core/Window/Window.cpp" + "Core/Window/Window.hpp" + "Core/Window/WindowGLFW.cpp" + "Core/Window/WindowGLFW.hpp" + "Event/Event.hpp" - - "OpenGL/OpenGLCommon.hpp" - "OpenGL/Buffer.cpp" - "OpenGL/Buffer.hpp" - "OpenGL/FrameBuffer.cpp" - "OpenGL/FrameBuffer.hpp" - "OpenGL/VertexArray.cpp" - "OpenGL/VertexArray.hpp" - "OpenGL/VertexAttribLayout.cpp" - "OpenGL/VertexAttribLayout.hpp" - + "ECS/Registry.hpp" "ECS/Components/BehaviourComponent.cpp" "ECS/Components/BehaviourComponent.hpp" @@ -102,6 +109,8 @@ set(GROUP_LIST "ECS/Components/ChildComponent.hpp" "ECS/Components/IDComponent.cpp" "ECS/Components/IDComponent.hpp" + "ECS/Components/MultiMaterialComponent.cpp" + "ECS/Components/MultiMaterialComponent.hpp" "ECS/Components/ParentComponent.cpp" "ECS/Components/ParentComponent.hpp" "ECS/Components/TransformComponent.cpp" @@ -110,29 +119,53 @@ set(GROUP_LIST "ECS/Entity/Entity.hpp" "ECS/Systems/System.cpp" "ECS/Systems/System.hpp" - + + "Graphics/AssetBridge.cpp" + "Graphics/AssetBridge.hpp" + "Graphics/GPUBuffer.cpp" + "Graphics/GPUBuffer.hpp" + "Graphics/GPUDescriptorSet.cpp" + "Graphics/GPUDescriptorSet.hpp" + "Graphics/GPUFrameBuffer.cpp" + "Graphics/GPUFrameBuffer.hpp" + "Graphics/GPUPipeline.cpp" + "Graphics/GPUPipeline.hpp" + "Graphics/GPUShader.cpp" + "Graphics/GPUShader.hpp" + "Graphics/GPUTexture.cpp" + "Graphics/GPUTexture.hpp" + "Graphics/GPUVertexAttribLayout.cpp" + "Graphics/GPUVertexAttribLayout.hpp" + "Graphics/Graphics.cpp" + "Graphics/Graphics.hpp" + "Graphics/GraphicsGL.cpp" + "Graphics/GraphicsGL.hpp" + "Graphics/GraphicsNone.cpp" + "Graphics/GraphicsNone.hpp" + "ImGui/FontAwesome.hpp" "ImGui/ImGuiRenderCommand.hpp" "ImGui/ImGuiRenderer.cpp" "ImGui/ImGuiRenderer.hpp" - + "Log/Log.cpp" "Log/Log.hpp" - + "Misc/AABB.cpp" "Misc/AABB.hpp" - "Misc/Vertex.hpp" + "Misc/OstreamImpls.cpp" + "Misc/Point.hpp" "Misc/Region.hpp" "Misc/Resolution.hpp" - "Misc/OstreamImpls.cpp" - "Misc/AssimpGLMConvert.hpp" - + "Misc/Vertex.hpp" + "Project/Project.cpp" "Project/Project.hpp" "Project/Serialize/ProjectSerializer.cpp" "Project/Serialize/ProjectSerializer.hpp" "Project/Serialize/ProjectDeserializer.cpp" "Project/Serialize/ProjectDeserializer.hpp" + "Project/Scene/ACamera.cpp" "Project/Scene/ACamera.hpp" "Project/Scene/OrthoCamera.cpp" "Project/Scene/OrthoCamera.hpp" @@ -144,11 +177,9 @@ set(GROUP_LIST "Project/Scene/Serialize/SceneSerializer.hpp" "Project/Scene/Serialize/SceneDeserializer.cpp" "Project/Scene/Serialize/SceneDeserializer.hpp" - + "Renderer/Renderer.cpp" "Renderer/Renderer.hpp" - "Renderer/OutlineRenderer.cpp" - "Renderer/OutlineRenderer.hpp" ) foreach(source IN LISTS GROUP_LIST) get_filename_component(source_path "${source}" PATH) @@ -159,7 +190,7 @@ foreach(source IN LISTS GROUP_LIST) endforeach() if(MSVC) - target_compile_options(Engine PRIVATE "/MP") + target_compile_options(Engine PRIVATE "/MP") endif() copy_folder_post_build(Fonts) diff --git a/Engine/CameraComponent.cpp b/Engine/CameraComponent.cpp index 2d51c21e..be9608b5 100644 --- a/Engine/CameraComponent.cpp +++ b/Engine/CameraComponent.cpp @@ -6,24 +6,20 @@ #include /* Ortho Camera Below */ - -OrthoCameraComponent::OrthoCameraComponent(const Entity owner, OrthoCamera&& data) noexcept : - entity(owner), - data(std::move(data)) { - UpdateMatrices(); -} - OrthoCameraComponent::OrthoCameraComponent(const Entity owner) noexcept : entity(owner) { UpdateMatrices(); } +OrthoCameraComponent::OrthoCameraComponent(const Entity owner, const OrthoCamera& data) noexcept : + entity(owner), + data(data) { + UpdateMatrices(); +} Entity OrthoCameraComponent::GetEntity() const { return entity; } -const OrthoCamera& OrthoCameraComponent::GetData() const { return data; } -void OrthoCameraComponent::SetData(OrthoCamera&& data) { - this->data = std::move(data); -} +const OrthoCamera& OrthoCameraComponent::GetData() const noexcept { return data; } +void OrthoCameraComponent::SetData(const OrthoCamera& data) noexcept { this->data = data; } void OrthoCameraComponent::UpdateMatrices() { data.UpdateView(); @@ -34,30 +30,23 @@ void OrthoCameraComponent::UpdateMatrices() { void OrthoCameraComponent::TurnOn() { isActiveAndRendering = true; } void OrthoCameraComponent::TurnOff() { isActiveAndRendering = false; } bool OrthoCameraComponent::IsActiveAndRendering() const { return isActiveAndRendering; } - -FrameBuffer& OrthoCameraComponent::GetFrameBuffer() { return frameBuffer; } -const FrameBuffer& OrthoCameraComponent::GetFrameBuffer() const { return frameBuffer; } - /* Ortho Camera Above */ -/* Perpective Camera Below */ - -PerspectiveCameraComponent::PerspectiveCameraComponent(const Entity owner, PerspectiveCamera&& data) noexcept : - entity(owner), - data(std::move(data)) { - UpdateMatrices(); -} +/* Perpective Camera Below */ PerspectiveCameraComponent::PerspectiveCameraComponent(const Entity owner) noexcept : entity(owner) { UpdateMatrices(); } +PerspectiveCameraComponent::PerspectiveCameraComponent(const Entity owner, const PerspectiveCamera& data) noexcept : + entity(owner), + data(data) { + UpdateMatrices(); +} Entity PerspectiveCameraComponent::GetEntity() const { return entity; } -const PerspectiveCamera& PerspectiveCameraComponent::GetData() const { return data; } -void PerspectiveCameraComponent::SetData(PerspectiveCamera&& data) { - this->data = std::move(data); -} +const PerspectiveCamera& PerspectiveCameraComponent::GetData() const noexcept { return data; } +void PerspectiveCameraComponent::SetData(const PerspectiveCamera& data) noexcept { this->data = data; } void PerspectiveCameraComponent::UpdateMatrices() { data.UpdateView(); @@ -68,8 +57,4 @@ void PerspectiveCameraComponent::UpdateMatrices() { void PerspectiveCameraComponent::TurnOn() { isActiveAndRendering = true; } void PerspectiveCameraComponent::TurnOff() { isActiveAndRendering = false; } bool PerspectiveCameraComponent::IsActiveAndRendering() const { return isActiveAndRendering; } - -FrameBuffer& PerspectiveCameraComponent::GetFrameBuffer() { return frameBuffer; } -const FrameBuffer& PerspectiveCameraComponent::GetFrameBuffer() const { return frameBuffer; } - /* Perspective Camera Above*/ \ No newline at end of file diff --git a/Engine/CameraComponent.hpp b/Engine/CameraComponent.hpp index b75f4f6b..9dcb379d 100644 --- a/Engine/CameraComponent.hpp +++ b/Engine/CameraComponent.hpp @@ -9,7 +9,6 @@ #include "Resolution.hpp" #include "Entity.hpp" -#include "FrameBuffer.hpp" struct Scene; namespace tinyxml2 { @@ -27,17 +26,15 @@ struct OrthoCameraComponent { -1.0f, 1.0f, -1.0f, 1.0f }; - FrameBuffer frameBuffer{}; - - OrthoCameraComponent(const Entity owner, OrthoCamera&& matrix) noexcept; public: OrthoCameraComponent(const Entity owner) noexcept; + OrthoCameraComponent(const Entity owner, const OrthoCamera& matrix) noexcept; Entity GetEntity() const; - const OrthoCamera& GetData() const; - void SetData(OrthoCamera&& data); + const OrthoCamera& GetData() const noexcept; + void SetData(const OrthoCamera& data) noexcept; void UpdateMatrices(); @@ -45,9 +42,6 @@ struct OrthoCameraComponent { void TurnOff(); bool IsActiveAndRendering() const; - FrameBuffer& GetFrameBuffer(); - const FrameBuffer& GetFrameBuffer() const; - friend void SerializeOrthoCameraComponent(tinyxml2::XMLPrinter& printer, const OrthoCameraComponent& camera); friend OrthoCameraComponent DeserializeOrthoCameraComponent(tinyxml2::XMLElement* component, const Entity entity, const Scene& scene); }; @@ -63,17 +57,15 @@ struct PerspectiveCameraComponent { 0.001f, 10000.0f }; - FrameBuffer frameBuffer{}; - - PerspectiveCameraComponent(const Entity owner, PerspectiveCamera&& data) noexcept; public: PerspectiveCameraComponent(const Entity owner) noexcept; + PerspectiveCameraComponent(const Entity owner, const PerspectiveCamera& data) noexcept; Entity GetEntity() const; - const PerspectiveCamera& GetData() const; - void SetData(PerspectiveCamera&& data); + const PerspectiveCamera& GetData() const noexcept; + void SetData(const PerspectiveCamera& data) noexcept; void UpdateMatrices(); @@ -81,9 +73,6 @@ struct PerspectiveCameraComponent { void TurnOff(); bool IsActiveAndRendering() const; - FrameBuffer& GetFrameBuffer(); - const FrameBuffer& GetFrameBuffer() const; - friend void SerializePerspectiveCameraComponent(tinyxml2::XMLPrinter& printer, const PerspectiveCameraComponent& camera); friend PerspectiveCameraComponent DeserializePerspectiveCameraComponent(tinyxml2::XMLElement* component, const Entity entity, const Scene& scene); }; \ No newline at end of file diff --git a/Engine/ChildComponent.hpp b/Engine/ChildComponent.hpp index d4dad1c5..4ba71b6b 100644 --- a/Engine/ChildComponent.hpp +++ b/Engine/ChildComponent.hpp @@ -14,5 +14,4 @@ struct ChildComponent { private: Entity entity{ NULL_ENTT }; Entity parent{ NULL_ENTT }; - Entity _oldParent{ NULL_ENTT }; }; \ No newline at end of file diff --git a/Engine/ComponentDeserializer.cpp b/Engine/ComponentDeserializer.cpp index 2645d0ea..b1a9ec6b 100644 --- a/Engine/ComponentDeserializer.cpp +++ b/Engine/ComponentDeserializer.cpp @@ -102,14 +102,14 @@ static void CompilationMessageCallback(const asSMessageInfo* msg, void* param) { ComponentCompilerMessage m; if (msg->type == asMSGTYPE_INFORMATION) { - m.messageType = ComponentCompilerMessageType::INFO; + m.messageType = ComponentCompilerMessageType::Info; DOA_LOG_INFO("%s (%d, %d) : %s", msg->section, msg->row, msg->col, msg->message); } else if (msg->type == asMSGTYPE_WARNING) { - m.messageType = ComponentCompilerMessageType::WARNING; + m.messageType = ComponentCompilerMessageType::Warning; DOA_LOG_WARNING("%s (%d, %d) : %s", msg->section, msg->row, msg->col, msg->message); } else { cdr.erred = true; - m.messageType = ComponentCompilerMessageType::ERROR; + m.messageType = ComponentCompilerMessageType::Error; DOA_LOG_ERROR("%s (%d, %d) : %s", msg->section, msg->row, msg->col, msg->message); } m.message = msg->message; @@ -139,7 +139,7 @@ ComponentDeserializationResult DeserializeComponent(const FNode& file) { DOA_LOG_ERROR(errmsg.c_str()); rv.messages.emplace_back( 1, 1, - ComponentCompilerMessageType::ERROR, + ComponentCompilerMessageType::Error, errmsg ); rv.erred = true; @@ -156,7 +156,7 @@ ComponentDeserializationResult DeserializeComponent(const FNode& file) { DOA_LOG_ERROR(errmsg.c_str(), fileName.c_str()); rv.messages.emplace_back( 1, 1, - ComponentCompilerMessageType::ERROR, + ComponentCompilerMessageType::Error, errmsg ); rv.erred = true; @@ -170,7 +170,7 @@ ComponentDeserializationResult DeserializeComponent(const FNode& file) { DOA_LOG_ERROR(errmsg.c_str()); rv.messages.emplace_back( 1, 1, - ComponentCompilerMessageType::ERROR, + ComponentCompilerMessageType::Error, errmsg ); rv.erred = true; @@ -183,7 +183,7 @@ ComponentDeserializationResult DeserializeComponent(const FNode& file) { DOA_LOG_ERROR(errmsg.c_str(), fileName.c_str()); rv.messages.emplace_back( 1, 1, - ComponentCompilerMessageType::ERROR, + ComponentCompilerMessageType::Error, errmsg ); rv.erred = true; @@ -192,7 +192,7 @@ ComponentDeserializationResult DeserializeComponent(const FNode& file) { { // Check component count - we can have one and only one component per file! int componentDefinitionCount{ 0 }; - for (auto i = 0; i < declaredObjectCount; i++) { + for (decltype(declaredObjectCount) i = 0; i < declaredObjectCount; i++) { auto typeInfo = scriptModule->GetObjectTypeByIndex(i); if (angel->IsComponentDefinition(typeInfo)) { componentDefinitionCount++; @@ -204,7 +204,7 @@ ComponentDeserializationResult DeserializeComponent(const FNode& file) { DOA_LOG_ERROR(errmsg.c_str(), fileName.c_str()); rv.messages.emplace_back( 1, 1, - ComponentCompilerMessageType::ERROR, + ComponentCompilerMessageType::Error, errmsg ); rv.erred = true; @@ -216,61 +216,57 @@ ComponentDeserializationResult DeserializeComponent(const FNode& file) { DOA_LOG_WARNING(warnmsg.c_str(), fileName.c_str()); rv.messages.emplace_back( 1, 1, - ComponentCompilerMessageType::WARNING, + ComponentCompilerMessageType::Warning, warnmsg ); } } + auto typeInfo = scriptModule->GetObjectTypeByIndex(0); + if (angel->IsComponentDefinition(typeInfo)) { + if (typeInfo->GetFactoryCount() > 0) { + rv.messages.emplace_back( + 1, 1, + ComponentCompilerMessageType::Info, + "Constructors are stripped from binary, do not use constructors!" + ); + } - for (auto i = 0; i < declaredObjectCount; i++) { - auto typeInfo = scriptModule->GetObjectTypeByIndex(i); - if (angel->IsComponentDefinition(typeInfo)) { - if (typeInfo->GetFactoryCount() > 0) { - rv.messages.emplace_back( - 1, 1, - ComponentCompilerMessageType::INFO, - "Constructors are stripped from binary, do not use constructors!" - ); - } - - rv.deserializedComponent.name = typeInfo->GetName(); - rv.deserializedComponent.declaration = content; - - int fieldCount = typeInfo->GetPropertyCount(); - for (int j = 0; j < fieldCount; j++) { - PropertyData d; - const char* propName; - typeInfo->GetProperty(j, &propName, &d.typeId, &d.isPrivate, &d.isProtected, &d.offset, &d.isReference, &d.accessMask, &d.compositeOffset, &d.isCompositeIndirect); - d.name = propName; - d.typeInfo = typeInfo; + rv.deserializedComponent.name = typeInfo->GetName(); + rv.deserializedComponent.declaration = content; - switch (d.typeId) { - case asTYPEID_VOID: d.typeName = "void"; break; - case asTYPEID_BOOL: d.typeName = "bool"; break; - case asTYPEID_INT8: d.typeName = "int8"; break; - case asTYPEID_INT16: d.typeName = "int16"; break; - case asTYPEID_INT32: d.typeName = "int"; break; - case asTYPEID_INT64: d.typeName = "long"; break; - case asTYPEID_UINT8: d.typeName = "uint8"; break; - case asTYPEID_UINT16: d.typeName = "uint16"; break; - case asTYPEID_UINT32: d.typeName = "unsigned int"; break; - case asTYPEID_UINT64: d.typeName = "unsigned long"; break; - case asTYPEID_FLOAT: d.typeName = "float"; break; - case asTYPEID_DOUBLE: d.typeName = "double"; break; - case asTYPEID_OBJHANDLE: d.typeName = "ptr"; break; - case asTYPEID_HANDLETOCONST: d.typeName = "const ptr"; break; - case asTYPEID_MASK_OBJECT: d.typeName = "mask"; break; - case asTYPEID_APPOBJECT: d.typeName = "native object"; break; - case asTYPEID_SCRIPTOBJECT: d.typeName = "angel object"; break; - case asTYPEID_TEMPLATE: d.typeName = "template"; break; - case asTYPEID_MASK_SEQNBR: d.typeName = "mask sequence"; break; - default: d.typeName = scriptEngine.GetTypeInfoById(d.typeId)->GetName(); break; - } + int fieldCount = typeInfo->GetPropertyCount(); + for (int j = 0; j < fieldCount; j++) { + PropertyData d; + const char* propName; + typeInfo->GetProperty(j, &propName, &d.typeId, &d.isPrivate, &d.isProtected, &d.offset, &d.isReference, &d.accessMask, &d.compositeOffset, &d.isCompositeIndirect); + d.name = propName; + d.typeInfo = typeInfo; - rv.deserializedComponent.fields.emplace_back(d.typeName, d.name); + switch (d.typeId) { + case asTYPEID_VOID: d.typeName = "void"; break; + case asTYPEID_BOOL: d.typeName = "bool"; break; + case asTYPEID_INT8: d.typeName = "int8"; break; + case asTYPEID_INT16: d.typeName = "int16"; break; + case asTYPEID_INT32: d.typeName = "int"; break; + case asTYPEID_INT64: d.typeName = "long"; break; + case asTYPEID_UINT8: d.typeName = "uint8"; break; + case asTYPEID_UINT16: d.typeName = "uint16"; break; + case asTYPEID_UINT32: d.typeName = "unsigned int"; break; + case asTYPEID_UINT64: d.typeName = "unsigned long"; break; + case asTYPEID_FLOAT: d.typeName = "float"; break; + case asTYPEID_DOUBLE: d.typeName = "double"; break; + case asTYPEID_OBJHANDLE: d.typeName = "ptr"; break; + case asTYPEID_HANDLETOCONST: d.typeName = "const ptr"; break; + case asTYPEID_MASK_OBJECT: d.typeName = "mask"; break; + case asTYPEID_APPOBJECT: d.typeName = "native object"; break; + case asTYPEID_SCRIPTOBJECT: d.typeName = "angel object"; break; + case asTYPEID_TEMPLATE: d.typeName = "template"; break; + case asTYPEID_MASK_SEQNBR: d.typeName = "mask sequence"; break; + default: d.typeName = scriptEngine.GetTypeInfoById(d.typeId)->GetName(); break; } - break; + + rv.deserializedComponent.fields.emplace_back(d.typeName, d.name); } } @@ -314,7 +310,7 @@ ComponentDeserializationResult DeserializeComponent(const std::string_view data) DOA_LOG_ERROR("Component deserialization failed! Couldn't find a component definition!"); return ComponentDeserializationResult{ .erred{ true } }; } - for (auto i = 0; i < declaredObjectCount; i++) { + for (decltype(declaredObjectCount) i = 0; i < declaredObjectCount; i++) { auto typeInfo = scriptModule->GetObjectTypeByIndex(i); if (!angel->IsComponentDefinition(typeInfo)) { rv.deserializedComponent.name = typeInfo->GetName(); diff --git a/Engine/ComponentDeserializer.hpp b/Engine/ComponentDeserializer.hpp index ea7a9ac7..de82b07e 100644 --- a/Engine/ComponentDeserializer.hpp +++ b/Engine/ComponentDeserializer.hpp @@ -7,10 +7,9 @@ struct FNode; enum class ComponentCompilerMessageType { - INFO, - WARNING, - ERROR, - ComponentCompilerMessageType_COUNT + Info, + Warning, + Error, }; struct ComponentCompilerMessage { int lineNo, columnNo; diff --git a/Engine/Core.cpp b/Engine/Core.cpp index 1b76ef03..3163fc89 100644 --- a/Engine/Core.cpp +++ b/Engine/Core.cpp @@ -1,13 +1,13 @@ #include "Core.hpp" -#include +#include + #include #include #include "Angel.hpp" #include "Scene.hpp" #include "Window.hpp" -#include "FrameBuffer.hpp" #include "Log.hpp" #include "ImGuiRenderer.hpp" #include "Assets.hpp" @@ -17,30 +17,56 @@ #include "ProjectSerializer.hpp" #include "ProjectDeserializer.hpp" -static void message_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, GLchar const* message, void const* user_param); - -const CorePtr& Core::CreateCore(Resolution resolution, const char* title, bool isFullscreen, const char* windowIcon, bool renderOffscreen) { -#pragma region GLFW and Core/Window/Input Initialization - glfwInit(); - _this = CorePtr(new Core, DeleteCore); - _this->window = Window::CreateWindow(resolution, title, isFullscreen, windowIcon); - _this->input = CreateInput(); +#include +#include + +const CorePtr& Core::CreateCore(GraphicsBackend gBackend, WindowBackend wBackend, const ContextWindowCreationParams& params) { + std::set_new_handler(HandleNew); +#pragma region Core/Window/Input/ImGui Initialization + Monitors::Initialize(); + CorePtr& core = *const_cast(&GetCore()); // cast-away const to initialize internals. GetCore never instantiates a const Core! + + switch (wBackend) { + using enum WindowBackend; + case GLFW: + core->window = std::make_unique(gBackend, params); + break; +#ifdef SDL_SUPPORT + case SDL: + core->window = std::make_unique(gBackend, params); + break; +#endif + default: + std::unreachable(); + }; + + IWindow& window = *core->window.get(); + core->input = std::make_unique(window); + ImGuiInit(window); + ImGuiSetUpWindowIcons(params.IconPack); #pragma endregion #pragma region GLEW Initialization - glewExperimental = GL_TRUE; - glewInit(); + if (window.IsSoftwareRendererContextWindow() || window.IsOpenGLContextWindow()) { + glewExperimental = GL_TRUE; + GLenum error = glewInit(); + if (error != GLEW_OK) { + DOA_LOG_FATAL("GLEW couldn't initialize! Reason: %s", glewGetErrorString(error)); + std::abort(); + } + } #pragma endregion -#pragma region System Information - DOA_LOG_INFO("OpenGL version: %s", glGetString(GL_VERSION)); - DOA_LOG_INFO("GLSL version: %s", glGetString(GL_SHADING_LANGUAGE_VERSION)); - DOA_LOG_INFO("Vendor: %s", glGetString(GL_VENDOR)); - DOA_LOG_INFO("GPU: %s", glGetString(GL_RENDERER)); +#pragma region Graphics Backend Initialization + Graphics::ChangeGraphicsBackend(gBackend); #pragma endregion #pragma region Angel Initialization - _this->angel = std::make_unique(); + core->angel = std::make_unique(); +#pragma endregion + +#pragma region GPU Resource Allocator Initialization + core->gpuBridge = std::make_unique(); #pragma endregion #pragma region Built-in Stuff Initialization @@ -178,46 +204,66 @@ const CorePtr& Core::CreateCore(Resolution resolution, const char* title, bool i */ #pragma endregion - if (renderOffscreen) { - FrameBufferBuilder builder; - builder.SetResolution(resolution); - builder.AddColorAttachment(OpenGL::RGB8); - builder.SetDepthStencilAttachment(OpenGL::DEPTH24_STENCIL8); - _this->offscreenBuffer = builder.BuildUnique(); - } -#pragma endregion + return core; +} +const CorePtr& Core::GetCore() { + static CorePtr core{ new Core, DeleteCore }; + return core; +} +void Core::DestroyCore() { + Monitors::DeInitialize(); + ImGuiClean(); + CorePtr& core = *const_cast(&GetCore()); // cast-away const to destroy instance. GetCore never instantiates a const Core! + core->Stop(); + core.reset(); +} -#pragma region KHR_debug -#ifdef DEBUG - glEnable(GL_DEBUG_OUTPUT); - glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); - glDebugMessageCallback(message_callback, nullptr); - glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_NOTIFICATION, 0, nullptr, GL_FALSE); +bool Core::IsAnyContextInitialized() const noexcept { + return window && + (window->IsSoftwareRendererContextWindow() || + window->IsOpenGLContextWindow() || + window->IsVulkanContextWindow() || + window->IsDirect3D12ContextWindow() || + window->IsDirect3D11ContextWindow()); +} +bool Core::IsSoftwareRendererInitialized() const noexcept { + return window && window->IsSoftwareRendererContextWindow(); +} +#if defined(OPENGL_4_6_SUPPORT) || defined(OPENGL_3_3_SUPPORT) +bool Core::IsOpenGLInitialized() const noexcept { + return window && window->IsOpenGLContextWindow(); +} #endif -#pragma endregion - - return _this; +#ifdef VULKAN_SUPPORT +bool Core::IsVulkanInitialized() const noexcept { + return window && window->IsVulkanContextWindow(); } -const CorePtr& Core::GetCore() { return _this; } -void Core::DestroyCore() { - _this->Stop(); - _this.reset(); +#endif +#ifdef DIRECT3D_12_SUPPORT +bool Core::IsDirect3D12Initialized() const noexcept { + return window && window->IsDirect3D12ContextWindow(); } -void Core::DeleteCore(Core* core) { delete core; } +#endif +#ifdef DIRECT3D_11_SUPPORT +bool Core::IsDirect3D11Initialized() const noexcept { + return window && window->IsDirect3D11ContextWindow(); +} +#endif bool Core::IsRunning() const { return running; } bool Core::IsPlaying() const { return playing; } void Core::SetPlaying(bool playing) { this->playing = playing; } std::unique_ptr& Core::GetAngel() { return angel; } -WindowPtr& Core::GetWindow() { return window; } +std::unique_ptr& Core::GetWindow() { return window; } std::unique_ptr& Core::GetInput() { return input; } -std::unique_ptr& Core::GetFrameBuffer() { return offscreenBuffer; } void Core::CreateAndLoadProject(std::string_view workspace, std::string_view name) { UnloadProject(); + + gpuBridge = std::make_unique(); project = std::make_unique(std::string(workspace), std::string(name)); - assets = std::make_unique(*(project.get())); + assets = std::make_unique(*project.get(), *gpuBridge.get()); assets->EnsureDeserialization(); } void Core::LoadProject(const std::string& path) { @@ -229,8 +275,9 @@ void Core::LoadProject(const std::string& path) { DOA_LOG_FATAL("Could not deserialize project @%s", path.c_str()); std::exit(1); } + gpuBridge = std::make_unique(); project = std::make_unique(std::move(pdr.project)); - assets = std::make_unique(*project.get()); + assets = std::make_unique(*project.get(), *gpuBridge.get()); assets->EnsureDeserialization(); project->OpenStartupScene(); } @@ -249,23 +296,17 @@ void Core::SaveLoadedProjectToDisk() const { bool Core::HasLoadedProject() { return project != nullptr; } std::unique_ptr& Core::GetAssets() { return assets; } +std::unique_ptr& Core::GetAssetGPUBridge() { return gpuBridge; } void Core::Start() { - static bool renderingOffscreen = offscreenBuffer != nullptr; + // TODO get rid of glfwGetTime here! float lastTime = static_cast(glfwGetTime()); float currentTime; - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_LEQUAL); - - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - glFrontFace(GL_CCW); - running = true; while (running) { + // TODO get rid of glfwGetTime here! currentTime = static_cast(glfwGetTime()); - glViewport(0, 0, window->GetContentResolution().Width, window->GetContentResolution().Height); float delta = currentTime - lastTime; @@ -273,20 +314,10 @@ void Core::Start() { for (auto [id, attachment] : _attachments) { attachment->BeforeFrame(project.get()); } + Scene& scene = project->GetOpenScene(); - if (playing) { - scene.Update(delta); - } - if (renderingOffscreen) { - offscreenBuffer->Bind(); - } - glClearColor(scene.ClearColor.r, scene.ClearColor.g, scene.ClearColor.b, 1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - scene.Render(); - if (renderingOffscreen) { - FrameBuffer::BackBuffer().Bind(); - glViewport(0, 0, window->GetContentResolution().Width, window->GetContentResolution().Height); - } + scene.ExecuteSystems(playing, delta); + for (auto [id, attachment] : _attachments) { attachment->AfterFrame(project.get()); } @@ -294,54 +325,21 @@ void Core::Start() { ImGuiRender(delta); - glfwSwapBuffers(window->GetPlatformWindow()); - glfwPollEvents(); + window->SwapBuffers(); + + input->Step(); + window->PollEvents(); lastTime = currentTime; - if (glfwWindowShouldClose(window->GetPlatformWindow())) { - Stop(); - } + if (window->ShouldClose()) { Stop(); } } } void Core::Stop() { running = false; } -static void message_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, GLchar const* message, void const* user_param) { - auto const src_str = [source]() { - switch (source) { - case GL_DEBUG_SOURCE_API: return "API"; - case GL_DEBUG_SOURCE_WINDOW_SYSTEM: return "WINDOW SYSTEM"; - case GL_DEBUG_SOURCE_SHADER_COMPILER: return "SHADER COMPILER"; - case GL_DEBUG_SOURCE_THIRD_PARTY: return "THIRD PARTY"; - case GL_DEBUG_SOURCE_APPLICATION: return "APPLICATION"; - case GL_DEBUG_SOURCE_OTHER: return "OTHER"; - default: return "DEFAULT"; - } - }(); - - auto const type_str = [type]() { - switch (type) { - case GL_DEBUG_TYPE_ERROR: return "ERROR"; - case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: return "DEPRECATED_BEHAVIOR"; - case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: return "UNDEFINED_BEHAVIOR"; - case GL_DEBUG_TYPE_PORTABILITY: return "PORTABILITY"; - case GL_DEBUG_TYPE_PERFORMANCE: return "PERFORMANCE"; - case GL_DEBUG_TYPE_MARKER: return "MARKER"; - case GL_DEBUG_TYPE_OTHER: return "OTHER"; - default: return "DEFAULT"; - } - }(); - - auto const severity_str = [severity]() { - switch (severity) { - case GL_DEBUG_SEVERITY_NOTIFICATION: return "NOTIFICATION"; - case GL_DEBUG_SEVERITY_LOW: return "LOW"; - case GL_DEBUG_SEVERITY_MEDIUM: return "MEDIUM"; - case GL_DEBUG_SEVERITY_HIGH: return "HIGH"; - default: return "DEFAULT"; - } - }(); - - DOA_LOG_OPENGL("%s, %s, %s, %d: %s", src_str, type_str, severity_str, id, message); +void Core::DeleteCore(Core* core) { delete core; } +void Core::HandleNew() { + DOA_LOG_FATAL("Memory allocation failed, terminating"); + std::set_new_handler(nullptr); } \ No newline at end of file diff --git a/Engine/Core.hpp b/Engine/Core.hpp index 18c8e40a..015c3f28 100644 --- a/Engine/Core.hpp +++ b/Engine/Core.hpp @@ -13,7 +13,9 @@ #include "Assets.hpp" #include "Window.hpp" #include "Project.hpp" -#include "FrameBuffer.hpp" +#include "AssetBridge.hpp" + +#include struct Core; struct Resolution; @@ -22,18 +24,32 @@ using CoreDeleter = std::function; using CorePtr = std::unique_ptr; struct Core { - static const CorePtr& CreateCore(Resolution resolution, const char* title, bool isFullscreen = false, const char* windowIcon = nullptr, bool renderOffscreen = false); + static const CorePtr& CreateCore(GraphicsBackend gBackend, WindowBackend wBackend, const ContextWindowCreationParams& params); static const CorePtr& GetCore(); static void DestroyCore(); + bool IsAnyContextInitialized() const noexcept; + bool IsSoftwareRendererInitialized() const noexcept; +#if defined(OPENGL_4_6_SUPPORT) || defined(OPENGL_3_3_SUPPORT) + bool IsOpenGLInitialized() const noexcept; +#endif +#ifdef VULKAN_SUPPORT + bool IsVulkanInitialized() const noexcept; +#endif +#ifdef DIRECT3D_12_SUPPORT + bool IsDirect3D12Initialized() const noexcept; +#endif +#ifdef DIRECT3D_11_SUPPORT + bool IsDirect3D11Initialized() const noexcept; +#endif + bool IsRunning() const; void SetPlaying(bool playing); bool IsPlaying() const; std::unique_ptr& GetAngel(); - WindowPtr& GetWindow(); + std::unique_ptr& GetWindow(); std::unique_ptr& GetInput(); - std::unique_ptr& GetFrameBuffer(); void CreateAndLoadProject(std::string_view workspace, std::string_view name); void LoadProject(const std::string& path); @@ -44,22 +60,21 @@ struct Core { bool HasLoadedProject(); std::unique_ptr& GetAssets(); + std::unique_ptr& GetAssetGPUBridge(); void Start(); void Stop(); private: - inline static CorePtr _this{ nullptr }; - bool running{ false }; bool playing{ false }; - std::unique_ptr angel{ nullptr }; - WindowPtr window{ nullptr }; - std::unique_ptr input{ nullptr }; - std::unique_ptr offscreenBuffer{ nullptr }; - std::unique_ptr project{ nullptr }; - std::unique_ptr assets{ nullptr }; + std::unique_ptr angel{}; + std::unique_ptr window{}; + std::unique_ptr input{}; + std::unique_ptr project{}; + std::unique_ptr assets{}; + std::unique_ptr gpuBridge{}; Core() = default; ~Core() = default; @@ -69,6 +84,7 @@ struct Core { Core& operator=(Core&&) = delete; static void DeleteCore(Core* core); + static void HandleNew(); /* Core Attachment */ public: @@ -95,6 +111,5 @@ struct Core { private: entt::dense_map> _attachments{}; - //std::vector> _attachments{}; /* Core Attachment */ }; diff --git a/Engine/DataTypes.cpp b/Engine/DataTypes.cpp new file mode 100644 index 00000000..abd0a9e6 --- /dev/null +++ b/Engine/DataTypes.cpp @@ -0,0 +1 @@ +#include \ No newline at end of file diff --git a/Engine/DataTypes.hpp b/Engine/DataTypes.hpp new file mode 100644 index 00000000..0b5d2340 --- /dev/null +++ b/Engine/DataTypes.hpp @@ -0,0 +1,10 @@ +#include +#include +#include + +using OwningPointerToRawData = std::unique_ptr; +using NonOwningPointerToRawData = std::byte*; +using NonOwningPointerToConstRawData = const std::byte*; +using RawData = std::vector; +using RawDataView = std::span; +using RawDataWriteableView = std::span; \ No newline at end of file diff --git a/Engine/Event.hpp b/Engine/Event.hpp index fc896210..10feff06 100644 --- a/Engine/Event.hpp +++ b/Engine/Event.hpp @@ -11,7 +11,7 @@ struct Event { Event& operator+=(std::function callback) noexcept; void operator()(Args... args) noexcept; private: - eventpp::CallbackList callbackList; + mutable eventpp::CallbackList callbackList; }; template diff --git a/Engine/FileNode.cpp b/Engine/FileNode.cpp index f114d61d..a67f1097 100644 --- a/Engine/FileNode.cpp +++ b/Engine/FileNode.cpp @@ -9,9 +9,15 @@ #include "Project.hpp" FNode::ChildrenList::ChildrenList(std::vector>& children) noexcept : children(children) {} + +FNode& FNode::ChildrenList::operator[](std::size_t idx) { return *children.operator[](idx).get(); } +const FNode& FNode::ChildrenList::operator[](std::size_t idx) const { return *children.operator[](idx).get(); } + FNode::ChildrenList::Iterator FNode::ChildrenList::begin() { return { std::to_address(children.begin()) }; } FNode::ChildrenList::Iterator FNode::ChildrenList::end() { return { std::to_address(children.end()) }; } +size_t FNode::ChildrenList::size() const { return children.size(); } + FNode::FNode(const FNodeCreationParams& params) noexcept : owner(params.owner), parent(params.parent), @@ -55,7 +61,7 @@ std::filesystem::path FNode::FolderPath() const { return parent->Path(); } else { auto pos = fullName.find_last_of(std::filesystem::path::preferred_separator); - if (pos != -1) { + if (pos != std::string::npos) { return fullName.substr(0, pos); } else { DOA_LOG_ERROR("FNode::FolderPath cannot calculate folder path!"); @@ -126,7 +132,12 @@ bool FNode::ReadContent() const { std::ifstream file(Path(), std::ifstream::in | std::ifstream::binary); if (file.is_open()) { - std::getline(file, content, '\0'); + file.seekg(0, std::ios::end); + std::streampos fileSize = file.tellg(); + file.seekg(0, std::ios::beg); + + content.resize(fileSize); + file.read(content.data(), fileSize); file.close(); return true; } diff --git a/Engine/FileNode.hpp b/Engine/FileNode.hpp index b5db72b7..b73271ef 100644 --- a/Engine/FileNode.hpp +++ b/Engine/FileNode.hpp @@ -24,27 +24,32 @@ struct FNode { using iterator_category = std::forward_iterator_tag; using difference_type = std::ptrdiff_t; using value_type = T; - using ptr = value_type*; - using ref = value_type&; + using pointer = value_type*; + using reference = value_type&; using smart = const std::unique_ptr>; - Iterator(smart* ptr) : _ptr(ptr) {} + Iterator(smart* ptr) : ptr(ptr) {} - ref operator*() const { return *(_ptr->get()); } - ptr operator->() { return _ptr->get(); } - Iterator& operator++() { _ptr++; return *this; } + reference operator*() const { return *(ptr->get()); } + pointer operator->() { return ptr->get(); } + Iterator& operator++() { ptr++; return *this; } Iterator operator++(int) { Iterator tmp = *this; ++(*this); return tmp; } friend bool operator==(const Iterator& a, const Iterator& b) = default; private: - smart* _ptr; + smart* ptr; }; ChildrenList(std::vector>& children) noexcept; + FNode& operator[](std::size_t idx); + const FNode& operator[](std::size_t idx) const; + Iterator begin(); Iterator end(); + size_t size() const; + private: std::vector>& children; }; @@ -126,7 +131,7 @@ struct FNode { bool isDirectory{ false }; - /* FNode guarantees references are never invalid unless DeleteChildNode is called therefore the unique_ptr's */ + /* FNode guarantees references are always valid unless DeleteChildNode is called therefore, the unique_ptr's */ /* are used here to prevent FNode objects from sliding around on deletions/reallocations of vector. */ std::vector> children{}; diff --git a/Engine/FrameBuffer.cpp b/Engine/FrameBuffer.cpp deleted file mode 100644 index e0d3e49c..00000000 --- a/Engine/FrameBuffer.cpp +++ /dev/null @@ -1,206 +0,0 @@ -#include - -#include - -#include -#include -#include - -FrameBufferBuilder& FrameBufferBuilder::Clear() noexcept { - resolution = {}; - msaaSamples = {}; - colorAttachmentCount = {}; - colorFormats = {}; - renderFormat = {}; - return *this; -} - -FrameBufferBuilder& FrameBufferBuilder::SetResolution(Resolution resolution) noexcept { this->resolution = resolution; return *this; } -FrameBufferBuilder& FrameBufferBuilder::SetMSAASamples(int samples) noexcept { msaaSamples = samples; return *this; } -FrameBufferBuilder& FrameBufferBuilder::AddColorAttachment(OpenGL::TextureFormat format, OpenGL::TextureWrapMode mode) noexcept { - if (colorAttachmentCount < colorFormats.size()) { - auto& fmt = colorFormats[colorAttachmentCount]; - fmt.format = format; - fmt.mode = mode; - colorAttachmentCount++; - } else { - DOA_LOG_WARNING("Trying to add color but builder already has max amount!"); - } - return *this; -} -FrameBufferBuilder& FrameBufferBuilder::SetDepthStencilAttachment(OpenGL::DepthFormat format) noexcept { renderFormat = format; return *this; } -FrameBufferBuilder& FrameBufferBuilder::SetDepthStencilAttachment(OpenGL::StencilFormat format) noexcept { renderFormat = format; return *this; } -FrameBufferBuilder& FrameBufferBuilder::SetDepthStencilAttachment(OpenGL::DepthStencilFormat format) noexcept { renderFormat = format; return *this; } - -FrameBuffer FrameBufferBuilder::Build() noexcept { return { resolution, msaaSamples, colorFormats, colorAttachmentCount, renderFormat }; } -FrameBuffer* FrameBufferBuilder::BuildRaw() noexcept { return new FrameBuffer{ resolution, msaaSamples, colorFormats, colorAttachmentCount, renderFormat }; } -std::unique_ptr FrameBufferBuilder::BuildUnique() noexcept { return std::make_unique(resolution, msaaSamples, colorFormats, colorAttachmentCount, renderFormat); } - -FrameBuffer FrameBuffer::BackBuffer() noexcept { return {}; } - -FrameBuffer::FrameBuffer(Resolution resolution, int msaaSamples, std::array colorFormats, int colorAttachmentCount, GLuint renderFormat) noexcept : - resolution(resolution), - msaaSamples(msaaSamples), - colorAttachmentCount(colorAttachmentCount) { - auto a = OpenGL::BufferBit::COLOR_BUFFER_BIT | OpenGL::BufferBit::DEPTH_BUFFER_BIT; - hasDepth = - renderFormat == OpenGL::DepthFormat::DEPTH_COMPONENT16 || - renderFormat == OpenGL::DepthFormat::DEPTH_COMPONENT24 || - renderFormat == OpenGL::DepthFormat::DEPTH_COMPONENT32 || - renderFormat == OpenGL::DepthFormat::DEPTH_COMPONENT32F || - renderFormat == OpenGL::DepthStencilFormat::DEPTH24_STENCIL8 || - renderFormat == OpenGL::DepthStencilFormat::DEPTH32F_STENCIL8; - hasStencil = - renderFormat == OpenGL::StencilFormat::STENCIL_INDEX1 || - renderFormat == OpenGL::StencilFormat::STENCIL_INDEX4 || - renderFormat == OpenGL::StencilFormat::STENCIL_INDEX8 || - renderFormat == OpenGL::StencilFormat::STENCIL_INDEX16 || - renderFormat == OpenGL::DepthStencilFormat::DEPTH24_STENCIL8 || - renderFormat == OpenGL::DepthStencilFormat::DEPTH32F_STENCIL8; - - AllocateGPU(colorFormats, renderFormat); -} -FrameBuffer::~FrameBuffer() noexcept { - DeallocateGPU(); -} -FrameBuffer::FrameBuffer(FrameBuffer&& other) noexcept : - frameBufferObject(std::exchange(other.frameBufferObject, {})), - resolution(std::exchange(other.resolution, {})), - colorAttachments(std::exchange(other.colorAttachments, {})), - colorAttachmentCount(std::exchange(other.colorAttachmentCount, {})), - renderBuffer(std::exchange(other.renderBuffer, {})), - hasDepth(std::exchange(other.hasDepth, {})), - hasStencil(std::exchange(other.hasStencil, {})) {} -FrameBuffer& FrameBuffer::operator=(FrameBuffer&& other) noexcept { - frameBufferObject = std::exchange(other.frameBufferObject, {}); - resolution = std::exchange(other.resolution, {}); - colorAttachments = std::exchange(other.colorAttachments, {}); - colorAttachmentCount = std::exchange(other.colorAttachmentCount, {}); - renderBuffer = std::exchange(other.renderBuffer, {}); - hasDepth = std::exchange(other.hasDepth, {}); - hasStencil = std::exchange(other.hasStencil, {}); - return *this; -} - -GLuint FrameBuffer::GetHandle() const noexcept { return frameBufferObject; } -GLuint FrameBuffer::GetColorAttachment(unsigned int index) const noexcept { return colorAttachments[index]; } -GLuint FrameBuffer::GetDepthAttachment() const noexcept { - if (!HasDepthAttachment()) { return 0; } - return renderBuffer; -} -GLuint FrameBuffer::GetStencilAttachment() const noexcept { - if (!HasStencilAttachment()) { return 0; } - return renderBuffer; -} -bool FrameBuffer::HasColorAttachment() const noexcept { return colorAttachmentCount > 0; } -bool FrameBuffer::HasDepthAttachment() const noexcept { return hasDepth; } -bool FrameBuffer::HasStencilAttachment() const noexcept { return hasStencil; } - -void FrameBuffer::Bind() const noexcept { - glBindFramebuffer(GL_FRAMEBUFFER, frameBufferObject); - glViewport(0, 0, resolution.Width, resolution.Height); -} -void FrameBuffer::BindDraw() const noexcept { - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBufferObject); - glViewport(0, 0, resolution.Width, resolution.Height); -} -void FrameBuffer::BindRead() const noexcept { - glBindFramebuffer(GL_READ_FRAMEBUFFER, frameBufferObject); -} - -void FrameBuffer::BlitTo(const FrameBuffer& target, OpenGL::BufferBit buffers, Region source, Region destination) const noexcept { - if (source.X < 0 || source.Y < 0 || source.Width < 0 || source.Height < 0) { - source = { 0, 0, resolution.Width, resolution.Height }; - } - if (destination.X < 0 || destination.Y < 0 || destination.Width < 0 || destination.Height < 0) { - destination = { 0, 0, target.resolution.Width, target.resolution.Height }; - } - glBlitNamedFramebuffer( - frameBufferObject, - target.frameBufferObject, - source.X, source.Y, - source.X + source.Width, source.Y + source.Height, - destination.X, destination.Y, - destination.X + destination.Width, destination.Y + destination.Height, - buffers, - GL_LINEAR - ); -} - -void FrameBuffer::ClearBuffers(float r, float g, float b, float a, float depth, int stencil) noexcept { - ClearColorBuffer(r, g, b, a); - ClearDepthBuffer(depth); - ClearStencilBuffer(stencil); -} -void FrameBuffer::ClearColorBuffer(float r, float g, float b, float a) noexcept { - const float color[] { r, g, b, a }; - for (int i = 0; i < colorAttachmentCount; i++) { - glClearTexImage(colorAttachments[i], 0, GL_RGBA, GL_FLOAT, &color); - } -} -void FrameBuffer::ClearDepthBuffer(float depth) noexcept { - if (!HasDepthAttachment()) { return; } - glClearNamedFramebufferfv(frameBufferObject, GL_DEPTH, 0, &depth); -} -void FrameBuffer::ClearStencilBuffer(int stencil) noexcept { - if (!HasStencilAttachment()) { return; } - glClearNamedFramebufferiv(frameBufferObject, GL_STENCIL, 0, &stencil); -} - -void FrameBuffer::AllocateGPU(std::array colorFormats, GLuint renderFormat) noexcept { - glCreateFramebuffers(1, &frameBufferObject); - - if (msaaSamples > 1) { - /* Multisampled FBO request*/ - if (HasColorAttachment()) { - glCreateTextures(GL_TEXTURE_2D_MULTISAMPLE, colorAttachmentCount, &colorAttachments[0]); - for (unsigned int i = 0; i < colorAttachmentCount; i++) { - glTextureStorage2DMultisample(colorAttachments[i], msaaSamples, colorFormats[i].format, resolution.Width, resolution.Height, GL_TRUE); - } - } - if (HasDepthAttachment() || HasStencilAttachment()) { - /* Create depth/stencil attachment render buffer */ - glCreateRenderbuffers(1, &renderBuffer); - glNamedRenderbufferStorageMultisample(renderBuffer, msaaSamples, renderFormat, resolution.Width, resolution.Height); - - glNamedFramebufferRenderbuffer(frameBufferObject, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, renderBuffer); - } - } else { - /* Singlesampled FBO request*/ - if (colorAttachmentCount > 0) { - /* Create color attachment texture */ - glCreateTextures(GL_TEXTURE_2D, colorAttachmentCount, &colorAttachments[0]); - for (unsigned int i = 0; i < colorAttachmentCount; i++) { - glTextureParameteri(colorAttachments[i], GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTextureParameteri(colorAttachments[i], GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTextureParameteri(colorAttachments[i], GL_TEXTURE_WRAP_S, colorFormats[i].mode); - glTextureParameteri(colorAttachments[i], GL_TEXTURE_WRAP_T, colorFormats[i].mode); - glTextureStorage2D(colorAttachments[i], 1, colorFormats[i].format, resolution.Width, resolution.Height); - } - } - if (HasDepthAttachment() || HasStencilAttachment()) { - /* Create depth/stencil attachment render buffer */ - glCreateRenderbuffers(1, &renderBuffer); - glNamedRenderbufferStorage(renderBuffer, renderFormat, resolution.Width, resolution.Height); - - glNamedFramebufferRenderbuffer(frameBufferObject, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, renderBuffer); - } - } - - static std::array drawBufs; - for (unsigned int i = 0; i < colorAttachmentCount; i++) { - glNamedFramebufferTexture(frameBufferObject, GL_COLOR_ATTACHMENT0 + i, colorAttachments[i], 0); - drawBufs[i] = GL_COLOR_ATTACHMENT0 + i; - } - glNamedFramebufferDrawBuffers(frameBufferObject, colorAttachmentCount, &drawBufs[0]); - - GLenum error = glCheckNamedFramebufferStatus(frameBufferObject, GL_FRAMEBUFFER); - if (error != GL_FRAMEBUFFER_COMPLETE) { - std::cout << "Framebuffer couldn't initialize! ERR_CODE: " << error << std::endl; - } -} -void FrameBuffer::DeallocateGPU() noexcept { - glDeleteFramebuffers(1, &frameBufferObject); - glDeleteTextures(colorAttachmentCount, &colorAttachments[0]); - glDeleteRenderbuffers(1, &renderBuffer); -} \ No newline at end of file diff --git a/Engine/FrameBuffer.hpp b/Engine/FrameBuffer.hpp deleted file mode 100644 index 42a9401c..00000000 --- a/Engine/FrameBuffer.hpp +++ /dev/null @@ -1,85 +0,0 @@ -#pragma once - -#include -#include -#include - -#include - -#include -#include -#include - -struct FrameBuffer; - -struct FrameBufferBuilder { - struct ColorAttachment { - GLuint format; - GLuint mode; - }; - - FrameBufferBuilder& Clear() noexcept; - - FrameBufferBuilder& SetResolution(Resolution resolution) noexcept; - FrameBufferBuilder& SetMSAASamples(int samples) noexcept; - FrameBufferBuilder& AddColorAttachment(OpenGL::TextureFormat format, OpenGL::TextureWrapMode mode = OpenGL::TextureWrapMode::REPEAT) noexcept; - FrameBufferBuilder& SetDepthStencilAttachment(OpenGL::DepthFormat format) noexcept; - FrameBufferBuilder& SetDepthStencilAttachment(OpenGL::StencilFormat format) noexcept; - FrameBufferBuilder& SetDepthStencilAttachment(OpenGL::DepthStencilFormat format) noexcept; - - FrameBuffer Build() noexcept; - FrameBuffer* BuildRaw() noexcept; - std::unique_ptr BuildUnique() noexcept; - -private: - Resolution resolution{}; - int msaaSamples{}; - std::array colorFormats{}; - int colorAttachmentCount{}; - GLuint renderFormat{}; -}; - -struct FrameBuffer { - - static FrameBuffer BackBuffer() noexcept; - - FrameBuffer() noexcept = default; - FrameBuffer(Resolution resolution, int msaaSamples, std::array colorFormats, int colorAttachmentCount, GLuint renderFormat) noexcept; - ~FrameBuffer() noexcept; - FrameBuffer(const FrameBuffer&) = delete; - FrameBuffer(FrameBuffer&& other) noexcept; - FrameBuffer& operator=(const FrameBuffer&) = delete; - FrameBuffer& operator=(FrameBuffer&& other) noexcept; - - GLuint GetHandle() const noexcept; - GLuint GetColorAttachment(unsigned int index = 0) const noexcept; - GLuint GetDepthAttachment() const noexcept; - GLuint GetStencilAttachment() const noexcept; - bool HasColorAttachment() const noexcept; - bool HasDepthAttachment() const noexcept; - bool HasStencilAttachment() const noexcept; - - void Bind() const noexcept; - void BindDraw() const noexcept; - void BindRead() const noexcept; - - void BlitTo(const FrameBuffer& target, OpenGL::BufferBit buffers = OpenGL::BufferBit::COLOR_BUFFER_BIT, Region source = { -1, -1, -1, -1 }, Region destination = { -1, -1, -1, -1 }) const noexcept; - - void ClearBuffers(float r = 0.0f, float g = 0.0f, float b = 0.0f, float a = 0.0f, float depth = 1.0f, int stencil = 0) noexcept; - void ClearColorBuffer(float r = 0.0f, float g = 0.0f, float b = 0.0f, float a = 0.0f) noexcept; - void ClearDepthBuffer(float depth = 1.0f) noexcept; - void ClearStencilBuffer(int stencil = 0) noexcept; - -private: - GLuint frameBufferObject{}; - - Resolution resolution{}; - int msaaSamples{}; - std::array colorAttachments{}; - int colorAttachmentCount{}; - GLuint renderBuffer{}; - bool hasDepth, hasStencil; - - void AllocateGPU(std::array colorFormats, GLuint renderFormat) noexcept; - void DeallocateGPU() noexcept; -}; diff --git a/Engine/GPUBuffer.cpp b/Engine/GPUBuffer.cpp new file mode 100644 index 00000000..e8f0f33a --- /dev/null +++ b/Engine/GPUBuffer.cpp @@ -0,0 +1,67 @@ +#include + +#include + +GPUBuffer::~GPUBuffer() noexcept { + Graphics::Destructors::Destruct(*this); +} +GPUBuffer::GPUBuffer(const GPUBuffer& other) noexcept { + *this = other; +} +GPUBuffer::GPUBuffer(GPUBuffer&& other) noexcept { + *this = std::move(other); +} +GPUBuffer& GPUBuffer::operator=(const GPUBuffer& other) noexcept { + GPUBufferBuilder builder; + builder +#ifdef DEBUG + .SetName(other.Name + " (Copy)") +#endif + .SetProperties(other.Properties); + auto&& [buffer, _] = builder.Build(); + + *this = std::move(buffer.value()); + Graphics::CopyBufferSubData(other, *this, SizeBytes); + return *this; +} +GPUBuffer& GPUBuffer::operator=(GPUBuffer&& other) noexcept { + std::swap(GLObjectID, other.GLObjectID); +#ifdef DEBUG + Name = std::move(other.Name); +#endif + Properties = std::exchange(other.Properties, {}); + SizeBytes = std::exchange(other.SizeBytes, {}); + return *this; +} + +bool GPUBuffer::IsDynamicStorage() const noexcept { return static_cast(Properties & BufferProperties::DynamicStorage); } +bool GPUBuffer::IsReadableFromCPU() const noexcept { return static_cast(Properties & BufferProperties::ReadableFromCPU); } +bool GPUBuffer::IsWriteableFromCPU() const noexcept { return static_cast(Properties & BufferProperties::WriteableFromCPU); } +bool GPUBuffer::IsPersistent() const noexcept { return static_cast(Properties & BufferProperties::Persistent); } +bool GPUBuffer::IsCoherent() const noexcept { return static_cast(Properties & BufferProperties::Coherent); } +bool GPUBuffer::IsCPUStorage() const noexcept { return static_cast(Properties & BufferProperties::CPUStorage); } + +GPUBufferBuilder& GPUBufferBuilder::SetName(std::string_view name) noexcept { +#ifdef DEBUG + this->name = name; +#endif + return *this; +} +GPUBufferBuilder& GPUBufferBuilder::SetProperties(BufferProperties properties) noexcept { + this->properties = properties; + return *this; +} +GPUBufferBuilder& GPUBufferBuilder::SetStorage(RawDataView dataView) noexcept { + size = dataView.size_bytes(); + data = dataView.data(); + return *this; +} +GPUBufferBuilder& GPUBufferBuilder::SetStorage(size_t sizeBytes, NonOwningPointerToConstRawData data) noexcept { + size = sizeBytes; + this->data = data; + return *this; +} + +std::pair, std::vector> GPUBufferBuilder::Build() noexcept { + return Graphics::Builders::Build(*this); +} \ No newline at end of file diff --git a/Engine/GPUBuffer.hpp b/Engine/GPUBuffer.hpp new file mode 100644 index 00000000..18a0152d --- /dev/null +++ b/Engine/GPUBuffer.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include + +struct GPUBuffer { + GLuint GLObjectID{}; +#ifdef DEBUG + std::string Name{}; +#endif + BufferProperties Properties; + size_t SizeBytes{}; + + bool IsDynamicStorage() const noexcept; + bool IsReadableFromCPU() const noexcept; + bool IsWriteableFromCPU() const noexcept; + bool IsPersistent() const noexcept; + bool IsCoherent() const noexcept; + bool IsCPUStorage() const noexcept; + + ND_GRAPHICS_COPYABLE_MOVEABLE_RESOURCE(GPUBuffer); +}; + +struct GPUBufferBuilder { + GPUBufferBuilder& SetName(std::string_view name) noexcept; + GPUBufferBuilder& SetProperties(BufferProperties properties) noexcept; + GPUBufferBuilder& SetStorage(RawDataView dataView) noexcept; + GPUBufferBuilder& SetStorage(size_t sizeBytes, NonOwningPointerToConstRawData data) noexcept; + + [[nodiscard]] std::pair, std::vector> Build() noexcept; + +private: +#ifdef DEBUG + std::string name{}; +#endif + BufferProperties properties{}; + size_t size{}; + NonOwningPointerToConstRawData data{}; + +public: + ND_GRAPHICS_BUILDER_RULE_OF_0(GPUBufferBuilder); + +private: +#ifdef OPENGL_4_6_SUPPORT + friend std::pair, std::vector> Graphics::OpenGL::Build(GPUBufferBuilder&) noexcept; +#endif +}; \ No newline at end of file diff --git a/Engine/GPUDescriptorSet.cpp b/Engine/GPUDescriptorSet.cpp new file mode 100644 index 00000000..53446c84 --- /dev/null +++ b/Engine/GPUDescriptorSet.cpp @@ -0,0 +1,36 @@ +#include + +#include +#include + +GPUDescriptorSetBuilder& GPUDescriptorSetBuilder::SetName(std::string_view name) noexcept { +#ifdef DEBUG + this->name = name; +#endif + return *this; +} +GPUDescriptorSetBuilder& GPUDescriptorSetBuilder::SetUniformBufferBinding(unsigned binding, const GPUBuffer& buffer) noexcept { + assert(idx < MaxDescriptorBinding && std::format("index out of range expected [0-{}), received {}", MaxDescriptorBinding, idx).c_str()); + bindings[idx].BindingSlot = binding; + bindings[idx].Descriptor.emplace(buffer); + idx++; + return *this; +} +GPUDescriptorSetBuilder& GPUDescriptorSetBuilder::SetStorageBufferBinding(unsigned binding, const GPUBuffer& buffer) noexcept { + assert(idx < MaxDescriptorBinding && std::format("index out of range expected [0-{}), received {}", MaxDescriptorBinding, idx).c_str()); + bindings[idx].BindingSlot = binding; + bindings[idx].Descriptor.emplace(buffer); + idx++; + return *this; +} +GPUDescriptorSetBuilder& GPUDescriptorSetBuilder::SetCombinedImageSamplerBinding(unsigned binding, const GPUTexture& texture, const GPUSampler& sampler) noexcept { + assert(idx < MaxDescriptorBinding && std::format("index out of range expected [0-{}), received {}", MaxDescriptorBinding, idx).c_str()); + bindings[idx].BindingSlot = binding; + bindings[idx].Descriptor.emplace(texture, sampler); + idx++; + return *this; +} + +std::pair, std::vector> GPUDescriptorSetBuilder::Build() noexcept { + return Graphics::Builders::Build(*this); +} \ No newline at end of file diff --git a/Engine/GPUDescriptorSet.hpp b/Engine/GPUDescriptorSet.hpp new file mode 100644 index 00000000..fa202398 --- /dev/null +++ b/Engine/GPUDescriptorSet.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include + +// Descriptor Set +enum class DescriptorType { + UniformBuffer, + StorageBuffer, + CombinedImageSampler, + // TODO implement these in the future + // Sampler, + // SampledImage, + // StorageImage, + // UniformTexelBuffer, + // StorageTexelBuffer, + // InputAttachment +}; +struct DescriptorBinding { + struct UniformBuffer { + std::reference_wrapper Buffer; + }; + struct StorageBuffer { + std::reference_wrapper Buffer; + }; + struct CombinedImageSampler { + std::reference_wrapper Texture; + std::reference_wrapper Sampler; + }; + + unsigned BindingSlot{}; + std::variant Descriptor{}; +}; + +struct GPUDescriptorSet { +#ifdef DEBUG + std::string Name{}; +#endif + std::array Bindings{}; +}; +struct GPUDescriptorSetBuilder { + GPUDescriptorSetBuilder& SetName(std::string_view name) noexcept; + GPUDescriptorSetBuilder& SetUniformBufferBinding(unsigned binding, const GPUBuffer& buffer) noexcept; + GPUDescriptorSetBuilder& SetStorageBufferBinding(unsigned binding, const GPUBuffer& buffer) noexcept; + GPUDescriptorSetBuilder& SetCombinedImageSamplerBinding(unsigned binding, const GPUTexture& texture, const GPUSampler& sampler) noexcept; + + [[nodiscard]] std::pair, std::vector> Build() noexcept; + +private: +#ifdef DEBUG + std::string name{}; +#endif + int idx{}; + std::array bindings{}; + +public: + ND_GRAPHICS_BUILDER_RULE_OF_0(GPUDescriptorSetBuilder); + +private: +#ifdef OPENGL_4_6_SUPPORT + friend std::pair, std::vector> Graphics::OpenGL::Build(GPUDescriptorSetBuilder&) noexcept; +#endif +}; \ No newline at end of file diff --git a/Engine/GPUFrameBuffer.cpp b/Engine/GPUFrameBuffer.cpp new file mode 100644 index 00000000..c5557990 --- /dev/null +++ b/Engine/GPUFrameBuffer.cpp @@ -0,0 +1,122 @@ +#include + +#include + +// RenderBuffer +GPURenderBuffer::~GPURenderBuffer() noexcept { + Graphics::Destructors::Destruct(*this); +} +GPURenderBuffer::GPURenderBuffer(GPURenderBuffer&& other) noexcept { + *this = std::move(other); +} +GPURenderBuffer& GPURenderBuffer::operator=(GPURenderBuffer&& other) noexcept { + std::swap(GLObjectID, other.GLObjectID); +#ifdef DEBUG + Name = std::move(other.Name); +#endif + Width = std::exchange(other.Width, {}); + Height = std::exchange(other.Height, {}); + Format = std::exchange(other.Format, {}); + Samples = std::exchange(other.Samples, {}); + return *this; +} + +GPURenderBufferBuilder& GPURenderBufferBuilder::SetName(std::string_view name) noexcept { +#ifdef DEBUG + this->name = name; +#endif + return *this; +} +GPURenderBufferBuilder& GPURenderBufferBuilder::SetWidth(unsigned width) noexcept { + this->width = width; + return *this; +} +GPURenderBufferBuilder& GPURenderBufferBuilder::SetHeight(unsigned height) noexcept { + this->height = height; + return *this; +} +GPURenderBufferBuilder& GPURenderBufferBuilder::SetFormat(DataFormat format) noexcept { + this->format = format; + return *this; +} +GPURenderBufferBuilder& GPURenderBufferBuilder::SetLayout(unsigned width, unsigned height, DataFormat format) noexcept { + SetWidth(width).SetHeight(height).SetFormat(format); + return *this; +} +GPURenderBufferBuilder& GPURenderBufferBuilder::SetSamples(Multisample multisample) noexcept { + samples = multisample; + return *this; +} + +std::pair, std::vector> GPURenderBufferBuilder::Build() noexcept { + return Graphics::Builders::Build(*this); +} + +// FrameBuffer +GPUFrameBuffer::~GPUFrameBuffer() noexcept { + Graphics::Destructors::Destruct(*this); +} +GPUFrameBuffer::GPUFrameBuffer(GPUFrameBuffer&& other) noexcept { + *this = std::move(other); +} +GPUFrameBuffer& GPUFrameBuffer::operator=(GPUFrameBuffer&& other) noexcept { + std::swap(GLObjectID, other.GLObjectID); +#ifdef DEBUG + Name = std::move(other.Name); +#endif + std::swap(ColorAttachments, other.ColorAttachments); + std::swap(DepthAttachment, other.DepthAttachment); + std::swap(StencilAttachment, other.StencilAttachment); + std::swap(DepthStencilAttachment, other.DepthStencilAttachment); + return *this; +} + +GPUFrameBufferBuilder& GPUFrameBufferBuilder::SetName(std::string_view name) noexcept { +#ifdef DEBUG + this->name = name; +#endif + return *this; +} +GPUFrameBufferBuilder& GPUFrameBufferBuilder::AttachColorTexture(GPUTexture&& texture, unsigned index) noexcept { + assert(index < colorAttachments.size()); + assert(texture.Depth == 1); // Only 2D textures are allowed! + colorAttachments[index].emplace(std::move(texture)); + return *this; +} +GPUFrameBufferBuilder& GPUFrameBufferBuilder::AttachDepthTexture(GPUTexture&& texture) noexcept { + assert(texture.Depth == 1); // Only 2D textures are allowed! + depthAttachment.emplace(std::move(texture)); + return *this; +} +GPUFrameBufferBuilder& GPUFrameBufferBuilder::AttachStencilTexture(GPUTexture&& texture) noexcept { + assert(texture.Depth == 1); // Only 2D textures are allowed! + stencilAttachment.emplace(std::move(texture)); + return *this; +} +GPUFrameBufferBuilder& GPUFrameBufferBuilder::AttachDepthStencilTexture(GPUTexture&& texture) noexcept { + assert(texture.Depth == 1); // Only 2D textures are allowed! + depthStencilAttachment.emplace(std::move(texture)); + return *this; +} +GPUFrameBufferBuilder& GPUFrameBufferBuilder::AttachColorRenderBuffer(GPURenderBuffer&& renderBuffer, unsigned index) noexcept { + assert(index < colorAttachments.size()); + colorAttachments[index].emplace(std::move(renderBuffer)); + return *this; +} +GPUFrameBufferBuilder& GPUFrameBufferBuilder::AttachDepthRenderBuffer(GPURenderBuffer&& renderBuffer) noexcept { + depthAttachment.emplace(std::move(renderBuffer)); + return *this; +} +GPUFrameBufferBuilder& GPUFrameBufferBuilder::AttachStencilRenderBuffer(GPURenderBuffer&& renderBuffer) noexcept { + stencilAttachment.emplace(std::move(renderBuffer)); + return *this; +} +GPUFrameBufferBuilder& GPUFrameBufferBuilder::AttachDepthStencilRenderBuffer(GPURenderBuffer&& renderBuffer) noexcept { + depthStencilAttachment.emplace(std::move(renderBuffer)); + return *this; +} + +std::pair, std::vector> GPUFrameBufferBuilder::Build() noexcept { + return Graphics::Builders::Build(*this); +} + diff --git a/Engine/GPUFrameBuffer.hpp b/Engine/GPUFrameBuffer.hpp new file mode 100644 index 00000000..78d813e0 --- /dev/null +++ b/Engine/GPUFrameBuffer.hpp @@ -0,0 +1,96 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +// RenderBuffer +struct GPURenderBuffer { + GLuint GLObjectID{}; +#ifdef DEBUG + std::string Name{}; +#endif + unsigned Width{}; + unsigned Height{}; + DataFormat Format{}; + Multisample Samples{}; + + ND_GRAPHICS_MOVE_ONLY_RESOURCE(GPURenderBuffer); +}; +struct GPURenderBufferBuilder { + + GPURenderBufferBuilder& SetName(std::string_view name) noexcept; + GPURenderBufferBuilder& SetWidth(unsigned width) noexcept; + GPURenderBufferBuilder& SetHeight(unsigned height) noexcept; + GPURenderBufferBuilder& SetFormat(DataFormat format) noexcept; + GPURenderBufferBuilder& SetLayout(unsigned width, unsigned height, DataFormat format) noexcept; + GPURenderBufferBuilder& SetSamples(Multisample multisample) noexcept; + + [[nodiscard]] std::pair, std::vector> Build() noexcept; + +private: +#ifdef DEBUG + std::string name{}; +#endif + unsigned width{ 1 }; + unsigned height{ 1 }; + DataFormat format{}; + Multisample samples{}; +public: + ND_GRAPHICS_BUILDER_RULE_OF_0(GPURenderBufferBuilder); + +private: +#ifdef OPENGL_4_6_SUPPORT + friend std::pair, std::vector> Graphics::OpenGL::Build(GPURenderBufferBuilder&) noexcept; +#endif +}; + +// FrameBuffer +struct GPUFrameBuffer { + GLuint GLObjectID{}; +#ifdef DEBUG + std::string Name{ "Backbuffer" }; +#endif + std::array>, MaxFrameBufferColorAttachments> ColorAttachments{}; + std::optional> DepthAttachment{}; + std::optional> StencilAttachment{}; + std::optional> DepthStencilAttachment{}; + + ND_GRAPHICS_MOVE_ONLY_RESOURCE(GPUFrameBuffer); +}; +struct GPUFrameBufferBuilder { + + GPUFrameBufferBuilder& SetName(std::string_view name) noexcept; + GPUFrameBufferBuilder& AttachColorTexture(GPUTexture&& texture, unsigned index) noexcept; + GPUFrameBufferBuilder& AttachDepthTexture(GPUTexture&& texture) noexcept; + GPUFrameBufferBuilder& AttachStencilTexture(GPUTexture&& texture) noexcept; + GPUFrameBufferBuilder& AttachDepthStencilTexture(GPUTexture&& texture) noexcept; + GPUFrameBufferBuilder& AttachColorRenderBuffer(GPURenderBuffer&& renderBuffer, unsigned index) noexcept; + GPUFrameBufferBuilder& AttachDepthRenderBuffer(GPURenderBuffer&& renderBuffer) noexcept; + GPUFrameBufferBuilder& AttachStencilRenderBuffer(GPURenderBuffer&& renderBuffer) noexcept; + GPUFrameBufferBuilder& AttachDepthStencilRenderBuffer(GPURenderBuffer&& renderBuffer) noexcept; + [[nodiscard]] std::pair, std::vector> Build() noexcept; + +private: +#ifdef DEBUG + std::string name{}; +#endif + std::array>, MaxFrameBufferColorAttachments> colorAttachments{}; + std::optional> depthAttachment{}; + std::optional> stencilAttachment{}; + std::optional> depthStencilAttachment{}; +public: + ND_GRAPHICS_BUILDER_RULE_OF_0(GPUFrameBufferBuilder); + +private: +#ifdef OPENGL_4_6_SUPPORT + friend std::pair, std::vector> Graphics::OpenGL::Build(GPUFrameBufferBuilder&) noexcept; +#endif +}; \ No newline at end of file diff --git a/Engine/GPUPipeline.cpp b/Engine/GPUPipeline.cpp new file mode 100644 index 00000000..c52831a8 --- /dev/null +++ b/Engine/GPUPipeline.cpp @@ -0,0 +1,132 @@ +#include + +#include + +#include + +GPUPipeline::~GPUPipeline() noexcept { + Graphics::Destructors::Destruct(*this); +} +GPUPipeline::GPUPipeline(GPUPipeline&& other) noexcept { + *this = std::move(other); +} +GPUPipeline& GPUPipeline::operator=(GPUPipeline&& other) noexcept { + std::swap(GLObjectID, other.GLObjectID); +#ifdef DEBUG + Name = std::move(other.Name); +#endif + VertexBuffers = std::move(other.VertexBuffers); + VertexLayouts = std::move(other.VertexLayouts); + IndexBuffer = other.IndexBuffer; + IndexType = other.IndexType; + Topology = other.Topology; + Polygon = other.Polygon; + IsFaceCullingEnabled = other.IsFaceCullingEnabled; + Cull = other.Cull; + Viewport = other.Viewport; + IsScissorEnabled = other.IsScissorEnabled; + Scissor = other.Scissor; + IsDepthTestEnabled = other.IsDepthTestEnabled; + IsDepthWriteEnabled = other.IsDepthWriteEnabled; + DepthFunc = other.DepthFunc; + IsDepthClampEnabled = other.IsDepthClampEnabled; + IsMultisampleEnabled = other.IsMultisampleEnabled; + IsBlendEnabled = other.IsBlendEnabled; + SourceFactor = other.SourceFactor; + DestinationFactor = other.DestinationFactor; + SourceAlphaFactor = other.SourceAlphaFactor; + DestinationAlphaFactor = other.DestinationAlphaFactor; + ShaderProgram = std::move(other.ShaderProgram); + return *this; +} + +GPUPipelineBuilder& GPUPipelineBuilder::SetName(std::string_view name) noexcept { +#ifdef DEBUG + this->name = name; +#endif + return *this; +} +GPUPipelineBuilder& GPUPipelineBuilder::SetArrayBuffer(uint8_t binding, const GPUBuffer& buffer, const GPUVertexAttribLayout& layout) noexcept { + assert(binding < MaxVertexBufferBinding); + vertexBuffers[binding].emplace(buffer); + vertexLayouts[binding] = layout; + return *this; +} +GPUPipelineBuilder& GPUPipelineBuilder::SetIndexBuffer(const GPUBuffer& buffer, DataType indexType) noexcept { + indexBuffer = &buffer; + this->indexType = indexType; + return *this; +} +GPUPipelineBuilder& GPUPipelineBuilder::SetTopology(TopologyType topology) noexcept { + this->topology = topology; + return *this; +} +GPUPipelineBuilder& GPUPipelineBuilder::SetPolygonMode(PolygonMode polygonMode) noexcept { + this->polygonMode = polygonMode; + return *this; +} +GPUPipelineBuilder& GPUPipelineBuilder::SetFaceCullEnabled(bool enabled) noexcept { + this->isFaceCullingEnabled = enabled; + return *this; +} +GPUPipelineBuilder& GPUPipelineBuilder::SetCullMode(CullMode cullMode) noexcept { + this->cullMode = cullMode; + return *this; +} +GPUPipelineBuilder& GPUPipelineBuilder::SetViewport(Region viewport) noexcept { + this->viewport = viewport; + return *this; +} +GPUPipelineBuilder& GPUPipelineBuilder::SetScissorTestEnabled(bool enabled) noexcept { + isScissorEnabled = enabled; + return *this; +} +GPUPipelineBuilder& GPUPipelineBuilder::SetScissorRegion(Region scissor) noexcept { + this->scissor = scissor; + return *this; +} +GPUPipelineBuilder& GPUPipelineBuilder::SetDepthTestEnabled(bool enabled) noexcept { + isDepthTestEnabled = enabled; + return *this; +} +GPUPipelineBuilder& GPUPipelineBuilder::SetDepthWriteEnabled(bool enabled) noexcept { + isDepthWriteEnabled = enabled; + return *this; +} +GPUPipelineBuilder& GPUPipelineBuilder::SetDepthFunc(DepthFunction depthFunction) noexcept { + this->depthFunction = depthFunction; + return *this; +} +GPUPipelineBuilder& GPUPipelineBuilder::SetDepthClampEnabled(bool enabled) noexcept { + isDepthClampEnabled = enabled; + return *this; +} + +GPUPipelineBuilder& GPUPipelineBuilder::SetMultisampleEnabled(bool enabled) noexcept { + isMultisampleEnabled = enabled; + return *this; +} +GPUPipelineBuilder& GPUPipelineBuilder::SetBlendEnabled(bool enabled) noexcept { + isBlendEnabled = enabled; + return *this; +} +GPUPipelineBuilder& GPUPipelineBuilder::SetBlendFunction(BlendFactor srcFactor, BlendFactor dstFactor) noexcept { + srcRGBFactor = srcFactor; + dstRGBFactor = dstFactor; + return *this; +} +GPUPipelineBuilder& GPUPipelineBuilder::SetBlendFunctionSeparate(BlendFactor srcRGBFactor, BlendFactor dstRGBFactor, BlendFactor srcAlphaFactor, BlendFactor dstAlphaFactor) noexcept { + this->srcRGBFactor = srcRGBFactor; + this->dstRGBFactor = dstRGBFactor; + this->srcAlphaFactor = srcAlphaFactor; + this->dstAlphaFactor = dstAlphaFactor; + return *this; +} +GPUPipelineBuilder& GPUPipelineBuilder::SetShaderProgram(const GPUShaderProgram& program) noexcept { + shaderProgam.emplace(program); + return *this; +} + +std::pair, std::vector> GPUPipelineBuilder::Build() noexcept { + return Graphics::Builders::Build(*this); +} \ No newline at end of file diff --git a/Engine/GPUPipeline.hpp b/Engine/GPUPipeline.hpp new file mode 100644 index 00000000..e2026ec6 --- /dev/null +++ b/Engine/GPUPipeline.hpp @@ -0,0 +1,122 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +struct GPUPipeline { + + GLuint GLObjectID{}; +#ifdef DEBUG + std::string Name{}; +#endif + std::array>, MaxVertexBufferBinding> VertexBuffers{}; + std::array VertexLayouts{}; + const GPUBuffer* IndexBuffer{}; + DataType IndexType{}; + + TopologyType Topology{}; + PolygonMode Polygon{}; + + bool IsFaceCullingEnabled{}; + CullMode Cull{}; + Region Viewport{}; + + bool IsScissorEnabled{}; + Region Scissor{}; + + bool IsDepthTestEnabled{}; + bool IsDepthWriteEnabled{}; + DepthFunction DepthFunc{}; + bool IsDepthClampEnabled{}; + + bool IsMultisampleEnabled{}; + + bool IsBlendEnabled{}; + BlendFactor SourceFactor{}, DestinationFactor{}; + BlendFactor SourceAlphaFactor{}, DestinationAlphaFactor{}; + + std::optional> ShaderProgram{}; + + ND_GRAPHICS_MOVE_ONLY_RESOURCE(GPUPipeline); +}; + +struct GPUPipelineBuilder { + + GPUPipelineBuilder& SetName(std::string_view name) noexcept; + GPUPipelineBuilder& SetArrayBuffer(uint8_t binding, const GPUBuffer& buffer, const GPUVertexAttribLayout& layout) noexcept; + GPUPipelineBuilder& SetIndexBuffer(const GPUBuffer& buffer, DataType indexType = DataType::UnsignedShort) noexcept; + GPUPipelineBuilder& SetTopology(TopologyType topology) noexcept; + GPUPipelineBuilder& SetPolygonMode(PolygonMode polygonMode) noexcept; + GPUPipelineBuilder& SetFaceCullEnabled(bool enabled) noexcept; + GPUPipelineBuilder& SetCullMode(CullMode cullMode) noexcept; + GPUPipelineBuilder& SetViewport(Region region) noexcept; + GPUPipelineBuilder& SetScissorTestEnabled(bool enabled) noexcept; + GPUPipelineBuilder& SetScissorRegion(Region scissor) noexcept; + GPUPipelineBuilder& SetDepthTestEnabled(bool enabled) noexcept; + GPUPipelineBuilder& SetDepthWriteEnabled(bool enabled) noexcept; + GPUPipelineBuilder& SetDepthFunc(DepthFunction depthFunction) noexcept; + GPUPipelineBuilder& SetDepthClampEnabled(bool enabled) noexcept; + //GPUPipelineBuilder& SetStencilTestEnabled(bool enabled); + //GPUPipelineBuilder& SetStencilWriteEnabled(bool enabled); + GPUPipelineBuilder& SetMultisampleEnabled(bool enabled) noexcept; + GPUPipelineBuilder& SetBlendEnabled(bool enabled) noexcept; + GPUPipelineBuilder& SetBlendFunction(BlendFactor srcFactor, BlendFactor dstFactor) noexcept; + GPUPipelineBuilder& SetBlendFunctionSeparate(BlendFactor srcRGBFactor, BlendFactor dstRGBFactor, BlendFactor srcAlphaFactor, BlendFactor dstAlphaFactor) noexcept; + GPUPipelineBuilder& SetShaderProgram(const GPUShaderProgram& program) noexcept; + + [[nodiscard]] std::pair, std::vector> Build() noexcept; + + GPUPipelineBuilder& SetArrayBuffer(uint8_t binding, GPUBuffer&& buffer, const GPUVertexAttribLayout& layout) noexcept = delete; + GPUPipelineBuilder& SetIndexBuffer(GPUBuffer&& buffer, DataType indexType = DataType::UnsignedShort) noexcept = delete; + GPUPipelineBuilder& SetShaderProgram(GPUShaderProgram&& program) noexcept = delete; + +private: +#ifdef DEBUG + std::string name{}; +#endif + std::array>, MaxVertexBufferBinding> vertexBuffers{}; + std::array vertexLayouts{}; + const GPUBuffer* indexBuffer{}; + DataType indexType{}; + + TopologyType topology{ TopologyType::Triangles }; + PolygonMode polygonMode{ PolygonMode::Fill }; + + bool isFaceCullingEnabled{}; + CullMode cullMode{ CullMode::Back }; + Region viewport{}; + + bool isScissorEnabled{}; + Region scissor{}; + + bool isDepthTestEnabled{}; + bool isDepthWriteEnabled{}; + DepthFunction depthFunction{ DepthFunction::Less }; + bool isDepthClampEnabled{}; + + bool isMultisampleEnabled{}; + + bool isBlendEnabled{}; + BlendFactor srcRGBFactor{ BlendFactor::One }; + BlendFactor dstRGBFactor{ BlendFactor::Zero }; + BlendFactor srcAlphaFactor{ srcRGBFactor }; + BlendFactor dstAlphaFactor{ dstRGBFactor }; + + std::optional> shaderProgam{}; + +public: + ND_GRAPHICS_BUILDER_RULE_OF_0(GPUPipelineBuilder); + +private: +#ifdef OPENGL_4_6_SUPPORT + friend std::pair, std::vector> Graphics::OpenGL::Build(GPUPipelineBuilder&) noexcept; +#endif +}; \ No newline at end of file diff --git a/Engine/GPUShader.cpp b/Engine/GPUShader.cpp new file mode 100644 index 00000000..1000da47 --- /dev/null +++ b/Engine/GPUShader.cpp @@ -0,0 +1,101 @@ +#include + +#include + +#include + +// Shader +GPUShader::~GPUShader() noexcept { + Graphics::Destructors::Destruct(*this); +} +GPUShader::GPUShader(GPUShader&& other) noexcept { + *this = std::move(other); +} +GPUShader& GPUShader::operator=(GPUShader&& other) noexcept { + std::swap(GLObjectID, other.GLObjectID); + Type = std::exchange(other.Type, {}); +#ifdef DEBUG + Name = std::move(other.Name); +#endif + return *this; +} + +GPUShaderBuilder& GPUShaderBuilder::SetType(ShaderType shaderType) noexcept { + type = shaderType; + return *this; +} +GPUShaderBuilder& GPUShaderBuilder::SetName(std::string_view shaderName) noexcept { +#ifdef DEBUG + name = shaderName; +#endif + return *this; +} +GPUShaderBuilder& GPUShaderBuilder::SetSourceCode(std::string_view code) noexcept { + sourceCode = code; + return *this; +} +std::pair, std::vector> GPUShaderBuilder::Build() noexcept { + return Graphics::Builders::Build(*this); +} + +// Shader Program +GPUShaderProgram::~GPUShaderProgram() noexcept { + Graphics::Destructors::Destruct(*this); +} +GPUShaderProgram::GPUShaderProgram(GPUShaderProgram&& other) noexcept { + *this = std::move(other); +} +GPUShaderProgram& GPUShaderProgram::operator=(GPUShaderProgram&& other) noexcept { + std::swap(GLObjectID, other.GLObjectID); +#ifdef DEBUG + Name = std::move(other.Name); +#endif + Uniforms = std::move(other.Uniforms); + return *this; +} + +int GPUShaderProgram::GetUniformLocation(std::string_view name) const noexcept { + auto search = std::ranges::find_if(Uniforms, [name](const Uniform& uniform) { return uniform.Name == name; }); +#ifdef DEBUG + if (search == Uniforms.end()) { + DOA_LOG_WARNING("Program %s does not contain uniform %s", std::quoted(Name), std::quoted(name)); + return -1; + } +#endif + return search->Location; +} + +GPUShaderProgramBuilder& GPUShaderProgramBuilder::SetName(const std::string_view programName) noexcept { +#ifdef DEBUG + name = programName; +#endif + return *this; +} +GPUShaderProgramBuilder& GPUShaderProgramBuilder::SetVertexShader(GPUShader& shader) noexcept { + vertShader = &shader; + return *this; +} +GPUShaderProgramBuilder& GPUShaderProgramBuilder::SetTessellationControlShader(GPUShader& shader) noexcept { + tessCtrlShader = &shader; + return *this; +} +GPUShaderProgramBuilder& GPUShaderProgramBuilder::SetTessellationEvaluationShader(GPUShader& shader) noexcept { + tessEvalShader = &shader; + return *this; +} +GPUShaderProgramBuilder& GPUShaderProgramBuilder::SetGeometryShader(GPUShader& shader) noexcept { + geomShader = &shader; + return *this; +} +GPUShaderProgramBuilder& GPUShaderProgramBuilder::SetFragmentShader(GPUShader& shader) noexcept { + fragShader = &shader; + return *this; +} +GPUShaderProgramBuilder& GPUShaderProgramBuilder::SetComputeShader(GPUShader& shader) noexcept { + compShader = &shader; + return *this; +} + +std::pair, std::vector> GPUShaderProgramBuilder::Build() noexcept { + return Graphics::Builders::Build(*this); +} \ No newline at end of file diff --git a/Engine/GPUShader.hpp b/Engine/GPUShader.hpp new file mode 100644 index 00000000..206f9774 --- /dev/null +++ b/Engine/GPUShader.hpp @@ -0,0 +1,92 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include + +// Shader +struct GPUShader { + GLuint GLObjectID{}; + ShaderType Type{}; +#ifdef DEBUG + std::string Name{}; +#endif + + ND_GRAPHICS_MOVE_ONLY_RESOURCE(GPUShader); +}; +struct GPUShaderBuilder { + GPUShaderBuilder& SetType(ShaderType type) noexcept; + GPUShaderBuilder& SetName(std::string_view name) noexcept; + GPUShaderBuilder& SetSourceCode(std::string_view sourceCode) noexcept; + [[nodiscard]] std::pair, std::vector> Build() noexcept; + +private: + ShaderType type{}; +#ifdef DEBUG + std::string name{}; +#endif + std::string sourceCode{}; + +public: + ND_GRAPHICS_BUILDER_RULE_OF_0(GPUShaderBuilder); + +private: +#ifdef OPENGL_4_6_SUPPORT + friend std::pair, std::vector> Graphics::OpenGL::Build(GPUShaderBuilder&) noexcept; +#endif +}; + +// Shader Program +struct GPUShaderProgram { + struct Uniform { + int Location; + std::string TypeName; + std::string Name; + int ArraySize; + ShaderType ReferencedBy; + }; + + GLuint GLObjectID{}; +#ifdef DEBUG + std::string Name{}; +#endif + + std::vector Uniforms{}; + + ND_GRAPHICS_MOVE_ONLY_RESOURCE(GPUShaderProgram); + + int GetUniformLocation(std::string_view name) const noexcept; +}; +struct GPUShaderProgramBuilder { + GPUShaderProgramBuilder& SetName(const std::string_view name) noexcept; + GPUShaderProgramBuilder& SetVertexShader(GPUShader& shader) noexcept; + GPUShaderProgramBuilder& SetTessellationControlShader(GPUShader& shader) noexcept; + GPUShaderProgramBuilder& SetTessellationEvaluationShader(GPUShader& shader) noexcept; + GPUShaderProgramBuilder& SetGeometryShader(GPUShader& shader) noexcept; + GPUShaderProgramBuilder& SetFragmentShader(GPUShader& shader) noexcept; + GPUShaderProgramBuilder& SetComputeShader(GPUShader& shader) noexcept; + [[nodiscard]] std::pair, std::vector> Build() noexcept; + +private: +#ifdef DEBUG + std::string name{}; +#endif + GPUShader* vertShader{}; + GPUShader* tessCtrlShader{}; + GPUShader* tessEvalShader{}; + GPUShader* geomShader{}; + GPUShader* fragShader{}; + GPUShader* compShader{}; +public: + ND_GRAPHICS_BUILDER_RULE_OF_0(GPUShaderProgramBuilder); + +private: +#ifdef OPENGL_4_6_SUPPORT + friend std::pair, std::vector> Graphics::OpenGL::Build(GPUShaderProgramBuilder&) noexcept; +#endif +}; \ No newline at end of file diff --git a/Engine/GPUTexture.cpp b/Engine/GPUTexture.cpp new file mode 100644 index 00000000..0cbe8cd5 --- /dev/null +++ b/Engine/GPUTexture.cpp @@ -0,0 +1,140 @@ +#include + +#include +#include +#include + +// Sampler +GPUSampler::~GPUSampler() noexcept { + Graphics::Destructors::Destruct(*this); +} +GPUSampler::GPUSampler(GPUSampler&& other) noexcept { + *this = std::move(other); +} +GPUSampler& GPUSampler::operator=(GPUSampler&& other) noexcept { + std::swap(GLObjectID, other.GLObjectID); +#ifdef DEBUG + Name = std::move(other.Name); +#endif + return *this; +} + +GPUSamplerBuilder& GPUSamplerBuilder::SetName(std::string_view name) noexcept { +#ifdef DEBUG + this->name = name; +#endif + return *this; +} +GPUSamplerBuilder& GPUSamplerBuilder::SetMinificationFilter(TextureMinificationMode mode) noexcept { + minFilter = mode; + return *this; +} +GPUSamplerBuilder& GPUSamplerBuilder::SetMagnificationFilter(TextureMagnificationMode mode) noexcept { + magFilter = mode; + return *this; +} +GPUSamplerBuilder& GPUSamplerBuilder::SetMinLOD(float lod) noexcept { + minLOD = lod; + return *this; +} +GPUSamplerBuilder& GPUSamplerBuilder::SetMaxLOD(float lod) noexcept { + maxLOD = lod; + return *this; +} +GPUSamplerBuilder& GPUSamplerBuilder::SetLODBias(float bias) noexcept { + LODBias = bias; + return *this; +} +GPUSamplerBuilder& GPUSamplerBuilder::SetWrapS(TextureWrappingMode mode) noexcept { + wrapS = mode; + return *this; +} +GPUSamplerBuilder& GPUSamplerBuilder::SetWrapT(TextureWrappingMode mode) noexcept { + wrapT = mode; + return *this; +} +GPUSamplerBuilder& GPUSamplerBuilder::SetWrapR(TextureWrappingMode mode) noexcept { + wrapR = mode; + return *this; +} +GPUSamplerBuilder& GPUSamplerBuilder::SetBorderColor(float r, float b, float g, float a) noexcept { + borderColor[0] = std::clamp(r, 0.0f, 1.0f); + borderColor[1] = std::clamp(g, 0.0f, 1.0f); + borderColor[2] = std::clamp(b, 0.0f, 1.0f); + borderColor[3] = std::clamp(a, 0.0f, 1.0f); + return *this; +} +GPUSamplerBuilder& GPUSamplerBuilder::SetCompareMode(TextureCompareMode mode) noexcept { + compareMode = mode; + return *this; +} +GPUSamplerBuilder& GPUSamplerBuilder::SetCompareFunction(TextureCompareFunction func) noexcept { + compareFunction = func; + return *this; +} +GPUSamplerBuilder& GPUSamplerBuilder::SetMaxAnisotropy(float value) noexcept { + maxAnisotropy = value; + return *this; +} +GPUSamplerBuilder& GPUSamplerBuilder::SetCubemapSeamless(bool seamless) noexcept { + cubemapSeamless = seamless; + return *this; +} +std::pair, std::vector> GPUSamplerBuilder::Build() noexcept { + return Graphics::Builders::Build(*this); +} + +// Texture +GPUTexture::~GPUTexture() noexcept { + Graphics::Destructors::Destruct(*this); +} +GPUTexture::GPUTexture(GPUTexture&& other) noexcept { + *this = std::move(other); +} +GPUTexture& GPUTexture::operator=(GPUTexture&& other) noexcept { + std::swap(GLObjectID, other.GLObjectID); +#ifdef DEBUG + Name = std::move(other.Name); +#endif + Width = std::exchange(other.Width, {}); + Height = std::exchange(other.Height, {}); + Depth = std::exchange(other.Depth, {}); + Format = std::exchange(other.Format, {}); + Samples = std::exchange(other.Samples, {}); + return *this; +} + +bool GPUTexture::IsMultisampled() const noexcept { return Samples != Multisample::None; } +GPUTexture::operator void* () const { return reinterpret_cast(static_cast(GLObjectID)); } + +GPUTextureBuilder& GPUTextureBuilder::SetName(std::string_view name) noexcept { +#ifdef DEBUG + this->name = name; +#endif + return *this; +} +GPUTextureBuilder& GPUTextureBuilder::SetWidth(unsigned width) noexcept { + this->width = width; + return *this; +} +GPUTextureBuilder& GPUTextureBuilder::SetHeight(unsigned height) noexcept { + this->height = height; + return *this; +} +GPUTextureBuilder& GPUTextureBuilder::SetDepth(unsigned depth) noexcept { + this->depth = depth; + return *this; +} +GPUTextureBuilder& GPUTextureBuilder::SetSamples(Multisample multisample) noexcept { + samples = multisample; + return *this; +} +GPUTextureBuilder& GPUTextureBuilder::SetData(DataFormat format, RawDataView data) noexcept { + this->format = format; + this->pixels = data; + return *this; +} + +std::pair, std::vector> GPUTextureBuilder::Build() noexcept { + return Graphics::Builders::Build(*this); +} \ No newline at end of file diff --git a/Engine/GPUTexture.hpp b/Engine/GPUTexture.hpp new file mode 100644 index 00000000..7d195030 --- /dev/null +++ b/Engine/GPUTexture.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +// Sampler +struct GPUSampler { + GLuint GLObjectID{}; +#ifdef DEBUG + std::string Name{}; +#endif + + ND_GRAPHICS_MOVE_ONLY_RESOURCE(GPUSampler); +}; +struct GPUSamplerBuilder { + GPUSamplerBuilder& SetName(std::string_view name) noexcept; + GPUSamplerBuilder& SetMinificationFilter(TextureMinificationMode mode) noexcept; + GPUSamplerBuilder& SetMagnificationFilter(TextureMagnificationMode mode) noexcept; + GPUSamplerBuilder& SetMinLOD(float lod) noexcept; + GPUSamplerBuilder& SetMaxLOD(float lod) noexcept; + GPUSamplerBuilder& SetLODBias(float bias) noexcept; + GPUSamplerBuilder& SetWrapS(TextureWrappingMode mode) noexcept; + GPUSamplerBuilder& SetWrapT(TextureWrappingMode mode) noexcept; + GPUSamplerBuilder& SetWrapR(TextureWrappingMode mode) noexcept; + GPUSamplerBuilder& SetBorderColor(float r, float g, float b, float a) noexcept; + GPUSamplerBuilder& SetCompareMode(TextureCompareMode mode) noexcept; + GPUSamplerBuilder& SetCompareFunction(TextureCompareFunction func) noexcept; + GPUSamplerBuilder& SetMaxAnisotropy(float value) noexcept; + GPUSamplerBuilder& SetCubemapSeamless(bool seamless) noexcept; + [[nodiscard]] std::pair, std::vector> Build() noexcept; + +private: +#ifdef DEBUG + std::string name{}; +#endif + TextureMinificationMode minFilter{ TextureMinificationMode::NearestMipmapLinear }; + TextureMagnificationMode magFilter{ TextureMagnificationMode::Linear }; + float minLOD{ -1000.0f }; + float maxLOD{ 1000.0f }; + float LODBias{ 0.0f }; + TextureWrappingMode wrapS{ TextureWrappingMode::Repeat }; + TextureWrappingMode wrapT{ TextureWrappingMode::Repeat }; + TextureWrappingMode wrapR{ TextureWrappingMode::Repeat }; + std::array borderColor{ 0.0f, 0.0f, 0.0f, 0.0f }; + TextureCompareMode compareMode{ TextureCompareMode::None }; + TextureCompareFunction compareFunction{ TextureCompareFunction::LessEqual }; + + float maxAnisotropy{ 1.0f }; + bool cubemapSeamless{ false }; + +public: + ND_GRAPHICS_BUILDER_RULE_OF_0(GPUSamplerBuilder); + +private: +#ifdef OPENGL_4_6_SUPPORT + friend std::pair, std::vector> Graphics::OpenGL::Build(GPUSamplerBuilder&) noexcept; +#endif +}; + +// Texture +struct GPUTexture { + GLuint GLObjectID{}; +#ifdef DEBUG + std::string Name{}; +#endif + unsigned Width{}; + unsigned Height{}; + unsigned Depth{}; + DataFormat Format{}; + Multisample Samples{}; + + bool IsMultisampled() const noexcept; + operator void* () const; + + ND_GRAPHICS_MOVE_ONLY_RESOURCE(GPUTexture); +}; +struct GPUTextureBuilder { + GPUTextureBuilder& SetName(std::string_view name) noexcept; + GPUTextureBuilder& SetWidth(unsigned width) noexcept; + GPUTextureBuilder& SetHeight(unsigned height) noexcept; + GPUTextureBuilder& SetDepth(unsigned depth) noexcept; + GPUTextureBuilder& SetData(DataFormat format, RawDataView data) noexcept; + GPUTextureBuilder& SetSamples(Multisample multisample) noexcept; + + [[nodiscard]] std::pair, std::vector> Build() noexcept; + +private: +#ifdef DEBUG + std::string name{}; +#endif + unsigned width{ 1 }; + unsigned height{ 1 }; + unsigned depth{ 1 }; + DataFormat format{}; + RawDataView pixels{}; + Multisample samples{ Multisample::None }; +public: + ND_GRAPHICS_BUILDER_RULE_OF_0(GPUTextureBuilder); + +private: +#ifdef OPENGL_4_6_SUPPORT + friend std::pair, std::vector> Graphics::OpenGL::Build(GPUTextureBuilder&) noexcept; +#endif +}; diff --git a/Engine/GPUVertexAttribLayout.cpp b/Engine/GPUVertexAttribLayout.cpp new file mode 100644 index 00000000..df2e8cd3 --- /dev/null +++ b/Engine/GPUVertexAttribLayout.cpp @@ -0,0 +1,71 @@ +#include + +GPUVertexAttribLayout::~GPUVertexAttribLayout() noexcept = default; +GPUVertexAttribLayout::GPUVertexAttribLayout(const GPUVertexAttribLayout&) noexcept = default; +GPUVertexAttribLayout::GPUVertexAttribLayout(GPUVertexAttribLayout&&) noexcept = default; +GPUVertexAttribLayout& GPUVertexAttribLayout::operator=(const GPUVertexAttribLayout&) noexcept = default; +GPUVertexAttribLayout& GPUVertexAttribLayout::operator=(GPUVertexAttribLayout&&) noexcept = default; + +template<> +void GPUVertexAttribLayout::Define(unsigned count, bool isNormalized) { + static unsigned size{ sizeof(float_t) }; + Elements[AttribCount++] = { GL_FLOAT, count, isNormalized }; + Offsets[AttribCount] = Offsets[AttribCount - 1] + count * size; + Stride += count * size; +} + +template<> +void GPUVertexAttribLayout::Define(unsigned count, bool isNormalized) { + static unsigned size{ sizeof(double_t) }; + Elements[AttribCount++] = { GL_DOUBLE, count, isNormalized }; + Offsets[AttribCount] = Offsets[AttribCount - 1] + count * size; + Stride += count * size; +} + +template<> +void GPUVertexAttribLayout::Define(unsigned count, bool isNormalized) { + static unsigned size{ sizeof(int8_t) }; + Elements[AttribCount++] = { GL_BYTE, count, isNormalized }; + Offsets[AttribCount] = Offsets[AttribCount - 1] + count * size; + Stride += count * size; +} + +template<> +void GPUVertexAttribLayout::Define(unsigned count, bool isNormalized) { + static unsigned size{ sizeof(uint8_t) }; + Elements[AttribCount++] = { GL_UNSIGNED_BYTE, count, isNormalized }; + Offsets[AttribCount] = Offsets[AttribCount - 1] + count * size; + Stride += count * size; +} + +template<> +void GPUVertexAttribLayout::Define(unsigned count, bool isNormalized) { + static unsigned size{ sizeof(int16_t) }; + Elements[AttribCount++] = { GL_SHORT, count, isNormalized }; + Offsets[AttribCount] = Offsets[AttribCount - 1] + count * size; + Stride += count * size; +} + +template<> +void GPUVertexAttribLayout::Define(unsigned count, bool isNormalized) { + static unsigned size{ sizeof(uint16_t) }; + Elements[AttribCount++] = { GL_UNSIGNED_SHORT, count, isNormalized }; + Offsets[AttribCount] = Offsets[AttribCount - 1] + count * size; + Stride += count * size; +} + +template<> +void GPUVertexAttribLayout::Define(unsigned count, bool isNormalized) { + static unsigned size{ sizeof(int32_t) }; + Elements[AttribCount++] = { GL_INT, count, isNormalized }; + Offsets[AttribCount] = Offsets[AttribCount - 1] + count * size; + Stride += count * size; +} + +template<> +void GPUVertexAttribLayout::Define(unsigned count, bool isNormalized) { + static unsigned size{ sizeof(uint32_t) }; + Elements[AttribCount++] = { GL_UNSIGNED_INT, count, isNormalized }; + Offsets[AttribCount] = Offsets[AttribCount - 1] + count * size; + Stride += count * size; +} \ No newline at end of file diff --git a/Engine/GPUVertexAttribLayout.hpp b/Engine/GPUVertexAttribLayout.hpp new file mode 100644 index 00000000..a00ed45f --- /dev/null +++ b/Engine/GPUVertexAttribLayout.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include + +struct GPUVertexAttribLayout { + + struct Element { + unsigned Type{}; + unsigned Count{}; + bool IsNormalized{}; + }; + + unsigned Stride{}; + InputRate InputRate{ InputRate::PerVertex }; + unsigned AttribCount{}; + std::array Elements{}; + std::array Offsets{ 0u }; + + template + void Define([[maybe_unused]] unsigned count, [[maybe_unused]] bool isNormalized = false) { DOA_LOG_FATAL("Unsupported Vertex attrib!"); std::unreachable(); } + + ND_GRAPHICS_COPYABLE_MOVEABLE_RESOURCE(GPUVertexAttribLayout); + +private: + friend struct GPUPipeline; +}; + +template<> +void GPUVertexAttribLayout::Define(unsigned count, bool isNormalized); +template<> +void GPUVertexAttribLayout::Define(unsigned count, bool isNormalized); +template<> +void GPUVertexAttribLayout::Define(unsigned count, bool isNormalized); +template<> +void GPUVertexAttribLayout::Define(unsigned count, bool isNormalized); +template<> +void GPUVertexAttribLayout::Define(unsigned count, bool isNormalized); +template<> +void GPUVertexAttribLayout::Define(unsigned count, bool isNormalized); +template<> +void GPUVertexAttribLayout::Define(unsigned count, bool isNormalized); +template<> +void GPUVertexAttribLayout::Define(unsigned count, bool isNormalized); \ No newline at end of file diff --git a/Engine/Graphics.cpp b/Engine/Graphics.cpp new file mode 100644 index 00000000..7567411a --- /dev/null +++ b/Engine/Graphics.cpp @@ -0,0 +1,382 @@ +#include + +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include + +namespace { + GraphicsBackend currentBackend = static_cast(-1); + + std::function bufferSubData; + std::function getBufferSubData; + std::function copyBufferSubData; + std::function clearBufferSubData; + + std::function blit; + std::function)> blitColor; + std::function blitDepth; + std::function blitStencil; + std::function blitDepthStencil; + + std::function render; + std::function renderInstanced; + + std::function setRenderTarget; + std::function)> setRenderTargetPartial; + std::function, unsigned)> clearRenderTargetColor; + std::function)> clearRenderTargetColors; + std::function clearRenderTargetDepth; + std::function clearRenderTargetStencil; + std::function, float, int)> clearRenderTarget; + + std::function bindPipeline; + + std::function bindDescriptorSet; + + std::function, std::vector> (GPUBufferBuilder&)> buildBuffer; + std::function, std::vector>(GPUDescriptorSetBuilder&)> buildDescriptorSet; + std::function, std::vector> (GPURenderBufferBuilder&)> buildRenderBuffer; + std::function, std::vector> (GPUFrameBufferBuilder&)> buildFrameBuffer; + std::function, std::vector> (GPUPipelineBuilder&)> buildPipeline; + std::function, std::vector> (GPUShaderBuilder&)> buildShader; + std::function, std::vector> (GPUShaderProgramBuilder&)> buildShaderProgram; + std::function, std::vector> (GPUSamplerBuilder&)> buildSampler; + std::function, std::vector> (GPUTextureBuilder&)> buildTexture; + + std::function destroyBuffer; + std::function destroyDescriptorSet; + std::function destroyRenderBuffer; + std::function destroyFrameBuffer; + std::function destroyPipeline; + std::function destroyShader; + std::function destroyShaderProgram; + std::function destroySampler; + std::function destroyTexture; +} + +void Graphics::ChangeGraphicsBackend(GraphicsBackend backend) noexcept { + currentBackend = backend; + using enum GraphicsBackend; + if (backend == None) { + DOA_LOG_WARNING("Graphics backend set to None. No rendering will be performed."); + + bufferSubData = static_cast(Graphics::None::BufferSubData); + getBufferSubData = Graphics::None::GetBufferSubData; + copyBufferSubData = Graphics::None::CopyBufferSubData; + clearBufferSubData = Graphics::None::ClearBufferSubData; + + blit = Graphics::None::Blit; + blitColor = Graphics::None::BlitColor; + blitDepth = Graphics::None::BlitDepth; + blitStencil = Graphics::None::BlitStencil; + blitDepthStencil = Graphics::None::BlitDepthStencil; + + render = Graphics::None::Render; + renderInstanced = Graphics::None::RenderInstanced; + + setRenderTarget = static_cast (Graphics::OpenGL::SetRenderTarget); + setRenderTargetPartial = static_cast)>(Graphics::OpenGL::SetRenderTarget); + clearRenderTargetColor = Graphics::None::ClearRenderTargetColor; + clearRenderTargetColors = Graphics::None::ClearRenderTargetColors; + clearRenderTargetDepth = Graphics::None::ClearRenderTargetDepth; + clearRenderTargetStencil = Graphics::None::ClearRenderTargetStencil; + clearRenderTarget = Graphics::None::ClearRenderTarget; + + bindPipeline = Graphics::None::BindPipeline; + + bindDescriptorSet = Graphics::None::BindDescriptorSet; + + buildBuffer = static_cast, std::vector>(*) (GPUBufferBuilder&)> (Graphics::None::Build); + buildDescriptorSet = static_cast, std::vector>(*)(GPUDescriptorSetBuilder&)>(Graphics::None::Build); + buildRenderBuffer = static_cast, std::vector>(*) (GPURenderBufferBuilder&)> (Graphics::None::Build); + buildFrameBuffer = static_cast, std::vector>(*) (GPUFrameBufferBuilder&)> (Graphics::None::Build); + buildPipeline = static_cast, std::vector>(*) (GPUPipelineBuilder&)> (Graphics::None::Build); + buildShader = static_cast, std::vector>(*) (GPUShaderBuilder&)> (Graphics::None::Build); + buildShaderProgram = static_cast, std::vector>(*) (GPUShaderProgramBuilder&)>(Graphics::None::Build); + buildSampler = static_cast, std::vector>(*) (GPUSamplerBuilder&)> (Graphics::None::Build); + buildTexture = static_cast, std::vector>(*) (GPUTextureBuilder&)> (Graphics::None::Build); + + destroyBuffer = static_cast (Graphics::None::Destruct); + destroyDescriptorSet = static_cast(Graphics::None::Destruct); + destroyRenderBuffer = static_cast (Graphics::None::Destruct); + destroyFrameBuffer = static_cast (Graphics::None::Destruct); + destroyPipeline = static_cast (Graphics::None::Destruct); + destroyShader = static_cast (Graphics::None::Destruct); + destroyShaderProgram = static_cast(Graphics::None::Destruct); + destroySampler = static_cast (Graphics::None::Destruct); + destroyTexture = static_cast (Graphics::None::Destruct); + } else if (backend == Software) { + std::unreachable(); /* not implemented yet */ + } +#ifdef OPENGL_4_6_SUPPORT + else if (backend == OpenGL4_6) { + assert(Core::GetCore()->IsOpenGLInitialized()); + DOA_LOG_INFO("OpenGL version: %s", glGetString(GL_VERSION)); + DOA_LOG_INFO("GLSL version: %s", glGetString(GL_SHADING_LANGUAGE_VERSION)); + DOA_LOG_INFO("Vendor: %s", glGetString(GL_VENDOR)); + DOA_LOG_INFO("GPU: %s", glGetString(GL_RENDERER)); + + bufferSubData = static_cast(Graphics::OpenGL::BufferSubData); + getBufferSubData = Graphics::OpenGL::GetBufferSubData; + copyBufferSubData = Graphics::OpenGL::CopyBufferSubData; + clearBufferSubData = Graphics::OpenGL::ClearBufferSubData; + + blit = Graphics::OpenGL::Blit; + blitColor = Graphics::OpenGL::BlitColor; + blitDepth = Graphics::OpenGL::BlitDepth; + blitStencil = Graphics::OpenGL::BlitStencil; + blitDepthStencil = Graphics::OpenGL::BlitDepthStencil; + + render = Graphics::OpenGL::Render; + renderInstanced = Graphics::OpenGL::RenderInstanced; + + setRenderTarget = static_cast (Graphics::OpenGL::SetRenderTarget); + setRenderTargetPartial = static_cast)>(Graphics::OpenGL::SetRenderTarget); + clearRenderTargetColor = Graphics::OpenGL::ClearRenderTargetColor; + clearRenderTargetColors = Graphics::OpenGL::ClearRenderTargetColors; + clearRenderTargetDepth = Graphics::OpenGL::ClearRenderTargetDepth; + clearRenderTargetStencil = Graphics::OpenGL::ClearRenderTargetStencil; + clearRenderTarget = Graphics::OpenGL::ClearRenderTarget; + + bindPipeline = Graphics::OpenGL::BindPipeline; + + bindDescriptorSet = Graphics::OpenGL::BindDescriptorSet; + + buildBuffer = static_cast, std::vector>(*) (GPUBufferBuilder&)> (Graphics::OpenGL::Build); + buildDescriptorSet = static_cast, std::vector>(*)(GPUDescriptorSetBuilder&)>(Graphics::OpenGL::Build); + buildRenderBuffer = static_cast, std::vector>(*) (GPURenderBufferBuilder&)> (Graphics::OpenGL::Build); + buildFrameBuffer = static_cast, std::vector>(*) (GPUFrameBufferBuilder&)> (Graphics::OpenGL::Build); + buildPipeline = static_cast, std::vector>(*) (GPUPipelineBuilder&)> (Graphics::OpenGL::Build); + buildShader = static_cast, std::vector>(*) (GPUShaderBuilder&)> (Graphics::OpenGL::Build); + buildShaderProgram = static_cast, std::vector>(*) (GPUShaderProgramBuilder&)>(Graphics::OpenGL::Build); + buildSampler = static_cast, std::vector>(*) (GPUSamplerBuilder&)> (Graphics::OpenGL::Build); + buildTexture = static_cast, std::vector>(*) (GPUTextureBuilder&)> (Graphics::OpenGL::Build); + + destroyBuffer = static_cast (Graphics::OpenGL::Destruct); + destroyDescriptorSet = static_cast(Graphics::OpenGL::Destruct); + destroyRenderBuffer = static_cast (Graphics::OpenGL::Destruct); + destroyFrameBuffer = static_cast (Graphics::OpenGL::Destruct); + destroyPipeline = static_cast (Graphics::OpenGL::Destruct); + destroyShader = static_cast (Graphics::OpenGL::Destruct); + destroyShaderProgram = static_cast(Graphics::OpenGL::Destruct); + destroySampler = static_cast (Graphics::OpenGL::Destruct); + destroyTexture = static_cast (Graphics::OpenGL::Destruct); + } +#endif +#ifdef OPENGL_3_3_SUPPORT + else if (backend == OpenGL3_3) { + assert(Core::GetCore()->IsOpenGLInitialized()); + std::unreachable(); /* not implemented yet */ + } +#endif +#ifdef VULKAN_SUPPORT + else if (backend == Vulkan) { + assert(Core::GetCore()->IsVulkanInitialized()); + std::unreachable(); /* not implemented yet */ + } +#endif +#ifdef DIRECT3D_12_SUPPORT + else if (backend == Direct3D12) { + assert(Core::GetCore()->IsDirect3D12Initialized()); + std::unreachable(); /* not implemented yet */ + } +#endif +#ifdef DIRECT3D_11_SUPPORT + else if (backend == Direct3D11) { + assert(Core::GetCore()->IsDirect3D11Initialized()); + std::unreachable(); /* not implemented yet */ + } +#endif +} + +void Graphics::BufferSubData(GPUBuffer& buffer, RawDataView dataView, size_t offsetBytes) noexcept { + assert(bufferSubData && "Did you forget to call Graphics::ChangeGraphicsBackend()?"); + bufferSubData(buffer, dataView.size_bytes(), dataView.data(), offsetBytes); +} +void Graphics::BufferSubData(GPUBuffer& buffer, size_t sizeBytes, NonOwningPointerToConstRawData data, size_t offsetBytes) noexcept { + assert(bufferSubData && "Did you forget to call Graphics::ChangeGraphicsBackend()?"); + assert(static_cast(buffer.Properties & BufferProperties::DynamicStorage)); + bufferSubData(buffer, sizeBytes, data, offsetBytes); +} +void Graphics::GetBufferSubData(const GPUBuffer& buffer, RawDataWriteableView dataView, size_t offsetBytes) noexcept { + assert(getBufferSubData && "Did you forget to call Graphics::ChangeGraphicsBackend()?"); + getBufferSubData(buffer, dataView, offsetBytes); +} +void Graphics::CopyBufferSubData(const GPUBuffer& readBuffer, GPUBuffer& writeBuffer, size_t sizeBytesToCopy, size_t readOffsetBytes, size_t writeOffsetBytes) noexcept { + assert(copyBufferSubData && "Did you forget to call Graphics::ChangeGraphicsBackend()?"); + copyBufferSubData( + readBuffer, + writeBuffer, + readOffsetBytes, + writeOffsetBytes, + sizeBytesToCopy + ); +} +void Graphics::ClearBufferSubData(GPUBuffer& buffer, DataFormat format, size_t sizeBytesToClear, size_t offsetBytes) noexcept { + assert(clearBufferSubData && "Did you forget to call Graphics::ChangeGraphicsBackend()?"); + clearBufferSubData(buffer, format, sizeBytesToClear, offsetBytes); +} + +void Graphics::Blit(const GPUFrameBuffer& source, GPUFrameBuffer& destination) noexcept { + assert(blit && "Did you forget to call Graphics::ChangeGraphicsBackend()?"); + blit(source, destination); +} +void Graphics::BlitColor(const GPUFrameBuffer& source, GPUFrameBuffer& destination, unsigned srcAttachment, std::span dstAttachments) noexcept { + assert(blitColor && "Did you forget to call Graphics::ChangeGraphicsBackend()?"); + blitColor(source, destination, srcAttachment, dstAttachments); +} +void Graphics::BlitDepth(const GPUFrameBuffer& source, GPUFrameBuffer& destination) noexcept { + assert(blitDepth && "Did you forget to call Graphics::ChangeGraphicsBackend()?"); + blitDepth(source, destination); +} +void Graphics::BlitStencil(const GPUFrameBuffer& source, GPUFrameBuffer& destination) noexcept { + assert(blitStencil && "Did you forget to call Graphics::ChangeGraphicsBackend()?"); + blitStencil(source, destination); +} +void Graphics::BlitDepthStencil(const GPUFrameBuffer& source, GPUFrameBuffer& destination) noexcept { + assert(blitDepthStencil && "Did you forget to call Graphics::ChangeGraphicsBackend()?"); + blitDepthStencil(source, destination); +} + +void Graphics::Render(int count, int first) noexcept { + assert(render && "Did you forget to call Graphics::ChangeGraphicsBackend()?"); + render(count, first); +} +void Graphics::RenderInstanced(int instanceCount, int count, int first) noexcept { + assert(renderInstanced && "Did you forget to call Graphics::ChangeGraphicsBackend()?"); + renderInstanced(instanceCount, count, first); +} + +void Graphics::SetRenderTarget(const GPUFrameBuffer& renderTarget) noexcept { + assert(setRenderTarget && "Did you forget to call Graphics::ChangeGraphicsBackend()?"); + setRenderTarget(renderTarget); +} +void Graphics::SetRenderTarget(const GPUFrameBuffer& renderTarget, std::span targets) noexcept { + assert(setRenderTargetPartial && "Did you forget to call Graphics::ChangeGraphicsBackend()?"); + setRenderTargetPartial(renderTarget, targets); +} +void Graphics::ClearRenderTargetColor(const GPUFrameBuffer& renderTarget, std::array color, unsigned colorBufferIndex) noexcept { + assert(clearRenderTargetColor && "Did you forget to call Graphics::ChangeGraphicsBackend()?"); + clearRenderTargetColor(renderTarget, color, colorBufferIndex); +} +void Graphics::ClearRenderTargetColors(const GPUFrameBuffer& renderTarget, std::array color) noexcept { + assert(clearRenderTargetColors && "Did you forget to call Graphics::ChangeGraphicsBackend()?"); + clearRenderTargetColors(renderTarget, color); +} +void Graphics::ClearRenderTargetDepth(const GPUFrameBuffer& renderTarget, float depth) noexcept { + assert(clearRenderTargetDepth && "Did you forget to call Graphics::ChangeGraphicsBackend()?"); + clearRenderTargetDepth(renderTarget, depth); +} +void Graphics::ClearRenderTargetStencil(const GPUFrameBuffer& renderTarget, int stencil) noexcept { + assert(clearRenderTargetStencil && "Did you forget to call Graphics::ChangeGraphicsBackend()?"); + clearRenderTargetStencil(renderTarget, stencil); +} +void Graphics::ClearRenderTarget(const GPUFrameBuffer& renderTarget, std::array color, float depth, int stencil) noexcept { + assert(clearRenderTarget && "Did you forget to call Graphics::ChangeGraphicsBackend()?"); + clearRenderTarget(renderTarget, color, depth, stencil); +} + +void Graphics::BindPipeline(const GPUPipeline& pipeline) noexcept { + assert(bindPipeline && "Did you forget to call Graphics::ChangeGraphicsBackend()?"); + bindPipeline(pipeline); +} + +void Graphics::BindDescriptorSet(const GPUDescriptorSet& descriptorSet) noexcept { + assert(bindDescriptorSet && "Did you forget to call Graphics::ChangeGraphicsBackend()?"); + bindDescriptorSet(descriptorSet); +} + +std::pair, std::vector> Graphics::Builders::Build(GPUBufferBuilder& builder) noexcept { + assert(buildBuffer && "Did you forget to call Graphics::ChangeGraphicsBackend()?"); + return buildBuffer(builder); +} +std::pair, std::vector> Graphics::Builders::Build(GPUDescriptorSetBuilder& builder) noexcept { + assert(buildDescriptorSet && "Did you forget to call Graphics::ChangeGraphicsBackend()?"); + return buildDescriptorSet(builder); +} +std::pair, std::vector> Graphics::Builders::Build(GPURenderBufferBuilder& builder) noexcept { + assert(buildRenderBuffer && "Did you forget to call Graphics::ChangeGraphicsBackend()?"); + return buildRenderBuffer(builder); +} +std::pair, std::vector> Graphics::Builders::Build(GPUFrameBufferBuilder& builder) noexcept { + assert(buildFrameBuffer && "Did you forget to call Graphics::ChangeGraphicsBackend()?"); + return buildFrameBuffer(builder); +} +std::pair, std::vector> Graphics::Builders::Build(GPUPipelineBuilder& builder) noexcept { + assert(buildPipeline && "Did you forget to call Graphics::ChangeGraphicsBackend()?"); + return buildPipeline(builder); +} +std::pair, std::vector> Graphics::Builders::Build(GPUShaderBuilder& builder) noexcept { + assert(buildShader && "Did you forget to call Graphics::ChangeGraphicsBackend()?"); + return buildShader(builder); +} +std::pair, std::vector> Graphics::Builders::Build(GPUShaderProgramBuilder& builder) noexcept { + assert(buildShaderProgram && "Did you forget to call Graphics::ChangeGraphicsBackend()?"); + return buildShaderProgram(builder); +} +std::pair, std::vector> Graphics::Builders::Build(GPUSamplerBuilder& builder) noexcept { + assert(buildSampler && "Did you forget to call Graphics::ChangeGraphicsBackend()?"); + return buildSampler(builder); +} +std::pair, std::vector> Graphics::Builders::Build(GPUTextureBuilder& builder) noexcept { + assert(buildTexture && "Did you forget to call Graphics::ChangeGraphicsBackend()?"); + return buildTexture(builder); +} + +void Graphics::Destructors::Destruct(GPUBuffer& buffer) noexcept { + destroyBuffer(buffer); +} +void Graphics::Destructors::Destruct(GPUDescriptorSet& set) noexcept { + destroyDescriptorSet(set); +} +void Graphics::Destructors::Destruct(GPURenderBuffer& renderbuffer) noexcept { + destroyRenderBuffer(renderbuffer); +} +void Graphics::Destructors::Destruct(GPUFrameBuffer& framebuffer) noexcept { + destroyFrameBuffer(framebuffer); +} +void Graphics::Destructors::Destruct(GPUPipeline& pipeline) noexcept { + destroyPipeline(pipeline); +} +void Graphics::Destructors::Destruct(GPUShader& shader) noexcept { + destroyShader(shader); +} +void Graphics::Destructors::Destruct(GPUShaderProgram& program) noexcept { + destroyShaderProgram(program); +} +void Graphics::Destructors::Destruct(GPUSampler& sampler) noexcept { + destroySampler(sampler); +} +void Graphics::Destructors::Destruct(GPUTexture& texture) noexcept { + destroyTexture(texture); +} + +std::ostream& operator<<(std::ostream& os, GraphicsBackend backend) { return os << ToString(backend); } +std::ostream& operator<<(std::ostream& os, BufferProperties property) { return os << ToString(property); } +std::ostream& operator<<(std::ostream& os, ShaderType type) { return os << ToString(type); } +std::ostream& operator<<(std::ostream& os, TextureMinificationMode mode) { return os << ToString(mode); } +std::ostream& operator<<(std::ostream& os, TextureMagnificationMode mode) { return os << ToString(mode); } +std::ostream& operator<<(std::ostream& os, TextureWrappingMode mode) { return os << ToString(mode); } +std::ostream& operator<<(std::ostream& os, TextureCompareMode mode) { return os << ToString(mode); } +std::ostream& operator<<(std::ostream& os, TextureCompareFunction func) { return os << ToString(func); } +std::ostream& operator<<(std::ostream& os, Multisample sample) { return os << ToString(sample); } +std::ostream& operator<<(std::ostream& os, DataFormat format) { return os << ToString(format); } +std::ostream& operator<<(std::ostream& os, TopologyType type) { return os << ToString(type); } +std::ostream& operator<<(std::ostream& os, DataType type) { return os << ToString(type); } +std::ostream& operator<<(std::ostream& os, PolygonMode mode) { return os << ToString(mode); } +std::ostream& operator<<(std::ostream& os, CullMode mode) { return os << ToString(mode); } +std::ostream& operator<<(std::ostream& os, DepthFunction func) { return os << ToString(func); } +std::ostream& operator<<(std::ostream& os, BlendFactor factor) { return os << ToString(factor); } +std::ostream& operator<<(std::ostream& os, InputRate rate) { return os << ToString(rate); } \ No newline at end of file diff --git a/Engine/Graphics.hpp b/Engine/Graphics.hpp new file mode 100644 index 00000000..68007dc0 --- /dev/null +++ b/Engine/Graphics.hpp @@ -0,0 +1,708 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +struct Region; +struct GPUBuffer; struct GPUBufferBuilder; +struct GPUShader; struct GPUShaderBuilder; +struct GPUTexture; struct GPUTextureBuilder; +struct GPUSampler; struct GPUSamplerBuilder; +struct GPUPipeline; struct GPUPipelineBuilder; +struct GPUFrameBuffer; struct GPUFrameBufferBuilder; +struct GPURenderBuffer; struct GPURenderBufferBuilder; +struct GPUShaderProgram; struct GPUShaderProgramBuilder; +struct GPUDescriptorSet; struct GPUDescriptorSetBuilder; + +#pragma region Graphics Messages +using BufferAllocatorMessage = std::string; + +using DescriptorSetAllocatorMessage = std::string; + +using RenderBufferAllocatorMessage = std::string; + +using FrameBufferAllocatorMessage = std::string; + +using PipelineAllocatorMessage = std::string; + +struct ShaderCompilerMessage { + int LineNo; + enum class Type { + Info, + Warning, + Error + } MessageType; + std::string ShortMessage, FullMessage; +}; +using ShaderLinkerMessage = std::string; + +using SamplerAllocatorMessage = std::string; + +using TextureAllocatorMessage = std::string; +#pragma endregion + +#pragma region Constants +constexpr uint8_t MaxDescriptorBinding = 16; + +constexpr uint8_t MaxFrameBufferColorAttachments = 8; + +constexpr uint8_t MaxVertexBufferBinding = 8; + +constexpr uint8_t MaxVertexAttributes = 16; +#pragma endregion + +enum class GraphicsBackend { + None, + Software, +#ifdef OPENGL_4_6_SUPPORT + OpenGL4_6, +#endif +#ifdef OPENGL_3_3_SUPPORT + OpenGL3_3, // TODO Implement this +#endif +#ifdef VULKAN_SUPPORT + Vulkan, // TODO Implement this +#endif +#ifdef DIRECT3D_12_SUPPORT + Direct3D12, // TODO Implement this +#endif +#ifdef DIRECT3D_11_SUPPORT + Direct3D11 // TODO Implement this +#endif +}; + +#define ND_GRAPHICS_COPYABLE_MOVEABLE_RESOURCE(x) \ + x() noexcept = default; \ + ~x() noexcept; \ + x(const x& other) noexcept; \ + x(x&& other) noexcept; \ + x& operator=(const x& other) noexcept; \ + x& operator=(x&& other) noexcept + +#define ND_GRAPHICS_MOVE_ONLY_RESOURCE(x) \ + x() noexcept = default; \ + ~x() noexcept; \ + x(const x&) = delete; \ + x(x&& other) noexcept; \ + x& operator=(const x&) = delete; \ + x& operator=(x&& other) noexcept + +#define ND_GRAPHICS_BUILDER_RULE_OF_0(x) \ + x() = default; \ + ~x() = default; \ + x(const x&) = delete; \ + x& operator=(const x&) = delete; \ + x(x&&) = delete; \ + x& operator=(x&&) = delete + +#pragma region Enumerations +enum class BufferProperties : uint32_t { + None = (1 << 0), + DynamicStorage = (1 << 1), + ReadableFromCPU = (1 << 2), + WriteableFromCPU = (1 << 3), + Persistent = (1 << 4), + Coherent = (1 << 5), + CPUStorage = (1 << 6) +}; +constexpr BufferProperties operator &(const BufferProperties lhs, const BufferProperties rhs) { + return static_cast(static_cast(lhs) & static_cast(rhs)); +} +constexpr BufferProperties operator |(const BufferProperties lhs, enum BufferProperties rhs) { + return static_cast(static_cast(lhs) | static_cast(rhs)); +} + +enum class ShaderType { + Vertex, + TessellationControl, + TessellationEvaluation, + Geometry, + Fragment, + Compute +}; +enum class TextureMinificationMode { + Nearest, + Linear, + NearestMipmapNearest, + LinearMipmapNearest, + NearestMipmapLinear, + LinearMipmapLinear +}; +enum class TextureMagnificationMode { + Nearest, + Linear +}; +enum class TextureWrappingMode { + Repeat, + MirroredRepeat, + ClampToEdge, + MirrorClampToEdge, + ClampToBorder +}; +enum class TextureCompareMode { + CompareRefToTexture, + None +}; +enum class TextureCompareFunction { + LessEqual, + GreaterEqual, + Less, + Greater, + Equal, + NotEqual, + Always, + Never +}; +enum class Multisample : uint8_t { + None = 1, + x2 = 2, + x4 = 4, + x8 = 8 +}; +enum class DataFormat { + // Unsigned unified formats + R8, + RG8, + RGB8, + RGBA8, + R16, + RG16, + RGB16, + RGBA16, + R16F, + RG16F, + RGB16F, + RGBA16F, + R32F, + RG32F, + RGB32F, + RGBA32F, + // Signed normalized unified formats + R8_SNORM, + RG8_SNORM, + RGB8_SNORM, + RGBA8_SNORM, + R16_SNORM, + RG16_SNORM, + RGB16_SNORM, + RGBA16_SNORM, + // Unsigned integer unified formats + R8UI, + RG8UI, + RGB8UI, + RGBA8UI, + R16UI, + RG16UI, + RGB16UI, + RGBA16UI, + R32UI, + RG32UI, + RGB32UI, + RGBA32UI, + // Signed integer unified formats + R8I, + RG8I, + RGB8I, + RGBA8I, + R16I, + RG16I, + RGB16I, + RGBA16I, + R32I, + RG32I, + RGB32I, + RGBA32I, + // SRGB Formats + SRGB8, + SRGBA8, + + // Depth formats + DEPTH16, + DEPTH24, + DEPTH32, + DEPTH32F, + // Stencil Formats + STENCIL1, + STENCIL4, + STENCIL8, + STENCIL16, + // Depth-stencil formats + DEPTH24_STENCIL8, + DEPTH32F_STENCIL8, + + // Split Formats + R3G3B2, + RGB5A1, + RGB10A2, + RGB10A2UI, + R11FG11FB10F, + RGB9E5, + // Other Formats (when do they even get used?) + RGB4, + RGB5, + RGB565, + RGB10, + RGB12, + RGBA2, + RGBA4, + RGBA12 +}; +enum class TopologyType { + Points, + Lines, + LineStrip, + LineLoop, + Triangles, + TriangleStrip, + TriangleFan +}; +enum class DataType { + Byte, + UnsignedByte, + Short, + UnsignedShort, + Int, + UnsignedInt, + Float, + Double +}; +enum class PolygonMode { + Fill, + Line, + Point +}; +enum class CullMode { + Front, + Back, + FrontAndBack +}; +enum class DepthFunction { + Always, + Never, + Less, + Equal, + LessEqual, + Greater, + GreaterEqual, + NotEqual, +}; +enum class BlendFactor { + Zero, + One, + SrcColor, + OneMinusSrcColor, + DstColor, + OneMinusDstColor, + SrcAlpha, + OneMinusSrcAlpha, + DstAlpha, + OneMinusDstAlpha, + ConstantColor, + OneMinusConstantColor, + ConstantAlpha, + OneMinusConstantAlpha, + SrcAlphaSaturate, +}; +enum class InputRate { + PerVertex, + PerInstance +}; +#pragma endregion + +#define GRAPHICS_FUNCTIONS(base, builders, destructors) \ +namespace base { \ + void BufferSubData(GPUBuffer& buffer, RawDataView dataView, size_t offsetBytes = 0uLL) noexcept; \ + void BufferSubData(GPUBuffer& buffer, size_t sizeBytes, NonOwningPointerToConstRawData data, size_t offsetBytes = 0uLL) noexcept; \ + void GetBufferSubData(const GPUBuffer& buffer, RawDataWriteableView dataView, size_t offsetBytes = 0uLL) noexcept; \ + void CopyBufferSubData(const GPUBuffer& readBuffer, GPUBuffer& writeBuffer, size_t sizeBytesToCopy, size_t readOffsetBytes = 0uLL, size_t writeOffsetBytes = 0uLL) noexcept;\ + void ClearBufferSubData(GPUBuffer& buffer, DataFormat format, size_t sizeBytesToClear, size_t offsetBytes = 0uLL) noexcept; \ + \ + void Blit(const GPUFrameBuffer& source, GPUFrameBuffer& destination) noexcept; \ + void BlitColor(const GPUFrameBuffer& source, GPUFrameBuffer& destination, unsigned srcAttachment, std::span dstAttachments) noexcept; \ + void BlitDepth(const GPUFrameBuffer& source, GPUFrameBuffer& destination) noexcept; \ + void BlitStencil(const GPUFrameBuffer& source, GPUFrameBuffer& destination) noexcept; \ + void BlitDepthStencil(const GPUFrameBuffer& source, GPUFrameBuffer& destination) noexcept; \ + \ + void Render(int count, int first = 0) noexcept; \ + void RenderInstanced(int instanceCount, int count, int first = 0) noexcept; \ + \ + void SetRenderTarget(const GPUFrameBuffer& renderTarget) noexcept; \ + void SetRenderTarget(const GPUFrameBuffer& renderTarget, std::span targets) noexcept; \ + void ClearRenderTargetColor(const GPUFrameBuffer& renderTarget, std::array color = { 0, 0, 0, 0 }, unsigned colorBufferIndex = 0) noexcept; \ + void ClearRenderTargetColors(const GPUFrameBuffer& renderTarget, std::array color = { 0, 0, 0, 0 }) noexcept; \ + void ClearRenderTargetDepth(const GPUFrameBuffer& renderTarget, float depth = 1) noexcept; \ + void ClearRenderTargetStencil(const GPUFrameBuffer& renderTarget, int stencil = 0) noexcept; \ + void ClearRenderTarget(const GPUFrameBuffer& renderTarget, std::array color = { 0, 0, 0, 0 }, float depth = 1, int stencil = 0) noexcept; \ + \ + void BindPipeline(const GPUPipeline& pipeline) noexcept; \ + \ + void BindDescriptorSet(const GPUDescriptorSet& descriptorSet) noexcept; \ +} \ +namespace builders { \ + [[nodiscard]] std::pair, std::vector> Build(GPUBufferBuilder& builder) noexcept; \ + [[nodiscard]] std::pair, std::vector> Build(GPUDescriptorSetBuilder& builder) noexcept; \ + [[nodiscard]] std::pair, std::vector> Build(GPURenderBufferBuilder& builder) noexcept; \ + [[nodiscard]] std::pair, std::vector> Build(GPUFrameBufferBuilder& builder) noexcept; \ + [[nodiscard]] std::pair, std::vector> Build(GPUPipelineBuilder& builder) noexcept; \ + [[nodiscard]] std::pair, std::vector> Build(GPUShaderBuilder& builder) noexcept; \ + [[nodiscard]] std::pair, std::vector> Build(GPUShaderProgramBuilder& builder) noexcept; \ + [[nodiscard]] std::pair, std::vector> Build(GPUSamplerBuilder& builder) noexcept; \ + [[nodiscard]] std::pair, std::vector> Build(GPUTextureBuilder& builder) noexcept; \ +} \ +namespace destructors { \ + void Destruct(GPUBuffer& buffer) noexcept; \ + void Destruct(GPUDescriptorSet& builder) noexcept; \ + void Destruct(GPURenderBuffer& renderbuffer) noexcept; \ + void Destruct(GPUFrameBuffer& framebuffer) noexcept; \ + void Destruct(GPUPipeline& pipeline) noexcept; \ + void Destruct(GPUShader& shader) noexcept; \ + void Destruct(GPUShaderProgram& program) noexcept; \ + void Destruct(GPUSampler& sampler) noexcept; \ + void Destruct(GPUTexture& texture) noexcept; \ +} + +namespace Graphics { + void ChangeGraphicsBackend(GraphicsBackend backend) noexcept; +} + +GRAPHICS_FUNCTIONS(Graphics, Graphics::Builders, Graphics::Destructors) +GRAPHICS_FUNCTIONS(Graphics::None, Graphics::None, Graphics::None) +GRAPHICS_FUNCTIONS(Graphics::Software, Graphics::Software, Graphics::Software) +#ifdef OPENGL_4_6_SUPPORT +GRAPHICS_FUNCTIONS(Graphics::OpenGL, Graphics::OpenGL, Graphics::OpenGL) +namespace Graphics::OpenGL { + void Initialize() noexcept; +} +#endif +#ifdef OPENGL_3_3_SUPPORT +GRAPHICS_FUNCTIONS(Graphics::OpenGL33, Graphics::OpenGL33, Graphics::OpenGL33) +#endif +#ifdef VULKAN_SUPPORT +GRAPHICS_FUNCTIONS(Graphics::Vulkan, Graphics::Vulkan, Graphics::Vulkan) +#endif +#ifdef DIRECT3D_12_SUPPORT +GRAPHICS_FUNCTIONS(Graphics::Direct3D12, Graphics::Direct3D12, Graphics::Direct3D12) +#endif +#ifdef DIRECT3D_11_SUPPORT +GRAPHICS_FUNCTIONS(Graphics::Direct3D11, Graphics::Direct3D11, Graphics::Direct3D11) +#endif + +constexpr std::string_view ToString(GraphicsBackend backend) noexcept { + using enum GraphicsBackend; + switch (backend) { + case None: return "None"; + case Software: return "Software"; +#ifdef OPENGL_4_6_SUPPORT + case OpenGL4_6: return "OpenGL 4.6"; +#endif +#ifdef OPENGL_3_3_SUPPORT + case OpenGL3_3: return "OpenGL 3.3"; +#endif +#ifdef VULKAN_SUPPORT + case Vulkan: return "Vulkan"; +#endif +#ifdef DIRECT3D_12_SUPPORT + case Direct3D12: return "Direct3D 12"; +#endif +#ifdef DIRECT3D_11_SUPPORT + case Direct3D11: return "Direct3D 11"; +#endif + } + std::unreachable(); +} +constexpr std::string ToString(BufferProperties properties) noexcept { + if (static_cast(properties) == 0) { return "None"; } + + std::string result; + using enum BufferProperties; + if (static_cast(properties & DynamicStorage)) result = "DynamicStorage"; + if (static_cast(properties & ReadableFromCPU)) result += (result.empty() ? "" : " | ") + std::string("ReadableFromCPU"); + if (static_cast(properties & WriteableFromCPU)) result += (result.empty() ? "" : " | ") + std::string("WriteableFromCPU"); + if (static_cast(properties & Persistent)) result += (result.empty() ? "" : " | ") + std::string("Persistent"); + if (static_cast(properties & Coherent)) result += (result.empty() ? "" : " | ") + std::string("Coherent"); + if (static_cast(properties & CPUStorage)) result += (result.empty() ? "" : " | ") + std::string("CPUStorage"); + + return result; +} +constexpr std::string_view ToString(ShaderType type) noexcept { + using enum ShaderType; + switch (type) { + case Vertex: return "Vertex"; + case TessellationControl: return "Tessellation Control"; + case TessellationEvaluation: return "Tessellation Evaluation"; + case Geometry: return "Geometry"; + case Fragment: return "Fragment"; + case Compute: return "Compute"; + } + std::unreachable(); +} +constexpr std::string_view ToString(TextureMinificationMode mode) noexcept { + using enum TextureMinificationMode; + switch (mode) { + case Nearest: return "Nearest"; + case Linear: return "Linear"; + case NearestMipmapNearest: return "Nearest Mipmap Nearest"; + case LinearMipmapNearest: return "Linear Mipmap Nearest"; + case NearestMipmapLinear: return "Nearest Mipmap Linear"; + case LinearMipmapLinear: return "Linear Mipmap Linear"; + } + std::unreachable(); +} +constexpr std::string_view ToString(TextureMagnificationMode mode) noexcept { + using enum TextureMagnificationMode; + switch (mode) { + case Nearest: return "Nearest"; + case Linear: return "Linear"; + } + std::unreachable(); +} +constexpr std::string_view ToString(TextureWrappingMode mode) noexcept { + using enum TextureWrappingMode; + switch (mode) { + case Repeat: return "Repeat"; + case MirroredRepeat: return "Mirrored Repeat"; + case ClampToEdge: return "Clamp To Edge"; + case MirrorClampToEdge: return "Mirror Clamp To Edge"; + case ClampToBorder: return "Clamp To Border"; + } + std::unreachable(); +} +constexpr std::string_view ToString(TextureCompareMode mode) noexcept { + using enum TextureCompareMode; + switch (mode) { + case CompareRefToTexture: return "Compare Reference to Texture"; + case None: return "None"; + } + std::unreachable(); +} +constexpr std::string_view ToString(TextureCompareFunction func) noexcept { + using enum TextureCompareFunction; + switch (func) { + case LessEqual: return "Less Equal"; + case GreaterEqual: return "Greater Equal"; + case Less: return "Less"; + case Greater: return "Greater"; + case Equal: return "Equal"; + case NotEqual: return "Not Equal"; + case Always: return "Always"; + case Never: return "Never"; + } + std::unreachable(); +} +constexpr std::string_view ToString(Multisample ms) noexcept { + using enum Multisample; + switch (ms) { + case None: return "No MS"; + case x2: return "2x MS"; + case x4: return "4x MS"; + case x8: return "8x MS"; + } + std::unreachable(); +} +constexpr std::string_view ToString(DataFormat format) { + using enum DataFormat; + switch (format) { + // Unsigned unified formats + case R8: return "R8"; + case RG8: return "RG8"; + case RGB8: return "RGB8"; + case RGBA8: return "RGBA8"; + case R16: return "R16"; + case RG16: return "RG16"; + case RGB16: return "RGB16"; + case RGBA16: return "RGBA16"; + case R16F: return "R16F"; + case RG16F: return "RG16F"; + case RGB16F: return "RGB16F"; + case RGBA16F: return "RGBA16F"; + case R32F: return "R32F"; + case RG32F: return "RG32F"; + case RGB32F: return "RGB32F"; + case RGBA32F: return "RGBA32F"; + // Signed normalized unified formats + case R8_SNORM: return "R8_SNORM"; + case RG8_SNORM: return "RG8_SNORM"; + case RGB8_SNORM: return "RGB8_SNORM"; + case RGBA8_SNORM: return "RGBA8_SNORM"; + case R16_SNORM: return "R16_SNORM"; + case RG16_SNORM: return "RG16_SNORM"; + case RGB16_SNORM: return "RGB16_SNORM"; + case RGBA16_SNORM: return "RGBA16_SNORM"; + // Unsigned integer unified formats + case R8UI: return "R8UI"; + case RG8UI: return "RG8UI"; + case RGB8UI: return "RGB8UI"; + case RGBA8UI: return "RGBA8UI"; + case R16UI: return "R16UI"; + case RG16UI: return "RG16UI"; + case RGB16UI: return "RGB16UI"; + case RGBA16UI: return "RGBA16UI"; + case R32UI: return "R32UI"; + case RG32UI: return "RG32UI"; + case RGB32UI: return "RGB32UI"; + case RGBA32UI: return "RGBA32UI"; + // Signed integer unified formats + case R8I: return "R8I"; + case RG8I: return "RG8I"; + case RGB8I: return "RGB8I"; + case RGBA8I: return "RGBA8I"; + case R16I: return "R16I"; + case RG16I: return "RG16I"; + case RGB16I: return "RGB16I"; + case RGBA16I: return "RGBA16I"; + case R32I: return "R32I"; + case RG32I: return "RG32I"; + case RGB32I: return "RGB32I"; + case RGBA32I: return "RGBA32I"; + // SRGB Formats + case SRGB8: return "sRGB8"; + case SRGBA8: return "sRGBA8"; + // Depth formats + case DEPTH16: return "DEPTH16"; + case DEPTH24: return "DEPTH24"; + case DEPTH32: return "DEPTH32"; + case DEPTH32F: return "DEPTH32F"; + // Stencil formats + case STENCIL1: return "STENCIL1"; + case STENCIL4: return "STENCIL4"; + case STENCIL8: return "STENCIL8"; + case STENCIL16: return "STENCIL16"; + // Depth-stencil formats + case DEPTH24_STENCIL8: return "DEPTH24_STENCIL8"; + case DEPTH32F_STENCIL8: return "DEPTH32F_STENCIL8"; + // Split Formats + case R3G3B2: return "R3G3B2"; + case RGB5A1: return "RGB5A1"; + case RGB10A2: return "RGB10A2"; + case RGB10A2UI: return "RGB10A2UI"; + case R11FG11FB10F: return "R11FG11FB10F"; + case RGB9E5: return "RGB9E5"; + // Other Formats (when do they even get used?) + case RGB4: return "RGB4"; + case RGB5: return "RGB5"; + case RGB565: return "RGB565"; + case RGB10: return "RGB10"; + case RGB12: return "RGB12"; + case RGBA2: return "RGBA2"; + case RGBA4: return "RGBA4"; + case RGBA12: return "RGBA12"; + } + std::unreachable(); +} +constexpr std::string_view ToString(TopologyType t) noexcept { + using enum TopologyType; + switch (t) { + case Points: return "Points"; + case Lines: return "Lines"; + case LineStrip: return "Line Strip"; + case LineLoop: return "Line Loop"; + case Triangles: return "Triangles"; + case TriangleStrip: return "Triangle Strip"; + case TriangleFan: return "Triangle Fan"; + } + std::unreachable(); +} +constexpr std::string_view ToString(DataType t) noexcept { + using enum DataType; + switch (t) { + case Byte: return "Byte"; + case UnsignedByte: return "Unsigned Byte"; + case Short: return "Short"; + case UnsignedShort: return "Unsigned Short"; + case Int: return "Int"; + case UnsignedInt: return "Unsigned Int"; + case Float: return "Float"; + case Double: return "Double"; + } + std::unreachable(); +} +constexpr std::string_view ToString(PolygonMode mode) noexcept { + using enum PolygonMode; + switch (mode) { + case Fill: return "Fill"; + case Line: return "Line"; + case Point: return "Point"; + } + std::unreachable(); +} +constexpr std::string_view ToString(CullMode mode) noexcept { + using enum CullMode; + switch (mode) { + case Front: return "Front"; + case Back: return "Back"; + case FrontAndBack: return "Front And Back"; + } + std::unreachable(); +} +constexpr std::string_view ToString(DepthFunction mode) noexcept { + using enum DepthFunction; + switch (mode) { + case Always: return "Always"; + case Never: return "Never"; + case Less: return "Less"; + case Equal: return "Equal"; + case LessEqual: return "Less Equal"; + case Greater: return "Greater"; + case GreaterEqual: return "Greater Equal"; + case NotEqual: return "Not Equal"; + } + std::unreachable(); +} +constexpr std::string_view ToString(BlendFactor factor) noexcept { + using enum BlendFactor; + switch (factor) { + case Zero: return "Zero"; + case One: return "One"; + case SrcColor: return "Src Color"; + case OneMinusSrcColor: return "One Minus Src Color"; + case DstColor: return "Dst Color"; + case OneMinusDstColor: return "One Minus Dst Color"; + case SrcAlpha: return "Src Alpha"; + case OneMinusSrcAlpha: return "One Minus Src Alpha"; + case DstAlpha: return "Dst Alpha"; + case OneMinusDstAlpha: return "One Minus Dst Alpha"; + case ConstantColor: return "Constant Color"; + case OneMinusConstantColor: return "One Minus Constant Color"; + case ConstantAlpha: return "Constant Alpha"; + case OneMinusConstantAlpha: return "One Minus Constant Alpha"; + case SrcAlphaSaturate: return "Src Alpha Saturate"; + } + std::unreachable(); +} +constexpr std::string_view ToString(InputRate rate) noexcept { + using enum InputRate; + switch (rate) { + case PerVertex: return "Per Vertex"; + case PerInstance: return "Per Instance"; + } + std::unreachable(); +} + +std::ostream& operator<<(std::ostream& os, GraphicsBackend backend); +std::ostream& operator<<(std::ostream& os, BufferProperties property); +std::ostream& operator<<(std::ostream& os, ShaderType type); +std::ostream& operator<<(std::ostream& os, TextureMinificationMode mode); +std::ostream& operator<<(std::ostream& os, TextureMagnificationMode mode); +std::ostream& operator<<(std::ostream& os, TextureWrappingMode mode); +std::ostream& operator<<(std::ostream& os, TextureCompareMode mode); +std::ostream& operator<<(std::ostream& os, TextureCompareFunction func); +std::ostream& operator<<(std::ostream& os, Multisample sample); +std::ostream& operator<<(std::ostream& os, DataFormat format); +std::ostream& operator<<(std::ostream& os, TopologyType type); +std::ostream& operator<<(std::ostream& os, DataType type); +std::ostream& operator<<(std::ostream& os, PolygonMode mode); +std::ostream& operator<<(std::ostream& os, CullMode mode); +std::ostream& operator<<(std::ostream& os, DepthFunction func); +std::ostream& operator<<(std::ostream& os, BlendFactor factor); +std::ostream& operator<<(std::ostream& os, InputRate rate); \ No newline at end of file diff --git a/Engine/GraphicsGL.cpp b/Engine/GraphicsGL.cpp new file mode 100644 index 00000000..ae980218 --- /dev/null +++ b/Engine/GraphicsGL.cpp @@ -0,0 +1,1287 @@ +#ifdef OPENGL_4_6_SUPPORT +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef DEBUG +#include +static void MessageCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, GLchar const* message, void const* user_param); +#endif +static std::vector SplitCompilerMessages(const std::string& messages) noexcept; +static ShaderCompilerMessage ParseCompilerMessage(const std::string& message) noexcept; +static void QueryShaderCompilerMessages(GLuint shader, std::vector& messages) noexcept; +static std::vector SplitLinkerMessages(const std::string& messages) noexcept; +static std::string_view SymbolicConstantToShaderUniformType(GLint symbolicConstant) noexcept; +static bool LinkProgram(GLuint program, std::vector& messages) noexcept; +static std::vector ExtractActiveProgramUniforms(GLuint program, std::vector& messages) noexcept; + +namespace { + + std::optional> currentPipeline; + + Resolution GetAttachmentDimensions(const std::variant& attachment) noexcept { + return std::visit(overloaded::lambda{ + [](const GPUTexture& t) -> Resolution { + return { t.Width, t.Height }; + }, + [](const GPURenderBuffer& rb) -> Resolution { + return { rb.Width, rb.Height }; + } + }, attachment); + } + + std::pair, std::vector> BuildGraphicsPipeline(std::string& name, std::array pipelineStages) noexcept { + const GPUShader* vertShader = pipelineStages[0]; + const GPUShader* tessCtrlShader = pipelineStages[1]; + const GPUShader* tessEvalShader = pipelineStages[2]; + const GPUShader* geomShader = pipelineStages[3]; + const GPUShader* fragShader = pipelineStages[4]; + + std::vector messages{}; + if (!vertShader) { + messages.emplace_back("Vertex shader cannot be null while building a graphics pipeline."); + return { std::nullopt, std::move(messages) }; + } + if (!fragShader) { + messages.emplace_back("Fragment shader cannot be null while building a graphics pipeline."); + return { std::nullopt, std::move(messages) }; + } + + GLuint program = glCreateProgram(); + + // Attach pipeline stages + glAttachShader(program, vertShader->GLObjectID); + if (tessCtrlShader) { + glAttachShader(program, tessCtrlShader->GLObjectID); + } + if (tessEvalShader) { + glAttachShader(program, tessEvalShader->GLObjectID); + } + if (geomShader) { + glAttachShader(program, geomShader->GLObjectID); + } + glAttachShader(program, fragShader->GLObjectID); + + // Link program + bool success = LinkProgram(program, messages); + + std::optional gpuShaderProgram{ std::nullopt }; + if (success) { + gpuShaderProgram.emplace(); + gpuShaderProgram->GLObjectID = program; +#ifdef DEBUG + gpuShaderProgram->Name = std::move(name); +#endif + gpuShaderProgram->Uniforms = ExtractActiveProgramUniforms(program, messages); + } + + // Detach pipeline stages + glDetachShader(program, vertShader->GLObjectID); + if (tessCtrlShader) { + glDetachShader(program, tessCtrlShader->GLObjectID); + } + if (tessEvalShader) { + glDetachShader(program, tessEvalShader->GLObjectID); + } + if (geomShader) { + glDetachShader(program, geomShader->GLObjectID); + } + glDetachShader(program, fragShader->GLObjectID); + + return { std::move(gpuShaderProgram), std::move(messages) }; + } + std::pair, std::vector> BuildComputePipeline(std::string& name, GPUShader* computeStage) noexcept { + std::vector messages{}; + if (!computeStage) { + messages.emplace_back("Compute shader cannot be null while building a compute pipeline."); + return { std::nullopt, std::move(messages) }; + } + + GLuint program = glCreateProgram(); + + // Attach compute stage + glAttachShader(program, computeStage->GLObjectID); + + // Link program + bool success = LinkProgram(program, messages); + + std::optional gpuShaderProgram{ std::nullopt }; + if (success) { + gpuShaderProgram.emplace(); + gpuShaderProgram->GLObjectID = program; +#ifdef DEBUG + gpuShaderProgram->Name = std::move(name); +#endif + gpuShaderProgram->Uniforms = ExtractActiveProgramUniforms(program, messages); + } + + // Detach compute stage + glDetachShader(program, computeStage->GLObjectID); + + return { std::move(gpuShaderProgram), std::move(messages) }; + } +} + +void Graphics::OpenGL::Initialize() noexcept { + DOA_LOG_INFO("OpenGL version: %s", glGetString(GL_VERSION)); + DOA_LOG_INFO("GLSL version: %s", glGetString(GL_SHADING_LANGUAGE_VERSION)); + DOA_LOG_INFO("Vendor: %s", glGetString(GL_VENDOR)); + DOA_LOG_INFO("GPU: %s", glGetString(GL_RENDERER)); + +#ifdef DEBUG + glEnable(GL_DEBUG_OUTPUT); + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); + glDebugMessageCallback(MessageCallback, nullptr); + glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_NOTIFICATION, 0, nullptr, GL_FALSE); +#endif +} + +void Graphics::OpenGL::BufferSubData(GPUBuffer& buffer, size_t sizeBytes, NonOwningPointerToConstRawData data, size_t offsetBytes) noexcept { + assert(static_cast(buffer.Properties & BufferProperties::DynamicStorage)); + glNamedBufferSubData(buffer.GLObjectID, offsetBytes, sizeBytes, data); +} +void Graphics::OpenGL::GetBufferSubData(const GPUBuffer& buffer, RawDataWriteableView dataView, size_t offsetBytes) noexcept { + glGetNamedBufferSubData(buffer.GLObjectID, offsetBytes, dataView.size_bytes(), dataView.data()); +} +void Graphics::OpenGL::CopyBufferSubData(const GPUBuffer& readBuffer, GPUBuffer& writeBuffer, size_t sizeBytesToCopy, size_t readOffsetBytes, size_t writeOffsetBytes) noexcept { + assert(readBuffer.GLObjectID > 0); + assert(readBuffer.GLObjectID > 0); + glCopyNamedBufferSubData( + readBuffer.GLObjectID, + writeBuffer.GLObjectID, + readOffsetBytes, + writeOffsetBytes, + sizeBytesToCopy + ); +} +void Graphics::OpenGL::ClearBufferSubData(GPUBuffer& buffer, DataFormat format, size_t sizeBytesToClear, size_t offsetBytes) noexcept { + glClearNamedBufferSubData(buffer.GLObjectID, ToGLSizedFormat(format), offsetBytes, sizeBytesToClear, ToGLBaseFormat(format), GL_UNSIGNED_BYTE, nullptr); +} + +void Graphics::OpenGL::Blit(const GPUFrameBuffer& source, GPUFrameBuffer& destination) noexcept { + bool blitColor = !source.ColorAttachments.empty() && !destination.ColorAttachments.empty(); + bool blitDepth = + (source.DepthAttachment.has_value() || source.DepthStencilAttachment.has_value()) && + (destination.DepthAttachment.has_value() || destination.DepthStencilAttachment.has_value()); + bool blitStencil = + (source.StencilAttachment.has_value() || source.DepthStencilAttachment.has_value()) && + (destination.StencilAttachment.has_value() || destination.DepthStencilAttachment.has_value()); + + unsigned srcX{}, srcY{}, dstX{}, dstY{}; + if (blitColor) { + const auto& src { source.ColorAttachments[0].value() }; + std::visit(overloaded::lambda{ + [&srcX, &srcY](const GPUTexture& t) { + srcX = t.Width; + srcY = t.Height; + }, + [&srcX, &srcY](const GPURenderBuffer& rb) { + srcX = rb.Width; + srcY = rb.Height; + }}, src); + + const auto& dst{ destination.ColorAttachments[0].value() }; + std::visit(overloaded::lambda{ + [&dstX, &dstY](const GPUTexture& t) { + dstX = t.Width; + dstY = t.Height; + }, + [&dstX, &dstY](const GPURenderBuffer& rb) { + dstX = rb.Width; + dstY = rb.Height; + }}, dst); + } else if (blitDepth) { + const std::variant* src{}; + if (source.DepthAttachment.has_value()) { + src = &source.DepthAttachment.value(); + } else { + src = &source.DepthStencilAttachment.value(); + } + std::visit(overloaded::lambda{ + [&srcX, &srcY](const GPUTexture& t) { + srcX = t.Width; + srcY = t.Height; + }, + [&srcX, &srcY](const GPURenderBuffer& rb) { + srcX = rb.Width; + srcY = rb.Height; + }}, *src); + + const std::variant* dst{}; + if (destination.DepthAttachment.has_value()) { + dst = &destination.DepthAttachment.value(); + } else { + dst = &destination.DepthStencilAttachment.value(); + } + std::visit(overloaded::lambda{ + [&dstX, &dstY](const GPUTexture& t) { + dstX = t.Width; + dstY = t.Height; + }, + [&dstX, &dstY](const GPURenderBuffer& rb) { + dstX = rb.Width; + dstY = rb.Height; + }}, *dst); + } else if (blitStencil) { + const std::variant* src{}; + if (source.StencilAttachment.has_value()) { + src = &source.StencilAttachment.value(); + } else { + src = &source.DepthStencilAttachment.value(); + } + std::visit(overloaded::lambda{ + [&srcX, &srcY](const GPUTexture& t) { + srcX = t.Width; + srcY = t.Height; + }, + [&srcX, &srcY](const GPURenderBuffer& rb) { + srcX = rb.Width; + srcY = rb.Height; + }}, *src); + + const std::variant* dst{}; + if (destination.StencilAttachment.has_value()) { + dst = &destination.StencilAttachment.value(); + } else { + dst = &destination.DepthStencilAttachment.value(); + } + std::visit(overloaded::lambda{ + [&dstX, &dstY](const GPUTexture& t) { + dstX = t.Width; + dstY = t.Height; + }, + [&dstX, &dstY](const GPURenderBuffer& rb) { + dstX = rb.Width; + dstY = rb.Height; + }}, *dst); + } else { + return; + } + + auto mask = (blitColor ? GL_COLOR_BUFFER_BIT : GLuint()) | + (blitDepth ? GL_DEPTH_BUFFER_BIT : GLuint()) | + (blitStencil ? GL_STENCIL_BUFFER_BIT : GLuint()); + + glBlitNamedFramebuffer( + source.GLObjectID, + destination.GLObjectID, + 0, 0, srcX, srcY, + 0, 0, dstX, dstY, + mask, + GL_NEAREST + ); +} +void Graphics::OpenGL::BlitColor(const GPUFrameBuffer& source, GPUFrameBuffer& destination, unsigned srcAttachment, std::span dstAttachments) noexcept { + assert(srcAttachment < source.ColorAttachments.size()); + assert(source.ColorAttachments[srcAttachment].has_value()); + + assert(std::ranges::all_of(dstAttachments, [&destination](auto dstAttachment) { return dstAttachment < destination.ColorAttachments.size(); })); + assert(std::ranges::all_of(dstAttachments, [&destination](auto dstAttachment) { return destination.ColorAttachments[dstAttachment].has_value(); })); + assert(std::ranges::all_of(dstAttachments, [&destination, &dstAttachments](auto dstAttachment) { + return GetAttachmentDimensions(destination.ColorAttachments[dstAttachments[0]].value()) == GetAttachmentDimensions(destination.ColorAttachments[dstAttachment].value()); + })); + + std::array drawBuffers{ GL_NONE }; + for (auto dstAttachment : dstAttachments) { + drawBuffers[dstAttachment] = GL_COLOR_ATTACHMENT0 + dstAttachment; + } + + // Set read/draw buffers + glNamedFramebufferReadBuffer(source.GLObjectID, GL_COLOR_ATTACHMENT0 + srcAttachment); + glNamedFramebufferDrawBuffers(destination.GLObjectID, static_cast(drawBuffers.size()), drawBuffers.data()); + + Resolution srcResolution{ GetAttachmentDimensions(source.ColorAttachments[srcAttachment].value()) }; + Resolution dstResolution{ GetAttachmentDimensions(destination.ColorAttachments[dstAttachments[0]].value()) }; + + glBlitNamedFramebuffer( + source.GLObjectID, + destination.GLObjectID, + 0, 0, srcResolution.Width, srcResolution.Height, + 0, 0, dstResolution.Width, dstResolution.Height, + GL_COLOR_BUFFER_BIT, + GL_NEAREST + ); +} +void Graphics::OpenGL::BlitDepth(const GPUFrameBuffer& source, GPUFrameBuffer& destination) noexcept { + const auto* srcAttachment = source.DepthAttachment ? &source.DepthAttachment : nullptr; + const auto* dstAttachment = destination.DepthAttachment ? &destination.DepthAttachment : nullptr; + + assert(srcAttachment && srcAttachment->has_value()); + assert(dstAttachment && dstAttachment->has_value()); + + Resolution srcResolution{ GetAttachmentDimensions(srcAttachment->value()) }; + Resolution dstResolution{ GetAttachmentDimensions(dstAttachment->value()) }; + + glBlitNamedFramebuffer( + source.GLObjectID, + destination.GLObjectID, + 0, 0, srcResolution.Width, srcResolution.Height, + 0, 0, dstResolution.Width, dstResolution.Height, + GL_DEPTH_BUFFER_BIT, + GL_NEAREST + ); +} +void Graphics::OpenGL::BlitStencil(const GPUFrameBuffer& source, GPUFrameBuffer& destination) noexcept { + const auto* srcAttachment = source.StencilAttachment ? &source.StencilAttachment : nullptr; + const auto* dstAttachment = destination.StencilAttachment ? &destination.StencilAttachment : nullptr; + + assert(srcAttachment && srcAttachment->has_value()); + assert(dstAttachment && dstAttachment->has_value()); + + Resolution srcResolution{ GetAttachmentDimensions(srcAttachment->value()) }; + Resolution dstResolution{ GetAttachmentDimensions(dstAttachment->value()) }; + + glBlitNamedFramebuffer( + source.GLObjectID, + destination.GLObjectID, + 0, 0, srcResolution.Width, srcResolution.Height, + 0, 0, dstResolution.Width, dstResolution.Height, + GL_STENCIL_BUFFER_BIT, + GL_NEAREST + ); +} +void Graphics::OpenGL::BlitDepthStencil(const GPUFrameBuffer& source, GPUFrameBuffer& destination) noexcept { + const auto* srcAttachment = source.DepthStencilAttachment ? &source.DepthStencilAttachment : nullptr; + const auto* dstAttachment = destination.DepthStencilAttachment ? &destination.DepthStencilAttachment : nullptr; + + assert(srcAttachment && srcAttachment->has_value()); + assert(dstAttachment && dstAttachment->has_value()); + + Resolution srcResolution{ GetAttachmentDimensions(srcAttachment->value()) }; + Resolution dstResolution{ GetAttachmentDimensions(dstAttachment->value()) }; + + glBlitNamedFramebuffer( + source.GLObjectID, + destination.GLObjectID, + 0, 0, srcResolution.Width, srcResolution.Height, + 0, 0, dstResolution.Width, dstResolution.Height, + GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, + GL_NEAREST + ); +} + +void Graphics::OpenGL::Render(int count, int first) noexcept { + const GPUPipeline& pipeline = currentPipeline->get(); + if (pipeline.IndexBuffer) { + glDrawElements(ToGLTopology(pipeline.Topology), count, ToGLDataType(pipeline.IndexType), nullptr); + } else { + glDrawArrays(ToGLTopology(pipeline.Topology), first, count); + } +} +void Graphics::OpenGL::RenderInstanced(int instanceCount, int count, int first) noexcept { + const GPUPipeline& pipeline = currentPipeline->get(); + if (pipeline.IndexBuffer) { + glDrawElementsInstanced(ToGLTopology(pipeline.Topology), count, ToGLDataType(pipeline.IndexType), nullptr, instanceCount); + } else { + glDrawArraysInstanced(ToGLTopology(pipeline.Topology), first, count, instanceCount); + } +} + +void Graphics::OpenGL::SetRenderTarget(const GPUFrameBuffer& renderTarget) noexcept { + if (renderTarget.GLObjectID != 0) { + std::array drawBuffers{ GL_NONE }; + for (size_t i = 0; i < renderTarget.ColorAttachments.size(); ++i) { + if (renderTarget.ColorAttachments[i].has_value()) { + drawBuffers[i] = GL_COLOR_ATTACHMENT0 + static_cast(i); + } + } + glNamedFramebufferDrawBuffers(renderTarget.GLObjectID, static_cast(drawBuffers.size()), drawBuffers.data()); + } + glBindFramebuffer(GL_FRAMEBUFFER, renderTarget.GLObjectID); +} +void Graphics::OpenGL::SetRenderTarget(const GPUFrameBuffer& renderTarget, std::span targets) noexcept { + std::array drawBuffers{ GL_NONE }; + if (targets.size() < drawBuffers.size()) { + std::ranges::transform(targets, drawBuffers.begin(), [](auto target) { return GL_COLOR_ATTACHMENT0 + target; }); + } else { + std::transform(targets.begin(), targets.begin() + drawBuffers.size(), drawBuffers.begin(), [](auto target) { return GL_COLOR_ATTACHMENT0 + target; }); + } + glNamedFramebufferDrawBuffers(renderTarget.GLObjectID, static_cast(drawBuffers.size()), drawBuffers.data()); + glBindFramebuffer(GL_FRAMEBUFFER, renderTarget.GLObjectID); +} +void Graphics::OpenGL::ClearRenderTargetColor(const GPUFrameBuffer& renderTarget, std::array color, unsigned colorBufferIndex) noexcept { + assert(renderTarget.ColorAttachments[colorBufferIndex]); + glClearNamedFramebufferfv(renderTarget.GLObjectID, GL_COLOR, colorBufferIndex, color.data()); +} +void Graphics::OpenGL::ClearRenderTargetColors(const GPUFrameBuffer& renderTarget, std::array color) noexcept { + for (size_t i = 0; i < renderTarget.ColorAttachments.size(); i++) { + if (renderTarget.ColorAttachments[i]) { + glClearNamedFramebufferfv(renderTarget.GLObjectID, GL_COLOR, static_cast(i), color.data()); + } + } +} +void Graphics::OpenGL::ClearRenderTargetDepth(const GPUFrameBuffer& renderTarget, float depth) noexcept { + assert(renderTarget.DepthAttachment || renderTarget.DepthStencilAttachment); + glClearNamedFramebufferfv(renderTarget.GLObjectID, GL_DEPTH, 0, &depth); +} +void Graphics::OpenGL::ClearRenderTargetStencil(const GPUFrameBuffer& renderTarget, int stencil) noexcept { + assert(renderTarget.StencilAttachment || renderTarget.DepthStencilAttachment); + glClearNamedFramebufferiv(renderTarget.GLObjectID, GL_STENCIL, 0, &stencil); +} +void Graphics::OpenGL::ClearRenderTarget(const GPUFrameBuffer& renderTarget, std::array color, float depth, int stencil) noexcept { + if (!renderTarget.ColorAttachments.empty()) { + ClearRenderTargetColors(renderTarget, color); + } + if (renderTarget.DepthStencilAttachment || (renderTarget.DepthAttachment && renderTarget.StencilAttachment)) { + glClearNamedFramebufferfi(renderTarget.GLObjectID, GL_DEPTH_STENCIL, 0, depth, stencil); + } else { + if (renderTarget.DepthAttachment) { + ClearRenderTargetDepth(renderTarget, depth); + } + if (renderTarget.StencilAttachment) { + ClearRenderTargetStencil(renderTarget, stencil); + } + } +} + +void Graphics::OpenGL::BindPipeline(const GPUPipeline& pipeline) noexcept { + currentPipeline.emplace(pipeline); + + glPolygonMode(GL_FRONT_AND_BACK, ToGLPolygonMode(pipeline.Polygon)); + pipeline.IsFaceCullingEnabled ? glEnable(GL_CULL_FACE) : glDisable(GL_CULL_FACE); + glCullFace(ToGLCullMode(pipeline.Cull)); + glViewport(pipeline.Viewport.X, pipeline.Viewport.Y, pipeline.Viewport.Width, pipeline.Viewport.Height); + + pipeline.IsScissorEnabled ? glEnable(GL_SCISSOR_TEST) : glDisable(GL_SCISSOR_TEST); + glScissor(pipeline.Scissor.X, pipeline.Scissor.Y, pipeline.Scissor.Width, pipeline.Scissor.Height); + + pipeline.IsDepthTestEnabled ? glEnable(GL_DEPTH_TEST) : glDisable(GL_DEPTH_TEST); + glDepthMask(pipeline.IsDepthWriteEnabled ? GL_TRUE : GL_FALSE); + glDepthFunc(ToGLDepthFunction(pipeline.DepthFunc)); + pipeline.IsDepthClampEnabled ? glEnable(GL_DEPTH_CLAMP) : glDisable(GL_DEPTH_CLAMP); + + pipeline.IsMultisampleEnabled ? glEnable(GL_MULTISAMPLE) : glDisable(GL_MULTISAMPLE); + + pipeline.IsBlendEnabled ? glEnable(GL_BLEND) : glDisable(GL_BLEND); + glBlendFuncSeparate( + ToGLBlendFactor(pipeline.SourceFactor), + ToGLBlendFactor(pipeline.DestinationFactor), + ToGLBlendFactor(pipeline.SourceAlphaFactor), + ToGLBlendFactor(pipeline.DestinationAlphaFactor) + ); + + assert(pipeline.ShaderProgram); + glUseProgram(pipeline.ShaderProgram->get().GLObjectID); + glBindVertexArray(pipeline.GLObjectID); +} + +void Graphics::OpenGL::BindDescriptorSet(const GPUDescriptorSet& descriptorSet) noexcept { + for (const DescriptorBinding& binding : descriptorSet.Bindings) { + if (std::holds_alternative(binding.Descriptor)) { continue; } + + std::visit(overloaded::lambda { + [] (const std::monostate&) { /* empty */ }, + [&binding](const DescriptorBinding::UniformBuffer& uniformBuffer) { + glBindBufferBase(GL_UNIFORM_BUFFER, binding.BindingSlot, uniformBuffer.Buffer.get().GLObjectID); + }, + [&binding](const DescriptorBinding::StorageBuffer& storageBuffer) { + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, binding.BindingSlot, storageBuffer.Buffer.get().GLObjectID); + }, + [&binding](const DescriptorBinding::CombinedImageSampler& combinedImageSampler) { + glBindTextureUnit(binding.BindingSlot, combinedImageSampler.Texture.get().GLObjectID); + glBindSampler(binding.BindingSlot, combinedImageSampler.Sampler.get().GLObjectID); + }, + }, binding.Descriptor); + } +} + +std::pair, std::vector> Graphics::OpenGL::Build(GPUBufferBuilder& builder) noexcept { + GLuint buffer; + glCreateBuffers(1, &buffer); + + glNamedBufferStorage(buffer, builder.size, builder.data, ToGLBufferFlags(builder.properties)); + + std::optional gpuBuffer{ std::nullopt }; + gpuBuffer.emplace(); + gpuBuffer->GLObjectID = buffer; +#ifdef DEBUG + gpuBuffer->Name = std::move(builder.name); +#endif + gpuBuffer->Properties = builder.properties; + gpuBuffer->SizeBytes = builder.size; + + return { std::move(gpuBuffer), {} }; +} +std::pair, std::vector> Graphics::OpenGL::Build(GPUDescriptorSetBuilder& builder) noexcept { + std::optional gpuDescriptorSet{ std::nullopt }; + gpuDescriptorSet.emplace(); + + gpuDescriptorSet->Bindings = std::move(builder.bindings); // this doesn't perform a move - yet (std::array.operator=(&&) copies) + + return { gpuDescriptorSet, {} }; +} +std::pair, std::vector> Graphics::OpenGL::Build(GPURenderBufferBuilder& builder) noexcept { + GLuint renderBuffer; + glCreateRenderbuffers(1, &renderBuffer); + if (builder.samples == Multisample::None) { + glNamedRenderbufferStorage( + renderBuffer, + ToGLSizedFormat(builder.format), + builder.width, builder.height + ); + } else { + glNamedRenderbufferStorageMultisample( + renderBuffer, + static_cast(builder.samples), + ToGLSizedFormat(builder.format), + builder.width, builder.height + ); + } + + std::optional gpuRenderBuffer{ std::nullopt }; + gpuRenderBuffer.emplace(); + gpuRenderBuffer->GLObjectID = renderBuffer; +#ifdef DEBUG + gpuRenderBuffer->Name = std::move(builder.name); +#endif + gpuRenderBuffer->Width = builder.width; + gpuRenderBuffer->Height = builder.height; + gpuRenderBuffer->Format = builder.format; + gpuRenderBuffer->Samples = builder.samples; + + return { std::move(gpuRenderBuffer), {} }; +} +std::pair, std::vector> Graphics::OpenGL::Build(GPUFrameBufferBuilder& builder) noexcept { + GLuint frameBuffer; + glCreateFramebuffers(1, &frameBuffer); + + std::array drawBuffers{ GL_NONE }; + for (size_t i = 0; i < builder.colorAttachments.size(); i++) { + const auto& colorAttachment = builder.colorAttachments[i]; + + if (builder.colorAttachments[i].has_value()) { + drawBuffers[i] = GL_COLOR_ATTACHMENT0 + static_cast(i); + std::visit(overloaded::lambda { + [frameBuffer, &i](const GPUTexture& texture) { + glNamedFramebufferTexture(frameBuffer, GL_COLOR_ATTACHMENT0 + static_cast(i), texture.GLObjectID, 0); + }, + [frameBuffer, &i](const GPURenderBuffer& renderBuffer) { + glNamedFramebufferRenderbuffer(frameBuffer, GL_COLOR_ATTACHMENT0 + static_cast(i), GL_RENDERBUFFER, renderBuffer.GLObjectID); + } + }, colorAttachment.value()); + } + } + glNamedFramebufferDrawBuffers(frameBuffer, static_cast(drawBuffers.size()), drawBuffers.data()); + + if (builder.depthStencilAttachment) { + std::visit(overloaded::lambda{ + [frameBuffer](const GPUTexture& texture) { + glNamedFramebufferTexture(frameBuffer, GL_DEPTH_STENCIL_ATTACHMENT, texture.GLObjectID, 0); + }, + [frameBuffer](const GPURenderBuffer& renderBuffer) { + glNamedFramebufferRenderbuffer(frameBuffer, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, renderBuffer.GLObjectID); + } + }, builder.depthStencilAttachment.value()); + } else { + if (builder.depthAttachment) { + std::visit(overloaded::lambda{ + [frameBuffer](const GPUTexture& texture) { + glNamedFramebufferTexture(frameBuffer, GL_DEPTH_ATTACHMENT, texture.GLObjectID, 0); + }, + [frameBuffer](const GPURenderBuffer& renderBuffer) { + glNamedFramebufferRenderbuffer(frameBuffer, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderBuffer.GLObjectID); + } + }, builder.depthAttachment.value()); + } + if (builder.stencilAttachment) { + std::visit(overloaded::lambda{ + [frameBuffer](const GPUTexture& texture) { + glNamedFramebufferTexture(frameBuffer, GL_STENCIL_ATTACHMENT, texture.GLObjectID, 0); + }, + [frameBuffer](const GPURenderBuffer& renderBuffer) { + glNamedFramebufferRenderbuffer(frameBuffer, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, renderBuffer.GLObjectID); + } + }, builder.stencilAttachment.value()); + } + } + + std::vector messages{}; + GLenum status = glCheckNamedFramebufferStatus(frameBuffer, GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + messages.emplace_back("Framebuffer is not complete."); + switch (status) { + case GL_FRAMEBUFFER_UNDEFINED: + messages.emplace_back("Framebuffer is undefined."); + break; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + messages.emplace_back("Framebuffer has incomplete attachment."); + break; + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + messages.emplace_back("Framebuffer is missing attachment."); + break; + case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: + messages.emplace_back("Framebuffer has incomplete draw buffer."); + break; + case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: + messages.emplace_back("Framebuffer has incomplete read buffer."); + break; + case GL_FRAMEBUFFER_UNSUPPORTED: + messages.emplace_back("Framebuffer is unsupported."); + break; + case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: + messages.emplace_back("Framebuffer has incomplete multisample."); + break; + case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS: + messages.emplace_back("Framebuffer has incomplete layer targets."); + break; + default: + std::unreachable(); + } + } + + std::optional gpuFrameBuffer{ std::nullopt }; + gpuFrameBuffer.emplace(); + gpuFrameBuffer->GLObjectID = frameBuffer; +#ifdef DEBUG + gpuFrameBuffer->Name = std::move(builder.name); +#endif + gpuFrameBuffer->ColorAttachments = std::move(builder.colorAttachments); + gpuFrameBuffer->DepthAttachment = std::move(builder.depthAttachment); + gpuFrameBuffer->StencilAttachment = std::move(builder.stencilAttachment); + gpuFrameBuffer->DepthStencilAttachment = std::move(builder.depthStencilAttachment); + + return { std::move(gpuFrameBuffer), std::move(messages) }; +} +std::pair, std::vector> Graphics::OpenGL::Build(GPUPipelineBuilder& builder) noexcept { + assert(builder.vertexBuffers.size() == builder.vertexLayouts.size()); // impossible + + if (!builder.shaderProgam) { + return { std::nullopt, { "Cannot create pipeline without a shader program!" } }; + } + + GLuint vertexArray; + glCreateVertexArrays(1, &vertexArray); + + unsigned attribIndex{}; + for (unsigned bindingIndx = 0; bindingIndx < builder.vertexBuffers.size(); bindingIndx++) { + if (!builder.vertexBuffers[bindingIndx].has_value()) { continue; } + + const GPUBuffer& vertexBuffer{ builder.vertexBuffers[bindingIndx].value().get() }; + const GPUVertexAttribLayout& layout{ builder.vertexLayouts[bindingIndx] }; + glVertexArrayVertexBuffer(vertexArray, bindingIndx, vertexBuffer.GLObjectID, 0, layout.Stride); + glVertexArrayBindingDivisor(vertexArray, bindingIndx, ToGLInputRate(layout.InputRate)); + for (unsigned i = 0; i < layout.Elements.size(); i++) { + const GPUVertexAttribLayout::Element& elem{ layout.Elements[i] }; + if (elem.Count == 0) { continue; } + + glEnableVertexArrayAttrib(vertexArray, attribIndex); + if (elem.Type == GL_INT || + elem.Type == GL_BYTE || + elem.Type == GL_SHORT || + elem.Type == GL_UNSIGNED_INT || + elem.Type == GL_UNSIGNED_BYTE || + elem.Type == GL_UNSIGNED_SHORT) { + glVertexArrayAttribIFormat(vertexArray, attribIndex, elem.Count, elem.Type, layout.Offsets[i]); + } else if (elem.Type == GL_DOUBLE) { + glVertexArrayAttribLFormat(vertexArray, attribIndex, elem.Count, elem.Type, layout.Offsets[i]); + } else { + glVertexArrayAttribFormat(vertexArray, attribIndex, elem.Count, elem.Type, elem.IsNormalized, layout.Offsets[i]); + } + glVertexArrayAttribBinding(vertexArray, attribIndex, bindingIndx); + attribIndex++; + } + } + + if (builder.indexBuffer) { + glVertexArrayElementBuffer(vertexArray, builder.indexBuffer->GLObjectID); + } + + std::optional gpuPipeline{ std::nullopt }; + gpuPipeline.emplace(); + gpuPipeline->GLObjectID = vertexArray; +#ifdef DEBUG + gpuPipeline->Name = std::move(builder.name); +#endif + gpuPipeline->VertexBuffers = std::move(builder.vertexBuffers); + gpuPipeline->VertexLayouts = std::move(builder.vertexLayouts); + gpuPipeline->IndexBuffer = builder.indexBuffer; + gpuPipeline->IndexType = builder.indexType; + gpuPipeline->Topology = builder.topology; + gpuPipeline->IsFaceCullingEnabled = builder.isFaceCullingEnabled; + gpuPipeline->Cull = builder.cullMode; + gpuPipeline->Polygon = builder.polygonMode; + gpuPipeline->Viewport = builder.viewport; + gpuPipeline->IsScissorEnabled = builder.isScissorEnabled; + gpuPipeline->Scissor = builder.scissor; + gpuPipeline->IsDepthTestEnabled = builder.isDepthTestEnabled; + gpuPipeline->IsDepthWriteEnabled = builder.isDepthWriteEnabled; + gpuPipeline->DepthFunc = builder.depthFunction; + gpuPipeline->IsDepthClampEnabled = builder.isDepthClampEnabled; + gpuPipeline->IsMultisampleEnabled = builder.isMultisampleEnabled; + gpuPipeline->IsBlendEnabled = builder.isBlendEnabled; + gpuPipeline->SourceFactor = builder.srcRGBFactor; + gpuPipeline->DestinationFactor = builder.dstRGBFactor; + gpuPipeline->SourceAlphaFactor = builder.srcAlphaFactor; + gpuPipeline->DestinationAlphaFactor = builder.dstAlphaFactor; + gpuPipeline->ShaderProgram = builder.shaderProgam; + + return { std::move(gpuPipeline), {} }; +} +std::pair, std::vector> Graphics::OpenGL::Build(GPUShaderBuilder& builder) noexcept { + std::vector messages{}; + messages.emplace_back(0, ShaderCompilerMessage::Type::Info, std::format("Make sure this file contains GLSL {} Shader code!", ToString(builder.type))); + + GLuint shader{}; + switch (builder.type) { + using enum ShaderType; + case Vertex: shader = glCreateShader(GL_VERTEX_SHADER); break; + case TessellationControl: shader = glCreateShader(GL_TESS_CONTROL_SHADER); break; + case TessellationEvaluation: shader = glCreateShader(GL_TESS_EVALUATION_SHADER); break; + case Geometry: shader = glCreateShader(GL_GEOMETRY_SHADER); break; + case Fragment: shader = glCreateShader(GL_FRAGMENT_SHADER); break; + case Compute: shader = glCreateShader(GL_COMPUTE_SHADER); break; + } + if (shader == decltype(shader){}) { + // switch-case failed / user entered out of bounds type! + messages.emplace_back(0, ShaderCompilerMessage::Type::Error, std::format("{} Shader compilation failed!", ToString(builder.type))); + messages.emplace_back(0, ShaderCompilerMessage::Type::Error, "Couldn't make out shader type!"); + return { std::nullopt, messages }; + } + + const auto source = builder.sourceCode.data(); + glShaderSource(shader, 1, &source, nullptr); + + int success; + glCompileShader(shader); + glGetShaderiv(shader, GL_COMPILE_STATUS, &success); + + QueryShaderCompilerMessages(shader, messages); + + if (!success) { + messages.emplace_back(0, ShaderCompilerMessage::Type::Error, std::format("{} Shader compilation failed!", ToString(builder.type))); + glDeleteShader(shader); + } + + std::optional gpuShader{ std::nullopt }; + if (success) { + gpuShader.emplace(); + gpuShader->GLObjectID = shader; + gpuShader->Type = builder.type; +#ifdef DEBUG + gpuShader->Name = std::move(builder.name); +#endif + } + + return { std::move(gpuShader), std::move(messages) }; +} +std::pair, std::vector> Graphics::OpenGL::Build(GPUShaderProgramBuilder& builder) noexcept { + if (builder.compShader) { +#ifdef DEBUG + return BuildComputePipeline(builder.name, builder.compShader); +#elif NDEBUG + std::string name = ""; + return BuildComputePipeline(name, builder.compShader); +#endif + } else if (builder.vertShader && builder.fragShader) { + std::array stages { + builder.vertShader, + builder.tessCtrlShader, + builder.tessEvalShader, + builder.geomShader, + builder.fragShader + }; +#ifdef DEBUG + return BuildGraphicsPipeline(builder.name, stages); +#elif NDEBUG + std::string name = ""; + return BuildGraphicsPipeline(name, stages); +#endif + } else { + return { std::nullopt, { "No pipeline can be built with shaders set.", "Make sure you set either both Vertex and Fragment shader or the Compute Shader alone." } }; + } +} +std::pair, std::vector> Graphics::OpenGL::Build(GPUSamplerBuilder& builder) noexcept { + GLuint sampler; + glGenSamplers(1, &sampler); + + glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, ToGLTextureMinificationMode(builder.minFilter)); + glSamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, ToGLTextureMagnificationMode(builder.magFilter)); + glSamplerParameterf(sampler, GL_TEXTURE_MIN_LOD, builder.minLOD); + glSamplerParameterf(sampler, GL_TEXTURE_MAX_LOD, builder.maxLOD); + glSamplerParameterf(sampler, GL_TEXTURE_LOD_BIAS, builder.LODBias); + glSamplerParameteri(sampler, GL_TEXTURE_WRAP_S, ToGLTextureWrappingMode(builder.wrapS)); + glSamplerParameteri(sampler, GL_TEXTURE_WRAP_T, ToGLTextureWrappingMode(builder.wrapT)); + glSamplerParameteri(sampler, GL_TEXTURE_WRAP_R, ToGLTextureWrappingMode(builder.wrapR)); + glSamplerParameterfv(sampler, GL_TEXTURE_BORDER_COLOR, builder.borderColor.data()); + glSamplerParameteri(sampler, GL_TEXTURE_COMPARE_MODE, ToGLTextureCompareMode(builder.compareMode)); + glSamplerParameteri(sampler, GL_TEXTURE_COMPARE_FUNC, ToGLTextureCompareFunction(builder.compareFunction)); + glSamplerParameterf(sampler, GL_TEXTURE_MAX_ANISOTROPY_EXT, builder.maxAnisotropy); + glSamplerParameteri(sampler, GL_TEXTURE_CUBE_MAP_SEAMLESS, builder.cubemapSeamless); + + std::optional gpuSampler{ std::nullopt }; + gpuSampler.emplace(); + gpuSampler->GLObjectID = sampler; +#ifdef DEBUG + gpuSampler->Name = std::move(builder.name); +#endif + + return { std::move(gpuSampler), {} }; +} +std::pair, std::vector> Graphics::OpenGL::Build(GPUTextureBuilder& builder) noexcept { + GLuint texture; + if (builder.depth > 1) { + assert(builder.samples == Multisample::None); // Multisampled 3D textures are not allowed. + glCreateTextures(GL_TEXTURE_3D, 1, &texture); + glTextureStorage3D( + texture, + static_cast(1 + std::floor(std::log2(std::max({ builder.width, builder.height, builder.depth })))), + ToGLSizedFormat(builder.format), + builder.width, builder.height, builder.depth + ); + } else if (builder.width > 1) { + if (builder.samples == Multisample::None) { + glCreateTextures(GL_TEXTURE_2D, 1, &texture); + glTextureStorage2D( + texture, + static_cast(1 + std::floor(std::log2(std::max(builder.width, builder.height)))), + ToGLSizedFormat(builder.format), + builder.width, builder.height + ); + } else { + glCreateTextures(GL_TEXTURE_2D_MULTISAMPLE, 1, &texture); + glTextureStorage2DMultisample( + texture, + static_cast(builder.samples), + ToGLSizedFormat(builder.format), + builder.width, builder.height, + GL_TRUE + ); + } + } else { + assert(builder.samples == Multisample::None); // Multisampled 1D textures are not allowed. + glCreateTextures(GL_TEXTURE_1D, 1, &texture); + glTextureStorage1D( + texture, + static_cast(1 + std::floor(std::log2(builder.width))), + ToGLSizedFormat(builder.format), + builder.width + ); + } + + if (!builder.pixels.empty()) { + if (builder.depth > 1) { + glTextureSubImage3D( + texture, + 0, + 0, 0, 0, + builder.width, builder.height, builder.depth, + ToGLBaseFormat(builder.format), + GL_UNSIGNED_BYTE, + builder.pixels.data() + ); + } else if (builder.width > 1) { + glTextureSubImage2D( + texture, + 0, + 0, 0, + builder.width, builder.height, + ToGLBaseFormat(builder.format), + GL_UNSIGNED_BYTE, + builder.pixels.data() + ); + } else { + glTextureSubImage1D( + texture, + 0, + 0, + builder.width, + ToGLBaseFormat(builder.format), + GL_UNSIGNED_BYTE, + builder.pixels.data() + ); + } + if (builder.samples == Multisample::None) { + glGenerateTextureMipmap(texture); // Mipmaps are not allowed on multisampled textures. + } + } + + std::optional gpuTexture{ std::nullopt }; + gpuTexture.emplace(); + gpuTexture->GLObjectID = texture; +#ifdef DEBUG + gpuTexture->Name = std::move(builder.name); +#endif + gpuTexture->Width = builder.width; + gpuTexture->Height = builder.height; + gpuTexture->Depth = builder.depth; + gpuTexture->Format = builder.format; + + return { std::move(gpuTexture), {} }; +} + +void Graphics::OpenGL::Destruct(GPUBuffer& buffer) noexcept { + glDeleteBuffers(1, &buffer.GLObjectID); +} +void Graphics::OpenGL::Destruct([[maybe_unused]] GPUDescriptorSet& set) noexcept {} +void Graphics::OpenGL::Destruct(GPURenderBuffer& renderbuffer) noexcept { + glDeleteRenderbuffers(1, &renderbuffer.GLObjectID); +} +void Graphics::OpenGL::Destruct(GPUFrameBuffer& framebuffer) noexcept { + glDeleteFramebuffers(1, &framebuffer.GLObjectID); +} +void Graphics::OpenGL::Destruct(GPUPipeline& pipeline) noexcept { + glDeleteVertexArrays(1, &pipeline.GLObjectID); +} +void Graphics::OpenGL::Destruct(GPUShader& shader) noexcept { + glDeleteShader(shader.GLObjectID); +} +void Graphics::OpenGL::Destruct(GPUShaderProgram& program) noexcept { + glDeleteProgram(program.GLObjectID); +} +void Graphics::OpenGL::Destruct(GPUSampler& sampler) noexcept { + glDeleteSamplers(1, &sampler.GLObjectID); +} +void Graphics::OpenGL::Destruct(GPUTexture& texture) noexcept { + glDeleteTextures(1, &texture.GLObjectID); +} + +#ifdef DEBUG +static void MessageCallback(GLenum source, GLenum type, GLuint id, GLenum severity, [[maybe_unused]] GLsizei length, const GLchar* message, [[maybe_unused]] const void* user_param) { + auto const src_str = [source]() { + switch (source) { + case GL_DEBUG_SOURCE_API: return "API"; + case GL_DEBUG_SOURCE_WINDOW_SYSTEM: return "WINDOW SYSTEM"; + case GL_DEBUG_SOURCE_SHADER_COMPILER: return "SHADER COMPILER"; + case GL_DEBUG_SOURCE_THIRD_PARTY: return "THIRD PARTY"; + case GL_DEBUG_SOURCE_APPLICATION: return "APPLICATION"; + case GL_DEBUG_SOURCE_OTHER: return "OTHER"; + default: return "DEFAULT"; + } + }(); + + auto const type_str = [type]() { + switch (type) { + case GL_DEBUG_TYPE_ERROR: return "ERROR"; + case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: return "DEPRECATED_BEHAVIOR"; + case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: return "UNDEFINED_BEHAVIOR"; + case GL_DEBUG_TYPE_PORTABILITY: return "PORTABILITY"; + case GL_DEBUG_TYPE_PERFORMANCE: return "PERFORMANCE"; + case GL_DEBUG_TYPE_MARKER: return "MARKER"; + case GL_DEBUG_TYPE_OTHER: return "OTHER"; + default: return "DEFAULT"; + } + }(); + + auto const severity_str = [severity]() { + switch (severity) { + case GL_DEBUG_SEVERITY_NOTIFICATION: return "NOTIFICATION"; + case GL_DEBUG_SEVERITY_LOW: return "LOW"; + case GL_DEBUG_SEVERITY_MEDIUM: return "MEDIUM"; + case GL_DEBUG_SEVERITY_HIGH: return "HIGH"; + default: return "DEFAULT"; + } + }(); + + DOA_LOG_OPENGL("%s, %s, %s, %d: %s", src_str, type_str, severity_str, id, message); + debug_break(); +} +#endif +static std::vector SplitCompilerMessages(const std::string& messages) noexcept { + static const std::regex regex{ "\n(?!\\s)" }; + + std::vector rv; + std::copy( + std::sregex_token_iterator{ messages.cbegin(), messages.cend(), regex, -1 }, + std::sregex_token_iterator{}, + std::back_inserter(rv) + ); + + return rv; +} +static ShaderCompilerMessage ParseCompilerMessage(const std::string& message) noexcept { + static const std::regex regex{ "0\\((\\d*)\\)\\s*:\\s*(\\w*)\\s*\\w*\\s*:\\s*(.*)" }; + + ShaderCompilerMessage rv; + + std::smatch match; + if (std::regex_search(message, match, regex)) { + rv.FullMessage = trim_copy(message); + rv.LineNo = std::stoi(match[1]); + std::string type = match[2]; + if (type == "info") { + rv.MessageType = ShaderCompilerMessage::Type::Info; + } else if (type == "warning") { + rv.MessageType = ShaderCompilerMessage::Type::Warning; + } else if (type == "error") { + rv.MessageType = ShaderCompilerMessage::Type::Error; + } else { + assert(false); // matches to something else, find and implement! + } + rv.ShortMessage = match[3]; + } else { + assert(false); // investigate why regex didn't match! + } + return rv; +} +static void QueryShaderCompilerMessages(GLuint shader, std::vector& messages) noexcept { + GLint bufferLength; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &bufferLength); + if (bufferLength > 1) { + GLchar* logChars = new char[bufferLength + 1]; + glGetShaderInfoLog(shader, bufferLength, NULL, logChars); + std::string logString{ logChars }; + trim(logString); + auto logs = SplitCompilerMessages(logString); + for (const auto& log : logs) { + messages.emplace_back(ParseCompilerMessage(log)); + } + delete[] logChars; + } +} +static std::vector SplitLinkerMessages(const std::string& messages) noexcept { + static const std::regex regex{ "\n(?!\\s)" }; + + std::vector rv; + std::copy( + std::sregex_token_iterator{ messages.cbegin(), messages.cend(), regex, -1 }, + std::sregex_token_iterator{}, + std::back_inserter(rv) + ); + + return rv; +} +static std::string_view SymbolicConstantToShaderUniformType(GLint symbolicConstant) noexcept { + if (symbolicConstant == GL_FLOAT) { return "float"; } + else if (symbolicConstant == GL_FLOAT_VEC2) { return "vec2"; } + else if (symbolicConstant == GL_FLOAT_VEC3) { return "vec3"; } + else if (symbolicConstant == GL_FLOAT_VEC4) { return "vec4"; } + + else if (symbolicConstant == GL_DOUBLE) { return "double"; } + else if (symbolicConstant == GL_DOUBLE_VEC2) { return "dvec2"; } + else if (symbolicConstant == GL_DOUBLE_VEC3) { return "dvec3"; } + else if (symbolicConstant == GL_DOUBLE_VEC4) { return "dvec4"; } + + else if (symbolicConstant == GL_INT) { return "int"; } + else if (symbolicConstant == GL_INT_VEC2) { return "ivec2"; } + else if (symbolicConstant == GL_INT_VEC3) { return "ivec3"; } + else if (symbolicConstant == GL_INT_VEC4) { return "ivec4"; } + + else if (symbolicConstant == GL_UNSIGNED_INT) { return "unsigned int"; } + else if (symbolicConstant == GL_UNSIGNED_INT_VEC2) { return "uvec2"; } + else if (symbolicConstant == GL_UNSIGNED_INT_VEC3) { return "uvec3"; } + else if (symbolicConstant == GL_UNSIGNED_INT_VEC4) { return "uvec4"; } + + else if (symbolicConstant == GL_BOOL) { return "bool"; } + else if (symbolicConstant == GL_BOOL_VEC2) { return "bvec2"; } + else if (symbolicConstant == GL_BOOL_VEC3) { return "bvec3"; } + else if (symbolicConstant == GL_BOOL_VEC4) { return "bvec4"; } + + else if (symbolicConstant == GL_FLOAT_MAT2) { return "mat2"; } + else if (symbolicConstant == GL_FLOAT_MAT3) { return "mat3"; } + else if (symbolicConstant == GL_FLOAT_MAT4) { return "mat4"; } + else if (symbolicConstant == GL_FLOAT_MAT2x3) { return "mat2x3"; } + else if (symbolicConstant == GL_FLOAT_MAT2x4) { return "mat2x4"; } + else if (symbolicConstant == GL_FLOAT_MAT3x2) { return "mat3x2"; } + else if (symbolicConstant == GL_FLOAT_MAT3x4) { return "mat3x4"; } + else if (symbolicConstant == GL_FLOAT_MAT4x2) { return "mat4x2"; } + else if (symbolicConstant == GL_FLOAT_MAT4x3) { return "mat4x3"; } + + else if (symbolicConstant == GL_DOUBLE_MAT2) { return "dmat2"; } + else if (symbolicConstant == GL_DOUBLE_MAT3) { return "dmat3"; } + else if (symbolicConstant == GL_DOUBLE_MAT4) { return "dmat4"; } + else if (symbolicConstant == GL_DOUBLE_MAT2x3) { return "dmat2x3"; } + else if (symbolicConstant == GL_DOUBLE_MAT2x4) { return "dmat2x4"; } + else if (symbolicConstant == GL_DOUBLE_MAT3x2) { return "dmat3x2"; } + else if (symbolicConstant == GL_DOUBLE_MAT3x4) { return "dmat3x4"; } + else if (symbolicConstant == GL_DOUBLE_MAT4x2) { return "dmat4x2"; } + else if (symbolicConstant == GL_DOUBLE_MAT4x3) { return "dmat4x3"; } + + else if (symbolicConstant == GL_SAMPLER_1D) { return "sampler1D"; } + else if (symbolicConstant == GL_SAMPLER_2D) { return "sampler2D"; } + else if (symbolicConstant == GL_SAMPLER_3D) { return "sampler3D"; } + else if (symbolicConstant == GL_SAMPLER_CUBE) { return "samplerCube"; } + else if (symbolicConstant == GL_SAMPLER_1D_SHADOW) { return "sampler1DShadow"; } + else if (symbolicConstant == GL_SAMPLER_2D_SHADOW) { return "sampler2DShadow"; } + else if (symbolicConstant == GL_SAMPLER_1D_ARRAY) { return "sampler1DArray"; } + else if (symbolicConstant == GL_SAMPLER_2D_ARRAY) { return "sampler2DArray"; } + else if (symbolicConstant == GL_SAMPLER_1D_ARRAY_SHADOW) { return "sampler1DArrayShadow"; } + else if (symbolicConstant == GL_SAMPLER_2D_ARRAY_SHADOW) { return "sampler2DArrayShadow"; } + else if (symbolicConstant == GL_SAMPLER_2D_MULTISAMPLE) { return "sampler2DMS"; } + else if (symbolicConstant == GL_SAMPLER_2D_MULTISAMPLE_ARRAY) { return "sampler2DMSArray"; } + else if (symbolicConstant == GL_SAMPLER_CUBE_SHADOW) { return "samplerCubeShadow"; } + else if (symbolicConstant == GL_SAMPLER_BUFFER) { return "samplerBuffer"; } + else if (symbolicConstant == GL_SAMPLER_2D_RECT) { return "sampler2DRect"; } + else if (symbolicConstant == GL_SAMPLER_2D_RECT_SHADOW) { return "sampler2DRectShadow"; } + + else if (symbolicConstant == GL_INT_SAMPLER_1D) { return "isampler1D"; } + else if (symbolicConstant == GL_INT_SAMPLER_2D) { return "isampler2D"; } + else if (symbolicConstant == GL_INT_SAMPLER_3D) { return "isampler3D"; } + else if (symbolicConstant == GL_INT_SAMPLER_CUBE) { return "isamplerCube"; } + else if (symbolicConstant == GL_INT_SAMPLER_1D_ARRAY) { return "isampler1DArray"; } + else if (symbolicConstant == GL_INT_SAMPLER_2D_ARRAY) { return "isampler2DArray"; } + else if (symbolicConstant == GL_INT_SAMPLER_2D_MULTISAMPLE) { return "isampler2DMS"; } + else if (symbolicConstant == GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY) { return "isampler2DMSArray"; } + else if (symbolicConstant == GL_INT_SAMPLER_BUFFER) { return "isamplerBuffer"; } + else if (symbolicConstant == GL_INT_SAMPLER_2D_RECT) { return "isampler2DRect"; } + + else if (symbolicConstant == GL_UNSIGNED_INT_SAMPLER_1D) { return "usampler1D"; } + else if (symbolicConstant == GL_UNSIGNED_INT_SAMPLER_2D) { return "usampler2D"; } + else if (symbolicConstant == GL_UNSIGNED_INT_SAMPLER_3D) { return "usampler3D"; } + else if (symbolicConstant == GL_UNSIGNED_INT_SAMPLER_CUBE) { return "usamplerCube"; } + else if (symbolicConstant == GL_UNSIGNED_INT_SAMPLER_1D_ARRAY) { return "usampler2DArray"; } + else if (symbolicConstant == GL_UNSIGNED_INT_SAMPLER_2D_ARRAY) { return "usampler2DArray"; } + else if (symbolicConstant == GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE) { return "usampler2DMS"; } + else if (symbolicConstant == GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY) { return "usampler2DMSArray"; } + else if (symbolicConstant == GL_UNSIGNED_INT_SAMPLER_BUFFER) { return "usamplerBuffer"; } + else if (symbolicConstant == GL_UNSIGNED_INT_SAMPLER_2D_RECT) { return "usampler2DRect"; } + + else if (symbolicConstant == GL_IMAGE_1D) { return "image1D"; } + else if (symbolicConstant == GL_IMAGE_2D) { return "image2D"; } + else if (symbolicConstant == GL_IMAGE_3D) { return "image3D"; } + else if (symbolicConstant == GL_IMAGE_2D_RECT) { return "image2DRect"; } + else if (symbolicConstant == GL_IMAGE_CUBE) { return "imageCube"; } + else if (symbolicConstant == GL_IMAGE_BUFFER) { return "imageBuffer"; } + else if (symbolicConstant == GL_IMAGE_1D_ARRAY) { return "image1DArray"; } + else if (symbolicConstant == GL_IMAGE_2D_ARRAY) { return "image2DArray"; } + else if (symbolicConstant == GL_IMAGE_2D_MULTISAMPLE) { return "image2DMS"; } + else if (symbolicConstant == GL_IMAGE_2D_MULTISAMPLE_ARRAY) { return "image2DMSArray"; } + + else if (symbolicConstant == GL_INT_IMAGE_1D) { return "iimage1D"; } + else if (symbolicConstant == GL_INT_IMAGE_2D) { return "iimage2D"; } + else if (symbolicConstant == GL_INT_IMAGE_3D) { return "iimage3D"; } + else if (symbolicConstant == GL_INT_IMAGE_2D_RECT) { return "iimage2DRect"; } + else if (symbolicConstant == GL_INT_IMAGE_CUBE) { return "iimageCube"; } + else if (symbolicConstant == GL_INT_IMAGE_BUFFER) { return "iimageBuffer"; } + else if (symbolicConstant == GL_INT_IMAGE_1D_ARRAY) { return "iimage1DArray"; } + else if (symbolicConstant == GL_INT_IMAGE_2D_ARRAY) { return "iimage2DArray"; } + else if (symbolicConstant == GL_INT_IMAGE_2D_MULTISAMPLE) { return "iimage2DMS"; } + else if (symbolicConstant == GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY) { return "iimage2DMSArray"; } + + else if (symbolicConstant == GL_UNSIGNED_INT_IMAGE_1D) { return "uimage1D"; } + else if (symbolicConstant == GL_UNSIGNED_INT_IMAGE_2D) { return "uimage2D"; } + else if (symbolicConstant == GL_UNSIGNED_INT_IMAGE_3D) { return "uimage3D"; } + else if (symbolicConstant == GL_UNSIGNED_INT_IMAGE_2D_RECT) { return "uimage2DRect"; } + else if (symbolicConstant == GL_UNSIGNED_INT_IMAGE_CUBE) { return "uimageCube"; } + else if (symbolicConstant == GL_UNSIGNED_INT_IMAGE_BUFFER) { return "uimageBuffer"; } + else if (symbolicConstant == GL_UNSIGNED_INT_IMAGE_1D_ARRAY) { return "uimage1DArray"; } + else if (symbolicConstant == GL_UNSIGNED_INT_IMAGE_2D_ARRAY) { return "uimage2DArray"; } + else if (symbolicConstant == GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE) { return "uimage2DMS"; } + else if (symbolicConstant == GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY) { return "uimage2DMSArray"; } + + else if (symbolicConstant == GL_UNSIGNED_INT_ATOMIC_COUNTER) { return "atomic_uint"; } + + else { + DOA_LOG_WARNING("Detected unknown type of uniform. Symbolic constant: %d", symbolicConstant); + std::unreachable(); + } +} +static bool LinkProgram(GLuint program, std::vector& messages) noexcept { + GLint success; + + glLinkProgram(program); + glGetProgramiv(program, GL_LINK_STATUS, &success); + if (!success) { + messages.emplace_back("Program linking failed!"); + + GLint bufferLength; + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufferLength); + if (bufferLength > 1) { + GLchar* logChars = new char[bufferLength + 1]; + glGetProgramInfoLog(program, bufferLength, NULL, logChars); + std::string logString{ logChars }; + trim(logString); + auto logs = SplitLinkerMessages(logString); + for (auto& log : logs) { + messages.emplace_back(std::move(log)); + } + delete[] logChars; + } + + glDeleteProgram(program); + } + + return static_cast(success); +} +static std::vector ExtractActiveProgramUniforms(GLuint program, std::vector& messages) noexcept { + std::vector rv; + + // Extract active uniforms + GLint numActiveUniforms = 0; + glGetProgramInterfaceiv(program, GL_UNIFORM, GL_ACTIVE_RESOURCES, &numActiveUniforms); + std::vector nameData(256); + std::vector properties; + properties.push_back(GL_NAME_LENGTH); // 0 + properties.push_back(GL_TYPE); // 1 + properties.push_back(GL_ARRAY_SIZE); // 2 + properties.push_back(GL_REFERENCED_BY_VERTEX_SHADER); // 3 + properties.push_back(GL_REFERENCED_BY_TESS_CONTROL_SHADER); // 4 + properties.push_back(GL_REFERENCED_BY_TESS_EVALUATION_SHADER); // 5 + properties.push_back(GL_REFERENCED_BY_GEOMETRY_SHADER); // 6 + properties.push_back(GL_REFERENCED_BY_FRAGMENT_SHADER); // 7 + properties.push_back(GL_REFERENCED_BY_COMPUTE_SHADER); // 8 + std::vector values(properties.size()); + + for (int i = 0; i < numActiveUniforms; i++) { + GPUShaderProgram::Uniform uniform; + glGetProgramResourceiv( + program, + GL_UNIFORM, i, + static_cast(properties.size()), properties.data(), + static_cast(values.size()), NULL, values.data() + ); + + // Extract uniform name + nameData.resize(values[0]); //The length of the name. + glGetProgramResourceName( + program, + GL_UNIFORM, i, + static_cast(nameData.size()), NULL, nameData.data() + ); + uniform.Name = std::string(nameData.data(), nameData.size() - 1); + + // Extract uniform location + uniform.Location = glGetUniformLocation(program, uniform.Name.c_str()); + + // Extract uniform type name + uniform.TypeName = SymbolicConstantToShaderUniformType(values[1]); + + // Extract uniform array size (eg. extracts 100 from "mat4 bones[100]") + uniform.ArraySize = values[2]; + + // Extract uniform source (which shader it comes from) + if (values[3]) { + uniform.ReferencedBy = ShaderType::Vertex; + } else if (values[4]) { + uniform.ReferencedBy = ShaderType::TessellationControl; + } else if (values[5]) { + uniform.ReferencedBy = ShaderType::TessellationEvaluation; + } else if (values[6]) { + uniform.ReferencedBy = ShaderType::Geometry; + } else if (values[7]) { + uniform.ReferencedBy = ShaderType::Fragment; + } else if (values[8]) { + uniform.ReferencedBy = ShaderType::Compute; + } else { + messages.emplace_back(std::format("Uniform {} not referenced by any shader.", uniform.Name)); + continue; // error! abort this uniform and continue with the next one. + } + + int idx = 0; + while (idx < uniform.ArraySize) { + rv.emplace_back(uniform); + + uniform.Location++; + uniform.Name = std::format("{}[{}]", uniform.Name.substr(0, uniform.Name.size() - 3), idx + 1); + idx++; + } + } + + return rv; +} +#endif \ No newline at end of file diff --git a/Engine/GraphicsGL.hpp b/Engine/GraphicsGL.hpp new file mode 100644 index 00000000..4c185883 --- /dev/null +++ b/Engine/GraphicsGL.hpp @@ -0,0 +1,342 @@ +#pragma once + +#ifdef OPENGL_4_6_SUPPORT + +#include + +#include + +#include + +constexpr GLenum ToGLBufferFlags(BufferProperties properties) noexcept { + GLenum glBitmask = 0; + using enum BufferProperties; + if (static_cast(properties & DynamicStorage)) glBitmask |= GL_DYNAMIC_STORAGE_BIT; + if (static_cast(properties & ReadableFromCPU)) glBitmask |= GL_MAP_READ_BIT; + if (static_cast(properties & WriteableFromCPU)) glBitmask |= GL_MAP_WRITE_BIT; + if (static_cast(properties & Persistent)) glBitmask |= GL_MAP_PERSISTENT_BIT; + if (static_cast(properties & Coherent)) glBitmask |= GL_MAP_COHERENT_BIT; + if (static_cast(properties & CPUStorage)) glBitmask |= GL_CLIENT_STORAGE_BIT; + return glBitmask; +} +constexpr GLenum ToGLTextureMinificationMode(TextureMinificationMode mode) noexcept { + using enum TextureMinificationMode; + switch (mode) { + case Nearest: return GL_NEAREST; + case Linear: return GL_LINEAR; + case NearestMipmapNearest: return GL_NEAREST_MIPMAP_NEAREST; + case LinearMipmapNearest: return GL_LINEAR_MIPMAP_NEAREST; + case NearestMipmapLinear: return GL_NEAREST_MIPMAP_LINEAR; + case LinearMipmapLinear: return GL_LINEAR_MIPMAP_LINEAR; + } + std::unreachable(); +} +constexpr GLenum ToGLTextureMagnificationMode(TextureMagnificationMode mode) noexcept { + using enum TextureMagnificationMode; + switch (mode) { + case Nearest: return GL_NEAREST; + case Linear: return GL_LINEAR; + } + std::unreachable(); +} +constexpr GLenum ToGLTextureWrappingMode(TextureWrappingMode mode) noexcept { + using enum TextureWrappingMode; + switch (mode) { + case Repeat: return GL_REPEAT; + case MirroredRepeat: return GL_MIRRORED_REPEAT; + case ClampToEdge: return GL_CLAMP_TO_EDGE; + case MirrorClampToEdge: return GL_MIRROR_CLAMP_TO_EDGE; + case ClampToBorder: return GL_CLAMP_TO_BORDER; + } + std::unreachable(); +} +constexpr GLenum ToGLTextureCompareMode(TextureCompareMode mode) noexcept { + using enum TextureCompareMode; + switch (mode) { + case CompareRefToTexture: return GL_COMPARE_REF_TO_TEXTURE; + case None: return GL_NONE; + } + std::unreachable(); +} +constexpr GLenum ToGLTextureCompareFunction(TextureCompareFunction func) noexcept { + using enum TextureCompareFunction; + switch (func) { + case LessEqual: return GL_LEQUAL; + case GreaterEqual: return GL_GEQUAL; + case Less: return GL_LESS; + case Greater: return GL_GREATER; + case Equal: return GL_EQUAL; + case NotEqual: return GL_NOTEQUAL; + case Always: return GL_ALWAYS; + case Never: return GL_NEVER; + } + std::unreachable(); +} +constexpr GLenum ToGLSizedFormat(DataFormat format) { + using enum DataFormat; + switch (format) { + // Unsigned normalized formats + case R8: return GL_R8; + case RG8: return GL_RG8; + case RGB8: return GL_RGB8; + case RGBA8: return GL_RGBA8; + case R16: return GL_R16; + case RG16: return GL_RG16; + case RGB16: return GL_RGB16; + case RGBA16: return GL_RGBA16; + case R16F: return GL_R16F; + case RG16F: return GL_RG16F; + case RGB16F: return GL_RGB16F; + case RGBA16F: return GL_RGBA16F; + case R32F: return GL_R32F; + case RG32F: return GL_RG32F; + case RGB32F: return GL_RGB32F; + case RGBA32F: return GL_RGBA32F; + // Signed normalized formats + case R8_SNORM: return GL_R8_SNORM; + case RG8_SNORM: return GL_RG8_SNORM; + case RGB8_SNORM: return GL_RGB8_SNORM; + case RGBA8_SNORM: return GL_RGBA8_SNORM; + case R16_SNORM: return GL_R16_SNORM; + case RG16_SNORM: return GL_RG16_SNORM; + case RGB16_SNORM: return GL_RGB16_SNORM; + case RGBA16_SNORM: return GL_RGBA16_SNORM; + // Unsigned integer formats + case R8UI: return GL_R8UI; + case RG8UI: return GL_RG8UI; + case RGB8UI: return GL_RGB8UI; + case RGBA8UI: return GL_RGBA8UI; + case R16UI: return GL_R16UI; + case RG16UI: return GL_RG16UI; + case RGB16UI: return GL_RGB16UI; + case RGBA16UI: return GL_RGBA16UI; + case R32UI: return GL_R32UI; + case RG32UI: return GL_RG32UI; + case RGB32UI: return GL_RGB32UI; + case RGBA32UI: return GL_RGBA32UI; + // Signed integer formats + case R8I: return GL_R8I; + case RG8I: return GL_RG8I; + case RGB8I: return GL_RGB8I; + case RGBA8I: return GL_RGBA8I; + case R16I: return GL_R16I; + case RG16I: return GL_RG16I; + case RGB16I: return GL_RGB16I; + case RGBA16I: return GL_RGBA16I; + case R32I: return GL_R32I; + case RG32I: return GL_RG32I; + case RGB32I: return GL_RGB32I; + case RGBA32I: return GL_RGBA32I; + // SRGB Formats + case SRGB8: return GL_SRGB8; + case SRGBA8: return GL_SRGB8_ALPHA8; + // Depth formats + case DEPTH16: return GL_DEPTH_COMPONENT16; + case DEPTH24: return GL_DEPTH_COMPONENT24; + case DEPTH32: return GL_DEPTH_COMPONENT32; + case DEPTH32F: return GL_DEPTH_COMPONENT32F; + // Stencil Formats + case STENCIL1: return GL_STENCIL_INDEX1; + case STENCIL4: return GL_STENCIL_INDEX4; + case STENCIL8: return GL_STENCIL_INDEX8; + case STENCIL16: return GL_STENCIL_INDEX16; + // Depth-stencil formats + case DEPTH24_STENCIL8: return GL_DEPTH24_STENCIL8; + case DEPTH32F_STENCIL8: return GL_DEPTH32F_STENCIL8; + // Split Formats + case R3G3B2: return GL_R3_G3_B2; + case RGB5A1: return GL_RGB5_A1; + case RGB10A2: return GL_RGB10_A2; + case RGB10A2UI: return GL_RGB10_A2UI; + case R11FG11FB10F: return GL_R11F_G11F_B10F; + case RGB9E5: return GL_RGB9_E5; + // Other Formats (when do they even get used?) + case RGB4: return GL_RGB4; + case RGB5: return GL_RGB5; + case RGB565: return GL_RGB565; + case RGB10: return GL_RGB10; + case RGB12: return GL_RGB12; + case RGBA2: return GL_RGBA2; + case RGBA4: return GL_RGBA4; + case RGBA12: return GL_RGBA12; + } + std::unreachable(); +} +constexpr GLenum ToGLBaseFormat(DataFormat format) { + using enum DataFormat; + switch (format) { + case R8: + case R16: + case R16F: + case R32F: + case R8_SNORM: + case R16_SNORM: + case R8UI: + case R16UI: + case R32UI: + case R8I: + case R16I: + case R32I: + return GL_RED; + case RG8: + case RG16: + case RG16F: + case RG32F: + case RG8_SNORM: + case RG16_SNORM: + case RG8UI: + case RG16UI: + case RG32UI: + case RG8I: + case RG16I: + case RG32I: + return GL_RG; + case RGB8: + case RGB16: + case RGB16F: + case RGB32F: + case RGB8_SNORM: + case RGB16_SNORM: + case RGB8UI: + case RGB16UI: + case RGB32UI: + case RGB8I: + case RGB16I: + case RGB32I: + case R3G3B2: + case R11FG11FB10F: + case RGB9E5: + case RGB4: + case RGB5: + case RGB565: + case RGB10: + case RGB12: + return GL_RGB; + case RGBA8: + case RGBA16: + case RGBA16F: + case RGBA32F: + case RGBA8_SNORM: + case RGBA16_SNORM: + case RGBA8UI: + case RGBA16UI: + case RGBA32UI: + case RGBA8I: + case RGBA16I: + case RGBA32I: + case RGB5A1: + case RGB10A2: + case RGB10A2UI: + case RGBA2: + case RGBA4: + case RGBA12: + return GL_RGBA; + case SRGB8: + return GL_SRGB; + case SRGBA8: + return GL_SRGB_ALPHA; + case DEPTH16: + case DEPTH24: + case DEPTH32: + case DEPTH32F: + return GL_DEPTH_COMPONENT; + case STENCIL1: + case STENCIL4: + case STENCIL8: + case STENCIL16: + return GL_STENCIL_INDEX; + case DEPTH24_STENCIL8: + case DEPTH32F_STENCIL8: + return GL_DEPTH_STENCIL; + } + std::unreachable(); +} +constexpr GLenum ToGLTopology(TopologyType t) noexcept { + using enum TopologyType; + switch (t) { + case Points: return GL_POINTS; + case Lines: return GL_LINES; + case LineStrip: return GL_LINE_STRIP; + case LineLoop: return GL_LINE_LOOP; + case Triangles: return GL_TRIANGLES; + case TriangleStrip: return GL_TRIANGLE_STRIP; + case TriangleFan: return GL_TRIANGLE_FAN; + } + std::unreachable(); +} +constexpr GLenum ToGLDataType(DataType t) noexcept { + using enum DataType; + switch (t) { + case Byte: return GL_BYTE; + case UnsignedByte: return GL_UNSIGNED_BYTE; + case Short: return GL_SHORT; + case UnsignedShort: return GL_UNSIGNED_SHORT; + case Int: return GL_INT; + case UnsignedInt: return GL_UNSIGNED_INT; + case Float: return GL_FLOAT; + case Double: return GL_DOUBLE; + } + std::unreachable(); +} +constexpr GLenum ToGLPolygonMode(PolygonMode mode) noexcept { + using enum PolygonMode; + switch (mode) { + case Fill: return GL_FILL; + case Line: return GL_LINE; + case Point: return GL_POINT; + } + std::unreachable(); +} +constexpr GLenum ToGLCullMode(CullMode mode) noexcept { + using enum CullMode; + switch (mode) { + case Front: return GL_FRONT; + case Back: return GL_BACK; + case FrontAndBack: return GL_FRONT_AND_BACK; + } + std::unreachable(); +} +constexpr GLenum ToGLDepthFunction(DepthFunction mode) noexcept { + using enum DepthFunction; + switch (mode) { + case Always: return GL_ALWAYS; + case Never: return GL_NEVER; + case Less: return GL_LESS; + case Equal: return GL_EQUAL; + case LessEqual: return GL_LEQUAL; + case Greater: return GL_GREATER; + case GreaterEqual: return GL_NOTEQUAL; + case NotEqual: return GL_GEQUAL; + } + std::unreachable(); +} +constexpr GLenum ToGLBlendFactor(BlendFactor factor) noexcept { + using enum BlendFactor; + switch (factor) { + case Zero: return GL_ZERO; + case One: return GL_ONE; + case SrcColor: return GL_SRC_COLOR; + case OneMinusSrcColor: return GL_ONE_MINUS_SRC_COLOR; + case DstColor: return GL_DST_COLOR; + case OneMinusDstColor: return GL_ONE_MINUS_DST_COLOR; + case SrcAlpha: return GL_SRC_ALPHA; + case OneMinusSrcAlpha: return GL_ONE_MINUS_SRC_ALPHA; + case DstAlpha: return GL_DST_ALPHA; + case OneMinusDstAlpha: return GL_ONE_MINUS_DST_ALPHA; + case ConstantColor: return GL_CONSTANT_COLOR; + case OneMinusConstantColor: return GL_ONE_MINUS_CONSTANT_COLOR; + case ConstantAlpha: return GL_CONSTANT_ALPHA; + case OneMinusConstantAlpha: return GL_ONE_MINUS_CONSTANT_ALPHA; + case SrcAlphaSaturate: return GL_SRC_ALPHA_SATURATE; + } + std::unreachable(); +} +constexpr GLuint ToGLInputRate(InputRate rate) noexcept { + using enum InputRate; + switch (rate) { + case PerVertex: return 0; + case PerInstance: return 1; + } + std::unreachable(); +} +#endif \ No newline at end of file diff --git a/Engine/GraphicsNone.cpp b/Engine/GraphicsNone.cpp new file mode 100644 index 00000000..065b6dcb --- /dev/null +++ b/Engine/GraphicsNone.cpp @@ -0,0 +1,86 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER // Check if compiling with Visual C++ +#pragma warning(disable : 4100) // disable warning C4100: 'parameter': unreferenced formal parameter +#elif defined(__GNUC__) || defined(__clang__) // Check if compiling with GCC or Clang +#pragma GCC diagnostic ignored "-Wunused-parameter" // ignore -Wunused-parameter warning +#endif + +void Graphics::None::BufferSubData(GPUBuffer& buffer, size_t sizeBytes, NonOwningPointerToConstRawData data, size_t offsetBytes) noexcept {} +void Graphics::None::GetBufferSubData(const GPUBuffer& buffer, RawDataWriteableView dataView, size_t offsetBytes) noexcept {} +void Graphics::None::CopyBufferSubData(const GPUBuffer& readBuffer, GPUBuffer& writeBuffer, size_t sizeBytesToCopy, size_t readOffsetBytes, size_t writeOffsetBytes) noexcept {} +void Graphics::None::ClearBufferSubData(GPUBuffer& buffer, DataFormat format, size_t sizeBytesToClear, size_t offsetBytes) noexcept {} + +void Graphics::None::Blit(const GPUFrameBuffer& source, GPUFrameBuffer& destination) noexcept {} +void Graphics::None::BlitColor(const GPUFrameBuffer& source, GPUFrameBuffer& destination, unsigned srcAttachment, std::span dstAttachments) noexcept {} +void Graphics::None::BlitDepth(const GPUFrameBuffer& source, GPUFrameBuffer& destination) noexcept {} +void Graphics::None::BlitStencil(const GPUFrameBuffer& source, GPUFrameBuffer& destination) noexcept {} +void Graphics::None::BlitDepthStencil(const GPUFrameBuffer& source, GPUFrameBuffer& destination) noexcept {} + +void Graphics::None::Render(int count, int first) noexcept {} +void Graphics::None::RenderInstanced(int instanceCount, int count, int first) noexcept {} + +void Graphics::None::SetRenderTarget(const GPUFrameBuffer& renderTarget) noexcept {} +void Graphics::None::SetRenderTarget(const GPUFrameBuffer& renderTarget, std::span targets) noexcept {} +void Graphics::None::ClearRenderTargetColor(const GPUFrameBuffer& renderTarget, std::array color, unsigned colorBufferIndex) noexcept {} +void Graphics::None::ClearRenderTargetColors(const GPUFrameBuffer& renderTarget, std::array color) noexcept {} +void Graphics::None::ClearRenderTargetDepth(const GPUFrameBuffer& renderTarget, float depth) noexcept {} +void Graphics::None::ClearRenderTargetStencil(const GPUFrameBuffer& renderTarget, int stencil) noexcept {} +void Graphics::None::ClearRenderTarget(const GPUFrameBuffer& renderTarget, std::array color, float depth, int stencil) noexcept {} + +void Graphics::None::BindPipeline(const GPUPipeline& pipeline) noexcept {} + +void Graphics::None::BindDescriptorSet(const GPUDescriptorSet& descriptorSet) noexcept {} + +std::pair, std::vector> Graphics::None::Build(GPUBufferBuilder& builder) noexcept { + return { {{}}, { "You're using no-op graphics backend.", "This object will not function as desired." } }; +} +std::pair, std::vector> Graphics::None::Build(GPUDescriptorSetBuilder& builder) noexcept { + return { {{}}, { "You're using no-op graphics backend.", "This object will not function as desired." } }; +} +std::pair, std::vector> Graphics::None::Build(GPURenderBufferBuilder& builder) noexcept { + return { {{}}, { "You're using no-op graphics backend.", "This object will not function as desired." } }; +} +std::pair, std::vector> Graphics::None::Build(GPUFrameBufferBuilder& builder) noexcept { + return { {{}}, { "You're using no-op graphics backend.", "This object will not function as desired." } }; +} +std::pair, std::vector> Graphics::None::Build(GPUPipelineBuilder& builder) noexcept { + return { {{}}, { "You're using no-op graphics backend.", "This object will not function as desired." } }; +} +std::pair, std::vector> Graphics::None::Build(GPUShaderBuilder& builder) noexcept { + return { {{}}, {{ 0, ShaderCompilerMessage::Type::Info, "You're using no-op graphics backend.", }, { 0, ShaderCompilerMessage::Type::Info, "This object will not function as desired.", }}}; +} +std::pair, std::vector> Graphics::None::Build(GPUShaderProgramBuilder& builder) noexcept { + return { {{}}, { "You're using no-op graphics backend.", "This object will not function as desired." } }; +} +std::pair, std::vector> Graphics::None::Build(GPUSamplerBuilder& builder) noexcept { + return { {{}}, { "You're using no-op graphics backend.", "This object will not function as desired." } }; +} +std::pair, std::vector> Graphics::None::Build(GPUTextureBuilder& builder) noexcept { + return { {{}}, { "You're using no-op graphics backend.", "This object will not function as desired." } }; +} + +void Graphics::None::Destruct(GPUBuffer& buffer) noexcept {} +void Graphics::None::Destruct(GPUDescriptorSet& set) noexcept {} +void Graphics::None::Destruct(GPURenderBuffer& renderbuffer) noexcept {} +void Graphics::None::Destruct(GPUFrameBuffer& framebuffer) noexcept {} +void Graphics::None::Destruct(GPUPipeline& pipeline) noexcept {} +void Graphics::None::Destruct(GPUShader& shader) noexcept {} +void Graphics::None::Destruct(GPUShaderProgram& program) noexcept {} +void Graphics::None::Destruct(GPUSampler& sampler) noexcept {} +void Graphics::None::Destruct(GPUTexture& texture) noexcept {} + +#ifdef _MSC_VER +#pragma warning(default : 4100) // re-enable warning C4100 +#elif defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic warning "-Wunused-parameter" // restore -Wunused-parameter warning +#endif \ No newline at end of file diff --git a/Engine/GraphicsNone.hpp b/Engine/GraphicsNone.hpp new file mode 100644 index 00000000..7b9637ef --- /dev/null +++ b/Engine/GraphicsNone.hpp @@ -0,0 +1 @@ +#pragma once \ No newline at end of file diff --git a/Engine/ImGuiRenderer.cpp b/Engine/ImGuiRenderer.cpp index ed5c233c..421cc18d 100644 --- a/Engine/ImGuiRenderer.cpp +++ b/Engine/ImGuiRenderer.cpp @@ -1,28 +1,45 @@ #include #include - -#include +#include +#include #include #include +#ifdef SDL_SUPPORT +#include +#endif +#if defined(OPENGL_4_6_SUPPORT) || defined(OPENGL_3_3_SUPPORT) #include +#endif +#if defined(VULKAN_SUPPORT) +#include +#endif +#if defined(DIRECT3D_12_SUPPORT) +#include +#endif +#if defined(DIRECT3D_11_SUPPORT) +#include +#endif -#include - -#include +#include +#include +#include #include static std::vector commands; static ImGuiContext* context; +static IWindow* window; //- Data in-use for modifying newly created ImGui window icons -// void (*ImGuiPlatformCreateWindow)(ImGuiViewport* vp); -std::vector ImGuiWindowIcons{}; +WindowIconPack ImGuiWindowIcons{}; -ImGuiContext* ImGuiInit(GLFWwindow* window) { +ImGuiContext* ImGuiInit(IWindow& window) { if (context != nullptr) { return context; } + ::window = &window; + IMGUI_CHECKVERSION(); context = ImGui::CreateContext(); @@ -47,8 +64,72 @@ ImGuiContext* ImGuiInit(GLFWwindow* window) { io.FontDefault = io.Fonts->AddFontFromFileTTF("Fonts/OpenSans-Bold.ttf", 18.0f, nullptr, ICONS_RANGES_TURKISH); io.Fonts->AddFontFromFileTTF("Fonts/FA6/fa-solid-900.ttf", 16.0f, &icons_config, icons_ranges); - ImGui_ImplGlfw_InitForOpenGL(window, true); - ImGui_ImplOpenGL3_Init(GLSL_VERSION); + + switch (window.GetPlatformBackend()) { + using enum WindowBackend; + case GLFW: + if (window.IsSoftwareRendererContextWindow()) { + // TODO implement for software + } +#if defined(OPENGL_4_6_SUPPORT) || defined(OPENGL_3_3_SUPPORT) + else if (window.IsOpenGLContextWindow()) { + ImGui_ImplGlfw_InitForOpenGL(std::any_cast(window.GetPlatformWindowPointer()), true); + } +#endif +#ifdef VULKAN_SUPPORT + else if (window.IsVulkanContextWindow()) { + ImGui_ImplGlfw_InitForVulkan(std::any_cast(window.GetPlatformWindowPointer()), true); + } +#endif +#ifdef DIRECT3D_12_SUPPORT + else if (window.IsDirect3D12ContextWindow()) { + ImGui_ImplGlfw_InitForOther(std::any_cast(window.GetPlatformWindowPointer()), true); + } +#endif +#ifdef DIRECT3D_11_SUPPORT + else if (window.IsDirect3D11ContextWindow()) { + ImGui_ImplGlfw_InitForOther(std::any_cast(window.GetPlatformWindowPointer()), true); + } +#endif + break; +#ifdef SDL_SUPPORT + case SDL: + break; +#endif + default: + std::unreachable(); break; + } + + if (window.IsSoftwareRendererContextWindow()) { + // TODO implement for software + assert(false); + } +#if defined(OPENGL_4_6_SUPPORT) || defined(OPENGL_3_3_SUPPORT) + else if (window.IsOpenGLContextWindow()) { + ImGui_ImplOpenGL3_Init(); + } +#endif +#ifdef VULKAN_SUPPORT + else if (window.IsVulkanContextWindow()) { + // TODO implement for VK - take required fields from Graphics::Vulkan + // ImGui_ImplVulkan_Init(); + assert(false); + } +#endif +#ifdef DIRECT3D_12_SUPPORT + else if (window.IsDirect3D12ContextWindow()) { + // TODO implement for D3D - take required fields from Graphics::Direct3D12 + // ImGui_ImplDX12_Init(); + assert(false); + } +#endif +#ifdef DIRECT3D_11_SUPPORT + else if (window.IsDirect3D11ContextWindow()) { + // TODO implement for D3D - take required fields from Graphics::Direct3D11 + // ImGui_ImplDX11_Init(); + assert(false); + } +#endif //Courtesy of Yan Chernikov, aka The Cherno Project. { // Fancy lookin editor. @@ -94,36 +175,137 @@ ImGuiContext* ImGuiInit(GLFWwindow* window) { } } + ImGui::SetCurrentContext(context); return context; } -void ImGuiSetUpWindowIcons(std::vector&& icons) { +void ImGuiSetUpWindowIcons(WindowIconPack icons) { ImGuiPlatformCreateWindow = ImGui::GetPlatformIO().Platform_CreateWindow; + + // Move (own the copy of) icons ImGuiWindowIcons = std::move(icons); + ImGui::GetPlatformIO().Platform_CreateWindow = [](ImGuiViewport* viewport) { ImGuiPlatformCreateWindow(viewport); - PlatformWindow* platformWindow = reinterpret_cast(viewport->PlatformHandle); - glfwSetWindowIcon(platformWindow, ImGuiWindowIcons.size(), ImGuiWindowIcons.data()); + + switch (window->GetPlatformBackend()) { + using enum WindowBackend; + case GLFW: + WindowGLFW::SetIconOfPlatformWindow({ reinterpret_cast(viewport->PlatformHandle) }, ImGuiWindowIcons); + break; +#ifdef SDL_SUPPORT + case SDL: + WindowSDL::SetIconOfPlatformWindow({ reinterpret_cast(viewport->PlatformHandle) }, ImGuiWindowIcons); + break; +#endif + default: + std::unreachable(); break; + } }; } void ImGuiRender(float delta) { - ImGui_ImplOpenGL3_NewFrame(); - ImGui_ImplGlfw_NewFrame(); - ImGui::NewFrame(); + if (window->IsSoftwareRendererContextWindow()) { + // TODO implement for software + assert(false); + } +#if defined(OPENGL_4_6_SUPPORT) || defined(OPENGL_3_3_SUPPORT) + else if (window->IsOpenGLContextWindow()) { + ImGui_ImplOpenGL3_NewFrame(); + } +#endif +#ifdef VULKAN_SUPPORT + else if (window->IsVulkanContextWindow()) { + ImGui_ImplVulkan_NewFrame(); + } +#endif +#ifdef DIRECT3D_12_SUPPORT + else if (window->IsDirect3D12ContextWindow()) { + ImGui_ImplDX12_NewFrame(); + } +#endif +#ifdef DIRECT3D_11_SUPPORT + else if (window->IsDirect3D11ContextWindow()) { + ImGui_ImplDX11_NewFrame(); + } +#endif - for (const auto& command : commands) { - command(delta); + switch (window->GetPlatformBackend()) { + using enum WindowBackend; + case GLFW: + ImGui_ImplGlfw_NewFrame(); + break; +#ifdef SDL_SUPPORT + case SDL: + ImGui_ImplSDL2_NewFrame(); + break; +#endif + default: + std::unreachable(); break; } - ImGui::Render(); - ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + if (window->IsSoftwareRendererContextWindow() || + window->IsOpenGLContextWindow() || + window->IsVulkanContextWindow() || + window->IsDirect3D12ContextWindow() || + window->IsDirect3D11ContextWindow()) { + ImGui::NewFrame(); + + for (const auto& command : commands) { + command(delta); + } + + ImGui::Render(); + } + if (window->IsSoftwareRendererContextWindow()) { + // TODO implement for software + assert(false); + } +#if defined(OPENGL_4_6_SUPPORT) || defined(OPENGL_3_3_SUPPORT) + else if (window->IsOpenGLContextWindow()) { + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + } +#endif +#ifdef VULKAN_SUPPORT + else if (window->IsVulkanContextWindow()) { + // TODO implement for VK - take required fields from Graphics::Vulkan + // ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData()); + assert(false); + } +#endif +#ifdef DIRECT3D_12_SUPPORT + else if (window->IsDirect3D12ContextWindow()) { + // TODO implement for VK - take required fields from Graphics::Direct3D12 + // ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData()); + assert(false); + } +#endif +#ifdef DIRECT3D_11_SUPPORT + else if (window->IsDirect3D11ContextWindow()) { + ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); + } +#endif const ImGuiIO& io = ImGui::GetIO(); if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { - GLFWwindow* backup_current_context = glfwGetCurrentContext(); - ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindowsDefault(); - glfwMakeContextCurrent(backup_current_context); + std::any backupCurrentContext = window->GetPlatformWindowPointer(); + if (Core::GetCore()->IsAnyContextInitialized()) { + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + } + switch (window->GetPlatformBackend()) { + using enum WindowBackend; + case GLFW: + ImGui_ImplGlfw_NewFrame(); + break; +#ifdef SDL_SUPPORT + case SDL: + ImGui_ImplSDL2_NewFrame(); + break; +#endif + default: + std::unreachable(); break; + } + window->SetPlatformWindowPointerContext(backupCurrentContext); } } @@ -132,10 +314,44 @@ void ImGuiAddRenderCommand(ImGuiFunction function) { } void ImGuiClean() { - ImGui_ImplOpenGL3_Shutdown(); - ImGui_ImplGlfw_Shutdown(); - ImGui::DestroyContext(); - for (auto& icon : ImGuiWindowIcons) { - stbi_image_free(icon.pixels); + if (window->IsSoftwareRendererContextWindow()) { + // TODO implement for software + assert(false); + } +#if defined(OPENGL_4_6_SUPPORT) || defined(OPENGL_3_3_SUPPORT) + else if (window->IsOpenGLContextWindow()) { + ImGui_ImplOpenGL3_Shutdown(); + } +#endif +#ifdef VULKAN_SUPPORT + else if (window->IsVulkanContextWindow()) { + ImGui_ImplVulkan_Shutdown(); } +#endif +#ifdef DIRECT3D_12_SUPPORT + else if (window->IsDirect3D12ContextWindow()) { + ImGui_ImplDX12_Shutdown(); + } +#endif +#ifdef DIRECT3D_11_SUPPORT + else if (window->IsDirect3D11ContextWindow()) { + ImGui_ImplDX11_Shutdown(); + } +#endif + + switch (window->GetPlatformBackend()) { + using enum WindowBackend; + case GLFW: + ImGui_ImplGlfw_Shutdown(); + break; +#ifdef SDL_SUPPORT + case SDL: + ImGui_ImplSDL2_Shutdown(); + break; +#endif + default: + std::unreachable(); break; + } + ImGui::DestroyContext(); + context = nullptr; } diff --git a/Engine/ImGuiRenderer.hpp b/Engine/ImGuiRenderer.hpp index 96e71b10..e66b4c53 100644 --- a/Engine/ImGuiRenderer.hpp +++ b/Engine/ImGuiRenderer.hpp @@ -1,12 +1,15 @@ #pragma once +#include + #include -#include "TypedefsAndConstants.hpp" +#include -struct GLFWwindow; struct ImGuiContext; +using ImGuiFunction = std::function; + inline const ImVec4 WINDOW_BG{ 0.1f, 0.105f, 0.11f, 1.0f }; inline const ImVec4 WINDOW_BG_ALT{ 0.2f, 0.205f, 0.21f, 1.0f }; @@ -40,8 +43,8 @@ inline const ImWchar ICONS_RANGES_TURKISH[] = { 0 }; -ImGuiContext* ImGuiInit(GLFWwindow* window); -void ImGuiSetUpWindowIcons(std::vector&& icons); +ImGuiContext* ImGuiInit(IWindow& window); +void ImGuiSetUpWindowIcons(WindowIconPack icons); void ImGuiRender(float delta); void ImGuiAddRenderCommand(ImGuiFunction function); diff --git a/Engine/Input.cpp b/Engine/Input.cpp index 269e514f..22ac4652 100644 --- a/Engine/Input.cpp +++ b/Engine/Input.cpp @@ -1,18 +1,76 @@ -#include "Input.hpp" +#include -std::unique_ptr CreateInput() { - return std::make_unique(); +#include + +#include + +Input::Input(IWindow& window) noexcept { + window.Events.OnKeyPress += std::bind_front(&Input::HandleKeyPress, this); + window.Events.OnKeyRelease += std::bind_front(&Input::HandleKeyRelease, this); + + window.Events.OnMousePress += std::bind_front(&Input::HandleMousePress, this); + window.Events.OnMouseRelease += std::bind_front(&Input::HandleMouseRelease, this); + + window.Events.OnMouseMove += std::bind_front(&Input::HandleMouseMove, this); + window.Events.OnMouseScroll += std::bind_front(&Input::HandleMouseScroll, this); } -int Input::IsKeyPressed(int key) const noexcept { return keyboard.Keys[key]; } -int Input::IsKeyTyped(int key) const noexcept { return keyboard.Keys[key] == 1; } -int Input::IsKeyReleased(int key) const noexcept { return !keyboard.Keys[key]; } +bool Input::IsKeyPressed(Key key) const noexcept { return StateOf(key).Pressed; } +bool Input::IsKeyDepressed(Key key) const noexcept { return StateOf(key).Depressed; } +bool Input::IsKeyReleased(Key key) const noexcept { return StateOf(key).Released; } -int Input::IsMouseButtonPressed(int button) const noexcept { return mouse.Buttons[button]; } -int Input::IsMouseButtonReleased(int button) const noexcept { return !mouse.Buttons[button]; } +bool Input::IsMouseButtonPressed(MouseButton button) const noexcept { return StateOf(button).Pressed; } +bool Input::IsMouseButtonDepressed(MouseButton button) const noexcept { return StateOf(button).Depressed; } +bool Input::IsMouseButtonReleased(MouseButton button) const noexcept { return StateOf(button).Released; } -double Input::GetMouseX() const noexcept { return mouse.PosX; } -double Input::GetMouseY() const noexcept { return mouse.PosY; } +PointDouble Input::GetMousePos() const noexcept { return mousePosition; } +PointDouble Input::GetMouseScroll() const noexcept { return mouseScroll; } -double Input::GetMouseScrollX() const noexcept { return mouse.ScrollX; } -double Input::GetMouseScrollY() const noexcept { return mouse.ScrollY; } \ No newline at end of file +double Input::GetMousePosX() const noexcept { return GetMousePos().X; } +double Input::GetMousePosY() const noexcept { return GetMousePos().Y; } +double Input::GetMouseScrollX() const noexcept { return GetMouseScroll().X; } +double Input::GetMouseScrollY() const noexcept { return GetMouseScroll().Y; } + +void Input::Step() noexcept { + std::ranges::for_each(keyStates, [](State& state) { + if (state.Released) { state.Depressed = false; } + state.Released = false; + if (state.Pressed) { state.Depressed = true; } + state.Pressed = false; + }); + std::ranges::for_each(mouseButtonStates, [](State& state) { + if (state.Released) { state.Depressed = false; } + state.Released = false; + if (state.Pressed) { state.Depressed = true; } + state.Pressed = false; + }); +} + +Input::State& Input::StateOf(Key key) noexcept { + return keyStates[static_cast(key)]; +} +const Input::State& Input::StateOf(Key key) const noexcept { + return keyStates[static_cast(key)]; +} + +Input::State& Input::StateOf(MouseButton button) noexcept { + return mouseButtonStates[static_cast(button)]; +} +const Input::State& Input::StateOf(MouseButton button) const noexcept { + return mouseButtonStates[static_cast(button)]; +} + +void Input::HandleKeyPress(Key key) noexcept { StateOf(key).Pressed = true; } +void Input::HandleKeyRelease(Key key) noexcept { + State& state = StateOf(key); + state.Depressed = true; + state.Released = true; +} +void Input::HandleMousePress(MouseButton mouseButton) noexcept { StateOf(mouseButton).Pressed = true; } +void Input::HandleMouseRelease(MouseButton mouseButton) noexcept { + State& state = StateOf(mouseButton); + state.Depressed = true; + state.Released = true; +} +void Input::HandleMouseMove(PointDouble position) noexcept { mousePosition = position; } +void Input::HandleMouseScroll(MouseScroll scroll) noexcept { mouseScroll = { scroll.XOffset, scroll.YOffset }; } \ No newline at end of file diff --git a/Engine/Input.hpp b/Engine/Input.hpp index 9647877f..17faed72 100644 --- a/Engine/Input.hpp +++ b/Engine/Input.hpp @@ -3,35 +3,113 @@ #include #include -#include "TypedefsAndConstants.hpp" +#include + +struct IWindow; + +enum class Key { + // Special Keys + Unknown, + Space, + + // Alphabet + A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, + + // Numbers + Num0, Num1, Num2, Num3, Num4, Num5, Num6, Num7, Num8, Num9, + + // Function Keys + F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, F13, F14, F15, F16, F17, F18, F19, F20, F21, F22, F23, F24, F25, + + // Special Characters + Apostrophe, Comma, Minus, Period, Slash, Semicolon, Equal, LeftBracket, Backslash, RightBracket, GraveAccent, + + // Navigation + ArrowUp, ArrowDown, ArrowLeft, ArrowRight, + + // Control Keys + Escape, Enter, Tab, Backspace, Insert, Delete, Home, End, PageUp, PageDown, + + // Modifier Keys + LeftShift, RightShift, LeftControl, RightControl, LeftAlt, RightAlt, LeftSuper, RightSuper, + + // System Keys + CapsLock, ScrollLock, NumLock, PrintScreen, Pause, + + // Keypad + Keypad0, Keypad1, Keypad2, Keypad3, Keypad4, Keypad5, Keypad6, Keypad7, Keypad8, Keypad9, + KeypadDecimal, KeypadDivide, KeypadMultiply, KeypadSubtract, KeypadAdd, KeypadEnter, KeypadEqual, + + // Miscellaneous + Menu, World1, World2, + + Key_Last +}; + +enum class MouseButton { + Left, + Right, + Middle, + Mouse4, + Mouse5, + Mouse6, + Mouse7, + Mouse8, + MouseButton_Last +}; + +struct MouseScroll { + double XOffset; + double YOffset; +}; struct Input { - int IsKeyPressed(int key) const noexcept; - int IsKeyTyped(int key) const noexcept; - int IsKeyReleased(int key) const noexcept; - int IsMouseButtonPressed(int button) const noexcept; - int IsMouseButtonReleased(int button) const noexcept; + explicit Input(IWindow& window) noexcept; + + bool IsKeyPressed(Key key) const noexcept; + bool IsKeyDepressed(Key key) const noexcept; + bool IsKeyReleased(Key key) const noexcept; + + bool IsMouseButtonPressed(MouseButton button) const noexcept; + bool IsMouseButtonDepressed(MouseButton button) const noexcept; + bool IsMouseButtonReleased(MouseButton button) const noexcept; - double GetMouseX() const noexcept; - double GetMouseY() const noexcept; + PointDouble GetMousePos() const noexcept; + PointDouble GetMouseScroll() const noexcept; + + double GetMousePosX() const noexcept; + double GetMousePosY() const noexcept; double GetMouseScrollX() const noexcept; double GetMouseScrollY() const noexcept; private: - struct Mouse { - double PosX, PosY, ScrollX, ScrollY; - std::array Buttons; - }; - struct Keyboard { - std::array Keys; + struct State { + bool Pressed{}; + bool Depressed{}; + bool Released{}; }; - Mouse mouse; - Keyboard keyboard; + void Step() noexcept; - friend struct Window; -}; + State& StateOf(Key key) noexcept; + const State& StateOf(Key key) const noexcept; + + State& StateOf(MouseButton button) noexcept; + const State& StateOf(MouseButton button) const noexcept; + + void HandleKeyPress(Key key) noexcept; + void HandleKeyRelease(Key key) noexcept; + void HandleMousePress(MouseButton mouseButton) noexcept; + void HandleMouseRelease(MouseButton mouseButton) noexcept; + void HandleMouseMove(PointDouble position) noexcept; + void HandleMouseScroll(MouseScroll scroll) noexcept; + + std::array(Key::Key_Last)> keyStates{}; + std::array(MouseButton::MouseButton_Last)> mouseButtonStates{}; + PointDouble mousePosition{}; + PointDouble mouseScroll{}; -std::unique_ptr CreateInput(); + friend struct Core; +}; \ No newline at end of file diff --git a/Engine/Log.hpp b/Engine/Log.hpp index 4062deb8..43cd51b3 100644 --- a/Engine/Log.hpp +++ b/Engine/Log.hpp @@ -20,8 +20,8 @@ enum class LogSource { }; struct LogMessage { - const std::string _message; const LogSeverity _severity; + const std::string _message; LogMessage(LogSeverity severity, const std::string& message) noexcept; LogMessage(LogSeverity severity, std::string&& message) noexcept; diff --git a/Engine/Material.cpp b/Engine/Material.cpp new file mode 100644 index 00000000..34557c0c --- /dev/null +++ b/Engine/Material.cpp @@ -0,0 +1,84 @@ +#include + +#include +#include +#include +#include + +#pragma region Preprocessor Defines +#ifndef __NEODOA_MATERIAL_UNIFORMS_SET_IMPL +#define __NEODOA_MATERIAL_UNIFORMS_SET_IMPL \ + EnsureCapacity(location + 1); \ + this->values[location] = UniformValue{ \ + .Location = location, \ + .Name = std::string(name), \ + .Value = value \ + }; +#endif // __NEODOA_MATERIAL_UNIFORMS_SETV_IMPL +#pragma endregion + +void Material::Uniforms::Clear() noexcept { values.clear(); } + +const UniformValue& Material::Uniforms::Get(unsigned location) const noexcept { + assert(location < values.size()); + return values[location]; +} +const UniformValue* Material::Uniforms::TryGet(unsigned location) const noexcept { + if (location >= values.size()) { return nullptr; } + return &values[location]; +} +const Material::UniformValues& Material::Uniforms::GetAll() const noexcept { return values; } + +void Material::Uniforms::Set(unsigned location, std::string_view name, Uniform1f value) noexcept { __NEODOA_MATERIAL_UNIFORMS_SET_IMPL } +void Material::Uniforms::Set(unsigned location, std::string_view name, Uniform2f value) noexcept { __NEODOA_MATERIAL_UNIFORMS_SET_IMPL } +void Material::Uniforms::Set(unsigned location, std::string_view name, Uniform3f value) noexcept { __NEODOA_MATERIAL_UNIFORMS_SET_IMPL } +void Material::Uniforms::Set(unsigned location, std::string_view name, Uniform4f value) noexcept { __NEODOA_MATERIAL_UNIFORMS_SET_IMPL } + +void Material::Uniforms::Set(unsigned location, std::string_view name, Uniform1i value) noexcept{ __NEODOA_MATERIAL_UNIFORMS_SET_IMPL } +void Material::Uniforms::Set(unsigned location, std::string_view name, Uniform2i value) noexcept{ __NEODOA_MATERIAL_UNIFORMS_SET_IMPL } +void Material::Uniforms::Set(unsigned location, std::string_view name, Uniform3i value) noexcept{ __NEODOA_MATERIAL_UNIFORMS_SET_IMPL } +void Material::Uniforms::Set(unsigned location, std::string_view name, Uniform4i value) noexcept{ __NEODOA_MATERIAL_UNIFORMS_SET_IMPL } + +void Material::Uniforms::Set(unsigned location, std::string_view name, Uniform1ui value) noexcept{ __NEODOA_MATERIAL_UNIFORMS_SET_IMPL } +void Material::Uniforms::Set(unsigned location, std::string_view name, Uniform2ui value) noexcept{ __NEODOA_MATERIAL_UNIFORMS_SET_IMPL } +void Material::Uniforms::Set(unsigned location, std::string_view name, Uniform3ui value) noexcept{ __NEODOA_MATERIAL_UNIFORMS_SET_IMPL } +void Material::Uniforms::Set(unsigned location, std::string_view name, Uniform4ui value) noexcept{ __NEODOA_MATERIAL_UNIFORMS_SET_IMPL } + +void Material::Uniforms::Set(unsigned location, std::string_view name, UniformMatrix2f value, [[maybe_unused]] bool transpose) noexcept { __NEODOA_MATERIAL_UNIFORMS_SET_IMPL } +void Material::Uniforms::Set(unsigned location, std::string_view name, UniformMatrix3f value, [[maybe_unused]] bool transpose) noexcept { __NEODOA_MATERIAL_UNIFORMS_SET_IMPL } +void Material::Uniforms::Set(unsigned location, std::string_view name, UniformMatrix4f value, [[maybe_unused]] bool transpose) noexcept { __NEODOA_MATERIAL_UNIFORMS_SET_IMPL } +void Material::Uniforms::Set(unsigned location, std::string_view name, UniformMatrix2x3f value, [[maybe_unused]] bool transpose) noexcept { __NEODOA_MATERIAL_UNIFORMS_SET_IMPL } +void Material::Uniforms::Set(unsigned location, std::string_view name, UniformMatrix3x2f value, [[maybe_unused]] bool transpose) noexcept { __NEODOA_MATERIAL_UNIFORMS_SET_IMPL } +void Material::Uniforms::Set(unsigned location, std::string_view name, UniformMatrix2x4f value, [[maybe_unused]] bool transpose) noexcept { __NEODOA_MATERIAL_UNIFORMS_SET_IMPL } +void Material::Uniforms::Set(unsigned location, std::string_view name, UniformMatrix4x2f value, [[maybe_unused]] bool transpose) noexcept { __NEODOA_MATERIAL_UNIFORMS_SET_IMPL } +void Material::Uniforms::Set(unsigned location, std::string_view name, UniformMatrix3x4f value, [[maybe_unused]] bool transpose) noexcept { __NEODOA_MATERIAL_UNIFORMS_SET_IMPL } +void Material::Uniforms::Set(unsigned location, std::string_view name, UniformMatrix4x3f value, [[maybe_unused]] bool transpose) noexcept { __NEODOA_MATERIAL_UNIFORMS_SET_IMPL } + +void Material::Uniforms::Set(unsigned location, std::string_view name, UniformSampler2D value) noexcept { __NEODOA_MATERIAL_UNIFORMS_SET_IMPL } + +void Material::Uniforms::EnsureCapacity(const size_t cap) noexcept { + if (values.size() <= cap) { + values.resize(cap); + } +} + +bool Material::HasShaderProgram() const noexcept { return ShaderProgram != UUID::Empty(); } + +void Material::ClearAllUniforms() noexcept { + VertexUniforms.Clear(); + TessellationControlUniforms.Clear(); + TessellationEvaluationUniforms.Clear(); + GeometryUniforms.Clear(); + FragmentUniforms.Clear(); +} + +std::string Material::Serialize() const noexcept { + return SerializeMaterial(*this); +} +Material Material::Deserialize(const std::string_view data) noexcept { + return DeserializeMaterial(data).deserializedMaterial; +} + +Material Material::Copy(const Material& material) noexcept { + return material.Deserialize(material.Serialize()); +} \ No newline at end of file diff --git a/Engine/Material.hpp b/Engine/Material.hpp new file mode 100644 index 00000000..a1b951a3 --- /dev/null +++ b/Engine/Material.hpp @@ -0,0 +1,140 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +struct AssetHandle; + +using Uniform1f = glm::vec1; +using Uniform2f = glm::vec2; +using Uniform3f = glm::vec3; +using Uniform4f = glm::vec4; + +using Uniform1i = glm::ivec1; +using Uniform2i = glm::ivec2; +using Uniform3i = glm::ivec3; +using Uniform4i = glm::ivec4; + +using Uniform1ui = glm::uvec1; +using Uniform2ui = glm::uvec2; +using Uniform3ui = glm::uvec3; +using Uniform4ui = glm::uvec4; + +using UniformMatrix2f = glm::mat2; +using UniformMatrix3f = glm::mat3; +using UniformMatrix4f = glm::mat4; +using UniformMatrix2x3f = glm::mat2x3; +using UniformMatrix3x2f = glm::mat3x2; +using UniformMatrix2x4f = glm::mat2x4; +using UniformMatrix4x2f = glm::mat4x2; +using UniformMatrix3x4f = glm::mat3x4; +using UniformMatrix4x3f = glm::mat4x3; + +//struct UniformSampler1D { +// Texture1D texture; +// Sampler sampler; +// UUID textureUUID{ UUID::Empty() }; +// UUID samplerUUID{ UUID::Empty() }; +//}; +struct UniformSampler2D { + UUID textureUUID{ UUID::Empty() }; + UUID samplerUUID{ UUID::Empty() }; +}; +//struct UniformSampler3D { +// Texture3D texture; +// Sampler sampler; +// UUID textureUUID{ UUID::Empty() }; +// UUID samplerUUID{ UUID::Empty() }; +//}; + +struct UniformValue { + static constexpr unsigned InvalidLocation{ std::numeric_limits::max() }; + + unsigned Location{ InvalidLocation }; + std::string Name; + std::variant< + Uniform1f, Uniform2f, Uniform3f, Uniform4f, + Uniform1i, Uniform2i, Uniform3i, Uniform4i, + Uniform1ui, Uniform2ui, Uniform3ui, Uniform4ui, + UniformMatrix2f, UniformMatrix3f, UniformMatrix4f, + UniformMatrix2x3f, UniformMatrix3x2f, + UniformMatrix2x4f, UniformMatrix4x2f, + UniformMatrix3x4f, UniformMatrix4x3f, + UniformSampler2D + > Value; +}; + +struct Material { + + using UniformValues = std::vector; + + struct Uniforms { + void Clear() noexcept; + + const UniformValue& Get(unsigned location) const noexcept; + const UniformValue* TryGet(unsigned location) const noexcept; + const UniformValues& GetAll() const noexcept; + + void Set(unsigned location, std::string_view name, Uniform1f value) noexcept; + void Set(unsigned location, std::string_view name, Uniform2f value) noexcept; + void Set(unsigned location, std::string_view name, Uniform3f value) noexcept; + void Set(unsigned location, std::string_view name, Uniform4f value) noexcept; + + void Set(unsigned location, std::string_view name, Uniform1i value) noexcept; + void Set(unsigned location, std::string_view name, Uniform2i value) noexcept; + void Set(unsigned location, std::string_view name, Uniform3i value) noexcept; + void Set(unsigned location, std::string_view name, Uniform4i value) noexcept; + + void Set(unsigned location, std::string_view name, Uniform1ui value) noexcept; + void Set(unsigned location, std::string_view name, Uniform2ui value) noexcept; + void Set(unsigned location, std::string_view name, Uniform3ui value) noexcept; + void Set(unsigned location, std::string_view name, Uniform4ui value) noexcept; + + //TODO implement GPU transpose + void Set(unsigned location, std::string_view name, UniformMatrix2f value, bool transpose = false) noexcept; + void Set(unsigned location, std::string_view name, UniformMatrix3f value, bool transpose = false) noexcept; + void Set(unsigned location, std::string_view name, UniformMatrix4f value, bool transpose = false) noexcept; + void Set(unsigned location, std::string_view name, UniformMatrix2x3f value, bool transpose = false) noexcept; + void Set(unsigned location, std::string_view name, UniformMatrix3x2f value, bool transpose = false) noexcept; + void Set(unsigned location, std::string_view name, UniformMatrix2x4f value, bool transpose = false) noexcept; + void Set(unsigned location, std::string_view name, UniformMatrix4x2f value, bool transpose = false) noexcept; + void Set(unsigned location, std::string_view name, UniformMatrix3x4f value, bool transpose = false) noexcept; + void Set(unsigned location, std::string_view name, UniformMatrix4x3f value, bool transpose = false) noexcept; + + void Set(unsigned location, std::string_view name, UniformSampler2D value) noexcept; + + private: + UniformValues values{}; + + void EnsureCapacity(const size_t cap) noexcept; + + friend struct Material; + }; + + std::string Name; + UUID ShaderProgram{ UUID::Empty() }; + Uniforms VertexUniforms{}; + Uniforms TessellationControlUniforms{}; + Uniforms TessellationEvaluationUniforms{}; + Uniforms GeometryUniforms{}; + Uniforms FragmentUniforms{}; + + bool HasShaderProgram() const noexcept; + + void ClearAllUniforms() noexcept; + + std::string Serialize() const noexcept; + static Material Deserialize(const std::string_view data) noexcept; + + static Material Copy(const Material& material) noexcept; +}; \ No newline at end of file diff --git a/Engine/MaterialDeserializer.cpp b/Engine/MaterialDeserializer.cpp new file mode 100644 index 00000000..27a46bb0 --- /dev/null +++ b/Engine/MaterialDeserializer.cpp @@ -0,0 +1,409 @@ +#include + +#include +#include + +#include + +#include + +#include +#include +#include + +MaterialDeserializationResult DeserializeMaterial(const FNode& file) noexcept { + file.ReadContent(); + auto rv = DeserializeMaterial(file.DisposeContent()); + if (!rv.erred) { + rv.deserializedMaterial.Name = file.Name(); + } + return rv; +} + +MaterialDeserializationResult DeserializeMaterial(const std::string_view data) noexcept { + MaterialDeserializationResult rv; + + tinyxml2::XMLDocument doc; + tinyxml2::XMLError err = doc.Parse(data.data()); + if (err != tinyxml2::XML_SUCCESS) { + rv.erred = true; + rv.errors.emplace_back("Couldn't deserialize material!"); + rv.errors.emplace_back("This should normally never happen. If you didn't try to edit the file manually, please submit an issue."); + DOA_LOG_ERROR("Couldn't deserialize material!\n\n%s", data); + } else { + MaterialDeserializer::Deserialize(*doc.RootElement(), rv); + } + + return rv; +} + +void MaterialDeserializer::DefaultDeserialize(tinyxml2::XMLElement& rootElem, MaterialDeserializationResult& mdr) noexcept { + Program::Deserialize(*rootElem.FirstChildElement("program"), mdr); + Uniforms::Deserialize(*rootElem.FirstChildElement("uniforms"), mdr); +} + +void MaterialDeserializer::Program::DefaultDeserialize(tinyxml2::XMLElement& programElem, MaterialDeserializationResult& mdr) noexcept { + DeserializeID(programElem, mdr); +} +void MaterialDeserializer::Program::DefaultDeserializeID(tinyxml2::XMLElement& programElem, MaterialDeserializationResult& mdr) noexcept { + static auto& Core = Core::GetCore(); + + UUID programID = programElem.Unsigned64Attribute("id", UUID::Empty()); + AssetHandle handle = Core->GetAssets()->FindAsset(programID); + if (handle.HasValue()) { + mdr.deserializedMaterial.ShaderProgram = handle->ID(); + } else { + mdr.erred = true; + mdr.errors.emplace_back(std::format("Material references non-existant program (ID:{})", programID.AsString())); + } +} + +void MaterialDeserializer::Uniforms::DefaultDeserialize(tinyxml2::XMLElement& uniformElem, MaterialDeserializationResult& mdr) noexcept { + DeserializeVertexUniforms(*uniformElem.FirstChildElement("vertex-uniforms"), mdr); + DeserializeTessellationControlUniforms(*uniformElem.FirstChildElement("tessellation-control-uniforms"), mdr); + DeserializeTessellationEvaluationUniforms(*uniformElem.FirstChildElement("tessellation-evaluation-uniforms"), mdr); + DeserializeGeometryUniforms(*uniformElem.FirstChildElement("geometry-uniforms"), mdr); + DeserializeFragmentUniforms(*uniformElem.FirstChildElement("fragment-uniforms"), mdr); +} +void MaterialDeserializer::Uniforms::DefaultDeserializeVertexUniforms(tinyxml2::XMLElement& vertexUniformsElem, MaterialDeserializationResult& mdr) noexcept { + Material::Uniforms uniforms; + for (tinyxml2::XMLElement* uniform = vertexUniformsElem.FirstChildElement(); uniform != nullptr; uniform = uniform->NextSiblingElement()) { + std::string name = uniform->Name(); + if (name == "uniform") { + DeserializeUniform(*uniform, mdr, uniforms); + } else { + mdr.erred = true; + mdr.errors.emplace_back(std::format("Error while deserializing material, expected (uniform | uniform-vector) - received {}", name)); + } + } + mdr.deserializedMaterial.VertexUniforms = std::move(uniforms); +} +void MaterialDeserializer::Uniforms::DefaultDeserializeTessellationControlUniforms(tinyxml2::XMLElement& tessellationControlUniformsElem, MaterialDeserializationResult& mdr) noexcept { + Material::Uniforms uniforms; + for (tinyxml2::XMLElement* uniform = tessellationControlUniformsElem.FirstChildElement(); uniform != nullptr; uniform = uniform->NextSiblingElement()) { + std::string name = uniform->Name(); + if (name == "uniform") { + DeserializeUniform(*uniform, mdr, uniforms); + } else { + mdr.erred = true; + mdr.errors.emplace_back(std::format("Error while deserializing material, expected (uniform | uniform-vector) - received {}", name)); + } + } + mdr.deserializedMaterial.TessellationControlUniforms = std::move(uniforms); +} +void MaterialDeserializer::Uniforms::DefaultDeserializeTessellationEvaluationUniforms(tinyxml2::XMLElement& tessellationEvaluationUniformsElem, MaterialDeserializationResult& mdr) noexcept { + Material::Uniforms uniforms; + for (tinyxml2::XMLElement* uniform = tessellationEvaluationUniformsElem.FirstChildElement(); uniform != nullptr; uniform = uniform->NextSiblingElement()) { + std::string name = uniform->Name(); + if (name == "uniform") { + DeserializeUniform(*uniform, mdr, uniforms); + } else { + mdr.erred = true; + mdr.errors.emplace_back(std::format("Error while deserializing material, expected (uniform | uniform-vector) - received {}", name)); + } + } + mdr.deserializedMaterial.TessellationEvaluationUniforms = std::move(uniforms); +} +void MaterialDeserializer::Uniforms::DefaultDeserializeGeometryUniforms(tinyxml2::XMLElement& geometryUniformsElem, MaterialDeserializationResult& mdr) noexcept { + Material::Uniforms uniforms; + for (tinyxml2::XMLElement* uniform = geometryUniformsElem.FirstChildElement(); uniform != nullptr; uniform = uniform->NextSiblingElement()) { + std::string name = uniform->Name(); + if (name == "uniform") { + DeserializeUniform(*uniform, mdr, uniforms); + } else { + mdr.erred = true; + mdr.errors.emplace_back(std::format("Error while deserializing material, expected (uniform | uniform-vector) - received {}", name)); + } + } + mdr.deserializedMaterial.GeometryUniforms = std::move(uniforms); +} +void MaterialDeserializer::Uniforms::DefaultDeserializeFragmentUniforms(tinyxml2::XMLElement& fragmentUniformsElem, MaterialDeserializationResult& mdr) noexcept { + Material::Uniforms uniforms; + for (tinyxml2::XMLElement* uniform = fragmentUniformsElem.FirstChildElement(); uniform != nullptr; uniform = uniform->NextSiblingElement()) { + std::string name = uniform->Name(); + if (name == "uniform") { + DeserializeUniform(*uniform, mdr, uniforms); + } else { + mdr.erred = true; + mdr.errors.emplace_back(std::format("Error while deserializing material, expected (uniform | uniform-vector) - received {}", name)); + } + } + mdr.deserializedMaterial.FragmentUniforms = std::move(uniforms); +} +void MaterialDeserializer::Uniforms::DefaultDeserializeUniform(tinyxml2::XMLElement& uniformElem, MaterialDeserializationResult& mdr, Material::Uniforms& uniformsToFill) noexcept { + int location = uniformElem.IntAttribute("location", UniformValue::InvalidLocation); + std::string_view name = uniformElem.Attribute("name"); + std::string_view typeString = uniformElem.Attribute("type"); + Helpers::UniformType type = Helpers::ExtractUniformType(typeString); + + // then we iterate each "value" node, deserialize and emplace the deserialized content into our vector + const tinyxml2::XMLElement* valueElemPtr = uniformElem.FirstChildElement("value"); + if (!valueElemPtr) { + mdr.erred = true; + mdr.errors.emplace_back(std::format("Error while deserializing material, uniform location {} has no value!", location)); + return; + } + const tinyxml2::XMLElement& valueElem = *valueElemPtr; + + switch (type) { + case MaterialDeserializer::Helpers::UniformType::Uniform1f: + uniformsToFill.Set(location, name, Helpers::DeserializeUniform1f(valueElem)); + break; + case MaterialDeserializer::Helpers::UniformType::Uniform2f: + uniformsToFill.Set(location, name, Helpers::DeserializeUniform2f(valueElem)); + break; + case MaterialDeserializer::Helpers::UniformType::Uniform3f: + uniformsToFill.Set(location, name, Helpers::DeserializeUniform3f(valueElem)); + break; + case MaterialDeserializer::Helpers::UniformType::Uniform4f: + uniformsToFill.Set(location, name, Helpers::DeserializeUniform4f(valueElem)); + break; + case MaterialDeserializer::Helpers::UniformType::Uniform1i: + uniformsToFill.Set(location, name, Helpers::DeserializeUniform1i(valueElem)); + break; + case MaterialDeserializer::Helpers::UniformType::Uniform2i: + uniformsToFill.Set(location, name, Helpers::DeserializeUniform2i(valueElem)); + break; + case MaterialDeserializer::Helpers::UniformType::Uniform3i: + uniformsToFill.Set(location, name, Helpers::DeserializeUniform3i(valueElem)); + break; + case MaterialDeserializer::Helpers::UniformType::Uniform4i: + uniformsToFill.Set(location, name, Helpers::DeserializeUniform4i(valueElem)); + break; + case MaterialDeserializer::Helpers::UniformType::Uniform1ui: + uniformsToFill.Set(location, name, Helpers::DeserializeUniform1ui(valueElem)); + break; + case MaterialDeserializer::Helpers::UniformType::Uniform2ui: + uniformsToFill.Set(location, name, Helpers::DeserializeUniform2ui(valueElem)); + break; + case MaterialDeserializer::Helpers::UniformType::Uniform3ui: + uniformsToFill.Set(location, name, Helpers::DeserializeUniform3ui(valueElem)); + break; + case MaterialDeserializer::Helpers::UniformType::Uniform4ui: + uniformsToFill.Set(location, name, Helpers::DeserializeUniform4ui(valueElem)); + break; + + case MaterialDeserializer::Helpers::UniformType::UniformMatrix2f: + uniformsToFill.Set(location, name, Helpers::DeserializeUniformMatrix2f(valueElem)); + break; + case MaterialDeserializer::Helpers::UniformType::UniformMatrix3f: + uniformsToFill.Set(location, name, Helpers::DeserializeUniformMatrix3f(valueElem)); + break; + case MaterialDeserializer::Helpers::UniformType::UniformMatrix4f: + uniformsToFill.Set(location, name, Helpers::DeserializeUniformMatrix4f(valueElem)); + break; + case MaterialDeserializer::Helpers::UniformType::UniformMatrix2x3f: + uniformsToFill.Set(location, name, Helpers::DeserializeUniformMatrix2x3f(valueElem)); + break; + case MaterialDeserializer::Helpers::UniformType::UniformMatrix3x2f: + uniformsToFill.Set(location, name, Helpers::DeserializeUniformMatrix3x2f(valueElem)); + break; + case MaterialDeserializer::Helpers::UniformType::UniformMatrix2x4f: + uniformsToFill.Set(location, name, Helpers::DeserializeUniformMatrix2x4f(valueElem)); + break; + case MaterialDeserializer::Helpers::UniformType::UniformMatrix4x2f: + uniformsToFill.Set(location, name, Helpers::DeserializeUniformMatrix4x2f(valueElem)); + break; + case MaterialDeserializer::Helpers::UniformType::UniformMatrix3x4f: + uniformsToFill.Set(location, name, Helpers::DeserializeUniformMatrix3x4f(valueElem)); + break; + case MaterialDeserializer::Helpers::UniformType::UniformMatrix4x3f: + uniformsToFill.Set(location, name, Helpers::DeserializeUniformMatrix4x3f(valueElem)); + break; + + case MaterialDeserializer::Helpers::UniformType::UniformSampler2D: + uniformsToFill.Set(location, name, Helpers::DeserializeUniformSampler2D(valueElem)); + break; + default: + mdr.erred = true; + mdr.errors.emplace_back(std::format("Error while deserializing material, uniform location {} has invalid enum!", location)); + break; + } +} + +MaterialDeserializer::Helpers::UniformType MaterialDeserializer::Helpers::ExtractUniformType(const std::string_view typeString) noexcept { + if (typeString == nameof(Uniform1f)) { return UniformType::Uniform1f; } + else if (typeString == nameof(Uniform2f)) { return UniformType::Uniform2f; } + else if (typeString == nameof(Uniform3f)) { return UniformType::Uniform3f; } + else if (typeString == nameof(Uniform4f)) { return UniformType::Uniform4f; } + + else if (typeString == nameof(Uniform1i)) { return UniformType::Uniform1i; } + else if (typeString == nameof(Uniform2i)) { return UniformType::Uniform2i; } + else if (typeString == nameof(Uniform3i)) { return UniformType::Uniform3i; } + else if (typeString == nameof(Uniform4i)) { return UniformType::Uniform4i; } + + else if (typeString == nameof(Uniform1ui)) { return UniformType::Uniform1ui; } + else if (typeString == nameof(Uniform2ui)) { return UniformType::Uniform2ui; } + else if (typeString == nameof(Uniform3ui)) { return UniformType::Uniform3ui; } + else if (typeString == nameof(Uniform4ui)) { return UniformType::Uniform4ui; } + + else if (typeString == nameof(UniformMatrix2f)) { return UniformType::UniformMatrix2f; } + else if (typeString == nameof(UniformMatrix3f)) { return UniformType::UniformMatrix3f; } + else if (typeString == nameof(UniformMatrix4f)) { return UniformType::UniformMatrix4f; } + else if (typeString == nameof(UniformMatrix2x3f)) { return UniformType::UniformMatrix2x3f; } + else if (typeString == nameof(UniformMatrix3x2f)) { return UniformType::UniformMatrix3x2f; } + else if (typeString == nameof(UniformMatrix2x4f)) { return UniformType::UniformMatrix2x4f; } + else if (typeString == nameof(UniformMatrix4x2f)) { return UniformType::UniformMatrix4x2f; } + else if (typeString == nameof(UniformMatrix3x4f)) { return UniformType::UniformMatrix3x4f; } + else if (typeString == nameof(UniformMatrix4x3f)) { return UniformType::UniformMatrix4x3f; } + + else if (typeString == nameof(UniformSampler2D)) { return UniformType::UniformSampler2D; } + else { + // invalid enum + DOA_LOG_ERROR("Couldn't extract uniform type. Data is corrupted."); + assert(false); + return UniformType::InvalidEnum; + } +} + +Uniform1f MaterialDeserializer::Helpers::DeserializeUniform1f(const tinyxml2::XMLElement& elem) noexcept { + return Uniform1f { + elem.FloatAttribute("index0", 0) + }; +} +Uniform2f MaterialDeserializer::Helpers::DeserializeUniform2f(const tinyxml2::XMLElement& elem) noexcept { + return { + elem.FloatAttribute("index0", 0), + elem.FloatAttribute("index1", 0), + }; +} +Uniform3f MaterialDeserializer::Helpers::DeserializeUniform3f(const tinyxml2::XMLElement& elem) noexcept { + return { + elem.FloatAttribute("index0", 0), + elem.FloatAttribute("index1", 0), + elem.FloatAttribute("index2", 0), + }; +} +Uniform4f MaterialDeserializer::Helpers::DeserializeUniform4f(const tinyxml2::XMLElement& elem) noexcept { + return { + elem.FloatAttribute("index0", 0), + elem.FloatAttribute("index1", 0), + elem.FloatAttribute("index2", 0), + elem.FloatAttribute("index3", 0), + }; +} + +Uniform1i MaterialDeserializer::Helpers::DeserializeUniform1i(const tinyxml2::XMLElement& elem) noexcept { + return Uniform1i { + elem.IntAttribute("index0", 0) + }; +} +Uniform2i MaterialDeserializer::Helpers::DeserializeUniform2i(const tinyxml2::XMLElement& elem) noexcept { + return { + elem.IntAttribute("index0", 0), + elem.IntAttribute("index1", 0), + }; +} +Uniform3i MaterialDeserializer::Helpers::DeserializeUniform3i(const tinyxml2::XMLElement& elem) noexcept { + return { + elem.IntAttribute("index0", 0), + elem.IntAttribute("index1", 0), + elem.IntAttribute("index2", 0), + }; +} +Uniform4i MaterialDeserializer::Helpers::DeserializeUniform4i(const tinyxml2::XMLElement& elem) noexcept { + return { + elem.IntAttribute("index0", 0), + elem.IntAttribute("index1", 0), + elem.IntAttribute("index2", 0), + elem.IntAttribute("index3", 0), + }; +} + +Uniform1ui MaterialDeserializer::Helpers::DeserializeUniform1ui(const tinyxml2::XMLElement& elem) noexcept { + return Uniform1ui { + elem.UnsignedAttribute("index0", 0) + }; +} +Uniform2ui MaterialDeserializer::Helpers::DeserializeUniform2ui(const tinyxml2::XMLElement& elem) noexcept { + return { + elem.UnsignedAttribute("index0", 0), + elem.UnsignedAttribute("index1", 0), + }; +} +Uniform3ui MaterialDeserializer::Helpers::DeserializeUniform3ui(const tinyxml2::XMLElement& elem) noexcept { + return { + elem.UnsignedAttribute("index0", 0), + elem.UnsignedAttribute("index1", 0), + elem.UnsignedAttribute("index2", 0), + }; +} +Uniform4ui MaterialDeserializer::Helpers::DeserializeUniform4ui(const tinyxml2::XMLElement& elem) noexcept { + return { + elem.UnsignedAttribute("index0", 0), + elem.UnsignedAttribute("index1", 0), + elem.UnsignedAttribute("index2", 0), + elem.UnsignedAttribute("index3", 0), + }; +} + +UniformMatrix2f MaterialDeserializer::Helpers::DeserializeUniformMatrix2f(const tinyxml2::XMLElement& elem) noexcept { + return { + elem.FloatAttribute("index00", 0), elem.FloatAttribute("index01", 0), + elem.FloatAttribute("index10", 0), elem.FloatAttribute("index11", 0), + }; +} +UniformMatrix3f MaterialDeserializer::Helpers::DeserializeUniformMatrix3f(const tinyxml2::XMLElement& elem) noexcept { + return { + elem.FloatAttribute("index00", 0), elem.FloatAttribute("index01", 0), elem.FloatAttribute("index02", 0), + elem.FloatAttribute("index10", 0), elem.FloatAttribute("index11", 0), elem.FloatAttribute("index12", 0), + elem.FloatAttribute("index20", 0), elem.FloatAttribute("index21", 0), elem.FloatAttribute("index22", 0), + }; +} +UniformMatrix4f MaterialDeserializer::Helpers::DeserializeUniformMatrix4f(const tinyxml2::XMLElement& elem) noexcept { + return { + elem.FloatAttribute("index00", 0), elem.FloatAttribute("index01", 0), elem.FloatAttribute("index02", 0), elem.FloatAttribute("index03", 0), + elem.FloatAttribute("index10", 0), elem.FloatAttribute("index11", 0), elem.FloatAttribute("index12", 0), elem.FloatAttribute("index13", 0), + elem.FloatAttribute("index20", 0), elem.FloatAttribute("index21", 0), elem.FloatAttribute("index22", 0), elem.FloatAttribute("index23", 0), + elem.FloatAttribute("index30", 0), elem.FloatAttribute("index31", 0), elem.FloatAttribute("index32", 0), elem.FloatAttribute("index33", 0) + }; +} +UniformMatrix2x3f MaterialDeserializer::Helpers::DeserializeUniformMatrix2x3f(const tinyxml2::XMLElement& elem) noexcept { + return { + elem.FloatAttribute("index00", 0), elem.FloatAttribute("index01", 0), + elem.FloatAttribute("index10", 0), elem.FloatAttribute("index11", 0), + elem.FloatAttribute("index20", 0), elem.FloatAttribute("index21", 0), + }; +} +UniformMatrix3x2f MaterialDeserializer::Helpers::DeserializeUniformMatrix3x2f(const tinyxml2::XMLElement& elem) noexcept { + return { + elem.FloatAttribute("index00", 0), elem.FloatAttribute("index01", 0), elem.FloatAttribute("index02", 0), + elem.FloatAttribute("index10", 0), elem.FloatAttribute("index11", 0), elem.FloatAttribute("index12", 0), + }; +} +UniformMatrix2x4f MaterialDeserializer::Helpers::DeserializeUniformMatrix2x4f(const tinyxml2::XMLElement& elem) noexcept { + return { + elem.FloatAttribute("index00", 0), elem.FloatAttribute("index01", 0), + elem.FloatAttribute("index10", 0), elem.FloatAttribute("index11", 0), + elem.FloatAttribute("index20", 0), elem.FloatAttribute("index21", 0), + elem.FloatAttribute("index30", 0), elem.FloatAttribute("index31", 0), + }; +} +UniformMatrix4x2f MaterialDeserializer::Helpers::DeserializeUniformMatrix4x2f(const tinyxml2::XMLElement& elem) noexcept { + return { + elem.FloatAttribute("index00", 0), elem.FloatAttribute("index01", 0), elem.FloatAttribute("index02", 0), elem.FloatAttribute("index03", 0), + elem.FloatAttribute("index10", 0), elem.FloatAttribute("index11", 0), elem.FloatAttribute("index12", 0), elem.FloatAttribute("index13", 0), + }; +} +UniformMatrix3x4f MaterialDeserializer::Helpers::DeserializeUniformMatrix3x4f(const tinyxml2::XMLElement& elem) noexcept { + return { + elem.FloatAttribute("index00", 0), elem.FloatAttribute("index01", 0), elem.FloatAttribute("index02", 0), + elem.FloatAttribute("index10", 0), elem.FloatAttribute("index11", 0), elem.FloatAttribute("index12", 0), + elem.FloatAttribute("index20", 0), elem.FloatAttribute("index21", 0), elem.FloatAttribute("index22", 0), + elem.FloatAttribute("index30", 0), elem.FloatAttribute("index31", 0), elem.FloatAttribute("index32", 0), + }; +} +UniformMatrix4x3f MaterialDeserializer::Helpers::DeserializeUniformMatrix4x3f(const tinyxml2::XMLElement& elem) noexcept { + return { + elem.FloatAttribute("index00", 0), elem.FloatAttribute("index01", 0), elem.FloatAttribute("index02", 0), elem.FloatAttribute("index03", 0), + elem.FloatAttribute("index10", 0), elem.FloatAttribute("index11", 0), elem.FloatAttribute("index12", 0), elem.FloatAttribute("index13", 0), + elem.FloatAttribute("index20", 0), elem.FloatAttribute("index21", 0), elem.FloatAttribute("index22", 0), elem.FloatAttribute("index23", 0), + }; +} + +UniformSampler2D MaterialDeserializer::Helpers::DeserializeUniformSampler2D(const tinyxml2::XMLElement& elem) noexcept { + return { + elem.Unsigned64Attribute("texture"), + elem.Unsigned64Attribute("sampler") + }; +} diff --git a/Engine/MaterialDeserializer.hpp b/Engine/MaterialDeserializer.hpp new file mode 100644 index 00000000..829c34a3 --- /dev/null +++ b/Engine/MaterialDeserializer.hpp @@ -0,0 +1,136 @@ +#pragma once + +#include +#include +#include + +#include + +#include + +struct FNode; + +struct MaterialDeserializationResult { + bool erred{ false }; + std::vector errors{}; + Material deserializedMaterial{ "" }; +}; + +/* This is the big bad bada-boom of deserializers, this function will call appropriate deserializers. See below for an overview! */ +MaterialDeserializationResult DeserializeMaterial(const FNode& file) noexcept; +MaterialDeserializationResult DeserializeMaterial(const std::string_view data) noexcept; + +/* Here is the general overview */ +namespace MaterialDeserializer { + + /* ------- Type Definitons ------- */ + using DeserializeFunction = std::function; + namespace Program { + using DeserializeFunction = std::function; + using DeserializeIDFunction = std::function; + } + namespace Uniforms { + using DeserializeFunction = std::function; + using DeserializeVertexUniformsFunction = std::function; + using DeserializeTessellationControlUniformsFunction = std::function; + using DeserializeTessellationEvaluationUniformsFunction = std::function; + using DeserializeGeometryUniformsFunction = std::function; + using DeserializeFragmentUniformsFunction = std::function; + using DeserializeUniformFunction = std::function; + } + + /* --- Default Implementations --- */ + /* These are how NeoDoa will desrialize by default. */ + void DefaultDeserialize(tinyxml2::XMLElement& rootElem, MaterialDeserializationResult& mdr) noexcept; + namespace Program { + void DefaultDeserialize(tinyxml2::XMLElement& programElem, MaterialDeserializationResult& mdr) noexcept; + void DefaultDeserializeID(tinyxml2::XMLElement& programElem, MaterialDeserializationResult& mdr) noexcept; + } + namespace Uniforms { + void DefaultDeserialize(tinyxml2::XMLElement& uniformElem, MaterialDeserializationResult& mdr) noexcept; + void DefaultDeserializeVertexUniforms(tinyxml2::XMLElement& vertexUniformsElem, MaterialDeserializationResult& mdr) noexcept; + void DefaultDeserializeTessellationControlUniforms(tinyxml2::XMLElement& tessellationControlUniformsElem, MaterialDeserializationResult& mdr) noexcept; + void DefaultDeserializeTessellationEvaluationUniforms(tinyxml2::XMLElement& tessellationEvaluationUniformsElem, MaterialDeserializationResult& mdr) noexcept; + void DefaultDeserializeGeometryUniforms(tinyxml2::XMLElement& geometryUniformsElem, MaterialDeserializationResult& mdr) noexcept; + void DefaultDeserializeFragmentUniforms(tinyxml2::XMLElement& fragmentUniformsElem, MaterialDeserializationResult& mdr) noexcept; + void DefaultDeserializeUniform(tinyxml2::XMLElement& uniformElem, MaterialDeserializationResult& mdr, Material::Uniforms& uniformsToFill) noexcept; + } + + /* ----- Deserializer Functions ----- */ + inline DeserializeFunction Deserialize{ DefaultDeserialize }; /* Feel free to assign this your own function, if you need custom serialization */ + namespace Program { + inline DeserializeFunction Deserialize{ DefaultDeserialize }; /* Feel free to assign this your own function, if you need custom serialization */ + inline DeserializeIDFunction DeserializeID{ DefaultDeserializeID }; /* Feel free to assign this your own function, if you need custom serialization */ + } + namespace Uniforms { + inline DeserializeFunction Deserialize{ DefaultDeserialize }; /* Feel free to assign this your own function, if you need custom serialization */ + inline DeserializeVertexUniformsFunction DeserializeVertexUniforms{ DefaultDeserializeVertexUniforms }; /* Feel free to assign this your own function, if you need custom serialization */ + inline DeserializeTessellationControlUniformsFunction DeserializeTessellationControlUniforms{ DefaultDeserializeTessellationControlUniforms }; /* Feel free to assign this your own function, if you need custom serialization */ + inline DeserializeTessellationEvaluationUniformsFunction DeserializeTessellationEvaluationUniforms{ DefaultDeserializeTessellationEvaluationUniforms }; /* Feel free to assign this your own function, if you need custom serialization */ + inline DeserializeGeometryUniformsFunction DeserializeGeometryUniforms{ DefaultDeserializeGeometryUniforms }; /* Feel free to assign this your own function, if you need custom serialization */ + inline DeserializeFragmentUniformsFunction DeserializeFragmentUniforms{ DefaultDeserializeFragmentUniforms }; /* Feel free to assign this your own function, if you need custom serialization */ + inline DeserializeUniformFunction DeserializeUniform{ DefaultDeserializeUniform }; /* Feel free to assign this your own function, if you need custom serialization */ + } +} + +/* Some Helper functions to trivially deserialize (not-so) trivial data types */ +namespace MaterialDeserializer::Helpers { + enum class UniformType { + Uniform1f, + Uniform2f, + Uniform3f, + Uniform4f, + + Uniform1i, + Uniform2i, + Uniform3i, + Uniform4i, + + Uniform1ui, + Uniform2ui, + Uniform3ui, + Uniform4ui, + + UniformMatrix2f, + UniformMatrix3f, + UniformMatrix4f, + UniformMatrix2x3f, + UniformMatrix3x2f, + UniformMatrix2x4f, + UniformMatrix4x2f, + UniformMatrix3x4f, + UniformMatrix4x3f, + + UniformSampler2D, + + InvalidEnum + }; + UniformType ExtractUniformType(const std::string_view typeString) noexcept; + + Uniform1f DeserializeUniform1f(const tinyxml2::XMLElement& elem) noexcept; + Uniform2f DeserializeUniform2f(const tinyxml2::XMLElement& elem) noexcept; + Uniform3f DeserializeUniform3f(const tinyxml2::XMLElement& elem) noexcept; + Uniform4f DeserializeUniform4f(const tinyxml2::XMLElement& elem) noexcept; + + Uniform1i DeserializeUniform1i(const tinyxml2::XMLElement& elem) noexcept; + Uniform2i DeserializeUniform2i(const tinyxml2::XMLElement& elem) noexcept; + Uniform3i DeserializeUniform3i(const tinyxml2::XMLElement& elem) noexcept; + Uniform4i DeserializeUniform4i(const tinyxml2::XMLElement& elem) noexcept; + + Uniform1ui DeserializeUniform1ui(const tinyxml2::XMLElement& elem) noexcept; + Uniform2ui DeserializeUniform2ui(const tinyxml2::XMLElement& elem) noexcept; + Uniform3ui DeserializeUniform3ui(const tinyxml2::XMLElement& elem) noexcept; + Uniform4ui DeserializeUniform4ui(const tinyxml2::XMLElement& elem) noexcept; + + UniformMatrix2f DeserializeUniformMatrix2f(const tinyxml2::XMLElement& elem) noexcept; + UniformMatrix3f DeserializeUniformMatrix3f(const tinyxml2::XMLElement& elem) noexcept; + UniformMatrix4f DeserializeUniformMatrix4f(const tinyxml2::XMLElement& elem) noexcept; + UniformMatrix2x3f DeserializeUniformMatrix2x3f(const tinyxml2::XMLElement& elem) noexcept; + UniformMatrix3x2f DeserializeUniformMatrix3x2f(const tinyxml2::XMLElement& elem) noexcept; + UniformMatrix2x4f DeserializeUniformMatrix2x4f(const tinyxml2::XMLElement& elem) noexcept; + UniformMatrix4x2f DeserializeUniformMatrix4x2f(const tinyxml2::XMLElement& elem) noexcept; + UniformMatrix3x4f DeserializeUniformMatrix3x4f(const tinyxml2::XMLElement& elem) noexcept; + UniformMatrix4x3f DeserializeUniformMatrix4x3f(const tinyxml2::XMLElement& elem) noexcept; + + UniformSampler2D DeserializeUniformSampler2D(const tinyxml2::XMLElement& elem) noexcept; +} \ No newline at end of file diff --git a/Engine/MaterialSerializer.cpp b/Engine/MaterialSerializer.cpp new file mode 100644 index 00000000..f46f1d7f --- /dev/null +++ b/Engine/MaterialSerializer.cpp @@ -0,0 +1,484 @@ +#include + +#include + +#include + +#include +#include + +#include +#include + +std::string SerializeMaterial(const Material& material) noexcept { + tinyxml2::XMLPrinter printer; + MaterialSerializer::HeaderComment(printer, material); + MaterialSerializer::Serialize(printer, material); + + return printer.CStr(); +} + +void MaterialSerializer::DefaultHeaderComment(tinyxml2::XMLPrinter& printer, [[maybe_unused]] const Material& material) noexcept { + printer.PushComment("WARNING!! This file is not for editing! Don't!"); +} +void MaterialSerializer::DefaultSerialize(tinyxml2::XMLPrinter& printer, const Material& material) noexcept { + printer.OpenElement("material"); + { + Program::Serialize(printer, material); + Uniforms::Serialize(printer, material); + } + printer.CloseElement(); +} + +void MaterialSerializer::Program::DefaultSerialize(tinyxml2::XMLPrinter& printer, const Material& material) noexcept { + printer.OpenElement("program"); + { + SerializeID(printer, material.ShaderProgram); + } + printer.CloseElement(); +} +void MaterialSerializer::Program::DefaultSerializeID(tinyxml2::XMLPrinter& printer, const UUID id) noexcept { + printer.PushAttribute("id", id); +} + +void MaterialSerializer::Uniforms::DefaultSerialize(tinyxml2::XMLPrinter& printer, const Material& material) noexcept { + printer.OpenElement("uniforms"); + { + SerializeVertexUniforms(printer, material.VertexUniforms); + SerializeTessellationControlUniforms(printer, material.TessellationControlUniforms); + SerializeTessellationEvaluationUniforms(printer, material.TessellationEvaluationUniforms); + SerializeGeometryUniforms(printer, material.GeometryUniforms); + SerializeFragmentUniforms(printer, material.FragmentUniforms); + } + printer.CloseElement(); +} +void MaterialSerializer::Uniforms::DefaultSerializeVertexUniforms(tinyxml2::XMLPrinter& printer, const Material::Uniforms& uniforms) noexcept { + printer.OpenElement("vertex-uniforms"); + { + for (const auto& uniform : uniforms.GetAll()) { + SerializeUniform(printer, uniform); + } + } + printer.CloseElement(); +} +void MaterialSerializer::Uniforms::DefaultSerializeTessellationControlUniforms(tinyxml2::XMLPrinter& printer, const Material::Uniforms& uniforms) noexcept { + printer.OpenElement("tessellation-control-uniforms"); + { + for (const auto& uniform : uniforms.GetAll()) { + SerializeUniform(printer, uniform); + } + } + printer.CloseElement(); +} +void MaterialSerializer::Uniforms::DefaultSerializeTessellationEvaluationUniforms(tinyxml2::XMLPrinter& printer, const Material::Uniforms& uniforms) noexcept { + printer.OpenElement("tessellation-evaluation-uniforms"); + { + for (const auto& uniform : uniforms.GetAll()) { + SerializeUniform(printer, uniform); + } + } + printer.CloseElement(); +} +void MaterialSerializer::Uniforms::DefaultSerializeGeometryUniforms(tinyxml2::XMLPrinter& printer, const Material::Uniforms& uniforms) noexcept { + printer.OpenElement("geometry-uniforms"); + { + for (const auto& uniform : uniforms.GetAll()) { + SerializeUniform(printer, uniform); + } + } + printer.CloseElement(); +} +void MaterialSerializer::Uniforms::DefaultSerializeFragmentUniforms(tinyxml2::XMLPrinter& printer, const Material::Uniforms& uniforms) noexcept { + printer.OpenElement("fragment-uniforms"); + { + for (const auto& uniform : uniforms.GetAll()) { + SerializeUniform(printer, uniform); + } + } + printer.CloseElement(); +} +void MaterialSerializer::Uniforms::DefaultSerializeUniform(tinyxml2::XMLPrinter& printer, const UniformValue& uniform) noexcept { + printer.OpenElement("uniform"); + { + printer.PushAttribute("location", uniform.Location); + printer.PushAttribute("name", uniform.Name.c_str()); + printer.PushAttribute("type", Helpers::ExtractUniformTypeString(uniform).data()); + + std::visit(overloaded::lambda{ + [&printer](const Uniform1f& u) { + Helpers::SerializeUniform1f(printer, u); + }, + [&printer](const Uniform2f& u) { + Helpers::SerializeUniform2f(printer, u); + }, + [&printer](const Uniform3f& u) { + Helpers::SerializeUniform3f(printer, u); + }, + [&printer](const Uniform4f& u) { + Helpers::SerializeUniform4f(printer, u); + }, + + [&printer](const Uniform1i& u) { + Helpers::SerializeUniform1i(printer, u); + }, + [&printer](const Uniform2i& u) { + Helpers::SerializeUniform2i(printer, u); + }, + [&printer](const Uniform3i& u) { + Helpers::SerializeUniform3i(printer, u); + }, + [&printer](const Uniform4i& u) { + Helpers::SerializeUniform4i(printer, u); + }, + + [&printer](const Uniform1ui& u) { + Helpers::SerializeUniform1ui(printer, u); + }, + [&printer](const Uniform2ui& u) { + Helpers::SerializeUniform2ui(printer, u); + }, + [&printer](const Uniform3ui& u) { + Helpers::SerializeUniform3ui(printer, u); + }, + [&printer](const Uniform4ui& u) { + Helpers::SerializeUniform4ui(printer, u); + }, + + [&printer](const UniformMatrix2f& u) { + Helpers::SerializeUniformMatrix2f(printer, u); + }, + [&printer](const UniformMatrix3f& u) { + Helpers::SerializeUniformMatrix3f(printer, u); + }, + [&printer](const UniformMatrix4f& u) { + Helpers::SerializeUniformMatrix4f(printer, u); + }, + [&printer](const UniformMatrix2x3f& u) { + Helpers::SerializeUniformMatrix2x3f(printer, u); + }, + [&printer](const UniformMatrix3x2f& u) { + Helpers::SerializeUniformMatrix3x2f(printer, u); + }, + [&printer](const UniformMatrix2x4f& u) { + Helpers::SerializeUniformMatrix2x4f(printer, u); + }, + [&printer](const UniformMatrix4x2f& u) { + Helpers::SerializeUniformMatrix4x2f(printer, u); + }, + [&printer](const UniformMatrix3x4f& u) { + Helpers::SerializeUniformMatrix3x4f(printer, u); + }, + [&printer](const UniformMatrix4x3f& u) { + Helpers::SerializeUniformMatrix4x3f(printer, u); + }, + + [&printer](const UniformSampler2D& u) { + Helpers::SerializeUniformSampler2D(printer, u); + } + }, uniform.Value); + } + printer.CloseElement(); +} + +std::string MaterialSerializer::Helpers::ExtractUniformTypeString(const UniformValue& uniform) noexcept { + std::string rv; + + std::visit(overloaded::lambda{ + [&rv]([[maybe_unused]] const Uniform1f& _) { rv = nameof(Uniform1f); }, + [&rv]([[maybe_unused]] const Uniform2f& _) { rv = nameof(Uniform2f); }, + [&rv]([[maybe_unused]] const Uniform3f& _) { rv = nameof(Uniform3f); }, + [&rv]([[maybe_unused]] const Uniform4f& _) { rv = nameof(Uniform4f); }, + + [&rv]([[maybe_unused]] const Uniform1i& _) { rv = nameof(Uniform1i); }, + [&rv]([[maybe_unused]] const Uniform2i& _) { rv = nameof(Uniform2i); }, + [&rv]([[maybe_unused]] const Uniform3i& _) { rv = nameof(Uniform3i); }, + [&rv]([[maybe_unused]] const Uniform4i& _) { rv = nameof(Uniform4i); }, + + [&rv]([[maybe_unused]] const Uniform1ui& _) { rv = nameof(Uniform1ui); }, + [&rv]([[maybe_unused]] const Uniform2ui& _) { rv = nameof(Uniform2ui); }, + [&rv]([[maybe_unused]] const Uniform3ui& _) { rv = nameof(Uniform3ui); }, + [&rv]([[maybe_unused]] const Uniform4ui& _) { rv = nameof(Uniform4ui); }, + + [&rv]([[maybe_unused]] const UniformMatrix2f& _) { rv = nameof(UniformMatrix2f); }, + [&rv]([[maybe_unused]] const UniformMatrix3f& _) { rv = nameof(UniformMatrix3f); }, + [&rv]([[maybe_unused]] const UniformMatrix4f& _) { rv = nameof(UniformMatrix4f); }, + [&rv]([[maybe_unused]] const UniformMatrix2x3f& _) { rv = nameof(UniformMatrix2x3f); }, + [&rv]([[maybe_unused]] const UniformMatrix3x2f& _) { rv = nameof(UniformMatrix3x2f); }, + [&rv]([[maybe_unused]] const UniformMatrix2x4f& _) { rv = nameof(UniformMatrix2x4f); }, + [&rv]([[maybe_unused]] const UniformMatrix4x2f& _) { rv = nameof(UniformMatrix4x2f); }, + [&rv]([[maybe_unused]] const UniformMatrix3x4f& _) { rv = nameof(UniformMatrix3x4f); }, + [&rv]([[maybe_unused]] const UniformMatrix4x3f& _) { rv = nameof(UniformMatrix4x3f); }, + + [&rv]([[maybe_unused]] const UniformSampler2D& _) { rv = nameof(UniformSampler2D); } + }, uniform.Value); + + return rv; +} + +void MaterialSerializer::Helpers::SerializeUniform1f(tinyxml2::XMLPrinter& printer, const Uniform1f& uniformValue) noexcept { + printer.OpenElement("value"); + { + printer.PushAttribute("index0", uniformValue[0]); + } + printer.CloseElement(); +} +void MaterialSerializer::Helpers::SerializeUniform2f(tinyxml2::XMLPrinter& printer, const Uniform2f& uniformValue) noexcept { + printer.OpenElement("value"); + { + printer.PushAttribute("index0", uniformValue[0]); + printer.PushAttribute("index1", uniformValue[1]); + } + printer.CloseElement(); +} +void MaterialSerializer::Helpers::SerializeUniform3f(tinyxml2::XMLPrinter& printer, const Uniform3f& uniformValue) noexcept { + printer.OpenElement("value"); + { + printer.PushAttribute("index0", uniformValue[0]); + printer.PushAttribute("index1", uniformValue[1]); + printer.PushAttribute("index2", uniformValue[2]); + } + printer.CloseElement(); +} +void MaterialSerializer::Helpers::SerializeUniform4f(tinyxml2::XMLPrinter& printer, const Uniform4f& uniformValue) noexcept { + printer.OpenElement("value"); + { + printer.PushAttribute("index0", uniformValue[0]); + printer.PushAttribute("index1", uniformValue[1]); + printer.PushAttribute("index2", uniformValue[2]); + printer.PushAttribute("index3", uniformValue[3]); + } + printer.CloseElement(); +} + +void MaterialSerializer::Helpers::SerializeUniform1i(tinyxml2::XMLPrinter& printer, const Uniform1i& uniformValue) noexcept { + printer.OpenElement("value"); + { + printer.PushAttribute("index0", uniformValue[0]); + } + printer.CloseElement(); +} +void MaterialSerializer::Helpers::SerializeUniform2i(tinyxml2::XMLPrinter& printer, const Uniform2i& uniformValue) noexcept { + printer.OpenElement("value"); + { + printer.PushAttribute("index0", uniformValue[0]); + printer.PushAttribute("index1", uniformValue[1]); + } + printer.CloseElement(); +} +void MaterialSerializer::Helpers::SerializeUniform3i(tinyxml2::XMLPrinter& printer, const Uniform3i& uniformValue) noexcept { + printer.OpenElement("value"); + { + printer.PushAttribute("index0", uniformValue[0]); + printer.PushAttribute("index1", uniformValue[1]); + printer.PushAttribute("index2", uniformValue[2]); + } + printer.CloseElement(); +} +void MaterialSerializer::Helpers::SerializeUniform4i(tinyxml2::XMLPrinter& printer, const Uniform4i& uniformValue) noexcept { + printer.OpenElement("value"); + { + printer.PushAttribute("index0", uniformValue[0]); + printer.PushAttribute("index1", uniformValue[1]); + printer.PushAttribute("index2", uniformValue[2]); + printer.PushAttribute("index3", uniformValue[3]); + } + printer.CloseElement(); +} + +void MaterialSerializer::Helpers::SerializeUniform1ui(tinyxml2::XMLPrinter& printer, const Uniform1ui& uniformValue) noexcept { + printer.OpenElement("value"); + { + printer.PushAttribute("index0", uniformValue[0]); + } + printer.CloseElement(); +} +void MaterialSerializer::Helpers::SerializeUniform2ui(tinyxml2::XMLPrinter& printer, const Uniform2ui& uniformValue) noexcept { + printer.OpenElement("value"); + { + printer.PushAttribute("index0", uniformValue[0]); + printer.PushAttribute("index1", uniformValue[1]); + } + printer.CloseElement(); +} +void MaterialSerializer::Helpers::SerializeUniform3ui(tinyxml2::XMLPrinter& printer, const Uniform3ui& uniformValue) noexcept { + printer.OpenElement("value"); + { + printer.PushAttribute("index0", uniformValue[0]); + printer.PushAttribute("index1", uniformValue[1]); + printer.PushAttribute("index2", uniformValue[2]); + } + printer.CloseElement(); +} +void MaterialSerializer::Helpers::SerializeUniform4ui(tinyxml2::XMLPrinter& printer, const Uniform4ui& uniformValue) noexcept { + printer.OpenElement("value"); + { + printer.PushAttribute("index0", uniformValue[0]); + printer.PushAttribute("index1", uniformValue[1]); + printer.PushAttribute("index2", uniformValue[2]); + printer.PushAttribute("index3", uniformValue[3]); + } + printer.CloseElement(); +} + +void MaterialSerializer::Helpers::SerializeUniformMatrix2f(tinyxml2::XMLPrinter& printer, const UniformMatrix2f& uniformValue) noexcept { + printer.OpenElement("value"); + { + printer.PushAttribute("index00", uniformValue[0][0]); + printer.PushAttribute("index01", uniformValue[0][1]); + + printer.PushAttribute("index10", uniformValue[1][0]); + printer.PushAttribute("index11", uniformValue[1][1]); + } + printer.CloseElement(); +} +void MaterialSerializer::Helpers::SerializeUniformMatrix3f(tinyxml2::XMLPrinter& printer, const UniformMatrix3f& uniformValue) noexcept { + printer.OpenElement("value"); + { + printer.PushAttribute("index00", uniformValue[0][0]); + printer.PushAttribute("index01", uniformValue[0][1]); + printer.PushAttribute("index02", uniformValue[0][2]); + + printer.PushAttribute("index10", uniformValue[1][0]); + printer.PushAttribute("index11", uniformValue[1][1]); + printer.PushAttribute("index12", uniformValue[1][2]); + + printer.PushAttribute("index20", uniformValue[2][0]); + printer.PushAttribute("index21", uniformValue[2][1]); + printer.PushAttribute("index22", uniformValue[2][2]); + } + printer.CloseElement(); +} +void MaterialSerializer::Helpers::SerializeUniformMatrix4f(tinyxml2::XMLPrinter& printer, const UniformMatrix4f& uniformValue) noexcept { + printer.OpenElement("value"); + { + printer.PushAttribute("index00", uniformValue[0][0]); + printer.PushAttribute("index01", uniformValue[0][1]); + printer.PushAttribute("index02", uniformValue[0][2]); + printer.PushAttribute("index03", uniformValue[0][3]); + + printer.PushAttribute("index10", uniformValue[1][0]); + printer.PushAttribute("index11", uniformValue[1][1]); + printer.PushAttribute("index12", uniformValue[1][2]); + printer.PushAttribute("index13", uniformValue[2][3]); + + printer.PushAttribute("index20", uniformValue[2][0]); + printer.PushAttribute("index21", uniformValue[2][1]); + printer.PushAttribute("index22", uniformValue[2][2]); + printer.PushAttribute("index23", uniformValue[2][3]); + + printer.PushAttribute("index30", uniformValue[3][0]); + printer.PushAttribute("index31", uniformValue[3][1]); + printer.PushAttribute("index32", uniformValue[3][2]); + printer.PushAttribute("index33", uniformValue[3][3]); + } + printer.CloseElement(); +} +void MaterialSerializer::Helpers::SerializeUniformMatrix2x3f(tinyxml2::XMLPrinter& printer, const UniformMatrix2x3f& uniformValue) noexcept { + printer.OpenElement("value"); + { + printer.PushAttribute("index00", uniformValue[0][0]); + printer.PushAttribute("index01", uniformValue[0][1]); + + printer.PushAttribute("index10", uniformValue[1][0]); + printer.PushAttribute("index11", uniformValue[1][1]); + + printer.PushAttribute("index20", uniformValue[2][0]); + printer.PushAttribute("index21", uniformValue[2][1]); + } + printer.CloseElement(); +} +void MaterialSerializer::Helpers::SerializeUniformMatrix3x2f(tinyxml2::XMLPrinter& printer, const UniformMatrix3x2f& uniformValue) noexcept { + printer.OpenElement("value"); + { + printer.PushAttribute("index00", uniformValue[0][0]); + printer.PushAttribute("index01", uniformValue[0][1]); + printer.PushAttribute("index02", uniformValue[0][2]); + + printer.PushAttribute("index10", uniformValue[1][0]); + printer.PushAttribute("index11", uniformValue[1][1]); + printer.PushAttribute("index12", uniformValue[1][2]); + } + printer.CloseElement(); +} +void MaterialSerializer::Helpers::SerializeUniformMatrix2x4f(tinyxml2::XMLPrinter& printer, const UniformMatrix2x4f& uniformValue) noexcept { + printer.OpenElement("value"); + { + printer.PushAttribute("index00", uniformValue[0][0]); + printer.PushAttribute("index01", uniformValue[0][1]); + + printer.PushAttribute("index10", uniformValue[1][0]); + printer.PushAttribute("index11", uniformValue[1][1]); + + printer.PushAttribute("index20", uniformValue[2][0]); + printer.PushAttribute("index21", uniformValue[2][1]); + + printer.PushAttribute("index30", uniformValue[3][0]); + printer.PushAttribute("index31", uniformValue[3][1]); + } + printer.CloseElement(); +} +void MaterialSerializer::Helpers::SerializeUniformMatrix4x2f(tinyxml2::XMLPrinter& printer, const UniformMatrix4x2f& uniformValue) noexcept { + printer.OpenElement("value"); + { + printer.PushAttribute("index00", uniformValue[0][0]); + printer.PushAttribute("index01", uniformValue[0][1]); + printer.PushAttribute("index02", uniformValue[0][2]); + printer.PushAttribute("index03", uniformValue[0][3]); + + printer.PushAttribute("index10", uniformValue[1][0]); + printer.PushAttribute("index11", uniformValue[1][1]); + printer.PushAttribute("index12", uniformValue[1][2]); + printer.PushAttribute("index13", uniformValue[1][3]); + } + printer.CloseElement(); +} +void MaterialSerializer::Helpers::SerializeUniformMatrix3x4f(tinyxml2::XMLPrinter& printer, const UniformMatrix3x4f& uniformValue) noexcept { + printer.OpenElement("value"); + { + printer.PushAttribute("index00", uniformValue[0][0]); + printer.PushAttribute("index01", uniformValue[0][1]); + printer.PushAttribute("index02", uniformValue[0][2]); + + printer.PushAttribute("index10", uniformValue[1][0]); + printer.PushAttribute("index11", uniformValue[1][1]); + printer.PushAttribute("index12", uniformValue[1][2]); + + printer.PushAttribute("index20", uniformValue[2][0]); + printer.PushAttribute("index21", uniformValue[2][1]); + printer.PushAttribute("index22", uniformValue[2][2]); + + printer.PushAttribute("index30", uniformValue[3][0]); + printer.PushAttribute("index31", uniformValue[3][1]); + printer.PushAttribute("index32", uniformValue[3][2]); + } + printer.CloseElement(); +} +void MaterialSerializer::Helpers::SerializeUniformMatrix4x3f(tinyxml2::XMLPrinter& printer, const UniformMatrix4x3f& uniformValue) noexcept { + printer.OpenElement("value"); + { + printer.PushAttribute("index00", uniformValue[0][0]); + printer.PushAttribute("index01", uniformValue[0][1]); + printer.PushAttribute("index02", uniformValue[0][2]); + printer.PushAttribute("index03", uniformValue[0][3]); + + printer.PushAttribute("index10", uniformValue[1][0]); + printer.PushAttribute("index11", uniformValue[1][1]); + printer.PushAttribute("index12", uniformValue[1][2]); + printer.PushAttribute("index13", uniformValue[1][3]); + + printer.PushAttribute("index20", uniformValue[2][0]); + printer.PushAttribute("index21", uniformValue[2][1]); + printer.PushAttribute("index22", uniformValue[2][2]); + printer.PushAttribute("index23", uniformValue[2][3]); + } + printer.CloseElement(); +} + +void MaterialSerializer::Helpers::SerializeUniformSampler2D(tinyxml2::XMLPrinter& printer, const UniformSampler2D& uniformValue) noexcept { + printer.OpenElement("value"); + { + printer.PushAttribute("texture", uniformValue.textureUUID); + printer.PushAttribute("sampler", uniformValue.samplerUUID); + } + printer.CloseElement(); +} diff --git a/Engine/MaterialSerializer.hpp b/Engine/MaterialSerializer.hpp new file mode 100644 index 00000000..c0610762 --- /dev/null +++ b/Engine/MaterialSerializer.hpp @@ -0,0 +1,100 @@ +#pragma once + +#include +#include + +#include + +#include +#include + +/* This is the big bad bada-boom of serializers, this function will call appropriate serializers. See below for an overview! */ +std::string SerializeMaterial(const Material& material) noexcept; + +/* Here is the general overview */ +namespace MaterialSerializer { + + /* ------- Type Definitons ------- */ + using HeaderCommentFunction = std::function; + using SerializeFunction = std::function; + namespace Program { + using SerializeFunction = std::function; + using SerializeIDFunction = std::function; + } + namespace Uniforms { + using SerializeFunction = std::function; + using SerializeVertexUniformsFunction = std::function; + using SerializeTessellationControlUniformsFunction = std::function; + using SerializeTessellationEvaluationUniformsFunction = std::function; + using SerializeGeometryUniformsFunction = std::function; + using SerializeFragmentUniformsFunction = std::function; + using SerializeUniformFunction = std::function; + } + + /* --- Default Implementations --- */ + /* These are how NeoDoa will serialize by default. */ + void DefaultHeaderComment(tinyxml2::XMLPrinter& printer, [[maybe_unused]] const Material& material) noexcept; + void DefaultSerialize(tinyxml2::XMLPrinter& printer, const Material& material) noexcept; + namespace Program { + void DefaultSerialize(tinyxml2::XMLPrinter& printer, const Material& material) noexcept; + void DefaultSerializeID(tinyxml2::XMLPrinter& printer, const UUID id) noexcept; + } + namespace Uniforms { + void DefaultSerialize(tinyxml2::XMLPrinter& printer, const Material& material) noexcept; + void DefaultSerializeVertexUniforms(tinyxml2::XMLPrinter& printer, const Material::Uniforms& uniforms) noexcept; + void DefaultSerializeTessellationControlUniforms(tinyxml2::XMLPrinter& printer, const Material::Uniforms& uniforms) noexcept; + void DefaultSerializeTessellationEvaluationUniforms(tinyxml2::XMLPrinter& printer, const Material::Uniforms& uniforms) noexcept; + void DefaultSerializeGeometryUniforms(tinyxml2::XMLPrinter& printer, const Material::Uniforms& uniforms) noexcept; + void DefaultSerializeFragmentUniforms(tinyxml2::XMLPrinter& printer, const Material::Uniforms& uniforms) noexcept; + void DefaultSerializeUniform(tinyxml2::XMLPrinter& printer, const UniformValue& uniform) noexcept; + } + + /* ----- Serializer Functions ----- */ + inline HeaderCommentFunction HeaderComment{ DefaultHeaderComment }; /* Feel free to assign this your own function, if you need custom serialization */ + inline SerializeFunction Serialize{ DefaultSerialize }; /* Feel free to assign this your own function, if you need custom serialization */ + namespace Program { + inline SerializeFunction Serialize{ DefaultSerialize }; /* Feel free to assign this your own function, if you need custom serialization */ + inline SerializeIDFunction SerializeID{ DefaultSerializeID }; /* Feel free to assign this your own function, if you need custom serialization */ + } + namespace Uniforms { + inline SerializeFunction Serialize{ DefaultSerialize }; /* Feel free to assign this your own function, if you need custom serialization */ + inline SerializeVertexUniformsFunction SerializeVertexUniforms{ DefaultSerializeVertexUniforms }; /* Feel free to assign this your own function, if you need custom serialization */ + inline SerializeTessellationControlUniformsFunction SerializeTessellationControlUniforms{ DefaultSerializeTessellationControlUniforms }; /* Feel free to assign this your own function, if you need custom serialization */ + inline SerializeTessellationEvaluationUniformsFunction SerializeTessellationEvaluationUniforms{ DefaultSerializeTessellationEvaluationUniforms }; /* Feel free to assign this your own function, if you need custom serialization */ + inline SerializeGeometryUniformsFunction SerializeGeometryUniforms{ DefaultSerializeGeometryUniforms }; /* Feel free to assign this your own function, if you need custom serialization */ + inline SerializeFragmentUniformsFunction SerializeFragmentUniforms{ DefaultSerializeFragmentUniforms }; /* Feel free to assign this your own function, if you need custom serialization */ + inline SerializeUniformFunction SerializeUniform{ DefaultSerializeUniform }; /* Feel free to assign this your own function, if you need custom serialization */ + } +} + +/* Some Helper functions to trivially serialize (not-so) trivial data types */ +namespace MaterialSerializer::Helpers { + std::string ExtractUniformTypeString(const UniformValue& uniform) noexcept; + + void SerializeUniform1f(tinyxml2::XMLPrinter& printer, const Uniform1f& uniformValue) noexcept; + void SerializeUniform2f(tinyxml2::XMLPrinter& printer, const Uniform2f& uniformValue) noexcept; + void SerializeUniform3f(tinyxml2::XMLPrinter& printer, const Uniform3f& uniformValue) noexcept; + void SerializeUniform4f(tinyxml2::XMLPrinter& printer, const Uniform4f& uniformValue) noexcept; + + void SerializeUniform1i(tinyxml2::XMLPrinter& printer, const Uniform1i& uniformValue) noexcept; + void SerializeUniform2i(tinyxml2::XMLPrinter& printer, const Uniform2i& uniformValue) noexcept; + void SerializeUniform3i(tinyxml2::XMLPrinter& printer, const Uniform3i& uniformValue) noexcept; + void SerializeUniform4i(tinyxml2::XMLPrinter& printer, const Uniform4i& uniformValue) noexcept; + + void SerializeUniform1ui(tinyxml2::XMLPrinter& printer, const Uniform1ui& uniformValue) noexcept; + void SerializeUniform2ui(tinyxml2::XMLPrinter& printer, const Uniform2ui& uniformValue) noexcept; + void SerializeUniform3ui(tinyxml2::XMLPrinter& printer, const Uniform3ui& uniformValue) noexcept; + void SerializeUniform4ui(tinyxml2::XMLPrinter& printer, const Uniform4ui& uniformValue) noexcept; + + void SerializeUniformMatrix2f(tinyxml2::XMLPrinter& printer, const UniformMatrix2f& uniformValue) noexcept; + void SerializeUniformMatrix3f(tinyxml2::XMLPrinter& printer, const UniformMatrix3f& uniformValue) noexcept; + void SerializeUniformMatrix4f(tinyxml2::XMLPrinter& printer, const UniformMatrix4f& uniformValue) noexcept; + void SerializeUniformMatrix2x3f(tinyxml2::XMLPrinter& printer, const UniformMatrix2x3f& uniformValue) noexcept; + void SerializeUniformMatrix3x2f(tinyxml2::XMLPrinter& printer, const UniformMatrix3x2f& uniformValue) noexcept; + void SerializeUniformMatrix2x4f(tinyxml2::XMLPrinter& printer, const UniformMatrix2x4f& uniformValue) noexcept; + void SerializeUniformMatrix4x2f(tinyxml2::XMLPrinter& printer, const UniformMatrix4x2f& uniformValue) noexcept; + void SerializeUniformMatrix3x4f(tinyxml2::XMLPrinter& printer, const UniformMatrix3x4f& uniformValue) noexcept; + void SerializeUniformMatrix4x3f(tinyxml2::XMLPrinter& printer, const UniformMatrix4x3f& uniformValue) noexcept; + + void SerializeUniformSampler2D(tinyxml2::XMLPrinter& printer, const UniformSampler2D& uniformValue) noexcept; +} \ No newline at end of file diff --git a/Engine/Model.cpp b/Engine/Model.cpp deleted file mode 100644 index 698a47e8..00000000 --- a/Engine/Model.cpp +++ /dev/null @@ -1,213 +0,0 @@ -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -Model::Model(std::string_view path) noexcept { - std::cout << "Creating model from disk: " << path << std::endl; - -#if defined(DEBUG) - Assimp::DefaultLogger::create("", Assimp::Logger::VERBOSE, aiDefaultLogStream_STDOUT); -#else - Assimp::DefaultLogger::create("", Assimp::Logger::NORMAL, aiDefaultLogStream_STDOUT); -#endif - Assimp::Importer importer; - const aiScene* scene = importer.ReadFile(path.data(), aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_FlipWindingOrder | aiProcess_GenSmoothNormals | aiProcess_FixInfacingNormals); - Assimp::DefaultLogger::kill(); - - if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) { - std::cout << "Couldn't read scene file: " << importer.GetErrorString() << std::endl; - return; - } - this->name = path.substr(path.find_last_of("/\\") + 1); - this->path = path; - - processNode(avatar, scene->mRootNode, scene); - meshes = realizeMeshes(avatar); - CalculateAABB(); -} - -const std::string& Model::Name() const noexcept { return name; } -const std::string& Model::Path() const noexcept { return path; } - -const Avatar& Model::GetAvatar() const noexcept { return avatar; } - -void Model::CalculateAABB() const noexcept { - aabb = meshes[0].AABB; - - for (const auto& mesh : meshes) { - const auto& bb = mesh.AABB; - - aabb.Min = glm::min(aabb.Min, bb.Min); - aabb.Max = glm::max(aabb.Max, bb.Max); - } -} -const AABB& Model::GetAABB() const noexcept { return aabb; } - -//----------------------------------------------------------------- -void Model::processNode(struct Avatar& avatar, aiNode* node, const aiScene* scene) noexcept { - Avatar::Node* parent{ nullptr }; - if (node->mParent) { - parent = &avatar.Nodes[node->mParent->mName.C_Str()]; - } - auto [iter, _] = avatar.Nodes.try_emplace( - std::string(node->mName.C_Str()), - std::string(node->mName.C_Str()), - aiMat4ToGLMMat4(node->mTransformation), - nullptr, //parent, - 0, - std::array{} - ); - Avatar::Node& current = iter->second; - if (parent) { - parent->Children[parent->ChildCount++] = ¤t; - } - - for (unsigned int i = 0; i < node->mNumMeshes; i++) { - aiMesh* mesh = scene->mMeshes[node->mMeshes[i]]; - Avatar::Node::Mesh& processedMesh = processMesh(current, mesh); - if (mesh->mNumBones == 0) { /* boneless animation */ - int boneId = static_cast(avatar.Bones.size()); - avatar.Bones.try_emplace( - current.Name, - boneId, - current.Name, - glm::mat4(1) - ); - for (auto& vertex : processedMesh.Vertices) { - vertex.BoneIDs[0] = boneId; - vertex.BoneWeights[0] = 1.0f; - } - } else { /* skeletal animation */ - for (unsigned int j = 0; j < mesh->mNumBones; j++) { - processBone(avatar, processedMesh, mesh->mBones[j]); - } - } - } - - for (unsigned int i = 0; i < node->mNumChildren; i++) { - processNode(avatar, node->mChildren[i], scene); - } - - if (node == scene->mRootNode) { - avatar.RootNode = ¤t; - } -} -Avatar::Node::Mesh& Model::processMesh(Avatar::Node& node, aiMesh* mesh) noexcept { - auto [iter, _] = node.Meshes.try_emplace( - mesh->mName.C_Str(), - mesh->mName.C_Str(), - std::vector{}, - std::vector{} - ); - Avatar::Node::Mesh& current = iter->second; - current.Vertices.resize(mesh->mNumVertices); - for (unsigned int i = 0; i < mesh->mNumVertices; i++) { - Vertex& vertex = current.Vertices[i]; - - vertex.Position = aiVecToGLMVec(mesh->mVertices[i]); - vertex.Normal = aiVecToGLMVec(mesh->mNormals[i]); - if (mesh->mColors[0]) { - vertex.Color = aiColorToGLMVec(mesh->mColors[0][i]); - } else { - vertex.Color = { 1.0f, 1.0f, 1.0f, 1.0f }; - } - if (mesh->mTextureCoords[0]) { // does the mesh contain texture coordinates? - vertex.TexCoords = aiVecToGLMVec(mesh->mTextureCoords[0][i]); - } else { - vertex.TexCoords = { 0.0f, 0.0f }; - } - - vertex.BoneIDs.fill(-1); - vertex.BoneWeights.fill(0.0f); - } - - for (unsigned int i = 0; i < mesh->mNumFaces; i++) { - aiFace face = mesh->mFaces[i]; - for (unsigned int j = 0; j < face.mNumIndices; j++) { - current.Indices.emplace_back(face.mIndices[j]); - } - } - - return current; -} -void Model::processBone(struct Avatar& avatar, Avatar::Node::Mesh& mesh, aiBone* bone) noexcept { - auto SetVertexBoneData = [](Vertex& vertex, int boneID, float weight) { - for (int i = 0; i < Vertex::MAX_BONE_PER_VERTEX; ++i) { - if (vertex.BoneIDs[i] < 0) { - vertex.BoneIDs[i] = boneID; - vertex.BoneWeights[i] = weight; - break; - } - } - }; - - auto [iter, _] = avatar.Bones.try_emplace( - bone->mName.C_Str(), - avatar.Bones.size(), - bone->mName.C_Str(), - aiMat4ToGLMMat4(bone->mOffsetMatrix) - ); - Avatar::Bone& current = iter->second; - - auto weights = bone->mWeights; - auto numWeights = bone->mNumWeights; - - for (unsigned int j = 0; j < numWeights; j++) { - auto vertexID = weights[j].mVertexId; - auto weight = weights[j].mWeight; - assert(vertexID <= mesh.Vertices.size()); /* this also practically should never happen! re-export your model */ - SetVertexBoneData(mesh.Vertices[vertexID], current.Id, weight); - } -} -std::vector Model::realizeMeshes(const Avatar& avatar) noexcept { - std::vector rv{}; - for (const auto& [_, node] : avatar.Nodes) { - for (const auto& [__, mesh] : node.Meshes) { - rv.emplace_back(&mesh); - } - } - return rv; -} - -Model::RenderableMesh::RenderableMesh(const Avatar::Node::Mesh* const mesh) noexcept : - Mesh(mesh) { - Buffer vbo(Buffer::Accept(mesh->Vertices)); - VertexAttribLayout layout; - layout.Define(3); // Position - layout.Define(3); // Normal - layout.Define(4); // Color - layout.Define(2); // UV - layout.Define(4); // BoneIDs - layout.Define(4); // BoneWeights - VAO.BindVertexAttribBuffer(std::move(vbo), std::move(layout)); - - Buffer ebo(Buffer::Accept(mesh->Indices)); - VAO.BindElementBuffer(std::move(ebo)); - // --------------------------------------------------------------- - AABB.Min = glm::vec3(std::numeric_limits::max()); - AABB.Max = -glm::vec3(std::numeric_limits::max()); - - for (const auto& vertex : mesh->Vertices) { - const auto& position = vertex.Position; - - AABB.Min.x = std::min(AABB.Min.x, position.x); - AABB.Min.y = std::min(AABB.Min.y, position.y); - AABB.Min.z = std::min(AABB.Min.z, position.z); - - AABB.Max.x = std::max(AABB.Max.x, position.x); - AABB.Max.y = std::max(AABB.Max.y, position.y); - AABB.Max.z = std::max(AABB.Max.z, position.z); - } -} \ No newline at end of file diff --git a/Engine/Model.hpp b/Engine/Model.hpp deleted file mode 100644 index 270aa149..00000000 --- a/Engine/Model.hpp +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -struct Model { - struct RenderableMesh { - VertexArray VAO{}; - const Avatar::Node::Mesh* const Mesh; - struct AABB AABB; - - explicit RenderableMesh(const Avatar::Node::Mesh* const mesh) noexcept; - }; - - Model(std::string_view path) noexcept; - ~Model() noexcept = default; - Model(const Model& other) noexcept = default; - Model(Model&& other) noexcept = default; - Model& operator=(const Model& other) noexcept = default; - Model& operator=(Model&& other) noexcept = default; - - const std::string& Name() const noexcept; - const std::string& Path() const noexcept; - - const Avatar& GetAvatar() const noexcept; - - void CalculateAABB() const noexcept; - const AABB& GetAABB() const noexcept; - -private: - - std::string name{}; - std::string path{}; - - Avatar avatar{}; - std::vector meshes{}; - mutable AABB aabb{}; - - static void processMaterials(Avatar& avatar, const aiScene* scene) noexcept; - static void processNode(Avatar& avatar, aiNode* node, const aiScene* scene) noexcept; - static Avatar::Node::Mesh& processMesh(Avatar::Node& node, aiMesh* mesh) noexcept; - static void processBone(Avatar& avatar, Avatar::Node::Mesh& mesh, aiBone* bone) noexcept; - static std::vector realizeMeshes(const Avatar& avatar) noexcept; - -private: - using itr = decltype(meshes)::iterator; - using const_itr = decltype(meshes)::const_iterator; -public: - inline itr begin() { return meshes.begin(); } - inline itr end() { return meshes.end(); } - inline const_itr begin() const { return meshes.begin(); } - inline const_itr end() const { return meshes.end(); } - inline const_itr cbegin() const { return meshes.cbegin(); } - inline const_itr cend() const { return meshes.cend(); } -}; diff --git a/Engine/Monitor.cpp b/Engine/Monitor.cpp new file mode 100644 index 00000000..10605843 --- /dev/null +++ b/Engine/Monitor.cpp @@ -0,0 +1,47 @@ +#include + +#include + +#include + +#include +#include + +IMonitor::~IMonitor() noexcept = default; + +Monitors::MonitorList::MonitorList(std::vector>& monitors) noexcept : monitors(monitors) {} +Monitors::MonitorList::Iterator Monitors::MonitorList::begin() { return { std::to_address(monitors.begin()) }; } +Monitors::MonitorList::Iterator Monitors::MonitorList::end() { return { std::to_address(monitors.end()) }; } + +IMonitor& Monitors::PrimaryMonitor() noexcept { assert(!monitors.empty()); return *monitors[0]; } +Monitors::MonitorList Monitors::AllMonitors() noexcept { return monitors; } + +void Monitors::Initialize() noexcept { + assert(!initialized); + initialized = true; + if (!glfwInit()) { + const char* errorMessage; + glfwGetError(&errorMessage); + DOA_LOG_FATAL("GLFW couldn't initialize! Reason: %s", errorMessage); + std::abort(); + } + + int major, minor, revision; + glfwGetVersion(&major, &minor, &revision); + DOA_LOG_INFO("Initialized GLFW %i.%i.%i for Monitor queries.", major, minor, revision); + + int count; + auto** glfwMonitors = glfwGetMonitors(&count); + + monitors.reserve(count); + for (int i = 0; i < count; i++) { + MonitorGLFW* monitor = new(std::nothrow) MonitorGLFW(i, glfwMonitors[i]); + monitors.emplace_back(monitor); + } +} + +void Monitors::DeInitialize() noexcept { + assert(initialized); + monitors.clear(); + glfwTerminate(); +} diff --git a/Engine/Monitor.hpp b/Engine/Monitor.hpp new file mode 100644 index 00000000..5f3c12d8 --- /dev/null +++ b/Engine/Monitor.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include + +struct VideoMode { + Resolution Size{ 1, 1 }; // Must set + int RedBits{ 8 }; // Can ignore safely + int GreenBits{ 8 }; // Can ignore safely + int BlueBits{ 8 }; // Can ignore safely + int RefreshRate{ -1 }; // -1 is "Don't care" +}; + +struct IMonitor { + + virtual ~IMonitor() noexcept = 0; + + virtual unsigned GetIndex() const noexcept = 0; + + virtual Point GetPosition() const noexcept = 0; + virtual Region GetPositionWorkArea() const noexcept = 0; + + virtual std::string_view GetName() const noexcept = 0; + + virtual std::span GetVideoModes() const noexcept = 0; + virtual VideoMode GetCurrentVideoMode() const noexcept = 0; +}; + +struct Monitors { + struct MonitorList { + template + struct Iterator { + using iterator_category = std::forward_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = T; + using pointer = value_type*; + using reference = value_type&; + using smart = const std::unique_ptr>; + + Iterator(smart* ptr) : ptr(ptr) {} + + reference operator*() const { return *ptr; } + pointer operator->() { return ptr; } + Iterator& operator++() { ptr++; return *this; } + Iterator operator++(int) { Iterator tmp = *this; ++(*this); return tmp; } + friend bool operator==(const Iterator& a, const Iterator& b) = default; + + private: + smart* ptr; + }; + + MonitorList(std::vector>& monitors) noexcept; + + Iterator begin(); + Iterator end(); + + private: + std::vector>& monitors; + }; + + static IMonitor& PrimaryMonitor() noexcept; + static MonitorList AllMonitors() noexcept; + +private: + static inline bool initialized{}; + static inline std::vector> monitors{}; + + static void Initialize() noexcept; + static void DeInitialize() noexcept; + + friend struct Core; +}; \ No newline at end of file diff --git a/Engine/MonitorGLFW.cpp b/Engine/MonitorGLFW.cpp new file mode 100644 index 00000000..d52ccb02 --- /dev/null +++ b/Engine/MonitorGLFW.cpp @@ -0,0 +1,54 @@ +#include + +#include + +MonitorGLFW::MonitorGLFW(unsigned index, GLFWmonitor* glfwMonitor) noexcept : + index(index), + glfwMonitor(glfwMonitor) { + int count; + const GLFWvidmode* modes = glfwGetVideoModes(glfwMonitor, &count); + for (int i = 0; i < count; i++) { + videoModes.emplace_back(GLFWvidmodeToVideoMode(modes[i])); + } +} + +unsigned MonitorGLFW::GetIndex() const noexcept { return index; } + +Point MonitorGLFW::GetPosition() const noexcept { + int xpos, ypos; + glfwGetMonitorPos(glfwMonitor, &xpos, &ypos); + + return { + static_cast(xpos), + static_cast(ypos), + }; +} +Region MonitorGLFW::GetPositionWorkArea() const noexcept { + int xpos, ypos, width, height; + glfwGetMonitorWorkarea(glfwMonitor, &xpos, &ypos, &width, &height); + + return { + static_cast(xpos), + static_cast(ypos), + static_cast(width), + static_cast(height), + }; +} + +std::string_view MonitorGLFW::GetName() const noexcept { return glfwGetMonitorName(glfwMonitor); } + +std::span MonitorGLFW::GetVideoModes() const noexcept { return videoModes; } +VideoMode MonitorGLFW::GetCurrentVideoMode() const noexcept { return GLFWvidmodeToVideoMode(*glfwGetVideoMode(glfwMonitor)); } + +VideoMode MonitorGLFW::GLFWvidmodeToVideoMode(const GLFWvidmode& vidMode) noexcept { + return { + Resolution{ + static_cast(vidMode.width), + static_cast(vidMode.height) + }, + vidMode.redBits, + vidMode.greenBits, + vidMode.blueBits, + vidMode.refreshRate + }; +} \ No newline at end of file diff --git a/Engine/MonitorGLFW.hpp b/Engine/MonitorGLFW.hpp new file mode 100644 index 00000000..902faccc --- /dev/null +++ b/Engine/MonitorGLFW.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include + +struct GLFWmonitor; +struct GLFWvidmode; + +struct MonitorGLFW : public IMonitor { + + unsigned GetIndex() const noexcept override; + + Point GetPosition() const noexcept override; + Region GetPositionWorkArea() const noexcept override; + + std::string_view GetName() const noexcept override; + + std::span GetVideoModes() const noexcept override; + VideoMode GetCurrentVideoMode() const noexcept override; + +private: + + static VideoMode GLFWvidmodeToVideoMode(const GLFWvidmode& vidMode) noexcept; + + MonitorGLFW(unsigned index, GLFWmonitor* glfwMonitor) noexcept; + + unsigned index{}; + GLFWmonitor* glfwMonitor{}; + std::vector videoModes{}; + + friend struct Monitors; +}; \ No newline at end of file diff --git a/Engine/MultiMaterialComponent.cpp b/Engine/MultiMaterialComponent.cpp new file mode 100644 index 00000000..82b6892b --- /dev/null +++ b/Engine/MultiMaterialComponent.cpp @@ -0,0 +1,9 @@ +#include + +MultiMaterialComponent::MultiMaterialComponent(Entity entity) noexcept : + entity(entity) {} + +Entity MultiMaterialComponent::GetEntity() const noexcept { return entity; } + +std::vector& MultiMaterialComponent::GetMaterials() noexcept { return materials; } +const std::vector& MultiMaterialComponent::GetMaterials() const noexcept { return materials; } \ No newline at end of file diff --git a/Engine/MultiMaterialComponent.hpp b/Engine/MultiMaterialComponent.hpp new file mode 100644 index 00000000..812941d4 --- /dev/null +++ b/Engine/MultiMaterialComponent.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include + +#include +#include + +struct MultiMaterialComponent { + + explicit MultiMaterialComponent(Entity entity) noexcept; + + Entity GetEntity() const noexcept; + + std::vector& GetMaterials() noexcept; + const std::vector& GetMaterials() const noexcept; + +private: + + Entity entity{}; + std::vector materials{}; +}; \ No newline at end of file diff --git a/Engine/NeoDoa.hpp b/Engine/NeoDoa.hpp index e4b4a429..a63ef8d6 100644 --- a/Engine/NeoDoa.hpp +++ b/Engine/NeoDoa.hpp @@ -81,7 +81,6 @@ #include #include #include -#include #pragma endregion #pragma region Log @@ -120,7 +119,6 @@ #pragma endregion #pragma region Model #include -#include #pragma endregion #pragma region ScriptComponent #pragma region Modules diff --git a/Engine/OpenGLCommon.hpp b/Engine/OpenGLCommon.hpp deleted file mode 100644 index 6b095d7b..00000000 --- a/Engine/OpenGLCommon.hpp +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -#include - -#include - -namespace OpenGL { - using BufferData = std::vector; - - enum BufferBit { - COLOR_BUFFER_BIT = GL_COLOR_BUFFER_BIT, - DEPTH_BUFFER_BIT = GL_DEPTH_BUFFER_BIT, - STENCIL_BUFFER_BIT = GL_STENCIL_BUFFER_BIT, - }; - - enum TextureWrapMode { - REPEAT = GL_REPEAT, - MIRRORED_REPEAT = GL_MIRRORED_REPEAT, - CLAMP_TO_EDGE = GL_CLAMP_TO_EDGE, - CLAMP_TO_BORDER = GL_CLAMP_TO_BORDER - }; - - enum TextureFormat { - RGB8 = GL_RGB8, - RGBA8 = GL_RGBA8, - RGB16 = GL_RGB16, - RGBA16 = GL_RGBA16, - RGB16F = GL_RGB16F, - RGBA16F = GL_RGBA16F, - RGB32F = GL_RGB32F, - RGBA32F = GL_RGBA32F, - R11F_G11F_B10F = GL_R11F_G11F_B10F - }; - - enum DepthFormat { - DEPTH_COMPONENT16 = GL_DEPTH_COMPONENT16, - DEPTH_COMPONENT24 = GL_DEPTH_COMPONENT24, - DEPTH_COMPONENT32 = GL_DEPTH_COMPONENT32, - DEPTH_COMPONENT32F = GL_DEPTH_COMPONENT32F, - }; - - enum StencilFormat { - STENCIL_INDEX1 = GL_STENCIL_INDEX1, - STENCIL_INDEX4 = GL_STENCIL_INDEX4, - STENCIL_INDEX8 = GL_STENCIL_INDEX8, - STENCIL_INDEX16 = GL_STENCIL_INDEX16, - }; - - enum DepthStencilFormat { - DEPTH24_STENCIL8 = GL_DEPTH24_STENCIL8, - DEPTH32F_STENCIL8 = GL_DEPTH32F_STENCIL8 - }; -} \ No newline at end of file diff --git a/Engine/OrthoCamera.cpp b/Engine/OrthoCamera.cpp index d88fd9f2..d021f89d 100644 --- a/Engine/OrthoCamera.cpp +++ b/Engine/OrthoCamera.cpp @@ -1,36 +1,37 @@ -#include "OrthoCamera.hpp" +#include #include #include -OrthoCamera::OrthoCamera(float left, float right, float bottom, float top, float near, float far) : - _left(left), - _right(right), - _bottom(bottom), - _top(top), - _near(near), - _far(far) {} +OrthoCamera::OrthoCamera(float left, float right, float bottom, float top, float near, float far) noexcept : + LeftPlane(left), + RightPlane(right), + BottomPlane(bottom), + TopPlane(top), + NearPlane(near), + FarPlane(far) {} -void OrthoCamera::Set(float left, float right, float bottom, float top, float near, float far) { - _left = left; - _right = right; - _bottom = bottom; - _top = top; - _near = near; - _far = far; +void OrthoCamera::Set(float left, float right, float bottom, float top, float near, float far) noexcept { + LeftPlane = left; + RightPlane = right; + BottomPlane = bottom; + TopPlane = top; + NearPlane = near; + FarPlane = far; } - -void OrthoCamera::UpdateView() { - forward = glm::normalize(forward); - up = glm::normalize(up); - _viewMatrix = glm::lookAt(eye, eye + forward, up); - _viewMatrix = glm::scale(_viewMatrix, { 1, 1, 1 / zoom }); -} - -void OrthoCamera::UpdateProjection() { - _projectionMatrix = glm::ortho(_left, _right, _bottom, _top, _near, _far); +void OrthoCamera::UpdateView() noexcept { + Forward = glm::normalize(Forward); + Up = glm::normalize(Up); + viewMatrix = glm::lookAt(Eye, Eye + Forward, Up); + viewMatrix = glm::scale(viewMatrix, { 1, 1, 1 / Zoom }); } - -void OrthoCamera::UpdateViewProjection() { - _viewProjectionMatrix = _projectionMatrix * _viewMatrix; +void OrthoCamera::UpdateProjection() noexcept { + projectionMatrix = glm::ortho( + LeftPlane, RightPlane, + BottomPlane, TopPlane, + NearPlane, FarPlane + ); } +void OrthoCamera::UpdateViewProjection() noexcept { + viewProjectionMatrix = projectionMatrix * viewMatrix; +} \ No newline at end of file diff --git a/Engine/OrthoCamera.hpp b/Engine/OrthoCamera.hpp index d2a28b99..03bc2b27 100644 --- a/Engine/OrthoCamera.hpp +++ b/Engine/OrthoCamera.hpp @@ -1,19 +1,19 @@ #pragma once -#include "ACamera.hpp" +#include struct OrthoCamera : ACamera { - float _left; - float _right; - float _bottom; - float _top; - float _near; - float _far; + float LeftPlane; + float RightPlane; + float BottomPlane; + float TopPlane; + float NearPlane; + float FarPlane; - OrthoCamera(float left, float right, float bottom, float top, float near, float far); + OrthoCamera(float left, float right, float bottom, float top, float near, float far) noexcept; - void Set(float left, float right, float bottom, float top, float near, float far); - void UpdateView() override; - void UpdateProjection() override; - void UpdateViewProjection() override; + void Set(float left, float right, float bottom, float top, float near, float far) noexcept; + void UpdateView() noexcept override; + void UpdateProjection() noexcept override; + void UpdateViewProjection() noexcept override; }; diff --git a/Engine/OstreamImpls.cpp b/Engine/OstreamImpls.cpp index 5df8a6c4..7081dfd0 100644 --- a/Engine/OstreamImpls.cpp +++ b/Engine/OstreamImpls.cpp @@ -1,22 +1,31 @@ #include #include +#include #include #include #include -std::ostream& operator <<(std::ostream& os, const Resolution& r) { - return os << "Resolution [Width=" << r.Width << ", Height=" << r.Height << "]"; +std::ostream& operator<<(std::ostream& os, const AABB& aabb) { + return os << "AABB [Min=" << glm::to_string(aabb.Min) << ", Max=" << glm::to_string(aabb.Max) << "]"; +} + +std::ostream& operator <<(std::ostream& os, const Point& p) { + return os << "Point [X=" << p.X << ", Y=" << p.Y << "]"; +} + +std::ostream& operator <<(std::ostream& os, const PointDouble& p) { + return os << "Point [X=" << p.X << ", Y=" << p.Y << "]"; } std::ostream& operator<<(std::ostream& os, const Vertex& v) { - return os << "Vertex [Position=" << glm::to_string(v.Position) << ", Normal= " << glm::to_string(v.Normal) << ", UV=" << glm::to_string(v.TexCoords); + return os << "Vertex [\tPosition=" << glm::to_string(v.Position) << ",\n\tNormal= " << glm::to_string(v.Normal) << ",\n\tUV=" << glm::to_string(v.TexCoords) << "\n]"; } std::ostream& operator <<(std::ostream& os, const Region& r) { return os << "Region [X=" << r.X << ", Y=" << r.Y << ", Width=" << r.Width << ", Height=" << r.Height << "]"; } -std::ostream& operator<<(std::ostream& os, const AABB& aabb) { - return os << "AABB [Min=" << glm::to_string(aabb.Min) << ", Max=" << glm::to_string(aabb.Max); +std::ostream& operator <<(std::ostream& os, const Resolution& r) { + return os << "Resolution [Width=" << r.Width << ", Height=" << r.Height << "]"; } \ No newline at end of file diff --git a/Engine/OutlineRenderer.cpp b/Engine/OutlineRenderer.cpp deleted file mode 100644 index 4fca0228..00000000 --- a/Engine/OutlineRenderer.cpp +++ /dev/null @@ -1,104 +0,0 @@ -#include "OutlineRenderer.hpp" - -#include -#include - -#include "Model.hpp" -#include "Shader.hpp" - -#include "OrthoCamera.hpp" -#include "PerspectiveCamera.hpp" - -#include "Texture.hpp" - -//----------------------------------------------------------------- - -void OutlineRenderer::Render(std::vector>& objects, ACamera* cam, glm::vec3 outlineColor) { - /* - vertices = 0; - indices = 0; - drawCalls = 0; - - std::weak_ptr stdShader = FindShader("Simple Shader"); - std::shared_ptr shader = stdShader.lock(); - shader->Use(); - - glEnable(GL_STENCIL_TEST); - glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE); - glStencilFunc(GL_ALWAYS, 1, 0xFF); - glStencilMask(0xFF); - for (auto& [t, mr] : objects) { - shader->UniformMat4("modelMatrix", (float*)glm::value_ptr(CreateModelMatrixFromTransform(t))); - shader->UniformMat4("viewProjMatrix", glm::value_ptr(cam->_viewProjectionMatrix)); - Model*& model = mr.Model(); - for (auto& mesh : model->_meshes) { - FindTexture("!!missing!!").value().lock()->Bind(); - int i = 1; - for (auto& tex : mesh._textures) { - if (i == 17) { - break; - } - tex.lock()->Bind(i++); - } - - if (!mr._isActive) { - glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); - } else { - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - } - - glBindVertexArray(mesh._vao); - if (mesh._ebo > 0) { - glDrawElements(GL_TRIANGLES, mesh._indices.size(), GL_UNSIGNED_INT, 0); - } else { - glDrawArrays(GL_TRIANGLES, 0, mesh._vertices.size()); - } - - vertices += mesh._vertices.size(); - indices += mesh._indices.size(); - drawCalls++; - } - } - glStencilFunc(GL_NOTEQUAL, 1, 0xFF); - glStencilMask(0x00); // disable writing to the stencil buffer - glDisable(GL_DEPTH_TEST); - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - stdShader = FindShader("Solid Color Shader"); - shader = stdShader.lock(); - shader->Use(); - for (auto& [t, mr] : objects) { - glm::vec3 origScale = t.Scale(); - t.Scale() += std::clamp(glm::distance(cam->eye, t.Translation()) / 50, 0.01f, 20.0f); - shader->UniformMat4("modelMatrix", (float*)glm::value_ptr(CreateModelMatrixFromTransform(t))); - shader->UniformMat4("viewProjMatrix", glm::value_ptr(cam->_viewProjectionMatrix)); - shader->Uniform4f("color", glm::value_ptr(outlineColor)); - t.Scale() = origScale; - Model*& model = mr.Model(); - for (auto& mesh : model->_meshes) { - FindTexture("!!missing!!").value().lock()->Bind(); - int i = 1; - for (auto& tex : mesh._textures) { - if (i == 17) { - break; - } - tex.lock()->Bind(i++); - } - - glBindVertexArray(mesh._vao); - if (mesh._ebo > 0) { - glDrawElements(GL_TRIANGLES, mesh._indices.size(), GL_UNSIGNED_INT, 0); - } else { - glDrawArrays(GL_TRIANGLES, 0, mesh._vertices.size()); - } - - vertices += mesh._vertices.size(); - indices += mesh._indices.size(); - drawCalls++; - } - } - glStencilMask(0xFF); - glEnable(GL_DEPTH_TEST); - glStencilFunc(GL_ALWAYS, 1, 0xFF); - glDisable(GL_STENCIL_TEST); - */ -} \ No newline at end of file diff --git a/Engine/OutlineRenderer.hpp b/Engine/OutlineRenderer.hpp deleted file mode 100644 index 4dd0b8f9..00000000 --- a/Engine/OutlineRenderer.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include -#include - -struct Transform; -struct ModelRenderer; -struct ACamera; - -struct OutlineRenderer { - - int vertices = 0; - int indices = 0; - int drawCalls = 0; - - void Render(std::vector>& objects, ACamera* cam, glm::vec3 outlineColor); -}; diff --git a/Engine/PerspectiveCamera.cpp b/Engine/PerspectiveCamera.cpp index ddc755b3..5b2a8182 100644 --- a/Engine/PerspectiveCamera.cpp +++ b/Engine/PerspectiveCamera.cpp @@ -1,34 +1,31 @@ -#include "PerspectiveCamera.hpp" +#include #include #include #include -PerspectiveCamera::PerspectiveCamera(float fov, float aspect, float near, float far) : - _fov(fov), - _aspect(aspect), - _near(near), - _far(far) {} +PerspectiveCamera::PerspectiveCamera(float fov, float aspect, float near, float far) noexcept : + FOV(fov), + AspectRatio(aspect), + NearPlane(near), + FarPlane(far) {} -void PerspectiveCamera::Set(float fov, float aspect, float near, float far) { - _fov = fov; - _aspect = aspect; - _near = near; - _far = far; +void PerspectiveCamera::Set(float fov, float aspect, float near, float far) noexcept { + FOV = fov; + AspectRatio = aspect; + NearPlane = near; + FarPlane = far; } - -void PerspectiveCamera::UpdateView() { - forward = glm::normalize(forward); - up = glm::normalize(up); - _viewMatrix = glm::lookAt(eye, eye + forward, up); +void PerspectiveCamera::UpdateView() noexcept { + Forward = glm::normalize(Forward); + Up = glm::normalize(Up); + viewMatrix = glm::lookAt(Eye, Eye + Forward, Up); } - -void PerspectiveCamera::UpdateProjection() { - zoom = std::max(1.f, zoom); - _projectionMatrix = glm::perspective(glm::radians(_fov / zoom), _aspect, _near, _far); +void PerspectiveCamera::UpdateProjection() noexcept { + Zoom = std::max(1.f, Zoom); + projectionMatrix = glm::perspective(glm::radians(FOV / Zoom), AspectRatio, NearPlane, FarPlane); } - -void PerspectiveCamera::UpdateViewProjection() { - _viewProjectionMatrix = _projectionMatrix * _viewMatrix; +void PerspectiveCamera::UpdateViewProjection() noexcept { + viewProjectionMatrix = projectionMatrix * viewMatrix; } diff --git a/Engine/PerspectiveCamera.hpp b/Engine/PerspectiveCamera.hpp index f547c3bb..92dc3fff 100644 --- a/Engine/PerspectiveCamera.hpp +++ b/Engine/PerspectiveCamera.hpp @@ -1,17 +1,17 @@ #pragma once -#include "ACamera.hpp" +#include struct PerspectiveCamera : ACamera { - float _fov; - float _aspect; - float _near; - float _far; + float FOV; + float AspectRatio; + float NearPlane; + float FarPlane; - PerspectiveCamera(float fov, float aspect, float near, float far); + PerspectiveCamera(float fov, float aspect, float near, float far) noexcept; - void Set(float fov, float aspect, float near, float far); - void UpdateView() override; - void UpdateProjection() override; - void UpdateViewProjection() override; -}; + void Set(float fov, float aspect, float near, float far) noexcept; + void UpdateView() noexcept override; + void UpdateProjection() noexcept override; + void UpdateViewProjection() noexcept override; +}; \ No newline at end of file diff --git a/Engine/Point.hpp b/Engine/Point.hpp new file mode 100644 index 00000000..27937b90 --- /dev/null +++ b/Engine/Point.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include + +struct Point { + unsigned X{}; + unsigned Y{}; + + bool operator==(const Point& other) const noexcept = default; + + friend std::ostream& operator<<(std::ostream& os, const Point& r); +}; + +struct PointDouble { + double X{}; + double Y{}; + + bool operator==(const PointDouble& other) const noexcept = default; + + friend std::ostream& operator<<(std::ostream& os, const Point& r); +}; \ No newline at end of file diff --git a/Engine/Region.hpp b/Engine/Region.hpp index 69bb3e57..3f012625 100644 --- a/Engine/Region.hpp +++ b/Engine/Region.hpp @@ -1,14 +1,10 @@ #pragma once -#include - struct Region { - int X{}; - int Y{}; - int Width{}; - int Height{}; + unsigned X{}; + unsigned Y{}; + unsigned Width{}; + unsigned Height{}; bool operator==(const Region& other) const noexcept = default; - - friend std::ostream& operator<<(std::ostream& os, const Region& r); }; \ No newline at end of file diff --git a/Engine/Renderer.hpp b/Engine/Renderer.hpp index 0d2f4d24..3bdcaf48 100644 --- a/Engine/Renderer.hpp +++ b/Engine/Renderer.hpp @@ -10,7 +10,8 @@ #include "Shader.hpp" #include "Entity.hpp" #include "Registry.hpp" -#include "TypedefsAndConstants.hpp" + +typedef GLuint VBO; struct Model; struct Mesh; diff --git a/Engine/Resolution.hpp b/Engine/Resolution.hpp index c50392a5..89876629 100644 --- a/Engine/Resolution.hpp +++ b/Engine/Resolution.hpp @@ -3,8 +3,8 @@ #include struct Resolution { - int Width{}; - int Height{}; + unsigned Width{}; + unsigned Height{}; bool operator==(const Resolution& other) const noexcept = default; diff --git a/Engine/Sampler.cpp b/Engine/Sampler.cpp new file mode 100644 index 00000000..6480d156 --- /dev/null +++ b/Engine/Sampler.cpp @@ -0,0 +1,9 @@ +#include + +#include +#include + +std::string Sampler::Serialize() const noexcept { return SerializeSampler(*this); } +Sampler Sampler::Deserialize(const std::string_view data) noexcept { return DeserializeSampler(data).deserializedSampler; } + +Sampler Sampler::Copy(const Sampler& sampler) noexcept { return sampler; } \ No newline at end of file diff --git a/Engine/Sampler.hpp b/Engine/Sampler.hpp new file mode 100644 index 00000000..95c7d5b3 --- /dev/null +++ b/Engine/Sampler.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +struct Sampler { + std::string Name{}; + TextureMinificationMode MinFilter{ TextureMinificationMode::NearestMipmapLinear }; + TextureMagnificationMode MagFilter{ TextureMagnificationMode::Linear }; + float MinLOD{ -1000.0f }; + float MaxLOD{ 1000.0f }; + float LODBias{ 0.0f }; + TextureWrappingMode WrapS{ TextureWrappingMode::Repeat }; + TextureWrappingMode WrapT{ TextureWrappingMode::Repeat }; + TextureWrappingMode WrapR{ TextureWrappingMode::Repeat }; + Color BorderColor{ 0.0f, 0.0f, 0.0f, 0.0f }; + TextureCompareMode CompareMode{ TextureCompareMode::None }; + TextureCompareFunction CompareFunction{ TextureCompareFunction::LessEqual }; + + float MaxAnisotropy{ 1.0f }; + bool CubemapSeamless{ false }; + + std::string Serialize() const noexcept; + static Sampler Deserialize(const std::string_view data) noexcept; + + static Sampler Copy(const Sampler& sampler) noexcept; +}; \ No newline at end of file diff --git a/Engine/SamplerDeserializer.cpp b/Engine/SamplerDeserializer.cpp new file mode 100644 index 00000000..665a6711 --- /dev/null +++ b/Engine/SamplerDeserializer.cpp @@ -0,0 +1,345 @@ +#include + +#include + +#include + +#include +#include + +SamplerDeserializationResult DeserializeSampler(const FNode& file) noexcept { + file.ReadContent(); + auto rv = DeserializeSampler(file.DisposeContent()); + if (!rv.erred) { + rv.deserializedSampler.Name = file.Name(); + } + return rv; +} + +SamplerDeserializationResult DeserializeSampler(const std::string_view data) noexcept { + SamplerDeserializationResult rv; + + tinyxml2::XMLDocument doc; + tinyxml2::XMLError err = doc.Parse(data.data()); + if (err != tinyxml2::XML_SUCCESS) { + rv.erred = true; + rv.errors.emplace_back("Couldn't deserialize sampler!"); + rv.errors.emplace_back("This should normally never happen. If you didn't try to edit the file manually, please submit an issue."); + DOA_LOG_ERROR("Couldn't deserialize sampler!\n\n%s", data); + } else { + SamplerDeserializer::Deserialize(*doc.RootElement(), rv); + } + + return rv; +} + +void SamplerDeserializer::DefaultDeserialize(tinyxml2::XMLElement& rootElem, SamplerDeserializationResult& sdr) noexcept { + DeserializeName(rootElem, sdr); + DeserializeMinFilter(rootElem, sdr); + DeserializeMagFilter(rootElem, sdr); + DeserializeMinLOD(rootElem, sdr); + DeserializeMaxLOD(rootElem, sdr); + DeserializeLODBias(rootElem, sdr); + DeserializeWrapS(rootElem, sdr); + DeserializeWrapT(rootElem, sdr); + DeserializeWrapR(rootElem, sdr); + DeserializeBorderColor(rootElem, sdr); + DeserializeCompareMode(rootElem, sdr); + DeserializeCompareFunction(rootElem, sdr); + DeserializeMaxAnisotropy(rootElem, sdr); + DeserializeCubemapSeamless(rootElem, sdr); +} +void SamplerDeserializer::DefaultDeserializeName(tinyxml2::XMLElement& samplerElem, SamplerDeserializationResult& sdr) noexcept { + std::string attribName = nameof(Sampler::Name); + auto* attribPtr = samplerElem.FindAttribute(attribName.c_str()); + if (!attribPtr) { + sdr.errors.emplace_back(std::format("Error while deserializing sampler, {} attrib missing.", attribName)); + sdr.erred = true; + return; + } + + sdr.deserializedSampler.Name = attribPtr->Value(); +} +void SamplerDeserializer::DefaultDeserializeMinFilter(tinyxml2::XMLElement& samplerElem, SamplerDeserializationResult& sdr) noexcept { + std::string attribName = nameof(Sampler::MinFilter); + auto* attribPtr = samplerElem.FindAttribute(attribName.c_str()); + if (!attribPtr) { + sdr.errors.emplace_back(std::format("Error while deserializing sampler, {} attrib missing.", attribName)); + sdr.erred = true; + return; + } + + std::optional minMode = Helpers::ToTextureMinificationMode(attribPtr->Value()); + if (!minMode.has_value()) { + sdr.errors.emplace_back(std::format("Error while deserializing sampler, {} attrib has corrupt data.", attribName)); + sdr.erred = true; + return; + } + + sdr.deserializedSampler.MinFilter = minMode.value(); +} +void SamplerDeserializer::DefaultDeserializeMagFilter(tinyxml2::XMLElement& samplerElem, SamplerDeserializationResult& sdr) noexcept { + std::string attribName = nameof(Sampler::MagFilter); + auto* attribPtr = samplerElem.FindAttribute(attribName.c_str()); + if (!attribPtr) { + sdr.errors.emplace_back(std::format("Error while deserializing sampler, {} attrib missing.", attribName)); + sdr.erred = true; + return; + } + + std::optional magMode = Helpers::ToTextureMagnificationMode(attribPtr->Value()); + if (!magMode.has_value()) { + sdr.errors.emplace_back(std::format("Error while deserializing sampler, {} attrib has corrupt data.", attribName)); + sdr.erred = true; + return; + } + + sdr.deserializedSampler.MagFilter = magMode.value(); +} +void SamplerDeserializer::DefaultDeserializeMinLOD(tinyxml2::XMLElement& samplerElem, SamplerDeserializationResult& sdr) noexcept { + std::string attribName = nameof(Sampler::MinLOD); + auto* attribPtr = samplerElem.FindAttribute(attribName.c_str()); + if (!attribPtr) { + sdr.errors.emplace_back(std::format("Error while deserializing sampler, {} attrib missing.", attribName)); + sdr.erred = true; + return; + } + + float minLOD; + if (attribPtr->QueryFloatValue(&minLOD) == tinyxml2::XML_WRONG_ATTRIBUTE_TYPE) { + sdr.errors.emplace_back(std::format("Error while deserializing sampler, {} attrib has corrupt data.", attribName)); + sdr.erred = true; + return; + } + + sdr.deserializedSampler.MinLOD = minLOD; +} +void SamplerDeserializer::DefaultDeserializeMaxLOD(tinyxml2::XMLElement& samplerElem, SamplerDeserializationResult& sdr) noexcept { + std::string attribName = nameof(Sampler::MaxLOD); + auto* attribPtr = samplerElem.FindAttribute(attribName.c_str()); + if (!attribPtr) { + sdr.errors.emplace_back(std::format("Error while deserializing sampler, {} attrib missing.", attribName)); + sdr.erred = true; + return; + } + + float maxLOD; + if (attribPtr->QueryFloatValue(&maxLOD) == tinyxml2::XML_WRONG_ATTRIBUTE_TYPE) { + sdr.errors.emplace_back(std::format("Error while deserializing sampler, {} attrib has corrupt data.", attribName)); + sdr.erred = true; + return; + } + + sdr.deserializedSampler.MaxLOD = maxLOD; +} +void SamplerDeserializer::DefaultDeserializeLODBias(tinyxml2::XMLElement& samplerElem, SamplerDeserializationResult& sdr) noexcept { + std::string attribName = nameof(Sampler::LODBias); + auto* attribPtr = samplerElem.FindAttribute(attribName.c_str()); + if (!attribPtr) { + sdr.errors.emplace_back(std::format("Error while deserializing sampler, {} attrib missing.", attribName)); + sdr.erred = true; + return; + } + + float LODBias; + if (attribPtr->QueryFloatValue(&LODBias) == tinyxml2::XML_WRONG_ATTRIBUTE_TYPE) { + sdr.errors.emplace_back(std::format("Error while deserializing sampler, {} attrib has corrupt data.", attribName)); + sdr.erred = true; + return; + } + + sdr.deserializedSampler.LODBias = LODBias; +} +void SamplerDeserializer::DefaultDeserializeWrapS(tinyxml2::XMLElement& samplerElem, SamplerDeserializationResult& sdr) noexcept { + std::string attribName = nameof(Sampler::WrapS); + auto* attribPtr = samplerElem.FindAttribute(attribName.c_str()); + if (!attribPtr) { + sdr.errors.emplace_back(std::format("Error while deserializing sampler, {} attrib missing.", attribName)); + sdr.erred = true; + return; + } + + std::optional wrapMode = Helpers::ToTextureWrappingMode(attribPtr->Value()); + if (!wrapMode.has_value()) { + sdr.errors.emplace_back(std::format("Error while deserializing sampler, {} attrib has corrupt data.", attribName)); + sdr.erred = true; + return; + } + + sdr.deserializedSampler.WrapS = wrapMode.value(); +} +void SamplerDeserializer::DefaultDeserializeWrapT(tinyxml2::XMLElement& samplerElem, SamplerDeserializationResult& sdr) noexcept { + std::string attribName = nameof(Sampler::WrapT); + auto* attribPtr = samplerElem.FindAttribute(attribName.c_str()); + if (!attribPtr) { + sdr.errors.emplace_back(std::format("Error while deserializing sampler, {} attrib missing.", attribName)); + sdr.erred = true; + return; + } + + std::optional wrapMode = Helpers::ToTextureWrappingMode(attribPtr->Value()); + if (!wrapMode.has_value()) { + sdr.errors.emplace_back(std::format("Error while deserializing sampler, {} attrib has corrupt data.", attribName)); + sdr.erred = true; + return; + } + + sdr.deserializedSampler.WrapT = wrapMode.value(); +} +void SamplerDeserializer::DefaultDeserializeWrapR(tinyxml2::XMLElement& samplerElem, SamplerDeserializationResult& sdr) noexcept { + std::string attribName = nameof(Sampler::WrapR); + auto* attribPtr = samplerElem.FindAttribute(attribName.c_str()); + if (!attribPtr) { + sdr.errors.emplace_back(std::format("Error while deserializing sampler, {} attrib missing.", attribName)); + sdr.erred = true; + return; + } + + std::optional wrapMode = Helpers::ToTextureWrappingMode(attribPtr->Value()); + if (!wrapMode.has_value()) { + sdr.errors.emplace_back(std::format("Error while deserializing sampler, {} attrib has corrupt data.", attribName)); + sdr.erred = true; + return; + } + + sdr.deserializedSampler.WrapR = wrapMode.value(); +} +void SamplerDeserializer::DefaultDeserializeBorderColor(tinyxml2::XMLElement& samplerElem, SamplerDeserializationResult& sdr) noexcept { + std::string attribName = nameof(Sampler::BorderColor); + auto* attribPtr = samplerElem.FindAttribute(attribName.c_str()); + if (!attribPtr) { + sdr.errors.emplace_back(std::format("Error while deserializing sampler, {} attrib missing.", attribName)); + sdr.erred = true; + return; + } + + static const std::regex regex("Color\\[(\\d*\\.?\\d*)\\, (\\d*\\.?\\d*)\\, (\\d*\\.?\\d*)\\, (\\d*\\.?\\d*)\\]"); + std::smatch match; + + std::string colorString = attribPtr->Value(); + if (!std::regex_match(colorString, match, regex) || match.size() != 5) { + sdr.errors.emplace_back(std::format("Error while deserializing sampler, {} attrib has corrupt data. Regex doesn't match", attribName)); + sdr.erred = true; + return; + } + sdr.deserializedSampler.BorderColor.r = std::stof(match[1]); + sdr.deserializedSampler.BorderColor.g = std::stof(match[2]); + sdr.deserializedSampler.BorderColor.b = std::stof(match[3]); + sdr.deserializedSampler.BorderColor.a = std::stof(match[4]); +} +void SamplerDeserializer::DefaultDeserializeCompareMode(tinyxml2::XMLElement& samplerElem, SamplerDeserializationResult& sdr) noexcept { + std::string attribName = nameof(Sampler::CompareMode); + auto* attribPtr = samplerElem.FindAttribute(attribName.c_str()); + if (!attribPtr) { + sdr.errors.emplace_back(std::format("Error while deserializing sampler, {} attrib missing.", attribName)); + sdr.erred = true; + return; + } + + std::optional compMode = Helpers::ToTextureCompareMode(attribPtr->Value()); + if (!compMode.has_value()) { + sdr.errors.emplace_back(std::format("Error while deserializing sampler, {} attrib has corrupt data.", attribName)); + sdr.erred = true; + return; + } + + sdr.deserializedSampler.CompareMode = compMode.value(); +} +void SamplerDeserializer::DefaultDeserializeCompareFunction(tinyxml2::XMLElement& samplerElem, SamplerDeserializationResult& sdr) noexcept { + std::string attribName = nameof(Sampler::CompareFunction); + auto* attribPtr = samplerElem.FindAttribute(attribName.c_str()); + if (!attribPtr) { + sdr.errors.emplace_back(std::format("Error while deserializing sampler, {} attrib missing.", attribName)); + sdr.erred = true; + return; + } + + std::optional compFunc = Helpers::ToTextureCompareFunction(attribPtr->Value()); + if (!compFunc.has_value()) { + sdr.errors.emplace_back(std::format("Error while deserializing sampler, {} attrib has corrupt data.", attribName)); + sdr.erred = true; + return; + } + + sdr.deserializedSampler.CompareFunction = compFunc.value(); +} +void SamplerDeserializer::DefaultDeserializeMaxAnisotropy(tinyxml2::XMLElement& samplerElem, SamplerDeserializationResult& sdr) noexcept { + std::string attribName = nameof(Sampler::MaxAnisotropy); + auto* attribPtr = samplerElem.FindAttribute(attribName.c_str()); + if (!attribPtr) { + sdr.errors.emplace_back(std::format("Error while deserializing sampler, {} attrib missing.", attribName)); + sdr.erred = true; + return; + } + + float maxAnisotropy; + if (attribPtr->QueryFloatValue(&maxAnisotropy) == tinyxml2::XML_WRONG_ATTRIBUTE_TYPE) { + sdr.errors.emplace_back(std::format("Error while deserializing sampler, {} attrib has corrupt data.", attribName)); + sdr.erred = true; + return; + } + + sdr.deserializedSampler.MaxAnisotropy = maxAnisotropy; +} +void SamplerDeserializer::DefaultDeserializeCubemapSeamless(tinyxml2::XMLElement& samplerElem, SamplerDeserializationResult& sdr) noexcept { + std::string attribName = nameof(Sampler::CubemapSeamless); + auto* attribPtr = samplerElem.FindAttribute(attribName.c_str()); + if (!attribPtr) { + sdr.errors.emplace_back(std::format("Error while deserializing sampler, {} attrib missing.", attribName)); + sdr.erred = true; + return; + } + + bool seamless; + if (attribPtr->QueryBoolValue(&seamless) == tinyxml2::XML_WRONG_ATTRIBUTE_TYPE) { + sdr.errors.emplace_back(std::format("Error while deserializing sampler, {} attrib has corrupt data.", attribName)); + sdr.erred = true; + return; + } + + sdr.deserializedSampler.CubemapSeamless = seamless; +} + +std::optional SamplerDeserializer::Helpers::ToTextureMinificationMode(const std::string_view mode) noexcept { + using enum TextureMinificationMode; + if (mode == nameof(Nearest)) { return Nearest; } + if (mode == nameof(Linear)) { return Linear; } + if (mode == nameof(NearestMipmapNearest)) { return NearestMipmapNearest; } + if (mode == nameof(LinearMipmapNearest)) { return LinearMipmapNearest; } + if (mode == nameof(NearestMipmapLinear)) { return NearestMipmapLinear; } + if (mode == nameof(LinearMipmapLinear)) { return LinearMipmapLinear; } + return std::nullopt; +} +std::optional < TextureMagnificationMode> SamplerDeserializer::Helpers::ToTextureMagnificationMode(const std::string_view mode) noexcept { + using enum TextureMagnificationMode; + if (mode == nameof(Nearest)) { return Nearest; } + if (mode == nameof(Linear)) { return Linear; } + return std::nullopt; +} +std::optional SamplerDeserializer::Helpers::ToTextureWrappingMode(const std::string_view mode) noexcept { + using enum TextureWrappingMode; + if (mode == nameof(Repeat)) { return Repeat; } + if (mode == nameof(MirroredRepeat)) { return MirroredRepeat; } + if (mode == nameof(ClampToEdge)) { return ClampToEdge; } + if (mode == nameof(MirrorClampToEdge)) { return MirrorClampToEdge; } + if (mode == nameof(ClampToBorder)) { return ClampToBorder; } + return std::nullopt; +} +std::optional SamplerDeserializer::Helpers::ToTextureCompareMode(const std::string_view mode) noexcept { + using enum TextureCompareMode; + if (mode == nameof(CompareRefToTexture)) { return CompareRefToTexture; } + if (mode == nameof(None)) { return None; } + return std::nullopt; +} +std::optional SamplerDeserializer::Helpers::ToTextureCompareFunction(const std::string_view function) noexcept { + using enum TextureCompareFunction; + if(function == nameof(LessEqual)) { return LessEqual; } + if(function == nameof(GreaterEqual)) { return GreaterEqual; } + if(function == nameof(Less)) { return Less; } + if(function == nameof(Greater)) { return Greater; } + if(function == nameof(Equal)) { return Equal; } + if(function == nameof(NotEqual)) { return NotEqual; } + if(function == nameof(Always)) { return Always; } + if(function == nameof(Never)) { return Never; } + return std::nullopt; +} \ No newline at end of file diff --git a/Engine/SamplerDeserializer.hpp b/Engine/SamplerDeserializer.hpp new file mode 100644 index 00000000..443a4d17 --- /dev/null +++ b/Engine/SamplerDeserializer.hpp @@ -0,0 +1,86 @@ +#pragma once + +#include +#include +#include + +#include + +#include + +struct FNode; + +struct SamplerDeserializationResult { + bool erred{ false }; + std::vector errors{}; + Sampler deserializedSampler{}; +}; + +/* This is the big bad bada-boom of deserializers, this function will call appropriate deserializers. See below for an overview! */ +SamplerDeserializationResult DeserializeSampler(const FNode& file) noexcept; +SamplerDeserializationResult DeserializeSampler(const std::string_view data) noexcept; + +/* Here is the general overview */ +namespace SamplerDeserializer { + + /* ------- Type Definitons ------- */ + using DeserializeFunction = std::function; + using DeserializeNameFunction = std::function; + using DeserializeMinFilterFunction = std::function; + using DeserializeMagFilterFunction = std::function; + using DeserializeMinLODFunction = std::function; + using DeserializeMaxLODFunction = std::function; + using DeserializeLODBiasFunction = std::function; + using DeserializeWrapSFunction = std::function; + using DeserializeWrapTFunction = std::function; + using DeserializeWrapRFunction = std::function; + using DeserializeBorderColorFunction = std::function; + using DeserializeCompareModeFunction = std::function; + using DeserializeCompareFunctionFunction = std::function; + using DeserializeMaxAnisotropyFunction = std::function; + using DeserializeCubemapSeamlessFunction = std::function; + + /* --- Default Implementations --- */ + /* These are how NeoDoa will desrialize by default. */ + void DefaultDeserialize(tinyxml2::XMLElement& rootElem, SamplerDeserializationResult& sdr) noexcept; + void DefaultDeserializeName(tinyxml2::XMLElement& samplerElem, SamplerDeserializationResult& sdr) noexcept; + void DefaultDeserializeMinFilter(tinyxml2::XMLElement& samplerElem, SamplerDeserializationResult& sdr) noexcept; + void DefaultDeserializeMagFilter(tinyxml2::XMLElement& samplerElem, SamplerDeserializationResult& sdr) noexcept; + void DefaultDeserializeMinLOD(tinyxml2::XMLElement& samplerElem, SamplerDeserializationResult& sdr) noexcept; + void DefaultDeserializeMaxLOD(tinyxml2::XMLElement& samplerElem, SamplerDeserializationResult& sdr) noexcept; + void DefaultDeserializeLODBias(tinyxml2::XMLElement& samplerElem, SamplerDeserializationResult& sdr) noexcept; + void DefaultDeserializeWrapS(tinyxml2::XMLElement& samplerElem, SamplerDeserializationResult& sdr) noexcept; + void DefaultDeserializeWrapT(tinyxml2::XMLElement& samplerElem, SamplerDeserializationResult& sdr) noexcept; + void DefaultDeserializeWrapR(tinyxml2::XMLElement& samplerElem, SamplerDeserializationResult& sdr) noexcept; + void DefaultDeserializeBorderColor(tinyxml2::XMLElement& samplerElem, SamplerDeserializationResult& sdr) noexcept; + void DefaultDeserializeCompareMode(tinyxml2::XMLElement& samplerElem, SamplerDeserializationResult& sdr) noexcept; + void DefaultDeserializeCompareFunction(tinyxml2::XMLElement& samplerElem, SamplerDeserializationResult& sdr) noexcept; + void DefaultDeserializeMaxAnisotropy(tinyxml2::XMLElement& samplerElem, SamplerDeserializationResult& sdr) noexcept; + void DefaultDeserializeCubemapSeamless(tinyxml2::XMLElement& samplerElem, SamplerDeserializationResult& sdr) noexcept; + + /* ----- Serializer Functions ----- */ + inline DeserializeFunction Deserialize{ DefaultDeserialize }; /* Feel free to assign this your own function, if you need custom deserialization */ + inline DeserializeNameFunction DeserializeName{ DefaultDeserializeName }; /* Feel free to assign this your own function, if you need custom deserialization */ + inline DeserializeMinFilterFunction DeserializeMinFilter{ DefaultDeserializeMinFilter }; /* Feel free to assign this your own function, if you need custom deserialization */ + inline DeserializeMagFilterFunction DeserializeMagFilter{ DefaultDeserializeMagFilter }; /* Feel free to assign this your own function, if you need custom deserialization */ + inline DeserializeMinLODFunction DeserializeMinLOD{ DefaultDeserializeMinLOD }; /* Feel free to assign this your own function, if you need custom deserialization */ + inline DeserializeMaxLODFunction DeserializeMaxLOD{ DefaultDeserializeMaxLOD }; /* Feel free to assign this your own function, if you need custom deserialization */ + inline DeserializeLODBiasFunction DeserializeLODBias{ DefaultDeserializeLODBias }; /* Feel free to assign this your own function, if you need custom deserialization */ + inline DeserializeWrapSFunction DeserializeWrapS{ DefaultDeserializeWrapS }; /* Feel free to assign this your own function, if you need custom deserialization */ + inline DeserializeWrapTFunction DeserializeWrapT{ DefaultDeserializeWrapT }; /* Feel free to assign this your own function, if you need custom deserialization */ + inline DeserializeWrapRFunction DeserializeWrapR{ DefaultDeserializeWrapR }; /* Feel free to assign this your own function, if you need custom deserialization */ + inline DeserializeBorderColorFunction DeserializeBorderColor{ DefaultDeserializeBorderColor }; /* Feel free to assign this your own function, if you need custom deserialization */ + inline DeserializeCompareModeFunction DeserializeCompareMode{ DefaultDeserializeCompareMode }; /* Feel free to assign this your own function, if you need custom deserialization */ + inline DeserializeCompareFunctionFunction DeserializeCompareFunction{ DefaultDeserializeCompareFunction }; /* Feel free to assign this your own function, if you need custom deserialization */ + inline DeserializeMaxAnisotropyFunction DeserializeMaxAnisotropy{ DefaultDeserializeMaxAnisotropy }; /* Feel free to assign this your own function, if you need custom deserialization */ + inline DeserializeCubemapSeamlessFunction DeserializeCubemapSeamless{ DefaultDeserializeCubemapSeamless }; /* Feel free to assign this your own function, if you need custom deserialization */ +} + +/* Some Helper functions to trivially serialize (not-so) trivial data types */ +namespace SamplerDeserializer::Helpers { + std::optional ToTextureMinificationMode(const std::string_view mode) noexcept; + std::optional ToTextureMagnificationMode(const std::string_view mode) noexcept; + std::optional ToTextureWrappingMode(const std::string_view mode) noexcept; + std::optional ToTextureCompareMode(const std::string_view mode) noexcept; + std::optional ToTextureCompareFunction(const std::string_view function) noexcept; +} \ No newline at end of file diff --git a/Engine/SamplerSerializer.cpp b/Engine/SamplerSerializer.cpp new file mode 100644 index 00000000..f6ad5cad --- /dev/null +++ b/Engine/SamplerSerializer.cpp @@ -0,0 +1,133 @@ +#include + +#include + +#include + +std::string SerializeSampler(const Sampler& sampler) noexcept { + tinyxml2::XMLPrinter printer; + SamplerSerializer::HeaderComment(printer, sampler); + SamplerSerializer::Serialize(printer, sampler); + + return printer.CStr(); +} + +void SamplerSerializer::DefaultHeaderComment(tinyxml2::XMLPrinter& printer, [[maybe_unused]] const Sampler& sampler) noexcept { + printer.PushComment("WARNING!! This file is not for editing! Don't!"); +} +void SamplerSerializer::DefaultSerialize(tinyxml2::XMLPrinter& printer, const Sampler& sampler) noexcept { + printer.OpenElement("sampler"); + { + SerializeName(printer, sampler.Name); + SerializeMinFilter(printer, sampler.MinFilter); + SerializeMagFilter(printer, sampler.MagFilter); + SerializeMinLOD(printer, sampler.MinLOD); + SerializeMaxLOD(printer, sampler.MaxLOD); + SerializeLODBias(printer, sampler.LODBias); + SerializeWrapS(printer, sampler.WrapS); + SerializeWrapT(printer, sampler.WrapT); + SerializeWrapR(printer, sampler.WrapR); + SerializeBorderColor(printer, sampler.BorderColor); + SerializeCompareMode(printer, sampler.CompareMode); + SerializeCompareFunction(printer, sampler.CompareFunction); + SerializeMaxAnisotropy(printer, sampler.MaxAnisotropy); + SerializeCubemapSeamless(printer, sampler.CubemapSeamless); + } + printer.CloseElement(); +} +void SamplerSerializer::DefaultSerializeName(tinyxml2::XMLPrinter& printer, const std::string_view name) noexcept { + printer.PushAttribute(nameof_c(Sampler::Name), name.data()); +} +void SamplerSerializer::DefaultSerializeMinFilter(tinyxml2::XMLPrinter& printer, const TextureMinificationMode mode) noexcept { + printer.PushAttribute(nameof_c(Sampler::MinFilter), Helpers::ToString(mode).c_str()); +} +void SamplerSerializer::DefaultSerializeMagFilter(tinyxml2::XMLPrinter& printer, const TextureMagnificationMode mode) noexcept { + printer.PushAttribute(nameof_c(Sampler::MagFilter), Helpers::ToString(mode).c_str()); +} +void SamplerSerializer::DefaultSerializeMinLOD(tinyxml2::XMLPrinter& printer, const float minLOD) noexcept { + printer.PushAttribute(nameof_c(Sampler::MinLOD), minLOD); +} +void SamplerSerializer::DefaultSerializeMaxLOD(tinyxml2::XMLPrinter& printer, const float maxLOD) noexcept { + printer.PushAttribute(nameof_c(Sampler::MaxLOD), maxLOD); +} +void SamplerSerializer::DefaultSerializeLODBias(tinyxml2::XMLPrinter& printer, const float LODBias) noexcept { + printer.PushAttribute(nameof_c(Sampler::LODBias), LODBias); +} +void SamplerSerializer::DefaultSerializeWrapS(tinyxml2::XMLPrinter& printer, const TextureWrappingMode wrapS) noexcept { + printer.PushAttribute(nameof_c(Sampler::WrapS), Helpers::ToString(wrapS).c_str()); +} +void SamplerSerializer::DefaultSerializeWrapT(tinyxml2::XMLPrinter& printer, const TextureWrappingMode wrapT) noexcept { + printer.PushAttribute(nameof_c(Sampler::WrapT), Helpers::ToString(wrapT).c_str()); +} +void SamplerSerializer::DefaultSerializeWrapR(tinyxml2::XMLPrinter& printer, const TextureWrappingMode wrapR) noexcept { + printer.PushAttribute(nameof_c(Sampler::WrapR), Helpers::ToString(wrapR).c_str()); +} +void SamplerSerializer::DefaultSerializeBorderColor(tinyxml2::XMLPrinter& printer, const Color& borderColor) noexcept { + printer.PushAttribute(nameof_c(Sampler::BorderColor), borderColor.ToString().c_str()); +} +void SamplerSerializer::DefaultSerializeCompareMode(tinyxml2::XMLPrinter& printer, const TextureCompareMode mode) noexcept { + printer.PushAttribute(nameof_c(Sampler::CompareMode), Helpers::ToString(mode).c_str()); +} +void SamplerSerializer::DefaultSerializeCompareFunction(tinyxml2::XMLPrinter& printer, const TextureCompareFunction function) noexcept { + printer.PushAttribute(nameof_c(Sampler::CompareFunction), Helpers::ToString(function).c_str()); +} +void SamplerSerializer::DefaultSerializeMaxAnisotropy(tinyxml2::XMLPrinter& printer, const float maxAniso) noexcept { + printer.PushAttribute(nameof_c(Sampler::MaxAnisotropy), maxAniso); +} +void SamplerSerializer::DefaultSerializeCubemapSeamless(tinyxml2::XMLPrinter& printer, const bool seamless) noexcept { + printer.PushAttribute(nameof_c(Sampler::CubemapSeamless), seamless); +} + +std::string SamplerSerializer::Helpers::ToString(const TextureMinificationMode mode) noexcept { + switch(mode) { + using enum TextureMinificationMode; + case Nearest: return nameof(Nearest); + case Linear: return nameof(Linear); + case NearestMipmapNearest: return nameof(NearestMipmapNearest); + case LinearMipmapNearest: return nameof(LinearMipmapNearest); + case NearestMipmapLinear: return nameof(NearestMipmapLinear); + case LinearMipmapLinear: return nameof(LinearMipmapLinear); + } + std::unreachable(); +} +std::string SamplerSerializer::Helpers::ToString(const TextureMagnificationMode mode) noexcept { + switch(mode) { + using enum TextureMagnificationMode; + case Nearest: return nameof(Nearest); + case Linear: return nameof(Linear); + } + std::unreachable(); +} +std::string SamplerSerializer::Helpers::ToString(const TextureWrappingMode mode) noexcept { + switch(mode) { + using enum TextureWrappingMode; + case Repeat: return nameof(Repeat); + case MirroredRepeat: return nameof(MirroredRepeat); + case ClampToEdge: return nameof(ClampToEdge); + case MirrorClampToEdge: return nameof(MirrorClampToEdge); + case ClampToBorder: return nameof(ClampToBorder); + } + std::unreachable(); +} +std::string SamplerSerializer::Helpers::ToString(const TextureCompareMode mode) noexcept { + switch(mode) { + using enum TextureCompareMode; + case CompareRefToTexture: return nameof(CompareRefToTexture); + case None: return nameof(None); + } + std::unreachable(); +} +std::string SamplerSerializer::Helpers::ToString(const TextureCompareFunction function) noexcept { + switch(function) { + using enum TextureCompareFunction; + case LessEqual: return nameof(LessEqual); + case GreaterEqual: return nameof(GreaterEqual); + case Less: return nameof(Less); + case Greater: return nameof(Greater); + case Equal: return nameof(Equal); + case NotEqual: return nameof(NotEqual); + case Always: return nameof(Always); + case Never: return nameof(Never); + } + std::unreachable(); +} \ No newline at end of file diff --git a/Engine/SamplerSerializer.hpp b/Engine/SamplerSerializer.hpp new file mode 100644 index 00000000..93babc8d --- /dev/null +++ b/Engine/SamplerSerializer.hpp @@ -0,0 +1,79 @@ +#pragma once + +#include +#include + +#include + +#include + +/* This is the big bad bada-boom of serializers, this function will call appropriate serializers. See below for an overview! */ +std::string SerializeSampler(const Sampler& sampler) noexcept; + +/* Here is the general overview */ +namespace SamplerSerializer { + + /* ------- Type Definitons ------- */ + using HeaderCommentFunction = std::function; + using SerializeFunction = std::function; + using SerializeNameFunction = std::function; + using SerializeMinFilterFunction = std::function; + using SerializeMagFilterFunction = std::function; + using SerializeMinLODFunction = std::function; + using SerializeMaxLODFunction = std::function; + using SerializeLODBiasFunction = std::function; + using SerializeWrapSFunction = std::function; + using SerializeWrapTFunction = std::function; + using SerializeWrapRFunction = std::function; + using SerializeBorderColorFunction = std::function; + using SerializeCompareModeFunction = std::function; + using SerializeCompareFunctionFunction = std::function; + using SerializeMaxAnisotropyFunction = std::function; + using SerializeCubemapSeamlessFunction = std::function; + + /* --- Default Implementations --- */ + /* These are how NeoDoa will serialize by default. */ + void DefaultHeaderComment(tinyxml2::XMLPrinter& printer, [[maybe_unused]] const Sampler& sampler) noexcept; + void DefaultSerialize(tinyxml2::XMLPrinter& printer, const Sampler& sampler) noexcept; + void DefaultSerializeName(tinyxml2::XMLPrinter& printer, const std::string_view name) noexcept; + void DefaultSerializeMinFilter(tinyxml2::XMLPrinter& printer, const TextureMinificationMode mode) noexcept; + void DefaultSerializeMagFilter(tinyxml2::XMLPrinter& printer, const TextureMagnificationMode mode) noexcept; + void DefaultSerializeMinLOD(tinyxml2::XMLPrinter& printer, const float minLOD) noexcept; + void DefaultSerializeMaxLOD(tinyxml2::XMLPrinter& printer, const float maxLOD) noexcept; + void DefaultSerializeLODBias(tinyxml2::XMLPrinter& printer, const float LODBias) noexcept; + void DefaultSerializeWrapS(tinyxml2::XMLPrinter& printer, const TextureWrappingMode wrapS) noexcept; + void DefaultSerializeWrapT(tinyxml2::XMLPrinter& printer, const TextureWrappingMode wrapT) noexcept; + void DefaultSerializeWrapR(tinyxml2::XMLPrinter& printer, const TextureWrappingMode wrapR) noexcept; + void DefaultSerializeBorderColor(tinyxml2::XMLPrinter& printer, const Color& borderColor) noexcept; + void DefaultSerializeCompareMode(tinyxml2::XMLPrinter& printer, const TextureCompareMode mode) noexcept; + void DefaultSerializeCompareFunction(tinyxml2::XMLPrinter& printer, const TextureCompareFunction function) noexcept; + void DefaultSerializeMaxAnisotropy(tinyxml2::XMLPrinter& printer, const float maxAniso) noexcept; + void DefaultSerializeCubemapSeamless(tinyxml2::XMLPrinter& printer, const bool seamless) noexcept; + + /* ----- Serializer Functions ----- */ + inline HeaderCommentFunction HeaderComment{ DefaultHeaderComment }; /* Feel free to assign this your own function, if you need custom serialization */ + inline SerializeFunction Serialize{ DefaultSerialize }; /* Feel free to assign this your own function, if you need custom serialization */ + inline SerializeNameFunction SerializeName{ DefaultSerializeName }; /* Feel free to assign this your own function, if you need custom serialization */ + inline SerializeMinFilterFunction SerializeMinFilter{ DefaultSerializeMinFilter }; /* Feel free to assign this your own function, if you need custom serialization */ + inline SerializeMagFilterFunction SerializeMagFilter{ DefaultSerializeMagFilter }; /* Feel free to assign this your own function, if you need custom serialization */ + inline SerializeMinLODFunction SerializeMinLOD{ DefaultSerializeMinLOD }; /* Feel free to assign this your own function, if you need custom serialization */ + inline SerializeMaxLODFunction SerializeMaxLOD{ DefaultSerializeMaxLOD }; /* Feel free to assign this your own function, if you need custom serialization */ + inline SerializeLODBiasFunction SerializeLODBias{ DefaultSerializeLODBias }; /* Feel free to assign this your own function, if you need custom serialization */ + inline SerializeWrapSFunction SerializeWrapS{ DefaultSerializeWrapS }; /* Feel free to assign this your own function, if you need custom serialization */ + inline SerializeWrapTFunction SerializeWrapT{ DefaultSerializeWrapT }; /* Feel free to assign this your own function, if you need custom serialization */ + inline SerializeWrapRFunction SerializeWrapR{ DefaultSerializeWrapR }; /* Feel free to assign this your own function, if you need custom serialization */ + inline SerializeBorderColorFunction SerializeBorderColor{ DefaultSerializeBorderColor }; /* Feel free to assign this your own function, if you need custom serialization */ + inline SerializeCompareModeFunction SerializeCompareMode{ DefaultSerializeCompareMode }; /* Feel free to assign this your own function, if you need custom serialization */ + inline SerializeCompareFunctionFunction SerializeCompareFunction{ DefaultSerializeCompareFunction }; /* Feel free to assign this your own function, if you need custom serialization */ + inline SerializeMaxAnisotropyFunction SerializeMaxAnisotropy{ DefaultSerializeMaxAnisotropy }; /* Feel free to assign this your own function, if you need custom serialization */ + inline SerializeCubemapSeamlessFunction SerializeCubemapSeamless{ DefaultSerializeCubemapSeamless }; /* Feel free to assign this your own function, if you need custom serialization */ +} + +/* Some Helper functions to trivially serialize (not-so) trivial data types */ +namespace SamplerSerializer::Helpers { + std::string ToString(const TextureMinificationMode mode) noexcept; + std::string ToString(const TextureMagnificationMode mode) noexcept; + std::string ToString(const TextureWrappingMode mode) noexcept; + std::string ToString(const TextureCompareMode mode) noexcept; + std::string ToString(const TextureCompareFunction function) noexcept; +} \ No newline at end of file diff --git a/Engine/Scene.cpp b/Engine/Scene.cpp index 85daf3db..cce8449b 100644 --- a/Engine/Scene.cpp +++ b/Engine/Scene.cpp @@ -1,22 +1,19 @@ -#include "Scene.hpp" +#include #include -#include "Shader.hpp" -#include "Model.hpp" -#include "Renderer.hpp" +#include -#include "Core.hpp" - -#include "Log.hpp" -#include "Texture.hpp" -#include "TransformComponent.hpp" -#include "IDComponent.hpp" -#include "ParentComponent.hpp" -#include "CameraComponent.hpp" -#include "Project.hpp" -#include "SceneSerializer.hpp" -#include "SceneDeserializer.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include Scene& Scene::GetLoadedScene() { static auto& core = Core::GetCore(); @@ -28,8 +25,6 @@ Scene& Scene::GetLoadedScene() { Scene::Scene(std::string_view name) noexcept : Name(name) {} -Renderer::Stats Scene::GetRendererStats() const { return _renderer.stats; } - Entity Scene::CreateEntity(std::string name, uint32_t desiredID) { Entity entt; if (desiredID != EntityTo(NULL_ENTT)) { @@ -51,12 +46,24 @@ Entity Scene::CreateEntity(std::string name, uint32_t desiredID) { void Scene::DeleteEntity(Entity entt) { if (HasComponent(entt)) { - auto& parent = GetComponent(entt); + ParentComponent& parent = GetComponent(entt); for (const auto& child : parent.GetChildren()) { DeleteEntity(child); } } + if (HasComponent(entt)) { + const ChildComponent& child = GetComponent(entt); + assert(HasComponent(child.GetParent())); + ParentComponent& parent = GetComponent(child.GetParent()); + auto search = std::ranges::find(parent.GetChildren(), entt); + assert(search != parent.GetChildren().end()); + parent.GetChildren().erase(search); + if (parent.GetChildren().empty()) { + RemoveComponent(child.GetParent()); + } + } + _registry.destroy(entt); std::erase(_entities, entt); } @@ -72,78 +79,9 @@ Scene Scene::Deserialize(const std::string& data) { return DeserializeScene(data Scene Scene::Copy(const Scene& scene) { return scene.Deserialize(scene.Serialize()); } -void Scene::Update(float deltaTime) { +void Scene::ExecuteSystems(bool isPlaying, float deltaTime) { for (entt::poly& s : _systems) { s->Init(_registry); s->Execute(_registry, deltaTime); } -} -void Scene::Render() { - //auto& cam = GetActiveCamera(); - //cam.UpdateView(); - //cam.UpdateProjection(); - //cam.UpdateViewProjection(); - - _registry.view().each([](Entity entt, OrthoCameraComponent& camera) { - if (!camera.IsActiveAndRendering()) { return; } - camera.UpdateMatrices(); - auto& fbo = camera.GetFrameBuffer(); - fbo.Bind(); - fbo.ClearBuffers(); - DOA_LOG_INFO("ortho camera attached to entity %d", EntityTo(camera.GetEntity())); - //render stuff here - }); - - _registry.view().each([](Entity entt, PerspectiveCameraComponent& camera) { - if (!camera.IsActiveAndRendering()) { return; } - camera.UpdateMatrices(); - auto& fbo = camera.GetFrameBuffer(); - fbo.Bind(); - fbo.ClearBuffers(); - DOA_LOG_INFO("perspective camera attached to entity %d", EntityTo(camera.GetEntity())); - //render stuff here - }); - - /* - _registry.view().each([this, &angel](Entity entity, ScriptComponent& script) { - for (auto& module : script._modules) { - if (module._name == "ModelRenderer" && module._isActive) { - auto& modelRenderer = module.As(); - Model*& mdl = modelRenderer.Model(); - Shader*& shdr = modelRenderer.Shader(); - _renderer.Register( - shdr->weak_from_this(), - mdl->weak_from_this() - ); - - _renderer.Submit( - shdr->weak_from_this(), - mdl->weak_from_this(), - entity - ); - } - } - }); - _renderer.Render(_registry, _activeCamera); - */ - -#ifdef EDITOR - /* - // Find selected objects - std::vector> selecteds; - _registry.view().each([&](Entity entity, ScriptComponent& script) { - Transform& t = script["Transform"].As(); - if (t.Selected()) { - auto opt = script.TryGet("ModelRenderer"); - if(opt.has_value()) { - ModelRenderer& mr = opt.value().get().As(); - selecteds.push_back({ t, mr }); - } - } - }); - - // Outline them - _outlineRenderer.Render(selecteds, _activeCamera, SelectionOutlineColor); - */ -#endif } \ No newline at end of file diff --git a/Engine/Scene.hpp b/Engine/Scene.hpp index a54fb0bf..074057f5 100644 --- a/Engine/Scene.hpp +++ b/Engine/Scene.hpp @@ -4,8 +4,6 @@ #include #include -#include "Renderer.hpp" -#include "OutlineRenderer.hpp" #include "OrthoCamera.hpp" #include "PerspectiveCamera.hpp" #include "System.hpp" @@ -19,12 +17,9 @@ struct Scene { std::string Name; Color ClearColor{ 0.2f, 0.3f, 0.3f }; - Color SelectionOutlineColor{ 0.68f, 0.49f, 0 }; explicit Scene(std::string_view name = "New Scene") noexcept; - Renderer::Stats GetRendererStats() const; - // E - Entity Entity CreateEntity(std::string name = "", uint32_t desiredID = EntityTo(NULL_ENTT)); void DeleteEntity(Entity entt); @@ -97,13 +92,9 @@ struct Scene { static Scene Copy(const Scene& scene); - void Update(float delta); - void Render(); + void ExecuteSystems(bool isPlaying, float delta); private: - Renderer _renderer; - OutlineRenderer _outlineRenderer; - Registry _registry; std::vector _entities; std::vector> _systems; diff --git a/Engine/SceneDeserializer.cpp b/Engine/SceneDeserializer.cpp index 5acd3132..f549c707 100644 --- a/Engine/SceneDeserializer.cpp +++ b/Engine/SceneDeserializer.cpp @@ -23,7 +23,7 @@ Scene DeserializeScene(const std::string& data) { tinyxml2::XMLDocument doc; tinyxml2::XMLError err = doc.Parse(data.c_str()); if (err != tinyxml2::XML_SUCCESS) { - DOA_LOG_ERROR("Couldn't deserialize scene!\n\n%s", data); + DOA_LOG_ERROR("Couldn't deserialize scene!\n\n%s", data.c_str()); } Scene scene{}; @@ -39,8 +39,6 @@ void SceneDeserializer::DefaultDeserialize(tinyxml2::XMLElement& rootNode, Scene void SceneDeserializer::SceneConfig::DefaultDeserialize(tinyxml2::XMLElement& configNode, Scene& scene) { DeserializeName(configNode, scene); DeserializeClearColor(configNode, scene); - DeserializeSelectionOutlineColor(configNode, scene); - Cameras::Deserialize(*configNode.FirstChildElement("cameras"), scene); } void SceneDeserializer::SceneConfig::DefaultDeserializeName(tinyxml2::XMLElement& configNode, Scene& scene) { scene.Name = configNode.Attribute("name"); @@ -53,56 +51,6 @@ void SceneDeserializer::SceneConfig::DefaultDeserializeClearColor(tinyxml2::XMLE clearColor.FloatAttribute("b") }; } -void SceneDeserializer::SceneConfig::DefaultDeserializeSelectionOutlineColor(tinyxml2::XMLElement& configNode, Scene& scene) { - const tinyxml2::XMLElement& selectionOutlineColor = *configNode.FirstChildElement("selectionOutlineColor"); - scene.SelectionOutlineColor = { - selectionOutlineColor.FloatAttribute("r"), - selectionOutlineColor.FloatAttribute("g"), - selectionOutlineColor.FloatAttribute("b") - }; -} - -void SceneDeserializer::SceneConfig::Cameras::DefaultDeserialize(tinyxml2::XMLElement& camerasNode, Scene& scene) { - //DeserializeActiveCamera(*camerasNode.FirstChildElement("activeCamera"), scene); - //DeserializeOrthoCamera(*camerasNode.FirstChildElement("orthoCamera"), scene); - //DeserializePerspectiveCamera(*camerasNode.FirstChildElement("perspectiveCamera"), scene); -} -void SceneDeserializer::SceneConfig::Cameras::DefaultDeserializeActiveCamera(tinyxml2::XMLElement& activeCameraNode, Scene& scene) { - //std::string activeCamType = activeCameraNode.Attribute("type"); - - //if (activeCamType == "ortho") { - // scene.SwitchToOrtho(); - //} else if (activeCamType == "perspective") { - // scene.SwitchToPerspective(); - //} - //ACamera& activeCamera = scene.GetActiveCamera(); - - //{ - // const tinyxml2::XMLElement* eye = activeCameraNode.FirstChildElement("eye"); - // activeCamera.eye = { eye->FloatAttribute("x"), eye->FloatAttribute("y"), eye->FloatAttribute("z") }; - //} - //{ - // const tinyxml2::XMLElement* forward = activeCameraNode.FirstChildElement("forward"); - // activeCamera.forward = { forward->FloatAttribute("x"), forward->FloatAttribute("y"), forward->FloatAttribute("z") }; - //} - //{ - // const tinyxml2::XMLElement* up = activeCameraNode.FirstChildElement("up"); - // activeCamera.up = { up->FloatAttribute("x"), up->FloatAttribute("y"), up->FloatAttribute("z") }; - //} - //{ - // const tinyxml2::XMLElement* zoom = activeCameraNode.FirstChildElement("zoom"); - // activeCamera.zoom = { zoom->FloatAttribute("value") }; - //} -} -void SceneDeserializer::SceneConfig::Cameras::DefaultDeserializeOrthoCamera(tinyxml2::XMLElement& orthoCameraNode, Scene& scene) { - /*scene.SetOrtho(orthoCameraNode.FloatAttribute("left"), orthoCameraNode.FloatAttribute("right"), - orthoCameraNode.FloatAttribute("bottom"), orthoCameraNode.FloatAttribute("top"), - orthoCameraNode.FloatAttribute("near"), orthoCameraNode.FloatAttribute("far"));*/ -} -void SceneDeserializer::SceneConfig::Cameras::DefaultDeserializePerspectiveCamera(tinyxml2::XMLElement& perspectiveCameraNode, Scene& scene) { - /*scene.SetPerpective(perspectiveCameraNode.FloatAttribute("fov"), perspectiveCameraNode.FloatAttribute("aspect"), - perspectiveCameraNode.FloatAttribute("near"), perspectiveCameraNode.FloatAttribute("far"));*/ -} void SceneDeserializer::Entities::DefaultDeserialize(tinyxml2::XMLElement& entitiesNode, Scene& scene) { for (tinyxml2::XMLElement* entity = entitiesNode.FirstChildElement(); entity != nullptr; entity = entity->NextSiblingElement()) { @@ -123,6 +71,10 @@ void SceneDeserializer::Entities::DefaultDeserializeEntity(tinyxml2::XMLElement& DeserializeParentComponent(*component, scene, entt); } else if (name == nameof(ChildComponent)) { DeserializeChildComponent(*component, scene, entt); + } else if (name == nameof(OrthoCameraComponent)) { + DeserializeOrthoCameraComponent(*component, scene, entt); + } else if (name == nameof(PerspectiveCameraComponent)) { + DeserializePerspectiveCameraComponent(*component, scene, entt); } } else { DeserializeUserDefinedComponents(*component, scene, entt, name); @@ -137,8 +89,13 @@ void SceneDeserializer::Entities::DefaultDeserializeIDComponent(tinyxml2::XMLEle scene.RemoveComponent(id.GetEntity()); /* remove tr-cmp bevause we will deserialize one from file */ } void SceneDeserializer::Entities::DefaultDeserializeTransformComponent(tinyxml2::XMLElement& componentNode, Scene& scene, Entity entity) { - auto matrix = Helpers::DeserializeMat4(*componentNode.FirstChildElement(nameof_c(TransformComponent::localMatrix))); - TransformComponent transform = { entity, matrix }; + auto t = Helpers::DeserializeVec3(*componentNode.FirstChildElement(nameof_c(TransformComponent::localTranslation))); + auto r = Helpers::DeserializeQuat(*componentNode.FirstChildElement(nameof_c(TransformComponent::localRotation))); + auto s = Helpers::DeserializeVec3(*componentNode.FirstChildElement(nameof_c(TransformComponent::localScale))); + TransformComponent transform{ entity }; + transform.SetLocalTranslation(t); + transform.SetLocalRotation(r); + transform.SetLocalScale(s); scene.InsertComponent(transform.GetEntity(), std::move(transform)); } void SceneDeserializer::Entities::DefaultDeserializeParentComponent(tinyxml2::XMLElement& componentNode, Scene& scene, Entity entity) { @@ -158,31 +115,45 @@ void SceneDeserializer::Entities::DefaultDeserializeChildComponent(tinyxml2::XML ChildComponent children{ entity, parent }; scene.InsertComponent(children.GetEntity(), std::move(children)); } -void SceneDeserializer::Entities::DefaultDeserializeUserDefinedComponents(tinyxml2::XMLElement&, Scene&, Entity, const std::string& name) {} +void SceneDeserializer::Entities::DefaultDeserializeOrthoCameraComponent(tinyxml2::XMLElement& componentNode, Scene& scene, Entity entity) { + OrthoCamera camera { + Helpers::DeserializeFloat(*componentNode.FirstChildElement(nameof_c(OrthoCamera::LeftPlane))), + Helpers::DeserializeFloat(*componentNode.FirstChildElement(nameof_c(OrthoCamera::RightPlane))), + Helpers::DeserializeFloat(*componentNode.FirstChildElement(nameof_c(OrthoCamera::BottomPlane))), + Helpers::DeserializeFloat(*componentNode.FirstChildElement(nameof_c(OrthoCamera::TopPlane))), + Helpers::DeserializeFloat(*componentNode.FirstChildElement(nameof_c(OrthoCamera::NearPlane))), + Helpers::DeserializeFloat(*componentNode.FirstChildElement(nameof_c(OrthoCamera::FarPlane))), + }; + camera.Eye = Helpers::DeserializeVec3(*componentNode.FirstChildElement(nameof_c(OrthoCamera::Eye))); + camera.Forward = Helpers::DeserializeVec3(*componentNode.FirstChildElement(nameof_c(OrthoCamera::Forward))); + camera.Up = Helpers::DeserializeVec3(*componentNode.FirstChildElement(nameof_c(OrthoCamera::Up))); + camera.Zoom = Helpers::DeserializeFloat(*componentNode.FirstChildElement(nameof_c(OrthoCamera::Zoom))); + OrthoCameraComponent ortho{ entity, std::move(camera) }; + scene.InsertComponent(ortho.GetEntity(), std::move(ortho)); +} +void SceneDeserializer::Entities::DefaultDeserializePerspectiveCameraComponent(tinyxml2::XMLElement& componentNode, Scene& scene, Entity entity) { + PerspectiveCamera camera { + Helpers::DeserializeFloat(*componentNode.FirstChildElement(nameof_c(PerspectiveCamera::FOV))), + Helpers::DeserializeFloat(*componentNode.FirstChildElement(nameof_c(PerspectiveCamera::AspectRatio))), + Helpers::DeserializeFloat(*componentNode.FirstChildElement(nameof_c(PerspectiveCamera::NearPlane))), + Helpers::DeserializeFloat(*componentNode.FirstChildElement(nameof_c(PerspectiveCamera::FarPlane))), + }; + camera.Eye = Helpers::DeserializeVec3(*componentNode.FirstChildElement(nameof_c(PerspectiveCamera::Eye))); + camera.Forward = Helpers::DeserializeVec3(*componentNode.FirstChildElement(nameof_c(PerspectiveCamera::Forward))); + camera.Up = Helpers::DeserializeVec3(*componentNode.FirstChildElement(nameof_c(PerspectiveCamera::Up))); + camera.Zoom = Helpers::DeserializeFloat(*componentNode.FirstChildElement(nameof_c(PerspectiveCamera::Zoom))); -/* - int DeserializeEnum(const tinyxml2::XMLElement& property); - Entity DeserializeEntityID(const tinyxml2::XMLElement& property, std::string_view identifierOverride = "id"); - int8_t DeserializeInt8 (const tinyxml2::XMLElement& property); - int16_t DeserializeInt16 (const tinyxml2::XMLElement& property); - int32_t DeserializeInt32 (const tinyxml2::XMLElement& property); - int64_t DeserializeInt64 (const tinyxml2::XMLElement& property); - uint8_t DeserializeUInt8 (const tinyxml2::XMLElement& property); - uint16_t DeserializeUInt16(const tinyxml2::XMLElement& property); - uint32_t DeserializeUInt32(const tinyxml2::XMLElement& property); - uint64_t DeserializeUInt64(const tinyxml2::XMLElement& property); - int DeserializeInt(const tinyxml2::XMLElement& property); - float DeserializeFloat(const tinyxml2::XMLElement& property); - double DeserializeDouble(const tinyxml2::XMLElement& property); - bool DeserializeBool(const tinyxml2::XMLElement& property); -*/ + PerspectiveCameraComponent perspective{ entity, std::move(camera) }; + scene.InsertComponent(perspective.GetEntity(), std::move(perspective)); +} +void SceneDeserializer::Entities::DefaultDeserializeUserDefinedComponents([[maybe_unused]] tinyxml2::XMLElement& componentNode, [[maybe_unused]] Scene& scene, [[maybe_unused]] Entity entity, [[maybe_unused]] const std::string& name) {} int SceneDeserializer::Helpers::DeserializeEnum(const tinyxml2::XMLElement& property) { return property.IntAttribute("value"); } Entity SceneDeserializer::Helpers::DeserializeEntityID(const tinyxml2::XMLElement& property, std::string_view identifierOverride) { - return static_cast(property.UnsignedAttribute(identifierOverride.data(), -1)); + return static_cast(property.UnsignedAttribute(identifierOverride.data(), static_cast(NULL_ENTT))); } int8_t SceneDeserializer::Helpers::DeserializeInt8(const tinyxml2::XMLElement& property) { return static_cast(property.IntAttribute("value")); diff --git a/Engine/SceneDeserializer.hpp b/Engine/SceneDeserializer.hpp index 1247206e..dd93c242 100644 --- a/Engine/SceneDeserializer.hpp +++ b/Engine/SceneDeserializer.hpp @@ -26,22 +26,17 @@ namespace SceneDeserializer { using DeserializeFunction = std::function; using DeserializeNameFunction = std::function; using DeserializeClearColorFunction = std::function; - using DeserializeSelectionOutlineColorFunction = std::function; - namespace Cameras { - using DeserializeFunction = std::function; - using DeserializeActiveCameraFunction = std::function; - using DeserializeOrthoCameraFunction = std::function; - using DeserializePerspectiveCameraFunction = std::function; - } } namespace Entities { - using DeserializeFunction = std::function; - using DeserializeEntityFunction = std::function; - using DeserializeIDComponentFunction = std::function; - using DeserializeTransformComponentFunction = std::function; - using DeserializeParentComponentFunction = std::function; - using DeserializeChildComponentFunction = std::function; - using DeserializeUserDefinedComponentsFunction = std::function; + using DeserializeFunction = std::function; + using DeserializeEntityFunction = std::function; + using DeserializeIDComponentFunction = std::function; + using DeserializeTransformComponentFunction = std::function; + using DeserializeParentComponentFunction = std::function; + using DeserializeChildComponentFunction = std::function; + using DeserializeOrthoCameraComponentFunction = std::function; + using DeserializePerspectiveCameraComponentFunction = std::function; + using DeserializeUserDefinedComponentsFunction = std::function; } /* --- Default Implementations --- */ @@ -51,13 +46,6 @@ namespace SceneDeserializer { void DefaultDeserialize(tinyxml2::XMLElement& configNode, Scene& scene); void DefaultDeserializeName(tinyxml2::XMLElement& configNode, Scene& scene); void DefaultDeserializeClearColor(tinyxml2::XMLElement& configNode, Scene& scene); - void DefaultDeserializeSelectionOutlineColor(tinyxml2::XMLElement& configNode, Scene& scene); - namespace Cameras { - void DefaultDeserialize(tinyxml2::XMLElement& camerasNode, Scene& scene); - void DefaultDeserializeActiveCamera(tinyxml2::XMLElement& activeCameraNode, Scene& scene); - void DefaultDeserializeOrthoCamera(tinyxml2::XMLElement& orthoCameraNode, Scene& scene); - void DefaultDeserializePerspectiveCamera(tinyxml2::XMLElement& perspectiveCameraNode, Scene& scene); - } } namespace Entities { void DefaultDeserialize(tinyxml2::XMLElement& entitiesNode, Scene& scene); @@ -66,31 +54,28 @@ namespace SceneDeserializer { void DefaultDeserializeTransformComponent(tinyxml2::XMLElement& componentNode, Scene& scene, Entity entity); void DefaultDeserializeParentComponent(tinyxml2::XMLElement& componentNode, Scene& scene, Entity entity); void DefaultDeserializeChildComponent(tinyxml2::XMLElement& componentNode, Scene& scene, Entity entity); - void DefaultDeserializeUserDefinedComponents(tinyxml2::XMLElement&, Scene&, Entity, const std::string&); + void DefaultDeserializeOrthoCameraComponent(tinyxml2::XMLElement& componentNode, Scene& scene, Entity entity); + void DefaultDeserializePerspectiveCameraComponent(tinyxml2::XMLElement& componentNode, Scene& scene, Entity entity); + void DefaultDeserializeUserDefinedComponents(tinyxml2::XMLElement& componentNode, Scene& scene, Entity entity, const std::string& name); } /* ----- Deserializer Functions ----- */ - inline DeserializeFunction Deserialize{ DefaultDeserialize }; /* Feel free to assign this your own function, if you need custom serialization */ + inline DeserializeFunction Deserialize{ DefaultDeserialize }; /* Feel free to assign this your own function, if you need custom serialization */ namespace SceneConfig { - inline DeserializeFunction Deserialize{ DefaultDeserialize }; /* Feel free to assign this your own function, if you need custom serialization */ - inline DeserializeNameFunction DeserializeName{ DefaultDeserializeName }; /* Feel free to assign this your own function, if you need custom serialization */ - inline DeserializeClearColorFunction DeserializeClearColor{ DefaultDeserializeClearColor }; /* Feel free to assign this your own function, if you need custom serialization */ - inline DeserializeSelectionOutlineColorFunction DeserializeSelectionOutlineColor{ DefaultDeserializeSelectionOutlineColor }; /* Feel free to assign this your own function, if you need custom serialization */ - namespace Cameras { - inline DeserializeFunction Deserialize{ DefaultDeserialize }; /* Feel free to assign this your own function, if you need custom serialization */ - inline DeserializeActiveCameraFunction DeserializeActiveCamera{ DefaultDeserializeActiveCamera }; /* Feel free to assign this your own function, if you need custom serialization */ - inline DeserializeOrthoCameraFunction DeserializeOrthoCamera{ DefaultDeserializeOrthoCamera }; /* Feel free to assign this your own function, if you need custom serialization */ - inline DeserializePerspectiveCameraFunction DeserializePerspectiveCamera{ DefaultDeserializePerspectiveCamera }; /* Feel free to assign this your own function, if you need custom serialization */ - } + inline DeserializeFunction Deserialize{ DefaultDeserialize }; /* Feel free to assign this your own function, if you need custom serialization */ + inline DeserializeNameFunction DeserializeName{ DefaultDeserializeName }; /* Feel free to assign this your own function, if you need custom serialization */ + inline DeserializeClearColorFunction DeserializeClearColor{ DefaultDeserializeClearColor }; /* Feel free to assign this your own function, if you need custom serialization */ } namespace Entities { - inline DeserializeFunction Deserialize{ DefaultDeserialize }; /* Feel free to assign this your own function, if you need custom serialization */ - inline DeserializeEntityFunction DeserializeEntity{ DefaultDeserializeEntity }; /* Feel free to assign this your own function, if you need custom serialization */ - inline DeserializeIDComponentFunction DeserializeIDComponent{ DefaultDeserializeIDComponent }; /* Feel free to assign this your own function, if you need custom serialization */ - inline DeserializeTransformComponentFunction DeserializeTransformComponent{ DefaultDeserializeTransformComponent }; /* Feel free to assign this your own function, if you need custom serialization */ - inline DeserializeParentComponentFunction DeserializeParentComponent{ DefaultDeserializeParentComponent }; /* Feel free to assign this your own function, if you need custom serialization */ - inline DeserializeChildComponentFunction DeserializeChildComponent{ DefaultDeserializeChildComponent }; /* Feel free to assign this your own function, if you need custom serialization */ - inline DeserializeUserDefinedComponentsFunction DeserializeUserDefinedComponents{ DefaultDeserializeUserDefinedComponents }; /* Feel free to assign this your own function, if you need custom serialization */ + inline DeserializeFunction Deserialize{ DefaultDeserialize }; /* Feel free to assign this your own function, if you need custom serialization */ + inline DeserializeEntityFunction DeserializeEntity{ DefaultDeserializeEntity }; /* Feel free to assign this your own function, if you need custom serialization */ + inline DeserializeIDComponentFunction DeserializeIDComponent{ DefaultDeserializeIDComponent }; /* Feel free to assign this your own function, if you need custom serialization */ + inline DeserializeTransformComponentFunction DeserializeTransformComponent{ DefaultDeserializeTransformComponent }; /* Feel free to assign this your own function, if you need custom serialization */ + inline DeserializeParentComponentFunction DeserializeParentComponent{ DefaultDeserializeParentComponent }; /* Feel free to assign this your own function, if you need custom serialization */ + inline DeserializeChildComponentFunction DeserializeChildComponent{ DefaultDeserializeChildComponent }; /* Feel free to assign this your own function, if you need custom serialization */ + inline DeserializeOrthoCameraComponentFunction DeserializeOrthoCameraComponent{ DefaultDeserializeOrthoCameraComponent }; /* Feel free to assign this your own function, if you need custom serialization */ + inline DeserializePerspectiveCameraComponentFunction DeserializePerspectiveCameraComponent{ DefaultDeserializePerspectiveCameraComponent }; /* Feel free to assign this your own function, if you need custom serialization */ + inline DeserializeUserDefinedComponentsFunction DeserializeUserDefinedComponents{ DefaultDeserializeUserDefinedComponents }; /* Feel free to assign this your own function, if you need custom serialization */ } } diff --git a/Engine/SceneSerializer.cpp b/Engine/SceneSerializer.cpp index 1bd8b643..aa87090e 100644 --- a/Engine/SceneSerializer.cpp +++ b/Engine/SceneSerializer.cpp @@ -1,4 +1,4 @@ -#include "SceneSerializer.hpp" +#include #include @@ -34,8 +34,6 @@ void SceneSerializer::SceneConfig::DefaultSerialize(tinyxml2::XMLPrinter& printe { SerializeName(printer, scene.Name); SerializeClearColor(printer, scene.ClearColor); - SerializeSelectionOutlineColor(printer, scene.SelectionOutlineColor); - Cameras::Serialize(printer, scene); } printer.CloseElement(); // config close } @@ -49,82 +47,6 @@ void SceneSerializer::SceneConfig::DefaultSerializeClearColor(tinyxml2::XMLPrint printer.PushAttribute("b", color.b); printer.CloseElement(); } -void SceneSerializer::SceneConfig::DefaultSerializeSelectionOutlineColor(tinyxml2::XMLPrinter& printer, const Color& color){ - printer.OpenElement("selectionOutlineColor"); - printer.PushAttribute("r", color.r); - printer.PushAttribute("g", color.g); - printer.PushAttribute("b", color.b); - printer.CloseElement(); -} - -void SceneSerializer::SceneConfig::Cameras::DefaultSerialize(tinyxml2::XMLPrinter& printer, const Scene& scene) { - /*printer.OpenElement("cameras"); - { - SerializeActiveCamera(printer, scene); - SerializeOrthoCamera(printer, scene.GetOrtho()); - SerializePerspectiveCamera(printer, scene.GetPerspective()); - } - printer.CloseElement();*/ -} -void SceneSerializer::SceneConfig::Cameras::DefaultSerializeActiveCamera(tinyxml2::XMLPrinter& printer, const Scene& scene) { - //printer.OpenElement("activeCamera"); - //{ - // if (scene.IsOrtho()) { - // printer.PushAttribute("type", "ortho"); - // } else if (scene.IsPerspective()) { - // printer.PushAttribute("type", "perspective"); - // } else { - // assert(false); /* no camera? */ - // throw 1; - // } - - // const ACamera& activeCamera = scene.GetActiveCamera(); - // printer.OpenElement("eye"); - // printer.PushAttribute("x", activeCamera.eye.x); - // printer.PushAttribute("y", activeCamera.eye.y); - // printer.PushAttribute("z", activeCamera.eye.z); - // printer.CloseElement(); - - // printer.OpenElement("forward"); - // printer.PushAttribute("x", activeCamera.forward.x); - // printer.PushAttribute("y", activeCamera.forward.y); - // printer.PushAttribute("z", activeCamera.forward.z); - // printer.CloseElement(); - - // printer.OpenElement("up"); - // printer.PushAttribute("x", activeCamera.up.x); - // printer.PushAttribute("y", activeCamera.up.y); - // printer.PushAttribute("z", activeCamera.up.z); - // printer.CloseElement(); - - // printer.OpenElement("zoom"); - // printer.PushAttribute("value", activeCamera.zoom); - // printer.CloseElement(); - //} - //printer.CloseElement(); -} -void SceneSerializer::SceneConfig::Cameras::DefaultSerializeOrthoCamera(tinyxml2::XMLPrinter& printer, const OrthoCamera& camera) { - printer.OpenElement("orthoCamera"); - { - printer.PushAttribute("left", camera._left); - printer.PushAttribute("right", camera._right); - printer.PushAttribute("bottom", camera._bottom); - printer.PushAttribute("top", camera._top); - printer.PushAttribute("near", camera._near); - printer.PushAttribute("far", camera._far); - } - printer.CloseElement(); -} -void SceneSerializer::SceneConfig::Cameras::DefaultSerializePerspectiveCamera(tinyxml2::XMLPrinter& printer, const PerspectiveCamera& camera) { - printer.OpenElement("perspectiveCamera"); - { - printer.PushAttribute("fov", camera._fov); - printer.PushAttribute("aspect", camera._aspect); - printer.PushAttribute("near", camera._near); - printer.PushAttribute("far", camera._far); - } - printer.CloseElement(); -} void SceneSerializer::Entities::DefaultSerialize(tinyxml2::XMLPrinter& printer, const Scene& scene) { printer.OpenElement("entities"); @@ -146,6 +68,12 @@ void SceneSerializer::Entities::DefaultSerializeEntity(tinyxml2::XMLPrinter& pri if (scene.HasComponent(entity)) { SerializeChildComponent(printer, scene.GetComponent(entity)); } + if (scene.HasComponent(entity)) { + SerializeOrthoCameraComponent(printer, scene.GetComponent(entity)); + } + if (scene.HasComponent(entity)) { + SerializePerspectiveCameraComponent(printer, scene.GetComponent(entity)); + } SerializeUserDefinedComponents(printer, scene, entity); } printer.CloseElement(); @@ -173,9 +101,19 @@ void SceneSerializer::Entities::DefaultSerializeTransformComponent(tinyxml2::XML { printer.PushAttribute("name", nameof_c(TransformComponent)); - auto name = nameof(TransformComponent::localMatrix); + auto name = nameof(TransformComponent::localTranslation); + printer.OpenElement(name.data()); + SceneSerializer::Helpers::SerializeVec3(printer, component.GetLocalTranslation()); + printer.CloseElement(); + + name = nameof(TransformComponent::localRotation); printer.OpenElement(name.data()); - SceneSerializer::Helpers::SerializeMat4(printer, component.GetLocalMatrix()); + SceneSerializer::Helpers::SerializeQuat(printer, component.GetLocalRotation()); + printer.CloseElement(); + + name = nameof(TransformComponent::localScale); + printer.OpenElement(name.data()); + SceneSerializer::Helpers::SerializeVec3(printer, component.GetLocalScale()); printer.CloseElement(); } printer.CloseElement(); @@ -219,6 +157,32 @@ void SceneSerializer::Entities::DefaultSerializeChildComponent(tinyxml2::XMLPrin } printer.CloseElement(); } +void SceneSerializer::Entities::DefaultSerializeOrthoCameraComponent(tinyxml2::XMLPrinter& printer, const OrthoCameraComponent& component) { + printer.OpenElement("cpp-component"); + { + printer.PushAttribute("name", nameof_c(OrthoCameraComponent)); + + printer.PushAttribute(nameof_c(OrthoCameraComponent::LeftPlane), component.GetData().LeftPlane); + printer.PushAttribute(nameof_c(OrthoCameraComponent::RightPlane), component.GetData().RightPlane); + printer.PushAttribute(nameof_c(OrthoCameraComponent::BottomPlane), component.GetData().BottomPlane); + printer.PushAttribute(nameof_c(OrthoCameraComponent::TopPlane), component.GetData().TopPlane); + printer.PushAttribute(nameof_c(OrthoCameraComponent::NearPlane), component.GetData().NearPlane); + printer.PushAttribute(nameof_c(OrthoCameraComponent::FarPlane), component.GetData().FarPlane); + } + printer.CloseElement(); +} +void SceneSerializer::Entities::DefaultSerializePerspectiveCameraComponent(tinyxml2::XMLPrinter& printer, const PerspectiveCameraComponent& component) { + printer.OpenElement("cpp-component"); + { + printer.PushAttribute("name", nameof_c(PerspectiveCameraComponent)); + + printer.PushAttribute(nameof_c(OrthoCameraComponent::FOV), component.GetData().FOV); + printer.PushAttribute(nameof_c(OrthoCameraComponent::AspectRatio), component.GetData().AspectRatio); + printer.PushAttribute(nameof_c(OrthoCameraComponent::NearPlane), component.GetData().NearPlane); + printer.PushAttribute(nameof_c(OrthoCameraComponent::FarPlane), component.GetData().FarPlane); + } + printer.CloseElement(); +} void SceneSerializer::Entities::DefaultSerializeUserDefinedComponents(tinyxml2::XMLPrinter&, const Scene&, Entity) {} void SceneSerializer::Helpers::SerializeEnum(tinyxml2::XMLPrinter& printer, std::string_view name, const int value) { diff --git a/Engine/SceneSerializer.hpp b/Engine/SceneSerializer.hpp index 84cc6d4c..4427481b 100644 --- a/Engine/SceneSerializer.hpp +++ b/Engine/SceneSerializer.hpp @@ -8,13 +8,10 @@ #include -#include "Entity.hpp" +#include -struct Scene; struct Color; -struct ACamera; -struct OrthoCamera; -struct PerspectiveCamera; +struct Scene; struct IDComponent; struct TransformComponent; struct ParentComponent; @@ -35,22 +32,17 @@ namespace SceneSerializer { using SerializeFunction = std::function; using SerializeNameFunction = std::function; using SerializeClearColorFunction = std::function; - using SerializeSelectionOutlineColorFunction = std::function; - namespace Cameras { - using SerializeFunction = std::function; - using SerializeActiveCameraFunction = std::function; - using SerializeOrthoCameraFunction = std::function; - using SerializePerspectiveCameraFunction = std::function; - } } namespace Entities { - using SerializeFunction = std::function; - using SerializeEntityFunction = std::function; - using SerializeIDComponentFunction = std::function; - using SerializeTransformComponentFunction = std::function; - using SerializeParentComponentFunction = std::function; - using SerializeChildComponentFunction = std::function; - using SerializeUserDefinedComponentsFunction = std::function; + using SerializeFunction = std::function; + using SerializeEntityFunction = std::function; + using SerializeIDComponentFunction = std::function; + using SerializeTransformComponentFunction = std::function; + using SerializeParentComponentFunction = std::function; + using SerializeChildComponentFunction = std::function; + using SerializeOrthoCameraComponentFunction = std::function; + using SerializePerspectiveCameraComponentFunction = std::function; + using SerializeUserDefinedComponentsFunction = std::function; } /* --- Default Implementations --- */ @@ -61,13 +53,6 @@ namespace SceneSerializer { void DefaultSerialize(tinyxml2::XMLPrinter& printer, const Scene& scene); void DefaultSerializeName(tinyxml2::XMLPrinter& printer, ::std::string_view name); void DefaultSerializeClearColor(tinyxml2::XMLPrinter& printer, const Color& color); - void DefaultSerializeSelectionOutlineColor(tinyxml2::XMLPrinter& printer, const Color& color); - namespace Cameras { - void DefaultSerialize(tinyxml2::XMLPrinter& printer, const Scene& scene); - void DefaultSerializeActiveCamera(tinyxml2::XMLPrinter& printer, const Scene& scene); - void DefaultSerializeOrthoCamera(tinyxml2::XMLPrinter& printer, const OrthoCamera& camera); - void DefaultSerializePerspectiveCamera(tinyxml2::XMLPrinter& printer, const PerspectiveCamera& camera); - } } namespace Entities { void DefaultSerialize(tinyxml2::XMLPrinter& printer, const Scene& scene); @@ -76,32 +61,29 @@ namespace SceneSerializer { void DefaultSerializeTransformComponent(tinyxml2::XMLPrinter& printer, const TransformComponent& component); void DefaultSerializeParentComponent(tinyxml2::XMLPrinter& printer, const ParentComponent& component); void DefaultSerializeChildComponent(tinyxml2::XMLPrinter& printer, const ChildComponent& component); + void DefaultSerializeOrthoCameraComponent(tinyxml2::XMLPrinter& printer, const OrthoCameraComponent& component); + void DefaultSerializePerspectiveCameraComponent(tinyxml2::XMLPrinter& printer, const PerspectiveCameraComponent& component); void DefaultSerializeUserDefinedComponents(tinyxml2::XMLPrinter&, const Scene&, Entity); } /* ----- Serializer Functions ----- */ - inline HeaderCommentFunction HeaderComment{ DefaultHeaderComment }; /* Feel free to assign this your own function, if you need custom serialization */ - inline SerializeFunction Serialize{ DefaultSerialize }; /* Feel free to assign this your own function, if you need custom serialization */ + inline HeaderCommentFunction HeaderComment{ DefaultHeaderComment }; /* Feel free to assign this your own function, if you need custom serialization */ + inline SerializeFunction Serialize{ DefaultSerialize }; /* Feel free to assign this your own function, if you need custom serialization */ namespace SceneConfig { - inline SerializeFunction Serialize{ DefaultSerialize }; /* Feel free to assign this your own function, if you need custom serialization */ - inline SerializeNameFunction SerializeName{ DefaultSerializeName }; /* Feel free to assign this your own function, if you need custom serialization */ - inline SerializeClearColorFunction SerializeClearColor{ DefaultSerializeClearColor }; /* Feel free to assign this your own function, if you need custom serialization */ - inline SerializeSelectionOutlineColorFunction SerializeSelectionOutlineColor{ DefaultSerializeSelectionOutlineColor }; /* Feel free to assign this your own function, if you need custom serialization */ - namespace Cameras { - inline SerializeFunction Serialize{ DefaultSerialize }; /* Feel free to assign this your own function, if you need custom serialization */ - inline SerializeActiveCameraFunction SerializeActiveCamera{ DefaultSerializeActiveCamera }; /* Feel free to assign this your own function, if you need custom serialization */ - inline SerializeOrthoCameraFunction SerializeOrthoCamera{ DefaultSerializeOrthoCamera }; /* Feel free to assign this your own function, if you need custom serialization */ - inline SerializePerspectiveCameraFunction SerializePerspectiveCamera{ DefaultSerializePerspectiveCamera }; /* Feel free to assign this your own function, if you need custom serialization */ - } + inline SerializeFunction Serialize{ DefaultSerialize }; /* Feel free to assign this your own function, if you need custom serialization */ + inline SerializeNameFunction SerializeName{ DefaultSerializeName }; /* Feel free to assign this your own function, if you need custom serialization */ + inline SerializeClearColorFunction SerializeClearColor{ DefaultSerializeClearColor }; /* Feel free to assign this your own function, if you need custom serialization */ } namespace Entities { - inline SerializeFunction Serialize{ DefaultSerialize }; /* Feel free to assign this your own function, if you need custom serialization */ - inline SerializeEntityFunction SerializeEntity{ DefaultSerializeEntity }; /* Feel free to assign this your own function, if you need custom serialization */ - inline SerializeIDComponentFunction SerializeIDComponent{ DefaultSerializeIDComponent }; /* Feel free to assign this your own function, if you need custom serialization */ - inline SerializeTransformComponentFunction SerializeTransformComponent{ DefaultSerializeTransformComponent }; /* Feel free to assign this your own function, if you need custom serialization */ - inline SerializeParentComponentFunction SerializeParentComponent{ DefaultSerializeParentComponent }; /* Feel free to assign this your own function, if you need custom serialization */ - inline SerializeChildComponentFunction SerializeChildComponent{ DefaultSerializeChildComponent }; /* Feel free to assign this your own function, if you need custom serialization */ - inline SerializeUserDefinedComponentsFunction SerializeUserDefinedComponents{ DefaultSerializeUserDefinedComponents }; /* Feel free to assign this your own function, if you need custom serialization */ + inline SerializeFunction Serialize{ DefaultSerialize }; /* Feel free to assign this your own function, if you need custom serialization */ + inline SerializeEntityFunction SerializeEntity{ DefaultSerializeEntity }; /* Feel free to assign this your own function, if you need custom serialization */ + inline SerializeIDComponentFunction SerializeIDComponent{ DefaultSerializeIDComponent }; /* Feel free to assign this your own function, if you need custom serialization */ + inline SerializeTransformComponentFunction SerializeTransformComponent{ DefaultSerializeTransformComponent }; /* Feel free to assign this your own function, if you need custom serialization */ + inline SerializeParentComponentFunction SerializeParentComponent{ DefaultSerializeParentComponent }; /* Feel free to assign this your own function, if you need custom serialization */ + inline SerializeChildComponentFunction SerializeChildComponent{ DefaultSerializeChildComponent }; /* Feel free to assign this your own function, if you need custom serialization */ + inline SerializeOrthoCameraComponentFunction SerializeOrthoCameraComponent{ DefaultSerializeOrthoCameraComponent }; /* Feel free to assign this your own function, if you need custom serialization */ + inline SerializePerspectiveCameraComponentFunction SerializePerspectiveCameraComponent{ DefaultSerializePerspectiveCameraComponent }; /* Feel free to assign this your own function, if you need custom serialization */ + inline SerializeUserDefinedComponentsFunction SerializeUserDefinedComponents{ DefaultSerializeUserDefinedComponents }; /* Feel free to assign this your own function, if you need custom serialization */ } } diff --git a/Engine/Shader.cpp b/Engine/Shader.cpp index 016e51c4..0325f2e4 100644 --- a/Engine/Shader.cpp +++ b/Engine/Shader.cpp @@ -2,12 +2,13 @@ #include +#include #include #include #include -std::string Shader::Serialize() const { return SourceCode; } -Shader Shader::Deserialize(const std::string_view data) { +std::string Shader::Serialize() const noexcept { return SourceCode; } +Shader Shader::Deserialize(const std::string_view data) noexcept { auto result = DeserializeVertexShader(data); if (!result.erred) { return result.deserializedShader; } @@ -29,18 +30,30 @@ Shader Shader::Deserialize(const std::string_view data) { return {}; // TODO return shader } -Shader Shader::Copy(const Shader& shader) { +Shader Shader::Copy(const Shader& shader) noexcept { return shader.Deserialize(shader.Serialize()); } -bool ShaderProgram::IsComplete() const { return HasVertexShader() && HasFragmentShader(); } -bool ShaderProgram::HasVertexShader() const { return VertexShader != UUID::Empty(); } -bool ShaderProgram::HasTessellationControlShader() const { return TessellationControlShader != UUID::Empty(); } -bool ShaderProgram::HasTessellationEvaluationShader() const { return TessellationEvaluationShader != UUID::Empty(); } -bool ShaderProgram::HasGeometryShader() const { return GeometryShader != UUID::Empty(); } -bool ShaderProgram::HasFragmentShader() const { return FragmentShader != UUID::Empty(); } +bool ShaderProgram::IsComplete() const noexcept { + if (!HasComputeShader()) { + return HasVertexShader() && HasFragmentShader() ; + } else { + return !HasVertexShader() && + !HasTessellationControlShader() && + !HasTessellationEvaluationShader() && + !HasGeometryShader() && + !HasFragmentShader() && + HasComputeShader(); + } +} +bool ShaderProgram::HasVertexShader() const noexcept { return VertexShader != UUID::Empty(); } +bool ShaderProgram::HasTessellationControlShader() const noexcept { return TessellationControlShader != UUID::Empty(); } +bool ShaderProgram::HasTessellationEvaluationShader() const noexcept { return TessellationEvaluationShader != UUID::Empty(); } +bool ShaderProgram::HasGeometryShader() const noexcept { return GeometryShader != UUID::Empty(); } +bool ShaderProgram::HasFragmentShader() const noexcept { return FragmentShader != UUID::Empty(); } +bool ShaderProgram::HasComputeShader() const noexcept { return ComputeShader != UUID::Empty(); } -std::string ShaderProgram::Serialize() const { return SerializeShaderProgram(*this); } -ShaderProgram ShaderProgram::Deserialize(std::string_view data) { return DeserializeShaderProgram(data).deserializedShaderProgram; } +std::string ShaderProgram::Serialize() const noexcept { return SerializeShaderProgram(*this); } +ShaderProgram ShaderProgram::Deserialize(std::string_view data) noexcept { return DeserializeShaderProgram(data).deserializedShaderProgram; } -ShaderProgram ShaderProgram::Copy(const ShaderProgram& program) { return program.Deserialize(program.Serialize()); } \ No newline at end of file +ShaderProgram ShaderProgram::Copy(const ShaderProgram& program) noexcept { return program.Deserialize(program.Serialize()); } \ No newline at end of file diff --git a/Engine/Shader.hpp b/Engine/Shader.hpp index 7276225e..c4fe6d86 100644 --- a/Engine/Shader.hpp +++ b/Engine/Shader.hpp @@ -7,36 +7,20 @@ #include #include +#include struct Shader { - enum class ShaderType { - Vertex, - TessellationControl, - TessellationEvaluation, - Geometry, - Fragment, - Compute - }; - - struct Uniform { - std::string TypeName; - std::string Name; - }; - - GLuint ID; ShaderType Type; std::string Name; std::string SourceCode; - std::vector Uniforms; - std::string Serialize() const; - static Shader Deserialize(const std::string_view data); + std::string Serialize() const noexcept; + static Shader Deserialize(const std::string_view data) noexcept; - static Shader Copy(const Shader& shader); + static Shader Copy(const Shader& shader) noexcept; }; struct ShaderProgram { - GLuint ID; std::string Name; UUID VertexShader { UUID::Empty() }; @@ -44,16 +28,18 @@ struct ShaderProgram { UUID TessellationEvaluationShader{ UUID::Empty() }; UUID GeometryShader { UUID::Empty() }; UUID FragmentShader { UUID::Empty() }; + UUID ComputeShader { UUID::Empty() }; - bool IsComplete() const; - bool HasVertexShader() const; - bool HasTessellationControlShader() const; - bool HasTessellationEvaluationShader() const; - bool HasGeometryShader() const; - bool HasFragmentShader() const; + bool IsComplete() const noexcept; + bool HasVertexShader() const noexcept; + bool HasTessellationControlShader() const noexcept; + bool HasTessellationEvaluationShader() const noexcept; + bool HasGeometryShader() const noexcept; + bool HasFragmentShader() const noexcept; + bool HasComputeShader() const noexcept; - std::string Serialize() const; - static ShaderProgram Deserialize(const std::string_view data); + std::string Serialize() const noexcept; + static ShaderProgram Deserialize(const std::string_view data) noexcept; - static ShaderProgram Copy(const ShaderProgram& program); -}; + static ShaderProgram Copy(const ShaderProgram& program) noexcept; +}; \ No newline at end of file diff --git a/Engine/ShaderDeserializer.cpp b/Engine/ShaderDeserializer.cpp index ad0b6bdb..2eb3a483 100644 --- a/Engine/ShaderDeserializer.cpp +++ b/Engine/ShaderDeserializer.cpp @@ -1,300 +1,63 @@ #include -#include -#include -#include - -#include - #include -static std::vector SplitCompilerMessages(const std::string& messages) { - static const std::regex regex{ "\n(?!\\s)" }; - - std::vector rv; - std::copy( - std::sregex_token_iterator{ messages.cbegin(), messages.cend(), regex, -1 }, - std::sregex_token_iterator{}, - std::back_inserter(rv) - ); - - return rv; -} - -static ShaderCompilerMessage ParseCompilerMessage(const std::string& message) { - static const std::regex regex{ "0\\((\\d*)\\)\\s*:\\s*(\\w*)\\s*\\w*\\s*:\\s*(.*)" }; - - ShaderCompilerMessage rv; - - std::smatch match; - if (std::regex_search(message, match, regex)) { - rv.fullMessage = trim_copy(message); - rv.lineNo = std::stoi(match[1]); - std::string type = match[2]; - if (type == "info") { - rv.messageType = ShaderCompilerMessageType::INFO; - } else if (type == "warning") { - rv.messageType = ShaderCompilerMessageType::WARNING; - } else if (type == "error") { - rv.messageType = ShaderCompilerMessageType::ERROR; - } else { - assert(false); // matches to something else, find and implement! - } - rv.shortMessage = match[3]; - } else { - assert(false); // investigate why regex didn't match! - } - return rv; -} - ShaderDeserializationResult DeserializeVertexShader(const FNode& file) { file.ReadContent(); - auto rv = DeserializeVertexShader(file.DisposeContent()); - if (!rv.erred) { - rv.deserializedShader.Name = file.Name(); - } + ShaderDeserializationResult rv = DeserializeVertexShader(file.DisposeContent()); + rv.deserializedShader.Name = file.Name(); return rv; } ShaderDeserializationResult DeserializeVertexShader(const std::string_view data) { - ShaderDeserializationResult rv; - rv.messages.emplace_back(0, ShaderCompilerMessageType::INFO, "Make sure this file contains GLSL Vertex Shader code!"); - - const auto source = data.data(); - GLuint v = glCreateShader(GL_VERTEX_SHADER); - glShaderSource(v, 1, &source, NULL); - - int success; - glCompileShader(v); - glGetShaderiv(v, GL_COMPILE_STATUS, &success); - if (!success) { - rv.messages.emplace_back(0, ShaderCompilerMessageType::ERROR, "Vertex Shader compilation failed!"); - } else { - rv.deserializedShader = { v, Shader::ShaderType::Vertex, "", source, {} }; - } - - GLint bufferLength; - glGetShaderiv(v, GL_INFO_LOG_LENGTH, &bufferLength); - if (bufferLength > 1) { - GLchar* logChars = new char[bufferLength + 1]; - glGetShaderInfoLog(v, bufferLength, NULL, logChars); - std::string logString{ logChars }; - trim(logString); - auto logs = SplitCompilerMessages(logString); - for (const auto& log : logs) { - rv.messages.emplace_back(ParseCompilerMessage(log)); - } - delete[] logChars; - } - - return rv; + return { false, {}, { ShaderType::Vertex, "", data.data() }}; } ShaderDeserializationResult DeserializeTessellationControlShader(const FNode& file) { file.ReadContent(); - auto rv = DeserializeTessellationControlShader(file.DisposeContent()); - if (!rv.erred) { - rv.deserializedShader.Name = file.Name(); - } + ShaderDeserializationResult rv = DeserializeTessellationControlShader(file.DisposeContent()); + rv.deserializedShader.Name = file.Name(); return rv; } ShaderDeserializationResult DeserializeTessellationControlShader(const std::string_view data) { - ShaderDeserializationResult rv; - rv.messages.emplace_back(0, ShaderCompilerMessageType::INFO, "Make sure this file contains GLSL Tessellation Control Shader code!"); - - const auto source = data.data(); - GLuint tc = glCreateShader(GL_TESS_CONTROL_SHADER); - glShaderSource(tc, 1, &source, NULL); - - int success; - glCompileShader(tc); - glGetShaderiv(tc, GL_COMPILE_STATUS, &success); - if (!success) { - rv.messages.emplace_back(0, ShaderCompilerMessageType::ERROR, "Tessellation Control Shader compilation failed!"); - } else { - rv.deserializedShader = { tc, Shader::ShaderType::TessellationControl, "", source, {} }; - } - - GLint bufferLength; - glGetShaderiv(tc, GL_INFO_LOG_LENGTH, &bufferLength); - if (bufferLength > 1) { - GLchar* logChars = new char[bufferLength + 1]; - glGetShaderInfoLog(tc, bufferLength, NULL, logChars); - std::string logString{ logChars }; - trim(logString); - auto logs = SplitCompilerMessages(logString); - for (const auto& log : logs) { - rv.messages.emplace_back(ParseCompilerMessage(log)); - } - delete[] logChars; - } - - return rv; + return { false, {}, { ShaderType::TessellationControl, "", data.data() } }; } ShaderDeserializationResult DeserializeTessellationEvaluationShader(const FNode& file) { file.ReadContent(); - auto rv = DeserializeTessellationEvaluationShader(file.DisposeContent()); - if (!rv.erred) { - rv.deserializedShader.Name = file.Name(); - } + ShaderDeserializationResult rv = DeserializeTessellationEvaluationShader(file.DisposeContent()); + rv.deserializedShader.Name = file.Name(); return rv; } ShaderDeserializationResult DeserializeTessellationEvaluationShader(const std::string_view data) { - ShaderDeserializationResult rv; - rv.messages.emplace_back(0, ShaderCompilerMessageType::INFO, "Make sure this file contains GLSL Tessellation Evaluation Shader code!"); - - const auto source = data.data(); - GLuint te = glCreateShader(GL_TESS_EVALUATION_SHADER); - glShaderSource(te, 1, &source, NULL); - - int success; - glCompileShader(te); - glGetShaderiv(te, GL_COMPILE_STATUS, &success); - if (!success) { - rv.messages.emplace_back(0, ShaderCompilerMessageType::ERROR, "Tessellation Evaluation Shader compilation failed!"); - } else { - rv.deserializedShader = { te, Shader::ShaderType::TessellationEvaluation, "", source, {} }; - } - - GLint bufferLength; - glGetShaderiv(te, GL_INFO_LOG_LENGTH, &bufferLength); - if (bufferLength > 1) { - GLchar* logChars = new char[bufferLength + 1]; - glGetShaderInfoLog(te, bufferLength, NULL, logChars); - std::string logString{ logChars }; - trim(logString); - auto logs = SplitCompilerMessages(logString); - for (const auto& log : logs) { - rv.messages.emplace_back(ParseCompilerMessage(log)); - } - delete[] logChars; - } - - return rv; + return { false, {}, { ShaderType::TessellationEvaluation, "", data.data() } }; } ShaderDeserializationResult DeserializeGeometryShader(const FNode& file) { file.ReadContent(); - auto rv = DeserializeGeometryShader(file.DisposeContent()); - if (!rv.erred) { - rv.deserializedShader.Name = file.Name(); - } + ShaderDeserializationResult rv = DeserializeGeometryShader(file.DisposeContent()); + rv.deserializedShader.Name = file.Name(); return rv; } ShaderDeserializationResult DeserializeGeometryShader(const std::string_view data) { - ShaderDeserializationResult rv; - rv.messages.emplace_back(0, ShaderCompilerMessageType::INFO, "Make sure this file contains GLSL Geometry Shader code!"); - - const auto source = data.data(); - GLuint g = glCreateShader(GL_GEOMETRY_SHADER); - glShaderSource(g, 1, &source, NULL); - - int success; - glCompileShader(g); - glGetShaderiv(g, GL_COMPILE_STATUS, &success); - if (!success) { - rv.messages.emplace_back(0, ShaderCompilerMessageType::ERROR, "Geometry Shader compilation failed!"); - } else { - rv.deserializedShader = { g, Shader::ShaderType::Geometry, "", source, {} }; - } - - GLint bufferLength; - glGetShaderiv(g, GL_INFO_LOG_LENGTH, &bufferLength); - if (bufferLength > 1) { - GLchar* logChars = new char[bufferLength + 1]; - glGetShaderInfoLog(g, bufferLength, NULL, logChars); - std::string logString{ logChars }; - trim(logString); - auto logs = SplitCompilerMessages(logString); - for (const auto& log : logs) { - rv.messages.emplace_back(ParseCompilerMessage(log)); - } - delete[] logChars; - } - - return rv; + return { false, {}, { ShaderType::Geometry, "", data.data() } }; } ShaderDeserializationResult DeserializeFragmentShader(const FNode& file) { file.ReadContent(); - auto rv = DeserializeFragmentShader(file.DisposeContent()); - if (!rv.erred) { - rv.deserializedShader.Name = file.Name(); - } + ShaderDeserializationResult rv = DeserializeFragmentShader(file.DisposeContent()); + rv.deserializedShader.Name = file.Name(); return rv; } ShaderDeserializationResult DeserializeFragmentShader(const std::string_view data) { - ShaderDeserializationResult rv; - rv.messages.emplace_back(0, ShaderCompilerMessageType::INFO, "Make sure this file contains GLSL Fragment Shader code!"); - - const auto source = data.data(); - GLuint f = glCreateShader(GL_FRAGMENT_SHADER); - glShaderSource(f, 1, &source, NULL); - - int success; - glCompileShader(f); - glGetShaderiv(f, GL_COMPILE_STATUS, &success); - if (!success) { - rv.messages.emplace_back(0, ShaderCompilerMessageType::ERROR, "Fragment Shader compilation failed!"); - } else { - rv.deserializedShader = { f, Shader::ShaderType::Fragment, "", source, {} }; - } - - GLint bufferLength; - glGetShaderiv(f, GL_INFO_LOG_LENGTH, &bufferLength); - if (bufferLength > 1) { - GLchar* logChars = new char[bufferLength + 1]; - glGetShaderInfoLog(f, bufferLength, NULL, logChars); - std::string logString{ logChars }; - trim(logString); - auto logs = SplitCompilerMessages(logString); - for (const auto& log : logs) { - rv.messages.emplace_back(ParseCompilerMessage(log)); - } - delete[] logChars; - } - - return rv; + return { false, {}, { ShaderType::Fragment, "", data.data() } }; } ShaderDeserializationResult DeserializeComputeShader(const FNode& file) { file.ReadContent(); - auto rv = DeserializeComputeShader(file.DisposeContent()); - if (!rv.erred) { - rv.deserializedShader.Name = file.Name(); - } + ShaderDeserializationResult rv = DeserializeComputeShader(file.DisposeContent()); + rv.deserializedShader.Name = file.Name(); return rv; } ShaderDeserializationResult DeserializeComputeShader(const std::string_view data) { - ShaderDeserializationResult rv; - rv.messages.emplace_back(0, ShaderCompilerMessageType::INFO, "Make sure this file contains GLSL Compute Shader code!"); - - const auto source = data.data(); - GLuint c = glCreateShader(GL_COMPUTE_SHADER); - glShaderSource(c, 1, &source, NULL); - - int success; - glCompileShader(c); - glGetShaderiv(c, GL_COMPILE_STATUS, &success); - if (!success) { - rv.messages.emplace_back(0, ShaderCompilerMessageType::ERROR, "Compute Shader compilation failed!"); - } else { - rv.deserializedShader = { c, Shader::ShaderType::Compute, "", source, {} }; - } - - GLint bufferLength; - glGetShaderiv(c, GL_INFO_LOG_LENGTH, &bufferLength); - if (bufferLength > 1) { - GLchar* logChars = new char[bufferLength + 1]; - glGetShaderInfoLog(c, bufferLength, NULL, logChars); - std::string logString{ logChars }; - trim(logString); - auto logs = SplitCompilerMessages(logString); - for (const auto& log : logs) { - rv.messages.emplace_back(ParseCompilerMessage(log)); - } - delete[] logChars; - } - - return rv; + return { false, {}, { ShaderType::Compute, "", data.data() } }; } diff --git a/Engine/ShaderDeserializer.hpp b/Engine/ShaderDeserializer.hpp index e981cfe8..e6882280 100644 --- a/Engine/ShaderDeserializer.hpp +++ b/Engine/ShaderDeserializer.hpp @@ -1,25 +1,18 @@ #pragma once +#include #include +#include #include +#include struct FNode; -enum class ShaderCompilerMessageType { - INFO, - WARNING, - ERROR, - ComponentCompilerMessageType_COUNT -}; -struct ShaderCompilerMessage { - int lineNo; - ShaderCompilerMessageType messageType; - std::string shortMessage, fullMessage; -}; +// This struct is redundant, but is here to conform to the deserialization standard of NeoDoa. struct ShaderDeserializationResult { bool erred{ false }; - std::vector messages{}; + std::vector errors{}; Shader deserializedShader; }; diff --git a/Engine/ShaderProgramDeserializer.cpp b/Engine/ShaderProgramDeserializer.cpp index 8cd587ae..5cf5eef9 100644 --- a/Engine/ShaderProgramDeserializer.cpp +++ b/Engine/ShaderProgramDeserializer.cpp @@ -1,29 +1,10 @@ #include -#include -#include #include -#include - -#include #include -#include #include -static std::vector SplitLinkerMessages(const std::string& messages) { - static const std::regex regex{ "\n(?!\\s)" }; - - std::vector rv; - std::copy( - std::sregex_token_iterator{ messages.cbegin(), messages.cend(), regex, -1 }, - std::sregex_token_iterator{}, - std::back_inserter(rv) - ); - - return rv; -} - ShaderProgramDeserializationResult DeserializeShaderProgram(const FNode& file) { file.ReadContent(); return DeserializeShaderProgram(file.DisposeContent()); @@ -36,7 +17,7 @@ ShaderProgramDeserializationResult DeserializeShaderProgram(std::string_view dat if (err != tinyxml2::XML_SUCCESS) { rv.erred = true; rv.errors.emplace_back("Error while parsing program file. Did you edit it? Don't."); - DOA_LOG_ERROR("Couldn't deserialize shader program!\n\n%s", data); + DOA_LOG_ERROR("Couldn't deserialize shader program!\n\n%s", data.data()); return rv; } @@ -44,180 +25,84 @@ ShaderProgramDeserializationResult DeserializeShaderProgram(std::string_view dat return rv; } - -void ShaderProgramDeserializer::DefaultDeserialize(tinyxml2::XMLElement& rootNode, ShaderProgramDeserializationResult& spdr) { +void ShaderProgramDeserializer::DefaultDeserialize(const tinyxml2::XMLElement& rootNode, ShaderProgramDeserializationResult& spdr) { ProgramConfig::Deserialize(*rootNode.FirstChildElement("config"), spdr); Shaders::Deserialize(*rootNode.FirstChildElement("shaders"), spdr); - Linking::Link(spdr); } -void ShaderProgramDeserializer::ProgramConfig::DefaultDeserialize(tinyxml2::XMLElement& configNode, ShaderProgramDeserializationResult& spdr) { +void ShaderProgramDeserializer::ProgramConfig::DefaultDeserialize(const tinyxml2::XMLElement& configNode, ShaderProgramDeserializationResult& spdr) { DeserializeName(configNode, spdr); } -void ShaderProgramDeserializer::ProgramConfig::DefaultDeserializeName(tinyxml2::XMLElement& configNode, ShaderProgramDeserializationResult& spdr) { +void ShaderProgramDeserializer::ProgramConfig::DefaultDeserializeName(const tinyxml2::XMLElement& configNode, ShaderProgramDeserializationResult& spdr) { spdr.deserializedShaderProgram.Name = configNode.Attribute("name"); } -void ShaderProgramDeserializer::Shaders::DefaultDeserialize(tinyxml2::XMLElement& shadersNode, ShaderProgramDeserializationResult& spdr) { - tinyxml2::XMLElement* node; +void ShaderProgramDeserializer::Shaders::DefaultDeserialize(const tinyxml2::XMLElement& shadersNode, ShaderProgramDeserializationResult& spdr) { + const tinyxml2::XMLElement* node; node = shadersNode.FirstChildElement("vertex-shader"); - if (node == nullptr) { + if (!node) { spdr.erred = true; spdr.errors.emplace_back(std::format("Can't find node \"{}-shader\" in program file.", "vertex")); + return; } DeserializeVertexShader(*node, spdr); node = shadersNode.FirstChildElement("tess-ctrl-shader"); - if (node == nullptr) { + if (!node) { spdr.erred = true; spdr.errors.emplace_back(std::format("Can't find node \"{}-shader\" in program file.", "tess-ctrl")); + return; } DeserializeTessellationControlShader(*node, spdr); node = shadersNode.FirstChildElement("tess-eval-shader"); - if (node == nullptr) { + if (!node) { spdr.erred = true; spdr.errors.emplace_back(std::format("Can't find node \"{}-shader\" in program file.", "tess-eval")); + return; } DeserializeTessellationEvaluationShader(*node, spdr); node = shadersNode.FirstChildElement("geometry-shader"); - if (node == nullptr) { + if (!node) { spdr.erred = true; spdr.errors.emplace_back(std::format("Can't find node \"{}-shader\" in program file.", "geometry")); + return; } DeserializeGeometryShader(*node, spdr); node = shadersNode.FirstChildElement("fragment-shader"); - if (node == nullptr) { + if (!node) { spdr.erred = true; spdr.errors.emplace_back(std::format("Can't find node \"{}-shader\" in program file.", "fragment")); + return; } DeserializeFragmentShader(*node, spdr); - //DeserializeVertexShader(shadersNode.FirstChildElement("compute-shader"), program); + + node = shadersNode.FirstChildElement("compute-shader"); + if (!node) { + spdr.erred = true; + spdr.errors.emplace_back(std::format("Can't find node \"{}-shader\" in program file.", "compute")); + return; + } + DeserializeComputeShader(*node, spdr); } -void ShaderProgramDeserializer::Shaders::DefaultDeserializeVertexShader(tinyxml2::XMLElement& vertexShaderNode, ShaderProgramDeserializationResult& spdr) { +void ShaderProgramDeserializer::Shaders::DefaultDeserializeVertexShader(const tinyxml2::XMLElement& vertexShaderNode, ShaderProgramDeserializationResult& spdr) { spdr.deserializedShaderProgram.VertexShader = static_cast(vertexShaderNode.Unsigned64Attribute("uuid", UUID::Empty())); } -void ShaderProgramDeserializer::Shaders::DefaultDeserializeTessellationControlShader(tinyxml2::XMLElement& tessCtrlShaderNode, ShaderProgramDeserializationResult& spdr) { +void ShaderProgramDeserializer::Shaders::DefaultDeserializeTessellationControlShader(const tinyxml2::XMLElement& tessCtrlShaderNode, ShaderProgramDeserializationResult& spdr) { spdr.deserializedShaderProgram.TessellationControlShader = static_cast(tessCtrlShaderNode.Unsigned64Attribute("uuid", UUID::Empty())); } -void ShaderProgramDeserializer::Shaders::DefaultDeserializeTessellationEvaluationShader(tinyxml2::XMLElement& tessEvalShaderNode, ShaderProgramDeserializationResult& spdr) { +void ShaderProgramDeserializer::Shaders::DefaultDeserializeTessellationEvaluationShader(const tinyxml2::XMLElement& tessEvalShaderNode, ShaderProgramDeserializationResult& spdr) { spdr.deserializedShaderProgram.TessellationEvaluationShader = static_cast(tessEvalShaderNode.Unsigned64Attribute("uuid", UUID::Empty())); } -void ShaderProgramDeserializer::Shaders::DefaultDeserializeGeometryShader(tinyxml2::XMLElement& geometryShaderNode, ShaderProgramDeserializationResult& spdr) { +void ShaderProgramDeserializer::Shaders::DefaultDeserializeGeometryShader(const tinyxml2::XMLElement& geometryShaderNode, ShaderProgramDeserializationResult& spdr) { spdr.deserializedShaderProgram.GeometryShader = static_cast(geometryShaderNode.Unsigned64Attribute("uuid", UUID::Empty())); } -void ShaderProgramDeserializer::Shaders::DefaultDeserializeFragmentShader(tinyxml2::XMLElement& fragmentShaderNode, ShaderProgramDeserializationResult& spdr) { +void ShaderProgramDeserializer::Shaders::DefaultDeserializeFragmentShader(const tinyxml2::XMLElement& fragmentShaderNode, ShaderProgramDeserializationResult& spdr) { spdr.deserializedShaderProgram.FragmentShader = static_cast(fragmentShaderNode.Unsigned64Attribute("uuid", UUID::Empty())); } -void ShaderProgramDeserializer::Shaders::DefaultDeserializeComputeShader(tinyxml2::XMLElement& computeShaderNode, ShaderProgramDeserializationResult& spdr) { - //program.vertexShader = computeShaderNode.FindAttribute("id")->Unsigned64Attribute(); -} - -void ShaderProgramDeserializer::Linking::DefaultLink(ShaderProgramDeserializationResult& spdr) { - if (spdr.erred) { return; } /* if we have errors, don't even try to link program. */ - OpenGLLink(spdr); -} -void ShaderProgramDeserializer::Linking::DefaultOpenGLLink(ShaderProgramDeserializationResult& spdr) { - ShaderProgram& program = spdr.deserializedShaderProgram; - if (!program.IsComplete()) { - if (!program.HasVertexShader()) { - spdr.erred = true; - spdr.errors.emplace_back("Can't link program, program does not have a vertex shader!"); - } - if (!program.HasFragmentShader()) { - spdr.erred = true; - spdr.errors.emplace_back("Can't link program, program does not have a fragment shader!"); - } - } - GLuint ID = glCreateProgram(); - std::array attachedShaders{}; - - const Assets& assets = *Core::GetCore()->GetAssets().get(); - - // attach mandatory shaders - vertex and fragment - AssetHandle vertexShaderAsset = assets.FindAsset(program.VertexShader); - if (!vertexShaderAsset.HasValue()) { - spdr.erred = true; - spdr.errors.emplace_back("Can't link program, program references a non-existant vertex shader, has your shader successfully deserialized?"); - } - AssetHandle fragmentShaderAsset = assets.FindAsset(program.FragmentShader); - if (!fragmentShaderAsset.HasValue()) { - spdr.erred = true; - spdr.errors.emplace_back("Can't link program, program references a non-existant fragment shader, has your shader successfully deserialized?"); - } - if (!vertexShaderAsset.HasValue() || !fragmentShaderAsset.HasValue()) { - spdr.errors.emplace_back("Please fix errors before continuing."); - return; - } - const Shader& vertexShader = vertexShaderAsset->DataAs(); - glAttachShader(ID, vertexShader.ID); - const Shader& fragmentShader = fragmentShaderAsset->DataAs(); - glAttachShader(ID, fragmentShader.ID); - attachedShaders[0] = vertexShader.ID; - attachedShaders[4] = fragmentShader.ID; - - // attach optional shaders - tess_ctrl, tess_eval and geometry - bool usingTessellation{ program.HasTessellationEvaluationShader() }; - if (usingTessellation && program.HasTessellationControlShader()) { - AssetHandle tessCtrlShaderAsset = assets.FindAsset(program.TessellationControlShader); - if (tessCtrlShaderAsset.HasValue()) { - const Shader& tessCtrlShader = tessCtrlShaderAsset->DataAs(); - glAttachShader(ID, tessCtrlShader.ID); - attachedShaders[1] = tessCtrlShader.ID; - } - } - if (usingTessellation) { - AssetHandle tessEvalShaderAsset = assets.FindAsset(program.TessellationEvaluationShader); - if (!tessEvalShaderAsset.HasValue()) { - spdr.erred = true; - spdr.errors.emplace_back("Can't link program, program uses tessellation but references a non-existant evaluation shader, has your shader successfully deserialized?"); - return; - } - const Shader& tessEvalShader = tessEvalShaderAsset->DataAs(); - glAttachShader(ID, tessEvalShader.ID); - attachedShaders[2] = tessEvalShader.ID; - } - if (program.HasGeometryShader()) { - AssetHandle geometryShaderAsset = assets.FindAsset(program.GeometryShader); - if (!geometryShaderAsset.HasValue()) { - spdr.erred = true; - spdr.errors.emplace_back("Can't link program, program references a non-existant geometry shader, has your shader successfully deserialized?"); - return; - } - const Shader& geometryShader = geometryShaderAsset->DataAs(); - glAttachShader(ID, geometryShader.ID); - attachedShaders[3] = geometryShader.ID; - } - - int success; - glLinkProgram(ID); - glGetProgramiv(ID, GL_LINK_STATUS, &success); - if (!success) { - spdr.erred = true; - spdr.errors.emplace_back("Program linking failed!"); - glDeleteProgram(ID); - } else { - program.ID = ID; - } - - GLint bufferLength; - glGetProgramiv(ID, GL_INFO_LOG_LENGTH, &bufferLength); - if (bufferLength > 1) { - GLchar* logChars = new char[bufferLength + 1]; - glGetProgramInfoLog(ID, bufferLength, NULL, logChars); - std::string logString{ logChars }; - trim(logString); - auto logs = SplitLinkerMessages(logString); - for (auto& log : logs) { - spdr.errors.emplace_back(std::move(log)); - } - delete[] logChars; - } - - for (const auto shader : attachedShaders) { - glDetachShader(ID, shader); - } +void ShaderProgramDeserializer::Shaders::DefaultDeserializeComputeShader(const tinyxml2::XMLElement& computeShaderNode, ShaderProgramDeserializationResult& spdr) { + spdr.deserializedShaderProgram.ComputeShader = static_cast(computeShaderNode.Unsigned64Attribute("uuid", UUID::Empty())); } \ No newline at end of file diff --git a/Engine/ShaderProgramDeserializer.hpp b/Engine/ShaderProgramDeserializer.hpp index 3f9de4db..d454edf7 100644 --- a/Engine/ShaderProgramDeserializer.hpp +++ b/Engine/ShaderProgramDeserializer.hpp @@ -1,17 +1,20 @@ #pragma once +#include +#include #include #include #include #include +#include struct FNode; struct ShaderProgramDeserializationResult { bool erred{ false }; - std::vector errors{}; + std::vector errors{}; ShaderProgram deserializedShaderProgram; }; @@ -22,44 +25,36 @@ ShaderProgramDeserializationResult DeserializeShaderProgram(std::string_view dat namespace ShaderProgramDeserializer { /* ------- Type Definitons ------- */ - using DeserializeFunction = std::function; + using DeserializeFunction = std::function; namespace ProgramConfig { - using DeserializeFunction = std::function; - using DeserializeNameFunction = std::function; + using DeserializeFunction = std::function; + using DeserializeNameFunction = std::function; } namespace Shaders { - using DeserializeFunction = std::function; - using DeserializeVertexShaderFunction = std::function; - using DeserializeTessellationControlShaderFunction = std::function; - using DeserializeTessellationEvaluationShaderFunction = std::function; - using DeserializeGeometryShaderFunction = std::function; - using DeserializeFragmentShaderFunction = std::function; - using DeserializeComputeShaderFunction = std::function; - } - namespace Linking { - using LinkFunction = std::function; - using OpenGLLinkFunction = std::function; + using DeserializeFunction = std::function; + using DeserializeVertexShaderFunction = std::function; + using DeserializeTessellationControlShaderFunction = std::function; + using DeserializeTessellationEvaluationShaderFunction = std::function; + using DeserializeGeometryShaderFunction = std::function; + using DeserializeFragmentShaderFunction = std::function; + using DeserializeComputeShaderFunction = std::function; } /* --- Default Implementations --- */ /* These are how NeoDoa will deserialize by default. */ - void DefaultDeserialize(tinyxml2::XMLElement& rootNode, ShaderProgramDeserializationResult& spdr); + void DefaultDeserialize(const tinyxml2::XMLElement& rootNode, ShaderProgramDeserializationResult& spdr); namespace ProgramConfig { - void DefaultDeserialize(tinyxml2::XMLElement& configNode, ShaderProgramDeserializationResult& spdr); - void DefaultDeserializeName(tinyxml2::XMLElement& configNode, ShaderProgramDeserializationResult& spdr); + void DefaultDeserialize(const tinyxml2::XMLElement& configNode, ShaderProgramDeserializationResult& spdr); + void DefaultDeserializeName(const tinyxml2::XMLElement& configNode, ShaderProgramDeserializationResult& spdr); } namespace Shaders { - void DefaultDeserialize(tinyxml2::XMLElement& shadersNode, ShaderProgramDeserializationResult& spdr); - void DefaultDeserializeVertexShader(tinyxml2::XMLElement& vertexShaderNode, ShaderProgramDeserializationResult& spdr); - void DefaultDeserializeTessellationControlShader(tinyxml2::XMLElement& tessCtrlShaderNode, ShaderProgramDeserializationResult& spdr); - void DefaultDeserializeTessellationEvaluationShader(tinyxml2::XMLElement& tessEvalShaderNode, ShaderProgramDeserializationResult& spdr); - void DefaultDeserializeGeometryShader(tinyxml2::XMLElement& geometryShaderNode, ShaderProgramDeserializationResult& spdr); - void DefaultDeserializeFragmentShader(tinyxml2::XMLElement& fragmentShaderNode, ShaderProgramDeserializationResult& spdr); - void DefaultDeserializeComputeShader(tinyxml2::XMLElement& computeShaderNode, ShaderProgramDeserializationResult& spdr); - } - namespace Linking { - void DefaultLink(ShaderProgramDeserializationResult& spdr); - void DefaultOpenGLLink(ShaderProgramDeserializationResult& spdr); + void DefaultDeserialize(const tinyxml2::XMLElement& shadersNode, ShaderProgramDeserializationResult& spdr); + void DefaultDeserializeVertexShader(const tinyxml2::XMLElement& vertexShaderNode, ShaderProgramDeserializationResult& spdr); + void DefaultDeserializeTessellationControlShader(const tinyxml2::XMLElement& tessCtrlShaderNode, ShaderProgramDeserializationResult& spdr); + void DefaultDeserializeTessellationEvaluationShader(const tinyxml2::XMLElement& tessEvalShaderNode, ShaderProgramDeserializationResult& spdr); + void DefaultDeserializeGeometryShader(const tinyxml2::XMLElement& geometryShaderNode, ShaderProgramDeserializationResult& spdr); + void DefaultDeserializeFragmentShader(const tinyxml2::XMLElement& fragmentShaderNode, ShaderProgramDeserializationResult& spdr); + void DefaultDeserializeComputeShader(const tinyxml2::XMLElement& computeShaderNode, ShaderProgramDeserializationResult& spdr); } /* ----- Deserializer Functions ----- */ @@ -77,8 +72,4 @@ namespace ShaderProgramDeserializer { inline DeserializeFragmentShaderFunction DeserializeFragmentShader{ DefaultDeserializeFragmentShader }; /* Feel free to assign this your own function, if you need custom serialization */ inline DeserializeComputeShaderFunction DeserializeComputeShader{ DefaultDeserializeComputeShader }; /* Feel free to assign this your own function, if you need custom serialization */ } - namespace Linking { - inline LinkFunction Link{ DefaultLink }; /* Feel free to assign this your own function, if you need custom linking */ - inline OpenGLLinkFunction OpenGLLink{ DefaultOpenGLLink }; /* Feel free to assign this your own function, if you need custom linking */ - } } diff --git a/Engine/ShaderProgramSerializer.cpp b/Engine/ShaderProgramSerializer.cpp index 07535bf2..b3573f0e 100644 --- a/Engine/ShaderProgramSerializer.cpp +++ b/Engine/ShaderProgramSerializer.cpp @@ -41,6 +41,7 @@ void ShaderProgramSerializer::Shaders::DefaultSerialize(tinyxml2::XMLPrinter& pr SerializeTessellationEvaluationShader(printer, program.TessellationEvaluationShader); SerializeGeometryShader(printer, program.GeometryShader); SerializeFragmentShader(printer, program.FragmentShader); + SerializeComputeShader(printer, program.ComputeShader); } printer.CloseElement(); // shaders close } @@ -70,7 +71,7 @@ void ShaderProgramSerializer::Shaders::DefaultSerializeFragmentShader(tinyxml2:: printer.CloseElement(); } void ShaderProgramSerializer::Shaders::DefaultSerializeComputeShader(tinyxml2::XMLPrinter& printer, const UUID& computeShader) { - /*printer.OpenElement("vertex-shader"); - printer.PushAttribute("uuid", vertexShader); - printer.CloseElement();*/ + printer.OpenElement("compute-shader"); + printer.PushAttribute("uuid", computeShader); + printer.CloseElement(); } diff --git a/Engine/ShaderProgramSerializer.hpp b/Engine/ShaderProgramSerializer.hpp index 0d33884c..9e722b4c 100644 --- a/Engine/ShaderProgramSerializer.hpp +++ b/Engine/ShaderProgramSerializer.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include diff --git a/Engine/System.hpp b/Engine/System.hpp index 27a2a27d..a6543fb4 100644 --- a/Engine/System.hpp +++ b/Engine/System.hpp @@ -7,7 +7,7 @@ struct System : entt::type_list { template struct type : Base { bool inited = false; - void Init(Registry& reg) { if (inited) return; inited = true; entt::poly_call<0>(*this, reg); } + void Init(Registry& reg) { if (inited) { inited = true; return; } entt::poly_call<0>(*this, reg); } void Execute(Registry& reg, float deltaTime) { entt::poly_call<1>(*this, reg, deltaTime); } }; diff --git a/Engine/Texture.cpp b/Engine/Texture.cpp index 8e2e745a..64dd20d6 100644 --- a/Engine/Texture.cpp +++ b/Engine/Texture.cpp @@ -1,188 +1,54 @@ -#include "Texture.hpp" - -#include - -#include - -#include - -#include "TextureSerializer.hpp" -#include "TextureDeserializer.hpp" -#include "Log.hpp" - -Texture Texture::CreateTexture(std::string_view name, const char* path, TextureTransparency transparency) { - stbi_set_flip_vertically_on_load(true); - int width, height, nrChannels; - unsigned char* pixelData = stbi_load(path, &width, &height, &nrChannels, transparency == TextureTransparency::YES ? STBI_rgb_alpha : STBI_rgb); - if (pixelData == nullptr) { - stbi_image_free(pixelData); - DOA_LOG_WARNING("Couldn't load %s! No such file at %s!", name.data(), path); - return Empty(); +#include + +#include +#include + +const Texture& Texture::Missing() noexcept { +#define ND_BYTE(x) static_cast(x) + + constexpr unsigned textureSize = 256; + static bool initialized = false; + static std::vector pixelData(textureSize * textureSize * 4); + + if (!initialized) { + // Fill the textureSize x textureSize texture with the same pattern as the original 2x2 texture + for (unsigned y = 0; y < textureSize; ++y) { + for (unsigned x = 0; x < textureSize; ++x) { + unsigned index = (y * textureSize + x) * 4; + bool isCyan = ((x / (textureSize / 2)) % 2) == ((y / (textureSize / 2)) % 2); + if (isCyan) { + pixelData[index] = ND_BYTE(0); + pixelData[index + 1] = ND_BYTE(255); + pixelData[index + 2] = ND_BYTE(255); + pixelData[index + 3] = ND_BYTE(255); + } else { + pixelData[index] = ND_BYTE(0); + pixelData[index + 1] = ND_BYTE(0); + pixelData[index + 2] = ND_BYTE(0); + pixelData[index + 3] = ND_BYTE(255); + } + } + } + initialized = true; } -#ifdef DEBUG - Texture::FACTORY_FLAG = true; - Texture rv{ name, static_cast(width), static_cast(height), pixelData, transparency }; - Texture::FACTORY_FLAG = false; -#else - Texture rv{ name, static_cast(width), static_cast(height), pixelData, transparency }; -#endif - stbi_image_free(pixelData); - return rv; -} -Texture Texture::CreateTexture(std::string_view name, const unsigned char* data, size_t length, TextureTransparency transparency) { - stbi_set_flip_vertically_on_load(true); - int width, height, nrChannels; - unsigned char* pixelData = stbi_load_from_memory(data, static_cast(length), &width, &height, &nrChannels, transparency == TextureTransparency::YES ? STBI_rgb_alpha : STBI_rgb); - if (pixelData == nullptr) { - stbi_image_free(pixelData); - DOA_LOG_WARNING("Couldn't load %s!", name.data()); - return Empty(); - } + static Texture missing{ + .Name = "Missing Texture", + .Width = textureSize, + .Height = textureSize, + .Channels = 4, + .Format = DataFormat::RGBA8, + .PixelData = pixelData + }; -#ifdef DEBUG - Texture::FACTORY_FLAG = true; - Texture rv{ name, static_cast(width), static_cast(height), pixelData, transparency }; - Texture::FACTORY_FLAG = false; -#else - Texture rv{ name, static_cast(width), static_cast(height), pixelData, transparency }; -#endif - stbi_image_free(pixelData); - return rv; -} -Texture Texture::CreateTexture(std::string_view name, ByteVector data, TextureTransparency transparency){ - return CreateTexture(name, reinterpret_cast(data.data()), data.size(), transparency); -} -Texture Texture::CreateTextureRaw(std::string_view name, const unsigned char* pixelData, size_t width, size_t height, TextureTransparency transparency) { - if (pixelData == nullptr) { - DOA_LOG_WARNING("Couldn't load %s from raw pointer to memory! Pointer is nullptr", name.data()); - return Empty(); - } -#ifdef DEBUG - Texture::FACTORY_FLAG = true; - Texture rv{ name, width, height, pixelData, transparency }; - Texture::FACTORY_FLAG = false; -#else - Texture rv{ name, width, height, pixelData, transparency }; -#endif - return rv; +#undef ND_BYTE + return missing; } -const std::byte* const Texture::GetByteBufferOf(const Texture& texture) { return texture._pixelData.data(); } -ByteVector Texture::RequestPixelDataOf(const Texture& texture) { return texture._pixelData; } -void Texture::ApplyPixelDataTo(Texture& texture, const ByteVector& data) { - Texture::ApplyPixelDataTo(texture, std::move(ByteVector(data))); -} -void Texture::ApplyPixelDataTo(Texture& texture, ByteVector&& data) { - texture._pixelData = std::move(data); - texture.DeallocateGPU(); - texture.AllocateGPU(); -} +bool Texture::HasTransparency() const noexcept { return Channels == 4; } -void Texture::Bind(int slot) { - glBindTextureUnit(slot, _glTextureID); -} +EncodedTextureData Texture::Serialize(TextureEncoding encoding) const noexcept { return SerializeTexture(*this, encoding); } +Texture Texture::Deserialize(const EncodedTextureData& data) noexcept { return DeserializeTexture(data).deserializedTexture; } -const std::string& Texture::Name() const { return _name; } -size_t Texture::Width() const { return _width; } -size_t Texture::Height() const { return _height; } -bool Texture::HasTransparency() const { return _transparency == TextureTransparency::YES ? true : false; } -TEX Texture::TextureID() const { return _glTextureID; } -void* Texture::TextureIDRaw() const { return reinterpret_cast(static_cast(_glTextureID)); } +Texture Texture::Copy(const Texture& texture) noexcept { return texture; } -Texture::~Texture() noexcept { - DeallocateGPU(); -} -Texture::Texture(Texture&& other) noexcept : - _name(std::move(other._name)), - _width(std::exchange(other._width, 0)), - _height(std::exchange(other._height, 0)), - _transparency(std::exchange(other._transparency, TextureTransparency::NO)), - _pixelData(std::move(other._pixelData)), - _glTextureID(std::exchange(other._glTextureID, 0)) {} -Texture& Texture::operator=(Texture&& other) noexcept { - _name = std::move(other._name); - _width = std::exchange(other._width, 0); - _height = std::exchange(other._height, 0); - _transparency = std::exchange(other._transparency, TextureTransparency::NO); - _glTextureID = std::exchange(other._glTextureID, 0); - _pixelData = std::move(other._pixelData); - return *this; -} -bool Texture::operator==(const Texture& other) noexcept { - if (this == &other) return true; - /* - note that we don't need to check _glTextureID - because if two distinct objects have the same - _glTextureID, then they are the SAME (which is - covered by this==&other. why you ask? because if - they aren't the SAME, same _glTextureID will be - double-deleted (see dtor). we don't want that. - */ - if (this->_name == other._name && - this->_width == other._width && - this->_height == other._height && - this->_transparency == other._transparency && - this->_pixelData == other._pixelData) { - return true; - } - return false; -} -bool Texture::operator!=(const Texture& other) noexcept { return !this->operator==(other); } - -EncodedTextureData Texture::Serialize(TextureEncoding encoding) const { return SerializeTexture(*this, encoding); } -Texture Texture::Deserialize(const EncodedTextureData& data) { return DeserializeTexture(data); } - -Texture Texture::Copy(const Texture& texture) { return CreateTextureRaw(texture._name, reinterpret_cast(texture._pixelData.data()), texture._width, texture._height, texture._transparency); } - -//----------------------------------------------------------------- - -Texture::Texture() noexcept {} -Texture::Texture(std::string_view name, size_t width, size_t height, const unsigned char* const pixelData, TextureTransparency transparency) noexcept : - _name(name), - _width(width), - _height(height), - _transparency(transparency), - _pixelData(width * height * (transparency == TextureTransparency::YES ? static_cast(4) : static_cast(3))) { - assert(FACTORY_FLAG); /* don't call ctor directly, use CreateTexture */ - for (auto i = 0; i < _pixelData.size(); i++) { - _pixelData[i] = static_cast(pixelData[i]); - } - if (_width > 0 && _height > 0) { - AllocateGPU(); - } else { - DOA_LOG_ERROR("Textures must have positive dimensions! Expected width > 0 && height > 0, received width = %d height = %d", _width, _height); - } -} - -void Texture::AllocateGPU() noexcept { - glCreateTextures(GL_TEXTURE_2D, 1, &_glTextureID); - - glTextureParameteri(_glTextureID, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTextureParameteri(_glTextureID, GL_TEXTURE_WRAP_T, GL_REPEAT); - glTextureParameteri(_glTextureID, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - glTextureParameteri(_glTextureID, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - glTextureStorage2D( - _glTextureID, - 1, - _transparency == TextureTransparency::YES ? GL_RGBA8 : GL_RGB8, - static_cast(_width), - static_cast(_height) - ); - glTextureSubImage2D( - _glTextureID, - 0, - 0, - 0, - static_cast(_width), - static_cast(_height), - _transparency == TextureTransparency::YES ? GL_RGBA : GL_RGB, - GL_UNSIGNED_BYTE, - _pixelData.data() - ); - glGenerateTextureMipmap(_glTextureID); - glTextureParameterf(_glTextureID, GL_TEXTURE_LOD_BIAS, -0.5f); - -} -void Texture::DeallocateGPU() noexcept { glDeleteTextures(1, &_glTextureID); } \ No newline at end of file diff --git a/Engine/Texture.hpp b/Engine/Texture.hpp index eaa6011b..b6abc66e 100644 --- a/Engine/Texture.hpp +++ b/Engine/Texture.hpp @@ -1,85 +1,31 @@ #pragma once -#include "TypedefsAndConstants.hpp" - -using ByteVector = std::vector; +#include enum class TextureEncoding { PNG, BMP, TGA, JPG, }; struct EncodedTextureData { - - std::string name; - - ByteVector data; - - bool hasTransparency; - - TextureEncoding encoding; -}; - -enum class TextureTransparency { - YES, NO + std::string Name; + RawData EncodedData; + bool HasTransparency; + TextureEncoding Encoding; }; struct Texture { + static const Texture& Missing() noexcept; - static Texture Empty() noexcept { -#ifdef DEBUG - Texture::FACTORY_FLAG = true; return {}; Texture::FACTORY_FLAG = false; -#else - return {}; -#endif - }; - - static Texture CreateTexture(std::string_view name, const char* path, TextureTransparency transparency = TextureTransparency::YES); - static Texture CreateTexture(std::string_view name, const unsigned char* data, size_t length, TextureTransparency transparency = TextureTransparency::YES); - static Texture CreateTexture(std::string_view name, ByteVector data, TextureTransparency transparency = TextureTransparency::YES); - static Texture CreateTextureRaw(std::string_view name, const unsigned char* pixelData, size_t width, size_t height, TextureTransparency transparency = TextureTransparency::YES); - - static const std::byte* const GetByteBufferOf(const Texture& texture); - static ByteVector RequestPixelDataOf(const Texture& texture); - static void ApplyPixelDataTo(Texture& texture, const ByteVector& data); - static void ApplyPixelDataTo(Texture& texture, ByteVector&& data); - - void Bind(int slot = 0); - - const std::string& Name() const; - size_t Width() const; - size_t Height() const; - bool HasTransparency() const; - TEX TextureID() const; - void* TextureIDRaw() const; - - ~Texture() noexcept; - Texture(const Texture&) = delete; - Texture(Texture&&) noexcept; - Texture& operator=(const Texture&) = delete; - Texture& operator=(Texture&&) noexcept; - bool operator==(const Texture& other) noexcept; - bool operator!=(const Texture& other) noexcept; - - EncodedTextureData Serialize(TextureEncoding encoding = TextureEncoding::PNG) const; - static Texture Deserialize(const EncodedTextureData& data); - - static Texture Copy(const Texture& texture); - -private: -#ifdef DEBUG - static inline bool FACTORY_FLAG{ false }; -#endif + std::string Name{}; + unsigned Width{ 1 }, Height{ 1 }; + unsigned Channels{}; + DataFormat Format{}; + RawData PixelData{}; - std::string _name{}; - size_t _width{ 0 }, _height{ 0 }; - TextureTransparency _transparency{ TextureTransparency::NO }; - TEX _glTextureID{ 0 }; - ByteVector _pixelData{}; + bool HasTransparency() const noexcept; - /* don't call ctor, use factory functions */ - Texture() noexcept; - Texture(std::string_view name, size_t width, size_t height, const unsigned char* const pixelData, TextureTransparency transparency) noexcept; + EncodedTextureData Serialize(TextureEncoding encoding = TextureEncoding::PNG) const noexcept; + static Texture Deserialize(const EncodedTextureData& data) noexcept; - void AllocateGPU() noexcept; - void DeallocateGPU() noexcept; + static Texture Copy(const Texture& texture) noexcept; }; diff --git a/Engine/TextureDeserializer.cpp b/Engine/TextureDeserializer.cpp index f67db5b5..61b36887 100644 --- a/Engine/TextureDeserializer.cpp +++ b/Engine/TextureDeserializer.cpp @@ -1,14 +1,90 @@ -#include "TextureDeserializer.hpp" - -#include "FileNode.hpp" -#include "Texture.hpp" -#include "TextureSerializer.hpp" - -Texture DeserializeTexture(const FNode& file) { return Texture::CreateTexture(file.Name(), file.AbsolutePath().string().c_str()); } -Texture DeserializeTexture(const EncodedTextureData& data) { - return Texture::CreateTexture( - data.name, - data.data, - data.hasTransparency ? TextureTransparency::YES : TextureTransparency::NO +#include + +#include +#include + +#include + +#include +#include +#include + +static RawData ToRawData(const std::string_view str) { + RawData raw; + raw.reserve(str.size()); + for (auto c : str) { + raw.push_back(static_cast(c)); + } + return raw; +} +TextureEncoding ExtToEncoding(const std::string_view ext) noexcept { + using enum TextureEncoding; + if (ext == Assets::TextureExtensionPNG) { + return PNG; + } else if (ext == Assets::TextureExtensionBMP) { + return BMP; + } else if (ext == Assets::TextureExtensionTGA) { + return TGA; + } else if(ext == Assets::TextureExtensionJPG || ext == Assets::TextureExtensionJPEG) { + return JPG; + } else { + std::unreachable(); + } +} + +TextureDeserializationResult DeserializeTexture(const FNode& file) { + file.ReadContent(); + + EncodedTextureData encodedData; + encodedData.Name = file.Name(); + encodedData.EncodedData = ToRawData(file.DisposeContent()); + + const auto& ext = file.Extension(); + encodedData.HasTransparency = ext == Assets::TextureExtensionPNG; + encodedData.Encoding = ExtToEncoding(ext); + + return DeserializeTexture(encodedData); +} +TextureDeserializationResult DeserializeTexture(const EncodedTextureData& data) { + TextureDeserializationResult rv; + + stbi_set_flip_vertically_on_load(true); + const stbi_uc* stbi_encoded_data = reinterpret_cast(data.EncodedData.data()); + int data_length = static_cast(data.EncodedData.size()); + int width, height, nrChannels; + unsigned char* pixelData = stbi_load_from_memory( + stbi_encoded_data, + data_length, + &width, &height, + &nrChannels, + STBI_default ); + if (pixelData == nullptr) { + rv.erred = true; + rv.errors.emplace_back(std::format("Couldn't load \"{}\" from memory!", data.Name)); + DOA_LOG_WARNING("Couldn't load \"%s\" from memory!", data.Name.c_str()); + } else { + rv.deserializedTexture.Name = data.Name; + rv.deserializedTexture.Width = width; + rv.deserializedTexture.Height = height; + rv.deserializedTexture.Channels = nrChannels; + if (nrChannels == 1) { + rv.deserializedTexture.Format = DataFormat::R8; + } else if (nrChannels == 2) { + rv.deserializedTexture.Format = DataFormat::RG8; + } else if (nrChannels == 3) { + rv.deserializedTexture.Format = DataFormat::RGB8; + } else if (nrChannels == 4) { + rv.deserializedTexture.Format = DataFormat::RGBA8; + } else { + std::unreachable(); + } + rv.deserializedTexture.PixelData.resize(width * height * nrChannels); + for (size_t i = 0; i < rv.deserializedTexture.PixelData.size(); i++) { + rv.deserializedTexture.PixelData[i] = static_cast(pixelData[i]); + } + } + + stbi_image_free(pixelData); + return rv; } diff --git a/Engine/TextureDeserializer.hpp b/Engine/TextureDeserializer.hpp index a5ca846c..eb6ce3f6 100644 --- a/Engine/TextureDeserializer.hpp +++ b/Engine/TextureDeserializer.hpp @@ -1,8 +1,17 @@ #pragma once +#include +#include + +TextureEncoding ExtToEncoding(const std::string_view ext) noexcept; + struct FNode; -struct Texture; -struct EncodedTextureData; -Texture DeserializeTexture(const FNode& file); -Texture DeserializeTexture(const EncodedTextureData& data); +struct TextureDeserializationResult { + bool erred{ false }; + std::vector errors{}; + Texture deserializedTexture; +}; + +TextureDeserializationResult DeserializeTexture(const FNode& file); +TextureDeserializationResult DeserializeTexture(const EncodedTextureData& data); diff --git a/Engine/TextureSerializer.cpp b/Engine/TextureSerializer.cpp index 6cbba166..c3a81408 100644 --- a/Engine/TextureSerializer.cpp +++ b/Engine/TextureSerializer.cpp @@ -1,48 +1,47 @@ -#include "TextureSerializer.hpp" +#include -#include -#include #include +#include #include static void WriteFunction(void* ctx, void* data, int size) { - std::span pixels{ reinterpret_cast(data), static_cast(size) }; + std::span pixels{ reinterpret_cast(data), static_cast(size) }; EncodedTextureData* etd = reinterpret_cast(ctx); - etd->data.reserve(size); + etd->EncodedData.reserve(size); - std::copy(pixels.begin(), pixels.end(), etd->data.begin()); + std::ranges::copy(pixels, etd->EncodedData.begin()); } EncodedTextureData SerializeTexture(const Texture& texture, TextureEncoding encoding) { stbi_flip_vertically_on_write(true); EncodedTextureData rv; - rv.name = texture.Name(); - rv.hasTransparency = texture.HasTransparency(); + rv.Name = texture.Name; + rv.HasTransparency = texture.HasTransparency(); - int texW = static_cast(texture.Width()); - int texH = static_cast(texture.Height()); - int stride = rv.hasTransparency ? 4 : 3; - auto pixel = Texture::RequestPixelDataOf(texture); + int texW = static_cast(texture.Width); + int texH = static_cast(texture.Height); + int stride = texture.Channels; + auto& pixel = texture.PixelData; switch (encoding) { - case TextureEncoding::PNG: - stbi_write_png_to_func( - WriteFunction, &rv, texW, texH, stride, pixel.data(), texH * stride); + using enum TextureEncoding; + case PNG: + stbi_write_png_to_func(WriteFunction, &rv, texW, texH, stride, pixel.data(), texH * stride); break; - case TextureEncoding::BMP: + case BMP: stbi_write_bmp_to_func(WriteFunction, &rv, texW, texH, stride, pixel.data()); break; - case TextureEncoding::TGA: + case TGA: stbi_write_tga_to_func(WriteFunction, &rv, texW, texH, stride, pixel.data()); break; - case TextureEncoding::JPG: + case JPG: stbi_write_jpg_to_func(WriteFunction, &rv, texW, texH, stride, pixel.data(), 100); break; } - rv.encoding = encoding; + rv.Encoding = encoding; return rv; } diff --git a/Engine/TextureSerializer.hpp b/Engine/TextureSerializer.hpp index 74497932..0bba1488 100644 --- a/Engine/TextureSerializer.hpp +++ b/Engine/TextureSerializer.hpp @@ -1,5 +1,5 @@ #pragma once -#include "Texture.hpp" +#include EncodedTextureData SerializeTexture(const Texture& texture, TextureEncoding encoding = TextureEncoding::PNG); diff --git a/Engine/TransformComponent.cpp b/Engine/TransformComponent.cpp index 235b09a0..6febf147 100644 --- a/Engine/TransformComponent.cpp +++ b/Engine/TransformComponent.cpp @@ -1,138 +1,106 @@ -#include "TransformComponent.hpp" +#include -#include "Scene.hpp" -#include "ParentComponent.hpp" -#include "ChildComponent.hpp" -#include "Core.hpp" +#include +#include +#include +#include + +#include TransformComponent::TransformComponent(const Entity owner) noexcept : entity(owner) {} -TransformComponent::TransformComponent(const Entity owner, const glm::mat4& matrix) noexcept : - entity(owner), - localMatrix(matrix) { - Decompose(localMatrix, localTranslation, localRotation, localScale); -} +glm::vec3 TransformComponent::ComputeWorldTranslation(const Entity entity, const Scene& scene) { + glm::vec3 translation{}; + Entity currentEntity = entity; + while (true) { + translation += scene.GetComponent(currentEntity).GetLocalTranslation(); -void TransformComponent::InsertTranslation(glm::mat4& m, const glm::vec3& translation) { - m[3] = glm::vec4(translation, 1.0f); -} - -void TransformComponent::InsertRotation(glm::mat4& m, const glm::quat& rotation) { - glm::quat orig = ExtractRotation(m); - m *= glm::toMat4(glm::inverse(orig)); - m *= glm::toMat4(rotation); -} + if (!scene.HasComponent(currentEntity)) { + break; + } -void TransformComponent::InsertScale(glm::mat4& m, const glm::vec3& scale) { - glm::vec3 orig = ExtractScale(m); - for (int i = 0; i < 3; i++) { - orig[i] = 1 / orig[i]; + currentEntity = scene.GetComponent(currentEntity).GetParent(); } - m *= glm::scale(orig); - m *= glm::scale(scale); -} -void TransformComponent::Compose(glm::mat4& m, const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale) { - glm::mat4 T = glm::translate(translation); - glm::mat4 R = glm::toMat4(rotation); - glm::mat4 S = glm::scale(scale); - m = T * R * S; + return translation; } +glm::quat TransformComponent::ComputeWorldRotation(const Entity entity, const Scene& scene) { + glm::quat rotation{ glm::quat_identity() }; + Entity currentEntity = entity; + while (true) { + rotation = scene.GetComponent(currentEntity).GetLocalRotation() * rotation; -glm::vec3 TransformComponent::ExtractTranslation(const glm::mat4& m) { - return glm::vec3(m[3]); -} + if (!scene.HasComponent(currentEntity)) { + break; + } -glm::quat TransformComponent::ExtractRotation(const glm::mat4& m) { - glm::vec3 scale; - for (int i = 0; i < 3; i++) { - scale[i] = glm::length(glm::vec3(m[i])); + currentEntity = scene.GetComponent(currentEntity).GetParent(); } - const glm::mat3 rotMtx( - glm::vec3(m[0]) / scale[0], - glm::vec3(m[1]) / scale[1], - glm::vec3(m[2]) / scale[2] - ); - - return(glm::quat_cast(rotMtx)); + return rotation; } +glm::vec3 TransformComponent::ComputeLossyScale(const Entity entity, const Scene& scene) { + glm::vec3 scale{ 1.0f, 1.0f, 1.0f }; + Entity currentEntity = entity; + + while (true) { + scale *= scene.GetComponent(currentEntity).GetLocalScale(); + + if (!scene.HasComponent(currentEntity)) { + break; + } -glm::vec3 TransformComponent::ExtractScale(const glm::mat4& m) { - glm::vec3 scale; - for (int i = 0; i < 3; i++) { - scale[i] = glm::length(glm::vec3(m[i])); + currentEntity = scene.GetComponent(currentEntity).GetParent(); } - return scale; -} -void TransformComponent::Decompose(const glm::mat4& m, glm::vec3& tra, glm::quat& rot, glm::vec3& scale) { - tra = ExtractTranslation(m); - rot = ExtractRotation(m); - scale = ExtractScale(m); + return scale; } +glm::mat4 TransformComponent::ComputeWorldMatrix(const Entity entity, const Scene& scene) { + glm::mat4 worldMatrix{ glm::identity() }; + Entity currentEntity = entity; -Entity TransformComponent::GetEntity() const { return entity; } + while (true) { + const TransformComponent& transform = scene.GetComponent(currentEntity); + glm::mat4 localMatrix = Compose(transform.GetLocalTranslation(), transform.GetLocalRotation(), transform.GetLocalScale()); + worldMatrix = localMatrix * worldMatrix; -glm::vec3 TransformComponent::GetWorldTranslation() const { return ExtractTranslation(GetWorldMatrix()); } -void TransformComponent::SetWorldTranslation(glm::vec3 worldTranslation) { - Scene& scene = Scene::GetLoadedScene(); + if (!scene.HasComponent(currentEntity)) { + break; + } - glm::vec3 local = worldTranslation; - if (scene.HasComponent(entity)) { - auto parent = scene.GetComponent(entity).GetParent(); - local -= scene.GetComponent(parent).GetWorldTranslation(); + currentEntity = scene.GetComponent(currentEntity).GetParent(); } - SetLocalTranslation(local); -} - -glm::quat TransformComponent::GetWorldRotation() const { return ExtractRotation(GetWorldMatrix()); } -void TransformComponent::SetWorldRotation(glm::quat worldRotation) { - Scene& scene = Scene::GetLoadedScene(); - glm::quat local = worldRotation; - if (scene.HasComponent(entity)) { - auto parent = scene.GetComponent(entity).GetParent(); - local *= glm::inverse(scene.GetComponent(parent).GetWorldRotation()); - } - SetLocalRotation(local); + return worldMatrix; } -glm::vec3 TransformComponent::GetLossyScale() const { return ExtractScale(GetWorldMatrix()); } - -glm::mat4 TransformComponent::GetWorldMatrix() const { - Scene& scene = Scene::GetLoadedScene(); +glm::mat4 TransformComponent::Compose(glm::vec3 translation, glm::quat rotation, glm::vec3 scale) { + glm::mat4 T = glm::translate(translation); + glm::mat4 R = glm::toMat4(rotation); + glm::mat4 S = glm::scale(scale); + return T * R * S; +} +void TransformComponent::Decompose(const glm::mat4& matrix, glm::vec3* translation, glm::quat* rotation, glm::vec3* scale) { + glm::vec3 l_translation; + glm::quat l_rotation; + glm::vec3 l_scale; + glm::vec3 skew; + glm::vec4 perspective; + glm::decompose(matrix, l_scale, l_rotation, l_translation, skew, perspective); - glm::mat4 transform = glm::mat4(1.0f); // identity - if (scene.HasComponent(entity)) { - auto parent = scene.GetComponent(entity).GetParent(); - transform = scene.GetComponent(parent).GetLocalMatrix(); - } - transform = transform * GetLocalMatrix(); - return transform; + if (translation) { *translation = l_translation; } + if (rotation) { *rotation = l_rotation; } + if (scale) { *scale = l_scale; } } +Entity TransformComponent::GetEntity() const { return entity; } + glm::vec3 TransformComponent::GetLocalTranslation() const { return localTranslation; } -void TransformComponent::SetLocalTranslation(glm::vec3 localTranslation) { - InsertTranslation(localMatrix, localTranslation); - this->localTranslation = localTranslation; -} +void TransformComponent::SetLocalTranslation(glm::vec3 localTranslation) { this->localTranslation = localTranslation; } glm::quat TransformComponent::GetLocalRotation() const { return localRotation; } -void TransformComponent::SetLocalRotation(glm::quat localRotation) { - InsertRotation(localMatrix, localRotation); - this->localRotation = localRotation; -} +void TransformComponent::SetLocalRotation(glm::quat localRotation) { this->localRotation = localRotation; } glm::vec3 TransformComponent::GetLocalScale() const { return localScale; } -void TransformComponent::SetLocalScale(glm::vec3 localScale) { - InsertScale(localMatrix, localScale); - this->localScale = localScale; -} - -glm::mat4 TransformComponent::GetLocalMatrix() const { return localMatrix; } -void TransformComponent::SetLocalMatrix(glm::mat4 localMatrix) { - this->localMatrix = localMatrix; - Decompose(localMatrix, localTranslation, localRotation, localScale); -} +void TransformComponent::SetLocalScale(glm::vec3 localScale) { this->localScale = localScale; } \ No newline at end of file diff --git a/Engine/TransformComponent.hpp b/Engine/TransformComponent.hpp index 991a81ab..0cec1220 100644 --- a/Engine/TransformComponent.hpp +++ b/Engine/TransformComponent.hpp @@ -6,36 +6,24 @@ #include #include #include -#include #include +struct Scene; + struct TransformComponent { explicit TransformComponent(const Entity owner) noexcept; - TransformComponent(const Entity owner, const glm::mat4& matrix) noexcept; - static void InsertTranslation(glm::mat4& m, const glm::vec3& translation); - static void InsertRotation(glm::mat4& m, const glm::quat& rotation); - static void InsertScale(glm::mat4& m, const glm::vec3& scale); - static void Compose(glm::mat4& m, const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale); + static glm::vec3 ComputeWorldTranslation(const Entity entity, const Scene& scene); + static glm::quat ComputeWorldRotation(const Entity entity, const Scene& scene); + static glm::vec3 ComputeLossyScale(const Entity entity, const Scene& scene); + static glm::mat4 ComputeWorldMatrix(const Entity entity, const Scene& scene); - static glm::vec3 ExtractTranslation(const glm::mat4& m); - static glm::quat ExtractRotation(const glm::mat4& m); - static glm::vec3 ExtractScale(const glm::mat4& m); - static void Decompose(const glm::mat4& m, glm::vec3& pos, glm::quat& rot, glm::vec3& scale); + static glm::mat4 Compose(glm::vec3 translation, glm::quat rotation, glm::vec3 scale); + static void Decompose(const glm::mat4& matrix, glm::vec3* translation, glm::quat* rotation, glm::vec3* scale); Entity GetEntity() const; - glm::vec3 GetWorldTranslation() const; - void SetWorldTranslation(glm::vec3 worldTranslation); - - glm::quat GetWorldRotation() const; - void SetWorldRotation(glm::quat worldRotation); - - glm::vec3 GetLossyScale() const; - - glm::mat4 GetWorldMatrix() const; - glm::vec3 GetLocalTranslation() const; void SetLocalTranslation(glm::vec3 localTranslation); @@ -45,14 +33,10 @@ struct TransformComponent { glm::vec3 GetLocalScale() const; void SetLocalScale(glm::vec3 localScale); - glm::mat4 GetLocalMatrix() const; - void SetLocalMatrix(glm::mat4 localMatrix); - private: Entity entity; - glm::vec3 localTranslation{ glm::vec3(0, 0, 0) }; + glm::vec3 localTranslation{ 0, 0, 0 }; glm::quat localRotation{ glm::quat_identity() }; - glm::vec3 localScale{ glm::vec3(1, 1, 1) }; - glm::mat4 localMatrix{ glm::identity() }; + glm::vec3 localScale{ 1, 1, 1 }; }; \ No newline at end of file diff --git a/Engine/TypedefsAndConstants.hpp b/Engine/TypedefsAndConstants.hpp deleted file mode 100644 index 667c9ade..00000000 --- a/Engine/TypedefsAndConstants.hpp +++ /dev/null @@ -1,160 +0,0 @@ -#pragma once - -#include - -#include -#include -#include - -constexpr const char* GLSL_VERSION = "#version 430"; -constexpr int OPGL_VERSION_MAJ = 3; -constexpr int OPGL_VERSION_MIN = 3; - -typedef GLuint VAO; -typedef GLuint VAOBindSlot; -typedef GLuint VBO; -typedef GLuint EBO; -typedef GLuint FBO; -typedef GLuint TEX; -typedef GLuint RBO; -typedef GLuint ProgramID; -typedef GLint UniformLocation; - -typedef std::function ImGuiFunction; - -/* This is taken from glfw3.h, if that wasn't obvious. They only exist to be registered to Angel Script. */ -static int KEY_UNKNOWN = GLFW_KEY_UNKNOWN; -static int KEY_SPACE = GLFW_KEY_SPACE; -static int KEY_APOSTROPHE = GLFW_KEY_APOSTROPHE; /* ' */ -static int KEY_COMMA = GLFW_KEY_COMMA; /* , */ -static int KEY_MINUS = GLFW_KEY_MINUS; /* - */ -static int KEY_PERIOD = GLFW_KEY_PERIOD; /* . */ -static int KEY_SLASH = GLFW_KEY_SLASH; /* / */ -static int KEY_0 = GLFW_KEY_0; -static int KEY_1 = GLFW_KEY_1; -static int KEY_2 = GLFW_KEY_2; -static int KEY_3 = GLFW_KEY_3; -static int KEY_4 = GLFW_KEY_4; -static int KEY_5 = GLFW_KEY_5; -static int KEY_6 = GLFW_KEY_6; -static int KEY_7 = GLFW_KEY_7; -static int KEY_8 = GLFW_KEY_8; -static int KEY_9 = GLFW_KEY_9; -static int KEY_SEMICOLON = GLFW_KEY_SEMICOLON; /* ; */ -static int KEY_EQUAL = GLFW_KEY_EQUAL; /* = */ -static int KEY_A = GLFW_KEY_A; -static int KEY_B = GLFW_KEY_B; -static int KEY_C = GLFW_KEY_C; -static int KEY_D = GLFW_KEY_D; -static int KEY_E = GLFW_KEY_E; -static int KEY_F = GLFW_KEY_F; -static int KEY_G = GLFW_KEY_G; -static int KEY_H = GLFW_KEY_H; -static int KEY_I = GLFW_KEY_I; -static int KEY_J = GLFW_KEY_J; -static int KEY_K = GLFW_KEY_K; -static int KEY_L = GLFW_KEY_L; -static int KEY_M = GLFW_KEY_M; -static int KEY_N = GLFW_KEY_N; -static int KEY_O = GLFW_KEY_O; -static int KEY_P = GLFW_KEY_P; -static int KEY_Q = GLFW_KEY_Q; -static int KEY_R = GLFW_KEY_R; -static int KEY_S = GLFW_KEY_S; -static int KEY_T = GLFW_KEY_T; -static int KEY_U = GLFW_KEY_U; -static int KEY_V = GLFW_KEY_V; -static int KEY_W = GLFW_KEY_W; -static int KEY_X = GLFW_KEY_X; -static int KEY_Y = GLFW_KEY_Y; -static int KEY_Z = GLFW_KEY_Z; -static int KEY_LEFT_BRACKET = GLFW_KEY_LEFT_BRACKET; /* [ */ -static int KEY_BACKSLASH = GLFW_KEY_BACKSLASH; /* \ */ -static int KEY_RIGHT_BRACKET = GLFW_KEY_RIGHT_BRACKET; /* ] */ -static int KEY_GRAVE_ACCENT = GLFW_KEY_GRAVE_ACCENT; /* ` */ -static int KEY_WORLD_1 = GLFW_KEY_WORLD_1; /* non-US #1 */ -static int KEY_WORLD_2 = GLFW_KEY_WORLD_2; /* non-US #2 */ -static int KEY_ESCAPE = GLFW_KEY_ESCAPE; -static int KEY_ENTER = GLFW_KEY_ENTER; -static int KEY_TAB = GLFW_KEY_TAB; -static int KEY_BACKSPACE = GLFW_KEY_BACKSPACE; -static int KEY_INSERT = GLFW_KEY_INSERT; -static int KEY_DELETE = GLFW_KEY_DELETE; -static int KEY_RIGHT = GLFW_KEY_RIGHT; -static int KEY_LEFT = GLFW_KEY_LEFT; -static int KEY_DOWN = GLFW_KEY_DOWN; -static int KEY_UP = GLFW_KEY_UP; -static int KEY_PAGE_UP = GLFW_KEY_PAGE_UP; -static int KEY_PAGE_DOWN = GLFW_KEY_PAGE_DOWN; -static int KEY_HOME = GLFW_KEY_HOME; -static int KEY_END = GLFW_KEY_END; -static int KEY_CAPS_LOCK = GLFW_KEY_CAPS_LOCK; -static int KEY_SCROLL_LOCK = GLFW_KEY_SCROLL_LOCK; -static int KEY_NUM_LOCK = GLFW_KEY_NUM_LOCK; -static int KEY_PRINT_SCREEN = GLFW_KEY_PRINT_SCREEN; -static int KEY_PAUSE = GLFW_KEY_PAUSE; -static int KEY_F1 = GLFW_KEY_F1; -static int KEY_F2 = GLFW_KEY_F2; -static int KEY_F3 = GLFW_KEY_F3; -static int KEY_F4 = GLFW_KEY_F4; -static int KEY_F5 = GLFW_KEY_F5; -static int KEY_F6 = GLFW_KEY_F6; -static int KEY_F7 = GLFW_KEY_F7; -static int KEY_F8 = GLFW_KEY_F8; -static int KEY_F9 = GLFW_KEY_F9; -static int KEY_F10 = GLFW_KEY_F10; -static int KEY_F11 = GLFW_KEY_F11; -static int KEY_F12 = GLFW_KEY_F12; -static int KEY_F13 = GLFW_KEY_F13; -static int KEY_F14 = GLFW_KEY_F14; -static int KEY_F15 = GLFW_KEY_F15; -static int KEY_F16 = GLFW_KEY_F16; -static int KEY_F17 = GLFW_KEY_F17; -static int KEY_F18 = GLFW_KEY_F18; -static int KEY_F19 = GLFW_KEY_F19; -static int KEY_F20 = GLFW_KEY_F20; -static int KEY_F21 = GLFW_KEY_F21; -static int KEY_F22 = GLFW_KEY_F22; -static int KEY_F23 = GLFW_KEY_F23; -static int KEY_F24 = GLFW_KEY_F24; -static int KEY_F25 = GLFW_KEY_F25; -static int KEY_KP_0 = GLFW_KEY_KP_0; -static int KEY_KP_1 = GLFW_KEY_KP_1; -static int KEY_KP_2 = GLFW_KEY_KP_2; -static int KEY_KP_3 = GLFW_KEY_KP_3; -static int KEY_KP_4 = GLFW_KEY_KP_4; -static int KEY_KP_5 = GLFW_KEY_KP_5; -static int KEY_KP_6 = GLFW_KEY_KP_6; -static int KEY_KP_7 = GLFW_KEY_KP_7; -static int KEY_KP_8 = GLFW_KEY_KP_8; -static int KEY_KP_9 = GLFW_KEY_KP_9; -static int KEY_KP_DECIMAL = GLFW_KEY_KP_DECIMAL; -static int KEY_KP_DIVIDE = GLFW_KEY_KP_DIVIDE; -static int KEY_KP_MULTIPLY = GLFW_KEY_KP_MULTIPLY; -static int KEY_KP_SUBTRACT = GLFW_KEY_KP_SUBTRACT; -static int KEY_KP_ADD = GLFW_KEY_KP_ADD; -static int KEY_KP_ENTER = GLFW_KEY_KP_ENTER; -static int KEY_KP_EQUAL = GLFW_KEY_KP_EQUAL; -static int KEY_LEFT_SHIFT = GLFW_KEY_LEFT_SHIFT; -static int KEY_LEFT_CONTROL = GLFW_KEY_LEFT_CONTROL; -static int KEY_LEFT_ALT = GLFW_KEY_LEFT_ALT; -static int KEY_LEFT_SUPER = GLFW_KEY_LEFT_SUPER; -static int KEY_RIGHT_SHIFT = GLFW_KEY_RIGHT_SHIFT; -static int KEY_RIGHT_CONTROL = GLFW_KEY_RIGHT_CONTROL; -static int KEY_RIGHT_ALT = GLFW_KEY_RIGHT_ALT; -static int KEY_RIGHT_SUPER = GLFW_KEY_RIGHT_SUPER; -static int KEY_MENU = GLFW_KEY_MENU; -constexpr int KEY_LAST = GLFW_KEY_LAST; - -static int MOUSE_BUTTON_1 = GLFW_MOUSE_BUTTON_1; -static int MOUSE_BUTTON_2 = GLFW_MOUSE_BUTTON_2; -static int MOUSE_BUTTON_3 = GLFW_MOUSE_BUTTON_3; -static int MOUSE_BUTTON_4 = GLFW_MOUSE_BUTTON_4; -static int MOUSE_BUTTON_5 = GLFW_MOUSE_BUTTON_5; -static int MOUSE_BUTTON_6 = GLFW_MOUSE_BUTTON_6; -static int MOUSE_BUTTON_7 = GLFW_MOUSE_BUTTON_7; -static int MOUSE_BUTTON_8 = GLFW_MOUSE_BUTTON_8; -constexpr int MOUSE_BUTTON_LAST = GLFW_MOUSE_BUTTON_LAST; -static int MOUSE_BUTTON_LEFT = GLFW_MOUSE_BUTTON_LEFT; -static int MOUSE_BUTTON_RIGHT = GLFW_MOUSE_BUTTON_RIGHT; -static int MOUSE_BUTTON_MIDDLE = GLFW_MOUSE_BUTTON_MIDDLE; diff --git a/Engine/VertexArray.cpp b/Engine/VertexArray.cpp deleted file mode 100644 index 49b1a292..00000000 --- a/Engine/VertexArray.cpp +++ /dev/null @@ -1,100 +0,0 @@ -#include - -#include - -VertexArray::VertexArray() noexcept { AllocateGPU(); } -VertexArray::~VertexArray() noexcept { DeallocateGPU(); } -VertexArray::VertexArray(const VertexArray& other) noexcept : VertexArray() { - for (size_t i = 0; i < other.buffers.size(); i++) { - BindVertexAttribBuffer(other.buffers[i], other.layouts[i]); - } - BindElementBuffer(other.elemBuffer); -} -VertexArray::VertexArray(VertexArray&& other) noexcept { *this = std::move(other); } -VertexArray& VertexArray::operator=(const VertexArray& other) noexcept { - // TODO instead of dealloc / alloc, reset everything in VAO? - DeallocateGPU(); - AllocateGPU(); - buffers.clear(); - layouts.clear(); - for (size_t i = 0; i < other.buffers.size(); i++) { - BindVertexAttribBuffer(other.buffers[i], other.layouts[i]); - } - BindElementBuffer(other.elemBuffer); - return *this; -} -VertexArray& VertexArray::operator=(VertexArray&& other) noexcept { - object = std::exchange(other.object, object); - buffers = std::move(other.buffers); - layouts = std::move(other.layouts); - elemBuffer = std::move(other.elemBuffer); - return *this; -} - -void VertexArray::BindVertexAttribBuffer(const Buffer& buffer, const VertexAttribLayout& layout) noexcept { - auto bindingIndx { static_cast(buffers.size()) }; - - const Buffer& buf{ buffers.emplace_back(buffer) }; - VertexAttribLayout& lay { layouts.emplace_back(layout) }; - - glVertexArrayVertexBuffer(object, bindingIndx, buf.object, 0, lay.stride); - for (size_t i = 0; i < lay.elements.size(); i++) { - auto&& index{ static_cast(i) }; - const VertexAttribLayout::Element& elem { lay.elements[i] }; - - glEnableVertexArrayAttrib(object, index); - if (elem.type == GL_INT || - elem.type == GL_BYTE || - elem.type == GL_SHORT || - elem.type == GL_UNSIGNED_INT || - elem.type == GL_UNSIGNED_BYTE || - elem.type == GL_UNSIGNED_SHORT) { - glVertexArrayAttribIFormat(object, index, elem.count, elem.type, lay.offsets[i]); - } else if (elem.type == GL_DOUBLE) { - glVertexArrayAttribLFormat(object, index, elem.count, elem.type, lay.offsets[i]); - } else { - glVertexArrayAttribFormat(object, index, elem.count, elem.type, elem.isNormalized, lay.offsets[i]); - } - glVertexArrayAttribBinding(object, index, bindingIndx); - } -} -void VertexArray::BindVertexAttribBuffer(Buffer&& buffer, VertexAttribLayout&& layout) noexcept { - auto bindingIndx { static_cast(buffers.size()) }; - - const Buffer& buf{ buffers.emplace_back(std::move(buffer)) }; - VertexAttribLayout& lay { layouts.emplace_back(std::move(layout)) }; - - glVertexArrayVertexBuffer(object, bindingIndx, buf.object, 0, lay.stride); - for (size_t i = 0; i < lay.elements.size(); i++) { - auto&& index{ static_cast(i) }; - const VertexAttribLayout::Element& elem { lay.elements[i] }; - - glEnableVertexArrayAttrib(object, index); - if (elem.type == GL_INT || - elem.type == GL_BYTE || - elem.type == GL_SHORT || - elem.type == GL_UNSIGNED_INT || - elem.type == GL_UNSIGNED_BYTE || - elem.type == GL_UNSIGNED_SHORT) { - glVertexArrayAttribIFormat(object, index, elem.count, elem.type, lay.offsets[i]); - } else if (elem.type == GL_DOUBLE) { - glVertexArrayAttribLFormat(object, index, elem.count, elem.type, lay.offsets[i]); - } else { - glVertexArrayAttribFormat(object, index, elem.count, elem.type, elem.isNormalized, lay.offsets[i]); - } - glVertexArrayAttribBinding(object, index, bindingIndx); - } -} -void VertexArray::BindElementBuffer(const Buffer& buffer) noexcept { - elemBuffer = buffer; - glVertexArrayElementBuffer(object, elemBuffer.object); -} -void VertexArray::BindElementBuffer(Buffer&& buffer) noexcept { - elemBuffer = std::move(buffer); - glVertexArrayElementBuffer(object, elemBuffer.object); -} - -void VertexArray::Use() const noexcept { glBindVertexArray(object); } - -void VertexArray::AllocateGPU() noexcept { glCreateVertexArrays(1, &object); } -void VertexArray::DeallocateGPU() noexcept { glDeleteVertexArrays(1, &object); } \ No newline at end of file diff --git a/Engine/VertexArray.hpp b/Engine/VertexArray.hpp deleted file mode 100644 index 9563b2fe..00000000 --- a/Engine/VertexArray.hpp +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include - -#include - -#include -#include - -struct VertexArray { - - VertexArray() noexcept; - ~VertexArray() noexcept; - VertexArray(const VertexArray& other) noexcept; - VertexArray(VertexArray&& other) noexcept; - VertexArray& operator=(const VertexArray& other) noexcept; - VertexArray& operator=(VertexArray&& other) noexcept; - - void BindVertexAttribBuffer(const Buffer& buffer, const VertexAttribLayout& layout) noexcept; - void BindVertexAttribBuffer(Buffer&& buffer, VertexAttribLayout&& layout) noexcept; - void BindElementBuffer(const Buffer& buffer) noexcept; - void BindElementBuffer(Buffer&& buffer) noexcept; - - void Use() const noexcept; - -private: - GLuint object{}; - std::vector buffers{}; - std::vector layouts{}; - Buffer elemBuffer{}; - - void AllocateGPU() noexcept; - void DeallocateGPU() noexcept; -}; \ No newline at end of file diff --git a/Engine/VertexAttribLayout.cpp b/Engine/VertexAttribLayout.cpp deleted file mode 100644 index fe867e6c..00000000 --- a/Engine/VertexAttribLayout.cpp +++ /dev/null @@ -1,65 +0,0 @@ -#include - -template<> -void VertexAttribLayout::Define(size_t count, bool isNormalized) { - static size_t size{ sizeof(float_t) }; - elements.emplace_back(GL_FLOAT, count, isNormalized); - offsets.emplace_back(offsets.back() + count * size); - stride += static_cast(count * size); -} - -template<> -void VertexAttribLayout::Define(size_t count, bool isNormalized) { - static size_t size{ sizeof(double_t) }; - elements.emplace_back(GL_DOUBLE, count, isNormalized); - offsets.emplace_back(offsets.back() + count * size); - stride += static_cast(count * size); -} - -template<> -void VertexAttribLayout::Define(size_t count, bool isNormalized) { - static size_t size{ sizeof(int8_t) }; - elements.emplace_back(GL_BYTE, count, isNormalized); - offsets.emplace_back(offsets.back() + count * size); - stride += static_cast(count * size); -} - -template<> -void VertexAttribLayout::Define(size_t count, bool isNormalized) { - static size_t size{ sizeof(uint8_t) }; - elements.emplace_back(GL_UNSIGNED_BYTE, count, isNormalized); - offsets.emplace_back(offsets.back() + count * size); - stride += static_cast(count * size); -} - -template<> -void VertexAttribLayout::Define(size_t count, bool isNormalized) { - static size_t size{ sizeof(int16_t) }; - elements.emplace_back(GL_SHORT, count, isNormalized); - offsets.emplace_back(offsets.back() + count * size); - stride += static_cast(count * size); -} - -template<> -void VertexAttribLayout::Define(size_t count, bool isNormalized) { - static size_t size{ sizeof(uint16_t) }; - elements.emplace_back(GL_UNSIGNED_SHORT, count, isNormalized); - offsets.emplace_back(offsets.back() + count * size); - stride += static_cast(count * size); -} - -template<> -void VertexAttribLayout::Define(size_t count, bool isNormalized) { - static size_t size{ sizeof(int32_t) }; - elements.emplace_back(GL_INT, count, isNormalized); - offsets.emplace_back(offsets.back() + count * size); - stride += static_cast(count * size); -} - -template<> -void VertexAttribLayout::Define(size_t count, bool isNormalized) { - static size_t size{ sizeof(uint32_t) }; - elements.emplace_back(GL_UNSIGNED_INT, count, isNormalized); - offsets.emplace_back(offsets.back() + count * size); - stride += static_cast(count * size); -} \ No newline at end of file diff --git a/Engine/VertexAttribLayout.hpp b/Engine/VertexAttribLayout.hpp deleted file mode 100644 index 3bd47109..00000000 --- a/Engine/VertexAttribLayout.hpp +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include -#include - -#include - -struct VertexAttribLayout { - - struct Element { - GLuint type{}; - GLuint count{}; - GLboolean isNormalized{}; - }; - - template - void Define([[maybe_unused]] size_t count, [[maybe_unused]] bool isNormalized = false) {} - -private: - GLuint stride{}; - std::vector elements{}; - std::vector offsets{ 0 }; - - friend struct VertexArray; -}; - -template<> -void VertexAttribLayout::Define(size_t count, bool isNormalized); -template<> -void VertexAttribLayout::Define(size_t count, bool isNormalized); -template<> -void VertexAttribLayout::Define(size_t count, bool isNormalized); -template<> -void VertexAttribLayout::Define(size_t count, bool isNormalized); -template<> -void VertexAttribLayout::Define(size_t count, bool isNormalized); -template<> -void VertexAttribLayout::Define(size_t count, bool isNormalized); -template<> -void VertexAttribLayout::Define(size_t count, bool isNormalized); -template<> -void VertexAttribLayout::Define(size_t count, bool isNormalized); \ No newline at end of file diff --git a/Engine/Window.cpp b/Engine/Window.cpp index c5b4e0a2..24b18516 100644 --- a/Engine/Window.cpp +++ b/Engine/Window.cpp @@ -1,154 +1,3 @@ -#include "Window.hpp" +#include -#include -#include - -#include "Core.hpp" -#include "Input.hpp" -#include "ImGuiRenderer.hpp" - -const Resolution& Window::GetResolution() const { return _resolution; } -const Resolution& Window::GetContentResolution() const { return _contentResolution; } - -std::string Window::GetTitle() const { return _title; } -void Window::SetTitle(const std::string& title) { SetTitle(std::string(title)); } -void Window::SetTitle(std::string&& title) { - _title = std::move(title); - glfwSetWindowTitle(_glfwWindow, _title.c_str()); -} - -void Window::SetDecorated(bool isDecorated) const { - glfwHideWindow(_glfwWindow); - glfwSetWindowAttrib(_glfwWindow, GLFW_DECORATED, isDecorated ? GLFW_TRUE : GLFW_FALSE); - glfwShowWindow(_glfwWindow); -} - -PlatformWindow* Window::GetPlatformWindow() const { return _glfwWindow; } -PlatformUI* Window::GetPlatformUI() const { return _imGuiContext; } - -void Window::DisableCursor() const { glfwSetInputMode(_glfwWindow, GLFW_CURSOR, GLFW_CURSOR_DISABLED); } -void Window::HideCursor() const { glfwSetInputMode(_glfwWindow, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); } -void Window::EnableCursor() const { glfwSetInputMode(_glfwWindow, GLFW_CURSOR, GLFW_CURSOR_NORMAL); } - -void Window::Minimize() const { glfwIconifyWindow(_glfwWindow); } -void Window::Maximize() const { glfwMaximizeWindow(_glfwWindow); }; - -glm::vec2 Window::GetPosition() const { - int xpos, ypos; - glfwGetWindowPos(_glfwWindow, &xpos, &ypos); - return { xpos, ypos }; -} -void Window::SetPosition(glm::vec2 pos) const { - glfwSetWindowPos(_glfwWindow, pos.x, pos.y); -} -//----------------------------------------------------------------- - -WindowPtr Window::CreateWindow(Resolution resolution, const char* title, bool isFullscreen, const char* windowIcon) { - auto rv = std::unique_ptr>(new Window, DeleteWindow); - rv->_resolution = resolution; - rv->_title = title; - rv->_isFullscreen = isFullscreen; - -#ifdef DEBUG - glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); //TODO add support -#endif - - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5); - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - glfwWindowHint(GLFW_SAMPLES, 4); - rv->_glfwWindow = glfwCreateWindow(rv->_resolution.Width, rv->_resolution.Height, title, isFullscreen ? glfwGetPrimaryMonitor() : nullptr, nullptr); - glfwGetWindowSize(rv->_glfwWindow, &rv->_contentResolution.Width, &rv->_contentResolution.Height); - - glfwMakeContextCurrent(rv->_glfwWindow); - //glfwSwapInterval(0); // TODO VSYNC - - glfwSetFramebufferSizeCallback(rv->_glfwWindow, glfwWindowOnResize); - glfwSetKeyCallback(rv->_glfwWindow, glfwWindowOnKeyStateChange); - glfwSetMouseButtonCallback(rv->_glfwWindow, glfwWindowOnMouseButtonStateChange); - glfwSetCursorPosCallback(rv->_glfwWindow, glfwWindowOnMouseMove); - glfwSetScrollCallback(rv->_glfwWindow, glfwWindowOnMouseScroll); - - rv->_imGuiContext = ImGuiInit(rv->_glfwWindow); - ImGui::SetCurrentContext(rv->_imGuiContext); - -#pragma region Window Icon Initialization - if (windowIcon) { - std::vector windowIcons{}; - windowIcons.resize(6); - - std::string str; - str.reserve(256); - - str.append(windowIcon); - str.append("-16_x_16.png"); - windowIcons[0].pixels = stbi_load(str.c_str(), &windowIcons[0].width, &windowIcons[0].height, 0, 4); - - str.resize(0); - str.append(windowIcon); - str.append("-32_x_32.png"); - windowIcons[1].pixels = stbi_load(str.c_str(), &windowIcons[1].width, &windowIcons[1].height, 0, 4); - - str.resize(0); - str.append(windowIcon); - str.append("-48_x_48.png"); - windowIcons[2].pixels = stbi_load(str.c_str(), &windowIcons[2].width, &windowIcons[2].height, 0, 4); - - str.resize(0); - str.append(windowIcon); - str.append("-64_x_64.png"); - windowIcons[3].pixels = stbi_load(str.c_str(), &windowIcons[3].width, &windowIcons[3].height, 0, 4); - - str.resize(0); - str.append(windowIcon); - str.append("-128_x_128.png"); - windowIcons[4].pixels = stbi_load(str.c_str(), &windowIcons[4].width, &windowIcons[4].height, 0, 4); - - str.resize(0); - str.append(windowIcon); - str.append("-256_x_256.png"); - windowIcons[5].pixels = stbi_load(str.c_str(), &windowIcons[5].width, &windowIcons[5].height, 0, 4); - - glfwSetWindowIcon(rv->_glfwWindow, windowIcons.size(), windowIcons.data()); - ImGuiSetUpWindowIcons(std::move(windowIcons)); - } -#pragma endregion - - return rv; -} - -void Window::DeleteWindow(Window* window) { - ImGuiClean(); - glfwDestroyWindow(window->_glfwWindow); - glfwTerminate(); - - delete window; -} - -void Window::glfwWindowOnResize(GLFWwindow* window, int width, int height) { - static auto& Window = Core::GetCore()->GetWindow(); - Window->_resolution = { width, height }; - glfwGetWindowSize(Window->_glfwWindow, &Window->_contentResolution.Width, &Window->_contentResolution.Height); -} - -void Window::glfwWindowOnKeyStateChange(GLFWwindow* window, int key, int scancode, int action, int mods) { - static auto& input = Core::GetCore()->GetInput(); - input->keyboard.Keys[key] = action; -} - -void Window::glfwWindowOnMouseButtonStateChange(GLFWwindow* window, int button, int action, int mods) { - static auto& input = Core::GetCore()->GetInput(); - input->mouse.Buttons[button] = action; -} - -void Window::glfwWindowOnMouseMove(GLFWwindow* window, double xpos, double ypos) { - static auto& input = Core::GetCore()->GetInput(); - input->mouse.PosX = xpos; - input->mouse.PosY = ypos; -} - -void Window::glfwWindowOnMouseScroll(GLFWwindow* window, double xoffset, double yoffset) { - static auto& input = Core::GetCore()->GetInput(); - input->mouse.ScrollX = xoffset; - input->mouse.ScrollY = yoffset; -} +IWindow::~IWindow() noexcept = default; \ No newline at end of file diff --git a/Engine/Window.hpp b/Engine/Window.hpp index cbd7f3b6..ec4b8f97 100644 --- a/Engine/Window.hpp +++ b/Engine/Window.hpp @@ -1,68 +1,202 @@ #pragma once -#include -#include - -#include - -#include "TypedefsAndConstants.hpp" -#include "Resolution.hpp" - -struct Window; -struct Project; -struct GLFWwindow; -struct ImGuiContext; - -using WindowPtr = std::unique_ptr>; -using PlatformWindow = GLFWwindow; -using PlatformUI = ImGuiContext; - -struct Window { - - const Resolution& GetResolution() const; - const Resolution& GetContentResolution() const; - - std::string GetTitle() const; - void SetTitle(const std::string& title); - void SetTitle(std::string&& title); - - void SetDecorated(bool isDecorated) const; - - PlatformWindow* GetPlatformWindow() const; - PlatformUI* GetPlatformUI() const; - - void DisableCursor() const; - void HideCursor() const; - void EnableCursor() const; - - void Minimize() const; - void Maximize() const; +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +enum class WindowBackend { + GLFW, +#ifdef SDL_SUPPORT + SDL +#endif +}; - glm::vec2 GetPosition() const; - void SetPosition(glm::vec2 pos) const; +enum class CursorType { + Arrow, + IBeam, + XHair, + PointingHand, + ResizeEW, + ResizeNS, + ResizeNWSE, + ResizeNESW, + ResizeAll, + NotAllowed, +}; -private: - Resolution _resolution{}; - Resolution _contentResolution{}; - std::string _title{}; - bool _isFullscreen{ false }; - GLFWwindow* _glfwWindow{ nullptr }; - ImGuiContext* _imGuiContext{ nullptr }; +struct CursorData { + Resolution Size{}; + RawData Pixels{}; + Point HotSpot{}; +}; - Window() = default; - ~Window() = default; - Window(const Window&) = delete; - Window(Window&&) = delete; - Window& operator=(const Window&) = delete; - Window& operator=(Window&&) = delete; +struct WindowIconPack { + struct IconData { + Resolution Size{}; + RawData Pixels{}; + }; + std::vector Icons{}; +}; - friend struct Core; - static WindowPtr CreateWindow(Resolution resolution, const char* title, bool isFullscreen, const char* windowIcon); - static void DeleteWindow(Window* window); +struct ContextWindowCreationParams { + Resolution Size{ 1, 1 }; // defaults to the smallest possible window size + std::string_view Title{ "NeoDoa Window" }; + + bool IsFullScreen{ false }; + unsigned Monitor{}; // 0 is first monitor + + bool IsResizable{ false }; + bool IsVisible{ false }; + bool IsDecorated{ true }; + bool IsFocused{ true }; + bool IsAutoIconified{ true }; + bool IsAlwaysOnTop{ false }; + bool IsMaximized{ false }; + bool IsCursorCentered{ true }; + bool HasTransparentFrameBuffer{ false }; + bool HasFocusOnShow{ true }; + bool ShouldScaleToMonitor{ false }; + bool IsStereoscopic{ false }; + bool IssRGBCapable{ false }; + bool IsDoubleBuffered{ true }; + + int RedBits{ 8 }; + int GreenBits{ 8 }; + int BlueBits{ 8 }; + int AlphaBits{ 8 }; + int DepthBits{ 24 }; + int StencilBits{ 8 }; + + int Samples{ 0 }; + int RefreshRate{ -1 }; // -1 is don't care + + WindowIconPack IconPack{}; + CursorData ArrowCursor{}; + CursorData IBeamCursor{}; + CursorData XHairCursor{}; + CursorData PointingHandCursor{}; + CursorData ResizeEWCursor{}; + CursorData ResizeNSCursor{}; + CursorData ResizeNWSECursor{}; + CursorData ResizeNESWCursor{}; + CursorData ResizeAllCursor{}; + CursorData NotAllowedCursor{}; +}; - static void glfwWindowOnResize(GLFWwindow* window, int width, int height); - static void glfwWindowOnKeyStateChange(GLFWwindow* window, int key, int scancode, int action, int mods); - static void glfwWindowOnMouseButtonStateChange(GLFWwindow* window, int button, int action, int mods); - static void glfwWindowOnMouseMove(GLFWwindow* window, double xpos, double ypos); - static void glfwWindowOnMouseScroll(GLFWwindow* window, double width, double height); +struct IWindow { + + enum class Iconify { + Iconified, + Restored + }; + + enum class Focus { + FocusGained, + FocusLost + }; + + struct Events { + Event OnClose{}; + Event OnResize{}; + Event OnFrameBufferResize{}; + Event OnMove{}; + Event OnIconify{}; + Event OnFocus{}; + Event OnRefresh{}; + + Event OnKeyPress{}; + Event OnKeyRelease{}; + Event OnCharInput{}; + + Event OnMouseMove{}; + Event OnMouseEnter{}; + Event OnMouseLeave{}; + Event OnMousePress{}; + Event OnMouseRelease{}; + Event OnMouseScroll{}; + Event)> OnPathDrop{}; + } Events; + + virtual ~IWindow() noexcept = 0; + + virtual void SetResolution(Resolution resolution) noexcept = 0; + virtual Resolution GetResolution() const noexcept = 0; + virtual void SetContentResolution(Resolution resolution) noexcept = 0; + virtual Resolution GetContentResolution() const noexcept = 0; + + virtual std::string_view GetTitle() const noexcept = 0; + virtual void SetTitle(std::string_view title) noexcept = 0; + + virtual bool IsFullscreen() const noexcept = 0; + virtual void SetFullscreen(const IMonitor& monitor, const VideoMode& videoMode) noexcept = 0; + virtual void SetWindowed(const VideoMode& videoMode) noexcept = 0; + + virtual bool IsResizable() const noexcept = 0; + virtual void SetResizable(bool isResizable) noexcept = 0; + + virtual bool IsVisible() const noexcept = 0; + virtual void SetVisible(bool isVisible) noexcept = 0; + + virtual bool IsDecorated() const noexcept = 0; + virtual void SetDecorated(bool isDecorated) noexcept = 0; + + virtual bool HasFocus() const noexcept = 0; + virtual void Focus() noexcept = 0; + + virtual bool IsAutoIconified() const noexcept = 0; + virtual void SetAutoIconified(bool isAutoIconified) noexcept = 0; + + virtual bool IsAlwaysOnTop() const noexcept = 0; + virtual void SetAlwaysOnTop(bool isAlwaysOnTop) noexcept = 0; + + virtual void Minimize() const noexcept = 0; + virtual void Restore() const noexcept = 0; + virtual void Maximize() const noexcept = 0; + + virtual Point GetPosition() const noexcept = 0; + virtual void SetPosition(Point position) const noexcept = 0; + + virtual void ChangeCursor(CursorType type) noexcept = 0; + virtual void CaptureCursor() const noexcept = 0; + virtual void DisableCursor() const noexcept = 0; + virtual void HideCursor() const noexcept = 0; + virtual void EnableCursor() const noexcept = 0; + + virtual void SetIcon(const WindowIconPack& iconPack) noexcept = 0; + + virtual void Close() noexcept = 0; + virtual void PollEvents() const noexcept = 0; + virtual void SwapBuffers() const noexcept = 0; + virtual bool ShouldClose() const noexcept = 0; + + virtual WindowBackend GetPlatformBackend() const noexcept = 0; + virtual std::any GetPlatformWindowPointer() const noexcept = 0; + virtual void SetPlatformWindowPointerContext(std::any context) noexcept = 0; + virtual bool IsSoftwareRendererContextWindow() const noexcept = 0; + virtual bool IsOpenGLContextWindow() const noexcept = 0; + virtual bool IsVulkanContextWindow() const noexcept = 0; + virtual bool IsDirect3D12ContextWindow() const noexcept = 0; + virtual bool IsDirect3D11ContextWindow() const noexcept = 0; }; + +constexpr std::string_view ToString(WindowBackend backend) noexcept { + using enum WindowBackend; + switch (backend) { + case GLFW: return "GLFW"; +#ifdef SDL_SUPPORT + case SDL: return "SDL"; +#endif + } + std::unreachable(); +} + +std::ostream& operator<<(std::ostream& os, WindowBackend backend); \ No newline at end of file diff --git a/Engine/WindowGLFW.cpp b/Engine/WindowGLFW.cpp new file mode 100644 index 00000000..cc5f5bde --- /dev/null +++ b/Engine/WindowGLFW.cpp @@ -0,0 +1,556 @@ +#include + +#include +#include +#include + +#include + +#include + +void WindowGLFW::SetIconOfPlatformWindow(std::any platformWindow, const WindowIconPack& iconPack) noexcept { + if (iconPack.Icons.empty()) { return; } + + std::vector icons{}; + icons.reserve(iconPack.Icons.size()); + for (const WindowIconPack::IconData& data : iconPack.Icons) { + icons.emplace_back( + data.Size.Width, + data.Size.Height, + const_cast(reinterpret_cast(data.Pixels.data())) + ); + } + glfwSetWindowIcon(std::any_cast(platformWindow), static_cast(icons.size()), icons.data()); +} + +WindowGLFW::WindowGLFW(GraphicsBackend backend, const ContextWindowCreationParams& params) noexcept { + glfwWindowHint(GLFW_RESIZABLE, params.IsResizable ? GLFW_TRUE : GLFW_FALSE); + glfwWindowHint(GLFW_VISIBLE, params.IsVisible ? GLFW_TRUE : GLFW_FALSE); + glfwWindowHint(GLFW_DECORATED, params.IsDecorated ? GLFW_TRUE : GLFW_FALSE); + glfwWindowHint(GLFW_FLOATING, params.IsAlwaysOnTop ? GLFW_TRUE : GLFW_FALSE); + glfwWindowHint(GLFW_MAXIMIZED, params.IsMaximized ? GLFW_TRUE : GLFW_FALSE); + glfwWindowHint(GLFW_CENTER_CURSOR, params.IsCursorCentered ? GLFW_TRUE : GLFW_FALSE); + glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, params.HasTransparentFrameBuffer ? GLFW_TRUE : GLFW_FALSE); + glfwWindowHint(GLFW_FOCUS_ON_SHOW, params.HasFocusOnShow ? GLFW_TRUE : GLFW_FALSE); + glfwWindowHint(GLFW_SCALE_TO_MONITOR, params.ShouldScaleToMonitor ? GLFW_TRUE : GLFW_FALSE); + glfwWindowHint(GLFW_STEREO, params.IsStereoscopic ? GLFW_TRUE : GLFW_FALSE); + glfwWindowHint(GLFW_SRGB_CAPABLE, params.IssRGBCapable ? GLFW_TRUE : GLFW_FALSE); + glfwWindowHint(GLFW_DOUBLEBUFFER, params.IsDoubleBuffered ? GLFW_TRUE : GLFW_FALSE); + + glfwWindowHint(GLFW_RED_BITS, params.RedBits); + glfwWindowHint(GLFW_GREEN_BITS, params.GreenBits); + glfwWindowHint(GLFW_BLUE_BITS, params.BlueBits); + glfwWindowHint(GLFW_ALPHA_BITS, params.AlphaBits); + glfwWindowHint(GLFW_DEPTH_BITS, params.DepthBits); + glfwWindowHint(GLFW_STENCIL_BITS, params.StencilBits); + + glfwWindowHint(GLFW_SAMPLES, params.Samples); + glfwWindowHint(GLFW_REFRESH_RATE, params.RefreshRate); + + isSoftware = backend == GraphicsBackend::Software; +#ifdef OPENGL_4_6_SUPPORT + isOpenGL |= backend == GraphicsBackend::OpenGL4_6; + isOpenGL46 = backend == GraphicsBackend::OpenGL4_6; +#endif +#ifdef OPENGL_3_3_SUPPORT + isOpenGL |= backend == GraphicsBackend::OpenGL3_3; + isOpenGL33 = backend == GraphicsBackend::OpenGL3_3; +#endif +#ifdef VULKAN_SUPPORT + isVulkan = backend == GraphicsBackend::Vulkan; +#endif +#ifdef DIRECT3D_12_SUPPORT + isDirect3D12 = backend == GraphicsBackend::Direct3D12; +#endif +#ifdef DIRECT3D_11_SUPPORT + isDirect3D11 = backend == GraphicsBackend::Direct3D11; +#endif + + + if (isOpenGL) { + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); + glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API); + if (isOpenGL46) { + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6); + } else if (isOpenGL33) { + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + } else { + std::unreachable(); + } + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); +#ifdef DEBUG + glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); + glfwWindowHint(GLFW_CONTEXT_NO_ERROR, GLFW_FALSE); +#elif NDEBUG + glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_FALSE); + glfwWindowHint(GLFW_CONTEXT_NO_ERROR, GLFW_TRUE); +#else +#error "Neither DEBUG nor NDEBUG are defined!" +#endif + } + + GLFWmonitor* monitor{}; + if (params.IsFullScreen) { + int count; + GLFWmonitor** monitors = glfwGetMonitors(&count); + assert(params.Monitor < static_cast(count)); + monitor = monitors[params.Monitor]; + } + + glfwWindow = glfwCreateWindow(params.Size.Width, params.Size.Height, params.Title.data(), monitor, nullptr); + if (!glfwWindow) { + const char* errorMessage; + glfwGetError(&errorMessage); + DOA_LOG_FATAL("Failed to create GLFW window. Reason: %s", errorMessage); + std::unreachable(); + } + + glfwSetWindowUserPointer(glfwWindow, this); + if (isOpenGL) { + glfwMakeContextCurrent(glfwWindow); + } + + SetIcon(params.IconPack); + arrowCursor = CreateCursor(params.ArrowCursor, GLFW_ARROW_CURSOR); + iBeamCursor = CreateCursor(params.IBeamCursor, GLFW_IBEAM_CURSOR); + xHairCursor = CreateCursor(params.XHairCursor, GLFW_CROSSHAIR_CURSOR); + pointingHandCursor = CreateCursor(params.PointingHandCursor, GLFW_POINTING_HAND_CURSOR); + resizeEWCursor = CreateCursor(params.ResizeEWCursor, GLFW_RESIZE_EW_CURSOR); + resizeNSCursor = CreateCursor(params.ResizeNSCursor, GLFW_RESIZE_NS_CURSOR); + resizeNWSECursor = CreateCursor(params.ResizeNWSECursor, GLFW_RESIZE_NWSE_CURSOR); + resizeNESWCursor = CreateCursor(params.ResizeNESWCursor, GLFW_RESIZE_NESW_CURSOR); + resizeAllCursor = CreateCursor(params.ResizeAllCursor, GLFW_RESIZE_ALL_CURSOR); + notAllowedCursor = CreateCursor(params.NotAllowedCursor, GLFW_NOT_ALLOWED_CURSOR); + + glfwSetWindowCloseCallback(glfwWindow, [](GLFWwindow* window) { + static_cast(glfwGetWindowUserPointer(window))->Events.OnClose(); + }); + glfwSetWindowSizeCallback(glfwWindow, [](GLFWwindow* window, int width, int height) { + static_cast(glfwGetWindowUserPointer(window))->Events.OnResize({ + static_cast(width), + static_cast(height) + }); + }); + glfwSetFramebufferSizeCallback(glfwWindow, [](GLFWwindow* window, int width, int height) { + static_cast(glfwGetWindowUserPointer(window))->Events.OnFrameBufferResize({ + static_cast(width), + static_cast(height) + }); + }); + glfwSetWindowPosCallback(glfwWindow, [](GLFWwindow* window, int xpos, int ypos) { + WindowGLFW* dis = static_cast(glfwGetWindowUserPointer(window)); + + dis->windowedPos.X = static_cast(xpos); + dis->windowedPos.Y = static_cast(ypos); + dis->Events.OnMove(dis->windowedPos); + }); + glfwSetWindowIconifyCallback(glfwWindow, [](GLFWwindow* window, int iconified) { + static_cast(glfwGetWindowUserPointer(window))->Events.OnIconify({ + iconified == GLFW_TRUE ? Iconify::Iconified : Iconify::Restored + }); + }); + glfwSetWindowFocusCallback(glfwWindow, [](GLFWwindow* window, int focused) { + static_cast(glfwGetWindowUserPointer(window))->Events.OnFocus({ + focused == GLFW_TRUE ? Focus::FocusGained : Focus::FocusLost + }); + }); + glfwSetWindowRefreshCallback(glfwWindow, [](GLFWwindow* window) { + static_cast(glfwGetWindowUserPointer(window))->Events.OnRefresh(); + }); + + glfwSetKeyCallback(glfwWindow, [](GLFWwindow* window, int key, [[maybe_unused]] int scancode, int action, [[maybe_unused]] int mods) { + if (action == GLFW_PRESS) { + static_cast(glfwGetWindowUserPointer(window))->Events.OnKeyPress(ConvertGLFWKey(key)); + } + if (action == GLFW_RELEASE) { + static_cast(glfwGetWindowUserPointer(window))->Events.OnKeyRelease(ConvertGLFWKey(key)); + } + }); + + glfwSetCursorPosCallback(glfwWindow, [](GLFWwindow* window, double xpos, double ypos) { + static_cast(glfwGetWindowUserPointer(window))->Events.OnMouseMove({ + static_cast(xpos), + static_cast(ypos) + }); + }); + glfwSetCursorEnterCallback(glfwWindow, [](GLFWwindow* window, int entered) { + if (entered == GLFW_TRUE) { + static_cast(glfwGetWindowUserPointer(window))->Events.OnMouseEnter(); + } else { + static_cast(glfwGetWindowUserPointer(window))->Events.OnMouseLeave(); + } + }); + glfwSetMouseButtonCallback(glfwWindow, [](GLFWwindow* window, int button, int action, [[maybe_unused]] int mods) { + using enum MouseButton; + MouseButton mouseButton{}; + if (button == GLFW_MOUSE_BUTTON_1) { mouseButton = Left; } + if (button == GLFW_MOUSE_BUTTON_2) { mouseButton = Right; } + if (button == GLFW_MOUSE_BUTTON_3) { mouseButton = Middle; } + if (button == GLFW_MOUSE_BUTTON_4) { mouseButton = Mouse4; } + if (button == GLFW_MOUSE_BUTTON_5) { mouseButton = Mouse5; } + if (button == GLFW_MOUSE_BUTTON_6) { mouseButton = Mouse6; } + if (button == GLFW_MOUSE_BUTTON_7) { mouseButton = Mouse7; } + if (button == GLFW_MOUSE_BUTTON_8) { mouseButton = Mouse8; } + + if (action == GLFW_PRESS) { + static_cast(glfwGetWindowUserPointer(window))->Events.OnMousePress(mouseButton); + } else { + static_cast(glfwGetWindowUserPointer(window))->Events.OnMouseRelease(mouseButton); + } + }); + glfwSetScrollCallback(glfwWindow, [](GLFWwindow* window, double xoffset, double yoffset) { + static_cast(glfwGetWindowUserPointer(window))->Events.OnMouseScroll({ + static_cast(xoffset), + static_cast(yoffset) + }); + }); + glfwSetDropCallback(glfwWindow, [](GLFWwindow* window, int path_count, const char* paths[]) { + std::string_view* views = static_cast(alloca(path_count * sizeof(std::string_view))); + std::transform(paths, paths + path_count, views, [](const char* path) -> std::string_view { + return { path }; + }); + + static_cast(glfwGetWindowUserPointer(window))->Events.OnPathDrop({ views, static_cast(path_count) }); + }); +} +WindowGLFW::~WindowGLFW() noexcept { + glfwDestroyWindow(glfwWindow); +} +WindowGLFW::WindowGLFW(WindowGLFW&& other) noexcept { + *this = std::move(other); +} +WindowGLFW& WindowGLFW::operator=(WindowGLFW&& other) noexcept { + std::swap(glfwWindow, other.glfwWindow); + return *this; +} + +void WindowGLFW::SetResolution(Resolution resolution) noexcept { + glfwSetWindowSize(glfwWindow, resolution.Width, resolution.Height); +} +Resolution WindowGLFW::GetResolution() const noexcept { + int w, h; + glfwGetWindowSize(glfwWindow, &w, &h); + return { + static_cast(w), + static_cast(h) + }; +} +void WindowGLFW::SetContentResolution(Resolution resolution) noexcept { + int left, top, right, bottom; + glfwGetWindowFrameSize(glfwWindow, &left, &top, &right, &bottom); + glfwSetWindowSize(glfwWindow, resolution.Width + left + right, resolution.Height + top + bottom); +} +Resolution WindowGLFW::GetContentResolution() const noexcept { + int w, h; + glfwGetFramebufferSize(glfwWindow, &w, &h); + return { + static_cast(w), + static_cast(h) + }; +} + +std::string_view WindowGLFW::GetTitle() const noexcept { + return glfwGetWindowTitle(glfwWindow); +} +void WindowGLFW::SetTitle(std::string_view title) noexcept { + glfwSetWindowTitle(glfwWindow, title.data()); +} + +bool WindowGLFW::IsFullscreen() const noexcept { + return glfwGetWindowMonitor(glfwWindow) != nullptr; +} +void WindowGLFW::SetFullscreen(const IMonitor& monitor, const VideoMode& videoMode) noexcept { + int xpos, ypos; + glfwGetWindowPos(glfwWindow, &xpos, &ypos); + windowedPos.X = static_cast(xpos); + windowedPos.Y = static_cast(ypos); + + [[maybe_unused]] int count; + glfwSetWindowMonitor(glfwWindow, glfwGetMonitors(&count)[monitor.GetIndex()], 0, 0, videoMode.Size.Width, videoMode.Size.Height, videoMode.RefreshRate); +} +void WindowGLFW::SetWindowed(const VideoMode& videoMode) noexcept { + glfwSetWindowMonitor(glfwWindow, nullptr, windowedPos.X, windowedPos.Y, videoMode.Size.Width, videoMode.Size.Height, videoMode.RefreshRate); +} + +bool WindowGLFW::IsResizable() const noexcept { + return glfwGetWindowAttrib(glfwWindow, GLFW_RESIZABLE) == GLFW_TRUE; +} +void WindowGLFW::SetResizable(bool isResizable) noexcept { + glfwSetWindowAttrib(glfwWindow, GLFW_RESIZABLE, isResizable ? GLFW_TRUE : GLFW_FALSE); +} + +bool WindowGLFW::IsVisible() const noexcept { + return glfwGetWindowAttrib(glfwWindow, GLFW_VISIBLE) == GLFW_TRUE; +} +void WindowGLFW::SetVisible(bool isVisible) noexcept { + glfwSetWindowAttrib(glfwWindow, GLFW_VISIBLE, isVisible ? GLFW_TRUE : GLFW_FALSE); +} + +bool WindowGLFW::IsDecorated() const noexcept { + return glfwGetWindowAttrib(glfwWindow, GLFW_DECORATED) == GLFW_TRUE; +} +void WindowGLFW::SetDecorated(bool isDecorated) noexcept { + glfwSetWindowAttrib(glfwWindow, GLFW_DECORATED, isDecorated ? GLFW_TRUE : GLFW_FALSE); +} + +bool WindowGLFW::HasFocus() const noexcept { + return glfwGetWindowAttrib(glfwWindow, GLFW_FOCUSED) == GLFW_TRUE; +} +void WindowGLFW::Focus() noexcept { + glfwFocusWindow(glfwWindow); +} + +bool WindowGLFW::IsAutoIconified() const noexcept { + return glfwGetWindowAttrib(glfwWindow, GLFW_AUTO_ICONIFY) == GLFW_TRUE; +} +void WindowGLFW::SetAutoIconified(bool isAutoIconified) noexcept { + return glfwSetWindowAttrib(glfwWindow, GLFW_AUTO_ICONIFY, isAutoIconified ? GLFW_TRUE : GLFW_FALSE); +} + +bool WindowGLFW::IsAlwaysOnTop() const noexcept { + return glfwGetWindowAttrib(glfwWindow, GLFW_FLOATING) == GLFW_TRUE; +} +void WindowGLFW::SetAlwaysOnTop(bool isAlwaysOnTop) noexcept { + glfwSetWindowAttrib(glfwWindow, GLFW_FLOATING, isAlwaysOnTop ? GLFW_TRUE : GLFW_FALSE); +} + +void WindowGLFW::Minimize() const noexcept { glfwIconifyWindow(glfwWindow); } +void WindowGLFW::Restore() const noexcept { glfwRestoreWindow(glfwWindow); } +void WindowGLFW::Maximize() const noexcept { glfwMaximizeWindow(glfwWindow); } + +Point WindowGLFW::GetPosition() const noexcept { + int x, y; + glfwGetWindowPos(glfwWindow, &x, &y); + return { + static_cast(x), + static_cast(y) + }; +} +void WindowGLFW::SetPosition(Point position) const noexcept { + glfwSetWindowPos(glfwWindow, position.X, position.Y); +} + +void WindowGLFW::ChangeCursor(CursorType type) noexcept { + using enum CursorType; + switch (type) { + case Arrow: glfwSetCursor(glfwWindow, arrowCursor); return; + case IBeam: glfwSetCursor(glfwWindow, iBeamCursor); return; + case XHair: glfwSetCursor(glfwWindow, xHairCursor); return; + case PointingHand: glfwSetCursor(glfwWindow, pointingHandCursor); return; + case ResizeEW: glfwSetCursor(glfwWindow, resizeEWCursor); return; + case ResizeNS: glfwSetCursor(glfwWindow, resizeNSCursor); return; + case ResizeNWSE: glfwSetCursor(glfwWindow, resizeNWSECursor); return; + case ResizeNESW: glfwSetCursor(glfwWindow, resizeNESWCursor); return; + case ResizeAll: glfwSetCursor(glfwWindow, resizeAllCursor); return; + case NotAllowed: glfwSetCursor(glfwWindow, notAllowedCursor); return; + } + std::unreachable(); +} +void WindowGLFW::CaptureCursor() const noexcept { + glfwSetInputMode(glfwWindow, GLFW_CURSOR, GLFW_CURSOR_CAPTURED); +} +void WindowGLFW::DisableCursor() const noexcept { + glfwSetInputMode(glfwWindow, GLFW_CURSOR, GLFW_CURSOR_DISABLED); +} +void WindowGLFW::HideCursor() const noexcept { + glfwSetInputMode(glfwWindow, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); +} +void WindowGLFW::EnableCursor() const noexcept { + glfwSetInputMode(glfwWindow, GLFW_CURSOR, GLFW_CURSOR_NORMAL); +} + +void WindowGLFW::SetIcon(const WindowIconPack& iconPack) noexcept { + SetIconOfPlatformWindow(glfwWindow, iconPack); +} + +void WindowGLFW::Close() noexcept { + glfwSetWindowShouldClose(glfwWindow, GLFW_TRUE); +} +void WindowGLFW::PollEvents() const noexcept { + glfwPollEvents(); +} +void WindowGLFW::SwapBuffers() const noexcept { + glfwSwapBuffers(glfwWindow); +} +bool WindowGLFW::ShouldClose() const noexcept { + return glfwWindowShouldClose(glfwWindow); +} + +WindowBackend WindowGLFW::GetPlatformBackend() const noexcept { + return WindowBackend::GLFW; +} +std::any WindowGLFW::GetPlatformWindowPointer() const noexcept { + return glfwWindow; +} +void WindowGLFW::SetPlatformWindowPointerContext(std::any context) noexcept { + auto* ctx = std::any_cast(context); + glfwWindow = ctx; + glfwMakeContextCurrent(ctx); +} +bool WindowGLFW::IsSoftwareRendererContextWindow() const noexcept { + return isSoftware; +} +bool WindowGLFW::IsOpenGLContextWindow() const noexcept { + return isOpenGL; +} +bool WindowGLFW::IsVulkanContextWindow() const noexcept { + return isVulkan; +} +bool WindowGLFW::IsDirect3D12ContextWindow() const noexcept { + return isDirect3D12; +} +bool WindowGLFW::IsDirect3D11ContextWindow() const noexcept { + return isDirect3D11; +} + +GLFWcursor* WindowGLFW::CreateCursor(const CursorData& data, int glfwConstantToFallbackTo) noexcept { + if (data.Size == Resolution{}) { return glfwCreateStandardCursor(glfwConstantToFallbackTo); } + + GLFWimage cursor; + cursor.width = data.Size.Width; + cursor.height = data.Size.Height; + cursor.pixels = const_cast(reinterpret_cast(data.Pixels.data())); + return glfwCreateCursor(&cursor, data.HotSpot.X, data.HotSpot.Y); +} + +Key WindowGLFW::ConvertGLFWKey(int glfwKey) noexcept { + using enum Key; + switch (glfwKey) { + case GLFW_KEY_UNKNOWN: return Unknown; + case GLFW_KEY_SPACE: return Space; + + case GLFW_KEY_A: return A; + case GLFW_KEY_B: return B; + case GLFW_KEY_C: return C; + case GLFW_KEY_D: return D; + case GLFW_KEY_E: return E; + case GLFW_KEY_F: return F; + case GLFW_KEY_G: return G; + case GLFW_KEY_H: return H; + case GLFW_KEY_I: return I; + case GLFW_KEY_J: return J; + case GLFW_KEY_K: return K; + case GLFW_KEY_L: return L; + case GLFW_KEY_M: return M; + case GLFW_KEY_N: return N; + case GLFW_KEY_O: return O; + case GLFW_KEY_P: return P; + case GLFW_KEY_Q: return Q; + case GLFW_KEY_R: return R; + case GLFW_KEY_S: return S; + case GLFW_KEY_T: return T; + case GLFW_KEY_U: return U; + case GLFW_KEY_V: return V; + case GLFW_KEY_W: return W; + case GLFW_KEY_X: return X; + case GLFW_KEY_Y: return Y; + case GLFW_KEY_Z: return Z; + + case GLFW_KEY_0: return Num0; + case GLFW_KEY_1: return Num1; + case GLFW_KEY_2: return Num2; + case GLFW_KEY_3: return Num3; + case GLFW_KEY_4: return Num4; + case GLFW_KEY_5: return Num5; + case GLFW_KEY_6: return Num6; + case GLFW_KEY_7: return Num7; + case GLFW_KEY_8: return Num8; + case GLFW_KEY_9: return Num9; + + case GLFW_KEY_F1: return F1; + case GLFW_KEY_F2: return F2; + case GLFW_KEY_F3: return F3; + case GLFW_KEY_F4: return F4; + case GLFW_KEY_F5: return F5; + case GLFW_KEY_F6: return F6; + case GLFW_KEY_F7: return F7; + case GLFW_KEY_F8: return F8; + case GLFW_KEY_F9: return F9; + case GLFW_KEY_F10: return F10; + case GLFW_KEY_F11: return F11; + case GLFW_KEY_F12: return F12; + case GLFW_KEY_F13: return F13; + case GLFW_KEY_F14: return F14; + case GLFW_KEY_F15: return F15; + case GLFW_KEY_F16: return F16; + case GLFW_KEY_F17: return F17; + case GLFW_KEY_F18: return F18; + case GLFW_KEY_F19: return F19; + case GLFW_KEY_F20: return F20; + case GLFW_KEY_F21: return F21; + case GLFW_KEY_F22: return F22; + case GLFW_KEY_F23: return F23; + case GLFW_KEY_F24: return F24; + case GLFW_KEY_F25: return F25; + + case GLFW_KEY_APOSTROPHE: return Apostrophe; + case GLFW_KEY_COMMA: return Comma; + case GLFW_KEY_MINUS: return Minus; + case GLFW_KEY_PERIOD: return Period; + case GLFW_KEY_SLASH: return Slash; + case GLFW_KEY_SEMICOLON: return Semicolon; + case GLFW_KEY_EQUAL: return Equal; + case GLFW_KEY_LEFT_BRACKET: return LeftBracket; + case GLFW_KEY_BACKSLASH: return Backslash; + case GLFW_KEY_RIGHT_BRACKET: return RightBracket; + case GLFW_KEY_GRAVE_ACCENT: return GraveAccent; + + case GLFW_KEY_UP: return ArrowUp; + case GLFW_KEY_DOWN: return ArrowDown; + case GLFW_KEY_LEFT: return ArrowLeft; + case GLFW_KEY_RIGHT: return ArrowRight; + + case GLFW_KEY_ESCAPE: return Escape; + case GLFW_KEY_ENTER: return Enter; + case GLFW_KEY_TAB: return Tab; + case GLFW_KEY_BACKSPACE: return Backspace; + case GLFW_KEY_INSERT: return Insert; + case GLFW_KEY_DELETE: return Delete; + case GLFW_KEY_HOME: return Home; + case GLFW_KEY_END: return End; + case GLFW_KEY_PAGE_UP: return PageUp; + case GLFW_KEY_PAGE_DOWN: return PageDown; + + case GLFW_KEY_LEFT_SHIFT: return LeftShift; + case GLFW_KEY_RIGHT_SHIFT: return RightShift; + case GLFW_KEY_LEFT_CONTROL: return LeftControl; + case GLFW_KEY_RIGHT_CONTROL: return RightControl; + case GLFW_KEY_LEFT_ALT: return LeftAlt; + case GLFW_KEY_RIGHT_ALT: return RightAlt; + case GLFW_KEY_LEFT_SUPER: return LeftSuper; + case GLFW_KEY_RIGHT_SUPER: return RightSuper; + + case GLFW_KEY_CAPS_LOCK: return CapsLock; + case GLFW_KEY_SCROLL_LOCK: return ScrollLock; + case GLFW_KEY_NUM_LOCK: return NumLock; + case GLFW_KEY_PRINT_SCREEN: return PrintScreen; + case GLFW_KEY_PAUSE: return Pause; + + case GLFW_KEY_KP_0: return Keypad0; + case GLFW_KEY_KP_1: return Keypad1; + case GLFW_KEY_KP_2: return Keypad2; + case GLFW_KEY_KP_3: return Keypad3; + case GLFW_KEY_KP_4: return Keypad4; + case GLFW_KEY_KP_5: return Keypad5; + case GLFW_KEY_KP_6: return Keypad6; + case GLFW_KEY_KP_7: return Keypad7; + case GLFW_KEY_KP_8: return Keypad8; + case GLFW_KEY_KP_9: return Keypad9; + case GLFW_KEY_KP_DECIMAL: return KeypadDecimal; + case GLFW_KEY_KP_DIVIDE: return KeypadDivide; + case GLFW_KEY_KP_MULTIPLY: return KeypadMultiply; + case GLFW_KEY_KP_SUBTRACT: return KeypadSubtract; + case GLFW_KEY_KP_ADD: return KeypadAdd; + case GLFW_KEY_KP_ENTER: return KeypadEnter; + case GLFW_KEY_KP_EQUAL: return KeypadEqual; + + case GLFW_KEY_MENU: return Menu; + case GLFW_KEY_WORLD_1: return World1; + case GLFW_KEY_WORLD_2: return World2; + + default: return Unknown; + } +} \ No newline at end of file diff --git a/Engine/WindowGLFW.hpp b/Engine/WindowGLFW.hpp new file mode 100644 index 00000000..de52b95d --- /dev/null +++ b/Engine/WindowGLFW.hpp @@ -0,0 +1,107 @@ +#pragma once + +#include +#include + +struct GLFWwindow; +struct GLFWcursor; + +struct WindowGLFW : public IWindow { + + using PlatformWindowType = GLFWwindow*; + + static void SetIconOfPlatformWindow(std::any platformWindow, const WindowIconPack& iconPack) noexcept; + + WindowGLFW(GraphicsBackend backend, const ContextWindowCreationParams& params) noexcept; + ~WindowGLFW() noexcept override; + WindowGLFW(const WindowGLFW&) = delete; + WindowGLFW(WindowGLFW&& other) noexcept; + WindowGLFW& operator=(const WindowGLFW&) = delete; + WindowGLFW& operator=(WindowGLFW&& other) noexcept; + + void SetResolution(Resolution resolution) noexcept override; + Resolution GetResolution() const noexcept override; + void SetContentResolution(Resolution resolution) noexcept override; + Resolution GetContentResolution() const noexcept override; + + std::string_view GetTitle() const noexcept override; + void SetTitle(std::string_view title) noexcept override; + + bool IsFullscreen() const noexcept override; + void SetFullscreen(const IMonitor& monitor, const VideoMode& videoMode) noexcept override; + void SetWindowed(const VideoMode& videoMode) noexcept override; + + bool IsResizable() const noexcept override; + void SetResizable(bool isResizable) noexcept override; + + bool IsVisible() const noexcept override; + void SetVisible(bool isVisible) noexcept override; + + bool IsDecorated() const noexcept override; + void SetDecorated(bool isDecorated) noexcept override; + + bool HasFocus() const noexcept override; + void Focus() noexcept override; + + bool IsAutoIconified() const noexcept override; + void SetAutoIconified(bool isAutoIconified) noexcept override; + + bool IsAlwaysOnTop() const noexcept override; + void SetAlwaysOnTop(bool isAlwaysOnTop) noexcept override; + + void Minimize() const noexcept override; + void Restore() const noexcept override; + void Maximize() const noexcept override; + + Point GetPosition() const noexcept override; + void SetPosition(Point position) const noexcept override; + + void ChangeCursor(CursorType type) noexcept override; + void CaptureCursor() const noexcept override; + void DisableCursor() const noexcept override; + void HideCursor() const noexcept override; + void EnableCursor() const noexcept override; + + void SetIcon(const WindowIconPack& iconPack) noexcept override; + + void Close() noexcept override; + void PollEvents() const noexcept override; + void SwapBuffers() const noexcept override; + bool ShouldClose() const noexcept override; + + WindowBackend GetPlatformBackend() const noexcept override; + std::any GetPlatformWindowPointer() const noexcept override; + void SetPlatformWindowPointerContext(std::any context) noexcept override; + bool IsSoftwareRendererContextWindow() const noexcept override; + bool IsOpenGLContextWindow() const noexcept override; + bool IsVulkanContextWindow() const noexcept override; + bool IsDirect3D12ContextWindow() const noexcept override; + bool IsDirect3D11ContextWindow() const noexcept override; + +private: + + static GLFWcursor* CreateCursor(const CursorData& dataToCreateFrom, int glfwConstantToFallbackTo) noexcept; + static Key ConvertGLFWKey(int glfwKey) noexcept; + + GLFWwindow* glfwWindow{}; + GLFWcursor* arrowCursor{}; + GLFWcursor* iBeamCursor{}; + GLFWcursor* xHairCursor{}; + GLFWcursor* pointingHandCursor{}; + GLFWcursor* resizeEWCursor{}; + GLFWcursor* resizeNSCursor{}; + GLFWcursor* resizeNWSECursor{}; + GLFWcursor* resizeNESWCursor{}; + GLFWcursor* resizeAllCursor{}; + GLFWcursor* notAllowedCursor{}; + + bool isSoftware{}; + bool isOpenGL{}; + bool isOpenGL46{}; + bool isOpenGL33{}; + bool isVulkan{}; + bool isDirect3D12{}; + bool isDirect3D11{}; + + Point windowedPos; +}; \ No newline at end of file diff --git a/FetchDependencies.sh b/FetchDependencies.sh index dd62ad17..14fc08ae 100644 --- a/FetchDependencies.sh +++ b/FetchDependencies.sh @@ -1,2 +1,2 @@ -./scripts/vcpkg.sh +./scripts/vcpkg.sh $1 $2 $3 read -rsp $'Press any key to continue...\n' -n1 key \ No newline at end of file diff --git a/Launcher/FileDialog.cpp b/Launcher/FileDialog.cpp index cfdfeb78..63c9d981 100644 --- a/Launcher/FileDialog.cpp +++ b/Launcher/FileDialog.cpp @@ -1,3 +1,9 @@ +// This is external code. Disabling warnings. +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wbitwise-instead-of-logical" // Disable -Wunsequenced, https://easings.net/#easeOutBounce +#endif + #include #include @@ -19,6 +25,8 @@ #include #endif +#include + #define IFD_ICON_SIZE ImGui::GetFont()->FontSize + 3 #define IFD_GUI_ELEMENT_SIZE std::max(GImGui->FontSize + 10.f, 24.f) #define IFD_DEFAULT_ICON_SIZE 32 @@ -401,7 +409,7 @@ FileDialog::FileDialog() { DWORD d = GetLogicalDrives(); for (int i = 0; i < 26; i++) if (d & (1 << i)) - thisPC->Children.push_back(new FileTreeNode(std::string(1, 'A' + i) + ":")); + thisPC->Children.push_back(new FileTreeNode(std::string(1, static_cast('A' + i)) + ":")); m_treeCache.push_back(thisPC); #else std::error_code ec; @@ -1148,12 +1156,12 @@ void FileDialog::m_renderContent() ImGui::TableSetColumnIndex(1); auto tm = std::localtime(&entry.DateModified); if (tm != nullptr) - ImGui::Text("%d/%d/%d %02d:%02d", tm->tm_mon + 1, tm->tm_mday, 1900 + tm->tm_year, tm->tm_hour, tm->tm_min); + ImGuiFormattedText("{}/{}/{} {:02}:{:02}", tm->tm_mon + 1, tm->tm_mday, 1900 + tm->tm_year, tm->tm_hour, tm->tm_min); else ImGui::Text("---"); // size ImGui::TableSetColumnIndex(2); - ImGui::Text("%.3f KiB", entry.Size/1024.0f); + ImGuiFormattedText("{:.3f} KiB", entry.Size/1024.0f); } ImGui::EndTable(); @@ -1235,7 +1243,7 @@ void FileDialog::m_renderPopups() ImGui::CloseCurrentPopup(); else { const FileData& data = m_content[m_selectedFileItem]; - ImGui::Text("Are you sure you want to delete %s?", data.Path.filename().string().c_str()); + ImGuiFormattedText("Are you sure you want to delete {}?", data.Path.filename().string().c_str()); ImGui::Separator(); @@ -1527,5 +1535,11 @@ static const unsigned int folder_icon[] = { 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, }; -const char* GetDefaultFolderIcon() { return (const char*)&folder_icon[0]; } -const char* GetDefaultFileIcon() { return (const char*)&file_icon[0]; } +#ifndef _WIN32 +static const char* GetDefaultFolderIcon() { return (const char*) &folder_icon[0]; } +static const char* GetDefaultFileIcon() { return (const char*) &file_icon[0]; } +#endif + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif \ No newline at end of file diff --git a/Launcher/FileDialog.hpp b/Launcher/FileDialog.hpp index 45e5eb83..e49e5d55 100644 --- a/Launcher/FileDialog.hpp +++ b/Launcher/FileDialog.hpp @@ -10,8 +10,8 @@ #include #include -#include #include +#include // TODO change this to use engine #define IFD_DIALOG_FILE 0 #define IFD_DIALOG_DIRECTORY 1 @@ -21,7 +21,7 @@ class FileDialog { public: static inline void Initialize() { Instance().CreateTexture = [](uint8_t* data, int w, int h, char fmt) -> void* { - TEX tex; + GLuint tex; glGenTextures(1, &tex); glBindTexture(GL_TEXTURE_2D, tex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); @@ -31,10 +31,10 @@ class FileDialog { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, (fmt == 0) ? GL_BGRA : GL_RGBA, GL_UNSIGNED_BYTE, data); glGenerateMipmap(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); - return reinterpret_cast(tex); + return reinterpret_cast(static_cast(tex)); }; Instance().DeleteTexture = [](void* tex) { - TEX texID = static_cast(reinterpret_cast(tex)); + GLuint texID = static_cast(reinterpret_cast(tex)); glDeleteTextures(1, &texID); }; } diff --git a/Launcher/GUI.cpp b/Launcher/GUI.cpp index 1cede754..0b8a00e5 100644 --- a/Launcher/GUI.cpp +++ b/Launcher/GUI.cpp @@ -7,12 +7,15 @@ #include #include +#include #include #include #include +#include + #include GUI::GUI(const CorePtr& core) noexcept : @@ -26,6 +29,51 @@ GUI::GUI(const CorePtr& core) noexcept : Window->SetTitle("NeoDoa Launcher"); + stbi_set_flip_vertically_on_load(true); + { // Load launcher logo + int w, h, nrChannels; + auto* readPixels = stbi_load("Images/launcherlogo-64_x_64.png", &w, &h, &nrChannels, STBI_rgb_alpha); + + GPUTextureBuilder builder; + builder.SetName("launcher_logo"); + if (readPixels) { + std::span pixels{ reinterpret_cast(readPixels), w * h * nrChannels * sizeof(stbi_uc) }; + builder.SetWidth(w) + .SetHeight(h) + .SetData(DataFormat::RGBA8, pixels); + } else { + const Texture& texture = Texture::Missing(); + builder.SetWidth(texture.Width) + .SetHeight(texture.Height) + .SetData(texture.Format, texture.PixelData); + } + auto [tex, _] = builder.Build(); + launcherLogo = std::move(tex.value()); + + stbi_image_free(readPixels); + } + {// Load vibrant launcher logo + int w, h, nrChannels; + auto* readPixels = stbi_load("Images/launcherlogovivid-64_x_64.png", &w, &h, &nrChannels, STBI_rgb_alpha); + + GPUTextureBuilder builder; + builder.SetName("launcher_logo_vivid"); + if (readPixels) { + std::span pixels{ reinterpret_cast(readPixels), w * h * nrChannels * sizeof(stbi_uc) }; + builder.SetWidth(w) + .SetHeight(h) + .SetData(DataFormat::RGBA8, pixels); + } else { + const Texture& texture = Texture::Missing(); + builder.SetWidth(texture.Width) + .SetHeight(texture.Height) + .SetData(texture.Format, texture.PixelData); + } + auto [texVivid, __] = builder.Build(); + launcherLogoVivid = std::move(texVivid.value()); + + stbi_image_free(readPixels); + } projectDataFile = std::make_unique(FNodeCreationParams { .name = "projects" }); @@ -162,7 +210,7 @@ void GUI::RenderCustomTitleBar() noexcept { ); #pragma region Handle Window Dragging - static glm::vec2 windowPos{}; + static Point windowPos{}; static bool dragging{ false }; if (ImGui::IsMouseHoveringRect(p0, p1) && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) { windowPos = Window->GetPosition(); @@ -173,7 +221,10 @@ void GUI::RenderCustomTitleBar() noexcept { } if (dragging && ImGui::IsMouseDragging(ImGuiMouseButton_Left)) { auto drag = ImGui::GetMouseDragDelta(); - Window->SetPosition(windowPos + glm::vec2{ drag.x, drag.y }); + Window->SetPosition({ + static_cast(windowPos.X + drag.x), + static_cast(windowPos.Y + drag.y) + }); } #pragma endregion @@ -182,9 +233,9 @@ void GUI::RenderCustomTitleBar() noexcept { p0 = p0 + TitleBarInternalPadding; ImVec2 p2 = p0 + TitleBarLogoSize; if (ImGui::IsMouseHoveringRect(p0, p1) || ImGui::IsMouseHoveringRect(p0, p2)) { - logo = launcherLogoVivid.TextureIDRaw(); + logo = launcherLogoVivid; } else { - logo = launcherLogo.TextureIDRaw(); + logo = launcherLogo; } ImGui::Image(logo, TitleBarLogoSize); @@ -369,9 +420,12 @@ void GUI::RenderProjectData(ProjectData& data) noexcept { if constexpr (detect::is_windows_v) { exe = "start Editor.exe"; } else if constexpr (detect::is_linux_v) { - exe = "./Editor &"; + exe = "./Editor"; + } + std::string command = std::string(exe).append(1, ' ').append(data.AbsolutePath).append(data.Name).append(Assets::ProjectExtension); + if constexpr (detect::is_linux_v) { + command = command.append(" &"); } - std::string command = std::string(exe).append(1, ' ').append(data.AbsolutePath).append(data.Name).append(Assets::PROJ_EXT); auto sys = std::system(command.c_str()); if (sys == -1) { DOA_LOG_WARNING("[Launcher::GUI] A call to std::system returned -1."); @@ -480,14 +534,14 @@ bool GUI::IsProjectAlreadyOpen(const ProjectData& project) noexcept { using namespace std::chrono_literals; auto maxRequestReplies{ projectDataCollection.size() }; - for (int i = 0; i < maxRequestReplies; i++) { + for (size_t i = 0; i < maxRequestReplies; i++) { request.Send("request_project_path", SendFlag::DontWait); } std::this_thread::sleep_for(150ms); // wait for replies to be sent. std::vector openProjects{}; - for (int i = 0; i < maxRequestReplies; i++) { + for (size_t i = 0; i < maxRequestReplies; i++) { auto path = reply.Receive(ReceiveFlag::DontWait); if (path == "") { continue; } @@ -495,7 +549,7 @@ bool GUI::IsProjectAlreadyOpen(const ProjectData& project) noexcept { openProjects.push_back(trim(s)); } - std::string absolutePath = std::string(project.AbsolutePath).append(project.Name).append(Assets::PROJ_EXT); + std::string absolutePath = std::string(project.AbsolutePath).append(project.Name).append(Assets::ProjectExtension); auto search = std::ranges::find_if(openProjects, [&absolutePath](auto& elem) { return elem == absolutePath; }); diff --git a/Launcher/GUI.hpp b/Launcher/GUI.hpp index 46de2bd2..85ef5e60 100644 --- a/Launcher/GUI.hpp +++ b/Launcher/GUI.hpp @@ -8,8 +8,8 @@ #include #include -#include #include +#include #include #include @@ -21,7 +21,7 @@ struct GUI { const CorePtr& Core; - const WindowPtr& Window; + const std::unique_ptr& Window; explicit GUI(const CorePtr& core) noexcept; @@ -34,8 +34,8 @@ struct GUI { private: float delta; - Texture launcherLogo{ Texture::CreateTexture("launcher_logo", "Images/launcherlogo-64_x_64.png") }; - Texture launcherLogoVivid{ Texture::CreateTexture("launcher_logo_vivid", "Images/launcherlogovivid-64_x_64.png") }; + GPUTexture launcherLogo{}; + GPUTexture launcherLogoVivid{}; ImGuiWindowFlags window_flags{ ImGuiWindowFlags_None }; ImGuiDockNodeFlags dockspace_flags{ ImGuiDockNodeFlags_None }; diff --git a/Launcher/ImportProjectModal.cpp b/Launcher/ImportProjectModal.cpp index e673c7c0..4489002f 100644 --- a/Launcher/ImportProjectModal.cpp +++ b/Launcher/ImportProjectModal.cpp @@ -2,6 +2,8 @@ #include +#include + #include #include @@ -72,8 +74,8 @@ void ImportProjectModal::RenderSelectionDialog() noexcept { } } void ImportProjectModal::RenderConfirmationDialog() noexcept { - ImGui::Text("Name: %s", importProjectData.name.c_str()); - ImGui::Text("Path: %s", importProjectData.path.c_str()); + ImGuiFormattedText("Name: {}", importProjectData.name.c_str()); + ImGuiFormattedText("Path: {}", importProjectData.path.c_str()); ImGui::NewLine(); ImGui::TextUnformatted("Continue and open project?"); ImGui::Separator(); diff --git a/Launcher/NewProjectModal.cpp b/Launcher/NewProjectModal.cpp index 0ac32e57..12dab600 100644 --- a/Launcher/NewProjectModal.cpp +++ b/Launcher/NewProjectModal.cpp @@ -2,6 +2,8 @@ #include +#include + #include #include @@ -106,8 +108,8 @@ void NewProjectModal::RenderNameSelectionDialog() noexcept { } } void NewProjectModal::RenderConfirmationDialog() noexcept { - ImGui::Text("Name: %s", newProjectData.name.c_str()); - ImGui::Text("Path: %s", newProjectData.path.c_str()); + ImGuiFormattedText("Name: {}", newProjectData.name.c_str()); + ImGuiFormattedText("Path: {}", newProjectData.path.c_str()); ImGui::NewLine(); ImGui::TextUnformatted("Continue and create project?"); ImGui::Separator(); diff --git a/Launcher/ProjectData.cpp b/Launcher/ProjectData.cpp index 6c8c7da6..b2c43319 100644 --- a/Launcher/ProjectData.cpp +++ b/Launcher/ProjectData.cpp @@ -10,7 +10,7 @@ bool CheckProjectValidity(const ProjectData& data) noexcept { std::filesystem::path path = data.AbsolutePath; FNode projectFile{{ .name = (path / data.Name).string(), - .ext = Assets::PROJ_EXT + .ext = Assets::ProjectExtension }}; ProjectDeserializationResult pdr = DeserializeProject(&projectFile); return !pdr.erred; diff --git a/Launcher/main.cpp b/Launcher/main.cpp index 7c2b4a24..f3a55354 100644 --- a/Launcher/main.cpp +++ b/Launcher/main.cpp @@ -1,16 +1,53 @@ +#include + #include #include #include #include +WindowIconPack::IconData LoadIcon(std::string_view path) noexcept { + WindowIconPack::IconData data; + int w, h, channels; + + stbi_uc* pixelData = stbi_load(path.data(), &w, &h, &channels, STBI_rgb_alpha); + if (pixelData) { + data.Size.Width = static_cast(w); + data.Size.Height = static_cast(h); + data.Pixels.assign(reinterpret_cast>(pixelData), + reinterpret_cast>(pixelData) + (w * h * STBI_rgb_alpha)); + stbi_image_free(pixelData); + } + return data; +} + int main() { + //- Setup params -// + WindowIconPack pack{}; + pack.Icons.emplace_back(LoadIcon("Images/launcherlogo-16_x_16.png")); + pack.Icons.emplace_back(LoadIcon("Images/launcherlogo-32_x_32.png")); + pack.Icons.emplace_back(LoadIcon("Images/launcherlogo-48_x_48.png")); + pack.Icons.emplace_back(LoadIcon("Images/launcherlogo-64_x_64.png")); + pack.Icons.emplace_back(LoadIcon("Images/launcherlogo-128_x_128.png")); + pack.Icons.emplace_back(LoadIcon("Images/launcherlogo-256_x_256.png")); + + ContextWindowCreationParams params{ + .Size{ 800, 600 }, + .Title{ "NeoDoa Launcher" }, + + .IsVisible{ true }, + .IsDecorated{ false }, + + .Samples{ 8 }, + + .IconPack{ pack }, + }; + //- Setup params -// + DOA_LOG_INFO("Allocating %d bytes...", sizeof(Core)); - const CorePtr& core = Core::CreateCore({ 800, 600 }, "NeoDoa Launcher", false, "Images/launcherlogo"); + const CorePtr& core = Core::CreateCore(GraphicsBackend::OpenGL4_6, WindowBackend::GLFW, params); DOA_LOG_INFO("Core dynamically allocated!"); - core->GetWindow()->SetDecorated(false); - DOA_LOG_INFO("Allocating %d bytes...", sizeof(GUI)); std::shared_ptr gui_ptr = std::make_shared(core); DOA_LOG_INFO("GUI dynamically allocated!"); diff --git a/Submodules/EZEasing b/Submodules/EZEasing index d525d4b1..da365b4e 160000 --- a/Submodules/EZEasing +++ b/Submodules/EZEasing @@ -1 +1 @@ -Subproject commit d525d4b1059dfe1fe0a170120a2444060a733fa0 +Subproject commit da365b4e13348a88afda74d3d0220763ec11b182 diff --git a/Submodules/debugbreak b/Submodules/debugbreak new file mode 160000 index 00000000..6678e851 --- /dev/null +++ b/Submodules/debugbreak @@ -0,0 +1 @@ +Subproject commit 6678e85155c1e823ae10333297a32c175e73dc41 diff --git a/Submodules/imgInspect b/Submodules/imgInspect index d61dc777..2aea13cf 160000 --- a/Submodules/imgInspect +++ b/Submodules/imgInspect @@ -1 +1 @@ -Subproject commit d61dc7775ef1f4563106a7b09d62ed4224a7c0e9 +Subproject commit 2aea13cfc50f1bc43d8aab60bd5cfeb8c6e0a182 diff --git a/Submodules/stb_impl b/Submodules/stb_impl index e6fa4c86..28e12730 160000 --- a/Submodules/stb_impl +++ b/Submodules/stb_impl @@ -1 +1 @@ -Subproject commit e6fa4c86980f21fd84ae05249cc35309b9270f7e +Subproject commit 28e12730fba30b53b190e8a9551c3dd7ec0ed60b diff --git a/Utility/AdjacencyList.hpp b/Utility/AdjacencyList.hpp new file mode 100644 index 00000000..715db992 --- /dev/null +++ b/Utility/AdjacencyList.hpp @@ -0,0 +1,268 @@ +#pragma once + +#include +#include +#include + +#include + +template + requires std::equality_comparable +struct AdjacencyList { + using EdgeList = std::vector; + using DataStructure = std::vector>; + + /// + /// Iterates over incoming edges and retrieves origin vertices. + /// Use HasNext() to check if there are more edges and iteration can continue, + /// Use Next() to retrieve the next vertex + /// + struct IncomingEdgeIterator { + + using iterator_category = std::input_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = Vertex; + using pointer = value_type*; + using reference = value_type&; + + IncomingEdgeIterator(DataStructure::const_iterator begin, DataStructure::const_iterator end, const Vertex& vertex); + + bool HasNext() const noexcept; + const Vertex& Next() noexcept; + + private: + DataStructure::const_iterator begin, current, end; + EdgeList::value_type vertexIndex; + }; + + /// + /// Iterates over outgoing edges and retrieves destination vertices. + /// Use HasNext() to check if there are more edges and iteration can continue, + /// Use Next() to retrieve the next vertex + /// + struct OutgoingEdgeIterator { + + OutgoingEdgeIterator(DataStructure::const_iterator begin, DataStructure::const_iterator end, const Vertex& vertex); + + bool HasNext() const noexcept; + const Vertex& Next() noexcept; + + private: + DataStructure::const_iterator begin, end; + EdgeList::size_type currentIndex{}; + const EdgeList& edgeList; + }; + + AdjacencyList() noexcept; + + /// + /// Precondition: vertex is absent. + /// Postcondition: vertex is present. + /// + void AddVertex(const Vertex& vertex) noexcept; + + /// + /// Precondition: origin and destination are present + /// origin and destionation are different + /// an edge from origin to destination is absent. + /// Postcondition: An edge from origin to destination is present. + /// + void AddEdge(const Vertex& origin, const Vertex& destination) noexcept; + + /// + /// Returns true if vertex exists. + /// Precondition: None. + /// Postcondition: None. + /// + bool HasVertex(const Vertex& vertex) const noexcept; + + /// + /// Precondition: origin is present. + /// Postcondition: None. + /// + bool HasEdge(const Vertex& origin, const Vertex& destination) const noexcept; + + /// + /// Precondition: vertex is present + /// Postcondition: vertex is absent + /// incoming and outgoing edges to/from vertex are absent + /// + void RemoveVertex(const Vertex& vertex) noexcept; + + /// + /// Precondition: origin and destination are present + /// origin and destination are different + /// an edge from origin to destination is present. + /// Postcondition: An edge from origin to destination is absent. + /// + void RemoveEdge(const Vertex& origin, const Vertex& destination) noexcept; + + /// + /// Precondition: None. + /// Postcondition: All vertices and edges are absent. + /// + void Clear() noexcept; + + /// + /// Precondition: origin is present. + /// Postcondition: None. + /// + IncomingEdgeIterator GetIncomingEdgesOf(const Vertex& origin) const noexcept; + + /// + /// Precondition: origin is present. + /// Postcondition: None. + /// + OutgoingEdgeIterator GetOutgoingEdgesOf(const Vertex& origin) const noexcept; + +private: + DataStructure data{}; +}; + +template + requires std::equality_comparable +inline AdjacencyList::IncomingEdgeIterator::IncomingEdgeIterator(DataStructure::const_iterator begin, DataStructure::const_iterator end, const Vertex& vertex) : + begin(begin), + current(begin), + end(end), + vertexIndex(std::distance(begin, std::find_if(begin, end, [&vertex](auto& pair) { return pair.first == vertex; }))) { + while(current < end) { + auto search = std::ranges::find(current->second, vertexIndex); + if (search == current->second.end()) { + current = std::next(current); + } else { + break; + } + } +} + +template + requires std::equality_comparable +inline bool AdjacencyList::IncomingEdgeIterator::HasNext() const noexcept { return current < end; } +template + requires std::equality_comparable +inline const Vertex& AdjacencyList::IncomingEdgeIterator::Next() noexcept { + auto& rv = current->first; + current = std::next(current); + while (current < end) { + auto search = std::ranges::find(current->second, vertexIndex); + if (search == current->second.end()) { + current = std::next(current); + } else { + break; + } + } + return rv; +} + +template + requires std::equality_comparable +inline AdjacencyList::OutgoingEdgeIterator::OutgoingEdgeIterator(DataStructure::const_iterator begin, DataStructure::const_iterator end, const Vertex& vertex) : + begin(begin), + end(end), + edgeList(std::find_if(begin, end, [&vertex](auto& pair) { return pair.first == vertex; })->second) {} + +template + requires std::equality_comparable +inline bool AdjacencyList::OutgoingEdgeIterator::HasNext() const noexcept { return currentIndex < edgeList.size(); } +template + requires std::equality_comparable +inline const Vertex& AdjacencyList::OutgoingEdgeIterator::Next() noexcept { + size_t edgeVertexIndex = edgeList[currentIndex++]; + return (begin + edgeVertexIndex)->first; +} + +template + requires std::equality_comparable +inline AdjacencyList::AdjacencyList() noexcept { + data.reserve(InitialVertexCount); +} + +template + requires std::equality_comparable +inline void AdjacencyList::AddVertex(const Vertex& vertex) noexcept { + data.emplace_back(vertex, EdgeList{}).second.reserve(InitialEdgeCountPerVertex); +} + +template + requires std::equality_comparable +inline void AdjacencyList::AddEdge(const Vertex& origin, const Vertex& destination) noexcept { + EdgeList& originEdgeList = std::ranges::find_if(data, [&origin](const auto& pair) { return pair.first == origin; })->second; + + auto destinationIndex = std::distance(data.begin(), std::ranges::find_if(data, [&destination](const auto& pair) { return pair.first == destination; })); + + originEdgeList.push_back(destinationIndex); +} + +template + requires std::equality_comparable +inline bool AdjacencyList::HasVertex(const Vertex& origin) const noexcept { + return std::ranges::find_if(data, [&origin](const auto& pair) { return pair.first == origin; }) != data.end(); +} + +template + requires std::equality_comparable +inline bool AdjacencyList::HasEdge(const Vertex& origin, const Vertex& destination) const noexcept { + const EdgeList& originEdgeList = std::ranges::find_if(data, [&origin](const auto& pair) { return pair.first == origin; })->second; + + auto destinationIndex = std::distance(data.begin(), std::ranges::find_if(data, [&destination](const auto& pair) { return pair.first == destination; })); + + return std::ranges::find(originEdgeList, destinationIndex) != originEdgeList.end(); +} + +template + requires std::equality_comparable +inline void AdjacencyList::RemoveVertex(const Vertex& vertex) noexcept { + // Step 1. Remove all incoming edges - traverse graph, check every edge list and remove own index + // Step 2. Remove all outgoing edges - trivial, remove vertex from graph (graphnode contains vertex and it's outgoing edges), this will break indices + // Step 3. Fix indices - traverse graph, traverse every edge list and decrement entries greater than own index + + // S1 + auto vertexIterator = std::ranges::find_if(data, [&vertex](const auto& pair) { return pair.first == vertex; }); + auto vertexIndex = static_cast(std::distance(data.begin(), vertexIterator)); + for (auto& [_, edgeList] : data) { + auto vertexIndexInEdges = std::ranges::find(edgeList, vertexIndex); + edgeList.erase(vertexIndexInEdges); + } + + // S2 + data.erase(vertexIterator); + + // S3 + for (auto& [_, edgeList] : data) { + for (auto& edgeIndex : edgeList) { + // Branchless code below + int modifier = static_cast(edgeIndex > vertexIndex); + edgeIndex = edgeIndex - modifier; + /// if (edgeIndex > vertexIndex) edgeIndex--; + } + } +} + +template + requires std::equality_comparable +inline void AdjacencyList::RemoveEdge(const Vertex& origin, const Vertex& destination) noexcept { + EdgeList& originEdgeList = std::ranges::find_if(data, [&origin](const auto& pair) { return pair.first == origin; })->second; + + auto destinationIndex = static_cast(std::distance(data.begin(), std::ranges::find_if(data, [&destination](auto& pair) { return pair.first == destination; }))); + + auto destinationIndexInEdges = std::ranges::find(originEdgeList, destinationIndex); + originEdgeList.erase(destinationIndexInEdges); +} + +template + requires std::equality_comparable +inline void AdjacencyList::Clear() noexcept { + data.clear(); +} + +template + requires std::equality_comparable +inline AdjacencyList::IncomingEdgeIterator AdjacencyList::GetIncomingEdgesOf(const Vertex& vertex) const noexcept { + return { data.cbegin(), data.cend(), vertex }; +} +template + requires std::equality_comparable +inline AdjacencyList::OutgoingEdgeIterator AdjacencyList::GetOutgoingEdgesOf(const Vertex& vertex) const noexcept { + return { data.cbegin(), data.cend(), vertex }; +} diff --git a/Utility/CMakeLists.txt b/Utility/CMakeLists.txt index c5859d82..efc26697 100644 --- a/Utility/CMakeLists.txt +++ b/Utility/CMakeLists.txt @@ -16,6 +16,7 @@ target_link_libraries(Utility PUBLIC ICU::uc ICU::dt ICU::i18n) target_link_libraries(Utility PUBLIC cppzmq cppzmq-static) set(GROUP_LIST + "AdjacencyList.hpp" "CheckSubstring.cpp" "CheckSubstring.hpp" "ConstexprConcat.hpp" diff --git a/Utility/SimpleSocket.cpp b/Utility/SimpleSocket.cpp index 2a1a327f..5e700708 100644 --- a/Utility/SimpleSocket.cpp +++ b/Utility/SimpleSocket.cpp @@ -1,5 +1,7 @@ #include +#include + static zmq::socket_type GetZMQSocketType(SocketType type) { switch (type) { case SocketType::Request: @@ -22,9 +24,8 @@ static zmq::socket_type GetZMQSocketType(SocketType type) { return zmq::socket_type::push; case SocketType::Pull: return zmq::socket_type::pull; - default: - break; } + std::unreachable(); } static zmq::send_flags GetZMQSendFlag(SendFlag flag) { @@ -35,9 +36,8 @@ static zmq::send_flags GetZMQSendFlag(SendFlag flag) { return zmq::send_flags::dontwait; case SendFlag::SendMore: return zmq::send_flags::sndmore; - default: - break; } + std::unreachable(); } static zmq::recv_flags GetZMQRecvFlag(ReceiveFlag flag) { @@ -46,9 +46,8 @@ static zmq::recv_flags GetZMQRecvFlag(ReceiveFlag flag) { return zmq::recv_flags::none; case ReceiveFlag::DontWait: return zmq::recv_flags::dontwait; - default: - break; } + std::unreachable(); } @@ -61,7 +60,7 @@ int Poller::Poll(std::chrono::milliseconds timeout) noexcept { SimpleSocket::SimpleSocket(SocketType type) noexcept : socket(context, GetZMQSocketType(type)) {} -SimpleSocket::~SimpleSocket() {} +SimpleSocket::~SimpleSocket() noexcept {} void SimpleSocket::Send(std::string_view message, SendFlag flag) noexcept { socket.send(zmq::buffer(message), GetZMQSendFlag(flag)); diff --git a/Utility/TemplateUtilities.hpp b/Utility/TemplateUtilities.hpp index 6baaf4bb..41d82d13 100644 --- a/Utility/TemplateUtilities.hpp +++ b/Utility/TemplateUtilities.hpp @@ -8,7 +8,7 @@ namespace concepts { concept IsAnyOf = (std::same_as || ...); template - concept Copyable = requires(const T & t) { + concept Copyable = requires(const T& t) { { T::Copy(t) } -> std::same_as; }; @@ -17,6 +17,15 @@ namespace concepts { &T::Serialize; &T::Deserialize; }; + + // https://en.cppreference.com/w/cpp/language/constraints + // Declaration of the concept "Hashable", which is satisfied by any type 'T' + // such that for values 'a' of type 'T', the expression std::hash{}(a) + // compiles and its result is convertible to std::size_t + template + concept Hashable = requires(T t) { + { std::hash{}(t) } -> std::convertible_to; + }; } namespace overloaded { diff --git a/Utility/UndoRedoStack.cpp b/Utility/UndoRedoStack.cpp index 53dc68a7..6db5a3fa 100644 --- a/Utility/UndoRedoStack.cpp +++ b/Utility/UndoRedoStack.cpp @@ -4,9 +4,9 @@ ICommand::~ICommand() noexcept {}; -bool ICommand::TryMergeWith(UndoRedoStack& history, const ICommand* command) noexcept { return false; } +bool ICommand::TryMergeWith([[maybe_unused]] UndoRedoStack& history, [[maybe_unused]] const ICommand* command) noexcept { return false; } -void UndoRedoStack::Do(std::unique_ptr&& command) noexcept { +void UndoRedoStack::Do(Command&& command) noexcept { if (!undoStack.empty() && undoStack.back()->TryMergeWith(*this, command.get())) { // Can Merge, don't push command but merge it with peek and re-execute peek (instead of command) undoStack.back()->Execute(); diff --git a/Utility/UndoRedoStack.hpp b/Utility/UndoRedoStack.hpp index cb9ca06c..6dddb01d 100644 --- a/Utility/UndoRedoStack.hpp +++ b/Utility/UndoRedoStack.hpp @@ -13,7 +13,7 @@ struct ICommand { virtual void Execute() noexcept = 0; virtual void UnExecute() noexcept = 0; - virtual bool TryMergeWith(UndoRedoStack& history, const ICommand* command) noexcept; + virtual bool TryMergeWith([[maybe_unused]] UndoRedoStack& history, [[maybe_unused]] const ICommand* command) noexcept; }; struct UndoRedoStack { @@ -58,7 +58,7 @@ struct UndoRedoStack { bool HasNext() const noexcept override; Element Next() noexcept override; private: - int currentIndex{}; + size_t currentIndex{}; const UndoRedoStack& stack; }; // UndoStackIterator @@ -71,7 +71,7 @@ struct UndoRedoStack { bool HasNext() const noexcept override; Element Next() noexcept override; private: - int currentIndex{}; + size_t currentIndex{}; const UndoRedoStack& stack; }; // UndoStackReverseIterator @@ -84,7 +84,7 @@ struct UndoRedoStack { bool HasNext() const noexcept override; Element Next() noexcept override; private: - int currentIndex{}; + size_t currentIndex{}; const UndoRedoStack& stack; }; // RedoStackIterator @@ -97,7 +97,7 @@ struct UndoRedoStack { bool HasNext() const noexcept override; Element Next() noexcept override; private: - int currentIndex{}; + size_t currentIndex{}; const UndoRedoStack& stack; }; // RedoStackReverseIterator @@ -108,7 +108,7 @@ struct UndoRedoStack { bool HasNext() const noexcept override; Element Next() noexcept override; private: - int currentIndex{}; + size_t currentIndex{}; std::array, 2> iterators; }; // UndoRedoStackIterator @@ -119,7 +119,7 @@ struct UndoRedoStack { bool HasNext() const noexcept override; Element Next() noexcept override; private: - int currentIndex{}; + size_t currentIndex{}; std::array, 2> iterators; }; // UndoRedoStackReverseIterator diff --git a/scripts/linux_compiler_setup.sh b/scripts/linux_compiler_setup.sh new file mode 100644 index 00000000..c1fb2033 --- /dev/null +++ b/scripts/linux_compiler_setup.sh @@ -0,0 +1,7 @@ +sudo apt install cmake -y +sudo apt install gcc-14 -y +sudo apt install libstdc++-14-dev -y +sudo apt install clang -y +sudo update-alternatives --config cc +sudo update-alternatives --config c++ +clang -v \ No newline at end of file