| 1 | /**************************************************************************/ |
| 2 | /* node_3d_editor_plugin.h */ |
| 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 | #ifndef NODE_3D_EDITOR_PLUGIN_H |
| 32 | #define NODE_3D_EDITOR_PLUGIN_H |
| 33 | |
| 34 | #include "editor/editor_plugin.h" |
| 35 | #include "editor/editor_scale.h" |
| 36 | #include "editor/plugins/node_3d_editor_gizmos.h" |
| 37 | #include "scene/gui/box_container.h" |
| 38 | #include "scene/gui/button.h" |
| 39 | #include "scene/gui/spin_box.h" |
| 40 | |
| 41 | class AcceptDialog; |
| 42 | class CheckBox; |
| 43 | class ColorPickerButton; |
| 44 | class ConfirmationDialog; |
| 45 | class DirectionalLight3D; |
| 46 | class EditorData; |
| 47 | class EditorSelection; |
| 48 | class EditorSpinSlider; |
| 49 | class HSplitContainer; |
| 50 | class LineEdit; |
| 51 | class ; |
| 52 | class Node3DEditor; |
| 53 | class Node3DEditorViewport; |
| 54 | class OptionButton; |
| 55 | class PanelContainer; |
| 56 | class ProceduralSkyMaterial; |
| 57 | class SubViewport; |
| 58 | class SubViewportContainer; |
| 59 | class VSeparator; |
| 60 | class VSplitContainer; |
| 61 | class ViewportNavigationControl; |
| 62 | class WorldEnvironment; |
| 63 | |
| 64 | class ViewportRotationControl : public Control { |
| 65 | GDCLASS(ViewportRotationControl, Control); |
| 66 | |
| 67 | struct Axis2D { |
| 68 | Vector2i screen_point; |
| 69 | float z_axis = -99.0; |
| 70 | int axis = -1; |
| 71 | }; |
| 72 | |
| 73 | struct Axis2DCompare { |
| 74 | _FORCE_INLINE_ bool operator()(const Axis2D &l, const Axis2D &r) const { |
| 75 | return l.z_axis < r.z_axis; |
| 76 | } |
| 77 | }; |
| 78 | |
| 79 | Node3DEditorViewport *viewport = nullptr; |
| 80 | Vector<Color> axis_colors; |
| 81 | Vector<int> ; |
| 82 | Vector2i orbiting_mouse_start; |
| 83 | int orbiting_index = -1; |
| 84 | int focused_axis = -2; |
| 85 | |
| 86 | const float AXIS_CIRCLE_RADIUS = 8.0f * EDSCALE; |
| 87 | |
| 88 | protected: |
| 89 | void _notification(int p_what); |
| 90 | virtual void gui_input(const Ref<InputEvent> &p_event) override; |
| 91 | void _draw(); |
| 92 | void _draw_axis(const Axis2D &p_axis); |
| 93 | void _get_sorted_axis(Vector<Axis2D> &r_axis); |
| 94 | void _update_focus(); |
| 95 | void _on_mouse_exited(); |
| 96 | void _process_click(int p_index, Vector2 p_position, bool p_pressed); |
| 97 | void _process_drag(Ref<InputEventWithModifiers> p_event, int p_index, Vector2 p_position, Vector2 p_relative_position); |
| 98 | |
| 99 | public: |
| 100 | void set_viewport(Node3DEditorViewport *p_viewport); |
| 101 | }; |
| 102 | |
| 103 | class Node3DEditorViewport : public Control { |
| 104 | GDCLASS(Node3DEditorViewport, Control); |
| 105 | friend class Node3DEditor; |
| 106 | friend class ViewportNavigationControl; |
| 107 | friend class ViewportRotationControl; |
| 108 | enum { |
| 109 | VIEW_TOP, |
| 110 | VIEW_BOTTOM, |
| 111 | VIEW_LEFT, |
| 112 | VIEW_RIGHT, |
| 113 | VIEW_FRONT, |
| 114 | VIEW_REAR, |
| 115 | VIEW_CENTER_TO_ORIGIN, |
| 116 | VIEW_CENTER_TO_SELECTION, |
| 117 | VIEW_ALIGN_TRANSFORM_WITH_VIEW, |
| 118 | VIEW_ALIGN_ROTATION_WITH_VIEW, |
| 119 | VIEW_PERSPECTIVE, |
| 120 | VIEW_ENVIRONMENT, |
| 121 | VIEW_ORTHOGONAL, |
| 122 | VIEW_SWITCH_PERSPECTIVE_ORTHOGONAL, |
| 123 | VIEW_HALF_RESOLUTION, |
| 124 | VIEW_AUDIO_LISTENER, |
| 125 | VIEW_AUDIO_DOPPLER, |
| 126 | VIEW_GIZMOS, |
| 127 | VIEW_INFORMATION, |
| 128 | VIEW_FRAME_TIME, |
| 129 | |
| 130 | // < Keep in sync with menu. |
| 131 | VIEW_DISPLAY_NORMAL, |
| 132 | VIEW_DISPLAY_WIREFRAME, |
| 133 | VIEW_DISPLAY_OVERDRAW, |
| 134 | VIEW_DISPLAY_LIGHTING, |
| 135 | VIEW_DISPLAY_UNSHADED, |
| 136 | VIEW_DISPLAY_ADVANCED, |
| 137 | // Advanced menu: |
| 138 | VIEW_DISPLAY_DEBUG_PSSM_SPLITS, |
| 139 | VIEW_DISPLAY_NORMAL_BUFFER, |
| 140 | VIEW_DISPLAY_DEBUG_SHADOW_ATLAS, |
| 141 | VIEW_DISPLAY_DEBUG_DIRECTIONAL_SHADOW_ATLAS, |
| 142 | VIEW_DISPLAY_DEBUG_DECAL_ATLAS, |
| 143 | VIEW_DISPLAY_DEBUG_VOXEL_GI_ALBEDO, |
| 144 | VIEW_DISPLAY_DEBUG_VOXEL_GI_LIGHTING, |
| 145 | VIEW_DISPLAY_DEBUG_VOXEL_GI_EMISSION, |
| 146 | VIEW_DISPLAY_DEBUG_SDFGI, |
| 147 | VIEW_DISPLAY_DEBUG_SDFGI_PROBES, |
| 148 | VIEW_DISPLAY_DEBUG_SCENE_LUMINANCE, |
| 149 | VIEW_DISPLAY_DEBUG_SSAO, |
| 150 | VIEW_DISPLAY_DEBUG_SSIL, |
| 151 | VIEW_DISPLAY_DEBUG_GI_BUFFER, |
| 152 | VIEW_DISPLAY_DEBUG_DISABLE_LOD, |
| 153 | VIEW_DISPLAY_DEBUG_CLUSTER_OMNI_LIGHTS, |
| 154 | VIEW_DISPLAY_DEBUG_CLUSTER_SPOT_LIGHTS, |
| 155 | VIEW_DISPLAY_DEBUG_CLUSTER_DECALS, |
| 156 | VIEW_DISPLAY_DEBUG_CLUSTER_REFLECTION_PROBES, |
| 157 | VIEW_DISPLAY_DEBUG_OCCLUDERS, |
| 158 | VIEW_DISPLAY_MOTION_VECTORS, |
| 159 | VIEW_DISPLAY_MAX, |
| 160 | // > Keep in sync with menu. |
| 161 | |
| 162 | VIEW_LOCK_ROTATION, |
| 163 | VIEW_CINEMATIC_PREVIEW, |
| 164 | VIEW_AUTO_ORTHOGONAL, |
| 165 | VIEW_MAX |
| 166 | }; |
| 167 | |
| 168 | enum ViewType { |
| 169 | VIEW_TYPE_USER, |
| 170 | VIEW_TYPE_TOP, |
| 171 | VIEW_TYPE_BOTTOM, |
| 172 | VIEW_TYPE_LEFT, |
| 173 | VIEW_TYPE_RIGHT, |
| 174 | VIEW_TYPE_FRONT, |
| 175 | VIEW_TYPE_REAR, |
| 176 | }; |
| 177 | |
| 178 | public: |
| 179 | enum { |
| 180 | GIZMO_BASE_LAYER = 27, |
| 181 | GIZMO_EDIT_LAYER = 26, |
| 182 | GIZMO_GRID_LAYER = 25, |
| 183 | MISC_TOOL_LAYER = 24, |
| 184 | |
| 185 | FRAME_TIME_HISTORY = 20, |
| 186 | }; |
| 187 | |
| 188 | enum NavigationScheme { |
| 189 | NAVIGATION_GODOT, |
| 190 | NAVIGATION_MAYA, |
| 191 | NAVIGATION_MODO, |
| 192 | }; |
| 193 | |
| 194 | enum FreelookNavigationScheme { |
| 195 | FREELOOK_DEFAULT, |
| 196 | FREELOOK_PARTIALLY_AXIS_LOCKED, |
| 197 | FREELOOK_FULLY_AXIS_LOCKED, |
| 198 | }; |
| 199 | |
| 200 | private: |
| 201 | double cpu_time_history[FRAME_TIME_HISTORY]; |
| 202 | int cpu_time_history_index; |
| 203 | double gpu_time_history[FRAME_TIME_HISTORY]; |
| 204 | int gpu_time_history_index; |
| 205 | |
| 206 | int index; |
| 207 | ViewType view_type; |
| 208 | void (int p_option); |
| 209 | void _set_auto_orthogonal(); |
| 210 | Node3D *preview_node = nullptr; |
| 211 | bool update_preview_node = false; |
| 212 | Point2 preview_node_viewport_pos; |
| 213 | Vector3 preview_node_pos; |
| 214 | AABB *preview_bounds = nullptr; |
| 215 | Vector<String> selected_files; |
| 216 | AcceptDialog *accept = nullptr; |
| 217 | |
| 218 | Node *target_node = nullptr; |
| 219 | Point2 drop_pos; |
| 220 | |
| 221 | EditorSelection *editor_selection = nullptr; |
| 222 | |
| 223 | CheckBox *preview_camera = nullptr; |
| 224 | SubViewportContainer *subviewport_container = nullptr; |
| 225 | |
| 226 | MenuButton * = nullptr; |
| 227 | PopupMenu * = nullptr; |
| 228 | |
| 229 | Control *surface = nullptr; |
| 230 | SubViewport *viewport = nullptr; |
| 231 | Camera3D *camera = nullptr; |
| 232 | bool transforming = false; |
| 233 | bool orthogonal; |
| 234 | bool auto_orthogonal; |
| 235 | bool lock_rotation; |
| 236 | real_t gizmo_scale; |
| 237 | |
| 238 | bool freelook_active; |
| 239 | real_t freelook_speed; |
| 240 | Vector2 previous_mouse_position; |
| 241 | |
| 242 | Label *info_label = nullptr; |
| 243 | Label *cinema_label = nullptr; |
| 244 | Label *locked_label = nullptr; |
| 245 | Label *zoom_limit_label = nullptr; |
| 246 | |
| 247 | Label *preview_material_label = nullptr; |
| 248 | Label *preview_material_label_desc = nullptr; |
| 249 | |
| 250 | VBoxContainer *top_right_vbox = nullptr; |
| 251 | VBoxContainer *bottom_center_vbox = nullptr; |
| 252 | ViewportNavigationControl *position_control = nullptr; |
| 253 | ViewportNavigationControl *look_control = nullptr; |
| 254 | ViewportRotationControl *rotation_control = nullptr; |
| 255 | Gradient *frame_time_gradient = nullptr; |
| 256 | Label *cpu_time_label = nullptr; |
| 257 | Label *gpu_time_label = nullptr; |
| 258 | Label *fps_label = nullptr; |
| 259 | |
| 260 | struct _RayResult { |
| 261 | Node3D *item = nullptr; |
| 262 | real_t depth = 0; |
| 263 | _FORCE_INLINE_ bool operator<(const _RayResult &p_rr) const { return depth < p_rr.depth; } |
| 264 | }; |
| 265 | |
| 266 | void _update_name(); |
| 267 | void _compute_edit(const Point2 &p_point); |
| 268 | void _clear_selected(); |
| 269 | void _select_clicked(bool p_allow_locked); |
| 270 | ObjectID _select_ray(const Point2 &p_pos) const; |
| 271 | void _find_items_at_pos(const Point2 &p_pos, Vector<_RayResult> &r_results, bool p_include_locked); |
| 272 | Vector3 _get_ray_pos(const Vector2 &p_pos) const; |
| 273 | Vector3 _get_ray(const Vector2 &p_pos) const; |
| 274 | Point2 _point_to_screen(const Vector3 &p_point); |
| 275 | Transform3D _get_camera_transform() const; |
| 276 | int get_selected_count() const; |
| 277 | void cancel_transform(); |
| 278 | void _update_shrink(); |
| 279 | |
| 280 | Vector3 _get_camera_position() const; |
| 281 | Vector3 _get_camera_normal() const; |
| 282 | Vector3 _get_screen_to_space(const Vector3 &p_vector3); |
| 283 | |
| 284 | void _select_region(); |
| 285 | bool _transform_gizmo_select(const Vector2 &p_screenpos, bool p_highlight_only = false); |
| 286 | void _transform_gizmo_apply(Node3D *p_node, const Transform3D &p_transform, bool p_local); |
| 287 | |
| 288 | void _nav_pan(Ref<InputEventWithModifiers> p_event, const Vector2 &p_relative); |
| 289 | void _nav_zoom(Ref<InputEventWithModifiers> p_event, const Vector2 &p_relative); |
| 290 | void _nav_orbit(Ref<InputEventWithModifiers> p_event, const Vector2 &p_relative); |
| 291 | void _nav_look(Ref<InputEventWithModifiers> p_event, const Vector2 &p_relative); |
| 292 | |
| 293 | float get_znear() const; |
| 294 | float get_zfar() const; |
| 295 | float get_fov() const; |
| 296 | |
| 297 | ObjectID clicked; |
| 298 | ObjectID material_target; |
| 299 | Vector<_RayResult> selection_results; |
| 300 | Vector<_RayResult> ; |
| 301 | bool clicked_wants_append = false; |
| 302 | bool selection_in_progress = false; |
| 303 | |
| 304 | PopupMenu * = nullptr; |
| 305 | |
| 306 | enum NavigationZoomStyle { |
| 307 | NAVIGATION_ZOOM_VERTICAL, |
| 308 | NAVIGATION_ZOOM_HORIZONTAL |
| 309 | }; |
| 310 | |
| 311 | enum NavigationMode { |
| 312 | NAVIGATION_NONE, |
| 313 | NAVIGATION_PAN, |
| 314 | NAVIGATION_ZOOM, |
| 315 | NAVIGATION_ORBIT, |
| 316 | NAVIGATION_LOOK, |
| 317 | NAVIGATION_MOVE |
| 318 | }; |
| 319 | enum TransformMode { |
| 320 | TRANSFORM_NONE, |
| 321 | TRANSFORM_ROTATE, |
| 322 | TRANSFORM_TRANSLATE, |
| 323 | TRANSFORM_SCALE |
| 324 | |
| 325 | }; |
| 326 | enum TransformPlane { |
| 327 | TRANSFORM_VIEW, |
| 328 | TRANSFORM_X_AXIS, |
| 329 | TRANSFORM_Y_AXIS, |
| 330 | TRANSFORM_Z_AXIS, |
| 331 | TRANSFORM_YZ, |
| 332 | TRANSFORM_XZ, |
| 333 | TRANSFORM_XY, |
| 334 | }; |
| 335 | |
| 336 | struct EditData { |
| 337 | TransformMode mode; |
| 338 | TransformPlane plane; |
| 339 | Transform3D original; |
| 340 | Vector3 click_ray; |
| 341 | Vector3 click_ray_pos; |
| 342 | Vector3 center; |
| 343 | Point2 mouse_pos; |
| 344 | Point2 original_mouse_pos; |
| 345 | bool snap = false; |
| 346 | bool show_rotation_line = false; |
| 347 | Ref<EditorNode3DGizmo> gizmo; |
| 348 | int gizmo_handle = 0; |
| 349 | bool gizmo_handle_secondary = false; |
| 350 | Variant gizmo_initial_value; |
| 351 | bool original_local; |
| 352 | bool instant; |
| 353 | |
| 354 | // Numeric blender-style transforms (e.g. 'g5x'). |
| 355 | // numeric_input tracks the current input value, e.g. 1.23. |
| 356 | // numeric_negate indicates whether '-' has been pressed to negate the value |
| 357 | // while numeric_next_decimal is 0, numbers are input before the decimal point |
| 358 | // after pressing '.', numeric next decimal changes to -1, and decrements after each press. |
| 359 | double numeric_input = 0.0; |
| 360 | bool numeric_negate = false; |
| 361 | int numeric_next_decimal = 0; |
| 362 | } _edit; |
| 363 | |
| 364 | struct Cursor { |
| 365 | Vector3 pos; |
| 366 | real_t x_rot, y_rot, distance, fov_scale; |
| 367 | Vector3 eye_pos; // Used in freelook mode |
| 368 | bool region_select; |
| 369 | Point2 region_begin, region_end; |
| 370 | |
| 371 | Cursor() { |
| 372 | // These rotations place the camera in +X +Y +Z, aka south east, facing north west. |
| 373 | x_rot = 0.5; |
| 374 | y_rot = -0.5; |
| 375 | distance = 4; |
| 376 | fov_scale = 1.0; |
| 377 | region_select = false; |
| 378 | } |
| 379 | }; |
| 380 | // Viewport camera supports movement smoothing, |
| 381 | // so one cursor is the real cursor, while the other can be an interpolated version. |
| 382 | Cursor cursor; // Immediate cursor |
| 383 | Cursor camera_cursor; // That one may be interpolated (don't modify this one except for smoothing purposes) |
| 384 | |
| 385 | void scale_fov(real_t p_fov_offset); |
| 386 | void reset_fov(); |
| 387 | void scale_cursor_distance(real_t scale); |
| 388 | |
| 389 | void set_freelook_active(bool active_now); |
| 390 | void scale_freelook_speed(real_t scale); |
| 391 | |
| 392 | real_t zoom_indicator_delay; |
| 393 | int zoom_failed_attempts_count = 0; |
| 394 | |
| 395 | RID move_gizmo_instance[3], move_plane_gizmo_instance[3], rotate_gizmo_instance[4], scale_gizmo_instance[3], scale_plane_gizmo_instance[3], axis_gizmo_instance[3]; |
| 396 | |
| 397 | String last_message; |
| 398 | String message; |
| 399 | double message_time; |
| 400 | |
| 401 | void set_message(String p_message, float p_time = 5); |
| 402 | |
| 403 | void _view_settings_confirmed(real_t p_interp_delta); |
| 404 | void _update_camera(real_t p_interp_delta); |
| 405 | void _update_navigation_controls_visibility(); |
| 406 | Transform3D to_camera_transform(const Cursor &p_cursor) const; |
| 407 | void _draw(); |
| 408 | |
| 409 | void _surface_mouse_enter(); |
| 410 | void _surface_mouse_exit(); |
| 411 | void _surface_focus_enter(); |
| 412 | void _surface_focus_exit(); |
| 413 | |
| 414 | void input(const Ref<InputEvent> &p_event) override; |
| 415 | void _sinput(const Ref<InputEvent> &p_event); |
| 416 | void _update_freelook(real_t delta); |
| 417 | Node3DEditor *spatial_editor = nullptr; |
| 418 | |
| 419 | Camera3D *previewing = nullptr; |
| 420 | Camera3D *preview = nullptr; |
| 421 | |
| 422 | bool previewing_camera = false; |
| 423 | bool previewing_cinema = false; |
| 424 | bool _is_node_locked(const Node *p_node); |
| 425 | void _preview_exited_scene(); |
| 426 | void _toggle_camera_preview(bool); |
| 427 | void _toggle_cinema_preview(bool); |
| 428 | void _init_gizmo_instance(int p_idx); |
| 429 | void _finish_gizmo_instances(); |
| 430 | void _selection_result_pressed(int); |
| 431 | void (); |
| 432 | void _list_select(Ref<InputEventMouseButton> b); |
| 433 | Point2i _get_warped_mouse_motion(const Ref<InputEventMouseMotion> &p_ev_mouse_motion) const; |
| 434 | |
| 435 | Vector3 _get_instance_position(const Point2 &p_pos) const; |
| 436 | static AABB _calculate_spatial_bounds(const Node3D *p_parent, bool p_exclude_top_level_transform = true); |
| 437 | |
| 438 | Node *_sanitize_preview_node(Node *p_node) const; |
| 439 | |
| 440 | void _create_preview_node(const Vector<String> &files) const; |
| 441 | void _remove_preview_node(); |
| 442 | bool _apply_preview_material(ObjectID p_target, const Point2 &p_point) const; |
| 443 | void _reset_preview_material() const; |
| 444 | void _remove_preview_material(); |
| 445 | bool _cyclical_dependency_exists(const String &p_target_scene_path, Node *p_desired_node); |
| 446 | bool _create_instance(Node *parent, String &path, const Point2 &p_point); |
| 447 | void _perform_drop_data(); |
| 448 | |
| 449 | bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from); |
| 450 | void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from); |
| 451 | |
| 452 | void _project_settings_changed(); |
| 453 | |
| 454 | Transform3D _compute_transform(TransformMode p_mode, const Transform3D &p_original, const Transform3D &p_original_local, Vector3 p_motion, double , bool p_local, bool p_orthogonal); |
| 455 | |
| 456 | void begin_transform(TransformMode p_mode, bool instant); |
| 457 | void commit_transform(); |
| 458 | void apply_transform(Vector3 p_motion, double p_snap); |
| 459 | void update_transform(bool p_shift); |
| 460 | void update_transform_numeric(); |
| 461 | void finish_transform(); |
| 462 | |
| 463 | void register_shortcut_action(const String &p_path, const String &p_name, Key p_keycode, bool p_physical = false); |
| 464 | void shortcut_changed_callback(const Ref<Shortcut> p_shortcut, const String &p_shortcut_path); |
| 465 | |
| 466 | void _set_lock_view_rotation(bool p_lock_rotation); |
| 467 | |
| 468 | protected: |
| 469 | void _notification(int p_what); |
| 470 | static void _bind_methods(); |
| 471 | |
| 472 | public: |
| 473 | void update_surface() { surface->queue_redraw(); } |
| 474 | void update_transform_gizmo_view(); |
| 475 | |
| 476 | void set_can_preview(Camera3D *p_preview); |
| 477 | void set_state(const Dictionary &p_state); |
| 478 | Dictionary get_state() const; |
| 479 | void reset(); |
| 480 | bool is_freelook_active() const { return freelook_active; } |
| 481 | |
| 482 | void focus_selection(); |
| 483 | |
| 484 | void assign_pending_data_pointers( |
| 485 | Node3D *p_preview_node, |
| 486 | AABB *p_preview_bounds, |
| 487 | AcceptDialog *p_accept); |
| 488 | |
| 489 | SubViewport *get_viewport_node() { return viewport; } |
| 490 | Camera3D *get_camera_3d() { return camera; } // return the default camera object. |
| 491 | |
| 492 | Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p_index); |
| 493 | ~Node3DEditorViewport(); |
| 494 | }; |
| 495 | |
| 496 | class Node3DEditorSelectedItem : public Object { |
| 497 | GDCLASS(Node3DEditorSelectedItem, Object); |
| 498 | |
| 499 | public: |
| 500 | AABB aabb; |
| 501 | Transform3D original; // original location when moving |
| 502 | Transform3D original_local; |
| 503 | Transform3D last_xform; // last transform |
| 504 | bool last_xform_dirty; |
| 505 | Node3D *sp = nullptr; |
| 506 | RID sbox_instance; |
| 507 | RID sbox_instance_offset; |
| 508 | RID sbox_instance_xray; |
| 509 | RID sbox_instance_xray_offset; |
| 510 | Ref<EditorNode3DGizmo> gizmo; |
| 511 | HashMap<int, Transform3D> subgizmos; // map ID -> initial transform |
| 512 | |
| 513 | Node3DEditorSelectedItem() { |
| 514 | sp = nullptr; |
| 515 | last_xform_dirty = true; |
| 516 | } |
| 517 | ~Node3DEditorSelectedItem(); |
| 518 | }; |
| 519 | |
| 520 | class Node3DEditorViewportContainer : public Container { |
| 521 | GDCLASS(Node3DEditorViewportContainer, Container); |
| 522 | |
| 523 | public: |
| 524 | enum View { |
| 525 | VIEW_USE_1_VIEWPORT, |
| 526 | VIEW_USE_2_VIEWPORTS, |
| 527 | VIEW_USE_2_VIEWPORTS_ALT, |
| 528 | VIEW_USE_3_VIEWPORTS, |
| 529 | VIEW_USE_3_VIEWPORTS_ALT, |
| 530 | VIEW_USE_4_VIEWPORTS, |
| 531 | }; |
| 532 | |
| 533 | private: |
| 534 | View view; |
| 535 | bool mouseover; |
| 536 | real_t ratio_h; |
| 537 | real_t ratio_v; |
| 538 | |
| 539 | bool hovering_v; |
| 540 | bool hovering_h; |
| 541 | |
| 542 | bool dragging_v; |
| 543 | bool dragging_h; |
| 544 | Vector2 drag_begin_pos; |
| 545 | Vector2 drag_begin_ratio; |
| 546 | |
| 547 | virtual void gui_input(const Ref<InputEvent> &p_event) override; |
| 548 | |
| 549 | protected: |
| 550 | void _notification(int p_what); |
| 551 | |
| 552 | public: |
| 553 | void set_view(View p_view); |
| 554 | View get_view(); |
| 555 | |
| 556 | Node3DEditorViewportContainer(); |
| 557 | }; |
| 558 | |
| 559 | class Node3DEditor : public VBoxContainer { |
| 560 | GDCLASS(Node3DEditor, VBoxContainer); |
| 561 | |
| 562 | public: |
| 563 | static const unsigned int VIEWPORTS_COUNT = 4; |
| 564 | |
| 565 | enum ToolMode { |
| 566 | TOOL_MODE_SELECT, |
| 567 | TOOL_MODE_MOVE, |
| 568 | TOOL_MODE_ROTATE, |
| 569 | TOOL_MODE_SCALE, |
| 570 | TOOL_MODE_LIST_SELECT, |
| 571 | TOOL_LOCK_SELECTED, |
| 572 | TOOL_UNLOCK_SELECTED, |
| 573 | TOOL_GROUP_SELECTED, |
| 574 | TOOL_UNGROUP_SELECTED, |
| 575 | TOOL_MAX |
| 576 | }; |
| 577 | |
| 578 | enum ToolOptions { |
| 579 | TOOL_OPT_LOCAL_COORDS, |
| 580 | TOOL_OPT_USE_SNAP, |
| 581 | TOOL_OPT_OVERRIDE_CAMERA, |
| 582 | TOOL_OPT_MAX |
| 583 | |
| 584 | }; |
| 585 | |
| 586 | private: |
| 587 | EditorSelection *editor_selection = nullptr; |
| 588 | |
| 589 | Node3DEditorViewportContainer *viewport_base = nullptr; |
| 590 | Node3DEditorViewport *viewports[VIEWPORTS_COUNT]; |
| 591 | VSplitContainer *shader_split = nullptr; |
| 592 | HSplitContainer *left_panel_split = nullptr; |
| 593 | HSplitContainer *right_panel_split = nullptr; |
| 594 | |
| 595 | ///// |
| 596 | |
| 597 | ToolMode tool_mode; |
| 598 | |
| 599 | RID origin; |
| 600 | RID origin_instance; |
| 601 | bool origin_enabled = false; |
| 602 | RID grid[3]; |
| 603 | RID grid_instance[3]; |
| 604 | bool grid_visible[3] = { false, false, false }; //currently visible |
| 605 | bool grid_enable[3] = { false, false, false }; //should be always visible if true |
| 606 | bool grid_enabled = false; |
| 607 | bool grid_init_draw = false; |
| 608 | Camera3D::ProjectionType grid_camera_last_update_perspective = Camera3D::PROJECTION_PERSPECTIVE; |
| 609 | Vector3 grid_camera_last_update_position; |
| 610 | |
| 611 | Ref<ArrayMesh> move_gizmo[3], move_plane_gizmo[3], rotate_gizmo[4], scale_gizmo[3], scale_plane_gizmo[3], axis_gizmo[3]; |
| 612 | Ref<StandardMaterial3D> gizmo_color[3]; |
| 613 | Ref<StandardMaterial3D> plane_gizmo_color[3]; |
| 614 | Ref<ShaderMaterial> rotate_gizmo_color[3]; |
| 615 | Ref<StandardMaterial3D> gizmo_color_hl[3]; |
| 616 | Ref<StandardMaterial3D> plane_gizmo_color_hl[3]; |
| 617 | Ref<ShaderMaterial> rotate_gizmo_color_hl[3]; |
| 618 | |
| 619 | Ref<Node3DGizmo> current_hover_gizmo; |
| 620 | int current_hover_gizmo_handle; |
| 621 | bool current_hover_gizmo_handle_secondary; |
| 622 | |
| 623 | real_t snap_translate_value; |
| 624 | real_t snap_rotate_value; |
| 625 | real_t snap_scale_value; |
| 626 | |
| 627 | Ref<ArrayMesh> selection_box_xray; |
| 628 | Ref<ArrayMesh> selection_box; |
| 629 | RID indicators; |
| 630 | RID indicators_instance; |
| 631 | RID cursor_mesh; |
| 632 | RID cursor_instance; |
| 633 | Ref<StandardMaterial3D> indicator_mat; |
| 634 | Ref<ShaderMaterial> grid_mat[3]; |
| 635 | Ref<StandardMaterial3D> cursor_material; |
| 636 | |
| 637 | // Scene drag and drop support |
| 638 | Node3D *preview_node = nullptr; |
| 639 | AABB preview_bounds; |
| 640 | |
| 641 | Ref<Material> preview_material; |
| 642 | Ref<Material> preview_reset_material; |
| 643 | ObjectID preview_material_target; |
| 644 | int preview_material_surface = -1; |
| 645 | |
| 646 | struct Gizmo { |
| 647 | bool visible = false; |
| 648 | real_t scale = 0; |
| 649 | Transform3D transform; |
| 650 | } gizmo; |
| 651 | |
| 652 | enum { |
| 653 | , |
| 654 | , |
| 655 | , |
| 656 | , |
| 657 | , |
| 658 | , |
| 659 | , |
| 660 | , |
| 661 | , |
| 662 | , |
| 663 | , |
| 664 | , |
| 665 | , |
| 666 | , |
| 667 | , |
| 668 | , |
| 669 | , |
| 670 | , |
| 671 | , |
| 672 | , |
| 673 | , |
| 674 | , |
| 675 | , |
| 676 | , |
| 677 | |
| 678 | }; |
| 679 | |
| 680 | Button *tool_button[TOOL_MAX]; |
| 681 | Button *tool_option_button[TOOL_OPT_MAX]; |
| 682 | |
| 683 | MenuButton * = nullptr; |
| 684 | PopupMenu * = nullptr; |
| 685 | MenuButton * = nullptr; |
| 686 | |
| 687 | AcceptDialog *accept = nullptr; |
| 688 | |
| 689 | ConfirmationDialog *snap_dialog = nullptr; |
| 690 | ConfirmationDialog *xform_dialog = nullptr; |
| 691 | ConfirmationDialog *settings_dialog = nullptr; |
| 692 | |
| 693 | bool snap_enabled; |
| 694 | bool snap_key_enabled; |
| 695 | LineEdit *snap_translate = nullptr; |
| 696 | LineEdit *snap_rotate = nullptr; |
| 697 | LineEdit *snap_scale = nullptr; |
| 698 | |
| 699 | LineEdit *xform_translate[3]; |
| 700 | LineEdit *xform_rotate[3]; |
| 701 | LineEdit *xform_scale[3]; |
| 702 | OptionButton *xform_type = nullptr; |
| 703 | |
| 704 | VBoxContainer *settings_vbc = nullptr; |
| 705 | SpinBox *settings_fov = nullptr; |
| 706 | SpinBox *settings_znear = nullptr; |
| 707 | SpinBox *settings_zfar = nullptr; |
| 708 | |
| 709 | void _snap_changed(); |
| 710 | void _snap_update(); |
| 711 | void _xform_dialog_action(); |
| 712 | void (int p_option); |
| 713 | void (bool pressed, int p_option); |
| 714 | void (int p_option); |
| 715 | void _update_camera_override_button(bool p_game_running); |
| 716 | void _update_camera_override_viewport(Object *p_viewport); |
| 717 | // Used for secondary menu items which are displayed depending on the currently selected node |
| 718 | // (such as MeshInstance's "Mesh" menu). |
| 719 | PanelContainer *context_toolbar_panel = nullptr; |
| 720 | HBoxContainer *context_toolbar_hbox = nullptr; |
| 721 | HashMap<Control *, VSeparator *> context_toolbar_separators; |
| 722 | |
| 723 | void _update_context_toolbar(); |
| 724 | |
| 725 | void _generate_selection_boxes(); |
| 726 | |
| 727 | int camera_override_viewport_id; |
| 728 | |
| 729 | void _init_indicators(); |
| 730 | void (); |
| 731 | void (); |
| 732 | void _init_grid(); |
| 733 | void _finish_indicators(); |
| 734 | void _finish_grid(); |
| 735 | |
| 736 | void _toggle_maximize_view(Object *p_viewport); |
| 737 | |
| 738 | Node *custom_camera = nullptr; |
| 739 | |
| 740 | Object *_get_editor_data(Object *p_what); |
| 741 | |
| 742 | Ref<Environment> viewport_environment; |
| 743 | |
| 744 | Node3D *selected = nullptr; |
| 745 | |
| 746 | void _request_gizmo(Object *p_obj); |
| 747 | void _request_gizmo_for_id(ObjectID p_id); |
| 748 | void _set_subgizmo_selection(Object *p_obj, Ref<Node3DGizmo> p_gizmo, int p_id, Transform3D p_transform = Transform3D()); |
| 749 | void _clear_subgizmo_selection(Object *p_obj = nullptr); |
| 750 | |
| 751 | static Node3DEditor *singleton; |
| 752 | |
| 753 | void _node_added(Node *p_node); |
| 754 | void _node_removed(Node *p_node); |
| 755 | Vector<Ref<EditorNode3DGizmoPlugin>> gizmo_plugins_by_priority; |
| 756 | Vector<Ref<EditorNode3DGizmoPlugin>> gizmo_plugins_by_name; |
| 757 | |
| 758 | void _register_all_gizmos(); |
| 759 | |
| 760 | void _selection_changed(); |
| 761 | void (); |
| 762 | |
| 763 | bool do_snap_selected_nodes_to_floor = false; |
| 764 | void _snap_selected_nodes_to_floor(); |
| 765 | |
| 766 | // Preview Sun and Environment |
| 767 | |
| 768 | uint32_t world_env_count = 0; |
| 769 | uint32_t directional_light_count = 0; |
| 770 | |
| 771 | Button *sun_button = nullptr; |
| 772 | Label *sun_state = nullptr; |
| 773 | Label *sun_title = nullptr; |
| 774 | VBoxContainer *sun_vb = nullptr; |
| 775 | Popup * = nullptr; |
| 776 | Control *sun_direction = nullptr; |
| 777 | EditorSpinSlider *sun_angle_altitude = nullptr; |
| 778 | EditorSpinSlider *sun_angle_azimuth = nullptr; |
| 779 | ColorPickerButton *sun_color = nullptr; |
| 780 | EditorSpinSlider *sun_energy = nullptr; |
| 781 | EditorSpinSlider *sun_max_distance = nullptr; |
| 782 | Button *sun_add_to_scene = nullptr; |
| 783 | |
| 784 | void _sun_direction_draw(); |
| 785 | void _sun_direction_input(const Ref<InputEvent> &p_event); |
| 786 | void _sun_direction_angle_set(); |
| 787 | |
| 788 | Vector2 sun_rotation; |
| 789 | |
| 790 | Ref<Shader> sun_direction_shader; |
| 791 | Ref<ShaderMaterial> sun_direction_material; |
| 792 | |
| 793 | Button *environ_button = nullptr; |
| 794 | Label *environ_state = nullptr; |
| 795 | Label *environ_title = nullptr; |
| 796 | VBoxContainer *environ_vb = nullptr; |
| 797 | ColorPickerButton *environ_sky_color = nullptr; |
| 798 | ColorPickerButton *environ_ground_color = nullptr; |
| 799 | EditorSpinSlider *environ_energy = nullptr; |
| 800 | Button *environ_ao_button = nullptr; |
| 801 | Button *environ_glow_button = nullptr; |
| 802 | Button *environ_tonemap_button = nullptr; |
| 803 | Button *environ_gi_button = nullptr; |
| 804 | Button *environ_add_to_scene = nullptr; |
| 805 | |
| 806 | Button *sun_environ_settings = nullptr; |
| 807 | |
| 808 | DirectionalLight3D *preview_sun = nullptr; |
| 809 | bool preview_sun_dangling = false; |
| 810 | WorldEnvironment *preview_environment = nullptr; |
| 811 | bool preview_env_dangling = false; |
| 812 | Ref<Environment> environment; |
| 813 | Ref<CameraAttributesPractical> camera_attributes; |
| 814 | Ref<ProceduralSkyMaterial> sky_material; |
| 815 | |
| 816 | bool sun_environ_updating = false; |
| 817 | |
| 818 | void _load_default_preview_settings(); |
| 819 | void _update_preview_environment(); |
| 820 | |
| 821 | void _preview_settings_changed(); |
| 822 | void _sun_environ_settings_pressed(); |
| 823 | |
| 824 | void _add_sun_to_scene(bool p_already_added_environment = false); |
| 825 | void _add_environment_to_scene(bool p_already_added_sun = false); |
| 826 | |
| 827 | void _update_theme(); |
| 828 | |
| 829 | protected: |
| 830 | void _notification(int p_what); |
| 831 | //void _gui_input(InputEvent p_event); |
| 832 | virtual void shortcut_input(const Ref<InputEvent> &p_event) override; |
| 833 | |
| 834 | static void _bind_methods(); |
| 835 | |
| 836 | public: |
| 837 | static Node3DEditor *get_singleton() { return singleton; } |
| 838 | |
| 839 | Vector3 snap_point(Vector3 p_target, Vector3 p_start = Vector3(0, 0, 0)) const; |
| 840 | |
| 841 | float get_znear() const { return settings_znear->get_value(); } |
| 842 | float get_zfar() const { return settings_zfar->get_value(); } |
| 843 | float get_fov() const { return settings_fov->get_value(); } |
| 844 | |
| 845 | Transform3D get_gizmo_transform() const { return gizmo.transform; } |
| 846 | bool is_gizmo_visible() const; |
| 847 | |
| 848 | ToolMode get_tool_mode() const { return tool_mode; } |
| 849 | bool are_local_coords_enabled() const { return tool_option_button[Node3DEditor::TOOL_OPT_LOCAL_COORDS]->is_pressed(); } |
| 850 | void set_local_coords_enabled(bool on) const { tool_option_button[Node3DEditor::TOOL_OPT_LOCAL_COORDS]->set_pressed(on); } |
| 851 | bool is_snap_enabled() const { return snap_enabled ^ snap_key_enabled; } |
| 852 | double get_translate_snap() const; |
| 853 | double get_rotate_snap() const; |
| 854 | double get_scale_snap() const; |
| 855 | |
| 856 | Ref<ArrayMesh> get_move_gizmo(int idx) const { return move_gizmo[idx]; } |
| 857 | Ref<ArrayMesh> get_axis_gizmo(int idx) const { return axis_gizmo[idx]; } |
| 858 | Ref<ArrayMesh> get_move_plane_gizmo(int idx) const { return move_plane_gizmo[idx]; } |
| 859 | Ref<ArrayMesh> get_rotate_gizmo(int idx) const { return rotate_gizmo[idx]; } |
| 860 | Ref<ArrayMesh> get_scale_gizmo(int idx) const { return scale_gizmo[idx]; } |
| 861 | Ref<ArrayMesh> get_scale_plane_gizmo(int idx) const { return scale_plane_gizmo[idx]; } |
| 862 | |
| 863 | void update_grid(); |
| 864 | void update_transform_gizmo(); |
| 865 | void update_all_gizmos(Node *p_node = nullptr); |
| 866 | void snap_selected_nodes_to_floor(); |
| 867 | void select_gizmo_highlight_axis(int p_axis); |
| 868 | void set_custom_camera(Node *p_camera) { custom_camera = p_camera; } |
| 869 | |
| 870 | Dictionary get_state() const; |
| 871 | void set_state(const Dictionary &p_state); |
| 872 | |
| 873 | Ref<Environment> get_viewport_environment() { return viewport_environment; } |
| 874 | |
| 875 | void (Control *p_control); |
| 876 | void (Control *p_control); |
| 877 | |
| 878 | void add_control_to_left_panel(Control *p_control); |
| 879 | void remove_control_from_left_panel(Control *p_control); |
| 880 | |
| 881 | void add_control_to_right_panel(Control *p_control); |
| 882 | void remove_control_from_right_panel(Control *p_control); |
| 883 | |
| 884 | void move_control_to_left_panel(Control *p_control); |
| 885 | void move_control_to_right_panel(Control *p_control); |
| 886 | |
| 887 | VSplitContainer *get_shader_split(); |
| 888 | |
| 889 | Node3D *get_single_selected_node() { return selected; } |
| 890 | bool is_current_selected_gizmo(const EditorNode3DGizmo *p_gizmo); |
| 891 | bool is_subgizmo_selected(int p_id); |
| 892 | Vector<int> get_subgizmo_selection(); |
| 893 | |
| 894 | Ref<EditorNode3DGizmo> get_current_hover_gizmo() const { return current_hover_gizmo; } |
| 895 | void set_current_hover_gizmo(Ref<EditorNode3DGizmo> p_gizmo) { current_hover_gizmo = p_gizmo; } |
| 896 | |
| 897 | void set_current_hover_gizmo_handle(int p_id, bool p_secondary) { |
| 898 | current_hover_gizmo_handle = p_id; |
| 899 | current_hover_gizmo_handle_secondary = p_secondary; |
| 900 | } |
| 901 | |
| 902 | int get_current_hover_gizmo_handle(bool &r_secondary) const { |
| 903 | r_secondary = current_hover_gizmo_handle_secondary; |
| 904 | return current_hover_gizmo_handle; |
| 905 | } |
| 906 | |
| 907 | void set_can_preview(Camera3D *p_preview); |
| 908 | |
| 909 | void set_preview_material(Ref<Material> p_material) { preview_material = p_material; } |
| 910 | Ref<Material> get_preview_material() { return preview_material; } |
| 911 | void set_preview_reset_material(Ref<Material> p_material) { preview_reset_material = p_material; } |
| 912 | Ref<Material> get_preview_reset_material() const { return preview_reset_material; } |
| 913 | void set_preview_material_target(ObjectID p_object_id) { preview_material_target = p_object_id; } |
| 914 | ObjectID get_preview_material_target() const { return preview_material_target; } |
| 915 | void set_preview_material_surface(int p_surface) { preview_material_surface = p_surface; } |
| 916 | int get_preview_material_surface() const { return preview_material_surface; } |
| 917 | |
| 918 | Node3DEditorViewport *get_editor_viewport(int p_idx) { |
| 919 | ERR_FAIL_INDEX_V(p_idx, static_cast<int>(VIEWPORTS_COUNT), nullptr); |
| 920 | return viewports[p_idx]; |
| 921 | } |
| 922 | |
| 923 | void add_gizmo_plugin(Ref<EditorNode3DGizmoPlugin> p_plugin); |
| 924 | void remove_gizmo_plugin(Ref<EditorNode3DGizmoPlugin> p_plugin); |
| 925 | |
| 926 | void edit(Node3D *p_spatial); |
| 927 | void clear(); |
| 928 | |
| 929 | Node3DEditor(); |
| 930 | ~Node3DEditor(); |
| 931 | }; |
| 932 | |
| 933 | class Node3DEditorPlugin : public EditorPlugin { |
| 934 | GDCLASS(Node3DEditorPlugin, EditorPlugin); |
| 935 | |
| 936 | Node3DEditor *spatial_editor = nullptr; |
| 937 | |
| 938 | public: |
| 939 | Node3DEditor *get_spatial_editor() { return spatial_editor; } |
| 940 | virtual String get_name() const override { return "3D" ; } |
| 941 | bool has_main_screen() const override { return true; } |
| 942 | virtual void make_visible(bool p_visible) override; |
| 943 | virtual void edit(Object *p_object) override; |
| 944 | virtual bool handles(Object *p_object) const override; |
| 945 | |
| 946 | virtual Dictionary get_state() const override; |
| 947 | virtual void set_state(const Dictionary &p_state) override; |
| 948 | virtual void clear() override { spatial_editor->clear(); } |
| 949 | |
| 950 | virtual void edited_scene_changed() override; |
| 951 | |
| 952 | Node3DEditorPlugin(); |
| 953 | ~Node3DEditorPlugin(); |
| 954 | }; |
| 955 | |
| 956 | class ViewportNavigationControl : public Control { |
| 957 | GDCLASS(ViewportNavigationControl, Control); |
| 958 | |
| 959 | Node3DEditorViewport *viewport = nullptr; |
| 960 | Vector2i focused_mouse_start; |
| 961 | Vector2 focused_pos; |
| 962 | bool hovered = false; |
| 963 | int focused_index = -1; |
| 964 | Node3DEditorViewport::NavigationMode nav_mode = Node3DEditorViewport::NavigationMode::NAVIGATION_NONE; |
| 965 | |
| 966 | const float AXIS_CIRCLE_RADIUS = 30.0f * EDSCALE; |
| 967 | |
| 968 | protected: |
| 969 | void _notification(int p_what); |
| 970 | virtual void gui_input(const Ref<InputEvent> &p_event) override; |
| 971 | void _draw(); |
| 972 | void _on_mouse_entered(); |
| 973 | void _on_mouse_exited(); |
| 974 | void _process_click(int p_index, Vector2 p_position, bool p_pressed); |
| 975 | void _process_drag(int p_index, Vector2 p_position, Vector2 p_relative_position); |
| 976 | void _update_navigation(); |
| 977 | |
| 978 | public: |
| 979 | void set_navigation_mode(Node3DEditorViewport::NavigationMode p_nav_mode); |
| 980 | void set_viewport(Node3DEditorViewport *p_viewport); |
| 981 | }; |
| 982 | |
| 983 | #endif // NODE_3D_EDITOR_PLUGIN_H |
| 984 | |