diff --git a/Tetragrama/CMakeLists.txt b/Tetragrama/CMakeLists.txt index 521c366d..e8e64d2b 100644 --- a/Tetragrama/CMakeLists.txt +++ b/Tetragrama/CMakeLists.txt @@ -45,6 +45,7 @@ target_include_directories (${TARGET_NAME} ./Helpers ./Importers ./Serializers + ./Managers ${ENLISTMENT_ROOT}/ZEngine ) diff --git a/Tetragrama/Components/DockspaceUIComponent.cpp b/Tetragrama/Components/DockspaceUIComponent.cpp index babb4246..b84884a8 100644 --- a/Tetragrama/Components/DockspaceUIComponent.cpp +++ b/Tetragrama/Components/DockspaceUIComponent.cpp @@ -15,11 +15,15 @@ using namespace ZEngine::Helpers; namespace Tetragrama::Components { - ImVec4 DockspaceUIComponent::s_asset_importer_report_msg_color = {1, 1, 1, 1}; - char DockspaceUIComponent::s_asset_importer_input_buffer[1024] = {0}; - char DockspaceUIComponent::s_save_as_input_buffer[1024] = {0}; - std::string DockspaceUIComponent::s_asset_importer_report_msg = ""; - float DockspaceUIComponent::s_editor_scene_serializer_progress = 0.0f; + ImVec4 DockspaceUIComponent::s_asset_importer_report_msg_color = {1, 1, 1, 1}; + char DockspaceUIComponent::s_asset_importer_input_buffer[1024] = {0}; + char DockspaceUIComponent::s_save_as_input_buffer[1024] = {0}; + std::string DockspaceUIComponent::s_asset_importer_report_msg = ""; + float DockspaceUIComponent::s_editor_scene_serializer_progress = 0.0f; + + static bool s_is_scene_loading = false; + static char s_scene_serializer_log[DEFAULT_STR_BUFFER] = {0}; + static ImVec4 s_scene_serializer_log_color = {1, 1, 1, 1}; DockspaceUIComponent::DockspaceUIComponent() {} @@ -29,35 +33,30 @@ namespace Tetragrama::Components { UIComponent::Initialize(parent, name, visibility, closed); + parent->LayerArena.CreateSubArena(ZMega(10), &LocalArena); + m_asset_importer = ZPushStructCtor(&(parent->LayerArena), Importers::AssimpImporter); m_editor_serializer = ZPushStructCtor(&(parent->LayerArena), Serializers::EditorSceneSerializer); m_editor_serializer->Initialize(&(parent->LayerArena)); m_asset_importer->Initialize(&(parent->LayerArena)); - m_editor_serializer->AssetImporter = m_asset_importer; - - m_dockspace_node_flag = ImGuiDockNodeFlags_NoWindowMenuButton | ImGuiDockNodeFlags_PassthruCentralNode; - m_window_flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus; - - auto context = reinterpret_cast(ParentLayer->ParentContext); - m_editor_serializer->Context = context; - m_asset_importer->Context = context; + m_dockspace_node_flag = ImGuiDockNodeFlags_NoWindowMenuButton | ImGuiDockNodeFlags_PassthruCentralNode; + m_window_flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus; - const auto& editor_config = *context->ConfigurationPtr; + auto context = reinterpret_cast(ParentLayer->ParentContext); + m_editor_serializer->Context = context; + m_asset_importer->Context = context; - m_default_import_configuration = {}; - m_default_import_configuration.OutputWorkingSpacePath.init(&(parent->LayerArena), editor_config.WorkingSpacePath.c_str()); - m_default_import_configuration.OutputModelFilePath.init(&(parent->LayerArena), editor_config.SceneDataPath.c_str()); - m_default_import_configuration.OutputMeshFilePath.init(&(parent->LayerArena), editor_config.SceneDataPath.c_str()); - m_default_import_configuration.OutputMaterialsPath.init(&(parent->LayerArena), editor_config.SceneDataPath.c_str()); - m_default_import_configuration.OutputTextureFilesPath.init(&(parent->LayerArena), editor_config.DefaultImportTexturePath.c_str()); + const auto& editor_config = *context->ConfigurationPtr; - auto editor_serializer_default_output = fmt::format("{0}{1}{2}", editor_config.WorkingSpacePath.c_str(), PLATFORM_OS_BACKSLASH, editor_config.ScenePath.c_str()); + auto editor_serializer_default_output = fmt::format("{0}{1}{2}", editor_config.WorkingSpacePath.c_str(), PLATFORM_OS_BACKSLASH, editor_config.ScenePath.c_str()); m_editor_serializer->SetDefaultOutput(editor_serializer_default_output); m_editor_serializer->SetOnProgressCallback(OnEditorSceneSerializerProgress); + m_editor_serializer->SetOnCompleteCallback(OnEditorSceneSerializerComplete); m_editor_serializer->SetOnDeserializeCompleteCallback(OnEditorSceneSerializerDeserializeComplete); + m_editor_serializer->SetOnLogCallback(OnEditorSceneSerializerLog); m_editor_serializer->SetOnErrorCallback(OnEditorSceneSerializerError); m_asset_importer->SetOnCompleteCallback(OnAssetImporterComplete); @@ -118,19 +117,22 @@ namespace Tetragrama::Components } RenderMenuBar(); - RenderImporter(); - RenderExitPopup(); + RenderLoadScene(); RenderSaveScene(); RenderSaveSceneAs(); + RenderImporter(); + + RenderExitPopup(); + ImGui::End(); auto ctx = reinterpret_cast(ParentLayer->ParentContext); - if (ctx->CurrentScenePtr && ctx->CurrentScenePtr->RenderScene->IsDrawDataDirty) - { - ctx->CurrentScenePtr->RenderScene->InitOrResetDrawBuffer(renderer->Device, renderer->RenderGraph, renderer->AsyncLoader); - } + // if (ctx->CurrentScenePtr && ctx->CurrentScenePtr->RenderScene->IsDrawDataDirty) + //{ + // ctx->CurrentScenePtr->RenderScene->InitOrResetDrawBuffer(renderer->Device, renderer->RenderGraph, renderer->AsyncLoader); + // } } void DockspaceUIComponent::RenderImporter() @@ -145,84 +147,117 @@ namespace Tetragrama::Components return; } + const char* str_id = "Model Importer"; + ImGui::OpenPopup(str_id); ImVec2 center = ImGui::GetMainViewport()->GetCenter(); ImGui::SetNextWindowPos(center, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); ImGui::SetNextWindowSize(ImVec2(700, 100), ImGuiCond_Always); - if (!ImGui::Begin("Model Importer", NULL, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse)) - { - ImGui::End(); - return; - } - bool is_import_button_enabled = !m_asset_importer->IsImporting(); - ImGui::PushItemWidth(620); - ImGui::InputText("##ModelImporterUI", s_asset_importer_input_buffer, IM_ARRAYSIZE(s_asset_importer_input_buffer), ImGuiInputTextFlags_ReadOnly); - ImGui::PopItemWidth(); - - ImGui::SameLine(); - - if (ImGui::Button("...", ImVec2(50, 0)) && is_import_button_enabled) + if (ImGui::BeginPopupModal(str_id, NULL, ImGuiWindowFlags_AlwaysAutoResize)) { - Helpers::UIDispatcher::RunAsync([this]() -> std::future { - if (ParentLayer && ParentLayer->ParentWindow) - { - auto window = ParentLayer->ParentWindow; - std::vector filters{".obj", ".gltf"}; - std::string filename = co_await window->OpenFileDialogAsync(filters); + ImGui::PushItemWidth(620); + ImGui::InputText("##ModelImporterUI", s_asset_importer_input_buffer, IM_ARRAYSIZE(s_asset_importer_input_buffer), ImGuiInputTextFlags_ReadOnly); + ImGui::PopItemWidth(); - if (!filename.empty()) + ImGui::SameLine(); + + if (ImGui::Button("...", ImVec2(50, 0)) && is_import_button_enabled) + { + Helpers::UIDispatcher::RunAsync([this]() -> std::future { + if (ParentLayer && ParentLayer->ParentWindow) { - ZEngine::Helpers::secure_memset(s_asset_importer_input_buffer, 0, IM_ARRAYSIZE(s_asset_importer_input_buffer), IM_ARRAYSIZE(s_asset_importer_input_buffer)); - ZEngine::Helpers::secure_memcpy(s_asset_importer_input_buffer, IM_ARRAYSIZE(s_asset_importer_input_buffer), filename.c_str(), filename.size()); - } - } - }); - } + auto window = ParentLayer->ParentWindow; + std::vector filters{".obj", ".gltf"}; + std::string filename = co_await window->OpenFileDialogAsync(filters); - ImGui::Separator(); + if (!filename.empty()) + { + ZEngine::Helpers::secure_memset(s_asset_importer_input_buffer, 0, IM_ARRAYSIZE(s_asset_importer_input_buffer), IM_ARRAYSIZE(s_asset_importer_input_buffer)); + ZEngine::Helpers::secure_memcpy(s_asset_importer_input_buffer, IM_ARRAYSIZE(s_asset_importer_input_buffer), filename.c_str(), filename.size()); + } + } + }); + } - ImGui::SetCursorPosX(ImGui::GetWindowSize().x - 180); - ImGui::SetCursorPosY(ImGui::GetWindowSize().y - ImGui::GetFrameHeightWithSpacing() - 5); + ImGui::Separator(); - if (!is_import_button_enabled || std::string_view(s_asset_importer_input_buffer).empty()) - { - ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.5f, 0.5f, 0.5f, 1.0f)); // Grayed out color - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.5f, 0.5f, 0.5f, 1.0f)); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.5f, 0.5f, 0.5f, 1.0f)); - } + ImGui::SetCursorPosX(ImGui::GetWindowSize().x - 180); + ImGui::SetCursorPosY(ImGui::GetWindowSize().y - ImGui::GetFrameHeightWithSpacing() - 5); - if (ImGui::Button("Launch", ImVec2(80, 0)) && is_import_button_enabled) - { - Helpers::UIDispatcher::RunAsync([this] { OnImportAssetAsync(s_asset_importer_input_buffer); }); - } + if (!is_import_button_enabled || std::string_view(s_asset_importer_input_buffer).empty()) + { + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.5f, 0.5f, 0.5f, 1.0f)); // Grayed out color + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.5f, 0.5f, 0.5f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.5f, 0.5f, 0.5f, 1.0f)); + } - if (!is_import_button_enabled || std::string_view(s_asset_importer_input_buffer).empty()) - { - ImGui::PopStyleColor(3); // Pop the grayed out color - } + if (ImGui::Button("Launch", ImVec2(80, 0)) && is_import_button_enabled) + { + OnImportAssetAsync(s_asset_importer_input_buffer); + } - ImGui::SameLine(); - if (ImGui::Button("Close", ImVec2(80, 0))) - { - if (m_asset_importer->IsImporting()) + if (!is_import_button_enabled || std::string_view(s_asset_importer_input_buffer).empty()) { - ZENGINE_CORE_WARN("Import in progress : {}", s_asset_importer_input_buffer) + ImGui::PopStyleColor(3); // Pop the grayed out color } - else + + ImGui::SameLine(); + if (ImGui::Button("Close", ImVec2(80, 0))) { - m_open_asset_importer = false; - ResetImporterBuffers(); + if (m_asset_importer->IsImporting()) + { + ZENGINE_CORE_WARN("Import in progress : {}", s_asset_importer_input_buffer) + } + else + { + m_open_asset_importer = false; + ResetImporterBuffers(); + ImGui::CloseCurrentPopup(); + } } + + ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[0]); + ImGui::SetCursorPos(ImVec2(10, ImGui::GetWindowSize().y - 30)); + ImGui::TextColored(s_asset_importer_report_msg_color, s_asset_importer_report_msg.c_str()); + ImGui::PopFont(); + + ImGui::EndPopup(); } + } - ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[0]); - ImGui::SetCursorPos(ImVec2(10, ImGui::GetWindowSize().y - 30)); - ImGui::TextColored(s_asset_importer_report_msg_color, s_asset_importer_report_msg.c_str()); - ImGui::PopFont(); + void DockspaceUIComponent::RenderLoadScene() + { + if (!s_is_scene_loading) + { + return; + } - ImGui::End(); + const char* str_id = "Loading Scene"; + ImGui::OpenPopup(str_id); + ImVec2 center = ImGui::GetMainViewport()->GetCenter(); + ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); + ImGui::SetNextWindowSize(ImVec2(700, 100), ImGuiCond_Always); + + if (ImGui::BeginPopupModal(str_id, NULL, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_AlwaysAutoResize)) + { + // Calculate position for the progress bar + ImVec2 wind_size = ImGui::GetWindowSize(); + ImVec2 reg_available = ImGui::GetContentRegionAvail(); + ImVec2 progress_bar_pos = ImVec2((wind_size.x - reg_available.x) * 0.5f, (wind_size.y - reg_available.y)); + + // Display the progress bar + ImGui::SetCursorPos(progress_bar_pos); + ImGui::ProgressBar(s_editor_scene_serializer_progress, ImVec2(reg_available.x, 20.0f), " "); + + ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[0]); + ImGui::SetCursorPos(ImVec2(10, wind_size.y - 30)); + ImGui::TextColored(s_scene_serializer_log_color, s_scene_serializer_log); + ImGui::PopFont(); + + ImGui::EndPopup(); + } } void DockspaceUIComponent::RenderSaveScene() @@ -400,16 +435,17 @@ namespace Tetragrama::Components ZEngine::Helpers::secure_memset(s_save_as_input_buffer, 0, IM_ARRAYSIZE(s_save_as_input_buffer), IM_ARRAYSIZE(s_save_as_input_buffer)); } - void DockspaceUIComponent::OnAssetImporterComplete(void* const context, Importers::ImporterData&& data) + void DockspaceUIComponent::OnAssetImporterComplete(void* const context, ZEngine::Core::Containers::ArrayView result) { + auto ctx = reinterpret_cast(context); + + for (unsigned i = 0; i < result.size(); ++i) + { + ctx->CurrentScenePtr->PushAssetFile(result[i]); + } + s_asset_importer_report_msg_color = {0.0f, 1.0f, 0.0f, 1.0f}; s_asset_importer_report_msg = "Completed"; - - auto context_ptr = reinterpret_cast(context); - /* - * Removing the WorkingSpace Path - */ - context_ptr->CurrentScenePtr->Push(&(context_ptr->Arena), data.SerializedMeshesPath.c_str(), data.SerializedModelPath.c_str(), data.SerializedMaterialsPath.c_str()); } void DockspaceUIComponent::OnAssetImporterProgress(void* const context, float value) @@ -429,6 +465,11 @@ namespace Tetragrama::Components ZENGINE_CORE_ERROR("{}", msg) } + void DockspaceUIComponent::OnEditorSceneSerializerLog(void* const, std::string_view msg) + { + ZEngine::Helpers::secure_strcpy(s_scene_serializer_log, DEFAULT_STR_BUFFER, msg.data()); + } + void DockspaceUIComponent::OnAssetImporterLog(void* const, std::string_view msg) { s_asset_importer_report_msg_color = {1.0f, 1.0f, 1.0f, 1.0f}; @@ -492,25 +533,37 @@ namespace Tetragrama::Components s_editor_scene_serializer_progress = value; } - void DockspaceUIComponent::OnEditorSceneSerializerComplete(void* const) {} + void DockspaceUIComponent::OnEditorSceneSerializerComplete(void* const context) + { + auto ctx = reinterpret_cast(context); + ctx->CurrentScenePtr->HasPendingChanges.store(false, std::memory_order_release); + } void DockspaceUIComponent::OnEditorSceneSerializerDeserializeComplete(void* const context, EditorScene&& scene) { auto ctx = reinterpret_cast(context); // Todo : Ensure no data race on CurrentScenePtr - ctx->ConfigurationPtr->ActiveSceneName.reserve(ZEngine::Helpers::secure_strlen(scene.Name)); ctx->ConfigurationPtr->ActiveSceneName.clear(); ctx->ConfigurationPtr->ActiveSceneName.append(scene.Name); - ctx->CurrentScenePtr->Name = ctx->ConfigurationPtr->ActiveSceneName.c_str(); - ctx->CurrentScenePtr->Data = scene.Data; - ctx->CurrentScenePtr->MeshFiles = scene.MeshFiles; - ctx->CurrentScenePtr->ModelFiles = scene.ModelFiles; - ctx->CurrentScenePtr->MaterialFiles = scene.MaterialFiles; - ctx->CurrentScenePtr->RenderScene = scene.RenderScene; + ctx->CurrentScenePtr->MarkDirty(true); + ctx->SelectedSceneNode.store(-1, std::memory_order_release); + ctx->CurrentScenePtr->Reset(); + ctx->CurrentScenePtr->ExtractAsync(scene); + + ctx->CurrentScenePtr->Name = ctx->ConfigurationPtr->ActiveSceneName.c_str(); + + ctx->CurrentScenePtr->MarkDirty(false); + + { + auto msg = fmt::format("Scene {} deserialized successfully", ctx->CurrentScenePtr->Name); + ZEngine::Helpers::secure_strcpy(s_scene_serializer_log, DEFAULT_STR_BUFFER, msg.data()); + + ZENGINE_CORE_INFO("{}", msg.c_str()) + } - ZENGINE_CORE_INFO("Scene {} deserialized successfully", ctx->CurrentScenePtr->Name) + s_is_scene_loading = false; } std::future DockspaceUIComponent::OnNewSceneAsync() @@ -528,24 +581,38 @@ namespace Tetragrama::Components if (!scene_filename.empty()) { + s_is_scene_loading = true; m_editor_serializer->Deserialize(scene_filename); } } co_return; } - std::future DockspaceUIComponent::OnImportAssetAsync(std::string_view filename) + std::future DockspaceUIComponent::OnImportAssetAsync(const char* filename) { - if (!filename.empty()) + if (ZEngine::Helpers::secure_strlen(filename) == 0) { - auto parent_path = std::filesystem::path(filename).parent_path().string(); - auto asset_name = fs::path(filename).filename().replace_extension().string(); - auto import_config = m_default_import_configuration; - import_config.AssetFilename.init(&(ParentLayer->LayerArena), asset_name.c_str()); - import_config.InputBaseAssetFilePath.init(&(ParentLayer->LayerArena), parent_path.c_str()); - co_await m_asset_importer->ImportAsync(filename, import_config); + co_return; } - co_return; + + LocalArena.Clear(); + + auto context = reinterpret_cast(ParentLayer->ParentContext); + auto arena = &LocalArena; + const auto& editor_config = *context->ConfigurationPtr; + auto parent_path = std::filesystem::path(filename).parent_path().string(); + auto asset_name = fs::path(filename).filename().replace_extension().string(); + auto output_asset_file = fmt::format("{}.zemesh", asset_name.c_str()); + + Importers::ImportConfiguration config = {}; + config.OutputWorkingSpacePath.init(arena, editor_config.WorkingSpacePath.c_str()); + config.OutputTextureFilesPath.init(arena, editor_config.DefaultImportTexturePath.c_str()); + config.OutputAssetsPath.init(arena, editor_config.SceneDataPath.c_str()); + config.AssetName.init(arena, asset_name.c_str()); + config.OutputAssetFile.init(arena, output_asset_file.c_str()); + config.InputBaseAssetFilePath.init(arena, parent_path.c_str()); + + co_await m_asset_importer->ImportAsync(filename, config); } std::future DockspaceUIComponent::OnExitAsync() diff --git a/Tetragrama/Components/DockspaceUIComponent.h b/Tetragrama/Components/DockspaceUIComponent.h index 879264c4..db19cbc8 100644 --- a/Tetragrama/Components/DockspaceUIComponent.h +++ b/Tetragrama/Components/DockspaceUIComponent.h @@ -13,39 +13,43 @@ namespace Tetragrama::Components DockspaceUIComponent(); virtual ~DockspaceUIComponent(); - void Initialize(Layers::ImguiLayer* parent = nullptr, const char* name = "Dockspace", bool visibility = true, bool closed = false) override; + ZEngine::Core::Memory::ArenaAllocator LocalArena = {}; - void Update(ZEngine::Core::TimeStep dt) override; - virtual void Render(ZEngine::Rendering::Renderers::GraphicRenderer* const renderer, ZEngine::Hardwares::CommandBuffer* const command_buffer) override; + void Initialize(Layers::ImguiLayer* parent = nullptr, const char* name = "Dockspace", bool visibility = true, bool closed = false) override; - void RenderMenuBar(); - void RenderSaveScene(); - void RenderSaveSceneAs(); - void ResetSaveAsBuffers(); - void RenderExitPopup(); + void Update(ZEngine::Core::TimeStep dt) override; + virtual void Render(ZEngine::Rendering::Renderers::GraphicRenderer* const renderer, ZEngine::Hardwares::CommandBuffer* const command_buffer) override; + + void RenderMenuBar(); + void ResetSaveAsBuffers(); + void RenderExitPopup(); /* * Model Importer Funcs */ - void RenderImporter(); - void ResetImporterBuffers(); - static void OnAssetImporterComplete(void* const, Importers::ImporterData&&); - static void OnAssetImporterProgress(void* const, float value); - static void OnAssetImporterError(void* const, std::string_view); - static void OnAssetImporterLog(void* const, std::string_view); + void RenderImporter(); + void ResetImporterBuffers(); + static void OnAssetImporterComplete(void* const context, ZEngine::Core::Containers::ArrayView result); + static void OnAssetImporterProgress(void* const, float value); + static void OnAssetImporterError(void* const, std::string_view); + static void OnAssetImporterLog(void* const, std::string_view); /* * Editor Scene Funcs */ - static void OnEditorSceneSerializerProgress(void* const, float value); - static void OnEditorSceneSerializerComplete(void* const); - static void OnEditorSceneSerializerDeserializeComplete(void* const, EditorScene&&); - static void OnEditorSceneSerializerError(void* const, std::string_view); + void RenderLoadScene(); + void RenderSaveScene(); + void RenderSaveSceneAs(); + static void OnEditorSceneSerializerProgress(void* const, float value); + static void OnEditorSceneSerializerComplete(void* const); + static void OnEditorSceneSerializerDeserializeComplete(void* const, EditorScene&&); + static void OnEditorSceneSerializerError(void* const, std::string_view); + static void OnEditorSceneSerializerLog(void* const, std::string_view); - std::future OnNewSceneAsync(); - std::future OnOpenSceneAsync(); - std::future OnImportAssetAsync(std::string_view filename); - std::future OnExitAsync(); + std::future OnNewSceneAsync(); + std::future OnOpenSceneAsync(); + std::future OnImportAssetAsync(const char* filename); + std::future OnExitAsync(); private: static ImVec4 s_asset_importer_report_msg_color; diff --git a/Tetragrama/Components/HierarchyViewUIComponent.cpp b/Tetragrama/Components/HierarchyViewUIComponent.cpp index dc8281d8..46fa4b2a 100644 --- a/Tetragrama/Components/HierarchyViewUIComponent.cpp +++ b/Tetragrama/Components/HierarchyViewUIComponent.cpp @@ -13,6 +13,7 @@ #include #include #include +#include using namespace ZEngine; using namespace ZEngine::Helpers; @@ -58,15 +59,15 @@ namespace Tetragrama::Components void HierarchyViewUIComponent::Render(ZEngine::Rendering::Renderers::GraphicRenderer* const renderer, ZEngine::Hardwares::CommandBuffer* const command_buffer) { - auto ctx = reinterpret_cast(ParentLayer->ParentContext); - auto render_scene = ctx->CurrentScenePtr->RenderScene; + auto ctx = reinterpret_cast(ParentLayer->ParentContext); + auto current_scene = ctx->CurrentScenePtr; ImGui::Begin(Name, (CanBeClosed ? &CanBeClosed : NULL), ImGuiWindowFlags_NoCollapse); if (ImGui::BeginPopupContextWindow(Name)) { if (ImGui::MenuItem("Create Empty")) { - render_scene->CreateEntityAsync(); + current_scene->CreateSceneNode(); } ImGui::EndPopup(); } @@ -76,8 +77,8 @@ namespace Tetragrama::Components // 0 means left buttom if (ImGui::IsMouseDown(0) && ImGui::IsWindowHovered()) { - m_selected_node_identifier = -1; - Messengers::IMessenger::SendAsync(EDITOR_COMPONENT_HIERARCHYVIEW_NODE_UNSELECTED, Messengers::EmptyMessage{}); + ctx->SelectedSceneNode.store(-1, std::memory_order_release); + // Messengers::IMessenger::SendAsync(EDITOR_COMPONENT_HIERARCHYVIEW_NODE_UNSELECTED, Messengers::EmptyMessage{}); } RenderGuizmo(); @@ -87,27 +88,38 @@ namespace Tetragrama::Components void HierarchyViewUIComponent::RenderTreeNodes() { - auto ctx = reinterpret_cast(ParentLayer->ParentContext); - auto render_scene = ctx->CurrentScenePtr->RenderScene; + auto ctx = reinterpret_cast(ParentLayer->ParentContext); + auto current_scene = ctx->CurrentScenePtr; - auto root_nodes = render_scene->GetRootSceneNodes(); + if (current_scene->IsDirty()) + { + return; + } - for (int node : root_nodes) + for (int i = 0; i < (int) current_scene->Hierarchies.size(); ++i) { - RenderSceneNodeTree(node); + if (!current_scene->IsSceneNodeDeleted(i) && current_scene->Hierarchies[i].Parent == -1) + { + RenderNode(current_scene, i, ctx->SelectedSceneNode); + } } } void HierarchyViewUIComponent::RenderGuizmo() { - if (m_selected_node_identifier <= -1) + auto ctx = reinterpret_cast(ParentLayer->ParentContext); + auto current_scene = ctx->CurrentScenePtr; + + if (current_scene->IsDirty()) { return; } - // auto entity_wrapper = GraphicScene::GetSceneNodeEntityWrapper(m_selected_node_identifier); - auto ctx = reinterpret_cast(ParentLayer->ParentContext); - auto render_scene = ctx->CurrentScenePtr->RenderScene; + int selected_node = ctx->SelectedSceneNode.load(std::memory_order_acquire); + if (selected_node == -1) + { + return; + } if (auto active_editor_camera = ctx->CameraControllerPtr) { @@ -115,9 +127,9 @@ namespace Tetragrama::Components const auto camera_projection = camera->GetPerspectiveMatrix(); const auto camera_view_matrix = camera->GetViewMatrix(); - auto& global_transform = render_scene->GetSceneNodeGlobalTransform(m_selected_node_identifier); + auto& global_transform = current_scene->GlobalTransforms[selected_node]; auto initial_transform = global_transform; - auto& local_transform = render_scene->GetSceneNodeLocalTransform(m_selected_node_identifier); + auto& local_transform = current_scene->LocalTransforms[selected_node]; if (camera && IDevice::As()->IsKeyPressed(ZENGINE_KEY_F, Engine::GetWindow())) { @@ -140,7 +152,7 @@ namespace Tetragrama::Components auto delta_transform = glm::inverse(initial_transform) * global_transform; local_transform = local_transform * delta_transform; - render_scene->MarkSceneNodeAsChanged(m_selected_node_identifier); + // current_scene->MarkSceneNodeAsChanged(m_selected_node_identifier); if (ImGuizmo::IsUsing()) { @@ -154,72 +166,139 @@ namespace Tetragrama::Components } } - void HierarchyViewUIComponent::RenderSceneNodeTree(int node_identifier) + void HierarchyViewUIComponent::RenderNode(EditorScene* scene, int root_id, std::atomic_int& selected_node) { - if (node_identifier < 0) + struct StackEntry { - return; - } + int node_id; + bool open; + }; - auto ctx = reinterpret_cast(ParentLayer->ParentContext); - auto render_scene = ctx->CurrentScenePtr->RenderScene; + std::stack stack; + stack.push({root_id, false}); - const auto& node_hierarchy = render_scene->GetSceneNodeHierarchy(node_identifier); - auto node_name = render_scene->GetSceneNodeName(node_identifier); - auto node_identifier_string = fmt::format("SceneNode_{0}", node_identifier); - auto flags = (node_hierarchy.FirstChild < 0) ? (ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_Bullet | m_node_flag) : m_node_flag; - flags |= (m_selected_node_identifier == node_identifier) ? ImGuiTreeNodeFlags_Selected : 0; - auto label = (!node_name.empty()) ? std::string(node_name) : fmt::format("Node_{0}", node_identifier); - bool is_node_opened = ImGui::TreeNodeEx(node_identifier_string.c_str(), flags, "%s", label.c_str()); - - if (ImGui::IsItemClicked()) + while (!stack.empty()) { - m_selected_node_identifier = node_identifier; + auto entry = stack.top(); + stack.pop(); - auto entity = render_scene->GetSceneNodeEntityWrapper(m_selected_node_identifier); - Messengers::IMessenger::SendAsync>(EDITOR_COMPONENT_HIERARCHYVIEW_NODE_SELECTED, Messengers::GenericMessage{std::move(entity)}); - } + // Handle manual TreePop marker + if (entry.node_id == -1) + { + ImGui::TreePop(); + continue; + } - if (is_node_opened) - { - /* - * Popup features - */ - bool request_entity_removal = false; - if (ImGui::BeginPopupContextItem(node_identifier_string.c_str())) + if (scene->IsSceneNodeDeleted(entry.node_id)) + { + continue; + } + const auto& node = scene->Hierarchies[entry.node_id]; + + auto name_id = scene->NodeNames[entry.node_id]; + + bool is_selected = (selected_node == entry.node_id); + bool has_children = (node.FirstChild != -1); + + ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_SpanFullWidth; + if (!has_children) + flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; + if (is_selected) + flags |= ImGuiTreeNodeFlags_Selected; + + auto node_id = fmt::format("SceneNode_{0}", entry.node_id); + bool open = ImGui::TreeNodeEx(node_id.c_str(), flags, "%s", scene->Names[name_id].c_str()); + + if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) + { + selected_node.store(entry.node_id, std::memory_order_release); + } + + // === Drag source === + if (ImGui::BeginDragDropSource()) + { + ImGui::SetDragDropPayload("NODE_DRAG", &entry.node_id, sizeof(int)); + ImGui::EndDragDropSource(); + } + + // === Drop target === + if (ImGui::BeginDragDropTarget()) + { + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("NODE_DRAG")) + { + int dragged_id = *(const int*) payload->Data; + if (dragged_id != entry.node_id && !scene->IsSceneNodeDeleted(dragged_id)) + { + // prevent making it a child of itself or its descendants + int test = entry.node_id; + bool is_descendant = false; + while (test != -1) + { + if (test == dragged_id) + { + is_descendant = true; + break; + } + test = scene->Hierarchies[test].Parent; + } + + if (!is_descendant) + { + scene->ReparentNode(dragged_id, entry.node_id); + } + } + } + ImGui::EndDragDropTarget(); + } + + if (ImGui::BeginPopupContextItem(node_id.c_str())) { if (ImGui::MenuItem("Create Empty child")) { - render_scene->CreateEntityAsync("Empty Entity", m_selected_node_identifier, node_hierarchy.DepthLevel + 1); + scene->CreateSceneNode(entry.node_id, node.DepthLevel + 1); } if (ImGui::MenuItem("Rename")) { } + if (ImGui::MenuItem("Delete")) { - Messengers::IMessenger::SendAsync(EDITOR_COMPONENT_HIERARCHYVIEW_NODE_DELETED, Messengers::EmptyMessage{}); - request_entity_removal = true; + scene->RemoveSceneNode(entry.node_id); } ImGui::EndPopup(); } - if (request_entity_removal) + if (open && has_children) { - render_scene->RemoveNodeAsync(node_identifier); - } + // Push TreePop manually + stack.push({-1, 0}); // Marker for TreePop - if (node_hierarchy.FirstChild > -1) - { - auto sibling_scene_node_collection = render_scene->GetSceneNodeSiblingCollection(node_hierarchy.FirstChild); - // We consider first child as sibling node - sibling_scene_node_collection.emplace(std::begin(sibling_scene_node_collection), node_hierarchy.FirstChild); - for (auto sibling_identifier : sibling_scene_node_collection) + // Push children in reverse order + auto scratch = ZGetScratch(&ParentLayer->LayerArena); + ZEngine::Core::Containers::Array children; + children.init(scratch.Arena, 5); + + for (int child = node.FirstChild; child != -1; child = scene->Hierarchies[child].RightSibling) { - RenderSceneNodeTree(sibling_identifier); + if (!scene->IsSceneNodeDeleted(child)) + { + children.push(child); + } } + + for (int i = static_cast(children.size()) - 1; i >= 0; --i) + { + stack.push({children[i], false}); + } + + ZReleaseScratch(scratch); + } + else if (!has_children && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) + { + // Only pop if not marked as Leaf + ImGui::TreePop(); } - ImGui::TreePop(); } } } // namespace Tetragrama::Components diff --git a/Tetragrama/Components/HierarchyViewUIComponent.h b/Tetragrama/Components/HierarchyViewUIComponent.h index 5b2054c4..2817dcbd 100644 --- a/Tetragrama/Components/HierarchyViewUIComponent.h +++ b/Tetragrama/Components/HierarchyViewUIComponent.h @@ -22,12 +22,11 @@ namespace Tetragrama::Components void RenderTreeNodes(); void RenderGuizmo(); - void RenderSceneNodeTree(int node_identifier); + void RenderNode(EditorScene* scene, int root_id, std::atomic_int& selected); private: ImGuiTreeNodeFlags m_node_flag; bool m_is_node_opened{false}; - int m_selected_node_identifier{-1}; int m_gizmo_operation{-1}; }; } // namespace Tetragrama::Components diff --git a/Tetragrama/Editor.cpp b/Tetragrama/Editor.cpp index 85bd44ba..4e000c67 100644 --- a/Tetragrama/Editor.cpp +++ b/Tetragrama/Editor.cpp @@ -12,15 +12,19 @@ using namespace ZEngine::Helpers; using namespace Tetragrama::Layers; using namespace Tetragrama::Messengers; using namespace Tetragrama::Controllers; +using namespace Tetragrama::Managers; namespace Tetragrama { void Editor::Initialize(ArenaAllocator* arena, const char* file) { + AssetManager::Initialize(arena); + Context = ZPushStruct(arena, EditorContext); - arena->CreateSubArena(ZMega(50), &(Context->Arena)); + arena->CreateSubArena(ZMega(800), &(Context->Arena)); + Context->AssetManagerPtr = AssetManager::Instance(); Context->ConfigurationPtr = ZPushStructCtor(&(Context->Arena), EditorConfiguration); Context->CameraControllerPtr = ZPushStructCtor(&(Context->Arena), EditorCameraController); UILayer = ZPushStructCtor(&(Context->Arena), ImguiLayer); @@ -28,7 +32,7 @@ namespace Tetragrama Context->CurrentScenePtr = ZPushStructCtor(&(Context->Arena), EditorScene); - if (Helpers::secure_strlen(file)) + if (ZEngine::Helpers::secure_strlen(file)) { Context->ConfigurationPtr->ReadConfig(arena, file); @@ -59,61 +63,14 @@ namespace Tetragrama void Editor::Dispose() { - ZEngine::Engine::Dispose(); + Engine::Dispose(); + AssetManager::Shutdown(); } void Editor::Run() { - ZEngine::Engine::Run(); - } - - void EditorScene::Initialize(ZEngine::Core::Memory::ArenaAllocator* arena, const char* name) - { - Name = name; - - RenderScene = ZPushStructCtor(arena, ZEngine::Rendering::Scenes::GraphicScene); - RenderScene->IsDrawDataDirty = true; - - MeshFiles.init(arena, 1); - ModelFiles.init(arena, 1); - MaterialFiles.init(arena, 1); - Hashes.init(arena, 1); - } - - void EditorScene::Push(ZEngine::Core::Memory::ArenaAllocator* arena, const char* mesh, const char* model, const char* material) - { - uint16_t mesh_file_id = MeshFiles.size(); - uint16_t model_file_id = ModelFiles.size(); - uint16_t material_file_id = MaterialFiles.size(); - - String mesh_file = {}; - String model_file = {}; - String mat_file = {}; - - mesh_file.init(arena, mesh); - model_file.init(arena, model); - mat_file.init(arena, material); - - MeshFiles.push(mesh_file); - ModelFiles.push(model_file); - MaterialFiles.push(mat_file); - - std::stringstream ss; - ss << mesh_file_id << ":" << model_file_id << ":" << material_file_id; - auto hash = ss.str(); - - String hash_str = {}; - hash_str.init(arena, hash.c_str()); - Hashes.push(hash_str); - - Data[hash_str.c_str()] = {.MeshFileIndex = mesh_file_id, .ModelPathIndex = model_file_id, .MaterialPathIndex = material_file_id}; - - m_has_pending_change = true; - } - - bool EditorScene::HasPendingChange() const - { - return m_has_pending_change; + AssetManager::Run(); + Engine::Run(); } void EditorConfiguration::ReadConfig(ZEngine::Core::Memory::ArenaAllocator* arena, const char* file) diff --git a/Tetragrama/Editor.h b/Tetragrama/Editor.h index 4be89633..3ccae214 100644 --- a/Tetragrama/Editor.h +++ b/Tetragrama/Editor.h @@ -1,5 +1,8 @@ #pragma once +#include #include +#include +#include #include #include #include @@ -16,38 +19,6 @@ namespace Tetragrama::Serializers namespace Tetragrama { - class EditorScene - { - public: - struct Model; - - EditorScene() = default; - - void Initialize(ZEngine::Core::Memory::ArenaAllocator* arena, const char* scene_name = ""); - void Push(ZEngine::Core::Memory::ArenaAllocator* arena, const char* mesh, const char* model, const char* material); - bool HasPendingChange() const; - - const char* Name = ""; - ZEngine::Core::Containers::Array MeshFiles = {}; - ZEngine::Core::Containers::Array ModelFiles = {}; - ZEngine::Core::Containers::Array MaterialFiles = {}; - - ZEngine::Core::Containers::Array Hashes = {}; - std::map Data = {}; - - ZRawPtr(ZEngine::Rendering::Scenes::GraphicScene) RenderScene = nullptr; - - private: - std::atomic_bool m_has_pending_change; - friend struct Serializers::EditorSceneSerializer; - }; - - struct EditorScene::Model - { - uint16_t MeshFileIndex = 0xFFFF; - uint16_t ModelPathIndex = 0xFFFF; - uint16_t MaterialPathIndex = 0xFFFF; - }; struct EditorConfiguration { @@ -65,9 +36,11 @@ namespace Tetragrama struct EditorContext { ZEngine::Core::Memory::ArenaAllocator Arena = {}; + std::atomic_int SelectedSceneNode = -1; ZRawPtr(EditorConfiguration) ConfigurationPtr = nullptr; ZRawPtr(EditorScene) CurrentScenePtr = nullptr; ZRawPtr(Controllers::EditorCameraController) CameraControllerPtr = nullptr; + ZRawPtr(Managers::AssetManager) AssetManagerPtr = nullptr; }; struct Editor diff --git a/Tetragrama/EditorScene.cpp b/Tetragrama/EditorScene.cpp new file mode 100644 index 00000000..0394a7e4 --- /dev/null +++ b/Tetragrama/EditorScene.cpp @@ -0,0 +1,324 @@ +#include +#include +#include +#include + +namespace Tetragrama +{ + void EditorScene::Initialize(ZEngine::Core::Memory::ArenaAllocator* arena, const char* name) + { + arena->CreateSubArena(ZMega(200), &LocalArena); + + Name = name; + + AssetFiles.init(&LocalArena, 500); + HashToAssetFile.init(&LocalArena, 500); + + Hierarchies.init(&LocalArena, 1000); + Names.init(&LocalArena, 1000); + LocalTransforms.init(&LocalArena, 1000); + GlobalTransforms.init(&LocalArena, 1000); + NodeMeshes.init(&LocalArena, 1000); + NodeNames.init(&LocalArena, 1000); + + /* + * Root Scene node + */ + Reset(); + + InitRootNode(); + + RenderScene = ZPushStructCtor(&LocalArena, ZEngine::Rendering::Scenes::GraphicScene); + RenderScene->IsDrawDataDirty = true; + } + + bool EditorScene::HasPendingChange() const + { + return HasPendingChanges.load(std::memory_order_acquire); + } + + int EditorScene::AddHierarchyNode(int parent, int depth) + { + if (depth < 0) + { + return -1; + } + + int node_id = static_cast(Hierarchies.size()); + + // Create new node + EditorSceneNodeHierarchy new_node = {}; + new_node.Parent = parent; + new_node.DepthLevel = depth; + + Hierarchies.push(new_node); + LocalTransforms.push(glm::mat4(1.0f)); + GlobalTransforms.push(glm::mat4(1.0f)); + + if (parent >= 0) + { + auto& parent_node = Hierarchies[parent]; + + if (parent_node.FirstChild == -1) + { + // First child + parent_node.FirstChild = node_id; + parent_node.LastChild = node_id; + } + else + { + // Append to last child's sibling list in O(1) + Hierarchies[parent_node.LastChild].RightSibling = node_id; + parent_node.LastChild = node_id; + } + } + + HasPendingChanges.store(true, std::memory_order_release); + + return node_id; + } + + void EditorScene::CreateSceneNode(int parent, int depth) + { + int node_id = AddHierarchyNode(parent, depth); + if (node_id < 0) + { + ZENGINE_CORE_ERROR("EditorScene::CreateSceneNode, failed to create scene node") + return; + } + + NodeNames[node_id] = Names.size(); + auto& name = Names.push_use({}); + name.init(&LocalArena, "Empty entity"); + + NodeMeshes.insert(node_id, uuids::uuid{}); + + HasPendingChanges.store(true, std::memory_order_release); + } + + void EditorScene::RemoveSceneNode(int node_id) + { + if (node_id < 0 || node_id >= static_cast(Hierarchies.size())) + return; + + if (IsSceneNodeDeleted(node_id)) + return; + + auto& node = Hierarchies[node_id]; + + // Unlink from parent's child list + if (node.Parent >= 0) + { + auto& parent = Hierarchies[node.Parent]; + int prev = -1; + int current = parent.FirstChild; + + while (current != -1) + { + if (current == node_id) + { + if (prev == -1) + parent.FirstChild = node.RightSibling; + else + Hierarchies[prev].RightSibling = node.RightSibling; + break; + } + + prev = current; + current = Hierarchies[current].RightSibling; + } + } + + std::stack to_delete; + to_delete.push(node_id); + + while (!to_delete.empty()) + { + int current_id = to_delete.top(); + to_delete.pop(); + + auto& current_node = Hierarchies[current_id]; + + // Push children to delete stack + int child = current_node.FirstChild; + while (child != -1) + { + int next_sibling = Hierarchies[child].RightSibling; + to_delete.push(child); + child = next_sibling; + } + + // Mark as deleted + current_node.Parent = -2; + current_node.FirstChild = -1; + current_node.RightSibling = -1; + current_node.DepthLevel = -1; + + Names[current_id].clear(); + Names[current_id].append("(deleted)"); + } + + HasPendingChanges.store(true, std::memory_order_release); + } + + void EditorScene::ReparentNode(int node_id, int new_parent) + { + auto& node = Hierarchies[node_id]; + int old_parent = node.Parent; + + // unlink from old parent + if (old_parent >= 0) + { + auto& parent = Hierarchies[old_parent]; + int prev = -1; + int current = parent.FirstChild; + + while (current != -1) + { + if (current == node_id) + { + if (prev == -1) + parent.FirstChild = node.RightSibling; + else + Hierarchies[prev].RightSibling = node.RightSibling; + break; + } + + prev = current; + current = Hierarchies[current].RightSibling; + } + } + + // link to new parent + node.Parent = new_parent; + node.RightSibling = Hierarchies[new_parent].FirstChild; + Hierarchies[new_parent].FirstChild = node_id; + node.DepthLevel = Hierarchies[new_parent].DepthLevel + 1; + } + + bool EditorScene::IsSceneNodeDeleted(int node) + { + if (node < 0) + { + return true; + } + return Hierarchies[node].Parent == -2; + } + + void EditorScene::PushAssetFile(const Importers::AssetImporterOutput& data) + { + if (data.Type == Importers::AssetFileType::UNKNOWN) + { + return; + } + + EditorAssetSceneFiles asset_file = {}; + asset_file.Type = data.Type; + asset_file.Hash = ZEngine::Core::Containers::hash_compute(data.Path.c_str()); + asset_file.Path.init(&(LocalArena), data.Path.c_str()); + asset_file.RootPath.init(&(LocalArena), data.RootPath.c_str()); + + if (HashToAssetFile.contains(asset_file.Hash)) + { + ZENGINE_CORE_WARN("Asset file already exist at that location : {}", asset_file.Path.c_str()) + return; + } + + auto index = AssetFiles.size(); + AssetFiles.push(asset_file); + HashToAssetFile.insert(asset_file.Hash, index); + + HasPendingChanges.store(true, std::memory_order_release); + } + + void EditorScene::MarkDirty(bool value) + { + Dirty.store(value, std::memory_order_release); + } + + bool EditorScene::IsDirty() + { + return Dirty.load(std::memory_order_acquire); + } + + void EditorScene::Reset() + { + AssetFiles.clear(); + HashToAssetFile.clear(); + + Hierarchies.clear(); + Names.clear(); + LocalTransforms.clear(); + GlobalTransforms.clear(); + NodeMeshes.clear(); + NodeNames.clear(); + + Dirty.store(false, std::memory_order_release); + } + + void EditorScene::InitRootNode() + { + NodeNames.insert(0, 0); + auto& root_name = Names.push_use({}); + root_name.init(&LocalArena, Name); + + LocalTransforms.push(glm::mat4(1.0f)); + GlobalTransforms.push(glm::mat4(1.0f)); + + auto& node = Hierarchies.push_use(EditorSceneNodeHierarchy{}); + node.DepthLevel = 0; + NodeMeshes.insert(0, uuids::uuid{}); + } + + void EditorScene::ExtractAsync(EditorScene& scene) + { + for (const auto& file : scene.AssetFiles) + { + auto& f = AssetFiles.push_use({}); + f.Hash = file.Hash; + f.Type = file.Type; + f.Path.init(&(LocalArena), file.Path.c_str()); + f.RootPath.init(&(LocalArena), file.RootPath.c_str()); + } + + for (const auto& name : scene.Names) + { + auto& n = Names.push_use({}); + n.init(&(LocalArena), name.c_str()); + } + + for (const auto& h : scene.Hierarchies) + { + Hierarchies.push(h); + } + + for (const auto& lt : scene.LocalTransforms) + { + LocalTransforms.push(lt); + } + + for (const auto& gt : scene.GlobalTransforms) + { + GlobalTransforms.push(gt); + } + + auto node_mesh_view = scene.NodeMeshes.view(); + for (const auto& [k, v] : node_mesh_view) + { + NodeMeshes.insert(k, v); + } + + auto node_name_view = scene.NodeNames.view(); + for (const auto& [k, v] : node_name_view) + { + NodeNames.insert(k, v); + } + + auto asset_manager = Managers::AssetManager::Instance(); + + for (const auto& file : AssetFiles) + { + asset_manager->LoadAssetFile(Importers::AssetImporterOutput{.Type = file.Type, .Path = file.Path.c_str(), .RootPath = file.RootPath.c_str()}); + } + } +} // namespace Tetragrama \ No newline at end of file diff --git a/Tetragrama/EditorScene.h b/Tetragrama/EditorScene.h new file mode 100644 index 00000000..c4fe915d --- /dev/null +++ b/Tetragrama/EditorScene.h @@ -0,0 +1,81 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Tetragrama::Serializers +{ + struct EditorSceneSerializer; +} // namespace Tetragrama::Serializers + +namespace Tetragrama +{ + struct EditorAssetSceneFiles + { + Importers::AssetFileType Type = Importers::AssetFileType::UNKNOWN; + uint64_t Hash = {}; + ZEngine::Core::Containers::String Path = {}; + ZEngine::Core::Containers::String RootPath = {}; + }; + + struct EditorSceneNodeHierarchy : public Helpers::NodeHierarchy + { + /* + * NodeRef serves as a mapping between Scene NodeHierarchy & Asset NodeHierarchy + */ + Importers::AssetNodeRef NodeRef = {}; + }; + + struct EditorScene + { + void Initialize(ZEngine::Core::Memory::ArenaAllocator* arena, const char* scene_name = ""); + + bool HasPendingChange() const; + + std::atomic_bool Dirty = false; + std::atomic_bool HasPendingChanges = false; + + const char* Name = ""; + ZEngine::Core::Containers::Array Hierarchies = {}; + ZEngine::Core::Containers::Array Names = {}; + ZEngine::Core::Containers::Array LocalTransforms = {}; + ZEngine::Core::Containers::Array GlobalTransforms = {}; + ZEngine::Core::Containers::HashMap NodeMeshes = {}; + ZEngine::Core::Containers::HashMap NodeNames = {}; + + ZEngine::Core::Containers::Array Vertices = {}; + ZEngine::Core::Containers::Array Indices = {}; + + ZEngine::Core::Containers::HashMap HashToAssetFile = {}; + ZEngine::Core::Containers::Array AssetFiles = {}; + + ZEngine::Core::Memory::ArenaAllocator LocalArena = {}; + + int AddHierarchyNode(int parent, int depth); + + void CreateSceneNode(int parent = 0, int depth = 1); + void RemoveSceneNode(int node_id); + void ReparentNode(int node_id, int new_parent); + bool IsSceneNodeDeleted(int node_id); + + void PushAssetFile(const Importers::AssetImporterOutput&); + + void MarkDirty(bool value); + bool IsDirty(); + + void Reset(); + void InitRootNode(); + + void ExtractAsync(EditorScene& scene); // Todo : this should be const EditorScene& ... the map::view() props prevents us to do it... def a impl issue + + ZRawPtr(ZEngine::Rendering::Scenes::GraphicScene) RenderScene = nullptr; + }; + +} // namespace Tetragrama \ No newline at end of file diff --git a/Tetragrama/EntryPoint.cpp b/Tetragrama/EntryPoint.cpp index c52dd9e8..48110598 100644 --- a/Tetragrama/EntryPoint.cpp +++ b/Tetragrama/EntryPoint.cpp @@ -22,7 +22,7 @@ int applicationEntryPoint(int argc, char* argv[]) CLI11_PARSE(app, argc, argv); MemoryManager manager = {}; - MemoryConfiguration config = {.DefaultSize = ZGiga(1)}; + MemoryConfiguration config = {.DefaultSize = ZGiga(3u)}; manager.Initialize(config); auto arena = &(manager.ArenaAllocator); diff --git a/Tetragrama/Helpers/NodeHierarchyHelper.h b/Tetragrama/Helpers/NodeHierarchyHelper.h new file mode 100644 index 00000000..c3624ffd --- /dev/null +++ b/Tetragrama/Helpers/NodeHierarchyHelper.h @@ -0,0 +1,13 @@ +#pragma once + +namespace Tetragrama::Helpers +{ + struct NodeHierarchy + { + int Parent = -1; // -2 <-> node deleted + int FirstChild = -1; + int LastChild = -1; + int RightSibling = -1; + int DepthLevel = -1; + }; +} // namespace Tetragrama::Helpers \ No newline at end of file diff --git a/Tetragrama/Helpers/SerializerCommonHelper.cpp b/Tetragrama/Helpers/SerializerCommonHelper.cpp index 41dee762..3589fac9 100644 --- a/Tetragrama/Helpers/SerializerCommonHelper.cpp +++ b/Tetragrama/Helpers/SerializerCommonHelper.cpp @@ -2,87 +2,8 @@ #include #include -#define DEFAULT_STR_BUFFER 256 - using namespace ZEngine::Core::Containers; namespace Tetragrama::Helpers { - void SerializeStringData(std::ostream& os, ZEngine::Core::Containers::StringView str) - { - size_t f_count = str.size(); - os.write(reinterpret_cast(&f_count), sizeof(size_t)); - os.write(str.data(), f_count + 1); - } - - void DeserializeStringData(ZEngine::Core::Memory::ArenaAllocator* Arena, std::istream& in, ZEngine::Core::Containers::String& d) - { - size_t v_count; - char buf[DEFAULT_STR_BUFFER] = {0}; - in.read(reinterpret_cast(&v_count), sizeof(size_t)); - in.read(buf, v_count + 1); - - d.init(Arena, buf); - } - - void SerializeStringArrayData(std::ostream& os, ZEngine::Core::Containers::ArrayView str_view) - { - size_t count = str_view.size(); - os.write(reinterpret_cast(&count), sizeof(size_t)); - for (unsigned i = 0; i < count; ++i) - { - size_t f_count = str_view[i].size(); - os.write(reinterpret_cast(&f_count), sizeof(size_t)); - os.write(str_view[i].data(), f_count + 1); - } - } - - void SerializeMapData(std::ostream& os, const std::unordered_map& data) - { - std::vector flat_data = {}; - flat_data.reserve(data.size() * 2); - for (auto d : data) - { - flat_data.push_back(d.first); - flat_data.push_back(d.second); - } - - size_t data_count = flat_data.size(); - os.write(reinterpret_cast(&data_count), sizeof(size_t)); - os.write(reinterpret_cast(flat_data.data()), sizeof(uint32_t) * flat_data.size()); - } - - void DeserializeStringArrayData(ZEngine::Core::Memory::ArenaAllocator* Arena, std::istream& in, ZEngine::Core::Containers::Array& data) - { - size_t data_count; - in.read(reinterpret_cast(&data_count), sizeof(size_t)); - data.init(Arena, data_count); - - for (int i = 0; i < data_count; ++i) - { - size_t v_count; - char buf[DEFAULT_STR_BUFFER] = {0}; - in.read(reinterpret_cast(&v_count), sizeof(size_t)); - in.read(buf, v_count + 1); - - String v; - v.init(Arena, buf); - data.push(v); - } - } - - void DeserializeMapData(ZEngine::Core::Memory::ArenaAllocator* Arena, std::istream& in, std::unordered_map& data) - { - size_t data_count; - in.read(reinterpret_cast(&data_count), sizeof(size_t)); - - std::vector flat_data = {}; - flat_data.resize(data_count); - in.read(reinterpret_cast(flat_data.data()), sizeof(uint32_t) * data_count); - - for (int i = 0; i < data_count; i += 2) - { - data[flat_data[i]] = flat_data[i + 1]; - } - } } // namespace Tetragrama::Helpers \ No newline at end of file diff --git a/Tetragrama/Helpers/SerializerCommonHelper.h b/Tetragrama/Helpers/SerializerCommonHelper.h index 2ab41dea..ec7c5dcf 100644 --- a/Tetragrama/Helpers/SerializerCommonHelper.h +++ b/Tetragrama/Helpers/SerializerCommonHelper.h @@ -1,17 +1,169 @@ #pragma once #include +#include #include #include +#include #include -#include +#include namespace Tetragrama::Helpers { - void SerializeStringData(std::ostream&, ZEngine::Core::Containers::StringView); - void SerializeStringArrayData(std::ostream&, ZEngine::Core::Containers::ArrayView); - void SerializeMapData(std::ostream&, const std::unordered_map&); + template + static void WriteBinary(std::ostream& writer, const T& data) + { + writer.write(reinterpret_cast(&data), sizeof(T)); + } - void DeserializeStringData(ZEngine::Core::Memory::ArenaAllocator*, std::istream& in, ZEngine::Core::Containers::String& data); - void DeserializeStringArrayData(ZEngine::Core::Memory::ArenaAllocator*, std::istream&, ZEngine::Core::Containers::Array&); - void DeserializeMapData(ZEngine::Core::Memory::ArenaAllocator*, std::istream&, std::unordered_map&); + template || std::is_same_v>> + static void WriteBinaryString(std::ostream& writer, const T& str) + { + if constexpr (std::is_same_v) + + { + size_t count = str.size(); + writer.write(reinterpret_cast(&count), sizeof(size_t)); + if (count > 0) + { + writer.write(str.c_str(), count + 1); + } + } + else if constexpr (std::is_same_v) + + { + size_t count = ZEngine::Helpers::secure_strlen(str); + writer.write(reinterpret_cast(&count), sizeof(size_t)); + + if (count > 0) + { + writer.write(str, count); + } + } + } + + template + static void WriteBinaryArray(std::ostream& writer, ZEngine::Core::Containers::ArrayView arr) + { + size_t count = arr.size(); + writer.write(reinterpret_cast(&count), sizeof(size_t)); + + if constexpr (std::is_same_v) + + { + for (unsigned i = 0; i < count; ++i) + { + WriteBinaryString(writer, arr[i]); + } + } + + else + { + if (count > 0) + { + writer.write(reinterpret_cast(arr.data()), sizeof(T) * count); + } + } + } + + template + static void WriteBinaryHashMap(std::ostream& writer, ZEngine::Core::Containers::HashMap& map) + { + size_t size = map.size(); + WriteBinary(writer, size); + + auto view = map.view(); + for (const auto& [key, val] : view) + { + WriteBinary(writer, key); + WriteBinary(writer, val); + } + } + + template + static void ReadBinary(std::istream& in, T& value) + { + in.read(reinterpret_cast(&value), sizeof(T)); + } + + static void ReadBinaryString(ZEngine::Core::Memory::ArenaAllocator* arena, std::istream& in, ZEngine::Core::Containers::String& str) + { + size_t size = 0; + char buf[DEFAULT_STR_BUFFER] = {0}; + in.read(reinterpret_cast(&size), sizeof(size_t)); + + if (size > 0) + { + in.read(buf, size + 1); + str.init(arena, buf); + } + } + + static void ReadBinaryCString(ZEngine::Core::Memory::ArenaAllocator* arena, std::istream& in, char* str) + { + size_t size = 0; + in.read(reinterpret_cast(&size), sizeof(size_t)); + + if (size > 0) + { + in.read(str, size); + } + } + + template + static void ReadBinaryArray(ZEngine::Core::Memory::ArenaAllocator* arena, std::istream& in, ZEngine::Core::Containers::Array& arr) + { + size_t size = 0; + ReadBinary(in, size); + arr.init(arena, size, size); + + if constexpr (std::is_same_v) + + { + for (unsigned i = 0; i < size; ++i) + { + auto& str = arr[i]; + ReadBinaryString(arena, in, str); + } + } + else + { + if (size > 0) + { + in.read(reinterpret_cast(arr.data()), sizeof(T) * size); + } + } + } + + template + static void ReadHashMap(ZEngine::Core::Memory::ArenaAllocator* arena, std::istream& in, ZEngine::Core::Containers::HashMap& map) + { + size_t size = 0; + ReadBinary(in, size); + map.init(arena, size); + + for (uint32_t i = 0; i < size; ++i) + { + T key; + U val; + + if constexpr (std::is_same_v) + { + ReadBinaryString(arena, in, key); + } + else + { + ReadBinary(in, key); + } + + if constexpr (std::is_same_v) + { + ReadBinaryString(arena, in, val); + } + else + { + ReadBinary(in, val); + } + map.insert(key, val); + } + } } // namespace Tetragrama::Helpers \ No newline at end of file diff --git a/Tetragrama/Importers/AssetTypes.h b/Tetragrama/Importers/AssetTypes.h new file mode 100644 index 00000000..293e00a7 --- /dev/null +++ b/Tetragrama/Importers/AssetTypes.h @@ -0,0 +1,97 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +namespace Tetragrama::Importers +{ + enum AssetFileType : uint8_t + { + UNKNOWN = 0, + MESH, + MATERIAL, + TEXTURES + }; + + struct AssetSubMesh + { + uuids::uuid MaterialUUID = {}; + uint32_t VertexCount = 0; + uint32_t IndexCount = 0; + uint32_t VertexOffset = 0; + uint32_t IndexOffset = 0; + uint32_t StreamOffset = 0; + uint32_t IndexStreamOffset = 0; + uint32_t VertexUnitStreamSize = 0; + uint32_t IndexUnitStreamSize = 0; + uint32_t TotalByteSize = 0; + }; + + struct AssetMesh + { + uuids::uuid MeshUUID = {}; + ZEngine::Core::Containers::Array Vertices = {}; + ZEngine::Core::Containers::Array Indices = {}; + ZEngine::Core::Containers::Array SubMeshes = {}; + }; + + struct AssetMaterial + { + ZEngine::Core::Containers::String Name = {}; + uuids::uuid MaterialUUID = {}; + uuids::uuid AlbedoTexUUID = {}; + uuids::uuid EmissiveTexUUID = {}; + uuids::uuid NormalTexUUID = {}; + uuids::uuid OpacityTexUUID = {}; + uuids::uuid SpecularTexUUID = {}; + float AmbientColor[4] = {0}; + float AlbedoColor[4] = {0}; + float EmissiveColor[4] = {0}; + float RoughnessColor[4] = {0}; + float SpecularColor[4] = {0}; + float Factors[4] = {0}; + }; + + struct AssetTexture + { + uuids::uuid TextureUUID = {}; + ZEngine::Core::Containers::String Path = {}; + }; + + struct AssetNodeHierarchy + { + uuids::uuid NodeHierarchyUUID = {}; + uuids::uuid MeshUUID = {}; + ZEngine::Core::Containers::Array Hierarchies = {}; + ZEngine::Core::Containers::Array LocalTransforms = {}; + ZEngine::Core::Containers::Array GlobalTransforms = {}; + ZEngine::Core::Containers::Array Names = {}; + ZEngine::Core::Containers::Array MaterialNames = {}; + ZEngine::Core::Containers::HashMap NodeNames = {}; + ZEngine::Core::Containers::HashMap NodeMeshes = {}; + ZEngine::Core::Containers::HashMap NodeMaterials = {}; + }; + + struct AssetNodeRef + { + uint32_t AssetNodeHandle = 0xFFFFFFFF; + int NodeHierarchyIndex = -1; + + bool IsValid() const + { + return (AssetNodeHandle != 0xFFFFFFFF) && (NodeHierarchyIndex != -1); + } + }; + + struct AssetFile + { + const char* Name = nullptr; + AssetNodeHierarchy Hierarchy = {}; + AssetMesh Mesh = {}; + ZEngine::Core::Containers::Array Materials = {}; + ZEngine::Core::Containers::Array Textures = {}; + }; +} // namespace Tetragrama::Importers diff --git a/Tetragrama/Importers/AssimpImporter.cpp b/Tetragrama/Importers/AssimpImporter.cpp index c5c205c2..e8cf98f9 100644 --- a/Tetragrama/Importers/AssimpImporter.cpp +++ b/Tetragrama/Importers/AssimpImporter.cpp @@ -2,7 +2,6 @@ #include #include #include -#include #include #include #include @@ -12,6 +11,7 @@ using namespace Tetragrama::Helpers; using namespace ZEngine::Rendering::Meshes; using namespace ZEngine::Rendering::Scenes; using namespace ZEngine::Core::Containers; +using namespace uuids; namespace fs = std::filesystem; @@ -24,11 +24,11 @@ namespace Tetragrama::Importers AssimpImporter::~AssimpImporter() {} - std::future AssimpImporter::ImportAsync(std::string_view filename, ImportConfiguration config) + std::future AssimpImporter::ImportAsync(const char* filename, ImportConfiguration& config) { - ThreadPoolHelper::Submit([this, path = std::string(filename.data()), config] { + ThreadPoolHelper::Submit([this, path = filename, config] { std::unique_lock l(m_mutex); - Arena.Clear(); + m_is_importing.store(true, std::memory_order_release); Assimp::Importer importer{}; @@ -45,21 +45,42 @@ namespace Tetragrama::Importers } else { - ImporterData import_data = {}; - - ExtractMeshes(scene, import_data); - ExtractMaterials(scene, import_data); - ExtractTextures(scene, import_data); - CreateHierachyScene(scene, import_data); - /* - * Serialization of ImporterData - */ - REPORT_LOG(Context, "Serializing model...") - SerializeImporterData(&Arena, import_data, config); + std::random_device rd; + std::mt19937 generator(rd()); + uuid_random_generator gen(&generator); + + AssetMesh mesh = {}; + AssetNodeHierarchy hierarchies = {}; + Array materials = {}; + Array textures = {}; + + ExtractMeshes(&Arena, scene, gen, mesh); + ExtractMaterials(&Arena, scene, gen, materials, hierarchies); + ExtractTextures(&Arena, scene, gen, materials, textures); + CreateHierachy(&Arena, scene, gen, hierarchies, mesh, materials); + CopyTextureFiles(&Arena, textures, config); + + Array outputs = {}; + outputs.init(&Arena, 100); + + auto out_m = SerializeMeshAssetFile(&Arena, mesh, hierarchies, config); + outputs.push(out_m); + + for (unsigned i = 0; i < materials.size(); ++i) + { + auto& material = materials[i]; + auto out_mat = SerializeMaterialAssetFile(&Arena, material, config); + outputs.push(out_mat); + } + + auto out_tex = SerializeTextureAssetFiles(&Arena, ArrayView{textures}, config); + outputs.push(out_tex); + + auto result = fmt::format("{0}{1}{2}", config.OutputAssetsPath.c_str(), PLATFORM_OS_BACKSLASH, config.OutputAssetFile.c_str()); if (m_complete_callback) { - m_complete_callback(Context, std::move(import_data)); + m_complete_callback(Context, ArrayView{outputs}); } } @@ -67,25 +88,42 @@ namespace Tetragrama::Importers importer.FreeScene(); m_is_importing.store(false, std::memory_order_release); + + Arena.Clear(); }); co_return; } - void AssimpImporter::ExtractMeshes(const aiScene* scene, ImporterData& importer_data) + void AssimpImporter::ExtractMeshes(ZEngine::Core::Memory::ArenaAllocator* arena, const aiScene* scene, uuids::uuid_random_generator& generator, AssetMesh& mesh) { if ((!scene) || (!scene->HasMeshes())) { return; } - uint32_t number_of_meshes = scene->mNumMeshes; - importer_data.Scene.Meshes.reserve(number_of_meshes); + unsigned int t_vertices = 0; + unsigned int t_indices = 0; + unsigned int t_meshes = scene->mNumMeshes; - for (uint32_t m = 0; m < number_of_meshes; ++m) + for (unsigned int i = 0; i < t_meshes; ++i) { + aiMesh* mesh = scene->mMeshes[i]; + t_vertices += mesh->mNumVertices; + t_indices += mesh->mNumFaces * 3; // assuming triangulated + } + + mesh.MeshUUID = generator(); + mesh.SubMeshes.init(arena, t_meshes); + mesh.Vertices.init(arena, t_vertices); + mesh.Indices.init(arena, t_indices); - REPORT_LOG(Context, fmt::format("Extrating Meshes : {0}/{1} ", (m + 1), number_of_meshes).c_str()) + uint32_t VertexOffset = 0; + uint32_t IndexOffset = 0; + + for (uint32_t m = 0; m < t_meshes; ++m) + { + REPORT_LOG(Context, fmt::format("Extrating Meshes : {0}/{1} ", (m + 1), t_meshes).c_str()) aiMesh* ai_mesh = scene->mMeshes[m]; @@ -98,16 +136,16 @@ namespace Tetragrama::Importers const aiVector3D normal = ai_mesh->mNormals[v]; const aiVector3D texture = ai_mesh->HasTextureCoords(0) ? ai_mesh->mTextureCoords[0][v] : aiVector3D{}; - importer_data.Scene.Vertices.push_back(position.x); - importer_data.Scene.Vertices.push_back(position.y); - importer_data.Scene.Vertices.push_back(position.z); + mesh.Vertices.push(position.x); + mesh.Vertices.push(position.y); + mesh.Vertices.push(position.z); - importer_data.Scene.Vertices.push_back(normal.x); - importer_data.Scene.Vertices.push_back(normal.y); - importer_data.Scene.Vertices.push_back(normal.z); + mesh.Vertices.push(normal.x); + mesh.Vertices.push(normal.y); + mesh.Vertices.push(normal.z); - importer_data.Scene.Vertices.push_back(texture.x); - importer_data.Scene.Vertices.push_back(texture.y); + mesh.Vertices.push(texture.x); + mesh.Vertices.push(texture.y); vertex_count++; } @@ -120,30 +158,30 @@ namespace Tetragrama::Importers for (int fidx = 0; fidx < ai_face.mNumIndices; ++fidx) { - importer_data.Scene.Indices.push_back(ai_face.mIndices[fidx]); + mesh.Indices.push(ai_face.mIndices[fidx]); index_count++; } } - MeshVNext& mesh = importer_data.Scene.Meshes.emplace_back(); - mesh.VertexCount = vertex_count; - mesh.VertexOffset = importer_data.VertexOffset; - mesh.VertexUnitStreamSize = sizeof(float) * (3 + 3 + 2) /*pos-cmp + normal-cmp + tex-cmp*/; - mesh.StreamOffset = (mesh.VertexUnitStreamSize * mesh.VertexOffset); - mesh.IndexOffset = importer_data.IndexOffset; - mesh.IndexCount = index_count; - mesh.IndexUnitStreamSize = sizeof(uint32_t); - mesh.IndexStreamOffset = (mesh.IndexUnitStreamSize * mesh.IndexOffset); - mesh.TotalByteSize = (mesh.VertexCount * mesh.VertexUnitStreamSize) + (mesh.IndexCount * mesh.IndexUnitStreamSize); + auto& subMesh = mesh.SubMeshes.push_use({}); + subMesh.VertexCount = vertex_count; + subMesh.VertexOffset = VertexOffset; + subMesh.VertexUnitStreamSize = sizeof(float) * (3 + 3 + 2) /*pos-cmp + normal-cmp + tex-cmp*/; + subMesh.StreamOffset = (subMesh.VertexUnitStreamSize * subMesh.VertexOffset); + subMesh.IndexOffset = IndexOffset; + subMesh.IndexCount = index_count; + subMesh.IndexUnitStreamSize = sizeof(uint32_t); + subMesh.IndexStreamOffset = (subMesh.IndexUnitStreamSize * subMesh.IndexOffset); + subMesh.TotalByteSize = (subMesh.VertexCount * subMesh.VertexUnitStreamSize) + (subMesh.IndexCount * subMesh.IndexUnitStreamSize); /* Computing offset data */ - importer_data.VertexOffset += ai_mesh->mNumVertices; - importer_data.IndexOffset += index_count; + VertexOffset += ai_mesh->mNumVertices; + IndexOffset += index_count; } } - void AssimpImporter::ExtractMaterials(const aiScene* scene, ImporterData& importer_data) + void AssimpImporter::ExtractMaterials(ZEngine::Core::Memory::ArenaAllocator* arena, const aiScene* scene, uuids::uuid_random_generator& generator, ZEngine::Core::Containers::Array& materials, AssetNodeHierarchy& model) { if (!scene) { @@ -151,43 +189,64 @@ namespace Tetragrama::Importers } uint32_t number_of_materials = scene->mNumMaterials; - importer_data.Scene.Materials.resize(number_of_materials); - importer_data.Scene.MaterialFiles.resize(number_of_materials); - importer_data.Scene.MaterialNames.resize(number_of_materials); + materials.init(arena, number_of_materials, number_of_materials); + model.MaterialNames.init(arena, number_of_materials, number_of_materials); for (uint32_t m = 0; m < number_of_materials; ++m) { REPORT_LOG(Context, fmt::format("Extrating materials : {0}/{1}", (m + 1), number_of_materials).c_str()) - aiColor4D color; - aiMaterial* ai_material = scene->mMaterials[m]; - aiString mat_name = ai_material->GetName(); - MeshMaterial& material = importer_data.Scene.Materials[m]; + aiColor4D color; + aiMaterial* ai_material = scene->mMaterials[m]; + aiString mat_name = ai_material->GetName(); + AssetMaterial& material = materials[m]; + + material.MaterialUUID = generator(); + + { + std::stringstream ss; + ss << material.MaterialUUID; + auto mat_uuid = ss.str(); + material.Name.init(arena, (mat_name.C_Str() ? mat_name.C_Str() : mat_uuid.c_str())); + ss.clear(); + } - importer_data.Scene.MaterialNames[m] = (mat_name.C_Str() ? std::string(mat_name.C_Str()) : std::string("")); + model.MaterialNames[m].init(arena, material.Name.c_str()); if (aiGetMaterialColor(ai_material, AI_MATKEY_COLOR_AMBIENT, &color) == AI_SUCCESS) { - material.AmbientColor = ZEngine::Rendering::gpuvec4{color.r, color.g, color.b, color.a}; - material.AmbientColor.w = glm::min(material.AmbientColor.w, 1.0f); + material.AmbientColor[0] = color.r; + material.AmbientColor[1] = color.g; + material.AmbientColor[2] = color.b; + material.AmbientColor[3] = color.a; + material.AmbientColor[3] = glm::min(material.AmbientColor[3], 1.0f); } if (aiGetMaterialColor(ai_material, AI_MATKEY_COLOR_DIFFUSE, &color) == AI_SUCCESS) { - material.AlbedoColor = ZEngine::Rendering::gpuvec4{color.r, color.g, color.b, color.a}; - material.AlbedoColor.w = std::min(material.AlbedoColor.w, 1.0f); + material.AlbedoColor[0] = color.r; + material.AlbedoColor[1] = color.g; + material.AlbedoColor[2] = color.b; + material.AlbedoColor[3] = color.a; + material.AlbedoColor[3] = glm::min(material.AlbedoColor[3], 1.0f); } if (aiGetMaterialColor(ai_material, AI_MATKEY_COLOR_SPECULAR, &color) == AI_SUCCESS) { - material.SpecularColor = ZEngine::Rendering::gpuvec4{color.r, color.g, color.b, color.a}; - material.SpecularColor.w = std::min(material.SpecularColor.w, 1.0f); + material.SpecularColor[0] = color.r; + material.SpecularColor[1] = color.g; + material.SpecularColor[2] = color.b; + material.SpecularColor[3] = color.a; + material.SpecularColor[3] = glm::min(material.SpecularColor[3], 1.0f); } if (aiGetMaterialColor(ai_material, AI_MATKEY_COLOR_EMISSIVE, &color) == AI_SUCCESS) { - material.EmissiveColor = ZEngine::Rendering::gpuvec4{color.r, color.g, color.b, color.a}; - material.EmissiveColor.w = std::min(material.EmissiveColor.w, 1.0f); + material.EmissiveColor[0] = color.r; + material.EmissiveColor[1] = color.g; + material.EmissiveColor[2] = color.b; + material.EmissiveColor[3] = color.a; + material.EmissiveColor[3] = glm::min(material.EmissiveColor[3], 1.0f); } float opacity = 1.0f; @@ -195,27 +254,27 @@ namespace Tetragrama::Importers if (aiGetMaterialFloat(ai_material, AI_MATKEY_OPACITY, &opacity) == AI_SUCCESS) { - material.Factors.x = glm::clamp(1.f - opacity, 0.0f, 1.0f); - if (material.Factors.x >= (1.0f - opaqueness_threshold)) + material.Factors[0] = glm::clamp(1.f - opacity, 0.0f, 1.0f); + if (material.Factors[0] >= (1.0f - opaqueness_threshold)) { - material.Factors.x = 0.0f; + material.Factors[0] = 0.0f; } } if (aiGetMaterialColor(ai_material, AI_MATKEY_COLOR_TRANSPARENT, &color) == AI_SUCCESS) { const float component_as_opacity = std::max(std::max(color.r, color.g), color.b); - material.Factors.x = glm::clamp(component_as_opacity, 0.0f, 1.0f); - if (material.Factors.x >= (1.0f - opaqueness_threshold)) + material.Factors[0] = glm::clamp(component_as_opacity, 0.0f, 1.0f); + if (material.Factors[0] >= (1.0f - opaqueness_threshold)) { - material.Factors.x = 0.0f; + material.Factors[0] = 0.0f; } - material.Factors.z = 0.5f; + material.Factors[2] = 0.5f; } } } - void AssimpImporter::ExtractTextures(const aiScene* scene, ImporterData& importer_data) + void AssimpImporter::ExtractTextures(ZEngine::Core::Memory::ArenaAllocator* arena, const aiScene* scene, uuids::uuid_random_generator& generator, ZEngine::Core::Containers::Array& materials, ZEngine::Core::Containers::Array& textures) { if (!scene) { @@ -231,418 +290,208 @@ namespace Tetragrama::Importers uint32_t texture_flags = 0; uint32_t number_of_materials = scene->mNumMaterials; + + textures.init(arena, number_of_materials); + for (uint32_t m = 0; m < number_of_materials; ++m) { REPORT_LOG(Context, fmt::format("Extrating Material's textures: {0}/{1} materials", (m + 1), number_of_materials).c_str()) - aiMaterial* ai_material = scene->mMaterials[m]; + aiMaterial* ai_material = scene->mMaterials[m]; + AssetMaterial& material = materials[m]; if (aiGetMaterialTexture(ai_material, aiTextureType_DIFFUSE, 0, &texture_filename, &texture_mapping, &uv_index, &blend, &texture_operation, texture_map_mode, &texture_flags) == AI_SUCCESS) { - secure_strcpy(importer_data.Scene.MaterialFiles[m].AlbedoTexture, MAX_FILE_PATH_COUNT, texture_filename.C_Str()); + AssetTexture& texture = textures.push_use({}); + texture.TextureUUID = generator(); + material.AlbedoTexUUID = texture.TextureUUID; + texture.Path.init(arena, texture_filename.C_Str()); } if (aiGetMaterialTexture(ai_material, aiTextureType_SPECULAR, 0, &texture_filename, &texture_mapping, &uv_index, &blend, &texture_operation, texture_map_mode, &texture_flags) == AI_SUCCESS) { - secure_strcpy(importer_data.Scene.MaterialFiles[m].SpecularTexture, MAX_FILE_PATH_COUNT, texture_filename.C_Str()); + AssetTexture& texture = textures.push_use({}); + texture.TextureUUID = generator(); + material.SpecularTexUUID = texture.TextureUUID; + texture.Path.init(arena, texture_filename.C_Str()); } if (aiGetMaterialTexture(ai_material, aiTextureType_EMISSIVE, 0, &texture_filename, &texture_mapping, &uv_index, &blend, &texture_operation, texture_map_mode, &texture_flags) == AI_SUCCESS) { - secure_strcpy(importer_data.Scene.MaterialFiles[m].EmissiveTexture, MAX_FILE_PATH_COUNT, texture_filename.C_Str()); + AssetTexture& texture = textures.push_use({}); + texture.TextureUUID = generator(); + material.EmissiveTexUUID = texture.TextureUUID; + texture.Path.init(arena, texture_filename.C_Str()); } if (aiGetMaterialTexture(ai_material, aiTextureType_NORMALS, 0, &texture_filename, &texture_mapping, &uv_index, &blend, &texture_operation, texture_map_mode, &texture_flags) == AI_SUCCESS) { - secure_strcpy(importer_data.Scene.MaterialFiles[m].NormalTexture, MAX_FILE_PATH_COUNT, texture_filename.C_Str()); + AssetTexture& texture = textures.push_use({}); + texture.TextureUUID = generator(); + material.NormalTexUUID = texture.TextureUUID; + texture.Path.init(arena, texture_filename.C_Str()); } - if (importer_data.Scene.Materials[m].NormalMap == 0xFFFFFFFF) + if (material.NormalTexUUID.is_nil()) { if (aiGetMaterialTexture(ai_material, aiTextureType_HEIGHT, 0, &texture_filename, &texture_mapping, &uv_index, &blend, &texture_operation, texture_map_mode, &texture_flags) == AI_SUCCESS) { - secure_strcpy(importer_data.Scene.MaterialFiles[m].NormalTexture, MAX_FILE_PATH_COUNT, texture_filename.C_Str()); + AssetTexture& texture = textures.push_use({}); + texture.TextureUUID = generator(); + material.NormalTexUUID = texture.TextureUUID; + texture.Path.init(arena, texture_filename.C_Str()); } } if (aiGetMaterialTexture(ai_material, aiTextureType_OPACITY, 0, &texture_filename, &texture_mapping, &uv_index, &blend, &texture_operation, texture_map_mode, &texture_flags) == AI_SUCCESS) { - secure_strcpy(importer_data.Scene.MaterialFiles[m].OpacityTexture, MAX_FILE_PATH_COUNT, texture_filename.C_Str()); - importer_data.Scene.Materials[m].Factors.z = 0.5f; + AssetTexture& texture = textures.push_use({}); + texture.TextureUUID = generator(); + material.OpacityTexUUID = texture.TextureUUID; + texture.Path.init(arena, texture_filename.C_Str()); + material.Factors[2] = 0.5f; } } } - void AssimpImporter::CreateHierachyScene(const aiScene* scene, ImporterData& importer_data) + void AssimpImporter::CreateHierachy(ZEngine::Core::Memory::ArenaAllocator* arena, const aiScene* scene, uuids::uuid_random_generator& generator, AssetNodeHierarchy& AssetNode, AssetMesh& asset_mesh, ZEngine::Core::Containers::Array& materials) { if (!scene || !(scene->mRootNode)) { return; } - TraverseNode(scene, &(importer_data.Scene), scene->mRootNode, -1, 0); + AssetNode.NodeHierarchyUUID = generator(); + AssetNode.MeshUUID = asset_mesh.MeshUUID; + + AssetNode.Hierarchies.init(arena, 10); + AssetNode.LocalTransforms.init(arena, 10); + AssetNode.GlobalTransforms.init(arena, 10); + AssetNode.Names.init(arena, 10); + AssetNode.NodeNames.init(arena, 10); + AssetNode.NodeMeshes.init(arena, 10); + AssetNode.NodeMaterials.init(arena, 10); + + TraverseNode(arena, scene, scene->mRootNode, AssetNode, asset_mesh, materials, -1, 0); } - void AssimpImporter::TraverseNode(const aiScene* ai_scene, SceneRawData* const scene, const aiNode* node, int parent_node_id, int depth_level) + void AssimpImporter::TraverseNode(ZEngine::Core::Memory::ArenaAllocator* arena, const aiScene* ai_scene, const aiNode* node, AssetNodeHierarchy& hierarchy, AssetMesh& asset_mesh, ZEngine::Core::Containers::Array& materials, int parent_node_id, int depth_level) { - auto node_id = scene->AddNode(parent_node_id, depth_level); - scene->NodeNames[node_id] = scene->Names.size(); - scene->Names.push_back(node->mName.C_Str() ? std::string(node->mName.C_Str()) : std::string{""}); + auto node_id = AddNode(hierarchy, parent_node_id, depth_level); + hierarchy.NodeNames[node_id] = hierarchy.Names.size(); + auto& name = hierarchy.Names.push_use({}); + name.init(arena, node->mName.C_Str() ? node->mName.C_Str() : ""); - scene->GlobalTransforms[node_id] = glm::mat4(1.0f); - scene->LocalTransforms[node_id] = ConvertToMat4(node->mTransformation); + hierarchy.GlobalTransforms[node_id] = glm::mat4(1.0f); + hierarchy.LocalTransforms[node_id] = ConvertToMat4(node->mTransformation); for (uint32_t i = 0; i < node->mNumMeshes; ++i) { - auto sub_node_id = scene->AddNode(node_id, depth_level + 1); - uint32_t mesh = node->mMeshes[i]; - aiString mesh_name = ai_scene->mMeshes[mesh]->mName; - - scene->NodeNames[sub_node_id] = scene->Names.size(); - scene->Names.push_back(mesh_name.C_Str() ? std::string(mesh_name.C_Str()) : std::string{""}); - - scene->NodeMeshes[sub_node_id] = mesh; - scene->NodeMaterials[sub_node_id] = ai_scene->mMeshes[mesh]->mMaterialIndex; - scene->GlobalTransforms[sub_node_id] = glm::mat4(1.0f); - scene->LocalTransforms[sub_node_id] = glm::mat4(1.0f); + auto sub_node_id = AddNode(hierarchy, node_id, depth_level + 1); + uint32_t mesh = node->mMeshes[i]; + uint32_t material_id = ai_scene->mMeshes[mesh]->mMaterialIndex; + + hierarchy.NodeNames[sub_node_id] = hierarchy.Names.size(); + auto& n = hierarchy.Names.push_use({}); + aiString mesh_name = ai_scene->mMeshes[mesh]->mName; + n.init(arena, mesh_name.C_Str() ? mesh_name.C_Str() : ""); + + hierarchy.NodeMeshes[sub_node_id] = mesh; + hierarchy.NodeMaterials[sub_node_id] = material_id; + hierarchy.GlobalTransforms[sub_node_id] = glm::mat4(1.0f); + hierarchy.LocalTransforms[sub_node_id] = glm::mat4(1.0f); + + auto& asset_mat = materials[material_id]; + auto& sub_mesh = asset_mesh.SubMeshes[mesh]; + sub_mesh.MaterialUUID = asset_mat.MaterialUUID; } for (uint32_t child = 0; child < node->mNumChildren; ++child) { - TraverseNode(ai_scene, scene, node->mChildren[child], node_id, (depth_level + 1)); + TraverseNode(arena, ai_scene, node->mChildren[child], hierarchy, asset_mesh, materials, node_id, (depth_level + 1)); } } - void AssimpImporter::SerializeImporterData(ZEngine::Core::Memory::ArenaAllocator* arena, ImporterData& importer_data, const ImportConfiguration& config) + void AssimpImporter::CopyTextureFiles(ZEngine::Core::Memory::ArenaAllocator* arena, ZEngine::Core::Containers::Array& textures, const ImportConfiguration& config) { - importer_data.Name = config.AssetFilename; + /* + * Normalize file naming + */ + auto dst_dir = fmt::format("{0}{1}{2}{3}{4}", config.OutputWorkingSpacePath.c_str(), PLATFORM_OS_BACKSLASH, config.OutputTextureFilesPath.c_str(), PLATFORM_OS_BACKSLASH, config.AssetName.c_str()); - if (!config.OutputMeshFilePath.empty()) - { - std::string output_mesh_file = fmt::format("{}.zemeshes", config.AssetFilename.c_str()); - std::string fullname_path = fmt::format("{0}{1}{2}{3}{4}", config.OutputWorkingSpacePath.c_str(), PLATFORM_OS_BACKSLASH, config.OutputMeshFilePath.c_str(), PLATFORM_OS_BACKSLASH, output_mesh_file.c_str()); - std::ofstream out(fullname_path, std::ios::binary | std::ios::trunc); + auto CreateBaseDirectoryFn = [](std::string_view filename) -> void { + auto base_dir = fs::absolute(filename).parent_path(); - if (out.is_open()) + std::error_code err = {}; + if (!fs::exists(base_dir)) { - out.seekp(std::ios::beg); - - size_t mesh_count = importer_data.Scene.Meshes.size(); - out.write(reinterpret_cast(&mesh_count), sizeof(size_t)); - out.write(reinterpret_cast(importer_data.Scene.Meshes.data()), sizeof(ZEngine::Rendering::Meshes::MeshVNext) * mesh_count); - - size_t indices_count = importer_data.Scene.Indices.size(); - out.write(reinterpret_cast(&indices_count), sizeof(size_t)); - out.write(reinterpret_cast(importer_data.Scene.Indices.data()), sizeof(uint32_t) * indices_count); - - size_t vertice_count = importer_data.Scene.Vertices.size(); - out.write(reinterpret_cast(&vertice_count), sizeof(size_t)); - out.write(reinterpret_cast(importer_data.Scene.Vertices.data()), sizeof(float) * vertice_count); + fs::create_directories(base_dir, err); } - out.close(); + }; - importer_data.SerializedMeshesPath.init(arena, output_mesh_file.c_str()); - } + auto scratch = ZGetScratch(arena); + Array src_tex_files = {}; + Array dst_tex_files = {}; + src_tex_files.init(scratch.Arena, textures.size()); + dst_tex_files.init(scratch.Arena, textures.size()); - if (!config.OutputMaterialsPath.empty() && !config.OutputTextureFilesPath.empty()) + for (auto& tex : textures) { - /* - * Normalize file naming - */ - auto dst_dir = fmt::format("{0}{1}{2}{3}{4}", config.OutputWorkingSpacePath.c_str(), PLATFORM_OS_BACKSLASH, config.OutputTextureFilesPath.c_str(), PLATFORM_OS_BACKSLASH, config.AssetFilename.c_str()); - - auto create_base_dir_fn = [](std::string_view filename) -> void { - auto base_dir = fs::absolute(filename).parent_path(); - - std::error_code err = {}; - if (!fs::exists(base_dir)) - { - fs::create_directories(base_dir, err); - } - }; - - auto scratch = ZGetScratch(arena); - Array src_tex_files = {}; - Array dst_tex_files = {}; - src_tex_files.init(scratch.Arena, 10); - dst_tex_files.init(scratch.Arena, 10); - - for (auto& mat_file : importer_data.Scene.MaterialFiles) - { - if (!std::string_view(mat_file.AlbedoTexture).empty()) - { - auto src_file = fmt::format("{0}{1}{2}", config.InputBaseAssetFilePath.c_str(), PLATFORM_OS_BACKSLASH, mat_file.AlbedoTexture); - auto dst_file = fmt::format("{0}{1}{2}", dst_dir, PLATFORM_OS_BACKSLASH, mat_file.AlbedoTexture); - create_base_dir_fn(dst_file); - - ZEngine::Helpers::secure_strcpy(mat_file.AlbedoTexture, MAX_FILE_PATH_COUNT, dst_file.c_str()); - - auto& sf = src_tex_files.push_use({}); - auto& df = dst_tex_files.push_use({}); - - sf.init(scratch.Arena, src_file.c_str()); - df.init(scratch.Arena, dst_file.c_str()); - } - - if (!std::string_view(mat_file.EmissiveTexture).empty()) - { - auto src_file = fmt::format("{0}{1}{2}", config.InputBaseAssetFilePath.c_str(), PLATFORM_OS_BACKSLASH, mat_file.EmissiveTexture); - auto dst_file = fmt::format("{0}{1}{2}", dst_dir, PLATFORM_OS_BACKSLASH, mat_file.EmissiveTexture); - - create_base_dir_fn(dst_file); - - ZEngine::Helpers::secure_strcpy(mat_file.EmissiveTexture, MAX_FILE_PATH_COUNT, dst_file.c_str()); - - auto& sf = src_tex_files.push_use({}); - auto& df = dst_tex_files.push_use({}); - - sf.init(scratch.Arena, src_file.c_str()); - df.init(scratch.Arena, dst_file.c_str()); - } - - if (!std::string_view(mat_file.NormalTexture).empty()) - { - auto src_file = fmt::format("{0}{1}{2}", config.InputBaseAssetFilePath.c_str(), PLATFORM_OS_BACKSLASH, mat_file.NormalTexture); - auto dst_file = fmt::format("{0}{1}{2}", dst_dir, PLATFORM_OS_BACKSLASH, mat_file.NormalTexture); - - create_base_dir_fn(dst_file); - ZEngine::Helpers::secure_strcpy(mat_file.NormalTexture, MAX_FILE_PATH_COUNT, dst_file.c_str()); - - auto& sf = src_tex_files.push_use({}); - auto& df = dst_tex_files.push_use({}); - - sf.init(scratch.Arena, src_file.c_str()); - df.init(scratch.Arena, dst_file.c_str()); - } - - if (!std::string_view(mat_file.OpacityTexture).empty()) - { - auto src_file = fmt::format("{0}{1}{2}", config.InputBaseAssetFilePath.c_str(), PLATFORM_OS_BACKSLASH, mat_file.OpacityTexture); - auto dst_file = fmt::format("{0}{1}{2}", dst_dir, PLATFORM_OS_BACKSLASH, mat_file.OpacityTexture); - - create_base_dir_fn(dst_file); - - ZEngine::Helpers::secure_strcpy(mat_file.OpacityTexture, MAX_FILE_PATH_COUNT, dst_file.c_str()); - - auto& sf = src_tex_files.push_use({}); - auto& df = dst_tex_files.push_use({}); - - sf.init(scratch.Arena, src_file.c_str()); - df.init(scratch.Arena, dst_file.c_str()); - } - - if (!std::string_view(mat_file.SpecularTexture).empty()) - { - auto src_file = fmt::format("{0}{1}{2}", config.InputBaseAssetFilePath.c_str(), PLATFORM_OS_BACKSLASH, mat_file.SpecularTexture); - auto dst_file = fmt::format("{0}{1}{2}", dst_dir, PLATFORM_OS_BACKSLASH, mat_file.SpecularTexture); - - create_base_dir_fn(dst_file); - - ZEngine::Helpers::secure_strcpy(mat_file.SpecularTexture, MAX_FILE_PATH_COUNT, dst_file.c_str()); - - auto& sf = src_tex_files.push_use({}); - auto& df = dst_tex_files.push_use({}); - - sf.init(scratch.Arena, src_file.c_str()); - df.init(scratch.Arena, dst_file.c_str()); - } - } - /* - * Texture files processing - * (1) Ensuring Scene sub-dir is created - * (2) Copying files to destination - */ - - ZENGINE_VALIDATE_ASSERT(src_tex_files.size() == dst_tex_files.size(), "source files count can't be diff of destination files count") - for (int i = 0; i < src_tex_files.size(); ++i) + if (tex.Path.empty()) { - auto src = fs::absolute(src_tex_files[i].c_str()); - auto dst = fs::absolute(dst_tex_files[i].c_str()); - - std::ifstream in(src.c_str(), std::ios::binary); - std::ofstream out(dst.c_str(), std::ios::binary); - - if (!in.is_open() || !out.is_open()) - { - in.close(); - out.close(); - continue; - } - - out << in.rdbuf(); - - in.close(); - out.close(); + continue; } - ZReleaseScratch(scratch); + auto src_file = fmt::format("{0}{1}{2}", config.InputBaseAssetFilePath.c_str(), PLATFORM_OS_BACKSLASH, tex.Path.c_str()); + auto dst_file = fmt::format("{0}{1}{2}", dst_dir, PLATFORM_OS_BACKSLASH, tex.Path.c_str()); - std::string output_material_file = fmt::format("{}.zematerials", config.AssetFilename.c_str()); - std::string fullname_path = fmt::format("{0}{1}{2}{3}{4}", config.OutputWorkingSpacePath.c_str(), PLATFORM_OS_BACKSLASH, config.OutputMaterialsPath.c_str(), PLATFORM_OS_BACKSLASH, output_material_file.c_str()); - std::ofstream out(fullname_path, std::ios::binary | std::ios::trunc); + CreateBaseDirectoryFn(dst_file); - if (out.is_open()) - { - out.seekp(std::ios::beg); + auto& sf = src_tex_files.push_use({}); + auto& df = dst_tex_files.push_use({}); - size_t material_total_count = importer_data.Scene.Materials.size(); - out.write(reinterpret_cast(&material_total_count), sizeof(size_t)); - out.write(reinterpret_cast(importer_data.Scene.Materials.data()), sizeof(ZEngine::Rendering::Meshes::MeshMaterial) * material_total_count); - - size_t mat_file_count = importer_data.Scene.MaterialFiles.size(); - out.write(reinterpret_cast(&mat_file_count), sizeof(size_t)); - for (auto& mat_file : importer_data.Scene.MaterialFiles) - { - Tetragrama::Helpers::SerializeStringData(out, mat_file.AlbedoTexture); - Tetragrama::Helpers::SerializeStringData(out, mat_file.EmissiveTexture); - Tetragrama::Helpers::SerializeStringData(out, mat_file.NormalTexture); - Tetragrama::Helpers::SerializeStringData(out, mat_file.OpacityTexture); - Tetragrama::Helpers::SerializeStringData(out, mat_file.SpecularTexture); - } - } - out.close(); - - importer_data.SerializedMaterialsPath.init(arena, output_material_file.c_str()); + sf.init(scratch.Arena, src_file.c_str()); + df.init(scratch.Arena, dst_file.c_str()); } - - if (!config.OutputModelFilePath.empty()) + /* + * Texture files processing + * (1) Ensuring Scene sub-dir is created + * (2) Copying files to destination + */ + + ZENGINE_VALIDATE_ASSERT(src_tex_files.size() == dst_tex_files.size(), "source files count can't be diff of destination files count") + for (int i = 0; i < src_tex_files.size(); ++i) { - std::string output_model_file = fmt::format("{}.zemodel", config.AssetFilename.c_str()); - std::string fullname_path = fmt::format("{0}{1}{2}{3}{4}", config.OutputWorkingSpacePath.c_str(), PLATFORM_OS_BACKSLASH, config.OutputModelFilePath.c_str(), PLATFORM_OS_BACKSLASH, output_model_file.c_str()); - std::ofstream out(fullname_path, std::ios::binary | std::ios::trunc); + auto src = fs::absolute(src_tex_files[i].c_str()); + auto dst = fs::absolute(dst_tex_files[i].c_str()); - if (out.is_open()) - { - out.seekp(std::ios::beg); - - size_t local_transform_count = importer_data.Scene.LocalTransforms.size(); - out.write(reinterpret_cast(&local_transform_count), sizeof(size_t)); - out.write(reinterpret_cast(importer_data.Scene.LocalTransforms.data()), sizeof(glm::mat4) * local_transform_count); - - size_t gobal_transform_count = importer_data.Scene.GlobalTransforms.size(); - out.write(reinterpret_cast(&gobal_transform_count), sizeof(size_t)); - out.write(reinterpret_cast(importer_data.Scene.GlobalTransforms.data()), sizeof(glm::mat4) * gobal_transform_count); - - size_t node_hierarchy_count = importer_data.Scene.NodeHierarchies.size(); - out.write(reinterpret_cast(&node_hierarchy_count), sizeof(size_t)); - out.write(reinterpret_cast(importer_data.Scene.NodeHierarchies.data()), sizeof(ZEngine::Rendering::Scenes::SceneNodeHierarchy) * node_hierarchy_count); - - // Todo Kernel : This should be part of Move Engine to Arena - // - // Tetragrama::Helpers::SerializeStringArrayData(out, importer_data.Scene.Names); - // Tetragrama::Helpers::SerializeStringArrayData(out, importer_data.Scene.MaterialNames); - Tetragrama::Helpers::SerializeMapData(out, importer_data.Scene.NodeNames); - Tetragrama::Helpers::SerializeMapData(out, importer_data.Scene.NodeMeshes); - Tetragrama::Helpers::SerializeMapData(out, importer_data.Scene.NodeMaterials); - } - - out.close(); + std::ifstream in(src.c_str(), std::ios::binary); + std::ofstream out(dst.c_str(), std::ios::binary); - importer_data.SerializedModelPath.init(arena, output_model_file.c_str()); - } - } - - ImporterData AssimpImporter::DeserializeImporterData(ZEngine::Core::Memory::ArenaAllocator* arena, std::string_view model_path, std::string_view mesh_path, std::string_view material_path) - { - ImporterData deserialized_data = {}; - - if (!mesh_path.empty()) - { - std::ifstream in(mesh_path.data(), std::ios::binary); - - if (in.is_open()) + if (!in.is_open() || !out.is_open()) { - in.seekg(0, std::ios::beg); - - size_t mesh_count; - in.read(reinterpret_cast(&mesh_count), sizeof(size_t)); - deserialized_data.Scene.Meshes.resize(mesh_count); - in.read(reinterpret_cast(deserialized_data.Scene.Meshes.data()), sizeof(ZEngine::Rendering::Meshes::MeshVNext) * mesh_count); - - size_t indices_count; - in.read(reinterpret_cast(&indices_count), sizeof(size_t)); - deserialized_data.Scene.Indices.resize(indices_count); - in.read(reinterpret_cast(deserialized_data.Scene.Indices.data()), sizeof(uint32_t) * indices_count); - - size_t vertice_count; - in.read(reinterpret_cast(&vertice_count), sizeof(size_t)); - deserialized_data.Scene.Vertices.resize(vertice_count); - in.read(reinterpret_cast(deserialized_data.Scene.Vertices.data()), sizeof(float) * vertice_count); + in.close(); + out.close(); + continue; } - in.close(); - } - if (!material_path.empty()) - { - std::ifstream in(material_path.data(), std::ios::binary); - - if (in.is_open()) - { - in.seekg(0, std::ios::beg); - - size_t material_total_count; - in.read(reinterpret_cast(&material_total_count), sizeof(size_t)); - deserialized_data.Scene.Materials.resize(material_total_count); - in.read(reinterpret_cast(deserialized_data.Scene.Materials.data()), sizeof(ZEngine::Rendering::Meshes::MeshMaterial) * material_total_count); + out << in.rdbuf(); - size_t mat_file_count; - in.read(reinterpret_cast(&mat_file_count), sizeof(size_t)); - deserialized_data.Scene.MaterialFiles.resize(mat_file_count); - - Array textures = {}; - textures.init(arena, 5, 5); - for (auto& mat_file : deserialized_data.Scene.MaterialFiles) - { - Tetragrama::Helpers::DeserializeStringData(arena, in, textures[0]); - Tetragrama::Helpers::DeserializeStringData(arena, in, textures[1]); - Tetragrama::Helpers::DeserializeStringData(arena, in, textures[2]); - Tetragrama::Helpers::DeserializeStringData(arena, in, textures[3]); - Tetragrama::Helpers::DeserializeStringData(arena, in, textures[4]); - - ZEngine::Helpers::secure_strcpy(mat_file.AlbedoTexture, MAX_FILE_PATH_COUNT, textures[0].c_str()); - ZEngine::Helpers::secure_strcpy(mat_file.EmissiveTexture, MAX_FILE_PATH_COUNT, textures[1].c_str()); - ZEngine::Helpers::secure_strcpy(mat_file.NormalTexture, MAX_FILE_PATH_COUNT, textures[2].c_str()); - ZEngine::Helpers::secure_strcpy(mat_file.OpacityTexture, MAX_FILE_PATH_COUNT, textures[3].c_str()); - ZEngine::Helpers::secure_strcpy(mat_file.SpecularTexture, MAX_FILE_PATH_COUNT, textures[4].c_str()); - } - } in.close(); + out.close(); } - if (!model_path.empty()) + ZReleaseScratch(scratch); + + /* + * Update texture path + */ + for (auto& tex : textures) { - std::ifstream in(model_path.data(), std::ios::binary); - if (in.is_open()) - { - in.seekg(0, std::ios::beg); - - size_t local_transform_count; - in.read(reinterpret_cast(&local_transform_count), sizeof(size_t)); - deserialized_data.Scene.LocalTransforms.resize(local_transform_count); - in.read(reinterpret_cast(deserialized_data.Scene.LocalTransforms.data()), sizeof(glm::mat4) * local_transform_count); - - size_t gobal_transform_count; - in.read(reinterpret_cast(&gobal_transform_count), sizeof(size_t)); - deserialized_data.Scene.GlobalTransforms.resize(gobal_transform_count); - in.read(reinterpret_cast(deserialized_data.Scene.GlobalTransforms.data()), sizeof(glm::mat4) * gobal_transform_count); - - size_t node_hierarchy_count; - in.read(reinterpret_cast(&node_hierarchy_count), sizeof(size_t)); - deserialized_data.Scene.NodeHierarchies.resize(node_hierarchy_count); - in.read(reinterpret_cast(deserialized_data.Scene.NodeHierarchies.data()), sizeof(ZEngine::Rendering::Scenes::SceneNodeHierarchy) * node_hierarchy_count); - - // Todo Kernel : This should be part of Move Engine to Arena - // - // DeserializeStringArrayData(arena, in, deserialized_data.Scene.Names); - // DeserializeStringArrayData(arena, in, deserialized_data.Scene.MaterialNames); - DeserializeMapData(arena, in, deserialized_data.Scene.NodeNames); - DeserializeMapData(arena, in, deserialized_data.Scene.NodeMeshes); - DeserializeMapData(arena, in, deserialized_data.Scene.NodeMaterials); - } - in.close(); + auto new_path = fmt::format("{0}{1}{2}{3}{4}", config.OutputTextureFilesPath.c_str(), PLATFORM_OS_BACKSLASH, config.AssetName.c_str(), PLATFORM_OS_BACKSLASH, tex.Path.c_str()); + tex.Path.clear(); + tex.Path.append(new_path.c_str()); } - - return deserialized_data; } glm::mat4 AssimpImporter::ConvertToMat4(const aiMatrix4x4& m) diff --git a/Tetragrama/Importers/AssimpImporter.h b/Tetragrama/Importers/AssimpImporter.h index b9159f77..bb538677 100644 --- a/Tetragrama/Importers/AssimpImporter.h +++ b/Tetragrama/Importers/AssimpImporter.h @@ -1,8 +1,10 @@ #pragma once +#include #include #include #include #include +#include namespace Tetragrama::Importers { @@ -24,9 +26,8 @@ namespace Tetragrama::Importers AssimpImporter(); virtual ~AssimpImporter(); - virtual std::future ImportAsync(std::string_view filename, ImportConfiguration config = {}) override; - virtual void SerializeImporterData(ZEngine::Core::Memory::ArenaAllocator* arena, ImporterData& data, const ImportConfiguration&) override; - virtual ImporterData DeserializeImporterData(ZEngine::Core::Memory::ArenaAllocator* arena, std::string_view model_path, std::string_view mesh_path, std::string_view material_path) override; + virtual std::future ImportAsync(const char* filename, ImportConfiguration& config) override; + void CopyTextureFiles(ZEngine::Core::Memory::ArenaAllocator*, ZEngine::Core::Containers::Array&, const ImportConfiguration&); private: uint32_t m_flags; @@ -34,12 +35,12 @@ namespace Tetragrama::Importers friend struct AssimpProgressHandler; - void ExtractMeshes(const aiScene*, ImporterData&); - void ExtractMaterials(const aiScene*, ImporterData&); - void ExtractTextures(const aiScene*, ImporterData&); - void CreateHierachyScene(const aiScene*, ImporterData&); + void ExtractMeshes(ZEngine::Core::Memory::ArenaAllocator*, const aiScene*, uuids::uuid_random_generator&, AssetMesh&); + void ExtractMaterials(ZEngine::Core::Memory::ArenaAllocator*, const aiScene*, uuids::uuid_random_generator&, ZEngine::Core::Containers::Array&, AssetNodeHierarchy&); + void ExtractTextures(ZEngine::Core::Memory::ArenaAllocator* arena, const aiScene*, uuids::uuid_random_generator&, ZEngine::Core::Containers::Array&, ZEngine::Core::Containers::Array&); + void CreateHierachy(ZEngine::Core::Memory::ArenaAllocator* arena, const aiScene*, uuids::uuid_random_generator&, AssetNodeHierarchy&, AssetMesh&, ZEngine::Core::Containers::Array&); - void TraverseNode(const aiScene*, ZEngine::Rendering::Scenes::SceneRawData* const, const aiNode*, int parent_node_id, int depth_level); + void TraverseNode(ZEngine::Core::Memory::ArenaAllocator* arena, const aiScene*, const aiNode*, AssetNodeHierarchy&, AssetMesh&, ZEngine::Core::Containers::Array&, int parent_node_id, int depth_level); glm::mat4 ConvertToMat4(const aiMatrix4x4& m); }; } // namespace Tetragrama::Importers \ No newline at end of file diff --git a/Tetragrama/Importers/IAssetImporter.cpp b/Tetragrama/Importers/IAssetImporter.cpp new file mode 100644 index 00000000..0dc23524 --- /dev/null +++ b/Tetragrama/Importers/IAssetImporter.cpp @@ -0,0 +1,396 @@ +#include +#include +#include +#include + +using namespace uuids; +using namespace ZEngine::Helpers; +using namespace Tetragrama::Helpers; +using namespace ZEngine::Core::Containers; + +namespace Tetragrama::Importers +{ + int AddNode(AssetNodeHierarchy& hierarchy, int parent, int depth) + { + if (depth < 0) + { + return -1; + } + + int node_id = (int) hierarchy.Hierarchies.size(); + + hierarchy.Hierarchies.push({.Parent = parent}); + hierarchy.LocalTransforms.push(glm::mat4(1.0f)); + hierarchy.GlobalTransforms.push(glm::mat4(1.0f)); + + if (parent > -1) + { + int first_child = hierarchy.Hierarchies[parent].FirstChild; + + if (first_child == -1) + { + hierarchy.Hierarchies[parent].FirstChild = node_id; + } + else + { + int right_sibling = hierarchy.Hierarchies[first_child].RightSibling; + if (right_sibling > -1) + { + // iterate nextSibling_ indices + for (right_sibling = first_child; hierarchy.Hierarchies[right_sibling].RightSibling != -1; right_sibling = hierarchy.Hierarchies[right_sibling].RightSibling) + { + } + hierarchy.Hierarchies[right_sibling].RightSibling = node_id; + } + else + { + hierarchy.Hierarchies[first_child].RightSibling = node_id; + } + } + } + hierarchy.Hierarchies[node_id].DepthLevel = depth; + hierarchy.Hierarchies[node_id].RightSibling = -1; + hierarchy.Hierarchies[node_id].FirstChild = -1; + + return node_id; + } + + void IAssetImporter::Initialize(ZEngine::Core::Memory::ArenaAllocator* arena) + { + arena->CreateSubArena(ZMega(150), &Arena); + } + + void IAssetImporter::SetOnCompleteCallback(on_import_complete_fn callback) + { + m_complete_callback = callback; + } + + void IAssetImporter::SetOnProgressCallback(on_import_progress_fn callback) + { + m_progress_callback = callback; + } + + void IAssetImporter::SetOnErrorCallback(on_import_error_fn callback) + { + m_error_callback = callback; + } + + void IAssetImporter::SetOnLogCallback(on_import_log_fn callback) + { + m_log_callback = callback; + } + + bool IAssetImporter::IsImporting() + { + return m_is_importing.load(std::memory_order_acquire); + } + + AssetImporterOutput IAssetImporter::SerializeMeshAssetFile(ZEngine::Core::Memory::ArenaAllocator* arena, AssetMesh& mesh, AssetNodeHierarchy& hierarchies, const ImportConfiguration& config) + { + AssetImporterOutput output = {}; + + if (config.OutputAssetFile.empty()) + { + return output; + } + + std::string fullname_path = fmt::format("{0}{1}{2}{3}{4}", config.OutputWorkingSpacePath.c_str(), PLATFORM_OS_BACKSLASH, config.OutputAssetsPath.c_str(), PLATFORM_OS_BACKSLASH, config.OutputAssetFile.c_str()); + std::ofstream out(fullname_path, std::ios::binary | std::ios::trunc); + + if (!out.is_open()) + { + out.close(); + return output; + } + + out.seekp(std::ios::beg); + /* + * Magic Headers + */ + WriteBinary(out, ZEMESH_MAGIC); + WriteBinary(out, ASSET_FILE_VERSION); + /* + * Asset Mesh + */ + WriteBinary(out, mesh.MeshUUID); + WriteBinaryArray(out, ArrayView{mesh.Vertices}); + WriteBinaryArray(out, ArrayView{mesh.Indices}); + WriteBinaryArray(out, ArrayView{mesh.SubMeshes}); + + /* + * Asset AssetNodeHierarchy + */ + WriteBinary(out, hierarchies.NodeHierarchyUUID); + WriteBinary(out, hierarchies.MeshUUID); + WriteBinaryArray(out, ArrayView{hierarchies.Hierarchies}); + WriteBinaryArray(out, ArrayView{hierarchies.LocalTransforms}); + WriteBinaryArray(out, ArrayView{hierarchies.GlobalTransforms}); + + uint32_t name_count = static_cast(hierarchies.Names.size()); + WriteBinary(out, name_count); + for (const auto& name : hierarchies.Names) + { + WriteBinaryString(out, name); + } + + uint32_t material_name_count = static_cast(hierarchies.MaterialNames.size()); + WriteBinary(out, material_name_count); + for (const auto& name : hierarchies.MaterialNames) + { + WriteBinaryString(out, name); + } + + WriteBinaryHashMap(out, hierarchies.NodeNames); + WriteBinaryHashMap(out, hierarchies.NodeMeshes); + WriteBinaryHashMap(out, hierarchies.NodeMaterials); + + out.close(); + + output = {.Type = AssetFileType::MESH, .Path = fmt::format("{0}{1}{2}", config.OutputAssetsPath.c_str(), PLATFORM_OS_BACKSLASH, config.OutputAssetFile.c_str()), .RootPath = config.OutputWorkingSpacePath.c_str()}; + return output; + } + + AssetImporterOutput IAssetImporter::SerializeMaterialAssetFile(ZEngine::Core::Memory::ArenaAllocator* arena, AssetMaterial& material, const ImportConfiguration& config) + { + + AssetImporterOutput output = {}; + + std::string asset_mat_filename = fmt::format("{0}{1}", material.Name.c_str(), ".zematerial"); + std::string fullname_path = fmt::format("{0}{1}{2}{3}{4}", config.OutputWorkingSpacePath.c_str(), PLATFORM_OS_BACKSLASH, config.OutputAssetsPath.c_str(), PLATFORM_OS_BACKSLASH, asset_mat_filename.c_str()); + std::ofstream out(fullname_path, std::ios::binary | std::ios::trunc); + + if (!out.is_open()) + { + out.close(); + return output; + } + + out.seekp(std::ios::beg); + /* + * Magic Headers + */ + WriteBinary(out, ZEMATERIAL_MAGIC); + WriteBinary(out, ASSET_FILE_VERSION); + + /* + * Asset Materials + */ + WriteBinaryString(out, material.Name); + WriteBinary(out, material.MaterialUUID); + WriteBinary(out, material.AlbedoTexUUID); + WriteBinary(out, material.EmissiveTexUUID); + WriteBinary(out, material.NormalTexUUID); + WriteBinary(out, material.OpacityTexUUID); + WriteBinary(out, material.SpecularTexUUID); + out.write(reinterpret_cast(material.AmbientColor), sizeof(material.AmbientColor)); + out.write(reinterpret_cast(material.AlbedoColor), sizeof(material.AlbedoColor)); + out.write(reinterpret_cast(material.EmissiveColor), sizeof(material.EmissiveColor)); + out.write(reinterpret_cast(material.RoughnessColor), sizeof(material.RoughnessColor)); + out.write(reinterpret_cast(material.SpecularColor), sizeof(material.SpecularColor)); + out.write(reinterpret_cast(material.Factors), sizeof(material.Factors)); + + out.close(); + + output = {.Type = AssetFileType::MATERIAL, .Path = fmt::format("{0}{1}{2}", config.OutputAssetsPath.c_str(), PLATFORM_OS_BACKSLASH, asset_mat_filename.c_str()), .RootPath = config.OutputWorkingSpacePath.c_str()}; + return output; + } + + AssetImporterOutput IAssetImporter::SerializeTextureAssetFiles(ZEngine::Core::Memory::ArenaAllocator* arena, ArrayView textures, const ImportConfiguration& config) + { + AssetImporterOutput output = {}; + + std::string asset_tex_filename = fmt::format("{0}{1}", config.AssetName.c_str(), ".zetextures"); + std::string fullname_path = fmt::format("{0}{1}{2}{3}{4}", config.OutputWorkingSpacePath.c_str(), PLATFORM_OS_BACKSLASH, config.OutputAssetsPath.c_str(), PLATFORM_OS_BACKSLASH, asset_tex_filename.c_str()); + std::ofstream out(fullname_path, std::ios::binary | std::ios::trunc); + + if (!out.is_open()) + { + out.close(); + return output; + } + + out.seekp(std::ios::beg); + /* + * Magic Headers + */ + WriteBinary(out, ZETEXTURES_MAGIC); + WriteBinary(out, ASSET_FILE_VERSION); + /* + * Asset Textures + */ + uint32_t texture_count = (uint32_t) textures.size(); + WriteBinary(out, texture_count); + for (unsigned i = 0; i < texture_count; ++i) + { + auto& tex = textures[i]; + WriteBinary(out, tex.TextureUUID); + WriteBinaryString(out, tex.Path); + } + + out.close(); + + output = {.Type = AssetFileType::TEXTURES, .Path = fmt::format("{0}{1}{2}", config.OutputAssetsPath.c_str(), PLATFORM_OS_BACKSLASH, asset_tex_filename.c_str()), .RootPath = config.OutputWorkingSpacePath.c_str()}; + return output; + } + + void IAssetImporter::DeserializeMeshAssetFile(ZEngine::Core::Memory::ArenaAllocator* arena, const char* asset_file, AssetMesh& mesh, AssetNodeHierarchy& hierarchies) + { + if (!ZEngine::Helpers::secure_strlen(asset_file)) + { + return; + } + + std::ifstream in(asset_file, std::ios::binary); + + if (!in.is_open()) + { + in.close(); + return; + } + + in.seekg(std::ios::beg); + + uint32_t scene_magic; + uint32_t scene_version; + ReadBinary(in, scene_magic); + ReadBinary(in, scene_version); + + if (scene_magic != ZEMESH_MAGIC && scene_version != ASSET_FILE_VERSION) + { + in.close(); + return; + } + + /* + * Asset Mesh + */ + ReadBinary(in, mesh.MeshUUID); + ReadBinaryArray(arena, in, mesh.Vertices); + ReadBinaryArray(arena, in, mesh.Indices); + ReadBinaryArray(arena, in, mesh.SubMeshes); + + /* + * Asset AssetNodeHierarchy + */ + ReadBinary(in, hierarchies.NodeHierarchyUUID); + ReadBinary(in, hierarchies.MeshUUID); + ReadBinaryArray(arena, in, hierarchies.Hierarchies); + ReadBinaryArray(arena, in, hierarchies.LocalTransforms); + ReadBinaryArray(arena, in, hierarchies.GlobalTransforms); + + uint32_t name_count = 0; + ReadBinary(in, name_count); + hierarchies.Names.init(arena, name_count, name_count); + for (int i = 0; i < name_count; ++i) + { + auto& name = hierarchies.Names[i]; + ReadBinaryString(arena, in, name); + } + + uint32_t material_name_count = 0; + ReadBinary(in, material_name_count); + hierarchies.MaterialNames.init(arena, material_name_count, material_name_count); + for (int i = 0; i < material_name_count; ++i) + { + auto& name = hierarchies.MaterialNames[i]; + ReadBinaryString(arena, in, name); + } + + ReadHashMap(arena, in, hierarchies.NodeNames); + ReadHashMap(arena, in, hierarchies.NodeMeshes); + ReadHashMap(arena, in, hierarchies.NodeMaterials); + + in.close(); + } + + void IAssetImporter::DeserializeMaterialAssetFile(ZEngine::Core::Memory::ArenaAllocator* arena, const char* asset_file, AssetMaterial& material) + { + if (!ZEngine::Helpers::secure_strlen(asset_file)) + { + return; + } + + std::ifstream in(asset_file, std::ios::binary); + + if (!in.is_open()) + { + in.close(); + return; + } + + in.seekg(std::ios::beg); + + uint32_t scene_magic; + uint32_t scene_version; + ReadBinary(in, scene_magic); + ReadBinary(in, scene_version); + + if (scene_magic != ZEMATERIAL_MAGIC && scene_version != ASSET_FILE_VERSION) + { + in.close(); + return; + } + + /* + * Asset Materials + */ + ReadBinary(in, material.MaterialUUID); + ReadBinary(in, material.AlbedoTexUUID); + ReadBinary(in, material.EmissiveTexUUID); + ReadBinary(in, material.NormalTexUUID); + ReadBinary(in, material.OpacityTexUUID); + ReadBinary(in, material.SpecularTexUUID); + in.read(reinterpret_cast(material.AmbientColor), sizeof(material.AmbientColor)); + in.read(reinterpret_cast(material.AlbedoColor), sizeof(material.AlbedoColor)); + in.read(reinterpret_cast(material.EmissiveColor), sizeof(material.EmissiveColor)); + in.read(reinterpret_cast(material.RoughnessColor), sizeof(material.RoughnessColor)); + in.read(reinterpret_cast(material.SpecularColor), sizeof(material.SpecularColor)); + in.read(reinterpret_cast(material.Factors), sizeof(material.Factors)); + + in.close(); + } + + void IAssetImporter::DeserializeTextureAssetFile(ZEngine::Core::Memory::ArenaAllocator* arena, const char* asset_file, ZEngine::Core::Containers::Array& textures) + { + if (!ZEngine::Helpers::secure_strlen(asset_file)) + { + return; + } + + std::ifstream in(asset_file, std::ios::binary); + + if (!in.is_open()) + { + in.close(); + return; + } + + in.seekg(std::ios::beg); + + uint32_t scene_magic; + uint32_t scene_version; + ReadBinary(in, scene_magic); + ReadBinary(in, scene_version); + + if (scene_magic != ZETEXTURES_MAGIC && scene_version != ASSET_FILE_VERSION) + { + in.close(); + return; + } + + /* + * Asset Textures + */ + uint32_t texture_count = 0; + ReadBinary(in, texture_count); + textures.init(arena, texture_count, texture_count); + for (int i = 0; i < texture_count; ++i) + { + auto& tex = textures[i]; + ReadBinary(in, tex.TextureUUID); + ReadBinaryString(arena, in, tex.Path); + } + + in.close(); + } +} // namespace Tetragrama::Importers \ No newline at end of file diff --git a/Tetragrama/Importers/IAssetImporter.h b/Tetragrama/Importers/IAssetImporter.h index 19962424..f3f74812 100644 --- a/Tetragrama/Importers/IAssetImporter.h +++ b/Tetragrama/Importers/IAssetImporter.h @@ -1,4 +1,5 @@ #pragma once +#include #include #include #include @@ -19,82 +20,61 @@ namespace Tetragrama::Importers { - struct ImporterData - { - /* Meshes Properties*/ - uint32_t VertexOffset = 0; - uint32_t IndexOffset = 0; - ZEngine::Rendering::Scenes::SceneRawData Scene = {}; - ZEngine::Core::Containers::String Name = {}; - ZEngine::Core::Containers::String SerializedMeshesPath = {}; - ZEngine::Core::Containers::String SerializedMaterialsPath = {}; - ZEngine::Core::Containers::String SerializedModelPath = {}; - }; + int AddNode(AssetNodeHierarchy& hierarchy, int parent, int depth); struct ImportConfiguration { - ZEngine::Core::Containers::String AssetFilename; + ZEngine::Core::Containers::String AssetName; + ZEngine::Core::Containers::String OutputAssetFile; + ZEngine::Core::Containers::String OutputAssetsPath; ZEngine::Core::Containers::String InputBaseAssetFilePath; ZEngine::Core::Containers::String OutputWorkingSpacePath; - ZEngine::Core::Containers::String OutputModelFilePath; - ZEngine::Core::Containers::String OutputMeshFilePath; ZEngine::Core::Containers::String OutputTextureFilesPath; - ZEngine::Core::Containers::String OutputMaterialsPath; + }; + + struct AssetImporterOutput + { + AssetFileType Type = AssetFileType::UNKNOWN; + std::string Path = ""; + std::string RootPath = ""; }; struct IAssetImporter { - typedef void (*on_import_complete_fn)(void* const, ImporterData&& result); + typedef void (*on_import_complete_fn)(void* const, ZEngine::Core::Containers::ArrayView result); typedef void (*on_import_progress_fn)(void* const, float progress); typedef void (*on_import_error_fn)(void* const, std::string_view error_message); typedef void (*on_import_log_fn)(void* const, std::string_view log_message); - on_import_complete_fn m_complete_callback{nullptr}; - on_import_progress_fn m_progress_callback{nullptr}; - on_import_error_fn m_error_callback{nullptr}; - on_import_log_fn m_log_callback{nullptr}; - - std::mutex m_mutex; - std::atomic_bool m_is_importing{false}; + on_import_complete_fn m_complete_callback = nullptr; + on_import_progress_fn m_progress_callback = nullptr; + on_import_error_fn m_error_callback = nullptr; + on_import_log_fn m_log_callback = nullptr; - virtual ~IAssetImporter() = default; + std::mutex m_mutex; + std::atomic_bool m_is_importing = false; - ZEngine::Core::Memory::ArenaAllocator Arena = {}; - void* Context = nullptr; + ZEngine::Core::Memory::ArenaAllocator Arena = {}; + void* Context = nullptr; - void Initialize(ZEngine::Core::Memory::ArenaAllocator* arena) - { - arena->CreateSubArena(ZMega(1), &Arena); - } + virtual ~IAssetImporter() {} - virtual void SetOnCompleteCallback(on_import_complete_fn callback) - { - m_complete_callback = callback; - } + void Initialize(ZEngine::Core::Memory::ArenaAllocator* arena); - virtual void SetOnProgressCallback(on_import_progress_fn callback) - { - m_progress_callback = callback; - } + virtual void SetOnCompleteCallback(on_import_complete_fn callback); + virtual void SetOnProgressCallback(on_import_progress_fn callback); + virtual void SetOnErrorCallback(on_import_error_fn callback); + virtual void SetOnLogCallback(on_import_log_fn callback); + virtual bool IsImporting(); - virtual void SetOnErrorCallback(on_import_error_fn callback) - { - m_error_callback = callback; - } + virtual std::future ImportAsync(const char* filename, ImportConfiguration& config) = 0; - virtual void SetOnLogCallback(on_import_log_fn callback) - { - m_log_callback = callback; - } + static AssetImporterOutput SerializeMeshAssetFile(ZEngine::Core::Memory::ArenaAllocator* arena, AssetMesh& data, AssetNodeHierarchy& hierarchies, const ImportConfiguration&); + static AssetImporterOutput SerializeMaterialAssetFile(ZEngine::Core::Memory::ArenaAllocator* arena, AssetMaterial& data, const ImportConfiguration&); + static AssetImporterOutput SerializeTextureAssetFiles(ZEngine::Core::Memory::ArenaAllocator* arena, ZEngine::Core::Containers::ArrayView data, const ImportConfiguration&); - virtual bool IsImporting() - { - return m_is_importing.load(std::memory_order_acquire); - } - - virtual std::future ImportAsync(std::string_view filename, ImportConfiguration config = {}) = 0; - virtual void SerializeImporterData(ZEngine::Core::Memory::ArenaAllocator* arena, ImporterData& data, const ImportConfiguration&) = 0; - virtual ImporterData DeserializeImporterData(ZEngine::Core::Memory::ArenaAllocator* arena, std::string_view model_path, std::string_view mesh_path, std::string_view material_path) = 0; + static void DeserializeMeshAssetFile(ZEngine::Core::Memory::ArenaAllocator* arena, const char* asset_file, AssetMesh&, AssetNodeHierarchy&); + static void DeserializeMaterialAssetFile(ZEngine::Core::Memory::ArenaAllocator* arena, const char* asset_file, AssetMaterial&); + static void DeserializeTextureAssetFile(ZEngine::Core::Memory::ArenaAllocator* arena, const char* asset_file, ZEngine::Core::Containers::Array&); }; - } // namespace Tetragrama::Importers diff --git a/Tetragrama/Layers/ImguiLayer.cpp b/Tetragrama/Layers/ImguiLayer.cpp index 6fe36d42..c806a397 100644 --- a/Tetragrama/Layers/ImguiLayer.cpp +++ b/Tetragrama/Layers/ImguiLayer.cpp @@ -28,7 +28,7 @@ namespace Tetragrama::Layers void ImguiLayer::Initialize(ZEngine::Core::Memory::ArenaAllocator* arena) { - arena->CreateSubArena(ZMega(10), &LayerArena); + arena->CreateSubArena(ZMega(200), &LayerArena); NodeHierarchies.init(&LayerArena, 10, 0); NodeUIComponents.init(&LayerArena, 10, 0); @@ -191,9 +191,10 @@ namespace Tetragrama::Layers NodeToRender.push(s); } - for (auto i : NodeToRender) + for (auto node : NodeToRender) { - auto& cmp = NodeUIComponents[i]; + + auto& cmp = NodeUIComponents[node]; cmp->Update(dt); } @@ -208,9 +209,9 @@ namespace Tetragrama::Layers } auto node = NodeHierarchies.size(); - NodeHierarchies.push(NodeHierarchy{.Parent = parent}); + NodeHierarchies.push(Helpers::NodeHierarchy{.Parent = parent}); - ArrayView nodes_view(NodeHierarchies.data(), NodeHierarchies.size()); + ArrayView nodes_view(NodeHierarchies.data(), NodeHierarchies.size()); if (parent > -1) { int first = NodeHierarchies[parent].FirstChild; diff --git a/Tetragrama/Layers/ImguiLayer.h b/Tetragrama/Layers/ImguiLayer.h index fb38542e..aa8d20c1 100644 --- a/Tetragrama/Layers/ImguiLayer.h +++ b/Tetragrama/Layers/ImguiLayer.h @@ -1,4 +1,5 @@ #pragma once +#include #include #include #include @@ -14,14 +15,6 @@ namespace Tetragrama::Components namespace Tetragrama::Layers { - struct NodeHierarchy - { - int Parent = -1; - int FirstChild = -1; - int RightSibling = -1; - int DepthLevel = -1; - }; - class ImguiLayer : public ZEngine::Windows::Layers::Layer, public ZEngine::Windows::Inputs::IKeyboardEventCallback, public ZEngine::Windows::Inputs::IMouseEventCallback, public ZEngine::Windows::Inputs::ITextInputEventCallback, public ZEngine::Windows::Inputs::IWindowEventCallback { @@ -29,7 +22,7 @@ namespace Tetragrama::Layers ImguiLayer(const char* name = "ImGUI Layer") : Layer(name) {} virtual ~ImguiLayer(); - ZEngine::Core::Containers::Array NodeHierarchies = {}; + ZEngine::Core::Containers::Array NodeHierarchies = {}; ZEngine::Core::Containers::Array NodeToRender = {}; ZEngine::Core::Containers::HashMap NodeUIComponents = {}; ZEngine::Core::Containers::HashMap KeyEntries = {}; diff --git a/Tetragrama/Managers/AssetManager.cpp b/Tetragrama/Managers/AssetManager.cpp new file mode 100644 index 00000000..56fa2ddb --- /dev/null +++ b/Tetragrama/Managers/AssetManager.cpp @@ -0,0 +1,258 @@ +#include +#include +#include +#include +#include + +using namespace ZEngine::Core::Containers; +using namespace Tetragrama::Importers; + +namespace Tetragrama::Managers +{ + static AssetManager* s_Instance = nullptr; + + AssetManager* AssetManager::Instance() + { + return s_Instance; + } + + AssetManager::AssetHandle AssetManager::CreateHandle(uint32_t id, AssetType at) + { + return ((uint32_t(at) & 0xF) << 28) | (id & 0x0FFFFFFF); + } + + uint32_t AssetManager::ReadAssetHandleIndex(AssetHandle h) + { + return (h & 0x0FFFFFFF); + } + + AssetType AssetManager::ReadAssetHandleType(AssetHandle h) + { + return AssetType((h >> 28) & 0xF); + } + + void AssetManager::Initialize(ZEngine::Core::Memory::ArenaAllocator* arena) + { + s_Instance = ZPushStructCtor(arena, AssetManager); + arena->CreateSubArena(ZMega(20), &(s_Instance->ThreadLocalArena)); + arena->CreateSubArena(ZMega(20), &(s_Instance->Arena)); + + s_Instance->NodeHierarchies.init(&(s_Instance->Arena), 1000); + s_Instance->Meshes.init(&(s_Instance->Arena), 1000); + s_Instance->Materials.init(&(s_Instance->Arena), 1000); + s_Instance->Textures.init(&(s_Instance->Arena), 1000); + s_Instance->UUIDToHandle.init(&(s_Instance->Arena), 1000); + s_Instance->HandleToUUID.init(&(s_Instance->Arena), 1000); + } + + void AssetManager::Run() + { + ZEngine::Helpers::ThreadPoolHelper::Submit([instance = s_Instance]() { instance->__Run(); }); + } + + void AssetManager::Shutdown() + { + { + if (!s_Instance) + { + return; + } + + std::lock_guard l(s_Instance->Mut); + s_Instance->RequestShutdown.store(true, std::memory_order_release); + s_Instance->Cond.notify_all(); + } + } + + bool AssetManager::IsLoadingAsset() + { + return s_Instance->IsLoading.load(std::memory_order_acquire); + } + + AssetManager::AssetHandle AssetManager::RegisterAsset(AssetType type, const uuids::uuid& uid, uint32_t asset_id) + { + auto handle = CreateHandle(asset_id, type); + if (!s_Instance->UUIDToHandle.contains(uid)) + { + s_Instance->UUIDToHandle.insert(uid, handle); + } + + if (!s_Instance->HandleToUUID.contains(handle)) + { + s_Instance->HandleToUUID.insert(handle, uid); + } + return handle; + } + + void AssetManager::LoadAssetFile(const Importers::AssetImporterOutput& file) + { + if (file.Path.empty()) + { + return; + } + + s_Instance->PendingAssetFiles.Enqueue(file); + s_Instance->Cond.notify_one(); + } + + void AssetManager::__Run() + { + while (true) + { + if (!s_Instance) + { + break; + } + + auto& mut = s_Instance->Mut; + auto& cond = s_Instance->Cond; + auto& pendings_asset_files = s_Instance->PendingAssetFiles; + auto& pendings_asset_meshes = s_Instance->PendingAssetMeshes; + auto& pendings_asset_node_hierarchies = s_Instance->PendingAssetNodeHierarchies; + auto& pendings_asset_materials = s_Instance->PendingAssetMaterials; + auto& pendings_asset_textures = s_Instance->PendingAssetTextures; + + std::unique_lock l(mut); + cond.wait(l, [&] { return (true == s_Instance->RequestShutdown.load(std::memory_order_acquire)) || !pendings_asset_files.Empty() || !pendings_asset_meshes.Empty() || !pendings_asset_node_hierarchies.Empty() || !pendings_asset_materials.Empty() || !pendings_asset_textures.Empty(); }); + + if (auto shutdown = s_Instance->RequestShutdown.load(std::memory_order_acquire)) + { + break; + } + + AssetMesh mesh = {}; + if (pendings_asset_meshes.Pop(mesh)) + { + auto asset_id = (uint32_t) Meshes.size(); + auto& m = Meshes.push_use({}); + m.MeshUUID = mesh.MeshUUID; + m.SubMeshes.init(&(s_Instance->Arena), mesh.SubMeshes.size()); + m.Vertices.init(&(s_Instance->Arena), mesh.Vertices.size(), mesh.Vertices.size()); + m.Indices.init(&(s_Instance->Arena), mesh.Indices.size(), mesh.Indices.size()); + + ZEngine::Helpers::secure_memcpy(m.Vertices.data(), m.Vertices.size() * sizeof(float), mesh.Vertices.data(), mesh.Vertices.size() * sizeof(float)); + ZEngine::Helpers::secure_memcpy(m.Indices.data(), m.Indices.size() * sizeof(uint32_t), mesh.Indices.data(), mesh.Indices.size() * sizeof(uint32_t)); + + for (auto& submesh : mesh.SubMeshes) + { + m.SubMeshes.push(submesh); + } + + RegisterAsset(AssetType::MESH, m.MeshUUID, asset_id); + + continue; + } + + AssetNodeHierarchy hierarchies = {}; + if (pendings_asset_node_hierarchies.Pop(hierarchies)) + { + auto asset_id = NodeHierarchies.size(); + auto& h = NodeHierarchies.push_use({}); + h.MeshUUID = hierarchies.MeshUUID; + + h.Hierarchies.init(&(s_Instance->Arena), hierarchies.Hierarchies.size()); + h.LocalTransforms.init(&(s_Instance->Arena), hierarchies.LocalTransforms.size()); + h.GlobalTransforms.init(&(s_Instance->Arena), hierarchies.GlobalTransforms.size()); + h.Names.init(&(s_Instance->Arena), hierarchies.Names.size()); + h.MaterialNames.init(&(s_Instance->Arena), hierarchies.MaterialNames.size()); + h.NodeNames.init(&(s_Instance->Arena), hierarchies.NodeNames.size()); + h.NodeMeshes.init(&(s_Instance->Arena), hierarchies.NodeMeshes.size()); + h.NodeMaterials.init(&(s_Instance->Arena), hierarchies.NodeMaterials.size()); + + ZEngine::Helpers::secure_memcpy(h.Hierarchies.data(), h.Hierarchies.size() * sizeof(AssetNodeHierarchy), hierarchies.Hierarchies.data(), hierarchies.Hierarchies.size() * sizeof(AssetNodeHierarchy)); + ZEngine::Helpers::secure_memcpy(h.LocalTransforms.data(), h.LocalTransforms.size() * sizeof(glm::mat4), hierarchies.LocalTransforms.data(), hierarchies.LocalTransforms.size() * sizeof(glm::mat4)); + ZEngine::Helpers::secure_memcpy(h.GlobalTransforms.data(), h.GlobalTransforms.size() * sizeof(glm::mat4), hierarchies.GlobalTransforms.data(), hierarchies.GlobalTransforms.size() * sizeof(glm::mat4)); + + for (auto& name : hierarchies.Names) + { + auto& n = h.Names.push_use({}); + n.init(&(s_Instance->Arena), name.c_str()); + } + + for (auto& mat_name : hierarchies.MaterialNames) + { + auto& n = h.MaterialNames.push_use({}); + n.init(&(s_Instance->Arena), mat_name.c_str()); + } + + auto node_names_view = hierarchies.NodeNames.view(); + for (auto [k, v] : node_names_view) + { + h.NodeNames.insert(k, v); + } + + auto node_meshes_view = hierarchies.NodeMeshes.view(); + for (auto [k, v] : node_meshes_view) + { + h.NodeMeshes.insert(k, v); + } + + auto node_mat_view = hierarchies.NodeMaterials.view(); + for (auto [k, v] : node_mat_view) + { + h.NodeMaterials.insert(k, v); + } + + RegisterAsset(AssetType::MESH_HIERARCHY, h.NodeHierarchyUUID, asset_id); + continue; + } + + AssetMaterial material = {}; + if (pendings_asset_materials.Pop(material)) + { + auto asset_id = (uint32_t) Materials.size(); + Materials.push(material); + + RegisterAsset(AssetType::MATERIAL, material.MaterialUUID, asset_id); + continue; + } + + Array textures = {}; + if (pendings_asset_textures.Pop(textures)) + { + for (auto& tex : textures) + { + auto asset_id = (uint32_t) Textures.size(); + auto& new_tex = Textures.push_use({}); + new_tex.TextureUUID = tex.TextureUUID; + new_tex.Path.init(&(s_Instance->Arena), tex.Path.c_str()); + + RegisterAsset(AssetType::TEXTURE, new_tex.TextureUUID, asset_id); + } + + continue; + } + + ThreadLocalArena.Clear(); + Importers::AssetImporterOutput file = {}; + if (pendings_asset_files.Pop(file)) + { + if (file.Type == Importers::AssetFileType::MESH) + { + AssetMesh mesh = {}; + AssetNodeHierarchy hierarchies = {}; + auto path = fmt::format("{0}{1}{2}", file.RootPath, PLATFORM_OS_BACKSLASH, file.Path); + IAssetImporter::DeserializeMeshAssetFile(&ThreadLocalArena, path.c_str(), mesh, hierarchies); + PendingAssetMeshes.Enqueue(mesh); + PendingAssetNodeHierarchies.Enqueue(hierarchies); + } + + else if (file.Type == Importers::AssetFileType::MATERIAL) + { + AssetMaterial material = {}; + auto path = fmt::format("{0}{1}{2}", file.RootPath, PLATFORM_OS_BACKSLASH, file.Path); + IAssetImporter::DeserializeMaterialAssetFile(&ThreadLocalArena, path.c_str(), material); + PendingAssetMaterials.Enqueue(material); + } + + else if (file.Type == Importers::AssetFileType::TEXTURES) + { + Array textures = {}; + auto path = fmt::format("{0}{1}{2}", file.RootPath, PLATFORM_OS_BACKSLASH, file.Path); + IAssetImporter::DeserializeTextureAssetFile(&ThreadLocalArena, path.c_str(), textures); + PendingAssetTextures.Enqueue(textures); + } + } + } + } +} // namespace Tetragrama::Managers diff --git a/Tetragrama/Managers/AssetManager.h b/Tetragrama/Managers/AssetManager.h new file mode 100644 index 00000000..b323b531 --- /dev/null +++ b/Tetragrama/Managers/AssetManager.h @@ -0,0 +1,139 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Tetragrama::Managers +{ + enum class AssetType : uint8_t + { + MESH = 0, + MATERIAL, + TEXTURE, + MESH_HIERARCHY + }; + + struct AssetManager + { + using AssetHandle = uint32_t; + + ZEngine::Core::Memory::ArenaAllocator Arena = {}; + ZEngine::Core::Memory::ArenaAllocator ThreadLocalArena = {}; + + std::atomic_bool IsLoading = false; + std::atomic_bool RequestShutdown = false; + + ZEngine::Core::Containers::Array NodeHierarchies = {}; + ZEngine::Core::Containers::Array Meshes = {}; + ZEngine::Core::Containers::Array Materials = {}; + ZEngine::Core::Containers::Array Textures = {}; + + ZEngine::Core::Containers::HashMap UUIDToHandle = {}; + ZEngine::Core::Containers::HashMap HandleToUUID = {}; + + std::mutex Mut; + std::condition_variable Cond; + ZEngine::Helpers::ThreadSafeQueue PendingAssetFiles = {}; + + ZEngine::Helpers::ThreadSafeQueue PendingAssetMeshes = {}; + ZEngine::Helpers::ThreadSafeQueue PendingAssetNodeHierarchies = {}; + ZEngine::Helpers::ThreadSafeQueue PendingAssetMaterials = {}; + ZEngine::Helpers::ThreadSafeQueue> PendingAssetTextures = {}; + + static AssetManager* Instance(); + + static AssetHandle CreateHandle(uint32_t, AssetType); + static uint32_t ReadAssetHandleIndex(AssetHandle); + static AssetType ReadAssetHandleType(AssetHandle); + + static void Initialize(ZEngine::Core::Memory::ArenaAllocator* arena); + static void Run(); + static void Shutdown(); + + static bool IsLoadingAsset(); + static AssetHandle RegisterAsset(AssetType type, const uuids::uuid& uid, uint32_t asset_id); + + static void LoadAssetFile(const Importers::AssetImporterOutput& file); + + template + static T* GetAsset(K key) + { + return nullptr; + } + + private: + void __Run(); + }; + + template <> + inline Importers::AssetMesh* AssetManager::GetAsset(AssetManager::AssetHandle key) + { + uint32_t index = ReadAssetHandleIndex(key); + if (index < Instance()->Meshes.size()) + { + return &Instance()->Meshes[index]; + } + return nullptr; + } + + template <> + inline Importers::AssetMaterial* AssetManager::GetAsset(AssetManager::AssetHandle key) + { + uint32_t index = ReadAssetHandleIndex(key); + if (index < Instance()->Materials.size()) + { + return &Instance()->Materials[index]; + } + return nullptr; + } + + template <> + inline Importers::AssetTexture* AssetManager::GetAsset(AssetManager::AssetHandle key) + { + uint32_t index = ReadAssetHandleIndex(key); + if (index < Instance()->Textures.size()) + { + return &Instance()->Textures[index]; + } + return nullptr; + } + + template <> + inline Importers::AssetMesh* AssetManager::GetAsset(uuids::uuid id) + { + if (Instance()->UUIDToHandle.contains(id)) + { + auto& handle = Instance()->UUIDToHandle[id]; + return GetAsset(handle); + } + return nullptr; + } + + template <> + inline Importers::AssetMaterial* AssetManager::GetAsset(uuids::uuid id) + { + if (Instance()->UUIDToHandle.contains(id)) + { + auto& handle = Instance()->UUIDToHandle[id]; + return GetAsset(handle); + } + return nullptr; + } + + template <> + inline Importers::AssetTexture* AssetManager::GetAsset(uuids::uuid id) + { + if (Instance()->UUIDToHandle.contains(id)) + { + auto& handle = Instance()->UUIDToHandle[id]; + return GetAsset(handle); + } + return nullptr; + } +} // namespace Tetragrama::Managers \ No newline at end of file diff --git a/Tetragrama/Serializers/EditorSceneSerializer.cpp b/Tetragrama/Serializers/EditorSceneSerializer.cpp index ede53c7a..f2193de2 100644 --- a/Tetragrama/Serializers/EditorSceneSerializer.cpp +++ b/Tetragrama/Serializers/EditorSceneSerializer.cpp @@ -20,7 +20,7 @@ namespace Tetragrama::Serializers return; } - ThreadPoolHelper::Submit([this, scene] { + ThreadPoolHelper::Submit([this, scene = scene] { std::unique_lock l(m_mutex); m_is_serializing.store(true, std::memory_order_release); Arena.Clear(); @@ -45,30 +45,26 @@ namespace Tetragrama::Serializers out.seekp(std::ios::beg); - size_t name_count = ZEngine::Helpers::secure_strlen(scene->Name); - out.write(reinterpret_cast(&name_count), sizeof(size_t)); - out.write(scene->Name, name_count + 1); + WriteBinary(out, ZESCENE_MAGIC); + WriteBinary(out, SCENE_FILE_VERSION); - SerializeStringArrayData(out, ArrayView(scene->MeshFiles)); - REPORT_PROGRESS(Context, 0.25f) - - SerializeStringArrayData(out, ArrayView(scene->ModelFiles)); - REPORT_PROGRESS(Context, 0.5f) - - SerializeStringArrayData(out, ArrayView(scene->MaterialFiles)); - REPORT_PROGRESS(Context, 0.75f) - - Array hashes = {}; - hashes.init(&Arena, scene->Data.size()); - - for (auto& [k, _] : scene->Data) + WriteBinary(out, scene->AssetFiles.size()); + for (auto& file : scene->AssetFiles) { - String s; - s.init(&Arena, k); - hashes.push(s); + WriteBinary(out, file.Type); + WriteBinary(out, file.Hash); + WriteBinaryString(out, file.Path); + WriteBinaryString(out, file.RootPath); } - SerializeStringArrayData(out, ArrayView(hashes)); - REPORT_PROGRESS(Context, 1.f) + + WriteBinaryString(out, scene->Name); + WriteBinaryArray(out, ArrayView{scene->Names}); + WriteBinaryArray(out, ArrayView{scene->Hierarchies}); + WriteBinaryArray(out, ArrayView{scene->LocalTransforms}); + WriteBinaryArray(out, ArrayView{scene->GlobalTransforms}); + + WriteBinaryHashMap(out, scene->NodeMeshes); + WriteBinaryHashMap(out, scene->NodeNames); out.close(); @@ -90,7 +86,6 @@ namespace Tetragrama::Serializers Arena.Clear(); EditorScene scene = {}; - scene.Initialize(&Arena); if (scene_filename.empty()) { @@ -115,84 +110,56 @@ namespace Tetragrama::Serializers return; } - in_stream.seekg(0, std::ios::beg); - - String name = {}; - size_t name_count; - in_stream.read(reinterpret_cast(&name_count), sizeof(size_t)); - name.init(&Arena, name_count + 1); - in_stream.read(name.data(), name_count + 1); + in_stream.seekg(std::ios::beg); - scene.Name = name.data(); + REPORT_LOG(Context, "Reading checksum information...") - DeserializeStringArrayData(&Arena, in_stream, scene.MeshFiles); - REPORT_PROGRESS(Context, 0.25f) + uint32_t scene_magic; + uint32_t scene_version; + ReadBinary(in_stream, scene_magic); + ReadBinary(in_stream, scene_version); - DeserializeStringArrayData(&Arena, in_stream, scene.ModelFiles); - REPORT_PROGRESS(Context, 0.5f) - - DeserializeStringArrayData(&Arena, in_stream, scene.MaterialFiles); - REPORT_PROGRESS(Context, 0.75f) - - Array hashes = {}; - DeserializeStringArrayData(&Arena, in_stream, hashes); - - for (auto& hash : hashes) + if (scene_magic != ZESCENE_MAGIC && scene_version != SCENE_FILE_VERSION) { - uint16_t indices[3] = {0}; - - int i = 0; - - while (i < hash.size() && hash[i] != ':') + in_stream.close(); + if (m_error_callback) { - indices[0] = indices[0] * 10 + (hash[i] - '0'); - i++; + m_error_callback(Context, "Error: Invalid scene file, unknown format"); } - i++; // Skip the colon + m_is_deserializing.store(false, std::memory_order_release); + return; + } - while (i < hash.size() && hash[i] != ':') - { - indices[1] = indices[1] * 10 + (hash[i] - '0'); - i++; - } - i++; + REPORT_LOG(Context, "Extracting scene asset files...") - while (i < hash.size() && hash[i] != '\0') - { - indices[2] = indices[2] * 10 + (hash[i] - '0'); - i++; - } + size_t asset_file_count; + ReadBinary(in_stream, asset_file_count); + scene.AssetFiles.init(&Arena, asset_file_count); - scene.Data[hash.c_str()] = {.MeshFileIndex = indices[0], .ModelPathIndex = indices[1], .MaterialPathIndex = indices[2]}; + for (int i = 0; i < asset_file_count; ++i) + { + auto& file = scene.AssetFiles.push_use({}); + ReadBinary(in_stream, file.Type); + ReadBinary(in_stream, file.Hash); + ReadBinaryString(&Arena, in_stream, file.Path); + ReadBinaryString(&Arena, in_stream, file.RootPath); } - REPORT_PROGRESS(Context, 1.f) - - in_stream.close(); + REPORT_LOG(Context, "Extracting scene name...") - auto ctx = reinterpret_cast(Context); - const auto& config = *ctx->ConfigurationPtr; + char buf[DEFAULT_STR_BUFFER] = {0}; + ReadBinaryCString(&Arena, in_stream, buf); + scene.Name = buf; - std::vector scene_data; - for (auto& [_, model] : scene.Data) - { - auto mesh_path = fmt::format("{0}{1}{2}{3}{4}", config.WorkingSpacePath.c_str(), PLATFORM_OS_BACKSLASH, config.SceneDataPath.c_str(), PLATFORM_OS_BACKSLASH, scene.MeshFiles[model.MeshFileIndex].c_str()); - auto model_path = fmt::format("{0}{1}{2}{3}{4}", config.WorkingSpacePath.c_str(), PLATFORM_OS_BACKSLASH, config.SceneDataPath.c_str(), PLATFORM_OS_BACKSLASH, scene.ModelFiles[model.ModelPathIndex].c_str()); - auto material_path = fmt::format("{0}{1}{2}{3}{4}", config.WorkingSpacePath.c_str(), PLATFORM_OS_BACKSLASH, config.SceneDataPath.c_str(), PLATFORM_OS_BACKSLASH, scene.MaterialFiles[model.MaterialPathIndex].c_str()); - - auto import_data = AssetImporter->DeserializeImporterData(&Arena, model_path, mesh_path, material_path); - scene_data.push_back(import_data.Scene); - } + REPORT_LOG(Context, "Extracting scene's hierarchies info...") - scene.RenderScene->SceneData->Vertices.clear(); - scene.RenderScene->SceneData->Indices.clear(); - scene.RenderScene->SceneData->Materials.clear(); - scene.RenderScene->SceneData->MaterialFiles.clear(); - scene.RenderScene->SceneData->DrawData.clear(); + ReadBinaryArray(&Arena, in_stream, scene.Names); + ReadBinaryArray(&Arena, in_stream, scene.Hierarchies); + ReadBinaryArray(&Arena, in_stream, scene.LocalTransforms); + ReadBinaryArray(&Arena, in_stream, scene.GlobalTransforms); - scene.RenderScene->SetRootNodeName(scene.Name); - scene.RenderScene->Merge(scene_data); - scene.RenderScene->IsDrawDataDirty = true; + ReadHashMap(&Arena, in_stream, scene.NodeMeshes); + ReadHashMap(&Arena, in_stream, scene.NodeNames); if (m_deserialize_complete_callback) { diff --git a/Tetragrama/Serializers/EditorSceneSerializer.h b/Tetragrama/Serializers/EditorSceneSerializer.h index e21d0f6d..e1cf0ef6 100644 --- a/Tetragrama/Serializers/EditorSceneSerializer.h +++ b/Tetragrama/Serializers/EditorSceneSerializer.h @@ -7,8 +7,6 @@ namespace Tetragrama::Serializers { struct EditorSceneSerializer : public Serializer { - ZRawPtr(Importers::IAssetImporter) AssetImporter = nullptr; - virtual void Serialize(ZRawPtr(EditorScene) const data) override; virtual void Deserialize(std::string_view filename) override; }; diff --git a/Tetragrama/Serializers/Serializer.h b/Tetragrama/Serializers/Serializer.h index 6a2a00be..5d4e6a2c 100644 --- a/Tetragrama/Serializers/Serializer.h +++ b/Tetragrama/Serializers/Serializer.h @@ -14,6 +14,14 @@ } \ } +#define REPORT_LOG(ctx, msg) \ + { \ + if (m_log_callback) \ + { \ + m_log_callback(ctx, msg); \ + } \ + } + namespace Tetragrama::Serializers { template @@ -81,6 +89,11 @@ namespace Tetragrama::Serializers return m_is_serializing.load(std::memory_order_acquire); } + virtual bool IsDeserializing() + { + return m_is_deserializing.load(std::memory_order_acquire); + } + virtual void Serialize(ZRawPtr(TSerializerData) const data) = 0; virtual void Deserialize(std::string_view filename) = 0; }; diff --git a/ZEngine/ZEngine/Core/Containers/HashMap.h b/ZEngine/ZEngine/Core/Containers/HashMap.h index b11faee3..848e46f9 100644 --- a/ZEngine/ZEngine/Core/Containers/HashMap.h +++ b/ZEngine/ZEngine/Core/Containers/HashMap.h @@ -1,6 +1,7 @@ #pragma once #include #include +#include #include using namespace ZEngine::Core::Memory; @@ -256,4 +257,9 @@ namespace ZEngine::Core::Containers Array m_entries; size_type m_size = 0; }; + + inline uint64_t hash_compute(const char* str) + { + return rapidhash(str, Helpers::secure_strlen(str)); + } } // namespace ZEngine::Core::Containers diff --git a/ZEngine/ZEngine/Core/Containers/Strings.h b/ZEngine/ZEngine/Core/Containers/Strings.h index e4b13a65..60012c37 100644 --- a/ZEngine/ZEngine/Core/Containers/Strings.h +++ b/ZEngine/ZEngine/Core/Containers/Strings.h @@ -189,6 +189,7 @@ namespace ZEngine::Core::Containers { if (m_data) { + Helpers::secure_memset(m_data, 0, m_capacity, m_capacity); m_size = 0; m_data[0] = '\0'; } diff --git a/ZEngine/ZEngine/Core/Memory/Allocator.cpp b/ZEngine/ZEngine/Core/Memory/Allocator.cpp index 8fd98b96..c79cbb1f 100644 --- a/ZEngine/ZEngine/Core/Memory/Allocator.cpp +++ b/ZEngine/ZEngine/Core/Memory/Allocator.cpp @@ -4,7 +4,7 @@ namespace ZEngine::Core::Memory { - void ArenaAllocator::Initialize(size_t size) + void ArenaAllocator::Initialize(uint64_t size) { m_memory = (uint8_t*) malloc(size); m_total_size = size; diff --git a/ZEngine/ZEngine/Core/Memory/Allocator.h b/ZEngine/ZEngine/Core/Memory/Allocator.h index 2ae695b9..3ee2916a 100644 --- a/ZEngine/ZEngine/Core/Memory/Allocator.h +++ b/ZEngine/ZEngine/Core/Memory/Allocator.h @@ -19,7 +19,7 @@ namespace ZEngine::Core::Memory { ~ArenaAllocator() {}; - void Initialize(size_t size); + void Initialize(uint64_t size); void Shutdown(); void* Allocate(size_t size, size_t alignment = DEFAULT_ALIGNMENT); diff --git a/ZEngine/ZEngine/Core/Memory/MemoryManager.h b/ZEngine/ZEngine/Core/Memory/MemoryManager.h index a080ca63..b49a2bda 100644 --- a/ZEngine/ZEngine/Core/Memory/MemoryManager.h +++ b/ZEngine/ZEngine/Core/Memory/MemoryManager.h @@ -5,7 +5,7 @@ namespace ZEngine::Core::Memory { struct MemoryConfiguration { - size_t DefaultSize = ZGiga(2ull); + uint64_t DefaultSize = ZGiga(2ull); }; struct MemoryManager diff --git a/ZEngine/ZEngine/Engine.cpp b/ZEngine/ZEngine/Engine.cpp index fe53cf5a..e1b24ad0 100644 --- a/ZEngine/ZEngine/Engine.cpp +++ b/ZEngine/ZEngine/Engine.cpp @@ -17,7 +17,7 @@ namespace ZEngine { g_current_window = window; - arena->CreateSubArena(ZMega(800), &g_engine_arena); + arena->CreateSubArena(ZGiga(1), &g_engine_arena); g_device = ZPushStructCtor(&g_engine_arena, Hardwares::VulkanDevice); g_renderer = ZPushStructCtor(&g_engine_arena, Rendering::Renderers::GraphicRenderer); diff --git a/ZEngine/ZEngine/Managers/IAssetManager.h b/ZEngine/ZEngine/Managers/IAssetManager.h deleted file mode 100644 index 8819628d..00000000 --- a/ZEngine/ZEngine/Managers/IAssetManager.h +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once -#include -#include - -namespace ZEngine::Managers -{ - - template - struct IAssetManager : public IManager> - { - IAssetManager() = default; - virtual ~IAssetManager() = default; - - /** - * Add an asset to the Asset manager store - * - * @param name Name of the asset. This name must be unique in the entire store - * @param filename Path to find the asset file in the system - * @return An asset instance - */ - virtual Helpers::Ref Add(const char* name, const char* filename) = 0; - - /** - * Add a asset to the Asset manager store - * - * @param filename Path to find the asset file in the system - * @return An asset instance - */ - virtual Helpers::Ref Load(const char* filename) = 0; - - /** - * Get an asset instance from the Asset manager store - * - * @param name Name of the asset. - * @return An asset instance. - * The asset must exists in the store, otherwise an assertion will be raised - */ - Helpers::Ref Obtains(const char* name) - { - const auto key = std::string(name).append(this->m_suffix); - const auto result = IManager>::Get(key); - assert(result.has_value() == true); - - return result->get(); - } - - protected: - std::string m_suffix{}; - }; -} // namespace ZEngine::Managers diff --git a/ZEngine/ZEngine/Managers/IManager.h b/ZEngine/ZEngine/Managers/IManager.h deleted file mode 100644 index 2b1a68b8..00000000 --- a/ZEngine/ZEngine/Managers/IManager.h +++ /dev/null @@ -1,87 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include - -namespace ZEngine::Managers -{ - - template || std::is_same_v>> - struct IManager - { - public: - IManager() = default; - virtual ~IManager() = default; - - protected: - auto Exists(const T& key) -> std::pair::iterator> - { - bool result{false}; - typename std::unordered_map::iterator it = std::find_if(std::begin(m_collection), std::end(m_collection), [&](const std::pair& item) { - if (std::is_arithmetic_v) - { - result = item.first == key; - } - - else if (std::is_same_v) - { - result = item.first.compare(key) == 0; - } - - return result; - }); - - return std::make_pair(result, it); - } - - std::optional> Add(const T& key, const K& val) - { - const auto& kv = Exists(key); - - if (kv.first) - { - auto it = kv.second; - return it->second; - } - - auto pair = m_collection.emplace(std::make_pair(key, val)); - return pair.first->second; - } - - std::optional> Add(T&& key, K&& val) - { - const auto& kv = Exists(key); - - if (kv.first) - { - auto it = kv.second; - return it->second; - } - - auto pair = m_collection.emplace(std::make_pair(std::move(key), std::move(val))); - return pair.first->second; - } - - std::optional> Get(const T& key) - { - - const auto& kv = Exists(key); - if (kv.first) - { - return kv.second->second; - } - return std::nullopt; - } - - std::unordered_map& GetAll() - { - return m_collection; - } - - protected: - inline static std::unordered_map m_collection{}; - }; - -} // namespace ZEngine::Managers diff --git a/ZEngine/ZEngine/ZEngineDef.h b/ZEngine/ZEngine/ZEngineDef.h index 370b2736..37023549 100644 --- a/ZEngine/ZEngine/ZEngineDef.h +++ b/ZEngine/ZEngine/ZEngineDef.h @@ -50,6 +50,7 @@ #define SINGLE_ARG(...) __VA_ARGS__ #define MAX_FILE_PATH_COUNT 256 +#define DEFAULT_STR_BUFFER 256 #define ZRawPtr(X) X* @@ -79,3 +80,17 @@ #define ZAlloc(allocator, size, alignment) ((allocator)->Allocate((size), (alignment))) #define ZResize(allocator, ptr, old_size, new_size, alignment) ((allocator)->Resize((ptr), (old_size), (new_size), (alignment))) #define ZAlignof(type) ((alignof(type) < DEFAULT_ALIGNMENT) ? DEFAULT_ALIGNMENT : alignof(type)) + +/* + * + */ +#define MAKE_MAGIC(a, b, c, d) ((uint32_t) (a) << 24 | (uint32_t) (b) << 16 | (uint32_t) (c) << 8 | (uint32_t) (d)) +#define MAKE_VERSION(major, minor, patch) (((uint32_t) (major) << 16) | ((uint32_t) (minor) << 8) | ((uint32_t) (patch))) + +#define ZEASSET_MAGIC MAKE_MAGIC('Z', 'A', 'S', 'T') +#define ZEMESH_MAGIC MAKE_MAGIC('Z', 'M', 'S', 'H') +#define ZEMATERIAL_MAGIC MAKE_MAGIC('Z', 'M', 'A', 'T') +#define ZETEXTURES_MAGIC MAKE_MAGIC('Z', 'T', 'E', 'X') +#define ZESCENE_MAGIC MAKE_MAGIC('Z', 'S', 'C', 'N') +#define ASSET_FILE_VERSION MAKE_VERSION(1, 0, 0) +#define SCENE_FILE_VERSION MAKE_VERSION(1, 0, 0)