diff --git a/Sandbox/src/ExampleLayer.cpp b/Sandbox/src/ExampleLayer.cpp index a5f2ac4e..be9ffbc5 100644 --- a/Sandbox/src/ExampleLayer.cpp +++ b/Sandbox/src/ExampleLayer.cpp @@ -7,6 +7,8 @@ #include "StarEngine.h" +#include "StarEngine/Asset/TextureImporter.h" + ExampleLayer::ExampleLayer() : Layer("ExampleLayer"), m_CameraController(1280.0f / 720.0f) { @@ -113,8 +115,8 @@ ExampleLayer::ExampleLayer() m_FlatColorShader = StarEngine::Shader::Create("FlatColor", flatColorShaderVertexSrc, flatColorShaderFragmentSrc); auto textureShader = m_ShaderLibrary.Load("assets/shaders/Texture.glsl"); - m_Texture = StarEngine::Texture2D::Create("assets/textures/Checkerboard.png"); - m_starLogoTexture = StarEngine::Texture2D::Create("assets/textures/starLogo.png"); + m_Texture = StarEngine::TextureImporter::LoadTexture2D("assets/textures/Checkerboard.png"); + m_starLogoTexture = StarEngine::TextureImporter::LoadTexture2D("assets/textures/starLogo.png"); textureShader->Bind(); textureShader->SetInt("u_Texture", 0); diff --git a/Sandbox/src/Sandbox2D.cpp b/Sandbox/src/Sandbox2D.cpp index f6a8b8e4..bfdf3c7a 100644 --- a/Sandbox/src/Sandbox2D.cpp +++ b/Sandbox/src/Sandbox2D.cpp @@ -7,6 +7,8 @@ #include "imgui/imgui_internal.h" +#include "StarEngine/Asset/TextureImporter.h" + Sandbox2D::Sandbox2D() :Layer("Sandbox2D"), m_CameraController(1280.0f / 720.0f, true), m_SquareColor({ 0.2f, 0.3f, 0.8f, 1.0f }) @@ -18,7 +20,7 @@ void Sandbox2D::OnAttach() { SE_PROFILE_FUNCTION(); - m_CheckerboardTexture = StarEngine::Texture2D::Create("assets/textures/Checkerboard.png"); + m_CheckerboardTexture = StarEngine::TextureImporter::LoadTexture2D("assets/textures/Checkerboard.png"); } void Sandbox2D::OnDetach() diff --git a/StarEditor/SandboxProject/Assets/AssetRegistry.ser b/StarEditor/SandboxProject/Assets/AssetRegistry.ser new file mode 100644 index 00000000..a24456c0 --- /dev/null +++ b/StarEditor/SandboxProject/Assets/AssetRegistry.ser @@ -0,0 +1,7 @@ +AssetRegistry: + - Handle: 9263618904997247981 + FilePath: Textures/starLogo.png + Type: AssetType::Texture2D + - Handle: 10448957311305808839 + FilePath: Scenes/Physics2D.starscene + Type: AssetType::Scene \ No newline at end of file diff --git a/StarEditor/SandboxProject/Assets/Scenes/Physics2D.starscene b/StarEditor/SandboxProject/Assets/Scenes/Physics2D.starscene index 16f8b624..87f8194b 100644 --- a/StarEditor/SandboxProject/Assets/Scenes/Physics2D.starscene +++ b/StarEditor/SandboxProject/Assets/Scenes/Physics2D.starscene @@ -1,37 +1,64 @@ Scene: Untitled Entities: - - Entity: 12327645238897851592 + - Entity: 14040563719264005740 TagComponent: - Tag: Welcome + Tag: Player TransformComponent: - Translation: [-3.79999995, 1.79999995, 0] + Translation: [0, 5, 0] Rotation: [0, 0, 0] Scale: [1, 1, 1] - TextComponent: - TextString: Welcome to StarEditor + SpriteRendererComponent: Color: [1, 1, 1, 1] - Kerning: 0 - LineSpacing: 0 - - Entity: 6793217508611625246 + TextureHandle: 0 + TilingFactor: 1 + RigidBody2DComponent: + BodyType: Dynamic + FixedRotation: false + BoxCollider2DComponent: + Offset: [0, 0] + Size: [0.5, 0.5] + Density: 1 + Friction: 0.5 + Restitution: 0 + RestitutionThreshold: 0.5 + - Entity: 16241606122818465121 TagComponent: - Tag: Circle + Tag: Camera TransformComponent: Translation: [0, 0, 0] Rotation: [0, 0, 0] Scale: [1, 1, 1] - CircleRendererComponent: - Color: [0.934362948, 0.497845858, 0, 1] - Thickness: 1 - Fade: 0.00499999989 + CameraComponent: + Camera: + ProjectionType: 1 + PerspectiveFOV: 0.785398185 + PerspectiveNear: 0.00999999978 + PerspectiveFar: 1000 + OrthographicSize: 25 + OrthographicNear: -1 + OrthographicFar: 1 + Primary: true + FixedAspectRatio: false + - Entity: 16059016071823036758 + TagComponent: + Tag: Cube + TransformComponent: + Translation: [0, 10, 0] + Rotation: [0, 0, 0] + Scale: [1, 1, 1] + SpriteRendererComponent: + Color: [1, 1, 1, 1] + TextureHandle: 0 + TilingFactor: 1 RigidBody2DComponent: BodyType: Dynamic FixedRotation: false - CircleCollider2DComponent: + BoxCollider2DComponent: Offset: [0, 0] - Radius: 0.5 + Size: [0.5, 0.5] Density: 1 Friction: 0.5 - Restitution: 1 + Restitution: 0.100000001 RestitutionThreshold: 0.5 - Entity: 7665505456185074185 TagComponent: @@ -42,6 +69,7 @@ Entities: Scale: [10, 1, 1] SpriteRendererComponent: Color: [1, 1, 1, 1] + TextureHandle: 0 TilingFactor: 1 RigidBody2DComponent: BodyType: Static @@ -53,61 +81,36 @@ Entities: Friction: 0.5 Restitution: 0 RestitutionThreshold: 0.5 - - Entity: 16059016071823036758 + - Entity: 6793217508611625246 TagComponent: - Tag: Cube + Tag: Circle TransformComponent: - Translation: [0, 10, 0] + Translation: [0, 0, 0] Rotation: [0, 0, 0] Scale: [1, 1, 1] - SpriteRendererComponent: - Color: [1, 1, 1, 1] - TilingFactor: 1 + CircleRendererComponent: + Color: [0.934362948, 0.497845858, 0, 1] + Thickness: 1 + Fade: 0.00499999989 RigidBody2DComponent: BodyType: Dynamic FixedRotation: false - BoxCollider2DComponent: + CircleCollider2DComponent: Offset: [0, 0] - Size: [0.5, 0.5] + Radius: 0.5 Density: 1 Friction: 0.5 - Restitution: 0.100000001 + Restitution: 1 RestitutionThreshold: 0.5 - - Entity: 16241606122818465121 - TagComponent: - Tag: Camera - TransformComponent: - Translation: [0, 0, 0] - Rotation: [0, 0, 0] - Scale: [1, 1, 1] - CameraComponent: - Camera: - ProjectionType: 1 - PerspectiveFOV: 0.785398185 - PerspectiveNear: 0.00999999978 - PerspectiveFar: 1000 - OrthographicSize: 25 - OrthographicNear: -1 - OrthographicFar: 1 - Primary: true - FixedAspectRatio: false - - Entity: 14040563719264005740 + - Entity: 12327645238897851592 TagComponent: - Tag: Player + Tag: Welcome TransformComponent: - Translation: [0, 5, 0] + Translation: [-3.79999995, 1.79999995, 0] Rotation: [0, 0, 0] Scale: [1, 1, 1] - SpriteRendererComponent: + TextComponent: + TextString: Welcome to StarEditor Color: [1, 1, 1, 1] - TilingFactor: 1 - RigidBody2DComponent: - BodyType: Dynamic - FixedRotation: false - BoxCollider2DComponent: - Offset: [0, 0] - Size: [0.5, 0.5] - Density: 1 - Friction: 0.5 - Restitution: 0 - RestitutionThreshold: 0.5 \ No newline at end of file + Kerning: 0 + LineSpacing: 0 \ No newline at end of file diff --git a/StarEditor/SandboxProject/Sandbox.starproj b/StarEditor/SandboxProject/Sandbox.starproj index b128b227..b9f4e281 100644 --- a/StarEditor/SandboxProject/Sandbox.starproj +++ b/StarEditor/SandboxProject/Sandbox.starproj @@ -1,5 +1,6 @@ Project: Name: Sandbox - StartScene: "Scenes/Physics2D.starscene" + StartScene: "0" AssetDirectory: "Assets" + AssetRegistryPath: "AssetRegistry.ser" ScriptModulePath: "Scripts/Binaries/Sandbox.dll" diff --git a/StarEditor/src/EditorLayer.cpp b/StarEditor/src/EditorLayer.cpp index beb2abb1..c83ca63d 100644 --- a/StarEditor/src/EditorLayer.cpp +++ b/StarEditor/src/EditorLayer.cpp @@ -6,6 +6,10 @@ #include "StarEngine/Scripting/ScriptEngine.h" #include "StarEngine/Renderer/Font.h" +#include "StarEngine/Asset/AssetManager.h" +#include "StarEngine/Asset/TextureImporter.h" +#include "StarEngine/Asset/SceneImporter.h" + #include #include @@ -29,13 +33,11 @@ namespace StarEngine { { SE_PROFILE_FUNCTION(); - m_CheckerboardTexture = Texture2D::Create("assets/textures/Checkerboard.png"); - - m_IconPlay = Texture2D::Create("Resources/Icons/PlayButton.png"); - m_IconPause = Texture2D::Create("Resources/Icons/PauseButton.png"); - m_IconSimulate = Texture2D::Create("Resources/Icons/SimulateButton.png"); - m_IconStep = Texture2D::Create("Resources/Icons/StepButton.png"); - m_IconStop = Texture2D::Create("Resources/Icons/StopButton.png"); + m_IconPlay = TextureImporter::LoadTexture2D("Resources/Icons/PlayButton.png"); + m_IconPause = TextureImporter::LoadTexture2D("Resources/Icons/PauseButton.png"); + m_IconSimulate = TextureImporter::LoadTexture2D("Resources/Icons/SimulateButton.png"); + m_IconStep = TextureImporter::LoadTexture2D("Resources/Icons/StepButton.png"); + m_IconStop = TextureImporter::LoadTexture2D("Resources/Icons/StopButton.png"); FramebufferSpecification fbSpec; fbSpec.Attachments = { FramebufferTextureFormat::RGBA8, FramebufferTextureFormat::RED_INTEGER, FramebufferTextureFormat::Depth }; @@ -297,8 +299,8 @@ namespace StarEngine { { if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("CONTENT_BROWSER_ITEM")) { - const wchar_t* path = (const wchar_t*)payload->Data; - OpenScene(path); + AssetHandle handle = *(AssetHandle*)payload->Data; + OpenScene(handle); } ImGui::EndDragDropTarget(); } @@ -464,6 +466,7 @@ namespace StarEngine { EventDispatcher dispatcher(e); dispatcher.Dispatch(SE_BIND_EVENT_FN(EditorLayer::OnKeyPressed)); dispatcher.Dispatch(SE_BIND_EVENT_FN(EditorLayer::OnMouseButtonPressed)); + dispatcher.Dispatch(SE_BIND_EVENT_FN(EditorLayer::OnWindowDrop)); } bool EditorLayer::OnKeyPressed(KeyPressedEvent& e) @@ -578,6 +581,15 @@ namespace StarEngine { return false; } + bool EditorLayer::OnWindowDrop(WindowDropEvent& e) + { + // TODO: if a project is dropped in, probably open it + + //AssetManager::ImportAsset(); + + return true; + } + void EditorLayer::OnOverlayRender() { if (m_SceneState == SceneState::Play) @@ -652,9 +664,10 @@ namespace StarEngine { { ScriptEngine::Init(); - auto startScenePath = Project::GetAssetFileSystemPath(Project::GetActive()->GetConfig().StartScene); - OpenScene(startScenePath); - m_ContentBrowserPanel = CreateScope(); + AssetHandle startScene = Project::GetActive()->GetConfig().StartScene; + if (startScene) + OpenScene(startScene); + m_ContentBrowserPanel = CreateScope(Project::GetActive()); } } @@ -684,33 +697,26 @@ namespace StarEngine { void EditorLayer::OpenScene() { - std::string filepath = FileDialogs::OpenFile("StarEngine Scene (*.starscene)\0*.starscene\0"); - if (!filepath.empty()) - OpenScene(filepath); + // std::string filepath = FileDialogs::OpenFile("Hazel Scene (*.hazel)\0*.hazel\0"); + // if (!filepath.empty()) + // OpenScene(filepath); } - void EditorLayer::OpenScene(const std::filesystem::path& path) + void EditorLayer::OpenScene(AssetHandle handle) { + SE_CORE_ASSERT(handle); + if (m_SceneState != SceneState::Edit) OnSceneStop(); - if (path.extension().string() != ".starscene") - { - SE_WARN("Could not load {0} - not a scene file", path.filename().string()); - return; - } + Ref readOnlyScene = AssetManager::GetAsset(handle); + Ref newScene = Scene::Copy(readOnlyScene); - Ref newScene = CreateRef(); - SceneSerializer serializer(newScene); - if (serializer.Deserialize(path.string())) - { - m_EditorScene = newScene; - m_EditorScene->OnViewportResize((uint32_t)m_ViewportSize.x, (uint32_t)m_ViewportSize.y); - m_SceneHierarchyPanel.SetContext(m_EditorScene); + m_EditorScene = newScene; + m_SceneHierarchyPanel.SetContext(m_EditorScene); - m_ActiveScene = m_EditorScene; - m_EditorScenePath = path; - } + m_ActiveScene = m_EditorScene; + m_EditorScenePath = Project::GetActive()->GetEditorAssetManager()->GetFilePath(handle); } void EditorLayer::SaveScene() @@ -733,8 +739,7 @@ namespace StarEngine { void EditorLayer::SerializeScene(Ref scene, const std::filesystem::path& path) { - SceneSerializer serializer(scene); - serializer.Serialize(path.string()); + SceneImporter::SaveScene(scene, path); } void EditorLayer::OnScenePlay() diff --git a/StarEditor/src/EditorLayer.h b/StarEditor/src/EditorLayer.h index 31b60c15..7950abc0 100644 --- a/StarEditor/src/EditorLayer.h +++ b/StarEditor/src/EditorLayer.h @@ -27,6 +27,7 @@ namespace StarEngine private: bool OnKeyPressed(KeyPressedEvent& e); bool OnMouseButtonPressed(MouseButtonPressedEvent& e); + bool OnWindowDrop(WindowDropEvent& e); void OnOverlayRender(); @@ -37,7 +38,7 @@ namespace StarEngine void NewScene(); void OpenScene(); - void OpenScene(const std::filesystem::path& path); + void OpenScene(AssetHandle handle); void SaveScene(); void SaveSceneAs(); diff --git a/StarEditor/src/Panels/ContentBrowserPanel.cpp b/StarEditor/src/Panels/ContentBrowserPanel.cpp index 56fb25cc..e85a8755 100644 --- a/StarEditor/src/Panels/ContentBrowserPanel.cpp +++ b/StarEditor/src/Panels/ContentBrowserPanel.cpp @@ -2,30 +2,47 @@ #include "ContentBrowserPanel.h" #include "StarEngine/Project/Project.h" +#include "StarEngine/Asset/TextureImporter.h" #include -namespace StarEngine -{ - ContentBrowserPanel::ContentBrowserPanel() - : m_BaseDirectory(Project::GetAssetDirectory()), m_CurrentDirectory(m_BaseDirectory) +#include "StarEngine/Utils/StringUtils.h" + +namespace StarEngine { + + ContentBrowserPanel::ContentBrowserPanel(Ref project) + : m_Project(project), m_ThumbnailCache(CreateRef(project)), m_BaseDirectory(m_Project->GetAssetDirectory()), m_CurrentDirectory(m_BaseDirectory) { - m_DirectoryIcon = Texture2D::Create("Resources/Icons/ContentBrowser/DirectoryIcon.png"); - m_FileIcon = Texture2D::Create("Resources/Icons/ContentBrowser/FileIcon.png"); + m_TreeNodes.push_back(TreeNode(".", 0)); + + m_DirectoryIcon = TextureImporter::LoadTexture2D("Resources/Icons/ContentBrowser/DirectoryIcon.png"); + m_FileIcon = TextureImporter::LoadTexture2D("Resources/Icons/ContentBrowser/FileIcon.png"); + + RefreshAssetTree(); + + m_Mode = Mode::FileSystem; } void ContentBrowserPanel::OnImGuiRender() { ImGui::Begin("Content Browser"); + const char* label = m_Mode == Mode::Asset ? "Asset" : "File"; + if (ImGui::Button(label)) + { + m_Mode = m_Mode == Mode::Asset ? Mode::FileSystem : Mode::Asset; + } + if (m_CurrentDirectory != std::filesystem::path(m_BaseDirectory)) { + ImGui::SameLine(); if (ImGui::Button("<-")) { m_CurrentDirectory = m_CurrentDirectory.parent_path(); } } + static float padding = 16.0f; static float thumbnailSize = 128.0f; float cellSize = thumbnailSize + padding; @@ -37,37 +54,224 @@ namespace StarEngine ImGui::Columns(columnCount, 0, false); - for (auto& directoryEntry : std::filesystem::directory_iterator(m_CurrentDirectory)) - { - const auto& path = directoryEntry.path(); - std::string filenameString = path.filename().string(); + uint32_t itemRenderCount = 0; - ImGui::PushID(filenameString.c_str()); - Ref icon = directoryEntry.is_directory() ? m_DirectoryIcon : m_FileIcon; - ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0)); - ImGui::ImageButton("##icon", (ImTextureID)icon->GetRendererID(), { thumbnailSize, thumbnailSize }, { 0, 1 }, { 1, 0 }); + if (m_Mode == Mode::Asset) + { + TreeNode* node = &m_TreeNodes[0]; - if (ImGui::BeginDragDropSource()) + auto currentDir = std::filesystem::relative(m_CurrentDirectory, Project::GetActiveAssetDirectory()); + for (const auto& p : currentDir) { - std::filesystem::path relativePath(path); - const wchar_t* itemPath = relativePath.c_str(); - ImGui::SetDragDropPayload("CONTENT_BROWSER_ITEM", itemPath, (wcslen(itemPath) + 1) * sizeof(wchar_t)); - ImGui::EndDragDropSource(); + // if only one level + if (node->Path == currentDir) + break; + + if (node->Children.find(p) != node->Children.end()) + { + node = &m_TreeNodes[node->Children[p]]; + continue; + } + else + { + // can't find path + SE_CORE_ASSERT(false); + } + } - ImGui::PopStyleColor(); - if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) + for (const auto& [item, treeNodeIndex] : node->Children) { - if (directoryEntry.is_directory()) - m_CurrentDirectory /= path.filename(); + bool isDirectory = std::filesystem::is_directory(Project::GetActiveAssetDirectory() / item); + + std::string itemStr = item.generic_string(); + + ImGui::PushID(itemStr.c_str()); + Ref icon = isDirectory ? m_DirectoryIcon : m_FileIcon; + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0)); + ImGui::ImageButton("##icon", (ImTextureID)icon->GetRendererID(), { thumbnailSize, thumbnailSize }, { 0, 1 }, { 1, 0 }); + + if (ImGui::BeginPopupContextItem()) + { + if (ImGui::MenuItem("Delete")) + { + SE_CORE_ASSERT(false, "Not implemented"); + } + ImGui::EndPopup(); + } + if (ImGui::BeginDragDropSource()) + { + AssetHandle handle = m_TreeNodes[treeNodeIndex].Handle; + ImGui::SetDragDropPayload("CONTENT_BROWSER_ITEM", &handle, sizeof(AssetHandle)); + ImGui::EndDragDropSource(); + } + + + ImGui::PopStyleColor(); + if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) + { + if (isDirectory) + m_CurrentDirectory /= item.filename(); + } + + ImGui::TextWrapped(itemStr.c_str()); + + ImGui::NextColumn(); + + ImGui::PopID(); + } + } + else + { + uint32_t count = 0; + for (auto& directoryEntry : std::filesystem::directory_iterator(m_CurrentDirectory)) + { + count++; } - ImGui::TextWrapped(filenameString.c_str()); - ImGui::NextColumn(); + // 1. How many entries? + // 2. Advance iterator to starting entry + ImGuiListClipper clipper; + clipper.Begin((int)glm::ceil((float)count / (float)columnCount)); + bool first = true; + while (clipper.Step()) + { + auto it = std::filesystem::directory_iterator(m_CurrentDirectory); + if (!first) + { + // advance to clipper.DisplayStart + for (int i = 0; i < clipper.DisplayStart; i++) + { + for (int c = 0; c < columnCount && it != std::filesystem::directory_iterator(); c++) + { + it++; + } + } + } + + for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) + { + int c; + for (c = 0; c < columnCount && it != std::filesystem::directory_iterator(); c++, it++) + { + const auto& directoryEntry = *it; + + const auto& path = directoryEntry.path(); + std::string filenameString = path.filename().string(); + + ImGui::PushID(filenameString.c_str()); + + // THUMBNAIL + auto relativePath = std::filesystem::relative(path, Project::GetActiveAssetDirectory()); + Ref thumbnail = m_DirectoryIcon; + if (!directoryEntry.is_directory()) + { + thumbnail = m_ThumbnailCache->GetOrCreateThumbnail(relativePath); + if (!thumbnail) + thumbnail = m_FileIcon; + } + + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0)); + + float thumbnailHeight = thumbnailSize * ((float)thumbnail->GetHeight() / (float)thumbnail->GetWidth()); + float diff = thumbnailSize - thumbnailHeight; - ImGui::PopID(); + ImGui::SetCursorPosY(ImGui::GetCursorPosY() + diff); // Center thumbnail vertically + + ImGui::ImageButton("##thumbnail", (ImTextureID)thumbnail->GetRendererID(), { thumbnailSize, thumbnailHeight }, { 0, 1 }, { 1, 0 }); + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + std::string sizeString = Utils::BytesToString(thumbnail->GetEstimatedSize()); + ImGui::Text("Memory: %s", sizeString.c_str()); + ImGui::EndTooltip(); + } + + itemRenderCount++; + + if (ImGui::BeginPopupContextItem()) + { + if (ImGui::MenuItem("Import")) + { + Project::GetActive()->GetEditorAssetManager()->ImportAsset(relativePath); + RefreshAssetTree(); + } + ImGui::EndPopup(); + } + + ImGui::PopStyleColor(); + if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) + { + if (directoryEntry.is_directory()) + m_CurrentDirectory /= path.filename(); + } + + ImGui::TextWrapped(filenameString.c_str()); + + ImGui::NextColumn(); + + ImGui::PopID(); + } + + if (first && c < columnCount) + { + for (int extra = 0; extra < columnCount - c; extra++) + { + ImGui::NextColumn(); + } + } + } + + first = false; + } } + /* + { + for (auto& directoryEntry : std::filesystem::directory_iterator(m_CurrentDirectory)) + { + const auto& path = directoryEntry.path(); + std::string filenameString = path.filename().string(); + + ImGui::PushID(filenameString.c_str()); + + // THUMBNAIL + auto relativePath = std::filesystem::relative(path, Project::GetActiveAssetDirectory()); + Ref thumbnail = m_DirectoryIcon; + if (!directoryEntry.is_directory()) + { + thumbnail = m_ThumbnailCache->GetOrCreateThumbnail(relativePath); + if (!thumbnail) + thumbnail = m_FileIcon; + } + + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0)); + ImGui::ImageButton("##thumbnail", (ImTextureID)thumbnail->GetRendererID(), { thumbnailSize, thumbnailSize }, { 0, 1 }, { 1, 0 }); + + if (ImGui::BeginPopupContextItem()) + { + if (ImGui::MenuItem("Import")) + { + Project::GetActive()->GetEditorAssetManager()->ImportAsset(relativePath); + RefreshAssetTree(); + } + ImGui::EndPopup(); + } + + ImGui::PopStyleColor(); + if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) + { + if (directoryEntry.is_directory()) + m_CurrentDirectory /= path.filename(); + } + + ImGui::TextWrapped(filenameString.c_str()); + + ImGui::NextColumn(); + + ImGui::PopID(); + } + }*/ ImGui::Columns(1); @@ -76,6 +280,38 @@ namespace StarEngine // TODO: status bar ImGui::End(); + + m_ThumbnailCache->OnUpdate(); + } + + + void ContentBrowserPanel::RefreshAssetTree() + { + const auto& assetRegistry = Project::GetActive()->GetEditorAssetManager()->GetAssetRegistry(); + for (const auto& [handle, metadata] : assetRegistry) + { + uint32_t currentNodeIndex = 0; + + for (const auto& p : metadata.FilePath) + { + auto it = m_TreeNodes[currentNodeIndex].Children.find(p.generic_string()); + if (it != m_TreeNodes[currentNodeIndex].Children.end()) + { + currentNodeIndex = it->second; + } + else + { + // add node + TreeNode newNode(p, handle); + newNode.Parent = currentNodeIndex; + m_TreeNodes.push_back(newNode); + + m_TreeNodes[currentNodeIndex].Children[p] = m_TreeNodes.size() - 1; + currentNodeIndex = m_TreeNodes.size() - 1; + } + + } + } } } diff --git a/StarEditor/src/Panels/ContentBrowserPanel.h b/StarEditor/src/Panels/ContentBrowserPanel.h index c30c24c7..ed1f05a7 100644 --- a/StarEditor/src/Panels/ContentBrowserPanel.h +++ b/StarEditor/src/Panels/ContentBrowserPanel.h @@ -2,22 +2,55 @@ #include "StarEngine/Renderer/Texture.h" +#include "ThumbnailCache.h" + +#include +#include #include -namespace StarEngine -{ +namespace StarEngine { + class ContentBrowserPanel { public: - ContentBrowserPanel(); + ContentBrowserPanel(Ref project); void OnImGuiRender(); private: + void RefreshAssetTree(); + private: + Ref m_Project; + Ref m_ThumbnailCache; + std::filesystem::path m_BaseDirectory; std::filesystem::path m_CurrentDirectory; Ref m_DirectoryIcon; Ref m_FileIcon; + + struct TreeNode + { + std::filesystem::path Path; + AssetHandle Handle = 0; + + uint32_t Parent = (uint32_t)-1; + std::map Children; + + TreeNode(const std::filesystem::path& path, AssetHandle handle) + : Path(path), Handle(handle) { + } + }; + + std::vector m_TreeNodes; + + std::map> m_AssetTree; + + enum class Mode + { + Asset = 0, FileSystem = 1 + }; + + Mode m_Mode = Mode::Asset; }; } diff --git a/StarEditor/src/Panels/SceneHierarchyPanel.cpp b/StarEditor/src/Panels/SceneHierarchyPanel.cpp index f9cec346..0507befb 100644 --- a/StarEditor/src/Panels/SceneHierarchyPanel.cpp +++ b/StarEditor/src/Panels/SceneHierarchyPanel.cpp @@ -1,9 +1,14 @@ +#include "sepch.h" + #include "SceneHierarchyPanel.h" #include "StarEngine/Scene/Components.h" #include "StarEngine/Scripting/ScriptEngine.h" #include "StarEngine/UI/UI.h" +#include "StarEngine/Asset/AssetManager.h" +#include "StarEngine/Asset/AssetMetadata.h" + #include #include #include @@ -19,8 +24,8 @@ #define _CRT_SECURE_NO_WARNINGS #endif -namespace StarEngine -{ +namespace StarEngine { + SceneHierarchyPanel::SceneHierarchyPanel(const Ref& context) { SetContext(context); @@ -56,6 +61,7 @@ namespace StarEngine ImGui::EndPopup(); } + } ImGui::End(); @@ -405,22 +411,60 @@ namespace StarEngine { ImGui::ColorEdit4("Color", glm::value_ptr(component.Color)); - ImGui::Button("Texture", ImVec2(100.0f, 0.0f)); + std::string label = "None"; + bool isTextureValid = false; + if (component.Texture != 0) + { + if (AssetManager::IsAssetHandleValid(component.Texture) + && AssetManager::GetAssetType(component.Texture) == AssetType::Texture2D) + { + const AssetMetadata& metadata = Project::GetActive()->GetEditorAssetManager()->GetMetadata(component.Texture); + label = metadata.FilePath.filename().string(); + isTextureValid = true; + } + else + { + label = "Invalid"; + } + } + + ImVec2 buttonLabelSize = ImGui::CalcTextSize(label.c_str()); + buttonLabelSize.x += 20.0f; + float buttonLabelWidth = glm::max(100.0f, buttonLabelSize.x); + + ImGui::Button(label.c_str(), ImVec2(buttonLabelWidth, 0.0f)); if (ImGui::BeginDragDropTarget()) { if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("CONTENT_BROWSER_ITEM")) { - const wchar_t* path = (const wchar_t*)payload->Data; - std::filesystem::path texturePath(path); - Ref texture = Texture2D::Create(texturePath.string()); - if (texture->IsLoaded()) - component.Texture = texture; + AssetHandle handle = *(AssetHandle*)payload->Data; + if (AssetManager::GetAssetType(handle) == AssetType::Texture2D) + { + component.Texture = handle; + } else - SE_WARN("Could not load texture {0}", texturePath.filename().string()); + { + SE_CORE_WARN("Wrong asset type!"); + } + } ImGui::EndDragDropTarget(); } + if (isTextureValid) + { + ImGui::SameLine(); + ImVec2 xLabelSize = ImGui::CalcTextSize("X"); + float buttonSize = xLabelSize.y + ImGui::GetStyle().FramePadding.y * 2.0f; + if (ImGui::Button("X", ImVec2(buttonSize, buttonSize))) + { + component.Texture = 0; + } + } + + ImGui::SameLine(); + ImGui::Text("Texture"); + ImGui::DragFloat("Tiling Factor", &component.TilingFactor, 0.1f, 0.0f, 100.0f); }); @@ -431,7 +475,7 @@ namespace StarEngine ImGui::DragFloat("Fade", &component.Fade, 0.00025f, 0.0f, 1.0f); }); - DrawComponent("Rigid Body 2D", entity, [](auto& component) + DrawComponent("RigidBody 2D", entity, [](auto& component) { const char* bodyTypeStrings[] = { "Static", "Dynamic", "Kinematic" }; const char* currentBodyTypeString = bodyTypeStrings[(int)component.Type]; @@ -483,6 +527,7 @@ namespace StarEngine ImGui::DragFloat("Kerning", &component.Kerning, 0.025f); ImGui::DragFloat("Line Spacing", &component.LineSpacing, 0.025f); }); + } template @@ -496,4 +541,5 @@ namespace StarEngine } } } + } diff --git a/StarEditor/src/Panels/SceneHierarchyPanel.h b/StarEditor/src/Panels/SceneHierarchyPanel.h index 7f17197d..2fdba695 100644 --- a/StarEditor/src/Panels/SceneHierarchyPanel.h +++ b/StarEditor/src/Panels/SceneHierarchyPanel.h @@ -1,12 +1,12 @@ #pragma once #include "StarEngine/Core/Base.h" - #include "StarEngine/Scene/Scene.h" #include "StarEngine/Scene/Entity.h" namespace StarEngine { - class SceneHierarchyPanel + + class SceneHierarchyPanel { public: SceneHierarchyPanel() = default; @@ -28,4 +28,5 @@ namespace StarEngine { Ref m_Context; Entity m_SelectionContext; }; + } diff --git a/StarEditor/src/Panels/ThumbnailCache.cpp b/StarEditor/src/Panels/ThumbnailCache.cpp new file mode 100644 index 00000000..6b595a43 --- /dev/null +++ b/StarEditor/src/Panels/ThumbnailCache.cpp @@ -0,0 +1,75 @@ +#include "ThumbnailCache.h" + +#include "StarEngine/Asset/TextureImporter.h" + +#include + +namespace StarEngine { + + + ThumbnailCache::ThumbnailCache(Ref project) + : m_Project(project) + { + // TODO: revisit this path (move to Cache dir) + m_ThumbnailCachePath = m_Project->GetAssetDirectory() / "Thumbnail.cache"; + } + + Ref ThumbnailCache::GetOrCreateThumbnail(const std::filesystem::path& assetPath) + { + // 1. Read file timestamp + // 2. Compare hashed timestamp with existing cached image (in memory first, then from cache file) + // 3. If equal, return associated thumbnail, otherwise load asset from disk and generate thumbnail + // 4. If generated new thumbnail, store in cache obviously + + auto absolutePath = m_Project->GetAssetAbsolutePath(assetPath); + std::filesystem::file_time_type lastWriteTime = std::filesystem::last_write_time(absolutePath); + uint64_t timestamp = std::chrono::duration_cast(lastWriteTime.time_since_epoch()).count(); + + if (m_CachedImages.find(assetPath) != m_CachedImages.end()) + { + auto& cachedImage = m_CachedImages.at(assetPath); + if (cachedImage.Timestamp == timestamp) + return cachedImage.Image; + } + + // TODO: PNGs for now + if (assetPath.extension() != ".png") + return nullptr; + + m_Queue.push({ absolutePath, assetPath, timestamp }); + return nullptr; + } + + void ThumbnailCache::OnUpdate() + { + while (!m_Queue.empty()) + { + const auto& thumbnailInfo = m_Queue.front(); + + if (m_CachedImages.find(thumbnailInfo.AssetPath) != m_CachedImages.end()) + { + auto& cachedImage = m_CachedImages.at(thumbnailInfo.AssetPath); + if (cachedImage.Timestamp == thumbnailInfo.Timestamp) + { + m_Queue.pop(); + continue; + } + } + + Ref texture = TextureImporter::LoadTexture2D(thumbnailInfo.AbsolutePath); + float thumbnailHeight = m_ThumbnailSize * ((float)texture->GetHeight() / (float)texture->GetWidth()); + texture->ChangeSize(m_ThumbnailSize, thumbnailHeight); + if (!texture) { + m_Queue.pop(); + continue; + } + + auto& cachedImage = m_CachedImages[thumbnailInfo.AssetPath]; + cachedImage.Timestamp = thumbnailInfo.Timestamp; + cachedImage.Image = texture; + m_Queue.pop(); + break; + } + } + +} diff --git a/StarEditor/src/Panels/ThumbnailCache.h b/StarEditor/src/Panels/ThumbnailCache.h new file mode 100644 index 00000000..5b5d394d --- /dev/null +++ b/StarEditor/src/Panels/ThumbnailCache.h @@ -0,0 +1,42 @@ +#pragma once + +#include "StarEngine/Project/Project.h" +#include "StarEngine/Renderer/Texture.h" + +#include + +namespace StarEngine { + + struct ThumbnailImage + { + uint64_t Timestamp; + Ref Image; + }; + + class ThumbnailCache + { + public: + ThumbnailCache(Ref project); + + Ref GetOrCreateThumbnail(const std::filesystem::path& path); + void OnUpdate(); + private: + Ref m_Project; + + uint32_t m_ThumbnailSize = 128; + + std::map m_CachedImages; + + struct ThumbnailInfo + { + std::filesystem::path AbsolutePath; + std::filesystem::path AssetPath; + uint64_t Timestamp; + }; + std::queue m_Queue; + + // TEMP (replace with StarEngine::Serialization) + std::filesystem::path m_ThumbnailCachePath; + }; + +} diff --git a/StarEngine/src/Platform/OpenGL/OpenGLTexture.cpp b/StarEngine/src/Platform/OpenGL/OpenGLTexture.cpp index bfa8d6ff..15e8b580 100644 --- a/StarEngine/src/Platform/OpenGL/OpenGLTexture.cpp +++ b/StarEngine/src/Platform/OpenGL/OpenGLTexture.cpp @@ -1,13 +1,13 @@ #include "sepch.h" #include "Platform/OpenGL/OpenGLTexture.h" -#include +#include "stb_image.h" namespace StarEngine { namespace Utils { - static GLenum HazelImageFormatToGLDataFormat(ImageFormat format) + static GLenum StarEngineImageFormatToGLDataFormat(ImageFormat format) { switch (format) { @@ -19,7 +19,7 @@ namespace StarEngine { return 0; } - static GLenum HazelImageFormatToGLInternalFormat(ImageFormat format) + static GLenum StarEngineImageFormatToGLInternalFormat(ImageFormat format) { switch (format) { @@ -33,13 +33,13 @@ namespace StarEngine { } - OpenGLTexture2D::OpenGLTexture2D(const TextureSpecification& specification) + OpenGLTexture2D::OpenGLTexture2D(const TextureSpecification& specification, Buffer data) : m_Specification(specification), m_Width(m_Specification.Width), m_Height(m_Specification.Height) { SE_PROFILE_FUNCTION(); - m_InternalFormat = Utils::HazelImageFormatToGLInternalFormat(m_Specification.Format); - m_DataFormat = Utils::HazelImageFormatToGLDataFormat(m_Specification.Format); + m_InternalFormat = Utils::StarEngineImageFormatToGLInternalFormat(m_Specification.Format); + m_DataFormat = Utils::StarEngineImageFormatToGLDataFormat(m_Specification.Format); glCreateTextures(GL_TEXTURE_2D, 1, &m_RendererID); glTextureStorage2D(m_RendererID, 1, m_InternalFormat, m_Width, m_Height); @@ -49,74 +49,57 @@ namespace StarEngine { glTextureParameteri(m_RendererID, GL_TEXTURE_WRAP_S, GL_REPEAT); glTextureParameteri(m_RendererID, GL_TEXTURE_WRAP_T, GL_REPEAT); + + if (data) + SetData(data); } - OpenGLTexture2D::OpenGLTexture2D(const std::string& path) - : m_Path(path) + OpenGLTexture2D::~OpenGLTexture2D() { SE_PROFILE_FUNCTION(); - int width, height, channels; - stbi_set_flip_vertically_on_load(1); - stbi_uc* data = nullptr; - { - SE_PROFILE_FUNCTION("stbi_load - OpenGLTexture2D::OpenGLTexture2D(const std::string&)"); - data = stbi_load(path.c_str(), &width, &height, &channels, 0); - } - - if (data) - { - m_IsLoaded = true; - - m_Width = width; - m_Height = height; - - GLenum internalFormat = 0, dataFormat = 0; - if (channels == 4) - { - internalFormat = GL_RGBA8; - dataFormat = GL_RGBA; - } - else if (channels == 3) - { - internalFormat = GL_RGB8; - dataFormat = GL_RGB; - } - - m_InternalFormat = internalFormat; - m_DataFormat = dataFormat; + glDeleteTextures(1, &m_RendererID); + } - SE_CORE_ASSERT(internalFormat & dataFormat, "Format not supported!"); + void OpenGLTexture2D::ChangeSize(uint32_t newWidth, uint32_t newHeight) + { + //Create new texture + uint32_t newTextureID; + glCreateTextures(GL_TEXTURE_2D, 1, &newTextureID); + glTextureStorage2D(newTextureID, 1, m_InternalFormat, newWidth, newHeight); - glCreateTextures(GL_TEXTURE_2D, 1, &m_RendererID); - glTextureStorage2D(m_RendererID, 1, internalFormat, m_Width, m_Height); + glTextureParameteri(newTextureID, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTextureParameteri(newTextureID, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTextureParameteri(m_RendererID, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTextureParameteri(m_RendererID, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTextureParameteri(newTextureID, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTextureParameteri(newTextureID, GL_TEXTURE_WRAP_T, GL_REPEAT); - glTextureParameteri(m_RendererID, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTextureParameteri(m_RendererID, GL_TEXTURE_WRAP_T, GL_REPEAT); + GLuint framebufferRendererIDs[2] = { 0 }; + glGenFramebuffers(2, framebufferRendererIDs); - glTextureSubImage2D(m_RendererID, 0, 0, 0, m_Width, m_Height, dataFormat, GL_UNSIGNED_BYTE, data); + glBindFramebuffer(GL_READ_FRAMEBUFFER, framebufferRendererIDs[0]); + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_RendererID, 0); - stbi_image_free(data); - } - } + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebufferRendererIDs[1]); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, newTextureID, 0); - OpenGLTexture2D::~OpenGLTexture2D() - { - SE_PROFILE_FUNCTION(); + glBlitFramebuffer(0, 0, m_Width, m_Height, 0, 0, newWidth, newHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR); glDeleteTextures(1, &m_RendererID); + glDeleteFramebuffers(2, framebufferRendererIDs); + + m_RendererID = newTextureID; + m_Width = newWidth; + m_Height = newHeight; } - void OpenGLTexture2D::SetData(void* data, uint32_t size) + void OpenGLTexture2D::SetData(Buffer data) { SE_PROFILE_FUNCTION(); uint32_t bpp = m_DataFormat == GL_RGBA ? 4 : 3; - SE_CORE_ASSERT(size == m_Width * m_Height * bpp, "Data must be entire texture!"); - glTextureSubImage2D(m_RendererID, 0, 0, 0, m_Width, m_Height, m_DataFormat, GL_UNSIGNED_BYTE, data); + SE_CORE_ASSERT(data.Size == m_Width * m_Height * bpp, "Data must be entire texture!"); + glTextureSubImage2D(m_RendererID, 0, 0, 0, m_Width, m_Height, m_DataFormat, GL_UNSIGNED_BYTE, data.Data); } void OpenGLTexture2D::Bind(uint32_t slot) const diff --git a/StarEngine/src/Platform/OpenGL/OpenGLTexture.h b/StarEngine/src/Platform/OpenGL/OpenGLTexture.h index 7c744167..160b8781 100644 --- a/StarEngine/src/Platform/OpenGL/OpenGLTexture.h +++ b/StarEngine/src/Platform/OpenGL/OpenGLTexture.h @@ -9,8 +9,7 @@ namespace StarEngine { class OpenGLTexture2D : public Texture2D { public: - OpenGLTexture2D(const TextureSpecification& specification); - OpenGLTexture2D(const std::string& path); + OpenGLTexture2D(const TextureSpecification& specification, Buffer data = Buffer()); virtual ~OpenGLTexture2D(); virtual const TextureSpecification& GetSpecification() const override { return m_Specification; } @@ -19,9 +18,11 @@ namespace StarEngine { virtual uint32_t GetHeight() const override { return m_Height; } virtual uint32_t GetRendererID() const override { return m_RendererID; } - virtual const std::string& GetPath() const override { return m_Path; } + virtual uint64_t GetEstimatedSize() const override { return m_Width * m_Height * 4; } // Assuming RGBA8 format - virtual void SetData(void* data, uint32_t size) override; + virtual void ChangeSize(uint32_t newWidth, uint32_t newHeight) override; + + virtual void SetData(Buffer data) override; virtual void Bind(uint32_t slot = 0) const override; @@ -34,7 +35,6 @@ namespace StarEngine { private: TextureSpecification m_Specification; - std::string m_Path; bool m_IsLoaded = false; uint32_t m_Width, m_Height; uint32_t m_RendererID; diff --git a/StarEngine/src/Platform/Windows/WindowsWindow.cpp b/StarEngine/src/Platform/Windows/WindowsWindow.cpp index bf71d812..9ac4ee61 100644 --- a/StarEngine/src/Platform/Windows/WindowsWindow.cpp +++ b/StarEngine/src/Platform/Windows/WindowsWindow.cpp @@ -171,6 +171,18 @@ namespace StarEngine { MouseMovedEvent event((float)xPos, (float)yPos); data.EventCallback(event); }); + + glfwSetDropCallback(m_Window, [](GLFWwindow* window, int pathCount, const char* paths[]) + { + WindowData& data = *(WindowData*)glfwGetWindowUserPointer(window); + + std::vector filepaths(pathCount); + for (int i = 0; i < pathCount; i++) + filepaths[i] = paths[i]; + + WindowDropEvent event(std::move(filepaths)); + data.EventCallback(event); + }); } void WindowsWindow::Shutdown() diff --git a/StarEngine/src/StarEngine/Asset/Asset.cpp b/StarEngine/src/StarEngine/Asset/Asset.cpp new file mode 100644 index 00000000..4d14699d --- /dev/null +++ b/StarEngine/src/StarEngine/Asset/Asset.cpp @@ -0,0 +1,33 @@ +#include "sepch.h" +#include "Asset.h" + +namespace StarEngine { + + std::string_view AssetTypeToString(AssetType type) + { + switch (type) + { + case AssetType::None: return "None"; + case AssetType::Scene: return "Scene"; + case AssetType::Texture2D: return "Texture2D"; + /*case AssetType::Audio: return "Audio"; + case AssetType::ObjModel: return "ObjModel"; + case AssetType::ScriptFile: return "ScriptFile";*/ + } + + return ""; + } + + AssetType AssetTypeFromString(std::string_view assetType) + { + if (assetType == "None") return AssetType::None; + if (assetType == "Scene") return AssetType::Scene; + if (assetType == "Texture2D") return AssetType::Texture2D; + /*if (assetType == "Audio") return AssetType::Audio; + if (assetType == "ObjModel") return AssetType::ObjModel; + if (assetType == "ScriptFile") return AssetType::ScriptFile;*/ + + return AssetType::None; + } + +} diff --git a/StarEngine/src/StarEngine/Asset/Asset.h b/StarEngine/src/StarEngine/Asset/Asset.h new file mode 100644 index 00000000..77758de0 --- /dev/null +++ b/StarEngine/src/StarEngine/Asset/Asset.h @@ -0,0 +1,32 @@ +#pragma once + +#include "StarEngine/Core/UUID.h" + +#include + +namespace StarEngine { + + using AssetHandle = UUID; + + enum class AssetType : uint16_t + { + None = 0, + Scene, + Texture2D, + /*Audio, + ObjModel, + ScriptFile,*/ + }; + + std::string_view AssetTypeToString(AssetType type); + AssetType AssetTypeFromString(std::string_view assetType); + + class Asset + { + public: + AssetHandle Handle; // Generate handle + + virtual AssetType GetType() const = 0; + }; + +} diff --git a/StarEngine/src/StarEngine/Asset/AssetImporter.cpp b/StarEngine/src/StarEngine/Asset/AssetImporter.cpp new file mode 100644 index 00000000..2d91efbe --- /dev/null +++ b/StarEngine/src/StarEngine/Asset/AssetImporter.cpp @@ -0,0 +1,48 @@ +#include "sepch.h" +#include "AssetImporter.h" + +#include "TextureImporter.h" +#include "SceneImporter.h" +/* +#include "FontImporter.h" +#include "AudioImporter.h" +#include "ObjModelImporter.h"*/ + +#include + +namespace StarEngine { + + using AssetImportFunction = std::function(AssetHandle, const AssetMetadata&)>; + static std::map s_AssetImportFunctions = { + { AssetType::Scene, SceneImporter::ImportScene }, + { AssetType::Texture2D, TextureImporter::ImportTexture2D },/* + { AssetType::Audio, AudioImporter::ImportAudio }, + { AssetType::ObjModel, ObjModelImporter::ImportObjModel }, + { AssetType::ScriptFile, SceneImporter::ImportScript }*/ + }; + + Ref AssetImporter::ImportAsset(AssetHandle handle, const AssetMetadata& metadata) + { + SE_PROFILE_FUNCTION_COLOR("AssetImporter::ImportAsset", 0xF2FA8A); + + { + SE_PROFILE_SCOPE_COLOR("AssetImporter::ImportAsset Scope", 0x27628A); + + if (s_AssetImportFunctions.find(metadata.Type) == s_AssetImportFunctions.end()) + { + SE_CORE_ERROR("No importer available for asset type: {}", (uint16_t)metadata.Type); + return nullptr; + } + } + + auto& result = s_AssetImportFunctions.at(metadata.Type);//(metadata.Type)(handle, metadata); + + { + SE_PROFILE_SCOPE_COLOR("AssetImporter::ImportAsset 2 Scope", 0xD1C48A); + + return result(handle, metadata); + } + + } + +} diff --git a/StarEngine/src/StarEngine/Asset/AssetImporter.h b/StarEngine/src/StarEngine/Asset/AssetImporter.h new file mode 100644 index 00000000..da037819 --- /dev/null +++ b/StarEngine/src/StarEngine/Asset/AssetImporter.h @@ -0,0 +1,14 @@ +#pragma once + +#include "AssetMetadata.h" + +namespace StarEngine +{ + class AssetImporter + { + public: + static Ref ImportAsset(AssetHandle handle, const AssetMetadata& metadata); + }; + + +} diff --git a/StarEngine/src/StarEngine/Asset/AssetManager.cpp b/StarEngine/src/StarEngine/Asset/AssetManager.cpp new file mode 100644 index 00000000..15c19c38 --- /dev/null +++ b/StarEngine/src/StarEngine/Asset/AssetManager.cpp @@ -0,0 +1,7 @@ +#include "sepch.h" +#include "AssetManager.h" + +namespace StarEngine +{ + +} diff --git a/StarEngine/src/StarEngine/Asset/AssetManager.h b/StarEngine/src/StarEngine/Asset/AssetManager.h new file mode 100644 index 00000000..9c8b24fa --- /dev/null +++ b/StarEngine/src/StarEngine/Asset/AssetManager.h @@ -0,0 +1,36 @@ +#pragma once + +#include "AssetManagerBase.h" + +#include "StarEngine/Project/Project.h" + +namespace StarEngine +{ + class AssetManager + { + public: + template + static Ref GetAsset(AssetHandle handle) + { + //SE_PROFILE_FUNCTION_COLOR("AssetManager::GetAsset", 0x8CCBFF); + + Ref asset = Project::GetActive()->GetAssetManager()->GetAsset(handle); + return std::static_pointer_cast(asset); + } + + static bool IsAssetHandleValid(AssetHandle handle) + { + return Project::GetActive()->GetAssetManager()->IsAssetHandleValid(handle); + } + + static bool IsAssetLoaded(AssetHandle handle) + { + return Project::GetActive()->GetAssetManager()->IsAssetLoaded(handle); + } + + static AssetType GetAssetType(AssetHandle handle) + { + return Project::GetActive()->GetAssetManager()->GetAssetType(handle); + } + }; +} diff --git a/StarEngine/src/StarEngine/Asset/AssetManagerBase.cpp b/StarEngine/src/StarEngine/Asset/AssetManagerBase.cpp new file mode 100644 index 00000000..15c19c38 --- /dev/null +++ b/StarEngine/src/StarEngine/Asset/AssetManagerBase.cpp @@ -0,0 +1,7 @@ +#include "sepch.h" +#include "AssetManager.h" + +namespace StarEngine +{ + +} diff --git a/StarEngine/src/StarEngine/Asset/AssetManagerBase.h b/StarEngine/src/StarEngine/Asset/AssetManagerBase.h new file mode 100644 index 00000000..37d774aa --- /dev/null +++ b/StarEngine/src/StarEngine/Asset/AssetManagerBase.h @@ -0,0 +1,20 @@ +#pragma once + +#include "Asset.h" + +#include + +namespace StarEngine +{ + using AssetMap = std::map>; + + class AssetManagerBase + { + public: + virtual Ref GetAsset(AssetHandle handle) = 0; + + virtual bool IsAssetHandleValid(AssetHandle handle) const = 0; + virtual bool IsAssetLoaded(AssetHandle handle) const = 0; + virtual AssetType GetAssetType(AssetHandle handle) const = 0; + }; +} diff --git a/StarEngine/src/StarEngine/Asset/AssetMetadata.h b/StarEngine/src/StarEngine/Asset/AssetMetadata.h new file mode 100644 index 00000000..d48ec6bc --- /dev/null +++ b/StarEngine/src/StarEngine/Asset/AssetMetadata.h @@ -0,0 +1,17 @@ +#pragma once + +#include "Asset.h" + +#include + +namespace StarEngine +{ + struct AssetMetadata + { + AssetType Type = AssetType::None; + std::filesystem::path FilePath = ""; + + operator bool() const { return Type != AssetType::None; } + }; + +} diff --git a/StarEngine/src/StarEngine/Asset/EditorAssetManager.cpp b/StarEngine/src/StarEngine/Asset/EditorAssetManager.cpp new file mode 100644 index 00000000..0ac128d4 --- /dev/null +++ b/StarEngine/src/StarEngine/Asset/EditorAssetManager.cpp @@ -0,0 +1,214 @@ +#include "sepch.h" +#include "AssetManager.h" + +#include "AssetImporter.h" + +#include +#include + +#include +#include + +namespace fmt { + template <> + struct formatter : formatter { + auto format(const std::filesystem::path& path, format_context& ctx) const { + return formatter::format(path.string(), ctx); + } + }; +} // namespace fmt + +namespace StarEngine { + + static std::map s_AssetExtensionMap = { + { ".starscene", AssetType::Scene }, + { ".hazel", AssetType::Scene }, + { ".png", AssetType::Texture2D }, + { ".jpg", AssetType::Texture2D }, + { ".jpeg", AssetType::Texture2D },/* + { ".mp3", AssetType::Audio }, + { ".wav", AssetType::Audio }, + { ".ogg", AssetType::Audio }, + { ".obj", AssetType::ObjModel }, + { ".cs", AssetType::ScriptFile },*/ + }; + + static AssetType GetAssetTypeFromFileExtension(const std::filesystem::path& extension) + { + if (s_AssetExtensionMap.find(extension) == s_AssetExtensionMap.end()) + { + SE_CORE_WARN("Could not find AssetType for {}", extension); + return AssetType::None; + } + + return s_AssetExtensionMap.at(extension); + } + + YAML::Emitter& operator<<(YAML::Emitter& out, const std::string_view& v) + { + out << std::string(v.data(), v.size()); + return out; + } + + bool EditorAssetManager::IsAssetHandleValid(AssetHandle handle) const + { + return handle != 0 && m_AssetRegistry.find(handle) != m_AssetRegistry.end(); + } + + bool EditorAssetManager::IsAssetLoaded(AssetHandle handle) const + { + return m_LoadedAssets.find(handle) != m_LoadedAssets.end(); + } + + AssetType EditorAssetManager::GetAssetType(AssetHandle handle) const + { + if (!IsAssetHandleValid(handle)) + return AssetType::None; + + return m_AssetRegistry.at(handle).Type; + } + + void EditorAssetManager::ImportAsset(const std::filesystem::path& filepath) + { + AssetHandle handle; // generate new handle + AssetMetadata metadata; + metadata.FilePath = filepath; + metadata.Type = GetAssetTypeFromFileExtension(filepath.extension()); + SE_CORE_ASSERT(metadata.Type != AssetType::None); + Ref asset = AssetImporter::ImportAsset(handle, metadata); + if (asset) + { + asset->Handle = handle; + m_LoadedAssets[handle] = asset; + m_AssetRegistry[handle] = metadata; + SerializeAssetRegistry(); + } + } + + void EditorAssetManager::ImportScriptAsset(const std::filesystem::path& filepath, uint64_t uuid) + { + AssetHandle handle = uuid; // generate new handle + AssetMetadata metadata; + metadata.FilePath = filepath; + metadata.Type = GetAssetTypeFromFileExtension(filepath.extension()); + SE_CORE_ASSERT(metadata.Type != AssetType::None); + Ref asset = AssetImporter::ImportAsset(handle, metadata); + if (asset) + { + asset->Handle = handle; + m_LoadedAssets[handle] = asset; + m_AssetRegistry[handle] = metadata; + SerializeAssetRegistry(); + } + } + + const AssetMetadata& EditorAssetManager::GetMetadata(AssetHandle handle) const + { + SE_PROFILE_FUNCTION_COLOR("EditorAssetManager::GetMetadata", 0xF2A58A); + + static AssetMetadata s_NullMetadata; + auto it = m_AssetRegistry.find(handle); + if (it == m_AssetRegistry.end()) + return s_NullMetadata; + + return it->second; + } + + const std::filesystem::path& EditorAssetManager::GetFilePath(AssetHandle handle) const + { + return GetMetadata(handle).FilePath; + } + + Ref EditorAssetManager::GetAsset(AssetHandle handle) + { + SE_PROFILE_FUNCTION_COLOR("EditorAssetManager::GetAsset", 0xA3FFA4); + + // 1. check if handle is valid + if (!IsAssetHandleValid(handle)) + return nullptr; + + // 2. check if asset needs load (and if so, load) + Ref asset; + if (IsAssetLoaded(handle)) + { + SE_PROFILE_SCOPE_COLOR("EditorAssetManager::GetAsset Scope", 0xFF7200); + + asset = m_LoadedAssets.at(handle); + } + else + { + //SE_PROFILE_SCOPE_COLOR("EditorAssetManager::GetAsset 2 Scope", 0xA331F3); + + // load asset + const AssetMetadata& metadata = GetMetadata(handle); + asset = AssetImporter::ImportAsset(handle, metadata); + if (!asset) + { + // import failed + SE_CORE_ERROR("EditorAssetManager::GetAsset - asset import failed!"); + } + + m_LoadedAssets[handle] = asset; + } + + // 3. return asset + return asset; + } + + void EditorAssetManager::SerializeAssetRegistry() + { + auto path = Project::GetActiveAssetRegistryPath(); + + YAML::Emitter out; + { + out << YAML::BeginMap; // Root + out << YAML::Key << "AssetRegistry" << YAML::Value; + + out << YAML::BeginSeq; + for (const auto& [handle, metadata] : m_AssetRegistry) + { + out << YAML::BeginMap; + out << YAML::Key << "Handle" << YAML::Value << handle; + std::string filepathStr = metadata.FilePath.generic_string(); + out << YAML::Key << "FilePath" << YAML::Value << filepathStr; + out << YAML::Key << "Type" << YAML::Value << AssetTypeToString(metadata.Type); + out << YAML::EndMap; + } + out << YAML::EndSeq; + out << YAML::EndMap; // Root + } + + std::ofstream fout(path); + fout << out.c_str(); + + } + + bool EditorAssetManager::DeserializeAssetRegistry() + { + auto path = Project::GetActiveAssetRegistryPath(); + YAML::Node data; + try + { + data = YAML::LoadFile(path.string()); + } + catch (YAML::ParserException e) + { + SE_CORE_ERROR("Failed to load project file '{0}'\n {1}", path, e.what()); + return false; + } + + auto rootNode = data["AssetRegistry"]; + if (!rootNode) + return false; + + for (const auto& node : rootNode) + { + AssetHandle handle = node["Handle"].as(); + auto& metadata = m_AssetRegistry[handle]; + metadata.FilePath = node["FilePath"].as(); + metadata.Type = AssetTypeFromString(node["Type"].as()); + } + + return true; + } +} diff --git a/StarEngine/src/StarEngine/Asset/EditorAssetManager.h b/StarEngine/src/StarEngine/Asset/EditorAssetManager.h new file mode 100644 index 00000000..955c1cb6 --- /dev/null +++ b/StarEngine/src/StarEngine/Asset/EditorAssetManager.h @@ -0,0 +1,38 @@ +#pragma once + +#include "AssetManagerBase.h" +#include "AssetMetadata.h" + +#include + +namespace StarEngine { + + using AssetRegistry = std::map; + + class EditorAssetManager : public AssetManagerBase + { + public: + virtual Ref GetAsset(AssetHandle handle) override; + + virtual bool IsAssetHandleValid(AssetHandle handle) const override; + virtual bool IsAssetLoaded(AssetHandle handle) const override; + virtual AssetType GetAssetType(AssetHandle handle) const override; + + void ImportAsset(const std::filesystem::path& filepath); + void ImportScriptAsset(const std::filesystem::path& filepath, uint64_t uuid); + + const AssetMetadata& GetMetadata(AssetHandle handle) const; + const std::filesystem::path& GetFilePath(AssetHandle handle) const; + + const AssetRegistry& GetAssetRegistry() const { return m_AssetRegistry; } + + void SerializeAssetRegistry(); + bool DeserializeAssetRegistry(); + + private: + AssetRegistry m_AssetRegistry; + AssetMap m_LoadedAssets; + + // TODO: memory-only assets + }; +} diff --git a/StarEngine/src/StarEngine/Asset/RuntimeAssetManager.cpp b/StarEngine/src/StarEngine/Asset/RuntimeAssetManager.cpp new file mode 100644 index 00000000..a7248558 --- /dev/null +++ b/StarEngine/src/StarEngine/Asset/RuntimeAssetManager.cpp @@ -0,0 +1,7 @@ +#include "sepch.h" +#include "AssetManager.h" + +namespace StarEngine { + + +} diff --git a/StarEngine/src/StarEngine/Asset/RuntimeAssetManager.h b/StarEngine/src/StarEngine/Asset/RuntimeAssetManager.h new file mode 100644 index 00000000..e0046203 --- /dev/null +++ b/StarEngine/src/StarEngine/Asset/RuntimeAssetManager.h @@ -0,0 +1,11 @@ +#pragma once + +#include "AssetManagerBase.h" + +namespace StarEngine { + + class RuntimeAssetManager : public AssetManagerBase + { + public: + }; +} diff --git a/StarEngine/src/StarEngine/Asset/SceneImporter.cpp b/StarEngine/src/StarEngine/Asset/SceneImporter.cpp new file mode 100644 index 00000000..6881609d --- /dev/null +++ b/StarEngine/src/StarEngine/Asset/SceneImporter.cpp @@ -0,0 +1,35 @@ +#include "sepch.h" +#include "SceneImporter.h" + +#include "StarEngine/Project/Project.h" +#include "StarEngine/Scene/SceneSerializer.h" +#include "StarEngine/Scripting/ScriptEngine.h" + +#include + +namespace StarEngine { + + Ref SceneImporter::ImportScene(AssetHandle handle, const AssetMetadata& metadata) + { + //SE_PROFILE_FUNCTION(); + + return LoadScene(Project::GetActiveAssetDirectory() / metadata.FilePath); + } + + Ref SceneImporter::LoadScene(const std::filesystem::path& path) + { + //SE_PROFILE_FUNCTION(); + + Ref scene = CreateRef(); + SceneSerializer serializer(scene); + serializer.Deserialize(path); + + return scene; + } + + void SceneImporter::SaveScene(Ref scene, const std::filesystem::path& path) + { + SceneSerializer serializer(scene); + serializer.Serialize(Project::GetActiveAssetDirectory() / path); + } +} diff --git a/StarEngine/src/StarEngine/Asset/SceneImporter.h b/StarEngine/src/StarEngine/Asset/SceneImporter.h new file mode 100644 index 00000000..e3bcbb23 --- /dev/null +++ b/StarEngine/src/StarEngine/Asset/SceneImporter.h @@ -0,0 +1,22 @@ +#pragma once + +#include "Asset.h" +#include "AssetMetadata.h" + +#include "StarEngine/Scene/Scene.h" + +namespace StarEngine { + + class SceneImporter + { + public: + // AssetMetadata filepath is relative to project asset directory + static Ref ImportScene(AssetHandle handle, const AssetMetadata& metadata); + + // Load from filepath + static Ref LoadScene(const std::filesystem::path& path); + + static void SaveScene(Ref scene, const std::filesystem::path& path); + }; + +} diff --git a/StarEngine/src/StarEngine/Asset/TextureImporter.cpp b/StarEngine/src/StarEngine/Asset/TextureImporter.cpp new file mode 100644 index 00000000..9e32f3e9 --- /dev/null +++ b/StarEngine/src/StarEngine/Asset/TextureImporter.cpp @@ -0,0 +1,57 @@ +#include "sepch.h" +#include "TextureImporter.h" + +#include "StarEngine/Project/Project.h" + +#include + +namespace StarEngine { + + Ref TextureImporter::ImportTexture2D(AssetHandle handle, const AssetMetadata& metadata) + { + //SE_PROFILE_FUNCTION(); + + return LoadTexture2D(Project::GetActiveAssetDirectory() / metadata.FilePath); + } + + Ref TextureImporter::LoadTexture2D(const std::filesystem::path& path) + { + //SE_PROFILE_FUNCTION(); + int width, height, channels; + stbi_set_flip_vertically_on_load(1); + Buffer data; + + { + //SE_PROFILE_SCOPE("stbi_load - TextureImporter::ImportTexture2D"); + std::string pathStr = path.string(); + data.Data = stbi_load(pathStr.c_str(), &width, &height, &channels, 4); + channels = 4; + } + + if (data.Data == nullptr) + { + SE_CORE_ERROR("TextureImporter::ImportTexture2D - Could not load texture from filepath: {}", path.string()); + return nullptr; + } + + // TODO: think about this + data.Size = width * height * channels; + + TextureSpecification spec; + spec.Width = width; + spec.Height = height; + switch (channels) + { + case 3: + spec.Format = ImageFormat::RGB8; + break; + case 4: + spec.Format = ImageFormat::RGBA8; + break; + } + + Ref texture = Texture2D::Create(spec, data); + data.Release(); + return texture; + } +} diff --git a/StarEngine/src/StarEngine/Asset/TextureImporter.h b/StarEngine/src/StarEngine/Asset/TextureImporter.h new file mode 100644 index 00000000..82f5099c --- /dev/null +++ b/StarEngine/src/StarEngine/Asset/TextureImporter.h @@ -0,0 +1,20 @@ +#pragma once + +#include "Asset.h" +#include "AssetMetadata.h" + +#include "StarEngine/Renderer/Texture.h" + +namespace StarEngine { + + class TextureImporter + { + public: + // AssetMetadata filepath is relative to project asset directory + static Ref ImportTexture2D(AssetHandle handle, const AssetMetadata& metadata); + + // Reads file directly from filesystem + // (i.e. path has to be relative / absolute to working directory) + static Ref LoadTexture2D(const std::filesystem::path& path); + }; +} diff --git a/StarEngine/src/StarEngine/Core/Assert.h b/StarEngine/src/StarEngine/Core/Assert.h index 28b8faf2..b6bceb62 100644 --- a/StarEngine/src/StarEngine/Core/Assert.h +++ b/StarEngine/src/StarEngine/Core/Assert.h @@ -2,7 +2,6 @@ #include "StarEngine/Core/Base.h" #include "StarEngine/Core/Log.h" - #include #ifdef SE_ENABLE_ASSERTS @@ -23,3 +22,22 @@ #define SE_ASSERT(...) #define SE_CORE_ASSERT(...) #endif + +#ifdef SE_ENABLE_VERIFY + +// Alteratively we could use the same "default" message for both "WITH_MSG" and "NO_MSG" and +// provide support for custom formatting by concatenating the formatting string instead of having the format inside the default message +#define SE_INTERNAL_VERIFY_IMPL(type, check, msg, ...) { if(!(check)) { SE##type##ERROR(msg, __VA_ARGS__); SE_DEBUGBREAK(); } } +#define SE_INTERNAL_VERIFY_WITH_MSG(type, check, ...) SE_INTERNAL_VERIFY_IMPL(type, check, "Assertion failed: {0}", __VA_ARGS__) +#define SE_INTERNAL_VERIFY_NO_MSG(type, check) SE_INTERNAL_VERIFY_IMPL(type, check, "Assertion '{0}' failed at {1}:{2}", SE_STRINGIFY_MACRO(check), std::filesystem::path(__FILE__).filename().string(), __LINE__) + +#define SE_INTERNAL_VERIFY_GET_MACRO_NAME(arg1, arg2, macro, ...) macro +#define SE_INTERNAL_VERIFY_GET_MACRO(...) SE_EXPAND_MACRO( SE_INTERNAL_VERIFY_GET_MACRO_NAME(__VA_ARGS__, SE_INTERNAL_VERIFY_WITH_MSG, SE_INTERNAL_VERIFY_NO_MSG) ) + +// Currently accepts at least the condition and one additional parameter (the message) being optional +#define SE_VERIFY(...) SE_EXPAND_MACRO( SE_INTERNAL_VERIFY_GET_MACRO(__VA_ARGS__)(_, __VA_ARGS__) ) +#define SE_CORE_VERIFY(...) SE_EXPAND_MACRO( SE_INTERNAL_VERIFY_GET_MACRO(__VA_ARGS__)(_CORE_, __VA_ARGS__) ) +#else +#define SE_VERIFY(...) +#define SE_CORE_VERIFY(...) +#endif diff --git a/StarEngine/src/StarEngine/Core/Base.h b/StarEngine/src/StarEngine/Core/Base.h index ed57ec30..f47f2478 100644 --- a/StarEngine/src/StarEngine/Core/Base.h +++ b/StarEngine/src/StarEngine/Core/Base.h @@ -1,11 +1,9 @@ #pragma once -#include - #include "StarEngine/Core/PlatformDetection.h" +#include -#ifdef SE_DEBUG #if defined(SE_PLATFORM_WINDOWS) #define SE_DEBUGBREAK() __debugbreak() #elif defined(SE_PLATFORM_LINUX) @@ -14,9 +12,13 @@ #else #error "Platform doesn't support debugbreak yet!" #endif + +#ifdef SE_DEBUG #define SE_ENABLE_ASSERTS -#else -#define SE_DEBUGBREAK() +#endif + +#ifndef SE_DIST +#define SE_ENABLE_VERIFY #endif #define SE_EXPAND_MACRO(x) x @@ -46,6 +48,5 @@ namespace StarEngine { } - #include "StarEngine/Core/Log.h" #include "StarEngine/Core/Assert.h" diff --git a/StarEngine/src/StarEngine/Core/Buffer.h b/StarEngine/src/StarEngine/Core/Buffer.h index 21b39557..9cccb7c5 100644 --- a/StarEngine/src/StarEngine/Core/Buffer.h +++ b/StarEngine/src/StarEngine/Core/Buffer.h @@ -17,6 +17,11 @@ namespace StarEngine { Allocate(size); } + Buffer(const void* data, uint64_t size) + : Data((uint8_t*)data), Size(size) + { + } + Buffer(const Buffer&) = default; static Buffer Copy(Buffer other) @@ -30,13 +35,13 @@ namespace StarEngine { { Release(); - Data = new uint8_t[size]; + Data = (uint8_t*)malloc(size); Size = size; } void Release() { - delete[] Data; + free(Data); Data = nullptr; Size = 0; } diff --git a/StarEngine/src/StarEngine/Debug/Instrumentor.h b/StarEngine/src/StarEngine/Debug/Instrumentor.h index 295c8b54..1a9f00ce 100644 --- a/StarEngine/src/StarEngine/Debug/Instrumentor.h +++ b/StarEngine/src/StarEngine/Debug/Instrumentor.h @@ -234,10 +234,14 @@ namespace StarEngine { ::StarEngine::InstrumentationTimer timer##line(fixedName##line.Data) #define SE_PROFILE_SCOPE_LINE(name, line) SE_PROFILE_SCOPE_LINE2(name, line) #define SE_PROFILE_SCOPE(name) SE_PROFILE_SCOPE_LINE(name, __LINE__) +#define SE_PROFILE_SCOPE_COLOR(name, ...) SE_PROFILE_FUNCTION_COLOR(name, __VA_ARGS__) #define SE_PROFILE_FUNCTION() SE_PROFILE_SCOPE(SS_FUNC_SIG) +#define SE_PROFILE_FUNCTION_COLOR(name, ...) ZoneScopedNC(name, __VA_ARGS__) // Color is in hexadecimal #else #define SE_PROFILE_BEGIN_SESSION(name, filepath) #define SE_PROFILE_END_SESSION() #define SE_PROFILE_SCOPE(name) +#define SE_PROFILE_SCOPE_COLOR(name, ...) #define SE_PROFILE_FUNCTION() +#define SE_PROFILE_FUNCTION_COLOR(name, ...) #endif diff --git a/StarEngine/src/StarEngine/Events/ApplicationEvent.h b/StarEngine/src/StarEngine/Events/ApplicationEvent.h index b19819df..bf081459 100644 --- a/StarEngine/src/StarEngine/Events/ApplicationEvent.h +++ b/StarEngine/src/StarEngine/Events/ApplicationEvent.h @@ -62,4 +62,23 @@ namespace StarEngine { EVENT_CLASS_TYPE(AppRender) EVENT_CLASS_CATEGORY(EventCategoryApplication) }; -} \ No newline at end of file + + class WindowDropEvent : public Event + { + public: + WindowDropEvent(const std::vector& paths) + : m_Paths(paths) { + } + + WindowDropEvent(std::vector&& paths) + : m_Paths(std::move(paths)) { + } + + const std::vector& GetPaths() const { return m_Paths; } + + EVENT_CLASS_TYPE(WindowDrop) + EVENT_CLASS_CATEGORY(EventCategoryApplication) + private: + std::vector m_Paths; + }; +} diff --git a/StarEngine/src/StarEngine/Events/Event.h b/StarEngine/src/StarEngine/Events/Event.h index 846ac5df..3541817f 100644 --- a/StarEngine/src/StarEngine/Events/Event.h +++ b/StarEngine/src/StarEngine/Events/Event.h @@ -15,7 +15,7 @@ namespace StarEngine { enum class EventType { None = 0, - WindowClose, WindowResize, WindowFocus, WindowLostFocus, WindowMoved, + WindowClose, WindowResize, WindowFocus, WindowLostFocus, WindowMoved, WindowDrop, AppTick, AppUpdate, AppRender, KeyPressed, KeyReleased, KeyTyped, MouseButtonPressed, MouseButtonReleased, MouseMoved, MouseScrolled diff --git a/StarEngine/src/StarEngine/Project/Project.cpp b/StarEngine/src/StarEngine/Project/Project.cpp index 8334dee4..46f77161 100644 --- a/StarEngine/src/StarEngine/Project/Project.cpp +++ b/StarEngine/src/StarEngine/Project/Project.cpp @@ -5,6 +5,11 @@ namespace StarEngine { + std::filesystem::path Project::GetAssetAbsolutePath(const std::filesystem::path& path) + { + return GetAssetDirectory() / path; + } + Ref Project::New() { s_ActiveProject = CreateRef(); @@ -20,6 +25,9 @@ namespace StarEngine { { project->m_ProjectDirectory = path.parent_path(); s_ActiveProject = project; + Ref editorAssetManager = std::make_shared(); + s_ActiveProject->m_AssetManager = editorAssetManager; + editorAssetManager->DeserializeAssetRegistry(); return s_ActiveProject; } diff --git a/StarEngine/src/StarEngine/Project/Project.h b/StarEngine/src/StarEngine/Project/Project.h index 0fbbb6c8..40892961 100644 --- a/StarEngine/src/StarEngine/Project/Project.h +++ b/StarEngine/src/StarEngine/Project/Project.h @@ -5,43 +5,65 @@ #include "StarEngine/Core/Base.h" +#include "StarEngine/Asset/RuntimeAssetManager.h" +#include "StarEngine/Asset/EditorAssetManager.h" + namespace StarEngine { struct ProjectConfig { std::string Name = "Untitled"; - std::filesystem::path StartScene; + AssetHandle StartScene; std::filesystem::path AssetDirectory; + std::filesystem::path AssetRegistryPath; // Relative to AssetDirectory std::filesystem::path ScriptModulePath; }; class Project { public: - static const std::filesystem::path& GetProjectDirectory() + const std::filesystem::path& GetProjectDirectory() { return m_ProjectDirectory; } + std::filesystem::path GetAssetDirectory() { return GetProjectDirectory() / s_ActiveProject->m_Config.AssetDirectory; } + std::filesystem::path GetAssetRegistryPath() { return GetAssetDirectory() / s_ActiveProject->m_Config.AssetRegistryPath; } + // TODO: move to asset manager when we have one + std::filesystem::path GetAssetFileSystemPath(const std::filesystem::path& path) { return GetAssetDirectory() / path; } + + std::filesystem::path GetAssetAbsolutePath(const std::filesystem::path& path); + + static const std::filesystem::path& GetActiveProjectDirectory() { SE_CORE_ASSERT(s_ActiveProject); - return s_ActiveProject->m_ProjectDirectory; + return s_ActiveProject->GetProjectDirectory(); } - static std::filesystem::path GetAssetDirectory() + static std::filesystem::path GetActiveAssetDirectory() { SE_CORE_ASSERT(s_ActiveProject); - return GetProjectDirectory() / s_ActiveProject->m_Config.AssetDirectory; + return s_ActiveProject->GetAssetDirectory(); + } + + static std::filesystem::path GetActiveAssetRegistryPath() + { + SE_CORE_ASSERT(s_ActiveProject); + return s_ActiveProject->GetAssetRegistryPath(); } // TODO: move to asset manager when we have one - static std::filesystem::path GetAssetFileSystemPath(const std::filesystem::path& path) + static std::filesystem::path GetActiveAssetFileSystemPath(const std::filesystem::path& path) { SE_CORE_ASSERT(s_ActiveProject); - return GetAssetDirectory() / path; + return s_ActiveProject->GetAssetFileSystemPath(path); } + ProjectConfig& GetConfig() { return m_Config; } static Ref GetActive() { return s_ActiveProject; } + std::shared_ptr GetAssetManager() { return m_AssetManager; } + std::shared_ptr GetRuntimeAssetManager() { return std::static_pointer_cast(m_AssetManager); } + std::shared_ptr GetEditorAssetManager() { return std::static_pointer_cast(m_AssetManager); } static Ref New(); static Ref Load(const std::filesystem::path& path); @@ -49,6 +71,7 @@ namespace StarEngine { private: ProjectConfig m_Config; std::filesystem::path m_ProjectDirectory; + std::shared_ptr m_AssetManager; inline static Ref s_ActiveProject; }; diff --git a/StarEngine/src/StarEngine/Project/ProjectSerializer.cpp b/StarEngine/src/StarEngine/Project/ProjectSerializer.cpp index 64a597cc..65807bb9 100644 --- a/StarEngine/src/StarEngine/Project/ProjectSerializer.cpp +++ b/StarEngine/src/StarEngine/Project/ProjectSerializer.cpp @@ -4,16 +4,6 @@ #include #include -namespace fmt { - template <> - struct formatter : formatter { - template - auto format(const std::filesystem::path& path, FormatContext& ctx) const { - return formatter::format(path.string(), ctx); - } - }; -} // namespace fmt - namespace StarEngine { ProjectSerializer::ProjectSerializer(Ref project) @@ -32,8 +22,9 @@ namespace StarEngine { { out << YAML::BeginMap;// Project out << YAML::Key << "Name" << YAML::Value << config.Name; - out << YAML::Key << "StartScene" << YAML::Value << config.StartScene.string(); + out << YAML::Key << "StartScene" << YAML::Value << (uint64_t)config.StartScene; out << YAML::Key << "AssetDirectory" << YAML::Value << config.AssetDirectory.string(); + out << YAML::Key << "AssetRegistryPath" << YAML::Value << config.AssetRegistryPath.string(); out << YAML::Key << "ScriptModulePath" << YAML::Value << config.ScriptModulePath.string(); out << YAML::EndMap; // Project } @@ -57,7 +48,8 @@ namespace StarEngine { } catch (YAML::ParserException e) { - SE_CORE_ERROR("Failed to load project file '{0}'\n {1}", filepath, e.what()); + SE_CORE_ERROR("Failed to load project file '{0}'\n {1}", filepath.string(), e.what()); + return false; } @@ -66,8 +58,10 @@ namespace StarEngine { return false; config.Name = projectNode["Name"].as(); - config.StartScene = projectNode["StartScene"].as(); + config.StartScene = projectNode["StartScene"].as(); config.AssetDirectory = projectNode["AssetDirectory"].as(); + if (projectNode["AssetRegistryPath"]) + config.AssetRegistryPath = projectNode["AssetRegistryPath"].as(); config.ScriptModulePath = projectNode["ScriptModulePath"].as(); return true; } diff --git a/StarEngine/src/StarEngine/Project/ProjectSerializer.h b/StarEngine/src/StarEngine/Project/ProjectSerializer.h index 093e8754..88d89c95 100644 --- a/StarEngine/src/StarEngine/Project/ProjectSerializer.h +++ b/StarEngine/src/StarEngine/Project/ProjectSerializer.h @@ -2,8 +2,8 @@ #include "Project.h" -namespace StarEngine -{ +namespace StarEngine { + class ProjectSerializer { public: diff --git a/StarEngine/src/StarEngine/Renderer/Font.cpp b/StarEngine/src/StarEngine/Renderer/Font.cpp index 707e0223..7f872387 100644 --- a/StarEngine/src/StarEngine/Renderer/Font.cpp +++ b/StarEngine/src/StarEngine/Renderer/Font.cpp @@ -32,7 +32,7 @@ namespace StarEngine { spec.GenerateMips = false; Ref texture = Texture2D::Create(spec); - texture->SetData((void*)bitmap.pixels, bitmap.width * bitmap.height * 3); + texture->SetData(Buffer((void*)bitmap.pixels, bitmap.width * bitmap.height * 3)); return texture; } diff --git a/StarEngine/src/StarEngine/Renderer/Renderer2D.cpp b/StarEngine/src/StarEngine/Renderer/Renderer2D.cpp index 332f5736..3ac0d848 100644 --- a/StarEngine/src/StarEngine/Renderer/Renderer2D.cpp +++ b/StarEngine/src/StarEngine/Renderer/Renderer2D.cpp @@ -5,6 +5,8 @@ #include "StarEngine/Renderer/UniformBuffer.h" #include "StarEngine/Renderer/RenderCommand.h" +#include "StarEngine/Asset/AssetManager.h" + #include #include @@ -201,7 +203,7 @@ namespace StarEngine { s_Data.WhiteTexture = Texture2D::Create(TextureSpecification()); uint32_t whiteTextureData = 0xffffffff; - s_Data.WhiteTexture->SetData(&whiteTextureData, sizeof(uint32_t)); + s_Data.WhiteTexture->SetData(Buffer(&whiteTextureData, sizeof(uint32_t))); int32_t samplers[s_Data.MaxTextureSlots]; for (uint32_t i = 0; i < s_Data.MaxTextureSlots; i++) @@ -404,6 +406,7 @@ namespace StarEngine { void Renderer2D::DrawQuad(const glm::mat4& transform, const Ref& texture, float tilingFactor, const glm::vec4& tintColor, int entityID) { SE_PROFILE_FUNCTION(); + SE_CORE_VERIFY(texture); constexpr size_t quadVertexCount = 4; constexpr glm::vec2 textureCoords[] = { { 0.0f, 0.0f }, { 1.0f, 0.0f }, { 1.0f, 1.0f }, { 0.0f, 1.0f } }; @@ -549,7 +552,8 @@ namespace StarEngine { { if (src.Texture) { - DrawQuad(transform, src.Texture, src.TilingFactor, src.Color, entityID); + Ref texture = AssetManager::GetAsset(src.Texture); + DrawQuad(transform, texture, src.TilingFactor, src.Color, entityID); } else { diff --git a/StarEngine/src/StarEngine/Renderer/Texture.cpp b/StarEngine/src/StarEngine/Renderer/Texture.cpp index 1efc3a2f..54823339 100644 --- a/StarEngine/src/StarEngine/Renderer/Texture.cpp +++ b/StarEngine/src/StarEngine/Renderer/Texture.cpp @@ -6,24 +6,12 @@ namespace StarEngine { - Ref Texture2D::Create(const TextureSpecification& specification) + Ref Texture2D::Create(const TextureSpecification& specification, Buffer data) { switch (Renderer::GetAPI()) { case RendererAPI::API::None: SE_CORE_ASSERT(false, "RendererAPI::None is currently not supported!"); return nullptr; - case RendererAPI::API::OpenGL: return CreateRef(specification); - } - - SE_CORE_ASSERT(false, "Unknown RendererAPI!"); - return nullptr; - } - - Ref Texture2D::Create(const std::string& path) - { - switch (Renderer::GetAPI()) - { - case RendererAPI::API::None: SE_CORE_ASSERT(false, "RendererAPI::None is currently not supported!"); return nullptr; - case RendererAPI::API::OpenGL: return CreateRef(path); + case RendererAPI::API::OpenGL: return CreateRef(specification, data); } SE_CORE_ASSERT(false, "Unknown RendererAPI!"); diff --git a/StarEngine/src/StarEngine/Renderer/Texture.h b/StarEngine/src/StarEngine/Renderer/Texture.h index 08676ff4..6ed48fa8 100644 --- a/StarEngine/src/StarEngine/Renderer/Texture.h +++ b/StarEngine/src/StarEngine/Renderer/Texture.h @@ -1,9 +1,13 @@ #pragma once #include "StarEngine/Core/Base.h" +#include "StarEngine/Core/Buffer.h" + +#include "StarEngine/Asset/Asset.h" #include + namespace StarEngine { enum class ImageFormat @@ -23,7 +27,7 @@ namespace StarEngine { bool GenerateMips = true; }; - class Texture + class Texture : public Asset { public: virtual ~Texture() = default; @@ -34,9 +38,9 @@ namespace StarEngine { virtual uint32_t GetHeight() const = 0; virtual uint32_t GetRendererID() const = 0; - virtual const std::string& GetPath() const = 0; + virtual uint64_t GetEstimatedSize() const = 0; - virtual void SetData(void* data, uint32_t size) = 0; + virtual void SetData(Buffer data) = 0; virtual void Bind(uint32_t slot = 0) const = 0; @@ -48,8 +52,11 @@ namespace StarEngine { class Texture2D : public Texture { public: - static Ref Create(const TextureSpecification& specification); - static Ref Create(const std::string& path); - }; + static Ref Create(const TextureSpecification& specification, Buffer data = Buffer()); + virtual void ChangeSize(uint32_t newWidth, uint32_t newHeight) = 0; + + static AssetType GetStaticType() { return AssetType::Texture2D; } + virtual AssetType GetType() const { return GetStaticType(); } + }; } diff --git a/StarEngine/src/StarEngine/Scene/Components.h b/StarEngine/src/StarEngine/Scene/Components.h index 738382b6..f981c8cc 100644 --- a/StarEngine/src/StarEngine/Scene/Components.h +++ b/StarEngine/src/StarEngine/Scene/Components.h @@ -58,7 +58,7 @@ namespace StarEngine { struct SpriteRendererComponent { glm::vec4 Color{ 1.0f, 1.0f, 1.0f, 1.0f }; - Ref Texture; + AssetHandle Texture = 0; float TilingFactor = 1.0f; SpriteRendererComponent() = default; diff --git a/StarEngine/src/StarEngine/Scene/Scene.h b/StarEngine/src/StarEngine/Scene/Scene.h index 6882beac..80042966 100644 --- a/StarEngine/src/StarEngine/Scene/Scene.h +++ b/StarEngine/src/StarEngine/Scene/Scene.h @@ -1,5 +1,6 @@ #pragma once +#include "StarEngine/Asset/Asset.h" #include "StarEngine/Core/Timestep.h" #include "StarEngine/Core/UUID.h" #include "StarEngine/Renderer/EditorCamera.h" @@ -12,7 +13,7 @@ namespace StarEngine { class Entity; - class Scene + class Scene : public Asset { public: Scene(); @@ -20,6 +21,8 @@ namespace StarEngine { static Ref Copy(Ref other); + virtual AssetType GetType() const { return AssetType::Scene; } + Entity CreateEntity(const std::string& name = std::string()); Entity CreateEntityWithUUID(UUID uuid, const std::string& name = std::string()); void DestroyEntity(Entity entity); diff --git a/StarEngine/src/StarEngine/Scene/SceneSerializer.cpp b/StarEngine/src/StarEngine/Scene/SceneSerializer.cpp index 90b03318..3f98d227 100644 --- a/StarEngine/src/StarEngine/Scene/SceneSerializer.cpp +++ b/StarEngine/src/StarEngine/Scene/SceneSerializer.cpp @@ -293,8 +293,7 @@ namespace StarEngine { auto& spriteRendererComponent = entity.GetComponent(); out << YAML::Key << "Color" << YAML::Value << spriteRendererComponent.Color; - if (spriteRendererComponent.Texture) - out << YAML::Key << "TexturePath" << YAML::Value << spriteRendererComponent.Texture->GetPath(); + out << YAML::Key << "TextureHandle" << YAML::Value << spriteRendererComponent.Texture; out << YAML::Key << "TilingFactor" << YAML::Value << spriteRendererComponent.TilingFactor; @@ -376,7 +375,7 @@ namespace StarEngine { out << YAML::EndMap; // Entity } - void SceneSerializer::Serialize(const std::string& filepath) + void SceneSerializer::Serialize(const std::filesystem::path& filepath) { YAML::Emitter out; out << YAML::BeginMap; @@ -400,22 +399,23 @@ namespace StarEngine { fout << out.c_str(); } - void SceneSerializer::SerializeRuntime(const std::string& filepath) + void SceneSerializer::SerializeRuntime(const std::filesystem::path& filepath) { // Not implemented SE_CORE_ASSERT(false); } - bool SceneSerializer::Deserialize(const std::string& filepath) + bool SceneSerializer::Deserialize(const std::filesystem::path& filepath) { YAML::Node data; try { - data = YAML::LoadFile(filepath); + data = YAML::LoadFile(filepath.string()); } catch (YAML::ParserException e) { - SE_CORE_ERROR("Failed to load .starscene file '{0}'\n {1}", filepath, e.what()); + SE_CORE_ERROR("Failed to load .starscene file '{0}'\n {1}", filepath.string(), e.what()); + return false; } @@ -535,11 +535,15 @@ namespace StarEngine { if (spriteRendererComponent["TexturePath"]) { - std::string texturePath = spriteRendererComponent["TexturePath"].as(); - auto path = Project::GetAssetFileSystemPath(texturePath); - src.Texture = Texture2D::Create(path.string()); + // NOTE: legacy, could try and find something in the asset registry that matches? + // std::string texturePath = spriteRendererComponent["TexturePath"].as(); + // auto path = Project::GetAssetFileSystemPath(texturePath); + // src.Texture = Texture2D::Create(path.string()); } + if (spriteRendererComponent["TextureHandle"]) + src.Texture = spriteRendererComponent["TextureHandle"].as(); + if (spriteRendererComponent["TilingFactor"]) src.TilingFactor = spriteRendererComponent["TilingFactor"].as(); } @@ -601,7 +605,7 @@ namespace StarEngine { return true; } - bool SceneSerializer::DeserializeRuntime(const std::string& filepath) + bool SceneSerializer::DeserializeRuntime(const std::filesystem::path& filepath) { // Not implemented SE_CORE_ASSERT(false); diff --git a/StarEngine/src/StarEngine/Scene/SceneSerializer.h b/StarEngine/src/StarEngine/Scene/SceneSerializer.h index 66935913..a4d189b0 100644 --- a/StarEngine/src/StarEngine/Scene/SceneSerializer.h +++ b/StarEngine/src/StarEngine/Scene/SceneSerializer.h @@ -11,11 +11,11 @@ namespace StarEngine public: SceneSerializer(const Ref& scene); - void Serialize(const std::string& filepath); - void SerializeRuntime(const std::string& filepath); + void Serialize(const std::filesystem::path& filepath); + void SerializeRuntime(const std::filesystem::path& filepath); - bool Deserialize(const std::string& filepath); - bool DeserializeRuntime(const std::string& filepath); + bool Deserialize(const std::filesystem::path& filepath); + bool DeserializeRuntime(const std::filesystem::path& filepath); private: Ref m_Scene; }; diff --git a/StarEngine/src/StarEngine/Scripting/ScriptEngine.cpp b/StarEngine/src/StarEngine/Scripting/ScriptEngine.cpp index 1613a8e9..01bfb6a3 100644 --- a/StarEngine/src/StarEngine/Scripting/ScriptEngine.cpp +++ b/StarEngine/src/StarEngine/Scripting/ScriptEngine.cpp @@ -194,7 +194,7 @@ namespace StarEngine { return; } - auto scriptModulePath = Project::GetAssetDirectory() / Project::GetActive()->GetConfig().ScriptModulePath; + auto scriptModulePath = Project::GetActiveAssetDirectory() / Project::GetActive()->GetConfig().ScriptModulePath; status = LoadAppAssembly(scriptModulePath); if (!status) { diff --git a/StarEngine/src/StarEngine/Utils/StringUtils.cpp b/StarEngine/src/StarEngine/Utils/StringUtils.cpp new file mode 100644 index 00000000..9f19fa55 --- /dev/null +++ b/StarEngine/src/StarEngine/Utils/StringUtils.cpp @@ -0,0 +1,25 @@ +#include "sepch.h" +#include "StringUtils.h" + +namespace StarEngine::Utils +{ + std::string BytesToString(uint64_t bytes) + { + constexpr uint64_t GB = 1024 * 1024 * 1024; + constexpr uint64_t MB = 1024 * 1024; + constexpr uint64_t KB = 1024; + + char buffer[32 + 1]{}; + + if (bytes >= GB) + snprintf(buffer, 32, "%.2f GB", (float)bytes / (float)GB); + else if (bytes >= MB) + snprintf(buffer, 32, "%.2f MB", (float)bytes / (float)MB); + else if (bytes >= KB) + snprintf(buffer, 32, "%.2f KB", (float)bytes / (float)KB); + else + snprintf(buffer, 32, "%.2f bytes", bytes); + + return std::string(buffer); + } +} diff --git a/StarEngine/src/StarEngine/Utils/StringUtils.h b/StarEngine/src/StarEngine/Utils/StringUtils.h new file mode 100644 index 00000000..9f3bab7d --- /dev/null +++ b/StarEngine/src/StarEngine/Utils/StringUtils.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +namespace StarEngine::Utils { + + std::string BytesToString(uint64_t bytes); +} diff --git a/StarEngine/src/sepch.h b/StarEngine/src/sepch.h index 4a8bf2d2..e946feb8 100644 --- a/StarEngine/src/sepch.h +++ b/StarEngine/src/sepch.h @@ -24,10 +24,12 @@ #include "StarEngine/Core/Base.h" +#include "StarEngine/Core/Buffer.h" + #include "StarEngine/Core/Log.h" #include "StarEngine/Debug/Instrumentor.h" #ifdef SE_PLATFORM_WINDOWS #include -#endif \ No newline at end of file +#endif diff --git a/StarEngine/vendor/mono/lib/Dist/libmono-static-sgen.lib b/StarEngine/vendor/mono/lib/Dist/libmono-static-sgen.lib new file mode 100644 index 00000000..346a117d Binary files /dev/null and b/StarEngine/vendor/mono/lib/Dist/libmono-static-sgen.lib differ