diff --git a/.idea/dictionaries/project.xml b/.idea/dictionaries/project.xml new file mode 100644 index 0000000..f7827e6 --- /dev/null +++ b/.idea/dictionaries/project.xml @@ -0,0 +1,7 @@ + + + + lerp + + + \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 45476d1..a90c716 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,7 +25,9 @@ file(GLOB_RECURSE SOURCE_FILES ) # --- Define target --- -add_executable(${PROJECT_NAME} ${SOURCE_FILES}) +add_executable(${PROJECT_NAME} ${SOURCE_FILES} + src/src/core/utils/PrimitiveGenerator.cpp + src/src/core/utils/PrimitiveGenerator.h) # --- Include directories for this target --- target_include_directories(${PROJECT_NAME} PRIVATE diff --git a/imgui.ini b/imgui.ini index 1e756ff..5da3557 100644 --- a/imgui.ini +++ b/imgui.ini @@ -10,52 +10,52 @@ Collapsed=1 [Window][WindowOverViewport_11111111] Pos=0,19 -Size=32,32 +Size=1920,1158 Collapsed=0 [Window][Inspector] -Pos=17,19 -Size=15,17 +Pos=1529,19 +Size=391,653 Collapsed=0 DockId=0x0000001B,0 [Window][Scene Tree] Pos=0,19 -Size=7,15 +Size=340,612 Collapsed=0 DockId=0x0000000F,0 [Window][Viewport] -Pos=9,19 -Size=6,7 +Pos=342,19 +Size=1185,612 Collapsed=0 -DockId=0x00000011,0 +DockId=0x00000010,0 [Window][##MainMenuBar] Size=1920,19 Collapsed=0 [Window][Performance Info] -Pos=25,45 -Size=7,7 +Pos=1587,674 +Size=333,307 Collapsed=0 -DockId=0x00000015,0 +DockId=0x00000013,0 [Window][Console] -Pos=9,27 -Size=6,7 +Pos=0,633 +Size=760,544 Collapsed=0 -DockId=0x00000012,0 +DockId=0x00000011,0 [Window][Tilemap Editor] Pos=265,19 Size=1263,674 Collapsed=0 -DockId=0x00000011,1 +DockId=0x00000010,1 [Window][Profiler] -Pos=343,955 -Size=1232,222 +Pos=0,850 +Size=1920,327 Collapsed=0 DockId=0x00000008,0 @@ -78,8 +78,8 @@ Collapsed=0 DockId=0x00000008,1 [Window][Color Correction] -Pos=25,49 -Size=7,7 +Pos=1587,1031 +Size=333,146 Collapsed=0 DockId=0x00000016,0 @@ -113,8 +113,8 @@ Collapsed=0 DockId=0x0000000E,0 [Window][Audio Output] -Pos=17,45 -Size=6,7 +Pos=1529,674 +Size=56,503 Collapsed=0 DockId=0x00000019,0 @@ -136,10 +136,10 @@ Size=550,695 Collapsed=0 [Window][Lua Globals] -Pos=17,38 -Size=15,7 +Pos=1587,983 +Size=333,46 Collapsed=0 -DockId=0x00000013,0 +DockId=0x00000014,0 [Window][Import Assets] Pos=626,263 @@ -152,10 +152,10 @@ Size=600,209 Collapsed=0 [Window][File Explorer] -Pos=0,36 -Size=15,15 +Pos=762,633 +Size=765,544 Collapsed=0 -DockId=0x0000001E,0 +DockId=0x00000012,0 [Window][ConfirmClearScene] Pos=808,551 @@ -197,30 +197,30 @@ Column 1 Width=86 Column 2 Weight=1.0000 [Docking][Data] -DockSpace ID=0x11111111 Window=0x1BBC0F80 Pos=0,19 Size=32,32 Split=X +DockSpace ID=0x11111111 Window=0x1BBC0F80 Pos=0,19 Size=1920,1158 Split=X DockNode ID=0x00000005 Parent=0x11111111 SizeRef=989,1158 Split=X DockNode ID=0x00000001 Parent=0x00000005 SizeRef=341,701 Split=Y Selected=0x12EF0F59 DockNode ID=0x00000003 Parent=0x00000001 SizeRef=342,637 HiddenTabBar=1 Selected=0x12EF0F59 DockNode ID=0x00000004 Parent=0x00000001 SizeRef=342,519 HiddenTabBar=1 Selected=0x36AF052B DockNode ID=0x00000002 Parent=0x00000005 SizeRef=1577,701 Split=Y Selected=0xC450F867 - DockNode ID=0x00000007 Parent=0x00000002 SizeRef=606,684 Split=X Selected=0xC450F867 + DockNode ID=0x00000007 Parent=0x00000002 SizeRef=606,829 Split=X Selected=0xC450F867 DockNode ID=0x00000017 Parent=0x00000007 SizeRef=1184,860 Split=Y Selected=0xC450F867 - DockNode ID=0x0000001D Parent=0x00000017 SizeRef=1208,763 Split=X Selected=0xC450F867 + DockNode ID=0x0000001D Parent=0x00000017 SizeRef=1208,283 Split=X Selected=0xC450F867 DockNode ID=0x0000000F Parent=0x0000001D SizeRef=340,399 HiddenTabBar=1 Selected=0x12EF0F59 - DockNode ID=0x00000010 Parent=0x0000001D SizeRef=1185,399 Split=Y Selected=0xC450F867 - DockNode ID=0x00000011 Parent=0x00000010 SizeRef=1185,526 CentralNode=1 HiddenTabBar=1 Selected=0xC450F867 - DockNode ID=0x00000012 Parent=0x00000010 SizeRef=1185,235 HiddenTabBar=1 Selected=0xEA83D666 - DockNode ID=0x0000001E Parent=0x00000017 SizeRef=1208,393 HiddenTabBar=1 Selected=0x9C2B5678 + DockNode ID=0x00000010 Parent=0x0000001D SizeRef=1185,399 CentralNode=1 HiddenTabBar=1 Selected=0xC450F867 + DockNode ID=0x0000001E Parent=0x00000017 SizeRef=1208,544 Split=X Selected=0x9C2B5678 + DockNode ID=0x00000011 Parent=0x0000001E SizeRef=760,399 HiddenTabBar=1 Selected=0xEA83D666 + DockNode ID=0x00000012 Parent=0x0000001E SizeRef=765,399 HiddenTabBar=1 Selected=0x9C2B5678 DockNode ID=0x00000018 Parent=0x00000007 SizeRef=391,860 Split=Y Selected=0x36DC96AB - DockNode ID=0x0000001B Parent=0x00000018 SizeRef=367,636 HiddenTabBar=1 Selected=0x36DC96AB - DockNode ID=0x0000001C Parent=0x00000018 SizeRef=367,520 Split=Y Selected=0x8CFF897F - DockNode ID=0x00000013 Parent=0x0000001C SizeRef=367,135 HiddenTabBar=1 Selected=0x8CFF897F - DockNode ID=0x00000014 Parent=0x0000001C SizeRef=367,383 Split=X Selected=0x56009A08 - DockNode ID=0x00000019 Parent=0x00000014 SizeRef=56,70 HiddenTabBar=1 Selected=0x56009A08 - DockNode ID=0x0000001A Parent=0x00000014 SizeRef=333,70 Split=Y Selected=0x3FC1A724 - DockNode ID=0x00000015 Parent=0x0000001A SizeRef=181,220 HiddenTabBar=1 Selected=0x3FC1A724 - DockNode ID=0x00000016 Parent=0x0000001A SizeRef=181,161 HiddenTabBar=1 Selected=0xA873C17F - DockNode ID=0x00000008 Parent=0x00000002 SizeRef=606,472 HiddenTabBar=1 Selected=0xEA83D666 + DockNode ID=0x0000001B Parent=0x00000018 SizeRef=367,467 HiddenTabBar=1 Selected=0x36DC96AB + DockNode ID=0x0000001C Parent=0x00000018 SizeRef=367,360 Split=X Selected=0x8CFF897F + DockNode ID=0x00000019 Parent=0x0000001C SizeRef=56,70 HiddenTabBar=1 Selected=0x56009A08 + DockNode ID=0x0000001A Parent=0x0000001C SizeRef=333,70 Split=Y Selected=0x3FC1A724 + DockNode ID=0x00000015 Parent=0x0000001A SizeRef=181,254 Split=Y Selected=0x3FC1A724 + DockNode ID=0x00000013 Parent=0x00000015 SizeRef=333,219 HiddenTabBar=1 Selected=0x3FC1A724 + DockNode ID=0x00000014 Parent=0x00000015 SizeRef=333,33 HiddenTabBar=1 Selected=0x8CFF897F + DockNode ID=0x00000016 Parent=0x0000001A SizeRef=181,104 HiddenTabBar=1 Selected=0xA873C17F + DockNode ID=0x00000008 Parent=0x00000002 SizeRef=606,327 HiddenTabBar=1 Selected=0xEA83D666 DockNode ID=0x00000006 Parent=0x11111111 SizeRef=289,1158 Split=Y Selected=0x36DC96AB DockNode ID=0x00000009 Parent=0x00000006 SizeRef=449,488 Split=Y Selected=0x36DC96AB DockNode ID=0x0000000B Parent=0x00000009 SizeRef=449,556 Split=Y Selected=0x36DC96AB diff --git a/src/src/Engine.cpp b/src/src/Engine.cpp index a03192a..ed50113 100644 --- a/src/src/Engine.cpp +++ b/src/src/Engine.cpp @@ -290,15 +290,15 @@ void ShowProfilerTimeline() // Layout metrics constexpr float rowH = 24.0f; int rows = CountNodes(cachedFrame); - float avail = ImGui::GetContentRegionAvail().x; + const float avail = ImGui::GetContentRegionAvail().x; float width = avail * zoom; float height = std::min(rows * rowH, 400.0f); // Begin child & get origin ImGui::BeginChild("TimelineScroll", ImVec2(0, height), false, ImGuiWindowFlags_HorizontalScrollbar); - ImVec2 origin = ImGui::GetCursorScreenPos(); + const ImVec2 origin = ImGui::GetCursorScreenPos(); ImDrawList *draw = ImGui::GetWindowDrawList(); - ImU32 gridCol = ImGui::GetColorU32(ImGuiCol_Border); + const ImU32 gridCol = ImGui::GetColorU32(ImGuiCol_Border); struct Line { @@ -469,6 +469,8 @@ void DrawGizmoForObject(const std::shared_ptr &obj, op = ImGuizmo::TRANSLATE; if (ImGui::IsKeyPressed(ImGuiKey_R)) op = ImGuizmo::ROTATE; + + } glm::mat4 manipulated = model; @@ -481,8 +483,24 @@ void DrawGizmoForObject(const std::shared_ptr &obj, float angleRadians = atan2(manipulated[1][0], manipulated[0][0]); // M10, M00 - obj->SetWorldPosition({pos.x, pos.y}); - obj->SetLocalRotation(glm::degrees(angleRadians)); // Z + + + switch (op) { + case ImGuizmo::ROTATE: + obj->SetLocalRotation(glm::degrees(angleRadians)); + break; + case ImGuizmo::TRANSLATE: + default: + obj->SetWorldPosition({pos.x, pos.y}); + break; + + } + + + + + + } } @@ -740,7 +758,7 @@ void Engine::Run() ProjectManager::GetCurrentProjectName() + ".cene"; SceneLoader::SaveScene(path); } - if (ImGui::MenuItem("Save Scene As")) + if (ImGui::MenuItem("Save Scene as")) { std::string file = CreateFileDialog(FileDialogType::Scenes); if (!file.empty()) @@ -931,14 +949,31 @@ void Engine::Run() if (g_engineConfig.settings.show_color_correction_window) ShowColorCorrectionWindow(); - ShowAssetBrowser(); - DrawAudioPlayingList(); - DrawImGuiWindow(); - FileExplorer::Update(); - FileExplorer::Show(nullptr); - FileExplorer::ProcessLoadQueue(); + { + PROFILE_ENGINE_SCOPE("ShowAssetBrowser"); + ShowAssetBrowser(); + } + { + PROFILE_ENGINE_SCOPE("DrawAudioPlayingList"); + DrawAudioPlayingList(); + } + + { + PROFILE_ENGINE_SCOPE("DrawLuaGlobalsWindow"); + DrawLuaGlobalsWindow(); + } + { + PROFILE_ENGINE_SCOPE("FileExplorer::Update"); + FileExplorer::Update(); + FileExplorer::ProcessLoadQueue(); + } + { + PROFILE_ENGINE_SCOPE("FileExplorer::Show"); + FileExplorer::Show(nullptr); + + } @@ -1608,6 +1643,10 @@ void Engine::Run() selected = previousSelection; previousSelection = nullptr; } + else + { + selected = nullptr; + } } } diff --git a/src/src/Renderer.cpp b/src/src/Renderer.cpp index ca6de9d..e89115c 100644 --- a/src/src/Renderer.cpp +++ b/src/src/Renderer.cpp @@ -164,7 +164,6 @@ void Renderer::InitQuadBatch() glBindBuffer(GL_ARRAY_BUFFER, s_QuadInstanceVBO); glBufferStorage(GL_ARRAY_BUFFER, MAX_QUADS * sizeof(QuadInstance), nullptr, s_QuadPersistentFlags); - // Map once for persistent write access s_QuadMappedPtr = glMapBufferRange(GL_ARRAY_BUFFER, 0, MAX_QUADS * sizeof(QuadInstance), s_QuadPersistentFlags); if (!s_QuadMappedPtr) { @@ -902,7 +901,12 @@ void Renderer::FlushSprites() currentShader->SetFloat("uRotation", entry.sprite.rotationRad); currentShader->SetInt("uTex", 0); - // Set UVs if applicable + //glm::mat4 model = + // glm::translate(glm::mat4(1.0f), glm::vec3(entry.sprite.screenPos, 0.0f)) * + // glm::rotate (glm::mat4(1.0f), entry.sprite.rotationRad, glm::vec3(0,0,1)) * + // glm::scale (glm::mat4(1.0f), glm::vec3(entry.sprite.size, 1.0f)); + //currentShader->SetMat4("uModel", model); + if (entry.usesUV) { currentShader->SetVec2("uUVMin", glm::vec2(entry.sprite.texCoords.x, entry.sprite.texCoords.y)); diff --git a/src/src/Renderer.h b/src/src/Renderer.h index 5b0a387..cb9e863 100644 --- a/src/src/Renderer.h +++ b/src/src/Renderer.h @@ -18,6 +18,10 @@ #include "core/utils/Texture.h" +struct Mesh { + GLuint VAO; + GLsizei indexCount; +}; diff --git a/src/src/core/functions/ProjectManager.cpp b/src/src/core/functions/ProjectManager.cpp index 724d933..3c5b703 100644 --- a/src/src/core/functions/ProjectManager.cpp +++ b/src/src/core/functions/ProjectManager.cpp @@ -15,10 +15,70 @@ #include #include #include +using namespace std::chrono; + #define ASSET_MANEFEST_NAME "assets.capk" +namespace { + struct CachedEntry { + fs::path path; + bool isDirectory; + AssetType type; + ImTextureID thumbID; + std::string label; + }; + static std::vector cacheEntries; + + struct TreeNode { + fs::path path; + std::vector children; + }; + static bool rebuild = true; + static TreeNode cachedTreeRoot; + static fs::path lastTreeRoot; + static bool treeDirty = true; + + // Recursively build the TreeNode children + void BuildTreeNode(const fs::path& p, TreeNode& node) { + for (auto& e : fs::directory_iterator(p)) { + if (e.is_directory()) { + node.children.push_back({ e.path(), {} }); + BuildTreeNode(e.path(), node.children.back()); + } + } + } + + // Ensure cachedTreeRoot matches s_root + void EnsureTreeCache(const fs::path& root) { + if (treeDirty || root != lastTreeRoot) { + cachedTreeRoot = { root, {} }; + BuildTreeNode(root, cachedTreeRoot); + lastTreeRoot = root; + treeDirty = false; + } + } + // Draw from the cached tree + void DrawCachedTree(const TreeNode& node) { + // use folder name (or root) + auto name = node.path == lastTreeRoot + ? "res://" + : node.path.filename().string().c_str(); + bool open = ImGui::TreeNodeEx( + node.path.string().c_str(), + ImGuiTreeNodeFlags_OpenOnArrow, + "%s", + name + ); + if (open) { + for (auto& child : node.children) + DrawCachedTree(child); + ImGui::TreePop(); + } + } +} + @@ -44,6 +104,12 @@ size_t FileExplorer::s_loadIndex = 0; bool FileExplorer::s_loadingInit = false; +std::mutex FileExplorer::s_fileMutex; +std::atomic FileExplorer::s_scanInProgress{false}; +steady_clock::time_point FileExplorer::s_lastScan = steady_clock::now(); +const milliseconds FileExplorer::s_scanInterval = milliseconds(1000); + + bool FileExplorer::Init() { if (s_initialized) @@ -86,6 +152,11 @@ bool FileExplorer::Init() void FileExplorer::Show(bool* p_open) { if (!s_initialized) return; + + // rebuild tree if the root changed + if (s_root != lastTreeRoot) treeDirty = true; + EnsureTreeCache(s_root); + if (!ImGui::Begin("File Explorer", p_open, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_HorizontalScrollbar)) { @@ -93,191 +164,281 @@ void FileExplorer::Show(bool* p_open) return; } - // --- Left pane: folder tree --- - ImGui::BeginChild("##FolderPane", ImVec2(250, 0), true); - ImGui::TextUnformatted("res://"); - ImGui::Separator(); - DrawFolderTree(s_root); + // ------------------------------------------------------------------------- + // LEFT PANE: interactive folder‐tree with icons + // ------------------------------------------------------------------------- + ImGui::BeginChild("##FolderPane", ImVec2(250,0), true); + { + // recursive lambda to draw each node + std::function drawNode = [&](const TreeNode& node) { + ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow + | ImGuiTreeNodeFlags_SpanAvailWidth; + if (node.children.empty()) + flags |= ImGuiTreeNodeFlags_Leaf; + if (node.path == s_currentDir) + flags |= ImGuiTreeNodeFlags_Selected; + + // 1) icon + ImGui::Image((ImTextureID)(intptr_t)s_folderIcon, ImVec2(16,16)); + ImGui::SameLine(); + + // 2) label + const char* label = (node.path == lastTreeRoot) + ? "res://" + : node.path.filename().string().c_str(); + bool open = ImGui::TreeNodeEx( + node.path.string().c_str(), + flags, + "%s", + label + ); + + // 3) click to change directory + if (ImGui::IsItemClicked()) { + s_currentDir = node.path; + treeDirty = true; // also force tree repaint? + rebuild = true; // file‐grid rebuild + } + + // 4) children + if (open) { + for (auto& child : node.children) + drawNode(child); + ImGui::TreePop(); + } + }; + + drawNode(cachedTreeRoot); + } ImGui::EndChild(); ImGui::SameLine(); - // --- Right pane: content + toolbar --- - ImGui::BeginChild("##ContentPane", ImVec2(0, 0), false); + // ------------------------------------------------------------------------- + // RIGHT PANE: toolbar + file‐grid + // ------------------------------------------------------------------------- + ImGui::BeginChild("##ContentPane", ImVec2(0,0), false); - // Filters / Search - if (ImGui::Button("Filters / Sort")) ImGui::OpenPopup("FileFilterPopup"); + // --- Filters / Sort / Search state (static to persist frame-to-frame) --- + static char searchBuf[256] = ""; + static fs::path lastDir; + static int lastSortMode = -1; + static bool lastSortAsc = false; + static bool lastShowScenes = true; + static bool lastShowImages = true; + static bool lastShowAudio = true; + static bool lastShowScripts = true; + static bool lastShowOther = true; + static std::string lastSearch; + static bool rebuild = true; + + // Filters / Sort popup + if (ImGui::Button("Filters / Sort")) ImGui::OpenPopup("FileFilterPopup"); ImGui::SameLine(); ImGui::SetNextItemWidth(200); - char buf[256]; - strncpy(buf, fileSearchQuery.c_str(), sizeof(buf)); - buf[sizeof(buf)-1] = '\0'; - if (ImGui::InputTextWithHint("##Search", "Search...", buf, sizeof(buf))) - fileSearchQuery = buf; - + if (ImGui::InputTextWithHint("##Search","Search...",searchBuf,sizeof(searchBuf))) { + fileSearchQuery = searchBuf; + rebuild = true; + } if (ImGui::BeginPopup("FileFilterPopup")) { - ImGui::Text("Sort By:"); - if (ImGui::Selectable("Name", sortMode == 0)) sortMode = 0; - if (ImGui::Selectable("Type", sortMode == 1)) sortMode = 1; - ImGui::Checkbox("Ascending", &sortAscending); + if (ImGui::Selectable("Name", sortMode==0)) { sortMode=0; rebuild=true; } + if (ImGui::Selectable("Type", sortMode==1)) { sortMode=1; rebuild=true; } + if (ImGui::Checkbox("Ascending",&sortAscending)) rebuild=true; ImGui::Separator(); - ImGui::Text("Type:"); - ImGui::Checkbox("Scenes", &showScenes); - ImGui::Checkbox("Images", &showImages); - ImGui::Checkbox("Audio", &showAudio); - ImGui::Checkbox("Scripts", &showScripts); - ImGui::Checkbox("Other", &showOther); + if (ImGui::Checkbox("Scenes", &showScenes)) rebuild=true; + if (ImGui::Checkbox("Images", &showImages)) rebuild=true; + if (ImGui::Checkbox("Audio", &showAudio)) rebuild=true; + if (ImGui::Checkbox("Scripts", &showScripts)) rebuild=true; + if (ImGui::Checkbox("Other", &showOther)) rebuild=true; ImGui::EndPopup(); } - ImGui::Separator(); - - // “Up” button + // “Up” button + current path display if (s_currentDir != s_root) { - if (ImGui::Button("Up")) s_currentDir = s_currentDir.parent_path(); + if (ImGui::Button("Up")) { + s_currentDir = s_currentDir.parent_path(); + rebuild = true; + } ImGui::SameLine(); } - - // Current path display - fs::path rel = fs::relative(s_currentDir, s_root); - std::string displayPath = "res://" + - (rel.empty() ? std::string() : rel.generic_string() + "/"); - ImGui::TextUnformatted(displayPath.c_str()); + { + fs::path rel = fs::relative(s_currentDir, s_root); + std::string displayPath = "res://" + + (rel.empty() ? "" : rel.generic_string()+"/"); + ImGui::TextUnformatted(displayPath.c_str()); + } ImGui::Separator(); - // Gather & filter - std::vector entries; - for (auto &e : fs::directory_iterator(s_currentDir)) { - if (!e.is_directory()) { - AssetType t = AssetManager::AssetTypeFromPath(e.path().string()); - switch (t) { - case AssetType::Scene: if (!showScenes) continue; break; - case AssetType::Image: if (!showImages) continue; break; - case AssetType::Audio: if (!showAudio) continue; break; - case AssetType::Script: if (!showScripts) continue; break; - default: if (!showOther) continue; break; - } - } - if (!fileSearchQuery.empty() && - e.path().filename().string().find(fileSearchQuery) == std::string::npos) - continue; - entries.push_back(e); - } - - std::sort(entries.begin(), entries.end(), [&](auto &a, auto &b) { - if (sortMode == 1) { - bool da = a.is_directory(), db = b.is_directory(); - if (da != db) return sortAscending ? da > db : da < db; - return sortAscending - ? a.path().extension() < b.path().extension() - : a.path().extension() > b.path().extension(); - } - return sortAscending - ? a.path().filename() < b.path().filename() - : a.path().filename() > b.path().filename(); - }); - - // Icon grid setup - constexpr float iconSize = 64.0f, padding = 8.0f; - float avail = ImGui::GetContentRegionAvail().x; - int cols = std::max(1, int(avail / (iconSize + padding))); - ImGui::Columns(cols, nullptr, false); - - for (auto &e : entries) { - fs::path p = e.path(); - std::string pathStr = p.string(); - AssetType type = AssetManager::AssetTypeFromPath(pathStr); - - ImGui::PushID(pathStr.c_str()); - - // 1) pick thumbnail - ImTextureID thumbID = 0; - if (e.is_directory()) { - thumbID = (ImTextureID)(intptr_t)s_folderIcon; - } else if (type == AssetType::Image) { - if (auto* asset = AssetManager::GetAssetByPath(pathStr)) - if (asset->type == AssetType::Image) - if (auto* img = dynamic_cast(asset)) - thumbID = (ImTextureID)(intptr_t)img->textureID; - } - if (!thumbID) { - GLuint iconTex = EngineLoadTextureIfNeeded( - std::string(ICONS_PATH) + IconFileForPath(p)); - thumbID = (ImTextureID)(intptr_t)iconTex; - } - - // 2) DRAW the single widget with a consistent ID: "##entry" - bool clicked = false; - if (e.is_directory() || type == AssetType::Image) { - clicked = ImGui::ImageButton( - "##entry", - thumbID, - ImVec2(iconSize, iconSize)); - } else { - std::string label; - switch (type) { - case AssetType::Prefab: label = "Prefab"; break; - case AssetType::Scene: label = "Scene"; break; - case AssetType::Audio: label = "Audio"; break; - case AssetType::Script: label = "Script"; break; - case AssetType::Video: label = "Video"; break; - case AssetType::Font: label = "Font"; break; - case AssetType::Shader: label = "Shader"; break; - default: - label = p.extension().string(); - if (!label.empty() && label.front()=='.') - label.erase(0,1); - break; - } - clicked = ImGui::Button( - (label + std::string("##entry") + label).c_str(), - ImVec2(iconSize, iconSize)); - } - - // 3) handle click/navigation - if (clicked) { - if (e.is_directory()) - s_currentDir = p; - else - SelectedPath = pathStr; - } - - // 4) context menu tied to the same "##entry" ID - if (ImGui::BeginPopupContextItem("##entry")) { - if (e.is_directory() && ImGui::MenuItem("Open")) - s_currentDir = p; - if (!e.is_directory() && ImGui::MenuItem("Open File")) - SelectedPath = pathStr; - if (ImGui::MenuItem("Copy Path")) - ImGui::SetClipboardText(pathStr.c_str()); - ImGui::EndPopup(); - } - - // 5) drag-drop source also tied to "##entry" - if (!e.is_directory() && - ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceAllowNullID)) + // Detect any state changes → rebuild the file‐grid cache + if ( lastDir != s_currentDir + || lastSortMode != sortMode + || lastSortAsc != sortAscending + || lastSearch != fileSearchQuery + || lastShowScenes != showScenes + || lastShowImages != showImages + || lastShowAudio != showAudio + || lastShowScripts != showScripts + || lastShowOther != showOther) { - if (auto* asset = AssetManager::GetAssetByPath(pathStr)) { - if (type == AssetType::Image) - ImGui::SetDragDropPayload(ASSET_TEXTURE, - &asset->uaid, - sizeof(asset->uaid)); - else if (type == AssetType::Prefab) - ImGui::SetDragDropPayload(ASSET_PREFAB, - &asset->uaid, - sizeof(asset->uaid)); - } - ImGui::TextUnformatted(p.filename().string().c_str()); - ImGui::EndDragDropSource(); + rebuild = true; + lastDir = s_currentDir; + lastSortMode = sortMode; + lastSortAsc = sortAscending; + lastSearch = fileSearchQuery; + lastShowScenes = showScenes; + lastShowImages = showImages; + lastShowAudio = showAudio; + lastShowScripts = showScripts; + lastShowOther = showOther; } - // 6) label and next column - ImGui::TextWrapped("%s", p.filename().string().c_str()); - ImGui::NextColumn(); + if (rebuild) { + cacheEntries.clear(); + cacheEntries.reserve(128); - ImGui::PopID(); -} + for (auto& e : fs::directory_iterator(s_currentDir)) { + bool isDir = e.is_directory(); + AssetType t = AssetManager::AssetTypeFromPath(e.path().string()); + // type filters + if (!isDir) { + if ((t==AssetType::Scene && !showScenes )|| + (t==AssetType::Image && !showImages )|| + (t==AssetType::Audio && !showAudio )|| + (t==AssetType::Script && !showScripts)|| + (t==AssetType::Unknown && !showOther )) + continue; + } + + // search filter + if (!fileSearchQuery.empty() + && e.path().filename().string().find(fileSearchQuery)==std::string::npos) + continue; + + CachedEntry ce; + ce.path = e.path(); + ce.isDirectory = isDir; + ce.type = t; + + // thumbnail + if (isDir) { + ce.thumbID = (ImTextureID)(intptr_t)s_folderIcon; + } + else if (t==AssetType::Image) { + if (auto* asset = AssetManager::GetAssetByPath(ce.path.string())) + if (auto* img = dynamic_cast(asset)) + ce.thumbID = (ImTextureID)(intptr_t)img->textureID; + } + if (!ce.thumbID) { + ce.thumbID = (ImTextureID)(intptr_t) + EngineLoadTextureIfNeeded(ICONS_PATH + IconFileForPath(ce.path)); + } + + // label for non‐image files + if (!isDir && t!=AssetType::Image) { + static const std::unordered_map L = { + {AssetType::Prefab,"Prefab"}, + {AssetType::Scene, "Scene"}, + {AssetType::Audio, "Audio"}, + {AssetType::Script,"Script"}, + {AssetType::Video, "Video"}, + {AssetType::Font, "Font"}, + {AssetType::Shader,"Shader"} + }; + auto it = L.find(t); + if (it!=L.end()) ce.label = it->second; + else { + auto ext = ce.path.extension().string(); + ce.label = ext.size()>1 ? ext.substr(1) : ext; + } + } + + cacheEntries.push_back(std::move(ce)); + } + + // sort + std::sort(cacheEntries.begin(), cacheEntries.end(), + [&](auto& a, auto& b){ + if (sortMode==1) { + if (a.isDirectory!=b.isDirectory) + return sortAscending + ? a.isDirectory>b.isDirectory + : a.isDirectoryb.path.extension(); + } + return sortAscending + ? a.path.filename()b.path.filename(); + }); + + rebuild = false; + } + + // Icon‐grid draw pass + constexpr float iconSize=64.0f, padding=8.0f; + float avail = ImGui::GetContentRegionAvail().x; + int cols = std::max(1,int(avail/(iconSize+padding))); + ImGui::Columns(cols,nullptr,false); + + for (auto& ce : cacheEntries) { + PROFILE_ENGINE_SCOPE("entry"); + const auto& p = ce.path; + const auto pathStr = p.string(); + + ImGui::PushID(pathStr.c_str()); + + bool clicked = false; + if (ce.isDirectory||ce.type==AssetType::Image) { + PROFILE_ENGINE_SCOPE("Image"); + clicked = ImGui::ImageButton("##entry", + ce.thumbID, + ImVec2(iconSize,iconSize)); + } else { + clicked = ImGui::Button( + (ce.label+"##entry").c_str(), + ImVec2(iconSize,iconSize) + ); + } + + if (clicked) { + if (ce.isDirectory) s_currentDir=p; + else SelectedPath=pathStr; + rebuild=true; + } + + if (ImGui::BeginPopupContextItem("##entry")) { + if (ce.isDirectory && ImGui::MenuItem("Open")) + s_currentDir=p, rebuild=true; + if (!ce.isDirectory && ImGui::MenuItem("Open File")) + SelectedPath=pathStr; + if (ImGui::MenuItem("Copy Path")) + ImGui::SetClipboardText(pathStr.c_str()); + ImGui::EndPopup(); + } + + if (!ce.isDirectory && + ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceAllowNullID)) + { + if (auto* asset=AssetManager::GetAssetByPath(pathStr)) { + const void* data=&asset->uaid; + const char* typeStr= + ce.type==AssetType::Image ? ASSET_TEXTURE : + ce.type==AssetType::Prefab ? ASSET_PREFAB : nullptr; + if(typeStr) + ImGui::SetDragDropPayload(typeStr, + data, + sizeof(asset->uaid)); + } + ImGui::TextUnformatted(p.filename().string().c_str()); + ImGui::EndDragDropSource(); + } + + ImGui::TextWrapped("%s",p.filename().string().c_str()); + ImGui::NextColumn(); + ImGui::PopID(); + } ImGui::Columns(1); ImGui::EndChild(); @@ -287,6 +448,56 @@ void FileExplorer::Show(bool* p_open) + +void FileExplorer::ScanDirectory() +{ + std::error_code ec; + std::unordered_set currentFiles; + + // walk the tree once + for (auto it = fs::recursive_directory_iterator(s_root, + fs::directory_options::skip_permission_denied, ec), + end = fs::recursive_directory_iterator{}; + it != end; it.increment(ec)) + { + if (ec) + { + + ec.clear(); + continue; + } + if (!it->is_regular_file()) continue; + + auto pathStr = it->path().string(); + currentFiles.insert(pathStr); + + { + std::lock_guard lock(s_fileMutex); + if (!s_knownFiles.count(pathStr)) + { + s_knownFiles.insert(pathStr); + s_loadQueue.push_back(it->path()); + + } + } + } + + { + std::lock_guard lock(s_fileMutex); + for (auto it = s_knownFiles.begin(); it != s_knownFiles.end(); ) + { + if (!currentFiles.count(*it)) + { + + AssetManager::GetAssetByPath(*it); + it = s_knownFiles.erase(it); + } + else ++it; + } + } +} + + void FileExplorer::Update() { if (!s_initialized) @@ -295,41 +506,17 @@ void FileExplorer::Update() return; } - std::error_code ec; - std::unordered_set currentFiles; - for (auto it = fs::recursive_directory_iterator(s_root, - fs::directory_options::skip_permission_denied, ec), - end = fs::recursive_directory_iterator{}; - it != end; it.increment(ec)) + auto now = steady_clock::now(); + + if (!s_scanInProgress.load() && now - s_lastScan >= s_scanInterval) { - if (ec) { - Logger::LogWarning("FileExplorer: skipping '%s': %s", - it->path().string().c_str(), - ec.message().c_str()); - ec.clear(); - continue; - } - if (!it->is_regular_file()) continue; - - auto pathStr = it->path().string(); - currentFiles.insert(pathStr); - - if (!s_knownFiles.count(pathStr)) { - s_knownFiles.insert(pathStr); - s_loadQueue.push_back(it->path()); - Logger::LogVerbose("FileExplorer: added '%s'", pathStr.c_str()); - } - } - - // detect deletions - for (auto it = s_knownFiles.begin(); it != s_knownFiles.end(); ) - { - if (!currentFiles.count(*it)) { - Logger::LogVerbose("FileExplorer: removed '%s'", it->c_str()); - AssetManager::GetAssetByPath(*it); - it = s_knownFiles.erase(it); - } - else ++it; + s_lastScan = now; + s_scanInProgress = true; + // fire‐and‐forget background scan + std::thread([](){ + ScanDirectory(); + s_scanInProgress = false; + }).detach(); } } @@ -340,6 +527,8 @@ bool FileExplorer::LoadingDone() void FileExplorer::DrawFolderTree(const fs::path &path) { + PROFILE_ENGINE_SCOPE("DrawTree"); + for (auto &e : fs::directory_iterator(path)) { if (!e.is_directory()) continue; auto &d = e.path(); diff --git a/src/src/core/functions/ProjectManager.h b/src/src/core/functions/ProjectManager.h index 1866b7f..823f44c 100644 --- a/src/src/core/functions/ProjectManager.h +++ b/src/src/core/functions/ProjectManager.h @@ -4,7 +4,8 @@ #include #include #include - +#include +#include @@ -54,6 +55,16 @@ private: static size_t s_loadIndex; static bool s_loadingInit; + + static void ScanDirectory(); + + static std::mutex s_fileMutex; + + // background‐scan control + static std::atomic s_scanInProgress; + static std::chrono::steady_clock::time_point s_lastScan; + static const std::chrono::milliseconds s_scanInterval; + static std::string IconFileForPath(const fs::path &path); static void DrawFolderTree(const fs::path &path); @@ -100,4 +111,7 @@ private: static std::string s_currentProjectName; static std::string s_defaultScene; + + + }; diff --git a/src/src/core/types/vec3.h b/src/src/core/types/vec3.h index 4c28e55..4e52de2 100644 --- a/src/src/core/types/vec3.h +++ b/src/src/core/types/vec3.h @@ -4,116 +4,114 @@ #include #include -namespace core -{ - namespace types +namespace core::types { + + struct Vec3 { + float x{0}, y{0}, z{0}; - struct Vec3 + Vec3() = default; + Vec3(const float x, const float y, const float z) : x(x), y(y), z(z) {} + explicit Vec3(const float v) : x(v), y(v), z(v) {} + explicit Vec3(const glm::vec3 &v) : x(v.x), y(v.y), z(v.z) {} + + operator glm::vec3() const { return { x, y, z }; } + + + Vec3 operator+(const Vec3 &rhs) const { return {x + rhs.x, y + rhs.y, z + rhs.z}; } + Vec3 operator-(const Vec3 &rhs) const { return {x - rhs.x, y - rhs.y, z - rhs.z}; } + Vec3 operator*(const float scalar) const { return {x * scalar, y * scalar, z * scalar}; } + Vec3 operator/(const float scalar) const { return {x / scalar, y / scalar, z / scalar}; } + + Vec3 operator*(const Vec3 &rhs) const { return {x * rhs.x, y * rhs.y, z * rhs.z}; } + + + + Vec3 &operator+=(const Vec3 &rhs) { x += rhs.x; y += rhs.y; z += rhs.z; return *this; } + Vec3 &operator-=(const Vec3 &rhs) { x -= rhs.x; y -= rhs.y; z -= rhs.z; return *this; } + Vec3 &operator*=(const float scalar) { x *= scalar; y *= scalar; z *= scalar; return *this; } + Vec3 &operator/=(const float scalar) { x /= scalar; y /= scalar; z /= scalar; return *this; } + + Vec3 operator-() const { return {-x, -y, -z}; } + + bool operator==(const Vec3 &rhs) const { return x == rhs.x && y == rhs.y && z == rhs.z; } + bool operator!=(const Vec3 &rhs) const { return !(*this == rhs); } + + float Length() const { return std::sqrt(x * x + y * y + z * z); } + float LengthSquared() const { return x * x + y * y + z * z; } + + Vec3 Normalized() const { - float x{0}, y{0}, z{0}; + const float len = Length(); + return len != 0 ? *this / len : Vec3(0.0f); + } - Vec3() = default; - Vec3(float x, float y, float z) : x(x), y(y), z(z) {} - Vec3(float v) : x(v), y(v), z(v) {} - Vec3(const glm::vec3 &v) : x(v.x), y(v.y), z(v.z) {} - - operator glm::vec3() const { return {x, y, z}; } - - Vec3 operator+(const Vec3 &rhs) const { return {x + rhs.x, y + rhs.y, z + rhs.z}; } - Vec3 operator-(const Vec3 &rhs) const { return {x - rhs.x, y - rhs.y, z - rhs.z}; } - Vec3 operator*(float scalar) const { return {x * scalar, y * scalar, z * scalar}; } - Vec3 operator/(float scalar) const { return {x / scalar, y / scalar, z / scalar}; } - - Vec3 operator*(const Vec3 &rhs) const { return {x * rhs.x, y * rhs.y, z * rhs.z}; } - - - - Vec3 &operator+=(const Vec3 &rhs) { x += rhs.x; y += rhs.y; z += rhs.z; return *this; } - Vec3 &operator-=(const Vec3 &rhs) { x -= rhs.x; y -= rhs.y; z -= rhs.z; return *this; } - Vec3 &operator*=(float scalar) { x *= scalar; y *= scalar; z *= scalar; return *this; } - Vec3 &operator/=(float scalar) { x /= scalar; y /= scalar; z /= scalar; return *this; } - - Vec3 operator-() const { return {-x, -y, -z}; } - - bool operator==(const Vec3 &rhs) const { return x == rhs.x && y == rhs.y && z == rhs.z; } - bool operator!=(const Vec3 &rhs) const { return !(*this == rhs); } - - float Length() const { return std::sqrt(x * x + y * y + z * z); } - float LengthSquared() const { return x * x + y * y + z * z; } - - Vec3 Normalized() const + void Normalize() + { + if (const float len = Length(); len != 0) { - float len = Length(); - return len != 0 ? *this / len : Vec3(0.0f); + x /= len; + y /= len; + z /= len; } + } - void Normalize() - { - float len = Length(); - if (len != 0) - { - x /= len; - y /= len; - z /= len; - } + float Dot(const Vec3 &rhs) const { return x * rhs.x + y * rhs.y + z * rhs.z; } + + Vec3 Cross(const Vec3 &rhs) const + { + return Vec3( + y * rhs.z - z * rhs.y, + z * rhs.x - x * rhs.z, + x * rhs.y - y * rhs.x + ); + } + + void Clamp(const Vec3 &min, const Vec3 &max) + { + x = std::max(min.x, std::min(x, max.x)); + y = std::max(min.y, std::min(y, max.y)); + z = std::max(min.z, std::min(z, max.z)); + } + + static Vec3 Lerp(const Vec3 &a, const Vec3 &b, const float t) + { + return a + (b - a) * t; + } + + static float Distance(const Vec3 &a, const Vec3 &b) + { + return (a - b).Length(); + } + + friend std::ostream &operator<<(std::ostream &os, const Vec3 &v) + { + return os << "(" << v.x << ", " << v.y << ", " << v.z << ")"; + } + + float& operator[](const int i) { + switch (i) { + case 0: return x; + case 1: return y; + case 2: return z; + default: throw std::out_of_range("Vec3 index out of range"); } + } - float Dot(const Vec3 &rhs) const { return x * rhs.x + y * rhs.y + z * rhs.z; } - - Vec3 Cross(const Vec3 &rhs) const - { - return Vec3( - y * rhs.z - z * rhs.y, - z * rhs.x - x * rhs.z, - x * rhs.y - y * rhs.x - ); + const float& operator[](const int i) const { + switch (i) { + case 0: return x; + case 1: return y; + case 2: return z; + default: throw std::out_of_range("Vec3 index out of range"); } + } - void Clamp(const Vec3 &min, const Vec3 &max) - { - x = std::max(min.x, std::min(x, max.x)); - y = std::max(min.y, std::min(y, max.y)); - z = std::max(min.z, std::min(z, max.z)); - } - static Vec3 Lerp(const Vec3 &a, const Vec3 &b, float t) - { - return a + (b - a) * t; - } + }; - static float Distance(const Vec3 &a, const Vec3 &b) - { - return (a - b).Length(); - } + inline Vec3 operator*(const float scalar, const Vec3 &v) { return v * scalar; } - friend std::ostream &operator<<(std::ostream &os, const Vec3 &v) - { - return os << "(" << v.x << ", " << v.y << ", " << v.z << ")"; - } - - inline float& operator[](int i) { - switch (i) { - case 0: return x; - case 1: return y; - case 2: return z; - default: throw std::out_of_range("Vec3 index out of range"); - } - } - - inline const float& operator[](int i) const { - switch (i) { - case 0: return x; - case 1: return y; - case 2: return z; - default: throw std::out_of_range("Vec3 index out of range"); - } - } - }; - inline Vec3 operator*(float scalar, const Vec3 &v) { return v * scalar; } - - - } // namespace types -} // namespace core +} diff --git a/src/src/core/utils/PrimitiveGenerator.cpp b/src/src/core/utils/PrimitiveGenerator.cpp new file mode 100644 index 0000000..cdc37b3 --- /dev/null +++ b/src/src/core/utils/PrimitiveGenerator.cpp @@ -0,0 +1,46 @@ +#include "PrimitiveGenerator.h" +#include + +using core::types::Vec3; + +namespace core::utils { + + void Primitive::CreateQuad(float width, float height, + std::vector& outVerts, std::vector& outIndices) + { + outVerts.clear(); outIndices.clear(); + float hw = width * 0.5f, hh = height * 0.5f; + + outVerts.emplace_back(-hw, -hh, 0.0f); + outVerts.emplace_back( hw, -hh, 0.0f); + outVerts.emplace_back( hw, hh, 0.0f); + outVerts.emplace_back(-hw, hh, 0.0f); + + outIndices = { 0, 1, 2, + 2, 3, 0 }; + } + + void Primitive::CreateCircle(float radius, int segments, + std::vector& outVerts, std::vector& outIndices) + { + outVerts.clear(); outIndices.clear(); + if (segments < 3) segments = 3; + + outVerts.emplace_back(0.0f, 0.0f, 0.0f); + + const float step = 2.0f * static_cast(M_PI) / static_cast(segments); + for (int i = 0; i <= segments; ++i) { + float a = step * static_cast(i); + outVerts.emplace_back(std::cos(a) * radius, + std::sin(a) * radius, + 0.0f); + } + + for (uint32_t i = 1; i < static_cast(segments + 1); ++i) { + outIndices.push_back(0); + outIndices.push_back(i); + outIndices.push_back(i+1); + } + } + +} // namespace core::utils diff --git a/src/src/core/utils/PrimitiveGenerator.h b/src/src/core/utils/PrimitiveGenerator.h new file mode 100644 index 0000000..bb0182c --- /dev/null +++ b/src/src/core/utils/PrimitiveGenerator.h @@ -0,0 +1,22 @@ +#pragma once + + +#include +#include "../types/Vec3.h" + +namespace core::utils { + struct Primitive { + // vertices in outVerts, triangle indices in outIndices + // – Quad centered at origin, width x height + static void CreateQuad(float width, float height, + std::vector& outVerts, + std::vector& outIndices); + + // – Filled circle (triangle‐fan) in XY plane, centered at origin + // segments controls tesselation + static void CreateCircle(float radius, int segments, + std::vector& outVerts, + std::vector& outIndices); + }; +} + diff --git a/src/src/editor/windows/LuaGlobals.cpp b/src/src/editor/windows/LuaGlobals.cpp index 3973173..cf819b4 100644 --- a/src/src/editor/windows/LuaGlobals.cpp +++ b/src/src/editor/windows/LuaGlobals.cpp @@ -10,7 +10,7 @@ #include "../../core/utils/EngineConfig.h" #include -void DrawImGuiWindow() +void DrawLuaGlobalsWindow() { if (!g_engineConfig.settings.show_lua_globals_window) return; diff --git a/src/src/editor/windows/LuaGlobals.h b/src/src/editor/windows/LuaGlobals.h index 655f0d8..e9f60e9 100644 --- a/src/src/editor/windows/LuaGlobals.h +++ b/src/src/editor/windows/LuaGlobals.h @@ -1,4 +1,4 @@ #pragma once -void DrawImGuiWindow(); +void DrawLuaGlobalsWindow();