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