1 | /**************************************************************************/ |
2 | /* occluder_instance_3d_gizmo_plugin.cpp */ |
3 | /**************************************************************************/ |
4 | /* This file is part of: */ |
5 | /* GODOT ENGINE */ |
6 | /* https://godotengine.org */ |
7 | /**************************************************************************/ |
8 | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ |
9 | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ |
10 | /* */ |
11 | /* Permission is hereby granted, free of charge, to any person obtaining */ |
12 | /* a copy of this software and associated documentation files (the */ |
13 | /* "Software"), to deal in the Software without restriction, including */ |
14 | /* without limitation the rights to use, copy, modify, merge, publish, */ |
15 | /* distribute, sublicense, and/or sell copies of the Software, and to */ |
16 | /* permit persons to whom the Software is furnished to do so, subject to */ |
17 | /* the following conditions: */ |
18 | /* */ |
19 | /* The above copyright notice and this permission notice shall be */ |
20 | /* included in all copies or substantial portions of the Software. */ |
21 | /* */ |
22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ |
23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ |
24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ |
25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ |
26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ |
27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ |
28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
29 | /**************************************************************************/ |
30 | |
31 | #include "occluder_instance_3d_gizmo_plugin.h" |
32 | |
33 | #include "editor/editor_settings.h" |
34 | #include "editor/editor_undo_redo_manager.h" |
35 | #include "editor/plugins/node_3d_editor_plugin.h" |
36 | #include "scene/3d/occluder_instance_3d.h" |
37 | |
38 | OccluderInstance3DGizmoPlugin::OccluderInstance3DGizmoPlugin() { |
39 | create_material("line_material" , EDITOR_DEF("editors/3d_gizmos/gizmo_colors/occluder" , Color(0.8, 0.5, 1))); |
40 | create_handle_material("handles" ); |
41 | } |
42 | |
43 | bool OccluderInstance3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { |
44 | return Object::cast_to<OccluderInstance3D>(p_spatial) != nullptr; |
45 | } |
46 | |
47 | String OccluderInstance3DGizmoPlugin::get_gizmo_name() const { |
48 | return "OccluderInstance3D" ; |
49 | } |
50 | |
51 | int OccluderInstance3DGizmoPlugin::get_priority() const { |
52 | return -1; |
53 | } |
54 | |
55 | String OccluderInstance3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const { |
56 | const OccluderInstance3D *cs = Object::cast_to<OccluderInstance3D>(p_gizmo->get_node_3d()); |
57 | |
58 | Ref<Occluder3D> o = cs->get_occluder(); |
59 | if (o.is_null()) { |
60 | return "" ; |
61 | } |
62 | |
63 | if (Object::cast_to<SphereOccluder3D>(*o)) { |
64 | return "Radius" ; |
65 | } |
66 | |
67 | if (Object::cast_to<BoxOccluder3D>(*o) || Object::cast_to<QuadOccluder3D>(*o)) { |
68 | return "Size" ; |
69 | } |
70 | |
71 | return "" ; |
72 | } |
73 | |
74 | Variant OccluderInstance3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const { |
75 | OccluderInstance3D *oi = Object::cast_to<OccluderInstance3D>(p_gizmo->get_node_3d()); |
76 | |
77 | Ref<Occluder3D> o = oi->get_occluder(); |
78 | if (o.is_null()) { |
79 | return Variant(); |
80 | } |
81 | |
82 | if (Object::cast_to<SphereOccluder3D>(*o)) { |
83 | Ref<SphereOccluder3D> so = o; |
84 | return so->get_radius(); |
85 | } |
86 | |
87 | if (Object::cast_to<BoxOccluder3D>(*o)) { |
88 | Ref<BoxOccluder3D> bo = o; |
89 | return bo->get_size(); |
90 | } |
91 | |
92 | if (Object::cast_to<QuadOccluder3D>(*o)) { |
93 | Ref<QuadOccluder3D> qo = o; |
94 | return qo->get_size(); |
95 | } |
96 | |
97 | return Variant(); |
98 | } |
99 | |
100 | void OccluderInstance3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) { |
101 | OccluderInstance3D *oi = Object::cast_to<OccluderInstance3D>(p_gizmo->get_node_3d()); |
102 | |
103 | Ref<Occluder3D> o = oi->get_occluder(); |
104 | if (o.is_null()) { |
105 | return; |
106 | } |
107 | |
108 | Transform3D gt = oi->get_global_transform(); |
109 | Transform3D gi = gt.affine_inverse(); |
110 | |
111 | Vector3 ray_from = p_camera->project_ray_origin(p_point); |
112 | Vector3 ray_dir = p_camera->project_ray_normal(p_point); |
113 | |
114 | Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 4096) }; |
115 | |
116 | bool snap_enabled = Node3DEditor::get_singleton()->is_snap_enabled(); |
117 | float snap = Node3DEditor::get_singleton()->get_translate_snap(); |
118 | |
119 | if (Object::cast_to<SphereOccluder3D>(*o)) { |
120 | Ref<SphereOccluder3D> so = o; |
121 | Vector3 ra, rb; |
122 | Geometry3D::get_closest_points_between_segments(Vector3(), Vector3(4096, 0, 0), sg[0], sg[1], ra, rb); |
123 | float d = ra.x; |
124 | if (snap_enabled) { |
125 | d = Math::snapped(d, snap); |
126 | } |
127 | |
128 | if (d < 0.001) { |
129 | d = 0.001; |
130 | } |
131 | |
132 | so->set_radius(d); |
133 | } |
134 | |
135 | if (Object::cast_to<BoxOccluder3D>(*o)) { |
136 | Vector3 axis; |
137 | axis[p_id] = 1.0; |
138 | Ref<BoxOccluder3D> bo = o; |
139 | Vector3 ra, rb; |
140 | Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); |
141 | float d = ra[p_id] * 2; |
142 | if (snap_enabled) { |
143 | d = Math::snapped(d, snap); |
144 | } |
145 | |
146 | if (d < 0.001) { |
147 | d = 0.001; |
148 | } |
149 | |
150 | Vector3 he = bo->get_size(); |
151 | he[p_id] = d; |
152 | bo->set_size(he); |
153 | } |
154 | |
155 | if (Object::cast_to<QuadOccluder3D>(*o)) { |
156 | Ref<QuadOccluder3D> qo = o; |
157 | Plane p = Plane(Vector3(0.0f, 0.0f, 1.0f), 0.0f); |
158 | Vector3 intersection; |
159 | if (!p.intersects_segment(sg[0], sg[1], &intersection)) { |
160 | return; |
161 | } |
162 | |
163 | if (p_id == 2) { |
164 | Vector2 s = Vector2(intersection.x, intersection.y) * 2.0f; |
165 | if (snap_enabled) { |
166 | s = s.snapped(Vector2(snap, snap)); |
167 | } |
168 | s = s.max(Vector2(0.001, 0.001)); |
169 | qo->set_size(s); |
170 | } else { |
171 | float d = intersection[p_id]; |
172 | if (snap_enabled) { |
173 | d = Math::snapped(d, snap); |
174 | } |
175 | |
176 | if (d < 0.001) { |
177 | d = 0.001; |
178 | } |
179 | |
180 | Vector2 he = qo->get_size(); |
181 | he[p_id] = d * 2.0f; |
182 | qo->set_size(he); |
183 | } |
184 | } |
185 | } |
186 | |
187 | void OccluderInstance3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel) { |
188 | OccluderInstance3D *oi = Object::cast_to<OccluderInstance3D>(p_gizmo->get_node_3d()); |
189 | |
190 | Ref<Occluder3D> o = oi->get_occluder(); |
191 | if (o.is_null()) { |
192 | return; |
193 | } |
194 | |
195 | if (Object::cast_to<SphereOccluder3D>(*o)) { |
196 | Ref<SphereOccluder3D> so = o; |
197 | if (p_cancel) { |
198 | so->set_radius(p_restore); |
199 | return; |
200 | } |
201 | |
202 | EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); |
203 | ur->create_action(TTR("Change Sphere Shape Radius" )); |
204 | ur->add_do_method(so.ptr(), "set_radius" , so->get_radius()); |
205 | ur->add_undo_method(so.ptr(), "set_radius" , p_restore); |
206 | ur->commit_action(); |
207 | } |
208 | |
209 | if (Object::cast_to<BoxOccluder3D>(*o)) { |
210 | Ref<BoxOccluder3D> bo = o; |
211 | if (p_cancel) { |
212 | bo->set_size(p_restore); |
213 | return; |
214 | } |
215 | |
216 | EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); |
217 | ur->create_action(TTR("Change Box Shape Size" )); |
218 | ur->add_do_method(bo.ptr(), "set_size" , bo->get_size()); |
219 | ur->add_undo_method(bo.ptr(), "set_size" , p_restore); |
220 | ur->commit_action(); |
221 | } |
222 | |
223 | if (Object::cast_to<QuadOccluder3D>(*o)) { |
224 | Ref<QuadOccluder3D> qo = o; |
225 | if (p_cancel) { |
226 | qo->set_size(p_restore); |
227 | return; |
228 | } |
229 | |
230 | EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); |
231 | ur->create_action(TTR("Change Box Shape Size" )); |
232 | ur->add_do_method(qo.ptr(), "set_size" , qo->get_size()); |
233 | ur->add_undo_method(qo.ptr(), "set_size" , p_restore); |
234 | ur->commit_action(); |
235 | } |
236 | } |
237 | |
238 | void OccluderInstance3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { |
239 | OccluderInstance3D *occluder_instance = Object::cast_to<OccluderInstance3D>(p_gizmo->get_node_3d()); |
240 | |
241 | p_gizmo->clear(); |
242 | |
243 | Ref<Occluder3D> o = occluder_instance->get_occluder(); |
244 | |
245 | if (!o.is_valid()) { |
246 | return; |
247 | } |
248 | |
249 | Vector<Vector3> lines = o->get_debug_lines(); |
250 | if (!lines.is_empty()) { |
251 | Ref<Material> material = get_material("line_material" , p_gizmo); |
252 | p_gizmo->add_lines(lines, material); |
253 | p_gizmo->add_collision_segments(lines); |
254 | } |
255 | |
256 | Ref<Material> handles_material = get_material("handles" ); |
257 | if (Object::cast_to<SphereOccluder3D>(*o)) { |
258 | Ref<SphereOccluder3D> so = o; |
259 | float r = so->get_radius(); |
260 | Vector<Vector3> handles = { Vector3(r, 0, 0) }; |
261 | p_gizmo->add_handles(handles, handles_material); |
262 | } |
263 | |
264 | if (Object::cast_to<BoxOccluder3D>(*o)) { |
265 | Ref<BoxOccluder3D> bo = o; |
266 | |
267 | Vector<Vector3> handles; |
268 | for (int i = 0; i < 3; i++) { |
269 | Vector3 ax; |
270 | ax[i] = bo->get_size()[i] / 2; |
271 | handles.push_back(ax); |
272 | } |
273 | |
274 | p_gizmo->add_handles(handles, handles_material); |
275 | } |
276 | |
277 | if (Object::cast_to<QuadOccluder3D>(*o)) { |
278 | Ref<QuadOccluder3D> qo = o; |
279 | Vector2 size = qo->get_size(); |
280 | Vector3 s = Vector3(size.x, size.y, 0.0f) / 2.0f; |
281 | Vector<Vector3> handles = { Vector3(s.x, 0.0f, 0.0f), Vector3(0.0f, s.y, 0.0f), Vector3(s.x, s.y, 0.0f) }; |
282 | p_gizmo->add_handles(handles, handles_material); |
283 | } |
284 | } |
285 | |