Unravel Engine C++ Reference
Loading...
Searching...
No Matches
inspector_math.cpp
Go to the documentation of this file.
1#include "inspector_math.h"
2#include "entt/meta/resolve.hpp"
3#include "imgui/imgui.h"
4#include <imgui/imgui_internal.h>
6
7namespace unravel
8{
9namespace
10{
11float DRAG_SPEED = 0.01f;
12
13auto quat_to_vec4(math::quat q) -> math::vec4
14{
15 math::vec4 v;
16 v.x = q.x;
17 v.y = q.y;
18 v.z = q.z;
19 v.w = q.w;
20 return v;
21}
22auto vec4_to_quat(math::vec4 v) -> math::quat
23{
24 math::quat q;
25 q.x = v.x;
26 q.y = v.y;
27 q.z = v.z;
28 q.w = v.w;
29 return q;
30}
31
32bool DragFloat2(math::vec2& data, const var_info& info, std::array<const char*, 2> formats = {{"X:%.2f", "Y:%.2f"}})
33{
34 bool result = ImGui::DragMultiFormatScalarN("##",
35 ImGuiDataType_Float,
36 math::value_ptr(data),
37 2,
38 DRAG_SPEED,
39 nullptr,
40 nullptr,
41 formats.data());
42 ImGui::ActiveItemWrapMousePos();
43
44 return result;
45}
46
47bool DragFloat3(math::vec3& data,
48 const var_info& info,
49 std::array<const char*, 3> formats = {{"X:%.3f", "Y:%.3f", "Z:%.3f"}})
50{
51 bool result = ImGui::DragMultiFormatScalarN("##",
52 ImGuiDataType_Float,
53 math::value_ptr(data),
54 3,
55 DRAG_SPEED,
56 nullptr,
57 nullptr,
58 formats.data());
59 ImGui::ActiveItemWrapMousePos();
60
61 return result;
62}
63
64bool DragFloat4(math::vec4& data,
65 const var_info& info,
66 std::array<const char*, 4> formats = {{"X:%.3f", "Y:%.3f", "Z:%.3f", "W:%.3f"}})
67{
68 bool result = ImGui::DragMultiFormatScalarN("##",
69 ImGuiDataType_Float,
70 math::value_ptr(data),
71 4,
72 DRAG_SPEED,
73 nullptr,
74 nullptr,
75 formats.data());
76 ImGui::ActiveItemWrapMousePos();
77
78 return result;
79}
80
81bool DragVec2(math::vec2& data, const var_info& info, const math::vec2* reset = nullptr, const char* format = "%.3f")
82{
83 bool result = ImGui::DragVecN("##",
84 ImGuiDataType_Float,
85 math::value_ptr(data),
86 data.length(),
87 DRAG_SPEED,
88 nullptr,
89 nullptr,
90 reset ? math::value_ptr(*reset) : nullptr,
91 format);
92 ImGui::ActiveItemWrapMousePos();
93
94 return result;
95}
96
97bool DragVec3(math::vec3& data, const var_info& info, const math::vec3* reset = nullptr, const char* format = "%.3f")
98{
99 bool result = ImGui::DragVecN("##",
100 ImGuiDataType_Float,
101 math::value_ptr(data),
102 data.length(),
103 DRAG_SPEED,
104 nullptr,
105 nullptr,
106 reset ? math::value_ptr(*reset) : nullptr,
107 format);
108
109 ImGui::ActiveItemWrapMousePos();
110
111 return result;
112}
113
114bool DragVec4(math::vec4& data, const var_info& info, const math::vec4* reset = nullptr, const char* format = "%.3f")
115{
116 bool result = ImGui::DragVecN("##",
117 ImGuiDataType_Float,
118 math::value_ptr(data),
119 data.length(),
120 DRAG_SPEED,
121 nullptr,
122 nullptr,
123 reset ? math::value_ptr(*reset) : nullptr,
124 format);
125
126 ImGui::ActiveItemWrapMousePos();
127
128 return result;
129}
130
131} // namespace
132
134 entt::meta_any& var,
135 const meta_any_proxy& var_proxy,
136 const var_info& info,
137 const entt::meta_custom& custom) -> inspect_result
138{
139 auto& data = var.cast<math::bvec2&>();
140 inspect_result result{};
141
142 enum bflags
143 {
144 none = 0,
145 x = 1 << 0,
146 y = 1 << 1,
147 };
148
149 int flags = 0;
150 flags |= data.x ? bflags::x : 0;
151 flags |= data.y ? bflags::y : 0;
152
153 bool mod = false;
154 ImGui::BeginGroup();
155 mod |= ImGui::CheckboxFlags("X", &flags, bflags::x);
156 ImGui::SameLine();
157 mod |= ImGui::CheckboxFlags("Y", &flags, bflags::y);
158 ImGui::EndGroup();
159 if(mod)
160 {
161 data.x = flags & bflags::x;
162 data.y = flags & bflags::y;
163 result.changed = true;
164 }
165 result.edit_finished = ImGui::IsItemDeactivatedAfterEdit();
166
167 return result;
168}
169
171 entt::meta_any& var,
172 const meta_any_proxy& var_proxy,
173 const var_info& info,
174 const entt::meta_custom& custom) -> inspect_result
175{
176 auto& data = var.cast<math::bvec3&>();
177 inspect_result result{};
178
179 enum bflags
180 {
181 none = 0,
182 x = 1 << 0,
183 y = 1 << 1,
184 z = 1 << 2,
185 };
186
187 int flags = 0;
188 flags |= data.x ? bflags::x : 0;
189 flags |= data.y ? bflags::y : 0;
190 flags |= data.z ? bflags::z : 0;
191
192 bool mod = false;
193 ImGui::BeginGroup();
194 mod |= ImGui::CheckboxFlags("X", &flags, bflags::x);
195 ImGui::SameLine();
196 mod |= ImGui::CheckboxFlags("Y", &flags, bflags::y);
197 ImGui::SameLine();
198 mod |= ImGui::CheckboxFlags("Z", &flags, bflags::z);
199 ImGui::EndGroup();
200
201 if(mod)
202 {
203 data.x = flags & bflags::x;
204 data.y = flags & bflags::y;
205 data.z = flags & bflags::z;
206 result.changed = true;
207 }
208 result.edit_finished = ImGui::IsItemDeactivatedAfterEdit();
209
210 return result;
211}
212
214 entt::meta_any& var,
215 const meta_any_proxy& var_proxy,
216 const var_info& info,
217 const entt::meta_custom& custom) -> inspect_result
218{
219 auto& data = var.cast<math::bvec4&>();
220 inspect_result result{};
221
222 enum bflags
223 {
224 none = 0,
225 x = 1 << 0,
226 y = 1 << 1,
227 z = 1 << 2,
228 w = 1 << 3,
229 };
230
231 int flags = 0;
232 flags |= data.x ? bflags::x : 0;
233 flags |= data.y ? bflags::y : 0;
234 flags |= data.z ? bflags::z : 0;
235 flags |= data.w ? bflags::w : 0;
236
237 bool mod = false;
238 ImGui::BeginGroup();
239 mod |= ImGui::CheckboxFlags("X", &flags, bflags::x);
240 ImGui::SameLine();
241 mod |= ImGui::CheckboxFlags("Y", &flags, bflags::y);
242 ImGui::SameLine();
243 mod |= ImGui::CheckboxFlags("Z", &flags, bflags::z);
244 ImGui::SameLine();
245 mod |= ImGui::CheckboxFlags("W", &flags, bflags::w);
246 ImGui::EndGroup();
247
248 if(mod)
249 {
250 data.x = flags & bflags::x;
251 data.y = flags & bflags::y;
252 data.z = flags & bflags::z;
253 data.w = flags & bflags::w;
254
255 result.changed = true;
256 }
257 result.edit_finished = ImGui::IsItemDeactivatedAfterEdit();
258
259 return result;
260}
261
263 entt::meta_any& var,
264 const meta_any_proxy& var_proxy,
265 const var_info& info,
266 const entt::meta_custom& custom) -> inspect_result
267{
268 auto& data = var.cast<math::vec2&>();
269 inspect_result result{};
270
271 if(DragVec2(data, info))
272 {
273 result.changed = true;
274 }
275 result.edit_finished = ImGui::IsItemDeactivatedAfterEdit();
276
277 return result;
278}
279
281 entt::meta_any& var,
282 const meta_any_proxy& var_proxy,
283 const var_info& info,
284 const entt::meta_custom& custom) -> inspect_result
285{
286 auto& data = var.cast<math::vec3&>();
287 inspect_result result{};
288
289 if(DragVec3(data, info))
290 {
291 result.changed = true;
292 }
293 result.edit_finished = ImGui::IsItemDeactivatedAfterEdit();
294
295 return result;
296}
297
299 entt::meta_any& var,
300 const meta_any_proxy& var_proxy,
301 const var_info& info,
302 const entt::meta_custom& custom) -> inspect_result
303{
304 auto& data = var.cast<math::vec4&>();
305 inspect_result result{};
306
307 if(DragVec4(data, info))
308 {
309 result.changed = true;
310 }
311 result.edit_finished = ImGui::IsItemDeactivatedAfterEdit();
312
313 return result;
314}
315
317 entt::meta_any& var,
318 const meta_any_proxy& var_proxy,
319 const var_info& info,
320 const entt::meta_custom& custom) -> inspect_result
321{
322 auto& data = var.cast<math::color&>();
323 inspect_result result{};
324
325 if(ImGui::ColorEdit4("##",
326 math::value_ptr(data.value),
327 ImGuiColorEditFlags_AlphaBar | ImGuiColorEditFlags_AlphaPreviewHalf))
328 {
329 result.changed = true;
330 }
331
332 result.edit_finished = ImGui::IsItemDeactivatedAfterEdit();
333
334 ImGui::DrawItemActivityOutline();
335
336 return result;
337}
338
340 entt::meta_any& var,
341 const meta_any_proxy& var_proxy,
342 const var_info& info,
343 const entt::meta_custom& custom) -> inspect_result
344{
345 auto& data = var.cast<math::quat&>();
346 inspect_result result{};
347
348 auto val = math::degrees(math::eulerAngles(data));
349
350 static const auto reset = math::zero<math::vec3>();
351
352 // auto val = quat_to_vec4(data);
353 if(DragVec3(val, info, &reset, "%.2f°"))
354 {
355 // data = vec4_to_quat(val);
356 data = math::quat(math::radians(val));
357 result.changed = true;
358 }
359 result.edit_finished |= ImGui::IsItemDeactivatedAfterEdit();
360
361 ImGui::DrawItemActivityOutline();
362
363 return result;
364}
365
366
367void inspector_transform::before_inspect(const entt::meta_data& prop)
368{
369 layout_ = std::make_unique<property_layout>();
370 layout_->set_data(prop, false);
371 open_ = layout_->push_tree_layout(ImGuiTreeNodeFlags_SpanFullWidth);
372}
373
375 entt::meta_any& var,
376 const meta_any_proxy& var_proxy,
377 const var_info& info,
378 const entt::meta_custom& custom) -> inspect_result
379{
380 if(!open_)
381 {
382 return {};
383 }
384 inspect_result result{};
385
386 auto& data = var.cast<math::transform&>();
387 auto position = data.get_translation();
388 auto rotation = data.get_rotation();
389 auto scale = data.get_scale();
390 auto skew = data.get_skew();
391 // auto perspective = data.get_perspective();
392
393 auto type = entt::resolve<math::transform>();
394
395 static math::vec3 euler_angles(0.0f, 0.0f, 0.0f);
396
397 math::quat old_quat(math::radians(euler_angles));
398
399 float dot_product = math::dot(old_quat, rotation);
400 bool equal = (dot_product > (1.0f - math::epsilon<float>()));
401 if(!equal && (!ImGui::IsMouseDragging(ImGuiMouseButton_Left) || ImGuizmo::IsUsing()))
402 {
403 euler_angles = data.get_rotation_euler_degrees();
404 }
405
406
407
408 ImGui::PushID("Position");
409 {
410 auto prop = type.data("position"_hs);
411 auto prop_name = entt::get_name(prop);
412 auto prop_pretty_name = entt::get_pretty_name(prop);
413
414 auto& override_ctx = ctx.get_cached<prefab_override_context>();
415 override_ctx.push_segment(prop_name, prop_pretty_name);
416
417 property_layout layout;
418 layout.set_data(prop_pretty_name, "");
419 layout.push_layout(false);
420
421 ImGui::SameLine();
422
423 ImGui::AlignedItem(
424 1.0f,
425 ImGui::GetContentRegionAvail().x,
426 ImGui::GetFrameHeight(),
427 [&]()
428 {
429 if(ImGui::Button(ICON_MDI_UNDO_VARIANT, ImVec2(ImGui::GetFrameHeight(), ImGui::GetFrameHeight())))
430 {
431 data.reset_position();
432 result.changed = true;
433 result.edit_finished = true;
434
435 auto prop_proxy = make_property_proxy(var_proxy, prop);
436 add_property_action(ctx, override_ctx, result, prop_proxy, position, data.get_position(), prop.custom());
437 }
438 result.edit_finished |= ImGui::IsItemDeactivatedAfterEdit();
439 ImGui::SetItemTooltipEx("Reset %s", prop_pretty_name.c_str());
440 });
441 layout.prepare_for_item();
442
443 ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x);
444 {
445 static const auto reset = math::zero<math::vec3>();
446 if(DragVec3(position, info, &reset))
447 {
448 data.set_position(position);
449 result.changed |= true;
450
451 auto prop_proxy = make_property_proxy(var_proxy, prop);
452 add_property_action(ctx, override_ctx, result, prop_proxy, position, data.get_position(), prop.custom());
453 }
454 result.edit_finished |= ImGui::IsItemDeactivatedAfterEdit();
455 }
456 ImGui::PopItemWidth();
457
458 override_ctx.pop_segment();
459 }
460 ImGui::PopID();
461
462 ImGui::PushID("Rotation");
463 {
464 auto prop = type.data("rotation"_hs);
465 auto prop_name = entt::get_name(prop);
466 auto prop_pretty_name = entt::get_pretty_name(prop);
467
468 auto& override_ctx = ctx.get_cached<prefab_override_context>();
469 override_ctx.push_segment(prop_name, prop_pretty_name);
470
471 property_layout layout;
472 layout.set_data(prop_pretty_name, "");
473 layout.push_layout(false);
474
475 ImGui::SameLine();
476
477 ImGui::AlignedItem(
478 1.0f,
479 ImGui::GetContentRegionAvail().x,
480 ImGui::GetFrameHeight(),
481 [&]()
482 {
483 if(ImGui::Button(ICON_MDI_UNDO_VARIANT, ImVec2(ImGui::GetFrameHeight(), ImGui::GetFrameHeight())))
484 {
485 data.reset_rotation();
486 result.changed = true;
487 result.edit_finished = true;
488
489 auto prop_proxy = make_property_proxy(var_proxy, prop);
490 add_property_action(ctx, override_ctx, result, prop_proxy, rotation, data.get_rotation(), prop.custom());
491 }
492 result.edit_finished |= ImGui::IsItemDeactivatedAfterEdit();
493
494 ImGui::SetItemTooltipEx("Reset %s", prop_pretty_name.c_str());
495 });
496 layout.prepare_for_item();
497
498 ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x);
499 {
500 auto old_euler = euler_angles;
501 static const auto reset = math::zero<math::vec3>();
502 if(DragVec3(euler_angles, info, &reset, "%.2f°"))
503 {
504 data.rotate_local(math::radians(euler_angles - old_euler));
505 result.changed |= true;
506
507 auto prop_proxy = make_property_proxy(var_proxy, prop);
508 add_property_action(ctx, override_ctx, result, prop_proxy, rotation, data.get_rotation(), prop.custom());
509 }
510 result.edit_finished |= ImGui::IsItemDeactivatedAfterEdit();
511 }
512
513 ImGui::PopItemWidth();
514
515 override_ctx.pop_segment();
516 }
517 ImGui::PopID();
518
519 ImGui::PushID("Scale");
520 {
521 auto prop = type.data("scale"_hs);
522 auto prop_name = entt::get_name(prop);
523 auto prop_pretty_name = entt::get_pretty_name(prop);
524
525 auto& override_ctx = ctx.get_cached<prefab_override_context>();
526 override_ctx.push_segment(prop_name, prop_pretty_name);
527
528 property_layout layout;
529 layout.set_data(prop_pretty_name, "");
530 layout.push_layout(false);
531
532 ImGui::SameLine();
533 static bool locked_scale = false;
534 auto label = locked_scale ? ICON_MDI_LOCK : ICON_MDI_LOCK_OPEN_VARIANT;
535 ImGui::AlignedItem(
536 1.0f,
537 ImGui::GetContentRegionAvail().x,
538 ImGui::CalcItemSize(label).x + ImGui::GetFrameHeight() + ImGui::GetStyle().ItemSpacing.x,
539 [&]()
540 {
541 if(ImGui::Button(label))
542 {
543 locked_scale = !locked_scale;
544 }
545
546 ImGui::SetItemTooltipEx("Enable/Disable Constrained Proportions");
547
548 ImGui::SameLine();
549
550 if(ImGui::Button(ICON_MDI_UNDO_VARIANT, ImVec2(ImGui::GetFrameHeight(), ImGui::GetFrameHeight())))
551 {
552 data.reset_scale();
553 result.changed = true;
554 result.edit_finished = true;
555
556 auto prop_proxy = make_property_proxy(var_proxy, prop);
557 add_property_action(ctx, override_ctx, result, prop_proxy, scale, data.get_scale(), prop.custom());
558 }
559 result.edit_finished |= ImGui::IsItemDeactivatedAfterEdit();
560
561 ImGui::SetItemTooltipEx("Reset %s", prop_pretty_name.c_str());
562 });
563
564 layout.prepare_for_item();
565
566 ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x);
567 {
568 static const auto reset = math::one<math::vec3>();
569 auto before_scale = scale;
570 if(DragVec3(scale, info, &reset))
571 {
572 auto delta = scale - before_scale;
573
574 if(locked_scale)
575 {
576 before_scale += math::vec3(delta.x);
577 before_scale += math::vec3(delta.y);
578 before_scale += math::vec3(delta.z);
579 scale = before_scale;
580 }
581
582 data.set_scale(scale);
583 result.changed |= true;
584
585 auto prop_proxy = make_property_proxy(var_proxy, prop);
586 add_property_action(ctx, override_ctx, result, prop_proxy, scale, data.get_scale(), prop.custom());
587 }
588
589 result.edit_finished |= ImGui::IsItemDeactivatedAfterEdit();
590 }
591 ImGui::PopItemWidth();
592
593 override_ctx.pop_segment();
594 }
595 ImGui::PopID();
596
597 ImGui::PushID("Skew");
598 {
599 auto prop = type.data("skew"_hs);
600 auto prop_name = entt::get_name(prop);
601 auto prop_pretty_name = entt::get_pretty_name(prop);
602
603 auto& override_ctx = ctx.get_cached<prefab_override_context>();
604 override_ctx.push_segment(prop_name, prop_pretty_name);
605
606 property_layout layout;
607 layout.set_data(prop_pretty_name, "");
608 layout.push_layout(false);
609
610 ImGui::SameLine();
611
612 ImGui::AlignedItem(
613 1.0f,
614 ImGui::GetContentRegionAvail().x,
615 ImGui::GetFrameHeight(),
616 [&]()
617 {
618 if(ImGui::Button(ICON_MDI_UNDO_VARIANT, ImVec2(ImGui::GetFrameHeight(), ImGui::GetFrameHeight())))
619 {
620 data.reset_skew();
621 result.changed = true;
622 result.edit_finished = true;
623
624 auto prop_proxy = make_property_proxy(var_proxy, prop);
625 add_property_action(ctx, override_ctx, result, prop_proxy, skew, data.get_skew(), prop.custom());
626 }
627 result.edit_finished |= ImGui::IsItemDeactivatedAfterEdit();
628 ImGui::SetItemTooltipEx("Reset %s", prop_pretty_name.c_str());
629 });
630 layout.prepare_for_item();
631
632 ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x);
633 {
634 static const auto reset = math::zero<math::vec3>();
635 if(DragVec3(skew, info, &reset))
636 {
637 data.set_skew(skew);
638 result.changed |= true;
639 auto prop_proxy = make_property_proxy(var_proxy, prop);
640 add_property_action(ctx, override_ctx, result, prop_proxy, skew, data.get_skew(), prop.custom());
641 }
642
643 result.edit_finished |= ImGui::IsItemDeactivatedAfterEdit();
644 }
645 ImGui::PopItemWidth();
646
647 override_ctx.pop_segment();
648 }
649 ImGui::PopID();
650
651 return result;
652}
653} // namespace unravel
manifold_type type
General purpose transformation class designed to maintain each component of the transformation separa...
Definition transform.hpp:27
auto get_position() const noexcept -> const vec3_t &
Get the position component.
auto get_translation() const noexcept -> const vec3_t &
Get the translation component.
Manages ImGui layout for property inspection in the editor.
Definition inspector.h:19
void set_data(const entt::meta_data &prop, bool columns=true)
Updates layout data from meta property.
Definition inspector.cpp:75
void push_layout(bool auto_proceed_to_next_column=true)
Initializes ImGui layout with tables and property label.
Definition inspector.cpp:92
void prepare_for_item()
Prepares ImGui for rendering the property value widget.
float scale
Definition hub.cpp:25
#define ICON_MDI_LOCK_OPEN_VARIANT
#define ICON_MDI_UNDO_VARIANT
#define ICON_MDI_LOCK
auto get_pretty_name(const meta_type &t) -> std::string
auto get_name(const meta_type &t) -> std::string
void reset(uint32_t _width, uint32_t _height, uint32_t _flags)
Definition graphics.cpp:260
auto make_property_proxy(const meta_any_proxy &var_proxy, const entt::meta_data &prop) -> meta_any_proxy
void add_property_action(rtti::context &ctx, prefab_override_context &override_ctx, inspect_result &result, const meta_any_proxy &var_proxy, const entt::meta_any &old_var, const entt::meta_any &new_var, const entt::meta_custom &custom)
float y
float x
float z
Result of an inspection operation indicating what changes occurred.
Definition inspector.h:146
bool edit_finished
Whether user finished editing (e.g., released mouse or pressed enter)
Definition inspector.h:150
auto inspect(rtti::context &ctx, entt::meta_any &var, const meta_any_proxy &var_proxy, const var_info &info, const entt::meta_custom &custom) -> inspect_result override
auto inspect(rtti::context &ctx, entt::meta_any &var, const meta_any_proxy &var_proxy, const var_info &info, const entt::meta_custom &custom) -> inspect_result override
auto inspect(rtti::context &ctx, entt::meta_any &var, const meta_any_proxy &var_proxy, const var_info &info, const entt::meta_custom &custom) -> inspect_result override
auto inspect(rtti::context &ctx, entt::meta_any &var, const meta_any_proxy &var_proxy, const var_info &info, const entt::meta_custom &custom) -> inspect_result override
auto inspect(rtti::context &ctx, entt::meta_any &var, const meta_any_proxy &var_proxy, const var_info &info, const entt::meta_custom &custom) -> inspect_result override
void before_inspect(const entt::meta_data &prop) override
auto inspect(rtti::context &ctx, entt::meta_any &var, const meta_any_proxy &var_proxy, const var_info &info, const entt::meta_custom &custom) -> inspect_result override
auto inspect(rtti::context &ctx, entt::meta_any &var, const meta_any_proxy &var_proxy, const var_info &info, const entt::meta_custom &custom) -> inspect_result override
auto inspect(rtti::context &ctx, entt::meta_any &var, const meta_any_proxy &var_proxy, const var_info &info, const entt::meta_custom &custom) -> inspect_result override
auto inspect(rtti::context &ctx, entt::meta_any &var, const meta_any_proxy &var_proxy, const var_info &info, const entt::meta_custom &custom) -> inspect_result override
Safe deferred property access proxy for arbitrary object properties.
Definition inspector.h:198
Global context for tracking prefab override changes during inspection.
Definition inspectors.h:94
void push_segment(const std::string &segment, const std::string &pretty_segment)
Pushes a new path segment onto both contexts and applies override styling.
Metadata about a variable being inspected.
Definition inspector.h:133