Olympe Engine 2.0
2D Game Engine with ECS Architecture
Loading...
Searching...
No Matches
AssetBrowser.cpp
Go to the documentation of this file.
1/*
2 * Olympe Blueprint Editor - Asset Browser Implementation
3 * Frontend component that uses BlueprintEditor backend for asset data
4 */
5
6#include "AssetBrowser.h"
7#include "BlueprintEditor.h"
10#include "../TaskSystem/TaskGraphTypes.h"
11#include "../third_party/imgui/imgui.h"
12#include <algorithm>
13#include <iostream>
14
15namespace Olympe
16{
18 : m_TypeFilterSelection(0)
19 {
20 m_SearchBuffer[0] = '\0';
21 m_AvailableTypes = {"All", "EntityBlueprint", "BehaviorTree", "Prefab", "Trigger", "FX", "Sound"};
22 }
23
27
29 {
30 // Set the root path in the backend
32
33 std::cout << "AssetBrowser: Initialized with root path: " << assetsRootPath << std::endl;
34 }
35
37 {
38 // Delegate to backend
40
41 std::cout << "AssetBrowser: Refreshed asset tree from backend" << std::endl;
42 }
43
44 bool AssetBrowser::PassesFilter(const std::shared_ptr<AssetNode>& node) const
45 {
46 // Directories always pass
47 if (node->isDirectory)
48 return true;
49
50 // Apply type filter
52 {
54 if (node->type != selectedType)
55 return false;
56 }
57
58 // Apply search filter
59 if (m_Filter.searchQuery.empty())
60 return true;
61
62 // Case-insensitive search in filename
63 std::string lowerName = node->name;
64 std::string lowerQuery = m_Filter.searchQuery;
65 std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), ::tolower);
66 std::transform(lowerQuery.begin(), lowerQuery.end(), lowerQuery.begin(), ::tolower);
67
68 return lowerName.find(lowerQuery) != std::string::npos;
69 }
70
72 {
73 // Search box
74 ImGui::SetNextItemWidth(200.0f);
75 if (ImGui::InputText("##search", m_SearchBuffer, sizeof(m_SearchBuffer)))
76 {
78 }
79
80 ImGui::SameLine();
81 ImGui::Text("Search");
82
83 ImGui::Separator();
84 // Type filter combo
85 //ImGui::SameLine(0.0f, 20.0f);
86 ImGui::SetNextItemWidth(150.0f);
87 if (ImGui::BeginCombo("##typefilter", m_AvailableTypes[m_TypeFilterSelection].c_str()))
88 {
89 for (int i = 0; i < (int)m_AvailableTypes.size(); i++)
90 {
92 if (ImGui::Selectable(m_AvailableTypes[i].c_str(), is_selected))
94 }
95 ImGui::EndCombo();
96 }
97 ImGui::SameLine();
98 ImGui::Text("Type Filter");
99
100 // Refresh button
101 ImGui::SameLine(0.0f, 20.0f);
102 if (ImGui::Button("Refresh"))
103 {
104 Refresh();
105 }
106
107 ImGui::Separator();
108 }
109
110 void AssetBrowser::RenderTreeNode(const std::shared_ptr<AssetNode>& node)
111 {
112 if (!node)
113 return;
114
115 // Skip if doesn't pass filter
116 if (!PassesFilter(node))
117 {
118 // But still check children for directories
119 if (node->isDirectory)
120 {
121 for (const auto& child : node->children)
123 }
124 return;
125 }
126
128
129 if (node->fullPath == m_SelectedAssetPath)
131
132 if (!node->isDirectory)
134
135 std::string label = node->name;
136 if (!node->isDirectory && !node->type.empty())
137 label += " [" + node->type + "]";
138
139 bool node_open = ImGui::TreeNodeEx((void*)(intptr_t)node.get(), flags, "%s", label.c_str());
140
141 // Handle selection (single click)
142 if (ImGui::IsItemClicked())
143 {
144 if (!node->isDirectory)
145 {
146 m_SelectedAssetPath = node->fullPath;
148 std::cout << "AssetBrowser: Selected asset: " << m_SelectedAssetPath << std::endl;
149 }
150 }
151
152 // Handle double-click to open
153 if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(0))
154 {
155 if (!node->isDirectory)
156 {
157 std::cout << "AssetBrowser: Double-clicked asset: " << node->fullPath << std::endl;
158
159 // Check if this is a graph type - open in appropriate editor
160 if (node->type == "BehaviorTree" || node->type == "HFSM" ||
161 node->type == "VisualScript" || node->type == "TaskGraph" ||
162 node->type == "Generic") // Allow opening Generic files to fix them
163 {
164 std::cout << "AssetBrowser: Opening " << node->type << " in editor" << std::endl;
166 }
167 // Otherwise, use the legacy callback if set (for EntityBlueprint, etc.)
168 else if (m_OnAssetOpen)
169 {
170 std::cout << "AssetBrowser: Opening asset via callback" << std::endl;
171 m_OnAssetOpen(node->fullPath);
172 }
173 }
174 }
175
176 // Tooltip for double-click action
177 if (ImGui::IsItemHovered())
178 {
179 if (node->type == "BehaviorTree" || node->type == "HFSM" ||
180 node->type == "VisualScript" || node->type == "TaskGraph" ||
181 node->type == "Generic")
182 {
183 ImGui::SetTooltip("Double-click to open in editor");
184 }
185 else if (!node->isDirectory)
186 {
187 ImGui::SetTooltip("Double-click to open");
188 }
189 }
190
191 if (node_open)
192 {
193 if (node->isDirectory)
194 {
195 for (const auto& child : node->children)
197 }
198 ImGui::TreePop();
199 }
200 }
201
203 {
204 if (ImGui::Begin("Asset Browser"))
205 {
207 }
208 ImGui::End();
209 }
210
212 {
213 // ===== Blueprint Files (Node Palette now integrated into right panel) =====
214 // Render the tree with filter UI
216
217 ImGui::Separator();
218
219 // Get asset tree from backend
221
222 if (rootNode)
223 {
224 // Render the tree starting from children (skip root "Blueprints" node)
225 for (const auto& child : rootNode->children)
227 }
228 else
229 {
230 // Check if backend has an error
232 {
233 ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f),
234 "Error: %s", BlueprintEditor::Get().GetLastError().c_str());
235 }
236 else
237 {
238 ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f),
239 "No blueprint files found.");
240 }
241 }
242 }
243
244 /*
245 // LEGACY METHOD - No longer used (Runtime Entities tab removed)
246 void AssetBrowser::RenderRuntimeEntities()
247 {
248 // Get runtime entities from BlueprintEditor backend
249 const auto& entities = BlueprintEditor::Get().GetRuntimeEntities();
250
251 if (entities.empty())
252 {
253 ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "No runtime entities.");
254 ImGui::TextWrapped("Create entities with World::CreateEntity() to see them here.");
255 return;
256 }
257
258 // Use EntityInspectorManager to get entity names and info
259 const auto& inspector = Olympe::EntityInspectorManager::Get();
260 if (!inspector.IsInitialized())
261 {
262 ImGui::TextColored(ImVec4(1.0f, 0.7f, 0.0f, 1.0f),
263 "Inspector not initialized.");
264 return;
265 }
266
267 // Get current selection
268 uint64_t selectedEntity = BlueprintEditor::Get().GetSelectedEntity();
269
270 // Render each entity as a selectable item
271 ImGui::BeginChild("RuntimeEntitiesScroll", ImVec2(0, 200), true);
272
273 for (uint64_t entityId : entities)
274 {
275 bool isSelected = (selectedEntity == entityId);
276
277 // Get entity info from inspector
278 EntityInfo info = inspector.GetEntityInfo(entityId);
279 std::string displayName = info.name;
280 if (displayName.empty())
281 {
282 displayName = "Entity_" + std::to_string(entityId);
283 }
284
285 // Add component count badge
286 displayName += " (" + std::to_string(info.componentTypes.size()) + " comp)";
287
288 // Selectable item
289 if (ImGui::Selectable(displayName.c_str(), isSelected))
290 {
291 BlueprintEditor::Get().SetSelectedEntity(entityId);
292 }
293
294 // Tooltip on hover
295 if (ImGui::IsItemHovered())
296 {
297 ImGui::BeginTooltip();
298 ImGui::Text("Entity ID: %llu", entityId);
299 ImGui::Text("Components: %zu", info.componentTypes.size());
300 if (!info.componentTypes.empty())
301 {
302 ImGui::Separator();
303 for (const auto& compType : info.componentTypes)
304 {
305 ImGui::BulletText("%s", compType.c_str());
306 }
307 }
308 ImGui::EndTooltip();
309 }
310 }
311
312 ImGui::EndChild();
313 }
314 */
315
317 {
318 ImGui::TextColored(ImVec4(0.5f, 1.0f, 0.5f, 1.0f),
319 "Drag nodes to the graph to add them");
320 ImGui::Separator();
321
322 // Helper lambda for creating draggable node items
323 auto RenderDraggableNode = [](const char* label, TaskNodeType nodeType, const char* tooltip) {
324 ImGui::Selectable(label);
325 if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None))
326 {
327 uint8_t enumValue = static_cast<uint8_t>(nodeType);
328 ImGui::SetDragDropPayload("VS_NODE_TYPE_ENUM", &enumValue, sizeof(uint8_t));
329 ImGui::Text("%s", label);
330 ImGui::EndDragDropSource();
331 }
332
333 if (ImGui::IsItemHovered())
334 {
335 ImGui::SetTooltip("%s", tooltip);
336 }
337 };
338
339 // ===== Flow Control Nodes =====
340 if (ImGui::CollapsingHeader("Flow Control", ImGuiTreeNodeFlags_DefaultOpen))
341 {
342 RenderDraggableNode("EntryPoint", TaskNodeType::EntryPoint, "Entry point of the graph (required)");
343 RenderDraggableNode("Branch", TaskNodeType::Branch, "If/Else conditional branch");
344 RenderDraggableNode("Sequence", TaskNodeType::VSSequence, "Execute pins in sequential order");
345 RenderDraggableNode("While", TaskNodeType::While, "Conditional loop");
346 RenderDraggableNode("ForEach", TaskNodeType::ForEach, "Iterate over blackboard list");
347 RenderDraggableNode("DoOnce", TaskNodeType::DoOnce, "Execute only once (until reset)");
348 RenderDraggableNode("Delay", TaskNodeType::Delay, "Wait for N seconds");
349 RenderDraggableNode("Switch", TaskNodeType::Switch, "Multi-branch on value");
350 }
351
352 // ===== Task Nodes =====
353 if (ImGui::CollapsingHeader("Tasks", ImGuiTreeNodeFlags_DefaultOpen))
354 {
355 RenderDraggableNode("AtomicTask", TaskNodeType::AtomicTask, "Execute an atomic task");
356 }
357
358 // ===== Blackboard Nodes =====
359 if (ImGui::CollapsingHeader("Blackboard", ImGuiTreeNodeFlags_DefaultOpen))
360 {
361 RenderDraggableNode("GetBBValue", TaskNodeType::GetBBValue, "Read a blackboard variable");
362 RenderDraggableNode("SetBBValue", TaskNodeType::SetBBValue, "Write a blackboard variable");
363 }
364
365 // ===== Math & Logic Nodes =====
366 if (ImGui::CollapsingHeader("Math & Logic", ImGuiTreeNodeFlags_DefaultOpen))
367 {
368 RenderDraggableNode("MathOp", TaskNodeType::MathOp, "Arithmetic operation (+, -, *, /)");
369 }
370
371 // ===== Advanced Nodes =====
372 if (ImGui::CollapsingHeader("Advanced", ImGuiTreeNodeFlags_DefaultOpen))
373 {
374 RenderDraggableNode("SubGraph", TaskNodeType::SubGraph, "Call another graph as a subgraph");
375 }
376
377 ImGui::Separator();
378 ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f),
379 "Tip: Drag & drop nodes onto the graph canvas");
380 }
381
383 {
384 return m_SelectedAssetPath;
385 }
386
388 {
389 return !m_SelectedAssetPath.empty();
390 }
391
392 void AssetBrowser::SetAssetOpenCallback(std::function<void(const std::string&)> callback)
393 {
395 }
396}
ComponentTypeID GetComponentTypeID_Static()
Definition ECS_Entity.h:56
bool HasSelection() const
void RenderTreeNode(const std::shared_ptr< AssetNode > &node)
std::vector< std::string > m_AvailableTypes
std::function< void(const std::string &)> m_OnAssetOpen
std::string m_SelectedAssetPath
void Initialize(const std::string &assetsRootPath)
void SetAssetOpenCallback(std::function< void(const std::string &)> callback)
bool PassesFilter(const std::shared_ptr< AssetNode > &node) const
std::string GetSelectedAssetPath() const
std::shared_ptr< AssetNode > GetAssetTree() const
void SetAssetRootPath(const std::string &path)
void SelectAsset(const std::string &assetPath)
void OpenGraphInEditor(const std::string &assetPath)
static BlueprintEditor & Get()
< Provides AssetID and INVALID_ASSET_ID
TaskNodeType
Identifies the role of a node in the task graph.
@ AtomicTask
Leaf node that executes a single atomic task.
@ While
Conditional loop (Loop / Completed exec outputs)
@ SubGraph
Sub-graph call (SubTask)
@ DoOnce
Single-fire execution (reset via Reset pin)
@ Delay
Timer (Completed exec output after N seconds)
@ GetBBValue
Data node – reads a Blackboard key.
@ MathOp
Data node – arithmetic operation (+, -, *, /)
@ SetBBValue
Data node – writes a Blackboard key.
@ ForEach
Iterate over BB list (Loop Body / Completed exec outputs)
@ Switch
Multi-branch on value (N exec outputs)
@ EntryPoint
Unique entry node for VS graphs (replaces Root)
@ Branch
If/Else conditional (Then / Else exec outputs)
@ VSSequence
Execute N outputs in order ("VS" prefix avoids collision with BT Sequence=1)
std::string searchQuery