summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/io/resource_loader.cpp3
-rw-r--r--doc/classes/Compositor.xml3
-rw-r--r--doc/classes/CompositorEffect.xml3
-rw-r--r--doc/classes/NavigationRegion2D.xml3
-rw-r--r--doc/classes/ProjectSettings.xml3
-rw-r--r--doc/classes/SkeletonModification2DPhysicalBones.xml3
-rw-r--r--doc/classes/XRHandModifier3D.xml34
-rw-r--r--doc/classes/XRHandTracker.xml223
-rw-r--r--doc/classes/XRServer.xml48
-rw-r--r--drivers/d3d12/d3d12ma.cpp1
-rw-r--r--drivers/d3d12/rendering_device_driver_d3d12.cpp7
-rw-r--r--editor/debugger/editor_debugger_node.cpp7
-rw-r--r--editor/editor_audio_buses.cpp5
-rw-r--r--editor/editor_command_palette.cpp1
-rw-r--r--editor/editor_dock_manager.cpp11
-rw-r--r--editor/editor_layouts_dialog.cpp1
-rw-r--r--editor/editor_node.cpp242
-rw-r--r--editor/editor_node.h31
-rw-r--r--editor/editor_plugin.cpp9
-rw-r--r--editor/export/project_export.cpp1
-rw-r--r--editor/gui/editor_bottom_panel.cpp272
-rw-r--r--editor/gui/editor_bottom_panel.h84
-rw-r--r--editor/gui/editor_object_selector.cpp1
-rw-r--r--editor/plugins/animation_player_editor_plugin.cpp7
-rw-r--r--editor/plugins/animation_tree_editor_plugin.cpp8
-rw-r--r--editor/plugins/debugger_editor_plugin.cpp3
-rw-r--r--editor/plugins/font_config_plugin.cpp1
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp18
-rw-r--r--editor/plugins/node_3d_editor_plugin.h1
-rw-r--r--editor/plugins/resource_preloader_editor_plugin.cpp7
-rw-r--r--editor/plugins/script_editor_plugin.cpp7
-rw-r--r--editor/plugins/shader_editor_plugin.cpp5
-rw-r--r--editor/plugins/shader_file_editor_plugin.cpp7
-rw-r--r--editor/plugins/sprite_frames_editor_plugin.cpp5
-rw-r--r--editor/plugins/text_shader_editor.h1
-rw-r--r--editor/plugins/theme_editor_plugin.cpp7
-rw-r--r--editor/plugins/tiles/tiles_editor_plugin.cpp15
-rw-r--r--editor/plugins/version_control_editor_plugin.cpp5
-rw-r--r--editor/run_instances_dialog.cpp55
-rw-r--r--misc/scripts/install_d3d12_sdk_windows.py34
-rw-r--r--modules/gdscript/gdscript_byte_codegen.cpp2
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/match_test_null.gd6
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/match_test_null.out2
-rw-r--r--modules/multiplayer/editor/multiplayer_editor_plugin.cpp11
-rw-r--r--modules/openxr/doc_classes/OpenXRHand.xml2
-rw-r--r--modules/openxr/doc_classes/OpenXRInterface.xml14
-rw-r--r--modules/openxr/editor/openxr_action_map_editor.cpp3
-rw-r--r--modules/openxr/editor/openxr_editor_plugin.cpp3
-rw-r--r--modules/openxr/extensions/openxr_hand_tracking_extension.cpp68
-rw-r--r--modules/openxr/extensions/openxr_hand_tracking_extension.h2
-rw-r--r--platform/android/doc_classes/EditorExportPlatformAndroid.xml30
-rw-r--r--platform/web/doc_classes/EditorExportPlatformWeb.xml3
-rw-r--r--scene/3d/xr_hand_modifier_3d.cpp309
-rw-r--r--scene/3d/xr_hand_modifier_3d.h87
-rw-r--r--scene/gui/color_picker.cpp1
-rw-r--r--scene/gui/popup_menu.cpp134
-rw-r--r--scene/gui/popup_menu.h5
-rw-r--r--scene/main/viewport.cpp2
-rw-r--r--scene/register_scene_types.cpp27
-rw-r--r--scene/resources/mesh.cpp2
-rw-r--r--servers/register_server_types.cpp4
-rw-r--r--servers/xr/xr_hand_tracker.cpp179
-rw-r--r--servers/xr/xr_hand_tracker.h137
-rw-r--r--servers/xr_server.cpp48
-rw-r--r--servers/xr_server.h11
-rw-r--r--tests/core/io/test_image.h5
-rw-r--r--tests/scene/test_primitives.h12
-rw-r--r--tests/test_main.cpp22
-rw-r--r--thirdparty/README.md2
-rw-r--r--thirdparty/tinyexr/tinyexr.h14
70 files changed, 1825 insertions, 504 deletions
diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp
index 5344266a9c..a3fc7bc370 100644
--- a/core/io/resource_loader.cpp
+++ b/core/io/resource_loader.cpp
@@ -658,9 +658,6 @@ Ref<Resource> ResourceLoader::_load_complete_inner(LoadToken &p_load_token, Erro
// resource loading that means that the task to wait for can be restarted here to break the
// cycle, with as much recursion into this process as needed.
// When the stack is eventually unrolled, the original load will have been notified to go on.
-#ifdef DEV_ENABLED
- print_verbose("ResourceLoader: Potential for deadlock detected in task dependency. Attempting to avoid it by re-issuing the load now.");
-#endif
// CACHE_MODE_IGNORE is needed because, otherwise, the new request would just see there's
// an ongoing load for that resource and wait for it again. This value forces a new load.
Ref<ResourceLoader::LoadToken> token = _load_start(load_task.local_path, load_task.type_hint, LOAD_THREAD_DISTRIBUTE, ResourceFormatLoader::CACHE_MODE_IGNORE);
diff --git a/doc/classes/Compositor.xml b/doc/classes/Compositor.xml
index 51336322a3..c287e3b015 100644
--- a/doc/classes/Compositor.xml
+++ b/doc/classes/Compositor.xml
@@ -1,11 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="Compositor" inherits="Resource" experimental="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+<class name="Compositor" inherits="Resource" experimental="More customisation of the rendering pipeline will be added in the future." xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
The compositor resource holds the configuration with which rendering of a viewport can be customised.
</brief_description>
<description>
The compositor resource holds the configuration with which rendering of a viewport can be customised.
- [b]Note:[/b] This functionality is still experimental as more customisation of the rendering pipeline will be added in the near future.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/CompositorEffect.xml b/doc/classes/CompositorEffect.xml
index 38307e2f10..b05dc44cf6 100644
--- a/doc/classes/CompositorEffect.xml
+++ b/doc/classes/CompositorEffect.xml
@@ -1,11 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="CompositorEffect" inherits="Resource" experimental="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+<class name="CompositorEffect" inherits="Resource" experimental="The implementation may change as more of the rendering internals are exposed over time." xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
This resource allows for creating a custom rendering effect.
</brief_description>
<description>
This resource defines a custom rendering effect that can be applied to [Viewport]s through the viewports' [Environment]. You can implement a callback that is called during rendering at a given stage of the rendering pipeline and allows you to insert additional passes. Note that this callback happens on the rendering thread.
- [b]Note:[/b] This functionality is still experimental, there will be changes to the implementation as we expose more of the rendering internals.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/NavigationRegion2D.xml b/doc/classes/NavigationRegion2D.xml
index 8ce1551715..c490620fc0 100644
--- a/doc/classes/NavigationRegion2D.xml
+++ b/doc/classes/NavigationRegion2D.xml
@@ -89,9 +89,8 @@
<member name="avoidance_layers" type="int" setter="set_avoidance_layers" getter="get_avoidance_layers" default="1">
A bitfield determining all avoidance layers for the avoidance constrain.
</member>
- <member name="constrain_avoidance" type="bool" setter="set_constrain_avoidance" getter="get_constrain_avoidance" default="false">
+ <member name="constrain_avoidance" type="bool" setter="set_constrain_avoidance" getter="get_constrain_avoidance" default="false" experimental="When enabled, agents are known to get stuck on the navigation polygon corners and edges, especially at a high frame rate. Not recommended for use in production at this stage.">
If [code]true[/code] constraints avoidance agent's with an avoidance mask bit that matches with a bit of the [member avoidance_layers] to the navigation polygon. Due to each navigation polygon outline creating an obstacle and each polygon edge creating an avoidance line constrain keep the navigation polygon shape as simple as possible for performance.
- [b]Experimental:[/b] This is an experimental feature and should not be used in production as agent's can get stuck on the navigation polygon corners and edges especially at high frame rate.
</member>
<member name="enabled" type="bool" setter="set_enabled" getter="is_enabled" default="true">
Determines if the [NavigationRegion2D] is enabled or disabled.
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index d8f8b73ca4..af01a849cb 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -2345,9 +2345,8 @@
If [code]true[/code], performs a previous depth pass before rendering 3D materials. This increases performance significantly in scenes with high overdraw, when complex materials and lighting are used. However, in scenes with few occluded surfaces, the depth prepass may reduce performance. If your game is viewed from a fixed angle that makes it easy to avoid overdraw (such as top-down or side-scrolling perspective), consider disabling the depth prepass to improve performance. This setting can be changed at run-time to optimize performance depending on the scene currently being viewed.
[b]Note:[/b] Depth prepass is only supported when using the Forward+ or Compatibility rendering method. When using the Mobile rendering method, there is no depth prepass performed.
</member>
- <member name="rendering/driver/threads/thread_model" type="int" setter="" getter="" default="1">
+ <member name="rendering/driver/threads/thread_model" type="int" setter="" getter="" default="1" experimental="This setting has several known bugs which can lead to crashing, especially when using particles or resizing the window. Not recommended for use in production at this stage.">
The thread model to use for rendering. Rendering on a thread may improve performance, but synchronizing to the main thread can cause a bit more jitter.
- [b]Note:[/b] The [b]Multi-Threaded[/b] option is experimental, and has several known bugs which can lead to crashing, especially when using particles or resizing the window. Not recommended for use in production at this stage.
</member>
<member name="rendering/environment/defaults/default_clear_color" type="Color" setter="" getter="" default="Color(0.3, 0.3, 0.3, 1)">
Default background clear color. Overridable per [Viewport] using its [Environment]. See [member Environment.background_mode] and [member Environment.background_color] in particular. To change this default color programmatically, use [method RenderingServer.set_default_clear_color].
diff --git a/doc/classes/SkeletonModification2DPhysicalBones.xml b/doc/classes/SkeletonModification2DPhysicalBones.xml
index 163fab892c..af9e982f6a 100644
--- a/doc/classes/SkeletonModification2DPhysicalBones.xml
+++ b/doc/classes/SkeletonModification2DPhysicalBones.xml
@@ -1,11 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="SkeletonModification2DPhysicalBones" inherits="SkeletonModification2D" experimental="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+<class name="SkeletonModification2DPhysicalBones" inherits="SkeletonModification2D" experimental="Physical bones may be changed in the future to perform the position update of [Bone2D] on their own, without needing this resource." xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
A modification that applies the transforms of [PhysicalBone2D] nodes to [Bone2D] nodes.
</brief_description>
<description>
This modification takes the transforms of [PhysicalBone2D] nodes and applies them to [Bone2D] nodes. This allows the [Bone2D] nodes to react to physics thanks to the linked [PhysicalBone2D] nodes.
- Experimental. Physical bones may be changed in the future to perform the position update of [Bone2D] on their own.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/XRHandModifier3D.xml b/doc/classes/XRHandModifier3D.xml
new file mode 100644
index 0000000000..7e08d3f687
--- /dev/null
+++ b/doc/classes/XRHandModifier3D.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="XRHandModifier3D" inherits="Node3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+ <brief_description>
+ A node for driving hand meshes from [XRHandTracker] data.
+ </brief_description>
+ <description>
+ This node uses hand tracking data from a [XRHandTracker] to animate the skeleton of a hand mesh.
+ </description>
+ <tutorials>
+ <link title="XR documentation index">$DOCS_URL/tutorials/xr/index.html</link>
+ </tutorials>
+ <members>
+ <member name="bone_update" type="int" setter="set_bone_update" getter="get_bone_update" enum="XRHandModifier3D.BoneUpdate" default="0">
+ Specifies the type of updates to perform on the bones.
+ </member>
+ <member name="hand_tracker" type="StringName" setter="set_hand_tracker" getter="get_hand_tracker" default="&amp;&quot;/user/left&quot;">
+ The name of the [XRHandTracker] registered with [XRServer] to obtain the hand tracking data from.
+ </member>
+ <member name="target" type="NodePath" setter="set_target" getter="get_target" default="NodePath(&quot;&quot;)">
+ A [NodePath] to a [Skeleton3D] to animate.
+ </member>
+ </members>
+ <constants>
+ <constant name="BONE_UPDATE_FULL" value="0" enum="BoneUpdate">
+ The skeleton's bones are fully updated (both position and rotation) to match the tracked bones.
+ </constant>
+ <constant name="BONE_UPDATE_ROTATION_ONLY" value="1" enum="BoneUpdate">
+ The skeleton's bones are only rotated to align with the tracked bones, preserving bone length.
+ </constant>
+ <constant name="BONE_UPDATE_MAX" value="2" enum="BoneUpdate">
+ Represents the size of the [enum BoneUpdate] enum.
+ </constant>
+ </constants>
+</class>
diff --git a/doc/classes/XRHandTracker.xml b/doc/classes/XRHandTracker.xml
new file mode 100644
index 0000000000..932fec083a
--- /dev/null
+++ b/doc/classes/XRHandTracker.xml
@@ -0,0 +1,223 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="XRHandTracker" inherits="RefCounted" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+ <brief_description>
+ A tracked hand in XR.
+ </brief_description>
+ <description>
+ A hand tracking system will create an instance of this object and add it to the [XRServer]. This tracking system will then obtain skeleton data, convert it to the Godot Humanoid hand skeleton and store this data on the [XRHandTracker] object.
+ Use [XRHandModifier3D] to animate a hand mesh using hand tracking data.
+ </description>
+ <tutorials>
+ <link title="XR documentation index">$DOCS_URL/tutorials/xr/index.html</link>
+ </tutorials>
+ <methods>
+ <method name="get_hand_joint_angular_velocity" qualifiers="const">
+ <return type="Vector3" />
+ <param index="0" name="joint" type="int" enum="XRHandTracker.HandJoint" />
+ <description>
+ Returns the angular velocity for the given hand joint.
+ </description>
+ </method>
+ <method name="get_hand_joint_flags" qualifiers="const">
+ <return type="int" enum="XRHandTracker.HandJointFlags" is_bitfield="true" />
+ <param index="0" name="joint" type="int" enum="XRHandTracker.HandJoint" />
+ <description>
+ Returns flags about the validity of the tracking data for the given hand joint (see [enum XRHandTracker.HandJointFlags]).
+ </description>
+ </method>
+ <method name="get_hand_joint_linear_velocity" qualifiers="const">
+ <return type="Vector3" />
+ <param index="0" name="joint" type="int" enum="XRHandTracker.HandJoint" />
+ <description>
+ Returns the linear velocity for the given hand joint.
+ </description>
+ </method>
+ <method name="get_hand_joint_radius" qualifiers="const">
+ <return type="float" />
+ <param index="0" name="joint" type="int" enum="XRHandTracker.HandJoint" />
+ <description>
+ Returns the radius of the given hand joint.
+ </description>
+ </method>
+ <method name="get_hand_joint_transform" qualifiers="const">
+ <return type="Transform3D" />
+ <param index="0" name="joint" type="int" enum="XRHandTracker.HandJoint" />
+ <description>
+ Returns the transform for the given hand joint.
+ </description>
+ </method>
+ <method name="set_hand_joint_angular_velocity">
+ <return type="void" />
+ <param index="0" name="joint" type="int" enum="XRHandTracker.HandJoint" />
+ <param index="1" name="angular_velocity" type="Vector3" />
+ <description>
+ Sets the angular velocity for the given hand joint.
+ </description>
+ </method>
+ <method name="set_hand_joint_flags">
+ <return type="void" />
+ <param index="0" name="joint" type="int" enum="XRHandTracker.HandJoint" />
+ <param index="1" name="flags" type="int" enum="XRHandTracker.HandJointFlags" is_bitfield="true" />
+ <description>
+ Sets flags about the validity of the tracking data for the given hand joint.
+ </description>
+ </method>
+ <method name="set_hand_joint_linear_velocity">
+ <return type="void" />
+ <param index="0" name="joint" type="int" enum="XRHandTracker.HandJoint" />
+ <param index="1" name="linear_velocity" type="Vector3" />
+ <description>
+ Sets the linear velocity for the given hand joint.
+ </description>
+ </method>
+ <method name="set_hand_joint_radius">
+ <return type="void" />
+ <param index="0" name="joint" type="int" enum="XRHandTracker.HandJoint" />
+ <param index="1" name="radius" type="float" />
+ <description>
+ Sets the radius of the given hand joint.
+ </description>
+ </method>
+ <method name="set_hand_joint_transform">
+ <return type="void" />
+ <param index="0" name="joint" type="int" enum="XRHandTracker.HandJoint" />
+ <param index="1" name="transform" type="Transform3D" />
+ <description>
+ Sets the transform for the given hand joint.
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="hand" type="int" setter="set_hand" getter="get_hand" enum="XRHandTracker.Hand" default="0">
+ The type of hand.
+ </member>
+ <member name="hand_tracking_source" type="int" setter="set_hand_tracking_source" getter="get_hand_tracking_source" enum="XRHandTracker.HandTrackingSource" default="0">
+ The source of the hand tracking data.
+ </member>
+ <member name="has_tracking_data" type="bool" setter="set_has_tracking_data" getter="get_has_tracking_data" default="false">
+ If [code]true[/code], the hand tracking data is valid.
+ </member>
+ </members>
+ <constants>
+ <constant name="HAND_LEFT" value="0" enum="Hand">
+ A left hand.
+ </constant>
+ <constant name="HAND_RIGHT" value="1" enum="Hand">
+ A right hand.
+ </constant>
+ <constant name="HAND_MAX" value="2" enum="Hand">
+ Represents the size of the [enum Hand] enum.
+ </constant>
+ <constant name="HAND_TRACKING_SOURCE_UNKNOWN" value="0" enum="HandTrackingSource">
+ The source of hand tracking data is unknown.
+ </constant>
+ <constant name="HAND_TRACKING_SOURCE_UNOBSTRUCTED" value="1" enum="HandTrackingSource">
+ The source of hand tracking data is unobstructed, meaning that an accurate method of hand tracking is used. These include optical hand tracking, data gloves, etc.
+ </constant>
+ <constant name="HAND_TRACKING_SOURCE_CONTROLLER" value="2" enum="HandTrackingSource">
+ The source of hand tracking data is a controller, meaning that joint positions are inferred from controller inputs.
+ </constant>
+ <constant name="HAND_TRACKING_SOURCE_MAX" value="3" enum="HandTrackingSource">
+ Represents the size of the [enum HandTrackingSource] enum.
+ </constant>
+ <constant name="HAND_JOINT_PALM" value="0" enum="HandJoint">
+ Palm joint.
+ </constant>
+ <constant name="HAND_JOINT_WRIST" value="1" enum="HandJoint">
+ Wrist joint.
+ </constant>
+ <constant name="HAND_JOINT_THUMB_METACARPAL" value="2" enum="HandJoint">
+ Thumb metacarpal joint.
+ </constant>
+ <constant name="HAND_JOINT_THUMB_PHALANX_PROXIMAL" value="3" enum="HandJoint">
+ Thumb phalanx proximal joint.
+ </constant>
+ <constant name="HAND_JOINT_THUMB_PHALANX_DISTAL" value="4" enum="HandJoint">
+ Thumb phalanx distal joint.
+ </constant>
+ <constant name="HAND_JOINT_THUMB_TIP" value="5" enum="HandJoint">
+ Thumb tip joint.
+ </constant>
+ <constant name="HAND_JOINT_INDEX_FINGER_METACARPAL" value="6" enum="HandJoint">
+ Index finger metacarpal joint.
+ </constant>
+ <constant name="HAND_JOINT_INDEX_FINGER_PHALANX_PROXIMAL" value="7" enum="HandJoint">
+ Index finger phalanx proximal joint.
+ </constant>
+ <constant name="HAND_JOINT_INDEX_FINGER_PHALANX_INTERMEDIATE" value="8" enum="HandJoint">
+ Index finger phalanx intermediate joint.
+ </constant>
+ <constant name="HAND_JOINT_INDEX_FINGER_PHALANX_DISTAL" value="9" enum="HandJoint">
+ Index finger phalanx distal joint.
+ </constant>
+ <constant name="HAND_JOINT_INDEX_FINGER_TIP" value="10" enum="HandJoint">
+ Index finger tip joint.
+ </constant>
+ <constant name="HAND_JOINT_MIDDLE_FINGER_METACARPAL" value="11" enum="HandJoint">
+ Middle finger metacarpal joint.
+ </constant>
+ <constant name="HAND_JOINT_MIDDLE_FINGER_PHALANX_PROXIMAL" value="12" enum="HandJoint">
+ Middle finger phalanx proximal joint.
+ </constant>
+ <constant name="HAND_JOINT_MIDDLE_FINGER_PHALANX_INTERMEDIATE" value="13" enum="HandJoint">
+ Middle finger phalanx intermediate joint.
+ </constant>
+ <constant name="HAND_JOINT_MIDDLE_FINGER_PHALANX_DISTAL" value="14" enum="HandJoint">
+ Middle finger phalanx distal joint.
+ </constant>
+ <constant name="HAND_JOINT_MIDDLE_FINGER_TIP" value="15" enum="HandJoint">
+ Middle finger tip joint.
+ </constant>
+ <constant name="HAND_JOINT_RING_FINGER_METACARPAL" value="16" enum="HandJoint">
+ Ring finger metacarpal joint.
+ </constant>
+ <constant name="HAND_JOINT_RING_FINGER_PHALANX_PROXIMAL" value="17" enum="HandJoint">
+ Ring finger phalanx proximal joint.
+ </constant>
+ <constant name="HAND_JOINT_RING_FINGER_PHALANX_INTERMEDIATE" value="18" enum="HandJoint">
+ Ring finger phalanx intermediate joint.
+ </constant>
+ <constant name="HAND_JOINT_RING_FINGER_PHALANX_DISTAL" value="19" enum="HandJoint">
+ Ring finger phalanx distal joint.
+ </constant>
+ <constant name="HAND_JOINT_RING_FINGER_TIP" value="20" enum="HandJoint">
+ Ring finger tip joint.
+ </constant>
+ <constant name="HAND_JOINT_PINKY_FINGER_METACARPAL" value="21" enum="HandJoint">
+ Pinky finger metacarpal joint.
+ </constant>
+ <constant name="HAND_JOINT_PINKY_FINGER_PHALANX_PROXIMAL" value="22" enum="HandJoint">
+ Pinky finger phalanx proximal joint.
+ </constant>
+ <constant name="HAND_JOINT_PINKY_FINGER_PHALANX_INTERMEDIATE" value="23" enum="HandJoint">
+ Pinky finger phalanx intermediate joint.
+ </constant>
+ <constant name="HAND_JOINT_PINKY_FINGER_PHALANX_DISTAL" value="24" enum="HandJoint">
+ Pinky finger phalanx distal joint.
+ </constant>
+ <constant name="HAND_JOINT_PINKY_FINGER_TIP" value="25" enum="HandJoint">
+ Pinky finger tip joint.
+ </constant>
+ <constant name="HAND_JOINT_MAX" value="26" enum="HandJoint">
+ Represents the size of the [enum HandJoint] enum.
+ </constant>
+ <constant name="HAND_JOINT_FLAG_ORIENTATION_VALID" value="1" enum="HandJointFlags" is_bitfield="true">
+ The hand joint's orientation data is valid.
+ </constant>
+ <constant name="HAND_JOINT_FLAG_ORIENTATION_TRACKED" value="2" enum="HandJointFlags" is_bitfield="true">
+ The hand joint's orientation is actively tracked. May not be set if tracking has been temporarily lost.
+ </constant>
+ <constant name="HAND_JOINT_FLAG_POSITION_VALID" value="4" enum="HandJointFlags" is_bitfield="true">
+ The hand joint's position data is valid.
+ </constant>
+ <constant name="HAND_JOINT_FLAG_POSITION_TRACKED" value="8" enum="HandJointFlags" is_bitfield="true">
+ The hand joint's position is actively tracked. May not be set if tracking has been temporarily lost.
+ </constant>
+ <constant name="HAND_JOINT_FLAG_LINEAR_VELOCITY_VALID" value="16" enum="HandJointFlags" is_bitfield="true">
+ The hand joint's linear velocity data is valid.
+ </constant>
+ <constant name="HAND_JOINT_FLAG_ANGULAR_VELOCITY_VALID" value="32" enum="HandJointFlags" is_bitfield="true">
+ The hand joint's angular velocity data is valid.
+ </constant>
+ </constants>
+</class>
diff --git a/doc/classes/XRServer.xml b/doc/classes/XRServer.xml
index f98c1d66a4..09e14f1b21 100644
--- a/doc/classes/XRServer.xml
+++ b/doc/classes/XRServer.xml
@@ -18,6 +18,14 @@
Registers a new [XRFaceTracker] that tracks the blend shapes of a face.
</description>
</method>
+ <method name="add_hand_tracker">
+ <return type="void" />
+ <param index="0" name="tracker_name" type="StringName" />
+ <param index="1" name="hand_tracker" type="XRHandTracker" />
+ <description>
+ Registers a new [XRHandTracker] that tracks the joints of a hand.
+ </description>
+ </method>
<method name="add_interface">
<return type="void" />
<param index="0" name="interface" type="XRInterface" />
@@ -71,6 +79,19 @@
Returns a dictionary of the registered face trackers. Each element of the dictionary is a tracker name mapping to the [XRFaceTracker] instance.
</description>
</method>
+ <method name="get_hand_tracker" qualifiers="const">
+ <return type="XRHandTracker" />
+ <param index="0" name="tracker_name" type="StringName" />
+ <description>
+ Returns the [XRHandTracker] with the given tracker name.
+ </description>
+ </method>
+ <method name="get_hand_trackers" qualifiers="const">
+ <return type="Dictionary" />
+ <description>
+ Returns a dictionary of the registered hand trackers. Each element of the dictionary is a tracker name mapping to the [XRHandTracker] instance.
+ </description>
+ </method>
<method name="get_hmd_transform">
<return type="Transform3D" />
<description>
@@ -123,6 +144,13 @@
Removes a registered [XRFaceTracker].
</description>
</method>
+ <method name="remove_hand_tracker">
+ <return type="void" />
+ <param index="0" name="tracker_name" type="StringName" />
+ <description>
+ Removes a registered [XRHandTracker].
+ </description>
+ </method>
<method name="remove_interface">
<return type="void" />
<param index="0" name="interface" type="XRInterface" />
@@ -171,6 +199,26 @@
Emitted when an existing face tracker is updated.
</description>
</signal>
+ <signal name="hand_tracker_added">
+ <param index="0" name="tracker_name" type="StringName" />
+ <param index="1" name="hand_tracker" type="XRHandTracker" />
+ <description>
+ Emitted when a new hand tracker is added.
+ </description>
+ </signal>
+ <signal name="hand_tracker_removed">
+ <param index="0" name="tracker_name" type="StringName" />
+ <description>
+ Emitted when a hand tracker is removed.
+ </description>
+ </signal>
+ <signal name="hand_tracker_updated">
+ <param index="0" name="tracker_name" type="StringName" />
+ <param index="1" name="hand_tracker" type="XRHandTracker" />
+ <description>
+ Emitted when an existing hand tracker is updated.
+ </description>
+ </signal>
<signal name="interface_added">
<param index="0" name="interface_name" type="StringName" />
<description>
diff --git a/drivers/d3d12/d3d12ma.cpp b/drivers/d3d12/d3d12ma.cpp
index ad7b4e570d..51171141de 100644
--- a/drivers/d3d12/d3d12ma.cpp
+++ b/drivers/d3d12/d3d12ma.cpp
@@ -42,6 +42,7 @@
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
#pragma GCC diagnostic ignored "-Wunused-function"
#pragma GCC diagnostic ignored "-Wnonnull-compare"
+#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif
#if defined(_MSC_VER)
diff --git a/drivers/d3d12/rendering_device_driver_d3d12.cpp b/drivers/d3d12/rendering_device_driver_d3d12.cpp
index 8c92737374..6efef0fb34 100644
--- a/drivers/d3d12/rendering_device_driver_d3d12.cpp
+++ b/drivers/d3d12/rendering_device_driver_d3d12.cpp
@@ -2885,7 +2885,7 @@ Vector<uint8_t> RenderingDeviceDriverD3D12::shader_compile_binary_from_spirv(Vec
uint32_t binding = (p_register % GODOT_NIR_DESCRIPTOR_SET_MULTIPLIER) / GODOT_NIR_BINDING_MULTIPLIER;
DEV_ASSERT(set < (uint32_t)shader_data_in.sets_bindings.size());
- bool found = false;
+ [[maybe_unused]] bool found = false;
for (int j = 0; j < shader_data_in.sets_bindings[set].size(); j++) {
if (shader_data_in.sets_bindings[set][j].binding != binding) {
continue;
@@ -2903,7 +2903,6 @@ Vector<uint8_t> RenderingDeviceDriverD3D12::shader_compile_binary_from_spirv(Vec
} else {
CRASH_NOW();
}
-
found = true;
break;
}
@@ -2913,8 +2912,7 @@ Vector<uint8_t> RenderingDeviceDriverD3D12::shader_compile_binary_from_spirv(Vec
godot_nir_callbacks.report_sc_bit_offset_fn = [](uint32_t p_sc_id, uint64_t p_bit_offset, void *p_data) {
ShaderData &shader_data_in = *(ShaderData *)p_data;
-
- bool found = false;
+ [[maybe_unused]] bool found = false;
for (int j = 0; j < shader_data_in.specialization_constants.size(); j++) {
if (shader_data_in.specialization_constants[j].constant_id != p_sc_id) {
continue;
@@ -2923,7 +2921,6 @@ Vector<uint8_t> RenderingDeviceDriverD3D12::shader_compile_binary_from_spirv(Vec
uint32_t offset_idx = SHADER_STAGES_BIT_OFFSET_INDICES[shader_data_in.stage];
DEV_ASSERT(shader_data_in.specialization_constants.write[j].stages_bit_offsets[offset_idx] == 0);
shader_data_in.specialization_constants.write[j].stages_bit_offsets[offset_idx] = p_bit_offset;
-
found = true;
break;
}
diff --git a/editor/debugger/editor_debugger_node.cpp b/editor/debugger/editor_debugger_node.cpp
index 14997c52bb..5fe35bde84 100644
--- a/editor/debugger/editor_debugger_node.cpp
+++ b/editor/debugger/editor_debugger_node.cpp
@@ -38,6 +38,7 @@
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
#include "editor/editor_undo_redo_manager.h"
+#include "editor/gui/editor_bottom_panel.h"
#include "editor/gui/editor_run_bar.h"
#include "editor/inspector_dock.h"
#include "editor/plugins/editor_debugger_plugin.h"
@@ -268,9 +269,9 @@ Error EditorDebuggerNode::start(const String &p_uri) {
stop(true);
current_uri = p_uri;
if (EDITOR_GET("run/output/always_open_output_on_play")) {
- EditorNode::get_singleton()->make_bottom_panel_item_visible(EditorNode::get_log());
+ EditorNode::get_bottom_panel()->make_item_visible(EditorNode::get_log());
} else {
- EditorNode::get_singleton()->make_bottom_panel_item_visible(this);
+ EditorNode::get_bottom_panel()->make_item_visible(this);
}
server = Ref<EditorDebuggerServer>(EditorDebuggerServer::create(p_uri.substr(0, p_uri.find("://") + 3)));
const Error err = server->start(p_uri);
@@ -502,7 +503,7 @@ void EditorDebuggerNode::_break_state_changed() {
const bool breaked = get_current_debugger()->is_breaked();
const bool can_debug = get_current_debugger()->is_debuggable();
if (breaked) { // Show debugger.
- EditorNode::get_singleton()->make_bottom_panel_item_visible(this);
+ EditorNode::get_bottom_panel()->make_item_visible(this);
}
// Update script menu.
diff --git a/editor/editor_audio_buses.cpp b/editor/editor_audio_buses.cpp
index 5127dd43d8..ee8b66cddf 100644
--- a/editor/editor_audio_buses.cpp
+++ b/editor/editor_audio_buses.cpp
@@ -39,6 +39,7 @@
#include "editor/editor_string_names.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/filesystem_dock.h"
+#include "editor/gui/editor_bottom_panel.h"
#include "editor/gui/editor_file_dialog.h"
#include "editor/themes/editor_scale.h"
#include "editor/themes/editor_theme_manager.h"
@@ -1040,7 +1041,7 @@ void EditorAudioBuses::_rebuild_buses() {
EditorAudioBuses *EditorAudioBuses::register_editor() {
EditorAudioBuses *audio_buses = memnew(EditorAudioBuses);
- EditorNode::get_singleton()->add_bottom_panel_item(TTR("Audio"), audio_buses);
+ EditorNode::get_bottom_panel()->add_item(TTR("Audio"), audio_buses);
return audio_buses;
}
@@ -1357,7 +1358,7 @@ EditorAudioBuses::EditorAudioBuses() {
}
void EditorAudioBuses::open_layout(const String &p_path) {
- EditorNode::get_singleton()->make_bottom_panel_item_visible(this);
+ EditorNode::get_bottom_panel()->make_item_visible(this);
Ref<AudioBusLayout> state = ResourceLoader::load(p_path, "", ResourceFormatLoader::CACHE_MODE_IGNORE);
if (state.is_null()) {
diff --git a/editor/editor_command_palette.cpp b/editor/editor_command_palette.cpp
index d740641f57..b963330fdc 100644
--- a/editor/editor_command_palette.cpp
+++ b/editor/editor_command_palette.cpp
@@ -36,6 +36,7 @@
#include "editor/gui/editor_toaster.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/control.h"
+#include "scene/gui/margin_container.h"
#include "scene/gui/tree.h"
EditorCommandPalette *EditorCommandPalette::singleton = nullptr;
diff --git a/editor/editor_dock_manager.cpp b/editor/editor_dock_manager.cpp
index dc4c809860..a6b16a245d 100644
--- a/editor/editor_dock_manager.cpp
+++ b/editor/editor_dock_manager.cpp
@@ -42,6 +42,7 @@
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
#include "editor/filesystem_dock.h"
+#include "editor/gui/editor_bottom_panel.h"
#include "editor/themes/editor_scale.h"
#include "editor/window_wrapper.h"
@@ -196,7 +197,7 @@ void EditorDockManager::_dock_select_input(const Ref<InputEvent> &p_input) {
if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) {
if (dock_bottom_selected_idx != -1) {
- EditorNode::get_singleton()->remove_bottom_panel_item(bottom_docks[dock_bottom_selected_idx]);
+ EditorNode::get_bottom_panel()->remove_item(bottom_docks[dock_bottom_selected_idx]);
bottom_docks[dock_bottom_selected_idx]->call("_set_dock_horizontal", false);
@@ -391,13 +392,13 @@ void EditorDockManager::_dock_move_selected_to_bottom() {
dock->call("_set_dock_horizontal", true);
bottom_docks.push_back(dock);
- EditorNode::get_singleton()->add_bottom_panel_item(dock->get_name(), dock, true);
+ EditorNode::get_bottom_panel()->add_item(dock->get_name(), dock, true);
dock_select_popup->hide();
update_dock_slots_visibility(true);
_edit_current();
emit_signal(SNAME("layout_changed"));
- EditorNode::get_singleton()->make_bottom_panel_item_visible(dock);
+ EditorNode::get_bottom_panel()->make_item_visible(dock);
}
void EditorDockManager::_dock_make_float(Control *p_dock, int p_slot_index, bool p_show_window) {
@@ -601,7 +602,7 @@ void EditorDockManager::load_docks_from_config(Ref<ConfigFile> p_layout, const S
dock_slot[atidx]->set_block_signals(false);
} else if (bottom_idx != -1) {
bottom_docks.erase(node);
- EditorNode::get_singleton()->remove_bottom_panel_item(node);
+ EditorNode::get_bottom_panel()->remove_item(node);
dock_slot[i]->add_child(node);
node->call("_set_dock_horizontal", false);
}
@@ -662,7 +663,7 @@ void EditorDockManager::load_docks_from_config(Ref<ConfigFile> p_layout, const S
node->call("_set_dock_horizontal", true);
bottom_docks.push_back(node);
- EditorNode::get_singleton()->add_bottom_panel_item(node->get_name(), node, true);
+ EditorNode::get_bottom_panel()->add_item(node->get_name(), node, true);
}
}
diff --git a/editor/editor_layouts_dialog.cpp b/editor/editor_layouts_dialog.cpp
index bd9a4ea1bf..db6fa2c035 100644
--- a/editor/editor_layouts_dialog.cpp
+++ b/editor/editor_layouts_dialog.cpp
@@ -37,6 +37,7 @@
#include "editor/themes/editor_scale.h"
#include "scene/gui/item_list.h"
#include "scene/gui/line_edit.h"
+#include "scene/gui/margin_container.h"
void EditorLayoutsDialog::_line_gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventKey> k = p_event;
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index fbde412834..2cee2c2198 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -106,6 +106,7 @@
#include "editor/export/project_export.h"
#include "editor/fbx_importer_manager.h"
#include "editor/filesystem_dock.h"
+#include "editor/gui/editor_bottom_panel.h"
#include "editor/gui/editor_file_dialog.h"
#include "editor/gui/editor_run_bar.h"
#include "editor/gui/editor_scene_tabs.h"
@@ -166,9 +167,6 @@
EditorNode *EditorNode::singleton = nullptr;
-// The metadata key used to store and retrieve the version text to copy to the clipboard.
-static const String META_TEXT_TO_COPY = "text_to_copy";
-
static const String EDITOR_NODE_CONFIG_SECTION = "EditorNode";
static const String REMOVE_ANDROID_BUILD_TEMPLATE_MESSAGE = "The Android build template is already installed in this project and it won't be overwritten.\nRemove the \"%s\" directory manually before attempting this operation again.";
@@ -518,7 +516,6 @@ void EditorNode::_update_theme(bool p_skip_creation) {
main_menu->add_theme_style_override("pressed", theme->get_stylebox(SNAME("MenuTransparent"), EditorStringName(EditorStyles)));
distraction_free->set_icon(theme->get_icon(SNAME("DistractionFree"), EditorStringName(EditorIcons)));
distraction_free->add_theme_style_override("pressed", theme->get_stylebox(SNAME("MenuTransparent"), EditorStringName(EditorStyles)));
- bottom_panel_raise->set_icon(theme->get_icon(SNAME("ExpandBottomDock"), EditorStringName(EditorIcons)));
help_menu->set_item_icon(help_menu->get_item_index(HELP_SEARCH), theme->get_icon(SNAME("HelpSearch"), EditorStringName(EditorIcons)));
help_menu->set_item_icon(help_menu->get_item_index(HELP_COPY_SYSTEM_INFO), theme->get_icon(SNAME("ActionCopy"), EditorStringName(EditorIcons)));
@@ -529,11 +526,6 @@ void EditorNode::_update_theme(bool p_skip_creation) {
bottom_panel->add_theme_style_override("panel", theme->get_stylebox(SNAME("BottomPanelDebuggerOverride"), EditorStringName(EditorStyles)));
}
- for (int i = 0; i < bottom_panel_items.size(); i++) {
- bottom_panel_items.write[i].button->add_theme_style_override("pressed", theme->get_stylebox(SNAME("MenuTransparent"), EditorStringName(EditorStyles)));
- bottom_panel_items.write[i].button->add_theme_style_override("hover_pressed", theme->get_stylebox(SNAME("MenuHover"), EditorStringName(EditorStyles)));
- }
-
for (int i = 0; i < main_editor_buttons.size(); i++) {
Button *tb = main_editor_buttons[i];
EditorPlugin *p_editor = editor_table[i];
@@ -623,8 +615,6 @@ void EditorNode::_notification(int p_what) {
ResourceImporterTexture::get_singleton()->update_imports();
- bottom_panel_updating = false;
-
if (requested_first_scan) {
requested_first_scan = false;
@@ -1174,10 +1164,6 @@ void EditorNode::_titlebar_resized() {
}
}
-void EditorNode::_version_button_pressed() {
- DisplayServer::get_singleton()->clipboard_set(version_btn->get_meta(META_TEXT_TO_COPY));
-}
-
void EditorNode::_update_undo_redo_allowed() {
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
file_menu->set_item_disabled(file_menu->get_item_index(EDIT_UNDO), !undo_redo->has_undo());
@@ -4335,7 +4321,7 @@ void EditorNode::_project_run_started() {
}
if (bool(EDITOR_GET("run/output/always_open_output_on_play"))) {
- make_bottom_panel_item_visible(log);
+ bottom_panel->make_item_visible(log);
}
}
@@ -4344,12 +4330,7 @@ void EditorNode::_project_run_stopped() {
return;
}
- for (int i = 0; i < bottom_panel_items.size(); i++) {
- if (bottom_panel_items[i].control == log) {
- _bottom_panel_switch(false, i);
- break;
- }
- }
+ bottom_panel->make_item_visible(log, false);
}
void EditorNode::notify_all_debug_sessions_exited() {
@@ -4931,18 +4912,7 @@ void EditorNode::_save_central_editor_layout_to_config(Ref<ConfigFile> p_config_
int center_split_offset = center_split->get_split_offset();
p_config_file->set_value(EDITOR_NODE_CONFIG_SECTION, "center_split_offset", center_split_offset);
- int selected_bottom_panel_item_idx = -1;
- for (int i = 0; i < bottom_panel_items.size(); i++) {
- if (bottom_panel_items[i].button->is_pressed()) {
- selected_bottom_panel_item_idx = i;
- break;
- }
- }
- if (selected_bottom_panel_item_idx != -1) {
- p_config_file->set_value(EDITOR_NODE_CONFIG_SECTION, "selected_bottom_panel_item", selected_bottom_panel_item_idx);
- } else {
- p_config_file->set_value(EDITOR_NODE_CONFIG_SECTION, "selected_bottom_panel_item", Variant());
- }
+ bottom_panel->save_layout_to_config(p_config_file, EDITOR_NODE_CONFIG_SECTION);
// Debugger tab.
@@ -4968,27 +4938,11 @@ void EditorNode::_save_central_editor_layout_to_config(Ref<ConfigFile> p_config_
void EditorNode::_load_central_editor_layout_from_config(Ref<ConfigFile> p_config_file) {
// Bottom panel.
- bool has_active_tab = false;
- if (p_config_file->has_section_key(EDITOR_NODE_CONFIG_SECTION, "selected_bottom_panel_item")) {
- int selected_bottom_panel_item_idx = p_config_file->get_value(EDITOR_NODE_CONFIG_SECTION, "selected_bottom_panel_item");
- if (selected_bottom_panel_item_idx >= 0 && selected_bottom_panel_item_idx < bottom_panel_items.size()) {
- // Make sure we don't try to open contextual editors which are not enabled in the current context.
- if (bottom_panel_items[selected_bottom_panel_item_idx].button->is_visible()) {
- _bottom_panel_switch(true, selected_bottom_panel_item_idx);
- has_active_tab = true;
- }
- }
- }
+ bottom_panel->load_layout_from_config(p_config_file, EDITOR_NODE_CONFIG_SECTION);
if (p_config_file->has_section_key(EDITOR_NODE_CONFIG_SECTION, "center_split_offset")) {
int center_split_offset = p_config_file->get_value(EDITOR_NODE_CONFIG_SECTION, "center_split_offset");
center_split->set_split_offset(center_split_offset);
-
- // If there is no active tab we need to collapse the panel.
- if (!has_active_tab) {
- bottom_panel_items[0].control->show(); // _bottom_panel_switch() can collapse only visible tabs.
- _bottom_panel_switch(false, 0);
- }
}
// Debugger tab.
@@ -5278,125 +5232,6 @@ void EditorNode::_scene_tab_closed(int p_tab) {
scene_tabs->update_scene_tabs();
}
-Button *EditorNode::add_bottom_panel_item(String p_text, Control *p_item, bool p_at_front) {
- Button *tb = memnew(Button);
- tb->set_theme_type_variation("FlatMenuButton");
- tb->connect("toggled", callable_mp(this, &EditorNode::_bottom_panel_switch_by_control).bind(p_item));
- tb->set_drag_forwarding(Callable(), callable_mp(this, &EditorNode::_bottom_panel_drag_hover).bind(tb, p_item), Callable());
- tb->set_text(p_text);
- tb->set_toggle_mode(true);
- tb->set_focus_mode(Control::FOCUS_NONE);
- bottom_panel_vb->add_child(p_item);
- bottom_panel_hb->move_to_front();
- bottom_panel_hb_editors->add_child(tb);
- if (p_at_front) {
- bottom_panel_hb_editors->move_child(tb, 0);
- }
- p_item->set_v_size_flags(Control::SIZE_EXPAND_FILL);
- p_item->hide();
- BottomPanelItem bpi;
- bpi.button = tb;
- bpi.control = p_item;
- bpi.name = p_text;
- bottom_panel_items.push_back(bpi);
-
- return tb;
-}
-
-void EditorNode::hide_bottom_panel() {
- for (int i = 0; i < bottom_panel_items.size(); i++) {
- if (bottom_panel_items[i].control->is_visible()) {
- _bottom_panel_switch(false, i);
- break;
- }
- }
-}
-
-void EditorNode::make_bottom_panel_item_visible(Control *p_item) {
- for (int i = 0; i < bottom_panel_items.size(); i++) {
- if (bottom_panel_items[i].control == p_item) {
- _bottom_panel_switch(true, i);
- break;
- }
- }
-}
-
-void EditorNode::raise_bottom_panel_item(Control *p_item) {
- for (int i = 0; i < bottom_panel_items.size(); i++) {
- if (bottom_panel_items[i].control == p_item) {
- bottom_panel_items[i].button->move_to_front();
- SWAP(bottom_panel_items.write[i], bottom_panel_items.write[bottom_panel_items.size() - 1]);
- break;
- }
- }
-}
-
-void EditorNode::remove_bottom_panel_item(Control *p_item) {
- for (int i = 0; i < bottom_panel_items.size(); i++) {
- if (bottom_panel_items[i].control == p_item) {
- if (p_item->is_visible_in_tree()) {
- _bottom_panel_switch(false, i);
- }
- bottom_panel_vb->remove_child(bottom_panel_items[i].control);
- bottom_panel_hb_editors->remove_child(bottom_panel_items[i].button);
- memdelete(bottom_panel_items[i].button);
- bottom_panel_items.remove_at(i);
- break;
- }
- }
-}
-
-void EditorNode::_bottom_panel_switch_by_control(bool p_enable, Control *p_control) {
- for (int i = 0; i < bottom_panel_items.size(); i++) {
- if (bottom_panel_items[i].control == p_control) {
- _bottom_panel_switch(p_enable, i);
- return;
- }
- }
-}
-
-void EditorNode::_bottom_panel_switch(bool p_enable, int p_idx) {
- if (bottom_panel_updating) {
- return;
- }
- ERR_FAIL_INDEX(p_idx, bottom_panel_items.size());
-
- if (bottom_panel_items[p_idx].control->is_visible() == p_enable) {
- return;
- }
-
- if (p_enable) {
- bottom_panel_updating = true;
-
- for (int i = 0; i < bottom_panel_items.size(); i++) {
- bottom_panel_items[i].button->set_pressed(i == p_idx);
- bottom_panel_items[i].control->set_visible(i == p_idx);
- }
- if (EditorDebuggerNode::get_singleton() == bottom_panel_items[p_idx].control) {
- // This is the debug panel which uses tabs, so the top section should be smaller.
- bottom_panel->add_theme_style_override("panel", theme->get_stylebox(SNAME("BottomPanelDebuggerOverride"), EditorStringName(EditorStyles)));
- } else {
- bottom_panel->add_theme_style_override("panel", theme->get_stylebox(SNAME("BottomPanel"), EditorStringName(EditorStyles)));
- }
- center_split->set_dragger_visibility(SplitContainer::DRAGGER_VISIBLE);
- center_split->set_collapsed(false);
- if (bottom_panel_raise->is_pressed()) {
- top_split->hide();
- }
- bottom_panel_raise->show();
- } else {
- bottom_panel->add_theme_style_override("panel", theme->get_stylebox(SNAME("BottomPanel"), EditorStringName(EditorStyles)));
- bottom_panel_items[p_idx].button->set_pressed(false);
- bottom_panel_items[p_idx].control->set_visible(false);
- center_split->set_dragger_visibility(SplitContainer::DRAGGER_HIDDEN);
- center_split->set_collapsed(true);
- bottom_panel_raise->hide();
- if (bottom_panel_raise->is_pressed()) {
- top_split->show();
- }
- }
-}
-
void EditorNode::_toggle_distraction_free_mode() {
if (EDITOR_GET("interface/editor/separate_distraction_mode")) {
int screen = -1;
@@ -6111,17 +5946,6 @@ Vector<Ref<EditorResourceConversionPlugin>> EditorNode::find_resource_conversion
return ret;
}
-void EditorNode::_bottom_panel_raise_toggled(bool p_pressed) {
- top_split->set_visible(!p_pressed);
-}
-
-bool EditorNode::_bottom_panel_drag_hover(const Vector2 &, const Variant &, Button *p_button, Control *p_control) {
- if (!p_button->is_pressed()) {
- _bottom_panel_switch_by_control(true, p_control);
- }
- return false;
-}
-
void EditorNode::_update_renderer_color() {
String rendering_method = renderer->get_selected_metadata();
@@ -7173,62 +6997,12 @@ EditorNode::EditorNode() {
// Bottom panels.
- bottom_panel = memnew(PanelContainer);
- bottom_panel->add_theme_style_override("panel", theme->get_stylebox(SNAME("BottomPanel"), EditorStringName(EditorStyles)));
+ bottom_panel = memnew(EditorBottomPanel);
center_split->add_child(bottom_panel);
center_split->set_dragger_visibility(SplitContainer::DRAGGER_HIDDEN);
- bottom_panel_vb = memnew(VBoxContainer);
- bottom_panel->add_child(bottom_panel_vb);
-
- bottom_panel_hb = memnew(HBoxContainer);
- bottom_panel_hb->set_custom_minimum_size(Size2(0, 24 * EDSCALE)); // Adjust for the height of the "Expand Bottom Dock" icon.
- bottom_panel_vb->add_child(bottom_panel_hb);
-
- bottom_panel_hb_editors = memnew(HBoxContainer);
- bottom_panel_hb_editors->set_h_size_flags(Control::SIZE_EXPAND_FILL);
- bottom_panel_hb->add_child(bottom_panel_hb_editors);
-
- editor_toaster = memnew(EditorToaster);
- bottom_panel_hb->add_child(editor_toaster);
-
- VBoxContainer *version_info_vbc = memnew(VBoxContainer);
- bottom_panel_hb->add_child(version_info_vbc);
-
- // Add a dummy control node for vertical spacing.
- Control *v_spacer = memnew(Control);
- version_info_vbc->add_child(v_spacer);
-
- version_btn = memnew(LinkButton);
- version_btn->set_text(VERSION_FULL_CONFIG);
- String hash = String(VERSION_HASH);
- if (hash.length() != 0) {
- hash = " " + vformat("[%s]", hash.left(9));
- }
- // Set the text to copy in metadata as it slightly differs from the button's text.
- version_btn->set_meta(META_TEXT_TO_COPY, "v" VERSION_FULL_BUILD + hash);
- // Fade out the version label to be less prominent, but still readable.
- version_btn->set_self_modulate(Color(1, 1, 1, 0.65));
- version_btn->set_underline_mode(LinkButton::UNDERLINE_MODE_ON_HOVER);
- version_btn->set_tooltip_text(TTR("Click to copy."));
- version_btn->connect("pressed", callable_mp(this, &EditorNode::_version_button_pressed));
- version_info_vbc->add_child(version_btn);
-
- // Add a dummy control node for horizontal spacing.
- Control *h_spacer = memnew(Control);
- bottom_panel_hb->add_child(h_spacer);
-
- bottom_panel_raise = memnew(Button);
- bottom_panel_hb->add_child(bottom_panel_raise);
- bottom_panel_raise->hide();
- bottom_panel_raise->set_flat(false);
- bottom_panel_raise->set_theme_type_variation("FlatMenuButton");
- bottom_panel_raise->set_toggle_mode(true);
- bottom_panel_raise->set_shortcut(ED_SHORTCUT_AND_COMMAND("editor/bottom_panel_expand", TTR("Expand Bottom Panel"), KeyModifierMask::SHIFT | Key::F12));
- bottom_panel_raise->connect("toggled", callable_mp(this, &EditorNode::_bottom_panel_raise_toggled));
-
log = memnew(EditorLog);
- Button *output_button = add_bottom_panel_item(TTR("Output"), log);
+ Button *output_button = bottom_panel->add_item(TTR("Output"), log);
log->set_tool_button(output_button);
center_split->connect("resized", callable_mp(this, &EditorNode::_vp_resized));
@@ -7389,7 +7163,7 @@ EditorNode::EditorNode() {
}
// More visually meaningful to have this later.
- raise_bottom_panel_item(AnimationPlayerEditor::get_singleton());
+ bottom_panel->move_item_to_end(AnimationPlayerEditor::get_singleton());
add_editor_plugin(VersionControlEditorPlugin::get_singleton());
diff --git a/editor/editor_node.h b/editor/editor_node.h
index 2aa743343e..d44f7b3bb4 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -77,6 +77,7 @@ class DockSplitContainer;
class DynamicFontImportSettingsDialog;
class EditorAbout;
class EditorBuildProfileManager;
+class EditorBottomPanel;
class EditorCommandPalette;
class EditorDockManager;
class EditorExport;
@@ -244,12 +245,6 @@ private:
MAX_BUILD_CALLBACKS = 128
};
- struct BottomPanelItem {
- String name;
- Control *control = nullptr;
- Button *button = nullptr;
- };
-
struct ExportDefer {
String preset;
String path;
@@ -324,7 +319,6 @@ private:
DisplayServer::WindowMode prev_mode = DisplayServer::WINDOW_MODE_MAXIMIZED;
int old_split_ofs = 0;
VSplitContainer *top_split = nullptr;
- HBoxContainer *bottom_hb = nullptr;
Control *vp_base = nullptr;
Label *project_title = nullptr;
@@ -428,15 +422,7 @@ private:
Timer *editor_layout_save_delay_timer = nullptr;
Button *distraction_free = nullptr;
- Vector<BottomPanelItem> bottom_panel_items;
- PanelContainer *bottom_panel = nullptr;
- HBoxContainer *bottom_panel_hb = nullptr;
- HBoxContainer *bottom_panel_hb_editors = nullptr;
- VBoxContainer *bottom_panel_vb = nullptr;
- EditorToaster *editor_toaster = nullptr;
- LinkButton *version_btn = nullptr;
- Button *bottom_panel_raise = nullptr;
- bool bottom_panel_updating = false;
+ EditorBottomPanel *bottom_panel = nullptr;
Tree *disk_changed_list = nullptr;
ConfirmationDialog *disk_changed = nullptr;
@@ -571,7 +557,6 @@ private:
void _show_messages();
void _vp_resized();
void _titlebar_resized();
- void _version_button_pressed();
void _update_undo_redo_allowed();
@@ -672,11 +657,6 @@ private:
void _immediate_dialog_confirmed();
void _select_default_main_screen_plugin();
- void _bottom_panel_switch_by_control(bool p_enable, Control *p_control);
- void _bottom_panel_switch(bool p_enable, int p_idx);
- void _bottom_panel_raise_toggled(bool);
- bool _bottom_panel_drag_hover(const Vector2 &, const Variant &, Button *p_button, Control *p_control);
-
void _begin_first_scan();
void _notify_scene_updated(Node *p_node);
@@ -707,6 +687,7 @@ public:
static EditorTitleBar *get_title_bar() { return singleton->title_bar; }
static VSplitContainer *get_top_split() { return singleton->top_split; }
+ static EditorBottomPanel *get_bottom_panel() { return singleton->bottom_panel; }
static String adjust_scene_name_casing(const String &root_name);
@@ -887,12 +868,6 @@ public:
bool is_exiting() const { return exiting; }
- Button *add_bottom_panel_item(String p_text, Control *p_item, bool p_at_front = false);
- void make_bottom_panel_item_visible(Control *p_item);
- void raise_bottom_panel_item(Control *p_item);
- void hide_bottom_panel();
- void remove_bottom_panel_item(Control *p_item);
-
Variant drag_resource(const Ref<Resource> &p_res, Control *p_from);
Variant drag_files_and_dirs(const Vector<String> &p_paths, Control *p_from);
diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp
index 866d7ae233..7b5b084f09 100644
--- a/editor/editor_plugin.cpp
+++ b/editor/editor_plugin.cpp
@@ -39,6 +39,7 @@
#include "editor/editor_translation_parser.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/export/editor_export.h"
+#include "editor/gui/editor_bottom_panel.h"
#include "editor/gui/editor_title_bar.h"
#include "editor/import/3d/resource_importer_scene.h"
#include "editor/import/editor_import_plugin.h"
@@ -80,7 +81,7 @@ void EditorPlugin::remove_autoload_singleton(const String &p_name) {
Button *EditorPlugin::add_control_to_bottom_panel(Control *p_control, const String &p_title) {
ERR_FAIL_NULL_V(p_control, nullptr);
- return EditorNode::get_singleton()->add_bottom_panel_item(p_title, p_control);
+ return EditorNode::get_bottom_panel()->add_item(p_title, p_control);
}
void EditorPlugin::add_control_to_dock(DockSlot p_slot, Control *p_control) {
@@ -95,7 +96,7 @@ void EditorPlugin::remove_control_from_docks(Control *p_control) {
void EditorPlugin::remove_control_from_bottom_panel(Control *p_control) {
ERR_FAIL_NULL(p_control);
- EditorNode::get_singleton()->remove_bottom_panel_item(p_control);
+ EditorNode::get_bottom_panel()->remove_item(p_control);
}
void EditorPlugin::add_control_to_container(CustomControlContainer p_location, Control *p_control) {
@@ -505,11 +506,11 @@ void EditorPlugin::queue_save_layout() {
}
void EditorPlugin::make_bottom_panel_item_visible(Control *p_item) {
- EditorNode::get_singleton()->make_bottom_panel_item_visible(p_item);
+ EditorNode::get_bottom_panel()->make_item_visible(p_item);
}
void EditorPlugin::hide_bottom_panel() {
- EditorNode::get_singleton()->hide_bottom_panel();
+ EditorNode::get_bottom_panel()->hide_bottom_panel();
}
EditorInterface *EditorPlugin::get_editor_interface() {
diff --git a/editor/export/project_export.cpp b/editor/export/project_export.cpp
index 7088d4e2ab..223900e8b3 100644
--- a/editor/export/project_export.cpp
+++ b/editor/export/project_export.cpp
@@ -45,6 +45,7 @@
#include "scene/gui/check_button.h"
#include "scene/gui/item_list.h"
#include "scene/gui/link_button.h"
+#include "scene/gui/margin_container.h"
#include "scene/gui/menu_button.h"
#include "scene/gui/option_button.h"
#include "scene/gui/popup_menu.h"
diff --git a/editor/gui/editor_bottom_panel.cpp b/editor/gui/editor_bottom_panel.cpp
new file mode 100644
index 0000000000..1cf9daa545
--- /dev/null
+++ b/editor/gui/editor_bottom_panel.cpp
@@ -0,0 +1,272 @@
+/**************************************************************************/
+/* editor_bottom_panel.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "editor_bottom_panel.h"
+
+#include "core/version.h"
+#include "editor/debugger/editor_debugger_node.h"
+#include "editor/editor_about.h"
+#include "editor/editor_command_palette.h"
+#include "editor/editor_node.h"
+#include "editor/editor_string_names.h"
+#include "editor/gui/editor_toaster.h"
+#include "editor/themes/editor_scale.h"
+#include "scene/gui/box_container.h"
+#include "scene/gui/button.h"
+#include "scene/gui/link_button.h"
+
+// The metadata key used to store and retrieve the version text to copy to the clipboard.
+static const String META_TEXT_TO_COPY = "text_to_copy";
+
+void EditorBottomPanel::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_THEME_CHANGED: {
+ expand_button->set_icon(get_editor_theme_icon(SNAME("ExpandBottomDock")));
+ for (int i = 0; i < items.size(); i++) {
+ items.write[i].button->add_theme_style_override("pressed", get_theme_stylebox(SNAME("MenuTransparent"), EditorStringName(EditorStyles)));
+ items.write[i].button->add_theme_style_override("hover_pressed", get_theme_stylebox(SNAME("MenuHover"), EditorStringName(EditorStyles)));
+ }
+ } break;
+ }
+}
+
+void EditorBottomPanel::_switch_by_control(bool p_visible, Control *p_control) {
+ for (int i = 0; i < items.size(); i++) {
+ if (items[i].control == p_control) {
+ _switch_to_item(p_visible, i);
+ return;
+ }
+ }
+}
+
+void EditorBottomPanel::_switch_to_item(bool p_visible, int p_idx) {
+ ERR_FAIL_INDEX(p_idx, items.size());
+
+ if (items[p_idx].control->is_visible() == p_visible) {
+ return;
+ }
+
+ SplitContainer *center_split = Object::cast_to<SplitContainer>(get_parent());
+ ERR_FAIL_NULL(center_split);
+
+ if (p_visible) {
+ for (int i = 0; i < items.size(); i++) {
+ items[i].button->set_pressed_no_signal(i == p_idx);
+ items[i].control->set_visible(i == p_idx);
+ }
+ if (EditorDebuggerNode::get_singleton() == items[p_idx].control) {
+ // This is the debug panel which uses tabs, so the top section should be smaller.
+ add_theme_style_override("panel", get_theme_stylebox(SNAME("BottomPanelDebuggerOverride"), EditorStringName(EditorStyles)));
+ } else {
+ add_theme_style_override("panel", get_theme_stylebox(SNAME("BottomPanel"), EditorStringName(EditorStyles)));
+ }
+ center_split->set_dragger_visibility(SplitContainer::DRAGGER_VISIBLE);
+ center_split->set_collapsed(false);
+ if (expand_button->is_pressed()) {
+ EditorNode::get_top_split()->hide();
+ }
+ expand_button->show();
+ } else {
+ add_theme_style_override("panel", get_theme_stylebox(SNAME("BottomPanel"), EditorStringName(EditorStyles)));
+ items[p_idx].button->set_pressed_no_signal(false);
+ items[p_idx].control->set_visible(false);
+ center_split->set_dragger_visibility(SplitContainer::DRAGGER_HIDDEN);
+ center_split->set_collapsed(true);
+ expand_button->hide();
+ if (expand_button->is_pressed()) {
+ EditorNode::get_top_split()->show();
+ }
+ }
+}
+
+void EditorBottomPanel::_expand_button_toggled(bool p_pressed) {
+ EditorNode::get_top_split()->set_visible(!p_pressed);
+}
+
+void EditorBottomPanel::_version_button_pressed() {
+ DisplayServer::get_singleton()->clipboard_set(version_btn->get_meta(META_TEXT_TO_COPY));
+}
+
+bool EditorBottomPanel::_button_drag_hover(const Vector2 &, const Variant &, Button *p_button, Control *p_control) {
+ if (!p_button->is_pressed()) {
+ _switch_by_control(true, p_control);
+ }
+ return false;
+}
+
+void EditorBottomPanel::save_layout_to_config(Ref<ConfigFile> p_config_file, const String &p_section) const {
+ int selected_item_idx = -1;
+ for (int i = 0; i < items.size(); i++) {
+ if (items[i].button->is_pressed()) {
+ selected_item_idx = i;
+ break;
+ }
+ }
+ if (selected_item_idx != -1) {
+ p_config_file->set_value(p_section, "selected_bottom_panel_item", selected_item_idx);
+ } else {
+ p_config_file->set_value(p_section, "selected_bottom_panel_item", Variant());
+ }
+}
+
+void EditorBottomPanel::load_layout_from_config(Ref<ConfigFile> p_config_file, const String &p_section) {
+ bool has_active_tab = false;
+ if (p_config_file->has_section_key(p_section, "selected_bottom_panel_item")) {
+ int selected_item_idx = p_config_file->get_value(p_section, "selected_bottom_panel_item");
+ if (selected_item_idx >= 0 && selected_item_idx < items.size()) {
+ // Make sure we don't try to open contextual editors which are not enabled in the current context.
+ if (items[selected_item_idx].button->is_visible()) {
+ _switch_to_item(true, selected_item_idx);
+ has_active_tab = true;
+ }
+ }
+ }
+ // If there is no active tab we need to collapse the panel.
+ if (!has_active_tab) {
+ items[0].control->show(); // _switch_to_item() can collapse only visible tabs.
+ _switch_to_item(false, 0);
+ }
+}
+
+Button *EditorBottomPanel::add_item(String p_text, Control *p_item, bool p_at_front) {
+ Button *tb = memnew(Button);
+ tb->set_theme_type_variation("FlatMenuButton");
+ tb->connect("toggled", callable_mp(this, &EditorBottomPanel::_switch_by_control).bind(p_item));
+ tb->set_drag_forwarding(Callable(), callable_mp(this, &EditorBottomPanel::_button_drag_hover).bind(tb, p_item), Callable());
+ tb->set_text(p_text);
+ tb->set_toggle_mode(true);
+ tb->set_focus_mode(Control::FOCUS_NONE);
+ item_vbox->add_child(p_item);
+
+ bottom_hbox->move_to_front();
+ button_hbox->add_child(tb);
+ if (p_at_front) {
+ button_hbox->move_child(tb, 0);
+ }
+ p_item->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ p_item->hide();
+
+ BottomPanelItem bpi;
+ bpi.button = tb;
+ bpi.control = p_item;
+ bpi.name = p_text;
+ items.push_back(bpi);
+
+ return tb;
+}
+
+void EditorBottomPanel::remove_item(Control *p_item) {
+ for (int i = 0; i < items.size(); i++) {
+ if (items[i].control == p_item) {
+ if (p_item->is_visible_in_tree()) {
+ _switch_to_item(false, i);
+ }
+ item_vbox->remove_child(items[i].control);
+ button_hbox->remove_child(items[i].button);
+ memdelete(items[i].button);
+ items.remove_at(i);
+ break;
+ }
+ }
+}
+
+void EditorBottomPanel::make_item_visible(Control *p_item, bool p_visible) {
+ _switch_by_control(p_visible, p_item);
+}
+
+void EditorBottomPanel::move_item_to_end(Control *p_item) {
+ for (int i = 0; i < items.size(); i++) {
+ if (items[i].control == p_item) {
+ items[i].button->move_to_front();
+ SWAP(items.write[i], items.write[items.size() - 1]);
+ break;
+ }
+ }
+}
+
+void EditorBottomPanel::hide_bottom_panel() {
+ for (int i = 0; i < items.size(); i++) {
+ if (items[i].control->is_visible()) {
+ _switch_to_item(false, i);
+ break;
+ }
+ }
+}
+
+EditorBottomPanel::EditorBottomPanel() {
+ item_vbox = memnew(VBoxContainer);
+ add_child(item_vbox);
+
+ bottom_hbox = memnew(HBoxContainer);
+ bottom_hbox->set_custom_minimum_size(Size2(0, 24 * EDSCALE)); // Adjust for the height of the "Expand Bottom Dock" icon.
+ item_vbox->add_child(bottom_hbox);
+
+ button_hbox = memnew(HBoxContainer);
+ button_hbox->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ bottom_hbox->add_child(button_hbox);
+
+ editor_toaster = memnew(EditorToaster);
+ bottom_hbox->add_child(editor_toaster);
+
+ VBoxContainer *version_info_vbox = memnew(VBoxContainer);
+ bottom_hbox->add_child(version_info_vbox);
+
+ // Add a dummy control node for vertical spacing.
+ Control *v_spacer = memnew(Control);
+ version_info_vbox->add_child(v_spacer);
+
+ version_btn = memnew(LinkButton);
+ version_btn->set_text(VERSION_FULL_CONFIG);
+ String hash = String(VERSION_HASH);
+ if (hash.length() != 0) {
+ hash = " " + vformat("[%s]", hash.left(9));
+ }
+ // Set the text to copy in metadata as it slightly differs from the button's text.
+ version_btn->set_meta(META_TEXT_TO_COPY, "v" VERSION_FULL_BUILD + hash);
+ // Fade out the version label to be less prominent, but still readable.
+ version_btn->set_self_modulate(Color(1, 1, 1, 0.65));
+ version_btn->set_underline_mode(LinkButton::UNDERLINE_MODE_ON_HOVER);
+ version_btn->set_tooltip_text(TTR("Click to copy."));
+ version_btn->connect("pressed", callable_mp(this, &EditorBottomPanel::_version_button_pressed));
+ version_info_vbox->add_child(version_btn);
+
+ // Add a dummy control node for horizontal spacing.
+ Control *h_spacer = memnew(Control);
+ bottom_hbox->add_child(h_spacer);
+
+ expand_button = memnew(Button);
+ bottom_hbox->add_child(expand_button);
+ expand_button->hide();
+ expand_button->set_flat(false);
+ expand_button->set_theme_type_variation("FlatMenuButton");
+ expand_button->set_toggle_mode(true);
+ expand_button->set_shortcut(ED_SHORTCUT_AND_COMMAND("editor/bottom_panel_expand", TTR("Expand Bottom Panel"), KeyModifierMask::SHIFT | Key::F12));
+ expand_button->connect("toggled", callable_mp(this, &EditorBottomPanel::_expand_button_toggled));
+}
diff --git a/editor/gui/editor_bottom_panel.h b/editor/gui/editor_bottom_panel.h
new file mode 100644
index 0000000000..54b3a1319d
--- /dev/null
+++ b/editor/gui/editor_bottom_panel.h
@@ -0,0 +1,84 @@
+/**************************************************************************/
+/* editor_bottom_panel.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 EDITOR_BOTTOM_PANEL_H
+#define EDITOR_BOTTOM_PANEL_H
+
+#include "scene/gui/panel_container.h"
+
+class Button;
+class ConfigFile;
+class EditorToaster;
+class HBoxContainer;
+class LinkButton;
+class VBoxContainer;
+
+class EditorBottomPanel : public PanelContainer {
+ GDCLASS(EditorBottomPanel, PanelContainer);
+
+ struct BottomPanelItem {
+ String name;
+ Control *control = nullptr;
+ Button *button = nullptr;
+ };
+
+ Vector<BottomPanelItem> items;
+
+ VBoxContainer *item_vbox = nullptr;
+ HBoxContainer *bottom_hbox = nullptr;
+ HBoxContainer *button_hbox = nullptr;
+ EditorToaster *editor_toaster = nullptr;
+ LinkButton *version_btn = nullptr;
+ Button *expand_button = nullptr;
+
+ void _switch_by_control(bool p_visible, Control *p_control);
+ void _switch_to_item(bool p_visible, int p_idx);
+ void _expand_button_toggled(bool p_pressed);
+ void _version_button_pressed();
+
+ bool _button_drag_hover(const Vector2 &, const Variant &, Button *p_button, Control *p_control);
+
+protected:
+ void _notification(int p_what);
+
+public:
+ void save_layout_to_config(Ref<ConfigFile> p_config_file, const String &p_section) const;
+ void load_layout_from_config(Ref<ConfigFile> p_config_file, const String &p_section);
+
+ Button *add_item(String p_text, Control *p_item, bool p_at_front = false);
+ void remove_item(Control *p_item);
+ void make_item_visible(Control *p_item, bool p_visible = true);
+ void move_item_to_end(Control *p_item);
+ void hide_bottom_panel();
+
+ EditorBottomPanel();
+};
+
+#endif // EDITOR_BOTTOM_PANEL_H
diff --git a/editor/gui/editor_object_selector.cpp b/editor/gui/editor_object_selector.cpp
index fd46788c73..862ecc0a68 100644
--- a/editor/gui/editor_object_selector.cpp
+++ b/editor/gui/editor_object_selector.cpp
@@ -35,6 +35,7 @@
#include "editor/editor_string_names.h"
#include "editor/multi_node_edit.h"
#include "editor/themes/editor_scale.h"
+#include "scene/gui/margin_container.h"
Size2 EditorObjectSelector::get_minimum_size() const {
Ref<Font> font = get_theme_font(SNAME("font"));
diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp
index 8dc69fa686..7ee155b478 100644
--- a/editor/plugins/animation_player_editor_plugin.cpp
+++ b/editor/plugins/animation_player_editor_plugin.cpp
@@ -38,6 +38,7 @@
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
#include "editor/editor_undo_redo_manager.h"
+#include "editor/gui/editor_bottom_panel.h"
#include "editor/gui/editor_file_dialog.h"
#include "editor/inspector_dock.h"
#include "editor/plugins/canvas_item_editor_plugin.h" // For onion skinning.
@@ -793,7 +794,7 @@ void AnimationPlayerEditor::set_state(const Dictionary &p_state) {
}
_update_player();
- EditorNode::get_singleton()->make_bottom_panel_item_visible(this);
+ EditorNode::get_bottom_panel()->make_item_visible(this);
set_process(true);
ensure_visibility();
@@ -2266,7 +2267,7 @@ bool AnimationPlayerEditorPlugin::handles(Object *p_object) const {
void AnimationPlayerEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
- EditorNode::get_singleton()->make_bottom_panel_item_visible(anim_editor);
+ EditorNode::get_bottom_panel()->make_item_visible(anim_editor);
anim_editor->set_process(true);
anim_editor->ensure_visibility();
}
@@ -2274,7 +2275,7 @@ void AnimationPlayerEditorPlugin::make_visible(bool p_visible) {
AnimationPlayerEditorPlugin::AnimationPlayerEditorPlugin() {
anim_editor = memnew(AnimationPlayerEditor(this));
- EditorNode::get_singleton()->add_bottom_panel_item(TTR("Animation"), anim_editor);
+ EditorNode::get_bottom_panel()->add_item(TTR("Animation"), anim_editor);
}
AnimationPlayerEditorPlugin::~AnimationPlayerEditorPlugin() {
diff --git a/editor/plugins/animation_tree_editor_plugin.cpp b/editor/plugins/animation_tree_editor_plugin.cpp
index 7269395baf..3e9eebdeab 100644
--- a/editor/plugins/animation_tree_editor_plugin.cpp
+++ b/editor/plugins/animation_tree_editor_plugin.cpp
@@ -40,11 +40,13 @@
#include "core/math/delaunay_2d.h"
#include "core/os/keyboard.h"
#include "editor/editor_node.h"
+#include "editor/gui/editor_bottom_panel.h"
#include "editor/gui/editor_file_dialog.h"
#include "editor/themes/editor_scale.h"
#include "scene/animation/animation_blend_tree.h"
#include "scene/animation/animation_player.h"
#include "scene/gui/button.h"
+#include "scene/gui/margin_container.h"
#include "scene/gui/menu_button.h"
#include "scene/gui/panel.h"
#include "scene/gui/scroll_container.h"
@@ -299,11 +301,11 @@ void AnimationTreeEditorPlugin::make_visible(bool p_visible) {
//editor->hide_animation_player_editors();
//editor->animation_panel_make_visible(true);
button->show();
- EditorNode::get_singleton()->make_bottom_panel_item_visible(anim_tree_editor);
+ EditorNode::get_bottom_panel()->make_item_visible(anim_tree_editor);
anim_tree_editor->set_process(true);
} else {
if (anim_tree_editor->is_visible_in_tree()) {
- EditorNode::get_singleton()->hide_bottom_panel();
+ EditorNode::get_bottom_panel()->hide_bottom_panel();
}
button->hide();
anim_tree_editor->set_process(false);
@@ -314,7 +316,7 @@ AnimationTreeEditorPlugin::AnimationTreeEditorPlugin() {
anim_tree_editor = memnew(AnimationTreeEditor);
anim_tree_editor->set_custom_minimum_size(Size2(0, 300) * EDSCALE);
- button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("AnimationTree"), anim_tree_editor);
+ button = EditorNode::get_bottom_panel()->add_item(TTR("AnimationTree"), anim_tree_editor);
button->hide();
}
diff --git a/editor/plugins/debugger_editor_plugin.cpp b/editor/plugins/debugger_editor_plugin.cpp
index 3a96d3e8c3..da7b256572 100644
--- a/editor/plugins/debugger_editor_plugin.cpp
+++ b/editor/plugins/debugger_editor_plugin.cpp
@@ -36,6 +36,7 @@
#include "editor/debugger/editor_file_server.h"
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
+#include "editor/gui/editor_bottom_panel.h"
#include "editor/plugins/script_editor_plugin.h"
#include "editor/run_instances_dialog.h"
#include "editor/themes/editor_scale.h"
@@ -54,7 +55,7 @@ DebuggerEditorPlugin::DebuggerEditorPlugin(PopupMenu *p_debug_menu) {
file_server = memnew(EditorFileServer);
EditorDebuggerNode *debugger = memnew(EditorDebuggerNode);
- Button *db = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Debugger"), debugger);
+ Button *db = EditorNode::get_bottom_panel()->add_item(TTR("Debugger"), debugger);
debugger->set_tool_button(db);
// Main editor debug menu.
diff --git a/editor/plugins/font_config_plugin.cpp b/editor/plugins/font_config_plugin.cpp
index b71a11e166..f9b5e280c9 100644
--- a/editor/plugins/font_config_plugin.cpp
+++ b/editor/plugins/font_config_plugin.cpp
@@ -33,6 +33,7 @@
#include "editor/editor_settings.h"
#include "editor/import/dynamic_font_import_settings.h"
#include "editor/themes/editor_scale.h"
+#include "scene/gui/margin_container.h"
/*************************************************************************/
/* EditorPropertyFontMetaObject */
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index 78825c99e6..64590653a4 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -3445,7 +3445,8 @@ void Node3DEditorViewport::_menu_option(int p_option) {
int idx = view_menu->get_popup()->get_item_index(VIEW_GIZMOS);
bool current = view_menu->get_popup()->is_item_checked(idx);
current = !current;
- uint32_t layers = ((1 << 20) - 1) | (1 << (GIZMO_BASE_LAYER + index)) | (1 << GIZMO_GRID_LAYER) | (1 << MISC_TOOL_LAYER);
+ uint32_t layers = camera->get_cull_mask();
+ layers &= ~(1 << GIZMO_EDIT_LAYER);
if (current) {
layers |= (1 << GIZMO_EDIT_LAYER);
}
@@ -3469,7 +3470,18 @@ void Node3DEditorViewport::_menu_option(int p_option) {
int idx = view_menu->get_popup()->get_item_index(VIEW_FRAME_TIME);
bool current = view_menu->get_popup()->is_item_checked(idx);
view_menu->get_popup()->set_item_checked(idx, !current);
-
+ } break;
+ case VIEW_GRID: {
+ int idx = view_menu->get_popup()->get_item_index(VIEW_GRID);
+ bool current = view_menu->get_popup()->is_item_checked(idx);
+ current = !current;
+ uint32_t layers = camera->get_cull_mask();
+ layers &= ~(1 << GIZMO_GRID_LAYER);
+ if (current) {
+ layers |= (1 << GIZMO_GRID_LAYER);
+ }
+ camera->set_cull_mask(layers);
+ view_menu->get_popup()->set_item_checked(idx, current);
} break;
case VIEW_DISPLAY_NORMAL:
case VIEW_DISPLAY_WIREFRAME:
@@ -5142,6 +5154,7 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p
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_grid_lines", TTR("View Grid")), VIEW_GRID);
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 Frame Time")), VIEW_FRAME_TIME);
view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_ENVIRONMENT), true);
@@ -5151,6 +5164,7 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p
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()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_GRID), 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);
diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h
index 61b1d38cfc..003d462552 100644
--- a/editor/plugins/node_3d_editor_plugin.h
+++ b/editor/plugins/node_3d_editor_plugin.h
@@ -124,6 +124,7 @@ class Node3DEditorViewport : public Control {
VIEW_AUDIO_LISTENER,
VIEW_AUDIO_DOPPLER,
VIEW_GIZMOS,
+ VIEW_GRID,
VIEW_INFORMATION,
VIEW_FRAME_TIME,
diff --git a/editor/plugins/resource_preloader_editor_plugin.cpp b/editor/plugins/resource_preloader_editor_plugin.cpp
index 9b31e40e94..9aac87da35 100644
--- a/editor/plugins/resource_preloader_editor_plugin.cpp
+++ b/editor/plugins/resource_preloader_editor_plugin.cpp
@@ -36,6 +36,7 @@
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
#include "editor/editor_undo_redo_manager.h"
+#include "editor/gui/editor_bottom_panel.h"
#include "editor/gui/editor_file_dialog.h"
#include "editor/themes/editor_scale.h"
@@ -406,11 +407,11 @@ void ResourcePreloaderEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
//preloader_editor->show();
button->show();
- EditorNode::get_singleton()->make_bottom_panel_item_visible(preloader_editor);
+ EditorNode::get_bottom_panel()->make_item_visible(preloader_editor);
//preloader_editor->set_process(true);
} else {
if (preloader_editor->is_visible_in_tree()) {
- EditorNode::get_singleton()->hide_bottom_panel();
+ EditorNode::get_bottom_panel()->hide_bottom_panel();
}
button->hide();
//preloader_editor->hide();
@@ -422,7 +423,7 @@ ResourcePreloaderEditorPlugin::ResourcePreloaderEditorPlugin() {
preloader_editor = memnew(ResourcePreloaderEditor);
preloader_editor->set_custom_minimum_size(Size2(0, 250) * EDSCALE);
- button = EditorNode::get_singleton()->add_bottom_panel_item("ResourcePreloader", preloader_editor);
+ button = EditorNode::get_bottom_panel()->add_item("ResourcePreloader", preloader_editor);
button->hide();
}
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index d7d5381ed3..4a127e5d87 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -51,6 +51,7 @@
#include "editor/editor_string_names.h"
#include "editor/filesystem_dock.h"
#include "editor/find_in_files.h"
+#include "editor/gui/editor_bottom_panel.h"
#include "editor/gui/editor_file_dialog.h"
#include "editor/gui/editor_run_bar.h"
#include "editor/gui/editor_toaster.h"
@@ -1713,7 +1714,7 @@ void ScriptEditor::_notification(int p_what) {
find_in_files_button->show();
} else {
if (find_in_files->is_visible_in_tree()) {
- EditorNode::get_singleton()->hide_bottom_panel();
+ EditorNode::get_bottom_panel()->hide_bottom_panel();
}
find_in_files_button->hide();
}
@@ -3814,7 +3815,7 @@ void ScriptEditor::_start_find_in_files(bool with_replace) {
find_in_files->set_replace_text(find_in_files_dialog->get_replace_text());
find_in_files->start_search();
- EditorNode::get_singleton()->make_bottom_panel_item_visible(find_in_files);
+ EditorNode::get_bottom_panel()->make_item_visible(find_in_files);
}
void ScriptEditor::_on_find_in_files_modified_files(PackedStringArray paths) {
@@ -4189,7 +4190,7 @@ ScriptEditor::ScriptEditor(WindowWrapper *p_wrapper) {
find_in_files_dialog->connect(FindInFilesDialog::SIGNAL_REPLACE_REQUESTED, callable_mp(this, &ScriptEditor::_start_find_in_files).bind(true));
add_child(find_in_files_dialog);
find_in_files = memnew(FindInFilesPanel);
- find_in_files_button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Search Results"), find_in_files);
+ find_in_files_button = EditorNode::get_bottom_panel()->add_item(TTR("Search Results"), find_in_files);
find_in_files->set_custom_minimum_size(Size2(0, 200) * EDSCALE);
find_in_files->connect(FindInFilesPanel::SIGNAL_RESULT_SELECTED, callable_mp(this, &ScriptEditor::_on_find_in_files_result_selected));
find_in_files->connect(FindInFilesPanel::SIGNAL_FILES_MODIFIED, callable_mp(this, &ScriptEditor::_on_find_in_files_modified_files));
diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp
index cce1f160b2..9321c6dafc 100644
--- a/editor/plugins/shader_editor_plugin.cpp
+++ b/editor/plugins/shader_editor_plugin.cpp
@@ -35,6 +35,7 @@
#include "editor/editor_string_names.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/filesystem_dock.h"
+#include "editor/gui/editor_bottom_panel.h"
#include "editor/inspector_dock.h"
#include "editor/plugins/text_shader_editor.h"
#include "editor/plugins/visual_shader_editor_plugin.h"
@@ -186,7 +187,7 @@ bool ShaderEditorPlugin::handles(Object *p_object) const {
void ShaderEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
- EditorNode::get_singleton()->make_bottom_panel_item_visible(window_wrapper);
+ EditorNode::get_bottom_panel()->make_item_visible(window_wrapper);
}
}
@@ -702,7 +703,7 @@ ShaderEditorPlugin::ShaderEditorPlugin() {
empty.instantiate();
shader_tabs->add_theme_style_override("panel", empty);
- button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Shader Editor"), window_wrapper);
+ button = EditorNode::get_bottom_panel()->add_item(TTR("Shader Editor"), window_wrapper);
shader_create_dialog = memnew(ShaderCreateDialog);
vb->add_child(shader_create_dialog);
diff --git a/editor/plugins/shader_file_editor_plugin.cpp b/editor/plugins/shader_file_editor_plugin.cpp
index 38cc51d3c0..e127007d0c 100644
--- a/editor/plugins/shader_file_editor_plugin.cpp
+++ b/editor/plugins/shader_file_editor_plugin.cpp
@@ -37,6 +37,7 @@
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
+#include "editor/gui/editor_bottom_panel.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/item_list.h"
#include "scene/gui/split_container.h"
@@ -308,12 +309,12 @@ bool ShaderFileEditorPlugin::handles(Object *p_object) const {
void ShaderFileEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
button->show();
- EditorNode::get_singleton()->make_bottom_panel_item_visible(shader_editor);
+ EditorNode::get_bottom_panel()->make_item_visible(shader_editor);
} else {
button->hide();
if (shader_editor->is_visible_in_tree()) {
- EditorNode::get_singleton()->hide_bottom_panel();
+ EditorNode::get_bottom_panel()->hide_bottom_panel();
}
}
}
@@ -322,7 +323,7 @@ ShaderFileEditorPlugin::ShaderFileEditorPlugin() {
shader_editor = memnew(ShaderFileEditor);
shader_editor->set_custom_minimum_size(Size2(0, 300) * EDSCALE);
- button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("ShaderFile"), shader_editor);
+ button = EditorNode::get_bottom_panel()->add_item(TTR("ShaderFile"), shader_editor);
button->hide();
}
diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp
index 2495d28e3c..d60345c906 100644
--- a/editor/plugins/sprite_frames_editor_plugin.cpp
+++ b/editor/plugins/sprite_frames_editor_plugin.cpp
@@ -38,6 +38,7 @@
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
#include "editor/editor_undo_redo_manager.h"
+#include "editor/gui/editor_bottom_panel.h"
#include "editor/gui/editor_file_dialog.h"
#include "editor/scene_tree_dock.h"
#include "editor/themes/editor_scale.h"
@@ -2219,7 +2220,7 @@ bool SpriteFramesEditorPlugin::handles(Object *p_object) const {
void SpriteFramesEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
button->show();
- EditorNode::get_singleton()->make_bottom_panel_item_visible(frames_editor);
+ EditorNode::get_bottom_panel()->make_item_visible(frames_editor);
} else {
button->hide();
frames_editor->edit(Ref<SpriteFrames>());
@@ -2229,7 +2230,7 @@ void SpriteFramesEditorPlugin::make_visible(bool p_visible) {
SpriteFramesEditorPlugin::SpriteFramesEditorPlugin() {
frames_editor = memnew(SpriteFramesEditor);
frames_editor->set_custom_minimum_size(Size2(0, 300) * EDSCALE);
- button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("SpriteFrames"), frames_editor);
+ button = EditorNode::get_bottom_panel()->add_item(TTR("SpriteFrames"), frames_editor);
button->hide();
}
diff --git a/editor/plugins/text_shader_editor.h b/editor/plugins/text_shader_editor.h
index a1b45d1b2e..73d7de98e4 100644
--- a/editor/plugins/text_shader_editor.h
+++ b/editor/plugins/text_shader_editor.h
@@ -32,6 +32,7 @@
#define TEXT_SHADER_EDITOR_H
#include "editor/code_editor.h"
+#include "scene/gui/margin_container.h"
#include "scene/gui/menu_button.h"
#include "scene/gui/panel_container.h"
#include "scene/gui/rich_text_label.h"
diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp
index 8f369b23b2..92f107f369 100644
--- a/editor/plugins/theme_editor_plugin.cpp
+++ b/editor/plugins/theme_editor_plugin.cpp
@@ -36,6 +36,7 @@
#include "editor/editor_resource_picker.h"
#include "editor/editor_string_names.h"
#include "editor/editor_undo_redo_manager.h"
+#include "editor/gui/editor_bottom_panel.h"
#include "editor/gui/editor_file_dialog.h"
#include "editor/inspector_dock.h"
#include "editor/progress_dialog.h"
@@ -3760,10 +3761,10 @@ bool ThemeEditorPlugin::handles(Object *p_object) const {
void ThemeEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
button->show();
- EditorNode::get_singleton()->make_bottom_panel_item_visible(theme_editor);
+ EditorNode::get_bottom_panel()->make_item_visible(theme_editor);
} else {
if (theme_editor->is_visible_in_tree()) {
- EditorNode::get_singleton()->hide_bottom_panel();
+ EditorNode::get_bottom_panel()->hide_bottom_panel();
}
button->hide();
@@ -3843,6 +3844,6 @@ ThemeEditorPlugin::ThemeEditorPlugin() {
theme_editor->plugin = this;
theme_editor->set_custom_minimum_size(Size2(0, 200) * EDSCALE);
- button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Theme"), theme_editor);
+ button = EditorNode::get_bottom_panel()->add_item(TTR("Theme"), theme_editor);
button->hide();
}
diff --git a/editor/plugins/tiles/tiles_editor_plugin.cpp b/editor/plugins/tiles/tiles_editor_plugin.cpp
index 8ef052c71f..46bc072c18 100644
--- a/editor/plugins/tiles/tiles_editor_plugin.cpp
+++ b/editor/plugins/tiles/tiles_editor_plugin.cpp
@@ -38,6 +38,7 @@
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
+#include "editor/gui/editor_bottom_panel.h"
#include "editor/plugins/canvas_item_editor_plugin.h"
#include "editor/themes/editor_scale.h"
#include "scene/2d/tile_map.h"
@@ -479,11 +480,11 @@ bool TileMapEditorPlugin::handles(Object *p_object) const {
void TileMapEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
button->show();
- EditorNode::get_singleton()->make_bottom_panel_item_visible(editor);
+ EditorNode::get_bottom_panel()->make_item_visible(editor);
} else {
button->hide();
if (editor->is_visible_in_tree()) {
- EditorNode::get_singleton()->hide_bottom_panel();
+ EditorNode::get_bottom_panel()->hide_bottom_panel();
}
}
}
@@ -498,7 +499,7 @@ void TileMapEditorPlugin::forward_canvas_draw_over_viewport(Control *p_overlay)
void TileMapEditorPlugin::hide_editor() {
if (editor->is_visible_in_tree()) {
- EditorNode::get_singleton()->hide_bottom_panel();
+ EditorNode::get_bottom_panel()->hide_bottom_panel();
}
}
@@ -519,7 +520,7 @@ TileMapEditorPlugin::TileMapEditorPlugin() {
editor->connect("change_selected_layer_request", callable_mp(this, &TileMapEditorPlugin::_select_layer));
editor->hide();
- button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("TileMap"), editor);
+ button = EditorNode::get_bottom_panel()->add_item(TTR("TileMap"), editor);
button->hide();
}
@@ -544,12 +545,12 @@ void TileSetEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
button->show();
if (!tile_map_plugin_singleton->is_editor_visible()) {
- EditorNode::get_singleton()->make_bottom_panel_item_visible(editor);
+ EditorNode::get_bottom_panel()->make_item_visible(editor);
}
} else {
button->hide();
if (editor->is_visible_in_tree()) {
- EditorNode::get_singleton()->hide_bottom_panel();
+ EditorNode::get_bottom_panel()->hide_bottom_panel();
}
}
}
@@ -570,7 +571,7 @@ TileSetEditorPlugin::TileSetEditorPlugin() {
editor->set_custom_minimum_size(Size2(0, 200) * EDSCALE);
editor->hide();
- button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("TileSet"), editor);
+ button = EditorNode::get_bottom_panel()->add_item(TTR("TileSet"), editor);
button->hide();
}
diff --git a/editor/plugins/version_control_editor_plugin.cpp b/editor/plugins/version_control_editor_plugin.cpp
index 19acc706f2..06526fea28 100644
--- a/editor/plugins/version_control_editor_plugin.cpp
+++ b/editor/plugins/version_control_editor_plugin.cpp
@@ -40,6 +40,7 @@
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
#include "editor/filesystem_dock.h"
+#include "editor/gui/editor_bottom_panel.h"
#include "editor/plugins/script_editor_plugin.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/separator.h"
@@ -912,7 +913,7 @@ void VersionControlEditorPlugin::fetch_available_vcs_plugin_names() {
void VersionControlEditorPlugin::register_editor() {
EditorDockManager::get_singleton()->add_control_to_dock(EditorDockManager::DOCK_SLOT_RIGHT_UL, version_commit_dock);
- version_control_dock_button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Version Control"), version_control_dock);
+ version_control_dock_button = EditorNode::get_bottom_panel()->add_item(TTR("Version Control"), version_control_dock);
_set_vcs_ui_state(true);
}
@@ -931,7 +932,7 @@ void VersionControlEditorPlugin::shut_down() {
EditorVCSInterface::set_singleton(nullptr);
EditorDockManager::get_singleton()->remove_control_from_dock(version_commit_dock);
- EditorNode::get_singleton()->remove_bottom_panel_item(version_control_dock);
+ EditorNode::get_bottom_panel()->remove_item(version_control_dock);
_set_vcs_ui_state(false);
}
diff --git a/editor/run_instances_dialog.cpp b/editor/run_instances_dialog.cpp
index e446af0808..d8733288b2 100644
--- a/editor/run_instances_dialog.cpp
+++ b/editor/run_instances_dialog.cpp
@@ -34,6 +34,7 @@
#include "editor/editor_settings.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/check_box.h"
+#include "scene/gui/grid_container.h"
#include "scene/gui/label.h"
#include "scene/gui/line_edit.h"
#include "scene/gui/separator.h"
@@ -271,52 +272,56 @@ RunInstancesDialog::RunInstancesDialog() {
VBoxContainer *main_vb = memnew(VBoxContainer);
add_child(main_vb);
+ GridContainer *args_gc = memnew(GridContainer);
+ args_gc->set_columns(3);
+ args_gc->add_theme_constant_override("h_separation", 12 * EDSCALE);
+ main_vb->add_child(args_gc);
+
+ enable_multiple_instances_checkbox = memnew(CheckBox);
+ enable_multiple_instances_checkbox->set_text(TTR("Enable Multiple Instances"));
+ enable_multiple_instances_checkbox->set_pressed(EditorSettings::get_singleton()->get_project_metadata("debug_options", "multiple_instances_enabled", false));
+ args_gc->add_child(enable_multiple_instances_checkbox);
+ enable_multiple_instances_checkbox->connect("pressed", callable_mp(this, &RunInstancesDialog::_start_main_timer));
+
{
Label *l = memnew(Label);
- main_vb->add_child(l);
l->set_text(TTR("Main Run Args:"));
+ args_gc->add_child(l);
}
- main_args_edit = memnew(LineEdit);
- main_vb->add_child(main_args_edit);
- _fetch_main_args();
- ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &RunInstancesDialog::_fetch_main_args));
- main_args_edit->connect("text_changed", callable_mp(this, &RunInstancesDialog::_start_main_timer).unbind(1));
-
{
Label *l = memnew(Label);
- main_vb->add_child(l);
l->set_text(TTR("Main Feature Tags:"));
+ args_gc->add_child(l);
}
- main_features_edit = memnew(LineEdit);
- main_features_edit->set_text(EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_main_feature_tags", ""));
- main_vb->add_child(main_features_edit);
- main_features_edit->connect("text_changed", callable_mp(this, &RunInstancesDialog::_start_main_timer).unbind(1));
-
- {
- HSeparator *sep = memnew(HSeparator);
- main_vb->add_child(sep);
- }
-
- enable_multiple_instances_checkbox = memnew(CheckBox);
- enable_multiple_instances_checkbox->set_text(TTR("Enable Multiple Instances"));
- enable_multiple_instances_checkbox->set_pressed(EditorSettings::get_singleton()->get_project_metadata("debug_options", "multiple_instances_enabled", false));
- main_vb->add_child(enable_multiple_instances_checkbox);
- enable_multiple_instances_checkbox->connect("pressed", callable_mp(this, &RunInstancesDialog::_start_main_timer));
-
stored_data = TypedArray<Dictionary>(EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_instances_config", TypedArray<Dictionary>()));
instance_count = memnew(SpinBox);
instance_count->set_min(1);
instance_count->set_max(20);
instance_count->set_value(stored_data.size());
- main_vb->add_child(instance_count);
+ args_gc->add_child(instance_count);
instance_count->connect("value_changed", callable_mp(this, &RunInstancesDialog::_start_instance_timer).unbind(1));
instance_count->connect("value_changed", callable_mp(this, &RunInstancesDialog::_refresh_argument_count).unbind(1));
enable_multiple_instances_checkbox->connect("toggled", callable_mp(instance_count, &SpinBox::set_editable));
instance_count->set_editable(enable_multiple_instances_checkbox->is_pressed());
+ main_args_edit = memnew(LineEdit);
+ main_args_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ main_args_edit->set_placeholder(TTR("Space-separated arguments, example: host player1 blue"));
+ args_gc->add_child(main_args_edit);
+ _fetch_main_args();
+ ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &RunInstancesDialog::_fetch_main_args));
+ main_args_edit->connect("text_changed", callable_mp(this, &RunInstancesDialog::_start_main_timer).unbind(1));
+
+ main_features_edit = memnew(LineEdit);
+ main_features_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ main_features_edit->set_placeholder(TTR("Comma-separated tags, example: demo, steam, event"));
+ main_features_edit->set_text(EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_main_feature_tags", ""));
+ args_gc->add_child(main_features_edit);
+ main_features_edit->connect("text_changed", callable_mp(this, &RunInstancesDialog::_start_main_timer).unbind(1));
+
{
Label *l = memnew(Label);
l->set_text(TTR("Instance Configuration"));
diff --git a/misc/scripts/install_d3d12_sdk_windows.py b/misc/scripts/install_d3d12_sdk_windows.py
index 8cfa1d399b..6dd0818b97 100644
--- a/misc/scripts/install_d3d12_sdk_windows.py
+++ b/misc/scripts/install_d3d12_sdk_windows.py
@@ -3,6 +3,7 @@
import os
import urllib.request
import shutil
+import subprocess
# Base Godot dependencies path
# If cross-compiling (no LOCALAPPDATA), we install in `bin`
@@ -18,8 +19,8 @@ dxc_filename = "dxc_2023_08_14.zip"
dxc_archive = os.path.join(deps_folder, dxc_filename)
dxc_folder = os.path.join(deps_folder, "dxc")
# Mesa NIR
-mesa_version = "23.1.0-devel"
-mesa_filename = "godot-nir-23.1.0-1-devel.zip"
+mesa_version = "23.1.9"
+mesa_filename = "godot-nir-23.1.9.zip"
mesa_archive = os.path.join(deps_folder, mesa_filename)
mesa_folder = os.path.join(deps_folder, "mesa")
# WinPixEventRuntime
@@ -70,6 +71,16 @@ os.remove(mesa_archive)
print(f"Mesa NIR {mesa_filename} installed successfully.\n")
# WinPixEventRuntime
+
+# MinGW needs DLLs converted with dlltool.
+# We rely on finding gendef/dlltool to detect if we have MinGW.
+# Check existence of needed tools for generating mingw library.
+gendef = shutil.which("gendef") or ""
+dlltool = shutil.which("dlltool") or ""
+if dlltool == "":
+ dlltool = shutil.which("x86_64-w64-mingw32-dlltool") or ""
+has_mingw = gendef != "" and dlltool != ""
+
print("[3/4] WinPixEventRuntime")
if os.path.isfile(pix_archive):
os.remove(pix_archive)
@@ -81,6 +92,23 @@ if os.path.exists(pix_folder):
print(f"Extracting WinPixEventRuntime {pix_version} to {pix_folder} ...")
shutil.unpack_archive(pix_archive, pix_folder, "zip")
os.remove(pix_archive)
+if has_mingw:
+ print("Adapting WinPixEventRuntime to also support MinGW alongside MSVC.")
+ cwd = os.getcwd()
+ os.chdir(pix_folder)
+ subprocess.run([gendef, "./bin/x64/WinPixEventRuntime.dll"])
+ subprocess.run(
+ [dlltool]
+ + "--machine i386:x86-64 --no-leading-underscore -d WinPixEventRuntime.def -D WinPixEventRuntime.dll -l ./bin/x64/libWinPixEventRuntime.a".split()
+ )
+ subprocess.run([gendef, "./bin/ARM64/WinPixEventRuntime.dll"])
+ subprocess.run(
+ [dlltool]
+ + "--machine arm64 --no-leading-underscore -d WinPixEventRuntime.def -D WinPixEventRuntime.dll -l ./bin/ARM64/libWinPixEventRuntime.a".split()
+ )
+ os.chdir(cwd)
+else:
+ print("MinGW wasn't found, so only MSVC support is provided for WinPixEventRuntime.")
print(f"WinPixEventRuntime {pix_version} installed successfully.\n")
# DirectX 12 Agility SDK
@@ -100,5 +128,5 @@ os.remove(agility_sdk_archive)
print(f"DirectX 12 Agility SDK {agility_sdk_version} installed successfully.\n")
# Complete message
-print(f"All Direct3D 12 SDK components were installed to {deps_folder} successfully!")
+print(f'All Direct3D 12 SDK components were installed to "{deps_folder}" successfully!')
print('You can now build Godot with Direct3D 12 support enabled by running "scons d3d12=yes".')
diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp
index 27766115d5..36b157521d 100644
--- a/modules/gdscript/gdscript_byte_codegen.cpp
+++ b/modules/gdscript/gdscript_byte_codegen.cpp
@@ -429,7 +429,7 @@ void GDScriptByteCodeGenerator::set_initial_line(int p_line) {
(m_var.type.has_type && m_var.type.kind == GDScriptDataType::BUILTIN)
#define IS_BUILTIN_TYPE(m_var, m_type) \
- (m_var.type.has_type && m_var.type.kind == GDScriptDataType::BUILTIN && m_var.type.builtin_type == m_type)
+ (m_var.type.has_type && m_var.type.kind == GDScriptDataType::BUILTIN && m_var.type.builtin_type == m_type && m_type != Variant::NIL)
void GDScriptByteCodeGenerator::write_type_adjust(const Address &p_target, Variant::Type p_new_type) {
switch (p_new_type) {
diff --git a/modules/gdscript/tests/scripts/runtime/features/match_test_null.gd b/modules/gdscript/tests/scripts/runtime/features/match_test_null.gd
new file mode 100644
index 0000000000..9bb57b68ee
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/match_test_null.gd
@@ -0,0 +1,6 @@
+func test():
+ match null:
+ null:
+ print('null matched')
+ _:
+ pass
diff --git a/modules/gdscript/tests/scripts/runtime/features/match_test_null.out b/modules/gdscript/tests/scripts/runtime/features/match_test_null.out
new file mode 100644
index 0000000000..7640cf42ab
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/match_test_null.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+null matched
diff --git a/modules/multiplayer/editor/multiplayer_editor_plugin.cpp b/modules/multiplayer/editor/multiplayer_editor_plugin.cpp
index af2db543c0..e8dfc3379c 100644
--- a/modules/multiplayer/editor/multiplayer_editor_plugin.cpp
+++ b/modules/multiplayer/editor/multiplayer_editor_plugin.cpp
@@ -36,6 +36,7 @@
#include "editor/editor_interface.h"
#include "editor/editor_node.h"
+#include "editor/gui/editor_bottom_panel.h"
void MultiplayerEditorDebugger::_bind_methods() {
ADD_SIGNAL(MethodInfo("open_request", PropertyInfo(Variant::STRING, "path")));
@@ -112,7 +113,7 @@ void MultiplayerEditorDebugger::setup_session(int p_session_id) {
MultiplayerEditorPlugin::MultiplayerEditorPlugin() {
repl_editor = memnew(ReplicationEditor);
- button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Replication"), repl_editor);
+ button = EditorNode::get_bottom_panel()->add_item(TTR("Replication"), repl_editor);
button->hide();
repl_editor->get_pin()->connect("pressed", callable_mp(this, &MultiplayerEditorPlugin::_pinned));
debugger.instantiate();
@@ -139,7 +140,7 @@ void MultiplayerEditorPlugin::_node_removed(Node *p_node) {
if (p_node && p_node == repl_editor->get_current()) {
repl_editor->edit(nullptr);
if (repl_editor->is_visible_in_tree()) {
- EditorNode::get_singleton()->hide_bottom_panel();
+ EditorNode::get_bottom_panel()->hide_bottom_panel();
}
button->hide();
repl_editor->get_pin()->set_pressed(false);
@@ -149,7 +150,7 @@ void MultiplayerEditorPlugin::_node_removed(Node *p_node) {
void MultiplayerEditorPlugin::_pinned() {
if (!repl_editor->get_pin()->is_pressed()) {
if (repl_editor->is_visible_in_tree()) {
- EditorNode::get_singleton()->hide_bottom_panel();
+ EditorNode::get_bottom_panel()->hide_bottom_panel();
}
button->hide();
}
@@ -166,10 +167,10 @@ bool MultiplayerEditorPlugin::handles(Object *p_object) const {
void MultiplayerEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
button->show();
- EditorNode::get_singleton()->make_bottom_panel_item_visible(repl_editor);
+ EditorNode::get_bottom_panel()->make_item_visible(repl_editor);
} else if (!repl_editor->get_pin()->is_pressed()) {
if (repl_editor->is_visible_in_tree()) {
- EditorNode::get_singleton()->hide_bottom_panel();
+ EditorNode::get_bottom_panel()->hide_bottom_panel();
}
button->hide();
}
diff --git a/modules/openxr/doc_classes/OpenXRHand.xml b/modules/openxr/doc_classes/OpenXRHand.xml
index 1c4da83138..23d932ddd7 100644
--- a/modules/openxr/doc_classes/OpenXRHand.xml
+++ b/modules/openxr/doc_classes/OpenXRHand.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="OpenXRHand" inherits="Node3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
+<class name="OpenXRHand" inherits="Node3D" deprecated="Use [XRHandModifier3D] instead." xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Node supporting hand and finger tracking in OpenXR.
</brief_description>
diff --git a/modules/openxr/doc_classes/OpenXRInterface.xml b/modules/openxr/doc_classes/OpenXRInterface.xml
index caaaeb90ff..5d38788dcd 100644
--- a/modules/openxr/doc_classes/OpenXRInterface.xml
+++ b/modules/openxr/doc_classes/OpenXRInterface.xml
@@ -23,7 +23,7 @@
Returns display refresh rates supported by the current HMD. Only returned if this feature is supported by the OpenXR runtime and after the interface has been initialized.
</description>
</method>
- <method name="get_hand_joint_angular_velocity" qualifiers="const">
+ <method name="get_hand_joint_angular_velocity" qualifiers="const" deprecated="Use [method XRHandTracker.get_hand_joint_angular_velocity] obtained from [method XRServer.get_hand_tracker] instead.">
<return type="Vector3" />
<param index="0" name="hand" type="int" enum="OpenXRInterface.Hand" />
<param index="1" name="joint" type="int" enum="OpenXRInterface.HandJoints" />
@@ -31,7 +31,7 @@
If handtracking is enabled, returns the angular velocity of a joint ([param joint]) of a hand ([param hand]) as provided by OpenXR. This is relative to [XROrigin3D]!
</description>
</method>
- <method name="get_hand_joint_flags" qualifiers="const">
+ <method name="get_hand_joint_flags" qualifiers="const" deprecated="Use [method XRHandTracker.get_hand_joint_flags] obtained from [method XRServer.get_hand_tracker] instead.">
<return type="int" enum="OpenXRInterface.HandJointFlags" is_bitfield="true" />
<param index="0" name="hand" type="int" enum="OpenXRInterface.Hand" />
<param index="1" name="joint" type="int" enum="OpenXRInterface.HandJoints" />
@@ -39,7 +39,7 @@
If handtracking is enabled, returns flags that inform us of the validity of the tracking data.
</description>
</method>
- <method name="get_hand_joint_linear_velocity" qualifiers="const">
+ <method name="get_hand_joint_linear_velocity" qualifiers="const" deprecated="Use [method XRHandTracker.get_hand_joint_linear_velocity] obtained from [method XRServer.get_hand_tracker] instead.">
<return type="Vector3" />
<param index="0" name="hand" type="int" enum="OpenXRInterface.Hand" />
<param index="1" name="joint" type="int" enum="OpenXRInterface.HandJoints" />
@@ -47,7 +47,7 @@
If handtracking is enabled, returns the linear velocity of a joint ([param joint]) of a hand ([param hand]) as provided by OpenXR. This is relative to [XROrigin3D] without worldscale applied!
</description>
</method>
- <method name="get_hand_joint_position" qualifiers="const">
+ <method name="get_hand_joint_position" qualifiers="const" deprecated="Use [method XRHandTracker.get_hand_joint_transform] obtained from [method XRServer.get_hand_tracker] instead.">
<return type="Vector3" />
<param index="0" name="hand" type="int" enum="OpenXRInterface.Hand" />
<param index="1" name="joint" type="int" enum="OpenXRInterface.HandJoints" />
@@ -55,7 +55,7 @@
If handtracking is enabled, returns the position of a joint ([param joint]) of a hand ([param hand]) as provided by OpenXR. This is relative to [XROrigin3D] without worldscale applied!
</description>
</method>
- <method name="get_hand_joint_radius" qualifiers="const">
+ <method name="get_hand_joint_radius" qualifiers="const" deprecated="Use [method XRHandTracker.get_hand_joint_radius] obtained from [method XRServer.get_hand_tracker] instead.">
<return type="float" />
<param index="0" name="hand" type="int" enum="OpenXRInterface.Hand" />
<param index="1" name="joint" type="int" enum="OpenXRInterface.HandJoints" />
@@ -63,7 +63,7 @@
If handtracking is enabled, returns the radius of a joint ([param joint]) of a hand ([param hand]) as provided by OpenXR. This is without worldscale applied!
</description>
</method>
- <method name="get_hand_joint_rotation" qualifiers="const">
+ <method name="get_hand_joint_rotation" qualifiers="const" deprecated="Use [method XRHandTracker.get_hand_joint_transform] obtained from [method XRServer.get_hand_tracker] instead.">
<return type="Quaternion" />
<param index="0" name="hand" type="int" enum="OpenXRInterface.Hand" />
<param index="1" name="joint" type="int" enum="OpenXRInterface.HandJoints" />
@@ -71,7 +71,7 @@
If handtracking is enabled, returns the rotation of a joint ([param joint]) of a hand ([param hand]) as provided by OpenXR.
</description>
</method>
- <method name="get_hand_tracking_source" qualifiers="const">
+ <method name="get_hand_tracking_source" qualifiers="const" deprecated="Use [member XRHandTracker.hand_tracking_source] obtained from [method XRServer.get_hand_tracker] instead.">
<return type="int" enum="OpenXRInterface.HandTrackedSource" />
<param index="0" name="hand" type="int" enum="OpenXRInterface.Hand" />
<description>
diff --git a/modules/openxr/editor/openxr_action_map_editor.cpp b/modules/openxr/editor/openxr_action_map_editor.cpp
index 15468a1c67..5dd737756a 100644
--- a/modules/openxr/editor/openxr_action_map_editor.cpp
+++ b/modules/openxr/editor/openxr_action_map_editor.cpp
@@ -33,6 +33,7 @@
#include "core/config/project_settings.h"
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
+#include "editor/gui/editor_bottom_panel.h"
#include "editor/gui/editor_file_dialog.h"
#include "editor/themes/editor_scale.h"
@@ -356,7 +357,7 @@ void OpenXRActionMapEditor::_do_remove_interaction_profile_editor(OpenXRInteract
}
void OpenXRActionMapEditor::open_action_map(String p_path) {
- EditorNode::get_singleton()->make_bottom_panel_item_visible(this);
+ EditorNode::get_bottom_panel()->make_item_visible(this);
// out with the old...
_clear_action_map();
diff --git a/modules/openxr/editor/openxr_editor_plugin.cpp b/modules/openxr/editor/openxr_editor_plugin.cpp
index 559890ecb3..f545e31073 100644
--- a/modules/openxr/editor/openxr_editor_plugin.cpp
+++ b/modules/openxr/editor/openxr_editor_plugin.cpp
@@ -33,6 +33,7 @@
#include "../action_map/openxr_action_map.h"
#include "editor/editor_node.h"
+#include "editor/gui/editor_bottom_panel.h"
void OpenXREditorPlugin::edit(Object *p_node) {
if (Object::cast_to<OpenXRActionMap>(p_node)) {
@@ -52,7 +53,7 @@ void OpenXREditorPlugin::make_visible(bool p_visible) {
OpenXREditorPlugin::OpenXREditorPlugin() {
action_map_editor = memnew(OpenXRActionMapEditor);
- EditorNode::get_singleton()->add_bottom_panel_item(TTR("OpenXR Action Map"), action_map_editor);
+ EditorNode::get_bottom_panel()->add_item(TTR("OpenXR Action Map"), action_map_editor);
#ifndef ANDROID_ENABLED
select_runtime = memnew(OpenXRSelectRuntime);
diff --git a/modules/openxr/extensions/openxr_hand_tracking_extension.cpp b/modules/openxr/extensions/openxr_hand_tracking_extension.cpp
index 884fb41a3c..b3c20ef8b9 100644
--- a/modules/openxr/extensions/openxr_hand_tracking_extension.cpp
+++ b/modules/openxr/extensions/openxr_hand_tracking_extension.cpp
@@ -193,11 +193,18 @@ void OpenXRHandTrackingExtension::on_process() {
hand_trackers[i].locations.jointCount = XR_HAND_JOINT_COUNT_EXT;
hand_trackers[i].locations.jointLocations = hand_trackers[i].joint_locations;
+ Ref<XRHandTracker> godot_tracker;
+ godot_tracker.instantiate();
+ godot_tracker->set_hand(i == 0 ? XRHandTracker::HAND_LEFT : XRHandTracker::HAND_RIGHT);
+ XRServer::get_singleton()->add_hand_tracker(i == 0 ? "/user/left" : "/user/right", godot_tracker);
+ hand_trackers[i].godot_tracker = godot_tracker;
+
hand_trackers[i].is_initialized = true;
}
}
if (hand_trackers[i].is_initialized) {
+ Ref<XRHandTracker> godot_tracker = hand_trackers[i].godot_tracker;
void *next_pointer = nullptr;
XrHandJointsMotionRangeInfoEXT motion_range_info = { XR_TYPE_HAND_JOINTS_MOTION_RANGE_INFO_EXT, next_pointer, hand_trackers[i].motion_range };
@@ -216,6 +223,7 @@ void OpenXRHandTrackingExtension::on_process() {
if (XR_FAILED(result)) {
// not successful? then we do nothing.
print_line("OpenXR: Failed to get tracking for hand", i, "[", OpenXRAPI::get_singleton()->get_error_string(result), "]");
+ godot_tracker->set_has_tracking_data(false);
continue;
}
@@ -225,6 +233,64 @@ void OpenXRHandTrackingExtension::on_process() {
!hand_trackers[i].locations.isActive || isnan(palm.position.x) || palm.position.x < -1000000.00 || palm.position.x > 1000000.00) {
hand_trackers[i].locations.isActive = false; // workaround, make sure its inactive
}
+
+ if (hand_trackers[i].locations.isActive) {
+ godot_tracker->set_has_tracking_data(true);
+
+ // SKELETON_RIG_HUMANOID bone adjustment. This rotation performs:
+ // OpenXR Z+ -> Godot Humanoid Y- (Back along the bone)
+ // OpenXR Y+ -> Godot Humanoid Z- (Out the back of the hand)
+ const Quaternion bone_adjustment(0.0, -Math_SQRT12, Math_SQRT12, 0.0);
+
+ for (int joint = 0; joint < XR_HAND_JOINT_COUNT_EXT; joint++) {
+ const XrHandJointLocationEXT &location = hand_trackers[i].joint_locations[joint];
+ const XrHandJointVelocityEXT &velocity = hand_trackers[i].joint_velocities[joint];
+ const XrHandTrackingDataSourceStateEXT &data_source = hand_trackers[i].data_source;
+ const XrPosef &pose = location.pose;
+
+ Transform3D transform;
+ BitField<XRHandTracker::HandJointFlags> flags;
+
+ if (location.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) {
+ if (pose.orientation.x != 0 || pose.orientation.y != 0 || pose.orientation.z != 0 || pose.orientation.w != 0) {
+ flags.set_flag(XRHandTracker::HAND_JOINT_FLAG_ORIENTATION_VALID);
+ transform.basis = Basis(Quaternion(pose.orientation.x, pose.orientation.y, pose.orientation.z, pose.orientation.w) * bone_adjustment);
+ }
+ }
+ if (location.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) {
+ flags.set_flag(XRHandTracker::HAND_JOINT_FLAG_POSITION_VALID);
+ transform.origin = Vector3(pose.position.x, pose.position.y, pose.position.z);
+ }
+ if (location.locationFlags & XR_SPACE_LOCATION_ORIENTATION_TRACKED_BIT) {
+ flags.set_flag(XRHandTracker::HAND_JOINT_FLAG_ORIENTATION_TRACKED);
+ }
+ if (location.locationFlags & XR_SPACE_LOCATION_POSITION_TRACKED_BIT) {
+ flags.set_flag(XRHandTracker::HAND_JOINT_FLAG_POSITION_TRACKED);
+ }
+ if (location.locationFlags & XR_SPACE_VELOCITY_LINEAR_VALID_BIT) {
+ flags.set_flag(XRHandTracker::HAND_JOINT_FLAG_LINEAR_VELOCITY_VALID);
+ godot_tracker->set_hand_joint_linear_velocity((XRHandTracker::HandJoint)joint, Vector3(velocity.linearVelocity.x, velocity.linearVelocity.y, velocity.linearVelocity.z));
+ }
+ if (location.locationFlags & XR_SPACE_VELOCITY_ANGULAR_VALID_BIT) {
+ flags.set_flag(XRHandTracker::HAND_JOINT_FLAG_ANGULAR_VELOCITY_VALID);
+ godot_tracker->set_hand_joint_angular_velocity((XRHandTracker::HandJoint)joint, Vector3(velocity.angularVelocity.x, velocity.angularVelocity.y, velocity.angularVelocity.z));
+ }
+
+ godot_tracker->set_hand_joint_flags((XRHandTracker::HandJoint)joint, flags);
+ godot_tracker->set_hand_joint_transform((XRHandTracker::HandJoint)joint, transform);
+ godot_tracker->set_hand_joint_radius((XRHandTracker::HandJoint)joint, location.radius);
+
+ XRHandTracker::HandTrackingSource source = XRHandTracker::HAND_TRACKING_SOURCE_UNKNOWN;
+ if (data_source.dataSource == XR_HAND_TRACKING_DATA_SOURCE_UNOBSTRUCTED_EXT) {
+ source = XRHandTracker::HAND_TRACKING_SOURCE_UNOBSTRUCTED;
+ } else if (data_source.dataSource == XR_HAND_TRACKING_DATA_SOURCE_CONTROLLER_EXT) {
+ source = XRHandTracker::HAND_TRACKING_SOURCE_CONTROLLER;
+ }
+ godot_tracker->set_hand_tracking_source(source);
+ }
+ } else {
+ godot_tracker->set_has_tracking_data(false);
+ }
}
}
}
@@ -244,6 +310,8 @@ void OpenXRHandTrackingExtension::cleanup_hand_tracking() {
hand_trackers[i].is_initialized = false;
hand_trackers[i].hand_tracker = XR_NULL_HANDLE;
+
+ XRServer::get_singleton()->remove_hand_tracker(i == 0 ? "/user/left" : "/user/right");
}
}
}
diff --git a/modules/openxr/extensions/openxr_hand_tracking_extension.h b/modules/openxr/extensions/openxr_hand_tracking_extension.h
index 967538b377..f709bc05c2 100644
--- a/modules/openxr/extensions/openxr_hand_tracking_extension.h
+++ b/modules/openxr/extensions/openxr_hand_tracking_extension.h
@@ -34,6 +34,7 @@
#include "../util.h"
#include "core/math/quaternion.h"
#include "openxr_extension_wrapper.h"
+#include "servers/xr/xr_hand_tracker.h"
class OpenXRHandTrackingExtension : public OpenXRExtensionWrapper {
public:
@@ -52,6 +53,7 @@ public:
struct HandTracker {
bool is_initialized = false;
+ Ref<XRHandTracker> godot_tracker;
XrHandJointsMotionRangeEXT motion_range = XR_HAND_JOINTS_MOTION_RANGE_UNOBSTRUCTED_EXT;
HandTrackedSource source = OPENXR_SOURCE_UNKNOWN;
diff --git a/platform/android/doc_classes/EditorExportPlatformAndroid.xml b/platform/android/doc_classes/EditorExportPlatformAndroid.xml
index 64485afeb0..7fce5359ae 100644
--- a/platform/android/doc_classes/EditorExportPlatformAndroid.xml
+++ b/platform/android/doc_classes/EditorExportPlatformAndroid.xml
@@ -274,8 +274,7 @@
<member name="permissions/custom_permissions" type="PackedStringArray" setter="" getter="">
Array of custom permission strings.
</member>
- <member name="permissions/delete_cache_files" type="bool" setter="" getter="">
- Deprecated.
+ <member name="permissions/delete_cache_files" type="bool" setter="" getter="" deprecated="">
</member>
<member name="permissions/delete_packages" type="bool" setter="" getter="">
Allows an application to delete packages. See [url=https://developer.android.com/reference/android/Manifest.permission#DELETE_PACKAGES]DELETE_PACKAGES[/url].
@@ -310,8 +309,7 @@
<member name="permissions/get_package_size" type="bool" setter="" getter="">
Allows an application to find out the space used by any package. See [url=https://developer.android.com/reference/android/Manifest.permission#GET_PACKAGE_SIZE]GET_PACKAGE_SIZE[/url].
</member>
- <member name="permissions/get_tasks" type="bool" setter="" getter="">
- Deprecated in API level 21.
+ <member name="permissions/get_tasks" type="bool" setter="" getter="" deprecated="Deprecated in API level 21.">
</member>
<member name="permissions/get_top_activity_info" type="bool" setter="" getter="">
Allows an application to retrieve private information about the current top activity.
@@ -379,13 +377,11 @@
<member name="permissions/nfc" type="bool" setter="" getter="">
Allows applications to perform I/O operations over NFC. See [url=https://developer.android.com/reference/android/Manifest.permission#NFC]NFC[/url].
</member>
- <member name="permissions/persistent_activity" type="bool" setter="" getter="">
+ <member name="permissions/persistent_activity" type="bool" setter="" getter="" deprecated="Deprecated in API level 15.">
Allow an application to make its activities persistent.
- Deprecated in API level 15.
</member>
- <member name="permissions/process_outgoing_calls" type="bool" setter="" getter="">
+ <member name="permissions/process_outgoing_calls" type="bool" setter="" getter="" deprecated="Deprecated in API level 29.">
Allows an application to see the number being dialed during an outgoing call with the option to redirect the call to a different number or abort the call altogether. See [url=https://developer.android.com/reference/android/Manifest.permission#PROCESS_OUTGOING_CALLS]PROCESS_OUTGOING_CALLS[/url].
- Deprecated in API level 29.
</member>
<member name="permissions/read_calendar" type="bool" setter="" getter="">
Allows an application to read the user's calendar data. See [url=https://developer.android.com/reference/android/Manifest.permission#READ_CALENDAR]READ_CALENDAR[/url].
@@ -396,9 +392,8 @@
<member name="permissions/read_contacts" type="bool" setter="" getter="">
Allows an application to read the user's contacts data. See [url=https://developer.android.com/reference/android/Manifest.permission#READ_CONTACTS]READ_CONTACTS[/url].
</member>
- <member name="permissions/read_external_storage" type="bool" setter="" getter="">
+ <member name="permissions/read_external_storage" type="bool" setter="" getter="" deprecated="Deprecated in API level 33.">
Allows an application to read from external storage. See [url=https://developer.android.com/reference/android/Manifest.permission#READ_EXTERNAL_STORAGE]READ_EXTERNAL_STORAGE[/url].
- Deprecated in API level 33.
</member>
<member name="permissions/read_frame_buffer" type="bool" setter="" getter="">
Allows an application to take screen shots and more generally get access to the frame buffer data.
@@ -406,8 +401,7 @@
<member name="permissions/read_history_bookmarks" type="bool" setter="" getter="">
Allows an application to read (but not write) the user's browsing history and bookmarks.
</member>
- <member name="permissions/read_input_state" type="bool" setter="" getter="">
- Deprecated in API level 16.
+ <member name="permissions/read_input_state" type="bool" setter="" getter="" deprecated="Deprecated in API level 16.">
</member>
<member name="permissions/read_logs" type="bool" setter="" getter="">
Allows an application to read the low-level system log files. See [url=https://developer.android.com/reference/android/Manifest.permission#READ_LOGS]READ_LOGS[/url].
@@ -454,8 +448,7 @@
<member name="permissions/reorder_tasks" type="bool" setter="" getter="">
Allows an application to change the Z-order of tasks. See [url=https://developer.android.com/reference/android/Manifest.permission#REORDER_TASKS]REORDER_TASKS[/url].
</member>
- <member name="permissions/restart_packages" type="bool" setter="" getter="">
- Deprecated in API level 15.
+ <member name="permissions/restart_packages" type="bool" setter="" getter="" deprecated="Deprecated in API level 15.">
</member>
<member name="permissions/send_respond_via_message" type="bool" setter="" getter="">
Allows an application (Phone) to send a request to other applications to handle the respond-via-message action during incoming calls. See [url=https://developer.android.com/reference/android/Manifest.permission#SEND_RESPOND_VIA_MESSAGE]SEND_RESPOND_VIA_MESSAGE[/url].
@@ -484,8 +477,7 @@
<member name="permissions/set_pointer_speed" type="bool" setter="" getter="">
Allows low-level access to setting the pointer speed.
</member>
- <member name="permissions/set_preferred_applications" type="bool" setter="" getter="">
- Deprecated in API level 15.
+ <member name="permissions/set_preferred_applications" type="bool" setter="" getter="" deprecated="Deprecated in API level 15.">
</member>
<member name="permissions/set_process_limit" type="bool" setter="" getter="">
Allows an application to set the maximum number of (not needed) application processes that can be running. See [url=https://developer.android.com/reference/android/Manifest.permission#SET_PROCESS_LIMIT]SET_PROCESS_LIMIT[/url].
@@ -511,8 +503,7 @@
<member name="permissions/subscribed_feeds_read" type="bool" setter="" getter="">
Allows an application to allow access the subscribed feeds ContentProvider.
</member>
- <member name="permissions/subscribed_feeds_write" type="bool" setter="" getter="">
- Deprecated.
+ <member name="permissions/subscribed_feeds_write" type="bool" setter="" getter="" deprecated="">
</member>
<member name="permissions/system_alert_window" type="bool" setter="" getter="">
Allows an app to create windows using the type WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, shown on top of all other apps. See [url=https://developer.android.com/reference/android/Manifest.permission#SYSTEM_ALERT_WINDOW]SYSTEM_ALERT_WINDOW[/url].
@@ -520,8 +511,7 @@
<member name="permissions/transmit_ir" type="bool" setter="" getter="">
Allows using the device's IR transmitter, if available. See [url=https://developer.android.com/reference/android/Manifest.permission#TRANSMIT_IR]TRANSMIT_IR[/url].
</member>
- <member name="permissions/uninstall_shortcut" type="bool" setter="" getter="">
- Deprecated.
+ <member name="permissions/uninstall_shortcut" type="bool" setter="" getter="" deprecated="">
</member>
<member name="permissions/update_device_stats" type="bool" setter="" getter="">
Allows an application to update device statistics. See [url=https://developer.android.com/reference/android/Manifest.permission#UPDATE_DEVICE_STATS]UPDATE_DEVICE_STATS[/url].
diff --git a/platform/web/doc_classes/EditorExportPlatformWeb.xml b/platform/web/doc_classes/EditorExportPlatformWeb.xml
index 75125e2708..c6e506ab9f 100644
--- a/platform/web/doc_classes/EditorExportPlatformWeb.xml
+++ b/platform/web/doc_classes/EditorExportPlatformWeb.xml
@@ -28,9 +28,8 @@
The custom HTML page that wraps the exported web build. If left empty, the default HTML shell is used.
For more information, see the [url=$DOCS_URL/tutorials/platform/web/customizing_html5_shell.html]Customizing HTML5 Shell[/url] tutorial.
</member>
- <member name="html/experimental_virtual_keyboard" type="bool" setter="" getter="">
+ <member name="html/experimental_virtual_keyboard" type="bool" setter="" getter="" experimental="">
If [code]true[/code], embeds support for a virtual keyboard into the web page, which is shown when necessary on touchscreen devices.
- [b]Warning:[/b] This feature is experimental and may be changed in a future release.
</member>
<member name="html/export_icon" type="bool" setter="" getter="">
If [code]true[/code], the project icon will be used as the favicon for this application's web page.
diff --git a/scene/3d/xr_hand_modifier_3d.cpp b/scene/3d/xr_hand_modifier_3d.cpp
new file mode 100644
index 0000000000..3c38519713
--- /dev/null
+++ b/scene/3d/xr_hand_modifier_3d.cpp
@@ -0,0 +1,309 @@
+/**************************************************************************/
+/* xr_hand_modifier_3d.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "xr_hand_modifier_3d.h"
+
+#include "scene/3d/skeleton_3d.h"
+#include "servers/xr/xr_pose.h"
+#include "servers/xr_server.h"
+
+void XRHandModifier3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_hand_tracker", "tracker_name"), &XRHandModifier3D::set_hand_tracker);
+ ClassDB::bind_method(D_METHOD("get_hand_tracker"), &XRHandModifier3D::get_hand_tracker);
+
+ ClassDB::bind_method(D_METHOD("set_target", "target"), &XRHandModifier3D::set_target);
+ ClassDB::bind_method(D_METHOD("get_target"), &XRHandModifier3D::get_target);
+
+ ClassDB::bind_method(D_METHOD("set_bone_update", "bone_update"), &XRHandModifier3D::set_bone_update);
+ ClassDB::bind_method(D_METHOD("get_bone_update"), &XRHandModifier3D::get_bone_update);
+
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "hand_tracker", PROPERTY_HINT_ENUM_SUGGESTION, "/user/left,/user/right"), "set_hand_tracker", "get_hand_tracker");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton3D"), "set_target", "get_target");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "bone_update", PROPERTY_HINT_ENUM, "Full,Rotation Only"), "set_bone_update", "get_bone_update");
+
+ BIND_ENUM_CONSTANT(BONE_UPDATE_FULL);
+ BIND_ENUM_CONSTANT(BONE_UPDATE_ROTATION_ONLY);
+ BIND_ENUM_CONSTANT(BONE_UPDATE_MAX);
+}
+
+void XRHandModifier3D::set_hand_tracker(const StringName &p_tracker_name) {
+ tracker_name = p_tracker_name;
+}
+
+StringName XRHandModifier3D::get_hand_tracker() const {
+ return tracker_name;
+}
+
+void XRHandModifier3D::set_target(const NodePath &p_target) {
+ target = p_target;
+
+ if (is_inside_tree()) {
+ _get_joint_data();
+ }
+}
+
+NodePath XRHandModifier3D::get_target() const {
+ return target;
+}
+
+void XRHandModifier3D::set_bone_update(BoneUpdate p_bone_update) {
+ ERR_FAIL_INDEX(p_bone_update, BONE_UPDATE_MAX);
+ bone_update = p_bone_update;
+}
+
+XRHandModifier3D::BoneUpdate XRHandModifier3D::get_bone_update() const {
+ return bone_update;
+}
+
+Skeleton3D *XRHandModifier3D::get_skeleton() {
+ if (!has_node(target)) {
+ return nullptr;
+ }
+
+ Node *node = get_node(target);
+ if (!node) {
+ return nullptr;
+ }
+
+ Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(node);
+ return skeleton;
+}
+
+void XRHandModifier3D::_get_joint_data() {
+ // Table of bone names for different rig types.
+ static const String bone_names[XRHandTracker::HAND_JOINT_MAX] = {
+ "Palm",
+ "Hand",
+ "ThumbMetacarpal",
+ "ThumbProximal",
+ "ThumbDistal",
+ "ThumbTip",
+ "IndexMetacarpal",
+ "IndexProximal",
+ "IndexIntermediate",
+ "IndexDistal",
+ "IndexTip",
+ "MiddleMetacarpal",
+ "MiddleProximal",
+ "MiddleIntermediate",
+ "MiddleDistal",
+ "MiddleTip",
+ "RingMetacarpal",
+ "RingProximal",
+ "RingIntermediate",
+ "RingDistal",
+ "RingTip",
+ "LittleMetacarpal",
+ "LittleProximal",
+ "LittleIntermediate",
+ "LittleDistal",
+ "LittleTip",
+ };
+
+ static const String bone_name_format[2] = {
+ "Left<bone>",
+ "Right<bone>",
+ };
+
+ // reset JIC
+ for (int i = 0; i < XRHandTracker::HAND_JOINT_MAX; i++) {
+ joints[i].bone = -1;
+ joints[i].parent_joint = -1;
+ }
+
+ Skeleton3D *skeleton = get_skeleton();
+ if (!skeleton) {
+ return;
+ }
+
+ XRServer *xr_server = XRServer::get_singleton();
+ if (!xr_server) {
+ return;
+ }
+
+ Ref<XRHandTracker> tracker = xr_server->get_hand_tracker(tracker_name);
+ if (tracker.is_null()) {
+ return;
+ }
+
+ XRHandTracker::Hand hand = tracker->get_hand();
+
+ // Find the skeleton-bones associated with each joint.
+ int bones[XRHandTracker::HAND_JOINT_MAX];
+ for (int i = 0; i < XRHandTracker::HAND_JOINT_MAX; i++) {
+ // Construct the expected bone name.
+ String bone_name = bone_name_format[hand].replace("<bone>", bone_names[i]);
+
+ // Find the skeleton bone.
+ bones[i] = skeleton->find_bone(bone_name);
+ if (bones[i] == -1) {
+ WARN_PRINT(vformat("Couldn't obtain bone for %s", bone_name));
+ }
+ }
+
+ // Assemble the joint relationship to the available skeleton bones.
+ for (int i = 0; i < XRHandTracker::HAND_JOINT_MAX; i++) {
+ // Get the skeleton bone (skip if not found).
+ const int bone = bones[i];
+ if (bone == -1) {
+ continue;
+ }
+
+ // Find the parent skeleton-bone.
+ const int parent_bone = skeleton->get_bone_parent(bone);
+ if (parent_bone == -1) {
+ // If no parent skeleton-bone exists then drive this relative to palm joint.
+ joints[i].bone = bone;
+ joints[i].parent_joint = XRHandTracker::HAND_JOINT_PALM;
+ continue;
+ }
+
+ // Find the joint associated with the parent skeleton-bone.
+ for (int j = 0; j < XRHandTracker::HAND_JOINT_MAX; ++j) {
+ if (bones[j] == parent_bone) {
+ // If a parent joint is found then drive this bone relative to it.
+ joints[i].bone = bone;
+ joints[i].parent_joint = j;
+ break;
+ }
+ }
+ }
+}
+
+void XRHandModifier3D::_update_skeleton() {
+ Skeleton3D *skeleton = get_skeleton();
+ if (!skeleton) {
+ return;
+ }
+
+ XRServer *xr_server = XRServer::get_singleton();
+ if (!xr_server) {
+ return;
+ }
+
+ Ref<XRHandTracker> tracker = xr_server->get_hand_tracker(tracker_name);
+ if (tracker.is_null()) {
+ return;
+ }
+
+ // We cache our transforms so we can quickly calculate local transforms.
+ bool has_valid_data[XRHandTracker::HAND_JOINT_MAX];
+ Transform3D transforms[XRHandTracker::HAND_JOINT_MAX];
+ Transform3D inv_transforms[XRHandTracker::HAND_JOINT_MAX];
+
+ const float ws = xr_server->get_world_scale();
+
+ if (tracker->get_has_tracking_data()) {
+ for (int joint = 0; joint < XRHandTracker::HAND_JOINT_MAX; joint++) {
+ BitField<XRHandTracker::HandJointFlags> flags = tracker->get_hand_joint_flags((XRHandTracker::HandJoint)joint);
+ has_valid_data[joint] = flags.has_flag(XRHandTracker::HAND_JOINT_FLAG_ORIENTATION_VALID);
+
+ if (has_valid_data[joint]) {
+ transforms[joint] = tracker->get_hand_joint_transform((XRHandTracker::HandJoint)joint);
+ transforms[joint].origin *= ws;
+ inv_transforms[joint] = transforms[joint].inverse();
+ }
+ }
+
+ if (has_valid_data[XRHandTracker::HAND_JOINT_PALM]) {
+ for (int joint = 0; joint < XRHandTracker::HAND_JOINT_MAX; joint++) {
+ // Get the skeleton bone (skip if none).
+ const int bone = joints[joint].bone;
+ if (bone == -1) {
+ continue;
+ }
+
+ // Calculate the relative relationship to the parent bone joint.
+ const int parent_joint = joints[joint].parent_joint;
+ const Transform3D relative_transform = inv_transforms[parent_joint] * transforms[joint];
+
+ // Update the bone position if enabled by update mode.
+ if (bone_update == BONE_UPDATE_FULL) {
+ skeleton->set_bone_pose_position(joints[joint].bone, relative_transform.origin);
+ }
+
+ // Always update the bone rotation.
+ skeleton->set_bone_pose_rotation(joints[joint].bone, Quaternion(relative_transform.basis));
+ }
+
+ // Transform to the skeleton pose.
+ set_transform(transforms[XRHandTracker::HAND_JOINT_PALM]);
+
+ set_visible(true);
+ } else {
+ set_visible(false);
+ }
+ } else {
+ set_visible(false);
+ }
+}
+
+void XRHandModifier3D::_tracker_changed(StringName p_tracker_name, const Ref<XRHandTracker> &p_tracker) {
+ if (tracker_name == p_tracker_name) {
+ _get_joint_data();
+ }
+}
+
+void XRHandModifier3D::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+ XRServer *xr_server = XRServer::get_singleton();
+ if (xr_server) {
+ xr_server->connect("hand_tracker_added", callable_mp(this, &XRHandModifier3D::_tracker_changed));
+ xr_server->connect("hand_tracker_updated", callable_mp(this, &XRHandModifier3D::_tracker_changed));
+ xr_server->connect("hand_tracker_removed", callable_mp(this, &XRHandModifier3D::_tracker_changed).bind(Ref<XRHandTracker>()));
+ }
+
+ _get_joint_data();
+
+ set_process_internal(true);
+ } break;
+ case NOTIFICATION_EXIT_TREE: {
+ XRServer *xr_server = XRServer::get_singleton();
+ if (xr_server) {
+ xr_server->disconnect("hand_tracker_added", callable_mp(this, &XRHandModifier3D::_tracker_changed));
+ xr_server->disconnect("hand_tracker_updated", callable_mp(this, &XRHandModifier3D::_tracker_changed));
+ xr_server->disconnect("hand_tracker_removed", callable_mp(this, &XRHandModifier3D::_tracker_changed).bind(Ref<XRHandTracker>()));
+ }
+
+ set_process_internal(false);
+
+ for (int i = 0; i < XRHandTracker::HAND_JOINT_MAX; i++) {
+ joints[i].bone = -1;
+ joints[i].parent_joint = -1;
+ }
+ } break;
+ case NOTIFICATION_INTERNAL_PROCESS: {
+ _update_skeleton();
+ } break;
+ default: {
+ } break;
+ }
+}
diff --git a/scene/3d/xr_hand_modifier_3d.h b/scene/3d/xr_hand_modifier_3d.h
new file mode 100644
index 0000000000..2bc30d42d4
--- /dev/null
+++ b/scene/3d/xr_hand_modifier_3d.h
@@ -0,0 +1,87 @@
+/**************************************************************************/
+/* xr_hand_modifier_3d.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 XR_HAND_MODIFIER_3D_H
+#define XR_HAND_MODIFIER_3D_H
+
+#include "scene/3d/node_3d.h"
+#include "servers/xr/xr_hand_tracker.h"
+
+class Skeleton3D;
+
+/**
+ The XRHandModifier3D node drives a hand skeleton using hand tracking
+ data from an XRHandTracking instance.
+ */
+
+class XRHandModifier3D : public Node3D {
+ GDCLASS(XRHandModifier3D, Node3D);
+
+public:
+ enum BoneUpdate {
+ BONE_UPDATE_FULL,
+ BONE_UPDATE_ROTATION_ONLY,
+ BONE_UPDATE_MAX
+ };
+
+ void set_hand_tracker(const StringName &p_tracker_name);
+ StringName get_hand_tracker() const;
+
+ void set_target(const NodePath &p_target);
+ NodePath get_target() const;
+
+ void set_bone_update(BoneUpdate p_bone_update);
+ BoneUpdate get_bone_update() const;
+
+ void _notification(int p_what);
+
+protected:
+ static void _bind_methods();
+
+private:
+ struct JointData {
+ int bone = -1;
+ int parent_joint = -1;
+ };
+
+ StringName tracker_name = "/user/left";
+ NodePath target;
+ BoneUpdate bone_update = BONE_UPDATE_FULL;
+ JointData joints[XRHandTracker::HAND_JOINT_MAX];
+
+ Skeleton3D *get_skeleton();
+ void _get_joint_data();
+ void _update_skeleton();
+ void _tracker_changed(StringName p_tracker_name, const Ref<XRHandTracker> &p_tracker);
+};
+
+VARIANT_ENUM_CAST(XRHandModifier3D::BoneUpdate)
+
+#endif // XR_HAND_MODIFIER_3D_H
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index 9d24595916..cdb091cb32 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -36,6 +36,7 @@
#include "core/os/keyboard.h"
#include "core/os/os.h"
#include "scene/gui/color_mode.h"
+#include "scene/gui/margin_container.h"
#include "scene/resources/image_texture.h"
#include "scene/resources/style_box_flat.h"
#include "scene/resources/style_box_texture.h"
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index e69f713798..42100bf9fa 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -213,8 +213,8 @@ Size2 PopupMenu::_get_item_icon_size(int p_idx) const {
}
Size2 PopupMenu::_get_contents_minimum_size() const {
- Size2 minsize = theme_cache.panel_style->get_minimum_size(); // Accounts for margin in the margin container
- minsize.x += scroll_container->get_v_scroll_bar()->get_size().width * 2; // Adds a buffer so that the scrollbar does not render over the top of content
+ Size2 minsize = theme_cache.panel_style->get_minimum_size();
+ minsize.width += scroll_container->get_v_scroll_bar()->get_size().width;
float max_w = 0.0;
float icon_w = 0.0;
@@ -304,16 +304,11 @@ int PopupMenu::_get_items_total_height() const {
}
int PopupMenu::_get_mouse_over(const Point2 &p_over) const {
- if (p_over.x < 0 || p_over.x >= get_size().width) {
+ if (p_over.x < 0 || p_over.x >= get_size().width || p_over.y < theme_cache.panel_style->get_margin(Side::SIDE_TOP)) {
return -1;
}
- // Accounts for margin in the margin container
- Point2 ofs = theme_cache.panel_style->get_offset();
-
- if (ofs.y > p_over.y) {
- return -1;
- }
+ Point2 ofs;
for (int i = 0; i < items.size(); i++) {
ofs.y += theme_cache.v_separation;
@@ -570,32 +565,39 @@ void PopupMenu::_input_from_window_internal(const Ref<InputEvent> &p_event) {
if (scroll_container->get_v_scroll_bar()->is_visible_in_tree()) {
if (is_layout_rtl()) {
item_clickable_area.position.x += scroll_container->get_v_scroll_bar()->get_size().width;
- } else {
- item_clickable_area.size.width -= scroll_container->get_v_scroll_bar()->get_size().width;
}
+ item_clickable_area.size.width -= scroll_container->get_v_scroll_bar()->get_size().width;
}
Ref<InputEventMouseButton> b = p_event;
if (b.is_valid()) {
- if (!item_clickable_area.has_point(b->get_position())) {
- return;
- }
-
MouseButton button_idx = b->get_button_index();
- if (!b->is_pressed()) {
- // Activate the item on release of either the left mouse button or
- // any mouse button held down when the popup was opened.
- // This allows for opening the popup and triggering an action in a single mouse click.
- if (button_idx == MouseButton::LEFT || initial_button_mask.has_flag(mouse_button_to_mask(button_idx))) {
+ // Activate the item on release of either the left mouse button or
+ // any mouse button held down when the popup was opened.
+ // This allows for opening the popup and triggering an action in a single mouse click.
+ if (button_idx == MouseButton::LEFT || initial_button_mask.has_flag(mouse_button_to_mask(button_idx))) {
+ if (b->is_pressed()) {
+ is_scrolling = is_layout_rtl() ? b->get_position().x < item_clickable_area.position.x : b->get_position().x > item_clickable_area.size.width;
+
+ if (!item_clickable_area.has_point(b->get_position())) {
+ return;
+ }
+ _mouse_over_update(b->get_position());
+ } else {
+ if (is_scrolling) {
+ is_scrolling = false;
+ return;
+ }
bool was_during_grabbed_click = during_grabbed_click;
during_grabbed_click = false;
initial_button_mask.clear();
+ if (!item_clickable_area.has_point(b->get_position())) {
+ return;
+ }
// Disable clicks under a time threshold to avoid selection right when opening the popup.
- uint64_t now = OS::get_singleton()->get_ticks_msec();
- uint64_t diff = now - popup_time_msec;
- if (diff < 150) {
+ if (was_during_grabbed_click && OS::get_singleton()->get_ticks_msec() - popup_time_msec < 150) {
return;
}
@@ -638,25 +640,7 @@ void PopupMenu::_input_from_window_internal(const Ref<InputEvent> &p_event) {
if (!item_clickable_area.has_point(m->get_position())) {
return;
}
-
- int over = _get_mouse_over(m->get_position());
- int id = (over < 0 || items[over].separator || items[over].disabled) ? -1 : (items[over].id >= 0 ? items[over].id : over);
-
- if (id < 0) {
- mouse_over = -1;
- control->queue_redraw();
- return;
- }
-
- if (items[over].submenu && submenu_over != over) {
- submenu_over = over;
- submenu_timer->start();
- }
-
- if (over != mouse_over) {
- mouse_over = over;
- control->queue_redraw();
- }
+ _mouse_over_update(m->get_position());
}
Ref<InputEventKey> k = p_event;
@@ -700,14 +684,31 @@ void PopupMenu::_input_from_window_internal(const Ref<InputEvent> &p_event) {
}
}
+void PopupMenu::_mouse_over_update(const Point2 &p_over) {
+ int over = _get_mouse_over(p_over);
+ int id = (over < 0 || items[over].separator || items[over].disabled) ? -1 : (items[over].id >= 0 ? items[over].id : over);
+
+ if (id < 0) {
+ mouse_over = -1;
+ control->queue_redraw();
+ return;
+ }
+
+ if (!is_scrolling && items[over].submenu && submenu_over != over) {
+ submenu_over = over;
+ submenu_timer->start();
+ }
+
+ if (over != mouse_over) {
+ mouse_over = over;
+ control->queue_redraw();
+ }
+}
+
void PopupMenu::_draw_items() {
control->set_custom_minimum_size(Size2(0, _get_items_total_height()));
RID ci = control->get_canvas_item();
- Size2 margin_size;
- margin_size.width = margin_container->get_margin_size(SIDE_LEFT) + margin_container->get_margin_size(SIDE_RIGHT);
- margin_size.height = margin_container->get_margin_size(SIDE_TOP) + margin_container->get_margin_size(SIDE_BOTTOM);
-
// Space between the item content and the sides of popup menu.
bool rtl = control->is_layout_rtl();
// In Item::checkable_type enum order (less the non-checkable member), with disabled repeated at the end.
@@ -720,8 +721,7 @@ void PopupMenu::_draw_items() {
submenu = theme_cache.submenu;
}
- float scroll_width = scroll_container->get_v_scroll_bar()->is_visible_in_tree() ? scroll_container->get_v_scroll_bar()->get_size().width : 0;
- float display_width = control->get_size().width - scroll_width;
+ float display_width = control->get_size().width;
// Find the widest icon and whether any items have a checkbox, and store the offsets for each.
float icon_ofs = 0.0;
@@ -765,11 +765,7 @@ void PopupMenu::_draw_items() {
float h = _get_item_height(i);
if (i == mouse_over) {
- if (rtl) {
- theme_cache.hover_style->draw(ci, Rect2(item_ofs + Point2(scroll_width, -theme_cache.v_separation / 2), Size2(display_width, h + theme_cache.v_separation)));
- } else {
- theme_cache.hover_style->draw(ci, Rect2(item_ofs + Point2(0, -theme_cache.v_separation / 2), Size2(display_width, h + theme_cache.v_separation)));
- }
+ theme_cache.hover_style->draw(ci, Rect2(item_ofs + Point2(0, -theme_cache.v_separation / 2), Size2(display_width, h + theme_cache.v_separation)));
}
String text = items[i].xl_text;
@@ -851,7 +847,7 @@ void PopupMenu::_draw_items() {
// Submenu arrow on right hand side.
if (items[i].submenu) {
if (rtl) {
- submenu->draw(ci, Point2(scroll_width + theme_cache.panel_style->get_margin(SIDE_LEFT) + theme_cache.item_end_padding, item_ofs.y + Math::floor(h - submenu->get_height()) / 2), icon_color);
+ submenu->draw(ci, Point2(theme_cache.panel_style->get_margin(SIDE_LEFT) + theme_cache.item_end_padding, item_ofs.y + Math::floor(h - submenu->get_height()) / 2), icon_color);
} else {
submenu->draw(ci, Point2(display_width - theme_cache.panel_style->get_margin(SIDE_RIGHT) - submenu->get_width() - theme_cache.item_end_padding, item_ofs.y + Math::floor(h - submenu->get_height()) / 2), icon_color);
}
@@ -888,7 +884,7 @@ void PopupMenu::_draw_items() {
// Accelerator / Shortcut
if (items[i].accel != Key::NONE || (items[i].shortcut.is_valid() && items[i].shortcut->has_valid_event())) {
if (rtl) {
- item_ofs.x = scroll_width + theme_cache.panel_style->get_margin(SIDE_LEFT) + theme_cache.item_end_padding;
+ item_ofs.x = theme_cache.panel_style->get_margin(SIDE_LEFT) + theme_cache.item_end_padding;
} else {
item_ofs.x = display_width - theme_cache.panel_style->get_margin(SIDE_RIGHT) - items[i].accel_text_buf->get_size().x - theme_cache.item_end_padding;
}
@@ -907,11 +903,6 @@ void PopupMenu::_draw_items() {
}
}
-void PopupMenu::_draw_background() {
- RID ci2 = margin_container->get_canvas_item();
- theme_cache.panel_style->draw(ci2, Rect2(Point2(), margin_container->get_size()));
-}
-
void PopupMenu::_minimum_lifetime_timeout() {
close_allowed = true;
// If the mouse still isn't in this popup after timer expires, close.
@@ -1023,7 +1014,11 @@ void PopupMenu::_notification(int p_what) {
}
} break;
- case NOTIFICATION_THEME_CHANGED:
+ case NOTIFICATION_THEME_CHANGED: {
+ scroll_container->add_theme_style_override("panel", theme_cache.panel_style);
+
+ [[fallthrough]];
+ }
case Control::NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
case NOTIFICATION_TRANSLATION_CHANGED: {
DisplayServer *ds = DisplayServer::get_singleton();
@@ -1169,14 +1164,6 @@ void PopupMenu::_notification(int p_what) {
if (!is_embedded()) {
set_process_internal(true);
}
-
- // Set margin on the margin container
- margin_container->begin_bulk_theme_override();
- margin_container->add_theme_constant_override("margin_left", theme_cache.panel_style->get_margin(Side::SIDE_LEFT));
- margin_container->add_theme_constant_override("margin_top", theme_cache.panel_style->get_margin(Side::SIDE_TOP));
- margin_container->add_theme_constant_override("margin_right", theme_cache.panel_style->get_margin(Side::SIDE_RIGHT));
- margin_container->add_theme_constant_override("margin_bottom", theme_cache.panel_style->get_margin(Side::SIDE_BOTTOM));
- margin_container->end_bulk_theme_override();
}
} break;
}
@@ -2800,16 +2787,11 @@ void PopupMenu::popup(const Rect2i &p_bounds) {
}
PopupMenu::PopupMenu() {
- // Margin Container
- margin_container = memnew(MarginContainer);
- margin_container->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
- add_child(margin_container, false, INTERNAL_MODE_FRONT);
- margin_container->connect("draw", callable_mp(this, &PopupMenu::_draw_background));
-
// Scroll Container
scroll_container = memnew(ScrollContainer);
+ scroll_container->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
scroll_container->set_clip_contents(true);
- margin_container->add_child(scroll_container);
+ add_child(scroll_container, false, INTERNAL_MODE_FRONT);
// The control which will display the items
control = memnew(Control);
diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h
index 9f4d28e95d..492084b43f 100644
--- a/scene/gui/popup_menu.h
+++ b/scene/gui/popup_menu.h
@@ -32,7 +32,6 @@
#define POPUP_MENU_H
#include "core/input/shortcut.h"
-#include "scene/gui/margin_container.h"
#include "scene/gui/popup.h"
#include "scene/gui/scroll_container.h"
#include "scene/property_list_helper.h"
@@ -110,10 +109,12 @@ class PopupMenu : public Popup {
Vector<Item> items;
BitField<MouseButtonMask> initial_button_mask;
bool during_grabbed_click = false;
+ bool is_scrolling = false;
int mouse_over = -1;
int submenu_over = -1;
String _get_accel_text(const Item &p_item) const;
int _get_mouse_over(const Point2 &p_over) const;
+ void _mouse_over_update(const Point2 &p_over);
virtual Size2 _get_contents_minimum_size() const override;
int _get_item_height(int p_idx) const;
@@ -142,7 +143,6 @@ class PopupMenu : public Popup {
uint64_t search_time_msec = 0;
String search_string = "";
- MarginContainer *margin_container = nullptr;
ScrollContainer *scroll_container = nullptr;
Control *control = nullptr;
@@ -195,7 +195,6 @@ class PopupMenu : public Popup {
} theme_cache;
void _draw_items();
- void _draw_background();
void _minimum_lifetime_timeout();
void _close_pressed();
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index a86a685fc3..b582576412 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -4778,10 +4778,10 @@ void Viewport::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "scaling_3d_scale", PROPERTY_HINT_RANGE, "0.25,2.0,0.01"), "set_scaling_3d_scale", "get_scaling_3d_scale");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "texture_mipmap_bias", PROPERTY_HINT_RANGE, "-2,2,0.001"), "set_texture_mipmap_bias", "get_texture_mipmap_bias");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fsr_sharpness", PROPERTY_HINT_RANGE, "0,2,0.1"), "set_fsr_sharpness", "get_fsr_sharpness");
-#endif
ADD_GROUP("Variable Rate Shading", "vrs_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "vrs_mode", PROPERTY_HINT_ENUM, "Disabled,Texture,Depth buffer,XR"), "set_vrs_mode", "get_vrs_mode");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "vrs_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_vrs_texture", "get_vrs_texture");
+#endif
ADD_GROUP("Canvas Items", "canvas_item_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "canvas_item_default_texture_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,Linear Mipmap,Nearest Mipmap"), "set_default_canvas_item_texture_filter", "get_default_canvas_item_texture_filter");
ADD_PROPERTY(PropertyInfo(Variant::INT, "canvas_item_default_texture_repeat", PROPERTY_HINT_ENUM, "Disabled,Enabled,Mirror"), "set_default_canvas_item_texture_repeat", "get_default_canvas_item_texture_repeat");
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 18e96454ba..c23c66b961 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -147,25 +147,19 @@
#include "scene/resources/audio_stream_wav.h"
#include "scene/resources/bit_map.h"
#include "scene/resources/bone_map.h"
-#include "scene/resources/box_shape_3d.h"
#include "scene/resources/camera_attributes.h"
#include "scene/resources/camera_texture.h"
#include "scene/resources/capsule_shape_2d.h"
-#include "scene/resources/capsule_shape_3d.h"
#include "scene/resources/circle_shape_2d.h"
#include "scene/resources/compositor.h"
#include "scene/resources/compressed_texture.h"
#include "scene/resources/concave_polygon_shape_2d.h"
-#include "scene/resources/concave_polygon_shape_3d.h"
#include "scene/resources/convex_polygon_shape_2d.h"
-#include "scene/resources/convex_polygon_shape_3d.h"
#include "scene/resources/curve_texture.h"
-#include "scene/resources/cylinder_shape_3d.h"
#include "scene/resources/environment.h"
#include "scene/resources/font.h"
#include "scene/resources/gradient.h"
#include "scene/resources/gradient_texture.h"
-#include "scene/resources/height_map_shape_3d.h"
#include "scene/resources/image_texture.h"
#include "scene/resources/immediate_mesh.h"
#include "scene/resources/label_settings.h"
@@ -183,12 +177,10 @@
#include "scene/resources/placeholder_textures.h"
#include "scene/resources/polygon_path_finder.h"
#include "scene/resources/portable_compressed_texture.h"
-#include "scene/resources/primitive_meshes.h"
#include "scene/resources/rectangle_shape_2d.h"
#include "scene/resources/resource_format_text.h"
#include "scene/resources/segment_shape_2d.h"
#include "scene/resources/separation_ray_shape_2d.h"
-#include "scene/resources/separation_ray_shape_3d.h"
#include "scene/resources/shader_include.h"
#include "scene/resources/skeleton_modification_2d.h"
#include "scene/resources/skeleton_modification_2d_ccdik.h"
@@ -202,7 +194,6 @@
#include "scene/resources/skeleton_profile.h"
#include "scene/resources/sky.h"
#include "scene/resources/sky_material.h"
-#include "scene/resources/sphere_shape_3d.h"
#include "scene/resources/style_box.h"
#include "scene/resources/style_box_flat.h"
#include "scene/resources/style_box_line.h"
@@ -222,9 +213,7 @@
#include "scene/resources/visual_shader_particle_nodes.h"
#include "scene/resources/visual_shader_sdf_nodes.h"
#include "scene/resources/world_2d.h"
-#include "scene/resources/world_3d.h"
#include "scene/resources/world_boundary_shape_2d.h"
-#include "scene/resources/world_boundary_shape_3d.h"
#include "scene/scene_string_names.h"
#include "scene/theme/theme_db.h"
@@ -274,12 +263,23 @@
#include "scene/3d/voxel_gi.h"
#include "scene/3d/world_environment.h"
#include "scene/3d/xr_face_modifier_3d.h"
+#include "scene/3d/xr_hand_modifier_3d.h"
#include "scene/3d/xr_nodes.h"
#include "scene/animation/root_motion_view.h"
-#include "scene/resources/environment.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/fog_material.h"
+#include "scene/resources/height_map_shape_3d.h"
#include "scene/resources/importer_mesh.h"
#include "scene/resources/mesh_library.h"
+#include "scene/resources/primitive_meshes.h"
+#include "scene/resources/separation_ray_shape_3d.h"
+#include "scene/resources/sphere_shape_3d.h"
+#include "scene/resources/world_3d.h"
+#include "scene/resources/world_boundary_shape_3d.h"
#endif // _3D_DISABLED
static Ref<ResourceFormatSaverText> resource_saver_text;
@@ -523,6 +523,7 @@ void register_scene_types() {
GDREGISTER_CLASS(XRController3D);
GDREGISTER_CLASS(XRAnchor3D);
GDREGISTER_CLASS(XROrigin3D);
+ GDREGISTER_CLASS(XRHandModifier3D);
GDREGISTER_CLASS(XRFaceModifier3D);
GDREGISTER_CLASS(MeshInstance3D);
GDREGISTER_CLASS(OccluderInstance3D);
@@ -864,12 +865,12 @@ void register_scene_types() {
GDREGISTER_CLASS(WorldBoundaryShape3D);
GDREGISTER_CLASS(ConvexPolygonShape3D);
GDREGISTER_CLASS(ConcavePolygonShape3D);
+ GDREGISTER_CLASS(World3D);
OS::get_singleton()->yield(); // may take time to init
#endif // _3D_DISABLED
GDREGISTER_CLASS(PhysicsMaterial);
- GDREGISTER_CLASS(World3D);
GDREGISTER_CLASS(Compositor);
GDREGISTER_CLASS(Environment);
GDREGISTER_VIRTUAL_CLASS(CameraAttributes);
diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp
index 0abf878659..dcad58e6e2 100644
--- a/scene/resources/mesh.cpp
+++ b/scene/resources/mesh.cpp
@@ -2278,8 +2278,10 @@ void ArrayMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("surface_find_by_name", "name"), &ArrayMesh::surface_find_by_name);
ClassDB::bind_method(D_METHOD("surface_set_name", "surf_idx", "name"), &ArrayMesh::surface_set_name);
ClassDB::bind_method(D_METHOD("surface_get_name", "surf_idx"), &ArrayMesh::surface_get_name);
+#ifndef _3D_DISABLED
ClassDB::bind_method(D_METHOD("create_trimesh_shape"), &ArrayMesh::create_trimesh_shape);
ClassDB::bind_method(D_METHOD("create_convex_shape", "clean", "simplify"), &ArrayMesh::create_convex_shape, DEFVAL(true), DEFVAL(false));
+#endif // _3D_DISABLED
ClassDB::bind_method(D_METHOD("create_outline", "margin"), &ArrayMesh::create_outline);
ClassDB::bind_method(D_METHOD("regen_normal_maps"), &ArrayMesh::regen_normal_maps);
ClassDB::set_method_flags(get_class_static(), _scs_create("regen_normal_maps"), METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR);
diff --git a/servers/register_server_types.cpp b/servers/register_server_types.cpp
index 66843aaad8..380500769c 100644
--- a/servers/register_server_types.cpp
+++ b/servers/register_server_types.cpp
@@ -77,6 +77,7 @@
#include "text/text_server_extension.h"
#include "text_server.h"
#include "xr/xr_face_tracker.h"
+#include "xr/xr_hand_tracker.h"
#include "xr/xr_interface.h"
#include "xr/xr_interface_extension.h"
#include "xr/xr_positional_tracker.h"
@@ -173,6 +174,7 @@ void register_server_types() {
GDREGISTER_ABSTRACT_CLASS(RenderingDevice);
GDREGISTER_ABSTRACT_CLASS(XRInterface);
+ GDREGISTER_CLASS(XRHandTracker);
GDREGISTER_CLASS(XRInterfaceExtension); // can't register this as virtual because we need a creation function for our extensions.
GDREGISTER_CLASS(XRPose);
GDREGISTER_CLASS(XRPositionalTracker);
@@ -354,7 +356,9 @@ void register_server_singletons() {
Engine::get_singleton()->add_singleton(Engine::Singleton("RenderingServer", RenderingServer::get_singleton(), "RenderingServer"));
Engine::get_singleton()->add_singleton(Engine::Singleton("AudioServer", AudioServer::get_singleton(), "AudioServer"));
Engine::get_singleton()->add_singleton(Engine::Singleton("PhysicsServer2D", PhysicsServer2D::get_singleton(), "PhysicsServer2D"));
+#ifndef _3D_DISABLED
Engine::get_singleton()->add_singleton(Engine::Singleton("PhysicsServer3D", PhysicsServer3D::get_singleton(), "PhysicsServer3D"));
+#endif // _3D_DISABLED
Engine::get_singleton()->add_singleton(Engine::Singleton("NavigationServer2D", NavigationServer2D::get_singleton(), "NavigationServer2D"));
Engine::get_singleton()->add_singleton(Engine::Singleton("NavigationServer3D", NavigationServer3D::get_singleton(), "NavigationServer3D"));
Engine::get_singleton()->add_singleton(Engine::Singleton("XRServer", XRServer::get_singleton(), "XRServer"));
diff --git a/servers/xr/xr_hand_tracker.cpp b/servers/xr/xr_hand_tracker.cpp
new file mode 100644
index 0000000000..8cc2d5f7d2
--- /dev/null
+++ b/servers/xr/xr_hand_tracker.cpp
@@ -0,0 +1,179 @@
+/**************************************************************************/
+/* xr_hand_tracker.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "xr_hand_tracker.h"
+
+void XRHandTracker::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_hand", "hand"), &XRHandTracker::set_hand);
+ ClassDB::bind_method(D_METHOD("get_hand"), &XRHandTracker::get_hand);
+
+ ClassDB::bind_method(D_METHOD("set_has_tracking_data", "has_data"), &XRHandTracker::set_has_tracking_data);
+ ClassDB::bind_method(D_METHOD("get_has_tracking_data"), &XRHandTracker::get_has_tracking_data);
+
+ ClassDB::bind_method(D_METHOD("set_hand_tracking_source", "source"), &XRHandTracker::set_hand_tracking_source);
+ ClassDB::bind_method(D_METHOD("get_hand_tracking_source"), &XRHandTracker::get_hand_tracking_source);
+
+ ClassDB::bind_method(D_METHOD("set_hand_joint_flags", "joint", "flags"), &XRHandTracker::set_hand_joint_flags);
+ ClassDB::bind_method(D_METHOD("get_hand_joint_flags", "joint"), &XRHandTracker::get_hand_joint_flags);
+
+ ClassDB::bind_method(D_METHOD("set_hand_joint_transform", "joint", "transform"), &XRHandTracker::set_hand_joint_transform);
+ ClassDB::bind_method(D_METHOD("get_hand_joint_transform", "joint"), &XRHandTracker::get_hand_joint_transform);
+
+ ClassDB::bind_method(D_METHOD("set_hand_joint_radius", "joint", "radius"), &XRHandTracker::set_hand_joint_radius);
+ ClassDB::bind_method(D_METHOD("get_hand_joint_radius", "joint"), &XRHandTracker::get_hand_joint_radius);
+
+ ClassDB::bind_method(D_METHOD("set_hand_joint_linear_velocity", "joint", "linear_velocity"), &XRHandTracker::set_hand_joint_linear_velocity);
+ ClassDB::bind_method(D_METHOD("get_hand_joint_linear_velocity", "joint"), &XRHandTracker::get_hand_joint_linear_velocity);
+
+ ClassDB::bind_method(D_METHOD("set_hand_joint_angular_velocity", "joint", "angular_velocity"), &XRHandTracker::set_hand_joint_angular_velocity);
+ ClassDB::bind_method(D_METHOD("get_hand_joint_angular_velocity", "joint"), &XRHandTracker::get_hand_joint_angular_velocity);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "hand", PROPERTY_HINT_ENUM, "Left,Right"), "set_hand", "get_hand");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "has_tracking_data", PROPERTY_HINT_NONE), "set_has_tracking_data", "get_has_tracking_data");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "hand_tracking_source", PROPERTY_HINT_ENUM, "Unknown,Unobstructed,Controller"), "set_hand_tracking_source", "get_hand_tracking_source");
+
+ BIND_ENUM_CONSTANT(HAND_LEFT);
+ BIND_ENUM_CONSTANT(HAND_RIGHT);
+ BIND_ENUM_CONSTANT(HAND_MAX);
+
+ BIND_ENUM_CONSTANT(HAND_TRACKING_SOURCE_UNKNOWN);
+ BIND_ENUM_CONSTANT(HAND_TRACKING_SOURCE_UNOBSTRUCTED);
+ BIND_ENUM_CONSTANT(HAND_TRACKING_SOURCE_CONTROLLER);
+ BIND_ENUM_CONSTANT(HAND_TRACKING_SOURCE_MAX);
+
+ BIND_ENUM_CONSTANT(HAND_JOINT_PALM);
+ BIND_ENUM_CONSTANT(HAND_JOINT_WRIST);
+ BIND_ENUM_CONSTANT(HAND_JOINT_THUMB_METACARPAL);
+ BIND_ENUM_CONSTANT(HAND_JOINT_THUMB_PHALANX_PROXIMAL);
+ BIND_ENUM_CONSTANT(HAND_JOINT_THUMB_PHALANX_DISTAL);
+ BIND_ENUM_CONSTANT(HAND_JOINT_THUMB_TIP);
+ BIND_ENUM_CONSTANT(HAND_JOINT_INDEX_FINGER_METACARPAL);
+ BIND_ENUM_CONSTANT(HAND_JOINT_INDEX_FINGER_PHALANX_PROXIMAL);
+ BIND_ENUM_CONSTANT(HAND_JOINT_INDEX_FINGER_PHALANX_INTERMEDIATE);
+ BIND_ENUM_CONSTANT(HAND_JOINT_INDEX_FINGER_PHALANX_DISTAL);
+ BIND_ENUM_CONSTANT(HAND_JOINT_INDEX_FINGER_TIP);
+ BIND_ENUM_CONSTANT(HAND_JOINT_MIDDLE_FINGER_METACARPAL);
+ BIND_ENUM_CONSTANT(HAND_JOINT_MIDDLE_FINGER_PHALANX_PROXIMAL);
+ BIND_ENUM_CONSTANT(HAND_JOINT_MIDDLE_FINGER_PHALANX_INTERMEDIATE);
+ BIND_ENUM_CONSTANT(HAND_JOINT_MIDDLE_FINGER_PHALANX_DISTAL);
+ BIND_ENUM_CONSTANT(HAND_JOINT_MIDDLE_FINGER_TIP);
+ BIND_ENUM_CONSTANT(HAND_JOINT_RING_FINGER_METACARPAL);
+ BIND_ENUM_CONSTANT(HAND_JOINT_RING_FINGER_PHALANX_PROXIMAL);
+ BIND_ENUM_CONSTANT(HAND_JOINT_RING_FINGER_PHALANX_INTERMEDIATE);
+ BIND_ENUM_CONSTANT(HAND_JOINT_RING_FINGER_PHALANX_DISTAL);
+ BIND_ENUM_CONSTANT(HAND_JOINT_RING_FINGER_TIP);
+ BIND_ENUM_CONSTANT(HAND_JOINT_PINKY_FINGER_METACARPAL);
+ BIND_ENUM_CONSTANT(HAND_JOINT_PINKY_FINGER_PHALANX_PROXIMAL);
+ BIND_ENUM_CONSTANT(HAND_JOINT_PINKY_FINGER_PHALANX_INTERMEDIATE);
+ BIND_ENUM_CONSTANT(HAND_JOINT_PINKY_FINGER_PHALANX_DISTAL);
+ BIND_ENUM_CONSTANT(HAND_JOINT_PINKY_FINGER_TIP);
+ BIND_ENUM_CONSTANT(HAND_JOINT_MAX);
+
+ BIND_BITFIELD_FLAG(HAND_JOINT_FLAG_ORIENTATION_VALID);
+ BIND_BITFIELD_FLAG(HAND_JOINT_FLAG_ORIENTATION_TRACKED);
+ BIND_BITFIELD_FLAG(HAND_JOINT_FLAG_POSITION_VALID);
+ BIND_BITFIELD_FLAG(HAND_JOINT_FLAG_POSITION_TRACKED);
+ BIND_BITFIELD_FLAG(HAND_JOINT_FLAG_LINEAR_VELOCITY_VALID);
+ BIND_BITFIELD_FLAG(HAND_JOINT_FLAG_ANGULAR_VELOCITY_VALID);
+}
+
+void XRHandTracker::set_hand(XRHandTracker::Hand p_hand) {
+ hand = p_hand;
+}
+
+XRHandTracker::Hand XRHandTracker::get_hand() const {
+ return hand;
+}
+
+void XRHandTracker::set_has_tracking_data(bool p_has_tracking_data) {
+ has_tracking_data = p_has_tracking_data;
+}
+
+bool XRHandTracker::get_has_tracking_data() const {
+ return has_tracking_data;
+}
+
+void XRHandTracker::set_hand_tracking_source(XRHandTracker::HandTrackingSource p_source) {
+ hand_tracking_source = p_source;
+}
+
+XRHandTracker::HandTrackingSource XRHandTracker::get_hand_tracking_source() const {
+ return hand_tracking_source;
+}
+
+void XRHandTracker::set_hand_joint_flags(XRHandTracker::HandJoint p_joint, BitField<XRHandTracker::HandJointFlags> p_flags) {
+ ERR_FAIL_INDEX(p_joint, HAND_JOINT_MAX);
+ hand_joint_flags[p_joint] = p_flags;
+}
+
+BitField<XRHandTracker::HandJointFlags> XRHandTracker::get_hand_joint_flags(XRHandTracker::HandJoint p_joint) const {
+ ERR_FAIL_INDEX_V(p_joint, HAND_JOINT_MAX, BitField<HandJointFlags>());
+ return hand_joint_flags[p_joint];
+}
+
+void XRHandTracker::set_hand_joint_transform(XRHandTracker::HandJoint p_joint, const Transform3D &p_transform) {
+ ERR_FAIL_INDEX(p_joint, HAND_JOINT_MAX);
+ hand_joint_transforms[p_joint] = p_transform;
+}
+
+Transform3D XRHandTracker::get_hand_joint_transform(XRHandTracker::HandJoint p_joint) const {
+ ERR_FAIL_INDEX_V(p_joint, HAND_JOINT_MAX, Transform3D());
+ return hand_joint_transforms[p_joint];
+}
+
+void XRHandTracker::set_hand_joint_radius(XRHandTracker::HandJoint p_joint, float p_radius) {
+ ERR_FAIL_INDEX(p_joint, HAND_JOINT_MAX);
+ hand_joint_radii[p_joint] = p_radius;
+}
+
+float XRHandTracker::get_hand_joint_radius(XRHandTracker::HandJoint p_joint) const {
+ ERR_FAIL_INDEX_V(p_joint, HAND_JOINT_MAX, 0.0);
+ return hand_joint_radii[p_joint];
+}
+
+void XRHandTracker::set_hand_joint_linear_velocity(XRHandTracker::HandJoint p_joint, const Vector3 &p_velocity) {
+ ERR_FAIL_INDEX(p_joint, HAND_JOINT_MAX);
+ hand_joint_linear_velocities[p_joint] = p_velocity;
+}
+
+Vector3 XRHandTracker::get_hand_joint_linear_velocity(XRHandTracker::HandJoint p_joint) const {
+ ERR_FAIL_INDEX_V(p_joint, HAND_JOINT_MAX, Vector3());
+ return hand_joint_linear_velocities[p_joint];
+}
+
+void XRHandTracker::set_hand_joint_angular_velocity(XRHandTracker::HandJoint p_joint, const Vector3 &p_velocity) {
+ ERR_FAIL_INDEX(p_joint, HAND_JOINT_MAX);
+ hand_joint_angular_velocities[p_joint] = p_velocity;
+}
+
+Vector3 XRHandTracker::get_hand_joint_angular_velocity(XRHandTracker::HandJoint p_joint) const {
+ ERR_FAIL_INDEX_V(p_joint, HAND_JOINT_MAX, Vector3());
+ return hand_joint_angular_velocities[p_joint];
+}
diff --git a/servers/xr/xr_hand_tracker.h b/servers/xr/xr_hand_tracker.h
new file mode 100644
index 0000000000..648f02d1f8
--- /dev/null
+++ b/servers/xr/xr_hand_tracker.h
@@ -0,0 +1,137 @@
+/**************************************************************************/
+/* xr_hand_tracker.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 XR_HAND_TRACKER_H
+#define XR_HAND_TRACKER_H
+
+#include "core/object/ref_counted.h"
+
+class XRHandTracker : public RefCounted {
+ GDCLASS(XRHandTracker, RefCounted);
+ _THREAD_SAFE_CLASS_
+
+public:
+ enum Hand {
+ HAND_LEFT,
+ HAND_RIGHT,
+ HAND_MAX,
+ };
+
+ enum HandTrackingSource {
+ HAND_TRACKING_SOURCE_UNKNOWN,
+ HAND_TRACKING_SOURCE_UNOBSTRUCTED,
+ HAND_TRACKING_SOURCE_CONTROLLER,
+ HAND_TRACKING_SOURCE_MAX
+ };
+
+ enum HandJoint {
+ HAND_JOINT_PALM,
+ HAND_JOINT_WRIST,
+ HAND_JOINT_THUMB_METACARPAL,
+ HAND_JOINT_THUMB_PHALANX_PROXIMAL,
+ HAND_JOINT_THUMB_PHALANX_DISTAL,
+ HAND_JOINT_THUMB_TIP,
+ HAND_JOINT_INDEX_FINGER_METACARPAL,
+ HAND_JOINT_INDEX_FINGER_PHALANX_PROXIMAL,
+ HAND_JOINT_INDEX_FINGER_PHALANX_INTERMEDIATE,
+ HAND_JOINT_INDEX_FINGER_PHALANX_DISTAL,
+ HAND_JOINT_INDEX_FINGER_TIP,
+ HAND_JOINT_MIDDLE_FINGER_METACARPAL,
+ HAND_JOINT_MIDDLE_FINGER_PHALANX_PROXIMAL,
+ HAND_JOINT_MIDDLE_FINGER_PHALANX_INTERMEDIATE,
+ HAND_JOINT_MIDDLE_FINGER_PHALANX_DISTAL,
+ HAND_JOINT_MIDDLE_FINGER_TIP,
+ HAND_JOINT_RING_FINGER_METACARPAL,
+ HAND_JOINT_RING_FINGER_PHALANX_PROXIMAL,
+ HAND_JOINT_RING_FINGER_PHALANX_INTERMEDIATE,
+ HAND_JOINT_RING_FINGER_PHALANX_DISTAL,
+ HAND_JOINT_RING_FINGER_TIP,
+ HAND_JOINT_PINKY_FINGER_METACARPAL,
+ HAND_JOINT_PINKY_FINGER_PHALANX_PROXIMAL,
+ HAND_JOINT_PINKY_FINGER_PHALANX_INTERMEDIATE,
+ HAND_JOINT_PINKY_FINGER_PHALANX_DISTAL,
+ HAND_JOINT_PINKY_FINGER_TIP,
+ HAND_JOINT_MAX,
+ };
+
+ enum HandJointFlags {
+ HAND_JOINT_FLAG_ORIENTATION_VALID = 1,
+ HAND_JOINT_FLAG_ORIENTATION_TRACKED = 2,
+ HAND_JOINT_FLAG_POSITION_VALID = 4,
+ HAND_JOINT_FLAG_POSITION_TRACKED = 8,
+ HAND_JOINT_FLAG_LINEAR_VELOCITY_VALID = 16,
+ HAND_JOINT_FLAG_ANGULAR_VELOCITY_VALID = 32,
+ };
+
+ void set_hand(Hand p_hand);
+ Hand get_hand() const;
+
+ void set_has_tracking_data(bool p_has_tracking_data);
+ bool get_has_tracking_data() const;
+
+ void set_hand_tracking_source(HandTrackingSource p_source);
+ HandTrackingSource get_hand_tracking_source() const;
+
+ void set_hand_joint_flags(HandJoint p_joint, BitField<HandJointFlags> p_flags);
+ BitField<HandJointFlags> get_hand_joint_flags(HandJoint p_joint) const;
+
+ void set_hand_joint_transform(HandJoint p_joint, const Transform3D &p_transform);
+ Transform3D get_hand_joint_transform(HandJoint p_joint) const;
+
+ void set_hand_joint_radius(HandJoint p_joint, float p_radius);
+ float get_hand_joint_radius(HandJoint p_joint) const;
+
+ void set_hand_joint_linear_velocity(HandJoint p_joint, const Vector3 &p_velocity);
+ Vector3 get_hand_joint_linear_velocity(HandJoint p_joint) const;
+
+ void set_hand_joint_angular_velocity(HandJoint p_joint, const Vector3 &p_velocity);
+ Vector3 get_hand_joint_angular_velocity(HandJoint p_joint) const;
+
+protected:
+ static void _bind_methods();
+
+private:
+ Hand hand = HAND_LEFT;
+ bool has_tracking_data = false;
+ HandTrackingSource hand_tracking_source = HAND_TRACKING_SOURCE_UNKNOWN;
+
+ BitField<HandJointFlags> hand_joint_flags[HAND_JOINT_MAX];
+ Transform3D hand_joint_transforms[HAND_JOINT_MAX];
+ float hand_joint_radii[HAND_JOINT_MAX] = {};
+ Vector3 hand_joint_linear_velocities[HAND_JOINT_MAX];
+ Vector3 hand_joint_angular_velocities[HAND_JOINT_MAX];
+};
+
+VARIANT_ENUM_CAST(XRHandTracker::Hand)
+VARIANT_ENUM_CAST(XRHandTracker::HandTrackingSource)
+VARIANT_ENUM_CAST(XRHandTracker::HandJoint)
+VARIANT_BITFIELD_CAST(XRHandTracker::HandJointFlags)
+
+#endif // XR_HAND_TRACKER_H
diff --git a/servers/xr_server.cpp b/servers/xr_server.cpp
index b3bb0a3702..1e497e22c3 100644
--- a/servers/xr_server.cpp
+++ b/servers/xr_server.cpp
@@ -31,6 +31,7 @@
#include "xr_server.h"
#include "core/config/project_settings.h"
#include "xr/xr_face_tracker.h"
+#include "xr/xr_hand_tracker.h"
#include "xr/xr_interface.h"
#include "xr/xr_positional_tracker.h"
@@ -75,6 +76,11 @@ void XRServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_trackers", "tracker_types"), &XRServer::get_trackers);
ClassDB::bind_method(D_METHOD("get_tracker", "tracker_name"), &XRServer::get_tracker);
+ ClassDB::bind_method(D_METHOD("add_hand_tracker", "tracker_name", "hand_tracker"), &XRServer::add_hand_tracker);
+ ClassDB::bind_method(D_METHOD("remove_hand_tracker", "tracker_name"), &XRServer::remove_hand_tracker);
+ ClassDB::bind_method(D_METHOD("get_hand_trackers"), &XRServer::get_hand_trackers);
+ ClassDB::bind_method(D_METHOD("get_hand_tracker", "tracker_name"), &XRServer::get_hand_tracker);
+
ClassDB::bind_method(D_METHOD("add_face_tracker", "tracker_name", "face_tracker"), &XRServer::add_face_tracker);
ClassDB::bind_method(D_METHOD("remove_face_tracker", "tracker_name"), &XRServer::remove_face_tracker);
ClassDB::bind_method(D_METHOD("get_face_trackers"), &XRServer::get_face_trackers);
@@ -104,6 +110,10 @@ void XRServer::_bind_methods() {
ADD_SIGNAL(MethodInfo("tracker_updated", PropertyInfo(Variant::STRING_NAME, "tracker_name"), PropertyInfo(Variant::INT, "type")));
ADD_SIGNAL(MethodInfo("tracker_removed", PropertyInfo(Variant::STRING_NAME, "tracker_name"), PropertyInfo(Variant::INT, "type")));
+ ADD_SIGNAL(MethodInfo("hand_tracker_added", PropertyInfo(Variant::STRING_NAME, "tracker_name"), PropertyInfo(Variant::OBJECT, "hand_tracker", PROPERTY_HINT_RESOURCE_TYPE, "XRHandTracker")));
+ ADD_SIGNAL(MethodInfo("hand_tracker_updated", PropertyInfo(Variant::STRING_NAME, "tracker_name"), PropertyInfo(Variant::OBJECT, "hand_tracker", PROPERTY_HINT_RESOURCE_TYPE, "XRHandTracker")));
+ ADD_SIGNAL(MethodInfo("hand_tracker_removed", PropertyInfo(Variant::STRING_NAME, "tracker_name")));
+
ADD_SIGNAL(MethodInfo("face_tracker_added", PropertyInfo(Variant::STRING_NAME, "tracker_name"), PropertyInfo(Variant::OBJECT, "face_tracker", PROPERTY_HINT_RESOURCE_TYPE, "XRFaceTracker")));
ADD_SIGNAL(MethodInfo("face_tracker_updated", PropertyInfo(Variant::STRING_NAME, "tracker_name"), PropertyInfo(Variant::OBJECT, "face_tracker", PROPERTY_HINT_RESOURCE_TYPE, "XRFaceTracker")));
ADD_SIGNAL(MethodInfo("face_tracker_removed", PropertyInfo(Variant::STRING_NAME, "tracker_name")));
@@ -362,6 +372,44 @@ PackedStringArray XRServer::get_suggested_pose_names(const StringName &p_tracker
return arr;
}
+void XRServer::add_hand_tracker(const StringName &p_tracker_name, Ref<XRHandTracker> p_hand_tracker) {
+ ERR_FAIL_COND(p_hand_tracker.is_null());
+
+ if (!hand_trackers.has(p_tracker_name)) {
+ // We don't have a tracker with this name, we're going to add it.
+ hand_trackers[p_tracker_name] = p_hand_tracker;
+ emit_signal(SNAME("hand_tracker_added"), p_tracker_name, p_hand_tracker);
+ } else if (hand_trackers[p_tracker_name] != p_hand_tracker) {
+ // We already have a tracker with this name, we're going to replace it.
+ hand_trackers[p_tracker_name] = p_hand_tracker;
+ emit_signal(SNAME("hand_tracker_updated"), p_tracker_name, p_hand_tracker);
+ }
+}
+
+void XRServer::remove_hand_tracker(const StringName &p_tracker_name) {
+ // Skip if no hand tracker is found.
+ if (!hand_trackers.has(p_tracker_name)) {
+ return;
+ }
+
+ // Send the removed signal, then remove the hand tracker.
+ emit_signal(SNAME("hand_tracker_removed"), p_tracker_name);
+ hand_trackers.erase(p_tracker_name);
+}
+
+Dictionary XRServer::get_hand_trackers() const {
+ return hand_trackers;
+}
+
+Ref<XRHandTracker> XRServer::get_hand_tracker(const StringName &p_tracker_name) const {
+ // Skip if no tracker is found.
+ if (!hand_trackers.has(p_tracker_name)) {
+ return Ref<XRHandTracker>();
+ }
+
+ return hand_trackers[p_tracker_name];
+}
+
void XRServer::add_face_tracker(const StringName &p_tracker_name, Ref<XRFaceTracker> p_face_tracker) {
ERR_FAIL_COND(p_face_tracker.is_null());
diff --git a/servers/xr_server.h b/servers/xr_server.h
index 0a4e020a1f..3e45bbb76c 100644
--- a/servers/xr_server.h
+++ b/servers/xr_server.h
@@ -39,6 +39,7 @@
class XRInterface;
class XRPositionalTracker;
+class XRHandTracker;
class XRFaceTracker;
/**
@@ -86,7 +87,7 @@ private:
Vector<Ref<XRInterface>> interfaces;
Dictionary trackers;
-
+ Dictionary hand_trackers;
Dictionary face_trackers;
Ref<XRInterface> primary_interface; /* we'll identify one interface as primary, this will be used by our viewports */
@@ -187,6 +188,14 @@ public:
// Q: Should we add get_suggested_input_names and get_suggested_haptic_names even though we don't use them for the IDE?
/*
+ Hand trackers are objects that expose the tracked joints of a hand.
+ */
+ void add_hand_tracker(const StringName &p_tracker_name, Ref<XRHandTracker> p_hand_tracker);
+ void remove_hand_tracker(const StringName &p_tracker_name);
+ Dictionary get_hand_trackers() const;
+ Ref<XRHandTracker> get_hand_tracker(const StringName &p_tracker_name) const;
+
+ /*
Face trackers are objects that expose the tracked blend shapes of a face.
*/
void add_face_tracker(const StringName &p_tracker_name, Ref<XRFaceTracker> p_face_tracker);
diff --git a/tests/core/io/test_image.h b/tests/core/io/test_image.h
index 35d182f50c..7a0cbb13f9 100644
--- a/tests/core/io/test_image.h
+++ b/tests/core/io/test_image.h
@@ -420,12 +420,15 @@ TEST_CASE("[Image] Convert image") {
Ref<Image> image = memnew(Image(4, 4, false, Image::FORMAT_RGBA8));
PackedByteArray image_data = image->get_data();
+ ERR_PRINT_OFF;
image->convert((Image::Format)-1);
+ ERR_PRINT_ON;
CHECK_MESSAGE(image->get_data() == image_data, "Image conversion to invalid type (-1) should not alter image.");
-
Ref<Image> image2 = memnew(Image(4, 4, false, Image::FORMAT_RGBA8));
image_data = image2->get_data();
+ ERR_PRINT_OFF;
image2->convert((Image::Format)(Image::FORMAT_MAX + 1));
+ ERR_PRINT_ON;
CHECK_MESSAGE(image2->get_data() == image_data, "Image conversion to invalid type (Image::FORMAT_MAX + 1) should not alter image.");
}
diff --git a/tests/scene/test_primitives.h b/tests/scene/test_primitives.h
index 552f722d24..36b9b17b27 100644
--- a/tests/scene/test_primitives.h
+++ b/tests/scene/test_primitives.h
@@ -68,8 +68,10 @@ TEST_CASE("[SceneTree][Primitive][Capsule] Capsule Primitive") {
}
SUBCASE("[SceneTree][Primitive][Capsule] If set segments negative, default to at least 0") {
+ ERR_PRINT_OFF;
capsule->set_radial_segments(-5);
capsule->set_rings(-17);
+ ERR_PRINT_ON;
CHECK_MESSAGE(capsule->get_radial_segments() >= 0,
"Ensure number of radial segments is >= 0.");
@@ -165,9 +167,11 @@ TEST_CASE("[SceneTree][Primitive][Box] Box Primitive") {
}
SUBCASE("[SceneTree][Primitive][Box] Set subdivides to negative and ensure they are >= 0") {
+ ERR_PRINT_OFF;
box->set_subdivide_width(-2);
box->set_subdivide_height(-2);
box->set_subdivide_depth(-2);
+ ERR_PRINT_ON;
CHECK(box->get_subdivide_width() >= 0);
CHECK(box->get_subdivide_height() >= 0);
@@ -254,8 +258,10 @@ TEST_CASE("[SceneTree][Primitive][Cylinder] Cylinder Primitive") {
}
SUBCASE("[SceneTree][Primitive][Cylinder] Ensure num segments is >= 0") {
+ ERR_PRINT_OFF;
cylinder->set_radial_segments(-12);
cylinder->set_rings(-16);
+ ERR_PRINT_ON;
CHECK(cylinder->get_radial_segments() >= 0);
CHECK(cylinder->get_rings() >= 0);
@@ -441,8 +447,10 @@ TEST_CASE("[SceneTree][Primitive][Plane] Plane Primitive") {
}
SUBCASE("[SceneTree][Primitive][Plane] Ensure number of segments is >= 0.") {
+ ERR_PRINT_OFF;
plane->set_subdivide_width(-15);
plane->set_subdivide_depth(-29);
+ ERR_PRINT_ON;
CHECK(plane->get_subdivide_width() >= 0);
CHECK(plane->get_subdivide_depth() >= 0);
@@ -486,9 +494,11 @@ TEST_CASE("[SceneTree][Primitive][Prism] Prism Primitive") {
}
SUBCASE("[Primitive][Prism] Ensure number of segments always >= 0") {
+ ERR_PRINT_OFF;
prism->set_subdivide_width(-36);
prism->set_subdivide_height(-5);
prism->set_subdivide_depth(-64);
+ ERR_PRINT_ON;
CHECK(prism->get_subdivide_width() >= 0);
CHECK(prism->get_subdivide_height() >= 0);
@@ -521,8 +531,10 @@ TEST_CASE("[SceneTree][Primitive][Sphere] Sphere Primitive") {
}
SUBCASE("[Primitive][Sphere] Ensure number of segments always >= 0") {
+ ERR_PRINT_OFF;
sphere->set_radial_segments(-36);
sphere->set_rings(-5);
+ ERR_PRINT_ON;
CHECK(sphere->get_radial_segments() >= 0);
CHECK(sphere->get_rings() >= 0);
diff --git a/tests/test_main.cpp b/tests/test_main.cpp
index 47c8e9732d..f40f562973 100644
--- a/tests/test_main.cpp
+++ b/tests/test_main.cpp
@@ -107,14 +107,10 @@
#include "tests/scene/test_curve_3d.h"
#include "tests/scene/test_gradient.h"
#include "tests/scene/test_image_texture.h"
-#include "tests/scene/test_navigation_agent_2d.h"
-#include "tests/scene/test_navigation_obstacle_2d.h"
-#include "tests/scene/test_navigation_region_2d.h"
#include "tests/scene/test_node.h"
#include "tests/scene/test_node_2d.h"
#include "tests/scene/test_packed_scene.h"
#include "tests/scene/test_path_2d.h"
-#include "tests/scene/test_primitives.h"
#include "tests/scene/test_sprite_frames.h"
#include "tests/scene/test_text_edit.h"
#include "tests/scene/test_theme.h"
@@ -122,16 +118,20 @@
#include "tests/scene/test_visual_shader.h"
#include "tests/scene/test_window.h"
#include "tests/servers/rendering/test_shader_preprocessor.h"
-#include "tests/servers/test_navigation_server_2d.h"
#include "tests/servers/test_text_server.h"
#include "tests/test_validate_testing.h"
#ifndef _3D_DISABLED
#include "tests/scene/test_camera_3d.h"
+#include "tests/scene/test_navigation_agent_2d.h"
#include "tests/scene/test_navigation_agent_3d.h"
+#include "tests/scene/test_navigation_obstacle_2d.h"
#include "tests/scene/test_navigation_obstacle_3d.h"
+#include "tests/scene/test_navigation_region_2d.h"
#include "tests/scene/test_navigation_region_3d.h"
#include "tests/scene/test_path_3d.h"
+#include "tests/scene/test_primitives.h"
+#include "tests/servers/test_navigation_server_2d.h"
#include "tests/servers/test_navigation_server_3d.h"
#endif // _3D_DISABLED
@@ -141,8 +141,10 @@
#include "tests/test_macros.h"
#include "scene/theme/theme_db.h"
+#ifndef _3D_DISABLED
#include "servers/navigation_server_2d.h"
#include "servers/navigation_server_3d.h"
+#endif // _3D_DISABLED
#include "servers/physics_server_2d.h"
#ifndef _3D_DISABLED
#include "servers/physics_server_3d.h"
@@ -221,12 +223,12 @@ struct GodotTestCaseListener : public doctest::IReporter {
SignalWatcher *signal_watcher = nullptr;
+ PhysicsServer2D *physics_server_2d = nullptr;
#ifndef _3D_DISABLED
PhysicsServer3D *physics_server_3d = nullptr;
-#endif // _3D_DISABLED
- PhysicsServer2D *physics_server_2d = nullptr;
NavigationServer3D *navigation_server_3d = nullptr;
NavigationServer2D *navigation_server_2d = nullptr;
+#endif // _3D_DISABLED
void test_case_start(const doctest::TestCaseData &p_in) override {
reinitialize();
@@ -266,10 +268,12 @@ struct GodotTestCaseListener : public doctest::IReporter {
physics_server_2d = PhysicsServer2DManager::get_singleton()->new_default_server();
physics_server_2d->init();
+#ifndef _3D_DISABLED
ERR_PRINT_OFF;
navigation_server_3d = NavigationServer3DManager::new_default_server();
navigation_server_2d = NavigationServer2DManager::new_default_server();
ERR_PRINT_ON;
+#endif // _3D_DISABLED
memnew(InputMap);
InputMap::get_singleton()->load_default();
@@ -300,6 +304,7 @@ struct GodotTestCaseListener : public doctest::IReporter {
return;
}
+#ifndef _3D_DISABLED
if (suite_name.find("[Navigation]") != -1 && navigation_server_2d == nullptr && navigation_server_3d == nullptr) {
ERR_PRINT_OFF;
navigation_server_3d = NavigationServer3DManager::new_default_server();
@@ -307,6 +312,7 @@ struct GodotTestCaseListener : public doctest::IReporter {
ERR_PRINT_ON;
return;
}
+#endif // _3D_DISABLED
}
void test_case_end(const doctest::CurrentTestCaseStats &) override {
@@ -330,6 +336,7 @@ struct GodotTestCaseListener : public doctest::IReporter {
memdelete(SceneTree::get_singleton());
}
+#ifndef _3D_DISABLED
if (navigation_server_3d) {
memdelete(navigation_server_3d);
navigation_server_3d = nullptr;
@@ -339,6 +346,7 @@ struct GodotTestCaseListener : public doctest::IReporter {
memdelete(navigation_server_2d);
navigation_server_2d = nullptr;
}
+#endif // _3D_DISABLED
#ifndef _3D_DISABLED
if (physics_server_3d) {
diff --git a/thirdparty/README.md b/thirdparty/README.md
index 8a11cc9447..c9b1a3b7ae 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -844,7 +844,7 @@ comments and a patch is provided in the squish/ folder.
## tinyexr
- Upstream: https://github.com/syoyo/tinyexr
-- Version: 1.0.5 (3627ab3060592468d49547b4cdf5353e9e2b50dc, 2023)
+- Version: 1.0.8 (6c8742cc8145c8f629698cd8248900990946d6b1, 2024)
- License: BSD-3-Clause
Files extracted from upstream source:
diff --git a/thirdparty/tinyexr/tinyexr.h b/thirdparty/tinyexr/tinyexr.h
index e5ac54a506..d400157537 100644
--- a/thirdparty/tinyexr/tinyexr.h
+++ b/thirdparty/tinyexr/tinyexr.h
@@ -386,7 +386,7 @@ extern int IsEXRFromMemory(const unsigned char *memory, size_t size);
// error
extern int SaveEXRToMemory(const float *data, const int width, const int height,
const int components, const int save_as_fp16,
- const unsigned char **buffer, const char **err);
+ unsigned char **buffer, const char **err);
// @deprecated { Not recommended, but handy to use. }
// Saves single-frame OpenEXR image to a buffer. Assume EXR image contains RGB(A) channels.
@@ -780,7 +780,7 @@ static void cpy2(unsigned short *dst_val, const unsigned short *src_val) {
}
static void swap2(unsigned short *val) {
-#ifdef TINYEXR_LITTLE_ENDIAN
+#if TINYEXR_LITTLE_ENDIAN
(void)val;
#else
unsigned short tmp = *val;
@@ -839,7 +839,7 @@ static void cpy4(float *dst_val, const float *src_val) {
#endif
static void swap4(unsigned int *val) {
-#ifdef TINYEXR_LITTLE_ENDIAN
+#if TINYEXR_LITTLE_ENDIAN
(void)val;
#else
unsigned int tmp = *val;
@@ -854,7 +854,7 @@ static void swap4(unsigned int *val) {
}
static void swap4(int *val) {
-#ifdef TINYEXR_LITTLE_ENDIAN
+#if TINYEXR_LITTLE_ENDIAN
(void)val;
#else
int tmp = *val;
@@ -869,7 +869,7 @@ static void swap4(int *val) {
}
static void swap4(float *val) {
-#ifdef TINYEXR_LITTLE_ENDIAN
+#if TINYEXR_LITTLE_ENDIAN
(void)val;
#else
float tmp = *val;
@@ -900,7 +900,7 @@ static void cpy8(tinyexr::tinyexr_uint64 *dst_val, const tinyexr::tinyexr_uint64
#endif
static void swap8(tinyexr::tinyexr_uint64 *val) {
-#ifdef TINYEXR_LITTLE_ENDIAN
+#if TINYEXR_LITTLE_ENDIAN
(void)val;
#else
tinyexr::tinyexr_uint64 tmp = (*val);
@@ -9008,7 +9008,7 @@ int LoadEXRMultipartImageFromFile(EXRImage *exr_images,
}
int SaveEXRToMemory(const float *data, int width, int height, int components,
- const int save_as_fp16, const unsigned char **outbuf, const char **err) {
+ const int save_as_fp16, unsigned char **outbuf, const char **err) {
if ((components == 1) || components == 3 || components == 4) {
// OK