13
13
#include < glm/glm.hpp>
14
14
#include < gtc/type_ptr.hpp>
15
15
#include < imgui.h>
16
+ #include < stack>
16
17
17
18
using namespace ZEngine ;
18
19
using namespace ZEngine ::Helpers;
@@ -58,15 +59,15 @@ namespace Tetragrama::Components
58
59
59
60
void HierarchyViewUIComponent::Render (ZEngine::Rendering::Renderers::GraphicRenderer* const renderer, ZEngine::Hardwares::CommandBuffer* const command_buffer)
60
61
{
61
- auto ctx = reinterpret_cast <EditorContext*>(ParentLayer->ParentContext );
62
- auto render_scene = ctx->CurrentScenePtr -> RenderScene ;
62
+ auto ctx = reinterpret_cast <EditorContext*>(ParentLayer->ParentContext );
63
+ auto current_scene = ctx->CurrentScenePtr ;
63
64
64
65
ImGui::Begin (Name, (CanBeClosed ? &CanBeClosed : NULL ), ImGuiWindowFlags_NoCollapse);
65
66
if (ImGui::BeginPopupContextWindow (Name))
66
67
{
67
68
if (ImGui::MenuItem (" Create Empty" ))
68
69
{
69
- render_scene-> CreateEntityAsync ();
70
+ current_scene-> CreateSceneNode ();
70
71
}
71
72
ImGui::EndPopup ();
72
73
}
@@ -76,8 +77,8 @@ namespace Tetragrama::Components
76
77
// 0 means left buttom
77
78
if (ImGui::IsMouseDown (0 ) && ImGui::IsWindowHovered ())
78
79
{
79
- m_selected_node_identifier = - 1 ;
80
- Messengers::IMessenger::SendAsync<Components::UIComponent, Messengers::EmptyMessage>(EDITOR_COMPONENT_HIERARCHYVIEW_NODE_UNSELECTED, Messengers::EmptyMessage{});
80
+ ctx-> SelectedSceneNode . store (- 1 , std::memory_order_release) ;
81
+ // Messengers::IMessenger::SendAsync<Components::UIComponent, Messengers::EmptyMessage>(EDITOR_COMPONENT_HIERARCHYVIEW_NODE_UNSELECTED, Messengers::EmptyMessage{});
81
82
}
82
83
83
84
RenderGuizmo ();
@@ -87,37 +88,48 @@ namespace Tetragrama::Components
87
88
88
89
void HierarchyViewUIComponent::RenderTreeNodes ()
89
90
{
90
- auto ctx = reinterpret_cast <EditorContext*>(ParentLayer->ParentContext );
91
- auto render_scene = ctx->CurrentScenePtr -> RenderScene ;
91
+ auto ctx = reinterpret_cast <EditorContext*>(ParentLayer->ParentContext );
92
+ auto current_scene = ctx->CurrentScenePtr ;
92
93
93
- auto root_nodes = render_scene->GetRootSceneNodes ();
94
+ if (current_scene->IsDirty ())
95
+ {
96
+ return ;
97
+ }
94
98
95
- for (int node : root_nodes )
99
+ for (int i = 0 ; i < ( int ) current_scene-> Hierarchies . size (); ++i )
96
100
{
97
- RenderSceneNodeTree (node);
101
+ if (!current_scene->IsSceneNodeDeleted (i) && current_scene->Hierarchies [i].Parent == -1 )
102
+ {
103
+ RenderNode (current_scene, i, ctx->SelectedSceneNode );
104
+ }
98
105
}
99
106
}
100
107
101
108
void HierarchyViewUIComponent::RenderGuizmo ()
102
109
{
103
- if (m_selected_node_identifier <= -1 )
110
+ auto ctx = reinterpret_cast <EditorContext*>(ParentLayer->ParentContext );
111
+ auto current_scene = ctx->CurrentScenePtr ;
112
+
113
+ if (current_scene->IsDirty ())
104
114
{
105
115
return ;
106
116
}
107
117
108
- // auto entity_wrapper = GraphicScene::GetSceneNodeEntityWrapper(m_selected_node_identifier);
109
- auto ctx = reinterpret_cast <EditorContext*>(ParentLayer->ParentContext );
110
- auto render_scene = ctx->CurrentScenePtr ->RenderScene ;
118
+ int selected_node = ctx->SelectedSceneNode .load (std::memory_order_acquire);
119
+ if (selected_node == -1 )
120
+ {
121
+ return ;
122
+ }
111
123
112
124
if (auto active_editor_camera = ctx->CameraControllerPtr )
113
125
{
114
126
auto camera = active_editor_camera->GetCamera ();
115
127
const auto camera_projection = camera->GetPerspectiveMatrix ();
116
128
const auto camera_view_matrix = camera->GetViewMatrix ();
117
129
118
- auto & global_transform = render_scene-> GetSceneNodeGlobalTransform (m_selected_node_identifier) ;
130
+ auto & global_transform = current_scene-> GlobalTransforms [selected_node] ;
119
131
auto initial_transform = global_transform;
120
- auto & local_transform = render_scene-> GetSceneNodeLocalTransform (m_selected_node_identifier) ;
132
+ auto & local_transform = current_scene-> LocalTransforms [selected_node] ;
121
133
122
134
if (camera && IDevice::As<Keyboard>()->IsKeyPressed (ZENGINE_KEY_F, Engine::GetWindow ()))
123
135
{
@@ -140,7 +152,7 @@ namespace Tetragrama::Components
140
152
141
153
auto delta_transform = glm::inverse (initial_transform) * global_transform;
142
154
local_transform = local_transform * delta_transform;
143
- render_scene ->MarkSceneNodeAsChanged (m_selected_node_identifier);
155
+ // current_scene ->MarkSceneNodeAsChanged(m_selected_node_identifier);
144
156
145
157
if (ImGuizmo::IsUsing ())
146
158
{
@@ -154,72 +166,139 @@ namespace Tetragrama::Components
154
166
}
155
167
}
156
168
157
- void HierarchyViewUIComponent::RenderSceneNodeTree ( int node_identifier )
169
+ void HierarchyViewUIComponent::RenderNode (EditorScene* scene, int root_id, std::atomic_int& selected_node )
158
170
{
159
- if (node_identifier < 0 )
171
+ struct StackEntry
160
172
{
161
- return ;
162
- }
173
+ int node_id;
174
+ bool open;
175
+ };
163
176
164
- auto ctx = reinterpret_cast <EditorContext*>(ParentLayer-> ParentContext ) ;
165
- auto render_scene = ctx-> CurrentScenePtr -> RenderScene ;
177
+ std::stack<StackEntry> stack ;
178
+ stack. push ({root_id, false }) ;
166
179
167
- const auto & node_hierarchy = render_scene->GetSceneNodeHierarchy (node_identifier);
168
- auto node_name = render_scene->GetSceneNodeName (node_identifier);
169
- auto node_identifier_string = fmt::format (" SceneNode_{0}" , node_identifier);
170
- auto flags = (node_hierarchy.FirstChild < 0 ) ? (ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_Bullet | m_node_flag) : m_node_flag;
171
- flags |= (m_selected_node_identifier == node_identifier) ? ImGuiTreeNodeFlags_Selected : 0 ;
172
- auto label = (!node_name.empty ()) ? std::string (node_name) : fmt::format (" Node_{0}" , node_identifier);
173
- bool is_node_opened = ImGui::TreeNodeEx (node_identifier_string.c_str (), flags, " %s" , label.c_str ());
174
-
175
- if (ImGui::IsItemClicked ())
180
+ while (!stack.empty ())
176
181
{
177
- m_selected_node_identifier = node_identifier;
182
+ auto entry = stack.top ();
183
+ stack.pop ();
178
184
179
- auto entity = render_scene->GetSceneNodeEntityWrapper (m_selected_node_identifier);
180
- Messengers::IMessenger::SendAsync<Components::UIComponent, Messengers::GenericMessage<SceneEntity>>(EDITOR_COMPONENT_HIERARCHYVIEW_NODE_SELECTED, Messengers::GenericMessage<SceneEntity>{std::move (entity)});
181
- }
185
+ // Handle manual TreePop marker
186
+ if (entry.node_id == -1 )
187
+ {
188
+ ImGui::TreePop ();
189
+ continue ;
190
+ }
182
191
183
- if (is_node_opened)
184
- {
185
- /*
186
- * Popup features
187
- */
188
- bool request_entity_removal = false ;
189
- if (ImGui::BeginPopupContextItem (node_identifier_string.c_str ()))
192
+ if (scene->IsSceneNodeDeleted (entry.node_id ))
193
+ {
194
+ continue ;
195
+ }
196
+ const auto & node = scene->Hierarchies [entry.node_id ];
197
+
198
+ auto name_id = scene->NodeNames [entry.node_id ];
199
+
200
+ bool is_selected = (selected_node == entry.node_id );
201
+ bool has_children = (node.FirstChild != -1 );
202
+
203
+ ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_SpanFullWidth;
204
+ if (!has_children)
205
+ flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen;
206
+ if (is_selected)
207
+ flags |= ImGuiTreeNodeFlags_Selected;
208
+
209
+ auto node_id = fmt::format (" SceneNode_{0}" , entry.node_id );
210
+ bool open = ImGui::TreeNodeEx (node_id.c_str (), flags, " %s" , scene->Names [name_id].c_str ());
211
+
212
+ if (ImGui::IsItemClicked (ImGuiMouseButton_Left))
213
+ {
214
+ selected_node.store (entry.node_id , std::memory_order_release);
215
+ }
216
+
217
+ // === Drag source ===
218
+ if (ImGui::BeginDragDropSource ())
219
+ {
220
+ ImGui::SetDragDropPayload (" NODE_DRAG" , &entry.node_id , sizeof (int ));
221
+ ImGui::EndDragDropSource ();
222
+ }
223
+
224
+ // === Drop target ===
225
+ if (ImGui::BeginDragDropTarget ())
226
+ {
227
+ if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload (" NODE_DRAG" ))
228
+ {
229
+ int dragged_id = *(const int *) payload->Data ;
230
+ if (dragged_id != entry.node_id && !scene->IsSceneNodeDeleted (dragged_id))
231
+ {
232
+ // prevent making it a child of itself or its descendants
233
+ int test = entry.node_id ;
234
+ bool is_descendant = false ;
235
+ while (test != -1 )
236
+ {
237
+ if (test == dragged_id)
238
+ {
239
+ is_descendant = true ;
240
+ break ;
241
+ }
242
+ test = scene->Hierarchies [test].Parent ;
243
+ }
244
+
245
+ if (!is_descendant)
246
+ {
247
+ scene->ReparentNode (dragged_id, entry.node_id );
248
+ }
249
+ }
250
+ }
251
+ ImGui::EndDragDropTarget ();
252
+ }
253
+
254
+ if (ImGui::BeginPopupContextItem (node_id.c_str ()))
190
255
{
191
256
if (ImGui::MenuItem (" Create Empty child" ))
192
257
{
193
- render_scene-> CreateEntityAsync ( " Empty Entity " , m_selected_node_identifier, node_hierarchy .DepthLevel + 1 );
258
+ scene-> CreateSceneNode (entry. node_id , node .DepthLevel + 1 );
194
259
}
195
260
196
261
if (ImGui::MenuItem (" Rename" ))
197
262
{
198
263
}
264
+
199
265
if (ImGui::MenuItem (" Delete" ))
200
266
{
201
- Messengers::IMessenger::SendAsync<Components::UIComponent, Messengers::EmptyMessage>(EDITOR_COMPONENT_HIERARCHYVIEW_NODE_DELETED, Messengers::EmptyMessage{});
202
- request_entity_removal = true ;
267
+ scene->RemoveSceneNode (entry.node_id );
203
268
}
204
269
ImGui::EndPopup ();
205
270
}
206
271
207
- if (request_entity_removal )
272
+ if (open && has_children )
208
273
{
209
- render_scene-> RemoveNodeAsync (node_identifier);
210
- }
274
+ // Push TreePop manually
275
+ stack. push ({- 1 , 0 }); // Marker for TreePop
211
276
212
- if (node_hierarchy. FirstChild > - 1 )
213
- {
214
- auto sibling_scene_node_collection = render_scene-> GetSceneNodeSiblingCollection (node_hierarchy. FirstChild ) ;
215
- // We consider first child as sibling node
216
- sibling_scene_node_collection. emplace ( std::begin (sibling_scene_node_collection), node_hierarchy. FirstChild );
217
- for (auto sibling_identifier : sibling_scene_node_collection )
277
+ // Push children in reverse order
278
+ auto scratch = ZGetScratch (&ParentLayer-> LayerArena );
279
+ ZEngine::Core::Containers::Array< int > children ;
280
+ children. init (scratch. Arena , 5 );
281
+
282
+ for (int child = node. FirstChild ; child != - 1 ; child = scene-> Hierarchies [child]. RightSibling )
218
283
{
219
- RenderSceneNodeTree (sibling_identifier);
284
+ if (!scene->IsSceneNodeDeleted (child))
285
+ {
286
+ children.push (child);
287
+ }
220
288
}
289
+
290
+ for (int i = static_cast <int >(children.size ()) - 1 ; i >= 0 ; --i)
291
+ {
292
+ stack.push ({children[i], false });
293
+ }
294
+
295
+ ZReleaseScratch (scratch);
296
+ }
297
+ else if (!has_children && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
298
+ {
299
+ // Only pop if not marked as Leaf
300
+ ImGui::TreePop ();
221
301
}
222
- ImGui::TreePop ();
223
302
}
224
303
}
225
304
} // namespace Tetragrama::Components
0 commit comments