Blender V5.0
view3d_gizmo_geometry_nodes.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "WM_api.hh"
10#include "WM_types.hh"
11
12#include "DNA_modifier_types.h"
13#include "DNA_node_types.h"
14
16#include "BKE_context.hh"
18#include "BKE_geometry_set.hh"
20#include "BKE_instances.hh"
22#include "BKE_modifier.hh"
24#include "BKE_node_runtime.hh"
25#include "BKE_object.hh"
26
27#include "BLI_math_base_safe.h"
28#include "BLI_math_matrix.h"
29#include "BLI_math_matrix.hh"
30#include "BLI_math_rotation.h"
31#include "BLI_math_rotation.hh"
32
33#include "RNA_access.hh"
34
35#include "MOD_nodes.hh"
36
39
40#include "UI_resources.hh"
41
42#include "ED_gizmo_library.hh"
43#include "ED_node.hh"
44
45#include "DEG_depsgraph.hh"
47
48#include "view3d_intern.hh"
49
51namespace geo_eval_log = nodes::geo_eval_log;
52using geo_eval_log::GeoTreeLog;
53
54static bool gizmo_is_interacting(const wmGizmo &gizmo)
55{
56 return gizmo.interaction_data != nullptr;
57}
58
60{
61 switch (color_id) {
63 return TH_GIZMO_PRIMARY;
65 return TH_GIZMO_SECONDARY;
67 return TH_AXIS_X;
69 return TH_AXIS_Y;
71 return TH_AXIS_Z;
72 }
73 return TH_GIZMO_PRIMARY;
74}
75
77{
78 return std::array{TH_AXIS_X, TH_AXIS_Y, TH_AXIS_Z}[axis];
79}
80
81static void get_axis_gizmo_colors(const int axis, float *r_color, float *r_color_hi)
82{
83 const ThemeColorID theme_id = get_axis_theme_color_id(axis);
84 UI_GetThemeColor3fv(theme_id, r_color);
85 UI_GetThemeColor3fv(theme_id, r_color_hi);
86 r_color[3] = 0.6f;
87 r_color_hi[3] = 1.0f;
88}
89
91{
92 /* Without this, the gizmo may be skewed. */
95 m.z_axis() = math::normalize(m.z_axis());
97}
98
100 const float3 &direction,
101 const math::AxisSigned direction_axis)
102{
104 math::Quaternion rotation;
105 const float3 base_direction = math::to_vector<float3>(direction_axis);
106 rotation_between_vecs_to_quat(&rotation.w, base_direction, direction);
108 mat.location() = position;
109 return mat;
110}
111
114 bool invalid_transform = false;
115};
116
117using ApplyChangeFn = std::function<void(
118 StringRef socket_identifier, FunctionRef<void(bke::SocketValueVariant &value)> modify_value)>;
119
121 const bContext &C;
122 /* Transform of the object and geometry that the gizmo belongs to. */
125 GeoTreeLog &tree_log;
128
129 template<typename T> [[nodiscard]] bool get_input_value(const StringRef identifier, T &r_value)
130 {
131 const bNodeSocket &socket = *this->gizmo_node.input_by_identifier(identifier);
132 const std::optional<T> value_opt = this->tree_log.find_primitive_socket_value<T>(socket);
133 if (!value_opt) {
134 return false;
135 }
136 r_value = *value_opt;
137 return true;
138 }
139};
140
142 public:
149
150 virtual ~NodeGizmos() = default;
151
156 virtual void create_gizmos(wmGizmoGroup &gzgroup) = 0;
157
159 virtual void update(GizmosUpdateParams & /*params*/) {}
160
163
164 void hide_all()
165 {
166 for (wmGizmo *gizmo : this->get_all_gizmos()) {
168 }
169 }
170
171 void show_all()
172 {
173 for (wmGizmo *gizmo : this->get_all_gizmos()) {
174 WM_gizmo_set_flag(gizmo, WM_GIZMO_HIDDEN, false);
175 }
176 }
177
180 {
181 bool any_interacting = false;
182 for (const wmGizmo *gizmo : this->get_all_gizmos()) {
183 any_interacting |= gizmo_is_interacting(*gizmo);
184 }
185 return any_interacting;
186 }
187};
188
189class LinearGizmo : public NodeGizmos {
190 private:
191 wmGizmo *gizmo_ = nullptr;
192
193 struct EditData {
195 float factor_from_transform = 1.0f;
196 float current_value = 0.0f;
197 } edit_data_;
198
199 public:
200 void create_gizmos(wmGizmoGroup &gzgroup) override
201 {
202 gizmo_ = WM_gizmo_new("GIZMO_GT_arrow_3d", &gzgroup, nullptr);
203 }
204
206 {
207 return {gizmo_};
208 }
209
211 {
212 const auto &storage = *static_cast<const NodeGeometryLinearGizmo *>(params.gizmo_node.storage);
213 const bool is_interacting = gizmo_is_interacting(*gizmo_);
214
215 this->update_style(storage);
216
217 if (is_interacting) {
218 return;
219 }
220 if (!this->update_transform(params)) {
221 return;
222 }
224 }
225
227 {
228 /* Make sure the enum values are in sync. */
232 RNA_enum_set(gizmo_->ptr, "draw_style", storage.draw_style);
233
234 WM_gizmo_set_line_width(gizmo_, 1.0f);
235
236 const float length = (storage.draw_style == GEO_NODE_LINEAR_GIZMO_DRAW_STYLE_BOX) ? 0.8f :
237 1.0f;
238 RNA_float_set(gizmo_->ptr, "length", length);
239
240 const ThemeColorID color_theme_id = get_gizmo_theme_color_id(
242 UI_GetThemeColor3fv(color_theme_id, gizmo_->color);
243 UI_GetThemeColor3fv(TH_GIZMO_HI, gizmo_->color_hi);
244 }
245
247 {
248 float3 position;
249 float3 direction;
250 if (!params.get_input_value("Position", position) ||
251 !params.get_input_value("Direction", direction))
252 {
253 params.r_report.missing_socket_logs = true;
254 return false;
255 }
256 direction = math::normalize(direction);
257 if (math::is_zero(direction)) {
258 params.r_report.invalid_transform = true;
259 return false;
260 }
261
262 const float4x4 gizmo_base_transform = matrix_from_position_and_up_direction(
263 position, direction, math::AxisSigned::Z_POS);
264
265 float4x4 gizmo_transform = params.parent_transform * gizmo_base_transform;
266 edit_data_.factor_from_transform = safe_divide(1.0f, math::length(gizmo_transform.z_axis()));
268 copy_m4_m4(gizmo_->matrix_basis, gizmo_transform.ptr());
269 return true;
270 }
271
273 {
274 /* Always reset to 0 when not interacting. */
275 edit_data_.current_value = 0.0f;
276
277 wmGizmoPropertyFnParams fn_params{};
278 fn_params.user_data = this;
279 fn_params.value_set_fn =
280 [](const wmGizmo * /*gz*/, wmGizmoProperty *gz_prop, const void *value_ptr) {
281 LinearGizmo &self = *static_cast<LinearGizmo *>(gz_prop->custom_func.user_data);
282 const float new_gizmo_value = *static_cast<const float *>(value_ptr);
283 self.edit_data_.current_value = new_gizmo_value;
284 const float offset = new_gizmo_value * self.edit_data_.factor_from_transform;
285 self.apply_change("Value", [&](bke::SocketValueVariant &value_variant) {
286 value_variant.set(value_variant.get<float>() + offset);
287 });
288 };
289 fn_params.value_get_fn =
290 [](const wmGizmo * /*gz*/, wmGizmoProperty *gz_prop, void *value_ptr) {
291 LinearGizmo &self = *static_cast<LinearGizmo *>(gz_prop->custom_func.user_data);
292 *static_cast<float *>(value_ptr) = self.edit_data_.current_value;
293 };
294 WM_gizmo_target_property_def_func(gizmo_, "offset", &fn_params);
295 }
296};
297
298class DialGizmo : public NodeGizmos {
299 private:
300 wmGizmo *gizmo_ = nullptr;
301
302 struct EditData {
303 bool is_negative_transform = false;
304 float current_value = 0.0f;
305 } edit_data_;
306
307 public:
308 void create_gizmos(wmGizmoGroup &gzgroup) override
309 {
310 gizmo_ = WM_gizmo_new("GIZMO_GT_dial_3d", &gzgroup, nullptr);
311 }
312
314 {
315 return {gizmo_};
316 }
317
319 {
320 const auto &storage = *static_cast<const NodeGeometryDialGizmo *>(params.gizmo_node.storage);
321 const bool is_interacting = gizmo_is_interacting(*gizmo_);
322
323 this->update_style(storage, is_interacting);
324
325 if (is_interacting) {
326 return;
327 }
328 if (!this->update_transform(params)) {
329 return;
330 }
332 }
333
334 void update_style(const NodeGeometryDialGizmo &storage, const bool is_interacting)
335 {
337 WM_gizmo_set_line_width(gizmo_, 2.0f);
338 RNA_boolean_set(gizmo_->ptr, "wrap_angle", false);
339
340 int draw_options = RNA_enum_get(gizmo_->ptr, "draw_options");
341 SET_FLAG_FROM_TEST(draw_options, is_interacting, ED_GIZMO_DIAL_DRAW_FLAG_ANGLE_VALUE);
342 RNA_enum_set(gizmo_->ptr, "draw_options", draw_options);
343
344 const ThemeColorID color_theme_id = get_gizmo_theme_color_id(
346 UI_GetThemeColor3fv(color_theme_id, gizmo_->color);
347 UI_GetThemeColor3fv(TH_GIZMO_HI, gizmo_->color_hi);
348 }
349
351 {
352 float3 position;
353 float3 up;
354 bool screen_space;
355 float radius;
356 if (!params.get_input_value("Position", position) || !params.get_input_value("Up", up) ||
357 !params.get_input_value("Screen Space", screen_space) ||
358 !params.get_input_value("Radius", radius))
359 {
360 params.r_report.missing_socket_logs = true;
361 return false;
362 }
363 up = math::normalize(up);
364
365 if (math::is_zero(up) || math::is_zero(radius)) {
366 params.r_report.invalid_transform = true;
367 return false;
368 }
369
370 const float4x4 gizmo_base_transform = matrix_from_position_and_up_direction(
371 position, up, math::AxisSigned::Z_NEG);
372 float4x4 gizmo_transform = params.parent_transform * gizmo_base_transform;
373 edit_data_.is_negative_transform = math::determinant(gizmo_transform) < 0.0f;
375 copy_m4_m4(gizmo_->matrix_basis, gizmo_transform.ptr());
376
377 WM_gizmo_set_flag(gizmo_, WM_GIZMO_DRAW_NO_SCALE, !screen_space);
378 float transform_scale = 1.0f;
379 if (!screen_space) {
380 /* We can't scale the dial gizmo non-uniformly, so just take the average of the scale in each
381 * axis for now. */
383 }
384 copy_m4_m4(gizmo_->matrix_offset,
386
387 return true;
388 }
389
391 {
392 edit_data_.current_value = 0.0f;
393
395 params.user_data = this;
396 params.value_set_fn =
397 [](const wmGizmo * /*gz*/, wmGizmoProperty *gz_prop, const void *value_ptr) {
398 DialGizmo &self = *static_cast<DialGizmo *>(gz_prop->custom_func.user_data);
399 const float new_gizmo_value = *static_cast<const float *>(value_ptr);
400 self.edit_data_.current_value = new_gizmo_value;
401 float offset = new_gizmo_value;
402 if (self.edit_data_.is_negative_transform) {
403 offset = -offset;
404 }
405 self.apply_change("Value", [&](bke::SocketValueVariant &value_variant) {
406 value_variant.set(value_variant.get<float>() + offset);
407 });
408 };
409 params.value_get_fn = [](const wmGizmo * /*gz*/, wmGizmoProperty *gz_prop, void *value_ptr) {
410 DialGizmo &self = *static_cast<DialGizmo *>(gz_prop->custom_func.user_data);
411 *static_cast<float *>(value_ptr) = self.edit_data_.current_value;
412 };
413 WM_gizmo_target_property_def_func(gizmo_, "offset", &params);
414 }
415};
416
418 private:
419 std::array<wmGizmo *, 3> translation_gizmos_ = {};
420 std::array<wmGizmo *, 3> rotation_gizmos_ = {};
421 std::array<wmGizmo *, 3> scale_gizmos_ = {};
422
423 bool any_translation_visible_ = false;
424 bool any_rotation_visible_ = false;
425 bool any_scale_visible_ = false;
426
427 int transform_orientation_ = V3D_ORIENT_GLOBAL;
428
433 float4x4 parent_transform_;
434
435 struct EditData {
436 float3 current_translation;
437 float3 current_rotation;
438 float3 current_scale;
439 } edit_data_;
440
441 public:
442 void create_gizmos(wmGizmoGroup &gzgroup) override
443 {
444 /* Translation */
445 for (const int axis : IndexRange(3)) {
446 translation_gizmos_[axis] = WM_gizmo_new("GIZMO_GT_arrow_3d", &gzgroup, nullptr);
447 }
448
449 /* Rotation */
450 for (const int axis : IndexRange(3)) {
451 rotation_gizmos_[axis] = WM_gizmo_new("GIZMO_GT_dial_3d", &gzgroup, nullptr);
452 }
453
454 /* Scale */
455 for (const int axis : IndexRange(3)) {
456 scale_gizmos_[axis] = WM_gizmo_new("GIZMO_GT_arrow_3d", &gzgroup, nullptr);
457 }
458 }
459
461 {
462 Vector<wmGizmo *> gizmos;
463 gizmos.extend(translation_gizmos_);
464 gizmos.extend(rotation_gizmos_);
465 gizmos.extend(scale_gizmos_);
466 return gizmos;
467 }
468
470 {
471 const auto &storage = *static_cast<const NodeGeometryTransformGizmo *>(
472 params.gizmo_node.storage);
473
474 this->update_visibility(params, storage);
476 this->update_rotate_style();
477 this->update_scale_style();
478
479 float3 position;
480 math::Quaternion rotation;
481 if (!params.get_input_value("Position", position) ||
482 !params.get_input_value("Rotation", rotation))
483 {
484 params.r_report.missing_socket_logs = true;
485 return;
486 }
487
488 float4x4 base_transform_from_socket = math::from_rotation<float4x4>(rotation);
489 base_transform_from_socket.location() = position;
490
491 Scene &scene = *CTX_data_scene(&params.C);
492 const TransformOrientationSlot &orientation_slot = scene.orientation_slots[0];
493 transform_orientation_ = orientation_slot.type;
494
495 parent_transform_ = params.parent_transform;
496
497 this->update_translate_transform_and_target_property(params, base_transform_from_socket);
498 this->update_rotate_transform_and_target_property(params, base_transform_from_socket);
499 this->update_scale_transform_and_target_property(params, base_transform_from_socket);
500 }
501
503 {
504 any_translation_visible_ = false;
505 any_rotation_visible_ = false;
506 any_scale_visible_ = false;
507
508 const auto &elem = std::get<nodes::inverse_eval::MatrixElem>(params.elem.elem);
509
510 for (const int axis : IndexRange(3)) {
511 const bool translation_used = (storage.flag &
513 elem.translation;
514 const bool rotation_used = (storage.flag &
516 elem.rotation;
517 const bool scale_used = (storage.flag & (GEO_NODE_TRANSFORM_GIZMO_USE_SCALE_X << axis)) &&
518 elem.scale;
519
520 WM_gizmo_set_flag(translation_gizmos_[axis], WM_GIZMO_HIDDEN, !translation_used);
521 WM_gizmo_set_flag(rotation_gizmos_[axis], WM_GIZMO_HIDDEN, !rotation_used);
522 WM_gizmo_set_flag(scale_gizmos_[axis], WM_GIZMO_HIDDEN, !scale_used);
523
524 any_translation_visible_ |= translation_used;
525 any_rotation_visible_ |= rotation_used;
526 any_scale_visible_ |= scale_used;
527 }
528 }
529
531 {
532 for (const int axis : IndexRange(3)) {
533 wmGizmo *gizmo = translation_gizmos_[axis];
534 get_axis_gizmo_colors(axis, gizmo->color, gizmo->color_hi);
535 WM_gizmo_set_line_width(gizmo, 2.0f);
536
537 float start = 0.0f;
538 float length = 1.0f;
539 if (any_rotation_visible_) {
540 start = 1.125;
541 length = 0.0f;
542 }
543 else if (any_scale_visible_) {
544 start = 1.0f;
545 length = 0.0f;
546 }
547
548 unit_m4(gizmo->matrix_offset);
549 gizmo->matrix_offset[3][2] = start;
550 RNA_float_set(gizmo->ptr, "length", length);
552 }
553 }
554
556 {
557 for (const int axis : IndexRange(3)) {
558 wmGizmo *gizmo = rotation_gizmos_[axis];
559 get_axis_gizmo_colors(axis, gizmo->color, gizmo->color_hi);
560
561 const bool is_interacting = gizmo_is_interacting(*gizmo);
562 int draw_options = RNA_enum_get(gizmo->ptr, "draw_options");
563 /* The clipping currently looks a bit weird without the white circle around the gizmo.
564 * However, without clipping it looks also very confusing sometimes. */
565 draw_options |= ED_GIZMO_DIAL_DRAW_FLAG_CLIP;
566 SET_FLAG_FROM_TEST(draw_options, is_interacting, ED_GIZMO_DIAL_DRAW_FLAG_ANGLE_VALUE);
567 RNA_enum_set(gizmo->ptr, "draw_options", draw_options);
568
570 WM_gizmo_set_line_width(gizmo, 3.0f);
571 RNA_boolean_set(gizmo->ptr, "wrap_angle", false);
572 }
573 }
574
576 {
577 for (const int axis : IndexRange(3)) {
578 wmGizmo *gizmo = scale_gizmos_[axis];
579 get_axis_gizmo_colors(axis, gizmo->color, gizmo->color_hi);
580 RNA_enum_set(gizmo->ptr, "draw_style", ED_GIZMO_ARROW_STYLE_BOX);
581
582 const float length = (any_translation_visible_ || any_rotation_visible_) ? 0.775f : 1.0f;
583 RNA_float_set(gizmo->ptr, "length", length);
584
585 WM_gizmo_set_line_width(gizmo, 2.0f);
586 }
587 }
588
590 const float4x4 &base_transform_from_socket)
591 {
592 for (const int axis_i : IndexRange(3)) {
593 const math::Axis axis = math::Axis::from_int(axis_i);
594 wmGizmo *gizmo = translation_gizmos_[axis_i];
595 if (gizmo_is_interacting(*gizmo)) {
596 continue;
597 }
598
599 const float4x4 gizmo_transform = get_axis_gizmo_matrix_basis(
600 axis, base_transform_from_socket, params);
601 copy_m4_m4(gizmo->matrix_basis, gizmo_transform.ptr());
602
603 edit_data_.current_translation[axis_i] = 0.0f;
604
606 params.user_data = this;
607 params.value_set_fn = [](const wmGizmo *gz,
608 wmGizmoProperty *gz_prop,
609 const void *value_ptr) {
610 TransformGizmos &self = *static_cast<TransformGizmos *>(gz_prop->custom_func.user_data);
611 const int axis_i = Span(self.translation_gizmos_).first_index(const_cast<wmGizmo *>(gz));
612 const float new_gizmo_value = *static_cast<const float *>(value_ptr);
613 self.edit_data_.current_translation[axis_i] = new_gizmo_value;
614 float3 translation{};
615 translation[axis_i] = new_gizmo_value;
616 self.apply_change("Value", [&](bke::SocketValueVariant &value_variant) {
617 float4x4 value = value_variant.get<float4x4>();
618 const float3x3 orientation = float3x3(value);
619 float3 offset{};
620 if (self.transform_orientation_ == V3D_ORIENT_GLOBAL) {
621 offset = math::transform_direction(math::invert(self.parent_transform_), translation);
622 }
623 else {
624 const float factor = safe_divide(
625 1.0f, math::length((self.parent_transform_.view<3, 3>() * orientation)[axis_i]));
626 offset = math::transform_direction(orientation, translation) * factor;
627 }
628 value.location() += offset;
629 value_variant.set(value);
630 });
631 };
632 params.value_get_fn = [](const wmGizmo *gz, wmGizmoProperty *gz_prop, void *value_ptr) {
633 TransformGizmos &self = *static_cast<TransformGizmos *>(gz_prop->custom_func.user_data);
634 const int axis_i = Span(self.translation_gizmos_).first_index(const_cast<wmGizmo *>(gz));
635 *static_cast<float *>(value_ptr) = self.edit_data_.current_translation[axis_i];
636 };
637 WM_gizmo_target_property_def_func(gizmo, "offset", &params);
638 }
639 }
640
642 const float4x4 &base_transform_from_socket)
643 {
644 for (const int axis_i : IndexRange(3)) {
645 const math::Axis axis = math::Axis::from_int(axis_i);
646 wmGizmo *gizmo = rotation_gizmos_[axis_i];
647 if (gizmo_is_interacting(*gizmo)) {
648 continue;
649 }
650
651 const float4x4 gizmo_transform = get_axis_gizmo_matrix_basis(
652 axis, base_transform_from_socket, params);
653 copy_m4_m4(gizmo->matrix_basis, gizmo_transform.ptr());
654
655 edit_data_.current_rotation[axis_i] = 0.0f;
656
658 params.user_data = this;
659 params.value_set_fn = [](const wmGizmo *gz,
660 wmGizmoProperty *gz_prop,
661 const void *value_ptr) {
662 TransformGizmos &self = *static_cast<TransformGizmos *>(gz_prop->custom_func.user_data);
663 const int axis_i = Span(self.rotation_gizmos_).first_index(const_cast<wmGizmo *>(gz));
664 const math::Axis axis = math::Axis::from_int(axis_i);
665 const float new_gizmo_value = *static_cast<const float *>(value_ptr);
666 self.edit_data_.current_rotation[axis_i] = new_gizmo_value;
667 self.apply_change("Value", [&](bke::SocketValueVariant &value_variant) {
668 float4x4 value = value_variant.get<float4x4>();
669 float3 local_rotation_axis;
670 if (self.transform_orientation_ == V3D_ORIENT_GLOBAL) {
671 local_rotation_axis = math::normalize(math::transform_direction(
672 math::invert(float3x3(self.parent_transform_)), math::to_vector<float3>(axis)));
673 }
674 else {
675 local_rotation_axis = math::normalize(float3(value[axis_i]));
676 }
677 float3x3 rotation_matrix;
678 rotation_matrix = math::from_rotation<float3x3>(
679 math::AxisAngle(local_rotation_axis, -new_gizmo_value));
680 value.view<3, 3>() = rotation_matrix * value.view<3, 3>();
681 value_variant.set(value);
682 });
683 };
684 params.value_get_fn = [](const wmGizmo *gz, wmGizmoProperty *gz_prop, void *value_ptr) {
685 TransformGizmos &self = *static_cast<TransformGizmos *>(gz_prop->custom_func.user_data);
686 const int axis_i = Span(self.rotation_gizmos_).first_index(const_cast<wmGizmo *>(gz));
687 *static_cast<float *>(value_ptr) = self.edit_data_.current_rotation[axis_i];
688 };
689 WM_gizmo_target_property_def_func(gizmo, "offset", &params);
690 }
691 }
692
694 const float4x4 &base_transform_from_socket)
695 {
696 for (const int axis_i : IndexRange(3)) {
697 const math::Axis axis = math::Axis::from_int(axis_i);
698 wmGizmo *gizmo = scale_gizmos_[axis_i];
699 if (gizmo_is_interacting(*gizmo)) {
700 continue;
701 }
702
703 const float4x4 gizmo_transform = get_axis_gizmo_matrix_basis(
704 axis, base_transform_from_socket, params);
705 copy_m4_m4(gizmo->matrix_basis, gizmo_transform.ptr());
706
707 edit_data_.current_scale[axis_i] = 0.0f;
708
710 params.user_data = this;
711 params.value_set_fn = [](const wmGizmo *gz,
712 wmGizmoProperty *gz_prop,
713 const void *value_ptr) {
714 TransformGizmos &self = *static_cast<TransformGizmos *>(gz_prop->custom_func.user_data);
715 const int axis_i = Span(self.scale_gizmos_).first_index(const_cast<wmGizmo *>(gz));
716 const math::Axis axis = math::Axis::from_int(axis_i);
717 const float new_gizmo_value = *static_cast<const float *>(value_ptr);
718 self.edit_data_.current_scale[axis_i] = new_gizmo_value;
719 float3 scale{1.0f, 1.0f, 1.0f};
720 scale[axis_i] += new_gizmo_value;
721 self.apply_change("Value", [&](bke::SocketValueVariant &value_variant) {
722 float4x4 value = value_variant.get<float4x4>();
723 float3 local_scale_axis;
724 if (self.transform_orientation_ == V3D_ORIENT_GLOBAL) {
726 math::invert(float3x3(self.parent_transform_)), math::to_vector<float3>(axis)));
727 }
728 else {
729 local_scale_axis = math::normalize(float3(value[axis_i]));
730 }
731 const float3x3 rotation_matrix = math::from_rotation<float3x3>(
732 math::AxisAngle(local_scale_axis, math::to_vector<float3>(axis)));
733 const float3x3 scale_matrix = math::invert(rotation_matrix) *
734 math::from_scale<float3x3>(scale) * rotation_matrix;
735 value.view<3, 3>() = scale_matrix * value.view<3, 3>();
736 value_variant.set(value);
737 });
738 };
739 params.value_get_fn = [](const wmGizmo *gz, wmGizmoProperty *gz_prop, void *value_ptr) {
740 TransformGizmos &self = *static_cast<TransformGizmos *>(gz_prop->custom_func.user_data);
741 const int axis_i = Span(self.scale_gizmos_).first_index(const_cast<wmGizmo *>(gz));
742 *static_cast<float *>(value_ptr) = self.edit_data_.current_scale[axis_i];
743 };
744 WM_gizmo_target_property_def_func(gizmo, "offset", &params);
745 }
746 }
747
749 const float4x4 &base_transform_from_socket,
750 const GizmosUpdateParams &params) const
751 {
752 float4x4 gizmo_transform;
753 const float3 global_location =
754 (params.parent_transform * base_transform_from_socket).location();
755 const float3 axis_direction = math::to_vector<float3>(axis);
756 float3 global_direction{};
757 if (transform_orientation_ == V3D_ORIENT_GLOBAL) {
758 global_direction = axis_direction;
759 }
760 else {
761 global_direction = math::transform_direction(params.parent_transform.view<3, 3>() *
762 base_transform_from_socket.view<3, 3>(),
763 axis_direction);
764 }
765 global_direction = math::normalize(global_direction);
766 gizmo_transform.location() = global_location;
768 global_location, global_direction, math::AxisSigned::Z_POS);
769 }
770};
771
776
778
780 {
781 return get_default_hash(this->object_orig, this->gizmo_id);
782 }
783};
784
789
790static std::unique_ptr<NodeGizmos> create_gizmo_node_gizmos(const bNode &gizmo_node)
791{
792 switch (gizmo_node.type_legacy) {
794 return std::make_unique<LinearGizmo>();
796 return std::make_unique<DialGizmo>();
798 return std::make_unique<TransformGizmos>();
799 }
800 return {};
801}
802
805 const bke::NodeGizmoID &gizmo_id)
806{
807 if (const auto *edit_data_component = geometry.get_component<bke::GeometryComponentEditData>()) {
808 if (edit_data_component->gizmo_edit_hints_) {
809 if (const float4x4 *m = edit_data_component->gizmo_edit_hints_->gizmo_transforms.lookup_ptr(
810 gizmo_id))
811 {
812 return m;
813 }
814 }
815 }
816 return nullptr;
817}
818
823 const bke::NodeGizmoID &gizmo_id)
824{
825 if (find_direct_gizmo_transform(geometry, gizmo_id)) {
826 return true;
827 }
828 if (!geometry.has_instances()) {
829 return false;
830 }
831 const bke::Instances *instances = geometry.get_instances();
832 for (const bke::InstanceReference &reference : instances->references()) {
833 if (reference.type() != bke::InstanceReference::Type::GeometrySet) {
834 continue;
835 }
836 const bke::GeometrySet &reference_geometry = reference.geometry_set();
837 if (has_nested_gizmo_transform(reference_geometry, gizmo_id)) {
838 return true;
839 }
840 }
841 return false;
842}
843
844static std::optional<float4x4> find_gizmo_geometry_transform_recursive(
845 const bke::GeometrySet &geometry, const bke::NodeGizmoID &gizmo_id, const float4x4 &transform)
846{
847 if (const float4x4 *m = find_direct_gizmo_transform(geometry, gizmo_id)) {
848 return transform * *m;
849 }
850 if (!geometry.has_instances()) {
851 return std::nullopt;
852 }
853 const bke::Instances *instances = geometry.get_instances();
854 const Span<bke::InstanceReference> references = instances->references();
855 const Span<int> handles = instances->reference_handles();
856 const Span<float4x4> transforms = instances->transforms();
857 for (const int reference_i : references.index_range()) {
858 const bke::InstanceReference &reference = references[reference_i];
860 continue;
861 }
862 const bke::GeometrySet &reference_geometry = reference.geometry_set();
863 if (has_nested_gizmo_transform(reference_geometry, gizmo_id)) {
864 const int index = handles.first_index_try(reference_i);
865 if (index >= 0) {
866 const float4x4 sub_transform = transform * transforms[index];
867 if (const std::optional<float4x4> m = find_gizmo_geometry_transform_recursive(
868 reference_geometry, gizmo_id, sub_transform))
869 {
870 return m;
871 }
872 }
873 }
874 }
875 return std::nullopt;
876}
877
883 const NodesModifierData &nmd_orig,
884 const View3D &v3d)
885{
886 if (v3d.flag2 & V3D_SHOW_VIEWER) {
888 if (const geo_eval_log::ViewerNodeLog *viewer_log =
889 nmd_orig.runtime->eval_log->find_viewer_node_log_for_path(viewer_path))
890 {
891 if (const bke::GeometrySet *viewer_geometry = viewer_log->main_geometry()) {
892 return *viewer_geometry;
893 }
894 }
895 }
896 return bke::object_get_evaluated_geometry_set(object_eval);
897}
898
902static std::optional<float4x4> find_gizmo_geometry_transform(const bke::GeometrySet &geometry,
903 const bke::NodeGizmoID &gizmo_id)
904{
905 const float4x4 identity = float4x4::identity();
906 return find_gizmo_geometry_transform_recursive(geometry, gizmo_id, identity);
907}
908
910{
911 ScrArea *area = CTX_wm_area(C);
912 View3D *v3d = static_cast<View3D *>(area->spacedata.first);
914 return false;
915 }
916 return true;
917}
918
919static void WIDGETGROUP_geometry_nodes_setup(const bContext * /*C*/, wmGizmoGroup *gzgroup)
920{
921 GeometryNodesGizmoGroup *gzgroup_data = MEM_new<GeometryNodesGizmoGroup>(__func__);
922 gzgroup->customdata = gzgroup_data;
923 gzgroup->customdata_free = [](void *data) {
924 auto *gzgroup_data = static_cast<GeometryNodesGizmoGroup *>(data);
925 MEM_delete(gzgroup_data);
926 };
927}
928
930{
931 auto &gzgroup_data = *static_cast<GeometryNodesGizmoGroup *>(gzgroup->customdata);
932
933 View3D *v3d = CTX_wm_view3d(C);
934 if (!v3d) {
935 return;
936 }
937
939 if (wm == nullptr) {
940 return;
941 }
943
944 /* A new map containing the active gizmos is build. This is less error prone than trying to
945 * update the old map in place. */
947
948 /* This needs to stay around for a bit longer because the compute contexts are required when
949 * applying the gizmo changes. */
950 auto compute_context_cache = std::make_shared<bke::ComputeContextCache>();
951
953 *C,
954 *compute_context_cache,
955 [&](const Object &object_orig,
956 const NodesModifierData &nmd_orig,
957 const ComputeContext &compute_context,
958 const bNode &gizmo_node,
959 const bNodeSocket &gizmo_socket) {
960 const GeoNodesObjectGizmoID gizmo_id = {&object_orig,
961 {compute_context.hash(), gizmo_node.identifier}};
962 if (new_gizmos_by_node.contains(gizmo_id)) {
963 /* Already handled. */
964 return;
965 }
966 if (!nmd_orig.runtime->eval_log) {
967 /* Can't create gizmos without any logged data. */
968 return;
969 }
970 Object *object_eval = DEG_get_evaluated(depsgraph, const_cast<Object *>(&object_orig));
971 if (!object_eval) {
972 return;
973 }
974
975 const bke::GeometrySet geometry = find_geometry_for_gizmo(*object_eval, nmd_orig, *v3d);
976
977 /* Figure out which parts of the gizmo are editable. */
979 compute_context, gizmo_node, gizmo_socket);
980
981 bNodeTree &ntree = *nmd_orig.node_group;
982 ntree.ensure_topology_cache();
983
984 NodeGizmos *node_gizmos = nullptr;
985 if (std::optional<std::unique_ptr<NodeGizmos>> old_gizmos =
986 gzgroup_data.gizmos_by_node.pop_try(gizmo_id))
987 {
988 /* Gizmos for this node existed already, reuse them. */
989 node_gizmos = old_gizmos->get();
990 new_gizmos_by_node.add(gizmo_id, std::move(*old_gizmos));
991 }
992 else {
993 /* There are no gizmos for this node yet, create new ones. */
994 std::unique_ptr<NodeGizmos> new_node_gizmos = create_gizmo_node_gizmos(gizmo_node);
995 new_node_gizmos->create_gizmos(*gzgroup);
996 /* Enable undo for all geometry nodes gizmos. */
997 for (wmGizmo *gizmo : new_node_gizmos->get_all_gizmos()) {
998 gizmo->flag |= WM_GIZMO_NEEDS_UNDO;
999 }
1000 node_gizmos = new_node_gizmos.get();
1001 new_gizmos_by_node.add(gizmo_id, std::move(new_node_gizmos));
1002 }
1003
1004 /* Initially show all gizmos. They may be hidden as part of the update again. */
1005 node_gizmos->show_all();
1006
1007 GeoTreeLog &tree_log = nmd_orig.runtime->eval_log->get_tree_log(compute_context.hash());
1008 tree_log.ensure_socket_values();
1009 tree_log.ensure_evaluated_gizmo_nodes();
1010
1011 const std::optional<float4x4> crazy_space_geometry_transform =
1013
1014 const bool missing_logged_data = !tree_log.evaluated_gizmo_nodes.contains(
1015 gizmo_node.identifier);
1016 if (missing_logged_data) {
1017 /* Rerun modifier to make sure that values are logged. */
1019 depsgraph, const_cast<ID *>(&object_orig.id), ID_RECALC_GEOMETRY);
1021 node_gizmos->hide_all();
1022 return;
1023 }
1024 const bool missing_used_transform = gizmo_node.output_socket(0).is_logically_linked() &&
1025 !crazy_space_geometry_transform.has_value();
1026 if (missing_used_transform) {
1027 node_gizmos->hide_all();
1028 return;
1029 }
1030
1031 const float4x4 object_to_world{object_eval->object_to_world()};
1032 const float4x4 geometry_transform = crazy_space_geometry_transform.value_or(
1034
1035 UpdateReport report;
1036 GizmosUpdateParams update_params{
1037 *C, object_to_world * geometry_transform, gizmo_node, tree_log, report, elem};
1038 node_gizmos->update(update_params);
1039
1040 bool any_interacting = node_gizmos->is_any_interacting();
1041
1042 if (!any_interacting) {
1043 if (report.missing_socket_logs || report.invalid_transform) {
1044 /* Avoid showing gizmos which are in the wrong place. */
1045 node_gizmos->hide_all();
1046 return;
1047 }
1048 /* Update the callback to apply gizmo changes based on the new context. */
1049 node_gizmos->apply_change =
1050 [C = C,
1051 compute_context_cache,
1052 compute_context = &compute_context,
1053 gizmo_node_tree = &gizmo_node.owner_tree(),
1054 gizmo_node = &gizmo_node,
1055 object_orig = &object_orig,
1056 nmd = &nmd_orig,
1057 eval_log = nmd_orig.runtime->eval_log](
1058 const StringRef socket_identifier,
1059 const FunctionRef<void(bke::SocketValueVariant &)> modify_value) {
1060 gizmo_node_tree->ensure_topology_cache();
1061 const bNodeSocket &socket = *gizmo_node->input_by_identifier(socket_identifier);
1062
1064 const_cast<Object &>(*object_orig),
1065 const_cast<NodesModifierData &>(*nmd),
1066 *eval_log,
1067 *compute_context,
1068 socket,
1069 modify_value);
1070
1074 };
1075 }
1076 });
1077
1078 /* Hide all except the interacting gizmo. */
1079 bool any_gizmo_interactive = false;
1080 for (const std::unique_ptr<NodeGizmos> &node_gizmos : new_gizmos_by_node.values()) {
1081 any_gizmo_interactive |= node_gizmos->is_any_interacting();
1082 }
1083 if (any_gizmo_interactive) {
1084 for (std::unique_ptr<NodeGizmos> &node_gizmos : new_gizmos_by_node.values()) {
1085 for (wmGizmo *gizmo : node_gizmos->get_all_gizmos()) {
1086 if (!gizmo_is_interacting(*gizmo)) {
1087 WM_gizmo_set_flag(gizmo, WM_GIZMO_HIDDEN, true);
1088 }
1089 }
1090 }
1091 }
1092
1093 /* Remove gizmos that are not used anymore. */
1094 for (std::unique_ptr<NodeGizmos> &node_gizmos : gzgroup_data.gizmos_by_node.values()) {
1095 const Vector<wmGizmo *> gizmos = node_gizmos->get_all_gizmos();
1096 for (wmGizmo *gizmo : gizmos) {
1097 WM_gizmo_unlink(&gzgroup->gizmos, gzgroup->parent_gzmap, gizmo, const_cast<bContext *>(C));
1098 }
1099 }
1100
1101 gzgroup_data.gizmos_by_node = std::move(new_gizmos_by_node);
1102}
1103
1105 wmGizmoGroup * /*gzgroup*/)
1106{
1107}
1108
1109} // namespace blender::ed::view3d::geometry_nodes_gizmos
1110
1112{
1114
1115 gzgt->name = "Geometry Nodes Widgets";
1116 gzgt->idname = "VIEW3D_GGT_geometry_nodes";
1117
1119
1125}
ScrArea * CTX_wm_area(const bContext *C)
Depsgraph * CTX_data_depsgraph_pointer(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Main * CTX_data_main(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
View3D * CTX_wm_view3d(const bContext *C)
void BKE_main_ensure_invariants(Main &bmain, std::optional< blender::Span< ID * > > modified_ids=std::nullopt)
#define GEO_NODE_GIZMO_LINEAR
#define GEO_NODE_GIZMO_DIAL
#define GEO_NODE_GIZMO_TRANSFORM
General operations, lookup, etc. for blender objects.
#define BLI_assert(a)
Definition BLI_assert.h:46
MINLINE float safe_divide(float a, float b)
void copy_m4_m4(float m1[4][4], const float m2[4][4])
void unit_m4(float m[4][4])
void rotation_between_vecs_to_quat(float q[4], const float v1[3], const float v2[3])
#define BLI_STRUCT_EQUALITY_OPERATORS_2(Type, m1, m2)
#define SET_FLAG_FROM_TEST(value, test, flag)
void DEG_id_tag_update_for_side_effect_request(Depsgraph *depsgraph, ID *id, unsigned int flags)
T * DEG_get_evaluated(const Depsgraph *depsgraph, T *id)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
GeometryNodeGizmoColor
@ GEO_NODE_GIZMO_COLOR_Z
@ GEO_NODE_GIZMO_COLOR_Y
@ GEO_NODE_GIZMO_COLOR_X
@ GEO_NODE_GIZMO_COLOR_PRIMARY
@ GEO_NODE_GIZMO_COLOR_SECONDARY
@ GEO_NODE_LINEAR_GIZMO_DRAW_STYLE_CROSS
@ GEO_NODE_LINEAR_GIZMO_DRAW_STYLE_BOX
@ GEO_NODE_LINEAR_GIZMO_DRAW_STYLE_ARROW
@ GEO_NODE_TRANSFORM_GIZMO_USE_ROTATION_X
@ GEO_NODE_TRANSFORM_GIZMO_USE_SCALE_X
@ GEO_NODE_TRANSFORM_GIZMO_USE_TRANSLATION_X
@ V3D_SHOW_VIEWER
@ V3D_GIZMO_HIDE_MODIFIER
@ V3D_ORIENT_GLOBAL
@ ED_GIZMO_DIAL_DRAW_FLAG_ANGLE_VALUE
@ ED_GIZMO_DIAL_DRAW_FLAG_CLIP
@ ED_GIZMO_ARROW_STYLE_CROSS
@ ED_GIZMO_ARROW_STYLE_BOX
@ ED_GIZMO_ARROW_STYLE_NORMAL
#define C
Definition RandGen.cpp:29
void UI_GetThemeColor3fv(int colorid, float col[3])
ThemeColorID
@ TH_GIZMO_HI
@ TH_AXIS_Y
@ TH_AXIS_X
@ TH_GIZMO_PRIMARY
@ TH_AXIS_Z
@ TH_GIZMO_SECONDARY
@ WM_GIZMO_DRAW_NO_SCALE
@ WM_GIZMO_HIDDEN
@ WM_GIZMO_DRAW_VALUE
@ WM_GIZMO_NEEDS_UNDO
@ WM_GIZMO_DRAW_OFFSET_SCALE
@ WM_GIZMOGROUPTYPE_3D
@ WM_GIZMOGROUPTYPE_PERSISTENT
#define NC_GEOM
Definition WM_types.hh:393
#define ND_DATA
Definition WM_types.hh:509
BMesh const char void * data
PyObject * self
BPy_StructRNA * depsgraph
unsigned long long int uint64_t
const ComputeContextHash & hash() const
ValueIterator values() const &
Definition BLI_map.hh:884
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:295
bool contains(const Key &key) const
Definition BLI_map.hh:353
constexpr int64_t first_index(const T &search_value) const
Definition BLI_span.hh:377
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
void extend(Span< T > array)
Span< int > reference_handles() const
Definition instances.cc:215
Span< float4x4 > transforms() const
Definition instances.cc:228
Span< InstanceReference > references() const
Definition instances.cc:275
void update_style(const NodeGeometryDialGizmo &storage, const bool is_interacting)
virtual void create_gizmos(wmGizmoGroup &gzgroup)=0
float4x4 get_axis_gizmo_matrix_basis(const math::Axis axis, const float4x4 &base_transform_from_socket, const GizmosUpdateParams &params) const
void update_scale_transform_and_target_property(GizmosUpdateParams &params, const float4x4 &base_transform_from_socket)
void update_translate_transform_and_target_property(GizmosUpdateParams &params, const float4x4 &base_transform_from_socket)
void update_visibility(GizmosUpdateParams &params, const NodeGeometryTransformGizmo &storage)
void update_rotate_transform_and_target_property(GizmosUpdateParams &params, const float4x4 &base_transform_from_socket)
static constexpr Axis from_int(const int axis_int)
#define main()
float length(VecOp< float, D >) RET
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
#define T
GeometrySet object_get_evaluated_geometry_set(const Object &object, bool apply_subdiv=true)
static ThemeColorID get_axis_theme_color_id(const int axis)
static float4x4 matrix_from_position_and_up_direction(const float3 &position, const float3 &direction, const math::AxisSigned direction_axis)
static void WIDGETGROUP_geometry_nodes_refresh(const bContext *C, wmGizmoGroup *gzgroup)
static const float4x4 * find_direct_gizmo_transform(const bke::GeometrySet &geometry, const bke::NodeGizmoID &gizmo_id)
static void WIDGETGROUP_geometry_nodes_setup(const bContext *, wmGizmoGroup *gzgroup)
static std::optional< float4x4 > find_gizmo_geometry_transform(const bke::GeometrySet &geometry, const bke::NodeGizmoID &gizmo_id)
static std::unique_ptr< NodeGizmos > create_gizmo_node_gizmos(const bNode &gizmo_node)
static void WIDGETGROUP_geometry_nodes_draw_prepare(const bContext *, wmGizmoGroup *)
static bool WIDGETGROUP_geometry_nodes_poll(const bContext *C, wmGizmoGroupType *)
std::function< void( StringRef socket_identifier, FunctionRef< void(bke::SocketValueVariant &value)> modify_value)> ApplyChangeFn
static ThemeColorID get_gizmo_theme_color_id(const GeometryNodeGizmoColor color_id)
static bool gizmo_is_interacting(const wmGizmo &gizmo)
static bool has_nested_gizmo_transform(const bke::GeometrySet &geometry, const bke::NodeGizmoID &gizmo_id)
static void get_axis_gizmo_colors(const int axis, float *r_color, float *r_color_hi)
static bke::GeometrySet find_geometry_for_gizmo(const Object &object_eval, const NodesModifierData &nmd_orig, const View3D &v3d)
static std::optional< float4x4 > find_gizmo_geometry_transform_recursive(const bke::GeometrySet &geometry, const bke::NodeGizmoID &gizmo_id, const float4x4 &transform)
QuaternionBase< float > Quaternion
bool is_orthonormal(const MatT &mat)
T to_vector(const Axis axis)
T length(const VecBase< T, Size > &a)
AxisAngleBase< float, AngleRadianBase< float > > AxisAngle
bool is_unit_scale(const MatBase< T, NumCol, NumRow > &m)
T average(const VecBase< T, Size > &a)
bool is_zero(const T &a)
CartesianBasis invert(const CartesianBasis &basis)
AxisSigned cross(const AxisSigned a, const AxisSigned b)
MatT from_scale(const VecBase< typename MatT::base_type, ScaleDim > &scale)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
VecBase< T, 3 > to_scale(const MatBase< T, NumCol, NumRow > &mat)
VecBase< T, 3 > transform_direction(const MatBase< T, 3, 3 > &mat, const VecBase< T, 3 > &direction)
MatT from_rotation(const RotationT &rotation)
T determinant(const MatBase< T, Size, Size > &mat)
ie::ElemVariant get_editable_gizmo_elem(const ComputeContext &gizmo_context, const bNode &gizmo_node, const bNodeSocket &gizmo_socket)
void apply_gizmo_change(bContext &C, Object &object, NodesModifierData &nmd, geo_eval_log::GeoNodesLog &eval_log, const ComputeContext &gizmo_context, const bNodeSocket &gizmo_socket, const FunctionRef< void(bke::SocketValueVariant &value)> apply_on_gizmo_value_fn)
void foreach_active_gizmo(const bContext &C, bke::ComputeContextCache &compute_context_cache, const ForeachGizmoFn fn)
MatBase< float, 4, 4 > float4x4
uint64_t get_default_hash(const T &v, const Args &...args)
Definition BLI_hash.hh:233
MatBase< float, 3, 3 > float3x3
VecBase< float, 3 > float3
void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
void RNA_float_set(PointerRNA *ptr, const char *name, float value)
void RNA_enum_set(PointerRNA *ptr, const char *name, int value)
int RNA_enum_get(PointerRNA *ptr, const char *name)
Definition DNA_ID.h:414
void * first
struct bNodeTree * node_group
NodesModifierRuntimeHandle * runtime
TransformOrientationSlot orientation_slots[4]
ListBase spacedata
ViewerPath viewer_path
int16_t type_legacy
int32_t identifier
const c_style_mat & ptr() const
const MatView< T, ViewNumCol, ViewNumRow, NumCol, NumRow, SrcStartCol, SrcStartRow, Alignment > view() const
Map< GeoNodesObjectGizmoID, std::unique_ptr< NodeGizmos > > gizmos_by_node
wmGizmoGroupFnSetupKeymap setup_keymap
wmGizmoGroupFnRefresh refresh
wmGizmoGroupFnInit setup
const char * idname
eWM_GizmoFlagGroupTypeFlag flag
wmGizmoGroupFnPoll poll
wmGizmoGroupFnDrawPrepare draw_prepare
wmGizmoMap * parent_gzmap
void(* customdata_free)(void *)
wmGizmoPropertyFnGet value_get_fn
wmGizmoPropertyFnSet value_set_fn
void * interaction_data
float matrix_basis[4][4]
float matrix_offset[4][4]
float color_hi[4]
float color[4]
PointerRNA * ptr
ccl_device_inline Transform transform_scale(const float3 s)
Definition transform.h:280
ParamHandle ** handles
void VIEW3D_GGT_geometry_nodes(wmGizmoGroupType *gzgt)
void WM_main_add_notifier(uint type, void *reference)
PointerRNA * ptr
Definition wm_files.cc:4238
void WM_gizmo_set_line_width(wmGizmo *gz, const float line_width)
Definition wm_gizmo.cc:322
void WM_gizmo_set_flag(wmGizmo *gz, const int flag, const bool enable)
Definition wm_gizmo.cc:307
wmGizmo * WM_gizmo_new(const StringRef idname, wmGizmoGroup *gzgroup, PointerRNA *properties)
Definition wm_gizmo.cc:98
void WM_gizmo_unlink(ListBase *gizmolist, wmGizmoMap *gzmap, wmGizmo *gz, bContext *C)
Definition wm_gizmo.cc:165
wmKeyMap * WM_gizmogroup_setup_keymap_generic_maybe_drag(const wmGizmoGroupType *, wmKeyConfig *kc)
void WM_gizmo_target_property_def_func(wmGizmo *gz, const char *idname, const wmGizmoPropertyFnParams *params)