Optimised FileExplorere Window
This commit is contained in:
parent
0eca42dd94
commit
cfcfafd5a0
7
.idea/dictionaries/project.xml
generated
Normal file
7
.idea/dictionaries/project.xml
generated
Normal file
@ -0,0 +1,7 @@
|
||||
<component name="ProjectDictionaryState">
|
||||
<dictionary name="project">
|
||||
<words>
|
||||
<w>lerp</w>
|
||||
</words>
|
||||
</dictionary>
|
||||
</component>
|
@ -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
|
||||
|
84
imgui.ini
84
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
|
||||
|
@ -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<Object> &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<Object> &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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
|
@ -18,6 +18,10 @@
|
||||
#include "core/utils/Texture.h"
|
||||
|
||||
|
||||
struct Mesh {
|
||||
GLuint VAO;
|
||||
GLsizei indexCount;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
@ -15,10 +15,70 @@
|
||||
#include <imgui.h>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
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<CachedEntry> cacheEntries;
|
||||
|
||||
struct TreeNode {
|
||||
fs::path path;
|
||||
std::vector<TreeNode> 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<bool> 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<void(const TreeNode&)> 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<fs::directory_entry> 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<const ImageAssetInfo*>(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<const ImageAssetInfo*>(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<AssetType,const char*> 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.isDirectory<b.isDirectory;
|
||||
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();
|
||||
});
|
||||
|
||||
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<std::string> 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<std::mutex> lock(s_fileMutex);
|
||||
if (!s_knownFiles.count(pathStr))
|
||||
{
|
||||
s_knownFiles.insert(pathStr);
|
||||
s_loadQueue.push_back(it->path());
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> 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<std::string> 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();
|
||||
|
@ -4,7 +4,8 @@
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
|
||||
|
||||
|
||||
@ -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<bool> 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;
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
@ -4,116 +4,114 @@
|
||||
#include <ostream>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
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
|
||||
}
|
||||
|
46
src/src/core/utils/PrimitiveGenerator.cpp
Normal file
46
src/src/core/utils/PrimitiveGenerator.cpp
Normal file
@ -0,0 +1,46 @@
|
||||
#include "PrimitiveGenerator.h"
|
||||
#include <cmath>
|
||||
|
||||
using core::types::Vec3;
|
||||
|
||||
namespace core::utils {
|
||||
|
||||
void Primitive::CreateQuad(float width, float height,
|
||||
std::vector<Vec3>& outVerts, std::vector<uint32_t>& 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<Vec3>& outVerts, std::vector<uint32_t>& 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<float>(M_PI) / static_cast<float>(segments);
|
||||
for (int i = 0; i <= segments; ++i) {
|
||||
float a = step * static_cast<float>(i);
|
||||
outVerts.emplace_back(std::cos(a) * radius,
|
||||
std::sin(a) * radius,
|
||||
0.0f);
|
||||
}
|
||||
|
||||
for (uint32_t i = 1; i < static_cast<uint32_t>(segments + 1); ++i) {
|
||||
outIndices.push_back(0);
|
||||
outIndices.push_back(i);
|
||||
outIndices.push_back(i+1);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace core::utils
|
22
src/src/core/utils/PrimitiveGenerator.h
Normal file
22
src/src/core/utils/PrimitiveGenerator.h
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <vector>
|
||||
#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<core::types::Vec3>& outVerts,
|
||||
std::vector<uint32_t>& outIndices);
|
||||
|
||||
// – Filled circle (triangle‐fan) in XY plane, centered at origin
|
||||
// segments controls tesselation
|
||||
static void CreateCircle(float radius, int segments,
|
||||
std::vector<core::types::Vec3>& outVerts,
|
||||
std::vector<uint32_t>& outIndices);
|
||||
};
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
#include "../../core/utils/EngineConfig.h"
|
||||
#include <imgui.h>
|
||||
|
||||
void DrawImGuiWindow()
|
||||
void DrawLuaGlobalsWindow()
|
||||
{
|
||||
if (!g_engineConfig.settings.show_lua_globals_window)
|
||||
return;
|
||||
|
@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
void DrawImGuiWindow();
|
||||
void DrawLuaGlobalsWindow();
|
||||
|
Loading…
Reference in New Issue
Block a user