Skip to content

Commit 9fa4c62

Browse files
Editor: Add some gizmo handles for MeshInstance3D
1 parent adb2ec0 commit 9fa4c62

4 files changed

Lines changed: 240 additions & 34 deletions

File tree

editor/scene/3d/gizmos/gizmo_3d_helper.cpp

Lines changed: 90 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -142,26 +142,36 @@ void Gizmo3DHelper::box_commit_handle(const String &p_action_name, bool p_cancel
142142

143143
Vector<Vector3> Gizmo3DHelper::cylinder_get_handles(real_t p_height, real_t p_radius) {
144144
Vector<Vector3> handles;
145-
handles.push_back(Vector3(p_radius, 0, 0));
146145
handles.push_back(Vector3(0, p_height * 0.5, 0));
147146
handles.push_back(Vector3(0, p_height * -0.5, 0));
147+
handles.push_back(Vector3(p_radius, 0, 0));
148148
return handles;
149149
}
150150

151-
String Gizmo3DHelper::cylinder_get_handle_name(int p_id) const {
152-
if (p_id == 0) {
153-
return "Radius";
154-
} else {
151+
String Gizmo3DHelper::_cylinder_or_capsule_or_cone_frustum_get_handle_name(int p_id) const {
152+
if (p_id < 2) {
155153
return "Height";
154+
} else {
155+
return "Radius";
156156
}
157157
}
158158

159-
void Gizmo3DHelper::_cylinder_or_capsule_set_handle(const Vector3 p_segment[2], int p_id, real_t &r_height, real_t &r_radius, Vector3 &r_cylinder_position, bool p_is_capsule) {
160-
real_t initial_radius = initial_value.operator Vector2().x;
161-
real_t initial_height = initial_value.operator Vector2().y;
159+
void Gizmo3DHelper::_cylinder_or_capsule_or_cone_frustum_set_handle(const Vector3 p_segment[2], int p_id, real_t &r_height, real_t &r_radius_top, real_t &r_radius_bottom, Vector3 &r_position, bool p_is_capsule, bool p_is_frustum) {
160+
ERR_FAIL_COND(p_is_capsule && p_is_frustum);
161+
162+
real_t initial_radius_top, initial_radius_bottom, initial_height;
163+
if (p_is_frustum) {
164+
initial_radius_top = initial_value.operator Vector3().x;
165+
initial_radius_bottom = initial_value.operator Vector3().y;
166+
initial_height = initial_value.operator Vector3().z;
167+
} else {
168+
initial_radius_top = initial_value.operator Vector2().x;
169+
initial_radius_bottom = initial_radius_top;
170+
initial_height = initial_value.operator Vector2().y;
171+
}
162172

163-
int sign = p_id == 2 ? -1 : 1;
164-
int axis = p_id == 0 ? 0 : 1;
173+
int sign = p_id == 1 ? -1 : 1;
174+
int axis = p_id >= 2 ? 0 : 1;
165175

166176
Vector3 axis_vector;
167177
axis_vector[axis] = sign;
@@ -174,20 +184,7 @@ void Gizmo3DHelper::_cylinder_or_capsule_set_handle(const Vector3 p_segment[2],
174184
d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());
175185
}
176186

177-
if (p_id == 0) {
178-
// Adjust radius.
179-
if (d < 0.001) {
180-
d = 0.001;
181-
}
182-
r_radius = d;
183-
r_cylinder_position = initial_transform.get_origin();
184-
185-
if (p_is_capsule) {
186-
r_height = MAX(initial_height, r_radius * 2.0);
187-
} else {
188-
r_height = initial_height;
189-
}
190-
} else if (p_id == 1 || p_id == 2) {
187+
if (p_id < 2) {
191188
// Adjust height.
192189
if (Input::get_singleton()->is_key_pressed(Key::ALT)) {
193190
r_height = d * 2.0;
@@ -201,17 +198,41 @@ void Gizmo3DHelper::_cylinder_or_capsule_set_handle(const Vector3 p_segment[2],
201198

202199
// Adjust position.
203200
if (Input::get_singleton()->is_key_pressed(Key::ALT)) {
204-
r_cylinder_position = initial_transform.get_origin();
201+
r_position = initial_transform.get_origin();
205202
} else {
206203
Vector3 offset;
207204
offset[axis] = (r_height - initial_height) * 0.5 * sign;
208-
r_cylinder_position = initial_transform.xform(offset);
205+
r_position = initial_transform.xform(offset);
209206
}
210207

211208
if (p_is_capsule) {
212-
r_radius = MIN(initial_radius, r_height / 2.0);
209+
r_radius_top = MIN(initial_radius_top, r_height / 2.0);
210+
r_radius_bottom = r_radius_top;
211+
} else {
212+
r_radius_top = initial_radius_top;
213+
r_radius_bottom = initial_radius_bottom;
214+
}
215+
} else {
216+
// Adjust radius.
217+
if (d < 0.001) {
218+
d = 0.001;
219+
}
220+
if (!p_is_frustum) {
221+
r_radius_top = d;
222+
r_radius_bottom = d;
213223
} else {
214-
r_radius = initial_radius;
224+
real_t diff = d - (initial_radius_bottom + initial_radius_top) / 2.0;
225+
diff = MAX(MAX(-initial_radius_bottom + 0.001, -initial_radius_top + 0.001), diff);
226+
r_radius_top = initial_radius_top + diff;
227+
r_radius_bottom = initial_radius_bottom + diff;
228+
}
229+
230+
r_position = initial_transform.get_origin();
231+
232+
if (p_is_capsule) {
233+
r_height = MAX(initial_height, r_radius_top * 2.0);
234+
} else {
235+
r_height = initial_height;
215236
}
216237
}
217238
}
@@ -232,7 +253,7 @@ void Gizmo3DHelper::cylinder_commit_handle(int p_id, const String &p_radius_acti
232253
}
233254

234255
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
235-
ur->create_action(p_id == 0 ? p_radius_action_name : p_height_action_name);
256+
ur->create_action(p_id < 2 ? p_height_action_name : p_radius_action_name);
236257
ur->add_do_property(p_radius_object, p_radius_property, p_radius_object->get(p_radius_property));
237258
ur->add_undo_property(p_radius_object, p_radius_property, initial_value.operator Vector2().x);
238259
ur->add_do_property(p_height_object, p_height_property, p_height_object->get(p_height_property));
@@ -241,3 +262,43 @@ void Gizmo3DHelper::cylinder_commit_handle(int p_id, const String &p_radius_acti
241262
ur->add_undo_property(p_position_object, p_position_property, initial_transform.get_origin());
242263
ur->commit_action();
243264
}
265+
266+
Vector<Vector3> Gizmo3DHelper::cone_frustum_get_handles(real_t p_height, real_t p_radius_top, real_t p_radius_bottom) {
267+
Vector<Vector3> handles;
268+
handles.push_back(Vector3(0, p_height * 0.5, 0));
269+
handles.push_back(Vector3(0, p_height * -0.5, 0));
270+
handles.push_back(Vector3((p_radius_top + p_radius_bottom) / 2.0, 0, 0));
271+
return handles;
272+
}
273+
274+
void Gizmo3DHelper::cone_frustum_commit_handle(int p_id, const String &p_radius_action_name, const String &p_height_action_name, bool p_cancel, Object *p_position_object, Object *p_height_object, Object *p_radius_top_object, Object *p_radius_bottom_object, const StringName &p_position_property, const StringName &p_height_property, const StringName &p_radius_top_property, const StringName &p_radius_bottom_property) {
275+
if (!p_height_object) {
276+
p_height_object = p_position_object;
277+
}
278+
if (!p_radius_top_object) {
279+
p_radius_top_object = p_position_object;
280+
}
281+
if (!p_radius_bottom_object) {
282+
p_radius_bottom_object = p_position_object;
283+
}
284+
285+
if (p_cancel) {
286+
p_radius_top_object->set(p_radius_top_property, initial_value.operator Vector3().x);
287+
p_radius_bottom_object->set(p_radius_bottom_property, initial_value.operator Vector3().y);
288+
p_height_object->set(p_height_property, initial_value.operator Vector3().z);
289+
p_position_object->set(p_position_property, initial_transform.get_origin());
290+
return;
291+
}
292+
293+
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
294+
ur->create_action(p_id < 2 ? p_height_action_name : p_radius_action_name);
295+
ur->add_do_property(p_radius_top_object, p_radius_top_property, p_radius_top_object->get(p_radius_top_property));
296+
ur->add_do_property(p_radius_bottom_object, p_radius_bottom_property, p_radius_bottom_object->get(p_radius_bottom_property));
297+
ur->add_undo_property(p_radius_top_object, p_radius_top_property, initial_value.operator Vector3().x);
298+
ur->add_undo_property(p_radius_bottom_object, p_radius_bottom_property, initial_value.operator Vector3().y);
299+
ur->add_do_property(p_height_object, p_height_property, p_height_object->get(p_height_property));
300+
ur->add_undo_property(p_height_object, p_height_property, initial_value.operator Vector3().z);
301+
ur->add_do_property(p_position_object, p_position_property, p_position_object->get(p_position_property));
302+
ur->add_undo_property(p_position_object, p_position_property, initial_transform.get_origin());
303+
ur->commit_action();
304+
}

editor/scene/3d/gizmos/gizmo_3d_helper.h

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ class Gizmo3DHelper : public RefCounted {
4242
Transform3D initial_transform;
4343

4444
private:
45-
void _cylinder_or_capsule_set_handle(const Vector3 p_segment[2], int p_id, real_t &r_height, real_t &r_radius, Vector3 &r_cylinder_position, bool p_is_capsule);
45+
void _cylinder_or_capsule_or_cone_frustum_set_handle(const Vector3 p_segment[2], int p_id, real_t &r_height, real_t &r_radius_top, real_t &r_radius_bottom, Vector3 &r_position, bool p_is_capsule, bool p_is_frustum);
46+
String _cylinder_or_capsule_or_cone_frustum_get_handle_name(int p_id) const;
4647

4748
public:
4849
/**
@@ -51,6 +52,7 @@ class Gizmo3DHelper : public RefCounted {
5152
* Depending on the type of gizmo that will be used, different formats for the `p_initial_value` are required:
5253
* Box: The size of the box as `Vector3`
5354
* Cylinder or Capsule: A `Vector2` of the form `Vector2(radius, height)`
55+
* Cone frustum: A `Vector3` of the form `Vector3(radius_top, radius_bottom, height)`
5456
*/
5557
void initialize_handle_action(const Variant &p_initial_value, const Transform3D &p_initial_transform);
5658
void get_segment(Camera3D *p_camera, const Point2 &p_point, Vector3 *r_segment);
@@ -65,20 +67,31 @@ class Gizmo3DHelper : public RefCounted {
6567
// Cylinder
6668

6769
Vector<Vector3> cylinder_get_handles(real_t p_height, real_t p_radius);
68-
String cylinder_get_handle_name(int p_id) const;
70+
_FORCE_INLINE_ String cylinder_get_handle_name(int p_id) { return _cylinder_or_capsule_or_cone_frustum_get_handle_name(p_id); }
6971
_FORCE_INLINE_ void cylinder_set_handle(const Vector3 p_segment[2], int p_id, real_t &r_height, real_t &r_radius, Vector3 &r_cylinder_position) {
70-
_cylinder_or_capsule_set_handle(p_segment, p_id, r_height, r_radius, r_cylinder_position, false);
72+
real_t radius_bottom;
73+
_cylinder_or_capsule_or_cone_frustum_set_handle(p_segment, p_id, r_height, r_radius, radius_bottom, r_cylinder_position, false, false);
7174
}
7275
void cylinder_commit_handle(int p_id, const String &p_radius_action_name, const String &p_height_action_name, bool p_cancel, Object *p_position_object, Object *p_height_object = nullptr, Object *p_radius_object = nullptr, const StringName &p_position_property = "global_position", const StringName &p_height_property = "height", const StringName &p_radius_property = "radius");
7376

7477
// Capsule
7578

7679
_FORCE_INLINE_ Vector<Vector3> capsule_get_handles(real_t p_height, real_t p_radius) { return cylinder_get_handles(p_height, p_radius); }
77-
_FORCE_INLINE_ String capsule_get_handle_name(int p_id) { return cylinder_get_handle_name(p_id); }
80+
_FORCE_INLINE_ String capsule_get_handle_name(int p_id) { return _cylinder_or_capsule_or_cone_frustum_get_handle_name(p_id); }
7881
_FORCE_INLINE_ void capsule_set_handle(const Vector3 p_segment[2], int p_id, real_t &r_height, real_t &r_radius, Vector3 &r_capsule_position) {
79-
_cylinder_or_capsule_set_handle(p_segment, p_id, r_height, r_radius, r_capsule_position, true);
82+
real_t radius_bottom;
83+
_cylinder_or_capsule_or_cone_frustum_set_handle(p_segment, p_id, r_height, r_radius, radius_bottom, r_capsule_position, true, false);
8084
}
8185
_FORCE_INLINE_ void capsule_commit_handle(int p_id, const String &p_radius_action_name, const String &p_height_action_name, bool p_cancel, Object *p_position_object, Object *p_height_object = nullptr, Object *p_radius_object = nullptr, const StringName &p_position_property = "global_position", const StringName &p_height_property = "height", const StringName &p_radius_property = "radius") {
8286
cylinder_commit_handle(p_id, p_radius_action_name, p_height_action_name, p_cancel, p_position_object, p_height_object, p_radius_object, p_position_property, p_height_property, p_radius_property);
8387
}
88+
89+
// Cone frustum
90+
91+
Vector<Vector3> cone_frustum_get_handles(real_t p_height, real_t p_radius_top, real_t p_radius_bottom);
92+
_FORCE_INLINE_ String cone_frustum_get_handle_name(int p_id) { return _cylinder_or_capsule_or_cone_frustum_get_handle_name(p_id); }
93+
_FORCE_INLINE_ void cone_frustum_set_handle(const Vector3 p_segment[2], int p_id, real_t &r_height, real_t &r_radius_top, real_t &r_radius_bottom, Vector3 &r_frustum_position) {
94+
_cylinder_or_capsule_or_cone_frustum_set_handle(p_segment, p_id, r_height, r_radius_top, r_radius_bottom, r_frustum_position, false, true);
95+
}
96+
void cone_frustum_commit_handle(int p_id, const String &p_radius_action_name, const String &p_height_action_name, bool p_cancel, Object *p_position_object, Object *p_height_object = nullptr, Object *p_radius_top_object = nullptr, Object *p_radius_bottom_object = nullptr, const StringName &p_position_property = "global_position", const StringName &p_height_property = "height", const StringName &p_radius_top_property = "top_radius", const StringName &p_radius_bottom_property = "bottom_radius");
8497
};

editor/scene/3d/gizmos/mesh_instance_3d_gizmo_plugin.cpp

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,113 @@ bool MeshInstance3DGizmoPlugin::can_be_hidden() const {
5151
return false;
5252
}
5353

54+
String MeshInstance3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
55+
MeshInstance3D *mesh = Object::cast_to<MeshInstance3D>(p_gizmo->get_node_3d());
56+
57+
if (Object::cast_to<CapsuleMesh>(*mesh->get_mesh())) {
58+
return helper->capsule_get_handle_name(p_id);
59+
}
60+
61+
if (Object::cast_to<CylinderMesh>(*mesh->get_mesh())) {
62+
return helper->cone_frustum_get_handle_name(p_id);
63+
}
64+
65+
if (Object::cast_to<BoxMesh>(*mesh->get_mesh())) {
66+
return helper->box_get_handle_name(p_id);
67+
}
68+
69+
return "";
70+
}
71+
72+
Variant MeshInstance3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
73+
MeshInstance3D *mesh = Object::cast_to<MeshInstance3D>(p_gizmo->get_node_3d());
74+
75+
const Ref<CapsuleMesh> capsule_mesh = mesh->get_mesh();
76+
if (capsule_mesh.is_valid()) {
77+
return Vector2(capsule_mesh->get_radius(), capsule_mesh->get_height());
78+
}
79+
80+
const Ref<CylinderMesh> cylinder_mesh = mesh->get_mesh();
81+
if (cylinder_mesh.is_valid()) {
82+
return Vector3(cylinder_mesh->get_top_radius(), cylinder_mesh->get_bottom_radius(), cylinder_mesh->get_height());
83+
}
84+
85+
const Ref<BoxMesh> box_mesh = mesh->get_mesh();
86+
if (box_mesh.is_valid()) {
87+
return box_mesh->get_size();
88+
}
89+
90+
return Variant();
91+
}
92+
93+
void MeshInstance3DGizmoPlugin::begin_handle_action(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) {
94+
helper->initialize_handle_action(get_handle_value(p_gizmo, p_id, p_secondary), p_gizmo->get_node_3d()->get_global_transform());
95+
}
96+
97+
void MeshInstance3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) {
98+
MeshInstance3D *mesh = Object::cast_to<MeshInstance3D>(p_gizmo->get_node_3d());
99+
100+
Vector3 segment[2];
101+
helper->get_segment(p_camera, p_point, segment);
102+
103+
const Ref<CapsuleMesh> capsule_mesh = mesh->get_mesh();
104+
if (capsule_mesh.is_valid()) {
105+
real_t height, radius;
106+
Vector3 position;
107+
helper->capsule_set_handle(segment, p_id, height, radius, position);
108+
capsule_mesh->set_height(height);
109+
capsule_mesh->set_radius(radius);
110+
mesh->set_global_position(position);
111+
}
112+
113+
const Ref<CylinderMesh> cylinder_mesh = mesh->get_mesh();
114+
if (cylinder_mesh.is_valid()) {
115+
real_t height, radius_top, radius_bottom;
116+
Vector3 position;
117+
helper->cone_frustum_set_handle(segment, p_id, height, radius_top, radius_bottom, position);
118+
cylinder_mesh->set_height(height);
119+
cylinder_mesh->set_top_radius(radius_top);
120+
cylinder_mesh->set_bottom_radius(radius_bottom);
121+
mesh->set_global_position(position);
122+
}
123+
124+
const Ref<BoxMesh> box_mesh = mesh->get_mesh();
125+
if (box_mesh.is_valid()) {
126+
Vector3 box_size, position;
127+
helper->box_set_handle(segment, p_id, box_size, position);
128+
box_mesh->set_size(box_size);
129+
mesh->set_global_position(position);
130+
}
131+
}
132+
133+
void MeshInstance3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel) {
134+
MeshInstance3D *mesh = Object::cast_to<MeshInstance3D>(p_gizmo->get_node_3d());
135+
136+
const Ref<CapsuleMesh> capsule_mesh = mesh->get_mesh();
137+
if (capsule_mesh.is_valid()) {
138+
helper->cylinder_commit_handle(p_id, TTR("Change Capsule Mesh Radius"), TTR("Change Capsule Mesh Height"), p_cancel, mesh, *capsule_mesh, *capsule_mesh);
139+
}
140+
141+
const Ref<CylinderMesh> cylinder_mesh = mesh->get_mesh();
142+
if (cylinder_mesh.is_valid()) {
143+
helper->cone_frustum_commit_handle(p_id, TTR("Change Cylinder Mesh Radius"), TTR("Change Cylinder Mesh Height"), p_cancel, mesh, *cylinder_mesh, *cylinder_mesh, *cylinder_mesh);
144+
}
145+
146+
const Ref<BoxMesh> box_mesh = mesh->get_mesh();
147+
if (box_mesh.is_valid()) {
148+
helper->box_commit_handle(TTR("Change Box Mesh Size"), p_cancel, mesh, *box_mesh);
149+
}
150+
}
151+
54152
void MeshInstance3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
55153
MeshInstance3D *mesh = Object::cast_to<MeshInstance3D>(p_gizmo->get_node_3d());
56154

57155
p_gizmo->clear();
58156

59157
Ref<Mesh> m = mesh->get_mesh();
60158

159+
const Ref<Material> handles_material = get_material("handles");
160+
61161
if (m.is_null()) {
62162
return; //none
63163
}
@@ -81,4 +181,22 @@ void MeshInstance3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
81181
if (tm.is_valid()) {
82182
p_gizmo->add_collision_triangles(tm);
83183
}
184+
185+
const Ref<CapsuleMesh> capsule_mesh = mesh->get_mesh();
186+
if (capsule_mesh.is_valid()) {
187+
const Vector<Vector3> handles = helper->capsule_get_handles(capsule_mesh->get_height(), capsule_mesh->get_radius());
188+
p_gizmo->add_handles(handles, handles_material);
189+
}
190+
191+
const Ref<CylinderMesh> cylinder_mesh = mesh->get_mesh();
192+
if (cylinder_mesh.is_valid()) {
193+
const Vector<Vector3> handles = helper->cone_frustum_get_handles(cylinder_mesh->get_height(), cylinder_mesh->get_top_radius(), cylinder_mesh->get_bottom_radius());
194+
p_gizmo->add_handles(handles, handles_material);
195+
}
196+
197+
const Ref<BoxMesh> box_mesh = mesh->get_mesh();
198+
if (box_mesh.is_valid()) {
199+
const Vector<Vector3> handles = helper->box_get_handles(box_mesh->get_size());
200+
p_gizmo->add_handles(handles, handles_material);
201+
}
84202
}

editor/scene/3d/gizmos/mesh_instance_3d_gizmo_plugin.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,29 @@
3030

3131
#pragma once
3232

33+
#include "editor/scene/3d/gizmos/gizmo_3d_helper.h"
3334
#include "editor/scene/3d/node_3d_editor_gizmos.h"
3435

3536
class MeshInstance3DGizmoPlugin : public EditorNode3DGizmoPlugin {
3637
GDCLASS(MeshInstance3DGizmoPlugin, EditorNode3DGizmoPlugin);
3738

39+
Ref<Gizmo3DHelper> helper;
40+
3841
public:
3942
bool has_gizmo(Node3D *p_spatial) override;
4043
String get_gizmo_name() const override;
4144
int get_priority() const override;
4245
bool can_be_hidden() const override;
4346
void redraw(EditorNode3DGizmo *p_gizmo) override;
47+
48+
String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const override;
49+
Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const override;
50+
void begin_handle_action(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) override;
51+
void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) override;
52+
void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel = false) override;
53+
54+
MeshInstance3DGizmoPlugin() {
55+
helper.instantiate();
56+
create_handle_material("handles");
57+
}
4458
};

0 commit comments

Comments
 (0)