Unravel Engine C++ Reference
Loading...
Searching...
No Matches
editing_manager.h
Go to the documentation of this file.
1#pragma once
2#include "editor_actions.h"
3#include "uuid/uuid.h"
4
5#include <base/basetypes.hpp>
6#include <context/context.hpp>
10#include <math/math.h>
11#include <uuid/uuid.h>
12
16#include "actions/actions.h"
17
18namespace unravel
19{
20
22{
24 std::vector<std::shared_ptr<editing_action_t>> pending_actions; // Actions waiting to be executed
25 std::stack<bool> undo_stack_enabled;
26
27 struct selection
28 {
29 std::vector<entt::meta_any> objects{entt::meta_any{}};
30 };
31
32 struct focused
33 {
34 entt::meta_any object;
36
37 fs::path focus_path{};
38 };
39
40 struct snap
41 {
43 math::vec3 translation_snap = {1.0f, 1.0f, 1.0f};
45 float rotation_degree_snap = 15.0f;
47 float scale_snap = 0.1f;
48 };
49
50 struct grid
51 {
52 float opacity = 0.75f;
53 bool depth_aware {true};
54 };
55
57 {
58 float opacity = 0.75f;
59 float size = 0.5f;
60 bool depth_aware {false};
61 bool show_camera {true};
62 bool show_light {true};
64 bool show_audio_source {true};
66 };
67
69 {
72 math::vec4 selection_wireframe_color {0.15f, 0.85f, 0.75f, 1.0f};
74 bool show_camera {true};
75 bool show_light {true};
77 bool show_volume {true};
78 bool show_model {true};
79 bool show_model_bounds {false};
81 bool show_text {true};
87 };
88
90 {
91 int num_nodes = 2;
92 };
93
100
101 enum class editing_mode
102 {
103 scene,
104 prefab
105 };
106
107 enum class save_option
108 {
109 yes, // Save changes
110 no, // Don't save changes
111 prompt // Prompt the user whether to save changes
112 };
113
114 auto init(rtti::context& ctx) -> bool;
115 auto deinit(rtti::context& ctx) -> bool;
116
118 void on_play_begin(rtti::context& ctx);
121 void on_script_recompile(rtti::context& ctx, const std::string& protocol, uint64_t version);
123
124 void sync_prefab_entity(rtti::context& ctx, entt::handle entity, const asset_handle<prefab>& pfb);
126 auto get_select_mode() const -> select_mode;
127
128 //-----------------------------------------------------------------------------
129 // Name : select ()
133 //-----------------------------------------------------------------------------
134 void focus(entt::meta_any object);
135 void focus_path(const fs::path& object);
136
137 template<typename T>
139 {
140 focus(entry);
141 focus_path(fs::resolve_protocol(fs::path(entry.id()).parent_path()));
142 }
143
144 //-----------------------------------------------------------------------------
145 // Name : unselect ()
149 //-----------------------------------------------------------------------------
150 void unselect(bool clear_selection_tools = true);
151 void unfocus();
152
153 //-----------------------------------------------------------------------------
154 // Name : try_unselect ()
158 //-----------------------------------------------------------------------------
159 template<typename T>
161 {
163 {
164 unselect();
165 }
166 }
167
168 template<typename T>
170 {
171 if(focused_data.object.type() == entt::resolve<T>())
172 {
173 unfocus();
174 }
175 }
176
177 template<typename T>
178 auto is_selected(const T& entry) -> bool
179 {
180 for(const auto& object : selection_data.objects)
181 {
182 if(is_selected_impl(entry, object))
183 {
184 return true;
185 }
186 }
187 return false;
188 }
189
190 template<typename T>
191 auto is_selected_type() -> bool
192 {
193 const auto& selected = get_active_selection();
194
195 return selected && selected.type() == entt::resolve<T>();
196 }
197
198 auto get_active_selection() const -> const entt::meta_any&
199 {
200 return selection_data.objects.back();
201 }
202
203 auto get_active_selection() -> entt::meta_any&
204 {
205 return selection_data.objects.back();
206 }
207
208 auto get_selections() const -> hpp::span<const entt::meta_any>
209 {
210 return selection_data.objects;
211 }
212
213 auto get_selections() -> hpp::span<entt::meta_any>
214 {
215 return selection_data.objects;
216 }
217
218 template<typename T>
219 auto get_active_selection_as() const -> const T&
220 {
221 return get_active_selection().cast<const T&>();
222 }
223
224 template<typename T>
226 {
227 auto& active = get_active_selection();
228 if(active.type() == entt::resolve<T>())
229 {
230 return &active.cast<T&>();
231 }
232
233 return nullptr;
234 }
235
236 template<typename T>
237 auto try_get_active_selection_as() const -> const T*
238 {
239 const auto& active = get_active_selection();
240 if(active.type() == entt::resolve<T>())
241 {
242 return &active.cast<T&>();
243 }
244
245 return nullptr;
246 }
247
248 template<typename T>
249 auto try_get_selections_as() const -> std::vector<const T*>
250 {
251 std::vector<T*> result;
252
253 for(const auto& obj : selection_data.objects)
254 {
255 if(obj.type() == entt::resolve<T>())
256 {
257 result.emplace_back(&obj.cast<T&>());
258 }
259 }
260
261 return result;
262 }
263
264 template<typename T>
265 auto try_get_selections_as() -> std::vector<T*>
266 {
267 std::vector<T*> result;
268
269 for(auto& obj : selection_data.objects)
270 {
271 if(obj.type() == entt::resolve<T>())
272 {
273 result.emplace_back(&obj.cast<T&>());
274 }
275 }
276
277 return result;
278 }
279
280 template<typename T>
281 auto try_get_selections_as_copy() const -> std::vector<T>
282 {
283 std::vector<T> result;
284
285 for(const auto& obj : selection_data.objects)
286 {
287 if(obj.type() == entt::resolve<T>())
288 {
289 result.emplace_back(obj.cast<T>());
290 }
291 }
292
293 return result;
294 }
295
296 template<typename T>
297 auto is_focused(const T& entry) -> bool
298 {
299 const auto& focused = focused_data.object;
300
301 if(focused.type() != entt::resolve<T>())
302 {
303 return false;
304 }
305
306 return focused.cast<T>() == entry;
307 }
308
309 template<typename T>
310 auto is_focused(const asset_handle<T>& entry) -> bool
311 {
312 const auto& focused = focused_data.object;
313
314 if(focused.type() == entt::resolve<asset_handle<T>>())
315 {
316 return focused.cast<asset_handle<T>>() == entry;
317 }
318
319 if(focused.type() != entt::resolve<fs::path>())
320 {
321 return false;
322 }
323
324 return focused.cast<fs::path>() == fs::resolve_protocol(entry.id());
325 }
326
327 template<typename T>
328 auto try_get_active_focus_as() const -> const T*
329 {
330 const auto& focused = focused_data.object;
331 if(focused.type() == entt::resolve<T>())
332 {
333 return focused.try_cast<T>();
334 }
335
336 return nullptr;
337 }
338
339 template<typename T>
340 void unselect(const T& entry)
341 {
342 // Capture the old selection state before making changes
343 std::vector<entt::meta_any> old_selection = selection_data.objects;
344
345 // Perform the unselect operation
346 unselect_impl(entry);
347
348 // Capture the new selection state and create action
349 std::vector<entt::meta_any> new_selection = selection_data.objects;
350
351 // Only create action if selection actually changed
352 bool selection_changed = old_selection.size() != new_selection.size();
353 if (!selection_changed && !old_selection.empty())
354 {
355 // Check if contents are different
356 for (size_t i = 0; i < old_selection.size() && !selection_changed; ++i)
357 {
358 if (i >= new_selection.size() || old_selection[i] != new_selection[i])
359 {
360 selection_changed = true;
361 break;
362 }
363 }
364 }
365
366 if (selection_changed)
367 {
369 queue_action("Unselect", std::make_shared<selection_action_t>(this, old_selection, new_selection, false));
371 }
372 }
373
374 template<typename T>
375 void select(const T& entry, select_mode mode = select_mode::normal, std::string hint = "")
376 {
377 if(hint.empty())
378 {
379 if constexpr(std::is_same_v<T, entt::handle>)
380 {
381
382 if(auto tag = entry.template try_get<tag_component>())
383 {
384 hint = tag->name + " (Entity)";
385 }
386 else
387 {
388 hint = "Entity";
389 }
390 }
391 }
392 // Capture the old selection state before making changes
393 std::vector<entt::meta_any> old_selection = selection_data.objects;
394
395 // Perform the select operation
396 select_impl(entry, mode);
397
398 // Capture the new selection state and create action
399 std::vector<entt::meta_any> new_selection = selection_data.objects;
400
401 // Only create action if selection actually changed
402 bool selection_changed = old_selection.size() != new_selection.size();
403 if (!selection_changed)
404 {
405 // Check if contents are different
406 for (size_t i = 0; i < old_selection.size() && !selection_changed; ++i)
407 {
408 if (i >= new_selection.size() || old_selection[i] != new_selection[i])
409 {
410 selection_changed = true;
411 break;
412 }
413 }
414 }
415
416 if (selection_changed)
417 {
419 queue_action("Select " + hint, std::make_shared<selection_action_t>(this, old_selection, new_selection, true));
421 }
422
423 // auto& ctx = engine::context();
424 // auto& ui_ev = ctx.get_cached<ui_events>();
425 // ui_ev.on_selection_changed();
426 }
427
428 // Unified action system - all actions go through this interface
429 // Whether an action is undoable depends on the action's is_undoable() method
430 //
431 // Usage examples:
432 // 1. Non-undoable action: do_action("Quick Fix", []() { /* do something */ });
433 // 2. Undoable action: do_action("Move", []() { move(); }, []() { restore(); });
434 // 3. Custom action: do_action<transform_move_action_t>("Move Entity", entity, old_pos, new_pos);
435 // 4. Manual undo/redo: undo(), redo(), can_undo(), can_redo()
436
437 // Add an action with a lambda/function (non-undoable)
438 void do_action(const std::string& name, const std::function<void()>& action);
439
440 // Add an action with both do and undo lambdas (undoable)
441 void do_action(const std::string& name, const std::function<void()>& do_action, const std::function<void()>& undo_action);
442
443 // Add any action object (undoable or non-undoable determined by action itself)
444 void do_action(const std::string& name, std::shared_ptr<editing_action_t> action);
445
446 template<typename ActionType, typename... Args>
447 void do_action(const std::string& name, Args&&... args)
448 {
449 auto action = std::make_shared<ActionType>(std::forward<Args>(args)...);
450 do_action(name, std::move(action));
451 }
452
453 void queue_action(const std::string& name, const std::function<void()>& action);
454
455 // Add an action with both do and undo lambdas (undoable)
456 void queue_action(const std::string& name, const std::function<void()>& do_action, const std::function<void()>& undo_action);
457
458 // Add any action object (undoable or non-undoable determined by action itself)
459 void queue_action(const std::string& name, std::shared_ptr<editing_action_t> action);
460
461 template<typename ActionType, typename... Args>
462 void queue_action(const std::string& name, Args&&... args)
463 {
464 auto action = std::make_shared<ActionType>(std::forward<Args>(args)...);
465 queue_action(name, std::move(action));
466 }
467
468 void add_action(const std::string& name, std::shared_ptr<editing_action_t> action, bool immediate = true);
469
470
473
474 // Execute all pending actions (called automatically each frame)
475 void execute_actions();
476
477 // Undo/Redo operations
478 auto undo() -> std::shared_ptr<editing_action_t>;
479 auto redo() -> std::shared_ptr<editing_action_t>;
480 auto can_undo() const -> bool { return undo_stack.can_undo(); }
481 auto can_redo() const -> bool { return undo_stack.can_redo(); }
482
483
484 // Pending actions management
485 auto has_pending_actions() const -> bool { return !pending_actions.empty(); }
486 auto get_pending_actions_count() const -> size_t { return pending_actions.size(); }
487
488 auto has_unsaved_changes() const -> bool { return has_unsaved_changes_; }
489 void clear_unsaved_changes() { has_unsaved_changes_ = false; }
490
491 void clear(bool clear_unsaved = true);
492
493 // Prefab editing mode methods
494 void enter_prefab_mode(rtti::context& ctx, const asset_handle<prefab>& prefab, bool auto_save = false);
496 auto is_prefab_mode() const -> bool { return current_mode == editing_mode::prefab; }
498
499 // Returns the active scene based on the current edit mode
500 auto get_active_scene(rtti::context& ctx) -> scene*;
501
502 void unload_scenes_scripting(const std::vector<scene*>& scenes);
503
504 void on_action_executed(std::shared_ptr<editing_action_t> action);
505
507 bool show_grid = true;
509 bool show_icon_gizmos = true;
516 ImGuizmo::OPERATION operation = ImGuizmo::TRANSLATE;
518 ImGuizmo::MODE mode = ImGuizmo::LOCAL;
521
523
529
531
532 // Current editing mode
534
535 // Currently edited prefab
537
538 // The entity created from the prefab that we're editing
539 entt::handle prefab_entity;
540
541 // Separate scene for prefab editing
542 scene prefab_scene{"prefab_scene"};
543
544
546 {
547 if(selection_data.objects.empty())
548 {
549 selection_data = {};
550 }
551 }
552
553 // Helper method to restore selection from a snapshot (used by actions)
554 void restore_selection_impl(const std::vector<entt::meta_any>& selection_snapshot)
555 {
556 selection_data.objects.clear();
557 for (const auto& obj : selection_snapshot)
558 {
559 // Only add valid selections
560 if (obj)
561 {
562 // Check if it's an entity handle and if it's still valid
563 if (obj.type() == entt::resolve<entt::handle>())
564 {
565 auto handle = obj.cast<const entt::handle&>();
566 if (handle && handle.valid())
567 {
568 selection_data.objects.emplace_back(obj);
569 }
570 }
571 else
572 {
573 // For non-entity selections (like asset handles), just add them
574 selection_data.objects.emplace_back(obj);
575 }
576 }
577 }
579 }
580
581 private:
582 template<typename T>
583 auto is_selected_impl(const T& entry, const entt::meta_any& selected) -> bool
584 {
585 if(selected.type() != entt::resolve<T>())
586 {
587 return false;
588 }
589
590 return selected.cast<const T&>() == entry;
591 }
592
593 // Internal implementation methods that perform selection without creating actions
594 template<typename T>
595 void select_impl(const T& entry, select_mode mode = select_mode::normal)
596 {
597 focus(entry);
598 switch(mode)
599 {
601 {
602 selection_data.objects.clear();
603 selection_data.objects.emplace_back(entry);
604 break;
605 }
607 {
608 if(!selection_data.objects.empty())
609 {
610 if(!selection_data.objects.back())
611 {
612 selection_data.objects.clear();
613 }
614 }
615
616 if(!is_selected(entry))
617 {
618 selection_data.objects.emplace_back(entry);
619 }
620 else
621 {
622 unselect_impl(entry);
623 }
624 break;
625 }
627 {
628 if(!selection_data.objects.empty())
629 {
630 if(!selection_data.objects.back())
631 {
632 selection_data.objects.clear();
633 }
634 }
635
636 if(!is_selected(entry))
637 {
638 selection_data.objects.emplace_back(entry);
639 }
640 else
641 {
642 // make it active
643 unselect_impl(entry);
644
645 if(!selection_data.objects.back())
646 {
647 selection_data.objects.clear();
648 }
649 selection_data.objects.emplace_back(entry);
650 }
651 break;
652 }
653 default:
654 break;
655 }
656
658 }
659
660 template<typename T>
661 void unselect_impl(const T& entry)
662 {
663 std::erase_if(selection_data.objects,
664 [&](const auto& el)
665 {
666 return is_selected_impl(entry, el);
667 });
669 }
670
671 struct scene_cache
672 {
673 scene* scn = nullptr;
674 std::stringstream cache;
675 asset_handle<scene_prefab> cache_source;
676 };
677
678 struct selection_cache
679 {
680 std::vector<hpp::uuid> uids;
681 };
682
683 std::map<std::string, scene_cache> caches_;
684 selection_cache selection_cache_;
685 void save_selection(rtti::context& ctx);
686 void save_checkpoint(rtti::context& ctx, scene_cache& cache);
687 void load_checkpoint(rtti::context& ctx, scene_cache& cache, bool recover_selection = false);
688
689 std::shared_ptr<int> sentinel_ = std::make_shared<int>(0);
690
691 bool waiting_for_compilation_before_play_{};
692 bool has_unsaved_changes_;
693
694
695 // Prompts the user to save changes and returns true if changes should be saved
696 auto prompt_save_changes(rtti::context& ctx, const std::function<void()>& on_save, const std::function<void()>& on_continue) -> bool;
697};
698} // namespace unravel
699
std::chrono::duration< float > delta_t
std::string name
Definition hub.cpp:33
std::string tag
Definition hub.cpp:32
Definition cache.hpp:11
path resolve_protocol(const path &_path)
Given the specified path/filename, resolve the final full filename. This will be based on either the ...
Hash specialization for batch_key to enable use in std::unordered_map.
@ immediate
Schedule load on thread pool now (default, backward compatible).
entt::handle entity
Thread-safe handle to an asset.
std::vector< entt::meta_any > objects
auto has_pending_actions() const -> bool
auto is_prefab_mode() const -> bool
auto get_active_scene(rtti::context &ctx) -> scene *
auto get_selections() const -> hpp::span< const entt::meta_any >
auto try_get_active_selection_as() -> T *
void on_play_begin(rtti::context &ctx)
bool show_grid
enable editor grid
void on_play_after_end(rtti::context &ctx)
auto is_focused(const T &entry) -> bool
auto try_get_selections_as() const -> std::vector< const T * >
auto get_select_mode() const -> select_mode
bool wireframe_selection
enable wireframe selection
ImGuizmo::OPERATION operation
current manipulation gizmo operation.
void push_undo_stack_enabled(bool enabled)
void unselect(const T &entry)
ImGuizmo::MODE mode
current manipulation gizmo space.
void queue_action(const std::string &name, Args &&... args)
void on_action_executed(std::shared_ptr< editing_action_t > action)
void focus_path(const fs::path &object)
auto get_selections() -> hpp::span< entt::meta_any >
auto undo() -> std::shared_ptr< editing_action_t >
auto init(rtti::context &ctx) -> bool
billboard_gizmos billboard_data
void on_play_before_begin(rtti::context &ctx)
auto can_undo() const -> bool
void on_frame_update(rtti::context &ctx, delta_t)
void select(const T &entry, select_mode mode=select_mode::normal, std::string hint="")
void on_script_recompile(rtti::context &ctx, const std::string &protocol, uint64_t version)
auto is_selected_type() -> bool
void clear(bool clear_unsaved=true)
auto has_unsaved_changes() const -> bool
auto is_selected(const T &entry) -> bool
auto get_active_selection() const -> const entt::meta_any &
std::vector< std::shared_ptr< editing_action_t > > pending_actions
auto get_active_selection_as() const -> const T &
void foucs_asset(const asset_handle< T > &entry)
auto can_redo() const -> bool
void try_unselect()
Clears the selection data if it maches the type.
bool show_icon_gizmos
enable editor icon gizmos
void restore_selection_impl(const std::vector< entt::meta_any > &selection_snapshot)
auto try_get_active_focus_as() const -> const T *
void sync_prefab_entity(rtti::context &ctx, entt::handle entity, const asset_handle< prefab > &pfb)
selection selection_data
selection data containing selected object
void queue_action(const std::string &name, const std::function< void()> &action)
auto try_get_active_selection_as() const -> const T *
void do_action(const std::string &name, const std::function< void()> &action)
auto get_active_selection() -> entt::meta_any &
auto try_get_selections_as_copy() const -> std::vector< T >
auto redo() -> std::shared_ptr< editing_action_t >
void sync_prefab_instances(rtti::context &ctx, scene *scn)
void on_prefab_updated(const asset_handle< prefab > &pfb)
auto try_get_selections_as() -> std::vector< T * >
void unload_scenes_scripting(const std::vector< scene * > &scenes)
void enter_prefab_mode(rtti::context &ctx, const asset_handle< prefab > &prefab, bool auto_save=false)
std::stack< bool > undo_stack_enabled
void do_action(const std::string &name, Args &&... args)
auto deinit(rtti::context &ctx) -> bool
void add_action(const std::string &name, std::shared_ptr< editing_action_t > action, bool immediate=true)
void focus(entt::meta_any object)
Selects an object. Can be anything.
auto get_pending_actions_count() const -> size_t
auto is_focused(const asset_handle< T > &entry) -> bool
void exit_prefab_mode(rtti::context &ctx, save_option save_changes=save_option::prompt)
inverse_kinematics ik_data
void save_prefab_changes(rtti::context &ctx)
asset_handle< prefab > edited_prefab
snap snap_data
snap data containging various snap options
void unselect(bool clear_selection_tools=true)
Clears the selection data.
Represents a generic prefab with a buffer for serialized data.
Definition prefab.h:18
Represents a scene in the ACE framework, managing entities and their relationships.
Definition scene.h:70
auto can_undo() const -> bool
auto can_redo() const -> bool
gfx::uniform_handle handle
Definition uniform.cpp:9
cache_t cache
Definition uniform.cpp:15
bool enabled