Skip to content

Commit bf7cde5

Browse files
committed
Merge pull request godotengine#105523 from HolonProduction/gizmo-mesh
Editor: Add some gizmo handles for `MeshInstance3D`
2 parents 73840da + 9ff6eea commit bf7cde5

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
@@ -143,26 +143,36 @@ void Gizmo3DHelper::box_commit_handle(const String &p_action_name, bool p_cancel
143143

144144
Vector<Vector3> Gizmo3DHelper::cylinder_get_handles(real_t p_height, real_t p_radius) {
145145
Vector<Vector3> handles;
146-
handles.push_back(Vector3(p_radius, 0, 0));
147146
handles.push_back(Vector3(0, p_height * 0.5, 0));
148147
handles.push_back(Vector3(0, p_height * -0.5, 0));
148+
handles.push_back(Vector3(p_radius, 0, 0));
149149
return handles;
150150
}
151151

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

160-
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) {
161-
real_t initial_radius = initial_value.operator Vector2().x;
162-
real_t initial_height = initial_value.operator Vector2().y;
160+
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) {
161+
ERR_FAIL_COND(p_is_capsule && p_is_frustum);
162+
163+
real_t initial_radius_top, initial_radius_bottom, initial_height;
164+
if (p_is_frustum) {
165+
initial_radius_top = initial_value.operator Vector3().x;
166+
initial_radius_bottom = initial_value.operator Vector3().y;
167+
initial_height = initial_value.operator Vector3().z;
168+
} else {
169+
initial_radius_top = initial_value.operator Vector2().x;
170+
initial_radius_bottom = initial_radius_top;
171+
initial_height = initial_value.operator Vector2().y;
172+
}
163173

164-
int sign = p_id == 2 ? -1 : 1;
165-
int axis = p_id == 0 ? 0 : 1;
174+
int sign = p_id == 1 ? -1 : 1;
175+
int axis = p_id >= 2 ? 0 : 1;
166176

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

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

203200
// Adjust position.
204201
if (Input::get_singleton()->is_key_pressed(Key::ALT)) {
205-
r_cylinder_position = initial_transform.get_origin();
202+
r_position = initial_transform.get_origin();
206203
} else {
207204
Vector3 offset;
208205
offset[axis] = (r_height - initial_height) * 0.5 * sign;
209-
r_cylinder_position = initial_transform.xform(offset);
206+
r_position = initial_transform.xform(offset);
210207
}
211208

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

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

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

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

4343
private:
44-
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);
44+
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);
45+
String _cylinder_or_capsule_or_cone_frustum_get_handle_name(int p_id) const;
4546

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

6668
Vector<Vector3> cylinder_get_handles(real_t p_height, real_t p_radius);
67-
String cylinder_get_handle_name(int p_id) const;
69+
_FORCE_INLINE_ String cylinder_get_handle_name(int p_id) { return _cylinder_or_capsule_or_cone_frustum_get_handle_name(p_id); }
6870
_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) {
69-
_cylinder_or_capsule_set_handle(p_segment, p_id, r_height, r_radius, r_cylinder_position, false);
71+
real_t radius_bottom;
72+
_cylinder_or_capsule_or_cone_frustum_set_handle(p_segment, p_id, r_height, r_radius, radius_bottom, r_cylinder_position, false, false);
7073
}
7174
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");
7275

7376
// Capsule
7477

7578
_FORCE_INLINE_ Vector<Vector3> capsule_get_handles(real_t p_height, real_t p_radius) { return cylinder_get_handles(p_height, p_radius); }
76-
_FORCE_INLINE_ String capsule_get_handle_name(int p_id) { return cylinder_get_handle_name(p_id); }
79+
_FORCE_INLINE_ String capsule_get_handle_name(int p_id) { return _cylinder_or_capsule_or_cone_frustum_get_handle_name(p_id); }
7780
_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) {
78-
_cylinder_or_capsule_set_handle(p_segment, p_id, r_height, r_radius, r_capsule_position, true);
81+
real_t radius_bottom;
82+
_cylinder_or_capsule_or_cone_frustum_set_handle(p_segment, p_id, r_height, r_radius, radius_bottom, r_capsule_position, true, false);
7983
}
8084
_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") {
8185
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);
8286
}
87+
88+
// Cone frustum
89+
90+
Vector<Vector3> cone_frustum_get_handles(real_t p_height, real_t p_radius_top, real_t p_radius_bottom);
91+
_FORCE_INLINE_ String cone_frustum_get_handle_name(int p_id) { return _cylinder_or_capsule_or_cone_frustum_get_handle_name(p_id); }
92+
_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) {
93+
_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);
94+
}
95+
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");
8396
};

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)