From d1acbbce7f123c2b5fccdefc6417787dc91b6ced Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Verschelde?= Date: Fri, 27 Mar 2020 08:44:44 +0100 Subject: Rename more 2D and 3D nodes to follow convention Rename editor plugins to match the new node names. --- editor/debugger/script_editor_debugger.cpp | 2 +- editor/editor_node.cpp | 46 +- editor/editor_plugin.cpp | 2 +- editor/icons/BoxShape.svg | 1 - editor/icons/BoxShape3D.svg | 1 + editor/icons/ClippedCamera.svg | 1 - editor/icons/ClippedCamera3D.svg | 1 + editor/icons/HeightMapShape.svg | 1 - editor/icons/HeightMapShape3D.svg | 1 + editor/icons/PlaneShape.svg | 1 - editor/icons/SpatialMaterial.svg | 1 - editor/icons/VehicleBody.svg | 1 - editor/icons/VehicleBody3D.svg | 1 + editor/icons/VehicleWheel.svg | 1 - editor/icons/VehicleWheel3D.svg | 1 + editor/icons/World.svg | 1 - editor/icons/World3D.svg | 1 + editor/icons/WorldMarginShape.svg | 1 - editor/icons/WorldMarginShape3D.svg | 1 + editor/import/resource_importer_scene.cpp | 8 +- editor/node_3d_editor_gizmos.cpp | 4510 +++++++++++++ editor/node_3d_editor_gizmos.h | 434 ++ editor/plugins/animation_player_editor_plugin.cpp | 2 +- editor/plugins/camera_3d_editor_plugin.cpp | 124 + editor/plugins/camera_3d_editor_plugin.h | 75 + editor/plugins/camera_editor_plugin.cpp | 124 - editor/plugins/camera_editor_plugin.h | 75 - editor/plugins/canvas_item_editor_plugin.cpp | 12 +- editor/plugins/canvas_item_editor_plugin.h | 2 +- .../plugins/collision_polygon_3d_editor_plugin.cpp | 608 ++ .../plugins/collision_polygon_3d_editor_plugin.h | 119 + editor/plugins/collision_polygon_editor_plugin.cpp | 608 -- editor/plugins/collision_polygon_editor_plugin.h | 119 - editor/plugins/cpu_particles_2d_editor_plugin.cpp | 5 +- editor/plugins/cpu_particles_3d_editor_plugin.cpp | 145 + editor/plugins/cpu_particles_3d_editor_plugin.h | 85 + editor/plugins/cpu_particles_editor_plugin.cpp | 145 - editor/plugins/cpu_particles_editor_plugin.h | 85 - editor/plugins/gpu_particles_2d_editor_plugin.cpp | 435 ++ editor/plugins/gpu_particles_2d_editor_plugin.h | 100 + editor/plugins/gpu_particles_3d_editor_plugin.cpp | 493 ++ editor/plugins/gpu_particles_3d_editor_plugin.h | 121 + editor/plugins/gradient_editor_plugin.cpp | 2 +- editor/plugins/material_editor_plugin.cpp | 2 +- editor/plugins/mesh_editor_plugin.cpp | 2 +- editor/plugins/mesh_instance_3d_editor_plugin.cpp | 531 ++ editor/plugins/mesh_instance_3d_editor_plugin.h | 104 + editor/plugins/mesh_instance_editor_plugin.cpp | 531 -- editor/plugins/mesh_instance_editor_plugin.h | 104 - editor/plugins/mesh_library_editor_plugin.cpp | 2 +- editor/plugins/multimesh_editor_plugin.cpp | 4 +- editor/plugins/navigation_polygon_editor_plugin.h | 2 +- editor/plugins/node_3d_editor_plugin.cpp | 6770 ++++++++++++++++++++ editor/plugins/node_3d_editor_plugin.h | 883 +++ editor/plugins/particles_2d_editor_plugin.cpp | 435 -- editor/plugins/particles_2d_editor_plugin.h | 100 - editor/plugins/particles_editor_plugin.cpp | 493 -- editor/plugins/particles_editor_plugin.h | 121 - editor/plugins/path_3d_editor_plugin.cpp | 653 ++ editor/plugins/path_3d_editor_plugin.h | 123 + editor/plugins/path_editor_plugin.cpp | 653 -- editor/plugins/path_editor_plugin.h | 123 - editor/plugins/physical_bone_3d_editor_plugin.cpp | 113 + editor/plugins/physical_bone_3d_editor_plugin.h | 78 + editor/plugins/physical_bone_plugin.cpp | 112 - editor/plugins/physical_bone_plugin.h | 78 - editor/plugins/skeleton_3d_editor_plugin.cpp | 195 + editor/plugins/skeleton_3d_editor_plugin.h | 96 + editor/plugins/skeleton_editor_plugin.cpp | 195 - editor/plugins/skeleton_editor_plugin.h | 96 - editor/plugins/skeleton_ik_3d_editor_plugin.cpp | 96 + editor/plugins/skeleton_ik_3d_editor_plugin.h | 64 + editor/plugins/skeleton_ik_editor_plugin.cpp | 96 - editor/plugins/skeleton_ik_editor_plugin.h | 64 - editor/plugins/spatial_editor_plugin.cpp | 6770 -------------------- editor/plugins/spatial_editor_plugin.h | 883 --- editor/plugins/sprite_2d_editor_plugin.cpp | 611 ++ editor/plugins/sprite_2d_editor_plugin.h | 117 + editor/plugins/sprite_editor_plugin.cpp | 611 -- editor/plugins/sprite_editor_plugin.h | 117 - editor/scene_tree_dock.cpp | 2 +- editor/spatial_editor_gizmos.cpp | 4510 ------------- editor/spatial_editor_gizmos.h | 434 -- modules/csg/csg_gizmos.h | 2 +- modules/gridmap/grid_map_editor_plugin.cpp | 2 +- scene/2d/canvas_item.cpp | 1491 ----- scene/2d/canvas_item.h | 426 -- scene/2d/cpu_particles_2d.cpp | 6 +- scene/2d/gpu_particles_2d.cpp | 432 ++ scene/2d/gpu_particles_2d.h | 127 + scene/2d/navigation_2d.h | 2 +- scene/2d/navigation_polygon.cpp | 582 -- scene/2d/navigation_polygon.h | 130 - scene/2d/navigation_region_2d.cpp | 582 ++ scene/2d/navigation_region_2d.h | 130 + scene/2d/node_2d.h | 2 +- scene/2d/particles_2d.cpp | 432 -- scene/2d/particles_2d.h | 127 - scene/2d/visibility_notifier_2d.cpp | 6 +- scene/3d/audio_stream_player_3d.cpp | 2 +- scene/3d/camera_3d.cpp | 82 +- scene/3d/camera_3d.h | 26 +- scene/3d/collision_shape_3d.cpp | 2 +- scene/3d/node_3d.cpp | 6 +- scene/3d/node_3d.h | 2 +- scene/3d/physics_body_3d.cpp | 2 +- scene/3d/ray_cast_3d.cpp | 2 +- scene/3d/skeleton_ik_3d.cpp | 578 ++ scene/3d/skeleton_ik_3d.h | 220 + scene/3d/vehicle_body.cpp | 998 --- scene/3d/vehicle_body.h | 212 - scene/3d/vehicle_body_3d.cpp | 998 +++ scene/3d/vehicle_body_3d.h | 212 + scene/animation/skeleton_ik.cpp | 578 -- scene/animation/skeleton_ik.h | 220 - scene/gui/control.h | 2 +- scene/main/canvas_item.cpp | 1491 +++++ scene/main/canvas_item.h | 426 ++ scene/main/scene_tree.cpp | 2 +- scene/main/scene_tree.h | 2 +- scene/main/timer.cpp | 0 scene/main/timer.h | 0 scene/main/viewport.cpp | 14 +- scene/main/viewport.h | 10 +- scene/register_scene_types.cpp | 157 +- scene/resources/height_map_shape.cpp | 209 - scene/resources/height_map_shape.h | 63 - scene/resources/height_map_shape_3d.cpp | 209 + scene/resources/height_map_shape_3d.h | 63 + scene/resources/ray_shape.cpp | 105 - scene/resources/ray_shape.h | 57 - scene/resources/ray_shape_3d.cpp | 105 + scene/resources/ray_shape_3d.h | 57 + scene/resources/style_box.cpp | 3 +- scene/resources/tile_set.h | 2 +- scene/resources/world.cpp | 382 -- scene/resources/world.h | 92 - scene/resources/world_3d.cpp | 382 ++ scene/resources/world_3d.h | 91 + servers/navigation_2d_server.h | 2 +- 140 files changed, 24005 insertions(+), 24016 deletions(-) delete mode 100644 editor/icons/BoxShape.svg create mode 100644 editor/icons/BoxShape3D.svg delete mode 100644 editor/icons/ClippedCamera.svg create mode 100644 editor/icons/ClippedCamera3D.svg delete mode 100644 editor/icons/HeightMapShape.svg create mode 100644 editor/icons/HeightMapShape3D.svg delete mode 100644 editor/icons/PlaneShape.svg delete mode 100644 editor/icons/SpatialMaterial.svg delete mode 100644 editor/icons/VehicleBody.svg create mode 100644 editor/icons/VehicleBody3D.svg delete mode 100644 editor/icons/VehicleWheel.svg create mode 100644 editor/icons/VehicleWheel3D.svg delete mode 100644 editor/icons/World.svg create mode 100644 editor/icons/World3D.svg delete mode 100644 editor/icons/WorldMarginShape.svg create mode 100644 editor/icons/WorldMarginShape3D.svg create mode 100644 editor/node_3d_editor_gizmos.cpp create mode 100644 editor/node_3d_editor_gizmos.h create mode 100644 editor/plugins/camera_3d_editor_plugin.cpp create mode 100644 editor/plugins/camera_3d_editor_plugin.h delete mode 100644 editor/plugins/camera_editor_plugin.cpp delete mode 100644 editor/plugins/camera_editor_plugin.h create mode 100644 editor/plugins/collision_polygon_3d_editor_plugin.cpp create mode 100644 editor/plugins/collision_polygon_3d_editor_plugin.h delete mode 100644 editor/plugins/collision_polygon_editor_plugin.cpp delete mode 100644 editor/plugins/collision_polygon_editor_plugin.h create mode 100644 editor/plugins/cpu_particles_3d_editor_plugin.cpp create mode 100644 editor/plugins/cpu_particles_3d_editor_plugin.h delete mode 100644 editor/plugins/cpu_particles_editor_plugin.cpp delete mode 100644 editor/plugins/cpu_particles_editor_plugin.h create mode 100644 editor/plugins/gpu_particles_2d_editor_plugin.cpp create mode 100644 editor/plugins/gpu_particles_2d_editor_plugin.h create mode 100644 editor/plugins/gpu_particles_3d_editor_plugin.cpp create mode 100644 editor/plugins/gpu_particles_3d_editor_plugin.h create mode 100644 editor/plugins/mesh_instance_3d_editor_plugin.cpp create mode 100644 editor/plugins/mesh_instance_3d_editor_plugin.h delete mode 100644 editor/plugins/mesh_instance_editor_plugin.cpp delete mode 100644 editor/plugins/mesh_instance_editor_plugin.h create mode 100644 editor/plugins/node_3d_editor_plugin.cpp create mode 100644 editor/plugins/node_3d_editor_plugin.h delete mode 100644 editor/plugins/particles_2d_editor_plugin.cpp delete mode 100644 editor/plugins/particles_2d_editor_plugin.h delete mode 100644 editor/plugins/particles_editor_plugin.cpp delete mode 100644 editor/plugins/particles_editor_plugin.h create mode 100644 editor/plugins/path_3d_editor_plugin.cpp create mode 100644 editor/plugins/path_3d_editor_plugin.h delete mode 100644 editor/plugins/path_editor_plugin.cpp delete mode 100644 editor/plugins/path_editor_plugin.h create mode 100644 editor/plugins/physical_bone_3d_editor_plugin.cpp create mode 100644 editor/plugins/physical_bone_3d_editor_plugin.h delete mode 100644 editor/plugins/physical_bone_plugin.cpp delete mode 100644 editor/plugins/physical_bone_plugin.h create mode 100644 editor/plugins/skeleton_3d_editor_plugin.cpp create mode 100644 editor/plugins/skeleton_3d_editor_plugin.h delete mode 100644 editor/plugins/skeleton_editor_plugin.cpp delete mode 100644 editor/plugins/skeleton_editor_plugin.h create mode 100644 editor/plugins/skeleton_ik_3d_editor_plugin.cpp create mode 100644 editor/plugins/skeleton_ik_3d_editor_plugin.h delete mode 100644 editor/plugins/skeleton_ik_editor_plugin.cpp delete mode 100644 editor/plugins/skeleton_ik_editor_plugin.h delete mode 100644 editor/plugins/spatial_editor_plugin.cpp delete mode 100644 editor/plugins/spatial_editor_plugin.h create mode 100644 editor/plugins/sprite_2d_editor_plugin.cpp create mode 100644 editor/plugins/sprite_2d_editor_plugin.h delete mode 100644 editor/plugins/sprite_editor_plugin.cpp delete mode 100644 editor/plugins/sprite_editor_plugin.h delete mode 100644 editor/spatial_editor_gizmos.cpp delete mode 100644 editor/spatial_editor_gizmos.h delete mode 100644 scene/2d/canvas_item.cpp delete mode 100644 scene/2d/canvas_item.h create mode 100644 scene/2d/gpu_particles_2d.cpp create mode 100644 scene/2d/gpu_particles_2d.h delete mode 100644 scene/2d/navigation_polygon.cpp delete mode 100644 scene/2d/navigation_polygon.h create mode 100644 scene/2d/navigation_region_2d.cpp create mode 100644 scene/2d/navigation_region_2d.h delete mode 100644 scene/2d/particles_2d.cpp delete mode 100644 scene/2d/particles_2d.h create mode 100644 scene/3d/skeleton_ik_3d.cpp create mode 100644 scene/3d/skeleton_ik_3d.h delete mode 100644 scene/3d/vehicle_body.cpp delete mode 100644 scene/3d/vehicle_body.h create mode 100644 scene/3d/vehicle_body_3d.cpp create mode 100644 scene/3d/vehicle_body_3d.h delete mode 100644 scene/animation/skeleton_ik.cpp delete mode 100644 scene/animation/skeleton_ik.h create mode 100644 scene/main/canvas_item.cpp create mode 100644 scene/main/canvas_item.h mode change 100755 => 100644 scene/main/timer.cpp mode change 100755 => 100644 scene/main/timer.h delete mode 100644 scene/resources/height_map_shape.cpp delete mode 100644 scene/resources/height_map_shape.h create mode 100644 scene/resources/height_map_shape_3d.cpp create mode 100644 scene/resources/height_map_shape_3d.h delete mode 100644 scene/resources/ray_shape.cpp delete mode 100644 scene/resources/ray_shape.h create mode 100644 scene/resources/ray_shape_3d.cpp create mode 100644 scene/resources/ray_shape_3d.h delete mode 100644 scene/resources/world.cpp delete mode 100644 scene/resources/world.h create mode 100644 scene/resources/world_3d.cpp create mode 100644 scene/resources/world_3d.h diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp index 41ba7343d2..bdb1ebd4d7 100644 --- a/editor/debugger/script_editor_debugger.cpp +++ b/editor/debugger/script_editor_debugger.cpp @@ -42,7 +42,7 @@ #include "editor/editor_scale.h" #include "editor/editor_settings.h" #include "editor/plugins/canvas_item_editor_plugin.h" -#include "editor/plugins/spatial_editor_plugin.h" +#include "editor/plugins/node_3d_editor_plugin.h" #include "editor/property_editor.h" #include "main/performance.h" #include "scene/3d/camera_3d.h" diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 70ee45f845..6b332ca300 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -114,32 +114,33 @@ #include "editor/plugins/asset_library_editor_plugin.h" #include "editor/plugins/audio_stream_editor_plugin.h" #include "editor/plugins/baked_lightmap_editor_plugin.h" -#include "editor/plugins/camera_editor_plugin.h" +#include "editor/plugins/camera_3d_editor_plugin.h" #include "editor/plugins/canvas_item_editor_plugin.h" #include "editor/plugins/collision_polygon_2d_editor_plugin.h" -#include "editor/plugins/collision_polygon_editor_plugin.h" +#include "editor/plugins/collision_polygon_3d_editor_plugin.h" #include "editor/plugins/collision_shape_2d_editor_plugin.h" #include "editor/plugins/cpu_particles_2d_editor_plugin.h" -#include "editor/plugins/cpu_particles_editor_plugin.h" +#include "editor/plugins/cpu_particles_3d_editor_plugin.h" #include "editor/plugins/curve_editor_plugin.h" #include "editor/plugins/debugger_editor_plugin.h" #include "editor/plugins/editor_preview_plugins.h" #include "editor/plugins/gi_probe_editor_plugin.h" +#include "editor/plugins/gpu_particles_2d_editor_plugin.h" +#include "editor/plugins/gpu_particles_3d_editor_plugin.h" #include "editor/plugins/gradient_editor_plugin.h" #include "editor/plugins/item_list_editor_plugin.h" #include "editor/plugins/light_occluder_2d_editor_plugin.h" #include "editor/plugins/line_2d_editor_plugin.h" #include "editor/plugins/material_editor_plugin.h" #include "editor/plugins/mesh_editor_plugin.h" -#include "editor/plugins/mesh_instance_editor_plugin.h" +#include "editor/plugins/mesh_instance_3d_editor_plugin.h" #include "editor/plugins/mesh_library_editor_plugin.h" #include "editor/plugins/multimesh_editor_plugin.h" #include "editor/plugins/navigation_polygon_editor_plugin.h" -#include "editor/plugins/particles_2d_editor_plugin.h" -#include "editor/plugins/particles_editor_plugin.h" +#include "editor/plugins/node_3d_editor_plugin.h" #include "editor/plugins/path_2d_editor_plugin.h" -#include "editor/plugins/path_editor_plugin.h" -#include "editor/plugins/physical_bone_plugin.h" +#include "editor/plugins/path_3d_editor_plugin.h" +#include "editor/plugins/physical_bone_3d_editor_plugin.h" #include "editor/plugins/polygon_2d_editor_plugin.h" #include "editor/plugins/resource_preloader_editor_plugin.h" #include "editor/plugins/root_motion_editor_plugin.h" @@ -147,10 +148,9 @@ #include "editor/plugins/script_text_editor.h" #include "editor/plugins/shader_editor_plugin.h" #include "editor/plugins/skeleton_2d_editor_plugin.h" -#include "editor/plugins/skeleton_editor_plugin.h" -#include "editor/plugins/skeleton_ik_editor_plugin.h" -#include "editor/plugins/spatial_editor_plugin.h" -#include "editor/plugins/sprite_editor_plugin.h" +#include "editor/plugins/skeleton_3d_editor_plugin.h" +#include "editor/plugins/skeleton_ik_3d_editor_plugin.h" +#include "editor/plugins/sprite_2d_editor_plugin.h" #include "editor/plugins/sprite_frames_editor_plugin.h" #include "editor/plugins/style_box_editor_plugin.h" #include "editor/plugins/text_editor.h" @@ -6597,18 +6597,19 @@ EditorNode::EditorNode() { add_editor_plugin(memnew(ShaderEditorPlugin(this))); add_editor_plugin(memnew(VisualShaderEditorPlugin(this))); - add_editor_plugin(memnew(CameraEditorPlugin(this))); + add_editor_plugin(memnew(Camera3DEditorPlugin(this))); add_editor_plugin(memnew(ThemeEditorPlugin(this))); add_editor_plugin(memnew(MultiMeshEditorPlugin(this))); - add_editor_plugin(memnew(MeshInstanceEditorPlugin(this))); + add_editor_plugin(memnew(MeshInstance3DEditorPlugin(this))); add_editor_plugin(memnew(AnimationTreeEditorPlugin(this))); add_editor_plugin(memnew(MeshLibraryEditorPlugin(this))); add_editor_plugin(memnew(StyleBoxEditorPlugin(this))); - add_editor_plugin(memnew(SpriteEditorPlugin(this))); + add_editor_plugin(memnew(Sprite2DEditorPlugin(this))); add_editor_plugin(memnew(Skeleton2DEditorPlugin(this))); - add_editor_plugin(memnew(ParticlesEditorPlugin(this))); + add_editor_plugin(memnew(GPUParticles2DEditorPlugin(this))); + add_editor_plugin(memnew(GPUParticles3DEditorPlugin(this))); add_editor_plugin(memnew(CPUParticles2DEditorPlugin(this))); - add_editor_plugin(memnew(CPUParticlesEditorPlugin(this))); + add_editor_plugin(memnew(CPUParticles3DEditorPlugin(this))); add_editor_plugin(memnew(ResourcePreloaderEditorPlugin(this))); add_editor_plugin(memnew(ItemListEditorPlugin(this))); add_editor_plugin(memnew(Polygon3DEditorPlugin(this))); @@ -6617,11 +6618,10 @@ EditorNode::EditorNode() { add_editor_plugin(memnew(TileMapEditorPlugin(this))); add_editor_plugin(memnew(SpriteFramesEditorPlugin(this))); add_editor_plugin(memnew(TextureRegionEditorPlugin(this))); - add_editor_plugin(memnew(Particles2DEditorPlugin(this))); add_editor_plugin(memnew(GIProbeEditorPlugin(this))); - // add_editor_plugin(memnew(BakedLightmapEditorPlugin(this))); + //add_editor_plugin(memnew(BakedLightmapEditorPlugin(this))); add_editor_plugin(memnew(Path2DEditorPlugin(this))); - add_editor_plugin(memnew(PathEditorPlugin(this))); + add_editor_plugin(memnew(Path3DEditorPlugin(this))); add_editor_plugin(memnew(Line2DEditorPlugin(this))); add_editor_plugin(memnew(Polygon2DEditorPlugin(this))); add_editor_plugin(memnew(LightOccluder2DEditorPlugin(this))); @@ -6632,9 +6632,9 @@ EditorNode::EditorNode() { add_editor_plugin(memnew(TextureEditorPlugin(this))); add_editor_plugin(memnew(AudioStreamEditorPlugin(this))); add_editor_plugin(memnew(AudioBusesEditorPlugin(audio_bus_editor))); - add_editor_plugin(memnew(SkeletonEditorPlugin(this))); - add_editor_plugin(memnew(SkeletonIKEditorPlugin(this))); - add_editor_plugin(memnew(PhysicalBonePlugin(this))); + add_editor_plugin(memnew(Skeleton3DEditorPlugin(this))); + add_editor_plugin(memnew(SkeletonIK3DEditorPlugin(this))); + add_editor_plugin(memnew(PhysicalBone3DEditorPlugin(this))); add_editor_plugin(memnew(MeshEditorPlugin(this))); add_editor_plugin(memnew(MaterialEditorPlugin(this))); diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp index 2ddd86f4fb..2cafbe3cca 100644 --- a/editor/editor_plugin.cpp +++ b/editor/editor_plugin.cpp @@ -38,7 +38,7 @@ #include "editor_resource_preview.h" #include "main/main.h" #include "plugins/canvas_item_editor_plugin.h" -#include "plugins/spatial_editor_plugin.h" +#include "plugins/node_3d_editor_plugin.h" #include "scene/3d/camera_3d.h" #include "scene/gui/popup_menu.h" #include "servers/visual_server.h" diff --git a/editor/icons/BoxShape.svg b/editor/icons/BoxShape.svg deleted file mode 100644 index 171e95f4fa..0000000000 --- a/editor/icons/BoxShape.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/editor/icons/BoxShape3D.svg b/editor/icons/BoxShape3D.svg new file mode 100644 index 0000000000..171e95f4fa --- /dev/null +++ b/editor/icons/BoxShape3D.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/editor/icons/ClippedCamera.svg b/editor/icons/ClippedCamera.svg deleted file mode 100644 index 8c80c04e27..0000000000 --- a/editor/icons/ClippedCamera.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/editor/icons/ClippedCamera3D.svg b/editor/icons/ClippedCamera3D.svg new file mode 100644 index 0000000000..8c80c04e27 --- /dev/null +++ b/editor/icons/ClippedCamera3D.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/editor/icons/HeightMapShape.svg b/editor/icons/HeightMapShape.svg deleted file mode 100644 index 2e0bf53565..0000000000 --- a/editor/icons/HeightMapShape.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/editor/icons/HeightMapShape3D.svg b/editor/icons/HeightMapShape3D.svg new file mode 100644 index 0000000000..2e0bf53565 --- /dev/null +++ b/editor/icons/HeightMapShape3D.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/editor/icons/PlaneShape.svg b/editor/icons/PlaneShape.svg deleted file mode 100644 index 2c90cf6d53..0000000000 --- a/editor/icons/PlaneShape.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/editor/icons/SpatialMaterial.svg b/editor/icons/SpatialMaterial.svg deleted file mode 100644 index cfd994a0fe..0000000000 --- a/editor/icons/SpatialMaterial.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/editor/icons/VehicleBody.svg b/editor/icons/VehicleBody.svg deleted file mode 100644 index a509730602..0000000000 --- a/editor/icons/VehicleBody.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/editor/icons/VehicleBody3D.svg b/editor/icons/VehicleBody3D.svg new file mode 100644 index 0000000000..a509730602 --- /dev/null +++ b/editor/icons/VehicleBody3D.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/editor/icons/VehicleWheel.svg b/editor/icons/VehicleWheel.svg deleted file mode 100644 index bd870c0118..0000000000 --- a/editor/icons/VehicleWheel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/editor/icons/VehicleWheel3D.svg b/editor/icons/VehicleWheel3D.svg new file mode 100644 index 0000000000..bd870c0118 --- /dev/null +++ b/editor/icons/VehicleWheel3D.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/editor/icons/World.svg b/editor/icons/World.svg deleted file mode 100644 index 3db96a75a6..0000000000 --- a/editor/icons/World.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/editor/icons/World3D.svg b/editor/icons/World3D.svg new file mode 100644 index 0000000000..3db96a75a6 --- /dev/null +++ b/editor/icons/World3D.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/editor/icons/WorldMarginShape.svg b/editor/icons/WorldMarginShape.svg deleted file mode 100644 index 2c90cf6d53..0000000000 --- a/editor/icons/WorldMarginShape.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/editor/icons/WorldMarginShape3D.svg b/editor/icons/WorldMarginShape3D.svg new file mode 100644 index 0000000000..2c90cf6d53 --- /dev/null +++ b/editor/icons/WorldMarginShape3D.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp index 16b5c00969..fb57773b18 100644 --- a/editor/import/resource_importer_scene.cpp +++ b/editor/import/resource_importer_scene.cpp @@ -36,12 +36,12 @@ #include "scene/3d/mesh_instance_3d.h" #include "scene/3d/navigation_3d.h" #include "scene/3d/physics_body_3d.h" -#include "scene/3d/vehicle_body.h" +#include "scene/3d/vehicle_body_3d.h" #include "scene/animation/animation_player.h" #include "scene/resources/animation.h" #include "scene/resources/box_shape_3d.h" #include "scene/resources/packed_scene.h" -#include "scene/resources/ray_shape.h" +#include "scene/resources/ray_shape_3d.h" #include "scene/resources/resource_format_text.h" #include "scene/resources/sphere_shape_3d.h" #include "scene/resources/world_margin_shape_3d.h" @@ -576,7 +576,7 @@ Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map Node *owner = p_node->get_owner(); Node3D *s = Object::cast_to(p_node); - VehicleBody *bv = memnew(VehicleBody); + VehicleBody3D *bv = memnew(VehicleBody3D); String n = _fixstr(p_node->get_name(), "vehicle"); bv->set_name(n); p_node->replace_by(bv); @@ -596,7 +596,7 @@ Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map Node *owner = p_node->get_owner(); Node3D *s = Object::cast_to(p_node); - VehicleWheel *bv = memnew(VehicleWheel); + VehicleWheel3D *bv = memnew(VehicleWheel3D); String n = _fixstr(p_node->get_name(), "wheel"); bv->set_name(n); p_node->replace_by(bv); diff --git a/editor/node_3d_editor_gizmos.cpp b/editor/node_3d_editor_gizmos.cpp new file mode 100644 index 0000000000..67f0d991bd --- /dev/null +++ b/editor/node_3d_editor_gizmos.cpp @@ -0,0 +1,4510 @@ +/*************************************************************************/ +/* node_3d_editor_gizmos.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "node_3d_editor_gizmos.h" + +#include "core/math/geometry.h" +#include "core/math/quick_hull.h" +#include "scene/3d/audio_stream_player_3d.h" +#include "scene/3d/baked_lightmap.h" +#include "scene/3d/collision_polygon_3d.h" +#include "scene/3d/collision_shape_3d.h" +#include "scene/3d/cpu_particles_3d.h" +#include "scene/3d/gi_probe.h" +#include "scene/3d/gpu_particles_3d.h" +#include "scene/3d/light_3d.h" +#include "scene/3d/listener_3d.h" +#include "scene/3d/mesh_instance_3d.h" +#include "scene/3d/navigation_region_3d.h" +#include "scene/3d/physics_joint_3d.h" +#include "scene/3d/position_3d.h" +#include "scene/3d/ray_cast_3d.h" +#include "scene/3d/reflection_probe.h" +#include "scene/3d/soft_body_3d.h" +#include "scene/3d/spring_arm_3d.h" +#include "scene/3d/sprite_3d.h" +#include "scene/3d/vehicle_body_3d.h" +#include "scene/3d/visibility_notifier_3d.h" +#include "scene/resources/box_shape_3d.h" +#include "scene/resources/capsule_shape_3d.h" +#include "scene/resources/concave_polygon_shape_3d.h" +#include "scene/resources/convex_polygon_shape_3d.h" +#include "scene/resources/cylinder_shape_3d.h" +#include "scene/resources/height_map_shape_3d.h" +#include "scene/resources/primitive_meshes.h" +#include "scene/resources/ray_shape_3d.h" +#include "scene/resources/sphere_shape_3d.h" +#include "scene/resources/surface_tool.h" +#include "scene/resources/world_margin_shape_3d.h" + +#define HANDLE_HALF_SIZE 9.5 + +bool EditorNode3DGizmo::is_editable() const { + + ERR_FAIL_COND_V(!spatial_node, false); + Node *edited_root = spatial_node->get_tree()->get_edited_scene_root(); + if (spatial_node == edited_root) + return true; + if (spatial_node->get_owner() == edited_root) + return true; + + if (edited_root->is_editable_instance(spatial_node->get_owner())) + return true; + + return false; +} + +void EditorNode3DGizmo::clear() { + + for (int i = 0; i < instances.size(); i++) { + + if (instances[i].instance.is_valid()) + VS::get_singleton()->free(instances[i].instance); + } + + billboard_handle = false; + collision_segments.clear(); + collision_mesh = Ref(); + instances.clear(); + handles.clear(); + secondary_handles.clear(); +} + +void EditorNode3DGizmo::redraw() { + + if (get_script_instance() && get_script_instance()->has_method("redraw")) { + get_script_instance()->call("redraw"); + return; + } + + ERR_FAIL_COND(!gizmo_plugin); + gizmo_plugin->redraw(this); +} + +String EditorNode3DGizmo::get_handle_name(int p_idx) const { + + if (get_script_instance() && get_script_instance()->has_method("get_handle_name")) { + return get_script_instance()->call("get_handle_name", p_idx); + } + + ERR_FAIL_COND_V(!gizmo_plugin, ""); + return gizmo_plugin->get_handle_name(this, p_idx); +} + +bool EditorNode3DGizmo::is_handle_highlighted(int p_idx) const { + + if (get_script_instance() && get_script_instance()->has_method("is_handle_highlighted")) { + return get_script_instance()->call("is_handle_highlighted", p_idx); + } + + ERR_FAIL_COND_V(!gizmo_plugin, false); + return gizmo_plugin->is_handle_highlighted(this, p_idx); +} + +Variant EditorNode3DGizmo::get_handle_value(int p_idx) { + + if (get_script_instance() && get_script_instance()->has_method("get_handle_value")) { + return get_script_instance()->call("get_handle_value", p_idx); + } + + ERR_FAIL_COND_V(!gizmo_plugin, Variant()); + return gizmo_plugin->get_handle_value(this, p_idx); +} + +void EditorNode3DGizmo::set_handle(int p_idx, Camera3D *p_camera, const Point2 &p_point) { + + if (get_script_instance() && get_script_instance()->has_method("set_handle")) { + get_script_instance()->call("set_handle", p_idx, p_camera, p_point); + return; + } + + ERR_FAIL_COND(!gizmo_plugin); + gizmo_plugin->set_handle(this, p_idx, p_camera, p_point); +} + +void EditorNode3DGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p_cancel) { + + if (get_script_instance() && get_script_instance()->has_method("commit_handle")) { + get_script_instance()->call("commit_handle", p_idx, p_restore, p_cancel); + return; + } + + ERR_FAIL_COND(!gizmo_plugin); + gizmo_plugin->commit_handle(this, p_idx, p_restore, p_cancel); +} + +void EditorNode3DGizmo::set_spatial_node(Node3D *p_node) { + + ERR_FAIL_NULL(p_node); + spatial_node = p_node; +} + +void EditorNode3DGizmo::Instance::create_instance(Node3D *p_base, bool p_hidden) { + + instance = VS::get_singleton()->instance_create2(mesh->get_rid(), p_base->get_world()->get_scenario()); + VS::get_singleton()->instance_attach_object_instance_id(instance, p_base->get_instance_id()); + if (skin_reference.is_valid()) { + VS::get_singleton()->instance_attach_skeleton(instance, skin_reference->get_skeleton()); + } + if (extra_margin) + VS::get_singleton()->instance_set_extra_visibility_margin(instance, 1); + VS::get_singleton()->instance_geometry_set_cast_shadows_setting(instance, VS::SHADOW_CASTING_SETTING_OFF); + int layer = p_hidden ? 0 : 1 << Node3DEditorViewport::GIZMO_EDIT_LAYER; + VS::get_singleton()->instance_set_layer_mask(instance, layer); //gizmos are 26 +} + +void EditorNode3DGizmo::add_mesh(const Ref &p_mesh, bool p_billboard, const Ref &p_skin_reference, const Ref &p_material) { + + ERR_FAIL_COND(!spatial_node); + Instance ins; + + ins.billboard = p_billboard; + ins.mesh = p_mesh; + ins.skin_reference = p_skin_reference; + ins.material = p_material; + if (valid) { + ins.create_instance(spatial_node, hidden); + VS::get_singleton()->instance_set_transform(ins.instance, spatial_node->get_global_transform()); + if (ins.material.is_valid()) { + VS::get_singleton()->instance_geometry_set_material_override(ins.instance, p_material->get_rid()); + } + } + + instances.push_back(ins); +} + +void EditorNode3DGizmo::add_lines(const Vector &p_lines, const Ref &p_material, bool p_billboard, const Color &p_modulate) { + if (p_lines.empty()) { + return; + } + + ERR_FAIL_COND(!spatial_node); + Instance ins; + + Ref mesh = memnew(ArrayMesh); + Array a; + a.resize(Mesh::ARRAY_MAX); + + a[Mesh::ARRAY_VERTEX] = p_lines; + + Vector color; + color.resize(p_lines.size()); + { + Color *w = color.ptrw(); + for (int i = 0; i < p_lines.size(); i++) { + if (is_selected()) + w[i] = Color(1, 1, 1, 0.8) * p_modulate; + else + w[i] = Color(1, 1, 1, 0.2) * p_modulate; + } + } + + a[Mesh::ARRAY_COLOR] = color; + + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, a); + mesh->surface_set_material(0, p_material); + + if (p_billboard) { + float md = 0; + for (int i = 0; i < p_lines.size(); i++) { + + md = MAX(0, p_lines[i].length()); + } + if (md) { + mesh->set_custom_aabb(AABB(Vector3(-md, -md, -md), Vector3(md, md, md) * 2.0)); + } + } + + ins.billboard = p_billboard; + ins.mesh = mesh; + if (valid) { + ins.create_instance(spatial_node, hidden); + VS::get_singleton()->instance_set_transform(ins.instance, spatial_node->get_global_transform()); + } + + instances.push_back(ins); +} + +void EditorNode3DGizmo::add_unscaled_billboard(const Ref &p_material, float p_scale, const Color &p_modulate) { + + ERR_FAIL_COND(!spatial_node); + Instance ins; + + Vector vs; + Vector uv; + Vector colors; + + vs.push_back(Vector3(-p_scale, p_scale, 0)); + vs.push_back(Vector3(p_scale, p_scale, 0)); + vs.push_back(Vector3(p_scale, -p_scale, 0)); + vs.push_back(Vector3(-p_scale, -p_scale, 0)); + + uv.push_back(Vector2(0, 0)); + uv.push_back(Vector2(1, 0)); + uv.push_back(Vector2(1, 1)); + uv.push_back(Vector2(0, 1)); + + colors.push_back(p_modulate); + colors.push_back(p_modulate); + colors.push_back(p_modulate); + colors.push_back(p_modulate); + + Ref mesh = memnew(ArrayMesh); + Array a; + a.resize(Mesh::ARRAY_MAX); + a[Mesh::ARRAY_VERTEX] = vs; + a[Mesh::ARRAY_TEX_UV] = uv; + Vector indices; + indices.push_back(0); + indices.push_back(1); + indices.push_back(2); + indices.push_back(0); + indices.push_back(2); + indices.push_back(3); + a[Mesh::ARRAY_INDEX] = indices; + a[Mesh::ARRAY_COLOR] = colors; + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a); + mesh->surface_set_material(0, p_material); + + float md = 0; + for (int i = 0; i < vs.size(); i++) { + + md = MAX(0, vs[i].length()); + } + if (md) { + mesh->set_custom_aabb(AABB(Vector3(-md, -md, -md), Vector3(md, md, md) * 2.0)); + } + + selectable_icon_size = p_scale; + mesh->set_custom_aabb(AABB(Vector3(-selectable_icon_size, -selectable_icon_size, -selectable_icon_size) * 100.0f, Vector3(selectable_icon_size, selectable_icon_size, selectable_icon_size) * 200.0f)); + + ins.mesh = mesh; + ins.unscaled = true; + ins.billboard = true; + if (valid) { + ins.create_instance(spatial_node, hidden); + VS::get_singleton()->instance_set_transform(ins.instance, spatial_node->get_global_transform()); + } + + selectable_icon_size = p_scale; + + instances.push_back(ins); +} + +void EditorNode3DGizmo::add_collision_triangles(const Ref &p_tmesh) { + collision_mesh = p_tmesh; +} + +void EditorNode3DGizmo::add_collision_segments(const Vector &p_lines) { + + int from = collision_segments.size(); + collision_segments.resize(from + p_lines.size()); + for (int i = 0; i < p_lines.size(); i++) { + + collision_segments.write[from + i] = p_lines[i]; + } +} + +void EditorNode3DGizmo::add_handles(const Vector &p_handles, const Ref &p_material, bool p_billboard, bool p_secondary) { + + billboard_handle = p_billboard; + + if (!is_selected() || !is_editable()) + return; + + ERR_FAIL_COND(!spatial_node); + + Instance ins; + + Ref mesh = memnew(ArrayMesh); + + Array a; + a.resize(VS::ARRAY_MAX); + a[VS::ARRAY_VERTEX] = p_handles; + Vector colors; + { + colors.resize(p_handles.size()); + Color *w = colors.ptrw(); + for (int i = 0; i < p_handles.size(); i++) { + + Color col(1, 1, 1, 1); + if (is_handle_highlighted(i)) + col = Color(0, 0, 1, 0.9); + + if (Node3DEditor::get_singleton()->get_over_gizmo_handle() != i) + col.a = 0.8; + + w[i] = col; + } + } + a[VS::ARRAY_COLOR] = colors; + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_POINTS, a); + mesh->surface_set_material(0, p_material); + + if (p_billboard) { + float md = 0; + for (int i = 0; i < p_handles.size(); i++) { + + md = MAX(0, p_handles[i].length()); + } + if (md) { + mesh->set_custom_aabb(AABB(Vector3(-md, -md, -md), Vector3(md, md, md) * 2.0)); + } + } + + ins.mesh = mesh; + ins.billboard = p_billboard; + ins.extra_margin = true; + if (valid) { + ins.create_instance(spatial_node, hidden); + VS::get_singleton()->instance_set_transform(ins.instance, spatial_node->get_global_transform()); + } + instances.push_back(ins); + if (!p_secondary) { + int chs = handles.size(); + handles.resize(chs + p_handles.size()); + for (int i = 0; i < p_handles.size(); i++) { + handles.write[i + chs] = p_handles[i]; + } + } else { + + int chs = secondary_handles.size(); + secondary_handles.resize(chs + p_handles.size()); + for (int i = 0; i < p_handles.size(); i++) { + secondary_handles.write[i + chs] = p_handles[i]; + } + } +} + +void EditorNode3DGizmo::add_solid_box(Ref &p_material, Vector3 p_size, Vector3 p_position) { + ERR_FAIL_COND(!spatial_node); + + CubeMesh cubem; + cubem.set_size(p_size); + + Array arrays = cubem.surface_get_arrays(0); + PackedVector3Array vertex = arrays[VS::ARRAY_VERTEX]; + Vector3 *w = vertex.ptrw(); + + for (int i = 0; i < vertex.size(); ++i) { + w[i] += p_position; + } + + arrays[VS::ARRAY_VERTEX] = vertex; + + Ref m = memnew(ArrayMesh); + m->add_surface_from_arrays(cubem.surface_get_primitive_type(0), arrays); + m->surface_set_material(0, p_material); + add_mesh(m); +} + +bool EditorNode3DGizmo::intersect_frustum(const Camera3D *p_camera, const Vector &p_frustum) { + + ERR_FAIL_COND_V(!spatial_node, false); + ERR_FAIL_COND_V(!valid, false); + + if (hidden && !gizmo_plugin->is_selectable_when_hidden()) return false; + + if (selectable_icon_size > 0.0f) { + Vector3 origin = spatial_node->get_global_transform().get_origin(); + + const Plane *p = p_frustum.ptr(); + int fc = p_frustum.size(); + + bool any_out = false; + + for (int j = 0; j < fc; j++) { + + if (p[j].is_point_over(origin)) { + any_out = true; + break; + } + } + + return !any_out; + } + + if (collision_segments.size()) { + + const Plane *p = p_frustum.ptr(); + int fc = p_frustum.size(); + + int vc = collision_segments.size(); + const Vector3 *vptr = collision_segments.ptr(); + Transform t = spatial_node->get_global_transform(); + + bool any_out = false; + for (int j = 0; j < fc; j++) { + for (int i = 0; i < vc; i++) { + Vector3 v = t.xform(vptr[i]); + if (p[j].is_point_over(v)) { + any_out = true; + break; + } + } + if (any_out) break; + } + + if (!any_out) return true; + } + + if (collision_mesh.is_valid()) { + Transform t = spatial_node->get_global_transform(); + + Vector3 mesh_scale = t.get_basis().get_scale(); + t.orthonormalize(); + + Transform it = t.affine_inverse(); + + Vector transformed_frustum; + + for (int i = 0; i < 4; i++) { + transformed_frustum.push_back(it.xform(p_frustum[i])); + } + + if (collision_mesh->inside_convex_shape(transformed_frustum.ptr(), transformed_frustum.size(), mesh_scale)) { + return true; + } + } + + return false; +} + +bool EditorNode3DGizmo::intersect_ray(Camera3D *p_camera, const Point2 &p_point, Vector3 &r_pos, Vector3 &r_normal, int *r_gizmo_handle, bool p_sec_first) { + + ERR_FAIL_COND_V(!spatial_node, false); + ERR_FAIL_COND_V(!valid, false); + + if (hidden && !gizmo_plugin->is_selectable_when_hidden()) return false; + + if (r_gizmo_handle && !hidden) { + + Transform t = spatial_node->get_global_transform(); + if (billboard_handle) { + t.set_look_at(t.origin, t.origin - p_camera->get_transform().basis.get_axis(2), p_camera->get_transform().basis.get_axis(1)); + } + + float min_d = 1e20; + int idx = -1; + + for (int i = 0; i < secondary_handles.size(); i++) { + + Vector3 hpos = t.xform(secondary_handles[i]); + Vector2 p = p_camera->unproject_position(hpos); + + if (p.distance_to(p_point) < HANDLE_HALF_SIZE) { + + real_t dp = p_camera->get_transform().origin.distance_to(hpos); + if (dp < min_d) { + + r_pos = t.xform(hpos); + r_normal = p_camera->get_transform().basis.get_axis(2); + min_d = dp; + idx = i + handles.size(); + } + } + } + + if (p_sec_first && idx != -1) { + + *r_gizmo_handle = idx; + return true; + } + + min_d = 1e20; + + for (int i = 0; i < handles.size(); i++) { + + Vector3 hpos = t.xform(handles[i]); + Vector2 p = p_camera->unproject_position(hpos); + + if (p.distance_to(p_point) < HANDLE_HALF_SIZE) { + + real_t dp = p_camera->get_transform().origin.distance_to(hpos); + if (dp < min_d) { + + r_pos = t.xform(hpos); + r_normal = p_camera->get_transform().basis.get_axis(2); + min_d = dp; + idx = i; + } + } + } + + if (idx >= 0) { + *r_gizmo_handle = idx; + return true; + } + } + + if (selectable_icon_size > 0.0f) { + + Transform t = spatial_node->get_global_transform(); + Vector3 camera_position = p_camera->get_camera_transform().origin; + if (camera_position.distance_squared_to(t.origin) > 0.01) { + t.set_look_at(t.origin, camera_position, Vector3(0, 1, 0)); + } + + float scale = t.origin.distance_to(p_camera->get_camera_transform().origin); + + if (p_camera->get_projection() == Camera3D::PROJECTION_ORTHOGONAL) { + float aspect = p_camera->get_viewport()->get_visible_rect().size.aspect(); + float size = p_camera->get_size(); + scale = size / aspect; + } + + Point2 center = p_camera->unproject_position(t.origin); + + Transform orig_camera_transform = p_camera->get_camera_transform(); + + if (orig_camera_transform.origin.distance_squared_to(t.origin) > 0.01 && + ABS(orig_camera_transform.basis.get_axis(Vector3::AXIS_Z).dot(Vector3(0, 1, 0))) < 0.99) { + p_camera->look_at(t.origin, Vector3(0, 1, 0)); + } + + Vector3 c0 = t.xform(Vector3(selectable_icon_size, selectable_icon_size, 0) * scale); + Vector3 c1 = t.xform(Vector3(-selectable_icon_size, -selectable_icon_size, 0) * scale); + + Point2 p0 = p_camera->unproject_position(c0); + Point2 p1 = p_camera->unproject_position(c1); + + p_camera->set_global_transform(orig_camera_transform); + + Rect2 rect(p0, (p1 - p0).abs()); + + rect.set_position(center - rect.get_size() / 2.0); + + if (rect.has_point(p_point)) { + r_pos = t.origin; + r_normal = -p_camera->project_ray_normal(p_point); + return true; + } + + return false; + } + + if (collision_segments.size()) { + + Plane camp(p_camera->get_transform().origin, (-p_camera->get_transform().basis.get_axis(2)).normalized()); + + int vc = collision_segments.size(); + const Vector3 *vptr = collision_segments.ptr(); + Transform t = spatial_node->get_global_transform(); + if (billboard_handle) { + t.set_look_at(t.origin, t.origin - p_camera->get_transform().basis.get_axis(2), p_camera->get_transform().basis.get_axis(1)); + } + + Vector3 cp; + float cpd = 1e20; + + for (int i = 0; i < vc / 2; i++) { + + Vector3 a = t.xform(vptr[i * 2 + 0]); + Vector3 b = t.xform(vptr[i * 2 + 1]); + Vector2 s[2]; + s[0] = p_camera->unproject_position(a); + s[1] = p_camera->unproject_position(b); + + Vector2 p = Geometry::get_closest_point_to_segment_2d(p_point, s); + + float pd = p.distance_to(p_point); + + if (pd < cpd) { + + float d = s[0].distance_to(s[1]); + Vector3 tcp; + if (d > 0) { + + float d2 = s[0].distance_to(p) / d; + tcp = a + (b - a) * d2; + + } else { + tcp = a; + } + + if (camp.distance_to(tcp) < p_camera->get_znear()) + continue; + cp = tcp; + cpd = pd; + } + } + + if (cpd < 8) { + + r_pos = cp; + r_normal = -p_camera->project_ray_normal(p_point); + return true; + } + + return false; + } + + if (collision_mesh.is_valid()) { + Transform gt = spatial_node->get_global_transform(); + + if (billboard_handle) { + gt.set_look_at(gt.origin, gt.origin - p_camera->get_transform().basis.get_axis(2), p_camera->get_transform().basis.get_axis(1)); + } + + Transform ai = gt.affine_inverse(); + Vector3 ray_from = ai.xform(p_camera->project_ray_origin(p_point)); + Vector3 ray_dir = ai.basis.xform(p_camera->project_ray_normal(p_point)).normalized(); + Vector3 rpos, rnorm; + + if (collision_mesh->intersect_ray(ray_from, ray_dir, rpos, rnorm)) { + + r_pos = gt.xform(rpos); + r_normal = gt.basis.xform(rnorm).normalized(); + return true; + } + } + + return false; +} + +void EditorNode3DGizmo::create() { + + ERR_FAIL_COND(!spatial_node); + ERR_FAIL_COND(valid); + valid = true; + + for (int i = 0; i < instances.size(); i++) { + + instances.write[i].create_instance(spatial_node, hidden); + } + + transform(); +} + +void EditorNode3DGizmo::transform() { + + ERR_FAIL_COND(!spatial_node); + ERR_FAIL_COND(!valid); + for (int i = 0; i < instances.size(); i++) { + VS::get_singleton()->instance_set_transform(instances[i].instance, spatial_node->get_global_transform()); + } +} + +void EditorNode3DGizmo::free() { + + ERR_FAIL_COND(!spatial_node); + ERR_FAIL_COND(!valid); + + for (int i = 0; i < instances.size(); i++) { + + if (instances[i].instance.is_valid()) + VS::get_singleton()->free(instances[i].instance); + instances.write[i].instance = RID(); + } + + clear(); + + valid = false; +} + +void EditorNode3DGizmo::set_hidden(bool p_hidden) { + hidden = p_hidden; + int layer = hidden ? 0 : 1 << Node3DEditorViewport::GIZMO_EDIT_LAYER; + for (int i = 0; i < instances.size(); ++i) { + VS::get_singleton()->instance_set_layer_mask(instances[i].instance, layer); + } +} + +void EditorNode3DGizmo::set_plugin(EditorNode3DGizmoPlugin *p_plugin) { + gizmo_plugin = p_plugin; +} + +void EditorNode3DGizmo::_bind_methods() { + + ClassDB::bind_method(D_METHOD("add_lines", "lines", "material", "billboard", "modulate"), &EditorNode3DGizmo::add_lines, DEFVAL(false), DEFVAL(Color(1, 1, 1))); + ClassDB::bind_method(D_METHOD("add_mesh", "mesh", "billboard", "skeleton", "material"), &EditorNode3DGizmo::add_mesh, DEFVAL(false), DEFVAL(Ref()), DEFVAL(Variant())); + ClassDB::bind_method(D_METHOD("add_collision_segments", "segments"), &EditorNode3DGizmo::add_collision_segments); + ClassDB::bind_method(D_METHOD("add_collision_triangles", "triangles"), &EditorNode3DGizmo::add_collision_triangles); + ClassDB::bind_method(D_METHOD("add_unscaled_billboard", "material", "default_scale", "modulate"), &EditorNode3DGizmo::add_unscaled_billboard, DEFVAL(1), DEFVAL(Color(1, 1, 1))); + ClassDB::bind_method(D_METHOD("add_handles", "handles", "material", "billboard", "secondary"), &EditorNode3DGizmo::add_handles, DEFVAL(false), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("set_spatial_node", "node"), &EditorNode3DGizmo::_set_spatial_node); + ClassDB::bind_method(D_METHOD("get_spatial_node"), &EditorNode3DGizmo::get_spatial_node); + ClassDB::bind_method(D_METHOD("get_plugin"), &EditorNode3DGizmo::get_plugin); + ClassDB::bind_method(D_METHOD("clear"), &EditorNode3DGizmo::clear); + ClassDB::bind_method(D_METHOD("set_hidden", "hidden"), &EditorNode3DGizmo::set_hidden); + + BIND_VMETHOD(MethodInfo("redraw")); + BIND_VMETHOD(MethodInfo(Variant::STRING, "get_handle_name", PropertyInfo(Variant::INT, "index"))); + BIND_VMETHOD(MethodInfo(Variant::BOOL, "is_handle_highlighted", PropertyInfo(Variant::INT, "index"))); + + MethodInfo hvget(Variant::NIL, "get_handle_value", PropertyInfo(Variant::INT, "index")); + hvget.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; + BIND_VMETHOD(hvget); + + BIND_VMETHOD(MethodInfo("set_handle", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Camera3D"), PropertyInfo(Variant::VECTOR2, "point"))); + MethodInfo cm = MethodInfo("commit_handle", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::NIL, "restore"), PropertyInfo(Variant::BOOL, "cancel")); + cm.default_arguments.push_back(false); + BIND_VMETHOD(cm); +} + +EditorNode3DGizmo::EditorNode3DGizmo() { + valid = false; + billboard_handle = false; + hidden = false; + base = NULL; + selected = false; + instanced = false; + spatial_node = NULL; + gizmo_plugin = NULL; + selectable_icon_size = -1.0f; +} + +EditorNode3DGizmo::~EditorNode3DGizmo() { + + if (gizmo_plugin != NULL) gizmo_plugin->unregister_gizmo(this); + clear(); +} + +Vector3 EditorNode3DGizmo::get_handle_pos(int p_idx) const { + + ERR_FAIL_INDEX_V(p_idx, handles.size(), Vector3()); + + return handles[p_idx]; +} + +//// light gizmo + +LightNode3DGizmoPlugin::LightNode3DGizmoPlugin() { + + // Enable vertex colors for the materials below as the gizmo color depends on the light color. + create_material("lines_primary", Color(1, 1, 1), false, false, true); + create_material("lines_secondary", Color(1, 1, 1, 0.35), false, false, true); + create_material("lines_billboard", Color(1, 1, 1), true, false, true); + + create_icon_material("light_directional_icon", Node3DEditor::get_singleton()->get_theme_icon("GizmoDirectionalLight", "EditorIcons")); + create_icon_material("light_omni_icon", Node3DEditor::get_singleton()->get_theme_icon("GizmoLight", "EditorIcons")); + create_icon_material("light_spot_icon", Node3DEditor::get_singleton()->get_theme_icon("GizmoSpotLight", "EditorIcons")); + + create_handle_material("handles"); + create_handle_material("handles_billboard", true); +} + +bool LightNode3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != NULL; +} + +String LightNode3DGizmoPlugin::get_name() const { + return "Lights"; +} + +int LightNode3DGizmoPlugin::get_priority() const { + return -1; +} + +String LightNode3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { + + if (p_idx == 0) + return "Radius"; + else + return "Aperture"; +} + +Variant LightNode3DGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { + + Light3D *light = Object::cast_to(p_gizmo->get_spatial_node()); + if (p_idx == 0) + return light->get_param(Light3D::PARAM_RANGE); + if (p_idx == 1) + return light->get_param(Light3D::PARAM_SPOT_ANGLE); + + return Variant(); +} + +static float _find_closest_angle_to_half_pi_arc(const Vector3 &p_from, const Vector3 &p_to, float p_arc_radius, const Transform &p_arc_xform) { + + //bleh, discrete is simpler + static const int arc_test_points = 64; + float min_d = 1e20; + Vector3 min_p; + + for (int i = 0; i < arc_test_points; i++) { + + float a = i * Math_PI * 0.5 / arc_test_points; + float an = (i + 1) * Math_PI * 0.5 / arc_test_points; + Vector3 p = Vector3(Math::cos(a), 0, -Math::sin(a)) * p_arc_radius; + Vector3 n = Vector3(Math::cos(an), 0, -Math::sin(an)) * p_arc_radius; + + Vector3 ra, rb; + Geometry::get_closest_points_between_segments(p, n, p_from, p_to, ra, rb); + + float d = ra.distance_to(rb); + if (d < min_d) { + min_d = d; + min_p = ra; + } + } + + //min_p = p_arc_xform.affine_inverse().xform(min_p); + float a = (Math_PI * 0.5) - Vector2(min_p.x, -min_p.z).angle(); + return a * 180.0 / Math_PI; +} + +void LightNode3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) { + + Light3D *light = Object::cast_to(p_gizmo->get_spatial_node()); + Transform gt = light->get_global_transform(); + Transform gi = gt.affine_inverse(); + + Vector3 ray_from = p_camera->project_ray_origin(p_point); + Vector3 ray_dir = p_camera->project_ray_normal(p_point); + + Vector3 s[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 4096) }; + if (p_idx == 0) { + + if (Object::cast_to(light)) { + Vector3 ra, rb; + Geometry::get_closest_points_between_segments(Vector3(), Vector3(0, 0, -4096), s[0], s[1], ra, rb); + + float d = -ra.z; + if (Node3DEditor::get_singleton()->is_snap_enabled()) { + d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); + } + + if (d <= 0) // Equal is here for negative zero. + d = 0; + + light->set_param(Light3D::PARAM_RANGE, d); + } else if (Object::cast_to(light)) { + + Plane cp = Plane(gt.origin, p_camera->get_transform().basis.get_axis(2)); + + Vector3 inters; + if (cp.intersects_ray(ray_from, ray_dir, &inters)) { + + float r = inters.distance_to(gt.origin); + if (Node3DEditor::get_singleton()->is_snap_enabled()) { + r = Math::stepify(r, Node3DEditor::get_singleton()->get_translate_snap()); + } + + light->set_param(Light3D::PARAM_RANGE, r); + } + } + + } else if (p_idx == 1) { + + float a = _find_closest_angle_to_half_pi_arc(s[0], s[1], light->get_param(Light3D::PARAM_RANGE), gt); + light->set_param(Light3D::PARAM_SPOT_ANGLE, CLAMP(a, 0.01, 89.99)); + } +} + +void LightNode3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { + + Light3D *light = Object::cast_to(p_gizmo->get_spatial_node()); + if (p_cancel) { + + light->set_param(p_idx == 0 ? Light3D::PARAM_RANGE : Light3D::PARAM_SPOT_ANGLE, p_restore); + + } else if (p_idx == 0) { + + UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Change Light Radius")); + ur->add_do_method(light, "set_param", Light3D::PARAM_RANGE, light->get_param(Light3D::PARAM_RANGE)); + ur->add_undo_method(light, "set_param", Light3D::PARAM_RANGE, p_restore); + ur->commit_action(); + } else if (p_idx == 1) { + + UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Change Light Radius")); + ur->add_do_method(light, "set_param", Light3D::PARAM_SPOT_ANGLE, light->get_param(Light3D::PARAM_SPOT_ANGLE)); + ur->add_undo_method(light, "set_param", Light3D::PARAM_SPOT_ANGLE, p_restore); + ur->commit_action(); + } +} + +void LightNode3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + + Light3D *light = Object::cast_to(p_gizmo->get_spatial_node()); + + Color color = light->get_color(); + // Make the gizmo color as bright as possible for better visibility + color.set_hsv(color.get_h(), color.get_s(), 1); + + p_gizmo->clear(); + + if (Object::cast_to(light)) { + + Ref material = get_material("lines_primary", p_gizmo); + Ref icon = get_material("light_directional_icon", p_gizmo); + + const int arrow_points = 7; + const float arrow_length = 1.5; + + Vector3 arrow[arrow_points] = { + Vector3(0, 0, -1), + Vector3(0, 0.8, 0), + Vector3(0, 0.3, 0), + Vector3(0, 0.3, arrow_length), + Vector3(0, -0.3, arrow_length), + Vector3(0, -0.3, 0), + Vector3(0, -0.8, 0) + }; + + int arrow_sides = 2; + + Vector lines; + + for (int i = 0; i < arrow_sides; i++) { + for (int j = 0; j < arrow_points; j++) { + Basis ma(Vector3(0, 0, 1), Math_PI * i / arrow_sides); + + Vector3 v1 = arrow[j] - Vector3(0, 0, arrow_length); + Vector3 v2 = arrow[(j + 1) % arrow_points] - Vector3(0, 0, arrow_length); + + lines.push_back(ma.xform(v1)); + lines.push_back(ma.xform(v2)); + } + } + + p_gizmo->add_lines(lines, material, false, color); + p_gizmo->add_unscaled_billboard(icon, 0.05, color); + } + + if (Object::cast_to(light)) { + + // Use both a billboard circle and 3 non-billboard circles for a better sphere-like representation + const Ref lines_material = get_material("lines_secondary", p_gizmo); + const Ref lines_billboard_material = get_material("lines_billboard", p_gizmo); + const Ref icon = get_material("light_omni_icon", p_gizmo); + + OmniLight3D *on = Object::cast_to(light); + const float r = on->get_param(Light3D::PARAM_RANGE); + Vector points; + Vector points_billboard; + + for (int i = 0; i < 120; i++) { + + // Create a circle + const float ra = Math::deg2rad((float)(i * 3)); + const float rb = Math::deg2rad((float)((i + 1) * 3)); + const Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r; + const Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r; + + // Draw axis-aligned circles + points.push_back(Vector3(a.x, 0, a.y)); + points.push_back(Vector3(b.x, 0, b.y)); + points.push_back(Vector3(0, a.x, a.y)); + points.push_back(Vector3(0, b.x, b.y)); + points.push_back(Vector3(a.x, a.y, 0)); + points.push_back(Vector3(b.x, b.y, 0)); + + // Draw a billboarded circle + points_billboard.push_back(Vector3(a.x, a.y, 0)); + points_billboard.push_back(Vector3(b.x, b.y, 0)); + } + + p_gizmo->add_lines(points, lines_material, true, color); + p_gizmo->add_lines(points_billboard, lines_billboard_material, true, color); + p_gizmo->add_unscaled_billboard(icon, 0.05, color); + + Vector handles; + handles.push_back(Vector3(r, 0, 0)); + p_gizmo->add_handles(handles, get_material("handles_billboard"), true); + } + + if (Object::cast_to(light)) { + + const Ref material_primary = get_material("lines_primary", p_gizmo); + const Ref material_secondary = get_material("lines_secondary", p_gizmo); + const Ref icon = get_material("light_spot_icon", p_gizmo); + + Vector points_primary; + Vector points_secondary; + SpotLight3D *sl = Object::cast_to(light); + + float r = sl->get_param(Light3D::PARAM_RANGE); + float w = r * Math::sin(Math::deg2rad(sl->get_param(Light3D::PARAM_SPOT_ANGLE))); + float d = r * Math::cos(Math::deg2rad(sl->get_param(Light3D::PARAM_SPOT_ANGLE))); + + for (int i = 0; i < 120; i++) { + + // Draw a circle + const float ra = Math::deg2rad((float)(i * 3)); + const float rb = Math::deg2rad((float)((i + 1) * 3)); + const Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * w; + const Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * w; + + points_primary.push_back(Vector3(a.x, a.y, -d)); + points_primary.push_back(Vector3(b.x, b.y, -d)); + + if (i % 15 == 0) { + // Draw 8 lines from the cone origin to the sides of the circle + points_secondary.push_back(Vector3(a.x, a.y, -d)); + points_secondary.push_back(Vector3()); + } + } + + points_primary.push_back(Vector3(0, 0, -r)); + points_primary.push_back(Vector3()); + + p_gizmo->add_lines(points_primary, material_primary, false, color); + p_gizmo->add_lines(points_secondary, material_secondary, false, color); + + const float ra = 16 * Math_PI * 2.0 / 64.0; + const Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * w; + + Vector handles; + handles.push_back(Vector3(0, 0, -r)); + handles.push_back(Vector3(a.x, a.y, -d)); + + p_gizmo->add_handles(handles, get_material("handles")); + p_gizmo->add_unscaled_billboard(icon, 0.05, color); + } +} + +////// + +//// player gizmo +AudioStreamPlayer3DNode3DGizmoPlugin::AudioStreamPlayer3DNode3DGizmoPlugin() { + + Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/stream_player_3d", Color(0.4, 0.8, 1)); + + create_icon_material("stream_player_3d_icon", Node3DEditor::get_singleton()->get_theme_icon("Gizmo3DSamplePlayer", "EditorIcons")); + create_material("stream_player_3d_material_primary", gizmo_color); + create_material("stream_player_3d_material_secondary", gizmo_color * Color(1, 1, 1, 0.35)); + create_handle_material("handles"); +} + +bool AudioStreamPlayer3DNode3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != NULL; +} + +String AudioStreamPlayer3DNode3DGizmoPlugin::get_name() const { + return "AudioStreamPlayer3D"; +} + +int AudioStreamPlayer3DNode3DGizmoPlugin::get_priority() const { + return -1; +} + +String AudioStreamPlayer3DNode3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { + + return "Emission Radius"; +} + +Variant AudioStreamPlayer3DNode3DGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { + AudioStreamPlayer3D *player = Object::cast_to(p_gizmo->get_spatial_node()); + return player->get_emission_angle(); +} + +void AudioStreamPlayer3DNode3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) { + + AudioStreamPlayer3D *player = Object::cast_to(p_gizmo->get_spatial_node()); + + Transform gt = player->get_global_transform(); + Transform gi = gt.affine_inverse(); + + Vector3 ray_from = p_camera->project_ray_origin(p_point); + Vector3 ray_dir = p_camera->project_ray_normal(p_point); + Vector3 ray_to = ray_from + ray_dir * 4096; + + ray_from = gi.xform(ray_from); + ray_to = gi.xform(ray_to); + + float closest_dist = 1e20; + float closest_angle = 1e20; + + for (int i = 0; i < 180; i++) { + + float a = i * Math_PI / 180.0; + float an = (i + 1) * Math_PI / 180.0; + + Vector3 from(Math::sin(a), 0, -Math::cos(a)); + Vector3 to(Math::sin(an), 0, -Math::cos(an)); + + Vector3 r1, r2; + Geometry::get_closest_points_between_segments(from, to, ray_from, ray_to, r1, r2); + float d = r1.distance_to(r2); + if (d < closest_dist) { + closest_dist = d; + closest_angle = i; + } + } + + if (closest_angle < 91) { + player->set_emission_angle(closest_angle); + } +} + +void AudioStreamPlayer3DNode3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { + + AudioStreamPlayer3D *player = Object::cast_to(p_gizmo->get_spatial_node()); + + if (p_cancel) { + + player->set_emission_angle(p_restore); + + } else { + + UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Change AudioStreamPlayer3D Emission Angle")); + ur->add_do_method(player, "set_emission_angle", player->get_emission_angle()); + ur->add_undo_method(player, "set_emission_angle", p_restore); + ur->commit_action(); + } +} + +void AudioStreamPlayer3DNode3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + + const AudioStreamPlayer3D *player = Object::cast_to(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); + + const Ref icon = get_material("stream_player_3d_icon", p_gizmo); + + if (player->is_emission_angle_enabled()) { + + const float pc = player->get_emission_angle(); + const float ofs = -Math::cos(Math::deg2rad(pc)); + const float radius = Math::sin(Math::deg2rad(pc)); + + Vector points_primary; + points_primary.resize(200); + + for (int i = 0; i < 100; i++) { + + const float a = i * 2.0 * Math_PI / 100.0; + const float an = (i + 1) * 2.0 * Math_PI / 100.0; + + const Vector3 from(Math::sin(a) * radius, Math::cos(a) * radius, ofs); + const Vector3 to(Math::sin(an) * radius, Math::cos(an) * radius, ofs); + + points_primary.write[i * 2 + 0] = from; + points_primary.write[i * 2 + 1] = to; + } + + const Ref material_primary = get_material("stream_player_3d_material_primary", p_gizmo); + p_gizmo->add_lines(points_primary, material_primary); + + Vector points_secondary; + points_secondary.resize(16); + + for (int i = 0; i < 8; i++) { + + const float a = i * 2.0 * Math_PI / 8.0; + const Vector3 from(Math::sin(a) * radius, Math::cos(a) * radius, ofs); + + points_secondary.write[i * 2 + 0] = from; + points_secondary.write[i * 2 + 1] = Vector3(); + } + + const Ref material_secondary = get_material("stream_player_3d_material_secondary", p_gizmo); + p_gizmo->add_lines(points_secondary, material_secondary); + + Vector handles; + const float ha = Math::deg2rad(player->get_emission_angle()); + handles.push_back(Vector3(Math::sin(ha), 0, -Math::cos(ha))); + p_gizmo->add_handles(handles, get_material("handles")); + } + + p_gizmo->add_unscaled_billboard(icon, 0.05); +} + +////// + +CameraNode3DGizmoPlugin::CameraNode3DGizmoPlugin() { + + Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/camera", Color(0.8, 0.4, 0.8)); + + create_material("camera_material", gizmo_color); + create_handle_material("handles"); +} + +bool CameraNode3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != NULL; +} + +String CameraNode3DGizmoPlugin::get_name() const { + return "Camera3D"; +} + +int CameraNode3DGizmoPlugin::get_priority() const { + return -1; +} + +String CameraNode3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { + + Camera3D *camera = Object::cast_to(p_gizmo->get_spatial_node()); + + if (camera->get_projection() == Camera3D::PROJECTION_PERSPECTIVE) { + return "FOV"; + } else { + return "Size"; + } +} + +Variant CameraNode3DGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { + + Camera3D *camera = Object::cast_to(p_gizmo->get_spatial_node()); + + if (camera->get_projection() == Camera3D::PROJECTION_PERSPECTIVE) { + return camera->get_fov(); + } else { + + return camera->get_size(); + } +} + +void CameraNode3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) { + + Camera3D *camera = Object::cast_to(p_gizmo->get_spatial_node()); + + Transform gt = camera->get_global_transform(); + Transform gi = gt.affine_inverse(); + + Vector3 ray_from = p_camera->project_ray_origin(p_point); + Vector3 ray_dir = p_camera->project_ray_normal(p_point); + + Vector3 s[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 4096) }; + + if (camera->get_projection() == Camera3D::PROJECTION_PERSPECTIVE) { + Transform gt2 = camera->get_global_transform(); + float a = _find_closest_angle_to_half_pi_arc(s[0], s[1], 1.0, gt2); + camera->set("fov", CLAMP(a * 2.0, 1, 179)); + } else { + + Vector3 ra, rb; + Geometry::get_closest_points_between_segments(Vector3(0, 0, -1), Vector3(4096, 0, -1), s[0], s[1], ra, rb); + float d = ra.x * 2.0; + if (Node3DEditor::get_singleton()->is_snap_enabled()) { + d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); + } + + d = CLAMP(d, 0.1, 16384); + + camera->set("size", d); + } +} + +void CameraNode3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { + + Camera3D *camera = Object::cast_to(p_gizmo->get_spatial_node()); + + if (camera->get_projection() == Camera3D::PROJECTION_PERSPECTIVE) { + + if (p_cancel) { + + camera->set("fov", p_restore); + } else { + UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Change Camera FOV")); + ur->add_do_property(camera, "fov", camera->get_fov()); + ur->add_undo_property(camera, "fov", p_restore); + ur->commit_action(); + } + + } else { + + if (p_cancel) { + + camera->set("size", p_restore); + } else { + UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Change Camera Size")); + ur->add_do_property(camera, "size", camera->get_size()); + ur->add_undo_property(camera, "size", p_restore); + ur->commit_action(); + } + } +} + +void CameraNode3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + + Camera3D *camera = Object::cast_to(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); + + Vector lines; + Vector handles; + + Ref material = get_material("camera_material", p_gizmo); + +#define ADD_TRIANGLE(m_a, m_b, m_c) \ + { \ + lines.push_back(m_a); \ + lines.push_back(m_b); \ + lines.push_back(m_b); \ + lines.push_back(m_c); \ + lines.push_back(m_c); \ + lines.push_back(m_a); \ + } + +#define ADD_QUAD(m_a, m_b, m_c, m_d) \ + { \ + lines.push_back(m_a); \ + lines.push_back(m_b); \ + lines.push_back(m_b); \ + lines.push_back(m_c); \ + lines.push_back(m_c); \ + lines.push_back(m_d); \ + lines.push_back(m_d); \ + lines.push_back(m_a); \ + } + + switch (camera->get_projection()) { + + case Camera3D::PROJECTION_PERSPECTIVE: { + + // The real FOV is halved for accurate representation + float fov = camera->get_fov() / 2.0; + + Vector3 side = Vector3(Math::sin(Math::deg2rad(fov)), 0, -Math::cos(Math::deg2rad(fov))); + Vector3 nside = side; + nside.x = -nside.x; + Vector3 up = Vector3(0, side.x, 0); + + ADD_TRIANGLE(Vector3(), side + up, side - up); + ADD_TRIANGLE(Vector3(), nside + up, nside - up); + ADD_TRIANGLE(Vector3(), side + up, nside + up); + ADD_TRIANGLE(Vector3(), side - up, nside - up); + + handles.push_back(side); + side.x *= 0.25; + nside.x *= 0.25; + Vector3 tup(0, up.y * 3 / 2, side.z); + ADD_TRIANGLE(tup, side + up, nside + up); + + } break; + case Camera3D::PROJECTION_ORTHOGONAL: { + + float size = camera->get_size(); + + float hsize = size * 0.5; + Vector3 right(hsize, 0, 0); + Vector3 up(0, hsize, 0); + Vector3 back(0, 0, -1.0); + Vector3 front(0, 0, 0); + + ADD_QUAD(-up - right, -up + right, up + right, up - right); + ADD_QUAD(-up - right + back, -up + right + back, up + right + back, up - right + back); + ADD_QUAD(up + right, up + right + back, up - right + back, up - right); + ADD_QUAD(-up + right, -up + right + back, -up - right + back, -up - right); + + handles.push_back(right + back); + + right.x *= 0.25; + Vector3 tup(0, up.y * 3 / 2, back.z); + ADD_TRIANGLE(tup, right + up + back, -right + up + back); + + } break; + case Camera3D::PROJECTION_FRUSTUM: { + float hsize = camera->get_size() / 2.0; + + Vector3 side = Vector3(hsize, 0, -camera->get_znear()).normalized(); + Vector3 nside = side; + nside.x = -nside.x; + Vector3 up = Vector3(0, side.x, 0); + Vector3 offset = Vector3(camera->get_frustum_offset().x, camera->get_frustum_offset().y, 0.0); + + ADD_TRIANGLE(Vector3(), side + up + offset, side - up + offset); + ADD_TRIANGLE(Vector3(), nside + up + offset, nside - up + offset); + ADD_TRIANGLE(Vector3(), side + up + offset, nside + up + offset); + ADD_TRIANGLE(Vector3(), side - up + offset, nside - up + offset); + + side.x *= 0.25; + nside.x *= 0.25; + Vector3 tup(0, up.y * 3 / 2, side.z); + ADD_TRIANGLE(tup + offset, side + up + offset, nside + up + offset); + } + } + +#undef ADD_TRIANGLE +#undef ADD_QUAD + + p_gizmo->add_lines(lines, material); + p_gizmo->add_handles(handles, get_material("handles")); + + ClippedCamera3D *clipcam = Object::cast_to(camera); + if (clipcam) { + Node3D *parent = Object::cast_to(camera->get_parent()); + if (!parent) { + return; + } + Vector3 cam_normal = -camera->get_global_transform().basis.get_axis(Vector3::AXIS_Z).normalized(); + Vector3 cam_x = camera->get_global_transform().basis.get_axis(Vector3::AXIS_X).normalized(); + Vector3 cam_y = camera->get_global_transform().basis.get_axis(Vector3::AXIS_Y).normalized(); + Vector3 cam_pos = camera->get_global_transform().origin; + Vector3 parent_pos = parent->get_global_transform().origin; + + Plane parent_plane(parent_pos, cam_normal); + Vector3 ray_from = parent_plane.project(cam_pos); + + lines.clear(); + lines.push_back(ray_from + cam_x * 0.5 + cam_y * 0.5); + lines.push_back(ray_from + cam_x * 0.5 + cam_y * -0.5); + + lines.push_back(ray_from + cam_x * 0.5 + cam_y * -0.5); + lines.push_back(ray_from + cam_x * -0.5 + cam_y * -0.5); + + lines.push_back(ray_from + cam_x * -0.5 + cam_y * -0.5); + lines.push_back(ray_from + cam_x * -0.5 + cam_y * 0.5); + + lines.push_back(ray_from + cam_x * -0.5 + cam_y * 0.5); + lines.push_back(ray_from + cam_x * 0.5 + cam_y * 0.5); + + if (parent_plane.distance_to(cam_pos) < 0) { + lines.push_back(ray_from); + lines.push_back(cam_pos); + } + + Transform local = camera->get_global_transform().affine_inverse(); + for (int i = 0; i < lines.size(); i++) { + lines.write[i] = local.xform(lines[i]); + } + + p_gizmo->add_lines(lines, material); + } +} + +////// + +MeshInstanceNode3DGizmoPlugin::MeshInstanceNode3DGizmoPlugin() { +} + +bool MeshInstanceNode3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != NULL && Object::cast_to(p_spatial) == NULL; +} + +String MeshInstanceNode3DGizmoPlugin::get_name() const { + return "MeshInstance3D"; +} + +int MeshInstanceNode3DGizmoPlugin::get_priority() const { + return -1; +} + +bool MeshInstanceNode3DGizmoPlugin::can_be_hidden() const { + return false; +} + +void MeshInstanceNode3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + + MeshInstance3D *mesh = Object::cast_to(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); + + Ref m = mesh->get_mesh(); + + if (!m.is_valid()) + return; //none + + Ref tm = m->generate_triangle_mesh(); + if (tm.is_valid()) { + p_gizmo->add_collision_triangles(tm); + } +} + +///// +Sprite3DNode3DGizmoPlugin::Sprite3DNode3DGizmoPlugin() { +} + +bool Sprite3DNode3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != NULL; +} + +String Sprite3DNode3DGizmoPlugin::get_name() const { + return "Sprite3D"; +} + +int Sprite3DNode3DGizmoPlugin::get_priority() const { + return -1; +} + +bool Sprite3DNode3DGizmoPlugin::can_be_hidden() const { + return false; +} + +void Sprite3DNode3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + + Sprite3D *sprite = Object::cast_to(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); + + Ref tm = sprite->generate_triangle_mesh(); + if (tm.is_valid()) { + p_gizmo->add_collision_triangles(tm); + } +} + +/// + +Position3DNode3DGizmoPlugin::Position3DNode3DGizmoPlugin() { + pos3d_mesh = Ref(memnew(ArrayMesh)); + cursor_points = Vector(); + + Vector cursor_colors; + float cs = 0.25; + cursor_points.push_back(Vector3(+cs, 0, 0)); + cursor_points.push_back(Vector3(-cs, 0, 0)); + cursor_points.push_back(Vector3(0, +cs, 0)); + cursor_points.push_back(Vector3(0, -cs, 0)); + cursor_points.push_back(Vector3(0, 0, +cs)); + cursor_points.push_back(Vector3(0, 0, -cs)); + cursor_colors.push_back(EditorNode::get_singleton()->get_gui_base()->get_theme_color("axis_x_color", "Editor")); + cursor_colors.push_back(EditorNode::get_singleton()->get_gui_base()->get_theme_color("axis_x_color", "Editor")); + cursor_colors.push_back(EditorNode::get_singleton()->get_gui_base()->get_theme_color("axis_y_color", "Editor")); + cursor_colors.push_back(EditorNode::get_singleton()->get_gui_base()->get_theme_color("axis_y_color", "Editor")); + cursor_colors.push_back(EditorNode::get_singleton()->get_gui_base()->get_theme_color("axis_z_color", "Editor")); + cursor_colors.push_back(EditorNode::get_singleton()->get_gui_base()->get_theme_color("axis_z_color", "Editor")); + + Ref mat = memnew(StandardMaterial3D); + mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); + mat->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); + mat->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); + mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); + + Array d; + d.resize(VS::ARRAY_MAX); + d[Mesh::ARRAY_VERTEX] = cursor_points; + d[Mesh::ARRAY_COLOR] = cursor_colors; + pos3d_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, d); + pos3d_mesh->surface_set_material(0, mat); +} + +bool Position3DNode3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != NULL; +} + +String Position3DNode3DGizmoPlugin::get_name() const { + return "Position3D"; +} + +int Position3DNode3DGizmoPlugin::get_priority() const { + return -1; +} + +void Position3DNode3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + + p_gizmo->clear(); + p_gizmo->add_mesh(pos3d_mesh); + p_gizmo->add_collision_segments(cursor_points); +} + +///// + +SkeletonNode3DGizmoPlugin::SkeletonNode3DGizmoPlugin() { + + Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/skeleton", Color(1, 0.8, 0.4)); + create_material("skeleton_material", gizmo_color); +} + +bool SkeletonNode3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != NULL; +} + +String SkeletonNode3DGizmoPlugin::get_name() const { + return "Skeleton3D"; +} + +int SkeletonNode3DGizmoPlugin::get_priority() const { + return -1; +} + +void SkeletonNode3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + + Skeleton3D *skel = Object::cast_to(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); + + Ref material = get_material("skeleton_material", p_gizmo); + + Ref surface_tool(memnew(SurfaceTool)); + + surface_tool->begin(Mesh::PRIMITIVE_LINES); + surface_tool->set_material(material); + Vector grests; + grests.resize(skel->get_bone_count()); + + Vector bones; + Vector weights; + bones.resize(4); + weights.resize(4); + + for (int i = 0; i < 4; i++) { + bones.write[i] = 0; + weights.write[i] = 0; + } + + weights.write[0] = 1; + + AABB aabb; + + Color bonecolor = Color(1.0, 0.4, 0.4, 0.3); + Color rootcolor = Color(0.4, 1.0, 0.4, 0.1); + + for (int i_bone = 0; i_bone < skel->get_bone_count(); i_bone++) { + + int i = skel->get_process_order(i_bone); + + int parent = skel->get_bone_parent(i); + + if (parent >= 0) { + grests.write[i] = grests[parent] * skel->get_bone_rest(i); + + Vector3 v0 = grests[parent].origin; + Vector3 v1 = grests[i].origin; + Vector3 d = (v1 - v0).normalized(); + float dist = v0.distance_to(v1); + + //find closest axis + int closest = -1; + float closest_d = 0.0; + + for (int j = 0; j < 3; j++) { + float dp = Math::abs(grests[parent].basis[j].normalized().dot(d)); + if (j == 0 || dp > closest_d) + closest = j; + } + + //find closest other + Vector3 first; + Vector3 points[4]; + int pointidx = 0; + for (int j = 0; j < 3; j++) { + + bones.write[0] = parent; + surface_tool->add_bones(bones); + surface_tool->add_weights(weights); + surface_tool->add_color(rootcolor); + surface_tool->add_vertex(v0 - grests[parent].basis[j].normalized() * dist * 0.05); + surface_tool->add_bones(bones); + surface_tool->add_weights(weights); + surface_tool->add_color(rootcolor); + surface_tool->add_vertex(v0 + grests[parent].basis[j].normalized() * dist * 0.05); + + if (j == closest) + continue; + + Vector3 axis; + if (first == Vector3()) { + axis = d.cross(d.cross(grests[parent].basis[j])).normalized(); + first = axis; + } else { + axis = d.cross(first).normalized(); + } + + for (int k = 0; k < 2; k++) { + + if (k == 1) + axis = -axis; + Vector3 point = v0 + d * dist * 0.2; + point += axis * dist * 0.1; + + bones.write[0] = parent; + surface_tool->add_bones(bones); + surface_tool->add_weights(weights); + surface_tool->add_color(bonecolor); + surface_tool->add_vertex(v0); + surface_tool->add_bones(bones); + surface_tool->add_weights(weights); + surface_tool->add_color(bonecolor); + surface_tool->add_vertex(point); + + bones.write[0] = parent; + surface_tool->add_bones(bones); + surface_tool->add_weights(weights); + surface_tool->add_color(bonecolor); + surface_tool->add_vertex(point); + bones.write[0] = i; + surface_tool->add_bones(bones); + surface_tool->add_weights(weights); + surface_tool->add_color(bonecolor); + surface_tool->add_vertex(v1); + points[pointidx++] = point; + } + } + + SWAP(points[1], points[2]); + for (int j = 0; j < 4; j++) { + + bones.write[0] = parent; + surface_tool->add_bones(bones); + surface_tool->add_weights(weights); + surface_tool->add_color(bonecolor); + surface_tool->add_vertex(points[j]); + surface_tool->add_bones(bones); + surface_tool->add_weights(weights); + surface_tool->add_color(bonecolor); + surface_tool->add_vertex(points[(j + 1) % 4]); + } + + /* + bones[0]=parent; + surface_tool->add_bones(bones); + surface_tool->add_weights(weights); + surface_tool->add_color(Color(0.4,1,0.4,0.4)); + surface_tool->add_vertex(v0); + bones[0]=i; + surface_tool->add_bones(bones); + surface_tool->add_weights(weights); + surface_tool->add_color(Color(0.4,1,0.4,0.4)); + surface_tool->add_vertex(v1); +*/ + } else { + + grests.write[i] = skel->get_bone_rest(i); + bones.write[0] = i; + } + /* + Transform t = grests[i]; + t.orthonormalize(); + + for (int i=0;i<6;i++) { + + + Vector3 face_points[4]; + + for (int j=0;j<4;j++) { + + float v[3]; + v[0]=1.0; + v[1]=1-2*((j>>1)&1); + v[2]=v[1]*(1-2*(j&1)); + + for (int k=0;k<3;k++) { + + if (i<3) + face_points[j][(i+k)%3]=v[k]*(i>=3?-1:1); + else + face_points[3-j][(i+k)%3]=v[k]*(i>=3?-1:1); + } + } + + for(int j=0;j<4;j++) { + surface_tool->add_bones(bones); + surface_tool->add_weights(weights); + surface_tool->add_color(Color(1.0,0.4,0.4,0.4)); + surface_tool->add_vertex(t.xform(face_points[j]*0.04)); + surface_tool->add_bones(bones); + surface_tool->add_weights(weights); + surface_tool->add_color(Color(1.0,0.4,0.4,0.4)); + surface_tool->add_vertex(t.xform(face_points[(j+1)%4]*0.04)); + } + + } + */ + } + + Ref m = surface_tool->commit(); + p_gizmo->add_mesh(m, false, skel->register_skin(Ref())); +} + +//// + +PhysicalBoneNode3DGizmoPlugin::PhysicalBoneNode3DGizmoPlugin() { + create_material("joint_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/joint", Color(0.5, 0.8, 1))); +} + +bool PhysicalBoneNode3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != NULL; +} + +String PhysicalBoneNode3DGizmoPlugin::get_name() const { + return "PhysicalBones"; +} + +int PhysicalBoneNode3DGizmoPlugin::get_priority() const { + return -1; +} + +void PhysicalBoneNode3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + + p_gizmo->clear(); + + PhysicalBone3D *physical_bone = Object::cast_to(p_gizmo->get_spatial_node()); + + if (!physical_bone) + return; + + Skeleton3D *sk(physical_bone->find_skeleton_parent()); + if (!sk) + return; + + PhysicalBone3D *pb(sk->get_physical_bone(physical_bone->get_bone_id())); + if (!pb) + return; + + PhysicalBone3D *pbp(sk->get_physical_bone_parent(physical_bone->get_bone_id())); + if (!pbp) + return; + + Vector points; + + switch (physical_bone->get_joint_type()) { + case PhysicalBone3D::JOINT_TYPE_PIN: { + + JointNode3DGizmoPlugin::CreatePinJointGizmo(physical_bone->get_joint_offset(), points); + } break; + case PhysicalBone3D::JOINT_TYPE_CONE: { + + const PhysicalBone3D::ConeJointData *cjd(static_cast(physical_bone->get_joint_data())); + JointNode3DGizmoPlugin::CreateConeTwistJointGizmo( + physical_bone->get_joint_offset(), + physical_bone->get_global_transform() * physical_bone->get_joint_offset(), + pb->get_global_transform(), + pbp->get_global_transform(), + cjd->swing_span, + cjd->twist_span, + &points, + &points); + } break; + case PhysicalBone3D::JOINT_TYPE_HINGE: { + + const PhysicalBone3D::HingeJointData *hjd(static_cast(physical_bone->get_joint_data())); + JointNode3DGizmoPlugin::CreateHingeJointGizmo( + physical_bone->get_joint_offset(), + physical_bone->get_global_transform() * physical_bone->get_joint_offset(), + pb->get_global_transform(), + pbp->get_global_transform(), + hjd->angular_limit_lower, + hjd->angular_limit_upper, + hjd->angular_limit_enabled, + points, + &points, + &points); + } break; + case PhysicalBone3D::JOINT_TYPE_SLIDER: { + + const PhysicalBone3D::SliderJointData *sjd(static_cast(physical_bone->get_joint_data())); + JointNode3DGizmoPlugin::CreateSliderJointGizmo( + physical_bone->get_joint_offset(), + physical_bone->get_global_transform() * physical_bone->get_joint_offset(), + pb->get_global_transform(), + pbp->get_global_transform(), + sjd->angular_limit_lower, + sjd->angular_limit_upper, + sjd->linear_limit_lower, + sjd->linear_limit_upper, + points, + &points, + &points); + } break; + case PhysicalBone3D::JOINT_TYPE_6DOF: { + + const PhysicalBone3D::SixDOFJointData *sdofjd(static_cast(physical_bone->get_joint_data())); + JointNode3DGizmoPlugin::CreateGeneric6DOFJointGizmo( + physical_bone->get_joint_offset(), + + physical_bone->get_global_transform() * physical_bone->get_joint_offset(), + pb->get_global_transform(), + pbp->get_global_transform(), + + sdofjd->axis_data[0].angular_limit_lower, + sdofjd->axis_data[0].angular_limit_upper, + sdofjd->axis_data[0].linear_limit_lower, + sdofjd->axis_data[0].linear_limit_upper, + sdofjd->axis_data[0].angular_limit_enabled, + sdofjd->axis_data[0].linear_limit_enabled, + + sdofjd->axis_data[1].angular_limit_lower, + sdofjd->axis_data[1].angular_limit_upper, + sdofjd->axis_data[1].linear_limit_lower, + sdofjd->axis_data[1].linear_limit_upper, + sdofjd->axis_data[1].angular_limit_enabled, + sdofjd->axis_data[1].linear_limit_enabled, + + sdofjd->axis_data[2].angular_limit_lower, + sdofjd->axis_data[2].angular_limit_upper, + sdofjd->axis_data[2].linear_limit_lower, + sdofjd->axis_data[2].linear_limit_upper, + sdofjd->axis_data[2].angular_limit_enabled, + sdofjd->axis_data[2].linear_limit_enabled, + + points, + &points, + &points); + } break; + default: + return; + } + + Ref material = get_material("joint_material", p_gizmo); + + p_gizmo->add_collision_segments(points); + p_gizmo->add_lines(points, material); +} + +///// + +RayCastNode3DGizmoPlugin::RayCastNode3DGizmoPlugin() { + + const Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); + create_material("shape_material", gizmo_color); + const float gizmo_value = gizmo_color.get_v(); + const Color gizmo_color_disabled = Color(gizmo_value, gizmo_value, gizmo_value, 0.65); + create_material("shape_material_disabled", gizmo_color_disabled); +} + +bool RayCastNode3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != NULL; +} + +String RayCastNode3DGizmoPlugin::get_name() const { + return "RayCast"; +} + +int RayCastNode3DGizmoPlugin::get_priority() const { + return -1; +} + +void RayCastNode3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + + RayCast3D *raycast = Object::cast_to(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); + + Vector lines; + + lines.push_back(Vector3()); + lines.push_back(raycast->get_cast_to()); + + const Ref material = + get_material(raycast->is_enabled() ? "shape_material" : "shape_material_disabled", p_gizmo); + + p_gizmo->add_lines(lines, material); + p_gizmo->add_collision_segments(lines); +} + +///// + +void SpringArmNode3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + + SpringArm3D *spring_arm = Object::cast_to(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); + + Vector lines; + + lines.push_back(Vector3()); + lines.push_back(Vector3(0, 0, 1.0) * spring_arm->get_length()); + + Ref material = get_material("shape_material", p_gizmo); + + p_gizmo->add_lines(lines, material); + p_gizmo->add_collision_segments(lines); +} + +SpringArmNode3DGizmoPlugin::SpringArmNode3DGizmoPlugin() { + Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); + create_material("shape_material", gizmo_color); +} + +bool SpringArmNode3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != NULL; +} + +String SpringArmNode3DGizmoPlugin::get_name() const { + return "SpringArm"; +} + +int SpringArmNode3DGizmoPlugin::get_priority() const { + return -1; +} + +///// + +VehicleWheelNode3DGizmoPlugin::VehicleWheelNode3DGizmoPlugin() { + + Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); + create_material("shape_material", gizmo_color); +} + +bool VehicleWheelNode3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != NULL; +} + +String VehicleWheelNode3DGizmoPlugin::get_name() const { + return "VehicleWheel"; +} + +int VehicleWheelNode3DGizmoPlugin::get_priority() const { + return -1; +} + +void VehicleWheelNode3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + + VehicleWheel3D *car_wheel = Object::cast_to(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); + + Vector points; + + float r = car_wheel->get_radius(); + const int skip = 10; + for (int i = 0; i <= 360; i += skip) { + + float ra = Math::deg2rad((float)i); + float rb = Math::deg2rad((float)i + skip); + Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r; + Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r; + + points.push_back(Vector3(0, a.x, a.y)); + points.push_back(Vector3(0, b.x, b.y)); + + const int springsec = 4; + + for (int j = 0; j < springsec; j++) { + float t = car_wheel->get_suspension_rest_length() * 5; + points.push_back(Vector3(a.x, i / 360.0 * t / springsec + j * (t / springsec), a.y) * 0.2); + points.push_back(Vector3(b.x, (i + skip) / 360.0 * t / springsec + j * (t / springsec), b.y) * 0.2); + } + } + + //travel + points.push_back(Vector3(0, 0, 0)); + points.push_back(Vector3(0, car_wheel->get_suspension_rest_length(), 0)); + + //axis + points.push_back(Vector3(r * 0.2, car_wheel->get_suspension_rest_length(), 0)); + points.push_back(Vector3(-r * 0.2, car_wheel->get_suspension_rest_length(), 0)); + //axis + points.push_back(Vector3(r * 0.2, 0, 0)); + points.push_back(Vector3(-r * 0.2, 0, 0)); + + //forward line + points.push_back(Vector3(0, -r, 0)); + points.push_back(Vector3(0, -r, r * 2)); + points.push_back(Vector3(0, -r, r * 2)); + points.push_back(Vector3(r * 2 * 0.2, -r, r * 2 * 0.8)); + points.push_back(Vector3(0, -r, r * 2)); + points.push_back(Vector3(-r * 2 * 0.2, -r, r * 2 * 0.8)); + + Ref material = get_material("shape_material", p_gizmo); + + p_gizmo->add_lines(points, material); + p_gizmo->add_collision_segments(points); +} + +/////////// + +SoftBodyNode3DGizmoPlugin::SoftBodyNode3DGizmoPlugin() { + Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); + create_material("shape_material", gizmo_color); + create_handle_material("handles"); +} + +bool SoftBodyNode3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != NULL; +} + +String SoftBodyNode3DGizmoPlugin::get_name() const { + return "SoftBody"; +} + +int SoftBodyNode3DGizmoPlugin::get_priority() const { + return -1; +} + +bool SoftBodyNode3DGizmoPlugin::is_selectable_when_hidden() const { + return true; +} + +void SoftBodyNode3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + SoftBody3D *soft_body = Object::cast_to(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); + + if (!soft_body || soft_body->get_mesh().is_null()) { + return; + } + + // find mesh + + Vector lines; + + soft_body->get_mesh()->generate_debug_mesh_lines(lines); + + if (!lines.size()) { + return; + } + + Ref tm = soft_body->get_mesh()->generate_triangle_mesh(); + + Vector points; + soft_body->get_mesh()->generate_debug_mesh_indices(points); + + Ref material = get_material("shape_material", p_gizmo); + + p_gizmo->add_lines(lines, material); + p_gizmo->add_handles(points, get_material("handles")); + p_gizmo->add_collision_triangles(tm); +} + +String SoftBodyNode3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { + return "SoftBody pin point"; +} + +Variant SoftBodyNode3DGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { + SoftBody3D *soft_body = Object::cast_to(p_gizmo->get_spatial_node()); + return Variant(soft_body->is_point_pinned(p_idx)); +} + +void SoftBodyNode3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { + SoftBody3D *soft_body = Object::cast_to(p_gizmo->get_spatial_node()); + soft_body->pin_point_toggle(p_idx); +} + +bool SoftBodyNode3DGizmoPlugin::is_handle_highlighted(const EditorNode3DGizmo *p_gizmo, int idx) const { + SoftBody3D *soft_body = Object::cast_to(p_gizmo->get_spatial_node()); + return soft_body->is_point_pinned(idx); +} + +/////////// + +VisibilityNotifierGizmoPlugin::VisibilityNotifierGizmoPlugin() { + Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/visibility_notifier", Color(0.8, 0.5, 0.7)); + create_material("visibility_notifier_material", gizmo_color); + gizmo_color.a = 0.1; + create_material("visibility_notifier_solid_material", gizmo_color); + create_handle_material("handles"); +} + +bool VisibilityNotifierGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != NULL; +} + +String VisibilityNotifierGizmoPlugin::get_name() const { + return "VisibilityNotifier"; +} + +int VisibilityNotifierGizmoPlugin::get_priority() const { + return -1; +} + +String VisibilityNotifierGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { + + switch (p_idx) { + case 0: return "Size X"; + case 1: return "Size Y"; + case 2: return "Size Z"; + case 3: return "Pos X"; + case 4: return "Pos Y"; + case 5: return "Pos Z"; + } + + return ""; +} + +Variant VisibilityNotifierGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { + + VisibilityNotifier3D *notifier = Object::cast_to(p_gizmo->get_spatial_node()); + return notifier->get_aabb(); +} +void VisibilityNotifierGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) { + + VisibilityNotifier3D *notifier = Object::cast_to(p_gizmo->get_spatial_node()); + + Transform gt = notifier->get_global_transform(); + + Transform gi = gt.affine_inverse(); + + bool move = p_idx >= 3; + p_idx = p_idx % 3; + + AABB aabb = notifier->get_aabb(); + Vector3 ray_from = p_camera->project_ray_origin(p_point); + Vector3 ray_dir = p_camera->project_ray_normal(p_point); + + Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 4096) }; + + Vector3 ofs = aabb.position + aabb.size * 0.5; + + Vector3 axis; + axis[p_idx] = 1.0; + + if (move) { + + Vector3 ra, rb; + Geometry::get_closest_points_between_segments(ofs - axis * 4096, ofs + axis * 4096, sg[0], sg[1], ra, rb); + + float d = ra[p_idx]; + if (Node3DEditor::get_singleton()->is_snap_enabled()) { + d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); + } + + aabb.position[p_idx] = d - 1.0 - aabb.size[p_idx] * 0.5; + notifier->set_aabb(aabb); + + } else { + Vector3 ra, rb; + Geometry::get_closest_points_between_segments(ofs, ofs + axis * 4096, sg[0], sg[1], ra, rb); + + float d = ra[p_idx] - ofs[p_idx]; + if (Node3DEditor::get_singleton()->is_snap_enabled()) { + d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); + } + + if (d < 0.001) + d = 0.001; + //resize + aabb.position[p_idx] = (aabb.position[p_idx] + aabb.size[p_idx] * 0.5) - d; + aabb.size[p_idx] = d * 2; + notifier->set_aabb(aabb); + } +} + +void VisibilityNotifierGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { + + VisibilityNotifier3D *notifier = Object::cast_to(p_gizmo->get_spatial_node()); + + if (p_cancel) { + notifier->set_aabb(p_restore); + return; + } + + UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Change Notifier AABB")); + ur->add_do_method(notifier, "set_aabb", notifier->get_aabb()); + ur->add_undo_method(notifier, "set_aabb", p_restore); + ur->commit_action(); +} + +void VisibilityNotifierGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + + VisibilityNotifier3D *notifier = Object::cast_to(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); + + Vector lines; + AABB aabb = notifier->get_aabb(); + + for (int i = 0; i < 12; i++) { + Vector3 a, b; + aabb.get_edge(i, a, b); + lines.push_back(a); + lines.push_back(b); + } + + Vector handles; + + for (int i = 0; i < 3; i++) { + + Vector3 ax; + ax[i] = aabb.position[i] + aabb.size[i]; + ax[(i + 1) % 3] = aabb.position[(i + 1) % 3] + aabb.size[(i + 1) % 3] * 0.5; + ax[(i + 2) % 3] = aabb.position[(i + 2) % 3] + aabb.size[(i + 2) % 3] * 0.5; + handles.push_back(ax); + } + + Vector3 center = aabb.position + aabb.size * 0.5; + for (int i = 0; i < 3; i++) { + + Vector3 ax; + ax[i] = 1.0; + handles.push_back(center + ax); + lines.push_back(center); + lines.push_back(center + ax); + } + + Ref material = get_material("visibility_notifier_material", p_gizmo); + + p_gizmo->add_lines(lines, material); + p_gizmo->add_collision_segments(lines); + + if (p_gizmo->is_selected()) { + Ref solid_material = get_material("visibility_notifier_solid_material", p_gizmo); + p_gizmo->add_solid_box(solid_material, aabb.get_size(), aabb.get_position() + aabb.get_size() / 2.0); + } + + p_gizmo->add_handles(handles, get_material("handles")); +} + +//// + +CPUParticlesGizmoPlugin::CPUParticlesGizmoPlugin() { + create_icon_material("particles_icon", Node3DEditor::get_singleton()->get_theme_icon("GizmoCPUParticles", "EditorIcons")); +} + +bool CPUParticlesGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != NULL; +} + +String CPUParticlesGizmoPlugin::get_name() const { + return "CPUParticles"; +} + +int CPUParticlesGizmoPlugin::get_priority() const { + return -1; +} + +bool CPUParticlesGizmoPlugin::is_selectable_when_hidden() const { + return true; +} + +void CPUParticlesGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + Ref icon = get_material("particles_icon", p_gizmo); + p_gizmo->add_unscaled_billboard(icon, 0.05); +} + +//// + +ParticlesGizmoPlugin::ParticlesGizmoPlugin() { + Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/particles", Color(0.8, 0.7, 0.4)); + create_material("particles_material", gizmo_color); + gizmo_color.a = 0.1; + create_material("particles_solid_material", gizmo_color); + create_icon_material("particles_icon", Node3DEditor::get_singleton()->get_theme_icon("GizmoParticles", "EditorIcons")); + create_handle_material("handles"); +} + +bool ParticlesGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != NULL; +} + +String ParticlesGizmoPlugin::get_name() const { + return "Particles"; +} + +int ParticlesGizmoPlugin::get_priority() const { + return -1; +} + +bool ParticlesGizmoPlugin::is_selectable_when_hidden() const { + return true; +} + +String ParticlesGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { + + switch (p_idx) { + case 0: return "Size X"; + case 1: return "Size Y"; + case 2: return "Size Z"; + case 3: return "Pos X"; + case 4: return "Pos Y"; + case 5: return "Pos Z"; + } + + return ""; +} +Variant ParticlesGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { + + GPUParticles3D *particles = Object::cast_to(p_gizmo->get_spatial_node()); + return particles->get_visibility_aabb(); +} +void ParticlesGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) { + + GPUParticles3D *particles = Object::cast_to(p_gizmo->get_spatial_node()); + + Transform gt = particles->get_global_transform(); + Transform gi = gt.affine_inverse(); + + bool move = p_idx >= 3; + p_idx = p_idx % 3; + + AABB aabb = particles->get_visibility_aabb(); + Vector3 ray_from = p_camera->project_ray_origin(p_point); + Vector3 ray_dir = p_camera->project_ray_normal(p_point); + + Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 4096) }; + + Vector3 ofs = aabb.position + aabb.size * 0.5; + + Vector3 axis; + axis[p_idx] = 1.0; + + if (move) { + + Vector3 ra, rb; + Geometry::get_closest_points_between_segments(ofs - axis * 4096, ofs + axis * 4096, sg[0], sg[1], ra, rb); + + float d = ra[p_idx]; + if (Node3DEditor::get_singleton()->is_snap_enabled()) { + d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); + } + + aabb.position[p_idx] = d - 1.0 - aabb.size[p_idx] * 0.5; + particles->set_visibility_aabb(aabb); + + } else { + Vector3 ra, rb; + Geometry::get_closest_points_between_segments(ofs, ofs + axis * 4096, sg[0], sg[1], ra, rb); + + float d = ra[p_idx] - ofs[p_idx]; + if (Node3DEditor::get_singleton()->is_snap_enabled()) { + d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); + } + + if (d < 0.001) + d = 0.001; + //resize + aabb.position[p_idx] = (aabb.position[p_idx] + aabb.size[p_idx] * 0.5) - d; + aabb.size[p_idx] = d * 2; + particles->set_visibility_aabb(aabb); + } +} + +void ParticlesGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { + + GPUParticles3D *particles = Object::cast_to(p_gizmo->get_spatial_node()); + + if (p_cancel) { + particles->set_visibility_aabb(p_restore); + return; + } + + UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Change Particles AABB")); + ur->add_do_method(particles, "set_visibility_aabb", particles->get_visibility_aabb()); + ur->add_undo_method(particles, "set_visibility_aabb", p_restore); + ur->commit_action(); +} + +void ParticlesGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + + GPUParticles3D *particles = Object::cast_to(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); + + Vector lines; + AABB aabb = particles->get_visibility_aabb(); + + for (int i = 0; i < 12; i++) { + Vector3 a, b; + aabb.get_edge(i, a, b); + lines.push_back(a); + lines.push_back(b); + } + + Vector handles; + + for (int i = 0; i < 3; i++) { + + Vector3 ax; + ax[i] = aabb.position[i] + aabb.size[i]; + ax[(i + 1) % 3] = aabb.position[(i + 1) % 3] + aabb.size[(i + 1) % 3] * 0.5; + ax[(i + 2) % 3] = aabb.position[(i + 2) % 3] + aabb.size[(i + 2) % 3] * 0.5; + handles.push_back(ax); + } + + Vector3 center = aabb.position + aabb.size * 0.5; + for (int i = 0; i < 3; i++) { + + Vector3 ax; + ax[i] = 1.0; + handles.push_back(center + ax); + lines.push_back(center); + lines.push_back(center + ax); + } + + Ref material = get_material("particles_material", p_gizmo); + Ref icon = get_material("particles_icon", p_gizmo); + + p_gizmo->add_lines(lines, material); + + if (p_gizmo->is_selected()) { + Ref solid_material = get_material("particles_solid_material", p_gizmo); + p_gizmo->add_solid_box(solid_material, aabb.get_size(), aabb.get_position() + aabb.get_size() / 2.0); + } + + p_gizmo->add_handles(handles, get_material("handles")); + p_gizmo->add_unscaled_billboard(icon, 0.05); +} +//// + +ReflectionProbeGizmoPlugin::ReflectionProbeGizmoPlugin() { + Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/reflection_probe", Color(0.6, 1, 0.5)); + + create_material("reflection_probe_material", gizmo_color); + + gizmo_color.a = 0.5; + create_material("reflection_internal_material", gizmo_color); + + gizmo_color.a = 0.1; + create_material("reflection_probe_solid_material", gizmo_color); + + create_icon_material("reflection_probe_icon", Node3DEditor::get_singleton()->get_theme_icon("GizmoReflectionProbe", "EditorIcons")); + create_handle_material("handles"); +} + +bool ReflectionProbeGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != NULL; +} + +String ReflectionProbeGizmoPlugin::get_name() const { + return "ReflectionProbe"; +} + +int ReflectionProbeGizmoPlugin::get_priority() const { + return -1; +} + +String ReflectionProbeGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { + + switch (p_idx) { + case 0: return "Extents X"; + case 1: return "Extents Y"; + case 2: return "Extents Z"; + case 3: return "Origin X"; + case 4: return "Origin Y"; + case 5: return "Origin Z"; + } + + return ""; +} +Variant ReflectionProbeGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { + + ReflectionProbe *probe = Object::cast_to(p_gizmo->get_spatial_node()); + return AABB(probe->get_extents(), probe->get_origin_offset()); +} +void ReflectionProbeGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) { + + ReflectionProbe *probe = Object::cast_to(p_gizmo->get_spatial_node()); + Transform gt = probe->get_global_transform(); + + Transform gi = gt.affine_inverse(); + + if (p_idx < 3) { + Vector3 extents = probe->get_extents(); + + Vector3 ray_from = p_camera->project_ray_origin(p_point); + Vector3 ray_dir = p_camera->project_ray_normal(p_point); + + Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 16384) }; + + Vector3 axis; + axis[p_idx] = 1.0; + + Vector3 ra, rb; + Geometry::get_closest_points_between_segments(Vector3(), axis * 16384, sg[0], sg[1], ra, rb); + float d = ra[p_idx]; + if (Node3DEditor::get_singleton()->is_snap_enabled()) { + d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); + } + + if (d < 0.001) + d = 0.001; + + extents[p_idx] = d; + probe->set_extents(extents); + } else { + + p_idx -= 3; + + Vector3 origin = probe->get_origin_offset(); + origin[p_idx] = 0; + + Vector3 ray_from = p_camera->project_ray_origin(p_point); + Vector3 ray_dir = p_camera->project_ray_normal(p_point); + + Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 16384) }; + + Vector3 axis; + axis[p_idx] = 1.0; + + Vector3 ra, rb; + Geometry::get_closest_points_between_segments(origin - axis * 16384, origin + axis * 16384, sg[0], sg[1], ra, rb); + // Adjust the actual position to account for the gizmo handle position + float d = ra[p_idx] + 0.25; + if (Node3DEditor::get_singleton()->is_snap_enabled()) { + d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); + } + + origin[p_idx] = d; + probe->set_origin_offset(origin); + } +} + +void ReflectionProbeGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { + + ReflectionProbe *probe = Object::cast_to(p_gizmo->get_spatial_node()); + + AABB restore = p_restore; + + if (p_cancel) { + probe->set_extents(restore.position); + probe->set_origin_offset(restore.size); + return; + } + + UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Change Probe Extents")); + ur->add_do_method(probe, "set_extents", probe->get_extents()); + ur->add_do_method(probe, "set_origin_offset", probe->get_origin_offset()); + ur->add_undo_method(probe, "set_extents", restore.position); + ur->add_undo_method(probe, "set_origin_offset", restore.size); + ur->commit_action(); +} + +void ReflectionProbeGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + + ReflectionProbe *probe = Object::cast_to(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); + + Vector lines; + Vector internal_lines; + Vector3 extents = probe->get_extents(); + + AABB aabb; + aabb.position = -extents; + aabb.size = extents * 2; + + for (int i = 0; i < 12; i++) { + Vector3 a, b; + aabb.get_edge(i, a, b); + lines.push_back(a); + lines.push_back(b); + } + + for (int i = 0; i < 8; i++) { + Vector3 ep = aabb.get_endpoint(i); + internal_lines.push_back(probe->get_origin_offset()); + internal_lines.push_back(ep); + } + + Vector handles; + + for (int i = 0; i < 3; i++) { + + Vector3 ax; + ax[i] = aabb.position[i] + aabb.size[i]; + handles.push_back(ax); + } + + for (int i = 0; i < 3; i++) { + + Vector3 orig_handle = probe->get_origin_offset(); + orig_handle[i] -= 0.25; + lines.push_back(orig_handle); + handles.push_back(orig_handle); + + orig_handle[i] += 0.5; + lines.push_back(orig_handle); + } + + Ref material = get_material("reflection_probe_material", p_gizmo); + Ref material_internal = get_material("reflection_internal_material", p_gizmo); + Ref icon = get_material("reflection_probe_icon", p_gizmo); + + p_gizmo->add_lines(lines, material); + p_gizmo->add_lines(internal_lines, material_internal); + + if (p_gizmo->is_selected()) { + Ref solid_material = get_material("reflection_probe_solid_material", p_gizmo); + p_gizmo->add_solid_box(solid_material, probe->get_extents() * 2.0); + } + + p_gizmo->add_unscaled_billboard(icon, 0.05); + p_gizmo->add_handles(handles, get_material("handles")); +} + +GIProbeGizmoPlugin::GIProbeGizmoPlugin() { + Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/gi_probe", Color(0.5, 1, 0.6)); + + create_material("gi_probe_material", gizmo_color); + + // This gizmo draws a lot of lines. Use a low opacity to make it not too intrusive. + gizmo_color.a = 0.1; + create_material("gi_probe_internal_material", gizmo_color); + + gizmo_color.a = 0.05; + create_material("gi_probe_solid_material", gizmo_color); + + create_icon_material("gi_probe_icon", Node3DEditor::get_singleton()->get_theme_icon("GizmoGIProbe", "EditorIcons")); + create_handle_material("handles"); +} + +bool GIProbeGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != NULL; +} + +String GIProbeGizmoPlugin::get_name() const { + return "GIProbe"; +} + +int GIProbeGizmoPlugin::get_priority() const { + return -1; +} + +String GIProbeGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { + + switch (p_idx) { + case 0: return "Extents X"; + case 1: return "Extents Y"; + case 2: return "Extents Z"; + } + + return ""; +} +Variant GIProbeGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { + + GIProbe *probe = Object::cast_to(p_gizmo->get_spatial_node()); + return probe->get_extents(); +} +void GIProbeGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) { + + GIProbe *probe = Object::cast_to(p_gizmo->get_spatial_node()); + + Transform gt = probe->get_global_transform(); + Transform gi = gt.affine_inverse(); + + Vector3 extents = probe->get_extents(); + + Vector3 ray_from = p_camera->project_ray_origin(p_point); + Vector3 ray_dir = p_camera->project_ray_normal(p_point); + + Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 16384) }; + + Vector3 axis; + axis[p_idx] = 1.0; + + Vector3 ra, rb; + Geometry::get_closest_points_between_segments(Vector3(), axis * 16384, sg[0], sg[1], ra, rb); + float d = ra[p_idx]; + if (Node3DEditor::get_singleton()->is_snap_enabled()) { + d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); + } + + if (d < 0.001) + d = 0.001; + + extents[p_idx] = d; + probe->set_extents(extents); +} + +void GIProbeGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { + + GIProbe *probe = Object::cast_to(p_gizmo->get_spatial_node()); + + Vector3 restore = p_restore; + + if (p_cancel) { + probe->set_extents(restore); + return; + } + + UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Change Probe Extents")); + ur->add_do_method(probe, "set_extents", probe->get_extents()); + ur->add_undo_method(probe, "set_extents", restore); + ur->commit_action(); +} + +void GIProbeGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + + GIProbe *probe = Object::cast_to(p_gizmo->get_spatial_node()); + + Ref material = get_material("gi_probe_material", p_gizmo); + Ref icon = get_material("gi_probe_icon", p_gizmo); + Ref material_internal = get_material("gi_probe_internal_material", p_gizmo); + + p_gizmo->clear(); + + Vector lines; + Vector3 extents = probe->get_extents(); + + static const int subdivs[GIProbe::SUBDIV_MAX] = { 64, 128, 256, 512 }; + + AABB aabb = AABB(-extents, extents * 2); + int subdiv = subdivs[probe->get_subdiv()]; + float cell_size = aabb.get_longest_axis_size() / subdiv; + + for (int i = 0; i < 12; i++) { + Vector3 a, b; + aabb.get_edge(i, a, b); + lines.push_back(a); + lines.push_back(b); + } + + p_gizmo->add_lines(lines, material); + + lines.clear(); + + for (int i = 1; i < subdiv; i++) { + + for (int j = 0; j < 3; j++) { + + if (cell_size * i > aabb.size[j]) { + continue; + } + + Vector2 dir; + dir[j] = 1.0; + Vector2 ta, tb; + int j_n1 = (j + 1) % 3; + int j_n2 = (j + 2) % 3; + ta[j_n1] = 1.0; + tb[j_n2] = 1.0; + + for (int k = 0; k < 4; k++) { + + Vector3 from = aabb.position, to = aabb.position; + from[j] += cell_size * i; + to[j] += cell_size * i; + + if (k & 1) { + to[j_n1] += aabb.size[j_n1]; + } else { + + to[j_n2] += aabb.size[j_n2]; + } + + if (k & 2) { + from[j_n1] += aabb.size[j_n1]; + from[j_n2] += aabb.size[j_n2]; + } + + lines.push_back(from); + lines.push_back(to); + } + } + } + + p_gizmo->add_lines(lines, material_internal); + + Vector handles; + + for (int i = 0; i < 3; i++) { + + Vector3 ax; + ax[i] = aabb.position[i] + aabb.size[i]; + handles.push_back(ax); + } + + if (p_gizmo->is_selected()) { + Ref solid_material = get_material("gi_probe_solid_material", p_gizmo); + p_gizmo->add_solid_box(solid_material, aabb.get_size()); + } + + p_gizmo->add_unscaled_billboard(icon, 0.05); + p_gizmo->add_handles(handles, get_material("handles")); +} + +//// +#if 0 +BakedIndirectLightGizmoPlugin::BakedIndirectLightGizmoPlugin() { + Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/baked_indirect_light", Color(0.5, 0.6, 1)); + + create_material("baked_indirect_light_material", gizmo_color); + + gizmo_color.a = 0.1; + create_material("baked_indirect_light_internal_material", gizmo_color); + + create_icon_material("baked_indirect_light_icon", Node3DEditor::get_singleton()->get_icon("GizmoBakedLightmap", "EditorIcons")); + create_handle_material("handles"); +} + +String BakedIndirectLightGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { + + switch (p_idx) { + case 0: return "Extents X"; + case 1: return "Extents Y"; + case 2: return "Extents Z"; + } + + return ""; +} +Variant BakedIndirectLightGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { + + BakedLightmap *baker = Object::cast_to(p_gizmo->get_spatial_node()); + return baker->get_extents(); +} +void BakedIndirectLightGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera *p_camera, const Point2 &p_point) { + + BakedLightmap *baker = Object::cast_to(p_gizmo->get_spatial_node()); + + Transform gt = baker->get_global_transform(); + Transform gi = gt.affine_inverse(); + + Vector3 extents = baker->get_extents(); + + Vector3 ray_from = p_camera->project_ray_origin(p_point); + Vector3 ray_dir = p_camera->project_ray_normal(p_point); + + Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 16384) }; + + Vector3 axis; + axis[p_idx] = 1.0; + + Vector3 ra, rb; + Geometry::get_closest_points_between_segments(Vector3(), axis * 16384, sg[0], sg[1], ra, rb); + float d = ra[p_idx]; + if (Node3DEditor::get_singleton()->is_snap_enabled()) { + d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); + } + + if (d < 0.001) + d = 0.001; + + extents[p_idx] = d; + baker->set_extents(extents); +} + +void BakedIndirectLightGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { + + BakedLightmap *baker = Object::cast_to(p_gizmo->get_spatial_node()); + + Vector3 restore = p_restore; + + if (p_cancel) { + baker->set_extents(restore); + return; + } + + UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Change Probe Extents")); + ur->add_do_method(baker, "set_extents", baker->get_extents()); + ur->add_undo_method(baker, "set_extents", restore); + ur->commit_action(); +} + +bool BakedIndirectLightGizmoPlugin::has_gizmo(Spatial *p_spatial) { + return Object::cast_to(p_spatial) != NULL; +} + +String BakedIndirectLightGizmoPlugin::get_name() const { + return "BakedLightmap"; +} + +int BakedIndirectLightGizmoPlugin::get_priority() const { + return -1; +} + +void BakedIndirectLightGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + + BakedLightmap *baker = Object::cast_to(p_gizmo->get_spatial_node()); + + Ref material = get_material("baked_indirect_light_material", p_gizmo); + Ref icon = get_material("baked_indirect_light_icon", p_gizmo); + Ref material_internal = get_material("baked_indirect_light_internal_material", p_gizmo); + + p_gizmo->clear(); + + Vector lines; + Vector3 extents = baker->get_extents(); + + AABB aabb = AABB(-extents, extents * 2); + + for (int i = 0; i < 12; i++) { + Vector3 a, b; + aabb.get_edge(i, a, b); + lines.push_back(a); + lines.push_back(b); + } + + p_gizmo->add_lines(lines, material); + + Vector handles; + + for (int i = 0; i < 3; i++) { + + Vector3 ax; + ax[i] = aabb.position[i] + aabb.size[i]; + handles.push_back(ax); + } + + if (p_gizmo->is_selected()) { + p_gizmo->add_solid_box(material_internal, aabb.get_size()); + } + + p_gizmo->add_unscaled_billboard(icon, 0.05); + p_gizmo->add_handles(handles, get_material("handles")); +} +#endif +//// + +CollisionShapeNode3DGizmoPlugin::CollisionShapeNode3DGizmoPlugin() { + const Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); + create_material("shape_material", gizmo_color); + const float gizmo_value = gizmo_color.get_v(); + const Color gizmo_color_disabled = Color(gizmo_value, gizmo_value, gizmo_value, 0.65); + create_material("shape_material_disabled", gizmo_color_disabled); + create_handle_material("handles"); +} + +bool CollisionShapeNode3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != NULL; +} + +String CollisionShapeNode3DGizmoPlugin::get_name() const { + return "CollisionShape3D"; +} + +int CollisionShapeNode3DGizmoPlugin::get_priority() const { + return -1; +} + +String CollisionShapeNode3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { + + const CollisionShape3D *cs = Object::cast_to(p_gizmo->get_spatial_node()); + + Ref s = cs->get_shape(); + if (s.is_null()) + return ""; + + if (Object::cast_to(*s)) { + + return "Radius"; + } + + if (Object::cast_to(*s)) { + + return "Extents"; + } + + if (Object::cast_to(*s)) { + + return p_idx == 0 ? "Radius" : "Height"; + } + + if (Object::cast_to(*s)) { + + return p_idx == 0 ? "Radius" : "Height"; + } + + if (Object::cast_to(*s)) { + + return "Length"; + } + + return ""; +} + +Variant CollisionShapeNode3DGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { + + CollisionShape3D *cs = Object::cast_to(p_gizmo->get_spatial_node()); + + Ref s = cs->get_shape(); + if (s.is_null()) + return Variant(); + + if (Object::cast_to(*s)) { + + Ref ss = s; + return ss->get_radius(); + } + + if (Object::cast_to(*s)) { + + Ref bs = s; + return bs->get_extents(); + } + + if (Object::cast_to(*s)) { + + Ref cs2 = s; + return p_idx == 0 ? cs2->get_radius() : cs2->get_height(); + } + + if (Object::cast_to(*s)) { + + Ref cs2 = s; + return p_idx == 0 ? cs2->get_radius() : cs2->get_height(); + } + + if (Object::cast_to(*s)) { + + Ref cs2 = s; + return cs2->get_length(); + } + + return Variant(); +} +void CollisionShapeNode3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) { + + CollisionShape3D *cs = Object::cast_to(p_gizmo->get_spatial_node()); + + Ref s = cs->get_shape(); + if (s.is_null()) + return; + + Transform gt = cs->get_global_transform(); + Transform gi = gt.affine_inverse(); + + Vector3 ray_from = p_camera->project_ray_origin(p_point); + Vector3 ray_dir = p_camera->project_ray_normal(p_point); + + Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 4096) }; + + if (Object::cast_to(*s)) { + + Ref ss = s; + Vector3 ra, rb; + Geometry::get_closest_points_between_segments(Vector3(), Vector3(4096, 0, 0), sg[0], sg[1], ra, rb); + float d = ra.x; + if (Node3DEditor::get_singleton()->is_snap_enabled()) { + d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); + } + + if (d < 0.001) + d = 0.001; + + ss->set_radius(d); + } + + if (Object::cast_to(*s)) { + + Ref rs = s; + Vector3 ra, rb; + Geometry::get_closest_points_between_segments(Vector3(), Vector3(0, 0, 4096), sg[0], sg[1], ra, rb); + float d = ra.z; + if (Node3DEditor::get_singleton()->is_snap_enabled()) { + d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); + } + + if (d < 0.001) + d = 0.001; + + rs->set_length(d); + } + + if (Object::cast_to(*s)) { + + Vector3 axis; + axis[p_idx] = 1.0; + Ref bs = s; + Vector3 ra, rb; + Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); + float d = ra[p_idx]; + if (Node3DEditor::get_singleton()->is_snap_enabled()) { + d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); + } + + if (d < 0.001) + d = 0.001; + + Vector3 he = bs->get_extents(); + he[p_idx] = d; + bs->set_extents(he); + } + + if (Object::cast_to(*s)) { + + Vector3 axis; + axis[p_idx == 0 ? 0 : 2] = 1.0; + Ref cs2 = s; + Vector3 ra, rb; + Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); + float d = axis.dot(ra); + if (p_idx == 1) + d -= cs2->get_radius(); + + if (Node3DEditor::get_singleton()->is_snap_enabled()) { + d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); + } + + if (d < 0.001) + d = 0.001; + + if (p_idx == 0) + cs2->set_radius(d); + else if (p_idx == 1) + cs2->set_height(d * 2.0); + } + + if (Object::cast_to(*s)) { + + Vector3 axis; + axis[p_idx == 0 ? 0 : 1] = 1.0; + Ref cs2 = s; + Vector3 ra, rb; + Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); + float d = axis.dot(ra); + if (Node3DEditor::get_singleton()->is_snap_enabled()) { + d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); + } + + if (d < 0.001) + d = 0.001; + + if (p_idx == 0) + cs2->set_radius(d); + else if (p_idx == 1) + cs2->set_height(d * 2.0); + } +} +void CollisionShapeNode3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { + + CollisionShape3D *cs = Object::cast_to(p_gizmo->get_spatial_node()); + + Ref s = cs->get_shape(); + if (s.is_null()) + return; + + if (Object::cast_to(*s)) { + + Ref ss = s; + if (p_cancel) { + ss->set_radius(p_restore); + return; + } + + UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Change Sphere Shape Radius")); + ur->add_do_method(ss.ptr(), "set_radius", ss->get_radius()); + ur->add_undo_method(ss.ptr(), "set_radius", p_restore); + ur->commit_action(); + } + + if (Object::cast_to(*s)) { + + Ref ss = s; + if (p_cancel) { + ss->set_extents(p_restore); + return; + } + + UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Change Box Shape Extents")); + ur->add_do_method(ss.ptr(), "set_extents", ss->get_extents()); + ur->add_undo_method(ss.ptr(), "set_extents", p_restore); + ur->commit_action(); + } + + if (Object::cast_to(*s)) { + + Ref ss = s; + if (p_cancel) { + if (p_idx == 0) + ss->set_radius(p_restore); + else + ss->set_height(p_restore); + return; + } + + UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + if (p_idx == 0) { + ur->create_action(TTR("Change Capsule Shape Radius")); + ur->add_do_method(ss.ptr(), "set_radius", ss->get_radius()); + ur->add_undo_method(ss.ptr(), "set_radius", p_restore); + } else { + ur->create_action(TTR("Change Capsule Shape Height")); + ur->add_do_method(ss.ptr(), "set_height", ss->get_height()); + ur->add_undo_method(ss.ptr(), "set_height", p_restore); + } + + ur->commit_action(); + } + + if (Object::cast_to(*s)) { + + Ref ss = s; + if (p_cancel) { + if (p_idx == 0) + ss->set_radius(p_restore); + else + ss->set_height(p_restore); + return; + } + + UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + if (p_idx == 0) { + ur->create_action(TTR("Change Cylinder Shape Radius")); + ur->add_do_method(ss.ptr(), "set_radius", ss->get_radius()); + ur->add_undo_method(ss.ptr(), "set_radius", p_restore); + } else { + ur->create_action( + /// + + //////// + TTR("Change Cylinder Shape Height")); + ur->add_do_method(ss.ptr(), "set_height", ss->get_height()); + ur->add_undo_method(ss.ptr(), "set_height", p_restore); + } + + ur->commit_action(); + } + + if (Object::cast_to(*s)) { + + Ref ss = s; + if (p_cancel) { + ss->set_length(p_restore); + return; + } + + UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Change Ray Shape Length")); + ur->add_do_method(ss.ptr(), "set_length", ss->get_length()); + ur->add_undo_method(ss.ptr(), "set_length", p_restore); + ur->commit_action(); + } +} +void CollisionShapeNode3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + + CollisionShape3D *cs = Object::cast_to(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); + + Ref s = cs->get_shape(); + if (s.is_null()) + return; + + const Ref material = + get_material(!cs->is_disabled() ? "shape_material" : "shape_material_disabled", p_gizmo); + Ref handles_material = get_material("handles"); + + if (Object::cast_to(*s)) { + + Ref sp = s; + float r = sp->get_radius(); + + Vector points; + + for (int i = 0; i <= 360; i++) { + + float ra = Math::deg2rad((float)i); + float rb = Math::deg2rad((float)i + 1); + Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r; + Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r; + + points.push_back(Vector3(a.x, 0, a.y)); + points.push_back(Vector3(b.x, 0, b.y)); + points.push_back(Vector3(0, a.x, a.y)); + points.push_back(Vector3(0, b.x, b.y)); + points.push_back(Vector3(a.x, a.y, 0)); + points.push_back(Vector3(b.x, b.y, 0)); + } + + Vector collision_segments; + + for (int i = 0; i < 64; i++) { + + float ra = i * Math_PI * 2.0 / 64.0; + float rb = (i + 1) * Math_PI * 2.0 / 64.0; + Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r; + Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r; + + collision_segments.push_back(Vector3(a.x, 0, a.y)); + collision_segments.push_back(Vector3(b.x, 0, b.y)); + collision_segments.push_back(Vector3(0, a.x, a.y)); + collision_segments.push_back(Vector3(0, b.x, b.y)); + collision_segments.push_back(Vector3(a.x, a.y, 0)); + collision_segments.push_back(Vector3(b.x, b.y, 0)); + } + + p_gizmo->add_lines(points, material); + p_gizmo->add_collision_segments(collision_segments); + Vector handles; + handles.push_back(Vector3(r, 0, 0)); + p_gizmo->add_handles(handles, handles_material); + } + + if (Object::cast_to(*s)) { + + Ref bs = s; + Vector lines; + AABB aabb; + aabb.position = -bs->get_extents(); + aabb.size = aabb.position * -2; + + for (int i = 0; i < 12; i++) { + Vector3 a, b; + aabb.get_edge(i, a, b); + lines.push_back(a); + lines.push_back(b); + } + + Vector handles; + + for (int i = 0; i < 3; i++) { + + Vector3 ax; + ax[i] = bs->get_extents()[i]; + handles.push_back(ax); + } + + p_gizmo->add_lines(lines, material); + p_gizmo->add_collision_segments(lines); + p_gizmo->add_handles(handles, handles_material); + } + + if (Object::cast_to(*s)) { + + Ref cs2 = s; + float radius = cs2->get_radius(); + float height = cs2->get_height(); + + Vector points; + + Vector3 d(0, height * 0.5, 0); + for (int i = 0; i < 360; i++) { + + float ra = Math::deg2rad((float)i); + float rb = Math::deg2rad((float)i + 1); + Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius; + Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius; + + points.push_back(Vector3(a.x, 0, a.y) + d); + points.push_back(Vector3(b.x, 0, b.y) + d); + + points.push_back(Vector3(a.x, 0, a.y) - d); + points.push_back(Vector3(b.x, 0, b.y) - d); + + if (i % 90 == 0) { + + points.push_back(Vector3(a.x, 0, a.y) + d); + points.push_back(Vector3(a.x, 0, a.y) - d); + } + + Vector3 dud = i < 180 ? d : -d; + + points.push_back(Vector3(0, a.x, a.y) + dud); + points.push_back(Vector3(0, b.x, b.y) + dud); + points.push_back(Vector3(a.y, a.x, 0) + dud); + points.push_back(Vector3(b.y, b.x, 0) + dud); + } + + p_gizmo->add_lines(points, material); + + Vector collision_segments; + + for (int i = 0; i < 64; i++) { + + float ra = i * Math_PI * 2.0 / 64.0; + float rb = (i + 1) * Math_PI * 2.0 / 64.0; + Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius; + Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius; + + collision_segments.push_back(Vector3(a.x, 0, a.y) + d); + collision_segments.push_back(Vector3(b.x, 0, b.y) + d); + + collision_segments.push_back(Vector3(a.x, 0, a.y) - d); + collision_segments.push_back(Vector3(b.x, 0, b.y) - d); + + if (i % 16 == 0) { + + collision_segments.push_back(Vector3(a.x, 0, a.y) + d); + collision_segments.push_back(Vector3(a.x, 0, a.y) - d); + } + + Vector3 dud = i < 32 ? d : -d; + + collision_segments.push_back(Vector3(0, a.x, a.y) + dud); + collision_segments.push_back(Vector3(0, b.x, b.y) + dud); + collision_segments.push_back(Vector3(a.y, a.x, 0) + dud); + collision_segments.push_back(Vector3(b.y, b.x, 0) + dud); + } + + p_gizmo->add_collision_segments(collision_segments); + + Vector handles; + handles.push_back(Vector3(cs2->get_radius(), 0, 0)); + handles.push_back(Vector3(0, cs2->get_height() * 0.5 + cs2->get_radius(), 0)); + p_gizmo->add_handles(handles, handles_material); + } + + if (Object::cast_to(*s)) { + + Ref cs2 = s; + float radius = cs2->get_radius(); + float height = cs2->get_height(); + + Vector points; + + Vector3 d(0, height * 0.5, 0); + for (int i = 0; i < 360; i++) { + + float ra = Math::deg2rad((float)i); + float rb = Math::deg2rad((float)i + 1); + Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius; + Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius; + + points.push_back(Vector3(a.x, 0, a.y) + d); + points.push_back(Vector3(b.x, 0, b.y) + d); + + points.push_back(Vector3(a.x, 0, a.y) - d); + points.push_back(Vector3(b.x, 0, b.y) - d); + + if (i % 90 == 0) { + + points.push_back(Vector3(a.x, 0, a.y) + d); + points.push_back(Vector3(a.x, 0, a.y) - d); + } + } + + p_gizmo->add_lines(points, material); + + Vector collision_segments; + + for (int i = 0; i < 64; i++) { + + float ra = i * Math_PI * 2.0 / 64.0; + float rb = (i + 1) * Math_PI * 2.0 / 64.0; + Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius; + Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius; + + collision_segments.push_back(Vector3(a.x, 0, a.y) + d); + collision_segments.push_back(Vector3(b.x, 0, b.y) + d); + + collision_segments.push_back(Vector3(a.x, 0, a.y) - d); + collision_segments.push_back(Vector3(b.x, 0, b.y) - d); + + if (i % 16 == 0) { + + collision_segments.push_back(Vector3(a.x, 0, a.y) + d); + collision_segments.push_back(Vector3(a.x, 0, a.y) - d); + } + } + + p_gizmo->add_collision_segments(collision_segments); + + Vector handles; + handles.push_back(Vector3(cs2->get_radius(), 0, 0)); + handles.push_back(Vector3(0, cs2->get_height() * 0.5, 0)); + p_gizmo->add_handles(handles, handles_material); + } + + if (Object::cast_to(*s)) { + + Ref ps = s; + Plane p = ps->get_plane(); + Vector points; + + Vector3 n1 = p.get_any_perpendicular_normal(); + Vector3 n2 = p.normal.cross(n1).normalized(); + + Vector3 pface[4] = { + p.normal * p.d + n1 * 10.0 + n2 * 10.0, + p.normal * p.d + n1 * 10.0 + n2 * -10.0, + p.normal * p.d + n1 * -10.0 + n2 * -10.0, + p.normal * p.d + n1 * -10.0 + n2 * 10.0, + }; + + points.push_back(pface[0]); + points.push_back(pface[1]); + points.push_back(pface[1]); + points.push_back(pface[2]); + points.push_back(pface[2]); + points.push_back(pface[3]); + points.push_back(pface[3]); + points.push_back(pface[0]); + points.push_back(p.normal * p.d); + points.push_back(p.normal * p.d + p.normal * 3); + + p_gizmo->add_lines(points, material); + p_gizmo->add_collision_segments(points); + } + + if (Object::cast_to(*s)) { + + Vector points = Object::cast_to(*s)->get_points(); + + if (points.size() > 3) { + + Vector varr = Variant(points); + Geometry::MeshData md; + Error err = QuickHull::build(varr, md); + if (err == OK) { + Vector points2; + points2.resize(md.edges.size() * 2); + for (int i = 0; i < md.edges.size(); i++) { + points2.write[i * 2 + 0] = md.vertices[md.edges[i].a]; + points2.write[i * 2 + 1] = md.vertices[md.edges[i].b]; + } + + p_gizmo->add_lines(points2, material); + p_gizmo->add_collision_segments(points2); + } + } + } + + if (Object::cast_to(*s)) { + + Ref cs2 = s; + Ref mesh = cs2->get_debug_mesh(); + p_gizmo->add_mesh(mesh, false, Ref(), material); + p_gizmo->add_collision_segments(cs2->get_debug_mesh_lines()); + } + + if (Object::cast_to(*s)) { + + Ref rs = s; + + Vector points; + points.push_back(Vector3()); + points.push_back(Vector3(0, 0, rs->get_length())); + p_gizmo->add_lines(points, material); + p_gizmo->add_collision_segments(points); + Vector handles; + handles.push_back(Vector3(0, 0, rs->get_length())); + p_gizmo->add_handles(handles, handles_material); + } + + if (Object::cast_to(*s)) { + + Ref hms = s; + + Ref mesh = hms->get_debug_mesh(); + p_gizmo->add_mesh(mesh, false, Ref(), material); + } +} + +///// + +CollisionPolygonNode3DGizmoPlugin::CollisionPolygonNode3DGizmoPlugin() { + const Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); + create_material("shape_material", gizmo_color); + const float gizmo_value = gizmo_color.get_v(); + const Color gizmo_color_disabled = Color(gizmo_value, gizmo_value, gizmo_value, 0.65); + create_material("shape_material_disabled", gizmo_color_disabled); +} + +bool CollisionPolygonNode3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != NULL; +} + +String CollisionPolygonNode3DGizmoPlugin::get_name() const { + return "CollisionPolygon3D"; +} + +int CollisionPolygonNode3DGizmoPlugin::get_priority() const { + return -1; +} + +void CollisionPolygonNode3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + + CollisionPolygon3D *polygon = Object::cast_to(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); + + Vector points = polygon->get_polygon(); + float depth = polygon->get_depth() * 0.5; + + Vector lines; + for (int i = 0; i < points.size(); i++) { + + int n = (i + 1) % points.size(); + lines.push_back(Vector3(points[i].x, points[i].y, depth)); + lines.push_back(Vector3(points[n].x, points[n].y, depth)); + lines.push_back(Vector3(points[i].x, points[i].y, -depth)); + lines.push_back(Vector3(points[n].x, points[n].y, -depth)); + lines.push_back(Vector3(points[i].x, points[i].y, depth)); + lines.push_back(Vector3(points[i].x, points[i].y, -depth)); + } + + const Ref material = + get_material(!polygon->is_disabled() ? "shape_material" : "shape_material_disabled", p_gizmo); + + p_gizmo->add_lines(lines, material); + p_gizmo->add_collision_segments(lines); +} + +//// + +NavigationMeshNode3DGizmoPlugin::NavigationMeshNode3DGizmoPlugin() { + create_material("navigation_edge_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/navigation_edge", Color(0.5, 1, 1))); + create_material("navigation_edge_material_disabled", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/navigation_edge_disabled", Color(0.7, 0.7, 0.7))); + create_material("navigation_solid_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/navigation_solid", Color(0.5, 1, 1, 0.4))); + create_material("navigation_solid_material_disabled", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/navigation_solid_disabled", Color(0.7, 0.7, 0.7, 0.4))); +} + +bool NavigationMeshNode3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != NULL; +} + +String NavigationMeshNode3DGizmoPlugin::get_name() const { + return "NavigationRegion"; +} + +int NavigationMeshNode3DGizmoPlugin::get_priority() const { + return -1; +} + +void NavigationMeshNode3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + + NavigationRegion3D *navmesh = Object::cast_to(p_gizmo->get_spatial_node()); + + Ref edge_material = get_material("navigation_edge_material", p_gizmo); + Ref edge_material_disabled = get_material("navigation_edge_material_disabled", p_gizmo); + Ref solid_material = get_material("navigation_solid_material", p_gizmo); + Ref solid_material_disabled = get_material("navigation_solid_material_disabled", p_gizmo); + + p_gizmo->clear(); + Ref navmeshie = navmesh->get_navigation_mesh(); + if (navmeshie.is_null()) + return; + + Vector vertices = navmeshie->get_vertices(); + const Vector3 *vr = vertices.ptr(); + List faces; + for (int i = 0; i < navmeshie->get_polygon_count(); i++) { + Vector p = navmeshie->get_polygon(i); + + for (int j = 2; j < p.size(); j++) { + Face3 f; + f.vertex[0] = vr[p[0]]; + f.vertex[1] = vr[p[j - 1]]; + f.vertex[2] = vr[p[j]]; + + faces.push_back(f); + } + } + + if (faces.empty()) + return; + + Map<_EdgeKey, bool> edge_map; + Vector tmeshfaces; + tmeshfaces.resize(faces.size() * 3); + + { + Vector3 *tw = tmeshfaces.ptrw(); + int tidx = 0; + + for (List::Element *E = faces.front(); E; E = E->next()) { + + const Face3 &f = E->get(); + + for (int j = 0; j < 3; j++) { + + tw[tidx++] = f.vertex[j]; + _EdgeKey ek; + ek.from = f.vertex[j].snapped(Vector3(CMP_EPSILON, CMP_EPSILON, CMP_EPSILON)); + ek.to = f.vertex[(j + 1) % 3].snapped(Vector3(CMP_EPSILON, CMP_EPSILON, CMP_EPSILON)); + if (ek.from < ek.to) + SWAP(ek.from, ek.to); + + Map<_EdgeKey, bool>::Element *F = edge_map.find(ek); + + if (F) { + + F->get() = false; + + } else { + + edge_map[ek] = true; + } + } + } + } + Vector lines; + + for (Map<_EdgeKey, bool>::Element *E = edge_map.front(); E; E = E->next()) { + + if (E->get()) { + lines.push_back(E->key().from); + lines.push_back(E->key().to); + } + } + + Ref tmesh = memnew(TriangleMesh); + tmesh->create(tmeshfaces); + + if (lines.size()) + p_gizmo->add_lines(lines, navmesh->is_enabled() ? edge_material : edge_material_disabled); + p_gizmo->add_collision_triangles(tmesh); + Ref m = memnew(ArrayMesh); + Array a; + a.resize(Mesh::ARRAY_MAX); + a[0] = tmeshfaces; + m->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a); + m->surface_set_material(0, navmesh->is_enabled() ? solid_material : solid_material_disabled); + p_gizmo->add_mesh(m); + p_gizmo->add_collision_segments(lines); +} + +////// + +#define BODY_A_RADIUS 0.25 +#define BODY_B_RADIUS 0.27 + +Basis JointGizmosDrawer::look_body(const Transform &p_joint_transform, const Transform &p_body_transform) { + const Vector3 &p_eye(p_joint_transform.origin); + const Vector3 &p_target(p_body_transform.origin); + + Vector3 v_x, v_y, v_z; + + // Look the body with X + v_x = p_target - p_eye; + v_x.normalize(); + + v_z = v_x.cross(Vector3(0, 1, 0)); + v_z.normalize(); + + v_y = v_z.cross(v_x); + v_y.normalize(); + + Basis base; + base.set(v_x, v_y, v_z); + + // Absorb current joint transform + base = p_joint_transform.basis.inverse() * base; + + return base; +} + +Basis JointGizmosDrawer::look_body_toward(Vector3::Axis p_axis, const Transform &joint_transform, const Transform &body_transform) { + + switch (p_axis) { + case Vector3::AXIS_X: + return look_body_toward_x(joint_transform, body_transform); + case Vector3::AXIS_Y: + return look_body_toward_y(joint_transform, body_transform); + case Vector3::AXIS_Z: + return look_body_toward_z(joint_transform, body_transform); + default: + return Basis(); + } +} + +Basis JointGizmosDrawer::look_body_toward_x(const Transform &p_joint_transform, const Transform &p_body_transform) { + + const Vector3 &p_eye(p_joint_transform.origin); + const Vector3 &p_target(p_body_transform.origin); + + const Vector3 p_front(p_joint_transform.basis.get_axis(0)); + + Vector3 v_x, v_y, v_z; + + // Look the body with X + v_x = p_target - p_eye; + v_x.normalize(); + + v_y = p_front.cross(v_x); + v_y.normalize(); + + v_z = v_y.cross(p_front); + v_z.normalize(); + + // Clamp X to FRONT axis + v_x = p_front; + v_x.normalize(); + + Basis base; + base.set(v_x, v_y, v_z); + + // Absorb current joint transform + base = p_joint_transform.basis.inverse() * base; + + return base; +} + +Basis JointGizmosDrawer::look_body_toward_y(const Transform &p_joint_transform, const Transform &p_body_transform) { + + const Vector3 &p_eye(p_joint_transform.origin); + const Vector3 &p_target(p_body_transform.origin); + + const Vector3 p_up(p_joint_transform.basis.get_axis(1)); + + Vector3 v_x, v_y, v_z; + + // Look the body with X + v_x = p_target - p_eye; + v_x.normalize(); + + v_z = v_x.cross(p_up); + v_z.normalize(); + + v_x = p_up.cross(v_z); + v_x.normalize(); + + // Clamp Y to UP axis + v_y = p_up; + v_y.normalize(); + + Basis base; + base.set(v_x, v_y, v_z); + + // Absorb current joint transform + base = p_joint_transform.basis.inverse() * base; + + return base; +} + +Basis JointGizmosDrawer::look_body_toward_z(const Transform &p_joint_transform, const Transform &p_body_transform) { + + const Vector3 &p_eye(p_joint_transform.origin); + const Vector3 &p_target(p_body_transform.origin); + + const Vector3 p_lateral(p_joint_transform.basis.get_axis(2)); + + Vector3 v_x, v_y, v_z; + + // Look the body with X + v_x = p_target - p_eye; + v_x.normalize(); + + v_z = p_lateral; + v_z.normalize(); + + v_y = v_z.cross(v_x); + v_y.normalize(); + + // Clamp X to Z axis + v_x = v_y.cross(v_z); + v_x.normalize(); + + Basis base; + base.set(v_x, v_y, v_z); + + // Absorb current joint transform + base = p_joint_transform.basis.inverse() * base; + + return base; +} + +void JointGizmosDrawer::draw_circle(Vector3::Axis p_axis, real_t p_radius, const Transform &p_offset, const Basis &p_base, real_t p_limit_lower, real_t p_limit_upper, Vector &r_points, bool p_inverse) { + + if (p_limit_lower == p_limit_upper) { + + r_points.push_back(p_offset.translated(Vector3()).origin); + r_points.push_back(p_offset.translated(p_base.xform(Vector3(0.5, 0, 0))).origin); + + } else { + + if (p_limit_lower > p_limit_upper) { + p_limit_lower = -Math_PI; + p_limit_upper = Math_PI; + } + + const int points = 32; + + for (int i = 0; i < points; i++) { + + real_t s = p_limit_lower + i * (p_limit_upper - p_limit_lower) / points; + real_t n = p_limit_lower + (i + 1) * (p_limit_upper - p_limit_lower) / points; + + Vector3 from; + Vector3 to; + switch (p_axis) { + case Vector3::AXIS_X: + if (p_inverse) { + from = p_base.xform(Vector3(0, Math::sin(s), Math::cos(s))) * p_radius; + to = p_base.xform(Vector3(0, Math::sin(n), Math::cos(n))) * p_radius; + } else { + from = p_base.xform(Vector3(0, -Math::sin(s), Math::cos(s))) * p_radius; + to = p_base.xform(Vector3(0, -Math::sin(n), Math::cos(n))) * p_radius; + } + break; + case Vector3::AXIS_Y: + if (p_inverse) { + from = p_base.xform(Vector3(Math::cos(s), 0, -Math::sin(s))) * p_radius; + to = p_base.xform(Vector3(Math::cos(n), 0, -Math::sin(n))) * p_radius; + } else { + from = p_base.xform(Vector3(Math::cos(s), 0, Math::sin(s))) * p_radius; + to = p_base.xform(Vector3(Math::cos(n), 0, Math::sin(n))) * p_radius; + } + break; + case Vector3::AXIS_Z: + from = p_base.xform(Vector3(Math::cos(s), Math::sin(s), 0)) * p_radius; + to = p_base.xform(Vector3(Math::cos(n), Math::sin(n), 0)) * p_radius; + break; + } + + if (i == points - 1) { + r_points.push_back(p_offset.translated(to).origin); + r_points.push_back(p_offset.translated(Vector3()).origin); + } + if (i == 0) { + r_points.push_back(p_offset.translated(from).origin); + r_points.push_back(p_offset.translated(Vector3()).origin); + } + + r_points.push_back(p_offset.translated(from).origin); + r_points.push_back(p_offset.translated(to).origin); + } + + r_points.push_back(p_offset.translated(Vector3(0, p_radius * 1.5, 0)).origin); + r_points.push_back(p_offset.translated(Vector3()).origin); + } +} + +void JointGizmosDrawer::draw_cone(const Transform &p_offset, const Basis &p_base, real_t p_swing, real_t p_twist, Vector &r_points) { + + float r = 1.0; + float w = r * Math::sin(p_swing); + float d = r * Math::cos(p_swing); + + //swing + for (int i = 0; i < 360; i += 10) { + + float ra = Math::deg2rad((float)i); + float rb = Math::deg2rad((float)i + 10); + Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * w; + Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * w; + + r_points.push_back(p_offset.translated(p_base.xform(Vector3(d, a.x, a.y))).origin); + r_points.push_back(p_offset.translated(p_base.xform(Vector3(d, b.x, b.y))).origin); + + if (i % 90 == 0) { + + r_points.push_back(p_offset.translated(p_base.xform(Vector3(d, a.x, a.y))).origin); + r_points.push_back(p_offset.translated(p_base.xform(Vector3())).origin); + } + } + + r_points.push_back(p_offset.translated(p_base.xform(Vector3())).origin); + r_points.push_back(p_offset.translated(p_base.xform(Vector3(1, 0, 0))).origin); + + /// Twist + float ts = Math::rad2deg(p_twist); + ts = MIN(ts, 720); + + for (int i = 0; i < int(ts); i += 5) { + + float ra = Math::deg2rad((float)i); + float rb = Math::deg2rad((float)i + 5); + float c = i / 720.0; + float cn = (i + 5) / 720.0; + Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * w * c; + Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * w * cn; + + r_points.push_back(p_offset.translated(p_base.xform(Vector3(c, a.x, a.y))).origin); + r_points.push_back(p_offset.translated(p_base.xform(Vector3(cn, b.x, b.y))).origin); + } +} + +//// + +JointNode3DGizmoPlugin::JointNode3DGizmoPlugin() { + create_material("joint_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/joint", Color(0.5, 0.8, 1))); + create_material("joint_body_a_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/joint_body_a", Color(0.6, 0.8, 1))); + create_material("joint_body_b_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/joint_body_b", Color(0.6, 0.9, 1))); +} + +bool JointNode3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != NULL; +} + +String JointNode3DGizmoPlugin::get_name() const { + return "Joints"; +} + +int JointNode3DGizmoPlugin::get_priority() const { + return -1; +} + +void JointNode3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + Joint3D *joint = Object::cast_to(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); + + Node3D *node_body_a = NULL; + if (!joint->get_node_a().is_empty()) { + node_body_a = Object::cast_to(joint->get_node(joint->get_node_a())); + } + + Node3D *node_body_b = NULL; + if (!joint->get_node_b().is_empty()) { + node_body_b = Object::cast_to(joint->get_node(joint->get_node_b())); + } + + if (!node_body_a && !node_body_b) { + return; + } + + Ref common_material = get_material("joint_material", p_gizmo); + Ref body_a_material = get_material("joint_body_a_material", p_gizmo); + Ref body_b_material = get_material("joint_body_b_material", p_gizmo); + + Vector points; + Vector body_a_points; + Vector body_b_points; + + if (Object::cast_to(joint)) { + CreatePinJointGizmo(Transform(), points); + p_gizmo->add_collision_segments(points); + p_gizmo->add_lines(points, common_material); + } + + HingeJoint3D *hinge = Object::cast_to(joint); + if (hinge) { + + CreateHingeJointGizmo( + Transform(), + hinge->get_global_transform(), + node_body_a ? node_body_a->get_global_transform() : Transform(), + node_body_b ? node_body_b->get_global_transform() : Transform(), + hinge->get_param(HingeJoint3D::PARAM_LIMIT_LOWER), + hinge->get_param(HingeJoint3D::PARAM_LIMIT_UPPER), + hinge->get_flag(HingeJoint3D::FLAG_USE_LIMIT), + points, + node_body_a ? &body_a_points : NULL, + node_body_b ? &body_b_points : NULL); + + p_gizmo->add_collision_segments(points); + p_gizmo->add_collision_segments(body_a_points); + p_gizmo->add_collision_segments(body_b_points); + + p_gizmo->add_lines(points, common_material); + p_gizmo->add_lines(body_a_points, body_a_material); + p_gizmo->add_lines(body_b_points, body_b_material); + } + + SliderJoint3D *slider = Object::cast_to(joint); + if (slider) { + + CreateSliderJointGizmo( + Transform(), + slider->get_global_transform(), + node_body_a ? node_body_a->get_global_transform() : Transform(), + node_body_b ? node_body_b->get_global_transform() : Transform(), + slider->get_param(SliderJoint3D::PARAM_ANGULAR_LIMIT_LOWER), + slider->get_param(SliderJoint3D::PARAM_ANGULAR_LIMIT_UPPER), + slider->get_param(SliderJoint3D::PARAM_LINEAR_LIMIT_LOWER), + slider->get_param(SliderJoint3D::PARAM_LINEAR_LIMIT_UPPER), + points, + node_body_a ? &body_a_points : NULL, + node_body_b ? &body_b_points : NULL); + + p_gizmo->add_collision_segments(points); + p_gizmo->add_collision_segments(body_a_points); + p_gizmo->add_collision_segments(body_b_points); + + p_gizmo->add_lines(points, common_material); + p_gizmo->add_lines(body_a_points, body_a_material); + p_gizmo->add_lines(body_b_points, body_b_material); + } + + ConeTwistJoint3D *cone = Object::cast_to(joint); + if (cone) { + + CreateConeTwistJointGizmo( + Transform(), + cone->get_global_transform(), + node_body_a ? node_body_a->get_global_transform() : Transform(), + node_body_b ? node_body_b->get_global_transform() : Transform(), + cone->get_param(ConeTwistJoint3D::PARAM_SWING_SPAN), + cone->get_param(ConeTwistJoint3D::PARAM_TWIST_SPAN), + node_body_a ? &body_a_points : NULL, + node_body_b ? &body_b_points : NULL); + + p_gizmo->add_collision_segments(body_a_points); + p_gizmo->add_collision_segments(body_b_points); + + p_gizmo->add_lines(body_a_points, body_a_material); + p_gizmo->add_lines(body_b_points, body_b_material); + } + + Generic6DOFJoint3D *gen = Object::cast_to(joint); + if (gen) { + + CreateGeneric6DOFJointGizmo( + Transform(), + gen->get_global_transform(), + node_body_a ? node_body_a->get_global_transform() : Transform(), + node_body_b ? node_body_b->get_global_transform() : Transform(), + + gen->get_param_x(Generic6DOFJoint3D::PARAM_ANGULAR_LOWER_LIMIT), + gen->get_param_x(Generic6DOFJoint3D::PARAM_ANGULAR_UPPER_LIMIT), + gen->get_param_x(Generic6DOFJoint3D::PARAM_LINEAR_LOWER_LIMIT), + gen->get_param_x(Generic6DOFJoint3D::PARAM_LINEAR_UPPER_LIMIT), + gen->get_flag_x(Generic6DOFJoint3D::FLAG_ENABLE_ANGULAR_LIMIT), + gen->get_flag_x(Generic6DOFJoint3D::FLAG_ENABLE_LINEAR_LIMIT), + + gen->get_param_y(Generic6DOFJoint3D::PARAM_ANGULAR_LOWER_LIMIT), + gen->get_param_y(Generic6DOFJoint3D::PARAM_ANGULAR_UPPER_LIMIT), + gen->get_param_y(Generic6DOFJoint3D::PARAM_LINEAR_LOWER_LIMIT), + gen->get_param_y(Generic6DOFJoint3D::PARAM_LINEAR_UPPER_LIMIT), + gen->get_flag_y(Generic6DOFJoint3D::FLAG_ENABLE_ANGULAR_LIMIT), + gen->get_flag_y(Generic6DOFJoint3D::FLAG_ENABLE_LINEAR_LIMIT), + + gen->get_param_z(Generic6DOFJoint3D::PARAM_ANGULAR_LOWER_LIMIT), + gen->get_param_z(Generic6DOFJoint3D::PARAM_ANGULAR_UPPER_LIMIT), + gen->get_param_z(Generic6DOFJoint3D::PARAM_LINEAR_LOWER_LIMIT), + gen->get_param_z(Generic6DOFJoint3D::PARAM_LINEAR_UPPER_LIMIT), + gen->get_flag_z(Generic6DOFJoint3D::FLAG_ENABLE_ANGULAR_LIMIT), + gen->get_flag_z(Generic6DOFJoint3D::FLAG_ENABLE_LINEAR_LIMIT), + + points, + node_body_a ? &body_a_points : NULL, + node_body_a ? &body_b_points : NULL); + + p_gizmo->add_collision_segments(points); + p_gizmo->add_collision_segments(body_a_points); + p_gizmo->add_collision_segments(body_b_points); + + p_gizmo->add_lines(points, common_material); + p_gizmo->add_lines(body_a_points, body_a_material); + p_gizmo->add_lines(body_b_points, body_b_material); + } +} + +void JointNode3DGizmoPlugin::CreatePinJointGizmo(const Transform &p_offset, Vector &r_cursor_points) { + float cs = 0.25; + + r_cursor_points.push_back(p_offset.translated(Vector3(+cs, 0, 0)).origin); + r_cursor_points.push_back(p_offset.translated(Vector3(-cs, 0, 0)).origin); + r_cursor_points.push_back(p_offset.translated(Vector3(0, +cs, 0)).origin); + r_cursor_points.push_back(p_offset.translated(Vector3(0, -cs, 0)).origin); + r_cursor_points.push_back(p_offset.translated(Vector3(0, 0, +cs)).origin); + r_cursor_points.push_back(p_offset.translated(Vector3(0, 0, -cs)).origin); +} + +void JointNode3DGizmoPlugin::CreateHingeJointGizmo(const Transform &p_offset, const Transform &p_trs_joint, const Transform &p_trs_body_a, const Transform &p_trs_body_b, real_t p_limit_lower, real_t p_limit_upper, bool p_use_limit, Vector &r_common_points, Vector *r_body_a_points, Vector *r_body_b_points) { + + r_common_points.push_back(p_offset.translated(Vector3(0, 0, 0.5)).origin); + r_common_points.push_back(p_offset.translated(Vector3(0, 0, -0.5)).origin); + + if (!p_use_limit) { + p_limit_upper = -1; + p_limit_lower = 0; + } + + if (r_body_a_points) { + + JointGizmosDrawer::draw_circle(Vector3::AXIS_Z, + BODY_A_RADIUS, + p_offset, + JointGizmosDrawer::look_body_toward_z(p_trs_joint, p_trs_body_a), + p_limit_lower, + p_limit_upper, + *r_body_a_points); + } + + if (r_body_b_points) { + JointGizmosDrawer::draw_circle(Vector3::AXIS_Z, + BODY_B_RADIUS, + p_offset, + JointGizmosDrawer::look_body_toward_z(p_trs_joint, p_trs_body_b), + p_limit_lower, + p_limit_upper, + *r_body_b_points); + } +} + +void JointNode3DGizmoPlugin::CreateSliderJointGizmo(const Transform &p_offset, const Transform &p_trs_joint, const Transform &p_trs_body_a, const Transform &p_trs_body_b, real_t p_angular_limit_lower, real_t p_angular_limit_upper, real_t p_linear_limit_lower, real_t p_linear_limit_upper, Vector &r_points, Vector *r_body_a_points, Vector *r_body_b_points) { + + p_linear_limit_lower = -p_linear_limit_lower; + p_linear_limit_upper = -p_linear_limit_upper; + + float cs = 0.25; + r_points.push_back(p_offset.translated(Vector3(0, 0, 0.5)).origin); + r_points.push_back(p_offset.translated(Vector3(0, 0, -0.5)).origin); + + if (p_linear_limit_lower >= p_linear_limit_upper) { + + r_points.push_back(p_offset.translated(Vector3(p_linear_limit_upper, 0, 0)).origin); + r_points.push_back(p_offset.translated(Vector3(p_linear_limit_lower, 0, 0)).origin); + + r_points.push_back(p_offset.translated(Vector3(p_linear_limit_upper, -cs, -cs)).origin); + r_points.push_back(p_offset.translated(Vector3(p_linear_limit_upper, -cs, cs)).origin); + r_points.push_back(p_offset.translated(Vector3(p_linear_limit_upper, -cs, cs)).origin); + r_points.push_back(p_offset.translated(Vector3(p_linear_limit_upper, cs, cs)).origin); + r_points.push_back(p_offset.translated(Vector3(p_linear_limit_upper, cs, cs)).origin); + r_points.push_back(p_offset.translated(Vector3(p_linear_limit_upper, cs, -cs)).origin); + r_points.push_back(p_offset.translated(Vector3(p_linear_limit_upper, cs, -cs)).origin); + r_points.push_back(p_offset.translated(Vector3(p_linear_limit_upper, -cs, -cs)).origin); + + r_points.push_back(p_offset.translated(Vector3(p_linear_limit_lower, -cs, -cs)).origin); + r_points.push_back(p_offset.translated(Vector3(p_linear_limit_lower, -cs, cs)).origin); + r_points.push_back(p_offset.translated(Vector3(p_linear_limit_lower, -cs, cs)).origin); + r_points.push_back(p_offset.translated(Vector3(p_linear_limit_lower, cs, cs)).origin); + r_points.push_back(p_offset.translated(Vector3(p_linear_limit_lower, cs, cs)).origin); + r_points.push_back(p_offset.translated(Vector3(p_linear_limit_lower, cs, -cs)).origin); + r_points.push_back(p_offset.translated(Vector3(p_linear_limit_lower, cs, -cs)).origin); + r_points.push_back(p_offset.translated(Vector3(p_linear_limit_lower, -cs, -cs)).origin); + + } else { + + r_points.push_back(p_offset.translated(Vector3(+cs * 2, 0, 0)).origin); + r_points.push_back(p_offset.translated(Vector3(-cs * 2, 0, 0)).origin); + } + + if (r_body_a_points) + JointGizmosDrawer::draw_circle( + Vector3::AXIS_X, + BODY_A_RADIUS, + p_offset, + JointGizmosDrawer::look_body_toward(Vector3::AXIS_X, p_trs_joint, p_trs_body_a), + p_angular_limit_lower, + p_angular_limit_upper, + *r_body_a_points); + + if (r_body_b_points) + JointGizmosDrawer::draw_circle( + Vector3::AXIS_X, + BODY_B_RADIUS, + p_offset, + JointGizmosDrawer::look_body_toward(Vector3::AXIS_X, p_trs_joint, p_trs_body_b), + p_angular_limit_lower, + p_angular_limit_upper, + *r_body_b_points, + true); +} + +void JointNode3DGizmoPlugin::CreateConeTwistJointGizmo(const Transform &p_offset, const Transform &p_trs_joint, const Transform &p_trs_body_a, const Transform &p_trs_body_b, real_t p_swing, real_t p_twist, Vector *r_body_a_points, Vector *r_body_b_points) { + + if (r_body_a_points) + JointGizmosDrawer::draw_cone( + p_offset, + JointGizmosDrawer::look_body(p_trs_joint, p_trs_body_a), + p_swing, + p_twist, + *r_body_a_points); + + if (r_body_b_points) + JointGizmosDrawer::draw_cone( + p_offset, + JointGizmosDrawer::look_body(p_trs_joint, p_trs_body_b), + p_swing, + p_twist, + *r_body_b_points); +} + +void JointNode3DGizmoPlugin::CreateGeneric6DOFJointGizmo( + const Transform &p_offset, + const Transform &p_trs_joint, + const Transform &p_trs_body_a, + const Transform &p_trs_body_b, + real_t p_angular_limit_lower_x, + real_t p_angular_limit_upper_x, + real_t p_linear_limit_lower_x, + real_t p_linear_limit_upper_x, + bool p_enable_angular_limit_x, + bool p_enable_linear_limit_x, + real_t p_angular_limit_lower_y, + real_t p_angular_limit_upper_y, + real_t p_linear_limit_lower_y, + real_t p_linear_limit_upper_y, + bool p_enable_angular_limit_y, + bool p_enable_linear_limit_y, + real_t p_angular_limit_lower_z, + real_t p_angular_limit_upper_z, + real_t p_linear_limit_lower_z, + real_t p_linear_limit_upper_z, + bool p_enable_angular_limit_z, + bool p_enable_linear_limit_z, + Vector &r_points, + Vector *r_body_a_points, + Vector *r_body_b_points) { + + float cs = 0.25; + + for (int ax = 0; ax < 3; ax++) { + float ll = 0; + float ul = 0; + float lll = 0; + float lul = 0; + + int a1 = 0; + int a2 = 0; + int a3 = 0; + bool enable_ang = false; + bool enable_lin = false; + + switch (ax) { + case 0: + ll = p_angular_limit_lower_x; + ul = p_angular_limit_upper_x; + lll = -p_linear_limit_lower_x; + lul = -p_linear_limit_upper_x; + enable_ang = p_enable_angular_limit_x; + enable_lin = p_enable_linear_limit_x; + a1 = 0; + a2 = 1; + a3 = 2; + break; + case 1: + ll = p_angular_limit_lower_y; + ul = p_angular_limit_upper_y; + lll = -p_linear_limit_lower_y; + lul = -p_linear_limit_upper_y; + enable_ang = p_enable_angular_limit_y; + enable_lin = p_enable_linear_limit_y; + a1 = 1; + a2 = 2; + a3 = 0; + break; + case 2: + ll = p_angular_limit_lower_z; + ul = p_angular_limit_upper_z; + lll = -p_linear_limit_lower_z; + lul = -p_linear_limit_upper_z; + enable_ang = p_enable_angular_limit_z; + enable_lin = p_enable_linear_limit_z; + a1 = 2; + a2 = 0; + a3 = 1; + break; + } + +#define ADD_VTX(x, y, z) \ + { \ + Vector3 v; \ + v[a1] = (x); \ + v[a2] = (y); \ + v[a3] = (z); \ + r_points.push_back(p_offset.translated(v).origin); \ + } + + if (enable_lin && lll >= lul) { + + ADD_VTX(lul, 0, 0); + ADD_VTX(lll, 0, 0); + + ADD_VTX(lul, -cs, -cs); + ADD_VTX(lul, -cs, cs); + ADD_VTX(lul, -cs, cs); + ADD_VTX(lul, cs, cs); + ADD_VTX(lul, cs, cs); + ADD_VTX(lul, cs, -cs); + ADD_VTX(lul, cs, -cs); + ADD_VTX(lul, -cs, -cs); + + ADD_VTX(lll, -cs, -cs); + ADD_VTX(lll, -cs, cs); + ADD_VTX(lll, -cs, cs); + ADD_VTX(lll, cs, cs); + ADD_VTX(lll, cs, cs); + ADD_VTX(lll, cs, -cs); + ADD_VTX(lll, cs, -cs); + ADD_VTX(lll, -cs, -cs); + + } else { + + ADD_VTX(+cs * 2, 0, 0); + ADD_VTX(-cs * 2, 0, 0); + } + + if (!enable_ang) { + ll = 0; + ul = -1; + } + + if (r_body_a_points) + JointGizmosDrawer::draw_circle( + static_cast(ax), + BODY_A_RADIUS, + p_offset, + JointGizmosDrawer::look_body_toward(static_cast(ax), p_trs_joint, p_trs_body_a), + ll, + ul, + *r_body_a_points, + true); + + if (r_body_b_points) + JointGizmosDrawer::draw_circle( + static_cast(ax), + BODY_B_RADIUS, + p_offset, + JointGizmosDrawer::look_body_toward(static_cast(ax), p_trs_joint, p_trs_body_b), + ll, + ul, + *r_body_b_points); + } + +#undef ADD_VTX +} diff --git a/editor/node_3d_editor_gizmos.h b/editor/node_3d_editor_gizmos.h new file mode 100644 index 0000000000..014b736a0d --- /dev/null +++ b/editor/node_3d_editor_gizmos.h @@ -0,0 +1,434 @@ +/*************************************************************************/ +/* node_3d_editor_gizmos.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SPATIAL_EDITOR_GIZMOS_H +#define SPATIAL_EDITOR_GIZMOS_H + +#include "editor/plugins/node_3d_editor_plugin.h" +#include "scene/3d/camera_3d.h" + +class Camera3D; + +class LightNode3DGizmoPlugin : public EditorNode3DGizmoPlugin { + + GDCLASS(LightNode3DGizmoPlugin, EditorNode3DGizmoPlugin); + +public: + bool has_gizmo(Node3D *p_spatial); + String get_name() const; + int get_priority() const; + + String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const; + Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const; + void set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point); + void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false); + void redraw(EditorNode3DGizmo *p_gizmo); + + LightNode3DGizmoPlugin(); +}; + +class AudioStreamPlayer3DNode3DGizmoPlugin : public EditorNode3DGizmoPlugin { + + GDCLASS(AudioStreamPlayer3DNode3DGizmoPlugin, EditorNode3DGizmoPlugin); + +public: + bool has_gizmo(Node3D *p_spatial); + String get_name() const; + int get_priority() const; + + String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const; + Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const; + void set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point); + void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false); + void redraw(EditorNode3DGizmo *p_gizmo); + + AudioStreamPlayer3DNode3DGizmoPlugin(); +}; + +class CameraNode3DGizmoPlugin : public EditorNode3DGizmoPlugin { + + GDCLASS(CameraNode3DGizmoPlugin, EditorNode3DGizmoPlugin); + +public: + bool has_gizmo(Node3D *p_spatial); + String get_name() const; + int get_priority() const; + + String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const; + Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const; + void set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point); + void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false); + void redraw(EditorNode3DGizmo *p_gizmo); + + CameraNode3DGizmoPlugin(); +}; + +class MeshInstanceNode3DGizmoPlugin : public EditorNode3DGizmoPlugin { + + GDCLASS(MeshInstanceNode3DGizmoPlugin, EditorNode3DGizmoPlugin); + +public: + bool has_gizmo(Node3D *p_spatial); + String get_name() const; + int get_priority() const; + bool can_be_hidden() const; + void redraw(EditorNode3DGizmo *p_gizmo); + + MeshInstanceNode3DGizmoPlugin(); +}; + +class Sprite3DNode3DGizmoPlugin : public EditorNode3DGizmoPlugin { + + GDCLASS(Sprite3DNode3DGizmoPlugin, EditorNode3DGizmoPlugin); + +public: + bool has_gizmo(Node3D *p_spatial); + String get_name() const; + int get_priority() const; + bool can_be_hidden() const; + void redraw(EditorNode3DGizmo *p_gizmo); + + Sprite3DNode3DGizmoPlugin(); +}; + +class Position3DNode3DGizmoPlugin : public EditorNode3DGizmoPlugin { + + GDCLASS(Position3DNode3DGizmoPlugin, EditorNode3DGizmoPlugin); + + Ref pos3d_mesh; + Vector cursor_points; + +public: + bool has_gizmo(Node3D *p_spatial); + String get_name() const; + int get_priority() const; + void redraw(EditorNode3DGizmo *p_gizmo); + + Position3DNode3DGizmoPlugin(); +}; + +class SkeletonNode3DGizmoPlugin : public EditorNode3DGizmoPlugin { + + GDCLASS(SkeletonNode3DGizmoPlugin, EditorNode3DGizmoPlugin); + +public: + bool has_gizmo(Node3D *p_spatial); + String get_name() const; + int get_priority() const; + void redraw(EditorNode3DGizmo *p_gizmo); + + SkeletonNode3DGizmoPlugin(); +}; + +class PhysicalBoneNode3DGizmoPlugin : public EditorNode3DGizmoPlugin { + + GDCLASS(PhysicalBoneNode3DGizmoPlugin, EditorNode3DGizmoPlugin); + +public: + bool has_gizmo(Node3D *p_spatial); + String get_name() const; + int get_priority() const; + void redraw(EditorNode3DGizmo *p_gizmo); + + PhysicalBoneNode3DGizmoPlugin(); +}; + +class RayCastNode3DGizmoPlugin : public EditorNode3DGizmoPlugin { + + GDCLASS(RayCastNode3DGizmoPlugin, EditorNode3DGizmoPlugin); + +public: + bool has_gizmo(Node3D *p_spatial); + String get_name() const; + int get_priority() const; + void redraw(EditorNode3DGizmo *p_gizmo); + + RayCastNode3DGizmoPlugin(); +}; + +class SpringArmNode3DGizmoPlugin : public EditorNode3DGizmoPlugin { + + GDCLASS(SpringArmNode3DGizmoPlugin, EditorNode3DGizmoPlugin); + +public: + bool has_gizmo(Node3D *p_spatial); + String get_name() const; + int get_priority() const; + void redraw(EditorNode3DGizmo *p_gizmo); + + SpringArmNode3DGizmoPlugin(); +}; + +class VehicleWheelNode3DGizmoPlugin : public EditorNode3DGizmoPlugin { + + GDCLASS(VehicleWheelNode3DGizmoPlugin, EditorNode3DGizmoPlugin); + +public: + bool has_gizmo(Node3D *p_spatial); + String get_name() const; + int get_priority() const; + void redraw(EditorNode3DGizmo *p_gizmo); + + VehicleWheelNode3DGizmoPlugin(); +}; + +class SoftBodyNode3DGizmoPlugin : public EditorNode3DGizmoPlugin { + + GDCLASS(SoftBodyNode3DGizmoPlugin, EditorNode3DGizmoPlugin); + +public: + bool has_gizmo(Node3D *p_spatial); + String get_name() const; + int get_priority() const; + bool is_selectable_when_hidden() const; + void redraw(EditorNode3DGizmo *p_gizmo); + + String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const; + Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const; + void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel); + bool is_handle_highlighted(const EditorNode3DGizmo *p_gizmo, int idx) const; + + SoftBodyNode3DGizmoPlugin(); +}; + +class VisibilityNotifierGizmoPlugin : public EditorNode3DGizmoPlugin { + + GDCLASS(VisibilityNotifierGizmoPlugin, EditorNode3DGizmoPlugin); + +public: + bool has_gizmo(Node3D *p_spatial); + String get_name() const; + int get_priority() const; + void redraw(EditorNode3DGizmo *p_gizmo); + + String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const; + Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const; + void set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point); + void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false); + + VisibilityNotifierGizmoPlugin(); +}; + +class CPUParticlesGizmoPlugin : public EditorNode3DGizmoPlugin { + GDCLASS(CPUParticlesGizmoPlugin, EditorNode3DGizmoPlugin); + +public: + bool has_gizmo(Node3D *p_spatial); + String get_name() const; + int get_priority() const; + bool is_selectable_when_hidden() const; + void redraw(EditorNode3DGizmo *p_gizmo); + CPUParticlesGizmoPlugin(); +}; + +class ParticlesGizmoPlugin : public EditorNode3DGizmoPlugin { + + GDCLASS(ParticlesGizmoPlugin, EditorNode3DGizmoPlugin); + +public: + bool has_gizmo(Node3D *p_spatial); + String get_name() const; + int get_priority() const; + bool is_selectable_when_hidden() const; + void redraw(EditorNode3DGizmo *p_gizmo); + + String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const; + Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const; + void set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point); + void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false); + + ParticlesGizmoPlugin(); +}; + +class ReflectionProbeGizmoPlugin : public EditorNode3DGizmoPlugin { + + GDCLASS(ReflectionProbeGizmoPlugin, EditorNode3DGizmoPlugin); + +public: + bool has_gizmo(Node3D *p_spatial); + String get_name() const; + int get_priority() const; + void redraw(EditorNode3DGizmo *p_gizmo); + + String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const; + Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const; + void set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point); + void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false); + + ReflectionProbeGizmoPlugin(); +}; + +class GIProbeGizmoPlugin : public EditorNode3DGizmoPlugin { + + GDCLASS(GIProbeGizmoPlugin, EditorNode3DGizmoPlugin); + +public: + bool has_gizmo(Node3D *p_spatial); + String get_name() const; + int get_priority() const; + void redraw(EditorNode3DGizmo *p_gizmo); + + String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const; + Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const; + void set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point); + void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false); + + GIProbeGizmoPlugin(); +}; + +#if 0 +class BakedIndirectLightGizmoPlugin : public EditorNode3DGizmoPlugin { + + GDCLASS(BakedIndirectLightGizmoPlugin, EditorNode3DGizmoPlugin); + +public: + bool has_gizmo(Spatial *p_spatial); + String get_name() const; + int get_priority() const; + void redraw(EditorNode3DGizmo *p_gizmo); + + String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const; + Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const; + void set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera *p_camera, const Point2 &p_point); + void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false); + + BakedIndirectLightGizmoPlugin(); +}; +#endif +class CollisionShapeNode3DGizmoPlugin : public EditorNode3DGizmoPlugin { + + GDCLASS(CollisionShapeNode3DGizmoPlugin, EditorNode3DGizmoPlugin); + +public: + bool has_gizmo(Node3D *p_spatial); + String get_name() const; + int get_priority() const; + void redraw(EditorNode3DGizmo *p_gizmo); + + String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const; + Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const; + void set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point); + void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false); + + CollisionShapeNode3DGizmoPlugin(); +}; + +class CollisionPolygonNode3DGizmoPlugin : public EditorNode3DGizmoPlugin { + GDCLASS(CollisionPolygonNode3DGizmoPlugin, EditorNode3DGizmoPlugin); + +public: + bool has_gizmo(Node3D *p_spatial); + String get_name() const; + int get_priority() const; + void redraw(EditorNode3DGizmo *p_gizmo); + CollisionPolygonNode3DGizmoPlugin(); +}; + +class NavigationMeshNode3DGizmoPlugin : public EditorNode3DGizmoPlugin { + + GDCLASS(NavigationMeshNode3DGizmoPlugin, EditorNode3DGizmoPlugin); + + struct _EdgeKey { + + Vector3 from; + Vector3 to; + + bool operator<(const _EdgeKey &p_with) const { return from == p_with.from ? to < p_with.to : from < p_with.from; } + }; + +public: + bool has_gizmo(Node3D *p_spatial); + String get_name() const; + int get_priority() const; + void redraw(EditorNode3DGizmo *p_gizmo); + + NavigationMeshNode3DGizmoPlugin(); +}; + +class JointGizmosDrawer { +public: + static Basis look_body(const Transform &p_joint_transform, const Transform &p_body_transform); + static Basis look_body_toward(Vector3::Axis p_axis, const Transform &joint_transform, const Transform &body_transform); + static Basis look_body_toward_x(const Transform &p_joint_transform, const Transform &p_body_transform); + static Basis look_body_toward_y(const Transform &p_joint_transform, const Transform &p_body_transform); + /// Special function just used for physics joints, it returns a basis constrained toward Joint Z axis + /// with axis X and Y that are looking toward the body and oriented toward up + static Basis look_body_toward_z(const Transform &p_joint_transform, const Transform &p_body_transform); + + // Draw circle around p_axis + static void draw_circle(Vector3::Axis p_axis, real_t p_radius, const Transform &p_offset, const Basis &p_base, real_t p_limit_lower, real_t p_limit_upper, Vector &r_points, bool p_inverse = false); + static void draw_cone(const Transform &p_offset, const Basis &p_base, real_t p_swing, real_t p_twist, Vector &r_points); +}; + +class JointNode3DGizmoPlugin : public EditorNode3DGizmoPlugin { + + GDCLASS(JointNode3DGizmoPlugin, EditorNode3DGizmoPlugin); + +public: + bool has_gizmo(Node3D *p_spatial); + String get_name() const; + int get_priority() const; + void redraw(EditorNode3DGizmo *p_gizmo); + + static void CreatePinJointGizmo(const Transform &p_offset, Vector &r_cursor_points); + static void CreateHingeJointGizmo(const Transform &p_offset, const Transform &p_trs_joint, const Transform &p_trs_body_a, const Transform &p_trs_body_b, real_t p_limit_lower, real_t p_limit_upper, bool p_use_limit, Vector &r_common_points, Vector *r_body_a_points, Vector *r_body_b_points); + static void CreateSliderJointGizmo(const Transform &p_offset, const Transform &p_trs_joint, const Transform &p_trs_body_a, const Transform &p_trs_body_b, real_t p_angular_limit_lower, real_t p_angular_limit_upper, real_t p_linear_limit_lower, real_t p_linear_limit_upper, Vector &r_points, Vector *r_body_a_points, Vector *r_body_b_points); + static void CreateConeTwistJointGizmo(const Transform &p_offset, const Transform &p_trs_joint, const Transform &p_trs_body_a, const Transform &p_trs_body_b, real_t p_swing, real_t p_twist, Vector *r_body_a_points, Vector *r_body_b_points); + static void CreateGeneric6DOFJointGizmo( + const Transform &p_offset, + const Transform &p_trs_joint, + const Transform &p_trs_body_a, + const Transform &p_trs_body_b, + real_t p_angular_limit_lower_x, + real_t p_angular_limit_upper_x, + real_t p_linear_limit_lower_x, + real_t p_linear_limit_upper_x, + bool p_enable_angular_limit_x, + bool p_enable_linear_limit_x, + real_t p_angular_limit_lower_y, + real_t p_angular_limit_upper_y, + real_t p_linear_limit_lower_y, + real_t p_linear_limit_upper_y, + bool p_enable_angular_limit_y, + bool p_enable_linear_limit_y, + real_t p_angular_limit_lower_z, + real_t p_angular_limit_upper_z, + real_t p_linear_limit_lower_z, + real_t p_linear_limit_upper_z, + bool p_enable_angular_limit_z, + bool p_enable_linear_limit_z, + Vector &r_points, + Vector *r_body_a_points, + Vector *r_body_b_points); + + JointNode3DGizmoPlugin(); +}; + +#endif // SPATIAL_EDITOR_GIZMOS_H diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index b75c56dd47..41af2ab0cc 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -39,7 +39,7 @@ #include "editor/editor_scale.h" #include "editor/editor_settings.h" #include "editor/plugins/canvas_item_editor_plugin.h" // For onion skinning. -#include "editor/plugins/spatial_editor_plugin.h" // For onion skinning. +#include "editor/plugins/node_3d_editor_plugin.h" // For onion skinning. #include "scene/main/window.h" #include "servers/visual_server.h" diff --git a/editor/plugins/camera_3d_editor_plugin.cpp b/editor/plugins/camera_3d_editor_plugin.cpp new file mode 100644 index 0000000000..3d9b74c2da --- /dev/null +++ b/editor/plugins/camera_3d_editor_plugin.cpp @@ -0,0 +1,124 @@ +/*************************************************************************/ +/* camera_3d_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "camera_3d_editor_plugin.h" + +#include "node_3d_editor_plugin.h" + +void Camera3DEditor::_node_removed(Node *p_node) { + + if (p_node == node) { + node = NULL; + Node3DEditor::get_singleton()->set_custom_camera(NULL); + hide(); + } +} + +void Camera3DEditor::_pressed() { + + Node *sn = (node && preview->is_pressed()) ? node : NULL; + Node3DEditor::get_singleton()->set_custom_camera(sn); +} + +void Camera3DEditor::_bind_methods() { +} + +void Camera3DEditor::edit(Node *p_camera) { + + node = p_camera; + + if (!node) { + preview->set_pressed(false); + Node3DEditor::get_singleton()->set_custom_camera(NULL); + } else { + + if (preview->is_pressed()) + Node3DEditor::get_singleton()->set_custom_camera(p_camera); + else + Node3DEditor::get_singleton()->set_custom_camera(NULL); + } +} + +Camera3DEditor::Camera3DEditor() { + + preview = memnew(Button); + add_child(preview); + + preview->set_text(TTR("Preview")); + preview->set_toggle_mode(true); + preview->set_anchor(MARGIN_LEFT, Control::ANCHOR_END); + preview->set_anchor(MARGIN_RIGHT, Control::ANCHOR_END); + preview->set_margin(MARGIN_LEFT, -60); + preview->set_margin(MARGIN_RIGHT, 0); + preview->set_margin(MARGIN_TOP, 0); + preview->set_margin(MARGIN_BOTTOM, 10); + preview->connect("pressed", callable_mp(this, &Camera3DEditor::_pressed)); +} + +void Camera3DEditorPlugin::edit(Object *p_object) { + + Node3DEditor::get_singleton()->set_can_preview(Object::cast_to(p_object)); + //camera_editor->edit(Object::cast_to(p_object)); +} + +bool Camera3DEditorPlugin::handles(Object *p_object) const { + + return p_object->is_class("Camera3D"); +} + +void Camera3DEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + //Node3DEditor::get_singleton()->set_can_preview(Object::cast_to(p_object)); + } else { + Node3DEditor::get_singleton()->set_can_preview(NULL); + } +} + +Camera3DEditorPlugin::Camera3DEditorPlugin(EditorNode *p_node) { + + editor = p_node; + /* camera_editor = memnew( CameraEditor ); + editor->get_viewport()->add_child(camera_editor); + + camera_editor->set_anchor(MARGIN_LEFT,Control::ANCHOR_END); + camera_editor->set_anchor(MARGIN_RIGHT,Control::ANCHOR_END); + camera_editor->set_margin(MARGIN_LEFT,60); + camera_editor->set_margin(MARGIN_RIGHT,0); + camera_editor->set_margin(MARGIN_TOP,0); + camera_editor->set_margin(MARGIN_BOTTOM,10); + + + camera_editor->hide(); +*/ +} + +Camera3DEditorPlugin::~Camera3DEditorPlugin() { +} diff --git a/editor/plugins/camera_3d_editor_plugin.h b/editor/plugins/camera_3d_editor_plugin.h new file mode 100644 index 0000000000..1e57ac7cd2 --- /dev/null +++ b/editor/plugins/camera_3d_editor_plugin.h @@ -0,0 +1,75 @@ +/*************************************************************************/ +/* camera_3d_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef CAMERA_EDITOR_PLUGIN_H +#define CAMERA_EDITOR_PLUGIN_H + +#include "editor/editor_node.h" +#include "editor/editor_plugin.h" +#include "scene/3d/camera_3d.h" + +class Camera3DEditor : public Control { + + GDCLASS(Camera3DEditor, Control); + + Panel *panel; + Button *preview; + Node *node; + + void _pressed(); + +protected: + void _node_removed(Node *p_node); + static void _bind_methods(); + +public: + void edit(Node *p_camera); + Camera3DEditor(); +}; + +class Camera3DEditorPlugin : public EditorPlugin { + + GDCLASS(Camera3DEditorPlugin, EditorPlugin); + + //CameraEditor *camera_editor; + EditorNode *editor; + +public: + virtual String get_name() const { return "Camera3D"; } + bool has_main_screen() const { return false; } + virtual void edit(Object *p_object); + virtual bool handles(Object *p_object) const; + virtual void make_visible(bool p_visible); + + Camera3DEditorPlugin(EditorNode *p_node); + ~Camera3DEditorPlugin(); +}; + +#endif // CAMERA_EDITOR_PLUGIN_H diff --git a/editor/plugins/camera_editor_plugin.cpp b/editor/plugins/camera_editor_plugin.cpp deleted file mode 100644 index 2b6dd379e2..0000000000 --- a/editor/plugins/camera_editor_plugin.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/*************************************************************************/ -/* camera_editor_plugin.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "camera_editor_plugin.h" - -#include "spatial_editor_plugin.h" - -void CameraEditor::_node_removed(Node *p_node) { - - if (p_node == node) { - node = NULL; - Node3DEditor::get_singleton()->set_custom_camera(NULL); - hide(); - } -} - -void CameraEditor::_pressed() { - - Node *sn = (node && preview->is_pressed()) ? node : NULL; - Node3DEditor::get_singleton()->set_custom_camera(sn); -} - -void CameraEditor::_bind_methods() { -} - -void CameraEditor::edit(Node *p_camera) { - - node = p_camera; - - if (!node) { - preview->set_pressed(false); - Node3DEditor::get_singleton()->set_custom_camera(NULL); - } else { - - if (preview->is_pressed()) - Node3DEditor::get_singleton()->set_custom_camera(p_camera); - else - Node3DEditor::get_singleton()->set_custom_camera(NULL); - } -} - -CameraEditor::CameraEditor() { - - preview = memnew(Button); - add_child(preview); - - preview->set_text(TTR("Preview")); - preview->set_toggle_mode(true); - preview->set_anchor(MARGIN_LEFT, Control::ANCHOR_END); - preview->set_anchor(MARGIN_RIGHT, Control::ANCHOR_END); - preview->set_margin(MARGIN_LEFT, -60); - preview->set_margin(MARGIN_RIGHT, 0); - preview->set_margin(MARGIN_TOP, 0); - preview->set_margin(MARGIN_BOTTOM, 10); - preview->connect("pressed", callable_mp(this, &CameraEditor::_pressed)); -} - -void CameraEditorPlugin::edit(Object *p_object) { - - Node3DEditor::get_singleton()->set_can_preview(Object::cast_to(p_object)); - //camera_editor->edit(Object::cast_to(p_object)); -} - -bool CameraEditorPlugin::handles(Object *p_object) const { - - return p_object->is_class("Camera3D"); -} - -void CameraEditorPlugin::make_visible(bool p_visible) { - - if (p_visible) { - //Node3DEditor::get_singleton()->set_can_preview(Object::cast_to(p_object)); - } else { - Node3DEditor::get_singleton()->set_can_preview(NULL); - } -} - -CameraEditorPlugin::CameraEditorPlugin(EditorNode *p_node) { - - editor = p_node; - /* camera_editor = memnew( CameraEditor ); - editor->get_viewport()->add_child(camera_editor); - - camera_editor->set_anchor(MARGIN_LEFT,Control::ANCHOR_END); - camera_editor->set_anchor(MARGIN_RIGHT,Control::ANCHOR_END); - camera_editor->set_margin(MARGIN_LEFT,60); - camera_editor->set_margin(MARGIN_RIGHT,0); - camera_editor->set_margin(MARGIN_TOP,0); - camera_editor->set_margin(MARGIN_BOTTOM,10); - - - camera_editor->hide(); -*/ -} - -CameraEditorPlugin::~CameraEditorPlugin() { -} diff --git a/editor/plugins/camera_editor_plugin.h b/editor/plugins/camera_editor_plugin.h deleted file mode 100644 index 3e59d49371..0000000000 --- a/editor/plugins/camera_editor_plugin.h +++ /dev/null @@ -1,75 +0,0 @@ -/*************************************************************************/ -/* camera_editor_plugin.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef CAMERA_EDITOR_PLUGIN_H -#define CAMERA_EDITOR_PLUGIN_H - -#include "editor/editor_node.h" -#include "editor/editor_plugin.h" -#include "scene/3d/camera_3d.h" - -class CameraEditor : public Control { - - GDCLASS(CameraEditor, Control); - - Panel *panel; - Button *preview; - Node *node; - - void _pressed(); - -protected: - void _node_removed(Node *p_node); - static void _bind_methods(); - -public: - void edit(Node *p_camera); - CameraEditor(); -}; - -class CameraEditorPlugin : public EditorPlugin { - - GDCLASS(CameraEditorPlugin, EditorPlugin); - - //CameraEditor *camera_editor; - EditorNode *editor; - -public: - virtual String get_name() const { return "Camera3D"; } - bool has_main_screen() const { return false; } - virtual void edit(Object *p_object); - virtual bool handles(Object *p_object) const; - virtual void make_visible(bool p_visible); - - CameraEditorPlugin(EditorNode *p_node); - ~CameraEditorPlugin(); -}; - -#endif // CAMERA_EDITOR_PLUGIN_H diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index dbd1f68f00..ba9cba5515 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -40,8 +40,8 @@ #include "editor/editor_settings.h" #include "editor/plugins/animation_player_editor_plugin.h" #include "editor/plugins/script_editor_plugin.h" +#include "scene/2d/gpu_particles_2d.h" #include "scene/2d/light_2d.h" -#include "scene/2d/particles_2d.h" #include "scene/2d/polygon_2d.h" #include "scene/2d/skeleton_2d.h" #include "scene/2d/sprite_2d.h" @@ -6083,8 +6083,8 @@ void CanvasItemEditorViewport::_perform_drop_data() { Node *child; if (default_type == "Light2D") child = memnew(Light2D); - else if (default_type == "Particles2D") - child = memnew(Particles2D); + else if (default_type == "GPUParticles2D") + child = memnew(GPUParticles2D); else if (default_type == "Polygon2D") child = memnew(Polygon2D); else if (default_type == "TouchScreenButton") @@ -6247,11 +6247,11 @@ void CanvasItemEditorViewport::_bind_methods() { } CanvasItemEditorViewport::CanvasItemEditorViewport(EditorNode *p_node, CanvasItemEditor *p_canvas_item_editor) { - default_type = "Sprite"; + default_type = "Sprite2D"; // Node2D - types.push_back("Sprite"); + types.push_back("Sprite2D"); types.push_back("Light2D"); - types.push_back("Particles2D"); + types.push_back("GPUParticles2D"); types.push_back("Polygon2D"); types.push_back("TouchScreenButton"); // Control diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index f109b06aa3..34965868e0 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -33,12 +33,12 @@ #include "editor/editor_node.h" #include "editor/editor_plugin.h" -#include "scene/2d/canvas_item.h" #include "scene/gui/box_container.h" #include "scene/gui/check_box.h" #include "scene/gui/label.h" #include "scene/gui/panel_container.h" #include "scene/gui/spin_box.h" +#include "scene/main/canvas_item.h" class CanvasItemEditorViewport; diff --git a/editor/plugins/collision_polygon_3d_editor_plugin.cpp b/editor/plugins/collision_polygon_3d_editor_plugin.cpp new file mode 100644 index 0000000000..5b35a4826c --- /dev/null +++ b/editor/plugins/collision_polygon_3d_editor_plugin.cpp @@ -0,0 +1,608 @@ +/*************************************************************************/ +/* collision_polygon_3d_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "collision_polygon_3d_editor_plugin.h" + +#include "canvas_item_editor_plugin.h" +#include "core/input/input_filter.h" +#include "core/os/file_access.h" +#include "core/os/keyboard.h" +#include "editor/editor_settings.h" +#include "node_3d_editor_plugin.h" +#include "scene/3d/camera_3d.h" + +void CollisionPolygon3DEditor::_notification(int p_what) { + + switch (p_what) { + + case NOTIFICATION_READY: { + + button_create->set_icon(get_theme_icon("Edit", "EditorIcons")); + button_edit->set_icon(get_theme_icon("MovePoint", "EditorIcons")); + button_edit->set_pressed(true); + get_tree()->connect("node_removed", callable_mp(this, &CollisionPolygon3DEditor::_node_removed)); + + } break; + case NOTIFICATION_PROCESS: { + if (!node) { + return; + } + + if (_get_depth() != prev_depth) { + _polygon_draw(); + prev_depth = _get_depth(); + } + + } break; + } +} +void CollisionPolygon3DEditor::_node_removed(Node *p_node) { + + if (p_node == node) { + node = NULL; + if (imgeom->get_parent() == p_node) + p_node->remove_child(imgeom); + hide(); + set_process(false); + } +} + +void CollisionPolygon3DEditor::_menu_option(int p_option) { + + switch (p_option) { + + case MODE_CREATE: { + + mode = MODE_CREATE; + button_create->set_pressed(true); + button_edit->set_pressed(false); + } break; + case MODE_EDIT: { + + mode = MODE_EDIT; + button_create->set_pressed(false); + button_edit->set_pressed(true); + } break; + } +} + +void CollisionPolygon3DEditor::_wip_close() { + + undo_redo->create_action(TTR("Create Polygon3D")); + undo_redo->add_undo_method(node, "set_polygon", node->call("get_polygon")); + undo_redo->add_do_method(node, "set_polygon", wip); + undo_redo->add_do_method(this, "_polygon_draw"); + undo_redo->add_undo_method(this, "_polygon_draw"); + wip.clear(); + wip_active = false; + mode = MODE_EDIT; + button_edit->set_pressed(true); + button_create->set_pressed(false); + edited_point = -1; + undo_redo->commit_action(); +} + +bool CollisionPolygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, const Ref &p_event) { + + if (!node) + return false; + + Transform gt = node->get_global_transform(); + Transform gi = gt.affine_inverse(); + float depth = _get_depth() * 0.5; + Vector3 n = gt.basis.get_axis(2).normalized(); + Plane p(gt.origin + n * depth, n); + + Ref mb = p_event; + + if (mb.is_valid()) { + + Vector2 gpoint = mb->get_position(); + Vector3 ray_from = p_camera->project_ray_origin(gpoint); + Vector3 ray_dir = p_camera->project_ray_normal(gpoint); + + Vector3 spoint; + + if (!p.intersects_ray(ray_from, ray_dir, &spoint)) + return false; + + spoint = gi.xform(spoint); + + Vector2 cpoint(spoint.x, spoint.y); + + //DO NOT snap here, it's confusing in 3D for adding points. + //Let the snap happen when the point is being moved, instead. + //cpoint = CanvasItemEditor::get_singleton()->snap_point(cpoint); + + Vector poly = node->call("get_polygon"); + + //first check if a point is to be added (segment split) + real_t grab_threshold = EDITOR_GET("editors/poly_editor/point_grab_radius"); + + switch (mode) { + + case MODE_CREATE: { + + if (mb->get_button_index() == BUTTON_LEFT && mb->is_pressed()) { + + if (!wip_active) { + + wip.clear(); + wip.push_back(cpoint); + wip_active = true; + edited_point_pos = cpoint; + snap_ignore = false; + _polygon_draw(); + edited_point = 1; + return true; + } else { + + if (wip.size() > 1 && p_camera->unproject_position(gt.xform(Vector3(wip[0].x, wip[0].y, depth))).distance_to(gpoint) < grab_threshold) { + //wip closed + _wip_close(); + + return true; + } else { + + wip.push_back(cpoint); + edited_point = wip.size(); + snap_ignore = false; + _polygon_draw(); + return true; + } + } + } else if (mb->get_button_index() == BUTTON_RIGHT && mb->is_pressed() && wip_active) { + _wip_close(); + } + + } break; + + case MODE_EDIT: { + + if (mb->get_button_index() == BUTTON_LEFT) { + if (mb->is_pressed()) { + + if (mb->get_control()) { + + if (poly.size() < 3) { + + undo_redo->create_action(TTR("Edit Poly")); + undo_redo->add_undo_method(node, "set_polygon", poly); + poly.push_back(cpoint); + undo_redo->add_do_method(node, "set_polygon", poly); + undo_redo->add_do_method(this, "_polygon_draw"); + undo_redo->add_undo_method(this, "_polygon_draw"); + undo_redo->commit_action(); + return true; + } + + //search edges + int closest_idx = -1; + Vector2 closest_pos; + real_t closest_dist = 1e10; + for (int i = 0; i < poly.size(); i++) { + + Vector2 points[2] = { + p_camera->unproject_position(gt.xform(Vector3(poly[i].x, poly[i].y, depth))), + p_camera->unproject_position(gt.xform(Vector3(poly[(i + 1) % poly.size()].x, poly[(i + 1) % poly.size()].y, depth))) + }; + + Vector2 cp = Geometry::get_closest_point_to_segment_2d(gpoint, points); + if (cp.distance_squared_to(points[0]) < CMP_EPSILON2 || cp.distance_squared_to(points[1]) < CMP_EPSILON2) + continue; //not valid to reuse point + + real_t d = cp.distance_to(gpoint); + if (d < closest_dist && d < grab_threshold) { + closest_dist = d; + closest_pos = cp; + closest_idx = i; + } + } + + if (closest_idx >= 0) { + + pre_move_edit = poly; + poly.insert(closest_idx + 1, cpoint); + edited_point = closest_idx + 1; + edited_point_pos = cpoint; + node->call("set_polygon", poly); + _polygon_draw(); + snap_ignore = true; + + return true; + } + } else { + + //look for points to move + + int closest_idx = -1; + Vector2 closest_pos; + real_t closest_dist = 1e10; + for (int i = 0; i < poly.size(); i++) { + + Vector2 cp = p_camera->unproject_position(gt.xform(Vector3(poly[i].x, poly[i].y, depth))); + + real_t d = cp.distance_to(gpoint); + if (d < closest_dist && d < grab_threshold) { + closest_dist = d; + closest_pos = cp; + closest_idx = i; + } + } + + if (closest_idx >= 0) { + + pre_move_edit = poly; + edited_point = closest_idx; + edited_point_pos = poly[closest_idx]; + _polygon_draw(); + snap_ignore = false; + return true; + } + } + } else { + + snap_ignore = false; + + if (edited_point != -1) { + + //apply + + ERR_FAIL_INDEX_V(edited_point, poly.size(), false); + poly.write[edited_point] = edited_point_pos; + undo_redo->create_action(TTR("Edit Poly")); + undo_redo->add_do_method(node, "set_polygon", poly); + undo_redo->add_undo_method(node, "set_polygon", pre_move_edit); + undo_redo->add_do_method(this, "_polygon_draw"); + undo_redo->add_undo_method(this, "_polygon_draw"); + undo_redo->commit_action(); + + edited_point = -1; + return true; + } + } + } + if (mb->get_button_index() == BUTTON_RIGHT && mb->is_pressed() && edited_point == -1) { + + int closest_idx = -1; + Vector2 closest_pos; + real_t closest_dist = 1e10; + for (int i = 0; i < poly.size(); i++) { + + Vector2 cp = p_camera->unproject_position(gt.xform(Vector3(poly[i].x, poly[i].y, depth))); + + real_t d = cp.distance_to(gpoint); + if (d < closest_dist && d < grab_threshold) { + closest_dist = d; + closest_pos = cp; + closest_idx = i; + } + } + + if (closest_idx >= 0) { + + undo_redo->create_action(TTR("Edit Poly (Remove Point)")); + undo_redo->add_undo_method(node, "set_polygon", poly); + poly.remove(closest_idx); + undo_redo->add_do_method(node, "set_polygon", poly); + undo_redo->add_do_method(this, "_polygon_draw"); + undo_redo->add_undo_method(this, "_polygon_draw"); + undo_redo->commit_action(); + return true; + } + } + + } break; + } + } + + Ref mm = p_event; + + if (mm.is_valid()) { + if (edited_point != -1 && (wip_active || mm->get_button_mask() & BUTTON_MASK_LEFT)) { + + Vector2 gpoint = mm->get_position(); + + Vector3 ray_from = p_camera->project_ray_origin(gpoint); + Vector3 ray_dir = p_camera->project_ray_normal(gpoint); + + Vector3 spoint; + + if (!p.intersects_ray(ray_from, ray_dir, &spoint)) + return false; + + spoint = gi.xform(spoint); + + Vector2 cpoint(spoint.x, spoint.y); + + if (snap_ignore && !InputFilter::get_singleton()->is_key_pressed(KEY_CONTROL)) { + snap_ignore = false; + } + + if (!snap_ignore && Node3DEditor::get_singleton()->is_snap_enabled()) { + cpoint = cpoint.snapped(Vector2( + Node3DEditor::get_singleton()->get_translate_snap(), + Node3DEditor::get_singleton()->get_translate_snap())); + } + edited_point_pos = cpoint; + + _polygon_draw(); + } + } + + return false; +} + +float CollisionPolygon3DEditor::_get_depth() { + + if (bool(node->call("_has_editable_3d_polygon_no_depth"))) + return 0; + + return float(node->call("get_depth")); +} + +void CollisionPolygon3DEditor::_polygon_draw() { + + if (!node) + return; + + Vector poly; + + if (wip_active) + poly = wip; + else + poly = node->call("get_polygon"); + + float depth = _get_depth() * 0.5; + + imgeom->clear(); + imgeom->set_material_override(line_material); + imgeom->begin(Mesh::PRIMITIVE_LINES, Ref()); + + Rect2 rect; + + for (int i = 0; i < poly.size(); i++) { + + Vector2 p, p2; + p = i == edited_point ? edited_point_pos : poly[i]; + if ((wip_active && i == poly.size() - 1) || (((i + 1) % poly.size()) == edited_point)) + p2 = edited_point_pos; + else + p2 = poly[(i + 1) % poly.size()]; + + if (i == 0) + rect.position = p; + else + rect.expand_to(p); + + Vector3 point = Vector3(p.x, p.y, depth); + Vector3 next_point = Vector3(p2.x, p2.y, depth); + + imgeom->set_color(Color(1, 0.3, 0.1, 0.8)); + imgeom->add_vertex(point); + imgeom->set_color(Color(1, 0.3, 0.1, 0.8)); + imgeom->add_vertex(next_point); + + //Color col=Color(1,0.3,0.1,0.8); + //vpc->draw_line(point,next_point,col,2); + //vpc->draw_texture(handle,point-handle->get_size()*0.5); + } + + rect = rect.grow(1); + + AABB r; + r.position.x = rect.position.x; + r.position.y = rect.position.y; + r.position.z = depth; + r.size.x = rect.size.x; + r.size.y = rect.size.y; + r.size.z = 0; + + imgeom->set_color(Color(0.8, 0.8, 0.8, 0.2)); + imgeom->add_vertex(r.position); + imgeom->set_color(Color(0.8, 0.8, 0.8, 0.2)); + imgeom->add_vertex(r.position + Vector3(0.3, 0, 0)); + imgeom->set_color(Color(0.8, 0.8, 0.8, 0.2)); + imgeom->add_vertex(r.position); + imgeom->set_color(Color(0.8, 0.8, 0.8, 0.2)); + imgeom->add_vertex(r.position + Vector3(0.0, 0.3, 0)); + + imgeom->set_color(Color(0.8, 0.8, 0.8, 0.2)); + imgeom->add_vertex(r.position + Vector3(r.size.x, 0, 0)); + imgeom->set_color(Color(0.8, 0.8, 0.8, 0.2)); + imgeom->add_vertex(r.position + Vector3(r.size.x, 0, 0) - Vector3(0.3, 0, 0)); + imgeom->set_color(Color(0.8, 0.8, 0.8, 0.2)); + imgeom->add_vertex(r.position + Vector3(r.size.x, 0, 0)); + imgeom->set_color(Color(0.8, 0.8, 0.8, 0.2)); + imgeom->add_vertex(r.position + Vector3(r.size.x, 0, 0) + Vector3(0, 0.3, 0)); + + imgeom->set_color(Color(0.8, 0.8, 0.8, 0.2)); + imgeom->add_vertex(r.position + Vector3(0, r.size.y, 0)); + imgeom->set_color(Color(0.8, 0.8, 0.8, 0.2)); + imgeom->add_vertex(r.position + Vector3(0, r.size.y, 0) - Vector3(0, 0.3, 0)); + imgeom->set_color(Color(0.8, 0.8, 0.8, 0.2)); + imgeom->add_vertex(r.position + Vector3(0, r.size.y, 0)); + imgeom->set_color(Color(0.8, 0.8, 0.8, 0.2)); + imgeom->add_vertex(r.position + Vector3(0, r.size.y, 0) + Vector3(0.3, 0, 0)); + + imgeom->set_color(Color(0.8, 0.8, 0.8, 0.2)); + imgeom->add_vertex(r.position + r.size); + imgeom->set_color(Color(0.8, 0.8, 0.8, 0.2)); + imgeom->add_vertex(r.position + r.size - Vector3(0.3, 0, 0)); + imgeom->set_color(Color(0.8, 0.8, 0.8, 0.2)); + imgeom->add_vertex(r.position + r.size); + imgeom->set_color(Color(0.8, 0.8, 0.8, 0.2)); + imgeom->add_vertex(r.position + r.size - Vector3(0.0, 0.3, 0)); + + imgeom->end(); + + m->clear_surfaces(); + + if (poly.size() == 0) + return; + + Array a; + a.resize(Mesh::ARRAY_MAX); + Vector va; + { + + va.resize(poly.size()); + Vector3 *w = va.ptrw(); + for (int i = 0; i < poly.size(); i++) { + + Vector2 p, p2; + p = i == edited_point ? edited_point_pos : poly[i]; + + Vector3 point = Vector3(p.x, p.y, depth); + w[i] = point; + } + } + a[Mesh::ARRAY_VERTEX] = va; + m->add_surface_from_arrays(Mesh::PRIMITIVE_POINTS, a); + m->surface_set_material(0, handle_material); +} + +void CollisionPolygon3DEditor::edit(Node *p_collision_polygon) { + + if (p_collision_polygon) { + + node = Object::cast_to(p_collision_polygon); + //Enable the pencil tool if the polygon is empty + if (Vector(node->call("get_polygon")).size() == 0) { + _menu_option(MODE_CREATE); + } + wip.clear(); + wip_active = false; + edited_point = -1; + p_collision_polygon->add_child(imgeom); + _polygon_draw(); + set_process(true); + prev_depth = -1; + + } else { + node = NULL; + + if (imgeom->get_parent()) + imgeom->get_parent()->remove_child(imgeom); + + set_process(false); + } +} + +void CollisionPolygon3DEditor::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_polygon_draw"), &CollisionPolygon3DEditor::_polygon_draw); +} + +CollisionPolygon3DEditor::CollisionPolygon3DEditor(EditorNode *p_editor) { + + node = NULL; + editor = p_editor; + undo_redo = EditorNode::get_undo_redo(); + + add_child(memnew(VSeparator)); + button_create = memnew(ToolButton); + add_child(button_create); + button_create->connect("pressed", callable_mp(this, &CollisionPolygon3DEditor::_menu_option), varray(MODE_CREATE)); + button_create->set_toggle_mode(true); + + button_edit = memnew(ToolButton); + add_child(button_edit); + button_edit->connect("pressed", callable_mp(this, &CollisionPolygon3DEditor::_menu_option), varray(MODE_EDIT)); + button_edit->set_toggle_mode(true); + + mode = MODE_EDIT; + wip_active = false; + imgeom = memnew(ImmediateGeometry3D); + imgeom->set_transform(Transform(Basis(), Vector3(0, 0, 0.00001))); + + line_material = Ref(memnew(StandardMaterial3D)); + line_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); + line_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); + line_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); + line_material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); + line_material->set_albedo(Color(1, 1, 1)); + + handle_material = Ref(memnew(StandardMaterial3D)); + handle_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); + handle_material->set_flag(StandardMaterial3D::FLAG_USE_POINT_SIZE, true); + handle_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); + handle_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); + handle_material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); + Ref handle = editor->get_gui_base()->get_theme_icon("Editor3DHandle", "EditorIcons"); + handle_material->set_point_size(handle->get_width()); + handle_material->set_texture(StandardMaterial3D::TEXTURE_ALBEDO, handle); + + pointsm = memnew(MeshInstance3D); + imgeom->add_child(pointsm); + m.instance(); + pointsm->set_mesh(m); + pointsm->set_transform(Transform(Basis(), Vector3(0, 0, 0.00001))); + + snap_ignore = false; +} + +CollisionPolygon3DEditor::~CollisionPolygon3DEditor() { + + memdelete(imgeom); +} + +void Polygon3DEditorPlugin::edit(Object *p_object) { + + collision_polygon_editor->edit(Object::cast_to(p_object)); +} + +bool Polygon3DEditorPlugin::handles(Object *p_object) const { + + return Object::cast_to(p_object) && bool(p_object->call("_is_editable_3d_polygon")); +} + +void Polygon3DEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + collision_polygon_editor->show(); + } else { + + collision_polygon_editor->hide(); + collision_polygon_editor->edit(NULL); + } +} + +Polygon3DEditorPlugin::Polygon3DEditorPlugin(EditorNode *p_node) { + + editor = p_node; + collision_polygon_editor = memnew(CollisionPolygon3DEditor(p_node)); + Node3DEditor::get_singleton()->add_control_to_menu_panel(collision_polygon_editor); + + collision_polygon_editor->hide(); +} + +Polygon3DEditorPlugin::~Polygon3DEditorPlugin() { +} diff --git a/editor/plugins/collision_polygon_3d_editor_plugin.h b/editor/plugins/collision_polygon_3d_editor_plugin.h new file mode 100644 index 0000000000..9751b1f79e --- /dev/null +++ b/editor/plugins/collision_polygon_3d_editor_plugin.h @@ -0,0 +1,119 @@ +/*************************************************************************/ +/* collision_polygon_3d_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef COLLISION_POLYGON_EDITOR_PLUGIN_H +#define COLLISION_POLYGON_EDITOR_PLUGIN_H + +#include "editor/editor_node.h" +#include "editor/editor_plugin.h" +#include "scene/3d/collision_polygon_3d.h" +#include "scene/3d/immediate_geometry_3d.h" +#include "scene/3d/mesh_instance_3d.h" +#include "scene/gui/tool_button.h" + +class CanvasItemEditor; + +class CollisionPolygon3DEditor : public HBoxContainer { + + GDCLASS(CollisionPolygon3DEditor, HBoxContainer); + + UndoRedo *undo_redo; + enum Mode { + + MODE_CREATE, + MODE_EDIT, + + }; + + Mode mode; + + ToolButton *button_create; + ToolButton *button_edit; + + Ref line_material; + Ref handle_material; + + EditorNode *editor; + Panel *panel; + Node3D *node; + ImmediateGeometry3D *imgeom; + MeshInstance3D *pointsm; + Ref m; + + MenuButton *options; + + int edited_point; + Vector2 edited_point_pos; + Vector pre_move_edit; + Vector wip; + bool wip_active; + bool snap_ignore; + + float prev_depth; + + void _wip_close(); + void _polygon_draw(); + void _menu_option(int p_option); + + float _get_depth(); + +protected: + void _notification(int p_what); + void _node_removed(Node *p_node); + static void _bind_methods(); + +public: + virtual bool forward_spatial_gui_input(Camera3D *p_camera, const Ref &p_event); + void edit(Node *p_collision_polygon); + CollisionPolygon3DEditor(EditorNode *p_editor); + ~CollisionPolygon3DEditor(); +}; + +class Polygon3DEditorPlugin : public EditorPlugin { + + GDCLASS(Polygon3DEditorPlugin, EditorPlugin); + + CollisionPolygon3DEditor *collision_polygon_editor; + EditorNode *editor; + +public: + virtual bool forward_spatial_gui_input(Camera3D *p_camera, const Ref &p_event) { return collision_polygon_editor->forward_spatial_gui_input(p_camera, p_event); } + + virtual String get_name() const { return "Polygon3DEditor"; } + bool has_main_screen() const { return false; } + virtual void edit(Object *p_object); + virtual bool handles(Object *p_object) const; + virtual void make_visible(bool p_visible); + + Polygon3DEditorPlugin(EditorNode *p_node); + ~Polygon3DEditorPlugin(); +}; + +#endif // COLLISION_POLYGON_EDITOR_PLUGIN_H diff --git a/editor/plugins/collision_polygon_editor_plugin.cpp b/editor/plugins/collision_polygon_editor_plugin.cpp deleted file mode 100644 index 98bd655a03..0000000000 --- a/editor/plugins/collision_polygon_editor_plugin.cpp +++ /dev/null @@ -1,608 +0,0 @@ -/*************************************************************************/ -/* collision_polygon_editor_plugin.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "collision_polygon_editor_plugin.h" - -#include "canvas_item_editor_plugin.h" -#include "core/input/input_filter.h" -#include "core/os/file_access.h" -#include "core/os/keyboard.h" -#include "editor/editor_settings.h" -#include "scene/3d/camera_3d.h" -#include "spatial_editor_plugin.h" - -void Polygon3DEditor::_notification(int p_what) { - - switch (p_what) { - - case NOTIFICATION_READY: { - - button_create->set_icon(get_theme_icon("Edit", "EditorIcons")); - button_edit->set_icon(get_theme_icon("MovePoint", "EditorIcons")); - button_edit->set_pressed(true); - get_tree()->connect("node_removed", callable_mp(this, &Polygon3DEditor::_node_removed)); - - } break; - case NOTIFICATION_PROCESS: { - if (!node) { - return; - } - - if (_get_depth() != prev_depth) { - _polygon_draw(); - prev_depth = _get_depth(); - } - - } break; - } -} -void Polygon3DEditor::_node_removed(Node *p_node) { - - if (p_node == node) { - node = NULL; - if (imgeom->get_parent() == p_node) - p_node->remove_child(imgeom); - hide(); - set_process(false); - } -} - -void Polygon3DEditor::_menu_option(int p_option) { - - switch (p_option) { - - case MODE_CREATE: { - - mode = MODE_CREATE; - button_create->set_pressed(true); - button_edit->set_pressed(false); - } break; - case MODE_EDIT: { - - mode = MODE_EDIT; - button_create->set_pressed(false); - button_edit->set_pressed(true); - } break; - } -} - -void Polygon3DEditor::_wip_close() { - - undo_redo->create_action(TTR("Create Polygon3D")); - undo_redo->add_undo_method(node, "set_polygon", node->call("get_polygon")); - undo_redo->add_do_method(node, "set_polygon", wip); - undo_redo->add_do_method(this, "_polygon_draw"); - undo_redo->add_undo_method(this, "_polygon_draw"); - wip.clear(); - wip_active = false; - mode = MODE_EDIT; - button_edit->set_pressed(true); - button_create->set_pressed(false); - edited_point = -1; - undo_redo->commit_action(); -} - -bool Polygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, const Ref &p_event) { - - if (!node) - return false; - - Transform gt = node->get_global_transform(); - Transform gi = gt.affine_inverse(); - float depth = _get_depth() * 0.5; - Vector3 n = gt.basis.get_axis(2).normalized(); - Plane p(gt.origin + n * depth, n); - - Ref mb = p_event; - - if (mb.is_valid()) { - - Vector2 gpoint = mb->get_position(); - Vector3 ray_from = p_camera->project_ray_origin(gpoint); - Vector3 ray_dir = p_camera->project_ray_normal(gpoint); - - Vector3 spoint; - - if (!p.intersects_ray(ray_from, ray_dir, &spoint)) - return false; - - spoint = gi.xform(spoint); - - Vector2 cpoint(spoint.x, spoint.y); - - //DO NOT snap here, it's confusing in 3D for adding points. - //Let the snap happen when the point is being moved, instead. - //cpoint = CanvasItemEditor::get_singleton()->snap_point(cpoint); - - Vector poly = node->call("get_polygon"); - - //first check if a point is to be added (segment split) - real_t grab_threshold = EDITOR_GET("editors/poly_editor/point_grab_radius"); - - switch (mode) { - - case MODE_CREATE: { - - if (mb->get_button_index() == BUTTON_LEFT && mb->is_pressed()) { - - if (!wip_active) { - - wip.clear(); - wip.push_back(cpoint); - wip_active = true; - edited_point_pos = cpoint; - snap_ignore = false; - _polygon_draw(); - edited_point = 1; - return true; - } else { - - if (wip.size() > 1 && p_camera->unproject_position(gt.xform(Vector3(wip[0].x, wip[0].y, depth))).distance_to(gpoint) < grab_threshold) { - //wip closed - _wip_close(); - - return true; - } else { - - wip.push_back(cpoint); - edited_point = wip.size(); - snap_ignore = false; - _polygon_draw(); - return true; - } - } - } else if (mb->get_button_index() == BUTTON_RIGHT && mb->is_pressed() && wip_active) { - _wip_close(); - } - - } break; - - case MODE_EDIT: { - - if (mb->get_button_index() == BUTTON_LEFT) { - if (mb->is_pressed()) { - - if (mb->get_control()) { - - if (poly.size() < 3) { - - undo_redo->create_action(TTR("Edit Poly")); - undo_redo->add_undo_method(node, "set_polygon", poly); - poly.push_back(cpoint); - undo_redo->add_do_method(node, "set_polygon", poly); - undo_redo->add_do_method(this, "_polygon_draw"); - undo_redo->add_undo_method(this, "_polygon_draw"); - undo_redo->commit_action(); - return true; - } - - //search edges - int closest_idx = -1; - Vector2 closest_pos; - real_t closest_dist = 1e10; - for (int i = 0; i < poly.size(); i++) { - - Vector2 points[2] = { - p_camera->unproject_position(gt.xform(Vector3(poly[i].x, poly[i].y, depth))), - p_camera->unproject_position(gt.xform(Vector3(poly[(i + 1) % poly.size()].x, poly[(i + 1) % poly.size()].y, depth))) - }; - - Vector2 cp = Geometry::get_closest_point_to_segment_2d(gpoint, points); - if (cp.distance_squared_to(points[0]) < CMP_EPSILON2 || cp.distance_squared_to(points[1]) < CMP_EPSILON2) - continue; //not valid to reuse point - - real_t d = cp.distance_to(gpoint); - if (d < closest_dist && d < grab_threshold) { - closest_dist = d; - closest_pos = cp; - closest_idx = i; - } - } - - if (closest_idx >= 0) { - - pre_move_edit = poly; - poly.insert(closest_idx + 1, cpoint); - edited_point = closest_idx + 1; - edited_point_pos = cpoint; - node->call("set_polygon", poly); - _polygon_draw(); - snap_ignore = true; - - return true; - } - } else { - - //look for points to move - - int closest_idx = -1; - Vector2 closest_pos; - real_t closest_dist = 1e10; - for (int i = 0; i < poly.size(); i++) { - - Vector2 cp = p_camera->unproject_position(gt.xform(Vector3(poly[i].x, poly[i].y, depth))); - - real_t d = cp.distance_to(gpoint); - if (d < closest_dist && d < grab_threshold) { - closest_dist = d; - closest_pos = cp; - closest_idx = i; - } - } - - if (closest_idx >= 0) { - - pre_move_edit = poly; - edited_point = closest_idx; - edited_point_pos = poly[closest_idx]; - _polygon_draw(); - snap_ignore = false; - return true; - } - } - } else { - - snap_ignore = false; - - if (edited_point != -1) { - - //apply - - ERR_FAIL_INDEX_V(edited_point, poly.size(), false); - poly.write[edited_point] = edited_point_pos; - undo_redo->create_action(TTR("Edit Poly")); - undo_redo->add_do_method(node, "set_polygon", poly); - undo_redo->add_undo_method(node, "set_polygon", pre_move_edit); - undo_redo->add_do_method(this, "_polygon_draw"); - undo_redo->add_undo_method(this, "_polygon_draw"); - undo_redo->commit_action(); - - edited_point = -1; - return true; - } - } - } - if (mb->get_button_index() == BUTTON_RIGHT && mb->is_pressed() && edited_point == -1) { - - int closest_idx = -1; - Vector2 closest_pos; - real_t closest_dist = 1e10; - for (int i = 0; i < poly.size(); i++) { - - Vector2 cp = p_camera->unproject_position(gt.xform(Vector3(poly[i].x, poly[i].y, depth))); - - real_t d = cp.distance_to(gpoint); - if (d < closest_dist && d < grab_threshold) { - closest_dist = d; - closest_pos = cp; - closest_idx = i; - } - } - - if (closest_idx >= 0) { - - undo_redo->create_action(TTR("Edit Poly (Remove Point)")); - undo_redo->add_undo_method(node, "set_polygon", poly); - poly.remove(closest_idx); - undo_redo->add_do_method(node, "set_polygon", poly); - undo_redo->add_do_method(this, "_polygon_draw"); - undo_redo->add_undo_method(this, "_polygon_draw"); - undo_redo->commit_action(); - return true; - } - } - - } break; - } - } - - Ref mm = p_event; - - if (mm.is_valid()) { - if (edited_point != -1 && (wip_active || mm->get_button_mask() & BUTTON_MASK_LEFT)) { - - Vector2 gpoint = mm->get_position(); - - Vector3 ray_from = p_camera->project_ray_origin(gpoint); - Vector3 ray_dir = p_camera->project_ray_normal(gpoint); - - Vector3 spoint; - - if (!p.intersects_ray(ray_from, ray_dir, &spoint)) - return false; - - spoint = gi.xform(spoint); - - Vector2 cpoint(spoint.x, spoint.y); - - if (snap_ignore && !InputFilter::get_singleton()->is_key_pressed(KEY_CONTROL)) { - snap_ignore = false; - } - - if (!snap_ignore && Node3DEditor::get_singleton()->is_snap_enabled()) { - cpoint = cpoint.snapped(Vector2( - Node3DEditor::get_singleton()->get_translate_snap(), - Node3DEditor::get_singleton()->get_translate_snap())); - } - edited_point_pos = cpoint; - - _polygon_draw(); - } - } - - return false; -} - -float Polygon3DEditor::_get_depth() { - - if (bool(node->call("_has_editable_3d_polygon_no_depth"))) - return 0; - - return float(node->call("get_depth")); -} - -void Polygon3DEditor::_polygon_draw() { - - if (!node) - return; - - Vector poly; - - if (wip_active) - poly = wip; - else - poly = node->call("get_polygon"); - - float depth = _get_depth() * 0.5; - - imgeom->clear(); - imgeom->set_material_override(line_material); - imgeom->begin(Mesh::PRIMITIVE_LINES, Ref()); - - Rect2 rect; - - for (int i = 0; i < poly.size(); i++) { - - Vector2 p, p2; - p = i == edited_point ? edited_point_pos : poly[i]; - if ((wip_active && i == poly.size() - 1) || (((i + 1) % poly.size()) == edited_point)) - p2 = edited_point_pos; - else - p2 = poly[(i + 1) % poly.size()]; - - if (i == 0) - rect.position = p; - else - rect.expand_to(p); - - Vector3 point = Vector3(p.x, p.y, depth); - Vector3 next_point = Vector3(p2.x, p2.y, depth); - - imgeom->set_color(Color(1, 0.3, 0.1, 0.8)); - imgeom->add_vertex(point); - imgeom->set_color(Color(1, 0.3, 0.1, 0.8)); - imgeom->add_vertex(next_point); - - //Color col=Color(1,0.3,0.1,0.8); - //vpc->draw_line(point,next_point,col,2); - //vpc->draw_texture(handle,point-handle->get_size()*0.5); - } - - rect = rect.grow(1); - - AABB r; - r.position.x = rect.position.x; - r.position.y = rect.position.y; - r.position.z = depth; - r.size.x = rect.size.x; - r.size.y = rect.size.y; - r.size.z = 0; - - imgeom->set_color(Color(0.8, 0.8, 0.8, 0.2)); - imgeom->add_vertex(r.position); - imgeom->set_color(Color(0.8, 0.8, 0.8, 0.2)); - imgeom->add_vertex(r.position + Vector3(0.3, 0, 0)); - imgeom->set_color(Color(0.8, 0.8, 0.8, 0.2)); - imgeom->add_vertex(r.position); - imgeom->set_color(Color(0.8, 0.8, 0.8, 0.2)); - imgeom->add_vertex(r.position + Vector3(0.0, 0.3, 0)); - - imgeom->set_color(Color(0.8, 0.8, 0.8, 0.2)); - imgeom->add_vertex(r.position + Vector3(r.size.x, 0, 0)); - imgeom->set_color(Color(0.8, 0.8, 0.8, 0.2)); - imgeom->add_vertex(r.position + Vector3(r.size.x, 0, 0) - Vector3(0.3, 0, 0)); - imgeom->set_color(Color(0.8, 0.8, 0.8, 0.2)); - imgeom->add_vertex(r.position + Vector3(r.size.x, 0, 0)); - imgeom->set_color(Color(0.8, 0.8, 0.8, 0.2)); - imgeom->add_vertex(r.position + Vector3(r.size.x, 0, 0) + Vector3(0, 0.3, 0)); - - imgeom->set_color(Color(0.8, 0.8, 0.8, 0.2)); - imgeom->add_vertex(r.position + Vector3(0, r.size.y, 0)); - imgeom->set_color(Color(0.8, 0.8, 0.8, 0.2)); - imgeom->add_vertex(r.position + Vector3(0, r.size.y, 0) - Vector3(0, 0.3, 0)); - imgeom->set_color(Color(0.8, 0.8, 0.8, 0.2)); - imgeom->add_vertex(r.position + Vector3(0, r.size.y, 0)); - imgeom->set_color(Color(0.8, 0.8, 0.8, 0.2)); - imgeom->add_vertex(r.position + Vector3(0, r.size.y, 0) + Vector3(0.3, 0, 0)); - - imgeom->set_color(Color(0.8, 0.8, 0.8, 0.2)); - imgeom->add_vertex(r.position + r.size); - imgeom->set_color(Color(0.8, 0.8, 0.8, 0.2)); - imgeom->add_vertex(r.position + r.size - Vector3(0.3, 0, 0)); - imgeom->set_color(Color(0.8, 0.8, 0.8, 0.2)); - imgeom->add_vertex(r.position + r.size); - imgeom->set_color(Color(0.8, 0.8, 0.8, 0.2)); - imgeom->add_vertex(r.position + r.size - Vector3(0.0, 0.3, 0)); - - imgeom->end(); - - m->clear_surfaces(); - - if (poly.size() == 0) - return; - - Array a; - a.resize(Mesh::ARRAY_MAX); - Vector va; - { - - va.resize(poly.size()); - Vector3 *w = va.ptrw(); - for (int i = 0; i < poly.size(); i++) { - - Vector2 p, p2; - p = i == edited_point ? edited_point_pos : poly[i]; - - Vector3 point = Vector3(p.x, p.y, depth); - w[i] = point; - } - } - a[Mesh::ARRAY_VERTEX] = va; - m->add_surface_from_arrays(Mesh::PRIMITIVE_POINTS, a); - m->surface_set_material(0, handle_material); -} - -void Polygon3DEditor::edit(Node *p_collision_polygon) { - - if (p_collision_polygon) { - - node = Object::cast_to(p_collision_polygon); - //Enable the pencil tool if the polygon is empty - if (Vector(node->call("get_polygon")).size() == 0) { - _menu_option(MODE_CREATE); - } - wip.clear(); - wip_active = false; - edited_point = -1; - p_collision_polygon->add_child(imgeom); - _polygon_draw(); - set_process(true); - prev_depth = -1; - - } else { - node = NULL; - - if (imgeom->get_parent()) - imgeom->get_parent()->remove_child(imgeom); - - set_process(false); - } -} - -void Polygon3DEditor::_bind_methods() { - - ClassDB::bind_method(D_METHOD("_polygon_draw"), &Polygon3DEditor::_polygon_draw); -} - -Polygon3DEditor::Polygon3DEditor(EditorNode *p_editor) { - - node = NULL; - editor = p_editor; - undo_redo = EditorNode::get_undo_redo(); - - add_child(memnew(VSeparator)); - button_create = memnew(ToolButton); - add_child(button_create); - button_create->connect("pressed", callable_mp(this, &Polygon3DEditor::_menu_option), varray(MODE_CREATE)); - button_create->set_toggle_mode(true); - - button_edit = memnew(ToolButton); - add_child(button_edit); - button_edit->connect("pressed", callable_mp(this, &Polygon3DEditor::_menu_option), varray(MODE_EDIT)); - button_edit->set_toggle_mode(true); - - mode = MODE_EDIT; - wip_active = false; - imgeom = memnew(ImmediateGeometry3D); - imgeom->set_transform(Transform(Basis(), Vector3(0, 0, 0.00001))); - - line_material = Ref(memnew(StandardMaterial3D)); - line_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); - line_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); - line_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); - line_material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); - line_material->set_albedo(Color(1, 1, 1)); - - handle_material = Ref(memnew(StandardMaterial3D)); - handle_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); - handle_material->set_flag(StandardMaterial3D::FLAG_USE_POINT_SIZE, true); - handle_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); - handle_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); - handle_material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); - Ref handle = editor->get_gui_base()->get_theme_icon("Editor3DHandle", "EditorIcons"); - handle_material->set_point_size(handle->get_width()); - handle_material->set_texture(StandardMaterial3D::TEXTURE_ALBEDO, handle); - - pointsm = memnew(MeshInstance3D); - imgeom->add_child(pointsm); - m.instance(); - pointsm->set_mesh(m); - pointsm->set_transform(Transform(Basis(), Vector3(0, 0, 0.00001))); - - snap_ignore = false; -} - -Polygon3DEditor::~Polygon3DEditor() { - - memdelete(imgeom); -} - -void Polygon3DEditorPlugin::edit(Object *p_object) { - - collision_polygon_editor->edit(Object::cast_to(p_object)); -} - -bool Polygon3DEditorPlugin::handles(Object *p_object) const { - - return Object::cast_to(p_object) && bool(p_object->call("_is_editable_3d_polygon")); -} - -void Polygon3DEditorPlugin::make_visible(bool p_visible) { - - if (p_visible) { - collision_polygon_editor->show(); - } else { - - collision_polygon_editor->hide(); - collision_polygon_editor->edit(NULL); - } -} - -Polygon3DEditorPlugin::Polygon3DEditorPlugin(EditorNode *p_node) { - - editor = p_node; - collision_polygon_editor = memnew(Polygon3DEditor(p_node)); - Node3DEditor::get_singleton()->add_control_to_menu_panel(collision_polygon_editor); - - collision_polygon_editor->hide(); -} - -Polygon3DEditorPlugin::~Polygon3DEditorPlugin() { -} diff --git a/editor/plugins/collision_polygon_editor_plugin.h b/editor/plugins/collision_polygon_editor_plugin.h deleted file mode 100644 index 169b07b673..0000000000 --- a/editor/plugins/collision_polygon_editor_plugin.h +++ /dev/null @@ -1,119 +0,0 @@ -/*************************************************************************/ -/* collision_polygon_editor_plugin.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef COLLISION_POLYGON_EDITOR_PLUGIN_H -#define COLLISION_POLYGON_EDITOR_PLUGIN_H - -#include "editor/editor_node.h" -#include "editor/editor_plugin.h" -#include "scene/3d/collision_polygon_3d.h" -#include "scene/3d/immediate_geometry_3d.h" -#include "scene/3d/mesh_instance_3d.h" -#include "scene/gui/tool_button.h" - -class CanvasItemEditor; - -class Polygon3DEditor : public HBoxContainer { - - GDCLASS(Polygon3DEditor, HBoxContainer); - - UndoRedo *undo_redo; - enum Mode { - - MODE_CREATE, - MODE_EDIT, - - }; - - Mode mode; - - ToolButton *button_create; - ToolButton *button_edit; - - Ref line_material; - Ref handle_material; - - EditorNode *editor; - Panel *panel; - Node3D *node; - ImmediateGeometry3D *imgeom; - MeshInstance3D *pointsm; - Ref m; - - MenuButton *options; - - int edited_point; - Vector2 edited_point_pos; - Vector pre_move_edit; - Vector wip; - bool wip_active; - bool snap_ignore; - - float prev_depth; - - void _wip_close(); - void _polygon_draw(); - void _menu_option(int p_option); - - float _get_depth(); - -protected: - void _notification(int p_what); - void _node_removed(Node *p_node); - static void _bind_methods(); - -public: - virtual bool forward_spatial_gui_input(Camera3D *p_camera, const Ref &p_event); - void edit(Node *p_collision_polygon); - Polygon3DEditor(EditorNode *p_editor); - ~Polygon3DEditor(); -}; - -class Polygon3DEditorPlugin : public EditorPlugin { - - GDCLASS(Polygon3DEditorPlugin, EditorPlugin); - - Polygon3DEditor *collision_polygon_editor; - EditorNode *editor; - -public: - virtual bool forward_spatial_gui_input(Camera3D *p_camera, const Ref &p_event) { return collision_polygon_editor->forward_spatial_gui_input(p_camera, p_event); } - - virtual String get_name() const { return "Polygon3DEditor"; } - bool has_main_screen() const { return false; } - virtual void edit(Object *p_object); - virtual bool handles(Object *p_object) const; - virtual void make_visible(bool p_visible); - - Polygon3DEditorPlugin(EditorNode *p_node); - ~Polygon3DEditorPlugin(); -}; - -#endif // COLLISION_POLYGON_EDITOR_PLUGIN_H diff --git a/editor/plugins/cpu_particles_2d_editor_plugin.cpp b/editor/plugins/cpu_particles_2d_editor_plugin.cpp index 25b8fb8bcb..022663a61d 100644 --- a/editor/plugins/cpu_particles_2d_editor_plugin.cpp +++ b/editor/plugins/cpu_particles_2d_editor_plugin.cpp @@ -241,7 +241,7 @@ void CPUParticles2DEditorPlugin::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE) { menu->get_popup()->connect("id_pressed", callable_mp(this, &CPUParticles2DEditorPlugin::_menu_callback)); - menu->set_icon(epoints->get_theme_icon("Particles2D", "EditorIcons")); + menu->set_icon(epoints->get_theme_icon("CPUParticles2D", "EditorIcons")); file->connect("file_selected", callable_mp(this, &CPUParticles2DEditorPlugin::_file_selected)); } } @@ -265,8 +265,7 @@ CPUParticles2DEditorPlugin::CPUParticles2DEditorPlugin(EditorNode *p_node) { menu->get_popup()->add_item(TTR("Load Emission Mask"), MENU_LOAD_EMISSION_MASK); menu->get_popup()->add_separator(); menu->get_popup()->add_item(TTR("Restart"), MENU_RESTART); - // menu->get_popup()->add_item(TTR("Clear Emission Mask"), MENU_CLEAR_EMISSION_MASK); - menu->set_text(TTR("Particles")); + menu->set_text(TTR("CPUParticles2D")); menu->set_switch_on_hover(true); toolbar->add_child(menu); diff --git a/editor/plugins/cpu_particles_3d_editor_plugin.cpp b/editor/plugins/cpu_particles_3d_editor_plugin.cpp new file mode 100644 index 0000000000..887e9e48df --- /dev/null +++ b/editor/plugins/cpu_particles_3d_editor_plugin.cpp @@ -0,0 +1,145 @@ +/*************************************************************************/ +/* cpu_particles_3d_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "cpu_particles_3d_editor_plugin.h" + +#include "editor/plugins/node_3d_editor_plugin.h" + +void CPUParticles3DEditor::_node_removed(Node *p_node) { + + if (p_node == node) { + node = NULL; + hide(); + } +} + +void CPUParticles3DEditor::_notification(int p_notification) { + + if (p_notification == NOTIFICATION_ENTER_TREE) { + options->set_icon(get_theme_icon("CPUParticles3D", "EditorIcons")); + } +} + +void CPUParticles3DEditor::_menu_option(int p_option) { + + switch (p_option) { + + case MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE: { + + emission_tree_dialog->popup_centered_ratio(); + + } break; + + case MENU_OPTION_RESTART: { + + node->restart(); + + } break; + } +} + +void CPUParticles3DEditor::edit(CPUParticles3D *p_particles) { + + base_node = p_particles; + node = p_particles; +} + +void CPUParticles3DEditor::_generate_emission_points() { + + /// hacer codigo aca + Vector points; + Vector normals; + + if (!_generate(points, normals)) { + return; + } + + if (normals.size() == 0) { + node->set_emission_shape(CPUParticles3D::EMISSION_SHAPE_POINTS); + node->set_emission_points(points); + } else { + node->set_emission_shape(CPUParticles3D::EMISSION_SHAPE_DIRECTED_POINTS); + node->set_emission_points(points); + node->set_emission_normals(normals); + } +} + +void CPUParticles3DEditor::_bind_methods() { +} + +CPUParticles3DEditor::CPUParticles3DEditor() { + + particles_editor_hb = memnew(HBoxContainer); + Node3DEditor::get_singleton()->add_control_to_menu_panel(particles_editor_hb); + options = memnew(MenuButton); + options->set_switch_on_hover(true); + particles_editor_hb->add_child(options); + particles_editor_hb->hide(); + + options->set_text(TTR("CPUParticles3D")); + options->get_popup()->add_item(TTR("Create Emission Points From Node"), MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE); + options->get_popup()->add_separator(); + options->get_popup()->add_item(TTR("Restart"), MENU_OPTION_RESTART); + options->get_popup()->connect("id_pressed", callable_mp(this, &CPUParticles3DEditor::_menu_option)); +} + +void CPUParticles3DEditorPlugin::edit(Object *p_object) { + + particles_editor->edit(Object::cast_to(p_object)); +} + +bool CPUParticles3DEditorPlugin::handles(Object *p_object) const { + + return p_object->is_class("CPUParticles3D"); +} + +void CPUParticles3DEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + particles_editor->show(); + particles_editor->particles_editor_hb->show(); + } else { + particles_editor->particles_editor_hb->hide(); + particles_editor->hide(); + particles_editor->edit(NULL); + } +} + +CPUParticles3DEditorPlugin::CPUParticles3DEditorPlugin(EditorNode *p_node) { + + editor = p_node; + particles_editor = memnew(CPUParticles3DEditor); + editor->get_viewport()->add_child(particles_editor); + + particles_editor->hide(); +} + +CPUParticles3DEditorPlugin::~CPUParticles3DEditorPlugin() { +} diff --git a/editor/plugins/cpu_particles_3d_editor_plugin.h b/editor/plugins/cpu_particles_3d_editor_plugin.h new file mode 100644 index 0000000000..aac9cc9a2f --- /dev/null +++ b/editor/plugins/cpu_particles_3d_editor_plugin.h @@ -0,0 +1,85 @@ +/*************************************************************************/ +/* cpu_particles_3d_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef CPU_PARTICLES_EDITOR_PLUGIN_H +#define CPU_PARTICLES_EDITOR_PLUGIN_H + +#include "editor/plugins/gpu_particles_3d_editor_plugin.h" +#include "scene/3d/cpu_particles_3d.h" + +class CPUParticles3DEditor : public GPUParticles3DEditorBase { + + GDCLASS(CPUParticles3DEditor, GPUParticles3DEditorBase); + + enum Menu { + + MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE, + MENU_OPTION_CLEAR_EMISSION_VOLUME, + MENU_OPTION_RESTART + + }; + + CPUParticles3D *node; + + void _menu_option(int); + + friend class CPUParticles3DEditorPlugin; + + virtual void _generate_emission_points(); + +protected: + void _notification(int p_notification); + void _node_removed(Node *p_node); + static void _bind_methods(); + +public: + void edit(CPUParticles3D *p_particles); + CPUParticles3DEditor(); +}; + +class CPUParticles3DEditorPlugin : public EditorPlugin { + + GDCLASS(CPUParticles3DEditorPlugin, EditorPlugin); + + CPUParticles3DEditor *particles_editor; + EditorNode *editor; + +public: + virtual String get_name() const { return "CPUParticles"; } + bool has_main_screen() const { return false; } + virtual void edit(Object *p_object); + virtual bool handles(Object *p_object) const; + virtual void make_visible(bool p_visible); + + CPUParticles3DEditorPlugin(EditorNode *p_node); + ~CPUParticles3DEditorPlugin(); +}; + +#endif // CPU_PARTICLES_EDITOR_PLUGIN_H diff --git a/editor/plugins/cpu_particles_editor_plugin.cpp b/editor/plugins/cpu_particles_editor_plugin.cpp deleted file mode 100644 index 4674903256..0000000000 --- a/editor/plugins/cpu_particles_editor_plugin.cpp +++ /dev/null @@ -1,145 +0,0 @@ -/*************************************************************************/ -/* cpu_particles_editor_plugin.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "cpu_particles_editor_plugin.h" - -#include "editor/plugins/spatial_editor_plugin.h" - -void CPUParticlesEditor::_node_removed(Node *p_node) { - - if (p_node == node) { - node = NULL; - hide(); - } -} - -void CPUParticlesEditor::_notification(int p_notification) { - - if (p_notification == NOTIFICATION_ENTER_TREE) { - options->set_icon(get_theme_icon("CPUParticles", "EditorIcons")); - } -} - -void CPUParticlesEditor::_menu_option(int p_option) { - - switch (p_option) { - - case MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE: { - - emission_tree_dialog->popup_centered_ratio(); - - } break; - - case MENU_OPTION_RESTART: { - - node->restart(); - - } break; - } -} - -void CPUParticlesEditor::edit(CPUParticles3D *p_particles) { - - base_node = p_particles; - node = p_particles; -} - -void CPUParticlesEditor::_generate_emission_points() { - - /// hacer codigo aca - Vector points; - Vector normals; - - if (!_generate(points, normals)) { - return; - } - - if (normals.size() == 0) { - node->set_emission_shape(CPUParticles3D::EMISSION_SHAPE_POINTS); - node->set_emission_points(points); - } else { - node->set_emission_shape(CPUParticles3D::EMISSION_SHAPE_DIRECTED_POINTS); - node->set_emission_points(points); - node->set_emission_normals(normals); - } -} - -void CPUParticlesEditor::_bind_methods() { -} - -CPUParticlesEditor::CPUParticlesEditor() { - - particles_editor_hb = memnew(HBoxContainer); - Node3DEditor::get_singleton()->add_control_to_menu_panel(particles_editor_hb); - options = memnew(MenuButton); - options->set_switch_on_hover(true); - particles_editor_hb->add_child(options); - particles_editor_hb->hide(); - - options->set_text(TTR("CPUParticles")); - options->get_popup()->add_item(TTR("Create Emission Points From Node"), MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE); - options->get_popup()->add_separator(); - options->get_popup()->add_item(TTR("Restart"), MENU_OPTION_RESTART); - options->get_popup()->connect("id_pressed", callable_mp(this, &CPUParticlesEditor::_menu_option)); -} - -void CPUParticlesEditorPlugin::edit(Object *p_object) { - - particles_editor->edit(Object::cast_to(p_object)); -} - -bool CPUParticlesEditorPlugin::handles(Object *p_object) const { - - return p_object->is_class("CPUParticles3D"); -} - -void CPUParticlesEditorPlugin::make_visible(bool p_visible) { - - if (p_visible) { - particles_editor->show(); - particles_editor->particles_editor_hb->show(); - } else { - particles_editor->particles_editor_hb->hide(); - particles_editor->hide(); - particles_editor->edit(NULL); - } -} - -CPUParticlesEditorPlugin::CPUParticlesEditorPlugin(EditorNode *p_node) { - - editor = p_node; - particles_editor = memnew(CPUParticlesEditor); - editor->get_viewport()->add_child(particles_editor); - - particles_editor->hide(); -} - -CPUParticlesEditorPlugin::~CPUParticlesEditorPlugin() { -} diff --git a/editor/plugins/cpu_particles_editor_plugin.h b/editor/plugins/cpu_particles_editor_plugin.h deleted file mode 100644 index ed47ed1436..0000000000 --- a/editor/plugins/cpu_particles_editor_plugin.h +++ /dev/null @@ -1,85 +0,0 @@ -/*************************************************************************/ -/* cpu_particles_editor_plugin.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef CPU_PARTICLES_EDITOR_PLUGIN_H -#define CPU_PARTICLES_EDITOR_PLUGIN_H - -#include "editor/plugins/particles_editor_plugin.h" -#include "scene/3d/cpu_particles_3d.h" - -class CPUParticlesEditor : public ParticlesEditorBase { - - GDCLASS(CPUParticlesEditor, ParticlesEditorBase); - - enum Menu { - - MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE, - MENU_OPTION_CLEAR_EMISSION_VOLUME, - MENU_OPTION_RESTART - - }; - - CPUParticles3D *node; - - void _menu_option(int); - - friend class CPUParticlesEditorPlugin; - - virtual void _generate_emission_points(); - -protected: - void _notification(int p_notification); - void _node_removed(Node *p_node); - static void _bind_methods(); - -public: - void edit(CPUParticles3D *p_particles); - CPUParticlesEditor(); -}; - -class CPUParticlesEditorPlugin : public EditorPlugin { - - GDCLASS(CPUParticlesEditorPlugin, EditorPlugin); - - CPUParticlesEditor *particles_editor; - EditorNode *editor; - -public: - virtual String get_name() const { return "CPUParticles"; } - bool has_main_screen() const { return false; } - virtual void edit(Object *p_object); - virtual bool handles(Object *p_object) const; - virtual void make_visible(bool p_visible); - - CPUParticlesEditorPlugin(EditorNode *p_node); - ~CPUParticlesEditorPlugin(); -}; - -#endif // CPU_PARTICLES_EDITOR_PLUGIN_H diff --git a/editor/plugins/gpu_particles_2d_editor_plugin.cpp b/editor/plugins/gpu_particles_2d_editor_plugin.cpp new file mode 100644 index 0000000000..89bff7ccab --- /dev/null +++ b/editor/plugins/gpu_particles_2d_editor_plugin.cpp @@ -0,0 +1,435 @@ +/*************************************************************************/ +/* gpu_particles_2d_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "gpu_particles_2d_editor_plugin.h" + +#include "canvas_item_editor_plugin.h" +#include "core/io/image_loader.h" +#include "scene/2d/cpu_particles_2d.h" +#include "scene/gui/separator.h" +#include "scene/resources/particles_material.h" + +void GPUParticles2DEditorPlugin::edit(Object *p_object) { + + particles = Object::cast_to(p_object); +} + +bool GPUParticles2DEditorPlugin::handles(Object *p_object) const { + + return p_object->is_class("GPUParticles2D"); +} + +void GPUParticles2DEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + + toolbar->show(); + } else { + + toolbar->hide(); + } +} + +void GPUParticles2DEditorPlugin::_file_selected(const String &p_file) { + + source_emission_file = p_file; + emission_mask->popup_centered(); +} + +void GPUParticles2DEditorPlugin::_menu_callback(int p_idx) { + + switch (p_idx) { + case MENU_GENERATE_VISIBILITY_RECT: { + float gen_time = particles->get_lifetime(); + if (gen_time < 1.0) + generate_seconds->set_value(1.0); + else + generate_seconds->set_value(trunc(gen_time) + 1.0); + generate_visibility_rect->popup_centered(); + } break; + case MENU_LOAD_EMISSION_MASK: { + + file->popup_centered_ratio(); + + } break; + case MENU_CLEAR_EMISSION_MASK: { + + emission_mask->popup_centered(); + } break; + case MENU_OPTION_CONVERT_TO_CPU_PARTICLES: { + + CPUParticles2D *cpu_particles = memnew(CPUParticles2D); + cpu_particles->convert_from_particles(particles); + cpu_particles->set_name(particles->get_name()); + cpu_particles->set_transform(particles->get_transform()); + cpu_particles->set_visible(particles->is_visible()); + cpu_particles->set_pause_mode(particles->get_pause_mode()); + cpu_particles->set_z_index(particles->get_z_index()); + + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Convert to CPUParticles2D")); + ur->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", particles, cpu_particles, true, false); + ur->add_do_reference(cpu_particles); + ur->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", cpu_particles, particles, false, false); + ur->add_undo_reference(particles); + ur->commit_action(); + + } break; + case MENU_RESTART: { + + particles->restart(); + } + } +} + +void GPUParticles2DEditorPlugin::_generate_visibility_rect() { + + float time = generate_seconds->get_value(); + + float running = 0.0; + + EditorProgress ep("gen_vrect", TTR("Generating Visibility Rect"), int(time)); + + bool was_emitting = particles->is_emitting(); + if (!was_emitting) { + particles->set_emitting(true); + OS::get_singleton()->delay_usec(1000); + } + + Rect2 rect; + while (running < time) { + + uint64_t ticks = OS::get_singleton()->get_ticks_usec(); + ep.step("Generating...", int(running), true); + OS::get_singleton()->delay_usec(1000); + + Rect2 capture = particles->capture_rect(); + if (rect == Rect2()) + rect = capture; + else + rect = rect.merge(capture); + + running += (OS::get_singleton()->get_ticks_usec() - ticks) / 1000000.0; + } + + if (!was_emitting) { + particles->set_emitting(false); + } + + undo_redo->create_action(TTR("Generate Visibility Rect")); + undo_redo->add_do_method(particles, "set_visibility_rect", rect); + undo_redo->add_undo_method(particles, "set_visibility_rect", particles->get_visibility_rect()); + undo_redo->commit_action(); +} + +void GPUParticles2DEditorPlugin::_generate_emission_mask() { + + Ref pm = particles->get_process_material(); + if (!pm.is_valid()) { + EditorNode::get_singleton()->show_warning(TTR("Can only set point into a ParticlesMaterial process material")); + return; + } + + Ref img; + img.instance(); + Error err = ImageLoader::load_image(source_emission_file, img); + ERR_FAIL_COND_MSG(err != OK, "Error loading image '" + source_emission_file + "'."); + + if (img->is_compressed()) { + img->decompress(); + } + img->convert(Image::FORMAT_RGBA8); + ERR_FAIL_COND(img->get_format() != Image::FORMAT_RGBA8); + Size2i s = Size2(img->get_width(), img->get_height()); + ERR_FAIL_COND(s.width == 0 || s.height == 0); + + Vector valid_positions; + Vector valid_normals; + Vector valid_colors; + + valid_positions.resize(s.width * s.height); + + EmissionMode emode = (EmissionMode)emission_mask_mode->get_selected(); + + if (emode == EMISSION_MODE_BORDER_DIRECTED) { + valid_normals.resize(s.width * s.height); + } + + bool capture_colors = emission_colors->is_pressed(); + + if (capture_colors) { + valid_colors.resize(s.width * s.height * 4); + } + + int vpc = 0; + + { + Vector data = img->get_data(); + const uint8_t *r = data.ptr(); + + for (int i = 0; i < s.width; i++) { + for (int j = 0; j < s.height; j++) { + + uint8_t a = r[(j * s.width + i) * 4 + 3]; + + if (a > 128) { + + if (emode == EMISSION_MODE_SOLID) { + + if (capture_colors) { + valid_colors.write[vpc * 4 + 0] = r[(j * s.width + i) * 4 + 0]; + valid_colors.write[vpc * 4 + 1] = r[(j * s.width + i) * 4 + 1]; + valid_colors.write[vpc * 4 + 2] = r[(j * s.width + i) * 4 + 2]; + valid_colors.write[vpc * 4 + 3] = r[(j * s.width + i) * 4 + 3]; + } + valid_positions.write[vpc++] = Point2(i, j); + + } else { + + bool on_border = false; + for (int x = i - 1; x <= i + 1; x++) { + for (int y = j - 1; y <= j + 1; y++) { + + if (x < 0 || y < 0 || x >= s.width || y >= s.height || r[(y * s.width + x) * 4 + 3] <= 128) { + on_border = true; + break; + } + } + + if (on_border) + break; + } + + if (on_border) { + valid_positions.write[vpc] = Point2(i, j); + + if (emode == EMISSION_MODE_BORDER_DIRECTED) { + Vector2 normal; + for (int x = i - 2; x <= i + 2; x++) { + for (int y = j - 2; y <= j + 2; y++) { + + if (x == i && y == j) + continue; + + if (x < 0 || y < 0 || x >= s.width || y >= s.height || r[(y * s.width + x) * 4 + 3] <= 128) { + normal += Vector2(x - i, y - j).normalized(); + } + } + } + + normal.normalize(); + valid_normals.write[vpc] = normal; + } + + if (capture_colors) { + valid_colors.write[vpc * 4 + 0] = r[(j * s.width + i) * 4 + 0]; + valid_colors.write[vpc * 4 + 1] = r[(j * s.width + i) * 4 + 1]; + valid_colors.write[vpc * 4 + 2] = r[(j * s.width + i) * 4 + 2]; + valid_colors.write[vpc * 4 + 3] = r[(j * s.width + i) * 4 + 3]; + } + + vpc++; + } + } + } + } + } + } + + valid_positions.resize(vpc); + if (valid_normals.size()) { + valid_normals.resize(vpc); + } + + ERR_FAIL_COND_MSG(valid_positions.size() == 0, "No pixels with transparency > 128 in image..."); + + Vector texdata; + + int w = 2048; + int h = (vpc / 2048) + 1; + + texdata.resize(w * h * 2 * sizeof(float)); + + { + uint8_t *tw = texdata.ptrw(); + float *twf = (float *)tw; + for (int i = 0; i < vpc; i++) { + + twf[i * 2 + 0] = valid_positions[i].x; + twf[i * 2 + 1] = valid_positions[i].y; + } + } + + img.instance(); + img->create(w, h, false, Image::FORMAT_RGF, texdata); + + Ref imgt; + imgt.instance(); + imgt->create_from_image(img); + + pm->set_emission_point_texture(imgt); + pm->set_emission_point_count(vpc); + + if (capture_colors) { + + Vector colordata; + colordata.resize(w * h * 4); //use RG texture + + { + uint8_t *tw = colordata.ptrw(); + for (int i = 0; i < vpc * 4; i++) { + + tw[i] = valid_colors[i]; + } + } + + img.instance(); + img->create(w, h, false, Image::FORMAT_RGBA8, colordata); + + imgt.instance(); + imgt->create_from_image(img); + pm->set_emission_color_texture(imgt); + } + + if (valid_normals.size()) { + pm->set_emission_shape(ParticlesMaterial::EMISSION_SHAPE_DIRECTED_POINTS); + + Vector normdata; + normdata.resize(w * h * 2 * sizeof(float)); //use RG texture + + { + uint8_t *tw = normdata.ptrw(); + float *twf = (float *)tw; + for (int i = 0; i < vpc; i++) { + twf[i * 2 + 0] = valid_normals[i].x; + twf[i * 2 + 1] = valid_normals[i].y; + } + } + + img.instance(); + img->create(w, h, false, Image::FORMAT_RGF, normdata); + + imgt.instance(); + imgt->create_from_image(img); + pm->set_emission_normal_texture(imgt); + + } else { + pm->set_emission_shape(ParticlesMaterial::EMISSION_SHAPE_POINTS); + } +} + +void GPUParticles2DEditorPlugin::_notification(int p_what) { + + if (p_what == NOTIFICATION_ENTER_TREE) { + + menu->get_popup()->connect("id_pressed", callable_mp(this, &GPUParticles2DEditorPlugin::_menu_callback)); + menu->set_icon(menu->get_theme_icon("GPUParticles2D", "EditorIcons")); + file->connect("file_selected", callable_mp(this, &GPUParticles2DEditorPlugin::_file_selected)); + } +} + +void GPUParticles2DEditorPlugin::_bind_methods() { +} + +GPUParticles2DEditorPlugin::GPUParticles2DEditorPlugin(EditorNode *p_node) { + + particles = NULL; + editor = p_node; + undo_redo = editor->get_undo_redo(); + + toolbar = memnew(HBoxContainer); + add_control_to_container(CONTAINER_CANVAS_EDITOR_MENU, toolbar); + toolbar->hide(); + + toolbar->add_child(memnew(VSeparator)); + + menu = memnew(MenuButton); + menu->get_popup()->add_item(TTR("Generate Visibility Rect"), MENU_GENERATE_VISIBILITY_RECT); + menu->get_popup()->add_separator(); + menu->get_popup()->add_item(TTR("Load Emission Mask"), MENU_LOAD_EMISSION_MASK); + // menu->get_popup()->add_item(TTR("Clear Emission Mask"), MENU_CLEAR_EMISSION_MASK); + menu->get_popup()->add_separator(); + menu->get_popup()->add_item(TTR("Convert to CPUParticles2D"), MENU_OPTION_CONVERT_TO_CPU_PARTICLES); + menu->get_popup()->add_separator(); + menu->get_popup()->add_item(TTR("Restart"), MENU_RESTART); + menu->set_text(TTR("GPUParticles2D")); + menu->set_switch_on_hover(true); + toolbar->add_child(menu); + + file = memnew(EditorFileDialog); + List ext; + ImageLoader::get_recognized_extensions(&ext); + for (List::Element *E = ext.front(); E; E = E->next()) { + file->add_filter("*." + E->get() + "; " + E->get().to_upper()); + } + file->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE); + toolbar->add_child(file); + + epoints = memnew(SpinBox); + epoints->set_min(1); + epoints->set_max(8192); + epoints->set_step(1); + epoints->set_value(512); + file->get_vbox()->add_margin_child(TTR("Generated Point Count:"), epoints); + + generate_visibility_rect = memnew(ConfirmationDialog); + generate_visibility_rect->set_title(TTR("Generate Visibility Rect")); + VBoxContainer *genvb = memnew(VBoxContainer); + generate_visibility_rect->add_child(genvb); + generate_seconds = memnew(SpinBox); + genvb->add_margin_child(TTR("Generation Time (sec):"), generate_seconds); + generate_seconds->set_min(0.1); + generate_seconds->set_max(25); + generate_seconds->set_value(2); + + toolbar->add_child(generate_visibility_rect); + + generate_visibility_rect->connect("confirmed", callable_mp(this, &GPUParticles2DEditorPlugin::_generate_visibility_rect)); + + emission_mask = memnew(ConfirmationDialog); + emission_mask->set_title(TTR("Load Emission Mask")); + VBoxContainer *emvb = memnew(VBoxContainer); + emission_mask->add_child(emvb); + emission_mask_mode = memnew(OptionButton); + emvb->add_margin_child(TTR("Emission Mask"), emission_mask_mode); + emission_mask_mode->add_item(TTR("Solid Pixels"), EMISSION_MODE_SOLID); + emission_mask_mode->add_item(TTR("Border Pixels"), EMISSION_MODE_BORDER); + emission_mask_mode->add_item(TTR("Directed Border Pixels"), EMISSION_MODE_BORDER_DIRECTED); + emission_colors = memnew(CheckBox); + emission_colors->set_text(TTR("Capture from Pixel")); + emvb->add_margin_child(TTR("Emission Colors"), emission_colors); + + toolbar->add_child(emission_mask); + + emission_mask->connect("confirmed", callable_mp(this, &GPUParticles2DEditorPlugin::_generate_emission_mask)); +} + +GPUParticles2DEditorPlugin::~GPUParticles2DEditorPlugin() { +} diff --git a/editor/plugins/gpu_particles_2d_editor_plugin.h b/editor/plugins/gpu_particles_2d_editor_plugin.h new file mode 100644 index 0000000000..904786ffae --- /dev/null +++ b/editor/plugins/gpu_particles_2d_editor_plugin.h @@ -0,0 +1,100 @@ +/*************************************************************************/ +/* gpu_particles_2d_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef PARTICLES_2D_EDITOR_PLUGIN_H +#define PARTICLES_2D_EDITOR_PLUGIN_H + +#include "editor/editor_node.h" +#include "editor/editor_plugin.h" +#include "scene/2d/collision_polygon_2d.h" +#include "scene/2d/gpu_particles_2d.h" +#include "scene/gui/box_container.h" +#include "scene/gui/file_dialog.h" + +class GPUParticles2DEditorPlugin : public EditorPlugin { + + GDCLASS(GPUParticles2DEditorPlugin, EditorPlugin); + + enum { + + MENU_GENERATE_VISIBILITY_RECT, + MENU_LOAD_EMISSION_MASK, + MENU_CLEAR_EMISSION_MASK, + MENU_OPTION_CONVERT_TO_CPU_PARTICLES, + MENU_RESTART + }; + + enum EmissionMode { + EMISSION_MODE_SOLID, + EMISSION_MODE_BORDER, + EMISSION_MODE_BORDER_DIRECTED + }; + + GPUParticles2D *particles; + + EditorFileDialog *file; + EditorNode *editor; + + HBoxContainer *toolbar; + MenuButton *menu; + + SpinBox *epoints; + + ConfirmationDialog *generate_visibility_rect; + SpinBox *generate_seconds; + + ConfirmationDialog *emission_mask; + OptionButton *emission_mask_mode; + CheckBox *emission_colors; + + String source_emission_file; + + UndoRedo *undo_redo; + void _file_selected(const String &p_file); + void _menu_callback(int p_idx); + void _generate_visibility_rect(); + void _generate_emission_mask(); + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + virtual String get_name() const { return "Particles2D"; } + bool has_main_screen() const { return false; } + virtual void edit(Object *p_object); + virtual bool handles(Object *p_object) const; + virtual void make_visible(bool p_visible); + + GPUParticles2DEditorPlugin(EditorNode *p_node); + ~GPUParticles2DEditorPlugin(); +}; + +#endif // PARTICLES_2D_EDITOR_PLUGIN_H diff --git a/editor/plugins/gpu_particles_3d_editor_plugin.cpp b/editor/plugins/gpu_particles_3d_editor_plugin.cpp new file mode 100644 index 0000000000..655f03b7e0 --- /dev/null +++ b/editor/plugins/gpu_particles_3d_editor_plugin.cpp @@ -0,0 +1,493 @@ +/*************************************************************************/ +/* gpu_particles_3d_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "gpu_particles_3d_editor_plugin.h" + +#include "core/io/resource_loader.h" +#include "editor/plugins/node_3d_editor_plugin.h" +#include "scene/3d/cpu_particles_3d.h" +#include "scene/resources/particles_material.h" + +bool GPUParticles3DEditorBase::_generate(Vector &points, Vector &normals) { + + bool use_normals = emission_fill->get_selected() == 1; + + if (emission_fill->get_selected() < 2) { + + float area_accum = 0; + Map triangle_area_map; + + for (int i = 0; i < geometry.size(); i++) { + + float area = geometry[i].get_area(); + if (area < CMP_EPSILON) + continue; + triangle_area_map[area_accum] = i; + area_accum += area; + } + + if (!triangle_area_map.size() || area_accum == 0) { + + EditorNode::get_singleton()->show_warning(TTR("The geometry's faces don't contain any area.")); + return false; + } + + int emissor_count = emission_amount->get_value(); + + for (int i = 0; i < emissor_count; i++) { + + float areapos = Math::random(0.0f, area_accum); + + Map::Element *E = triangle_area_map.find_closest(areapos); + ERR_FAIL_COND_V(!E, false); + int index = E->get(); + ERR_FAIL_INDEX_V(index, geometry.size(), false); + + // ok FINALLY get face + Face3 face = geometry[index]; + //now compute some position inside the face... + + Vector3 pos = face.get_random_point_inside(); + + points.push_back(pos); + + if (use_normals) { + Vector3 normal = face.get_plane().normal; + normals.push_back(normal); + } + } + } else { + + int gcount = geometry.size(); + + if (gcount == 0) { + + EditorNode::get_singleton()->show_warning(TTR("The geometry doesn't contain any faces.")); + return false; + } + + const Face3 *r = geometry.ptr(); + + AABB aabb; + + for (int i = 0; i < gcount; i++) { + + for (int j = 0; j < 3; j++) { + + if (i == 0 && j == 0) + aabb.position = r[i].vertex[j]; + else + aabb.expand_to(r[i].vertex[j]); + } + } + + int emissor_count = emission_amount->get_value(); + + for (int i = 0; i < emissor_count; i++) { + + int attempts = 5; + + for (int j = 0; j < attempts; j++) { + + Vector3 dir; + dir[Math::rand() % 3] = 1.0; + Vector3 ofs = (Vector3(1, 1, 1) - dir) * Vector3(Math::randf(), Math::randf(), Math::randf()) * aabb.size + aabb.position; + + Vector3 ofsv = ofs + aabb.size * dir; + + //space it a little + ofs -= dir; + ofsv += dir; + + float max = -1e7, min = 1e7; + + for (int k = 0; k < gcount; k++) { + + const Face3 &f3 = r[k]; + + Vector3 res; + if (f3.intersects_segment(ofs, ofsv, &res)) { + + res -= ofs; + float d = dir.dot(res); + + if (d < min) + min = d; + if (d > max) + max = d; + } + } + + if (max < min) + continue; //lost attempt + + float val = min + (max - min) * Math::randf(); + + Vector3 point = ofs + dir * val; + + points.push_back(point); + break; + } + } + } + + return true; +} + +void GPUParticles3DEditorBase::_node_selected(const NodePath &p_path) { + + Node *sel = get_node(p_path); + if (!sel) + return; + + if (!sel->is_class("Node3D")) { + + EditorNode::get_singleton()->show_warning(vformat(TTR("\"%s\" doesn't inherit from Node3D."), sel->get_name())); + return; + } + + VisualInstance3D *vi = Object::cast_to(sel); + if (!vi) { + + EditorNode::get_singleton()->show_warning(vformat(TTR("\"%s\" doesn't contain geometry."), sel->get_name())); + return; + } + + geometry = vi->get_faces(VisualInstance3D::FACES_SOLID); + + if (geometry.size() == 0) { + + EditorNode::get_singleton()->show_warning(vformat(TTR("\"%s\" doesn't contain face geometry."), sel->get_name())); + return; + } + + Transform geom_xform = base_node->get_global_transform().affine_inverse() * vi->get_global_transform(); + + int gc = geometry.size(); + Face3 *w = geometry.ptrw(); + + for (int i = 0; i < gc; i++) { + for (int j = 0; j < 3; j++) { + w[i].vertex[j] = geom_xform.xform(w[i].vertex[j]); + } + } + + emission_dialog->popup_centered(Size2(300, 130)); +} + +void GPUParticles3DEditorBase::_bind_methods() { +} + +GPUParticles3DEditorBase::GPUParticles3DEditorBase() { + + emission_dialog = memnew(ConfirmationDialog); + emission_dialog->set_title(TTR("Create Emitter")); + add_child(emission_dialog); + VBoxContainer *emd_vb = memnew(VBoxContainer); + emission_dialog->add_child(emd_vb); + + emission_amount = memnew(SpinBox); + emission_amount->set_min(1); + emission_amount->set_max(100000); + emission_amount->set_value(512); + emd_vb->add_margin_child(TTR("Emission Points:"), emission_amount); + + emission_fill = memnew(OptionButton); + emission_fill->add_item(TTR("Surface Points")); + emission_fill->add_item(TTR("Surface Points+Normal (Directed)")); + emission_fill->add_item(TTR("Volume")); + emd_vb->add_margin_child(TTR("Emission Source: "), emission_fill); + + emission_dialog->get_ok()->set_text(TTR("Create")); + emission_dialog->connect("confirmed", callable_mp(this, &GPUParticles3DEditorBase::_generate_emission_points)); + + emission_tree_dialog = memnew(SceneTreeDialog); + add_child(emission_tree_dialog); + emission_tree_dialog->connect("selected", callable_mp(this, &GPUParticles3DEditorBase::_node_selected)); +} + +void GPUParticles3DEditor::_node_removed(Node *p_node) { + + if (p_node == node) { + node = NULL; + hide(); + } +} + +void GPUParticles3DEditor::_notification(int p_notification) { + + if (p_notification == NOTIFICATION_ENTER_TREE) { + options->set_icon(options->get_popup()->get_theme_icon("GPUParticles3D", "EditorIcons")); + get_tree()->connect("node_removed", callable_mp(this, &GPUParticles3DEditor::_node_removed)); + } +} + +void GPUParticles3DEditor::_menu_option(int p_option) { + + switch (p_option) { + + case MENU_OPTION_GENERATE_AABB: { + float gen_time = node->get_lifetime(); + + if (gen_time < 1.0) + generate_seconds->set_value(1.0); + else + generate_seconds->set_value(trunc(gen_time) + 1.0); + generate_aabb->popup_centered(); + } break; + case MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE: { + Ref material = node->get_process_material(); + if (material.is_null()) { + EditorNode::get_singleton()->show_warning(TTR("A processor material of type 'ParticlesMaterial' is required.")); + return; + } + + emission_tree_dialog->popup_centered_ratio(); + + } break; + case MENU_OPTION_CONVERT_TO_CPU_PARTICLES: { + + CPUParticles3D *cpu_particles = memnew(CPUParticles3D); + cpu_particles->convert_from_particles(node); + cpu_particles->set_name(node->get_name()); + cpu_particles->set_transform(node->get_transform()); + cpu_particles->set_visible(node->is_visible()); + cpu_particles->set_pause_mode(node->get_pause_mode()); + + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Convert to CPUParticles3D")); + ur->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", node, cpu_particles, true, false); + ur->add_do_reference(cpu_particles); + ur->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", cpu_particles, node, false, false); + ur->add_undo_reference(node); + ur->commit_action(); + + } break; + case MENU_OPTION_RESTART: { + + node->restart(); + + } break; + } +} + +void GPUParticles3DEditor::_generate_aabb() { + + float time = generate_seconds->get_value(); + + float running = 0.0; + + EditorProgress ep("gen_aabb", TTR("Generating AABB"), int(time)); + + bool was_emitting = node->is_emitting(); + if (!was_emitting) { + node->set_emitting(true); + OS::get_singleton()->delay_usec(1000); + } + + AABB rect; + + while (running < time) { + + uint64_t ticks = OS::get_singleton()->get_ticks_usec(); + ep.step("Generating...", int(running), true); + OS::get_singleton()->delay_usec(1000); + + AABB capture = node->capture_aabb(); + if (rect == AABB()) + rect = capture; + else + rect.merge_with(capture); + + running += (OS::get_singleton()->get_ticks_usec() - ticks) / 1000000.0; + } + + if (!was_emitting) { + node->set_emitting(false); + } + + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Generate Visibility AABB")); + ur->add_do_method(node, "set_visibility_aabb", rect); + ur->add_undo_method(node, "set_visibility_aabb", node->get_visibility_aabb()); + ur->commit_action(); +} + +void GPUParticles3DEditor::edit(GPUParticles3D *p_particles) { + + base_node = p_particles; + node = p_particles; +} + +void GPUParticles3DEditor::_generate_emission_points() { + + /// hacer codigo aca + Vector points; + Vector normals; + + if (!_generate(points, normals)) { + return; + } + + int point_count = points.size(); + + int w = 2048; + int h = (point_count / 2048) + 1; + + Vector point_img; + point_img.resize(w * h * 3 * sizeof(float)); + + { + uint8_t *iw = point_img.ptrw(); + zeromem(iw, w * h * 3 * sizeof(float)); + const Vector3 *r = points.ptr(); + float *wf = (float *)iw; + for (int i = 0; i < point_count; i++) { + wf[i * 3 + 0] = r[i].x; + wf[i * 3 + 1] = r[i].y; + wf[i * 3 + 2] = r[i].z; + } + } + + Ref image = memnew(Image(w, h, false, Image::FORMAT_RGBF, point_img)); + + Ref tex; + tex.instance(); + + Ref material = node->get_process_material(); + ERR_FAIL_COND(material.is_null()); + + if (normals.size() > 0) { + + material->set_emission_shape(ParticlesMaterial::EMISSION_SHAPE_DIRECTED_POINTS); + material->set_emission_point_count(point_count); + material->set_emission_point_texture(tex); + + Vector point_img2; + point_img2.resize(w * h * 3 * sizeof(float)); + + { + uint8_t *iw = point_img2.ptrw(); + zeromem(iw, w * h * 3 * sizeof(float)); + const Vector3 *r = normals.ptr(); + float *wf = (float *)iw; + for (int i = 0; i < point_count; i++) { + wf[i * 3 + 0] = r[i].x; + wf[i * 3 + 1] = r[i].y; + wf[i * 3 + 2] = r[i].z; + } + } + + Ref image2 = memnew(Image(w, h, false, Image::FORMAT_RGBF, point_img2)); + + Ref tex2; + tex2.instance(); + + material->set_emission_normal_texture(tex2); + } else { + + material->set_emission_shape(ParticlesMaterial::EMISSION_SHAPE_POINTS); + material->set_emission_point_count(point_count); + material->set_emission_point_texture(tex); + } +} + +void GPUParticles3DEditor::_bind_methods() { +} + +GPUParticles3DEditor::GPUParticles3DEditor() { + + node = NULL; + particles_editor_hb = memnew(HBoxContainer); + Node3DEditor::get_singleton()->add_control_to_menu_panel(particles_editor_hb); + options = memnew(MenuButton); + options->set_switch_on_hover(true); + particles_editor_hb->add_child(options); + particles_editor_hb->hide(); + + options->set_text(TTR("GPUParticles3D")); + options->get_popup()->add_item(TTR("Generate AABB"), MENU_OPTION_GENERATE_AABB); + options->get_popup()->add_separator(); + options->get_popup()->add_item(TTR("Create Emission Points From Node"), MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE); + options->get_popup()->add_separator(); + options->get_popup()->add_item(TTR("Convert to CPUParticles3D"), MENU_OPTION_CONVERT_TO_CPU_PARTICLES); + options->get_popup()->add_separator(); + options->get_popup()->add_item(TTR("Restart"), MENU_OPTION_RESTART); + + options->get_popup()->connect("id_pressed", callable_mp(this, &GPUParticles3DEditor::_menu_option)); + + generate_aabb = memnew(ConfirmationDialog); + generate_aabb->set_title(TTR("Generate Visibility AABB")); + VBoxContainer *genvb = memnew(VBoxContainer); + generate_aabb->add_child(genvb); + generate_seconds = memnew(SpinBox); + genvb->add_margin_child(TTR("Generation Time (sec):"), generate_seconds); + generate_seconds->set_min(0.1); + generate_seconds->set_max(25); + generate_seconds->set_value(2); + + add_child(generate_aabb); + + generate_aabb->connect("confirmed", callable_mp(this, &GPUParticles3DEditor::_generate_aabb)); +} + +void GPUParticles3DEditorPlugin::edit(Object *p_object) { + + particles_editor->edit(Object::cast_to(p_object)); +} + +bool GPUParticles3DEditorPlugin::handles(Object *p_object) const { + + return p_object->is_class("GPUParticles3D"); +} + +void GPUParticles3DEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + particles_editor->show(); + particles_editor->particles_editor_hb->show(); + } else { + particles_editor->particles_editor_hb->hide(); + particles_editor->hide(); + particles_editor->edit(NULL); + } +} + +GPUParticles3DEditorPlugin::GPUParticles3DEditorPlugin(EditorNode *p_node) { + + editor = p_node; + particles_editor = memnew(GPUParticles3DEditor); + editor->get_viewport()->add_child(particles_editor); + + particles_editor->hide(); +} + +GPUParticles3DEditorPlugin::~GPUParticles3DEditorPlugin() { +} diff --git a/editor/plugins/gpu_particles_3d_editor_plugin.h b/editor/plugins/gpu_particles_3d_editor_plugin.h new file mode 100644 index 0000000000..d730457d01 --- /dev/null +++ b/editor/plugins/gpu_particles_3d_editor_plugin.h @@ -0,0 +1,121 @@ +/*************************************************************************/ +/* gpu_particles_3d_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef PARTICLES_EDITOR_PLUGIN_H +#define PARTICLES_EDITOR_PLUGIN_H + +#include "editor/editor_node.h" +#include "editor/editor_plugin.h" +#include "scene/3d/gpu_particles_3d.h" +#include "scene/gui/spin_box.h" + +class GPUParticles3DEditorBase : public Control { + + GDCLASS(GPUParticles3DEditorBase, Control); + +protected: + Node3D *base_node; + Panel *panel; + MenuButton *options; + HBoxContainer *particles_editor_hb; + + SceneTreeDialog *emission_tree_dialog; + + ConfirmationDialog *emission_dialog; + SpinBox *emission_amount; + OptionButton *emission_fill; + + Vector geometry; + + bool _generate(Vector &points, Vector &normals); + virtual void _generate_emission_points() = 0; + void _node_selected(const NodePath &p_path); + + static void _bind_methods(); + +public: + GPUParticles3DEditorBase(); +}; + +class GPUParticles3DEditor : public GPUParticles3DEditorBase { + + GDCLASS(GPUParticles3DEditor, GPUParticles3DEditorBase); + + ConfirmationDialog *generate_aabb; + SpinBox *generate_seconds; + GPUParticles3D *node; + + enum Menu { + + MENU_OPTION_GENERATE_AABB, + MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE, + MENU_OPTION_CLEAR_EMISSION_VOLUME, + MENU_OPTION_CONVERT_TO_CPU_PARTICLES, + MENU_OPTION_RESTART, + + }; + + void _generate_aabb(); + + void _menu_option(int); + + friend class GPUParticles3DEditorPlugin; + + virtual void _generate_emission_points(); + +protected: + void _notification(int p_notification); + void _node_removed(Node *p_node); + static void _bind_methods(); + +public: + void edit(GPUParticles3D *p_particles); + GPUParticles3DEditor(); +}; + +class GPUParticles3DEditorPlugin : public EditorPlugin { + + GDCLASS(GPUParticles3DEditorPlugin, EditorPlugin); + + GPUParticles3DEditor *particles_editor; + EditorNode *editor; + +public: + virtual String get_name() const { return "Particles"; } + bool has_main_screen() const { return false; } + virtual void edit(Object *p_object); + virtual bool handles(Object *p_object) const; + virtual void make_visible(bool p_visible); + + GPUParticles3DEditorPlugin(EditorNode *p_node); + ~GPUParticles3DEditorPlugin(); +}; + +#endif // PARTICLES_EDITOR_PLUGIN_H diff --git a/editor/plugins/gradient_editor_plugin.cpp b/editor/plugins/gradient_editor_plugin.cpp index ff03fcf159..54b7840124 100644 --- a/editor/plugins/gradient_editor_plugin.cpp +++ b/editor/plugins/gradient_editor_plugin.cpp @@ -32,7 +32,7 @@ #include "canvas_item_editor_plugin.h" #include "editor/editor_scale.h" -#include "spatial_editor_plugin.h" +#include "node_3d_editor_plugin.h" Size2 GradientEditor::get_minimum_size() const { return Size2(0, 60) * EDSCALE; diff --git a/editor/plugins/material_editor_plugin.cpp b/editor/plugins/material_editor_plugin.cpp index 9fe9675c69..8129b9bd86 100644 --- a/editor/plugins/material_editor_plugin.cpp +++ b/editor/plugins/material_editor_plugin.cpp @@ -115,7 +115,7 @@ MaterialEditor::MaterialEditor() { add_child(vc); vc->set_anchors_and_margins_preset(PRESET_WIDE); viewport = memnew(SubViewport); - Ref world; + Ref world; world.instance(); viewport->set_world(world); //use own world vc->add_child(viewport); diff --git a/editor/plugins/mesh_editor_plugin.cpp b/editor/plugins/mesh_editor_plugin.cpp index d7dd777cd3..a8b455fdd2 100644 --- a/editor/plugins/mesh_editor_plugin.cpp +++ b/editor/plugins/mesh_editor_plugin.cpp @@ -116,7 +116,7 @@ void MeshEditor::_bind_methods() { MeshEditor::MeshEditor() { viewport = memnew(SubViewport); - Ref world; + Ref world; world.instance(); viewport->set_world(world); //use own world add_child(viewport); diff --git a/editor/plugins/mesh_instance_3d_editor_plugin.cpp b/editor/plugins/mesh_instance_3d_editor_plugin.cpp new file mode 100644 index 0000000000..a4e7b2df08 --- /dev/null +++ b/editor/plugins/mesh_instance_3d_editor_plugin.cpp @@ -0,0 +1,531 @@ +/*************************************************************************/ +/* mesh_instance_3d_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "mesh_instance_3d_editor_plugin.h" + +#include "editor/editor_scale.h" +#include "node_3d_editor_plugin.h" +#include "scene/3d/collision_shape_3d.h" +#include "scene/3d/navigation_region_3d.h" +#include "scene/3d/physics_body_3d.h" +#include "scene/gui/box_container.h" + +void MeshInstance3DEditor::_node_removed(Node *p_node) { + + if (p_node == node) { + node = NULL; + options->hide(); + } +} + +void MeshInstance3DEditor::edit(MeshInstance3D *p_mesh) { + + node = p_mesh; +} + +void MeshInstance3DEditor::_menu_option(int p_option) { + + Ref mesh = node->get_mesh(); + if (mesh.is_null()) { + err_dialog->set_text(TTR("Mesh is empty!")); + err_dialog->popup_centered(); + return; + } + + switch (p_option) { + case MENU_OPTION_CREATE_STATIC_TRIMESH_BODY: { + + EditorSelection *editor_selection = EditorNode::get_singleton()->get_editor_selection(); + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + + List selection = editor_selection->get_selected_node_list(); + + if (selection.empty()) { + Ref shape = mesh->create_trimesh_shape(); + if (shape.is_null()) { + err_dialog->set_text(TTR("Couldn't create a Trimesh collision shape.")); + err_dialog->popup_centered(); + return; + } + + CollisionShape3D *cshape = memnew(CollisionShape3D); + cshape->set_shape(shape); + StaticBody3D *body = memnew(StaticBody3D); + body->add_child(cshape); + + Node *owner = node == get_tree()->get_edited_scene_root() ? node : node->get_owner(); + + ur->create_action(TTR("Create Static Trimesh Body")); + ur->add_do_method(node, "add_child", body); + ur->add_do_method(body, "set_owner", owner); + ur->add_do_method(cshape, "set_owner", owner); + ur->add_do_reference(body); + ur->add_undo_method(node, "remove_child", body); + ur->commit_action(); + return; + } + + ur->create_action(TTR("Create Static Trimesh Body")); + + for (List::Element *E = selection.front(); E; E = E->next()) { + + MeshInstance3D *instance = Object::cast_to(E->get()); + if (!instance) + continue; + + Ref m = instance->get_mesh(); + if (m.is_null()) + continue; + + Ref shape = m->create_trimesh_shape(); + if (shape.is_null()) + continue; + + CollisionShape3D *cshape = memnew(CollisionShape3D); + cshape->set_shape(shape); + StaticBody3D *body = memnew(StaticBody3D); + body->add_child(cshape); + + Node *owner = instance == get_tree()->get_edited_scene_root() ? instance : instance->get_owner(); + + ur->add_do_method(instance, "add_child", body); + ur->add_do_method(body, "set_owner", owner); + ur->add_do_method(cshape, "set_owner", owner); + ur->add_do_reference(body); + ur->add_undo_method(instance, "remove_child", body); + } + + ur->commit_action(); + + } break; + + case MENU_OPTION_CREATE_TRIMESH_COLLISION_SHAPE: { + + if (node == get_tree()->get_edited_scene_root()) { + err_dialog->set_text(TTR("This doesn't work on scene root!")); + err_dialog->popup_centered(); + return; + } + + Ref shape = mesh->create_trimesh_shape(); + if (shape.is_null()) + return; + + CollisionShape3D *cshape = memnew(CollisionShape3D); + cshape->set_shape(shape); + + Node *owner = node->get_owner(); + + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + + ur->create_action(TTR("Create Trimesh Static Shape")); + + ur->add_do_method(node->get_parent(), "add_child", cshape); + ur->add_do_method(node->get_parent(), "move_child", cshape, node->get_index() + 1); + ur->add_do_method(cshape, "set_owner", owner); + ur->add_do_reference(cshape); + ur->add_undo_method(node->get_parent(), "remove_child", cshape); + ur->commit_action(); + } break; + case MENU_OPTION_CREATE_SINGLE_CONVEX_COLLISION_SHAPE: { + + if (node == get_tree()->get_edited_scene_root()) { + err_dialog->set_text(TTR("Can't create a single convex collision shape for the scene root.")); + err_dialog->popup_centered(); + return; + } + + Ref shape = mesh->create_convex_shape(); + + if (shape.is_null()) { + err_dialog->set_text(TTR("Couldn't create a single convex collision shape.")); + err_dialog->popup_centered(); + return; + } + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + + ur->create_action(TTR("Create Single Convex Shape")); + + CollisionShape3D *cshape = memnew(CollisionShape3D); + cshape->set_shape(shape); + cshape->set_transform(node->get_transform()); + + Node *owner = node->get_owner(); + + ur->add_do_method(node->get_parent(), "add_child", cshape); + ur->add_do_method(node->get_parent(), "move_child", cshape, node->get_index() + 1); + ur->add_do_method(cshape, "set_owner", owner); + ur->add_do_reference(cshape); + ur->add_undo_method(node->get_parent(), "remove_child", cshape); + + ur->commit_action(); + + } break; + case MENU_OPTION_CREATE_MULTIPLE_CONVEX_COLLISION_SHAPES: { + + if (node == get_tree()->get_edited_scene_root()) { + err_dialog->set_text(TTR("Can't create multiple convex collision shapes for the scene root.")); + err_dialog->popup_centered(); + return; + } + + Vector> shapes = mesh->convex_decompose(); + + if (!shapes.size()) { + err_dialog->set_text(TTR("Couldn't create any collision shapes.")); + err_dialog->popup_centered(); + return; + } + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + + ur->create_action(TTR("Create Multiple Convex Shapes")); + + for (int i = 0; i < shapes.size(); i++) { + + CollisionShape3D *cshape = memnew(CollisionShape3D); + cshape->set_shape(shapes[i]); + cshape->set_transform(node->get_transform()); + + Node *owner = node->get_owner(); + + ur->add_do_method(node->get_parent(), "add_child", cshape); + ur->add_do_method(node->get_parent(), "move_child", cshape, node->get_index() + 1); + ur->add_do_method(cshape, "set_owner", owner); + ur->add_do_reference(cshape); + ur->add_undo_method(node->get_parent(), "remove_child", cshape); + } + ur->commit_action(); + + } break; + + case MENU_OPTION_CREATE_NAVMESH: { + + Ref nmesh = memnew(NavigationMesh); + + if (nmesh.is_null()) + return; + + nmesh->create_from_mesh(mesh); + NavigationRegion3D *nmi = memnew(NavigationRegion3D); + nmi->set_navigation_mesh(nmesh); + + Node *owner = node == get_tree()->get_edited_scene_root() ? node : node->get_owner(); + + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Create Navigation Mesh")); + + ur->add_do_method(node, "add_child", nmi); + ur->add_do_method(nmi, "set_owner", owner); + + ur->add_do_reference(nmi); + ur->add_undo_method(node, "remove_child", nmi); + ur->commit_action(); + } break; + + case MENU_OPTION_CREATE_OUTLINE_MESH: { + + outline_dialog->popup_centered(Vector2(200, 90)); + } break; + case MENU_OPTION_CREATE_UV2: { + + Ref mesh2 = node->get_mesh(); + if (!mesh2.is_valid()) { + err_dialog->set_text(TTR("Contained Mesh is not of type ArrayMesh.")); + err_dialog->popup_centered(); + return; + } + + Error err = mesh2->lightmap_unwrap(node->get_global_transform()); + if (err != OK) { + err_dialog->set_text(TTR("UV Unwrap failed, mesh may not be manifold?")); + err_dialog->popup_centered(); + return; + } + + } break; + case MENU_OPTION_DEBUG_UV1: { + Ref mesh2 = node->get_mesh(); + if (!mesh2.is_valid()) { + err_dialog->set_text(TTR("No mesh to debug.")); + err_dialog->popup_centered(); + return; + } + _create_uv_lines(0); + } break; + case MENU_OPTION_DEBUG_UV2: { + Ref mesh2 = node->get_mesh(); + if (!mesh2.is_valid()) { + err_dialog->set_text(TTR("No mesh to debug.")); + err_dialog->popup_centered(); + return; + } + _create_uv_lines(1); + } break; + } +} + +struct MeshInstance3DEditorEdgeSort { + + Vector2 a; + Vector2 b; + + bool operator<(const MeshInstance3DEditorEdgeSort &p_b) const { + if (a == p_b.a) + return b < p_b.b; + else + return a < p_b.a; + } + + MeshInstance3DEditorEdgeSort() {} + MeshInstance3DEditorEdgeSort(const Vector2 &p_a, const Vector2 &p_b) { + if (p_a < p_b) { + a = p_a; + b = p_b; + } else { + b = p_a; + a = p_b; + } + } +}; + +void MeshInstance3DEditor::_create_uv_lines(int p_layer) { + + Ref mesh = node->get_mesh(); + ERR_FAIL_COND(!mesh.is_valid()); + + Set edges; + uv_lines.clear(); + for (int i = 0; i < mesh->get_surface_count(); i++) { + if (mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) + continue; + Array a = mesh->surface_get_arrays(i); + + Vector uv = a[p_layer == 0 ? Mesh::ARRAY_TEX_UV : Mesh::ARRAY_TEX_UV2]; + if (uv.size() == 0) { + err_dialog->set_text(TTR("Model has no UV in this layer")); + err_dialog->popup_centered(); + return; + } + + const Vector2 *r = uv.ptr(); + + Vector indices = a[Mesh::ARRAY_INDEX]; + const int *ri; + + int ic; + bool use_indices; + + if (indices.size()) { + ic = indices.size(); + ri = indices.ptr(); + use_indices = true; + } else { + ic = uv.size(); + use_indices = false; + } + + for (int j = 0; j < ic; j += 3) { + + for (int k = 0; k < 3; k++) { + + MeshInstance3DEditorEdgeSort edge; + if (use_indices) { + edge.a = r[ri[j + k]]; + edge.b = r[ri[j + ((k + 1) % 3)]]; + } else { + edge.a = r[j + k]; + edge.b = r[j + ((k + 1) % 3)]; + } + + if (edges.has(edge)) + continue; + + uv_lines.push_back(edge.a); + uv_lines.push_back(edge.b); + edges.insert(edge); + } + } + } + + debug_uv_dialog->popup_centered(); +} + +void MeshInstance3DEditor::_debug_uv_draw() { + + if (uv_lines.size() == 0) + return; + + debug_uv->set_clip_contents(true); + debug_uv->draw_rect(Rect2(Vector2(), debug_uv->get_size()), Color(0.2, 0.2, 0.0)); + debug_uv->draw_set_transform(Vector2(), 0, debug_uv->get_size()); + debug_uv->draw_multiline(uv_lines, Color(1.0, 0.8, 0.7)); +} + +void MeshInstance3DEditor::_create_outline_mesh() { + + Ref mesh = node->get_mesh(); + if (mesh.is_null()) { + err_dialog->set_text(TTR("MeshInstance lacks a Mesh!")); + err_dialog->popup_centered(); + return; + } + + if (mesh->get_surface_count() == 0) { + err_dialog->set_text(TTR("Mesh has not surface to create outlines from!")); + err_dialog->popup_centered(); + return; + } else if (mesh->get_surface_count() == 1 && mesh->surface_get_primitive_type(0) != Mesh::PRIMITIVE_TRIANGLES) { + err_dialog->set_text(TTR("Mesh primitive type is not PRIMITIVE_TRIANGLES!")); + err_dialog->popup_centered(); + return; + } + + Ref mesho = mesh->create_outline(outline_size->get_value()); + + if (mesho.is_null()) { + err_dialog->set_text(TTR("Could not create outline!")); + err_dialog->popup_centered(); + return; + } + + MeshInstance3D *mi = memnew(MeshInstance3D); + mi->set_mesh(mesho); + Node *owner = node->get_owner(); + if (get_tree()->get_edited_scene_root() == node) { + owner = node; + } + + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + + ur->create_action(TTR("Create Outline")); + + ur->add_do_method(node, "add_child", mi); + ur->add_do_method(mi, "set_owner", owner); + + ur->add_do_reference(mi); + ur->add_undo_method(node, "remove_child", mi); + ur->commit_action(); +} + +void MeshInstance3DEditor::_bind_methods() { +} + +MeshInstance3DEditor::MeshInstance3DEditor() { + + options = memnew(MenuButton); + options->set_switch_on_hover(true); + Node3DEditor::get_singleton()->add_control_to_menu_panel(options); + + options->set_text(TTR("Mesh")); + options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("MeshInstance3D", "EditorIcons")); + + options->get_popup()->add_item(TTR("Create Trimesh Static Body"), MENU_OPTION_CREATE_STATIC_TRIMESH_BODY); + options->get_popup()->set_item_tooltip(options->get_popup()->get_item_count() - 1, TTR("Creates a StaticBody and assigns a polygon-based collision shape to it automatically.\nThis is the most accurate (but slowest) option for collision detection.")); + options->get_popup()->add_separator(); + options->get_popup()->add_item(TTR("Create Trimesh Collision Sibling"), MENU_OPTION_CREATE_TRIMESH_COLLISION_SHAPE); + options->get_popup()->set_item_tooltip(options->get_popup()->get_item_count() - 1, TTR("Creates a polygon-based collision shape.\nThis is the most accurate (but slowest) option for collision detection.")); + options->get_popup()->add_item(TTR("Create Single Convex Collision Sibling"), MENU_OPTION_CREATE_SINGLE_CONVEX_COLLISION_SHAPE); + options->get_popup()->set_item_tooltip(options->get_popup()->get_item_count() - 1, TTR("Creates a single convex collision shape.\nThis is the fastest (but least accurate) option for collision detection.")); + options->get_popup()->add_item(TTR("Create Multiple Convex Collision Siblings"), MENU_OPTION_CREATE_MULTIPLE_CONVEX_COLLISION_SHAPES); + options->get_popup()->set_item_tooltip(options->get_popup()->get_item_count() - 1, TTR("Creates a polygon-based collision shape.\nThis is a performance middle-ground between the two above options.")); + options->get_popup()->add_separator(); + options->get_popup()->add_item(TTR("Create Navigation Mesh"), MENU_OPTION_CREATE_NAVMESH); + options->get_popup()->add_separator(); + options->get_popup()->add_item(TTR("Create Outline Mesh..."), MENU_OPTION_CREATE_OUTLINE_MESH); + options->get_popup()->set_item_tooltip(options->get_popup()->get_item_count() - 1, TTR("Creates a static outline mesh. The outline mesh will have its normals flipped automatically.\nThis can be used instead of the StandardMaterial Grow property when using that property isn't possible.")); + options->get_popup()->add_separator(); + options->get_popup()->add_item(TTR("View UV1"), MENU_OPTION_DEBUG_UV1); + options->get_popup()->add_item(TTR("View UV2"), MENU_OPTION_DEBUG_UV2); + options->get_popup()->add_item(TTR("Unwrap UV2 for Lightmap/AO"), MENU_OPTION_CREATE_UV2); + + options->get_popup()->connect("id_pressed", callable_mp(this, &MeshInstance3DEditor::_menu_option)); + + outline_dialog = memnew(ConfirmationDialog); + outline_dialog->set_title(TTR("Create Outline Mesh")); + outline_dialog->get_ok()->set_text(TTR("Create")); + + VBoxContainer *outline_dialog_vbc = memnew(VBoxContainer); + outline_dialog->add_child(outline_dialog_vbc); + //outline_dialog->set_child_rect(outline_dialog_vbc); + + outline_size = memnew(SpinBox); + outline_size->set_min(0.001); + outline_size->set_max(1024); + outline_size->set_step(0.001); + outline_size->set_value(0.05); + outline_dialog_vbc->add_margin_child(TTR("Outline Size:"), outline_size); + + add_child(outline_dialog); + outline_dialog->connect("confirmed", callable_mp(this, &MeshInstance3DEditor::_create_outline_mesh)); + + err_dialog = memnew(AcceptDialog); + add_child(err_dialog); + + debug_uv_dialog = memnew(AcceptDialog); + debug_uv_dialog->set_title(TTR("UV Channel Debug")); + add_child(debug_uv_dialog); + debug_uv = memnew(Control); + debug_uv->set_custom_minimum_size(Size2(600, 600) * EDSCALE); + debug_uv->connect("draw", callable_mp(this, &MeshInstance3DEditor::_debug_uv_draw)); + debug_uv_dialog->add_child(debug_uv); +} + +void MeshInstance3DEditorPlugin::edit(Object *p_object) { + + mesh_editor->edit(Object::cast_to(p_object)); +} + +bool MeshInstance3DEditorPlugin::handles(Object *p_object) const { + + return p_object->is_class("MeshInstance3D"); +} + +void MeshInstance3DEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + mesh_editor->options->show(); + } else { + + mesh_editor->options->hide(); + mesh_editor->edit(NULL); + } +} + +MeshInstance3DEditorPlugin::MeshInstance3DEditorPlugin(EditorNode *p_node) { + + editor = p_node; + mesh_editor = memnew(MeshInstance3DEditor); + editor->get_viewport()->add_child(mesh_editor); + + mesh_editor->options->hide(); +} + +MeshInstance3DEditorPlugin::~MeshInstance3DEditorPlugin() { +} diff --git a/editor/plugins/mesh_instance_3d_editor_plugin.h b/editor/plugins/mesh_instance_3d_editor_plugin.h new file mode 100644 index 0000000000..a5d90c42d5 --- /dev/null +++ b/editor/plugins/mesh_instance_3d_editor_plugin.h @@ -0,0 +1,104 @@ +/*************************************************************************/ +/* mesh_instance_3d_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef MESH_INSTANCE_EDITOR_PLUGIN_H +#define MESH_INSTANCE_EDITOR_PLUGIN_H + +#include "editor/editor_node.h" +#include "editor/editor_plugin.h" +#include "scene/3d/mesh_instance_3d.h" +#include "scene/gui/spin_box.h" + +class MeshInstance3DEditor : public Control { + + GDCLASS(MeshInstance3DEditor, Control); + + enum Menu { + + MENU_OPTION_CREATE_STATIC_TRIMESH_BODY, + MENU_OPTION_CREATE_TRIMESH_COLLISION_SHAPE, + MENU_OPTION_CREATE_SINGLE_CONVEX_COLLISION_SHAPE, + MENU_OPTION_CREATE_MULTIPLE_CONVEX_COLLISION_SHAPES, + MENU_OPTION_CREATE_NAVMESH, + MENU_OPTION_CREATE_OUTLINE_MESH, + MENU_OPTION_CREATE_UV2, + MENU_OPTION_DEBUG_UV1, + MENU_OPTION_DEBUG_UV2, + }; + + MeshInstance3D *node; + + MenuButton *options; + + ConfirmationDialog *outline_dialog; + SpinBox *outline_size; + + AcceptDialog *err_dialog; + + AcceptDialog *debug_uv_dialog; + Control *debug_uv; + Vector uv_lines; + + void _menu_option(int p_option); + void _create_outline_mesh(); + + void _create_uv_lines(int p_layer); + friend class MeshInstance3DEditorPlugin; + + void _debug_uv_draw(); + +protected: + void _node_removed(Node *p_node); + static void _bind_methods(); + +public: + void edit(MeshInstance3D *p_mesh); + MeshInstance3DEditor(); +}; + +class MeshInstance3DEditorPlugin : public EditorPlugin { + + GDCLASS(MeshInstance3DEditorPlugin, EditorPlugin); + + MeshInstance3DEditor *mesh_editor; + EditorNode *editor; + +public: + virtual String get_name() const { return "MeshInstance3D"; } + bool has_main_screen() const { return false; } + virtual void edit(Object *p_object); + virtual bool handles(Object *p_object) const; + virtual void make_visible(bool p_visible); + + MeshInstance3DEditorPlugin(EditorNode *p_node); + ~MeshInstance3DEditorPlugin(); +}; + +#endif // MESH_EDITOR_PLUGIN_H diff --git a/editor/plugins/mesh_instance_editor_plugin.cpp b/editor/plugins/mesh_instance_editor_plugin.cpp deleted file mode 100644 index dce69e53ca..0000000000 --- a/editor/plugins/mesh_instance_editor_plugin.cpp +++ /dev/null @@ -1,531 +0,0 @@ -/*************************************************************************/ -/* mesh_instance_editor_plugin.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "mesh_instance_editor_plugin.h" - -#include "editor/editor_scale.h" -#include "scene/3d/collision_shape_3d.h" -#include "scene/3d/navigation_region_3d.h" -#include "scene/3d/physics_body_3d.h" -#include "scene/gui/box_container.h" -#include "spatial_editor_plugin.h" - -void MeshInstanceEditor::_node_removed(Node *p_node) { - - if (p_node == node) { - node = NULL; - options->hide(); - } -} - -void MeshInstanceEditor::edit(MeshInstance3D *p_mesh) { - - node = p_mesh; -} - -void MeshInstanceEditor::_menu_option(int p_option) { - - Ref mesh = node->get_mesh(); - if (mesh.is_null()) { - err_dialog->set_text(TTR("Mesh is empty!")); - err_dialog->popup_centered(); - return; - } - - switch (p_option) { - case MENU_OPTION_CREATE_STATIC_TRIMESH_BODY: { - - EditorSelection *editor_selection = EditorNode::get_singleton()->get_editor_selection(); - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); - - List selection = editor_selection->get_selected_node_list(); - - if (selection.empty()) { - Ref shape = mesh->create_trimesh_shape(); - if (shape.is_null()) { - err_dialog->set_text(TTR("Couldn't create a Trimesh collision shape.")); - err_dialog->popup_centered(); - return; - } - - CollisionShape3D *cshape = memnew(CollisionShape3D); - cshape->set_shape(shape); - StaticBody3D *body = memnew(StaticBody3D); - body->add_child(cshape); - - Node *owner = node == get_tree()->get_edited_scene_root() ? node : node->get_owner(); - - ur->create_action(TTR("Create Static Trimesh Body")); - ur->add_do_method(node, "add_child", body); - ur->add_do_method(body, "set_owner", owner); - ur->add_do_method(cshape, "set_owner", owner); - ur->add_do_reference(body); - ur->add_undo_method(node, "remove_child", body); - ur->commit_action(); - return; - } - - ur->create_action(TTR("Create Static Trimesh Body")); - - for (List::Element *E = selection.front(); E; E = E->next()) { - - MeshInstance3D *instance = Object::cast_to(E->get()); - if (!instance) - continue; - - Ref m = instance->get_mesh(); - if (m.is_null()) - continue; - - Ref shape = m->create_trimesh_shape(); - if (shape.is_null()) - continue; - - CollisionShape3D *cshape = memnew(CollisionShape3D); - cshape->set_shape(shape); - StaticBody3D *body = memnew(StaticBody3D); - body->add_child(cshape); - - Node *owner = instance == get_tree()->get_edited_scene_root() ? instance : instance->get_owner(); - - ur->add_do_method(instance, "add_child", body); - ur->add_do_method(body, "set_owner", owner); - ur->add_do_method(cshape, "set_owner", owner); - ur->add_do_reference(body); - ur->add_undo_method(instance, "remove_child", body); - } - - ur->commit_action(); - - } break; - - case MENU_OPTION_CREATE_TRIMESH_COLLISION_SHAPE: { - - if (node == get_tree()->get_edited_scene_root()) { - err_dialog->set_text(TTR("This doesn't work on scene root!")); - err_dialog->popup_centered(); - return; - } - - Ref shape = mesh->create_trimesh_shape(); - if (shape.is_null()) - return; - - CollisionShape3D *cshape = memnew(CollisionShape3D); - cshape->set_shape(shape); - - Node *owner = node->get_owner(); - - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); - - ur->create_action(TTR("Create Trimesh Static Shape")); - - ur->add_do_method(node->get_parent(), "add_child", cshape); - ur->add_do_method(node->get_parent(), "move_child", cshape, node->get_index() + 1); - ur->add_do_method(cshape, "set_owner", owner); - ur->add_do_reference(cshape); - ur->add_undo_method(node->get_parent(), "remove_child", cshape); - ur->commit_action(); - } break; - case MENU_OPTION_CREATE_SINGLE_CONVEX_COLLISION_SHAPE: { - - if (node == get_tree()->get_edited_scene_root()) { - err_dialog->set_text(TTR("Can't create a single convex collision shape for the scene root.")); - err_dialog->popup_centered(); - return; - } - - Ref shape = mesh->create_convex_shape(); - - if (shape.is_null()) { - err_dialog->set_text(TTR("Couldn't create a single convex collision shape.")); - err_dialog->popup_centered(); - return; - } - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); - - ur->create_action(TTR("Create Single Convex Shape")); - - CollisionShape3D *cshape = memnew(CollisionShape3D); - cshape->set_shape(shape); - cshape->set_transform(node->get_transform()); - - Node *owner = node->get_owner(); - - ur->add_do_method(node->get_parent(), "add_child", cshape); - ur->add_do_method(node->get_parent(), "move_child", cshape, node->get_index() + 1); - ur->add_do_method(cshape, "set_owner", owner); - ur->add_do_reference(cshape); - ur->add_undo_method(node->get_parent(), "remove_child", cshape); - - ur->commit_action(); - - } break; - case MENU_OPTION_CREATE_MULTIPLE_CONVEX_COLLISION_SHAPES: { - - if (node == get_tree()->get_edited_scene_root()) { - err_dialog->set_text(TTR("Can't create multiple convex collision shapes for the scene root.")); - err_dialog->popup_centered(); - return; - } - - Vector> shapes = mesh->convex_decompose(); - - if (!shapes.size()) { - err_dialog->set_text(TTR("Couldn't create any collision shapes.")); - err_dialog->popup_centered(); - return; - } - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); - - ur->create_action(TTR("Create Multiple Convex Shapes")); - - for (int i = 0; i < shapes.size(); i++) { - - CollisionShape3D *cshape = memnew(CollisionShape3D); - cshape->set_shape(shapes[i]); - cshape->set_transform(node->get_transform()); - - Node *owner = node->get_owner(); - - ur->add_do_method(node->get_parent(), "add_child", cshape); - ur->add_do_method(node->get_parent(), "move_child", cshape, node->get_index() + 1); - ur->add_do_method(cshape, "set_owner", owner); - ur->add_do_reference(cshape); - ur->add_undo_method(node->get_parent(), "remove_child", cshape); - } - ur->commit_action(); - - } break; - - case MENU_OPTION_CREATE_NAVMESH: { - - Ref nmesh = memnew(NavigationMesh); - - if (nmesh.is_null()) - return; - - nmesh->create_from_mesh(mesh); - NavigationRegion3D *nmi = memnew(NavigationRegion3D); - nmi->set_navigation_mesh(nmesh); - - Node *owner = node == get_tree()->get_edited_scene_root() ? node : node->get_owner(); - - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); - ur->create_action(TTR("Create Navigation Mesh")); - - ur->add_do_method(node, "add_child", nmi); - ur->add_do_method(nmi, "set_owner", owner); - - ur->add_do_reference(nmi); - ur->add_undo_method(node, "remove_child", nmi); - ur->commit_action(); - } break; - - case MENU_OPTION_CREATE_OUTLINE_MESH: { - - outline_dialog->popup_centered(Vector2(200, 90)); - } break; - case MENU_OPTION_CREATE_UV2: { - - Ref mesh2 = node->get_mesh(); - if (!mesh2.is_valid()) { - err_dialog->set_text(TTR("Contained Mesh is not of type ArrayMesh.")); - err_dialog->popup_centered(); - return; - } - - Error err = mesh2->lightmap_unwrap(node->get_global_transform()); - if (err != OK) { - err_dialog->set_text(TTR("UV Unwrap failed, mesh may not be manifold?")); - err_dialog->popup_centered(); - return; - } - - } break; - case MENU_OPTION_DEBUG_UV1: { - Ref mesh2 = node->get_mesh(); - if (!mesh2.is_valid()) { - err_dialog->set_text(TTR("No mesh to debug.")); - err_dialog->popup_centered(); - return; - } - _create_uv_lines(0); - } break; - case MENU_OPTION_DEBUG_UV2: { - Ref mesh2 = node->get_mesh(); - if (!mesh2.is_valid()) { - err_dialog->set_text(TTR("No mesh to debug.")); - err_dialog->popup_centered(); - return; - } - _create_uv_lines(1); - } break; - } -} - -struct MeshInstanceEditorEdgeSort { - - Vector2 a; - Vector2 b; - - bool operator<(const MeshInstanceEditorEdgeSort &p_b) const { - if (a == p_b.a) - return b < p_b.b; - else - return a < p_b.a; - } - - MeshInstanceEditorEdgeSort() {} - MeshInstanceEditorEdgeSort(const Vector2 &p_a, const Vector2 &p_b) { - if (p_a < p_b) { - a = p_a; - b = p_b; - } else { - b = p_a; - a = p_b; - } - } -}; - -void MeshInstanceEditor::_create_uv_lines(int p_layer) { - - Ref mesh = node->get_mesh(); - ERR_FAIL_COND(!mesh.is_valid()); - - Set edges; - uv_lines.clear(); - for (int i = 0; i < mesh->get_surface_count(); i++) { - if (mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) - continue; - Array a = mesh->surface_get_arrays(i); - - Vector uv = a[p_layer == 0 ? Mesh::ARRAY_TEX_UV : Mesh::ARRAY_TEX_UV2]; - if (uv.size() == 0) { - err_dialog->set_text(TTR("Model has no UV in this layer")); - err_dialog->popup_centered(); - return; - } - - const Vector2 *r = uv.ptr(); - - Vector indices = a[Mesh::ARRAY_INDEX]; - const int *ri; - - int ic; - bool use_indices; - - if (indices.size()) { - ic = indices.size(); - ri = indices.ptr(); - use_indices = true; - } else { - ic = uv.size(); - use_indices = false; - } - - for (int j = 0; j < ic; j += 3) { - - for (int k = 0; k < 3; k++) { - - MeshInstanceEditorEdgeSort edge; - if (use_indices) { - edge.a = r[ri[j + k]]; - edge.b = r[ri[j + ((k + 1) % 3)]]; - } else { - edge.a = r[j + k]; - edge.b = r[j + ((k + 1) % 3)]; - } - - if (edges.has(edge)) - continue; - - uv_lines.push_back(edge.a); - uv_lines.push_back(edge.b); - edges.insert(edge); - } - } - } - - debug_uv_dialog->popup_centered(); -} - -void MeshInstanceEditor::_debug_uv_draw() { - - if (uv_lines.size() == 0) - return; - - debug_uv->set_clip_contents(true); - debug_uv->draw_rect(Rect2(Vector2(), debug_uv->get_size()), Color(0.2, 0.2, 0.0)); - debug_uv->draw_set_transform(Vector2(), 0, debug_uv->get_size()); - debug_uv->draw_multiline(uv_lines, Color(1.0, 0.8, 0.7)); -} - -void MeshInstanceEditor::_create_outline_mesh() { - - Ref mesh = node->get_mesh(); - if (mesh.is_null()) { - err_dialog->set_text(TTR("MeshInstance lacks a Mesh!")); - err_dialog->popup_centered(); - return; - } - - if (mesh->get_surface_count() == 0) { - err_dialog->set_text(TTR("Mesh has not surface to create outlines from!")); - err_dialog->popup_centered(); - return; - } else if (mesh->get_surface_count() == 1 && mesh->surface_get_primitive_type(0) != Mesh::PRIMITIVE_TRIANGLES) { - err_dialog->set_text(TTR("Mesh primitive type is not PRIMITIVE_TRIANGLES!")); - err_dialog->popup_centered(); - return; - } - - Ref mesho = mesh->create_outline(outline_size->get_value()); - - if (mesho.is_null()) { - err_dialog->set_text(TTR("Could not create outline!")); - err_dialog->popup_centered(); - return; - } - - MeshInstance3D *mi = memnew(MeshInstance3D); - mi->set_mesh(mesho); - Node *owner = node->get_owner(); - if (get_tree()->get_edited_scene_root() == node) { - owner = node; - } - - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); - - ur->create_action(TTR("Create Outline")); - - ur->add_do_method(node, "add_child", mi); - ur->add_do_method(mi, "set_owner", owner); - - ur->add_do_reference(mi); - ur->add_undo_method(node, "remove_child", mi); - ur->commit_action(); -} - -void MeshInstanceEditor::_bind_methods() { -} - -MeshInstanceEditor::MeshInstanceEditor() { - - options = memnew(MenuButton); - options->set_switch_on_hover(true); - Node3DEditor::get_singleton()->add_control_to_menu_panel(options); - - options->set_text(TTR("Mesh")); - options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("MeshInstance3D", "EditorIcons")); - - options->get_popup()->add_item(TTR("Create Trimesh Static Body"), MENU_OPTION_CREATE_STATIC_TRIMESH_BODY); - options->get_popup()->set_item_tooltip(options->get_popup()->get_item_count() - 1, TTR("Creates a StaticBody and assigns a polygon-based collision shape to it automatically.\nThis is the most accurate (but slowest) option for collision detection.")); - options->get_popup()->add_separator(); - options->get_popup()->add_item(TTR("Create Trimesh Collision Sibling"), MENU_OPTION_CREATE_TRIMESH_COLLISION_SHAPE); - options->get_popup()->set_item_tooltip(options->get_popup()->get_item_count() - 1, TTR("Creates a polygon-based collision shape.\nThis is the most accurate (but slowest) option for collision detection.")); - options->get_popup()->add_item(TTR("Create Single Convex Collision Sibling"), MENU_OPTION_CREATE_SINGLE_CONVEX_COLLISION_SHAPE); - options->get_popup()->set_item_tooltip(options->get_popup()->get_item_count() - 1, TTR("Creates a single convex collision shape.\nThis is the fastest (but least accurate) option for collision detection.")); - options->get_popup()->add_item(TTR("Create Multiple Convex Collision Siblings"), MENU_OPTION_CREATE_MULTIPLE_CONVEX_COLLISION_SHAPES); - options->get_popup()->set_item_tooltip(options->get_popup()->get_item_count() - 1, TTR("Creates a polygon-based collision shape.\nThis is a performance middle-ground between the two above options.")); - options->get_popup()->add_separator(); - options->get_popup()->add_item(TTR("Create Navigation Mesh"), MENU_OPTION_CREATE_NAVMESH); - options->get_popup()->add_separator(); - options->get_popup()->add_item(TTR("Create Outline Mesh..."), MENU_OPTION_CREATE_OUTLINE_MESH); - options->get_popup()->set_item_tooltip(options->get_popup()->get_item_count() - 1, TTR("Creates a static outline mesh. The outline mesh will have its normals flipped automatically.\nThis can be used instead of the StandardMaterial Grow property when using that property isn't possible.")); - options->get_popup()->add_separator(); - options->get_popup()->add_item(TTR("View UV1"), MENU_OPTION_DEBUG_UV1); - options->get_popup()->add_item(TTR("View UV2"), MENU_OPTION_DEBUG_UV2); - options->get_popup()->add_item(TTR("Unwrap UV2 for Lightmap/AO"), MENU_OPTION_CREATE_UV2); - - options->get_popup()->connect("id_pressed", callable_mp(this, &MeshInstanceEditor::_menu_option)); - - outline_dialog = memnew(ConfirmationDialog); - outline_dialog->set_title(TTR("Create Outline Mesh")); - outline_dialog->get_ok()->set_text(TTR("Create")); - - VBoxContainer *outline_dialog_vbc = memnew(VBoxContainer); - outline_dialog->add_child(outline_dialog_vbc); - //outline_dialog->set_child_rect(outline_dialog_vbc); - - outline_size = memnew(SpinBox); - outline_size->set_min(0.001); - outline_size->set_max(1024); - outline_size->set_step(0.001); - outline_size->set_value(0.05); - outline_dialog_vbc->add_margin_child(TTR("Outline Size:"), outline_size); - - add_child(outline_dialog); - outline_dialog->connect("confirmed", callable_mp(this, &MeshInstanceEditor::_create_outline_mesh)); - - err_dialog = memnew(AcceptDialog); - add_child(err_dialog); - - debug_uv_dialog = memnew(AcceptDialog); - debug_uv_dialog->set_title(TTR("UV Channel Debug")); - add_child(debug_uv_dialog); - debug_uv = memnew(Control); - debug_uv->set_custom_minimum_size(Size2(600, 600) * EDSCALE); - debug_uv->connect("draw", callable_mp(this, &MeshInstanceEditor::_debug_uv_draw)); - debug_uv_dialog->add_child(debug_uv); -} - -void MeshInstanceEditorPlugin::edit(Object *p_object) { - - mesh_editor->edit(Object::cast_to(p_object)); -} - -bool MeshInstanceEditorPlugin::handles(Object *p_object) const { - - return p_object->is_class("MeshInstance3D"); -} - -void MeshInstanceEditorPlugin::make_visible(bool p_visible) { - - if (p_visible) { - mesh_editor->options->show(); - } else { - - mesh_editor->options->hide(); - mesh_editor->edit(NULL); - } -} - -MeshInstanceEditorPlugin::MeshInstanceEditorPlugin(EditorNode *p_node) { - - editor = p_node; - mesh_editor = memnew(MeshInstanceEditor); - editor->get_viewport()->add_child(mesh_editor); - - mesh_editor->options->hide(); -} - -MeshInstanceEditorPlugin::~MeshInstanceEditorPlugin() { -} diff --git a/editor/plugins/mesh_instance_editor_plugin.h b/editor/plugins/mesh_instance_editor_plugin.h deleted file mode 100644 index 68354388ba..0000000000 --- a/editor/plugins/mesh_instance_editor_plugin.h +++ /dev/null @@ -1,104 +0,0 @@ -/*************************************************************************/ -/* mesh_instance_editor_plugin.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef MESH_INSTANCE_EDITOR_PLUGIN_H -#define MESH_INSTANCE_EDITOR_PLUGIN_H - -#include "editor/editor_node.h" -#include "editor/editor_plugin.h" -#include "scene/3d/mesh_instance_3d.h" -#include "scene/gui/spin_box.h" - -class MeshInstanceEditor : public Control { - - GDCLASS(MeshInstanceEditor, Control); - - enum Menu { - - MENU_OPTION_CREATE_STATIC_TRIMESH_BODY, - MENU_OPTION_CREATE_TRIMESH_COLLISION_SHAPE, - MENU_OPTION_CREATE_SINGLE_CONVEX_COLLISION_SHAPE, - MENU_OPTION_CREATE_MULTIPLE_CONVEX_COLLISION_SHAPES, - MENU_OPTION_CREATE_NAVMESH, - MENU_OPTION_CREATE_OUTLINE_MESH, - MENU_OPTION_CREATE_UV2, - MENU_OPTION_DEBUG_UV1, - MENU_OPTION_DEBUG_UV2, - }; - - MeshInstance3D *node; - - MenuButton *options; - - ConfirmationDialog *outline_dialog; - SpinBox *outline_size; - - AcceptDialog *err_dialog; - - AcceptDialog *debug_uv_dialog; - Control *debug_uv; - Vector uv_lines; - - void _menu_option(int p_option); - void _create_outline_mesh(); - - void _create_uv_lines(int p_layer); - friend class MeshInstanceEditorPlugin; - - void _debug_uv_draw(); - -protected: - void _node_removed(Node *p_node); - static void _bind_methods(); - -public: - void edit(MeshInstance3D *p_mesh); - MeshInstanceEditor(); -}; - -class MeshInstanceEditorPlugin : public EditorPlugin { - - GDCLASS(MeshInstanceEditorPlugin, EditorPlugin); - - MeshInstanceEditor *mesh_editor; - EditorNode *editor; - -public: - virtual String get_name() const { return "MeshInstance3D"; } - bool has_main_screen() const { return false; } - virtual void edit(Object *p_object); - virtual bool handles(Object *p_object) const; - virtual void make_visible(bool p_visible); - - MeshInstanceEditorPlugin(EditorNode *p_node); - ~MeshInstanceEditorPlugin(); -}; - -#endif // MESH_EDITOR_PLUGIN_H diff --git a/editor/plugins/mesh_library_editor_plugin.cpp b/editor/plugins/mesh_library_editor_plugin.cpp index 88a6254c5c..a3e3d88ae2 100644 --- a/editor/plugins/mesh_library_editor_plugin.cpp +++ b/editor/plugins/mesh_library_editor_plugin.cpp @@ -33,12 +33,12 @@ #include "editor/editor_node.h" #include "editor/editor_settings.h" #include "main/main.h" +#include "node_3d_editor_plugin.h" #include "scene/3d/mesh_instance_3d.h" #include "scene/3d/navigation_region_3d.h" #include "scene/3d/physics_body_3d.h" #include "scene/main/window.h" #include "scene/resources/packed_scene.h" -#include "spatial_editor_plugin.h" void MeshLibraryEditor::edit(const Ref &p_mesh_library) { diff --git a/editor/plugins/multimesh_editor_plugin.cpp b/editor/plugins/multimesh_editor_plugin.cpp index dd2781aea7..658e3ea565 100644 --- a/editor/plugins/multimesh_editor_plugin.cpp +++ b/editor/plugins/multimesh_editor_plugin.cpp @@ -30,9 +30,9 @@ #include "multimesh_editor_plugin.h" +#include "node_3d_editor_plugin.h" #include "scene/3d/mesh_instance_3d.h" #include "scene/gui/box_container.h" -#include "spatial_editor_plugin.h" void MultiMeshEditor::_node_removed(Node *p_node) { @@ -287,7 +287,7 @@ MultiMeshEditor::MultiMeshEditor() { Node3DEditor::get_singleton()->add_control_to_menu_panel(options); options->set_text("MultiMesh"); - options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("MultiMeshInstance", "EditorIcons")); + options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("MultiMeshInstance3D", "EditorIcons")); options->get_popup()->add_item(TTR("Populate Surface")); options->get_popup()->connect("id_pressed", callable_mp(this, &MultiMeshEditor::_menu_option)); diff --git a/editor/plugins/navigation_polygon_editor_plugin.h b/editor/plugins/navigation_polygon_editor_plugin.h index 10f8cbc0a5..0bc35e2498 100644 --- a/editor/plugins/navigation_polygon_editor_plugin.h +++ b/editor/plugins/navigation_polygon_editor_plugin.h @@ -32,7 +32,7 @@ #define NAVIGATIONPOLYGONEDITORPLUGIN_H #include "editor/plugins/abstract_polygon_2d_editor.h" -#include "scene/2d/navigation_polygon.h" +#include "scene/2d/navigation_region_2d.h" class NavigationPolygonEditor : public AbstractPolygon2DEditor { diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp new file mode 100644 index 0000000000..07eaf3f63f --- /dev/null +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -0,0 +1,6770 @@ +/*************************************************************************/ +/* node_3d_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "node_3d_editor_plugin.h" + +#include "core/input/input_filter.h" +#include "core/math/camera_matrix.h" +#include "core/os/keyboard.h" +#include "core/print_string.h" +#include "core/project_settings.h" +#include "core/sort_array.h" +#include "editor/debugger/editor_debugger_node.h" +#include "editor/editor_node.h" +#include "editor/editor_scale.h" +#include "editor/editor_settings.h" +#include "editor/node_3d_editor_gizmos.h" +#include "editor/plugins/animation_player_editor_plugin.h" +#include "editor/plugins/script_editor_plugin.h" +#include "scene/3d/camera_3d.h" +#include "scene/3d/collision_shape_3d.h" +#include "scene/3d/mesh_instance_3d.h" +#include "scene/3d/physics_body_3d.h" +#include "scene/3d/visual_instance_3d.h" +#include "scene/gui/viewport_container.h" +#include "scene/resources/packed_scene.h" +#include "scene/resources/surface_tool.h" +#include "servers/display_server.h" + +#define DISTANCE_DEFAULT 4 + +#define GIZMO_ARROW_SIZE 0.35 +#define GIZMO_RING_HALF_WIDTH 0.1 +#define GIZMO_SCALE_DEFAULT 0.15 +#define GIZMO_PLANE_SIZE 0.2 +#define GIZMO_PLANE_DST 0.3 +#define GIZMO_CIRCLE_SIZE 1.1 +#define GIZMO_SCALE_OFFSET (GIZMO_CIRCLE_SIZE + 0.3) +#define GIZMO_ARROW_OFFSET (GIZMO_CIRCLE_SIZE + 0.3) + +#define ZOOM_MIN_DISTANCE 0.001 +#define ZOOM_MULTIPLIER 1.08 +#define ZOOM_INDICATOR_DELAY_S 1.5 + +#define FREELOOK_MIN_SPEED 0.01 +#define FREELOOK_SPEED_MULTIPLIER 1.08 + +#define MIN_Z 0.01 +#define MAX_Z 1000000.0 + +#define MIN_FOV 0.01 +#define MAX_FOV 179 + +void ViewportRotationControl::_notification(int p_what) { + + if (p_what == NOTIFICATION_ENTER_TREE) { + axis_menu_options.clear(); + axis_menu_options.push_back(Node3DEditorViewport::VIEW_RIGHT); + axis_menu_options.push_back(Node3DEditorViewport::VIEW_TOP); + axis_menu_options.push_back(Node3DEditorViewport::VIEW_FRONT); + axis_menu_options.push_back(Node3DEditorViewport::VIEW_LEFT); + axis_menu_options.push_back(Node3DEditorViewport::VIEW_BOTTOM); + axis_menu_options.push_back(Node3DEditorViewport::VIEW_REAR); + + axis_colors.clear(); + axis_colors.push_back(get_theme_color("axis_x_color", "Editor")); + axis_colors.push_back(get_theme_color("axis_y_color", "Editor")); + axis_colors.push_back(get_theme_color("axis_z_color", "Editor")); + update(); + + if (!is_connected("mouse_exited", callable_mp(this, &ViewportRotationControl::_on_mouse_exited))) { + connect("mouse_exited", callable_mp(this, &ViewportRotationControl::_on_mouse_exited)); + } + } + + if (p_what == NOTIFICATION_DRAW && viewport != nullptr) { + _draw(); + } +} + +void ViewportRotationControl::_draw() { + Vector2i center = get_size() / 2.0; + float radius = get_size().x / 2.0; + + if (focused_axis > -2 || orbiting) { + draw_circle(center, radius, Color(0.5, 0.5, 0.5, 0.25)); + } + + Vector axis_to_draw; + _get_sorted_axis(axis_to_draw); + for (int i = 0; i < axis_to_draw.size(); ++i) { + _draw_axis(axis_to_draw[i]); + } +} + +void ViewportRotationControl::_draw_axis(const Axis2D &p_axis) { + bool focused = focused_axis == p_axis.axis; + bool positive = p_axis.axis < 3; + bool front = (Math::abs(p_axis.z_axis) <= 0.001 && positive) || p_axis.z_axis > 0.001; + int direction = p_axis.axis % 3; + + Color axis_color = axis_colors[direction]; + + if (!front) { + axis_color = axis_color.darkened(0.4); + } + Color c = focused ? Color(0.9, 0.9, 0.9) : axis_color; + + if (positive) { + Vector2i center = get_size() / 2.0; + draw_line(center, p_axis.screen_point, c, 1.5 * EDSCALE); + } + + if (front) { + String axis_name = direction == 0 ? "X" : (direction == 1 ? "Y" : "Z"); + draw_circle(p_axis.screen_point, AXIS_CIRCLE_RADIUS, c); + draw_char(get_theme_font("rotation_control", "EditorFonts"), p_axis.screen_point + Vector2i(-4, 5) * EDSCALE, axis_name, "", Color(0.3, 0.3, 0.3)); + } else { + draw_circle(p_axis.screen_point, AXIS_CIRCLE_RADIUS * (0.55 + (0.2 * (1.0 + p_axis.z_axis))), c); + } +} + +void ViewportRotationControl::_get_sorted_axis(Vector &r_axis) { + Vector2i center = get_size() / 2.0; + float radius = get_size().x / 2.0; + + float axis_radius = radius - AXIS_CIRCLE_RADIUS - 2.0 * EDSCALE; + Basis camera_basis = viewport->to_camera_transform(viewport->cursor).get_basis().inverse(); + + for (int i = 0; i < 3; ++i) { + Vector3 axis_3d = camera_basis.get_axis(i); + Vector2i axis_vector = Vector2(axis_3d.x, -axis_3d.y) * axis_radius; + + if (Math::abs(axis_3d.z) < 1.0) { + Axis2D pos_axis; + pos_axis.axis = i; + pos_axis.screen_point = center + axis_vector; + pos_axis.z_axis = axis_3d.z; + r_axis.push_back(pos_axis); + + Axis2D neg_axis; + neg_axis.axis = i + 3; + neg_axis.screen_point = center - axis_vector; + neg_axis.z_axis = -axis_3d.z; + r_axis.push_back(neg_axis); + } else { + // Special case when the camera is aligned with one axis + Axis2D axis; + axis.axis = i + (axis_3d.z < 0 ? 0 : 3); + axis.screen_point = center; + axis.z_axis = 1.0; + r_axis.push_back(axis); + } + } + + r_axis.sort_custom(); +} + +void ViewportRotationControl::_gui_input(Ref p_event) { + const Ref mb = p_event; + if (mb.is_valid() && mb->get_button_index() == BUTTON_LEFT) { + Vector2 pos = mb->get_position(); + if (mb->is_pressed()) { + if (pos.distance_to(get_size() / 2.0) < get_size().x / 2.0) { + orbiting = true; + } + } else { + if (focused_axis > -1) { + viewport->_menu_option(axis_menu_options[focused_axis]); + _update_focus(); + } + orbiting = false; + } + } + + const Ref mm = p_event; + if (mm.is_valid()) { + if (orbiting) { + viewport->_nav_orbit(mm, viewport->_get_warped_mouse_motion(mm)); + focused_axis = -1; + } else { + _update_focus(); + } + } +} + +void ViewportRotationControl::_update_focus() { + int original_focus = focused_axis; + focused_axis = -2; + Vector2 mouse_pos = get_local_mouse_position(); + + if (mouse_pos.distance_to(get_size() / 2.0) < get_size().x / 2.0) { + focused_axis = -1; + } + + Vector axes; + _get_sorted_axis(axes); + + for (int i = 0; i < axes.size(); i++) { + const Axis2D &axis = axes[i]; + if (mouse_pos.distance_to(axis.screen_point) < AXIS_CIRCLE_RADIUS) { + focused_axis = axis.axis; + } + } + + if (focused_axis != original_focus) { + update(); + } +} + +void ViewportRotationControl::_on_mouse_exited() { + focused_axis = -2; + update(); +} + +void ViewportRotationControl::set_viewport(Node3DEditorViewport *p_viewport) { + viewport = p_viewport; +} + +void ViewportRotationControl::_bind_methods() { + ClassDB::bind_method(D_METHOD("_gui_input"), &ViewportRotationControl::_gui_input); +} + +void Node3DEditorViewport::_update_camera(float p_interp_delta) { + + bool is_orthogonal = camera->get_projection() == Camera3D::PROJECTION_ORTHOGONAL; + + Cursor old_camera_cursor = camera_cursor; + camera_cursor = cursor; + + if (p_interp_delta > 0) { + + //------- + // Perform smoothing + + if (is_freelook_active()) { + + // Higher inertia should increase "lag" (lerp with factor between 0 and 1) + // Inertia of zero should produce instant movement (lerp with factor of 1) in this case it returns a really high value and gets clamped to 1. + real_t inertia = EDITOR_GET("editors/3d/freelook/freelook_inertia"); + inertia = MAX(0.001, inertia); + real_t factor = (1.0 / inertia) * p_interp_delta; + + // We interpolate a different point here, because in freelook mode the focus point (cursor.pos) orbits around eye_pos + camera_cursor.eye_pos = old_camera_cursor.eye_pos.linear_interpolate(cursor.eye_pos, CLAMP(factor, 0, 1)); + + float orbit_inertia = EDITOR_GET("editors/3d/navigation_feel/orbit_inertia"); + orbit_inertia = MAX(0.0001, orbit_inertia); + camera_cursor.x_rot = Math::lerp(old_camera_cursor.x_rot, cursor.x_rot, MIN(1.f, p_interp_delta * (1 / orbit_inertia))); + camera_cursor.y_rot = Math::lerp(old_camera_cursor.y_rot, cursor.y_rot, MIN(1.f, p_interp_delta * (1 / orbit_inertia))); + + if (Math::abs(camera_cursor.x_rot - cursor.x_rot) < 0.1) { + camera_cursor.x_rot = cursor.x_rot; + } + + if (Math::abs(camera_cursor.y_rot - cursor.y_rot) < 0.1) { + camera_cursor.y_rot = cursor.y_rot; + } + + Vector3 forward = to_camera_transform(camera_cursor).basis.xform(Vector3(0, 0, -1)); + camera_cursor.pos = camera_cursor.eye_pos + forward * camera_cursor.distance; + + } else { + + //when not being manipulated, move softly + float free_orbit_inertia = EDITOR_GET("editors/3d/navigation_feel/orbit_inertia"); + float free_translation_inertia = EDITOR_GET("editors/3d/navigation_feel/translation_inertia"); + //when being manipulated, move more quickly + float manip_orbit_inertia = EDITOR_GET("editors/3d/navigation_feel/manipulation_orbit_inertia"); + float manip_translation_inertia = EDITOR_GET("editors/3d/navigation_feel/manipulation_translation_inertia"); + + float zoom_inertia = EDITOR_GET("editors/3d/navigation_feel/zoom_inertia"); + + //determine if being manipulated + bool manipulated = InputFilter::get_singleton()->get_mouse_button_mask() & (2 | 4); + manipulated |= InputFilter::get_singleton()->is_key_pressed(KEY_SHIFT); + manipulated |= InputFilter::get_singleton()->is_key_pressed(KEY_ALT); + manipulated |= InputFilter::get_singleton()->is_key_pressed(KEY_CONTROL); + + float orbit_inertia = MAX(0.00001, manipulated ? manip_orbit_inertia : free_orbit_inertia); + float translation_inertia = MAX(0.0001, manipulated ? manip_translation_inertia : free_translation_inertia); + zoom_inertia = MAX(0.0001, zoom_inertia); + + camera_cursor.x_rot = Math::lerp(old_camera_cursor.x_rot, cursor.x_rot, MIN(1.f, p_interp_delta * (1 / orbit_inertia))); + camera_cursor.y_rot = Math::lerp(old_camera_cursor.y_rot, cursor.y_rot, MIN(1.f, p_interp_delta * (1 / orbit_inertia))); + + if (Math::abs(camera_cursor.x_rot - cursor.x_rot) < 0.1) { + camera_cursor.x_rot = cursor.x_rot; + } + + if (Math::abs(camera_cursor.y_rot - cursor.y_rot) < 0.1) { + camera_cursor.y_rot = cursor.y_rot; + } + + camera_cursor.pos = old_camera_cursor.pos.linear_interpolate(cursor.pos, MIN(1.f, p_interp_delta * (1 / translation_inertia))); + camera_cursor.distance = Math::lerp(old_camera_cursor.distance, cursor.distance, MIN(1.f, p_interp_delta * (1 / zoom_inertia))); + } + } + + //------- + // Apply camera transform + + float tolerance = 0.001; + bool equal = true; + if (Math::abs(old_camera_cursor.x_rot - camera_cursor.x_rot) > tolerance || Math::abs(old_camera_cursor.y_rot - camera_cursor.y_rot) > tolerance) { + equal = false; + } + + if (equal && old_camera_cursor.pos.distance_squared_to(camera_cursor.pos) > tolerance * tolerance) { + equal = false; + } + + if (equal && Math::abs(old_camera_cursor.distance - camera_cursor.distance) > tolerance) { + equal = false; + } + + if (!equal || p_interp_delta == 0 || is_freelook_active() || is_orthogonal != orthogonal) { + + camera->set_global_transform(to_camera_transform(camera_cursor)); + + if (orthogonal) { + float half_fov = Math::deg2rad(get_fov()) / 2.0; + float height = 2.0 * cursor.distance * Math::tan(half_fov); + camera->set_orthogonal(height, 0.1, 8192); + } else { + camera->set_perspective(get_fov(), get_znear(), get_zfar()); + } + + update_transform_gizmo_view(); + rotation_control->update(); + } +} + +Transform Node3DEditorViewport::to_camera_transform(const Cursor &p_cursor) const { + Transform camera_transform; + camera_transform.translate(p_cursor.pos); + camera_transform.basis.rotate(Vector3(1, 0, 0), -p_cursor.x_rot); + camera_transform.basis.rotate(Vector3(0, 1, 0), -p_cursor.y_rot); + + if (orthogonal) + camera_transform.translate(0, 0, 4096); + else + camera_transform.translate(0, 0, p_cursor.distance); + + return camera_transform; +} + +int Node3DEditorViewport::get_selected_count() const { + + Map &selection = editor_selection->get_selection(); + + int count = 0; + + for (Map::Element *E = selection.front(); E; E = E->next()) { + + Node3D *sp = Object::cast_to(E->key()); + if (!sp) + continue; + + Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data(sp); + if (!se) + continue; + + count++; + } + + return count; +} + +float Node3DEditorViewport::get_znear() const { + + return CLAMP(spatial_editor->get_znear(), MIN_Z, MAX_Z); +} +float Node3DEditorViewport::get_zfar() const { + + return CLAMP(spatial_editor->get_zfar(), MIN_Z, MAX_Z); +} +float Node3DEditorViewport::get_fov() const { + + return CLAMP(spatial_editor->get_fov(), MIN_FOV, MAX_FOV); +} + +Transform Node3DEditorViewport::_get_camera_transform() const { + + return camera->get_global_transform(); +} + +Vector3 Node3DEditorViewport::_get_camera_position() const { + + return _get_camera_transform().origin; +} + +Point2 Node3DEditorViewport::_point_to_screen(const Vector3 &p_point) { + + return camera->unproject_position(p_point) * viewport_container->get_stretch_shrink(); +} + +Vector3 Node3DEditorViewport::_get_ray_pos(const Vector2 &p_pos) const { + + return camera->project_ray_origin(p_pos / viewport_container->get_stretch_shrink()); +} + +Vector3 Node3DEditorViewport::_get_camera_normal() const { + + return -_get_camera_transform().basis.get_axis(2); +} + +Vector3 Node3DEditorViewport::_get_ray(const Vector2 &p_pos) const { + + return camera->project_ray_normal(p_pos / viewport_container->get_stretch_shrink()); +} + +void Node3DEditorViewport::_clear_selected() { + + editor_selection->clear(); +} + +void Node3DEditorViewport::_select_clicked(bool p_append, bool p_single, bool p_allow_locked) { + + if (clicked.is_null()) + return; + + Node *node = Object::cast_to(ObjectDB::get_instance(clicked)); + Node3D *selected = Object::cast_to(node); + if (!selected) + return; + + if (!p_allow_locked) { + // Replace the node by the group if grouped + while (node && node != editor->get_edited_scene()->get_parent()) { + Node3D *selected_tmp = Object::cast_to(node); + if (selected_tmp && node->has_meta("_edit_group_")) { + selected = selected_tmp; + } + node = node->get_parent(); + } + } + + if (p_allow_locked || !_is_node_locked(selected)) { + _select(selected, clicked_wants_append, true); + } +} + +void Node3DEditorViewport::_select(Node *p_node, bool p_append, bool p_single) { + + if (!p_append) { + editor_selection->clear(); + } + + if (editor_selection->is_selected(p_node)) { + //erase + editor_selection->remove_node(p_node); + } else { + + editor_selection->add_node(p_node); + } + + if (p_single) { + if (Engine::get_singleton()->is_editor_hint()) + editor->call("edit_node", p_node); + } +} + +ObjectID Node3DEditorViewport::_select_ray(const Point2 &p_pos, bool p_append, bool &r_includes_current, int *r_gizmo_handle, bool p_alt_select) { + + if (r_gizmo_handle) + *r_gizmo_handle = -1; + + Vector3 ray = _get_ray(p_pos); + Vector3 pos = _get_ray_pos(p_pos); + Vector2 shrinked_pos = p_pos / viewport_container->get_stretch_shrink(); + + Vector instances = VisualServer::get_singleton()->instances_cull_ray(pos, ray, get_tree()->get_root()->get_world()->get_scenario()); + Set> found_gizmos; + + Node *edited_scene = get_tree()->get_edited_scene_root(); + ObjectID closest; + Node *item = NULL; + float closest_dist = 1e20; + int selected_handle = -1; + + for (int i = 0; i < instances.size(); i++) { + + Node3D *spat = Object::cast_to(ObjectDB::get_instance(instances[i])); + + if (!spat) + continue; + + Ref seg = spat->get_gizmo(); + + if ((!seg.is_valid()) || found_gizmos.has(seg)) { + continue; + } + + found_gizmos.insert(seg); + Vector3 point; + Vector3 normal; + + int handle = -1; + bool inters = seg->intersect_ray(camera, shrinked_pos, point, normal, &handle, p_alt_select); + + if (!inters) + continue; + + float dist = pos.distance_to(point); + + if (dist < 0) + continue; + + if (dist < closest_dist) { + + item = Object::cast_to(spat); + while (item->get_owner() && item->get_owner() != edited_scene && !edited_scene->is_editable_instance(item->get_owner())) { + item = item->get_owner(); + } + + closest = item->get_instance_id(); + closest_dist = dist; + selected_handle = handle; + } + } + + if (!item) + return ObjectID(); + + if (!editor_selection->is_selected(item) || (r_gizmo_handle && selected_handle >= 0)) { + + if (r_gizmo_handle) + *r_gizmo_handle = selected_handle; + } + + return closest; +} + +void Node3DEditorViewport::_find_items_at_pos(const Point2 &p_pos, bool &r_includes_current, Vector<_RayResult> &results, bool p_alt_select) { + + Vector3 ray = _get_ray(p_pos); + Vector3 pos = _get_ray_pos(p_pos); + + Vector instances = VisualServer::get_singleton()->instances_cull_ray(pos, ray, get_tree()->get_root()->get_world()->get_scenario()); + Set> found_gizmos; + + r_includes_current = false; + + for (int i = 0; i < instances.size(); i++) { + + Node3D *spat = Object::cast_to(ObjectDB::get_instance(instances[i])); + + if (!spat) + continue; + + Ref seg = spat->get_gizmo(); + + if (!seg.is_valid()) + continue; + + if (found_gizmos.has(seg)) + continue; + + found_gizmos.insert(seg); + Vector3 point; + Vector3 normal; + + int handle = -1; + bool inters = seg->intersect_ray(camera, p_pos, point, normal, NULL, p_alt_select); + + if (!inters) + continue; + + float dist = pos.distance_to(point); + + if (dist < 0) + continue; + + if (editor_selection->is_selected(spat)) + r_includes_current = true; + + _RayResult res; + res.item = spat; + res.depth = dist; + res.handle = handle; + results.push_back(res); + } + + if (results.empty()) + return; + + results.sort(); +} + +Vector3 Node3DEditorViewport::_get_screen_to_space(const Vector3 &p_vector3) { + + CameraMatrix cm; + if (orthogonal) { + cm.set_orthogonal(camera->get_size(), get_size().aspect(), get_znear() + p_vector3.z, get_zfar()); + } else { + cm.set_perspective(get_fov(), get_size().aspect(), get_znear() + p_vector3.z, get_zfar()); + } + Vector2 screen_he = cm.get_viewport_half_extents(); + + Transform camera_transform; + camera_transform.translate(cursor.pos); + camera_transform.basis.rotate(Vector3(1, 0, 0), -cursor.x_rot); + camera_transform.basis.rotate(Vector3(0, 1, 0), -cursor.y_rot); + camera_transform.translate(0, 0, cursor.distance); + + return camera_transform.xform(Vector3(((p_vector3.x / get_size().width) * 2.0 - 1.0) * screen_he.x, ((1.0 - (p_vector3.y / get_size().height)) * 2.0 - 1.0) * screen_he.y, -(get_znear() + p_vector3.z))); +} + +void Node3DEditorViewport::_select_region() { + + if (cursor.region_begin == cursor.region_end) + return; //nothing really + + float z_offset = MAX(0.0, 5.0 - get_znear()); + + Vector3 box[4] = { + Vector3( + MIN(cursor.region_begin.x, cursor.region_end.x), + MIN(cursor.region_begin.y, cursor.region_end.y), + z_offset), + Vector3( + MAX(cursor.region_begin.x, cursor.region_end.x), + MIN(cursor.region_begin.y, cursor.region_end.y), + z_offset), + Vector3( + MAX(cursor.region_begin.x, cursor.region_end.x), + MAX(cursor.region_begin.y, cursor.region_end.y), + z_offset), + Vector3( + MIN(cursor.region_begin.x, cursor.region_end.x), + MAX(cursor.region_begin.y, cursor.region_end.y), + z_offset) + }; + + Vector frustum; + + Vector3 cam_pos = _get_camera_position(); + + for (int i = 0; i < 4; i++) { + + Vector3 a = _get_screen_to_space(box[i]); + Vector3 b = _get_screen_to_space(box[(i + 1) % 4]); + if (orthogonal) { + frustum.push_back(Plane(a, (a - b).normalized())); + } else { + frustum.push_back(Plane(a, b, cam_pos)); + } + } + + if (!orthogonal) { + Plane near(cam_pos, -_get_camera_normal()); + near.d -= get_znear(); + + frustum.push_back(near); + + Plane far = -near; + far.d += get_zfar(); + + frustum.push_back(far); + } + + Vector instances = VisualServer::get_singleton()->instances_cull_convex(frustum, get_tree()->get_root()->get_world()->get_scenario()); + Vector selected; + + Node *edited_scene = get_tree()->get_edited_scene_root(); + + for (int i = 0; i < instances.size(); i++) { + + Node3D *sp = Object::cast_to(ObjectDB::get_instance(instances[i])); + if (!sp || _is_node_locked(sp)) + continue; + + Node *item = Object::cast_to(sp); + while (item->get_owner() && item->get_owner() != edited_scene && !edited_scene->is_editable_instance(item->get_owner())) { + item = item->get_owner(); + } + + // Replace the node by the group if grouped + if (item->is_class("Node3D")) { + Node3D *sel = Object::cast_to(item); + while (item && item != editor->get_edited_scene()->get_parent()) { + Node3D *selected_tmp = Object::cast_to(item); + if (selected_tmp && item->has_meta("_edit_group_")) { + sel = selected_tmp; + } + item = item->get_parent(); + } + item = sel; + } + + if (selected.find(item) != -1) continue; + + if (_is_node_locked(item)) continue; + + Ref seg = sp->get_gizmo(); + + if (!seg.is_valid()) + continue; + + if (seg->intersect_frustum(camera, frustum)) { + selected.push_back(item); + } + } + + bool single = selected.size() == 1; + for (int i = 0; i < selected.size(); i++) { + _select(selected[i], true, single); + } +} + +void Node3DEditorViewport::_update_name() { + + String view_mode = orthogonal ? TTR("Orthogonal") : TTR("Perspective"); + + if (auto_orthogonal) { + view_mode += " [auto]"; + } + + if (name != "") + view_menu->set_text(name + " " + view_mode); + else + view_menu->set_text(view_mode); + + view_menu->set_size(Vector2(0, 0)); // resets the button size +} + +void Node3DEditorViewport::_compute_edit(const Point2 &p_point) { + + _edit.click_ray = _get_ray(Vector2(p_point.x, p_point.y)); + _edit.click_ray_pos = _get_ray_pos(Vector2(p_point.x, p_point.y)); + _edit.plane = TRANSFORM_VIEW; + spatial_editor->update_transform_gizmo(); + _edit.center = spatial_editor->get_gizmo_transform().origin; + + List &selection = editor_selection->get_selected_node_list(); + + for (List::Element *E = selection.front(); E; E = E->next()) { + + Node3D *sp = Object::cast_to(E->get()); + if (!sp) + continue; + + Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data(sp); + if (!se) + continue; + + se->original = se->sp->get_global_gizmo_transform(); + se->original_local = se->sp->get_local_gizmo_transform(); + } +} + +static int _get_key_modifier_setting(const String &p_property) { + + switch (EditorSettings::get_singleton()->get(p_property).operator int()) { + + case 0: return 0; + case 1: return KEY_SHIFT; + case 2: return KEY_ALT; + case 3: return KEY_META; + case 4: return KEY_CONTROL; + } + return 0; +} + +static int _get_key_modifier(Ref e) { + if (e->get_shift()) + return KEY_SHIFT; + if (e->get_alt()) + return KEY_ALT; + if (e->get_control()) + return KEY_CONTROL; + if (e->get_metakey()) + return KEY_META; + return 0; +} + +bool Node3DEditorViewport::_gizmo_select(const Vector2 &p_screenpos, bool p_highlight_only) { + + if (!spatial_editor->is_gizmo_visible()) + return false; + if (get_selected_count() == 0) { + if (p_highlight_only) + spatial_editor->select_gizmo_highlight_axis(-1); + return false; + } + + Vector3 ray_pos = _get_ray_pos(Vector2(p_screenpos.x, p_screenpos.y)); + Vector3 ray = _get_ray(Vector2(p_screenpos.x, p_screenpos.y)); + + Transform gt = spatial_editor->get_gizmo_transform(); + float gs = gizmo_scale; + + if (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_MOVE) { + + int col_axis = -1; + float col_d = 1e20; + + for (int i = 0; i < 3; i++) { + + Vector3 grabber_pos = gt.origin + gt.basis.get_axis(i) * gs * (GIZMO_ARROW_OFFSET + (GIZMO_ARROW_SIZE * 0.5)); + float grabber_radius = gs * GIZMO_ARROW_SIZE; + + Vector3 r; + + if (Geometry::segment_intersects_sphere(ray_pos, ray_pos + ray * MAX_Z, grabber_pos, grabber_radius, &r)) { + float d = r.distance_to(ray_pos); + if (d < col_d) { + col_d = d; + col_axis = i; + } + } + } + + bool is_plane_translate = false; + // plane select + if (col_axis == -1) { + col_d = 1e20; + + for (int i = 0; i < 3; i++) { + + Vector3 ivec2 = gt.basis.get_axis((i + 1) % 3).normalized(); + Vector3 ivec3 = gt.basis.get_axis((i + 2) % 3).normalized(); + + Vector3 grabber_pos = gt.origin + (ivec2 + ivec3) * gs * (GIZMO_PLANE_SIZE + GIZMO_PLANE_DST); + + Vector3 r; + Plane plane(gt.origin, gt.basis.get_axis(i).normalized()); + + if (plane.intersects_ray(ray_pos, ray, &r)) { + + float dist = r.distance_to(grabber_pos); + if (dist < (gs * GIZMO_PLANE_SIZE)) { + + float d = ray_pos.distance_to(r); + if (d < col_d) { + col_d = d; + col_axis = i; + + is_plane_translate = true; + } + } + } + } + } + + if (col_axis != -1) { + + if (p_highlight_only) { + + spatial_editor->select_gizmo_highlight_axis(col_axis + (is_plane_translate ? 6 : 0)); + + } else { + //handle plane translate + _edit.mode = TRANSFORM_TRANSLATE; + _compute_edit(Point2(p_screenpos.x, p_screenpos.y)); + _edit.plane = TransformPlane(TRANSFORM_X_AXIS + col_axis + (is_plane_translate ? 3 : 0)); + } + return true; + } + } + + if (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_ROTATE) { + + int col_axis = -1; + float col_d = 1e20; + + for (int i = 0; i < 3; i++) { + + Plane plane(gt.origin, gt.basis.get_axis(i).normalized()); + Vector3 r; + if (!plane.intersects_ray(ray_pos, ray, &r)) + continue; + + float dist = r.distance_to(gt.origin); + + if (dist > gs * (GIZMO_CIRCLE_SIZE - GIZMO_RING_HALF_WIDTH) && dist < gs * (GIZMO_CIRCLE_SIZE + GIZMO_RING_HALF_WIDTH)) { + + float d = ray_pos.distance_to(r); + if (d < col_d) { + col_d = d; + col_axis = i; + } + } + } + + if (col_axis != -1) { + + if (p_highlight_only) { + + spatial_editor->select_gizmo_highlight_axis(col_axis + 3); + } else { + //handle rotate + _edit.mode = TRANSFORM_ROTATE; + _compute_edit(Point2(p_screenpos.x, p_screenpos.y)); + _edit.plane = TransformPlane(TRANSFORM_X_AXIS + col_axis); + } + return true; + } + } + + if (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SCALE) { + + int col_axis = -1; + float col_d = 1e20; + + for (int i = 0; i < 3; i++) { + + Vector3 grabber_pos = gt.origin + gt.basis.get_axis(i) * gs * GIZMO_SCALE_OFFSET; + float grabber_radius = gs * GIZMO_ARROW_SIZE; + + Vector3 r; + + if (Geometry::segment_intersects_sphere(ray_pos, ray_pos + ray * MAX_Z, grabber_pos, grabber_radius, &r)) { + float d = r.distance_to(ray_pos); + if (d < col_d) { + col_d = d; + col_axis = i; + } + } + } + + bool is_plane_scale = false; + // plane select + if (col_axis == -1) { + col_d = 1e20; + + for (int i = 0; i < 3; i++) { + + Vector3 ivec2 = gt.basis.get_axis((i + 1) % 3).normalized(); + Vector3 ivec3 = gt.basis.get_axis((i + 2) % 3).normalized(); + + Vector3 grabber_pos = gt.origin + (ivec2 + ivec3) * gs * (GIZMO_PLANE_SIZE + GIZMO_PLANE_DST); + + Vector3 r; + Plane plane(gt.origin, gt.basis.get_axis(i).normalized()); + + if (plane.intersects_ray(ray_pos, ray, &r)) { + + float dist = r.distance_to(grabber_pos); + if (dist < (gs * GIZMO_PLANE_SIZE)) { + + float d = ray_pos.distance_to(r); + if (d < col_d) { + col_d = d; + col_axis = i; + + is_plane_scale = true; + } + } + } + } + } + + if (col_axis != -1) { + + if (p_highlight_only) { + + spatial_editor->select_gizmo_highlight_axis(col_axis + (is_plane_scale ? 12 : 9)); + + } else { + //handle scale + _edit.mode = TRANSFORM_SCALE; + _compute_edit(Point2(p_screenpos.x, p_screenpos.y)); + _edit.plane = TransformPlane(TRANSFORM_X_AXIS + col_axis + (is_plane_scale ? 3 : 0)); + } + return true; + } + } + + if (p_highlight_only) + spatial_editor->select_gizmo_highlight_axis(-1); + + return false; +} + +void Node3DEditorViewport::_surface_mouse_enter() { + + if (!surface->has_focus() && (!get_focus_owner() || !get_focus_owner()->is_text_field())) + surface->grab_focus(); +} + +void Node3DEditorViewport::_surface_mouse_exit() { + + _remove_preview(); +} + +void Node3DEditorViewport::_surface_focus_enter() { + + view_menu->set_disable_shortcuts(false); +} + +void Node3DEditorViewport::_surface_focus_exit() { + + view_menu->set_disable_shortcuts(true); +} +bool Node3DEditorViewport ::_is_node_locked(const Node *p_node) { + return p_node->has_meta("_edit_lock_") && p_node->get_meta("_edit_lock_"); +} +void Node3DEditorViewport::_list_select(Ref b) { + + _find_items_at_pos(b->get_position(), clicked_includes_current, selection_results, b->get_shift()); + + Node *scene = editor->get_edited_scene(); + + for (int i = 0; i < selection_results.size(); i++) { + Node3D *item = selection_results[i].item; + if (item != scene && item->get_owner() != scene && !scene->is_editable_instance(item->get_owner())) { + //invalid result + selection_results.remove(i); + i--; + } + } + + clicked_wants_append = b->get_shift(); + + if (selection_results.size() == 1) { + + clicked = selection_results[0].item->get_instance_id(); + selection_results.clear(); + + if (clicked.is_valid()) { + _select_clicked(clicked_wants_append, true, spatial_editor->get_tool_mode() != Node3DEditor::TOOL_MODE_LIST_SELECT); + clicked = ObjectID(); + } + + } else if (!selection_results.empty()) { + + NodePath root_path = get_tree()->get_edited_scene_root()->get_path(); + StringName root_name = root_path.get_name(root_path.get_name_count() - 1); + + for (int i = 0; i < selection_results.size(); i++) { + + Node3D *spat = selection_results[i].item; + + Ref icon = EditorNode::get_singleton()->get_object_icon(spat, "Node"); + + String node_path = "/" + root_name + "/" + root_path.rel_path_to(spat->get_path()); + + int locked = 0; + if (_is_node_locked(spat)) { + locked = 1; + } else { + Node *ed_scene = editor->get_edited_scene(); + Node *node = spat; + + while (node && node != ed_scene->get_parent()) { + Node3D *selected_tmp = Object::cast_to(node); + if (selected_tmp && node->has_meta("_edit_group_")) { + locked = 2; + } + node = node->get_parent(); + } + } + + String suffix = String(); + if (locked == 1) { + suffix = " (" + TTR("Locked") + ")"; + } else if (locked == 2) { + suffix = " (" + TTR("Grouped") + ")"; + } + selection_menu->add_item((String)spat->get_name() + suffix); + selection_menu->set_item_icon(i, icon); + selection_menu->set_item_metadata(i, node_path); + selection_menu->set_item_tooltip(i, String(spat->get_name()) + "\nType: " + spat->get_class() + "\nPath: " + node_path); + } + + selection_menu->set_position(get_screen_transform().xform(b->get_position())); + selection_menu->popup(); + } +} + +void Node3DEditorViewport::_sinput(const Ref &p_event) { + + if (previewing) + return; //do NONE + + { + EditorNode *en = editor; + EditorPluginList *force_input_forwarding_list = en->get_editor_plugins_force_input_forwarding(); + if (!force_input_forwarding_list->empty()) { + bool discard = force_input_forwarding_list->forward_spatial_gui_input(camera, p_event, true); + if (discard) + return; + } + } + { + EditorNode *en = editor; + EditorPluginList *over_plugin_list = en->get_editor_plugins_over(); + if (!over_plugin_list->empty()) { + bool discard = over_plugin_list->forward_spatial_gui_input(camera, p_event, false); + if (discard) + return; + } + } + + Ref b = p_event; + + if (b.is_valid()) { + emit_signal("clicked", this); + + float zoom_factor = 1 + (ZOOM_MULTIPLIER - 1) * b->get_factor(); + switch (b->get_button_index()) { + + case BUTTON_WHEEL_UP: { + if (is_freelook_active()) + scale_freelook_speed(zoom_factor); + else + scale_cursor_distance(1.0 / zoom_factor); + } break; + + case BUTTON_WHEEL_DOWN: { + if (is_freelook_active()) + scale_freelook_speed(1.0 / zoom_factor); + else + scale_cursor_distance(zoom_factor); + } break; + + case BUTTON_RIGHT: { + + NavigationScheme nav_scheme = (NavigationScheme)EditorSettings::get_singleton()->get("editors/3d/navigation/navigation_scheme").operator int(); + + if (b->is_pressed() && _edit.gizmo.is_valid()) { + //restore + _edit.gizmo->commit_handle(_edit.gizmo_handle, _edit.gizmo_initial_value, true); + _edit.gizmo = Ref(); + } + + if (_edit.mode == TRANSFORM_NONE && b->is_pressed()) { + + if (b->get_alt()) { + + if (nav_scheme == NAVIGATION_MAYA) + break; + + _list_select(b); + return; + } + } + + if (_edit.mode != TRANSFORM_NONE && b->is_pressed()) { + //cancel motion + _edit.mode = TRANSFORM_NONE; + + List &selection = editor_selection->get_selected_node_list(); + + for (List::Element *E = selection.front(); E; E = E->next()) { + + Node3D *sp = Object::cast_to(E->get()); + if (!sp) + continue; + + Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data(sp); + if (!se) + continue; + + sp->set_global_transform(se->original); + } + surface->update(); + set_message(TTR("Transform Aborted."), 3); + } + + if (b->is_pressed()) { + const int mod = _get_key_modifier(b); + if (!orthogonal) { + if (mod == _get_key_modifier_setting("editors/3d/freelook/freelook_activation_modifier")) { + set_freelook_active(true); + } + } + } else { + set_freelook_active(false); + } + + if (freelook_active && !surface->has_focus()) { + // Focus usually doesn't trigger on right-click, but in case of freelook it should, + // otherwise using keyboard navigation would misbehave + surface->grab_focus(); + } + + } break; + case BUTTON_MIDDLE: { + + if (b->is_pressed() && _edit.mode != TRANSFORM_NONE) { + + switch (_edit.plane) { + + case TRANSFORM_VIEW: { + + _edit.plane = TRANSFORM_X_AXIS; + set_message(TTR("X-Axis Transform."), 2); + name = ""; + _update_name(); + } break; + case TRANSFORM_X_AXIS: { + + _edit.plane = TRANSFORM_Y_AXIS; + set_message(TTR("Y-Axis Transform."), 2); + + } break; + case TRANSFORM_Y_AXIS: { + + _edit.plane = TRANSFORM_Z_AXIS; + set_message(TTR("Z-Axis Transform."), 2); + + } break; + case TRANSFORM_Z_AXIS: { + + _edit.plane = TRANSFORM_VIEW; + set_message(TTR("View Plane Transform."), 2); + + } break; + case TRANSFORM_YZ: + case TRANSFORM_XZ: + case TRANSFORM_XY: { + } break; + } + } + } break; + case BUTTON_LEFT: { + + if (b->is_pressed()) { + + NavigationScheme nav_scheme = (NavigationScheme)EditorSettings::get_singleton()->get("editors/3d/navigation/navigation_scheme").operator int(); + if ((nav_scheme == NAVIGATION_MAYA || nav_scheme == NAVIGATION_MODO) && b->get_alt()) { + break; + } + + if (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_LIST_SELECT) { + _list_select(b); + break; + } + + _edit.mouse_pos = b->get_position(); + _edit.snap = spatial_editor->is_snap_enabled(); + _edit.mode = TRANSFORM_NONE; + + //gizmo has priority over everything + + bool can_select_gizmos = true; + + { + int idx = view_menu->get_popup()->get_item_index(VIEW_GIZMOS); + can_select_gizmos = view_menu->get_popup()->is_item_checked(idx); + } + + if (can_select_gizmos && spatial_editor->get_selected()) { + + Ref seg = spatial_editor->get_selected()->get_gizmo(); + if (seg.is_valid()) { + int handle = -1; + Vector3 point; + Vector3 normal; + bool inters = seg->intersect_ray(camera, _edit.mouse_pos, point, normal, &handle, b->get_shift()); + if (inters && handle != -1) { + + _edit.gizmo = seg; + _edit.gizmo_handle = handle; + _edit.gizmo_initial_value = seg->get_handle_value(handle); + break; + } + } + } + + if (_gizmo_select(_edit.mouse_pos)) + break; + + clicked = ObjectID(); + clicked_includes_current = false; + + if ((spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT && b->get_control()) || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_ROTATE) { + + /* HANDLE ROTATION */ + if (get_selected_count() == 0) + break; //bye + //handle rotate + _edit.mode = TRANSFORM_ROTATE; + _compute_edit(b->get_position()); + break; + } + + if (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_MOVE) { + + if (get_selected_count() == 0) + break; //bye + //handle translate + _edit.mode = TRANSFORM_TRANSLATE; + _compute_edit(b->get_position()); + break; + } + + if (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SCALE) { + + if (get_selected_count() == 0) + break; //bye + //handle scale + _edit.mode = TRANSFORM_SCALE; + _compute_edit(b->get_position()); + break; + } + + // todo scale + + int gizmo_handle = -1; + + clicked = _select_ray(b->get_position(), b->get_shift(), clicked_includes_current, &gizmo_handle, b->get_shift()); + + //clicking is always deferred to either move or release + + clicked_wants_append = b->get_shift(); + + if (clicked.is_null()) { + + if (!clicked_wants_append) + _clear_selected(); + + //default to regionselect + cursor.region_select = true; + cursor.region_begin = b->get_position(); + cursor.region_end = b->get_position(); + } + + if (clicked.is_valid() && gizmo_handle >= 0) { + + Node3D *spa = Object::cast_to(ObjectDB::get_instance(clicked)); + if (spa) { + + Ref seg = spa->get_gizmo(); + if (seg.is_valid()) { + + _edit.gizmo = seg; + _edit.gizmo_handle = gizmo_handle; + _edit.gizmo_initial_value = seg->get_handle_value(gizmo_handle); + break; + } + } + } + + surface->update(); + } else { + + if (_edit.gizmo.is_valid()) { + + _edit.gizmo->commit_handle(_edit.gizmo_handle, _edit.gizmo_initial_value, false); + _edit.gizmo = Ref(); + break; + } + if (clicked.is_valid()) { + _select_clicked(clicked_wants_append, true); + // Processing was deferred. + clicked = ObjectID(); + } + + if (cursor.region_select) { + + if (!clicked_wants_append) _clear_selected(); + + _select_region(); + cursor.region_select = false; + surface->update(); + } + + if (_edit.mode != TRANSFORM_NONE) { + + static const char *_transform_name[4] = { "None", "Rotate", "Translate", "Scale" }; + undo_redo->create_action(_transform_name[_edit.mode]); + + List &selection = editor_selection->get_selected_node_list(); + + for (List::Element *E = selection.front(); E; E = E->next()) { + + Node3D *sp = Object::cast_to(E->get()); + if (!sp) + continue; + + Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data(sp); + if (!se) + continue; + + undo_redo->add_do_method(sp, "set_global_transform", sp->get_global_gizmo_transform()); + undo_redo->add_undo_method(sp, "set_global_transform", se->original); + } + undo_redo->commit_action(); + _edit.mode = TRANSFORM_NONE; + set_message(""); + } + + surface->update(); + } + + } break; + } + } + + Ref m = p_event; + + if (m.is_valid()) { + + _edit.mouse_pos = m->get_position(); + + if (spatial_editor->get_selected()) { + + Ref seg = spatial_editor->get_selected()->get_gizmo(); + if (seg.is_valid()) { + + int selected_handle = -1; + + int handle = -1; + Vector3 point; + Vector3 normal; + bool inters = seg->intersect_ray(camera, _edit.mouse_pos, point, normal, &handle, false); + if (inters && handle != -1) { + + selected_handle = handle; + } + + if (selected_handle != spatial_editor->get_over_gizmo_handle()) { + spatial_editor->set_over_gizmo_handle(selected_handle); + spatial_editor->get_selected()->update_gizmo(); + if (selected_handle != -1) + spatial_editor->select_gizmo_highlight_axis(-1); + } + } + } + + if (spatial_editor->get_over_gizmo_handle() == -1 && !(m->get_button_mask() & 1) && !_edit.gizmo.is_valid()) { + + _gizmo_select(_edit.mouse_pos, true); + } + + NavigationScheme nav_scheme = (NavigationScheme)EditorSettings::get_singleton()->get("editors/3d/navigation/navigation_scheme").operator int(); + NavigationMode nav_mode = NAVIGATION_NONE; + + if (_edit.gizmo.is_valid()) { + + _edit.gizmo->set_handle(_edit.gizmo_handle, camera, m->get_position()); + Variant v = _edit.gizmo->get_handle_value(_edit.gizmo_handle); + String n = _edit.gizmo->get_handle_name(_edit.gizmo_handle); + set_message(n + ": " + String(v)); + + } else if (m->get_button_mask() & BUTTON_MASK_LEFT) { + + if (nav_scheme == NAVIGATION_MAYA && m->get_alt()) { + nav_mode = NAVIGATION_ORBIT; + } else if (nav_scheme == NAVIGATION_MODO && m->get_alt() && m->get_shift()) { + nav_mode = NAVIGATION_PAN; + } else if (nav_scheme == NAVIGATION_MODO && m->get_alt() && m->get_control()) { + nav_mode = NAVIGATION_ZOOM; + } else if (nav_scheme == NAVIGATION_MODO && m->get_alt()) { + nav_mode = NAVIGATION_ORBIT; + } else { + if (clicked.is_valid()) { + + if (!clicked_includes_current) { + + _select_clicked(clicked_wants_append, true); + // Processing was deferred. + } + + _compute_edit(_edit.mouse_pos); + clicked = ObjectID(); + + _edit.mode = TRANSFORM_TRANSLATE; + } + + if (cursor.region_select) { + cursor.region_end = m->get_position(); + surface->update(); + return; + } + + if (_edit.mode == TRANSFORM_NONE) + return; + + Vector3 ray_pos = _get_ray_pos(m->get_position()); + Vector3 ray = _get_ray(m->get_position()); + float snap = EDITOR_GET("interface/inspector/default_float_step"); + int snap_step_decimals = Math::range_step_decimals(snap); + + switch (_edit.mode) { + + case TRANSFORM_SCALE: { + + Vector3 motion_mask; + Plane plane; + bool plane_mv = false; + + switch (_edit.plane) { + case TRANSFORM_VIEW: + motion_mask = Vector3(0, 0, 0); + plane = Plane(_edit.center, _get_camera_normal()); + break; + case TRANSFORM_X_AXIS: + motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(0); + plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized()); + break; + case TRANSFORM_Y_AXIS: + motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(1); + plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized()); + break; + case TRANSFORM_Z_AXIS: + motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2); + plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized()); + break; + case TRANSFORM_YZ: + motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2) + spatial_editor->get_gizmo_transform().basis.get_axis(1); + plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(0)); + plane_mv = true; + break; + case TRANSFORM_XZ: + motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2) + spatial_editor->get_gizmo_transform().basis.get_axis(0); + plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(1)); + plane_mv = true; + break; + case TRANSFORM_XY: + motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(0) + spatial_editor->get_gizmo_transform().basis.get_axis(1); + plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(2)); + plane_mv = true; + break; + } + + Vector3 intersection; + if (!plane.intersects_ray(ray_pos, ray, &intersection)) + break; + + Vector3 click; + if (!plane.intersects_ray(_edit.click_ray_pos, _edit.click_ray, &click)) + break; + + Vector3 motion = intersection - click; + if (_edit.plane != TRANSFORM_VIEW) { + + if (!plane_mv) { + + motion = motion_mask.dot(motion) * motion_mask; + + } else { + + // Alternative planar scaling mode + if (_get_key_modifier(m) != KEY_SHIFT) { + motion = motion_mask.dot(motion) * motion_mask; + } + } + + } else { + float center_click_dist = click.distance_to(_edit.center); + float center_inters_dist = intersection.distance_to(_edit.center); + if (center_click_dist == 0) + break; + + float scale = center_inters_dist - center_click_dist; + motion = Vector3(scale, scale, scale); + } + + List &selection = editor_selection->get_selected_node_list(); + + // Disable local transformation for TRANSFORM_VIEW + bool local_coords = (spatial_editor->are_local_coords_enabled() && _edit.plane != TRANSFORM_VIEW); + + if (_edit.snap || spatial_editor->is_snap_enabled()) { + snap = spatial_editor->get_scale_snap() / 100; + } + Vector3 motion_snapped = motion; + motion_snapped.snap(Vector3(snap, snap, snap)); + // This might not be necessary anymore after issue #288 is solved (in 4.0?). + set_message(TTR("Scaling: ") + "(" + String::num(motion_snapped.x, snap_step_decimals) + ", " + + String::num(motion_snapped.y, snap_step_decimals) + ", " + String::num(motion_snapped.z, snap_step_decimals) + ")"); + + for (List::Element *E = selection.front(); E; E = E->next()) { + + Node3D *sp = Object::cast_to(E->get()); + if (!sp) { + continue; + } + + Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data(sp); + if (!se) { + continue; + } + + if (sp->has_meta("_edit_lock_")) { + continue; + } + + Transform original = se->original; + Transform original_local = se->original_local; + Transform base = Transform(Basis(), _edit.center); + Transform t; + Vector3 local_scale; + + if (local_coords) { + + Basis g = original.basis.orthonormalized(); + Vector3 local_motion = g.inverse().xform(motion); + + if (_edit.snap || spatial_editor->is_snap_enabled()) { + local_motion.snap(Vector3(snap, snap, snap)); + } + + local_scale = original_local.basis.get_scale() * (local_motion + Vector3(1, 1, 1)); + + // Prevent scaling to 0 it would break the gizmo + Basis check = original_local.basis; + check.scale(local_scale); + if (check.determinant() != 0) { + + // Apply scale + sp->set_scale(local_scale); + } + + } else { + + if (_edit.snap || spatial_editor->is_snap_enabled()) { + motion.snap(Vector3(snap, snap, snap)); + } + + Transform r; + r.basis.scale(motion + Vector3(1, 1, 1)); + t = base * (r * (base.inverse() * original)); + + // Apply scale + sp->set_global_transform(t); + } + } + + surface->update(); + + } break; + + case TRANSFORM_TRANSLATE: { + + Vector3 motion_mask; + Plane plane; + bool plane_mv = false; + + switch (_edit.plane) { + case TRANSFORM_VIEW: + plane = Plane(_edit.center, _get_camera_normal()); + break; + case TRANSFORM_X_AXIS: + motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(0); + plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized()); + break; + case TRANSFORM_Y_AXIS: + motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(1); + plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized()); + break; + case TRANSFORM_Z_AXIS: + motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2); + plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized()); + break; + case TRANSFORM_YZ: + plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(0)); + plane_mv = true; + break; + case TRANSFORM_XZ: + plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(1)); + plane_mv = true; + break; + case TRANSFORM_XY: + plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(2)); + plane_mv = true; + break; + } + + Vector3 intersection; + if (!plane.intersects_ray(ray_pos, ray, &intersection)) + break; + + Vector3 click; + if (!plane.intersects_ray(_edit.click_ray_pos, _edit.click_ray, &click)) + break; + + Vector3 motion = intersection - click; + if (_edit.plane != TRANSFORM_VIEW) { + if (!plane_mv) { + motion = motion_mask.dot(motion) * motion_mask; + } + } + + List &selection = editor_selection->get_selected_node_list(); + + // Disable local transformation for TRANSFORM_VIEW + bool local_coords = (spatial_editor->are_local_coords_enabled() && _edit.plane != TRANSFORM_VIEW); + + if (_edit.snap || spatial_editor->is_snap_enabled()) { + snap = spatial_editor->get_translate_snap(); + } + Vector3 motion_snapped = motion; + motion_snapped.snap(Vector3(snap, snap, snap)); + set_message(TTR("Translating: ") + "(" + String::num(motion_snapped.x, snap_step_decimals) + ", " + + String::num(motion_snapped.y, snap_step_decimals) + ", " + String::num(motion_snapped.z, snap_step_decimals) + ")"); + + for (List::Element *E = selection.front(); E; E = E->next()) { + + Node3D *sp = Object::cast_to(E->get()); + if (!sp) { + continue; + } + + Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data(sp); + if (!se) { + continue; + } + + if (sp->has_meta("_edit_lock_")) { + continue; + } + + Transform original = se->original; + Transform t; + + if (local_coords) { + + if (_edit.snap || spatial_editor->is_snap_enabled()) { + Basis g = original.basis.orthonormalized(); + Vector3 local_motion = g.inverse().xform(motion); + local_motion.snap(Vector3(snap, snap, snap)); + + motion = g.xform(local_motion); + } + + } else { + + if (_edit.snap || spatial_editor->is_snap_enabled()) { + motion.snap(Vector3(snap, snap, snap)); + } + } + + // Apply translation + t = original; + t.origin += motion; + sp->set_global_transform(t); + } + + surface->update(); + + } break; + + case TRANSFORM_ROTATE: { + + Plane plane; + Vector3 axis; + + switch (_edit.plane) { + case TRANSFORM_VIEW: + plane = Plane(_edit.center, _get_camera_normal()); + break; + case TRANSFORM_X_AXIS: + plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(0)); + axis = Vector3(1, 0, 0); + break; + case TRANSFORM_Y_AXIS: + plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(1)); + axis = Vector3(0, 1, 0); + break; + case TRANSFORM_Z_AXIS: + plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(2)); + axis = Vector3(0, 0, 1); + break; + case TRANSFORM_YZ: + case TRANSFORM_XZ: + case TRANSFORM_XY: + break; + } + + Vector3 intersection; + if (!plane.intersects_ray(ray_pos, ray, &intersection)) + break; + + Vector3 click; + if (!plane.intersects_ray(_edit.click_ray_pos, _edit.click_ray, &click)) + break; + + Vector3 y_axis = (click - _edit.center).normalized(); + Vector3 x_axis = plane.normal.cross(y_axis).normalized(); + + float angle = Math::atan2(x_axis.dot(intersection - _edit.center), y_axis.dot(intersection - _edit.center)); + + if (_edit.snap || spatial_editor->is_snap_enabled()) { + snap = spatial_editor->get_rotate_snap(); + } + angle = Math::rad2deg(angle) + snap * 0.5; //else it won't reach +180 + angle -= Math::fmod(angle, snap); + set_message(vformat(TTR("Rotating %s degrees."), String::num(angle, snap_step_decimals))); + angle = Math::deg2rad(angle); + + List &selection = editor_selection->get_selected_node_list(); + + bool local_coords = (spatial_editor->are_local_coords_enabled() && _edit.plane != TRANSFORM_VIEW); // Disable local transformation for TRANSFORM_VIEW + + for (List::Element *E = selection.front(); E; E = E->next()) { + + Node3D *sp = Object::cast_to(E->get()); + if (!sp) + continue; + + Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data(sp); + if (!se) + continue; + + if (sp->has_meta("_edit_lock_")) { + continue; + } + + Transform t; + + if (local_coords) { + + Transform original_local = se->original_local; + Basis rot = Basis(axis, angle); + + t.basis = original_local.get_basis().orthonormalized() * rot; + t.origin = original_local.origin; + + // Apply rotation + sp->set_transform(t); + sp->set_scale(original_local.basis.get_scale()); // re-apply original scale + + } else { + + Transform original = se->original; + Transform r; + Transform base = Transform(Basis(), _edit.center); + + r.basis.rotate(plane.normal, angle); + t = base * r * base.inverse() * original; + + // Apply rotation + sp->set_global_transform(t); + } + } + + surface->update(); + + } break; + default: { + } + } + } + + } else if ((m->get_button_mask() & BUTTON_MASK_RIGHT) || freelook_active) { + + if (nav_scheme == NAVIGATION_MAYA && m->get_alt()) { + nav_mode = NAVIGATION_ZOOM; + } else if (freelook_active) { + nav_mode = NAVIGATION_LOOK; + } else if (orthogonal) { + nav_mode = NAVIGATION_PAN; + } + + } else if (m->get_button_mask() & BUTTON_MASK_MIDDLE) { + + if (nav_scheme == NAVIGATION_GODOT) { + + const int mod = _get_key_modifier(m); + + if (mod == _get_key_modifier_setting("editors/3d/navigation/pan_modifier")) { + nav_mode = NAVIGATION_PAN; + } else if (mod == _get_key_modifier_setting("editors/3d/navigation/zoom_modifier")) { + nav_mode = NAVIGATION_ZOOM; + } else if (mod == KEY_ALT || mod == _get_key_modifier_setting("editors/3d/navigation/orbit_modifier")) { + // Always allow Alt as a modifier to better support graphic tablets. + nav_mode = NAVIGATION_ORBIT; + } + + } else if (nav_scheme == NAVIGATION_MAYA) { + if (m->get_alt()) + nav_mode = NAVIGATION_PAN; + } + + } else if (EditorSettings::get_singleton()->get("editors/3d/navigation/emulate_3_button_mouse")) { + // Handle trackpad (no external mouse) use case + const int mod = _get_key_modifier(m); + + if (mod) { + if (mod == _get_key_modifier_setting("editors/3d/navigation/pan_modifier")) { + nav_mode = NAVIGATION_PAN; + } else if (mod == _get_key_modifier_setting("editors/3d/navigation/zoom_modifier")) { + nav_mode = NAVIGATION_ZOOM; + } else if (mod == KEY_ALT || mod == _get_key_modifier_setting("editors/3d/navigation/orbit_modifier")) { + // Always allow Alt as a modifier to better support graphic tablets. + nav_mode = NAVIGATION_ORBIT; + } + } + } + + switch (nav_mode) { + case NAVIGATION_PAN: { + _nav_pan(m, _get_warped_mouse_motion(m)); + + } break; + + case NAVIGATION_ZOOM: { + _nav_zoom(m, m->get_relative()); + + } break; + + case NAVIGATION_ORBIT: { + _nav_orbit(m, _get_warped_mouse_motion(m)); + + } break; + + case NAVIGATION_LOOK: { + _nav_look(m, _get_warped_mouse_motion(m)); + + } break; + + default: { + } + } + } + + Ref magnify_gesture = p_event; + if (magnify_gesture.is_valid()) { + + if (is_freelook_active()) + scale_freelook_speed(magnify_gesture->get_factor()); + else + scale_cursor_distance(1.0 / magnify_gesture->get_factor()); + } + + Ref pan_gesture = p_event; + if (pan_gesture.is_valid()) { + + NavigationScheme nav_scheme = (NavigationScheme)EditorSettings::get_singleton()->get("editors/3d/navigation/navigation_scheme").operator int(); + NavigationMode nav_mode = NAVIGATION_NONE; + + if (nav_scheme == NAVIGATION_GODOT) { + + const int mod = _get_key_modifier(pan_gesture); + + if (mod == _get_key_modifier_setting("editors/3d/navigation/pan_modifier")) { + nav_mode = NAVIGATION_PAN; + } else if (mod == _get_key_modifier_setting("editors/3d/navigation/zoom_modifier")) { + nav_mode = NAVIGATION_ZOOM; + } else if (mod == KEY_ALT || mod == _get_key_modifier_setting("editors/3d/navigation/orbit_modifier")) { + // Always allow Alt as a modifier to better support graphic tablets. + nav_mode = NAVIGATION_ORBIT; + } + + } else if (nav_scheme == NAVIGATION_MAYA) { + if (pan_gesture->get_alt()) + nav_mode = NAVIGATION_PAN; + } + + switch (nav_mode) { + case NAVIGATION_PAN: { + _nav_pan(m, pan_gesture->get_delta()); + + } break; + + case NAVIGATION_ZOOM: { + _nav_zoom(m, pan_gesture->get_delta()); + + } break; + + case NAVIGATION_ORBIT: { + _nav_orbit(m, pan_gesture->get_delta()); + + } break; + + case NAVIGATION_LOOK: { + _nav_look(m, pan_gesture->get_delta()); + + } break; + + default: { + } + } + } + + Ref k = p_event; + + if (k.is_valid()) { + if (!k->is_pressed()) + return; + + if (ED_IS_SHORTCUT("spatial_editor/snap", p_event)) { + if (_edit.mode != TRANSFORM_NONE) { + _edit.snap = !_edit.snap; + } + } + if (ED_IS_SHORTCUT("spatial_editor/bottom_view", p_event)) { + _menu_option(VIEW_BOTTOM); + } + if (ED_IS_SHORTCUT("spatial_editor/top_view", p_event)) { + _menu_option(VIEW_TOP); + } + if (ED_IS_SHORTCUT("spatial_editor/rear_view", p_event)) { + _menu_option(VIEW_REAR); + } + if (ED_IS_SHORTCUT("spatial_editor/front_view", p_event)) { + _menu_option(VIEW_FRONT); + } + if (ED_IS_SHORTCUT("spatial_editor/left_view", p_event)) { + _menu_option(VIEW_LEFT); + } + if (ED_IS_SHORTCUT("spatial_editor/right_view", p_event)) { + _menu_option(VIEW_RIGHT); + } + if (ED_IS_SHORTCUT("spatial_editor/focus_origin", p_event)) { + _menu_option(VIEW_CENTER_TO_ORIGIN); + } + if (ED_IS_SHORTCUT("spatial_editor/focus_selection", p_event)) { + _menu_option(VIEW_CENTER_TO_SELECTION); + } + // Orthgonal mode doesn't work in freelook. + if (!freelook_active && ED_IS_SHORTCUT("spatial_editor/switch_perspective_orthogonal", p_event)) { + _menu_option(orthogonal ? VIEW_PERSPECTIVE : VIEW_ORTHOGONAL); + _update_name(); + } + if (ED_IS_SHORTCUT("spatial_editor/align_transform_with_view", p_event)) { + _menu_option(VIEW_ALIGN_TRANSFORM_WITH_VIEW); + } + if (ED_IS_SHORTCUT("spatial_editor/align_rotation_with_view", p_event)) { + _menu_option(VIEW_ALIGN_ROTATION_WITH_VIEW); + } + if (ED_IS_SHORTCUT("spatial_editor/insert_anim_key", p_event)) { + if (!get_selected_count() || _edit.mode != TRANSFORM_NONE) + return; + + if (!AnimationPlayerEditor::singleton->get_track_editor()->has_keying()) { + set_message(TTR("Keying is disabled (no key inserted).")); + return; + } + + List &selection = editor_selection->get_selected_node_list(); + + for (List::Element *E = selection.front(); E; E = E->next()) { + + Node3D *sp = Object::cast_to(E->get()); + if (!sp) + continue; + + spatial_editor->emit_signal("transform_key_request", sp, "", sp->get_transform()); + } + + set_message(TTR("Animation Key Inserted.")); + } + + // Freelook doesn't work in orthogonal mode. + if (!orthogonal && ED_IS_SHORTCUT("spatial_editor/freelook_toggle", p_event)) { + set_freelook_active(!is_freelook_active()); + + } else if (k->get_keycode() == KEY_ESCAPE) { + set_freelook_active(false); + } + + if (k->get_keycode() == KEY_SPACE) { + if (!k->is_pressed()) emit_signal("toggle_maximize_view", this); + } + } + + // freelook uses most of the useful shortcuts, like save, so its ok + // to consider freelook active as end of the line for future events. + if (freelook_active) + accept_event(); +} + +void Node3DEditorViewport::_nav_pan(Ref p_event, const Vector2 &p_relative) { + + const NavigationScheme nav_scheme = (NavigationScheme)EditorSettings::get_singleton()->get("editors/3d/navigation/navigation_scheme").operator int(); + + real_t pan_speed = 1 / 150.0; + int pan_speed_modifier = 10; + if (nav_scheme == NAVIGATION_MAYA && p_event->get_shift()) + pan_speed *= pan_speed_modifier; + + Transform camera_transform; + + camera_transform.translate(cursor.pos); + camera_transform.basis.rotate(Vector3(1, 0, 0), -cursor.x_rot); + camera_transform.basis.rotate(Vector3(0, 1, 0), -cursor.y_rot); + Vector3 translation(-p_relative.x * pan_speed, p_relative.y * pan_speed, 0); + translation *= cursor.distance / DISTANCE_DEFAULT; + camera_transform.translate(translation); + cursor.pos = camera_transform.origin; +} + +void Node3DEditorViewport::_nav_zoom(Ref p_event, const Vector2 &p_relative) { + + const NavigationScheme nav_scheme = (NavigationScheme)EditorSettings::get_singleton()->get("editors/3d/navigation/navigation_scheme").operator int(); + + real_t zoom_speed = 1 / 80.0; + int zoom_speed_modifier = 10; + if (nav_scheme == NAVIGATION_MAYA && p_event->get_shift()) + zoom_speed *= zoom_speed_modifier; + + NavigationZoomStyle zoom_style = (NavigationZoomStyle)EditorSettings::get_singleton()->get("editors/3d/navigation/zoom_style").operator int(); + if (zoom_style == NAVIGATION_ZOOM_HORIZONTAL) { + if (p_relative.x > 0) + scale_cursor_distance(1 - p_relative.x * zoom_speed); + else if (p_relative.x < 0) + scale_cursor_distance(1.0 / (1 + p_relative.x * zoom_speed)); + } else { + if (p_relative.y > 0) + scale_cursor_distance(1 + p_relative.y * zoom_speed); + else if (p_relative.y < 0) + scale_cursor_distance(1.0 / (1 - p_relative.y * zoom_speed)); + } +} + +void Node3DEditorViewport::_nav_orbit(Ref p_event, const Vector2 &p_relative) { + + if (lock_rotation) { + _nav_pan(p_event, p_relative); + return; + } + + if (orthogonal && auto_orthogonal) { + _menu_option(VIEW_PERSPECTIVE); + } + + real_t degrees_per_pixel = EditorSettings::get_singleton()->get("editors/3d/navigation_feel/orbit_sensitivity"); + real_t radians_per_pixel = Math::deg2rad(degrees_per_pixel); + bool invert_y_axis = EditorSettings::get_singleton()->get("editors/3d/navigation/invert_y_axis"); + + if (invert_y_axis) { + cursor.x_rot -= p_relative.y * radians_per_pixel; + } else { + cursor.x_rot += p_relative.y * radians_per_pixel; + } + cursor.y_rot += p_relative.x * radians_per_pixel; + if (cursor.x_rot > Math_PI / 2.0) + cursor.x_rot = Math_PI / 2.0; + if (cursor.x_rot < -Math_PI / 2.0) + cursor.x_rot = -Math_PI / 2.0; + name = ""; + _update_name(); +} + +void Node3DEditorViewport::_nav_look(Ref p_event, const Vector2 &p_relative) { + + if (orthogonal) { + _nav_pan(p_event, p_relative); + return; + } + + if (orthogonal && auto_orthogonal) { + _menu_option(VIEW_PERSPECTIVE); + } + + real_t degrees_per_pixel = EditorSettings::get_singleton()->get("editors/3d/navigation_feel/orbit_sensitivity"); + real_t radians_per_pixel = Math::deg2rad(degrees_per_pixel); + bool invert_y_axis = EditorSettings::get_singleton()->get("editors/3d/navigation/invert_y_axis"); + + // Note: do NOT assume the camera has the "current" transform, because it is interpolated and may have "lag". + Transform prev_camera_transform = to_camera_transform(cursor); + + if (invert_y_axis) { + cursor.x_rot -= p_relative.y * radians_per_pixel; + } else { + cursor.x_rot += p_relative.y * radians_per_pixel; + } + cursor.y_rot += p_relative.x * radians_per_pixel; + if (cursor.x_rot > Math_PI / 2.0) + cursor.x_rot = Math_PI / 2.0; + if (cursor.x_rot < -Math_PI / 2.0) + cursor.x_rot = -Math_PI / 2.0; + + // Look is like the opposite of Orbit: the focus point rotates around the camera + Transform camera_transform = to_camera_transform(cursor); + Vector3 pos = camera_transform.xform(Vector3(0, 0, 0)); + Vector3 prev_pos = prev_camera_transform.xform(Vector3(0, 0, 0)); + Vector3 diff = prev_pos - pos; + cursor.pos += diff; + + name = ""; + _update_name(); +} + +void Node3DEditorViewport::set_freelook_active(bool active_now) { + + if (!freelook_active && active_now) { + // Sync camera cursor to cursor to "cut" interpolation jumps due to changing referential + cursor = camera_cursor; + + // Make sure eye_pos is synced, because freelook referential is eye pos rather than orbit pos + Vector3 forward = to_camera_transform(cursor).basis.xform(Vector3(0, 0, -1)); + cursor.eye_pos = cursor.pos - cursor.distance * forward; + // Also sync the camera cursor, otherwise switching to freelook will be trippy if inertia is active + camera_cursor.eye_pos = cursor.eye_pos; + + if (EditorSettings::get_singleton()->get("editors/3d/freelook/freelook_speed_zoom_link")) { + // Re-adjust freelook speed from the current zoom level + real_t base_speed = EditorSettings::get_singleton()->get("editors/3d/freelook/freelook_base_speed"); + freelook_speed = base_speed * cursor.distance; + } + + // Hide mouse like in an FPS (warping doesn't work) + DisplayServer::get_singleton()->mouse_set_mode(DisplayServer::MOUSE_MODE_CAPTURED); + + } else if (freelook_active && !active_now) { + // Sync camera cursor to cursor to "cut" interpolation jumps due to changing referential + cursor = camera_cursor; + + // Restore mouse + DisplayServer::get_singleton()->mouse_set_mode(DisplayServer::MOUSE_MODE_VISIBLE); + } + + freelook_active = active_now; +} + +void Node3DEditorViewport::scale_cursor_distance(real_t scale) { + + // Prevents zero distance which would short-circuit any scaling + if (cursor.distance < ZOOM_MIN_DISTANCE) + cursor.distance = ZOOM_MIN_DISTANCE; + + cursor.distance *= scale; + + if (cursor.distance < ZOOM_MIN_DISTANCE) + cursor.distance = ZOOM_MIN_DISTANCE; + + zoom_indicator_delay = ZOOM_INDICATOR_DELAY_S; + surface->update(); +} + +void Node3DEditorViewport::scale_freelook_speed(real_t scale) { + + // Prevents zero distance which would short-circuit any scaling + if (freelook_speed < FREELOOK_MIN_SPEED) + freelook_speed = FREELOOK_MIN_SPEED; + + freelook_speed *= scale; + + if (freelook_speed < FREELOOK_MIN_SPEED) + freelook_speed = FREELOOK_MIN_SPEED; + + zoom_indicator_delay = ZOOM_INDICATOR_DELAY_S; + surface->update(); +} + +Point2i Node3DEditorViewport::_get_warped_mouse_motion(const Ref &p_ev_mouse_motion) const { + Point2i relative; + if (bool(EDITOR_DEF("editors/3d/navigation/warped_mouse_panning", false))) { + relative = InputFilter::get_singleton()->warp_mouse_motion(p_ev_mouse_motion, surface->get_global_rect()); + } else { + relative = p_ev_mouse_motion->get_relative(); + } + return relative; +} + +static bool is_shortcut_pressed(const String &p_path) { + Ref shortcut = ED_GET_SHORTCUT(p_path); + if (shortcut.is_null()) { + return false; + } + InputEventKey *k = Object::cast_to(shortcut->get_shortcut().ptr()); + if (k == NULL) { + return false; + } + const InputFilter &input = *InputFilter::get_singleton(); + int keycode = k->get_keycode(); + return input.is_key_pressed(keycode); +} + +void Node3DEditorViewport::_update_freelook(real_t delta) { + + if (!is_freelook_active()) { + return; + } + + const Vector3 forward = camera->get_transform().basis.xform(Vector3(0, 0, -1)); + const Vector3 right = camera->get_transform().basis.xform(Vector3(1, 0, 0)); + const Vector3 up = camera->get_transform().basis.xform(Vector3(0, 1, 0)); + + Vector3 direction; + + if (is_shortcut_pressed("spatial_editor/freelook_left")) { + direction -= right; + } + if (is_shortcut_pressed("spatial_editor/freelook_right")) { + direction += right; + } + if (is_shortcut_pressed("spatial_editor/freelook_forward")) { + direction += forward; + } + if (is_shortcut_pressed("spatial_editor/freelook_backwards")) { + direction -= forward; + } + if (is_shortcut_pressed("spatial_editor/freelook_up")) { + direction += up; + } + if (is_shortcut_pressed("spatial_editor/freelook_down")) { + direction -= up; + } + + real_t speed = freelook_speed; + + if (is_shortcut_pressed("spatial_editor/freelook_speed_modifier")) { + speed *= 3.0; + } + if (is_shortcut_pressed("spatial_editor/freelook_slow_modifier")) { + speed *= 0.333333; + } + + const Vector3 motion = direction * speed * delta; + cursor.pos += motion; + cursor.eye_pos += motion; +} + +void Node3DEditorViewport::set_message(String p_message, float p_time) { + + message = p_message; + message_time = p_time; +} + +void Node3DEditorPlugin::edited_scene_changed() { + for (uint32_t i = 0; i < Node3DEditor::VIEWPORTS_COUNT; i++) { + Node3DEditorViewport *viewport = Node3DEditor::get_singleton()->get_editor_viewport(i); + if (viewport->is_visible()) { + viewport->notification(Control::NOTIFICATION_VISIBILITY_CHANGED); + } + } +} + +void Node3DEditorViewport::_notification(int p_what) { + + if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { + + bool visible = is_visible_in_tree(); + + set_process(visible); + + if (visible) { + orthogonal = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_ORTHOGONAL)); + _update_name(); + _update_camera(0); + } else { + set_freelook_active(false); + } + call_deferred("update_transform_gizmo_view"); + rotation_control->set_visible(EditorSettings::get_singleton()->get("editors/3d/navigation/show_viewport_rotation_gizmo")); + } + + if (p_what == NOTIFICATION_RESIZED) { + + call_deferred("update_transform_gizmo_view"); + } + + if (p_what == NOTIFICATION_READY) { + // The crosshair icon doesn't depend on the editor theme. + crosshair->set_texture(get_theme_icon("Crosshair", "EditorIcons")); + // Set the anchors and margins after changing the icon to ensure it's centered correctly. + crosshair->set_anchors_and_margins_preset(PRESET_CENTER); + } + + if (p_what == NOTIFICATION_PROCESS) { + + real_t delta = get_process_delta_time(); + + if (zoom_indicator_delay > 0) { + zoom_indicator_delay -= delta; + if (zoom_indicator_delay <= 0) { + surface->update(); + } + } + + _update_freelook(delta); + + Node *scene_root = editor->get_scene_tree_dock()->get_editor_data()->get_edited_scene_root(); + if (previewing_cinema && scene_root != NULL) { + Camera3D *cam = scene_root->get_viewport()->get_camera(); + if (cam != NULL && cam != previewing) { + //then switch the viewport's camera to the scene's viewport camera + if (previewing != NULL) { + previewing->disconnect("tree_exited", callable_mp(this, &Node3DEditorViewport::_preview_exited_scene)); + } + previewing = cam; + previewing->connect("tree_exited", callable_mp(this, &Node3DEditorViewport::_preview_exited_scene)); + VS::get_singleton()->viewport_attach_camera(viewport->get_viewport_rid(), cam->get_camera()); + surface->update(); + } + } + + _update_camera(delta); + + Map &selection = editor_selection->get_selection(); + + bool changed = false; + bool exist = false; + + for (Map::Element *E = selection.front(); E; E = E->next()) { + + Node3D *sp = Object::cast_to(E->key()); + if (!sp) + continue; + + Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data(sp); + if (!se) + continue; + + Transform t = sp->get_global_gizmo_transform(); + + exist = true; + if (se->last_xform == t && !se->last_xform_dirty) + continue; + changed = true; + se->last_xform_dirty = false; + se->last_xform = t; + + VisualInstance3D *vi = Object::cast_to(sp); + + se->aabb = vi ? vi->get_aabb() : _calculate_spatial_bounds(sp); + + t.translate(se->aabb.position); + + // apply AABB scaling before item's global transform + Basis aabb_s; + aabb_s.scale(se->aabb.size); + t.basis = t.basis * aabb_s; + + VisualServer::get_singleton()->instance_set_transform(se->sbox_instance, t); + } + + if (changed || (spatial_editor->is_gizmo_visible() && !exist)) { + spatial_editor->update_transform_gizmo(); + } + + if (message_time > 0) { + + if (message != last_message) { + surface->update(); + last_message = message; + } + + message_time -= get_physics_process_delta_time(); + if (message_time < 0) + surface->update(); + } + + //update shadow atlas if changed + + int shadowmap_size = ProjectSettings::get_singleton()->get("rendering/quality/shadow_atlas/size"); + int atlas_q0 = ProjectSettings::get_singleton()->get("rendering/quality/shadow_atlas/quadrant_0_subdiv"); + int atlas_q1 = ProjectSettings::get_singleton()->get("rendering/quality/shadow_atlas/quadrant_1_subdiv"); + int atlas_q2 = ProjectSettings::get_singleton()->get("rendering/quality/shadow_atlas/quadrant_2_subdiv"); + int atlas_q3 = ProjectSettings::get_singleton()->get("rendering/quality/shadow_atlas/quadrant_3_subdiv"); + + viewport->set_shadow_atlas_size(shadowmap_size); + viewport->set_shadow_atlas_quadrant_subdiv(0, Viewport::ShadowAtlasQuadrantSubdiv(atlas_q0)); + viewport->set_shadow_atlas_quadrant_subdiv(1, Viewport::ShadowAtlasQuadrantSubdiv(atlas_q1)); + viewport->set_shadow_atlas_quadrant_subdiv(2, Viewport::ShadowAtlasQuadrantSubdiv(atlas_q2)); + viewport->set_shadow_atlas_quadrant_subdiv(3, Viewport::ShadowAtlasQuadrantSubdiv(atlas_q3)); + + bool shrink = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_HALF_RESOLUTION)); + + if (shrink != (viewport_container->get_stretch_shrink() > 1)) { + viewport_container->set_stretch_shrink(shrink ? 2 : 1); + } + + //update msaa if changed + + int msaa_mode = ProjectSettings::get_singleton()->get("rendering/quality/filters/msaa"); + viewport->set_msaa(Viewport::MSAA(msaa_mode)); + + bool show_info = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_INFORMATION)); + info_label->set_visible(show_info); + + Camera3D *current_camera; + + if (previewing) { + current_camera = previewing; + } else { + current_camera = camera; + } + + // Display the crosshair only while freelooking. Hide it otherwise, + // as the crosshair can be distracting. + crosshair->set_visible(freelook_active); + + if (show_info) { + String text; + text += "X: " + rtos(current_camera->get_translation().x).pad_decimals(1) + "\n"; + text += "Y: " + rtos(current_camera->get_translation().y).pad_decimals(1) + "\n"; + text += "Z: " + rtos(current_camera->get_translation().z).pad_decimals(1) + "\n"; + text += TTR("Pitch") + ": " + itos(Math::round(current_camera->get_rotation_degrees().x)) + "\n"; + text += TTR("Yaw") + ": " + itos(Math::round(current_camera->get_rotation_degrees().y)) + "\n\n"; + text += TTR("Objects Drawn") + ": " + itos(viewport->get_render_info(Viewport::RENDER_INFO_OBJECTS_IN_FRAME)) + "\n"; + text += TTR("Material Changes") + ": " + itos(viewport->get_render_info(Viewport::RENDER_INFO_MATERIAL_CHANGES_IN_FRAME)) + "\n"; + text += TTR("Shader Changes") + ": " + itos(viewport->get_render_info(Viewport::RENDER_INFO_SHADER_CHANGES_IN_FRAME)) + "\n"; + text += TTR("Surface Changes") + ": " + itos(viewport->get_render_info(Viewport::RENDER_INFO_SURFACE_CHANGES_IN_FRAME)) + "\n"; + text += TTR("Draw Calls") + ": " + itos(viewport->get_render_info(Viewport::RENDER_INFO_DRAW_CALLS_IN_FRAME)) + "\n"; + text += TTR("Vertices") + ": " + itos(viewport->get_render_info(Viewport::RENDER_INFO_VERTICES_IN_FRAME)); + info_label->set_text(text); + } + + // FPS Counter. + bool show_fps = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_FPS)); + fps_label->set_visible(show_fps); + + if (show_fps) { + String text; + const float temp_fps = Engine::get_singleton()->get_frames_per_second(); + text += TTR(vformat("FPS: %d (%s ms)", temp_fps, String::num(1000.0f / temp_fps, 2))); + fps_label->set_text(text); + } + + bool show_cinema = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_CINEMATIC_PREVIEW)); + cinema_label->set_visible(show_cinema); + if (show_cinema) { + float cinema_half_width = cinema_label->get_size().width / 2.0f; + cinema_label->set_anchor_and_margin(MARGIN_LEFT, 0.5f, -cinema_half_width); + } + + if (lock_rotation) { + float locked_half_width = locked_label->get_size().width / 2.0f; + locked_label->set_anchor_and_margin(MARGIN_LEFT, 0.5f, -locked_half_width); + } + } + + if (p_what == NOTIFICATION_ENTER_TREE) { + + surface->connect("draw", callable_mp(this, &Node3DEditorViewport::_draw)); + surface->connect("gui_input", callable_mp(this, &Node3DEditorViewport::_sinput)); + surface->connect("mouse_entered", callable_mp(this, &Node3DEditorViewport::_surface_mouse_enter)); + surface->connect("mouse_exited", callable_mp(this, &Node3DEditorViewport::_surface_mouse_exit)); + surface->connect("focus_entered", callable_mp(this, &Node3DEditorViewport::_surface_focus_enter)); + surface->connect("focus_exited", callable_mp(this, &Node3DEditorViewport::_surface_focus_exit)); + + _init_gizmo_instance(index); + } + + if (p_what == NOTIFICATION_EXIT_TREE) { + + _finish_gizmo_instances(); + } + + if (p_what == NOTIFICATION_THEME_CHANGED) { + + view_menu->set_icon(get_theme_icon("GuiTabMenu", "EditorIcons")); + preview_camera->set_icon(get_theme_icon("Camera3D", "EditorIcons")); + + view_menu->add_theme_style_override("normal", editor->get_gui_base()->get_theme_stylebox("Information3dViewport", "EditorStyles")); + view_menu->add_theme_style_override("hover", editor->get_gui_base()->get_theme_stylebox("Information3dViewport", "EditorStyles")); + view_menu->add_theme_style_override("pressed", editor->get_gui_base()->get_theme_stylebox("Information3dViewport", "EditorStyles")); + view_menu->add_theme_style_override("focus", editor->get_gui_base()->get_theme_stylebox("Information3dViewport", "EditorStyles")); + view_menu->add_theme_style_override("disabled", editor->get_gui_base()->get_theme_stylebox("Information3dViewport", "EditorStyles")); + + preview_camera->add_theme_style_override("normal", editor->get_gui_base()->get_theme_stylebox("Information3dViewport", "EditorStyles")); + preview_camera->add_theme_style_override("hover", editor->get_gui_base()->get_theme_stylebox("Information3dViewport", "EditorStyles")); + preview_camera->add_theme_style_override("pressed", editor->get_gui_base()->get_theme_stylebox("Information3dViewport", "EditorStyles")); + preview_camera->add_theme_style_override("focus", editor->get_gui_base()->get_theme_stylebox("Information3dViewport", "EditorStyles")); + preview_camera->add_theme_style_override("disabled", editor->get_gui_base()->get_theme_stylebox("Information3dViewport", "EditorStyles")); + + info_label->add_theme_style_override("normal", editor->get_gui_base()->get_theme_stylebox("Information3dViewport", "EditorStyles")); + fps_label->add_theme_style_override("normal", editor->get_gui_base()->get_theme_stylebox("Information3dViewport", "EditorStyles")); + cinema_label->add_theme_style_override("normal", editor->get_gui_base()->get_theme_stylebox("Information3dViewport", "EditorStyles")); + locked_label->add_theme_style_override("normal", editor->get_gui_base()->get_theme_stylebox("Information3dViewport", "EditorStyles")); + } +} + +static void draw_indicator_bar(Control &surface, real_t fill, Ref icon) { + + // Adjust bar size from control height + Vector2 surface_size = surface.get_size(); + real_t h = surface_size.y / 2.0; + real_t y = (surface_size.y - h) / 2.0; + + Rect2 r(10, y, 6, h); + real_t sy = r.size.y * fill; + + // Note: because this bar appears over the viewport, it has to stay readable for any background color + // Draw both neutral dark and bright colors to account this + surface.draw_rect(r, Color(1, 1, 1, 0.2)); + surface.draw_rect(Rect2(r.position.x, r.position.y + r.size.y - sy, r.size.x, sy), Color(1, 1, 1, 0.6)); + surface.draw_rect(r.grow(1), Color(0, 0, 0, 0.7), false, Math::round(EDSCALE)); + + Vector2 icon_size = icon->get_size(); + Vector2 icon_pos = Vector2(r.position.x - (icon_size.x - r.size.x) / 2, r.position.y + r.size.y + 2); + surface.draw_texture(icon, icon_pos); +} + +void Node3DEditorViewport::_draw() { + + EditorPluginList *over_plugin_list = EditorNode::get_singleton()->get_editor_plugins_over(); + if (!over_plugin_list->empty()) { + over_plugin_list->forward_spatial_draw_over_viewport(surface); + } + + EditorPluginList *force_over_plugin_list = editor->get_editor_plugins_force_over(); + if (!force_over_plugin_list->empty()) { + force_over_plugin_list->forward_spatial_force_draw_over_viewport(surface); + } + + if (surface->has_focus()) { + Size2 size = surface->get_size(); + Rect2 r = Rect2(Point2(), size); + get_theme_stylebox("Focus", "EditorStyles")->draw(surface->get_canvas_item(), r); + } + + if (cursor.region_select) { + const Rect2 selection_rect = Rect2(cursor.region_begin, cursor.region_end - cursor.region_begin); + + surface->draw_rect( + selection_rect, + get_theme_color("box_selection_fill_color", "Editor")); + + surface->draw_rect( + selection_rect, + get_theme_color("box_selection_stroke_color", "Editor"), + false, + Math::round(EDSCALE)); + } + + RID ci = surface->get_canvas_item(); + + if (message_time > 0) { + Ref font = get_theme_font("font", "Label"); + Point2 msgpos = Point2(5, get_size().y - 20); + font->draw(ci, msgpos + Point2(1, 1), message, Color(0, 0, 0, 0.8)); + font->draw(ci, msgpos + Point2(-1, -1), message, Color(0, 0, 0, 0.8)); + font->draw(ci, msgpos, message, Color(1, 1, 1, 1)); + } + + if (_edit.mode == TRANSFORM_ROTATE) { + + Point2 center = _point_to_screen(_edit.center); + VisualServer::get_singleton()->canvas_item_add_line( + ci, + _edit.mouse_pos, + center, + get_theme_color("accent_color", "Editor") * Color(1, 1, 1, 0.6), + Math::round(2 * EDSCALE)); + } + if (previewing) { + + Size2 ss = Size2(ProjectSettings::get_singleton()->get("display/window/size/width"), ProjectSettings::get_singleton()->get("display/window/size/height")); + float aspect = ss.aspect(); + Size2 s = get_size(); + + Rect2 draw_rect; + + switch (previewing->get_keep_aspect_mode()) { + case Camera3D::KEEP_WIDTH: { + + draw_rect.size = Size2(s.width, s.width / aspect); + draw_rect.position.x = 0; + draw_rect.position.y = (s.height - draw_rect.size.y) * 0.5; + + } break; + case Camera3D::KEEP_HEIGHT: { + + draw_rect.size = Size2(s.height * aspect, s.height); + draw_rect.position.y = 0; + draw_rect.position.x = (s.width - draw_rect.size.x) * 0.5; + + } break; + } + + draw_rect = Rect2(Vector2(), s).clip(draw_rect); + + surface->draw_rect(draw_rect, Color(0.6, 0.6, 0.1, 0.5), false, Math::round(2 * EDSCALE)); + + } else { + + if (zoom_indicator_delay > 0.0) { + + if (is_freelook_active()) { + // Show speed + + real_t min_speed = FREELOOK_MIN_SPEED; + real_t max_speed = camera->get_zfar(); + real_t scale_length = (max_speed - min_speed); + + if (!Math::is_zero_approx(scale_length)) { + real_t logscale_t = 1.0 - Math::log(1 + freelook_speed - min_speed) / Math::log(1 + scale_length); + + // There is no real maximum speed so that factor can become negative, + // Let's make it look asymptotic instead (will decrease slower and slower). + if (logscale_t < 0.25) + logscale_t = 0.25 * Math::exp(4.0 * logscale_t - 1.0); + + draw_indicator_bar(*surface, 1.0 - logscale_t, get_theme_icon("ViewportSpeed", "EditorIcons")); + } + + } else { + // Show zoom + + real_t min_distance = ZOOM_MIN_DISTANCE; // TODO Why not pick znear to limit zoom? + real_t max_distance = camera->get_zfar(); + real_t scale_length = (max_distance - min_distance); + + if (!Math::is_zero_approx(scale_length)) { + real_t logscale_t = 1.0 - Math::log(1 + cursor.distance - min_distance) / Math::log(1 + scale_length); + + // There is no real maximum distance so that factor can become negative, + // Let's make it look asymptotic instead (will decrease slower and slower). + if (logscale_t < 0.25) + logscale_t = 0.25 * Math::exp(4.0 * logscale_t - 1.0); + + draw_indicator_bar(*surface, logscale_t, get_theme_icon("ViewportZoom", "EditorIcons")); + } + } + } + } +} + +void Node3DEditorViewport::_menu_option(int p_option) { + + switch (p_option) { + + case VIEW_TOP: { + + cursor.y_rot = 0; + cursor.x_rot = Math_PI / 2.0; + set_message(TTR("Top View."), 2); + name = TTR("Top"); + _set_auto_orthogonal(); + _update_name(); + + } break; + case VIEW_BOTTOM: { + + cursor.y_rot = 0; + cursor.x_rot = -Math_PI / 2.0; + set_message(TTR("Bottom View."), 2); + name = TTR("Bottom"); + _set_auto_orthogonal(); + _update_name(); + + } break; + case VIEW_LEFT: { + + cursor.x_rot = 0; + cursor.y_rot = Math_PI / 2.0; + set_message(TTR("Left View."), 2); + name = TTR("Left"); + _set_auto_orthogonal(); + _update_name(); + + } break; + case VIEW_RIGHT: { + + cursor.x_rot = 0; + cursor.y_rot = -Math_PI / 2.0; + set_message(TTR("Right View."), 2); + name = TTR("Right"); + _set_auto_orthogonal(); + _update_name(); + + } break; + case VIEW_FRONT: { + + cursor.x_rot = 0; + cursor.y_rot = 0; + set_message(TTR("Front View."), 2); + name = TTR("Front"); + _set_auto_orthogonal(); + _update_name(); + + } break; + case VIEW_REAR: { + + cursor.x_rot = 0; + cursor.y_rot = Math_PI; + set_message(TTR("Rear View."), 2); + name = TTR("Rear"); + _set_auto_orthogonal(); + _update_name(); + + } break; + case VIEW_CENTER_TO_ORIGIN: { + + cursor.pos = Vector3(0, 0, 0); + + } break; + case VIEW_CENTER_TO_SELECTION: { + + focus_selection(); + + } break; + case VIEW_ALIGN_TRANSFORM_WITH_VIEW: { + + if (!get_selected_count()) + break; + + Transform camera_transform = camera->get_global_transform(); + + List &selection = editor_selection->get_selected_node_list(); + + undo_redo->create_action(TTR("Align Transform with View")); + + for (List::Element *E = selection.front(); E; E = E->next()) { + + Node3D *sp = Object::cast_to(E->get()); + if (!sp) + continue; + + Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data(sp); + if (!se) + continue; + + Transform xform; + if (orthogonal) { + xform = sp->get_global_transform(); + xform.basis.set_euler(camera_transform.basis.get_euler()); + } else { + xform = camera_transform; + xform.scale_basis(sp->get_scale()); + } + + undo_redo->add_do_method(sp, "set_global_transform", xform); + undo_redo->add_undo_method(sp, "set_global_transform", sp->get_global_gizmo_transform()); + } + undo_redo->commit_action(); + focus_selection(); + + } break; + case VIEW_ALIGN_ROTATION_WITH_VIEW: { + + if (!get_selected_count()) + break; + + Transform camera_transform = camera->get_global_transform(); + + List &selection = editor_selection->get_selected_node_list(); + + undo_redo->create_action(TTR("Align Rotation with View")); + for (List::Element *E = selection.front(); E; E = E->next()) { + + Node3D *sp = Object::cast_to(E->get()); + if (!sp) + continue; + + Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data(sp); + if (!se) + continue; + + undo_redo->add_do_method(sp, "set_rotation", camera_transform.basis.get_rotation()); + undo_redo->add_undo_method(sp, "set_rotation", sp->get_rotation()); + } + undo_redo->commit_action(); + + } break; + case VIEW_ENVIRONMENT: { + + int idx = view_menu->get_popup()->get_item_index(VIEW_ENVIRONMENT); + bool current = view_menu->get_popup()->is_item_checked(idx); + current = !current; + if (current) { + + camera->set_environment(RES()); + } else { + + camera->set_environment(Node3DEditor::get_singleton()->get_viewport_environment()); + } + + view_menu->get_popup()->set_item_checked(idx, current); + + } break; + case VIEW_PERSPECTIVE: { + + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_PERSPECTIVE), true); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_ORTHOGONAL), false); + orthogonal = false; + auto_orthogonal = false; + call_deferred("update_transform_gizmo_view"); + _update_name(); + + } break; + case VIEW_ORTHOGONAL: { + + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_PERSPECTIVE), false); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_ORTHOGONAL), true); + orthogonal = true; + auto_orthogonal = false; + call_deferred("update_transform_gizmo_view"); + _update_name(); + + } break; + case VIEW_AUTO_ORTHOGONAL: { + + int idx = view_menu->get_popup()->get_item_index(VIEW_AUTO_ORTHOGONAL); + bool current = view_menu->get_popup()->is_item_checked(idx); + current = !current; + view_menu->get_popup()->set_item_checked(idx, current); + if (auto_orthogonal) { + auto_orthogonal = false; + _update_name(); + } + } break; + case VIEW_LOCK_ROTATION: { + + int idx = view_menu->get_popup()->get_item_index(VIEW_LOCK_ROTATION); + bool current = view_menu->get_popup()->is_item_checked(idx); + lock_rotation = !current; + view_menu->get_popup()->set_item_checked(idx, !current); + if (lock_rotation) { + locked_label->show(); + } else { + locked_label->hide(); + } + + } break; + case VIEW_AUDIO_LISTENER: { + + int idx = view_menu->get_popup()->get_item_index(VIEW_AUDIO_LISTENER); + bool current = view_menu->get_popup()->is_item_checked(idx); + current = !current; + viewport->set_as_audio_listener(current); + view_menu->get_popup()->set_item_checked(idx, current); + + } break; + case VIEW_AUDIO_DOPPLER: { + + int idx = view_menu->get_popup()->get_item_index(VIEW_AUDIO_DOPPLER); + bool current = view_menu->get_popup()->is_item_checked(idx); + current = !current; + camera->set_doppler_tracking(current ? Camera3D::DOPPLER_TRACKING_IDLE_STEP : Camera3D::DOPPLER_TRACKING_DISABLED); + view_menu->get_popup()->set_item_checked(idx, current); + + } break; + case VIEW_CINEMATIC_PREVIEW: { + + int idx = view_menu->get_popup()->get_item_index(VIEW_CINEMATIC_PREVIEW); + bool current = view_menu->get_popup()->is_item_checked(idx); + current = !current; + view_menu->get_popup()->set_item_checked(idx, current); + previewing_cinema = true; + _toggle_cinema_preview(current); + + if (current) { + preview_camera->hide(); + } else { + if (previewing != NULL) + preview_camera->show(); + } + } break; + case VIEW_GIZMOS: { + + int idx = view_menu->get_popup()->get_item_index(VIEW_GIZMOS); + bool current = view_menu->get_popup()->is_item_checked(idx); + current = !current; + if (current) + camera->set_cull_mask(((1 << 20) - 1) | (1 << (GIZMO_BASE_LAYER + index)) | (1 << GIZMO_EDIT_LAYER) | (1 << GIZMO_GRID_LAYER)); + else + camera->set_cull_mask(((1 << 20) - 1) | (1 << (GIZMO_BASE_LAYER + index)) | (1 << GIZMO_GRID_LAYER)); + view_menu->get_popup()->set_item_checked(idx, current); + + } break; + case VIEW_HALF_RESOLUTION: { + + int idx = view_menu->get_popup()->get_item_index(VIEW_HALF_RESOLUTION); + bool current = view_menu->get_popup()->is_item_checked(idx); + current = !current; + view_menu->get_popup()->set_item_checked(idx, current); + } break; + case VIEW_INFORMATION: { + + int idx = view_menu->get_popup()->get_item_index(VIEW_INFORMATION); + bool current = view_menu->get_popup()->is_item_checked(idx); + view_menu->get_popup()->set_item_checked(idx, !current); + + } break; + case VIEW_FPS: { + + int idx = view_menu->get_popup()->get_item_index(VIEW_FPS); + bool current = view_menu->get_popup()->is_item_checked(idx); + view_menu->get_popup()->set_item_checked(idx, !current); + + } break; + case VIEW_DISPLAY_NORMAL: + case VIEW_DISPLAY_WIREFRAME: + case VIEW_DISPLAY_OVERDRAW: + case VIEW_DISPLAY_SHADELESS: + case VIEW_DISPLAY_LIGHTING: + case VIEW_DISPLAY_NORMAL_BUFFER: + case VIEW_DISPLAY_DEBUG_SHADOW_ATLAS: + case VIEW_DISPLAY_DEBUG_DIRECTIONAL_SHADOW_ATLAS: + case VIEW_DISPLAY_DEBUG_GIPROBE_ALBEDO: + case VIEW_DISPLAY_DEBUG_GIPROBE_LIGHTING: + case VIEW_DISPLAY_DEBUG_GIPROBE_EMISSION: + case VIEW_DISPLAY_DEBUG_SCENE_LUMINANCE: + case VIEW_DISPLAY_DEBUG_SSAO: + case VIEW_DISPLAY_DEBUG_ROUGHNESS_LIMITER: { + + static const int display_options[] = { + VIEW_DISPLAY_NORMAL, + VIEW_DISPLAY_WIREFRAME, + VIEW_DISPLAY_OVERDRAW, + VIEW_DISPLAY_SHADELESS, + VIEW_DISPLAY_LIGHTING, + VIEW_DISPLAY_NORMAL_BUFFER, + VIEW_DISPLAY_WIREFRAME, + VIEW_DISPLAY_DEBUG_SHADOW_ATLAS, + VIEW_DISPLAY_DEBUG_DIRECTIONAL_SHADOW_ATLAS, + VIEW_DISPLAY_DEBUG_GIPROBE_ALBEDO, + VIEW_DISPLAY_DEBUG_GIPROBE_LIGHTING, + VIEW_DISPLAY_DEBUG_GIPROBE_EMISSION, + VIEW_DISPLAY_DEBUG_SCENE_LUMINANCE, + VIEW_DISPLAY_DEBUG_SSAO, + VIEW_DISPLAY_DEBUG_ROUGHNESS_LIMITER, + VIEW_MAX + }; + static const Viewport::DebugDraw debug_draw_modes[] = { + Viewport::DEBUG_DRAW_DISABLED, + Viewport::DEBUG_DRAW_WIREFRAME, + Viewport::DEBUG_DRAW_OVERDRAW, + Viewport::DEBUG_DRAW_UNSHADED, + Viewport::DEBUG_DRAW_LIGHTING, + Viewport::DEBUG_DRAW_NORMAL_BUFFER, + Viewport::DEBUG_DRAW_WIREFRAME, + Viewport::DEBUG_DRAW_SHADOW_ATLAS, + Viewport::DEBUG_DRAW_DIRECTIONAL_SHADOW_ATLAS, + Viewport::DEBUG_DRAW_GI_PROBE_ALBEDO, + Viewport::DEBUG_DRAW_GI_PROBE_LIGHTING, + Viewport::DEBUG_DRAW_GI_PROBE_EMISSION, + Viewport::DEBUG_DRAW_SCENE_LUMINANCE, + Viewport::DEBUG_DRAW_SSAO, + Viewport::DEBUG_DRAW_ROUGHNESS_LIMITER, + }; + + int idx = 0; + + while (display_options[idx] != VIEW_MAX) { + + int id = display_options[idx]; + int item_idx = view_menu->get_popup()->get_item_index(id); + if (item_idx != -1) { + view_menu->get_popup()->set_item_checked(item_idx, id == p_option); + } + item_idx = display_submenu->get_item_index(id); + if (item_idx != -1) { + display_submenu->set_item_checked(item_idx, id == p_option); + } + + if (id == p_option) { + viewport->set_debug_draw(debug_draw_modes[idx]); + } + idx++; + } + } break; + } +} + +void Node3DEditorViewport::_set_auto_orthogonal() { + if (!orthogonal && view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_AUTO_ORTHOGONAL))) { + _menu_option(VIEW_ORTHOGONAL); + auto_orthogonal = true; + } +} + +void Node3DEditorViewport::_preview_exited_scene() { + + preview_camera->disconnect("toggled", callable_mp(this, &Node3DEditorViewport::_toggle_camera_preview)); + preview_camera->set_pressed(false); + _toggle_camera_preview(false); + preview_camera->connect("toggled", callable_mp(this, &Node3DEditorViewport::_toggle_camera_preview)); + view_menu->show(); +} + +void Node3DEditorViewport::_init_gizmo_instance(int p_idx) { + + uint32_t layer = 1 << (GIZMO_BASE_LAYER + p_idx); + + for (int i = 0; i < 3; i++) { + move_gizmo_instance[i] = VS::get_singleton()->instance_create(); + VS::get_singleton()->instance_set_base(move_gizmo_instance[i], spatial_editor->get_move_gizmo(i)->get_rid()); + VS::get_singleton()->instance_set_scenario(move_gizmo_instance[i], get_tree()->get_root()->get_world()->get_scenario()); + VS::get_singleton()->instance_set_visible(move_gizmo_instance[i], false); + VS::get_singleton()->instance_geometry_set_cast_shadows_setting(move_gizmo_instance[i], VS::SHADOW_CASTING_SETTING_OFF); + VS::get_singleton()->instance_set_layer_mask(move_gizmo_instance[i], layer); + + move_plane_gizmo_instance[i] = VS::get_singleton()->instance_create(); + VS::get_singleton()->instance_set_base(move_plane_gizmo_instance[i], spatial_editor->get_move_plane_gizmo(i)->get_rid()); + VS::get_singleton()->instance_set_scenario(move_plane_gizmo_instance[i], get_tree()->get_root()->get_world()->get_scenario()); + VS::get_singleton()->instance_set_visible(move_plane_gizmo_instance[i], false); + VS::get_singleton()->instance_geometry_set_cast_shadows_setting(move_plane_gizmo_instance[i], VS::SHADOW_CASTING_SETTING_OFF); + VS::get_singleton()->instance_set_layer_mask(move_plane_gizmo_instance[i], layer); + + rotate_gizmo_instance[i] = VS::get_singleton()->instance_create(); + VS::get_singleton()->instance_set_base(rotate_gizmo_instance[i], spatial_editor->get_rotate_gizmo(i)->get_rid()); + VS::get_singleton()->instance_set_scenario(rotate_gizmo_instance[i], get_tree()->get_root()->get_world()->get_scenario()); + VS::get_singleton()->instance_set_visible(rotate_gizmo_instance[i], false); + VS::get_singleton()->instance_geometry_set_cast_shadows_setting(rotate_gizmo_instance[i], VS::SHADOW_CASTING_SETTING_OFF); + VS::get_singleton()->instance_set_layer_mask(rotate_gizmo_instance[i], layer); + + scale_gizmo_instance[i] = VS::get_singleton()->instance_create(); + VS::get_singleton()->instance_set_base(scale_gizmo_instance[i], spatial_editor->get_scale_gizmo(i)->get_rid()); + VS::get_singleton()->instance_set_scenario(scale_gizmo_instance[i], get_tree()->get_root()->get_world()->get_scenario()); + VS::get_singleton()->instance_set_visible(scale_gizmo_instance[i], false); + VS::get_singleton()->instance_geometry_set_cast_shadows_setting(scale_gizmo_instance[i], VS::SHADOW_CASTING_SETTING_OFF); + VS::get_singleton()->instance_set_layer_mask(scale_gizmo_instance[i], layer); + + scale_plane_gizmo_instance[i] = VS::get_singleton()->instance_create(); + VS::get_singleton()->instance_set_base(scale_plane_gizmo_instance[i], spatial_editor->get_scale_plane_gizmo(i)->get_rid()); + VS::get_singleton()->instance_set_scenario(scale_plane_gizmo_instance[i], get_tree()->get_root()->get_world()->get_scenario()); + VS::get_singleton()->instance_set_visible(scale_plane_gizmo_instance[i], false); + VS::get_singleton()->instance_geometry_set_cast_shadows_setting(scale_plane_gizmo_instance[i], VS::SHADOW_CASTING_SETTING_OFF); + VS::get_singleton()->instance_set_layer_mask(scale_plane_gizmo_instance[i], layer); + } +} + +void Node3DEditorViewport::_finish_gizmo_instances() { + + for (int i = 0; i < 3; i++) { + VS::get_singleton()->free(move_gizmo_instance[i]); + VS::get_singleton()->free(move_plane_gizmo_instance[i]); + VS::get_singleton()->free(rotate_gizmo_instance[i]); + VS::get_singleton()->free(scale_gizmo_instance[i]); + VS::get_singleton()->free(scale_plane_gizmo_instance[i]); + } +} +void Node3DEditorViewport::_toggle_camera_preview(bool p_activate) { + + ERR_FAIL_COND(p_activate && !preview); + ERR_FAIL_COND(!p_activate && !previewing); + + if (!p_activate) { + + previewing->disconnect("tree_exiting", callable_mp(this, &Node3DEditorViewport::_preview_exited_scene)); + previewing = NULL; + VS::get_singleton()->viewport_attach_camera(viewport->get_viewport_rid(), camera->get_camera()); //restore + if (!preview) + preview_camera->hide(); + view_menu->set_disabled(false); + surface->update(); + + } else { + + previewing = preview; + previewing->connect("tree_exiting", callable_mp(this, &Node3DEditorViewport::_preview_exited_scene)); + VS::get_singleton()->viewport_attach_camera(viewport->get_viewport_rid(), preview->get_camera()); //replace + view_menu->set_disabled(true); + surface->update(); + } +} + +void Node3DEditorViewport::_toggle_cinema_preview(bool p_activate) { + previewing_cinema = p_activate; + if (!previewing_cinema) { + if (previewing != NULL) + previewing->disconnect("tree_exited", callable_mp(this, &Node3DEditorViewport::_preview_exited_scene)); + + previewing = NULL; + VS::get_singleton()->viewport_attach_camera(viewport->get_viewport_rid(), camera->get_camera()); //restore + preview_camera->set_pressed(false); + if (!preview) { + preview_camera->hide(); + } else { + preview_camera->show(); + } + view_menu->show(); + surface->update(); + } +} + +void Node3DEditorViewport::_selection_result_pressed(int p_result) { + + if (selection_results.size() <= p_result) + return; + + clicked = selection_results[p_result].item->get_instance_id(); + + if (clicked.is_valid()) { + _select_clicked(clicked_wants_append, true, spatial_editor->get_tool_mode() != Node3DEditor::TOOL_MODE_LIST_SELECT); + clicked = ObjectID(); + } +} + +void Node3DEditorViewport::_selection_menu_hide() { + + selection_results.clear(); + selection_menu->clear(); + selection_menu->set_size(Vector2(0, 0)); +} + +void Node3DEditorViewport::set_can_preview(Camera3D *p_preview) { + + preview = p_preview; + + if (!preview_camera->is_pressed() && !previewing_cinema) + preview_camera->set_visible(p_preview); +} + +void Node3DEditorViewport::update_transform_gizmo_view() { + + if (!is_visible_in_tree()) + return; + + Transform xform = spatial_editor->get_gizmo_transform(); + + Transform camera_xform = camera->get_transform(); + + if (xform.origin.distance_squared_to(camera_xform.origin) < 0.01) { + for (int i = 0; i < 3; i++) { + VisualServer::get_singleton()->instance_set_visible(move_gizmo_instance[i], false); + VisualServer::get_singleton()->instance_set_visible(move_plane_gizmo_instance[i], false); + VisualServer::get_singleton()->instance_set_visible(rotate_gizmo_instance[i], false); + VisualServer::get_singleton()->instance_set_visible(scale_gizmo_instance[i], false); + VisualServer::get_singleton()->instance_set_visible(scale_plane_gizmo_instance[i], false); + } + return; + } + + Vector3 camz = -camera_xform.get_basis().get_axis(2).normalized(); + Vector3 camy = -camera_xform.get_basis().get_axis(1).normalized(); + Plane p(camera_xform.origin, camz); + float gizmo_d = Math::abs(p.distance_to(xform.origin)); + float d0 = camera->unproject_position(camera_xform.origin + camz * gizmo_d).y; + float d1 = camera->unproject_position(camera_xform.origin + camz * gizmo_d + camy).y; + float dd = Math::abs(d0 - d1); + if (dd == 0) + dd = 0.0001; + + float gizmo_size = EditorSettings::get_singleton()->get("editors/3d/manipulator_gizmo_size"); + // At low viewport heights, multiply the gizmo scale based on the viewport height. + // This prevents the gizmo from growing very large and going outside the viewport. + const int viewport_base_height = 400 * MAX(1, EDSCALE); + gizmo_scale = + (gizmo_size / Math::abs(dd)) * MAX(1, EDSCALE) * + MIN(viewport_base_height, viewport_container->get_size().height) / viewport_base_height / + viewport_container->get_stretch_shrink(); + Vector3 scale = Vector3(1, 1, 1) * gizmo_scale; + + xform.basis.scale(scale); + + for (int i = 0; i < 3; i++) { + VisualServer::get_singleton()->instance_set_transform(move_gizmo_instance[i], xform); + VisualServer::get_singleton()->instance_set_visible(move_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_MOVE)); + VisualServer::get_singleton()->instance_set_transform(move_plane_gizmo_instance[i], xform); + VisualServer::get_singleton()->instance_set_visible(move_plane_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_MOVE)); + VisualServer::get_singleton()->instance_set_transform(rotate_gizmo_instance[i], xform); + VisualServer::get_singleton()->instance_set_visible(rotate_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_ROTATE)); + VisualServer::get_singleton()->instance_set_transform(scale_gizmo_instance[i], xform); + VisualServer::get_singleton()->instance_set_visible(scale_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SCALE)); + VisualServer::get_singleton()->instance_set_transform(scale_plane_gizmo_instance[i], xform); + VisualServer::get_singleton()->instance_set_visible(scale_plane_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SCALE)); + } +} + +void Node3DEditorViewport::set_state(const Dictionary &p_state) { + + if (p_state.has("position")) + cursor.pos = p_state["position"]; + if (p_state.has("x_rotation")) + cursor.x_rot = p_state["x_rotation"]; + if (p_state.has("y_rotation")) + cursor.y_rot = p_state["y_rotation"]; + if (p_state.has("distance")) + cursor.distance = p_state["distance"]; + + if (p_state.has("use_orthogonal")) { + bool orth = p_state["use_orthogonal"]; + + if (orth) + _menu_option(VIEW_ORTHOGONAL); + else + _menu_option(VIEW_PERSPECTIVE); + } + if (p_state.has("view_name")) { + name = p_state["view_name"]; + _update_name(); + } + if (p_state.has("auto_orthogonal")) { + auto_orthogonal = p_state["auto_orthogonal"]; + _update_name(); + } + if (p_state.has("auto_orthogonal_enabled")) { + bool enabled = p_state["auto_orthogonal_enabled"]; + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_AUTO_ORTHOGONAL), enabled); + } + if (p_state.has("display_mode")) { + int display = p_state["display_mode"]; + + int idx = view_menu->get_popup()->get_item_index(display); + if (!view_menu->get_popup()->is_item_checked(idx)) + _menu_option(display); + } + if (p_state.has("lock_rotation")) { + lock_rotation = p_state["lock_rotation"]; + + int idx = view_menu->get_popup()->get_item_index(VIEW_LOCK_ROTATION); + view_menu->get_popup()->set_item_checked(idx, lock_rotation); + } + if (p_state.has("use_environment")) { + bool env = p_state["use_environment"]; + + if (env != camera->get_environment().is_valid()) + _menu_option(VIEW_ENVIRONMENT); + } + if (p_state.has("listener")) { + bool listener = p_state["listener"]; + + int idx = view_menu->get_popup()->get_item_index(VIEW_AUDIO_LISTENER); + viewport->set_as_audio_listener(listener); + view_menu->get_popup()->set_item_checked(idx, listener); + } + if (p_state.has("doppler")) { + bool doppler = p_state["doppler"]; + + int idx = view_menu->get_popup()->get_item_index(VIEW_AUDIO_DOPPLER); + camera->set_doppler_tracking(doppler ? Camera3D::DOPPLER_TRACKING_IDLE_STEP : Camera3D::DOPPLER_TRACKING_DISABLED); + view_menu->get_popup()->set_item_checked(idx, doppler); + } + if (p_state.has("gizmos")) { + bool gizmos = p_state["gizmos"]; + + int idx = view_menu->get_popup()->get_item_index(VIEW_GIZMOS); + if (view_menu->get_popup()->is_item_checked(idx) != gizmos) + _menu_option(VIEW_GIZMOS); + } + if (p_state.has("information")) { + bool information = p_state["information"]; + + int idx = view_menu->get_popup()->get_item_index(VIEW_INFORMATION); + if (view_menu->get_popup()->is_item_checked(idx) != information) + _menu_option(VIEW_INFORMATION); + } + if (p_state.has("fps")) { + bool fps = p_state["fps"]; + + int idx = view_menu->get_popup()->get_item_index(VIEW_FPS); + if (view_menu->get_popup()->is_item_checked(idx) != fps) + _menu_option(VIEW_FPS); + } + if (p_state.has("half_res")) { + bool half_res = p_state["half_res"]; + + int idx = view_menu->get_popup()->get_item_index(VIEW_HALF_RESOLUTION); + view_menu->get_popup()->set_item_checked(idx, half_res); + } + if (p_state.has("cinematic_preview")) { + previewing_cinema = p_state["cinematic_preview"]; + + int idx = view_menu->get_popup()->get_item_index(VIEW_CINEMATIC_PREVIEW); + view_menu->get_popup()->set_item_checked(idx, previewing_cinema); + } + + if (preview_camera->is_connected("toggled", callable_mp(this, &Node3DEditorViewport::_toggle_camera_preview))) { + preview_camera->disconnect("toggled", callable_mp(this, &Node3DEditorViewport::_toggle_camera_preview)); + } + if (p_state.has("previewing")) { + Node *pv = EditorNode::get_singleton()->get_edited_scene()->get_node(p_state["previewing"]); + if (Object::cast_to(pv)) { + previewing = Object::cast_to(pv); + previewing->connect("tree_exiting", callable_mp(this, &Node3DEditorViewport::_preview_exited_scene)); + VS::get_singleton()->viewport_attach_camera(viewport->get_viewport_rid(), previewing->get_camera()); //replace + view_menu->set_disabled(true); + surface->update(); + preview_camera->set_pressed(true); + preview_camera->show(); + } + } + preview_camera->connect("toggled", callable_mp(this, &Node3DEditorViewport::_toggle_camera_preview)); +} + +Dictionary Node3DEditorViewport::get_state() const { + + Dictionary d; + d["position"] = cursor.pos; + d["x_rotation"] = cursor.x_rot; + d["y_rotation"] = cursor.y_rot; + d["distance"] = cursor.distance; + d["use_environment"] = camera->get_environment().is_valid(); + d["use_orthogonal"] = camera->get_projection() == Camera3D::PROJECTION_ORTHOGONAL; + d["view_name"] = name; + d["auto_orthogonal"] = auto_orthogonal; + d["auto_orthogonal_enabled"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_AUTO_ORTHOGONAL)); + if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_NORMAL))) + d["display_mode"] = VIEW_DISPLAY_NORMAL; + else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_WIREFRAME))) + d["display_mode"] = VIEW_DISPLAY_WIREFRAME; + else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_OVERDRAW))) + d["display_mode"] = VIEW_DISPLAY_OVERDRAW; + else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_SHADELESS))) + d["display_mode"] = VIEW_DISPLAY_SHADELESS; + d["listener"] = viewport->is_audio_listener(); + d["doppler"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_AUDIO_DOPPLER)); + d["gizmos"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_GIZMOS)); + d["information"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_INFORMATION)); + d["fps"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_FPS)); + d["half_res"] = viewport_container->get_stretch_shrink() > 1; + d["cinematic_preview"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_CINEMATIC_PREVIEW)); + if (previewing) + d["previewing"] = EditorNode::get_singleton()->get_edited_scene()->get_path_to(previewing); + if (lock_rotation) + d["lock_rotation"] = lock_rotation; + + return d; +} + +void Node3DEditorViewport::_bind_methods() { + + ClassDB::bind_method(D_METHOD("update_transform_gizmo_view"), &Node3DEditorViewport::update_transform_gizmo_view); // Used by call_deferred. + ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &Node3DEditorViewport::can_drop_data_fw); + ClassDB::bind_method(D_METHOD("drop_data_fw"), &Node3DEditorViewport::drop_data_fw); + + ADD_SIGNAL(MethodInfo("toggle_maximize_view", PropertyInfo(Variant::OBJECT, "viewport"))); + ADD_SIGNAL(MethodInfo("clicked", PropertyInfo(Variant::OBJECT, "viewport"))); +} + +void Node3DEditorViewport::reset() { + + orthogonal = false; + auto_orthogonal = false; + lock_rotation = false; + message_time = 0; + message = ""; + last_message = ""; + name = ""; + + cursor.x_rot = 0.5; + cursor.y_rot = 0.5; + cursor.distance = 4; + cursor.region_select = false; + cursor.pos = Vector3(); + _update_name(); +} + +void Node3DEditorViewport::focus_selection() { + if (!get_selected_count()) + return; + + Vector3 center; + int count = 0; + + List &selection = editor_selection->get_selected_node_list(); + + for (List::Element *E = selection.front(); E; E = E->next()) { + + Node3D *sp = Object::cast_to(E->get()); + if (!sp) + continue; + + Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data(sp); + if (!se) + continue; + + center += sp->get_global_gizmo_transform().origin; + count++; + } + + if (count != 0) { + center /= float(count); + } + + cursor.pos = center; +} + +void Node3DEditorViewport::assign_pending_data_pointers(Node3D *p_preview_node, AABB *p_preview_bounds, AcceptDialog *p_accept) { + preview_node = p_preview_node; + preview_bounds = p_preview_bounds; + accept = p_accept; +} + +Vector3 Node3DEditorViewport::_get_instance_position(const Point2 &p_pos) const { + const float MAX_DISTANCE = 10; + + Vector3 world_ray = _get_ray(p_pos); + Vector3 world_pos = _get_ray_pos(p_pos); + + Vector instances = VisualServer::get_singleton()->instances_cull_ray(world_pos, world_ray, get_tree()->get_root()->get_world()->get_scenario()); + Set> found_gizmos; + + float closest_dist = MAX_DISTANCE; + + Vector3 point = world_pos + world_ray * MAX_DISTANCE; + Vector3 normal = Vector3(0.0, 0.0, 0.0); + + for (int i = 0; i < instances.size(); i++) { + + MeshInstance3D *mesh_instance = Object::cast_to(ObjectDB::get_instance(instances[i])); + + if (!mesh_instance) + continue; + + Ref seg = mesh_instance->get_gizmo(); + + if ((!seg.is_valid()) || found_gizmos.has(seg)) { + continue; + } + + found_gizmos.insert(seg); + + Vector3 hit_point; + Vector3 hit_normal; + bool inters = seg->intersect_ray(camera, p_pos, hit_point, hit_normal, NULL, false); + + if (!inters) + continue; + + float dist = world_pos.distance_to(hit_point); + + if (dist < 0) + continue; + + if (dist < closest_dist) { + closest_dist = dist; + point = hit_point; + normal = hit_normal; + } + } + Vector3 offset = Vector3(); + for (int i = 0; i < 3; i++) { + if (normal[i] > 0.0) + offset[i] = (preview_bounds->get_size()[i] - (preview_bounds->get_size()[i] + preview_bounds->get_position()[i])); + else if (normal[i] < 0.0) + offset[i] = -(preview_bounds->get_size()[i] + preview_bounds->get_position()[i]); + } + return point + offset; +} + +AABB Node3DEditorViewport::_calculate_spatial_bounds(const Node3D *p_parent, bool p_exclude_toplevel_transform) { + AABB bounds; + + const MeshInstance3D *mesh_instance = Object::cast_to(p_parent); + if (mesh_instance) { + bounds = mesh_instance->get_aabb(); + } + + for (int i = 0; i < p_parent->get_child_count(); i++) { + Node3D *child = Object::cast_to(p_parent->get_child(i)); + if (child) { + AABB child_bounds = _calculate_spatial_bounds(child, false); + + if (bounds.size == Vector3() && p_parent->get_class_name() == StringName("Node3D")) { + bounds = child_bounds; + } else { + bounds.merge_with(child_bounds); + } + } + } + + if (bounds.size == Vector3() && p_parent->get_class_name() != StringName("Node3D")) { + bounds = AABB(Vector3(-0.2, -0.2, -0.2), Vector3(0.4, 0.4, 0.4)); + } + + if (!p_exclude_toplevel_transform) { + bounds = p_parent->get_transform().xform(bounds); + } + + return bounds; +} + +void Node3DEditorViewport::_create_preview(const Vector &files) const { + for (int i = 0; i < files.size(); i++) { + String path = files[i]; + RES res = ResourceLoader::load(path); + ERR_CONTINUE(res.is_null()); + Ref scene = Ref(Object::cast_to(*res)); + Ref mesh = Ref(Object::cast_to(*res)); + if (mesh != NULL || scene != NULL) { + if (mesh != NULL) { + MeshInstance3D *mesh_instance = memnew(MeshInstance3D); + mesh_instance->set_mesh(mesh); + preview_node->add_child(mesh_instance); + } else { + if (scene.is_valid()) { + Node *instance = scene->instance(); + if (instance) { + preview_node->add_child(instance); + } + } + } + editor->get_scene_root()->add_child(preview_node); + } + } + *preview_bounds = _calculate_spatial_bounds(preview_node); +} + +void Node3DEditorViewport::_remove_preview() { + if (preview_node->get_parent()) { + for (int i = preview_node->get_child_count() - 1; i >= 0; i--) { + Node *node = preview_node->get_child(i); + node->queue_delete(); + preview_node->remove_child(node); + } + editor->get_scene_root()->remove_child(preview_node); + } +} + +bool Node3DEditorViewport::_cyclical_dependency_exists(const String &p_target_scene_path, Node *p_desired_node) { + if (p_desired_node->get_filename() == p_target_scene_path) { + return true; + } + + int childCount = p_desired_node->get_child_count(); + for (int i = 0; i < childCount; i++) { + Node *child = p_desired_node->get_child(i); + if (_cyclical_dependency_exists(p_target_scene_path, child)) { + return true; + } + } + return false; +} + +bool Node3DEditorViewport::_create_instance(Node *parent, String &path, const Point2 &p_point) { + RES res = ResourceLoader::load(path); + ERR_FAIL_COND_V(res.is_null(), false); + + Ref scene = Ref(Object::cast_to(*res)); + Ref mesh = Ref(Object::cast_to(*res)); + + Node *instanced_scene = NULL; + + if (mesh != NULL || scene != NULL) { + if (mesh != NULL) { + MeshInstance3D *mesh_instance = memnew(MeshInstance3D); + mesh_instance->set_mesh(mesh); + mesh_instance->set_name(path.get_file().get_basename()); + instanced_scene = mesh_instance; + } else { + if (!scene.is_valid()) { // invalid scene + return false; + } else { + instanced_scene = scene->instance(PackedScene::GEN_EDIT_STATE_INSTANCE); + } + } + } + + if (instanced_scene == NULL) { + return false; + } + + if (editor->get_edited_scene()->get_filename() != "") { // cyclical instancing + if (_cyclical_dependency_exists(editor->get_edited_scene()->get_filename(), instanced_scene)) { + memdelete(instanced_scene); + return false; + } + } + + if (scene != NULL) { + instanced_scene->set_filename(ProjectSettings::get_singleton()->localize_path(path)); + } + + editor_data->get_undo_redo().add_do_method(parent, "add_child", instanced_scene); + editor_data->get_undo_redo().add_do_method(instanced_scene, "set_owner", editor->get_edited_scene()); + editor_data->get_undo_redo().add_do_reference(instanced_scene); + editor_data->get_undo_redo().add_undo_method(parent, "remove_child", instanced_scene); + + String new_name = parent->validate_child_name(instanced_scene); + EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton(); + editor_data->get_undo_redo().add_do_method(ed, "live_debug_instance_node", editor->get_edited_scene()->get_path_to(parent), path, new_name); + editor_data->get_undo_redo().add_undo_method(ed, "live_debug_remove_node", NodePath(String(editor->get_edited_scene()->get_path_to(parent)) + "/" + new_name)); + + Transform global_transform; + Node3D *parent_spatial = Object::cast_to(parent); + if (parent_spatial) + global_transform = parent_spatial->get_global_gizmo_transform(); + + global_transform.origin = spatial_editor->snap_point(_get_instance_position(p_point)); + + editor_data->get_undo_redo().add_do_method(instanced_scene, "set_global_transform", global_transform); + + return true; +} + +void Node3DEditorViewport::_perform_drop_data() { + _remove_preview(); + + Vector error_files; + + editor_data->get_undo_redo().create_action(TTR("Create Node")); + + for (int i = 0; i < selected_files.size(); i++) { + String path = selected_files[i]; + RES res = ResourceLoader::load(path); + if (res.is_null()) { + continue; + } + Ref scene = Ref(Object::cast_to(*res)); + Ref mesh = Ref(Object::cast_to(*res)); + if (mesh != NULL || scene != NULL) { + bool success = _create_instance(target_node, path, drop_pos); + if (!success) { + error_files.push_back(path); + } + } + } + + editor_data->get_undo_redo().commit_action(); + + if (error_files.size() > 0) { + String files_str; + for (int i = 0; i < error_files.size(); i++) { + files_str += error_files[i].get_file().get_basename() + ","; + } + files_str = files_str.substr(0, files_str.length() - 1); + accept->set_text(vformat(TTR("Error instancing scene from %s"), files_str.c_str())); + accept->popup_centered(); + } +} + +bool Node3DEditorViewport::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const { + + bool can_instance = false; + + if (!preview_node->is_inside_tree()) { + Dictionary d = p_data; + if (d.has("type") && (String(d["type"]) == "files")) { + Vector files = d["files"]; + + List scene_extensions; + ResourceLoader::get_recognized_extensions_for_type("PackedScene", &scene_extensions); + List mesh_extensions; + ResourceLoader::get_recognized_extensions_for_type("Mesh", &mesh_extensions); + + for (int i = 0; i < files.size(); i++) { + if (mesh_extensions.find(files[i].get_extension()) || scene_extensions.find(files[i].get_extension())) { + RES res = ResourceLoader::load(files[i]); + if (res.is_null()) { + continue; + } + + String type = res->get_class(); + if (type == "PackedScene") { + Ref sdata = ResourceLoader::load(files[i]); + Node *instanced_scene = sdata->instance(PackedScene::GEN_EDIT_STATE_INSTANCE); + if (!instanced_scene) { + continue; + } + memdelete(instanced_scene); + } else if (type == "Mesh" || type == "ArrayMesh" || type == "PrimitiveMesh") { + Ref mesh = ResourceLoader::load(files[i]); + if (!mesh.is_valid()) { + continue; + } + } else { + continue; + } + can_instance = true; + break; + } + } + if (can_instance) { + _create_preview(files); + } + } + } else { + can_instance = true; + } + + if (can_instance) { + Transform global_transform = Transform(Basis(), _get_instance_position(p_point)); + preview_node->set_global_transform(global_transform); + } + + return can_instance; +} + +void Node3DEditorViewport::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) { + if (!can_drop_data_fw(p_point, p_data, p_from)) + return; + + bool is_shift = InputFilter::get_singleton()->is_key_pressed(KEY_SHIFT); + + selected_files.clear(); + Dictionary d = p_data; + if (d.has("type") && String(d["type"]) == "files") { + selected_files = d["files"]; + } + + List list = editor->get_editor_selection()->get_selected_node_list(); + if (list.size() == 0) { + Node *root_node = editor->get_edited_scene(); + if (root_node) { + list.push_back(root_node); + } else { + accept->set_text(TTR("No parent to instance a child at.")); + accept->popup_centered(); + _remove_preview(); + return; + } + } + if (list.size() != 1) { + accept->set_text(TTR("This operation requires a single selected node.")); + accept->popup_centered(); + _remove_preview(); + return; + } + + target_node = list[0]; + if (is_shift && target_node != editor->get_edited_scene()) { + target_node = target_node->get_parent(); + } + drop_pos = p_point; + + _perform_drop_data(); +} + +Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, EditorNode *p_editor, int p_index) { + + _edit.mode = TRANSFORM_NONE; + _edit.plane = TRANSFORM_VIEW; + _edit.edited_gizmo = 0; + _edit.snap = 1; + _edit.gizmo_handle = 0; + + index = p_index; + editor = p_editor; + editor_data = editor->get_scene_tree_dock()->get_editor_data(); + editor_selection = editor->get_editor_selection(); + undo_redo = editor->get_undo_redo(); + + clicked_includes_current = false; + orthogonal = false; + auto_orthogonal = false; + lock_rotation = false; + message_time = 0; + zoom_indicator_delay = 0.0; + + spatial_editor = p_spatial_editor; + ViewportContainer *c = memnew(ViewportContainer); + viewport_container = c; + c->set_stretch(true); + add_child(c); + c->set_anchors_and_margins_preset(Control::PRESET_WIDE); + viewport = memnew(SubViewport); + viewport->set_disable_input(true); + + c->add_child(viewport); + surface = memnew(Control); + surface->set_drag_forwarding(this); + add_child(surface); + surface->set_anchors_and_margins_preset(Control::PRESET_WIDE); + surface->set_clip_contents(true); + camera = memnew(Camera3D); + camera->set_disable_gizmo(true); + camera->set_cull_mask(((1 << 20) - 1) | (1 << (GIZMO_BASE_LAYER + p_index)) | (1 << GIZMO_EDIT_LAYER) | (1 << GIZMO_GRID_LAYER)); + viewport->add_child(camera); + camera->make_current(); + surface->set_focus_mode(FOCUS_ALL); + + crosshair = memnew(TextureRect); + crosshair->set_mouse_filter(MOUSE_FILTER_IGNORE); + surface->add_child(crosshair); + + VBoxContainer *vbox = memnew(VBoxContainer); + surface->add_child(vbox); + vbox->set_position(Point2(10, 10) * EDSCALE); + + view_menu = memnew(MenuButton); + view_menu->set_flat(false); + vbox->add_child(view_menu); + view_menu->set_h_size_flags(0); + + display_submenu = memnew(PopupMenu); + view_menu->get_popup()->add_child(display_submenu); + + view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/top_view"), VIEW_TOP); + view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/bottom_view"), VIEW_BOTTOM); + view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/left_view"), VIEW_LEFT); + view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/right_view"), VIEW_RIGHT); + view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/front_view"), VIEW_FRONT); + view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/rear_view"), VIEW_REAR); + view_menu->get_popup()->add_separator(); + view_menu->get_popup()->add_radio_check_item(TTR("Perspective") + " (" + ED_GET_SHORTCUT("spatial_editor/switch_perspective_orthogonal")->get_as_text() + ")", VIEW_PERSPECTIVE); + view_menu->get_popup()->add_radio_check_item(TTR("Orthogonal") + " (" + ED_GET_SHORTCUT("spatial_editor/switch_perspective_orthogonal")->get_as_text() + ")", VIEW_ORTHOGONAL); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_PERSPECTIVE), true); + view_menu->get_popup()->add_check_item(TTR("Auto Orthogonal Enabled"), VIEW_AUTO_ORTHOGONAL); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_AUTO_ORTHOGONAL), true); + view_menu->get_popup()->add_separator(); + view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_lock_rotation", TTR("Lock View Rotation")), VIEW_LOCK_ROTATION); + view_menu->get_popup()->add_separator(); + view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_normal", TTR("Display Normal")), VIEW_DISPLAY_NORMAL); + view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_wireframe", TTR("Display Wireframe")), VIEW_DISPLAY_WIREFRAME); + view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_overdraw", TTR("Display Overdraw")), VIEW_DISPLAY_OVERDRAW); + view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_lighting", TTR("Display Lighting")), VIEW_DISPLAY_LIGHTING); + view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_unshaded", TTR("Display Unshaded")), VIEW_DISPLAY_SHADELESS); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_NORMAL), true); + display_submenu->add_radio_check_item(TTR("Normal Buffer"), VIEW_DISPLAY_NORMAL_BUFFER); + display_submenu->add_separator(); + display_submenu->add_radio_check_item(TTR("Shadow Atlas"), VIEW_DISPLAY_DEBUG_SHADOW_ATLAS); + display_submenu->add_radio_check_item(TTR("Directional Shadow"), VIEW_DISPLAY_DEBUG_DIRECTIONAL_SHADOW_ATLAS); + display_submenu->add_separator(); + display_submenu->add_radio_check_item(TTR("GIProbe Lighting"), VIEW_DISPLAY_DEBUG_GIPROBE_LIGHTING); + display_submenu->add_radio_check_item(TTR("GIProbe Albedo"), VIEW_DISPLAY_DEBUG_GIPROBE_ALBEDO); + display_submenu->add_radio_check_item(TTR("GIProbe Emission"), VIEW_DISPLAY_DEBUG_GIPROBE_EMISSION); + display_submenu->add_separator(); + display_submenu->add_radio_check_item(TTR("Scene Luminance"), VIEW_DISPLAY_DEBUG_SCENE_LUMINANCE); + display_submenu->add_separator(); + display_submenu->add_radio_check_item(TTR("SSAO"), VIEW_DISPLAY_DEBUG_SSAO); + display_submenu->add_separator(); + display_submenu->add_radio_check_item(TTR("Roughness Limiter"), VIEW_DISPLAY_DEBUG_ROUGHNESS_LIMITER); + display_submenu->set_name("display_advanced"); + view_menu->get_popup()->add_submenu_item(TTR("Display Advanced..."), "display_advanced", VIEW_DISPLAY_ADVANCED); + view_menu->get_popup()->add_separator(); + view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_environment", TTR("View Environment")), VIEW_ENVIRONMENT); + view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_gizmos", TTR("View Gizmos")), VIEW_GIZMOS); + view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_information", TTR("View Information")), VIEW_INFORMATION); + view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_fps", TTR("View FPS")), VIEW_FPS); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_ENVIRONMENT), true); + view_menu->get_popup()->add_separator(); + view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_half_resolution", TTR("Half Resolution")), VIEW_HALF_RESOLUTION); + view_menu->get_popup()->add_separator(); + view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_audio_listener", TTR("Audio Listener")), VIEW_AUDIO_LISTENER); + view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_audio_doppler", TTR("Enable Doppler")), VIEW_AUDIO_DOPPLER); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_GIZMOS), true); + + view_menu->get_popup()->add_separator(); + view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_cinematic_preview", TTR("Cinematic Preview")), VIEW_CINEMATIC_PREVIEW); + + view_menu->get_popup()->add_separator(); + view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/focus_origin"), VIEW_CENTER_TO_ORIGIN); + view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/focus_selection"), VIEW_CENTER_TO_SELECTION); + view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/align_transform_with_view"), VIEW_ALIGN_TRANSFORM_WITH_VIEW); + view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/align_rotation_with_view"), VIEW_ALIGN_ROTATION_WITH_VIEW); + view_menu->get_popup()->connect("id_pressed", callable_mp(this, &Node3DEditorViewport::_menu_option)); + display_submenu->connect("id_pressed", callable_mp(this, &Node3DEditorViewport::_menu_option)); + view_menu->set_disable_shortcuts(true); +#ifndef _MSC_VER +#warning this needs to be fixed +#endif + //if (OS::get_singleton()->get_current_video_driver() == OS::VIDEO_DRIVER_GLES2) { + if (false) { + // Alternate display modes only work when using the Vulkan renderer; make this explicit. + const int normal_idx = view_menu->get_popup()->get_item_index(VIEW_DISPLAY_NORMAL); + const int wireframe_idx = view_menu->get_popup()->get_item_index(VIEW_DISPLAY_WIREFRAME); + const int overdraw_idx = view_menu->get_popup()->get_item_index(VIEW_DISPLAY_OVERDRAW); + const int shadeless_idx = view_menu->get_popup()->get_item_index(VIEW_DISPLAY_SHADELESS); + const String unsupported_tooltip = TTR("Not available when using the GLES2 renderer."); + + view_menu->get_popup()->set_item_disabled(normal_idx, true); + view_menu->get_popup()->set_item_tooltip(normal_idx, unsupported_tooltip); + view_menu->get_popup()->set_item_disabled(wireframe_idx, true); + view_menu->get_popup()->set_item_tooltip(wireframe_idx, unsupported_tooltip); + view_menu->get_popup()->set_item_disabled(overdraw_idx, true); + view_menu->get_popup()->set_item_tooltip(overdraw_idx, unsupported_tooltip); + view_menu->get_popup()->set_item_disabled(shadeless_idx, true); + view_menu->get_popup()->set_item_tooltip(shadeless_idx, unsupported_tooltip); + } + + ED_SHORTCUT("spatial_editor/freelook_left", TTR("Freelook Left"), KEY_A); + ED_SHORTCUT("spatial_editor/freelook_right", TTR("Freelook Right"), KEY_D); + ED_SHORTCUT("spatial_editor/freelook_forward", TTR("Freelook Forward"), KEY_W); + ED_SHORTCUT("spatial_editor/freelook_backwards", TTR("Freelook Backwards"), KEY_S); + ED_SHORTCUT("spatial_editor/freelook_up", TTR("Freelook Up"), KEY_E); + ED_SHORTCUT("spatial_editor/freelook_down", TTR("Freelook Down"), KEY_Q); + ED_SHORTCUT("spatial_editor/freelook_speed_modifier", TTR("Freelook Speed Modifier"), KEY_SHIFT); + ED_SHORTCUT("spatial_editor/freelook_slow_modifier", TTR("Freelook Slow Modifier"), KEY_ALT); + + preview_camera = memnew(CheckBox); + preview_camera->set_text(TTR("Preview")); + vbox->add_child(preview_camera); + preview_camera->set_h_size_flags(0); + preview_camera->hide(); + preview_camera->connect("toggled", callable_mp(this, &Node3DEditorViewport::_toggle_camera_preview)); + previewing = NULL; + gizmo_scale = 1.0; + + preview_node = NULL; + + info_label = memnew(Label); + info_label->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_END, -90 * EDSCALE); + info_label->set_anchor_and_margin(MARGIN_TOP, ANCHOR_END, -90 * EDSCALE); + info_label->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, -10 * EDSCALE); + info_label->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, -10 * EDSCALE); + info_label->set_h_grow_direction(GROW_DIRECTION_BEGIN); + info_label->set_v_grow_direction(GROW_DIRECTION_BEGIN); + surface->add_child(info_label); + info_label->hide(); + + fps_label = memnew(Label); + fps_label->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_END, -90 * EDSCALE); + fps_label->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 10 * EDSCALE); + fps_label->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, -10 * EDSCALE); + fps_label->set_h_grow_direction(GROW_DIRECTION_BEGIN); + fps_label->set_tooltip(TTR("Note: The FPS value displayed is the editor's framerate.\nIt cannot be used as a reliable indication of in-game performance.")); + fps_label->set_mouse_filter(MOUSE_FILTER_PASS); // Otherwise tooltip doesn't show. + surface->add_child(fps_label); + fps_label->hide(); + + cinema_label = memnew(Label); + cinema_label->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 10 * EDSCALE); + cinema_label->set_h_grow_direction(GROW_DIRECTION_END); + cinema_label->set_align(Label::ALIGN_CENTER); + surface->add_child(cinema_label); + cinema_label->set_text(TTR("Cinematic Preview")); + cinema_label->hide(); + previewing_cinema = false; + + locked_label = memnew(Label); + locked_label->set_anchor_and_margin(MARGIN_TOP, ANCHOR_END, -20 * EDSCALE); + locked_label->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, -10 * EDSCALE); + locked_label->set_h_grow_direction(GROW_DIRECTION_END); + locked_label->set_v_grow_direction(GROW_DIRECTION_BEGIN); + locked_label->set_align(Label::ALIGN_CENTER); + surface->add_child(locked_label); + locked_label->set_text(TTR("View Rotation Locked")); + locked_label->hide(); + + top_right_vbox = memnew(VBoxContainer); + top_right_vbox->set_anchors_and_margins_preset(PRESET_TOP_RIGHT, PRESET_MODE_MINSIZE, 2.0 * EDSCALE); + top_right_vbox->set_h_grow_direction(GROW_DIRECTION_BEGIN); + + rotation_control = memnew(ViewportRotationControl); + rotation_control->set_custom_minimum_size(Size2(80, 80) * EDSCALE); + rotation_control->set_h_size_flags(SIZE_SHRINK_END); + rotation_control->set_viewport(this); + top_right_vbox->add_child(rotation_control); + + fps_label = memnew(Label); + fps_label->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_END, -90 * EDSCALE); + fps_label->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 10 * EDSCALE); + fps_label->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, -10 * EDSCALE); + fps_label->set_h_grow_direction(GROW_DIRECTION_BEGIN); + fps_label->set_tooltip(TTR("Note: The FPS value displayed is the editor's framerate.\nIt cannot be used as a reliable indication of in-game performance.")); + fps_label->set_mouse_filter(MOUSE_FILTER_PASS); // Otherwise tooltip doesn't show. + top_right_vbox->add_child(fps_label); + fps_label->hide(); + + surface->add_child(top_right_vbox); + + accept = NULL; + + freelook_active = false; + freelook_speed = EditorSettings::get_singleton()->get("editors/3d/freelook/freelook_base_speed"); + + selection_menu = memnew(PopupMenu); + add_child(selection_menu); + selection_menu->set_min_size(Size2(100, 0) * EDSCALE); + selection_menu->connect("id_pressed", callable_mp(this, &Node3DEditorViewport::_selection_result_pressed)); + selection_menu->connect("popup_hide", callable_mp(this, &Node3DEditorViewport::_selection_menu_hide)); + + if (p_index == 0) { + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_AUDIO_LISTENER), true); + viewport->set_as_audio_listener(true); + } + + name = ""; + _update_name(); + + EditorSettings::get_singleton()->connect("settings_changed", callable_mp(this, &Node3DEditorViewport::update_transform_gizmo_view)); +} + +////////////////////////////////////////////////////////////// + +void Node3DEditorViewportContainer::_gui_input(const Ref &p_event) { + + Ref mb = p_event; + + if (mb.is_valid() && mb->get_button_index() == BUTTON_LEFT) { + + if (mb->is_pressed()) { + Vector2 size = get_size(); + + int h_sep = get_theme_constant("separation", "HSplitContainer"); + int v_sep = get_theme_constant("separation", "VSplitContainer"); + + int mid_w = size.width * ratio_h; + int mid_h = size.height * ratio_v; + + dragging_h = mb->get_position().x > (mid_w - h_sep / 2) && mb->get_position().x < (mid_w + h_sep / 2); + dragging_v = mb->get_position().y > (mid_h - v_sep / 2) && mb->get_position().y < (mid_h + v_sep / 2); + + drag_begin_pos = mb->get_position(); + drag_begin_ratio.x = ratio_h; + drag_begin_ratio.y = ratio_v; + + switch (view) { + case VIEW_USE_1_VIEWPORT: { + + dragging_h = false; + dragging_v = false; + + } break; + case VIEW_USE_2_VIEWPORTS: { + + dragging_h = false; + + } break; + case VIEW_USE_2_VIEWPORTS_ALT: { + + dragging_v = false; + + } break; + case VIEW_USE_3_VIEWPORTS: + case VIEW_USE_3_VIEWPORTS_ALT: + case VIEW_USE_4_VIEWPORTS: { + + // Do nothing. + + } break; + } + } else { + dragging_h = false; + dragging_v = false; + } + } + + Ref mm = p_event; + + if (mm.is_valid()) { + + if (view == VIEW_USE_3_VIEWPORTS || view == VIEW_USE_3_VIEWPORTS_ALT || view == VIEW_USE_4_VIEWPORTS) { + Vector2 size = get_size(); + + int h_sep = get_theme_constant("separation", "HSplitContainer"); + int v_sep = get_theme_constant("separation", "VSplitContainer"); + + int mid_w = size.width * ratio_h; + int mid_h = size.height * ratio_v; + + bool was_hovering_h = hovering_h; + bool was_hovering_v = hovering_v; + hovering_h = mm->get_position().x > (mid_w - h_sep / 2) && mm->get_position().x < (mid_w + h_sep / 2); + hovering_v = mm->get_position().y > (mid_h - v_sep / 2) && mm->get_position().y < (mid_h + v_sep / 2); + + if (was_hovering_h != hovering_h || was_hovering_v != hovering_v) { + update(); + } + } + + if (dragging_h) { + float new_ratio = drag_begin_ratio.x + (mm->get_position().x - drag_begin_pos.x) / get_size().width; + new_ratio = CLAMP(new_ratio, 40 / get_size().width, (get_size().width - 40) / get_size().width); + ratio_h = new_ratio; + queue_sort(); + update(); + } + if (dragging_v) { + float new_ratio = drag_begin_ratio.y + (mm->get_position().y - drag_begin_pos.y) / get_size().height; + new_ratio = CLAMP(new_ratio, 40 / get_size().height, (get_size().height - 40) / get_size().height); + ratio_v = new_ratio; + queue_sort(); + update(); + } + } +} + +void Node3DEditorViewportContainer::_notification(int p_what) { + + if (p_what == NOTIFICATION_MOUSE_ENTER || p_what == NOTIFICATION_MOUSE_EXIT) { + + mouseover = (p_what == NOTIFICATION_MOUSE_ENTER); + update(); + } + + if (p_what == NOTIFICATION_DRAW && mouseover) { + + Ref h_grabber = get_theme_icon("grabber", "HSplitContainer"); + Ref v_grabber = get_theme_icon("grabber", "VSplitContainer"); + + Ref hdiag_grabber = get_theme_icon("GuiViewportHdiagsplitter", "EditorIcons"); + Ref vdiag_grabber = get_theme_icon("GuiViewportVdiagsplitter", "EditorIcons"); + Ref vh_grabber = get_theme_icon("GuiViewportVhsplitter", "EditorIcons"); + + Vector2 size = get_size(); + + int h_sep = get_theme_constant("separation", "HSplitContainer"); + + int v_sep = get_theme_constant("separation", "VSplitContainer"); + + int mid_w = size.width * ratio_h; + int mid_h = size.height * ratio_v; + + int size_left = mid_w - h_sep / 2; + int size_bottom = size.height - mid_h - v_sep / 2; + + switch (view) { + + case VIEW_USE_1_VIEWPORT: { + + // Nothing to show. + + } break; + case VIEW_USE_2_VIEWPORTS: { + + draw_texture(v_grabber, Vector2((size.width - v_grabber->get_width()) / 2, mid_h - v_grabber->get_height() / 2)); + set_default_cursor_shape(CURSOR_VSPLIT); + + } break; + case VIEW_USE_2_VIEWPORTS_ALT: { + + draw_texture(h_grabber, Vector2(mid_w - h_grabber->get_width() / 2, (size.height - h_grabber->get_height()) / 2)); + set_default_cursor_shape(CURSOR_HSPLIT); + + } break; + case VIEW_USE_3_VIEWPORTS: { + + if ((hovering_v && hovering_h && !dragging_v && !dragging_h) || (dragging_v && dragging_h)) { + draw_texture(hdiag_grabber, Vector2(mid_w - hdiag_grabber->get_width() / 2, mid_h - v_grabber->get_height() / 4)); + set_default_cursor_shape(CURSOR_DRAG); + } else if ((hovering_v && !dragging_h) || dragging_v) { + draw_texture(v_grabber, Vector2((size.width - v_grabber->get_width()) / 2, mid_h - v_grabber->get_height() / 2)); + set_default_cursor_shape(CURSOR_VSPLIT); + } else if (hovering_h || dragging_h) { + draw_texture(h_grabber, Vector2(mid_w - h_grabber->get_width() / 2, mid_h + v_grabber->get_height() / 2 + (size_bottom - h_grabber->get_height()) / 2)); + set_default_cursor_shape(CURSOR_HSPLIT); + } + + } break; + case VIEW_USE_3_VIEWPORTS_ALT: { + + if ((hovering_v && hovering_h && !dragging_v && !dragging_h) || (dragging_v && dragging_h)) { + draw_texture(vdiag_grabber, Vector2(mid_w - vdiag_grabber->get_width() + v_grabber->get_height() / 4, mid_h - vdiag_grabber->get_height() / 2)); + set_default_cursor_shape(CURSOR_DRAG); + } else if ((hovering_v && !dragging_h) || dragging_v) { + draw_texture(v_grabber, Vector2((size_left - v_grabber->get_width()) / 2, mid_h - v_grabber->get_height() / 2)); + set_default_cursor_shape(CURSOR_VSPLIT); + } else if (hovering_h || dragging_h) { + draw_texture(h_grabber, Vector2(mid_w - h_grabber->get_width() / 2, (size.height - h_grabber->get_height()) / 2)); + set_default_cursor_shape(CURSOR_HSPLIT); + } + + } break; + case VIEW_USE_4_VIEWPORTS: { + + Vector2 half(mid_w, mid_h); + if ((hovering_v && hovering_h && !dragging_v && !dragging_h) || (dragging_v && dragging_h)) { + draw_texture(vh_grabber, half - vh_grabber->get_size() / 2.0); + set_default_cursor_shape(CURSOR_DRAG); + } else if ((hovering_v && !dragging_h) || dragging_v) { + draw_texture(v_grabber, half - v_grabber->get_size() / 2.0); + set_default_cursor_shape(CURSOR_VSPLIT); + } else if (hovering_h || dragging_h) { + draw_texture(h_grabber, half - h_grabber->get_size() / 2.0); + set_default_cursor_shape(CURSOR_HSPLIT); + } + + } break; + } + } + + if (p_what == NOTIFICATION_SORT_CHILDREN) { + + Node3DEditorViewport *viewports[4]; + int vc = 0; + for (int i = 0; i < get_child_count(); i++) { + viewports[vc] = Object::cast_to(get_child(i)); + if (viewports[vc]) { + vc++; + } + } + + ERR_FAIL_COND(vc != 4); + + Size2 size = get_size(); + + if (size.x < 10 || size.y < 10) { + for (int i = 0; i < 4; i++) { + viewports[i]->hide(); + } + return; + } + int h_sep = get_theme_constant("separation", "HSplitContainer"); + + int v_sep = get_theme_constant("separation", "VSplitContainer"); + + int mid_w = size.width * ratio_h; + int mid_h = size.height * ratio_v; + + int size_left = mid_w - h_sep / 2; + int size_right = size.width - mid_w - h_sep / 2; + + int size_top = mid_h - v_sep / 2; + int size_bottom = size.height - mid_h - v_sep / 2; + + switch (view) { + + case VIEW_USE_1_VIEWPORT: { + + viewports[0]->show(); + for (int i = 1; i < 4; i++) { + + viewports[i]->hide(); + } + + fit_child_in_rect(viewports[0], Rect2(Vector2(), size)); + + } break; + case VIEW_USE_2_VIEWPORTS: { + + for (int i = 0; i < 4; i++) { + + if (i == 1 || i == 3) + viewports[i]->hide(); + else + viewports[i]->show(); + } + + fit_child_in_rect(viewports[0], Rect2(Vector2(), Vector2(size.width, size_top))); + fit_child_in_rect(viewports[2], Rect2(Vector2(0, mid_h + v_sep / 2), Vector2(size.width, size_bottom))); + + } break; + case VIEW_USE_2_VIEWPORTS_ALT: { + + for (int i = 0; i < 4; i++) { + + if (i == 1 || i == 3) + viewports[i]->hide(); + else + viewports[i]->show(); + } + fit_child_in_rect(viewports[0], Rect2(Vector2(), Vector2(size_left, size.height))); + fit_child_in_rect(viewports[2], Rect2(Vector2(mid_w + h_sep / 2, 0), Vector2(size_right, size.height))); + + } break; + case VIEW_USE_3_VIEWPORTS: { + + for (int i = 0; i < 4; i++) { + + if (i == 1) + viewports[i]->hide(); + else + viewports[i]->show(); + } + + fit_child_in_rect(viewports[0], Rect2(Vector2(), Vector2(size.width, size_top))); + fit_child_in_rect(viewports[2], Rect2(Vector2(0, mid_h + v_sep / 2), Vector2(size_left, size_bottom))); + fit_child_in_rect(viewports[3], Rect2(Vector2(mid_w + h_sep / 2, mid_h + v_sep / 2), Vector2(size_right, size_bottom))); + + } break; + case VIEW_USE_3_VIEWPORTS_ALT: { + + for (int i = 0; i < 4; i++) { + + if (i == 1) + viewports[i]->hide(); + else + viewports[i]->show(); + } + + fit_child_in_rect(viewports[0], Rect2(Vector2(), Vector2(size_left, size_top))); + fit_child_in_rect(viewports[2], Rect2(Vector2(0, mid_h + v_sep / 2), Vector2(size_left, size_bottom))); + fit_child_in_rect(viewports[3], Rect2(Vector2(mid_w + h_sep / 2, 0), Vector2(size_right, size.height))); + + } break; + case VIEW_USE_4_VIEWPORTS: { + + for (int i = 0; i < 4; i++) { + + viewports[i]->show(); + } + + fit_child_in_rect(viewports[0], Rect2(Vector2(), Vector2(size_left, size_top))); + fit_child_in_rect(viewports[1], Rect2(Vector2(mid_w + h_sep / 2, 0), Vector2(size_right, size_top))); + fit_child_in_rect(viewports[2], Rect2(Vector2(0, mid_h + v_sep / 2), Vector2(size_left, size_bottom))); + fit_child_in_rect(viewports[3], Rect2(Vector2(mid_w + h_sep / 2, mid_h + v_sep / 2), Vector2(size_right, size_bottom))); + + } break; + } + } +} + +void Node3DEditorViewportContainer::set_view(View p_view) { + + view = p_view; + queue_sort(); +} + +Node3DEditorViewportContainer::View Node3DEditorViewportContainer::get_view() { + + return view; +} + +void Node3DEditorViewportContainer::_bind_methods() { + + ClassDB::bind_method("_gui_input", &Node3DEditorViewportContainer::_gui_input); +} + +Node3DEditorViewportContainer::Node3DEditorViewportContainer() { + + set_clip_contents(true); + view = VIEW_USE_1_VIEWPORT; + mouseover = false; + ratio_h = 0.5; + ratio_v = 0.5; + hovering_v = false; + hovering_h = false; + dragging_v = false; + dragging_h = false; +} + +/////////////////////////////////////////////////////////////////// + +Node3DEditor *Node3DEditor::singleton = NULL; + +Node3DEditorSelectedItem::~Node3DEditorSelectedItem() { + + if (sbox_instance.is_valid()) + VisualServer::get_singleton()->free(sbox_instance); +} + +void Node3DEditor::select_gizmo_highlight_axis(int p_axis) { + + for (int i = 0; i < 3; i++) { + + move_gizmo[i]->surface_set_material(0, i == p_axis ? gizmo_color_hl[i] : gizmo_color[i]); + move_plane_gizmo[i]->surface_set_material(0, (i + 6) == p_axis ? plane_gizmo_color_hl[i] : plane_gizmo_color[i]); + rotate_gizmo[i]->surface_set_material(0, (i + 3) == p_axis ? gizmo_color_hl[i] : gizmo_color[i]); + scale_gizmo[i]->surface_set_material(0, (i + 9) == p_axis ? gizmo_color_hl[i] : gizmo_color[i]); + scale_plane_gizmo[i]->surface_set_material(0, (i + 12) == p_axis ? plane_gizmo_color_hl[i] : plane_gizmo_color[i]); + } +} + +void Node3DEditor::update_transform_gizmo() { + + List &selection = editor_selection->get_selected_node_list(); + AABB center; + bool first = true; + + Basis gizmo_basis; + bool local_gizmo_coords = are_local_coords_enabled(); + + for (List::Element *E = selection.front(); E; E = E->next()) { + + Node3D *sp = Object::cast_to(E->get()); + if (!sp) + continue; + + Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data(sp); + if (!se) + continue; + + Transform xf = se->sp->get_global_gizmo_transform(); + + if (first) { + center.position = xf.origin; + first = false; + if (local_gizmo_coords) { + gizmo_basis = xf.basis; + gizmo_basis.orthonormalize(); + } + } else { + center.expand_to(xf.origin); + gizmo_basis = Basis(); + } + } + + Vector3 pcenter = center.position + center.size * 0.5; + gizmo.visible = !first; + gizmo.transform.origin = pcenter; + gizmo.transform.basis = gizmo_basis; + + for (uint32_t i = 0; i < VIEWPORTS_COUNT; i++) { + viewports[i]->update_transform_gizmo_view(); + } +} + +void _update_all_gizmos(Node *p_node) { + for (int i = p_node->get_child_count() - 1; 0 <= i; --i) { + Node3D *spatial_node = Object::cast_to(p_node->get_child(i)); + if (spatial_node) { + spatial_node->update_gizmo(); + } + + _update_all_gizmos(p_node->get_child(i)); + } +} + +void Node3DEditor::update_all_gizmos(Node *p_node) { + if (!p_node) { + p_node = SceneTree::get_singleton()->get_root(); + } + _update_all_gizmos(p_node); +} + +Object *Node3DEditor::_get_editor_data(Object *p_what) { + + Node3D *sp = Object::cast_to(p_what); + if (!sp) + return NULL; + + Node3DEditorSelectedItem *si = memnew(Node3DEditorSelectedItem); + + si->sp = sp; + si->sbox_instance = VisualServer::get_singleton()->instance_create2(selection_box->get_rid(), sp->get_world()->get_scenario()); + VS::get_singleton()->instance_geometry_set_cast_shadows_setting(si->sbox_instance, VS::SHADOW_CASTING_SETTING_OFF); + + return si; +} + +void Node3DEditor::_generate_selection_box() { + + AABB aabb(Vector3(), Vector3(1, 1, 1)); + aabb.grow_by(aabb.get_longest_axis_size() / 20.0); + + Ref st = memnew(SurfaceTool); + + st->begin(Mesh::PRIMITIVE_LINES); + for (int i = 0; i < 12; i++) { + + Vector3 a, b; + aabb.get_edge(i, a, b); + + st->add_color(Color(1.0, 1.0, 0.8, 0.8)); + st->add_vertex(a); + st->add_color(Color(1.0, 1.0, 0.8, 0.4)); + st->add_vertex(a.linear_interpolate(b, 0.2)); + + st->add_color(Color(1.0, 1.0, 0.8, 0.4)); + st->add_vertex(a.linear_interpolate(b, 0.8)); + st->add_color(Color(1.0, 1.0, 0.8, 0.8)); + st->add_vertex(b); + } + + Ref mat = memnew(StandardMaterial3D); + mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); + mat->set_albedo(Color(1, 1, 1)); + mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); + mat->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); + mat->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); + st->set_material(mat); + selection_box = st->commit(); +} + +Dictionary Node3DEditor::get_state() const { + + Dictionary d; + + d["snap_enabled"] = snap_enabled; + d["translate_snap"] = get_translate_snap(); + d["rotate_snap"] = get_rotate_snap(); + d["scale_snap"] = get_scale_snap(); + + d["local_coords"] = tool_option_button[TOOL_OPT_LOCAL_COORDS]->is_pressed(); + + int vc = 0; + if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT))) + vc = 1; + else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS))) + vc = 2; + else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS))) + vc = 3; + else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS))) + vc = 4; + else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS_ALT))) + vc = 5; + else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT))) + vc = 6; + + d["viewport_mode"] = vc; + Array vpdata; + for (int i = 0; i < 4; i++) { + vpdata.push_back(viewports[i]->get_state()); + } + + d["viewports"] = vpdata; + + d["show_grid"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_GRID)); + d["show_origin"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_ORIGIN)); + d["fov"] = get_fov(); + d["znear"] = get_znear(); + d["zfar"] = get_zfar(); + + Dictionary gizmos_status; + for (int i = 0; i < gizmo_plugins_by_name.size(); i++) { + if (!gizmo_plugins_by_name[i]->can_be_hidden()) continue; + int state = gizmos_menu->get_item_state(gizmos_menu->get_item_index(i)); + String name = gizmo_plugins_by_name[i]->get_name(); + gizmos_status[name] = state; + } + + d["gizmos_status"] = gizmos_status; + + return d; +} +void Node3DEditor::set_state(const Dictionary &p_state) { + + Dictionary d = p_state; + + if (d.has("snap_enabled")) { + snap_enabled = d["snap_enabled"]; + tool_option_button[TOOL_OPT_USE_SNAP]->set_pressed(d["snap_enabled"]); + } + + if (d.has("translate_snap")) + snap_translate_value = d["translate_snap"]; + + if (d.has("rotate_snap")) + snap_rotate_value = d["rotate_snap"]; + + if (d.has("scale_snap")) + snap_scale_value = d["scale_snap"]; + + _snap_update(); + + if (d.has("local_coords")) { + tool_option_button[TOOL_OPT_LOCAL_COORDS]->set_pressed(d["local_coords"]); + update_transform_gizmo(); + } + + if (d.has("viewport_mode")) { + int vc = d["viewport_mode"]; + + if (vc == 1) + _menu_item_pressed(MENU_VIEW_USE_1_VIEWPORT); + else if (vc == 2) + _menu_item_pressed(MENU_VIEW_USE_2_VIEWPORTS); + else if (vc == 3) + _menu_item_pressed(MENU_VIEW_USE_3_VIEWPORTS); + else if (vc == 4) + _menu_item_pressed(MENU_VIEW_USE_4_VIEWPORTS); + else if (vc == 5) + _menu_item_pressed(MENU_VIEW_USE_2_VIEWPORTS_ALT); + else if (vc == 6) + _menu_item_pressed(MENU_VIEW_USE_3_VIEWPORTS_ALT); + } + + if (d.has("viewports")) { + Array vp = d["viewports"]; + uint32_t vp_size = static_cast(vp.size()); + if (vp_size > VIEWPORTS_COUNT) { + WARN_PRINT("Ignoring superfluous viewport settings from spatial editor state."); + vp_size = VIEWPORTS_COUNT; + } + + for (uint32_t i = 0; i < vp_size; i++) { + viewports[i]->set_state(vp[i]); + } + } + + if (d.has("zfar")) + settings_zfar->set_value(float(d["zfar"])); + if (d.has("znear")) + settings_znear->set_value(float(d["znear"])); + if (d.has("fov")) + settings_fov->set_value(float(d["fov"])); + if (d.has("show_grid")) { + bool use = d["show_grid"]; + + if (use != view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_GRID))) { + _menu_item_pressed(MENU_VIEW_GRID); + } + } + + if (d.has("show_origin")) { + bool use = d["show_origin"]; + + if (use != view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_ORIGIN))) { + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_ORIGIN), use); + VisualServer::get_singleton()->instance_set_visible(origin_instance, use); + } + } + + if (d.has("gizmos_status")) { + Dictionary gizmos_status = d["gizmos_status"]; + List keys; + gizmos_status.get_key_list(&keys); + + for (int j = 0; j < gizmo_plugins_by_name.size(); ++j) { + if (!gizmo_plugins_by_name[j]->can_be_hidden()) continue; + int state = EditorNode3DGizmoPlugin::VISIBLE; + for (int i = 0; i < keys.size(); i++) { + if (gizmo_plugins_by_name.write[j]->get_name() == keys[i]) { + state = gizmos_status[keys[i]]; + break; + } + } + + gizmo_plugins_by_name.write[j]->set_state(state); + } + _update_gizmos_menu(); + } +} + +void Node3DEditor::edit(Node3D *p_spatial) { + + if (p_spatial != selected) { + if (selected) { + + Ref seg = selected->get_gizmo(); + if (seg.is_valid()) { + seg->set_selected(false); + selected->update_gizmo(); + } + } + + selected = p_spatial; + over_gizmo_handle = -1; + + if (selected) { + + Ref seg = selected->get_gizmo(); + if (seg.is_valid()) { + seg->set_selected(true); + selected->update_gizmo(); + } + } + } +} + +void Node3DEditor::_snap_changed() { + + snap_translate_value = snap_translate->get_text().to_double(); + snap_rotate_value = snap_rotate->get_text().to_double(); + snap_scale_value = snap_scale->get_text().to_double(); +} + +void Node3DEditor::_snap_update() { + + snap_translate->set_text(String::num(snap_translate_value)); + snap_rotate->set_text(String::num(snap_rotate_value)); + snap_scale->set_text(String::num(snap_scale_value)); +} + +void Node3DEditor::_xform_dialog_action() { + + Transform t; + //translation + Vector3 scale; + Vector3 rotate; + Vector3 translate; + + for (int i = 0; i < 3; i++) { + translate[i] = xform_translate[i]->get_text().to_double(); + rotate[i] = Math::deg2rad(xform_rotate[i]->get_text().to_double()); + scale[i] = xform_scale[i]->get_text().to_double(); + } + + t.basis.scale(scale); + t.basis.rotate(rotate); + t.origin = translate; + + undo_redo->create_action(TTR("XForm Dialog")); + + List &selection = editor_selection->get_selected_node_list(); + + for (List::Element *E = selection.front(); E; E = E->next()) { + + Node3D *sp = Object::cast_to(E->get()); + if (!sp) + continue; + + Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data(sp); + if (!se) + continue; + + bool post = xform_type->get_selected() > 0; + + Transform tr = sp->get_global_gizmo_transform(); + if (post) + tr = tr * t; + else { + + tr.basis = t.basis * tr.basis; + tr.origin += t.origin; + } + + undo_redo->add_do_method(sp, "set_global_transform", tr); + undo_redo->add_undo_method(sp, "set_global_transform", sp->get_global_gizmo_transform()); + } + undo_redo->commit_action(); +} + +void Node3DEditor::_menu_item_toggled(bool pressed, int p_option) { + + switch (p_option) { + case MENU_TOOL_LOCAL_COORDS: { + + tool_option_button[TOOL_OPT_LOCAL_COORDS]->set_pressed(pressed); + update_transform_gizmo(); + } break; + + case MENU_TOOL_USE_SNAP: { + tool_option_button[TOOL_OPT_USE_SNAP]->set_pressed(pressed); + snap_enabled = pressed; + } break; + + case MENU_TOOL_OVERRIDE_CAMERA: { + EditorDebuggerNode *const debugger = EditorDebuggerNode::get_singleton(); + + using Override = EditorDebuggerNode::CameraOverride; + if (pressed) { + + debugger->set_camera_override((Override)(Override::OVERRIDE_3D_1 + camera_override_viewport_id)); + } else { + debugger->set_camera_override(Override::OVERRIDE_NONE); + } + + } break; + } +} + +void Node3DEditor::_menu_gizmo_toggled(int p_option) { + + const int idx = gizmos_menu->get_item_index(p_option); + gizmos_menu->toggle_item_multistate(idx); + + // Change icon + const int state = gizmos_menu->get_item_state(idx); + switch (state) { + case EditorNode3DGizmoPlugin::VISIBLE: + gizmos_menu->set_item_icon(idx, view_menu->get_popup()->get_theme_icon("visibility_visible")); + break; + case EditorNode3DGizmoPlugin::ON_TOP: + gizmos_menu->set_item_icon(idx, view_menu->get_popup()->get_theme_icon("visibility_xray")); + break; + case EditorNode3DGizmoPlugin::HIDDEN: + gizmos_menu->set_item_icon(idx, view_menu->get_popup()->get_theme_icon("visibility_hidden")); + break; + } + + gizmo_plugins_by_name.write[p_option]->set_state(state); + + update_all_gizmos(); +} + +void Node3DEditor::_update_camera_override_button(bool p_game_running) { + Button *const button = tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]; + + if (p_game_running) { + button->set_disabled(false); + button->set_tooltip(TTR("Game Camera Override\nNo game instance running.")); + } else { + button->set_disabled(true); + button->set_pressed(false); + button->set_tooltip(TTR("Game Camera Override\nOverrides game camera with editor viewport camera.")); + } +} + +void Node3DEditor::_update_camera_override_viewport(Object *p_viewport) { + Node3DEditorViewport *current_viewport = Object::cast_to(p_viewport); + + if (!current_viewport) + return; + + EditorDebuggerNode *const debugger = EditorDebuggerNode::get_singleton(); + + camera_override_viewport_id = current_viewport->index; + if (debugger->get_camera_override() >= EditorDebuggerNode::OVERRIDE_3D_1) { + using Override = EditorDebuggerNode::CameraOverride; + + debugger->set_camera_override((Override)(Override::OVERRIDE_3D_1 + camera_override_viewport_id)); + } +} + +void Node3DEditor::_menu_item_pressed(int p_option) { + + switch (p_option) { + + case MENU_TOOL_SELECT: + case MENU_TOOL_MOVE: + case MENU_TOOL_ROTATE: + case MENU_TOOL_SCALE: + case MENU_TOOL_LIST_SELECT: { + + for (int i = 0; i < TOOL_MAX; i++) + tool_button[i]->set_pressed(i == p_option); + tool_mode = (ToolMode)p_option; + update_transform_gizmo(); + + } break; + case MENU_TRANSFORM_CONFIGURE_SNAP: { + + snap_dialog->popup_centered(Size2(200, 180)); + } break; + case MENU_TRANSFORM_DIALOG: { + + for (int i = 0; i < 3; i++) { + + xform_translate[i]->set_text("0"); + xform_rotate[i]->set_text("0"); + xform_scale[i]->set_text("1"); + } + + xform_dialog->popup_centered(Size2(320, 240) * EDSCALE); + + } break; + case MENU_VIEW_USE_1_VIEWPORT: { + + viewport_base->set_view(Node3DEditorViewportContainer::VIEW_USE_1_VIEWPORT); + + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), true); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), false); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS), false); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS), false); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS_ALT), false); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT), false); + + } break; + case MENU_VIEW_USE_2_VIEWPORTS: { + + viewport_base->set_view(Node3DEditorViewportContainer::VIEW_USE_2_VIEWPORTS); + + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), false); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), true); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS), false); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS), false); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS_ALT), false); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT), false); + + } break; + case MENU_VIEW_USE_2_VIEWPORTS_ALT: { + + viewport_base->set_view(Node3DEditorViewportContainer::VIEW_USE_2_VIEWPORTS_ALT); + + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), false); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), false); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS), false); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS), false); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS_ALT), true); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT), false); + + } break; + case MENU_VIEW_USE_3_VIEWPORTS: { + + viewport_base->set_view(Node3DEditorViewportContainer::VIEW_USE_3_VIEWPORTS); + + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), false); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), false); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS), true); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS), false); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS_ALT), false); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT), false); + + } break; + case MENU_VIEW_USE_3_VIEWPORTS_ALT: { + + viewport_base->set_view(Node3DEditorViewportContainer::VIEW_USE_3_VIEWPORTS_ALT); + + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), false); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), false); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS), false); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS), false); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS_ALT), false); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT), true); + + } break; + case MENU_VIEW_USE_4_VIEWPORTS: { + + viewport_base->set_view(Node3DEditorViewportContainer::VIEW_USE_4_VIEWPORTS); + + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), false); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), false); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS), false); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS), true); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS_ALT), false); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT), false); + + } break; + case MENU_VIEW_ORIGIN: { + + bool is_checked = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(p_option)); + + origin_enabled = !is_checked; + VisualServer::get_singleton()->instance_set_visible(origin_instance, origin_enabled); + // Update the grid since its appearance depends on whether the origin is enabled + _finish_grid(); + _init_grid(); + + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(p_option), origin_enabled); + } break; + case MENU_VIEW_GRID: { + + bool is_checked = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(p_option)); + + grid_enabled = !is_checked; + + for (int i = 0; i < 3; ++i) { + if (grid_enable[i]) { + VisualServer::get_singleton()->instance_set_visible(grid_instance[i], grid_enabled); + grid_visible[i] = grid_enabled; + } + } + + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(p_option), grid_enabled); + + } break; + case MENU_VIEW_CAMERA_SETTINGS: { + + settings_dialog->popup_centered(settings_vbc->get_combined_minimum_size() + Size2(50, 50)); + } break; + case MENU_SNAP_TO_FLOOR: { + snap_selected_nodes_to_floor(); + } break; + case MENU_LOCK_SELECTED: { + undo_redo->create_action(TTR("Lock Selected")); + + List &selection = editor_selection->get_selected_node_list(); + + for (List::Element *E = selection.front(); E; E = E->next()) { + + Node3D *spatial = Object::cast_to(E->get()); + if (!spatial || !spatial->is_visible_in_tree()) + continue; + + if (spatial->get_viewport() != EditorNode::get_singleton()->get_scene_root()) + continue; + + undo_redo->add_do_method(spatial, "set_meta", "_edit_lock_", true); + undo_redo->add_undo_method(spatial, "remove_meta", "_edit_lock_"); + undo_redo->add_do_method(this, "emit_signal", "item_lock_status_changed"); + undo_redo->add_undo_method(this, "emit_signal", "item_lock_status_changed"); + } + + undo_redo->add_do_method(this, "_refresh_menu_icons", Variant()); + undo_redo->add_undo_method(this, "_refresh_menu_icons", Variant()); + undo_redo->commit_action(); + } break; + case MENU_UNLOCK_SELECTED: { + undo_redo->create_action(TTR("Unlock Selected")); + + List &selection = editor_selection->get_selected_node_list(); + + for (List::Element *E = selection.front(); E; E = E->next()) { + + Node3D *spatial = Object::cast_to(E->get()); + if (!spatial || !spatial->is_visible_in_tree()) + continue; + + if (spatial->get_viewport() != EditorNode::get_singleton()->get_scene_root()) + continue; + + undo_redo->add_do_method(spatial, "remove_meta", "_edit_lock_"); + undo_redo->add_undo_method(spatial, "set_meta", "_edit_lock_", true); + undo_redo->add_do_method(this, "emit_signal", "item_lock_status_changed"); + undo_redo->add_undo_method(this, "emit_signal", "item_lock_status_changed"); + } + + undo_redo->add_do_method(this, "_refresh_menu_icons", Variant()); + undo_redo->add_undo_method(this, "_refresh_menu_icons", Variant()); + undo_redo->commit_action(); + } break; + case MENU_GROUP_SELECTED: { + undo_redo->create_action(TTR("Group Selected")); + + List &selection = editor_selection->get_selected_node_list(); + + for (List::Element *E = selection.front(); E; E = E->next()) { + + Node3D *spatial = Object::cast_to(E->get()); + if (!spatial || !spatial->is_visible_in_tree()) + continue; + + if (spatial->get_viewport() != EditorNode::get_singleton()->get_scene_root()) + continue; + + undo_redo->add_do_method(spatial, "set_meta", "_edit_group_", true); + undo_redo->add_undo_method(spatial, "remove_meta", "_edit_group_"); + undo_redo->add_do_method(this, "emit_signal", "item_group_status_changed"); + undo_redo->add_undo_method(this, "emit_signal", "item_group_status_changed"); + } + + undo_redo->add_do_method(this, "_refresh_menu_icons", Variant()); + undo_redo->add_undo_method(this, "_refresh_menu_icons", Variant()); + undo_redo->commit_action(); + } break; + case MENU_UNGROUP_SELECTED: { + undo_redo->create_action(TTR("Ungroup Selected")); + List &selection = editor_selection->get_selected_node_list(); + + for (List::Element *E = selection.front(); E; E = E->next()) { + + Node3D *spatial = Object::cast_to(E->get()); + if (!spatial || !spatial->is_visible_in_tree()) + continue; + + if (spatial->get_viewport() != EditorNode::get_singleton()->get_scene_root()) + continue; + + undo_redo->add_do_method(spatial, "remove_meta", "_edit_group_"); + undo_redo->add_undo_method(spatial, "set_meta", "_edit_group_", true); + undo_redo->add_do_method(this, "emit_signal", "item_group_status_changed"); + undo_redo->add_undo_method(this, "emit_signal", "item_group_status_changed"); + } + + undo_redo->add_do_method(this, "_refresh_menu_icons", Variant()); + undo_redo->add_undo_method(this, "_refresh_menu_icons", Variant()); + undo_redo->commit_action(); + } break; + } +} + +void Node3DEditor::_init_indicators() { + + { + origin_enabled = true; + grid_enabled = true; + + indicator_mat.instance(); + indicator_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); + indicator_mat->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); + indicator_mat->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); + + Vector origin_colors; + Vector origin_points; + + for (int i = 0; i < 3; i++) { + Vector3 axis; + axis[i] = 1; + Color origin_color; + switch (i) { + case 0: + origin_color = get_theme_color("axis_x_color", "Editor"); + break; + case 1: + origin_color = get_theme_color("axis_y_color", "Editor"); + break; + case 2: + origin_color = get_theme_color("axis_z_color", "Editor"); + break; + default: + origin_color = Color(); + break; + } + + grid_enable[i] = false; + grid_visible[i] = false; + + origin_colors.push_back(origin_color); + origin_colors.push_back(origin_color); + origin_points.push_back(axis * 4096); + origin_points.push_back(axis * -4096); + } + + grid_enable[1] = true; + grid_visible[1] = true; + + _init_grid(); + + origin = VisualServer::get_singleton()->mesh_create(); + Array d; + d.resize(VS::ARRAY_MAX); + d[VisualServer::ARRAY_VERTEX] = origin_points; + d[VisualServer::ARRAY_COLOR] = origin_colors; + + VisualServer::get_singleton()->mesh_add_surface_from_arrays(origin, VisualServer::PRIMITIVE_LINES, d); + VisualServer::get_singleton()->mesh_surface_set_material(origin, 0, indicator_mat->get_rid()); + + origin_instance = VisualServer::get_singleton()->instance_create2(origin, get_tree()->get_root()->get_world()->get_scenario()); + VS::get_singleton()->instance_set_layer_mask(origin_instance, 1 << Node3DEditorViewport::GIZMO_GRID_LAYER); + + VisualServer::get_singleton()->instance_geometry_set_cast_shadows_setting(origin_instance, VS::SHADOW_CASTING_SETTING_OFF); + } + + { + + //move gizmo + + for (int i = 0; i < 3; i++) { + + Color col; + switch (i) { + case 0: + col = get_theme_color("axis_x_color", "Editor"); + break; + case 1: + col = get_theme_color("axis_y_color", "Editor"); + break; + case 2: + col = get_theme_color("axis_z_color", "Editor"); + break; + default: + col = Color(); + break; + } + + col.a = EditorSettings::get_singleton()->get("editors/3d/manipulator_gizmo_opacity"); + + move_gizmo[i] = Ref(memnew(ArrayMesh)); + move_plane_gizmo[i] = Ref(memnew(ArrayMesh)); + rotate_gizmo[i] = Ref(memnew(ArrayMesh)); + scale_gizmo[i] = Ref(memnew(ArrayMesh)); + scale_plane_gizmo[i] = Ref(memnew(ArrayMesh)); + + Ref mat = memnew(StandardMaterial3D); + mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); + mat->set_on_top_of_alpha(); + mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); + mat->set_albedo(col); + gizmo_color[i] = mat; + + Ref mat_hl = mat->duplicate(); + mat_hl->set_albedo(Color(col.r, col.g, col.b, 1.0)); + gizmo_color_hl[i] = mat_hl; + + Vector3 ivec; + ivec[i] = 1; + Vector3 nivec; + nivec[(i + 1) % 3] = 1; + nivec[(i + 2) % 3] = 1; + Vector3 ivec2; + ivec2[(i + 1) % 3] = 1; + Vector3 ivec3; + ivec3[(i + 2) % 3] = 1; + + //translate + { + + Ref surftool = memnew(SurfaceTool); + surftool->begin(Mesh::PRIMITIVE_TRIANGLES); + + // Arrow profile + const int arrow_points = 5; + Vector3 arrow[5] = { + nivec * 0.0 + ivec * 0.0, + nivec * 0.01 + ivec * 0.0, + nivec * 0.01 + ivec * GIZMO_ARROW_OFFSET, + nivec * 0.065 + ivec * GIZMO_ARROW_OFFSET, + nivec * 0.0 + ivec * (GIZMO_ARROW_OFFSET + GIZMO_ARROW_SIZE), + }; + + int arrow_sides = 16; + + for (int k = 0; k < arrow_sides; k++) { + + Basis ma(ivec, Math_PI * 2 * float(k) / arrow_sides); + Basis mb(ivec, Math_PI * 2 * float(k + 1) / arrow_sides); + + for (int j = 0; j < arrow_points - 1; j++) { + + Vector3 points[4] = { + ma.xform(arrow[j]), + mb.xform(arrow[j]), + mb.xform(arrow[j + 1]), + ma.xform(arrow[j + 1]), + }; + surftool->add_vertex(points[0]); + surftool->add_vertex(points[1]); + surftool->add_vertex(points[2]); + + surftool->add_vertex(points[0]); + surftool->add_vertex(points[2]); + surftool->add_vertex(points[3]); + } + } + + surftool->set_material(mat); + surftool->commit(move_gizmo[i]); + } + + // Plane Translation + { + Ref surftool = memnew(SurfaceTool); + surftool->begin(Mesh::PRIMITIVE_TRIANGLES); + + Vector3 vec = ivec2 - ivec3; + Vector3 plane[4] = { + vec * GIZMO_PLANE_DST, + vec * GIZMO_PLANE_DST + ivec2 * GIZMO_PLANE_SIZE, + vec * (GIZMO_PLANE_DST + GIZMO_PLANE_SIZE), + vec * GIZMO_PLANE_DST - ivec3 * GIZMO_PLANE_SIZE + }; + + Basis ma(ivec, Math_PI / 2); + + Vector3 points[4] = { + ma.xform(plane[0]), + ma.xform(plane[1]), + ma.xform(plane[2]), + ma.xform(plane[3]), + }; + surftool->add_vertex(points[0]); + surftool->add_vertex(points[1]); + surftool->add_vertex(points[2]); + + surftool->add_vertex(points[0]); + surftool->add_vertex(points[2]); + surftool->add_vertex(points[3]); + + Ref plane_mat = memnew(StandardMaterial3D); + plane_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); + plane_mat->set_on_top_of_alpha(); + plane_mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); + plane_mat->set_cull_mode(StandardMaterial3D::CULL_DISABLED); + plane_mat->set_albedo(col); + plane_gizmo_color[i] = plane_mat; // needed, so we can draw planes from both sides + surftool->set_material(plane_mat); + surftool->commit(move_plane_gizmo[i]); + + Ref plane_mat_hl = plane_mat->duplicate(); + plane_mat_hl->set_albedo(Color(col.r, col.g, col.b, 1.0)); + plane_gizmo_color_hl[i] = plane_mat_hl; // needed, so we can draw planes from both sides + } + + // Rotate + { + + Ref surftool = memnew(SurfaceTool); + surftool->begin(Mesh::PRIMITIVE_TRIANGLES); + + Vector3 circle[5] = { + ivec * 0.02 + ivec2 * 0.02 + ivec2 * GIZMO_CIRCLE_SIZE, + ivec * -0.02 + ivec2 * 0.02 + ivec2 * GIZMO_CIRCLE_SIZE, + ivec * -0.02 + ivec2 * -0.02 + ivec2 * GIZMO_CIRCLE_SIZE, + ivec * 0.02 + ivec2 * -0.02 + ivec2 * GIZMO_CIRCLE_SIZE, + ivec * 0.02 + ivec2 * 0.02 + ivec2 * GIZMO_CIRCLE_SIZE, + }; + + for (int k = 0; k < 64; k++) { + + Basis ma(ivec, Math_PI * 2 * float(k) / 64); + Basis mb(ivec, Math_PI * 2 * float(k + 1) / 64); + + for (int j = 0; j < 4; j++) { + + Vector3 points[4] = { + ma.xform(circle[j]), + mb.xform(circle[j]), + mb.xform(circle[j + 1]), + ma.xform(circle[j + 1]), + }; + surftool->add_vertex(points[0]); + surftool->add_vertex(points[1]); + surftool->add_vertex(points[2]); + + surftool->add_vertex(points[0]); + surftool->add_vertex(points[2]); + surftool->add_vertex(points[3]); + } + } + + surftool->set_material(mat); + surftool->commit(rotate_gizmo[i]); + } + + // Scale + { + Ref surftool = memnew(SurfaceTool); + surftool->begin(Mesh::PRIMITIVE_TRIANGLES); + + // Cube arrow profile + const int arrow_points = 6; + Vector3 arrow[6] = { + nivec * 0.0 + ivec * 0.0, + nivec * 0.01 + ivec * 0.0, + nivec * 0.01 + ivec * 1.0 * GIZMO_SCALE_OFFSET, + nivec * 0.07 + ivec * 1.0 * GIZMO_SCALE_OFFSET, + nivec * 0.07 + ivec * 1.11 * GIZMO_SCALE_OFFSET, + nivec * 0.0 + ivec * 1.11 * GIZMO_SCALE_OFFSET, + }; + + int arrow_sides = 4; + + for (int k = 0; k < 4; k++) { + + Basis ma(ivec, Math_PI * 2 * float(k) / arrow_sides); + Basis mb(ivec, Math_PI * 2 * float(k + 1) / arrow_sides); + + for (int j = 0; j < arrow_points - 1; j++) { + + Vector3 points[4] = { + ma.xform(arrow[j]), + mb.xform(arrow[j]), + mb.xform(arrow[j + 1]), + ma.xform(arrow[j + 1]), + }; + surftool->add_vertex(points[0]); + surftool->add_vertex(points[1]); + surftool->add_vertex(points[2]); + + surftool->add_vertex(points[0]); + surftool->add_vertex(points[2]); + surftool->add_vertex(points[3]); + } + } + + surftool->set_material(mat); + surftool->commit(scale_gizmo[i]); + } + + // Plane Scale + { + Ref surftool = memnew(SurfaceTool); + surftool->begin(Mesh::PRIMITIVE_TRIANGLES); + + Vector3 vec = ivec2 - ivec3; + Vector3 plane[4] = { + vec * GIZMO_PLANE_DST, + vec * GIZMO_PLANE_DST + ivec2 * GIZMO_PLANE_SIZE, + vec * (GIZMO_PLANE_DST + GIZMO_PLANE_SIZE), + vec * GIZMO_PLANE_DST - ivec3 * GIZMO_PLANE_SIZE + }; + + Basis ma(ivec, Math_PI / 2); + + Vector3 points[4] = { + ma.xform(plane[0]), + ma.xform(plane[1]), + ma.xform(plane[2]), + ma.xform(plane[3]), + }; + surftool->add_vertex(points[0]); + surftool->add_vertex(points[1]); + surftool->add_vertex(points[2]); + + surftool->add_vertex(points[0]); + surftool->add_vertex(points[2]); + surftool->add_vertex(points[3]); + + Ref plane_mat = memnew(StandardMaterial3D); + plane_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); + plane_mat->set_on_top_of_alpha(); + plane_mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); + plane_mat->set_cull_mode(StandardMaterial3D::CULL_DISABLED); + plane_mat->set_albedo(col); + plane_gizmo_color[i] = plane_mat; // needed, so we can draw planes from both sides + surftool->set_material(plane_mat); + surftool->commit(scale_plane_gizmo[i]); + + Ref plane_mat_hl = plane_mat->duplicate(); + plane_mat_hl->set_albedo(Color(col.r, col.g, col.b, 1.0)); + plane_gizmo_color_hl[i] = plane_mat_hl; // needed, so we can draw planes from both sides + } + } + } + + _generate_selection_box(); +} + +void Node3DEditor::_update_gizmos_menu() { + + gizmos_menu->clear(); + + for (int i = 0; i < gizmo_plugins_by_name.size(); ++i) { + if (!gizmo_plugins_by_name[i]->can_be_hidden()) continue; + String plugin_name = gizmo_plugins_by_name[i]->get_name(); + const int plugin_state = gizmo_plugins_by_name[i]->get_state(); + gizmos_menu->add_multistate_item(TTR(plugin_name), 3, plugin_state, i); + const int idx = gizmos_menu->get_item_index(i); + switch (plugin_state) { + case EditorNode3DGizmoPlugin::VISIBLE: + gizmos_menu->set_item_icon(idx, gizmos_menu->get_theme_icon("visibility_visible")); + break; + case EditorNode3DGizmoPlugin::ON_TOP: + gizmos_menu->set_item_icon(idx, gizmos_menu->get_theme_icon("visibility_xray")); + break; + case EditorNode3DGizmoPlugin::HIDDEN: + gizmos_menu->set_item_icon(idx, gizmos_menu->get_theme_icon("visibility_hidden")); + break; + } + } +} + +void Node3DEditor::_update_gizmos_menu_theme() { + for (int i = 0; i < gizmo_plugins_by_name.size(); ++i) { + if (!gizmo_plugins_by_name[i]->can_be_hidden()) continue; + const int plugin_state = gizmo_plugins_by_name[i]->get_state(); + const int idx = gizmos_menu->get_item_index(i); + switch (plugin_state) { + case EditorNode3DGizmoPlugin::VISIBLE: + gizmos_menu->set_item_icon(idx, gizmos_menu->get_theme_icon("visibility_visible")); + break; + case EditorNode3DGizmoPlugin::ON_TOP: + gizmos_menu->set_item_icon(idx, gizmos_menu->get_theme_icon("visibility_xray")); + break; + case EditorNode3DGizmoPlugin::HIDDEN: + gizmos_menu->set_item_icon(idx, gizmos_menu->get_theme_icon("visibility_hidden")); + break; + } + } +} + +void Node3DEditor::_init_grid() { + + Vector grid_colors[3]; + Vector grid_points[3]; + + Color primary_grid_color = EditorSettings::get_singleton()->get("editors/3d/primary_grid_color"); + Color secondary_grid_color = EditorSettings::get_singleton()->get("editors/3d/secondary_grid_color"); + int grid_size = EditorSettings::get_singleton()->get("editors/3d/grid_size"); + int primary_grid_steps = EditorSettings::get_singleton()->get("editors/3d/primary_grid_steps"); + + for (int i = 0; i < 3; i++) { + Vector3 axis; + axis[i] = 1; + Vector3 axis_n1; + axis_n1[(i + 1) % 3] = 1; + Vector3 axis_n2; + axis_n2[(i + 2) % 3] = 1; + + for (int j = -grid_size; j <= grid_size; j++) { + Vector3 p1 = axis_n1 * j + axis_n2 * -grid_size; + Vector3 p1_dest = p1 * (-axis_n2 + axis_n1); + Vector3 p2 = axis_n2 * j + axis_n1 * -grid_size; + Vector3 p2_dest = p2 * (-axis_n1 + axis_n2); + + Color line_color = secondary_grid_color; + if (origin_enabled && j == 0) { + // Don't draw the center lines of the grid if the origin is enabled + // The origin would overlap the grid lines in this case, causing flickering + continue; + } else if (j % primary_grid_steps == 0) { + line_color = primary_grid_color; + } + + grid_points[i].push_back(p1); + grid_points[i].push_back(p1_dest); + grid_colors[i].push_back(line_color); + grid_colors[i].push_back(line_color); + + grid_points[i].push_back(p2); + grid_points[i].push_back(p2_dest); + grid_colors[i].push_back(line_color); + grid_colors[i].push_back(line_color); + } + + grid[i] = VisualServer::get_singleton()->mesh_create(); + Array d; + d.resize(VS::ARRAY_MAX); + d[VisualServer::ARRAY_VERTEX] = grid_points[i]; + d[VisualServer::ARRAY_COLOR] = grid_colors[i]; + VisualServer::get_singleton()->mesh_add_surface_from_arrays(grid[i], VisualServer::PRIMITIVE_LINES, d); + VisualServer::get_singleton()->mesh_surface_set_material(grid[i], 0, indicator_mat->get_rid()); + grid_instance[i] = VisualServer::get_singleton()->instance_create2(grid[i], get_tree()->get_root()->get_world()->get_scenario()); + + VisualServer::get_singleton()->instance_set_visible(grid_instance[i], grid_visible[i]); + VisualServer::get_singleton()->instance_geometry_set_cast_shadows_setting(grid_instance[i], VS::SHADOW_CASTING_SETTING_OFF); + VS::get_singleton()->instance_set_layer_mask(grid_instance[i], 1 << Node3DEditorViewport::GIZMO_GRID_LAYER); + } +} + +void Node3DEditor::_finish_indicators() { + + VisualServer::get_singleton()->free(origin_instance); + VisualServer::get_singleton()->free(origin); + + _finish_grid(); +} + +void Node3DEditor::_finish_grid() { + for (int i = 0; i < 3; i++) { + VisualServer::get_singleton()->free(grid_instance[i]); + VisualServer::get_singleton()->free(grid[i]); + } +} + +bool Node3DEditor::is_any_freelook_active() const { + for (unsigned int i = 0; i < VIEWPORTS_COUNT; ++i) { + if (viewports[i]->is_freelook_active()) + return true; + } + return false; +} + +void Node3DEditor::_refresh_menu_icons() { + + bool all_locked = true; + bool all_grouped = true; + + List &selection = editor_selection->get_selected_node_list(); + + if (selection.empty()) { + all_locked = false; + all_grouped = false; + } else { + for (List::Element *E = selection.front(); E; E = E->next()) { + if (Object::cast_to(E->get()) && !Object::cast_to(E->get())->has_meta("_edit_lock_")) { + all_locked = false; + break; + } + } + for (List::Element *E = selection.front(); E; E = E->next()) { + if (Object::cast_to(E->get()) && !Object::cast_to(E->get())->has_meta("_edit_group_")) { + all_grouped = false; + break; + } + } + } + + tool_button[TOOL_LOCK_SELECTED]->set_visible(!all_locked); + tool_button[TOOL_LOCK_SELECTED]->set_disabled(selection.empty()); + tool_button[TOOL_UNLOCK_SELECTED]->set_visible(all_locked); + + tool_button[TOOL_GROUP_SELECTED]->set_visible(!all_grouped); + tool_button[TOOL_GROUP_SELECTED]->set_disabled(selection.empty()); + tool_button[TOOL_UNGROUP_SELECTED]->set_visible(all_grouped); +} + +template +Set _get_child_nodes(Node *parent_node) { + Set nodes = Set(); + T *node = Node::cast_to(parent_node); + if (node) { + nodes.insert(node); + } + + for (int i = 0; i < parent_node->get_child_count(); i++) { + Node *child_node = parent_node->get_child(i); + Set child_nodes = _get_child_nodes(child_node); + for (typename Set::Element *I = child_nodes.front(); I; I = I->next()) { + nodes.insert(I->get()); + } + } + + return nodes; +} + +Set _get_physics_bodies_rid(Node *node) { + Set rids = Set(); + PhysicsBody3D *pb = Node::cast_to(node); + if (pb) { + rids.insert(pb->get_rid()); + } + Set child_nodes = _get_child_nodes(node); + for (Set::Element *I = child_nodes.front(); I; I = I->next()) { + rids.insert(I->get()->get_rid()); + } + + return rids; +} + +void Node3DEditor::snap_selected_nodes_to_floor() { + List &selection = editor_selection->get_selected_node_list(); + Dictionary snap_data; + + for (List::Element *E = selection.front(); E; E = E->next()) { + Node3D *sp = Object::cast_to(E->get()); + if (sp) { + Vector3 from = Vector3(); + Vector3 position_offset = Vector3(); + + // Priorities for snapping to floor are CollisionShapes, VisualInstances and then origin + Set vi = _get_child_nodes(sp); + Set cs = _get_child_nodes(sp); + + if (cs.size()) { + AABB aabb = sp->get_global_transform().xform(cs.front()->get()->get_shape()->get_debug_mesh()->get_aabb()); + for (Set::Element *I = cs.front(); I; I = I->next()) { + aabb.merge_with(sp->get_global_transform().xform(I->get()->get_shape()->get_debug_mesh()->get_aabb())); + } + Vector3 size = aabb.size * Vector3(0.5, 0.0, 0.5); + from = aabb.position + size; + position_offset.y = from.y - sp->get_global_transform().origin.y; + } else if (vi.size()) { + AABB aabb = vi.front()->get()->get_transformed_aabb(); + for (Set::Element *I = vi.front(); I; I = I->next()) { + aabb.merge_with(I->get()->get_transformed_aabb()); + } + Vector3 size = aabb.size * Vector3(0.5, 0.0, 0.5); + from = aabb.position + size; + position_offset.y = from.y - sp->get_global_transform().origin.y; + } else { + from = sp->get_global_transform().origin; + } + + // We add a bit of margin to the from position to avoid it from snapping + // when the spatial is already on a floor and there's another floor under + // it + from = from + Vector3(0.0, 0.2, 0.0); + + Dictionary d; + + d["from"] = from; + d["position_offset"] = position_offset; + snap_data[sp] = d; + } + } + + PhysicsDirectSpaceState *ss = get_tree()->get_root()->get_world()->get_direct_space_state(); + PhysicsDirectSpaceState::RayResult result; + + Array keys = snap_data.keys(); + + // The maximum height an object can travel to be snapped + const float max_snap_height = 20.0; + + // Will be set to `true` if at least one node from the selection was successfully snapped + bool snapped_to_floor = false; + + if (keys.size()) { + // For snapping to be performed, there must be solid geometry under at least one of the selected nodes. + // We need to check this before snapping to register the undo/redo action only if needed. + for (int i = 0; i < keys.size(); i++) { + Node *node = keys[i]; + Node3D *sp = Object::cast_to(node); + Dictionary d = snap_data[node]; + Vector3 from = d["from"]; + Vector3 to = from - Vector3(0.0, max_snap_height, 0.0); + Set excluded = _get_physics_bodies_rid(sp); + + if (ss->intersect_ray(from, to, result, excluded)) { + snapped_to_floor = true; + } + } + + if (snapped_to_floor) { + undo_redo->create_action(TTR("Snap Nodes To Floor")); + + // Perform snapping if at least one node can be snapped + for (int i = 0; i < keys.size(); i++) { + Node *node = keys[i]; + Node3D *sp = Object::cast_to(node); + Dictionary d = snap_data[node]; + Vector3 from = d["from"]; + Vector3 to = from - Vector3(0.0, max_snap_height, 0.0); + Set excluded = _get_physics_bodies_rid(sp); + + if (ss->intersect_ray(from, to, result, excluded)) { + Vector3 position_offset = d["position_offset"]; + Transform new_transform = sp->get_global_transform(); + + new_transform.origin.y = result.position.y; + new_transform.origin = new_transform.origin - position_offset; + + undo_redo->add_do_method(sp, "set_global_transform", new_transform); + undo_redo->add_undo_method(sp, "set_global_transform", sp->get_global_transform()); + } + } + + undo_redo->commit_action(); + } else { + EditorNode::get_singleton()->show_warning(TTR("Couldn't find a solid floor to snap the selection to.")); + } + } +} + +void Node3DEditor::_unhandled_key_input(Ref p_event) { + + if (!is_visible_in_tree()) + return; + + snap_key_enabled = InputFilter::get_singleton()->is_key_pressed(KEY_CONTROL); +} +void Node3DEditor::_notification(int p_what) { + + if (p_what == NOTIFICATION_READY) { + + tool_button[Node3DEditor::TOOL_MODE_SELECT]->set_icon(get_theme_icon("ToolSelect", "EditorIcons")); + tool_button[Node3DEditor::TOOL_MODE_MOVE]->set_icon(get_theme_icon("ToolMove", "EditorIcons")); + tool_button[Node3DEditor::TOOL_MODE_ROTATE]->set_icon(get_theme_icon("ToolRotate", "EditorIcons")); + tool_button[Node3DEditor::TOOL_MODE_SCALE]->set_icon(get_theme_icon("ToolScale", "EditorIcons")); + tool_button[Node3DEditor::TOOL_MODE_LIST_SELECT]->set_icon(get_theme_icon("ListSelect", "EditorIcons")); + tool_button[Node3DEditor::TOOL_LOCK_SELECTED]->set_icon(get_theme_icon("Lock", "EditorIcons")); + tool_button[Node3DEditor::TOOL_UNLOCK_SELECTED]->set_icon(get_theme_icon("Unlock", "EditorIcons")); + tool_button[Node3DEditor::TOOL_GROUP_SELECTED]->set_icon(get_theme_icon("Group", "EditorIcons")); + tool_button[Node3DEditor::TOOL_UNGROUP_SELECTED]->set_icon(get_theme_icon("Ungroup", "EditorIcons")); + + tool_option_button[Node3DEditor::TOOL_OPT_LOCAL_COORDS]->set_icon(get_theme_icon("Object", "EditorIcons")); + tool_option_button[Node3DEditor::TOOL_OPT_USE_SNAP]->set_icon(get_theme_icon("Snap", "EditorIcons")); + tool_option_button[Node3DEditor::TOOL_OPT_OVERRIDE_CAMERA]->set_icon(get_theme_icon("Camera3D", "EditorIcons")); + + view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), get_theme_icon("Panels1", "EditorIcons")); + view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), get_theme_icon("Panels2", "EditorIcons")); + view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS_ALT), get_theme_icon("Panels2Alt", "EditorIcons")); + view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS), get_theme_icon("Panels3", "EditorIcons")); + view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT), get_theme_icon("Panels3Alt", "EditorIcons")); + view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS), get_theme_icon("Panels4", "EditorIcons")); + + _menu_item_pressed(MENU_VIEW_USE_1_VIEWPORT); + + _refresh_menu_icons(); + + get_tree()->connect("node_removed", callable_mp(this, &Node3DEditor::_node_removed)); + EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor()->connect("node_changed", callable_mp(this, &Node3DEditor::_refresh_menu_icons)); + editor_selection->connect("selection_changed", callable_mp(this, &Node3DEditor::_refresh_menu_icons)); + + editor->connect("stop_pressed", callable_mp(this, &Node3DEditor::_update_camera_override_button), make_binds(false)); + editor->connect("play_pressed", callable_mp(this, &Node3DEditor::_update_camera_override_button), make_binds(true)); + } else if (p_what == NOTIFICATION_ENTER_TREE) { + + _register_all_gizmos(); + _update_gizmos_menu(); + _init_indicators(); + } else if (p_what == NOTIFICATION_THEME_CHANGED) { + _update_gizmos_menu_theme(); + } else if (p_what == NOTIFICATION_EXIT_TREE) { + + _finish_indicators(); + } else if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) { + tool_button[Node3DEditor::TOOL_MODE_SELECT]->set_icon(get_theme_icon("ToolSelect", "EditorIcons")); + tool_button[Node3DEditor::TOOL_MODE_MOVE]->set_icon(get_theme_icon("ToolMove", "EditorIcons")); + tool_button[Node3DEditor::TOOL_MODE_ROTATE]->set_icon(get_theme_icon("ToolRotate", "EditorIcons")); + tool_button[Node3DEditor::TOOL_MODE_SCALE]->set_icon(get_theme_icon("ToolScale", "EditorIcons")); + tool_button[Node3DEditor::TOOL_MODE_LIST_SELECT]->set_icon(get_theme_icon("ListSelect", "EditorIcons")); + tool_button[Node3DEditor::TOOL_LOCK_SELECTED]->set_icon(get_theme_icon("Lock", "EditorIcons")); + tool_button[Node3DEditor::TOOL_UNLOCK_SELECTED]->set_icon(get_theme_icon("Unlock", "EditorIcons")); + tool_button[Node3DEditor::TOOL_GROUP_SELECTED]->set_icon(get_theme_icon("Group", "EditorIcons")); + tool_button[Node3DEditor::TOOL_UNGROUP_SELECTED]->set_icon(get_theme_icon("Ungroup", "EditorIcons")); + + tool_option_button[Node3DEditor::TOOL_OPT_LOCAL_COORDS]->set_icon(get_theme_icon("Object", "EditorIcons")); + tool_option_button[Node3DEditor::TOOL_OPT_USE_SNAP]->set_icon(get_theme_icon("Snap", "EditorIcons")); + + view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), get_theme_icon("Panels1", "EditorIcons")); + view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), get_theme_icon("Panels2", "EditorIcons")); + view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS_ALT), get_theme_icon("Panels2Alt", "EditorIcons")); + view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS), get_theme_icon("Panels3", "EditorIcons")); + view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT), get_theme_icon("Panels3Alt", "EditorIcons")); + view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS), get_theme_icon("Panels4", "EditorIcons")); + + // Update grid color by rebuilding grid. + _finish_grid(); + _init_grid(); + } else if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { + if (!is_visible() && tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->is_pressed()) { + EditorDebuggerNode *debugger = EditorDebuggerNode::get_singleton(); + + debugger->set_camera_override(EditorDebuggerNode::OVERRIDE_NONE); + tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->set_pressed(false); + } + } +} + +void Node3DEditor::add_control_to_menu_panel(Control *p_control) { + + hbc_menu->add_child(p_control); +} + +void Node3DEditor::remove_control_from_menu_panel(Control *p_control) { + + hbc_menu->remove_child(p_control); +} + +void Node3DEditor::set_can_preview(Camera3D *p_preview) { + + for (int i = 0; i < 4; i++) { + viewports[i]->set_can_preview(p_preview); + } +} + +VSplitContainer *Node3DEditor::get_shader_split() { + + return shader_split; +} + +HSplitContainer *Node3DEditor::get_palette_split() { + + return palette_split; +} + +void Node3DEditor::_request_gizmo(Object *p_obj) { + + Node3D *sp = Object::cast_to(p_obj); + if (!sp) + return; + if (editor->get_edited_scene() && (sp == editor->get_edited_scene() || (sp->get_owner() && editor->get_edited_scene()->is_a_parent_of(sp)))) { + + Ref seg; + + for (int i = 0; i < gizmo_plugins_by_priority.size(); ++i) { + seg = gizmo_plugins_by_priority.write[i]->get_gizmo(sp); + + if (seg.is_valid()) { + sp->set_gizmo(seg); + + if (sp == selected) { + seg->set_selected(true); + selected->update_gizmo(); + } + + break; + } + } + } +} + +void Node3DEditor::_toggle_maximize_view(Object *p_viewport) { + if (!p_viewport) return; + Node3DEditorViewport *current_viewport = Object::cast_to(p_viewport); + if (!current_viewport) return; + + int index = -1; + bool maximized = false; + for (int i = 0; i < 4; i++) { + if (viewports[i] == current_viewport) { + index = i; + if (current_viewport->get_global_rect() == viewport_base->get_global_rect()) + maximized = true; + break; + } + } + if (index == -1) return; + + if (!maximized) { + + for (uint32_t i = 0; i < VIEWPORTS_COUNT; i++) { + if (i == (uint32_t)index) + viewports[i]->set_anchors_and_margins_preset(Control::PRESET_WIDE); + else + viewports[i]->hide(); + } + } else { + + for (uint32_t i = 0; i < VIEWPORTS_COUNT; i++) + viewports[i]->show(); + + if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT))) + _menu_item_pressed(MENU_VIEW_USE_1_VIEWPORT); + else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS))) + _menu_item_pressed(MENU_VIEW_USE_2_VIEWPORTS); + else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS_ALT))) + _menu_item_pressed(MENU_VIEW_USE_2_VIEWPORTS_ALT); + else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS))) + _menu_item_pressed(MENU_VIEW_USE_3_VIEWPORTS); + else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT))) + _menu_item_pressed(MENU_VIEW_USE_3_VIEWPORTS_ALT); + else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS))) + _menu_item_pressed(MENU_VIEW_USE_4_VIEWPORTS); + } +} + +void Node3DEditor::_node_removed(Node *p_node) { + + if (p_node == selected) + selected = NULL; +} + +void Node3DEditor::_register_all_gizmos() { + add_gizmo_plugin(Ref(memnew(CameraNode3DGizmoPlugin))); + add_gizmo_plugin(Ref(memnew(LightNode3DGizmoPlugin))); + add_gizmo_plugin(Ref(memnew(AudioStreamPlayer3DNode3DGizmoPlugin))); + add_gizmo_plugin(Ref(memnew(MeshInstanceNode3DGizmoPlugin))); + add_gizmo_plugin(Ref(memnew(SoftBodyNode3DGizmoPlugin))); + add_gizmo_plugin(Ref(memnew(Sprite3DNode3DGizmoPlugin))); + add_gizmo_plugin(Ref(memnew(SkeletonNode3DGizmoPlugin))); + add_gizmo_plugin(Ref(memnew(Position3DNode3DGizmoPlugin))); + add_gizmo_plugin(Ref(memnew(RayCastNode3DGizmoPlugin))); + add_gizmo_plugin(Ref(memnew(SpringArmNode3DGizmoPlugin))); + add_gizmo_plugin(Ref(memnew(VehicleWheelNode3DGizmoPlugin))); + add_gizmo_plugin(Ref(memnew(VisibilityNotifierGizmoPlugin))); + add_gizmo_plugin(Ref(memnew(ParticlesGizmoPlugin))); + add_gizmo_plugin(Ref(memnew(CPUParticlesGizmoPlugin))); + add_gizmo_plugin(Ref(memnew(ReflectionProbeGizmoPlugin))); + add_gizmo_plugin(Ref(memnew(GIProbeGizmoPlugin))); + // add_gizmo_plugin(Ref(memnew(BakedIndirectLightGizmoPlugin))); + add_gizmo_plugin(Ref(memnew(CollisionShapeNode3DGizmoPlugin))); + add_gizmo_plugin(Ref(memnew(CollisionPolygonNode3DGizmoPlugin))); + add_gizmo_plugin(Ref(memnew(NavigationMeshNode3DGizmoPlugin))); + add_gizmo_plugin(Ref(memnew(JointNode3DGizmoPlugin))); + add_gizmo_plugin(Ref(memnew(PhysicalBoneNode3DGizmoPlugin))); +} + +void Node3DEditor::_bind_methods() { + + ClassDB::bind_method("_unhandled_key_input", &Node3DEditor::_unhandled_key_input); + ClassDB::bind_method("_get_editor_data", &Node3DEditor::_get_editor_data); + ClassDB::bind_method("_request_gizmo", &Node3DEditor::_request_gizmo); + + ADD_SIGNAL(MethodInfo("transform_key_request")); + ADD_SIGNAL(MethodInfo("item_lock_status_changed")); + ADD_SIGNAL(MethodInfo("item_group_status_changed")); +} + +void Node3DEditor::clear() { + + settings_fov->set_value(EDITOR_DEF("editors/3d/default_fov", 70.0)); + settings_znear->set_value(EDITOR_DEF("editors/3d/default_z_near", 0.05)); + settings_zfar->set_value(EDITOR_DEF("editors/3d/default_z_far", 1500.0)); + + for (uint32_t i = 0; i < VIEWPORTS_COUNT; i++) { + viewports[i]->reset(); + } + + VisualServer::get_singleton()->instance_set_visible(origin_instance, true); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_ORIGIN), true); + for (int i = 0; i < 3; ++i) { + if (grid_enable[i]) { + VisualServer::get_singleton()->instance_set_visible(grid_instance[i], true); + grid_visible[i] = true; + } + } + + for (uint32_t i = 0; i < VIEWPORTS_COUNT; i++) { + + viewports[i]->view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(Node3DEditorViewport::VIEW_AUDIO_LISTENER), i == 0); + viewports[i]->viewport->set_as_audio_listener(i == 0); + } + + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_GRID), true); +} + +Node3DEditor::Node3DEditor(EditorNode *p_editor) { + + gizmo.visible = true; + gizmo.scale = 1.0; + + viewport_environment = Ref(memnew(Environment)); + undo_redo = p_editor->get_undo_redo(); + VBoxContainer *vbc = this; + + custom_camera = NULL; + singleton = this; + editor = p_editor; + editor_selection = editor->get_editor_selection(); + editor_selection->add_editor_plugin(this); + + snap_enabled = false; + snap_key_enabled = false; + tool_mode = TOOL_MODE_SELECT; + + camera_override_viewport_id = 0; + + hbc_menu = memnew(HBoxContainer); + vbc->add_child(hbc_menu); + + Vector button_binds; + button_binds.resize(1); + String sct; + + tool_button[TOOL_MODE_SELECT] = memnew(ToolButton); + hbc_menu->add_child(tool_button[TOOL_MODE_SELECT]); + tool_button[TOOL_MODE_SELECT]->set_toggle_mode(true); + tool_button[TOOL_MODE_SELECT]->set_flat(true); + tool_button[TOOL_MODE_SELECT]->set_pressed(true); + button_binds.write[0] = MENU_TOOL_SELECT; + tool_button[TOOL_MODE_SELECT]->connect("pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed), button_binds); + tool_button[TOOL_MODE_SELECT]->set_shortcut(ED_SHORTCUT("spatial_editor/tool_select", TTR("Select Mode"), KEY_Q)); + tool_button[TOOL_MODE_SELECT]->set_tooltip(keycode_get_string(KEY_MASK_CMD) + TTR("Drag: Rotate\nAlt+Drag: Move\nAlt+RMB: Depth list selection")); + + hbc_menu->add_child(memnew(VSeparator)); + + tool_button[TOOL_MODE_MOVE] = memnew(ToolButton); + hbc_menu->add_child(tool_button[TOOL_MODE_MOVE]); + tool_button[TOOL_MODE_MOVE]->set_toggle_mode(true); + tool_button[TOOL_MODE_MOVE]->set_flat(true); + button_binds.write[0] = MENU_TOOL_MOVE; + tool_button[TOOL_MODE_MOVE]->connect("pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed), button_binds); + tool_button[TOOL_MODE_MOVE]->set_shortcut(ED_SHORTCUT("spatial_editor/tool_move", TTR("Move Mode"), KEY_W)); + + tool_button[TOOL_MODE_ROTATE] = memnew(ToolButton); + hbc_menu->add_child(tool_button[TOOL_MODE_ROTATE]); + tool_button[TOOL_MODE_ROTATE]->set_toggle_mode(true); + tool_button[TOOL_MODE_ROTATE]->set_flat(true); + button_binds.write[0] = MENU_TOOL_ROTATE; + tool_button[TOOL_MODE_ROTATE]->connect("pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed), button_binds); + tool_button[TOOL_MODE_ROTATE]->set_shortcut(ED_SHORTCUT("spatial_editor/tool_rotate", TTR("Rotate Mode"), KEY_E)); + + tool_button[TOOL_MODE_SCALE] = memnew(ToolButton); + hbc_menu->add_child(tool_button[TOOL_MODE_SCALE]); + tool_button[TOOL_MODE_SCALE]->set_toggle_mode(true); + tool_button[TOOL_MODE_SCALE]->set_flat(true); + button_binds.write[0] = MENU_TOOL_SCALE; + tool_button[TOOL_MODE_SCALE]->connect("pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed), button_binds); + tool_button[TOOL_MODE_SCALE]->set_shortcut(ED_SHORTCUT("spatial_editor/tool_scale", TTR("Scale Mode"), KEY_R)); + + hbc_menu->add_child(memnew(VSeparator)); + + tool_button[TOOL_MODE_LIST_SELECT] = memnew(ToolButton); + hbc_menu->add_child(tool_button[TOOL_MODE_LIST_SELECT]); + tool_button[TOOL_MODE_LIST_SELECT]->set_toggle_mode(true); + tool_button[TOOL_MODE_LIST_SELECT]->set_flat(true); + button_binds.write[0] = MENU_TOOL_LIST_SELECT; + tool_button[TOOL_MODE_LIST_SELECT]->connect("pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed), button_binds); + tool_button[TOOL_MODE_LIST_SELECT]->set_tooltip(TTR("Show a list of all objects at the position clicked\n(same as Alt+RMB in select mode).")); + + tool_button[TOOL_LOCK_SELECTED] = memnew(ToolButton); + hbc_menu->add_child(tool_button[TOOL_LOCK_SELECTED]); + button_binds.write[0] = MENU_LOCK_SELECTED; + tool_button[TOOL_LOCK_SELECTED]->connect("pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed), button_binds); + tool_button[TOOL_LOCK_SELECTED]->set_tooltip(TTR("Lock the selected object in place (can't be moved).")); + + tool_button[TOOL_UNLOCK_SELECTED] = memnew(ToolButton); + hbc_menu->add_child(tool_button[TOOL_UNLOCK_SELECTED]); + button_binds.write[0] = MENU_UNLOCK_SELECTED; + tool_button[TOOL_UNLOCK_SELECTED]->connect("pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed), button_binds); + tool_button[TOOL_UNLOCK_SELECTED]->set_tooltip(TTR("Unlock the selected object (can be moved).")); + + tool_button[TOOL_GROUP_SELECTED] = memnew(ToolButton); + hbc_menu->add_child(tool_button[TOOL_GROUP_SELECTED]); + button_binds.write[0] = MENU_GROUP_SELECTED; + tool_button[TOOL_GROUP_SELECTED]->connect("pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed), button_binds); + tool_button[TOOL_GROUP_SELECTED]->set_tooltip(TTR("Makes sure the object's children are not selectable.")); + + tool_button[TOOL_UNGROUP_SELECTED] = memnew(ToolButton); + hbc_menu->add_child(tool_button[TOOL_UNGROUP_SELECTED]); + button_binds.write[0] = MENU_UNGROUP_SELECTED; + tool_button[TOOL_UNGROUP_SELECTED]->connect("pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed), button_binds); + tool_button[TOOL_UNGROUP_SELECTED]->set_tooltip(TTR("Restores the object's children's ability to be selected.")); + + hbc_menu->add_child(memnew(VSeparator)); + + tool_option_button[TOOL_OPT_LOCAL_COORDS] = memnew(ToolButton); + hbc_menu->add_child(tool_option_button[TOOL_OPT_LOCAL_COORDS]); + tool_option_button[TOOL_OPT_LOCAL_COORDS]->set_toggle_mode(true); + tool_option_button[TOOL_OPT_LOCAL_COORDS]->set_flat(true); + button_binds.write[0] = MENU_TOOL_LOCAL_COORDS; + tool_option_button[TOOL_OPT_LOCAL_COORDS]->connect("toggled", callable_mp(this, &Node3DEditor::_menu_item_toggled), button_binds); + tool_option_button[TOOL_OPT_LOCAL_COORDS]->set_shortcut(ED_SHORTCUT("spatial_editor/local_coords", TTR("Use Local Space"), KEY_T)); + + tool_option_button[TOOL_OPT_USE_SNAP] = memnew(ToolButton); + hbc_menu->add_child(tool_option_button[TOOL_OPT_USE_SNAP]); + tool_option_button[TOOL_OPT_USE_SNAP]->set_toggle_mode(true); + tool_option_button[TOOL_OPT_USE_SNAP]->set_flat(true); + button_binds.write[0] = MENU_TOOL_USE_SNAP; + tool_option_button[TOOL_OPT_USE_SNAP]->connect("toggled", callable_mp(this, &Node3DEditor::_menu_item_toggled), button_binds); + tool_option_button[TOOL_OPT_USE_SNAP]->set_shortcut(ED_SHORTCUT("spatial_editor/snap", TTR("Use Snap"), KEY_Y)); + + hbc_menu->add_child(memnew(VSeparator)); + + tool_option_button[TOOL_OPT_OVERRIDE_CAMERA] = memnew(ToolButton); + hbc_menu->add_child(tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]); + tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->set_toggle_mode(true); + tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->set_flat(true); + tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->set_disabled(true); + button_binds.write[0] = MENU_TOOL_OVERRIDE_CAMERA; + tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->connect("toggled", callable_mp(this, &Node3DEditor::_menu_item_toggled), button_binds); + _update_camera_override_button(false); + + hbc_menu->add_child(memnew(VSeparator)); + + // Drag and drop support; + preview_node = memnew(Node3D); + preview_bounds = AABB(); + + ED_SHORTCUT("spatial_editor/bottom_view", TTR("Bottom View"), KEY_MASK_ALT + KEY_KP_7); + ED_SHORTCUT("spatial_editor/top_view", TTR("Top View"), KEY_KP_7); + ED_SHORTCUT("spatial_editor/rear_view", TTR("Rear View"), KEY_MASK_ALT + KEY_KP_1); + ED_SHORTCUT("spatial_editor/front_view", TTR("Front View"), KEY_KP_1); + ED_SHORTCUT("spatial_editor/left_view", TTR("Left View"), KEY_MASK_ALT + KEY_KP_3); + ED_SHORTCUT("spatial_editor/right_view", TTR("Right View"), KEY_KP_3); + ED_SHORTCUT("spatial_editor/switch_perspective_orthogonal", TTR("Switch Perspective/Orthogonal View"), KEY_KP_5); + ED_SHORTCUT("spatial_editor/insert_anim_key", TTR("Insert Animation Key"), KEY_K); + ED_SHORTCUT("spatial_editor/focus_origin", TTR("Focus Origin"), KEY_O); + ED_SHORTCUT("spatial_editor/focus_selection", TTR("Focus Selection"), KEY_F); + ED_SHORTCUT("spatial_editor/align_transform_with_view", TTR("Align Transform with View"), KEY_MASK_ALT + KEY_MASK_CMD + KEY_M); + ED_SHORTCUT("spatial_editor/align_rotation_with_view", TTR("Align Rotation with View"), KEY_MASK_ALT + KEY_MASK_CMD + KEY_F); + ED_SHORTCUT("spatial_editor/freelook_toggle", TTR("Toggle Freelook"), KEY_MASK_SHIFT + KEY_F); + + PopupMenu *p; + + transform_menu = memnew(MenuButton); + transform_menu->set_text(TTR("Transform")); + transform_menu->set_switch_on_hover(true); + hbc_menu->add_child(transform_menu); + + p = transform_menu->get_popup(); + p->add_shortcut(ED_SHORTCUT("spatial_editor/snap_to_floor", TTR("Snap Object to Floor"), KEY_PAGEDOWN), MENU_SNAP_TO_FLOOR); + p->add_shortcut(ED_SHORTCUT("spatial_editor/transform_dialog", TTR("Transform Dialog...")), MENU_TRANSFORM_DIALOG); + + p->add_separator(); + p->add_shortcut(ED_SHORTCUT("spatial_editor/configure_snap", TTR("Configure Snap...")), MENU_TRANSFORM_CONFIGURE_SNAP); + + p->connect("id_pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed)); + + view_menu = memnew(MenuButton); + view_menu->set_text(TTR("View")); + view_menu->set_switch_on_hover(true); + hbc_menu->add_child(view_menu); + + p = view_menu->get_popup(); + + accept = memnew(AcceptDialog); + editor->get_gui_base()->add_child(accept); + + p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/1_viewport", TTR("1 Viewport"), KEY_MASK_CMD + KEY_1), MENU_VIEW_USE_1_VIEWPORT); + p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/2_viewports", TTR("2 Viewports"), KEY_MASK_CMD + KEY_2), MENU_VIEW_USE_2_VIEWPORTS); + p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/2_viewports_alt", TTR("2 Viewports (Alt)"), KEY_MASK_ALT + KEY_MASK_CMD + KEY_2), MENU_VIEW_USE_2_VIEWPORTS_ALT); + p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/3_viewports", TTR("3 Viewports"), KEY_MASK_CMD + KEY_3), MENU_VIEW_USE_3_VIEWPORTS); + p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/3_viewports_alt", TTR("3 Viewports (Alt)"), KEY_MASK_ALT + KEY_MASK_CMD + KEY_3), MENU_VIEW_USE_3_VIEWPORTS_ALT); + p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/4_viewports", TTR("4 Viewports"), KEY_MASK_CMD + KEY_4), MENU_VIEW_USE_4_VIEWPORTS); + p->add_separator(); + + p->add_submenu_item(TTR("Gizmos"), "GizmosMenu"); + + p->add_separator(); + p->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_origin", TTR("View Origin")), MENU_VIEW_ORIGIN); + p->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_grid", TTR("View Grid")), MENU_VIEW_GRID); + + p->add_separator(); + p->add_shortcut(ED_SHORTCUT("spatial_editor/settings", TTR("Settings...")), MENU_VIEW_CAMERA_SETTINGS); + + p->set_item_checked(p->get_item_index(MENU_VIEW_ORIGIN), true); + p->set_item_checked(p->get_item_index(MENU_VIEW_GRID), true); + + p->connect("id_pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed)); + + gizmos_menu = memnew(PopupMenu); + p->add_child(gizmos_menu); + gizmos_menu->set_name("GizmosMenu"); + gizmos_menu->set_hide_on_checkable_item_selection(false); + gizmos_menu->connect("id_pressed", callable_mp(this, &Node3DEditor::_menu_gizmo_toggled)); + + /* REST OF MENU */ + + palette_split = memnew(HSplitContainer); + palette_split->set_v_size_flags(SIZE_EXPAND_FILL); + vbc->add_child(palette_split); + + shader_split = memnew(VSplitContainer); + shader_split->set_h_size_flags(SIZE_EXPAND_FILL); + palette_split->add_child(shader_split); + viewport_base = memnew(Node3DEditorViewportContainer); + shader_split->add_child(viewport_base); + viewport_base->set_v_size_flags(SIZE_EXPAND_FILL); + for (uint32_t i = 0; i < VIEWPORTS_COUNT; i++) { + + viewports[i] = memnew(Node3DEditorViewport(this, editor, i)); + viewports[i]->connect("toggle_maximize_view", callable_mp(this, &Node3DEditor::_toggle_maximize_view)); + viewports[i]->connect("clicked", callable_mp(this, &Node3DEditor::_update_camera_override_viewport)); + viewports[i]->assign_pending_data_pointers(preview_node, &preview_bounds, accept); + viewport_base->add_child(viewports[i]); + } + + /* SNAP DIALOG */ + + snap_translate_value = 1; + snap_rotate_value = 15; + snap_scale_value = 10; + + snap_dialog = memnew(ConfirmationDialog); + snap_dialog->set_title(TTR("Snap Settings")); + add_child(snap_dialog); + snap_dialog->connect("confirmed", callable_mp(this, &Node3DEditor::_snap_changed)); + snap_dialog->get_cancel()->connect("pressed", callable_mp(this, &Node3DEditor::_snap_update)); + + VBoxContainer *snap_dialog_vbc = memnew(VBoxContainer); + snap_dialog->add_child(snap_dialog_vbc); + + snap_translate = memnew(LineEdit); + snap_dialog_vbc->add_margin_child(TTR("Translate Snap:"), snap_translate); + + snap_rotate = memnew(LineEdit); + snap_dialog_vbc->add_margin_child(TTR("Rotate Snap (deg.):"), snap_rotate); + + snap_scale = memnew(LineEdit); + snap_dialog_vbc->add_margin_child(TTR("Scale Snap (%):"), snap_scale); + + _snap_update(); + + /* SETTINGS DIALOG */ + + settings_dialog = memnew(ConfirmationDialog); + settings_dialog->set_title(TTR("Viewport Settings")); + add_child(settings_dialog); + settings_vbc = memnew(VBoxContainer); + settings_vbc->set_custom_minimum_size(Size2(200, 0) * EDSCALE); + settings_dialog->add_child(settings_vbc); + + settings_fov = memnew(SpinBox); + settings_fov->set_max(MAX_FOV); + settings_fov->set_min(MIN_FOV); + settings_fov->set_step(0.01); + settings_fov->set_value(EDITOR_DEF("editors/3d/default_fov", 70.0)); + settings_vbc->add_margin_child(TTR("Perspective FOV (deg.):"), settings_fov); + + settings_znear = memnew(SpinBox); + settings_znear->set_max(MAX_Z); + settings_znear->set_min(MIN_Z); + settings_znear->set_step(0.01); + settings_znear->set_value(EDITOR_DEF("editors/3d/default_z_near", 0.05)); + settings_vbc->add_margin_child(TTR("View Z-Near:"), settings_znear); + + settings_zfar = memnew(SpinBox); + settings_zfar->set_max(MAX_Z); + settings_zfar->set_min(MIN_Z); + settings_zfar->set_step(0.01); + settings_zfar->set_value(EDITOR_DEF("editors/3d/default_z_far", 1500)); + settings_vbc->add_margin_child(TTR("View Z-Far:"), settings_zfar); + + for (uint32_t i = 0; i < VIEWPORTS_COUNT; ++i) { + settings_dialog->connect("confirmed", callable_mp(viewports[i], &Node3DEditorViewport::_update_camera), varray(0.0)); + } + + /* XFORM DIALOG */ + + xform_dialog = memnew(ConfirmationDialog); + xform_dialog->set_title(TTR("Transform Change")); + add_child(xform_dialog); + + VBoxContainer *xform_vbc = memnew(VBoxContainer); + xform_dialog->add_child(xform_vbc); + + Label *l = memnew(Label); + l->set_text(TTR("Translate:")); + xform_vbc->add_child(l); + + HBoxContainer *xform_hbc = memnew(HBoxContainer); + xform_vbc->add_child(xform_hbc); + + for (int i = 0; i < 3; i++) { + + xform_translate[i] = memnew(LineEdit); + xform_translate[i]->set_h_size_flags(SIZE_EXPAND_FILL); + xform_hbc->add_child(xform_translate[i]); + } + + l = memnew(Label); + l->set_text(TTR("Rotate (deg.):")); + xform_vbc->add_child(l); + + xform_hbc = memnew(HBoxContainer); + xform_vbc->add_child(xform_hbc); + + for (int i = 0; i < 3; i++) { + xform_rotate[i] = memnew(LineEdit); + xform_rotate[i]->set_h_size_flags(SIZE_EXPAND_FILL); + xform_hbc->add_child(xform_rotate[i]); + } + + l = memnew(Label); + l->set_text(TTR("Scale (ratio):")); + xform_vbc->add_child(l); + + xform_hbc = memnew(HBoxContainer); + xform_vbc->add_child(xform_hbc); + + for (int i = 0; i < 3; i++) { + xform_scale[i] = memnew(LineEdit); + xform_scale[i]->set_h_size_flags(SIZE_EXPAND_FILL); + xform_hbc->add_child(xform_scale[i]); + } + + l = memnew(Label); + l->set_text(TTR("Transform Type")); + xform_vbc->add_child(l); + + xform_type = memnew(OptionButton); + xform_type->set_h_size_flags(SIZE_EXPAND_FILL); + xform_type->add_item(TTR("Pre")); + xform_type->add_item(TTR("Post")); + xform_vbc->add_child(xform_type); + + xform_dialog->connect("confirmed", callable_mp(this, &Node3DEditor::_xform_dialog_action)); + + scenario_debug = VisualServer::SCENARIO_DEBUG_DISABLED; + + selected = NULL; + + set_process_unhandled_key_input(true); + add_to_group("_spatial_editor_group"); + + EDITOR_DEF("editors/3d/manipulator_gizmo_size", 80); + EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "editors/3d/manipulator_gizmo_size", PROPERTY_HINT_RANGE, "16,1024,1")); + EDITOR_DEF("editors/3d/manipulator_gizmo_opacity", 0.4); + EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::FLOAT, "editors/3d/manipulator_gizmo_opacity", PROPERTY_HINT_RANGE, "0,1,0.01")); + EDITOR_DEF("editors/3d/navigation/show_viewport_rotation_gizmo", true); + + over_gizmo_handle = -1; +} + +Node3DEditor::~Node3DEditor() { + memdelete(preview_node); +} + +void Node3DEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + + spatial_editor->show(); + spatial_editor->set_process(true); + + } else { + + spatial_editor->hide(); + spatial_editor->set_process(false); + } +} +void Node3DEditorPlugin::edit(Object *p_object) { + + spatial_editor->edit(Object::cast_to(p_object)); +} + +bool Node3DEditorPlugin::handles(Object *p_object) const { + + return p_object->is_class("Node3D"); +} + +Dictionary Node3DEditorPlugin::get_state() const { + return spatial_editor->get_state(); +} + +void Node3DEditorPlugin::set_state(const Dictionary &p_state) { + + spatial_editor->set_state(p_state); +} + +void Node3DEditor::snap_cursor_to_plane(const Plane &p_plane) { + + //cursor.pos=p_plane.project(cursor.pos); +} + +Vector3 Node3DEditor::snap_point(Vector3 p_target, Vector3 p_start) const { + if (is_snap_enabled()) { + p_target.x = Math::snap_scalar(0.0, get_translate_snap(), p_target.x); + p_target.y = Math::snap_scalar(0.0, get_translate_snap(), p_target.y); + p_target.z = Math::snap_scalar(0.0, get_translate_snap(), p_target.z); + } + return p_target; +} + +float Node3DEditor::get_translate_snap() const { + float snap_value; + if (InputFilter::get_singleton()->is_key_pressed(KEY_SHIFT)) { + snap_value = snap_translate->get_text().to_double() / 10.0; + } else { + snap_value = snap_translate->get_text().to_double(); + } + + return snap_value; +} + +float Node3DEditor::get_rotate_snap() const { + float snap_value; + if (InputFilter::get_singleton()->is_key_pressed(KEY_SHIFT)) { + snap_value = snap_rotate->get_text().to_double() / 3.0; + } else { + snap_value = snap_rotate->get_text().to_double(); + } + + return snap_value; +} + +float Node3DEditor::get_scale_snap() const { + float snap_value; + if (InputFilter::get_singleton()->is_key_pressed(KEY_SHIFT)) { + snap_value = snap_scale->get_text().to_double() / 2.0; + } else { + snap_value = snap_scale->get_text().to_double(); + } + + return snap_value; +} + +void Node3DEditorPlugin::_bind_methods() { + + ClassDB::bind_method("snap_cursor_to_plane", &Node3DEditorPlugin::snap_cursor_to_plane); +} + +void Node3DEditorPlugin::snap_cursor_to_plane(const Plane &p_plane) { + + spatial_editor->snap_cursor_to_plane(p_plane); +} + +struct _GizmoPluginPriorityComparator { + + bool operator()(const Ref &p_a, const Ref &p_b) const { + if (p_a->get_priority() == p_b->get_priority()) { + return p_a->get_name() < p_b->get_name(); + } + return p_a->get_priority() > p_b->get_priority(); + } +}; + +struct _GizmoPluginNameComparator { + + bool operator()(const Ref &p_a, const Ref &p_b) const { + return p_a->get_name() < p_b->get_name(); + } +}; + +void Node3DEditor::add_gizmo_plugin(Ref p_plugin) { + ERR_FAIL_NULL(p_plugin.ptr()); + + gizmo_plugins_by_priority.push_back(p_plugin); + gizmo_plugins_by_priority.sort_custom<_GizmoPluginPriorityComparator>(); + + gizmo_plugins_by_name.push_back(p_plugin); + gizmo_plugins_by_name.sort_custom<_GizmoPluginNameComparator>(); + + _update_gizmos_menu(); + Node3DEditor::get_singleton()->update_all_gizmos(); +} + +void Node3DEditor::remove_gizmo_plugin(Ref p_plugin) { + gizmo_plugins_by_priority.erase(p_plugin); + gizmo_plugins_by_name.erase(p_plugin); + _update_gizmos_menu(); +} + +Node3DEditorPlugin::Node3DEditorPlugin(EditorNode *p_node) { + + editor = p_node; + spatial_editor = memnew(Node3DEditor(p_node)); + spatial_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL); + editor->get_viewport()->add_child(spatial_editor); + + spatial_editor->hide(); + spatial_editor->connect_compat("transform_key_request", editor->get_inspector_dock(), "_transform_keyed"); +} + +Node3DEditorPlugin::~Node3DEditorPlugin() { +} + +void EditorNode3DGizmoPlugin::create_material(const String &p_name, const Color &p_color, bool p_billboard, bool p_on_top, bool p_use_vertex_color) { + + Color instanced_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/instanced", Color(0.7, 0.7, 0.7, 0.6)); + + Vector> mats; + + for (int i = 0; i < 4; i++) { + bool selected = i % 2 == 1; + bool instanced = i < 2; + + Ref material = Ref(memnew(StandardMaterial3D)); + + Color color = instanced ? instanced_color : p_color; + + if (!selected) { + color.a *= 0.3; + } + + material->set_albedo(color); + material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); + material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); + material->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MIN + 1); + + if (p_use_vertex_color) { + material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); + material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); + } + + if (p_billboard) { + material->set_billboard_mode(StandardMaterial3D::BILLBOARD_ENABLED); + } + + if (p_on_top && selected) { + material->set_on_top_of_alpha(); + } + + mats.push_back(material); + } + + materials[p_name] = mats; +} + +void EditorNode3DGizmoPlugin::create_icon_material(const String &p_name, const Ref &p_texture, bool p_on_top, const Color &p_albedo) { + + Color instanced_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/instanced", Color(0.7, 0.7, 0.7, 0.6)); + + Vector> icons; + + for (int i = 0; i < 4; i++) { + bool selected = i % 2 == 1; + bool instanced = i < 2; + + Ref icon = Ref(memnew(StandardMaterial3D)); + + Color color = instanced ? instanced_color : p_albedo; + + if (!selected) { + color.a *= 0.85; + } + + icon->set_albedo(color); + + icon->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); + icon->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); + icon->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); + icon->set_cull_mode(StandardMaterial3D::CULL_DISABLED); + icon->set_depth_draw_mode(StandardMaterial3D::DEPTH_DRAW_DISABLED); + icon->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); + icon->set_texture(StandardMaterial3D::TEXTURE_ALBEDO, p_texture); + icon->set_flag(StandardMaterial3D::FLAG_FIXED_SIZE, true); + icon->set_billboard_mode(StandardMaterial3D::BILLBOARD_ENABLED); + icon->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MIN); + + if (p_on_top && selected) { + icon->set_on_top_of_alpha(); + } + + icons.push_back(icon); + } + + materials[p_name] = icons; +} + +void EditorNode3DGizmoPlugin::create_handle_material(const String &p_name, bool p_billboard) { + Ref handle_material = Ref(memnew(StandardMaterial3D)); + + handle_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); + handle_material->set_flag(StandardMaterial3D::FLAG_USE_POINT_SIZE, true); + Ref handle_t = Node3DEditor::get_singleton()->get_theme_icon("Editor3DHandle", "EditorIcons"); + handle_material->set_point_size(handle_t->get_width()); + handle_material->set_texture(StandardMaterial3D::TEXTURE_ALBEDO, handle_t); + handle_material->set_albedo(Color(1, 1, 1)); + handle_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); + handle_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); + handle_material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); + handle_material->set_on_top_of_alpha(); + if (p_billboard) { + handle_material->set_billboard_mode(StandardMaterial3D::BILLBOARD_ENABLED); + handle_material->set_on_top_of_alpha(); + } + + materials[p_name] = Vector>(); + materials[p_name].push_back(handle_material); +} + +void EditorNode3DGizmoPlugin::add_material(const String &p_name, Ref p_material) { + materials[p_name] = Vector>(); + materials[p_name].push_back(p_material); +} + +Ref EditorNode3DGizmoPlugin::get_material(const String &p_name, const Ref &p_gizmo) { + ERR_FAIL_COND_V(!materials.has(p_name), Ref()); + ERR_FAIL_COND_V(materials[p_name].size() == 0, Ref()); + + if (p_gizmo.is_null() || materials[p_name].size() == 1) return materials[p_name][0]; + + int index = (p_gizmo->is_selected() ? 1 : 0) + (p_gizmo->is_editable() ? 2 : 0); + + Ref mat = materials[p_name][index]; + + if (current_state == ON_TOP && p_gizmo->is_selected()) { + mat->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true); + } else { + mat->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, false); + } + + return mat; +} + +String EditorNode3DGizmoPlugin::get_name() const { + if (get_script_instance() && get_script_instance()->has_method("get_name")) { + return get_script_instance()->call("get_name"); + } + return TTR("Nameless gizmo"); +} + +int EditorNode3DGizmoPlugin::get_priority() const { + if (get_script_instance() && get_script_instance()->has_method("get_priority")) { + return get_script_instance()->call("get_priority"); + } + return 0; +} + +Ref EditorNode3DGizmoPlugin::get_gizmo(Node3D *p_spatial) { + + if (get_script_instance() && get_script_instance()->has_method("get_gizmo")) { + return get_script_instance()->call("get_gizmo", p_spatial); + } + + Ref ref = create_gizmo(p_spatial); + + if (ref.is_null()) return ref; + + ref->set_plugin(this); + ref->set_spatial_node(p_spatial); + ref->set_hidden(current_state == HIDDEN); + + current_gizmos.push_back(ref.ptr()); + return ref; +} + +void EditorNode3DGizmoPlugin::_bind_methods() { +#define GIZMO_REF PropertyInfo(Variant::OBJECT, "gizmo", PROPERTY_HINT_RESOURCE_TYPE, "EditorNode3DGizmo") + + BIND_VMETHOD(MethodInfo(Variant::BOOL, "has_gizmo", PropertyInfo(Variant::OBJECT, "spatial", PROPERTY_HINT_RESOURCE_TYPE, "Node3D"))); + BIND_VMETHOD(MethodInfo(GIZMO_REF, "create_gizmo", PropertyInfo(Variant::OBJECT, "spatial", PROPERTY_HINT_RESOURCE_TYPE, "Node3D"))); + + ClassDB::bind_method(D_METHOD("create_material", "name", "color", "billboard", "on_top", "use_vertex_color"), &EditorNode3DGizmoPlugin::create_material, DEFVAL(false), DEFVAL(false), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("create_icon_material", "name", "texture", "on_top", "color"), &EditorNode3DGizmoPlugin::create_icon_material, DEFVAL(false), DEFVAL(Color(1, 1, 1, 1))); + ClassDB::bind_method(D_METHOD("create_handle_material", "name", "billboard"), &EditorNode3DGizmoPlugin::create_handle_material, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("add_material", "name", "material"), &EditorNode3DGizmoPlugin::add_material); + + ClassDB::bind_method(D_METHOD("get_material", "name", "gizmo"), &EditorNode3DGizmoPlugin::get_material); //, DEFVAL(Ref())); + + BIND_VMETHOD(MethodInfo(Variant::STRING, "get_name")); + BIND_VMETHOD(MethodInfo(Variant::STRING, "get_priority")); + BIND_VMETHOD(MethodInfo(Variant::BOOL, "can_be_hidden")); + BIND_VMETHOD(MethodInfo(Variant::BOOL, "is_selectable_when_hidden")); + + BIND_VMETHOD(MethodInfo("redraw", GIZMO_REF)); + BIND_VMETHOD(MethodInfo(Variant::STRING, "get_handle_name", GIZMO_REF, PropertyInfo(Variant::INT, "index"))); + + MethodInfo hvget(Variant::NIL, "get_handle_value", GIZMO_REF, PropertyInfo(Variant::INT, "index")); + hvget.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; + BIND_VMETHOD(hvget); + + BIND_VMETHOD(MethodInfo("set_handle", GIZMO_REF, PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Camera3D"), PropertyInfo(Variant::VECTOR2, "point"))); + MethodInfo cm = MethodInfo("commit_handle", GIZMO_REF, PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::NIL, "restore"), PropertyInfo(Variant::BOOL, "cancel")); + cm.default_arguments.push_back(false); + BIND_VMETHOD(cm); + + BIND_VMETHOD(MethodInfo(Variant::BOOL, "is_handle_highlighted", GIZMO_REF, PropertyInfo(Variant::INT, "index"))); + +#undef GIZMO_REF +} + +bool EditorNode3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + if (get_script_instance() && get_script_instance()->has_method("has_gizmo")) { + return get_script_instance()->call("has_gizmo", p_spatial); + } + return false; +} + +Ref EditorNode3DGizmoPlugin::create_gizmo(Node3D *p_spatial) { + + if (get_script_instance() && get_script_instance()->has_method("create_gizmo")) { + return get_script_instance()->call("create_gizmo", p_spatial); + } + + Ref ref; + if (has_gizmo(p_spatial)) ref.instance(); + return ref; +} + +bool EditorNode3DGizmoPlugin::can_be_hidden() const { + if (get_script_instance() && get_script_instance()->has_method("can_be_hidden")) { + return get_script_instance()->call("can_be_hidden"); + } + return true; +} + +bool EditorNode3DGizmoPlugin::is_selectable_when_hidden() const { + if (get_script_instance() && get_script_instance()->has_method("is_selectable_when_hidden")) { + return get_script_instance()->call("is_selectable_when_hidden"); + } + return false; +} + +void EditorNode3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + if (get_script_instance() && get_script_instance()->has_method("redraw")) { + Ref ref(p_gizmo); + get_script_instance()->call("redraw", ref); + } +} + +String EditorNode3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { + if (get_script_instance() && get_script_instance()->has_method("get_handle_name")) { + return get_script_instance()->call("get_handle_name", p_gizmo, p_idx); + } + return ""; +} + +Variant EditorNode3DGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { + if (get_script_instance() && get_script_instance()->has_method("get_handle_value")) { + return get_script_instance()->call("get_handle_value", p_gizmo, p_idx); + } + return Variant(); +} + +void EditorNode3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) { + if (get_script_instance() && get_script_instance()->has_method("set_handle")) { + get_script_instance()->call("set_handle", p_gizmo, p_idx, p_camera, p_point); + } +} + +void EditorNode3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { + if (get_script_instance() && get_script_instance()->has_method("commit_handle")) { + get_script_instance()->call("commit_handle", p_gizmo, p_idx, p_restore, p_cancel); + } +} + +bool EditorNode3DGizmoPlugin::is_handle_highlighted(const EditorNode3DGizmo *p_gizmo, int p_idx) const { + if (get_script_instance() && get_script_instance()->has_method("is_handle_highlighted")) { + return get_script_instance()->call("is_handle_highlighted", p_gizmo, p_idx); + } + return false; +} + +void EditorNode3DGizmoPlugin::set_state(int p_state) { + current_state = p_state; + for (int i = 0; i < current_gizmos.size(); ++i) { + current_gizmos[i]->set_hidden(current_state == HIDDEN); + } +} + +int EditorNode3DGizmoPlugin::get_state() const { + return current_state; +} + +void EditorNode3DGizmoPlugin::unregister_gizmo(EditorNode3DGizmo *p_gizmo) { + current_gizmos.erase(p_gizmo); +} + +EditorNode3DGizmoPlugin::EditorNode3DGizmoPlugin() { + current_state = VISIBLE; +} + +EditorNode3DGizmoPlugin::~EditorNode3DGizmoPlugin() { + for (int i = 0; i < current_gizmos.size(); ++i) { + current_gizmos[i]->set_plugin(NULL); + current_gizmos[i]->get_spatial_node()->set_gizmo(NULL); + } + if (Node3DEditor::get_singleton()) { + Node3DEditor::get_singleton()->update_all_gizmos(); + } +} diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h new file mode 100644 index 0000000000..af6bc43b80 --- /dev/null +++ b/editor/plugins/node_3d_editor_plugin.h @@ -0,0 +1,883 @@ +/*************************************************************************/ +/* node_3d_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SPATIAL_EDITOR_PLUGIN_H +#define SPATIAL_EDITOR_PLUGIN_H + +#include "editor/editor_node.h" +#include "editor/editor_plugin.h" +#include "editor/editor_scale.h" +#include "scene/3d/immediate_geometry_3d.h" +#include "scene/3d/light_3d.h" +#include "scene/3d/visual_instance_3d.h" +#include "scene/gui/panel_container.h" + +class Camera3D; +class Node3DEditor; +class EditorNode3DGizmoPlugin; +class Node3DEditorViewport; +class ViewportContainer; + +class EditorNode3DGizmo : public Node3DGizmo { + + GDCLASS(EditorNode3DGizmo, Node3DGizmo); + + bool selected; + bool instanced; + +public: + void set_selected(bool p_selected) { selected = p_selected; } + bool is_selected() const { return selected; } + + struct Instance { + + RID instance; + Ref mesh; + Ref material; + Ref skin_reference; + RID skeleton; + bool billboard; + bool unscaled; + bool can_intersect; + bool extra_margin; + Instance() { + + billboard = false; + unscaled = false; + can_intersect = false; + extra_margin = false; + } + + void create_instance(Node3D *p_base, bool p_hidden = false); + }; + + Vector collision_segments; + Ref collision_mesh; + + struct Handle { + Vector3 pos; + bool billboard; + }; + + Vector handles; + Vector secondary_handles; + float selectable_icon_size; + bool billboard_handle; + + bool valid; + bool hidden; + Node3D *base; + Vector instances; + Node3D *spatial_node; + EditorNode3DGizmoPlugin *gizmo_plugin; + + void _set_spatial_node(Node *p_node) { set_spatial_node(Object::cast_to(p_node)); } + +protected: + static void _bind_methods(); + +public: + void add_lines(const Vector &p_lines, const Ref &p_material, bool p_billboard = false, const Color &p_modulate = Color(1, 1, 1)); + void add_mesh(const Ref &p_mesh, bool p_billboard = false, const Ref &p_skin_reference = Ref(), const Ref &p_material = Ref()); + void add_collision_segments(const Vector &p_lines); + void add_collision_triangles(const Ref &p_tmesh); + void add_unscaled_billboard(const Ref &p_material, float p_scale = 1, const Color &p_modulate = Color(1, 1, 1)); + void add_handles(const Vector &p_handles, const Ref &p_material, bool p_billboard = false, bool p_secondary = false); + void add_solid_box(Ref &p_material, Vector3 p_size, Vector3 p_position = Vector3()); + + virtual bool is_handle_highlighted(int p_idx) const; + virtual String get_handle_name(int p_idx) const; + virtual Variant get_handle_value(int p_idx); + virtual void set_handle(int p_idx, Camera3D *p_camera, const Point2 &p_point); + virtual void commit_handle(int p_idx, const Variant &p_restore, bool p_cancel = false); + + void set_spatial_node(Node3D *p_node); + Node3D *get_spatial_node() const { return spatial_node; } + Ref get_plugin() const { return gizmo_plugin; } + Vector3 get_handle_pos(int p_idx) const; + bool intersect_frustum(const Camera3D *p_camera, const Vector &p_frustum); + bool intersect_ray(Camera3D *p_camera, const Point2 &p_point, Vector3 &r_pos, Vector3 &r_normal, int *r_gizmo_handle = NULL, bool p_sec_first = false); + + virtual void clear(); + virtual void create(); + virtual void transform(); + virtual void redraw(); + virtual void free(); + + virtual bool is_editable() const; + + void set_hidden(bool p_hidden); + void set_plugin(EditorNode3DGizmoPlugin *p_plugin); + + EditorNode3DGizmo(); + ~EditorNode3DGizmo(); +}; + +class ViewportRotationControl : public Control { + GDCLASS(ViewportRotationControl, Control); + + struct Axis2D { + Vector2i screen_point; + float z_axis = -99.0; + int axis = -1; + }; + + struct Axis2DCompare { + _FORCE_INLINE_ bool operator()(const Axis2D &l, const Axis2D &r) const { + return l.z_axis < r.z_axis; + } + }; + + Node3DEditorViewport *viewport = nullptr; + Vector axis_colors; + Vector axis_menu_options; + bool orbiting = false; + int focused_axis = -2; + + const float AXIS_CIRCLE_RADIUS = 8.0f * EDSCALE; + +protected: + static void _bind_methods(); + void _notification(int p_what); + void _gui_input(Ref p_event); + void _draw(); + void _draw_axis(const Axis2D &p_axis); + void _get_sorted_axis(Vector &r_axis); + void _update_focus(); + void _on_mouse_exited(); + +public: + void set_viewport(Node3DEditorViewport *p_viewport); +}; + +class Node3DEditorViewport : public Control { + + GDCLASS(Node3DEditorViewport, Control); + friend class Node3DEditor; + friend class ViewportRotationControl; + enum { + + VIEW_TOP, + VIEW_BOTTOM, + VIEW_LEFT, + VIEW_RIGHT, + VIEW_FRONT, + VIEW_REAR, + VIEW_CENTER_TO_ORIGIN, + VIEW_CENTER_TO_SELECTION, + VIEW_ALIGN_TRANSFORM_WITH_VIEW, + VIEW_ALIGN_ROTATION_WITH_VIEW, + VIEW_PERSPECTIVE, + VIEW_ENVIRONMENT, + VIEW_ORTHOGONAL, + VIEW_HALF_RESOLUTION, + VIEW_AUDIO_LISTENER, + VIEW_AUDIO_DOPPLER, + VIEW_GIZMOS, + VIEW_INFORMATION, + VIEW_FPS, + VIEW_DISPLAY_NORMAL, + VIEW_DISPLAY_WIREFRAME, + VIEW_DISPLAY_OVERDRAW, + VIEW_DISPLAY_SHADELESS, + VIEW_DISPLAY_LIGHTING, + VIEW_DISPLAY_ADVANCED, + VIEW_DISPLAY_NORMAL_BUFFER, + VIEW_DISPLAY_DEBUG_SHADOW_ATLAS, + VIEW_DISPLAY_DEBUG_DIRECTIONAL_SHADOW_ATLAS, + VIEW_DISPLAY_DEBUG_GIPROBE_ALBEDO, + VIEW_DISPLAY_DEBUG_GIPROBE_LIGHTING, + VIEW_DISPLAY_DEBUG_GIPROBE_EMISSION, + VIEW_DISPLAY_DEBUG_SCENE_LUMINANCE, + VIEW_DISPLAY_DEBUG_SSAO, + VIEW_DISPLAY_DEBUG_ROUGHNESS_LIMITER, + VIEW_LOCK_ROTATION, + VIEW_CINEMATIC_PREVIEW, + VIEW_AUTO_ORTHOGONAL, + VIEW_MAX + }; + +public: + enum { + GIZMO_BASE_LAYER = 27, + GIZMO_EDIT_LAYER = 26, + GIZMO_GRID_LAYER = 25 + }; + + enum NavigationScheme { + NAVIGATION_GODOT, + NAVIGATION_MAYA, + NAVIGATION_MODO, + }; + +private: + int index; + String name; + void _menu_option(int p_option); + void _set_auto_orthogonal(); + Node3D *preview_node; + AABB *preview_bounds; + Vector selected_files; + AcceptDialog *accept; + + Node *target_node; + Point2 drop_pos; + + EditorNode *editor; + EditorData *editor_data; + EditorSelection *editor_selection; + UndoRedo *undo_redo; + + CheckBox *preview_camera; + ViewportContainer *viewport_container; + + MenuButton *view_menu; + PopupMenu *display_submenu; + + Control *surface; + SubViewport *viewport; + Camera3D *camera; + bool transforming; + bool orthogonal; + bool auto_orthogonal; + bool lock_rotation; + float gizmo_scale; + + bool freelook_active; + real_t freelook_speed; + + TextureRect *crosshair; + Label *info_label; + Label *cinema_label; + Label *locked_label; + + VBoxContainer *top_right_vbox; + ViewportRotationControl *rotation_control; + Label *fps_label; + + struct _RayResult { + + Node3D *item; + float depth; + int handle; + _FORCE_INLINE_ bool operator<(const _RayResult &p_rr) const { return depth < p_rr.depth; } + }; + + void _update_name(); + void _compute_edit(const Point2 &p_point); + void _clear_selected(); + void _select_clicked(bool p_append, bool p_single, bool p_allow_locked = false); + void _select(Node *p_node, bool p_append, bool p_single); + ObjectID _select_ray(const Point2 &p_pos, bool p_append, bool &r_includes_current, int *r_gizmo_handle = NULL, bool p_alt_select = false); + void _find_items_at_pos(const Point2 &p_pos, bool &r_includes_current, Vector<_RayResult> &results, bool p_alt_select = false); + Vector3 _get_ray_pos(const Vector2 &p_pos) const; + Vector3 _get_ray(const Vector2 &p_pos) const; + Point2 _point_to_screen(const Vector3 &p_point); + Transform _get_camera_transform() const; + int get_selected_count() const; + + Vector3 _get_camera_position() const; + Vector3 _get_camera_normal() const; + Vector3 _get_screen_to_space(const Vector3 &p_vector3); + + void _select_region(); + bool _gizmo_select(const Vector2 &p_screenpos, bool p_highlight_only = false); + + void _nav_pan(Ref p_event, const Vector2 &p_relative); + void _nav_zoom(Ref p_event, const Vector2 &p_relative); + void _nav_orbit(Ref p_event, const Vector2 &p_relative); + void _nav_look(Ref p_event, const Vector2 &p_relative); + + float get_znear() const; + float get_zfar() const; + float get_fov() const; + + ObjectID clicked; + Vector<_RayResult> selection_results; + bool clicked_includes_current; + bool clicked_wants_append; + + PopupMenu *selection_menu; + + enum NavigationZoomStyle { + NAVIGATION_ZOOM_VERTICAL, + NAVIGATION_ZOOM_HORIZONTAL + }; + + enum NavigationMode { + NAVIGATION_NONE, + NAVIGATION_PAN, + NAVIGATION_ZOOM, + NAVIGATION_ORBIT, + NAVIGATION_LOOK + }; + enum TransformMode { + TRANSFORM_NONE, + TRANSFORM_ROTATE, + TRANSFORM_TRANSLATE, + TRANSFORM_SCALE + + }; + enum TransformPlane { + TRANSFORM_VIEW, + TRANSFORM_X_AXIS, + TRANSFORM_Y_AXIS, + TRANSFORM_Z_AXIS, + TRANSFORM_YZ, + TRANSFORM_XZ, + TRANSFORM_XY, + }; + + struct EditData { + TransformMode mode; + TransformPlane plane; + Transform original; + Vector3 click_ray; + Vector3 click_ray_pos; + Vector3 center; + Vector3 orig_gizmo_pos; + int edited_gizmo; + Point2 mouse_pos; + bool snap; + Ref gizmo; + int gizmo_handle; + Variant gizmo_initial_value; + Vector3 gizmo_initial_pos; + } _edit; + + struct Cursor { + + Vector3 pos; + float x_rot, y_rot, distance; + Vector3 eye_pos; // Used in freelook mode + bool region_select; + Point2 region_begin, region_end; + + Cursor() { + x_rot = y_rot = 0.5; + distance = 4; + region_select = false; + } + }; + // Viewport camera supports movement smoothing, + // so one cursor is the real cursor, while the other can be an interpolated version. + Cursor cursor; // Immediate cursor + Cursor camera_cursor; // That one may be interpolated (don't modify this one except for smoothing purposes) + + void scale_cursor_distance(real_t scale); + + void set_freelook_active(bool active_now); + void scale_freelook_speed(real_t scale); + + real_t zoom_indicator_delay; + + RID move_gizmo_instance[3], move_plane_gizmo_instance[3], rotate_gizmo_instance[3], scale_gizmo_instance[3], scale_plane_gizmo_instance[3]; + + String last_message; + String message; + float message_time; + + void set_message(String p_message, float p_time = 5); + + // + void _update_camera(float p_interp_delta); + Transform to_camera_transform(const Cursor &p_cursor) const; + void _draw(); + + void _surface_mouse_enter(); + void _surface_mouse_exit(); + void _surface_focus_enter(); + void _surface_focus_exit(); + + void _sinput(const Ref &p_event); + void _update_freelook(real_t delta); + Node3DEditor *spatial_editor; + + Camera3D *previewing; + Camera3D *preview; + + bool previewing_cinema; + bool _is_node_locked(const Node *p_node); + void _preview_exited_scene(); + void _toggle_camera_preview(bool); + void _toggle_cinema_preview(bool); + void _init_gizmo_instance(int p_idx); + void _finish_gizmo_instances(); + void _selection_result_pressed(int); + void _selection_menu_hide(); + void _list_select(Ref b); + Point2i _get_warped_mouse_motion(const Ref &p_ev_mouse_motion) const; + + Vector3 _get_instance_position(const Point2 &p_pos) const; + static AABB _calculate_spatial_bounds(const Node3D *p_parent, bool p_exclude_toplevel_transform = true); + void _create_preview(const Vector &files) const; + void _remove_preview(); + bool _cyclical_dependency_exists(const String &p_target_scene_path, Node *p_desired_node); + bool _create_instance(Node *parent, String &path, const Point2 &p_point); + void _perform_drop_data(); + + bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const; + void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from); + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + void update_surface() { surface->update(); } + void update_transform_gizmo_view(); + + void set_can_preview(Camera3D *p_preview); + void set_state(const Dictionary &p_state); + Dictionary get_state() const; + void reset(); + bool is_freelook_active() const { return freelook_active; } + + void focus_selection(); + + void assign_pending_data_pointers( + Node3D *p_preview_node, + AABB *p_preview_bounds, + AcceptDialog *p_accept); + + SubViewport *get_viewport_node() { return viewport; } + Camera3D *get_camera() { return camera; } // return the default camera object. + + Node3DEditorViewport(Node3DEditor *p_spatial_editor, EditorNode *p_editor, int p_index); +}; + +class Node3DEditorSelectedItem : public Object { + + GDCLASS(Node3DEditorSelectedItem, Object); + +public: + AABB aabb; + Transform original; // original location when moving + Transform original_local; + Transform last_xform; // last transform + bool last_xform_dirty; + Node3D *sp; + RID sbox_instance; + + Node3DEditorSelectedItem() { + sp = NULL; + last_xform_dirty = true; + } + ~Node3DEditorSelectedItem(); +}; + +class Node3DEditorViewportContainer : public Container { + + GDCLASS(Node3DEditorViewportContainer, Container); + +public: + enum View { + VIEW_USE_1_VIEWPORT, + VIEW_USE_2_VIEWPORTS, + VIEW_USE_2_VIEWPORTS_ALT, + VIEW_USE_3_VIEWPORTS, + VIEW_USE_3_VIEWPORTS_ALT, + VIEW_USE_4_VIEWPORTS, + }; + +private: + View view; + bool mouseover; + float ratio_h; + float ratio_v; + + bool hovering_v; + bool hovering_h; + + bool dragging_v; + bool dragging_h; + Vector2 drag_begin_pos; + Vector2 drag_begin_ratio; + + void _gui_input(const Ref &p_event); + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + void set_view(View p_view); + View get_view(); + + Node3DEditorViewportContainer(); +}; + +class Node3DEditor : public VBoxContainer { + + GDCLASS(Node3DEditor, VBoxContainer); + +public: + static const unsigned int VIEWPORTS_COUNT = 4; + + enum ToolMode { + + TOOL_MODE_SELECT, + TOOL_MODE_MOVE, + TOOL_MODE_ROTATE, + TOOL_MODE_SCALE, + TOOL_MODE_LIST_SELECT, + TOOL_LOCK_SELECTED, + TOOL_UNLOCK_SELECTED, + TOOL_GROUP_SELECTED, + TOOL_UNGROUP_SELECTED, + TOOL_MAX + }; + + enum ToolOptions { + + TOOL_OPT_LOCAL_COORDS, + TOOL_OPT_USE_SNAP, + TOOL_OPT_OVERRIDE_CAMERA, + TOOL_OPT_MAX + + }; + +private: + EditorNode *editor; + EditorSelection *editor_selection; + + Node3DEditorViewportContainer *viewport_base; + Node3DEditorViewport *viewports[VIEWPORTS_COUNT]; + VSplitContainer *shader_split; + HSplitContainer *palette_split; + + ///// + + ToolMode tool_mode; + bool orthogonal; + + VisualServer::ScenarioDebugMode scenario_debug; + + RID origin; + RID origin_instance; + bool origin_enabled; + RID grid[3]; + RID grid_instance[3]; + bool grid_visible[3]; //currently visible + bool grid_enable[3]; //should be always visible if true + bool grid_enabled; + + Ref move_gizmo[3], move_plane_gizmo[3], rotate_gizmo[3], scale_gizmo[3], scale_plane_gizmo[3]; + Ref gizmo_color[3]; + Ref plane_gizmo_color[3]; + Ref gizmo_color_hl[3]; + Ref plane_gizmo_color_hl[3]; + + int over_gizmo_handle; + float snap_translate_value; + float snap_rotate_value; + float snap_scale_value; + + Ref selection_box; + RID indicators; + RID indicators_instance; + RID cursor_mesh; + RID cursor_instance; + Ref indicator_mat; + Ref cursor_material; + + // Scene drag and drop support + Node3D *preview_node; + AABB preview_bounds; + + struct Gizmo { + + bool visible; + float scale; + Transform transform; + } gizmo; + + enum MenuOption { + + MENU_TOOL_SELECT, + MENU_TOOL_MOVE, + MENU_TOOL_ROTATE, + MENU_TOOL_SCALE, + MENU_TOOL_LIST_SELECT, + MENU_TOOL_LOCAL_COORDS, + MENU_TOOL_USE_SNAP, + MENU_TOOL_OVERRIDE_CAMERA, + MENU_TRANSFORM_CONFIGURE_SNAP, + MENU_TRANSFORM_DIALOG, + MENU_VIEW_USE_1_VIEWPORT, + MENU_VIEW_USE_2_VIEWPORTS, + MENU_VIEW_USE_2_VIEWPORTS_ALT, + MENU_VIEW_USE_3_VIEWPORTS, + MENU_VIEW_USE_3_VIEWPORTS_ALT, + MENU_VIEW_USE_4_VIEWPORTS, + MENU_VIEW_ORIGIN, + MENU_VIEW_GRID, + MENU_VIEW_GIZMOS_3D_ICONS, + MENU_VIEW_CAMERA_SETTINGS, + MENU_LOCK_SELECTED, + MENU_UNLOCK_SELECTED, + MENU_GROUP_SELECTED, + MENU_UNGROUP_SELECTED, + MENU_SNAP_TO_FLOOR + }; + + Button *tool_button[TOOL_MAX]; + Button *tool_option_button[TOOL_OPT_MAX]; + + MenuButton *transform_menu; + PopupMenu *gizmos_menu; + MenuButton *view_menu; + + AcceptDialog *accept; + + ConfirmationDialog *snap_dialog; + ConfirmationDialog *xform_dialog; + ConfirmationDialog *settings_dialog; + + bool snap_enabled; + bool snap_key_enabled; + LineEdit *snap_translate; + LineEdit *snap_rotate; + LineEdit *snap_scale; + PanelContainer *menu_panel; + + LineEdit *xform_translate[3]; + LineEdit *xform_rotate[3]; + LineEdit *xform_scale[3]; + OptionButton *xform_type; + + VBoxContainer *settings_vbc; + SpinBox *settings_fov; + SpinBox *settings_znear; + SpinBox *settings_zfar; + + void _snap_changed(); + void _snap_update(); + void _xform_dialog_action(); + void _menu_item_pressed(int p_option); + void _menu_item_toggled(bool pressed, int p_option); + void _menu_gizmo_toggled(int p_option); + void _update_camera_override_button(bool p_game_running); + void _update_camera_override_viewport(Object *p_viewport); + + HBoxContainer *hbc_menu; + + void _generate_selection_box(); + UndoRedo *undo_redo; + + int camera_override_viewport_id; + + void _init_indicators(); + void _update_gizmos_menu(); + void _update_gizmos_menu_theme(); + void _init_grid(); + void _finish_indicators(); + void _finish_grid(); + + void _toggle_maximize_view(Object *p_viewport); + + Node *custom_camera; + + Object *_get_editor_data(Object *p_what); + + Ref viewport_environment; + + Node3D *selected; + + void _request_gizmo(Object *p_obj); + + static Node3DEditor *singleton; + + void _node_removed(Node *p_node); + Vector> gizmo_plugins_by_priority; + Vector> gizmo_plugins_by_name; + + void _register_all_gizmos(); + + Node3DEditor(); + + bool is_any_freelook_active() const; + + void _refresh_menu_icons(); + +protected: + void _notification(int p_what); + //void _gui_input(InputEvent p_event); + void _unhandled_key_input(Ref p_event); + + static void _bind_methods(); + +public: + static Node3DEditor *get_singleton() { return singleton; } + void snap_cursor_to_plane(const Plane &p_plane); + + Vector3 snap_point(Vector3 p_target, Vector3 p_start = Vector3(0, 0, 0)) const; + + float get_znear() const { return settings_znear->get_value(); } + float get_zfar() const { return settings_zfar->get_value(); } + float get_fov() const { return settings_fov->get_value(); } + + Transform get_gizmo_transform() const { return gizmo.transform; } + bool is_gizmo_visible() const { return gizmo.visible; } + + ToolMode get_tool_mode() const { return tool_mode; } + bool are_local_coords_enabled() const { return tool_option_button[Node3DEditor::TOOL_OPT_LOCAL_COORDS]->is_pressed(); } + bool is_snap_enabled() const { return snap_enabled ^ snap_key_enabled; } + float get_translate_snap() const; + float get_rotate_snap() const; + float get_scale_snap() const; + + Ref get_move_gizmo(int idx) const { return move_gizmo[idx]; } + Ref get_move_plane_gizmo(int idx) const { return move_plane_gizmo[idx]; } + Ref get_rotate_gizmo(int idx) const { return rotate_gizmo[idx]; } + Ref get_scale_gizmo(int idx) const { return scale_gizmo[idx]; } + Ref get_scale_plane_gizmo(int idx) const { return scale_plane_gizmo[idx]; } + + void update_transform_gizmo(); + void update_all_gizmos(Node *p_node = NULL); + void snap_selected_nodes_to_floor(); + void select_gizmo_highlight_axis(int p_axis); + void set_custom_camera(Node *p_camera) { custom_camera = p_camera; } + + void set_undo_redo(UndoRedo *p_undo_redo) { undo_redo = p_undo_redo; } + Dictionary get_state() const; + void set_state(const Dictionary &p_state); + + Ref get_viewport_environment() { return viewport_environment; } + + UndoRedo *get_undo_redo() { return undo_redo; } + + void add_control_to_menu_panel(Control *p_control); + void remove_control_from_menu_panel(Control *p_control); + + VSplitContainer *get_shader_split(); + HSplitContainer *get_palette_split(); + + Node3D *get_selected() { return selected; } + + int get_over_gizmo_handle() const { return over_gizmo_handle; } + void set_over_gizmo_handle(int idx) { over_gizmo_handle = idx; } + + void set_can_preview(Camera3D *p_preview); + + Node3DEditorViewport *get_editor_viewport(int p_idx) { + ERR_FAIL_INDEX_V(p_idx, static_cast(VIEWPORTS_COUNT), NULL); + return viewports[p_idx]; + } + + void add_gizmo_plugin(Ref p_plugin); + void remove_gizmo_plugin(Ref p_plugin); + + void edit(Node3D *p_spatial); + void clear(); + + Node3DEditor(EditorNode *p_editor); + ~Node3DEditor(); +}; + +class Node3DEditorPlugin : public EditorPlugin { + + GDCLASS(Node3DEditorPlugin, EditorPlugin); + + Node3DEditor *spatial_editor; + EditorNode *editor; + +protected: + static void _bind_methods(); + +public: + void snap_cursor_to_plane(const Plane &p_plane); + + Node3DEditor *get_spatial_editor() { return spatial_editor; } + virtual String get_name() const { return "3D"; } + bool has_main_screen() const { return true; } + virtual void make_visible(bool p_visible); + virtual void edit(Object *p_object); + virtual bool handles(Object *p_object) const; + + virtual Dictionary get_state() const; + virtual void set_state(const Dictionary &p_state); + virtual void clear() { spatial_editor->clear(); } + + virtual void edited_scene_changed(); + + Node3DEditorPlugin(EditorNode *p_node); + ~Node3DEditorPlugin(); +}; + +class EditorNode3DGizmoPlugin : public Resource { + + GDCLASS(EditorNode3DGizmoPlugin, Resource); + +public: + static const int VISIBLE = 0; + static const int HIDDEN = 1; + static const int ON_TOP = 2; + +private: + int current_state; + List current_gizmos; + HashMap>> materials; + +protected: + static void _bind_methods(); + virtual bool has_gizmo(Node3D *p_spatial); + virtual Ref create_gizmo(Node3D *p_spatial); + +public: + void create_material(const String &p_name, const Color &p_color, bool p_billboard = false, bool p_on_top = false, bool p_use_vertex_color = false); + void create_icon_material(const String &p_name, const Ref &p_texture, bool p_on_top = false, const Color &p_albedo = Color(1, 1, 1, 1)); + void create_handle_material(const String &p_name, bool p_billboard = false); + void add_material(const String &p_name, Ref p_material); + + Ref get_material(const String &p_name, const Ref &p_gizmo = Ref()); + + virtual String get_name() const; + virtual int get_priority() const; + virtual bool can_be_hidden() const; + virtual bool is_selectable_when_hidden() const; + + virtual void redraw(EditorNode3DGizmo *p_gizmo); + virtual String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const; + virtual Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const; + virtual void set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point); + virtual void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false); + virtual bool is_handle_highlighted(const EditorNode3DGizmo *p_gizmo, int p_idx) const; + + Ref get_gizmo(Node3D *p_spatial); + void set_state(int p_state); + int get_state() const; + void unregister_gizmo(EditorNode3DGizmo *p_gizmo); + + EditorNode3DGizmoPlugin(); + virtual ~EditorNode3DGizmoPlugin(); +}; + +#endif diff --git a/editor/plugins/particles_2d_editor_plugin.cpp b/editor/plugins/particles_2d_editor_plugin.cpp deleted file mode 100644 index b018ef383e..0000000000 --- a/editor/plugins/particles_2d_editor_plugin.cpp +++ /dev/null @@ -1,435 +0,0 @@ -/*************************************************************************/ -/* particles_2d_editor_plugin.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "particles_2d_editor_plugin.h" - -#include "canvas_item_editor_plugin.h" -#include "core/io/image_loader.h" -#include "scene/2d/cpu_particles_2d.h" -#include "scene/gui/separator.h" -#include "scene/resources/particles_material.h" - -void Particles2DEditorPlugin::edit(Object *p_object) { - - particles = Object::cast_to(p_object); -} - -bool Particles2DEditorPlugin::handles(Object *p_object) const { - - return p_object->is_class("GPUParticles2D"); -} - -void Particles2DEditorPlugin::make_visible(bool p_visible) { - - if (p_visible) { - - toolbar->show(); - } else { - - toolbar->hide(); - } -} - -void Particles2DEditorPlugin::_file_selected(const String &p_file) { - - source_emission_file = p_file; - emission_mask->popup_centered(); -} - -void Particles2DEditorPlugin::_menu_callback(int p_idx) { - - switch (p_idx) { - case MENU_GENERATE_VISIBILITY_RECT: { - float gen_time = particles->get_lifetime(); - if (gen_time < 1.0) - generate_seconds->set_value(1.0); - else - generate_seconds->set_value(trunc(gen_time) + 1.0); - generate_visibility_rect->popup_centered(); - } break; - case MENU_LOAD_EMISSION_MASK: { - - file->popup_centered_ratio(); - - } break; - case MENU_CLEAR_EMISSION_MASK: { - - emission_mask->popup_centered(); - } break; - case MENU_OPTION_CONVERT_TO_CPU_PARTICLES: { - - CPUParticles2D *cpu_particles = memnew(CPUParticles2D); - cpu_particles->convert_from_particles(particles); - cpu_particles->set_name(particles->get_name()); - cpu_particles->set_transform(particles->get_transform()); - cpu_particles->set_visible(particles->is_visible()); - cpu_particles->set_pause_mode(particles->get_pause_mode()); - cpu_particles->set_z_index(particles->get_z_index()); - - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); - ur->create_action(TTR("Convert to CPUParticles")); - ur->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", particles, cpu_particles, true, false); - ur->add_do_reference(cpu_particles); - ur->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", cpu_particles, particles, false, false); - ur->add_undo_reference(particles); - ur->commit_action(); - - } break; - case MENU_RESTART: { - - particles->restart(); - } - } -} - -void Particles2DEditorPlugin::_generate_visibility_rect() { - - float time = generate_seconds->get_value(); - - float running = 0.0; - - EditorProgress ep("gen_vrect", TTR("Generating Visibility Rect"), int(time)); - - bool was_emitting = particles->is_emitting(); - if (!was_emitting) { - particles->set_emitting(true); - OS::get_singleton()->delay_usec(1000); - } - - Rect2 rect; - while (running < time) { - - uint64_t ticks = OS::get_singleton()->get_ticks_usec(); - ep.step("Generating...", int(running), true); - OS::get_singleton()->delay_usec(1000); - - Rect2 capture = particles->capture_rect(); - if (rect == Rect2()) - rect = capture; - else - rect = rect.merge(capture); - - running += (OS::get_singleton()->get_ticks_usec() - ticks) / 1000000.0; - } - - if (!was_emitting) { - particles->set_emitting(false); - } - - undo_redo->create_action(TTR("Generate Visibility Rect")); - undo_redo->add_do_method(particles, "set_visibility_rect", rect); - undo_redo->add_undo_method(particles, "set_visibility_rect", particles->get_visibility_rect()); - undo_redo->commit_action(); -} - -void Particles2DEditorPlugin::_generate_emission_mask() { - - Ref pm = particles->get_process_material(); - if (!pm.is_valid()) { - EditorNode::get_singleton()->show_warning(TTR("Can only set point into a ParticlesMaterial process material")); - return; - } - - Ref img; - img.instance(); - Error err = ImageLoader::load_image(source_emission_file, img); - ERR_FAIL_COND_MSG(err != OK, "Error loading image '" + source_emission_file + "'."); - - if (img->is_compressed()) { - img->decompress(); - } - img->convert(Image::FORMAT_RGBA8); - ERR_FAIL_COND(img->get_format() != Image::FORMAT_RGBA8); - Size2i s = Size2(img->get_width(), img->get_height()); - ERR_FAIL_COND(s.width == 0 || s.height == 0); - - Vector valid_positions; - Vector valid_normals; - Vector valid_colors; - - valid_positions.resize(s.width * s.height); - - EmissionMode emode = (EmissionMode)emission_mask_mode->get_selected(); - - if (emode == EMISSION_MODE_BORDER_DIRECTED) { - valid_normals.resize(s.width * s.height); - } - - bool capture_colors = emission_colors->is_pressed(); - - if (capture_colors) { - valid_colors.resize(s.width * s.height * 4); - } - - int vpc = 0; - - { - Vector data = img->get_data(); - const uint8_t *r = data.ptr(); - - for (int i = 0; i < s.width; i++) { - for (int j = 0; j < s.height; j++) { - - uint8_t a = r[(j * s.width + i) * 4 + 3]; - - if (a > 128) { - - if (emode == EMISSION_MODE_SOLID) { - - if (capture_colors) { - valid_colors.write[vpc * 4 + 0] = r[(j * s.width + i) * 4 + 0]; - valid_colors.write[vpc * 4 + 1] = r[(j * s.width + i) * 4 + 1]; - valid_colors.write[vpc * 4 + 2] = r[(j * s.width + i) * 4 + 2]; - valid_colors.write[vpc * 4 + 3] = r[(j * s.width + i) * 4 + 3]; - } - valid_positions.write[vpc++] = Point2(i, j); - - } else { - - bool on_border = false; - for (int x = i - 1; x <= i + 1; x++) { - for (int y = j - 1; y <= j + 1; y++) { - - if (x < 0 || y < 0 || x >= s.width || y >= s.height || r[(y * s.width + x) * 4 + 3] <= 128) { - on_border = true; - break; - } - } - - if (on_border) - break; - } - - if (on_border) { - valid_positions.write[vpc] = Point2(i, j); - - if (emode == EMISSION_MODE_BORDER_DIRECTED) { - Vector2 normal; - for (int x = i - 2; x <= i + 2; x++) { - for (int y = j - 2; y <= j + 2; y++) { - - if (x == i && y == j) - continue; - - if (x < 0 || y < 0 || x >= s.width || y >= s.height || r[(y * s.width + x) * 4 + 3] <= 128) { - normal += Vector2(x - i, y - j).normalized(); - } - } - } - - normal.normalize(); - valid_normals.write[vpc] = normal; - } - - if (capture_colors) { - valid_colors.write[vpc * 4 + 0] = r[(j * s.width + i) * 4 + 0]; - valid_colors.write[vpc * 4 + 1] = r[(j * s.width + i) * 4 + 1]; - valid_colors.write[vpc * 4 + 2] = r[(j * s.width + i) * 4 + 2]; - valid_colors.write[vpc * 4 + 3] = r[(j * s.width + i) * 4 + 3]; - } - - vpc++; - } - } - } - } - } - } - - valid_positions.resize(vpc); - if (valid_normals.size()) { - valid_normals.resize(vpc); - } - - ERR_FAIL_COND_MSG(valid_positions.size() == 0, "No pixels with transparency > 128 in image..."); - - Vector texdata; - - int w = 2048; - int h = (vpc / 2048) + 1; - - texdata.resize(w * h * 2 * sizeof(float)); - - { - uint8_t *tw = texdata.ptrw(); - float *twf = (float *)tw; - for (int i = 0; i < vpc; i++) { - - twf[i * 2 + 0] = valid_positions[i].x; - twf[i * 2 + 1] = valid_positions[i].y; - } - } - - img.instance(); - img->create(w, h, false, Image::FORMAT_RGF, texdata); - - Ref imgt; - imgt.instance(); - imgt->create_from_image(img); - - pm->set_emission_point_texture(imgt); - pm->set_emission_point_count(vpc); - - if (capture_colors) { - - Vector colordata; - colordata.resize(w * h * 4); //use RG texture - - { - uint8_t *tw = colordata.ptrw(); - for (int i = 0; i < vpc * 4; i++) { - - tw[i] = valid_colors[i]; - } - } - - img.instance(); - img->create(w, h, false, Image::FORMAT_RGBA8, colordata); - - imgt.instance(); - imgt->create_from_image(img); - pm->set_emission_color_texture(imgt); - } - - if (valid_normals.size()) { - pm->set_emission_shape(ParticlesMaterial::EMISSION_SHAPE_DIRECTED_POINTS); - - Vector normdata; - normdata.resize(w * h * 2 * sizeof(float)); //use RG texture - - { - uint8_t *tw = normdata.ptrw(); - float *twf = (float *)tw; - for (int i = 0; i < vpc; i++) { - twf[i * 2 + 0] = valid_normals[i].x; - twf[i * 2 + 1] = valid_normals[i].y; - } - } - - img.instance(); - img->create(w, h, false, Image::FORMAT_RGF, normdata); - - imgt.instance(); - imgt->create_from_image(img); - pm->set_emission_normal_texture(imgt); - - } else { - pm->set_emission_shape(ParticlesMaterial::EMISSION_SHAPE_POINTS); - } -} - -void Particles2DEditorPlugin::_notification(int p_what) { - - if (p_what == NOTIFICATION_ENTER_TREE) { - - menu->get_popup()->connect("id_pressed", callable_mp(this, &Particles2DEditorPlugin::_menu_callback)); - menu->set_icon(menu->get_theme_icon("Particles2D", "EditorIcons")); - file->connect("file_selected", callable_mp(this, &Particles2DEditorPlugin::_file_selected)); - } -} - -void Particles2DEditorPlugin::_bind_methods() { -} - -Particles2DEditorPlugin::Particles2DEditorPlugin(EditorNode *p_node) { - - particles = NULL; - editor = p_node; - undo_redo = editor->get_undo_redo(); - - toolbar = memnew(HBoxContainer); - add_control_to_container(CONTAINER_CANVAS_EDITOR_MENU, toolbar); - toolbar->hide(); - - toolbar->add_child(memnew(VSeparator)); - - menu = memnew(MenuButton); - menu->get_popup()->add_item(TTR("Generate Visibility Rect"), MENU_GENERATE_VISIBILITY_RECT); - menu->get_popup()->add_separator(); - menu->get_popup()->add_item(TTR("Load Emission Mask"), MENU_LOAD_EMISSION_MASK); - // menu->get_popup()->add_item(TTR("Clear Emission Mask"), MENU_CLEAR_EMISSION_MASK); - menu->get_popup()->add_separator(); - menu->get_popup()->add_item(TTR("Convert to CPUParticles"), MENU_OPTION_CONVERT_TO_CPU_PARTICLES); - menu->get_popup()->add_separator(); - menu->get_popup()->add_item(TTR("Restart"), MENU_RESTART); - menu->set_text(TTR("Particles")); - menu->set_switch_on_hover(true); - toolbar->add_child(menu); - - file = memnew(EditorFileDialog); - List ext; - ImageLoader::get_recognized_extensions(&ext); - for (List::Element *E = ext.front(); E; E = E->next()) { - file->add_filter("*." + E->get() + "; " + E->get().to_upper()); - } - file->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE); - toolbar->add_child(file); - - epoints = memnew(SpinBox); - epoints->set_min(1); - epoints->set_max(8192); - epoints->set_step(1); - epoints->set_value(512); - file->get_vbox()->add_margin_child(TTR("Generated Point Count:"), epoints); - - generate_visibility_rect = memnew(ConfirmationDialog); - generate_visibility_rect->set_title(TTR("Generate Visibility Rect")); - VBoxContainer *genvb = memnew(VBoxContainer); - generate_visibility_rect->add_child(genvb); - generate_seconds = memnew(SpinBox); - genvb->add_margin_child(TTR("Generation Time (sec):"), generate_seconds); - generate_seconds->set_min(0.1); - generate_seconds->set_max(25); - generate_seconds->set_value(2); - - toolbar->add_child(generate_visibility_rect); - - generate_visibility_rect->connect("confirmed", callable_mp(this, &Particles2DEditorPlugin::_generate_visibility_rect)); - - emission_mask = memnew(ConfirmationDialog); - emission_mask->set_title(TTR("Load Emission Mask")); - VBoxContainer *emvb = memnew(VBoxContainer); - emission_mask->add_child(emvb); - emission_mask_mode = memnew(OptionButton); - emvb->add_margin_child(TTR("Emission Mask"), emission_mask_mode); - emission_mask_mode->add_item(TTR("Solid Pixels"), EMISSION_MODE_SOLID); - emission_mask_mode->add_item(TTR("Border Pixels"), EMISSION_MODE_BORDER); - emission_mask_mode->add_item(TTR("Directed Border Pixels"), EMISSION_MODE_BORDER_DIRECTED); - emission_colors = memnew(CheckBox); - emission_colors->set_text(TTR("Capture from Pixel")); - emvb->add_margin_child(TTR("Emission Colors"), emission_colors); - - toolbar->add_child(emission_mask); - - emission_mask->connect("confirmed", callable_mp(this, &Particles2DEditorPlugin::_generate_emission_mask)); -} - -Particles2DEditorPlugin::~Particles2DEditorPlugin() { -} diff --git a/editor/plugins/particles_2d_editor_plugin.h b/editor/plugins/particles_2d_editor_plugin.h deleted file mode 100644 index 29652a1826..0000000000 --- a/editor/plugins/particles_2d_editor_plugin.h +++ /dev/null @@ -1,100 +0,0 @@ -/*************************************************************************/ -/* particles_2d_editor_plugin.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef PARTICLES_2D_EDITOR_PLUGIN_H -#define PARTICLES_2D_EDITOR_PLUGIN_H - -#include "editor/editor_node.h" -#include "editor/editor_plugin.h" -#include "scene/2d/collision_polygon_2d.h" -#include "scene/2d/particles_2d.h" -#include "scene/gui/box_container.h" -#include "scene/gui/file_dialog.h" - -class Particles2DEditorPlugin : public EditorPlugin { - - GDCLASS(Particles2DEditorPlugin, EditorPlugin); - - enum { - - MENU_GENERATE_VISIBILITY_RECT, - MENU_LOAD_EMISSION_MASK, - MENU_CLEAR_EMISSION_MASK, - MENU_OPTION_CONVERT_TO_CPU_PARTICLES, - MENU_RESTART - }; - - enum EmissionMode { - EMISSION_MODE_SOLID, - EMISSION_MODE_BORDER, - EMISSION_MODE_BORDER_DIRECTED - }; - - Particles2D *particles; - - EditorFileDialog *file; - EditorNode *editor; - - HBoxContainer *toolbar; - MenuButton *menu; - - SpinBox *epoints; - - ConfirmationDialog *generate_visibility_rect; - SpinBox *generate_seconds; - - ConfirmationDialog *emission_mask; - OptionButton *emission_mask_mode; - CheckBox *emission_colors; - - String source_emission_file; - - UndoRedo *undo_redo; - void _file_selected(const String &p_file); - void _menu_callback(int p_idx); - void _generate_visibility_rect(); - void _generate_emission_mask(); - -protected: - void _notification(int p_what); - static void _bind_methods(); - -public: - virtual String get_name() const { return "Particles2D"; } - bool has_main_screen() const { return false; } - virtual void edit(Object *p_object); - virtual bool handles(Object *p_object) const; - virtual void make_visible(bool p_visible); - - Particles2DEditorPlugin(EditorNode *p_node); - ~Particles2DEditorPlugin(); -}; - -#endif // PARTICLES_2D_EDITOR_PLUGIN_H diff --git a/editor/plugins/particles_editor_plugin.cpp b/editor/plugins/particles_editor_plugin.cpp deleted file mode 100644 index 628eb4a7fe..0000000000 --- a/editor/plugins/particles_editor_plugin.cpp +++ /dev/null @@ -1,493 +0,0 @@ -/*************************************************************************/ -/* particles_editor_plugin.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "particles_editor_plugin.h" - -#include "core/io/resource_loader.h" -#include "editor/plugins/spatial_editor_plugin.h" -#include "scene/3d/cpu_particles_3d.h" -#include "scene/resources/particles_material.h" - -bool ParticlesEditorBase::_generate(Vector &points, Vector &normals) { - - bool use_normals = emission_fill->get_selected() == 1; - - if (emission_fill->get_selected() < 2) { - - float area_accum = 0; - Map triangle_area_map; - - for (int i = 0; i < geometry.size(); i++) { - - float area = geometry[i].get_area(); - if (area < CMP_EPSILON) - continue; - triangle_area_map[area_accum] = i; - area_accum += area; - } - - if (!triangle_area_map.size() || area_accum == 0) { - - EditorNode::get_singleton()->show_warning(TTR("The geometry's faces don't contain any area.")); - return false; - } - - int emissor_count = emission_amount->get_value(); - - for (int i = 0; i < emissor_count; i++) { - - float areapos = Math::random(0.0f, area_accum); - - Map::Element *E = triangle_area_map.find_closest(areapos); - ERR_FAIL_COND_V(!E, false); - int index = E->get(); - ERR_FAIL_INDEX_V(index, geometry.size(), false); - - // ok FINALLY get face - Face3 face = geometry[index]; - //now compute some position inside the face... - - Vector3 pos = face.get_random_point_inside(); - - points.push_back(pos); - - if (use_normals) { - Vector3 normal = face.get_plane().normal; - normals.push_back(normal); - } - } - } else { - - int gcount = geometry.size(); - - if (gcount == 0) { - - EditorNode::get_singleton()->show_warning(TTR("The geometry doesn't contain any faces.")); - return false; - } - - const Face3 *r = geometry.ptr(); - - AABB aabb; - - for (int i = 0; i < gcount; i++) { - - for (int j = 0; j < 3; j++) { - - if (i == 0 && j == 0) - aabb.position = r[i].vertex[j]; - else - aabb.expand_to(r[i].vertex[j]); - } - } - - int emissor_count = emission_amount->get_value(); - - for (int i = 0; i < emissor_count; i++) { - - int attempts = 5; - - for (int j = 0; j < attempts; j++) { - - Vector3 dir; - dir[Math::rand() % 3] = 1.0; - Vector3 ofs = (Vector3(1, 1, 1) - dir) * Vector3(Math::randf(), Math::randf(), Math::randf()) * aabb.size + aabb.position; - - Vector3 ofsv = ofs + aabb.size * dir; - - //space it a little - ofs -= dir; - ofsv += dir; - - float max = -1e7, min = 1e7; - - for (int k = 0; k < gcount; k++) { - - const Face3 &f3 = r[k]; - - Vector3 res; - if (f3.intersects_segment(ofs, ofsv, &res)) { - - res -= ofs; - float d = dir.dot(res); - - if (d < min) - min = d; - if (d > max) - max = d; - } - } - - if (max < min) - continue; //lost attempt - - float val = min + (max - min) * Math::randf(); - - Vector3 point = ofs + dir * val; - - points.push_back(point); - break; - } - } - } - - return true; -} - -void ParticlesEditorBase::_node_selected(const NodePath &p_path) { - - Node *sel = get_node(p_path); - if (!sel) - return; - - if (!sel->is_class("Node3D")) { - - EditorNode::get_singleton()->show_warning(vformat(TTR("\"%s\" doesn't inherit from Node3D."), sel->get_name())); - return; - } - - VisualInstance3D *vi = Object::cast_to(sel); - if (!vi) { - - EditorNode::get_singleton()->show_warning(vformat(TTR("\"%s\" doesn't contain geometry."), sel->get_name())); - return; - } - - geometry = vi->get_faces(VisualInstance3D::FACES_SOLID); - - if (geometry.size() == 0) { - - EditorNode::get_singleton()->show_warning(vformat(TTR("\"%s\" doesn't contain face geometry."), sel->get_name())); - return; - } - - Transform geom_xform = base_node->get_global_transform().affine_inverse() * vi->get_global_transform(); - - int gc = geometry.size(); - Face3 *w = geometry.ptrw(); - - for (int i = 0; i < gc; i++) { - for (int j = 0; j < 3; j++) { - w[i].vertex[j] = geom_xform.xform(w[i].vertex[j]); - } - } - - emission_dialog->popup_centered(Size2(300, 130)); -} - -void ParticlesEditorBase::_bind_methods() { -} - -ParticlesEditorBase::ParticlesEditorBase() { - - emission_dialog = memnew(ConfirmationDialog); - emission_dialog->set_title(TTR("Create Emitter")); - add_child(emission_dialog); - VBoxContainer *emd_vb = memnew(VBoxContainer); - emission_dialog->add_child(emd_vb); - - emission_amount = memnew(SpinBox); - emission_amount->set_min(1); - emission_amount->set_max(100000); - emission_amount->set_value(512); - emd_vb->add_margin_child(TTR("Emission Points:"), emission_amount); - - emission_fill = memnew(OptionButton); - emission_fill->add_item(TTR("Surface Points")); - emission_fill->add_item(TTR("Surface Points+Normal (Directed)")); - emission_fill->add_item(TTR("Volume")); - emd_vb->add_margin_child(TTR("Emission Source: "), emission_fill); - - emission_dialog->get_ok()->set_text(TTR("Create")); - emission_dialog->connect("confirmed", callable_mp(this, &ParticlesEditorBase::_generate_emission_points)); - - emission_tree_dialog = memnew(SceneTreeDialog); - add_child(emission_tree_dialog); - emission_tree_dialog->connect("selected", callable_mp(this, &ParticlesEditorBase::_node_selected)); -} - -void ParticlesEditor::_node_removed(Node *p_node) { - - if (p_node == node) { - node = NULL; - hide(); - } -} - -void ParticlesEditor::_notification(int p_notification) { - - if (p_notification == NOTIFICATION_ENTER_TREE) { - options->set_icon(options->get_popup()->get_theme_icon("Particles", "EditorIcons")); - get_tree()->connect("node_removed", callable_mp(this, &ParticlesEditor::_node_removed)); - } -} - -void ParticlesEditor::_menu_option(int p_option) { - - switch (p_option) { - - case MENU_OPTION_GENERATE_AABB: { - float gen_time = node->get_lifetime(); - - if (gen_time < 1.0) - generate_seconds->set_value(1.0); - else - generate_seconds->set_value(trunc(gen_time) + 1.0); - generate_aabb->popup_centered(); - } break; - case MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE: { - Ref material = node->get_process_material(); - if (material.is_null()) { - EditorNode::get_singleton()->show_warning(TTR("A processor material of type 'ParticlesMaterial' is required.")); - return; - } - - emission_tree_dialog->popup_centered_ratio(); - - } break; - case MENU_OPTION_CONVERT_TO_CPU_PARTICLES: { - - CPUParticles3D *cpu_particles = memnew(CPUParticles3D); - cpu_particles->convert_from_particles(node); - cpu_particles->set_name(node->get_name()); - cpu_particles->set_transform(node->get_transform()); - cpu_particles->set_visible(node->is_visible()); - cpu_particles->set_pause_mode(node->get_pause_mode()); - - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); - ur->create_action(TTR("Convert to CPUParticles")); - ur->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", node, cpu_particles, true, false); - ur->add_do_reference(cpu_particles); - ur->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", cpu_particles, node, false, false); - ur->add_undo_reference(node); - ur->commit_action(); - - } break; - case MENU_OPTION_RESTART: { - - node->restart(); - - } break; - } -} - -void ParticlesEditor::_generate_aabb() { - - float time = generate_seconds->get_value(); - - float running = 0.0; - - EditorProgress ep("gen_aabb", TTR("Generating AABB"), int(time)); - - bool was_emitting = node->is_emitting(); - if (!was_emitting) { - node->set_emitting(true); - OS::get_singleton()->delay_usec(1000); - } - - AABB rect; - - while (running < time) { - - uint64_t ticks = OS::get_singleton()->get_ticks_usec(); - ep.step("Generating...", int(running), true); - OS::get_singleton()->delay_usec(1000); - - AABB capture = node->capture_aabb(); - if (rect == AABB()) - rect = capture; - else - rect.merge_with(capture); - - running += (OS::get_singleton()->get_ticks_usec() - ticks) / 1000000.0; - } - - if (!was_emitting) { - node->set_emitting(false); - } - - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); - ur->create_action(TTR("Generate Visibility AABB")); - ur->add_do_method(node, "set_visibility_aabb", rect); - ur->add_undo_method(node, "set_visibility_aabb", node->get_visibility_aabb()); - ur->commit_action(); -} - -void ParticlesEditor::edit(GPUParticles3D *p_particles) { - - base_node = p_particles; - node = p_particles; -} - -void ParticlesEditor::_generate_emission_points() { - - /// hacer codigo aca - Vector points; - Vector normals; - - if (!_generate(points, normals)) { - return; - } - - int point_count = points.size(); - - int w = 2048; - int h = (point_count / 2048) + 1; - - Vector point_img; - point_img.resize(w * h * 3 * sizeof(float)); - - { - uint8_t *iw = point_img.ptrw(); - zeromem(iw, w * h * 3 * sizeof(float)); - const Vector3 *r = points.ptr(); - float *wf = (float *)iw; - for (int i = 0; i < point_count; i++) { - wf[i * 3 + 0] = r[i].x; - wf[i * 3 + 1] = r[i].y; - wf[i * 3 + 2] = r[i].z; - } - } - - Ref image = memnew(Image(w, h, false, Image::FORMAT_RGBF, point_img)); - - Ref tex; - tex.instance(); - - Ref material = node->get_process_material(); - ERR_FAIL_COND(material.is_null()); - - if (normals.size() > 0) { - - material->set_emission_shape(ParticlesMaterial::EMISSION_SHAPE_DIRECTED_POINTS); - material->set_emission_point_count(point_count); - material->set_emission_point_texture(tex); - - Vector point_img2; - point_img2.resize(w * h * 3 * sizeof(float)); - - { - uint8_t *iw = point_img2.ptrw(); - zeromem(iw, w * h * 3 * sizeof(float)); - const Vector3 *r = normals.ptr(); - float *wf = (float *)iw; - for (int i = 0; i < point_count; i++) { - wf[i * 3 + 0] = r[i].x; - wf[i * 3 + 1] = r[i].y; - wf[i * 3 + 2] = r[i].z; - } - } - - Ref image2 = memnew(Image(w, h, false, Image::FORMAT_RGBF, point_img2)); - - Ref tex2; - tex2.instance(); - - material->set_emission_normal_texture(tex2); - } else { - - material->set_emission_shape(ParticlesMaterial::EMISSION_SHAPE_POINTS); - material->set_emission_point_count(point_count); - material->set_emission_point_texture(tex); - } -} - -void ParticlesEditor::_bind_methods() { -} - -ParticlesEditor::ParticlesEditor() { - - node = NULL; - particles_editor_hb = memnew(HBoxContainer); - Node3DEditor::get_singleton()->add_control_to_menu_panel(particles_editor_hb); - options = memnew(MenuButton); - options->set_switch_on_hover(true); - particles_editor_hb->add_child(options); - particles_editor_hb->hide(); - - options->set_text(TTR("Particles")); - options->get_popup()->add_item(TTR("Generate AABB"), MENU_OPTION_GENERATE_AABB); - options->get_popup()->add_separator(); - options->get_popup()->add_item(TTR("Create Emission Points From Node"), MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE); - options->get_popup()->add_separator(); - options->get_popup()->add_item(TTR("Convert to CPUParticles"), MENU_OPTION_CONVERT_TO_CPU_PARTICLES); - options->get_popup()->add_separator(); - options->get_popup()->add_item(TTR("Restart"), MENU_OPTION_RESTART); - - options->get_popup()->connect("id_pressed", callable_mp(this, &ParticlesEditor::_menu_option)); - - generate_aabb = memnew(ConfirmationDialog); - generate_aabb->set_title(TTR("Generate Visibility AABB")); - VBoxContainer *genvb = memnew(VBoxContainer); - generate_aabb->add_child(genvb); - generate_seconds = memnew(SpinBox); - genvb->add_margin_child(TTR("Generation Time (sec):"), generate_seconds); - generate_seconds->set_min(0.1); - generate_seconds->set_max(25); - generate_seconds->set_value(2); - - add_child(generate_aabb); - - generate_aabb->connect("confirmed", callable_mp(this, &ParticlesEditor::_generate_aabb)); -} - -void ParticlesEditorPlugin::edit(Object *p_object) { - - particles_editor->edit(Object::cast_to(p_object)); -} - -bool ParticlesEditorPlugin::handles(Object *p_object) const { - - return p_object->is_class("GPUParticles3D"); -} - -void ParticlesEditorPlugin::make_visible(bool p_visible) { - - if (p_visible) { - particles_editor->show(); - particles_editor->particles_editor_hb->show(); - } else { - particles_editor->particles_editor_hb->hide(); - particles_editor->hide(); - particles_editor->edit(NULL); - } -} - -ParticlesEditorPlugin::ParticlesEditorPlugin(EditorNode *p_node) { - - editor = p_node; - particles_editor = memnew(ParticlesEditor); - editor->get_viewport()->add_child(particles_editor); - - particles_editor->hide(); -} - -ParticlesEditorPlugin::~ParticlesEditorPlugin() { -} diff --git a/editor/plugins/particles_editor_plugin.h b/editor/plugins/particles_editor_plugin.h deleted file mode 100644 index 773f305a2b..0000000000 --- a/editor/plugins/particles_editor_plugin.h +++ /dev/null @@ -1,121 +0,0 @@ -/*************************************************************************/ -/* particles_editor_plugin.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef PARTICLES_EDITOR_PLUGIN_H -#define PARTICLES_EDITOR_PLUGIN_H - -#include "editor/editor_node.h" -#include "editor/editor_plugin.h" -#include "scene/3d/gpu_particles_3d.h" -#include "scene/gui/spin_box.h" - -class ParticlesEditorBase : public Control { - - GDCLASS(ParticlesEditorBase, Control); - -protected: - Node3D *base_node; - Panel *panel; - MenuButton *options; - HBoxContainer *particles_editor_hb; - - SceneTreeDialog *emission_tree_dialog; - - ConfirmationDialog *emission_dialog; - SpinBox *emission_amount; - OptionButton *emission_fill; - - Vector geometry; - - bool _generate(Vector &points, Vector &normals); - virtual void _generate_emission_points() = 0; - void _node_selected(const NodePath &p_path); - - static void _bind_methods(); - -public: - ParticlesEditorBase(); -}; - -class ParticlesEditor : public ParticlesEditorBase { - - GDCLASS(ParticlesEditor, ParticlesEditorBase); - - ConfirmationDialog *generate_aabb; - SpinBox *generate_seconds; - GPUParticles3D *node; - - enum Menu { - - MENU_OPTION_GENERATE_AABB, - MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE, - MENU_OPTION_CLEAR_EMISSION_VOLUME, - MENU_OPTION_CONVERT_TO_CPU_PARTICLES, - MENU_OPTION_RESTART, - - }; - - void _generate_aabb(); - - void _menu_option(int); - - friend class ParticlesEditorPlugin; - - virtual void _generate_emission_points(); - -protected: - void _notification(int p_notification); - void _node_removed(Node *p_node); - static void _bind_methods(); - -public: - void edit(GPUParticles3D *p_particles); - ParticlesEditor(); -}; - -class ParticlesEditorPlugin : public EditorPlugin { - - GDCLASS(ParticlesEditorPlugin, EditorPlugin); - - ParticlesEditor *particles_editor; - EditorNode *editor; - -public: - virtual String get_name() const { return "Particles"; } - bool has_main_screen() const { return false; } - virtual void edit(Object *p_object); - virtual bool handles(Object *p_object) const; - virtual void make_visible(bool p_visible); - - ParticlesEditorPlugin(EditorNode *p_node); - ~ParticlesEditorPlugin(); -}; - -#endif // PARTICLES_EDITOR_PLUGIN_H diff --git a/editor/plugins/path_3d_editor_plugin.cpp b/editor/plugins/path_3d_editor_plugin.cpp new file mode 100644 index 0000000000..f3729a3e89 --- /dev/null +++ b/editor/plugins/path_3d_editor_plugin.cpp @@ -0,0 +1,653 @@ +/*************************************************************************/ +/* path_3d_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "path_3d_editor_plugin.h" + +#include "core/os/keyboard.h" +#include "node_3d_editor_plugin.h" +#include "scene/resources/curve.h" + +String PathNode3DGizmo::get_handle_name(int p_idx) const { + + Ref c = path->get_curve(); + if (c.is_null()) + return ""; + + if (p_idx < c->get_point_count()) { + + return TTR("Curve Point #") + itos(p_idx); + } + + p_idx = p_idx - c->get_point_count() + 1; + + int idx = p_idx / 2; + int t = p_idx % 2; + String n = TTR("Curve Point #") + itos(idx); + if (t == 0) + n += " In"; + else + n += " Out"; + + return n; +} +Variant PathNode3DGizmo::get_handle_value(int p_idx) { + + Ref c = path->get_curve(); + if (c.is_null()) + return Variant(); + + if (p_idx < c->get_point_count()) { + + original = c->get_point_position(p_idx); + return original; + } + + p_idx = p_idx - c->get_point_count() + 1; + + int idx = p_idx / 2; + int t = p_idx % 2; + + Vector3 ofs; + if (t == 0) + ofs = c->get_point_in(idx); + else + ofs = c->get_point_out(idx); + + original = ofs + c->get_point_position(idx); + + return ofs; +} +void PathNode3DGizmo::set_handle(int p_idx, Camera3D *p_camera, const Point2 &p_point) { + + Ref c = path->get_curve(); + if (c.is_null()) + return; + + Transform gt = path->get_global_transform(); + Transform gi = gt.affine_inverse(); + Vector3 ray_from = p_camera->project_ray_origin(p_point); + Vector3 ray_dir = p_camera->project_ray_normal(p_point); + + // Setting curve point positions + if (p_idx < c->get_point_count()) { + + Plane p(gt.xform(original), p_camera->get_transform().basis.get_axis(2)); + + Vector3 inters; + + if (p.intersects_ray(ray_from, ray_dir, &inters)) { + + if (Node3DEditor::get_singleton()->is_snap_enabled()) { + float snap = Node3DEditor::get_singleton()->get_translate_snap(); + inters.snap(Vector3(snap, snap, snap)); + } + + Vector3 local = gi.xform(inters); + c->set_point_position(p_idx, local); + } + + return; + } + + p_idx = p_idx - c->get_point_count() + 1; + + int idx = p_idx / 2; + int t = p_idx % 2; + + Vector3 base = c->get_point_position(idx); + + Plane p(gt.xform(original), p_camera->get_transform().basis.get_axis(2)); + + Vector3 inters; + + // Setting curve in/out positions + if (p.intersects_ray(ray_from, ray_dir, &inters)) { + + if (!Path3DEditorPlugin::singleton->is_handle_clicked()) { + orig_in_length = c->get_point_in(idx).length(); + orig_out_length = c->get_point_out(idx).length(); + Path3DEditorPlugin::singleton->set_handle_clicked(true); + } + + Vector3 local = gi.xform(inters) - base; + if (Node3DEditor::get_singleton()->is_snap_enabled()) { + float snap = Node3DEditor::get_singleton()->get_translate_snap(); + local.snap(Vector3(snap, snap, snap)); + } + + if (t == 0) { + c->set_point_in(idx, local); + if (Path3DEditorPlugin::singleton->mirror_angle_enabled()) + c->set_point_out(idx, Path3DEditorPlugin::singleton->mirror_length_enabled() ? -local : (-local.normalized() * orig_out_length)); + } else { + c->set_point_out(idx, local); + if (Path3DEditorPlugin::singleton->mirror_angle_enabled()) + c->set_point_in(idx, Path3DEditorPlugin::singleton->mirror_length_enabled() ? -local : (-local.normalized() * orig_in_length)); + } + } +} + +void PathNode3DGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p_cancel) { + + Ref c = path->get_curve(); + if (c.is_null()) + return; + + UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + + if (p_idx < c->get_point_count()) { + + if (p_cancel) { + + c->set_point_position(p_idx, p_restore); + return; + } + ur->create_action(TTR("Set Curve Point Position")); + ur->add_do_method(c.ptr(), "set_point_position", p_idx, c->get_point_position(p_idx)); + ur->add_undo_method(c.ptr(), "set_point_position", p_idx, p_restore); + ur->commit_action(); + + return; + } + + p_idx = p_idx - c->get_point_count() + 1; + + int idx = p_idx / 2; + int t = p_idx % 2; + + if (t == 0) { + if (p_cancel) { + c->set_point_in(p_idx, p_restore); + return; + } + + ur->create_action(TTR("Set Curve In Position")); + ur->add_do_method(c.ptr(), "set_point_in", idx, c->get_point_in(idx)); + ur->add_undo_method(c.ptr(), "set_point_in", idx, p_restore); + + if (Path3DEditorPlugin::singleton->mirror_angle_enabled()) { + ur->add_do_method(c.ptr(), "set_point_out", idx, Path3DEditorPlugin::singleton->mirror_length_enabled() ? -c->get_point_in(idx) : (-c->get_point_in(idx).normalized() * orig_out_length)); + ur->add_undo_method(c.ptr(), "set_point_out", idx, Path3DEditorPlugin::singleton->mirror_length_enabled() ? -static_cast(p_restore) : (-static_cast(p_restore).normalized() * orig_out_length)); + } + ur->commit_action(); + + } else { + if (p_cancel) { + c->set_point_out(idx, p_restore); + + return; + } + + ur->create_action(TTR("Set Curve Out Position")); + ur->add_do_method(c.ptr(), "set_point_out", idx, c->get_point_out(idx)); + ur->add_undo_method(c.ptr(), "set_point_out", idx, p_restore); + + if (Path3DEditorPlugin::singleton->mirror_angle_enabled()) { + ur->add_do_method(c.ptr(), "set_point_in", idx, Path3DEditorPlugin::singleton->mirror_length_enabled() ? -c->get_point_out(idx) : (-c->get_point_out(idx).normalized() * orig_in_length)); + ur->add_undo_method(c.ptr(), "set_point_in", idx, Path3DEditorPlugin::singleton->mirror_length_enabled() ? -static_cast(p_restore) : (-static_cast(p_restore).normalized() * orig_in_length)); + } + ur->commit_action(); + } +} + +void PathNode3DGizmo::redraw() { + + clear(); + + Ref path_material = gizmo_plugin->get_material("path_material", this); + Ref path_thin_material = gizmo_plugin->get_material("path_thin_material", this); + Ref handles_material = gizmo_plugin->get_material("handles"); + + Ref c = path->get_curve(); + if (c.is_null()) + return; + + Vector v3a = c->tessellate(); + //Vector v3a=c->get_baked_points(); + + int v3s = v3a.size(); + if (v3s == 0) + return; + Vector v3p; + const Vector3 *r = v3a.ptr(); + + // BUG: the following won't work when v3s, avoid drawing as a temporary workaround. + for (int i = 0; i < v3s - 1; i++) { + + v3p.push_back(r[i]); + v3p.push_back(r[i + 1]); + //v3p.push_back(r[i]); + //v3p.push_back(r[i]+Vector3(0,0.2,0)); + } + + if (v3p.size() > 1) { + add_lines(v3p, path_material); + add_collision_segments(v3p); + } + + if (Path3DEditorPlugin::singleton->get_edited_path() == path) { + v3p.clear(); + Vector handles; + Vector sec_handles; + + for (int i = 0; i < c->get_point_count(); i++) { + + Vector3 p = c->get_point_position(i); + handles.push_back(p); + if (i > 0) { + v3p.push_back(p); + v3p.push_back(p + c->get_point_in(i)); + sec_handles.push_back(p + c->get_point_in(i)); + } + + if (i < c->get_point_count() - 1) { + v3p.push_back(p); + v3p.push_back(p + c->get_point_out(i)); + sec_handles.push_back(p + c->get_point_out(i)); + } + } + + if (v3p.size() > 1) { + add_lines(v3p, path_thin_material); + } + if (handles.size()) { + add_handles(handles, handles_material); + } + if (sec_handles.size()) { + add_handles(sec_handles, handles_material, false, true); + } + } +} + +PathNode3DGizmo::PathNode3DGizmo(Path3D *p_path) { + + path = p_path; + set_spatial_node(p_path); +} + +bool Path3DEditorPlugin::forward_spatial_gui_input(Camera3D *p_camera, const Ref &p_event) { + + if (!path) + return false; + Ref c = path->get_curve(); + if (c.is_null()) + return false; + Transform gt = path->get_global_transform(); + Transform it = gt.affine_inverse(); + + static const int click_dist = 10; //should make global + + Ref mb = p_event; + + if (mb.is_valid()) { + + Point2 mbpos(mb->get_position().x, mb->get_position().y); + + if (!mb->is_pressed()) + set_handle_clicked(false); + + if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT && (curve_create->is_pressed() || (curve_edit->is_pressed() && mb->get_control()))) { + //click into curve, break it down + Vector v3a = c->tessellate(); + int idx = 0; + int rc = v3a.size(); + int closest_seg = -1; + Vector3 closest_seg_point; + float closest_d = 1e20; + + if (rc >= 2) { + const Vector3 *r = v3a.ptr(); + + if (p_camera->unproject_position(gt.xform(c->get_point_position(0))).distance_to(mbpos) < click_dist) + return false; //nope, existing + + for (int i = 0; i < c->get_point_count() - 1; i++) { + //find the offset and point index of the place to break up + int j = idx; + if (p_camera->unproject_position(gt.xform(c->get_point_position(i + 1))).distance_to(mbpos) < click_dist) + return false; //nope, existing + + while (j < rc && c->get_point_position(i + 1) != r[j]) { + + Vector3 from = r[j]; + Vector3 to = r[j + 1]; + real_t cdist = from.distance_to(to); + from = gt.xform(from); + to = gt.xform(to); + if (cdist > 0) { + Vector2 s[2]; + s[0] = p_camera->unproject_position(from); + s[1] = p_camera->unproject_position(to); + Vector2 inters = Geometry::get_closest_point_to_segment_2d(mbpos, s); + float d = inters.distance_to(mbpos); + + if (d < 10 && d < closest_d) { + + closest_d = d; + closest_seg = i; + Vector3 ray_from = p_camera->project_ray_origin(mbpos); + Vector3 ray_dir = p_camera->project_ray_normal(mbpos); + + Vector3 ra, rb; + Geometry::get_closest_points_between_segments(ray_from, ray_from + ray_dir * 4096, from, to, ra, rb); + + closest_seg_point = it.xform(rb); + } + } + j++; + } + if (idx == j) + idx++; //force next + else + idx = j; //swap + + if (j == rc) + break; + } + } + + UndoRedo *ur = editor->get_undo_redo(); + if (closest_seg != -1) { + //subdivide + + ur->create_action(TTR("Split Path")); + ur->add_do_method(c.ptr(), "add_point", closest_seg_point, Vector3(), Vector3(), closest_seg + 1); + ur->add_undo_method(c.ptr(), "remove_point", closest_seg + 1); + ur->commit_action(); + return true; + + } else { + + Vector3 org; + if (c->get_point_count() == 0) + org = path->get_transform().get_origin(); + else + org = gt.xform(c->get_point_position(c->get_point_count() - 1)); + Plane p(org, p_camera->get_transform().basis.get_axis(2)); + Vector3 ray_from = p_camera->project_ray_origin(mbpos); + Vector3 ray_dir = p_camera->project_ray_normal(mbpos); + + Vector3 inters; + if (p.intersects_ray(ray_from, ray_dir, &inters)) { + + ur->create_action(TTR("Add Point to Curve")); + ur->add_do_method(c.ptr(), "add_point", it.xform(inters), Vector3(), Vector3(), -1); + ur->add_undo_method(c.ptr(), "remove_point", c->get_point_count()); + ur->commit_action(); + return true; + } + + //add new at pos + } + + } else if (mb->is_pressed() && ((mb->get_button_index() == BUTTON_LEFT && curve_del->is_pressed()) || (mb->get_button_index() == BUTTON_RIGHT && curve_edit->is_pressed()))) { + + for (int i = 0; i < c->get_point_count(); i++) { + real_t dist_to_p = p_camera->unproject_position(gt.xform(c->get_point_position(i))).distance_to(mbpos); + real_t dist_to_p_out = p_camera->unproject_position(gt.xform(c->get_point_position(i) + c->get_point_out(i))).distance_to(mbpos); + real_t dist_to_p_in = p_camera->unproject_position(gt.xform(c->get_point_position(i) + c->get_point_in(i))).distance_to(mbpos); + + // Find the offset and point index of the place to break up. + // Also check for the control points. + if (dist_to_p < click_dist) { + + UndoRedo *ur = editor->get_undo_redo(); + ur->create_action(TTR("Remove Path Point")); + ur->add_do_method(c.ptr(), "remove_point", i); + ur->add_undo_method(c.ptr(), "add_point", c->get_point_position(i), c->get_point_in(i), c->get_point_out(i), i); + ur->commit_action(); + return true; + } else if (dist_to_p_out < click_dist) { + + UndoRedo *ur = editor->get_undo_redo(); + ur->create_action(TTR("Remove Out-Control Point")); + ur->add_do_method(c.ptr(), "set_point_out", i, Vector3()); + ur->add_undo_method(c.ptr(), "set_point_out", i, c->get_point_out(i)); + ur->commit_action(); + return true; + } else if (dist_to_p_in < click_dist) { + + UndoRedo *ur = editor->get_undo_redo(); + ur->create_action(TTR("Remove In-Control Point")); + ur->add_do_method(c.ptr(), "set_point_in", i, Vector3()); + ur->add_undo_method(c.ptr(), "set_point_in", i, c->get_point_in(i)); + ur->commit_action(); + return true; + } + } + } + } + + return false; +} + +void Path3DEditorPlugin::edit(Object *p_object) { + + if (p_object) { + path = Object::cast_to(p_object); + if (path) { + + if (path->get_curve().is_valid()) { + path->get_curve()->emit_signal("changed"); + } + } + } else { + Path3D *pre = path; + path = NULL; + if (pre) { + pre->get_curve()->emit_signal("changed"); + } + } + //collision_polygon_editor->edit(Object::cast_to(p_object)); +} + +bool Path3DEditorPlugin::handles(Object *p_object) const { + + return p_object->is_class("Path3D"); +} + +void Path3DEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + + curve_create->show(); + curve_edit->show(); + curve_del->show(); + curve_close->show(); + handle_menu->show(); + sep->show(); + } else { + + curve_create->hide(); + curve_edit->hide(); + curve_del->hide(); + curve_close->hide(); + handle_menu->hide(); + sep->hide(); + + { + Path3D *pre = path; + path = NULL; + if (pre && pre->get_curve().is_valid()) { + pre->get_curve()->emit_signal("changed"); + } + } + } +} + +void Path3DEditorPlugin::_mode_changed(int p_idx) { + + curve_create->set_pressed(p_idx == 0); + curve_edit->set_pressed(p_idx == 1); + curve_del->set_pressed(p_idx == 2); +} + +void Path3DEditorPlugin::_close_curve() { + + Ref c = path->get_curve(); + if (c.is_null()) + return; + if (c->get_point_count() < 2) + return; + c->add_point(c->get_point_position(0), c->get_point_in(0), c->get_point_out(0)); +} + +void Path3DEditorPlugin::_handle_option_pressed(int p_option) { + + PopupMenu *pm; + pm = handle_menu->get_popup(); + + switch (p_option) { + case HANDLE_OPTION_ANGLE: { + bool is_checked = pm->is_item_checked(HANDLE_OPTION_ANGLE); + mirror_handle_angle = !is_checked; + pm->set_item_checked(HANDLE_OPTION_ANGLE, mirror_handle_angle); + pm->set_item_disabled(HANDLE_OPTION_LENGTH, !mirror_handle_angle); + } break; + case HANDLE_OPTION_LENGTH: { + bool is_checked = pm->is_item_checked(HANDLE_OPTION_LENGTH); + mirror_handle_length = !is_checked; + pm->set_item_checked(HANDLE_OPTION_LENGTH, mirror_handle_length); + } break; + } +} + +void Path3DEditorPlugin::_notification(int p_what) { + + if (p_what == NOTIFICATION_ENTER_TREE) { + + curve_create->connect("pressed", callable_mp(this, &Path3DEditorPlugin::_mode_changed), make_binds(0)); + curve_edit->connect("pressed", callable_mp(this, &Path3DEditorPlugin::_mode_changed), make_binds(1)); + curve_del->connect("pressed", callable_mp(this, &Path3DEditorPlugin::_mode_changed), make_binds(2)); + curve_close->connect("pressed", callable_mp(this, &Path3DEditorPlugin::_close_curve)); + } +} + +void Path3DEditorPlugin::_bind_methods() { +} + +Path3DEditorPlugin *Path3DEditorPlugin::singleton = NULL; + +Path3DEditorPlugin::Path3DEditorPlugin(EditorNode *p_node) { + + path = NULL; + editor = p_node; + singleton = this; + mirror_handle_angle = true; + mirror_handle_length = true; + + Ref gizmo_plugin; + gizmo_plugin.instance(); + Node3DEditor::get_singleton()->add_gizmo_plugin(gizmo_plugin); + + sep = memnew(VSeparator); + sep->hide(); + Node3DEditor::get_singleton()->add_control_to_menu_panel(sep); + curve_edit = memnew(ToolButton); + curve_edit->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("CurveEdit", "EditorIcons")); + curve_edit->set_toggle_mode(true); + curve_edit->hide(); + curve_edit->set_focus_mode(Control::FOCUS_NONE); + curve_edit->set_tooltip(TTR("Select Points") + "\n" + TTR("Shift+Drag: Select Control Points") + "\n" + keycode_get_string(KEY_MASK_CMD) + TTR("Click: Add Point") + "\n" + TTR("Right Click: Delete Point")); + Node3DEditor::get_singleton()->add_control_to_menu_panel(curve_edit); + curve_create = memnew(ToolButton); + curve_create->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("CurveCreate", "EditorIcons")); + curve_create->set_toggle_mode(true); + curve_create->hide(); + curve_create->set_focus_mode(Control::FOCUS_NONE); + curve_create->set_tooltip(TTR("Add Point (in empty space)") + "\n" + TTR("Split Segment (in curve)")); + Node3DEditor::get_singleton()->add_control_to_menu_panel(curve_create); + curve_del = memnew(ToolButton); + curve_del->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("CurveDelete", "EditorIcons")); + curve_del->set_toggle_mode(true); + curve_del->hide(); + curve_del->set_focus_mode(Control::FOCUS_NONE); + curve_del->set_tooltip(TTR("Delete Point")); + Node3DEditor::get_singleton()->add_control_to_menu_panel(curve_del); + curve_close = memnew(ToolButton); + curve_close->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("CurveClose", "EditorIcons")); + curve_close->hide(); + curve_close->set_focus_mode(Control::FOCUS_NONE); + curve_close->set_tooltip(TTR("Close Curve")); + Node3DEditor::get_singleton()->add_control_to_menu_panel(curve_close); + + PopupMenu *menu; + + handle_menu = memnew(MenuButton); + handle_menu->set_text(TTR("Options")); + handle_menu->hide(); + Node3DEditor::get_singleton()->add_control_to_menu_panel(handle_menu); + + menu = handle_menu->get_popup(); + menu->add_check_item(TTR("Mirror Handle Angles")); + menu->set_item_checked(HANDLE_OPTION_ANGLE, mirror_handle_angle); + menu->add_check_item(TTR("Mirror Handle Lengths")); + menu->set_item_checked(HANDLE_OPTION_LENGTH, mirror_handle_length); + menu->connect("id_pressed", callable_mp(this, &Path3DEditorPlugin::_handle_option_pressed)); + + curve_edit->set_pressed(true); + /* + collision_polygon_editor = memnew( PathEditor(p_node) ); + editor->get_viewport()->add_child(collision_polygon_editor); + collision_polygon_editor->set_margin(MARGIN_LEFT,200); + collision_polygon_editor->set_margin(MARGIN_RIGHT,230); + collision_polygon_editor->set_margin(MARGIN_TOP,0); + collision_polygon_editor->set_margin(MARGIN_BOTTOM,10); + collision_polygon_editor->hide(); + */ +} + +Path3DEditorPlugin::~Path3DEditorPlugin() { +} + +Ref PathNode3DGizmoPlugin::create_gizmo(Node3D *p_spatial) { + Ref ref; + + Path3D *path = Object::cast_to(p_spatial); + if (path) ref = Ref(memnew(PathNode3DGizmo(path))); + + return ref; +} + +String PathNode3DGizmoPlugin::get_name() const { + return "Path"; +} + +int PathNode3DGizmoPlugin::get_priority() const { + return -1; +} + +PathNode3DGizmoPlugin::PathNode3DGizmoPlugin() { + + Color path_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/path", Color(0.5, 0.5, 1.0, 0.8)); + create_material("path_material", path_color); + create_material("path_thin_material", Color(0.5, 0.5, 0.5)); + create_handle_material("handles"); +} diff --git a/editor/plugins/path_3d_editor_plugin.h b/editor/plugins/path_3d_editor_plugin.h new file mode 100644 index 0000000000..ca051295f0 --- /dev/null +++ b/editor/plugins/path_3d_editor_plugin.h @@ -0,0 +1,123 @@ +/*************************************************************************/ +/* path_3d_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef PATH_EDITOR_PLUGIN_H +#define PATH_EDITOR_PLUGIN_H + +#include "editor/node_3d_editor_gizmos.h" +#include "scene/3d/path_3d.h" + +class PathNode3DGizmo : public EditorNode3DGizmo { + + GDCLASS(PathNode3DGizmo, EditorNode3DGizmo); + + Path3D *path; + mutable Vector3 original; + mutable float orig_in_length; + mutable float orig_out_length; + +public: + virtual String get_handle_name(int p_idx) const; + virtual Variant get_handle_value(int p_idx); + virtual void set_handle(int p_idx, Camera3D *p_camera, const Point2 &p_point); + virtual void commit_handle(int p_idx, const Variant &p_restore, bool p_cancel = false); + + virtual void redraw(); + PathNode3DGizmo(Path3D *p_path = NULL); +}; + +class PathNode3DGizmoPlugin : public EditorNode3DGizmoPlugin { + + GDCLASS(PathNode3DGizmoPlugin, EditorNode3DGizmoPlugin); + +protected: + Ref create_gizmo(Node3D *p_spatial); + +public: + String get_name() const; + int get_priority() const; + PathNode3DGizmoPlugin(); +}; + +class Path3DEditorPlugin : public EditorPlugin { + + GDCLASS(Path3DEditorPlugin, EditorPlugin); + + Separator *sep; + ToolButton *curve_create; + ToolButton *curve_edit; + ToolButton *curve_del; + ToolButton *curve_close; + MenuButton *handle_menu; + + EditorNode *editor; + + Path3D *path; + + void _mode_changed(int p_idx); + void _close_curve(); + void _handle_option_pressed(int p_option); + bool handle_clicked; + bool mirror_handle_angle; + bool mirror_handle_length; + + enum HandleOption { + HANDLE_OPTION_ANGLE, + HANDLE_OPTION_LENGTH + }; + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + Path3D *get_edited_path() { return path; } + + static Path3DEditorPlugin *singleton; + virtual bool forward_spatial_gui_input(Camera3D *p_camera, const Ref &p_event); + + //virtual bool forward_gui_input(const InputEvent& p_event) { return collision_polygon_editor->forward_gui_input(p_event); } + //virtual Ref create_spatial_gizmo(Spatial *p_spatial); + virtual String get_name() const { return "Path"; } + bool has_main_screen() const { return false; } + virtual void edit(Object *p_object); + virtual bool handles(Object *p_object) const; + virtual void make_visible(bool p_visible); + + bool mirror_angle_enabled() { return mirror_handle_angle; } + bool mirror_length_enabled() { return mirror_handle_length; } + bool is_handle_clicked() { return handle_clicked; } + void set_handle_clicked(bool clicked) { handle_clicked = clicked; } + + Path3DEditorPlugin(EditorNode *p_node); + ~Path3DEditorPlugin(); +}; + +#endif // PATH_EDITOR_PLUGIN_H diff --git a/editor/plugins/path_editor_plugin.cpp b/editor/plugins/path_editor_plugin.cpp deleted file mode 100644 index e54fd17a5b..0000000000 --- a/editor/plugins/path_editor_plugin.cpp +++ /dev/null @@ -1,653 +0,0 @@ -/*************************************************************************/ -/* path_editor_plugin.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "path_editor_plugin.h" - -#include "core/os/keyboard.h" -#include "scene/resources/curve.h" -#include "spatial_editor_plugin.h" - -String PathNode3DGizmo::get_handle_name(int p_idx) const { - - Ref c = path->get_curve(); - if (c.is_null()) - return ""; - - if (p_idx < c->get_point_count()) { - - return TTR("Curve Point #") + itos(p_idx); - } - - p_idx = p_idx - c->get_point_count() + 1; - - int idx = p_idx / 2; - int t = p_idx % 2; - String n = TTR("Curve Point #") + itos(idx); - if (t == 0) - n += " In"; - else - n += " Out"; - - return n; -} -Variant PathNode3DGizmo::get_handle_value(int p_idx) { - - Ref c = path->get_curve(); - if (c.is_null()) - return Variant(); - - if (p_idx < c->get_point_count()) { - - original = c->get_point_position(p_idx); - return original; - } - - p_idx = p_idx - c->get_point_count() + 1; - - int idx = p_idx / 2; - int t = p_idx % 2; - - Vector3 ofs; - if (t == 0) - ofs = c->get_point_in(idx); - else - ofs = c->get_point_out(idx); - - original = ofs + c->get_point_position(idx); - - return ofs; -} -void PathNode3DGizmo::set_handle(int p_idx, Camera3D *p_camera, const Point2 &p_point) { - - Ref c = path->get_curve(); - if (c.is_null()) - return; - - Transform gt = path->get_global_transform(); - Transform gi = gt.affine_inverse(); - Vector3 ray_from = p_camera->project_ray_origin(p_point); - Vector3 ray_dir = p_camera->project_ray_normal(p_point); - - // Setting curve point positions - if (p_idx < c->get_point_count()) { - - Plane p(gt.xform(original), p_camera->get_transform().basis.get_axis(2)); - - Vector3 inters; - - if (p.intersects_ray(ray_from, ray_dir, &inters)) { - - if (Node3DEditor::get_singleton()->is_snap_enabled()) { - float snap = Node3DEditor::get_singleton()->get_translate_snap(); - inters.snap(Vector3(snap, snap, snap)); - } - - Vector3 local = gi.xform(inters); - c->set_point_position(p_idx, local); - } - - return; - } - - p_idx = p_idx - c->get_point_count() + 1; - - int idx = p_idx / 2; - int t = p_idx % 2; - - Vector3 base = c->get_point_position(idx); - - Plane p(gt.xform(original), p_camera->get_transform().basis.get_axis(2)); - - Vector3 inters; - - // Setting curve in/out positions - if (p.intersects_ray(ray_from, ray_dir, &inters)) { - - if (!PathEditorPlugin::singleton->is_handle_clicked()) { - orig_in_length = c->get_point_in(idx).length(); - orig_out_length = c->get_point_out(idx).length(); - PathEditorPlugin::singleton->set_handle_clicked(true); - } - - Vector3 local = gi.xform(inters) - base; - if (Node3DEditor::get_singleton()->is_snap_enabled()) { - float snap = Node3DEditor::get_singleton()->get_translate_snap(); - local.snap(Vector3(snap, snap, snap)); - } - - if (t == 0) { - c->set_point_in(idx, local); - if (PathEditorPlugin::singleton->mirror_angle_enabled()) - c->set_point_out(idx, PathEditorPlugin::singleton->mirror_length_enabled() ? -local : (-local.normalized() * orig_out_length)); - } else { - c->set_point_out(idx, local); - if (PathEditorPlugin::singleton->mirror_angle_enabled()) - c->set_point_in(idx, PathEditorPlugin::singleton->mirror_length_enabled() ? -local : (-local.normalized() * orig_in_length)); - } - } -} - -void PathNode3DGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p_cancel) { - - Ref c = path->get_curve(); - if (c.is_null()) - return; - - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); - - if (p_idx < c->get_point_count()) { - - if (p_cancel) { - - c->set_point_position(p_idx, p_restore); - return; - } - ur->create_action(TTR("Set Curve Point Position")); - ur->add_do_method(c.ptr(), "set_point_position", p_idx, c->get_point_position(p_idx)); - ur->add_undo_method(c.ptr(), "set_point_position", p_idx, p_restore); - ur->commit_action(); - - return; - } - - p_idx = p_idx - c->get_point_count() + 1; - - int idx = p_idx / 2; - int t = p_idx % 2; - - if (t == 0) { - if (p_cancel) { - c->set_point_in(p_idx, p_restore); - return; - } - - ur->create_action(TTR("Set Curve In Position")); - ur->add_do_method(c.ptr(), "set_point_in", idx, c->get_point_in(idx)); - ur->add_undo_method(c.ptr(), "set_point_in", idx, p_restore); - - if (PathEditorPlugin::singleton->mirror_angle_enabled()) { - ur->add_do_method(c.ptr(), "set_point_out", idx, PathEditorPlugin::singleton->mirror_length_enabled() ? -c->get_point_in(idx) : (-c->get_point_in(idx).normalized() * orig_out_length)); - ur->add_undo_method(c.ptr(), "set_point_out", idx, PathEditorPlugin::singleton->mirror_length_enabled() ? -static_cast(p_restore) : (-static_cast(p_restore).normalized() * orig_out_length)); - } - ur->commit_action(); - - } else { - if (p_cancel) { - c->set_point_out(idx, p_restore); - - return; - } - - ur->create_action(TTR("Set Curve Out Position")); - ur->add_do_method(c.ptr(), "set_point_out", idx, c->get_point_out(idx)); - ur->add_undo_method(c.ptr(), "set_point_out", idx, p_restore); - - if (PathEditorPlugin::singleton->mirror_angle_enabled()) { - ur->add_do_method(c.ptr(), "set_point_in", idx, PathEditorPlugin::singleton->mirror_length_enabled() ? -c->get_point_out(idx) : (-c->get_point_out(idx).normalized() * orig_in_length)); - ur->add_undo_method(c.ptr(), "set_point_in", idx, PathEditorPlugin::singleton->mirror_length_enabled() ? -static_cast(p_restore) : (-static_cast(p_restore).normalized() * orig_in_length)); - } - ur->commit_action(); - } -} - -void PathNode3DGizmo::redraw() { - - clear(); - - Ref path_material = gizmo_plugin->get_material("path_material", this); - Ref path_thin_material = gizmo_plugin->get_material("path_thin_material", this); - Ref handles_material = gizmo_plugin->get_material("handles"); - - Ref c = path->get_curve(); - if (c.is_null()) - return; - - Vector v3a = c->tessellate(); - //Vector v3a=c->get_baked_points(); - - int v3s = v3a.size(); - if (v3s == 0) - return; - Vector v3p; - const Vector3 *r = v3a.ptr(); - - // BUG: the following won't work when v3s, avoid drawing as a temporary workaround. - for (int i = 0; i < v3s - 1; i++) { - - v3p.push_back(r[i]); - v3p.push_back(r[i + 1]); - //v3p.push_back(r[i]); - //v3p.push_back(r[i]+Vector3(0,0.2,0)); - } - - if (v3p.size() > 1) { - add_lines(v3p, path_material); - add_collision_segments(v3p); - } - - if (PathEditorPlugin::singleton->get_edited_path() == path) { - v3p.clear(); - Vector handles; - Vector sec_handles; - - for (int i = 0; i < c->get_point_count(); i++) { - - Vector3 p = c->get_point_position(i); - handles.push_back(p); - if (i > 0) { - v3p.push_back(p); - v3p.push_back(p + c->get_point_in(i)); - sec_handles.push_back(p + c->get_point_in(i)); - } - - if (i < c->get_point_count() - 1) { - v3p.push_back(p); - v3p.push_back(p + c->get_point_out(i)); - sec_handles.push_back(p + c->get_point_out(i)); - } - } - - if (v3p.size() > 1) { - add_lines(v3p, path_thin_material); - } - if (handles.size()) { - add_handles(handles, handles_material); - } - if (sec_handles.size()) { - add_handles(sec_handles, handles_material, false, true); - } - } -} - -PathNode3DGizmo::PathNode3DGizmo(Path3D *p_path) { - - path = p_path; - set_spatial_node(p_path); -} - -bool PathEditorPlugin::forward_spatial_gui_input(Camera3D *p_camera, const Ref &p_event) { - - if (!path) - return false; - Ref c = path->get_curve(); - if (c.is_null()) - return false; - Transform gt = path->get_global_transform(); - Transform it = gt.affine_inverse(); - - static const int click_dist = 10; //should make global - - Ref mb = p_event; - - if (mb.is_valid()) { - - Point2 mbpos(mb->get_position().x, mb->get_position().y); - - if (!mb->is_pressed()) - set_handle_clicked(false); - - if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT && (curve_create->is_pressed() || (curve_edit->is_pressed() && mb->get_control()))) { - //click into curve, break it down - Vector v3a = c->tessellate(); - int idx = 0; - int rc = v3a.size(); - int closest_seg = -1; - Vector3 closest_seg_point; - float closest_d = 1e20; - - if (rc >= 2) { - const Vector3 *r = v3a.ptr(); - - if (p_camera->unproject_position(gt.xform(c->get_point_position(0))).distance_to(mbpos) < click_dist) - return false; //nope, existing - - for (int i = 0; i < c->get_point_count() - 1; i++) { - //find the offset and point index of the place to break up - int j = idx; - if (p_camera->unproject_position(gt.xform(c->get_point_position(i + 1))).distance_to(mbpos) < click_dist) - return false; //nope, existing - - while (j < rc && c->get_point_position(i + 1) != r[j]) { - - Vector3 from = r[j]; - Vector3 to = r[j + 1]; - real_t cdist = from.distance_to(to); - from = gt.xform(from); - to = gt.xform(to); - if (cdist > 0) { - Vector2 s[2]; - s[0] = p_camera->unproject_position(from); - s[1] = p_camera->unproject_position(to); - Vector2 inters = Geometry::get_closest_point_to_segment_2d(mbpos, s); - float d = inters.distance_to(mbpos); - - if (d < 10 && d < closest_d) { - - closest_d = d; - closest_seg = i; - Vector3 ray_from = p_camera->project_ray_origin(mbpos); - Vector3 ray_dir = p_camera->project_ray_normal(mbpos); - - Vector3 ra, rb; - Geometry::get_closest_points_between_segments(ray_from, ray_from + ray_dir * 4096, from, to, ra, rb); - - closest_seg_point = it.xform(rb); - } - } - j++; - } - if (idx == j) - idx++; //force next - else - idx = j; //swap - - if (j == rc) - break; - } - } - - UndoRedo *ur = editor->get_undo_redo(); - if (closest_seg != -1) { - //subdivide - - ur->create_action(TTR("Split Path")); - ur->add_do_method(c.ptr(), "add_point", closest_seg_point, Vector3(), Vector3(), closest_seg + 1); - ur->add_undo_method(c.ptr(), "remove_point", closest_seg + 1); - ur->commit_action(); - return true; - - } else { - - Vector3 org; - if (c->get_point_count() == 0) - org = path->get_transform().get_origin(); - else - org = gt.xform(c->get_point_position(c->get_point_count() - 1)); - Plane p(org, p_camera->get_transform().basis.get_axis(2)); - Vector3 ray_from = p_camera->project_ray_origin(mbpos); - Vector3 ray_dir = p_camera->project_ray_normal(mbpos); - - Vector3 inters; - if (p.intersects_ray(ray_from, ray_dir, &inters)) { - - ur->create_action(TTR("Add Point to Curve")); - ur->add_do_method(c.ptr(), "add_point", it.xform(inters), Vector3(), Vector3(), -1); - ur->add_undo_method(c.ptr(), "remove_point", c->get_point_count()); - ur->commit_action(); - return true; - } - - //add new at pos - } - - } else if (mb->is_pressed() && ((mb->get_button_index() == BUTTON_LEFT && curve_del->is_pressed()) || (mb->get_button_index() == BUTTON_RIGHT && curve_edit->is_pressed()))) { - - for (int i = 0; i < c->get_point_count(); i++) { - real_t dist_to_p = p_camera->unproject_position(gt.xform(c->get_point_position(i))).distance_to(mbpos); - real_t dist_to_p_out = p_camera->unproject_position(gt.xform(c->get_point_position(i) + c->get_point_out(i))).distance_to(mbpos); - real_t dist_to_p_in = p_camera->unproject_position(gt.xform(c->get_point_position(i) + c->get_point_in(i))).distance_to(mbpos); - - // Find the offset and point index of the place to break up. - // Also check for the control points. - if (dist_to_p < click_dist) { - - UndoRedo *ur = editor->get_undo_redo(); - ur->create_action(TTR("Remove Path Point")); - ur->add_do_method(c.ptr(), "remove_point", i); - ur->add_undo_method(c.ptr(), "add_point", c->get_point_position(i), c->get_point_in(i), c->get_point_out(i), i); - ur->commit_action(); - return true; - } else if (dist_to_p_out < click_dist) { - - UndoRedo *ur = editor->get_undo_redo(); - ur->create_action(TTR("Remove Out-Control Point")); - ur->add_do_method(c.ptr(), "set_point_out", i, Vector3()); - ur->add_undo_method(c.ptr(), "set_point_out", i, c->get_point_out(i)); - ur->commit_action(); - return true; - } else if (dist_to_p_in < click_dist) { - - UndoRedo *ur = editor->get_undo_redo(); - ur->create_action(TTR("Remove In-Control Point")); - ur->add_do_method(c.ptr(), "set_point_in", i, Vector3()); - ur->add_undo_method(c.ptr(), "set_point_in", i, c->get_point_in(i)); - ur->commit_action(); - return true; - } - } - } - } - - return false; -} - -void PathEditorPlugin::edit(Object *p_object) { - - if (p_object) { - path = Object::cast_to(p_object); - if (path) { - - if (path->get_curve().is_valid()) { - path->get_curve()->emit_signal("changed"); - } - } - } else { - Path3D *pre = path; - path = NULL; - if (pre) { - pre->get_curve()->emit_signal("changed"); - } - } - //collision_polygon_editor->edit(Object::cast_to(p_object)); -} - -bool PathEditorPlugin::handles(Object *p_object) const { - - return p_object->is_class("Path3D"); -} - -void PathEditorPlugin::make_visible(bool p_visible) { - - if (p_visible) { - - curve_create->show(); - curve_edit->show(); - curve_del->show(); - curve_close->show(); - handle_menu->show(); - sep->show(); - } else { - - curve_create->hide(); - curve_edit->hide(); - curve_del->hide(); - curve_close->hide(); - handle_menu->hide(); - sep->hide(); - - { - Path3D *pre = path; - path = NULL; - if (pre && pre->get_curve().is_valid()) { - pre->get_curve()->emit_signal("changed"); - } - } - } -} - -void PathEditorPlugin::_mode_changed(int p_idx) { - - curve_create->set_pressed(p_idx == 0); - curve_edit->set_pressed(p_idx == 1); - curve_del->set_pressed(p_idx == 2); -} - -void PathEditorPlugin::_close_curve() { - - Ref c = path->get_curve(); - if (c.is_null()) - return; - if (c->get_point_count() < 2) - return; - c->add_point(c->get_point_position(0), c->get_point_in(0), c->get_point_out(0)); -} - -void PathEditorPlugin::_handle_option_pressed(int p_option) { - - PopupMenu *pm; - pm = handle_menu->get_popup(); - - switch (p_option) { - case HANDLE_OPTION_ANGLE: { - bool is_checked = pm->is_item_checked(HANDLE_OPTION_ANGLE); - mirror_handle_angle = !is_checked; - pm->set_item_checked(HANDLE_OPTION_ANGLE, mirror_handle_angle); - pm->set_item_disabled(HANDLE_OPTION_LENGTH, !mirror_handle_angle); - } break; - case HANDLE_OPTION_LENGTH: { - bool is_checked = pm->is_item_checked(HANDLE_OPTION_LENGTH); - mirror_handle_length = !is_checked; - pm->set_item_checked(HANDLE_OPTION_LENGTH, mirror_handle_length); - } break; - } -} - -void PathEditorPlugin::_notification(int p_what) { - - if (p_what == NOTIFICATION_ENTER_TREE) { - - curve_create->connect("pressed", callable_mp(this, &PathEditorPlugin::_mode_changed), make_binds(0)); - curve_edit->connect("pressed", callable_mp(this, &PathEditorPlugin::_mode_changed), make_binds(1)); - curve_del->connect("pressed", callable_mp(this, &PathEditorPlugin::_mode_changed), make_binds(2)); - curve_close->connect("pressed", callable_mp(this, &PathEditorPlugin::_close_curve)); - } -} - -void PathEditorPlugin::_bind_methods() { -} - -PathEditorPlugin *PathEditorPlugin::singleton = NULL; - -PathEditorPlugin::PathEditorPlugin(EditorNode *p_node) { - - path = NULL; - editor = p_node; - singleton = this; - mirror_handle_angle = true; - mirror_handle_length = true; - - Ref gizmo_plugin; - gizmo_plugin.instance(); - Node3DEditor::get_singleton()->add_gizmo_plugin(gizmo_plugin); - - sep = memnew(VSeparator); - sep->hide(); - Node3DEditor::get_singleton()->add_control_to_menu_panel(sep); - curve_edit = memnew(ToolButton); - curve_edit->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("CurveEdit", "EditorIcons")); - curve_edit->set_toggle_mode(true); - curve_edit->hide(); - curve_edit->set_focus_mode(Control::FOCUS_NONE); - curve_edit->set_tooltip(TTR("Select Points") + "\n" + TTR("Shift+Drag: Select Control Points") + "\n" + keycode_get_string(KEY_MASK_CMD) + TTR("Click: Add Point") + "\n" + TTR("Right Click: Delete Point")); - Node3DEditor::get_singleton()->add_control_to_menu_panel(curve_edit); - curve_create = memnew(ToolButton); - curve_create->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("CurveCreate", "EditorIcons")); - curve_create->set_toggle_mode(true); - curve_create->hide(); - curve_create->set_focus_mode(Control::FOCUS_NONE); - curve_create->set_tooltip(TTR("Add Point (in empty space)") + "\n" + TTR("Split Segment (in curve)")); - Node3DEditor::get_singleton()->add_control_to_menu_panel(curve_create); - curve_del = memnew(ToolButton); - curve_del->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("CurveDelete", "EditorIcons")); - curve_del->set_toggle_mode(true); - curve_del->hide(); - curve_del->set_focus_mode(Control::FOCUS_NONE); - curve_del->set_tooltip(TTR("Delete Point")); - Node3DEditor::get_singleton()->add_control_to_menu_panel(curve_del); - curve_close = memnew(ToolButton); - curve_close->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("CurveClose", "EditorIcons")); - curve_close->hide(); - curve_close->set_focus_mode(Control::FOCUS_NONE); - curve_close->set_tooltip(TTR("Close Curve")); - Node3DEditor::get_singleton()->add_control_to_menu_panel(curve_close); - - PopupMenu *menu; - - handle_menu = memnew(MenuButton); - handle_menu->set_text(TTR("Options")); - handle_menu->hide(); - Node3DEditor::get_singleton()->add_control_to_menu_panel(handle_menu); - - menu = handle_menu->get_popup(); - menu->add_check_item(TTR("Mirror Handle Angles")); - menu->set_item_checked(HANDLE_OPTION_ANGLE, mirror_handle_angle); - menu->add_check_item(TTR("Mirror Handle Lengths")); - menu->set_item_checked(HANDLE_OPTION_LENGTH, mirror_handle_length); - menu->connect("id_pressed", callable_mp(this, &PathEditorPlugin::_handle_option_pressed)); - - curve_edit->set_pressed(true); - /* - collision_polygon_editor = memnew( PathEditor(p_node) ); - editor->get_viewport()->add_child(collision_polygon_editor); - collision_polygon_editor->set_margin(MARGIN_LEFT,200); - collision_polygon_editor->set_margin(MARGIN_RIGHT,230); - collision_polygon_editor->set_margin(MARGIN_TOP,0); - collision_polygon_editor->set_margin(MARGIN_BOTTOM,10); - collision_polygon_editor->hide(); - */ -} - -PathEditorPlugin::~PathEditorPlugin() { -} - -Ref PathNode3DGizmoPlugin::create_gizmo(Node3D *p_spatial) { - Ref ref; - - Path3D *path = Object::cast_to(p_spatial); - if (path) ref = Ref(memnew(PathNode3DGizmo(path))); - - return ref; -} - -String PathNode3DGizmoPlugin::get_name() const { - return "Path"; -} - -int PathNode3DGizmoPlugin::get_priority() const { - return -1; -} - -PathNode3DGizmoPlugin::PathNode3DGizmoPlugin() { - - Color path_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/path", Color(0.5, 0.5, 1.0, 0.8)); - create_material("path_material", path_color); - create_material("path_thin_material", Color(0.5, 0.5, 0.5)); - create_handle_material("handles"); -} diff --git a/editor/plugins/path_editor_plugin.h b/editor/plugins/path_editor_plugin.h deleted file mode 100644 index 0a27e1068e..0000000000 --- a/editor/plugins/path_editor_plugin.h +++ /dev/null @@ -1,123 +0,0 @@ -/*************************************************************************/ -/* path_editor_plugin.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef PATH_EDITOR_PLUGIN_H -#define PATH_EDITOR_PLUGIN_H - -#include "editor/spatial_editor_gizmos.h" -#include "scene/3d/path_3d.h" - -class PathNode3DGizmo : public EditorNode3DGizmo { - - GDCLASS(PathNode3DGizmo, EditorNode3DGizmo); - - Path3D *path; - mutable Vector3 original; - mutable float orig_in_length; - mutable float orig_out_length; - -public: - virtual String get_handle_name(int p_idx) const; - virtual Variant get_handle_value(int p_idx); - virtual void set_handle(int p_idx, Camera3D *p_camera, const Point2 &p_point); - virtual void commit_handle(int p_idx, const Variant &p_restore, bool p_cancel = false); - - virtual void redraw(); - PathNode3DGizmo(Path3D *p_path = NULL); -}; - -class PathNode3DGizmoPlugin : public EditorNode3DGizmoPlugin { - - GDCLASS(PathNode3DGizmoPlugin, EditorNode3DGizmoPlugin); - -protected: - Ref create_gizmo(Node3D *p_spatial); - -public: - String get_name() const; - int get_priority() const; - PathNode3DGizmoPlugin(); -}; - -class PathEditorPlugin : public EditorPlugin { - - GDCLASS(PathEditorPlugin, EditorPlugin); - - Separator *sep; - ToolButton *curve_create; - ToolButton *curve_edit; - ToolButton *curve_del; - ToolButton *curve_close; - MenuButton *handle_menu; - - EditorNode *editor; - - Path3D *path; - - void _mode_changed(int p_idx); - void _close_curve(); - void _handle_option_pressed(int p_option); - bool handle_clicked; - bool mirror_handle_angle; - bool mirror_handle_length; - - enum HandleOption { - HANDLE_OPTION_ANGLE, - HANDLE_OPTION_LENGTH - }; - -protected: - void _notification(int p_what); - static void _bind_methods(); - -public: - Path3D *get_edited_path() { return path; } - - static PathEditorPlugin *singleton; - virtual bool forward_spatial_gui_input(Camera3D *p_camera, const Ref &p_event); - - //virtual bool forward_gui_input(const InputEvent& p_event) { return collision_polygon_editor->forward_gui_input(p_event); } - //virtual Ref create_spatial_gizmo(Spatial *p_spatial); - virtual String get_name() const { return "Path"; } - bool has_main_screen() const { return false; } - virtual void edit(Object *p_object); - virtual bool handles(Object *p_object) const; - virtual void make_visible(bool p_visible); - - bool mirror_angle_enabled() { return mirror_handle_angle; } - bool mirror_length_enabled() { return mirror_handle_length; } - bool is_handle_clicked() { return handle_clicked; } - void set_handle_clicked(bool clicked) { handle_clicked = clicked; } - - PathEditorPlugin(EditorNode *p_node); - ~PathEditorPlugin(); -}; - -#endif // PATH_EDITOR_PLUGIN_H diff --git a/editor/plugins/physical_bone_3d_editor_plugin.cpp b/editor/plugins/physical_bone_3d_editor_plugin.cpp new file mode 100644 index 0000000000..dd6d7b109b --- /dev/null +++ b/editor/plugins/physical_bone_3d_editor_plugin.cpp @@ -0,0 +1,113 @@ +/*************************************************************************/ +/* physical_bone_3d_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "physical_bone_3d_editor_plugin.h" + +#include "editor/plugins/node_3d_editor_plugin.h" +#include "scene/3d/physics_body_3d.h" + +void PhysicalBone3DEditor::_bind_methods() { +} + +void PhysicalBone3DEditor::_on_toggle_button_transform_joint(bool p_is_pressed) { + + _set_move_joint(); +} + +void PhysicalBone3DEditor::_set_move_joint() { + if (selected) { + selected->_set_gizmo_move_joint(button_transform_joint->is_pressed()); + } +} + +PhysicalBone3DEditor::PhysicalBone3DEditor(EditorNode *p_editor) : + editor(p_editor), + selected(NULL) { + + spatial_editor_hb = memnew(HBoxContainer); + spatial_editor_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL); + spatial_editor_hb->set_alignment(BoxContainer::ALIGN_BEGIN); + Node3DEditor::get_singleton()->add_control_to_menu_panel(spatial_editor_hb); + + spatial_editor_hb->add_child(memnew(VSeparator)); + + button_transform_joint = memnew(ToolButton); + spatial_editor_hb->add_child(button_transform_joint); + + button_transform_joint->set_text(TTR("Move Joint")); + button_transform_joint->set_icon(Node3DEditor::get_singleton()->get_theme_icon("PhysicalBone3D", "EditorIcons")); + button_transform_joint->set_toggle_mode(true); + button_transform_joint->connect("toggled", callable_mp(this, &PhysicalBone3DEditor::_on_toggle_button_transform_joint)); + + hide(); +} + +PhysicalBone3DEditor::~PhysicalBone3DEditor() {} + +void PhysicalBone3DEditor::set_selected(PhysicalBone3D *p_pb) { + + button_transform_joint->set_pressed(false); + + _set_move_joint(); + selected = p_pb; + _set_move_joint(); +} + +void PhysicalBone3DEditor::hide() { + spatial_editor_hb->hide(); +} + +void PhysicalBone3DEditor::show() { + spatial_editor_hb->show(); +} + +PhysicalBone3DEditorPlugin::PhysicalBone3DEditorPlugin(EditorNode *p_editor) : + editor(p_editor), + selected(NULL), + physical_bone_editor(editor) {} + +void PhysicalBone3DEditorPlugin::make_visible(bool p_visible) { + if (p_visible) { + + physical_bone_editor.show(); + } else { + + physical_bone_editor.hide(); + physical_bone_editor.set_selected(NULL); + selected = NULL; + } +} + +void PhysicalBone3DEditorPlugin::edit(Object *p_node) { + selected = static_cast(p_node); // Trust it + ERR_FAIL_COND(!selected); + + physical_bone_editor.set_selected(selected); +} diff --git a/editor/plugins/physical_bone_3d_editor_plugin.h b/editor/plugins/physical_bone_3d_editor_plugin.h new file mode 100644 index 0000000000..74932710d6 --- /dev/null +++ b/editor/plugins/physical_bone_3d_editor_plugin.h @@ -0,0 +1,78 @@ +/*************************************************************************/ +/* physical_bone_3d_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef PHYSICAL_BONE_PLUGIN_H +#define PHYSICAL_BONE_PLUGIN_H + +#include "editor/editor_node.h" + +class PhysicalBone3DEditor : public Object { + GDCLASS(PhysicalBone3DEditor, Object); + + EditorNode *editor; + HBoxContainer *spatial_editor_hb; + ToolButton *button_transform_joint; + + PhysicalBone3D *selected; + +protected: + static void _bind_methods(); + +private: + void _on_toggle_button_transform_joint(bool p_is_pressed); + void _set_move_joint(); + +public: + PhysicalBone3DEditor(EditorNode *p_editor); + ~PhysicalBone3DEditor(); + + void set_selected(PhysicalBone3D *p_pb); + + void hide(); + void show(); +}; + +class PhysicalBone3DEditorPlugin : public EditorPlugin { + GDCLASS(PhysicalBone3DEditorPlugin, EditorPlugin); + + EditorNode *editor; + PhysicalBone3D *selected; + PhysicalBone3DEditor physical_bone_editor; + +public: + virtual String get_name() const { return "PhysicalBone3D"; } + virtual bool handles(Object *p_object) const { return p_object->is_class("PhysicalBone3D"); } + virtual void make_visible(bool p_visible); + virtual void edit(Object *p_node); + + PhysicalBone3DEditorPlugin(EditorNode *p_editor); +}; + +#endif diff --git a/editor/plugins/physical_bone_plugin.cpp b/editor/plugins/physical_bone_plugin.cpp deleted file mode 100644 index d827472353..0000000000 --- a/editor/plugins/physical_bone_plugin.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/*************************************************************************/ -/* physical_bone_plugin.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "physical_bone_plugin.h" -#include "editor/plugins/spatial_editor_plugin.h" -#include "scene/3d/physics_body_3d.h" - -void PhysicalBoneEditor::_bind_methods() { -} - -void PhysicalBoneEditor::_on_toggle_button_transform_joint(bool p_is_pressed) { - - _set_move_joint(); -} - -void PhysicalBoneEditor::_set_move_joint() { - if (selected) { - selected->_set_gizmo_move_joint(button_transform_joint->is_pressed()); - } -} - -PhysicalBoneEditor::PhysicalBoneEditor(EditorNode *p_editor) : - editor(p_editor), - selected(NULL) { - - spatial_editor_hb = memnew(HBoxContainer); - spatial_editor_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL); - spatial_editor_hb->set_alignment(BoxContainer::ALIGN_BEGIN); - Node3DEditor::get_singleton()->add_control_to_menu_panel(spatial_editor_hb); - - spatial_editor_hb->add_child(memnew(VSeparator)); - - button_transform_joint = memnew(ToolButton); - spatial_editor_hb->add_child(button_transform_joint); - - button_transform_joint->set_text(TTR("Move Joint")); - button_transform_joint->set_icon(Node3DEditor::get_singleton()->get_theme_icon("PhysicalBone", "EditorIcons")); - button_transform_joint->set_toggle_mode(true); - button_transform_joint->connect("toggled", callable_mp(this, &PhysicalBoneEditor::_on_toggle_button_transform_joint)); - - hide(); -} - -PhysicalBoneEditor::~PhysicalBoneEditor() {} - -void PhysicalBoneEditor::set_selected(PhysicalBone3D *p_pb) { - - button_transform_joint->set_pressed(false); - - _set_move_joint(); - selected = p_pb; - _set_move_joint(); -} - -void PhysicalBoneEditor::hide() { - spatial_editor_hb->hide(); -} - -void PhysicalBoneEditor::show() { - spatial_editor_hb->show(); -} - -PhysicalBonePlugin::PhysicalBonePlugin(EditorNode *p_editor) : - editor(p_editor), - selected(NULL), - physical_bone_editor(editor) {} - -void PhysicalBonePlugin::make_visible(bool p_visible) { - if (p_visible) { - - physical_bone_editor.show(); - } else { - - physical_bone_editor.hide(); - physical_bone_editor.set_selected(NULL); - selected = NULL; - } -} - -void PhysicalBonePlugin::edit(Object *p_node) { - selected = static_cast(p_node); // Trust it - ERR_FAIL_COND(!selected); - - physical_bone_editor.set_selected(selected); -} diff --git a/editor/plugins/physical_bone_plugin.h b/editor/plugins/physical_bone_plugin.h deleted file mode 100644 index 0d4aa7138d..0000000000 --- a/editor/plugins/physical_bone_plugin.h +++ /dev/null @@ -1,78 +0,0 @@ -/*************************************************************************/ -/* physical_bone_plugin.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef PHYSICAL_BONE_PLUGIN_H -#define PHYSICAL_BONE_PLUGIN_H - -#include "editor/editor_node.h" - -class PhysicalBoneEditor : public Object { - GDCLASS(PhysicalBoneEditor, Object); - - EditorNode *editor; - HBoxContainer *spatial_editor_hb; - ToolButton *button_transform_joint; - - PhysicalBone3D *selected; - -protected: - static void _bind_methods(); - -private: - void _on_toggle_button_transform_joint(bool p_is_pressed); - void _set_move_joint(); - -public: - PhysicalBoneEditor(EditorNode *p_editor); - ~PhysicalBoneEditor(); - - void set_selected(PhysicalBone3D *p_pb); - - void hide(); - void show(); -}; - -class PhysicalBonePlugin : public EditorPlugin { - GDCLASS(PhysicalBonePlugin, EditorPlugin); - - EditorNode *editor; - PhysicalBone3D *selected; - PhysicalBoneEditor physical_bone_editor; - -public: - virtual String get_name() const { return "PhysicalBone3D"; } - virtual bool handles(Object *p_object) const { return p_object->is_class("PhysicalBone3D"); } - virtual void make_visible(bool p_visible); - virtual void edit(Object *p_node); - - PhysicalBonePlugin(EditorNode *p_editor); -}; - -#endif diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp new file mode 100644 index 0000000000..ae289dae4b --- /dev/null +++ b/editor/plugins/skeleton_3d_editor_plugin.cpp @@ -0,0 +1,195 @@ +/*************************************************************************/ +/* skeleton_3d_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "skeleton_3d_editor_plugin.h" + +#include "node_3d_editor_plugin.h" +#include "scene/3d/collision_shape_3d.h" +#include "scene/3d/physics_body_3d.h" +#include "scene/3d/physics_joint_3d.h" +#include "scene/resources/capsule_shape_3d.h" +#include "scene/resources/sphere_shape_3d.h" + +void Skeleton3DEditor::_on_click_option(int p_option) { + if (!skeleton) { + return; + } + + switch (p_option) { + case MENU_OPTION_CREATE_PHYSICAL_SKELETON: { + create_physical_skeleton(); + } break; + } +} + +void Skeleton3DEditor::create_physical_skeleton() { + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Node *owner = skeleton == get_tree()->get_edited_scene_root() ? skeleton : skeleton->get_owner(); + + const int bc = skeleton->get_bone_count(); + + if (!bc) { + return; + } + + Vector bones_infos; + bones_infos.resize(bc); + + for (int bone_id = 0; bc > bone_id; ++bone_id) { + + const int parent = skeleton->get_bone_parent(bone_id); + + if (parent < 0) { + + bones_infos.write[bone_id].relative_rest = skeleton->get_bone_rest(bone_id); + + } else { + + const int parent_parent = skeleton->get_bone_parent(parent); + + bones_infos.write[bone_id].relative_rest = bones_infos[parent].relative_rest * skeleton->get_bone_rest(bone_id); + + /// create physical bone on parent + if (!bones_infos[parent].physical_bone) { + + bones_infos.write[parent].physical_bone = create_physical_bone(parent, bone_id, bones_infos); + + ur->create_action(TTR("Create physical bones")); + ur->add_do_method(skeleton, "add_child", bones_infos[parent].physical_bone); + ur->add_do_reference(bones_infos[parent].physical_bone); + ur->add_undo_method(skeleton, "remove_child", bones_infos[parent].physical_bone); + ur->commit_action(); + + bones_infos[parent].physical_bone->set_bone_name(skeleton->get_bone_name(parent)); + bones_infos[parent].physical_bone->set_owner(owner); + bones_infos[parent].physical_bone->get_child(0)->set_owner(owner); // set shape owner + + /// Create joint between parent of parent + if (-1 != parent_parent) { + + bones_infos[parent].physical_bone->set_joint_type(PhysicalBone3D::JOINT_TYPE_PIN); + } + } + } + } +} + +PhysicalBone3D *Skeleton3DEditor::create_physical_bone(int bone_id, int bone_child_id, const Vector &bones_infos) { + + const Transform child_rest = skeleton->get_bone_rest(bone_child_id); + + const real_t half_height(child_rest.origin.length() * 0.5); + const real_t radius(half_height * 0.2); + + CapsuleShape3D *bone_shape_capsule = memnew(CapsuleShape3D); + bone_shape_capsule->set_height((half_height - radius) * 2); + bone_shape_capsule->set_radius(radius); + + CollisionShape3D *bone_shape = memnew(CollisionShape3D); + bone_shape->set_shape(bone_shape_capsule); + + Transform body_transform; + body_transform.set_look_at(Vector3(0, 0, 0), child_rest.origin, Vector3(0, 1, 0)); + body_transform.origin = body_transform.basis.xform(Vector3(0, 0, -half_height)); + + Transform joint_transform; + joint_transform.origin = Vector3(0, 0, half_height); + + PhysicalBone3D *physical_bone = memnew(PhysicalBone3D); + physical_bone->add_child(bone_shape); + physical_bone->set_name("Physical Bone " + skeleton->get_bone_name(bone_id)); + physical_bone->set_body_offset(body_transform); + physical_bone->set_joint_offset(joint_transform); + return physical_bone; +} + +void Skeleton3DEditor::edit(Skeleton3D *p_node) { + + skeleton = p_node; +} + +void Skeleton3DEditor::_notification(int p_what) { + if (p_what == NOTIFICATION_ENTER_TREE) { + get_tree()->connect("node_removed", callable_mp(this, &Skeleton3DEditor::_node_removed)); + } +} + +void Skeleton3DEditor::_node_removed(Node *p_node) { + + if (p_node == skeleton) { + skeleton = NULL; + options->hide(); + } +} + +void Skeleton3DEditor::_bind_methods() { +} + +Skeleton3DEditor::Skeleton3DEditor() { + skeleton = NULL; + options = memnew(MenuButton); + Node3DEditor::get_singleton()->add_control_to_menu_panel(options); + + options->set_text(TTR("Skeleton3D")); + options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("Skeleton3D", "EditorIcons")); + + options->get_popup()->add_item(TTR("Create physical skeleton"), MENU_OPTION_CREATE_PHYSICAL_SKELETON); + + options->get_popup()->connect("id_pressed", callable_mp(this, &Skeleton3DEditor::_on_click_option)); + options->hide(); +} + +Skeleton3DEditor::~Skeleton3DEditor() {} + +void Skeleton3DEditorPlugin::edit(Object *p_object) { + skeleton_editor->edit(Object::cast_to(p_object)); +} + +bool Skeleton3DEditorPlugin::handles(Object *p_object) const { + return p_object->is_class("Skeleton3D"); +} + +void Skeleton3DEditorPlugin::make_visible(bool p_visible) { + if (p_visible) { + skeleton_editor->options->show(); + } else { + + skeleton_editor->options->hide(); + skeleton_editor->edit(NULL); + } +} + +Skeleton3DEditorPlugin::Skeleton3DEditorPlugin(EditorNode *p_node) { + editor = p_node; + skeleton_editor = memnew(Skeleton3DEditor); + editor->get_viewport()->add_child(skeleton_editor); +} + +Skeleton3DEditorPlugin::~Skeleton3DEditorPlugin() {} diff --git a/editor/plugins/skeleton_3d_editor_plugin.h b/editor/plugins/skeleton_3d_editor_plugin.h new file mode 100644 index 0000000000..606e04bb79 --- /dev/null +++ b/editor/plugins/skeleton_3d_editor_plugin.h @@ -0,0 +1,96 @@ +/*************************************************************************/ +/* skeleton_3d_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SKELETON_3D_EDITOR_PLUGIN_H +#define SKELETON_3D_EDITOR_PLUGIN_H + +#include "editor/editor_node.h" +#include "editor/editor_plugin.h" +#include "scene/3d/skeleton_3d.h" + +class PhysicalBone3D; +class Joint3D; + +class Skeleton3DEditor : public Node { + GDCLASS(Skeleton3DEditor, Node); + + enum Menu { + MENU_OPTION_CREATE_PHYSICAL_SKELETON + }; + + struct BoneInfo { + PhysicalBone3D *physical_bone; + Transform relative_rest; // Relative to skeleton node + BoneInfo() : + physical_bone(NULL) {} + }; + + Skeleton3D *skeleton; + + MenuButton *options; + + void _on_click_option(int p_option); + + friend class Skeleton3DEditorPlugin; + +protected: + void _notification(int p_what); + void _node_removed(Node *p_node); + static void _bind_methods(); + + void create_physical_skeleton(); + PhysicalBone3D *create_physical_bone(int bone_id, int bone_child_id, const Vector &bones_infos); + +public: + void edit(Skeleton3D *p_node); + + Skeleton3DEditor(); + ~Skeleton3DEditor(); +}; + +class Skeleton3DEditorPlugin : public EditorPlugin { + + GDCLASS(Skeleton3DEditorPlugin, EditorPlugin); + + EditorNode *editor; + Skeleton3DEditor *skeleton_editor; + +public: + virtual String get_name() const { return "Skeleton3D"; } + virtual bool has_main_screen() const { return false; } + virtual void edit(Object *p_object); + virtual bool handles(Object *p_object) const; + virtual void make_visible(bool p_visible); + + Skeleton3DEditorPlugin(EditorNode *p_node); + ~Skeleton3DEditorPlugin(); +}; + +#endif // SKELETON_3D_EDITOR_PLUGIN_H diff --git a/editor/plugins/skeleton_editor_plugin.cpp b/editor/plugins/skeleton_editor_plugin.cpp deleted file mode 100644 index 941461bb64..0000000000 --- a/editor/plugins/skeleton_editor_plugin.cpp +++ /dev/null @@ -1,195 +0,0 @@ -/*************************************************************************/ -/* skeleton_editor_plugin.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "skeleton_editor_plugin.h" - -#include "scene/3d/collision_shape_3d.h" -#include "scene/3d/physics_body_3d.h" -#include "scene/3d/physics_joint_3d.h" -#include "scene/resources/capsule_shape_3d.h" -#include "scene/resources/sphere_shape_3d.h" -#include "spatial_editor_plugin.h" - -void SkeletonEditor::_on_click_option(int p_option) { - if (!skeleton) { - return; - } - - switch (p_option) { - case MENU_OPTION_CREATE_PHYSICAL_SKELETON: { - create_physical_skeleton(); - } break; - } -} - -void SkeletonEditor::create_physical_skeleton() { - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); - Node *owner = skeleton == get_tree()->get_edited_scene_root() ? skeleton : skeleton->get_owner(); - - const int bc = skeleton->get_bone_count(); - - if (!bc) { - return; - } - - Vector bones_infos; - bones_infos.resize(bc); - - for (int bone_id = 0; bc > bone_id; ++bone_id) { - - const int parent = skeleton->get_bone_parent(bone_id); - - if (parent < 0) { - - bones_infos.write[bone_id].relative_rest = skeleton->get_bone_rest(bone_id); - - } else { - - const int parent_parent = skeleton->get_bone_parent(parent); - - bones_infos.write[bone_id].relative_rest = bones_infos[parent].relative_rest * skeleton->get_bone_rest(bone_id); - - /// create physical bone on parent - if (!bones_infos[parent].physical_bone) { - - bones_infos.write[parent].physical_bone = create_physical_bone(parent, bone_id, bones_infos); - - ur->create_action(TTR("Create physical bones")); - ur->add_do_method(skeleton, "add_child", bones_infos[parent].physical_bone); - ur->add_do_reference(bones_infos[parent].physical_bone); - ur->add_undo_method(skeleton, "remove_child", bones_infos[parent].physical_bone); - ur->commit_action(); - - bones_infos[parent].physical_bone->set_bone_name(skeleton->get_bone_name(parent)); - bones_infos[parent].physical_bone->set_owner(owner); - bones_infos[parent].physical_bone->get_child(0)->set_owner(owner); // set shape owner - - /// Create joint between parent of parent - if (-1 != parent_parent) { - - bones_infos[parent].physical_bone->set_joint_type(PhysicalBone3D::JOINT_TYPE_PIN); - } - } - } - } -} - -PhysicalBone3D *SkeletonEditor::create_physical_bone(int bone_id, int bone_child_id, const Vector &bones_infos) { - - const Transform child_rest = skeleton->get_bone_rest(bone_child_id); - - const real_t half_height(child_rest.origin.length() * 0.5); - const real_t radius(half_height * 0.2); - - CapsuleShape3D *bone_shape_capsule = memnew(CapsuleShape3D); - bone_shape_capsule->set_height((half_height - radius) * 2); - bone_shape_capsule->set_radius(radius); - - CollisionShape3D *bone_shape = memnew(CollisionShape3D); - bone_shape->set_shape(bone_shape_capsule); - - Transform body_transform; - body_transform.set_look_at(Vector3(0, 0, 0), child_rest.origin, Vector3(0, 1, 0)); - body_transform.origin = body_transform.basis.xform(Vector3(0, 0, -half_height)); - - Transform joint_transform; - joint_transform.origin = Vector3(0, 0, half_height); - - PhysicalBone3D *physical_bone = memnew(PhysicalBone3D); - physical_bone->add_child(bone_shape); - physical_bone->set_name("Physical Bone " + skeleton->get_bone_name(bone_id)); - physical_bone->set_body_offset(body_transform); - physical_bone->set_joint_offset(joint_transform); - return physical_bone; -} - -void SkeletonEditor::edit(Skeleton3D *p_node) { - - skeleton = p_node; -} - -void SkeletonEditor::_notification(int p_what) { - if (p_what == NOTIFICATION_ENTER_TREE) { - get_tree()->connect("node_removed", callable_mp(this, &SkeletonEditor::_node_removed)); - } -} - -void SkeletonEditor::_node_removed(Node *p_node) { - - if (p_node == skeleton) { - skeleton = NULL; - options->hide(); - } -} - -void SkeletonEditor::_bind_methods() { -} - -SkeletonEditor::SkeletonEditor() { - skeleton = NULL; - options = memnew(MenuButton); - Node3DEditor::get_singleton()->add_control_to_menu_panel(options); - - options->set_text(TTR("Skeleton")); - options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("Skeleton3D", "EditorIcons")); - - options->get_popup()->add_item(TTR("Create physical skeleton"), MENU_OPTION_CREATE_PHYSICAL_SKELETON); - - options->get_popup()->connect("id_pressed", callable_mp(this, &SkeletonEditor::_on_click_option)); - options->hide(); -} - -SkeletonEditor::~SkeletonEditor() {} - -void SkeletonEditorPlugin::edit(Object *p_object) { - skeleton_editor->edit(Object::cast_to(p_object)); -} - -bool SkeletonEditorPlugin::handles(Object *p_object) const { - return p_object->is_class("Skeleton3D"); -} - -void SkeletonEditorPlugin::make_visible(bool p_visible) { - if (p_visible) { - skeleton_editor->options->show(); - } else { - - skeleton_editor->options->hide(); - skeleton_editor->edit(NULL); - } -} - -SkeletonEditorPlugin::SkeletonEditorPlugin(EditorNode *p_node) { - editor = p_node; - skeleton_editor = memnew(SkeletonEditor); - editor->get_viewport()->add_child(skeleton_editor); -} - -SkeletonEditorPlugin::~SkeletonEditorPlugin() {} diff --git a/editor/plugins/skeleton_editor_plugin.h b/editor/plugins/skeleton_editor_plugin.h deleted file mode 100644 index 91e244285d..0000000000 --- a/editor/plugins/skeleton_editor_plugin.h +++ /dev/null @@ -1,96 +0,0 @@ -/*************************************************************************/ -/* skeleton_editor_plugin.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef SKELETON_EDITOR_PLUGIN_H -#define SKELETON_EDITOR_PLUGIN_H - -#include "editor/editor_node.h" -#include "editor/editor_plugin.h" -#include "scene/3d/skeleton_3d.h" - -class PhysicalBone3D; -class Joint3D; - -class SkeletonEditor : public Node { - GDCLASS(SkeletonEditor, Node); - - enum Menu { - MENU_OPTION_CREATE_PHYSICAL_SKELETON - }; - - struct BoneInfo { - PhysicalBone3D *physical_bone; - Transform relative_rest; // Relative to skeleton node - BoneInfo() : - physical_bone(NULL) {} - }; - - Skeleton3D *skeleton; - - MenuButton *options; - - void _on_click_option(int p_option); - - friend class SkeletonEditorPlugin; - -protected: - void _notification(int p_what); - void _node_removed(Node *p_node); - static void _bind_methods(); - - void create_physical_skeleton(); - PhysicalBone3D *create_physical_bone(int bone_id, int bone_child_id, const Vector &bones_infos); - -public: - void edit(Skeleton3D *p_node); - - SkeletonEditor(); - ~SkeletonEditor(); -}; - -class SkeletonEditorPlugin : public EditorPlugin { - - GDCLASS(SkeletonEditorPlugin, EditorPlugin); - - EditorNode *editor; - SkeletonEditor *skeleton_editor; - -public: - virtual String get_name() const { return "Skeleton"; } - virtual bool has_main_screen() const { return false; } - virtual void edit(Object *p_object); - virtual bool handles(Object *p_object) const; - virtual void make_visible(bool p_visible); - - SkeletonEditorPlugin(EditorNode *p_node); - ~SkeletonEditorPlugin(); -}; - -#endif // SKELETON_EDITOR_PLUGIN_H diff --git a/editor/plugins/skeleton_ik_3d_editor_plugin.cpp b/editor/plugins/skeleton_ik_3d_editor_plugin.cpp new file mode 100644 index 0000000000..4b6a86bb5a --- /dev/null +++ b/editor/plugins/skeleton_ik_3d_editor_plugin.cpp @@ -0,0 +1,96 @@ +/*************************************************************************/ +/* skeleton_ik_3d_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "skeleton_ik_3d_editor_plugin.h" + +#include "scene/3d/skeleton_ik_3d.h" + +void SkeletonIK3DEditorPlugin::_play() { + + if (!skeleton_ik) + return; + + if (!skeleton_ik->get_parent_skeleton()) + return; + + if (play_btn->is_pressed()) { + skeleton_ik->start(); + } else { + skeleton_ik->stop(); + skeleton_ik->get_parent_skeleton()->clear_bones_global_pose_override(); + } +} + +void SkeletonIK3DEditorPlugin::edit(Object *p_object) { + + if (p_object != skeleton_ik) { + if (skeleton_ik) { + play_btn->set_pressed(false); + _play(); + } + } + + SkeletonIK3D *s = Object::cast_to(p_object); + if (!s) + return; + + skeleton_ik = s; +} + +bool SkeletonIK3DEditorPlugin::handles(Object *p_object) const { + + return p_object->is_class("SkeletonIK3D"); +} + +void SkeletonIK3DEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) + play_btn->show(); + else + play_btn->hide(); +} + +void SkeletonIK3DEditorPlugin::_bind_methods() { +} + +SkeletonIK3DEditorPlugin::SkeletonIK3DEditorPlugin(EditorNode *p_node) { + + editor = p_node; + play_btn = memnew(Button); + play_btn->set_icon(editor->get_gui_base()->get_theme_icon("Play", "EditorIcons")); + play_btn->set_text(TTR("Play IK")); + play_btn->set_toggle_mode(true); + play_btn->hide(); + play_btn->connect("pressed", callable_mp(this, &SkeletonIK3DEditorPlugin::_play)); + add_control_to_container(CONTAINER_SPATIAL_EDITOR_MENU, play_btn); + skeleton_ik = NULL; +} + +SkeletonIK3DEditorPlugin::~SkeletonIK3DEditorPlugin() {} diff --git a/editor/plugins/skeleton_ik_3d_editor_plugin.h b/editor/plugins/skeleton_ik_3d_editor_plugin.h new file mode 100644 index 0000000000..1466d670ba --- /dev/null +++ b/editor/plugins/skeleton_ik_3d_editor_plugin.h @@ -0,0 +1,64 @@ +/*************************************************************************/ +/* skeleton_ik_3d_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SKELETON_IK_3D_EDITOR_PLUGIN_H +#define SKELETON_IK_3D_EDITOR_PLUGIN_H + +#include "editor/editor_node.h" +#include "editor/editor_plugin.h" + +class SkeletonIK3D; + +class SkeletonIK3DEditorPlugin : public EditorPlugin { + + GDCLASS(SkeletonIK3DEditorPlugin, EditorPlugin); + + SkeletonIK3D *skeleton_ik; + + Button *play_btn; + EditorNode *editor; + + void _play(); + +protected: + static void _bind_methods(); + +public: + virtual String get_name() const { return "SkeletonIK"; } + bool has_main_screen() const { return false; } + virtual void edit(Object *p_object); + virtual bool handles(Object *p_object) const; + virtual void make_visible(bool p_visible); + + SkeletonIK3DEditorPlugin(EditorNode *p_node); + ~SkeletonIK3DEditorPlugin(); +}; + +#endif // SKELETON_IK_3D_EDITOR_PLUGIN_H diff --git a/editor/plugins/skeleton_ik_editor_plugin.cpp b/editor/plugins/skeleton_ik_editor_plugin.cpp deleted file mode 100644 index 548cbe9470..0000000000 --- a/editor/plugins/skeleton_ik_editor_plugin.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/*************************************************************************/ -/* skeleton_ik_editor_plugin.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "skeleton_ik_editor_plugin.h" - -#include "scene/animation/skeleton_ik.h" - -void SkeletonIKEditorPlugin::_play() { - - if (!skeleton_ik) - return; - - if (!skeleton_ik->get_parent_skeleton()) - return; - - if (play_btn->is_pressed()) { - skeleton_ik->start(); - } else { - skeleton_ik->stop(); - skeleton_ik->get_parent_skeleton()->clear_bones_global_pose_override(); - } -} - -void SkeletonIKEditorPlugin::edit(Object *p_object) { - - if (p_object != skeleton_ik) { - if (skeleton_ik) { - play_btn->set_pressed(false); - _play(); - } - } - - SkeletonIK3D *s = Object::cast_to(p_object); - if (!s) - return; - - skeleton_ik = s; -} - -bool SkeletonIKEditorPlugin::handles(Object *p_object) const { - - return p_object->is_class("SkeletonIK3D"); -} - -void SkeletonIKEditorPlugin::make_visible(bool p_visible) { - - if (p_visible) - play_btn->show(); - else - play_btn->hide(); -} - -void SkeletonIKEditorPlugin::_bind_methods() { -} - -SkeletonIKEditorPlugin::SkeletonIKEditorPlugin(EditorNode *p_node) { - - editor = p_node; - play_btn = memnew(Button); - play_btn->set_icon(editor->get_gui_base()->get_theme_icon("Play", "EditorIcons")); - play_btn->set_text(TTR("Play IK")); - play_btn->set_toggle_mode(true); - play_btn->hide(); - play_btn->connect("pressed", callable_mp(this, &SkeletonIKEditorPlugin::_play)); - add_control_to_container(CONTAINER_SPATIAL_EDITOR_MENU, play_btn); - skeleton_ik = NULL; -} - -SkeletonIKEditorPlugin::~SkeletonIKEditorPlugin() {} diff --git a/editor/plugins/skeleton_ik_editor_plugin.h b/editor/plugins/skeleton_ik_editor_plugin.h deleted file mode 100644 index 8895a548fe..0000000000 --- a/editor/plugins/skeleton_ik_editor_plugin.h +++ /dev/null @@ -1,64 +0,0 @@ -/*************************************************************************/ -/* skeleton_ik_editor_plugin.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef SKELETON_IK_EDITOR_PLUGIN_H -#define SKELETON_IK_EDITOR_PLUGIN_H - -#include "editor/editor_node.h" -#include "editor/editor_plugin.h" - -class SkeletonIK3D; - -class SkeletonIKEditorPlugin : public EditorPlugin { - - GDCLASS(SkeletonIKEditorPlugin, EditorPlugin); - - SkeletonIK3D *skeleton_ik; - - Button *play_btn; - EditorNode *editor; - - void _play(); - -protected: - static void _bind_methods(); - -public: - virtual String get_name() const { return "SkeletonIK"; } - bool has_main_screen() const { return false; } - virtual void edit(Object *p_object); - virtual bool handles(Object *p_object) const; - virtual void make_visible(bool p_visible); - - SkeletonIKEditorPlugin(EditorNode *p_node); - ~SkeletonIKEditorPlugin(); -}; - -#endif // SKELETON_IK_EDITOR_PLUGIN_H diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp deleted file mode 100644 index 318f3d6b09..0000000000 --- a/editor/plugins/spatial_editor_plugin.cpp +++ /dev/null @@ -1,6770 +0,0 @@ -/*************************************************************************/ -/* spatial_editor_plugin.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "spatial_editor_plugin.h" - -#include "core/input/input_filter.h" -#include "core/math/camera_matrix.h" -#include "core/os/keyboard.h" -#include "core/print_string.h" -#include "core/project_settings.h" -#include "core/sort_array.h" -#include "editor/debugger/editor_debugger_node.h" -#include "editor/editor_node.h" -#include "editor/editor_scale.h" -#include "editor/editor_settings.h" -#include "editor/plugins/animation_player_editor_plugin.h" -#include "editor/plugins/script_editor_plugin.h" -#include "editor/spatial_editor_gizmos.h" -#include "scene/3d/camera_3d.h" -#include "scene/3d/collision_shape_3d.h" -#include "scene/3d/mesh_instance_3d.h" -#include "scene/3d/physics_body_3d.h" -#include "scene/3d/visual_instance_3d.h" -#include "scene/gui/viewport_container.h" -#include "scene/resources/packed_scene.h" -#include "scene/resources/surface_tool.h" -#include "servers/display_server.h" - -#define DISTANCE_DEFAULT 4 - -#define GIZMO_ARROW_SIZE 0.35 -#define GIZMO_RING_HALF_WIDTH 0.1 -#define GIZMO_SCALE_DEFAULT 0.15 -#define GIZMO_PLANE_SIZE 0.2 -#define GIZMO_PLANE_DST 0.3 -#define GIZMO_CIRCLE_SIZE 1.1 -#define GIZMO_SCALE_OFFSET (GIZMO_CIRCLE_SIZE + 0.3) -#define GIZMO_ARROW_OFFSET (GIZMO_CIRCLE_SIZE + 0.3) - -#define ZOOM_MIN_DISTANCE 0.001 -#define ZOOM_MULTIPLIER 1.08 -#define ZOOM_INDICATOR_DELAY_S 1.5 - -#define FREELOOK_MIN_SPEED 0.01 -#define FREELOOK_SPEED_MULTIPLIER 1.08 - -#define MIN_Z 0.01 -#define MAX_Z 1000000.0 - -#define MIN_FOV 0.01 -#define MAX_FOV 179 - -void ViewportRotationControl::_notification(int p_what) { - - if (p_what == NOTIFICATION_ENTER_TREE) { - axis_menu_options.clear(); - axis_menu_options.push_back(Node3DEditorViewport::VIEW_RIGHT); - axis_menu_options.push_back(Node3DEditorViewport::VIEW_TOP); - axis_menu_options.push_back(Node3DEditorViewport::VIEW_FRONT); - axis_menu_options.push_back(Node3DEditorViewport::VIEW_LEFT); - axis_menu_options.push_back(Node3DEditorViewport::VIEW_BOTTOM); - axis_menu_options.push_back(Node3DEditorViewport::VIEW_REAR); - - axis_colors.clear(); - axis_colors.push_back(get_theme_color("axis_x_color", "Editor")); - axis_colors.push_back(get_theme_color("axis_y_color", "Editor")); - axis_colors.push_back(get_theme_color("axis_z_color", "Editor")); - update(); - - if (!is_connected("mouse_exited", callable_mp(this, &ViewportRotationControl::_on_mouse_exited))) { - connect("mouse_exited", callable_mp(this, &ViewportRotationControl::_on_mouse_exited)); - } - } - - if (p_what == NOTIFICATION_DRAW && viewport != nullptr) { - _draw(); - } -} - -void ViewportRotationControl::_draw() { - Vector2i center = get_size() / 2.0; - float radius = get_size().x / 2.0; - - if (focused_axis > -2 || orbiting) { - draw_circle(center, radius, Color(0.5, 0.5, 0.5, 0.25)); - } - - Vector axis_to_draw; - _get_sorted_axis(axis_to_draw); - for (int i = 0; i < axis_to_draw.size(); ++i) { - _draw_axis(axis_to_draw[i]); - } -} - -void ViewportRotationControl::_draw_axis(const Axis2D &p_axis) { - bool focused = focused_axis == p_axis.axis; - bool positive = p_axis.axis < 3; - bool front = (Math::abs(p_axis.z_axis) <= 0.001 && positive) || p_axis.z_axis > 0.001; - int direction = p_axis.axis % 3; - - Color axis_color = axis_colors[direction]; - - if (!front) { - axis_color = axis_color.darkened(0.4); - } - Color c = focused ? Color(0.9, 0.9, 0.9) : axis_color; - - if (positive) { - Vector2i center = get_size() / 2.0; - draw_line(center, p_axis.screen_point, c, 1.5 * EDSCALE); - } - - if (front) { - String axis_name = direction == 0 ? "X" : (direction == 1 ? "Y" : "Z"); - draw_circle(p_axis.screen_point, AXIS_CIRCLE_RADIUS, c); - draw_char(get_theme_font("rotation_control", "EditorFonts"), p_axis.screen_point + Vector2i(-4, 5) * EDSCALE, axis_name, "", Color(0.3, 0.3, 0.3)); - } else { - draw_circle(p_axis.screen_point, AXIS_CIRCLE_RADIUS * (0.55 + (0.2 * (1.0 + p_axis.z_axis))), c); - } -} - -void ViewportRotationControl::_get_sorted_axis(Vector &r_axis) { - Vector2i center = get_size() / 2.0; - float radius = get_size().x / 2.0; - - float axis_radius = radius - AXIS_CIRCLE_RADIUS - 2.0 * EDSCALE; - Basis camera_basis = viewport->to_camera_transform(viewport->cursor).get_basis().inverse(); - - for (int i = 0; i < 3; ++i) { - Vector3 axis_3d = camera_basis.get_axis(i); - Vector2i axis_vector = Vector2(axis_3d.x, -axis_3d.y) * axis_radius; - - if (Math::abs(axis_3d.z) < 1.0) { - Axis2D pos_axis; - pos_axis.axis = i; - pos_axis.screen_point = center + axis_vector; - pos_axis.z_axis = axis_3d.z; - r_axis.push_back(pos_axis); - - Axis2D neg_axis; - neg_axis.axis = i + 3; - neg_axis.screen_point = center - axis_vector; - neg_axis.z_axis = -axis_3d.z; - r_axis.push_back(neg_axis); - } else { - // Special case when the camera is aligned with one axis - Axis2D axis; - axis.axis = i + (axis_3d.z < 0 ? 0 : 3); - axis.screen_point = center; - axis.z_axis = 1.0; - r_axis.push_back(axis); - } - } - - r_axis.sort_custom(); -} - -void ViewportRotationControl::_gui_input(Ref p_event) { - const Ref mb = p_event; - if (mb.is_valid() && mb->get_button_index() == BUTTON_LEFT) { - Vector2 pos = mb->get_position(); - if (mb->is_pressed()) { - if (pos.distance_to(get_size() / 2.0) < get_size().x / 2.0) { - orbiting = true; - } - } else { - if (focused_axis > -1) { - viewport->_menu_option(axis_menu_options[focused_axis]); - _update_focus(); - } - orbiting = false; - } - } - - const Ref mm = p_event; - if (mm.is_valid()) { - if (orbiting) { - viewport->_nav_orbit(mm, viewport->_get_warped_mouse_motion(mm)); - focused_axis = -1; - } else { - _update_focus(); - } - } -} - -void ViewportRotationControl::_update_focus() { - int original_focus = focused_axis; - focused_axis = -2; - Vector2 mouse_pos = get_local_mouse_position(); - - if (mouse_pos.distance_to(get_size() / 2.0) < get_size().x / 2.0) { - focused_axis = -1; - } - - Vector axes; - _get_sorted_axis(axes); - - for (int i = 0; i < axes.size(); i++) { - const Axis2D &axis = axes[i]; - if (mouse_pos.distance_to(axis.screen_point) < AXIS_CIRCLE_RADIUS) { - focused_axis = axis.axis; - } - } - - if (focused_axis != original_focus) { - update(); - } -} - -void ViewportRotationControl::_on_mouse_exited() { - focused_axis = -2; - update(); -} - -void ViewportRotationControl::set_viewport(Node3DEditorViewport *p_viewport) { - viewport = p_viewport; -} - -void ViewportRotationControl::_bind_methods() { - ClassDB::bind_method(D_METHOD("_gui_input"), &ViewportRotationControl::_gui_input); -} - -void Node3DEditorViewport::_update_camera(float p_interp_delta) { - - bool is_orthogonal = camera->get_projection() == Camera3D::PROJECTION_ORTHOGONAL; - - Cursor old_camera_cursor = camera_cursor; - camera_cursor = cursor; - - if (p_interp_delta > 0) { - - //------- - // Perform smoothing - - if (is_freelook_active()) { - - // Higher inertia should increase "lag" (lerp with factor between 0 and 1) - // Inertia of zero should produce instant movement (lerp with factor of 1) in this case it returns a really high value and gets clamped to 1. - real_t inertia = EDITOR_GET("editors/3d/freelook/freelook_inertia"); - inertia = MAX(0.001, inertia); - real_t factor = (1.0 / inertia) * p_interp_delta; - - // We interpolate a different point here, because in freelook mode the focus point (cursor.pos) orbits around eye_pos - camera_cursor.eye_pos = old_camera_cursor.eye_pos.linear_interpolate(cursor.eye_pos, CLAMP(factor, 0, 1)); - - float orbit_inertia = EDITOR_GET("editors/3d/navigation_feel/orbit_inertia"); - orbit_inertia = MAX(0.0001, orbit_inertia); - camera_cursor.x_rot = Math::lerp(old_camera_cursor.x_rot, cursor.x_rot, MIN(1.f, p_interp_delta * (1 / orbit_inertia))); - camera_cursor.y_rot = Math::lerp(old_camera_cursor.y_rot, cursor.y_rot, MIN(1.f, p_interp_delta * (1 / orbit_inertia))); - - if (Math::abs(camera_cursor.x_rot - cursor.x_rot) < 0.1) { - camera_cursor.x_rot = cursor.x_rot; - } - - if (Math::abs(camera_cursor.y_rot - cursor.y_rot) < 0.1) { - camera_cursor.y_rot = cursor.y_rot; - } - - Vector3 forward = to_camera_transform(camera_cursor).basis.xform(Vector3(0, 0, -1)); - camera_cursor.pos = camera_cursor.eye_pos + forward * camera_cursor.distance; - - } else { - - //when not being manipulated, move softly - float free_orbit_inertia = EDITOR_GET("editors/3d/navigation_feel/orbit_inertia"); - float free_translation_inertia = EDITOR_GET("editors/3d/navigation_feel/translation_inertia"); - //when being manipulated, move more quickly - float manip_orbit_inertia = EDITOR_GET("editors/3d/navigation_feel/manipulation_orbit_inertia"); - float manip_translation_inertia = EDITOR_GET("editors/3d/navigation_feel/manipulation_translation_inertia"); - - float zoom_inertia = EDITOR_GET("editors/3d/navigation_feel/zoom_inertia"); - - //determine if being manipulated - bool manipulated = InputFilter::get_singleton()->get_mouse_button_mask() & (2 | 4); - manipulated |= InputFilter::get_singleton()->is_key_pressed(KEY_SHIFT); - manipulated |= InputFilter::get_singleton()->is_key_pressed(KEY_ALT); - manipulated |= InputFilter::get_singleton()->is_key_pressed(KEY_CONTROL); - - float orbit_inertia = MAX(0.00001, manipulated ? manip_orbit_inertia : free_orbit_inertia); - float translation_inertia = MAX(0.0001, manipulated ? manip_translation_inertia : free_translation_inertia); - zoom_inertia = MAX(0.0001, zoom_inertia); - - camera_cursor.x_rot = Math::lerp(old_camera_cursor.x_rot, cursor.x_rot, MIN(1.f, p_interp_delta * (1 / orbit_inertia))); - camera_cursor.y_rot = Math::lerp(old_camera_cursor.y_rot, cursor.y_rot, MIN(1.f, p_interp_delta * (1 / orbit_inertia))); - - if (Math::abs(camera_cursor.x_rot - cursor.x_rot) < 0.1) { - camera_cursor.x_rot = cursor.x_rot; - } - - if (Math::abs(camera_cursor.y_rot - cursor.y_rot) < 0.1) { - camera_cursor.y_rot = cursor.y_rot; - } - - camera_cursor.pos = old_camera_cursor.pos.linear_interpolate(cursor.pos, MIN(1.f, p_interp_delta * (1 / translation_inertia))); - camera_cursor.distance = Math::lerp(old_camera_cursor.distance, cursor.distance, MIN(1.f, p_interp_delta * (1 / zoom_inertia))); - } - } - - //------- - // Apply camera transform - - float tolerance = 0.001; - bool equal = true; - if (Math::abs(old_camera_cursor.x_rot - camera_cursor.x_rot) > tolerance || Math::abs(old_camera_cursor.y_rot - camera_cursor.y_rot) > tolerance) { - equal = false; - } - - if (equal && old_camera_cursor.pos.distance_squared_to(camera_cursor.pos) > tolerance * tolerance) { - equal = false; - } - - if (equal && Math::abs(old_camera_cursor.distance - camera_cursor.distance) > tolerance) { - equal = false; - } - - if (!equal || p_interp_delta == 0 || is_freelook_active() || is_orthogonal != orthogonal) { - - camera->set_global_transform(to_camera_transform(camera_cursor)); - - if (orthogonal) { - float half_fov = Math::deg2rad(get_fov()) / 2.0; - float height = 2.0 * cursor.distance * Math::tan(half_fov); - camera->set_orthogonal(height, 0.1, 8192); - } else { - camera->set_perspective(get_fov(), get_znear(), get_zfar()); - } - - update_transform_gizmo_view(); - rotation_control->update(); - } -} - -Transform Node3DEditorViewport::to_camera_transform(const Cursor &p_cursor) const { - Transform camera_transform; - camera_transform.translate(p_cursor.pos); - camera_transform.basis.rotate(Vector3(1, 0, 0), -p_cursor.x_rot); - camera_transform.basis.rotate(Vector3(0, 1, 0), -p_cursor.y_rot); - - if (orthogonal) - camera_transform.translate(0, 0, 4096); - else - camera_transform.translate(0, 0, p_cursor.distance); - - return camera_transform; -} - -int Node3DEditorViewport::get_selected_count() const { - - Map &selection = editor_selection->get_selection(); - - int count = 0; - - for (Map::Element *E = selection.front(); E; E = E->next()) { - - Node3D *sp = Object::cast_to(E->key()); - if (!sp) - continue; - - Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data(sp); - if (!se) - continue; - - count++; - } - - return count; -} - -float Node3DEditorViewport::get_znear() const { - - return CLAMP(spatial_editor->get_znear(), MIN_Z, MAX_Z); -} -float Node3DEditorViewport::get_zfar() const { - - return CLAMP(spatial_editor->get_zfar(), MIN_Z, MAX_Z); -} -float Node3DEditorViewport::get_fov() const { - - return CLAMP(spatial_editor->get_fov(), MIN_FOV, MAX_FOV); -} - -Transform Node3DEditorViewport::_get_camera_transform() const { - - return camera->get_global_transform(); -} - -Vector3 Node3DEditorViewport::_get_camera_position() const { - - return _get_camera_transform().origin; -} - -Point2 Node3DEditorViewport::_point_to_screen(const Vector3 &p_point) { - - return camera->unproject_position(p_point) * viewport_container->get_stretch_shrink(); -} - -Vector3 Node3DEditorViewport::_get_ray_pos(const Vector2 &p_pos) const { - - return camera->project_ray_origin(p_pos / viewport_container->get_stretch_shrink()); -} - -Vector3 Node3DEditorViewport::_get_camera_normal() const { - - return -_get_camera_transform().basis.get_axis(2); -} - -Vector3 Node3DEditorViewport::_get_ray(const Vector2 &p_pos) const { - - return camera->project_ray_normal(p_pos / viewport_container->get_stretch_shrink()); -} - -void Node3DEditorViewport::_clear_selected() { - - editor_selection->clear(); -} - -void Node3DEditorViewport::_select_clicked(bool p_append, bool p_single, bool p_allow_locked) { - - if (clicked.is_null()) - return; - - Node *node = Object::cast_to(ObjectDB::get_instance(clicked)); - Node3D *selected = Object::cast_to(node); - if (!selected) - return; - - if (!p_allow_locked) { - // Replace the node by the group if grouped - while (node && node != editor->get_edited_scene()->get_parent()) { - Node3D *selected_tmp = Object::cast_to(node); - if (selected_tmp && node->has_meta("_edit_group_")) { - selected = selected_tmp; - } - node = node->get_parent(); - } - } - - if (p_allow_locked || !_is_node_locked(selected)) { - _select(selected, clicked_wants_append, true); - } -} - -void Node3DEditorViewport::_select(Node *p_node, bool p_append, bool p_single) { - - if (!p_append) { - editor_selection->clear(); - } - - if (editor_selection->is_selected(p_node)) { - //erase - editor_selection->remove_node(p_node); - } else { - - editor_selection->add_node(p_node); - } - - if (p_single) { - if (Engine::get_singleton()->is_editor_hint()) - editor->call("edit_node", p_node); - } -} - -ObjectID Node3DEditorViewport::_select_ray(const Point2 &p_pos, bool p_append, bool &r_includes_current, int *r_gizmo_handle, bool p_alt_select) { - - if (r_gizmo_handle) - *r_gizmo_handle = -1; - - Vector3 ray = _get_ray(p_pos); - Vector3 pos = _get_ray_pos(p_pos); - Vector2 shrinked_pos = p_pos / viewport_container->get_stretch_shrink(); - - Vector instances = VisualServer::get_singleton()->instances_cull_ray(pos, ray, get_tree()->get_root()->get_world()->get_scenario()); - Set> found_gizmos; - - Node *edited_scene = get_tree()->get_edited_scene_root(); - ObjectID closest; - Node *item = NULL; - float closest_dist = 1e20; - int selected_handle = -1; - - for (int i = 0; i < instances.size(); i++) { - - Node3D *spat = Object::cast_to(ObjectDB::get_instance(instances[i])); - - if (!spat) - continue; - - Ref seg = spat->get_gizmo(); - - if ((!seg.is_valid()) || found_gizmos.has(seg)) { - continue; - } - - found_gizmos.insert(seg); - Vector3 point; - Vector3 normal; - - int handle = -1; - bool inters = seg->intersect_ray(camera, shrinked_pos, point, normal, &handle, p_alt_select); - - if (!inters) - continue; - - float dist = pos.distance_to(point); - - if (dist < 0) - continue; - - if (dist < closest_dist) { - - item = Object::cast_to(spat); - while (item->get_owner() && item->get_owner() != edited_scene && !edited_scene->is_editable_instance(item->get_owner())) { - item = item->get_owner(); - } - - closest = item->get_instance_id(); - closest_dist = dist; - selected_handle = handle; - } - } - - if (!item) - return ObjectID(); - - if (!editor_selection->is_selected(item) || (r_gizmo_handle && selected_handle >= 0)) { - - if (r_gizmo_handle) - *r_gizmo_handle = selected_handle; - } - - return closest; -} - -void Node3DEditorViewport::_find_items_at_pos(const Point2 &p_pos, bool &r_includes_current, Vector<_RayResult> &results, bool p_alt_select) { - - Vector3 ray = _get_ray(p_pos); - Vector3 pos = _get_ray_pos(p_pos); - - Vector instances = VisualServer::get_singleton()->instances_cull_ray(pos, ray, get_tree()->get_root()->get_world()->get_scenario()); - Set> found_gizmos; - - r_includes_current = false; - - for (int i = 0; i < instances.size(); i++) { - - Node3D *spat = Object::cast_to(ObjectDB::get_instance(instances[i])); - - if (!spat) - continue; - - Ref seg = spat->get_gizmo(); - - if (!seg.is_valid()) - continue; - - if (found_gizmos.has(seg)) - continue; - - found_gizmos.insert(seg); - Vector3 point; - Vector3 normal; - - int handle = -1; - bool inters = seg->intersect_ray(camera, p_pos, point, normal, NULL, p_alt_select); - - if (!inters) - continue; - - float dist = pos.distance_to(point); - - if (dist < 0) - continue; - - if (editor_selection->is_selected(spat)) - r_includes_current = true; - - _RayResult res; - res.item = spat; - res.depth = dist; - res.handle = handle; - results.push_back(res); - } - - if (results.empty()) - return; - - results.sort(); -} - -Vector3 Node3DEditorViewport::_get_screen_to_space(const Vector3 &p_vector3) { - - CameraMatrix cm; - if (orthogonal) { - cm.set_orthogonal(camera->get_size(), get_size().aspect(), get_znear() + p_vector3.z, get_zfar()); - } else { - cm.set_perspective(get_fov(), get_size().aspect(), get_znear() + p_vector3.z, get_zfar()); - } - Vector2 screen_he = cm.get_viewport_half_extents(); - - Transform camera_transform; - camera_transform.translate(cursor.pos); - camera_transform.basis.rotate(Vector3(1, 0, 0), -cursor.x_rot); - camera_transform.basis.rotate(Vector3(0, 1, 0), -cursor.y_rot); - camera_transform.translate(0, 0, cursor.distance); - - return camera_transform.xform(Vector3(((p_vector3.x / get_size().width) * 2.0 - 1.0) * screen_he.x, ((1.0 - (p_vector3.y / get_size().height)) * 2.0 - 1.0) * screen_he.y, -(get_znear() + p_vector3.z))); -} - -void Node3DEditorViewport::_select_region() { - - if (cursor.region_begin == cursor.region_end) - return; //nothing really - - float z_offset = MAX(0.0, 5.0 - get_znear()); - - Vector3 box[4] = { - Vector3( - MIN(cursor.region_begin.x, cursor.region_end.x), - MIN(cursor.region_begin.y, cursor.region_end.y), - z_offset), - Vector3( - MAX(cursor.region_begin.x, cursor.region_end.x), - MIN(cursor.region_begin.y, cursor.region_end.y), - z_offset), - Vector3( - MAX(cursor.region_begin.x, cursor.region_end.x), - MAX(cursor.region_begin.y, cursor.region_end.y), - z_offset), - Vector3( - MIN(cursor.region_begin.x, cursor.region_end.x), - MAX(cursor.region_begin.y, cursor.region_end.y), - z_offset) - }; - - Vector frustum; - - Vector3 cam_pos = _get_camera_position(); - - for (int i = 0; i < 4; i++) { - - Vector3 a = _get_screen_to_space(box[i]); - Vector3 b = _get_screen_to_space(box[(i + 1) % 4]); - if (orthogonal) { - frustum.push_back(Plane(a, (a - b).normalized())); - } else { - frustum.push_back(Plane(a, b, cam_pos)); - } - } - - if (!orthogonal) { - Plane near(cam_pos, -_get_camera_normal()); - near.d -= get_znear(); - - frustum.push_back(near); - - Plane far = -near; - far.d += get_zfar(); - - frustum.push_back(far); - } - - Vector instances = VisualServer::get_singleton()->instances_cull_convex(frustum, get_tree()->get_root()->get_world()->get_scenario()); - Vector selected; - - Node *edited_scene = get_tree()->get_edited_scene_root(); - - for (int i = 0; i < instances.size(); i++) { - - Node3D *sp = Object::cast_to(ObjectDB::get_instance(instances[i])); - if (!sp || _is_node_locked(sp)) - continue; - - Node *item = Object::cast_to(sp); - while (item->get_owner() && item->get_owner() != edited_scene && !edited_scene->is_editable_instance(item->get_owner())) { - item = item->get_owner(); - } - - // Replace the node by the group if grouped - if (item->is_class("Node3D")) { - Node3D *sel = Object::cast_to(item); - while (item && item != editor->get_edited_scene()->get_parent()) { - Node3D *selected_tmp = Object::cast_to(item); - if (selected_tmp && item->has_meta("_edit_group_")) { - sel = selected_tmp; - } - item = item->get_parent(); - } - item = sel; - } - - if (selected.find(item) != -1) continue; - - if (_is_node_locked(item)) continue; - - Ref seg = sp->get_gizmo(); - - if (!seg.is_valid()) - continue; - - if (seg->intersect_frustum(camera, frustum)) { - selected.push_back(item); - } - } - - bool single = selected.size() == 1; - for (int i = 0; i < selected.size(); i++) { - _select(selected[i], true, single); - } -} - -void Node3DEditorViewport::_update_name() { - - String view_mode = orthogonal ? TTR("Orthogonal") : TTR("Perspective"); - - if (auto_orthogonal) { - view_mode += " [auto]"; - } - - if (name != "") - view_menu->set_text(name + " " + view_mode); - else - view_menu->set_text(view_mode); - - view_menu->set_size(Vector2(0, 0)); // resets the button size -} - -void Node3DEditorViewport::_compute_edit(const Point2 &p_point) { - - _edit.click_ray = _get_ray(Vector2(p_point.x, p_point.y)); - _edit.click_ray_pos = _get_ray_pos(Vector2(p_point.x, p_point.y)); - _edit.plane = TRANSFORM_VIEW; - spatial_editor->update_transform_gizmo(); - _edit.center = spatial_editor->get_gizmo_transform().origin; - - List &selection = editor_selection->get_selected_node_list(); - - for (List::Element *E = selection.front(); E; E = E->next()) { - - Node3D *sp = Object::cast_to(E->get()); - if (!sp) - continue; - - Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data(sp); - if (!se) - continue; - - se->original = se->sp->get_global_gizmo_transform(); - se->original_local = se->sp->get_local_gizmo_transform(); - } -} - -static int _get_key_modifier_setting(const String &p_property) { - - switch (EditorSettings::get_singleton()->get(p_property).operator int()) { - - case 0: return 0; - case 1: return KEY_SHIFT; - case 2: return KEY_ALT; - case 3: return KEY_META; - case 4: return KEY_CONTROL; - } - return 0; -} - -static int _get_key_modifier(Ref e) { - if (e->get_shift()) - return KEY_SHIFT; - if (e->get_alt()) - return KEY_ALT; - if (e->get_control()) - return KEY_CONTROL; - if (e->get_metakey()) - return KEY_META; - return 0; -} - -bool Node3DEditorViewport::_gizmo_select(const Vector2 &p_screenpos, bool p_highlight_only) { - - if (!spatial_editor->is_gizmo_visible()) - return false; - if (get_selected_count() == 0) { - if (p_highlight_only) - spatial_editor->select_gizmo_highlight_axis(-1); - return false; - } - - Vector3 ray_pos = _get_ray_pos(Vector2(p_screenpos.x, p_screenpos.y)); - Vector3 ray = _get_ray(Vector2(p_screenpos.x, p_screenpos.y)); - - Transform gt = spatial_editor->get_gizmo_transform(); - float gs = gizmo_scale; - - if (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_MOVE) { - - int col_axis = -1; - float col_d = 1e20; - - for (int i = 0; i < 3; i++) { - - Vector3 grabber_pos = gt.origin + gt.basis.get_axis(i) * gs * (GIZMO_ARROW_OFFSET + (GIZMO_ARROW_SIZE * 0.5)); - float grabber_radius = gs * GIZMO_ARROW_SIZE; - - Vector3 r; - - if (Geometry::segment_intersects_sphere(ray_pos, ray_pos + ray * MAX_Z, grabber_pos, grabber_radius, &r)) { - float d = r.distance_to(ray_pos); - if (d < col_d) { - col_d = d; - col_axis = i; - } - } - } - - bool is_plane_translate = false; - // plane select - if (col_axis == -1) { - col_d = 1e20; - - for (int i = 0; i < 3; i++) { - - Vector3 ivec2 = gt.basis.get_axis((i + 1) % 3).normalized(); - Vector3 ivec3 = gt.basis.get_axis((i + 2) % 3).normalized(); - - Vector3 grabber_pos = gt.origin + (ivec2 + ivec3) * gs * (GIZMO_PLANE_SIZE + GIZMO_PLANE_DST); - - Vector3 r; - Plane plane(gt.origin, gt.basis.get_axis(i).normalized()); - - if (plane.intersects_ray(ray_pos, ray, &r)) { - - float dist = r.distance_to(grabber_pos); - if (dist < (gs * GIZMO_PLANE_SIZE)) { - - float d = ray_pos.distance_to(r); - if (d < col_d) { - col_d = d; - col_axis = i; - - is_plane_translate = true; - } - } - } - } - } - - if (col_axis != -1) { - - if (p_highlight_only) { - - spatial_editor->select_gizmo_highlight_axis(col_axis + (is_plane_translate ? 6 : 0)); - - } else { - //handle plane translate - _edit.mode = TRANSFORM_TRANSLATE; - _compute_edit(Point2(p_screenpos.x, p_screenpos.y)); - _edit.plane = TransformPlane(TRANSFORM_X_AXIS + col_axis + (is_plane_translate ? 3 : 0)); - } - return true; - } - } - - if (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_ROTATE) { - - int col_axis = -1; - float col_d = 1e20; - - for (int i = 0; i < 3; i++) { - - Plane plane(gt.origin, gt.basis.get_axis(i).normalized()); - Vector3 r; - if (!plane.intersects_ray(ray_pos, ray, &r)) - continue; - - float dist = r.distance_to(gt.origin); - - if (dist > gs * (GIZMO_CIRCLE_SIZE - GIZMO_RING_HALF_WIDTH) && dist < gs * (GIZMO_CIRCLE_SIZE + GIZMO_RING_HALF_WIDTH)) { - - float d = ray_pos.distance_to(r); - if (d < col_d) { - col_d = d; - col_axis = i; - } - } - } - - if (col_axis != -1) { - - if (p_highlight_only) { - - spatial_editor->select_gizmo_highlight_axis(col_axis + 3); - } else { - //handle rotate - _edit.mode = TRANSFORM_ROTATE; - _compute_edit(Point2(p_screenpos.x, p_screenpos.y)); - _edit.plane = TransformPlane(TRANSFORM_X_AXIS + col_axis); - } - return true; - } - } - - if (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SCALE) { - - int col_axis = -1; - float col_d = 1e20; - - for (int i = 0; i < 3; i++) { - - Vector3 grabber_pos = gt.origin + gt.basis.get_axis(i) * gs * GIZMO_SCALE_OFFSET; - float grabber_radius = gs * GIZMO_ARROW_SIZE; - - Vector3 r; - - if (Geometry::segment_intersects_sphere(ray_pos, ray_pos + ray * MAX_Z, grabber_pos, grabber_radius, &r)) { - float d = r.distance_to(ray_pos); - if (d < col_d) { - col_d = d; - col_axis = i; - } - } - } - - bool is_plane_scale = false; - // plane select - if (col_axis == -1) { - col_d = 1e20; - - for (int i = 0; i < 3; i++) { - - Vector3 ivec2 = gt.basis.get_axis((i + 1) % 3).normalized(); - Vector3 ivec3 = gt.basis.get_axis((i + 2) % 3).normalized(); - - Vector3 grabber_pos = gt.origin + (ivec2 + ivec3) * gs * (GIZMO_PLANE_SIZE + GIZMO_PLANE_DST); - - Vector3 r; - Plane plane(gt.origin, gt.basis.get_axis(i).normalized()); - - if (plane.intersects_ray(ray_pos, ray, &r)) { - - float dist = r.distance_to(grabber_pos); - if (dist < (gs * GIZMO_PLANE_SIZE)) { - - float d = ray_pos.distance_to(r); - if (d < col_d) { - col_d = d; - col_axis = i; - - is_plane_scale = true; - } - } - } - } - } - - if (col_axis != -1) { - - if (p_highlight_only) { - - spatial_editor->select_gizmo_highlight_axis(col_axis + (is_plane_scale ? 12 : 9)); - - } else { - //handle scale - _edit.mode = TRANSFORM_SCALE; - _compute_edit(Point2(p_screenpos.x, p_screenpos.y)); - _edit.plane = TransformPlane(TRANSFORM_X_AXIS + col_axis + (is_plane_scale ? 3 : 0)); - } - return true; - } - } - - if (p_highlight_only) - spatial_editor->select_gizmo_highlight_axis(-1); - - return false; -} - -void Node3DEditorViewport::_surface_mouse_enter() { - - if (!surface->has_focus() && (!get_focus_owner() || !get_focus_owner()->is_text_field())) - surface->grab_focus(); -} - -void Node3DEditorViewport::_surface_mouse_exit() { - - _remove_preview(); -} - -void Node3DEditorViewport::_surface_focus_enter() { - - view_menu->set_disable_shortcuts(false); -} - -void Node3DEditorViewport::_surface_focus_exit() { - - view_menu->set_disable_shortcuts(true); -} -bool Node3DEditorViewport ::_is_node_locked(const Node *p_node) { - return p_node->has_meta("_edit_lock_") && p_node->get_meta("_edit_lock_"); -} -void Node3DEditorViewport::_list_select(Ref b) { - - _find_items_at_pos(b->get_position(), clicked_includes_current, selection_results, b->get_shift()); - - Node *scene = editor->get_edited_scene(); - - for (int i = 0; i < selection_results.size(); i++) { - Node3D *item = selection_results[i].item; - if (item != scene && item->get_owner() != scene && !scene->is_editable_instance(item->get_owner())) { - //invalid result - selection_results.remove(i); - i--; - } - } - - clicked_wants_append = b->get_shift(); - - if (selection_results.size() == 1) { - - clicked = selection_results[0].item->get_instance_id(); - selection_results.clear(); - - if (clicked.is_valid()) { - _select_clicked(clicked_wants_append, true, spatial_editor->get_tool_mode() != Node3DEditor::TOOL_MODE_LIST_SELECT); - clicked = ObjectID(); - } - - } else if (!selection_results.empty()) { - - NodePath root_path = get_tree()->get_edited_scene_root()->get_path(); - StringName root_name = root_path.get_name(root_path.get_name_count() - 1); - - for (int i = 0; i < selection_results.size(); i++) { - - Node3D *spat = selection_results[i].item; - - Ref icon = EditorNode::get_singleton()->get_object_icon(spat, "Node"); - - String node_path = "/" + root_name + "/" + root_path.rel_path_to(spat->get_path()); - - int locked = 0; - if (_is_node_locked(spat)) { - locked = 1; - } else { - Node *ed_scene = editor->get_edited_scene(); - Node *node = spat; - - while (node && node != ed_scene->get_parent()) { - Node3D *selected_tmp = Object::cast_to(node); - if (selected_tmp && node->has_meta("_edit_group_")) { - locked = 2; - } - node = node->get_parent(); - } - } - - String suffix = String(); - if (locked == 1) { - suffix = " (" + TTR("Locked") + ")"; - } else if (locked == 2) { - suffix = " (" + TTR("Grouped") + ")"; - } - selection_menu->add_item((String)spat->get_name() + suffix); - selection_menu->set_item_icon(i, icon); - selection_menu->set_item_metadata(i, node_path); - selection_menu->set_item_tooltip(i, String(spat->get_name()) + "\nType: " + spat->get_class() + "\nPath: " + node_path); - } - - selection_menu->set_position(get_screen_transform().xform(b->get_position())); - selection_menu->popup(); - } -} - -void Node3DEditorViewport::_sinput(const Ref &p_event) { - - if (previewing) - return; //do NONE - - { - EditorNode *en = editor; - EditorPluginList *force_input_forwarding_list = en->get_editor_plugins_force_input_forwarding(); - if (!force_input_forwarding_list->empty()) { - bool discard = force_input_forwarding_list->forward_spatial_gui_input(camera, p_event, true); - if (discard) - return; - } - } - { - EditorNode *en = editor; - EditorPluginList *over_plugin_list = en->get_editor_plugins_over(); - if (!over_plugin_list->empty()) { - bool discard = over_plugin_list->forward_spatial_gui_input(camera, p_event, false); - if (discard) - return; - } - } - - Ref b = p_event; - - if (b.is_valid()) { - emit_signal("clicked", this); - - float zoom_factor = 1 + (ZOOM_MULTIPLIER - 1) * b->get_factor(); - switch (b->get_button_index()) { - - case BUTTON_WHEEL_UP: { - if (is_freelook_active()) - scale_freelook_speed(zoom_factor); - else - scale_cursor_distance(1.0 / zoom_factor); - } break; - - case BUTTON_WHEEL_DOWN: { - if (is_freelook_active()) - scale_freelook_speed(1.0 / zoom_factor); - else - scale_cursor_distance(zoom_factor); - } break; - - case BUTTON_RIGHT: { - - NavigationScheme nav_scheme = (NavigationScheme)EditorSettings::get_singleton()->get("editors/3d/navigation/navigation_scheme").operator int(); - - if (b->is_pressed() && _edit.gizmo.is_valid()) { - //restore - _edit.gizmo->commit_handle(_edit.gizmo_handle, _edit.gizmo_initial_value, true); - _edit.gizmo = Ref(); - } - - if (_edit.mode == TRANSFORM_NONE && b->is_pressed()) { - - if (b->get_alt()) { - - if (nav_scheme == NAVIGATION_MAYA) - break; - - _list_select(b); - return; - } - } - - if (_edit.mode != TRANSFORM_NONE && b->is_pressed()) { - //cancel motion - _edit.mode = TRANSFORM_NONE; - - List &selection = editor_selection->get_selected_node_list(); - - for (List::Element *E = selection.front(); E; E = E->next()) { - - Node3D *sp = Object::cast_to(E->get()); - if (!sp) - continue; - - Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data(sp); - if (!se) - continue; - - sp->set_global_transform(se->original); - } - surface->update(); - set_message(TTR("Transform Aborted."), 3); - } - - if (b->is_pressed()) { - const int mod = _get_key_modifier(b); - if (!orthogonal) { - if (mod == _get_key_modifier_setting("editors/3d/freelook/freelook_activation_modifier")) { - set_freelook_active(true); - } - } - } else { - set_freelook_active(false); - } - - if (freelook_active && !surface->has_focus()) { - // Focus usually doesn't trigger on right-click, but in case of freelook it should, - // otherwise using keyboard navigation would misbehave - surface->grab_focus(); - } - - } break; - case BUTTON_MIDDLE: { - - if (b->is_pressed() && _edit.mode != TRANSFORM_NONE) { - - switch (_edit.plane) { - - case TRANSFORM_VIEW: { - - _edit.plane = TRANSFORM_X_AXIS; - set_message(TTR("X-Axis Transform."), 2); - name = ""; - _update_name(); - } break; - case TRANSFORM_X_AXIS: { - - _edit.plane = TRANSFORM_Y_AXIS; - set_message(TTR("Y-Axis Transform."), 2); - - } break; - case TRANSFORM_Y_AXIS: { - - _edit.plane = TRANSFORM_Z_AXIS; - set_message(TTR("Z-Axis Transform."), 2); - - } break; - case TRANSFORM_Z_AXIS: { - - _edit.plane = TRANSFORM_VIEW; - set_message(TTR("View Plane Transform."), 2); - - } break; - case TRANSFORM_YZ: - case TRANSFORM_XZ: - case TRANSFORM_XY: { - } break; - } - } - } break; - case BUTTON_LEFT: { - - if (b->is_pressed()) { - - NavigationScheme nav_scheme = (NavigationScheme)EditorSettings::get_singleton()->get("editors/3d/navigation/navigation_scheme").operator int(); - if ((nav_scheme == NAVIGATION_MAYA || nav_scheme == NAVIGATION_MODO) && b->get_alt()) { - break; - } - - if (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_LIST_SELECT) { - _list_select(b); - break; - } - - _edit.mouse_pos = b->get_position(); - _edit.snap = spatial_editor->is_snap_enabled(); - _edit.mode = TRANSFORM_NONE; - - //gizmo has priority over everything - - bool can_select_gizmos = true; - - { - int idx = view_menu->get_popup()->get_item_index(VIEW_GIZMOS); - can_select_gizmos = view_menu->get_popup()->is_item_checked(idx); - } - - if (can_select_gizmos && spatial_editor->get_selected()) { - - Ref seg = spatial_editor->get_selected()->get_gizmo(); - if (seg.is_valid()) { - int handle = -1; - Vector3 point; - Vector3 normal; - bool inters = seg->intersect_ray(camera, _edit.mouse_pos, point, normal, &handle, b->get_shift()); - if (inters && handle != -1) { - - _edit.gizmo = seg; - _edit.gizmo_handle = handle; - _edit.gizmo_initial_value = seg->get_handle_value(handle); - break; - } - } - } - - if (_gizmo_select(_edit.mouse_pos)) - break; - - clicked = ObjectID(); - clicked_includes_current = false; - - if ((spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT && b->get_control()) || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_ROTATE) { - - /* HANDLE ROTATION */ - if (get_selected_count() == 0) - break; //bye - //handle rotate - _edit.mode = TRANSFORM_ROTATE; - _compute_edit(b->get_position()); - break; - } - - if (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_MOVE) { - - if (get_selected_count() == 0) - break; //bye - //handle translate - _edit.mode = TRANSFORM_TRANSLATE; - _compute_edit(b->get_position()); - break; - } - - if (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SCALE) { - - if (get_selected_count() == 0) - break; //bye - //handle scale - _edit.mode = TRANSFORM_SCALE; - _compute_edit(b->get_position()); - break; - } - - // todo scale - - int gizmo_handle = -1; - - clicked = _select_ray(b->get_position(), b->get_shift(), clicked_includes_current, &gizmo_handle, b->get_shift()); - - //clicking is always deferred to either move or release - - clicked_wants_append = b->get_shift(); - - if (clicked.is_null()) { - - if (!clicked_wants_append) - _clear_selected(); - - //default to regionselect - cursor.region_select = true; - cursor.region_begin = b->get_position(); - cursor.region_end = b->get_position(); - } - - if (clicked.is_valid() && gizmo_handle >= 0) { - - Node3D *spa = Object::cast_to(ObjectDB::get_instance(clicked)); - if (spa) { - - Ref seg = spa->get_gizmo(); - if (seg.is_valid()) { - - _edit.gizmo = seg; - _edit.gizmo_handle = gizmo_handle; - _edit.gizmo_initial_value = seg->get_handle_value(gizmo_handle); - break; - } - } - } - - surface->update(); - } else { - - if (_edit.gizmo.is_valid()) { - - _edit.gizmo->commit_handle(_edit.gizmo_handle, _edit.gizmo_initial_value, false); - _edit.gizmo = Ref(); - break; - } - if (clicked.is_valid()) { - _select_clicked(clicked_wants_append, true); - // Processing was deferred. - clicked = ObjectID(); - } - - if (cursor.region_select) { - - if (!clicked_wants_append) _clear_selected(); - - _select_region(); - cursor.region_select = false; - surface->update(); - } - - if (_edit.mode != TRANSFORM_NONE) { - - static const char *_transform_name[4] = { "None", "Rotate", "Translate", "Scale" }; - undo_redo->create_action(_transform_name[_edit.mode]); - - List &selection = editor_selection->get_selected_node_list(); - - for (List::Element *E = selection.front(); E; E = E->next()) { - - Node3D *sp = Object::cast_to(E->get()); - if (!sp) - continue; - - Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data(sp); - if (!se) - continue; - - undo_redo->add_do_method(sp, "set_global_transform", sp->get_global_gizmo_transform()); - undo_redo->add_undo_method(sp, "set_global_transform", se->original); - } - undo_redo->commit_action(); - _edit.mode = TRANSFORM_NONE; - set_message(""); - } - - surface->update(); - } - - } break; - } - } - - Ref m = p_event; - - if (m.is_valid()) { - - _edit.mouse_pos = m->get_position(); - - if (spatial_editor->get_selected()) { - - Ref seg = spatial_editor->get_selected()->get_gizmo(); - if (seg.is_valid()) { - - int selected_handle = -1; - - int handle = -1; - Vector3 point; - Vector3 normal; - bool inters = seg->intersect_ray(camera, _edit.mouse_pos, point, normal, &handle, false); - if (inters && handle != -1) { - - selected_handle = handle; - } - - if (selected_handle != spatial_editor->get_over_gizmo_handle()) { - spatial_editor->set_over_gizmo_handle(selected_handle); - spatial_editor->get_selected()->update_gizmo(); - if (selected_handle != -1) - spatial_editor->select_gizmo_highlight_axis(-1); - } - } - } - - if (spatial_editor->get_over_gizmo_handle() == -1 && !(m->get_button_mask() & 1) && !_edit.gizmo.is_valid()) { - - _gizmo_select(_edit.mouse_pos, true); - } - - NavigationScheme nav_scheme = (NavigationScheme)EditorSettings::get_singleton()->get("editors/3d/navigation/navigation_scheme").operator int(); - NavigationMode nav_mode = NAVIGATION_NONE; - - if (_edit.gizmo.is_valid()) { - - _edit.gizmo->set_handle(_edit.gizmo_handle, camera, m->get_position()); - Variant v = _edit.gizmo->get_handle_value(_edit.gizmo_handle); - String n = _edit.gizmo->get_handle_name(_edit.gizmo_handle); - set_message(n + ": " + String(v)); - - } else if (m->get_button_mask() & BUTTON_MASK_LEFT) { - - if (nav_scheme == NAVIGATION_MAYA && m->get_alt()) { - nav_mode = NAVIGATION_ORBIT; - } else if (nav_scheme == NAVIGATION_MODO && m->get_alt() && m->get_shift()) { - nav_mode = NAVIGATION_PAN; - } else if (nav_scheme == NAVIGATION_MODO && m->get_alt() && m->get_control()) { - nav_mode = NAVIGATION_ZOOM; - } else if (nav_scheme == NAVIGATION_MODO && m->get_alt()) { - nav_mode = NAVIGATION_ORBIT; - } else { - if (clicked.is_valid()) { - - if (!clicked_includes_current) { - - _select_clicked(clicked_wants_append, true); - // Processing was deferred. - } - - _compute_edit(_edit.mouse_pos); - clicked = ObjectID(); - - _edit.mode = TRANSFORM_TRANSLATE; - } - - if (cursor.region_select) { - cursor.region_end = m->get_position(); - surface->update(); - return; - } - - if (_edit.mode == TRANSFORM_NONE) - return; - - Vector3 ray_pos = _get_ray_pos(m->get_position()); - Vector3 ray = _get_ray(m->get_position()); - float snap = EDITOR_GET("interface/inspector/default_float_step"); - int snap_step_decimals = Math::range_step_decimals(snap); - - switch (_edit.mode) { - - case TRANSFORM_SCALE: { - - Vector3 motion_mask; - Plane plane; - bool plane_mv = false; - - switch (_edit.plane) { - case TRANSFORM_VIEW: - motion_mask = Vector3(0, 0, 0); - plane = Plane(_edit.center, _get_camera_normal()); - break; - case TRANSFORM_X_AXIS: - motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(0); - plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized()); - break; - case TRANSFORM_Y_AXIS: - motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(1); - plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized()); - break; - case TRANSFORM_Z_AXIS: - motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2); - plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized()); - break; - case TRANSFORM_YZ: - motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2) + spatial_editor->get_gizmo_transform().basis.get_axis(1); - plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(0)); - plane_mv = true; - break; - case TRANSFORM_XZ: - motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2) + spatial_editor->get_gizmo_transform().basis.get_axis(0); - plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(1)); - plane_mv = true; - break; - case TRANSFORM_XY: - motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(0) + spatial_editor->get_gizmo_transform().basis.get_axis(1); - plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(2)); - plane_mv = true; - break; - } - - Vector3 intersection; - if (!plane.intersects_ray(ray_pos, ray, &intersection)) - break; - - Vector3 click; - if (!plane.intersects_ray(_edit.click_ray_pos, _edit.click_ray, &click)) - break; - - Vector3 motion = intersection - click; - if (_edit.plane != TRANSFORM_VIEW) { - - if (!plane_mv) { - - motion = motion_mask.dot(motion) * motion_mask; - - } else { - - // Alternative planar scaling mode - if (_get_key_modifier(m) != KEY_SHIFT) { - motion = motion_mask.dot(motion) * motion_mask; - } - } - - } else { - float center_click_dist = click.distance_to(_edit.center); - float center_inters_dist = intersection.distance_to(_edit.center); - if (center_click_dist == 0) - break; - - float scale = center_inters_dist - center_click_dist; - motion = Vector3(scale, scale, scale); - } - - List &selection = editor_selection->get_selected_node_list(); - - // Disable local transformation for TRANSFORM_VIEW - bool local_coords = (spatial_editor->are_local_coords_enabled() && _edit.plane != TRANSFORM_VIEW); - - if (_edit.snap || spatial_editor->is_snap_enabled()) { - snap = spatial_editor->get_scale_snap() / 100; - } - Vector3 motion_snapped = motion; - motion_snapped.snap(Vector3(snap, snap, snap)); - // This might not be necessary anymore after issue #288 is solved (in 4.0?). - set_message(TTR("Scaling: ") + "(" + String::num(motion_snapped.x, snap_step_decimals) + ", " + - String::num(motion_snapped.y, snap_step_decimals) + ", " + String::num(motion_snapped.z, snap_step_decimals) + ")"); - - for (List::Element *E = selection.front(); E; E = E->next()) { - - Node3D *sp = Object::cast_to(E->get()); - if (!sp) { - continue; - } - - Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data(sp); - if (!se) { - continue; - } - - if (sp->has_meta("_edit_lock_")) { - continue; - } - - Transform original = se->original; - Transform original_local = se->original_local; - Transform base = Transform(Basis(), _edit.center); - Transform t; - Vector3 local_scale; - - if (local_coords) { - - Basis g = original.basis.orthonormalized(); - Vector3 local_motion = g.inverse().xform(motion); - - if (_edit.snap || spatial_editor->is_snap_enabled()) { - local_motion.snap(Vector3(snap, snap, snap)); - } - - local_scale = original_local.basis.get_scale() * (local_motion + Vector3(1, 1, 1)); - - // Prevent scaling to 0 it would break the gizmo - Basis check = original_local.basis; - check.scale(local_scale); - if (check.determinant() != 0) { - - // Apply scale - sp->set_scale(local_scale); - } - - } else { - - if (_edit.snap || spatial_editor->is_snap_enabled()) { - motion.snap(Vector3(snap, snap, snap)); - } - - Transform r; - r.basis.scale(motion + Vector3(1, 1, 1)); - t = base * (r * (base.inverse() * original)); - - // Apply scale - sp->set_global_transform(t); - } - } - - surface->update(); - - } break; - - case TRANSFORM_TRANSLATE: { - - Vector3 motion_mask; - Plane plane; - bool plane_mv = false; - - switch (_edit.plane) { - case TRANSFORM_VIEW: - plane = Plane(_edit.center, _get_camera_normal()); - break; - case TRANSFORM_X_AXIS: - motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(0); - plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized()); - break; - case TRANSFORM_Y_AXIS: - motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(1); - plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized()); - break; - case TRANSFORM_Z_AXIS: - motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2); - plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized()); - break; - case TRANSFORM_YZ: - plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(0)); - plane_mv = true; - break; - case TRANSFORM_XZ: - plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(1)); - plane_mv = true; - break; - case TRANSFORM_XY: - plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(2)); - plane_mv = true; - break; - } - - Vector3 intersection; - if (!plane.intersects_ray(ray_pos, ray, &intersection)) - break; - - Vector3 click; - if (!plane.intersects_ray(_edit.click_ray_pos, _edit.click_ray, &click)) - break; - - Vector3 motion = intersection - click; - if (_edit.plane != TRANSFORM_VIEW) { - if (!plane_mv) { - motion = motion_mask.dot(motion) * motion_mask; - } - } - - List &selection = editor_selection->get_selected_node_list(); - - // Disable local transformation for TRANSFORM_VIEW - bool local_coords = (spatial_editor->are_local_coords_enabled() && _edit.plane != TRANSFORM_VIEW); - - if (_edit.snap || spatial_editor->is_snap_enabled()) { - snap = spatial_editor->get_translate_snap(); - } - Vector3 motion_snapped = motion; - motion_snapped.snap(Vector3(snap, snap, snap)); - set_message(TTR("Translating: ") + "(" + String::num(motion_snapped.x, snap_step_decimals) + ", " + - String::num(motion_snapped.y, snap_step_decimals) + ", " + String::num(motion_snapped.z, snap_step_decimals) + ")"); - - for (List::Element *E = selection.front(); E; E = E->next()) { - - Node3D *sp = Object::cast_to(E->get()); - if (!sp) { - continue; - } - - Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data(sp); - if (!se) { - continue; - } - - if (sp->has_meta("_edit_lock_")) { - continue; - } - - Transform original = se->original; - Transform t; - - if (local_coords) { - - if (_edit.snap || spatial_editor->is_snap_enabled()) { - Basis g = original.basis.orthonormalized(); - Vector3 local_motion = g.inverse().xform(motion); - local_motion.snap(Vector3(snap, snap, snap)); - - motion = g.xform(local_motion); - } - - } else { - - if (_edit.snap || spatial_editor->is_snap_enabled()) { - motion.snap(Vector3(snap, snap, snap)); - } - } - - // Apply translation - t = original; - t.origin += motion; - sp->set_global_transform(t); - } - - surface->update(); - - } break; - - case TRANSFORM_ROTATE: { - - Plane plane; - Vector3 axis; - - switch (_edit.plane) { - case TRANSFORM_VIEW: - plane = Plane(_edit.center, _get_camera_normal()); - break; - case TRANSFORM_X_AXIS: - plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(0)); - axis = Vector3(1, 0, 0); - break; - case TRANSFORM_Y_AXIS: - plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(1)); - axis = Vector3(0, 1, 0); - break; - case TRANSFORM_Z_AXIS: - plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(2)); - axis = Vector3(0, 0, 1); - break; - case TRANSFORM_YZ: - case TRANSFORM_XZ: - case TRANSFORM_XY: - break; - } - - Vector3 intersection; - if (!plane.intersects_ray(ray_pos, ray, &intersection)) - break; - - Vector3 click; - if (!plane.intersects_ray(_edit.click_ray_pos, _edit.click_ray, &click)) - break; - - Vector3 y_axis = (click - _edit.center).normalized(); - Vector3 x_axis = plane.normal.cross(y_axis).normalized(); - - float angle = Math::atan2(x_axis.dot(intersection - _edit.center), y_axis.dot(intersection - _edit.center)); - - if (_edit.snap || spatial_editor->is_snap_enabled()) { - snap = spatial_editor->get_rotate_snap(); - } - angle = Math::rad2deg(angle) + snap * 0.5; //else it won't reach +180 - angle -= Math::fmod(angle, snap); - set_message(vformat(TTR("Rotating %s degrees."), String::num(angle, snap_step_decimals))); - angle = Math::deg2rad(angle); - - List &selection = editor_selection->get_selected_node_list(); - - bool local_coords = (spatial_editor->are_local_coords_enabled() && _edit.plane != TRANSFORM_VIEW); // Disable local transformation for TRANSFORM_VIEW - - for (List::Element *E = selection.front(); E; E = E->next()) { - - Node3D *sp = Object::cast_to(E->get()); - if (!sp) - continue; - - Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data(sp); - if (!se) - continue; - - if (sp->has_meta("_edit_lock_")) { - continue; - } - - Transform t; - - if (local_coords) { - - Transform original_local = se->original_local; - Basis rot = Basis(axis, angle); - - t.basis = original_local.get_basis().orthonormalized() * rot; - t.origin = original_local.origin; - - // Apply rotation - sp->set_transform(t); - sp->set_scale(original_local.basis.get_scale()); // re-apply original scale - - } else { - - Transform original = se->original; - Transform r; - Transform base = Transform(Basis(), _edit.center); - - r.basis.rotate(plane.normal, angle); - t = base * r * base.inverse() * original; - - // Apply rotation - sp->set_global_transform(t); - } - } - - surface->update(); - - } break; - default: { - } - } - } - - } else if ((m->get_button_mask() & BUTTON_MASK_RIGHT) || freelook_active) { - - if (nav_scheme == NAVIGATION_MAYA && m->get_alt()) { - nav_mode = NAVIGATION_ZOOM; - } else if (freelook_active) { - nav_mode = NAVIGATION_LOOK; - } else if (orthogonal) { - nav_mode = NAVIGATION_PAN; - } - - } else if (m->get_button_mask() & BUTTON_MASK_MIDDLE) { - - if (nav_scheme == NAVIGATION_GODOT) { - - const int mod = _get_key_modifier(m); - - if (mod == _get_key_modifier_setting("editors/3d/navigation/pan_modifier")) { - nav_mode = NAVIGATION_PAN; - } else if (mod == _get_key_modifier_setting("editors/3d/navigation/zoom_modifier")) { - nav_mode = NAVIGATION_ZOOM; - } else if (mod == KEY_ALT || mod == _get_key_modifier_setting("editors/3d/navigation/orbit_modifier")) { - // Always allow Alt as a modifier to better support graphic tablets. - nav_mode = NAVIGATION_ORBIT; - } - - } else if (nav_scheme == NAVIGATION_MAYA) { - if (m->get_alt()) - nav_mode = NAVIGATION_PAN; - } - - } else if (EditorSettings::get_singleton()->get("editors/3d/navigation/emulate_3_button_mouse")) { - // Handle trackpad (no external mouse) use case - const int mod = _get_key_modifier(m); - - if (mod) { - if (mod == _get_key_modifier_setting("editors/3d/navigation/pan_modifier")) { - nav_mode = NAVIGATION_PAN; - } else if (mod == _get_key_modifier_setting("editors/3d/navigation/zoom_modifier")) { - nav_mode = NAVIGATION_ZOOM; - } else if (mod == KEY_ALT || mod == _get_key_modifier_setting("editors/3d/navigation/orbit_modifier")) { - // Always allow Alt as a modifier to better support graphic tablets. - nav_mode = NAVIGATION_ORBIT; - } - } - } - - switch (nav_mode) { - case NAVIGATION_PAN: { - _nav_pan(m, _get_warped_mouse_motion(m)); - - } break; - - case NAVIGATION_ZOOM: { - _nav_zoom(m, m->get_relative()); - - } break; - - case NAVIGATION_ORBIT: { - _nav_orbit(m, _get_warped_mouse_motion(m)); - - } break; - - case NAVIGATION_LOOK: { - _nav_look(m, _get_warped_mouse_motion(m)); - - } break; - - default: { - } - } - } - - Ref magnify_gesture = p_event; - if (magnify_gesture.is_valid()) { - - if (is_freelook_active()) - scale_freelook_speed(magnify_gesture->get_factor()); - else - scale_cursor_distance(1.0 / magnify_gesture->get_factor()); - } - - Ref pan_gesture = p_event; - if (pan_gesture.is_valid()) { - - NavigationScheme nav_scheme = (NavigationScheme)EditorSettings::get_singleton()->get("editors/3d/navigation/navigation_scheme").operator int(); - NavigationMode nav_mode = NAVIGATION_NONE; - - if (nav_scheme == NAVIGATION_GODOT) { - - const int mod = _get_key_modifier(pan_gesture); - - if (mod == _get_key_modifier_setting("editors/3d/navigation/pan_modifier")) { - nav_mode = NAVIGATION_PAN; - } else if (mod == _get_key_modifier_setting("editors/3d/navigation/zoom_modifier")) { - nav_mode = NAVIGATION_ZOOM; - } else if (mod == KEY_ALT || mod == _get_key_modifier_setting("editors/3d/navigation/orbit_modifier")) { - // Always allow Alt as a modifier to better support graphic tablets. - nav_mode = NAVIGATION_ORBIT; - } - - } else if (nav_scheme == NAVIGATION_MAYA) { - if (pan_gesture->get_alt()) - nav_mode = NAVIGATION_PAN; - } - - switch (nav_mode) { - case NAVIGATION_PAN: { - _nav_pan(m, pan_gesture->get_delta()); - - } break; - - case NAVIGATION_ZOOM: { - _nav_zoom(m, pan_gesture->get_delta()); - - } break; - - case NAVIGATION_ORBIT: { - _nav_orbit(m, pan_gesture->get_delta()); - - } break; - - case NAVIGATION_LOOK: { - _nav_look(m, pan_gesture->get_delta()); - - } break; - - default: { - } - } - } - - Ref k = p_event; - - if (k.is_valid()) { - if (!k->is_pressed()) - return; - - if (ED_IS_SHORTCUT("spatial_editor/snap", p_event)) { - if (_edit.mode != TRANSFORM_NONE) { - _edit.snap = !_edit.snap; - } - } - if (ED_IS_SHORTCUT("spatial_editor/bottom_view", p_event)) { - _menu_option(VIEW_BOTTOM); - } - if (ED_IS_SHORTCUT("spatial_editor/top_view", p_event)) { - _menu_option(VIEW_TOP); - } - if (ED_IS_SHORTCUT("spatial_editor/rear_view", p_event)) { - _menu_option(VIEW_REAR); - } - if (ED_IS_SHORTCUT("spatial_editor/front_view", p_event)) { - _menu_option(VIEW_FRONT); - } - if (ED_IS_SHORTCUT("spatial_editor/left_view", p_event)) { - _menu_option(VIEW_LEFT); - } - if (ED_IS_SHORTCUT("spatial_editor/right_view", p_event)) { - _menu_option(VIEW_RIGHT); - } - if (ED_IS_SHORTCUT("spatial_editor/focus_origin", p_event)) { - _menu_option(VIEW_CENTER_TO_ORIGIN); - } - if (ED_IS_SHORTCUT("spatial_editor/focus_selection", p_event)) { - _menu_option(VIEW_CENTER_TO_SELECTION); - } - // Orthgonal mode doesn't work in freelook. - if (!freelook_active && ED_IS_SHORTCUT("spatial_editor/switch_perspective_orthogonal", p_event)) { - _menu_option(orthogonal ? VIEW_PERSPECTIVE : VIEW_ORTHOGONAL); - _update_name(); - } - if (ED_IS_SHORTCUT("spatial_editor/align_transform_with_view", p_event)) { - _menu_option(VIEW_ALIGN_TRANSFORM_WITH_VIEW); - } - if (ED_IS_SHORTCUT("spatial_editor/align_rotation_with_view", p_event)) { - _menu_option(VIEW_ALIGN_ROTATION_WITH_VIEW); - } - if (ED_IS_SHORTCUT("spatial_editor/insert_anim_key", p_event)) { - if (!get_selected_count() || _edit.mode != TRANSFORM_NONE) - return; - - if (!AnimationPlayerEditor::singleton->get_track_editor()->has_keying()) { - set_message(TTR("Keying is disabled (no key inserted).")); - return; - } - - List &selection = editor_selection->get_selected_node_list(); - - for (List::Element *E = selection.front(); E; E = E->next()) { - - Node3D *sp = Object::cast_to(E->get()); - if (!sp) - continue; - - spatial_editor->emit_signal("transform_key_request", sp, "", sp->get_transform()); - } - - set_message(TTR("Animation Key Inserted.")); - } - - // Freelook doesn't work in orthogonal mode. - if (!orthogonal && ED_IS_SHORTCUT("spatial_editor/freelook_toggle", p_event)) { - set_freelook_active(!is_freelook_active()); - - } else if (k->get_keycode() == KEY_ESCAPE) { - set_freelook_active(false); - } - - if (k->get_keycode() == KEY_SPACE) { - if (!k->is_pressed()) emit_signal("toggle_maximize_view", this); - } - } - - // freelook uses most of the useful shortcuts, like save, so its ok - // to consider freelook active as end of the line for future events. - if (freelook_active) - accept_event(); -} - -void Node3DEditorViewport::_nav_pan(Ref p_event, const Vector2 &p_relative) { - - const NavigationScheme nav_scheme = (NavigationScheme)EditorSettings::get_singleton()->get("editors/3d/navigation/navigation_scheme").operator int(); - - real_t pan_speed = 1 / 150.0; - int pan_speed_modifier = 10; - if (nav_scheme == NAVIGATION_MAYA && p_event->get_shift()) - pan_speed *= pan_speed_modifier; - - Transform camera_transform; - - camera_transform.translate(cursor.pos); - camera_transform.basis.rotate(Vector3(1, 0, 0), -cursor.x_rot); - camera_transform.basis.rotate(Vector3(0, 1, 0), -cursor.y_rot); - Vector3 translation(-p_relative.x * pan_speed, p_relative.y * pan_speed, 0); - translation *= cursor.distance / DISTANCE_DEFAULT; - camera_transform.translate(translation); - cursor.pos = camera_transform.origin; -} - -void Node3DEditorViewport::_nav_zoom(Ref p_event, const Vector2 &p_relative) { - - const NavigationScheme nav_scheme = (NavigationScheme)EditorSettings::get_singleton()->get("editors/3d/navigation/navigation_scheme").operator int(); - - real_t zoom_speed = 1 / 80.0; - int zoom_speed_modifier = 10; - if (nav_scheme == NAVIGATION_MAYA && p_event->get_shift()) - zoom_speed *= zoom_speed_modifier; - - NavigationZoomStyle zoom_style = (NavigationZoomStyle)EditorSettings::get_singleton()->get("editors/3d/navigation/zoom_style").operator int(); - if (zoom_style == NAVIGATION_ZOOM_HORIZONTAL) { - if (p_relative.x > 0) - scale_cursor_distance(1 - p_relative.x * zoom_speed); - else if (p_relative.x < 0) - scale_cursor_distance(1.0 / (1 + p_relative.x * zoom_speed)); - } else { - if (p_relative.y > 0) - scale_cursor_distance(1 + p_relative.y * zoom_speed); - else if (p_relative.y < 0) - scale_cursor_distance(1.0 / (1 - p_relative.y * zoom_speed)); - } -} - -void Node3DEditorViewport::_nav_orbit(Ref p_event, const Vector2 &p_relative) { - - if (lock_rotation) { - _nav_pan(p_event, p_relative); - return; - } - - if (orthogonal && auto_orthogonal) { - _menu_option(VIEW_PERSPECTIVE); - } - - real_t degrees_per_pixel = EditorSettings::get_singleton()->get("editors/3d/navigation_feel/orbit_sensitivity"); - real_t radians_per_pixel = Math::deg2rad(degrees_per_pixel); - bool invert_y_axis = EditorSettings::get_singleton()->get("editors/3d/navigation/invert_y_axis"); - - if (invert_y_axis) { - cursor.x_rot -= p_relative.y * radians_per_pixel; - } else { - cursor.x_rot += p_relative.y * radians_per_pixel; - } - cursor.y_rot += p_relative.x * radians_per_pixel; - if (cursor.x_rot > Math_PI / 2.0) - cursor.x_rot = Math_PI / 2.0; - if (cursor.x_rot < -Math_PI / 2.0) - cursor.x_rot = -Math_PI / 2.0; - name = ""; - _update_name(); -} - -void Node3DEditorViewport::_nav_look(Ref p_event, const Vector2 &p_relative) { - - if (orthogonal) { - _nav_pan(p_event, p_relative); - return; - } - - if (orthogonal && auto_orthogonal) { - _menu_option(VIEW_PERSPECTIVE); - } - - real_t degrees_per_pixel = EditorSettings::get_singleton()->get("editors/3d/navigation_feel/orbit_sensitivity"); - real_t radians_per_pixel = Math::deg2rad(degrees_per_pixel); - bool invert_y_axis = EditorSettings::get_singleton()->get("editors/3d/navigation/invert_y_axis"); - - // Note: do NOT assume the camera has the "current" transform, because it is interpolated and may have "lag". - Transform prev_camera_transform = to_camera_transform(cursor); - - if (invert_y_axis) { - cursor.x_rot -= p_relative.y * radians_per_pixel; - } else { - cursor.x_rot += p_relative.y * radians_per_pixel; - } - cursor.y_rot += p_relative.x * radians_per_pixel; - if (cursor.x_rot > Math_PI / 2.0) - cursor.x_rot = Math_PI / 2.0; - if (cursor.x_rot < -Math_PI / 2.0) - cursor.x_rot = -Math_PI / 2.0; - - // Look is like the opposite of Orbit: the focus point rotates around the camera - Transform camera_transform = to_camera_transform(cursor); - Vector3 pos = camera_transform.xform(Vector3(0, 0, 0)); - Vector3 prev_pos = prev_camera_transform.xform(Vector3(0, 0, 0)); - Vector3 diff = prev_pos - pos; - cursor.pos += diff; - - name = ""; - _update_name(); -} - -void Node3DEditorViewport::set_freelook_active(bool active_now) { - - if (!freelook_active && active_now) { - // Sync camera cursor to cursor to "cut" interpolation jumps due to changing referential - cursor = camera_cursor; - - // Make sure eye_pos is synced, because freelook referential is eye pos rather than orbit pos - Vector3 forward = to_camera_transform(cursor).basis.xform(Vector3(0, 0, -1)); - cursor.eye_pos = cursor.pos - cursor.distance * forward; - // Also sync the camera cursor, otherwise switching to freelook will be trippy if inertia is active - camera_cursor.eye_pos = cursor.eye_pos; - - if (EditorSettings::get_singleton()->get("editors/3d/freelook/freelook_speed_zoom_link")) { - // Re-adjust freelook speed from the current zoom level - real_t base_speed = EditorSettings::get_singleton()->get("editors/3d/freelook/freelook_base_speed"); - freelook_speed = base_speed * cursor.distance; - } - - // Hide mouse like in an FPS (warping doesn't work) - DisplayServer::get_singleton()->mouse_set_mode(DisplayServer::MOUSE_MODE_CAPTURED); - - } else if (freelook_active && !active_now) { - // Sync camera cursor to cursor to "cut" interpolation jumps due to changing referential - cursor = camera_cursor; - - // Restore mouse - DisplayServer::get_singleton()->mouse_set_mode(DisplayServer::MOUSE_MODE_VISIBLE); - } - - freelook_active = active_now; -} - -void Node3DEditorViewport::scale_cursor_distance(real_t scale) { - - // Prevents zero distance which would short-circuit any scaling - if (cursor.distance < ZOOM_MIN_DISTANCE) - cursor.distance = ZOOM_MIN_DISTANCE; - - cursor.distance *= scale; - - if (cursor.distance < ZOOM_MIN_DISTANCE) - cursor.distance = ZOOM_MIN_DISTANCE; - - zoom_indicator_delay = ZOOM_INDICATOR_DELAY_S; - surface->update(); -} - -void Node3DEditorViewport::scale_freelook_speed(real_t scale) { - - // Prevents zero distance which would short-circuit any scaling - if (freelook_speed < FREELOOK_MIN_SPEED) - freelook_speed = FREELOOK_MIN_SPEED; - - freelook_speed *= scale; - - if (freelook_speed < FREELOOK_MIN_SPEED) - freelook_speed = FREELOOK_MIN_SPEED; - - zoom_indicator_delay = ZOOM_INDICATOR_DELAY_S; - surface->update(); -} - -Point2i Node3DEditorViewport::_get_warped_mouse_motion(const Ref &p_ev_mouse_motion) const { - Point2i relative; - if (bool(EDITOR_DEF("editors/3d/navigation/warped_mouse_panning", false))) { - relative = InputFilter::get_singleton()->warp_mouse_motion(p_ev_mouse_motion, surface->get_global_rect()); - } else { - relative = p_ev_mouse_motion->get_relative(); - } - return relative; -} - -static bool is_shortcut_pressed(const String &p_path) { - Ref shortcut = ED_GET_SHORTCUT(p_path); - if (shortcut.is_null()) { - return false; - } - InputEventKey *k = Object::cast_to(shortcut->get_shortcut().ptr()); - if (k == NULL) { - return false; - } - const InputFilter &input = *InputFilter::get_singleton(); - int keycode = k->get_keycode(); - return input.is_key_pressed(keycode); -} - -void Node3DEditorViewport::_update_freelook(real_t delta) { - - if (!is_freelook_active()) { - return; - } - - const Vector3 forward = camera->get_transform().basis.xform(Vector3(0, 0, -1)); - const Vector3 right = camera->get_transform().basis.xform(Vector3(1, 0, 0)); - const Vector3 up = camera->get_transform().basis.xform(Vector3(0, 1, 0)); - - Vector3 direction; - - if (is_shortcut_pressed("spatial_editor/freelook_left")) { - direction -= right; - } - if (is_shortcut_pressed("spatial_editor/freelook_right")) { - direction += right; - } - if (is_shortcut_pressed("spatial_editor/freelook_forward")) { - direction += forward; - } - if (is_shortcut_pressed("spatial_editor/freelook_backwards")) { - direction -= forward; - } - if (is_shortcut_pressed("spatial_editor/freelook_up")) { - direction += up; - } - if (is_shortcut_pressed("spatial_editor/freelook_down")) { - direction -= up; - } - - real_t speed = freelook_speed; - - if (is_shortcut_pressed("spatial_editor/freelook_speed_modifier")) { - speed *= 3.0; - } - if (is_shortcut_pressed("spatial_editor/freelook_slow_modifier")) { - speed *= 0.333333; - } - - const Vector3 motion = direction * speed * delta; - cursor.pos += motion; - cursor.eye_pos += motion; -} - -void Node3DEditorViewport::set_message(String p_message, float p_time) { - - message = p_message; - message_time = p_time; -} - -void Node3DEditorPlugin::edited_scene_changed() { - for (uint32_t i = 0; i < Node3DEditor::VIEWPORTS_COUNT; i++) { - Node3DEditorViewport *viewport = Node3DEditor::get_singleton()->get_editor_viewport(i); - if (viewport->is_visible()) { - viewport->notification(Control::NOTIFICATION_VISIBILITY_CHANGED); - } - } -} - -void Node3DEditorViewport::_notification(int p_what) { - - if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { - - bool visible = is_visible_in_tree(); - - set_process(visible); - - if (visible) { - orthogonal = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_ORTHOGONAL)); - _update_name(); - _update_camera(0); - } else { - set_freelook_active(false); - } - call_deferred("update_transform_gizmo_view"); - rotation_control->set_visible(EditorSettings::get_singleton()->get("editors/3d/navigation/show_viewport_rotation_gizmo")); - } - - if (p_what == NOTIFICATION_RESIZED) { - - call_deferred("update_transform_gizmo_view"); - } - - if (p_what == NOTIFICATION_READY) { - // The crosshair icon doesn't depend on the editor theme. - crosshair->set_texture(get_theme_icon("Crosshair", "EditorIcons")); - // Set the anchors and margins after changing the icon to ensure it's centered correctly. - crosshair->set_anchors_and_margins_preset(PRESET_CENTER); - } - - if (p_what == NOTIFICATION_PROCESS) { - - real_t delta = get_process_delta_time(); - - if (zoom_indicator_delay > 0) { - zoom_indicator_delay -= delta; - if (zoom_indicator_delay <= 0) { - surface->update(); - } - } - - _update_freelook(delta); - - Node *scene_root = editor->get_scene_tree_dock()->get_editor_data()->get_edited_scene_root(); - if (previewing_cinema && scene_root != NULL) { - Camera3D *cam = scene_root->get_viewport()->get_camera(); - if (cam != NULL && cam != previewing) { - //then switch the viewport's camera to the scene's viewport camera - if (previewing != NULL) { - previewing->disconnect("tree_exited", callable_mp(this, &Node3DEditorViewport::_preview_exited_scene)); - } - previewing = cam; - previewing->connect("tree_exited", callable_mp(this, &Node3DEditorViewport::_preview_exited_scene)); - VS::get_singleton()->viewport_attach_camera(viewport->get_viewport_rid(), cam->get_camera()); - surface->update(); - } - } - - _update_camera(delta); - - Map &selection = editor_selection->get_selection(); - - bool changed = false; - bool exist = false; - - for (Map::Element *E = selection.front(); E; E = E->next()) { - - Node3D *sp = Object::cast_to(E->key()); - if (!sp) - continue; - - Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data(sp); - if (!se) - continue; - - Transform t = sp->get_global_gizmo_transform(); - - exist = true; - if (se->last_xform == t && !se->last_xform_dirty) - continue; - changed = true; - se->last_xform_dirty = false; - se->last_xform = t; - - VisualInstance3D *vi = Object::cast_to(sp); - - se->aabb = vi ? vi->get_aabb() : _calculate_spatial_bounds(sp); - - t.translate(se->aabb.position); - - // apply AABB scaling before item's global transform - Basis aabb_s; - aabb_s.scale(se->aabb.size); - t.basis = t.basis * aabb_s; - - VisualServer::get_singleton()->instance_set_transform(se->sbox_instance, t); - } - - if (changed || (spatial_editor->is_gizmo_visible() && !exist)) { - spatial_editor->update_transform_gizmo(); - } - - if (message_time > 0) { - - if (message != last_message) { - surface->update(); - last_message = message; - } - - message_time -= get_physics_process_delta_time(); - if (message_time < 0) - surface->update(); - } - - //update shadow atlas if changed - - int shadowmap_size = ProjectSettings::get_singleton()->get("rendering/quality/shadow_atlas/size"); - int atlas_q0 = ProjectSettings::get_singleton()->get("rendering/quality/shadow_atlas/quadrant_0_subdiv"); - int atlas_q1 = ProjectSettings::get_singleton()->get("rendering/quality/shadow_atlas/quadrant_1_subdiv"); - int atlas_q2 = ProjectSettings::get_singleton()->get("rendering/quality/shadow_atlas/quadrant_2_subdiv"); - int atlas_q3 = ProjectSettings::get_singleton()->get("rendering/quality/shadow_atlas/quadrant_3_subdiv"); - - viewport->set_shadow_atlas_size(shadowmap_size); - viewport->set_shadow_atlas_quadrant_subdiv(0, Viewport::ShadowAtlasQuadrantSubdiv(atlas_q0)); - viewport->set_shadow_atlas_quadrant_subdiv(1, Viewport::ShadowAtlasQuadrantSubdiv(atlas_q1)); - viewport->set_shadow_atlas_quadrant_subdiv(2, Viewport::ShadowAtlasQuadrantSubdiv(atlas_q2)); - viewport->set_shadow_atlas_quadrant_subdiv(3, Viewport::ShadowAtlasQuadrantSubdiv(atlas_q3)); - - bool shrink = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_HALF_RESOLUTION)); - - if (shrink != (viewport_container->get_stretch_shrink() > 1)) { - viewport_container->set_stretch_shrink(shrink ? 2 : 1); - } - - //update msaa if changed - - int msaa_mode = ProjectSettings::get_singleton()->get("rendering/quality/filters/msaa"); - viewport->set_msaa(Viewport::MSAA(msaa_mode)); - - bool show_info = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_INFORMATION)); - info_label->set_visible(show_info); - - Camera3D *current_camera; - - if (previewing) { - current_camera = previewing; - } else { - current_camera = camera; - } - - // Display the crosshair only while freelooking. Hide it otherwise, - // as the crosshair can be distracting. - crosshair->set_visible(freelook_active); - - if (show_info) { - String text; - text += "X: " + rtos(current_camera->get_translation().x).pad_decimals(1) + "\n"; - text += "Y: " + rtos(current_camera->get_translation().y).pad_decimals(1) + "\n"; - text += "Z: " + rtos(current_camera->get_translation().z).pad_decimals(1) + "\n"; - text += TTR("Pitch") + ": " + itos(Math::round(current_camera->get_rotation_degrees().x)) + "\n"; - text += TTR("Yaw") + ": " + itos(Math::round(current_camera->get_rotation_degrees().y)) + "\n\n"; - text += TTR("Objects Drawn") + ": " + itos(viewport->get_render_info(Viewport::RENDER_INFO_OBJECTS_IN_FRAME)) + "\n"; - text += TTR("Material Changes") + ": " + itos(viewport->get_render_info(Viewport::RENDER_INFO_MATERIAL_CHANGES_IN_FRAME)) + "\n"; - text += TTR("Shader Changes") + ": " + itos(viewport->get_render_info(Viewport::RENDER_INFO_SHADER_CHANGES_IN_FRAME)) + "\n"; - text += TTR("Surface Changes") + ": " + itos(viewport->get_render_info(Viewport::RENDER_INFO_SURFACE_CHANGES_IN_FRAME)) + "\n"; - text += TTR("Draw Calls") + ": " + itos(viewport->get_render_info(Viewport::RENDER_INFO_DRAW_CALLS_IN_FRAME)) + "\n"; - text += TTR("Vertices") + ": " + itos(viewport->get_render_info(Viewport::RENDER_INFO_VERTICES_IN_FRAME)); - info_label->set_text(text); - } - - // FPS Counter. - bool show_fps = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_FPS)); - fps_label->set_visible(show_fps); - - if (show_fps) { - String text; - const float temp_fps = Engine::get_singleton()->get_frames_per_second(); - text += TTR(vformat("FPS: %d (%s ms)", temp_fps, String::num(1000.0f / temp_fps, 2))); - fps_label->set_text(text); - } - - bool show_cinema = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_CINEMATIC_PREVIEW)); - cinema_label->set_visible(show_cinema); - if (show_cinema) { - float cinema_half_width = cinema_label->get_size().width / 2.0f; - cinema_label->set_anchor_and_margin(MARGIN_LEFT, 0.5f, -cinema_half_width); - } - - if (lock_rotation) { - float locked_half_width = locked_label->get_size().width / 2.0f; - locked_label->set_anchor_and_margin(MARGIN_LEFT, 0.5f, -locked_half_width); - } - } - - if (p_what == NOTIFICATION_ENTER_TREE) { - - surface->connect("draw", callable_mp(this, &Node3DEditorViewport::_draw)); - surface->connect("gui_input", callable_mp(this, &Node3DEditorViewport::_sinput)); - surface->connect("mouse_entered", callable_mp(this, &Node3DEditorViewport::_surface_mouse_enter)); - surface->connect("mouse_exited", callable_mp(this, &Node3DEditorViewport::_surface_mouse_exit)); - surface->connect("focus_entered", callable_mp(this, &Node3DEditorViewport::_surface_focus_enter)); - surface->connect("focus_exited", callable_mp(this, &Node3DEditorViewport::_surface_focus_exit)); - - _init_gizmo_instance(index); - } - - if (p_what == NOTIFICATION_EXIT_TREE) { - - _finish_gizmo_instances(); - } - - if (p_what == NOTIFICATION_THEME_CHANGED) { - - view_menu->set_icon(get_theme_icon("GuiTabMenu", "EditorIcons")); - preview_camera->set_icon(get_theme_icon("Camera3D", "EditorIcons")); - - view_menu->add_theme_style_override("normal", editor->get_gui_base()->get_theme_stylebox("Information3dViewport", "EditorStyles")); - view_menu->add_theme_style_override("hover", editor->get_gui_base()->get_theme_stylebox("Information3dViewport", "EditorStyles")); - view_menu->add_theme_style_override("pressed", editor->get_gui_base()->get_theme_stylebox("Information3dViewport", "EditorStyles")); - view_menu->add_theme_style_override("focus", editor->get_gui_base()->get_theme_stylebox("Information3dViewport", "EditorStyles")); - view_menu->add_theme_style_override("disabled", editor->get_gui_base()->get_theme_stylebox("Information3dViewport", "EditorStyles")); - - preview_camera->add_theme_style_override("normal", editor->get_gui_base()->get_theme_stylebox("Information3dViewport", "EditorStyles")); - preview_camera->add_theme_style_override("hover", editor->get_gui_base()->get_theme_stylebox("Information3dViewport", "EditorStyles")); - preview_camera->add_theme_style_override("pressed", editor->get_gui_base()->get_theme_stylebox("Information3dViewport", "EditorStyles")); - preview_camera->add_theme_style_override("focus", editor->get_gui_base()->get_theme_stylebox("Information3dViewport", "EditorStyles")); - preview_camera->add_theme_style_override("disabled", editor->get_gui_base()->get_theme_stylebox("Information3dViewport", "EditorStyles")); - - info_label->add_theme_style_override("normal", editor->get_gui_base()->get_theme_stylebox("Information3dViewport", "EditorStyles")); - fps_label->add_theme_style_override("normal", editor->get_gui_base()->get_theme_stylebox("Information3dViewport", "EditorStyles")); - cinema_label->add_theme_style_override("normal", editor->get_gui_base()->get_theme_stylebox("Information3dViewport", "EditorStyles")); - locked_label->add_theme_style_override("normal", editor->get_gui_base()->get_theme_stylebox("Information3dViewport", "EditorStyles")); - } -} - -static void draw_indicator_bar(Control &surface, real_t fill, Ref icon) { - - // Adjust bar size from control height - Vector2 surface_size = surface.get_size(); - real_t h = surface_size.y / 2.0; - real_t y = (surface_size.y - h) / 2.0; - - Rect2 r(10, y, 6, h); - real_t sy = r.size.y * fill; - - // Note: because this bar appears over the viewport, it has to stay readable for any background color - // Draw both neutral dark and bright colors to account this - surface.draw_rect(r, Color(1, 1, 1, 0.2)); - surface.draw_rect(Rect2(r.position.x, r.position.y + r.size.y - sy, r.size.x, sy), Color(1, 1, 1, 0.6)); - surface.draw_rect(r.grow(1), Color(0, 0, 0, 0.7), false, Math::round(EDSCALE)); - - Vector2 icon_size = icon->get_size(); - Vector2 icon_pos = Vector2(r.position.x - (icon_size.x - r.size.x) / 2, r.position.y + r.size.y + 2); - surface.draw_texture(icon, icon_pos); -} - -void Node3DEditorViewport::_draw() { - - EditorPluginList *over_plugin_list = EditorNode::get_singleton()->get_editor_plugins_over(); - if (!over_plugin_list->empty()) { - over_plugin_list->forward_spatial_draw_over_viewport(surface); - } - - EditorPluginList *force_over_plugin_list = editor->get_editor_plugins_force_over(); - if (!force_over_plugin_list->empty()) { - force_over_plugin_list->forward_spatial_force_draw_over_viewport(surface); - } - - if (surface->has_focus()) { - Size2 size = surface->get_size(); - Rect2 r = Rect2(Point2(), size); - get_theme_stylebox("Focus", "EditorStyles")->draw(surface->get_canvas_item(), r); - } - - if (cursor.region_select) { - const Rect2 selection_rect = Rect2(cursor.region_begin, cursor.region_end - cursor.region_begin); - - surface->draw_rect( - selection_rect, - get_theme_color("box_selection_fill_color", "Editor")); - - surface->draw_rect( - selection_rect, - get_theme_color("box_selection_stroke_color", "Editor"), - false, - Math::round(EDSCALE)); - } - - RID ci = surface->get_canvas_item(); - - if (message_time > 0) { - Ref font = get_theme_font("font", "Label"); - Point2 msgpos = Point2(5, get_size().y - 20); - font->draw(ci, msgpos + Point2(1, 1), message, Color(0, 0, 0, 0.8)); - font->draw(ci, msgpos + Point2(-1, -1), message, Color(0, 0, 0, 0.8)); - font->draw(ci, msgpos, message, Color(1, 1, 1, 1)); - } - - if (_edit.mode == TRANSFORM_ROTATE) { - - Point2 center = _point_to_screen(_edit.center); - VisualServer::get_singleton()->canvas_item_add_line( - ci, - _edit.mouse_pos, - center, - get_theme_color("accent_color", "Editor") * Color(1, 1, 1, 0.6), - Math::round(2 * EDSCALE)); - } - if (previewing) { - - Size2 ss = Size2(ProjectSettings::get_singleton()->get("display/window/size/width"), ProjectSettings::get_singleton()->get("display/window/size/height")); - float aspect = ss.aspect(); - Size2 s = get_size(); - - Rect2 draw_rect; - - switch (previewing->get_keep_aspect_mode()) { - case Camera3D::KEEP_WIDTH: { - - draw_rect.size = Size2(s.width, s.width / aspect); - draw_rect.position.x = 0; - draw_rect.position.y = (s.height - draw_rect.size.y) * 0.5; - - } break; - case Camera3D::KEEP_HEIGHT: { - - draw_rect.size = Size2(s.height * aspect, s.height); - draw_rect.position.y = 0; - draw_rect.position.x = (s.width - draw_rect.size.x) * 0.5; - - } break; - } - - draw_rect = Rect2(Vector2(), s).clip(draw_rect); - - surface->draw_rect(draw_rect, Color(0.6, 0.6, 0.1, 0.5), false, Math::round(2 * EDSCALE)); - - } else { - - if (zoom_indicator_delay > 0.0) { - - if (is_freelook_active()) { - // Show speed - - real_t min_speed = FREELOOK_MIN_SPEED; - real_t max_speed = camera->get_zfar(); - real_t scale_length = (max_speed - min_speed); - - if (!Math::is_zero_approx(scale_length)) { - real_t logscale_t = 1.0 - Math::log(1 + freelook_speed - min_speed) / Math::log(1 + scale_length); - - // There is no real maximum speed so that factor can become negative, - // Let's make it look asymptotic instead (will decrease slower and slower). - if (logscale_t < 0.25) - logscale_t = 0.25 * Math::exp(4.0 * logscale_t - 1.0); - - draw_indicator_bar(*surface, 1.0 - logscale_t, get_theme_icon("ViewportSpeed", "EditorIcons")); - } - - } else { - // Show zoom - - real_t min_distance = ZOOM_MIN_DISTANCE; // TODO Why not pick znear to limit zoom? - real_t max_distance = camera->get_zfar(); - real_t scale_length = (max_distance - min_distance); - - if (!Math::is_zero_approx(scale_length)) { - real_t logscale_t = 1.0 - Math::log(1 + cursor.distance - min_distance) / Math::log(1 + scale_length); - - // There is no real maximum distance so that factor can become negative, - // Let's make it look asymptotic instead (will decrease slower and slower). - if (logscale_t < 0.25) - logscale_t = 0.25 * Math::exp(4.0 * logscale_t - 1.0); - - draw_indicator_bar(*surface, logscale_t, get_theme_icon("ViewportZoom", "EditorIcons")); - } - } - } - } -} - -void Node3DEditorViewport::_menu_option(int p_option) { - - switch (p_option) { - - case VIEW_TOP: { - - cursor.y_rot = 0; - cursor.x_rot = Math_PI / 2.0; - set_message(TTR("Top View."), 2); - name = TTR("Top"); - _set_auto_orthogonal(); - _update_name(); - - } break; - case VIEW_BOTTOM: { - - cursor.y_rot = 0; - cursor.x_rot = -Math_PI / 2.0; - set_message(TTR("Bottom View."), 2); - name = TTR("Bottom"); - _set_auto_orthogonal(); - _update_name(); - - } break; - case VIEW_LEFT: { - - cursor.x_rot = 0; - cursor.y_rot = Math_PI / 2.0; - set_message(TTR("Left View."), 2); - name = TTR("Left"); - _set_auto_orthogonal(); - _update_name(); - - } break; - case VIEW_RIGHT: { - - cursor.x_rot = 0; - cursor.y_rot = -Math_PI / 2.0; - set_message(TTR("Right View."), 2); - name = TTR("Right"); - _set_auto_orthogonal(); - _update_name(); - - } break; - case VIEW_FRONT: { - - cursor.x_rot = 0; - cursor.y_rot = 0; - set_message(TTR("Front View."), 2); - name = TTR("Front"); - _set_auto_orthogonal(); - _update_name(); - - } break; - case VIEW_REAR: { - - cursor.x_rot = 0; - cursor.y_rot = Math_PI; - set_message(TTR("Rear View."), 2); - name = TTR("Rear"); - _set_auto_orthogonal(); - _update_name(); - - } break; - case VIEW_CENTER_TO_ORIGIN: { - - cursor.pos = Vector3(0, 0, 0); - - } break; - case VIEW_CENTER_TO_SELECTION: { - - focus_selection(); - - } break; - case VIEW_ALIGN_TRANSFORM_WITH_VIEW: { - - if (!get_selected_count()) - break; - - Transform camera_transform = camera->get_global_transform(); - - List &selection = editor_selection->get_selected_node_list(); - - undo_redo->create_action(TTR("Align Transform with View")); - - for (List::Element *E = selection.front(); E; E = E->next()) { - - Node3D *sp = Object::cast_to(E->get()); - if (!sp) - continue; - - Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data(sp); - if (!se) - continue; - - Transform xform; - if (orthogonal) { - xform = sp->get_global_transform(); - xform.basis.set_euler(camera_transform.basis.get_euler()); - } else { - xform = camera_transform; - xform.scale_basis(sp->get_scale()); - } - - undo_redo->add_do_method(sp, "set_global_transform", xform); - undo_redo->add_undo_method(sp, "set_global_transform", sp->get_global_gizmo_transform()); - } - undo_redo->commit_action(); - focus_selection(); - - } break; - case VIEW_ALIGN_ROTATION_WITH_VIEW: { - - if (!get_selected_count()) - break; - - Transform camera_transform = camera->get_global_transform(); - - List &selection = editor_selection->get_selected_node_list(); - - undo_redo->create_action(TTR("Align Rotation with View")); - for (List::Element *E = selection.front(); E; E = E->next()) { - - Node3D *sp = Object::cast_to(E->get()); - if (!sp) - continue; - - Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data(sp); - if (!se) - continue; - - undo_redo->add_do_method(sp, "set_rotation", camera_transform.basis.get_rotation()); - undo_redo->add_undo_method(sp, "set_rotation", sp->get_rotation()); - } - undo_redo->commit_action(); - - } break; - case VIEW_ENVIRONMENT: { - - int idx = view_menu->get_popup()->get_item_index(VIEW_ENVIRONMENT); - bool current = view_menu->get_popup()->is_item_checked(idx); - current = !current; - if (current) { - - camera->set_environment(RES()); - } else { - - camera->set_environment(Node3DEditor::get_singleton()->get_viewport_environment()); - } - - view_menu->get_popup()->set_item_checked(idx, current); - - } break; - case VIEW_PERSPECTIVE: { - - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_PERSPECTIVE), true); - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_ORTHOGONAL), false); - orthogonal = false; - auto_orthogonal = false; - call_deferred("update_transform_gizmo_view"); - _update_name(); - - } break; - case VIEW_ORTHOGONAL: { - - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_PERSPECTIVE), false); - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_ORTHOGONAL), true); - orthogonal = true; - auto_orthogonal = false; - call_deferred("update_transform_gizmo_view"); - _update_name(); - - } break; - case VIEW_AUTO_ORTHOGONAL: { - - int idx = view_menu->get_popup()->get_item_index(VIEW_AUTO_ORTHOGONAL); - bool current = view_menu->get_popup()->is_item_checked(idx); - current = !current; - view_menu->get_popup()->set_item_checked(idx, current); - if (auto_orthogonal) { - auto_orthogonal = false; - _update_name(); - } - } break; - case VIEW_LOCK_ROTATION: { - - int idx = view_menu->get_popup()->get_item_index(VIEW_LOCK_ROTATION); - bool current = view_menu->get_popup()->is_item_checked(idx); - lock_rotation = !current; - view_menu->get_popup()->set_item_checked(idx, !current); - if (lock_rotation) { - locked_label->show(); - } else { - locked_label->hide(); - } - - } break; - case VIEW_AUDIO_LISTENER: { - - int idx = view_menu->get_popup()->get_item_index(VIEW_AUDIO_LISTENER); - bool current = view_menu->get_popup()->is_item_checked(idx); - current = !current; - viewport->set_as_audio_listener(current); - view_menu->get_popup()->set_item_checked(idx, current); - - } break; - case VIEW_AUDIO_DOPPLER: { - - int idx = view_menu->get_popup()->get_item_index(VIEW_AUDIO_DOPPLER); - bool current = view_menu->get_popup()->is_item_checked(idx); - current = !current; - camera->set_doppler_tracking(current ? Camera3D::DOPPLER_TRACKING_IDLE_STEP : Camera3D::DOPPLER_TRACKING_DISABLED); - view_menu->get_popup()->set_item_checked(idx, current); - - } break; - case VIEW_CINEMATIC_PREVIEW: { - - int idx = view_menu->get_popup()->get_item_index(VIEW_CINEMATIC_PREVIEW); - bool current = view_menu->get_popup()->is_item_checked(idx); - current = !current; - view_menu->get_popup()->set_item_checked(idx, current); - previewing_cinema = true; - _toggle_cinema_preview(current); - - if (current) { - preview_camera->hide(); - } else { - if (previewing != NULL) - preview_camera->show(); - } - } break; - case VIEW_GIZMOS: { - - int idx = view_menu->get_popup()->get_item_index(VIEW_GIZMOS); - bool current = view_menu->get_popup()->is_item_checked(idx); - current = !current; - if (current) - camera->set_cull_mask(((1 << 20) - 1) | (1 << (GIZMO_BASE_LAYER + index)) | (1 << GIZMO_EDIT_LAYER) | (1 << GIZMO_GRID_LAYER)); - else - camera->set_cull_mask(((1 << 20) - 1) | (1 << (GIZMO_BASE_LAYER + index)) | (1 << GIZMO_GRID_LAYER)); - view_menu->get_popup()->set_item_checked(idx, current); - - } break; - case VIEW_HALF_RESOLUTION: { - - int idx = view_menu->get_popup()->get_item_index(VIEW_HALF_RESOLUTION); - bool current = view_menu->get_popup()->is_item_checked(idx); - current = !current; - view_menu->get_popup()->set_item_checked(idx, current); - } break; - case VIEW_INFORMATION: { - - int idx = view_menu->get_popup()->get_item_index(VIEW_INFORMATION); - bool current = view_menu->get_popup()->is_item_checked(idx); - view_menu->get_popup()->set_item_checked(idx, !current); - - } break; - case VIEW_FPS: { - - int idx = view_menu->get_popup()->get_item_index(VIEW_FPS); - bool current = view_menu->get_popup()->is_item_checked(idx); - view_menu->get_popup()->set_item_checked(idx, !current); - - } break; - case VIEW_DISPLAY_NORMAL: - case VIEW_DISPLAY_WIREFRAME: - case VIEW_DISPLAY_OVERDRAW: - case VIEW_DISPLAY_SHADELESS: - case VIEW_DISPLAY_LIGHTING: - case VIEW_DISPLAY_NORMAL_BUFFER: - case VIEW_DISPLAY_DEBUG_SHADOW_ATLAS: - case VIEW_DISPLAY_DEBUG_DIRECTIONAL_SHADOW_ATLAS: - case VIEW_DISPLAY_DEBUG_GIPROBE_ALBEDO: - case VIEW_DISPLAY_DEBUG_GIPROBE_LIGHTING: - case VIEW_DISPLAY_DEBUG_GIPROBE_EMISSION: - case VIEW_DISPLAY_DEBUG_SCENE_LUMINANCE: - case VIEW_DISPLAY_DEBUG_SSAO: - case VIEW_DISPLAY_DEBUG_ROUGHNESS_LIMITER: { - - static const int display_options[] = { - VIEW_DISPLAY_NORMAL, - VIEW_DISPLAY_WIREFRAME, - VIEW_DISPLAY_OVERDRAW, - VIEW_DISPLAY_SHADELESS, - VIEW_DISPLAY_LIGHTING, - VIEW_DISPLAY_NORMAL_BUFFER, - VIEW_DISPLAY_WIREFRAME, - VIEW_DISPLAY_DEBUG_SHADOW_ATLAS, - VIEW_DISPLAY_DEBUG_DIRECTIONAL_SHADOW_ATLAS, - VIEW_DISPLAY_DEBUG_GIPROBE_ALBEDO, - VIEW_DISPLAY_DEBUG_GIPROBE_LIGHTING, - VIEW_DISPLAY_DEBUG_GIPROBE_EMISSION, - VIEW_DISPLAY_DEBUG_SCENE_LUMINANCE, - VIEW_DISPLAY_DEBUG_SSAO, - VIEW_DISPLAY_DEBUG_ROUGHNESS_LIMITER, - VIEW_MAX - }; - static const Viewport::DebugDraw debug_draw_modes[] = { - Viewport::DEBUG_DRAW_DISABLED, - Viewport::DEBUG_DRAW_WIREFRAME, - Viewport::DEBUG_DRAW_OVERDRAW, - Viewport::DEBUG_DRAW_UNSHADED, - Viewport::DEBUG_DRAW_LIGHTING, - Viewport::DEBUG_DRAW_NORMAL_BUFFER, - Viewport::DEBUG_DRAW_WIREFRAME, - Viewport::DEBUG_DRAW_SHADOW_ATLAS, - Viewport::DEBUG_DRAW_DIRECTIONAL_SHADOW_ATLAS, - Viewport::DEBUG_DRAW_GI_PROBE_ALBEDO, - Viewport::DEBUG_DRAW_GI_PROBE_LIGHTING, - Viewport::DEBUG_DRAW_GI_PROBE_EMISSION, - Viewport::DEBUG_DRAW_SCENE_LUMINANCE, - Viewport::DEBUG_DRAW_SSAO, - Viewport::DEBUG_DRAW_ROUGHNESS_LIMITER, - }; - - int idx = 0; - - while (display_options[idx] != VIEW_MAX) { - - int id = display_options[idx]; - int item_idx = view_menu->get_popup()->get_item_index(id); - if (item_idx != -1) { - view_menu->get_popup()->set_item_checked(item_idx, id == p_option); - } - item_idx = display_submenu->get_item_index(id); - if (item_idx != -1) { - display_submenu->set_item_checked(item_idx, id == p_option); - } - - if (id == p_option) { - viewport->set_debug_draw(debug_draw_modes[idx]); - } - idx++; - } - } break; - } -} - -void Node3DEditorViewport::_set_auto_orthogonal() { - if (!orthogonal && view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_AUTO_ORTHOGONAL))) { - _menu_option(VIEW_ORTHOGONAL); - auto_orthogonal = true; - } -} - -void Node3DEditorViewport::_preview_exited_scene() { - - preview_camera->disconnect("toggled", callable_mp(this, &Node3DEditorViewport::_toggle_camera_preview)); - preview_camera->set_pressed(false); - _toggle_camera_preview(false); - preview_camera->connect("toggled", callable_mp(this, &Node3DEditorViewport::_toggle_camera_preview)); - view_menu->show(); -} - -void Node3DEditorViewport::_init_gizmo_instance(int p_idx) { - - uint32_t layer = 1 << (GIZMO_BASE_LAYER + p_idx); - - for (int i = 0; i < 3; i++) { - move_gizmo_instance[i] = VS::get_singleton()->instance_create(); - VS::get_singleton()->instance_set_base(move_gizmo_instance[i], spatial_editor->get_move_gizmo(i)->get_rid()); - VS::get_singleton()->instance_set_scenario(move_gizmo_instance[i], get_tree()->get_root()->get_world()->get_scenario()); - VS::get_singleton()->instance_set_visible(move_gizmo_instance[i], false); - VS::get_singleton()->instance_geometry_set_cast_shadows_setting(move_gizmo_instance[i], VS::SHADOW_CASTING_SETTING_OFF); - VS::get_singleton()->instance_set_layer_mask(move_gizmo_instance[i], layer); - - move_plane_gizmo_instance[i] = VS::get_singleton()->instance_create(); - VS::get_singleton()->instance_set_base(move_plane_gizmo_instance[i], spatial_editor->get_move_plane_gizmo(i)->get_rid()); - VS::get_singleton()->instance_set_scenario(move_plane_gizmo_instance[i], get_tree()->get_root()->get_world()->get_scenario()); - VS::get_singleton()->instance_set_visible(move_plane_gizmo_instance[i], false); - VS::get_singleton()->instance_geometry_set_cast_shadows_setting(move_plane_gizmo_instance[i], VS::SHADOW_CASTING_SETTING_OFF); - VS::get_singleton()->instance_set_layer_mask(move_plane_gizmo_instance[i], layer); - - rotate_gizmo_instance[i] = VS::get_singleton()->instance_create(); - VS::get_singleton()->instance_set_base(rotate_gizmo_instance[i], spatial_editor->get_rotate_gizmo(i)->get_rid()); - VS::get_singleton()->instance_set_scenario(rotate_gizmo_instance[i], get_tree()->get_root()->get_world()->get_scenario()); - VS::get_singleton()->instance_set_visible(rotate_gizmo_instance[i], false); - VS::get_singleton()->instance_geometry_set_cast_shadows_setting(rotate_gizmo_instance[i], VS::SHADOW_CASTING_SETTING_OFF); - VS::get_singleton()->instance_set_layer_mask(rotate_gizmo_instance[i], layer); - - scale_gizmo_instance[i] = VS::get_singleton()->instance_create(); - VS::get_singleton()->instance_set_base(scale_gizmo_instance[i], spatial_editor->get_scale_gizmo(i)->get_rid()); - VS::get_singleton()->instance_set_scenario(scale_gizmo_instance[i], get_tree()->get_root()->get_world()->get_scenario()); - VS::get_singleton()->instance_set_visible(scale_gizmo_instance[i], false); - VS::get_singleton()->instance_geometry_set_cast_shadows_setting(scale_gizmo_instance[i], VS::SHADOW_CASTING_SETTING_OFF); - VS::get_singleton()->instance_set_layer_mask(scale_gizmo_instance[i], layer); - - scale_plane_gizmo_instance[i] = VS::get_singleton()->instance_create(); - VS::get_singleton()->instance_set_base(scale_plane_gizmo_instance[i], spatial_editor->get_scale_plane_gizmo(i)->get_rid()); - VS::get_singleton()->instance_set_scenario(scale_plane_gizmo_instance[i], get_tree()->get_root()->get_world()->get_scenario()); - VS::get_singleton()->instance_set_visible(scale_plane_gizmo_instance[i], false); - VS::get_singleton()->instance_geometry_set_cast_shadows_setting(scale_plane_gizmo_instance[i], VS::SHADOW_CASTING_SETTING_OFF); - VS::get_singleton()->instance_set_layer_mask(scale_plane_gizmo_instance[i], layer); - } -} - -void Node3DEditorViewport::_finish_gizmo_instances() { - - for (int i = 0; i < 3; i++) { - VS::get_singleton()->free(move_gizmo_instance[i]); - VS::get_singleton()->free(move_plane_gizmo_instance[i]); - VS::get_singleton()->free(rotate_gizmo_instance[i]); - VS::get_singleton()->free(scale_gizmo_instance[i]); - VS::get_singleton()->free(scale_plane_gizmo_instance[i]); - } -} -void Node3DEditorViewport::_toggle_camera_preview(bool p_activate) { - - ERR_FAIL_COND(p_activate && !preview); - ERR_FAIL_COND(!p_activate && !previewing); - - if (!p_activate) { - - previewing->disconnect("tree_exiting", callable_mp(this, &Node3DEditorViewport::_preview_exited_scene)); - previewing = NULL; - VS::get_singleton()->viewport_attach_camera(viewport->get_viewport_rid(), camera->get_camera()); //restore - if (!preview) - preview_camera->hide(); - view_menu->set_disabled(false); - surface->update(); - - } else { - - previewing = preview; - previewing->connect("tree_exiting", callable_mp(this, &Node3DEditorViewport::_preview_exited_scene)); - VS::get_singleton()->viewport_attach_camera(viewport->get_viewport_rid(), preview->get_camera()); //replace - view_menu->set_disabled(true); - surface->update(); - } -} - -void Node3DEditorViewport::_toggle_cinema_preview(bool p_activate) { - previewing_cinema = p_activate; - if (!previewing_cinema) { - if (previewing != NULL) - previewing->disconnect("tree_exited", callable_mp(this, &Node3DEditorViewport::_preview_exited_scene)); - - previewing = NULL; - VS::get_singleton()->viewport_attach_camera(viewport->get_viewport_rid(), camera->get_camera()); //restore - preview_camera->set_pressed(false); - if (!preview) { - preview_camera->hide(); - } else { - preview_camera->show(); - } - view_menu->show(); - surface->update(); - } -} - -void Node3DEditorViewport::_selection_result_pressed(int p_result) { - - if (selection_results.size() <= p_result) - return; - - clicked = selection_results[p_result].item->get_instance_id(); - - if (clicked.is_valid()) { - _select_clicked(clicked_wants_append, true, spatial_editor->get_tool_mode() != Node3DEditor::TOOL_MODE_LIST_SELECT); - clicked = ObjectID(); - } -} - -void Node3DEditorViewport::_selection_menu_hide() { - - selection_results.clear(); - selection_menu->clear(); - selection_menu->set_size(Vector2(0, 0)); -} - -void Node3DEditorViewport::set_can_preview(Camera3D *p_preview) { - - preview = p_preview; - - if (!preview_camera->is_pressed() && !previewing_cinema) - preview_camera->set_visible(p_preview); -} - -void Node3DEditorViewport::update_transform_gizmo_view() { - - if (!is_visible_in_tree()) - return; - - Transform xform = spatial_editor->get_gizmo_transform(); - - Transform camera_xform = camera->get_transform(); - - if (xform.origin.distance_squared_to(camera_xform.origin) < 0.01) { - for (int i = 0; i < 3; i++) { - VisualServer::get_singleton()->instance_set_visible(move_gizmo_instance[i], false); - VisualServer::get_singleton()->instance_set_visible(move_plane_gizmo_instance[i], false); - VisualServer::get_singleton()->instance_set_visible(rotate_gizmo_instance[i], false); - VisualServer::get_singleton()->instance_set_visible(scale_gizmo_instance[i], false); - VisualServer::get_singleton()->instance_set_visible(scale_plane_gizmo_instance[i], false); - } - return; - } - - Vector3 camz = -camera_xform.get_basis().get_axis(2).normalized(); - Vector3 camy = -camera_xform.get_basis().get_axis(1).normalized(); - Plane p(camera_xform.origin, camz); - float gizmo_d = Math::abs(p.distance_to(xform.origin)); - float d0 = camera->unproject_position(camera_xform.origin + camz * gizmo_d).y; - float d1 = camera->unproject_position(camera_xform.origin + camz * gizmo_d + camy).y; - float dd = Math::abs(d0 - d1); - if (dd == 0) - dd = 0.0001; - - float gizmo_size = EditorSettings::get_singleton()->get("editors/3d/manipulator_gizmo_size"); - // At low viewport heights, multiply the gizmo scale based on the viewport height. - // This prevents the gizmo from growing very large and going outside the viewport. - const int viewport_base_height = 400 * MAX(1, EDSCALE); - gizmo_scale = - (gizmo_size / Math::abs(dd)) * MAX(1, EDSCALE) * - MIN(viewport_base_height, viewport_container->get_size().height) / viewport_base_height / - viewport_container->get_stretch_shrink(); - Vector3 scale = Vector3(1, 1, 1) * gizmo_scale; - - xform.basis.scale(scale); - - for (int i = 0; i < 3; i++) { - VisualServer::get_singleton()->instance_set_transform(move_gizmo_instance[i], xform); - VisualServer::get_singleton()->instance_set_visible(move_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_MOVE)); - VisualServer::get_singleton()->instance_set_transform(move_plane_gizmo_instance[i], xform); - VisualServer::get_singleton()->instance_set_visible(move_plane_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_MOVE)); - VisualServer::get_singleton()->instance_set_transform(rotate_gizmo_instance[i], xform); - VisualServer::get_singleton()->instance_set_visible(rotate_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_ROTATE)); - VisualServer::get_singleton()->instance_set_transform(scale_gizmo_instance[i], xform); - VisualServer::get_singleton()->instance_set_visible(scale_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SCALE)); - VisualServer::get_singleton()->instance_set_transform(scale_plane_gizmo_instance[i], xform); - VisualServer::get_singleton()->instance_set_visible(scale_plane_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SCALE)); - } -} - -void Node3DEditorViewport::set_state(const Dictionary &p_state) { - - if (p_state.has("position")) - cursor.pos = p_state["position"]; - if (p_state.has("x_rotation")) - cursor.x_rot = p_state["x_rotation"]; - if (p_state.has("y_rotation")) - cursor.y_rot = p_state["y_rotation"]; - if (p_state.has("distance")) - cursor.distance = p_state["distance"]; - - if (p_state.has("use_orthogonal")) { - bool orth = p_state["use_orthogonal"]; - - if (orth) - _menu_option(VIEW_ORTHOGONAL); - else - _menu_option(VIEW_PERSPECTIVE); - } - if (p_state.has("view_name")) { - name = p_state["view_name"]; - _update_name(); - } - if (p_state.has("auto_orthogonal")) { - auto_orthogonal = p_state["auto_orthogonal"]; - _update_name(); - } - if (p_state.has("auto_orthogonal_enabled")) { - bool enabled = p_state["auto_orthogonal_enabled"]; - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_AUTO_ORTHOGONAL), enabled); - } - if (p_state.has("display_mode")) { - int display = p_state["display_mode"]; - - int idx = view_menu->get_popup()->get_item_index(display); - if (!view_menu->get_popup()->is_item_checked(idx)) - _menu_option(display); - } - if (p_state.has("lock_rotation")) { - lock_rotation = p_state["lock_rotation"]; - - int idx = view_menu->get_popup()->get_item_index(VIEW_LOCK_ROTATION); - view_menu->get_popup()->set_item_checked(idx, lock_rotation); - } - if (p_state.has("use_environment")) { - bool env = p_state["use_environment"]; - - if (env != camera->get_environment().is_valid()) - _menu_option(VIEW_ENVIRONMENT); - } - if (p_state.has("listener")) { - bool listener = p_state["listener"]; - - int idx = view_menu->get_popup()->get_item_index(VIEW_AUDIO_LISTENER); - viewport->set_as_audio_listener(listener); - view_menu->get_popup()->set_item_checked(idx, listener); - } - if (p_state.has("doppler")) { - bool doppler = p_state["doppler"]; - - int idx = view_menu->get_popup()->get_item_index(VIEW_AUDIO_DOPPLER); - camera->set_doppler_tracking(doppler ? Camera3D::DOPPLER_TRACKING_IDLE_STEP : Camera3D::DOPPLER_TRACKING_DISABLED); - view_menu->get_popup()->set_item_checked(idx, doppler); - } - if (p_state.has("gizmos")) { - bool gizmos = p_state["gizmos"]; - - int idx = view_menu->get_popup()->get_item_index(VIEW_GIZMOS); - if (view_menu->get_popup()->is_item_checked(idx) != gizmos) - _menu_option(VIEW_GIZMOS); - } - if (p_state.has("information")) { - bool information = p_state["information"]; - - int idx = view_menu->get_popup()->get_item_index(VIEW_INFORMATION); - if (view_menu->get_popup()->is_item_checked(idx) != information) - _menu_option(VIEW_INFORMATION); - } - if (p_state.has("fps")) { - bool fps = p_state["fps"]; - - int idx = view_menu->get_popup()->get_item_index(VIEW_FPS); - if (view_menu->get_popup()->is_item_checked(idx) != fps) - _menu_option(VIEW_FPS); - } - if (p_state.has("half_res")) { - bool half_res = p_state["half_res"]; - - int idx = view_menu->get_popup()->get_item_index(VIEW_HALF_RESOLUTION); - view_menu->get_popup()->set_item_checked(idx, half_res); - } - if (p_state.has("cinematic_preview")) { - previewing_cinema = p_state["cinematic_preview"]; - - int idx = view_menu->get_popup()->get_item_index(VIEW_CINEMATIC_PREVIEW); - view_menu->get_popup()->set_item_checked(idx, previewing_cinema); - } - - if (preview_camera->is_connected("toggled", callable_mp(this, &Node3DEditorViewport::_toggle_camera_preview))) { - preview_camera->disconnect("toggled", callable_mp(this, &Node3DEditorViewport::_toggle_camera_preview)); - } - if (p_state.has("previewing")) { - Node *pv = EditorNode::get_singleton()->get_edited_scene()->get_node(p_state["previewing"]); - if (Object::cast_to(pv)) { - previewing = Object::cast_to(pv); - previewing->connect("tree_exiting", callable_mp(this, &Node3DEditorViewport::_preview_exited_scene)); - VS::get_singleton()->viewport_attach_camera(viewport->get_viewport_rid(), previewing->get_camera()); //replace - view_menu->set_disabled(true); - surface->update(); - preview_camera->set_pressed(true); - preview_camera->show(); - } - } - preview_camera->connect("toggled", callable_mp(this, &Node3DEditorViewport::_toggle_camera_preview)); -} - -Dictionary Node3DEditorViewport::get_state() const { - - Dictionary d; - d["position"] = cursor.pos; - d["x_rotation"] = cursor.x_rot; - d["y_rotation"] = cursor.y_rot; - d["distance"] = cursor.distance; - d["use_environment"] = camera->get_environment().is_valid(); - d["use_orthogonal"] = camera->get_projection() == Camera3D::PROJECTION_ORTHOGONAL; - d["view_name"] = name; - d["auto_orthogonal"] = auto_orthogonal; - d["auto_orthogonal_enabled"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_AUTO_ORTHOGONAL)); - if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_NORMAL))) - d["display_mode"] = VIEW_DISPLAY_NORMAL; - else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_WIREFRAME))) - d["display_mode"] = VIEW_DISPLAY_WIREFRAME; - else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_OVERDRAW))) - d["display_mode"] = VIEW_DISPLAY_OVERDRAW; - else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_SHADELESS))) - d["display_mode"] = VIEW_DISPLAY_SHADELESS; - d["listener"] = viewport->is_audio_listener(); - d["doppler"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_AUDIO_DOPPLER)); - d["gizmos"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_GIZMOS)); - d["information"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_INFORMATION)); - d["fps"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_FPS)); - d["half_res"] = viewport_container->get_stretch_shrink() > 1; - d["cinematic_preview"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_CINEMATIC_PREVIEW)); - if (previewing) - d["previewing"] = EditorNode::get_singleton()->get_edited_scene()->get_path_to(previewing); - if (lock_rotation) - d["lock_rotation"] = lock_rotation; - - return d; -} - -void Node3DEditorViewport::_bind_methods() { - - ClassDB::bind_method(D_METHOD("update_transform_gizmo_view"), &Node3DEditorViewport::update_transform_gizmo_view); // Used by call_deferred. - ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &Node3DEditorViewport::can_drop_data_fw); - ClassDB::bind_method(D_METHOD("drop_data_fw"), &Node3DEditorViewport::drop_data_fw); - - ADD_SIGNAL(MethodInfo("toggle_maximize_view", PropertyInfo(Variant::OBJECT, "viewport"))); - ADD_SIGNAL(MethodInfo("clicked", PropertyInfo(Variant::OBJECT, "viewport"))); -} - -void Node3DEditorViewport::reset() { - - orthogonal = false; - auto_orthogonal = false; - lock_rotation = false; - message_time = 0; - message = ""; - last_message = ""; - name = ""; - - cursor.x_rot = 0.5; - cursor.y_rot = 0.5; - cursor.distance = 4; - cursor.region_select = false; - cursor.pos = Vector3(); - _update_name(); -} - -void Node3DEditorViewport::focus_selection() { - if (!get_selected_count()) - return; - - Vector3 center; - int count = 0; - - List &selection = editor_selection->get_selected_node_list(); - - for (List::Element *E = selection.front(); E; E = E->next()) { - - Node3D *sp = Object::cast_to(E->get()); - if (!sp) - continue; - - Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data(sp); - if (!se) - continue; - - center += sp->get_global_gizmo_transform().origin; - count++; - } - - if (count != 0) { - center /= float(count); - } - - cursor.pos = center; -} - -void Node3DEditorViewport::assign_pending_data_pointers(Node3D *p_preview_node, AABB *p_preview_bounds, AcceptDialog *p_accept) { - preview_node = p_preview_node; - preview_bounds = p_preview_bounds; - accept = p_accept; -} - -Vector3 Node3DEditorViewport::_get_instance_position(const Point2 &p_pos) const { - const float MAX_DISTANCE = 10; - - Vector3 world_ray = _get_ray(p_pos); - Vector3 world_pos = _get_ray_pos(p_pos); - - Vector instances = VisualServer::get_singleton()->instances_cull_ray(world_pos, world_ray, get_tree()->get_root()->get_world()->get_scenario()); - Set> found_gizmos; - - float closest_dist = MAX_DISTANCE; - - Vector3 point = world_pos + world_ray * MAX_DISTANCE; - Vector3 normal = Vector3(0.0, 0.0, 0.0); - - for (int i = 0; i < instances.size(); i++) { - - MeshInstance3D *mesh_instance = Object::cast_to(ObjectDB::get_instance(instances[i])); - - if (!mesh_instance) - continue; - - Ref seg = mesh_instance->get_gizmo(); - - if ((!seg.is_valid()) || found_gizmos.has(seg)) { - continue; - } - - found_gizmos.insert(seg); - - Vector3 hit_point; - Vector3 hit_normal; - bool inters = seg->intersect_ray(camera, p_pos, hit_point, hit_normal, NULL, false); - - if (!inters) - continue; - - float dist = world_pos.distance_to(hit_point); - - if (dist < 0) - continue; - - if (dist < closest_dist) { - closest_dist = dist; - point = hit_point; - normal = hit_normal; - } - } - Vector3 offset = Vector3(); - for (int i = 0; i < 3; i++) { - if (normal[i] > 0.0) - offset[i] = (preview_bounds->get_size()[i] - (preview_bounds->get_size()[i] + preview_bounds->get_position()[i])); - else if (normal[i] < 0.0) - offset[i] = -(preview_bounds->get_size()[i] + preview_bounds->get_position()[i]); - } - return point + offset; -} - -AABB Node3DEditorViewport::_calculate_spatial_bounds(const Node3D *p_parent, bool p_exclude_toplevel_transform) { - AABB bounds; - - const MeshInstance3D *mesh_instance = Object::cast_to(p_parent); - if (mesh_instance) { - bounds = mesh_instance->get_aabb(); - } - - for (int i = 0; i < p_parent->get_child_count(); i++) { - Node3D *child = Object::cast_to(p_parent->get_child(i)); - if (child) { - AABB child_bounds = _calculate_spatial_bounds(child, false); - - if (bounds.size == Vector3() && p_parent->get_class_name() == StringName("Node3D")) { - bounds = child_bounds; - } else { - bounds.merge_with(child_bounds); - } - } - } - - if (bounds.size == Vector3() && p_parent->get_class_name() != StringName("Node3D")) { - bounds = AABB(Vector3(-0.2, -0.2, -0.2), Vector3(0.4, 0.4, 0.4)); - } - - if (!p_exclude_toplevel_transform) { - bounds = p_parent->get_transform().xform(bounds); - } - - return bounds; -} - -void Node3DEditorViewport::_create_preview(const Vector &files) const { - for (int i = 0; i < files.size(); i++) { - String path = files[i]; - RES res = ResourceLoader::load(path); - ERR_CONTINUE(res.is_null()); - Ref scene = Ref(Object::cast_to(*res)); - Ref mesh = Ref(Object::cast_to(*res)); - if (mesh != NULL || scene != NULL) { - if (mesh != NULL) { - MeshInstance3D *mesh_instance = memnew(MeshInstance3D); - mesh_instance->set_mesh(mesh); - preview_node->add_child(mesh_instance); - } else { - if (scene.is_valid()) { - Node *instance = scene->instance(); - if (instance) { - preview_node->add_child(instance); - } - } - } - editor->get_scene_root()->add_child(preview_node); - } - } - *preview_bounds = _calculate_spatial_bounds(preview_node); -} - -void Node3DEditorViewport::_remove_preview() { - if (preview_node->get_parent()) { - for (int i = preview_node->get_child_count() - 1; i >= 0; i--) { - Node *node = preview_node->get_child(i); - node->queue_delete(); - preview_node->remove_child(node); - } - editor->get_scene_root()->remove_child(preview_node); - } -} - -bool Node3DEditorViewport::_cyclical_dependency_exists(const String &p_target_scene_path, Node *p_desired_node) { - if (p_desired_node->get_filename() == p_target_scene_path) { - return true; - } - - int childCount = p_desired_node->get_child_count(); - for (int i = 0; i < childCount; i++) { - Node *child = p_desired_node->get_child(i); - if (_cyclical_dependency_exists(p_target_scene_path, child)) { - return true; - } - } - return false; -} - -bool Node3DEditorViewport::_create_instance(Node *parent, String &path, const Point2 &p_point) { - RES res = ResourceLoader::load(path); - ERR_FAIL_COND_V(res.is_null(), false); - - Ref scene = Ref(Object::cast_to(*res)); - Ref mesh = Ref(Object::cast_to(*res)); - - Node *instanced_scene = NULL; - - if (mesh != NULL || scene != NULL) { - if (mesh != NULL) { - MeshInstance3D *mesh_instance = memnew(MeshInstance3D); - mesh_instance->set_mesh(mesh); - mesh_instance->set_name(path.get_file().get_basename()); - instanced_scene = mesh_instance; - } else { - if (!scene.is_valid()) { // invalid scene - return false; - } else { - instanced_scene = scene->instance(PackedScene::GEN_EDIT_STATE_INSTANCE); - } - } - } - - if (instanced_scene == NULL) { - return false; - } - - if (editor->get_edited_scene()->get_filename() != "") { // cyclical instancing - if (_cyclical_dependency_exists(editor->get_edited_scene()->get_filename(), instanced_scene)) { - memdelete(instanced_scene); - return false; - } - } - - if (scene != NULL) { - instanced_scene->set_filename(ProjectSettings::get_singleton()->localize_path(path)); - } - - editor_data->get_undo_redo().add_do_method(parent, "add_child", instanced_scene); - editor_data->get_undo_redo().add_do_method(instanced_scene, "set_owner", editor->get_edited_scene()); - editor_data->get_undo_redo().add_do_reference(instanced_scene); - editor_data->get_undo_redo().add_undo_method(parent, "remove_child", instanced_scene); - - String new_name = parent->validate_child_name(instanced_scene); - EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton(); - editor_data->get_undo_redo().add_do_method(ed, "live_debug_instance_node", editor->get_edited_scene()->get_path_to(parent), path, new_name); - editor_data->get_undo_redo().add_undo_method(ed, "live_debug_remove_node", NodePath(String(editor->get_edited_scene()->get_path_to(parent)) + "/" + new_name)); - - Transform global_transform; - Node3D *parent_spatial = Object::cast_to(parent); - if (parent_spatial) - global_transform = parent_spatial->get_global_gizmo_transform(); - - global_transform.origin = spatial_editor->snap_point(_get_instance_position(p_point)); - - editor_data->get_undo_redo().add_do_method(instanced_scene, "set_global_transform", global_transform); - - return true; -} - -void Node3DEditorViewport::_perform_drop_data() { - _remove_preview(); - - Vector error_files; - - editor_data->get_undo_redo().create_action(TTR("Create Node")); - - for (int i = 0; i < selected_files.size(); i++) { - String path = selected_files[i]; - RES res = ResourceLoader::load(path); - if (res.is_null()) { - continue; - } - Ref scene = Ref(Object::cast_to(*res)); - Ref mesh = Ref(Object::cast_to(*res)); - if (mesh != NULL || scene != NULL) { - bool success = _create_instance(target_node, path, drop_pos); - if (!success) { - error_files.push_back(path); - } - } - } - - editor_data->get_undo_redo().commit_action(); - - if (error_files.size() > 0) { - String files_str; - for (int i = 0; i < error_files.size(); i++) { - files_str += error_files[i].get_file().get_basename() + ","; - } - files_str = files_str.substr(0, files_str.length() - 1); - accept->set_text(vformat(TTR("Error instancing scene from %s"), files_str.c_str())); - accept->popup_centered(); - } -} - -bool Node3DEditorViewport::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const { - - bool can_instance = false; - - if (!preview_node->is_inside_tree()) { - Dictionary d = p_data; - if (d.has("type") && (String(d["type"]) == "files")) { - Vector files = d["files"]; - - List scene_extensions; - ResourceLoader::get_recognized_extensions_for_type("PackedScene", &scene_extensions); - List mesh_extensions; - ResourceLoader::get_recognized_extensions_for_type("Mesh", &mesh_extensions); - - for (int i = 0; i < files.size(); i++) { - if (mesh_extensions.find(files[i].get_extension()) || scene_extensions.find(files[i].get_extension())) { - RES res = ResourceLoader::load(files[i]); - if (res.is_null()) { - continue; - } - - String type = res->get_class(); - if (type == "PackedScene") { - Ref sdata = ResourceLoader::load(files[i]); - Node *instanced_scene = sdata->instance(PackedScene::GEN_EDIT_STATE_INSTANCE); - if (!instanced_scene) { - continue; - } - memdelete(instanced_scene); - } else if (type == "Mesh" || type == "ArrayMesh" || type == "PrimitiveMesh") { - Ref mesh = ResourceLoader::load(files[i]); - if (!mesh.is_valid()) { - continue; - } - } else { - continue; - } - can_instance = true; - break; - } - } - if (can_instance) { - _create_preview(files); - } - } - } else { - can_instance = true; - } - - if (can_instance) { - Transform global_transform = Transform(Basis(), _get_instance_position(p_point)); - preview_node->set_global_transform(global_transform); - } - - return can_instance; -} - -void Node3DEditorViewport::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) { - if (!can_drop_data_fw(p_point, p_data, p_from)) - return; - - bool is_shift = InputFilter::get_singleton()->is_key_pressed(KEY_SHIFT); - - selected_files.clear(); - Dictionary d = p_data; - if (d.has("type") && String(d["type"]) == "files") { - selected_files = d["files"]; - } - - List list = editor->get_editor_selection()->get_selected_node_list(); - if (list.size() == 0) { - Node *root_node = editor->get_edited_scene(); - if (root_node) { - list.push_back(root_node); - } else { - accept->set_text(TTR("No parent to instance a child at.")); - accept->popup_centered(); - _remove_preview(); - return; - } - } - if (list.size() != 1) { - accept->set_text(TTR("This operation requires a single selected node.")); - accept->popup_centered(); - _remove_preview(); - return; - } - - target_node = list[0]; - if (is_shift && target_node != editor->get_edited_scene()) { - target_node = target_node->get_parent(); - } - drop_pos = p_point; - - _perform_drop_data(); -} - -Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, EditorNode *p_editor, int p_index) { - - _edit.mode = TRANSFORM_NONE; - _edit.plane = TRANSFORM_VIEW; - _edit.edited_gizmo = 0; - _edit.snap = 1; - _edit.gizmo_handle = 0; - - index = p_index; - editor = p_editor; - editor_data = editor->get_scene_tree_dock()->get_editor_data(); - editor_selection = editor->get_editor_selection(); - undo_redo = editor->get_undo_redo(); - - clicked_includes_current = false; - orthogonal = false; - auto_orthogonal = false; - lock_rotation = false; - message_time = 0; - zoom_indicator_delay = 0.0; - - spatial_editor = p_spatial_editor; - ViewportContainer *c = memnew(ViewportContainer); - viewport_container = c; - c->set_stretch(true); - add_child(c); - c->set_anchors_and_margins_preset(Control::PRESET_WIDE); - viewport = memnew(SubViewport); - viewport->set_disable_input(true); - - c->add_child(viewport); - surface = memnew(Control); - surface->set_drag_forwarding(this); - add_child(surface); - surface->set_anchors_and_margins_preset(Control::PRESET_WIDE); - surface->set_clip_contents(true); - camera = memnew(Camera3D); - camera->set_disable_gizmo(true); - camera->set_cull_mask(((1 << 20) - 1) | (1 << (GIZMO_BASE_LAYER + p_index)) | (1 << GIZMO_EDIT_LAYER) | (1 << GIZMO_GRID_LAYER)); - viewport->add_child(camera); - camera->make_current(); - surface->set_focus_mode(FOCUS_ALL); - - crosshair = memnew(TextureRect); - crosshair->set_mouse_filter(MOUSE_FILTER_IGNORE); - surface->add_child(crosshair); - - VBoxContainer *vbox = memnew(VBoxContainer); - surface->add_child(vbox); - vbox->set_position(Point2(10, 10) * EDSCALE); - - view_menu = memnew(MenuButton); - view_menu->set_flat(false); - vbox->add_child(view_menu); - view_menu->set_h_size_flags(0); - - display_submenu = memnew(PopupMenu); - view_menu->get_popup()->add_child(display_submenu); - - view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/top_view"), VIEW_TOP); - view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/bottom_view"), VIEW_BOTTOM); - view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/left_view"), VIEW_LEFT); - view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/right_view"), VIEW_RIGHT); - view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/front_view"), VIEW_FRONT); - view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/rear_view"), VIEW_REAR); - view_menu->get_popup()->add_separator(); - view_menu->get_popup()->add_radio_check_item(TTR("Perspective") + " (" + ED_GET_SHORTCUT("spatial_editor/switch_perspective_orthogonal")->get_as_text() + ")", VIEW_PERSPECTIVE); - view_menu->get_popup()->add_radio_check_item(TTR("Orthogonal") + " (" + ED_GET_SHORTCUT("spatial_editor/switch_perspective_orthogonal")->get_as_text() + ")", VIEW_ORTHOGONAL); - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_PERSPECTIVE), true); - view_menu->get_popup()->add_check_item(TTR("Auto Orthogonal Enabled"), VIEW_AUTO_ORTHOGONAL); - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_AUTO_ORTHOGONAL), true); - view_menu->get_popup()->add_separator(); - view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_lock_rotation", TTR("Lock View Rotation")), VIEW_LOCK_ROTATION); - view_menu->get_popup()->add_separator(); - view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_normal", TTR("Display Normal")), VIEW_DISPLAY_NORMAL); - view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_wireframe", TTR("Display Wireframe")), VIEW_DISPLAY_WIREFRAME); - view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_overdraw", TTR("Display Overdraw")), VIEW_DISPLAY_OVERDRAW); - view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_lighting", TTR("Display Lighting")), VIEW_DISPLAY_LIGHTING); - view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_unshaded", TTR("Display Unshaded")), VIEW_DISPLAY_SHADELESS); - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_NORMAL), true); - display_submenu->add_radio_check_item(TTR("Normal Buffer"), VIEW_DISPLAY_NORMAL_BUFFER); - display_submenu->add_separator(); - display_submenu->add_radio_check_item(TTR("Shadow Atlas"), VIEW_DISPLAY_DEBUG_SHADOW_ATLAS); - display_submenu->add_radio_check_item(TTR("Directional Shadow"), VIEW_DISPLAY_DEBUG_DIRECTIONAL_SHADOW_ATLAS); - display_submenu->add_separator(); - display_submenu->add_radio_check_item(TTR("GIProbe Lighting"), VIEW_DISPLAY_DEBUG_GIPROBE_LIGHTING); - display_submenu->add_radio_check_item(TTR("GIProbe Albedo"), VIEW_DISPLAY_DEBUG_GIPROBE_ALBEDO); - display_submenu->add_radio_check_item(TTR("GIProbe Emission"), VIEW_DISPLAY_DEBUG_GIPROBE_EMISSION); - display_submenu->add_separator(); - display_submenu->add_radio_check_item(TTR("Scene Luminance"), VIEW_DISPLAY_DEBUG_SCENE_LUMINANCE); - display_submenu->add_separator(); - display_submenu->add_radio_check_item(TTR("SSAO"), VIEW_DISPLAY_DEBUG_SSAO); - display_submenu->add_separator(); - display_submenu->add_radio_check_item(TTR("Roughness Limiter"), VIEW_DISPLAY_DEBUG_ROUGHNESS_LIMITER); - display_submenu->set_name("display_advanced"); - view_menu->get_popup()->add_submenu_item(TTR("Display Advanced..."), "display_advanced", VIEW_DISPLAY_ADVANCED); - view_menu->get_popup()->add_separator(); - view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_environment", TTR("View Environment")), VIEW_ENVIRONMENT); - view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_gizmos", TTR("View Gizmos")), VIEW_GIZMOS); - view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_information", TTR("View Information")), VIEW_INFORMATION); - view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_fps", TTR("View FPS")), VIEW_FPS); - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_ENVIRONMENT), true); - view_menu->get_popup()->add_separator(); - view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_half_resolution", TTR("Half Resolution")), VIEW_HALF_RESOLUTION); - view_menu->get_popup()->add_separator(); - view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_audio_listener", TTR("Audio Listener")), VIEW_AUDIO_LISTENER); - view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_audio_doppler", TTR("Enable Doppler")), VIEW_AUDIO_DOPPLER); - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_GIZMOS), true); - - view_menu->get_popup()->add_separator(); - view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_cinematic_preview", TTR("Cinematic Preview")), VIEW_CINEMATIC_PREVIEW); - - view_menu->get_popup()->add_separator(); - view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/focus_origin"), VIEW_CENTER_TO_ORIGIN); - view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/focus_selection"), VIEW_CENTER_TO_SELECTION); - view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/align_transform_with_view"), VIEW_ALIGN_TRANSFORM_WITH_VIEW); - view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/align_rotation_with_view"), VIEW_ALIGN_ROTATION_WITH_VIEW); - view_menu->get_popup()->connect("id_pressed", callable_mp(this, &Node3DEditorViewport::_menu_option)); - display_submenu->connect("id_pressed", callable_mp(this, &Node3DEditorViewport::_menu_option)); - view_menu->set_disable_shortcuts(true); -#ifndef _MSC_VER -#warning this needs to be fixed -#endif - //if (OS::get_singleton()->get_current_video_driver() == OS::VIDEO_DRIVER_GLES2) { - if (false) { - // Alternate display modes only work when using the Vulkan renderer; make this explicit. - const int normal_idx = view_menu->get_popup()->get_item_index(VIEW_DISPLAY_NORMAL); - const int wireframe_idx = view_menu->get_popup()->get_item_index(VIEW_DISPLAY_WIREFRAME); - const int overdraw_idx = view_menu->get_popup()->get_item_index(VIEW_DISPLAY_OVERDRAW); - const int shadeless_idx = view_menu->get_popup()->get_item_index(VIEW_DISPLAY_SHADELESS); - const String unsupported_tooltip = TTR("Not available when using the GLES2 renderer."); - - view_menu->get_popup()->set_item_disabled(normal_idx, true); - view_menu->get_popup()->set_item_tooltip(normal_idx, unsupported_tooltip); - view_menu->get_popup()->set_item_disabled(wireframe_idx, true); - view_menu->get_popup()->set_item_tooltip(wireframe_idx, unsupported_tooltip); - view_menu->get_popup()->set_item_disabled(overdraw_idx, true); - view_menu->get_popup()->set_item_tooltip(overdraw_idx, unsupported_tooltip); - view_menu->get_popup()->set_item_disabled(shadeless_idx, true); - view_menu->get_popup()->set_item_tooltip(shadeless_idx, unsupported_tooltip); - } - - ED_SHORTCUT("spatial_editor/freelook_left", TTR("Freelook Left"), KEY_A); - ED_SHORTCUT("spatial_editor/freelook_right", TTR("Freelook Right"), KEY_D); - ED_SHORTCUT("spatial_editor/freelook_forward", TTR("Freelook Forward"), KEY_W); - ED_SHORTCUT("spatial_editor/freelook_backwards", TTR("Freelook Backwards"), KEY_S); - ED_SHORTCUT("spatial_editor/freelook_up", TTR("Freelook Up"), KEY_E); - ED_SHORTCUT("spatial_editor/freelook_down", TTR("Freelook Down"), KEY_Q); - ED_SHORTCUT("spatial_editor/freelook_speed_modifier", TTR("Freelook Speed Modifier"), KEY_SHIFT); - ED_SHORTCUT("spatial_editor/freelook_slow_modifier", TTR("Freelook Slow Modifier"), KEY_ALT); - - preview_camera = memnew(CheckBox); - preview_camera->set_text(TTR("Preview")); - vbox->add_child(preview_camera); - preview_camera->set_h_size_flags(0); - preview_camera->hide(); - preview_camera->connect("toggled", callable_mp(this, &Node3DEditorViewport::_toggle_camera_preview)); - previewing = NULL; - gizmo_scale = 1.0; - - preview_node = NULL; - - info_label = memnew(Label); - info_label->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_END, -90 * EDSCALE); - info_label->set_anchor_and_margin(MARGIN_TOP, ANCHOR_END, -90 * EDSCALE); - info_label->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, -10 * EDSCALE); - info_label->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, -10 * EDSCALE); - info_label->set_h_grow_direction(GROW_DIRECTION_BEGIN); - info_label->set_v_grow_direction(GROW_DIRECTION_BEGIN); - surface->add_child(info_label); - info_label->hide(); - - fps_label = memnew(Label); - fps_label->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_END, -90 * EDSCALE); - fps_label->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 10 * EDSCALE); - fps_label->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, -10 * EDSCALE); - fps_label->set_h_grow_direction(GROW_DIRECTION_BEGIN); - fps_label->set_tooltip(TTR("Note: The FPS value displayed is the editor's framerate.\nIt cannot be used as a reliable indication of in-game performance.")); - fps_label->set_mouse_filter(MOUSE_FILTER_PASS); // Otherwise tooltip doesn't show. - surface->add_child(fps_label); - fps_label->hide(); - - cinema_label = memnew(Label); - cinema_label->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 10 * EDSCALE); - cinema_label->set_h_grow_direction(GROW_DIRECTION_END); - cinema_label->set_align(Label::ALIGN_CENTER); - surface->add_child(cinema_label); - cinema_label->set_text(TTR("Cinematic Preview")); - cinema_label->hide(); - previewing_cinema = false; - - locked_label = memnew(Label); - locked_label->set_anchor_and_margin(MARGIN_TOP, ANCHOR_END, -20 * EDSCALE); - locked_label->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, -10 * EDSCALE); - locked_label->set_h_grow_direction(GROW_DIRECTION_END); - locked_label->set_v_grow_direction(GROW_DIRECTION_BEGIN); - locked_label->set_align(Label::ALIGN_CENTER); - surface->add_child(locked_label); - locked_label->set_text(TTR("View Rotation Locked")); - locked_label->hide(); - - top_right_vbox = memnew(VBoxContainer); - top_right_vbox->set_anchors_and_margins_preset(PRESET_TOP_RIGHT, PRESET_MODE_MINSIZE, 2.0 * EDSCALE); - top_right_vbox->set_h_grow_direction(GROW_DIRECTION_BEGIN); - - rotation_control = memnew(ViewportRotationControl); - rotation_control->set_custom_minimum_size(Size2(80, 80) * EDSCALE); - rotation_control->set_h_size_flags(SIZE_SHRINK_END); - rotation_control->set_viewport(this); - top_right_vbox->add_child(rotation_control); - - fps_label = memnew(Label); - fps_label->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_END, -90 * EDSCALE); - fps_label->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 10 * EDSCALE); - fps_label->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, -10 * EDSCALE); - fps_label->set_h_grow_direction(GROW_DIRECTION_BEGIN); - fps_label->set_tooltip(TTR("Note: The FPS value displayed is the editor's framerate.\nIt cannot be used as a reliable indication of in-game performance.")); - fps_label->set_mouse_filter(MOUSE_FILTER_PASS); // Otherwise tooltip doesn't show. - top_right_vbox->add_child(fps_label); - fps_label->hide(); - - surface->add_child(top_right_vbox); - - accept = NULL; - - freelook_active = false; - freelook_speed = EditorSettings::get_singleton()->get("editors/3d/freelook/freelook_base_speed"); - - selection_menu = memnew(PopupMenu); - add_child(selection_menu); - selection_menu->set_min_size(Size2(100, 0) * EDSCALE); - selection_menu->connect("id_pressed", callable_mp(this, &Node3DEditorViewport::_selection_result_pressed)); - selection_menu->connect("popup_hide", callable_mp(this, &Node3DEditorViewport::_selection_menu_hide)); - - if (p_index == 0) { - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_AUDIO_LISTENER), true); - viewport->set_as_audio_listener(true); - } - - name = ""; - _update_name(); - - EditorSettings::get_singleton()->connect("settings_changed", callable_mp(this, &Node3DEditorViewport::update_transform_gizmo_view)); -} - -////////////////////////////////////////////////////////////// - -void Node3DEditorViewportContainer::_gui_input(const Ref &p_event) { - - Ref mb = p_event; - - if (mb.is_valid() && mb->get_button_index() == BUTTON_LEFT) { - - if (mb->is_pressed()) { - Vector2 size = get_size(); - - int h_sep = get_theme_constant("separation", "HSplitContainer"); - int v_sep = get_theme_constant("separation", "VSplitContainer"); - - int mid_w = size.width * ratio_h; - int mid_h = size.height * ratio_v; - - dragging_h = mb->get_position().x > (mid_w - h_sep / 2) && mb->get_position().x < (mid_w + h_sep / 2); - dragging_v = mb->get_position().y > (mid_h - v_sep / 2) && mb->get_position().y < (mid_h + v_sep / 2); - - drag_begin_pos = mb->get_position(); - drag_begin_ratio.x = ratio_h; - drag_begin_ratio.y = ratio_v; - - switch (view) { - case VIEW_USE_1_VIEWPORT: { - - dragging_h = false; - dragging_v = false; - - } break; - case VIEW_USE_2_VIEWPORTS: { - - dragging_h = false; - - } break; - case VIEW_USE_2_VIEWPORTS_ALT: { - - dragging_v = false; - - } break; - case VIEW_USE_3_VIEWPORTS: - case VIEW_USE_3_VIEWPORTS_ALT: - case VIEW_USE_4_VIEWPORTS: { - - // Do nothing. - - } break; - } - } else { - dragging_h = false; - dragging_v = false; - } - } - - Ref mm = p_event; - - if (mm.is_valid()) { - - if (view == VIEW_USE_3_VIEWPORTS || view == VIEW_USE_3_VIEWPORTS_ALT || view == VIEW_USE_4_VIEWPORTS) { - Vector2 size = get_size(); - - int h_sep = get_theme_constant("separation", "HSplitContainer"); - int v_sep = get_theme_constant("separation", "VSplitContainer"); - - int mid_w = size.width * ratio_h; - int mid_h = size.height * ratio_v; - - bool was_hovering_h = hovering_h; - bool was_hovering_v = hovering_v; - hovering_h = mm->get_position().x > (mid_w - h_sep / 2) && mm->get_position().x < (mid_w + h_sep / 2); - hovering_v = mm->get_position().y > (mid_h - v_sep / 2) && mm->get_position().y < (mid_h + v_sep / 2); - - if (was_hovering_h != hovering_h || was_hovering_v != hovering_v) { - update(); - } - } - - if (dragging_h) { - float new_ratio = drag_begin_ratio.x + (mm->get_position().x - drag_begin_pos.x) / get_size().width; - new_ratio = CLAMP(new_ratio, 40 / get_size().width, (get_size().width - 40) / get_size().width); - ratio_h = new_ratio; - queue_sort(); - update(); - } - if (dragging_v) { - float new_ratio = drag_begin_ratio.y + (mm->get_position().y - drag_begin_pos.y) / get_size().height; - new_ratio = CLAMP(new_ratio, 40 / get_size().height, (get_size().height - 40) / get_size().height); - ratio_v = new_ratio; - queue_sort(); - update(); - } - } -} - -void Node3DEditorViewportContainer::_notification(int p_what) { - - if (p_what == NOTIFICATION_MOUSE_ENTER || p_what == NOTIFICATION_MOUSE_EXIT) { - - mouseover = (p_what == NOTIFICATION_MOUSE_ENTER); - update(); - } - - if (p_what == NOTIFICATION_DRAW && mouseover) { - - Ref h_grabber = get_theme_icon("grabber", "HSplitContainer"); - Ref v_grabber = get_theme_icon("grabber", "VSplitContainer"); - - Ref hdiag_grabber = get_theme_icon("GuiViewportHdiagsplitter", "EditorIcons"); - Ref vdiag_grabber = get_theme_icon("GuiViewportVdiagsplitter", "EditorIcons"); - Ref vh_grabber = get_theme_icon("GuiViewportVhsplitter", "EditorIcons"); - - Vector2 size = get_size(); - - int h_sep = get_theme_constant("separation", "HSplitContainer"); - - int v_sep = get_theme_constant("separation", "VSplitContainer"); - - int mid_w = size.width * ratio_h; - int mid_h = size.height * ratio_v; - - int size_left = mid_w - h_sep / 2; - int size_bottom = size.height - mid_h - v_sep / 2; - - switch (view) { - - case VIEW_USE_1_VIEWPORT: { - - // Nothing to show. - - } break; - case VIEW_USE_2_VIEWPORTS: { - - draw_texture(v_grabber, Vector2((size.width - v_grabber->get_width()) / 2, mid_h - v_grabber->get_height() / 2)); - set_default_cursor_shape(CURSOR_VSPLIT); - - } break; - case VIEW_USE_2_VIEWPORTS_ALT: { - - draw_texture(h_grabber, Vector2(mid_w - h_grabber->get_width() / 2, (size.height - h_grabber->get_height()) / 2)); - set_default_cursor_shape(CURSOR_HSPLIT); - - } break; - case VIEW_USE_3_VIEWPORTS: { - - if ((hovering_v && hovering_h && !dragging_v && !dragging_h) || (dragging_v && dragging_h)) { - draw_texture(hdiag_grabber, Vector2(mid_w - hdiag_grabber->get_width() / 2, mid_h - v_grabber->get_height() / 4)); - set_default_cursor_shape(CURSOR_DRAG); - } else if ((hovering_v && !dragging_h) || dragging_v) { - draw_texture(v_grabber, Vector2((size.width - v_grabber->get_width()) / 2, mid_h - v_grabber->get_height() / 2)); - set_default_cursor_shape(CURSOR_VSPLIT); - } else if (hovering_h || dragging_h) { - draw_texture(h_grabber, Vector2(mid_w - h_grabber->get_width() / 2, mid_h + v_grabber->get_height() / 2 + (size_bottom - h_grabber->get_height()) / 2)); - set_default_cursor_shape(CURSOR_HSPLIT); - } - - } break; - case VIEW_USE_3_VIEWPORTS_ALT: { - - if ((hovering_v && hovering_h && !dragging_v && !dragging_h) || (dragging_v && dragging_h)) { - draw_texture(vdiag_grabber, Vector2(mid_w - vdiag_grabber->get_width() + v_grabber->get_height() / 4, mid_h - vdiag_grabber->get_height() / 2)); - set_default_cursor_shape(CURSOR_DRAG); - } else if ((hovering_v && !dragging_h) || dragging_v) { - draw_texture(v_grabber, Vector2((size_left - v_grabber->get_width()) / 2, mid_h - v_grabber->get_height() / 2)); - set_default_cursor_shape(CURSOR_VSPLIT); - } else if (hovering_h || dragging_h) { - draw_texture(h_grabber, Vector2(mid_w - h_grabber->get_width() / 2, (size.height - h_grabber->get_height()) / 2)); - set_default_cursor_shape(CURSOR_HSPLIT); - } - - } break; - case VIEW_USE_4_VIEWPORTS: { - - Vector2 half(mid_w, mid_h); - if ((hovering_v && hovering_h && !dragging_v && !dragging_h) || (dragging_v && dragging_h)) { - draw_texture(vh_grabber, half - vh_grabber->get_size() / 2.0); - set_default_cursor_shape(CURSOR_DRAG); - } else if ((hovering_v && !dragging_h) || dragging_v) { - draw_texture(v_grabber, half - v_grabber->get_size() / 2.0); - set_default_cursor_shape(CURSOR_VSPLIT); - } else if (hovering_h || dragging_h) { - draw_texture(h_grabber, half - h_grabber->get_size() / 2.0); - set_default_cursor_shape(CURSOR_HSPLIT); - } - - } break; - } - } - - if (p_what == NOTIFICATION_SORT_CHILDREN) { - - Node3DEditorViewport *viewports[4]; - int vc = 0; - for (int i = 0; i < get_child_count(); i++) { - viewports[vc] = Object::cast_to(get_child(i)); - if (viewports[vc]) { - vc++; - } - } - - ERR_FAIL_COND(vc != 4); - - Size2 size = get_size(); - - if (size.x < 10 || size.y < 10) { - for (int i = 0; i < 4; i++) { - viewports[i]->hide(); - } - return; - } - int h_sep = get_theme_constant("separation", "HSplitContainer"); - - int v_sep = get_theme_constant("separation", "VSplitContainer"); - - int mid_w = size.width * ratio_h; - int mid_h = size.height * ratio_v; - - int size_left = mid_w - h_sep / 2; - int size_right = size.width - mid_w - h_sep / 2; - - int size_top = mid_h - v_sep / 2; - int size_bottom = size.height - mid_h - v_sep / 2; - - switch (view) { - - case VIEW_USE_1_VIEWPORT: { - - viewports[0]->show(); - for (int i = 1; i < 4; i++) { - - viewports[i]->hide(); - } - - fit_child_in_rect(viewports[0], Rect2(Vector2(), size)); - - } break; - case VIEW_USE_2_VIEWPORTS: { - - for (int i = 0; i < 4; i++) { - - if (i == 1 || i == 3) - viewports[i]->hide(); - else - viewports[i]->show(); - } - - fit_child_in_rect(viewports[0], Rect2(Vector2(), Vector2(size.width, size_top))); - fit_child_in_rect(viewports[2], Rect2(Vector2(0, mid_h + v_sep / 2), Vector2(size.width, size_bottom))); - - } break; - case VIEW_USE_2_VIEWPORTS_ALT: { - - for (int i = 0; i < 4; i++) { - - if (i == 1 || i == 3) - viewports[i]->hide(); - else - viewports[i]->show(); - } - fit_child_in_rect(viewports[0], Rect2(Vector2(), Vector2(size_left, size.height))); - fit_child_in_rect(viewports[2], Rect2(Vector2(mid_w + h_sep / 2, 0), Vector2(size_right, size.height))); - - } break; - case VIEW_USE_3_VIEWPORTS: { - - for (int i = 0; i < 4; i++) { - - if (i == 1) - viewports[i]->hide(); - else - viewports[i]->show(); - } - - fit_child_in_rect(viewports[0], Rect2(Vector2(), Vector2(size.width, size_top))); - fit_child_in_rect(viewports[2], Rect2(Vector2(0, mid_h + v_sep / 2), Vector2(size_left, size_bottom))); - fit_child_in_rect(viewports[3], Rect2(Vector2(mid_w + h_sep / 2, mid_h + v_sep / 2), Vector2(size_right, size_bottom))); - - } break; - case VIEW_USE_3_VIEWPORTS_ALT: { - - for (int i = 0; i < 4; i++) { - - if (i == 1) - viewports[i]->hide(); - else - viewports[i]->show(); - } - - fit_child_in_rect(viewports[0], Rect2(Vector2(), Vector2(size_left, size_top))); - fit_child_in_rect(viewports[2], Rect2(Vector2(0, mid_h + v_sep / 2), Vector2(size_left, size_bottom))); - fit_child_in_rect(viewports[3], Rect2(Vector2(mid_w + h_sep / 2, 0), Vector2(size_right, size.height))); - - } break; - case VIEW_USE_4_VIEWPORTS: { - - for (int i = 0; i < 4; i++) { - - viewports[i]->show(); - } - - fit_child_in_rect(viewports[0], Rect2(Vector2(), Vector2(size_left, size_top))); - fit_child_in_rect(viewports[1], Rect2(Vector2(mid_w + h_sep / 2, 0), Vector2(size_right, size_top))); - fit_child_in_rect(viewports[2], Rect2(Vector2(0, mid_h + v_sep / 2), Vector2(size_left, size_bottom))); - fit_child_in_rect(viewports[3], Rect2(Vector2(mid_w + h_sep / 2, mid_h + v_sep / 2), Vector2(size_right, size_bottom))); - - } break; - } - } -} - -void Node3DEditorViewportContainer::set_view(View p_view) { - - view = p_view; - queue_sort(); -} - -Node3DEditorViewportContainer::View Node3DEditorViewportContainer::get_view() { - - return view; -} - -void Node3DEditorViewportContainer::_bind_methods() { - - ClassDB::bind_method("_gui_input", &Node3DEditorViewportContainer::_gui_input); -} - -Node3DEditorViewportContainer::Node3DEditorViewportContainer() { - - set_clip_contents(true); - view = VIEW_USE_1_VIEWPORT; - mouseover = false; - ratio_h = 0.5; - ratio_v = 0.5; - hovering_v = false; - hovering_h = false; - dragging_v = false; - dragging_h = false; -} - -/////////////////////////////////////////////////////////////////// - -Node3DEditor *Node3DEditor::singleton = NULL; - -Node3DEditorSelectedItem::~Node3DEditorSelectedItem() { - - if (sbox_instance.is_valid()) - VisualServer::get_singleton()->free(sbox_instance); -} - -void Node3DEditor::select_gizmo_highlight_axis(int p_axis) { - - for (int i = 0; i < 3; i++) { - - move_gizmo[i]->surface_set_material(0, i == p_axis ? gizmo_color_hl[i] : gizmo_color[i]); - move_plane_gizmo[i]->surface_set_material(0, (i + 6) == p_axis ? plane_gizmo_color_hl[i] : plane_gizmo_color[i]); - rotate_gizmo[i]->surface_set_material(0, (i + 3) == p_axis ? gizmo_color_hl[i] : gizmo_color[i]); - scale_gizmo[i]->surface_set_material(0, (i + 9) == p_axis ? gizmo_color_hl[i] : gizmo_color[i]); - scale_plane_gizmo[i]->surface_set_material(0, (i + 12) == p_axis ? plane_gizmo_color_hl[i] : plane_gizmo_color[i]); - } -} - -void Node3DEditor::update_transform_gizmo() { - - List &selection = editor_selection->get_selected_node_list(); - AABB center; - bool first = true; - - Basis gizmo_basis; - bool local_gizmo_coords = are_local_coords_enabled(); - - for (List::Element *E = selection.front(); E; E = E->next()) { - - Node3D *sp = Object::cast_to(E->get()); - if (!sp) - continue; - - Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data(sp); - if (!se) - continue; - - Transform xf = se->sp->get_global_gizmo_transform(); - - if (first) { - center.position = xf.origin; - first = false; - if (local_gizmo_coords) { - gizmo_basis = xf.basis; - gizmo_basis.orthonormalize(); - } - } else { - center.expand_to(xf.origin); - gizmo_basis = Basis(); - } - } - - Vector3 pcenter = center.position + center.size * 0.5; - gizmo.visible = !first; - gizmo.transform.origin = pcenter; - gizmo.transform.basis = gizmo_basis; - - for (uint32_t i = 0; i < VIEWPORTS_COUNT; i++) { - viewports[i]->update_transform_gizmo_view(); - } -} - -void _update_all_gizmos(Node *p_node) { - for (int i = p_node->get_child_count() - 1; 0 <= i; --i) { - Node3D *spatial_node = Object::cast_to(p_node->get_child(i)); - if (spatial_node) { - spatial_node->update_gizmo(); - } - - _update_all_gizmos(p_node->get_child(i)); - } -} - -void Node3DEditor::update_all_gizmos(Node *p_node) { - if (!p_node) { - p_node = SceneTree::get_singleton()->get_root(); - } - _update_all_gizmos(p_node); -} - -Object *Node3DEditor::_get_editor_data(Object *p_what) { - - Node3D *sp = Object::cast_to(p_what); - if (!sp) - return NULL; - - Node3DEditorSelectedItem *si = memnew(Node3DEditorSelectedItem); - - si->sp = sp; - si->sbox_instance = VisualServer::get_singleton()->instance_create2(selection_box->get_rid(), sp->get_world()->get_scenario()); - VS::get_singleton()->instance_geometry_set_cast_shadows_setting(si->sbox_instance, VS::SHADOW_CASTING_SETTING_OFF); - - return si; -} - -void Node3DEditor::_generate_selection_box() { - - AABB aabb(Vector3(), Vector3(1, 1, 1)); - aabb.grow_by(aabb.get_longest_axis_size() / 20.0); - - Ref st = memnew(SurfaceTool); - - st->begin(Mesh::PRIMITIVE_LINES); - for (int i = 0; i < 12; i++) { - - Vector3 a, b; - aabb.get_edge(i, a, b); - - st->add_color(Color(1.0, 1.0, 0.8, 0.8)); - st->add_vertex(a); - st->add_color(Color(1.0, 1.0, 0.8, 0.4)); - st->add_vertex(a.linear_interpolate(b, 0.2)); - - st->add_color(Color(1.0, 1.0, 0.8, 0.4)); - st->add_vertex(a.linear_interpolate(b, 0.8)); - st->add_color(Color(1.0, 1.0, 0.8, 0.8)); - st->add_vertex(b); - } - - Ref mat = memnew(StandardMaterial3D); - mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); - mat->set_albedo(Color(1, 1, 1)); - mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); - mat->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); - mat->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); - st->set_material(mat); - selection_box = st->commit(); -} - -Dictionary Node3DEditor::get_state() const { - - Dictionary d; - - d["snap_enabled"] = snap_enabled; - d["translate_snap"] = get_translate_snap(); - d["rotate_snap"] = get_rotate_snap(); - d["scale_snap"] = get_scale_snap(); - - d["local_coords"] = tool_option_button[TOOL_OPT_LOCAL_COORDS]->is_pressed(); - - int vc = 0; - if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT))) - vc = 1; - else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS))) - vc = 2; - else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS))) - vc = 3; - else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS))) - vc = 4; - else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS_ALT))) - vc = 5; - else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT))) - vc = 6; - - d["viewport_mode"] = vc; - Array vpdata; - for (int i = 0; i < 4; i++) { - vpdata.push_back(viewports[i]->get_state()); - } - - d["viewports"] = vpdata; - - d["show_grid"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_GRID)); - d["show_origin"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_ORIGIN)); - d["fov"] = get_fov(); - d["znear"] = get_znear(); - d["zfar"] = get_zfar(); - - Dictionary gizmos_status; - for (int i = 0; i < gizmo_plugins_by_name.size(); i++) { - if (!gizmo_plugins_by_name[i]->can_be_hidden()) continue; - int state = gizmos_menu->get_item_state(gizmos_menu->get_item_index(i)); - String name = gizmo_plugins_by_name[i]->get_name(); - gizmos_status[name] = state; - } - - d["gizmos_status"] = gizmos_status; - - return d; -} -void Node3DEditor::set_state(const Dictionary &p_state) { - - Dictionary d = p_state; - - if (d.has("snap_enabled")) { - snap_enabled = d["snap_enabled"]; - tool_option_button[TOOL_OPT_USE_SNAP]->set_pressed(d["snap_enabled"]); - } - - if (d.has("translate_snap")) - snap_translate_value = d["translate_snap"]; - - if (d.has("rotate_snap")) - snap_rotate_value = d["rotate_snap"]; - - if (d.has("scale_snap")) - snap_scale_value = d["scale_snap"]; - - _snap_update(); - - if (d.has("local_coords")) { - tool_option_button[TOOL_OPT_LOCAL_COORDS]->set_pressed(d["local_coords"]); - update_transform_gizmo(); - } - - if (d.has("viewport_mode")) { - int vc = d["viewport_mode"]; - - if (vc == 1) - _menu_item_pressed(MENU_VIEW_USE_1_VIEWPORT); - else if (vc == 2) - _menu_item_pressed(MENU_VIEW_USE_2_VIEWPORTS); - else if (vc == 3) - _menu_item_pressed(MENU_VIEW_USE_3_VIEWPORTS); - else if (vc == 4) - _menu_item_pressed(MENU_VIEW_USE_4_VIEWPORTS); - else if (vc == 5) - _menu_item_pressed(MENU_VIEW_USE_2_VIEWPORTS_ALT); - else if (vc == 6) - _menu_item_pressed(MENU_VIEW_USE_3_VIEWPORTS_ALT); - } - - if (d.has("viewports")) { - Array vp = d["viewports"]; - uint32_t vp_size = static_cast(vp.size()); - if (vp_size > VIEWPORTS_COUNT) { - WARN_PRINT("Ignoring superfluous viewport settings from spatial editor state."); - vp_size = VIEWPORTS_COUNT; - } - - for (uint32_t i = 0; i < vp_size; i++) { - viewports[i]->set_state(vp[i]); - } - } - - if (d.has("zfar")) - settings_zfar->set_value(float(d["zfar"])); - if (d.has("znear")) - settings_znear->set_value(float(d["znear"])); - if (d.has("fov")) - settings_fov->set_value(float(d["fov"])); - if (d.has("show_grid")) { - bool use = d["show_grid"]; - - if (use != view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_GRID))) { - _menu_item_pressed(MENU_VIEW_GRID); - } - } - - if (d.has("show_origin")) { - bool use = d["show_origin"]; - - if (use != view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_ORIGIN))) { - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_ORIGIN), use); - VisualServer::get_singleton()->instance_set_visible(origin_instance, use); - } - } - - if (d.has("gizmos_status")) { - Dictionary gizmos_status = d["gizmos_status"]; - List keys; - gizmos_status.get_key_list(&keys); - - for (int j = 0; j < gizmo_plugins_by_name.size(); ++j) { - if (!gizmo_plugins_by_name[j]->can_be_hidden()) continue; - int state = EditorNode3DGizmoPlugin::VISIBLE; - for (int i = 0; i < keys.size(); i++) { - if (gizmo_plugins_by_name.write[j]->get_name() == keys[i]) { - state = gizmos_status[keys[i]]; - break; - } - } - - gizmo_plugins_by_name.write[j]->set_state(state); - } - _update_gizmos_menu(); - } -} - -void Node3DEditor::edit(Node3D *p_spatial) { - - if (p_spatial != selected) { - if (selected) { - - Ref seg = selected->get_gizmo(); - if (seg.is_valid()) { - seg->set_selected(false); - selected->update_gizmo(); - } - } - - selected = p_spatial; - over_gizmo_handle = -1; - - if (selected) { - - Ref seg = selected->get_gizmo(); - if (seg.is_valid()) { - seg->set_selected(true); - selected->update_gizmo(); - } - } - } -} - -void Node3DEditor::_snap_changed() { - - snap_translate_value = snap_translate->get_text().to_double(); - snap_rotate_value = snap_rotate->get_text().to_double(); - snap_scale_value = snap_scale->get_text().to_double(); -} - -void Node3DEditor::_snap_update() { - - snap_translate->set_text(String::num(snap_translate_value)); - snap_rotate->set_text(String::num(snap_rotate_value)); - snap_scale->set_text(String::num(snap_scale_value)); -} - -void Node3DEditor::_xform_dialog_action() { - - Transform t; - //translation - Vector3 scale; - Vector3 rotate; - Vector3 translate; - - for (int i = 0; i < 3; i++) { - translate[i] = xform_translate[i]->get_text().to_double(); - rotate[i] = Math::deg2rad(xform_rotate[i]->get_text().to_double()); - scale[i] = xform_scale[i]->get_text().to_double(); - } - - t.basis.scale(scale); - t.basis.rotate(rotate); - t.origin = translate; - - undo_redo->create_action(TTR("XForm Dialog")); - - List &selection = editor_selection->get_selected_node_list(); - - for (List::Element *E = selection.front(); E; E = E->next()) { - - Node3D *sp = Object::cast_to(E->get()); - if (!sp) - continue; - - Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data(sp); - if (!se) - continue; - - bool post = xform_type->get_selected() > 0; - - Transform tr = sp->get_global_gizmo_transform(); - if (post) - tr = tr * t; - else { - - tr.basis = t.basis * tr.basis; - tr.origin += t.origin; - } - - undo_redo->add_do_method(sp, "set_global_transform", tr); - undo_redo->add_undo_method(sp, "set_global_transform", sp->get_global_gizmo_transform()); - } - undo_redo->commit_action(); -} - -void Node3DEditor::_menu_item_toggled(bool pressed, int p_option) { - - switch (p_option) { - case MENU_TOOL_LOCAL_COORDS: { - - tool_option_button[TOOL_OPT_LOCAL_COORDS]->set_pressed(pressed); - update_transform_gizmo(); - } break; - - case MENU_TOOL_USE_SNAP: { - tool_option_button[TOOL_OPT_USE_SNAP]->set_pressed(pressed); - snap_enabled = pressed; - } break; - - case MENU_TOOL_OVERRIDE_CAMERA: { - EditorDebuggerNode *const debugger = EditorDebuggerNode::get_singleton(); - - using Override = EditorDebuggerNode::CameraOverride; - if (pressed) { - - debugger->set_camera_override((Override)(Override::OVERRIDE_3D_1 + camera_override_viewport_id)); - } else { - debugger->set_camera_override(Override::OVERRIDE_NONE); - } - - } break; - } -} - -void Node3DEditor::_menu_gizmo_toggled(int p_option) { - - const int idx = gizmos_menu->get_item_index(p_option); - gizmos_menu->toggle_item_multistate(idx); - - // Change icon - const int state = gizmos_menu->get_item_state(idx); - switch (state) { - case EditorNode3DGizmoPlugin::VISIBLE: - gizmos_menu->set_item_icon(idx, view_menu->get_popup()->get_theme_icon("visibility_visible")); - break; - case EditorNode3DGizmoPlugin::ON_TOP: - gizmos_menu->set_item_icon(idx, view_menu->get_popup()->get_theme_icon("visibility_xray")); - break; - case EditorNode3DGizmoPlugin::HIDDEN: - gizmos_menu->set_item_icon(idx, view_menu->get_popup()->get_theme_icon("visibility_hidden")); - break; - } - - gizmo_plugins_by_name.write[p_option]->set_state(state); - - update_all_gizmos(); -} - -void Node3DEditor::_update_camera_override_button(bool p_game_running) { - Button *const button = tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]; - - if (p_game_running) { - button->set_disabled(false); - button->set_tooltip(TTR("Game Camera Override\nNo game instance running.")); - } else { - button->set_disabled(true); - button->set_pressed(false); - button->set_tooltip(TTR("Game Camera Override\nOverrides game camera with editor viewport camera.")); - } -} - -void Node3DEditor::_update_camera_override_viewport(Object *p_viewport) { - Node3DEditorViewport *current_viewport = Object::cast_to(p_viewport); - - if (!current_viewport) - return; - - EditorDebuggerNode *const debugger = EditorDebuggerNode::get_singleton(); - - camera_override_viewport_id = current_viewport->index; - if (debugger->get_camera_override() >= EditorDebuggerNode::OVERRIDE_3D_1) { - using Override = EditorDebuggerNode::CameraOverride; - - debugger->set_camera_override((Override)(Override::OVERRIDE_3D_1 + camera_override_viewport_id)); - } -} - -void Node3DEditor::_menu_item_pressed(int p_option) { - - switch (p_option) { - - case MENU_TOOL_SELECT: - case MENU_TOOL_MOVE: - case MENU_TOOL_ROTATE: - case MENU_TOOL_SCALE: - case MENU_TOOL_LIST_SELECT: { - - for (int i = 0; i < TOOL_MAX; i++) - tool_button[i]->set_pressed(i == p_option); - tool_mode = (ToolMode)p_option; - update_transform_gizmo(); - - } break; - case MENU_TRANSFORM_CONFIGURE_SNAP: { - - snap_dialog->popup_centered(Size2(200, 180)); - } break; - case MENU_TRANSFORM_DIALOG: { - - for (int i = 0; i < 3; i++) { - - xform_translate[i]->set_text("0"); - xform_rotate[i]->set_text("0"); - xform_scale[i]->set_text("1"); - } - - xform_dialog->popup_centered(Size2(320, 240) * EDSCALE); - - } break; - case MENU_VIEW_USE_1_VIEWPORT: { - - viewport_base->set_view(Node3DEditorViewportContainer::VIEW_USE_1_VIEWPORT); - - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), true); - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), false); - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS), false); - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS), false); - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS_ALT), false); - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT), false); - - } break; - case MENU_VIEW_USE_2_VIEWPORTS: { - - viewport_base->set_view(Node3DEditorViewportContainer::VIEW_USE_2_VIEWPORTS); - - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), false); - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), true); - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS), false); - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS), false); - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS_ALT), false); - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT), false); - - } break; - case MENU_VIEW_USE_2_VIEWPORTS_ALT: { - - viewport_base->set_view(Node3DEditorViewportContainer::VIEW_USE_2_VIEWPORTS_ALT); - - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), false); - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), false); - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS), false); - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS), false); - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS_ALT), true); - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT), false); - - } break; - case MENU_VIEW_USE_3_VIEWPORTS: { - - viewport_base->set_view(Node3DEditorViewportContainer::VIEW_USE_3_VIEWPORTS); - - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), false); - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), false); - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS), true); - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS), false); - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS_ALT), false); - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT), false); - - } break; - case MENU_VIEW_USE_3_VIEWPORTS_ALT: { - - viewport_base->set_view(Node3DEditorViewportContainer::VIEW_USE_3_VIEWPORTS_ALT); - - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), false); - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), false); - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS), false); - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS), false); - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS_ALT), false); - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT), true); - - } break; - case MENU_VIEW_USE_4_VIEWPORTS: { - - viewport_base->set_view(Node3DEditorViewportContainer::VIEW_USE_4_VIEWPORTS); - - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), false); - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), false); - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS), false); - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS), true); - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS_ALT), false); - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT), false); - - } break; - case MENU_VIEW_ORIGIN: { - - bool is_checked = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(p_option)); - - origin_enabled = !is_checked; - VisualServer::get_singleton()->instance_set_visible(origin_instance, origin_enabled); - // Update the grid since its appearance depends on whether the origin is enabled - _finish_grid(); - _init_grid(); - - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(p_option), origin_enabled); - } break; - case MENU_VIEW_GRID: { - - bool is_checked = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(p_option)); - - grid_enabled = !is_checked; - - for (int i = 0; i < 3; ++i) { - if (grid_enable[i]) { - VisualServer::get_singleton()->instance_set_visible(grid_instance[i], grid_enabled); - grid_visible[i] = grid_enabled; - } - } - - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(p_option), grid_enabled); - - } break; - case MENU_VIEW_CAMERA_SETTINGS: { - - settings_dialog->popup_centered(settings_vbc->get_combined_minimum_size() + Size2(50, 50)); - } break; - case MENU_SNAP_TO_FLOOR: { - snap_selected_nodes_to_floor(); - } break; - case MENU_LOCK_SELECTED: { - undo_redo->create_action(TTR("Lock Selected")); - - List &selection = editor_selection->get_selected_node_list(); - - for (List::Element *E = selection.front(); E; E = E->next()) { - - Node3D *spatial = Object::cast_to(E->get()); - if (!spatial || !spatial->is_visible_in_tree()) - continue; - - if (spatial->get_viewport() != EditorNode::get_singleton()->get_scene_root()) - continue; - - undo_redo->add_do_method(spatial, "set_meta", "_edit_lock_", true); - undo_redo->add_undo_method(spatial, "remove_meta", "_edit_lock_"); - undo_redo->add_do_method(this, "emit_signal", "item_lock_status_changed"); - undo_redo->add_undo_method(this, "emit_signal", "item_lock_status_changed"); - } - - undo_redo->add_do_method(this, "_refresh_menu_icons", Variant()); - undo_redo->add_undo_method(this, "_refresh_menu_icons", Variant()); - undo_redo->commit_action(); - } break; - case MENU_UNLOCK_SELECTED: { - undo_redo->create_action(TTR("Unlock Selected")); - - List &selection = editor_selection->get_selected_node_list(); - - for (List::Element *E = selection.front(); E; E = E->next()) { - - Node3D *spatial = Object::cast_to(E->get()); - if (!spatial || !spatial->is_visible_in_tree()) - continue; - - if (spatial->get_viewport() != EditorNode::get_singleton()->get_scene_root()) - continue; - - undo_redo->add_do_method(spatial, "remove_meta", "_edit_lock_"); - undo_redo->add_undo_method(spatial, "set_meta", "_edit_lock_", true); - undo_redo->add_do_method(this, "emit_signal", "item_lock_status_changed"); - undo_redo->add_undo_method(this, "emit_signal", "item_lock_status_changed"); - } - - undo_redo->add_do_method(this, "_refresh_menu_icons", Variant()); - undo_redo->add_undo_method(this, "_refresh_menu_icons", Variant()); - undo_redo->commit_action(); - } break; - case MENU_GROUP_SELECTED: { - undo_redo->create_action(TTR("Group Selected")); - - List &selection = editor_selection->get_selected_node_list(); - - for (List::Element *E = selection.front(); E; E = E->next()) { - - Node3D *spatial = Object::cast_to(E->get()); - if (!spatial || !spatial->is_visible_in_tree()) - continue; - - if (spatial->get_viewport() != EditorNode::get_singleton()->get_scene_root()) - continue; - - undo_redo->add_do_method(spatial, "set_meta", "_edit_group_", true); - undo_redo->add_undo_method(spatial, "remove_meta", "_edit_group_"); - undo_redo->add_do_method(this, "emit_signal", "item_group_status_changed"); - undo_redo->add_undo_method(this, "emit_signal", "item_group_status_changed"); - } - - undo_redo->add_do_method(this, "_refresh_menu_icons", Variant()); - undo_redo->add_undo_method(this, "_refresh_menu_icons", Variant()); - undo_redo->commit_action(); - } break; - case MENU_UNGROUP_SELECTED: { - undo_redo->create_action(TTR("Ungroup Selected")); - List &selection = editor_selection->get_selected_node_list(); - - for (List::Element *E = selection.front(); E; E = E->next()) { - - Node3D *spatial = Object::cast_to(E->get()); - if (!spatial || !spatial->is_visible_in_tree()) - continue; - - if (spatial->get_viewport() != EditorNode::get_singleton()->get_scene_root()) - continue; - - undo_redo->add_do_method(spatial, "remove_meta", "_edit_group_"); - undo_redo->add_undo_method(spatial, "set_meta", "_edit_group_", true); - undo_redo->add_do_method(this, "emit_signal", "item_group_status_changed"); - undo_redo->add_undo_method(this, "emit_signal", "item_group_status_changed"); - } - - undo_redo->add_do_method(this, "_refresh_menu_icons", Variant()); - undo_redo->add_undo_method(this, "_refresh_menu_icons", Variant()); - undo_redo->commit_action(); - } break; - } -} - -void Node3DEditor::_init_indicators() { - - { - origin_enabled = true; - grid_enabled = true; - - indicator_mat.instance(); - indicator_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); - indicator_mat->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); - indicator_mat->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); - - Vector origin_colors; - Vector origin_points; - - for (int i = 0; i < 3; i++) { - Vector3 axis; - axis[i] = 1; - Color origin_color; - switch (i) { - case 0: - origin_color = get_theme_color("axis_x_color", "Editor"); - break; - case 1: - origin_color = get_theme_color("axis_y_color", "Editor"); - break; - case 2: - origin_color = get_theme_color("axis_z_color", "Editor"); - break; - default: - origin_color = Color(); - break; - } - - grid_enable[i] = false; - grid_visible[i] = false; - - origin_colors.push_back(origin_color); - origin_colors.push_back(origin_color); - origin_points.push_back(axis * 4096); - origin_points.push_back(axis * -4096); - } - - grid_enable[1] = true; - grid_visible[1] = true; - - _init_grid(); - - origin = VisualServer::get_singleton()->mesh_create(); - Array d; - d.resize(VS::ARRAY_MAX); - d[VisualServer::ARRAY_VERTEX] = origin_points; - d[VisualServer::ARRAY_COLOR] = origin_colors; - - VisualServer::get_singleton()->mesh_add_surface_from_arrays(origin, VisualServer::PRIMITIVE_LINES, d); - VisualServer::get_singleton()->mesh_surface_set_material(origin, 0, indicator_mat->get_rid()); - - origin_instance = VisualServer::get_singleton()->instance_create2(origin, get_tree()->get_root()->get_world()->get_scenario()); - VS::get_singleton()->instance_set_layer_mask(origin_instance, 1 << Node3DEditorViewport::GIZMO_GRID_LAYER); - - VisualServer::get_singleton()->instance_geometry_set_cast_shadows_setting(origin_instance, VS::SHADOW_CASTING_SETTING_OFF); - } - - { - - //move gizmo - - for (int i = 0; i < 3; i++) { - - Color col; - switch (i) { - case 0: - col = get_theme_color("axis_x_color", "Editor"); - break; - case 1: - col = get_theme_color("axis_y_color", "Editor"); - break; - case 2: - col = get_theme_color("axis_z_color", "Editor"); - break; - default: - col = Color(); - break; - } - - col.a = EditorSettings::get_singleton()->get("editors/3d/manipulator_gizmo_opacity"); - - move_gizmo[i] = Ref(memnew(ArrayMesh)); - move_plane_gizmo[i] = Ref(memnew(ArrayMesh)); - rotate_gizmo[i] = Ref(memnew(ArrayMesh)); - scale_gizmo[i] = Ref(memnew(ArrayMesh)); - scale_plane_gizmo[i] = Ref(memnew(ArrayMesh)); - - Ref mat = memnew(StandardMaterial3D); - mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); - mat->set_on_top_of_alpha(); - mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); - mat->set_albedo(col); - gizmo_color[i] = mat; - - Ref mat_hl = mat->duplicate(); - mat_hl->set_albedo(Color(col.r, col.g, col.b, 1.0)); - gizmo_color_hl[i] = mat_hl; - - Vector3 ivec; - ivec[i] = 1; - Vector3 nivec; - nivec[(i + 1) % 3] = 1; - nivec[(i + 2) % 3] = 1; - Vector3 ivec2; - ivec2[(i + 1) % 3] = 1; - Vector3 ivec3; - ivec3[(i + 2) % 3] = 1; - - //translate - { - - Ref surftool = memnew(SurfaceTool); - surftool->begin(Mesh::PRIMITIVE_TRIANGLES); - - // Arrow profile - const int arrow_points = 5; - Vector3 arrow[5] = { - nivec * 0.0 + ivec * 0.0, - nivec * 0.01 + ivec * 0.0, - nivec * 0.01 + ivec * GIZMO_ARROW_OFFSET, - nivec * 0.065 + ivec * GIZMO_ARROW_OFFSET, - nivec * 0.0 + ivec * (GIZMO_ARROW_OFFSET + GIZMO_ARROW_SIZE), - }; - - int arrow_sides = 16; - - for (int k = 0; k < arrow_sides; k++) { - - Basis ma(ivec, Math_PI * 2 * float(k) / arrow_sides); - Basis mb(ivec, Math_PI * 2 * float(k + 1) / arrow_sides); - - for (int j = 0; j < arrow_points - 1; j++) { - - Vector3 points[4] = { - ma.xform(arrow[j]), - mb.xform(arrow[j]), - mb.xform(arrow[j + 1]), - ma.xform(arrow[j + 1]), - }; - surftool->add_vertex(points[0]); - surftool->add_vertex(points[1]); - surftool->add_vertex(points[2]); - - surftool->add_vertex(points[0]); - surftool->add_vertex(points[2]); - surftool->add_vertex(points[3]); - } - } - - surftool->set_material(mat); - surftool->commit(move_gizmo[i]); - } - - // Plane Translation - { - Ref surftool = memnew(SurfaceTool); - surftool->begin(Mesh::PRIMITIVE_TRIANGLES); - - Vector3 vec = ivec2 - ivec3; - Vector3 plane[4] = { - vec * GIZMO_PLANE_DST, - vec * GIZMO_PLANE_DST + ivec2 * GIZMO_PLANE_SIZE, - vec * (GIZMO_PLANE_DST + GIZMO_PLANE_SIZE), - vec * GIZMO_PLANE_DST - ivec3 * GIZMO_PLANE_SIZE - }; - - Basis ma(ivec, Math_PI / 2); - - Vector3 points[4] = { - ma.xform(plane[0]), - ma.xform(plane[1]), - ma.xform(plane[2]), - ma.xform(plane[3]), - }; - surftool->add_vertex(points[0]); - surftool->add_vertex(points[1]); - surftool->add_vertex(points[2]); - - surftool->add_vertex(points[0]); - surftool->add_vertex(points[2]); - surftool->add_vertex(points[3]); - - Ref plane_mat = memnew(StandardMaterial3D); - plane_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); - plane_mat->set_on_top_of_alpha(); - plane_mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); - plane_mat->set_cull_mode(StandardMaterial3D::CULL_DISABLED); - plane_mat->set_albedo(col); - plane_gizmo_color[i] = plane_mat; // needed, so we can draw planes from both sides - surftool->set_material(plane_mat); - surftool->commit(move_plane_gizmo[i]); - - Ref plane_mat_hl = plane_mat->duplicate(); - plane_mat_hl->set_albedo(Color(col.r, col.g, col.b, 1.0)); - plane_gizmo_color_hl[i] = plane_mat_hl; // needed, so we can draw planes from both sides - } - - // Rotate - { - - Ref surftool = memnew(SurfaceTool); - surftool->begin(Mesh::PRIMITIVE_TRIANGLES); - - Vector3 circle[5] = { - ivec * 0.02 + ivec2 * 0.02 + ivec2 * GIZMO_CIRCLE_SIZE, - ivec * -0.02 + ivec2 * 0.02 + ivec2 * GIZMO_CIRCLE_SIZE, - ivec * -0.02 + ivec2 * -0.02 + ivec2 * GIZMO_CIRCLE_SIZE, - ivec * 0.02 + ivec2 * -0.02 + ivec2 * GIZMO_CIRCLE_SIZE, - ivec * 0.02 + ivec2 * 0.02 + ivec2 * GIZMO_CIRCLE_SIZE, - }; - - for (int k = 0; k < 64; k++) { - - Basis ma(ivec, Math_PI * 2 * float(k) / 64); - Basis mb(ivec, Math_PI * 2 * float(k + 1) / 64); - - for (int j = 0; j < 4; j++) { - - Vector3 points[4] = { - ma.xform(circle[j]), - mb.xform(circle[j]), - mb.xform(circle[j + 1]), - ma.xform(circle[j + 1]), - }; - surftool->add_vertex(points[0]); - surftool->add_vertex(points[1]); - surftool->add_vertex(points[2]); - - surftool->add_vertex(points[0]); - surftool->add_vertex(points[2]); - surftool->add_vertex(points[3]); - } - } - - surftool->set_material(mat); - surftool->commit(rotate_gizmo[i]); - } - - // Scale - { - Ref surftool = memnew(SurfaceTool); - surftool->begin(Mesh::PRIMITIVE_TRIANGLES); - - // Cube arrow profile - const int arrow_points = 6; - Vector3 arrow[6] = { - nivec * 0.0 + ivec * 0.0, - nivec * 0.01 + ivec * 0.0, - nivec * 0.01 + ivec * 1.0 * GIZMO_SCALE_OFFSET, - nivec * 0.07 + ivec * 1.0 * GIZMO_SCALE_OFFSET, - nivec * 0.07 + ivec * 1.11 * GIZMO_SCALE_OFFSET, - nivec * 0.0 + ivec * 1.11 * GIZMO_SCALE_OFFSET, - }; - - int arrow_sides = 4; - - for (int k = 0; k < 4; k++) { - - Basis ma(ivec, Math_PI * 2 * float(k) / arrow_sides); - Basis mb(ivec, Math_PI * 2 * float(k + 1) / arrow_sides); - - for (int j = 0; j < arrow_points - 1; j++) { - - Vector3 points[4] = { - ma.xform(arrow[j]), - mb.xform(arrow[j]), - mb.xform(arrow[j + 1]), - ma.xform(arrow[j + 1]), - }; - surftool->add_vertex(points[0]); - surftool->add_vertex(points[1]); - surftool->add_vertex(points[2]); - - surftool->add_vertex(points[0]); - surftool->add_vertex(points[2]); - surftool->add_vertex(points[3]); - } - } - - surftool->set_material(mat); - surftool->commit(scale_gizmo[i]); - } - - // Plane Scale - { - Ref surftool = memnew(SurfaceTool); - surftool->begin(Mesh::PRIMITIVE_TRIANGLES); - - Vector3 vec = ivec2 - ivec3; - Vector3 plane[4] = { - vec * GIZMO_PLANE_DST, - vec * GIZMO_PLANE_DST + ivec2 * GIZMO_PLANE_SIZE, - vec * (GIZMO_PLANE_DST + GIZMO_PLANE_SIZE), - vec * GIZMO_PLANE_DST - ivec3 * GIZMO_PLANE_SIZE - }; - - Basis ma(ivec, Math_PI / 2); - - Vector3 points[4] = { - ma.xform(plane[0]), - ma.xform(plane[1]), - ma.xform(plane[2]), - ma.xform(plane[3]), - }; - surftool->add_vertex(points[0]); - surftool->add_vertex(points[1]); - surftool->add_vertex(points[2]); - - surftool->add_vertex(points[0]); - surftool->add_vertex(points[2]); - surftool->add_vertex(points[3]); - - Ref plane_mat = memnew(StandardMaterial3D); - plane_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); - plane_mat->set_on_top_of_alpha(); - plane_mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); - plane_mat->set_cull_mode(StandardMaterial3D::CULL_DISABLED); - plane_mat->set_albedo(col); - plane_gizmo_color[i] = plane_mat; // needed, so we can draw planes from both sides - surftool->set_material(plane_mat); - surftool->commit(scale_plane_gizmo[i]); - - Ref plane_mat_hl = plane_mat->duplicate(); - plane_mat_hl->set_albedo(Color(col.r, col.g, col.b, 1.0)); - plane_gizmo_color_hl[i] = plane_mat_hl; // needed, so we can draw planes from both sides - } - } - } - - _generate_selection_box(); -} - -void Node3DEditor::_update_gizmos_menu() { - - gizmos_menu->clear(); - - for (int i = 0; i < gizmo_plugins_by_name.size(); ++i) { - if (!gizmo_plugins_by_name[i]->can_be_hidden()) continue; - String plugin_name = gizmo_plugins_by_name[i]->get_name(); - const int plugin_state = gizmo_plugins_by_name[i]->get_state(); - gizmos_menu->add_multistate_item(TTR(plugin_name), 3, plugin_state, i); - const int idx = gizmos_menu->get_item_index(i); - switch (plugin_state) { - case EditorNode3DGizmoPlugin::VISIBLE: - gizmos_menu->set_item_icon(idx, gizmos_menu->get_theme_icon("visibility_visible")); - break; - case EditorNode3DGizmoPlugin::ON_TOP: - gizmos_menu->set_item_icon(idx, gizmos_menu->get_theme_icon("visibility_xray")); - break; - case EditorNode3DGizmoPlugin::HIDDEN: - gizmos_menu->set_item_icon(idx, gizmos_menu->get_theme_icon("visibility_hidden")); - break; - } - } -} - -void Node3DEditor::_update_gizmos_menu_theme() { - for (int i = 0; i < gizmo_plugins_by_name.size(); ++i) { - if (!gizmo_plugins_by_name[i]->can_be_hidden()) continue; - const int plugin_state = gizmo_plugins_by_name[i]->get_state(); - const int idx = gizmos_menu->get_item_index(i); - switch (plugin_state) { - case EditorNode3DGizmoPlugin::VISIBLE: - gizmos_menu->set_item_icon(idx, gizmos_menu->get_theme_icon("visibility_visible")); - break; - case EditorNode3DGizmoPlugin::ON_TOP: - gizmos_menu->set_item_icon(idx, gizmos_menu->get_theme_icon("visibility_xray")); - break; - case EditorNode3DGizmoPlugin::HIDDEN: - gizmos_menu->set_item_icon(idx, gizmos_menu->get_theme_icon("visibility_hidden")); - break; - } - } -} - -void Node3DEditor::_init_grid() { - - Vector grid_colors[3]; - Vector grid_points[3]; - - Color primary_grid_color = EditorSettings::get_singleton()->get("editors/3d/primary_grid_color"); - Color secondary_grid_color = EditorSettings::get_singleton()->get("editors/3d/secondary_grid_color"); - int grid_size = EditorSettings::get_singleton()->get("editors/3d/grid_size"); - int primary_grid_steps = EditorSettings::get_singleton()->get("editors/3d/primary_grid_steps"); - - for (int i = 0; i < 3; i++) { - Vector3 axis; - axis[i] = 1; - Vector3 axis_n1; - axis_n1[(i + 1) % 3] = 1; - Vector3 axis_n2; - axis_n2[(i + 2) % 3] = 1; - - for (int j = -grid_size; j <= grid_size; j++) { - Vector3 p1 = axis_n1 * j + axis_n2 * -grid_size; - Vector3 p1_dest = p1 * (-axis_n2 + axis_n1); - Vector3 p2 = axis_n2 * j + axis_n1 * -grid_size; - Vector3 p2_dest = p2 * (-axis_n1 + axis_n2); - - Color line_color = secondary_grid_color; - if (origin_enabled && j == 0) { - // Don't draw the center lines of the grid if the origin is enabled - // The origin would overlap the grid lines in this case, causing flickering - continue; - } else if (j % primary_grid_steps == 0) { - line_color = primary_grid_color; - } - - grid_points[i].push_back(p1); - grid_points[i].push_back(p1_dest); - grid_colors[i].push_back(line_color); - grid_colors[i].push_back(line_color); - - grid_points[i].push_back(p2); - grid_points[i].push_back(p2_dest); - grid_colors[i].push_back(line_color); - grid_colors[i].push_back(line_color); - } - - grid[i] = VisualServer::get_singleton()->mesh_create(); - Array d; - d.resize(VS::ARRAY_MAX); - d[VisualServer::ARRAY_VERTEX] = grid_points[i]; - d[VisualServer::ARRAY_COLOR] = grid_colors[i]; - VisualServer::get_singleton()->mesh_add_surface_from_arrays(grid[i], VisualServer::PRIMITIVE_LINES, d); - VisualServer::get_singleton()->mesh_surface_set_material(grid[i], 0, indicator_mat->get_rid()); - grid_instance[i] = VisualServer::get_singleton()->instance_create2(grid[i], get_tree()->get_root()->get_world()->get_scenario()); - - VisualServer::get_singleton()->instance_set_visible(grid_instance[i], grid_visible[i]); - VisualServer::get_singleton()->instance_geometry_set_cast_shadows_setting(grid_instance[i], VS::SHADOW_CASTING_SETTING_OFF); - VS::get_singleton()->instance_set_layer_mask(grid_instance[i], 1 << Node3DEditorViewport::GIZMO_GRID_LAYER); - } -} - -void Node3DEditor::_finish_indicators() { - - VisualServer::get_singleton()->free(origin_instance); - VisualServer::get_singleton()->free(origin); - - _finish_grid(); -} - -void Node3DEditor::_finish_grid() { - for (int i = 0; i < 3; i++) { - VisualServer::get_singleton()->free(grid_instance[i]); - VisualServer::get_singleton()->free(grid[i]); - } -} - -bool Node3DEditor::is_any_freelook_active() const { - for (unsigned int i = 0; i < VIEWPORTS_COUNT; ++i) { - if (viewports[i]->is_freelook_active()) - return true; - } - return false; -} - -void Node3DEditor::_refresh_menu_icons() { - - bool all_locked = true; - bool all_grouped = true; - - List &selection = editor_selection->get_selected_node_list(); - - if (selection.empty()) { - all_locked = false; - all_grouped = false; - } else { - for (List::Element *E = selection.front(); E; E = E->next()) { - if (Object::cast_to(E->get()) && !Object::cast_to(E->get())->has_meta("_edit_lock_")) { - all_locked = false; - break; - } - } - for (List::Element *E = selection.front(); E; E = E->next()) { - if (Object::cast_to(E->get()) && !Object::cast_to(E->get())->has_meta("_edit_group_")) { - all_grouped = false; - break; - } - } - } - - tool_button[TOOL_LOCK_SELECTED]->set_visible(!all_locked); - tool_button[TOOL_LOCK_SELECTED]->set_disabled(selection.empty()); - tool_button[TOOL_UNLOCK_SELECTED]->set_visible(all_locked); - - tool_button[TOOL_GROUP_SELECTED]->set_visible(!all_grouped); - tool_button[TOOL_GROUP_SELECTED]->set_disabled(selection.empty()); - tool_button[TOOL_UNGROUP_SELECTED]->set_visible(all_grouped); -} - -template -Set _get_child_nodes(Node *parent_node) { - Set nodes = Set(); - T *node = Node::cast_to(parent_node); - if (node) { - nodes.insert(node); - } - - for (int i = 0; i < parent_node->get_child_count(); i++) { - Node *child_node = parent_node->get_child(i); - Set child_nodes = _get_child_nodes(child_node); - for (typename Set::Element *I = child_nodes.front(); I; I = I->next()) { - nodes.insert(I->get()); - } - } - - return nodes; -} - -Set _get_physics_bodies_rid(Node *node) { - Set rids = Set(); - PhysicsBody3D *pb = Node::cast_to(node); - if (pb) { - rids.insert(pb->get_rid()); - } - Set child_nodes = _get_child_nodes(node); - for (Set::Element *I = child_nodes.front(); I; I = I->next()) { - rids.insert(I->get()->get_rid()); - } - - return rids; -} - -void Node3DEditor::snap_selected_nodes_to_floor() { - List &selection = editor_selection->get_selected_node_list(); - Dictionary snap_data; - - for (List::Element *E = selection.front(); E; E = E->next()) { - Node3D *sp = Object::cast_to(E->get()); - if (sp) { - Vector3 from = Vector3(); - Vector3 position_offset = Vector3(); - - // Priorities for snapping to floor are CollisionShapes, VisualInstances and then origin - Set vi = _get_child_nodes(sp); - Set cs = _get_child_nodes(sp); - - if (cs.size()) { - AABB aabb = sp->get_global_transform().xform(cs.front()->get()->get_shape()->get_debug_mesh()->get_aabb()); - for (Set::Element *I = cs.front(); I; I = I->next()) { - aabb.merge_with(sp->get_global_transform().xform(I->get()->get_shape()->get_debug_mesh()->get_aabb())); - } - Vector3 size = aabb.size * Vector3(0.5, 0.0, 0.5); - from = aabb.position + size; - position_offset.y = from.y - sp->get_global_transform().origin.y; - } else if (vi.size()) { - AABB aabb = vi.front()->get()->get_transformed_aabb(); - for (Set::Element *I = vi.front(); I; I = I->next()) { - aabb.merge_with(I->get()->get_transformed_aabb()); - } - Vector3 size = aabb.size * Vector3(0.5, 0.0, 0.5); - from = aabb.position + size; - position_offset.y = from.y - sp->get_global_transform().origin.y; - } else { - from = sp->get_global_transform().origin; - } - - // We add a bit of margin to the from position to avoid it from snapping - // when the spatial is already on a floor and there's another floor under - // it - from = from + Vector3(0.0, 0.2, 0.0); - - Dictionary d; - - d["from"] = from; - d["position_offset"] = position_offset; - snap_data[sp] = d; - } - } - - PhysicsDirectSpaceState *ss = get_tree()->get_root()->get_world()->get_direct_space_state(); - PhysicsDirectSpaceState::RayResult result; - - Array keys = snap_data.keys(); - - // The maximum height an object can travel to be snapped - const float max_snap_height = 20.0; - - // Will be set to `true` if at least one node from the selection was successfully snapped - bool snapped_to_floor = false; - - if (keys.size()) { - // For snapping to be performed, there must be solid geometry under at least one of the selected nodes. - // We need to check this before snapping to register the undo/redo action only if needed. - for (int i = 0; i < keys.size(); i++) { - Node *node = keys[i]; - Node3D *sp = Object::cast_to(node); - Dictionary d = snap_data[node]; - Vector3 from = d["from"]; - Vector3 to = from - Vector3(0.0, max_snap_height, 0.0); - Set excluded = _get_physics_bodies_rid(sp); - - if (ss->intersect_ray(from, to, result, excluded)) { - snapped_to_floor = true; - } - } - - if (snapped_to_floor) { - undo_redo->create_action(TTR("Snap Nodes To Floor")); - - // Perform snapping if at least one node can be snapped - for (int i = 0; i < keys.size(); i++) { - Node *node = keys[i]; - Node3D *sp = Object::cast_to(node); - Dictionary d = snap_data[node]; - Vector3 from = d["from"]; - Vector3 to = from - Vector3(0.0, max_snap_height, 0.0); - Set excluded = _get_physics_bodies_rid(sp); - - if (ss->intersect_ray(from, to, result, excluded)) { - Vector3 position_offset = d["position_offset"]; - Transform new_transform = sp->get_global_transform(); - - new_transform.origin.y = result.position.y; - new_transform.origin = new_transform.origin - position_offset; - - undo_redo->add_do_method(sp, "set_global_transform", new_transform); - undo_redo->add_undo_method(sp, "set_global_transform", sp->get_global_transform()); - } - } - - undo_redo->commit_action(); - } else { - EditorNode::get_singleton()->show_warning(TTR("Couldn't find a solid floor to snap the selection to.")); - } - } -} - -void Node3DEditor::_unhandled_key_input(Ref p_event) { - - if (!is_visible_in_tree()) - return; - - snap_key_enabled = InputFilter::get_singleton()->is_key_pressed(KEY_CONTROL); -} -void Node3DEditor::_notification(int p_what) { - - if (p_what == NOTIFICATION_READY) { - - tool_button[Node3DEditor::TOOL_MODE_SELECT]->set_icon(get_theme_icon("ToolSelect", "EditorIcons")); - tool_button[Node3DEditor::TOOL_MODE_MOVE]->set_icon(get_theme_icon("ToolMove", "EditorIcons")); - tool_button[Node3DEditor::TOOL_MODE_ROTATE]->set_icon(get_theme_icon("ToolRotate", "EditorIcons")); - tool_button[Node3DEditor::TOOL_MODE_SCALE]->set_icon(get_theme_icon("ToolScale", "EditorIcons")); - tool_button[Node3DEditor::TOOL_MODE_LIST_SELECT]->set_icon(get_theme_icon("ListSelect", "EditorIcons")); - tool_button[Node3DEditor::TOOL_LOCK_SELECTED]->set_icon(get_theme_icon("Lock", "EditorIcons")); - tool_button[Node3DEditor::TOOL_UNLOCK_SELECTED]->set_icon(get_theme_icon("Unlock", "EditorIcons")); - tool_button[Node3DEditor::TOOL_GROUP_SELECTED]->set_icon(get_theme_icon("Group", "EditorIcons")); - tool_button[Node3DEditor::TOOL_UNGROUP_SELECTED]->set_icon(get_theme_icon("Ungroup", "EditorIcons")); - - tool_option_button[Node3DEditor::TOOL_OPT_LOCAL_COORDS]->set_icon(get_theme_icon("Object", "EditorIcons")); - tool_option_button[Node3DEditor::TOOL_OPT_USE_SNAP]->set_icon(get_theme_icon("Snap", "EditorIcons")); - tool_option_button[Node3DEditor::TOOL_OPT_OVERRIDE_CAMERA]->set_icon(get_theme_icon("Camera3D", "EditorIcons")); - - view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), get_theme_icon("Panels1", "EditorIcons")); - view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), get_theme_icon("Panels2", "EditorIcons")); - view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS_ALT), get_theme_icon("Panels2Alt", "EditorIcons")); - view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS), get_theme_icon("Panels3", "EditorIcons")); - view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT), get_theme_icon("Panels3Alt", "EditorIcons")); - view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS), get_theme_icon("Panels4", "EditorIcons")); - - _menu_item_pressed(MENU_VIEW_USE_1_VIEWPORT); - - _refresh_menu_icons(); - - get_tree()->connect("node_removed", callable_mp(this, &Node3DEditor::_node_removed)); - EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor()->connect("node_changed", callable_mp(this, &Node3DEditor::_refresh_menu_icons)); - editor_selection->connect("selection_changed", callable_mp(this, &Node3DEditor::_refresh_menu_icons)); - - editor->connect("stop_pressed", callable_mp(this, &Node3DEditor::_update_camera_override_button), make_binds(false)); - editor->connect("play_pressed", callable_mp(this, &Node3DEditor::_update_camera_override_button), make_binds(true)); - } else if (p_what == NOTIFICATION_ENTER_TREE) { - - _register_all_gizmos(); - _update_gizmos_menu(); - _init_indicators(); - } else if (p_what == NOTIFICATION_THEME_CHANGED) { - _update_gizmos_menu_theme(); - } else if (p_what == NOTIFICATION_EXIT_TREE) { - - _finish_indicators(); - } else if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) { - tool_button[Node3DEditor::TOOL_MODE_SELECT]->set_icon(get_theme_icon("ToolSelect", "EditorIcons")); - tool_button[Node3DEditor::TOOL_MODE_MOVE]->set_icon(get_theme_icon("ToolMove", "EditorIcons")); - tool_button[Node3DEditor::TOOL_MODE_ROTATE]->set_icon(get_theme_icon("ToolRotate", "EditorIcons")); - tool_button[Node3DEditor::TOOL_MODE_SCALE]->set_icon(get_theme_icon("ToolScale", "EditorIcons")); - tool_button[Node3DEditor::TOOL_MODE_LIST_SELECT]->set_icon(get_theme_icon("ListSelect", "EditorIcons")); - tool_button[Node3DEditor::TOOL_LOCK_SELECTED]->set_icon(get_theme_icon("Lock", "EditorIcons")); - tool_button[Node3DEditor::TOOL_UNLOCK_SELECTED]->set_icon(get_theme_icon("Unlock", "EditorIcons")); - tool_button[Node3DEditor::TOOL_GROUP_SELECTED]->set_icon(get_theme_icon("Group", "EditorIcons")); - tool_button[Node3DEditor::TOOL_UNGROUP_SELECTED]->set_icon(get_theme_icon("Ungroup", "EditorIcons")); - - tool_option_button[Node3DEditor::TOOL_OPT_LOCAL_COORDS]->set_icon(get_theme_icon("Object", "EditorIcons")); - tool_option_button[Node3DEditor::TOOL_OPT_USE_SNAP]->set_icon(get_theme_icon("Snap", "EditorIcons")); - - view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), get_theme_icon("Panels1", "EditorIcons")); - view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), get_theme_icon("Panels2", "EditorIcons")); - view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS_ALT), get_theme_icon("Panels2Alt", "EditorIcons")); - view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS), get_theme_icon("Panels3", "EditorIcons")); - view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT), get_theme_icon("Panels3Alt", "EditorIcons")); - view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS), get_theme_icon("Panels4", "EditorIcons")); - - // Update grid color by rebuilding grid. - _finish_grid(); - _init_grid(); - } else if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { - if (!is_visible() && tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->is_pressed()) { - EditorDebuggerNode *debugger = EditorDebuggerNode::get_singleton(); - - debugger->set_camera_override(EditorDebuggerNode::OVERRIDE_NONE); - tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->set_pressed(false); - } - } -} - -void Node3DEditor::add_control_to_menu_panel(Control *p_control) { - - hbc_menu->add_child(p_control); -} - -void Node3DEditor::remove_control_from_menu_panel(Control *p_control) { - - hbc_menu->remove_child(p_control); -} - -void Node3DEditor::set_can_preview(Camera3D *p_preview) { - - for (int i = 0; i < 4; i++) { - viewports[i]->set_can_preview(p_preview); - } -} - -VSplitContainer *Node3DEditor::get_shader_split() { - - return shader_split; -} - -HSplitContainer *Node3DEditor::get_palette_split() { - - return palette_split; -} - -void Node3DEditor::_request_gizmo(Object *p_obj) { - - Node3D *sp = Object::cast_to(p_obj); - if (!sp) - return; - if (editor->get_edited_scene() && (sp == editor->get_edited_scene() || (sp->get_owner() && editor->get_edited_scene()->is_a_parent_of(sp)))) { - - Ref seg; - - for (int i = 0; i < gizmo_plugins_by_priority.size(); ++i) { - seg = gizmo_plugins_by_priority.write[i]->get_gizmo(sp); - - if (seg.is_valid()) { - sp->set_gizmo(seg); - - if (sp == selected) { - seg->set_selected(true); - selected->update_gizmo(); - } - - break; - } - } - } -} - -void Node3DEditor::_toggle_maximize_view(Object *p_viewport) { - if (!p_viewport) return; - Node3DEditorViewport *current_viewport = Object::cast_to(p_viewport); - if (!current_viewport) return; - - int index = -1; - bool maximized = false; - for (int i = 0; i < 4; i++) { - if (viewports[i] == current_viewport) { - index = i; - if (current_viewport->get_global_rect() == viewport_base->get_global_rect()) - maximized = true; - break; - } - } - if (index == -1) return; - - if (!maximized) { - - for (uint32_t i = 0; i < VIEWPORTS_COUNT; i++) { - if (i == (uint32_t)index) - viewports[i]->set_anchors_and_margins_preset(Control::PRESET_WIDE); - else - viewports[i]->hide(); - } - } else { - - for (uint32_t i = 0; i < VIEWPORTS_COUNT; i++) - viewports[i]->show(); - - if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT))) - _menu_item_pressed(MENU_VIEW_USE_1_VIEWPORT); - else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS))) - _menu_item_pressed(MENU_VIEW_USE_2_VIEWPORTS); - else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS_ALT))) - _menu_item_pressed(MENU_VIEW_USE_2_VIEWPORTS_ALT); - else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS))) - _menu_item_pressed(MENU_VIEW_USE_3_VIEWPORTS); - else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT))) - _menu_item_pressed(MENU_VIEW_USE_3_VIEWPORTS_ALT); - else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS))) - _menu_item_pressed(MENU_VIEW_USE_4_VIEWPORTS); - } -} - -void Node3DEditor::_node_removed(Node *p_node) { - - if (p_node == selected) - selected = NULL; -} - -void Node3DEditor::_register_all_gizmos() { - add_gizmo_plugin(Ref(memnew(CameraNode3DGizmoPlugin))); - add_gizmo_plugin(Ref(memnew(LightNode3DGizmoPlugin))); - add_gizmo_plugin(Ref(memnew(AudioStreamPlayer3DNode3DGizmoPlugin))); - add_gizmo_plugin(Ref(memnew(MeshInstanceNode3DGizmoPlugin))); - add_gizmo_plugin(Ref(memnew(SoftBodyNode3DGizmoPlugin))); - add_gizmo_plugin(Ref(memnew(Sprite3DNode3DGizmoPlugin))); - add_gizmo_plugin(Ref(memnew(SkeletonNode3DGizmoPlugin))); - add_gizmo_plugin(Ref(memnew(Position3DNode3DGizmoPlugin))); - add_gizmo_plugin(Ref(memnew(RayCastNode3DGizmoPlugin))); - add_gizmo_plugin(Ref(memnew(SpringArmNode3DGizmoPlugin))); - add_gizmo_plugin(Ref(memnew(VehicleWheelNode3DGizmoPlugin))); - add_gizmo_plugin(Ref(memnew(VisibilityNotifierGizmoPlugin))); - add_gizmo_plugin(Ref(memnew(ParticlesGizmoPlugin))); - add_gizmo_plugin(Ref(memnew(CPUParticlesGizmoPlugin))); - add_gizmo_plugin(Ref(memnew(ReflectionProbeGizmoPlugin))); - add_gizmo_plugin(Ref(memnew(GIProbeGizmoPlugin))); - // add_gizmo_plugin(Ref(memnew(BakedIndirectLightGizmoPlugin))); - add_gizmo_plugin(Ref(memnew(CollisionShapeNode3DGizmoPlugin))); - add_gizmo_plugin(Ref(memnew(CollisionPolygonNode3DGizmoPlugin))); - add_gizmo_plugin(Ref(memnew(NavigationMeshNode3DGizmoPlugin))); - add_gizmo_plugin(Ref(memnew(JointNode3DGizmoPlugin))); - add_gizmo_plugin(Ref(memnew(PhysicalBoneNode3DGizmoPlugin))); -} - -void Node3DEditor::_bind_methods() { - - ClassDB::bind_method("_unhandled_key_input", &Node3DEditor::_unhandled_key_input); - ClassDB::bind_method("_get_editor_data", &Node3DEditor::_get_editor_data); - ClassDB::bind_method("_request_gizmo", &Node3DEditor::_request_gizmo); - - ADD_SIGNAL(MethodInfo("transform_key_request")); - ADD_SIGNAL(MethodInfo("item_lock_status_changed")); - ADD_SIGNAL(MethodInfo("item_group_status_changed")); -} - -void Node3DEditor::clear() { - - settings_fov->set_value(EDITOR_DEF("editors/3d/default_fov", 70.0)); - settings_znear->set_value(EDITOR_DEF("editors/3d/default_z_near", 0.05)); - settings_zfar->set_value(EDITOR_DEF("editors/3d/default_z_far", 1500.0)); - - for (uint32_t i = 0; i < VIEWPORTS_COUNT; i++) { - viewports[i]->reset(); - } - - VisualServer::get_singleton()->instance_set_visible(origin_instance, true); - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_ORIGIN), true); - for (int i = 0; i < 3; ++i) { - if (grid_enable[i]) { - VisualServer::get_singleton()->instance_set_visible(grid_instance[i], true); - grid_visible[i] = true; - } - } - - for (uint32_t i = 0; i < VIEWPORTS_COUNT; i++) { - - viewports[i]->view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(Node3DEditorViewport::VIEW_AUDIO_LISTENER), i == 0); - viewports[i]->viewport->set_as_audio_listener(i == 0); - } - - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_GRID), true); -} - -Node3DEditor::Node3DEditor(EditorNode *p_editor) { - - gizmo.visible = true; - gizmo.scale = 1.0; - - viewport_environment = Ref(memnew(Environment)); - undo_redo = p_editor->get_undo_redo(); - VBoxContainer *vbc = this; - - custom_camera = NULL; - singleton = this; - editor = p_editor; - editor_selection = editor->get_editor_selection(); - editor_selection->add_editor_plugin(this); - - snap_enabled = false; - snap_key_enabled = false; - tool_mode = TOOL_MODE_SELECT; - - camera_override_viewport_id = 0; - - hbc_menu = memnew(HBoxContainer); - vbc->add_child(hbc_menu); - - Vector button_binds; - button_binds.resize(1); - String sct; - - tool_button[TOOL_MODE_SELECT] = memnew(ToolButton); - hbc_menu->add_child(tool_button[TOOL_MODE_SELECT]); - tool_button[TOOL_MODE_SELECT]->set_toggle_mode(true); - tool_button[TOOL_MODE_SELECT]->set_flat(true); - tool_button[TOOL_MODE_SELECT]->set_pressed(true); - button_binds.write[0] = MENU_TOOL_SELECT; - tool_button[TOOL_MODE_SELECT]->connect("pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed), button_binds); - tool_button[TOOL_MODE_SELECT]->set_shortcut(ED_SHORTCUT("spatial_editor/tool_select", TTR("Select Mode"), KEY_Q)); - tool_button[TOOL_MODE_SELECT]->set_tooltip(keycode_get_string(KEY_MASK_CMD) + TTR("Drag: Rotate\nAlt+Drag: Move\nAlt+RMB: Depth list selection")); - - hbc_menu->add_child(memnew(VSeparator)); - - tool_button[TOOL_MODE_MOVE] = memnew(ToolButton); - hbc_menu->add_child(tool_button[TOOL_MODE_MOVE]); - tool_button[TOOL_MODE_MOVE]->set_toggle_mode(true); - tool_button[TOOL_MODE_MOVE]->set_flat(true); - button_binds.write[0] = MENU_TOOL_MOVE; - tool_button[TOOL_MODE_MOVE]->connect("pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed), button_binds); - tool_button[TOOL_MODE_MOVE]->set_shortcut(ED_SHORTCUT("spatial_editor/tool_move", TTR("Move Mode"), KEY_W)); - - tool_button[TOOL_MODE_ROTATE] = memnew(ToolButton); - hbc_menu->add_child(tool_button[TOOL_MODE_ROTATE]); - tool_button[TOOL_MODE_ROTATE]->set_toggle_mode(true); - tool_button[TOOL_MODE_ROTATE]->set_flat(true); - button_binds.write[0] = MENU_TOOL_ROTATE; - tool_button[TOOL_MODE_ROTATE]->connect("pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed), button_binds); - tool_button[TOOL_MODE_ROTATE]->set_shortcut(ED_SHORTCUT("spatial_editor/tool_rotate", TTR("Rotate Mode"), KEY_E)); - - tool_button[TOOL_MODE_SCALE] = memnew(ToolButton); - hbc_menu->add_child(tool_button[TOOL_MODE_SCALE]); - tool_button[TOOL_MODE_SCALE]->set_toggle_mode(true); - tool_button[TOOL_MODE_SCALE]->set_flat(true); - button_binds.write[0] = MENU_TOOL_SCALE; - tool_button[TOOL_MODE_SCALE]->connect("pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed), button_binds); - tool_button[TOOL_MODE_SCALE]->set_shortcut(ED_SHORTCUT("spatial_editor/tool_scale", TTR("Scale Mode"), KEY_R)); - - hbc_menu->add_child(memnew(VSeparator)); - - tool_button[TOOL_MODE_LIST_SELECT] = memnew(ToolButton); - hbc_menu->add_child(tool_button[TOOL_MODE_LIST_SELECT]); - tool_button[TOOL_MODE_LIST_SELECT]->set_toggle_mode(true); - tool_button[TOOL_MODE_LIST_SELECT]->set_flat(true); - button_binds.write[0] = MENU_TOOL_LIST_SELECT; - tool_button[TOOL_MODE_LIST_SELECT]->connect("pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed), button_binds); - tool_button[TOOL_MODE_LIST_SELECT]->set_tooltip(TTR("Show a list of all objects at the position clicked\n(same as Alt+RMB in select mode).")); - - tool_button[TOOL_LOCK_SELECTED] = memnew(ToolButton); - hbc_menu->add_child(tool_button[TOOL_LOCK_SELECTED]); - button_binds.write[0] = MENU_LOCK_SELECTED; - tool_button[TOOL_LOCK_SELECTED]->connect("pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed), button_binds); - tool_button[TOOL_LOCK_SELECTED]->set_tooltip(TTR("Lock the selected object in place (can't be moved).")); - - tool_button[TOOL_UNLOCK_SELECTED] = memnew(ToolButton); - hbc_menu->add_child(tool_button[TOOL_UNLOCK_SELECTED]); - button_binds.write[0] = MENU_UNLOCK_SELECTED; - tool_button[TOOL_UNLOCK_SELECTED]->connect("pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed), button_binds); - tool_button[TOOL_UNLOCK_SELECTED]->set_tooltip(TTR("Unlock the selected object (can be moved).")); - - tool_button[TOOL_GROUP_SELECTED] = memnew(ToolButton); - hbc_menu->add_child(tool_button[TOOL_GROUP_SELECTED]); - button_binds.write[0] = MENU_GROUP_SELECTED; - tool_button[TOOL_GROUP_SELECTED]->connect("pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed), button_binds); - tool_button[TOOL_GROUP_SELECTED]->set_tooltip(TTR("Makes sure the object's children are not selectable.")); - - tool_button[TOOL_UNGROUP_SELECTED] = memnew(ToolButton); - hbc_menu->add_child(tool_button[TOOL_UNGROUP_SELECTED]); - button_binds.write[0] = MENU_UNGROUP_SELECTED; - tool_button[TOOL_UNGROUP_SELECTED]->connect("pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed), button_binds); - tool_button[TOOL_UNGROUP_SELECTED]->set_tooltip(TTR("Restores the object's children's ability to be selected.")); - - hbc_menu->add_child(memnew(VSeparator)); - - tool_option_button[TOOL_OPT_LOCAL_COORDS] = memnew(ToolButton); - hbc_menu->add_child(tool_option_button[TOOL_OPT_LOCAL_COORDS]); - tool_option_button[TOOL_OPT_LOCAL_COORDS]->set_toggle_mode(true); - tool_option_button[TOOL_OPT_LOCAL_COORDS]->set_flat(true); - button_binds.write[0] = MENU_TOOL_LOCAL_COORDS; - tool_option_button[TOOL_OPT_LOCAL_COORDS]->connect("toggled", callable_mp(this, &Node3DEditor::_menu_item_toggled), button_binds); - tool_option_button[TOOL_OPT_LOCAL_COORDS]->set_shortcut(ED_SHORTCUT("spatial_editor/local_coords", TTR("Use Local Space"), KEY_T)); - - tool_option_button[TOOL_OPT_USE_SNAP] = memnew(ToolButton); - hbc_menu->add_child(tool_option_button[TOOL_OPT_USE_SNAP]); - tool_option_button[TOOL_OPT_USE_SNAP]->set_toggle_mode(true); - tool_option_button[TOOL_OPT_USE_SNAP]->set_flat(true); - button_binds.write[0] = MENU_TOOL_USE_SNAP; - tool_option_button[TOOL_OPT_USE_SNAP]->connect("toggled", callable_mp(this, &Node3DEditor::_menu_item_toggled), button_binds); - tool_option_button[TOOL_OPT_USE_SNAP]->set_shortcut(ED_SHORTCUT("spatial_editor/snap", TTR("Use Snap"), KEY_Y)); - - hbc_menu->add_child(memnew(VSeparator)); - - tool_option_button[TOOL_OPT_OVERRIDE_CAMERA] = memnew(ToolButton); - hbc_menu->add_child(tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]); - tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->set_toggle_mode(true); - tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->set_flat(true); - tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->set_disabled(true); - button_binds.write[0] = MENU_TOOL_OVERRIDE_CAMERA; - tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->connect("toggled", callable_mp(this, &Node3DEditor::_menu_item_toggled), button_binds); - _update_camera_override_button(false); - - hbc_menu->add_child(memnew(VSeparator)); - - // Drag and drop support; - preview_node = memnew(Node3D); - preview_bounds = AABB(); - - ED_SHORTCUT("spatial_editor/bottom_view", TTR("Bottom View"), KEY_MASK_ALT + KEY_KP_7); - ED_SHORTCUT("spatial_editor/top_view", TTR("Top View"), KEY_KP_7); - ED_SHORTCUT("spatial_editor/rear_view", TTR("Rear View"), KEY_MASK_ALT + KEY_KP_1); - ED_SHORTCUT("spatial_editor/front_view", TTR("Front View"), KEY_KP_1); - ED_SHORTCUT("spatial_editor/left_view", TTR("Left View"), KEY_MASK_ALT + KEY_KP_3); - ED_SHORTCUT("spatial_editor/right_view", TTR("Right View"), KEY_KP_3); - ED_SHORTCUT("spatial_editor/switch_perspective_orthogonal", TTR("Switch Perspective/Orthogonal View"), KEY_KP_5); - ED_SHORTCUT("spatial_editor/insert_anim_key", TTR("Insert Animation Key"), KEY_K); - ED_SHORTCUT("spatial_editor/focus_origin", TTR("Focus Origin"), KEY_O); - ED_SHORTCUT("spatial_editor/focus_selection", TTR("Focus Selection"), KEY_F); - ED_SHORTCUT("spatial_editor/align_transform_with_view", TTR("Align Transform with View"), KEY_MASK_ALT + KEY_MASK_CMD + KEY_M); - ED_SHORTCUT("spatial_editor/align_rotation_with_view", TTR("Align Rotation with View"), KEY_MASK_ALT + KEY_MASK_CMD + KEY_F); - ED_SHORTCUT("spatial_editor/freelook_toggle", TTR("Toggle Freelook"), KEY_MASK_SHIFT + KEY_F); - - PopupMenu *p; - - transform_menu = memnew(MenuButton); - transform_menu->set_text(TTR("Transform")); - transform_menu->set_switch_on_hover(true); - hbc_menu->add_child(transform_menu); - - p = transform_menu->get_popup(); - p->add_shortcut(ED_SHORTCUT("spatial_editor/snap_to_floor", TTR("Snap Object to Floor"), KEY_PAGEDOWN), MENU_SNAP_TO_FLOOR); - p->add_shortcut(ED_SHORTCUT("spatial_editor/transform_dialog", TTR("Transform Dialog...")), MENU_TRANSFORM_DIALOG); - - p->add_separator(); - p->add_shortcut(ED_SHORTCUT("spatial_editor/configure_snap", TTR("Configure Snap...")), MENU_TRANSFORM_CONFIGURE_SNAP); - - p->connect("id_pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed)); - - view_menu = memnew(MenuButton); - view_menu->set_text(TTR("View")); - view_menu->set_switch_on_hover(true); - hbc_menu->add_child(view_menu); - - p = view_menu->get_popup(); - - accept = memnew(AcceptDialog); - editor->get_gui_base()->add_child(accept); - - p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/1_viewport", TTR("1 Viewport"), KEY_MASK_CMD + KEY_1), MENU_VIEW_USE_1_VIEWPORT); - p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/2_viewports", TTR("2 Viewports"), KEY_MASK_CMD + KEY_2), MENU_VIEW_USE_2_VIEWPORTS); - p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/2_viewports_alt", TTR("2 Viewports (Alt)"), KEY_MASK_ALT + KEY_MASK_CMD + KEY_2), MENU_VIEW_USE_2_VIEWPORTS_ALT); - p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/3_viewports", TTR("3 Viewports"), KEY_MASK_CMD + KEY_3), MENU_VIEW_USE_3_VIEWPORTS); - p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/3_viewports_alt", TTR("3 Viewports (Alt)"), KEY_MASK_ALT + KEY_MASK_CMD + KEY_3), MENU_VIEW_USE_3_VIEWPORTS_ALT); - p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/4_viewports", TTR("4 Viewports"), KEY_MASK_CMD + KEY_4), MENU_VIEW_USE_4_VIEWPORTS); - p->add_separator(); - - p->add_submenu_item(TTR("Gizmos"), "GizmosMenu"); - - p->add_separator(); - p->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_origin", TTR("View Origin")), MENU_VIEW_ORIGIN); - p->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_grid", TTR("View Grid")), MENU_VIEW_GRID); - - p->add_separator(); - p->add_shortcut(ED_SHORTCUT("spatial_editor/settings", TTR("Settings...")), MENU_VIEW_CAMERA_SETTINGS); - - p->set_item_checked(p->get_item_index(MENU_VIEW_ORIGIN), true); - p->set_item_checked(p->get_item_index(MENU_VIEW_GRID), true); - - p->connect("id_pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed)); - - gizmos_menu = memnew(PopupMenu); - p->add_child(gizmos_menu); - gizmos_menu->set_name("GizmosMenu"); - gizmos_menu->set_hide_on_checkable_item_selection(false); - gizmos_menu->connect("id_pressed", callable_mp(this, &Node3DEditor::_menu_gizmo_toggled)); - - /* REST OF MENU */ - - palette_split = memnew(HSplitContainer); - palette_split->set_v_size_flags(SIZE_EXPAND_FILL); - vbc->add_child(palette_split); - - shader_split = memnew(VSplitContainer); - shader_split->set_h_size_flags(SIZE_EXPAND_FILL); - palette_split->add_child(shader_split); - viewport_base = memnew(Node3DEditorViewportContainer); - shader_split->add_child(viewport_base); - viewport_base->set_v_size_flags(SIZE_EXPAND_FILL); - for (uint32_t i = 0; i < VIEWPORTS_COUNT; i++) { - - viewports[i] = memnew(Node3DEditorViewport(this, editor, i)); - viewports[i]->connect("toggle_maximize_view", callable_mp(this, &Node3DEditor::_toggle_maximize_view)); - viewports[i]->connect("clicked", callable_mp(this, &Node3DEditor::_update_camera_override_viewport)); - viewports[i]->assign_pending_data_pointers(preview_node, &preview_bounds, accept); - viewport_base->add_child(viewports[i]); - } - - /* SNAP DIALOG */ - - snap_translate_value = 1; - snap_rotate_value = 15; - snap_scale_value = 10; - - snap_dialog = memnew(ConfirmationDialog); - snap_dialog->set_title(TTR("Snap Settings")); - add_child(snap_dialog); - snap_dialog->connect("confirmed", callable_mp(this, &Node3DEditor::_snap_changed)); - snap_dialog->get_cancel()->connect("pressed", callable_mp(this, &Node3DEditor::_snap_update)); - - VBoxContainer *snap_dialog_vbc = memnew(VBoxContainer); - snap_dialog->add_child(snap_dialog_vbc); - - snap_translate = memnew(LineEdit); - snap_dialog_vbc->add_margin_child(TTR("Translate Snap:"), snap_translate); - - snap_rotate = memnew(LineEdit); - snap_dialog_vbc->add_margin_child(TTR("Rotate Snap (deg.):"), snap_rotate); - - snap_scale = memnew(LineEdit); - snap_dialog_vbc->add_margin_child(TTR("Scale Snap (%):"), snap_scale); - - _snap_update(); - - /* SETTINGS DIALOG */ - - settings_dialog = memnew(ConfirmationDialog); - settings_dialog->set_title(TTR("Viewport Settings")); - add_child(settings_dialog); - settings_vbc = memnew(VBoxContainer); - settings_vbc->set_custom_minimum_size(Size2(200, 0) * EDSCALE); - settings_dialog->add_child(settings_vbc); - - settings_fov = memnew(SpinBox); - settings_fov->set_max(MAX_FOV); - settings_fov->set_min(MIN_FOV); - settings_fov->set_step(0.01); - settings_fov->set_value(EDITOR_DEF("editors/3d/default_fov", 70.0)); - settings_vbc->add_margin_child(TTR("Perspective FOV (deg.):"), settings_fov); - - settings_znear = memnew(SpinBox); - settings_znear->set_max(MAX_Z); - settings_znear->set_min(MIN_Z); - settings_znear->set_step(0.01); - settings_znear->set_value(EDITOR_DEF("editors/3d/default_z_near", 0.05)); - settings_vbc->add_margin_child(TTR("View Z-Near:"), settings_znear); - - settings_zfar = memnew(SpinBox); - settings_zfar->set_max(MAX_Z); - settings_zfar->set_min(MIN_Z); - settings_zfar->set_step(0.01); - settings_zfar->set_value(EDITOR_DEF("editors/3d/default_z_far", 1500)); - settings_vbc->add_margin_child(TTR("View Z-Far:"), settings_zfar); - - for (uint32_t i = 0; i < VIEWPORTS_COUNT; ++i) { - settings_dialog->connect("confirmed", callable_mp(viewports[i], &Node3DEditorViewport::_update_camera), varray(0.0)); - } - - /* XFORM DIALOG */ - - xform_dialog = memnew(ConfirmationDialog); - xform_dialog->set_title(TTR("Transform Change")); - add_child(xform_dialog); - - VBoxContainer *xform_vbc = memnew(VBoxContainer); - xform_dialog->add_child(xform_vbc); - - Label *l = memnew(Label); - l->set_text(TTR("Translate:")); - xform_vbc->add_child(l); - - HBoxContainer *xform_hbc = memnew(HBoxContainer); - xform_vbc->add_child(xform_hbc); - - for (int i = 0; i < 3; i++) { - - xform_translate[i] = memnew(LineEdit); - xform_translate[i]->set_h_size_flags(SIZE_EXPAND_FILL); - xform_hbc->add_child(xform_translate[i]); - } - - l = memnew(Label); - l->set_text(TTR("Rotate (deg.):")); - xform_vbc->add_child(l); - - xform_hbc = memnew(HBoxContainer); - xform_vbc->add_child(xform_hbc); - - for (int i = 0; i < 3; i++) { - xform_rotate[i] = memnew(LineEdit); - xform_rotate[i]->set_h_size_flags(SIZE_EXPAND_FILL); - xform_hbc->add_child(xform_rotate[i]); - } - - l = memnew(Label); - l->set_text(TTR("Scale (ratio):")); - xform_vbc->add_child(l); - - xform_hbc = memnew(HBoxContainer); - xform_vbc->add_child(xform_hbc); - - for (int i = 0; i < 3; i++) { - xform_scale[i] = memnew(LineEdit); - xform_scale[i]->set_h_size_flags(SIZE_EXPAND_FILL); - xform_hbc->add_child(xform_scale[i]); - } - - l = memnew(Label); - l->set_text(TTR("Transform Type")); - xform_vbc->add_child(l); - - xform_type = memnew(OptionButton); - xform_type->set_h_size_flags(SIZE_EXPAND_FILL); - xform_type->add_item(TTR("Pre")); - xform_type->add_item(TTR("Post")); - xform_vbc->add_child(xform_type); - - xform_dialog->connect("confirmed", callable_mp(this, &Node3DEditor::_xform_dialog_action)); - - scenario_debug = VisualServer::SCENARIO_DEBUG_DISABLED; - - selected = NULL; - - set_process_unhandled_key_input(true); - add_to_group("_spatial_editor_group"); - - EDITOR_DEF("editors/3d/manipulator_gizmo_size", 80); - EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "editors/3d/manipulator_gizmo_size", PROPERTY_HINT_RANGE, "16,1024,1")); - EDITOR_DEF("editors/3d/manipulator_gizmo_opacity", 0.4); - EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::FLOAT, "editors/3d/manipulator_gizmo_opacity", PROPERTY_HINT_RANGE, "0,1,0.01")); - EDITOR_DEF("editors/3d/navigation/show_viewport_rotation_gizmo", true); - - over_gizmo_handle = -1; -} - -Node3DEditor::~Node3DEditor() { - memdelete(preview_node); -} - -void Node3DEditorPlugin::make_visible(bool p_visible) { - - if (p_visible) { - - spatial_editor->show(); - spatial_editor->set_process(true); - - } else { - - spatial_editor->hide(); - spatial_editor->set_process(false); - } -} -void Node3DEditorPlugin::edit(Object *p_object) { - - spatial_editor->edit(Object::cast_to(p_object)); -} - -bool Node3DEditorPlugin::handles(Object *p_object) const { - - return p_object->is_class("Node3D"); -} - -Dictionary Node3DEditorPlugin::get_state() const { - return spatial_editor->get_state(); -} - -void Node3DEditorPlugin::set_state(const Dictionary &p_state) { - - spatial_editor->set_state(p_state); -} - -void Node3DEditor::snap_cursor_to_plane(const Plane &p_plane) { - - //cursor.pos=p_plane.project(cursor.pos); -} - -Vector3 Node3DEditor::snap_point(Vector3 p_target, Vector3 p_start) const { - if (is_snap_enabled()) { - p_target.x = Math::snap_scalar(0.0, get_translate_snap(), p_target.x); - p_target.y = Math::snap_scalar(0.0, get_translate_snap(), p_target.y); - p_target.z = Math::snap_scalar(0.0, get_translate_snap(), p_target.z); - } - return p_target; -} - -float Node3DEditor::get_translate_snap() const { - float snap_value; - if (InputFilter::get_singleton()->is_key_pressed(KEY_SHIFT)) { - snap_value = snap_translate->get_text().to_double() / 10.0; - } else { - snap_value = snap_translate->get_text().to_double(); - } - - return snap_value; -} - -float Node3DEditor::get_rotate_snap() const { - float snap_value; - if (InputFilter::get_singleton()->is_key_pressed(KEY_SHIFT)) { - snap_value = snap_rotate->get_text().to_double() / 3.0; - } else { - snap_value = snap_rotate->get_text().to_double(); - } - - return snap_value; -} - -float Node3DEditor::get_scale_snap() const { - float snap_value; - if (InputFilter::get_singleton()->is_key_pressed(KEY_SHIFT)) { - snap_value = snap_scale->get_text().to_double() / 2.0; - } else { - snap_value = snap_scale->get_text().to_double(); - } - - return snap_value; -} - -void Node3DEditorPlugin::_bind_methods() { - - ClassDB::bind_method("snap_cursor_to_plane", &Node3DEditorPlugin::snap_cursor_to_plane); -} - -void Node3DEditorPlugin::snap_cursor_to_plane(const Plane &p_plane) { - - spatial_editor->snap_cursor_to_plane(p_plane); -} - -struct _GizmoPluginPriorityComparator { - - bool operator()(const Ref &p_a, const Ref &p_b) const { - if (p_a->get_priority() == p_b->get_priority()) { - return p_a->get_name() < p_b->get_name(); - } - return p_a->get_priority() > p_b->get_priority(); - } -}; - -struct _GizmoPluginNameComparator { - - bool operator()(const Ref &p_a, const Ref &p_b) const { - return p_a->get_name() < p_b->get_name(); - } -}; - -void Node3DEditor::add_gizmo_plugin(Ref p_plugin) { - ERR_FAIL_NULL(p_plugin.ptr()); - - gizmo_plugins_by_priority.push_back(p_plugin); - gizmo_plugins_by_priority.sort_custom<_GizmoPluginPriorityComparator>(); - - gizmo_plugins_by_name.push_back(p_plugin); - gizmo_plugins_by_name.sort_custom<_GizmoPluginNameComparator>(); - - _update_gizmos_menu(); - Node3DEditor::get_singleton()->update_all_gizmos(); -} - -void Node3DEditor::remove_gizmo_plugin(Ref p_plugin) { - gizmo_plugins_by_priority.erase(p_plugin); - gizmo_plugins_by_name.erase(p_plugin); - _update_gizmos_menu(); -} - -Node3DEditorPlugin::Node3DEditorPlugin(EditorNode *p_node) { - - editor = p_node; - spatial_editor = memnew(Node3DEditor(p_node)); - spatial_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL); - editor->get_viewport()->add_child(spatial_editor); - - spatial_editor->hide(); - spatial_editor->connect_compat("transform_key_request", editor->get_inspector_dock(), "_transform_keyed"); -} - -Node3DEditorPlugin::~Node3DEditorPlugin() { -} - -void EditorNode3DGizmoPlugin::create_material(const String &p_name, const Color &p_color, bool p_billboard, bool p_on_top, bool p_use_vertex_color) { - - Color instanced_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/instanced", Color(0.7, 0.7, 0.7, 0.6)); - - Vector> mats; - - for (int i = 0; i < 4; i++) { - bool selected = i % 2 == 1; - bool instanced = i < 2; - - Ref material = Ref(memnew(StandardMaterial3D)); - - Color color = instanced ? instanced_color : p_color; - - if (!selected) { - color.a *= 0.3; - } - - material->set_albedo(color); - material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); - material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); - material->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MIN + 1); - - if (p_use_vertex_color) { - material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); - material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); - } - - if (p_billboard) { - material->set_billboard_mode(StandardMaterial3D::BILLBOARD_ENABLED); - } - - if (p_on_top && selected) { - material->set_on_top_of_alpha(); - } - - mats.push_back(material); - } - - materials[p_name] = mats; -} - -void EditorNode3DGizmoPlugin::create_icon_material(const String &p_name, const Ref &p_texture, bool p_on_top, const Color &p_albedo) { - - Color instanced_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/instanced", Color(0.7, 0.7, 0.7, 0.6)); - - Vector> icons; - - for (int i = 0; i < 4; i++) { - bool selected = i % 2 == 1; - bool instanced = i < 2; - - Ref icon = Ref(memnew(StandardMaterial3D)); - - Color color = instanced ? instanced_color : p_albedo; - - if (!selected) { - color.a *= 0.85; - } - - icon->set_albedo(color); - - icon->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); - icon->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); - icon->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); - icon->set_cull_mode(StandardMaterial3D::CULL_DISABLED); - icon->set_depth_draw_mode(StandardMaterial3D::DEPTH_DRAW_DISABLED); - icon->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); - icon->set_texture(StandardMaterial3D::TEXTURE_ALBEDO, p_texture); - icon->set_flag(StandardMaterial3D::FLAG_FIXED_SIZE, true); - icon->set_billboard_mode(StandardMaterial3D::BILLBOARD_ENABLED); - icon->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MIN); - - if (p_on_top && selected) { - icon->set_on_top_of_alpha(); - } - - icons.push_back(icon); - } - - materials[p_name] = icons; -} - -void EditorNode3DGizmoPlugin::create_handle_material(const String &p_name, bool p_billboard) { - Ref handle_material = Ref(memnew(StandardMaterial3D)); - - handle_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); - handle_material->set_flag(StandardMaterial3D::FLAG_USE_POINT_SIZE, true); - Ref handle_t = Node3DEditor::get_singleton()->get_theme_icon("Editor3DHandle", "EditorIcons"); - handle_material->set_point_size(handle_t->get_width()); - handle_material->set_texture(StandardMaterial3D::TEXTURE_ALBEDO, handle_t); - handle_material->set_albedo(Color(1, 1, 1)); - handle_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); - handle_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); - handle_material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); - handle_material->set_on_top_of_alpha(); - if (p_billboard) { - handle_material->set_billboard_mode(StandardMaterial3D::BILLBOARD_ENABLED); - handle_material->set_on_top_of_alpha(); - } - - materials[p_name] = Vector>(); - materials[p_name].push_back(handle_material); -} - -void EditorNode3DGizmoPlugin::add_material(const String &p_name, Ref p_material) { - materials[p_name] = Vector>(); - materials[p_name].push_back(p_material); -} - -Ref EditorNode3DGizmoPlugin::get_material(const String &p_name, const Ref &p_gizmo) { - ERR_FAIL_COND_V(!materials.has(p_name), Ref()); - ERR_FAIL_COND_V(materials[p_name].size() == 0, Ref()); - - if (p_gizmo.is_null() || materials[p_name].size() == 1) return materials[p_name][0]; - - int index = (p_gizmo->is_selected() ? 1 : 0) + (p_gizmo->is_editable() ? 2 : 0); - - Ref mat = materials[p_name][index]; - - if (current_state == ON_TOP && p_gizmo->is_selected()) { - mat->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true); - } else { - mat->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, false); - } - - return mat; -} - -String EditorNode3DGizmoPlugin::get_name() const { - if (get_script_instance() && get_script_instance()->has_method("get_name")) { - return get_script_instance()->call("get_name"); - } - return TTR("Nameless gizmo"); -} - -int EditorNode3DGizmoPlugin::get_priority() const { - if (get_script_instance() && get_script_instance()->has_method("get_priority")) { - return get_script_instance()->call("get_priority"); - } - return 0; -} - -Ref EditorNode3DGizmoPlugin::get_gizmo(Node3D *p_spatial) { - - if (get_script_instance() && get_script_instance()->has_method("get_gizmo")) { - return get_script_instance()->call("get_gizmo", p_spatial); - } - - Ref ref = create_gizmo(p_spatial); - - if (ref.is_null()) return ref; - - ref->set_plugin(this); - ref->set_spatial_node(p_spatial); - ref->set_hidden(current_state == HIDDEN); - - current_gizmos.push_back(ref.ptr()); - return ref; -} - -void EditorNode3DGizmoPlugin::_bind_methods() { -#define GIZMO_REF PropertyInfo(Variant::OBJECT, "gizmo", PROPERTY_HINT_RESOURCE_TYPE, "EditorNode3DGizmo") - - BIND_VMETHOD(MethodInfo(Variant::BOOL, "has_gizmo", PropertyInfo(Variant::OBJECT, "spatial", PROPERTY_HINT_RESOURCE_TYPE, "Node3D"))); - BIND_VMETHOD(MethodInfo(GIZMO_REF, "create_gizmo", PropertyInfo(Variant::OBJECT, "spatial", PROPERTY_HINT_RESOURCE_TYPE, "Node3D"))); - - ClassDB::bind_method(D_METHOD("create_material", "name", "color", "billboard", "on_top", "use_vertex_color"), &EditorNode3DGizmoPlugin::create_material, DEFVAL(false), DEFVAL(false), DEFVAL(false)); - ClassDB::bind_method(D_METHOD("create_icon_material", "name", "texture", "on_top", "color"), &EditorNode3DGizmoPlugin::create_icon_material, DEFVAL(false), DEFVAL(Color(1, 1, 1, 1))); - ClassDB::bind_method(D_METHOD("create_handle_material", "name", "billboard"), &EditorNode3DGizmoPlugin::create_handle_material, DEFVAL(false)); - ClassDB::bind_method(D_METHOD("add_material", "name", "material"), &EditorNode3DGizmoPlugin::add_material); - - ClassDB::bind_method(D_METHOD("get_material", "name", "gizmo"), &EditorNode3DGizmoPlugin::get_material); //, DEFVAL(Ref())); - - BIND_VMETHOD(MethodInfo(Variant::STRING, "get_name")); - BIND_VMETHOD(MethodInfo(Variant::STRING, "get_priority")); - BIND_VMETHOD(MethodInfo(Variant::BOOL, "can_be_hidden")); - BIND_VMETHOD(MethodInfo(Variant::BOOL, "is_selectable_when_hidden")); - - BIND_VMETHOD(MethodInfo("redraw", GIZMO_REF)); - BIND_VMETHOD(MethodInfo(Variant::STRING, "get_handle_name", GIZMO_REF, PropertyInfo(Variant::INT, "index"))); - - MethodInfo hvget(Variant::NIL, "get_handle_value", GIZMO_REF, PropertyInfo(Variant::INT, "index")); - hvget.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; - BIND_VMETHOD(hvget); - - BIND_VMETHOD(MethodInfo("set_handle", GIZMO_REF, PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Camera3D"), PropertyInfo(Variant::VECTOR2, "point"))); - MethodInfo cm = MethodInfo("commit_handle", GIZMO_REF, PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::NIL, "restore"), PropertyInfo(Variant::BOOL, "cancel")); - cm.default_arguments.push_back(false); - BIND_VMETHOD(cm); - - BIND_VMETHOD(MethodInfo(Variant::BOOL, "is_handle_highlighted", GIZMO_REF, PropertyInfo(Variant::INT, "index"))); - -#undef GIZMO_REF -} - -bool EditorNode3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - if (get_script_instance() && get_script_instance()->has_method("has_gizmo")) { - return get_script_instance()->call("has_gizmo", p_spatial); - } - return false; -} - -Ref EditorNode3DGizmoPlugin::create_gizmo(Node3D *p_spatial) { - - if (get_script_instance() && get_script_instance()->has_method("create_gizmo")) { - return get_script_instance()->call("create_gizmo", p_spatial); - } - - Ref ref; - if (has_gizmo(p_spatial)) ref.instance(); - return ref; -} - -bool EditorNode3DGizmoPlugin::can_be_hidden() const { - if (get_script_instance() && get_script_instance()->has_method("can_be_hidden")) { - return get_script_instance()->call("can_be_hidden"); - } - return true; -} - -bool EditorNode3DGizmoPlugin::is_selectable_when_hidden() const { - if (get_script_instance() && get_script_instance()->has_method("is_selectable_when_hidden")) { - return get_script_instance()->call("is_selectable_when_hidden"); - } - return false; -} - -void EditorNode3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - if (get_script_instance() && get_script_instance()->has_method("redraw")) { - Ref ref(p_gizmo); - get_script_instance()->call("redraw", ref); - } -} - -String EditorNode3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { - if (get_script_instance() && get_script_instance()->has_method("get_handle_name")) { - return get_script_instance()->call("get_handle_name", p_gizmo, p_idx); - } - return ""; -} - -Variant EditorNode3DGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { - if (get_script_instance() && get_script_instance()->has_method("get_handle_value")) { - return get_script_instance()->call("get_handle_value", p_gizmo, p_idx); - } - return Variant(); -} - -void EditorNode3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) { - if (get_script_instance() && get_script_instance()->has_method("set_handle")) { - get_script_instance()->call("set_handle", p_gizmo, p_idx, p_camera, p_point); - } -} - -void EditorNode3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { - if (get_script_instance() && get_script_instance()->has_method("commit_handle")) { - get_script_instance()->call("commit_handle", p_gizmo, p_idx, p_restore, p_cancel); - } -} - -bool EditorNode3DGizmoPlugin::is_handle_highlighted(const EditorNode3DGizmo *p_gizmo, int p_idx) const { - if (get_script_instance() && get_script_instance()->has_method("is_handle_highlighted")) { - return get_script_instance()->call("is_handle_highlighted", p_gizmo, p_idx); - } - return false; -} - -void EditorNode3DGizmoPlugin::set_state(int p_state) { - current_state = p_state; - for (int i = 0; i < current_gizmos.size(); ++i) { - current_gizmos[i]->set_hidden(current_state == HIDDEN); - } -} - -int EditorNode3DGizmoPlugin::get_state() const { - return current_state; -} - -void EditorNode3DGizmoPlugin::unregister_gizmo(EditorNode3DGizmo *p_gizmo) { - current_gizmos.erase(p_gizmo); -} - -EditorNode3DGizmoPlugin::EditorNode3DGizmoPlugin() { - current_state = VISIBLE; -} - -EditorNode3DGizmoPlugin::~EditorNode3DGizmoPlugin() { - for (int i = 0; i < current_gizmos.size(); ++i) { - current_gizmos[i]->set_plugin(NULL); - current_gizmos[i]->get_spatial_node()->set_gizmo(NULL); - } - if (Node3DEditor::get_singleton()) { - Node3DEditor::get_singleton()->update_all_gizmos(); - } -} diff --git a/editor/plugins/spatial_editor_plugin.h b/editor/plugins/spatial_editor_plugin.h deleted file mode 100644 index 819178408b..0000000000 --- a/editor/plugins/spatial_editor_plugin.h +++ /dev/null @@ -1,883 +0,0 @@ -/*************************************************************************/ -/* spatial_editor_plugin.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef SPATIAL_EDITOR_PLUGIN_H -#define SPATIAL_EDITOR_PLUGIN_H - -#include "editor/editor_node.h" -#include "editor/editor_plugin.h" -#include "editor/editor_scale.h" -#include "scene/3d/immediate_geometry_3d.h" -#include "scene/3d/light_3d.h" -#include "scene/3d/visual_instance_3d.h" -#include "scene/gui/panel_container.h" - -class Camera3D; -class Node3DEditor; -class EditorNode3DGizmoPlugin; -class Node3DEditorViewport; -class ViewportContainer; - -class EditorNode3DGizmo : public Node3DGizmo { - - GDCLASS(EditorNode3DGizmo, Node3DGizmo); - - bool selected; - bool instanced; - -public: - void set_selected(bool p_selected) { selected = p_selected; } - bool is_selected() const { return selected; } - - struct Instance { - - RID instance; - Ref mesh; - Ref material; - Ref skin_reference; - RID skeleton; - bool billboard; - bool unscaled; - bool can_intersect; - bool extra_margin; - Instance() { - - billboard = false; - unscaled = false; - can_intersect = false; - extra_margin = false; - } - - void create_instance(Node3D *p_base, bool p_hidden = false); - }; - - Vector collision_segments; - Ref collision_mesh; - - struct Handle { - Vector3 pos; - bool billboard; - }; - - Vector handles; - Vector secondary_handles; - float selectable_icon_size; - bool billboard_handle; - - bool valid; - bool hidden; - Node3D *base; - Vector instances; - Node3D *spatial_node; - EditorNode3DGizmoPlugin *gizmo_plugin; - - void _set_spatial_node(Node *p_node) { set_spatial_node(Object::cast_to(p_node)); } - -protected: - static void _bind_methods(); - -public: - void add_lines(const Vector &p_lines, const Ref &p_material, bool p_billboard = false, const Color &p_modulate = Color(1, 1, 1)); - void add_mesh(const Ref &p_mesh, bool p_billboard = false, const Ref &p_skin_reference = Ref(), const Ref &p_material = Ref()); - void add_collision_segments(const Vector &p_lines); - void add_collision_triangles(const Ref &p_tmesh); - void add_unscaled_billboard(const Ref &p_material, float p_scale = 1, const Color &p_modulate = Color(1, 1, 1)); - void add_handles(const Vector &p_handles, const Ref &p_material, bool p_billboard = false, bool p_secondary = false); - void add_solid_box(Ref &p_material, Vector3 p_size, Vector3 p_position = Vector3()); - - virtual bool is_handle_highlighted(int p_idx) const; - virtual String get_handle_name(int p_idx) const; - virtual Variant get_handle_value(int p_idx); - virtual void set_handle(int p_idx, Camera3D *p_camera, const Point2 &p_point); - virtual void commit_handle(int p_idx, const Variant &p_restore, bool p_cancel = false); - - void set_spatial_node(Node3D *p_node); - Node3D *get_spatial_node() const { return spatial_node; } - Ref get_plugin() const { return gizmo_plugin; } - Vector3 get_handle_pos(int p_idx) const; - bool intersect_frustum(const Camera3D *p_camera, const Vector &p_frustum); - bool intersect_ray(Camera3D *p_camera, const Point2 &p_point, Vector3 &r_pos, Vector3 &r_normal, int *r_gizmo_handle = NULL, bool p_sec_first = false); - - virtual void clear(); - virtual void create(); - virtual void transform(); - virtual void redraw(); - virtual void free(); - - virtual bool is_editable() const; - - void set_hidden(bool p_hidden); - void set_plugin(EditorNode3DGizmoPlugin *p_plugin); - - EditorNode3DGizmo(); - ~EditorNode3DGizmo(); -}; - -class ViewportRotationControl : public Control { - GDCLASS(ViewportRotationControl, Control); - - struct Axis2D { - Vector2i screen_point; - float z_axis = -99.0; - int axis = -1; - }; - - struct Axis2DCompare { - _FORCE_INLINE_ bool operator()(const Axis2D &l, const Axis2D &r) const { - return l.z_axis < r.z_axis; - } - }; - - Node3DEditorViewport *viewport = nullptr; - Vector axis_colors; - Vector axis_menu_options; - bool orbiting = false; - int focused_axis = -2; - - const float AXIS_CIRCLE_RADIUS = 8.0f * EDSCALE; - -protected: - static void _bind_methods(); - void _notification(int p_what); - void _gui_input(Ref p_event); - void _draw(); - void _draw_axis(const Axis2D &p_axis); - void _get_sorted_axis(Vector &r_axis); - void _update_focus(); - void _on_mouse_exited(); - -public: - void set_viewport(Node3DEditorViewport *p_viewport); -}; - -class Node3DEditorViewport : public Control { - - GDCLASS(Node3DEditorViewport, Control); - friend class Node3DEditor; - friend class ViewportRotationControl; - enum { - - VIEW_TOP, - VIEW_BOTTOM, - VIEW_LEFT, - VIEW_RIGHT, - VIEW_FRONT, - VIEW_REAR, - VIEW_CENTER_TO_ORIGIN, - VIEW_CENTER_TO_SELECTION, - VIEW_ALIGN_TRANSFORM_WITH_VIEW, - VIEW_ALIGN_ROTATION_WITH_VIEW, - VIEW_PERSPECTIVE, - VIEW_ENVIRONMENT, - VIEW_ORTHOGONAL, - VIEW_HALF_RESOLUTION, - VIEW_AUDIO_LISTENER, - VIEW_AUDIO_DOPPLER, - VIEW_GIZMOS, - VIEW_INFORMATION, - VIEW_FPS, - VIEW_DISPLAY_NORMAL, - VIEW_DISPLAY_WIREFRAME, - VIEW_DISPLAY_OVERDRAW, - VIEW_DISPLAY_SHADELESS, - VIEW_DISPLAY_LIGHTING, - VIEW_DISPLAY_ADVANCED, - VIEW_DISPLAY_NORMAL_BUFFER, - VIEW_DISPLAY_DEBUG_SHADOW_ATLAS, - VIEW_DISPLAY_DEBUG_DIRECTIONAL_SHADOW_ATLAS, - VIEW_DISPLAY_DEBUG_GIPROBE_ALBEDO, - VIEW_DISPLAY_DEBUG_GIPROBE_LIGHTING, - VIEW_DISPLAY_DEBUG_GIPROBE_EMISSION, - VIEW_DISPLAY_DEBUG_SCENE_LUMINANCE, - VIEW_DISPLAY_DEBUG_SSAO, - VIEW_DISPLAY_DEBUG_ROUGHNESS_LIMITER, - VIEW_LOCK_ROTATION, - VIEW_CINEMATIC_PREVIEW, - VIEW_AUTO_ORTHOGONAL, - VIEW_MAX - }; - -public: - enum { - GIZMO_BASE_LAYER = 27, - GIZMO_EDIT_LAYER = 26, - GIZMO_GRID_LAYER = 25 - }; - - enum NavigationScheme { - NAVIGATION_GODOT, - NAVIGATION_MAYA, - NAVIGATION_MODO, - }; - -private: - int index; - String name; - void _menu_option(int p_option); - void _set_auto_orthogonal(); - Node3D *preview_node; - AABB *preview_bounds; - Vector selected_files; - AcceptDialog *accept; - - Node *target_node; - Point2 drop_pos; - - EditorNode *editor; - EditorData *editor_data; - EditorSelection *editor_selection; - UndoRedo *undo_redo; - - CheckBox *preview_camera; - ViewportContainer *viewport_container; - - MenuButton *view_menu; - PopupMenu *display_submenu; - - Control *surface; - SubViewport *viewport; - Camera3D *camera; - bool transforming; - bool orthogonal; - bool auto_orthogonal; - bool lock_rotation; - float gizmo_scale; - - bool freelook_active; - real_t freelook_speed; - - TextureRect *crosshair; - Label *info_label; - Label *cinema_label; - Label *locked_label; - - VBoxContainer *top_right_vbox; - ViewportRotationControl *rotation_control; - Label *fps_label; - - struct _RayResult { - - Node3D *item; - float depth; - int handle; - _FORCE_INLINE_ bool operator<(const _RayResult &p_rr) const { return depth < p_rr.depth; } - }; - - void _update_name(); - void _compute_edit(const Point2 &p_point); - void _clear_selected(); - void _select_clicked(bool p_append, bool p_single, bool p_allow_locked = false); - void _select(Node *p_node, bool p_append, bool p_single); - ObjectID _select_ray(const Point2 &p_pos, bool p_append, bool &r_includes_current, int *r_gizmo_handle = NULL, bool p_alt_select = false); - void _find_items_at_pos(const Point2 &p_pos, bool &r_includes_current, Vector<_RayResult> &results, bool p_alt_select = false); - Vector3 _get_ray_pos(const Vector2 &p_pos) const; - Vector3 _get_ray(const Vector2 &p_pos) const; - Point2 _point_to_screen(const Vector3 &p_point); - Transform _get_camera_transform() const; - int get_selected_count() const; - - Vector3 _get_camera_position() const; - Vector3 _get_camera_normal() const; - Vector3 _get_screen_to_space(const Vector3 &p_vector3); - - void _select_region(); - bool _gizmo_select(const Vector2 &p_screenpos, bool p_highlight_only = false); - - void _nav_pan(Ref p_event, const Vector2 &p_relative); - void _nav_zoom(Ref p_event, const Vector2 &p_relative); - void _nav_orbit(Ref p_event, const Vector2 &p_relative); - void _nav_look(Ref p_event, const Vector2 &p_relative); - - float get_znear() const; - float get_zfar() const; - float get_fov() const; - - ObjectID clicked; - Vector<_RayResult> selection_results; - bool clicked_includes_current; - bool clicked_wants_append; - - PopupMenu *selection_menu; - - enum NavigationZoomStyle { - NAVIGATION_ZOOM_VERTICAL, - NAVIGATION_ZOOM_HORIZONTAL - }; - - enum NavigationMode { - NAVIGATION_NONE, - NAVIGATION_PAN, - NAVIGATION_ZOOM, - NAVIGATION_ORBIT, - NAVIGATION_LOOK - }; - enum TransformMode { - TRANSFORM_NONE, - TRANSFORM_ROTATE, - TRANSFORM_TRANSLATE, - TRANSFORM_SCALE - - }; - enum TransformPlane { - TRANSFORM_VIEW, - TRANSFORM_X_AXIS, - TRANSFORM_Y_AXIS, - TRANSFORM_Z_AXIS, - TRANSFORM_YZ, - TRANSFORM_XZ, - TRANSFORM_XY, - }; - - struct EditData { - TransformMode mode; - TransformPlane plane; - Transform original; - Vector3 click_ray; - Vector3 click_ray_pos; - Vector3 center; - Vector3 orig_gizmo_pos; - int edited_gizmo; - Point2 mouse_pos; - bool snap; - Ref gizmo; - int gizmo_handle; - Variant gizmo_initial_value; - Vector3 gizmo_initial_pos; - } _edit; - - struct Cursor { - - Vector3 pos; - float x_rot, y_rot, distance; - Vector3 eye_pos; // Used in freelook mode - bool region_select; - Point2 region_begin, region_end; - - Cursor() { - x_rot = y_rot = 0.5; - distance = 4; - region_select = false; - } - }; - // Viewport camera supports movement smoothing, - // so one cursor is the real cursor, while the other can be an interpolated version. - Cursor cursor; // Immediate cursor - Cursor camera_cursor; // That one may be interpolated (don't modify this one except for smoothing purposes) - - void scale_cursor_distance(real_t scale); - - void set_freelook_active(bool active_now); - void scale_freelook_speed(real_t scale); - - real_t zoom_indicator_delay; - - RID move_gizmo_instance[3], move_plane_gizmo_instance[3], rotate_gizmo_instance[3], scale_gizmo_instance[3], scale_plane_gizmo_instance[3]; - - String last_message; - String message; - float message_time; - - void set_message(String p_message, float p_time = 5); - - // - void _update_camera(float p_interp_delta); - Transform to_camera_transform(const Cursor &p_cursor) const; - void _draw(); - - void _surface_mouse_enter(); - void _surface_mouse_exit(); - void _surface_focus_enter(); - void _surface_focus_exit(); - - void _sinput(const Ref &p_event); - void _update_freelook(real_t delta); - Node3DEditor *spatial_editor; - - Camera3D *previewing; - Camera3D *preview; - - bool previewing_cinema; - bool _is_node_locked(const Node *p_node); - void _preview_exited_scene(); - void _toggle_camera_preview(bool); - void _toggle_cinema_preview(bool); - void _init_gizmo_instance(int p_idx); - void _finish_gizmo_instances(); - void _selection_result_pressed(int); - void _selection_menu_hide(); - void _list_select(Ref b); - Point2i _get_warped_mouse_motion(const Ref &p_ev_mouse_motion) const; - - Vector3 _get_instance_position(const Point2 &p_pos) const; - static AABB _calculate_spatial_bounds(const Node3D *p_parent, bool p_exclude_toplevel_transform = true); - void _create_preview(const Vector &files) const; - void _remove_preview(); - bool _cyclical_dependency_exists(const String &p_target_scene_path, Node *p_desired_node); - bool _create_instance(Node *parent, String &path, const Point2 &p_point); - void _perform_drop_data(); - - bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const; - void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from); - -protected: - void _notification(int p_what); - static void _bind_methods(); - -public: - void update_surface() { surface->update(); } - void update_transform_gizmo_view(); - - void set_can_preview(Camera3D *p_preview); - void set_state(const Dictionary &p_state); - Dictionary get_state() const; - void reset(); - bool is_freelook_active() const { return freelook_active; } - - void focus_selection(); - - void assign_pending_data_pointers( - Node3D *p_preview_node, - AABB *p_preview_bounds, - AcceptDialog *p_accept); - - SubViewport *get_viewport_node() { return viewport; } - Camera3D *get_camera() { return camera; } // return the default camera object. - - Node3DEditorViewport(Node3DEditor *p_spatial_editor, EditorNode *p_editor, int p_index); -}; - -class Node3DEditorSelectedItem : public Object { - - GDCLASS(Node3DEditorSelectedItem, Object); - -public: - AABB aabb; - Transform original; // original location when moving - Transform original_local; - Transform last_xform; // last transform - bool last_xform_dirty; - Node3D *sp; - RID sbox_instance; - - Node3DEditorSelectedItem() { - sp = NULL; - last_xform_dirty = true; - } - ~Node3DEditorSelectedItem(); -}; - -class Node3DEditorViewportContainer : public Container { - - GDCLASS(Node3DEditorViewportContainer, Container); - -public: - enum View { - VIEW_USE_1_VIEWPORT, - VIEW_USE_2_VIEWPORTS, - VIEW_USE_2_VIEWPORTS_ALT, - VIEW_USE_3_VIEWPORTS, - VIEW_USE_3_VIEWPORTS_ALT, - VIEW_USE_4_VIEWPORTS, - }; - -private: - View view; - bool mouseover; - float ratio_h; - float ratio_v; - - bool hovering_v; - bool hovering_h; - - bool dragging_v; - bool dragging_h; - Vector2 drag_begin_pos; - Vector2 drag_begin_ratio; - - void _gui_input(const Ref &p_event); - -protected: - void _notification(int p_what); - static void _bind_methods(); - -public: - void set_view(View p_view); - View get_view(); - - Node3DEditorViewportContainer(); -}; - -class Node3DEditor : public VBoxContainer { - - GDCLASS(Node3DEditor, VBoxContainer); - -public: - static const unsigned int VIEWPORTS_COUNT = 4; - - enum ToolMode { - - TOOL_MODE_SELECT, - TOOL_MODE_MOVE, - TOOL_MODE_ROTATE, - TOOL_MODE_SCALE, - TOOL_MODE_LIST_SELECT, - TOOL_LOCK_SELECTED, - TOOL_UNLOCK_SELECTED, - TOOL_GROUP_SELECTED, - TOOL_UNGROUP_SELECTED, - TOOL_MAX - }; - - enum ToolOptions { - - TOOL_OPT_LOCAL_COORDS, - TOOL_OPT_USE_SNAP, - TOOL_OPT_OVERRIDE_CAMERA, - TOOL_OPT_MAX - - }; - -private: - EditorNode *editor; - EditorSelection *editor_selection; - - Node3DEditorViewportContainer *viewport_base; - Node3DEditorViewport *viewports[VIEWPORTS_COUNT]; - VSplitContainer *shader_split; - HSplitContainer *palette_split; - - ///// - - ToolMode tool_mode; - bool orthogonal; - - VisualServer::ScenarioDebugMode scenario_debug; - - RID origin; - RID origin_instance; - bool origin_enabled; - RID grid[3]; - RID grid_instance[3]; - bool grid_visible[3]; //currently visible - bool grid_enable[3]; //should be always visible if true - bool grid_enabled; - - Ref move_gizmo[3], move_plane_gizmo[3], rotate_gizmo[3], scale_gizmo[3], scale_plane_gizmo[3]; - Ref gizmo_color[3]; - Ref plane_gizmo_color[3]; - Ref gizmo_color_hl[3]; - Ref plane_gizmo_color_hl[3]; - - int over_gizmo_handle; - float snap_translate_value; - float snap_rotate_value; - float snap_scale_value; - - Ref selection_box; - RID indicators; - RID indicators_instance; - RID cursor_mesh; - RID cursor_instance; - Ref indicator_mat; - Ref cursor_material; - - // Scene drag and drop support - Node3D *preview_node; - AABB preview_bounds; - - struct Gizmo { - - bool visible; - float scale; - Transform transform; - } gizmo; - - enum MenuOption { - - MENU_TOOL_SELECT, - MENU_TOOL_MOVE, - MENU_TOOL_ROTATE, - MENU_TOOL_SCALE, - MENU_TOOL_LIST_SELECT, - MENU_TOOL_LOCAL_COORDS, - MENU_TOOL_USE_SNAP, - MENU_TOOL_OVERRIDE_CAMERA, - MENU_TRANSFORM_CONFIGURE_SNAP, - MENU_TRANSFORM_DIALOG, - MENU_VIEW_USE_1_VIEWPORT, - MENU_VIEW_USE_2_VIEWPORTS, - MENU_VIEW_USE_2_VIEWPORTS_ALT, - MENU_VIEW_USE_3_VIEWPORTS, - MENU_VIEW_USE_3_VIEWPORTS_ALT, - MENU_VIEW_USE_4_VIEWPORTS, - MENU_VIEW_ORIGIN, - MENU_VIEW_GRID, - MENU_VIEW_GIZMOS_3D_ICONS, - MENU_VIEW_CAMERA_SETTINGS, - MENU_LOCK_SELECTED, - MENU_UNLOCK_SELECTED, - MENU_GROUP_SELECTED, - MENU_UNGROUP_SELECTED, - MENU_SNAP_TO_FLOOR - }; - - Button *tool_button[TOOL_MAX]; - Button *tool_option_button[TOOL_OPT_MAX]; - - MenuButton *transform_menu; - PopupMenu *gizmos_menu; - MenuButton *view_menu; - - AcceptDialog *accept; - - ConfirmationDialog *snap_dialog; - ConfirmationDialog *xform_dialog; - ConfirmationDialog *settings_dialog; - - bool snap_enabled; - bool snap_key_enabled; - LineEdit *snap_translate; - LineEdit *snap_rotate; - LineEdit *snap_scale; - PanelContainer *menu_panel; - - LineEdit *xform_translate[3]; - LineEdit *xform_rotate[3]; - LineEdit *xform_scale[3]; - OptionButton *xform_type; - - VBoxContainer *settings_vbc; - SpinBox *settings_fov; - SpinBox *settings_znear; - SpinBox *settings_zfar; - - void _snap_changed(); - void _snap_update(); - void _xform_dialog_action(); - void _menu_item_pressed(int p_option); - void _menu_item_toggled(bool pressed, int p_option); - void _menu_gizmo_toggled(int p_option); - void _update_camera_override_button(bool p_game_running); - void _update_camera_override_viewport(Object *p_viewport); - - HBoxContainer *hbc_menu; - - void _generate_selection_box(); - UndoRedo *undo_redo; - - int camera_override_viewport_id; - - void _init_indicators(); - void _update_gizmos_menu(); - void _update_gizmos_menu_theme(); - void _init_grid(); - void _finish_indicators(); - void _finish_grid(); - - void _toggle_maximize_view(Object *p_viewport); - - Node *custom_camera; - - Object *_get_editor_data(Object *p_what); - - Ref viewport_environment; - - Node3D *selected; - - void _request_gizmo(Object *p_obj); - - static Node3DEditor *singleton; - - void _node_removed(Node *p_node); - Vector> gizmo_plugins_by_priority; - Vector> gizmo_plugins_by_name; - - void _register_all_gizmos(); - - Node3DEditor(); - - bool is_any_freelook_active() const; - - void _refresh_menu_icons(); - -protected: - void _notification(int p_what); - //void _gui_input(InputEvent p_event); - void _unhandled_key_input(Ref p_event); - - static void _bind_methods(); - -public: - static Node3DEditor *get_singleton() { return singleton; } - void snap_cursor_to_plane(const Plane &p_plane); - - Vector3 snap_point(Vector3 p_target, Vector3 p_start = Vector3(0, 0, 0)) const; - - float get_znear() const { return settings_znear->get_value(); } - float get_zfar() const { return settings_zfar->get_value(); } - float get_fov() const { return settings_fov->get_value(); } - - Transform get_gizmo_transform() const { return gizmo.transform; } - bool is_gizmo_visible() const { return gizmo.visible; } - - ToolMode get_tool_mode() const { return tool_mode; } - bool are_local_coords_enabled() const { return tool_option_button[Node3DEditor::TOOL_OPT_LOCAL_COORDS]->is_pressed(); } - bool is_snap_enabled() const { return snap_enabled ^ snap_key_enabled; } - float get_translate_snap() const; - float get_rotate_snap() const; - float get_scale_snap() const; - - Ref get_move_gizmo(int idx) const { return move_gizmo[idx]; } - Ref get_move_plane_gizmo(int idx) const { return move_plane_gizmo[idx]; } - Ref get_rotate_gizmo(int idx) const { return rotate_gizmo[idx]; } - Ref get_scale_gizmo(int idx) const { return scale_gizmo[idx]; } - Ref get_scale_plane_gizmo(int idx) const { return scale_plane_gizmo[idx]; } - - void update_transform_gizmo(); - void update_all_gizmos(Node *p_node = NULL); - void snap_selected_nodes_to_floor(); - void select_gizmo_highlight_axis(int p_axis); - void set_custom_camera(Node *p_camera) { custom_camera = p_camera; } - - void set_undo_redo(UndoRedo *p_undo_redo) { undo_redo = p_undo_redo; } - Dictionary get_state() const; - void set_state(const Dictionary &p_state); - - Ref get_viewport_environment() { return viewport_environment; } - - UndoRedo *get_undo_redo() { return undo_redo; } - - void add_control_to_menu_panel(Control *p_control); - void remove_control_from_menu_panel(Control *p_control); - - VSplitContainer *get_shader_split(); - HSplitContainer *get_palette_split(); - - Node3D *get_selected() { return selected; } - - int get_over_gizmo_handle() const { return over_gizmo_handle; } - void set_over_gizmo_handle(int idx) { over_gizmo_handle = idx; } - - void set_can_preview(Camera3D *p_preview); - - Node3DEditorViewport *get_editor_viewport(int p_idx) { - ERR_FAIL_INDEX_V(p_idx, static_cast(VIEWPORTS_COUNT), NULL); - return viewports[p_idx]; - } - - void add_gizmo_plugin(Ref p_plugin); - void remove_gizmo_plugin(Ref p_plugin); - - void edit(Node3D *p_spatial); - void clear(); - - Node3DEditor(EditorNode *p_editor); - ~Node3DEditor(); -}; - -class Node3DEditorPlugin : public EditorPlugin { - - GDCLASS(Node3DEditorPlugin, EditorPlugin); - - Node3DEditor *spatial_editor; - EditorNode *editor; - -protected: - static void _bind_methods(); - -public: - void snap_cursor_to_plane(const Plane &p_plane); - - Node3DEditor *get_spatial_editor() { return spatial_editor; } - virtual String get_name() const { return "3D"; } - bool has_main_screen() const { return true; } - virtual void make_visible(bool p_visible); - virtual void edit(Object *p_object); - virtual bool handles(Object *p_object) const; - - virtual Dictionary get_state() const; - virtual void set_state(const Dictionary &p_state); - virtual void clear() { spatial_editor->clear(); } - - virtual void edited_scene_changed(); - - Node3DEditorPlugin(EditorNode *p_node); - ~Node3DEditorPlugin(); -}; - -class EditorNode3DGizmoPlugin : public Resource { - - GDCLASS(EditorNode3DGizmoPlugin, Resource); - -public: - static const int VISIBLE = 0; - static const int HIDDEN = 1; - static const int ON_TOP = 2; - -private: - int current_state; - List current_gizmos; - HashMap>> materials; - -protected: - static void _bind_methods(); - virtual bool has_gizmo(Node3D *p_spatial); - virtual Ref create_gizmo(Node3D *p_spatial); - -public: - void create_material(const String &p_name, const Color &p_color, bool p_billboard = false, bool p_on_top = false, bool p_use_vertex_color = false); - void create_icon_material(const String &p_name, const Ref &p_texture, bool p_on_top = false, const Color &p_albedo = Color(1, 1, 1, 1)); - void create_handle_material(const String &p_name, bool p_billboard = false); - void add_material(const String &p_name, Ref p_material); - - Ref get_material(const String &p_name, const Ref &p_gizmo = Ref()); - - virtual String get_name() const; - virtual int get_priority() const; - virtual bool can_be_hidden() const; - virtual bool is_selectable_when_hidden() const; - - virtual void redraw(EditorNode3DGizmo *p_gizmo); - virtual String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const; - virtual Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const; - virtual void set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point); - virtual void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false); - virtual bool is_handle_highlighted(const EditorNode3DGizmo *p_gizmo, int p_idx) const; - - Ref get_gizmo(Node3D *p_spatial); - void set_state(int p_state); - int get_state() const; - void unregister_gizmo(EditorNode3DGizmo *p_gizmo); - - EditorNode3DGizmoPlugin(); - virtual ~EditorNode3DGizmoPlugin(); -}; - -#endif diff --git a/editor/plugins/sprite_2d_editor_plugin.cpp b/editor/plugins/sprite_2d_editor_plugin.cpp new file mode 100644 index 0000000000..ce994ee6c7 --- /dev/null +++ b/editor/plugins/sprite_2d_editor_plugin.cpp @@ -0,0 +1,611 @@ +/*************************************************************************/ +/* sprite_2d_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "sprite_2d_editor_plugin.h" + +#include "canvas_item_editor_plugin.h" +#include "editor/editor_scale.h" +#include "scene/2d/collision_polygon_2d.h" +#include "scene/2d/light_occluder_2d.h" +#include "scene/2d/mesh_instance_2d.h" +#include "scene/2d/polygon_2d.h" +#include "scene/gui/box_container.h" +#include "thirdparty/misc/clipper.hpp" + +void Sprite2DEditor::_node_removed(Node *p_node) { + + if (p_node == node) { + node = NULL; + options->hide(); + } +} + +void Sprite2DEditor::edit(Sprite2D *p_sprite) { + + node = p_sprite; +} + +#define PRECISION 10.0 + +Vector expand(const Vector &points, const Rect2i &rect, float epsilon = 2.0) { + int size = points.size(); + ERR_FAIL_COND_V(size < 2, Vector()); + + ClipperLib::Path subj; + ClipperLib::PolyTree solution; + ClipperLib::PolyTree out; + + for (int i = 0; i < points.size(); i++) { + + subj << ClipperLib::IntPoint(points[i].x * PRECISION, points[i].y * PRECISION); + } + ClipperLib::ClipperOffset co; + co.AddPath(subj, ClipperLib::jtMiter, ClipperLib::etClosedPolygon); + co.Execute(solution, epsilon * PRECISION); + + ClipperLib::PolyNode *p = solution.GetFirst(); + + ERR_FAIL_COND_V(!p, points); + + while (p->IsHole()) { + p = p->GetNext(); + } + + //turn the result into simply polygon (AKA, fix overlap) + + //clamp into the specified rect + ClipperLib::Clipper cl; + cl.StrictlySimple(true); + cl.AddPath(p->Contour, ClipperLib::ptSubject, true); + //create the clipping rect + ClipperLib::Path clamp; + clamp.push_back(ClipperLib::IntPoint(0, 0)); + clamp.push_back(ClipperLib::IntPoint(rect.size.width * PRECISION, 0)); + clamp.push_back(ClipperLib::IntPoint(rect.size.width * PRECISION, rect.size.height * PRECISION)); + clamp.push_back(ClipperLib::IntPoint(0, rect.size.height * PRECISION)); + cl.AddPath(clamp, ClipperLib::ptClip, true); + cl.Execute(ClipperLib::ctIntersection, out); + + Vector outPoints; + ClipperLib::PolyNode *p2 = out.GetFirst(); + ERR_FAIL_COND_V(!p2, points); + + while (p2->IsHole()) { + p2 = p2->GetNext(); + } + + int lasti = p2->Contour.size() - 1; + Vector2 prev = Vector2(p2->Contour[lasti].X / PRECISION, p2->Contour[lasti].Y / PRECISION); + for (uint64_t i = 0; i < p2->Contour.size(); i++) { + + Vector2 cur = Vector2(p2->Contour[i].X / PRECISION, p2->Contour[i].Y / PRECISION); + if (cur.distance_to(prev) > 0.5) { + outPoints.push_back(cur); + prev = cur; + } + } + return outPoints; +} + +void Sprite2DEditor::_menu_option(int p_option) { + + if (!node) { + return; + } + + selected_menu_item = (Menu)p_option; + + switch (p_option) { + case MENU_OPTION_CONVERT_TO_MESH_2D: { + + debug_uv_dialog->get_ok()->set_text(TTR("Create Mesh2D")); + debug_uv_dialog->set_title(TTR("Mesh2D Preview")); + + _update_mesh_data(); + debug_uv_dialog->popup_centered(); + debug_uv->update(); + + } break; + case MENU_OPTION_CONVERT_TO_POLYGON_2D: { + + debug_uv_dialog->get_ok()->set_text(TTR("Create Polygon2D")); + debug_uv_dialog->set_title(TTR("Polygon2D Preview")); + + _update_mesh_data(); + debug_uv_dialog->popup_centered(); + debug_uv->update(); + } break; + case MENU_OPTION_CREATE_COLLISION_POLY_2D: { + + debug_uv_dialog->get_ok()->set_text(TTR("Create CollisionPolygon2D")); + debug_uv_dialog->set_title(TTR("CollisionPolygon2D Preview")); + + _update_mesh_data(); + debug_uv_dialog->popup_centered(); + debug_uv->update(); + + } break; + case MENU_OPTION_CREATE_LIGHT_OCCLUDER_2D: { + + debug_uv_dialog->get_ok()->set_text(TTR("Create LightOccluder2D")); + debug_uv_dialog->set_title(TTR("LightOccluder2D Preview")); + + _update_mesh_data(); + debug_uv_dialog->popup_centered(); + debug_uv->update(); + + } break; + } +} + +void Sprite2DEditor::_update_mesh_data() { + + Ref texture = node->get_texture(); + if (texture.is_null()) { + err_dialog->set_text(TTR("Sprite2D is empty!")); + err_dialog->popup_centered(); + return; + } + + if (node->get_hframes() > 1 || node->get_vframes() > 1) { + err_dialog->set_text(TTR("Can't convert a sprite using animation frames to mesh.")); + err_dialog->popup_centered(); + return; + } + + Ref image = texture->get_data(); + ERR_FAIL_COND(image.is_null()); + Rect2 rect; + if (node->is_region()) + rect = node->get_region_rect(); + else + rect.size = Size2(image->get_width(), image->get_height()); + + Ref bm; + bm.instance(); + bm->create_from_image_alpha(image); + + int shrink = shrink_pixels->get_value(); + if (shrink > 0) { + bm->shrink_mask(shrink, rect); + } + + int grow = grow_pixels->get_value(); + if (grow > 0) { + bm->grow_mask(grow, rect); + } + + float epsilon = simplification->get_value(); + + Vector> lines = bm->clip_opaque_to_polygons(rect, epsilon); + + uv_lines.clear(); + + computed_vertices.clear(); + computed_uv.clear(); + computed_indices.clear(); + + Size2 img_size = Vector2(image->get_width(), image->get_height()); + for (int i = 0; i < lines.size(); i++) { + lines.write[i] = expand(lines[i], rect, epsilon); + } + + if (selected_menu_item == MENU_OPTION_CONVERT_TO_MESH_2D) { + + for (int j = 0; j < lines.size(); j++) { + int index_ofs = computed_vertices.size(); + + for (int i = 0; i < lines[j].size(); i++) { + Vector2 vtx = lines[j][i]; + computed_uv.push_back(vtx / img_size); + + vtx -= rect.position; //offset by rect position + + //flip if flipped + if (node->is_flipped_h()) + vtx.x = rect.size.x - vtx.x - 1.0; + if (node->is_flipped_v()) + vtx.y = rect.size.y - vtx.y - 1.0; + + if (node->is_centered()) + vtx -= rect.size / 2.0; + + computed_vertices.push_back(vtx); + } + + Vector poly = Geometry::triangulate_polygon(lines[j]); + + for (int i = 0; i < poly.size(); i += 3) { + for (int k = 0; k < 3; k++) { + int idx = i + k; + int idxn = i + (k + 1) % 3; + uv_lines.push_back(lines[j][poly[idx]]); + uv_lines.push_back(lines[j][poly[idxn]]); + + computed_indices.push_back(poly[idx] + index_ofs); + } + } + } + } + + outline_lines.clear(); + computed_outline_lines.clear(); + + if (selected_menu_item == MENU_OPTION_CONVERT_TO_POLYGON_2D || selected_menu_item == MENU_OPTION_CREATE_COLLISION_POLY_2D || selected_menu_item == MENU_OPTION_CREATE_LIGHT_OCCLUDER_2D) { + outline_lines.resize(lines.size()); + computed_outline_lines.resize(lines.size()); + for (int pi = 0; pi < lines.size(); pi++) { + + Vector ol; + Vector col; + + ol.resize(lines[pi].size()); + col.resize(lines[pi].size()); + + for (int i = 0; i < lines[pi].size(); i++) { + Vector2 vtx = lines[pi][i]; + + ol.write[i] = vtx; + + vtx -= rect.position; //offset by rect position + + //flip if flipped + if (node->is_flipped_h()) + vtx.x = rect.size.x - vtx.x - 1.0; + if (node->is_flipped_v()) + vtx.y = rect.size.y - vtx.y - 1.0; + + if (node->is_centered()) + vtx -= rect.size / 2.0; + + col.write[i] = vtx; + } + + outline_lines.write[pi] = ol; + computed_outline_lines.write[pi] = col; + } + } + + debug_uv->update(); +} + +void Sprite2DEditor::_create_node() { + switch (selected_menu_item) { + case MENU_OPTION_CONVERT_TO_MESH_2D: { + _convert_to_mesh_2d_node(); + } break; + case MENU_OPTION_CONVERT_TO_POLYGON_2D: { + _convert_to_polygon_2d_node(); + } break; + case MENU_OPTION_CREATE_COLLISION_POLY_2D: { + _create_collision_polygon_2d_node(); + } break; + case MENU_OPTION_CREATE_LIGHT_OCCLUDER_2D: { + _create_light_occluder_2d_node(); + } break; + } +} + +void Sprite2DEditor::_convert_to_mesh_2d_node() { + + if (computed_vertices.size() < 3) { + err_dialog->set_text(TTR("Invalid geometry, can't replace by mesh.")); + err_dialog->popup_centered(); + return; + } + + Ref mesh; + mesh.instance(); + + Array a; + a.resize(Mesh::ARRAY_MAX); + a[Mesh::ARRAY_VERTEX] = computed_vertices; + a[Mesh::ARRAY_TEX_UV] = computed_uv; + a[Mesh::ARRAY_INDEX] = computed_indices; + + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a, Array(), Dictionary(), Mesh::ARRAY_FLAG_USE_2D_VERTICES); + + MeshInstance2D *mesh_instance = memnew(MeshInstance2D); + mesh_instance->set_mesh(mesh); + + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Convert to Mesh2D")); + ur->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", node, mesh_instance, true, false); + ur->add_do_reference(mesh_instance); + ur->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", mesh_instance, node, false, false); + ur->add_undo_reference(node); + ur->commit_action(); +} + +void Sprite2DEditor::_convert_to_polygon_2d_node() { + + if (computed_outline_lines.empty()) { + err_dialog->set_text(TTR("Invalid geometry, can't create polygon.")); + err_dialog->popup_centered(); + return; + } + + Polygon2D *polygon_2d_instance = memnew(Polygon2D); + + int total_point_count = 0; + for (int i = 0; i < computed_outline_lines.size(); i++) + total_point_count += computed_outline_lines[i].size(); + + PackedVector2Array polygon; + polygon.resize(total_point_count); + Vector2 *polygon_write = polygon.ptrw(); + + PackedVector2Array uvs; + uvs.resize(total_point_count); + Vector2 *uvs_write = uvs.ptrw(); + + int current_point_index = 0; + + Array polys; + polys.resize(computed_outline_lines.size()); + + for (int i = 0; i < computed_outline_lines.size(); i++) { + + Vector outline = computed_outline_lines[i]; + Vector uv_outline = outline_lines[i]; + + PackedInt32Array pia; + pia.resize(outline.size()); + int *pia_write = pia.ptrw(); + + for (int pi = 0; pi < outline.size(); pi++) { + polygon_write[current_point_index] = outline[pi]; + uvs_write[current_point_index] = uv_outline[pi]; + pia_write[pi] = current_point_index; + current_point_index++; + } + + polys[i] = pia; + } + + polygon_2d_instance->set_uv(uvs); + polygon_2d_instance->set_polygon(polygon); + polygon_2d_instance->set_polygons(polys); + + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Convert to Polygon2D")); + ur->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", node, polygon_2d_instance, true, false); + ur->add_do_reference(polygon_2d_instance); + ur->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", polygon_2d_instance, node, false, false); + ur->add_undo_reference(node); + ur->commit_action(); +} + +void Sprite2DEditor::_create_collision_polygon_2d_node() { + + if (computed_outline_lines.empty()) { + err_dialog->set_text(TTR("Invalid geometry, can't create collision polygon.")); + err_dialog->popup_centered(); + return; + } + + for (int i = 0; i < computed_outline_lines.size(); i++) { + + Vector outline = computed_outline_lines[i]; + + CollisionPolygon2D *collision_polygon_2d_instance = memnew(CollisionPolygon2D); + collision_polygon_2d_instance->set_polygon(outline); + + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Create CollisionPolygon2D Sibling")); + ur->add_do_method(this, "_add_as_sibling_or_child", node, collision_polygon_2d_instance); + ur->add_do_reference(collision_polygon_2d_instance); + ur->add_undo_method(node != this->get_tree()->get_edited_scene_root() ? node->get_parent() : this->get_tree()->get_edited_scene_root(), "remove_child", collision_polygon_2d_instance); + ur->commit_action(); + } +} + +void Sprite2DEditor::_create_light_occluder_2d_node() { + + if (computed_outline_lines.empty()) { + err_dialog->set_text(TTR("Invalid geometry, can't create light occluder.")); + err_dialog->popup_centered(); + return; + } + + for (int i = 0; i < computed_outline_lines.size(); i++) { + + Vector outline = computed_outline_lines[i]; + + Ref polygon; + polygon.instance(); + + PackedVector2Array a; + a.resize(outline.size()); + Vector2 *aw = a.ptrw(); + for (int io = 0; io < outline.size(); io++) { + aw[io] = outline[io]; + } + polygon->set_polygon(a); + + LightOccluder2D *light_occluder_2d_instance = memnew(LightOccluder2D); + light_occluder_2d_instance->set_occluder_polygon(polygon); + + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Create LightOccluder2D Sibling")); + ur->add_do_method(this, "_add_as_sibling_or_child", node, light_occluder_2d_instance); + ur->add_do_reference(light_occluder_2d_instance); + ur->add_undo_method(node != this->get_tree()->get_edited_scene_root() ? node->get_parent() : this->get_tree()->get_edited_scene_root(), "remove_child", light_occluder_2d_instance); + ur->commit_action(); + } +} + +void Sprite2DEditor::_add_as_sibling_or_child(Node *p_own_node, Node *p_new_node) { + // Can't make sibling if own node is scene root + if (p_own_node != this->get_tree()->get_edited_scene_root()) { + p_own_node->get_parent()->add_child(p_new_node, true); + Object::cast_to(p_new_node)->set_transform(Object::cast_to(p_own_node)->get_transform()); + } else { + p_own_node->add_child(p_new_node, true); + } + + p_new_node->set_owner(this->get_tree()->get_edited_scene_root()); +} + +void Sprite2DEditor::_debug_uv_draw() { + + Ref tex = node->get_texture(); + ERR_FAIL_COND(!tex.is_valid()); + + Point2 draw_pos_offset = Point2(1.0, 1.0); + Size2 draw_size_offset = Size2(2.0, 2.0); + + debug_uv->set_clip_contents(true); + debug_uv->draw_texture(tex, draw_pos_offset); + debug_uv->set_custom_minimum_size(tex->get_size() + draw_size_offset); + debug_uv->draw_set_transform(draw_pos_offset, 0, Size2(1.0, 1.0)); + + Color color = Color(1.0, 0.8, 0.7); + + if (selected_menu_item == MENU_OPTION_CONVERT_TO_MESH_2D && uv_lines.size() > 0) { + debug_uv->draw_multiline(uv_lines, color); + + } else if ((selected_menu_item == MENU_OPTION_CONVERT_TO_POLYGON_2D || selected_menu_item == MENU_OPTION_CREATE_COLLISION_POLY_2D || selected_menu_item == MENU_OPTION_CREATE_LIGHT_OCCLUDER_2D) && outline_lines.size() > 0) { + for (int i = 0; i < outline_lines.size(); i++) { + Vector outline = outline_lines[i]; + + debug_uv->draw_polyline(outline, color); + debug_uv->draw_line(outline[0], outline[outline.size() - 1], color); + } + } +} + +void Sprite2DEditor::_bind_methods() { + + ClassDB::bind_method("_add_as_sibling_or_child", &Sprite2DEditor::_add_as_sibling_or_child); +} + +Sprite2DEditor::Sprite2DEditor() { + + options = memnew(MenuButton); + + CanvasItemEditor::get_singleton()->add_control_to_menu_panel(options); + + options->set_text(TTR("Sprite2D")); + options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("Sprite2D", "EditorIcons")); + + options->get_popup()->add_item(TTR("Convert to Mesh2D"), MENU_OPTION_CONVERT_TO_MESH_2D); + options->get_popup()->add_item(TTR("Convert to Polygon2D"), MENU_OPTION_CONVERT_TO_POLYGON_2D); + options->get_popup()->add_item(TTR("Create CollisionPolygon2D Sibling"), MENU_OPTION_CREATE_COLLISION_POLY_2D); + options->get_popup()->add_item(TTR("Create LightOccluder2D Sibling"), MENU_OPTION_CREATE_LIGHT_OCCLUDER_2D); + options->set_switch_on_hover(true); + + options->get_popup()->connect("id_pressed", callable_mp(this, &Sprite2DEditor::_menu_option)); + + err_dialog = memnew(AcceptDialog); + add_child(err_dialog); + + debug_uv_dialog = memnew(ConfirmationDialog); + debug_uv_dialog->get_ok()->set_text(TTR("Create Mesh2D")); + debug_uv_dialog->set_title("Mesh 2D Preview"); + VBoxContainer *vb = memnew(VBoxContainer); + debug_uv_dialog->add_child(vb); + ScrollContainer *scroll = memnew(ScrollContainer); + scroll->set_custom_minimum_size(Size2(800, 500) * EDSCALE); + scroll->set_enable_h_scroll(true); + scroll->set_enable_v_scroll(true); + vb->add_margin_child(TTR("Preview:"), scroll, true); + debug_uv = memnew(Control); + debug_uv->connect("draw", callable_mp(this, &Sprite2DEditor::_debug_uv_draw)); + scroll->add_child(debug_uv); + debug_uv_dialog->connect("confirmed", callable_mp(this, &Sprite2DEditor::_create_node)); + + HBoxContainer *hb = memnew(HBoxContainer); + hb->add_child(memnew(Label(TTR("Simplification: ")))); + simplification = memnew(SpinBox); + simplification->set_min(0.01); + simplification->set_max(10.00); + simplification->set_step(0.01); + simplification->set_value(2); + hb->add_child(simplification); + hb->add_spacer(); + hb->add_child(memnew(Label(TTR("Shrink (Pixels): ")))); + shrink_pixels = memnew(SpinBox); + shrink_pixels->set_min(0); + shrink_pixels->set_max(10); + shrink_pixels->set_step(1); + shrink_pixels->set_value(0); + hb->add_child(shrink_pixels); + hb->add_spacer(); + hb->add_child(memnew(Label(TTR("Grow (Pixels): ")))); + grow_pixels = memnew(SpinBox); + grow_pixels->set_min(0); + grow_pixels->set_max(10); + grow_pixels->set_step(1); + grow_pixels->set_value(2); + hb->add_child(grow_pixels); + hb->add_spacer(); + update_preview = memnew(Button); + update_preview->set_text(TTR("Update Preview")); + update_preview->connect("pressed", callable_mp(this, &Sprite2DEditor::_update_mesh_data)); + hb->add_child(update_preview); + vb->add_margin_child(TTR("Settings:"), hb); + + add_child(debug_uv_dialog); +} + +void Sprite2DEditorPlugin::edit(Object *p_object) { + + sprite_editor->edit(Object::cast_to(p_object)); +} + +bool Sprite2DEditorPlugin::handles(Object *p_object) const { + + return p_object->is_class("Sprite2D"); +} + +void Sprite2DEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + sprite_editor->options->show(); + } else { + + sprite_editor->options->hide(); + sprite_editor->edit(NULL); + } +} + +Sprite2DEditorPlugin::Sprite2DEditorPlugin(EditorNode *p_node) { + + editor = p_node; + sprite_editor = memnew(Sprite2DEditor); + editor->get_viewport()->add_child(sprite_editor); + make_visible(false); + + //sprite_editor->options->hide(); +} + +Sprite2DEditorPlugin::~Sprite2DEditorPlugin() { +} diff --git a/editor/plugins/sprite_2d_editor_plugin.h b/editor/plugins/sprite_2d_editor_plugin.h new file mode 100644 index 0000000000..d0ebf9c84e --- /dev/null +++ b/editor/plugins/sprite_2d_editor_plugin.h @@ -0,0 +1,117 @@ +/*************************************************************************/ +/* sprite_2d_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SPRITE_EDITOR_PLUGIN_H +#define SPRITE_EDITOR_PLUGIN_H + +#include "editor/editor_node.h" +#include "editor/editor_plugin.h" +#include "scene/2d/sprite_2d.h" +#include "scene/gui/spin_box.h" + +class Sprite2DEditor : public Control { + + GDCLASS(Sprite2DEditor, Control); + + enum Menu { + MENU_OPTION_CONVERT_TO_MESH_2D, + MENU_OPTION_CONVERT_TO_POLYGON_2D, + MENU_OPTION_CREATE_COLLISION_POLY_2D, + MENU_OPTION_CREATE_LIGHT_OCCLUDER_2D + }; + + Menu selected_menu_item; + + Sprite2D *node; + + MenuButton *options; + + ConfirmationDialog *outline_dialog; + + AcceptDialog *err_dialog; + + ConfirmationDialog *debug_uv_dialog; + Control *debug_uv; + Vector uv_lines; + Vector> outline_lines; + Vector> computed_outline_lines; + Vector computed_vertices; + Vector computed_uv; + Vector computed_indices; + + SpinBox *simplification; + SpinBox *grow_pixels; + SpinBox *shrink_pixels; + Button *update_preview; + + void _menu_option(int p_option); + + //void _create_uv_lines(); + friend class Sprite2DEditorPlugin; + + void _debug_uv_draw(); + void _update_mesh_data(); + + void _create_node(); + void _convert_to_mesh_2d_node(); + void _convert_to_polygon_2d_node(); + void _create_collision_polygon_2d_node(); + void _create_light_occluder_2d_node(); + + void _add_as_sibling_or_child(Node *p_own_node, Node *p_new_node); + +protected: + void _node_removed(Node *p_node); + static void _bind_methods(); + +public: + void edit(Sprite2D *p_sprite); + Sprite2DEditor(); +}; + +class Sprite2DEditorPlugin : public EditorPlugin { + + GDCLASS(Sprite2DEditorPlugin, EditorPlugin); + + Sprite2DEditor *sprite_editor; + EditorNode *editor; + +public: + virtual String get_name() const { return "Sprite"; } + bool has_main_screen() const { return false; } + virtual void edit(Object *p_object); + virtual bool handles(Object *p_object) const; + virtual void make_visible(bool p_visible); + + Sprite2DEditorPlugin(EditorNode *p_node); + ~Sprite2DEditorPlugin(); +}; + +#endif // SPRITE_EDITOR_PLUGIN_H diff --git a/editor/plugins/sprite_editor_plugin.cpp b/editor/plugins/sprite_editor_plugin.cpp deleted file mode 100644 index 3eb76946a6..0000000000 --- a/editor/plugins/sprite_editor_plugin.cpp +++ /dev/null @@ -1,611 +0,0 @@ -/*************************************************************************/ -/* sprite_editor_plugin.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "sprite_editor_plugin.h" - -#include "canvas_item_editor_plugin.h" -#include "editor/editor_scale.h" -#include "scene/2d/collision_polygon_2d.h" -#include "scene/2d/light_occluder_2d.h" -#include "scene/2d/mesh_instance_2d.h" -#include "scene/2d/polygon_2d.h" -#include "scene/gui/box_container.h" -#include "thirdparty/misc/clipper.hpp" - -void SpriteEditor::_node_removed(Node *p_node) { - - if (p_node == node) { - node = NULL; - options->hide(); - } -} - -void SpriteEditor::edit(Sprite2D *p_sprite) { - - node = p_sprite; -} - -#define PRECISION 10.0 - -Vector expand(const Vector &points, const Rect2i &rect, float epsilon = 2.0) { - int size = points.size(); - ERR_FAIL_COND_V(size < 2, Vector()); - - ClipperLib::Path subj; - ClipperLib::PolyTree solution; - ClipperLib::PolyTree out; - - for (int i = 0; i < points.size(); i++) { - - subj << ClipperLib::IntPoint(points[i].x * PRECISION, points[i].y * PRECISION); - } - ClipperLib::ClipperOffset co; - co.AddPath(subj, ClipperLib::jtMiter, ClipperLib::etClosedPolygon); - co.Execute(solution, epsilon * PRECISION); - - ClipperLib::PolyNode *p = solution.GetFirst(); - - ERR_FAIL_COND_V(!p, points); - - while (p->IsHole()) { - p = p->GetNext(); - } - - //turn the result into simply polygon (AKA, fix overlap) - - //clamp into the specified rect - ClipperLib::Clipper cl; - cl.StrictlySimple(true); - cl.AddPath(p->Contour, ClipperLib::ptSubject, true); - //create the clipping rect - ClipperLib::Path clamp; - clamp.push_back(ClipperLib::IntPoint(0, 0)); - clamp.push_back(ClipperLib::IntPoint(rect.size.width * PRECISION, 0)); - clamp.push_back(ClipperLib::IntPoint(rect.size.width * PRECISION, rect.size.height * PRECISION)); - clamp.push_back(ClipperLib::IntPoint(0, rect.size.height * PRECISION)); - cl.AddPath(clamp, ClipperLib::ptClip, true); - cl.Execute(ClipperLib::ctIntersection, out); - - Vector outPoints; - ClipperLib::PolyNode *p2 = out.GetFirst(); - ERR_FAIL_COND_V(!p2, points); - - while (p2->IsHole()) { - p2 = p2->GetNext(); - } - - int lasti = p2->Contour.size() - 1; - Vector2 prev = Vector2(p2->Contour[lasti].X / PRECISION, p2->Contour[lasti].Y / PRECISION); - for (uint64_t i = 0; i < p2->Contour.size(); i++) { - - Vector2 cur = Vector2(p2->Contour[i].X / PRECISION, p2->Contour[i].Y / PRECISION); - if (cur.distance_to(prev) > 0.5) { - outPoints.push_back(cur); - prev = cur; - } - } - return outPoints; -} - -void SpriteEditor::_menu_option(int p_option) { - - if (!node) { - return; - } - - selected_menu_item = (Menu)p_option; - - switch (p_option) { - case MENU_OPTION_CONVERT_TO_MESH_2D: { - - debug_uv_dialog->get_ok()->set_text(TTR("Create Mesh2D")); - debug_uv_dialog->set_title(TTR("Mesh2D Preview")); - - _update_mesh_data(); - debug_uv_dialog->popup_centered(); - debug_uv->update(); - - } break; - case MENU_OPTION_CONVERT_TO_POLYGON_2D: { - - debug_uv_dialog->get_ok()->set_text(TTR("Create Polygon2D")); - debug_uv_dialog->set_title(TTR("Polygon2D Preview")); - - _update_mesh_data(); - debug_uv_dialog->popup_centered(); - debug_uv->update(); - } break; - case MENU_OPTION_CREATE_COLLISION_POLY_2D: { - - debug_uv_dialog->get_ok()->set_text(TTR("Create CollisionPolygon2D")); - debug_uv_dialog->set_title(TTR("CollisionPolygon2D Preview")); - - _update_mesh_data(); - debug_uv_dialog->popup_centered(); - debug_uv->update(); - - } break; - case MENU_OPTION_CREATE_LIGHT_OCCLUDER_2D: { - - debug_uv_dialog->get_ok()->set_text(TTR("Create LightOccluder2D")); - debug_uv_dialog->set_title(TTR("LightOccluder2D Preview")); - - _update_mesh_data(); - debug_uv_dialog->popup_centered(); - debug_uv->update(); - - } break; - } -} - -void SpriteEditor::_update_mesh_data() { - - Ref texture = node->get_texture(); - if (texture.is_null()) { - err_dialog->set_text(TTR("Sprite is empty!")); - err_dialog->popup_centered(); - return; - } - - if (node->get_hframes() > 1 || node->get_vframes() > 1) { - err_dialog->set_text(TTR("Can't convert a sprite using animation frames to mesh.")); - err_dialog->popup_centered(); - return; - } - - Ref image = texture->get_data(); - ERR_FAIL_COND(image.is_null()); - Rect2 rect; - if (node->is_region()) - rect = node->get_region_rect(); - else - rect.size = Size2(image->get_width(), image->get_height()); - - Ref bm; - bm.instance(); - bm->create_from_image_alpha(image); - - int shrink = shrink_pixels->get_value(); - if (shrink > 0) { - bm->shrink_mask(shrink, rect); - } - - int grow = grow_pixels->get_value(); - if (grow > 0) { - bm->grow_mask(grow, rect); - } - - float epsilon = simplification->get_value(); - - Vector> lines = bm->clip_opaque_to_polygons(rect, epsilon); - - uv_lines.clear(); - - computed_vertices.clear(); - computed_uv.clear(); - computed_indices.clear(); - - Size2 img_size = Vector2(image->get_width(), image->get_height()); - for (int i = 0; i < lines.size(); i++) { - lines.write[i] = expand(lines[i], rect, epsilon); - } - - if (selected_menu_item == MENU_OPTION_CONVERT_TO_MESH_2D) { - - for (int j = 0; j < lines.size(); j++) { - int index_ofs = computed_vertices.size(); - - for (int i = 0; i < lines[j].size(); i++) { - Vector2 vtx = lines[j][i]; - computed_uv.push_back(vtx / img_size); - - vtx -= rect.position; //offset by rect position - - //flip if flipped - if (node->is_flipped_h()) - vtx.x = rect.size.x - vtx.x - 1.0; - if (node->is_flipped_v()) - vtx.y = rect.size.y - vtx.y - 1.0; - - if (node->is_centered()) - vtx -= rect.size / 2.0; - - computed_vertices.push_back(vtx); - } - - Vector poly = Geometry::triangulate_polygon(lines[j]); - - for (int i = 0; i < poly.size(); i += 3) { - for (int k = 0; k < 3; k++) { - int idx = i + k; - int idxn = i + (k + 1) % 3; - uv_lines.push_back(lines[j][poly[idx]]); - uv_lines.push_back(lines[j][poly[idxn]]); - - computed_indices.push_back(poly[idx] + index_ofs); - } - } - } - } - - outline_lines.clear(); - computed_outline_lines.clear(); - - if (selected_menu_item == MENU_OPTION_CONVERT_TO_POLYGON_2D || selected_menu_item == MENU_OPTION_CREATE_COLLISION_POLY_2D || selected_menu_item == MENU_OPTION_CREATE_LIGHT_OCCLUDER_2D) { - outline_lines.resize(lines.size()); - computed_outline_lines.resize(lines.size()); - for (int pi = 0; pi < lines.size(); pi++) { - - Vector ol; - Vector col; - - ol.resize(lines[pi].size()); - col.resize(lines[pi].size()); - - for (int i = 0; i < lines[pi].size(); i++) { - Vector2 vtx = lines[pi][i]; - - ol.write[i] = vtx; - - vtx -= rect.position; //offset by rect position - - //flip if flipped - if (node->is_flipped_h()) - vtx.x = rect.size.x - vtx.x - 1.0; - if (node->is_flipped_v()) - vtx.y = rect.size.y - vtx.y - 1.0; - - if (node->is_centered()) - vtx -= rect.size / 2.0; - - col.write[i] = vtx; - } - - outline_lines.write[pi] = ol; - computed_outline_lines.write[pi] = col; - } - } - - debug_uv->update(); -} - -void SpriteEditor::_create_node() { - switch (selected_menu_item) { - case MENU_OPTION_CONVERT_TO_MESH_2D: { - _convert_to_mesh_2d_node(); - } break; - case MENU_OPTION_CONVERT_TO_POLYGON_2D: { - _convert_to_polygon_2d_node(); - } break; - case MENU_OPTION_CREATE_COLLISION_POLY_2D: { - _create_collision_polygon_2d_node(); - } break; - case MENU_OPTION_CREATE_LIGHT_OCCLUDER_2D: { - _create_light_occluder_2d_node(); - } break; - } -} - -void SpriteEditor::_convert_to_mesh_2d_node() { - - if (computed_vertices.size() < 3) { - err_dialog->set_text(TTR("Invalid geometry, can't replace by mesh.")); - err_dialog->popup_centered(); - return; - } - - Ref mesh; - mesh.instance(); - - Array a; - a.resize(Mesh::ARRAY_MAX); - a[Mesh::ARRAY_VERTEX] = computed_vertices; - a[Mesh::ARRAY_TEX_UV] = computed_uv; - a[Mesh::ARRAY_INDEX] = computed_indices; - - mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a, Array(), Dictionary(), Mesh::ARRAY_FLAG_USE_2D_VERTICES); - - MeshInstance2D *mesh_instance = memnew(MeshInstance2D); - mesh_instance->set_mesh(mesh); - - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); - ur->create_action(TTR("Convert to Mesh2D")); - ur->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", node, mesh_instance, true, false); - ur->add_do_reference(mesh_instance); - ur->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", mesh_instance, node, false, false); - ur->add_undo_reference(node); - ur->commit_action(); -} - -void SpriteEditor::_convert_to_polygon_2d_node() { - - if (computed_outline_lines.empty()) { - err_dialog->set_text(TTR("Invalid geometry, can't create polygon.")); - err_dialog->popup_centered(); - return; - } - - Polygon2D *polygon_2d_instance = memnew(Polygon2D); - - int total_point_count = 0; - for (int i = 0; i < computed_outline_lines.size(); i++) - total_point_count += computed_outline_lines[i].size(); - - PackedVector2Array polygon; - polygon.resize(total_point_count); - Vector2 *polygon_write = polygon.ptrw(); - - PackedVector2Array uvs; - uvs.resize(total_point_count); - Vector2 *uvs_write = uvs.ptrw(); - - int current_point_index = 0; - - Array polys; - polys.resize(computed_outline_lines.size()); - - for (int i = 0; i < computed_outline_lines.size(); i++) { - - Vector outline = computed_outline_lines[i]; - Vector uv_outline = outline_lines[i]; - - PackedInt32Array pia; - pia.resize(outline.size()); - int *pia_write = pia.ptrw(); - - for (int pi = 0; pi < outline.size(); pi++) { - polygon_write[current_point_index] = outline[pi]; - uvs_write[current_point_index] = uv_outline[pi]; - pia_write[pi] = current_point_index; - current_point_index++; - } - - polys[i] = pia; - } - - polygon_2d_instance->set_uv(uvs); - polygon_2d_instance->set_polygon(polygon); - polygon_2d_instance->set_polygons(polys); - - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); - ur->create_action(TTR("Convert to Polygon2D")); - ur->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", node, polygon_2d_instance, true, false); - ur->add_do_reference(polygon_2d_instance); - ur->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", polygon_2d_instance, node, false, false); - ur->add_undo_reference(node); - ur->commit_action(); -} - -void SpriteEditor::_create_collision_polygon_2d_node() { - - if (computed_outline_lines.empty()) { - err_dialog->set_text(TTR("Invalid geometry, can't create collision polygon.")); - err_dialog->popup_centered(); - return; - } - - for (int i = 0; i < computed_outline_lines.size(); i++) { - - Vector outline = computed_outline_lines[i]; - - CollisionPolygon2D *collision_polygon_2d_instance = memnew(CollisionPolygon2D); - collision_polygon_2d_instance->set_polygon(outline); - - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); - ur->create_action(TTR("Create CollisionPolygon2D Sibling")); - ur->add_do_method(this, "_add_as_sibling_or_child", node, collision_polygon_2d_instance); - ur->add_do_reference(collision_polygon_2d_instance); - ur->add_undo_method(node != this->get_tree()->get_edited_scene_root() ? node->get_parent() : this->get_tree()->get_edited_scene_root(), "remove_child", collision_polygon_2d_instance); - ur->commit_action(); - } -} - -void SpriteEditor::_create_light_occluder_2d_node() { - - if (computed_outline_lines.empty()) { - err_dialog->set_text(TTR("Invalid geometry, can't create light occluder.")); - err_dialog->popup_centered(); - return; - } - - for (int i = 0; i < computed_outline_lines.size(); i++) { - - Vector outline = computed_outline_lines[i]; - - Ref polygon; - polygon.instance(); - - PackedVector2Array a; - a.resize(outline.size()); - Vector2 *aw = a.ptrw(); - for (int io = 0; io < outline.size(); io++) { - aw[io] = outline[io]; - } - polygon->set_polygon(a); - - LightOccluder2D *light_occluder_2d_instance = memnew(LightOccluder2D); - light_occluder_2d_instance->set_occluder_polygon(polygon); - - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); - ur->create_action(TTR("Create LightOccluder2D Sibling")); - ur->add_do_method(this, "_add_as_sibling_or_child", node, light_occluder_2d_instance); - ur->add_do_reference(light_occluder_2d_instance); - ur->add_undo_method(node != this->get_tree()->get_edited_scene_root() ? node->get_parent() : this->get_tree()->get_edited_scene_root(), "remove_child", light_occluder_2d_instance); - ur->commit_action(); - } -} - -void SpriteEditor::_add_as_sibling_or_child(Node *p_own_node, Node *p_new_node) { - // Can't make sibling if own node is scene root - if (p_own_node != this->get_tree()->get_edited_scene_root()) { - p_own_node->get_parent()->add_child(p_new_node, true); - Object::cast_to(p_new_node)->set_transform(Object::cast_to(p_own_node)->get_transform()); - } else { - p_own_node->add_child(p_new_node, true); - } - - p_new_node->set_owner(this->get_tree()->get_edited_scene_root()); -} - -void SpriteEditor::_debug_uv_draw() { - - Ref tex = node->get_texture(); - ERR_FAIL_COND(!tex.is_valid()); - - Point2 draw_pos_offset = Point2(1.0, 1.0); - Size2 draw_size_offset = Size2(2.0, 2.0); - - debug_uv->set_clip_contents(true); - debug_uv->draw_texture(tex, draw_pos_offset); - debug_uv->set_custom_minimum_size(tex->get_size() + draw_size_offset); - debug_uv->draw_set_transform(draw_pos_offset, 0, Size2(1.0, 1.0)); - - Color color = Color(1.0, 0.8, 0.7); - - if (selected_menu_item == MENU_OPTION_CONVERT_TO_MESH_2D && uv_lines.size() > 0) { - debug_uv->draw_multiline(uv_lines, color); - - } else if ((selected_menu_item == MENU_OPTION_CONVERT_TO_POLYGON_2D || selected_menu_item == MENU_OPTION_CREATE_COLLISION_POLY_2D || selected_menu_item == MENU_OPTION_CREATE_LIGHT_OCCLUDER_2D) && outline_lines.size() > 0) { - for (int i = 0; i < outline_lines.size(); i++) { - Vector outline = outline_lines[i]; - - debug_uv->draw_polyline(outline, color); - debug_uv->draw_line(outline[0], outline[outline.size() - 1], color); - } - } -} - -void SpriteEditor::_bind_methods() { - - ClassDB::bind_method("_add_as_sibling_or_child", &SpriteEditor::_add_as_sibling_or_child); -} - -SpriteEditor::SpriteEditor() { - - options = memnew(MenuButton); - - CanvasItemEditor::get_singleton()->add_control_to_menu_panel(options); - - options->set_text(TTR("Sprite")); - options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("Sprite", "EditorIcons")); - - options->get_popup()->add_item(TTR("Convert to Mesh2D"), MENU_OPTION_CONVERT_TO_MESH_2D); - options->get_popup()->add_item(TTR("Convert to Polygon2D"), MENU_OPTION_CONVERT_TO_POLYGON_2D); - options->get_popup()->add_item(TTR("Create CollisionPolygon2D Sibling"), MENU_OPTION_CREATE_COLLISION_POLY_2D); - options->get_popup()->add_item(TTR("Create LightOccluder2D Sibling"), MENU_OPTION_CREATE_LIGHT_OCCLUDER_2D); - options->set_switch_on_hover(true); - - options->get_popup()->connect("id_pressed", callable_mp(this, &SpriteEditor::_menu_option)); - - err_dialog = memnew(AcceptDialog); - add_child(err_dialog); - - debug_uv_dialog = memnew(ConfirmationDialog); - debug_uv_dialog->get_ok()->set_text(TTR("Create Mesh2D")); - debug_uv_dialog->set_title("Mesh 2D Preview"); - VBoxContainer *vb = memnew(VBoxContainer); - debug_uv_dialog->add_child(vb); - ScrollContainer *scroll = memnew(ScrollContainer); - scroll->set_custom_minimum_size(Size2(800, 500) * EDSCALE); - scroll->set_enable_h_scroll(true); - scroll->set_enable_v_scroll(true); - vb->add_margin_child(TTR("Preview:"), scroll, true); - debug_uv = memnew(Control); - debug_uv->connect("draw", callable_mp(this, &SpriteEditor::_debug_uv_draw)); - scroll->add_child(debug_uv); - debug_uv_dialog->connect("confirmed", callable_mp(this, &SpriteEditor::_create_node)); - - HBoxContainer *hb = memnew(HBoxContainer); - hb->add_child(memnew(Label(TTR("Simplification: ")))); - simplification = memnew(SpinBox); - simplification->set_min(0.01); - simplification->set_max(10.00); - simplification->set_step(0.01); - simplification->set_value(2); - hb->add_child(simplification); - hb->add_spacer(); - hb->add_child(memnew(Label(TTR("Shrink (Pixels): ")))); - shrink_pixels = memnew(SpinBox); - shrink_pixels->set_min(0); - shrink_pixels->set_max(10); - shrink_pixels->set_step(1); - shrink_pixels->set_value(0); - hb->add_child(shrink_pixels); - hb->add_spacer(); - hb->add_child(memnew(Label(TTR("Grow (Pixels): ")))); - grow_pixels = memnew(SpinBox); - grow_pixels->set_min(0); - grow_pixels->set_max(10); - grow_pixels->set_step(1); - grow_pixels->set_value(2); - hb->add_child(grow_pixels); - hb->add_spacer(); - update_preview = memnew(Button); - update_preview->set_text(TTR("Update Preview")); - update_preview->connect("pressed", callable_mp(this, &SpriteEditor::_update_mesh_data)); - hb->add_child(update_preview); - vb->add_margin_child(TTR("Settings:"), hb); - - add_child(debug_uv_dialog); -} - -void SpriteEditorPlugin::edit(Object *p_object) { - - sprite_editor->edit(Object::cast_to(p_object)); -} - -bool SpriteEditorPlugin::handles(Object *p_object) const { - - return p_object->is_class("Sprite2D"); -} - -void SpriteEditorPlugin::make_visible(bool p_visible) { - - if (p_visible) { - sprite_editor->options->show(); - } else { - - sprite_editor->options->hide(); - sprite_editor->edit(NULL); - } -} - -SpriteEditorPlugin::SpriteEditorPlugin(EditorNode *p_node) { - - editor = p_node; - sprite_editor = memnew(SpriteEditor); - editor->get_viewport()->add_child(sprite_editor); - make_visible(false); - - //sprite_editor->options->hide(); -} - -SpriteEditorPlugin::~SpriteEditorPlugin() { -} diff --git a/editor/plugins/sprite_editor_plugin.h b/editor/plugins/sprite_editor_plugin.h deleted file mode 100644 index b55446469c..0000000000 --- a/editor/plugins/sprite_editor_plugin.h +++ /dev/null @@ -1,117 +0,0 @@ -/*************************************************************************/ -/* sprite_editor_plugin.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef SPRITE_EDITOR_PLUGIN_H -#define SPRITE_EDITOR_PLUGIN_H - -#include "editor/editor_node.h" -#include "editor/editor_plugin.h" -#include "scene/2d/sprite_2d.h" -#include "scene/gui/spin_box.h" - -class SpriteEditor : public Control { - - GDCLASS(SpriteEditor, Control); - - enum Menu { - MENU_OPTION_CONVERT_TO_MESH_2D, - MENU_OPTION_CONVERT_TO_POLYGON_2D, - MENU_OPTION_CREATE_COLLISION_POLY_2D, - MENU_OPTION_CREATE_LIGHT_OCCLUDER_2D - }; - - Menu selected_menu_item; - - Sprite2D *node; - - MenuButton *options; - - ConfirmationDialog *outline_dialog; - - AcceptDialog *err_dialog; - - ConfirmationDialog *debug_uv_dialog; - Control *debug_uv; - Vector uv_lines; - Vector> outline_lines; - Vector> computed_outline_lines; - Vector computed_vertices; - Vector computed_uv; - Vector computed_indices; - - SpinBox *simplification; - SpinBox *grow_pixels; - SpinBox *shrink_pixels; - Button *update_preview; - - void _menu_option(int p_option); - - //void _create_uv_lines(); - friend class SpriteEditorPlugin; - - void _debug_uv_draw(); - void _update_mesh_data(); - - void _create_node(); - void _convert_to_mesh_2d_node(); - void _convert_to_polygon_2d_node(); - void _create_collision_polygon_2d_node(); - void _create_light_occluder_2d_node(); - - void _add_as_sibling_or_child(Node *p_own_node, Node *p_new_node); - -protected: - void _node_removed(Node *p_node); - static void _bind_methods(); - -public: - void edit(Sprite2D *p_sprite); - SpriteEditor(); -}; - -class SpriteEditorPlugin : public EditorPlugin { - - GDCLASS(SpriteEditorPlugin, EditorPlugin); - - SpriteEditor *sprite_editor; - EditorNode *editor; - -public: - virtual String get_name() const { return "Sprite"; } - bool has_main_screen() const { return false; } - virtual void edit(Object *p_object); - virtual bool handles(Object *p_object) const; - virtual void make_visible(bool p_visible); - - SpriteEditorPlugin(EditorNode *p_node); - ~SpriteEditorPlugin(); -}; - -#endif // SPRITE_EDITOR_PLUGIN_H diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 186a569183..7d429eb4b5 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -42,8 +42,8 @@ #include "editor/multi_node_edit.h" #include "editor/plugins/animation_player_editor_plugin.h" #include "editor/plugins/canvas_item_editor_plugin.h" +#include "editor/plugins/node_3d_editor_plugin.h" #include "editor/plugins/script_editor_plugin.h" -#include "editor/plugins/spatial_editor_plugin.h" #include "scene/main/window.h" #include "scene/resources/packed_scene.h" #include "servers/display_server.h" diff --git a/editor/spatial_editor_gizmos.cpp b/editor/spatial_editor_gizmos.cpp deleted file mode 100644 index a7d014be33..0000000000 --- a/editor/spatial_editor_gizmos.cpp +++ /dev/null @@ -1,4510 +0,0 @@ -/*************************************************************************/ -/* spatial_editor_gizmos.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "spatial_editor_gizmos.h" - -#include "core/math/geometry.h" -#include "core/math/quick_hull.h" -#include "scene/3d/audio_stream_player_3d.h" -#include "scene/3d/baked_lightmap.h" -#include "scene/3d/collision_polygon_3d.h" -#include "scene/3d/collision_shape_3d.h" -#include "scene/3d/cpu_particles_3d.h" -#include "scene/3d/gi_probe.h" -#include "scene/3d/gpu_particles_3d.h" -#include "scene/3d/light_3d.h" -#include "scene/3d/listener_3d.h" -#include "scene/3d/mesh_instance_3d.h" -#include "scene/3d/navigation_region_3d.h" -#include "scene/3d/physics_joint_3d.h" -#include "scene/3d/position_3d.h" -#include "scene/3d/ray_cast_3d.h" -#include "scene/3d/reflection_probe.h" -#include "scene/3d/soft_body_3d.h" -#include "scene/3d/spring_arm_3d.h" -#include "scene/3d/sprite_3d.h" -#include "scene/3d/vehicle_body.h" -#include "scene/3d/visibility_notifier_3d.h" -#include "scene/resources/box_shape_3d.h" -#include "scene/resources/capsule_shape_3d.h" -#include "scene/resources/concave_polygon_shape_3d.h" -#include "scene/resources/convex_polygon_shape_3d.h" -#include "scene/resources/cylinder_shape_3d.h" -#include "scene/resources/height_map_shape.h" -#include "scene/resources/primitive_meshes.h" -#include "scene/resources/ray_shape.h" -#include "scene/resources/sphere_shape_3d.h" -#include "scene/resources/surface_tool.h" -#include "scene/resources/world_margin_shape_3d.h" - -#define HANDLE_HALF_SIZE 9.5 - -bool EditorNode3DGizmo::is_editable() const { - - ERR_FAIL_COND_V(!spatial_node, false); - Node *edited_root = spatial_node->get_tree()->get_edited_scene_root(); - if (spatial_node == edited_root) - return true; - if (spatial_node->get_owner() == edited_root) - return true; - - if (edited_root->is_editable_instance(spatial_node->get_owner())) - return true; - - return false; -} - -void EditorNode3DGizmo::clear() { - - for (int i = 0; i < instances.size(); i++) { - - if (instances[i].instance.is_valid()) - VS::get_singleton()->free(instances[i].instance); - } - - billboard_handle = false; - collision_segments.clear(); - collision_mesh = Ref(); - instances.clear(); - handles.clear(); - secondary_handles.clear(); -} - -void EditorNode3DGizmo::redraw() { - - if (get_script_instance() && get_script_instance()->has_method("redraw")) { - get_script_instance()->call("redraw"); - return; - } - - ERR_FAIL_COND(!gizmo_plugin); - gizmo_plugin->redraw(this); -} - -String EditorNode3DGizmo::get_handle_name(int p_idx) const { - - if (get_script_instance() && get_script_instance()->has_method("get_handle_name")) { - return get_script_instance()->call("get_handle_name", p_idx); - } - - ERR_FAIL_COND_V(!gizmo_plugin, ""); - return gizmo_plugin->get_handle_name(this, p_idx); -} - -bool EditorNode3DGizmo::is_handle_highlighted(int p_idx) const { - - if (get_script_instance() && get_script_instance()->has_method("is_handle_highlighted")) { - return get_script_instance()->call("is_handle_highlighted", p_idx); - } - - ERR_FAIL_COND_V(!gizmo_plugin, false); - return gizmo_plugin->is_handle_highlighted(this, p_idx); -} - -Variant EditorNode3DGizmo::get_handle_value(int p_idx) { - - if (get_script_instance() && get_script_instance()->has_method("get_handle_value")) { - return get_script_instance()->call("get_handle_value", p_idx); - } - - ERR_FAIL_COND_V(!gizmo_plugin, Variant()); - return gizmo_plugin->get_handle_value(this, p_idx); -} - -void EditorNode3DGizmo::set_handle(int p_idx, Camera3D *p_camera, const Point2 &p_point) { - - if (get_script_instance() && get_script_instance()->has_method("set_handle")) { - get_script_instance()->call("set_handle", p_idx, p_camera, p_point); - return; - } - - ERR_FAIL_COND(!gizmo_plugin); - gizmo_plugin->set_handle(this, p_idx, p_camera, p_point); -} - -void EditorNode3DGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p_cancel) { - - if (get_script_instance() && get_script_instance()->has_method("commit_handle")) { - get_script_instance()->call("commit_handle", p_idx, p_restore, p_cancel); - return; - } - - ERR_FAIL_COND(!gizmo_plugin); - gizmo_plugin->commit_handle(this, p_idx, p_restore, p_cancel); -} - -void EditorNode3DGizmo::set_spatial_node(Node3D *p_node) { - - ERR_FAIL_NULL(p_node); - spatial_node = p_node; -} - -void EditorNode3DGizmo::Instance::create_instance(Node3D *p_base, bool p_hidden) { - - instance = VS::get_singleton()->instance_create2(mesh->get_rid(), p_base->get_world()->get_scenario()); - VS::get_singleton()->instance_attach_object_instance_id(instance, p_base->get_instance_id()); - if (skin_reference.is_valid()) { - VS::get_singleton()->instance_attach_skeleton(instance, skin_reference->get_skeleton()); - } - if (extra_margin) - VS::get_singleton()->instance_set_extra_visibility_margin(instance, 1); - VS::get_singleton()->instance_geometry_set_cast_shadows_setting(instance, VS::SHADOW_CASTING_SETTING_OFF); - int layer = p_hidden ? 0 : 1 << Node3DEditorViewport::GIZMO_EDIT_LAYER; - VS::get_singleton()->instance_set_layer_mask(instance, layer); //gizmos are 26 -} - -void EditorNode3DGizmo::add_mesh(const Ref &p_mesh, bool p_billboard, const Ref &p_skin_reference, const Ref &p_material) { - - ERR_FAIL_COND(!spatial_node); - Instance ins; - - ins.billboard = p_billboard; - ins.mesh = p_mesh; - ins.skin_reference = p_skin_reference; - ins.material = p_material; - if (valid) { - ins.create_instance(spatial_node, hidden); - VS::get_singleton()->instance_set_transform(ins.instance, spatial_node->get_global_transform()); - if (ins.material.is_valid()) { - VS::get_singleton()->instance_geometry_set_material_override(ins.instance, p_material->get_rid()); - } - } - - instances.push_back(ins); -} - -void EditorNode3DGizmo::add_lines(const Vector &p_lines, const Ref &p_material, bool p_billboard, const Color &p_modulate) { - if (p_lines.empty()) { - return; - } - - ERR_FAIL_COND(!spatial_node); - Instance ins; - - Ref mesh = memnew(ArrayMesh); - Array a; - a.resize(Mesh::ARRAY_MAX); - - a[Mesh::ARRAY_VERTEX] = p_lines; - - Vector color; - color.resize(p_lines.size()); - { - Color *w = color.ptrw(); - for (int i = 0; i < p_lines.size(); i++) { - if (is_selected()) - w[i] = Color(1, 1, 1, 0.8) * p_modulate; - else - w[i] = Color(1, 1, 1, 0.2) * p_modulate; - } - } - - a[Mesh::ARRAY_COLOR] = color; - - mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, a); - mesh->surface_set_material(0, p_material); - - if (p_billboard) { - float md = 0; - for (int i = 0; i < p_lines.size(); i++) { - - md = MAX(0, p_lines[i].length()); - } - if (md) { - mesh->set_custom_aabb(AABB(Vector3(-md, -md, -md), Vector3(md, md, md) * 2.0)); - } - } - - ins.billboard = p_billboard; - ins.mesh = mesh; - if (valid) { - ins.create_instance(spatial_node, hidden); - VS::get_singleton()->instance_set_transform(ins.instance, spatial_node->get_global_transform()); - } - - instances.push_back(ins); -} - -void EditorNode3DGizmo::add_unscaled_billboard(const Ref &p_material, float p_scale, const Color &p_modulate) { - - ERR_FAIL_COND(!spatial_node); - Instance ins; - - Vector vs; - Vector uv; - Vector colors; - - vs.push_back(Vector3(-p_scale, p_scale, 0)); - vs.push_back(Vector3(p_scale, p_scale, 0)); - vs.push_back(Vector3(p_scale, -p_scale, 0)); - vs.push_back(Vector3(-p_scale, -p_scale, 0)); - - uv.push_back(Vector2(0, 0)); - uv.push_back(Vector2(1, 0)); - uv.push_back(Vector2(1, 1)); - uv.push_back(Vector2(0, 1)); - - colors.push_back(p_modulate); - colors.push_back(p_modulate); - colors.push_back(p_modulate); - colors.push_back(p_modulate); - - Ref mesh = memnew(ArrayMesh); - Array a; - a.resize(Mesh::ARRAY_MAX); - a[Mesh::ARRAY_VERTEX] = vs; - a[Mesh::ARRAY_TEX_UV] = uv; - Vector indices; - indices.push_back(0); - indices.push_back(1); - indices.push_back(2); - indices.push_back(0); - indices.push_back(2); - indices.push_back(3); - a[Mesh::ARRAY_INDEX] = indices; - a[Mesh::ARRAY_COLOR] = colors; - mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a); - mesh->surface_set_material(0, p_material); - - float md = 0; - for (int i = 0; i < vs.size(); i++) { - - md = MAX(0, vs[i].length()); - } - if (md) { - mesh->set_custom_aabb(AABB(Vector3(-md, -md, -md), Vector3(md, md, md) * 2.0)); - } - - selectable_icon_size = p_scale; - mesh->set_custom_aabb(AABB(Vector3(-selectable_icon_size, -selectable_icon_size, -selectable_icon_size) * 100.0f, Vector3(selectable_icon_size, selectable_icon_size, selectable_icon_size) * 200.0f)); - - ins.mesh = mesh; - ins.unscaled = true; - ins.billboard = true; - if (valid) { - ins.create_instance(spatial_node, hidden); - VS::get_singleton()->instance_set_transform(ins.instance, spatial_node->get_global_transform()); - } - - selectable_icon_size = p_scale; - - instances.push_back(ins); -} - -void EditorNode3DGizmo::add_collision_triangles(const Ref &p_tmesh) { - collision_mesh = p_tmesh; -} - -void EditorNode3DGizmo::add_collision_segments(const Vector &p_lines) { - - int from = collision_segments.size(); - collision_segments.resize(from + p_lines.size()); - for (int i = 0; i < p_lines.size(); i++) { - - collision_segments.write[from + i] = p_lines[i]; - } -} - -void EditorNode3DGizmo::add_handles(const Vector &p_handles, const Ref &p_material, bool p_billboard, bool p_secondary) { - - billboard_handle = p_billboard; - - if (!is_selected() || !is_editable()) - return; - - ERR_FAIL_COND(!spatial_node); - - Instance ins; - - Ref mesh = memnew(ArrayMesh); - - Array a; - a.resize(VS::ARRAY_MAX); - a[VS::ARRAY_VERTEX] = p_handles; - Vector colors; - { - colors.resize(p_handles.size()); - Color *w = colors.ptrw(); - for (int i = 0; i < p_handles.size(); i++) { - - Color col(1, 1, 1, 1); - if (is_handle_highlighted(i)) - col = Color(0, 0, 1, 0.9); - - if (Node3DEditor::get_singleton()->get_over_gizmo_handle() != i) - col.a = 0.8; - - w[i] = col; - } - } - a[VS::ARRAY_COLOR] = colors; - mesh->add_surface_from_arrays(Mesh::PRIMITIVE_POINTS, a); - mesh->surface_set_material(0, p_material); - - if (p_billboard) { - float md = 0; - for (int i = 0; i < p_handles.size(); i++) { - - md = MAX(0, p_handles[i].length()); - } - if (md) { - mesh->set_custom_aabb(AABB(Vector3(-md, -md, -md), Vector3(md, md, md) * 2.0)); - } - } - - ins.mesh = mesh; - ins.billboard = p_billboard; - ins.extra_margin = true; - if (valid) { - ins.create_instance(spatial_node, hidden); - VS::get_singleton()->instance_set_transform(ins.instance, spatial_node->get_global_transform()); - } - instances.push_back(ins); - if (!p_secondary) { - int chs = handles.size(); - handles.resize(chs + p_handles.size()); - for (int i = 0; i < p_handles.size(); i++) { - handles.write[i + chs] = p_handles[i]; - } - } else { - - int chs = secondary_handles.size(); - secondary_handles.resize(chs + p_handles.size()); - for (int i = 0; i < p_handles.size(); i++) { - secondary_handles.write[i + chs] = p_handles[i]; - } - } -} - -void EditorNode3DGizmo::add_solid_box(Ref &p_material, Vector3 p_size, Vector3 p_position) { - ERR_FAIL_COND(!spatial_node); - - CubeMesh cubem; - cubem.set_size(p_size); - - Array arrays = cubem.surface_get_arrays(0); - PackedVector3Array vertex = arrays[VS::ARRAY_VERTEX]; - Vector3 *w = vertex.ptrw(); - - for (int i = 0; i < vertex.size(); ++i) { - w[i] += p_position; - } - - arrays[VS::ARRAY_VERTEX] = vertex; - - Ref m = memnew(ArrayMesh); - m->add_surface_from_arrays(cubem.surface_get_primitive_type(0), arrays); - m->surface_set_material(0, p_material); - add_mesh(m); -} - -bool EditorNode3DGizmo::intersect_frustum(const Camera3D *p_camera, const Vector &p_frustum) { - - ERR_FAIL_COND_V(!spatial_node, false); - ERR_FAIL_COND_V(!valid, false); - - if (hidden && !gizmo_plugin->is_selectable_when_hidden()) return false; - - if (selectable_icon_size > 0.0f) { - Vector3 origin = spatial_node->get_global_transform().get_origin(); - - const Plane *p = p_frustum.ptr(); - int fc = p_frustum.size(); - - bool any_out = false; - - for (int j = 0; j < fc; j++) { - - if (p[j].is_point_over(origin)) { - any_out = true; - break; - } - } - - return !any_out; - } - - if (collision_segments.size()) { - - const Plane *p = p_frustum.ptr(); - int fc = p_frustum.size(); - - int vc = collision_segments.size(); - const Vector3 *vptr = collision_segments.ptr(); - Transform t = spatial_node->get_global_transform(); - - bool any_out = false; - for (int j = 0; j < fc; j++) { - for (int i = 0; i < vc; i++) { - Vector3 v = t.xform(vptr[i]); - if (p[j].is_point_over(v)) { - any_out = true; - break; - } - } - if (any_out) break; - } - - if (!any_out) return true; - } - - if (collision_mesh.is_valid()) { - Transform t = spatial_node->get_global_transform(); - - Vector3 mesh_scale = t.get_basis().get_scale(); - t.orthonormalize(); - - Transform it = t.affine_inverse(); - - Vector transformed_frustum; - - for (int i = 0; i < 4; i++) { - transformed_frustum.push_back(it.xform(p_frustum[i])); - } - - if (collision_mesh->inside_convex_shape(transformed_frustum.ptr(), transformed_frustum.size(), mesh_scale)) { - return true; - } - } - - return false; -} - -bool EditorNode3DGizmo::intersect_ray(Camera3D *p_camera, const Point2 &p_point, Vector3 &r_pos, Vector3 &r_normal, int *r_gizmo_handle, bool p_sec_first) { - - ERR_FAIL_COND_V(!spatial_node, false); - ERR_FAIL_COND_V(!valid, false); - - if (hidden && !gizmo_plugin->is_selectable_when_hidden()) return false; - - if (r_gizmo_handle && !hidden) { - - Transform t = spatial_node->get_global_transform(); - if (billboard_handle) { - t.set_look_at(t.origin, t.origin - p_camera->get_transform().basis.get_axis(2), p_camera->get_transform().basis.get_axis(1)); - } - - float min_d = 1e20; - int idx = -1; - - for (int i = 0; i < secondary_handles.size(); i++) { - - Vector3 hpos = t.xform(secondary_handles[i]); - Vector2 p = p_camera->unproject_position(hpos); - - if (p.distance_to(p_point) < HANDLE_HALF_SIZE) { - - real_t dp = p_camera->get_transform().origin.distance_to(hpos); - if (dp < min_d) { - - r_pos = t.xform(hpos); - r_normal = p_camera->get_transform().basis.get_axis(2); - min_d = dp; - idx = i + handles.size(); - } - } - } - - if (p_sec_first && idx != -1) { - - *r_gizmo_handle = idx; - return true; - } - - min_d = 1e20; - - for (int i = 0; i < handles.size(); i++) { - - Vector3 hpos = t.xform(handles[i]); - Vector2 p = p_camera->unproject_position(hpos); - - if (p.distance_to(p_point) < HANDLE_HALF_SIZE) { - - real_t dp = p_camera->get_transform().origin.distance_to(hpos); - if (dp < min_d) { - - r_pos = t.xform(hpos); - r_normal = p_camera->get_transform().basis.get_axis(2); - min_d = dp; - idx = i; - } - } - } - - if (idx >= 0) { - *r_gizmo_handle = idx; - return true; - } - } - - if (selectable_icon_size > 0.0f) { - - Transform t = spatial_node->get_global_transform(); - Vector3 camera_position = p_camera->get_camera_transform().origin; - if (camera_position.distance_squared_to(t.origin) > 0.01) { - t.set_look_at(t.origin, camera_position, Vector3(0, 1, 0)); - } - - float scale = t.origin.distance_to(p_camera->get_camera_transform().origin); - - if (p_camera->get_projection() == Camera3D::PROJECTION_ORTHOGONAL) { - float aspect = p_camera->get_viewport()->get_visible_rect().size.aspect(); - float size = p_camera->get_size(); - scale = size / aspect; - } - - Point2 center = p_camera->unproject_position(t.origin); - - Transform orig_camera_transform = p_camera->get_camera_transform(); - - if (orig_camera_transform.origin.distance_squared_to(t.origin) > 0.01 && - ABS(orig_camera_transform.basis.get_axis(Vector3::AXIS_Z).dot(Vector3(0, 1, 0))) < 0.99) { - p_camera->look_at(t.origin, Vector3(0, 1, 0)); - } - - Vector3 c0 = t.xform(Vector3(selectable_icon_size, selectable_icon_size, 0) * scale); - Vector3 c1 = t.xform(Vector3(-selectable_icon_size, -selectable_icon_size, 0) * scale); - - Point2 p0 = p_camera->unproject_position(c0); - Point2 p1 = p_camera->unproject_position(c1); - - p_camera->set_global_transform(orig_camera_transform); - - Rect2 rect(p0, (p1 - p0).abs()); - - rect.set_position(center - rect.get_size() / 2.0); - - if (rect.has_point(p_point)) { - r_pos = t.origin; - r_normal = -p_camera->project_ray_normal(p_point); - return true; - } - - return false; - } - - if (collision_segments.size()) { - - Plane camp(p_camera->get_transform().origin, (-p_camera->get_transform().basis.get_axis(2)).normalized()); - - int vc = collision_segments.size(); - const Vector3 *vptr = collision_segments.ptr(); - Transform t = spatial_node->get_global_transform(); - if (billboard_handle) { - t.set_look_at(t.origin, t.origin - p_camera->get_transform().basis.get_axis(2), p_camera->get_transform().basis.get_axis(1)); - } - - Vector3 cp; - float cpd = 1e20; - - for (int i = 0; i < vc / 2; i++) { - - Vector3 a = t.xform(vptr[i * 2 + 0]); - Vector3 b = t.xform(vptr[i * 2 + 1]); - Vector2 s[2]; - s[0] = p_camera->unproject_position(a); - s[1] = p_camera->unproject_position(b); - - Vector2 p = Geometry::get_closest_point_to_segment_2d(p_point, s); - - float pd = p.distance_to(p_point); - - if (pd < cpd) { - - float d = s[0].distance_to(s[1]); - Vector3 tcp; - if (d > 0) { - - float d2 = s[0].distance_to(p) / d; - tcp = a + (b - a) * d2; - - } else { - tcp = a; - } - - if (camp.distance_to(tcp) < p_camera->get_znear()) - continue; - cp = tcp; - cpd = pd; - } - } - - if (cpd < 8) { - - r_pos = cp; - r_normal = -p_camera->project_ray_normal(p_point); - return true; - } - - return false; - } - - if (collision_mesh.is_valid()) { - Transform gt = spatial_node->get_global_transform(); - - if (billboard_handle) { - gt.set_look_at(gt.origin, gt.origin - p_camera->get_transform().basis.get_axis(2), p_camera->get_transform().basis.get_axis(1)); - } - - Transform ai = gt.affine_inverse(); - Vector3 ray_from = ai.xform(p_camera->project_ray_origin(p_point)); - Vector3 ray_dir = ai.basis.xform(p_camera->project_ray_normal(p_point)).normalized(); - Vector3 rpos, rnorm; - - if (collision_mesh->intersect_ray(ray_from, ray_dir, rpos, rnorm)) { - - r_pos = gt.xform(rpos); - r_normal = gt.basis.xform(rnorm).normalized(); - return true; - } - } - - return false; -} - -void EditorNode3DGizmo::create() { - - ERR_FAIL_COND(!spatial_node); - ERR_FAIL_COND(valid); - valid = true; - - for (int i = 0; i < instances.size(); i++) { - - instances.write[i].create_instance(spatial_node, hidden); - } - - transform(); -} - -void EditorNode3DGizmo::transform() { - - ERR_FAIL_COND(!spatial_node); - ERR_FAIL_COND(!valid); - for (int i = 0; i < instances.size(); i++) { - VS::get_singleton()->instance_set_transform(instances[i].instance, spatial_node->get_global_transform()); - } -} - -void EditorNode3DGizmo::free() { - - ERR_FAIL_COND(!spatial_node); - ERR_FAIL_COND(!valid); - - for (int i = 0; i < instances.size(); i++) { - - if (instances[i].instance.is_valid()) - VS::get_singleton()->free(instances[i].instance); - instances.write[i].instance = RID(); - } - - clear(); - - valid = false; -} - -void EditorNode3DGizmo::set_hidden(bool p_hidden) { - hidden = p_hidden; - int layer = hidden ? 0 : 1 << Node3DEditorViewport::GIZMO_EDIT_LAYER; - for (int i = 0; i < instances.size(); ++i) { - VS::get_singleton()->instance_set_layer_mask(instances[i].instance, layer); - } -} - -void EditorNode3DGizmo::set_plugin(EditorNode3DGizmoPlugin *p_plugin) { - gizmo_plugin = p_plugin; -} - -void EditorNode3DGizmo::_bind_methods() { - - ClassDB::bind_method(D_METHOD("add_lines", "lines", "material", "billboard", "modulate"), &EditorNode3DGizmo::add_lines, DEFVAL(false), DEFVAL(Color(1, 1, 1))); - ClassDB::bind_method(D_METHOD("add_mesh", "mesh", "billboard", "skeleton", "material"), &EditorNode3DGizmo::add_mesh, DEFVAL(false), DEFVAL(Ref()), DEFVAL(Variant())); - ClassDB::bind_method(D_METHOD("add_collision_segments", "segments"), &EditorNode3DGizmo::add_collision_segments); - ClassDB::bind_method(D_METHOD("add_collision_triangles", "triangles"), &EditorNode3DGizmo::add_collision_triangles); - ClassDB::bind_method(D_METHOD("add_unscaled_billboard", "material", "default_scale", "modulate"), &EditorNode3DGizmo::add_unscaled_billboard, DEFVAL(1), DEFVAL(Color(1, 1, 1))); - ClassDB::bind_method(D_METHOD("add_handles", "handles", "material", "billboard", "secondary"), &EditorNode3DGizmo::add_handles, DEFVAL(false), DEFVAL(false)); - ClassDB::bind_method(D_METHOD("set_spatial_node", "node"), &EditorNode3DGizmo::_set_spatial_node); - ClassDB::bind_method(D_METHOD("get_spatial_node"), &EditorNode3DGizmo::get_spatial_node); - ClassDB::bind_method(D_METHOD("get_plugin"), &EditorNode3DGizmo::get_plugin); - ClassDB::bind_method(D_METHOD("clear"), &EditorNode3DGizmo::clear); - ClassDB::bind_method(D_METHOD("set_hidden", "hidden"), &EditorNode3DGizmo::set_hidden); - - BIND_VMETHOD(MethodInfo("redraw")); - BIND_VMETHOD(MethodInfo(Variant::STRING, "get_handle_name", PropertyInfo(Variant::INT, "index"))); - BIND_VMETHOD(MethodInfo(Variant::BOOL, "is_handle_highlighted", PropertyInfo(Variant::INT, "index"))); - - MethodInfo hvget(Variant::NIL, "get_handle_value", PropertyInfo(Variant::INT, "index")); - hvget.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; - BIND_VMETHOD(hvget); - - BIND_VMETHOD(MethodInfo("set_handle", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Camera3D"), PropertyInfo(Variant::VECTOR2, "point"))); - MethodInfo cm = MethodInfo("commit_handle", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::NIL, "restore"), PropertyInfo(Variant::BOOL, "cancel")); - cm.default_arguments.push_back(false); - BIND_VMETHOD(cm); -} - -EditorNode3DGizmo::EditorNode3DGizmo() { - valid = false; - billboard_handle = false; - hidden = false; - base = NULL; - selected = false; - instanced = false; - spatial_node = NULL; - gizmo_plugin = NULL; - selectable_icon_size = -1.0f; -} - -EditorNode3DGizmo::~EditorNode3DGizmo() { - - if (gizmo_plugin != NULL) gizmo_plugin->unregister_gizmo(this); - clear(); -} - -Vector3 EditorNode3DGizmo::get_handle_pos(int p_idx) const { - - ERR_FAIL_INDEX_V(p_idx, handles.size(), Vector3()); - - return handles[p_idx]; -} - -//// light gizmo - -LightNode3DGizmoPlugin::LightNode3DGizmoPlugin() { - - // Enable vertex colors for the materials below as the gizmo color depends on the light color. - create_material("lines_primary", Color(1, 1, 1), false, false, true); - create_material("lines_secondary", Color(1, 1, 1, 0.35), false, false, true); - create_material("lines_billboard", Color(1, 1, 1), true, false, true); - - create_icon_material("light_directional_icon", Node3DEditor::get_singleton()->get_theme_icon("GizmoDirectionalLight", "EditorIcons")); - create_icon_material("light_omni_icon", Node3DEditor::get_singleton()->get_theme_icon("GizmoLight", "EditorIcons")); - create_icon_material("light_spot_icon", Node3DEditor::get_singleton()->get_theme_icon("GizmoSpotLight", "EditorIcons")); - - create_handle_material("handles"); - create_handle_material("handles_billboard", true); -} - -bool LightNode3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != NULL; -} - -String LightNode3DGizmoPlugin::get_name() const { - return "Lights"; -} - -int LightNode3DGizmoPlugin::get_priority() const { - return -1; -} - -String LightNode3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { - - if (p_idx == 0) - return "Radius"; - else - return "Aperture"; -} - -Variant LightNode3DGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { - - Light3D *light = Object::cast_to(p_gizmo->get_spatial_node()); - if (p_idx == 0) - return light->get_param(Light3D::PARAM_RANGE); - if (p_idx == 1) - return light->get_param(Light3D::PARAM_SPOT_ANGLE); - - return Variant(); -} - -static float _find_closest_angle_to_half_pi_arc(const Vector3 &p_from, const Vector3 &p_to, float p_arc_radius, const Transform &p_arc_xform) { - - //bleh, discrete is simpler - static const int arc_test_points = 64; - float min_d = 1e20; - Vector3 min_p; - - for (int i = 0; i < arc_test_points; i++) { - - float a = i * Math_PI * 0.5 / arc_test_points; - float an = (i + 1) * Math_PI * 0.5 / arc_test_points; - Vector3 p = Vector3(Math::cos(a), 0, -Math::sin(a)) * p_arc_radius; - Vector3 n = Vector3(Math::cos(an), 0, -Math::sin(an)) * p_arc_radius; - - Vector3 ra, rb; - Geometry::get_closest_points_between_segments(p, n, p_from, p_to, ra, rb); - - float d = ra.distance_to(rb); - if (d < min_d) { - min_d = d; - min_p = ra; - } - } - - //min_p = p_arc_xform.affine_inverse().xform(min_p); - float a = (Math_PI * 0.5) - Vector2(min_p.x, -min_p.z).angle(); - return a * 180.0 / Math_PI; -} - -void LightNode3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) { - - Light3D *light = Object::cast_to(p_gizmo->get_spatial_node()); - Transform gt = light->get_global_transform(); - Transform gi = gt.affine_inverse(); - - Vector3 ray_from = p_camera->project_ray_origin(p_point); - Vector3 ray_dir = p_camera->project_ray_normal(p_point); - - Vector3 s[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 4096) }; - if (p_idx == 0) { - - if (Object::cast_to(light)) { - Vector3 ra, rb; - Geometry::get_closest_points_between_segments(Vector3(), Vector3(0, 0, -4096), s[0], s[1], ra, rb); - - float d = -ra.z; - if (Node3DEditor::get_singleton()->is_snap_enabled()) { - d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); - } - - if (d <= 0) // Equal is here for negative zero. - d = 0; - - light->set_param(Light3D::PARAM_RANGE, d); - } else if (Object::cast_to(light)) { - - Plane cp = Plane(gt.origin, p_camera->get_transform().basis.get_axis(2)); - - Vector3 inters; - if (cp.intersects_ray(ray_from, ray_dir, &inters)) { - - float r = inters.distance_to(gt.origin); - if (Node3DEditor::get_singleton()->is_snap_enabled()) { - r = Math::stepify(r, Node3DEditor::get_singleton()->get_translate_snap()); - } - - light->set_param(Light3D::PARAM_RANGE, r); - } - } - - } else if (p_idx == 1) { - - float a = _find_closest_angle_to_half_pi_arc(s[0], s[1], light->get_param(Light3D::PARAM_RANGE), gt); - light->set_param(Light3D::PARAM_SPOT_ANGLE, CLAMP(a, 0.01, 89.99)); - } -} - -void LightNode3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { - - Light3D *light = Object::cast_to(p_gizmo->get_spatial_node()); - if (p_cancel) { - - light->set_param(p_idx == 0 ? Light3D::PARAM_RANGE : Light3D::PARAM_SPOT_ANGLE, p_restore); - - } else if (p_idx == 0) { - - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); - ur->create_action(TTR("Change Light Radius")); - ur->add_do_method(light, "set_param", Light3D::PARAM_RANGE, light->get_param(Light3D::PARAM_RANGE)); - ur->add_undo_method(light, "set_param", Light3D::PARAM_RANGE, p_restore); - ur->commit_action(); - } else if (p_idx == 1) { - - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); - ur->create_action(TTR("Change Light Radius")); - ur->add_do_method(light, "set_param", Light3D::PARAM_SPOT_ANGLE, light->get_param(Light3D::PARAM_SPOT_ANGLE)); - ur->add_undo_method(light, "set_param", Light3D::PARAM_SPOT_ANGLE, p_restore); - ur->commit_action(); - } -} - -void LightNode3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - - Light3D *light = Object::cast_to(p_gizmo->get_spatial_node()); - - Color color = light->get_color(); - // Make the gizmo color as bright as possible for better visibility - color.set_hsv(color.get_h(), color.get_s(), 1); - - p_gizmo->clear(); - - if (Object::cast_to(light)) { - - Ref material = get_material("lines_primary", p_gizmo); - Ref icon = get_material("light_directional_icon", p_gizmo); - - const int arrow_points = 7; - const float arrow_length = 1.5; - - Vector3 arrow[arrow_points] = { - Vector3(0, 0, -1), - Vector3(0, 0.8, 0), - Vector3(0, 0.3, 0), - Vector3(0, 0.3, arrow_length), - Vector3(0, -0.3, arrow_length), - Vector3(0, -0.3, 0), - Vector3(0, -0.8, 0) - }; - - int arrow_sides = 2; - - Vector lines; - - for (int i = 0; i < arrow_sides; i++) { - for (int j = 0; j < arrow_points; j++) { - Basis ma(Vector3(0, 0, 1), Math_PI * i / arrow_sides); - - Vector3 v1 = arrow[j] - Vector3(0, 0, arrow_length); - Vector3 v2 = arrow[(j + 1) % arrow_points] - Vector3(0, 0, arrow_length); - - lines.push_back(ma.xform(v1)); - lines.push_back(ma.xform(v2)); - } - } - - p_gizmo->add_lines(lines, material, false, color); - p_gizmo->add_unscaled_billboard(icon, 0.05, color); - } - - if (Object::cast_to(light)) { - - // Use both a billboard circle and 3 non-billboard circles for a better sphere-like representation - const Ref lines_material = get_material("lines_secondary", p_gizmo); - const Ref lines_billboard_material = get_material("lines_billboard", p_gizmo); - const Ref icon = get_material("light_omni_icon", p_gizmo); - - OmniLight3D *on = Object::cast_to(light); - const float r = on->get_param(Light3D::PARAM_RANGE); - Vector points; - Vector points_billboard; - - for (int i = 0; i < 120; i++) { - - // Create a circle - const float ra = Math::deg2rad((float)(i * 3)); - const float rb = Math::deg2rad((float)((i + 1) * 3)); - const Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r; - const Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r; - - // Draw axis-aligned circles - points.push_back(Vector3(a.x, 0, a.y)); - points.push_back(Vector3(b.x, 0, b.y)); - points.push_back(Vector3(0, a.x, a.y)); - points.push_back(Vector3(0, b.x, b.y)); - points.push_back(Vector3(a.x, a.y, 0)); - points.push_back(Vector3(b.x, b.y, 0)); - - // Draw a billboarded circle - points_billboard.push_back(Vector3(a.x, a.y, 0)); - points_billboard.push_back(Vector3(b.x, b.y, 0)); - } - - p_gizmo->add_lines(points, lines_material, true, color); - p_gizmo->add_lines(points_billboard, lines_billboard_material, true, color); - p_gizmo->add_unscaled_billboard(icon, 0.05, color); - - Vector handles; - handles.push_back(Vector3(r, 0, 0)); - p_gizmo->add_handles(handles, get_material("handles_billboard"), true); - } - - if (Object::cast_to(light)) { - - const Ref material_primary = get_material("lines_primary", p_gizmo); - const Ref material_secondary = get_material("lines_secondary", p_gizmo); - const Ref icon = get_material("light_spot_icon", p_gizmo); - - Vector points_primary; - Vector points_secondary; - SpotLight3D *sl = Object::cast_to(light); - - float r = sl->get_param(Light3D::PARAM_RANGE); - float w = r * Math::sin(Math::deg2rad(sl->get_param(Light3D::PARAM_SPOT_ANGLE))); - float d = r * Math::cos(Math::deg2rad(sl->get_param(Light3D::PARAM_SPOT_ANGLE))); - - for (int i = 0; i < 120; i++) { - - // Draw a circle - const float ra = Math::deg2rad((float)(i * 3)); - const float rb = Math::deg2rad((float)((i + 1) * 3)); - const Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * w; - const Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * w; - - points_primary.push_back(Vector3(a.x, a.y, -d)); - points_primary.push_back(Vector3(b.x, b.y, -d)); - - if (i % 15 == 0) { - // Draw 8 lines from the cone origin to the sides of the circle - points_secondary.push_back(Vector3(a.x, a.y, -d)); - points_secondary.push_back(Vector3()); - } - } - - points_primary.push_back(Vector3(0, 0, -r)); - points_primary.push_back(Vector3()); - - p_gizmo->add_lines(points_primary, material_primary, false, color); - p_gizmo->add_lines(points_secondary, material_secondary, false, color); - - const float ra = 16 * Math_PI * 2.0 / 64.0; - const Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * w; - - Vector handles; - handles.push_back(Vector3(0, 0, -r)); - handles.push_back(Vector3(a.x, a.y, -d)); - - p_gizmo->add_handles(handles, get_material("handles")); - p_gizmo->add_unscaled_billboard(icon, 0.05, color); - } -} - -////// - -//// player gizmo -AudioStreamPlayer3DNode3DGizmoPlugin::AudioStreamPlayer3DNode3DGizmoPlugin() { - - Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/stream_player_3d", Color(0.4, 0.8, 1)); - - create_icon_material("stream_player_3d_icon", Node3DEditor::get_singleton()->get_theme_icon("Gizmo3DSamplePlayer", "EditorIcons")); - create_material("stream_player_3d_material_primary", gizmo_color); - create_material("stream_player_3d_material_secondary", gizmo_color * Color(1, 1, 1, 0.35)); - create_handle_material("handles"); -} - -bool AudioStreamPlayer3DNode3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != NULL; -} - -String AudioStreamPlayer3DNode3DGizmoPlugin::get_name() const { - return "AudioStreamPlayer3D"; -} - -int AudioStreamPlayer3DNode3DGizmoPlugin::get_priority() const { - return -1; -} - -String AudioStreamPlayer3DNode3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { - - return "Emission Radius"; -} - -Variant AudioStreamPlayer3DNode3DGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { - AudioStreamPlayer3D *player = Object::cast_to(p_gizmo->get_spatial_node()); - return player->get_emission_angle(); -} - -void AudioStreamPlayer3DNode3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) { - - AudioStreamPlayer3D *player = Object::cast_to(p_gizmo->get_spatial_node()); - - Transform gt = player->get_global_transform(); - Transform gi = gt.affine_inverse(); - - Vector3 ray_from = p_camera->project_ray_origin(p_point); - Vector3 ray_dir = p_camera->project_ray_normal(p_point); - Vector3 ray_to = ray_from + ray_dir * 4096; - - ray_from = gi.xform(ray_from); - ray_to = gi.xform(ray_to); - - float closest_dist = 1e20; - float closest_angle = 1e20; - - for (int i = 0; i < 180; i++) { - - float a = i * Math_PI / 180.0; - float an = (i + 1) * Math_PI / 180.0; - - Vector3 from(Math::sin(a), 0, -Math::cos(a)); - Vector3 to(Math::sin(an), 0, -Math::cos(an)); - - Vector3 r1, r2; - Geometry::get_closest_points_between_segments(from, to, ray_from, ray_to, r1, r2); - float d = r1.distance_to(r2); - if (d < closest_dist) { - closest_dist = d; - closest_angle = i; - } - } - - if (closest_angle < 91) { - player->set_emission_angle(closest_angle); - } -} - -void AudioStreamPlayer3DNode3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { - - AudioStreamPlayer3D *player = Object::cast_to(p_gizmo->get_spatial_node()); - - if (p_cancel) { - - player->set_emission_angle(p_restore); - - } else { - - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); - ur->create_action(TTR("Change AudioStreamPlayer3D Emission Angle")); - ur->add_do_method(player, "set_emission_angle", player->get_emission_angle()); - ur->add_undo_method(player, "set_emission_angle", p_restore); - ur->commit_action(); - } -} - -void AudioStreamPlayer3DNode3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - - const AudioStreamPlayer3D *player = Object::cast_to(p_gizmo->get_spatial_node()); - - p_gizmo->clear(); - - const Ref icon = get_material("stream_player_3d_icon", p_gizmo); - - if (player->is_emission_angle_enabled()) { - - const float pc = player->get_emission_angle(); - const float ofs = -Math::cos(Math::deg2rad(pc)); - const float radius = Math::sin(Math::deg2rad(pc)); - - Vector points_primary; - points_primary.resize(200); - - for (int i = 0; i < 100; i++) { - - const float a = i * 2.0 * Math_PI / 100.0; - const float an = (i + 1) * 2.0 * Math_PI / 100.0; - - const Vector3 from(Math::sin(a) * radius, Math::cos(a) * radius, ofs); - const Vector3 to(Math::sin(an) * radius, Math::cos(an) * radius, ofs); - - points_primary.write[i * 2 + 0] = from; - points_primary.write[i * 2 + 1] = to; - } - - const Ref material_primary = get_material("stream_player_3d_material_primary", p_gizmo); - p_gizmo->add_lines(points_primary, material_primary); - - Vector points_secondary; - points_secondary.resize(16); - - for (int i = 0; i < 8; i++) { - - const float a = i * 2.0 * Math_PI / 8.0; - const Vector3 from(Math::sin(a) * radius, Math::cos(a) * radius, ofs); - - points_secondary.write[i * 2 + 0] = from; - points_secondary.write[i * 2 + 1] = Vector3(); - } - - const Ref material_secondary = get_material("stream_player_3d_material_secondary", p_gizmo); - p_gizmo->add_lines(points_secondary, material_secondary); - - Vector handles; - const float ha = Math::deg2rad(player->get_emission_angle()); - handles.push_back(Vector3(Math::sin(ha), 0, -Math::cos(ha))); - p_gizmo->add_handles(handles, get_material("handles")); - } - - p_gizmo->add_unscaled_billboard(icon, 0.05); -} - -////// - -CameraNode3DGizmoPlugin::CameraNode3DGizmoPlugin() { - - Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/camera", Color(0.8, 0.4, 0.8)); - - create_material("camera_material", gizmo_color); - create_handle_material("handles"); -} - -bool CameraNode3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != NULL; -} - -String CameraNode3DGizmoPlugin::get_name() const { - return "Camera3D"; -} - -int CameraNode3DGizmoPlugin::get_priority() const { - return -1; -} - -String CameraNode3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { - - Camera3D *camera = Object::cast_to(p_gizmo->get_spatial_node()); - - if (camera->get_projection() == Camera3D::PROJECTION_PERSPECTIVE) { - return "FOV"; - } else { - return "Size"; - } -} - -Variant CameraNode3DGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { - - Camera3D *camera = Object::cast_to(p_gizmo->get_spatial_node()); - - if (camera->get_projection() == Camera3D::PROJECTION_PERSPECTIVE) { - return camera->get_fov(); - } else { - - return camera->get_size(); - } -} - -void CameraNode3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) { - - Camera3D *camera = Object::cast_to(p_gizmo->get_spatial_node()); - - Transform gt = camera->get_global_transform(); - Transform gi = gt.affine_inverse(); - - Vector3 ray_from = p_camera->project_ray_origin(p_point); - Vector3 ray_dir = p_camera->project_ray_normal(p_point); - - Vector3 s[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 4096) }; - - if (camera->get_projection() == Camera3D::PROJECTION_PERSPECTIVE) { - Transform gt2 = camera->get_global_transform(); - float a = _find_closest_angle_to_half_pi_arc(s[0], s[1], 1.0, gt2); - camera->set("fov", CLAMP(a * 2.0, 1, 179)); - } else { - - Vector3 ra, rb; - Geometry::get_closest_points_between_segments(Vector3(0, 0, -1), Vector3(4096, 0, -1), s[0], s[1], ra, rb); - float d = ra.x * 2.0; - if (Node3DEditor::get_singleton()->is_snap_enabled()) { - d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); - } - - d = CLAMP(d, 0.1, 16384); - - camera->set("size", d); - } -} - -void CameraNode3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { - - Camera3D *camera = Object::cast_to(p_gizmo->get_spatial_node()); - - if (camera->get_projection() == Camera3D::PROJECTION_PERSPECTIVE) { - - if (p_cancel) { - - camera->set("fov", p_restore); - } else { - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); - ur->create_action(TTR("Change Camera FOV")); - ur->add_do_property(camera, "fov", camera->get_fov()); - ur->add_undo_property(camera, "fov", p_restore); - ur->commit_action(); - } - - } else { - - if (p_cancel) { - - camera->set("size", p_restore); - } else { - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); - ur->create_action(TTR("Change Camera Size")); - ur->add_do_property(camera, "size", camera->get_size()); - ur->add_undo_property(camera, "size", p_restore); - ur->commit_action(); - } - } -} - -void CameraNode3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - - Camera3D *camera = Object::cast_to(p_gizmo->get_spatial_node()); - - p_gizmo->clear(); - - Vector lines; - Vector handles; - - Ref material = get_material("camera_material", p_gizmo); - -#define ADD_TRIANGLE(m_a, m_b, m_c) \ - { \ - lines.push_back(m_a); \ - lines.push_back(m_b); \ - lines.push_back(m_b); \ - lines.push_back(m_c); \ - lines.push_back(m_c); \ - lines.push_back(m_a); \ - } - -#define ADD_QUAD(m_a, m_b, m_c, m_d) \ - { \ - lines.push_back(m_a); \ - lines.push_back(m_b); \ - lines.push_back(m_b); \ - lines.push_back(m_c); \ - lines.push_back(m_c); \ - lines.push_back(m_d); \ - lines.push_back(m_d); \ - lines.push_back(m_a); \ - } - - switch (camera->get_projection()) { - - case Camera3D::PROJECTION_PERSPECTIVE: { - - // The real FOV is halved for accurate representation - float fov = camera->get_fov() / 2.0; - - Vector3 side = Vector3(Math::sin(Math::deg2rad(fov)), 0, -Math::cos(Math::deg2rad(fov))); - Vector3 nside = side; - nside.x = -nside.x; - Vector3 up = Vector3(0, side.x, 0); - - ADD_TRIANGLE(Vector3(), side + up, side - up); - ADD_TRIANGLE(Vector3(), nside + up, nside - up); - ADD_TRIANGLE(Vector3(), side + up, nside + up); - ADD_TRIANGLE(Vector3(), side - up, nside - up); - - handles.push_back(side); - side.x *= 0.25; - nside.x *= 0.25; - Vector3 tup(0, up.y * 3 / 2, side.z); - ADD_TRIANGLE(tup, side + up, nside + up); - - } break; - case Camera3D::PROJECTION_ORTHOGONAL: { - - float size = camera->get_size(); - - float hsize = size * 0.5; - Vector3 right(hsize, 0, 0); - Vector3 up(0, hsize, 0); - Vector3 back(0, 0, -1.0); - Vector3 front(0, 0, 0); - - ADD_QUAD(-up - right, -up + right, up + right, up - right); - ADD_QUAD(-up - right + back, -up + right + back, up + right + back, up - right + back); - ADD_QUAD(up + right, up + right + back, up - right + back, up - right); - ADD_QUAD(-up + right, -up + right + back, -up - right + back, -up - right); - - handles.push_back(right + back); - - right.x *= 0.25; - Vector3 tup(0, up.y * 3 / 2, back.z); - ADD_TRIANGLE(tup, right + up + back, -right + up + back); - - } break; - case Camera3D::PROJECTION_FRUSTUM: { - float hsize = camera->get_size() / 2.0; - - Vector3 side = Vector3(hsize, 0, -camera->get_znear()).normalized(); - Vector3 nside = side; - nside.x = -nside.x; - Vector3 up = Vector3(0, side.x, 0); - Vector3 offset = Vector3(camera->get_frustum_offset().x, camera->get_frustum_offset().y, 0.0); - - ADD_TRIANGLE(Vector3(), side + up + offset, side - up + offset); - ADD_TRIANGLE(Vector3(), nside + up + offset, nside - up + offset); - ADD_TRIANGLE(Vector3(), side + up + offset, nside + up + offset); - ADD_TRIANGLE(Vector3(), side - up + offset, nside - up + offset); - - side.x *= 0.25; - nside.x *= 0.25; - Vector3 tup(0, up.y * 3 / 2, side.z); - ADD_TRIANGLE(tup + offset, side + up + offset, nside + up + offset); - } - } - -#undef ADD_TRIANGLE -#undef ADD_QUAD - - p_gizmo->add_lines(lines, material); - p_gizmo->add_handles(handles, get_material("handles")); - - ClippedCamera *clipcam = Object::cast_to(camera); - if (clipcam) { - Node3D *parent = Object::cast_to(camera->get_parent()); - if (!parent) { - return; - } - Vector3 cam_normal = -camera->get_global_transform().basis.get_axis(Vector3::AXIS_Z).normalized(); - Vector3 cam_x = camera->get_global_transform().basis.get_axis(Vector3::AXIS_X).normalized(); - Vector3 cam_y = camera->get_global_transform().basis.get_axis(Vector3::AXIS_Y).normalized(); - Vector3 cam_pos = camera->get_global_transform().origin; - Vector3 parent_pos = parent->get_global_transform().origin; - - Plane parent_plane(parent_pos, cam_normal); - Vector3 ray_from = parent_plane.project(cam_pos); - - lines.clear(); - lines.push_back(ray_from + cam_x * 0.5 + cam_y * 0.5); - lines.push_back(ray_from + cam_x * 0.5 + cam_y * -0.5); - - lines.push_back(ray_from + cam_x * 0.5 + cam_y * -0.5); - lines.push_back(ray_from + cam_x * -0.5 + cam_y * -0.5); - - lines.push_back(ray_from + cam_x * -0.5 + cam_y * -0.5); - lines.push_back(ray_from + cam_x * -0.5 + cam_y * 0.5); - - lines.push_back(ray_from + cam_x * -0.5 + cam_y * 0.5); - lines.push_back(ray_from + cam_x * 0.5 + cam_y * 0.5); - - if (parent_plane.distance_to(cam_pos) < 0) { - lines.push_back(ray_from); - lines.push_back(cam_pos); - } - - Transform local = camera->get_global_transform().affine_inverse(); - for (int i = 0; i < lines.size(); i++) { - lines.write[i] = local.xform(lines[i]); - } - - p_gizmo->add_lines(lines, material); - } -} - -////// - -MeshInstanceNode3DGizmoPlugin::MeshInstanceNode3DGizmoPlugin() { -} - -bool MeshInstanceNode3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != NULL && Object::cast_to(p_spatial) == NULL; -} - -String MeshInstanceNode3DGizmoPlugin::get_name() const { - return "MeshInstance3D"; -} - -int MeshInstanceNode3DGizmoPlugin::get_priority() const { - return -1; -} - -bool MeshInstanceNode3DGizmoPlugin::can_be_hidden() const { - return false; -} - -void MeshInstanceNode3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - - MeshInstance3D *mesh = Object::cast_to(p_gizmo->get_spatial_node()); - - p_gizmo->clear(); - - Ref m = mesh->get_mesh(); - - if (!m.is_valid()) - return; //none - - Ref tm = m->generate_triangle_mesh(); - if (tm.is_valid()) { - p_gizmo->add_collision_triangles(tm); - } -} - -///// -Sprite3DNode3DGizmoPlugin::Sprite3DNode3DGizmoPlugin() { -} - -bool Sprite3DNode3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != NULL; -} - -String Sprite3DNode3DGizmoPlugin::get_name() const { - return "Sprite3D"; -} - -int Sprite3DNode3DGizmoPlugin::get_priority() const { - return -1; -} - -bool Sprite3DNode3DGizmoPlugin::can_be_hidden() const { - return false; -} - -void Sprite3DNode3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - - Sprite3D *sprite = Object::cast_to(p_gizmo->get_spatial_node()); - - p_gizmo->clear(); - - Ref tm = sprite->generate_triangle_mesh(); - if (tm.is_valid()) { - p_gizmo->add_collision_triangles(tm); - } -} - -/// - -Position3DNode3DGizmoPlugin::Position3DNode3DGizmoPlugin() { - pos3d_mesh = Ref(memnew(ArrayMesh)); - cursor_points = Vector(); - - Vector cursor_colors; - float cs = 0.25; - cursor_points.push_back(Vector3(+cs, 0, 0)); - cursor_points.push_back(Vector3(-cs, 0, 0)); - cursor_points.push_back(Vector3(0, +cs, 0)); - cursor_points.push_back(Vector3(0, -cs, 0)); - cursor_points.push_back(Vector3(0, 0, +cs)); - cursor_points.push_back(Vector3(0, 0, -cs)); - cursor_colors.push_back(EditorNode::get_singleton()->get_gui_base()->get_theme_color("axis_x_color", "Editor")); - cursor_colors.push_back(EditorNode::get_singleton()->get_gui_base()->get_theme_color("axis_x_color", "Editor")); - cursor_colors.push_back(EditorNode::get_singleton()->get_gui_base()->get_theme_color("axis_y_color", "Editor")); - cursor_colors.push_back(EditorNode::get_singleton()->get_gui_base()->get_theme_color("axis_y_color", "Editor")); - cursor_colors.push_back(EditorNode::get_singleton()->get_gui_base()->get_theme_color("axis_z_color", "Editor")); - cursor_colors.push_back(EditorNode::get_singleton()->get_gui_base()->get_theme_color("axis_z_color", "Editor")); - - Ref mat = memnew(StandardMaterial3D); - mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); - mat->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); - mat->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); - mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); - - Array d; - d.resize(VS::ARRAY_MAX); - d[Mesh::ARRAY_VERTEX] = cursor_points; - d[Mesh::ARRAY_COLOR] = cursor_colors; - pos3d_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, d); - pos3d_mesh->surface_set_material(0, mat); -} - -bool Position3DNode3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != NULL; -} - -String Position3DNode3DGizmoPlugin::get_name() const { - return "Position3D"; -} - -int Position3DNode3DGizmoPlugin::get_priority() const { - return -1; -} - -void Position3DNode3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - - p_gizmo->clear(); - p_gizmo->add_mesh(pos3d_mesh); - p_gizmo->add_collision_segments(cursor_points); -} - -///// - -SkeletonNode3DGizmoPlugin::SkeletonNode3DGizmoPlugin() { - - Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/skeleton", Color(1, 0.8, 0.4)); - create_material("skeleton_material", gizmo_color); -} - -bool SkeletonNode3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != NULL; -} - -String SkeletonNode3DGizmoPlugin::get_name() const { - return "Skeleton3D"; -} - -int SkeletonNode3DGizmoPlugin::get_priority() const { - return -1; -} - -void SkeletonNode3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - - Skeleton3D *skel = Object::cast_to(p_gizmo->get_spatial_node()); - - p_gizmo->clear(); - - Ref material = get_material("skeleton_material", p_gizmo); - - Ref surface_tool(memnew(SurfaceTool)); - - surface_tool->begin(Mesh::PRIMITIVE_LINES); - surface_tool->set_material(material); - Vector grests; - grests.resize(skel->get_bone_count()); - - Vector bones; - Vector weights; - bones.resize(4); - weights.resize(4); - - for (int i = 0; i < 4; i++) { - bones.write[i] = 0; - weights.write[i] = 0; - } - - weights.write[0] = 1; - - AABB aabb; - - Color bonecolor = Color(1.0, 0.4, 0.4, 0.3); - Color rootcolor = Color(0.4, 1.0, 0.4, 0.1); - - for (int i_bone = 0; i_bone < skel->get_bone_count(); i_bone++) { - - int i = skel->get_process_order(i_bone); - - int parent = skel->get_bone_parent(i); - - if (parent >= 0) { - grests.write[i] = grests[parent] * skel->get_bone_rest(i); - - Vector3 v0 = grests[parent].origin; - Vector3 v1 = grests[i].origin; - Vector3 d = (v1 - v0).normalized(); - float dist = v0.distance_to(v1); - - //find closest axis - int closest = -1; - float closest_d = 0.0; - - for (int j = 0; j < 3; j++) { - float dp = Math::abs(grests[parent].basis[j].normalized().dot(d)); - if (j == 0 || dp > closest_d) - closest = j; - } - - //find closest other - Vector3 first; - Vector3 points[4]; - int pointidx = 0; - for (int j = 0; j < 3; j++) { - - bones.write[0] = parent; - surface_tool->add_bones(bones); - surface_tool->add_weights(weights); - surface_tool->add_color(rootcolor); - surface_tool->add_vertex(v0 - grests[parent].basis[j].normalized() * dist * 0.05); - surface_tool->add_bones(bones); - surface_tool->add_weights(weights); - surface_tool->add_color(rootcolor); - surface_tool->add_vertex(v0 + grests[parent].basis[j].normalized() * dist * 0.05); - - if (j == closest) - continue; - - Vector3 axis; - if (first == Vector3()) { - axis = d.cross(d.cross(grests[parent].basis[j])).normalized(); - first = axis; - } else { - axis = d.cross(first).normalized(); - } - - for (int k = 0; k < 2; k++) { - - if (k == 1) - axis = -axis; - Vector3 point = v0 + d * dist * 0.2; - point += axis * dist * 0.1; - - bones.write[0] = parent; - surface_tool->add_bones(bones); - surface_tool->add_weights(weights); - surface_tool->add_color(bonecolor); - surface_tool->add_vertex(v0); - surface_tool->add_bones(bones); - surface_tool->add_weights(weights); - surface_tool->add_color(bonecolor); - surface_tool->add_vertex(point); - - bones.write[0] = parent; - surface_tool->add_bones(bones); - surface_tool->add_weights(weights); - surface_tool->add_color(bonecolor); - surface_tool->add_vertex(point); - bones.write[0] = i; - surface_tool->add_bones(bones); - surface_tool->add_weights(weights); - surface_tool->add_color(bonecolor); - surface_tool->add_vertex(v1); - points[pointidx++] = point; - } - } - - SWAP(points[1], points[2]); - for (int j = 0; j < 4; j++) { - - bones.write[0] = parent; - surface_tool->add_bones(bones); - surface_tool->add_weights(weights); - surface_tool->add_color(bonecolor); - surface_tool->add_vertex(points[j]); - surface_tool->add_bones(bones); - surface_tool->add_weights(weights); - surface_tool->add_color(bonecolor); - surface_tool->add_vertex(points[(j + 1) % 4]); - } - - /* - bones[0]=parent; - surface_tool->add_bones(bones); - surface_tool->add_weights(weights); - surface_tool->add_color(Color(0.4,1,0.4,0.4)); - surface_tool->add_vertex(v0); - bones[0]=i; - surface_tool->add_bones(bones); - surface_tool->add_weights(weights); - surface_tool->add_color(Color(0.4,1,0.4,0.4)); - surface_tool->add_vertex(v1); -*/ - } else { - - grests.write[i] = skel->get_bone_rest(i); - bones.write[0] = i; - } - /* - Transform t = grests[i]; - t.orthonormalize(); - - for (int i=0;i<6;i++) { - - - Vector3 face_points[4]; - - for (int j=0;j<4;j++) { - - float v[3]; - v[0]=1.0; - v[1]=1-2*((j>>1)&1); - v[2]=v[1]*(1-2*(j&1)); - - for (int k=0;k<3;k++) { - - if (i<3) - face_points[j][(i+k)%3]=v[k]*(i>=3?-1:1); - else - face_points[3-j][(i+k)%3]=v[k]*(i>=3?-1:1); - } - } - - for(int j=0;j<4;j++) { - surface_tool->add_bones(bones); - surface_tool->add_weights(weights); - surface_tool->add_color(Color(1.0,0.4,0.4,0.4)); - surface_tool->add_vertex(t.xform(face_points[j]*0.04)); - surface_tool->add_bones(bones); - surface_tool->add_weights(weights); - surface_tool->add_color(Color(1.0,0.4,0.4,0.4)); - surface_tool->add_vertex(t.xform(face_points[(j+1)%4]*0.04)); - } - - } - */ - } - - Ref m = surface_tool->commit(); - p_gizmo->add_mesh(m, false, skel->register_skin(Ref())); -} - -//// - -PhysicalBoneNode3DGizmoPlugin::PhysicalBoneNode3DGizmoPlugin() { - create_material("joint_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/joint", Color(0.5, 0.8, 1))); -} - -bool PhysicalBoneNode3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != NULL; -} - -String PhysicalBoneNode3DGizmoPlugin::get_name() const { - return "PhysicalBones"; -} - -int PhysicalBoneNode3DGizmoPlugin::get_priority() const { - return -1; -} - -void PhysicalBoneNode3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - - p_gizmo->clear(); - - PhysicalBone3D *physical_bone = Object::cast_to(p_gizmo->get_spatial_node()); - - if (!physical_bone) - return; - - Skeleton3D *sk(physical_bone->find_skeleton_parent()); - if (!sk) - return; - - PhysicalBone3D *pb(sk->get_physical_bone(physical_bone->get_bone_id())); - if (!pb) - return; - - PhysicalBone3D *pbp(sk->get_physical_bone_parent(physical_bone->get_bone_id())); - if (!pbp) - return; - - Vector points; - - switch (physical_bone->get_joint_type()) { - case PhysicalBone3D::JOINT_TYPE_PIN: { - - JointNode3DGizmoPlugin::CreatePinJointGizmo(physical_bone->get_joint_offset(), points); - } break; - case PhysicalBone3D::JOINT_TYPE_CONE: { - - const PhysicalBone3D::ConeJointData *cjd(static_cast(physical_bone->get_joint_data())); - JointNode3DGizmoPlugin::CreateConeTwistJointGizmo( - physical_bone->get_joint_offset(), - physical_bone->get_global_transform() * physical_bone->get_joint_offset(), - pb->get_global_transform(), - pbp->get_global_transform(), - cjd->swing_span, - cjd->twist_span, - &points, - &points); - } break; - case PhysicalBone3D::JOINT_TYPE_HINGE: { - - const PhysicalBone3D::HingeJointData *hjd(static_cast(physical_bone->get_joint_data())); - JointNode3DGizmoPlugin::CreateHingeJointGizmo( - physical_bone->get_joint_offset(), - physical_bone->get_global_transform() * physical_bone->get_joint_offset(), - pb->get_global_transform(), - pbp->get_global_transform(), - hjd->angular_limit_lower, - hjd->angular_limit_upper, - hjd->angular_limit_enabled, - points, - &points, - &points); - } break; - case PhysicalBone3D::JOINT_TYPE_SLIDER: { - - const PhysicalBone3D::SliderJointData *sjd(static_cast(physical_bone->get_joint_data())); - JointNode3DGizmoPlugin::CreateSliderJointGizmo( - physical_bone->get_joint_offset(), - physical_bone->get_global_transform() * physical_bone->get_joint_offset(), - pb->get_global_transform(), - pbp->get_global_transform(), - sjd->angular_limit_lower, - sjd->angular_limit_upper, - sjd->linear_limit_lower, - sjd->linear_limit_upper, - points, - &points, - &points); - } break; - case PhysicalBone3D::JOINT_TYPE_6DOF: { - - const PhysicalBone3D::SixDOFJointData *sdofjd(static_cast(physical_bone->get_joint_data())); - JointNode3DGizmoPlugin::CreateGeneric6DOFJointGizmo( - physical_bone->get_joint_offset(), - - physical_bone->get_global_transform() * physical_bone->get_joint_offset(), - pb->get_global_transform(), - pbp->get_global_transform(), - - sdofjd->axis_data[0].angular_limit_lower, - sdofjd->axis_data[0].angular_limit_upper, - sdofjd->axis_data[0].linear_limit_lower, - sdofjd->axis_data[0].linear_limit_upper, - sdofjd->axis_data[0].angular_limit_enabled, - sdofjd->axis_data[0].linear_limit_enabled, - - sdofjd->axis_data[1].angular_limit_lower, - sdofjd->axis_data[1].angular_limit_upper, - sdofjd->axis_data[1].linear_limit_lower, - sdofjd->axis_data[1].linear_limit_upper, - sdofjd->axis_data[1].angular_limit_enabled, - sdofjd->axis_data[1].linear_limit_enabled, - - sdofjd->axis_data[2].angular_limit_lower, - sdofjd->axis_data[2].angular_limit_upper, - sdofjd->axis_data[2].linear_limit_lower, - sdofjd->axis_data[2].linear_limit_upper, - sdofjd->axis_data[2].angular_limit_enabled, - sdofjd->axis_data[2].linear_limit_enabled, - - points, - &points, - &points); - } break; - default: - return; - } - - Ref material = get_material("joint_material", p_gizmo); - - p_gizmo->add_collision_segments(points); - p_gizmo->add_lines(points, material); -} - -///// - -RayCastNode3DGizmoPlugin::RayCastNode3DGizmoPlugin() { - - const Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); - create_material("shape_material", gizmo_color); - const float gizmo_value = gizmo_color.get_v(); - const Color gizmo_color_disabled = Color(gizmo_value, gizmo_value, gizmo_value, 0.65); - create_material("shape_material_disabled", gizmo_color_disabled); -} - -bool RayCastNode3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != NULL; -} - -String RayCastNode3DGizmoPlugin::get_name() const { - return "RayCast"; -} - -int RayCastNode3DGizmoPlugin::get_priority() const { - return -1; -} - -void RayCastNode3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - - RayCast3D *raycast = Object::cast_to(p_gizmo->get_spatial_node()); - - p_gizmo->clear(); - - Vector lines; - - lines.push_back(Vector3()); - lines.push_back(raycast->get_cast_to()); - - const Ref material = - get_material(raycast->is_enabled() ? "shape_material" : "shape_material_disabled", p_gizmo); - - p_gizmo->add_lines(lines, material); - p_gizmo->add_collision_segments(lines); -} - -///// - -void SpringArmNode3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - - SpringArm3D *spring_arm = Object::cast_to(p_gizmo->get_spatial_node()); - - p_gizmo->clear(); - - Vector lines; - - lines.push_back(Vector3()); - lines.push_back(Vector3(0, 0, 1.0) * spring_arm->get_length()); - - Ref material = get_material("shape_material", p_gizmo); - - p_gizmo->add_lines(lines, material); - p_gizmo->add_collision_segments(lines); -} - -SpringArmNode3DGizmoPlugin::SpringArmNode3DGizmoPlugin() { - Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); - create_material("shape_material", gizmo_color); -} - -bool SpringArmNode3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != NULL; -} - -String SpringArmNode3DGizmoPlugin::get_name() const { - return "SpringArm"; -} - -int SpringArmNode3DGizmoPlugin::get_priority() const { - return -1; -} - -///// - -VehicleWheelNode3DGizmoPlugin::VehicleWheelNode3DGizmoPlugin() { - - Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); - create_material("shape_material", gizmo_color); -} - -bool VehicleWheelNode3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != NULL; -} - -String VehicleWheelNode3DGizmoPlugin::get_name() const { - return "VehicleWheel"; -} - -int VehicleWheelNode3DGizmoPlugin::get_priority() const { - return -1; -} - -void VehicleWheelNode3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - - VehicleWheel *car_wheel = Object::cast_to(p_gizmo->get_spatial_node()); - - p_gizmo->clear(); - - Vector points; - - float r = car_wheel->get_radius(); - const int skip = 10; - for (int i = 0; i <= 360; i += skip) { - - float ra = Math::deg2rad((float)i); - float rb = Math::deg2rad((float)i + skip); - Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r; - Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r; - - points.push_back(Vector3(0, a.x, a.y)); - points.push_back(Vector3(0, b.x, b.y)); - - const int springsec = 4; - - for (int j = 0; j < springsec; j++) { - float t = car_wheel->get_suspension_rest_length() * 5; - points.push_back(Vector3(a.x, i / 360.0 * t / springsec + j * (t / springsec), a.y) * 0.2); - points.push_back(Vector3(b.x, (i + skip) / 360.0 * t / springsec + j * (t / springsec), b.y) * 0.2); - } - } - - //travel - points.push_back(Vector3(0, 0, 0)); - points.push_back(Vector3(0, car_wheel->get_suspension_rest_length(), 0)); - - //axis - points.push_back(Vector3(r * 0.2, car_wheel->get_suspension_rest_length(), 0)); - points.push_back(Vector3(-r * 0.2, car_wheel->get_suspension_rest_length(), 0)); - //axis - points.push_back(Vector3(r * 0.2, 0, 0)); - points.push_back(Vector3(-r * 0.2, 0, 0)); - - //forward line - points.push_back(Vector3(0, -r, 0)); - points.push_back(Vector3(0, -r, r * 2)); - points.push_back(Vector3(0, -r, r * 2)); - points.push_back(Vector3(r * 2 * 0.2, -r, r * 2 * 0.8)); - points.push_back(Vector3(0, -r, r * 2)); - points.push_back(Vector3(-r * 2 * 0.2, -r, r * 2 * 0.8)); - - Ref material = get_material("shape_material", p_gizmo); - - p_gizmo->add_lines(points, material); - p_gizmo->add_collision_segments(points); -} - -/////////// - -SoftBodyNode3DGizmoPlugin::SoftBodyNode3DGizmoPlugin() { - Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); - create_material("shape_material", gizmo_color); - create_handle_material("handles"); -} - -bool SoftBodyNode3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != NULL; -} - -String SoftBodyNode3DGizmoPlugin::get_name() const { - return "SoftBody"; -} - -int SoftBodyNode3DGizmoPlugin::get_priority() const { - return -1; -} - -bool SoftBodyNode3DGizmoPlugin::is_selectable_when_hidden() const { - return true; -} - -void SoftBodyNode3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - SoftBody3D *soft_body = Object::cast_to(p_gizmo->get_spatial_node()); - - p_gizmo->clear(); - - if (!soft_body || soft_body->get_mesh().is_null()) { - return; - } - - // find mesh - - Vector lines; - - soft_body->get_mesh()->generate_debug_mesh_lines(lines); - - if (!lines.size()) { - return; - } - - Ref tm = soft_body->get_mesh()->generate_triangle_mesh(); - - Vector points; - soft_body->get_mesh()->generate_debug_mesh_indices(points); - - Ref material = get_material("shape_material", p_gizmo); - - p_gizmo->add_lines(lines, material); - p_gizmo->add_handles(points, get_material("handles")); - p_gizmo->add_collision_triangles(tm); -} - -String SoftBodyNode3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { - return "SoftBody pin point"; -} - -Variant SoftBodyNode3DGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { - SoftBody3D *soft_body = Object::cast_to(p_gizmo->get_spatial_node()); - return Variant(soft_body->is_point_pinned(p_idx)); -} - -void SoftBodyNode3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { - SoftBody3D *soft_body = Object::cast_to(p_gizmo->get_spatial_node()); - soft_body->pin_point_toggle(p_idx); -} - -bool SoftBodyNode3DGizmoPlugin::is_handle_highlighted(const EditorNode3DGizmo *p_gizmo, int idx) const { - SoftBody3D *soft_body = Object::cast_to(p_gizmo->get_spatial_node()); - return soft_body->is_point_pinned(idx); -} - -/////////// - -VisibilityNotifierGizmoPlugin::VisibilityNotifierGizmoPlugin() { - Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/visibility_notifier", Color(0.8, 0.5, 0.7)); - create_material("visibility_notifier_material", gizmo_color); - gizmo_color.a = 0.1; - create_material("visibility_notifier_solid_material", gizmo_color); - create_handle_material("handles"); -} - -bool VisibilityNotifierGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != NULL; -} - -String VisibilityNotifierGizmoPlugin::get_name() const { - return "VisibilityNotifier"; -} - -int VisibilityNotifierGizmoPlugin::get_priority() const { - return -1; -} - -String VisibilityNotifierGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { - - switch (p_idx) { - case 0: return "Size X"; - case 1: return "Size Y"; - case 2: return "Size Z"; - case 3: return "Pos X"; - case 4: return "Pos Y"; - case 5: return "Pos Z"; - } - - return ""; -} - -Variant VisibilityNotifierGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { - - VisibilityNotifier3D *notifier = Object::cast_to(p_gizmo->get_spatial_node()); - return notifier->get_aabb(); -} -void VisibilityNotifierGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) { - - VisibilityNotifier3D *notifier = Object::cast_to(p_gizmo->get_spatial_node()); - - Transform gt = notifier->get_global_transform(); - - Transform gi = gt.affine_inverse(); - - bool move = p_idx >= 3; - p_idx = p_idx % 3; - - AABB aabb = notifier->get_aabb(); - Vector3 ray_from = p_camera->project_ray_origin(p_point); - Vector3 ray_dir = p_camera->project_ray_normal(p_point); - - Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 4096) }; - - Vector3 ofs = aabb.position + aabb.size * 0.5; - - Vector3 axis; - axis[p_idx] = 1.0; - - if (move) { - - Vector3 ra, rb; - Geometry::get_closest_points_between_segments(ofs - axis * 4096, ofs + axis * 4096, sg[0], sg[1], ra, rb); - - float d = ra[p_idx]; - if (Node3DEditor::get_singleton()->is_snap_enabled()) { - d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); - } - - aabb.position[p_idx] = d - 1.0 - aabb.size[p_idx] * 0.5; - notifier->set_aabb(aabb); - - } else { - Vector3 ra, rb; - Geometry::get_closest_points_between_segments(ofs, ofs + axis * 4096, sg[0], sg[1], ra, rb); - - float d = ra[p_idx] - ofs[p_idx]; - if (Node3DEditor::get_singleton()->is_snap_enabled()) { - d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); - } - - if (d < 0.001) - d = 0.001; - //resize - aabb.position[p_idx] = (aabb.position[p_idx] + aabb.size[p_idx] * 0.5) - d; - aabb.size[p_idx] = d * 2; - notifier->set_aabb(aabb); - } -} - -void VisibilityNotifierGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { - - VisibilityNotifier3D *notifier = Object::cast_to(p_gizmo->get_spatial_node()); - - if (p_cancel) { - notifier->set_aabb(p_restore); - return; - } - - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); - ur->create_action(TTR("Change Notifier AABB")); - ur->add_do_method(notifier, "set_aabb", notifier->get_aabb()); - ur->add_undo_method(notifier, "set_aabb", p_restore); - ur->commit_action(); -} - -void VisibilityNotifierGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - - VisibilityNotifier3D *notifier = Object::cast_to(p_gizmo->get_spatial_node()); - - p_gizmo->clear(); - - Vector lines; - AABB aabb = notifier->get_aabb(); - - for (int i = 0; i < 12; i++) { - Vector3 a, b; - aabb.get_edge(i, a, b); - lines.push_back(a); - lines.push_back(b); - } - - Vector handles; - - for (int i = 0; i < 3; i++) { - - Vector3 ax; - ax[i] = aabb.position[i] + aabb.size[i]; - ax[(i + 1) % 3] = aabb.position[(i + 1) % 3] + aabb.size[(i + 1) % 3] * 0.5; - ax[(i + 2) % 3] = aabb.position[(i + 2) % 3] + aabb.size[(i + 2) % 3] * 0.5; - handles.push_back(ax); - } - - Vector3 center = aabb.position + aabb.size * 0.5; - for (int i = 0; i < 3; i++) { - - Vector3 ax; - ax[i] = 1.0; - handles.push_back(center + ax); - lines.push_back(center); - lines.push_back(center + ax); - } - - Ref material = get_material("visibility_notifier_material", p_gizmo); - - p_gizmo->add_lines(lines, material); - p_gizmo->add_collision_segments(lines); - - if (p_gizmo->is_selected()) { - Ref solid_material = get_material("visibility_notifier_solid_material", p_gizmo); - p_gizmo->add_solid_box(solid_material, aabb.get_size(), aabb.get_position() + aabb.get_size() / 2.0); - } - - p_gizmo->add_handles(handles, get_material("handles")); -} - -//// - -CPUParticlesGizmoPlugin::CPUParticlesGizmoPlugin() { - create_icon_material("particles_icon", Node3DEditor::get_singleton()->get_theme_icon("GizmoCPUParticles", "EditorIcons")); -} - -bool CPUParticlesGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != NULL; -} - -String CPUParticlesGizmoPlugin::get_name() const { - return "CPUParticles"; -} - -int CPUParticlesGizmoPlugin::get_priority() const { - return -1; -} - -bool CPUParticlesGizmoPlugin::is_selectable_when_hidden() const { - return true; -} - -void CPUParticlesGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - Ref icon = get_material("particles_icon", p_gizmo); - p_gizmo->add_unscaled_billboard(icon, 0.05); -} - -//// - -ParticlesGizmoPlugin::ParticlesGizmoPlugin() { - Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/particles", Color(0.8, 0.7, 0.4)); - create_material("particles_material", gizmo_color); - gizmo_color.a = 0.1; - create_material("particles_solid_material", gizmo_color); - create_icon_material("particles_icon", Node3DEditor::get_singleton()->get_theme_icon("GizmoParticles", "EditorIcons")); - create_handle_material("handles"); -} - -bool ParticlesGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != NULL; -} - -String ParticlesGizmoPlugin::get_name() const { - return "Particles"; -} - -int ParticlesGizmoPlugin::get_priority() const { - return -1; -} - -bool ParticlesGizmoPlugin::is_selectable_when_hidden() const { - return true; -} - -String ParticlesGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { - - switch (p_idx) { - case 0: return "Size X"; - case 1: return "Size Y"; - case 2: return "Size Z"; - case 3: return "Pos X"; - case 4: return "Pos Y"; - case 5: return "Pos Z"; - } - - return ""; -} -Variant ParticlesGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { - - GPUParticles3D *particles = Object::cast_to(p_gizmo->get_spatial_node()); - return particles->get_visibility_aabb(); -} -void ParticlesGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) { - - GPUParticles3D *particles = Object::cast_to(p_gizmo->get_spatial_node()); - - Transform gt = particles->get_global_transform(); - Transform gi = gt.affine_inverse(); - - bool move = p_idx >= 3; - p_idx = p_idx % 3; - - AABB aabb = particles->get_visibility_aabb(); - Vector3 ray_from = p_camera->project_ray_origin(p_point); - Vector3 ray_dir = p_camera->project_ray_normal(p_point); - - Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 4096) }; - - Vector3 ofs = aabb.position + aabb.size * 0.5; - - Vector3 axis; - axis[p_idx] = 1.0; - - if (move) { - - Vector3 ra, rb; - Geometry::get_closest_points_between_segments(ofs - axis * 4096, ofs + axis * 4096, sg[0], sg[1], ra, rb); - - float d = ra[p_idx]; - if (Node3DEditor::get_singleton()->is_snap_enabled()) { - d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); - } - - aabb.position[p_idx] = d - 1.0 - aabb.size[p_idx] * 0.5; - particles->set_visibility_aabb(aabb); - - } else { - Vector3 ra, rb; - Geometry::get_closest_points_between_segments(ofs, ofs + axis * 4096, sg[0], sg[1], ra, rb); - - float d = ra[p_idx] - ofs[p_idx]; - if (Node3DEditor::get_singleton()->is_snap_enabled()) { - d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); - } - - if (d < 0.001) - d = 0.001; - //resize - aabb.position[p_idx] = (aabb.position[p_idx] + aabb.size[p_idx] * 0.5) - d; - aabb.size[p_idx] = d * 2; - particles->set_visibility_aabb(aabb); - } -} - -void ParticlesGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { - - GPUParticles3D *particles = Object::cast_to(p_gizmo->get_spatial_node()); - - if (p_cancel) { - particles->set_visibility_aabb(p_restore); - return; - } - - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); - ur->create_action(TTR("Change Particles AABB")); - ur->add_do_method(particles, "set_visibility_aabb", particles->get_visibility_aabb()); - ur->add_undo_method(particles, "set_visibility_aabb", p_restore); - ur->commit_action(); -} - -void ParticlesGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - - GPUParticles3D *particles = Object::cast_to(p_gizmo->get_spatial_node()); - - p_gizmo->clear(); - - Vector lines; - AABB aabb = particles->get_visibility_aabb(); - - for (int i = 0; i < 12; i++) { - Vector3 a, b; - aabb.get_edge(i, a, b); - lines.push_back(a); - lines.push_back(b); - } - - Vector handles; - - for (int i = 0; i < 3; i++) { - - Vector3 ax; - ax[i] = aabb.position[i] + aabb.size[i]; - ax[(i + 1) % 3] = aabb.position[(i + 1) % 3] + aabb.size[(i + 1) % 3] * 0.5; - ax[(i + 2) % 3] = aabb.position[(i + 2) % 3] + aabb.size[(i + 2) % 3] * 0.5; - handles.push_back(ax); - } - - Vector3 center = aabb.position + aabb.size * 0.5; - for (int i = 0; i < 3; i++) { - - Vector3 ax; - ax[i] = 1.0; - handles.push_back(center + ax); - lines.push_back(center); - lines.push_back(center + ax); - } - - Ref material = get_material("particles_material", p_gizmo); - Ref icon = get_material("particles_icon", p_gizmo); - - p_gizmo->add_lines(lines, material); - - if (p_gizmo->is_selected()) { - Ref solid_material = get_material("particles_solid_material", p_gizmo); - p_gizmo->add_solid_box(solid_material, aabb.get_size(), aabb.get_position() + aabb.get_size() / 2.0); - } - - p_gizmo->add_handles(handles, get_material("handles")); - p_gizmo->add_unscaled_billboard(icon, 0.05); -} -//// - -ReflectionProbeGizmoPlugin::ReflectionProbeGizmoPlugin() { - Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/reflection_probe", Color(0.6, 1, 0.5)); - - create_material("reflection_probe_material", gizmo_color); - - gizmo_color.a = 0.5; - create_material("reflection_internal_material", gizmo_color); - - gizmo_color.a = 0.1; - create_material("reflection_probe_solid_material", gizmo_color); - - create_icon_material("reflection_probe_icon", Node3DEditor::get_singleton()->get_theme_icon("GizmoReflectionProbe", "EditorIcons")); - create_handle_material("handles"); -} - -bool ReflectionProbeGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != NULL; -} - -String ReflectionProbeGizmoPlugin::get_name() const { - return "ReflectionProbe"; -} - -int ReflectionProbeGizmoPlugin::get_priority() const { - return -1; -} - -String ReflectionProbeGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { - - switch (p_idx) { - case 0: return "Extents X"; - case 1: return "Extents Y"; - case 2: return "Extents Z"; - case 3: return "Origin X"; - case 4: return "Origin Y"; - case 5: return "Origin Z"; - } - - return ""; -} -Variant ReflectionProbeGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { - - ReflectionProbe *probe = Object::cast_to(p_gizmo->get_spatial_node()); - return AABB(probe->get_extents(), probe->get_origin_offset()); -} -void ReflectionProbeGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) { - - ReflectionProbe *probe = Object::cast_to(p_gizmo->get_spatial_node()); - Transform gt = probe->get_global_transform(); - - Transform gi = gt.affine_inverse(); - - if (p_idx < 3) { - Vector3 extents = probe->get_extents(); - - Vector3 ray_from = p_camera->project_ray_origin(p_point); - Vector3 ray_dir = p_camera->project_ray_normal(p_point); - - Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 16384) }; - - Vector3 axis; - axis[p_idx] = 1.0; - - Vector3 ra, rb; - Geometry::get_closest_points_between_segments(Vector3(), axis * 16384, sg[0], sg[1], ra, rb); - float d = ra[p_idx]; - if (Node3DEditor::get_singleton()->is_snap_enabled()) { - d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); - } - - if (d < 0.001) - d = 0.001; - - extents[p_idx] = d; - probe->set_extents(extents); - } else { - - p_idx -= 3; - - Vector3 origin = probe->get_origin_offset(); - origin[p_idx] = 0; - - Vector3 ray_from = p_camera->project_ray_origin(p_point); - Vector3 ray_dir = p_camera->project_ray_normal(p_point); - - Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 16384) }; - - Vector3 axis; - axis[p_idx] = 1.0; - - Vector3 ra, rb; - Geometry::get_closest_points_between_segments(origin - axis * 16384, origin + axis * 16384, sg[0], sg[1], ra, rb); - // Adjust the actual position to account for the gizmo handle position - float d = ra[p_idx] + 0.25; - if (Node3DEditor::get_singleton()->is_snap_enabled()) { - d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); - } - - origin[p_idx] = d; - probe->set_origin_offset(origin); - } -} - -void ReflectionProbeGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { - - ReflectionProbe *probe = Object::cast_to(p_gizmo->get_spatial_node()); - - AABB restore = p_restore; - - if (p_cancel) { - probe->set_extents(restore.position); - probe->set_origin_offset(restore.size); - return; - } - - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); - ur->create_action(TTR("Change Probe Extents")); - ur->add_do_method(probe, "set_extents", probe->get_extents()); - ur->add_do_method(probe, "set_origin_offset", probe->get_origin_offset()); - ur->add_undo_method(probe, "set_extents", restore.position); - ur->add_undo_method(probe, "set_origin_offset", restore.size); - ur->commit_action(); -} - -void ReflectionProbeGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - - ReflectionProbe *probe = Object::cast_to(p_gizmo->get_spatial_node()); - - p_gizmo->clear(); - - Vector lines; - Vector internal_lines; - Vector3 extents = probe->get_extents(); - - AABB aabb; - aabb.position = -extents; - aabb.size = extents * 2; - - for (int i = 0; i < 12; i++) { - Vector3 a, b; - aabb.get_edge(i, a, b); - lines.push_back(a); - lines.push_back(b); - } - - for (int i = 0; i < 8; i++) { - Vector3 ep = aabb.get_endpoint(i); - internal_lines.push_back(probe->get_origin_offset()); - internal_lines.push_back(ep); - } - - Vector handles; - - for (int i = 0; i < 3; i++) { - - Vector3 ax; - ax[i] = aabb.position[i] + aabb.size[i]; - handles.push_back(ax); - } - - for (int i = 0; i < 3; i++) { - - Vector3 orig_handle = probe->get_origin_offset(); - orig_handle[i] -= 0.25; - lines.push_back(orig_handle); - handles.push_back(orig_handle); - - orig_handle[i] += 0.5; - lines.push_back(orig_handle); - } - - Ref material = get_material("reflection_probe_material", p_gizmo); - Ref material_internal = get_material("reflection_internal_material", p_gizmo); - Ref icon = get_material("reflection_probe_icon", p_gizmo); - - p_gizmo->add_lines(lines, material); - p_gizmo->add_lines(internal_lines, material_internal); - - if (p_gizmo->is_selected()) { - Ref solid_material = get_material("reflection_probe_solid_material", p_gizmo); - p_gizmo->add_solid_box(solid_material, probe->get_extents() * 2.0); - } - - p_gizmo->add_unscaled_billboard(icon, 0.05); - p_gizmo->add_handles(handles, get_material("handles")); -} - -GIProbeGizmoPlugin::GIProbeGizmoPlugin() { - Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/gi_probe", Color(0.5, 1, 0.6)); - - create_material("gi_probe_material", gizmo_color); - - // This gizmo draws a lot of lines. Use a low opacity to make it not too intrusive. - gizmo_color.a = 0.1; - create_material("gi_probe_internal_material", gizmo_color); - - gizmo_color.a = 0.05; - create_material("gi_probe_solid_material", gizmo_color); - - create_icon_material("gi_probe_icon", Node3DEditor::get_singleton()->get_theme_icon("GizmoGIProbe", "EditorIcons")); - create_handle_material("handles"); -} - -bool GIProbeGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != NULL; -} - -String GIProbeGizmoPlugin::get_name() const { - return "GIProbe"; -} - -int GIProbeGizmoPlugin::get_priority() const { - return -1; -} - -String GIProbeGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { - - switch (p_idx) { - case 0: return "Extents X"; - case 1: return "Extents Y"; - case 2: return "Extents Z"; - } - - return ""; -} -Variant GIProbeGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { - - GIProbe *probe = Object::cast_to(p_gizmo->get_spatial_node()); - return probe->get_extents(); -} -void GIProbeGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) { - - GIProbe *probe = Object::cast_to(p_gizmo->get_spatial_node()); - - Transform gt = probe->get_global_transform(); - Transform gi = gt.affine_inverse(); - - Vector3 extents = probe->get_extents(); - - Vector3 ray_from = p_camera->project_ray_origin(p_point); - Vector3 ray_dir = p_camera->project_ray_normal(p_point); - - Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 16384) }; - - Vector3 axis; - axis[p_idx] = 1.0; - - Vector3 ra, rb; - Geometry::get_closest_points_between_segments(Vector3(), axis * 16384, sg[0], sg[1], ra, rb); - float d = ra[p_idx]; - if (Node3DEditor::get_singleton()->is_snap_enabled()) { - d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); - } - - if (d < 0.001) - d = 0.001; - - extents[p_idx] = d; - probe->set_extents(extents); -} - -void GIProbeGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { - - GIProbe *probe = Object::cast_to(p_gizmo->get_spatial_node()); - - Vector3 restore = p_restore; - - if (p_cancel) { - probe->set_extents(restore); - return; - } - - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); - ur->create_action(TTR("Change Probe Extents")); - ur->add_do_method(probe, "set_extents", probe->get_extents()); - ur->add_undo_method(probe, "set_extents", restore); - ur->commit_action(); -} - -void GIProbeGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - - GIProbe *probe = Object::cast_to(p_gizmo->get_spatial_node()); - - Ref material = get_material("gi_probe_material", p_gizmo); - Ref icon = get_material("gi_probe_icon", p_gizmo); - Ref material_internal = get_material("gi_probe_internal_material", p_gizmo); - - p_gizmo->clear(); - - Vector lines; - Vector3 extents = probe->get_extents(); - - static const int subdivs[GIProbe::SUBDIV_MAX] = { 64, 128, 256, 512 }; - - AABB aabb = AABB(-extents, extents * 2); - int subdiv = subdivs[probe->get_subdiv()]; - float cell_size = aabb.get_longest_axis_size() / subdiv; - - for (int i = 0; i < 12; i++) { - Vector3 a, b; - aabb.get_edge(i, a, b); - lines.push_back(a); - lines.push_back(b); - } - - p_gizmo->add_lines(lines, material); - - lines.clear(); - - for (int i = 1; i < subdiv; i++) { - - for (int j = 0; j < 3; j++) { - - if (cell_size * i > aabb.size[j]) { - continue; - } - - Vector2 dir; - dir[j] = 1.0; - Vector2 ta, tb; - int j_n1 = (j + 1) % 3; - int j_n2 = (j + 2) % 3; - ta[j_n1] = 1.0; - tb[j_n2] = 1.0; - - for (int k = 0; k < 4; k++) { - - Vector3 from = aabb.position, to = aabb.position; - from[j] += cell_size * i; - to[j] += cell_size * i; - - if (k & 1) { - to[j_n1] += aabb.size[j_n1]; - } else { - - to[j_n2] += aabb.size[j_n2]; - } - - if (k & 2) { - from[j_n1] += aabb.size[j_n1]; - from[j_n2] += aabb.size[j_n2]; - } - - lines.push_back(from); - lines.push_back(to); - } - } - } - - p_gizmo->add_lines(lines, material_internal); - - Vector handles; - - for (int i = 0; i < 3; i++) { - - Vector3 ax; - ax[i] = aabb.position[i] + aabb.size[i]; - handles.push_back(ax); - } - - if (p_gizmo->is_selected()) { - Ref solid_material = get_material("gi_probe_solid_material", p_gizmo); - p_gizmo->add_solid_box(solid_material, aabb.get_size()); - } - - p_gizmo->add_unscaled_billboard(icon, 0.05); - p_gizmo->add_handles(handles, get_material("handles")); -} - -//// -#if 0 -BakedIndirectLightGizmoPlugin::BakedIndirectLightGizmoPlugin() { - Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/baked_indirect_light", Color(0.5, 0.6, 1)); - - create_material("baked_indirect_light_material", gizmo_color); - - gizmo_color.a = 0.1; - create_material("baked_indirect_light_internal_material", gizmo_color); - - create_icon_material("baked_indirect_light_icon", Node3DEditor::get_singleton()->get_icon("GizmoBakedLightmap", "EditorIcons")); - create_handle_material("handles"); -} - -String BakedIndirectLightGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { - - switch (p_idx) { - case 0: return "Extents X"; - case 1: return "Extents Y"; - case 2: return "Extents Z"; - } - - return ""; -} -Variant BakedIndirectLightGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { - - BakedLightmap *baker = Object::cast_to(p_gizmo->get_spatial_node()); - return baker->get_extents(); -} -void BakedIndirectLightGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera *p_camera, const Point2 &p_point) { - - BakedLightmap *baker = Object::cast_to(p_gizmo->get_spatial_node()); - - Transform gt = baker->get_global_transform(); - Transform gi = gt.affine_inverse(); - - Vector3 extents = baker->get_extents(); - - Vector3 ray_from = p_camera->project_ray_origin(p_point); - Vector3 ray_dir = p_camera->project_ray_normal(p_point); - - Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 16384) }; - - Vector3 axis; - axis[p_idx] = 1.0; - - Vector3 ra, rb; - Geometry::get_closest_points_between_segments(Vector3(), axis * 16384, sg[0], sg[1], ra, rb); - float d = ra[p_idx]; - if (Node3DEditor::get_singleton()->is_snap_enabled()) { - d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); - } - - if (d < 0.001) - d = 0.001; - - extents[p_idx] = d; - baker->set_extents(extents); -} - -void BakedIndirectLightGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { - - BakedLightmap *baker = Object::cast_to(p_gizmo->get_spatial_node()); - - Vector3 restore = p_restore; - - if (p_cancel) { - baker->set_extents(restore); - return; - } - - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); - ur->create_action(TTR("Change Probe Extents")); - ur->add_do_method(baker, "set_extents", baker->get_extents()); - ur->add_undo_method(baker, "set_extents", restore); - ur->commit_action(); -} - -bool BakedIndirectLightGizmoPlugin::has_gizmo(Spatial *p_spatial) { - return Object::cast_to(p_spatial) != NULL; -} - -String BakedIndirectLightGizmoPlugin::get_name() const { - return "BakedLightmap"; -} - -int BakedIndirectLightGizmoPlugin::get_priority() const { - return -1; -} - -void BakedIndirectLightGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - - BakedLightmap *baker = Object::cast_to(p_gizmo->get_spatial_node()); - - Ref material = get_material("baked_indirect_light_material", p_gizmo); - Ref icon = get_material("baked_indirect_light_icon", p_gizmo); - Ref material_internal = get_material("baked_indirect_light_internal_material", p_gizmo); - - p_gizmo->clear(); - - Vector lines; - Vector3 extents = baker->get_extents(); - - AABB aabb = AABB(-extents, extents * 2); - - for (int i = 0; i < 12; i++) { - Vector3 a, b; - aabb.get_edge(i, a, b); - lines.push_back(a); - lines.push_back(b); - } - - p_gizmo->add_lines(lines, material); - - Vector handles; - - for (int i = 0; i < 3; i++) { - - Vector3 ax; - ax[i] = aabb.position[i] + aabb.size[i]; - handles.push_back(ax); - } - - if (p_gizmo->is_selected()) { - p_gizmo->add_solid_box(material_internal, aabb.get_size()); - } - - p_gizmo->add_unscaled_billboard(icon, 0.05); - p_gizmo->add_handles(handles, get_material("handles")); -} -#endif -//// - -CollisionShapeNode3DGizmoPlugin::CollisionShapeNode3DGizmoPlugin() { - const Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); - create_material("shape_material", gizmo_color); - const float gizmo_value = gizmo_color.get_v(); - const Color gizmo_color_disabled = Color(gizmo_value, gizmo_value, gizmo_value, 0.65); - create_material("shape_material_disabled", gizmo_color_disabled); - create_handle_material("handles"); -} - -bool CollisionShapeNode3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != NULL; -} - -String CollisionShapeNode3DGizmoPlugin::get_name() const { - return "CollisionShape3D"; -} - -int CollisionShapeNode3DGizmoPlugin::get_priority() const { - return -1; -} - -String CollisionShapeNode3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { - - const CollisionShape3D *cs = Object::cast_to(p_gizmo->get_spatial_node()); - - Ref s = cs->get_shape(); - if (s.is_null()) - return ""; - - if (Object::cast_to(*s)) { - - return "Radius"; - } - - if (Object::cast_to(*s)) { - - return "Extents"; - } - - if (Object::cast_to(*s)) { - - return p_idx == 0 ? "Radius" : "Height"; - } - - if (Object::cast_to(*s)) { - - return p_idx == 0 ? "Radius" : "Height"; - } - - if (Object::cast_to(*s)) { - - return "Length"; - } - - return ""; -} - -Variant CollisionShapeNode3DGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { - - CollisionShape3D *cs = Object::cast_to(p_gizmo->get_spatial_node()); - - Ref s = cs->get_shape(); - if (s.is_null()) - return Variant(); - - if (Object::cast_to(*s)) { - - Ref ss = s; - return ss->get_radius(); - } - - if (Object::cast_to(*s)) { - - Ref bs = s; - return bs->get_extents(); - } - - if (Object::cast_to(*s)) { - - Ref cs2 = s; - return p_idx == 0 ? cs2->get_radius() : cs2->get_height(); - } - - if (Object::cast_to(*s)) { - - Ref cs2 = s; - return p_idx == 0 ? cs2->get_radius() : cs2->get_height(); - } - - if (Object::cast_to(*s)) { - - Ref cs2 = s; - return cs2->get_length(); - } - - return Variant(); -} -void CollisionShapeNode3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) { - - CollisionShape3D *cs = Object::cast_to(p_gizmo->get_spatial_node()); - - Ref s = cs->get_shape(); - if (s.is_null()) - return; - - Transform gt = cs->get_global_transform(); - Transform gi = gt.affine_inverse(); - - Vector3 ray_from = p_camera->project_ray_origin(p_point); - Vector3 ray_dir = p_camera->project_ray_normal(p_point); - - Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 4096) }; - - if (Object::cast_to(*s)) { - - Ref ss = s; - Vector3 ra, rb; - Geometry::get_closest_points_between_segments(Vector3(), Vector3(4096, 0, 0), sg[0], sg[1], ra, rb); - float d = ra.x; - if (Node3DEditor::get_singleton()->is_snap_enabled()) { - d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); - } - - if (d < 0.001) - d = 0.001; - - ss->set_radius(d); - } - - if (Object::cast_to(*s)) { - - Ref rs = s; - Vector3 ra, rb; - Geometry::get_closest_points_between_segments(Vector3(), Vector3(0, 0, 4096), sg[0], sg[1], ra, rb); - float d = ra.z; - if (Node3DEditor::get_singleton()->is_snap_enabled()) { - d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); - } - - if (d < 0.001) - d = 0.001; - - rs->set_length(d); - } - - if (Object::cast_to(*s)) { - - Vector3 axis; - axis[p_idx] = 1.0; - Ref bs = s; - Vector3 ra, rb; - Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); - float d = ra[p_idx]; - if (Node3DEditor::get_singleton()->is_snap_enabled()) { - d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); - } - - if (d < 0.001) - d = 0.001; - - Vector3 he = bs->get_extents(); - he[p_idx] = d; - bs->set_extents(he); - } - - if (Object::cast_to(*s)) { - - Vector3 axis; - axis[p_idx == 0 ? 0 : 2] = 1.0; - Ref cs2 = s; - Vector3 ra, rb; - Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); - float d = axis.dot(ra); - if (p_idx == 1) - d -= cs2->get_radius(); - - if (Node3DEditor::get_singleton()->is_snap_enabled()) { - d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); - } - - if (d < 0.001) - d = 0.001; - - if (p_idx == 0) - cs2->set_radius(d); - else if (p_idx == 1) - cs2->set_height(d * 2.0); - } - - if (Object::cast_to(*s)) { - - Vector3 axis; - axis[p_idx == 0 ? 0 : 1] = 1.0; - Ref cs2 = s; - Vector3 ra, rb; - Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); - float d = axis.dot(ra); - if (Node3DEditor::get_singleton()->is_snap_enabled()) { - d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); - } - - if (d < 0.001) - d = 0.001; - - if (p_idx == 0) - cs2->set_radius(d); - else if (p_idx == 1) - cs2->set_height(d * 2.0); - } -} -void CollisionShapeNode3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { - - CollisionShape3D *cs = Object::cast_to(p_gizmo->get_spatial_node()); - - Ref s = cs->get_shape(); - if (s.is_null()) - return; - - if (Object::cast_to(*s)) { - - Ref ss = s; - if (p_cancel) { - ss->set_radius(p_restore); - return; - } - - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); - ur->create_action(TTR("Change Sphere Shape Radius")); - ur->add_do_method(ss.ptr(), "set_radius", ss->get_radius()); - ur->add_undo_method(ss.ptr(), "set_radius", p_restore); - ur->commit_action(); - } - - if (Object::cast_to(*s)) { - - Ref ss = s; - if (p_cancel) { - ss->set_extents(p_restore); - return; - } - - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); - ur->create_action(TTR("Change Box Shape Extents")); - ur->add_do_method(ss.ptr(), "set_extents", ss->get_extents()); - ur->add_undo_method(ss.ptr(), "set_extents", p_restore); - ur->commit_action(); - } - - if (Object::cast_to(*s)) { - - Ref ss = s; - if (p_cancel) { - if (p_idx == 0) - ss->set_radius(p_restore); - else - ss->set_height(p_restore); - return; - } - - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); - if (p_idx == 0) { - ur->create_action(TTR("Change Capsule Shape Radius")); - ur->add_do_method(ss.ptr(), "set_radius", ss->get_radius()); - ur->add_undo_method(ss.ptr(), "set_radius", p_restore); - } else { - ur->create_action(TTR("Change Capsule Shape Height")); - ur->add_do_method(ss.ptr(), "set_height", ss->get_height()); - ur->add_undo_method(ss.ptr(), "set_height", p_restore); - } - - ur->commit_action(); - } - - if (Object::cast_to(*s)) { - - Ref ss = s; - if (p_cancel) { - if (p_idx == 0) - ss->set_radius(p_restore); - else - ss->set_height(p_restore); - return; - } - - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); - if (p_idx == 0) { - ur->create_action(TTR("Change Cylinder Shape Radius")); - ur->add_do_method(ss.ptr(), "set_radius", ss->get_radius()); - ur->add_undo_method(ss.ptr(), "set_radius", p_restore); - } else { - ur->create_action( - /// - - //////// - TTR("Change Cylinder Shape Height")); - ur->add_do_method(ss.ptr(), "set_height", ss->get_height()); - ur->add_undo_method(ss.ptr(), "set_height", p_restore); - } - - ur->commit_action(); - } - - if (Object::cast_to(*s)) { - - Ref ss = s; - if (p_cancel) { - ss->set_length(p_restore); - return; - } - - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); - ur->create_action(TTR("Change Ray Shape Length")); - ur->add_do_method(ss.ptr(), "set_length", ss->get_length()); - ur->add_undo_method(ss.ptr(), "set_length", p_restore); - ur->commit_action(); - } -} -void CollisionShapeNode3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - - CollisionShape3D *cs = Object::cast_to(p_gizmo->get_spatial_node()); - - p_gizmo->clear(); - - Ref s = cs->get_shape(); - if (s.is_null()) - return; - - const Ref material = - get_material(!cs->is_disabled() ? "shape_material" : "shape_material_disabled", p_gizmo); - Ref handles_material = get_material("handles"); - - if (Object::cast_to(*s)) { - - Ref sp = s; - float r = sp->get_radius(); - - Vector points; - - for (int i = 0; i <= 360; i++) { - - float ra = Math::deg2rad((float)i); - float rb = Math::deg2rad((float)i + 1); - Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r; - Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r; - - points.push_back(Vector3(a.x, 0, a.y)); - points.push_back(Vector3(b.x, 0, b.y)); - points.push_back(Vector3(0, a.x, a.y)); - points.push_back(Vector3(0, b.x, b.y)); - points.push_back(Vector3(a.x, a.y, 0)); - points.push_back(Vector3(b.x, b.y, 0)); - } - - Vector collision_segments; - - for (int i = 0; i < 64; i++) { - - float ra = i * Math_PI * 2.0 / 64.0; - float rb = (i + 1) * Math_PI * 2.0 / 64.0; - Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r; - Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r; - - collision_segments.push_back(Vector3(a.x, 0, a.y)); - collision_segments.push_back(Vector3(b.x, 0, b.y)); - collision_segments.push_back(Vector3(0, a.x, a.y)); - collision_segments.push_back(Vector3(0, b.x, b.y)); - collision_segments.push_back(Vector3(a.x, a.y, 0)); - collision_segments.push_back(Vector3(b.x, b.y, 0)); - } - - p_gizmo->add_lines(points, material); - p_gizmo->add_collision_segments(collision_segments); - Vector handles; - handles.push_back(Vector3(r, 0, 0)); - p_gizmo->add_handles(handles, handles_material); - } - - if (Object::cast_to(*s)) { - - Ref bs = s; - Vector lines; - AABB aabb; - aabb.position = -bs->get_extents(); - aabb.size = aabb.position * -2; - - for (int i = 0; i < 12; i++) { - Vector3 a, b; - aabb.get_edge(i, a, b); - lines.push_back(a); - lines.push_back(b); - } - - Vector handles; - - for (int i = 0; i < 3; i++) { - - Vector3 ax; - ax[i] = bs->get_extents()[i]; - handles.push_back(ax); - } - - p_gizmo->add_lines(lines, material); - p_gizmo->add_collision_segments(lines); - p_gizmo->add_handles(handles, handles_material); - } - - if (Object::cast_to(*s)) { - - Ref cs2 = s; - float radius = cs2->get_radius(); - float height = cs2->get_height(); - - Vector points; - - Vector3 d(0, height * 0.5, 0); - for (int i = 0; i < 360; i++) { - - float ra = Math::deg2rad((float)i); - float rb = Math::deg2rad((float)i + 1); - Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius; - Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius; - - points.push_back(Vector3(a.x, 0, a.y) + d); - points.push_back(Vector3(b.x, 0, b.y) + d); - - points.push_back(Vector3(a.x, 0, a.y) - d); - points.push_back(Vector3(b.x, 0, b.y) - d); - - if (i % 90 == 0) { - - points.push_back(Vector3(a.x, 0, a.y) + d); - points.push_back(Vector3(a.x, 0, a.y) - d); - } - - Vector3 dud = i < 180 ? d : -d; - - points.push_back(Vector3(0, a.x, a.y) + dud); - points.push_back(Vector3(0, b.x, b.y) + dud); - points.push_back(Vector3(a.y, a.x, 0) + dud); - points.push_back(Vector3(b.y, b.x, 0) + dud); - } - - p_gizmo->add_lines(points, material); - - Vector collision_segments; - - for (int i = 0; i < 64; i++) { - - float ra = i * Math_PI * 2.0 / 64.0; - float rb = (i + 1) * Math_PI * 2.0 / 64.0; - Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius; - Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius; - - collision_segments.push_back(Vector3(a.x, 0, a.y) + d); - collision_segments.push_back(Vector3(b.x, 0, b.y) + d); - - collision_segments.push_back(Vector3(a.x, 0, a.y) - d); - collision_segments.push_back(Vector3(b.x, 0, b.y) - d); - - if (i % 16 == 0) { - - collision_segments.push_back(Vector3(a.x, 0, a.y) + d); - collision_segments.push_back(Vector3(a.x, 0, a.y) - d); - } - - Vector3 dud = i < 32 ? d : -d; - - collision_segments.push_back(Vector3(0, a.x, a.y) + dud); - collision_segments.push_back(Vector3(0, b.x, b.y) + dud); - collision_segments.push_back(Vector3(a.y, a.x, 0) + dud); - collision_segments.push_back(Vector3(b.y, b.x, 0) + dud); - } - - p_gizmo->add_collision_segments(collision_segments); - - Vector handles; - handles.push_back(Vector3(cs2->get_radius(), 0, 0)); - handles.push_back(Vector3(0, cs2->get_height() * 0.5 + cs2->get_radius(), 0)); - p_gizmo->add_handles(handles, handles_material); - } - - if (Object::cast_to(*s)) { - - Ref cs2 = s; - float radius = cs2->get_radius(); - float height = cs2->get_height(); - - Vector points; - - Vector3 d(0, height * 0.5, 0); - for (int i = 0; i < 360; i++) { - - float ra = Math::deg2rad((float)i); - float rb = Math::deg2rad((float)i + 1); - Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius; - Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius; - - points.push_back(Vector3(a.x, 0, a.y) + d); - points.push_back(Vector3(b.x, 0, b.y) + d); - - points.push_back(Vector3(a.x, 0, a.y) - d); - points.push_back(Vector3(b.x, 0, b.y) - d); - - if (i % 90 == 0) { - - points.push_back(Vector3(a.x, 0, a.y) + d); - points.push_back(Vector3(a.x, 0, a.y) - d); - } - } - - p_gizmo->add_lines(points, material); - - Vector collision_segments; - - for (int i = 0; i < 64; i++) { - - float ra = i * Math_PI * 2.0 / 64.0; - float rb = (i + 1) * Math_PI * 2.0 / 64.0; - Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius; - Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius; - - collision_segments.push_back(Vector3(a.x, 0, a.y) + d); - collision_segments.push_back(Vector3(b.x, 0, b.y) + d); - - collision_segments.push_back(Vector3(a.x, 0, a.y) - d); - collision_segments.push_back(Vector3(b.x, 0, b.y) - d); - - if (i % 16 == 0) { - - collision_segments.push_back(Vector3(a.x, 0, a.y) + d); - collision_segments.push_back(Vector3(a.x, 0, a.y) - d); - } - } - - p_gizmo->add_collision_segments(collision_segments); - - Vector handles; - handles.push_back(Vector3(cs2->get_radius(), 0, 0)); - handles.push_back(Vector3(0, cs2->get_height() * 0.5, 0)); - p_gizmo->add_handles(handles, handles_material); - } - - if (Object::cast_to(*s)) { - - Ref ps = s; - Plane p = ps->get_plane(); - Vector points; - - Vector3 n1 = p.get_any_perpendicular_normal(); - Vector3 n2 = p.normal.cross(n1).normalized(); - - Vector3 pface[4] = { - p.normal * p.d + n1 * 10.0 + n2 * 10.0, - p.normal * p.d + n1 * 10.0 + n2 * -10.0, - p.normal * p.d + n1 * -10.0 + n2 * -10.0, - p.normal * p.d + n1 * -10.0 + n2 * 10.0, - }; - - points.push_back(pface[0]); - points.push_back(pface[1]); - points.push_back(pface[1]); - points.push_back(pface[2]); - points.push_back(pface[2]); - points.push_back(pface[3]); - points.push_back(pface[3]); - points.push_back(pface[0]); - points.push_back(p.normal * p.d); - points.push_back(p.normal * p.d + p.normal * 3); - - p_gizmo->add_lines(points, material); - p_gizmo->add_collision_segments(points); - } - - if (Object::cast_to(*s)) { - - Vector points = Object::cast_to(*s)->get_points(); - - if (points.size() > 3) { - - Vector varr = Variant(points); - Geometry::MeshData md; - Error err = QuickHull::build(varr, md); - if (err == OK) { - Vector points2; - points2.resize(md.edges.size() * 2); - for (int i = 0; i < md.edges.size(); i++) { - points2.write[i * 2 + 0] = md.vertices[md.edges[i].a]; - points2.write[i * 2 + 1] = md.vertices[md.edges[i].b]; - } - - p_gizmo->add_lines(points2, material); - p_gizmo->add_collision_segments(points2); - } - } - } - - if (Object::cast_to(*s)) { - - Ref cs2 = s; - Ref mesh = cs2->get_debug_mesh(); - p_gizmo->add_mesh(mesh, false, Ref(), material); - p_gizmo->add_collision_segments(cs2->get_debug_mesh_lines()); - } - - if (Object::cast_to(*s)) { - - Ref rs = s; - - Vector points; - points.push_back(Vector3()); - points.push_back(Vector3(0, 0, rs->get_length())); - p_gizmo->add_lines(points, material); - p_gizmo->add_collision_segments(points); - Vector handles; - handles.push_back(Vector3(0, 0, rs->get_length())); - p_gizmo->add_handles(handles, handles_material); - } - - if (Object::cast_to(*s)) { - - Ref hms = s; - - Ref mesh = hms->get_debug_mesh(); - p_gizmo->add_mesh(mesh, false, Ref(), material); - } -} - -///// - -CollisionPolygonNode3DGizmoPlugin::CollisionPolygonNode3DGizmoPlugin() { - const Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); - create_material("shape_material", gizmo_color); - const float gizmo_value = gizmo_color.get_v(); - const Color gizmo_color_disabled = Color(gizmo_value, gizmo_value, gizmo_value, 0.65); - create_material("shape_material_disabled", gizmo_color_disabled); -} - -bool CollisionPolygonNode3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != NULL; -} - -String CollisionPolygonNode3DGizmoPlugin::get_name() const { - return "CollisionPolygon3D"; -} - -int CollisionPolygonNode3DGizmoPlugin::get_priority() const { - return -1; -} - -void CollisionPolygonNode3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - - CollisionPolygon3D *polygon = Object::cast_to(p_gizmo->get_spatial_node()); - - p_gizmo->clear(); - - Vector points = polygon->get_polygon(); - float depth = polygon->get_depth() * 0.5; - - Vector lines; - for (int i = 0; i < points.size(); i++) { - - int n = (i + 1) % points.size(); - lines.push_back(Vector3(points[i].x, points[i].y, depth)); - lines.push_back(Vector3(points[n].x, points[n].y, depth)); - lines.push_back(Vector3(points[i].x, points[i].y, -depth)); - lines.push_back(Vector3(points[n].x, points[n].y, -depth)); - lines.push_back(Vector3(points[i].x, points[i].y, depth)); - lines.push_back(Vector3(points[i].x, points[i].y, -depth)); - } - - const Ref material = - get_material(!polygon->is_disabled() ? "shape_material" : "shape_material_disabled", p_gizmo); - - p_gizmo->add_lines(lines, material); - p_gizmo->add_collision_segments(lines); -} - -//// - -NavigationMeshNode3DGizmoPlugin::NavigationMeshNode3DGizmoPlugin() { - create_material("navigation_edge_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/navigation_edge", Color(0.5, 1, 1))); - create_material("navigation_edge_material_disabled", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/navigation_edge_disabled", Color(0.7, 0.7, 0.7))); - create_material("navigation_solid_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/navigation_solid", Color(0.5, 1, 1, 0.4))); - create_material("navigation_solid_material_disabled", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/navigation_solid_disabled", Color(0.7, 0.7, 0.7, 0.4))); -} - -bool NavigationMeshNode3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != NULL; -} - -String NavigationMeshNode3DGizmoPlugin::get_name() const { - return "NavigationRegion"; -} - -int NavigationMeshNode3DGizmoPlugin::get_priority() const { - return -1; -} - -void NavigationMeshNode3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - - NavigationRegion3D *navmesh = Object::cast_to(p_gizmo->get_spatial_node()); - - Ref edge_material = get_material("navigation_edge_material", p_gizmo); - Ref edge_material_disabled = get_material("navigation_edge_material_disabled", p_gizmo); - Ref solid_material = get_material("navigation_solid_material", p_gizmo); - Ref solid_material_disabled = get_material("navigation_solid_material_disabled", p_gizmo); - - p_gizmo->clear(); - Ref navmeshie = navmesh->get_navigation_mesh(); - if (navmeshie.is_null()) - return; - - Vector vertices = navmeshie->get_vertices(); - const Vector3 *vr = vertices.ptr(); - List faces; - for (int i = 0; i < navmeshie->get_polygon_count(); i++) { - Vector p = navmeshie->get_polygon(i); - - for (int j = 2; j < p.size(); j++) { - Face3 f; - f.vertex[0] = vr[p[0]]; - f.vertex[1] = vr[p[j - 1]]; - f.vertex[2] = vr[p[j]]; - - faces.push_back(f); - } - } - - if (faces.empty()) - return; - - Map<_EdgeKey, bool> edge_map; - Vector tmeshfaces; - tmeshfaces.resize(faces.size() * 3); - - { - Vector3 *tw = tmeshfaces.ptrw(); - int tidx = 0; - - for (List::Element *E = faces.front(); E; E = E->next()) { - - const Face3 &f = E->get(); - - for (int j = 0; j < 3; j++) { - - tw[tidx++] = f.vertex[j]; - _EdgeKey ek; - ek.from = f.vertex[j].snapped(Vector3(CMP_EPSILON, CMP_EPSILON, CMP_EPSILON)); - ek.to = f.vertex[(j + 1) % 3].snapped(Vector3(CMP_EPSILON, CMP_EPSILON, CMP_EPSILON)); - if (ek.from < ek.to) - SWAP(ek.from, ek.to); - - Map<_EdgeKey, bool>::Element *F = edge_map.find(ek); - - if (F) { - - F->get() = false; - - } else { - - edge_map[ek] = true; - } - } - } - } - Vector lines; - - for (Map<_EdgeKey, bool>::Element *E = edge_map.front(); E; E = E->next()) { - - if (E->get()) { - lines.push_back(E->key().from); - lines.push_back(E->key().to); - } - } - - Ref tmesh = memnew(TriangleMesh); - tmesh->create(tmeshfaces); - - if (lines.size()) - p_gizmo->add_lines(lines, navmesh->is_enabled() ? edge_material : edge_material_disabled); - p_gizmo->add_collision_triangles(tmesh); - Ref m = memnew(ArrayMesh); - Array a; - a.resize(Mesh::ARRAY_MAX); - a[0] = tmeshfaces; - m->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a); - m->surface_set_material(0, navmesh->is_enabled() ? solid_material : solid_material_disabled); - p_gizmo->add_mesh(m); - p_gizmo->add_collision_segments(lines); -} - -////// - -#define BODY_A_RADIUS 0.25 -#define BODY_B_RADIUS 0.27 - -Basis JointGizmosDrawer::look_body(const Transform &p_joint_transform, const Transform &p_body_transform) { - const Vector3 &p_eye(p_joint_transform.origin); - const Vector3 &p_target(p_body_transform.origin); - - Vector3 v_x, v_y, v_z; - - // Look the body with X - v_x = p_target - p_eye; - v_x.normalize(); - - v_z = v_x.cross(Vector3(0, 1, 0)); - v_z.normalize(); - - v_y = v_z.cross(v_x); - v_y.normalize(); - - Basis base; - base.set(v_x, v_y, v_z); - - // Absorb current joint transform - base = p_joint_transform.basis.inverse() * base; - - return base; -} - -Basis JointGizmosDrawer::look_body_toward(Vector3::Axis p_axis, const Transform &joint_transform, const Transform &body_transform) { - - switch (p_axis) { - case Vector3::AXIS_X: - return look_body_toward_x(joint_transform, body_transform); - case Vector3::AXIS_Y: - return look_body_toward_y(joint_transform, body_transform); - case Vector3::AXIS_Z: - return look_body_toward_z(joint_transform, body_transform); - default: - return Basis(); - } -} - -Basis JointGizmosDrawer::look_body_toward_x(const Transform &p_joint_transform, const Transform &p_body_transform) { - - const Vector3 &p_eye(p_joint_transform.origin); - const Vector3 &p_target(p_body_transform.origin); - - const Vector3 p_front(p_joint_transform.basis.get_axis(0)); - - Vector3 v_x, v_y, v_z; - - // Look the body with X - v_x = p_target - p_eye; - v_x.normalize(); - - v_y = p_front.cross(v_x); - v_y.normalize(); - - v_z = v_y.cross(p_front); - v_z.normalize(); - - // Clamp X to FRONT axis - v_x = p_front; - v_x.normalize(); - - Basis base; - base.set(v_x, v_y, v_z); - - // Absorb current joint transform - base = p_joint_transform.basis.inverse() * base; - - return base; -} - -Basis JointGizmosDrawer::look_body_toward_y(const Transform &p_joint_transform, const Transform &p_body_transform) { - - const Vector3 &p_eye(p_joint_transform.origin); - const Vector3 &p_target(p_body_transform.origin); - - const Vector3 p_up(p_joint_transform.basis.get_axis(1)); - - Vector3 v_x, v_y, v_z; - - // Look the body with X - v_x = p_target - p_eye; - v_x.normalize(); - - v_z = v_x.cross(p_up); - v_z.normalize(); - - v_x = p_up.cross(v_z); - v_x.normalize(); - - // Clamp Y to UP axis - v_y = p_up; - v_y.normalize(); - - Basis base; - base.set(v_x, v_y, v_z); - - // Absorb current joint transform - base = p_joint_transform.basis.inverse() * base; - - return base; -} - -Basis JointGizmosDrawer::look_body_toward_z(const Transform &p_joint_transform, const Transform &p_body_transform) { - - const Vector3 &p_eye(p_joint_transform.origin); - const Vector3 &p_target(p_body_transform.origin); - - const Vector3 p_lateral(p_joint_transform.basis.get_axis(2)); - - Vector3 v_x, v_y, v_z; - - // Look the body with X - v_x = p_target - p_eye; - v_x.normalize(); - - v_z = p_lateral; - v_z.normalize(); - - v_y = v_z.cross(v_x); - v_y.normalize(); - - // Clamp X to Z axis - v_x = v_y.cross(v_z); - v_x.normalize(); - - Basis base; - base.set(v_x, v_y, v_z); - - // Absorb current joint transform - base = p_joint_transform.basis.inverse() * base; - - return base; -} - -void JointGizmosDrawer::draw_circle(Vector3::Axis p_axis, real_t p_radius, const Transform &p_offset, const Basis &p_base, real_t p_limit_lower, real_t p_limit_upper, Vector &r_points, bool p_inverse) { - - if (p_limit_lower == p_limit_upper) { - - r_points.push_back(p_offset.translated(Vector3()).origin); - r_points.push_back(p_offset.translated(p_base.xform(Vector3(0.5, 0, 0))).origin); - - } else { - - if (p_limit_lower > p_limit_upper) { - p_limit_lower = -Math_PI; - p_limit_upper = Math_PI; - } - - const int points = 32; - - for (int i = 0; i < points; i++) { - - real_t s = p_limit_lower + i * (p_limit_upper - p_limit_lower) / points; - real_t n = p_limit_lower + (i + 1) * (p_limit_upper - p_limit_lower) / points; - - Vector3 from; - Vector3 to; - switch (p_axis) { - case Vector3::AXIS_X: - if (p_inverse) { - from = p_base.xform(Vector3(0, Math::sin(s), Math::cos(s))) * p_radius; - to = p_base.xform(Vector3(0, Math::sin(n), Math::cos(n))) * p_radius; - } else { - from = p_base.xform(Vector3(0, -Math::sin(s), Math::cos(s))) * p_radius; - to = p_base.xform(Vector3(0, -Math::sin(n), Math::cos(n))) * p_radius; - } - break; - case Vector3::AXIS_Y: - if (p_inverse) { - from = p_base.xform(Vector3(Math::cos(s), 0, -Math::sin(s))) * p_radius; - to = p_base.xform(Vector3(Math::cos(n), 0, -Math::sin(n))) * p_radius; - } else { - from = p_base.xform(Vector3(Math::cos(s), 0, Math::sin(s))) * p_radius; - to = p_base.xform(Vector3(Math::cos(n), 0, Math::sin(n))) * p_radius; - } - break; - case Vector3::AXIS_Z: - from = p_base.xform(Vector3(Math::cos(s), Math::sin(s), 0)) * p_radius; - to = p_base.xform(Vector3(Math::cos(n), Math::sin(n), 0)) * p_radius; - break; - } - - if (i == points - 1) { - r_points.push_back(p_offset.translated(to).origin); - r_points.push_back(p_offset.translated(Vector3()).origin); - } - if (i == 0) { - r_points.push_back(p_offset.translated(from).origin); - r_points.push_back(p_offset.translated(Vector3()).origin); - } - - r_points.push_back(p_offset.translated(from).origin); - r_points.push_back(p_offset.translated(to).origin); - } - - r_points.push_back(p_offset.translated(Vector3(0, p_radius * 1.5, 0)).origin); - r_points.push_back(p_offset.translated(Vector3()).origin); - } -} - -void JointGizmosDrawer::draw_cone(const Transform &p_offset, const Basis &p_base, real_t p_swing, real_t p_twist, Vector &r_points) { - - float r = 1.0; - float w = r * Math::sin(p_swing); - float d = r * Math::cos(p_swing); - - //swing - for (int i = 0; i < 360; i += 10) { - - float ra = Math::deg2rad((float)i); - float rb = Math::deg2rad((float)i + 10); - Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * w; - Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * w; - - r_points.push_back(p_offset.translated(p_base.xform(Vector3(d, a.x, a.y))).origin); - r_points.push_back(p_offset.translated(p_base.xform(Vector3(d, b.x, b.y))).origin); - - if (i % 90 == 0) { - - r_points.push_back(p_offset.translated(p_base.xform(Vector3(d, a.x, a.y))).origin); - r_points.push_back(p_offset.translated(p_base.xform(Vector3())).origin); - } - } - - r_points.push_back(p_offset.translated(p_base.xform(Vector3())).origin); - r_points.push_back(p_offset.translated(p_base.xform(Vector3(1, 0, 0))).origin); - - /// Twist - float ts = Math::rad2deg(p_twist); - ts = MIN(ts, 720); - - for (int i = 0; i < int(ts); i += 5) { - - float ra = Math::deg2rad((float)i); - float rb = Math::deg2rad((float)i + 5); - float c = i / 720.0; - float cn = (i + 5) / 720.0; - Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * w * c; - Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * w * cn; - - r_points.push_back(p_offset.translated(p_base.xform(Vector3(c, a.x, a.y))).origin); - r_points.push_back(p_offset.translated(p_base.xform(Vector3(cn, b.x, b.y))).origin); - } -} - -//// - -JointNode3DGizmoPlugin::JointNode3DGizmoPlugin() { - create_material("joint_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/joint", Color(0.5, 0.8, 1))); - create_material("joint_body_a_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/joint_body_a", Color(0.6, 0.8, 1))); - create_material("joint_body_b_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/joint_body_b", Color(0.6, 0.9, 1))); -} - -bool JointNode3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != NULL; -} - -String JointNode3DGizmoPlugin::get_name() const { - return "Joints"; -} - -int JointNode3DGizmoPlugin::get_priority() const { - return -1; -} - -void JointNode3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - Joint3D *joint = Object::cast_to(p_gizmo->get_spatial_node()); - - p_gizmo->clear(); - - Node3D *node_body_a = NULL; - if (!joint->get_node_a().is_empty()) { - node_body_a = Object::cast_to(joint->get_node(joint->get_node_a())); - } - - Node3D *node_body_b = NULL; - if (!joint->get_node_b().is_empty()) { - node_body_b = Object::cast_to(joint->get_node(joint->get_node_b())); - } - - if (!node_body_a && !node_body_b) { - return; - } - - Ref common_material = get_material("joint_material", p_gizmo); - Ref body_a_material = get_material("joint_body_a_material", p_gizmo); - Ref body_b_material = get_material("joint_body_b_material", p_gizmo); - - Vector points; - Vector body_a_points; - Vector body_b_points; - - if (Object::cast_to(joint)) { - CreatePinJointGizmo(Transform(), points); - p_gizmo->add_collision_segments(points); - p_gizmo->add_lines(points, common_material); - } - - HingeJoint3D *hinge = Object::cast_to(joint); - if (hinge) { - - CreateHingeJointGizmo( - Transform(), - hinge->get_global_transform(), - node_body_a ? node_body_a->get_global_transform() : Transform(), - node_body_b ? node_body_b->get_global_transform() : Transform(), - hinge->get_param(HingeJoint3D::PARAM_LIMIT_LOWER), - hinge->get_param(HingeJoint3D::PARAM_LIMIT_UPPER), - hinge->get_flag(HingeJoint3D::FLAG_USE_LIMIT), - points, - node_body_a ? &body_a_points : NULL, - node_body_b ? &body_b_points : NULL); - - p_gizmo->add_collision_segments(points); - p_gizmo->add_collision_segments(body_a_points); - p_gizmo->add_collision_segments(body_b_points); - - p_gizmo->add_lines(points, common_material); - p_gizmo->add_lines(body_a_points, body_a_material); - p_gizmo->add_lines(body_b_points, body_b_material); - } - - SliderJoint3D *slider = Object::cast_to(joint); - if (slider) { - - CreateSliderJointGizmo( - Transform(), - slider->get_global_transform(), - node_body_a ? node_body_a->get_global_transform() : Transform(), - node_body_b ? node_body_b->get_global_transform() : Transform(), - slider->get_param(SliderJoint3D::PARAM_ANGULAR_LIMIT_LOWER), - slider->get_param(SliderJoint3D::PARAM_ANGULAR_LIMIT_UPPER), - slider->get_param(SliderJoint3D::PARAM_LINEAR_LIMIT_LOWER), - slider->get_param(SliderJoint3D::PARAM_LINEAR_LIMIT_UPPER), - points, - node_body_a ? &body_a_points : NULL, - node_body_b ? &body_b_points : NULL); - - p_gizmo->add_collision_segments(points); - p_gizmo->add_collision_segments(body_a_points); - p_gizmo->add_collision_segments(body_b_points); - - p_gizmo->add_lines(points, common_material); - p_gizmo->add_lines(body_a_points, body_a_material); - p_gizmo->add_lines(body_b_points, body_b_material); - } - - ConeTwistJoint3D *cone = Object::cast_to(joint); - if (cone) { - - CreateConeTwistJointGizmo( - Transform(), - cone->get_global_transform(), - node_body_a ? node_body_a->get_global_transform() : Transform(), - node_body_b ? node_body_b->get_global_transform() : Transform(), - cone->get_param(ConeTwistJoint3D::PARAM_SWING_SPAN), - cone->get_param(ConeTwistJoint3D::PARAM_TWIST_SPAN), - node_body_a ? &body_a_points : NULL, - node_body_b ? &body_b_points : NULL); - - p_gizmo->add_collision_segments(body_a_points); - p_gizmo->add_collision_segments(body_b_points); - - p_gizmo->add_lines(body_a_points, body_a_material); - p_gizmo->add_lines(body_b_points, body_b_material); - } - - Generic6DOFJoint3D *gen = Object::cast_to(joint); - if (gen) { - - CreateGeneric6DOFJointGizmo( - Transform(), - gen->get_global_transform(), - node_body_a ? node_body_a->get_global_transform() : Transform(), - node_body_b ? node_body_b->get_global_transform() : Transform(), - - gen->get_param_x(Generic6DOFJoint3D::PARAM_ANGULAR_LOWER_LIMIT), - gen->get_param_x(Generic6DOFJoint3D::PARAM_ANGULAR_UPPER_LIMIT), - gen->get_param_x(Generic6DOFJoint3D::PARAM_LINEAR_LOWER_LIMIT), - gen->get_param_x(Generic6DOFJoint3D::PARAM_LINEAR_UPPER_LIMIT), - gen->get_flag_x(Generic6DOFJoint3D::FLAG_ENABLE_ANGULAR_LIMIT), - gen->get_flag_x(Generic6DOFJoint3D::FLAG_ENABLE_LINEAR_LIMIT), - - gen->get_param_y(Generic6DOFJoint3D::PARAM_ANGULAR_LOWER_LIMIT), - gen->get_param_y(Generic6DOFJoint3D::PARAM_ANGULAR_UPPER_LIMIT), - gen->get_param_y(Generic6DOFJoint3D::PARAM_LINEAR_LOWER_LIMIT), - gen->get_param_y(Generic6DOFJoint3D::PARAM_LINEAR_UPPER_LIMIT), - gen->get_flag_y(Generic6DOFJoint3D::FLAG_ENABLE_ANGULAR_LIMIT), - gen->get_flag_y(Generic6DOFJoint3D::FLAG_ENABLE_LINEAR_LIMIT), - - gen->get_param_z(Generic6DOFJoint3D::PARAM_ANGULAR_LOWER_LIMIT), - gen->get_param_z(Generic6DOFJoint3D::PARAM_ANGULAR_UPPER_LIMIT), - gen->get_param_z(Generic6DOFJoint3D::PARAM_LINEAR_LOWER_LIMIT), - gen->get_param_z(Generic6DOFJoint3D::PARAM_LINEAR_UPPER_LIMIT), - gen->get_flag_z(Generic6DOFJoint3D::FLAG_ENABLE_ANGULAR_LIMIT), - gen->get_flag_z(Generic6DOFJoint3D::FLAG_ENABLE_LINEAR_LIMIT), - - points, - node_body_a ? &body_a_points : NULL, - node_body_a ? &body_b_points : NULL); - - p_gizmo->add_collision_segments(points); - p_gizmo->add_collision_segments(body_a_points); - p_gizmo->add_collision_segments(body_b_points); - - p_gizmo->add_lines(points, common_material); - p_gizmo->add_lines(body_a_points, body_a_material); - p_gizmo->add_lines(body_b_points, body_b_material); - } -} - -void JointNode3DGizmoPlugin::CreatePinJointGizmo(const Transform &p_offset, Vector &r_cursor_points) { - float cs = 0.25; - - r_cursor_points.push_back(p_offset.translated(Vector3(+cs, 0, 0)).origin); - r_cursor_points.push_back(p_offset.translated(Vector3(-cs, 0, 0)).origin); - r_cursor_points.push_back(p_offset.translated(Vector3(0, +cs, 0)).origin); - r_cursor_points.push_back(p_offset.translated(Vector3(0, -cs, 0)).origin); - r_cursor_points.push_back(p_offset.translated(Vector3(0, 0, +cs)).origin); - r_cursor_points.push_back(p_offset.translated(Vector3(0, 0, -cs)).origin); -} - -void JointNode3DGizmoPlugin::CreateHingeJointGizmo(const Transform &p_offset, const Transform &p_trs_joint, const Transform &p_trs_body_a, const Transform &p_trs_body_b, real_t p_limit_lower, real_t p_limit_upper, bool p_use_limit, Vector &r_common_points, Vector *r_body_a_points, Vector *r_body_b_points) { - - r_common_points.push_back(p_offset.translated(Vector3(0, 0, 0.5)).origin); - r_common_points.push_back(p_offset.translated(Vector3(0, 0, -0.5)).origin); - - if (!p_use_limit) { - p_limit_upper = -1; - p_limit_lower = 0; - } - - if (r_body_a_points) { - - JointGizmosDrawer::draw_circle(Vector3::AXIS_Z, - BODY_A_RADIUS, - p_offset, - JointGizmosDrawer::look_body_toward_z(p_trs_joint, p_trs_body_a), - p_limit_lower, - p_limit_upper, - *r_body_a_points); - } - - if (r_body_b_points) { - JointGizmosDrawer::draw_circle(Vector3::AXIS_Z, - BODY_B_RADIUS, - p_offset, - JointGizmosDrawer::look_body_toward_z(p_trs_joint, p_trs_body_b), - p_limit_lower, - p_limit_upper, - *r_body_b_points); - } -} - -void JointNode3DGizmoPlugin::CreateSliderJointGizmo(const Transform &p_offset, const Transform &p_trs_joint, const Transform &p_trs_body_a, const Transform &p_trs_body_b, real_t p_angular_limit_lower, real_t p_angular_limit_upper, real_t p_linear_limit_lower, real_t p_linear_limit_upper, Vector &r_points, Vector *r_body_a_points, Vector *r_body_b_points) { - - p_linear_limit_lower = -p_linear_limit_lower; - p_linear_limit_upper = -p_linear_limit_upper; - - float cs = 0.25; - r_points.push_back(p_offset.translated(Vector3(0, 0, 0.5)).origin); - r_points.push_back(p_offset.translated(Vector3(0, 0, -0.5)).origin); - - if (p_linear_limit_lower >= p_linear_limit_upper) { - - r_points.push_back(p_offset.translated(Vector3(p_linear_limit_upper, 0, 0)).origin); - r_points.push_back(p_offset.translated(Vector3(p_linear_limit_lower, 0, 0)).origin); - - r_points.push_back(p_offset.translated(Vector3(p_linear_limit_upper, -cs, -cs)).origin); - r_points.push_back(p_offset.translated(Vector3(p_linear_limit_upper, -cs, cs)).origin); - r_points.push_back(p_offset.translated(Vector3(p_linear_limit_upper, -cs, cs)).origin); - r_points.push_back(p_offset.translated(Vector3(p_linear_limit_upper, cs, cs)).origin); - r_points.push_back(p_offset.translated(Vector3(p_linear_limit_upper, cs, cs)).origin); - r_points.push_back(p_offset.translated(Vector3(p_linear_limit_upper, cs, -cs)).origin); - r_points.push_back(p_offset.translated(Vector3(p_linear_limit_upper, cs, -cs)).origin); - r_points.push_back(p_offset.translated(Vector3(p_linear_limit_upper, -cs, -cs)).origin); - - r_points.push_back(p_offset.translated(Vector3(p_linear_limit_lower, -cs, -cs)).origin); - r_points.push_back(p_offset.translated(Vector3(p_linear_limit_lower, -cs, cs)).origin); - r_points.push_back(p_offset.translated(Vector3(p_linear_limit_lower, -cs, cs)).origin); - r_points.push_back(p_offset.translated(Vector3(p_linear_limit_lower, cs, cs)).origin); - r_points.push_back(p_offset.translated(Vector3(p_linear_limit_lower, cs, cs)).origin); - r_points.push_back(p_offset.translated(Vector3(p_linear_limit_lower, cs, -cs)).origin); - r_points.push_back(p_offset.translated(Vector3(p_linear_limit_lower, cs, -cs)).origin); - r_points.push_back(p_offset.translated(Vector3(p_linear_limit_lower, -cs, -cs)).origin); - - } else { - - r_points.push_back(p_offset.translated(Vector3(+cs * 2, 0, 0)).origin); - r_points.push_back(p_offset.translated(Vector3(-cs * 2, 0, 0)).origin); - } - - if (r_body_a_points) - JointGizmosDrawer::draw_circle( - Vector3::AXIS_X, - BODY_A_RADIUS, - p_offset, - JointGizmosDrawer::look_body_toward(Vector3::AXIS_X, p_trs_joint, p_trs_body_a), - p_angular_limit_lower, - p_angular_limit_upper, - *r_body_a_points); - - if (r_body_b_points) - JointGizmosDrawer::draw_circle( - Vector3::AXIS_X, - BODY_B_RADIUS, - p_offset, - JointGizmosDrawer::look_body_toward(Vector3::AXIS_X, p_trs_joint, p_trs_body_b), - p_angular_limit_lower, - p_angular_limit_upper, - *r_body_b_points, - true); -} - -void JointNode3DGizmoPlugin::CreateConeTwistJointGizmo(const Transform &p_offset, const Transform &p_trs_joint, const Transform &p_trs_body_a, const Transform &p_trs_body_b, real_t p_swing, real_t p_twist, Vector *r_body_a_points, Vector *r_body_b_points) { - - if (r_body_a_points) - JointGizmosDrawer::draw_cone( - p_offset, - JointGizmosDrawer::look_body(p_trs_joint, p_trs_body_a), - p_swing, - p_twist, - *r_body_a_points); - - if (r_body_b_points) - JointGizmosDrawer::draw_cone( - p_offset, - JointGizmosDrawer::look_body(p_trs_joint, p_trs_body_b), - p_swing, - p_twist, - *r_body_b_points); -} - -void JointNode3DGizmoPlugin::CreateGeneric6DOFJointGizmo( - const Transform &p_offset, - const Transform &p_trs_joint, - const Transform &p_trs_body_a, - const Transform &p_trs_body_b, - real_t p_angular_limit_lower_x, - real_t p_angular_limit_upper_x, - real_t p_linear_limit_lower_x, - real_t p_linear_limit_upper_x, - bool p_enable_angular_limit_x, - bool p_enable_linear_limit_x, - real_t p_angular_limit_lower_y, - real_t p_angular_limit_upper_y, - real_t p_linear_limit_lower_y, - real_t p_linear_limit_upper_y, - bool p_enable_angular_limit_y, - bool p_enable_linear_limit_y, - real_t p_angular_limit_lower_z, - real_t p_angular_limit_upper_z, - real_t p_linear_limit_lower_z, - real_t p_linear_limit_upper_z, - bool p_enable_angular_limit_z, - bool p_enable_linear_limit_z, - Vector &r_points, - Vector *r_body_a_points, - Vector *r_body_b_points) { - - float cs = 0.25; - - for (int ax = 0; ax < 3; ax++) { - float ll = 0; - float ul = 0; - float lll = 0; - float lul = 0; - - int a1 = 0; - int a2 = 0; - int a3 = 0; - bool enable_ang = false; - bool enable_lin = false; - - switch (ax) { - case 0: - ll = p_angular_limit_lower_x; - ul = p_angular_limit_upper_x; - lll = -p_linear_limit_lower_x; - lul = -p_linear_limit_upper_x; - enable_ang = p_enable_angular_limit_x; - enable_lin = p_enable_linear_limit_x; - a1 = 0; - a2 = 1; - a3 = 2; - break; - case 1: - ll = p_angular_limit_lower_y; - ul = p_angular_limit_upper_y; - lll = -p_linear_limit_lower_y; - lul = -p_linear_limit_upper_y; - enable_ang = p_enable_angular_limit_y; - enable_lin = p_enable_linear_limit_y; - a1 = 1; - a2 = 2; - a3 = 0; - break; - case 2: - ll = p_angular_limit_lower_z; - ul = p_angular_limit_upper_z; - lll = -p_linear_limit_lower_z; - lul = -p_linear_limit_upper_z; - enable_ang = p_enable_angular_limit_z; - enable_lin = p_enable_linear_limit_z; - a1 = 2; - a2 = 0; - a3 = 1; - break; - } - -#define ADD_VTX(x, y, z) \ - { \ - Vector3 v; \ - v[a1] = (x); \ - v[a2] = (y); \ - v[a3] = (z); \ - r_points.push_back(p_offset.translated(v).origin); \ - } - - if (enable_lin && lll >= lul) { - - ADD_VTX(lul, 0, 0); - ADD_VTX(lll, 0, 0); - - ADD_VTX(lul, -cs, -cs); - ADD_VTX(lul, -cs, cs); - ADD_VTX(lul, -cs, cs); - ADD_VTX(lul, cs, cs); - ADD_VTX(lul, cs, cs); - ADD_VTX(lul, cs, -cs); - ADD_VTX(lul, cs, -cs); - ADD_VTX(lul, -cs, -cs); - - ADD_VTX(lll, -cs, -cs); - ADD_VTX(lll, -cs, cs); - ADD_VTX(lll, -cs, cs); - ADD_VTX(lll, cs, cs); - ADD_VTX(lll, cs, cs); - ADD_VTX(lll, cs, -cs); - ADD_VTX(lll, cs, -cs); - ADD_VTX(lll, -cs, -cs); - - } else { - - ADD_VTX(+cs * 2, 0, 0); - ADD_VTX(-cs * 2, 0, 0); - } - - if (!enable_ang) { - ll = 0; - ul = -1; - } - - if (r_body_a_points) - JointGizmosDrawer::draw_circle( - static_cast(ax), - BODY_A_RADIUS, - p_offset, - JointGizmosDrawer::look_body_toward(static_cast(ax), p_trs_joint, p_trs_body_a), - ll, - ul, - *r_body_a_points, - true); - - if (r_body_b_points) - JointGizmosDrawer::draw_circle( - static_cast(ax), - BODY_B_RADIUS, - p_offset, - JointGizmosDrawer::look_body_toward(static_cast(ax), p_trs_joint, p_trs_body_b), - ll, - ul, - *r_body_b_points); - } - -#undef ADD_VTX -} diff --git a/editor/spatial_editor_gizmos.h b/editor/spatial_editor_gizmos.h deleted file mode 100644 index 86926b38d2..0000000000 --- a/editor/spatial_editor_gizmos.h +++ /dev/null @@ -1,434 +0,0 @@ -/*************************************************************************/ -/* spatial_editor_gizmos.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef SPATIAL_EDITOR_GIZMOS_H -#define SPATIAL_EDITOR_GIZMOS_H - -#include "editor/plugins/spatial_editor_plugin.h" -#include "scene/3d/camera_3d.h" - -class Camera3D; - -class LightNode3DGizmoPlugin : public EditorNode3DGizmoPlugin { - - GDCLASS(LightNode3DGizmoPlugin, EditorNode3DGizmoPlugin); - -public: - bool has_gizmo(Node3D *p_spatial); - String get_name() const; - int get_priority() const; - - String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const; - Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const; - void set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point); - void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false); - void redraw(EditorNode3DGizmo *p_gizmo); - - LightNode3DGizmoPlugin(); -}; - -class AudioStreamPlayer3DNode3DGizmoPlugin : public EditorNode3DGizmoPlugin { - - GDCLASS(AudioStreamPlayer3DNode3DGizmoPlugin, EditorNode3DGizmoPlugin); - -public: - bool has_gizmo(Node3D *p_spatial); - String get_name() const; - int get_priority() const; - - String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const; - Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const; - void set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point); - void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false); - void redraw(EditorNode3DGizmo *p_gizmo); - - AudioStreamPlayer3DNode3DGizmoPlugin(); -}; - -class CameraNode3DGizmoPlugin : public EditorNode3DGizmoPlugin { - - GDCLASS(CameraNode3DGizmoPlugin, EditorNode3DGizmoPlugin); - -public: - bool has_gizmo(Node3D *p_spatial); - String get_name() const; - int get_priority() const; - - String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const; - Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const; - void set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point); - void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false); - void redraw(EditorNode3DGizmo *p_gizmo); - - CameraNode3DGizmoPlugin(); -}; - -class MeshInstanceNode3DGizmoPlugin : public EditorNode3DGizmoPlugin { - - GDCLASS(MeshInstanceNode3DGizmoPlugin, EditorNode3DGizmoPlugin); - -public: - bool has_gizmo(Node3D *p_spatial); - String get_name() const; - int get_priority() const; - bool can_be_hidden() const; - void redraw(EditorNode3DGizmo *p_gizmo); - - MeshInstanceNode3DGizmoPlugin(); -}; - -class Sprite3DNode3DGizmoPlugin : public EditorNode3DGizmoPlugin { - - GDCLASS(Sprite3DNode3DGizmoPlugin, EditorNode3DGizmoPlugin); - -public: - bool has_gizmo(Node3D *p_spatial); - String get_name() const; - int get_priority() const; - bool can_be_hidden() const; - void redraw(EditorNode3DGizmo *p_gizmo); - - Sprite3DNode3DGizmoPlugin(); -}; - -class Position3DNode3DGizmoPlugin : public EditorNode3DGizmoPlugin { - - GDCLASS(Position3DNode3DGizmoPlugin, EditorNode3DGizmoPlugin); - - Ref pos3d_mesh; - Vector cursor_points; - -public: - bool has_gizmo(Node3D *p_spatial); - String get_name() const; - int get_priority() const; - void redraw(EditorNode3DGizmo *p_gizmo); - - Position3DNode3DGizmoPlugin(); -}; - -class SkeletonNode3DGizmoPlugin : public EditorNode3DGizmoPlugin { - - GDCLASS(SkeletonNode3DGizmoPlugin, EditorNode3DGizmoPlugin); - -public: - bool has_gizmo(Node3D *p_spatial); - String get_name() const; - int get_priority() const; - void redraw(EditorNode3DGizmo *p_gizmo); - - SkeletonNode3DGizmoPlugin(); -}; - -class PhysicalBoneNode3DGizmoPlugin : public EditorNode3DGizmoPlugin { - - GDCLASS(PhysicalBoneNode3DGizmoPlugin, EditorNode3DGizmoPlugin); - -public: - bool has_gizmo(Node3D *p_spatial); - String get_name() const; - int get_priority() const; - void redraw(EditorNode3DGizmo *p_gizmo); - - PhysicalBoneNode3DGizmoPlugin(); -}; - -class RayCastNode3DGizmoPlugin : public EditorNode3DGizmoPlugin { - - GDCLASS(RayCastNode3DGizmoPlugin, EditorNode3DGizmoPlugin); - -public: - bool has_gizmo(Node3D *p_spatial); - String get_name() const; - int get_priority() const; - void redraw(EditorNode3DGizmo *p_gizmo); - - RayCastNode3DGizmoPlugin(); -}; - -class SpringArmNode3DGizmoPlugin : public EditorNode3DGizmoPlugin { - - GDCLASS(SpringArmNode3DGizmoPlugin, EditorNode3DGizmoPlugin); - -public: - bool has_gizmo(Node3D *p_spatial); - String get_name() const; - int get_priority() const; - void redraw(EditorNode3DGizmo *p_gizmo); - - SpringArmNode3DGizmoPlugin(); -}; - -class VehicleWheelNode3DGizmoPlugin : public EditorNode3DGizmoPlugin { - - GDCLASS(VehicleWheelNode3DGizmoPlugin, EditorNode3DGizmoPlugin); - -public: - bool has_gizmo(Node3D *p_spatial); - String get_name() const; - int get_priority() const; - void redraw(EditorNode3DGizmo *p_gizmo); - - VehicleWheelNode3DGizmoPlugin(); -}; - -class SoftBodyNode3DGizmoPlugin : public EditorNode3DGizmoPlugin { - - GDCLASS(SoftBodyNode3DGizmoPlugin, EditorNode3DGizmoPlugin); - -public: - bool has_gizmo(Node3D *p_spatial); - String get_name() const; - int get_priority() const; - bool is_selectable_when_hidden() const; - void redraw(EditorNode3DGizmo *p_gizmo); - - String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const; - Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const; - void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel); - bool is_handle_highlighted(const EditorNode3DGizmo *p_gizmo, int idx) const; - - SoftBodyNode3DGizmoPlugin(); -}; - -class VisibilityNotifierGizmoPlugin : public EditorNode3DGizmoPlugin { - - GDCLASS(VisibilityNotifierGizmoPlugin, EditorNode3DGizmoPlugin); - -public: - bool has_gizmo(Node3D *p_spatial); - String get_name() const; - int get_priority() const; - void redraw(EditorNode3DGizmo *p_gizmo); - - String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const; - Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const; - void set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point); - void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false); - - VisibilityNotifierGizmoPlugin(); -}; - -class CPUParticlesGizmoPlugin : public EditorNode3DGizmoPlugin { - GDCLASS(CPUParticlesGizmoPlugin, EditorNode3DGizmoPlugin); - -public: - bool has_gizmo(Node3D *p_spatial); - String get_name() const; - int get_priority() const; - bool is_selectable_when_hidden() const; - void redraw(EditorNode3DGizmo *p_gizmo); - CPUParticlesGizmoPlugin(); -}; - -class ParticlesGizmoPlugin : public EditorNode3DGizmoPlugin { - - GDCLASS(ParticlesGizmoPlugin, EditorNode3DGizmoPlugin); - -public: - bool has_gizmo(Node3D *p_spatial); - String get_name() const; - int get_priority() const; - bool is_selectable_when_hidden() const; - void redraw(EditorNode3DGizmo *p_gizmo); - - String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const; - Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const; - void set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point); - void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false); - - ParticlesGizmoPlugin(); -}; - -class ReflectionProbeGizmoPlugin : public EditorNode3DGizmoPlugin { - - GDCLASS(ReflectionProbeGizmoPlugin, EditorNode3DGizmoPlugin); - -public: - bool has_gizmo(Node3D *p_spatial); - String get_name() const; - int get_priority() const; - void redraw(EditorNode3DGizmo *p_gizmo); - - String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const; - Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const; - void set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point); - void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false); - - ReflectionProbeGizmoPlugin(); -}; - -class GIProbeGizmoPlugin : public EditorNode3DGizmoPlugin { - - GDCLASS(GIProbeGizmoPlugin, EditorNode3DGizmoPlugin); - -public: - bool has_gizmo(Node3D *p_spatial); - String get_name() const; - int get_priority() const; - void redraw(EditorNode3DGizmo *p_gizmo); - - String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const; - Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const; - void set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point); - void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false); - - GIProbeGizmoPlugin(); -}; - -#if 0 -class BakedIndirectLightGizmoPlugin : public EditorNode3DGizmoPlugin { - - GDCLASS(BakedIndirectLightGizmoPlugin, EditorNode3DGizmoPlugin); - -public: - bool has_gizmo(Spatial *p_spatial); - String get_name() const; - int get_priority() const; - void redraw(EditorNode3DGizmo *p_gizmo); - - String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const; - Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const; - void set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera *p_camera, const Point2 &p_point); - void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false); - - BakedIndirectLightGizmoPlugin(); -}; -#endif -class CollisionShapeNode3DGizmoPlugin : public EditorNode3DGizmoPlugin { - - GDCLASS(CollisionShapeNode3DGizmoPlugin, EditorNode3DGizmoPlugin); - -public: - bool has_gizmo(Node3D *p_spatial); - String get_name() const; - int get_priority() const; - void redraw(EditorNode3DGizmo *p_gizmo); - - String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const; - Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const; - void set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point); - void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false); - - CollisionShapeNode3DGizmoPlugin(); -}; - -class CollisionPolygonNode3DGizmoPlugin : public EditorNode3DGizmoPlugin { - GDCLASS(CollisionPolygonNode3DGizmoPlugin, EditorNode3DGizmoPlugin); - -public: - bool has_gizmo(Node3D *p_spatial); - String get_name() const; - int get_priority() const; - void redraw(EditorNode3DGizmo *p_gizmo); - CollisionPolygonNode3DGizmoPlugin(); -}; - -class NavigationMeshNode3DGizmoPlugin : public EditorNode3DGizmoPlugin { - - GDCLASS(NavigationMeshNode3DGizmoPlugin, EditorNode3DGizmoPlugin); - - struct _EdgeKey { - - Vector3 from; - Vector3 to; - - bool operator<(const _EdgeKey &p_with) const { return from == p_with.from ? to < p_with.to : from < p_with.from; } - }; - -public: - bool has_gizmo(Node3D *p_spatial); - String get_name() const; - int get_priority() const; - void redraw(EditorNode3DGizmo *p_gizmo); - - NavigationMeshNode3DGizmoPlugin(); -}; - -class JointGizmosDrawer { -public: - static Basis look_body(const Transform &p_joint_transform, const Transform &p_body_transform); - static Basis look_body_toward(Vector3::Axis p_axis, const Transform &joint_transform, const Transform &body_transform); - static Basis look_body_toward_x(const Transform &p_joint_transform, const Transform &p_body_transform); - static Basis look_body_toward_y(const Transform &p_joint_transform, const Transform &p_body_transform); - /// Special function just used for physics joints, it returns a basis constrained toward Joint Z axis - /// with axis X and Y that are looking toward the body and oriented toward up - static Basis look_body_toward_z(const Transform &p_joint_transform, const Transform &p_body_transform); - - // Draw circle around p_axis - static void draw_circle(Vector3::Axis p_axis, real_t p_radius, const Transform &p_offset, const Basis &p_base, real_t p_limit_lower, real_t p_limit_upper, Vector &r_points, bool p_inverse = false); - static void draw_cone(const Transform &p_offset, const Basis &p_base, real_t p_swing, real_t p_twist, Vector &r_points); -}; - -class JointNode3DGizmoPlugin : public EditorNode3DGizmoPlugin { - - GDCLASS(JointNode3DGizmoPlugin, EditorNode3DGizmoPlugin); - -public: - bool has_gizmo(Node3D *p_spatial); - String get_name() const; - int get_priority() const; - void redraw(EditorNode3DGizmo *p_gizmo); - - static void CreatePinJointGizmo(const Transform &p_offset, Vector &r_cursor_points); - static void CreateHingeJointGizmo(const Transform &p_offset, const Transform &p_trs_joint, const Transform &p_trs_body_a, const Transform &p_trs_body_b, real_t p_limit_lower, real_t p_limit_upper, bool p_use_limit, Vector &r_common_points, Vector *r_body_a_points, Vector *r_body_b_points); - static void CreateSliderJointGizmo(const Transform &p_offset, const Transform &p_trs_joint, const Transform &p_trs_body_a, const Transform &p_trs_body_b, real_t p_angular_limit_lower, real_t p_angular_limit_upper, real_t p_linear_limit_lower, real_t p_linear_limit_upper, Vector &r_points, Vector *r_body_a_points, Vector *r_body_b_points); - static void CreateConeTwistJointGizmo(const Transform &p_offset, const Transform &p_trs_joint, const Transform &p_trs_body_a, const Transform &p_trs_body_b, real_t p_swing, real_t p_twist, Vector *r_body_a_points, Vector *r_body_b_points); - static void CreateGeneric6DOFJointGizmo( - const Transform &p_offset, - const Transform &p_trs_joint, - const Transform &p_trs_body_a, - const Transform &p_trs_body_b, - real_t p_angular_limit_lower_x, - real_t p_angular_limit_upper_x, - real_t p_linear_limit_lower_x, - real_t p_linear_limit_upper_x, - bool p_enable_angular_limit_x, - bool p_enable_linear_limit_x, - real_t p_angular_limit_lower_y, - real_t p_angular_limit_upper_y, - real_t p_linear_limit_lower_y, - real_t p_linear_limit_upper_y, - bool p_enable_angular_limit_y, - bool p_enable_linear_limit_y, - real_t p_angular_limit_lower_z, - real_t p_angular_limit_upper_z, - real_t p_linear_limit_lower_z, - real_t p_linear_limit_upper_z, - bool p_enable_angular_limit_z, - bool p_enable_linear_limit_z, - Vector &r_points, - Vector *r_body_a_points, - Vector *r_body_b_points); - - JointNode3DGizmoPlugin(); -}; - -#endif // SPATIAL_EDITOR_GIZMOS_H diff --git a/modules/csg/csg_gizmos.h b/modules/csg/csg_gizmos.h index cca27a4629..90a059b31c 100644 --- a/modules/csg/csg_gizmos.h +++ b/modules/csg/csg_gizmos.h @@ -33,7 +33,7 @@ #include "csg_shape.h" #include "editor/editor_plugin.h" -#include "editor/spatial_editor_gizmos.h" +#include "editor/node_3d_editor_gizmos.h" class CSGShapeNode3DGizmoPlugin : public EditorNode3DGizmoPlugin { diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp index 0f377e2dec..a2479e764f 100644 --- a/modules/gridmap/grid_map_editor_plugin.cpp +++ b/modules/gridmap/grid_map_editor_plugin.cpp @@ -32,7 +32,7 @@ #include "core/input/input_filter.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" -#include "editor/plugins/spatial_editor_plugin.h" +#include "editor/plugins/node_3d_editor_plugin.h" #include "scene/3d/camera_3d.h" #include "core/math/geometry.h" diff --git a/scene/2d/canvas_item.cpp b/scene/2d/canvas_item.cpp deleted file mode 100644 index 7a0fc3352b..0000000000 --- a/scene/2d/canvas_item.cpp +++ /dev/null @@ -1,1491 +0,0 @@ -/*************************************************************************/ -/* canvas_item.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "canvas_item.h" - -#include "core/input/input_filter.h" -#include "core/message_queue.h" -#include "core/method_bind_ext.gen.inc" -#include "scene/main/canvas_layer.h" -#include "scene/main/viewport.h" -#include "scene/main/window.h" -#include "scene/resources/font.h" -#include "scene/resources/style_box.h" -#include "scene/resources/texture.h" -#include "scene/scene_string_names.h" -#include "servers/visual/visual_server_raster.h" -#include "servers/visual_server.h" - -Mutex CanvasItemMaterial::material_mutex; -SelfList::List *CanvasItemMaterial::dirty_materials = NULL; -Map CanvasItemMaterial::shader_map; -CanvasItemMaterial::ShaderNames *CanvasItemMaterial::shader_names = NULL; - -void CanvasItemMaterial::init_shaders() { - - dirty_materials = memnew(SelfList::List); - - shader_names = memnew(ShaderNames); - - shader_names->particles_anim_h_frames = "particles_anim_h_frames"; - shader_names->particles_anim_v_frames = "particles_anim_v_frames"; - shader_names->particles_anim_loop = "particles_anim_loop"; -} - -void CanvasItemMaterial::finish_shaders() { - - memdelete(dirty_materials); - memdelete(shader_names); - dirty_materials = NULL; -} - -void CanvasItemMaterial::_update_shader() { - - dirty_materials->remove(&element); - - MaterialKey mk = _compute_key(); - if (mk.key == current_key.key) - return; //no update required in the end - - if (shader_map.has(current_key)) { - shader_map[current_key].users--; - if (shader_map[current_key].users == 0) { - //deallocate shader, as it's no longer in use - VS::get_singleton()->free(shader_map[current_key].shader); - shader_map.erase(current_key); - } - } - - current_key = mk; - - if (shader_map.has(mk)) { - - VS::get_singleton()->material_set_shader(_get_material(), shader_map[mk].shader); - shader_map[mk].users++; - return; - } - - //must create a shader! - - String code = "shader_type canvas_item;\nrender_mode "; - switch (blend_mode) { - case BLEND_MODE_MIX: code += "blend_mix"; break; - case BLEND_MODE_ADD: code += "blend_add"; break; - case BLEND_MODE_SUB: code += "blend_sub"; break; - case BLEND_MODE_MUL: code += "blend_mul"; break; - case BLEND_MODE_PREMULT_ALPHA: code += "blend_premul_alpha"; break; - case BLEND_MODE_DISABLED: code += "blend_disabled"; break; - } - - switch (light_mode) { - case LIGHT_MODE_NORMAL: break; - case LIGHT_MODE_UNSHADED: code += ",unshaded"; break; - case LIGHT_MODE_LIGHT_ONLY: code += ",light_only"; break; - } - - code += ";\n"; - - if (particles_animation) { - - code += "uniform int particles_anim_h_frames;\n"; - code += "uniform int particles_anim_v_frames;\n"; - code += "uniform bool particles_anim_loop;\n"; - - code += "void vertex() {\n"; - - code += "\tfloat h_frames = float(particles_anim_h_frames);\n"; - code += "\tfloat v_frames = float(particles_anim_v_frames);\n"; - - code += "\tVERTEX.xy /= vec2(h_frames, v_frames);\n"; - - code += "\tfloat particle_total_frames = float(particles_anim_h_frames * particles_anim_v_frames);\n"; - code += "\tfloat particle_frame = floor(INSTANCE_CUSTOM.z * float(particle_total_frames));\n"; - code += "\tif (!particles_anim_loop) {\n"; - code += "\t\tparticle_frame = clamp(particle_frame, 0.0, particle_total_frames - 1.0);\n"; - code += "\t} else {\n"; - code += "\t\tparticle_frame = mod(particle_frame, particle_total_frames);\n"; - code += "\t}"; - code += "\tUV /= vec2(h_frames, v_frames);\n"; - code += "\tUV += vec2(mod(particle_frame, h_frames) / h_frames, floor(particle_frame / h_frames) / v_frames);\n"; - code += "}\n"; - } - - ShaderData shader_data; - shader_data.shader = VS::get_singleton()->shader_create(); - shader_data.users = 1; - - VS::get_singleton()->shader_set_code(shader_data.shader, code); - - shader_map[mk] = shader_data; - - VS::get_singleton()->material_set_shader(_get_material(), shader_data.shader); -} - -void CanvasItemMaterial::flush_changes() { - - MutexLock lock(material_mutex); - - while (dirty_materials->first()) { - - dirty_materials->first()->self()->_update_shader(); - } -} - -void CanvasItemMaterial::_queue_shader_change() { - - MutexLock lock(material_mutex); - - if (!element.in_list()) { - dirty_materials->add(&element); - } -} - -bool CanvasItemMaterial::_is_shader_dirty() const { - - MutexLock lock(material_mutex); - - return element.in_list(); -} -void CanvasItemMaterial::set_blend_mode(BlendMode p_blend_mode) { - - blend_mode = p_blend_mode; - _queue_shader_change(); -} - -CanvasItemMaterial::BlendMode CanvasItemMaterial::get_blend_mode() const { - return blend_mode; -} - -void CanvasItemMaterial::set_light_mode(LightMode p_light_mode) { - - light_mode = p_light_mode; - _queue_shader_change(); -} - -CanvasItemMaterial::LightMode CanvasItemMaterial::get_light_mode() const { - - return light_mode; -} - -void CanvasItemMaterial::set_particles_animation(bool p_particles_anim) { - particles_animation = p_particles_anim; - _queue_shader_change(); - _change_notify(); -} - -bool CanvasItemMaterial::get_particles_animation() const { - return particles_animation; -} - -void CanvasItemMaterial::set_particles_anim_h_frames(int p_frames) { - - particles_anim_h_frames = p_frames; - VS::get_singleton()->material_set_param(_get_material(), shader_names->particles_anim_h_frames, p_frames); -} - -int CanvasItemMaterial::get_particles_anim_h_frames() const { - - return particles_anim_h_frames; -} -void CanvasItemMaterial::set_particles_anim_v_frames(int p_frames) { - - particles_anim_v_frames = p_frames; - VS::get_singleton()->material_set_param(_get_material(), shader_names->particles_anim_v_frames, p_frames); -} - -int CanvasItemMaterial::get_particles_anim_v_frames() const { - - return particles_anim_v_frames; -} - -void CanvasItemMaterial::set_particles_anim_loop(bool p_loop) { - - particles_anim_loop = p_loop; - VS::get_singleton()->material_set_param(_get_material(), shader_names->particles_anim_loop, particles_anim_loop); -} - -bool CanvasItemMaterial::get_particles_anim_loop() const { - - return particles_anim_loop; -} - -void CanvasItemMaterial::_validate_property(PropertyInfo &property) const { - if (property.name.begins_with("particles_anim_") && !particles_animation) { - property.usage = 0; - } -} - -RID CanvasItemMaterial::get_shader_rid() const { - - ERR_FAIL_COND_V(!shader_map.has(current_key), RID()); - return shader_map[current_key].shader; -} - -Shader::Mode CanvasItemMaterial::get_shader_mode() const { - - return Shader::MODE_CANVAS_ITEM; -} - -void CanvasItemMaterial::_bind_methods() { - - ClassDB::bind_method(D_METHOD("set_blend_mode", "blend_mode"), &CanvasItemMaterial::set_blend_mode); - ClassDB::bind_method(D_METHOD("get_blend_mode"), &CanvasItemMaterial::get_blend_mode); - - ClassDB::bind_method(D_METHOD("set_light_mode", "light_mode"), &CanvasItemMaterial::set_light_mode); - ClassDB::bind_method(D_METHOD("get_light_mode"), &CanvasItemMaterial::get_light_mode); - - ClassDB::bind_method(D_METHOD("set_particles_animation", "particles_anim"), &CanvasItemMaterial::set_particles_animation); - ClassDB::bind_method(D_METHOD("get_particles_animation"), &CanvasItemMaterial::get_particles_animation); - - ClassDB::bind_method(D_METHOD("set_particles_anim_h_frames", "frames"), &CanvasItemMaterial::set_particles_anim_h_frames); - ClassDB::bind_method(D_METHOD("get_particles_anim_h_frames"), &CanvasItemMaterial::get_particles_anim_h_frames); - - ClassDB::bind_method(D_METHOD("set_particles_anim_v_frames", "frames"), &CanvasItemMaterial::set_particles_anim_v_frames); - ClassDB::bind_method(D_METHOD("get_particles_anim_v_frames"), &CanvasItemMaterial::get_particles_anim_v_frames); - - ClassDB::bind_method(D_METHOD("set_particles_anim_loop", "loop"), &CanvasItemMaterial::set_particles_anim_loop); - ClassDB::bind_method(D_METHOD("get_particles_anim_loop"), &CanvasItemMaterial::get_particles_anim_loop); - - ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_mode", PROPERTY_HINT_ENUM, "Mix,Add,Sub,Mul,Premult Alpha"), "set_blend_mode", "get_blend_mode"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "light_mode", PROPERTY_HINT_ENUM, "Normal,Unshaded,Light Only"), "set_light_mode", "get_light_mode"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "particles_animation"), "set_particles_animation", "get_particles_animation"); - - ADD_PROPERTY(PropertyInfo(Variant::INT, "particles_anim_h_frames", PROPERTY_HINT_RANGE, "1,128,1"), "set_particles_anim_h_frames", "get_particles_anim_h_frames"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "particles_anim_v_frames", PROPERTY_HINT_RANGE, "1,128,1"), "set_particles_anim_v_frames", "get_particles_anim_v_frames"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "particles_anim_loop"), "set_particles_anim_loop", "get_particles_anim_loop"); - - BIND_ENUM_CONSTANT(BLEND_MODE_MIX); - BIND_ENUM_CONSTANT(BLEND_MODE_ADD); - BIND_ENUM_CONSTANT(BLEND_MODE_SUB); - BIND_ENUM_CONSTANT(BLEND_MODE_MUL); - BIND_ENUM_CONSTANT(BLEND_MODE_PREMULT_ALPHA); - - BIND_ENUM_CONSTANT(LIGHT_MODE_NORMAL); - BIND_ENUM_CONSTANT(LIGHT_MODE_UNSHADED); - BIND_ENUM_CONSTANT(LIGHT_MODE_LIGHT_ONLY); -} - -CanvasItemMaterial::CanvasItemMaterial() : - element(this) { - - blend_mode = BLEND_MODE_MIX; - light_mode = LIGHT_MODE_NORMAL; - particles_animation = false; - - set_particles_anim_h_frames(1); - set_particles_anim_v_frames(1); - set_particles_anim_loop(false); - - current_key.key = 0; - current_key.invalid_key = 1; - _queue_shader_change(); -} - -CanvasItemMaterial::~CanvasItemMaterial() { - - MutexLock lock(material_mutex); - - if (shader_map.has(current_key)) { - shader_map[current_key].users--; - if (shader_map[current_key].users == 0) { - //deallocate shader, as it's no longer in use - VS::get_singleton()->free(shader_map[current_key].shader); - shader_map.erase(current_key); - } - - VS::get_singleton()->material_set_shader(_get_material(), RID()); - } -} - -/////////////////////////////////////////////////////////////////// -#ifdef TOOLS_ENABLED -bool CanvasItem::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { - if (_edit_use_rect()) { - return _edit_get_rect().has_point(p_point); - } else { - return p_point.length() < p_tolerance; - } -} - -Transform2D CanvasItem::_edit_get_transform() const { - return Transform2D(_edit_get_rotation(), _edit_get_position() + _edit_get_pivot()); -} -#endif - -bool CanvasItem::is_visible_in_tree() const { - - if (!is_inside_tree()) - return false; - - const CanvasItem *p = this; - - while (p) { - if (!p->visible) - return false; - if (p->window && !p->window->is_visible()) { - return false; - } - p = p->get_parent_item(); - } - - return true; -} - -void CanvasItem::_propagate_visibility_changed(bool p_visible) { - - if (p_visible && first_draw) { //avoid propagating it twice - first_draw = false; - } - notification(NOTIFICATION_VISIBILITY_CHANGED); - - if (p_visible) - update(); //todo optimize - else - emit_signal(SceneStringNames::get_singleton()->hide); - _block(); - - for (int i = 0; i < get_child_count(); i++) { - - CanvasItem *c = Object::cast_to(get_child(i)); - - if (c && c->visible) //should the toplevels stop propagation? i think so but.. - c->_propagate_visibility_changed(p_visible); - } - - _unblock(); -} - -void CanvasItem::show() { - - if (visible) - return; - - visible = true; - VisualServer::get_singleton()->canvas_item_set_visible(canvas_item, true); - - if (!is_inside_tree()) - return; - - _propagate_visibility_changed(true); - _change_notify("visible"); -} - -void CanvasItem::hide() { - - if (!visible) - return; - - visible = false; - VisualServer::get_singleton()->canvas_item_set_visible(canvas_item, false); - - if (!is_inside_tree()) - return; - - _propagate_visibility_changed(false); - _change_notify("visible"); -} - -CanvasItem *CanvasItem::current_item_drawn = NULL; -CanvasItem *CanvasItem::get_current_item_drawn() { - return current_item_drawn; -} - -void CanvasItem::_update_callback() { - - if (!is_inside_tree()) { - pending_update = false; - return; - } - - VisualServer::get_singleton()->canvas_item_clear(get_canvas_item()); - //todo updating = true - only allow drawing here - if (is_visible_in_tree()) { //todo optimize this!! - if (first_draw) { - notification(NOTIFICATION_VISIBILITY_CHANGED); - first_draw = false; - } - drawing = true; - current_item_drawn = this; - notification(NOTIFICATION_DRAW); - emit_signal(SceneStringNames::get_singleton()->draw); - if (get_script_instance()) { - get_script_instance()->call_multilevel_reversed(SceneStringNames::get_singleton()->_draw, NULL, 0); - } - current_item_drawn = NULL; - drawing = false; - } - //todo updating = false - pending_update = false; // don't change to false until finished drawing (avoid recursive update) -} - -Transform2D CanvasItem::get_global_transform_with_canvas() const { - - if (canvas_layer) - return canvas_layer->get_transform() * get_global_transform(); - else if (is_inside_tree()) - return get_viewport()->get_canvas_transform() * get_global_transform(); - else - return get_global_transform(); -} - -Transform2D CanvasItem::get_screen_transform() const { - ERR_FAIL_COND_V(!is_inside_tree(), Transform2D()); - Transform2D xform = get_global_transform_with_canvas(); - - Window *w = Object::cast_to(get_viewport()); - if (w && !w->is_embedding_subwindows()) { - Transform2D s; - s.set_origin(w->get_position()); - - xform = s * xform; - } - - return xform; -} - -Transform2D CanvasItem::get_global_transform() const { -#ifdef DEBUG_ENABLED - ERR_FAIL_COND_V(!is_inside_tree(), get_transform()); -#endif - if (global_invalid) { - - const CanvasItem *pi = get_parent_item(); - if (pi) - global_transform = pi->get_global_transform() * get_transform(); - else - global_transform = get_transform(); - - global_invalid = false; - } - - return global_transform; -} - -void CanvasItem::_toplevel_raise_self() { - - if (!is_inside_tree()) - return; - - if (canvas_layer) - VisualServer::get_singleton()->canvas_item_set_draw_index(canvas_item, canvas_layer->get_sort_index()); - else - VisualServer::get_singleton()->canvas_item_set_draw_index(canvas_item, get_viewport()->gui_get_canvas_sort_index()); -} - -void CanvasItem::_enter_canvas() { - - if ((!Object::cast_to(get_parent())) || toplevel) { - - Node *n = this; - - canvas_layer = NULL; - - while (n) { - - canvas_layer = Object::cast_to(n); - if (canvas_layer) { - break; - } - if (Object::cast_to(n)) { - break; - } - n = n->get_parent(); - } - - RID canvas; - if (canvas_layer) - canvas = canvas_layer->get_canvas(); - else - canvas = get_viewport()->find_world_2d()->get_canvas(); - - VisualServer::get_singleton()->canvas_item_set_parent(canvas_item, canvas); - - group = "root_canvas" + itos(canvas.get_id()); - - add_to_group(group); - if (canvas_layer) - canvas_layer->reset_sort_index(); - else - get_viewport()->gui_reset_canvas_sort_index(); - - get_tree()->call_group_flags(SceneTree::GROUP_CALL_UNIQUE, group, "_toplevel_raise_self"); - - } else { - - CanvasItem *parent = get_parent_item(); - canvas_layer = parent->canvas_layer; - VisualServer::get_singleton()->canvas_item_set_parent(canvas_item, parent->get_canvas_item()); - VisualServer::get_singleton()->canvas_item_set_draw_index(canvas_item, get_index()); - } - - pending_update = false; - update(); - - notification(NOTIFICATION_ENTER_CANVAS); -} - -void CanvasItem::_exit_canvas() { - - notification(NOTIFICATION_EXIT_CANVAS, true); //reverse the notification - VisualServer::get_singleton()->canvas_item_set_parent(canvas_item, RID()); - canvas_layer = NULL; - group = ""; -} - -void CanvasItem::_notification(int p_what) { - - switch (p_what) { - case NOTIFICATION_ENTER_TREE: { - - _update_texture_filter_changed(false); - _update_texture_repeat_changed(false); - - first_draw = true; - Node *parent = get_parent(); - if (parent) { - CanvasItem *ci = Object::cast_to(parent); - if (ci) - C = ci->children_items.push_back(this); - if (!ci) { - //look for a window - Viewport *viewport = nullptr; - - while (parent) { - viewport = Object::cast_to(parent); - if (viewport) { - break; - } - parent = parent->get_parent(); - } - - ERR_FAIL_COND(!viewport); - - window = Object::cast_to(viewport); - if (window) { - window->connect(SceneStringNames::get_singleton()->visibility_changed, callable_mp(this, &CanvasItem::_window_visibility_changed)); - } - } - } - _enter_canvas(); - if (!block_transform_notify && !xform_change.in_list()) { - get_tree()->xform_change_list.add(&xform_change); - } - } break; - case NOTIFICATION_MOVED_IN_PARENT: { - - if (!is_inside_tree()) - break; - - if (group != "") { - get_tree()->call_group_flags(SceneTree::GROUP_CALL_UNIQUE, group, "_toplevel_raise_self"); - } else { - CanvasItem *p = get_parent_item(); - ERR_FAIL_COND(!p); - VisualServer::get_singleton()->canvas_item_set_draw_index(canvas_item, get_index()); - } - - } break; - case NOTIFICATION_EXIT_TREE: { - if (xform_change.in_list()) - get_tree()->xform_change_list.remove(&xform_change); - _exit_canvas(); - if (C) { - Object::cast_to(get_parent())->children_items.erase(C); - C = NULL; - } - if (window) { - window->disconnect(SceneStringNames::get_singleton()->visibility_changed, callable_mp(this, &CanvasItem::_window_visibility_changed)); - } - global_invalid = true; - } break; - case NOTIFICATION_DRAW: - case NOTIFICATION_TRANSFORM_CHANGED: { - - } break; - case NOTIFICATION_VISIBILITY_CHANGED: { - - emit_signal(SceneStringNames::get_singleton()->visibility_changed); - } break; - } -} - -void CanvasItem::set_visible(bool p_visible) { - - if (p_visible) - show(); - else - hide(); -} - -void CanvasItem::_window_visibility_changed() { - - if (visible) { - _propagate_visibility_changed(window->is_visible()); - } -} - -bool CanvasItem::is_visible() const { - - return visible; -} - -void CanvasItem::update() { - - if (!is_inside_tree()) - return; - if (pending_update) - return; - - pending_update = true; - - MessageQueue::get_singleton()->push_call(this, "_update_callback"); -} - -void CanvasItem::set_modulate(const Color &p_modulate) { - - if (modulate == p_modulate) - return; - - modulate = p_modulate; - VisualServer::get_singleton()->canvas_item_set_modulate(canvas_item, modulate); -} -Color CanvasItem::get_modulate() const { - - return modulate; -} - -void CanvasItem::set_as_toplevel(bool p_toplevel) { - - if (toplevel == p_toplevel) - return; - - if (!is_inside_tree()) { - toplevel = p_toplevel; - return; - } - - _exit_canvas(); - toplevel = p_toplevel; - _enter_canvas(); -} - -bool CanvasItem::is_set_as_toplevel() const { - - return toplevel; -} - -CanvasItem *CanvasItem::get_parent_item() const { - - if (toplevel) - return NULL; - - return Object::cast_to(get_parent()); -} - -void CanvasItem::set_self_modulate(const Color &p_self_modulate) { - - if (self_modulate == p_self_modulate) - return; - - self_modulate = p_self_modulate; - VisualServer::get_singleton()->canvas_item_set_self_modulate(canvas_item, self_modulate); -} -Color CanvasItem::get_self_modulate() const { - - return self_modulate; -} - -void CanvasItem::set_light_mask(int p_light_mask) { - - if (light_mask == p_light_mask) - return; - - light_mask = p_light_mask; - VS::get_singleton()->canvas_item_set_light_mask(canvas_item, p_light_mask); -} - -int CanvasItem::get_light_mask() const { - - return light_mask; -} - -void CanvasItem::item_rect_changed(bool p_size_changed) { - - if (p_size_changed) - update(); - emit_signal(SceneStringNames::get_singleton()->item_rect_changed); -} - -void CanvasItem::draw_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, float p_width) { - - ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); - - VisualServer::get_singleton()->canvas_item_add_line(canvas_item, p_from, p_to, p_color, p_width); -} - -void CanvasItem::draw_polyline(const Vector &p_points, const Color &p_color, float p_width) { - - ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); - - Vector colors; - colors.push_back(p_color); - VisualServer::get_singleton()->canvas_item_add_polyline(canvas_item, p_points, colors, p_width); -} - -void CanvasItem::draw_polyline_colors(const Vector &p_points, const Vector &p_colors, float p_width) { - - ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); - - VisualServer::get_singleton()->canvas_item_add_polyline(canvas_item, p_points, p_colors, p_width); -} - -void CanvasItem::draw_arc(const Vector2 &p_center, float p_radius, float p_start_angle, float p_end_angle, int p_point_count, const Color &p_color, float p_width) { - - Vector points; - points.resize(p_point_count); - const float delta_angle = p_end_angle - p_start_angle; - for (int i = 0; i < p_point_count; i++) { - float theta = (i / (p_point_count - 1.0f)) * delta_angle + p_start_angle; - points.set(i, p_center + Vector2(Math::cos(theta), Math::sin(theta)) * p_radius); - } - - draw_polyline(points, p_color, p_width); -} - -void CanvasItem::draw_multiline(const Vector &p_points, const Color &p_color, float p_width) { - - ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); - - Vector colors; - colors.push_back(p_color); - VisualServer::get_singleton()->canvas_item_add_multiline(canvas_item, p_points, colors, p_width); -} - -void CanvasItem::draw_multiline_colors(const Vector &p_points, const Vector &p_colors, float p_width) { - - ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); - - VisualServer::get_singleton()->canvas_item_add_multiline(canvas_item, p_points, p_colors, p_width); -} - -void CanvasItem::draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_filled, float p_width) { - - ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); - - if (p_filled) { - if (p_width != 1.0) { - WARN_PRINT("The draw_rect() \"width\" argument has no effect when \"filled\" is \"true\"."); - } - - VisualServer::get_singleton()->canvas_item_add_rect(canvas_item, p_rect, p_color); - } else { - // Thick lines are offset depending on their width to avoid partial overlapping. - // Thin lines don't require an offset, so don't apply one in this case - float offset; - if (p_width >= 2) { - offset = p_width / 2.0; - } else { - offset = 0.0; - } - - VisualServer::get_singleton()->canvas_item_add_line( - canvas_item, - p_rect.position + Size2(-offset, 0), - p_rect.position + Size2(p_rect.size.width + offset, 0), - p_color, - p_width); - VisualServer::get_singleton()->canvas_item_add_line( - canvas_item, - p_rect.position + Size2(p_rect.size.width, offset), - p_rect.position + Size2(p_rect.size.width, p_rect.size.height - offset), - p_color, - p_width); - VisualServer::get_singleton()->canvas_item_add_line( - canvas_item, - p_rect.position + Size2(p_rect.size.width + offset, p_rect.size.height), - p_rect.position + Size2(-offset, p_rect.size.height), - p_color, - p_width); - VisualServer::get_singleton()->canvas_item_add_line( - canvas_item, - p_rect.position + Size2(0, p_rect.size.height - offset), - p_rect.position + Size2(0, offset), - p_color, - p_width); - } -} - -void CanvasItem::draw_circle(const Point2 &p_pos, float p_radius, const Color &p_color) { - - ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); - - VisualServer::get_singleton()->canvas_item_add_circle(canvas_item, p_pos, p_radius, p_color); -} - -void CanvasItem::draw_texture(const Ref &p_texture, const Point2 &p_pos, const Color &p_modulate, const Ref &p_normal_map, const Ref &p_specular_map, const Color &p_specular_color_shininess, TextureFilter p_texture_filter, TextureRepeat p_texture_repeat) { - - ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); - - ERR_FAIL_COND(p_texture.is_null()); - - p_texture->draw(canvas_item, p_pos, p_modulate, false, p_normal_map, p_specular_map, p_specular_color_shininess, VS::CanvasItemTextureFilter(p_texture_filter), VS::CanvasItemTextureRepeat(p_texture_repeat)); -} - -void CanvasItem::draw_texture_rect(const Ref &p_texture, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose, const Ref &p_normal_map, const Ref &p_specular_map, const Color &p_specular_color_shininess, TextureFilter p_texture_filter, TextureRepeat p_texture_repeat) { - - ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); - - ERR_FAIL_COND(p_texture.is_null()); - p_texture->draw_rect(canvas_item, p_rect, p_tile, p_modulate, p_transpose, p_normal_map, p_specular_map, p_specular_color_shininess, VS::CanvasItemTextureFilter(p_texture_filter), VS::CanvasItemTextureRepeat(p_texture_repeat)); -} -void CanvasItem::draw_texture_rect_region(const Ref &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, const Ref &p_normal_map, const Ref &p_specular_map, const Color &p_specular_color_shininess, bool p_clip_uv, TextureFilter p_texture_filter, TextureRepeat p_texture_repeat) { - - ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); - ERR_FAIL_COND(p_texture.is_null()); - p_texture->draw_rect_region(canvas_item, p_rect, p_src_rect, p_modulate, p_transpose, p_normal_map, p_specular_map, p_specular_color_shininess, VS::CanvasItemTextureFilter(p_texture_filter), VS::CanvasItemTextureRepeat(p_texture_repeat), p_clip_uv); -} - -void CanvasItem::draw_style_box(const Ref &p_style_box, const Rect2 &p_rect) { - ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); - - ERR_FAIL_COND(p_style_box.is_null()); - - p_style_box->draw(canvas_item, p_rect); -} -void CanvasItem::draw_primitive(const Vector &p_points, const Vector &p_colors, const Vector &p_uvs, Ref p_texture, float p_width, const Ref &p_normal_map, const Ref &p_specular_map, const Color &p_specular_color_shininess, TextureFilter p_texture_filter, TextureRepeat p_texture_repeat) { - - ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); - - RID rid = p_texture.is_valid() ? p_texture->get_rid() : RID(); - RID rid_normal = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID(); - RID rid_specular = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID(); - - VisualServer::get_singleton()->canvas_item_add_primitive(canvas_item, p_points, p_colors, p_uvs, rid, p_width, rid_normal, rid_specular, p_specular_color_shininess, VS::CanvasItemTextureFilter(p_texture_filter), VS::CanvasItemTextureRepeat(p_texture_repeat)); -} -void CanvasItem::draw_set_transform(const Point2 &p_offset, float p_rot, const Size2 &p_scale) { - - ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); - - Transform2D xform(p_rot, p_offset); - xform.scale_basis(p_scale); - VisualServer::get_singleton()->canvas_item_add_set_transform(canvas_item, xform); -} - -void CanvasItem::draw_set_transform_matrix(const Transform2D &p_matrix) { - - ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); - - VisualServer::get_singleton()->canvas_item_add_set_transform(canvas_item, p_matrix); -} - -void CanvasItem::draw_polygon(const Vector &p_points, const Vector &p_colors, const Vector &p_uvs, Ref p_texture, const Ref &p_normal_map, const Ref &p_specular_map, const Color &p_specular_color_shininess, TextureFilter p_texture_filter, TextureRepeat p_texture_repeat) { - - ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); - - RID rid = p_texture.is_valid() ? p_texture->get_rid() : RID(); - RID rid_normal = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID(); - RID rid_specular = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID(); - - VisualServer::get_singleton()->canvas_item_add_polygon(canvas_item, p_points, p_colors, p_uvs, rid, rid_normal, rid_specular, p_specular_color_shininess, VS::CanvasItemTextureFilter(p_texture_filter), VS::CanvasItemTextureRepeat(p_texture_repeat)); -} - -void CanvasItem::draw_colored_polygon(const Vector &p_points, const Color &p_color, const Vector &p_uvs, Ref p_texture, const Ref &p_normal_map, const Ref &p_specular_map, const Color &p_specular_color_shininess, TextureFilter p_texture_filter, TextureRepeat p_texture_repeat) { - - ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); - - Vector colors; - colors.push_back(p_color); - RID rid = p_texture.is_valid() ? p_texture->get_rid() : RID(); - RID rid_normal = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID(); - RID rid_specular = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID(); - - VisualServer::get_singleton()->canvas_item_add_polygon(canvas_item, p_points, colors, p_uvs, rid, rid_normal, rid_specular, p_specular_color_shininess, VS::CanvasItemTextureFilter(p_texture_filter), VS::CanvasItemTextureRepeat(p_texture_repeat)); -} - -void CanvasItem::draw_mesh(const Ref &p_mesh, const Ref &p_texture, const Ref &p_normal_map, const Ref &p_specular_map, const Color &p_specular_color_shininess, const Transform2D &p_transform, const Color &p_modulate, TextureFilter p_texture_filter, TextureRepeat p_texture_repeat) { - - ERR_FAIL_COND(p_mesh.is_null()); - RID texture_rid = p_texture.is_valid() ? p_texture->get_rid() : RID(); - RID normal_map_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID(); - RID specular_map_rid = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID(); - - VisualServer::get_singleton()->canvas_item_add_mesh(canvas_item, p_mesh->get_rid(), p_transform, p_modulate, texture_rid, normal_map_rid, specular_map_rid, p_specular_color_shininess, VS::CanvasItemTextureFilter(p_texture_filter), VS::CanvasItemTextureRepeat(p_texture_repeat)); -} -void CanvasItem::draw_multimesh(const Ref &p_multimesh, const Ref &p_texture, const Ref &p_normal_map, const Ref &p_specular_map, const Color &p_specular_color_shininess, TextureFilter p_texture_filter, TextureRepeat p_texture_repeat) { - - ERR_FAIL_COND(p_multimesh.is_null()); - RID texture_rid = p_texture.is_valid() ? p_texture->get_rid() : RID(); - RID normal_map_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID(); - RID specular_map_rid = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID(); - - VisualServer::get_singleton()->canvas_item_add_multimesh(canvas_item, p_multimesh->get_rid(), texture_rid, normal_map_rid, specular_map_rid, p_specular_color_shininess, VS::CanvasItemTextureFilter(p_texture_filter), VS::CanvasItemTextureRepeat(p_texture_repeat)); -} - -void CanvasItem::draw_string(const Ref &p_font, const Point2 &p_pos, const String &p_text, const Color &p_modulate, int p_clip_w) { - - ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); - - ERR_FAIL_COND(p_font.is_null()); - p_font->draw(canvas_item, p_pos, p_text, p_modulate, p_clip_w); -} - -float CanvasItem::draw_char(const Ref &p_font, const Point2 &p_pos, const String &p_char, const String &p_next, const Color &p_modulate) { - - ERR_FAIL_COND_V_MSG(!drawing, 0, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); - - ERR_FAIL_COND_V(p_char.length() != 1, 0); - ERR_FAIL_COND_V(p_font.is_null(), 0); - - if (p_font->has_outline()) { - p_font->draw_char(canvas_item, p_pos, p_char[0], p_next.c_str()[0], Color(1, 1, 1), true); - } - return p_font->draw_char(canvas_item, p_pos, p_char[0], p_next.c_str()[0], p_modulate); -} - -void CanvasItem::_notify_transform(CanvasItem *p_node) { - - /* This check exists to avoid re-propagating the transform - * notification down the tree on dirty nodes. It provides - * optimization by avoiding redundancy (nodes are dirty, will get the - * notification anyway). - */ - - if (/*p_node->xform_change.in_list() &&*/ p_node->global_invalid) { - return; //nothing to do - } - - p_node->global_invalid = true; - - if (p_node->notify_transform && !p_node->xform_change.in_list()) { - if (!p_node->block_transform_notify) { - if (p_node->is_inside_tree()) - get_tree()->xform_change_list.add(&p_node->xform_change); - } - } - - for (List::Element *E = p_node->children_items.front(); E; E = E->next()) { - - CanvasItem *ci = E->get(); - if (ci->toplevel) - continue; - _notify_transform(ci); - } -} - -Rect2 CanvasItem::get_viewport_rect() const { - - ERR_FAIL_COND_V(!is_inside_tree(), Rect2()); - return get_viewport()->get_visible_rect(); -} - -RID CanvasItem::get_canvas() const { - - ERR_FAIL_COND_V(!is_inside_tree(), RID()); - - if (canvas_layer) - return canvas_layer->get_canvas(); - else - return get_viewport()->find_world_2d()->get_canvas(); -} - -ObjectID CanvasItem::get_canvas_layer_instance_id() const { - - if (canvas_layer) { - return canvas_layer->get_instance_id(); - } else { - return ObjectID(); - } -} - -CanvasItem *CanvasItem::get_toplevel() const { - - CanvasItem *ci = const_cast(this); - while (!ci->toplevel && Object::cast_to(ci->get_parent())) { - ci = Object::cast_to(ci->get_parent()); - } - - return ci; -} - -Ref CanvasItem::get_world_2d() const { - - ERR_FAIL_COND_V(!is_inside_tree(), Ref()); - - CanvasItem *tl = get_toplevel(); - - if (tl->get_viewport()) { - return tl->get_viewport()->find_world_2d(); - } else { - return Ref(); - } -} - -RID CanvasItem::get_viewport_rid() const { - - ERR_FAIL_COND_V(!is_inside_tree(), RID()); - return get_viewport()->get_viewport_rid(); -} - -void CanvasItem::set_block_transform_notify(bool p_enable) { - block_transform_notify = p_enable; -} - -bool CanvasItem::is_block_transform_notify_enabled() const { - - return block_transform_notify; -} - -void CanvasItem::set_draw_behind_parent(bool p_enable) { - - if (behind == p_enable) - return; - behind = p_enable; - VisualServer::get_singleton()->canvas_item_set_draw_behind_parent(canvas_item, behind); -} - -bool CanvasItem::is_draw_behind_parent_enabled() const { - - return behind; -} - -void CanvasItem::set_material(const Ref &p_material) { - - material = p_material; - RID rid; - if (material.is_valid()) - rid = material->get_rid(); - VS::get_singleton()->canvas_item_set_material(canvas_item, rid); - _change_notify(); //properties for material exposed -} - -void CanvasItem::set_use_parent_material(bool p_use_parent_material) { - - use_parent_material = p_use_parent_material; - VS::get_singleton()->canvas_item_set_use_parent_material(canvas_item, p_use_parent_material); -} - -bool CanvasItem::get_use_parent_material() const { - - return use_parent_material; -} - -Ref CanvasItem::get_material() const { - - return material; -} - -Vector2 CanvasItem::make_canvas_position_local(const Vector2 &screen_point) const { - - ERR_FAIL_COND_V(!is_inside_tree(), screen_point); - - Transform2D local_matrix = (get_canvas_transform() * get_global_transform()).affine_inverse(); - - return local_matrix.xform(screen_point); -} - -Ref CanvasItem::make_input_local(const Ref &p_event) const { - - ERR_FAIL_COND_V(p_event.is_null(), p_event); - ERR_FAIL_COND_V(!is_inside_tree(), p_event); - - return p_event->xformed_by((get_canvas_transform() * get_global_transform()).affine_inverse()); -} - -Vector2 CanvasItem::get_global_mouse_position() const { - - ERR_FAIL_COND_V(!get_viewport(), Vector2()); - return get_canvas_transform().affine_inverse().xform(get_viewport()->get_mouse_position()); -} - -Vector2 CanvasItem::get_local_mouse_position() const { - - ERR_FAIL_COND_V(!get_viewport(), Vector2()); - - return get_global_transform().affine_inverse().xform(get_global_mouse_position()); -} - -void CanvasItem::force_update_transform() { - ERR_FAIL_COND(!is_inside_tree()); - if (!xform_change.in_list()) { - return; - } - - get_tree()->xform_change_list.remove(&xform_change); - - notification(NOTIFICATION_TRANSFORM_CHANGED); -} - -void CanvasItem::_bind_methods() { - - ClassDB::bind_method(D_METHOD("_toplevel_raise_self"), &CanvasItem::_toplevel_raise_self); - ClassDB::bind_method(D_METHOD("_update_callback"), &CanvasItem::_update_callback); - -#ifdef TOOLS_ENABLED - ClassDB::bind_method(D_METHOD("_edit_set_state", "state"), &CanvasItem::_edit_set_state); - ClassDB::bind_method(D_METHOD("_edit_get_state"), &CanvasItem::_edit_get_state); - ClassDB::bind_method(D_METHOD("_edit_set_position", "position"), &CanvasItem::_edit_set_position); - ClassDB::bind_method(D_METHOD("_edit_get_position"), &CanvasItem::_edit_get_position); - ClassDB::bind_method(D_METHOD("_edit_set_scale", "scale"), &CanvasItem::_edit_set_scale); - ClassDB::bind_method(D_METHOD("_edit_get_scale"), &CanvasItem::_edit_get_scale); - ClassDB::bind_method(D_METHOD("_edit_set_rect", "rect"), &CanvasItem::_edit_set_rect); - ClassDB::bind_method(D_METHOD("_edit_get_rect"), &CanvasItem::_edit_get_rect); - ClassDB::bind_method(D_METHOD("_edit_use_rect"), &CanvasItem::_edit_use_rect); - ClassDB::bind_method(D_METHOD("_edit_set_rotation", "degrees"), &CanvasItem::_edit_set_rotation); - ClassDB::bind_method(D_METHOD("_edit_get_rotation"), &CanvasItem::_edit_get_rotation); - ClassDB::bind_method(D_METHOD("_edit_use_rotation"), &CanvasItem::_edit_use_rotation); - ClassDB::bind_method(D_METHOD("_edit_set_pivot", "pivot"), &CanvasItem::_edit_set_pivot); - ClassDB::bind_method(D_METHOD("_edit_get_pivot"), &CanvasItem::_edit_get_pivot); - ClassDB::bind_method(D_METHOD("_edit_use_pivot"), &CanvasItem::_edit_use_pivot); - ClassDB::bind_method(D_METHOD("_edit_get_transform"), &CanvasItem::_edit_get_transform); -#endif - - ClassDB::bind_method(D_METHOD("get_canvas_item"), &CanvasItem::get_canvas_item); - - ClassDB::bind_method(D_METHOD("set_visible", "visible"), &CanvasItem::set_visible); - ClassDB::bind_method(D_METHOD("is_visible"), &CanvasItem::is_visible); - ClassDB::bind_method(D_METHOD("is_visible_in_tree"), &CanvasItem::is_visible_in_tree); - ClassDB::bind_method(D_METHOD("show"), &CanvasItem::show); - ClassDB::bind_method(D_METHOD("hide"), &CanvasItem::hide); - - ClassDB::bind_method(D_METHOD("update"), &CanvasItem::update); - - ClassDB::bind_method(D_METHOD("set_as_toplevel", "enable"), &CanvasItem::set_as_toplevel); - ClassDB::bind_method(D_METHOD("is_set_as_toplevel"), &CanvasItem::is_set_as_toplevel); - - ClassDB::bind_method(D_METHOD("set_light_mask", "light_mask"), &CanvasItem::set_light_mask); - ClassDB::bind_method(D_METHOD("get_light_mask"), &CanvasItem::get_light_mask); - - ClassDB::bind_method(D_METHOD("set_modulate", "modulate"), &CanvasItem::set_modulate); - ClassDB::bind_method(D_METHOD("get_modulate"), &CanvasItem::get_modulate); - ClassDB::bind_method(D_METHOD("set_self_modulate", "self_modulate"), &CanvasItem::set_self_modulate); - ClassDB::bind_method(D_METHOD("get_self_modulate"), &CanvasItem::get_self_modulate); - - ClassDB::bind_method(D_METHOD("set_draw_behind_parent", "enable"), &CanvasItem::set_draw_behind_parent); - ClassDB::bind_method(D_METHOD("is_draw_behind_parent_enabled"), &CanvasItem::is_draw_behind_parent_enabled); - - ClassDB::bind_method(D_METHOD("_set_on_top", "on_top"), &CanvasItem::_set_on_top); - ClassDB::bind_method(D_METHOD("_is_on_top"), &CanvasItem::_is_on_top); - //ClassDB::bind_method(D_METHOD("get_transform"),&CanvasItem::get_transform); - - ClassDB::bind_method(D_METHOD("draw_line", "from", "to", "color", "width"), &CanvasItem::draw_line, DEFVAL(1.0)); - ClassDB::bind_method(D_METHOD("draw_polyline", "points", "color", "width"), &CanvasItem::draw_polyline, DEFVAL(1.0)); - ClassDB::bind_method(D_METHOD("draw_polyline_colors", "points", "colors", "width"), &CanvasItem::draw_polyline_colors, DEFVAL(1.0)); - ClassDB::bind_method(D_METHOD("draw_arc", "center", "radius", "start_angle", "end_angle", "point_count", "color", "width"), &CanvasItem::draw_arc, DEFVAL(1.0)); - ClassDB::bind_method(D_METHOD("draw_multiline", "points", "color", "width"), &CanvasItem::draw_multiline, DEFVAL(1.0)); - ClassDB::bind_method(D_METHOD("draw_multiline_colors", "points", "colors", "width"), &CanvasItem::draw_multiline_colors, DEFVAL(1.0)); - ClassDB::bind_method(D_METHOD("draw_rect", "rect", "color", "filled", "width"), &CanvasItem::draw_rect, DEFVAL(true), DEFVAL(1.0)); - ClassDB::bind_method(D_METHOD("draw_circle", "position", "radius", "color"), &CanvasItem::draw_circle); - ClassDB::bind_method(D_METHOD("draw_texture", "texture", "position", "modulate", "normal_map", "specular_map", "specular_shininess", "texture_filter", "texture_repeat"), &CanvasItem::draw_texture, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(Ref()), DEFVAL(Ref()), DEFVAL(Color(1, 1, 1, 1)), DEFVAL(TEXTURE_FILTER_PARENT_NODE), DEFVAL(TEXTURE_REPEAT_PARENT_NODE)); - ClassDB::bind_method(D_METHOD("draw_texture_rect", "texture", "rect", "tile", "modulate", "transpose", "normal_map", "specular_map", "specular_shininess", "texture_filter", "texture_repeat"), &CanvasItem::draw_texture_rect, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(false), DEFVAL(Ref()), DEFVAL(Ref()), DEFVAL(Color(1, 1, 1, 1)), DEFVAL(TEXTURE_FILTER_PARENT_NODE), DEFVAL(TEXTURE_REPEAT_PARENT_NODE)); - ClassDB::bind_method(D_METHOD("draw_texture_rect_region", "texture", "rect", "src_rect", "modulate", "transpose", "normal_map", "specular_map", "specular_shininess", "clip_uv", "texture_filter", "texture_repeat"), &CanvasItem::draw_texture_rect_region, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(false), DEFVAL(Ref()), DEFVAL(Ref()), DEFVAL(Color(1, 1, 1, 1)), DEFVAL(true), DEFVAL(TEXTURE_FILTER_PARENT_NODE), DEFVAL(TEXTURE_REPEAT_PARENT_NODE)); - ClassDB::bind_method(D_METHOD("draw_style_box", "style_box", "rect"), &CanvasItem::draw_style_box); - ClassDB::bind_method(D_METHOD("draw_primitive", "points", "colors", "uvs", "texture", "width", "normal_map", "specular_map", "specular_shininess", "texture_filter", "texture_repeat"), &CanvasItem::draw_primitive, DEFVAL(Ref()), DEFVAL(1.0), DEFVAL(Ref()), DEFVAL(Ref()), DEFVAL(Color(1, 1, 1, 1)), DEFVAL(TEXTURE_FILTER_PARENT_NODE), DEFVAL(TEXTURE_REPEAT_PARENT_NODE)); - ClassDB::bind_method(D_METHOD("draw_polygon", "points", "colors", "uvs", "texture", "normal_map", "specular_map", "specular_shininess", "texture_filter", "texture_repeat"), &CanvasItem::draw_polygon, DEFVAL(PackedVector2Array()), DEFVAL(Ref()), DEFVAL(Ref()), DEFVAL(Ref()), DEFVAL(Color(1, 1, 1, 1)), DEFVAL(TEXTURE_FILTER_PARENT_NODE), DEFVAL(TEXTURE_REPEAT_PARENT_NODE)); - ClassDB::bind_method(D_METHOD("draw_colored_polygon", "points", "color", "uvs", "texture", "normal_map", "specular_map", "specular_shininess", "texture_filter", "texture_repeat"), &CanvasItem::draw_colored_polygon, DEFVAL(PackedVector2Array()), DEFVAL(Ref()), DEFVAL(Ref()), DEFVAL(Ref()), DEFVAL(Color(1, 1, 1, 1)), DEFVAL(TEXTURE_FILTER_PARENT_NODE), DEFVAL(TEXTURE_REPEAT_PARENT_NODE)); - ClassDB::bind_method(D_METHOD("draw_string", "font", "position", "text", "modulate", "clip_w"), &CanvasItem::draw_string, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(-1)); - ClassDB::bind_method(D_METHOD("draw_char", "font", "position", "char", "next", "modulate"), &CanvasItem::draw_char, DEFVAL(Color(1, 1, 1, 1))); - ClassDB::bind_method(D_METHOD("draw_mesh", "mesh", "texture", "normal_map", "specular_map", "specular_shininess", "transform", "modulate", "texture_filter", "texture_repeat"), &CanvasItem::draw_mesh, DEFVAL(Ref()), DEFVAL(Ref()), DEFVAL(Color(1, 1, 1, 1)), DEFVAL(Transform2D()), DEFVAL(Color(1, 1, 1, 1)), DEFVAL(TEXTURE_FILTER_PARENT_NODE), DEFVAL(TEXTURE_REPEAT_PARENT_NODE)); - ClassDB::bind_method(D_METHOD("draw_multimesh", "multimesh", "texture", "normal_map", "specular_map", "specular_shininess", "texture_filter", "texture_repeat"), &CanvasItem::draw_multimesh, DEFVAL(Ref()), DEFVAL(Ref()), DEFVAL(Color(1, 1, 1, 1)), DEFVAL(TEXTURE_FILTER_PARENT_NODE), DEFVAL(TEXTURE_REPEAT_PARENT_NODE)); - - ClassDB::bind_method(D_METHOD("draw_set_transform", "position", "rotation", "scale"), &CanvasItem::draw_set_transform); - ClassDB::bind_method(D_METHOD("draw_set_transform_matrix", "xform"), &CanvasItem::draw_set_transform_matrix); - ClassDB::bind_method(D_METHOD("get_transform"), &CanvasItem::get_transform); - ClassDB::bind_method(D_METHOD("get_global_transform"), &CanvasItem::get_global_transform); - ClassDB::bind_method(D_METHOD("get_global_transform_with_canvas"), &CanvasItem::get_global_transform_with_canvas); - ClassDB::bind_method(D_METHOD("get_viewport_transform"), &CanvasItem::get_viewport_transform); - ClassDB::bind_method(D_METHOD("get_viewport_rect"), &CanvasItem::get_viewport_rect); - ClassDB::bind_method(D_METHOD("get_canvas_transform"), &CanvasItem::get_canvas_transform); - ClassDB::bind_method(D_METHOD("get_local_mouse_position"), &CanvasItem::get_local_mouse_position); - ClassDB::bind_method(D_METHOD("get_global_mouse_position"), &CanvasItem::get_global_mouse_position); - ClassDB::bind_method(D_METHOD("get_canvas"), &CanvasItem::get_canvas); - ClassDB::bind_method(D_METHOD("get_world_2d"), &CanvasItem::get_world_2d); - //ClassDB::bind_method(D_METHOD("get_viewport"),&CanvasItem::get_viewport); - - ClassDB::bind_method(D_METHOD("set_material", "material"), &CanvasItem::set_material); - ClassDB::bind_method(D_METHOD("get_material"), &CanvasItem::get_material); - - ClassDB::bind_method(D_METHOD("set_use_parent_material", "enable"), &CanvasItem::set_use_parent_material); - ClassDB::bind_method(D_METHOD("get_use_parent_material"), &CanvasItem::get_use_parent_material); - - ClassDB::bind_method(D_METHOD("set_notify_local_transform", "enable"), &CanvasItem::set_notify_local_transform); - ClassDB::bind_method(D_METHOD("is_local_transform_notification_enabled"), &CanvasItem::is_local_transform_notification_enabled); - - ClassDB::bind_method(D_METHOD("set_notify_transform", "enable"), &CanvasItem::set_notify_transform); - ClassDB::bind_method(D_METHOD("is_transform_notification_enabled"), &CanvasItem::is_transform_notification_enabled); - - ClassDB::bind_method(D_METHOD("force_update_transform"), &CanvasItem::force_update_transform); - - ClassDB::bind_method(D_METHOD("make_canvas_position_local", "screen_point"), &CanvasItem::make_canvas_position_local); - ClassDB::bind_method(D_METHOD("make_input_local", "event"), &CanvasItem::make_input_local); - - ClassDB::bind_method(D_METHOD("set_texture_filter", "mode"), &CanvasItem::set_texture_filter); - ClassDB::bind_method(D_METHOD("get_texture_filter"), &CanvasItem::get_texture_filter); - - ClassDB::bind_method(D_METHOD("set_texture_repeat", "mode"), &CanvasItem::set_texture_repeat); - ClassDB::bind_method(D_METHOD("get_texture_repeat"), &CanvasItem::get_texture_repeat); - - BIND_VMETHOD(MethodInfo("_draw")); - - ADD_GROUP("Visibility", ""); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible"); - ADD_PROPERTY(PropertyInfo(Variant::COLOR, "modulate"), "set_modulate", "get_modulate"); - ADD_PROPERTY(PropertyInfo(Variant::COLOR, "self_modulate"), "set_self_modulate", "get_self_modulate"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_behind_parent"), "set_draw_behind_parent", "is_draw_behind_parent_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_on_top", PROPERTY_HINT_NONE, "", 0), "_set_on_top", "_is_on_top"); //compatibility - ADD_PROPERTY(PropertyInfo(Variant::INT, "light_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_light_mask", "get_light_mask"); - - ADD_GROUP("Texture", "texture_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "ParentNode,Nearest,Linear,MipmapNearest,MipmapLinear,MipmapNearestAniso,MipmapLinearAniso"), "set_texture_filter", "get_texture_filter"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_repeat", PROPERTY_HINT_ENUM, "ParentNode,Disabled,Enabled,Mirror"), "set_texture_repeat", "get_texture_repeat"); - - ADD_GROUP("Material", ""); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,CanvasItemMaterial"), "set_material", "get_material"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_parent_material"), "set_use_parent_material", "get_use_parent_material"); - //exporting these things doesn't really make much sense i think - // ADD_PROPERTY(PropertyInfo(Variant::BOOL, "toplevel", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_as_toplevel", "is_set_as_toplevel"); - // ADD_PROPERTY(PropertyInfo(Variant::BOOL,"transform/notify"),"set_transform_notify","is_transform_notify_enabled"); - - ADD_SIGNAL(MethodInfo("draw")); - ADD_SIGNAL(MethodInfo("visibility_changed")); - ADD_SIGNAL(MethodInfo("hide")); - ADD_SIGNAL(MethodInfo("item_rect_changed")); - - BIND_CONSTANT(NOTIFICATION_TRANSFORM_CHANGED); - BIND_CONSTANT(NOTIFICATION_DRAW); - BIND_CONSTANT(NOTIFICATION_VISIBILITY_CHANGED); - BIND_CONSTANT(NOTIFICATION_ENTER_CANVAS); - BIND_CONSTANT(NOTIFICATION_EXIT_CANVAS); - - BIND_ENUM_CONSTANT(TEXTURE_FILTER_PARENT_NODE); - BIND_ENUM_CONSTANT(TEXTURE_FILTER_NEAREST); - BIND_ENUM_CONSTANT(TEXTURE_FILTER_LINEAR); - BIND_ENUM_CONSTANT(TEXTURE_FILTER_NEAREST_WITH_MIPMAPS); - BIND_ENUM_CONSTANT(TEXTURE_FILTER_LINEAR_WITH_MIPMAPS); - BIND_ENUM_CONSTANT(TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC); - BIND_ENUM_CONSTANT(TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC); - BIND_ENUM_CONSTANT(TEXTURE_FILTER_MAX); - - BIND_ENUM_CONSTANT(TEXTURE_REPEAT_PARENT_NODE); - BIND_ENUM_CONSTANT(TEXTURE_REPEAT_DISABLED); - BIND_ENUM_CONSTANT(TEXTURE_REPEAT_ENABLED); - BIND_ENUM_CONSTANT(TEXTURE_REPEAT_MIRROR); - BIND_ENUM_CONSTANT(TEXTURE_REPEAT_MAX); -} - -Transform2D CanvasItem::get_canvas_transform() const { - - ERR_FAIL_COND_V(!is_inside_tree(), Transform2D()); - - if (canvas_layer) - return canvas_layer->get_transform(); - else if (Object::cast_to(get_parent())) - return Object::cast_to(get_parent())->get_canvas_transform(); - else - return get_viewport()->get_canvas_transform(); -} - -Transform2D CanvasItem::get_viewport_transform() const { - - ERR_FAIL_COND_V(!is_inside_tree(), Transform2D()); - - if (canvas_layer) { - - if (get_viewport()) { - return get_viewport()->get_final_transform() * canvas_layer->get_transform(); - } else { - return canvas_layer->get_transform(); - } - - } else { - return get_viewport()->get_final_transform() * get_viewport()->get_canvas_transform(); - } -} - -void CanvasItem::set_notify_local_transform(bool p_enable) { - notify_local_transform = p_enable; -} - -bool CanvasItem::is_local_transform_notification_enabled() const { - return notify_local_transform; -} - -void CanvasItem::set_notify_transform(bool p_enable) { - if (notify_transform == p_enable) - return; - - notify_transform = p_enable; - - if (notify_transform && is_inside_tree()) { - //this ensures that invalid globals get resolved, so notifications can be received - get_global_transform(); - } -} - -bool CanvasItem::is_transform_notification_enabled() const { - return notify_transform; -} - -int CanvasItem::get_canvas_layer() const { - - if (canvas_layer) - return canvas_layer->get_layer(); - else - return 0; -} - -void CanvasItem::_update_texture_filter_changed(bool p_propagate) { - - if (!is_inside_tree()) { - return; - } - - if (texture_filter == TEXTURE_FILTER_PARENT_NODE) { - CanvasItem *parent_item = get_parent_item(); - if (parent_item) { - texture_filter_cache = parent_item->texture_filter_cache; - } else { - //from viewport - switch (get_viewport()->get_default_canvas_item_texture_filter()) { - case Viewport::DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_NEAREST: texture_filter_cache = VS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST; break; - case Viewport::DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_LINEAR: texture_filter_cache = VS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR; break; - case Viewport::DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS: texture_filter_cache = VS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS; break; - case Viewport::DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS: texture_filter_cache = VS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS; break; - default: { - } - } - } - } else { - texture_filter_cache = VS::CanvasItemTextureFilter(texture_filter); - } - VS::get_singleton()->canvas_item_set_default_texture_filter(get_canvas_item(), texture_filter_cache); - update(); - - if (p_propagate) { - for (List::Element *E = children_items.front(); E; E = E->next()) { - if (!E->get()->toplevel && E->get()->texture_filter == TEXTURE_FILTER_PARENT_NODE) { - E->get()->_update_texture_filter_changed(true); - } - } - } -} - -void CanvasItem::set_texture_filter(TextureFilter p_texture_filter) { - ERR_FAIL_INDEX(p_texture_filter, TEXTURE_FILTER_MAX); - if (texture_filter == p_texture_filter) { - return; - } - texture_filter = p_texture_filter; - _update_texture_filter_changed(true); -} - -CanvasItem::TextureFilter CanvasItem::get_texture_filter() const { - return texture_filter; -} - -void CanvasItem::_update_texture_repeat_changed(bool p_propagate) { - - if (!is_inside_tree()) { - return; - } - - if (texture_repeat == TEXTURE_REPEAT_PARENT_NODE) { - CanvasItem *parent_item = get_parent_item(); - if (parent_item) { - texture_repeat_cache = parent_item->texture_repeat_cache; - } else { - //from viewport - switch (get_viewport()->get_default_canvas_item_texture_repeat()) { - case Viewport::DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_DISABLED: texture_repeat_cache = VS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED; break; - case Viewport::DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_ENABLED: texture_repeat_cache = VS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED; break; - case Viewport::DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_MIRROR: texture_repeat_cache = VS::CANVAS_ITEM_TEXTURE_REPEAT_MIRROR; break; - default: { - } - } - } - } else { - texture_repeat_cache = VS::CanvasItemTextureRepeat(texture_repeat); - } - VS::get_singleton()->canvas_item_set_default_texture_repeat(get_canvas_item(), texture_repeat_cache); - update(); - if (p_propagate) { - for (List::Element *E = children_items.front(); E; E = E->next()) { - if (!E->get()->toplevel && E->get()->texture_repeat == TEXTURE_REPEAT_PARENT_NODE) { - E->get()->_update_texture_repeat_changed(true); - } - } - } -} - -void CanvasItem::set_texture_repeat(TextureRepeat p_texture_repeat) { - ERR_FAIL_INDEX(p_texture_repeat, TEXTURE_REPEAT_MAX); - if (texture_repeat == p_texture_repeat) { - return; - } - texture_repeat = p_texture_repeat; - _update_texture_repeat_changed(true); -} - -CanvasItem::TextureRepeat CanvasItem::get_texture_repeat() const { - return texture_repeat; -} - -CanvasItem::CanvasItem() : - xform_change(this) { - - window = nullptr; - canvas_item = VisualServer::get_singleton()->canvas_item_create(); - visible = true; - pending_update = false; - modulate = Color(1, 1, 1, 1); - self_modulate = Color(1, 1, 1, 1); - toplevel = false; - first_draw = false; - drawing = false; - behind = false; - block_transform_notify = false; - //viewport=NULL; - canvas_layer = NULL; - use_parent_material = false; - global_invalid = true; - notify_local_transform = false; - notify_transform = false; - light_mask = 1; - texture_repeat = TEXTURE_REPEAT_PARENT_NODE; - texture_filter = TEXTURE_FILTER_PARENT_NODE; - texture_filter_cache = VS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR; - texture_repeat_cache = VS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED; - - C = NULL; -} - -CanvasItem::~CanvasItem() { - - VisualServer::get_singleton()->free(canvas_item); -} diff --git a/scene/2d/canvas_item.h b/scene/2d/canvas_item.h deleted file mode 100644 index 3f176e5f60..0000000000 --- a/scene/2d/canvas_item.h +++ /dev/null @@ -1,426 +0,0 @@ -/*************************************************************************/ -/* canvas_item.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef CANVAS_ITEM_H -#define CANVAS_ITEM_H - -#include "scene/main/node.h" -#include "scene/main/scene_tree.h" -#include "scene/resources/material.h" -#include "scene/resources/multimesh.h" -#include "scene/resources/shader.h" -#include "scene/resources/texture.h" - -class CanvasLayer; -class Viewport; -class Font; - -class StyleBox; - -class CanvasItemMaterial : public Material { - - GDCLASS(CanvasItemMaterial, Material); - -public: - enum BlendMode { - BLEND_MODE_MIX, - BLEND_MODE_ADD, - BLEND_MODE_SUB, - BLEND_MODE_MUL, - BLEND_MODE_PREMULT_ALPHA, - BLEND_MODE_DISABLED - }; - - enum LightMode { - LIGHT_MODE_NORMAL, - LIGHT_MODE_UNSHADED, - LIGHT_MODE_LIGHT_ONLY - }; - -private: - union MaterialKey { - - struct { - uint32_t blend_mode : 4; - uint32_t light_mode : 4; - uint32_t particles_animation : 1; - uint32_t invalid_key : 1; - }; - - uint32_t key; - - bool operator<(const MaterialKey &p_key) const { - return key < p_key.key; - } - }; - - struct ShaderNames { - StringName particles_anim_h_frames; - StringName particles_anim_v_frames; - StringName particles_anim_loop; - }; - - static ShaderNames *shader_names; - - struct ShaderData { - RID shader; - int users; - }; - - static Map shader_map; - - MaterialKey current_key; - - _FORCE_INLINE_ MaterialKey _compute_key() const { - - MaterialKey mk; - mk.key = 0; - mk.blend_mode = blend_mode; - mk.light_mode = light_mode; - mk.particles_animation = particles_animation; - return mk; - } - - static Mutex material_mutex; - static SelfList::List *dirty_materials; - SelfList element; - - void _update_shader(); - _FORCE_INLINE_ void _queue_shader_change(); - _FORCE_INLINE_ bool _is_shader_dirty() const; - - BlendMode blend_mode; - LightMode light_mode; - bool particles_animation; - - int particles_anim_h_frames; - int particles_anim_v_frames; - bool particles_anim_loop; - -protected: - static void _bind_methods(); - void _validate_property(PropertyInfo &property) const; - -public: - void set_blend_mode(BlendMode p_blend_mode); - BlendMode get_blend_mode() const; - - void set_light_mode(LightMode p_light_mode); - LightMode get_light_mode() const; - - void set_particles_animation(bool p_particles_anim); - bool get_particles_animation() const; - - void set_particles_anim_h_frames(int p_frames); - int get_particles_anim_h_frames() const; - void set_particles_anim_v_frames(int p_frames); - int get_particles_anim_v_frames() const; - - void set_particles_anim_loop(bool p_loop); - bool get_particles_anim_loop() const; - - static void init_shaders(); - static void finish_shaders(); - static void flush_changes(); - - RID get_shader_rid() const; - - virtual Shader::Mode get_shader_mode() const; - - CanvasItemMaterial(); - virtual ~CanvasItemMaterial(); -}; - -VARIANT_ENUM_CAST(CanvasItemMaterial::BlendMode) -VARIANT_ENUM_CAST(CanvasItemMaterial::LightMode) - -class CanvasItem : public Node { - - GDCLASS(CanvasItem, Node); - -public: - enum TextureFilter { - TEXTURE_FILTER_PARENT_NODE, - TEXTURE_FILTER_NEAREST, - TEXTURE_FILTER_LINEAR, - TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, - TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, - TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, - TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, - TEXTURE_FILTER_MAX - }; - - enum TextureRepeat { - TEXTURE_REPEAT_PARENT_NODE, - TEXTURE_REPEAT_DISABLED, - TEXTURE_REPEAT_ENABLED, - TEXTURE_REPEAT_MIRROR, - TEXTURE_REPEAT_MAX, - }; - -private: - mutable SelfList xform_change; - - RID canvas_item; - String group; - - CanvasLayer *canvas_layer; - - Color modulate; - Color self_modulate; - - List children_items; - List::Element *C; - - int light_mask; - - Window *window; - bool first_draw; - bool visible; - bool pending_update; - bool toplevel; - bool drawing; - bool block_transform_notify; - bool behind; - bool use_parent_material; - bool notify_local_transform; - bool notify_transform; - - VS::CanvasItemTextureFilter texture_filter_cache; - VS::CanvasItemTextureRepeat texture_repeat_cache; - - TextureFilter texture_filter; - TextureRepeat texture_repeat; - - Ref material; - - mutable Transform2D global_transform; - mutable bool global_invalid; - - void _toplevel_raise_self(); - - void _propagate_visibility_changed(bool p_visible); - - void _update_callback(); - - void _enter_canvas(); - void _exit_canvas(); - - void _window_visibility_changed(); - - void _notify_transform(CanvasItem *p_node); - - void _set_on_top(bool p_on_top) { set_draw_behind_parent(!p_on_top); } - bool _is_on_top() const { return !is_draw_behind_parent_enabled(); } - - static CanvasItem *current_item_drawn; - friend class Viewport; - void _update_texture_repeat_changed(bool p_propagate); - void _update_texture_filter_changed(bool p_propagate); - -protected: - _FORCE_INLINE_ void _notify_transform() { - if (!is_inside_tree()) return; - _notify_transform(this); - if (!block_transform_notify && notify_local_transform) notification(NOTIFICATION_LOCAL_TRANSFORM_CHANGED); - } - - void item_rect_changed(bool p_size_changed = true); - - void _notification(int p_what); - static void _bind_methods(); - -public: - enum { - NOTIFICATION_TRANSFORM_CHANGED = SceneTree::NOTIFICATION_TRANSFORM_CHANGED, //unique - NOTIFICATION_DRAW = 30, - NOTIFICATION_VISIBILITY_CHANGED = 31, - NOTIFICATION_ENTER_CANVAS = 32, - NOTIFICATION_EXIT_CANVAS = 33, - NOTIFICATION_LOCAL_TRANSFORM_CHANGED = 35, - NOTIFICATION_WORLD_2D_CHANGED = 36, - - }; - - /* EDITOR */ -#ifdef TOOLS_ENABLED - // Select the node - virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; - - // Save and restore a CanvasItem state - virtual void _edit_set_state(const Dictionary &p_state){}; - virtual Dictionary _edit_get_state() const { return Dictionary(); }; - - // Used to move the node - virtual void _edit_set_position(const Point2 &p_position) = 0; - virtual Point2 _edit_get_position() const = 0; - - // Used to scale the node - virtual void _edit_set_scale(const Size2 &p_scale) = 0; - virtual Size2 _edit_get_scale() const = 0; - - // Used to rotate the node - virtual bool _edit_use_rotation() const { return false; }; - virtual void _edit_set_rotation(float p_rotation){}; - virtual float _edit_get_rotation() const { return 0.0; }; - - // Used to resize/move the node - virtual bool _edit_use_rect() const { return false; }; // MAYBE REPLACE BY A _edit_get_editmode() - virtual void _edit_set_rect(const Rect2 &p_rect){}; - virtual Rect2 _edit_get_rect() const { return Rect2(0, 0, 0, 0); }; - virtual Size2 _edit_get_minimum_size() const { return Size2(-1, -1); }; // LOOKS WEIRD - - // Used to set a pivot - virtual bool _edit_use_pivot() const { return false; }; - virtual void _edit_set_pivot(const Point2 &p_pivot){}; - virtual Point2 _edit_get_pivot() const { return Point2(); }; - - virtual Transform2D _edit_get_transform() const; -#endif - - /* VISIBILITY */ - - void set_visible(bool p_visible); - bool is_visible() const; - bool is_visible_in_tree() const; - void show(); - void hide(); - - void update(); - - virtual void set_light_mask(int p_light_mask); - int get_light_mask() const; - - void set_modulate(const Color &p_modulate); - Color get_modulate() const; - - void set_self_modulate(const Color &p_self_modulate); - Color get_self_modulate() const; - - /* DRAWING API */ - - void draw_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, float p_width = 1.0); - void draw_polyline(const Vector &p_points, const Color &p_color, float p_width = 1.0); - void draw_polyline_colors(const Vector &p_points, const Vector &p_colors, float p_width = 1.0); - void draw_arc(const Vector2 &p_center, float p_radius, float p_start_angle, float p_end_angle, int p_point_count, const Color &p_color, float p_width = 1.0); - void draw_multiline(const Vector &p_points, const Color &p_color, float p_width = 1.0); - void draw_multiline_colors(const Vector &p_points, const Vector &p_colors, float p_width = 1.0); - void draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_filled = true, float p_width = 1.0); - void draw_circle(const Point2 &p_pos, float p_radius, const Color &p_color); - void draw_texture(const Ref &p_texture, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1, 1), const Ref &p_normal_map = Ref(), const Ref &p_specular_map = Ref(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), TextureFilter p_texture_filter = TEXTURE_FILTER_PARENT_NODE, TextureRepeat p_texture_repeat = TEXTURE_REPEAT_PARENT_NODE); - void draw_texture_rect(const Ref &p_texture, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref &p_normal_map = Ref(), const Ref &p_specular_map = Ref(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), TextureFilter p_texture_filter = TEXTURE_FILTER_PARENT_NODE, TextureRepeat p_texture_repeat = TEXTURE_REPEAT_PARENT_NODE); - void draw_texture_rect_region(const Ref &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref &p_normal_map = Ref(), const Ref &p_specular_map = Ref(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), bool p_clip_uv = false, TextureFilter p_texture_filter = TEXTURE_FILTER_PARENT_NODE, TextureRepeat p_texture_repeat = TEXTURE_REPEAT_PARENT_NODE); - void draw_style_box(const Ref &p_style_box, const Rect2 &p_rect); - void draw_primitive(const Vector &p_points, const Vector &p_colors, const Vector &p_uvs, Ref p_texture = Ref(), float p_width = 1, const Ref &p_normal_map = Ref(), const Ref &p_specular_map = Ref(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), TextureFilter p_texture_filter = TEXTURE_FILTER_PARENT_NODE, TextureRepeat p_texture_repeat = TEXTURE_REPEAT_PARENT_NODE); - void draw_polygon(const Vector &p_points, const Vector &p_colors, const Vector &p_uvs = Vector(), Ref p_texture = Ref(), const Ref &p_normal_map = Ref(), const Ref &p_specular_map = Ref(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), TextureFilter p_texture_filter = TEXTURE_FILTER_PARENT_NODE, TextureRepeat p_texture_repeat = TEXTURE_REPEAT_PARENT_NODE); - void draw_colored_polygon(const Vector &p_points, const Color &p_color, const Vector &p_uvs = Vector(), Ref p_texture = Ref(), const Ref &p_normal_map = Ref(), const Ref &p_specular_map = Ref(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), TextureFilter p_texture_filter = TEXTURE_FILTER_PARENT_NODE, TextureRepeat p_texture_repeat = TEXTURE_REPEAT_PARENT_NODE); - - void draw_mesh(const Ref &p_mesh, const Ref &p_texture, const Ref &p_normal_map = Ref(), const Ref &p_specular_map = Ref(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), const Transform2D &p_transform = Transform2D(), const Color &p_modulate = Color(1, 1, 1), TextureFilter p_texture_filter = TEXTURE_FILTER_PARENT_NODE, TextureRepeat p_texture_repeat = TEXTURE_REPEAT_PARENT_NODE); - void draw_multimesh(const Ref &p_multimesh, const Ref &p_texture, const Ref &p_normal_map = Ref(), const Ref &p_specular_map = Ref(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), TextureFilter p_texture_filter = TEXTURE_FILTER_PARENT_NODE, TextureRepeat p_texture_repeat = TEXTURE_REPEAT_PARENT_NODE); - - void draw_string(const Ref &p_font, const Point2 &p_pos, const String &p_text, const Color &p_modulate = Color(1, 1, 1), int p_clip_w = -1); - float draw_char(const Ref &p_font, const Point2 &p_pos, const String &p_char, const String &p_next = "", const Color &p_modulate = Color(1, 1, 1)); - - void draw_set_transform(const Point2 &p_offset, float p_rot, const Size2 &p_scale); - void draw_set_transform_matrix(const Transform2D &p_matrix); - - static CanvasItem *get_current_item_drawn(); - - /* RECT / TRANSFORM */ - - void set_as_toplevel(bool p_toplevel); - bool is_set_as_toplevel() const; - - void set_draw_behind_parent(bool p_enable); - bool is_draw_behind_parent_enabled() const; - - CanvasItem *get_parent_item() const; - - virtual Transform2D get_transform() const = 0; - - virtual Transform2D get_global_transform() const; - virtual Transform2D get_global_transform_with_canvas() const; - virtual Transform2D get_screen_transform() const; - - CanvasItem *get_toplevel() const; - _FORCE_INLINE_ RID get_canvas_item() const { - return canvas_item; - } - - void set_block_transform_notify(bool p_enable); - bool is_block_transform_notify_enabled() const; - - Transform2D get_canvas_transform() const; - Transform2D get_viewport_transform() const; - Rect2 get_viewport_rect() const; - RID get_viewport_rid() const; - RID get_canvas() const; - ObjectID get_canvas_layer_instance_id() const; - Ref get_world_2d() const; - - virtual void set_material(const Ref &p_material); - Ref get_material() const; - - virtual void set_use_parent_material(bool p_use_parent_material); - bool get_use_parent_material() const; - - Ref make_input_local(const Ref &p_event) const; - Vector2 make_canvas_position_local(const Vector2 &screen_point) const; - - Vector2 get_global_mouse_position() const; - Vector2 get_local_mouse_position() const; - - void set_notify_local_transform(bool p_enable); - bool is_local_transform_notification_enabled() const; - - void set_notify_transform(bool p_enable); - bool is_transform_notification_enabled() const; - - void force_update_transform(); - - void set_texture_filter(TextureFilter p_texture_filter); - TextureFilter get_texture_filter() const; - - void set_texture_repeat(TextureRepeat p_texture_repeat); - TextureRepeat get_texture_repeat() const; - - // Used by control nodes to retrieve the parent's anchorable area - virtual Rect2 get_anchorable_rect() const { return Rect2(0, 0, 0, 0); }; - - int get_canvas_layer() const; - - CanvasItem(); - ~CanvasItem(); -}; - -VARIANT_ENUM_CAST(CanvasItem::TextureFilter) -VARIANT_ENUM_CAST(CanvasItem::TextureRepeat) - -#endif // CANVAS_ITEM_H diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp index 3b8a81d2ca..8678e5a1f4 100644 --- a/scene/2d/cpu_particles_2d.cpp +++ b/scene/2d/cpu_particles_2d.cpp @@ -31,8 +31,8 @@ #include "cpu_particles_2d.h" #include "core/core_string_names.h" -#include "scene/2d/canvas_item.h" -#include "scene/2d/particles_2d.h" +#include "scene/2d/gpu_particles_2d.h" +#include "scene/main/canvas_item.h" #include "scene/resources/particles_material.h" #include "servers/visual_server.h" @@ -1144,7 +1144,7 @@ void CPUParticles2D::_notification(int p_what) { void CPUParticles2D::convert_from_particles(Node *p_particles) { - Particles2D *particles = Object::cast_to(p_particles); + GPUParticles2D *particles = Object::cast_to(p_particles); ERR_FAIL_COND_MSG(!particles, "Only Particles2D nodes can be converted to CPUParticles2D."); set_emitting(particles->is_emitting()); diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp new file mode 100644 index 0000000000..d3d1326018 --- /dev/null +++ b/scene/2d/gpu_particles_2d.cpp @@ -0,0 +1,432 @@ +/*************************************************************************/ +/* gpu_particles_2d.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "gpu_particles_2d.h" + +#include "core/os/os.h" +#include "scene/resources/particles_material.h" +#include "scene/scene_string_names.h" + +#ifdef TOOLS_ENABLED +#include "core/engine.h" +#endif + +void GPUParticles2D::set_emitting(bool p_emitting) { + + VS::get_singleton()->particles_set_emitting(particles, p_emitting); + + if (p_emitting && one_shot) { + set_process_internal(true); + } else if (!p_emitting) { + set_process_internal(false); + } +} + +void GPUParticles2D::set_amount(int p_amount) { + + ERR_FAIL_COND_MSG(p_amount < 1, "Amount of particles cannot be smaller than 1."); + amount = p_amount; + VS::get_singleton()->particles_set_amount(particles, amount); +} +void GPUParticles2D::set_lifetime(float p_lifetime) { + + ERR_FAIL_COND_MSG(p_lifetime <= 0, "Particles lifetime must be greater than 0."); + lifetime = p_lifetime; + VS::get_singleton()->particles_set_lifetime(particles, lifetime); +} + +void GPUParticles2D::set_one_shot(bool p_enable) { + + one_shot = p_enable; + VS::get_singleton()->particles_set_one_shot(particles, one_shot); + + if (is_emitting()) { + + set_process_internal(true); + if (!one_shot) + VisualServer::get_singleton()->particles_restart(particles); + } + + if (!one_shot) + set_process_internal(false); +} +void GPUParticles2D::set_pre_process_time(float p_time) { + + pre_process_time = p_time; + VS::get_singleton()->particles_set_pre_process_time(particles, pre_process_time); +} +void GPUParticles2D::set_explosiveness_ratio(float p_ratio) { + + explosiveness_ratio = p_ratio; + VS::get_singleton()->particles_set_explosiveness_ratio(particles, explosiveness_ratio); +} +void GPUParticles2D::set_randomness_ratio(float p_ratio) { + + randomness_ratio = p_ratio; + VS::get_singleton()->particles_set_randomness_ratio(particles, randomness_ratio); +} +void GPUParticles2D::set_visibility_rect(const Rect2 &p_visibility_rect) { + + visibility_rect = p_visibility_rect; + AABB aabb; + aabb.position.x = p_visibility_rect.position.x; + aabb.position.y = p_visibility_rect.position.y; + aabb.size.x = p_visibility_rect.size.x; + aabb.size.y = p_visibility_rect.size.y; + + VS::get_singleton()->particles_set_custom_aabb(particles, aabb); + + _change_notify("visibility_rect"); + update(); +} +void GPUParticles2D::set_use_local_coordinates(bool p_enable) { + + local_coords = p_enable; + VS::get_singleton()->particles_set_use_local_coordinates(particles, local_coords); + set_notify_transform(!p_enable); + if (!p_enable && is_inside_tree()) { + _update_particle_emission_transform(); + } +} + +void GPUParticles2D::_update_particle_emission_transform() { + + Transform2D xf2d = get_global_transform(); + Transform xf; + xf.basis.set_axis(0, Vector3(xf2d.get_axis(0).x, xf2d.get_axis(0).y, 0)); + xf.basis.set_axis(1, Vector3(xf2d.get_axis(1).x, xf2d.get_axis(1).y, 0)); + xf.set_origin(Vector3(xf2d.get_origin().x, xf2d.get_origin().y, 0)); + + VS::get_singleton()->particles_set_emission_transform(particles, xf); +} + +void GPUParticles2D::set_process_material(const Ref &p_material) { + + process_material = p_material; + Ref pm = p_material; + if (pm.is_valid() && !pm->get_flag(ParticlesMaterial::FLAG_DISABLE_Z) && pm->get_gravity() == Vector3(0, -9.8, 0)) { + // Likely a new (3D) material, modify it to match 2D space + pm->set_flag(ParticlesMaterial::FLAG_DISABLE_Z, true); + pm->set_gravity(Vector3(0, 98, 0)); + } + RID material_rid; + if (process_material.is_valid()) + material_rid = process_material->get_rid(); + VS::get_singleton()->particles_set_process_material(particles, material_rid); + + update_configuration_warning(); +} + +void GPUParticles2D::set_speed_scale(float p_scale) { + + speed_scale = p_scale; + VS::get_singleton()->particles_set_speed_scale(particles, p_scale); +} + +bool GPUParticles2D::is_emitting() const { + + return VS::get_singleton()->particles_get_emitting(particles); +} +int GPUParticles2D::get_amount() const { + + return amount; +} +float GPUParticles2D::get_lifetime() const { + + return lifetime; +} + +bool GPUParticles2D::get_one_shot() const { + + return one_shot; +} +float GPUParticles2D::get_pre_process_time() const { + + return pre_process_time; +} +float GPUParticles2D::get_explosiveness_ratio() const { + + return explosiveness_ratio; +} +float GPUParticles2D::get_randomness_ratio() const { + + return randomness_ratio; +} +Rect2 GPUParticles2D::get_visibility_rect() const { + + return visibility_rect; +} +bool GPUParticles2D::get_use_local_coordinates() const { + + return local_coords; +} +Ref GPUParticles2D::get_process_material() const { + + return process_material; +} + +float GPUParticles2D::get_speed_scale() const { + + return speed_scale; +} + +void GPUParticles2D::set_draw_order(DrawOrder p_order) { + + draw_order = p_order; + VS::get_singleton()->particles_set_draw_order(particles, VS::ParticlesDrawOrder(p_order)); +} + +GPUParticles2D::DrawOrder GPUParticles2D::get_draw_order() const { + + return draw_order; +} + +void GPUParticles2D::set_fixed_fps(int p_count) { + fixed_fps = p_count; + VS::get_singleton()->particles_set_fixed_fps(particles, p_count); +} + +int GPUParticles2D::get_fixed_fps() const { + return fixed_fps; +} + +void GPUParticles2D::set_fractional_delta(bool p_enable) { + fractional_delta = p_enable; + VS::get_singleton()->particles_set_fractional_delta(particles, p_enable); +} + +bool GPUParticles2D::get_fractional_delta() const { + return fractional_delta; +} + +String GPUParticles2D::get_configuration_warning() const { + + if (VisualServer::get_singleton()->is_low_end()) { + return TTR("GPU-based particles are not supported by the GLES2 video driver.\nUse the CPUParticles2D node instead. You can use the \"Convert to CPUParticles\" option for this purpose."); + } + + String warnings; + + if (process_material.is_null()) { + if (warnings != String()) + warnings += "\n"; + warnings += "- " + TTR("A material to process the particles is not assigned, so no behavior is imprinted."); + } else { + + CanvasItemMaterial *mat = Object::cast_to(get_material().ptr()); + + if (get_material().is_null() || (mat && !mat->get_particles_animation())) { + const ParticlesMaterial *process = Object::cast_to(process_material.ptr()); + if (process && + (process->get_param(ParticlesMaterial::PARAM_ANIM_SPEED) != 0.0 || process->get_param(ParticlesMaterial::PARAM_ANIM_OFFSET) != 0.0 || + process->get_param_texture(ParticlesMaterial::PARAM_ANIM_SPEED).is_valid() || process->get_param_texture(ParticlesMaterial::PARAM_ANIM_OFFSET).is_valid())) { + if (warnings != String()) + warnings += "\n"; + warnings += "- " + TTR("Particles2D animation requires the usage of a CanvasItemMaterial with \"Particles Animation\" enabled."); + } + } + } + + return warnings; +} + +Rect2 GPUParticles2D::capture_rect() const { + + AABB aabb = VS::get_singleton()->particles_get_current_aabb(particles); + Rect2 r; + r.position.x = aabb.position.x; + r.position.y = aabb.position.y; + r.size.x = aabb.size.x; + r.size.y = aabb.size.y; + return r; +} + +void GPUParticles2D::set_texture(const Ref &p_texture) { + texture = p_texture; + update(); +} + +Ref GPUParticles2D::get_texture() const { + return texture; +} + +void GPUParticles2D::set_normal_map(const Ref &p_normal_map) { + + normal_map = p_normal_map; + update(); +} + +Ref GPUParticles2D::get_normal_map() const { + return normal_map; +} + +void GPUParticles2D::_validate_property(PropertyInfo &property) const { +} + +void GPUParticles2D::restart() { + VS::get_singleton()->particles_restart(particles); + VS::get_singleton()->particles_set_emitting(particles, true); +} + +void GPUParticles2D::_notification(int p_what) { + + if (p_what == NOTIFICATION_DRAW) { + + RID texture_rid; + if (texture.is_valid()) + texture_rid = texture->get_rid(); + RID normal_rid; + if (normal_map.is_valid()) + normal_rid = normal_map->get_rid(); + + VS::get_singleton()->canvas_item_add_particles(get_canvas_item(), particles, texture_rid, normal_rid); + +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint() && (this == get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->is_a_parent_of(this))) { + + draw_rect(visibility_rect, Color(0, 0.7, 0.9, 0.4), false); + } +#endif + } + + if (p_what == NOTIFICATION_PAUSED || p_what == NOTIFICATION_UNPAUSED) { + if (can_process()) { + VS::get_singleton()->particles_set_speed_scale(particles, speed_scale); + } else { + + VS::get_singleton()->particles_set_speed_scale(particles, 0); + } + } + + if (p_what == NOTIFICATION_TRANSFORM_CHANGED) { + _update_particle_emission_transform(); + } + + if (p_what == NOTIFICATION_INTERNAL_PROCESS) { + + if (one_shot && !is_emitting()) { + _change_notify(); + set_process_internal(false); + } + } +} + +void GPUParticles2D::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_emitting", "emitting"), &GPUParticles2D::set_emitting); + ClassDB::bind_method(D_METHOD("set_amount", "amount"), &GPUParticles2D::set_amount); + ClassDB::bind_method(D_METHOD("set_lifetime", "secs"), &GPUParticles2D::set_lifetime); + ClassDB::bind_method(D_METHOD("set_one_shot", "secs"), &GPUParticles2D::set_one_shot); + ClassDB::bind_method(D_METHOD("set_pre_process_time", "secs"), &GPUParticles2D::set_pre_process_time); + ClassDB::bind_method(D_METHOD("set_explosiveness_ratio", "ratio"), &GPUParticles2D::set_explosiveness_ratio); + ClassDB::bind_method(D_METHOD("set_randomness_ratio", "ratio"), &GPUParticles2D::set_randomness_ratio); + ClassDB::bind_method(D_METHOD("set_visibility_rect", "visibility_rect"), &GPUParticles2D::set_visibility_rect); + ClassDB::bind_method(D_METHOD("set_use_local_coordinates", "enable"), &GPUParticles2D::set_use_local_coordinates); + ClassDB::bind_method(D_METHOD("set_fixed_fps", "fps"), &GPUParticles2D::set_fixed_fps); + ClassDB::bind_method(D_METHOD("set_fractional_delta", "enable"), &GPUParticles2D::set_fractional_delta); + ClassDB::bind_method(D_METHOD("set_process_material", "material"), &GPUParticles2D::set_process_material); + ClassDB::bind_method(D_METHOD("set_speed_scale", "scale"), &GPUParticles2D::set_speed_scale); + + ClassDB::bind_method(D_METHOD("is_emitting"), &GPUParticles2D::is_emitting); + ClassDB::bind_method(D_METHOD("get_amount"), &GPUParticles2D::get_amount); + ClassDB::bind_method(D_METHOD("get_lifetime"), &GPUParticles2D::get_lifetime); + ClassDB::bind_method(D_METHOD("get_one_shot"), &GPUParticles2D::get_one_shot); + ClassDB::bind_method(D_METHOD("get_pre_process_time"), &GPUParticles2D::get_pre_process_time); + ClassDB::bind_method(D_METHOD("get_explosiveness_ratio"), &GPUParticles2D::get_explosiveness_ratio); + ClassDB::bind_method(D_METHOD("get_randomness_ratio"), &GPUParticles2D::get_randomness_ratio); + ClassDB::bind_method(D_METHOD("get_visibility_rect"), &GPUParticles2D::get_visibility_rect); + ClassDB::bind_method(D_METHOD("get_use_local_coordinates"), &GPUParticles2D::get_use_local_coordinates); + ClassDB::bind_method(D_METHOD("get_fixed_fps"), &GPUParticles2D::get_fixed_fps); + ClassDB::bind_method(D_METHOD("get_fractional_delta"), &GPUParticles2D::get_fractional_delta); + ClassDB::bind_method(D_METHOD("get_process_material"), &GPUParticles2D::get_process_material); + ClassDB::bind_method(D_METHOD("get_speed_scale"), &GPUParticles2D::get_speed_scale); + + ClassDB::bind_method(D_METHOD("set_draw_order", "order"), &GPUParticles2D::set_draw_order); + ClassDB::bind_method(D_METHOD("get_draw_order"), &GPUParticles2D::get_draw_order); + + ClassDB::bind_method(D_METHOD("set_texture", "texture"), &GPUParticles2D::set_texture); + ClassDB::bind_method(D_METHOD("get_texture"), &GPUParticles2D::get_texture); + + ClassDB::bind_method(D_METHOD("set_normal_map", "texture"), &GPUParticles2D::set_normal_map); + ClassDB::bind_method(D_METHOD("get_normal_map"), &GPUParticles2D::get_normal_map); + + ClassDB::bind_method(D_METHOD("capture_rect"), &GPUParticles2D::capture_rect); + + ClassDB::bind_method(D_METHOD("restart"), &GPUParticles2D::restart); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_EXP_RANGE, "1,1000000,1"), "set_amount", "get_amount"); + ADD_GROUP("Time", ""); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01,or_greater"), "set_lifetime", "get_lifetime"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "get_one_shot"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "preprocess", PROPERTY_HINT_RANGE, "0.00,600.0,0.01"), "set_pre_process_time", "get_pre_process_time"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "speed_scale", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_speed_scale", "get_speed_scale"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "explosiveness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_explosiveness_ratio", "get_explosiveness_ratio"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_randomness_ratio", "get_randomness_ratio"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_fps", PROPERTY_HINT_RANGE, "0,1000,1"), "set_fixed_fps", "get_fixed_fps"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fract_delta"), "set_fractional_delta", "get_fractional_delta"); + ADD_GROUP("Drawing", ""); + ADD_PROPERTY(PropertyInfo(Variant::RECT2, "visibility_rect"), "set_visibility_rect", "get_visibility_rect"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "local_coords"), "set_use_local_coordinates", "get_use_local_coordinates"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "draw_order", PROPERTY_HINT_ENUM, "Index,Lifetime"), "set_draw_order", "get_draw_order"); + ADD_GROUP("Process Material", "process_"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "process_material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,ParticlesMaterial"), "set_process_material", "get_process_material"); + ADD_GROUP("Textures", ""); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "normal_map", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_normal_map", "get_normal_map"); + + BIND_ENUM_CONSTANT(DRAW_ORDER_INDEX); + BIND_ENUM_CONSTANT(DRAW_ORDER_LIFETIME); +} + +GPUParticles2D::GPUParticles2D() { + + particles = VS::get_singleton()->particles_create(); + + one_shot = false; // Needed so that set_emitting doesn't access uninitialized values + set_emitting(true); + set_one_shot(false); + set_amount(8); + set_lifetime(1); + set_fixed_fps(0); + set_fractional_delta(true); + set_pre_process_time(0); + set_explosiveness_ratio(0); + set_randomness_ratio(0); + set_visibility_rect(Rect2(Vector2(-100, -100), Vector2(200, 200))); + set_use_local_coordinates(true); + set_draw_order(DRAW_ORDER_INDEX); + set_speed_scale(1); +} + +GPUParticles2D::~GPUParticles2D() { + + VS::get_singleton()->free(particles); +} diff --git a/scene/2d/gpu_particles_2d.h b/scene/2d/gpu_particles_2d.h new file mode 100644 index 0000000000..47951d76dc --- /dev/null +++ b/scene/2d/gpu_particles_2d.h @@ -0,0 +1,127 @@ +/*************************************************************************/ +/* gpu_particles_2d.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef PARTICLES_2D_H +#define PARTICLES_2D_H + +#include "core/rid.h" +#include "scene/2d/node_2d.h" +#include "scene/resources/texture.h" + +class GPUParticles2D : public Node2D { +private: + GDCLASS(GPUParticles2D, Node2D); + +public: + enum DrawOrder { + DRAW_ORDER_INDEX, + DRAW_ORDER_LIFETIME, + }; + +private: + RID particles; + + bool one_shot; + int amount; + float lifetime; + float pre_process_time; + float explosiveness_ratio; + float randomness_ratio; + float speed_scale; + Rect2 visibility_rect; + bool local_coords; + int fixed_fps; + bool fractional_delta; + + Ref process_material; + + DrawOrder draw_order; + + Ref texture; + Ref normal_map; + + void _update_particle_emission_transform(); + +protected: + static void _bind_methods(); + virtual void _validate_property(PropertyInfo &property) const; + void _notification(int p_what); + +public: + void set_emitting(bool p_emitting); + void set_amount(int p_amount); + void set_lifetime(float p_lifetime); + void set_one_shot(bool p_enable); + void set_pre_process_time(float p_time); + void set_explosiveness_ratio(float p_ratio); + void set_randomness_ratio(float p_ratio); + void set_visibility_rect(const Rect2 &p_visibility_rect); + void set_use_local_coordinates(bool p_enable); + void set_process_material(const Ref &p_material); + void set_speed_scale(float p_scale); + + bool is_emitting() const; + int get_amount() const; + float get_lifetime() const; + bool get_one_shot() const; + float get_pre_process_time() const; + float get_explosiveness_ratio() const; + float get_randomness_ratio() const; + Rect2 get_visibility_rect() const; + bool get_use_local_coordinates() const; + Ref get_process_material() const; + float get_speed_scale() const; + + void set_fixed_fps(int p_count); + int get_fixed_fps() const; + + void set_fractional_delta(bool p_enable); + bool get_fractional_delta() const; + + void set_draw_order(DrawOrder p_order); + DrawOrder get_draw_order() const; + + void set_texture(const Ref &p_texture); + Ref get_texture() const; + + void set_normal_map(const Ref &p_normal_map); + Ref get_normal_map() const; + + virtual String get_configuration_warning() const; + + void restart(); + Rect2 capture_rect() const; + GPUParticles2D(); + ~GPUParticles2D(); +}; + +VARIANT_ENUM_CAST(GPUParticles2D::DrawOrder) + +#endif // PARTICLES_2D_H diff --git a/scene/2d/navigation_2d.h b/scene/2d/navigation_2d.h index 5520f5006e..1da13fc78a 100644 --- a/scene/2d/navigation_2d.h +++ b/scene/2d/navigation_2d.h @@ -31,7 +31,7 @@ #ifndef NAVIGATION_2D_H #define NAVIGATION_2D_H -#include "scene/2d/navigation_polygon.h" +#include "scene/2d/navigation_region_2d.h" #include "scene/2d/node_2d.h" class Navigation2D : public Node2D { diff --git a/scene/2d/navigation_polygon.cpp b/scene/2d/navigation_polygon.cpp deleted file mode 100644 index 9159ef21c5..0000000000 --- a/scene/2d/navigation_polygon.cpp +++ /dev/null @@ -1,582 +0,0 @@ -/*************************************************************************/ -/* navigation_polygon.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "navigation_polygon.h" - -#include "core/core_string_names.h" -#include "core/engine.h" -#include "core/os/mutex.h" -#include "navigation_2d.h" -#include "servers/navigation_2d_server.h" - -#include "thirdparty/misc/triangulator.h" - -#ifdef TOOLS_ENABLED -Rect2 NavigationPolygon::_edit_get_rect() const { - - if (rect_cache_dirty) { - item_rect = Rect2(); - bool first = true; - - for (int i = 0; i < outlines.size(); i++) { - const Vector &outline = outlines[i]; - const int outline_size = outline.size(); - if (outline_size < 3) - continue; - const Vector2 *p = outline.ptr(); - for (int j = 0; j < outline_size; j++) { - if (first) { - item_rect = Rect2(p[j], Vector2(0, 0)); - first = false; - } else { - item_rect.expand_to(p[j]); - } - } - } - - rect_cache_dirty = false; - } - return item_rect; -} - -bool NavigationPolygon::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { - - for (int i = 0; i < outlines.size(); i++) { - const Vector &outline = outlines[i]; - const int outline_size = outline.size(); - if (outline_size < 3) - continue; - if (Geometry::is_point_in_polygon(p_point, Variant(outline))) - return true; - } - return false; -} -#endif - -void NavigationPolygon::set_vertices(const Vector &p_vertices) { - - { - MutexLock lock(navmesh_generation); - navmesh.unref(); - } - vertices = p_vertices; - rect_cache_dirty = true; -} - -Vector NavigationPolygon::get_vertices() const { - - return vertices; -} - -void NavigationPolygon::_set_polygons(const Array &p_array) { - - { - MutexLock lock(navmesh_generation); - navmesh.unref(); - } - polygons.resize(p_array.size()); - for (int i = 0; i < p_array.size(); i++) { - polygons.write[i].indices = p_array[i]; - } -} - -Array NavigationPolygon::_get_polygons() const { - - Array ret; - ret.resize(polygons.size()); - for (int i = 0; i < ret.size(); i++) { - ret[i] = polygons[i].indices; - } - - return ret; -} - -void NavigationPolygon::_set_outlines(const Array &p_array) { - - outlines.resize(p_array.size()); - for (int i = 0; i < p_array.size(); i++) { - outlines.write[i] = p_array[i]; - } - rect_cache_dirty = true; -} - -Array NavigationPolygon::_get_outlines() const { - - Array ret; - ret.resize(outlines.size()); - for (int i = 0; i < ret.size(); i++) { - ret[i] = outlines[i]; - } - - return ret; -} - -void NavigationPolygon::add_polygon(const Vector &p_polygon) { - - Polygon polygon; - polygon.indices = p_polygon; - polygons.push_back(polygon); - { - MutexLock lock(navmesh_generation); - navmesh.unref(); - } -} - -void NavigationPolygon::add_outline_at_index(const Vector &p_outline, int p_index) { - - outlines.insert(p_index, p_outline); - rect_cache_dirty = true; -} - -int NavigationPolygon::get_polygon_count() const { - - return polygons.size(); -} -Vector NavigationPolygon::get_polygon(int p_idx) { - - ERR_FAIL_INDEX_V(p_idx, polygons.size(), Vector()); - return polygons[p_idx].indices; -} -void NavigationPolygon::clear_polygons() { - - polygons.clear(); - { - MutexLock lock(navmesh_generation); - navmesh.unref(); - } -} - -Ref NavigationPolygon::get_mesh() { - MutexLock lock(navmesh_generation); - - if (navmesh.is_null()) { - navmesh.instance(); - Vector verts; - { - verts.resize(get_vertices().size()); - Vector3 *w = verts.ptrw(); - - const Vector2 *r = get_vertices().ptr(); - - for (int i(0); i < get_vertices().size(); i++) { - w[i] = Vector3(r[i].x, 0.0, r[i].y); - } - } - navmesh->set_vertices(verts); - - for (int i(0); i < get_polygon_count(); i++) { - navmesh->add_polygon(get_polygon(i)); - } - } - - return navmesh; -} - -void NavigationPolygon::add_outline(const Vector &p_outline) { - - outlines.push_back(p_outline); - rect_cache_dirty = true; -} - -int NavigationPolygon::get_outline_count() const { - - return outlines.size(); -} - -void NavigationPolygon::set_outline(int p_idx, const Vector &p_outline) { - ERR_FAIL_INDEX(p_idx, outlines.size()); - outlines.write[p_idx] = p_outline; - rect_cache_dirty = true; -} - -void NavigationPolygon::remove_outline(int p_idx) { - - ERR_FAIL_INDEX(p_idx, outlines.size()); - outlines.remove(p_idx); - rect_cache_dirty = true; -} - -Vector NavigationPolygon::get_outline(int p_idx) const { - ERR_FAIL_INDEX_V(p_idx, outlines.size(), Vector()); - return outlines[p_idx]; -} - -void NavigationPolygon::clear_outlines() { - - outlines.clear(); - rect_cache_dirty = true; -} -void NavigationPolygon::make_polygons_from_outlines() { - - { - MutexLock lock(navmesh_generation); - navmesh.unref(); - } - List in_poly, out_poly; - - Vector2 outside_point(-1e10, -1e10); - - for (int i = 0; i < outlines.size(); i++) { - - Vector ol = outlines[i]; - int olsize = ol.size(); - if (olsize < 3) - continue; - const Vector2 *r = ol.ptr(); - for (int j = 0; j < olsize; j++) { - outside_point.x = MAX(r[j].x, outside_point.x); - outside_point.y = MAX(r[j].y, outside_point.y); - } - } - - outside_point += Vector2(0.7239784, 0.819238); //avoid precision issues - - for (int i = 0; i < outlines.size(); i++) { - - Vector ol = outlines[i]; - int olsize = ol.size(); - if (olsize < 3) - continue; - const Vector2 *r = ol.ptr(); - - int interscount = 0; - //test if this is an outer outline - for (int k = 0; k < outlines.size(); k++) { - - if (i == k) - continue; //no self intersect - - Vector ol2 = outlines[k]; - int olsize2 = ol2.size(); - if (olsize2 < 3) - continue; - const Vector2 *r2 = ol2.ptr(); - - for (int l = 0; l < olsize2; l++) { - - if (Geometry::segment_intersects_segment_2d(r[0], outside_point, r2[l], r2[(l + 1) % olsize2], NULL)) { - interscount++; - } - } - } - - bool outer = (interscount % 2) == 0; - - TriangulatorPoly tp; - tp.Init(olsize); - for (int j = 0; j < olsize; j++) { - tp[j] = r[j]; - } - - if (outer) - tp.SetOrientation(TRIANGULATOR_CCW); - else { - tp.SetOrientation(TRIANGULATOR_CW); - tp.SetHole(true); - } - - in_poly.push_back(tp); - } - - TriangulatorPartition tpart; - if (tpart.ConvexPartition_HM(&in_poly, &out_poly) == 0) { //failed! - ERR_PRINT("NavigationPolygon: Convex partition failed!"); - return; - } - - polygons.clear(); - vertices.resize(0); - - Map points; - for (List::Element *I = out_poly.front(); I; I = I->next()) { - - TriangulatorPoly &tp = I->get(); - - struct Polygon p; - - for (int64_t i = 0; i < tp.GetNumPoints(); i++) { - - Map::Element *E = points.find(tp[i]); - if (!E) { - E = points.insert(tp[i], vertices.size()); - vertices.push_back(tp[i]); - } - p.indices.push_back(E->get()); - } - - polygons.push_back(p); - } - - emit_signal(CoreStringNames::get_singleton()->changed); -} - -void NavigationPolygon::_bind_methods() { - - ClassDB::bind_method(D_METHOD("set_vertices", "vertices"), &NavigationPolygon::set_vertices); - ClassDB::bind_method(D_METHOD("get_vertices"), &NavigationPolygon::get_vertices); - - ClassDB::bind_method(D_METHOD("add_polygon", "polygon"), &NavigationPolygon::add_polygon); - ClassDB::bind_method(D_METHOD("get_polygon_count"), &NavigationPolygon::get_polygon_count); - ClassDB::bind_method(D_METHOD("get_polygon", "idx"), &NavigationPolygon::get_polygon); - ClassDB::bind_method(D_METHOD("clear_polygons"), &NavigationPolygon::clear_polygons); - - ClassDB::bind_method(D_METHOD("add_outline", "outline"), &NavigationPolygon::add_outline); - ClassDB::bind_method(D_METHOD("add_outline_at_index", "outline", "index"), &NavigationPolygon::add_outline_at_index); - ClassDB::bind_method(D_METHOD("get_outline_count"), &NavigationPolygon::get_outline_count); - ClassDB::bind_method(D_METHOD("set_outline", "idx", "outline"), &NavigationPolygon::set_outline); - ClassDB::bind_method(D_METHOD("get_outline", "idx"), &NavigationPolygon::get_outline); - ClassDB::bind_method(D_METHOD("remove_outline", "idx"), &NavigationPolygon::remove_outline); - ClassDB::bind_method(D_METHOD("clear_outlines"), &NavigationPolygon::clear_outlines); - ClassDB::bind_method(D_METHOD("make_polygons_from_outlines"), &NavigationPolygon::make_polygons_from_outlines); - - ClassDB::bind_method(D_METHOD("_set_polygons", "polygons"), &NavigationPolygon::_set_polygons); - ClassDB::bind_method(D_METHOD("_get_polygons"), &NavigationPolygon::_get_polygons); - - ClassDB::bind_method(D_METHOD("_set_outlines", "outlines"), &NavigationPolygon::_set_outlines); - ClassDB::bind_method(D_METHOD("_get_outlines"), &NavigationPolygon::_get_outlines); - - ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_vertices", "get_vertices"); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_polygons", "_get_polygons"); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "outlines", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_outlines", "_get_outlines"); -} - -NavigationPolygon::NavigationPolygon() : - rect_cache_dirty(true) { -} - -NavigationPolygon::~NavigationPolygon() { -} - -void NavigationRegion2D::set_enabled(bool p_enabled) { - - if (enabled == p_enabled) - return; - enabled = p_enabled; - - if (!is_inside_tree()) - return; - - if (!enabled) { - - Navigation2DServer::get_singleton()->region_set_map(region, RID()); - } else { - - if (navigation) { - - Navigation2DServer::get_singleton()->region_set_map(region, navigation->get_rid()); - } - } - - if (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_navigation_hint()) - update(); -} - -bool NavigationRegion2D::is_enabled() const { - - return enabled; -} - -///////////////////////////// -#ifdef TOOLS_ENABLED -Rect2 NavigationRegion2D::_edit_get_rect() const { - - return navpoly.is_valid() ? navpoly->_edit_get_rect() : Rect2(); -} - -bool NavigationRegion2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { - - return navpoly.is_valid() ? navpoly->_edit_is_selected_on_click(p_point, p_tolerance) : false; -} -#endif - -void NavigationRegion2D::_notification(int p_what) { - - switch (p_what) { - case NOTIFICATION_ENTER_TREE: { - - Node2D *c = this; - while (c) { - - navigation = Object::cast_to(c); - if (navigation) { - - if (enabled) { - - Navigation2DServer::get_singleton()->region_set_map(region, navigation->get_rid()); - } - break; - } - - c = Object::cast_to(c->get_parent()); - } - - } break; - case NOTIFICATION_TRANSFORM_CHANGED: { - - Navigation2DServer::get_singleton()->region_set_transform(region, get_global_transform()); - - } break; - case NOTIFICATION_EXIT_TREE: { - - if (navigation) { - - Navigation2DServer::get_singleton()->region_set_map(region, RID()); - } - navigation = NULL; - } break; - case NOTIFICATION_DRAW: { - - if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_navigation_hint()) && navpoly.is_valid()) { - - Vector verts = navpoly->get_vertices(); - int vsize = verts.size(); - if (vsize < 3) - return; - - Color color; - if (enabled) { - color = get_tree()->get_debug_navigation_color(); - } else { - color = get_tree()->get_debug_navigation_disabled_color(); - } - Vector colors; - Vector vertices; - vertices.resize(vsize); - colors.resize(vsize); - { - const Vector2 *vr = verts.ptr(); - for (int i = 0; i < vsize; i++) { - vertices.write[i] = vr[i]; - colors.write[i] = color; - } - } - - Vector indices; - - for (int i = 0; i < navpoly->get_polygon_count(); i++) { - Vector polygon = navpoly->get_polygon(i); - - for (int j = 2; j < polygon.size(); j++) { - - int kofs[3] = { 0, j - 1, j }; - for (int k = 0; k < 3; k++) { - - int idx = polygon[kofs[k]]; - ERR_FAIL_INDEX(idx, vsize); - indices.push_back(idx); - } - } - } - VS::get_singleton()->canvas_item_add_triangle_array(get_canvas_item(), indices, vertices, colors); - } - } break; - } -} - -void NavigationRegion2D::set_navigation_polygon(const Ref &p_navpoly) { - - if (p_navpoly == navpoly) { - return; - } - - if (navpoly.is_valid()) { - navpoly->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NavigationRegion2D::_navpoly_changed)); - } - - navpoly = p_navpoly; - Navigation2DServer::get_singleton()->region_set_navpoly(region, p_navpoly); - - if (navpoly.is_valid()) { - navpoly->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NavigationRegion2D::_navpoly_changed)); - } - _navpoly_changed(); - - _change_notify("navpoly"); - update_configuration_warning(); -} - -Ref NavigationRegion2D::get_navigation_polygon() const { - - return navpoly; -} - -void NavigationRegion2D::_navpoly_changed() { - - if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_navigation_hint())) - update(); -} - -String NavigationRegion2D::get_configuration_warning() const { - - if (!is_visible_in_tree() || !is_inside_tree()) - return String(); - - if (!navpoly.is_valid()) { - return TTR("A NavigationPolygon resource must be set or created for this node to work. Please set a property or draw a polygon."); - } - const Node2D *c = this; - while (c) { - - if (Object::cast_to(c)) { - return String(); - } - - c = Object::cast_to(c->get_parent()); - } - - return TTR("NavigationRegion2D must be a child or grandchild to a Navigation2D node. It only provides navigation data."); -} - -void NavigationRegion2D::_bind_methods() { - - ClassDB::bind_method(D_METHOD("set_navigation_polygon", "navpoly"), &NavigationRegion2D::set_navigation_polygon); - ClassDB::bind_method(D_METHOD("get_navigation_polygon"), &NavigationRegion2D::get_navigation_polygon); - - ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &NavigationRegion2D::set_enabled); - ClassDB::bind_method(D_METHOD("is_enabled"), &NavigationRegion2D::is_enabled); - - ClassDB::bind_method(D_METHOD("_navpoly_changed"), &NavigationRegion2D::_navpoly_changed); - - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "navpoly", PROPERTY_HINT_RESOURCE_TYPE, "NavigationPolygon"), "set_navigation_polygon", "get_navigation_polygon"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled"); -} - -NavigationRegion2D::NavigationRegion2D() { - - enabled = true; - set_notify_transform(true); - region = Navigation2DServer::get_singleton()->region_create(); - - navigation = NULL; -} - -NavigationRegion2D::~NavigationRegion2D() { - Navigation2DServer::get_singleton()->free(region); -} diff --git a/scene/2d/navigation_polygon.h b/scene/2d/navigation_polygon.h deleted file mode 100644 index 3d096ec91b..0000000000 --- a/scene/2d/navigation_polygon.h +++ /dev/null @@ -1,130 +0,0 @@ -/*************************************************************************/ -/* navigation_polygon.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef NAVIGATION_POLYGON_H -#define NAVIGATION_POLYGON_H - -#include "scene/2d/node_2d.h" -#include "scene/resources/navigation_mesh.h" - -class NavigationPolygon : public Resource { - - GDCLASS(NavigationPolygon, Resource); - - Vector vertices; - struct Polygon { - Vector indices; - }; - Vector polygons; - Vector> outlines; - - mutable Rect2 item_rect; - mutable bool rect_cache_dirty; - - Mutex navmesh_generation; - // Navigation mesh - Ref navmesh; - -protected: - static void _bind_methods(); - - void _set_polygons(const Array &p_array); - Array _get_polygons() const; - - void _set_outlines(const Array &p_array); - Array _get_outlines() const; - -public: -#ifdef TOOLS_ENABLED - Rect2 _edit_get_rect() const; - bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; -#endif - - void set_vertices(const Vector &p_vertices); - Vector get_vertices() const; - - void add_polygon(const Vector &p_polygon); - int get_polygon_count() const; - - void add_outline(const Vector &p_outline); - void add_outline_at_index(const Vector &p_outline, int p_index); - void set_outline(int p_idx, const Vector &p_outline); - Vector get_outline(int p_idx) const; - void remove_outline(int p_idx); - int get_outline_count() const; - - void clear_outlines(); - void make_polygons_from_outlines(); - - Vector get_polygon(int p_idx); - void clear_polygons(); - - Ref get_mesh(); - - NavigationPolygon(); - ~NavigationPolygon(); -}; - -class Navigation2D; - -class NavigationRegion2D : public Node2D { - - GDCLASS(NavigationRegion2D, Node2D); - - bool enabled; - RID region; - Navigation2D *navigation; - Ref navpoly; - - void _navpoly_changed(); - -protected: - void _notification(int p_what); - static void _bind_methods(); - -public: -#ifdef TOOLS_ENABLED - virtual Rect2 _edit_get_rect() const; - virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; -#endif - - void set_enabled(bool p_enabled); - bool is_enabled() const; - - void set_navigation_polygon(const Ref &p_navpoly); - Ref get_navigation_polygon() const; - - String get_configuration_warning() const; - - NavigationRegion2D(); - ~NavigationRegion2D(); -}; - -#endif // NAVIGATIONPOLYGON_H diff --git a/scene/2d/navigation_region_2d.cpp b/scene/2d/navigation_region_2d.cpp new file mode 100644 index 0000000000..bc3e305281 --- /dev/null +++ b/scene/2d/navigation_region_2d.cpp @@ -0,0 +1,582 @@ +/*************************************************************************/ +/* navigation_region_2d.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "navigation_region_2d.h" + +#include "core/core_string_names.h" +#include "core/engine.h" +#include "core/os/mutex.h" +#include "navigation_2d.h" +#include "servers/navigation_2d_server.h" + +#include "thirdparty/misc/triangulator.h" + +#ifdef TOOLS_ENABLED +Rect2 NavigationPolygon::_edit_get_rect() const { + + if (rect_cache_dirty) { + item_rect = Rect2(); + bool first = true; + + for (int i = 0; i < outlines.size(); i++) { + const Vector &outline = outlines[i]; + const int outline_size = outline.size(); + if (outline_size < 3) + continue; + const Vector2 *p = outline.ptr(); + for (int j = 0; j < outline_size; j++) { + if (first) { + item_rect = Rect2(p[j], Vector2(0, 0)); + first = false; + } else { + item_rect.expand_to(p[j]); + } + } + } + + rect_cache_dirty = false; + } + return item_rect; +} + +bool NavigationPolygon::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { + + for (int i = 0; i < outlines.size(); i++) { + const Vector &outline = outlines[i]; + const int outline_size = outline.size(); + if (outline_size < 3) + continue; + if (Geometry::is_point_in_polygon(p_point, Variant(outline))) + return true; + } + return false; +} +#endif + +void NavigationPolygon::set_vertices(const Vector &p_vertices) { + + { + MutexLock lock(navmesh_generation); + navmesh.unref(); + } + vertices = p_vertices; + rect_cache_dirty = true; +} + +Vector NavigationPolygon::get_vertices() const { + + return vertices; +} + +void NavigationPolygon::_set_polygons(const Array &p_array) { + + { + MutexLock lock(navmesh_generation); + navmesh.unref(); + } + polygons.resize(p_array.size()); + for (int i = 0; i < p_array.size(); i++) { + polygons.write[i].indices = p_array[i]; + } +} + +Array NavigationPolygon::_get_polygons() const { + + Array ret; + ret.resize(polygons.size()); + for (int i = 0; i < ret.size(); i++) { + ret[i] = polygons[i].indices; + } + + return ret; +} + +void NavigationPolygon::_set_outlines(const Array &p_array) { + + outlines.resize(p_array.size()); + for (int i = 0; i < p_array.size(); i++) { + outlines.write[i] = p_array[i]; + } + rect_cache_dirty = true; +} + +Array NavigationPolygon::_get_outlines() const { + + Array ret; + ret.resize(outlines.size()); + for (int i = 0; i < ret.size(); i++) { + ret[i] = outlines[i]; + } + + return ret; +} + +void NavigationPolygon::add_polygon(const Vector &p_polygon) { + + Polygon polygon; + polygon.indices = p_polygon; + polygons.push_back(polygon); + { + MutexLock lock(navmesh_generation); + navmesh.unref(); + } +} + +void NavigationPolygon::add_outline_at_index(const Vector &p_outline, int p_index) { + + outlines.insert(p_index, p_outline); + rect_cache_dirty = true; +} + +int NavigationPolygon::get_polygon_count() const { + + return polygons.size(); +} +Vector NavigationPolygon::get_polygon(int p_idx) { + + ERR_FAIL_INDEX_V(p_idx, polygons.size(), Vector()); + return polygons[p_idx].indices; +} +void NavigationPolygon::clear_polygons() { + + polygons.clear(); + { + MutexLock lock(navmesh_generation); + navmesh.unref(); + } +} + +Ref NavigationPolygon::get_mesh() { + MutexLock lock(navmesh_generation); + + if (navmesh.is_null()) { + navmesh.instance(); + Vector verts; + { + verts.resize(get_vertices().size()); + Vector3 *w = verts.ptrw(); + + const Vector2 *r = get_vertices().ptr(); + + for (int i(0); i < get_vertices().size(); i++) { + w[i] = Vector3(r[i].x, 0.0, r[i].y); + } + } + navmesh->set_vertices(verts); + + for (int i(0); i < get_polygon_count(); i++) { + navmesh->add_polygon(get_polygon(i)); + } + } + + return navmesh; +} + +void NavigationPolygon::add_outline(const Vector &p_outline) { + + outlines.push_back(p_outline); + rect_cache_dirty = true; +} + +int NavigationPolygon::get_outline_count() const { + + return outlines.size(); +} + +void NavigationPolygon::set_outline(int p_idx, const Vector &p_outline) { + ERR_FAIL_INDEX(p_idx, outlines.size()); + outlines.write[p_idx] = p_outline; + rect_cache_dirty = true; +} + +void NavigationPolygon::remove_outline(int p_idx) { + + ERR_FAIL_INDEX(p_idx, outlines.size()); + outlines.remove(p_idx); + rect_cache_dirty = true; +} + +Vector NavigationPolygon::get_outline(int p_idx) const { + ERR_FAIL_INDEX_V(p_idx, outlines.size(), Vector()); + return outlines[p_idx]; +} + +void NavigationPolygon::clear_outlines() { + + outlines.clear(); + rect_cache_dirty = true; +} +void NavigationPolygon::make_polygons_from_outlines() { + + { + MutexLock lock(navmesh_generation); + navmesh.unref(); + } + List in_poly, out_poly; + + Vector2 outside_point(-1e10, -1e10); + + for (int i = 0; i < outlines.size(); i++) { + + Vector ol = outlines[i]; + int olsize = ol.size(); + if (olsize < 3) + continue; + const Vector2 *r = ol.ptr(); + for (int j = 0; j < olsize; j++) { + outside_point.x = MAX(r[j].x, outside_point.x); + outside_point.y = MAX(r[j].y, outside_point.y); + } + } + + outside_point += Vector2(0.7239784, 0.819238); //avoid precision issues + + for (int i = 0; i < outlines.size(); i++) { + + Vector ol = outlines[i]; + int olsize = ol.size(); + if (olsize < 3) + continue; + const Vector2 *r = ol.ptr(); + + int interscount = 0; + //test if this is an outer outline + for (int k = 0; k < outlines.size(); k++) { + + if (i == k) + continue; //no self intersect + + Vector ol2 = outlines[k]; + int olsize2 = ol2.size(); + if (olsize2 < 3) + continue; + const Vector2 *r2 = ol2.ptr(); + + for (int l = 0; l < olsize2; l++) { + + if (Geometry::segment_intersects_segment_2d(r[0], outside_point, r2[l], r2[(l + 1) % olsize2], NULL)) { + interscount++; + } + } + } + + bool outer = (interscount % 2) == 0; + + TriangulatorPoly tp; + tp.Init(olsize); + for (int j = 0; j < olsize; j++) { + tp[j] = r[j]; + } + + if (outer) + tp.SetOrientation(TRIANGULATOR_CCW); + else { + tp.SetOrientation(TRIANGULATOR_CW); + tp.SetHole(true); + } + + in_poly.push_back(tp); + } + + TriangulatorPartition tpart; + if (tpart.ConvexPartition_HM(&in_poly, &out_poly) == 0) { //failed! + ERR_PRINT("NavigationPolygon: Convex partition failed!"); + return; + } + + polygons.clear(); + vertices.resize(0); + + Map points; + for (List::Element *I = out_poly.front(); I; I = I->next()) { + + TriangulatorPoly &tp = I->get(); + + struct Polygon p; + + for (int64_t i = 0; i < tp.GetNumPoints(); i++) { + + Map::Element *E = points.find(tp[i]); + if (!E) { + E = points.insert(tp[i], vertices.size()); + vertices.push_back(tp[i]); + } + p.indices.push_back(E->get()); + } + + polygons.push_back(p); + } + + emit_signal(CoreStringNames::get_singleton()->changed); +} + +void NavigationPolygon::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_vertices", "vertices"), &NavigationPolygon::set_vertices); + ClassDB::bind_method(D_METHOD("get_vertices"), &NavigationPolygon::get_vertices); + + ClassDB::bind_method(D_METHOD("add_polygon", "polygon"), &NavigationPolygon::add_polygon); + ClassDB::bind_method(D_METHOD("get_polygon_count"), &NavigationPolygon::get_polygon_count); + ClassDB::bind_method(D_METHOD("get_polygon", "idx"), &NavigationPolygon::get_polygon); + ClassDB::bind_method(D_METHOD("clear_polygons"), &NavigationPolygon::clear_polygons); + + ClassDB::bind_method(D_METHOD("add_outline", "outline"), &NavigationPolygon::add_outline); + ClassDB::bind_method(D_METHOD("add_outline_at_index", "outline", "index"), &NavigationPolygon::add_outline_at_index); + ClassDB::bind_method(D_METHOD("get_outline_count"), &NavigationPolygon::get_outline_count); + ClassDB::bind_method(D_METHOD("set_outline", "idx", "outline"), &NavigationPolygon::set_outline); + ClassDB::bind_method(D_METHOD("get_outline", "idx"), &NavigationPolygon::get_outline); + ClassDB::bind_method(D_METHOD("remove_outline", "idx"), &NavigationPolygon::remove_outline); + ClassDB::bind_method(D_METHOD("clear_outlines"), &NavigationPolygon::clear_outlines); + ClassDB::bind_method(D_METHOD("make_polygons_from_outlines"), &NavigationPolygon::make_polygons_from_outlines); + + ClassDB::bind_method(D_METHOD("_set_polygons", "polygons"), &NavigationPolygon::_set_polygons); + ClassDB::bind_method(D_METHOD("_get_polygons"), &NavigationPolygon::_get_polygons); + + ClassDB::bind_method(D_METHOD("_set_outlines", "outlines"), &NavigationPolygon::_set_outlines); + ClassDB::bind_method(D_METHOD("_get_outlines"), &NavigationPolygon::_get_outlines); + + ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_vertices", "get_vertices"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_polygons", "_get_polygons"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "outlines", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_outlines", "_get_outlines"); +} + +NavigationPolygon::NavigationPolygon() : + rect_cache_dirty(true) { +} + +NavigationPolygon::~NavigationPolygon() { +} + +void NavigationRegion2D::set_enabled(bool p_enabled) { + + if (enabled == p_enabled) + return; + enabled = p_enabled; + + if (!is_inside_tree()) + return; + + if (!enabled) { + + Navigation2DServer::get_singleton()->region_set_map(region, RID()); + } else { + + if (navigation) { + + Navigation2DServer::get_singleton()->region_set_map(region, navigation->get_rid()); + } + } + + if (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_navigation_hint()) + update(); +} + +bool NavigationRegion2D::is_enabled() const { + + return enabled; +} + +///////////////////////////// +#ifdef TOOLS_ENABLED +Rect2 NavigationRegion2D::_edit_get_rect() const { + + return navpoly.is_valid() ? navpoly->_edit_get_rect() : Rect2(); +} + +bool NavigationRegion2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { + + return navpoly.is_valid() ? navpoly->_edit_is_selected_on_click(p_point, p_tolerance) : false; +} +#endif + +void NavigationRegion2D::_notification(int p_what) { + + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + + Node2D *c = this; + while (c) { + + navigation = Object::cast_to(c); + if (navigation) { + + if (enabled) { + + Navigation2DServer::get_singleton()->region_set_map(region, navigation->get_rid()); + } + break; + } + + c = Object::cast_to(c->get_parent()); + } + + } break; + case NOTIFICATION_TRANSFORM_CHANGED: { + + Navigation2DServer::get_singleton()->region_set_transform(region, get_global_transform()); + + } break; + case NOTIFICATION_EXIT_TREE: { + + if (navigation) { + + Navigation2DServer::get_singleton()->region_set_map(region, RID()); + } + navigation = NULL; + } break; + case NOTIFICATION_DRAW: { + + if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_navigation_hint()) && navpoly.is_valid()) { + + Vector verts = navpoly->get_vertices(); + int vsize = verts.size(); + if (vsize < 3) + return; + + Color color; + if (enabled) { + color = get_tree()->get_debug_navigation_color(); + } else { + color = get_tree()->get_debug_navigation_disabled_color(); + } + Vector colors; + Vector vertices; + vertices.resize(vsize); + colors.resize(vsize); + { + const Vector2 *vr = verts.ptr(); + for (int i = 0; i < vsize; i++) { + vertices.write[i] = vr[i]; + colors.write[i] = color; + } + } + + Vector indices; + + for (int i = 0; i < navpoly->get_polygon_count(); i++) { + Vector polygon = navpoly->get_polygon(i); + + for (int j = 2; j < polygon.size(); j++) { + + int kofs[3] = { 0, j - 1, j }; + for (int k = 0; k < 3; k++) { + + int idx = polygon[kofs[k]]; + ERR_FAIL_INDEX(idx, vsize); + indices.push_back(idx); + } + } + } + VS::get_singleton()->canvas_item_add_triangle_array(get_canvas_item(), indices, vertices, colors); + } + } break; + } +} + +void NavigationRegion2D::set_navigation_polygon(const Ref &p_navpoly) { + + if (p_navpoly == navpoly) { + return; + } + + if (navpoly.is_valid()) { + navpoly->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NavigationRegion2D::_navpoly_changed)); + } + + navpoly = p_navpoly; + Navigation2DServer::get_singleton()->region_set_navpoly(region, p_navpoly); + + if (navpoly.is_valid()) { + navpoly->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NavigationRegion2D::_navpoly_changed)); + } + _navpoly_changed(); + + _change_notify("navpoly"); + update_configuration_warning(); +} + +Ref NavigationRegion2D::get_navigation_polygon() const { + + return navpoly; +} + +void NavigationRegion2D::_navpoly_changed() { + + if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_navigation_hint())) + update(); +} + +String NavigationRegion2D::get_configuration_warning() const { + + if (!is_visible_in_tree() || !is_inside_tree()) + return String(); + + if (!navpoly.is_valid()) { + return TTR("A NavigationPolygon resource must be set or created for this node to work. Please set a property or draw a polygon."); + } + const Node2D *c = this; + while (c) { + + if (Object::cast_to(c)) { + return String(); + } + + c = Object::cast_to(c->get_parent()); + } + + return TTR("NavigationRegion2D must be a child or grandchild to a Navigation2D node. It only provides navigation data."); +} + +void NavigationRegion2D::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_navigation_polygon", "navpoly"), &NavigationRegion2D::set_navigation_polygon); + ClassDB::bind_method(D_METHOD("get_navigation_polygon"), &NavigationRegion2D::get_navigation_polygon); + + ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &NavigationRegion2D::set_enabled); + ClassDB::bind_method(D_METHOD("is_enabled"), &NavigationRegion2D::is_enabled); + + ClassDB::bind_method(D_METHOD("_navpoly_changed"), &NavigationRegion2D::_navpoly_changed); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "navpoly", PROPERTY_HINT_RESOURCE_TYPE, "NavigationPolygon"), "set_navigation_polygon", "get_navigation_polygon"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled"); +} + +NavigationRegion2D::NavigationRegion2D() { + + enabled = true; + set_notify_transform(true); + region = Navigation2DServer::get_singleton()->region_create(); + + navigation = NULL; +} + +NavigationRegion2D::~NavigationRegion2D() { + Navigation2DServer::get_singleton()->free(region); +} diff --git a/scene/2d/navigation_region_2d.h b/scene/2d/navigation_region_2d.h new file mode 100644 index 0000000000..73e056a353 --- /dev/null +++ b/scene/2d/navigation_region_2d.h @@ -0,0 +1,130 @@ +/*************************************************************************/ +/* navigation_region_2d.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef NAVIGATION_REGION_2D_H +#define NAVIGATION_REGION_2D_H + +#include "scene/2d/node_2d.h" +#include "scene/resources/navigation_mesh.h" + +class NavigationPolygon : public Resource { + + GDCLASS(NavigationPolygon, Resource); + + Vector vertices; + struct Polygon { + Vector indices; + }; + Vector polygons; + Vector> outlines; + + mutable Rect2 item_rect; + mutable bool rect_cache_dirty; + + Mutex navmesh_generation; + // Navigation mesh + Ref navmesh; + +protected: + static void _bind_methods(); + + void _set_polygons(const Array &p_array); + Array _get_polygons() const; + + void _set_outlines(const Array &p_array); + Array _get_outlines() const; + +public: +#ifdef TOOLS_ENABLED + Rect2 _edit_get_rect() const; + bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; +#endif + + void set_vertices(const Vector &p_vertices); + Vector get_vertices() const; + + void add_polygon(const Vector &p_polygon); + int get_polygon_count() const; + + void add_outline(const Vector &p_outline); + void add_outline_at_index(const Vector &p_outline, int p_index); + void set_outline(int p_idx, const Vector &p_outline); + Vector get_outline(int p_idx) const; + void remove_outline(int p_idx); + int get_outline_count() const; + + void clear_outlines(); + void make_polygons_from_outlines(); + + Vector get_polygon(int p_idx); + void clear_polygons(); + + Ref get_mesh(); + + NavigationPolygon(); + ~NavigationPolygon(); +}; + +class Navigation2D; + +class NavigationRegion2D : public Node2D { + + GDCLASS(NavigationRegion2D, Node2D); + + bool enabled; + RID region; + Navigation2D *navigation; + Ref navpoly; + + void _navpoly_changed(); + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: +#ifdef TOOLS_ENABLED + virtual Rect2 _edit_get_rect() const; + virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; +#endif + + void set_enabled(bool p_enabled); + bool is_enabled() const; + + void set_navigation_polygon(const Ref &p_navpoly); + Ref get_navigation_polygon() const; + + String get_configuration_warning() const; + + NavigationRegion2D(); + ~NavigationRegion2D(); +}; + +#endif // NAVIGATION_REGION_2D_H diff --git a/scene/2d/node_2d.h b/scene/2d/node_2d.h index 00202481a6..abed05ed0c 100644 --- a/scene/2d/node_2d.h +++ b/scene/2d/node_2d.h @@ -31,7 +31,7 @@ #ifndef NODE2D_H #define NODE2D_H -#include "scene/2d/canvas_item.h" +#include "scene/main/canvas_item.h" class Node2D : public CanvasItem { diff --git a/scene/2d/particles_2d.cpp b/scene/2d/particles_2d.cpp deleted file mode 100644 index e5c17fe9a4..0000000000 --- a/scene/2d/particles_2d.cpp +++ /dev/null @@ -1,432 +0,0 @@ -/*************************************************************************/ -/* particles_2d.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "particles_2d.h" - -#include "core/os/os.h" -#include "scene/resources/particles_material.h" -#include "scene/scene_string_names.h" - -#ifdef TOOLS_ENABLED -#include "core/engine.h" -#endif - -void Particles2D::set_emitting(bool p_emitting) { - - VS::get_singleton()->particles_set_emitting(particles, p_emitting); - - if (p_emitting && one_shot) { - set_process_internal(true); - } else if (!p_emitting) { - set_process_internal(false); - } -} - -void Particles2D::set_amount(int p_amount) { - - ERR_FAIL_COND_MSG(p_amount < 1, "Amount of particles cannot be smaller than 1."); - amount = p_amount; - VS::get_singleton()->particles_set_amount(particles, amount); -} -void Particles2D::set_lifetime(float p_lifetime) { - - ERR_FAIL_COND_MSG(p_lifetime <= 0, "Particles lifetime must be greater than 0."); - lifetime = p_lifetime; - VS::get_singleton()->particles_set_lifetime(particles, lifetime); -} - -void Particles2D::set_one_shot(bool p_enable) { - - one_shot = p_enable; - VS::get_singleton()->particles_set_one_shot(particles, one_shot); - - if (is_emitting()) { - - set_process_internal(true); - if (!one_shot) - VisualServer::get_singleton()->particles_restart(particles); - } - - if (!one_shot) - set_process_internal(false); -} -void Particles2D::set_pre_process_time(float p_time) { - - pre_process_time = p_time; - VS::get_singleton()->particles_set_pre_process_time(particles, pre_process_time); -} -void Particles2D::set_explosiveness_ratio(float p_ratio) { - - explosiveness_ratio = p_ratio; - VS::get_singleton()->particles_set_explosiveness_ratio(particles, explosiveness_ratio); -} -void Particles2D::set_randomness_ratio(float p_ratio) { - - randomness_ratio = p_ratio; - VS::get_singleton()->particles_set_randomness_ratio(particles, randomness_ratio); -} -void Particles2D::set_visibility_rect(const Rect2 &p_visibility_rect) { - - visibility_rect = p_visibility_rect; - AABB aabb; - aabb.position.x = p_visibility_rect.position.x; - aabb.position.y = p_visibility_rect.position.y; - aabb.size.x = p_visibility_rect.size.x; - aabb.size.y = p_visibility_rect.size.y; - - VS::get_singleton()->particles_set_custom_aabb(particles, aabb); - - _change_notify("visibility_rect"); - update(); -} -void Particles2D::set_use_local_coordinates(bool p_enable) { - - local_coords = p_enable; - VS::get_singleton()->particles_set_use_local_coordinates(particles, local_coords); - set_notify_transform(!p_enable); - if (!p_enable && is_inside_tree()) { - _update_particle_emission_transform(); - } -} - -void Particles2D::_update_particle_emission_transform() { - - Transform2D xf2d = get_global_transform(); - Transform xf; - xf.basis.set_axis(0, Vector3(xf2d.get_axis(0).x, xf2d.get_axis(0).y, 0)); - xf.basis.set_axis(1, Vector3(xf2d.get_axis(1).x, xf2d.get_axis(1).y, 0)); - xf.set_origin(Vector3(xf2d.get_origin().x, xf2d.get_origin().y, 0)); - - VS::get_singleton()->particles_set_emission_transform(particles, xf); -} - -void Particles2D::set_process_material(const Ref &p_material) { - - process_material = p_material; - Ref pm = p_material; - if (pm.is_valid() && !pm->get_flag(ParticlesMaterial::FLAG_DISABLE_Z) && pm->get_gravity() == Vector3(0, -9.8, 0)) { - // Likely a new (3D) material, modify it to match 2D space - pm->set_flag(ParticlesMaterial::FLAG_DISABLE_Z, true); - pm->set_gravity(Vector3(0, 98, 0)); - } - RID material_rid; - if (process_material.is_valid()) - material_rid = process_material->get_rid(); - VS::get_singleton()->particles_set_process_material(particles, material_rid); - - update_configuration_warning(); -} - -void Particles2D::set_speed_scale(float p_scale) { - - speed_scale = p_scale; - VS::get_singleton()->particles_set_speed_scale(particles, p_scale); -} - -bool Particles2D::is_emitting() const { - - return VS::get_singleton()->particles_get_emitting(particles); -} -int Particles2D::get_amount() const { - - return amount; -} -float Particles2D::get_lifetime() const { - - return lifetime; -} - -bool Particles2D::get_one_shot() const { - - return one_shot; -} -float Particles2D::get_pre_process_time() const { - - return pre_process_time; -} -float Particles2D::get_explosiveness_ratio() const { - - return explosiveness_ratio; -} -float Particles2D::get_randomness_ratio() const { - - return randomness_ratio; -} -Rect2 Particles2D::get_visibility_rect() const { - - return visibility_rect; -} -bool Particles2D::get_use_local_coordinates() const { - - return local_coords; -} -Ref Particles2D::get_process_material() const { - - return process_material; -} - -float Particles2D::get_speed_scale() const { - - return speed_scale; -} - -void Particles2D::set_draw_order(DrawOrder p_order) { - - draw_order = p_order; - VS::get_singleton()->particles_set_draw_order(particles, VS::ParticlesDrawOrder(p_order)); -} - -Particles2D::DrawOrder Particles2D::get_draw_order() const { - - return draw_order; -} - -void Particles2D::set_fixed_fps(int p_count) { - fixed_fps = p_count; - VS::get_singleton()->particles_set_fixed_fps(particles, p_count); -} - -int Particles2D::get_fixed_fps() const { - return fixed_fps; -} - -void Particles2D::set_fractional_delta(bool p_enable) { - fractional_delta = p_enable; - VS::get_singleton()->particles_set_fractional_delta(particles, p_enable); -} - -bool Particles2D::get_fractional_delta() const { - return fractional_delta; -} - -String Particles2D::get_configuration_warning() const { - - if (VisualServer::get_singleton()->is_low_end()) { - return TTR("GPU-based particles are not supported by the GLES2 video driver.\nUse the CPUParticles2D node instead. You can use the \"Convert to CPUParticles\" option for this purpose."); - } - - String warnings; - - if (process_material.is_null()) { - if (warnings != String()) - warnings += "\n"; - warnings += "- " + TTR("A material to process the particles is not assigned, so no behavior is imprinted."); - } else { - - CanvasItemMaterial *mat = Object::cast_to(get_material().ptr()); - - if (get_material().is_null() || (mat && !mat->get_particles_animation())) { - const ParticlesMaterial *process = Object::cast_to(process_material.ptr()); - if (process && - (process->get_param(ParticlesMaterial::PARAM_ANIM_SPEED) != 0.0 || process->get_param(ParticlesMaterial::PARAM_ANIM_OFFSET) != 0.0 || - process->get_param_texture(ParticlesMaterial::PARAM_ANIM_SPEED).is_valid() || process->get_param_texture(ParticlesMaterial::PARAM_ANIM_OFFSET).is_valid())) { - if (warnings != String()) - warnings += "\n"; - warnings += "- " + TTR("Particles2D animation requires the usage of a CanvasItemMaterial with \"Particles Animation\" enabled."); - } - } - } - - return warnings; -} - -Rect2 Particles2D::capture_rect() const { - - AABB aabb = VS::get_singleton()->particles_get_current_aabb(particles); - Rect2 r; - r.position.x = aabb.position.x; - r.position.y = aabb.position.y; - r.size.x = aabb.size.x; - r.size.y = aabb.size.y; - return r; -} - -void Particles2D::set_texture(const Ref &p_texture) { - texture = p_texture; - update(); -} - -Ref Particles2D::get_texture() const { - return texture; -} - -void Particles2D::set_normal_map(const Ref &p_normal_map) { - - normal_map = p_normal_map; - update(); -} - -Ref Particles2D::get_normal_map() const { - return normal_map; -} - -void Particles2D::_validate_property(PropertyInfo &property) const { -} - -void Particles2D::restart() { - VS::get_singleton()->particles_restart(particles); - VS::get_singleton()->particles_set_emitting(particles, true); -} - -void Particles2D::_notification(int p_what) { - - if (p_what == NOTIFICATION_DRAW) { - - RID texture_rid; - if (texture.is_valid()) - texture_rid = texture->get_rid(); - RID normal_rid; - if (normal_map.is_valid()) - normal_rid = normal_map->get_rid(); - - VS::get_singleton()->canvas_item_add_particles(get_canvas_item(), particles, texture_rid, normal_rid); - -#ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint() && (this == get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->is_a_parent_of(this))) { - - draw_rect(visibility_rect, Color(0, 0.7, 0.9, 0.4), false); - } -#endif - } - - if (p_what == NOTIFICATION_PAUSED || p_what == NOTIFICATION_UNPAUSED) { - if (can_process()) { - VS::get_singleton()->particles_set_speed_scale(particles, speed_scale); - } else { - - VS::get_singleton()->particles_set_speed_scale(particles, 0); - } - } - - if (p_what == NOTIFICATION_TRANSFORM_CHANGED) { - _update_particle_emission_transform(); - } - - if (p_what == NOTIFICATION_INTERNAL_PROCESS) { - - if (one_shot && !is_emitting()) { - _change_notify(); - set_process_internal(false); - } - } -} - -void Particles2D::_bind_methods() { - - ClassDB::bind_method(D_METHOD("set_emitting", "emitting"), &Particles2D::set_emitting); - ClassDB::bind_method(D_METHOD("set_amount", "amount"), &Particles2D::set_amount); - ClassDB::bind_method(D_METHOD("set_lifetime", "secs"), &Particles2D::set_lifetime); - ClassDB::bind_method(D_METHOD("set_one_shot", "secs"), &Particles2D::set_one_shot); - ClassDB::bind_method(D_METHOD("set_pre_process_time", "secs"), &Particles2D::set_pre_process_time); - ClassDB::bind_method(D_METHOD("set_explosiveness_ratio", "ratio"), &Particles2D::set_explosiveness_ratio); - ClassDB::bind_method(D_METHOD("set_randomness_ratio", "ratio"), &Particles2D::set_randomness_ratio); - ClassDB::bind_method(D_METHOD("set_visibility_rect", "visibility_rect"), &Particles2D::set_visibility_rect); - ClassDB::bind_method(D_METHOD("set_use_local_coordinates", "enable"), &Particles2D::set_use_local_coordinates); - ClassDB::bind_method(D_METHOD("set_fixed_fps", "fps"), &Particles2D::set_fixed_fps); - ClassDB::bind_method(D_METHOD("set_fractional_delta", "enable"), &Particles2D::set_fractional_delta); - ClassDB::bind_method(D_METHOD("set_process_material", "material"), &Particles2D::set_process_material); - ClassDB::bind_method(D_METHOD("set_speed_scale", "scale"), &Particles2D::set_speed_scale); - - ClassDB::bind_method(D_METHOD("is_emitting"), &Particles2D::is_emitting); - ClassDB::bind_method(D_METHOD("get_amount"), &Particles2D::get_amount); - ClassDB::bind_method(D_METHOD("get_lifetime"), &Particles2D::get_lifetime); - ClassDB::bind_method(D_METHOD("get_one_shot"), &Particles2D::get_one_shot); - ClassDB::bind_method(D_METHOD("get_pre_process_time"), &Particles2D::get_pre_process_time); - ClassDB::bind_method(D_METHOD("get_explosiveness_ratio"), &Particles2D::get_explosiveness_ratio); - ClassDB::bind_method(D_METHOD("get_randomness_ratio"), &Particles2D::get_randomness_ratio); - ClassDB::bind_method(D_METHOD("get_visibility_rect"), &Particles2D::get_visibility_rect); - ClassDB::bind_method(D_METHOD("get_use_local_coordinates"), &Particles2D::get_use_local_coordinates); - ClassDB::bind_method(D_METHOD("get_fixed_fps"), &Particles2D::get_fixed_fps); - ClassDB::bind_method(D_METHOD("get_fractional_delta"), &Particles2D::get_fractional_delta); - ClassDB::bind_method(D_METHOD("get_process_material"), &Particles2D::get_process_material); - ClassDB::bind_method(D_METHOD("get_speed_scale"), &Particles2D::get_speed_scale); - - ClassDB::bind_method(D_METHOD("set_draw_order", "order"), &Particles2D::set_draw_order); - ClassDB::bind_method(D_METHOD("get_draw_order"), &Particles2D::get_draw_order); - - ClassDB::bind_method(D_METHOD("set_texture", "texture"), &Particles2D::set_texture); - ClassDB::bind_method(D_METHOD("get_texture"), &Particles2D::get_texture); - - ClassDB::bind_method(D_METHOD("set_normal_map", "texture"), &Particles2D::set_normal_map); - ClassDB::bind_method(D_METHOD("get_normal_map"), &Particles2D::get_normal_map); - - ClassDB::bind_method(D_METHOD("capture_rect"), &Particles2D::capture_rect); - - ClassDB::bind_method(D_METHOD("restart"), &Particles2D::restart); - - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_EXP_RANGE, "1,1000000,1"), "set_amount", "get_amount"); - ADD_GROUP("Time", ""); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01,or_greater"), "set_lifetime", "get_lifetime"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "get_one_shot"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "preprocess", PROPERTY_HINT_RANGE, "0.00,600.0,0.01"), "set_pre_process_time", "get_pre_process_time"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "speed_scale", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_speed_scale", "get_speed_scale"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "explosiveness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_explosiveness_ratio", "get_explosiveness_ratio"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_randomness_ratio", "get_randomness_ratio"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_fps", PROPERTY_HINT_RANGE, "0,1000,1"), "set_fixed_fps", "get_fixed_fps"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fract_delta"), "set_fractional_delta", "get_fractional_delta"); - ADD_GROUP("Drawing", ""); - ADD_PROPERTY(PropertyInfo(Variant::RECT2, "visibility_rect"), "set_visibility_rect", "get_visibility_rect"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "local_coords"), "set_use_local_coordinates", "get_use_local_coordinates"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "draw_order", PROPERTY_HINT_ENUM, "Index,Lifetime"), "set_draw_order", "get_draw_order"); - ADD_GROUP("Process Material", "process_"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "process_material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,ParticlesMaterial"), "set_process_material", "get_process_material"); - ADD_GROUP("Textures", ""); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "normal_map", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_normal_map", "get_normal_map"); - - BIND_ENUM_CONSTANT(DRAW_ORDER_INDEX); - BIND_ENUM_CONSTANT(DRAW_ORDER_LIFETIME); -} - -Particles2D::Particles2D() { - - particles = VS::get_singleton()->particles_create(); - - one_shot = false; // Needed so that set_emitting doesn't access uninitialized values - set_emitting(true); - set_one_shot(false); - set_amount(8); - set_lifetime(1); - set_fixed_fps(0); - set_fractional_delta(true); - set_pre_process_time(0); - set_explosiveness_ratio(0); - set_randomness_ratio(0); - set_visibility_rect(Rect2(Vector2(-100, -100), Vector2(200, 200))); - set_use_local_coordinates(true); - set_draw_order(DRAW_ORDER_INDEX); - set_speed_scale(1); -} - -Particles2D::~Particles2D() { - - VS::get_singleton()->free(particles); -} diff --git a/scene/2d/particles_2d.h b/scene/2d/particles_2d.h deleted file mode 100644 index 66281d7950..0000000000 --- a/scene/2d/particles_2d.h +++ /dev/null @@ -1,127 +0,0 @@ -/*************************************************************************/ -/* particles_2d.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef PARTICLES_2D_H -#define PARTICLES_2D_H - -#include "core/rid.h" -#include "scene/2d/node_2d.h" -#include "scene/resources/texture.h" - -class Particles2D : public Node2D { -private: - GDCLASS(Particles2D, Node2D); - -public: - enum DrawOrder { - DRAW_ORDER_INDEX, - DRAW_ORDER_LIFETIME, - }; - -private: - RID particles; - - bool one_shot; - int amount; - float lifetime; - float pre_process_time; - float explosiveness_ratio; - float randomness_ratio; - float speed_scale; - Rect2 visibility_rect; - bool local_coords; - int fixed_fps; - bool fractional_delta; - - Ref process_material; - - DrawOrder draw_order; - - Ref texture; - Ref normal_map; - - void _update_particle_emission_transform(); - -protected: - static void _bind_methods(); - virtual void _validate_property(PropertyInfo &property) const; - void _notification(int p_what); - -public: - void set_emitting(bool p_emitting); - void set_amount(int p_amount); - void set_lifetime(float p_lifetime); - void set_one_shot(bool p_enable); - void set_pre_process_time(float p_time); - void set_explosiveness_ratio(float p_ratio); - void set_randomness_ratio(float p_ratio); - void set_visibility_rect(const Rect2 &p_visibility_rect); - void set_use_local_coordinates(bool p_enable); - void set_process_material(const Ref &p_material); - void set_speed_scale(float p_scale); - - bool is_emitting() const; - int get_amount() const; - float get_lifetime() const; - bool get_one_shot() const; - float get_pre_process_time() const; - float get_explosiveness_ratio() const; - float get_randomness_ratio() const; - Rect2 get_visibility_rect() const; - bool get_use_local_coordinates() const; - Ref get_process_material() const; - float get_speed_scale() const; - - void set_fixed_fps(int p_count); - int get_fixed_fps() const; - - void set_fractional_delta(bool p_enable); - bool get_fractional_delta() const; - - void set_draw_order(DrawOrder p_order); - DrawOrder get_draw_order() const; - - void set_texture(const Ref &p_texture); - Ref get_texture() const; - - void set_normal_map(const Ref &p_normal_map); - Ref get_normal_map() const; - - virtual String get_configuration_warning() const; - - void restart(); - Rect2 capture_rect() const; - Particles2D(); - ~Particles2D(); -}; - -VARIANT_ENUM_CAST(Particles2D::DrawOrder) - -#endif // PARTICLES_2D_H diff --git a/scene/2d/visibility_notifier_2d.cpp b/scene/2d/visibility_notifier_2d.cpp index 6c1d7c3749..c374dd5faa 100644 --- a/scene/2d/visibility_notifier_2d.cpp +++ b/scene/2d/visibility_notifier_2d.cpp @@ -31,7 +31,7 @@ #include "visibility_notifier_2d.h" #include "core/engine.h" -#include "particles_2d.h" +#include "gpu_particles_2d.h" #include "scene/2d/animated_sprite_2d.h" #include "scene/2d/physics_body_2d.h" #include "scene/animation/animation_player.h" @@ -212,7 +212,7 @@ void VisibilityEnabler2D::_find_nodes(Node *p_node) { } { - Particles2D *ps = Object::cast_to(p_node); + GPUParticles2D *ps = Object::cast_to(p_node); if (ps) { add = true; } @@ -304,7 +304,7 @@ void VisibilityEnabler2D::_change_node_state(Node *p_node, bool p_enabled) { } if (enabler[ENABLER_PAUSE_PARTICLES]) { - Particles2D *ps = Object::cast_to(p_node); + GPUParticles2D *ps = Object::cast_to(p_node); if (ps) { diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp index 5cba16d346..807515219f 100644 --- a/scene/3d/audio_stream_player_3d.cpp +++ b/scene/3d/audio_stream_player_3d.cpp @@ -384,7 +384,7 @@ void AudioStreamPlayer3D::_notification(int p_what) { linear_velocity = velocity_tracker->get_tracked_linear_velocity(); } - Ref world = get_world(); + Ref world = get_world(); ERR_FAIL_COND(world.is_null()); int new_output_count = 0; diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp index bf216ba115..60cccbeedd 100644 --- a/scene/3d/camera_3d.cpp +++ b/scene/3d/camera_3d.cpp @@ -726,13 +726,13 @@ Camera3D::~Camera3D() { //////////////////////////////////////// -void ClippedCamera::set_margin(float p_margin) { +void ClippedCamera3D::set_margin(float p_margin) { margin = p_margin; } -float ClippedCamera::get_margin() const { +float ClippedCamera3D::get_margin() const { return margin; } -void ClippedCamera::set_process_mode(ProcessMode p_mode) { +void ClippedCamera3D::set_process_mode(ProcessMode p_mode) { if (process_mode == p_mode) { return; @@ -741,18 +741,18 @@ void ClippedCamera::set_process_mode(ProcessMode p_mode) { set_process_internal(process_mode == CLIP_PROCESS_IDLE); set_physics_process_internal(process_mode == CLIP_PROCESS_PHYSICS); } -ClippedCamera::ProcessMode ClippedCamera::get_process_mode() const { +ClippedCamera3D::ProcessMode ClippedCamera3D::get_process_mode() const { return process_mode; } -Transform ClippedCamera::get_camera_transform() const { +Transform ClippedCamera3D::get_camera_transform() const { Transform t = Camera3D::get_camera_transform(); t.origin += -t.basis.get_axis(Vector3::AXIS_Z).normalized() * clip_offset; return t; } -void ClippedCamera::_notification(int p_what) { +void ClippedCamera3D::_notification(int p_what) { if (p_what == NOTIFICATION_INTERNAL_PROCESS || p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) { Node3D *parent = Object::cast_to(get_parent()); @@ -813,17 +813,17 @@ void ClippedCamera::_notification(int p_what) { } } -void ClippedCamera::set_collision_mask(uint32_t p_mask) { +void ClippedCamera3D::set_collision_mask(uint32_t p_mask) { collision_mask = p_mask; } -uint32_t ClippedCamera::get_collision_mask() const { +uint32_t ClippedCamera3D::get_collision_mask() const { return collision_mask; } -void ClippedCamera::set_collision_mask_bit(int p_bit, bool p_value) { +void ClippedCamera3D::set_collision_mask_bit(int p_bit, bool p_value) { uint32_t mask = get_collision_mask(); if (p_value) @@ -833,17 +833,17 @@ void ClippedCamera::set_collision_mask_bit(int p_bit, bool p_value) { set_collision_mask(mask); } -bool ClippedCamera::get_collision_mask_bit(int p_bit) const { +bool ClippedCamera3D::get_collision_mask_bit(int p_bit) const { return get_collision_mask() & (1 << p_bit); } -void ClippedCamera::add_exception_rid(const RID &p_rid) { +void ClippedCamera3D::add_exception_rid(const RID &p_rid) { exclude.insert(p_rid); } -void ClippedCamera::add_exception(const Object *p_object) { +void ClippedCamera3D::add_exception(const Object *p_object) { ERR_FAIL_NULL(p_object); const CollisionObject3D *co = Object::cast_to(p_object); @@ -852,12 +852,12 @@ void ClippedCamera::add_exception(const Object *p_object) { add_exception_rid(co->get_rid()); } -void ClippedCamera::remove_exception_rid(const RID &p_rid) { +void ClippedCamera3D::remove_exception_rid(const RID &p_rid) { exclude.erase(p_rid); } -void ClippedCamera::remove_exception(const Object *p_object) { +void ClippedCamera3D::remove_exception(const Object *p_object) { ERR_FAIL_NULL(p_object); const CollisionObject3D *co = Object::cast_to(p_object); @@ -866,65 +866,65 @@ void ClippedCamera::remove_exception(const Object *p_object) { remove_exception_rid(co->get_rid()); } -void ClippedCamera::clear_exceptions() { +void ClippedCamera3D::clear_exceptions() { exclude.clear(); } -float ClippedCamera::get_clip_offset() const { +float ClippedCamera3D::get_clip_offset() const { return clip_offset; } -void ClippedCamera::set_clip_to_areas(bool p_clip) { +void ClippedCamera3D::set_clip_to_areas(bool p_clip) { clip_to_areas = p_clip; } -bool ClippedCamera::is_clip_to_areas_enabled() const { +bool ClippedCamera3D::is_clip_to_areas_enabled() const { return clip_to_areas; } -void ClippedCamera::set_clip_to_bodies(bool p_clip) { +void ClippedCamera3D::set_clip_to_bodies(bool p_clip) { clip_to_bodies = p_clip; } -bool ClippedCamera::is_clip_to_bodies_enabled() const { +bool ClippedCamera3D::is_clip_to_bodies_enabled() const { return clip_to_bodies; } -void ClippedCamera::_bind_methods() { +void ClippedCamera3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_margin", "margin"), &ClippedCamera::set_margin); - ClassDB::bind_method(D_METHOD("get_margin"), &ClippedCamera::get_margin); + ClassDB::bind_method(D_METHOD("set_margin", "margin"), &ClippedCamera3D::set_margin); + ClassDB::bind_method(D_METHOD("get_margin"), &ClippedCamera3D::get_margin); - ClassDB::bind_method(D_METHOD("set_process_mode", "process_mode"), &ClippedCamera::set_process_mode); - ClassDB::bind_method(D_METHOD("get_process_mode"), &ClippedCamera::get_process_mode); + ClassDB::bind_method(D_METHOD("set_process_mode", "process_mode"), &ClippedCamera3D::set_process_mode); + ClassDB::bind_method(D_METHOD("get_process_mode"), &ClippedCamera3D::get_process_mode); - ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &ClippedCamera::set_collision_mask); - ClassDB::bind_method(D_METHOD("get_collision_mask"), &ClippedCamera::get_collision_mask); + ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &ClippedCamera3D::set_collision_mask); + ClassDB::bind_method(D_METHOD("get_collision_mask"), &ClippedCamera3D::get_collision_mask); - ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &ClippedCamera::set_collision_mask_bit); - ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &ClippedCamera::get_collision_mask_bit); + ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &ClippedCamera3D::set_collision_mask_bit); + ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &ClippedCamera3D::get_collision_mask_bit); - ClassDB::bind_method(D_METHOD("add_exception_rid", "rid"), &ClippedCamera::add_exception_rid); - ClassDB::bind_method(D_METHOD("add_exception", "node"), &ClippedCamera::add_exception); + ClassDB::bind_method(D_METHOD("add_exception_rid", "rid"), &ClippedCamera3D::add_exception_rid); + ClassDB::bind_method(D_METHOD("add_exception", "node"), &ClippedCamera3D::add_exception); - ClassDB::bind_method(D_METHOD("remove_exception_rid", "rid"), &ClippedCamera::remove_exception_rid); - ClassDB::bind_method(D_METHOD("remove_exception", "node"), &ClippedCamera::remove_exception); + ClassDB::bind_method(D_METHOD("remove_exception_rid", "rid"), &ClippedCamera3D::remove_exception_rid); + ClassDB::bind_method(D_METHOD("remove_exception", "node"), &ClippedCamera3D::remove_exception); - ClassDB::bind_method(D_METHOD("set_clip_to_areas", "enable"), &ClippedCamera::set_clip_to_areas); - ClassDB::bind_method(D_METHOD("is_clip_to_areas_enabled"), &ClippedCamera::is_clip_to_areas_enabled); + ClassDB::bind_method(D_METHOD("set_clip_to_areas", "enable"), &ClippedCamera3D::set_clip_to_areas); + ClassDB::bind_method(D_METHOD("is_clip_to_areas_enabled"), &ClippedCamera3D::is_clip_to_areas_enabled); - ClassDB::bind_method(D_METHOD("get_clip_offset"), &ClippedCamera::get_clip_offset); + ClassDB::bind_method(D_METHOD("get_clip_offset"), &ClippedCamera3D::get_clip_offset); - ClassDB::bind_method(D_METHOD("set_clip_to_bodies", "enable"), &ClippedCamera::set_clip_to_bodies); - ClassDB::bind_method(D_METHOD("is_clip_to_bodies_enabled"), &ClippedCamera::is_clip_to_bodies_enabled); + ClassDB::bind_method(D_METHOD("set_clip_to_bodies", "enable"), &ClippedCamera3D::set_clip_to_bodies); + ClassDB::bind_method(D_METHOD("is_clip_to_bodies_enabled"), &ClippedCamera3D::is_clip_to_bodies_enabled); - ClassDB::bind_method(D_METHOD("clear_exceptions"), &ClippedCamera::clear_exceptions); + ClassDB::bind_method(D_METHOD("clear_exceptions"), &ClippedCamera3D::clear_exceptions); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "margin", PROPERTY_HINT_RANGE, "0,32,0.01"), "set_margin", "get_margin"); ADD_PROPERTY(PropertyInfo(Variant::INT, "process_mode", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_process_mode", "get_process_mode"); @@ -937,7 +937,7 @@ void ClippedCamera::_bind_methods() { BIND_ENUM_CONSTANT(CLIP_PROCESS_PHYSICS); BIND_ENUM_CONSTANT(CLIP_PROCESS_IDLE); } -ClippedCamera::ClippedCamera() { +ClippedCamera3D::ClippedCamera3D() { margin = 0; clip_offset = 0; process_mode = CLIP_PROCESS_PHYSICS; @@ -949,6 +949,6 @@ ClippedCamera::ClippedCamera() { clip_to_areas = false; clip_to_bodies = true; } -ClippedCamera::~ClippedCamera() { +ClippedCamera3D::~ClippedCamera3D() { PhysicsServer::get_singleton()->free(pyramid_shape); } diff --git a/scene/3d/camera_3d.h b/scene/3d/camera_3d.h index 3c743ccdff..9a005226cb 100644 --- a/scene/3d/camera_3d.h +++ b/scene/3d/camera_3d.h @@ -48,10 +48,8 @@ public: PROJECTION_FRUSTUM }; - enum KeepAspect { - KEEP_WIDTH, - KEEP_HEIGHT - }; + enum KeepAspect { KEEP_WIDTH, + KEEP_HEIGHT }; enum DopplerTracking { DOPPLER_TRACKING_DISABLED, @@ -77,7 +75,7 @@ private: RID camera; RID scenario_id; - //String camera_group; + // String camera_group; uint32_t layers; @@ -86,7 +84,7 @@ private: virtual bool _can_gizmo_scale() const; - //void _camera_make_current(Node *p_camera); + // void _camera_make_current(Node *p_camera); friend class Viewport; void _update_audio_listener_state(); @@ -112,7 +110,8 @@ public: void set_perspective(float p_fovy_degrees, float p_z_near, float p_z_far); void set_orthogonal(float p_size, float p_z_near, float p_z_far); - void set_frustum(float p_size, Vector2 p_offset, float p_z_near, float p_z_far); + void set_frustum(float p_size, Vector2 p_offset, float p_z_near, + float p_z_far); void set_projection(Camera3D::Projection p_mode); void make_current(); @@ -143,7 +142,8 @@ public: virtual Vector3 project_local_ray_normal(const Point2 &p_pos) const; virtual Point2 unproject_position(const Vector3 &p_pos) const; bool is_position_behind(const Vector3 &p_pos) const; - virtual Vector3 project_position(const Point2 &p_point, float p_z_depth) const; + virtual Vector3 project_position(const Point2 &p_point, + float p_z_depth) const; Vector get_near_plane_points() const; @@ -183,9 +183,9 @@ VARIANT_ENUM_CAST(Camera3D::Projection); VARIANT_ENUM_CAST(Camera3D::KeepAspect); VARIANT_ENUM_CAST(Camera3D::DopplerTracking); -class ClippedCamera : public Camera3D { +class ClippedCamera3D : public Camera3D { - GDCLASS(ClippedCamera, Camera3D); + GDCLASS(ClippedCamera3D, Camera3D); public: enum ProcessMode { @@ -238,9 +238,9 @@ public: float get_clip_offset() const; - ClippedCamera(); - ~ClippedCamera(); + ClippedCamera3D(); + ~ClippedCamera3D(); }; -VARIANT_ENUM_CAST(ClippedCamera::ProcessMode); +VARIANT_ENUM_CAST(ClippedCamera3D::ProcessMode); #endif diff --git a/scene/3d/collision_shape_3d.cpp b/scene/3d/collision_shape_3d.cpp index cd171aad16..eab09883cd 100644 --- a/scene/3d/collision_shape_3d.cpp +++ b/scene/3d/collision_shape_3d.cpp @@ -33,7 +33,7 @@ #include "scene/resources/capsule_shape_3d.h" #include "scene/resources/concave_polygon_shape_3d.h" #include "scene/resources/convex_polygon_shape_3d.h" -#include "scene/resources/ray_shape.h" +#include "scene/resources/ray_shape_3d.h" #include "scene/resources/sphere_shape_3d.h" #include "scene/resources/world_margin_shape_3d.h" #include "servers/visual_server.h" diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp index 594eca284c..7f444d59bf 100644 --- a/scene/3d/node_3d.cpp +++ b/scene/3d/node_3d.cpp @@ -507,10 +507,10 @@ bool Node3D::is_set_as_toplevel() const { return data.toplevel; } -Ref Node3D::get_world() const { +Ref Node3D::get_world() const { - ERR_FAIL_COND_V(!is_inside_world(), Ref()); - ERR_FAIL_COND_V(!data.viewport, Ref()); + ERR_FAIL_COND_V(!is_inside_world(), Ref()); + ERR_FAIL_COND_V(!data.viewport, Ref()); return data.viewport->find_world(); } diff --git a/scene/3d/node_3d.h b/scene/3d/node_3d.h index fd14bc730f..f97a8a97dc 100644 --- a/scene/3d/node_3d.h +++ b/scene/3d/node_3d.h @@ -124,7 +124,7 @@ public: Node3D *get_parent_spatial() const; - Ref get_world() const; + Ref get_world() const; void set_translation(const Vector3 &p_translation); void set_rotation(const Vector3 &p_euler_rad); diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp index aae9e38988..ce1643aa6c 100644 --- a/scene/3d/physics_body_3d.cpp +++ b/scene/3d/physics_body_3d.cpp @@ -41,7 +41,7 @@ #include "servers/navigation_server.h" #ifdef TOOLS_ENABLED -#include "editor/plugins/spatial_editor_plugin.h" +#include "editor/plugins/node_3d_editor_plugin.h" #endif Vector3 PhysicsBody3D::get_linear_velocity() const { diff --git a/scene/3d/ray_cast_3d.cpp b/scene/3d/ray_cast_3d.cpp index 578b9c0170..e22c44a3a2 100644 --- a/scene/3d/ray_cast_3d.cpp +++ b/scene/3d/ray_cast_3d.cpp @@ -196,7 +196,7 @@ void RayCast3D::_notification(int p_what) { } void RayCast3D::_update_raycast_state() { - Ref w3d = get_world(); + Ref w3d = get_world(); ERR_FAIL_COND(w3d.is_null()); PhysicsDirectSpaceState *dss = PhysicsServer::get_singleton()->space_get_direct_state(w3d->get_space()); diff --git a/scene/3d/skeleton_ik_3d.cpp b/scene/3d/skeleton_ik_3d.cpp new file mode 100644 index 0000000000..a6c3e25399 --- /dev/null +++ b/scene/3d/skeleton_ik_3d.cpp @@ -0,0 +1,578 @@ +/*************************************************************************/ +/* skeleton_ik_3d.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +/** + * @author AndreaCatania + */ + +#include "skeleton_ik_3d.h" + +#ifndef _3D_DISABLED + +FabrikInverseKinematic::ChainItem *FabrikInverseKinematic::ChainItem::find_child(const BoneId p_bone_id) { + for (int i = children.size() - 1; 0 <= i; --i) { + if (p_bone_id == children[i].bone) { + return &children.write[i]; + } + } + return NULL; +} + +FabrikInverseKinematic::ChainItem *FabrikInverseKinematic::ChainItem::add_child(const BoneId p_bone_id) { + const int infant_child_id = children.size(); + children.resize(infant_child_id + 1); + children.write[infant_child_id].bone = p_bone_id; + children.write[infant_child_id].parent_item = this; + return &children.write[infant_child_id]; +} + +/// Build a chain that starts from the root to tip +bool FabrikInverseKinematic::build_chain(Task *p_task, bool p_force_simple_chain) { + + ERR_FAIL_COND_V(-1 == p_task->root_bone, false); + + Chain &chain(p_task->chain); + + chain.tips.resize(p_task->end_effectors.size()); + chain.chain_root.bone = p_task->root_bone; + chain.chain_root.initial_transform = p_task->skeleton->get_bone_global_pose(chain.chain_root.bone); + chain.chain_root.current_pos = chain.chain_root.initial_transform.origin; + chain.chain_root.pb = p_task->skeleton->get_physical_bone(chain.chain_root.bone); + chain.middle_chain_item = NULL; + + // Holds all IDs that are composing a single chain in reverse order + Vector chain_ids; + // This is used to know the chain size + int sub_chain_size; + // Resize only one time in order to fit all joints for performance reason + chain_ids.resize(p_task->skeleton->get_bone_count()); + + for (int x = p_task->end_effectors.size() - 1; 0 <= x; --x) { + + const EndEffector *ee(&p_task->end_effectors[x]); + ERR_FAIL_COND_V(p_task->root_bone >= ee->tip_bone, false); + ERR_FAIL_INDEX_V(ee->tip_bone, p_task->skeleton->get_bone_count(), false); + + sub_chain_size = 0; + // Picks all IDs that composing a single chain in reverse order (except the root) + BoneId chain_sub_tip(ee->tip_bone); + while (chain_sub_tip > p_task->root_bone) { + + chain_ids.write[sub_chain_size++] = chain_sub_tip; + chain_sub_tip = p_task->skeleton->get_bone_parent(chain_sub_tip); + } + + BoneId middle_chain_item_id = (((float)sub_chain_size) * 0.5); + + // Build chain by reading chain ids in reverse order + // For each chain item id will be created a ChainItem if doesn't exists + ChainItem *sub_chain(&chain.chain_root); + for (int i = sub_chain_size - 1; 0 <= i; --i) { + + ChainItem *child_ci(sub_chain->find_child(chain_ids[i])); + if (!child_ci) { + + child_ci = sub_chain->add_child(chain_ids[i]); + + child_ci->pb = p_task->skeleton->get_physical_bone(child_ci->bone); + + child_ci->initial_transform = p_task->skeleton->get_bone_global_pose(child_ci->bone); + child_ci->current_pos = child_ci->initial_transform.origin; + + if (child_ci->parent_item) { + child_ci->length = (child_ci->current_pos - child_ci->parent_item->current_pos).length(); + } + } + + sub_chain = child_ci; + + if (middle_chain_item_id == i) { + chain.middle_chain_item = child_ci; + } + } + + if (!middle_chain_item_id) + chain.middle_chain_item = NULL; + + // Initialize current tip + chain.tips.write[x].chain_item = sub_chain; + chain.tips.write[x].end_effector = ee; + + if (p_force_simple_chain) { + // NOTE: + // This is an "hack" that force to create only one tip per chain since the solver of multi tip (end effector) + // is not yet created. + // Remove this code when this is done + break; + } + } + return true; +} + +void FabrikInverseKinematic::update_chain(const Skeleton3D *p_sk, ChainItem *p_chain_item) { + + if (!p_chain_item) + return; + + p_chain_item->initial_transform = p_sk->get_bone_global_pose(p_chain_item->bone); + p_chain_item->current_pos = p_chain_item->initial_transform.origin; + + ChainItem *items = p_chain_item->children.ptrw(); + for (int i = 0; i < p_chain_item->children.size(); i += 1) { + update_chain(p_sk, items + i); + } +} + +void FabrikInverseKinematic::solve_simple(Task *p_task, bool p_solve_magnet) { + + real_t distance_to_goal(1e4); + real_t previous_distance_to_goal(0); + int can_solve(p_task->max_iterations); + while (distance_to_goal > p_task->min_distance && Math::abs(previous_distance_to_goal - distance_to_goal) > 0.005 && can_solve) { + previous_distance_to_goal = distance_to_goal; + --can_solve; + + solve_simple_backwards(p_task->chain, p_solve_magnet); + solve_simple_forwards(p_task->chain, p_solve_magnet); + + distance_to_goal = (p_task->chain.tips[0].chain_item->current_pos - p_task->chain.tips[0].end_effector->goal_transform.origin).length(); + } +} + +void FabrikInverseKinematic::solve_simple_backwards(Chain &r_chain, bool p_solve_magnet) { + + if (p_solve_magnet && !r_chain.middle_chain_item) { + return; + } + + Vector3 goal; + ChainItem *sub_chain_tip; + if (p_solve_magnet) { + goal = r_chain.magnet_position; + sub_chain_tip = r_chain.middle_chain_item; + } else { + goal = r_chain.tips[0].end_effector->goal_transform.origin; + sub_chain_tip = r_chain.tips[0].chain_item; + } + + while (sub_chain_tip) { + sub_chain_tip->current_pos = goal; + + if (sub_chain_tip->parent_item) { + // Not yet in the chain root + // So calculate next goal location + + const Vector3 look_parent((sub_chain_tip->parent_item->current_pos - sub_chain_tip->current_pos).normalized()); + goal = sub_chain_tip->current_pos + (look_parent * sub_chain_tip->length); + + // [TODO] Constraints goes here + } + + sub_chain_tip = sub_chain_tip->parent_item; + } +} + +void FabrikInverseKinematic::solve_simple_forwards(Chain &r_chain, bool p_solve_magnet) { + + if (p_solve_magnet && !r_chain.middle_chain_item) { + return; + } + + ChainItem *sub_chain_root(&r_chain.chain_root); + Vector3 origin(r_chain.chain_root.initial_transform.origin); + + while (sub_chain_root) { // Reach the tip + sub_chain_root->current_pos = origin; + + if (!sub_chain_root->children.empty()) { + + ChainItem &child(sub_chain_root->children.write[0]); + + // Is not tip + // So calculate next origin location + + // Look child + sub_chain_root->current_ori = (child.current_pos - sub_chain_root->current_pos).normalized(); + origin = sub_chain_root->current_pos + (sub_chain_root->current_ori * child.length); + + // [TODO] Constraints goes here + + if (p_solve_magnet && sub_chain_root == r_chain.middle_chain_item) { + // In case of magnet solving this is the tip + sub_chain_root = NULL; + } else { + sub_chain_root = &child; + } + } else { + + // Is tip + sub_chain_root = NULL; + } + } +} + +FabrikInverseKinematic::Task *FabrikInverseKinematic::create_simple_task(Skeleton3D *p_sk, BoneId root_bone, BoneId tip_bone, const Transform &goal_transform) { + + FabrikInverseKinematic::EndEffector ee; + ee.tip_bone = tip_bone; + + Task *task(memnew(Task)); + task->skeleton = p_sk; + task->root_bone = root_bone; + task->end_effectors.push_back(ee); + task->goal_global_transform = goal_transform; + + if (!build_chain(task)) { + free_task(task); + return NULL; + } + + return task; +} + +void FabrikInverseKinematic::free_task(Task *p_task) { + if (p_task) + memdelete(p_task); +} + +void FabrikInverseKinematic::set_goal(Task *p_task, const Transform &p_goal) { + p_task->goal_global_transform = p_goal; +} + +void FabrikInverseKinematic::make_goal(Task *p_task, const Transform &p_inverse_transf, real_t blending_delta) { + + if (blending_delta >= 0.99f) { + // Update the end_effector (local transform) without blending + p_task->end_effectors.write[0].goal_transform = p_inverse_transf * p_task->goal_global_transform; + } else { + + // End effector in local transform + const Transform end_effector_pose(p_task->skeleton->get_bone_global_pose(p_task->end_effectors.write[0].tip_bone)); + + // Update the end_effector (local transform) by blending with current pose + p_task->end_effectors.write[0].goal_transform = end_effector_pose.interpolate_with(p_inverse_transf * p_task->goal_global_transform, blending_delta); + } +} + +void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool override_tip_basis, bool p_use_magnet, const Vector3 &p_magnet_position) { + + if (blending_delta <= 0.01f) { + return; // Skip solving + } + + p_task->skeleton->clear_bones_global_pose_override(); + + make_goal(p_task, p_task->skeleton->get_global_transform().affine_inverse().scaled(p_task->skeleton->get_global_transform().get_basis().get_scale()), blending_delta); + + update_chain(p_task->skeleton, &p_task->chain.chain_root); + + if (p_use_magnet && p_task->chain.middle_chain_item) { + p_task->chain.magnet_position = p_task->chain.middle_chain_item->initial_transform.origin.linear_interpolate(p_magnet_position, blending_delta); + solve_simple(p_task, true); + } + solve_simple(p_task, false); + + // Assign new bone position. + ChainItem *ci(&p_task->chain.chain_root); + while (ci) { + Transform new_bone_pose(ci->initial_transform); + new_bone_pose.origin = ci->current_pos; + + if (!ci->children.empty()) { + + /// Rotate basis + const Vector3 initial_ori((ci->children[0].initial_transform.origin - ci->initial_transform.origin).normalized()); + const Vector3 rot_axis(initial_ori.cross(ci->current_ori).normalized()); + + if (rot_axis[0] != 0 && rot_axis[1] != 0 && rot_axis[2] != 0) { + const real_t rot_angle(Math::acos(CLAMP(initial_ori.dot(ci->current_ori), -1, 1))); + new_bone_pose.basis.rotate(rot_axis, rot_angle); + } + } else { + // Set target orientation to tip + if (override_tip_basis) + new_bone_pose.basis = p_task->chain.tips[0].end_effector->goal_transform.basis; + else + new_bone_pose.basis = new_bone_pose.basis * p_task->chain.tips[0].end_effector->goal_transform.basis; + } + + p_task->skeleton->set_bone_global_pose_override(ci->bone, new_bone_pose, 1.0, true); + + if (!ci->children.empty()) + ci = &ci->children.write[0]; + else + ci = NULL; + } +} + +void SkeletonIK3D::_validate_property(PropertyInfo &property) const { + + if (property.name == "root_bone" || property.name == "tip_bone") { + + if (skeleton) { + + String names("--,"); + for (int i = 0; i < skeleton->get_bone_count(); i++) { + if (i > 0) + names += ","; + names += skeleton->get_bone_name(i); + } + + property.hint = PROPERTY_HINT_ENUM; + property.hint_string = names; + } else { + + property.hint = PROPERTY_HINT_NONE; + property.hint_string = ""; + } + } +} + +void SkeletonIK3D::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_root_bone", "root_bone"), &SkeletonIK3D::set_root_bone); + ClassDB::bind_method(D_METHOD("get_root_bone"), &SkeletonIK3D::get_root_bone); + + ClassDB::bind_method(D_METHOD("set_tip_bone", "tip_bone"), &SkeletonIK3D::set_tip_bone); + ClassDB::bind_method(D_METHOD("get_tip_bone"), &SkeletonIK3D::get_tip_bone); + + ClassDB::bind_method(D_METHOD("set_interpolation", "interpolation"), &SkeletonIK3D::set_interpolation); + ClassDB::bind_method(D_METHOD("get_interpolation"), &SkeletonIK3D::get_interpolation); + + ClassDB::bind_method(D_METHOD("set_target_transform", "target"), &SkeletonIK3D::set_target_transform); + ClassDB::bind_method(D_METHOD("get_target_transform"), &SkeletonIK3D::get_target_transform); + + ClassDB::bind_method(D_METHOD("set_target_node", "node"), &SkeletonIK3D::set_target_node); + ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonIK3D::get_target_node); + + ClassDB::bind_method(D_METHOD("set_override_tip_basis", "override"), &SkeletonIK3D::set_override_tip_basis); + ClassDB::bind_method(D_METHOD("is_override_tip_basis"), &SkeletonIK3D::is_override_tip_basis); + + ClassDB::bind_method(D_METHOD("set_use_magnet", "use"), &SkeletonIK3D::set_use_magnet); + ClassDB::bind_method(D_METHOD("is_using_magnet"), &SkeletonIK3D::is_using_magnet); + + ClassDB::bind_method(D_METHOD("set_magnet_position", "local_position"), &SkeletonIK3D::set_magnet_position); + ClassDB::bind_method(D_METHOD("get_magnet_position"), &SkeletonIK3D::get_magnet_position); + + ClassDB::bind_method(D_METHOD("get_parent_skeleton"), &SkeletonIK3D::get_parent_skeleton); + ClassDB::bind_method(D_METHOD("is_running"), &SkeletonIK3D::is_running); + + ClassDB::bind_method(D_METHOD("set_min_distance", "min_distance"), &SkeletonIK3D::set_min_distance); + ClassDB::bind_method(D_METHOD("get_min_distance"), &SkeletonIK3D::get_min_distance); + + ClassDB::bind_method(D_METHOD("set_max_iterations", "iterations"), &SkeletonIK3D::set_max_iterations); + ClassDB::bind_method(D_METHOD("get_max_iterations"), &SkeletonIK3D::get_max_iterations); + + ClassDB::bind_method(D_METHOD("start", "one_time"), &SkeletonIK3D::start, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("stop"), &SkeletonIK3D::stop); + + ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "root_bone"), "set_root_bone", "get_root_bone"); + ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "tip_bone"), "set_tip_bone", "get_tip_bone"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "interpolation", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_interpolation", "get_interpolation"); + ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM, "target"), "set_target_transform", "get_target_transform"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_tip_basis"), "set_override_tip_basis", "is_override_tip_basis"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_magnet"), "set_use_magnet", "is_using_magnet"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "magnet"), "set_magnet_position", "get_magnet_position"); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_node"), "set_target_node", "get_target_node"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "min_distance"), "set_min_distance", "get_min_distance"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "max_iterations"), "set_max_iterations", "get_max_iterations"); +} + +void SkeletonIK3D::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + skeleton = Object::cast_to(get_parent()); + set_process_priority(1); + reload_chain(); + } break; + case NOTIFICATION_INTERNAL_PROCESS: { + + if (target_node_override) + reload_goal(); + + _solve_chain(); + + } break; + case NOTIFICATION_EXIT_TREE: { + reload_chain(); + } break; + } +} + +SkeletonIK3D::SkeletonIK3D() : + interpolation(1), + override_tip_basis(true), + use_magnet(false), + min_distance(0.01), + max_iterations(10), + skeleton(NULL), + target_node_override(NULL), + task(NULL) { +} + +SkeletonIK3D::~SkeletonIK3D() { + FabrikInverseKinematic::free_task(task); + task = NULL; +} + +void SkeletonIK3D::set_root_bone(const StringName &p_root_bone) { + root_bone = p_root_bone; + reload_chain(); +} + +StringName SkeletonIK3D::get_root_bone() const { + return root_bone; +} + +void SkeletonIK3D::set_tip_bone(const StringName &p_tip_bone) { + tip_bone = p_tip_bone; + reload_chain(); +} + +StringName SkeletonIK3D::get_tip_bone() const { + return tip_bone; +} + +void SkeletonIK3D::set_interpolation(real_t p_interpolation) { + interpolation = p_interpolation; +} + +real_t SkeletonIK3D::get_interpolation() const { + return interpolation; +} + +void SkeletonIK3D::set_target_transform(const Transform &p_target) { + target = p_target; + reload_goal(); +} + +const Transform &SkeletonIK3D::get_target_transform() const { + return target; +} + +void SkeletonIK3D::set_target_node(const NodePath &p_node) { + target_node_path_override = p_node; + target_node_override = NULL; + reload_goal(); +} + +NodePath SkeletonIK3D::get_target_node() { + return target_node_path_override; +} + +void SkeletonIK3D::set_override_tip_basis(bool p_override) { + override_tip_basis = p_override; +} + +bool SkeletonIK3D::is_override_tip_basis() const { + return override_tip_basis; +} + +void SkeletonIK3D::set_use_magnet(bool p_use) { + use_magnet = p_use; +} + +bool SkeletonIK3D::is_using_magnet() const { + return use_magnet; +} + +void SkeletonIK3D::set_magnet_position(const Vector3 &p_local_position) { + magnet_position = p_local_position; +} + +const Vector3 &SkeletonIK3D::get_magnet_position() const { + return magnet_position; +} + +void SkeletonIK3D::set_min_distance(real_t p_min_distance) { + min_distance = p_min_distance; +} + +void SkeletonIK3D::set_max_iterations(int p_iterations) { + max_iterations = p_iterations; +} + +bool SkeletonIK3D::is_running() { + return is_processing_internal(); +} + +void SkeletonIK3D::start(bool p_one_time) { + if (p_one_time) { + set_process_internal(false); + _solve_chain(); + } else { + set_process_internal(true); + } +} + +void SkeletonIK3D::stop() { + set_process_internal(false); +} + +Transform SkeletonIK3D::_get_target_transform() { + + if (!target_node_override && !target_node_path_override.is_empty()) + target_node_override = Object::cast_to(get_node(target_node_path_override)); + + if (target_node_override) + return target_node_override->get_global_transform(); + else + return target; +} + +void SkeletonIK3D::reload_chain() { + + FabrikInverseKinematic::free_task(task); + task = NULL; + + if (!skeleton) + return; + + task = FabrikInverseKinematic::create_simple_task(skeleton, skeleton->find_bone(root_bone), skeleton->find_bone(tip_bone), _get_target_transform()); + if (task) { + task->max_iterations = max_iterations; + task->min_distance = min_distance; + } +} + +void SkeletonIK3D::reload_goal() { + if (!task) + return; + + FabrikInverseKinematic::set_goal(task, _get_target_transform()); +} + +void SkeletonIK3D::_solve_chain() { + if (!task) + return; + FabrikInverseKinematic::solve(task, interpolation, override_tip_basis, use_magnet, magnet_position); +} + +#endif // _3D_DISABLED diff --git a/scene/3d/skeleton_ik_3d.h b/scene/3d/skeleton_ik_3d.h new file mode 100644 index 0000000000..ebfebd1e66 --- /dev/null +++ b/scene/3d/skeleton_ik_3d.h @@ -0,0 +1,220 @@ +/*************************************************************************/ +/* skeleton_ik_3d.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SKELETON_IK_H +#define SKELETON_IK_H + +#ifndef _3D_DISABLED + +/** + * @author AndreaCatania + */ + +#include "core/math/transform.h" +#include "scene/3d/skeleton_3d.h" + +class FabrikInverseKinematic { + + struct EndEffector { + BoneId tip_bone; + Transform goal_transform; + }; + + struct ChainItem { + + Vector children; + ChainItem *parent_item; + + // Bone info + BoneId bone; + PhysicalBone3D *pb; + + real_t length; + /// Positions relative to root bone + Transform initial_transform; + Vector3 current_pos; + // Direction from this bone to child + Vector3 current_ori; + + ChainItem() : + parent_item(NULL), + bone(-1), + pb(NULL), + length(0) {} + + ChainItem *find_child(const BoneId p_bone_id); + ChainItem *add_child(const BoneId p_bone_id); + }; + + struct ChainTip { + ChainItem *chain_item; + const EndEffector *end_effector; + + ChainTip() : + chain_item(NULL), + end_effector(NULL) {} + + ChainTip(ChainItem *p_chain_item, const EndEffector *p_end_effector) : + chain_item(p_chain_item), + end_effector(p_end_effector) {} + + ChainTip(const ChainTip &p_other_ct) : + chain_item(p_other_ct.chain_item), + end_effector(p_other_ct.end_effector) {} + }; + + struct Chain { + ChainItem chain_root; + ChainItem *middle_chain_item; + Vector tips; + Vector3 magnet_position; + }; + +public: + struct Task { + RID self; + Skeleton3D *skeleton; + + Chain chain; + + // Settings + real_t min_distance; + int max_iterations; + + // Bone data + BoneId root_bone; + Vector end_effectors; + + Transform goal_global_transform; + + Task() : + skeleton(NULL), + min_distance(0.01), + max_iterations(10), + root_bone(-1) {} + }; + +private: + /// Init a chain that starts from the root to tip + static bool build_chain(Task *p_task, bool p_force_simple_chain = true); + + static void update_chain(const Skeleton3D *p_sk, ChainItem *p_chain_item); + + static void solve_simple(Task *p_task, bool p_solve_magnet); + /// Special solvers that solve only chains with one end effector + static void solve_simple_backwards(Chain &r_chain, bool p_solve_magnet); + static void solve_simple_forwards(Chain &r_chain, bool p_solve_magnet); + +public: + static Task *create_simple_task(Skeleton3D *p_sk, BoneId root_bone, BoneId tip_bone, const Transform &goal_transform); + static void free_task(Task *p_task); + // The goal of chain should be always in local space + static void set_goal(Task *p_task, const Transform &p_goal); + static void make_goal(Task *p_task, const Transform &p_inverse_transf, real_t blending_delta); + static void solve(Task *p_task, real_t blending_delta, bool override_tip_basis, bool p_use_magnet, const Vector3 &p_magnet_position); +}; + +class SkeletonIK3D : public Node { + GDCLASS(SkeletonIK3D, Node); + + StringName root_bone; + StringName tip_bone; + real_t interpolation; + Transform target; + NodePath target_node_path_override; + bool override_tip_basis; + bool use_magnet; + Vector3 magnet_position; + + real_t min_distance; + int max_iterations; + + Skeleton3D *skeleton; + Node3D *target_node_override; + FabrikInverseKinematic::Task *task; + +protected: + virtual void + _validate_property(PropertyInfo &property) const; + + static void _bind_methods(); + virtual void _notification(int p_what); + +public: + SkeletonIK3D(); + virtual ~SkeletonIK3D(); + + void set_root_bone(const StringName &p_root_bone); + StringName get_root_bone() const; + + void set_tip_bone(const StringName &p_tip_bone); + StringName get_tip_bone() const; + + void set_interpolation(real_t p_interpolation); + real_t get_interpolation() const; + + void set_target_transform(const Transform &p_target); + const Transform &get_target_transform() const; + + void set_target_node(const NodePath &p_node); + NodePath get_target_node(); + + void set_override_tip_basis(bool p_override); + bool is_override_tip_basis() const; + + void set_use_magnet(bool p_use); + bool is_using_magnet() const; + + void set_magnet_position(const Vector3 &p_local_position); + const Vector3 &get_magnet_position() const; + + void set_min_distance(real_t p_min_distance); + real_t get_min_distance() const { return min_distance; } + + void set_max_iterations(int p_iterations); + int get_max_iterations() const { return max_iterations; } + + Skeleton3D *get_parent_skeleton() const { return skeleton; } + + bool is_running(); + + void start(bool p_one_time = false); + void stop(); + +private: + Transform _get_target_transform(); + void reload_chain(); + void reload_goal(); + void _solve_chain(); +}; + +#endif // _3D_DISABLED + +#endif // SKELETON_IK_H diff --git a/scene/3d/vehicle_body.cpp b/scene/3d/vehicle_body.cpp deleted file mode 100644 index 5bef2a14f5..0000000000 --- a/scene/3d/vehicle_body.cpp +++ /dev/null @@ -1,998 +0,0 @@ -/*************************************************************************/ -/* vehicle_body.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "vehicle_body.h" - -#define ROLLING_INFLUENCE_FIX - -class btVehicleJacobianEntry { -public: - Vector3 m_linearJointAxis; - Vector3 m_aJ; - Vector3 m_bJ; - Vector3 m_0MinvJt; - Vector3 m_1MinvJt; - //Optimization: can be stored in the w/last component of one of the vectors - real_t m_Adiag; - - real_t getDiagonal() const { return m_Adiag; } - - btVehicleJacobianEntry(){}; - //constraint between two different rigidbodies - btVehicleJacobianEntry( - const Basis &world2A, - const Basis &world2B, - const Vector3 &rel_pos1, - const Vector3 &rel_pos2, - const Vector3 &jointAxis, - const Vector3 &inertiaInvA, - const real_t massInvA, - const Vector3 &inertiaInvB, - const real_t massInvB) : - m_linearJointAxis(jointAxis) { - m_aJ = world2A.xform(rel_pos1.cross(m_linearJointAxis)); - m_bJ = world2B.xform(rel_pos2.cross(-m_linearJointAxis)); - m_0MinvJt = inertiaInvA * m_aJ; - m_1MinvJt = inertiaInvB * m_bJ; - m_Adiag = massInvA + m_0MinvJt.dot(m_aJ) + massInvB + m_1MinvJt.dot(m_bJ); - - //btAssert(m_Adiag > real_t(0.0)); - } - - real_t getRelativeVelocity(const Vector3 &linvelA, const Vector3 &angvelA, const Vector3 &linvelB, const Vector3 &angvelB) { - Vector3 linrel = linvelA - linvelB; - Vector3 angvela = angvelA * m_aJ; - Vector3 angvelb = angvelB * m_bJ; - linrel *= m_linearJointAxis; - angvela += angvelb; - angvela += linrel; - real_t rel_vel2 = angvela[0] + angvela[1] + angvela[2]; - return rel_vel2 + CMP_EPSILON; - } -}; - -void VehicleWheel::_notification(int p_what) { - - if (p_what == NOTIFICATION_ENTER_TREE) { - - VehicleBody *cb = Object::cast_to(get_parent()); - if (!cb) - return; - body = cb; - local_xform = get_transform(); - cb->wheels.push_back(this); - - m_chassisConnectionPointCS = get_transform().origin; - m_wheelDirectionCS = -get_transform().basis.get_axis(Vector3::AXIS_Y).normalized(); - m_wheelAxleCS = get_transform().basis.get_axis(Vector3::AXIS_X).normalized(); - } - if (p_what == NOTIFICATION_EXIT_TREE) { - - VehicleBody *cb = Object::cast_to(get_parent()); - if (!cb) - return; - cb->wheels.erase(this); - body = NULL; - } -} - -String VehicleWheel::get_configuration_warning() const { - if (!Object::cast_to(get_parent())) { - return TTR("VehicleWheel serves to provide a wheel system to a VehicleBody. Please use it as a child of a VehicleBody."); - } - - return String(); -} - -void VehicleWheel::_update(PhysicsDirectBodyState *s) { - - if (m_raycastInfo.m_isInContact) - - { - real_t project = m_raycastInfo.m_contactNormalWS.dot(m_raycastInfo.m_wheelDirectionWS); - Vector3 chassis_velocity_at_contactPoint; - Vector3 relpos = m_raycastInfo.m_contactPointWS - s->get_transform().origin; - - chassis_velocity_at_contactPoint = s->get_linear_velocity() + - (s->get_angular_velocity()).cross(relpos); // * mPos); - - real_t projVel = m_raycastInfo.m_contactNormalWS.dot(chassis_velocity_at_contactPoint); - if (project >= real_t(-0.1)) { - m_suspensionRelativeVelocity = real_t(0.0); - m_clippedInvContactDotSuspension = real_t(1.0) / real_t(0.1); - } else { - real_t inv = real_t(-1.) / project; - m_suspensionRelativeVelocity = projVel * inv; - m_clippedInvContactDotSuspension = inv; - } - - } - - else // Not in contact : position wheel in a nice (rest length) position - { - m_raycastInfo.m_suspensionLength = m_suspensionRestLength; - m_suspensionRelativeVelocity = real_t(0.0); - m_raycastInfo.m_contactNormalWS = -m_raycastInfo.m_wheelDirectionWS; - m_clippedInvContactDotSuspension = real_t(1.0); - } -} - -void VehicleWheel::set_radius(float p_radius) { - - m_wheelRadius = p_radius; - update_gizmo(); -} - -float VehicleWheel::get_radius() const { - - return m_wheelRadius; -} - -void VehicleWheel::set_suspension_rest_length(float p_length) { - - m_suspensionRestLength = p_length; - update_gizmo(); -} -float VehicleWheel::get_suspension_rest_length() const { - - return m_suspensionRestLength; -} - -void VehicleWheel::set_suspension_travel(float p_length) { - - m_maxSuspensionTravelCm = p_length / 0.01; -} -float VehicleWheel::get_suspension_travel() const { - - return m_maxSuspensionTravelCm * 0.01; -} - -void VehicleWheel::set_suspension_stiffness(float p_value) { - - m_suspensionStiffness = p_value; -} -float VehicleWheel::get_suspension_stiffness() const { - - return m_suspensionStiffness; -} - -void VehicleWheel::set_suspension_max_force(float p_value) { - - m_maxSuspensionForce = p_value; -} -float VehicleWheel::get_suspension_max_force() const { - - return m_maxSuspensionForce; -} - -void VehicleWheel::set_damping_compression(float p_value) { - - m_wheelsDampingCompression = p_value; -} -float VehicleWheel::get_damping_compression() const { - - return m_wheelsDampingCompression; -} - -void VehicleWheel::set_damping_relaxation(float p_value) { - - m_wheelsDampingRelaxation = p_value; -} -float VehicleWheel::get_damping_relaxation() const { - - return m_wheelsDampingRelaxation; -} - -void VehicleWheel::set_friction_slip(float p_value) { - - m_frictionSlip = p_value; -} -float VehicleWheel::get_friction_slip() const { - - return m_frictionSlip; -} - -void VehicleWheel::set_roll_influence(float p_value) { - m_rollInfluence = p_value; -} - -float VehicleWheel::get_roll_influence() const { - return m_rollInfluence; -} - -bool VehicleWheel::is_in_contact() const { - return m_raycastInfo.m_isInContact; -} - -void VehicleWheel::_bind_methods() { - - ClassDB::bind_method(D_METHOD("set_radius", "length"), &VehicleWheel::set_radius); - ClassDB::bind_method(D_METHOD("get_radius"), &VehicleWheel::get_radius); - - ClassDB::bind_method(D_METHOD("set_suspension_rest_length", "length"), &VehicleWheel::set_suspension_rest_length); - ClassDB::bind_method(D_METHOD("get_suspension_rest_length"), &VehicleWheel::get_suspension_rest_length); - - ClassDB::bind_method(D_METHOD("set_suspension_travel", "length"), &VehicleWheel::set_suspension_travel); - ClassDB::bind_method(D_METHOD("get_suspension_travel"), &VehicleWheel::get_suspension_travel); - - ClassDB::bind_method(D_METHOD("set_suspension_stiffness", "length"), &VehicleWheel::set_suspension_stiffness); - ClassDB::bind_method(D_METHOD("get_suspension_stiffness"), &VehicleWheel::get_suspension_stiffness); - - ClassDB::bind_method(D_METHOD("set_suspension_max_force", "length"), &VehicleWheel::set_suspension_max_force); - ClassDB::bind_method(D_METHOD("get_suspension_max_force"), &VehicleWheel::get_suspension_max_force); - - ClassDB::bind_method(D_METHOD("set_damping_compression", "length"), &VehicleWheel::set_damping_compression); - ClassDB::bind_method(D_METHOD("get_damping_compression"), &VehicleWheel::get_damping_compression); - - ClassDB::bind_method(D_METHOD("set_damping_relaxation", "length"), &VehicleWheel::set_damping_relaxation); - ClassDB::bind_method(D_METHOD("get_damping_relaxation"), &VehicleWheel::get_damping_relaxation); - - ClassDB::bind_method(D_METHOD("set_use_as_traction", "enable"), &VehicleWheel::set_use_as_traction); - ClassDB::bind_method(D_METHOD("is_used_as_traction"), &VehicleWheel::is_used_as_traction); - - ClassDB::bind_method(D_METHOD("set_use_as_steering", "enable"), &VehicleWheel::set_use_as_steering); - ClassDB::bind_method(D_METHOD("is_used_as_steering"), &VehicleWheel::is_used_as_steering); - - ClassDB::bind_method(D_METHOD("set_friction_slip", "length"), &VehicleWheel::set_friction_slip); - ClassDB::bind_method(D_METHOD("get_friction_slip"), &VehicleWheel::get_friction_slip); - - ClassDB::bind_method(D_METHOD("is_in_contact"), &VehicleWheel::is_in_contact); - - ClassDB::bind_method(D_METHOD("set_roll_influence", "roll_influence"), &VehicleWheel::set_roll_influence); - ClassDB::bind_method(D_METHOD("get_roll_influence"), &VehicleWheel::get_roll_influence); - - ClassDB::bind_method(D_METHOD("get_skidinfo"), &VehicleWheel::get_skidinfo); - - ClassDB::bind_method(D_METHOD("get_rpm"), &VehicleWheel::get_rpm); - - ClassDB::bind_method(D_METHOD("set_engine_force", "engine_force"), &VehicleWheel::set_engine_force); - ClassDB::bind_method(D_METHOD("get_engine_force"), &VehicleWheel::get_engine_force); - - ClassDB::bind_method(D_METHOD("set_brake", "brake"), &VehicleWheel::set_brake); - ClassDB::bind_method(D_METHOD("get_brake"), &VehicleWheel::get_brake); - - ClassDB::bind_method(D_METHOD("set_steering", "steering"), &VehicleWheel::set_steering); - ClassDB::bind_method(D_METHOD("get_steering"), &VehicleWheel::get_steering); - - ADD_GROUP("Per-Wheel Motion", ""); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "engine_force", PROPERTY_HINT_RANGE, "0.00,1024.0,0.01,or_greater"), "set_engine_force", "get_engine_force"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "brake", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_brake", "get_brake"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "steering", PROPERTY_HINT_RANGE, "-180,180.0,0.01"), "set_steering", "get_steering"); - ADD_GROUP("VehicleBody Motion", ""); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_as_traction"), "set_use_as_traction", "is_used_as_traction"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_as_steering"), "set_use_as_steering", "is_used_as_steering"); - ADD_GROUP("Wheel", "wheel_"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wheel_roll_influence"), "set_roll_influence", "get_roll_influence"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wheel_radius"), "set_radius", "get_radius"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wheel_rest_length"), "set_suspension_rest_length", "get_suspension_rest_length"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wheel_friction_slip"), "set_friction_slip", "get_friction_slip"); - ADD_GROUP("Suspension", "suspension_"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "suspension_travel"), "set_suspension_travel", "get_suspension_travel"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "suspension_stiffness"), "set_suspension_stiffness", "get_suspension_stiffness"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "suspension_max_force"), "set_suspension_max_force", "get_suspension_max_force"); - ADD_GROUP("Damping", "damping_"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "damping_compression"), "set_damping_compression", "get_damping_compression"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "damping_relaxation"), "set_damping_relaxation", "get_damping_relaxation"); -} - -void VehicleWheel::set_engine_force(float p_engine_force) { - - m_engineForce = p_engine_force; -} - -float VehicleWheel::get_engine_force() const { - - return m_engineForce; -} - -void VehicleWheel::set_brake(float p_brake) { - - m_brake = p_brake; -} -float VehicleWheel::get_brake() const { - - return m_brake; -} - -void VehicleWheel::set_steering(float p_steering) { - - m_steering = p_steering; -} -float VehicleWheel::get_steering() const { - - return m_steering; -} - -void VehicleWheel::set_use_as_traction(bool p_enable) { - - engine_traction = p_enable; -} - -bool VehicleWheel::is_used_as_traction() const { - - return engine_traction; -} - -void VehicleWheel::set_use_as_steering(bool p_enabled) { - - steers = p_enabled; -} - -bool VehicleWheel::is_used_as_steering() const { - - return steers; -} - -float VehicleWheel::get_skidinfo() const { - - return m_skidInfo; -} - -float VehicleWheel::get_rpm() const { - - return m_rpm; -} - -VehicleWheel::VehicleWheel() { - - steers = false; - engine_traction = false; - m_steering = real_t(0.); - m_engineForce = real_t(0.); - m_rotation = real_t(0.); - m_deltaRotation = real_t(0.); - m_brake = real_t(0.); - m_rollInfluence = real_t(0.1); - - m_suspensionRestLength = 0.15; - m_wheelRadius = 0.5; //0.28; - m_suspensionStiffness = 5.88; - m_wheelsDampingCompression = 0.83; - m_wheelsDampingRelaxation = 0.88; - m_frictionSlip = 10.5; - m_bIsFrontWheel = false; - m_maxSuspensionTravelCm = 500; - m_maxSuspensionForce = 6000; - - m_suspensionRelativeVelocity = 0; - m_clippedInvContactDotSuspension = 1.0; - m_raycastInfo.m_isInContact = false; - - body = NULL; -} - -void VehicleBody::_update_wheel_transform(VehicleWheel &wheel, PhysicsDirectBodyState *s) { - - wheel.m_raycastInfo.m_isInContact = false; - - Transform chassisTrans = s->get_transform(); - /* - if (interpolatedTransform && (getRigidBody()->getMotionState())) { - getRigidBody()->getMotionState()->getWorldTransform(chassisTrans); - } - */ - - wheel.m_raycastInfo.m_hardPointWS = chassisTrans.xform(wheel.m_chassisConnectionPointCS); - //wheel.m_raycastInfo.m_hardPointWS+=s->get_linear_velocity()*s->get_step(); - wheel.m_raycastInfo.m_wheelDirectionWS = chassisTrans.get_basis().xform(wheel.m_wheelDirectionCS).normalized(); - wheel.m_raycastInfo.m_wheelAxleWS = chassisTrans.get_basis().xform(wheel.m_wheelAxleCS).normalized(); -} - -void VehicleBody::_update_wheel(int p_idx, PhysicsDirectBodyState *s) { - - VehicleWheel &wheel = *wheels[p_idx]; - _update_wheel_transform(wheel, s); - - Vector3 up = -wheel.m_raycastInfo.m_wheelDirectionWS; - const Vector3 &right = wheel.m_raycastInfo.m_wheelAxleWS; - Vector3 fwd = up.cross(right); - fwd = fwd.normalized(); - - Basis steeringMat(up, wheel.m_steering); - - Basis rotatingMat(right, wheel.m_rotation); - - Basis basis2( - right[0], up[0], fwd[0], - right[1], up[1], fwd[1], - right[2], up[2], fwd[2]); - - wheel.m_worldTransform.set_basis(steeringMat * rotatingMat * basis2); - //wheel.m_worldTransform.set_basis(basis2 * (steeringMat * rotatingMat)); - wheel.m_worldTransform.set_origin( - wheel.m_raycastInfo.m_hardPointWS + wheel.m_raycastInfo.m_wheelDirectionWS * wheel.m_raycastInfo.m_suspensionLength); -} - -real_t VehicleBody::_ray_cast(int p_idx, PhysicsDirectBodyState *s) { - - VehicleWheel &wheel = *wheels[p_idx]; - - _update_wheel_transform(wheel, s); - - real_t depth = -1; - - real_t raylen = wheel.m_suspensionRestLength + wheel.m_wheelRadius; - - Vector3 rayvector = wheel.m_raycastInfo.m_wheelDirectionWS * (raylen); - Vector3 source = wheel.m_raycastInfo.m_hardPointWS; - wheel.m_raycastInfo.m_contactPointWS = source + rayvector; - const Vector3 &target = wheel.m_raycastInfo.m_contactPointWS; - source -= wheel.m_wheelRadius * wheel.m_raycastInfo.m_wheelDirectionWS; - - real_t param = real_t(0.); - - PhysicsDirectSpaceState::RayResult rr; - - PhysicsDirectSpaceState *ss = s->get_space_state(); - - bool col = ss->intersect_ray(source, target, rr, exclude); - - wheel.m_raycastInfo.m_groundObject = 0; - - if (col) { - param = source.distance_to(rr.position) / source.distance_to(target); - depth = raylen * param; - wheel.m_raycastInfo.m_contactNormalWS = rr.normal; - - wheel.m_raycastInfo.m_isInContact = true; - if (rr.collider) - wheel.m_raycastInfo.m_groundObject = Object::cast_to(rr.collider); - - real_t hitDistance = param * raylen; - wheel.m_raycastInfo.m_suspensionLength = hitDistance - wheel.m_wheelRadius; - //clamp on max suspension travel - - real_t minSuspensionLength = wheel.m_suspensionRestLength - wheel.m_maxSuspensionTravelCm * real_t(0.01); - real_t maxSuspensionLength = wheel.m_suspensionRestLength + wheel.m_maxSuspensionTravelCm * real_t(0.01); - if (wheel.m_raycastInfo.m_suspensionLength < minSuspensionLength) { - wheel.m_raycastInfo.m_suspensionLength = minSuspensionLength; - } - if (wheel.m_raycastInfo.m_suspensionLength > maxSuspensionLength) { - wheel.m_raycastInfo.m_suspensionLength = maxSuspensionLength; - } - - wheel.m_raycastInfo.m_contactPointWS = rr.position; - - real_t denominator = wheel.m_raycastInfo.m_contactNormalWS.dot(wheel.m_raycastInfo.m_wheelDirectionWS); - - Vector3 chassis_velocity_at_contactPoint; - //Vector3 relpos = wheel.m_raycastInfo.m_contactPointWS-getRigidBody()->getCenterOfMassPosition(); - - //chassis_velocity_at_contactPoint = getRigidBody()->getVelocityInLocalPoint(relpos); - - chassis_velocity_at_contactPoint = s->get_linear_velocity() + - (s->get_angular_velocity()).cross(wheel.m_raycastInfo.m_contactPointWS - s->get_transform().origin); // * mPos); - - real_t projVel = wheel.m_raycastInfo.m_contactNormalWS.dot(chassis_velocity_at_contactPoint); - - if (denominator >= real_t(-0.1)) { - wheel.m_suspensionRelativeVelocity = real_t(0.0); - wheel.m_clippedInvContactDotSuspension = real_t(1.0) / real_t(0.1); - } else { - real_t inv = real_t(-1.) / denominator; - wheel.m_suspensionRelativeVelocity = projVel * inv; - wheel.m_clippedInvContactDotSuspension = inv; - } - - } else { - wheel.m_raycastInfo.m_isInContact = false; - //put wheel info as in rest position - wheel.m_raycastInfo.m_suspensionLength = wheel.m_suspensionRestLength; - wheel.m_suspensionRelativeVelocity = real_t(0.0); - wheel.m_raycastInfo.m_contactNormalWS = -wheel.m_raycastInfo.m_wheelDirectionWS; - wheel.m_clippedInvContactDotSuspension = real_t(1.0); - } - - return depth; -} - -void VehicleBody::_update_suspension(PhysicsDirectBodyState *s) { - - real_t chassisMass = mass; - - for (int w_it = 0; w_it < wheels.size(); w_it++) { - VehicleWheel &wheel_info = *wheels[w_it]; - - if (wheel_info.m_raycastInfo.m_isInContact) { - real_t force; - //Spring - { - real_t susp_length = wheel_info.m_suspensionRestLength; - real_t current_length = wheel_info.m_raycastInfo.m_suspensionLength; - - real_t length_diff = (susp_length - current_length); - - force = wheel_info.m_suspensionStiffness * length_diff * wheel_info.m_clippedInvContactDotSuspension; - } - - // Damper - { - real_t projected_rel_vel = wheel_info.m_suspensionRelativeVelocity; - { - real_t susp_damping; - if (projected_rel_vel < real_t(0.0)) { - susp_damping = wheel_info.m_wheelsDampingCompression; - } else { - susp_damping = wheel_info.m_wheelsDampingRelaxation; - } - force -= susp_damping * projected_rel_vel; - } - } - - // RESULT - wheel_info.m_wheelsSuspensionForce = force * chassisMass; - if (wheel_info.m_wheelsSuspensionForce < real_t(0.)) { - wheel_info.m_wheelsSuspensionForce = real_t(0.); - } - } else { - wheel_info.m_wheelsSuspensionForce = real_t(0.0); - } - } -} - -//bilateral constraint between two dynamic objects -void VehicleBody::_resolve_single_bilateral(PhysicsDirectBodyState *s, const Vector3 &pos1, - PhysicsBody3D *body2, const Vector3 &pos2, const Vector3 &normal, real_t &impulse, const real_t p_rollInfluence) { - - real_t normalLenSqr = normal.length_squared(); - //ERR_FAIL_COND( normalLenSqr < real_t(1.1)); - - if (normalLenSqr > real_t(1.1)) { - impulse = real_t(0.); - return; - } - - Vector3 rel_pos1 = pos1 - s->get_transform().origin; - Vector3 rel_pos2; - if (body2) - rel_pos2 = pos2 - body2->get_global_transform().origin; - //this jacobian entry could be re-used for all iterations - - Vector3 vel1 = s->get_linear_velocity() + (s->get_angular_velocity()).cross(rel_pos1); // * mPos); - Vector3 vel2; - - if (body2) - vel2 = body2->get_linear_velocity() + body2->get_angular_velocity().cross(rel_pos2); - - Vector3 vel = vel1 - vel2; - - Basis b2trans; - float b2invmass = 0; - Vector3 b2lv; - Vector3 b2av; - Vector3 b2invinertia; //todo - - if (body2) { - b2trans = body2->get_global_transform().basis.transposed(); - b2invmass = body2->get_inverse_mass(); - b2lv = body2->get_linear_velocity(); - b2av = body2->get_angular_velocity(); - } - - btVehicleJacobianEntry jac(s->get_transform().basis.transposed(), - b2trans, - rel_pos1, - rel_pos2, - normal, - s->get_inverse_inertia_tensor().get_main_diagonal(), - 1.0 / mass, - b2invinertia, - b2invmass); - - // FIXME: rel_vel assignment here is overwritten by the following assignment. - // What seems to be intended in the next next assignment is: rel_vel = normal.dot(rel_vel); - // Investigate why. - real_t rel_vel = jac.getRelativeVelocity( - s->get_linear_velocity(), - s->get_transform().basis.transposed().xform(s->get_angular_velocity()), - b2lv, - b2trans.xform(b2av)); - - rel_vel = normal.dot(vel); - - // !BAS! We had this set to 0.4, in bullet its 0.2 - real_t contactDamping = real_t(0.2); - - if (p_rollInfluence > 0.0) { - // !BAS! But seeing we apply this frame by frame, makes more sense to me to make this time based - // keeping in mind our anti roll factor if it is set - contactDamping = MIN(contactDamping, s->get_step() / p_rollInfluence); - } - -#define ONLY_USE_LINEAR_MASS -#ifdef ONLY_USE_LINEAR_MASS - real_t massTerm = real_t(1.) / ((1.0 / mass) + b2invmass); - impulse = -contactDamping * rel_vel * massTerm; -#else - real_t velocityImpulse = -contactDamping * rel_vel * jacDiagABInv; - impulse = velocityImpulse; -#endif -} - -VehicleBody::btVehicleWheelContactPoint::btVehicleWheelContactPoint(PhysicsDirectBodyState *s, PhysicsBody3D *body1, const Vector3 &frictionPosWorld, const Vector3 &frictionDirectionWorld, real_t maxImpulse) : - m_s(s), - m_body1(body1), - m_frictionPositionWorld(frictionPosWorld), - m_frictionDirectionWorld(frictionDirectionWorld), - m_maxImpulse(maxImpulse) { - float denom0 = 0; - float denom1 = 0; - - { - Vector3 r0 = frictionPosWorld - s->get_transform().origin; - Vector3 c0 = (r0).cross(frictionDirectionWorld); - Vector3 vec = s->get_inverse_inertia_tensor().xform_inv(c0).cross(r0); - denom0 = s->get_inverse_mass() + frictionDirectionWorld.dot(vec); - } - - /* TODO: Why is this code unused? - if (body1) { - - Vector3 r0 = frictionPosWorld - body1->get_global_transform().origin; - Vector3 c0 = (r0).cross(frictionDirectionWorld); - Vector3 vec = s->get_inverse_inertia_tensor().xform_inv(c0).cross(r0); - //denom1= body1->get_inverse_mass() + frictionDirectionWorld.dot(vec); - - } - */ - - real_t relaxation = 1.f; - m_jacDiagABInv = relaxation / (denom0 + denom1); -} - -real_t VehicleBody::_calc_rolling_friction(btVehicleWheelContactPoint &contactPoint) { - - real_t j1 = 0.f; - - const Vector3 &contactPosWorld = contactPoint.m_frictionPositionWorld; - - Vector3 rel_pos1 = contactPosWorld - contactPoint.m_s->get_transform().origin; - Vector3 rel_pos2; - if (contactPoint.m_body1) - rel_pos2 = contactPosWorld - contactPoint.m_body1->get_global_transform().origin; - - real_t maxImpulse = contactPoint.m_maxImpulse; - - Vector3 vel1 = contactPoint.m_s->get_linear_velocity() + (contactPoint.m_s->get_angular_velocity()).cross(rel_pos1); // * mPos); - - Vector3 vel2; - if (contactPoint.m_body1) { - vel2 = contactPoint.m_body1->get_linear_velocity() + contactPoint.m_body1->get_angular_velocity().cross(rel_pos2); - } - - Vector3 vel = vel1 - vel2; - - real_t vrel = contactPoint.m_frictionDirectionWorld.dot(vel); - - // calculate j that moves us to zero relative velocity - j1 = -vrel * contactPoint.m_jacDiagABInv; - - return CLAMP(j1, -maxImpulse, maxImpulse); -} - -static const real_t sideFrictionStiffness2 = real_t(1.0); -void VehicleBody::_update_friction(PhysicsDirectBodyState *s) { - - //calculate the impulse, so that the wheels don't move sidewards - int numWheel = wheels.size(); - if (!numWheel) - return; - - m_forwardWS.resize(numWheel); - m_axle.resize(numWheel); - m_forwardImpulse.resize(numWheel); - m_sideImpulse.resize(numWheel); - - //collapse all those loops into one! - for (int i = 0; i < wheels.size(); i++) { - m_sideImpulse.write[i] = real_t(0.); - m_forwardImpulse.write[i] = real_t(0.); - } - - { - - for (int i = 0; i < wheels.size(); i++) { - - VehicleWheel &wheelInfo = *wheels[i]; - - if (wheelInfo.m_raycastInfo.m_isInContact) { - - //const btTransform& wheelTrans = getWheelTransformWS( i ); - - Basis wheelBasis0 = wheelInfo.m_worldTransform.basis; //get_global_transform().basis; - - m_axle.write[i] = wheelBasis0.get_axis(Vector3::AXIS_X); - //m_axle[i] = wheelInfo.m_raycastInfo.m_wheelAxleWS; - - const Vector3 &surfNormalWS = wheelInfo.m_raycastInfo.m_contactNormalWS; - real_t proj = m_axle[i].dot(surfNormalWS); - m_axle.write[i] -= surfNormalWS * proj; - m_axle.write[i] = m_axle[i].normalized(); - - m_forwardWS.write[i] = surfNormalWS.cross(m_axle[i]); - m_forwardWS.write[i].normalize(); - - _resolve_single_bilateral(s, wheelInfo.m_raycastInfo.m_contactPointWS, - wheelInfo.m_raycastInfo.m_groundObject, wheelInfo.m_raycastInfo.m_contactPointWS, - m_axle[i], m_sideImpulse.write[i], wheelInfo.m_rollInfluence); - - m_sideImpulse.write[i] *= sideFrictionStiffness2; - } - } - } - - real_t sideFactor = real_t(1.); - real_t fwdFactor = 0.5; - - bool sliding = false; - { - for (int wheel = 0; wheel < wheels.size(); wheel++) { - VehicleWheel &wheelInfo = *wheels[wheel]; - - //class btRigidBody* groundObject = (class btRigidBody*) wheelInfo.m_raycastInfo.m_groundObject; - - real_t rollingFriction = 0.f; - - if (wheelInfo.m_raycastInfo.m_isInContact) { - if (wheelInfo.m_engineForce != 0.f) { - rollingFriction = -wheelInfo.m_engineForce * s->get_step(); - } else { - real_t defaultRollingFrictionImpulse = 0.f; - real_t maxImpulse = wheelInfo.m_brake ? wheelInfo.m_brake : defaultRollingFrictionImpulse; - btVehicleWheelContactPoint contactPt(s, wheelInfo.m_raycastInfo.m_groundObject, wheelInfo.m_raycastInfo.m_contactPointWS, m_forwardWS[wheel], maxImpulse); - rollingFriction = _calc_rolling_friction(contactPt); - } - } - - //switch between active rolling (throttle), braking and non-active rolling friction (no throttle/break) - - m_forwardImpulse.write[wheel] = real_t(0.); - wheelInfo.m_skidInfo = real_t(1.); - - if (wheelInfo.m_raycastInfo.m_isInContact) { - wheelInfo.m_skidInfo = real_t(1.); - - real_t maximp = wheelInfo.m_wheelsSuspensionForce * s->get_step() * wheelInfo.m_frictionSlip; - real_t maximpSide = maximp; - - real_t maximpSquared = maximp * maximpSide; - - m_forwardImpulse.write[wheel] = rollingFriction; //wheelInfo.m_engineForce* timeStep; - - real_t x = (m_forwardImpulse[wheel]) * fwdFactor; - real_t y = (m_sideImpulse[wheel]) * sideFactor; - - real_t impulseSquared = (x * x + y * y); - - if (impulseSquared > maximpSquared) { - sliding = true; - - real_t factor = maximp / Math::sqrt(impulseSquared); - - wheelInfo.m_skidInfo *= factor; - } - } - } - } - - if (sliding) { - for (int wheel = 0; wheel < wheels.size(); wheel++) { - if (m_sideImpulse[wheel] != real_t(0.)) { - if (wheels[wheel]->m_skidInfo < real_t(1.)) { - m_forwardImpulse.write[wheel] *= wheels[wheel]->m_skidInfo; - m_sideImpulse.write[wheel] *= wheels[wheel]->m_skidInfo; - } - } - } - } - - // apply the impulses - { - for (int wheel = 0; wheel < wheels.size(); wheel++) { - VehicleWheel &wheelInfo = *wheels[wheel]; - - Vector3 rel_pos = wheelInfo.m_raycastInfo.m_contactPointWS - - s->get_transform().origin; - - if (m_forwardImpulse[wheel] != real_t(0.)) { - s->apply_impulse(rel_pos, m_forwardWS[wheel] * (m_forwardImpulse[wheel])); - } - if (m_sideImpulse[wheel] != real_t(0.)) { - PhysicsBody3D *groundObject = wheelInfo.m_raycastInfo.m_groundObject; - - Vector3 rel_pos2; - if (groundObject) { - rel_pos2 = wheelInfo.m_raycastInfo.m_contactPointWS - groundObject->get_global_transform().origin; - } - - Vector3 sideImp = m_axle[wheel] * m_sideImpulse[wheel]; - -#if defined ROLLING_INFLUENCE_FIX // fix. It only worked if car's up was along Y - VT. - Vector3 vChassisWorldUp = s->get_transform().basis.transposed()[1]; //getRigidBody()->getCenterOfMassTransform().getBasis().getColumn(m_indexUpAxis); - rel_pos -= vChassisWorldUp * (vChassisWorldUp.dot(rel_pos) * (1.f - wheelInfo.m_rollInfluence)); -#else - rel_pos[1] *= wheelInfo.m_rollInfluence; //? -#endif - s->apply_impulse(rel_pos, sideImp); - - //apply friction impulse on the ground - //todo - //groundObject->applyImpulse(-sideImp,rel_pos2); - } - } - } -} - -void VehicleBody::_direct_state_changed(Object *p_state) { - - RigidBody3D::_direct_state_changed(p_state); - - state = Object::cast_to(p_state); - - float step = state->get_step(); - - for (int i = 0; i < wheels.size(); i++) { - - _update_wheel(i, state); - } - - for (int i = 0; i < wheels.size(); i++) { - - _ray_cast(i, state); - wheels[i]->set_transform(state->get_transform().inverse() * wheels[i]->m_worldTransform); - } - - _update_suspension(state); - - for (int i = 0; i < wheels.size(); i++) { - - //apply suspension force - VehicleWheel &wheel = *wheels[i]; - - real_t suspensionForce = wheel.m_wheelsSuspensionForce; - - if (suspensionForce > wheel.m_maxSuspensionForce) { - suspensionForce = wheel.m_maxSuspensionForce; - } - Vector3 impulse = wheel.m_raycastInfo.m_contactNormalWS * suspensionForce * step; - Vector3 relpos = wheel.m_raycastInfo.m_contactPointWS - state->get_transform().origin; - - state->apply_impulse(relpos, impulse); - //getRigidBody()->applyImpulse(impulse, relpos); - } - - _update_friction(state); - - for (int i = 0; i < wheels.size(); i++) { - VehicleWheel &wheel = *wheels[i]; - Vector3 relpos = wheel.m_raycastInfo.m_hardPointWS - state->get_transform().origin; - Vector3 vel = state->get_linear_velocity() + (state->get_angular_velocity()).cross(relpos); // * mPos); - - if (wheel.m_raycastInfo.m_isInContact) { - const Transform &chassisWorldTransform = state->get_transform(); - - Vector3 fwd( - chassisWorldTransform.basis[0][Vector3::AXIS_Z], - chassisWorldTransform.basis[1][Vector3::AXIS_Z], - chassisWorldTransform.basis[2][Vector3::AXIS_Z]); - - real_t proj = fwd.dot(wheel.m_raycastInfo.m_contactNormalWS); - fwd -= wheel.m_raycastInfo.m_contactNormalWS * proj; - - real_t proj2 = fwd.dot(vel); - - wheel.m_deltaRotation = (proj2 * step) / (wheel.m_wheelRadius); - } - - wheel.m_rotation += wheel.m_deltaRotation; - wheel.m_rpm = ((wheel.m_deltaRotation / step) * 60) / Math_TAU; - - wheel.m_deltaRotation *= real_t(0.99); //damping of rotation when not in contact - } - - state = NULL; -} - -void VehicleBody::set_engine_force(float p_engine_force) { - - engine_force = p_engine_force; - for (int i = 0; i < wheels.size(); i++) { - VehicleWheel &wheelInfo = *wheels[i]; - if (wheelInfo.engine_traction) - wheelInfo.m_engineForce = p_engine_force; - } -} - -float VehicleBody::get_engine_force() const { - - return engine_force; -} - -void VehicleBody::set_brake(float p_brake) { - - brake = p_brake; - for (int i = 0; i < wheels.size(); i++) { - VehicleWheel &wheelInfo = *wheels[i]; - wheelInfo.m_brake = p_brake; - } -} -float VehicleBody::get_brake() const { - - return brake; -} - -void VehicleBody::set_steering(float p_steering) { - - m_steeringValue = p_steering; - for (int i = 0; i < wheels.size(); i++) { - VehicleWheel &wheelInfo = *wheels[i]; - if (wheelInfo.steers) - wheelInfo.m_steering = p_steering; - } -} -float VehicleBody::get_steering() const { - - return m_steeringValue; -} - -void VehicleBody::_bind_methods() { - - ClassDB::bind_method(D_METHOD("set_engine_force", "engine_force"), &VehicleBody::set_engine_force); - ClassDB::bind_method(D_METHOD("get_engine_force"), &VehicleBody::get_engine_force); - - ClassDB::bind_method(D_METHOD("set_brake", "brake"), &VehicleBody::set_brake); - ClassDB::bind_method(D_METHOD("get_brake"), &VehicleBody::get_brake); - - ClassDB::bind_method(D_METHOD("set_steering", "steering"), &VehicleBody::set_steering); - ClassDB::bind_method(D_METHOD("get_steering"), &VehicleBody::get_steering); - - ADD_GROUP("Motion", ""); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "engine_force", PROPERTY_HINT_RANGE, "0.00,1024.0,0.01,or_greater"), "set_engine_force", "get_engine_force"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "brake", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_brake", "get_brake"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "steering", PROPERTY_HINT_RANGE, "-180,180.0,0.01"), "set_steering", "get_steering"); -} - -VehicleBody::VehicleBody() { - - m_pitchControl = 0; - m_currentVehicleSpeedKmHour = real_t(0.); - m_steeringValue = real_t(0.); - - engine_force = 0; - brake = 0; - - state = NULL; - ccd = false; - - exclude.insert(get_rid()); - //PhysicsServer::get_singleton()->body_set_force_integration_callback(get_rid(), this, "_direct_state_changed"); - - set_mass(40); -} diff --git a/scene/3d/vehicle_body.h b/scene/3d/vehicle_body.h deleted file mode 100644 index b754311b7b..0000000000 --- a/scene/3d/vehicle_body.h +++ /dev/null @@ -1,212 +0,0 @@ -/*************************************************************************/ -/* vehicle_body.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef VEHICLE_BODY_H -#define VEHICLE_BODY_H - -#include "scene/3d/physics_body_3d.h" - -class VehicleBody; - -class VehicleWheel : public Node3D { - - GDCLASS(VehicleWheel, Node3D); - - friend class VehicleBody; - - Transform m_worldTransform; - Transform local_xform; - bool engine_traction; - bool steers; - - Vector3 m_chassisConnectionPointCS; //const - Vector3 m_wheelDirectionCS; //const - Vector3 m_wheelAxleCS; // const or modified by steering - - real_t m_suspensionRestLength; - real_t m_maxSuspensionTravelCm; - real_t m_wheelRadius; - - real_t m_suspensionStiffness; - real_t m_wheelsDampingCompression; - real_t m_wheelsDampingRelaxation; - real_t m_frictionSlip; - real_t m_maxSuspensionForce; - bool m_bIsFrontWheel; - - VehicleBody *body; - - //btVector3 m_wheelAxleCS; // const or modified by steering ? - - real_t m_steering; - real_t m_rotation; - real_t m_deltaRotation; - real_t m_rpm; - real_t m_rollInfluence; - real_t m_engineForce; - real_t m_brake; - - real_t m_clippedInvContactDotSuspension; - real_t m_suspensionRelativeVelocity; - //calculated by suspension - real_t m_wheelsSuspensionForce; - real_t m_skidInfo; - - struct RaycastInfo { - //set by raycaster - Vector3 m_contactNormalWS; //contactnormal - Vector3 m_contactPointWS; //raycast hitpoint - real_t m_suspensionLength; - Vector3 m_hardPointWS; //raycast starting point - Vector3 m_wheelDirectionWS; //direction in worldspace - Vector3 m_wheelAxleWS; // axle in worldspace - bool m_isInContact; - PhysicsBody3D *m_groundObject; //could be general void* ptr - } m_raycastInfo; - - void _update(PhysicsDirectBodyState *s); - -protected: - void _notification(int p_what); - static void _bind_methods(); - -public: - void set_radius(float p_radius); - float get_radius() const; - - void set_suspension_rest_length(float p_length); - float get_suspension_rest_length() const; - - void set_suspension_travel(float p_length); - float get_suspension_travel() const; - - void set_suspension_stiffness(float p_value); - float get_suspension_stiffness() const; - - void set_suspension_max_force(float p_value); - float get_suspension_max_force() const; - - void set_damping_compression(float p_value); - float get_damping_compression() const; - - void set_damping_relaxation(float p_value); - float get_damping_relaxation() const; - - void set_friction_slip(float p_value); - float get_friction_slip() const; - - void set_use_as_traction(bool p_enable); - bool is_used_as_traction() const; - - void set_use_as_steering(bool p_enabled); - bool is_used_as_steering() const; - - bool is_in_contact() const; - - void set_roll_influence(float p_value); - float get_roll_influence() const; - - float get_skidinfo() const; - - float get_rpm() const; - - void set_engine_force(float p_engine_force); - float get_engine_force() const; - - void set_brake(float p_brake); - float get_brake() const; - - void set_steering(float p_steering); - float get_steering() const; - - String get_configuration_warning() const; - - VehicleWheel(); -}; - -class VehicleBody : public RigidBody3D { - - GDCLASS(VehicleBody, RigidBody3D); - - float engine_force; - float brake; - - real_t m_pitchControl; - real_t m_steeringValue; - real_t m_currentVehicleSpeedKmHour; - - Set exclude; - - Vector m_forwardWS; - Vector m_axle; - Vector m_forwardImpulse; - Vector m_sideImpulse; - - struct btVehicleWheelContactPoint { - PhysicsDirectBodyState *m_s; - PhysicsBody3D *m_body1; - Vector3 m_frictionPositionWorld; - Vector3 m_frictionDirectionWorld; - real_t m_jacDiagABInv; - real_t m_maxImpulse; - - btVehicleWheelContactPoint(PhysicsDirectBodyState *s, PhysicsBody3D *body1, const Vector3 &frictionPosWorld, const Vector3 &frictionDirectionWorld, real_t maxImpulse); - }; - - void _resolve_single_bilateral(PhysicsDirectBodyState *s, const Vector3 &pos1, PhysicsBody3D *body2, const Vector3 &pos2, const Vector3 &normal, real_t &impulse, const real_t p_rollInfluence); - real_t _calc_rolling_friction(btVehicleWheelContactPoint &contactPoint); - - void _update_friction(PhysicsDirectBodyState *s); - void _update_suspension(PhysicsDirectBodyState *s); - real_t _ray_cast(int p_idx, PhysicsDirectBodyState *s); - void _update_wheel_transform(VehicleWheel &wheel, PhysicsDirectBodyState *s); - void _update_wheel(int p_idx, PhysicsDirectBodyState *s); - - friend class VehicleWheel; - Vector wheels; - - static void _bind_methods(); - - void _direct_state_changed(Object *p_state); - -public: - void set_engine_force(float p_engine_force); - float get_engine_force() const; - - void set_brake(float p_brake); - float get_brake() const; - - void set_steering(float p_steering); - float get_steering() const; - - VehicleBody(); -}; - -#endif // VEHICLE_BODY_H diff --git a/scene/3d/vehicle_body_3d.cpp b/scene/3d/vehicle_body_3d.cpp new file mode 100644 index 0000000000..5d601b0d43 --- /dev/null +++ b/scene/3d/vehicle_body_3d.cpp @@ -0,0 +1,998 @@ +/*************************************************************************/ +/* vehicle_body_3d.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "vehicle_body_3d.h" + +#define ROLLING_INFLUENCE_FIX + +class btVehicleJacobianEntry { +public: + Vector3 m_linearJointAxis; + Vector3 m_aJ; + Vector3 m_bJ; + Vector3 m_0MinvJt; + Vector3 m_1MinvJt; + //Optimization: can be stored in the w/last component of one of the vectors + real_t m_Adiag; + + real_t getDiagonal() const { return m_Adiag; } + + btVehicleJacobianEntry(){}; + //constraint between two different rigidbodies + btVehicleJacobianEntry( + const Basis &world2A, + const Basis &world2B, + const Vector3 &rel_pos1, + const Vector3 &rel_pos2, + const Vector3 &jointAxis, + const Vector3 &inertiaInvA, + const real_t massInvA, + const Vector3 &inertiaInvB, + const real_t massInvB) : + m_linearJointAxis(jointAxis) { + m_aJ = world2A.xform(rel_pos1.cross(m_linearJointAxis)); + m_bJ = world2B.xform(rel_pos2.cross(-m_linearJointAxis)); + m_0MinvJt = inertiaInvA * m_aJ; + m_1MinvJt = inertiaInvB * m_bJ; + m_Adiag = massInvA + m_0MinvJt.dot(m_aJ) + massInvB + m_1MinvJt.dot(m_bJ); + + //btAssert(m_Adiag > real_t(0.0)); + } + + real_t getRelativeVelocity(const Vector3 &linvelA, const Vector3 &angvelA, const Vector3 &linvelB, const Vector3 &angvelB) { + Vector3 linrel = linvelA - linvelB; + Vector3 angvela = angvelA * m_aJ; + Vector3 angvelb = angvelB * m_bJ; + linrel *= m_linearJointAxis; + angvela += angvelb; + angvela += linrel; + real_t rel_vel2 = angvela[0] + angvela[1] + angvela[2]; + return rel_vel2 + CMP_EPSILON; + } +}; + +void VehicleWheel3D::_notification(int p_what) { + + if (p_what == NOTIFICATION_ENTER_TREE) { + + VehicleBody3D *cb = Object::cast_to(get_parent()); + if (!cb) + return; + body = cb; + local_xform = get_transform(); + cb->wheels.push_back(this); + + m_chassisConnectionPointCS = get_transform().origin; + m_wheelDirectionCS = -get_transform().basis.get_axis(Vector3::AXIS_Y).normalized(); + m_wheelAxleCS = get_transform().basis.get_axis(Vector3::AXIS_X).normalized(); + } + if (p_what == NOTIFICATION_EXIT_TREE) { + + VehicleBody3D *cb = Object::cast_to(get_parent()); + if (!cb) + return; + cb->wheels.erase(this); + body = NULL; + } +} + +String VehicleWheel3D::get_configuration_warning() const { + if (!Object::cast_to(get_parent())) { + return TTR("VehicleWheel serves to provide a wheel system to a VehicleBody. Please use it as a child of a VehicleBody."); + } + + return String(); +} + +void VehicleWheel3D::_update(PhysicsDirectBodyState *s) { + + if (m_raycastInfo.m_isInContact) + + { + real_t project = m_raycastInfo.m_contactNormalWS.dot(m_raycastInfo.m_wheelDirectionWS); + Vector3 chassis_velocity_at_contactPoint; + Vector3 relpos = m_raycastInfo.m_contactPointWS - s->get_transform().origin; + + chassis_velocity_at_contactPoint = s->get_linear_velocity() + + (s->get_angular_velocity()).cross(relpos); // * mPos); + + real_t projVel = m_raycastInfo.m_contactNormalWS.dot(chassis_velocity_at_contactPoint); + if (project >= real_t(-0.1)) { + m_suspensionRelativeVelocity = real_t(0.0); + m_clippedInvContactDotSuspension = real_t(1.0) / real_t(0.1); + } else { + real_t inv = real_t(-1.) / project; + m_suspensionRelativeVelocity = projVel * inv; + m_clippedInvContactDotSuspension = inv; + } + + } + + else // Not in contact : position wheel in a nice (rest length) position + { + m_raycastInfo.m_suspensionLength = m_suspensionRestLength; + m_suspensionRelativeVelocity = real_t(0.0); + m_raycastInfo.m_contactNormalWS = -m_raycastInfo.m_wheelDirectionWS; + m_clippedInvContactDotSuspension = real_t(1.0); + } +} + +void VehicleWheel3D::set_radius(float p_radius) { + + m_wheelRadius = p_radius; + update_gizmo(); +} + +float VehicleWheel3D::get_radius() const { + + return m_wheelRadius; +} + +void VehicleWheel3D::set_suspension_rest_length(float p_length) { + + m_suspensionRestLength = p_length; + update_gizmo(); +} +float VehicleWheel3D::get_suspension_rest_length() const { + + return m_suspensionRestLength; +} + +void VehicleWheel3D::set_suspension_travel(float p_length) { + + m_maxSuspensionTravelCm = p_length / 0.01; +} +float VehicleWheel3D::get_suspension_travel() const { + + return m_maxSuspensionTravelCm * 0.01; +} + +void VehicleWheel3D::set_suspension_stiffness(float p_value) { + + m_suspensionStiffness = p_value; +} +float VehicleWheel3D::get_suspension_stiffness() const { + + return m_suspensionStiffness; +} + +void VehicleWheel3D::set_suspension_max_force(float p_value) { + + m_maxSuspensionForce = p_value; +} +float VehicleWheel3D::get_suspension_max_force() const { + + return m_maxSuspensionForce; +} + +void VehicleWheel3D::set_damping_compression(float p_value) { + + m_wheelsDampingCompression = p_value; +} +float VehicleWheel3D::get_damping_compression() const { + + return m_wheelsDampingCompression; +} + +void VehicleWheel3D::set_damping_relaxation(float p_value) { + + m_wheelsDampingRelaxation = p_value; +} +float VehicleWheel3D::get_damping_relaxation() const { + + return m_wheelsDampingRelaxation; +} + +void VehicleWheel3D::set_friction_slip(float p_value) { + + m_frictionSlip = p_value; +} +float VehicleWheel3D::get_friction_slip() const { + + return m_frictionSlip; +} + +void VehicleWheel3D::set_roll_influence(float p_value) { + m_rollInfluence = p_value; +} + +float VehicleWheel3D::get_roll_influence() const { + return m_rollInfluence; +} + +bool VehicleWheel3D::is_in_contact() const { + return m_raycastInfo.m_isInContact; +} + +void VehicleWheel3D::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_radius", "length"), &VehicleWheel3D::set_radius); + ClassDB::bind_method(D_METHOD("get_radius"), &VehicleWheel3D::get_radius); + + ClassDB::bind_method(D_METHOD("set_suspension_rest_length", "length"), &VehicleWheel3D::set_suspension_rest_length); + ClassDB::bind_method(D_METHOD("get_suspension_rest_length"), &VehicleWheel3D::get_suspension_rest_length); + + ClassDB::bind_method(D_METHOD("set_suspension_travel", "length"), &VehicleWheel3D::set_suspension_travel); + ClassDB::bind_method(D_METHOD("get_suspension_travel"), &VehicleWheel3D::get_suspension_travel); + + ClassDB::bind_method(D_METHOD("set_suspension_stiffness", "length"), &VehicleWheel3D::set_suspension_stiffness); + ClassDB::bind_method(D_METHOD("get_suspension_stiffness"), &VehicleWheel3D::get_suspension_stiffness); + + ClassDB::bind_method(D_METHOD("set_suspension_max_force", "length"), &VehicleWheel3D::set_suspension_max_force); + ClassDB::bind_method(D_METHOD("get_suspension_max_force"), &VehicleWheel3D::get_suspension_max_force); + + ClassDB::bind_method(D_METHOD("set_damping_compression", "length"), &VehicleWheel3D::set_damping_compression); + ClassDB::bind_method(D_METHOD("get_damping_compression"), &VehicleWheel3D::get_damping_compression); + + ClassDB::bind_method(D_METHOD("set_damping_relaxation", "length"), &VehicleWheel3D::set_damping_relaxation); + ClassDB::bind_method(D_METHOD("get_damping_relaxation"), &VehicleWheel3D::get_damping_relaxation); + + ClassDB::bind_method(D_METHOD("set_use_as_traction", "enable"), &VehicleWheel3D::set_use_as_traction); + ClassDB::bind_method(D_METHOD("is_used_as_traction"), &VehicleWheel3D::is_used_as_traction); + + ClassDB::bind_method(D_METHOD("set_use_as_steering", "enable"), &VehicleWheel3D::set_use_as_steering); + ClassDB::bind_method(D_METHOD("is_used_as_steering"), &VehicleWheel3D::is_used_as_steering); + + ClassDB::bind_method(D_METHOD("set_friction_slip", "length"), &VehicleWheel3D::set_friction_slip); + ClassDB::bind_method(D_METHOD("get_friction_slip"), &VehicleWheel3D::get_friction_slip); + + ClassDB::bind_method(D_METHOD("is_in_contact"), &VehicleWheel3D::is_in_contact); + + ClassDB::bind_method(D_METHOD("set_roll_influence", "roll_influence"), &VehicleWheel3D::set_roll_influence); + ClassDB::bind_method(D_METHOD("get_roll_influence"), &VehicleWheel3D::get_roll_influence); + + ClassDB::bind_method(D_METHOD("get_skidinfo"), &VehicleWheel3D::get_skidinfo); + + ClassDB::bind_method(D_METHOD("get_rpm"), &VehicleWheel3D::get_rpm); + + ClassDB::bind_method(D_METHOD("set_engine_force", "engine_force"), &VehicleWheel3D::set_engine_force); + ClassDB::bind_method(D_METHOD("get_engine_force"), &VehicleWheel3D::get_engine_force); + + ClassDB::bind_method(D_METHOD("set_brake", "brake"), &VehicleWheel3D::set_brake); + ClassDB::bind_method(D_METHOD("get_brake"), &VehicleWheel3D::get_brake); + + ClassDB::bind_method(D_METHOD("set_steering", "steering"), &VehicleWheel3D::set_steering); + ClassDB::bind_method(D_METHOD("get_steering"), &VehicleWheel3D::get_steering); + + ADD_GROUP("Per-Wheel Motion", ""); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "engine_force", PROPERTY_HINT_RANGE, "0.00,1024.0,0.01,or_greater"), "set_engine_force", "get_engine_force"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "brake", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_brake", "get_brake"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "steering", PROPERTY_HINT_RANGE, "-180,180.0,0.01"), "set_steering", "get_steering"); + ADD_GROUP("VehicleBody Motion", ""); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_as_traction"), "set_use_as_traction", "is_used_as_traction"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_as_steering"), "set_use_as_steering", "is_used_as_steering"); + ADD_GROUP("Wheel", "wheel_"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wheel_roll_influence"), "set_roll_influence", "get_roll_influence"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wheel_radius"), "set_radius", "get_radius"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wheel_rest_length"), "set_suspension_rest_length", "get_suspension_rest_length"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wheel_friction_slip"), "set_friction_slip", "get_friction_slip"); + ADD_GROUP("Suspension", "suspension_"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "suspension_travel"), "set_suspension_travel", "get_suspension_travel"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "suspension_stiffness"), "set_suspension_stiffness", "get_suspension_stiffness"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "suspension_max_force"), "set_suspension_max_force", "get_suspension_max_force"); + ADD_GROUP("Damping", "damping_"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "damping_compression"), "set_damping_compression", "get_damping_compression"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "damping_relaxation"), "set_damping_relaxation", "get_damping_relaxation"); +} + +void VehicleWheel3D::set_engine_force(float p_engine_force) { + + m_engineForce = p_engine_force; +} + +float VehicleWheel3D::get_engine_force() const { + + return m_engineForce; +} + +void VehicleWheel3D::set_brake(float p_brake) { + + m_brake = p_brake; +} +float VehicleWheel3D::get_brake() const { + + return m_brake; +} + +void VehicleWheel3D::set_steering(float p_steering) { + + m_steering = p_steering; +} +float VehicleWheel3D::get_steering() const { + + return m_steering; +} + +void VehicleWheel3D::set_use_as_traction(bool p_enable) { + + engine_traction = p_enable; +} + +bool VehicleWheel3D::is_used_as_traction() const { + + return engine_traction; +} + +void VehicleWheel3D::set_use_as_steering(bool p_enabled) { + + steers = p_enabled; +} + +bool VehicleWheel3D::is_used_as_steering() const { + + return steers; +} + +float VehicleWheel3D::get_skidinfo() const { + + return m_skidInfo; +} + +float VehicleWheel3D::get_rpm() const { + + return m_rpm; +} + +VehicleWheel3D::VehicleWheel3D() { + + steers = false; + engine_traction = false; + m_steering = real_t(0.); + m_engineForce = real_t(0.); + m_rotation = real_t(0.); + m_deltaRotation = real_t(0.); + m_brake = real_t(0.); + m_rollInfluence = real_t(0.1); + + m_suspensionRestLength = 0.15; + m_wheelRadius = 0.5; //0.28; + m_suspensionStiffness = 5.88; + m_wheelsDampingCompression = 0.83; + m_wheelsDampingRelaxation = 0.88; + m_frictionSlip = 10.5; + m_bIsFrontWheel = false; + m_maxSuspensionTravelCm = 500; + m_maxSuspensionForce = 6000; + + m_suspensionRelativeVelocity = 0; + m_clippedInvContactDotSuspension = 1.0; + m_raycastInfo.m_isInContact = false; + + body = NULL; +} + +void VehicleBody3D::_update_wheel_transform(VehicleWheel3D &wheel, PhysicsDirectBodyState *s) { + + wheel.m_raycastInfo.m_isInContact = false; + + Transform chassisTrans = s->get_transform(); + /* + if (interpolatedTransform && (getRigidBody()->getMotionState())) { + getRigidBody()->getMotionState()->getWorldTransform(chassisTrans); + } + */ + + wheel.m_raycastInfo.m_hardPointWS = chassisTrans.xform(wheel.m_chassisConnectionPointCS); + //wheel.m_raycastInfo.m_hardPointWS+=s->get_linear_velocity()*s->get_step(); + wheel.m_raycastInfo.m_wheelDirectionWS = chassisTrans.get_basis().xform(wheel.m_wheelDirectionCS).normalized(); + wheel.m_raycastInfo.m_wheelAxleWS = chassisTrans.get_basis().xform(wheel.m_wheelAxleCS).normalized(); +} + +void VehicleBody3D::_update_wheel(int p_idx, PhysicsDirectBodyState *s) { + + VehicleWheel3D &wheel = *wheels[p_idx]; + _update_wheel_transform(wheel, s); + + Vector3 up = -wheel.m_raycastInfo.m_wheelDirectionWS; + const Vector3 &right = wheel.m_raycastInfo.m_wheelAxleWS; + Vector3 fwd = up.cross(right); + fwd = fwd.normalized(); + + Basis steeringMat(up, wheel.m_steering); + + Basis rotatingMat(right, wheel.m_rotation); + + Basis basis2( + right[0], up[0], fwd[0], + right[1], up[1], fwd[1], + right[2], up[2], fwd[2]); + + wheel.m_worldTransform.set_basis(steeringMat * rotatingMat * basis2); + //wheel.m_worldTransform.set_basis(basis2 * (steeringMat * rotatingMat)); + wheel.m_worldTransform.set_origin( + wheel.m_raycastInfo.m_hardPointWS + wheel.m_raycastInfo.m_wheelDirectionWS * wheel.m_raycastInfo.m_suspensionLength); +} + +real_t VehicleBody3D::_ray_cast(int p_idx, PhysicsDirectBodyState *s) { + + VehicleWheel3D &wheel = *wheels[p_idx]; + + _update_wheel_transform(wheel, s); + + real_t depth = -1; + + real_t raylen = wheel.m_suspensionRestLength + wheel.m_wheelRadius; + + Vector3 rayvector = wheel.m_raycastInfo.m_wheelDirectionWS * (raylen); + Vector3 source = wheel.m_raycastInfo.m_hardPointWS; + wheel.m_raycastInfo.m_contactPointWS = source + rayvector; + const Vector3 &target = wheel.m_raycastInfo.m_contactPointWS; + source -= wheel.m_wheelRadius * wheel.m_raycastInfo.m_wheelDirectionWS; + + real_t param = real_t(0.); + + PhysicsDirectSpaceState::RayResult rr; + + PhysicsDirectSpaceState *ss = s->get_space_state(); + + bool col = ss->intersect_ray(source, target, rr, exclude); + + wheel.m_raycastInfo.m_groundObject = 0; + + if (col) { + param = source.distance_to(rr.position) / source.distance_to(target); + depth = raylen * param; + wheel.m_raycastInfo.m_contactNormalWS = rr.normal; + + wheel.m_raycastInfo.m_isInContact = true; + if (rr.collider) + wheel.m_raycastInfo.m_groundObject = Object::cast_to(rr.collider); + + real_t hitDistance = param * raylen; + wheel.m_raycastInfo.m_suspensionLength = hitDistance - wheel.m_wheelRadius; + //clamp on max suspension travel + + real_t minSuspensionLength = wheel.m_suspensionRestLength - wheel.m_maxSuspensionTravelCm * real_t(0.01); + real_t maxSuspensionLength = wheel.m_suspensionRestLength + wheel.m_maxSuspensionTravelCm * real_t(0.01); + if (wheel.m_raycastInfo.m_suspensionLength < minSuspensionLength) { + wheel.m_raycastInfo.m_suspensionLength = minSuspensionLength; + } + if (wheel.m_raycastInfo.m_suspensionLength > maxSuspensionLength) { + wheel.m_raycastInfo.m_suspensionLength = maxSuspensionLength; + } + + wheel.m_raycastInfo.m_contactPointWS = rr.position; + + real_t denominator = wheel.m_raycastInfo.m_contactNormalWS.dot(wheel.m_raycastInfo.m_wheelDirectionWS); + + Vector3 chassis_velocity_at_contactPoint; + //Vector3 relpos = wheel.m_raycastInfo.m_contactPointWS-getRigidBody()->getCenterOfMassPosition(); + + //chassis_velocity_at_contactPoint = getRigidBody()->getVelocityInLocalPoint(relpos); + + chassis_velocity_at_contactPoint = s->get_linear_velocity() + + (s->get_angular_velocity()).cross(wheel.m_raycastInfo.m_contactPointWS - s->get_transform().origin); // * mPos); + + real_t projVel = wheel.m_raycastInfo.m_contactNormalWS.dot(chassis_velocity_at_contactPoint); + + if (denominator >= real_t(-0.1)) { + wheel.m_suspensionRelativeVelocity = real_t(0.0); + wheel.m_clippedInvContactDotSuspension = real_t(1.0) / real_t(0.1); + } else { + real_t inv = real_t(-1.) / denominator; + wheel.m_suspensionRelativeVelocity = projVel * inv; + wheel.m_clippedInvContactDotSuspension = inv; + } + + } else { + wheel.m_raycastInfo.m_isInContact = false; + //put wheel info as in rest position + wheel.m_raycastInfo.m_suspensionLength = wheel.m_suspensionRestLength; + wheel.m_suspensionRelativeVelocity = real_t(0.0); + wheel.m_raycastInfo.m_contactNormalWS = -wheel.m_raycastInfo.m_wheelDirectionWS; + wheel.m_clippedInvContactDotSuspension = real_t(1.0); + } + + return depth; +} + +void VehicleBody3D::_update_suspension(PhysicsDirectBodyState *s) { + + real_t chassisMass = mass; + + for (int w_it = 0; w_it < wheels.size(); w_it++) { + VehicleWheel3D &wheel_info = *wheels[w_it]; + + if (wheel_info.m_raycastInfo.m_isInContact) { + real_t force; + //Spring + { + real_t susp_length = wheel_info.m_suspensionRestLength; + real_t current_length = wheel_info.m_raycastInfo.m_suspensionLength; + + real_t length_diff = (susp_length - current_length); + + force = wheel_info.m_suspensionStiffness * length_diff * wheel_info.m_clippedInvContactDotSuspension; + } + + // Damper + { + real_t projected_rel_vel = wheel_info.m_suspensionRelativeVelocity; + { + real_t susp_damping; + if (projected_rel_vel < real_t(0.0)) { + susp_damping = wheel_info.m_wheelsDampingCompression; + } else { + susp_damping = wheel_info.m_wheelsDampingRelaxation; + } + force -= susp_damping * projected_rel_vel; + } + } + + // RESULT + wheel_info.m_wheelsSuspensionForce = force * chassisMass; + if (wheel_info.m_wheelsSuspensionForce < real_t(0.)) { + wheel_info.m_wheelsSuspensionForce = real_t(0.); + } + } else { + wheel_info.m_wheelsSuspensionForce = real_t(0.0); + } + } +} + +//bilateral constraint between two dynamic objects +void VehicleBody3D::_resolve_single_bilateral(PhysicsDirectBodyState *s, const Vector3 &pos1, + PhysicsBody3D *body2, const Vector3 &pos2, const Vector3 &normal, real_t &impulse, const real_t p_rollInfluence) { + + real_t normalLenSqr = normal.length_squared(); + //ERR_FAIL_COND( normalLenSqr < real_t(1.1)); + + if (normalLenSqr > real_t(1.1)) { + impulse = real_t(0.); + return; + } + + Vector3 rel_pos1 = pos1 - s->get_transform().origin; + Vector3 rel_pos2; + if (body2) + rel_pos2 = pos2 - body2->get_global_transform().origin; + //this jacobian entry could be re-used for all iterations + + Vector3 vel1 = s->get_linear_velocity() + (s->get_angular_velocity()).cross(rel_pos1); // * mPos); + Vector3 vel2; + + if (body2) + vel2 = body2->get_linear_velocity() + body2->get_angular_velocity().cross(rel_pos2); + + Vector3 vel = vel1 - vel2; + + Basis b2trans; + float b2invmass = 0; + Vector3 b2lv; + Vector3 b2av; + Vector3 b2invinertia; //todo + + if (body2) { + b2trans = body2->get_global_transform().basis.transposed(); + b2invmass = body2->get_inverse_mass(); + b2lv = body2->get_linear_velocity(); + b2av = body2->get_angular_velocity(); + } + + btVehicleJacobianEntry jac(s->get_transform().basis.transposed(), + b2trans, + rel_pos1, + rel_pos2, + normal, + s->get_inverse_inertia_tensor().get_main_diagonal(), + 1.0 / mass, + b2invinertia, + b2invmass); + + // FIXME: rel_vel assignment here is overwritten by the following assignment. + // What seems to be intended in the next next assignment is: rel_vel = normal.dot(rel_vel); + // Investigate why. + real_t rel_vel = jac.getRelativeVelocity( + s->get_linear_velocity(), + s->get_transform().basis.transposed().xform(s->get_angular_velocity()), + b2lv, + b2trans.xform(b2av)); + + rel_vel = normal.dot(vel); + + // !BAS! We had this set to 0.4, in bullet its 0.2 + real_t contactDamping = real_t(0.2); + + if (p_rollInfluence > 0.0) { + // !BAS! But seeing we apply this frame by frame, makes more sense to me to make this time based + // keeping in mind our anti roll factor if it is set + contactDamping = MIN(contactDamping, s->get_step() / p_rollInfluence); + } + +#define ONLY_USE_LINEAR_MASS +#ifdef ONLY_USE_LINEAR_MASS + real_t massTerm = real_t(1.) / ((1.0 / mass) + b2invmass); + impulse = -contactDamping * rel_vel * massTerm; +#else + real_t velocityImpulse = -contactDamping * rel_vel * jacDiagABInv; + impulse = velocityImpulse; +#endif +} + +VehicleBody3D::btVehicleWheelContactPoint::btVehicleWheelContactPoint(PhysicsDirectBodyState *s, PhysicsBody3D *body1, const Vector3 &frictionPosWorld, const Vector3 &frictionDirectionWorld, real_t maxImpulse) : + m_s(s), + m_body1(body1), + m_frictionPositionWorld(frictionPosWorld), + m_frictionDirectionWorld(frictionDirectionWorld), + m_maxImpulse(maxImpulse) { + float denom0 = 0; + float denom1 = 0; + + { + Vector3 r0 = frictionPosWorld - s->get_transform().origin; + Vector3 c0 = (r0).cross(frictionDirectionWorld); + Vector3 vec = s->get_inverse_inertia_tensor().xform_inv(c0).cross(r0); + denom0 = s->get_inverse_mass() + frictionDirectionWorld.dot(vec); + } + + /* TODO: Why is this code unused? + if (body1) { + + Vector3 r0 = frictionPosWorld - body1->get_global_transform().origin; + Vector3 c0 = (r0).cross(frictionDirectionWorld); + Vector3 vec = s->get_inverse_inertia_tensor().xform_inv(c0).cross(r0); + //denom1= body1->get_inverse_mass() + frictionDirectionWorld.dot(vec); + + } + */ + + real_t relaxation = 1.f; + m_jacDiagABInv = relaxation / (denom0 + denom1); +} + +real_t VehicleBody3D::_calc_rolling_friction(btVehicleWheelContactPoint &contactPoint) { + + real_t j1 = 0.f; + + const Vector3 &contactPosWorld = contactPoint.m_frictionPositionWorld; + + Vector3 rel_pos1 = contactPosWorld - contactPoint.m_s->get_transform().origin; + Vector3 rel_pos2; + if (contactPoint.m_body1) + rel_pos2 = contactPosWorld - contactPoint.m_body1->get_global_transform().origin; + + real_t maxImpulse = contactPoint.m_maxImpulse; + + Vector3 vel1 = contactPoint.m_s->get_linear_velocity() + (contactPoint.m_s->get_angular_velocity()).cross(rel_pos1); // * mPos); + + Vector3 vel2; + if (contactPoint.m_body1) { + vel2 = contactPoint.m_body1->get_linear_velocity() + contactPoint.m_body1->get_angular_velocity().cross(rel_pos2); + } + + Vector3 vel = vel1 - vel2; + + real_t vrel = contactPoint.m_frictionDirectionWorld.dot(vel); + + // calculate j that moves us to zero relative velocity + j1 = -vrel * contactPoint.m_jacDiagABInv; + + return CLAMP(j1, -maxImpulse, maxImpulse); +} + +static const real_t sideFrictionStiffness2 = real_t(1.0); +void VehicleBody3D::_update_friction(PhysicsDirectBodyState *s) { + + //calculate the impulse, so that the wheels don't move sidewards + int numWheel = wheels.size(); + if (!numWheel) + return; + + m_forwardWS.resize(numWheel); + m_axle.resize(numWheel); + m_forwardImpulse.resize(numWheel); + m_sideImpulse.resize(numWheel); + + //collapse all those loops into one! + for (int i = 0; i < wheels.size(); i++) { + m_sideImpulse.write[i] = real_t(0.); + m_forwardImpulse.write[i] = real_t(0.); + } + + { + + for (int i = 0; i < wheels.size(); i++) { + + VehicleWheel3D &wheelInfo = *wheels[i]; + + if (wheelInfo.m_raycastInfo.m_isInContact) { + + //const btTransform& wheelTrans = getWheelTransformWS( i ); + + Basis wheelBasis0 = wheelInfo.m_worldTransform.basis; //get_global_transform().basis; + + m_axle.write[i] = wheelBasis0.get_axis(Vector3::AXIS_X); + //m_axle[i] = wheelInfo.m_raycastInfo.m_wheelAxleWS; + + const Vector3 &surfNormalWS = wheelInfo.m_raycastInfo.m_contactNormalWS; + real_t proj = m_axle[i].dot(surfNormalWS); + m_axle.write[i] -= surfNormalWS * proj; + m_axle.write[i] = m_axle[i].normalized(); + + m_forwardWS.write[i] = surfNormalWS.cross(m_axle[i]); + m_forwardWS.write[i].normalize(); + + _resolve_single_bilateral(s, wheelInfo.m_raycastInfo.m_contactPointWS, + wheelInfo.m_raycastInfo.m_groundObject, wheelInfo.m_raycastInfo.m_contactPointWS, + m_axle[i], m_sideImpulse.write[i], wheelInfo.m_rollInfluence); + + m_sideImpulse.write[i] *= sideFrictionStiffness2; + } + } + } + + real_t sideFactor = real_t(1.); + real_t fwdFactor = 0.5; + + bool sliding = false; + { + for (int wheel = 0; wheel < wheels.size(); wheel++) { + VehicleWheel3D &wheelInfo = *wheels[wheel]; + + //class btRigidBody* groundObject = (class btRigidBody*) wheelInfo.m_raycastInfo.m_groundObject; + + real_t rollingFriction = 0.f; + + if (wheelInfo.m_raycastInfo.m_isInContact) { + if (wheelInfo.m_engineForce != 0.f) { + rollingFriction = -wheelInfo.m_engineForce * s->get_step(); + } else { + real_t defaultRollingFrictionImpulse = 0.f; + real_t maxImpulse = wheelInfo.m_brake ? wheelInfo.m_brake : defaultRollingFrictionImpulse; + btVehicleWheelContactPoint contactPt(s, wheelInfo.m_raycastInfo.m_groundObject, wheelInfo.m_raycastInfo.m_contactPointWS, m_forwardWS[wheel], maxImpulse); + rollingFriction = _calc_rolling_friction(contactPt); + } + } + + //switch between active rolling (throttle), braking and non-active rolling friction (no throttle/break) + + m_forwardImpulse.write[wheel] = real_t(0.); + wheelInfo.m_skidInfo = real_t(1.); + + if (wheelInfo.m_raycastInfo.m_isInContact) { + wheelInfo.m_skidInfo = real_t(1.); + + real_t maximp = wheelInfo.m_wheelsSuspensionForce * s->get_step() * wheelInfo.m_frictionSlip; + real_t maximpSide = maximp; + + real_t maximpSquared = maximp * maximpSide; + + m_forwardImpulse.write[wheel] = rollingFriction; //wheelInfo.m_engineForce* timeStep; + + real_t x = (m_forwardImpulse[wheel]) * fwdFactor; + real_t y = (m_sideImpulse[wheel]) * sideFactor; + + real_t impulseSquared = (x * x + y * y); + + if (impulseSquared > maximpSquared) { + sliding = true; + + real_t factor = maximp / Math::sqrt(impulseSquared); + + wheelInfo.m_skidInfo *= factor; + } + } + } + } + + if (sliding) { + for (int wheel = 0; wheel < wheels.size(); wheel++) { + if (m_sideImpulse[wheel] != real_t(0.)) { + if (wheels[wheel]->m_skidInfo < real_t(1.)) { + m_forwardImpulse.write[wheel] *= wheels[wheel]->m_skidInfo; + m_sideImpulse.write[wheel] *= wheels[wheel]->m_skidInfo; + } + } + } + } + + // apply the impulses + { + for (int wheel = 0; wheel < wheels.size(); wheel++) { + VehicleWheel3D &wheelInfo = *wheels[wheel]; + + Vector3 rel_pos = wheelInfo.m_raycastInfo.m_contactPointWS - + s->get_transform().origin; + + if (m_forwardImpulse[wheel] != real_t(0.)) { + s->apply_impulse(rel_pos, m_forwardWS[wheel] * (m_forwardImpulse[wheel])); + } + if (m_sideImpulse[wheel] != real_t(0.)) { + PhysicsBody3D *groundObject = wheelInfo.m_raycastInfo.m_groundObject; + + Vector3 rel_pos2; + if (groundObject) { + rel_pos2 = wheelInfo.m_raycastInfo.m_contactPointWS - groundObject->get_global_transform().origin; + } + + Vector3 sideImp = m_axle[wheel] * m_sideImpulse[wheel]; + +#if defined ROLLING_INFLUENCE_FIX // fix. It only worked if car's up was along Y - VT. + Vector3 vChassisWorldUp = s->get_transform().basis.transposed()[1]; //getRigidBody()->getCenterOfMassTransform().getBasis().getColumn(m_indexUpAxis); + rel_pos -= vChassisWorldUp * (vChassisWorldUp.dot(rel_pos) * (1.f - wheelInfo.m_rollInfluence)); +#else + rel_pos[1] *= wheelInfo.m_rollInfluence; //? +#endif + s->apply_impulse(rel_pos, sideImp); + + //apply friction impulse on the ground + //todo + //groundObject->applyImpulse(-sideImp,rel_pos2); + } + } + } +} + +void VehicleBody3D::_direct_state_changed(Object *p_state) { + + RigidBody3D::_direct_state_changed(p_state); + + state = Object::cast_to(p_state); + + float step = state->get_step(); + + for (int i = 0; i < wheels.size(); i++) { + + _update_wheel(i, state); + } + + for (int i = 0; i < wheels.size(); i++) { + + _ray_cast(i, state); + wheels[i]->set_transform(state->get_transform().inverse() * wheels[i]->m_worldTransform); + } + + _update_suspension(state); + + for (int i = 0; i < wheels.size(); i++) { + + //apply suspension force + VehicleWheel3D &wheel = *wheels[i]; + + real_t suspensionForce = wheel.m_wheelsSuspensionForce; + + if (suspensionForce > wheel.m_maxSuspensionForce) { + suspensionForce = wheel.m_maxSuspensionForce; + } + Vector3 impulse = wheel.m_raycastInfo.m_contactNormalWS * suspensionForce * step; + Vector3 relpos = wheel.m_raycastInfo.m_contactPointWS - state->get_transform().origin; + + state->apply_impulse(relpos, impulse); + //getRigidBody()->applyImpulse(impulse, relpos); + } + + _update_friction(state); + + for (int i = 0; i < wheels.size(); i++) { + VehicleWheel3D &wheel = *wheels[i]; + Vector3 relpos = wheel.m_raycastInfo.m_hardPointWS - state->get_transform().origin; + Vector3 vel = state->get_linear_velocity() + (state->get_angular_velocity()).cross(relpos); // * mPos); + + if (wheel.m_raycastInfo.m_isInContact) { + const Transform &chassisWorldTransform = state->get_transform(); + + Vector3 fwd( + chassisWorldTransform.basis[0][Vector3::AXIS_Z], + chassisWorldTransform.basis[1][Vector3::AXIS_Z], + chassisWorldTransform.basis[2][Vector3::AXIS_Z]); + + real_t proj = fwd.dot(wheel.m_raycastInfo.m_contactNormalWS); + fwd -= wheel.m_raycastInfo.m_contactNormalWS * proj; + + real_t proj2 = fwd.dot(vel); + + wheel.m_deltaRotation = (proj2 * step) / (wheel.m_wheelRadius); + } + + wheel.m_rotation += wheel.m_deltaRotation; + wheel.m_rpm = ((wheel.m_deltaRotation / step) * 60) / Math_TAU; + + wheel.m_deltaRotation *= real_t(0.99); //damping of rotation when not in contact + } + + state = NULL; +} + +void VehicleBody3D::set_engine_force(float p_engine_force) { + + engine_force = p_engine_force; + for (int i = 0; i < wheels.size(); i++) { + VehicleWheel3D &wheelInfo = *wheels[i]; + if (wheelInfo.engine_traction) + wheelInfo.m_engineForce = p_engine_force; + } +} + +float VehicleBody3D::get_engine_force() const { + + return engine_force; +} + +void VehicleBody3D::set_brake(float p_brake) { + + brake = p_brake; + for (int i = 0; i < wheels.size(); i++) { + VehicleWheel3D &wheelInfo = *wheels[i]; + wheelInfo.m_brake = p_brake; + } +} +float VehicleBody3D::get_brake() const { + + return brake; +} + +void VehicleBody3D::set_steering(float p_steering) { + + m_steeringValue = p_steering; + for (int i = 0; i < wheels.size(); i++) { + VehicleWheel3D &wheelInfo = *wheels[i]; + if (wheelInfo.steers) + wheelInfo.m_steering = p_steering; + } +} +float VehicleBody3D::get_steering() const { + + return m_steeringValue; +} + +void VehicleBody3D::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_engine_force", "engine_force"), &VehicleBody3D::set_engine_force); + ClassDB::bind_method(D_METHOD("get_engine_force"), &VehicleBody3D::get_engine_force); + + ClassDB::bind_method(D_METHOD("set_brake", "brake"), &VehicleBody3D::set_brake); + ClassDB::bind_method(D_METHOD("get_brake"), &VehicleBody3D::get_brake); + + ClassDB::bind_method(D_METHOD("set_steering", "steering"), &VehicleBody3D::set_steering); + ClassDB::bind_method(D_METHOD("get_steering"), &VehicleBody3D::get_steering); + + ADD_GROUP("Motion", ""); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "engine_force", PROPERTY_HINT_RANGE, "0.00,1024.0,0.01,or_greater"), "set_engine_force", "get_engine_force"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "brake", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_brake", "get_brake"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "steering", PROPERTY_HINT_RANGE, "-180,180.0,0.01"), "set_steering", "get_steering"); +} + +VehicleBody3D::VehicleBody3D() { + + m_pitchControl = 0; + m_currentVehicleSpeedKmHour = real_t(0.); + m_steeringValue = real_t(0.); + + engine_force = 0; + brake = 0; + + state = NULL; + ccd = false; + + exclude.insert(get_rid()); + //PhysicsServer::get_singleton()->body_set_force_integration_callback(get_rid(), this, "_direct_state_changed"); + + set_mass(40); +} diff --git a/scene/3d/vehicle_body_3d.h b/scene/3d/vehicle_body_3d.h new file mode 100644 index 0000000000..1ca9b6253f --- /dev/null +++ b/scene/3d/vehicle_body_3d.h @@ -0,0 +1,212 @@ +/*************************************************************************/ +/* vehicle_body_3d.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef VEHICLE_BODY_H +#define VEHICLE_BODY_H + +#include "scene/3d/physics_body_3d.h" + +class VehicleBody3D; + +class VehicleWheel3D : public Node3D { + + GDCLASS(VehicleWheel3D, Node3D); + + friend class VehicleBody3D; + + Transform m_worldTransform; + Transform local_xform; + bool engine_traction; + bool steers; + + Vector3 m_chassisConnectionPointCS; //const + Vector3 m_wheelDirectionCS; //const + Vector3 m_wheelAxleCS; // const or modified by steering + + real_t m_suspensionRestLength; + real_t m_maxSuspensionTravelCm; + real_t m_wheelRadius; + + real_t m_suspensionStiffness; + real_t m_wheelsDampingCompression; + real_t m_wheelsDampingRelaxation; + real_t m_frictionSlip; + real_t m_maxSuspensionForce; + bool m_bIsFrontWheel; + + VehicleBody3D *body; + + //btVector3 m_wheelAxleCS; // const or modified by steering ? + + real_t m_steering; + real_t m_rotation; + real_t m_deltaRotation; + real_t m_rpm; + real_t m_rollInfluence; + real_t m_engineForce; + real_t m_brake; + + real_t m_clippedInvContactDotSuspension; + real_t m_suspensionRelativeVelocity; + //calculated by suspension + real_t m_wheelsSuspensionForce; + real_t m_skidInfo; + + struct RaycastInfo { + //set by raycaster + Vector3 m_contactNormalWS; //contactnormal + Vector3 m_contactPointWS; //raycast hitpoint + real_t m_suspensionLength; + Vector3 m_hardPointWS; //raycast starting point + Vector3 m_wheelDirectionWS; //direction in worldspace + Vector3 m_wheelAxleWS; // axle in worldspace + bool m_isInContact; + PhysicsBody3D *m_groundObject; //could be general void* ptr + } m_raycastInfo; + + void _update(PhysicsDirectBodyState *s); + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + void set_radius(float p_radius); + float get_radius() const; + + void set_suspension_rest_length(float p_length); + float get_suspension_rest_length() const; + + void set_suspension_travel(float p_length); + float get_suspension_travel() const; + + void set_suspension_stiffness(float p_value); + float get_suspension_stiffness() const; + + void set_suspension_max_force(float p_value); + float get_suspension_max_force() const; + + void set_damping_compression(float p_value); + float get_damping_compression() const; + + void set_damping_relaxation(float p_value); + float get_damping_relaxation() const; + + void set_friction_slip(float p_value); + float get_friction_slip() const; + + void set_use_as_traction(bool p_enable); + bool is_used_as_traction() const; + + void set_use_as_steering(bool p_enabled); + bool is_used_as_steering() const; + + bool is_in_contact() const; + + void set_roll_influence(float p_value); + float get_roll_influence() const; + + float get_skidinfo() const; + + float get_rpm() const; + + void set_engine_force(float p_engine_force); + float get_engine_force() const; + + void set_brake(float p_brake); + float get_brake() const; + + void set_steering(float p_steering); + float get_steering() const; + + String get_configuration_warning() const; + + VehicleWheel3D(); +}; + +class VehicleBody3D : public RigidBody3D { + + GDCLASS(VehicleBody3D, RigidBody3D); + + float engine_force; + float brake; + + real_t m_pitchControl; + real_t m_steeringValue; + real_t m_currentVehicleSpeedKmHour; + + Set exclude; + + Vector m_forwardWS; + Vector m_axle; + Vector m_forwardImpulse; + Vector m_sideImpulse; + + struct btVehicleWheelContactPoint { + PhysicsDirectBodyState *m_s; + PhysicsBody3D *m_body1; + Vector3 m_frictionPositionWorld; + Vector3 m_frictionDirectionWorld; + real_t m_jacDiagABInv; + real_t m_maxImpulse; + + btVehicleWheelContactPoint(PhysicsDirectBodyState *s, PhysicsBody3D *body1, const Vector3 &frictionPosWorld, const Vector3 &frictionDirectionWorld, real_t maxImpulse); + }; + + void _resolve_single_bilateral(PhysicsDirectBodyState *s, const Vector3 &pos1, PhysicsBody3D *body2, const Vector3 &pos2, const Vector3 &normal, real_t &impulse, const real_t p_rollInfluence); + real_t _calc_rolling_friction(btVehicleWheelContactPoint &contactPoint); + + void _update_friction(PhysicsDirectBodyState *s); + void _update_suspension(PhysicsDirectBodyState *s); + real_t _ray_cast(int p_idx, PhysicsDirectBodyState *s); + void _update_wheel_transform(VehicleWheel3D &wheel, PhysicsDirectBodyState *s); + void _update_wheel(int p_idx, PhysicsDirectBodyState *s); + + friend class VehicleWheel3D; + Vector wheels; + + static void _bind_methods(); + + void _direct_state_changed(Object *p_state); + +public: + void set_engine_force(float p_engine_force); + float get_engine_force() const; + + void set_brake(float p_brake); + float get_brake() const; + + void set_steering(float p_steering); + float get_steering() const; + + VehicleBody3D(); +}; + +#endif // VEHICLE_BODY_H diff --git a/scene/animation/skeleton_ik.cpp b/scene/animation/skeleton_ik.cpp deleted file mode 100644 index 71aa5e13b3..0000000000 --- a/scene/animation/skeleton_ik.cpp +++ /dev/null @@ -1,578 +0,0 @@ -/*************************************************************************/ -/* skeleton_ik.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -/** - * @author AndreaCatania - */ - -#include "skeleton_ik.h" - -#ifndef _3D_DISABLED - -FabrikInverseKinematic::ChainItem *FabrikInverseKinematic::ChainItem::find_child(const BoneId p_bone_id) { - for (int i = children.size() - 1; 0 <= i; --i) { - if (p_bone_id == children[i].bone) { - return &children.write[i]; - } - } - return NULL; -} - -FabrikInverseKinematic::ChainItem *FabrikInverseKinematic::ChainItem::add_child(const BoneId p_bone_id) { - const int infant_child_id = children.size(); - children.resize(infant_child_id + 1); - children.write[infant_child_id].bone = p_bone_id; - children.write[infant_child_id].parent_item = this; - return &children.write[infant_child_id]; -} - -/// Build a chain that starts from the root to tip -bool FabrikInverseKinematic::build_chain(Task *p_task, bool p_force_simple_chain) { - - ERR_FAIL_COND_V(-1 == p_task->root_bone, false); - - Chain &chain(p_task->chain); - - chain.tips.resize(p_task->end_effectors.size()); - chain.chain_root.bone = p_task->root_bone; - chain.chain_root.initial_transform = p_task->skeleton->get_bone_global_pose(chain.chain_root.bone); - chain.chain_root.current_pos = chain.chain_root.initial_transform.origin; - chain.chain_root.pb = p_task->skeleton->get_physical_bone(chain.chain_root.bone); - chain.middle_chain_item = NULL; - - // Holds all IDs that are composing a single chain in reverse order - Vector chain_ids; - // This is used to know the chain size - int sub_chain_size; - // Resize only one time in order to fit all joints for performance reason - chain_ids.resize(p_task->skeleton->get_bone_count()); - - for (int x = p_task->end_effectors.size() - 1; 0 <= x; --x) { - - const EndEffector *ee(&p_task->end_effectors[x]); - ERR_FAIL_COND_V(p_task->root_bone >= ee->tip_bone, false); - ERR_FAIL_INDEX_V(ee->tip_bone, p_task->skeleton->get_bone_count(), false); - - sub_chain_size = 0; - // Picks all IDs that composing a single chain in reverse order (except the root) - BoneId chain_sub_tip(ee->tip_bone); - while (chain_sub_tip > p_task->root_bone) { - - chain_ids.write[sub_chain_size++] = chain_sub_tip; - chain_sub_tip = p_task->skeleton->get_bone_parent(chain_sub_tip); - } - - BoneId middle_chain_item_id = (((float)sub_chain_size) * 0.5); - - // Build chain by reading chain ids in reverse order - // For each chain item id will be created a ChainItem if doesn't exists - ChainItem *sub_chain(&chain.chain_root); - for (int i = sub_chain_size - 1; 0 <= i; --i) { - - ChainItem *child_ci(sub_chain->find_child(chain_ids[i])); - if (!child_ci) { - - child_ci = sub_chain->add_child(chain_ids[i]); - - child_ci->pb = p_task->skeleton->get_physical_bone(child_ci->bone); - - child_ci->initial_transform = p_task->skeleton->get_bone_global_pose(child_ci->bone); - child_ci->current_pos = child_ci->initial_transform.origin; - - if (child_ci->parent_item) { - child_ci->length = (child_ci->current_pos - child_ci->parent_item->current_pos).length(); - } - } - - sub_chain = child_ci; - - if (middle_chain_item_id == i) { - chain.middle_chain_item = child_ci; - } - } - - if (!middle_chain_item_id) - chain.middle_chain_item = NULL; - - // Initialize current tip - chain.tips.write[x].chain_item = sub_chain; - chain.tips.write[x].end_effector = ee; - - if (p_force_simple_chain) { - // NOTE: - // This is an "hack" that force to create only one tip per chain since the solver of multi tip (end effector) - // is not yet created. - // Remove this code when this is done - break; - } - } - return true; -} - -void FabrikInverseKinematic::update_chain(const Skeleton3D *p_sk, ChainItem *p_chain_item) { - - if (!p_chain_item) - return; - - p_chain_item->initial_transform = p_sk->get_bone_global_pose(p_chain_item->bone); - p_chain_item->current_pos = p_chain_item->initial_transform.origin; - - ChainItem *items = p_chain_item->children.ptrw(); - for (int i = 0; i < p_chain_item->children.size(); i += 1) { - update_chain(p_sk, items + i); - } -} - -void FabrikInverseKinematic::solve_simple(Task *p_task, bool p_solve_magnet) { - - real_t distance_to_goal(1e4); - real_t previous_distance_to_goal(0); - int can_solve(p_task->max_iterations); - while (distance_to_goal > p_task->min_distance && Math::abs(previous_distance_to_goal - distance_to_goal) > 0.005 && can_solve) { - previous_distance_to_goal = distance_to_goal; - --can_solve; - - solve_simple_backwards(p_task->chain, p_solve_magnet); - solve_simple_forwards(p_task->chain, p_solve_magnet); - - distance_to_goal = (p_task->chain.tips[0].chain_item->current_pos - p_task->chain.tips[0].end_effector->goal_transform.origin).length(); - } -} - -void FabrikInverseKinematic::solve_simple_backwards(Chain &r_chain, bool p_solve_magnet) { - - if (p_solve_magnet && !r_chain.middle_chain_item) { - return; - } - - Vector3 goal; - ChainItem *sub_chain_tip; - if (p_solve_magnet) { - goal = r_chain.magnet_position; - sub_chain_tip = r_chain.middle_chain_item; - } else { - goal = r_chain.tips[0].end_effector->goal_transform.origin; - sub_chain_tip = r_chain.tips[0].chain_item; - } - - while (sub_chain_tip) { - sub_chain_tip->current_pos = goal; - - if (sub_chain_tip->parent_item) { - // Not yet in the chain root - // So calculate next goal location - - const Vector3 look_parent((sub_chain_tip->parent_item->current_pos - sub_chain_tip->current_pos).normalized()); - goal = sub_chain_tip->current_pos + (look_parent * sub_chain_tip->length); - - // [TODO] Constraints goes here - } - - sub_chain_tip = sub_chain_tip->parent_item; - } -} - -void FabrikInverseKinematic::solve_simple_forwards(Chain &r_chain, bool p_solve_magnet) { - - if (p_solve_magnet && !r_chain.middle_chain_item) { - return; - } - - ChainItem *sub_chain_root(&r_chain.chain_root); - Vector3 origin(r_chain.chain_root.initial_transform.origin); - - while (sub_chain_root) { // Reach the tip - sub_chain_root->current_pos = origin; - - if (!sub_chain_root->children.empty()) { - - ChainItem &child(sub_chain_root->children.write[0]); - - // Is not tip - // So calculate next origin location - - // Look child - sub_chain_root->current_ori = (child.current_pos - sub_chain_root->current_pos).normalized(); - origin = sub_chain_root->current_pos + (sub_chain_root->current_ori * child.length); - - // [TODO] Constraints goes here - - if (p_solve_magnet && sub_chain_root == r_chain.middle_chain_item) { - // In case of magnet solving this is the tip - sub_chain_root = NULL; - } else { - sub_chain_root = &child; - } - } else { - - // Is tip - sub_chain_root = NULL; - } - } -} - -FabrikInverseKinematic::Task *FabrikInverseKinematic::create_simple_task(Skeleton3D *p_sk, BoneId root_bone, BoneId tip_bone, const Transform &goal_transform) { - - FabrikInverseKinematic::EndEffector ee; - ee.tip_bone = tip_bone; - - Task *task(memnew(Task)); - task->skeleton = p_sk; - task->root_bone = root_bone; - task->end_effectors.push_back(ee); - task->goal_global_transform = goal_transform; - - if (!build_chain(task)) { - free_task(task); - return NULL; - } - - return task; -} - -void FabrikInverseKinematic::free_task(Task *p_task) { - if (p_task) - memdelete(p_task); -} - -void FabrikInverseKinematic::set_goal(Task *p_task, const Transform &p_goal) { - p_task->goal_global_transform = p_goal; -} - -void FabrikInverseKinematic::make_goal(Task *p_task, const Transform &p_inverse_transf, real_t blending_delta) { - - if (blending_delta >= 0.99f) { - // Update the end_effector (local transform) without blending - p_task->end_effectors.write[0].goal_transform = p_inverse_transf * p_task->goal_global_transform; - } else { - - // End effector in local transform - const Transform end_effector_pose(p_task->skeleton->get_bone_global_pose(p_task->end_effectors.write[0].tip_bone)); - - // Update the end_effector (local transform) by blending with current pose - p_task->end_effectors.write[0].goal_transform = end_effector_pose.interpolate_with(p_inverse_transf * p_task->goal_global_transform, blending_delta); - } -} - -void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool override_tip_basis, bool p_use_magnet, const Vector3 &p_magnet_position) { - - if (blending_delta <= 0.01f) { - return; // Skip solving - } - - p_task->skeleton->clear_bones_global_pose_override(); - - make_goal(p_task, p_task->skeleton->get_global_transform().affine_inverse().scaled(p_task->skeleton->get_global_transform().get_basis().get_scale()), blending_delta); - - update_chain(p_task->skeleton, &p_task->chain.chain_root); - - if (p_use_magnet && p_task->chain.middle_chain_item) { - p_task->chain.magnet_position = p_task->chain.middle_chain_item->initial_transform.origin.linear_interpolate(p_magnet_position, blending_delta); - solve_simple(p_task, true); - } - solve_simple(p_task, false); - - // Assign new bone position. - ChainItem *ci(&p_task->chain.chain_root); - while (ci) { - Transform new_bone_pose(ci->initial_transform); - new_bone_pose.origin = ci->current_pos; - - if (!ci->children.empty()) { - - /// Rotate basis - const Vector3 initial_ori((ci->children[0].initial_transform.origin - ci->initial_transform.origin).normalized()); - const Vector3 rot_axis(initial_ori.cross(ci->current_ori).normalized()); - - if (rot_axis[0] != 0 && rot_axis[1] != 0 && rot_axis[2] != 0) { - const real_t rot_angle(Math::acos(CLAMP(initial_ori.dot(ci->current_ori), -1, 1))); - new_bone_pose.basis.rotate(rot_axis, rot_angle); - } - } else { - // Set target orientation to tip - if (override_tip_basis) - new_bone_pose.basis = p_task->chain.tips[0].end_effector->goal_transform.basis; - else - new_bone_pose.basis = new_bone_pose.basis * p_task->chain.tips[0].end_effector->goal_transform.basis; - } - - p_task->skeleton->set_bone_global_pose_override(ci->bone, new_bone_pose, 1.0, true); - - if (!ci->children.empty()) - ci = &ci->children.write[0]; - else - ci = NULL; - } -} - -void SkeletonIK3D::_validate_property(PropertyInfo &property) const { - - if (property.name == "root_bone" || property.name == "tip_bone") { - - if (skeleton) { - - String names("--,"); - for (int i = 0; i < skeleton->get_bone_count(); i++) { - if (i > 0) - names += ","; - names += skeleton->get_bone_name(i); - } - - property.hint = PROPERTY_HINT_ENUM; - property.hint_string = names; - } else { - - property.hint = PROPERTY_HINT_NONE; - property.hint_string = ""; - } - } -} - -void SkeletonIK3D::_bind_methods() { - - ClassDB::bind_method(D_METHOD("set_root_bone", "root_bone"), &SkeletonIK3D::set_root_bone); - ClassDB::bind_method(D_METHOD("get_root_bone"), &SkeletonIK3D::get_root_bone); - - ClassDB::bind_method(D_METHOD("set_tip_bone", "tip_bone"), &SkeletonIK3D::set_tip_bone); - ClassDB::bind_method(D_METHOD("get_tip_bone"), &SkeletonIK3D::get_tip_bone); - - ClassDB::bind_method(D_METHOD("set_interpolation", "interpolation"), &SkeletonIK3D::set_interpolation); - ClassDB::bind_method(D_METHOD("get_interpolation"), &SkeletonIK3D::get_interpolation); - - ClassDB::bind_method(D_METHOD("set_target_transform", "target"), &SkeletonIK3D::set_target_transform); - ClassDB::bind_method(D_METHOD("get_target_transform"), &SkeletonIK3D::get_target_transform); - - ClassDB::bind_method(D_METHOD("set_target_node", "node"), &SkeletonIK3D::set_target_node); - ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonIK3D::get_target_node); - - ClassDB::bind_method(D_METHOD("set_override_tip_basis", "override"), &SkeletonIK3D::set_override_tip_basis); - ClassDB::bind_method(D_METHOD("is_override_tip_basis"), &SkeletonIK3D::is_override_tip_basis); - - ClassDB::bind_method(D_METHOD("set_use_magnet", "use"), &SkeletonIK3D::set_use_magnet); - ClassDB::bind_method(D_METHOD("is_using_magnet"), &SkeletonIK3D::is_using_magnet); - - ClassDB::bind_method(D_METHOD("set_magnet_position", "local_position"), &SkeletonIK3D::set_magnet_position); - ClassDB::bind_method(D_METHOD("get_magnet_position"), &SkeletonIK3D::get_magnet_position); - - ClassDB::bind_method(D_METHOD("get_parent_skeleton"), &SkeletonIK3D::get_parent_skeleton); - ClassDB::bind_method(D_METHOD("is_running"), &SkeletonIK3D::is_running); - - ClassDB::bind_method(D_METHOD("set_min_distance", "min_distance"), &SkeletonIK3D::set_min_distance); - ClassDB::bind_method(D_METHOD("get_min_distance"), &SkeletonIK3D::get_min_distance); - - ClassDB::bind_method(D_METHOD("set_max_iterations", "iterations"), &SkeletonIK3D::set_max_iterations); - ClassDB::bind_method(D_METHOD("get_max_iterations"), &SkeletonIK3D::get_max_iterations); - - ClassDB::bind_method(D_METHOD("start", "one_time"), &SkeletonIK3D::start, DEFVAL(false)); - ClassDB::bind_method(D_METHOD("stop"), &SkeletonIK3D::stop); - - ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "root_bone"), "set_root_bone", "get_root_bone"); - ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "tip_bone"), "set_tip_bone", "get_tip_bone"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "interpolation", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_interpolation", "get_interpolation"); - ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM, "target"), "set_target_transform", "get_target_transform"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_tip_basis"), "set_override_tip_basis", "is_override_tip_basis"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_magnet"), "set_use_magnet", "is_using_magnet"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "magnet"), "set_magnet_position", "get_magnet_position"); - ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_node"), "set_target_node", "get_target_node"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "min_distance"), "set_min_distance", "get_min_distance"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "max_iterations"), "set_max_iterations", "get_max_iterations"); -} - -void SkeletonIK3D::_notification(int p_what) { - switch (p_what) { - case NOTIFICATION_ENTER_TREE: { - skeleton = Object::cast_to(get_parent()); - set_process_priority(1); - reload_chain(); - } break; - case NOTIFICATION_INTERNAL_PROCESS: { - - if (target_node_override) - reload_goal(); - - _solve_chain(); - - } break; - case NOTIFICATION_EXIT_TREE: { - reload_chain(); - } break; - } -} - -SkeletonIK3D::SkeletonIK3D() : - interpolation(1), - override_tip_basis(true), - use_magnet(false), - min_distance(0.01), - max_iterations(10), - skeleton(NULL), - target_node_override(NULL), - task(NULL) { -} - -SkeletonIK3D::~SkeletonIK3D() { - FabrikInverseKinematic::free_task(task); - task = NULL; -} - -void SkeletonIK3D::set_root_bone(const StringName &p_root_bone) { - root_bone = p_root_bone; - reload_chain(); -} - -StringName SkeletonIK3D::get_root_bone() const { - return root_bone; -} - -void SkeletonIK3D::set_tip_bone(const StringName &p_tip_bone) { - tip_bone = p_tip_bone; - reload_chain(); -} - -StringName SkeletonIK3D::get_tip_bone() const { - return tip_bone; -} - -void SkeletonIK3D::set_interpolation(real_t p_interpolation) { - interpolation = p_interpolation; -} - -real_t SkeletonIK3D::get_interpolation() const { - return interpolation; -} - -void SkeletonIK3D::set_target_transform(const Transform &p_target) { - target = p_target; - reload_goal(); -} - -const Transform &SkeletonIK3D::get_target_transform() const { - return target; -} - -void SkeletonIK3D::set_target_node(const NodePath &p_node) { - target_node_path_override = p_node; - target_node_override = NULL; - reload_goal(); -} - -NodePath SkeletonIK3D::get_target_node() { - return target_node_path_override; -} - -void SkeletonIK3D::set_override_tip_basis(bool p_override) { - override_tip_basis = p_override; -} - -bool SkeletonIK3D::is_override_tip_basis() const { - return override_tip_basis; -} - -void SkeletonIK3D::set_use_magnet(bool p_use) { - use_magnet = p_use; -} - -bool SkeletonIK3D::is_using_magnet() const { - return use_magnet; -} - -void SkeletonIK3D::set_magnet_position(const Vector3 &p_local_position) { - magnet_position = p_local_position; -} - -const Vector3 &SkeletonIK3D::get_magnet_position() const { - return magnet_position; -} - -void SkeletonIK3D::set_min_distance(real_t p_min_distance) { - min_distance = p_min_distance; -} - -void SkeletonIK3D::set_max_iterations(int p_iterations) { - max_iterations = p_iterations; -} - -bool SkeletonIK3D::is_running() { - return is_processing_internal(); -} - -void SkeletonIK3D::start(bool p_one_time) { - if (p_one_time) { - set_process_internal(false); - _solve_chain(); - } else { - set_process_internal(true); - } -} - -void SkeletonIK3D::stop() { - set_process_internal(false); -} - -Transform SkeletonIK3D::_get_target_transform() { - - if (!target_node_override && !target_node_path_override.is_empty()) - target_node_override = Object::cast_to(get_node(target_node_path_override)); - - if (target_node_override) - return target_node_override->get_global_transform(); - else - return target; -} - -void SkeletonIK3D::reload_chain() { - - FabrikInverseKinematic::free_task(task); - task = NULL; - - if (!skeleton) - return; - - task = FabrikInverseKinematic::create_simple_task(skeleton, skeleton->find_bone(root_bone), skeleton->find_bone(tip_bone), _get_target_transform()); - if (task) { - task->max_iterations = max_iterations; - task->min_distance = min_distance; - } -} - -void SkeletonIK3D::reload_goal() { - if (!task) - return; - - FabrikInverseKinematic::set_goal(task, _get_target_transform()); -} - -void SkeletonIK3D::_solve_chain() { - if (!task) - return; - FabrikInverseKinematic::solve(task, interpolation, override_tip_basis, use_magnet, magnet_position); -} - -#endif // _3D_DISABLED diff --git a/scene/animation/skeleton_ik.h b/scene/animation/skeleton_ik.h deleted file mode 100644 index 938c912065..0000000000 --- a/scene/animation/skeleton_ik.h +++ /dev/null @@ -1,220 +0,0 @@ -/*************************************************************************/ -/* skeleton_ik.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef SKELETON_IK_H -#define SKELETON_IK_H - -#ifndef _3D_DISABLED - -/** - * @author AndreaCatania - */ - -#include "core/math/transform.h" -#include "scene/3d/skeleton_3d.h" - -class FabrikInverseKinematic { - - struct EndEffector { - BoneId tip_bone; - Transform goal_transform; - }; - - struct ChainItem { - - Vector children; - ChainItem *parent_item; - - // Bone info - BoneId bone; - PhysicalBone3D *pb; - - real_t length; - /// Positions relative to root bone - Transform initial_transform; - Vector3 current_pos; - // Direction from this bone to child - Vector3 current_ori; - - ChainItem() : - parent_item(NULL), - bone(-1), - pb(NULL), - length(0) {} - - ChainItem *find_child(const BoneId p_bone_id); - ChainItem *add_child(const BoneId p_bone_id); - }; - - struct ChainTip { - ChainItem *chain_item; - const EndEffector *end_effector; - - ChainTip() : - chain_item(NULL), - end_effector(NULL) {} - - ChainTip(ChainItem *p_chain_item, const EndEffector *p_end_effector) : - chain_item(p_chain_item), - end_effector(p_end_effector) {} - - ChainTip(const ChainTip &p_other_ct) : - chain_item(p_other_ct.chain_item), - end_effector(p_other_ct.end_effector) {} - }; - - struct Chain { - ChainItem chain_root; - ChainItem *middle_chain_item; - Vector tips; - Vector3 magnet_position; - }; - -public: - struct Task { - RID self; - Skeleton3D *skeleton; - - Chain chain; - - // Settings - real_t min_distance; - int max_iterations; - - // Bone data - BoneId root_bone; - Vector end_effectors; - - Transform goal_global_transform; - - Task() : - skeleton(NULL), - min_distance(0.01), - max_iterations(10), - root_bone(-1) {} - }; - -private: - /// Init a chain that starts from the root to tip - static bool build_chain(Task *p_task, bool p_force_simple_chain = true); - - static void update_chain(const Skeleton3D *p_sk, ChainItem *p_chain_item); - - static void solve_simple(Task *p_task, bool p_solve_magnet); - /// Special solvers that solve only chains with one end effector - static void solve_simple_backwards(Chain &r_chain, bool p_solve_magnet); - static void solve_simple_forwards(Chain &r_chain, bool p_solve_magnet); - -public: - static Task *create_simple_task(Skeleton3D *p_sk, BoneId root_bone, BoneId tip_bone, const Transform &goal_transform); - static void free_task(Task *p_task); - // The goal of chain should be always in local space - static void set_goal(Task *p_task, const Transform &p_goal); - static void make_goal(Task *p_task, const Transform &p_inverse_transf, real_t blending_delta); - static void solve(Task *p_task, real_t blending_delta, bool override_tip_basis, bool p_use_magnet, const Vector3 &p_magnet_position); -}; - -class SkeletonIK3D : public Node { - GDCLASS(SkeletonIK3D, Node); - - StringName root_bone; - StringName tip_bone; - real_t interpolation; - Transform target; - NodePath target_node_path_override; - bool override_tip_basis; - bool use_magnet; - Vector3 magnet_position; - - real_t min_distance; - int max_iterations; - - Skeleton3D *skeleton; - Node3D *target_node_override; - FabrikInverseKinematic::Task *task; - -protected: - virtual void - _validate_property(PropertyInfo &property) const; - - static void _bind_methods(); - virtual void _notification(int p_what); - -public: - SkeletonIK3D(); - virtual ~SkeletonIK3D(); - - void set_root_bone(const StringName &p_root_bone); - StringName get_root_bone() const; - - void set_tip_bone(const StringName &p_tip_bone); - StringName get_tip_bone() const; - - void set_interpolation(real_t p_interpolation); - real_t get_interpolation() const; - - void set_target_transform(const Transform &p_target); - const Transform &get_target_transform() const; - - void set_target_node(const NodePath &p_node); - NodePath get_target_node(); - - void set_override_tip_basis(bool p_override); - bool is_override_tip_basis() const; - - void set_use_magnet(bool p_use); - bool is_using_magnet() const; - - void set_magnet_position(const Vector3 &p_local_position); - const Vector3 &get_magnet_position() const; - - void set_min_distance(real_t p_min_distance); - real_t get_min_distance() const { return min_distance; } - - void set_max_iterations(int p_iterations); - int get_max_iterations() const { return max_iterations; } - - Skeleton3D *get_parent_skeleton() const { return skeleton; } - - bool is_running(); - - void start(bool p_one_time = false); - void stop(); - -private: - Transform _get_target_transform(); - void reload_chain(); - void reload_goal(); - void _solve_chain(); -}; - -#endif // _3D_DISABLED - -#endif // SKELETON_IK_H diff --git a/scene/gui/control.h b/scene/gui/control.h index c52e80fa70..d02fea20a6 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -33,8 +33,8 @@ #include "core/math/transform_2d.h" #include "core/rid.h" -#include "scene/2d/canvas_item.h" #include "scene/gui/shortcut.h" +#include "scene/main/canvas_item.h" #include "scene/main/node.h" #include "scene/main/timer.h" #include "scene/resources/theme.h" diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp new file mode 100644 index 0000000000..7a0fc3352b --- /dev/null +++ b/scene/main/canvas_item.cpp @@ -0,0 +1,1491 @@ +/*************************************************************************/ +/* canvas_item.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "canvas_item.h" + +#include "core/input/input_filter.h" +#include "core/message_queue.h" +#include "core/method_bind_ext.gen.inc" +#include "scene/main/canvas_layer.h" +#include "scene/main/viewport.h" +#include "scene/main/window.h" +#include "scene/resources/font.h" +#include "scene/resources/style_box.h" +#include "scene/resources/texture.h" +#include "scene/scene_string_names.h" +#include "servers/visual/visual_server_raster.h" +#include "servers/visual_server.h" + +Mutex CanvasItemMaterial::material_mutex; +SelfList::List *CanvasItemMaterial::dirty_materials = NULL; +Map CanvasItemMaterial::shader_map; +CanvasItemMaterial::ShaderNames *CanvasItemMaterial::shader_names = NULL; + +void CanvasItemMaterial::init_shaders() { + + dirty_materials = memnew(SelfList::List); + + shader_names = memnew(ShaderNames); + + shader_names->particles_anim_h_frames = "particles_anim_h_frames"; + shader_names->particles_anim_v_frames = "particles_anim_v_frames"; + shader_names->particles_anim_loop = "particles_anim_loop"; +} + +void CanvasItemMaterial::finish_shaders() { + + memdelete(dirty_materials); + memdelete(shader_names); + dirty_materials = NULL; +} + +void CanvasItemMaterial::_update_shader() { + + dirty_materials->remove(&element); + + MaterialKey mk = _compute_key(); + if (mk.key == current_key.key) + return; //no update required in the end + + if (shader_map.has(current_key)) { + shader_map[current_key].users--; + if (shader_map[current_key].users == 0) { + //deallocate shader, as it's no longer in use + VS::get_singleton()->free(shader_map[current_key].shader); + shader_map.erase(current_key); + } + } + + current_key = mk; + + if (shader_map.has(mk)) { + + VS::get_singleton()->material_set_shader(_get_material(), shader_map[mk].shader); + shader_map[mk].users++; + return; + } + + //must create a shader! + + String code = "shader_type canvas_item;\nrender_mode "; + switch (blend_mode) { + case BLEND_MODE_MIX: code += "blend_mix"; break; + case BLEND_MODE_ADD: code += "blend_add"; break; + case BLEND_MODE_SUB: code += "blend_sub"; break; + case BLEND_MODE_MUL: code += "blend_mul"; break; + case BLEND_MODE_PREMULT_ALPHA: code += "blend_premul_alpha"; break; + case BLEND_MODE_DISABLED: code += "blend_disabled"; break; + } + + switch (light_mode) { + case LIGHT_MODE_NORMAL: break; + case LIGHT_MODE_UNSHADED: code += ",unshaded"; break; + case LIGHT_MODE_LIGHT_ONLY: code += ",light_only"; break; + } + + code += ";\n"; + + if (particles_animation) { + + code += "uniform int particles_anim_h_frames;\n"; + code += "uniform int particles_anim_v_frames;\n"; + code += "uniform bool particles_anim_loop;\n"; + + code += "void vertex() {\n"; + + code += "\tfloat h_frames = float(particles_anim_h_frames);\n"; + code += "\tfloat v_frames = float(particles_anim_v_frames);\n"; + + code += "\tVERTEX.xy /= vec2(h_frames, v_frames);\n"; + + code += "\tfloat particle_total_frames = float(particles_anim_h_frames * particles_anim_v_frames);\n"; + code += "\tfloat particle_frame = floor(INSTANCE_CUSTOM.z * float(particle_total_frames));\n"; + code += "\tif (!particles_anim_loop) {\n"; + code += "\t\tparticle_frame = clamp(particle_frame, 0.0, particle_total_frames - 1.0);\n"; + code += "\t} else {\n"; + code += "\t\tparticle_frame = mod(particle_frame, particle_total_frames);\n"; + code += "\t}"; + code += "\tUV /= vec2(h_frames, v_frames);\n"; + code += "\tUV += vec2(mod(particle_frame, h_frames) / h_frames, floor(particle_frame / h_frames) / v_frames);\n"; + code += "}\n"; + } + + ShaderData shader_data; + shader_data.shader = VS::get_singleton()->shader_create(); + shader_data.users = 1; + + VS::get_singleton()->shader_set_code(shader_data.shader, code); + + shader_map[mk] = shader_data; + + VS::get_singleton()->material_set_shader(_get_material(), shader_data.shader); +} + +void CanvasItemMaterial::flush_changes() { + + MutexLock lock(material_mutex); + + while (dirty_materials->first()) { + + dirty_materials->first()->self()->_update_shader(); + } +} + +void CanvasItemMaterial::_queue_shader_change() { + + MutexLock lock(material_mutex); + + if (!element.in_list()) { + dirty_materials->add(&element); + } +} + +bool CanvasItemMaterial::_is_shader_dirty() const { + + MutexLock lock(material_mutex); + + return element.in_list(); +} +void CanvasItemMaterial::set_blend_mode(BlendMode p_blend_mode) { + + blend_mode = p_blend_mode; + _queue_shader_change(); +} + +CanvasItemMaterial::BlendMode CanvasItemMaterial::get_blend_mode() const { + return blend_mode; +} + +void CanvasItemMaterial::set_light_mode(LightMode p_light_mode) { + + light_mode = p_light_mode; + _queue_shader_change(); +} + +CanvasItemMaterial::LightMode CanvasItemMaterial::get_light_mode() const { + + return light_mode; +} + +void CanvasItemMaterial::set_particles_animation(bool p_particles_anim) { + particles_animation = p_particles_anim; + _queue_shader_change(); + _change_notify(); +} + +bool CanvasItemMaterial::get_particles_animation() const { + return particles_animation; +} + +void CanvasItemMaterial::set_particles_anim_h_frames(int p_frames) { + + particles_anim_h_frames = p_frames; + VS::get_singleton()->material_set_param(_get_material(), shader_names->particles_anim_h_frames, p_frames); +} + +int CanvasItemMaterial::get_particles_anim_h_frames() const { + + return particles_anim_h_frames; +} +void CanvasItemMaterial::set_particles_anim_v_frames(int p_frames) { + + particles_anim_v_frames = p_frames; + VS::get_singleton()->material_set_param(_get_material(), shader_names->particles_anim_v_frames, p_frames); +} + +int CanvasItemMaterial::get_particles_anim_v_frames() const { + + return particles_anim_v_frames; +} + +void CanvasItemMaterial::set_particles_anim_loop(bool p_loop) { + + particles_anim_loop = p_loop; + VS::get_singleton()->material_set_param(_get_material(), shader_names->particles_anim_loop, particles_anim_loop); +} + +bool CanvasItemMaterial::get_particles_anim_loop() const { + + return particles_anim_loop; +} + +void CanvasItemMaterial::_validate_property(PropertyInfo &property) const { + if (property.name.begins_with("particles_anim_") && !particles_animation) { + property.usage = 0; + } +} + +RID CanvasItemMaterial::get_shader_rid() const { + + ERR_FAIL_COND_V(!shader_map.has(current_key), RID()); + return shader_map[current_key].shader; +} + +Shader::Mode CanvasItemMaterial::get_shader_mode() const { + + return Shader::MODE_CANVAS_ITEM; +} + +void CanvasItemMaterial::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_blend_mode", "blend_mode"), &CanvasItemMaterial::set_blend_mode); + ClassDB::bind_method(D_METHOD("get_blend_mode"), &CanvasItemMaterial::get_blend_mode); + + ClassDB::bind_method(D_METHOD("set_light_mode", "light_mode"), &CanvasItemMaterial::set_light_mode); + ClassDB::bind_method(D_METHOD("get_light_mode"), &CanvasItemMaterial::get_light_mode); + + ClassDB::bind_method(D_METHOD("set_particles_animation", "particles_anim"), &CanvasItemMaterial::set_particles_animation); + ClassDB::bind_method(D_METHOD("get_particles_animation"), &CanvasItemMaterial::get_particles_animation); + + ClassDB::bind_method(D_METHOD("set_particles_anim_h_frames", "frames"), &CanvasItemMaterial::set_particles_anim_h_frames); + ClassDB::bind_method(D_METHOD("get_particles_anim_h_frames"), &CanvasItemMaterial::get_particles_anim_h_frames); + + ClassDB::bind_method(D_METHOD("set_particles_anim_v_frames", "frames"), &CanvasItemMaterial::set_particles_anim_v_frames); + ClassDB::bind_method(D_METHOD("get_particles_anim_v_frames"), &CanvasItemMaterial::get_particles_anim_v_frames); + + ClassDB::bind_method(D_METHOD("set_particles_anim_loop", "loop"), &CanvasItemMaterial::set_particles_anim_loop); + ClassDB::bind_method(D_METHOD("get_particles_anim_loop"), &CanvasItemMaterial::get_particles_anim_loop); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_mode", PROPERTY_HINT_ENUM, "Mix,Add,Sub,Mul,Premult Alpha"), "set_blend_mode", "get_blend_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "light_mode", PROPERTY_HINT_ENUM, "Normal,Unshaded,Light Only"), "set_light_mode", "get_light_mode"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "particles_animation"), "set_particles_animation", "get_particles_animation"); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "particles_anim_h_frames", PROPERTY_HINT_RANGE, "1,128,1"), "set_particles_anim_h_frames", "get_particles_anim_h_frames"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "particles_anim_v_frames", PROPERTY_HINT_RANGE, "1,128,1"), "set_particles_anim_v_frames", "get_particles_anim_v_frames"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "particles_anim_loop"), "set_particles_anim_loop", "get_particles_anim_loop"); + + BIND_ENUM_CONSTANT(BLEND_MODE_MIX); + BIND_ENUM_CONSTANT(BLEND_MODE_ADD); + BIND_ENUM_CONSTANT(BLEND_MODE_SUB); + BIND_ENUM_CONSTANT(BLEND_MODE_MUL); + BIND_ENUM_CONSTANT(BLEND_MODE_PREMULT_ALPHA); + + BIND_ENUM_CONSTANT(LIGHT_MODE_NORMAL); + BIND_ENUM_CONSTANT(LIGHT_MODE_UNSHADED); + BIND_ENUM_CONSTANT(LIGHT_MODE_LIGHT_ONLY); +} + +CanvasItemMaterial::CanvasItemMaterial() : + element(this) { + + blend_mode = BLEND_MODE_MIX; + light_mode = LIGHT_MODE_NORMAL; + particles_animation = false; + + set_particles_anim_h_frames(1); + set_particles_anim_v_frames(1); + set_particles_anim_loop(false); + + current_key.key = 0; + current_key.invalid_key = 1; + _queue_shader_change(); +} + +CanvasItemMaterial::~CanvasItemMaterial() { + + MutexLock lock(material_mutex); + + if (shader_map.has(current_key)) { + shader_map[current_key].users--; + if (shader_map[current_key].users == 0) { + //deallocate shader, as it's no longer in use + VS::get_singleton()->free(shader_map[current_key].shader); + shader_map.erase(current_key); + } + + VS::get_singleton()->material_set_shader(_get_material(), RID()); + } +} + +/////////////////////////////////////////////////////////////////// +#ifdef TOOLS_ENABLED +bool CanvasItem::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { + if (_edit_use_rect()) { + return _edit_get_rect().has_point(p_point); + } else { + return p_point.length() < p_tolerance; + } +} + +Transform2D CanvasItem::_edit_get_transform() const { + return Transform2D(_edit_get_rotation(), _edit_get_position() + _edit_get_pivot()); +} +#endif + +bool CanvasItem::is_visible_in_tree() const { + + if (!is_inside_tree()) + return false; + + const CanvasItem *p = this; + + while (p) { + if (!p->visible) + return false; + if (p->window && !p->window->is_visible()) { + return false; + } + p = p->get_parent_item(); + } + + return true; +} + +void CanvasItem::_propagate_visibility_changed(bool p_visible) { + + if (p_visible && first_draw) { //avoid propagating it twice + first_draw = false; + } + notification(NOTIFICATION_VISIBILITY_CHANGED); + + if (p_visible) + update(); //todo optimize + else + emit_signal(SceneStringNames::get_singleton()->hide); + _block(); + + for (int i = 0; i < get_child_count(); i++) { + + CanvasItem *c = Object::cast_to(get_child(i)); + + if (c && c->visible) //should the toplevels stop propagation? i think so but.. + c->_propagate_visibility_changed(p_visible); + } + + _unblock(); +} + +void CanvasItem::show() { + + if (visible) + return; + + visible = true; + VisualServer::get_singleton()->canvas_item_set_visible(canvas_item, true); + + if (!is_inside_tree()) + return; + + _propagate_visibility_changed(true); + _change_notify("visible"); +} + +void CanvasItem::hide() { + + if (!visible) + return; + + visible = false; + VisualServer::get_singleton()->canvas_item_set_visible(canvas_item, false); + + if (!is_inside_tree()) + return; + + _propagate_visibility_changed(false); + _change_notify("visible"); +} + +CanvasItem *CanvasItem::current_item_drawn = NULL; +CanvasItem *CanvasItem::get_current_item_drawn() { + return current_item_drawn; +} + +void CanvasItem::_update_callback() { + + if (!is_inside_tree()) { + pending_update = false; + return; + } + + VisualServer::get_singleton()->canvas_item_clear(get_canvas_item()); + //todo updating = true - only allow drawing here + if (is_visible_in_tree()) { //todo optimize this!! + if (first_draw) { + notification(NOTIFICATION_VISIBILITY_CHANGED); + first_draw = false; + } + drawing = true; + current_item_drawn = this; + notification(NOTIFICATION_DRAW); + emit_signal(SceneStringNames::get_singleton()->draw); + if (get_script_instance()) { + get_script_instance()->call_multilevel_reversed(SceneStringNames::get_singleton()->_draw, NULL, 0); + } + current_item_drawn = NULL; + drawing = false; + } + //todo updating = false + pending_update = false; // don't change to false until finished drawing (avoid recursive update) +} + +Transform2D CanvasItem::get_global_transform_with_canvas() const { + + if (canvas_layer) + return canvas_layer->get_transform() * get_global_transform(); + else if (is_inside_tree()) + return get_viewport()->get_canvas_transform() * get_global_transform(); + else + return get_global_transform(); +} + +Transform2D CanvasItem::get_screen_transform() const { + ERR_FAIL_COND_V(!is_inside_tree(), Transform2D()); + Transform2D xform = get_global_transform_with_canvas(); + + Window *w = Object::cast_to(get_viewport()); + if (w && !w->is_embedding_subwindows()) { + Transform2D s; + s.set_origin(w->get_position()); + + xform = s * xform; + } + + return xform; +} + +Transform2D CanvasItem::get_global_transform() const { +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_V(!is_inside_tree(), get_transform()); +#endif + if (global_invalid) { + + const CanvasItem *pi = get_parent_item(); + if (pi) + global_transform = pi->get_global_transform() * get_transform(); + else + global_transform = get_transform(); + + global_invalid = false; + } + + return global_transform; +} + +void CanvasItem::_toplevel_raise_self() { + + if (!is_inside_tree()) + return; + + if (canvas_layer) + VisualServer::get_singleton()->canvas_item_set_draw_index(canvas_item, canvas_layer->get_sort_index()); + else + VisualServer::get_singleton()->canvas_item_set_draw_index(canvas_item, get_viewport()->gui_get_canvas_sort_index()); +} + +void CanvasItem::_enter_canvas() { + + if ((!Object::cast_to(get_parent())) || toplevel) { + + Node *n = this; + + canvas_layer = NULL; + + while (n) { + + canvas_layer = Object::cast_to(n); + if (canvas_layer) { + break; + } + if (Object::cast_to(n)) { + break; + } + n = n->get_parent(); + } + + RID canvas; + if (canvas_layer) + canvas = canvas_layer->get_canvas(); + else + canvas = get_viewport()->find_world_2d()->get_canvas(); + + VisualServer::get_singleton()->canvas_item_set_parent(canvas_item, canvas); + + group = "root_canvas" + itos(canvas.get_id()); + + add_to_group(group); + if (canvas_layer) + canvas_layer->reset_sort_index(); + else + get_viewport()->gui_reset_canvas_sort_index(); + + get_tree()->call_group_flags(SceneTree::GROUP_CALL_UNIQUE, group, "_toplevel_raise_self"); + + } else { + + CanvasItem *parent = get_parent_item(); + canvas_layer = parent->canvas_layer; + VisualServer::get_singleton()->canvas_item_set_parent(canvas_item, parent->get_canvas_item()); + VisualServer::get_singleton()->canvas_item_set_draw_index(canvas_item, get_index()); + } + + pending_update = false; + update(); + + notification(NOTIFICATION_ENTER_CANVAS); +} + +void CanvasItem::_exit_canvas() { + + notification(NOTIFICATION_EXIT_CANVAS, true); //reverse the notification + VisualServer::get_singleton()->canvas_item_set_parent(canvas_item, RID()); + canvas_layer = NULL; + group = ""; +} + +void CanvasItem::_notification(int p_what) { + + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + + _update_texture_filter_changed(false); + _update_texture_repeat_changed(false); + + first_draw = true; + Node *parent = get_parent(); + if (parent) { + CanvasItem *ci = Object::cast_to(parent); + if (ci) + C = ci->children_items.push_back(this); + if (!ci) { + //look for a window + Viewport *viewport = nullptr; + + while (parent) { + viewport = Object::cast_to(parent); + if (viewport) { + break; + } + parent = parent->get_parent(); + } + + ERR_FAIL_COND(!viewport); + + window = Object::cast_to(viewport); + if (window) { + window->connect(SceneStringNames::get_singleton()->visibility_changed, callable_mp(this, &CanvasItem::_window_visibility_changed)); + } + } + } + _enter_canvas(); + if (!block_transform_notify && !xform_change.in_list()) { + get_tree()->xform_change_list.add(&xform_change); + } + } break; + case NOTIFICATION_MOVED_IN_PARENT: { + + if (!is_inside_tree()) + break; + + if (group != "") { + get_tree()->call_group_flags(SceneTree::GROUP_CALL_UNIQUE, group, "_toplevel_raise_self"); + } else { + CanvasItem *p = get_parent_item(); + ERR_FAIL_COND(!p); + VisualServer::get_singleton()->canvas_item_set_draw_index(canvas_item, get_index()); + } + + } break; + case NOTIFICATION_EXIT_TREE: { + if (xform_change.in_list()) + get_tree()->xform_change_list.remove(&xform_change); + _exit_canvas(); + if (C) { + Object::cast_to(get_parent())->children_items.erase(C); + C = NULL; + } + if (window) { + window->disconnect(SceneStringNames::get_singleton()->visibility_changed, callable_mp(this, &CanvasItem::_window_visibility_changed)); + } + global_invalid = true; + } break; + case NOTIFICATION_DRAW: + case NOTIFICATION_TRANSFORM_CHANGED: { + + } break; + case NOTIFICATION_VISIBILITY_CHANGED: { + + emit_signal(SceneStringNames::get_singleton()->visibility_changed); + } break; + } +} + +void CanvasItem::set_visible(bool p_visible) { + + if (p_visible) + show(); + else + hide(); +} + +void CanvasItem::_window_visibility_changed() { + + if (visible) { + _propagate_visibility_changed(window->is_visible()); + } +} + +bool CanvasItem::is_visible() const { + + return visible; +} + +void CanvasItem::update() { + + if (!is_inside_tree()) + return; + if (pending_update) + return; + + pending_update = true; + + MessageQueue::get_singleton()->push_call(this, "_update_callback"); +} + +void CanvasItem::set_modulate(const Color &p_modulate) { + + if (modulate == p_modulate) + return; + + modulate = p_modulate; + VisualServer::get_singleton()->canvas_item_set_modulate(canvas_item, modulate); +} +Color CanvasItem::get_modulate() const { + + return modulate; +} + +void CanvasItem::set_as_toplevel(bool p_toplevel) { + + if (toplevel == p_toplevel) + return; + + if (!is_inside_tree()) { + toplevel = p_toplevel; + return; + } + + _exit_canvas(); + toplevel = p_toplevel; + _enter_canvas(); +} + +bool CanvasItem::is_set_as_toplevel() const { + + return toplevel; +} + +CanvasItem *CanvasItem::get_parent_item() const { + + if (toplevel) + return NULL; + + return Object::cast_to(get_parent()); +} + +void CanvasItem::set_self_modulate(const Color &p_self_modulate) { + + if (self_modulate == p_self_modulate) + return; + + self_modulate = p_self_modulate; + VisualServer::get_singleton()->canvas_item_set_self_modulate(canvas_item, self_modulate); +} +Color CanvasItem::get_self_modulate() const { + + return self_modulate; +} + +void CanvasItem::set_light_mask(int p_light_mask) { + + if (light_mask == p_light_mask) + return; + + light_mask = p_light_mask; + VS::get_singleton()->canvas_item_set_light_mask(canvas_item, p_light_mask); +} + +int CanvasItem::get_light_mask() const { + + return light_mask; +} + +void CanvasItem::item_rect_changed(bool p_size_changed) { + + if (p_size_changed) + update(); + emit_signal(SceneStringNames::get_singleton()->item_rect_changed); +} + +void CanvasItem::draw_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, float p_width) { + + ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + + VisualServer::get_singleton()->canvas_item_add_line(canvas_item, p_from, p_to, p_color, p_width); +} + +void CanvasItem::draw_polyline(const Vector &p_points, const Color &p_color, float p_width) { + + ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + + Vector colors; + colors.push_back(p_color); + VisualServer::get_singleton()->canvas_item_add_polyline(canvas_item, p_points, colors, p_width); +} + +void CanvasItem::draw_polyline_colors(const Vector &p_points, const Vector &p_colors, float p_width) { + + ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + + VisualServer::get_singleton()->canvas_item_add_polyline(canvas_item, p_points, p_colors, p_width); +} + +void CanvasItem::draw_arc(const Vector2 &p_center, float p_radius, float p_start_angle, float p_end_angle, int p_point_count, const Color &p_color, float p_width) { + + Vector points; + points.resize(p_point_count); + const float delta_angle = p_end_angle - p_start_angle; + for (int i = 0; i < p_point_count; i++) { + float theta = (i / (p_point_count - 1.0f)) * delta_angle + p_start_angle; + points.set(i, p_center + Vector2(Math::cos(theta), Math::sin(theta)) * p_radius); + } + + draw_polyline(points, p_color, p_width); +} + +void CanvasItem::draw_multiline(const Vector &p_points, const Color &p_color, float p_width) { + + ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + + Vector colors; + colors.push_back(p_color); + VisualServer::get_singleton()->canvas_item_add_multiline(canvas_item, p_points, colors, p_width); +} + +void CanvasItem::draw_multiline_colors(const Vector &p_points, const Vector &p_colors, float p_width) { + + ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + + VisualServer::get_singleton()->canvas_item_add_multiline(canvas_item, p_points, p_colors, p_width); +} + +void CanvasItem::draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_filled, float p_width) { + + ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + + if (p_filled) { + if (p_width != 1.0) { + WARN_PRINT("The draw_rect() \"width\" argument has no effect when \"filled\" is \"true\"."); + } + + VisualServer::get_singleton()->canvas_item_add_rect(canvas_item, p_rect, p_color); + } else { + // Thick lines are offset depending on their width to avoid partial overlapping. + // Thin lines don't require an offset, so don't apply one in this case + float offset; + if (p_width >= 2) { + offset = p_width / 2.0; + } else { + offset = 0.0; + } + + VisualServer::get_singleton()->canvas_item_add_line( + canvas_item, + p_rect.position + Size2(-offset, 0), + p_rect.position + Size2(p_rect.size.width + offset, 0), + p_color, + p_width); + VisualServer::get_singleton()->canvas_item_add_line( + canvas_item, + p_rect.position + Size2(p_rect.size.width, offset), + p_rect.position + Size2(p_rect.size.width, p_rect.size.height - offset), + p_color, + p_width); + VisualServer::get_singleton()->canvas_item_add_line( + canvas_item, + p_rect.position + Size2(p_rect.size.width + offset, p_rect.size.height), + p_rect.position + Size2(-offset, p_rect.size.height), + p_color, + p_width); + VisualServer::get_singleton()->canvas_item_add_line( + canvas_item, + p_rect.position + Size2(0, p_rect.size.height - offset), + p_rect.position + Size2(0, offset), + p_color, + p_width); + } +} + +void CanvasItem::draw_circle(const Point2 &p_pos, float p_radius, const Color &p_color) { + + ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + + VisualServer::get_singleton()->canvas_item_add_circle(canvas_item, p_pos, p_radius, p_color); +} + +void CanvasItem::draw_texture(const Ref &p_texture, const Point2 &p_pos, const Color &p_modulate, const Ref &p_normal_map, const Ref &p_specular_map, const Color &p_specular_color_shininess, TextureFilter p_texture_filter, TextureRepeat p_texture_repeat) { + + ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + + ERR_FAIL_COND(p_texture.is_null()); + + p_texture->draw(canvas_item, p_pos, p_modulate, false, p_normal_map, p_specular_map, p_specular_color_shininess, VS::CanvasItemTextureFilter(p_texture_filter), VS::CanvasItemTextureRepeat(p_texture_repeat)); +} + +void CanvasItem::draw_texture_rect(const Ref &p_texture, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose, const Ref &p_normal_map, const Ref &p_specular_map, const Color &p_specular_color_shininess, TextureFilter p_texture_filter, TextureRepeat p_texture_repeat) { + + ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + + ERR_FAIL_COND(p_texture.is_null()); + p_texture->draw_rect(canvas_item, p_rect, p_tile, p_modulate, p_transpose, p_normal_map, p_specular_map, p_specular_color_shininess, VS::CanvasItemTextureFilter(p_texture_filter), VS::CanvasItemTextureRepeat(p_texture_repeat)); +} +void CanvasItem::draw_texture_rect_region(const Ref &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, const Ref &p_normal_map, const Ref &p_specular_map, const Color &p_specular_color_shininess, bool p_clip_uv, TextureFilter p_texture_filter, TextureRepeat p_texture_repeat) { + + ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + ERR_FAIL_COND(p_texture.is_null()); + p_texture->draw_rect_region(canvas_item, p_rect, p_src_rect, p_modulate, p_transpose, p_normal_map, p_specular_map, p_specular_color_shininess, VS::CanvasItemTextureFilter(p_texture_filter), VS::CanvasItemTextureRepeat(p_texture_repeat), p_clip_uv); +} + +void CanvasItem::draw_style_box(const Ref &p_style_box, const Rect2 &p_rect) { + ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + + ERR_FAIL_COND(p_style_box.is_null()); + + p_style_box->draw(canvas_item, p_rect); +} +void CanvasItem::draw_primitive(const Vector &p_points, const Vector &p_colors, const Vector &p_uvs, Ref p_texture, float p_width, const Ref &p_normal_map, const Ref &p_specular_map, const Color &p_specular_color_shininess, TextureFilter p_texture_filter, TextureRepeat p_texture_repeat) { + + ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + + RID rid = p_texture.is_valid() ? p_texture->get_rid() : RID(); + RID rid_normal = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID(); + RID rid_specular = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID(); + + VisualServer::get_singleton()->canvas_item_add_primitive(canvas_item, p_points, p_colors, p_uvs, rid, p_width, rid_normal, rid_specular, p_specular_color_shininess, VS::CanvasItemTextureFilter(p_texture_filter), VS::CanvasItemTextureRepeat(p_texture_repeat)); +} +void CanvasItem::draw_set_transform(const Point2 &p_offset, float p_rot, const Size2 &p_scale) { + + ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + + Transform2D xform(p_rot, p_offset); + xform.scale_basis(p_scale); + VisualServer::get_singleton()->canvas_item_add_set_transform(canvas_item, xform); +} + +void CanvasItem::draw_set_transform_matrix(const Transform2D &p_matrix) { + + ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + + VisualServer::get_singleton()->canvas_item_add_set_transform(canvas_item, p_matrix); +} + +void CanvasItem::draw_polygon(const Vector &p_points, const Vector &p_colors, const Vector &p_uvs, Ref p_texture, const Ref &p_normal_map, const Ref &p_specular_map, const Color &p_specular_color_shininess, TextureFilter p_texture_filter, TextureRepeat p_texture_repeat) { + + ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + + RID rid = p_texture.is_valid() ? p_texture->get_rid() : RID(); + RID rid_normal = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID(); + RID rid_specular = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID(); + + VisualServer::get_singleton()->canvas_item_add_polygon(canvas_item, p_points, p_colors, p_uvs, rid, rid_normal, rid_specular, p_specular_color_shininess, VS::CanvasItemTextureFilter(p_texture_filter), VS::CanvasItemTextureRepeat(p_texture_repeat)); +} + +void CanvasItem::draw_colored_polygon(const Vector &p_points, const Color &p_color, const Vector &p_uvs, Ref p_texture, const Ref &p_normal_map, const Ref &p_specular_map, const Color &p_specular_color_shininess, TextureFilter p_texture_filter, TextureRepeat p_texture_repeat) { + + ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + + Vector colors; + colors.push_back(p_color); + RID rid = p_texture.is_valid() ? p_texture->get_rid() : RID(); + RID rid_normal = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID(); + RID rid_specular = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID(); + + VisualServer::get_singleton()->canvas_item_add_polygon(canvas_item, p_points, colors, p_uvs, rid, rid_normal, rid_specular, p_specular_color_shininess, VS::CanvasItemTextureFilter(p_texture_filter), VS::CanvasItemTextureRepeat(p_texture_repeat)); +} + +void CanvasItem::draw_mesh(const Ref &p_mesh, const Ref &p_texture, const Ref &p_normal_map, const Ref &p_specular_map, const Color &p_specular_color_shininess, const Transform2D &p_transform, const Color &p_modulate, TextureFilter p_texture_filter, TextureRepeat p_texture_repeat) { + + ERR_FAIL_COND(p_mesh.is_null()); + RID texture_rid = p_texture.is_valid() ? p_texture->get_rid() : RID(); + RID normal_map_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID(); + RID specular_map_rid = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID(); + + VisualServer::get_singleton()->canvas_item_add_mesh(canvas_item, p_mesh->get_rid(), p_transform, p_modulate, texture_rid, normal_map_rid, specular_map_rid, p_specular_color_shininess, VS::CanvasItemTextureFilter(p_texture_filter), VS::CanvasItemTextureRepeat(p_texture_repeat)); +} +void CanvasItem::draw_multimesh(const Ref &p_multimesh, const Ref &p_texture, const Ref &p_normal_map, const Ref &p_specular_map, const Color &p_specular_color_shininess, TextureFilter p_texture_filter, TextureRepeat p_texture_repeat) { + + ERR_FAIL_COND(p_multimesh.is_null()); + RID texture_rid = p_texture.is_valid() ? p_texture->get_rid() : RID(); + RID normal_map_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID(); + RID specular_map_rid = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID(); + + VisualServer::get_singleton()->canvas_item_add_multimesh(canvas_item, p_multimesh->get_rid(), texture_rid, normal_map_rid, specular_map_rid, p_specular_color_shininess, VS::CanvasItemTextureFilter(p_texture_filter), VS::CanvasItemTextureRepeat(p_texture_repeat)); +} + +void CanvasItem::draw_string(const Ref &p_font, const Point2 &p_pos, const String &p_text, const Color &p_modulate, int p_clip_w) { + + ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + + ERR_FAIL_COND(p_font.is_null()); + p_font->draw(canvas_item, p_pos, p_text, p_modulate, p_clip_w); +} + +float CanvasItem::draw_char(const Ref &p_font, const Point2 &p_pos, const String &p_char, const String &p_next, const Color &p_modulate) { + + ERR_FAIL_COND_V_MSG(!drawing, 0, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + + ERR_FAIL_COND_V(p_char.length() != 1, 0); + ERR_FAIL_COND_V(p_font.is_null(), 0); + + if (p_font->has_outline()) { + p_font->draw_char(canvas_item, p_pos, p_char[0], p_next.c_str()[0], Color(1, 1, 1), true); + } + return p_font->draw_char(canvas_item, p_pos, p_char[0], p_next.c_str()[0], p_modulate); +} + +void CanvasItem::_notify_transform(CanvasItem *p_node) { + + /* This check exists to avoid re-propagating the transform + * notification down the tree on dirty nodes. It provides + * optimization by avoiding redundancy (nodes are dirty, will get the + * notification anyway). + */ + + if (/*p_node->xform_change.in_list() &&*/ p_node->global_invalid) { + return; //nothing to do + } + + p_node->global_invalid = true; + + if (p_node->notify_transform && !p_node->xform_change.in_list()) { + if (!p_node->block_transform_notify) { + if (p_node->is_inside_tree()) + get_tree()->xform_change_list.add(&p_node->xform_change); + } + } + + for (List::Element *E = p_node->children_items.front(); E; E = E->next()) { + + CanvasItem *ci = E->get(); + if (ci->toplevel) + continue; + _notify_transform(ci); + } +} + +Rect2 CanvasItem::get_viewport_rect() const { + + ERR_FAIL_COND_V(!is_inside_tree(), Rect2()); + return get_viewport()->get_visible_rect(); +} + +RID CanvasItem::get_canvas() const { + + ERR_FAIL_COND_V(!is_inside_tree(), RID()); + + if (canvas_layer) + return canvas_layer->get_canvas(); + else + return get_viewport()->find_world_2d()->get_canvas(); +} + +ObjectID CanvasItem::get_canvas_layer_instance_id() const { + + if (canvas_layer) { + return canvas_layer->get_instance_id(); + } else { + return ObjectID(); + } +} + +CanvasItem *CanvasItem::get_toplevel() const { + + CanvasItem *ci = const_cast(this); + while (!ci->toplevel && Object::cast_to(ci->get_parent())) { + ci = Object::cast_to(ci->get_parent()); + } + + return ci; +} + +Ref CanvasItem::get_world_2d() const { + + ERR_FAIL_COND_V(!is_inside_tree(), Ref()); + + CanvasItem *tl = get_toplevel(); + + if (tl->get_viewport()) { + return tl->get_viewport()->find_world_2d(); + } else { + return Ref(); + } +} + +RID CanvasItem::get_viewport_rid() const { + + ERR_FAIL_COND_V(!is_inside_tree(), RID()); + return get_viewport()->get_viewport_rid(); +} + +void CanvasItem::set_block_transform_notify(bool p_enable) { + block_transform_notify = p_enable; +} + +bool CanvasItem::is_block_transform_notify_enabled() const { + + return block_transform_notify; +} + +void CanvasItem::set_draw_behind_parent(bool p_enable) { + + if (behind == p_enable) + return; + behind = p_enable; + VisualServer::get_singleton()->canvas_item_set_draw_behind_parent(canvas_item, behind); +} + +bool CanvasItem::is_draw_behind_parent_enabled() const { + + return behind; +} + +void CanvasItem::set_material(const Ref &p_material) { + + material = p_material; + RID rid; + if (material.is_valid()) + rid = material->get_rid(); + VS::get_singleton()->canvas_item_set_material(canvas_item, rid); + _change_notify(); //properties for material exposed +} + +void CanvasItem::set_use_parent_material(bool p_use_parent_material) { + + use_parent_material = p_use_parent_material; + VS::get_singleton()->canvas_item_set_use_parent_material(canvas_item, p_use_parent_material); +} + +bool CanvasItem::get_use_parent_material() const { + + return use_parent_material; +} + +Ref CanvasItem::get_material() const { + + return material; +} + +Vector2 CanvasItem::make_canvas_position_local(const Vector2 &screen_point) const { + + ERR_FAIL_COND_V(!is_inside_tree(), screen_point); + + Transform2D local_matrix = (get_canvas_transform() * get_global_transform()).affine_inverse(); + + return local_matrix.xform(screen_point); +} + +Ref CanvasItem::make_input_local(const Ref &p_event) const { + + ERR_FAIL_COND_V(p_event.is_null(), p_event); + ERR_FAIL_COND_V(!is_inside_tree(), p_event); + + return p_event->xformed_by((get_canvas_transform() * get_global_transform()).affine_inverse()); +} + +Vector2 CanvasItem::get_global_mouse_position() const { + + ERR_FAIL_COND_V(!get_viewport(), Vector2()); + return get_canvas_transform().affine_inverse().xform(get_viewport()->get_mouse_position()); +} + +Vector2 CanvasItem::get_local_mouse_position() const { + + ERR_FAIL_COND_V(!get_viewport(), Vector2()); + + return get_global_transform().affine_inverse().xform(get_global_mouse_position()); +} + +void CanvasItem::force_update_transform() { + ERR_FAIL_COND(!is_inside_tree()); + if (!xform_change.in_list()) { + return; + } + + get_tree()->xform_change_list.remove(&xform_change); + + notification(NOTIFICATION_TRANSFORM_CHANGED); +} + +void CanvasItem::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_toplevel_raise_self"), &CanvasItem::_toplevel_raise_self); + ClassDB::bind_method(D_METHOD("_update_callback"), &CanvasItem::_update_callback); + +#ifdef TOOLS_ENABLED + ClassDB::bind_method(D_METHOD("_edit_set_state", "state"), &CanvasItem::_edit_set_state); + ClassDB::bind_method(D_METHOD("_edit_get_state"), &CanvasItem::_edit_get_state); + ClassDB::bind_method(D_METHOD("_edit_set_position", "position"), &CanvasItem::_edit_set_position); + ClassDB::bind_method(D_METHOD("_edit_get_position"), &CanvasItem::_edit_get_position); + ClassDB::bind_method(D_METHOD("_edit_set_scale", "scale"), &CanvasItem::_edit_set_scale); + ClassDB::bind_method(D_METHOD("_edit_get_scale"), &CanvasItem::_edit_get_scale); + ClassDB::bind_method(D_METHOD("_edit_set_rect", "rect"), &CanvasItem::_edit_set_rect); + ClassDB::bind_method(D_METHOD("_edit_get_rect"), &CanvasItem::_edit_get_rect); + ClassDB::bind_method(D_METHOD("_edit_use_rect"), &CanvasItem::_edit_use_rect); + ClassDB::bind_method(D_METHOD("_edit_set_rotation", "degrees"), &CanvasItem::_edit_set_rotation); + ClassDB::bind_method(D_METHOD("_edit_get_rotation"), &CanvasItem::_edit_get_rotation); + ClassDB::bind_method(D_METHOD("_edit_use_rotation"), &CanvasItem::_edit_use_rotation); + ClassDB::bind_method(D_METHOD("_edit_set_pivot", "pivot"), &CanvasItem::_edit_set_pivot); + ClassDB::bind_method(D_METHOD("_edit_get_pivot"), &CanvasItem::_edit_get_pivot); + ClassDB::bind_method(D_METHOD("_edit_use_pivot"), &CanvasItem::_edit_use_pivot); + ClassDB::bind_method(D_METHOD("_edit_get_transform"), &CanvasItem::_edit_get_transform); +#endif + + ClassDB::bind_method(D_METHOD("get_canvas_item"), &CanvasItem::get_canvas_item); + + ClassDB::bind_method(D_METHOD("set_visible", "visible"), &CanvasItem::set_visible); + ClassDB::bind_method(D_METHOD("is_visible"), &CanvasItem::is_visible); + ClassDB::bind_method(D_METHOD("is_visible_in_tree"), &CanvasItem::is_visible_in_tree); + ClassDB::bind_method(D_METHOD("show"), &CanvasItem::show); + ClassDB::bind_method(D_METHOD("hide"), &CanvasItem::hide); + + ClassDB::bind_method(D_METHOD("update"), &CanvasItem::update); + + ClassDB::bind_method(D_METHOD("set_as_toplevel", "enable"), &CanvasItem::set_as_toplevel); + ClassDB::bind_method(D_METHOD("is_set_as_toplevel"), &CanvasItem::is_set_as_toplevel); + + ClassDB::bind_method(D_METHOD("set_light_mask", "light_mask"), &CanvasItem::set_light_mask); + ClassDB::bind_method(D_METHOD("get_light_mask"), &CanvasItem::get_light_mask); + + ClassDB::bind_method(D_METHOD("set_modulate", "modulate"), &CanvasItem::set_modulate); + ClassDB::bind_method(D_METHOD("get_modulate"), &CanvasItem::get_modulate); + ClassDB::bind_method(D_METHOD("set_self_modulate", "self_modulate"), &CanvasItem::set_self_modulate); + ClassDB::bind_method(D_METHOD("get_self_modulate"), &CanvasItem::get_self_modulate); + + ClassDB::bind_method(D_METHOD("set_draw_behind_parent", "enable"), &CanvasItem::set_draw_behind_parent); + ClassDB::bind_method(D_METHOD("is_draw_behind_parent_enabled"), &CanvasItem::is_draw_behind_parent_enabled); + + ClassDB::bind_method(D_METHOD("_set_on_top", "on_top"), &CanvasItem::_set_on_top); + ClassDB::bind_method(D_METHOD("_is_on_top"), &CanvasItem::_is_on_top); + //ClassDB::bind_method(D_METHOD("get_transform"),&CanvasItem::get_transform); + + ClassDB::bind_method(D_METHOD("draw_line", "from", "to", "color", "width"), &CanvasItem::draw_line, DEFVAL(1.0)); + ClassDB::bind_method(D_METHOD("draw_polyline", "points", "color", "width"), &CanvasItem::draw_polyline, DEFVAL(1.0)); + ClassDB::bind_method(D_METHOD("draw_polyline_colors", "points", "colors", "width"), &CanvasItem::draw_polyline_colors, DEFVAL(1.0)); + ClassDB::bind_method(D_METHOD("draw_arc", "center", "radius", "start_angle", "end_angle", "point_count", "color", "width"), &CanvasItem::draw_arc, DEFVAL(1.0)); + ClassDB::bind_method(D_METHOD("draw_multiline", "points", "color", "width"), &CanvasItem::draw_multiline, DEFVAL(1.0)); + ClassDB::bind_method(D_METHOD("draw_multiline_colors", "points", "colors", "width"), &CanvasItem::draw_multiline_colors, DEFVAL(1.0)); + ClassDB::bind_method(D_METHOD("draw_rect", "rect", "color", "filled", "width"), &CanvasItem::draw_rect, DEFVAL(true), DEFVAL(1.0)); + ClassDB::bind_method(D_METHOD("draw_circle", "position", "radius", "color"), &CanvasItem::draw_circle); + ClassDB::bind_method(D_METHOD("draw_texture", "texture", "position", "modulate", "normal_map", "specular_map", "specular_shininess", "texture_filter", "texture_repeat"), &CanvasItem::draw_texture, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(Ref()), DEFVAL(Ref()), DEFVAL(Color(1, 1, 1, 1)), DEFVAL(TEXTURE_FILTER_PARENT_NODE), DEFVAL(TEXTURE_REPEAT_PARENT_NODE)); + ClassDB::bind_method(D_METHOD("draw_texture_rect", "texture", "rect", "tile", "modulate", "transpose", "normal_map", "specular_map", "specular_shininess", "texture_filter", "texture_repeat"), &CanvasItem::draw_texture_rect, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(false), DEFVAL(Ref()), DEFVAL(Ref()), DEFVAL(Color(1, 1, 1, 1)), DEFVAL(TEXTURE_FILTER_PARENT_NODE), DEFVAL(TEXTURE_REPEAT_PARENT_NODE)); + ClassDB::bind_method(D_METHOD("draw_texture_rect_region", "texture", "rect", "src_rect", "modulate", "transpose", "normal_map", "specular_map", "specular_shininess", "clip_uv", "texture_filter", "texture_repeat"), &CanvasItem::draw_texture_rect_region, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(false), DEFVAL(Ref()), DEFVAL(Ref()), DEFVAL(Color(1, 1, 1, 1)), DEFVAL(true), DEFVAL(TEXTURE_FILTER_PARENT_NODE), DEFVAL(TEXTURE_REPEAT_PARENT_NODE)); + ClassDB::bind_method(D_METHOD("draw_style_box", "style_box", "rect"), &CanvasItem::draw_style_box); + ClassDB::bind_method(D_METHOD("draw_primitive", "points", "colors", "uvs", "texture", "width", "normal_map", "specular_map", "specular_shininess", "texture_filter", "texture_repeat"), &CanvasItem::draw_primitive, DEFVAL(Ref()), DEFVAL(1.0), DEFVAL(Ref()), DEFVAL(Ref()), DEFVAL(Color(1, 1, 1, 1)), DEFVAL(TEXTURE_FILTER_PARENT_NODE), DEFVAL(TEXTURE_REPEAT_PARENT_NODE)); + ClassDB::bind_method(D_METHOD("draw_polygon", "points", "colors", "uvs", "texture", "normal_map", "specular_map", "specular_shininess", "texture_filter", "texture_repeat"), &CanvasItem::draw_polygon, DEFVAL(PackedVector2Array()), DEFVAL(Ref()), DEFVAL(Ref()), DEFVAL(Ref()), DEFVAL(Color(1, 1, 1, 1)), DEFVAL(TEXTURE_FILTER_PARENT_NODE), DEFVAL(TEXTURE_REPEAT_PARENT_NODE)); + ClassDB::bind_method(D_METHOD("draw_colored_polygon", "points", "color", "uvs", "texture", "normal_map", "specular_map", "specular_shininess", "texture_filter", "texture_repeat"), &CanvasItem::draw_colored_polygon, DEFVAL(PackedVector2Array()), DEFVAL(Ref()), DEFVAL(Ref()), DEFVAL(Ref()), DEFVAL(Color(1, 1, 1, 1)), DEFVAL(TEXTURE_FILTER_PARENT_NODE), DEFVAL(TEXTURE_REPEAT_PARENT_NODE)); + ClassDB::bind_method(D_METHOD("draw_string", "font", "position", "text", "modulate", "clip_w"), &CanvasItem::draw_string, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("draw_char", "font", "position", "char", "next", "modulate"), &CanvasItem::draw_char, DEFVAL(Color(1, 1, 1, 1))); + ClassDB::bind_method(D_METHOD("draw_mesh", "mesh", "texture", "normal_map", "specular_map", "specular_shininess", "transform", "modulate", "texture_filter", "texture_repeat"), &CanvasItem::draw_mesh, DEFVAL(Ref()), DEFVAL(Ref()), DEFVAL(Color(1, 1, 1, 1)), DEFVAL(Transform2D()), DEFVAL(Color(1, 1, 1, 1)), DEFVAL(TEXTURE_FILTER_PARENT_NODE), DEFVAL(TEXTURE_REPEAT_PARENT_NODE)); + ClassDB::bind_method(D_METHOD("draw_multimesh", "multimesh", "texture", "normal_map", "specular_map", "specular_shininess", "texture_filter", "texture_repeat"), &CanvasItem::draw_multimesh, DEFVAL(Ref()), DEFVAL(Ref()), DEFVAL(Color(1, 1, 1, 1)), DEFVAL(TEXTURE_FILTER_PARENT_NODE), DEFVAL(TEXTURE_REPEAT_PARENT_NODE)); + + ClassDB::bind_method(D_METHOD("draw_set_transform", "position", "rotation", "scale"), &CanvasItem::draw_set_transform); + ClassDB::bind_method(D_METHOD("draw_set_transform_matrix", "xform"), &CanvasItem::draw_set_transform_matrix); + ClassDB::bind_method(D_METHOD("get_transform"), &CanvasItem::get_transform); + ClassDB::bind_method(D_METHOD("get_global_transform"), &CanvasItem::get_global_transform); + ClassDB::bind_method(D_METHOD("get_global_transform_with_canvas"), &CanvasItem::get_global_transform_with_canvas); + ClassDB::bind_method(D_METHOD("get_viewport_transform"), &CanvasItem::get_viewport_transform); + ClassDB::bind_method(D_METHOD("get_viewport_rect"), &CanvasItem::get_viewport_rect); + ClassDB::bind_method(D_METHOD("get_canvas_transform"), &CanvasItem::get_canvas_transform); + ClassDB::bind_method(D_METHOD("get_local_mouse_position"), &CanvasItem::get_local_mouse_position); + ClassDB::bind_method(D_METHOD("get_global_mouse_position"), &CanvasItem::get_global_mouse_position); + ClassDB::bind_method(D_METHOD("get_canvas"), &CanvasItem::get_canvas); + ClassDB::bind_method(D_METHOD("get_world_2d"), &CanvasItem::get_world_2d); + //ClassDB::bind_method(D_METHOD("get_viewport"),&CanvasItem::get_viewport); + + ClassDB::bind_method(D_METHOD("set_material", "material"), &CanvasItem::set_material); + ClassDB::bind_method(D_METHOD("get_material"), &CanvasItem::get_material); + + ClassDB::bind_method(D_METHOD("set_use_parent_material", "enable"), &CanvasItem::set_use_parent_material); + ClassDB::bind_method(D_METHOD("get_use_parent_material"), &CanvasItem::get_use_parent_material); + + ClassDB::bind_method(D_METHOD("set_notify_local_transform", "enable"), &CanvasItem::set_notify_local_transform); + ClassDB::bind_method(D_METHOD("is_local_transform_notification_enabled"), &CanvasItem::is_local_transform_notification_enabled); + + ClassDB::bind_method(D_METHOD("set_notify_transform", "enable"), &CanvasItem::set_notify_transform); + ClassDB::bind_method(D_METHOD("is_transform_notification_enabled"), &CanvasItem::is_transform_notification_enabled); + + ClassDB::bind_method(D_METHOD("force_update_transform"), &CanvasItem::force_update_transform); + + ClassDB::bind_method(D_METHOD("make_canvas_position_local", "screen_point"), &CanvasItem::make_canvas_position_local); + ClassDB::bind_method(D_METHOD("make_input_local", "event"), &CanvasItem::make_input_local); + + ClassDB::bind_method(D_METHOD("set_texture_filter", "mode"), &CanvasItem::set_texture_filter); + ClassDB::bind_method(D_METHOD("get_texture_filter"), &CanvasItem::get_texture_filter); + + ClassDB::bind_method(D_METHOD("set_texture_repeat", "mode"), &CanvasItem::set_texture_repeat); + ClassDB::bind_method(D_METHOD("get_texture_repeat"), &CanvasItem::get_texture_repeat); + + BIND_VMETHOD(MethodInfo("_draw")); + + ADD_GROUP("Visibility", ""); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "modulate"), "set_modulate", "get_modulate"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "self_modulate"), "set_self_modulate", "get_self_modulate"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_behind_parent"), "set_draw_behind_parent", "is_draw_behind_parent_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_on_top", PROPERTY_HINT_NONE, "", 0), "_set_on_top", "_is_on_top"); //compatibility + ADD_PROPERTY(PropertyInfo(Variant::INT, "light_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_light_mask", "get_light_mask"); + + ADD_GROUP("Texture", "texture_"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "ParentNode,Nearest,Linear,MipmapNearest,MipmapLinear,MipmapNearestAniso,MipmapLinearAniso"), "set_texture_filter", "get_texture_filter"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_repeat", PROPERTY_HINT_ENUM, "ParentNode,Disabled,Enabled,Mirror"), "set_texture_repeat", "get_texture_repeat"); + + ADD_GROUP("Material", ""); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,CanvasItemMaterial"), "set_material", "get_material"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_parent_material"), "set_use_parent_material", "get_use_parent_material"); + //exporting these things doesn't really make much sense i think + // ADD_PROPERTY(PropertyInfo(Variant::BOOL, "toplevel", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_as_toplevel", "is_set_as_toplevel"); + // ADD_PROPERTY(PropertyInfo(Variant::BOOL,"transform/notify"),"set_transform_notify","is_transform_notify_enabled"); + + ADD_SIGNAL(MethodInfo("draw")); + ADD_SIGNAL(MethodInfo("visibility_changed")); + ADD_SIGNAL(MethodInfo("hide")); + ADD_SIGNAL(MethodInfo("item_rect_changed")); + + BIND_CONSTANT(NOTIFICATION_TRANSFORM_CHANGED); + BIND_CONSTANT(NOTIFICATION_DRAW); + BIND_CONSTANT(NOTIFICATION_VISIBILITY_CHANGED); + BIND_CONSTANT(NOTIFICATION_ENTER_CANVAS); + BIND_CONSTANT(NOTIFICATION_EXIT_CANVAS); + + BIND_ENUM_CONSTANT(TEXTURE_FILTER_PARENT_NODE); + BIND_ENUM_CONSTANT(TEXTURE_FILTER_NEAREST); + BIND_ENUM_CONSTANT(TEXTURE_FILTER_LINEAR); + BIND_ENUM_CONSTANT(TEXTURE_FILTER_NEAREST_WITH_MIPMAPS); + BIND_ENUM_CONSTANT(TEXTURE_FILTER_LINEAR_WITH_MIPMAPS); + BIND_ENUM_CONSTANT(TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC); + BIND_ENUM_CONSTANT(TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC); + BIND_ENUM_CONSTANT(TEXTURE_FILTER_MAX); + + BIND_ENUM_CONSTANT(TEXTURE_REPEAT_PARENT_NODE); + BIND_ENUM_CONSTANT(TEXTURE_REPEAT_DISABLED); + BIND_ENUM_CONSTANT(TEXTURE_REPEAT_ENABLED); + BIND_ENUM_CONSTANT(TEXTURE_REPEAT_MIRROR); + BIND_ENUM_CONSTANT(TEXTURE_REPEAT_MAX); +} + +Transform2D CanvasItem::get_canvas_transform() const { + + ERR_FAIL_COND_V(!is_inside_tree(), Transform2D()); + + if (canvas_layer) + return canvas_layer->get_transform(); + else if (Object::cast_to(get_parent())) + return Object::cast_to(get_parent())->get_canvas_transform(); + else + return get_viewport()->get_canvas_transform(); +} + +Transform2D CanvasItem::get_viewport_transform() const { + + ERR_FAIL_COND_V(!is_inside_tree(), Transform2D()); + + if (canvas_layer) { + + if (get_viewport()) { + return get_viewport()->get_final_transform() * canvas_layer->get_transform(); + } else { + return canvas_layer->get_transform(); + } + + } else { + return get_viewport()->get_final_transform() * get_viewport()->get_canvas_transform(); + } +} + +void CanvasItem::set_notify_local_transform(bool p_enable) { + notify_local_transform = p_enable; +} + +bool CanvasItem::is_local_transform_notification_enabled() const { + return notify_local_transform; +} + +void CanvasItem::set_notify_transform(bool p_enable) { + if (notify_transform == p_enable) + return; + + notify_transform = p_enable; + + if (notify_transform && is_inside_tree()) { + //this ensures that invalid globals get resolved, so notifications can be received + get_global_transform(); + } +} + +bool CanvasItem::is_transform_notification_enabled() const { + return notify_transform; +} + +int CanvasItem::get_canvas_layer() const { + + if (canvas_layer) + return canvas_layer->get_layer(); + else + return 0; +} + +void CanvasItem::_update_texture_filter_changed(bool p_propagate) { + + if (!is_inside_tree()) { + return; + } + + if (texture_filter == TEXTURE_FILTER_PARENT_NODE) { + CanvasItem *parent_item = get_parent_item(); + if (parent_item) { + texture_filter_cache = parent_item->texture_filter_cache; + } else { + //from viewport + switch (get_viewport()->get_default_canvas_item_texture_filter()) { + case Viewport::DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_NEAREST: texture_filter_cache = VS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST; break; + case Viewport::DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_LINEAR: texture_filter_cache = VS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR; break; + case Viewport::DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS: texture_filter_cache = VS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS; break; + case Viewport::DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS: texture_filter_cache = VS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS; break; + default: { + } + } + } + } else { + texture_filter_cache = VS::CanvasItemTextureFilter(texture_filter); + } + VS::get_singleton()->canvas_item_set_default_texture_filter(get_canvas_item(), texture_filter_cache); + update(); + + if (p_propagate) { + for (List::Element *E = children_items.front(); E; E = E->next()) { + if (!E->get()->toplevel && E->get()->texture_filter == TEXTURE_FILTER_PARENT_NODE) { + E->get()->_update_texture_filter_changed(true); + } + } + } +} + +void CanvasItem::set_texture_filter(TextureFilter p_texture_filter) { + ERR_FAIL_INDEX(p_texture_filter, TEXTURE_FILTER_MAX); + if (texture_filter == p_texture_filter) { + return; + } + texture_filter = p_texture_filter; + _update_texture_filter_changed(true); +} + +CanvasItem::TextureFilter CanvasItem::get_texture_filter() const { + return texture_filter; +} + +void CanvasItem::_update_texture_repeat_changed(bool p_propagate) { + + if (!is_inside_tree()) { + return; + } + + if (texture_repeat == TEXTURE_REPEAT_PARENT_NODE) { + CanvasItem *parent_item = get_parent_item(); + if (parent_item) { + texture_repeat_cache = parent_item->texture_repeat_cache; + } else { + //from viewport + switch (get_viewport()->get_default_canvas_item_texture_repeat()) { + case Viewport::DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_DISABLED: texture_repeat_cache = VS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED; break; + case Viewport::DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_ENABLED: texture_repeat_cache = VS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED; break; + case Viewport::DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_MIRROR: texture_repeat_cache = VS::CANVAS_ITEM_TEXTURE_REPEAT_MIRROR; break; + default: { + } + } + } + } else { + texture_repeat_cache = VS::CanvasItemTextureRepeat(texture_repeat); + } + VS::get_singleton()->canvas_item_set_default_texture_repeat(get_canvas_item(), texture_repeat_cache); + update(); + if (p_propagate) { + for (List::Element *E = children_items.front(); E; E = E->next()) { + if (!E->get()->toplevel && E->get()->texture_repeat == TEXTURE_REPEAT_PARENT_NODE) { + E->get()->_update_texture_repeat_changed(true); + } + } + } +} + +void CanvasItem::set_texture_repeat(TextureRepeat p_texture_repeat) { + ERR_FAIL_INDEX(p_texture_repeat, TEXTURE_REPEAT_MAX); + if (texture_repeat == p_texture_repeat) { + return; + } + texture_repeat = p_texture_repeat; + _update_texture_repeat_changed(true); +} + +CanvasItem::TextureRepeat CanvasItem::get_texture_repeat() const { + return texture_repeat; +} + +CanvasItem::CanvasItem() : + xform_change(this) { + + window = nullptr; + canvas_item = VisualServer::get_singleton()->canvas_item_create(); + visible = true; + pending_update = false; + modulate = Color(1, 1, 1, 1); + self_modulate = Color(1, 1, 1, 1); + toplevel = false; + first_draw = false; + drawing = false; + behind = false; + block_transform_notify = false; + //viewport=NULL; + canvas_layer = NULL; + use_parent_material = false; + global_invalid = true; + notify_local_transform = false; + notify_transform = false; + light_mask = 1; + texture_repeat = TEXTURE_REPEAT_PARENT_NODE; + texture_filter = TEXTURE_FILTER_PARENT_NODE; + texture_filter_cache = VS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR; + texture_repeat_cache = VS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED; + + C = NULL; +} + +CanvasItem::~CanvasItem() { + + VisualServer::get_singleton()->free(canvas_item); +} diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h new file mode 100644 index 0000000000..3f176e5f60 --- /dev/null +++ b/scene/main/canvas_item.h @@ -0,0 +1,426 @@ +/*************************************************************************/ +/* canvas_item.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef CANVAS_ITEM_H +#define CANVAS_ITEM_H + +#include "scene/main/node.h" +#include "scene/main/scene_tree.h" +#include "scene/resources/material.h" +#include "scene/resources/multimesh.h" +#include "scene/resources/shader.h" +#include "scene/resources/texture.h" + +class CanvasLayer; +class Viewport; +class Font; + +class StyleBox; + +class CanvasItemMaterial : public Material { + + GDCLASS(CanvasItemMaterial, Material); + +public: + enum BlendMode { + BLEND_MODE_MIX, + BLEND_MODE_ADD, + BLEND_MODE_SUB, + BLEND_MODE_MUL, + BLEND_MODE_PREMULT_ALPHA, + BLEND_MODE_DISABLED + }; + + enum LightMode { + LIGHT_MODE_NORMAL, + LIGHT_MODE_UNSHADED, + LIGHT_MODE_LIGHT_ONLY + }; + +private: + union MaterialKey { + + struct { + uint32_t blend_mode : 4; + uint32_t light_mode : 4; + uint32_t particles_animation : 1; + uint32_t invalid_key : 1; + }; + + uint32_t key; + + bool operator<(const MaterialKey &p_key) const { + return key < p_key.key; + } + }; + + struct ShaderNames { + StringName particles_anim_h_frames; + StringName particles_anim_v_frames; + StringName particles_anim_loop; + }; + + static ShaderNames *shader_names; + + struct ShaderData { + RID shader; + int users; + }; + + static Map shader_map; + + MaterialKey current_key; + + _FORCE_INLINE_ MaterialKey _compute_key() const { + + MaterialKey mk; + mk.key = 0; + mk.blend_mode = blend_mode; + mk.light_mode = light_mode; + mk.particles_animation = particles_animation; + return mk; + } + + static Mutex material_mutex; + static SelfList::List *dirty_materials; + SelfList element; + + void _update_shader(); + _FORCE_INLINE_ void _queue_shader_change(); + _FORCE_INLINE_ bool _is_shader_dirty() const; + + BlendMode blend_mode; + LightMode light_mode; + bool particles_animation; + + int particles_anim_h_frames; + int particles_anim_v_frames; + bool particles_anim_loop; + +protected: + static void _bind_methods(); + void _validate_property(PropertyInfo &property) const; + +public: + void set_blend_mode(BlendMode p_blend_mode); + BlendMode get_blend_mode() const; + + void set_light_mode(LightMode p_light_mode); + LightMode get_light_mode() const; + + void set_particles_animation(bool p_particles_anim); + bool get_particles_animation() const; + + void set_particles_anim_h_frames(int p_frames); + int get_particles_anim_h_frames() const; + void set_particles_anim_v_frames(int p_frames); + int get_particles_anim_v_frames() const; + + void set_particles_anim_loop(bool p_loop); + bool get_particles_anim_loop() const; + + static void init_shaders(); + static void finish_shaders(); + static void flush_changes(); + + RID get_shader_rid() const; + + virtual Shader::Mode get_shader_mode() const; + + CanvasItemMaterial(); + virtual ~CanvasItemMaterial(); +}; + +VARIANT_ENUM_CAST(CanvasItemMaterial::BlendMode) +VARIANT_ENUM_CAST(CanvasItemMaterial::LightMode) + +class CanvasItem : public Node { + + GDCLASS(CanvasItem, Node); + +public: + enum TextureFilter { + TEXTURE_FILTER_PARENT_NODE, + TEXTURE_FILTER_NEAREST, + TEXTURE_FILTER_LINEAR, + TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, + TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, + TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, + TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, + TEXTURE_FILTER_MAX + }; + + enum TextureRepeat { + TEXTURE_REPEAT_PARENT_NODE, + TEXTURE_REPEAT_DISABLED, + TEXTURE_REPEAT_ENABLED, + TEXTURE_REPEAT_MIRROR, + TEXTURE_REPEAT_MAX, + }; + +private: + mutable SelfList xform_change; + + RID canvas_item; + String group; + + CanvasLayer *canvas_layer; + + Color modulate; + Color self_modulate; + + List children_items; + List::Element *C; + + int light_mask; + + Window *window; + bool first_draw; + bool visible; + bool pending_update; + bool toplevel; + bool drawing; + bool block_transform_notify; + bool behind; + bool use_parent_material; + bool notify_local_transform; + bool notify_transform; + + VS::CanvasItemTextureFilter texture_filter_cache; + VS::CanvasItemTextureRepeat texture_repeat_cache; + + TextureFilter texture_filter; + TextureRepeat texture_repeat; + + Ref material; + + mutable Transform2D global_transform; + mutable bool global_invalid; + + void _toplevel_raise_self(); + + void _propagate_visibility_changed(bool p_visible); + + void _update_callback(); + + void _enter_canvas(); + void _exit_canvas(); + + void _window_visibility_changed(); + + void _notify_transform(CanvasItem *p_node); + + void _set_on_top(bool p_on_top) { set_draw_behind_parent(!p_on_top); } + bool _is_on_top() const { return !is_draw_behind_parent_enabled(); } + + static CanvasItem *current_item_drawn; + friend class Viewport; + void _update_texture_repeat_changed(bool p_propagate); + void _update_texture_filter_changed(bool p_propagate); + +protected: + _FORCE_INLINE_ void _notify_transform() { + if (!is_inside_tree()) return; + _notify_transform(this); + if (!block_transform_notify && notify_local_transform) notification(NOTIFICATION_LOCAL_TRANSFORM_CHANGED); + } + + void item_rect_changed(bool p_size_changed = true); + + void _notification(int p_what); + static void _bind_methods(); + +public: + enum { + NOTIFICATION_TRANSFORM_CHANGED = SceneTree::NOTIFICATION_TRANSFORM_CHANGED, //unique + NOTIFICATION_DRAW = 30, + NOTIFICATION_VISIBILITY_CHANGED = 31, + NOTIFICATION_ENTER_CANVAS = 32, + NOTIFICATION_EXIT_CANVAS = 33, + NOTIFICATION_LOCAL_TRANSFORM_CHANGED = 35, + NOTIFICATION_WORLD_2D_CHANGED = 36, + + }; + + /* EDITOR */ +#ifdef TOOLS_ENABLED + // Select the node + virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; + + // Save and restore a CanvasItem state + virtual void _edit_set_state(const Dictionary &p_state){}; + virtual Dictionary _edit_get_state() const { return Dictionary(); }; + + // Used to move the node + virtual void _edit_set_position(const Point2 &p_position) = 0; + virtual Point2 _edit_get_position() const = 0; + + // Used to scale the node + virtual void _edit_set_scale(const Size2 &p_scale) = 0; + virtual Size2 _edit_get_scale() const = 0; + + // Used to rotate the node + virtual bool _edit_use_rotation() const { return false; }; + virtual void _edit_set_rotation(float p_rotation){}; + virtual float _edit_get_rotation() const { return 0.0; }; + + // Used to resize/move the node + virtual bool _edit_use_rect() const { return false; }; // MAYBE REPLACE BY A _edit_get_editmode() + virtual void _edit_set_rect(const Rect2 &p_rect){}; + virtual Rect2 _edit_get_rect() const { return Rect2(0, 0, 0, 0); }; + virtual Size2 _edit_get_minimum_size() const { return Size2(-1, -1); }; // LOOKS WEIRD + + // Used to set a pivot + virtual bool _edit_use_pivot() const { return false; }; + virtual void _edit_set_pivot(const Point2 &p_pivot){}; + virtual Point2 _edit_get_pivot() const { return Point2(); }; + + virtual Transform2D _edit_get_transform() const; +#endif + + /* VISIBILITY */ + + void set_visible(bool p_visible); + bool is_visible() const; + bool is_visible_in_tree() const; + void show(); + void hide(); + + void update(); + + virtual void set_light_mask(int p_light_mask); + int get_light_mask() const; + + void set_modulate(const Color &p_modulate); + Color get_modulate() const; + + void set_self_modulate(const Color &p_self_modulate); + Color get_self_modulate() const; + + /* DRAWING API */ + + void draw_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, float p_width = 1.0); + void draw_polyline(const Vector &p_points, const Color &p_color, float p_width = 1.0); + void draw_polyline_colors(const Vector &p_points, const Vector &p_colors, float p_width = 1.0); + void draw_arc(const Vector2 &p_center, float p_radius, float p_start_angle, float p_end_angle, int p_point_count, const Color &p_color, float p_width = 1.0); + void draw_multiline(const Vector &p_points, const Color &p_color, float p_width = 1.0); + void draw_multiline_colors(const Vector &p_points, const Vector &p_colors, float p_width = 1.0); + void draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_filled = true, float p_width = 1.0); + void draw_circle(const Point2 &p_pos, float p_radius, const Color &p_color); + void draw_texture(const Ref &p_texture, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1, 1), const Ref &p_normal_map = Ref(), const Ref &p_specular_map = Ref(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), TextureFilter p_texture_filter = TEXTURE_FILTER_PARENT_NODE, TextureRepeat p_texture_repeat = TEXTURE_REPEAT_PARENT_NODE); + void draw_texture_rect(const Ref &p_texture, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref &p_normal_map = Ref(), const Ref &p_specular_map = Ref(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), TextureFilter p_texture_filter = TEXTURE_FILTER_PARENT_NODE, TextureRepeat p_texture_repeat = TEXTURE_REPEAT_PARENT_NODE); + void draw_texture_rect_region(const Ref &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref &p_normal_map = Ref(), const Ref &p_specular_map = Ref(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), bool p_clip_uv = false, TextureFilter p_texture_filter = TEXTURE_FILTER_PARENT_NODE, TextureRepeat p_texture_repeat = TEXTURE_REPEAT_PARENT_NODE); + void draw_style_box(const Ref &p_style_box, const Rect2 &p_rect); + void draw_primitive(const Vector &p_points, const Vector &p_colors, const Vector &p_uvs, Ref p_texture = Ref(), float p_width = 1, const Ref &p_normal_map = Ref(), const Ref &p_specular_map = Ref(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), TextureFilter p_texture_filter = TEXTURE_FILTER_PARENT_NODE, TextureRepeat p_texture_repeat = TEXTURE_REPEAT_PARENT_NODE); + void draw_polygon(const Vector &p_points, const Vector &p_colors, const Vector &p_uvs = Vector(), Ref p_texture = Ref(), const Ref &p_normal_map = Ref(), const Ref &p_specular_map = Ref(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), TextureFilter p_texture_filter = TEXTURE_FILTER_PARENT_NODE, TextureRepeat p_texture_repeat = TEXTURE_REPEAT_PARENT_NODE); + void draw_colored_polygon(const Vector &p_points, const Color &p_color, const Vector &p_uvs = Vector(), Ref p_texture = Ref(), const Ref &p_normal_map = Ref(), const Ref &p_specular_map = Ref(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), TextureFilter p_texture_filter = TEXTURE_FILTER_PARENT_NODE, TextureRepeat p_texture_repeat = TEXTURE_REPEAT_PARENT_NODE); + + void draw_mesh(const Ref &p_mesh, const Ref &p_texture, const Ref &p_normal_map = Ref(), const Ref &p_specular_map = Ref(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), const Transform2D &p_transform = Transform2D(), const Color &p_modulate = Color(1, 1, 1), TextureFilter p_texture_filter = TEXTURE_FILTER_PARENT_NODE, TextureRepeat p_texture_repeat = TEXTURE_REPEAT_PARENT_NODE); + void draw_multimesh(const Ref &p_multimesh, const Ref &p_texture, const Ref &p_normal_map = Ref(), const Ref &p_specular_map = Ref(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), TextureFilter p_texture_filter = TEXTURE_FILTER_PARENT_NODE, TextureRepeat p_texture_repeat = TEXTURE_REPEAT_PARENT_NODE); + + void draw_string(const Ref &p_font, const Point2 &p_pos, const String &p_text, const Color &p_modulate = Color(1, 1, 1), int p_clip_w = -1); + float draw_char(const Ref &p_font, const Point2 &p_pos, const String &p_char, const String &p_next = "", const Color &p_modulate = Color(1, 1, 1)); + + void draw_set_transform(const Point2 &p_offset, float p_rot, const Size2 &p_scale); + void draw_set_transform_matrix(const Transform2D &p_matrix); + + static CanvasItem *get_current_item_drawn(); + + /* RECT / TRANSFORM */ + + void set_as_toplevel(bool p_toplevel); + bool is_set_as_toplevel() const; + + void set_draw_behind_parent(bool p_enable); + bool is_draw_behind_parent_enabled() const; + + CanvasItem *get_parent_item() const; + + virtual Transform2D get_transform() const = 0; + + virtual Transform2D get_global_transform() const; + virtual Transform2D get_global_transform_with_canvas() const; + virtual Transform2D get_screen_transform() const; + + CanvasItem *get_toplevel() const; + _FORCE_INLINE_ RID get_canvas_item() const { + return canvas_item; + } + + void set_block_transform_notify(bool p_enable); + bool is_block_transform_notify_enabled() const; + + Transform2D get_canvas_transform() const; + Transform2D get_viewport_transform() const; + Rect2 get_viewport_rect() const; + RID get_viewport_rid() const; + RID get_canvas() const; + ObjectID get_canvas_layer_instance_id() const; + Ref get_world_2d() const; + + virtual void set_material(const Ref &p_material); + Ref get_material() const; + + virtual void set_use_parent_material(bool p_use_parent_material); + bool get_use_parent_material() const; + + Ref make_input_local(const Ref &p_event) const; + Vector2 make_canvas_position_local(const Vector2 &screen_point) const; + + Vector2 get_global_mouse_position() const; + Vector2 get_local_mouse_position() const; + + void set_notify_local_transform(bool p_enable); + bool is_local_transform_notification_enabled() const; + + void set_notify_transform(bool p_enable); + bool is_transform_notification_enabled() const; + + void force_update_transform(); + + void set_texture_filter(TextureFilter p_texture_filter); + TextureFilter get_texture_filter() const; + + void set_texture_repeat(TextureRepeat p_texture_repeat); + TextureRepeat get_texture_repeat() const; + + // Used by control nodes to retrieve the parent's anchorable area + virtual Rect2 get_anchorable_rect() const { return Rect2(0, 0, 0, 0); }; + + int get_canvas_layer() const; + + CanvasItem(); + ~CanvasItem(); +}; + +VARIANT_ENUM_CAST(CanvasItem::TextureFilter) +VARIANT_ENUM_CAST(CanvasItem::TextureRepeat) + +#endif // CANVAS_ITEM_H diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index fad10524b9..d6c0323f25 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -1410,7 +1410,7 @@ SceneTree::SceneTree() { root = memnew(Window); root->set_name("root"); if (!root->get_world().is_valid()) - root->set_world(Ref(memnew(World))); + root->set_world(Ref(memnew(World3D))); // Initialize network state multiplayer_poll = true; diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h index 48b3fcacb3..319b5a7e74 100644 --- a/scene/main/scene_tree.h +++ b/scene/main/scene_tree.h @@ -36,8 +36,8 @@ #include "core/os/thread_safe.h" #include "core/self_list.h" #include "scene/resources/mesh.h" -#include "scene/resources/world.h" #include "scene/resources/world_2d.h" +#include "scene/resources/world_3d.h" #undef Window diff --git a/scene/main/timer.cpp b/scene/main/timer.cpp old mode 100755 new mode 100644 diff --git a/scene/main/timer.h b/scene/main/timer.h old mode 100755 new mode 100644 diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 2f131631af..d293a3cd32 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -1285,7 +1285,7 @@ void Viewport::_propagate_exit_world(Node *p_node) { } } -void Viewport::set_world(const Ref &p_world) { +void Viewport::set_world(const Ref &p_world) { if (world == p_world) return; @@ -1304,7 +1304,7 @@ void Viewport::set_world(const Ref &p_world) { own_world = world->duplicate(); world->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Viewport::_own_world_changed)); } else { - own_world = Ref(memnew(World)); + own_world = Ref(memnew(World3D)); } } @@ -1318,7 +1318,7 @@ void Viewport::set_world(const Ref &p_world) { _update_listener(); } -Ref Viewport::get_world() const { +Ref Viewport::get_world() const { return world; } @@ -1328,7 +1328,7 @@ Ref Viewport::get_world_2d() const { return world_2d; } -Ref Viewport::find_world() const { +Ref Viewport::find_world() const { if (own_world.is_valid()) return own_world; @@ -1337,7 +1337,7 @@ Ref Viewport::find_world() const { else if (parent) return parent->find_world(); else - return Ref(); + return Ref(); } Listener3D *Viewport::get_listener() const { @@ -3092,7 +3092,7 @@ void Viewport::set_use_own_world(bool p_world) { _propagate_exit_world(this); if (!p_world) { - own_world = Ref(); + own_world = Ref(); if (world.is_valid()) { world->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Viewport::_own_world_changed)); } @@ -3101,7 +3101,7 @@ void Viewport::set_use_own_world(bool p_world) { own_world = world->duplicate(); world->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Viewport::_own_world_changed)); } else { - own_world = Ref(memnew(World)); + own_world = Ref(memnew(World3D)); } } diff --git a/scene/main/viewport.h b/scene/main/viewport.h index 31eb461cee..8a56caea52 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -245,8 +245,8 @@ private: Map physics_2d_mouseover; Ref world_2d; - Ref world; - Ref own_world; + Ref world; + Ref own_world; Rect2i to_screen_rect; StringName input_group; @@ -467,10 +467,10 @@ public: Rect2 get_visible_rect() const; RID get_viewport_rid() const; - void set_world(const Ref &p_world); + void set_world(const Ref &p_world); void set_world_2d(const Ref &p_world_2d); - Ref get_world() const; - Ref find_world() const; + Ref get_world() const; + Ref find_world() const; Ref get_world_2d() const; Ref find_world_2d() const; diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index f94d7a00ac..1094759ce7 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -38,11 +38,11 @@ #include "scene/2d/audio_stream_player_2d.h" #include "scene/2d/back_buffer_copy.h" #include "scene/2d/camera_2d.h" -#include "scene/2d/canvas_item.h" #include "scene/2d/canvas_modulate.h" #include "scene/2d/collision_polygon_2d.h" #include "scene/2d/collision_shape_2d.h" #include "scene/2d/cpu_particles_2d.h" +#include "scene/2d/gpu_particles_2d.h" #include "scene/2d/joints_2d.h" #include "scene/2d/light_2d.h" #include "scene/2d/light_occluder_2d.h" @@ -54,7 +54,6 @@ #include "scene/2d/navigation_obstacle_2d.h" #include "scene/2d/parallax_background.h" #include "scene/2d/parallax_layer.h" -#include "scene/2d/particles_2d.h" #include "scene/2d/path_2d.h" #include "scene/2d/physics_body_2d.h" #include "scene/2d/polygon_2d.h" @@ -121,6 +120,7 @@ #include "scene/gui/tree.h" #include "scene/gui/video_player.h" #include "scene/gui/viewport_container.h" +#include "scene/main/canvas_item.h" #include "scene/main/canvas_layer.h" #include "scene/main/http_request.h" #include "scene/main/instance_placeholder.h" @@ -143,7 +143,7 @@ #include "scene/resources/default_theme/default_theme.h" #include "scene/resources/dynamic_font.h" #include "scene/resources/gradient.h" -#include "scene/resources/height_map_shape.h" +#include "scene/resources/height_map_shape_3d.h" #include "scene/resources/line_shape_2d.h" #include "scene/resources/material.h" #include "scene/resources/mesh.h" @@ -154,7 +154,7 @@ #include "scene/resources/physics_material.h" #include "scene/resources/polygon_path_finder.h" #include "scene/resources/primitive_meshes.h" -#include "scene/resources/ray_shape.h" +#include "scene/resources/ray_shape_3d.h" #include "scene/resources/rectangle_shape_2d.h" #include "scene/resources/resource_format_text.h" #include "scene/resources/segment_shape_2d.h" @@ -168,8 +168,8 @@ #include "scene/resources/video_stream.h" #include "scene/resources/visual_shader.h" #include "scene/resources/visual_shader_nodes.h" -#include "scene/resources/world.h" #include "scene/resources/world_2d.h" +#include "scene/resources/world_3d.h" #include "scene/resources/world_margin_shape_3d.h" #include "scene/scene_string_names.h" @@ -206,13 +206,13 @@ #include "scene/3d/ray_cast_3d.h" #include "scene/3d/reflection_probe.h" #include "scene/3d/remote_transform_3d.h" +#include "scene/3d/skeleton_ik_3d.h" #include "scene/3d/soft_body_3d.h" #include "scene/3d/spring_arm_3d.h" #include "scene/3d/sprite_3d.h" -#include "scene/3d/vehicle_body.h" +#include "scene/3d/vehicle_body_3d.h" #include "scene/3d/visibility_notifier_3d.h" #include "scene/3d/world_environment.h" -#include "scene/animation/skeleton_ik.h" #include "scene/resources/environment.h" #include "scene/resources/mesh_library.h" #endif @@ -279,6 +279,7 @@ void register_scene_types() { ClassDB::register_class(); /* REGISTER GUI */ + ClassDB::register_class(); ClassDB::register_virtual_class(); @@ -335,7 +336,6 @@ void register_scene_types() { ClassDB::register_class(); #ifndef ADVANCED_GUI_DISABLED - ClassDB::register_class(); ClassDB::register_class(); @@ -351,9 +351,10 @@ void register_scene_types() { ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); - ; + ClassDB::register_class(); ClassDB::register_class(); + ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_virtual_class(); @@ -364,6 +365,7 @@ void register_scene_types() { OS::get_singleton()->yield(); //may take time to init + AcceptDialog::set_swap_ok_cancel(GLOBAL_DEF("gui/common/swap_ok_cancel", bool(DisplayServer::get_singleton()->get_swap_ok_cancel()))); #endif /* REGISTER 3D */ @@ -405,7 +407,7 @@ void register_scene_types() { ClassDB::register_virtual_class(); ClassDB::register_virtual_class(); ClassDB::register_class(); - ClassDB::register_class(); + ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); @@ -448,8 +450,8 @@ void register_scene_types() { ClassDB::register_class(); ClassDB::register_class(); - ClassDB::register_class(); - ClassDB::register_class(); + ClassDB::register_class(); + ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); @@ -478,11 +480,9 @@ void register_scene_types() { ClassDB::register_class(); OS::get_singleton()->yield(); //may take time to init - #endif - ClassDB::register_class(); - AcceptDialog::set_swap_ok_cancel(GLOBAL_DEF("gui/common/swap_ok_cancel", bool(DisplayServer::get_singleton()->get_swap_ok_cancel()))); + /* REGISTER SHADER */ ClassDB::register_class(); ClassDB::register_class(); @@ -556,12 +556,13 @@ void register_scene_types() { ClassDB::register_class(); SceneTree::add_idle_callback(CanvasItemMaterial::flush_changes); CanvasItemMaterial::init_shaders(); + + /* REGISTER 2D */ + ClassDB::register_class(); ClassDB::register_class(); - ClassDB::register_class(); - //ClassDB::register_class(); + ClassDB::register_class(); ClassDB::register_class(); - //ClassDB::register_type(); ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); @@ -649,7 +650,7 @@ void register_scene_types() { ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); - ClassDB::register_class(); + ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); @@ -657,10 +658,10 @@ void register_scene_types() { OS::get_singleton()->yield(); //may take time to init ClassDB::register_class(); - #endif + ClassDB::register_class(); - ClassDB::register_class(); + ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); @@ -729,6 +730,7 @@ void register_scene_types() { ClassDB::register_class(); ClassDB::register_class(); + ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); @@ -744,90 +746,79 @@ void register_scene_types() { ClassDB::register_virtual_class(); //sorry, you can't create it #ifndef DISABLE_DEPRECATED - - ClassDB::add_compatibility_class("AnimatedSprite", "AnimatedSprite2D"); - ClassDB::add_compatibility_class("Sprite", "Sprite2D"); - - ClassDB::add_compatibility_class("SpatialMaterial", "StandardMaterial3D"); - ClassDB::add_compatibility_class("Mesh", "ArrayMesh"); + // Dropped in 4.0, near approximation. ClassDB::add_compatibility_class("AnimationTreePlayer", "AnimationTree"); - ClassDB::add_compatibility_class("VisualShaderNodeScalarConstant", "VisualShaderNodeFloatConstant"); - ClassDB::add_compatibility_class("VisualShaderNodeScalarUniform", "VisualShaderNodeFloatUniform"); - ClassDB::add_compatibility_class("VisualShaderNodeScalarOp", "VisualShaderNodeFloatOp"); - ClassDB::add_compatibility_class("VisualShaderNodeScalarFunc", "VisualShaderNodeFloatFunc"); - ClassDB::add_compatibility_class("NavigationMeshInstance", "NavigationRegion3D"); - ClassDB::add_compatibility_class("NavigationPolygonInstance", "NavigationRegion2D"); - ClassDB::add_compatibility_class("PlaneShape", "WorldMarginShape3D"); - ClassDB::add_compatibility_class("WorldMarginShape", "WorldMarginShape3D"); - ClassDB::add_compatibility_class("Spatial", "Node3D"); + // Renamed in 4.0. + ClassDB::add_compatibility_class("AnimatedSprite", "AnimatedSprite2D"); ClassDB::add_compatibility_class("Area", "Area3D"); + ClassDB::add_compatibility_class("BoneAttachment", "BoneAttachment3D"); + ClassDB::add_compatibility_class("BoxShape", "BoxShape3D"); ClassDB::add_compatibility_class("Camera", "Camera3D"); - - ClassDB::add_compatibility_class("Particles", "GPUParticles3D"); + ClassDB::add_compatibility_class("CapsuleShape", "CapsuleShape3D"); + ClassDB::add_compatibility_class("ClippedCamera", "ClippedCamera3D"); + ClassDB::add_compatibility_class("CollisionObject", "CollisionObject3D"); + ClassDB::add_compatibility_class("CollisionPolygon", "CollisionPolygon3D"); + ClassDB::add_compatibility_class("CollisionShape", "CollisionShape3D"); + ClassDB::add_compatibility_class("ConcavePolygonShape", "ConcavePolygonShape3D"); + ClassDB::add_compatibility_class("ConeTwistJoint", "ConeTwistJoint3D"); + ClassDB::add_compatibility_class("ConvexPolygonShape", "ConvexPolygonShape3D"); ClassDB::add_compatibility_class("CPUParticles", "CPUParticles3D"); - - ClassDB::add_compatibility_class("Particles2D", "GPUParticles2D"); - - ClassDB::add_compatibility_class("Light", "Light3D"); + ClassDB::add_compatibility_class("CylinderShape", "CylinderShape3D"); ClassDB::add_compatibility_class("DirectionalLight", "DirectionalLight3D"); - ClassDB::add_compatibility_class("SpotLight", "SpotLight3D"); - ClassDB::add_compatibility_class("OmniLight", "OmniLight3D"); - + ClassDB::add_compatibility_class("Generic6DOFJoint", "Generic6DOFJoint3D"); + ClassDB::add_compatibility_class("HeightMapShape", "HeightMapShape3D"); + ClassDB::add_compatibility_class("HingeJoint", "HingeJoint3D"); + ClassDB::add_compatibility_class("ImmediateGeometry", "ImmediateGeometry3D"); + ClassDB::add_compatibility_class("Joint", "Joint3D"); + ClassDB::add_compatibility_class("KinematicBody", "KinematicBody3D"); + ClassDB::add_compatibility_class("KinematicCollision", "KinematicCollision3D"); + ClassDB::add_compatibility_class("Light", "Light3D"); ClassDB::add_compatibility_class("Listener", "Listener3D"); - ClassDB::add_compatibility_class("MeshInstance", "MeshInstance3D"); ClassDB::add_compatibility_class("MultiMeshInstance", "MultiMeshInstance3D"); - ClassDB::add_compatibility_class("ImmediateGeometry", "ImmediateGeometry3D"); - ClassDB::add_compatibility_class("Navigation", "Navigation3D"); ClassDB::add_compatibility_class("NavigationAgent", "NavigationAgent3D"); + ClassDB::add_compatibility_class("NavigationMeshInstance", "NavigationRegion3D"); ClassDB::add_compatibility_class("NavigationObstacle", "NavigationObstacle3D"); + ClassDB::add_compatibility_class("NavigationPolygonInstance", "NavigationRegion2D"); ClassDB::add_compatibility_class("NavigationRegion", "NavigationRegion3D"); - + ClassDB::add_compatibility_class("OmniLight", "OmniLight3D"); + ClassDB::add_compatibility_class("Particles", "GPUParticles3D"); + ClassDB::add_compatibility_class("Particles2D", "GPUParticles2D"); ClassDB::add_compatibility_class("Path", "Path3D"); ClassDB::add_compatibility_class("PathFollow", "PathFollow3D"); - + ClassDB::add_compatibility_class("PhysicalBone", "PhysicalBone3D"); + ClassDB::add_compatibility_class("PhysicsBody", "PhysicsBody3D"); + ClassDB::add_compatibility_class("PinJoint", "PinJoint3D"); + ClassDB::add_compatibility_class("PlaneShape", "WorldMarginShape3D"); ClassDB::add_compatibility_class("ProximityGroup", "ProximityGroup3D"); - ClassDB::add_compatibility_class("RayCast", "RayCast3D"); + ClassDB::add_compatibility_class("RayShape", "RayShape3D"); ClassDB::add_compatibility_class("RemoteTransform", "RemoteTransform3D"); - + ClassDB::add_compatibility_class("RigidBody", "RigidBody3D"); + ClassDB::add_compatibility_class("Shape", "Shape3D"); ClassDB::add_compatibility_class("Skeleton", "Skeleton3D"); ClassDB::add_compatibility_class("SkeletonIK", "SkeletonIK3D"); - - ClassDB::add_compatibility_class("VisibilityNotifier", "VisibilityNotifier3D"); - ClassDB::add_compatibility_class("VisibilityEnabler", "VisibilityEnabler3D"); - - ClassDB::add_compatibility_class("CollisionObject", "CollisionObject3D"); - ClassDB::add_compatibility_class("CollisionShape", "CollisionShape3D"); - ClassDB::add_compatibility_class("CollisionPolygon", "CollisionPolygon3D"); - - ClassDB::add_compatibility_class("PhysicsBody", "PhysicsBody3D"); - ClassDB::add_compatibility_class("RigidBody", "RigidBody3D"); - ClassDB::add_compatibility_class("StaticBody", "StaticBody3D"); - ClassDB::add_compatibility_class("KinematicBody", "KinematicBody3D"); + ClassDB::add_compatibility_class("SliderJoint", "SliderJoint3D"); ClassDB::add_compatibility_class("SoftBody", "SoftBody3D"); - ClassDB::add_compatibility_class("PhysicalBone", "PhysicalBone3D"); - ClassDB::add_compatibility_class("KinematicCollision", "KinematicCollision3D"); - ClassDB::add_compatibility_class("SpringArm", "SpringArm3D"); - - ClassDB::add_compatibility_class("Shape", "Shape3D"); - ClassDB::add_compatibility_class("RayShape", "RayShape3D"); + ClassDB::add_compatibility_class("Spatial", "Node3D"); + ClassDB::add_compatibility_class("SpatialMaterial", "StandardMaterial3D"); + ClassDB::add_compatibility_class("SpatialVelocityTracker", "VelocityTracker3D"); ClassDB::add_compatibility_class("SphereShape", "SphereShape3D"); - ClassDB::add_compatibility_class("BoxShape", "BoxShape3D"); - ClassDB::add_compatibility_class("CylinderShape", "CylinderShape3D"); - ClassDB::add_compatibility_class("CapsuleShape", "CapsuleShape3D"); - ClassDB::add_compatibility_class("ConvexPolygonShape", "ConvexPolygonShape3D"); - ClassDB::add_compatibility_class("ConcavePolygonShape", "ConcavePolygonShape3D"); - - ClassDB::add_compatibility_class("Joint", "Joint3D"); - ClassDB::add_compatibility_class("PinJoint", "PinJoint3D"); - ClassDB::add_compatibility_class("SliderJoint", "SliderJoint3D"); - ClassDB::add_compatibility_class("HingeJoint", "HingeJoint3D"); - ClassDB::add_compatibility_class("ConeTwistJoint", "ConeTwistJoint3D"); - ClassDB::add_compatibility_class("Generic6DOFJoint", "Generic6DOFJoint3D"); - + ClassDB::add_compatibility_class("SpotLight", "SpotLight3D"); + ClassDB::add_compatibility_class("SpringArm", "SpringArm3D"); + ClassDB::add_compatibility_class("Sprite", "Sprite2D"); + ClassDB::add_compatibility_class("StaticBody", "StaticBody3D"); + ClassDB::add_compatibility_class("VehicleBody", "VehicleBody3D"); + ClassDB::add_compatibility_class("VehicleWheel", "VehicleWheel3D"); + ClassDB::add_compatibility_class("VisibilityEnabler", "VisibilityEnabler3D"); + ClassDB::add_compatibility_class("VisibilityNotifier", "VisibilityNotifier3D"); + ClassDB::add_compatibility_class("VisualShaderNodeScalarConstant", "VisualShaderNodeFloatConstant"); + ClassDB::add_compatibility_class("VisualShaderNodeScalarFunc", "VisualShaderNodeFloatFunc"); + ClassDB::add_compatibility_class("VisualShaderNodeScalarOp", "VisualShaderNodeFloatOp"); + ClassDB::add_compatibility_class("VisualShaderNodeScalarUniform", "VisualShaderNodeFloatUniform"); + ClassDB::add_compatibility_class("World", "World3D"); #endif OS::get_singleton()->yield(); //may take time to init diff --git a/scene/resources/height_map_shape.cpp b/scene/resources/height_map_shape.cpp deleted file mode 100644 index 34f5ed8558..0000000000 --- a/scene/resources/height_map_shape.cpp +++ /dev/null @@ -1,209 +0,0 @@ -/*************************************************************************/ -/* height_map_shape.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "height_map_shape.h" -#include "servers/physics_server.h" - -Vector HeightMapShape::get_debug_mesh_lines() { - Vector points; - - if ((map_width != 0) && (map_depth != 0)) { - - // This will be slow for large maps... - // also we'll have to figure out how well bullet centers this shape... - - Vector2 size(map_width - 1, map_depth - 1); - Vector2 start = size * -0.5; - - const real_t *r = map_data.ptr(); - - // reserve some memory for our points.. - points.resize(((map_width - 1) * map_depth * 2) + (map_width * (map_depth - 1) * 2)); - - // now set our points - int r_offset = 0; - int w_offset = 0; - for (int d = 0; d < map_depth; d++) { - Vector3 height(start.x, 0.0, start.y); - - for (int w = 0; w < map_width; w++) { - height.y = r[r_offset++]; - - if (w != map_width - 1) { - points.write[w_offset++] = height; - points.write[w_offset++] = Vector3(height.x + 1.0, r[r_offset], height.z); - } - - if (d != map_depth - 1) { - points.write[w_offset++] = height; - points.write[w_offset++] = Vector3(height.x, r[r_offset + map_width - 1], height.z + 1.0); - } - - height.x += 1.0; - } - - start.y += 1.0; - } - } - - return points; -} - -real_t HeightMapShape::get_enclosing_radius() const { - return Vector3(real_t(map_width), max_height - min_height, real_t(map_depth)).length(); -} - -void HeightMapShape::_update_shape() { - - Dictionary d; - d["width"] = map_width; - d["depth"] = map_depth; - d["heights"] = map_data; - d["min_height"] = min_height; - d["max_height"] = max_height; - PhysicsServer::get_singleton()->shape_set_data(get_shape(), d); - Shape3D::_update_shape(); -} - -void HeightMapShape::set_map_width(int p_new) { - if (p_new < 1) { - // ignore - } else if (map_width != p_new) { - int was_size = map_width * map_depth; - map_width = p_new; - - int new_size = map_width * map_depth; - map_data.resize(map_width * map_depth); - - real_t *w = map_data.ptrw(); - while (was_size < new_size) { - w[was_size++] = 0.0; - } - - _update_shape(); - notify_change_to_owners(); - _change_notify("map_width"); - _change_notify("map_data"); - } -} - -int HeightMapShape::get_map_width() const { - return map_width; -} - -void HeightMapShape::set_map_depth(int p_new) { - if (p_new < 1) { - // ignore - } else if (map_depth != p_new) { - int was_size = map_width * map_depth; - map_depth = p_new; - - int new_size = map_width * map_depth; - map_data.resize(new_size); - - real_t *w = map_data.ptrw(); - while (was_size < new_size) { - w[was_size++] = 0.0; - } - - _update_shape(); - notify_change_to_owners(); - _change_notify("map_depth"); - _change_notify("map_data"); - } -} - -int HeightMapShape::get_map_depth() const { - return map_depth; -} - -void HeightMapShape::set_map_data(PackedFloat32Array p_new) { - int size = (map_width * map_depth); - if (p_new.size() != size) { - // fail - return; - } - - // copy - real_t *w = map_data.ptrw(); - const real_t *r = p_new.ptr(); - for (int i = 0; i < size; i++) { - float val = r[i]; - w[i] = val; - if (i == 0) { - min_height = val; - max_height = val; - } else { - if (min_height > val) - min_height = val; - - if (max_height < val) - max_height = val; - } - } - - _update_shape(); - notify_change_to_owners(); - _change_notify("map_data"); -} - -PackedFloat32Array HeightMapShape::get_map_data() const { - return map_data; -} - -void HeightMapShape::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_map_width", "width"), &HeightMapShape::set_map_width); - ClassDB::bind_method(D_METHOD("get_map_width"), &HeightMapShape::get_map_width); - ClassDB::bind_method(D_METHOD("set_map_depth", "height"), &HeightMapShape::set_map_depth); - ClassDB::bind_method(D_METHOD("get_map_depth"), &HeightMapShape::get_map_depth); - ClassDB::bind_method(D_METHOD("set_map_data", "data"), &HeightMapShape::set_map_data); - ClassDB::bind_method(D_METHOD("get_map_data"), &HeightMapShape::get_map_data); - - ADD_PROPERTY(PropertyInfo(Variant::INT, "map_width", PROPERTY_HINT_RANGE, "1,4096,1"), "set_map_width", "get_map_width"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "map_depth", PROPERTY_HINT_RANGE, "1,4096,1"), "set_map_depth", "get_map_depth"); - ADD_PROPERTY(PropertyInfo(Variant::PACKED_FLOAT32_ARRAY, "map_data"), "set_map_data", "get_map_data"); -} - -HeightMapShape::HeightMapShape() : - Shape3D(PhysicsServer::get_singleton()->shape_create(PhysicsServer::SHAPE_HEIGHTMAP)) { - - map_width = 2; - map_depth = 2; - map_data.resize(map_width * map_depth); - real_t *w = map_data.ptrw(); - w[0] = 0.0; - w[1] = 0.0; - w[2] = 0.0; - w[3] = 0.0; - min_height = 0.0; - max_height = 0.0; - - _update_shape(); -} diff --git a/scene/resources/height_map_shape.h b/scene/resources/height_map_shape.h deleted file mode 100644 index e740388a02..0000000000 --- a/scene/resources/height_map_shape.h +++ /dev/null @@ -1,63 +0,0 @@ -/*************************************************************************/ -/* height_map_shape.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef HEIGHT_MAP_SHAPE_H -#define HEIGHT_MAP_SHAPE_H - -#include "scene/resources/shape_3d.h" - -class HeightMapShape : public Shape3D { - GDCLASS(HeightMapShape, Shape3D); - - int map_width; - int map_depth; - PackedFloat32Array map_data; - float min_height; - float max_height; - -protected: - static void _bind_methods(); - virtual void _update_shape(); - -public: - void set_map_width(int p_new); - int get_map_width() const; - void set_map_depth(int p_new); - int get_map_depth() const; - void set_map_data(PackedFloat32Array p_new); - PackedFloat32Array get_map_data() const; - - virtual Vector get_debug_mesh_lines(); - virtual real_t get_enclosing_radius() const; - - HeightMapShape(); -}; - -#endif /* !HEIGHT_MAP_SHAPE_H */ diff --git a/scene/resources/height_map_shape_3d.cpp b/scene/resources/height_map_shape_3d.cpp new file mode 100644 index 0000000000..85c081238d --- /dev/null +++ b/scene/resources/height_map_shape_3d.cpp @@ -0,0 +1,209 @@ +/*************************************************************************/ +/* height_map_shape_3d.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "height_map_shape_3d.h" +#include "servers/physics_server.h" + +Vector HeightMapShape3D::get_debug_mesh_lines() { + Vector points; + + if ((map_width != 0) && (map_depth != 0)) { + + // This will be slow for large maps... + // also we'll have to figure out how well bullet centers this shape... + + Vector2 size(map_width - 1, map_depth - 1); + Vector2 start = size * -0.5; + + const real_t *r = map_data.ptr(); + + // reserve some memory for our points.. + points.resize(((map_width - 1) * map_depth * 2) + (map_width * (map_depth - 1) * 2)); + + // now set our points + int r_offset = 0; + int w_offset = 0; + for (int d = 0; d < map_depth; d++) { + Vector3 height(start.x, 0.0, start.y); + + for (int w = 0; w < map_width; w++) { + height.y = r[r_offset++]; + + if (w != map_width - 1) { + points.write[w_offset++] = height; + points.write[w_offset++] = Vector3(height.x + 1.0, r[r_offset], height.z); + } + + if (d != map_depth - 1) { + points.write[w_offset++] = height; + points.write[w_offset++] = Vector3(height.x, r[r_offset + map_width - 1], height.z + 1.0); + } + + height.x += 1.0; + } + + start.y += 1.0; + } + } + + return points; +} + +real_t HeightMapShape3D::get_enclosing_radius() const { + return Vector3(real_t(map_width), max_height - min_height, real_t(map_depth)).length(); +} + +void HeightMapShape3D::_update_shape() { + + Dictionary d; + d["width"] = map_width; + d["depth"] = map_depth; + d["heights"] = map_data; + d["min_height"] = min_height; + d["max_height"] = max_height; + PhysicsServer::get_singleton()->shape_set_data(get_shape(), d); + Shape3D::_update_shape(); +} + +void HeightMapShape3D::set_map_width(int p_new) { + if (p_new < 1) { + // ignore + } else if (map_width != p_new) { + int was_size = map_width * map_depth; + map_width = p_new; + + int new_size = map_width * map_depth; + map_data.resize(map_width * map_depth); + + real_t *w = map_data.ptrw(); + while (was_size < new_size) { + w[was_size++] = 0.0; + } + + _update_shape(); + notify_change_to_owners(); + _change_notify("map_width"); + _change_notify("map_data"); + } +} + +int HeightMapShape3D::get_map_width() const { + return map_width; +} + +void HeightMapShape3D::set_map_depth(int p_new) { + if (p_new < 1) { + // ignore + } else if (map_depth != p_new) { + int was_size = map_width * map_depth; + map_depth = p_new; + + int new_size = map_width * map_depth; + map_data.resize(new_size); + + real_t *w = map_data.ptrw(); + while (was_size < new_size) { + w[was_size++] = 0.0; + } + + _update_shape(); + notify_change_to_owners(); + _change_notify("map_depth"); + _change_notify("map_data"); + } +} + +int HeightMapShape3D::get_map_depth() const { + return map_depth; +} + +void HeightMapShape3D::set_map_data(PackedFloat32Array p_new) { + int size = (map_width * map_depth); + if (p_new.size() != size) { + // fail + return; + } + + // copy + real_t *w = map_data.ptrw(); + const real_t *r = p_new.ptr(); + for (int i = 0; i < size; i++) { + float val = r[i]; + w[i] = val; + if (i == 0) { + min_height = val; + max_height = val; + } else { + if (min_height > val) + min_height = val; + + if (max_height < val) + max_height = val; + } + } + + _update_shape(); + notify_change_to_owners(); + _change_notify("map_data"); +} + +PackedFloat32Array HeightMapShape3D::get_map_data() const { + return map_data; +} + +void HeightMapShape3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_map_width", "width"), &HeightMapShape3D::set_map_width); + ClassDB::bind_method(D_METHOD("get_map_width"), &HeightMapShape3D::get_map_width); + ClassDB::bind_method(D_METHOD("set_map_depth", "height"), &HeightMapShape3D::set_map_depth); + ClassDB::bind_method(D_METHOD("get_map_depth"), &HeightMapShape3D::get_map_depth); + ClassDB::bind_method(D_METHOD("set_map_data", "data"), &HeightMapShape3D::set_map_data); + ClassDB::bind_method(D_METHOD("get_map_data"), &HeightMapShape3D::get_map_data); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "map_width", PROPERTY_HINT_RANGE, "1,4096,1"), "set_map_width", "get_map_width"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "map_depth", PROPERTY_HINT_RANGE, "1,4096,1"), "set_map_depth", "get_map_depth"); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_FLOAT32_ARRAY, "map_data"), "set_map_data", "get_map_data"); +} + +HeightMapShape3D::HeightMapShape3D() : + Shape3D(PhysicsServer::get_singleton()->shape_create(PhysicsServer::SHAPE_HEIGHTMAP)) { + + map_width = 2; + map_depth = 2; + map_data.resize(map_width * map_depth); + real_t *w = map_data.ptrw(); + w[0] = 0.0; + w[1] = 0.0; + w[2] = 0.0; + w[3] = 0.0; + min_height = 0.0; + max_height = 0.0; + + _update_shape(); +} diff --git a/scene/resources/height_map_shape_3d.h b/scene/resources/height_map_shape_3d.h new file mode 100644 index 0000000000..291d41a34e --- /dev/null +++ b/scene/resources/height_map_shape_3d.h @@ -0,0 +1,63 @@ +/*************************************************************************/ +/* height_map_shape_3d.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef HEIGHT_MAP_SHAPE_H +#define HEIGHT_MAP_SHAPE_H + +#include "scene/resources/shape_3d.h" + +class HeightMapShape3D : public Shape3D { + GDCLASS(HeightMapShape3D, Shape3D); + + int map_width; + int map_depth; + PackedFloat32Array map_data; + float min_height; + float max_height; + +protected: + static void _bind_methods(); + virtual void _update_shape(); + +public: + void set_map_width(int p_new); + int get_map_width() const; + void set_map_depth(int p_new); + int get_map_depth() const; + void set_map_data(PackedFloat32Array p_new); + PackedFloat32Array get_map_data() const; + + virtual Vector get_debug_mesh_lines(); + virtual real_t get_enclosing_radius() const; + + HeightMapShape3D(); +}; + +#endif /* !HEIGHT_MAP_SHAPE_H */ diff --git a/scene/resources/ray_shape.cpp b/scene/resources/ray_shape.cpp deleted file mode 100644 index f4f8eff1bd..0000000000 --- a/scene/resources/ray_shape.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/*************************************************************************/ -/* ray_shape.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "ray_shape.h" - -#include "servers/physics_server.h" - -Vector RayShape3D::get_debug_mesh_lines() { - - Vector points; - points.push_back(Vector3()); - points.push_back(Vector3(0, 0, get_length())); - - return points; -} - -real_t RayShape3D::get_enclosing_radius() const { - return length; -} - -void RayShape3D::_update_shape() { - - Dictionary d; - d["length"] = length; - d["slips_on_slope"] = slips_on_slope; - PhysicsServer::get_singleton()->shape_set_data(get_shape(), d); - Shape3D::_update_shape(); -} - -void RayShape3D::set_length(float p_length) { - - length = p_length; - _update_shape(); - notify_change_to_owners(); - _change_notify("length"); -} - -float RayShape3D::get_length() const { - - return length; -} - -void RayShape3D::set_slips_on_slope(bool p_active) { - - slips_on_slope = p_active; - _update_shape(); - notify_change_to_owners(); - _change_notify("slips_on_slope"); -} - -bool RayShape3D::get_slips_on_slope() const { - return slips_on_slope; -} - -void RayShape3D::_bind_methods() { - - ClassDB::bind_method(D_METHOD("set_length", "length"), &RayShape3D::set_length); - ClassDB::bind_method(D_METHOD("get_length"), &RayShape3D::get_length); - - ClassDB::bind_method(D_METHOD("set_slips_on_slope", "active"), &RayShape3D::set_slips_on_slope); - ClassDB::bind_method(D_METHOD("get_slips_on_slope"), &RayShape3D::get_slips_on_slope); - - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "length", PROPERTY_HINT_RANGE, "0,4096,0.01"), "set_length", "get_length"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slips_on_slope"), "set_slips_on_slope", "get_slips_on_slope"); -} - -RayShape3D::RayShape3D() : - Shape3D(PhysicsServer::get_singleton()->shape_create(PhysicsServer::SHAPE_RAY)) { - - length = 1.0; - slips_on_slope = false; - - /* Code copied from setters to prevent the use of uninitialized variables */ - _update_shape(); - notify_change_to_owners(); - _change_notify("length"); - _change_notify("slips_on_slope"); -} diff --git a/scene/resources/ray_shape.h b/scene/resources/ray_shape.h deleted file mode 100644 index 1d8c482c93..0000000000 --- a/scene/resources/ray_shape.h +++ /dev/null @@ -1,57 +0,0 @@ -/*************************************************************************/ -/* ray_shape.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef RAY_SHAPE_H -#define RAY_SHAPE_H -#include "scene/resources/shape_3d.h" - -class RayShape3D : public Shape3D { - - GDCLASS(RayShape3D, Shape3D); - float length; - bool slips_on_slope; - -protected: - static void _bind_methods(); - virtual void _update_shape(); - -public: - void set_length(float p_length); - float get_length() const; - - void set_slips_on_slope(bool p_active); - bool get_slips_on_slope() const; - - virtual Vector get_debug_mesh_lines(); - virtual real_t get_enclosing_radius() const; - - RayShape3D(); -}; -#endif // RAY_SHAPE_H diff --git a/scene/resources/ray_shape_3d.cpp b/scene/resources/ray_shape_3d.cpp new file mode 100644 index 0000000000..ad927afd58 --- /dev/null +++ b/scene/resources/ray_shape_3d.cpp @@ -0,0 +1,105 @@ +/*************************************************************************/ +/* ray_shape_3d.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "ray_shape_3d.h" + +#include "servers/physics_server.h" + +Vector RayShape3D::get_debug_mesh_lines() { + + Vector points; + points.push_back(Vector3()); + points.push_back(Vector3(0, 0, get_length())); + + return points; +} + +real_t RayShape3D::get_enclosing_radius() const { + return length; +} + +void RayShape3D::_update_shape() { + + Dictionary d; + d["length"] = length; + d["slips_on_slope"] = slips_on_slope; + PhysicsServer::get_singleton()->shape_set_data(get_shape(), d); + Shape3D::_update_shape(); +} + +void RayShape3D::set_length(float p_length) { + + length = p_length; + _update_shape(); + notify_change_to_owners(); + _change_notify("length"); +} + +float RayShape3D::get_length() const { + + return length; +} + +void RayShape3D::set_slips_on_slope(bool p_active) { + + slips_on_slope = p_active; + _update_shape(); + notify_change_to_owners(); + _change_notify("slips_on_slope"); +} + +bool RayShape3D::get_slips_on_slope() const { + return slips_on_slope; +} + +void RayShape3D::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_length", "length"), &RayShape3D::set_length); + ClassDB::bind_method(D_METHOD("get_length"), &RayShape3D::get_length); + + ClassDB::bind_method(D_METHOD("set_slips_on_slope", "active"), &RayShape3D::set_slips_on_slope); + ClassDB::bind_method(D_METHOD("get_slips_on_slope"), &RayShape3D::get_slips_on_slope); + + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "length", PROPERTY_HINT_RANGE, "0,4096,0.01"), "set_length", "get_length"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slips_on_slope"), "set_slips_on_slope", "get_slips_on_slope"); +} + +RayShape3D::RayShape3D() : + Shape3D(PhysicsServer::get_singleton()->shape_create(PhysicsServer::SHAPE_RAY)) { + + length = 1.0; + slips_on_slope = false; + + /* Code copied from setters to prevent the use of uninitialized variables */ + _update_shape(); + notify_change_to_owners(); + _change_notify("length"); + _change_notify("slips_on_slope"); +} diff --git a/scene/resources/ray_shape_3d.h b/scene/resources/ray_shape_3d.h new file mode 100644 index 0000000000..83bb71cca3 --- /dev/null +++ b/scene/resources/ray_shape_3d.h @@ -0,0 +1,57 @@ +/*************************************************************************/ +/* ray_shape_3d.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef RAY_SHAPE_H +#define RAY_SHAPE_H +#include "scene/resources/shape_3d.h" + +class RayShape3D : public Shape3D { + + GDCLASS(RayShape3D, Shape3D); + float length; + bool slips_on_slope; + +protected: + static void _bind_methods(); + virtual void _update_shape(); + +public: + void set_length(float p_length); + float get_length() const; + + void set_slips_on_slope(bool p_active); + bool get_slips_on_slope() const; + + virtual Vector get_debug_mesh_lines(); + virtual real_t get_enclosing_radius() const; + + RayShape3D(); +}; +#endif // RAY_SHAPE_H diff --git a/scene/resources/style_box.cpp b/scene/resources/style_box.cpp index 119cbcd098..0cd2bed71d 100644 --- a/scene/resources/style_box.cpp +++ b/scene/resources/style_box.cpp @@ -29,7 +29,8 @@ /*************************************************************************/ #include "style_box.h" -#include "scene/2d/canvas_item.h" + +#include "scene/main/canvas_item.h" #include diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h index 3c964ec667..5252c560a4 100644 --- a/scene/resources/tile_set.h +++ b/scene/resources/tile_set.h @@ -34,7 +34,7 @@ #include "core/array.h" #include "core/resource.h" #include "scene/2d/light_occluder_2d.h" -#include "scene/2d/navigation_polygon.h" +#include "scene/2d/navigation_region_2d.h" #include "scene/resources/convex_polygon_shape_2d.h" #include "scene/resources/shape_2d.h" #include "scene/resources/texture.h" diff --git a/scene/resources/world.cpp b/scene/resources/world.cpp deleted file mode 100644 index 43903db277..0000000000 --- a/scene/resources/world.cpp +++ /dev/null @@ -1,382 +0,0 @@ -/*************************************************************************/ -/* world.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "world.h" - -#include "core/math/camera_matrix.h" -#include "core/math/octree.h" -#include "scene/3d/camera_3d.h" -#include "scene/3d/visibility_notifier_3d.h" -#include "scene/scene_string_names.h" - -struct SpatialIndexer { - - Octree octree; - - struct NotifierData { - - AABB aabb; - OctreeElementID id; - }; - - Map notifiers; - struct CameraData { - - Map notifiers; - }; - - Map cameras; - - enum { - VISIBILITY_CULL_MAX = 32768 - }; - - Vector cull; - - bool changed; - uint64_t pass; - uint64_t last_frame; - - void _notifier_add(VisibilityNotifier3D *p_notifier, const AABB &p_rect) { - - ERR_FAIL_COND(notifiers.has(p_notifier)); - notifiers[p_notifier].aabb = p_rect; - notifiers[p_notifier].id = octree.create(p_notifier, p_rect); - changed = true; - } - - void _notifier_update(VisibilityNotifier3D *p_notifier, const AABB &p_rect) { - - Map::Element *E = notifiers.find(p_notifier); - ERR_FAIL_COND(!E); - if (E->get().aabb == p_rect) - return; - - E->get().aabb = p_rect; - octree.move(E->get().id, E->get().aabb); - changed = true; - } - - void _notifier_remove(VisibilityNotifier3D *p_notifier) { - - Map::Element *E = notifiers.find(p_notifier); - ERR_FAIL_COND(!E); - - octree.erase(E->get().id); - notifiers.erase(p_notifier); - - List removed; - for (Map::Element *F = cameras.front(); F; F = F->next()) { - - Map::Element *G = F->get().notifiers.find(p_notifier); - - if (G) { - F->get().notifiers.erase(G); - removed.push_back(F->key()); - } - } - - while (!removed.empty()) { - - p_notifier->_exit_camera(removed.front()->get()); - removed.pop_front(); - } - - changed = true; - } - - void _add_camera(Camera3D *p_camera) { - - ERR_FAIL_COND(cameras.has(p_camera)); - CameraData vd; - cameras[p_camera] = vd; - changed = true; - } - - void _update_camera(Camera3D *p_camera) { - - Map::Element *E = cameras.find(p_camera); - ERR_FAIL_COND(!E); - changed = true; - } - - void _remove_camera(Camera3D *p_camera) { - ERR_FAIL_COND(!cameras.has(p_camera)); - List removed; - for (Map::Element *E = cameras[p_camera].notifiers.front(); E; E = E->next()) { - - removed.push_back(E->key()); - } - - while (!removed.empty()) { - removed.front()->get()->_exit_camera(p_camera); - removed.pop_front(); - } - - cameras.erase(p_camera); - } - - void _update(uint64_t p_frame) { - - if (p_frame == last_frame) - return; - last_frame = p_frame; - - if (!changed) - return; - - for (Map::Element *E = cameras.front(); E; E = E->next()) { - - pass++; - - Camera3D *c = E->key(); - - Vector planes = c->get_frustum(); - - int culled = octree.cull_convex(planes, cull.ptrw(), cull.size()); - - VisibilityNotifier3D **ptr = cull.ptrw(); - - List added; - List removed; - - for (int i = 0; i < culled; i++) { - - //notifiers in frustum - - Map::Element *H = E->get().notifiers.find(ptr[i]); - if (!H) { - - E->get().notifiers.insert(ptr[i], pass); - added.push_back(ptr[i]); - } else { - H->get() = pass; - } - } - - for (Map::Element *F = E->get().notifiers.front(); F; F = F->next()) { - - if (F->get() != pass) - removed.push_back(F->key()); - } - - while (!added.empty()) { - added.front()->get()->_enter_camera(E->key()); - added.pop_front(); - } - - while (!removed.empty()) { - E->get().notifiers.erase(removed.front()->get()); - removed.front()->get()->_exit_camera(E->key()); - removed.pop_front(); - } - } - changed = false; - } - - SpatialIndexer() { - - pass = 0; - last_frame = 0; - changed = false; - cull.resize(VISIBILITY_CULL_MAX); - } -}; - -void World::_register_camera(Camera3D *p_camera) { - -#ifndef _3D_DISABLED - indexer->_add_camera(p_camera); -#endif -} - -void World::_update_camera(Camera3D *p_camera) { - -#ifndef _3D_DISABLED - indexer->_update_camera(p_camera); -#endif -} -void World::_remove_camera(Camera3D *p_camera) { - -#ifndef _3D_DISABLED - indexer->_remove_camera(p_camera); -#endif -} - -void World::_register_notifier(VisibilityNotifier3D *p_notifier, const AABB &p_rect) { - -#ifndef _3D_DISABLED - indexer->_notifier_add(p_notifier, p_rect); -#endif -} - -void World::_update_notifier(VisibilityNotifier3D *p_notifier, const AABB &p_rect) { - -#ifndef _3D_DISABLED - indexer->_notifier_update(p_notifier, p_rect); -#endif -} - -void World::_remove_notifier(VisibilityNotifier3D *p_notifier) { - -#ifndef _3D_DISABLED - indexer->_notifier_remove(p_notifier); -#endif -} - -void World::_update(uint64_t p_frame) { - -#ifndef _3D_DISABLED - indexer->_update(p_frame); -#endif -} - -RID World::get_space() const { - - return space; -} - -RID World::get_scenario() const { - - return scenario; -} - -void World::set_environment(const Ref &p_environment) { - if (environment == p_environment) { - return; - } - - environment = p_environment; - if (environment.is_valid()) - VS::get_singleton()->scenario_set_environment(scenario, environment->get_rid()); - else - VS::get_singleton()->scenario_set_environment(scenario, RID()); - - emit_changed(); -} - -Ref World::get_environment() const { - - return environment; -} - -void World::set_fallback_environment(const Ref &p_environment) { - if (fallback_environment == p_environment) { - return; - } - - fallback_environment = p_environment; - if (fallback_environment.is_valid()) - VS::get_singleton()->scenario_set_fallback_environment(scenario, p_environment->get_rid()); - else - VS::get_singleton()->scenario_set_fallback_environment(scenario, RID()); - - emit_changed(); -} - -Ref World::get_fallback_environment() const { - - return fallback_environment; -} - -void World::set_camera_effects(const Ref &p_camera_effects) { - - camera_effects = p_camera_effects; - if (camera_effects.is_valid()) - VS::get_singleton()->scenario_set_camera_effects(scenario, camera_effects->get_rid()); - else - VS::get_singleton()->scenario_set_camera_effects(scenario, RID()); -} - -Ref World::get_camera_effects() const { - - return camera_effects; -} - -PhysicsDirectSpaceState *World::get_direct_space_state() { - - return PhysicsServer::get_singleton()->space_get_direct_state(space); -} - -void World::get_camera_list(List *r_cameras) { - - for (Map::Element *E = indexer->cameras.front(); E; E = E->next()) { - r_cameras->push_back(E->key()); - } -} - -void World::_bind_methods() { - - ClassDB::bind_method(D_METHOD("get_space"), &World::get_space); - ClassDB::bind_method(D_METHOD("get_scenario"), &World::get_scenario); - ClassDB::bind_method(D_METHOD("set_environment", "env"), &World::set_environment); - ClassDB::bind_method(D_METHOD("get_environment"), &World::get_environment); - ClassDB::bind_method(D_METHOD("set_fallback_environment", "env"), &World::set_fallback_environment); - ClassDB::bind_method(D_METHOD("get_fallback_environment"), &World::get_fallback_environment); - ClassDB::bind_method(D_METHOD("set_camera_effects", "env"), &World::set_camera_effects); - ClassDB::bind_method(D_METHOD("get_camera_effects"), &World::get_camera_effects); - ClassDB::bind_method(D_METHOD("get_direct_space_state"), &World::get_direct_space_state); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "environment", PROPERTY_HINT_RESOURCE_TYPE, "Environment"), "set_environment", "get_environment"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "fallback_environment", PROPERTY_HINT_RESOURCE_TYPE, "Environment"), "set_fallback_environment", "get_fallback_environment"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "camera_effects", PROPERTY_HINT_RESOURCE_TYPE, "CameraEffects"), "set_camera_effects", "get_camera_effects"); - ADD_PROPERTY(PropertyInfo(Variant::_RID, "space", PROPERTY_HINT_NONE, "", 0), "", "get_space"); - ADD_PROPERTY(PropertyInfo(Variant::_RID, "scenario", PROPERTY_HINT_NONE, "", 0), "", "get_scenario"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "direct_space_state", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsDirectSpaceState", 0), "", "get_direct_space_state"); -} - -World::World() { - - space = PhysicsServer::get_singleton()->space_create(); - scenario = VisualServer::get_singleton()->scenario_create(); - - PhysicsServer::get_singleton()->space_set_active(space, true); - PhysicsServer::get_singleton()->area_set_param(space, PhysicsServer::AREA_PARAM_GRAVITY, GLOBAL_DEF("physics/3d/default_gravity", 9.8)); - PhysicsServer::get_singleton()->area_set_param(space, PhysicsServer::AREA_PARAM_GRAVITY_VECTOR, GLOBAL_DEF("physics/3d/default_gravity_vector", Vector3(0, -1, 0))); - PhysicsServer::get_singleton()->area_set_param(space, PhysicsServer::AREA_PARAM_LINEAR_DAMP, GLOBAL_DEF("physics/3d/default_linear_damp", 0.1)); - ProjectSettings::get_singleton()->set_custom_property_info("physics/3d/default_linear_damp", PropertyInfo(Variant::FLOAT, "physics/3d/default_linear_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater")); - PhysicsServer::get_singleton()->area_set_param(space, PhysicsServer::AREA_PARAM_ANGULAR_DAMP, GLOBAL_DEF("physics/3d/default_angular_damp", 0.1)); - ProjectSettings::get_singleton()->set_custom_property_info("physics/3d/default_angular_damp", PropertyInfo(Variant::FLOAT, "physics/3d/default_angular_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater")); - -#ifdef _3D_DISABLED - indexer = NULL; -#else - indexer = memnew(SpatialIndexer); -#endif -} - -World::~World() { - - PhysicsServer::get_singleton()->free(space); - VisualServer::get_singleton()->free(scenario); - -#ifndef _3D_DISABLED - memdelete(indexer); -#endif -} diff --git a/scene/resources/world.h b/scene/resources/world.h deleted file mode 100644 index 8132a018d2..0000000000 --- a/scene/resources/world.h +++ /dev/null @@ -1,92 +0,0 @@ -/*************************************************************************/ -/* world.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef WORLD_H -#define WORLD_H - -#include "core/resource.h" -#include "scene/resources/environment.h" -#include "servers/physics_server.h" -#include "servers/visual_server.h" - -class Camera3D; -class VisibilityNotifier3D; -struct SpatialIndexer; - -class World : public Resource { - GDCLASS(World, Resource); - RES_BASE_EXTENSION("world"); - -private: - RID space; - RID scenario; - SpatialIndexer *indexer; - Ref environment; - Ref fallback_environment; - Ref camera_effects; - -protected: - static void _bind_methods(); - - friend class Camera3D; - friend class VisibilityNotifier3D; - - void _register_camera(Camera3D *p_camera); - void _update_camera(Camera3D *p_camera); - void _remove_camera(Camera3D *p_camera); - - void _register_notifier(VisibilityNotifier3D *p_notifier, const AABB &p_rect); - void _update_notifier(VisibilityNotifier3D *p_notifier, const AABB &p_rect); - void _remove_notifier(VisibilityNotifier3D *p_notifier); - friend class Viewport; - void _update(uint64_t p_frame); - -public: - RID get_space() const; - RID get_scenario() const; - - void set_environment(const Ref &p_environment); - Ref get_environment() const; - - void set_fallback_environment(const Ref &p_environment); - Ref get_fallback_environment() const; - - void set_camera_effects(const Ref &p_camera_effects); - Ref get_camera_effects() const; - - void get_camera_list(List *r_cameras); - - PhysicsDirectSpaceState *get_direct_space_state(); - - World(); - ~World(); -}; - -#endif // WORLD_H diff --git a/scene/resources/world_3d.cpp b/scene/resources/world_3d.cpp new file mode 100644 index 0000000000..0a687af803 --- /dev/null +++ b/scene/resources/world_3d.cpp @@ -0,0 +1,382 @@ +/*************************************************************************/ +/* world_3d.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "world_3d.h" + +#include "core/math/camera_matrix.h" +#include "core/math/octree.h" +#include "scene/3d/camera_3d.h" +#include "scene/3d/visibility_notifier_3d.h" +#include "scene/scene_string_names.h" + +struct SpatialIndexer { + + Octree octree; + + struct NotifierData { + + AABB aabb; + OctreeElementID id; + }; + + Map notifiers; + struct CameraData { + + Map notifiers; + }; + + Map cameras; + + enum { + VISIBILITY_CULL_MAX = 32768 + }; + + Vector cull; + + bool changed; + uint64_t pass; + uint64_t last_frame; + + void _notifier_add(VisibilityNotifier3D *p_notifier, const AABB &p_rect) { + + ERR_FAIL_COND(notifiers.has(p_notifier)); + notifiers[p_notifier].aabb = p_rect; + notifiers[p_notifier].id = octree.create(p_notifier, p_rect); + changed = true; + } + + void _notifier_update(VisibilityNotifier3D *p_notifier, const AABB &p_rect) { + + Map::Element *E = notifiers.find(p_notifier); + ERR_FAIL_COND(!E); + if (E->get().aabb == p_rect) + return; + + E->get().aabb = p_rect; + octree.move(E->get().id, E->get().aabb); + changed = true; + } + + void _notifier_remove(VisibilityNotifier3D *p_notifier) { + + Map::Element *E = notifiers.find(p_notifier); + ERR_FAIL_COND(!E); + + octree.erase(E->get().id); + notifiers.erase(p_notifier); + + List removed; + for (Map::Element *F = cameras.front(); F; F = F->next()) { + + Map::Element *G = F->get().notifiers.find(p_notifier); + + if (G) { + F->get().notifiers.erase(G); + removed.push_back(F->key()); + } + } + + while (!removed.empty()) { + + p_notifier->_exit_camera(removed.front()->get()); + removed.pop_front(); + } + + changed = true; + } + + void _add_camera(Camera3D *p_camera) { + + ERR_FAIL_COND(cameras.has(p_camera)); + CameraData vd; + cameras[p_camera] = vd; + changed = true; + } + + void _update_camera(Camera3D *p_camera) { + + Map::Element *E = cameras.find(p_camera); + ERR_FAIL_COND(!E); + changed = true; + } + + void _remove_camera(Camera3D *p_camera) { + ERR_FAIL_COND(!cameras.has(p_camera)); + List removed; + for (Map::Element *E = cameras[p_camera].notifiers.front(); E; E = E->next()) { + + removed.push_back(E->key()); + } + + while (!removed.empty()) { + removed.front()->get()->_exit_camera(p_camera); + removed.pop_front(); + } + + cameras.erase(p_camera); + } + + void _update(uint64_t p_frame) { + + if (p_frame == last_frame) + return; + last_frame = p_frame; + + if (!changed) + return; + + for (Map::Element *E = cameras.front(); E; E = E->next()) { + + pass++; + + Camera3D *c = E->key(); + + Vector planes = c->get_frustum(); + + int culled = octree.cull_convex(planes, cull.ptrw(), cull.size()); + + VisibilityNotifier3D **ptr = cull.ptrw(); + + List added; + List removed; + + for (int i = 0; i < culled; i++) { + + //notifiers in frustum + + Map::Element *H = E->get().notifiers.find(ptr[i]); + if (!H) { + + E->get().notifiers.insert(ptr[i], pass); + added.push_back(ptr[i]); + } else { + H->get() = pass; + } + } + + for (Map::Element *F = E->get().notifiers.front(); F; F = F->next()) { + + if (F->get() != pass) + removed.push_back(F->key()); + } + + while (!added.empty()) { + added.front()->get()->_enter_camera(E->key()); + added.pop_front(); + } + + while (!removed.empty()) { + E->get().notifiers.erase(removed.front()->get()); + removed.front()->get()->_exit_camera(E->key()); + removed.pop_front(); + } + } + changed = false; + } + + SpatialIndexer() { + + pass = 0; + last_frame = 0; + changed = false; + cull.resize(VISIBILITY_CULL_MAX); + } +}; + +void World3D::_register_camera(Camera3D *p_camera) { + +#ifndef _3D_DISABLED + indexer->_add_camera(p_camera); +#endif +} + +void World3D::_update_camera(Camera3D *p_camera) { + +#ifndef _3D_DISABLED + indexer->_update_camera(p_camera); +#endif +} +void World3D::_remove_camera(Camera3D *p_camera) { + +#ifndef _3D_DISABLED + indexer->_remove_camera(p_camera); +#endif +} + +void World3D::_register_notifier(VisibilityNotifier3D *p_notifier, const AABB &p_rect) { + +#ifndef _3D_DISABLED + indexer->_notifier_add(p_notifier, p_rect); +#endif +} + +void World3D::_update_notifier(VisibilityNotifier3D *p_notifier, const AABB &p_rect) { + +#ifndef _3D_DISABLED + indexer->_notifier_update(p_notifier, p_rect); +#endif +} + +void World3D::_remove_notifier(VisibilityNotifier3D *p_notifier) { + +#ifndef _3D_DISABLED + indexer->_notifier_remove(p_notifier); +#endif +} + +void World3D::_update(uint64_t p_frame) { + +#ifndef _3D_DISABLED + indexer->_update(p_frame); +#endif +} + +RID World3D::get_space() const { + + return space; +} + +RID World3D::get_scenario() const { + + return scenario; +} + +void World3D::set_environment(const Ref &p_environment) { + if (environment == p_environment) { + return; + } + + environment = p_environment; + if (environment.is_valid()) + VS::get_singleton()->scenario_set_environment(scenario, environment->get_rid()); + else + VS::get_singleton()->scenario_set_environment(scenario, RID()); + + emit_changed(); +} + +Ref World3D::get_environment() const { + + return environment; +} + +void World3D::set_fallback_environment(const Ref &p_environment) { + if (fallback_environment == p_environment) { + return; + } + + fallback_environment = p_environment; + if (fallback_environment.is_valid()) + VS::get_singleton()->scenario_set_fallback_environment(scenario, p_environment->get_rid()); + else + VS::get_singleton()->scenario_set_fallback_environment(scenario, RID()); + + emit_changed(); +} + +Ref World3D::get_fallback_environment() const { + + return fallback_environment; +} + +void World3D::set_camera_effects(const Ref &p_camera_effects) { + + camera_effects = p_camera_effects; + if (camera_effects.is_valid()) + VS::get_singleton()->scenario_set_camera_effects(scenario, camera_effects->get_rid()); + else + VS::get_singleton()->scenario_set_camera_effects(scenario, RID()); +} + +Ref World3D::get_camera_effects() const { + + return camera_effects; +} + +PhysicsDirectSpaceState *World3D::get_direct_space_state() { + + return PhysicsServer::get_singleton()->space_get_direct_state(space); +} + +void World3D::get_camera_list(List *r_cameras) { + + for (Map::Element *E = indexer->cameras.front(); E; E = E->next()) { + r_cameras->push_back(E->key()); + } +} + +void World3D::_bind_methods() { + + ClassDB::bind_method(D_METHOD("get_space"), &World3D::get_space); + ClassDB::bind_method(D_METHOD("get_scenario"), &World3D::get_scenario); + ClassDB::bind_method(D_METHOD("set_environment", "env"), &World3D::set_environment); + ClassDB::bind_method(D_METHOD("get_environment"), &World3D::get_environment); + ClassDB::bind_method(D_METHOD("set_fallback_environment", "env"), &World3D::set_fallback_environment); + ClassDB::bind_method(D_METHOD("get_fallback_environment"), &World3D::get_fallback_environment); + ClassDB::bind_method(D_METHOD("set_camera_effects", "env"), &World3D::set_camera_effects); + ClassDB::bind_method(D_METHOD("get_camera_effects"), &World3D::get_camera_effects); + ClassDB::bind_method(D_METHOD("get_direct_space_state"), &World3D::get_direct_space_state); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "environment", PROPERTY_HINT_RESOURCE_TYPE, "Environment"), "set_environment", "get_environment"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "fallback_environment", PROPERTY_HINT_RESOURCE_TYPE, "Environment"), "set_fallback_environment", "get_fallback_environment"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "camera_effects", PROPERTY_HINT_RESOURCE_TYPE, "CameraEffects"), "set_camera_effects", "get_camera_effects"); + ADD_PROPERTY(PropertyInfo(Variant::_RID, "space", PROPERTY_HINT_NONE, "", 0), "", "get_space"); + ADD_PROPERTY(PropertyInfo(Variant::_RID, "scenario", PROPERTY_HINT_NONE, "", 0), "", "get_scenario"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "direct_space_state", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsDirectSpaceState", 0), "", "get_direct_space_state"); +} + +World3D::World3D() { + + space = PhysicsServer::get_singleton()->space_create(); + scenario = VisualServer::get_singleton()->scenario_create(); + + PhysicsServer::get_singleton()->space_set_active(space, true); + PhysicsServer::get_singleton()->area_set_param(space, PhysicsServer::AREA_PARAM_GRAVITY, GLOBAL_DEF("physics/3d/default_gravity", 9.8)); + PhysicsServer::get_singleton()->area_set_param(space, PhysicsServer::AREA_PARAM_GRAVITY_VECTOR, GLOBAL_DEF("physics/3d/default_gravity_vector", Vector3(0, -1, 0))); + PhysicsServer::get_singleton()->area_set_param(space, PhysicsServer::AREA_PARAM_LINEAR_DAMP, GLOBAL_DEF("physics/3d/default_linear_damp", 0.1)); + ProjectSettings::get_singleton()->set_custom_property_info("physics/3d/default_linear_damp", PropertyInfo(Variant::FLOAT, "physics/3d/default_linear_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater")); + PhysicsServer::get_singleton()->area_set_param(space, PhysicsServer::AREA_PARAM_ANGULAR_DAMP, GLOBAL_DEF("physics/3d/default_angular_damp", 0.1)); + ProjectSettings::get_singleton()->set_custom_property_info("physics/3d/default_angular_damp", PropertyInfo(Variant::FLOAT, "physics/3d/default_angular_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater")); + +#ifdef _3D_DISABLED + indexer = NULL; +#else + indexer = memnew(SpatialIndexer); +#endif +} + +World3D::~World3D() { + + PhysicsServer::get_singleton()->free(space); + VisualServer::get_singleton()->free(scenario); + +#ifndef _3D_DISABLED + memdelete(indexer); +#endif +} diff --git a/scene/resources/world_3d.h b/scene/resources/world_3d.h new file mode 100644 index 0000000000..4c18ba9cea --- /dev/null +++ b/scene/resources/world_3d.h @@ -0,0 +1,91 @@ +/*************************************************************************/ +/* world_3d.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef WORLD_3D_H +#define WORLD_3D_H + +#include "core/resource.h" +#include "scene/resources/environment.h" +#include "servers/physics_server.h" +#include "servers/visual_server.h" + +class Camera3D; +class VisibilityNotifier3D; +struct SpatialIndexer; + +class World3D : public Resource { + GDCLASS(World3D, Resource); + +private: + RID space; + RID scenario; + SpatialIndexer *indexer; + Ref environment; + Ref fallback_environment; + Ref camera_effects; + +protected: + static void _bind_methods(); + + friend class Camera3D; + friend class VisibilityNotifier3D; + + void _register_camera(Camera3D *p_camera); + void _update_camera(Camera3D *p_camera); + void _remove_camera(Camera3D *p_camera); + + void _register_notifier(VisibilityNotifier3D *p_notifier, const AABB &p_rect); + void _update_notifier(VisibilityNotifier3D *p_notifier, const AABB &p_rect); + void _remove_notifier(VisibilityNotifier3D *p_notifier); + friend class Viewport; + void _update(uint64_t p_frame); + +public: + RID get_space() const; + RID get_scenario() const; + + void set_environment(const Ref &p_environment); + Ref get_environment() const; + + void set_fallback_environment(const Ref &p_environment); + Ref get_fallback_environment() const; + + void set_camera_effects(const Ref &p_camera_effects); + Ref get_camera_effects() const; + + void get_camera_list(List *r_cameras); + + PhysicsDirectSpaceState *get_direct_space_state(); + + World3D(); + ~World3D(); +}; + +#endif // WORLD_3D_H diff --git a/servers/navigation_2d_server.h b/servers/navigation_2d_server.h index 7b0b9fbb01..35902f83d4 100644 --- a/servers/navigation_2d_server.h +++ b/servers/navigation_2d_server.h @@ -37,7 +37,7 @@ #include "core/object.h" #include "core/rid.h" -#include "scene/2d/navigation_polygon.h" +#include "scene/2d/navigation_region_2d.h" // This server exposes the 3D `NavigationServer` features in the 2D world. class Navigation2DServer : public Object { -- cgit v1.2.3