summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/input/input_event.cpp20
-rw-r--r--core/input/input_map.cpp5
-rw-r--r--core/math/basis.cpp12
-rw-r--r--core/math/basis.h2
-rw-r--r--core/math/vector2.cpp4
-rw-r--r--core/math/vector2.h30
-rw-r--r--core/math/vector3.cpp14
-rw-r--r--core/math/vector3.h60
-rw-r--r--core/math/vector3i.cpp4
-rw-r--r--core/math/vector3i.h50
-rw-r--r--core/object/object.cpp15
-rw-r--r--core/object/object.h1
-rw-r--r--core/templates/paged_allocator.h5
-rw-r--r--doc/classes/BoneAttachment3D.xml65
-rw-r--r--doc/classes/CharacterBody2D.xml44
-rw-r--r--doc/classes/CharacterBody3D.xml48
-rw-r--r--doc/classes/KinematicCollision2D.xml7
-rw-r--r--doc/classes/KinematicCollision3D.xml7
-rw-r--r--doc/classes/PhysicsServer2D.xml16
-rw-r--r--doc/classes/PhysicsTestMotionResult2D.xml4
-rw-r--r--doc/classes/PhysicsTestMotionResult3D.xml4
-rw-r--r--doc/classes/Skeleton3D.xml158
-rw-r--r--doc/classes/SkeletonModification3D.xml68
-rw-r--r--doc/classes/SkeletonModification3DCCDIK.xml138
-rw-r--r--doc/classes/SkeletonModification3DFABRIK.xml163
-rw-r--r--doc/classes/SkeletonModification3DJiggle.xml201
-rw-r--r--doc/classes/SkeletonModification3DLookAt.xml66
-rw-r--r--doc/classes/SkeletonModification3DStackHolder.xml29
-rw-r--r--doc/classes/SkeletonModification3DTwoBoneIK.xml193
-rw-r--r--doc/classes/SkeletonModificationStack3D.xml90
-rw-r--r--doc/classes/VisualShaderNodeColorFunc.xml3
-rw-r--r--doc/classes/VisualShaderNodeColorOp.xml3
-rw-r--r--doc/classes/VisualShaderNodeCompare.xml9
-rw-r--r--doc/classes/VisualShaderNodeCubemap.xml6
-rw-r--r--doc/classes/VisualShaderNodeFloatFunc.xml3
-rw-r--r--doc/classes/VisualShaderNodeFloatOp.xml3
-rw-r--r--doc/classes/VisualShaderNodeFloatUniform.xml3
-rw-r--r--doc/classes/VisualShaderNodeIntFunc.xml3
-rw-r--r--doc/classes/VisualShaderNodeIntOp.xml3
-rw-r--r--doc/classes/VisualShaderNodeIntUniform.xml3
-rw-r--r--doc/classes/VisualShaderNodeIs.xml3
-rw-r--r--doc/classes/VisualShaderNodeParticleAccelerator.xml3
-rw-r--r--doc/classes/VisualShaderNodeParticleRandomness.xml1
-rw-r--r--doc/classes/VisualShaderNodeSample3D.xml3
-rw-r--r--doc/classes/VisualShaderNodeScalarDerivativeFunc.xml3
-rw-r--r--doc/classes/VisualShaderNodeTexture.xml6
-rw-r--r--doc/classes/VisualShaderNodeTextureUniform.xml6
-rw-r--r--doc/classes/VisualShaderNodeTransformFunc.xml3
-rw-r--r--doc/classes/VisualShaderNodeTransformOp.xml2
-rw-r--r--doc/classes/VisualShaderNodeTransformVecMult.xml3
-rw-r--r--doc/classes/VisualShaderNodeUniform.xml3
-rw-r--r--doc/classes/VisualShaderNodeVectorDerivativeFunc.xml3
-rw-r--r--doc/classes/VisualShaderNodeVectorFunc.xml3
-rw-r--r--doc/classes/VisualShaderNodeVectorOp.xml3
-rw-r--r--doc/classes/WorldMarginShape2D.xml (renamed from doc/classes/LineShape2D.xml)2
-rw-r--r--editor/animation_bezier_editor.cpp35
-rw-r--r--editor/animation_track_editor.cpp5
-rw-r--r--editor/code_editor.cpp79
-rw-r--r--editor/editor_command_palette.cpp68
-rw-r--r--editor/editor_node.cpp7
-rw-r--r--editor/editor_node.h1
-rw-r--r--editor/editor_plugin.cpp2
-rw-r--r--editor/editor_settings.cpp193
-rw-r--r--editor/editor_spin_slider.cpp314
-rw-r--r--editor/editor_spin_slider.h1
-rw-r--r--editor/editor_themes.cpp114
-rw-r--r--editor/icons/GridLayout.svg2
-rw-r--r--editor/icons/VisualScriptComment.svg1
-rw-r--r--editor/icons/VisualScriptExpression.svg1
-rw-r--r--editor/icons/VisualShaderGraphTextureUniform.svg1
-rw-r--r--editor/icons/VisualShaderNodeBooleanUniform.svg1
-rw-r--r--editor/icons/VisualShaderNodeColorConstant.svg1
-rw-r--r--editor/icons/VisualShaderNodeColorOp.svg1
-rw-r--r--editor/icons/VisualShaderNodeColorUniform.svg1
-rw-r--r--editor/icons/VisualShaderNodeComment.svg1
-rw-r--r--editor/icons/VisualShaderNodeCubemap.svg1
-rw-r--r--editor/icons/VisualShaderNodeCubemapUniform.svg1
-rw-r--r--editor/icons/VisualShaderNodeCurveTexture.svg1
-rw-r--r--editor/icons/VisualShaderNodeCurveXYZTexture.svg1
-rw-r--r--editor/icons/VisualShaderNodeExpression.svg1
-rw-r--r--editor/icons/VisualShaderNodeFloatFunc.svg1
-rw-r--r--editor/icons/VisualShaderNodeFloatOp.svg1
-rw-r--r--editor/icons/VisualShaderNodeFloatUniform.svg1
-rw-r--r--editor/icons/VisualShaderNodeGlobalExpression.svg1
-rw-r--r--editor/icons/VisualShaderNodeInput.svg1
-rw-r--r--editor/icons/VisualShaderNodeIntFunc.svg1
-rw-r--r--editor/icons/VisualShaderNodeIntOp.svg1
-rw-r--r--editor/icons/VisualShaderNodeIntUniform.svg1
-rw-r--r--editor/icons/VisualShaderNodeTexture2DUniformTriplanar.svg1
-rw-r--r--editor/icons/VisualShaderNodeTransformCompose.svg1
-rw-r--r--editor/icons/VisualShaderNodeTransformDecompose.svg1
-rw-r--r--editor/icons/VisualShaderNodeTransformUniform.svg1
-rw-r--r--editor/icons/VisualShaderNodeTransformVecMult.svg1
-rw-r--r--editor/icons/VisualShaderNodeVec3Uniform.svg1
-rw-r--r--editor/icons/VisualShaderNodeVectorCompose.svg1
-rw-r--r--editor/icons/VisualShaderNodeVectorDecompose.svg1
-rw-r--r--editor/icons/VisualShaderNodeVectorDistance.svg1
-rw-r--r--editor/icons/VisualShaderNodeVectorFunc.svg1
-rw-r--r--editor/icons/VisualShaderNodeVectorLen.svg1
-rw-r--r--editor/icons/WorldMarginShape2D.svg (renamed from editor/icons/LineShape2D.svg)0
-rw-r--r--editor/inspector_dock.cpp7
-rw-r--r--editor/inspector_dock.h1
-rw-r--r--editor/plugins/abstract_polygon_2d_editor.cpp4
-rw-r--r--editor/plugins/collision_shape_2d_editor_plugin.cpp22
-rw-r--r--editor/plugins/collision_shape_2d_editor_plugin.h2
-rw-r--r--editor/plugins/editor_preview_plugins.cpp12
-rw-r--r--editor/plugins/gpu_particles_2d_editor_plugin.cpp15
-rw-r--r--editor/plugins/gpu_particles_3d_editor_plugin.cpp14
-rw-r--r--editor/plugins/node_3d_editor_gizmos.cpp79
-rw-r--r--editor/plugins/node_3d_editor_gizmos.h1
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp180
-rw-r--r--editor/plugins/node_3d_editor_plugin.h12
-rw-r--r--editor/plugins/path_2d_editor_plugin.cpp2
-rw-r--r--editor/plugins/script_editor_plugin.cpp60
-rw-r--r--editor/plugins/script_text_editor.cpp10
-rw-r--r--editor/plugins/shader_editor_plugin.cpp24
-rw-r--r--editor/plugins/skeleton_3d_editor_plugin.cpp26
-rw-r--r--editor/plugins/sprite_frames_editor_plugin.cpp30
-rw-r--r--editor/plugins/text_editor.cpp2
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp36
-rw-r--r--editor/settings_config_dialog.cpp2
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.cpp44
-rw-r--r--modules/gdscript/gdscript_editor.cpp4
-rw-r--r--modules/gdscript/gdscript_parser.cpp2
-rw-r--r--modules/gdscript/gdscript_tokenizer.cpp2
-rw-r--r--modules/gdscript/tests/test_gdscript.cpp2
-rw-r--r--modules/mobile_vr/mobile_vr_interface.h8
-rw-r--r--modules/mono/csharp_script.cpp195
-rw-r--r--modules/mono/csharp_script.h17
-rw-r--r--modules/mono/glue/base_object_glue.cpp10
-rw-r--r--modules/mono/mono_gd/gd_mono_internals.cpp7
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.cpp19
-rw-r--r--platform/android/android_input_handler.cpp2
-rw-r--r--platform/osx/joypad_osx.cpp16
-rw-r--r--platform/osx/joypad_osx.h1
-rw-r--r--scene/2d/camera_2d.cpp10
-rw-r--r--scene/2d/joints_2d.cpp2
-rw-r--r--scene/2d/navigation_agent_2d.cpp8
-rw-r--r--scene/2d/navigation_obstacle_2d.cpp6
-rw-r--r--scene/2d/physics_body_2d.cpp166
-rw-r--r--scene/2d/physics_body_2d.h31
-rw-r--r--scene/2d/skeleton_2d.cpp4
-rw-r--r--scene/3d/SCsub5
-rw-r--r--scene/3d/audio_stream_player_3d.cpp2
-rw-r--r--scene/3d/bone_attachment_3d.cpp320
-rw-r--r--scene/3d/bone_attachment_3d.h39
-rw-r--r--scene/3d/physics_body_3d.cpp152
-rw-r--r--scene/3d/physics_body_3d.h18
-rw-r--r--scene/3d/physics_joint_3d.cpp4
-rw-r--r--scene/3d/skeleton_3d.cpp697
-rw-r--r--scene/3d/skeleton_3d.h103
-rw-r--r--scene/SCsub3
-rw-r--r--scene/debugger/scene_debugger.cpp4
-rw-r--r--scene/gui/base_button.cpp5
-rw-r--r--scene/gui/line_edit.cpp8
-rw-r--r--scene/gui/menu_button.cpp1
-rw-r--r--scene/gui/shortcut.cpp2
-rw-r--r--scene/gui/tab_container.cpp69
-rw-r--r--scene/gui/tab_container.h1
-rw-r--r--scene/gui/text_edit.cpp14
-rw-r--r--scene/main/scene_tree.cpp13
-rw-r--r--scene/main/viewport.cpp27
-rw-r--r--scene/main/viewport.h4
-rw-r--r--scene/main/window.cpp2
-rw-r--r--scene/register_scene_types.cpp22
-rw-r--r--scene/resources/canvas_item_material.cpp7
-rw-r--r--scene/resources/default_theme/icon_grid_layout.pngbin640 -> 2170 bytes
-rw-r--r--scene/resources/default_theme/theme_data.h6
-rw-r--r--scene/resources/material.cpp8
-rw-r--r--scene/resources/packed_scene.cpp8
-rw-r--r--scene/resources/particles_material.cpp7
-rw-r--r--scene/resources/skeleton_modification_2d.cpp8
-rw-r--r--scene/resources/skeleton_modification_2d_ccdik.cpp8
-rw-r--r--scene/resources/skeleton_modification_2d_fabrik.cpp10
-rw-r--r--scene/resources/skeleton_modification_2d_jiggle.cpp6
-rw-r--r--scene/resources/skeleton_modification_2d_lookat.cpp2
-rw-r--r--scene/resources/skeleton_modification_2d_twoboneik.cpp4
-rw-r--r--scene/resources/skeleton_modification_3d.cpp170
-rw-r--r--scene/resources/skeleton_modification_3d.h76
-rw-r--r--scene/resources/skeleton_modification_3d_ccdik.cpp474
-rw-r--r--scene/resources/skeleton_modification_3d_ccdik.h114
-rw-r--r--scene/resources/skeleton_modification_3d_fabrik.cpp628
-rw-r--r--scene/resources/skeleton_modification_3d_fabrik.h122
-rw-r--r--scene/resources/skeleton_modification_3d_jiggle.cpp573
-rw-r--r--scene/resources/skeleton_modification_3d_jiggle.h138
-rw-r--r--scene/resources/skeleton_modification_3d_lookat.cpp265
-rw-r--r--scene/resources/skeleton_modification_3d_lookat.h89
-rw-r--r--scene/resources/skeleton_modification_3d_stackholder.cpp104
-rw-r--r--scene/resources/skeleton_modification_3d_stackholder.h59
-rw-r--r--scene/resources/skeleton_modification_3d_twoboneik.cpp599
-rw-r--r--scene/resources/skeleton_modification_3d_twoboneik.h118
-rw-r--r--scene/resources/skeleton_modification_stack_3d.cpp222
-rw-r--r--scene/resources/skeleton_modification_stack_3d.h91
-rw-r--r--scene/resources/sky_material.cpp15
-rw-r--r--scene/resources/visual_shader.cpp21
-rw-r--r--scene/resources/visual_shader.h1
-rw-r--r--scene/resources/visual_shader_nodes.cpp433
-rw-r--r--scene/resources/visual_shader_nodes.h97
-rw-r--r--scene/resources/visual_shader_particle_nodes.cpp25
-rw-r--r--scene/resources/visual_shader_particle_nodes.h4
-rw-r--r--scene/resources/world_margin_shape_2d.cpp (renamed from scene/resources/line_shape_2d.cpp)36
-rw-r--r--scene/resources/world_margin_shape_2d.h (renamed from scene/resources/line_shape_2d.h)16
-rw-r--r--scene/scene_string_names.cpp1
-rw-r--r--scene/scene_string_names.h1
-rw-r--r--servers/physics_2d/collision_solver_2d_sat.cpp4
-rw-r--r--servers/physics_2d/collision_solver_2d_sw.cpp18
-rw-r--r--servers/physics_2d/collision_solver_2d_sw.h2
-rw-r--r--servers/physics_2d/physics_server_2d_sw.cpp8
-rw-r--r--servers/physics_2d/physics_server_2d_sw.h2
-rw-r--r--servers/physics_2d/physics_server_2d_wrap_mt.h2
-rw-r--r--servers/physics_2d/shape_2d_sw.cpp12
-rw-r--r--servers/physics_2d/shape_2d_sw.h6
-rw-r--r--servers/physics_2d/space_2d_sw.cpp10
-rw-r--r--servers/physics_3d/area_3d_sw.cpp8
-rw-r--r--servers/physics_3d/area_3d_sw.h34
-rw-r--r--servers/physics_3d/area_pair_3d_sw.cpp82
-rw-r--r--servers/physics_3d/area_pair_3d_sw.h18
-rw-r--r--servers/physics_3d/body_3d_sw.cpp2
-rw-r--r--servers/physics_3d/body_3d_sw.h12
-rw-r--r--servers/physics_3d/collision_solver_3d_sw.cpp4
-rw-r--r--servers/physics_3d/soft_body_3d_sw.cpp41
-rw-r--r--servers/physics_3d/soft_body_3d_sw.h26
-rw-r--r--servers/physics_3d/space_3d_sw.cpp14
-rw-r--r--servers/physics_server_2d.cpp18
-rw-r--r--servers/physics_server_2d.h14
-rw-r--r--servers/physics_server_3d.cpp14
-rw-r--r--servers/physics_server_3d.h10
-rw-r--r--servers/rendering/renderer_rd/effects_rd.cpp343
-rw-r--r--servers/rendering/renderer_rd/effects_rd.h90
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp13
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp4
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp80
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h35
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp3
-rw-r--r--servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp3
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_render_rd.cpp179
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_render_rd.h16
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp3
-rw-r--r--servers/rendering/renderer_rd/renderer_storage_rd.cpp3
-rw-r--r--servers/rendering/renderer_rd/shader_rd.h4
-rw-r--r--servers/rendering/renderer_rd/shaders/blur_raster.glsl98
-rw-r--r--servers/rendering/renderer_rd/shaders/blur_raster_inc.glsl15
-rw-r--r--servers/rendering/renderer_rd/shaders/bokeh_dof.glsl38
-rw-r--r--servers/rendering/renderer_rd/shaders/bokeh_dof_inc.glsl37
-rw-r--r--servers/rendering/renderer_rd/shaders/bokeh_dof_raster.glsl253
-rw-r--r--servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl83
-rw-r--r--tests/test_physics_2d.cpp2
-rw-r--r--thirdparty/README.md2
-rw-r--r--thirdparty/spirv-reflect/patches/specialization-constants.patch71
-rw-r--r--thirdparty/spirv-reflect/spirv_reflect.c519
-rw-r--r--thirdparty/spirv-reflect/spirv_reflect.h63
251 files changed, 9652 insertions, 2306 deletions
diff --git a/core/input/input_event.cpp b/core/input/input_event.cpp
index 16bb92d94b..325cdf2127 100644
--- a/core/input/input_event.cpp
+++ b/core/input/input_event.cpp
@@ -431,10 +431,11 @@ bool InputEventKey::action_match(const Ref<InputEvent> &p_event, bool *p_pressed
match = get_keycode() == key->get_keycode() && (!key->is_pressed() || (code & event_code) == code);
}
if (match) {
+ bool pressed = key->is_pressed();
if (p_pressed != nullptr) {
- *p_pressed = key->is_pressed();
+ *p_pressed = pressed;
}
- float strength = (p_pressed != nullptr && *p_pressed) ? 1.0f : 0.0f;
+ float strength = pressed ? 1.0f : 0.0f;
if (p_strength != nullptr) {
*p_strength = strength;
}
@@ -587,10 +588,11 @@ bool InputEventMouseButton::action_match(const Ref<InputEvent> &p_event, bool *p
bool match = mb->button_index == button_index;
if (match) {
+ bool pressed = mb->is_pressed();
if (p_pressed != nullptr) {
- *p_pressed = mb->is_pressed();
+ *p_pressed = pressed;
}
- float strength = (p_pressed != nullptr && *p_pressed) ? 1.0f : 0.0f;
+ float strength = pressed ? 1.0f : 0.0f;
if (p_strength != nullptr) {
*p_strength = strength;
}
@@ -998,10 +1000,11 @@ bool InputEventJoypadButton::action_match(const Ref<InputEvent> &p_event, bool *
bool match = button_index == jb->button_index;
if (match) {
+ bool pressed = jb->is_pressed();
if (p_pressed != nullptr) {
- *p_pressed = jb->is_pressed();
+ *p_pressed = pressed;
}
- float strength = (p_pressed != nullptr && *p_pressed) ? 1.0f : 0.0f;
+ float strength = pressed ? 1.0f : 0.0f;
if (p_strength != nullptr) {
*p_strength = strength;
}
@@ -1291,10 +1294,11 @@ bool InputEventAction::action_match(const Ref<InputEvent> &p_event, bool *p_pres
bool match = action == act->action;
if (match) {
+ bool pressed = act->pressed;
if (p_pressed != nullptr) {
- *p_pressed = act->pressed;
+ *p_pressed = pressed;
}
- float strength = (p_pressed != nullptr && *p_pressed) ? 1.0f : 0.0f;
+ float strength = pressed ? 1.0f : 0.0f;
if (p_strength != nullptr) {
*p_strength = strength;
}
diff --git a/core/input/input_map.cpp b/core/input/input_map.cpp
index 15be0f1e36..83ec70757e 100644
--- a/core/input/input_map.cpp
+++ b/core/input/input_map.cpp
@@ -222,11 +222,12 @@ bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const Str
Ref<InputEventAction> input_event_action = p_event;
if (input_event_action.is_valid()) {
+ bool pressed = input_event_action->is_pressed();
if (p_pressed != nullptr) {
- *p_pressed = input_event_action->is_pressed();
+ *p_pressed = pressed;
}
if (p_strength != nullptr) {
- *p_strength = (p_pressed != nullptr && *p_pressed) ? input_event_action->get_strength() : 0.0f;
+ *p_strength = pressed ? input_event_action->get_strength() : 0.0f;
}
return input_event_action->get_action() == p_action;
}
diff --git a/core/math/basis.cpp b/core/math/basis.cpp
index b5e25fb837..5c42213e61 100644
--- a/core/math/basis.cpp
+++ b/core/math/basis.cpp
@@ -381,6 +381,18 @@ Quaternion Basis::get_rotation_quaternion() const {
return m.get_quaternion();
}
+void Basis::rotate_to_align(Vector3 p_start_direction, Vector3 p_end_direction) {
+ // Takes two vectors and rotates the basis from the first vector to the second vector.
+ // Adopted from: https://gist.github.com/kevinmoran/b45980723e53edeb8a5a43c49f134724
+ const Vector3 axis = p_start_direction.cross(p_end_direction).normalized();
+ if (axis.length_squared() != 0) {
+ real_t dot = p_start_direction.dot(p_end_direction);
+ dot = CLAMP(dot, -1.0, 1.0);
+ const real_t angle_rads = Math::acos(dot);
+ set_axis_angle(axis, angle_rads);
+ }
+}
+
void Basis::get_rotation_axis_angle(Vector3 &p_axis, real_t &p_angle) const {
// Assumes that the matrix can be decomposed into a proper rotation and scaling matrix as M = R.S,
// and returns the Euler angles corresponding to the rotation part, complementing get_scale().
diff --git a/core/math/basis.h b/core/math/basis.h
index 3db2227b70..9d8ed16e29 100644
--- a/core/math/basis.h
+++ b/core/math/basis.h
@@ -88,6 +88,8 @@ public:
Quaternion get_rotation_quaternion() const;
Vector3 get_rotation() const { return get_rotation_euler(); };
+ void rotate_to_align(Vector3 p_start_direction, Vector3 p_end_direction);
+
Vector3 rotref_posscale_decomposition(Basis &rotref) const;
Vector3 get_euler_xyz() const;
diff --git a/core/math/vector2.cpp b/core/math/vector2.cpp
index eb3301f5d0..54abc1b7f2 100644
--- a/core/math/vector2.cpp
+++ b/core/math/vector2.cpp
@@ -102,7 +102,7 @@ Vector2 Vector2::round() const {
return Vector2(Math::round(x), Math::round(y));
}
-Vector2 Vector2::rotated(real_t p_by) const {
+Vector2 Vector2::rotated(const real_t p_by) const {
real_t sine = Math::sin(p_by);
real_t cosi = Math::cos(p_by);
return Vector2(
@@ -145,7 +145,7 @@ Vector2 Vector2::limit_length(const real_t p_len) const {
return v;
}
-Vector2 Vector2::cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, real_t p_weight) const {
+Vector2 Vector2::cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, const real_t p_weight) const {
Vector2 p0 = p_pre_a;
Vector2 p1 = *this;
Vector2 p2 = p_b;
diff --git a/core/math/vector2.h b/core/math/vector2.h
index 4d9f3126e9..330b4741b1 100644
--- a/core/math/vector2.h
+++ b/core/math/vector2.h
@@ -66,7 +66,7 @@ struct Vector2 {
return p_idx ? y : x;
}
- _FORCE_INLINE_ void set_all(real_t p_value) {
+ _FORCE_INLINE_ void set_all(const real_t p_value) {
x = y = p_value;
}
@@ -106,11 +106,11 @@ struct Vector2 {
Vector2 posmodv(const Vector2 &p_modv) const;
Vector2 project(const Vector2 &p_to) const;
- Vector2 plane_project(real_t p_d, const Vector2 &p_vec) const;
+ Vector2 plane_project(const real_t p_d, const Vector2 &p_vec) const;
- _FORCE_INLINE_ Vector2 lerp(const Vector2 &p_to, real_t p_weight) const;
- _FORCE_INLINE_ Vector2 slerp(const Vector2 &p_to, real_t p_weight) const;
- Vector2 cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, real_t p_weight) const;
+ _FORCE_INLINE_ Vector2 lerp(const Vector2 &p_to, const real_t p_weight) const;
+ _FORCE_INLINE_ Vector2 slerp(const Vector2 &p_to, const real_t p_weight) const;
+ Vector2 cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, const real_t p_weight) const;
Vector2 move_toward(const Vector2 &p_to, const real_t p_delta) const;
Vector2 slide(const Vector2 &p_normal) const;
@@ -152,7 +152,7 @@ struct Vector2 {
return Vector2(Math::abs(x), Math::abs(y));
}
- Vector2 rotated(real_t p_by) const;
+ Vector2 rotated(const real_t p_by) const;
Vector2 orthogonal() const {
return Vector2(y, -x);
}
@@ -168,29 +168,29 @@ struct Vector2 {
operator String() const;
_FORCE_INLINE_ Vector2() {}
- _FORCE_INLINE_ Vector2(real_t p_x, real_t p_y) {
+ _FORCE_INLINE_ Vector2(const real_t p_x, const real_t p_y) {
x = p_x;
y = p_y;
}
};
-_FORCE_INLINE_ Vector2 Vector2::plane_project(real_t p_d, const Vector2 &p_vec) const {
+_FORCE_INLINE_ Vector2 Vector2::plane_project(const real_t p_d, const Vector2 &p_vec) const {
return p_vec - *this * (dot(p_vec) - p_d);
}
-_FORCE_INLINE_ Vector2 operator*(float p_scalar, const Vector2 &p_vec) {
+_FORCE_INLINE_ Vector2 operator*(const float p_scalar, const Vector2 &p_vec) {
return p_vec * p_scalar;
}
-_FORCE_INLINE_ Vector2 operator*(double p_scalar, const Vector2 &p_vec) {
+_FORCE_INLINE_ Vector2 operator*(const double p_scalar, const Vector2 &p_vec) {
return p_vec * p_scalar;
}
-_FORCE_INLINE_ Vector2 operator*(int32_t p_scalar, const Vector2 &p_vec) {
+_FORCE_INLINE_ Vector2 operator*(const int32_t p_scalar, const Vector2 &p_vec) {
return p_vec * p_scalar;
}
-_FORCE_INLINE_ Vector2 operator*(int64_t p_scalar, const Vector2 &p_vec) {
+_FORCE_INLINE_ Vector2 operator*(const int64_t p_scalar, const Vector2 &p_vec) {
return p_vec * p_scalar;
}
@@ -250,7 +250,7 @@ _FORCE_INLINE_ bool Vector2::operator!=(const Vector2 &p_vec2) const {
return x != p_vec2.x || y != p_vec2.y;
}
-Vector2 Vector2::lerp(const Vector2 &p_to, real_t p_weight) const {
+Vector2 Vector2::lerp(const Vector2 &p_to, const real_t p_weight) const {
Vector2 res = *this;
res.x += (p_weight * (p_to.x - x));
@@ -259,7 +259,7 @@ Vector2 Vector2::lerp(const Vector2 &p_to, real_t p_weight) const {
return res;
}
-Vector2 Vector2::slerp(const Vector2 &p_to, real_t p_weight) const {
+Vector2 Vector2::slerp(const Vector2 &p_to, const real_t p_weight) const {
#ifdef MATH_CHECKS
ERR_FAIL_COND_V_MSG(!is_normalized(), Vector2(), "The start Vector2 must be normalized.");
#endif
@@ -357,7 +357,7 @@ struct Vector2i {
x = (int32_t)p_vec2.x;
y = (int32_t)p_vec2.y;
}
- inline Vector2i(int32_t p_x, int32_t p_y) {
+ inline Vector2i(const int32_t p_x, const int32_t p_y) {
x = p_x;
y = p_y;
}
diff --git a/core/math/vector3.cpp b/core/math/vector3.cpp
index 3d59064af6..401c3ccd9c 100644
--- a/core/math/vector3.cpp
+++ b/core/math/vector3.cpp
@@ -32,22 +32,22 @@
#include "core/math/basis.h"
-void Vector3::rotate(const Vector3 &p_axis, real_t p_phi) {
+void Vector3::rotate(const Vector3 &p_axis, const real_t p_phi) {
*this = Basis(p_axis, p_phi).xform(*this);
}
-Vector3 Vector3::rotated(const Vector3 &p_axis, real_t p_phi) const {
+Vector3 Vector3::rotated(const Vector3 &p_axis, const real_t p_phi) const {
Vector3 r = *this;
r.rotate(p_axis, p_phi);
return r;
}
-void Vector3::set_axis(int p_axis, real_t p_value) {
+void Vector3::set_axis(const int p_axis, const real_t p_value) {
ERR_FAIL_INDEX(p_axis, 3);
coord[p_axis] = p_value;
}
-real_t Vector3::get_axis(int p_axis) const {
+real_t Vector3::get_axis(const int p_axis) const {
ERR_FAIL_INDEX_V(p_axis, 3, 0);
return operator[](p_axis);
}
@@ -59,13 +59,13 @@ Vector3 Vector3::clamp(const Vector3 &p_min, const Vector3 &p_max) const {
CLAMP(z, p_min.z, p_max.z));
}
-void Vector3::snap(Vector3 p_step) {
+void Vector3::snap(const Vector3 p_step) {
x = Math::snapped(x, p_step.x);
y = Math::snapped(y, p_step.y);
z = Math::snapped(z, p_step.z);
}
-Vector3 Vector3::snapped(Vector3 p_step) const {
+Vector3 Vector3::snapped(const Vector3 p_step) const {
Vector3 v = *this;
v.snap(p_step);
return v;
@@ -82,7 +82,7 @@ Vector3 Vector3::limit_length(const real_t p_len) const {
return v;
}
-Vector3 Vector3::cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, real_t p_weight) const {
+Vector3 Vector3::cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, const real_t p_weight) const {
Vector3 p0 = p_pre_a;
Vector3 p1 = *this;
Vector3 p2 = p_b;
diff --git a/core/math/vector3.h b/core/math/vector3.h
index d8d3cd3cc0..6a4c42f41b 100644
--- a/core/math/vector3.h
+++ b/core/math/vector3.h
@@ -56,18 +56,18 @@ struct Vector3 {
real_t coord[3] = { 0 };
};
- _FORCE_INLINE_ const real_t &operator[](int p_axis) const {
+ _FORCE_INLINE_ const real_t &operator[](const int p_axis) const {
return coord[p_axis];
}
- _FORCE_INLINE_ real_t &operator[](int p_axis) {
+ _FORCE_INLINE_ real_t &operator[](const int p_axis) {
return coord[p_axis];
}
- void set_axis(int p_axis, real_t p_value);
- real_t get_axis(int p_axis) const;
+ void set_axis(const int p_axis, const real_t p_value);
+ real_t get_axis(const int p_axis) const;
- _FORCE_INLINE_ void set_all(real_t p_value) {
+ _FORCE_INLINE_ void set_all(const real_t p_value) {
x = y = z = p_value;
}
@@ -90,17 +90,17 @@ struct Vector3 {
_FORCE_INLINE_ void zero();
- void snap(Vector3 p_val);
- Vector3 snapped(Vector3 p_val) const;
+ void snap(const Vector3 p_val);
+ Vector3 snapped(const Vector3 p_val) const;
- void rotate(const Vector3 &p_axis, real_t p_phi);
- Vector3 rotated(const Vector3 &p_axis, real_t p_phi) const;
+ void rotate(const Vector3 &p_axis, const real_t p_phi);
+ Vector3 rotated(const Vector3 &p_axis, const real_t p_phi) const;
/* Static Methods between 2 vector3s */
- _FORCE_INLINE_ Vector3 lerp(const Vector3 &p_to, real_t p_weight) const;
- _FORCE_INLINE_ Vector3 slerp(const Vector3 &p_to, real_t p_weight) const;
- Vector3 cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, real_t p_weight) const;
+ _FORCE_INLINE_ Vector3 lerp(const Vector3 &p_to, const real_t p_weight) const;
+ _FORCE_INLINE_ Vector3 slerp(const Vector3 &p_to, const real_t p_weight) const;
+ Vector3 cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, const real_t p_weight) const;
Vector3 move_toward(const Vector3 &p_to, const real_t p_delta) const;
_FORCE_INLINE_ Vector3 cross(const Vector3 &p_b) const;
@@ -143,10 +143,10 @@ struct Vector3 {
_FORCE_INLINE_ Vector3 &operator/=(const Vector3 &p_v);
_FORCE_INLINE_ Vector3 operator/(const Vector3 &p_v) const;
- _FORCE_INLINE_ Vector3 &operator*=(real_t p_scalar);
- _FORCE_INLINE_ Vector3 operator*(real_t p_scalar) const;
- _FORCE_INLINE_ Vector3 &operator/=(real_t p_scalar);
- _FORCE_INLINE_ Vector3 operator/(real_t p_scalar) const;
+ _FORCE_INLINE_ Vector3 &operator*=(const real_t p_scalar);
+ _FORCE_INLINE_ Vector3 operator*(const real_t p_scalar) const;
+ _FORCE_INLINE_ Vector3 &operator/=(const real_t p_scalar);
+ _FORCE_INLINE_ Vector3 operator/(const real_t p_scalar) const;
_FORCE_INLINE_ Vector3 operator-() const;
@@ -168,7 +168,7 @@ struct Vector3 {
y = p_ivec.y;
z = p_ivec.z;
}
- _FORCE_INLINE_ Vector3(real_t p_x, real_t p_y, real_t p_z) {
+ _FORCE_INLINE_ Vector3(const real_t p_x, const real_t p_y, const real_t p_z) {
x = p_x;
y = p_y;
z = p_z;
@@ -208,14 +208,14 @@ Vector3 Vector3::round() const {
return Vector3(Math::round(x), Math::round(y), Math::round(z));
}
-Vector3 Vector3::lerp(const Vector3 &p_to, real_t p_weight) const {
+Vector3 Vector3::lerp(const Vector3 &p_to, const real_t p_weight) const {
return Vector3(
x + (p_weight * (p_to.x - x)),
y + (p_weight * (p_to.y - y)),
z + (p_weight * (p_to.z - z)));
}
-Vector3 Vector3::slerp(const Vector3 &p_to, real_t p_weight) const {
+Vector3 Vector3::slerp(const Vector3 &p_to, const real_t p_weight) const {
real_t theta = angle_to(p_to);
return rotated(cross(p_to).normalized(), theta * p_weight);
}
@@ -303,29 +303,41 @@ Vector3 Vector3::operator/(const Vector3 &p_v) const {
return Vector3(x / p_v.x, y / p_v.y, z / p_v.z);
}
-Vector3 &Vector3::operator*=(real_t p_scalar) {
+Vector3 &Vector3::operator*=(const real_t p_scalar) {
x *= p_scalar;
y *= p_scalar;
z *= p_scalar;
return *this;
}
-_FORCE_INLINE_ Vector3 operator*(real_t p_scalar, const Vector3 &p_vec) {
+_FORCE_INLINE_ Vector3 operator*(const float p_scalar, const Vector3 &p_vec) {
return p_vec * p_scalar;
}
-Vector3 Vector3::operator*(real_t p_scalar) const {
+_FORCE_INLINE_ Vector3 operator*(const double p_scalar, const Vector3 &p_vec) {
+ return p_vec * p_scalar;
+}
+
+_FORCE_INLINE_ Vector3 operator*(const int32_t p_scalar, const Vector3 &p_vec) {
+ return p_vec * p_scalar;
+}
+
+_FORCE_INLINE_ Vector3 operator*(const int64_t p_scalar, const Vector3 &p_vec) {
+ return p_vec * p_scalar;
+}
+
+Vector3 Vector3::operator*(const real_t p_scalar) const {
return Vector3(x * p_scalar, y * p_scalar, z * p_scalar);
}
-Vector3 &Vector3::operator/=(real_t p_scalar) {
+Vector3 &Vector3::operator/=(const real_t p_scalar) {
x /= p_scalar;
y /= p_scalar;
z /= p_scalar;
return *this;
}
-Vector3 Vector3::operator/(real_t p_scalar) const {
+Vector3 Vector3::operator/(const real_t p_scalar) const {
return Vector3(x / p_scalar, y / p_scalar, z / p_scalar);
}
diff --git a/core/math/vector3i.cpp b/core/math/vector3i.cpp
index 2de1e4e331..d3a57af77c 100644
--- a/core/math/vector3i.cpp
+++ b/core/math/vector3i.cpp
@@ -30,12 +30,12 @@
#include "vector3i.h"
-void Vector3i::set_axis(int p_axis, int32_t p_value) {
+void Vector3i::set_axis(const int p_axis, const int32_t p_value) {
ERR_FAIL_INDEX(p_axis, 3);
coord[p_axis] = p_value;
}
-int32_t Vector3i::get_axis(int p_axis) const {
+int32_t Vector3i::get_axis(const int p_axis) const {
ERR_FAIL_INDEX_V(p_axis, 3, 0);
return operator[](p_axis);
}
diff --git a/core/math/vector3i.h b/core/math/vector3i.h
index 37c7c1c368..9308d09045 100644
--- a/core/math/vector3i.h
+++ b/core/math/vector3i.h
@@ -51,16 +51,16 @@ struct Vector3i {
int32_t coord[3] = { 0 };
};
- _FORCE_INLINE_ const int32_t &operator[](int p_axis) const {
+ _FORCE_INLINE_ const int32_t &operator[](const int p_axis) const {
return coord[p_axis];
}
- _FORCE_INLINE_ int32_t &operator[](int p_axis) {
+ _FORCE_INLINE_ int32_t &operator[](const int p_axis) {
return coord[p_axis];
}
- void set_axis(int p_axis, int32_t p_value);
- int32_t get_axis(int p_axis) const;
+ void set_axis(const int p_axis, const int32_t p_value);
+ int32_t get_axis(const int p_axis) const;
int min_axis() const;
int max_axis() const;
@@ -84,12 +84,12 @@ struct Vector3i {
_FORCE_INLINE_ Vector3i &operator%=(const Vector3i &p_v);
_FORCE_INLINE_ Vector3i operator%(const Vector3i &p_v) const;
- _FORCE_INLINE_ Vector3i &operator*=(int32_t p_scalar);
- _FORCE_INLINE_ Vector3i operator*(int32_t p_scalar) const;
- _FORCE_INLINE_ Vector3i &operator/=(int32_t p_scalar);
- _FORCE_INLINE_ Vector3i operator/(int32_t p_scalar) const;
- _FORCE_INLINE_ Vector3i &operator%=(int32_t p_scalar);
- _FORCE_INLINE_ Vector3i operator%(int32_t p_scalar) const;
+ _FORCE_INLINE_ Vector3i &operator*=(const int32_t p_scalar);
+ _FORCE_INLINE_ Vector3i operator*(const int32_t p_scalar) const;
+ _FORCE_INLINE_ Vector3i &operator/=(const int32_t p_scalar);
+ _FORCE_INLINE_ Vector3i operator/(const int32_t p_scalar) const;
+ _FORCE_INLINE_ Vector3i &operator%=(const int32_t p_scalar);
+ _FORCE_INLINE_ Vector3i operator%(const int32_t p_scalar) const;
_FORCE_INLINE_ Vector3i operator-() const;
@@ -103,7 +103,7 @@ struct Vector3i {
operator String() const;
_FORCE_INLINE_ Vector3i() {}
- _FORCE_INLINE_ Vector3i(int32_t p_x, int32_t p_y, int32_t p_z) {
+ _FORCE_INLINE_ Vector3i(const int32_t p_x, const int32_t p_y, const int32_t p_z) {
x = p_x;
y = p_y;
z = p_z;
@@ -175,40 +175,52 @@ Vector3i Vector3i::operator%(const Vector3i &p_v) const {
return Vector3i(x % p_v.x, y % p_v.y, z % p_v.z);
}
-Vector3i &Vector3i::operator*=(int32_t p_scalar) {
+Vector3i &Vector3i::operator*=(const int32_t p_scalar) {
x *= p_scalar;
y *= p_scalar;
z *= p_scalar;
return *this;
}
-_FORCE_INLINE_ Vector3i operator*(int32_t p_scalar, const Vector3i &p_vec) {
- return p_vec * p_scalar;
+_FORCE_INLINE_ Vector3i operator*(const int32_t p_scalar, const Vector3i &p_vector) {
+ return p_vector * p_scalar;
}
-Vector3i Vector3i::operator*(int32_t p_scalar) const {
+_FORCE_INLINE_ Vector3i operator*(const int64_t p_scalar, const Vector3i &p_vector) {
+ return p_vector * p_scalar;
+}
+
+_FORCE_INLINE_ Vector3i operator*(const float p_scalar, const Vector3i &p_vector) {
+ return p_vector * p_scalar;
+}
+
+_FORCE_INLINE_ Vector3i operator*(const double p_scalar, const Vector3i &p_vector) {
+ return p_vector * p_scalar;
+}
+
+Vector3i Vector3i::operator*(const int32_t p_scalar) const {
return Vector3i(x * p_scalar, y * p_scalar, z * p_scalar);
}
-Vector3i &Vector3i::operator/=(int32_t p_scalar) {
+Vector3i &Vector3i::operator/=(const int32_t p_scalar) {
x /= p_scalar;
y /= p_scalar;
z /= p_scalar;
return *this;
}
-Vector3i Vector3i::operator/(int32_t p_scalar) const {
+Vector3i Vector3i::operator/(const int32_t p_scalar) const {
return Vector3i(x / p_scalar, y / p_scalar, z / p_scalar);
}
-Vector3i &Vector3i::operator%=(int32_t p_scalar) {
+Vector3i &Vector3i::operator%=(const int32_t p_scalar) {
x %= p_scalar;
y %= p_scalar;
z %= p_scalar;
return *this;
}
-Vector3i Vector3i::operator%(int32_t p_scalar) const {
+Vector3i Vector3i::operator%(const int32_t p_scalar) const {
return Vector3i(x % p_scalar, y % p_scalar, z % p_scalar);
}
diff --git a/core/object/object.cpp b/core/object/object.cpp
index c191109a8f..2bb4b981b9 100644
--- a/core/object/object.cpp
+++ b/core/object/object.cpp
@@ -1811,6 +1811,21 @@ void *Object::get_instance_binding(void *p_token, const GDNativeInstanceBindingC
return binding;
}
+bool Object::has_instance_binding(void *p_token) {
+ bool found = false;
+ _instance_binding_mutex.lock();
+ for (uint32_t i = 0; i < _instance_binding_count; i++) {
+ if (_instance_bindings[i].token == p_token) {
+ found = true;
+ break;
+ }
+ }
+
+ _instance_binding_mutex.unlock();
+
+ return found;
+}
+
void Object::_construct_object(bool p_reference) {
type_is_reference = p_reference;
_instance_id = ObjectDB::add_instance(this);
diff --git a/core/object/object.h b/core/object/object.h
index 1f5e17c99f..6523105820 100644
--- a/core/object/object.h
+++ b/core/object/object.h
@@ -809,6 +809,7 @@ public:
void *get_instance_binding(void *p_token, const GDNativeInstanceBindingCallbacks *p_callbacks);
// Used on creation by binding only.
void set_instance_binding(void *p_token, void *p_binding, const GDNativeInstanceBindingCallbacks *p_callbacks);
+ bool has_instance_binding(void *p_token);
void clear_internal_resource_paths();
diff --git a/core/templates/paged_allocator.h b/core/templates/paged_allocator.h
index 481289309f..dfc885c6eb 100644
--- a/core/templates/paged_allocator.h
+++ b/core/templates/paged_allocator.h
@@ -50,7 +50,8 @@ class PagedAllocator {
SpinLock spin_lock;
public:
- T *alloc() {
+ template <class... Args>
+ T *alloc(const Args &&...p_args) {
if (thread_safe) {
spin_lock.lock();
}
@@ -75,7 +76,7 @@ public:
if (thread_safe) {
spin_lock.unlock();
}
- memnew_placement(alloc, T);
+ memnew_placement(alloc, T(p_args...));
return alloc;
}
diff --git a/doc/classes/BoneAttachment3D.xml b/doc/classes/BoneAttachment3D.xml
index ab1e5b17d9..b493002c70 100644
--- a/doc/classes/BoneAttachment3D.xml
+++ b/doc/classes/BoneAttachment3D.xml
@@ -4,13 +4,76 @@
A node that will attach to a bone.
</brief_description>
<description>
- This node must be the child of a [Skeleton3D] node. You can then select a bone for this node to attach to. The BoneAttachment3D node will copy the transform of the selected bone.
+ This node will allow you to select a bone for this node to attach to. The BoneAttachment3D node can copy the transform of the select bone, or can override the transform of the selected bone.
+ The BoneAttachment3D node must either be a child of a [Skeleton3D] node or be given an external [Skeleton3D] to use in order to function properly.
</description>
<tutorials>
</tutorials>
<methods>
+ <method name="get_external_skeleton" qualifiers="const">
+ <return type="NodePath" />
+ <description>
+ Returns the [NodePath] to the external [Skeleton3D] node, if one has been set.
+ </description>
+ </method>
+ <method name="get_override_mode" qualifiers="const">
+ <return type="int" />
+ <description>
+ Returns the override mode for the BoneAttachment3D node.
+ </description>
+ </method>
+ <method name="get_override_pose" qualifiers="const">
+ <return type="bool" />
+ <description>
+ Returns whether the BoneAttachment3D node is overriding the bone pose of the bone it's attached to.
+ </description>
+ </method>
+ <method name="get_use_external_skeleton" qualifiers="const">
+ <return type="bool" />
+ <description>
+ Returns whether the BoneAttachment3D node is using an external [Skeleton3D] rather than attempting to use its parent node as the [Skeleton3D].
+ </description>
+ </method>
+ <method name="on_bone_pose_update">
+ <return type="void" />
+ <argument index="0" name="bone_index" type="int" />
+ <description>
+ A function that is called automatically when the [Skeleton3D] the BoneAttachment3D node is using has a bone that has changed its pose. This function is where the BoneAttachment3D node updates its position so it is correctly bound when it is [i]not[/i] set to override the bone pose.
+ </description>
+ </method>
+ <method name="set_external_skeleton">
+ <return type="void" />
+ <argument index="0" name="external_skeleton" type="NodePath" />
+ <description>
+ Sets the [NodePath] to the external skeleton that the BoneAttachment3D node should use. The external [Skeleton3D] node is only used when [code]use_external_skeleton[/code] is set to [code]true[/code].
+ </description>
+ </method>
+ <method name="set_override_mode">
+ <return type="void" />
+ <argument index="0" name="override_mode" type="int" />
+ <description>
+ Sets the override mode for the BoneAttachment3D node. The override mode defines which of the bone poses the BoneAttachment3D node will override.
+ </description>
+ </method>
+ <method name="set_override_pose">
+ <return type="void" />
+ <argument index="0" name="override_pose" type="bool" />
+ <description>
+ Sets whether the BoneAttachment3D node will override the bone pose of the bone it is attached to. When set to [code]true[/code], the BoneAttachment3D node can change the pose of the bone.
+ </description>
+ </method>
+ <method name="set_use_external_skeleton">
+ <return type="void" />
+ <argument index="0" name="use_external_skeleton" type="bool" />
+ <description>
+ Sets whether the BoneAttachment3D node will use an extenral [Skeleton3D] node rather than attenpting to use its parent node as the [Skeleton3D]. When set to [code]true[/code], the BoneAttachment3D node will use the external [Skeleton3D] node set in [code]set_external_skeleton[/code].
+ </description>
+ </method>
</methods>
<members>
+ <member name="bone_idx" type="int" setter="set_bone_idx" getter="get_bone_idx" default="-1">
+ The index of the attached bone.
+ </member>
<member name="bone_name" type="String" setter="set_bone_name" getter="get_bone_name" default="&quot;&quot;">
The name of the attached bone.
</member>
diff --git a/doc/classes/CharacterBody2D.xml b/doc/classes/CharacterBody2D.xml
index a20b0aeea6..e23ceedc28 100644
--- a/doc/classes/CharacterBody2D.xml
+++ b/doc/classes/CharacterBody2D.xml
@@ -15,12 +15,25 @@
<link title="2D Platformer Demo">https://godotengine.org/asset-library/asset/120</link>
</tutorials>
<methods>
+ <method name="get_floor_angle" qualifiers="const">
+ <return type="float" />
+ <argument index="0" name="up_direction" type="Vector2" default="Vector2(0, -1)" />
+ <description>
+ Returns the floor's collision angle at the last collision point according to [code]up_direction[/code], which is [code]Vector2.UP[/code] by default. This value is always positive and only valid after calling [method move_and_slide] and when [method is_on_floor] returns [code]true[/code].
+ </description>
+ </method>
<method name="get_floor_normal" qualifiers="const">
<return type="Vector2" />
<description>
Returns the surface normal of the floor at the last collision point. Only valid after calling [method move_and_slide] and when [method is_on_floor] returns [code]true[/code].
</description>
</method>
+ <method name="get_last_slide_collision">
+ <return type="KinematicCollision2D" />
+ <description>
+ Returns a [KinematicCollision2D], which contains information about the latest collision that occurred during the last call to [method move_and_slide].
+ </description>
+ </method>
<method name="get_platform_velocity" qualifiers="const">
<return type="Vector2" />
<description>
@@ -31,11 +44,11 @@
<return type="KinematicCollision2D" />
<argument index="0" name="slide_idx" type="int" />
<description>
- Returns a [KinematicCollision2D], which contains information about a collision that occurred during the last call to [method move_and_slide]. Since the body can collide several times in a single call to [method move_and_slide], you must specify the index of the collision in the range 0 to ([method get_slide_count] - 1).
+ Returns a [KinematicCollision2D], which contains information about a collision that occurred during the last call to [method move_and_slide]. Since the body can collide several times in a single call to [method move_and_slide], you must specify the index of the collision in the range 0 to ([method get_slide_collision_count] - 1).
[b]Example usage:[/b]
[codeblocks]
[gdscript]
- for i in get_slide_count():
+ for i in get_slide_collision_count():
var collision = get_slide_collision(i)
print("Collided with: ", collision.collider.name)
[/gdscript]
@@ -49,7 +62,7 @@
[/codeblocks]
</description>
</method>
- <method name="get_slide_count" qualifiers="const">
+ <method name="get_slide_collision_count" qualifiers="const">
<return type="int" />
<description>
Returns the number of times the body collided and changed direction during the last call to [method move_and_slide].
@@ -92,12 +105,13 @@
</description>
</method>
<method name="move_and_slide">
- <return type="void" />
+ <return type="bool" />
<description>
- Moves the body based on [member linear_velocity]. If the body collides with another, it will slide along the other body rather than stop immediately. If the other body is a [CharacterBody2D] or [RigidBody2D], it will also be affected by the motion of the other body. You can use this to make moving and rotating platforms, or to make nodes push other nodes.
+ Moves the body based on [member linear_velocity]. If the body collides with another, it will slide along the other body (by default only on floor) rather than stop immediately. If the other body is a [CharacterBody2D] or [RigidBody2D], it will also be affected by the motion of the other body. You can use this to make moving and rotating platforms, or to make nodes push other nodes.
This method should be used in [method Node._physics_process] (or in a method called by [method Node._physics_process]), as it uses the physics step's [code]delta[/code] value automatically in calculations. Otherwise, the simulation will run at an incorrect speed.
- Modifies [member linear_velocity] if a slide collision occurred. To get detailed information about collisions that occurred, use [method get_slide_collision].
+ Modifies [member linear_velocity] if a slide collision occurred. To get the latest collision call [method get_last_slide_collision], for detailed information about collisions that occurred, use [method get_slide_collision].
When the body touches a moving platform, the platform's velocity is automatically added to the body motion. If a collision occurs due to the platform's motion, it will always be first in the slide collisions.
+ Returns [code]true[/code] if the body collided, otherwise, returns [code]false[/code].
</description>
</method>
</methods>
@@ -108,13 +122,13 @@
A higher value means it's more flexible for detecting collision, which helps with consistently detecting walls and floors.
A lower value forces the collision algorithm to use more exact detection, so it can be used in cases that specifically require precision, e.g at very low scale to avoid visible jittering, or for stability with a stack of character bodies.
</member>
- <member name="constant_speed_on_floor" type="bool" setter="set_constant_speed_on_floor_enabled" getter="is_constant_speed_on_floor_enabled" default="false">
+ <member name="floor_block_on_wall" type="bool" setter="set_floor_block_on_wall_enabled" getter="is_floor_block_on_wall_enabled" default="true">
+ If [code]true[/code], the body will be able to move on the floor only. This option avoids to be able to walk on walls, it will however allow to slide down along them.
+ </member>
+ <member name="floor_constant_speed" type="bool" setter="set_floor_constant_speed_enabled" getter="is_floor_constant_speed_enabled" default="false">
If [code]false[/code] (by default), the body will move faster on downward slopes and slower on upward slopes.
If [code]true[/code], the body will always move at the same speed on the ground no matter the slope. Note that you need to use [member floor_snap_length] to stick along a downward slope at constant speed.
</member>
- <member name="exclude_body_layers" type="int" setter="set_exclude_body_layers" getter="get_exclude_body_layers" default="0">
- Collision layers that will be excluded for detecting bodies that will act as moving platforms to be followed by the [CharacterBody2D]. By default, all touching bodies are detected and propagate their velocity. You can add excluded layers to ignore bodies that are contained in these layers.
- </member>
<member name="floor_max_angle" type="float" setter="set_floor_max_angle" getter="get_floor_max_angle" default="0.785398">
Maximum angle (in radians) where a slope is still considered a floor (or a ceiling), rather than a wall, when calling [method move_and_slide]. The default value equals 45 degrees.
</member>
@@ -122,21 +136,21 @@
Sets a snapping distance. When set to a value different from [code]0.0[/code], the body is kept attached to slopes when calling [method move_and_slide]. The snapping vector is determined by the given distance along the opposite direction of the [member up_direction].
As long as the snapping vector is in contact with the ground and the body moves against `up_direction`, the body will remain attached to the surface. Snapping is not applied if the body moves along `up_direction`, so it will be able to detach from the ground when jumping.
</member>
+ <member name="floor_stop_on_slope" type="bool" setter="set_floor_stop_on_slope_enabled" getter="is_floor_stop_on_slope_enabled" default="false">
+ If [code]true[/code], the body will not slide on floor's slopes when you include gravity in [code]linear_velocity[/code] when calling [method move_and_slide] and the body is standing still.
+ </member>
<member name="linear_velocity" type="Vector2" setter="set_linear_velocity" getter="get_linear_velocity" default="Vector2(0, 0)">
Current velocity vector in pixels per second, used and modified during calls to [method move_and_slide].
</member>
<member name="max_slides" type="int" setter="set_max_slides" getter="get_max_slides" default="4">
Maximum number of times the body can change direction before it stops when calling [method move_and_slide].
</member>
- <member name="move_on_floor_only" type="bool" setter="set_move_on_floor_only_enabled" getter="is_move_on_floor_only_enabled" default="true">
- If [code]true[/code], the body will be able to move on the floor only, this option avoids to be able to walk on walls, it will however allow to slide down along them.
+ <member name="moving_platform_ignore_layers" type="int" setter="set_moving_platform_ignore_layers" getter="get_moving_platform_ignore_layers" default="0">
+ Collision layers that will be excluded for detecting bodies that will act as moving platforms to be followed by the [CharacterBody2D]. By default, all touching bodies are detected and propagate their velocity. You can add excluded layers to ignore bodies that are contained in these layers.
</member>
<member name="slide_on_ceiling" type="bool" setter="set_slide_on_ceiling_enabled" getter="is_slide_on_ceiling_enabled" default="true">
If [code]true[/code], during a jump against the ceiling, the body will slide, if [code]false[/code] it will be stopped and will fall vertically.
</member>
- <member name="stop_on_slope" type="bool" setter="set_stop_on_slope_enabled" getter="is_stop_on_slope_enabled" default="false">
- If [code]true[/code], the body will not slide on slopes when you include gravity in [code]linear_velocity[/code] when calling [method move_and_slide] and the body is standing still.
- </member>
<member name="up_direction" type="Vector2" setter="set_up_direction" getter="get_up_direction" default="Vector2(0, -1)">
Direction vector used to determine what is a wall and what is a floor (or a ceiling), rather than a wall, when calling [method move_and_slide]. Defaults to [code]Vector2.UP[/code]. If set to [code]Vector2(0, 0)[/code], everything is considered a wall. This is useful for topdown games.
</member>
diff --git a/doc/classes/CharacterBody3D.xml b/doc/classes/CharacterBody3D.xml
index 7dc4ae9be3..85135d5509 100644
--- a/doc/classes/CharacterBody3D.xml
+++ b/doc/classes/CharacterBody3D.xml
@@ -16,13 +16,26 @@
<link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link>
</tutorials>
<methods>
+ <method name="get_floor_angle" qualifiers="const">
+ <return type="float" />
+ <argument index="0" name="up_direction" type="Vector3" default="Vector3(0, 1, 0)" />
+ <description>
+ Returns the floor's collision angle at the last collision point according to [code]up_direction[/code], which is [code]Vector3.UP[/code] by default. This value is always positive and only valid after calling [method move_and_slide] and when [method is_on_floor] returns [code]true[/code].
+ </description>
+ </method>
<method name="get_floor_normal" qualifiers="const">
<return type="Vector3" />
<description>
Returns the surface normal of the floor at the last collision point. Only valid after calling [method move_and_slide] and when [method is_on_floor] returns [code]true[/code].
</description>
</method>
- <method name="get_floor_velocity" qualifiers="const">
+ <method name="get_last_slide_collision">
+ <return type="KinematicCollision3D" />
+ <description>
+ Returns a [KinematicCollision3D], which contains information about the latest collision that occurred during the last call to [method move_and_slide].
+ </description>
+ </method>
+ <method name="get_platform_velocity" qualifiers="const">
<return type="Vector3" />
<description>
Returns the linear velocity of the floor at the last collision point. Only valid after calling [method move_and_slide] and when [method is_on_floor] returns [code]true[/code].
@@ -32,10 +45,10 @@
<return type="KinematicCollision3D" />
<argument index="0" name="slide_idx" type="int" />
<description>
- Returns a [KinematicCollision3D], which contains information about a collision that occurred during the last call to [method move_and_slide]. Since the body can collide several times in a single call to [method move_and_slide], you must specify the index of the collision in the range 0 to ([method get_slide_count] - 1).
+ Returns a [KinematicCollision3D], which contains information about a collision that occurred during the last call to [method move_and_slide]. Since the body can collide several times in a single call to [method move_and_slide], you must specify the index of the collision in the range 0 to ([method get_slide_collision_count] - 1).
</description>
</method>
- <method name="get_slide_count" qualifiers="const">
+ <method name="get_slide_collision_count" qualifiers="const">
<return type="int" />
<description>
Returns the number of times the body collided and changed direction during the last call to [method move_and_slide].
@@ -47,25 +60,44 @@
Returns [code]true[/code] if the body collided with the ceiling on the last call of [method move_and_slide]. Otherwise, returns [code]false[/code].
</description>
</method>
+ <method name="is_on_ceiling_only" qualifiers="const">
+ <return type="bool" />
+ <description>
+ Returns [code]true[/code] if the body collided only with the ceiling on the last call of [method move_and_slide]. Otherwise, returns [code]false[/code].
+ </description>
+ </method>
<method name="is_on_floor" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if the body collided with the floor on the last call of [method move_and_slide]. Otherwise, returns [code]false[/code].
</description>
</method>
+ <method name="is_on_floor_only" qualifiers="const">
+ <return type="bool" />
+ <description>
+ Returns [code]true[/code] if the body collided only with the floor on the last call of [method move_and_slide]. Otherwise, returns [code]false[/code].
+ </description>
+ </method>
<method name="is_on_wall" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if the body collided with a wall on the last call of [method move_and_slide]. Otherwise, returns [code]false[/code].
</description>
</method>
+ <method name="is_on_wall_only" qualifiers="const">
+ <return type="bool" />
+ <description>
+ Returns [code]true[/code] if the body collided only with a wall on the last call of [method move_and_slide]. Otherwise, returns [code]false[/code].
+ </description>
+ </method>
<method name="move_and_slide">
- <return type="void" />
+ <return type="bool" />
<description>
Moves the body based on [member linear_velocity]. If the body collides with another, it will slide along the other body rather than stop immediately. If the other body is a [CharacterBody3D] or [RigidBody3D], it will also be affected by the motion of the other body. You can use this to make moving and rotating platforms, or to make nodes push other nodes.
This method should be used in [method Node._physics_process] (or in a method called by [method Node._physics_process]), as it uses the physics step's [code]delta[/code] value automatically in calculations. Otherwise, the simulation will run at an incorrect speed.
- Modifies [member linear_velocity] if a slide collision occurred. To get detailed information about collisions that occurred, use [method get_slide_collision].
+ Modifies [member linear_velocity] if a slide collision occurred. To get the latest collision call [method get_last_slide_collision], for more detailed information about collisions that occurred, use [method get_slide_collision].
When the body touches a moving platform, the platform's velocity is automatically added to the body motion. If a collision occurs due to the platform's motion, it will always be first in the slide collisions.
+ Returns [code]true[/code] if the body collided, otherwise, returns [code]false[/code].
</description>
</method>
</methods>
@@ -79,6 +111,9 @@
<member name="floor_max_angle" type="float" setter="set_floor_max_angle" getter="get_floor_max_angle" default="0.785398">
Maximum angle (in radians) where a slope is still considered a floor (or a ceiling), rather than a wall, when calling [method move_and_slide]. The default value equals 45 degrees.
</member>
+ <member name="floor_stop_on_slope" type="bool" setter="set_floor_stop_on_slope_enabled" getter="is_floor_stop_on_slope_enabled" default="false">
+ If [code]true[/code], the body will not slide on slopes when you include gravity in [code]linear_velocity[/code] when calling [method move_and_slide] and the body is standing still.
+ </member>
<member name="linear_velocity" type="Vector3" setter="set_linear_velocity" getter="get_linear_velocity" default="Vector3(0, 0, 0)">
Current velocity vector (typically meters per second), used and modified during calls to [method move_and_slide].
</member>
@@ -89,9 +124,6 @@
When set to a value different from [code]Vector3(0, 0, 0)[/code], the body is kept attached to slopes when calling [method move_and_slide].
As long as the [code]snap[/code] vector is in contact with the ground, the body will remain attached to the surface. This means you must disable snap in order to jump, for example. You can do this by setting [code]snap[/code] to [code]Vector3(0, 0, 0)[/code].
</member>
- <member name="stop_on_slope" type="bool" setter="set_stop_on_slope_enabled" getter="is_stop_on_slope_enabled" default="false">
- If [code]true[/code], the body will not slide on slopes when you include gravity in [code]linear_velocity[/code] when calling [method move_and_slide] and the body is standing still.
- </member>
<member name="up_direction" type="Vector3" setter="set_up_direction" getter="get_up_direction" default="Vector3(0, 1, 0)">
Direction vector used to determine what is a wall and what is a floor (or a ceiling), rather than a wall, when calling [method move_and_slide]. Defaults to [code]Vector3.UP[/code]. If set to [code]Vector3(0, 0, 0)[/code], everything is considered a wall. This is useful for topdown games.
</member>
diff --git a/doc/classes/KinematicCollision2D.xml b/doc/classes/KinematicCollision2D.xml
index d7999f1aa0..721b840e99 100644
--- a/doc/classes/KinematicCollision2D.xml
+++ b/doc/classes/KinematicCollision2D.xml
@@ -10,6 +10,13 @@
<tutorials>
</tutorials>
<methods>
+ <method name="get_angle" qualifiers="const">
+ <return type="float" />
+ <argument index="0" name="up_direction" type="Vector2" default="Vector2(0, -1)" />
+ <description>
+ The collision angle according to [code]up_direction[/code], which is [code]Vector2.UP[/code] by default. This value is always positive.
+ </description>
+ </method>
</methods>
<members>
<member name="collider" type="Object" setter="" getter="get_collider">
diff --git a/doc/classes/KinematicCollision3D.xml b/doc/classes/KinematicCollision3D.xml
index abdb5b4f4e..5477736c25 100644
--- a/doc/classes/KinematicCollision3D.xml
+++ b/doc/classes/KinematicCollision3D.xml
@@ -10,6 +10,13 @@
<tutorials>
</tutorials>
<methods>
+ <method name="get_angle" qualifiers="const">
+ <return type="float" />
+ <argument index="0" name="up_direction" type="Vector3" default="Vector3(0, 1, 0)" />
+ <description>
+ The collision angle according to [code]up_direction[/code], which is [code]Vector3.UP[/code] by default. This value is always positive.
+ </description>
+ </method>
</methods>
<members>
<member name="collider" type="Object" setter="" getter="get_collider">
diff --git a/doc/classes/PhysicsServer2D.xml b/doc/classes/PhysicsServer2D.xml
index 387d79d210..1df2fd0158 100644
--- a/doc/classes/PhysicsServer2D.xml
+++ b/doc/classes/PhysicsServer2D.xml
@@ -716,11 +716,6 @@
Sets a joint parameter. See [enum JointParam] for a list of available parameters.
</description>
</method>
- <method name="line_shape_create">
- <return type="RID" />
- <description>
- </description>
- </method>
<method name="rectangle_shape_create">
<return type="RID" />
<description>
@@ -812,6 +807,11 @@
Sets the value for a space parameter. See [enum SpaceParameter] for a list of available parameters.
</description>
</method>
+ <method name="world_margin_shape_create">
+ <return type="RID" />
+ <description>
+ </description>
+ </method>
</methods>
<constants>
<constant name="SPACE_PARAM_CONTACT_RECYCLE_RADIUS" value="0" enum="SpaceParameter">
@@ -837,11 +837,11 @@
</constant>
<constant name="SPACE_PARAM_TEST_MOTION_MIN_CONTACT_DEPTH" value="7" enum="SpaceParameter">
</constant>
- <constant name="SHAPE_LINE" value="0" enum="ShapeType">
- This is the constant for creating line shapes. A line shape is an infinite line with an origin point, and a normal. Thus, it can be used for front/behind checks.
+ <constant name="SHAPE_WORLD_MARGIN" value="0" enum="ShapeType">
+ This is the constant for creating world margin shapes. A world margin shape is an [i]infinite[/i] line with an origin point, and a normal. Thus, it can be used for front/behind checks.
</constant>
<constant name="SHAPE_SEGMENT" value="1" enum="ShapeType">
- This is the constant for creating segment shapes. A segment shape is a line from a point A to a point B. It can be checked for intersections.
+ This is the constant for creating segment shapes. A segment shape is a [i]finite[/i] line from a point A to a point B. It can be checked for intersections.
</constant>
<constant name="SHAPE_CIRCLE" value="2" enum="ShapeType">
This is the constant for creating circle shapes. A circle shape only has a radius. It can be used for intersections and inside/outside checks.
diff --git a/doc/classes/PhysicsTestMotionResult2D.xml b/doc/classes/PhysicsTestMotionResult2D.xml
index bf3497386e..9c5d525f85 100644
--- a/doc/classes/PhysicsTestMotionResult2D.xml
+++ b/doc/classes/PhysicsTestMotionResult2D.xml
@@ -29,9 +29,9 @@
</member>
<member name="collision_unsafe_fraction" type="float" setter="" getter="get_collision_unsafe_fraction" default="0.0">
</member>
- <member name="motion" type="Vector2" setter="" getter="get_motion" default="Vector2(0, 0)">
+ <member name="remainder" type="Vector2" setter="" getter="get_remainder" default="Vector2(0, 0)">
</member>
- <member name="motion_remainder" type="Vector2" setter="" getter="get_motion_remainder" default="Vector2(0, 0)">
+ <member name="travel" type="Vector2" setter="" getter="get_travel" default="Vector2(0, 0)">
</member>
</members>
<constants>
diff --git a/doc/classes/PhysicsTestMotionResult3D.xml b/doc/classes/PhysicsTestMotionResult3D.xml
index 08c72ba965..6c18a097a1 100644
--- a/doc/classes/PhysicsTestMotionResult3D.xml
+++ b/doc/classes/PhysicsTestMotionResult3D.xml
@@ -29,9 +29,9 @@
</member>
<member name="collision_unsafe_fraction" type="float" setter="" getter="get_collision_unsafe_fraction" default="0.0">
</member>
- <member name="motion" type="Vector3" setter="" getter="get_motion" default="Vector3(0, 0, 0)">
+ <member name="remainder" type="Vector3" setter="" getter="get_remainder" default="Vector3(0, 0, 0)">
</member>
- <member name="motion_remainder" type="Vector3" setter="" getter="get_motion_remainder" default="Vector3(0, 0, 0)">
+ <member name="travel" type="Vector3" setter="" getter="get_travel" default="Vector3(0, 0, 0)">
</member>
</members>
<constants>
diff --git a/doc/classes/Skeleton3D.xml b/doc/classes/Skeleton3D.xml
index 0ec2f6b7ca..2f340adb9f 100644
--- a/doc/classes/Skeleton3D.xml
+++ b/doc/classes/Skeleton3D.xml
@@ -20,9 +20,10 @@
Adds a bone, with name [code]name[/code]. [method get_bone_count] will become the bone index.
</description>
</method>
- <method name="bone_transform_to_world_transform">
- <return type="Transform3D" />
- <argument index="0" name="bone_transform" type="Transform3D" />
+ <method name="add_bone_child">
+ <return type="void" />
+ <argument index="0" name="bone_idx" type="int" />
+ <argument index="1" name="child_bone_idx" type="int" />
<description>
Takes the given bone pose/transform and converts it to a world transform, relative to the [Skeleton3D] node.
This is useful for using the bone transform in calculations with transforms from [Node3D]-based nodes.
@@ -40,6 +41,20 @@
Removes the global pose override on all bones in the skeleton.
</description>
</method>
+ <method name="clear_bones_local_pose_override">
+ <return type="void" />
+ <description>
+ Removes the local pose override on all bones in the skeleton.
+ </description>
+ </method>
+ <method name="execute_modifications">
+ <return type="void" />
+ <argument index="0" name="delta" type="float" />
+ <argument index="1" name="execution_mode" type="int" />
+ <description>
+ Executes all the modifications on the [SkeletonModificationStack3D], if the Skeleton3D has one assigned.
+ </description>
+ </method>
<method name="find_bone" qualifiers="const">
<return type="int" />
<argument index="0" name="name" type="String" />
@@ -47,6 +62,26 @@
Returns the bone index that matches [code]name[/code] as its name.
</description>
</method>
+ <method name="force_update_all_bone_transforms">
+ <return type="void" />
+ <description>
+ Force updates the bone transforms/poses for all bones in the skeleton.
+ </description>
+ </method>
+ <method name="force_update_bone_child_transform">
+ <return type="void" />
+ <argument index="0" name="bone_idx" type="int" />
+ <description>
+ Force updates the bone transform for the bone at [code]bone_idx[/code] and all of its children.
+ </description>
+ </method>
+ <method name="get_bone_children">
+ <return type="PackedInt32Array" />
+ <argument index="0" name="bone_idx" type="int" />
+ <description>
+ Returns an array containing the bone indexes of all the children node of the passed in bone, [code]bone_idx[/code].
+ </description>
+ </method>
<method name="get_bone_count" qualifiers="const">
<return type="int" />
<description>
@@ -74,6 +109,19 @@
Returns the overall transform of the specified bone, with respect to the skeleton, but without any global pose overrides. Being relative to the skeleton frame, this is not the actual "global" transform of the bone.
</description>
</method>
+ <method name="get_bone_global_pose_override" qualifiers="const">
+ <return type="Transform3D" />
+ <argument index="0" name="bone_idx" type="int" />
+ <description>
+ </description>
+ </method>
+ <method name="get_bone_local_pose_override" qualifiers="const">
+ <return type="Transform3D" />
+ <argument index="0" name="bone_idx" type="int" />
+ <description>
+ Returns the local pose override transform for [code]bone_idx[/code].
+ </description>
+ </method>
<method name="get_bone_name" qualifiers="const">
<return type="String" />
<argument index="0" name="bone_idx" type="int" />
@@ -96,16 +144,49 @@
Returns the pose transform of the specified bone. Pose is applied on top of the custom pose, which is applied on top the rest pose.
</description>
</method>
- <method name="get_bone_process_orders">
+ <method name="get_bone_rest" qualifiers="const">
+ <return type="Transform3D" />
+ <argument index="0" name="bone_idx" type="int" />
+ <description>
+ Returns the rest transform for a bone [code]bone_idx[/code].
+ </description>
+ </method>
+ <method name="get_modification_stack">
+ <return type="SkeletonModificationStack3D" />
+ <description>
+ Returns the modification stack attached to this skeleton, if one exists.
+ </description>
+ </method>
+ <method name="get_parentless_bones">
<return type="PackedInt32Array" />
<description>
+ Returns an array with all of the bones that are parentless. Another way to look at this is that it returns the indexes of all the bones that are not dependent or modified by other bones in the Skeleton.
</description>
</method>
- <method name="get_bone_rest" qualifiers="const">
+ <method name="global_pose_to_local_pose">
<return type="Transform3D" />
<argument index="0" name="bone_idx" type="int" />
+ <argument index="1" name="global_pose" type="Transform3D" />
<description>
- Returns the rest transform for a bone [code]bone_idx[/code].
+ Takes the passed-in global pose and converts it to local pose transform.
+ This can be used to easily convert a global pose from [method get_bone_global_pose] to a global transform in [method set_bone_local_pose_override].
+ </description>
+ </method>
+ <method name="global_pose_to_world_transform">
+ <return type="Transform3D" />
+ <argument index="0" name="global_pose" type="Transform3D" />
+ <description>
+ Takes the passed-in global pose and converts it to a world transform.
+ This can be used to easily convert a global pose from [method get_bone_global_pose] to a global transform usable with a node's transform, like [member Node3D.global_transform] for example.
+ </description>
+ </method>
+ <method name="global_pose_z_forward_to_bone_forward">
+ <return type="Basis" />
+ <argument index="0" name="bone_idx" type="int" />
+ <argument index="1" name="basis" type="Basis" />
+ <description>
+ Rotates the given [Basis] so that the forward axis of the Basis is facing in the forward direction of the bone at [code]bone_idx[/code].
+ This is helper function to make using [method Transform3D.looking_at] easier with bone poses.
</description>
</method>
<method name="is_bone_rest_disabled" qualifiers="const">
@@ -115,6 +196,15 @@
Returns whether the bone rest for the bone at [code]bone_idx[/code] is disabled.
</description>
</method>
+ <method name="local_pose_to_global_pose">
+ <return type="Transform3D" />
+ <argument index="0" name="bone_idx" type="int" />
+ <argument index="1" name="local_pose" type="Transform3D" />
+ <description>
+ Converts the passed-in local pose to a global pose relative to the inputted bone, [code]bone_idx[/code].
+ This could be used to convert [method get_bone_pose] for use with the [method set_bone_global_pose_override] function.
+ </description>
+ </method>
<method name="localize_rests">
<return type="void" />
<description>
@@ -158,13 +248,30 @@
Binds the given Skin to the Skeleton.
</description>
</method>
+ <method name="remove_bone_child">
+ <return type="void" />
+ <argument index="0" name="bone_idx" type="int" />
+ <argument index="1" name="child_bone_idx" type="int" />
+ <description>
+ Removes the passed in child bone index, [code]child_bone_idx[/code], from the passed-in bone, [code]bone_idx[/code], if it exists.
+ [b]Note:[/b] This does not remove the child bone, but instead it removes the connection it has to the parent bone.
+ </description>
+ </method>
+ <method name="set_bone_children">
+ <return type="void" />
+ <argument index="0" name="bone_idx" type="int" />
+ <argument index="1" name="bone_children" type="PackedInt32Array" />
+ <description>
+ Sets the children for the passed in bone, [code]bone_idx[/code], to the passed-in array of bone indexes, [code]bone_children[/code].
+ </description>
+ </method>
<method name="set_bone_custom_pose">
<return type="void" />
<argument index="0" name="bone_idx" type="int" />
<argument index="1" name="custom_pose" type="Transform3D" />
<description>
Sets the custom pose transform, [code]custom_pose[/code], for the bone at [code]bone_idx[/code]. This pose is an addition to the bone rest pose.
- [b]Note[/b]: The pose transform needs to be in bone space. Use [method world_transform_to_bone_transform] to convert a world transform, like one you can get from a [Node3D], to bone space.
+ [b]Note:[/b] The pose transform needs to be in bone space. Use [method world_transform_to_global_pose] to convert a world transform, like one you can get from a [Node3D], to bone space.
</description>
</method>
<method name="set_bone_disable_rest">
@@ -183,8 +290,20 @@
<argument index="3" name="persistent" type="bool" default="false" />
<description>
Sets the global pose transform, [code]pose[/code], for the bone at [code]bone_idx[/code].
- [code]amount[/code] is the interpolation strength that will be used when applying the pose, and [code]persistent[/code] determines if the applied pose will remain.
- [b]Note[/b]: The pose transform needs to be in bone space. Use [method world_transform_to_bone_transform] to convert a world transform, like one you can get from a [Node3D], to bone space.
+ [code]amount[/code] is the interpolation strengh that will be used when applying the pose, and [code]persistent[/code] determines if the applied pose will remain.
+ [b]Note:[/b] The pose transform needs to be a global pose! Use [method world_transform_to_global_pose] to convert a world transform, like one you can get from a [Node3D], to a global pose.
+ </description>
+ </method>
+ <method name="set_bone_local_pose_override">
+ <return type="void" />
+ <argument index="0" name="bone_idx" type="int" />
+ <argument index="1" name="pose" type="Transform3D" />
+ <argument index="2" name="amount" type="float" />
+ <argument index="3" name="persistent" type="bool" default="false" />
+ <description>
+ Sets the local pose transform, [code]pose[/code], for the bone at [code]bone_idx[/code].
+ [code]amount[/code] is the interpolation strengh that will be used when applying the pose, and [code]persistent[/code] determines if the applied pose will remain.
+ [b]Note:[/b] The pose transform needs to be a local pose! Use [method global_pose_to_local_pose] to convert a global pose to a local pose.
</description>
</method>
<method name="set_bone_name">
@@ -209,7 +328,7 @@
<argument index="1" name="pose" type="Transform3D" />
<description>
Sets the pose transform for bone [code]bone_idx[/code].
- [b]Note[/b]: The pose transform needs to be in bone space. Use [method world_transform_to_bone_transform] to convert a world transform, like one you can get from a [Node3D], to bone space.
+ [b]Note:[/b] The pose transform needs to be in bone space. Use [method world_transform_to_global_pose] to convert a world transform, like one you can get from a [Node3D], to bone space.
</description>
</method>
<method name="set_bone_rest">
@@ -220,6 +339,13 @@
Sets the rest transform for bone [code]bone_idx[/code].
</description>
</method>
+ <method name="set_modification_stack">
+ <return type="void" />
+ <argument index="0" name="modification_stack" type="SkeletonModificationStack3D" />
+ <description>
+ Sets the modification stack for this skeleton to the passed-in modification stack, [code]modification_stack[/code].
+ </description>
+ </method>
<method name="unparent_bone_and_rest">
<return type="void" />
<argument index="0" name="bone_idx" type="int" />
@@ -227,12 +353,12 @@
Unparents the bone at [code]bone_idx[/code] and sets its rest position to that of its parent prior to being reset.
</description>
</method>
- <method name="world_transform_to_bone_transform">
+ <method name="world_transform_to_global_pose">
<return type="Transform3D" />
<argument index="0" name="world_transform" type="Transform3D" />
<description>
- Takes the given world transform, relative to the [Skeleton3D], and converts it to a bone pose/transform.
- This is useful for using setting bone poses using transforms from [Node3D]-based nodes.
+ Takes the passed-in global transform and converts it to a global pose.
+ This can be used to easily convert a global transform from [member Node3D.global_transform] to a global pose usable with [method set_bone_global_pose_override], for example.
</description>
</method>
</methods>
@@ -241,6 +367,12 @@
</member>
</members>
<signals>
+ <signal name="bone_pose_changed">
+ <argument index="0" name="bone_idx" type="int" />
+ <description>
+ This signal is emitted when one of the bones in the Skeleton3D node have changed their pose. This is used to inform nodes that rely on bone positions that one of the bones in the Skeleton3D have changed their transform/pose.
+ </description>
+ </signal>
<signal name="pose_updated">
<description>
</description>
diff --git a/doc/classes/SkeletonModification3D.xml b/doc/classes/SkeletonModification3D.xml
new file mode 100644
index 0000000000..48b8a905b9
--- /dev/null
+++ b/doc/classes/SkeletonModification3D.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="SkeletonModification3D" inherits="Resource" version="4.0">
+ <brief_description>
+ A resource that operates on bones in a [Skeleton3D].
+ </brief_description>
+ <description>
+ This resource provides an interface that can be expanded so code that operates on bones in a [Skeleton3D] can be mixed and matched together to create complex interactions.
+ This is used to provide Godot with a flexible and powerful Inverse Kinematics solution that can be adapted for many different uses.
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="_execute" qualifiers="virtual">
+ <return type="void" />
+ <argument index="0" name="delta" type="float" />
+ <description>
+ Executes the given modification. This is where the modification performs whatever function it is designed to do.
+ </description>
+ </method>
+ <method name="_setup_modification" qualifiers="virtual">
+ <return type="void" />
+ <argument index="0" name="modification_stack" type="SkeletonModificationStack3D" />
+ <description>
+ Sets up the modification so it can be executed. This function should be called automatically by the [SkeletonModificationStack3D] containing this modification.
+ If you need to initialize a modification before use, this is the place to do it!
+ </description>
+ </method>
+ <method name="clamp_angle">
+ <return type="float" />
+ <argument index="0" name="angle" type="float" />
+ <argument index="1" name="min" type="float" />
+ <argument index="2" name="max" type="float" />
+ <argument index="3" name="invert" type="bool" />
+ <description>
+ Takes a angle and clamps it so it is within the passed-in [code]min[/code] and [code]max[/code] range. [code]invert[/code] will inversely clamp the angle, clamping it to the range outside of the given bounds.
+ </description>
+ </method>
+ <method name="get_is_setup" qualifiers="const">
+ <return type="bool" />
+ <description>
+ Returns whether this modification has been successfully setup or not.
+ </description>
+ </method>
+ <method name="get_modification_stack">
+ <return type="SkeletonModificationStack3D" />
+ <description>
+ Returns the [SkeletonModificationStack3D] that this modification is bound to. Through the modification stack, you can access the Skeleton3D the modification is operating on.
+ </description>
+ </method>
+ <method name="set_is_setup">
+ <return type="void" />
+ <argument index="0" name="is_setup" type="bool" />
+ <description>
+ Manually allows you to set the setup state of the modification. This function should only rarely be used, as the [SkeletonModificationStack3D] the modification is bound to should handle setting the modification up.
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="enabled" type="bool" setter="set_enabled" getter="get_enabled" default="true">
+ When true, the modification's [method _execute] function will be called by the [SkeletonModificationStack3D].
+ </member>
+ <member name="execution_mode" type="int" setter="set_execution_mode" getter="get_execution_mode" default="0">
+ The execution mode for the modification. This tells the modification stack when to execute the modification. Some modifications have settings that are only availible in certain execution modes.
+ </member>
+ </members>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/SkeletonModification3DCCDIK.xml b/doc/classes/SkeletonModification3DCCDIK.xml
new file mode 100644
index 0000000000..aa7ddad56e
--- /dev/null
+++ b/doc/classes/SkeletonModification3DCCDIK.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="SkeletonModification3DCCDIK" inherits="SkeletonModification3D" version="4.0">
+ <brief_description>
+ A modification that uses CCDIK to manipulate a series of bones to reach a target.
+ </brief_description>
+ <description>
+ This [SkeletonModification3D] uses an algorithm called [b]C[/b]yclic [b]C[/b]oordinate [b]D[/b]escent [b]I[/b]nverse [b]K[/b]inematics, or CCDIK, to maniuplate a chain of bones in a Skeleton so it reaches a defined target.
+ CCDIK works by rotating a set of bones, typically called a "bone chain", on a single axis. Each bone is rotated to face the target from the tip (by default), which over a chain of bones allow it to rotate properly to reach the target. Because the bones only rotate on a single axis, CCDIK [i]can[/i] look more robotic than other IK solvers.
+ [b]Note:[/b] The CCDIK modifier has [code]ccdik_joints[/code], which are the data objects that hold the data for each joint in the CCDIK chain. This is different from a bone! CCDIK joints hold the data needed for each bone in the bone chain used by CCDIK.
+ CCDIK also fully supports angle constraints, allowing for more control over how a solution is met.
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="get_ccdik_joint_bone_index" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="joint_idx" type="int" />
+ <description>
+ Returns the bone index of the bone assigned to the CCDIK joint at [code]joint_idx[/code].
+ </description>
+ </method>
+ <method name="get_ccdik_joint_bone_name" qualifiers="const">
+ <return type="String" />
+ <argument index="0" name="joint_idx" type="int" />
+ <description>
+ Returns the name of the bone that is assigned to the CCDIK joint at [code]joint_idx[/code].
+ </description>
+ </method>
+ <method name="get_ccdik_joint_ccdik_axis" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="joint_idx" type="int" />
+ <description>
+ Returns the integer representing the joint axis of the CCDIK joint at [code]joint_idx[/code].
+ </description>
+ </method>
+ <method name="get_ccdik_joint_constraint_angle_max" qualifiers="const">
+ <return type="float" />
+ <argument index="0" name="joint_idx" type="int" />
+ <description>
+ Returns the maximum angle constraint for the joint at [code]joint_idx[/code]. [b]Note:[/b] This angle is in degrees!
+ </description>
+ </method>
+ <method name="get_ccdik_joint_constraint_angle_min" qualifiers="const">
+ <return type="float" />
+ <argument index="0" name="joint_idx" type="int" />
+ <description>
+ Returns the minimum angle constraint for the joint at [code]joint_idx[/code]. [b]Note:[/b] This angle is in degrees!
+ </description>
+ </method>
+ <method name="get_ccdik_joint_constraint_invert" qualifiers="const">
+ <return type="bool" />
+ <argument index="0" name="joint_idx" type="int" />
+ <description>
+ Returns whether the CCDIK joint at [code]joint_idx[/code] uses an inverted joint constraint. See [method set_ccdik_joint_constraint_invert] for details.
+ </description>
+ </method>
+ <method name="get_ccdik_joint_enable_joint_constraint" qualifiers="const">
+ <return type="bool" />
+ <argument index="0" name="joint_idx" type="int" />
+ <description>
+ Enables angle constraints to the CCDIK joint at [code]joint_idx[/code].
+ </description>
+ </method>
+ <method name="set_ccdik_joint_bone_index">
+ <return type="void" />
+ <argument index="0" name="joint_idx" type="int" />
+ <argument index="1" name="bone_index" type="int" />
+ <description>
+ Sets the bone index, [code]bone_index[/code], of the CCDIK joint at [code]joint_idx[/code]. When possible, this will also update the [code]bone_name[/code] of the CCDIK joint based on data provided by the linked skeleton.
+ </description>
+ </method>
+ <method name="set_ccdik_joint_bone_name">
+ <return type="void" />
+ <argument index="0" name="joint_idx" type="int" />
+ <argument index="1" name="bone_name" type="String" />
+ <description>
+ Sets the bone name, [code]bone_name[/code], of the CCDIK joint at [code]joint_idx[/code]. When possible, this will also update the [code]bone_index[/code] of the CCDIK joint based on data provided by the linked skeleton.
+ </description>
+ </method>
+ <method name="set_ccdik_joint_ccdik_axis">
+ <return type="void" />
+ <argument index="0" name="joint_idx" type="int" />
+ <argument index="1" name="axis" type="int" />
+ <description>
+ Sets the joint axis of the CCDIK joint at [code]joint_idx[/code] to the passed-in joint axis, [code]axis[/code].
+ </description>
+ </method>
+ <method name="set_ccdik_joint_constraint_angle_max">
+ <return type="void" />
+ <argument index="0" name="joint_idx" type="int" />
+ <argument index="1" name="max_angle" type="float" />
+ <description>
+ Sets the maximum angle constraint for the joint at [code]joint_idx[/code]. [b]Note:[/b] This angle must be in radians!
+ </description>
+ </method>
+ <method name="set_ccdik_joint_constraint_angle_min">
+ <return type="void" />
+ <argument index="0" name="joint_idx" type="int" />
+ <argument index="1" name="min_angle" type="float" />
+ <description>
+ Sets the minimum angle constraint for the joint at [code]joint_idx[/code]. [b]Note:[/b] This angle must be in radians!
+ </description>
+ </method>
+ <method name="set_ccdik_joint_constraint_invert">
+ <return type="void" />
+ <argument index="0" name="joint_idx" type="int" />
+ <argument index="1" name="invert" type="bool" />
+ <description>
+ Sets whether the CCDIK joint at [code]joint_idx[/code] uses an inverted joint constraint.
+ An inverted joint constraint only constraints the CCDIK joint to the angles [i]outside of[/i] the inputted minimum and maximum angles. For this reason, it is referred to as an inverted joint constraint, as it constraints the joint to the outside of the inputted values.
+ </description>
+ </method>
+ <method name="set_ccdik_joint_enable_joint_constraint">
+ <return type="void" />
+ <argument index="0" name="joint_idx" type="int" />
+ <argument index="1" name="enable" type="bool" />
+ <description>
+ Sets whether joint constraints are enabled for the CCDIK joint at [code]joint_idx[/code].
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="ccdik_data_chain_length" type="int" setter="set_ccdik_data_chain_length" getter="get_ccdik_data_chain_length" default="0">
+ The amount of CCDIK joints in the CCDIK modification.
+ </member>
+ <member name="high_quality_solve" type="bool" setter="set_use_high_quality_solve" getter="get_use_high_quality_solve" default="true">
+ When true, the CCDIK algorithm will perform a higher quality solve that returns more natural results. A high quality solve requires more computation power to solve though, and therefore can be disabled to save performance.
+ </member>
+ <member name="target_nodepath" type="NodePath" setter="set_target_node" getter="get_target_node" default="NodePath(&quot;&quot;)">
+ The NodePath to the node that is the target for the CCDIK modification. This node is what the CCDIK chain will attempt to rotate the bone chain to.
+ </member>
+ <member name="tip_nodepath" type="NodePath" setter="set_tip_node" getter="get_tip_node" default="NodePath(&quot;&quot;)">
+ The end position of the CCDIK chain. Typically, this should be a child of a [BoneAttachment3D] node attached to the final bone in the CCDIK chain, where the child node is offset so it is at the end of the final bone.
+ </member>
+ </members>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/SkeletonModification3DFABRIK.xml b/doc/classes/SkeletonModification3DFABRIK.xml
new file mode 100644
index 0000000000..7058e37e94
--- /dev/null
+++ b/doc/classes/SkeletonModification3DFABRIK.xml
@@ -0,0 +1,163 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="SkeletonModification3DFABRIK" inherits="SkeletonModification3D" version="4.0">
+ <brief_description>
+ A modification that uses FABRIK to manipulate a series of bones to reach a target.
+ </brief_description>
+ <description>
+ This [SkeletonModification3D] uses an algorithm called [b]F[/b]orward [b]A[/b]nd [b]B[/b]ackward [b]R[/b]eaching [b]I[/b]nverse [b]K[/b]inematics, or FABRIK, to rotate a bone chain so that it reaches a target.
+ FABRIK works by knowing the positions and lengths of a series of bones, typically called a "bone chain". It first starts by running a forward pass, which places the final bone at the target's position. Then all other bones are moved towards the tip bone, so they stay at the defined bone length away. Then a backwards pass is performed, where the root/first bone in the FABRIK chain is placed back at the origin. then all other bones are moved so they stay at the defined bone length away. This positions the bone chain so that it reaches the target when possible, but all of the bones stay the correct length away from each other.
+ Because of how FABRIK works, it often gives more natural results than those seen in [SkeletonModification3DCCDIK], though FABRIK currently does not support joint constraints.
+ [b]Note:[/b] The FABRIK modifier has [code]fabrik_joints[/code], which are the data objects that hold the data for each joint in the FABRIK chain. This is different from a bone! FABRIK joints hold the data needed for each bone in the bone chain used by FABRIK.
+ To help control how the FABRIK joints move, a magnet vector can be passed, which can nudge the bones in a certain direction prior to solving, giving a level of control over the final result.
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="fabrik_joint_auto_calculate_length">
+ <return type="void" />
+ <argument index="0" name="joint_idx" type="int" />
+ <description>
+ Will attempt to automatically calculate the length of the bone assigned to the FABRIK joint at [code]joint_idx[/code].
+ </description>
+ </method>
+ <method name="get_fabrik_joint_auto_calculate_length" qualifiers="const">
+ <return type="bool" />
+ <argument index="0" name="joint_idx" type="int" />
+ <description>
+ Returns a boolean that indiciates whether this modification will attempt to autocalculate the length of the bone assigned to the FABRIK joint at [code]joint_idx[/code].
+ </description>
+ </method>
+ <method name="get_fabrik_joint_bone_index" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="joint_idx" type="int" />
+ <description>
+ Returns the bone index of the bone assigned to the FABRIK joint at [code]joint_idx[/code].
+ </description>
+ </method>
+ <method name="get_fabrik_joint_bone_name" qualifiers="const">
+ <return type="String" />
+ <argument index="0" name="joint_idx" type="int" />
+ <description>
+ Returns the name of the bone that is assigned to the FABRIK joint at [code]joint_idx[/code].
+ </description>
+ </method>
+ <method name="get_fabrik_joint_length" qualifiers="const">
+ <return type="float" />
+ <argument index="0" name="joint_idx" type="int" />
+ <description>
+ Returns the length of the FABRIK joint at [code]joint_idx[/code].
+ </description>
+ </method>
+ <method name="get_fabrik_joint_magnet" qualifiers="const">
+ <return type="Vector3" />
+ <argument index="0" name="joint_idx" type="int" />
+ <description>
+ Returns the magnet vector of the FABRIK joint at [code]joint_idx[/code].
+ </description>
+ </method>
+ <method name="get_fabrik_joint_tip_node" qualifiers="const">
+ <return type="NodePath" />
+ <argument index="0" name="joint_idx" type="int" />
+ <description>
+ Returns the [Node3D]-based node placed at the tip of the FABRIK joint at [code]joint_idx[/code], if one has been set.
+ </description>
+ </method>
+ <method name="get_fabrik_joint_use_target_basis" qualifiers="const">
+ <return type="bool" />
+ <argument index="0" name="joint_idx" type="int" />
+ <description>
+ Returns a boolean indiciating whether the FABRIK joint uses the target's [Basis] for its rotation.
+ [b]Note:[/b] this option is only available for the final bone in the FABRIK chain, with this setting being ignored for all other bones.
+ </description>
+ </method>
+ <method name="get_fabrik_joint_use_tip_node" qualifiers="const">
+ <return type="bool" />
+ <argument index="0" name="joint_idx" type="int" />
+ <description>
+ Sets the [Node3D]-based node that will be used as the tip of the FABRIK joint at [code]joint_idx[/code].
+ </description>
+ </method>
+ <method name="set_fabrik_joint_auto_calculate_length">
+ <return type="void" />
+ <argument index="0" name="joint_idx" type="int" />
+ <argument index="1" name="auto_calculate_length" type="bool" />
+ <description>
+ When [code]true[/code], this modification will attempt to automatically calculate the length of the bone for the FABRIK joint at [code]joint_idx[/code]. It does this by either using the tip node assigned, if there is one assigned, or the distance the of the bone's children, if the bone has any. If the bone has no children and no tip node is assigned, then the modification [b]cannot[/b] autocalculate the joint's length. In this case, the joint length should be entered manually or a tip node assigned.
+ </description>
+ </method>
+ <method name="set_fabrik_joint_bone_index">
+ <return type="void" />
+ <argument index="0" name="joint_idx" type="int" />
+ <argument index="1" name="bone_index" type="int" />
+ <description>
+ Sets the bone index, [code]bone_index[/code], of the FABRIK joint at [code]joint_idx[/code]. When possible, this will also update the [code]bone_name[/code] of the FABRIK joint based on data provided by the [Skeleton3D].
+ </description>
+ </method>
+ <method name="set_fabrik_joint_bone_name">
+ <return type="void" />
+ <argument index="0" name="joint_idx" type="int" />
+ <argument index="1" name="bone_name" type="String" />
+ <description>
+ Sets the bone name, [code]bone_name[/code], of the FABRIK joint at [code]joint_idx[/code]. When possible, this will also update the [code]bone_index[/code] of the FABRIK joint based on data provided by the [Skeleton3D].
+ </description>
+ </method>
+ <method name="set_fabrik_joint_length">
+ <return type="void" />
+ <argument index="0" name="joint_idx" type="int" />
+ <argument index="1" name="length" type="float" />
+ <description>
+ Sets the joint length, [code]length[/code], of the FABRIK joint at [code]joint_idx[/code].
+ </description>
+ </method>
+ <method name="set_fabrik_joint_magnet">
+ <return type="void" />
+ <argument index="0" name="joint_idx" type="int" />
+ <argument index="1" name="magnet_position" type="Vector3" />
+ <description>
+ Sets the magenet position to [code]magnet_position[/code] for the joint at [code]joint_idx[/code]. The magnet position is used to nudge the joint in that direction when solving, which gives some control over how that joint will bend when being solved.
+ </description>
+ </method>
+ <method name="set_fabrik_joint_tip_node">
+ <return type="void" />
+ <argument index="0" name="joint_idx" type="int" />
+ <argument index="1" name="tip_node" type="NodePath" />
+ <description>
+ Sets the nodepath of the FARIK joint at [code]joint_idx[/code] to [code]tip_node[/code]. The tip node is used to calculate the length of the FABRIK joint when set to automatically calculate joint length.
+ [b]Note:[/b] The tip node should generally be a child node of a [BoneAttachment3D] node attached to the bone that this FABRIK joint operates on, with the child node being offset so it is at the end of the bone.
+ </description>
+ </method>
+ <method name="set_fabrik_joint_use_target_basis">
+ <return type="void" />
+ <argument index="0" name="joint_idx" type="int" />
+ <argument index="1" name="use_target_basis" type="bool" />
+ <description>
+ Sets whether the FABRIK joint at [code]joint_idx[/code] uses the target's [Basis] for its rotation.
+ [b]Note:[/b] this option is only available for the final bone in the FABRIK chain, with this setting being ignored for all other bones.
+ </description>
+ </method>
+ <method name="set_fabrik_joint_use_tip_node">
+ <return type="void" />
+ <argument index="0" name="joint_idx" type="int" />
+ <argument index="1" name="use_tip_node" type="bool" />
+ <description>
+ Sets whether the tip node should be used when autocalculating the joint length for the FABRIK joint at [code]joint_idx[/code]. This will only work if there is a node assigned to the tip nodepath for this joint.
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="chain_max_iterations" type="int" setter="set_chain_max_iterations" getter="get_chain_max_iterations" default="10">
+ The number of times FABRIK will try to solve each time the [code]execute[/code] function is called. Setting this value to a lower number will be result in better performance, but this can also result in harsher movements and slower solves.
+ </member>
+ <member name="chain_tolerance" type="float" setter="set_chain_tolerance" getter="get_chain_tolerance" default="0.01">
+ The minimum distance the target has to be from the tip of the final bone in the bone chain. Setting this value to a higher number allows for greater performance, but less accurate solves.
+ </member>
+ <member name="fabrik_data_chain_length" type="int" setter="set_fabrik_data_chain_length" getter="get_fabrik_data_chain_length" default="0">
+ The amount of FABRIK joints in the FABRIK modification.
+ </member>
+ <member name="target_nodepath" type="NodePath" setter="set_target_node" getter="get_target_node" default="NodePath(&quot;&quot;)">
+ The NodePath to the node that is the target for the FABRIK modification. This node is what the FABRIK chain will attempt to rotate the bone chain to.
+ </member>
+ </members>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/SkeletonModification3DJiggle.xml b/doc/classes/SkeletonModification3DJiggle.xml
new file mode 100644
index 0000000000..e48e382cd4
--- /dev/null
+++ b/doc/classes/SkeletonModification3DJiggle.xml
@@ -0,0 +1,201 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="SkeletonModification3DJiggle" inherits="SkeletonModification3D" version="4.0">
+ <brief_description>
+ A modification that jiggles bones as they move towards a target.
+ </brief_description>
+ <description>
+ This modification moves a series of bones, typically called a bone chain, towards a target. What makes this modification special is that it calculates the velocity and acceleration for each bone in the bone chain, and runs a very light physics-like calculation using the inputted values. This allows the bones to overshoot the target and "jiggle" around. It can be configured to act more like a spring, or sway around like cloth might.
+ This modification is useful for adding additional motion to things like hair, the edges of clothing, and more. It has several settings to that allow control over how the joint moves when the target moves.
+ [b]Note:[/b] The Jiggle modifier has [code]jiggle_joints[/code], which are the data objects that hold the data for each joint in the Jiggle chain. This is different from a bone! Jiggle joints hold the data needed for each bone in the bone chain used by the Jiggle modification.
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="get_collision_mask" qualifiers="const">
+ <return type="int" />
+ <description>
+ Returns the collision mask that the Jiggle modifier will take into account when performing physics calculations.
+ </description>
+ </method>
+ <method name="get_jiggle_joint_bone_index" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="joint_idx" type="int" />
+ <description>
+ Returns the bone index of the bone assigned to the Jiggle joint at [code]joint_idx[/code].
+ </description>
+ </method>
+ <method name="get_jiggle_joint_bone_name" qualifiers="const">
+ <return type="String" />
+ <argument index="0" name="joint_idx" type="int" />
+ <description>
+ Returns the name of the bone that is assigned to the Jiggle joint at [code]joint_idx[/code].
+ </description>
+ </method>
+ <method name="get_jiggle_joint_damping" qualifiers="const">
+ <return type="float" />
+ <argument index="0" name="joint_idx" type="int" />
+ <description>
+ Returns the amount of dampening of the Jiggle joint at [code]joint_idx[/code].
+ </description>
+ </method>
+ <method name="get_jiggle_joint_gravity" qualifiers="const">
+ <return type="Vector3" />
+ <argument index="0" name="joint_idx" type="int" />
+ <description>
+ Returns a [Vector3] representign the amount of gravity the Jiggle joint at [code]joint_idx[/code] is influenced by.
+ </description>
+ </method>
+ <method name="get_jiggle_joint_mass" qualifiers="const">
+ <return type="float" />
+ <argument index="0" name="joint_idx" type="int" />
+ <description>
+ Returns the amount of mass of the Jiggle joint at [code]joint_idx[/code].
+ </description>
+ </method>
+ <method name="get_jiggle_joint_override" qualifiers="const">
+ <return type="bool" />
+ <argument index="0" name="joint_idx" type="int" />
+ <description>
+ Returns a boolean that indiciates whether the joint at [code]joint_idx[/code] is overriding the default jiggle joint data defined in the modification.
+ </description>
+ </method>
+ <method name="get_jiggle_joint_roll" qualifiers="const">
+ <return type="float" />
+ <argument index="0" name="joint_idx" type="int" />
+ <description>
+ Returns the amount of roll/twist applied to the bone that the Jiggle joint is applied to.
+ </description>
+ </method>
+ <method name="get_jiggle_joint_stiffness" qualifiers="const">
+ <return type="float" />
+ <argument index="0" name="joint_idx" type="int" />
+ <description>
+ Returns the stiffness of the Jiggle joint at [code]joint_idx[/code].
+ </description>
+ </method>
+ <method name="get_jiggle_joint_use_gravity" qualifiers="const">
+ <return type="bool" />
+ <argument index="0" name="joint_idx" type="int" />
+ <description>
+ Returns a boolean that indiciates whether the joint at [code]joint_idx[/code] is using gravity or not.
+ </description>
+ </method>
+ <method name="get_use_colliders" qualifiers="const">
+ <return type="bool" />
+ <description>
+ Returns whether the Jiggle modifier is taking physics colliders into account when solving.
+ </description>
+ </method>
+ <method name="set_collision_mask">
+ <return type="void" />
+ <argument index="0" name="mask" type="int" />
+ <description>
+ Sets the collision mask that the Jiggle modifier takes into account when performing physics calculations.
+ </description>
+ </method>
+ <method name="set_jiggle_joint_bone_index">
+ <return type="void" />
+ <argument index="0" name="joint_idx" type="int" />
+ <argument index="1" name="bone_idx" type="int" />
+ <description>
+ Sets the bone index, [code]bone_index[/code], of the Jiggle joint at [code]joint_idx[/code]. When possible, this will also update the [code]bone_name[/code] of the Jiggle joint based on data provided by the [Skeleton3D].
+ </description>
+ </method>
+ <method name="set_jiggle_joint_bone_name">
+ <return type="void" />
+ <argument index="0" name="joint_idx" type="int" />
+ <argument index="1" name="name" type="String" />
+ <description>
+ Sets the bone name, [code]bone_name[/code], of the Jiggle joint at [code]joint_idx[/code]. When possible, this will also update the [code]bone_index[/code] of the Jiggle joint based on data provided by the [Skeleton3D].
+ </description>
+ </method>
+ <method name="set_jiggle_joint_damping">
+ <return type="void" />
+ <argument index="0" name="joint_idx" type="int" />
+ <argument index="1" name="damping" type="float" />
+ <description>
+ Sets the amount of dampening of the Jiggle joint at [code]joint_idx[/code].
+ </description>
+ </method>
+ <method name="set_jiggle_joint_gravity">
+ <return type="void" />
+ <argument index="0" name="joint_idx" type="int" />
+ <argument index="1" name="gravity" type="Vector3" />
+ <description>
+ Sets the gravity vector of the Jiggle joint at [code]joint_idx[/code].
+ </description>
+ </method>
+ <method name="set_jiggle_joint_mass">
+ <return type="void" />
+ <argument index="0" name="joint_idx" type="int" />
+ <argument index="1" name="mass" type="float" />
+ <description>
+ Sets the of mass of the Jiggle joint at [code]joint_idx[/code].
+ </description>
+ </method>
+ <method name="set_jiggle_joint_override">
+ <return type="void" />
+ <argument index="0" name="joint_idx" type="int" />
+ <argument index="1" name="override" type="bool" />
+ <description>
+ Sets whether the Jiggle joint at [code]joint_idx[/code] should override the default Jiggle joint settings. Setting this to true will make the joint use its own settings rather than the default ones attached to the modification.
+ </description>
+ </method>
+ <method name="set_jiggle_joint_roll">
+ <return type="void" />
+ <argument index="0" name="joint_idx" type="int" />
+ <argument index="1" name="roll" type="float" />
+ <description>
+ Sets the amount of roll/twist on the bone the Jiggle joint is attached to.
+ </description>
+ </method>
+ <method name="set_jiggle_joint_stiffness">
+ <return type="void" />
+ <argument index="0" name="joint_idx" type="int" />
+ <argument index="1" name="stiffness" type="float" />
+ <description>
+ Sets the of stiffness of the Jiggle joint at [code]joint_idx[/code].
+ </description>
+ </method>
+ <method name="set_jiggle_joint_use_gravity">
+ <return type="void" />
+ <argument index="0" name="joint_idx" type="int" />
+ <argument index="1" name="use_gravity" type="bool" />
+ <description>
+ Sets whether the Jiggle joint at [code]joint_idx[/code] should use gravity.
+ </description>
+ </method>
+ <method name="set_use_colliders">
+ <return type="void" />
+ <argument index="0" name="use_colliders" type="bool" />
+ <description>
+ When [code]true[/code], the Jiggle modifier will use raycasting to prevent the Jiggle joints from rotating themselves into collision objects when solving.
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="damping" type="float" setter="set_damping" getter="get_damping" default="0.75">
+ The default amount of dampening applied to the Jiggle joints, if they are not overriden. Higher values lead to more of the calculated velocity being applied.
+ </member>
+ <member name="gravity" type="Vector3" setter="set_gravity" getter="get_gravity" default="Vector3(0, -6, 0)">
+ The default amount of gravity applied to the Jiggle joints, if they are not overriden.
+ </member>
+ <member name="jiggle_data_chain_length" type="int" setter="set_jiggle_data_chain_length" getter="get_jiggle_data_chain_length" default="0">
+ The amount of Jiggle joints in the Jiggle modification.
+ </member>
+ <member name="mass" type="float" setter="set_mass" getter="get_mass" default="0.75">
+ The default amount of mass assigned to the Jiggle joints, if they are not overriden. Higher values lead to faster movements and more overshooting.
+ </member>
+ <member name="stiffness" type="float" setter="set_stiffness" getter="get_stiffness" default="3.0">
+ The default amount of stiffness assigned to the Jiggle joints, if they are not overriden. Higher values act more like springs, quickly moving into the correct position.
+ </member>
+ <member name="target_nodepath" type="NodePath" setter="set_target_node" getter="get_target_node" default="NodePath(&quot;&quot;)">
+ The NodePath to the node that is the target for the Jiggle modification. This node is what the Jiggle chain will attempt to rotate the bone chain to.
+ </member>
+ <member name="use_gravity" type="bool" setter="set_use_gravity" getter="get_use_gravity" default="false">
+ Whether the gravity vector, [member gravity], should be applied to the Jiggle joints, assuming they are not overriding the default settings.
+ </member>
+ </members>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/SkeletonModification3DLookAt.xml b/doc/classes/SkeletonModification3DLookAt.xml
new file mode 100644
index 0000000000..c01d764cff
--- /dev/null
+++ b/doc/classes/SkeletonModification3DLookAt.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="SkeletonModification3DLookAt" inherits="SkeletonModification3D" version="4.0">
+ <brief_description>
+ A modification that rotates a bone to look at a target.
+ </brief_description>
+ <description>
+ This [SkeletonModification3D] rotates a bone to look a target. This is extremely helpful for moving character's heads to look at the player, rotating a turret to look at a target, or any other case where you want to make a bone rotate towards something quickly and easily.
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="get_additional_rotation" qualifiers="const">
+ <return type="Vector3" />
+ <description>
+ Returns the amount of extra rotation that is applied to the bone after the LookAt modification executes.
+ </description>
+ </method>
+ <method name="get_lock_rotation_plane" qualifiers="const">
+ <return type="int" />
+ <description>
+ Returns the plane that the LookAt modification is limiting rotation to.
+ </description>
+ </method>
+ <method name="get_lock_rotation_to_plane" qualifiers="const">
+ <return type="bool" />
+ <description>
+ Returns whether the LookAt modification is limiting rotation to a single plane in 3D space.
+ </description>
+ </method>
+ <method name="set_additional_rotation">
+ <return type="void" />
+ <argument index="0" name="additional_rotation" type="Vector3" />
+ <description>
+ Sets the amount of extra rotation to be applied after the LookAt modification executes. This allows you to adjust the finished result.
+ </description>
+ </method>
+ <method name="set_lock_rotation_plane">
+ <return type="void" />
+ <argument index="0" name="plane" type="int" />
+ <description>
+ </description>
+ </method>
+ <method name="set_lock_rotation_to_plane">
+ <return type="void" />
+ <argument index="0" name="lock_to_plane" type="bool" />
+ <description>
+ When [code]true[/code], the LookAt modification will limit its rotation to a single plane in 3D space. The plane used can be configured using the [code]set_lock_rotation_plane[/code] function.
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="bone_index" type="int" setter="set_bone_index" getter="get_bone_index" default="-2">
+ The bone index of the bone that should be operated on by this modification.
+ When possible, this will also update the [member bone_name] based on data provided by the [Skeleton3D].
+ </member>
+ <member name="bone_name" type="String" setter="set_bone_name" getter="get_bone_name" default="&quot;&quot;">
+ The name of the bone that should be operated on by this modification.
+ When possible, this will also update the [member bone_index] based on data provided by the [Skeleton3D].
+ </member>
+ <member name="target_nodepath" type="NodePath" setter="set_target_node" getter="get_target_node" default="NodePath(&quot;&quot;)">
+ The NodePath to the node that is the target for the modification.
+ </member>
+ </members>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/SkeletonModification3DStackHolder.xml b/doc/classes/SkeletonModification3DStackHolder.xml
new file mode 100644
index 0000000000..bb923b680d
--- /dev/null
+++ b/doc/classes/SkeletonModification3DStackHolder.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="SkeletonModification3DStackHolder" inherits="SkeletonModification3D" version="4.0">
+ <brief_description>
+ A modification that holds and executes a [SkeletonModificationStack3D].
+ </brief_description>
+ <description>
+ This [SkeletonModification3D] holds a reference to a [SkeletonModificationStack3D], allowing you to use multiple modification stacks on a single [Skeleton3D].
+ [b]Note[/b]: The modifications in the held [SkeletonModificationStack3D] will only be executed if their execution mode matches the execution mode of the SkeletonModification3DStackHolder.
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="get_held_modification_stack" qualifiers="const">
+ <return type="SkeletonModificationStack3D" />
+ <description>
+ Returns the [SkeletonModificationStack3D] that this modification is holding.
+ </description>
+ </method>
+ <method name="set_held_modification_stack">
+ <return type="void" />
+ <argument index="0" name="held_modification_stack" type="SkeletonModificationStack3D" />
+ <description>
+ Sets the [SkeletonModificationStack3D] that this modification is holding. This modification stack will then be executed when this modification is executed.
+ </description>
+ </method>
+ </methods>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/SkeletonModification3DTwoBoneIK.xml b/doc/classes/SkeletonModification3DTwoBoneIK.xml
new file mode 100644
index 0000000000..5c863367df
--- /dev/null
+++ b/doc/classes/SkeletonModification3DTwoBoneIK.xml
@@ -0,0 +1,193 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="SkeletonModification3DTwoBoneIK" inherits="SkeletonModification3D" version="4.0">
+ <brief_description>
+ A modification that moves two bones to reach the target.
+ </brief_description>
+ <description>
+ This [SkeletonModification3D] uses an algorithm typically called TwoBoneIK. This algorithm works by leveraging the law of cosigns and the lengths of the bones to figure out what rotation the bones currently have, and what rotation they need to make a complete triangle, where the first bone, the second bone, and the target form the three verticies of the triangle. Because the algorithm works by making a triangle, it can only opperate on two bones.
+ TwoBoneIK is great for arms, legs, and really any joints that can be represented by just two bones that bend to reach a target. This solver is more lightweight than [SkeletonModification3DFABRIK], but gives similar, natural looking results.
+ A [Node3D]-based node can be used to define the pole, or bend direction, allowing control over which direction the joint takes when bending to reach the target when the target is within reach.
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="get_auto_calculate_joint_length" qualifiers="const">
+ <return type="bool" />
+ <description>
+ Returns whether the TwoBoneIK modification will attempt to autocalculate the lengths of the two bones.
+ </description>
+ </method>
+ <method name="get_joint_one_bone_idx" qualifiers="const">
+ <return type="int" />
+ <description>
+ Returns the bone index of the first bone in the TwoBoneIK modification.
+ </description>
+ </method>
+ <method name="get_joint_one_bone_name" qualifiers="const">
+ <return type="String" />
+ <description>
+ Returns the name of the first bone in the TwoBoneIK modification.
+ </description>
+ </method>
+ <method name="get_joint_one_length" qualifiers="const">
+ <return type="float" />
+ <description>
+ Returns the length of the first bone in the TwoBoneIK modification.
+ </description>
+ </method>
+ <method name="get_joint_one_roll" qualifiers="const">
+ <return type="float" />
+ <description>
+ Returns the amount of roll/twist applied to the first bone in the TwoBoneIK modification.
+ </description>
+ </method>
+ <method name="get_joint_two_bone_idx" qualifiers="const">
+ <return type="int" />
+ <description>
+ Returns the bone index of the second bone in the TwoBoneIK modification.
+ </description>
+ </method>
+ <method name="get_joint_two_bone_name" qualifiers="const">
+ <return type="String" />
+ <description>
+ Returns the name of the second bone in the TwoBoneIK modification.
+ </description>
+ </method>
+ <method name="get_joint_two_length" qualifiers="const">
+ <return type="float" />
+ <description>
+ Returns the length of the second bone in the TwoBoneIK modification.
+ </description>
+ </method>
+ <method name="get_joint_two_roll" qualifiers="const">
+ <return type="float" />
+ <description>
+ Returns the amount of roll/twist applied to the second bone in the TwoBoneIK modification.
+ </description>
+ </method>
+ <method name="get_pole_node" qualifiers="const">
+ <return type="NodePath" />
+ <description>
+ Returns the node that is being used as the pole node for the TwoBoneIK modification, if a pole node has been set.
+ </description>
+ </method>
+ <method name="get_tip_node" qualifiers="const">
+ <return type="NodePath" />
+ <description>
+ Returns the node that is being used to calculate the tip position of the second bone in the TwoBoneIK modification, if a tip node has been set.
+ </description>
+ </method>
+ <method name="get_use_pole_node" qualifiers="const">
+ <return type="bool" />
+ <description>
+ Returns whether the TwoBoneIK modification will attempt to use the pole node to figure out which direction to bend, if a pole node has been set.
+ </description>
+ </method>
+ <method name="get_use_tip_node" qualifiers="const">
+ <return type="bool" />
+ <description>
+ Returns whether the TwoBoneIK modification will attempt to use the tip node to figure out the length and position of the tip of the second bone.
+ </description>
+ </method>
+ <method name="set_auto_calculate_joint_length">
+ <return type="void" />
+ <argument index="0" name="auto_calculate_joint_length" type="bool" />
+ <description>
+ If true, the TwoBoneIK modification will attempt to autocalculate the lengths of the bones being used. The first bone will be calculated by using the distance from the origin of the first bone to the origin of the second bone.
+ The second bone will be calculated either using the tip node if that setting is enabled, or by using the distances of the second bone's children. If the tip node is not enabled and the bone has no children, then the length cannot be autocalculated. In this case, the length will either have to be manually inputted or a tip node used to calculate the length.
+ </description>
+ </method>
+ <method name="set_joint_one_bone_idx">
+ <return type="void" />
+ <argument index="0" name="bone_idx" type="int" />
+ <description>
+ Sets the bone index, [code]bone_index[/code], of the first bone. When possible, this will also update the [code]bone_name[/code] of the first bone based on data provided by the [Skeleton3D].
+ </description>
+ </method>
+ <method name="set_joint_one_bone_name">
+ <return type="void" />
+ <argument index="0" name="bone_name" type="String" />
+ <description>
+ Sets the bone name, [code]bone_name[/code], of the first bone. When possible, this will also update the [code]bone_index[/code] of the first bone based on data provided by the [Skeleton3D].
+ </description>
+ </method>
+ <method name="set_joint_one_length">
+ <return type="void" />
+ <argument index="0" name="bone_length" type="float" />
+ <description>
+ Sets the length of the first bone in the TwoBoneIK modification.
+ </description>
+ </method>
+ <method name="set_joint_one_roll">
+ <return type="void" />
+ <argument index="0" name="roll" type="float" />
+ <description>
+ Sets the amount of roll/twist applied to the first bone in the TwoBoneIK modification.
+ </description>
+ </method>
+ <method name="set_joint_two_bone_idx">
+ <return type="void" />
+ <argument index="0" name="bone_idx" type="int" />
+ <description>
+ Sets the bone index, [code]bone_index[/code], of the second bone. When possible, this will also update the [code]bone_name[/code] of the second bone based on data provided by the [Skeleton3D].
+ </description>
+ </method>
+ <method name="set_joint_two_bone_name">
+ <return type="void" />
+ <argument index="0" name="bone_name" type="String" />
+ <description>
+ Sets the bone name, [code]bone_name[/code], of the second bone. When possible, this will also update the [code]bone_index[/code] of the second bone based on data provided by the [Skeleton3D].
+ </description>
+ </method>
+ <method name="set_joint_two_length">
+ <return type="void" />
+ <argument index="0" name="bone_length" type="float" />
+ <description>
+ Sets the length of the second bone in the TwoBoneIK modification.
+ </description>
+ </method>
+ <method name="set_joint_two_roll">
+ <return type="void" />
+ <argument index="0" name="roll" type="float" />
+ <description>
+ Sets the amount of roll/twist applied to the second bone in the TwoBoneIK modification.
+ </description>
+ </method>
+ <method name="set_pole_node">
+ <return type="void" />
+ <argument index="0" name="pole_nodepath" type="NodePath" />
+ <description>
+ Sets the node to be used as the for the pole of the TwoBoneIK. When a node is set and the modification is set to use the pole node, the TwoBoneIK modification will bend the nodes in the direction towards this node when the bones need to bend.
+ </description>
+ </method>
+ <method name="set_tip_node">
+ <return type="void" />
+ <argument index="0" name="tip_nodepath" type="NodePath" />
+ <description>
+ Sets the node to be used as the tip for the second bone. This is used to calculate the length and position of the end of the second bone in the TwoBoneIK modification.
+ [b]Note:[/b] The tip node should generally be a child node of a [BoneAttachment3D] node attached to the second bone, with the child node being offset so it is at the end of the bone.
+ </description>
+ </method>
+ <method name="set_use_pole_node">
+ <return type="void" />
+ <argument index="0" name="use_pole_node" type="bool" />
+ <description>
+ When [code]true[/code], the TwoBoneIK modification will bend the bones towards the pole node, if one has been set. This gives control over the direction the TwoBoneIK solver will bend, which is helpful for joints like elbows that only bend in certain directions.
+ </description>
+ </method>
+ <method name="set_use_tip_node">
+ <return type="void" />
+ <argument index="0" name="use_tip_node" type="bool" />
+ <description>
+ When [code]true[/code], the TwoBoneIK modification will use the tip node to calculate the distance and position of the end/tip of the second bone. This is the most stable solution for knowing the tip position and length of the second bone.
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="target_nodepath" type="NodePath" setter="set_target_node" getter="get_target_node" default="NodePath(&quot;&quot;)">
+ The NodePath to the node that is the target for the TwoBoneIK modification. This node is what the modification will attempt to rotate the bones to reach.
+ </member>
+ </members>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/SkeletonModificationStack3D.xml b/doc/classes/SkeletonModificationStack3D.xml
new file mode 100644
index 0000000000..4e5e9d72d8
--- /dev/null
+++ b/doc/classes/SkeletonModificationStack3D.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="SkeletonModificationStack3D" inherits="Resource" version="4.0">
+ <brief_description>
+ A resource that holds a stack of [SkeletonModification3D]s.
+ </brief_description>
+ <description>
+ This resource is used by the Skeleton and holds a stack of [SkeletonModification3D]s. The SkeletonModificationStack3D controls the order of the modifications, which controls how they are applied. Modification order is especially important for full-body IK setups, as you need to execute the modifications in the correct order to get the desired results. For example, you want to execute a modification on the spine [i]before[/i] the arms on a humanoid skeleton.
+ Additionally, the SkeletonModificationStack3D also controls how strongly the modifications are applied to the [Skeleton3D] node.
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="add_modification">
+ <return type="void" />
+ <argument index="0" name="modification" type="SkeletonModification3D" />
+ <description>
+ Adds the passed-in [SkeletonModification3D] to the stack.
+ </description>
+ </method>
+ <method name="delete_modification">
+ <return type="void" />
+ <argument index="0" name="mod_idx" type="int" />
+ <description>
+ Deletes the [SkeletonModification3D] at the index position [code]mod_idx[/code], if it exists.
+ </description>
+ </method>
+ <method name="enable_all_modifications">
+ <return type="void" />
+ <argument index="0" name="enabled" type="bool" />
+ <description>
+ Enables all [SkeletonModification3D]s in the stack.
+ </description>
+ </method>
+ <method name="execute">
+ <return type="void" />
+ <argument index="0" name="delta" type="float" />
+ <argument index="1" name="execution_mode" type="int" />
+ <description>
+ Executes all of the [SkeletonModification3D]s in the stack that use the same execution mode as the passed-in [code]execution_mode[/code], starting from index [code]0[/code] to [member modification_count].
+ [b]Note:[/b] The order of the modifications can matter depending on the modifications. For example, modifications on a spine should operate before modifications on the arms in order to get proper results.
+ </description>
+ </method>
+ <method name="get_is_setup" qualifiers="const">
+ <return type="bool" />
+ <description>
+ Returns a boolean that indiciates whether the modification stack is setup and can execute.
+ </description>
+ </method>
+ <method name="get_modification" qualifiers="const">
+ <return type="SkeletonModification3D" />
+ <argument index="0" name="mod_idx" type="int" />
+ <description>
+ Returns the [SkeletonModification3D] at the passed-in index, [code]mod_idx[/code].
+ </description>
+ </method>
+ <method name="get_skeleton" qualifiers="const">
+ <return type="Skeleton3D" />
+ <description>
+ Returns the [Skeleton3D] node that the SkeletonModificationStack3D is bound to.
+ </description>
+ </method>
+ <method name="set_modification">
+ <return type="void" />
+ <argument index="0" name="mod_idx" type="int" />
+ <argument index="1" name="modification" type="SkeletonModification3D" />
+ <description>
+ Sets the modification at [code]mod_idx[/code] to the passed-in modification, [code]modification[/code].
+ </description>
+ </method>
+ <method name="setup">
+ <return type="void" />
+ <description>
+ Sets up the modification stack so it can execute. This function should be called by [Skeleton3D] and shouldn't be called unless you know what you are doing.
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="enabled" type="bool" setter="set_enabled" getter="get_enabled" default="false">
+ When true, the modification's in the stack will be called. This is handled automatically through the [Skeleton3D] node.
+ </member>
+ <member name="modification_count" type="int" setter="set_modification_count" getter="get_modification_count" default="0">
+ The amount of modifications in the stack.
+ </member>
+ <member name="strength" type="float" setter="set_strength" getter="get_strength" default="1.0">
+ The interpolation strength of the modifications in stack. A value of [code]0[/code] will make it where the modifications are not applied, a strength of [code]0.5[/code] will be half applied, and a strength of [code]1[/code] will allow the modifications to be fully applied and override the skeleton bone poses.
+ </member>
+ </members>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/VisualShaderNodeColorFunc.xml b/doc/classes/VisualShaderNodeColorFunc.xml
index b9abf85802..8d410104b8 100644
--- a/doc/classes/VisualShaderNodeColorFunc.xml
+++ b/doc/classes/VisualShaderNodeColorFunc.xml
@@ -36,5 +36,8 @@
return vec3(r, g, b);
[/codeblock]
</constant>
+ <constant name="FUNC_MAX" value="2" enum="Function">
+ Represents the size of the [enum Function] enum.
+ </constant>
</constants>
</class>
diff --git a/doc/classes/VisualShaderNodeColorOp.xml b/doc/classes/VisualShaderNodeColorOp.xml
index 629e84d47b..55b006b098 100644
--- a/doc/classes/VisualShaderNodeColorOp.xml
+++ b/doc/classes/VisualShaderNodeColorOp.xml
@@ -94,5 +94,8 @@
}
[/codeblock]
</constant>
+ <constant name="OP_MAX" value="9" enum="Operator">
+ Represents the size of the [enum Operator] enum.
+ </constant>
</constants>
</class>
diff --git a/doc/classes/VisualShaderNodeCompare.xml b/doc/classes/VisualShaderNodeCompare.xml
index 9c2331edea..96bb3df494 100644
--- a/doc/classes/VisualShaderNodeCompare.xml
+++ b/doc/classes/VisualShaderNodeCompare.xml
@@ -37,6 +37,9 @@
<constant name="CTYPE_TRANSFORM" value="4" enum="ComparisonType">
A transform ([code]mat4[/code]) type.
</constant>
+ <constant name="CTYPE_MAX" value="5" enum="ComparisonType">
+ Represents the size of the [enum ComparisonType] enum.
+ </constant>
<constant name="FUNC_EQUAL" value="0" enum="Function">
Comparison for equality ([code]a == b[/code]).
</constant>
@@ -55,11 +58,17 @@
<constant name="FUNC_LESS_THAN_EQUAL" value="5" enum="Function">
Comparison for less than or equal ([code]a &lt;= b[/code]). Cannot be used if [member type] set to [constant CTYPE_BOOLEAN] or [constant CTYPE_TRANSFORM].
</constant>
+ <constant name="FUNC_MAX" value="6" enum="Function">
+ Represents the size of the [enum Function] enum.
+ </constant>
<constant name="COND_ALL" value="0" enum="Condition">
The result will be true if all of component in vector satisfy the comparison condition.
</constant>
<constant name="COND_ANY" value="1" enum="Condition">
The result will be true if any of component in vector satisfy the comparison condition.
</constant>
+ <constant name="COND_MAX" value="2" enum="Condition">
+ Represents the size of the [enum Condition] enum.
+ </constant>
</constants>
</class>
diff --git a/doc/classes/VisualShaderNodeCubemap.xml b/doc/classes/VisualShaderNodeCubemap.xml
index 13b367e8f2..4a5f58261d 100644
--- a/doc/classes/VisualShaderNodeCubemap.xml
+++ b/doc/classes/VisualShaderNodeCubemap.xml
@@ -28,6 +28,9 @@
<constant name="SOURCE_PORT" value="1" enum="Source">
Use the [Cubemap] sampler reference passed via the [code]samplerCube[/code] port. If this is set to [member source], the [member cube_map] texture is ignored.
</constant>
+ <constant name="SOURCE_MAX" value="2" enum="Source">
+ Represents the size of the [enum Source] enum.
+ </constant>
<constant name="TYPE_DATA" value="0" enum="TextureType">
No hints are added to the uniform declaration.
</constant>
@@ -37,5 +40,8 @@
<constant name="TYPE_NORMAL_MAP" value="2" enum="TextureType">
Adds [code]hint_normal[/code] as hint to the uniform declaration, which internally converts the texture for proper usage as normal map.
</constant>
+ <constant name="TYPE_MAX" value="3" enum="TextureType">
+ Represents the size of the [enum TextureType] enum.
+ </constant>
</constants>
</class>
diff --git a/doc/classes/VisualShaderNodeFloatFunc.xml b/doc/classes/VisualShaderNodeFloatFunc.xml
index bb7486e8cf..ff499d25a6 100644
--- a/doc/classes/VisualShaderNodeFloatFunc.xml
+++ b/doc/classes/VisualShaderNodeFloatFunc.xml
@@ -112,5 +112,8 @@
<constant name="FUNC_ONEMINUS" value="31" enum="Function">
Subtracts scalar [code]x[/code] from 1 (i.e. [code]1 - x[/code]).
</constant>
+ <constant name="FUNC_MAX" value="32" enum="Function">
+ Represents the size of the [enum Function] enum.
+ </constant>
</constants>
</class>
diff --git a/doc/classes/VisualShaderNodeFloatOp.xml b/doc/classes/VisualShaderNodeFloatOp.xml
index 2c9ebabb89..ba000f258b 100644
--- a/doc/classes/VisualShaderNodeFloatOp.xml
+++ b/doc/classes/VisualShaderNodeFloatOp.xml
@@ -46,5 +46,8 @@
<constant name="OP_STEP" value="9" enum="Operator">
Generates a step function by comparing [code]b[/code](x) to [code]a[/code](edge). Returns 0.0 if [code]x[/code] is smaller than [code]edge[/code] and otherwise 1.0. Translates to [code]step(a, b)[/code] in the Godot Shader Language.
</constant>
+ <constant name="OP_ENUM_SIZE" value="10" enum="Operator">
+ Represents the size of the [enum Operator] enum.
+ </constant>
</constants>
</class>
diff --git a/doc/classes/VisualShaderNodeFloatUniform.xml b/doc/classes/VisualShaderNodeFloatUniform.xml
index 705d5e8796..fcfd78dbea 100644
--- a/doc/classes/VisualShaderNodeFloatUniform.xml
+++ b/doc/classes/VisualShaderNodeFloatUniform.xml
@@ -40,5 +40,8 @@
<constant name="HINT_RANGE_STEP" value="2" enum="Hint">
A range hint for scalar value with step, which limits possible input values between [member min] and [member max], with a step (increment) of [member step]). Translated to [code]hint_range(min, max, step)[/code] in shader code.
</constant>
+ <constant name="HINT_MAX" value="3" enum="Hint">
+ Represents the size of the [enum Hint] enum.
+ </constant>
</constants>
</class>
diff --git a/doc/classes/VisualShaderNodeIntFunc.xml b/doc/classes/VisualShaderNodeIntFunc.xml
index a9f4144a01..358a3f3512 100644
--- a/doc/classes/VisualShaderNodeIntFunc.xml
+++ b/doc/classes/VisualShaderNodeIntFunc.xml
@@ -25,5 +25,8 @@
<constant name="FUNC_SIGN" value="2" enum="Function">
Extracts the sign of the parameter. Translates to [code]sign(x)[/code] in the Godot Shader Language.
</constant>
+ <constant name="FUNC_MAX" value="3" enum="Function">
+ Represents the size of the [enum Function] enum.
+ </constant>
</constants>
</class>
diff --git a/doc/classes/VisualShaderNodeIntOp.xml b/doc/classes/VisualShaderNodeIntOp.xml
index fc9a0a9a0a..2d55cdaf78 100644
--- a/doc/classes/VisualShaderNodeIntOp.xml
+++ b/doc/classes/VisualShaderNodeIntOp.xml
@@ -37,5 +37,8 @@
<constant name="OP_MIN" value="6" enum="Operator">
Returns the lesser of two numbers. Translates to [code]max(a, b)[/code] in the Godot Shader Language.
</constant>
+ <constant name="OP_ENUM_SIZE" value="7" enum="Operator">
+ Represents the size of the [enum Operator] enum.
+ </constant>
</constants>
</class>
diff --git a/doc/classes/VisualShaderNodeIntUniform.xml b/doc/classes/VisualShaderNodeIntUniform.xml
index e39eba865b..4070553884 100644
--- a/doc/classes/VisualShaderNodeIntUniform.xml
+++ b/doc/classes/VisualShaderNodeIntUniform.xml
@@ -40,5 +40,8 @@
<constant name="HINT_RANGE_STEP" value="2" enum="Hint">
A range hint for scalar value with step, which limits possible input values between [member min] and [member max], with a step (increment) of [member step]). Translated to [code]hint_range(min, max, step)[/code] in shader code.
</constant>
+ <constant name="HINT_MAX" value="3" enum="Hint">
+ Represents the size of the [enum Hint] enum.
+ </constant>
</constants>
</class>
diff --git a/doc/classes/VisualShaderNodeIs.xml b/doc/classes/VisualShaderNodeIs.xml
index b767a9638e..f46267677e 100644
--- a/doc/classes/VisualShaderNodeIs.xml
+++ b/doc/classes/VisualShaderNodeIs.xml
@@ -22,5 +22,8 @@
<constant name="FUNC_IS_NAN" value="1" enum="Function">
Comparison with [code]NaN[/code] (Not a Number; denotes invalid numeric results, e.g. division by zero).
</constant>
+ <constant name="FUNC_MAX" value="2" enum="Function">
+ Represents the size of the [enum Function] enum.
+ </constant>
</constants>
</class>
diff --git a/doc/classes/VisualShaderNodeParticleAccelerator.xml b/doc/classes/VisualShaderNodeParticleAccelerator.xml
index 82aae1df63..2f3f58c0cf 100644
--- a/doc/classes/VisualShaderNodeParticleAccelerator.xml
+++ b/doc/classes/VisualShaderNodeParticleAccelerator.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualShaderNodeParticleAccelerator" inherits="VisualShaderNodeOutput" version="4.0">
+<class name="VisualShaderNodeParticleAccelerator" inherits="VisualShaderNode" version="4.0">
<brief_description>
</brief_description>
<description>
@@ -20,6 +20,7 @@
<constant name="MODE_TANGENTIAL" value="2" enum="Mode">
</constant>
<constant name="MODE_MAX" value="3" enum="Mode">
+ Represents the size of the [enum Mode] enum.
</constant>
</constants>
</class>
diff --git a/doc/classes/VisualShaderNodeParticleRandomness.xml b/doc/classes/VisualShaderNodeParticleRandomness.xml
index f19723a663..75736992ee 100644
--- a/doc/classes/VisualShaderNodeParticleRandomness.xml
+++ b/doc/classes/VisualShaderNodeParticleRandomness.xml
@@ -18,6 +18,7 @@
<constant name="OP_TYPE_VECTOR" value="1" enum="OpType">
</constant>
<constant name="OP_TYPE_MAX" value="2" enum="OpType">
+ Represents the size of the [enum OpType] enum.
</constant>
</constants>
</class>
diff --git a/doc/classes/VisualShaderNodeSample3D.xml b/doc/classes/VisualShaderNodeSample3D.xml
index cf6933ab55..82bcac5f69 100644
--- a/doc/classes/VisualShaderNodeSample3D.xml
+++ b/doc/classes/VisualShaderNodeSample3D.xml
@@ -22,5 +22,8 @@
<constant name="SOURCE_PORT" value="1" enum="Source">
Use the uniform texture from sampler port.
</constant>
+ <constant name="SOURCE_MAX" value="2" enum="Source">
+ Represents the size of the [enum Source] enum.
+ </constant>
</constants>
</class>
diff --git a/doc/classes/VisualShaderNodeScalarDerivativeFunc.xml b/doc/classes/VisualShaderNodeScalarDerivativeFunc.xml
index 33777c1e49..305586ac49 100644
--- a/doc/classes/VisualShaderNodeScalarDerivativeFunc.xml
+++ b/doc/classes/VisualShaderNodeScalarDerivativeFunc.xml
@@ -25,5 +25,8 @@
<constant name="FUNC_Y" value="2" enum="Function">
Derivative in [code]y[/code] using local differencing.
</constant>
+ <constant name="FUNC_MAX" value="3" enum="Function">
+ Represents the size of the [enum Function] enum.
+ </constant>
</constants>
</class>
diff --git a/doc/classes/VisualShaderNodeTexture.xml b/doc/classes/VisualShaderNodeTexture.xml
index 8fa71b490d..17c079f385 100644
--- a/doc/classes/VisualShaderNodeTexture.xml
+++ b/doc/classes/VisualShaderNodeTexture.xml
@@ -40,6 +40,9 @@
<constant name="SOURCE_PORT" value="5" enum="Source">
Use the texture provided in the input port for this function.
</constant>
+ <constant name="SOURCE_MAX" value="6" enum="Source">
+ Represents the size of the [enum Source] enum.
+ </constant>
<constant name="TYPE_DATA" value="0" enum="TextureType">
No hints are added to the uniform declaration.
</constant>
@@ -49,5 +52,8 @@
<constant name="TYPE_NORMAL_MAP" value="2" enum="TextureType">
Adds [code]hint_normal[/code] as hint to the uniform declaration, which internally converts the texture for proper usage as normal map.
</constant>
+ <constant name="TYPE_MAX" value="3" enum="TextureType">
+ Represents the size of the [enum TextureType] enum.
+ </constant>
</constants>
</class>
diff --git a/doc/classes/VisualShaderNodeTextureUniform.xml b/doc/classes/VisualShaderNodeTextureUniform.xml
index 5a7474cca1..492c6010df 100644
--- a/doc/classes/VisualShaderNodeTextureUniform.xml
+++ b/doc/classes/VisualShaderNodeTextureUniform.xml
@@ -31,11 +31,17 @@
<constant name="TYPE_ANISO" value="3" enum="TextureType">
Adds [code]hint_aniso[/code] as hint to the uniform declaration to use for a flowmap.
</constant>
+ <constant name="TYPE_MAX" value="4" enum="TextureType">
+ Represents the size of the [enum TextureType] enum.
+ </constant>
<constant name="COLOR_DEFAULT_WHITE" value="0" enum="ColorDefault">
Defaults to white color.
</constant>
<constant name="COLOR_DEFAULT_BLACK" value="1" enum="ColorDefault">
Defaults to black color.
</constant>
+ <constant name="COLOR_DEFAULT_MAX" value="2" enum="ColorDefault">
+ Represents the size of the [enum ColorDefault] enum.
+ </constant>
</constants>
</class>
diff --git a/doc/classes/VisualShaderNodeTransformFunc.xml b/doc/classes/VisualShaderNodeTransformFunc.xml
index 41a58e1458..51b1100e22 100644
--- a/doc/classes/VisualShaderNodeTransformFunc.xml
+++ b/doc/classes/VisualShaderNodeTransformFunc.xml
@@ -22,5 +22,8 @@
<constant name="FUNC_TRANSPOSE" value="1" enum="Function">
Perform the transpose operation on the [Transform3D] matrix.
</constant>
+ <constant name="FUNC_MAX" value="2" enum="Function">
+ Represents the size of the [enum Function] enum.
+ </constant>
</constants>
</class>
diff --git a/doc/classes/VisualShaderNodeTransformOp.xml b/doc/classes/VisualShaderNodeTransformOp.xml
index a628fa3e8d..02debd890f 100644
--- a/doc/classes/VisualShaderNodeTransformOp.xml
+++ b/doc/classes/VisualShaderNodeTransformOp.xml
@@ -43,7 +43,7 @@
<constant name="OP_B_DIV_A" value="8" enum="Operator">
Divides the transform [code]b[/code] by the transform [code]a[/code].
</constant>
- <constant name="OP_LIMITER" value="9" enum="Operator">
+ <constant name="OP_MAX" value="9" enum="Operator">
Represents the size of the [enum Operator] enum.
</constant>
</constants>
diff --git a/doc/classes/VisualShaderNodeTransformVecMult.xml b/doc/classes/VisualShaderNodeTransformVecMult.xml
index 2c9c115d9c..d8f7ebbd55 100644
--- a/doc/classes/VisualShaderNodeTransformVecMult.xml
+++ b/doc/classes/VisualShaderNodeTransformVecMult.xml
@@ -28,5 +28,8 @@
<constant name="OP_3x3_BxA" value="3" enum="Operator">
Multiplies vector [code]b[/code] by the transform [code]a[/code], skipping the last row and column of the transform.
</constant>
+ <constant name="OP_MAX" value="4" enum="Operator">
+ Represents the size of the [enum Operator] enum.
+ </constant>
</constants>
</class>
diff --git a/doc/classes/VisualShaderNodeUniform.xml b/doc/classes/VisualShaderNodeUniform.xml
index 83261344bd..561d87af45 100644
--- a/doc/classes/VisualShaderNodeUniform.xml
+++ b/doc/classes/VisualShaderNodeUniform.xml
@@ -24,5 +24,8 @@
</constant>
<constant name="QUAL_INSTANCE" value="2" enum="Qualifier">
</constant>
+ <constant name="QUAL_MAX" value="3" enum="Qualifier">
+ Represents the size of the [enum Qualifier] enum.
+ </constant>
</constants>
</class>
diff --git a/doc/classes/VisualShaderNodeVectorDerivativeFunc.xml b/doc/classes/VisualShaderNodeVectorDerivativeFunc.xml
index 859c47bc33..9fd8ba2806 100644
--- a/doc/classes/VisualShaderNodeVectorDerivativeFunc.xml
+++ b/doc/classes/VisualShaderNodeVectorDerivativeFunc.xml
@@ -25,5 +25,8 @@
<constant name="FUNC_Y" value="2" enum="Function">
Derivative in [code]y[/code] using local differencing.
</constant>
+ <constant name="FUNC_MAX" value="3" enum="Function">
+ Represents the size of the [enum Function] enum.
+ </constant>
</constants>
</class>
diff --git a/doc/classes/VisualShaderNodeVectorFunc.xml b/doc/classes/VisualShaderNodeVectorFunc.xml
index cbda3dfb46..79bf3f6a07 100644
--- a/doc/classes/VisualShaderNodeVectorFunc.xml
+++ b/doc/classes/VisualShaderNodeVectorFunc.xml
@@ -121,5 +121,8 @@
<constant name="FUNC_ONEMINUS" value="34" enum="Function">
Returns [code]1.0 - vector[/code].
</constant>
+ <constant name="FUNC_MAX" value="35" enum="Function">
+ Represents the size of the [enum Function] enum.
+ </constant>
</constants>
</class>
diff --git a/doc/classes/VisualShaderNodeVectorOp.xml b/doc/classes/VisualShaderNodeVectorOp.xml
index d56c012f8f..263485d38e 100644
--- a/doc/classes/VisualShaderNodeVectorOp.xml
+++ b/doc/classes/VisualShaderNodeVectorOp.xml
@@ -52,5 +52,8 @@
<constant name="OP_STEP" value="11" enum="Operator">
Vector step operator. Returns [code]0.0[/code] if [code]a[/code] is smaller than [code]b[/code] and [code]1.0[/code] otherwise.
</constant>
+ <constant name="OP_ENUM_SIZE" value="12" enum="Operator">
+ Represents the size of the [enum Operator] enum.
+ </constant>
</constants>
</class>
diff --git a/doc/classes/LineShape2D.xml b/doc/classes/WorldMarginShape2D.xml
index 434e6fba8e..1839ab16ad 100644
--- a/doc/classes/LineShape2D.xml
+++ b/doc/classes/WorldMarginShape2D.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="LineShape2D" inherits="Shape2D" version="4.0">
+<class name="WorldMarginShape2D" inherits="Shape2D" version="4.0">
<brief_description>
Line shape for 2D collisions.
</brief_description>
diff --git a/editor/animation_bezier_editor.cpp b/editor/animation_bezier_editor.cpp
index 6ef36f16f5..ea2ae53e82 100644
--- a/editor/animation_bezier_editor.cpp
+++ b/editor/animation_bezier_editor.cpp
@@ -178,7 +178,7 @@ void AnimationBezierTrackEdit::_draw_track(int p_track, const Color &p_color) {
}
if (lines.size() >= 2) {
- draw_multiline(lines, p_color);
+ draw_multiline(lines, p_color, Math::round(EDSCALE));
}
}
}
@@ -212,7 +212,7 @@ void AnimationBezierTrackEdit::_draw_line_clipped(const Vector2 &p_from, const V
from = from.lerp(to, c);
}
- draw_line(from, to, p_color);
+ draw_line(from, to, p_color, Math::round(EDSCALE));
}
void AnimationBezierTrackEdit::_notification(int p_what) {
@@ -244,7 +244,7 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
if (has_focus()) {
Color accent = get_theme_color(SNAME("accent_color"), SNAME("Editor"));
accent.a *= 0.7;
- draw_rect(Rect2(Point2(), get_size()), accent, false);
+ draw_rect(Rect2(Point2(), get_size()), accent, false, Math::round(EDSCALE));
}
Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Label"));
@@ -255,11 +255,11 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
Color linecolor = color;
linecolor.a = 0.2;
- draw_line(Point2(limit, 0), Point2(limit, get_size().height), linecolor);
+ draw_line(Point2(limit, 0), Point2(limit, get_size().height), linecolor, Math::round(EDSCALE));
int right_limit = get_size().width - timeline->get_buttons_width();
- draw_line(Point2(right_limit, 0), Point2(right_limit, get_size().height), linecolor);
+ draw_line(Point2(right_limit, 0), Point2(right_limit, get_size().height), linecolor, Math::round(EDSCALE));
Ref<Texture2D> close_icon = get_theme_icon(SNAME("Close"), SNAME("EditorIcons"));
@@ -387,7 +387,7 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
if (!first && iv != prev_iv) {
Color lc = linecolor;
lc.a *= 0.5;
- draw_line(Point2(limit, i), Point2(right_limit, i), lc);
+ draw_line(Point2(limit, i), Point2(right_limit, i), lc, Math::round(EDSCALE));
Color c = color;
c.a *= 0.5;
draw_string(font, Point2(limit + 8, i - 2), TS->format_number(rtos(Math::snapped((iv + 1) * scale, step))), HALIGN_LEFT, -1, font_size, c);
@@ -461,7 +461,7 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
ep.point_rect.size = bezier_icon->get_size();
if (selection.has(i)) {
draw_texture(selected_icon, ep.point_rect.position);
- draw_string(font, ep.point_rect.position + Vector2(8, -font->get_height(font_size) - 4), TTR("Time:") + " " + TS->format_number(rtos(Math::snapped(offset, 0.001))), HALIGN_LEFT, -1, font_size, accent);
+ draw_string(font, ep.point_rect.position + Vector2(8, -font->get_height(font_size) - 8), TTR("Time:") + " " + TS->format_number(rtos(Math::snapped(offset, 0.001))), HALIGN_LEFT, -1, font_size, accent);
draw_string(font, ep.point_rect.position + Vector2(8, -8), TTR("Value:") + " " + TS->format_number(rtos(Math::snapped(value, 0.001))), HALIGN_LEFT, -1, font_size, accent);
} else {
draw_texture(bezier_icon, ep.point_rect.position);
@@ -485,8 +485,6 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
}
if (box_selecting) {
- Color bs = accent;
- bs.a *= 0.5;
Vector2 bs_from = box_selection_from;
Vector2 bs_to = box_selection_to;
if (bs_from.x > bs_to.x) {
@@ -495,7 +493,14 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
if (bs_from.y > bs_to.y) {
SWAP(bs_from.y, bs_to.y);
}
- draw_rect(Rect2(bs_from, bs_to - bs_from), bs);
+ draw_rect(
+ Rect2(bs_from, bs_to - bs_from),
+ get_theme_color("box_selection_fill_color", "Editor"));
+ draw_rect(
+ Rect2(bs_from, bs_to - bs_from),
+ get_theme_color("box_selection_stroke_color", "Editor"),
+ false,
+ Math::round(EDSCALE));
}
}
}
@@ -618,9 +623,9 @@ void AnimationBezierTrackEdit::_gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN) {
- float v_zoom_orig = v_zoom;
+ const float v_zoom_orig = v_zoom;
if (mb->is_command_pressed()) {
- timeline->get_zoom()->set_value(timeline->get_zoom()->get_value() * 1.05);
+ timeline->get_zoom()->set_value(timeline->get_zoom()->get_value() / 1.05);
} else {
if (v_zoom < 100000) {
v_zoom *= 1.2;
@@ -631,9 +636,9 @@ void AnimationBezierTrackEdit::_gui_input(const Ref<InputEvent> &p_event) {
}
if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_WHEEL_UP) {
- float v_zoom_orig = v_zoom;
+ const float v_zoom_orig = v_zoom;
if (mb->is_command_pressed()) {
- timeline->get_zoom()->set_value(timeline->get_zoom()->get_value() / 1.05);
+ timeline->get_zoom()->set_value(timeline->get_zoom()->get_value() * 1.05);
} else {
if (v_zoom > 0.000001) {
v_zoom /= 1.2;
@@ -974,7 +979,7 @@ void AnimationBezierTrackEdit::_gui_input(const Ref<InputEvent> &p_event) {
if (moving_handle != 0 && mm.is_valid()) {
float y = (get_size().height / 2 - mm->get_position().y) * v_zoom + v_scroll;
- float x = ((mm->get_position().x - timeline->get_name_limit()) / timeline->get_zoom_scale()) + timeline->get_value();
+ float x = editor->snap_time((mm->get_position().x - timeline->get_name_limit()) / timeline->get_zoom_scale()) + timeline->get_value();
Vector2 key_pos = Vector2(animation->track_get_key_time(track, moving_handle_key), animation->bezier_track_get_key_value(track, moving_handle_key));
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index 964c37906f..ff2818f027 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -5642,6 +5642,11 @@ float AnimationTrackEditor::snap_time(float p_value, bool p_relative) {
snap_increment = step->get_value();
}
+ if (Input::get_singleton()->is_key_pressed(KEY_SHIFT)) {
+ // Use more precise snapping when holding Shift.
+ snap_increment *= 0.25;
+ }
+
if (p_relative) {
double rel = Math::fmod(timeline->get_value(), snap_increment);
p_value = Math::snapped(p_value + rel, snap_increment) - rel;
diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp
index a5943c952b..48f2be450b 100644
--- a/editor/code_editor.cpp
+++ b/editor/code_editor.cpp
@@ -926,38 +926,55 @@ bool CodeTextEditor::_add_font_size(int p_delta) {
}
void CodeTextEditor::update_editor_settings() {
- completion_font_color = EDITOR_GET("text_editor/highlighting/completion_font_color");
- completion_string_color = EDITOR_GET("text_editor/highlighting/string_color");
- completion_comment_color = EDITOR_GET("text_editor/highlighting/comment_color");
-
- text_editor->set_highlight_all_occurrences(EditorSettings::get_singleton()->get("text_editor/highlighting/highlight_all_occurrences"));
- text_editor->set_highlight_current_line(EditorSettings::get_singleton()->get("text_editor/highlighting/highlight_current_line"));
- text_editor->set_indent_using_spaces(EditorSettings::get_singleton()->get("text_editor/indent/type"));
- text_editor->set_indent_size(EditorSettings::get_singleton()->get("text_editor/indent/size"));
- text_editor->set_auto_indent_enabled(EditorSettings::get_singleton()->get("text_editor/indent/auto_indent"));
- text_editor->set_draw_tabs(EditorSettings::get_singleton()->get("text_editor/indent/draw_tabs"));
- text_editor->set_draw_spaces(EditorSettings::get_singleton()->get("text_editor/indent/draw_spaces"));
- text_editor->set_smooth_scroll_enabled(EditorSettings::get_singleton()->get("text_editor/navigation/smooth_scrolling"));
- text_editor->set_v_scroll_speed(EditorSettings::get_singleton()->get("text_editor/navigation/v_scroll_speed"));
- text_editor->set_draw_minimap(EditorSettings::get_singleton()->get("text_editor/navigation/show_minimap"));
- text_editor->set_minimap_width((int)EditorSettings::get_singleton()->get("text_editor/navigation/minimap_width") * EDSCALE);
- text_editor->set_draw_line_numbers(EditorSettings::get_singleton()->get("text_editor/appearance/show_line_numbers"));
- text_editor->set_line_numbers_zero_padded(EditorSettings::get_singleton()->get("text_editor/appearance/line_numbers_zero_padded"));
- text_editor->set_draw_bookmarks_gutter(EditorSettings::get_singleton()->get("text_editor/appearance/show_bookmark_gutter"));
- text_editor->set_line_folding_enabled(EditorSettings::get_singleton()->get("text_editor/appearance/code_folding"));
- text_editor->set_draw_fold_gutter(EditorSettings::get_singleton()->get("text_editor/appearance/code_folding"));
- text_editor->set_line_wrapping_mode((TextEdit::LineWrappingMode)EditorSettings::get_singleton()->get("text_editor/appearance/word_wrap").operator int());
- text_editor->set_scroll_past_end_of_file_enabled(EditorSettings::get_singleton()->get("text_editor/cursor/scroll_past_end_of_file"));
- text_editor->set_caret_type((TextEdit::CaretType)EditorSettings::get_singleton()->get("text_editor/cursor/type").operator int());
- text_editor->set_caret_blink_enabled(EditorSettings::get_singleton()->get("text_editor/cursor/caret_blink"));
- text_editor->set_caret_blink_speed(EditorSettings::get_singleton()->get("text_editor/cursor/caret_blink_speed"));
+ // Theme: Highlighting
+ completion_font_color = EDITOR_GET("text_editor/theme/highlighting/completion_font_color");
+ completion_string_color = EDITOR_GET("text_editor/theme/highlighting/string_color");
+ completion_comment_color = EDITOR_GET("text_editor/theme/highlighting/comment_color");
+
+ // Appearance: Caret
+ text_editor->set_caret_type((TextEdit::CaretType)EditorSettings::get_singleton()->get("text_editor/appearance/caret/type").operator int());
+ text_editor->set_caret_blink_enabled(EditorSettings::get_singleton()->get("text_editor/appearance/caret/caret_blink"));
+ text_editor->set_caret_blink_speed(EditorSettings::get_singleton()->get("text_editor/appearance/caret/caret_blink_speed"));
+ text_editor->set_highlight_current_line(EditorSettings::get_singleton()->get("text_editor/appearance/caret/highlight_current_line"));
+ text_editor->set_highlight_all_occurrences(EditorSettings::get_singleton()->get("text_editor/appearance/caret/highlight_all_occurrences"));
+
+ // Appearance: Gutters
+ text_editor->set_draw_line_numbers(EditorSettings::get_singleton()->get("text_editor/appearance/gutters/show_line_numbers"));
+ text_editor->set_line_numbers_zero_padded(EditorSettings::get_singleton()->get("text_editor/appearance/gutters/line_numbers_zero_padded"));
+ text_editor->set_draw_bookmarks_gutter(EditorSettings::get_singleton()->get("text_editor/appearance/gutters/show_bookmark_gutter"));
+
+ // Appearance: Minimap
+ text_editor->set_draw_minimap(EditorSettings::get_singleton()->get("text_editor/appearance/minimap/show_minimap"));
+ text_editor->set_minimap_width((int)EditorSettings::get_singleton()->get("text_editor/appearance/minimap/minimap_width") * EDSCALE);
+
+ // Appearance: Lines
+ text_editor->set_line_folding_enabled(EditorSettings::get_singleton()->get("text_editor/appearance/lines/code_folding"));
+ text_editor->set_draw_fold_gutter(EditorSettings::get_singleton()->get("text_editor/appearance/lines/code_folding"));
+ text_editor->set_line_wrapping_mode((TextEdit::LineWrappingMode)EditorSettings::get_singleton()->get("text_editor/appearance/lines/word_wrap").operator int());
+
+ // Appearance: Whitespace
+ text_editor->set_draw_tabs(EditorSettings::get_singleton()->get("text_editor/appearance/whitespace/draw_tabs"));
+ text_editor->set_draw_spaces(EditorSettings::get_singleton()->get("text_editor/appearance/whitespace/draw_spaces"));
+
+ // Behavior: Navigation
+ text_editor->set_scroll_past_end_of_file_enabled(EditorSettings::get_singleton()->get("text_editor/behavior/navigation/scroll_past_end_of_file"));
+ text_editor->set_smooth_scroll_enabled(EditorSettings::get_singleton()->get("text_editor/behavior/navigation/smooth_scrolling"));
+ text_editor->set_v_scroll_speed(EditorSettings::get_singleton()->get("text_editor/behavior/navigation/v_scroll_speed"));
+
+ // Behavior: indent
+ text_editor->set_indent_using_spaces(EditorSettings::get_singleton()->get("text_editor/behavior/indent/type"));
+ text_editor->set_indent_size(EditorSettings::get_singleton()->get("text_editor/behavior/indent/size"));
+ text_editor->set_auto_indent_enabled(EditorSettings::get_singleton()->get("text_editor/behavior/indent/auto_indent"));
+
+ // Completion
text_editor->set_auto_brace_completion_enabled(EditorSettings::get_singleton()->get("text_editor/completion/auto_brace_complete"));
- if (EditorSettings::get_singleton()->get("text_editor/appearance/show_line_length_guidelines")) {
+ // Appearance: Guidelines
+ if (EditorSettings::get_singleton()->get("text_editor/appearance/guidelines/show_line_length_guidelines")) {
TypedArray<int> guideline_cols;
- guideline_cols.append(EditorSettings::get_singleton()->get("text_editor/appearance/line_length_guideline_hard_column"));
- if (EditorSettings::get_singleton()->get("text_editor/appearance/line_length_guideline_soft_column") != guideline_cols[0]) {
- guideline_cols.append(EditorSettings::get_singleton()->get("text_editor/appearance/line_length_guideline_soft_column"));
+ guideline_cols.append(EditorSettings::get_singleton()->get("text_editor/appearance/guidelines/line_length_guideline_hard_column"));
+ if (EditorSettings::get_singleton()->get("text_editor/appearance/guidelines/line_length_guideline_soft_column") != guideline_cols[0]) {
+ guideline_cols.append(EditorSettings::get_singleton()->get("text_editor/appearance/guidelines/line_length_guideline_soft_column"));
}
text_editor->set_line_length_guidelines(guideline_cols);
}
@@ -1028,7 +1045,7 @@ void CodeTextEditor::insert_final_newline() {
}
void CodeTextEditor::convert_indent_to_spaces() {
- int indent_size = EditorSettings::get_singleton()->get("text_editor/indent/size");
+ int indent_size = EditorSettings::get_singleton()->get("text_editor/behavior/indent/size");
String indent = "";
for (int i = 0; i < indent_size; i++) {
@@ -1072,7 +1089,7 @@ void CodeTextEditor::convert_indent_to_spaces() {
}
void CodeTextEditor::convert_indent_to_tabs() {
- int indent_size = EditorSettings::get_singleton()->get("text_editor/indent/size");
+ int indent_size = EditorSettings::get_singleton()->get("text_editor/behavior/indent/size");
indent_size -= 1;
int cursor_line = text_editor->get_caret_line();
diff --git a/editor/editor_command_palette.cpp b/editor/editor_command_palette.cpp
index bffd0655a7..6349beeef4 100644
--- a/editor/editor_command_palette.cpp
+++ b/editor/editor_command_palette.cpp
@@ -60,7 +60,6 @@ void EditorCommandPalette::_update_command_search(const String &search_text) {
commands.get_key_list(&command_keys);
ERR_FAIL_COND(command_keys.is_empty());
- const bool empty_search = search_text.is_empty();
Map<String, TreeItem *> sections;
TreeItem *first_section = nullptr;
@@ -71,7 +70,7 @@ void EditorCommandPalette::_update_command_search(const String &search_text) {
r.key_name = command_keys[i];
r.display_name = commands[r.key_name].name;
r.shortcut_text = commands[r.key_name].shortcut;
- if (!empty_search && search_text.is_subsequence_ofi(r.display_name)) {
+ if (search_text.is_subsequence_ofi(r.display_name)) {
r.score = _score_path(search_text, r.display_name.to_lower());
entries.push_back(r);
}
@@ -83,7 +82,7 @@ void EditorCommandPalette::_update_command_search(const String &search_text) {
root->clear_children();
if (entries.size() > 0) {
- if (!empty_search) {
+ if (!search_text.is_empty()) {
SortArray<CommandEntry, CommandEntryComparator> sorter;
sorter.sort(entries.ptrw(), entries.size());
}
@@ -128,7 +127,7 @@ void EditorCommandPalette::_update_command_search(const String &search_text) {
get_ok_button()->set_disabled(false);
} else {
TreeItem *ti = search_options->create_item(root);
- ti->set_text(0, TTR("No Matching Command"));
+ ti->set_text(0, TTR("No matching commands found"));
ti->set_metadata(0, "");
Color c = Color(0.5, 0.5, 0.5, 0.5);
ti->set_custom_color(0, c);
@@ -179,35 +178,35 @@ void EditorCommandPalette::get_actions_list(List<String> *p_list) const {
}
void EditorCommandPalette::remove_command(String p_key_name) {
- ERR_FAIL_COND_MSG(!commands.has(p_key_name), "The EditorAction '" + String(p_key_name) + "' Doesn't exists. Unable to remove it.");
+ ERR_FAIL_COND_MSG(!commands.has(p_key_name), "The Command '" + String(p_key_name) + "' doesn't exists. Unable to remove it.");
commands.erase(p_key_name);
}
void EditorCommandPalette::add_command(String p_command_name, String p_key_name, Callable p_action, Vector<Variant> arguments, String p_shortcut_text) {
- ERR_FAIL_COND_MSG(commands.has(p_key_name), "The EditorAction '" + String(p_command_name) + "' already exists. Unable to add it.");
+ ERR_FAIL_COND_MSG(commands.has(p_key_name), "The Command '" + String(p_command_name) + "' already exists. Unable to add it.");
const Variant **argptrs = (const Variant **)alloca(sizeof(Variant *) * arguments.size());
for (int i = 0; i < arguments.size(); i++) {
argptrs[i] = &arguments[i];
}
- Command p_command;
- p_command.name = p_command_name;
- p_command.callable = p_action.bind(argptrs, arguments.size());
- p_command.shortcut = p_shortcut_text;
+ Command command;
+ command.name = p_command_name;
+ command.callable = p_action.bind(argptrs, arguments.size());
+ command.shortcut = p_shortcut_text;
- commands[p_key_name] = p_command;
+ commands[p_key_name] = command;
}
void EditorCommandPalette::_add_command(String p_command_name, String p_key_name, Callable p_binded_action, String p_shortcut_text) {
- ERR_FAIL_COND_MSG(commands.has(p_key_name), "The EditorAction '" + String(p_command_name) + "' already exists. Unable to add it.");
+ ERR_FAIL_COND_MSG(commands.has(p_key_name), "The Command '" + String(p_command_name) + "' already exists. Unable to add it.");
- Command p_command;
- p_command.name = p_command_name;
- p_command.callable = p_binded_action;
- p_command.shortcut = p_shortcut_text;
+ Command command;
+ command.name = p_command_name;
+ command.callable = p_binded_action;
+ command.shortcut = p_shortcut_text;
- commands[p_key_name] = p_command;
+ commands[p_key_name] = command;
}
void EditorCommandPalette::execute_command(String &p_command_key) {
@@ -216,17 +215,17 @@ void EditorCommandPalette::execute_command(String &p_command_key) {
}
void EditorCommandPalette::register_shortcuts_as_command() {
- const String *p_key = nullptr;
- p_key = unregistered_shortcuts.next(p_key);
- while (p_key != nullptr) {
- String command_name = unregistered_shortcuts[*p_key].first;
- Ref<Shortcut> p_shortcut = unregistered_shortcuts[*p_key].second;
+ const String *key = nullptr;
+ key = unregistered_shortcuts.next(key);
+ while (key != nullptr) {
+ String command_name = unregistered_shortcuts[*key].first;
+ Ref<Shortcut> shortcut = unregistered_shortcuts[*key].second;
Ref<InputEventShortcut> ev;
ev.instantiate();
- ev->set_shortcut(p_shortcut);
- String shortcut_text = String(p_shortcut->get_as_text());
- add_command(command_name, *p_key, callable_mp(EditorNode::get_singleton()->get_viewport(), &Viewport::unhandled_input), varray(ev, false), shortcut_text);
- p_key = unregistered_shortcuts.next(p_key);
+ ev->set_shortcut(shortcut);
+ String shortcut_text = String(shortcut->get_as_text());
+ add_command(command_name, *key, callable_mp(EditorNode::get_singleton()->get_viewport(), &Viewport::unhandled_input), varray(ev, false), shortcut_text);
+ key = unregistered_shortcuts.next(key);
}
unregistered_shortcuts.clear();
}
@@ -241,8 +240,8 @@ Ref<Shortcut> EditorCommandPalette::add_shortcut_command(const String &p_command
} else {
const String key_name = String(p_key);
const String command_name = String(p_command);
- Pair p_pair = Pair(command_name, p_shortcut);
- unregistered_shortcuts[key_name] = p_pair;
+ Pair pair = Pair(command_name, p_shortcut);
+ unregistered_shortcuts[key_name] = pair;
}
return p_shortcut;
}
@@ -264,12 +263,11 @@ EditorCommandPalette::EditorCommandPalette() {
add_child(vbc);
command_search_box = memnew(LineEdit);
- command_search_box->set_placeholder("search for a command");
- command_search_box->set_placeholder_alpha(0.5);
+ command_search_box->set_placeholder(TTR("Filter commands"));
command_search_box->connect("gui_input", callable_mp(this, &EditorCommandPalette::_sbox_input));
command_search_box->connect("text_changed", callable_mp(this, &EditorCommandPalette::_update_command_search));
- command_search_box->connect("text_submitted", callable_mp(this, &EditorCommandPalette::_confirmed).unbind(1));
command_search_box->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ command_search_box->set_clear_button_enabled(true);
MarginContainer *margin_container_csb = memnew(MarginContainer);
margin_container_csb->add_child(command_search_box);
vbc->add_child(margin_container_csb);
@@ -285,9 +283,9 @@ EditorCommandPalette::EditorCommandPalette() {
search_options->set_v_size_flags(Control::SIZE_EXPAND_FILL);
search_options->set_h_size_flags(Control::SIZE_EXPAND_FILL);
search_options->set_column_custom_minimum_width(0, int(8 * EDSCALE));
-
vbc->add_child(search_options, true);
+ connect("confirmed", callable_mp(this, &EditorCommandPalette::_confirmed));
set_hide_on_ok(false);
}
@@ -296,7 +294,7 @@ Ref<Shortcut> ED_SHORTCUT_AND_COMMAND(const String &p_path, const String &p_name
p_command_name = p_name;
}
- Ref<Shortcut> p_shortcut = ED_SHORTCUT(p_path, p_name, p_keycode);
- EditorCommandPalette::get_singleton()->add_shortcut_command(p_command_name, p_path, p_shortcut);
- return p_shortcut;
+ Ref<Shortcut> shortcut = ED_SHORTCUT(p_path, p_name, p_keycode);
+ EditorCommandPalette::get_singleton()->add_shortcut_command(p_command_name, p_path, shortcut);
+ return shortcut;
}
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 19a6d027bc..4cd2e8bdd0 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -6369,6 +6369,7 @@ EditorNode::EditorNode() {
#else
p->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/editor_settings", TTR("Editor Settings...")), SETTINGS_PREFERENCES);
#endif
+ p->add_shortcut(ED_SHORTCUT("editor/command_palette", TTR("Command Palette..."), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_P), HELP_COMMAND_PALETTE);
p->add_separator();
editor_layouts = memnew(PopupMenu);
@@ -6682,7 +6683,7 @@ EditorNode::EditorNode() {
bottom_panel_raise->set_flat(true);
bottom_panel_raise->set_icon(gui_base->get_theme_icon(SNAME("ExpandBottomDock"), SNAME("EditorIcons")));
- bottom_panel_raise->set_shortcut(ED_SHORTCUT("editor/bottom_panel_expand", TTR("Expand Bottom Panel"), KEY_MASK_SHIFT | KEY_F12));
+ bottom_panel_raise->set_shortcut(ED_SHORTCUT_AND_COMMAND("editor/bottom_panel_expand", TTR("Expand Bottom Panel"), KEY_MASK_SHIFT | KEY_F12));
bottom_panel_hb->add_child(bottom_panel_raise);
bottom_panel_raise->hide();
@@ -7047,17 +7048,15 @@ EditorNode::EditorNode() {
ED_SHORTCUT_AND_COMMAND("editor/editor_3d", TTR("Open 3D Editor"), KEY_MASK_ALT | KEY_2);
ED_SHORTCUT_AND_COMMAND("editor/editor_script", TTR("Open Script Editor"), KEY_MASK_ALT | KEY_3);
ED_SHORTCUT_AND_COMMAND("editor/editor_assetlib", TTR("Open Asset Library"), KEY_MASK_ALT | KEY_4);
- ED_SHORTCUT("editor/command_palette", TTR("Open Command Palette"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_P);
#else
// Use the Ctrl modifier so F2 can be used to rename nodes in the scene tree dock.
ED_SHORTCUT_AND_COMMAND("editor/editor_2d", TTR("Open 2D Editor"), KEY_MASK_CTRL | KEY_F1);
ED_SHORTCUT_AND_COMMAND("editor/editor_3d", TTR("Open 3D Editor"), KEY_MASK_CTRL | KEY_F2);
ED_SHORTCUT_AND_COMMAND("editor/editor_script", TTR("Open Script Editor"), KEY_MASK_CTRL | KEY_F3);
ED_SHORTCUT_AND_COMMAND("editor/editor_assetlib", TTR("Open Asset Library"), KEY_MASK_CTRL | KEY_F4);
- ED_SHORTCUT("editor/command_palette", TTR("Open Command Palette"), KEY_MASK_CTRL | KEY_MASK_SHIFT | KEY_P);
#endif
ED_SHORTCUT_AND_COMMAND("editor/editor_next", TTR("Next Editor Tab"));
- ED_SHORTCUT_AND_COMMAND("editor/editor_prev", TTR("Next Editor Tab"));
+ ED_SHORTCUT_AND_COMMAND("editor/editor_prev", TTR("Previous Editor Tab"));
screenshot_timer = memnew(Timer);
screenshot_timer->set_one_shot(true);
diff --git a/editor/editor_node.h b/editor/editor_node.h
index fed4d23704..911139f470 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -708,7 +708,6 @@ public:
EditorInspector *get_inspector() { return inspector_dock->get_inspector(); }
Container *get_inspector_dock_addon_area() { return inspector_dock->get_addon_area(); }
ScriptCreateDialog *get_script_create_dialog() { return scene_tree_dock->get_script_create_dialog(); }
- EditorCommandPalette *get_editor_command_palette() { return command_palette; }
ProjectSettingsEditor *get_project_settings() { return project_settings; }
diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp
index b71a3944fc..98b5ec7d56 100644
--- a/editor/editor_plugin.cpp
+++ b/editor/editor_plugin.cpp
@@ -311,7 +311,7 @@ bool EditorInterface::is_distraction_free_mode_enabled() const {
}
EditorCommandPalette *EditorInterface::get_command_palette() const {
- return EditorNode::get_singleton()->get_editor_command_palette();
+ return EditorCommandPalette::get_singleton();
}
EditorInterface *EditorInterface::singleton = nullptr;
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index 009a83994c..3fc010c701 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -492,68 +492,73 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
// Theme
_initial_set("text_editor/theme/color_theme", "Default");
hints["text_editor/theme/color_theme"] = PropertyInfo(Variant::STRING, "text_editor/theme/color_theme", PROPERTY_HINT_ENUM, "Default,Godot 2,Custom");
- _initial_set("text_editor/theme/line_spacing", 6);
- hints["text_editor/theme/line_spacing"] = PropertyInfo(Variant::INT, "text_editor/theme/line_spacing", PROPERTY_HINT_RANGE, "0,50,1");
+ // Theme: Highlighting
_load_godot2_text_editor_theme();
- // Highlighting
- _initial_set("text_editor/highlighting/highlight_all_occurrences", true);
- _initial_set("text_editor/highlighting/highlight_current_line", true);
- _initial_set("text_editor/highlighting/highlight_type_safe_lines", true);
-
- // Indent
- _initial_set("text_editor/indent/type", 0);
- hints["text_editor/indent/type"] = PropertyInfo(Variant::INT, "text_editor/indent/type", PROPERTY_HINT_ENUM, "Tabs,Spaces");
- _initial_set("text_editor/indent/size", 4);
- hints["text_editor/indent/size"] = PropertyInfo(Variant::INT, "text_editor/indent/size", PROPERTY_HINT_RANGE, "1, 64, 1"); // size of 0 crashes.
- _initial_set("text_editor/indent/auto_indent", true);
- _initial_set("text_editor/indent/convert_indent_on_save", true);
- _initial_set("text_editor/indent/draw_tabs", true);
- _initial_set("text_editor/indent/draw_spaces", false);
-
- // Navigation
- _initial_set("text_editor/navigation/smooth_scrolling", true);
- _initial_set("text_editor/navigation/v_scroll_speed", 80);
- _initial_set("text_editor/navigation/show_minimap", true);
- _initial_set("text_editor/navigation/minimap_width", 80);
- hints["text_editor/navigation/minimap_width"] = PropertyInfo(Variant::INT, "text_editor/navigation/minimap_width", PROPERTY_HINT_RANGE, "50,250,1");
-
// Appearance
- _initial_set("text_editor/appearance/show_line_numbers", true);
- _initial_set("text_editor/appearance/line_numbers_zero_padded", false);
- _initial_set("text_editor/appearance/show_bookmark_gutter", true);
- _initial_set("text_editor/appearance/show_info_gutter", true);
- _initial_set("text_editor/appearance/code_folding", true);
- _initial_set("text_editor/appearance/word_wrap", 0);
- hints["text_editor/appearance/word_wrap"] = PropertyInfo(Variant::INT, "text_editor/appearance/word_wrap", PROPERTY_HINT_ENUM, "None,Boundary");
-
- _initial_set("text_editor/appearance/show_line_length_guidelines", true);
- _initial_set("text_editor/appearance/line_length_guideline_soft_column", 80);
- hints["text_editor/appearance/line_length_guideline_soft_column"] = PropertyInfo(Variant::INT, "text_editor/appearance/line_length_guideline_soft_column", PROPERTY_HINT_RANGE, "20, 160, 1");
- _initial_set("text_editor/appearance/line_length_guideline_hard_column", 100);
- hints["text_editor/appearance/line_length_guideline_hard_column"] = PropertyInfo(Variant::INT, "text_editor/appearance/line_length_guideline_hard_column", PROPERTY_HINT_RANGE, "20, 160, 1");
+ // Appearance: Caret
+ _initial_set("text_editor/appearance/caret/type", 0);
+ hints["text_editor/appearance/caret/type"] = PropertyInfo(Variant::INT, "text_editor/appearance/caret/type", PROPERTY_HINT_ENUM, "Line,Block");
+ _initial_set("text_editor/appearance/caret/caret_blink", true);
+ _initial_set("text_editor/appearance/caret/caret_blink_speed", 0.5);
+ hints["text_editor/appearance/caret/caret_blink_speed"] = PropertyInfo(Variant::FLOAT, "text_editor/appearance/caret/caret_blink_speed", PROPERTY_HINT_RANGE, "0.1, 10, 0.01");
+ _initial_set("text_editor/appearance/caret/highlight_current_line", true);
+ _initial_set("text_editor/appearance/caret/highlight_all_occurrences", true);
+
+ // Appearance: Guidelines
+ _initial_set("text_editor/appearance/guidelines/show_line_length_guidelines", true);
+ _initial_set("text_editor/appearance/guidelines/line_length_guideline_soft_column", 80);
+ hints["text_editor/appearance/guidelines/line_length_guideline_soft_column"] = PropertyInfo(Variant::INT, "text_editor/appearance/guidelines/line_length_guideline_soft_column", PROPERTY_HINT_RANGE, "20, 160, 1");
+ _initial_set("text_editor/appearance/guidelines/line_length_guideline_hard_column", 100);
+ hints["text_editor/appearance/guidelines/line_length_guideline_hard_column"] = PropertyInfo(Variant::INT, "text_editor/appearance/guidelines/line_length_guideline_hard_column", PROPERTY_HINT_RANGE, "20, 160, 1");
+
+ // Appearance: Gutters
+ _initial_set("text_editor/appearance/gutters/show_line_numbers", true);
+ _initial_set("text_editor/appearance/gutters/line_numbers_zero_padded", false);
+ _initial_set("text_editor/appearance/gutters/highlight_type_safe_lines", true);
+ _initial_set("text_editor/appearance/gutters/show_bookmark_gutter", true);
+ _initial_set("text_editor/appearance/gutters/show_info_gutter", true);
+
+ // Appearance: Minimap
+ _initial_set("text_editor/appearance/minimap/show_minimap", true);
+ _initial_set("text_editor/appearance/minimap/minimap_width", 80);
+ hints["text_editor/appearance/minimap/minimap_width"] = PropertyInfo(Variant::INT, "text_editor/appearance/minimap/minimap_width", PROPERTY_HINT_RANGE, "50,250,1");
+
+ // Appearance: Lines
+ _initial_set("text_editor/appearance/lines/code_folding", true);
+ _initial_set("text_editor/appearance/lines/word_wrap", 0);
+ hints["text_editor/appearance/lines/word_wrap"] = PropertyInfo(Variant::INT, "text_editor/appearance/lines/word_wrap", PROPERTY_HINT_ENUM, "None,Boundary");
+
+ // Appearance: Whitespace
+ _initial_set("text_editor/appearance/whitespace/draw_tabs", true);
+ _initial_set("text_editor/appearance/whitespace/draw_spaces", false);
+ _initial_set("text_editor/appearance/whitespace/line_spacing", 6);
+ hints["text_editor/appearance/whitespace/line_spacing"] = PropertyInfo(Variant::INT, "text_editor/appearance/whitespace/line_spacing", PROPERTY_HINT_RANGE, "0,50,1");
+
+ // Behavior
+ // Behavior: Navigation
+ _initial_set("text_editor/behavior/navigation/move_caret_on_right_click", true);
+ _initial_set("text_editor/behavior/navigation/scroll_past_end_of_file", false);
+ _initial_set("text_editor/behavior/navigation/smooth_scrolling", true);
+ _initial_set("text_editor/behavior/navigation/v_scroll_speed", 80);
+
+ // Behavior: Indent
+ _initial_set("text_editor/behavior/indent/type", 0);
+ hints["text_editor/behavior/indent/type"] = PropertyInfo(Variant::INT, "text_editor/behavior/indent/type", PROPERTY_HINT_ENUM, "Tabs,Spaces");
+ _initial_set("text_editor/behavior/indent/size", 4);
+ hints["text_editor/behavior/indent/size"] = PropertyInfo(Variant::INT, "text_editor/behavior/indent/size", PROPERTY_HINT_RANGE, "1, 64, 1"); // size of 0 crashes.
+ _initial_set("text_editor/behavior/indent/auto_indent", true);
+
+ // Behavior: Files
+ _initial_set("text_editor/behavior/files/trim_trailing_whitespace_on_save", false);
+ _initial_set("text_editor/behavior/files/autosave_interval_secs", 0);
+ _initial_set("text_editor/behavior/files/restore_scripts_on_load", true);
+ _initial_set("text_editor/behavior/files/convert_indent_on_save", true);
// Script list
_initial_set("text_editor/script_list/show_members_overview", true);
-
- // Files
- _initial_set("text_editor/files/trim_trailing_whitespace_on_save", false);
- _initial_set("text_editor/files/autosave_interval_secs", 0);
- _initial_set("text_editor/files/restore_scripts_on_load", true);
-
- // Tools
- _initial_set("text_editor/tools/create_signal_callbacks", true);
- _initial_set("text_editor/tools/sort_members_outline_alphabetically", false);
-
- // Cursor
- _initial_set("text_editor/cursor/scroll_past_end_of_file", false);
- _initial_set("text_editor/cursor/type", 0);
- hints["text_editor/cursor/type"] = PropertyInfo(Variant::INT, "text_editor/cursor/type", PROPERTY_HINT_ENUM, "Line,Block");
- _initial_set("text_editor/cursor/caret_blink", true);
- _initial_set("text_editor/cursor/caret_blink_speed", 0.5);
- hints["text_editor/cursor/caret_blink_speed"] = PropertyInfo(Variant::FLOAT, "text_editor/cursor/caret_blink_speed", PROPERTY_HINT_RANGE, "0.1, 10, 0.01");
- _initial_set("text_editor/cursor/right_click_moves_caret", true);
+ _initial_set("text_editor/script_list/sort_members_outline_alphabetically", false);
// Completion
_initial_set("text_editor/completion/idle_parse_delay", 2.0);
@@ -777,41 +782,41 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
void EditorSettings::_load_godot2_text_editor_theme() {
// Godot 2 is only a dark theme; it doesn't have a light theme counterpart.
- _initial_set("text_editor/highlighting/symbol_color", Color(0.73, 0.87, 1.0));
- _initial_set("text_editor/highlighting/keyword_color", Color(1.0, 1.0, 0.7));
- _initial_set("text_editor/highlighting/control_flow_keyword_color", Color(1.0, 0.85, 0.7));
- _initial_set("text_editor/highlighting/base_type_color", Color(0.64, 1.0, 0.83));
- _initial_set("text_editor/highlighting/engine_type_color", Color(0.51, 0.83, 1.0));
- _initial_set("text_editor/highlighting/user_type_color", Color(0.42, 0.67, 0.93));
- _initial_set("text_editor/highlighting/comment_color", Color(0.4, 0.4, 0.4));
- _initial_set("text_editor/highlighting/string_color", Color(0.94, 0.43, 0.75));
- _initial_set("text_editor/highlighting/background_color", Color(0.13, 0.12, 0.15));
- _initial_set("text_editor/highlighting/completion_background_color", Color(0.17, 0.16, 0.2));
- _initial_set("text_editor/highlighting/completion_selected_color", Color(0.26, 0.26, 0.27));
- _initial_set("text_editor/highlighting/completion_existing_color", Color(0.13, 0.87, 0.87, 0.87));
- _initial_set("text_editor/highlighting/completion_scroll_color", Color(1, 1, 1));
- _initial_set("text_editor/highlighting/completion_font_color", Color(0.67, 0.67, 0.67));
- _initial_set("text_editor/highlighting/text_color", Color(0.67, 0.67, 0.67));
- _initial_set("text_editor/highlighting/line_number_color", Color(0.67, 0.67, 0.67, 0.4));
- _initial_set("text_editor/highlighting/safe_line_number_color", Color(0.67, 0.78, 0.67, 0.6));
- _initial_set("text_editor/highlighting/caret_color", Color(0.67, 0.67, 0.67));
- _initial_set("text_editor/highlighting/caret_background_color", Color(0, 0, 0));
- _initial_set("text_editor/highlighting/text_selected_color", Color(0, 0, 0));
- _initial_set("text_editor/highlighting/selection_color", Color(0.41, 0.61, 0.91, 0.35));
- _initial_set("text_editor/highlighting/brace_mismatch_color", Color(1, 0.2, 0.2));
- _initial_set("text_editor/highlighting/current_line_color", Color(0.3, 0.5, 0.8, 0.15));
- _initial_set("text_editor/highlighting/line_length_guideline_color", Color(0.3, 0.5, 0.8, 0.1));
- _initial_set("text_editor/highlighting/word_highlighted_color", Color(0.8, 0.9, 0.9, 0.15));
- _initial_set("text_editor/highlighting/number_color", Color(0.92, 0.58, 0.2));
- _initial_set("text_editor/highlighting/function_color", Color(0.4, 0.64, 0.81));
- _initial_set("text_editor/highlighting/member_variable_color", Color(0.9, 0.31, 0.35));
- _initial_set("text_editor/highlighting/mark_color", Color(1.0, 0.4, 0.4, 0.4));
- _initial_set("text_editor/highlighting/bookmark_color", Color(0.08, 0.49, 0.98));
- _initial_set("text_editor/highlighting/breakpoint_color", Color(0.9, 0.29, 0.3));
- _initial_set("text_editor/highlighting/executing_line_color", Color(0.98, 0.89, 0.27));
- _initial_set("text_editor/highlighting/code_folding_color", Color(0.8, 0.8, 0.8, 0.8));
- _initial_set("text_editor/highlighting/search_result_color", Color(0.05, 0.25, 0.05, 1));
- _initial_set("text_editor/highlighting/search_result_border_color", Color(0.41, 0.61, 0.91, 0.38));
+ _initial_set("text_editor/theme/highlighting/symbol_color", Color(0.73, 0.87, 1.0));
+ _initial_set("text_editor/theme/highlighting/keyword_color", Color(1.0, 1.0, 0.7));
+ _initial_set("text_editor/theme/highlighting/control_flow_keyword_color", Color(1.0, 0.85, 0.7));
+ _initial_set("text_editor/theme/highlighting/base_type_color", Color(0.64, 1.0, 0.83));
+ _initial_set("text_editor/theme/highlighting/engine_type_color", Color(0.51, 0.83, 1.0));
+ _initial_set("text_editor/theme/highlighting/user_type_color", Color(0.42, 0.67, 0.93));
+ _initial_set("text_editor/theme/highlighting/comment_color", Color(0.4, 0.4, 0.4));
+ _initial_set("text_editor/theme/highlighting/string_color", Color(0.94, 0.43, 0.75));
+ _initial_set("text_editor/theme/highlighting/background_color", Color(0.13, 0.12, 0.15));
+ _initial_set("text_editor/theme/highlighting/completion_background_color", Color(0.17, 0.16, 0.2));
+ _initial_set("text_editor/theme/highlighting/completion_selected_color", Color(0.26, 0.26, 0.27));
+ _initial_set("text_editor/theme/highlighting/completion_existing_color", Color(0.13, 0.87, 0.87, 0.87));
+ _initial_set("text_editor/theme/highlighting/completion_scroll_color", Color(1, 1, 1));
+ _initial_set("text_editor/theme/highlighting/completion_font_color", Color(0.67, 0.67, 0.67));
+ _initial_set("text_editor/theme/highlighting/text_color", Color(0.67, 0.67, 0.67));
+ _initial_set("text_editor/theme/highlighting/line_number_color", Color(0.67, 0.67, 0.67, 0.4));
+ _initial_set("text_editor/theme/highlighting/safe_line_number_color", Color(0.67, 0.78, 0.67, 0.6));
+ _initial_set("text_editor/theme/highlighting/caret_color", Color(0.67, 0.67, 0.67));
+ _initial_set("text_editor/theme/highlighting/caret_background_color", Color(0, 0, 0));
+ _initial_set("text_editor/theme/highlighting/text_selected_color", Color(0, 0, 0));
+ _initial_set("text_editor/theme/highlighting/selection_color", Color(0.41, 0.61, 0.91, 0.35));
+ _initial_set("text_editor/theme/highlighting/brace_mismatch_color", Color(1, 0.2, 0.2));
+ _initial_set("text_editor/theme/highlighting/current_line_color", Color(0.3, 0.5, 0.8, 0.15));
+ _initial_set("text_editor/theme/highlighting/line_length_guideline_color", Color(0.3, 0.5, 0.8, 0.1));
+ _initial_set("text_editor/theme/highlighting/word_highlighted_color", Color(0.8, 0.9, 0.9, 0.15));
+ _initial_set("text_editor/theme/highlighting/number_color", Color(0.92, 0.58, 0.2));
+ _initial_set("text_editor/theme/highlighting/function_color", Color(0.4, 0.64, 0.81));
+ _initial_set("text_editor/theme/highlighting/member_variable_color", Color(0.9, 0.31, 0.35));
+ _initial_set("text_editor/theme/highlighting/mark_color", Color(1.0, 0.4, 0.4, 0.4));
+ _initial_set("text_editor/theme/highlighting/bookmark_color", Color(0.08, 0.49, 0.98));
+ _initial_set("text_editor/theme/highlighting/breakpoint_color", Color(0.9, 0.29, 0.3));
+ _initial_set("text_editor/theme/highlighting/executing_line_color", Color(0.98, 0.89, 0.27));
+ _initial_set("text_editor/theme/highlighting/code_folding_color", Color(0.8, 0.8, 0.8, 0.8));
+ _initial_set("text_editor/theme/highlighting/search_result_color", Color(0.05, 0.25, 0.05, 1));
+ _initial_set("text_editor/theme/highlighting/search_result_border_color", Color(0.41, 0.61, 0.91, 0.38));
}
bool EditorSettings::_save_text_editor_theme(String p_file) {
@@ -823,8 +828,8 @@ bool EditorSettings::_save_text_editor_theme(String p_file) {
keys.sort();
for (const String &key : keys) {
- if (key.begins_with("text_editor/highlighting/") && key.find("color") >= 0) {
- cf->set_value(theme_section, key.replace("text_editor/highlighting/", ""), ((Color)props[key].variant).to_html());
+ if (key.begins_with("text_editor/theme/highlighting/") && key.find("color") >= 0) {
+ cf->set_value(theme_section, key.replace("text_editor/theme/highlighting/", ""), ((Color)props[key].variant).to_html());
}
}
@@ -1340,10 +1345,10 @@ void EditorSettings::load_text_editor_theme() {
String val = cf->get_value("color_theme", key);
// don't load if it's not already there!
- if (has_setting("text_editor/highlighting/" + key)) {
+ if (has_setting("text_editor/theme/highlighting/" + key)) {
// make sure it is actually a color
if (val.is_valid_html_color() && key.find("color") >= 0) {
- props["text_editor/highlighting/" + key].variant = Color::html(val); // change manually to prevent "Settings changed" console spam
+ props["text_editor/theme/highlighting/" + key].variant = Color::html(val); // change manually to prevent "Settings changed" console spam
}
}
}
diff --git a/editor/editor_spin_slider.cpp b/editor/editor_spin_slider.cpp
index a802afda0f..91f00deeaa 100644
--- a/editor/editor_spin_slider.cpp
+++ b/editor/editor_spin_slider.cpp
@@ -195,11 +195,11 @@ void EditorSpinSlider::_update_value_input_stylebox() {
if (!value_input) {
return;
}
+
// Add a left margin to the stylebox to make the number align with the Label
// when it's edited. The LineEdit "focus" stylebox uses the "normal" stylebox's
// default margins.
- Ref<StyleBoxFlat> stylebox =
- EditorNode::get_singleton()->get_theme_base()->get_theme_stylebox(SNAME("normal"), SNAME("LineEdit"))->duplicate();
+ Ref<StyleBox> stylebox = get_theme_stylebox(SNAME("normal"), SNAME("LineEdit"))->duplicate();
// EditorSpinSliders with a label have more space on the left, so add an
// higher margin to match the location where the text begins.
// The margin values below were determined by empirical testing.
@@ -210,188 +210,197 @@ void EditorSpinSlider::_update_value_input_stylebox() {
stylebox->set_default_margin(SIDE_LEFT, (get_label() != String() ? 23 : 16) * EDSCALE);
stylebox->set_default_margin(SIDE_RIGHT, 0);
}
+
value_input->add_theme_style_override("normal", stylebox);
}
-void EditorSpinSlider::_notification(int p_what) {
- if (p_what == NOTIFICATION_WM_WINDOW_FOCUS_OUT ||
- p_what == NOTIFICATION_WM_WINDOW_FOCUS_IN ||
- p_what == NOTIFICATION_EXIT_TREE) {
- if (grabbing_spinner) {
- grabber->hide();
- Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE);
- grabbing_spinner = false;
- grabbing_spinner_attempt = false;
- }
- }
- if (p_what == NOTIFICATION_READY || p_what == NOTIFICATION_THEME_CHANGED) {
- _update_value_input_stylebox();
- }
+void EditorSpinSlider::_draw_spin_slider() {
+ updown_offset = -1;
- if (p_what == NOTIFICATION_DRAW) {
- updown_offset = -1;
+ RID ci = get_canvas_item();
+ bool rtl = is_layout_rtl();
+ Vector2 size = get_size();
- RID ci = get_canvas_item();
- bool rtl = is_layout_rtl();
- Vector2 size = get_size();
+ Ref<StyleBox> sb = get_theme_stylebox(SNAME("normal"), SNAME("LineEdit"));
+ if (!flat) {
+ draw_style_box(sb, Rect2(Vector2(), size));
+ }
+ Ref<Font> font = get_theme_font(SNAME("font"), SNAME("LineEdit"));
+ int font_size = get_theme_font_size(SNAME("font_size"), SNAME("LineEdit"));
+ int sep_base = 4 * EDSCALE;
+ int sep = sep_base + sb->get_offset().x; //make it have the same margin on both sides, looks better
- Ref<StyleBox> sb = get_theme_stylebox(SNAME("normal"), SNAME("LineEdit"));
- if (!flat) {
- draw_style_box(sb, Rect2(Vector2(), size));
- }
- Ref<Font> font = get_theme_font(SNAME("font"), SNAME("LineEdit"));
- int font_size = get_theme_font_size(SNAME("font_size"), SNAME("LineEdit"));
- int sep_base = 4 * EDSCALE;
- int sep = sep_base + sb->get_offset().x; //make it have the same margin on both sides, looks better
+ int label_width = font->get_string_size(label, font_size).width;
+ int number_width = size.width - sb->get_minimum_size().width - label_width - sep;
- int label_width = font->get_string_size(label, font_size).width;
- int number_width = size.width - sb->get_minimum_size().width - label_width - sep;
+ Ref<Texture2D> updown = get_theme_icon(SNAME("updown"), SNAME("SpinBox"));
- Ref<Texture2D> updown = get_theme_icon(SNAME("updown"), SNAME("SpinBox"));
+ if (get_step() == 1) {
+ number_width -= updown->get_width();
+ }
- if (get_step() == 1) {
- number_width -= updown->get_width();
- }
+ String numstr = get_text_value();
- String numstr = get_text_value();
+ int vofs = (size.height - font->get_height(font_size)) / 2 + font->get_ascent(font_size);
- int vofs = (size.height - font->get_height(font_size)) / 2 + font->get_ascent(font_size);
+ Color fc = get_theme_color(SNAME("font_color"), SNAME("LineEdit"));
+ Color lc;
+ if (use_custom_label_color) {
+ lc = custom_label_color;
+ } else {
+ lc = fc;
+ }
- Color fc = get_theme_color(SNAME("font_color"), SNAME("LineEdit"));
- Color lc;
- if (use_custom_label_color) {
- lc = custom_label_color;
+ if (flat && label != String()) {
+ Color label_bg_color = get_theme_color(SNAME("dark_color_3"), SNAME("Editor"));
+ if (rtl) {
+ draw_rect(Rect2(Vector2(size.width - (sb->get_offset().x * 2 + label_width), 0), Vector2(sb->get_offset().x * 2 + label_width, size.height)), label_bg_color);
} else {
- lc = fc;
+ draw_rect(Rect2(Vector2(), Vector2(sb->get_offset().x * 2 + label_width, size.height)), label_bg_color);
}
+ }
- if (flat && label != String()) {
- Color label_bg_color = get_theme_color(SNAME("dark_color_3"), SNAME("Editor"));
- if (rtl) {
- draw_rect(Rect2(Vector2(size.width - (sb->get_offset().x * 2 + label_width), 0), Vector2(sb->get_offset().x * 2 + label_width, size.height)), label_bg_color);
- } else {
- draw_rect(Rect2(Vector2(), Vector2(sb->get_offset().x * 2 + label_width, size.height)), label_bg_color);
- }
- }
+ if (has_focus()) {
+ Ref<StyleBox> focus = get_theme_stylebox(SNAME("focus"), SNAME("LineEdit"));
+ draw_style_box(focus, Rect2(Vector2(), size));
+ }
+
+ if (rtl) {
+ draw_string(font, Vector2(Math::round(size.width - sb->get_offset().x - label_width), vofs), label, HALIGN_RIGHT, -1, font_size, lc * Color(1, 1, 1, 0.5));
+ } else {
+ draw_string(font, Vector2(Math::round(sb->get_offset().x), vofs), label, HALIGN_LEFT, -1, font_size, lc * Color(1, 1, 1, 0.5));
+ }
- if (has_focus()) {
- Ref<StyleBox> focus = get_theme_stylebox(SNAME("focus"), SNAME("LineEdit"));
- draw_style_box(focus, Rect2(Vector2(), size));
+ int suffix_start = numstr.length();
+ RID num_rid = TS->create_shaped_text();
+ TS->shaped_text_add_string(num_rid, numstr + U"\u2009" + suffix, font->get_rids(), font_size);
+
+ float text_start = rtl ? Math::round(sb->get_offset().x) : Math::round(sb->get_offset().x + label_width + sep);
+ Vector2 text_ofs = rtl ? Vector2(text_start + (number_width - TS->shaped_text_get_width(num_rid)), vofs) : Vector2(text_start, vofs);
+ const Vector<TextServer::Glyph> visual = TS->shaped_text_get_glyphs(num_rid);
+ int v_size = visual.size();
+ const TextServer::Glyph *glyphs = visual.ptr();
+ for (int i = 0; i < v_size; i++) {
+ for (int j = 0; j < glyphs[i].repeat; j++) {
+ if (text_ofs.x >= text_start && (text_ofs.x + glyphs[i].advance) <= (text_start + number_width)) {
+ Color color = fc;
+ if (glyphs[i].start >= suffix_start) {
+ color.a *= 0.4;
+ }
+ if (glyphs[i].font_rid != RID()) {
+ TS->font_draw_glyph(glyphs[i].font_rid, ci, glyphs[i].font_size, text_ofs + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, color);
+ } else if ((glyphs[i].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
+ TS->draw_hex_code_box(ci, glyphs[i].font_size, text_ofs + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, color);
+ }
+ }
+ text_ofs.x += glyphs[i].advance;
}
+ }
+ TS->free(num_rid);
+ if (get_step() == 1) {
+ Ref<Texture2D> updown2 = get_theme_icon(SNAME("updown"), SNAME("SpinBox"));
+ int updown_vofs = (size.height - updown2->get_height()) / 2;
if (rtl) {
- draw_string(font, Vector2(Math::round(size.width - sb->get_offset().x - label_width), vofs), label, HALIGN_RIGHT, -1, font_size, lc * Color(1, 1, 1, 0.5));
+ updown_offset = sb->get_margin(SIDE_LEFT);
} else {
- draw_string(font, Vector2(Math::round(sb->get_offset().x), vofs), label, HALIGN_LEFT, -1, font_size, lc * Color(1, 1, 1, 0.5));
+ updown_offset = size.width - sb->get_margin(SIDE_RIGHT) - updown2->get_width();
}
-
- int suffix_start = numstr.length();
- RID num_rid = TS->create_shaped_text();
- TS->shaped_text_add_string(num_rid, numstr + U"\u2009" + suffix, font->get_rids(), font_size);
-
- float text_start = rtl ? Math::round(sb->get_offset().x) : Math::round(sb->get_offset().x + label_width + sep);
- Vector2 text_ofs = rtl ? Vector2(text_start + (number_width - TS->shaped_text_get_width(num_rid)), vofs) : Vector2(text_start, vofs);
- const Vector<TextServer::Glyph> visual = TS->shaped_text_get_glyphs(num_rid);
- int v_size = visual.size();
- const TextServer::Glyph *glyphs = visual.ptr();
- for (int i = 0; i < v_size; i++) {
- for (int j = 0; j < glyphs[i].repeat; j++) {
- if (text_ofs.x >= text_start && (text_ofs.x + glyphs[i].advance) <= (text_start + number_width)) {
- Color color = fc;
- if (glyphs[i].start >= suffix_start) {
- color.a *= 0.4;
- }
- if (glyphs[i].font_rid != RID()) {
- TS->font_draw_glyph(glyphs[i].font_rid, ci, glyphs[i].font_size, text_ofs + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, color);
- } else if ((glyphs[i].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
- TS->draw_hex_code_box(ci, glyphs[i].font_size, text_ofs + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, color);
- }
- }
- text_ofs.x += glyphs[i].advance;
- }
+ Color c(1, 1, 1);
+ if (hover_updown) {
+ c *= Color(1.2, 1.2, 1.2);
}
- TS->free(num_rid);
-
- if (get_step() == 1) {
- Ref<Texture2D> updown2 = get_theme_icon(SNAME("updown"), SNAME("SpinBox"));
- int updown_vofs = (size.height - updown2->get_height()) / 2;
- if (rtl) {
- updown_offset = sb->get_margin(SIDE_LEFT);
+ draw_texture(updown2, Vector2(updown_offset, updown_vofs), c);
+ if (grabber->is_visible()) {
+ grabber->hide();
+ }
+ } else if (!hide_slider) {
+ int grabber_w = 4 * EDSCALE;
+ int width = size.width - sb->get_minimum_size().width - grabber_w;
+ int ofs = sb->get_offset().x;
+ int svofs = (size.height + vofs) / 2 - 1;
+ Color c = fc;
+ c.a = 0.2;
+
+ draw_rect(Rect2(ofs, svofs + 1, width, 2 * EDSCALE), c);
+ int gofs = get_as_ratio() * width;
+ c.a = 0.9;
+ Rect2 grabber_rect = Rect2(ofs + gofs, svofs + 1, grabber_w, 2 * EDSCALE);
+ draw_rect(grabber_rect, c);
+
+ grabbing_spinner_mouse_pos = get_global_position() + grabber_rect.position + grabber_rect.size * 0.5;
+
+ bool display_grabber = (mouse_over_spin || mouse_over_grabber) && !grabbing_spinner && !(value_input_popup && value_input_popup->is_visible());
+ if (grabber->is_visible() != display_grabber) {
+ if (display_grabber) {
+ grabber->show();
} else {
- updown_offset = size.width - sb->get_margin(SIDE_RIGHT) - updown2->get_width();
- }
- Color c(1, 1, 1);
- if (hover_updown) {
- c *= Color(1.2, 1.2, 1.2);
- }
- draw_texture(updown2, Vector2(updown_offset, updown_vofs), c);
- if (grabber->is_visible()) {
grabber->hide();
}
- } else if (!hide_slider) {
- int grabber_w = 4 * EDSCALE;
- int width = size.width - sb->get_minimum_size().width - grabber_w;
- int ofs = sb->get_offset().x;
- int svofs = (size.height + vofs) / 2 - 1;
- Color c = fc;
- c.a = 0.2;
-
- draw_rect(Rect2(ofs, svofs + 1, width, 2 * EDSCALE), c);
- int gofs = get_as_ratio() * width;
- c.a = 0.9;
- Rect2 grabber_rect = Rect2(ofs + gofs, svofs + 1, grabber_w, 2 * EDSCALE);
- draw_rect(grabber_rect, c);
-
- grabbing_spinner_mouse_pos = get_global_position() + grabber_rect.position + grabber_rect.size * 0.5;
-
- bool display_grabber = (mouse_over_spin || mouse_over_grabber) && !grabbing_spinner && !(value_input_popup && value_input_popup->is_visible());
- if (grabber->is_visible() != display_grabber) {
- if (display_grabber) {
- grabber->show();
- } else {
- grabber->hide();
- }
- }
-
- if (display_grabber) {
- Ref<Texture2D> grabber_tex;
- if (mouse_over_grabber) {
- grabber_tex = get_theme_icon(SNAME("grabber_highlight"), SNAME("HSlider"));
- } else {
- grabber_tex = get_theme_icon(SNAME("grabber"), SNAME("HSlider"));
- }
+ }
- if (grabber->get_texture() != grabber_tex) {
- grabber->set_texture(grabber_tex);
- }
+ if (display_grabber) {
+ Ref<Texture2D> grabber_tex;
+ if (mouse_over_grabber) {
+ grabber_tex = get_theme_icon(SNAME("grabber_highlight"), SNAME("HSlider"));
+ } else {
+ grabber_tex = get_theme_icon(SNAME("grabber"), SNAME("HSlider"));
+ }
- Vector2 scale = get_global_transform_with_canvas().get_scale();
- grabber->set_scale(scale);
- grabber->set_size(Size2(0, 0));
- grabber->set_position(get_global_position() + (grabber_rect.position + grabber_rect.size * 0.5 - grabber->get_size() * 0.5) * scale);
+ if (grabber->get_texture() != grabber_tex) {
+ grabber->set_texture(grabber_tex);
+ }
- if (mousewheel_over_grabber) {
- Input::get_singleton()->warp_mouse_position(grabber->get_position() + grabber_rect.size);
- }
+ Vector2 scale = get_global_transform_with_canvas().get_scale();
+ grabber->set_scale(scale);
+ grabber->set_size(Size2(0, 0));
+ grabber->set_position(get_global_position() + (grabber_rect.position + grabber_rect.size * 0.5 - grabber->get_size() * 0.5) * scale);
- grabber_range = width;
+ if (mousewheel_over_grabber) {
+ Input::get_singleton()->warp_mouse_position(grabber->get_position() + grabber_rect.size);
}
+
+ grabber_range = width;
}
}
+}
- if (p_what == NOTIFICATION_MOUSE_ENTER) {
- mouse_over_spin = true;
- update();
- }
- if (p_what == NOTIFICATION_MOUSE_EXIT) {
- mouse_over_spin = false;
- update();
- }
- if (p_what == NOTIFICATION_FOCUS_ENTER) {
- if ((Input::get_singleton()->is_action_pressed("ui_focus_next") || Input::get_singleton()->is_action_pressed("ui_focus_prev")) && !value_input_just_closed) {
- _focus_entered();
- }
- value_input_just_closed = false;
+void EditorSpinSlider::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_THEME_CHANGED:
+ _update_value_input_stylebox();
+ break;
+
+ case NOTIFICATION_DRAW:
+ _draw_spin_slider();
+ break;
+
+ case NOTIFICATION_WM_WINDOW_FOCUS_IN:
+ case NOTIFICATION_WM_WINDOW_FOCUS_OUT:
+ case NOTIFICATION_EXIT_TREE:
+ if (grabbing_spinner) {
+ grabber->hide();
+ Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE);
+ grabbing_spinner = false;
+ grabbing_spinner_attempt = false;
+ }
+ break;
+
+ case NOTIFICATION_MOUSE_ENTER:
+ mouse_over_spin = true;
+ update();
+ break;
+ case NOTIFICATION_MOUSE_EXIT:
+ mouse_over_spin = false;
+ update();
+ break;
+ case NOTIFICATION_FOCUS_ENTER:
+ if ((Input::get_singleton()->is_action_pressed("ui_focus_next") || Input::get_singleton()->is_action_pressed("ui_focus_prev")) && !value_input_just_closed) {
+ _focus_entered();
+ }
+ value_input_just_closed = false;
+ break;
}
}
@@ -567,8 +576,10 @@ void EditorSpinSlider::_ensure_input_popup() {
if (value_input_popup) {
return;
}
+
value_input_popup = memnew(Popup);
add_child(value_input_popup);
+
value_input = memnew(LineEdit);
value_input_popup->add_child(value_input);
value_input_popup->set_wrap_controls(true);
@@ -576,6 +587,7 @@ void EditorSpinSlider::_ensure_input_popup() {
value_input_popup->connect("popup_hide", callable_mp(this, &EditorSpinSlider::_value_input_closed));
value_input->connect("text_submitted", callable_mp(this, &EditorSpinSlider::_value_input_submitted));
value_input->connect("focus_exited", callable_mp(this, &EditorSpinSlider::_value_focus_exited));
+
if (is_inside_tree()) {
_update_value_input_stylebox();
}
diff --git a/editor/editor_spin_slider.h b/editor/editor_spin_slider.h
index 5b99f88505..c09d084e88 100644
--- a/editor/editor_spin_slider.h
+++ b/editor/editor_spin_slider.h
@@ -81,6 +81,7 @@ class EditorSpinSlider : public Range {
void _update_value_input_stylebox();
void _ensure_input_popup();
+ void _draw_spin_slider();
protected:
void _notification(int p_what);
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index 0d70e970e8..7f8229e5e8 100644
--- a/editor/editor_themes.cpp
+++ b/editor/editor_themes.cpp
@@ -1066,7 +1066,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_icon("folded", "CodeEdit", theme->get_icon("GuiTreeArrowRight", "EditorIcons"));
theme->set_icon("can_fold", "CodeEdit", theme->get_icon("GuiTreeArrowDown", "EditorIcons"));
theme->set_icon("executing_line", "CodeEdit", theme->get_icon("MainPlay", "EditorIcons"));
- theme->set_constant("line_spacing", "CodeEdit", EDITOR_DEF("text_editor/theme/line_spacing", 6));
+ theme->set_constant("line_spacing", "CodeEdit", EDITOR_DEF("text_editor/appearance/whitespace/line_spacing", 6));
// H/VSplitContainer
theme->set_stylebox("bg", "VSplitContainer", make_stylebox(theme->get_icon("GuiVsplitBg", "EditorIcons"), 1, 1, 1, 1));
@@ -1438,67 +1438,67 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
EditorSettings *setting = EditorSettings::get_singleton();
String text_editor_color_theme = setting->get("text_editor/theme/color_theme");
if (text_editor_color_theme == "Default") {
- setting->set_initial_value("text_editor/highlighting/symbol_color", symbol_color, true);
- setting->set_initial_value("text_editor/highlighting/keyword_color", keyword_color, true);
- setting->set_initial_value("text_editor/highlighting/control_flow_keyword_color", control_flow_keyword_color, true);
- setting->set_initial_value("text_editor/highlighting/base_type_color", basetype_color, true);
- setting->set_initial_value("text_editor/highlighting/engine_type_color", type_color, true);
- setting->set_initial_value("text_editor/highlighting/user_type_color", usertype_color, true);
- setting->set_initial_value("text_editor/highlighting/comment_color", comment_color, true);
- setting->set_initial_value("text_editor/highlighting/string_color", string_color, true);
- setting->set_initial_value("text_editor/highlighting/background_color", te_background_color, true);
- setting->set_initial_value("text_editor/highlighting/completion_background_color", completion_background_color, true);
- setting->set_initial_value("text_editor/highlighting/completion_selected_color", completion_selected_color, true);
- setting->set_initial_value("text_editor/highlighting/completion_existing_color", completion_existing_color, true);
- setting->set_initial_value("text_editor/highlighting/completion_scroll_color", completion_scroll_color, true);
- setting->set_initial_value("text_editor/highlighting/completion_font_color", completion_font_color, true);
- setting->set_initial_value("text_editor/highlighting/text_color", text_color, true);
- setting->set_initial_value("text_editor/highlighting/line_number_color", line_number_color, true);
- setting->set_initial_value("text_editor/highlighting/safe_line_number_color", safe_line_number_color, true);
- setting->set_initial_value("text_editor/highlighting/caret_color", caret_color, true);
- setting->set_initial_value("text_editor/highlighting/caret_background_color", caret_background_color, true);
- setting->set_initial_value("text_editor/highlighting/text_selected_color", text_selected_color, true);
- setting->set_initial_value("text_editor/highlighting/selection_color", selection_color, true);
- setting->set_initial_value("text_editor/highlighting/brace_mismatch_color", brace_mismatch_color, true);
- setting->set_initial_value("text_editor/highlighting/current_line_color", current_line_color, true);
- setting->set_initial_value("text_editor/highlighting/line_length_guideline_color", line_length_guideline_color, true);
- setting->set_initial_value("text_editor/highlighting/word_highlighted_color", word_highlighted_color, true);
- setting->set_initial_value("text_editor/highlighting/number_color", number_color, true);
- setting->set_initial_value("text_editor/highlighting/function_color", function_color, true);
- setting->set_initial_value("text_editor/highlighting/member_variable_color", member_variable_color, true);
- setting->set_initial_value("text_editor/highlighting/mark_color", mark_color, true);
- setting->set_initial_value("text_editor/highlighting/bookmark_color", bookmark_color, true);
- setting->set_initial_value("text_editor/highlighting/breakpoint_color", breakpoint_color, true);
- setting->set_initial_value("text_editor/highlighting/executing_line_color", executing_line_color, true);
- setting->set_initial_value("text_editor/highlighting/code_folding_color", code_folding_color, true);
- setting->set_initial_value("text_editor/highlighting/search_result_color", search_result_color, true);
- setting->set_initial_value("text_editor/highlighting/search_result_border_color", search_result_border_color, true);
+ setting->set_initial_value("text_editor/theme/highlighting/symbol_color", symbol_color, true);
+ setting->set_initial_value("text_editor/theme/highlighting/keyword_color", keyword_color, true);
+ setting->set_initial_value("text_editor/theme/highlighting/control_flow_keyword_color", control_flow_keyword_color, true);
+ setting->set_initial_value("text_editor/theme/highlighting/base_type_color", basetype_color, true);
+ setting->set_initial_value("text_editor/theme/highlighting/engine_type_color", type_color, true);
+ setting->set_initial_value("text_editor/theme/highlighting/user_type_color", usertype_color, true);
+ setting->set_initial_value("text_editor/theme/highlighting/comment_color", comment_color, true);
+ setting->set_initial_value("text_editor/theme/highlighting/string_color", string_color, true);
+ setting->set_initial_value("text_editor/theme/highlighting/background_color", te_background_color, true);
+ setting->set_initial_value("text_editor/theme/highlighting/completion_background_color", completion_background_color, true);
+ setting->set_initial_value("text_editor/theme/highlighting/completion_selected_color", completion_selected_color, true);
+ setting->set_initial_value("text_editor/theme/highlighting/completion_existing_color", completion_existing_color, true);
+ setting->set_initial_value("text_editor/theme/highlighting/completion_scroll_color", completion_scroll_color, true);
+ setting->set_initial_value("text_editor/theme/highlighting/completion_font_color", completion_font_color, true);
+ setting->set_initial_value("text_editor/theme/highlighting/text_color", text_color, true);
+ setting->set_initial_value("text_editor/theme/highlighting/line_number_color", line_number_color, true);
+ setting->set_initial_value("text_editor/theme/highlighting/safe_line_number_color", safe_line_number_color, true);
+ setting->set_initial_value("text_editor/theme/highlighting/caret_color", caret_color, true);
+ setting->set_initial_value("text_editor/theme/highlighting/caret_background_color", caret_background_color, true);
+ setting->set_initial_value("text_editor/theme/highlighting/text_selected_color", text_selected_color, true);
+ setting->set_initial_value("text_editor/theme/highlighting/selection_color", selection_color, true);
+ setting->set_initial_value("text_editor/theme/highlighting/brace_mismatch_color", brace_mismatch_color, true);
+ setting->set_initial_value("text_editor/theme/highlighting/current_line_color", current_line_color, true);
+ setting->set_initial_value("text_editor/theme/highlighting/line_length_guideline_color", line_length_guideline_color, true);
+ setting->set_initial_value("text_editor/theme/highlighting/word_highlighted_color", word_highlighted_color, true);
+ setting->set_initial_value("text_editor/theme/highlighting/number_color", number_color, true);
+ setting->set_initial_value("text_editor/theme/highlighting/function_color", function_color, true);
+ setting->set_initial_value("text_editor/theme/highlighting/member_variable_color", member_variable_color, true);
+ setting->set_initial_value("text_editor/theme/highlighting/mark_color", mark_color, true);
+ setting->set_initial_value("text_editor/theme/highlighting/bookmark_color", bookmark_color, true);
+ setting->set_initial_value("text_editor/theme/highlighting/breakpoint_color", breakpoint_color, true);
+ setting->set_initial_value("text_editor/theme/highlighting/executing_line_color", executing_line_color, true);
+ setting->set_initial_value("text_editor/theme/highlighting/code_folding_color", code_folding_color, true);
+ setting->set_initial_value("text_editor/theme/highlighting/search_result_color", search_result_color, true);
+ setting->set_initial_value("text_editor/theme/highlighting/search_result_border_color", search_result_border_color, true);
} else if (text_editor_color_theme == "Godot 2") {
setting->load_text_editor_theme();
}
// Now theme is loaded, apply it to CodeEdit.
- theme->set_color("background_color", "CodeEdit", EDITOR_GET("text_editor/highlighting/background_color"));
- theme->set_color("completion_background_color", "CodeEdit", EDITOR_GET("text_editor/highlighting/completion_background_color"));
- theme->set_color("completion_selected_color", "CodeEdit", EDITOR_GET("text_editor/highlighting/completion_selected_color"));
- theme->set_color("completion_existing_color", "CodeEdit", EDITOR_GET("text_editor/highlighting/completion_existing_color"));
- theme->set_color("completion_scroll_color", "CodeEdit", EDITOR_GET("text_editor/highlighting/completion_scroll_color"));
- theme->set_color("completion_font_color", "CodeEdit", EDITOR_GET("text_editor/highlighting/completion_font_color"));
- theme->set_color("font_color", "CodeEdit", EDITOR_GET("text_editor/highlighting/text_color"));
- theme->set_color("line_number_color", "CodeEdit", EDITOR_GET("text_editor/highlighting/line_number_color"));
- theme->set_color("caret_color", "CodeEdit", EDITOR_GET("text_editor/highlighting/caret_color"));
- theme->set_color("font_selected_color", "CodeEdit", EDITOR_GET("text_editor/highlighting/text_selected_color"));
- theme->set_color("selection_color", "CodeEdit", EDITOR_GET("text_editor/highlighting/selection_color"));
- theme->set_color("brace_mismatch_color", "CodeEdit", EDITOR_GET("text_editor/highlighting/brace_mismatch_color"));
- theme->set_color("current_line_color", "CodeEdit", EDITOR_GET("text_editor/highlighting/current_line_color"));
- theme->set_color("line_length_guideline_color", "CodeEdit", EDITOR_GET("text_editor/highlighting/line_length_guideline_color"));
- theme->set_color("word_highlighted_color", "CodeEdit", EDITOR_GET("text_editor/highlighting/word_highlighted_color"));
- theme->set_color("bookmark_color", "CodeEdit", EDITOR_GET("text_editor/highlighting/bookmark_color"));
- theme->set_color("breakpoint_color", "CodeEdit", EDITOR_GET("text_editor/highlighting/breakpoint_color"));
- theme->set_color("executing_line_color", "CodeEdit", EDITOR_GET("text_editor/highlighting/executing_line_color"));
- theme->set_color("code_folding_color", "CodeEdit", EDITOR_GET("text_editor/highlighting/code_folding_color"));
- theme->set_color("search_result_color", "CodeEdit", EDITOR_GET("text_editor/highlighting/search_result_color"));
- theme->set_color("search_result_border_color", "CodeEdit", EDITOR_GET("text_editor/highlighting/search_result_border_color"));
+ theme->set_color("background_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/background_color"));
+ theme->set_color("completion_background_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/completion_background_color"));
+ theme->set_color("completion_selected_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/completion_selected_color"));
+ theme->set_color("completion_existing_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/completion_existing_color"));
+ theme->set_color("completion_scroll_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/completion_scroll_color"));
+ theme->set_color("completion_font_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/completion_font_color"));
+ theme->set_color("font_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/text_color"));
+ theme->set_color("line_number_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/line_number_color"));
+ theme->set_color("caret_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/caret_color"));
+ theme->set_color("font_selected_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/text_selected_color"));
+ theme->set_color("selection_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/selection_color"));
+ theme->set_color("brace_mismatch_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/brace_mismatch_color"));
+ theme->set_color("current_line_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/current_line_color"));
+ theme->set_color("line_length_guideline_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/line_length_guideline_color"));
+ theme->set_color("word_highlighted_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/word_highlighted_color"));
+ theme->set_color("bookmark_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/bookmark_color"));
+ theme->set_color("breakpoint_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/breakpoint_color"));
+ theme->set_color("executing_line_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/executing_line_color"));
+ theme->set_color("code_folding_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/code_folding_color"));
+ theme->set_color("search_result_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/search_result_color"));
+ theme->set_color("search_result_border_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/search_result_border_color"));
return theme;
}
diff --git a/editor/icons/GridLayout.svg b/editor/icons/GridLayout.svg
index 71ad504477..f05bc239a9 100644
--- a/editor/icons/GridLayout.svg
+++ b/editor/icons/GridLayout.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m14 2.1992188v2.6152343l-2.625 1.3125v-2.6152343zm-12 4.0644531 2.625 1.3125v2.5507811l-2.625-1.3124999zm12 0v2.5507812l-2.625 1.3124999v-2.5507811zm-8 1.4550781h4v2.640625h-4zm-4 2.560547 2.625 1.3125v2.521484l-2.625-1.3125zm12 0v2.521484l-2.625 1.3125v-2.521484zm-8 1.455078h4v2.640625h-4zm1.7014535-8.109375h2.2985465v2.734375h-4.15625s-.7487346.647119-.8746377.640625c-.1310411-.0067594-1.5097373-1.4558594-1.5097373-1.4558594l-1.459375-.7296875v-2.6152343l.068419.034223s.026411-.4573464.062111-.6760553c.0346282-.2121439.1970747-.59225724.1970747-.59225724l-1.0483078-.52372301c-.0795772-.04012218-.1668141-.06276382-.2558594-.06640625-.35427845-.01325803-.64865004.27047362-.6484375.625v12c.00021484.236623.13402736.45284.34570312.558594l3.99999998 2c.086686.043505.1823067.06624.2792969.066406h6c.09699-.000166.192611-.0229.279297-.06641l4-2c.211676-.10575.345488-.321967.345703-.55859v-12c-.000468-.46423753-.488958-.76598317-.904297-.55859375l-3.869141 1.93359375h-2.9709527s.033448.4166167.015891.625c-.029188.3464401-.1950466.625-.1950468.625z" fill="#b05b5b"/><path d="m5 6s-2.21875-2.1616704-2.21875-3.2425057c0-1.0808352 0-2.6072392 2.21875-2.6072392s2.21875 1.526404 2.21875 2.6072392c0 1.0808353-2.21875 3.2425057-2.21875 3.2425057z" fill="#fff" fill-opacity=".68627"/></svg>
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="1.5" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke="#e0e0e0" stroke-width="1.3"><path d="m1.87 6.54123h2.917v2.917h-2.917z"/><path d="m6.53582 6.54123h2.917v2.917h-2.917z"/><path d="m11.20164 6.54123h2.917v2.917h-2.917z"/><g transform="matrix(.99999939 .00000001 -.00000005 1.00000043 -.000003 .000001)"><path d="m5.432 1.112-1.95 1.95 1.95 1.95" stroke-linejoin="miter" stroke-miterlimit="10"/><path d="m3.482 3.062h9.386"/></g><g transform="matrix(.99999939 .00000001 -.00000005 1.00000043 0 -.000005)"><path d="m10.731 11.112 1.95 1.95-1.95 1.95" stroke-linejoin="miter" stroke-miterlimit="10"/><path d="m3.294 13.062h9.387"/></g></g></svg>
diff --git a/editor/icons/VisualScriptComment.svg b/editor/icons/VisualScriptComment.svg
new file mode 100644
index 0000000000..3887853b58
--- /dev/null
+++ b/editor/icons/VisualScriptComment.svg
@@ -0,0 +1 @@
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><path d="m3 1v2h-2v2h2v4h-2v2h2v2h2v-2h4v2h2v-2h2v-2h-2v-4h2v-2h-2v-2h-2v2h-4v-2zm2 4h4v4h-4z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/VisualScriptExpression.svg b/editor/icons/VisualScriptExpression.svg
new file mode 100644
index 0000000000..d6a3c2d9a8
--- /dev/null
+++ b/editor/icons/VisualScriptExpression.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0"><path d="m4.859536 3.0412379c-2.0539867 0-3.7190721 1.6650852-3.7190721 3.719072v6.1984521h2.4793814v-2.479381h2.4793814v-2.4793803h-2.4793814v-1.2396908c0-.6846622.5550285-1.2396907 1.2396907-1.2396907h1.2396907v-2.4793813z"/><path d="m7.5889175 3.0000003 2.5000005 4.9999997-2.5000005 5h2.5000005l1.135249-2.727 1.36475 2.727h2.499999l-2.499999-5 2.499999-4.9999997h-2.499999l-1.13525 2.7269998-1.364749-2.7269998zm7.4999985 9.9999997v-6.25z"/></g></svg>
diff --git a/editor/icons/VisualShaderGraphTextureUniform.svg b/editor/icons/VisualShaderGraphTextureUniform.svg
new file mode 100644
index 0000000000..ed9e084fd3
--- /dev/null
+++ b/editor/icons/VisualShaderGraphTextureUniform.svg
@@ -0,0 +1 @@
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><path d="m2 0c-1.1045695 0-2 .8954-2 2v10c0 1.1046.8954305 2 2 2h10c1.104569 0 2-.8954 2-2v-10c0-1.1046-.895431-2-2-2zm0 2h10v10h-10zm9 2-4 4-2-2-2 3h8z" fill="#eae068"/></svg>
diff --git a/editor/icons/VisualShaderNodeBooleanUniform.svg b/editor/icons/VisualShaderNodeBooleanUniform.svg
new file mode 100644
index 0000000000..b4a7043fb3
--- /dev/null
+++ b/editor/icons/VisualShaderNodeBooleanUniform.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2 .02734375c-1.108 0-2 .892-2 2.00000005v1.9726562h2v2a3 3 0 0 1 2.5 1.3457031 3 3 0 0 1 2.5-1.3457031 3 3 0 0 1 2 .7675781 3 3 0 0 1 2-.7675781 3 3 0 0 1 2 .7695312v-2.7695312h2v5a1 1 0 0 0 1 1v-7.9726562c0-1.10800005-.892-2.00000005-2-2.00000005zm0 7.97265625v2a1 1 0 0 0 1-1 1 1 0 0 0 -1-1zm5 0a1 1 0 0 0 -1 1 1 1 0 0 0 1 1 1 1 0 0 0 1-1 1 1 0 0 0 -1-1zm4 0a1 1 0 0 0 -1 1 1 1 0 0 0 1 1 1 1 0 0 0 1-1 1 1 0 0 0 -1-1zm-6.5 2.654297a3 3 0 0 1 -2.5 1.345703h-2v2.027344c0 1.108.892 2 2 2h12c1.108 0 2-.892 2-2v-2.027344a3 3 0 0 1 -2.5-1.345703 3 3 0 0 1 -2.5 1.345703 3 3 0 0 1 -2-.767578 3 3 0 0 1 -2 .767578 3 3 0 0 1 -2.5-1.345703z" fill="#6f91f0" stroke-linecap="square" stroke-opacity=".75" stroke-width="2"/></svg>
diff --git a/editor/icons/VisualShaderNodeColorConstant.svg b/editor/icons/VisualShaderNodeColorConstant.svg
new file mode 100644
index 0000000000..cbc5b3a471
--- /dev/null
+++ b/editor/icons/VisualShaderNodeColorConstant.svg
@@ -0,0 +1 @@
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><g fill-opacity=".392157" transform="translate(0 -1038.3622)"><path d="m7 1039.3622a4.0000172 4.0000172 0 0 0 -4 4 4.0000172 4.0000172 0 0 0 .03906.5195 4.0000172 4.0000172 0 0 0 -2.039062 3.4805 4.0000172 4.0000172 0 0 0 4 4 4.0000172 4.0000172 0 0 0 1.998047-.541 4.0000172 4.0000172 0 0 0 2.001953.541 4.0000172 4.0000172 0 0 0 4-4 4.0000172 4.0000172 0 0 0 -2.037109-3.4824 4.0000172 4.0000172 0 0 0 .03711-.5176 4.0000172 4.0000172 0 0 0 -4-4z" fill="#fff"/><path d="m7 1040.3622a3 3 0 0 0 -3 3 3 3 0 0 0 .210937 1.1055 3 3 0 0 0 -2.210937 2.8945 3 3 0 0 0 3 3 3 3 0 0 0 2-.7676 3 3 0 0 0 2 .7676 3 3 0 0 0 3-3 3 3 0 0 0 -2.2148438-2.8906 3 3 0 0 0 .2148438-1.1094 3 3 0 0 0 -3-3z" fill="#fff"/><circle cx="7" cy="1043.3622" fill="#f00" r="3"/><circle cx="5" cy="1047.3622" fill="#00f" r="3"/><circle cx="9" cy="1047.3622" fill="#0f0" r="3"/><circle cx="7" cy="1043.3622" fill="#f00" r="3"/><circle cx="5" cy="1047.3622" fill="#00f" r="3"/><circle cx="9" cy="1047.3622" fill="#0f0" r="3"/></g></svg>
diff --git a/editor/icons/VisualShaderNodeColorOp.svg b/editor/icons/VisualShaderNodeColorOp.svg
new file mode 100644
index 0000000000..7b6cd8149b
--- /dev/null
+++ b/editor/icons/VisualShaderNodeColorOp.svg
@@ -0,0 +1 @@
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1038.3622)"><g fill="#fff"><path d="m4 1050.3622h6v-10h-6z" fill-rule="evenodd" stroke="#fff" stroke-linejoin="round" stroke-width="2"/><path d="m1 1041.3622h2v2h-2z"/><path d="m1 1047.3622h2v2h-2z"/><path d="m11 1044.3622h2v2h-2z"/></g><g fill-opacity=".862745"><path d="m5 1041.3622h4v2h-4z" fill="#ff4646"/><path d="m5 1044.3622h4v2h-4z" fill="#46ff46"/><path d="m5 1047.3622h4v2h-4z" fill="#4646ff"/></g></g></svg>
diff --git a/editor/icons/VisualShaderNodeColorUniform.svg b/editor/icons/VisualShaderNodeColorUniform.svg
new file mode 100644
index 0000000000..ce89b16583
--- /dev/null
+++ b/editor/icons/VisualShaderNodeColorUniform.svg
@@ -0,0 +1 @@
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1038.3622)"><path d="m2 1038.3622c-1.10457 0-2 .8954-2 2v10c0 1.1046.89543 2 2 2h10c1.104569 0 2-.8954 2-2v-10c0-1.1046-.895431-2-2-2z" fill="#fff"/><g fill-opacity=".392157"><path d="m7 2a3 3 0 0 0 -3 3 3 3 0 0 0 .2109375 1.1054688 3 3 0 0 0 -2.2109375 2.8945312 3 3 0 0 0 3 3 3 3 0 0 0 2-.767578 3 3 0 0 0 2 .767578 3 3 0 0 0 3-3 3 3 0 0 0 -2.2148438-2.890625 3 3 0 0 0 .2148438-1.109375 3 3 0 0 0 -3-3z" fill="#fff" transform="translate(0 1038.3622)"/><circle cx="7" cy="1043.3622" fill="#f00" r="3"/><circle cx="5" cy="1047.3622" fill="#00f" r="3"/><circle cx="9" cy="1047.3622" fill="#0f0" r="3"/><circle cx="7" cy="1043.3622" fill="#f00" r="3"/><circle cx="5" cy="1047.3622" fill="#00f" r="3"/><circle cx="9" cy="1047.3622" fill="#0f0" r="3"/></g></g></svg>
diff --git a/editor/icons/VisualShaderNodeComment.svg b/editor/icons/VisualShaderNodeComment.svg
new file mode 100644
index 0000000000..3887853b58
--- /dev/null
+++ b/editor/icons/VisualShaderNodeComment.svg
@@ -0,0 +1 @@
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><path d="m3 1v2h-2v2h2v4h-2v2h2v2h2v-2h4v2h2v-2h2v-2h-2v-4h2v-2h-2v-2h-2v2h-4v-2zm2 4h4v4h-4z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/VisualShaderNodeCubemap.svg b/editor/icons/VisualShaderNodeCubemap.svg
new file mode 100644
index 0000000000..fecb4d1287
--- /dev/null
+++ b/editor/icons/VisualShaderNodeCubemap.svg
@@ -0,0 +1 @@
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><path d="m7 3.5136719-2.6894531 1.34375 2.6894531 1.34375 2.6894531-1.34375zm-3.5722656 2.4980469v2.6894531l2.8574218 1.4277341v-2.6874998zm7.1445316 0-2.8574222 1.4296874v2.6874998l2.8574222-1.4277341z" fill="#eac968"/></svg>
diff --git a/editor/icons/VisualShaderNodeCubemapUniform.svg b/editor/icons/VisualShaderNodeCubemapUniform.svg
new file mode 100644
index 0000000000..e3463de0a2
--- /dev/null
+++ b/editor/icons/VisualShaderNodeCubemapUniform.svg
@@ -0,0 +1 @@
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><path d="m2 0c-1.1045695 0-2 .8954-2 2v10c0 1.1046.8954305 2 2 2h10c1.104569 0 2-.8954 2-2v-10c0-1.1046-.895431-2-2-2zm4.9726562 2a.71438238.71438238 0 0 1 .3476563.0742188l4.2851565 2.1425781a.71438238.71438238 0 0 1 .394531.640625v4.2851562a.71438238.71438238 0 0 1 -.394531.6386719l-4.2851565 2.142578a.71438238.71438238 0 0 1 -.640625 0l-4.2851563-2.142578a.71438238.71438238 0 0 1 -.3945312-.6386719v-4.2851562a.71438238.71438238 0 0 1 .3945312-.640625l4.2851563-2.1425781a.71438238.71438238 0 0 1 .2929687-.0742188zm.0273438 1.5136719-2.6894531 1.34375 2.6894531 1.34375 2.6894531-1.34375zm-3.5722656 2.4980469v2.6894531l2.8574218 1.4277341v-2.6874998zm7.1445316 0-2.8574222 1.4296874v2.6874998l2.8574222-1.4277341z" fill="#eac968"/></svg>
diff --git a/editor/icons/VisualShaderNodeCurveTexture.svg b/editor/icons/VisualShaderNodeCurveTexture.svg
new file mode 100644
index 0000000000..c0ee634ca4
--- /dev/null
+++ b/editor/icons/VisualShaderNodeCurveTexture.svg
@@ -0,0 +1 @@
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1038.3622)"><path d="m2 1049.3622c8 0 9 0 9-9" fill="none" stroke="#f6f6f6" stroke-linecap="round" stroke-width="2"/><path d="m11 4a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -2-2zm-5 5a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -2-2z" fill="#68d0ea" transform="translate(0 1038.3622)"/></g></svg>
diff --git a/editor/icons/VisualShaderNodeCurveXYZTexture.svg b/editor/icons/VisualShaderNodeCurveXYZTexture.svg
new file mode 100644
index 0000000000..c0ee634ca4
--- /dev/null
+++ b/editor/icons/VisualShaderNodeCurveXYZTexture.svg
@@ -0,0 +1 @@
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1038.3622)"><path d="m2 1049.3622c8 0 9 0 9-9" fill="none" stroke="#f6f6f6" stroke-linecap="round" stroke-width="2"/><path d="m11 4a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -2-2zm-5 5a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -2-2z" fill="#68d0ea" transform="translate(0 1038.3622)"/></g></svg>
diff --git a/editor/icons/VisualShaderNodeExpression.svg b/editor/icons/VisualShaderNodeExpression.svg
new file mode 100644
index 0000000000..8a930d4078
--- /dev/null
+++ b/editor/icons/VisualShaderNodeExpression.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#ac73f1"><path d="m4.859536 3.0412379c-2.0539867 0-3.7190721 1.6650852-3.7190721 3.719072v6.1984521h2.4793814v-2.479381h2.4793814v-2.4793803h-2.4793814v-1.2396908c0-.6846622.5550285-1.2396907 1.2396907-1.2396907h1.2396907v-2.4793813z"/><path d="m7.5889175 3.0000003 2.5000005 4.9999997-2.5000005 5h2.5000005l1.135249-2.727 1.36475 2.727h2.499999l-2.499999-5 2.499999-4.9999997h-2.499999l-1.13525 2.7269998-1.364749-2.7269998zm7.4999985 9.9999997v-6.25z"/></g></svg>
diff --git a/editor/icons/VisualShaderNodeFloatFunc.svg b/editor/icons/VisualShaderNodeFloatFunc.svg
new file mode 100644
index 0000000000..382c4e66af
--- /dev/null
+++ b/editor/icons/VisualShaderNodeFloatFunc.svg
@@ -0,0 +1 @@
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><g fill="#cf68ea" transform="translate(0 -1038.3622)"><path d="m6 1042.3622h2v4.999982h-2z"/><path d="m9.0703125 1a3 3 0 0 0 -1.5703125.4023438 3 3 0 0 0 -1.5 2.5976562h2a1 1 0 0 1 1-1 1 1 0 0 1 1 1h2a3 3 0 0 0 -1.5-2.5976562 3 3 0 0 0 -1.4296875-.4023438z" transform="translate(0 1038.3622)"/><path d="m10 1042.3622h2v1.000017h-2z"/><path d="m2 10a3 3 0 0 0 1.5 2.597656 3 3 0 0 0 3 0 3 3 0 0 0 1.5-2.597656h-2a1 1 0 0 1 -1 1 1 1 0 0 1 -1-1z" transform="translate(0 1038.3622)"/><path d="m6-1048.3622h2v1.000017h-2z" transform="scale(1 -1)"/><path d="m4 1044.3622h6v2h-6z"/></g></svg>
diff --git a/editor/icons/VisualShaderNodeFloatOp.svg b/editor/icons/VisualShaderNodeFloatOp.svg
new file mode 100644
index 0000000000..546ffc148e
--- /dev/null
+++ b/editor/icons/VisualShaderNodeFloatOp.svg
@@ -0,0 +1 @@
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><g fill="#cf68ea" transform="translate(0 -1038.3622)"><path d="m4 1c-.5522619.0001-.9999448.4477-1 1v10c.0000552.5523.4477381.9999 1 1h6c.552262-.0001.999945-.4477 1-1v-10c-.000055-.5523-.447738-.9999-1-1zm1 3 4 3-4 3z" fill-rule="evenodd" transform="translate(0 1038.3622)"/><path d="m1 1041.3622h2v2h-2z"/><path d="m1 1047.3622h2v2h-2z"/><path d="m11 1044.3622h2v2h-2z"/></g></svg>
diff --git a/editor/icons/VisualShaderNodeFloatUniform.svg b/editor/icons/VisualShaderNodeFloatUniform.svg
new file mode 100644
index 0000000000..dda5d098a3
--- /dev/null
+++ b/editor/icons/VisualShaderNodeFloatUniform.svg
@@ -0,0 +1 @@
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><path d="m2 0a2 2 0 0 0 -2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-10a2 2 0 0 0 -2-2zm4 2h5v2h-5a1.0000174 1.0000174 0 0 0 -1 1 1.0000174 1.0000174 0 0 0 1 1h2c1.6568542 0 3 1.3431 3 3s-1.3431458 3-3 3h-5v-2h5a1.0000174 1.0000174 0 0 0 1-1 1.0000174 1.0000174 0 0 0 -1-1h-2c-1.6568542 0-3-1.3431-3-3s1.3431458-3 3-3z" fill="#cf68ea"/></svg>
diff --git a/editor/icons/VisualShaderNodeGlobalExpression.svg b/editor/icons/VisualShaderNodeGlobalExpression.svg
new file mode 100644
index 0000000000..0cafffb152
--- /dev/null
+++ b/editor/icons/VisualShaderNodeGlobalExpression.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#35d4f4"><path d="m4.859536 3.0412379c-2.0539867 0-3.7190721 1.6650852-3.7190721 3.719072v6.1984521h2.4793814v-2.479381h2.4793814v-2.4793803h-2.4793814v-1.2396908c0-.6846622.5550285-1.2396907 1.2396907-1.2396907h1.2396907v-2.4793813z"/><path d="m7.5889175 3.0000003 2.5000005 4.9999997-2.5000005 5h2.5000005l1.135249-2.727 1.36475 2.727h2.499999l-2.499999-5 2.499999-4.9999997h-2.499999l-1.13525 2.7269998-1.364749-2.7269998zm7.4999985 9.9999997v-6.25z"/></g></svg>
diff --git a/editor/icons/VisualShaderNodeInput.svg b/editor/icons/VisualShaderNodeInput.svg
new file mode 100644
index 0000000000..ec347100d7
--- /dev/null
+++ b/editor/icons/VisualShaderNodeInput.svg
@@ -0,0 +1 @@
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><circle cx="7" cy="7" fill="#f6f6f6" r="6"/></svg>
diff --git a/editor/icons/VisualShaderNodeIntFunc.svg b/editor/icons/VisualShaderNodeIntFunc.svg
new file mode 100644
index 0000000000..382c4e66af
--- /dev/null
+++ b/editor/icons/VisualShaderNodeIntFunc.svg
@@ -0,0 +1 @@
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><g fill="#cf68ea" transform="translate(0 -1038.3622)"><path d="m6 1042.3622h2v4.999982h-2z"/><path d="m9.0703125 1a3 3 0 0 0 -1.5703125.4023438 3 3 0 0 0 -1.5 2.5976562h2a1 1 0 0 1 1-1 1 1 0 0 1 1 1h2a3 3 0 0 0 -1.5-2.5976562 3 3 0 0 0 -1.4296875-.4023438z" transform="translate(0 1038.3622)"/><path d="m10 1042.3622h2v1.000017h-2z"/><path d="m2 10a3 3 0 0 0 1.5 2.597656 3 3 0 0 0 3 0 3 3 0 0 0 1.5-2.597656h-2a1 1 0 0 1 -1 1 1 1 0 0 1 -1-1z" transform="translate(0 1038.3622)"/><path d="m6-1048.3622h2v1.000017h-2z" transform="scale(1 -1)"/><path d="m4 1044.3622h6v2h-6z"/></g></svg>
diff --git a/editor/icons/VisualShaderNodeIntOp.svg b/editor/icons/VisualShaderNodeIntOp.svg
new file mode 100644
index 0000000000..546ffc148e
--- /dev/null
+++ b/editor/icons/VisualShaderNodeIntOp.svg
@@ -0,0 +1 @@
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><g fill="#cf68ea" transform="translate(0 -1038.3622)"><path d="m4 1c-.5522619.0001-.9999448.4477-1 1v10c.0000552.5523.4477381.9999 1 1h6c.552262-.0001.999945-.4477 1-1v-10c-.000055-.5523-.447738-.9999-1-1zm1 3 4 3-4 3z" fill-rule="evenodd" transform="translate(0 1038.3622)"/><path d="m1 1041.3622h2v2h-2z"/><path d="m1 1047.3622h2v2h-2z"/><path d="m11 1044.3622h2v2h-2z"/></g></svg>
diff --git a/editor/icons/VisualShaderNodeIntUniform.svg b/editor/icons/VisualShaderNodeIntUniform.svg
new file mode 100644
index 0000000000..dda5d098a3
--- /dev/null
+++ b/editor/icons/VisualShaderNodeIntUniform.svg
@@ -0,0 +1 @@
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><path d="m2 0a2 2 0 0 0 -2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-10a2 2 0 0 0 -2-2zm4 2h5v2h-5a1.0000174 1.0000174 0 0 0 -1 1 1.0000174 1.0000174 0 0 0 1 1h2c1.6568542 0 3 1.3431 3 3s-1.3431458 3-3 3h-5v-2h5a1.0000174 1.0000174 0 0 0 1-1 1.0000174 1.0000174 0 0 0 -1-1h-2c-1.6568542 0-3-1.3431-3-3s1.3431458-3 3-3z" fill="#cf68ea"/></svg>
diff --git a/editor/icons/VisualShaderNodeTexture2DUniformTriplanar.svg b/editor/icons/VisualShaderNodeTexture2DUniformTriplanar.svg
new file mode 100644
index 0000000000..ed9e084fd3
--- /dev/null
+++ b/editor/icons/VisualShaderNodeTexture2DUniformTriplanar.svg
@@ -0,0 +1 @@
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><path d="m2 0c-1.1045695 0-2 .8954-2 2v10c0 1.1046.8954305 2 2 2h10c1.104569 0 2-.8954 2-2v-10c0-1.1046-.895431-2-2-2zm0 2h10v10h-10zm9 2-4 4-2-2-2 3h8z" fill="#eae068"/></svg>
diff --git a/editor/icons/VisualShaderNodeTransformCompose.svg b/editor/icons/VisualShaderNodeTransformCompose.svg
new file mode 100644
index 0000000000..6c7b28cda3
--- /dev/null
+++ b/editor/icons/VisualShaderNodeTransformCompose.svg
@@ -0,0 +1 @@
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="1.000747" x2="13.014989" y1="1045.3622" y2="1045.3622"><stop offset="0" stop-color="#b8ea68"/><stop offset="1" stop-color="#ea686c"/></linearGradient><path d="m1.9909808 1039.3524a1.0001 1.0001 0 0 0 -.697265 1.7168l3.2929683 3.293h-2.5859373a1.0001 1.0001 0 1 0 0 2h2.5859373l-3.2929683 3.293a1.0001 1.0001 0 1 0 1.414062 1.414l4.7070313-4.707h4.5859379a1.0001 1.0001 0 1 0 0-2h-4.5859379l-4.7070313-4.707a1.0001 1.0001 0 0 0 -.716797-.3028z" fill="url(#a)" fill-rule="evenodd" transform="translate(0 -1038.3622)"/></svg>
diff --git a/editor/icons/VisualShaderNodeTransformDecompose.svg b/editor/icons/VisualShaderNodeTransformDecompose.svg
new file mode 100644
index 0000000000..276b3ea7c8
--- /dev/null
+++ b/editor/icons/VisualShaderNodeTransformDecompose.svg
@@ -0,0 +1 @@
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientTransform="matrix(-1 0 0 1 13.999754 1038.3622)" gradientUnits="userSpaceOnUse" x1="1" x2="13.014242" y1="7" y2="7"><stop offset="0" stop-color="#b8ea68"/><stop offset="1" stop-color="#ea686c"/></linearGradient><path d="m12.00952 1039.3524a1.0001 1.0001 0 0 1 .697265 1.7168l-3.2929683 3.293h2.5859373a1.0001 1.0001 0 1 1 0 2h-2.5859373l3.2929683 3.293a1.0001 1.0001 0 1 1 -1.414062 1.414l-4.7070313-4.707h-4.5859377a1.0001 1.0001 0 1 1 0-2h4.5859377l4.7070313-4.707a1.0001 1.0001 0 0 1 .716797-.3028z" fill="url(#a)" fill-rule="evenodd" transform="translate(0 -1038.3622)"/></svg>
diff --git a/editor/icons/VisualShaderNodeTransformUniform.svg b/editor/icons/VisualShaderNodeTransformUniform.svg
new file mode 100644
index 0000000000..5d3e6977e0
--- /dev/null
+++ b/editor/icons/VisualShaderNodeTransformUniform.svg
@@ -0,0 +1 @@
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><path d="m2 0c-1.1045695 0-2 .8954-2 2v10c0 1.1046.8954305 2 2 2h10c1.104569 0 2-.8954 2-2v-10c0-1.1046-.895431-2-2-2zm-1 1h1 2v1h-2v10h2v1h-2-1v-1-10zm9 0h3v11 1h-3v-1h2v-10h-2zm-.0292969 2a1.0001 1.0001 0 0 1 1.0292969 1v7h-2v-4.5859375l-1.2929688 1.2929687a1.0001 1.0001 0 0 1 -1.4140624 0l-1.2929688-1.2929687v4.5859375h-2v-7a1.0001 1.0001 0 0 1 .984375-.9980469 1.0001 1.0001 0 0 1 .7226562.2910157l2.2929688 2.2929687 2.2929688-2.2929687a1.0001 1.0001 0 0 1 .6777343-.2929688z" fill="#ea686c"/></svg>
diff --git a/editor/icons/VisualShaderNodeTransformVecMult.svg b/editor/icons/VisualShaderNodeTransformVecMult.svg
new file mode 100644
index 0000000000..fe133b6ffe
--- /dev/null
+++ b/editor/icons/VisualShaderNodeTransformVecMult.svg
@@ -0,0 +1 @@
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1038.3622)"><path d="m9 1042.3622 2 6 2-6" fill="none" stroke="#b8ea68" stroke-linejoin="round" stroke-width="2"/><circle cx="7" cy="1046.3622" fill="#b8ea68" r="1"/><path d="m1 1049.3621v-7l2 3 2-3v7" fill="none" stroke="#ea686c" stroke-linejoin="round" stroke-width="2"/></g></svg>
diff --git a/editor/icons/VisualShaderNodeVec3Uniform.svg b/editor/icons/VisualShaderNodeVec3Uniform.svg
new file mode 100644
index 0000000000..6e0175230c
--- /dev/null
+++ b/editor/icons/VisualShaderNodeVec3Uniform.svg
@@ -0,0 +1 @@
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><path d="m2 0c-1.1045695 0-2 .8954305-2 2v10c0 1.104569.8954305 2 2 2h10c1.104569 0 2-.895431 2-2v-10c0-1.1045695-.895431-2-2-2zm6 0 3 3-3 3v-2h-5v-2h5zm-3.65625 5.6289062.5136719.8574219 2.1425781 3.5703129 2.1425781-3.5703129.5136719-.8574219 1.714844 1.0292969-.513672.8574219-3.0000001 5c-.3885014.647055-1.3263424.647055-1.7148438 0l-3-5-.5136719-.8574219z" fill="#b8ea68"/><path d="m23 0v2h-5v2h5v2l3-3zm-3.65625 5.6289062-1.714844 1.0292969.513672.8574219 3 5c.388501.647056 1.326343.647056 1.714844 0l3-5 .513672-.8574219-1.714844-1.0292969-.513672.8574219-2.142578 3.5703129-2.142578-3.5703129z" fill-rule="evenodd"/></svg>
diff --git a/editor/icons/VisualShaderNodeVectorCompose.svg b/editor/icons/VisualShaderNodeVectorCompose.svg
new file mode 100644
index 0000000000..8e12ab2ff6
--- /dev/null
+++ b/editor/icons/VisualShaderNodeVectorCompose.svg
@@ -0,0 +1 @@
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="1" x2="13.014242" y1="7" y2="7"><stop offset="0" stop-color="#cf68ea"/><stop offset="1" stop-color="#b8ea68"/></linearGradient><path d="m1.9902344.99023438a1.0001 1.0001 0 0 0 -.6972656 1.71679682l3.2929687 3.2929688h-2.5859375a1.0001 1.0001 0 1 0 0 2h2.5859375l-3.2929687 3.292969a1.0001 1.0001 0 1 0 1.4140624 1.414062l4.7070313-4.707031h4.5859375a1.0001 1.0001 0 1 0 0-2h-4.5859375l-4.7070313-4.7070312a1.0001 1.0001 0 0 0 -.7167968-.30273442z" fill="url(#a)" fill-rule="evenodd"/></svg>
diff --git a/editor/icons/VisualShaderNodeVectorDecompose.svg b/editor/icons/VisualShaderNodeVectorDecompose.svg
new file mode 100644
index 0000000000..4bd2dc2138
--- /dev/null
+++ b/editor/icons/VisualShaderNodeVectorDecompose.svg
@@ -0,0 +1 @@
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientTransform="matrix(-1 0 0 1 13.999754 1038.3622)" gradientUnits="userSpaceOnUse" x1="1" x2="13.014242" y1="7" y2="7"><stop offset="0" stop-color="#cf68ea"/><stop offset="1" stop-color="#b8ea68"/></linearGradient><path d="m12.00952 1039.3524a1.0001 1.0001 0 0 1 .697265 1.7168l-3.2929685 3.293h2.5859375a1.0001 1.0001 0 1 1 0 2h-2.5859375l3.2929685 3.293a1.0001 1.0001 0 1 1 -1.414062 1.414l-4.7070315-4.707h-4.5859375a1.0001 1.0001 0 1 1 0-2h4.5859375l4.7070315-4.707a1.0001 1.0001 0 0 1 .716797-.3028z" fill="url(#a)" fill-rule="evenodd" transform="translate(0 -1038.3622)"/></svg>
diff --git a/editor/icons/VisualShaderNodeVectorDistance.svg b/editor/icons/VisualShaderNodeVectorDistance.svg
new file mode 100644
index 0000000000..74a46047bf
--- /dev/null
+++ b/editor/icons/VisualShaderNodeVectorDistance.svg
@@ -0,0 +1 @@
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><path d="m2 1050.3622 10-10" fill="none" stroke="#b8ea68" stroke-linecap="round" stroke-width="2" transform="translate(0 -1038.3622)"/></svg>
diff --git a/editor/icons/VisualShaderNodeVectorFunc.svg b/editor/icons/VisualShaderNodeVectorFunc.svg
new file mode 100644
index 0000000000..dcd4cee3e4
--- /dev/null
+++ b/editor/icons/VisualShaderNodeVectorFunc.svg
@@ -0,0 +1 @@
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><g fill="#b8ea68" transform="translate(0 -1038.3622)"><path d="m6 1042.3622h2v4.999982h-2z"/><path d="m9.0703125 1a3 3 0 0 0 -1.5703125.4023438 3 3 0 0 0 -1.5 2.5976562h2a1 1 0 0 1 1-1 1 1 0 0 1 1 1h2a3 3 0 0 0 -1.5-2.5976562 3 3 0 0 0 -1.4296875-.4023438z" transform="translate(0 1038.3622)"/><path d="m10 1042.3622h2v1.000017h-2z"/><path d="m2 10a3 3 0 0 0 1.5 2.597656 3 3 0 0 0 3 0 3 3 0 0 0 1.5-2.597656h-2a1 1 0 0 1 -1 1 1 1 0 0 1 -1-1z" transform="translate(0 1038.3622)"/><path d="m6-1048.3622h2v1.000017h-2z" transform="scale(1 -1)"/><path d="m4 1044.3622h6v2h-6z"/></g></svg>
diff --git a/editor/icons/VisualShaderNodeVectorLen.svg b/editor/icons/VisualShaderNodeVectorLen.svg
new file mode 100644
index 0000000000..71faffdc3f
--- /dev/null
+++ b/editor/icons/VisualShaderNodeVectorLen.svg
@@ -0,0 +1 @@
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><path d="m8 1038.3614v2h-5v2h5v2l3-3zm-3.65625 5.6289-1.714844 1.0293.513672.8574 3 5c.388501.647 1.326343.647 1.714844 0l3-5 .513672-.8574-1.714844-1.0293-.513672.8574-2.142578 3.5703-2.142578-3.5703z" fill="#b8ea68" fill-rule="evenodd" transform="translate(0 -1038.3622)"/></svg>
diff --git a/editor/icons/LineShape2D.svg b/editor/icons/WorldMarginShape2D.svg
index f1dbe97c6f..f1dbe97c6f 100644
--- a/editor/icons/LineShape2D.svg
+++ b/editor/icons/WorldMarginShape2D.svg
diff --git a/editor/inspector_dock.cpp b/editor/inspector_dock.cpp
index 952bec4d87..778046f45c 100644
--- a/editor/inspector_dock.cpp
+++ b/editor/inspector_dock.cpp
@@ -209,6 +209,12 @@ void InspectorDock::_paste_resource() {
}
}
+void InspectorDock::_prepare_resource_extra_popup() {
+ RES r = EditorSettings::get_singleton()->get_resource_clipboard();
+ PopupMenu *popup = resource_extra_button->get_popup();
+ popup->set_item_disabled(popup->get_item_index(RESOURCE_EDIT_CLIPBOARD), r.is_null());
+}
+
void InspectorDock::_prepare_history() {
EditorHistory *editor_history = EditorNode::get_singleton()->get_editor_history();
@@ -525,6 +531,7 @@ InspectorDock::InspectorDock(EditorNode *p_editor, EditorData &p_editor_data) {
resource_extra_button->set_icon(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons")));
resource_extra_button->set_tooltip(TTR("Extra resource options."));
general_options_hb->add_child(resource_extra_button);
+ resource_extra_button->connect("about_to_popup", callable_mp(this, &InspectorDock::_prepare_resource_extra_popup));
resource_extra_button->get_popup()->add_icon_shortcut(get_theme_icon(SNAME("ActionPaste"), SNAME("EditorIcons")), ED_SHORTCUT("property_editor/paste_resource", TTR("Edit Resource from Clipboard")), RESOURCE_EDIT_CLIPBOARD);
resource_extra_button->get_popup()->add_icon_shortcut(get_theme_icon(SNAME("ActionCopy"), SNAME("EditorIcons")), ED_SHORTCUT("property_editor/copy_resource", TTR("Copy Resource")), RESOURCE_COPY);
resource_extra_button->get_popup()->set_item_disabled(1, true);
diff --git a/editor/inspector_dock.h b/editor/inspector_dock.h
index d50785d95c..6615845b66 100644
--- a/editor/inspector_dock.h
+++ b/editor/inspector_dock.h
@@ -102,6 +102,7 @@ class InspectorDock : public VBoxContainer {
void _unref_resource();
void _copy_resource();
void _paste_resource();
+ void _prepare_resource_extra_popup();
void _warning_pressed();
void _resource_created();
diff --git a/editor/plugins/abstract_polygon_2d_editor.cpp b/editor/plugins/abstract_polygon_2d_editor.cpp
index df01ecd1be..ef3b0588b8 100644
--- a/editor/plugins/abstract_polygon_2d_editor.cpp
+++ b/editor/plugins/abstract_polygon_2d_editor.cpp
@@ -261,7 +261,7 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event)
Transform2D xform = canvas_item_editor->get_canvas_transform() * _get_node()->get_global_transform();
Vector2 gpoint = mb->get_position();
- Vector2 cpoint = _get_node()->get_global_transform().affine_inverse().xform(canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(mb->get_position())));
+ Vector2 cpoint = _get_node()->to_local(canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(mb->get_position())));
if (mode == MODE_EDIT || (_is_line() && mode == MODE_CREATE)) {
if (mb->get_button_index() == MOUSE_BUTTON_LEFT) {
@@ -396,7 +396,7 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event)
Vector2 gpoint = mm->get_position();
if (edited_point.valid() && (wip_active || (mm->get_button_mask() & MOUSE_BUTTON_MASK_LEFT))) {
- Vector2 cpoint = _get_node()->get_global_transform().affine_inverse().xform(canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint)));
+ Vector2 cpoint = _get_node()->to_local(canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint)));
//Move the point in a single axis. Should only work when editing a polygon and while holding shift.
if (mode == MODE_EDIT && mm->is_shift_pressed()) {
diff --git a/editor/plugins/collision_shape_2d_editor_plugin.cpp b/editor/plugins/collision_shape_2d_editor_plugin.cpp
index 4266e0f676..486f947e43 100644
--- a/editor/plugins/collision_shape_2d_editor_plugin.cpp
+++ b/editor/plugins/collision_shape_2d_editor_plugin.cpp
@@ -36,9 +36,9 @@
#include "scene/resources/circle_shape_2d.h"
#include "scene/resources/concave_polygon_shape_2d.h"
#include "scene/resources/convex_polygon_shape_2d.h"
-#include "scene/resources/line_shape_2d.h"
#include "scene/resources/rectangle_shape_2d.h"
#include "scene/resources/segment_shape_2d.h"
+#include "scene/resources/world_margin_shape_2d.h"
void CollisionShape2DEditor::_node_removed(Node *p_node) {
if (p_node == node) {
@@ -74,8 +74,8 @@ Variant CollisionShape2DEditor::get_handle_value(int idx) const {
case CONVEX_POLYGON_SHAPE: {
} break;
- case LINE_SHAPE: {
- Ref<LineShape2D> line = node->get_shape();
+ case WORLD_MARGIN_SHAPE: {
+ Ref<WorldMarginShape2D> line = node->get_shape();
if (idx == 0) {
return line->get_distance();
@@ -142,9 +142,9 @@ void CollisionShape2DEditor::set_handle(int idx, Point2 &p_point) {
case CONVEX_POLYGON_SHAPE: {
} break;
- case LINE_SHAPE: {
+ case WORLD_MARGIN_SHAPE: {
if (idx < 2) {
- Ref<LineShape2D> line = node->get_shape();
+ Ref<WorldMarginShape2D> line = node->get_shape();
if (idx == 0) {
line->set_distance(p_point.length());
@@ -241,8 +241,8 @@ void CollisionShape2DEditor::commit_handle(int idx, Variant &p_org) {
// Cannot be edited directly, use CollisionPolygon2D instead.
} break;
- case LINE_SHAPE: {
- Ref<LineShape2D> line = node->get_shape();
+ case WORLD_MARGIN_SHAPE: {
+ Ref<WorldMarginShape2D> line = node->get_shape();
if (idx == 0) {
undo_redo->add_do_method(line.ptr(), "set_distance", line->get_distance());
@@ -397,8 +397,8 @@ void CollisionShape2DEditor::_get_current_shape_type() {
shape_type = CONCAVE_POLYGON_SHAPE;
} else if (Object::cast_to<ConvexPolygonShape2D>(*s)) {
shape_type = CONVEX_POLYGON_SHAPE;
- } else if (Object::cast_to<LineShape2D>(*s)) {
- shape_type = LINE_SHAPE;
+ } else if (Object::cast_to<WorldMarginShape2D>(*s)) {
+ shape_type = WORLD_MARGIN_SHAPE;
} else if (Object::cast_to<RectangleShape2D>(*s)) {
shape_type = RECTANGLE_SHAPE;
} else if (Object::cast_to<SegmentShape2D>(*s)) {
@@ -464,8 +464,8 @@ void CollisionShape2DEditor::forward_canvas_draw_over_viewport(Control *p_overla
case CONVEX_POLYGON_SHAPE: {
} break;
- case LINE_SHAPE: {
- Ref<LineShape2D> shape = node->get_shape();
+ case WORLD_MARGIN_SHAPE: {
+ Ref<WorldMarginShape2D> shape = node->get_shape();
handles.resize(2);
handles.write[0] = shape->get_normal() * shape->get_distance();
diff --git a/editor/plugins/collision_shape_2d_editor_plugin.h b/editor/plugins/collision_shape_2d_editor_plugin.h
index 130ec708cf..056e1b5b7d 100644
--- a/editor/plugins/collision_shape_2d_editor_plugin.h
+++ b/editor/plugins/collision_shape_2d_editor_plugin.h
@@ -46,7 +46,7 @@ class CollisionShape2DEditor : public Control {
CIRCLE_SHAPE,
CONCAVE_POLYGON_SHAPE,
CONVEX_POLYGON_SHAPE,
- LINE_SHAPE,
+ WORLD_MARGIN_SHAPE,
RECTANGLE_SHAPE,
SEGMENT_SHAPE
};
diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp
index d47bd2d410..95f68d5f7f 100644
--- a/editor/plugins/editor_preview_plugins.cpp
+++ b/editor/plugins/editor_preview_plugins.cpp
@@ -505,12 +505,12 @@ Ref<Texture2D> EditorScriptPreviewPlugin::generate(const RES &p_from, const Size
int thumbnail_size = MAX(p_size.x, p_size.y);
img->create(thumbnail_size, thumbnail_size, false, Image::FORMAT_RGBA8);
- Color bg_color = EditorSettings::get_singleton()->get("text_editor/highlighting/background_color");
- Color keyword_color = EditorSettings::get_singleton()->get("text_editor/highlighting/keyword_color");
- Color control_flow_keyword_color = EditorSettings::get_singleton()->get("text_editor/highlighting/control_flow_keyword_color");
- Color text_color = EditorSettings::get_singleton()->get("text_editor/highlighting/text_color");
- Color symbol_color = EditorSettings::get_singleton()->get("text_editor/highlighting/symbol_color");
- Color comment_color = EditorSettings::get_singleton()->get("text_editor/highlighting/comment_color");
+ Color bg_color = EditorSettings::get_singleton()->get("text_editor/theme/highlighting/background_color");
+ Color keyword_color = EditorSettings::get_singleton()->get("text_editor/theme/highlighting/keyword_color");
+ Color control_flow_keyword_color = EditorSettings::get_singleton()->get("text_editor/theme/highlighting/control_flow_keyword_color");
+ Color text_color = EditorSettings::get_singleton()->get("text_editor/theme/highlighting/text_color");
+ Color symbol_color = EditorSettings::get_singleton()->get("text_editor/theme/highlighting/symbol_color");
+ Color comment_color = EditorSettings::get_singleton()->get("text_editor/theme/highlighting/comment_color");
if (bg_color.a == 0) {
bg_color = Color(0, 0, 0, 0);
diff --git a/editor/plugins/gpu_particles_2d_editor_plugin.cpp b/editor/plugins/gpu_particles_2d_editor_plugin.cpp
index efec5a709d..dd91df747a 100644
--- a/editor/plugins/gpu_particles_2d_editor_plugin.cpp
+++ b/editor/plugins/gpu_particles_2d_editor_plugin.cpp
@@ -60,13 +60,16 @@ void GPUParticles2DEditorPlugin::_file_selected(const String &p_file) {
void GPUParticles2DEditorPlugin::_menu_callback(int p_idx) {
switch (p_idx) {
case MENU_GENERATE_VISIBILITY_RECT: {
- double gen_time = particles->get_lifetime();
- if (gen_time < 1.0) {
- generate_seconds->set_value(1.0);
+ // Add one second to the default generation lifetime, since the progress is updated every second.
+ generate_seconds->set_value(MAX(1.0, trunc(particles->get_lifetime()) + 1.0));
+
+ if (generate_seconds->get_value() >= 11.0 + CMP_EPSILON) {
+ // Only pop up the time dialog if the particle's lifetime is long enough to warrant shortening it.
+ generate_visibility_rect->popup_centered();
} else {
- generate_seconds->set_value(trunc(gen_time) + 1.0);
+ // Generate the visibility rect immediately.
+ _generate_visibility_rect();
}
- generate_visibility_rect->popup_centered();
} break;
case MENU_LOAD_EMISSION_MASK: {
file->popup_file_dialog();
@@ -104,7 +107,7 @@ void GPUParticles2DEditorPlugin::_generate_visibility_rect() {
float running = 0.0;
- EditorProgress ep("gen_vrect", TTR("Generating Visibility Rect"), int(time));
+ EditorProgress ep("gen_vrect", TTR("Generating Visibility Rect (Waiting for Particle Simulation)"), int(time));
bool was_emitting = particles->is_emitting();
if (!was_emitting) {
diff --git a/editor/plugins/gpu_particles_3d_editor_plugin.cpp b/editor/plugins/gpu_particles_3d_editor_plugin.cpp
index fff25b6f59..903a3689b0 100644
--- a/editor/plugins/gpu_particles_3d_editor_plugin.cpp
+++ b/editor/plugins/gpu_particles_3d_editor_plugin.cpp
@@ -238,14 +238,16 @@ void GPUParticles3DEditor::_notification(int p_notification) {
void GPUParticles3DEditor::_menu_option(int p_option) {
switch (p_option) {
case MENU_OPTION_GENERATE_AABB: {
- float gen_time = node->get_lifetime();
+ // Add one second to the default generation lifetime, since the progress is updated every second.
+ generate_seconds->set_value(MAX(1.0, trunc(node->get_lifetime()) + 1.0));
- if (gen_time < 1.0) {
- generate_seconds->set_value(1.0);
+ if (generate_seconds->get_value() >= 11.0 + CMP_EPSILON) {
+ // Only pop up the time dialog if the particle's lifetime is long enough to warrant shortening it.
+ generate_aabb->popup_centered();
} else {
- generate_seconds->set_value(trunc(gen_time) + 1.0);
+ // Generate the visibility AABB immediately.
+ _generate_aabb();
}
- generate_aabb->popup_centered();
} break;
case MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE: {
Ref<ParticlesMaterial> material = node->get_process_material();
@@ -286,7 +288,7 @@ void GPUParticles3DEditor::_generate_aabb() {
double running = 0.0;
- EditorProgress ep("gen_aabb", TTR("Generating AABB"), int(time));
+ EditorProgress ep("gen_aabb", TTR("Generating Visibility AABB (Waiting for Particle Simulation)"), int(time));
bool was_emitting = node->is_emitting();
if (!was_emitting) {
diff --git a/editor/plugins/node_3d_editor_gizmos.cpp b/editor/plugins/node_3d_editor_gizmos.cpp
index dcea7b26f3..b93e12d7fa 100644
--- a/editor/plugins/node_3d_editor_gizmos.cpp
+++ b/editor/plugins/node_3d_editor_gizmos.cpp
@@ -2100,64 +2100,76 @@ void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
surface_tool->begin(Mesh::PRIMITIVE_LINES);
surface_tool->set_material(material);
- Vector<Transform3D> grests;
+ LocalVector<Transform3D> grests;
grests.resize(skel->get_bone_count());
- Vector<int> bones;
- Vector<float> weights;
+ LocalVector<int> bones;
+ LocalVector<real_t> weights;
bones.resize(4);
weights.resize(4);
for (int i = 0; i < 4; i++) {
- bones.write[i] = 0;
- weights.write[i] = 0;
+ bones[i] = 0;
+ weights[i] = 0;
}
- weights.write[0] = 1;
+ weights[0] = 1;
AABB aabb;
Color bonecolor = Color(1.0, 0.4, 0.4, 0.3);
Color rootcolor = Color(0.4, 1.0, 0.4, 0.1);
- for (int i_bone = 0; i_bone < skel->get_bone_count(); i_bone++) {
- int i = skel->get_process_order(i_bone);
+ //LocalVector<int> bones_to_process = skel->get_parentless_bones();
+ LocalVector<int> bones_to_process;
+ bones_to_process = skel->get_parentless_bones();
- int parent = skel->get_bone_parent(i);
+ while (bones_to_process.size() > 0) {
+ int current_bone_idx = bones_to_process[0];
+ bones_to_process.erase(current_bone_idx);
- if (parent >= 0) {
- grests.write[i] = grests[parent] * skel->get_bone_rest(i);
+ LocalVector<int> child_bones_vector;
+ child_bones_vector = skel->get_bone_children(current_bone_idx);
+ int child_bones_size = child_bones_vector.size();
- Vector3 v0 = grests[parent].origin;
- Vector3 v1 = grests[i].origin;
- Vector3 d = (v1 - v0).normalized();
- float dist = v0.distance_to(v1);
+ // You have children but no parent, then you must be a root/parentless bone.
+ if (child_bones_size >= 0 && skel->get_bone_parent(current_bone_idx) <= 0) {
+ grests[current_bone_idx] = skel->global_pose_to_local_pose(current_bone_idx, skel->get_bone_global_pose(current_bone_idx));
+ }
- //find closest axis
- int closest = -1;
- float closest_d = 0.0;
+ for (int i = 0; i < child_bones_size; i++) {
+ int child_bone_idx = child_bones_vector[i];
+ grests[child_bone_idx] = skel->global_pose_to_local_pose(child_bone_idx, skel->get_bone_global_pose(child_bone_idx));
+ Vector3 v0 = grests[current_bone_idx].origin;
+ Vector3 v1 = grests[child_bone_idx].origin;
+ Vector3 d = skel->get_bone_rest(child_bone_idx).origin.normalized();
+ real_t dist = skel->get_bone_rest(child_bone_idx).origin.length();
+
+ // Find closest axis.
+ int closest = -1;
+ real_t closest_d = 0.0;
for (int j = 0; j < 3; j++) {
- float dp = Math::abs(grests[parent].basis[j].normalized().dot(d));
+ real_t dp = Math::abs(grests[current_bone_idx].basis[j].normalized().dot(d));
if (j == 0 || dp > closest_d) {
closest = j;
}
}
- //find closest other
+ // Find closest other.
Vector3 first;
Vector3 points[4];
- int pointidx = 0;
+ int point_idx = 0;
for (int j = 0; j < 3; j++) {
- bones.write[0] = parent;
+ bones[0] = current_bone_idx;
surface_tool->set_bones(bones);
surface_tool->set_weights(weights);
surface_tool->set_color(rootcolor);
- surface_tool->add_vertex(v0 - grests[parent].basis[j].normalized() * dist * 0.05);
+ surface_tool->add_vertex(v0 - grests[current_bone_idx].basis[j].normalized() * dist * 0.05);
surface_tool->set_bones(bones);
surface_tool->set_weights(weights);
surface_tool->set_color(rootcolor);
- surface_tool->add_vertex(v0 + grests[parent].basis[j].normalized() * dist * 0.05);
+ surface_tool->add_vertex(v0 + grests[current_bone_idx].basis[j].normalized() * dist * 0.05);
if (j == closest) {
continue;
@@ -2165,7 +2177,7 @@ void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
Vector3 axis;
if (first == Vector3()) {
- axis = d.cross(d.cross(grests[parent].basis[j])).normalized();
+ axis = d.cross(d.cross(grests[current_bone_idx].basis[j])).normalized();
first = axis;
} else {
axis = d.cross(first).normalized();
@@ -2178,7 +2190,7 @@ void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
Vector3 point = v0 + d * dist * 0.2;
point += axis * dist * 0.1;
- bones.write[0] = parent;
+ bones[0] = current_bone_idx;
surface_tool->set_bones(bones);
surface_tool->set_weights(weights);
surface_tool->set_color(bonecolor);
@@ -2188,23 +2200,22 @@ void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
surface_tool->set_color(bonecolor);
surface_tool->add_vertex(point);
- bones.write[0] = parent;
+ bones[0] = current_bone_idx;
surface_tool->set_bones(bones);
surface_tool->set_weights(weights);
surface_tool->set_color(bonecolor);
surface_tool->add_vertex(point);
- bones.write[0] = i;
+ bones[0] = child_bone_idx;
surface_tool->set_bones(bones);
surface_tool->set_weights(weights);
surface_tool->set_color(bonecolor);
surface_tool->add_vertex(v1);
- points[pointidx++] = point;
+ points[point_idx++] = point;
}
}
-
SWAP(points[1], points[2]);
for (int j = 0; j < 4; j++) {
- bones.write[0] = parent;
+ bones[0] = current_bone_idx;
surface_tool->set_bones(bones);
surface_tool->set_weights(weights);
surface_tool->set_color(bonecolor);
@@ -2214,9 +2225,9 @@ void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
surface_tool->set_color(bonecolor);
surface_tool->add_vertex(points[(j + 1) % 4]);
}
- } else {
- grests.write[i] = skel->get_bone_rest(i);
- bones.write[0] = i;
+
+ // Add the bone's children to the list of bones to be processed.
+ bones_to_process.push_back(child_bones_vector[i]);
}
}
diff --git a/editor/plugins/node_3d_editor_gizmos.h b/editor/plugins/node_3d_editor_gizmos.h
index 64f46f2b1a..2cc0951557 100644
--- a/editor/plugins/node_3d_editor_gizmos.h
+++ b/editor/plugins/node_3d_editor_gizmos.h
@@ -31,6 +31,7 @@
#ifndef NODE_3D_EDITOR_GIZMOS_H
#define NODE_3D_EDITOR_GIZMOS_H
+#include "core/templates/local_vector.h"
#include "core/templates/ordered_hash_map.h"
#include "scene/3d/node_3d.h"
#include "scene/3d/skeleton_3d.h"
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index e9a67491aa..4300a56ef0 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -690,53 +690,55 @@ void Node3DEditorViewport::_select_region() {
Node3D *single_selected = spatial_editor->get_single_selected_node();
Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(single_selected);
- Ref<EditorNode3DGizmo> old_gizmo;
- if (!clicked_wants_append) {
- se->subgizmos.clear();
- old_gizmo = se->gizmo;
- se->gizmo.unref();
- }
-
- bool found_subgizmos = false;
- Vector<Ref<Node3DGizmo>> gizmos = single_selected->get_gizmos();
- for (int j = 0; j < gizmos.size(); j++) {
- Ref<EditorNode3DGizmo> seg = gizmos[j];
- if (!seg.is_valid()) {
- continue;
+ if (se) {
+ Ref<EditorNode3DGizmo> old_gizmo;
+ if (!clicked_wants_append) {
+ se->subgizmos.clear();
+ old_gizmo = se->gizmo;
+ se->gizmo.unref();
}
- if (se->gizmo.is_valid() && se->gizmo != seg) {
- continue;
- }
+ bool found_subgizmos = false;
+ Vector<Ref<Node3DGizmo>> gizmos = single_selected->get_gizmos();
+ for (int j = 0; j < gizmos.size(); j++) {
+ Ref<EditorNode3DGizmo> seg = gizmos[j];
+ if (!seg.is_valid()) {
+ continue;
+ }
+
+ if (se->gizmo.is_valid() && se->gizmo != seg) {
+ continue;
+ }
- Vector<int> subgizmos = seg->subgizmos_intersect_frustum(camera, frustum);
- if (!subgizmos.is_empty()) {
- se->gizmo = seg;
- for (int i = 0; i < subgizmos.size(); i++) {
- int subgizmo_id = subgizmos[i];
- if (!se->subgizmos.has(subgizmo_id)) {
- se->subgizmos.insert(subgizmo_id, se->gizmo->get_subgizmo_transform(subgizmo_id));
+ Vector<int> subgizmos = seg->subgizmos_intersect_frustum(camera, frustum);
+ if (!subgizmos.is_empty()) {
+ se->gizmo = seg;
+ for (int i = 0; i < subgizmos.size(); i++) {
+ int subgizmo_id = subgizmos[i];
+ if (!se->subgizmos.has(subgizmo_id)) {
+ se->subgizmos.insert(subgizmo_id, se->gizmo->get_subgizmo_transform(subgizmo_id));
+ }
}
+ found_subgizmos = true;
+ break;
}
- found_subgizmos = true;
- break;
}
- }
- if (!clicked_wants_append || found_subgizmos) {
- if (se->gizmo.is_valid()) {
- se->gizmo->redraw();
- }
+ if (!clicked_wants_append || found_subgizmos) {
+ if (se->gizmo.is_valid()) {
+ se->gizmo->redraw();
+ }
- if (old_gizmo != se->gizmo && old_gizmo.is_valid()) {
- old_gizmo->redraw();
- }
+ if (old_gizmo != se->gizmo && old_gizmo.is_valid()) {
+ old_gizmo->redraw();
+ }
- spatial_editor->update_transform_gizmo();
- }
+ spatial_editor->update_transform_gizmo();
+ }
- if (found_subgizmos) {
- return;
+ if (found_subgizmos) {
+ return;
+ }
}
}
@@ -809,18 +811,66 @@ void Node3DEditorViewport::_select_region() {
}
void Node3DEditorViewport::_update_name() {
- String view_mode = orthogonal ? TTR("Orthogonal") : TTR("Perspective");
+ String name;
- if (auto_orthogonal) {
- view_mode += " [auto]";
+ switch (view_type) {
+ case VIEW_TYPE_USER: {
+ if (orthogonal) {
+ name = TTR("Orthogonal");
+ } else {
+ name = TTR("Perspective");
+ }
+ } break;
+ case VIEW_TYPE_TOP: {
+ if (orthogonal) {
+ name = TTR("Top Orthogonal");
+ } else {
+ name = TTR("Top Perspective");
+ }
+ } break;
+ case VIEW_TYPE_BOTTOM: {
+ if (orthogonal) {
+ name = TTR("Bottom Orthogonal");
+ } else {
+ name = TTR("Bottom Perspective");
+ }
+ } break;
+ case VIEW_TYPE_LEFT: {
+ if (orthogonal) {
+ name = TTR("Left Orthogonal");
+ } else {
+ name = TTR("Right Perspective");
+ }
+ } break;
+ case VIEW_TYPE_RIGHT: {
+ if (orthogonal) {
+ name = TTR("Right Orthogonal");
+ } else {
+ name = TTR("Right Perspective");
+ }
+ } break;
+ case VIEW_TYPE_FRONT: {
+ if (orthogonal) {
+ name = TTR("Front Orthogonal");
+ } else {
+ name = TTR("Front Perspective");
+ }
+ } break;
+ case VIEW_TYPE_REAR: {
+ if (orthogonal) {
+ name = TTR("Rear Orthogonal");
+ } else {
+ name = TTR("Rear Perspective");
+ }
+ } break;
}
- if (name != "") {
- view_menu->set_text(name + " " + view_mode);
- } else {
- view_menu->set_text(view_mode);
+ if (auto_orthogonal) {
+ // TRANSLATORS: This will be appended to the view name when Auto Orthogonal is enabled.
+ name += TTR(" [auto]");
}
+ view_menu->set_text(name);
view_menu->set_size(Vector2(0, 0)); // resets the button size
}
@@ -1384,7 +1434,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
case TRANSFORM_VIEW: {
_edit.plane = TRANSFORM_X_AXIS;
set_message(TTR("X-Axis Transform."), 2);
- name = "";
+ view_type = VIEW_TYPE_USER;
_update_name();
} break;
case TRANSFORM_X_AXIS: {
@@ -2325,7 +2375,7 @@ void Node3DEditorViewport::_nav_orbit(Ref<InputEventWithModifiers> p_event, cons
} else {
cursor.y_rot += p_relative.x * radians_per_pixel;
}
- name = "";
+ view_type = VIEW_TYPE_USER;
_update_name();
}
@@ -2363,7 +2413,7 @@ void Node3DEditorViewport::_nav_look(Ref<InputEventWithModifiers> p_event, const
Vector3 diff = prev_pos - pos;
cursor.pos += diff;
- name = "";
+ view_type = VIEW_TYPE_USER;
_update_name();
}
@@ -3008,7 +3058,7 @@ void Node3DEditorViewport::_menu_option(int p_option) {
cursor.y_rot = 0;
cursor.x_rot = Math_PI / 2.0;
set_message(TTR("Top View."), 2);
- name = TTR("Top");
+ view_type = VIEW_TYPE_TOP;
_set_auto_orthogonal();
_update_name();
@@ -3017,7 +3067,7 @@ void Node3DEditorViewport::_menu_option(int p_option) {
cursor.y_rot = 0;
cursor.x_rot = -Math_PI / 2.0;
set_message(TTR("Bottom View."), 2);
- name = TTR("Bottom");
+ view_type = VIEW_TYPE_BOTTOM;
_set_auto_orthogonal();
_update_name();
@@ -3026,7 +3076,7 @@ void Node3DEditorViewport::_menu_option(int p_option) {
cursor.x_rot = 0;
cursor.y_rot = Math_PI / 2.0;
set_message(TTR("Left View."), 2);
- name = TTR("Left");
+ view_type = VIEW_TYPE_LEFT;
_set_auto_orthogonal();
_update_name();
@@ -3035,7 +3085,7 @@ void Node3DEditorViewport::_menu_option(int p_option) {
cursor.x_rot = 0;
cursor.y_rot = -Math_PI / 2.0;
set_message(TTR("Right View."), 2);
- name = TTR("Right");
+ view_type = VIEW_TYPE_RIGHT;
_set_auto_orthogonal();
_update_name();
@@ -3044,7 +3094,7 @@ void Node3DEditorViewport::_menu_option(int p_option) {
cursor.x_rot = 0;
cursor.y_rot = Math_PI;
set_message(TTR("Front View."), 2);
- name = TTR("Front");
+ view_type = VIEW_TYPE_FRONT;
_set_auto_orthogonal();
_update_name();
@@ -3053,7 +3103,7 @@ void Node3DEditorViewport::_menu_option(int p_option) {
cursor.x_rot = 0;
cursor.y_rot = 0;
set_message(TTR("Rear View."), 2);
- name = TTR("Rear");
+ view_type = VIEW_TYPE_REAR;
_set_auto_orthogonal();
_update_name();
@@ -3597,8 +3647,8 @@ void Node3DEditorViewport::set_state(const Dictionary &p_state) {
_menu_option(VIEW_PERSPECTIVE);
}
}
- if (p_state.has("view_name")) {
- name = p_state["view_name"];
+ if (p_state.has("view_type")) {
+ view_type = ViewType(p_state["view_type"].operator int());
_update_name();
}
if (p_state.has("auto_orthogonal")) {
@@ -3706,7 +3756,7 @@ Dictionary Node3DEditorViewport::get_state() const {
d["distance"] = cursor.distance;
d["use_environment"] = camera->get_environment().is_valid();
d["use_orthogonal"] = camera->get_projection() == Camera3D::PROJECTION_ORTHOGONAL;
- d["view_name"] = name;
+ d["view_type"] = view_type;
d["auto_orthogonal"] = auto_orthogonal;
d["auto_orthogonal_enabled"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_AUTO_ORTHOGONAL));
if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_NORMAL))) {
@@ -3751,7 +3801,7 @@ void Node3DEditorViewport::reset() {
message_time = 0;
message = "";
last_message = "";
- name = "";
+ view_type = VIEW_TYPE_USER;
cursor = Cursor();
_update_name();
@@ -4366,7 +4416,7 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, Edito
viewport->set_as_audio_listener_3d(true);
}
- name = "";
+ view_type = VIEW_TYPE_USER;
_update_name();
EditorSettings::get_singleton()->connect("settings_changed", callable_mp(this, &Node3DEditorViewport::update_transform_gizmo_view));
@@ -4776,13 +4826,13 @@ void _update_all_gizmos(Node *p_node) {
}
void Node3DEditor::update_all_gizmos(Node *p_node) {
+ if (!p_node && get_tree()) {
+ p_node = get_tree()->get_edited_scene_root();
+ }
+
if (!p_node) {
- if (SceneTree::get_singleton()) {
- p_node = SceneTree::get_singleton()->get_root();
- } else {
- // No scene tree, so nothing to update.
- return;
- }
+ // No edited scene, so nothing to update.
+ return;
}
_update_all_gizmos(p_node);
}
@@ -6591,6 +6641,7 @@ void Node3DEditor::_notification(int p_what) {
_register_all_gizmos();
_update_gizmos_menu();
_init_indicators();
+ update_all_gizmos();
} break;
case NOTIFICATION_EXIT_TREE: {
_finish_indicators();
@@ -7724,7 +7775,6 @@ void Node3DEditor::add_gizmo_plugin(Ref<EditorNode3DGizmoPlugin> p_plugin) {
gizmo_plugins_by_name.sort_custom<_GizmoPluginNameComparator>();
_update_gizmos_menu();
- Node3DEditor::get_singleton()->update_all_gizmos();
}
void Node3DEditor::remove_gizmo_plugin(Ref<EditorNode3DGizmoPlugin> p_plugin) {
diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h
index 094aa5662f..d29c1efabb 100644
--- a/editor/plugins/node_3d_editor_plugin.h
+++ b/editor/plugins/node_3d_editor_plugin.h
@@ -143,6 +143,16 @@ class Node3DEditorViewport : public Control {
VIEW_MAX
};
+ enum ViewType {
+ VIEW_TYPE_USER,
+ VIEW_TYPE_TOP,
+ VIEW_TYPE_BOTTOM,
+ VIEW_TYPE_LEFT,
+ VIEW_TYPE_RIGHT,
+ VIEW_TYPE_FRONT,
+ VIEW_TYPE_REAR,
+ };
+
public:
enum {
GIZMO_BASE_LAYER = 27,
@@ -172,7 +182,7 @@ private:
int gpu_time_history_index;
int index;
- String name;
+ ViewType view_type;
void _menu_option(int p_option);
void _set_auto_orthogonal();
Node3D *preview_node;
diff --git a/editor/plugins/path_2d_editor_plugin.cpp b/editor/plugins/path_2d_editor_plugin.cpp
index 8866e8c53e..584eb84ecd 100644
--- a/editor/plugins/path_2d_editor_plugin.cpp
+++ b/editor/plugins/path_2d_editor_plugin.cpp
@@ -77,7 +77,7 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
Transform2D xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform();
Vector2 gpoint = mb->get_position();
- Vector2 cpoint = node->get_global_transform().affine_inverse().xform(canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(mb->get_position())));
+ Vector2 cpoint = node->to_local(canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(mb->get_position())));
if (mb->is_pressed() && action == ACTION_NONE) {
Ref<Curve2D> curve = node->get_curve();
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index e6762826dd..226a54b966 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -94,13 +94,13 @@ void EditorStandardSyntaxHighlighter::_update_cache() {
highlighter->clear_member_keyword_colors();
highlighter->clear_color_regions();
- highlighter->set_symbol_color(EDITOR_GET("text_editor/highlighting/symbol_color"));
- highlighter->set_function_color(EDITOR_GET("text_editor/highlighting/function_color"));
- highlighter->set_number_color(EDITOR_GET("text_editor/highlighting/number_color"));
- highlighter->set_member_variable_color(EDITOR_GET("text_editor/highlighting/member_variable_color"));
+ highlighter->set_symbol_color(EDITOR_GET("text_editor/theme/highlighting/symbol_color"));
+ highlighter->set_function_color(EDITOR_GET("text_editor/theme/highlighting/function_color"));
+ highlighter->set_number_color(EDITOR_GET("text_editor/theme/highlighting/number_color"));
+ highlighter->set_member_variable_color(EDITOR_GET("text_editor/theme/highlighting/member_variable_color"));
/* Engine types. */
- const Color type_color = EDITOR_GET("text_editor/highlighting/engine_type_color");
+ const Color type_color = EDITOR_GET("text_editor/theme/highlighting/engine_type_color");
List<StringName> types;
ClassDB::get_class_list(&types);
for (const StringName &E : types) {
@@ -112,7 +112,7 @@ void EditorStandardSyntaxHighlighter::_update_cache() {
}
/* User types. */
- const Color usertype_color = EDITOR_GET("text_editor/highlighting/user_type_color");
+ const Color usertype_color = EDITOR_GET("text_editor/theme/highlighting/user_type_color");
List<StringName> global_classes;
ScriptServer::get_global_class_list(&global_classes);
for (const StringName &E : global_classes) {
@@ -131,7 +131,7 @@ void EditorStandardSyntaxHighlighter::_update_cache() {
const Ref<Script> script = _get_edited_resource();
if (script.is_valid()) {
/* Core types. */
- const Color basetype_color = EDITOR_GET("text_editor/highlighting/base_type_color");
+ const Color basetype_color = EDITOR_GET("text_editor/theme/highlighting/base_type_color");
List<String> core_types;
script->get_language()->get_core_type_words(&core_types);
for (const String &E : core_types) {
@@ -139,8 +139,8 @@ void EditorStandardSyntaxHighlighter::_update_cache() {
}
/* Reserved words. */
- const Color keyword_color = EDITOR_GET("text_editor/highlighting/keyword_color");
- const Color control_flow_keyword_color = EDITOR_GET("text_editor/highlighting/control_flow_keyword_color");
+ const Color keyword_color = EDITOR_GET("text_editor/theme/highlighting/keyword_color");
+ const Color control_flow_keyword_color = EDITOR_GET("text_editor/theme/highlighting/control_flow_keyword_color");
List<String> keywords;
script->get_language()->get_reserved_words(&keywords);
for (const String &E : keywords) {
@@ -152,7 +152,7 @@ void EditorStandardSyntaxHighlighter::_update_cache() {
}
/* Member types. */
- const Color member_variable_color = EDITOR_GET("text_editor/highlighting/member_variable_color");
+ const Color member_variable_color = EDITOR_GET("text_editor/theme/highlighting/member_variable_color");
StringName instance_base = script->get_instance_base_type();
if (instance_base != StringName()) {
List<PropertyInfo> plist;
@@ -176,7 +176,7 @@ void EditorStandardSyntaxHighlighter::_update_cache() {
}
/* Comments */
- const Color comment_color = EDITOR_GET("text_editor/highlighting/comment_color");
+ const Color comment_color = EDITOR_GET("text_editor/theme/highlighting/comment_color");
List<String> comments;
script->get_language()->get_comment_delimiters(&comments);
for (const String &comment : comments) {
@@ -186,7 +186,7 @@ void EditorStandardSyntaxHighlighter::_update_cache() {
}
/* Strings */
- const Color string_color = EDITOR_GET("text_editor/highlighting/string_color");
+ const Color string_color = EDITOR_GET("text_editor/theme/highlighting/string_color");
List<String> strings;
script->get_language()->get_string_delimiters(&strings);
for (const String &string : strings) {
@@ -961,7 +961,7 @@ bool ScriptEditor::_test_script_times_on_disk(RES p_for_script) {
bool need_ask = false;
bool need_reload = false;
- bool use_autoreload = bool(EDITOR_DEF("text_editor/files/auto_reload_scripts_on_external_change", false));
+ bool use_autoreload = bool(EDITOR_DEF("text_editor/behavior/files/auto_reload_scripts_on_external_change", false));
for (int i = 0; i < tab_container->get_child_count(); i++) {
ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
@@ -1712,7 +1712,7 @@ void ScriptEditor::_update_members_overview_visibility() {
}
void ScriptEditor::_toggle_members_overview_alpha_sort(bool p_alphabetic_sort) {
- EditorSettings::get_singleton()->set("text_editor/tools/sort_members_outline_alphabetically", p_alphabetic_sort);
+ EditorSettings::get_singleton()->set("text_editor/script_list/sort_members_outline_alphabetically", p_alphabetic_sort);
_update_members_overview();
}
@@ -1725,7 +1725,7 @@ void ScriptEditor::_update_members_overview() {
}
Vector<String> functions = se->get_functions();
- if (EditorSettings::get_singleton()->get("text_editor/tools/sort_members_outline_alphabetically")) {
+ if (EditorSettings::get_singleton()->get("text_editor/script_list/sort_members_outline_alphabetically")) {
functions.sort();
}
@@ -2104,7 +2104,7 @@ bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_gra
const bool use_external_editor =
EditorSettings::get_singleton()->get("text_editor/external/use_external_editor") ||
(script.is_valid() && script->get_language()->overrides_external_editor());
- const bool open_dominant = EditorSettings::get_singleton()->get("text_editor/files/open_dominant_script_on_scene_change");
+ const bool open_dominant = EditorSettings::get_singleton()->get("text_editor/behavior/files/open_dominant_script_on_scene_change");
const bool should_open = (open_dominant && !use_external_editor) || !EditorNode::get_singleton()->is_changing_scene();
@@ -2469,9 +2469,9 @@ void ScriptEditor::_save_layout() {
}
void ScriptEditor::_editor_settings_changed() {
- trim_trailing_whitespace_on_save = EditorSettings::get_singleton()->get("text_editor/files/trim_trailing_whitespace_on_save");
- convert_indent_on_save = EditorSettings::get_singleton()->get("text_editor/indent/convert_indent_on_save");
- use_space_indentation = EditorSettings::get_singleton()->get("text_editor/indent/type");
+ trim_trailing_whitespace_on_save = EditorSettings::get_singleton()->get("text_editor/behavior/files/trim_trailing_whitespace_on_save");
+ convert_indent_on_save = EditorSettings::get_singleton()->get("text_editor/behavior/files/convert_indent_on_save");
+ use_space_indentation = EditorSettings::get_singleton()->get("text_editor/behavior/indent/type");
members_overview_enabled = EditorSettings::get_singleton()->get("text_editor/script_list/show_members_overview");
help_overview_enabled = EditorSettings::get_singleton()->get("text_editor/help/show_help_index");
@@ -2498,7 +2498,7 @@ void ScriptEditor::_editor_settings_changed() {
_update_script_colors();
_update_script_names();
- ScriptServer::set_reload_scripts_on_save(EDITOR_DEF("text_editor/files/auto_reload_and_parse_scripts_on_save", true));
+ ScriptServer::set_reload_scripts_on_save(EDITOR_DEF("text_editor/behavior/files/auto_reload_and_parse_scripts_on_save", true));
}
void ScriptEditor::_filesystem_changed() {
@@ -2537,7 +2537,7 @@ void ScriptEditor::_update_autosave_timer() {
return;
}
- float autosave_time = EditorSettings::get_singleton()->get("text_editor/files/autosave_interval_secs");
+ float autosave_time = EditorSettings::get_singleton()->get("text_editor/behavior/files/autosave_interval_secs");
if (autosave_time > 0) {
autosave_timer->set_wait_time(autosave_time);
autosave_timer->start();
@@ -2827,7 +2827,7 @@ void ScriptEditor::_make_script_list_context_menu() {
}
void ScriptEditor::set_window_layout(Ref<ConfigFile> p_layout) {
- if (!bool(EDITOR_DEF("text_editor/files/restore_scripts_on_load", true))) {
+ if (!bool(EDITOR_DEF("text_editor/behavior/files/restore_scripts_on_load", true))) {
return;
}
@@ -3120,7 +3120,7 @@ void ScriptEditor::set_scene_root_script(Ref<Script> p_script) {
const bool use_external_editor =
EditorSettings::get_singleton()->get("text_editor/external/use_external_editor") ||
(p_script.is_valid() && p_script->get_language()->overrides_external_editor());
- const bool open_dominant = EditorSettings::get_singleton()->get("text_editor/files/open_dominant_script_on_scene_change");
+ const bool open_dominant = EditorSettings::get_singleton()->get("text_editor/behavior/files/open_dominant_script_on_scene_change");
if (open_dominant && !use_external_editor && p_script.is_valid()) {
edit(p_script);
@@ -3367,7 +3367,7 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
members_overview_alphabeta_sort_button->set_flat(true);
members_overview_alphabeta_sort_button->set_tooltip(TTR("Toggle alphabetical sorting of the method list."));
members_overview_alphabeta_sort_button->set_toggle_mode(true);
- members_overview_alphabeta_sort_button->set_pressed(EditorSettings::get_singleton()->get("text_editor/tools/sort_members_outline_alphabetically"));
+ members_overview_alphabeta_sort_button->set_pressed(EditorSettings::get_singleton()->get("text_editor/script_list/sort_members_outline_alphabetically"));
members_overview_alphabeta_sort_button->connect("toggled", callable_mp(this, &ScriptEditor::_toggle_members_overview_alpha_sort));
buttons_hbox->add_child(members_overview_alphabeta_sort_button);
@@ -3604,9 +3604,9 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
history_pos = -1;
edit_pass = 0;
- trim_trailing_whitespace_on_save = EditorSettings::get_singleton()->get("text_editor/files/trim_trailing_whitespace_on_save");
- convert_indent_on_save = EditorSettings::get_singleton()->get("text_editor/indent/convert_indent_on_save");
- use_space_indentation = EditorSettings::get_singleton()->get("text_editor/indent/type");
+ trim_trailing_whitespace_on_save = EditorSettings::get_singleton()->get("text_editor/behavior/files/trim_trailing_whitespace_on_save");
+ convert_indent_on_save = EditorSettings::get_singleton()->get("text_editor/behavior/files/convert_indent_on_save");
+ use_space_indentation = EditorSettings::get_singleton()->get("text_editor/behavior/indent/type");
ScriptServer::edit_request_func = _open_script_request;
@@ -3703,9 +3703,9 @@ ScriptEditorPlugin::ScriptEditorPlugin(EditorNode *p_node) {
script_editor->hide();
- EDITOR_DEF("text_editor/files/auto_reload_scripts_on_external_change", true);
- ScriptServer::set_reload_scripts_on_save(EDITOR_DEF("text_editor/files/auto_reload_and_parse_scripts_on_save", true));
- EDITOR_DEF("text_editor/files/open_dominant_script_on_scene_change", true);
+ EDITOR_DEF("text_editor/behavior/files/auto_reload_scripts_on_external_change", true);
+ ScriptServer::set_reload_scripts_on_save(EDITOR_DEF("text_editor/behavior/files/auto_reload_and_parse_scripts_on_save", true));
+ EDITOR_DEF("text_editor/behavior/files/open_dominant_script_on_scene_change", true);
EDITOR_DEF("text_editor/external/use_external_editor", false);
EDITOR_DEF("text_editor/external/exec_path", "");
EDITOR_DEF("text_editor/script_list/script_temperature_enabled", true);
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index 1d7592d2c3..4491c13b4c 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -166,8 +166,8 @@ void ScriptTextEditor::enable_editor() {
void ScriptTextEditor::_load_theme_settings() {
CodeEdit *text_edit = code_editor->get_text_editor();
- Color updated_marked_line_color = EDITOR_GET("text_editor/highlighting/mark_color");
- Color updated_safe_line_number_color = EDITOR_GET("text_editor/highlighting/safe_line_number_color");
+ Color updated_marked_line_color = EDITOR_GET("text_editor/theme/highlighting/mark_color");
+ Color updated_safe_line_number_color = EDITOR_GET("text_editor/theme/highlighting/safe_line_number_color");
bool safe_line_number_color_updated = updated_safe_line_number_color != safe_line_number_color;
bool marked_line_color_updated = updated_marked_line_color != marked_line_color;
@@ -294,7 +294,7 @@ bool ScriptTextEditor::show_members_overview() {
}
void ScriptTextEditor::update_settings() {
- code_editor->get_text_editor()->set_gutter_draw(connection_gutter, EditorSettings::get_singleton()->get("text_editor/appearance/show_info_gutter"));
+ code_editor->get_text_editor()->set_gutter_draw(connection_gutter, EditorSettings::get_singleton()->get("text_editor/appearance/gutters/show_info_gutter"));
code_editor->update_editor_settings();
}
@@ -506,7 +506,7 @@ void ScriptTextEditor::_validate_script() {
}
errors_panel->pop(); // Table
- bool highlight_safe = EDITOR_DEF("text_editor/highlighting/highlight_type_safe_lines", true);
+ bool highlight_safe = EDITOR_DEF("text_editor/appearance/gutters/highlight_type_safe_lines", true);
bool last_is_safe = false;
for (int i = 0; i < te->get_line_count(); i++) {
if (errors.is_empty()) {
@@ -1527,7 +1527,7 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) {
int row = pos.y;
int col = pos.x;
- tx->set_move_caret_on_right_click_enabled(EditorSettings::get_singleton()->get("text_editor/cursor/right_click_moves_caret"));
+ tx->set_move_caret_on_right_click_enabled(EditorSettings::get_singleton()->get("text_editor/behavior/navigation/move_caret_on_right_click"));
if (tx->is_move_caret_on_right_click_enabled()) {
if (tx->has_selection()) {
int from_line = tx->get_selection_from_line();
diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp
index 29436e32b2..22ca5592bd 100644
--- a/editor/plugins/shader_editor_plugin.cpp
+++ b/editor/plugins/shader_editor_plugin.cpp
@@ -96,7 +96,7 @@ void ShaderTextEditor::set_warnings_panel(RichTextLabel *p_warnings_panel) {
void ShaderTextEditor::_load_theme_settings() {
CodeEdit *text_editor = get_text_editor();
- Color updated_marked_line_color = EDITOR_GET("text_editor/highlighting/mark_color");
+ Color updated_marked_line_color = EDITOR_GET("text_editor/theme/highlighting/mark_color");
if (updated_marked_line_color != marked_line_color) {
for (int i = 0; i < text_editor->get_line_count(); i++) {
if (text_editor->get_line_background_color(i) == marked_line_color) {
@@ -106,17 +106,17 @@ void ShaderTextEditor::_load_theme_settings() {
marked_line_color = updated_marked_line_color;
}
- syntax_highlighter->set_number_color(EDITOR_GET("text_editor/highlighting/number_color"));
- syntax_highlighter->set_symbol_color(EDITOR_GET("text_editor/highlighting/symbol_color"));
- syntax_highlighter->set_function_color(EDITOR_GET("text_editor/highlighting/function_color"));
- syntax_highlighter->set_member_variable_color(EDITOR_GET("text_editor/highlighting/member_variable_color"));
+ syntax_highlighter->set_number_color(EDITOR_GET("text_editor/theme/highlighting/number_color"));
+ syntax_highlighter->set_symbol_color(EDITOR_GET("text_editor/theme/highlighting/symbol_color"));
+ syntax_highlighter->set_function_color(EDITOR_GET("text_editor/theme/highlighting/function_color"));
+ syntax_highlighter->set_member_variable_color(EDITOR_GET("text_editor/theme/highlighting/member_variable_color"));
syntax_highlighter->clear_keyword_colors();
List<String> keywords;
ShaderLanguage::get_keyword_list(&keywords);
- const Color keyword_color = EDITOR_GET("text_editor/highlighting/keyword_color");
- const Color control_flow_keyword_color = EDITOR_GET("text_editor/highlighting/control_flow_keyword_color");
+ const Color keyword_color = EDITOR_GET("text_editor/theme/highlighting/keyword_color");
+ const Color control_flow_keyword_color = EDITOR_GET("text_editor/theme/highlighting/control_flow_keyword_color");
for (const String &E : keywords) {
if (ShaderLanguage::is_control_flow_keyword(E)) {
@@ -142,14 +142,14 @@ void ShaderTextEditor::_load_theme_settings() {
}
}
- const Color member_variable_color = EDITOR_GET("text_editor/highlighting/member_variable_color");
+ const Color member_variable_color = EDITOR_GET("text_editor/theme/highlighting/member_variable_color");
for (const String &E : built_ins) {
syntax_highlighter->add_keyword_color(E, member_variable_color);
}
// Colorize comments.
- const Color comment_color = EDITOR_GET("text_editor/highlighting/comment_color");
+ const Color comment_color = EDITOR_GET("text_editor/theme/highlighting/comment_color");
syntax_highlighter->clear_color_regions();
syntax_highlighter->add_color_region("/*", "*/", comment_color, false);
syntax_highlighter->add_color_region("//", "", comment_color, true);
@@ -397,7 +397,7 @@ void ShaderEditor::_notification(int p_what) {
void ShaderEditor::_editor_settings_changed() {
shader_editor->update_editor_settings();
- shader_editor->get_text_editor()->add_theme_constant_override("line_spacing", EditorSettings::get_singleton()->get("text_editor/theme/line_spacing"));
+ shader_editor->get_text_editor()->add_theme_constant_override("line_spacing", EditorSettings::get_singleton()->get("text_editor/appearance/whitespace/line_spacing"));
shader_editor->get_text_editor()->set_draw_breakpoints_gutter(false);
shader_editor->get_text_editor()->set_draw_executing_lines_gutter(false);
}
@@ -483,7 +483,7 @@ void ShaderEditor::_check_for_external_edit() {
return;
}
- bool use_autoreload = bool(EDITOR_DEF("text_editor/files/auto_reload_scripts_on_external_change", false));
+ bool use_autoreload = bool(EDITOR_DEF("text_editor/behavior/files/auto_reload_scripts_on_external_change", false));
if (shader->get_last_modified_time() != FileAccess::get_modified_time(shader->get_path())) {
if (use_autoreload) {
_reload_shader_from_disk();
@@ -555,7 +555,7 @@ void ShaderEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) {
Point2i pos = tx->get_line_column_at_pos(mb->get_global_position() - tx->get_global_position());
int row = pos.y;
int col = pos.x;
- tx->set_move_caret_on_right_click_enabled(EditorSettings::get_singleton()->get("text_editor/cursor/right_click_moves_caret"));
+ tx->set_move_caret_on_right_click_enabled(EditorSettings::get_singleton()->get("text_editor/behavior/navigation/move_caret_on_right_click"));
if (tx->is_move_caret_on_right_click_enabled()) {
if (tx->has_selection()) {
diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp
index 0bb0bfde6f..309821b3dc 100644
--- a/editor/plugins/skeleton_3d_editor_plugin.cpp
+++ b/editor/plugins/skeleton_3d_editor_plugin.cpp
@@ -551,22 +551,30 @@ void Skeleton3DEditor::update_joint_tree() {
items.insert(-1, root);
- const Vector<int> &joint_porder = skeleton->get_bone_process_orders();
Ref<Texture> bone_icon = get_theme_icon(SNAME("BoneAttachment3D"), SNAME("EditorIcons"));
- for (int i = 0; i < joint_porder.size(); ++i) {
- const int b_idx = joint_porder[i];
+ Vector<int> bones_to_process = skeleton->get_parentless_bones();
+ while (bones_to_process.size() > 0) {
+ int current_bone_idx = bones_to_process[0];
+ bones_to_process.erase(current_bone_idx);
- const int p_idx = skeleton->get_bone_parent(b_idx);
- TreeItem *p_item = items.find(p_idx)->get();
+ const int parent_idx = skeleton->get_bone_parent(current_bone_idx);
+ TreeItem *parent_item = items.find(parent_idx)->get();
- TreeItem *joint_item = joint_tree->create_item(p_item);
- items.insert(b_idx, joint_item);
+ TreeItem *joint_item = joint_tree->create_item(parent_item);
+ items.insert(current_bone_idx, joint_item);
- joint_item->set_text(0, skeleton->get_bone_name(b_idx));
+ joint_item->set_text(0, skeleton->get_bone_name(current_bone_idx));
joint_item->set_icon(0, bone_icon);
joint_item->set_selectable(0, true);
- joint_item->set_metadata(0, "bones/" + itos(b_idx));
+ joint_item->set_metadata(0, "bones/" + itos(current_bone_idx));
+
+ // Add the bone's children to the list of bones to be processed
+ Vector<int> current_bone_child_bones = skeleton->get_bone_children(current_bone_idx);
+ int child_bone_size = current_bone_child_bones.size();
+ for (int i = 0; i < child_bone_size; i++) {
+ bones_to_process.push_back(current_bone_child_bones[i]);
+ }
}
}
diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp
index 42f7d23da2..2883dbbc81 100644
--- a/editor/plugins/sprite_frames_editor_plugin.cpp
+++ b/editor/plugins/sprite_frames_editor_plugin.cpp
@@ -200,34 +200,36 @@ void SpriteFramesEditor::_sheet_scroll_input(const Ref<InputEvent> &p_event) {
void SpriteFramesEditor::_sheet_add_frames() {
Size2i size = split_sheet_preview->get_texture()->get_size();
- int h = split_sheet_h->get_value();
- int v = split_sheet_v->get_value();
+ int frame_count_x = split_sheet_h->get_value();
+ int frame_count_y = split_sheet_v->get_value();
+ Size2 frame_size(size.width / frame_count_x, size.height / frame_count_y);
undo_redo->create_action(TTR("Add Frame"));
int fc = frames->get_frame_count(edited_anim);
- AtlasTexture *atlas_source = Object::cast_to<AtlasTexture>(*split_sheet_preview->get_texture());
-
- Rect2 region_rect = Rect2();
+ Point2 src_origin;
+ Rect2 src_region(Point2(), size);
- if (atlas_source && atlas_source->get_atlas().is_valid()) {
- region_rect = atlas_source->get_region();
+ AtlasTexture *src_atlas = Object::cast_to<AtlasTexture>(*split_sheet_preview->get_texture());
+ if (src_atlas && src_atlas->get_atlas().is_valid()) {
+ src_origin = src_atlas->get_region().position - src_atlas->get_margin().position;
+ src_region = src_atlas->get_region();
}
for (Set<int>::Element *E = frames_selected.front(); E; E = E->next()) {
int idx = E->get();
- int width = size.width / h;
- int height = size.height / v;
- int xp = idx % h;
- int yp = (idx - xp) / h;
- int x = (xp * width) + region_rect.position.x;
- int y = (yp * height) + region_rect.position.y;
+ Point2 frame_coords(idx % frame_count_x, idx / frame_count_x);
+
+ Rect2 frame(frame_coords * frame_size + src_origin, frame_size);
+ Rect2 region = frame.intersection(src_region);
+ Rect2 margin(region == Rect2() ? Point2() : region.position - frame.position, frame.size - region.size);
Ref<AtlasTexture> at;
at.instantiate();
at->set_atlas(split_sheet_preview->get_texture());
- at->set_region(Rect2(x, y, width, height));
+ at->set_region(region);
+ at->set_margin(margin);
undo_redo->add_do_method(frames, "add_frame", edited_anim, at, -1);
undo_redo->add_undo_method(frames, "remove_frame", edited_anim, fc);
diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp
index cfccf90499..481eb84081 100644
--- a/editor/plugins/text_editor.cpp
+++ b/editor/plugins/text_editor.cpp
@@ -433,7 +433,7 @@ void TextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) {
int row = pos.y;
int col = pos.x;
- tx->set_move_caret_on_right_click_enabled(EditorSettings::get_singleton()->get("text_editor/cursor/right_click_moves_caret"));
+ tx->set_move_caret_on_right_click_enabled(EditorSettings::get_singleton()->get("text_editor/behavior/navigation/move_caret_on_right_click"));
bool can_fold = tx->can_fold_line(row);
bool is_folded = tx->is_line_folded(row);
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index 1fbf5eb0e6..2dd8270ee3 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -859,15 +859,15 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) {
node->add_child(expression_box);
register_expression_edit(p_id, expression_box);
- Color background_color = EDITOR_GET("text_editor/highlighting/background_color");
- Color text_color = EDITOR_GET("text_editor/highlighting/text_color");
- Color keyword_color = EDITOR_GET("text_editor/highlighting/keyword_color");
- Color control_flow_keyword_color = EDITOR_GET("text_editor/highlighting/control_flow_keyword_color");
- Color comment_color = EDITOR_GET("text_editor/highlighting/comment_color");
- Color symbol_color = EDITOR_GET("text_editor/highlighting/symbol_color");
- Color function_color = EDITOR_GET("text_editor/highlighting/function_color");
- Color number_color = EDITOR_GET("text_editor/highlighting/number_color");
- Color members_color = EDITOR_GET("text_editor/highlighting/member_variable_color");
+ Color background_color = EDITOR_GET("text_editor/theme/highlighting/background_color");
+ Color text_color = EDITOR_GET("text_editor/theme/highlighting/text_color");
+ Color keyword_color = EDITOR_GET("text_editor/theme/highlighting/keyword_color");
+ Color control_flow_keyword_color = EDITOR_GET("text_editor/theme/highlighting/control_flow_keyword_color");
+ Color comment_color = EDITOR_GET("text_editor/theme/highlighting/comment_color");
+ Color symbol_color = EDITOR_GET("text_editor/theme/highlighting/symbol_color");
+ Color function_color = EDITOR_GET("text_editor/theme/highlighting/function_color");
+ Color number_color = EDITOR_GET("text_editor/theme/highlighting/number_color");
+ Color members_color = EDITOR_GET("text_editor/theme/highlighting/member_variable_color");
expression_box->set_syntax_highlighter(expression_syntax_highlighter);
expression_box->add_theme_color_override("background_color", background_color);
@@ -3112,15 +3112,15 @@ void VisualShaderEditor::_notification(int p_what) {
preview_shader->set_icon(Control::get_theme_icon(SNAME("Shader"), SNAME("EditorIcons")));
{
- Color background_color = EDITOR_GET("text_editor/highlighting/background_color");
- Color text_color = EDITOR_GET("text_editor/highlighting/text_color");
+ Color background_color = EDITOR_GET("text_editor/theme/highlighting/background_color");
+ Color text_color = EDITOR_GET("text_editor/theme/highlighting/text_color");
Color keyword_color = EDITOR_GET("text_editor/highlighting/keyword_color");
- Color control_flow_keyword_color = EDITOR_GET("text_editor/highlighting/control_flow_keyword_color");
- Color comment_color = EDITOR_GET("text_editor/highlighting/comment_color");
- Color symbol_color = EDITOR_GET("text_editor/highlighting/symbol_color");
- Color function_color = EDITOR_GET("text_editor/highlighting/function_color");
- Color number_color = EDITOR_GET("text_editor/highlighting/number_color");
- Color members_color = EDITOR_GET("text_editor/highlighting/member_variable_color");
+ Color control_flow_keyword_color = EDITOR_GET("text_editor/theme/highlighting/control_flow_keyword_color");
+ Color comment_color = EDITOR_GET("text_editor/theme/highlighting/comment_color");
+ Color symbol_color = EDITOR_GET("text_editor/theme/highlighting/symbol_color");
+ Color function_color = EDITOR_GET("text_editor/theme/highlighting/function_color");
+ Color number_color = EDITOR_GET("text_editor/theme/highlighting/number_color");
+ Color members_color = EDITOR_GET("text_editor/theme/highlighting/member_variable_color");
preview_text->add_theme_color_override("background_color", background_color);
@@ -3786,7 +3786,7 @@ void VisualShaderEditor::_update_preview() {
preview_text->set_line_background_color(i, Color(0, 0, 0, 0));
}
if (err != OK) {
- Color error_line_color = EDITOR_GET("text_editor/highlighting/mark_color");
+ Color error_line_color = EDITOR_GET("text_editor/theme/highlighting/mark_color");
preview_text->set_line_background_color(sl.get_error_line() - 1, error_line_color);
error_panel->show();
diff --git a/editor/settings_config_dialog.cpp b/editor/settings_config_dialog.cpp
index 5c77c9f124..ee100ca938 100644
--- a/editor/settings_config_dialog.cpp
+++ b/editor/settings_config_dialog.cpp
@@ -59,7 +59,7 @@ void EditorSettingsDialog::_settings_property_edited(const String &p_name) {
if (full_name == "interface/theme/accent_color" || full_name == "interface/theme/base_color" || full_name == "interface/theme/contrast") {
EditorSettings::get_singleton()->set_manually("interface/theme/preset", "Custom"); // set preset to Custom
- } else if (full_name.begins_with("text_editor/highlighting")) {
+ } else if (full_name.begins_with("text_editor/theme/highlighting")) {
EditorSettings::get_singleton()->set_manually("text_editor/theme/color_theme", "Custom");
}
}
diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp
index ed8b0a4690..3441233811 100644
--- a/modules/gdscript/editor/gdscript_highlighter.cpp
+++ b/modules/gdscript/editor/gdscript_highlighter.cpp
@@ -449,13 +449,13 @@ void GDScriptSyntaxHighlighter::_update_cache() {
color_region_cache.clear();
font_color = text_edit->get_theme_color(SNAME("font_color"));
- symbol_color = EDITOR_GET("text_editor/highlighting/symbol_color");
- function_color = EDITOR_GET("text_editor/highlighting/function_color");
- number_color = EDITOR_GET("text_editor/highlighting/number_color");
- member_color = EDITOR_GET("text_editor/highlighting/member_variable_color");
+ symbol_color = EDITOR_GET("text_editor/theme/highlighting/symbol_color");
+ function_color = EDITOR_GET("text_editor/theme/highlighting/function_color");
+ number_color = EDITOR_GET("text_editor/theme/highlighting/number_color");
+ member_color = EDITOR_GET("text_editor/theme/highlighting/member_variable_color");
/* Engine types. */
- const Color types_color = EDITOR_GET("text_editor/highlighting/engine_type_color");
+ const Color types_color = EDITOR_GET("text_editor/theme/highlighting/engine_type_color");
List<StringName> types;
ClassDB::get_class_list(&types);
for (const StringName &E : types) {
@@ -467,7 +467,7 @@ void GDScriptSyntaxHighlighter::_update_cache() {
}
/* User types. */
- const Color usertype_color = EDITOR_GET("text_editor/highlighting/user_type_color");
+ const Color usertype_color = EDITOR_GET("text_editor/theme/highlighting/user_type_color");
List<StringName> global_classes;
ScriptServer::get_global_class_list(&global_classes);
for (const StringName &E : global_classes) {
@@ -486,7 +486,7 @@ void GDScriptSyntaxHighlighter::_update_cache() {
const GDScriptLanguage *gdscript = GDScriptLanguage::get_singleton();
/* Core types. */
- const Color basetype_color = EDITOR_GET("text_editor/highlighting/base_type_color");
+ const Color basetype_color = EDITOR_GET("text_editor/theme/highlighting/base_type_color");
List<String> core_types;
gdscript->get_core_type_words(&core_types);
for (const String &E : core_types) {
@@ -494,8 +494,8 @@ void GDScriptSyntaxHighlighter::_update_cache() {
}
/* Reserved words. */
- const Color keyword_color = EDITOR_GET("text_editor/highlighting/keyword_color");
- const Color control_flow_keyword_color = EDITOR_GET("text_editor/highlighting/control_flow_keyword_color");
+ const Color keyword_color = EDITOR_GET("text_editor/theme/highlighting/keyword_color");
+ const Color control_flow_keyword_color = EDITOR_GET("text_editor/theme/highlighting/control_flow_keyword_color");
List<String> keyword_list;
gdscript->get_reserved_words(&keyword_list);
for (const String &E : keyword_list) {
@@ -507,7 +507,7 @@ void GDScriptSyntaxHighlighter::_update_cache() {
}
/* Comments */
- const Color comment_color = EDITOR_GET("text_editor/highlighting/comment_color");
+ const Color comment_color = EDITOR_GET("text_editor/theme/highlighting/comment_color");
List<String> comments;
gdscript->get_comment_delimiters(&comments);
for (const String &comment : comments) {
@@ -517,7 +517,7 @@ void GDScriptSyntaxHighlighter::_update_cache() {
}
/* Strings */
- const Color string_color = EDITOR_GET("text_editor/highlighting/string_color");
+ const Color string_color = EDITOR_GET("text_editor/theme/highlighting/string_color");
List<String> strings;
gdscript->get_string_delimiters(&strings);
for (const String &string : strings) {
@@ -529,7 +529,7 @@ void GDScriptSyntaxHighlighter::_update_cache() {
const Ref<Script> script = _get_edited_resource();
if (script.is_valid()) {
/* Member types. */
- const Color member_variable_color = EDITOR_GET("text_editor/highlighting/member_variable_color");
+ const Color member_variable_color = EDITOR_GET("text_editor/theme/highlighting/member_variable_color");
StringName instance_base = script->get_instance_base_type();
if (instance_base != StringName()) {
List<PropertyInfo> plist;
@@ -566,28 +566,28 @@ void GDScriptSyntaxHighlighter::_update_cache() {
annotation_color = Color(0.8, 0.5, 0.25);
}
- EDITOR_DEF("text_editor/highlighting/gdscript/function_definition_color", function_definition_color);
- EDITOR_DEF("text_editor/highlighting/gdscript/node_path_color", node_path_color);
- EDITOR_DEF("text_editor/highlighting/gdscript/annotation_color", annotation_color);
+ EDITOR_DEF("text_editor/theme/highlighting/gdscript/function_definition_color", function_definition_color);
+ EDITOR_DEF("text_editor/theme/highlighting/gdscript/node_path_color", node_path_color);
+ EDITOR_DEF("text_editor/theme/highlighting/gdscript/annotation_color", annotation_color);
if (text_edit_color_theme == "Default" || godot_2_theme) {
EditorSettings::get_singleton()->set_initial_value(
- "text_editor/highlighting/gdscript/function_definition_color",
+ "text_editor/theme/highlighting/gdscript/function_definition_color",
function_definition_color,
true);
EditorSettings::get_singleton()->set_initial_value(
- "text_editor/highlighting/gdscript/node_path_color",
+ "text_editor/theme/highlighting/gdscript/node_path_color",
node_path_color,
true);
EditorSettings::get_singleton()->set_initial_value(
- "text_editor/highlighting/gdscript/annotation_color",
+ "text_editor/theme/highlighting/gdscript/annotation_color",
annotation_color,
true);
}
- function_definition_color = EDITOR_GET("text_editor/highlighting/gdscript/function_definition_color");
- node_path_color = EDITOR_GET("text_editor/highlighting/gdscript/node_path_color");
- annotation_color = EDITOR_GET("text_editor/highlighting/gdscript/annotation_color");
- type_color = EDITOR_GET("text_editor/highlighting/base_type_color");
+ function_definition_color = EDITOR_GET("text_editor/theme/highlighting/gdscript/function_definition_color");
+ node_path_color = EDITOR_GET("text_editor/theme/highlighting/gdscript/node_path_color");
+ annotation_color = EDITOR_GET("text_editor/theme/highlighting/gdscript/annotation_color");
+ type_color = EDITOR_GET("text_editor/theme/highlighting/base_type_color");
}
void GDScriptSyntaxHighlighter::add_color_region(const String &p_start_key, const String &p_end_key, const Color &p_color, bool p_line_only) {
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index 1c6f23f454..73661855b4 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -2703,10 +2703,10 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path
String GDScriptLanguage::_get_indentation() const {
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
- bool use_space_indentation = EDITOR_DEF("text_editor/indent/type", false);
+ bool use_space_indentation = EDITOR_DEF("text_editor/behavior/indent/type", false);
if (use_space_indentation) {
- int indent_size = EDITOR_DEF("text_editor/indent/size", 4);
+ int indent_size = EDITOR_DEF("text_editor/behavior/indent/size", 4);
String space_indent = "";
for (int i = 0; i < indent_size; i++) {
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index a21167ad95..8c3bb1ea57 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -337,7 +337,7 @@ Error GDScriptParser::parse(const String &p_source_code, const String &p_script_
int tab_size = 4;
#ifdef TOOLS_ENABLED
if (EditorSettings::get_singleton()) {
- tab_size = EditorSettings::get_singleton()->get_setting("text_editor/indent/size");
+ tab_size = EditorSettings::get_singleton()->get_setting("text_editor/behavior/indent/size");
}
#endif // TOOLS_ENABLED
diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp
index 3f14156dfa..d4a098811a 100644
--- a/modules/gdscript/gdscript_tokenizer.cpp
+++ b/modules/gdscript/gdscript_tokenizer.cpp
@@ -1440,7 +1440,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::scan() {
GDScriptTokenizer::GDScriptTokenizer() {
#ifdef TOOLS_ENABLED
if (EditorSettings::get_singleton()) {
- tab_size = EditorSettings::get_singleton()->get_setting("text_editor/indent/size");
+ tab_size = EditorSettings::get_singleton()->get_setting("text_editor/behavior/indent/size");
}
#endif // TOOLS_ENABLED
}
diff --git a/modules/gdscript/tests/test_gdscript.cpp b/modules/gdscript/tests/test_gdscript.cpp
index 52e9d92223..e54f055f2b 100644
--- a/modules/gdscript/tests/test_gdscript.cpp
+++ b/modules/gdscript/tests/test_gdscript.cpp
@@ -56,7 +56,7 @@ static void test_tokenizer(const String &p_code, const Vector<String> &p_lines)
int tab_size = 4;
#ifdef TOOLS_ENABLED
if (EditorSettings::get_singleton()) {
- tab_size = EditorSettings::get_singleton()->get_setting("text_editor/indent/size");
+ tab_size = EditorSettings::get_singleton()->get_setting("text_editor/behavior/indent/size");
}
#endif // TOOLS_ENABLED
String tab = String(" ").repeat(tab_size);
diff --git a/modules/mobile_vr/mobile_vr_interface.h b/modules/mobile_vr/mobile_vr_interface.h
index 29ce0f92c8..0c05dc1ebb 100644
--- a/modules/mobile_vr/mobile_vr_interface.h
+++ b/modules/mobile_vr/mobile_vr_interface.h
@@ -86,20 +86,20 @@ private:
Vector3 mag_next_max;
///@TODO a few support functions for trackers, most are math related and should likely be moved elsewhere
- float floor_decimals(float p_value, float p_decimals) {
+ float floor_decimals(const float p_value, const float p_decimals) {
float power_of_10 = pow(10.0f, p_decimals);
return floor(p_value * power_of_10) / power_of_10;
};
- Vector3 floor_decimals(const Vector3 &p_vector, float p_decimals) {
+ Vector3 floor_decimals(const Vector3 &p_vector, const float p_decimals) {
return Vector3(floor_decimals(p_vector.x, p_decimals), floor_decimals(p_vector.y, p_decimals), floor_decimals(p_vector.z, p_decimals));
};
- Vector3 low_pass(const Vector3 &p_vector, const Vector3 &p_last_vector, float p_factor) {
+ Vector3 low_pass(const Vector3 &p_vector, const Vector3 &p_last_vector, const float p_factor) {
return p_vector + (p_factor * (p_last_vector - p_vector));
};
- Vector3 scrub(const Vector3 &p_vector, const Vector3 &p_last_vector, float p_decimals, float p_factor) {
+ Vector3 scrub(const Vector3 &p_vector, const Vector3 &p_last_vector, const float p_decimals, const float p_factor) {
return low_pass(floor_decimals(p_vector, p_decimals), p_last_vector, p_factor);
};
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 520262c0eb..1f7f1390ea 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -82,6 +82,12 @@ static bool _create_project_solution_if_needed() {
CSharpLanguage *CSharpLanguage::singleton = nullptr;
+GDNativeInstanceBindingCallbacks CSharpLanguage::_instance_binding_callbacks = {
+ &_instance_binding_create_callback,
+ &_instance_binding_free_callback,
+ &_instance_binding_reference_callback
+};
+
String CSharpLanguage::get_name() const {
return "C#";
}
@@ -542,10 +548,10 @@ String CSharpLanguage::make_function(const String &, const String &, const Packe
String CSharpLanguage::_get_indentation() const {
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
- bool use_space_indentation = EDITOR_DEF("text_editor/indent/type", 0);
+ bool use_space_indentation = EDITOR_DEF("text_editor/behavior/indent/type", 0);
if (use_space_indentation) {
- int indent_size = EDITOR_DEF("text_editor/indent/size", 4);
+ int indent_size = EDITOR_DEF("text_editor/behavior/indent/size", 4);
String space_indent = "";
for (int i = 0; i < indent_size; i++) {
@@ -1444,46 +1450,46 @@ bool CSharpLanguage::setup_csharp_script_binding(CSharpScriptBinding &r_script_b
return true;
}
-void *CSharpLanguage::alloc_instance_binding_data(Object *p_object) {
- MutexLock lock(language_bind_mutex);
+Map<Object *, CSharpScriptBinding>::Element *CSharpLanguage::insert_script_binding(Object *p_object, const CSharpScriptBinding &p_script_binding) {
+ return script_bindings.insert(p_object, p_script_binding);
+}
+
+void *CSharpLanguage::_instance_binding_create_callback(void *, void *p_instance) {
+ CSharpLanguage *csharp_lang = CSharpLanguage::get_singleton();
- Map<Object *, CSharpScriptBinding>::Element *match = script_bindings.find(p_object);
+ MutexLock lock(csharp_lang->language_bind_mutex);
+
+ Map<Object *, CSharpScriptBinding>::Element *match = csharp_lang->script_bindings.find((Object *)p_instance);
if (match) {
return (void *)match;
}
CSharpScriptBinding script_binding;
- if (!setup_csharp_script_binding(script_binding, p_object)) {
- return nullptr;
- }
-
- return (void *)insert_script_binding(p_object, script_binding);
+ return (void *)csharp_lang->insert_script_binding((Object *)p_instance, script_binding);
}
-Map<Object *, CSharpScriptBinding>::Element *CSharpLanguage::insert_script_binding(Object *p_object, const CSharpScriptBinding &p_script_binding) {
- return script_bindings.insert(p_object, p_script_binding);
-}
+void CSharpLanguage::_instance_binding_free_callback(void *, void *, void *p_binding) {
+ CSharpLanguage *csharp_lang = CSharpLanguage::get_singleton();
-void CSharpLanguage::free_instance_binding_data(void *p_data) {
if (GDMono::get_singleton() == nullptr) {
#ifdef DEBUG_ENABLED
- CRASH_COND(!script_bindings.is_empty());
+ CRASH_COND(!csharp_lang->script_bindings.is_empty());
#endif
// Mono runtime finalized, all the gchandle bindings were already released
return;
}
- if (finalizing) {
+ if (csharp_lang->finalizing) {
return; // inside CSharpLanguage::finish(), all the gchandle bindings are released there
}
GD_MONO_ASSERT_THREAD_ATTACHED;
{
- MutexLock lock(language_bind_mutex);
+ MutexLock lock(csharp_lang->language_bind_mutex);
- Map<Object *, CSharpScriptBinding>::Element *data = (Map<Object *, CSharpScriptBinding>::Element *)p_data;
+ Map<Object *, CSharpScriptBinding>::Element *data = (Map<Object *, CSharpScriptBinding>::Element *)p_binding;
CSharpScriptBinding &script_binding = data->value();
@@ -1497,92 +1503,110 @@ void CSharpLanguage::free_instance_binding_data(void *p_data) {
script_binding.gchandle.release();
}
- script_bindings.erase(data);
+ csharp_lang->script_bindings.erase(data);
}
}
-void CSharpLanguage::refcount_incremented_instance_binding(Object *p_object) {
-#if 0
- RefCounted *rc_owner = Object::cast_to<RefCounted>(p_object);
+GDNativeBool CSharpLanguage::_instance_binding_reference_callback(void *p_token, void *p_binding, GDNativeBool p_reference) {
+ CRASH_COND(!p_binding);
+
+ CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)p_binding)->get();
+
+ RefCounted *rc_owner = Object::cast_to<RefCounted>(script_binding.owner);
#ifdef DEBUG_ENABLED
CRASH_COND(!rc_owner);
- CRASH_COND(!p_object->has_script_instance_binding(get_language_index()));
#endif
- void *data = p_object->get_script_instance_binding(get_language_index());
- CRASH_COND(!data);
-
- CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
MonoGCHandleData &gchandle = script_binding.gchandle;
+ int refcount = rc_owner->reference_get_count();
+
if (!script_binding.inited) {
- return;
+ return refcount == 0;
}
- if (rc_owner->reference_get_count() > 1 && gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
- GD_MONO_SCOPE_THREAD_ATTACH;
+ if (p_reference) {
+ // Refcount incremented
+ if (refcount > 1 && gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
+ GD_MONO_SCOPE_THREAD_ATTACH;
- // The reference count was increased after the managed side was the only one referencing our owner.
- // This means the owner is being referenced again by the unmanaged side,
- // so the owner must hold the managed side alive again to avoid it from being GCed.
+ // The reference count was increased after the managed side was the only one referencing our owner.
+ // This means the owner is being referenced again by the unmanaged side,
+ // so the owner must hold the managed side alive again to avoid it from being GCed.
+
+ MonoObject *target = gchandle.get_target();
+ if (!target) {
+ return false; // Called after the managed side was collected, so nothing to do here
+ }
- MonoObject *target = gchandle.get_target();
- if (!target) {
- return; // Called after the managed side was collected, so nothing to do here
+ // Release the current weak handle and replace it with a strong handle.
+ MonoGCHandleData strong_gchandle = MonoGCHandleData::new_strong_handle(target);
+ gchandle.release();
+ gchandle = strong_gchandle;
}
- // Release the current weak handle and replace it with a strong handle.
- MonoGCHandleData strong_gchandle = MonoGCHandleData::new_strong_handle(target);
- gchandle.release();
- gchandle = strong_gchandle;
- }
-#endif
-}
+ return false;
+ } else {
+ // Refcount decremented
+ if (refcount == 1 && !gchandle.is_released() && !gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
+ GD_MONO_SCOPE_THREAD_ATTACH;
-bool CSharpLanguage::refcount_decremented_instance_binding(Object *p_object) {
-#if 0
- RefCounted *rc_owner = Object::cast_to<RefCounted>(p_object);
+ // If owner owner is no longer referenced by the unmanaged side,
+ // the managed instance takes responsibility of deleting the owner when GCed.
-#ifdef DEBUG_ENABLED
- CRASH_COND(!rc_owner);
- CRASH_COND(!p_object->has_script_instance_binding(get_language_index()));
-#endif
+ MonoObject *target = gchandle.get_target();
+ if (!target) {
+ return refcount == 0; // Called after the managed side was collected, so nothing to do here
+ }
- void *data = p_object->get_script_instance_binding(get_language_index());
- CRASH_COND(!data);
+ // Release the current strong handle and replace it with a weak handle.
+ MonoGCHandleData weak_gchandle = MonoGCHandleData::new_weak_handle(target);
+ gchandle.release();
+ gchandle = weak_gchandle;
- CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
- MonoGCHandleData &gchandle = script_binding.gchandle;
-
- int refcount = rc_owner->reference_get_count();
+ return false;
+ }
- if (!script_binding.inited) {
return refcount == 0;
}
+}
- if (refcount == 1 && !gchandle.is_released() && !gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
- GD_MONO_SCOPE_THREAD_ATTACH;
+void *CSharpLanguage::get_instance_binding(Object *p_object) {
+ void *binding = p_object->get_instance_binding(get_singleton(), &_instance_binding_callbacks);
- // If owner owner is no longer referenced by the unmanaged side,
- // the managed instance takes responsibility of deleting the owner when GCed.
+ // Initially this was in `_instance_binding_create_callback`. However, after the new instance
+ // binding re-write it was resulting in a deadlock in `_instance_binding_reference`, as
+ // `setup_csharp_script_binding` may call `reference()`. It was moved here outside to fix that.
- MonoObject *target = gchandle.get_target();
- if (!target) {
- return refcount == 0; // Called after the managed side was collected, so nothing to do here
- }
+ if (binding) {
+ CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)binding)->value();
- // Release the current strong handle and replace it with a weak handle.
- MonoGCHandleData weak_gchandle = MonoGCHandleData::new_weak_handle(target);
- gchandle.release();
- gchandle = weak_gchandle;
+ if (!script_binding.inited) {
+ MutexLock lock(CSharpLanguage::get_singleton()->get_language_bind_mutex());
- return false;
+ if (!script_binding.inited) { // Another thread may have set it up
+ CSharpLanguage::get_singleton()->setup_csharp_script_binding(script_binding, p_object);
+ }
+ }
}
- return refcount == 0;
+ return binding;
+}
+
+void *CSharpLanguage::get_existing_instance_binding(Object *p_object) {
+#ifdef DEBUG_ENABLED
+ CRASH_COND(p_object->has_instance_binding(p_object));
#endif
- return false;
+ return p_object->get_instance_binding(get_singleton(), &_instance_binding_callbacks);
+}
+
+void CSharpLanguage::set_instance_binding(Object *p_object, void *p_binding) {
+ p_object->set_instance_binding(get_singleton(), p_binding, &_instance_binding_callbacks);
+}
+
+bool CSharpLanguage::has_instance_binding(Object *p_object) {
+ return p_object->has_instance_binding(get_singleton());
}
CSharpInstance *CSharpInstance::create_for_managed_type(Object *p_owner, CSharpScript *p_script, const MonoGCHandleData &p_gchandle) {
@@ -2260,29 +2284,16 @@ CSharpInstance::~CSharpInstance() {
// Otherwise, the unsafe reference debug checks will incorrectly detect a bug.
bool die = _unreference_owner_unsafe();
CRASH_COND(die); // `owner_keep_alive` holds a reference, so it can't die
-#if 0
- void *data = owner->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index());
-
+ void *data = CSharpLanguage::get_instance_binding(owner);
CRASH_COND(data == nullptr);
-
CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
-
- if (!script_binding.inited) {
- MutexLock lock(CSharpLanguage::get_singleton()->get_language_bind_mutex());
-
- if (!script_binding.inited) { // Other thread may have set it up
- // Already had a binding that needs to be setup
- CSharpLanguage::get_singleton()->setup_csharp_script_binding(script_binding, owner);
- CRASH_COND(!script_binding.inited);
- }
- }
+ CRASH_COND(!script_binding.inited);
#ifdef DEBUG_ENABLED
// The "instance binding" holds a reference so the refcount should be at least 2 before `scope_keep_owner_alive` goes out of scope
CRASH_COND(rc_owner->reference_get_count() <= 1);
#endif
-#endif
}
if (script.is_valid() && owner) {
@@ -3100,10 +3111,10 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg
// Hold it alive. Important if we have to dispose a script instance binding before creating the CSharpInstance.
ref = Ref<RefCounted>(static_cast<RefCounted *>(p_owner));
}
-#if 0
+
// If the object had a script instance binding, dispose it before adding the CSharpInstance
- if (p_owner->has_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index())) {
- void *data = p_owner->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index());
+ if (CSharpLanguage::has_instance_binding(p_owner)) {
+ void *data = CSharpLanguage::get_existing_instance_binding(p_owner);
CRASH_COND(data == nullptr);
CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
@@ -3122,7 +3133,7 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg
script_binding.inited = false;
}
}
-#endif
+
CSharpInstance *instance = memnew(CSharpInstance(Ref<CSharpScript>(this)));
instance->base_ref_counted = p_is_ref_counted;
instance->owner = p_owner;
@@ -3192,7 +3203,7 @@ Variant CSharpScript::_new(const Variant **p_args, int p_argcount, Callable::Cal
CSharpInstance *instance = _create_instance(p_args, p_argcount, owner, r != nullptr, r_error);
if (!instance) {
if (ref.is_null()) {
- memdelete(owner); //no owner, sorry
+ memdelete(owner); // no owner, sorry
}
return Variant();
}
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index da6b60aee2..4552f376d0 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -401,7 +401,18 @@ class CSharpLanguage : public ScriptLanguage {
static void _editor_init_callback();
#endif
+ static void *_instance_binding_create_callback(void *p_token, void *p_instance);
+ static void _instance_binding_free_callback(void *p_token, void *p_instance, void *p_binding);
+ static GDNativeBool _instance_binding_reference_callback(void *p_token, void *p_binding, GDNativeBool p_reference);
+
+ static GDNativeInstanceBindingCallbacks _instance_binding_callbacks;
+
public:
+ static void *get_instance_binding(Object *p_object);
+ static void *get_existing_instance_binding(Object *p_object);
+ static void set_instance_binding(Object *p_object, void *p_binding);
+ static bool has_instance_binding(Object *p_object);
+
StringNameCache string_names;
const Mutex &get_language_bind_mutex() { return language_bind_mutex; }
@@ -507,12 +518,6 @@ public:
void thread_enter() override;
void thread_exit() override;
- // Don't use these. I'm watching you
- void *alloc_instance_binding_data(Object *p_object) override;
- void free_instance_binding_data(void *p_data) override;
- void refcount_incremented_instance_binding(Object *p_object) override;
- bool refcount_decremented_instance_binding(Object *p_object) override;
-
Map<Object *, CSharpScriptBinding>::Element *insert_script_binding(Object *p_object, const CSharpScriptBinding &p_script_binding);
bool setup_csharp_script_binding(CSharpScriptBinding &r_script_binding, Object *p_object);
diff --git a/modules/mono/glue/base_object_glue.cpp b/modules/mono/glue/base_object_glue.cpp
index a99dff8432..6c5503a3bd 100644
--- a/modules/mono/glue/base_object_glue.cpp
+++ b/modules/mono/glue/base_object_glue.cpp
@@ -64,8 +64,8 @@ void godot_icall_Object_Disposed(MonoObject *p_obj, Object *p_ptr) {
return;
}
}
-#if 0
- void *data = p_ptr->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index());
+
+ void *data = CSharpLanguage::get_existing_instance_binding(p_ptr);
if (data) {
CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
@@ -76,7 +76,6 @@ void godot_icall_Object_Disposed(MonoObject *p_obj, Object *p_ptr) {
}
}
}
-#endif
}
void godot_icall_RefCounted_Disposed(MonoObject *p_obj, Object *p_ptr, MonoBoolean p_is_finalizer) {
@@ -85,7 +84,7 @@ void godot_icall_RefCounted_Disposed(MonoObject *p_obj, Object *p_ptr, MonoBoole
// This is only called with RefCounted derived classes
CRASH_COND(!Object::cast_to<RefCounted>(p_ptr));
#endif
-#if 0
+
RefCounted *rc = static_cast<RefCounted *>(p_ptr);
if (rc->get_script_instance()) {
@@ -113,7 +112,7 @@ void godot_icall_RefCounted_Disposed(MonoObject *p_obj, Object *p_ptr, MonoBoole
if (rc->unreference()) {
memdelete(rc);
} else {
- void *data = rc->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index());
+ void *data = CSharpLanguage::get_existing_instance_binding(rc);
if (data) {
CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
@@ -125,7 +124,6 @@ void godot_icall_RefCounted_Disposed(MonoObject *p_obj, Object *p_ptr, MonoBoole
}
}
}
-#endif
}
void godot_icall_Object_ConnectEventSignals(Object *p_ptr) {
diff --git a/modules/mono/mono_gd/gd_mono_internals.cpp b/modules/mono/mono_gd/gd_mono_internals.cpp
index d6545d50ec..60047545c4 100644
--- a/modules/mono/mono_gd/gd_mono_internals.cpp
+++ b/modules/mono/mono_gd/gd_mono_internals.cpp
@@ -45,7 +45,7 @@
namespace GDMonoInternals {
void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) {
// This method should not fail
-#if 0
+
CRASH_COND(!unmanaged);
// All mono objects created from the managed world (e.g.: 'new Player()')
@@ -89,12 +89,12 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) {
}
// The object was just created, no script instance binding should have been attached
- CRASH_COND(unmanaged->has_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index()));
+ CRASH_COND(CSharpLanguage::has_instance_binding(unmanaged));
void *data = (void *)CSharpLanguage::get_singleton()->insert_script_binding(unmanaged, script_binding);
// Should be thread safe because the object was just created and nothing else should be referencing it
- unmanaged->set_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index(), data);
+ CSharpLanguage::set_instance_binding(unmanaged, data);
return;
}
@@ -108,7 +108,6 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) {
CSharpInstance *csharp_instance = CSharpInstance::create_for_managed_type(unmanaged, script.ptr(), gchandle);
unmanaged->set_script_and_instance(script, csharp_instance);
-#endif
}
void unhandled_exception(MonoException *p_exc) {
diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp
index 080398c997..13939bd014 100644
--- a/modules/mono/mono_gd/gd_mono_utils.cpp
+++ b/modules/mono/mono_gd/gd_mono_utils.cpp
@@ -54,7 +54,6 @@
namespace GDMonoUtils {
MonoObject *unmanaged_get_managed(Object *unmanaged) {
-#if 0
if (!unmanaged) {
return nullptr;
}
@@ -69,22 +68,10 @@ MonoObject *unmanaged_get_managed(Object *unmanaged) {
// If the owner does not have a CSharpInstance...
- void *data = unmanaged->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index());
-
+ void *data = CSharpLanguage::get_instance_binding(unmanaged);
ERR_FAIL_NULL_V(data, nullptr);
-
CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->value();
-
- if (!script_binding.inited) {
- MutexLock lock(CSharpLanguage::get_singleton()->get_language_bind_mutex());
-
- if (!script_binding.inited) { // Other thread may have set it up
- // Already had a binding that needs to be setup
- CSharpLanguage::get_singleton()->setup_csharp_script_binding(script_binding, unmanaged);
-
- ERR_FAIL_COND_V(!script_binding.inited, nullptr);
- }
- }
+ ERR_FAIL_COND_V(!script_binding.inited, nullptr);
MonoGCHandleData &gchandle = script_binding.gchandle;
@@ -121,8 +108,6 @@ MonoObject *unmanaged_get_managed(Object *unmanaged) {
}
return mono_object;
-#endif
- return nullptr;
}
void set_main_thread(MonoThread *p_thread) {
diff --git a/platform/android/android_input_handler.cpp b/platform/android/android_input_handler.cpp
index b9004c4989..e03375e8d9 100644
--- a/platform/android/android_input_handler.cpp
+++ b/platform/android/android_input_handler.cpp
@@ -377,7 +377,7 @@ MouseButton AndroidInputHandler::_android_button_mask_to_godot_button_mask(int a
if (android_button_mask & AMOTION_EVENT_BUTTON_BACK) {
godot_button_mask |= MOUSE_BUTTON_MASK_XBUTTON1;
}
- if (android_button_mask & AMOTION_EVENT_BUTTON_SECONDARY) {
+ if (android_button_mask & AMOTION_EVENT_BUTTON_FORWARD) {
godot_button_mask |= MOUSE_BUTTON_MASK_XBUTTON2;
}
diff --git a/platform/osx/joypad_osx.cpp b/platform/osx/joypad_osx.cpp
index d778271350..e67d2b0e91 100644
--- a/platform/osx/joypad_osx.cpp
+++ b/platform/osx/joypad_osx.cpp
@@ -143,6 +143,8 @@ void joypad::add_hid_element(IOHIDElementRef p_element) {
switch (usage) {
case kHIDUsage_Sim_Rudder:
case kHIDUsage_Sim_Throttle:
+ case kHIDUsage_Sim_Accelerator:
+ case kHIDUsage_Sim_Brake:
if (!has_element(cookie, &axis_elements)) {
list = &axis_elements;
}
@@ -332,6 +334,13 @@ bool JoypadOSX::configure_joypad(IOHIDDeviceRef p_device_ref, joypad *p_joy) {
p_joy->add_hid_elements(array);
CFRelease(array);
}
+ // Xbox controller hat values start at 1 rather than 0.
+ p_joy->offset_hat = vendor == 0x45e &&
+ (product_id == 0x0b05 ||
+ product_id == 0x02e0 ||
+ product_id == 0x02fd ||
+ product_id == 0x0b13);
+
return true;
}
@@ -388,13 +397,16 @@ bool joypad::check_ff_features() {
return false;
}
-static int process_hat_value(int p_min, int p_max, int p_value) {
+static int process_hat_value(int p_min, int p_max, int p_value, bool p_offset_hat) {
int range = (p_max - p_min + 1);
int value = p_value - p_min;
int hat_value = HatMask::HAT_MASK_CENTER;
if (range == 4) {
value *= 2;
}
+ if (p_offset_hat) {
+ value -= 1;
+ }
switch (value) {
case 0:
@@ -468,7 +480,7 @@ void JoypadOSX::process_joypads() {
for (int j = 0; j < joy.hat_elements.size(); j++) {
rec_element &elem = joy.hat_elements.write[j];
int value = joy.get_hid_element_state(&elem);
- int hat_value = process_hat_value(elem.min, elem.max, value);
+ int hat_value = process_hat_value(elem.min, elem.max, value, joy.offset_hat);
input->joy_hat(joy.id, (HatMask)hat_value);
}
diff --git a/platform/osx/joypad_osx.h b/platform/osx/joypad_osx.h
index bf7e8949df..c060c3d523 100644
--- a/platform/osx/joypad_osx.h
+++ b/platform/osx/joypad_osx.h
@@ -64,6 +64,7 @@ struct joypad {
Vector<rec_element> hat_elements;
int id = 0;
+ bool offset_hat = false;
io_service_t ffservice = 0; /* Interface for force feedback, 0 = no ff */
FFCONSTANTFORCE ff_constant_force;
diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp
index ac75d81272..13b37aa2b2 100644
--- a/scene/2d/camera_2d.cpp
+++ b/scene/2d/camera_2d.cpp
@@ -96,7 +96,7 @@ Transform2D Camera2D::get_camera_transform() {
Size2 screen_size = _get_camera_screen_size();
- Point2 new_camera_pos = get_global_transform().get_origin();
+ Point2 new_camera_pos = get_global_position();
Point2 ret_camera_pos;
if (!first) {
@@ -169,7 +169,7 @@ Transform2D Camera2D::get_camera_transform() {
Point2 screen_offset = (anchor_mode == ANCHOR_MODE_DRAG_CENTER ? (screen_size * 0.5 * zoom) : Point2());
- real_t angle = get_global_transform().get_rotation();
+ real_t angle = get_global_rotation();
if (rotating) {
screen_offset = screen_offset.rotated(angle);
}
@@ -305,8 +305,8 @@ void Camera2D::_notification(int p_what) {
limit_drawing_width = 3;
}
- Vector2 camera_origin = get_global_transform().get_origin();
- Vector2 camera_scale = get_global_transform().get_scale().abs();
+ Vector2 camera_origin = get_global_position();
+ Vector2 camera_scale = get_global_scale().abs();
Vector2 limit_points[4] = {
(Vector2(limit[SIDE_LEFT], limit[SIDE_TOP]) - camera_origin) / camera_scale,
(Vector2(limit[SIDE_RIGHT], limit[SIDE_TOP]) - camera_origin) / camera_scale,
@@ -489,7 +489,7 @@ void Camera2D::align() {
Size2 screen_size = _get_camera_screen_size();
- Point2 current_camera_pos = get_global_transform().get_origin();
+ Point2 current_camera_pos = get_global_position();
if (anchor_mode == ANCHOR_MODE_DRAG_CENTER) {
if (drag_horizontal_offset < 0) {
camera_pos.x = current_camera_pos.x + screen_size.x * 0.5 * drag_margin[SIDE_RIGHT] * drag_horizontal_offset;
diff --git a/scene/2d/joints_2d.cpp b/scene/2d/joints_2d.cpp
index eabbf313d0..4a6606256e 100644
--- a/scene/2d/joints_2d.cpp
+++ b/scene/2d/joints_2d.cpp
@@ -252,7 +252,7 @@ void PinJoint2D::_notification(int p_what) {
}
void PinJoint2D::_configure_joint(RID p_joint, PhysicsBody2D *body_a, PhysicsBody2D *body_b) {
- PhysicsServer2D::get_singleton()->joint_make_pin(p_joint, get_global_transform().get_origin(), body_a->get_rid(), body_b ? body_b->get_rid() : RID());
+ PhysicsServer2D::get_singleton()->joint_make_pin(p_joint, get_global_position(), body_a->get_rid(), body_b ? body_b->get_rid() : RID());
PhysicsServer2D::get_singleton()->pin_joint_set_param(p_joint, PhysicsServer2D::PIN_JOINT_SOFTNESS, softness);
}
diff --git a/scene/2d/navigation_agent_2d.cpp b/scene/2d/navigation_agent_2d.cpp
index 927f6c1c42..2f00978123 100644
--- a/scene/2d/navigation_agent_2d.cpp
+++ b/scene/2d/navigation_agent_2d.cpp
@@ -102,7 +102,7 @@ void NavigationAgent2D::_notification(int p_what) {
} break;
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
if (agent_parent) {
- NavigationServer2D::get_singleton()->agent_set_position(agent, agent_parent->get_global_transform().get_origin());
+ NavigationServer2D::get_singleton()->agent_set_position(agent, agent_parent->get_global_position());
_check_distance_to_target();
}
} break;
@@ -185,7 +185,7 @@ Vector2 NavigationAgent2D::get_next_location() {
update_navigation();
if (navigation_path.size() == 0) {
ERR_FAIL_COND_V(agent_parent == nullptr, Vector2());
- return agent_parent->get_global_transform().get_origin();
+ return agent_parent->get_global_position();
} else {
return navigation_path[nav_path_index];
}
@@ -193,7 +193,7 @@ Vector2 NavigationAgent2D::get_next_location() {
real_t NavigationAgent2D::distance_to_target() const {
ERR_FAIL_COND_V(agent_parent == nullptr, 0.0);
- return agent_parent->get_global_transform().get_origin().distance_to(target_location);
+ return agent_parent->get_global_position().distance_to(target_location);
}
bool NavigationAgent2D::is_target_reached() const {
@@ -260,7 +260,7 @@ void NavigationAgent2D::update_navigation() {
update_frame_id = Engine::get_singleton()->get_physics_frames();
- Vector2 o = agent_parent->get_global_transform().get_origin();
+ Vector2 o = agent_parent->get_global_position();
bool reload_path = false;
diff --git a/scene/2d/navigation_obstacle_2d.cpp b/scene/2d/navigation_obstacle_2d.cpp
index c99b9777cd..0a105826c0 100644
--- a/scene/2d/navigation_obstacle_2d.cpp
+++ b/scene/2d/navigation_obstacle_2d.cpp
@@ -53,7 +53,7 @@ void NavigationObstacle2D::_notification(int p_what) {
} break;
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
if (parent_node2d) {
- NavigationServer2D::get_singleton()->agent_set_position(agent, parent_node2d->get_global_transform().get_origin());
+ NavigationServer2D::get_singleton()->agent_set_position(agent, parent_node2d->get_global_position());
}
} break;
}
@@ -92,13 +92,13 @@ void NavigationObstacle2D::update_agent_shape() {
// and add the enclosing shape radius
r += cs->get_shape()->get_enclosing_radius();
}
- Size2 s = cs->get_global_transform().get_scale();
+ Size2 s = cs->get_global_scale();
r *= MAX(s.x, s.y);
// Takes the biggest radius
radius = MAX(radius, r);
}
}
- Vector2 s = parent_node2d->get_global_transform().get_scale();
+ Vector2 s = parent_node2d->get_global_scale();
radius *= MAX(s.x, s.y);
if (radius == 0.0) {
diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp
index d52c293763..dd1a4671d9 100644
--- a/scene/2d/physics_body_2d.cpp
+++ b/scene/2d/physics_body_2d.cpp
@@ -102,21 +102,21 @@ bool PhysicsBody2D::move_and_collide(const Vector2 &p_motion, PhysicsServer2D::M
}
// Check depth of recovery.
- real_t projected_length = r_result.motion.dot(motion_normal);
- Vector2 recovery = r_result.motion - motion_normal * projected_length;
+ real_t projected_length = r_result.travel.dot(motion_normal);
+ Vector2 recovery = r_result.travel - motion_normal * projected_length;
real_t recovery_length = recovery.length();
// Fixes cases where canceling slide causes the motion to go too deep into the ground,
// because we're only taking rest information into account and not general recovery.
if (recovery_length < (real_t)p_margin + precision) {
// Apply adjustment to motion.
- r_result.motion = motion_normal * projected_length;
- r_result.remainder = p_motion - r_result.motion;
+ r_result.travel = motion_normal * projected_length;
+ r_result.remainder = p_motion - r_result.travel;
}
}
}
if (!p_test_only) {
- gt.elements[2] += r_result.motion;
+ gt.elements[2] += r_result.travel;
set_global_transform(gt);
}
@@ -1043,14 +1043,14 @@ void RigidBody2D::_reload_physics_characteristics() {
// So, if you pass 45 as limit, avoid numerical precision errors when angle is 45.
#define FLOOR_ANGLE_THRESHOLD 0.01
-void CharacterBody2D::move_and_slide() {
+bool CharacterBody2D::move_and_slide() {
// Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky.
float delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time();
Vector2 current_platform_velocity = platform_velocity;
if ((on_floor || on_wall) && platform_rid.is_valid()) {
- bool excluded = (exclude_body_layers & platform_layer) != 0;
+ bool excluded = (moving_platform_ignore_layers & platform_layer) != 0;
if (!excluded) {
// This approach makes sure there is less delay between the actual body velocity and the one we saved.
PhysicsDirectBodyState2D *bs = PhysicsServer2D::get_singleton()->body_get_direct_state(platform_rid);
@@ -1095,7 +1095,7 @@ void CharacterBody2D::move_and_slide() {
// No sliding on first attempt to keep floor motion stable when possible,
// When stop on slope is enabled or when there is no up direction.
- bool sliding_enabled = !stop_on_slope || up_direction == Vector2();
+ bool sliding_enabled = !floor_stop_on_slope || up_direction == Vector2();
// Constant speed can be applied only the first time sliding is enabled.
bool can_apply_constant_speed = sliding_enabled;
bool first_slide = true;
@@ -1104,23 +1104,21 @@ void CharacterBody2D::move_and_slide() {
for (int iteration = 0; iteration < max_slides; ++iteration) {
PhysicsServer2D::MotionResult result;
- bool found_collision = false;
Vector2 prev_position = get_global_transform().elements[2];
bool collided = move_and_collide(motion, result, margin, false, !sliding_enabled);
if (collided) {
- found_collision = true;
motion_results.push_back(result);
_set_collision_direction(result);
- if (on_floor && stop_on_slope && (linear_velocity.normalized() + up_direction).length() < 0.01) {
+ if (on_floor && floor_stop_on_slope && (linear_velocity.normalized() + up_direction).length() < 0.01) {
Transform2D gt = get_global_transform();
- if (result.motion.length() > margin) {
- gt.elements[2] -= result.motion.slide(up_direction);
+ if (result.travel.length() > margin) {
+ gt.elements[2] -= result.travel.slide(up_direction);
} else {
- gt.elements[2] -= result.motion;
+ gt.elements[2] -= result.travel;
}
set_global_transform(gt);
linear_velocity = Vector2();
@@ -1134,14 +1132,14 @@ void CharacterBody2D::move_and_slide() {
}
// Move on floor only checks.
- if (move_on_floor_only && on_wall && motion_slide_up.dot(result.collision_normal) <= 0) {
- // Avoid to move forward on a wall if move_on_floor_only is true.
- if (was_on_floor && !is_on_floor_only() && !vel_dir_facing_up) {
+ if (floor_block_on_wall && on_wall && motion_slide_up.dot(result.collision_normal) <= 0) {
+ // Avoid to move forward on a wall if floor_block_on_wall is true.
+ if (was_on_floor && !on_floor && !vel_dir_facing_up) {
// If the movement is large the body can be prevented from reaching the walls.
- if (result.motion.length() <= margin) {
+ if (result.travel.length() <= margin) {
// Cancels the motion.
Transform2D gt = get_global_transform();
- gt.elements[2] -= result.motion;
+ gt.elements[2] -= result.travel;
set_global_transform(gt);
}
on_floor = true;
@@ -1155,7 +1153,7 @@ void CharacterBody2D::move_and_slide() {
break;
}
// Prevents the body from being able to climb a slope when it moves forward against the wall.
- else if (!is_on_floor_only()) {
+ else if (!on_floor) {
motion = up_direction * up_direction.dot(result.remainder);
motion = motion.slide(result.collision_normal);
} else {
@@ -1163,12 +1161,10 @@ void CharacterBody2D::move_and_slide() {
}
}
// Constant Speed when the slope is upward.
- else if (constant_speed_on_floor && is_on_floor_only() && can_apply_constant_speed && was_on_floor && motion.dot(result.collision_normal) < 0) {
+ else if (floor_constant_speed && is_on_floor_only() && can_apply_constant_speed && was_on_floor && motion.dot(result.collision_normal) < 0) {
can_apply_constant_speed = false;
Vector2 motion_slide_norm = result.remainder.slide(result.collision_normal).normalized();
- if (!motion_slide_norm.is_equal_approx(Vector2())) {
- motion = motion_slide_norm * (motion_slide_up.length() - result.motion.slide(up_direction).length() - last_travel.slide(up_direction).length());
- }
+ motion = motion_slide_norm * (motion_slide_up.length() - result.travel.slide(up_direction).length() - last_travel.slide(up_direction).length());
}
// Regular sliding, the last part of the test handle the case when you don't want to slide on the ceiling.
else if ((sliding_enabled || !on_floor) && (!on_ceiling || slide_on_ceiling || !vel_dir_facing_up)) {
@@ -1197,11 +1193,11 @@ void CharacterBody2D::move_and_slide() {
}
}
- last_travel = result.motion;
+ last_travel = result.travel;
}
// When you move forward in a downward slope you don’t collide because you will be in the air.
// This test ensures that constant speed is applied, only if the player is still on the ground after the snap is applied.
- else if (constant_speed_on_floor && first_slide && _on_floor_if_snapped(was_on_floor, vel_dir_facing_up)) {
+ else if (floor_constant_speed && first_slide && _on_floor_if_snapped(was_on_floor, vel_dir_facing_up)) {
can_apply_constant_speed = false;
sliding_enabled = true;
Transform2D gt = get_global_transform();
@@ -1209,17 +1205,15 @@ void CharacterBody2D::move_and_slide() {
set_global_transform(gt);
Vector2 motion_slide_norm = motion.slide(prev_floor_normal).normalized();
- if (!motion_slide_norm.is_equal_approx(Vector2())) {
- motion = motion_slide_norm * (motion_slide_up.length());
- found_collision = true;
- }
+ motion = motion_slide_norm * (motion_slide_up.length());
+ collided = true;
}
can_apply_constant_speed = !can_apply_constant_speed && !sliding_enabled;
sliding_enabled = true;
first_slide = false;
- if (!found_collision || motion.is_equal_approx(Vector2())) {
+ if (!collided || motion.is_equal_approx(Vector2())) {
break;
}
}
@@ -1235,6 +1229,8 @@ void CharacterBody2D::move_and_slide() {
if (on_floor && !vel_dir_facing_up) {
linear_velocity = linear_velocity.slide(up_direction);
}
+
+ return motion_results.size() > 0;
}
void CharacterBody2D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up) {
@@ -1246,20 +1242,19 @@ void CharacterBody2D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up)
PhysicsServer2D::MotionResult result;
if (move_and_collide(up_direction * -floor_snap_length, result, margin, true, false)) {
bool apply = true;
- float collision_angle = Math::acos(result.collision_normal.dot(up_direction));
- if (collision_angle <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) {
+ if (result.get_angle(up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) {
on_floor = true;
floor_normal = result.collision_normal;
platform_velocity = result.collider_velocity;
_set_platform_data(result);
- if (stop_on_slope) {
+ if (floor_stop_on_slope) {
// move and collide may stray the object a bit because of pre un-stucking,
// so only ensure that motion happens on floor direction in this case.
- if (result.motion.length() > margin) {
- result.motion = up_direction * up_direction.dot(result.motion);
+ if (result.travel.length() > margin) {
+ result.travel = up_direction * up_direction.dot(result.travel);
} else {
- result.motion = Vector2();
+ result.travel = Vector2();
}
}
} else {
@@ -1267,7 +1262,7 @@ void CharacterBody2D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up)
}
if (apply) {
- gt.elements[2] += result.motion;
+ gt.elements[2] += result.travel;
set_global_transform(gt);
}
}
@@ -1280,7 +1275,7 @@ bool CharacterBody2D::_on_floor_if_snapped(bool was_on_floor, bool vel_dir_facin
PhysicsServer2D::MotionResult result;
if (move_and_collide(up_direction * -floor_snap_length, result, margin, true, false)) {
- if (Math::acos(result.collision_normal.dot(up_direction)) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) {
+ if (result.get_angle(up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) {
return true;
}
}
@@ -1293,12 +1288,12 @@ void CharacterBody2D::_set_collision_direction(const PhysicsServer2D::MotionResu
return;
}
- if (Math::acos(p_result.collision_normal.dot(up_direction)) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //floor
+ if (p_result.get_angle(up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //floor
on_floor = true;
floor_normal = p_result.collision_normal;
platform_velocity = p_result.collider_velocity;
_set_platform_data(p_result);
- } else if (Math::acos(p_result.collision_normal.dot(-up_direction)) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //ceiling
+ } else if (p_result.get_angle(-up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //ceiling
on_ceiling = true;
} else {
on_wall = true;
@@ -1352,11 +1347,16 @@ Vector2 CharacterBody2D::get_floor_normal() const {
return floor_normal;
}
+real_t CharacterBody2D::get_floor_angle(const Vector2 &p_up_direction) const {
+ ERR_FAIL_COND_V(p_up_direction == Vector2(), 0);
+ return Math::acos(floor_normal.dot(p_up_direction));
+}
+
Vector2 CharacterBody2D::get_platform_velocity() const {
return platform_velocity;
}
-int CharacterBody2D::get_slide_count() const {
+int CharacterBody2D::get_slide_collision_count() const {
return motion_results.size();
}
@@ -1380,6 +1380,13 @@ Ref<KinematicCollision2D> CharacterBody2D::_get_slide_collision(int p_bounce) {
return slide_colliders[p_bounce];
}
+Ref<KinematicCollision2D> CharacterBody2D::_get_last_slide_collision() {
+ if (motion_results.size() == 0) {
+ return Ref<KinematicCollision2D>();
+ }
+ return _get_slide_collision(motion_results.size() - 1);
+}
+
void CharacterBody2D::set_safe_margin(real_t p_margin) {
margin = p_margin;
}
@@ -1388,28 +1395,28 @@ real_t CharacterBody2D::get_safe_margin() const {
return margin;
}
-bool CharacterBody2D::is_stop_on_slope_enabled() const {
- return stop_on_slope;
+bool CharacterBody2D::is_floor_stop_on_slope_enabled() const {
+ return floor_stop_on_slope;
}
-void CharacterBody2D::set_stop_on_slope_enabled(bool p_enabled) {
- stop_on_slope = p_enabled;
+void CharacterBody2D::set_floor_stop_on_slope_enabled(bool p_enabled) {
+ floor_stop_on_slope = p_enabled;
}
-bool CharacterBody2D::is_constant_speed_on_floor_enabled() const {
- return constant_speed_on_floor;
+bool CharacterBody2D::is_floor_constant_speed_enabled() const {
+ return floor_constant_speed;
}
-void CharacterBody2D::set_constant_speed_on_floor_enabled(bool p_enabled) {
- constant_speed_on_floor = p_enabled;
+void CharacterBody2D::set_floor_constant_speed_enabled(bool p_enabled) {
+ floor_constant_speed = p_enabled;
}
-bool CharacterBody2D::is_move_on_floor_only_enabled() const {
- return move_on_floor_only;
+bool CharacterBody2D::is_floor_block_on_wall_enabled() const {
+ return floor_block_on_wall;
}
-void CharacterBody2D::set_move_on_floor_only_enabled(bool p_enabled) {
- move_on_floor_only = p_enabled;
+void CharacterBody2D::set_floor_block_on_wall_enabled(bool p_enabled) {
+ floor_block_on_wall = p_enabled;
}
bool CharacterBody2D::is_slide_on_ceiling_enabled() const {
@@ -1420,12 +1427,12 @@ void CharacterBody2D::set_slide_on_ceiling_enabled(bool p_enabled) {
slide_on_ceiling = p_enabled;
}
-uint32_t CharacterBody2D::get_exclude_body_layers() const {
- return exclude_body_layers;
+uint32_t CharacterBody2D::get_moving_platform_ignore_layers() const {
+ return moving_platform_ignore_layers;
}
-void CharacterBody2D::set_exclude_body_layers(uint32_t p_exclude_layers) {
- exclude_body_layers = p_exclude_layers;
+void CharacterBody2D::set_moving_platform_ignore_layers(uint32_t p_exclude_layers) {
+ moving_platform_ignore_layers = p_exclude_layers;
}
int CharacterBody2D::get_max_slides() const {
@@ -1484,17 +1491,17 @@ void CharacterBody2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_safe_margin", "pixels"), &CharacterBody2D::set_safe_margin);
ClassDB::bind_method(D_METHOD("get_safe_margin"), &CharacterBody2D::get_safe_margin);
- ClassDB::bind_method(D_METHOD("is_stop_on_slope_enabled"), &CharacterBody2D::is_stop_on_slope_enabled);
- ClassDB::bind_method(D_METHOD("set_stop_on_slope_enabled", "enabled"), &CharacterBody2D::set_stop_on_slope_enabled);
- ClassDB::bind_method(D_METHOD("set_constant_speed_on_floor_enabled", "enabled"), &CharacterBody2D::set_constant_speed_on_floor_enabled);
- ClassDB::bind_method(D_METHOD("is_constant_speed_on_floor_enabled"), &CharacterBody2D::is_constant_speed_on_floor_enabled);
- ClassDB::bind_method(D_METHOD("set_move_on_floor_only_enabled", "enabled"), &CharacterBody2D::set_move_on_floor_only_enabled);
- ClassDB::bind_method(D_METHOD("is_move_on_floor_only_enabled"), &CharacterBody2D::is_move_on_floor_only_enabled);
+ ClassDB::bind_method(D_METHOD("is_floor_stop_on_slope_enabled"), &CharacterBody2D::is_floor_stop_on_slope_enabled);
+ ClassDB::bind_method(D_METHOD("set_floor_stop_on_slope_enabled", "enabled"), &CharacterBody2D::set_floor_stop_on_slope_enabled);
+ ClassDB::bind_method(D_METHOD("set_floor_constant_speed_enabled", "enabled"), &CharacterBody2D::set_floor_constant_speed_enabled);
+ ClassDB::bind_method(D_METHOD("is_floor_constant_speed_enabled"), &CharacterBody2D::is_floor_constant_speed_enabled);
+ ClassDB::bind_method(D_METHOD("set_floor_block_on_wall_enabled", "enabled"), &CharacterBody2D::set_floor_block_on_wall_enabled);
+ ClassDB::bind_method(D_METHOD("is_floor_block_on_wall_enabled"), &CharacterBody2D::is_floor_block_on_wall_enabled);
ClassDB::bind_method(D_METHOD("set_slide_on_ceiling_enabled", "enabled"), &CharacterBody2D::set_slide_on_ceiling_enabled);
ClassDB::bind_method(D_METHOD("is_slide_on_ceiling_enabled"), &CharacterBody2D::is_slide_on_ceiling_enabled);
- ClassDB::bind_method(D_METHOD("set_exclude_body_layers", "exclude_layer"), &CharacterBody2D::set_exclude_body_layers);
- ClassDB::bind_method(D_METHOD("get_exclude_body_layers"), &CharacterBody2D::get_exclude_body_layers);
+ ClassDB::bind_method(D_METHOD("set_moving_platform_ignore_layers", "exclude_layer"), &CharacterBody2D::set_moving_platform_ignore_layers);
+ ClassDB::bind_method(D_METHOD("get_moving_platform_ignore_layers"), &CharacterBody2D::get_moving_platform_ignore_layers);
ClassDB::bind_method(D_METHOD("get_max_slides"), &CharacterBody2D::get_max_slides);
ClassDB::bind_method(D_METHOD("set_max_slides", "max_slides"), &CharacterBody2D::set_max_slides);
@@ -1512,21 +1519,24 @@ void CharacterBody2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_on_wall"), &CharacterBody2D::is_on_wall);
ClassDB::bind_method(D_METHOD("is_on_wall_only"), &CharacterBody2D::is_on_wall_only);
ClassDB::bind_method(D_METHOD("get_floor_normal"), &CharacterBody2D::get_floor_normal);
+ ClassDB::bind_method(D_METHOD("get_floor_angle", "up_direction"), &CharacterBody2D::get_floor_angle, DEFVAL(Vector2(0.0, -1.0)));
ClassDB::bind_method(D_METHOD("get_platform_velocity"), &CharacterBody2D::get_platform_velocity);
- ClassDB::bind_method(D_METHOD("get_slide_count"), &CharacterBody2D::get_slide_count);
+ ClassDB::bind_method(D_METHOD("get_slide_collision_count"), &CharacterBody2D::get_slide_collision_count);
ClassDB::bind_method(D_METHOD("get_slide_collision", "slide_idx"), &CharacterBody2D::_get_slide_collision);
+ ClassDB::bind_method(D_METHOD("get_last_slide_collision"), &CharacterBody2D::_get_last_slide_collision);
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "linear_velocity"), "set_linear_velocity", "get_linear_velocity");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stop_on_slope"), "set_stop_on_slope_enabled", "is_stop_on_slope_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "constant_speed_on_floor"), "set_constant_speed_on_floor_enabled", "is_constant_speed_on_floor_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "move_on_floor_only"), "set_move_on_floor_only_enabled", "is_move_on_floor_only_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slide_on_ceiling"), "set_slide_on_ceiling_enabled", "is_slide_on_ceiling_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "max_slides", PROPERTY_HINT_RANGE, "1,8,1,or_greater"), "set_max_slides", "get_max_slides");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "max_slides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_max_slides", "get_max_slides");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "up_direction"), "set_up_direction", "get_up_direction");
+ ADD_GROUP("Floor", "floor_");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "floor_stop_on_slope"), "set_floor_stop_on_slope_enabled", "is_floor_stop_on_slope_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "floor_constant_speed"), "set_floor_constant_speed_enabled", "is_floor_constant_speed_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "floor_block_on_wall"), "set_floor_block_on_wall_enabled", "is_floor_block_on_wall_enabled");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_max_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians"), "set_floor_max_angle", "get_floor_max_angle");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_snap_length", PROPERTY_HINT_RANGE, "0,1000,0.1"), "set_floor_snap_length", "get_floor_snap_length");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "up_direction"), "set_up_direction", "get_up_direction");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "exclude_body_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_exclude_body_layers", "get_exclude_body_layers");
-
+ ADD_GROUP("Moving platform", "moving_platform");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_ignore_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_moving_platform_ignore_layers", "get_moving_platform_ignore_layers");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision/safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001"), "set_safe_margin", "get_safe_margin");
}
@@ -1553,13 +1563,18 @@ Vector2 KinematicCollision2D::get_normal() const {
}
Vector2 KinematicCollision2D::get_travel() const {
- return result.motion;
+ return result.travel;
}
Vector2 KinematicCollision2D::get_remainder() const {
return result.remainder;
}
+real_t KinematicCollision2D::get_angle(const Vector2 &p_up_direction) const {
+ ERR_FAIL_COND_V(p_up_direction == Vector2(), 0);
+ return result.get_angle(p_up_direction);
+}
+
Object *KinematicCollision2D::get_local_shape() const {
if (!owner) {
return nullptr;
@@ -1614,6 +1629,7 @@ void KinematicCollision2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_normal"), &KinematicCollision2D::get_normal);
ClassDB::bind_method(D_METHOD("get_travel"), &KinematicCollision2D::get_travel);
ClassDB::bind_method(D_METHOD("get_remainder"), &KinematicCollision2D::get_remainder);
+ ClassDB::bind_method(D_METHOD("get_angle", "up_direction"), &KinematicCollision2D::get_angle, DEFVAL(Vector2(0.0, -1.0)));
ClassDB::bind_method(D_METHOD("get_local_shape"), &KinematicCollision2D::get_local_shape);
ClassDB::bind_method(D_METHOD("get_collider"), &KinematicCollision2D::get_collider);
ClassDB::bind_method(D_METHOD("get_collider_id"), &KinematicCollision2D::get_collider_id);
diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h
index 3d894416f0..81c5067146 100644
--- a/scene/2d/physics_body_2d.h
+++ b/scene/2d/physics_body_2d.h
@@ -271,16 +271,16 @@ class CharacterBody2D : public PhysicsBody2D {
private:
real_t margin = 0.08;
- bool stop_on_slope = false;
- bool constant_speed_on_floor = false;
- bool move_on_floor_only = true;
+ bool floor_stop_on_slope = false;
+ bool floor_constant_speed = false;
+ bool floor_block_on_wall = true;
bool slide_on_ceiling = true;
int max_slides = 4;
int platform_layer;
real_t floor_max_angle = Math::deg2rad((real_t)45.0);
float floor_snap_length = 0;
Vector2 up_direction = Vector2(0.0, -1.0);
- uint32_t exclude_body_layers = 0;
+ uint32_t moving_platform_ignore_layers = 0;
Vector2 linear_velocity;
Vector2 floor_normal;
@@ -296,14 +296,14 @@ private:
void set_safe_margin(real_t p_margin);
real_t get_safe_margin() const;
- bool is_stop_on_slope_enabled() const;
- void set_stop_on_slope_enabled(bool p_enabled);
+ bool is_floor_stop_on_slope_enabled() const;
+ void set_floor_stop_on_slope_enabled(bool p_enabled);
- bool is_constant_speed_on_floor_enabled() const;
- void set_constant_speed_on_floor_enabled(bool p_enabled);
+ bool is_floor_constant_speed_enabled() const;
+ void set_floor_constant_speed_enabled(bool p_enabled);
- bool is_move_on_floor_only_enabled() const;
- void set_move_on_floor_only_enabled(bool p_enabled);
+ bool is_floor_block_on_wall_enabled() const;
+ void set_floor_block_on_wall_enabled(bool p_enabled);
bool is_slide_on_ceiling_enabled() const;
void set_slide_on_ceiling_enabled(bool p_enabled);
@@ -317,10 +317,11 @@ private:
real_t get_floor_snap_length();
void set_floor_snap_length(real_t p_floor_snap_length);
- uint32_t get_exclude_body_layers() const;
- void set_exclude_body_layers(const uint32_t p_exclude_layer);
+ uint32_t get_moving_platform_ignore_layers() const;
+ void set_moving_platform_ignore_layers(const uint32_t p_exclude_layer);
Ref<KinematicCollision2D> _get_slide_collision(int p_bounce);
+ Ref<KinematicCollision2D> _get_last_slide_collision();
const Vector2 &get_up_direction() const;
bool _on_floor_if_snapped(bool was_on_floor, bool vel_dir_facing_up);
void set_up_direction(const Vector2 &p_up_direction);
@@ -333,7 +334,7 @@ protected:
static void _bind_methods();
public:
- void move_and_slide();
+ bool move_and_slide();
const Vector2 &get_linear_velocity() const;
void set_linear_velocity(const Vector2 &p_velocity);
@@ -345,9 +346,10 @@ public:
bool is_on_ceiling() const;
bool is_on_ceiling_only() const;
Vector2 get_floor_normal() const;
+ real_t get_floor_angle(const Vector2 &p_up_direction = Vector2(0.0, -1.0)) const;
Vector2 get_platform_velocity() const;
- int get_slide_count() const;
+ int get_slide_collision_count() const;
PhysicsServer2D::MotionResult get_slide_collision(int p_bounce) const;
CharacterBody2D();
@@ -370,6 +372,7 @@ public:
Vector2 get_normal() const;
Vector2 get_travel() const;
Vector2 get_remainder() const;
+ real_t get_angle(const Vector2 &p_up_direction = Vector2(0.0, -1.0)) const;
Object *get_local_shape() const;
Object *get_collider() const;
ObjectID get_collider_id() const;
diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp
index 3d44c629d3..4bbbc3575d 100644
--- a/scene/2d/skeleton_2d.cpp
+++ b/scene/2d/skeleton_2d.cpp
@@ -325,7 +325,7 @@ bool Bone2D::_editor_get_bone_shape(Vector<Vector2> *p_shape, Vector<Vector2> *p
Vector2 rel;
if (p_other_bone) {
- rel = (p_other_bone->get_global_transform().get_origin() - get_global_transform().get_origin());
+ rel = (p_other_bone->get_global_position() - get_global_position());
rel = rel.rotated(-get_global_rotation()); // Undo Bone2D node's rotation so its drawn correctly regardless of the node's rotation
} else {
real_t angle_to_use = get_rotation() + bone_angle;
@@ -454,7 +454,7 @@ void Bone2D::calculate_length_and_rotation() {
for (int i = 0; i < child_count; i++) {
Bone2D *child = Object::cast_to<Bone2D>(get_child(i));
if (child) {
- Vector2 child_local_pos = to_local(child->get_global_transform().get_origin());
+ Vector2 child_local_pos = to_local(child->get_global_position());
length = child_local_pos.length();
bone_angle = Math::atan2(child_local_pos.normalized().y, child_local_pos.normalized().x);
calculated = true;
diff --git a/scene/3d/SCsub b/scene/3d/SCsub
index 40bdaee47d..fc61250247 100644
--- a/scene/3d/SCsub
+++ b/scene/3d/SCsub
@@ -2,7 +2,4 @@
Import("env")
-if env["disable_3d"]:
- env.add_source_files(env.scene_sources, "node_3d.cpp")
-else:
- env.add_source_files(env.scene_sources, "*.cpp")
+env.add_source_files(env.scene_sources, "*.cpp")
diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp
index 711e7fbd5a..d2424c9a3b 100644
--- a/scene/3d/audio_stream_player_3d.cpp
+++ b/scene/3d/audio_stream_player_3d.cpp
@@ -428,7 +428,7 @@ void AudioStreamPlayer3D::_notification(int p_what) {
if (area && area->is_using_reverb_bus() && area->get_reverb_uniformity() > 0) {
area_sound_pos = space_state->get_closest_point_to_object_volume(area->get_rid(), listener_node->get_global_transform().origin);
- listener_area_pos = listener_node->get_global_transform().affine_inverse().xform(area_sound_pos);
+ listener_area_pos = listener_node->to_local(area_sound_pos);
}
if (max_distance > 0) {
diff --git a/scene/3d/bone_attachment_3d.cpp b/scene/3d/bone_attachment_3d.cpp
index 5315e685a0..70361f4787 100644
--- a/scene/3d/bone_attachment_3d.cpp
+++ b/scene/3d/bone_attachment_3d.cpp
@@ -32,7 +32,15 @@
void BoneAttachment3D::_validate_property(PropertyInfo &property) const {
if (property.name == "bone_name") {
- Skeleton3D *parent = Object::cast_to<Skeleton3D>(get_parent());
+ // Because it is a constant function, we cannot use the _get_skeleton_3d function.
+ const Skeleton3D *parent = nullptr;
+ if (use_external_skeleton) {
+ if (external_skeleton_node_cache.is_valid()) {
+ parent = Object::cast_to<Skeleton3D>(ObjectDB::get_instance(external_skeleton_node_cache));
+ }
+ } else {
+ parent = Object::cast_to<Skeleton3D>(get_parent());
+ }
if (parent) {
String names;
@@ -52,55 +60,321 @@ void BoneAttachment3D::_validate_property(PropertyInfo &property) const {
}
}
+bool BoneAttachment3D::_set(const StringName &p_path, const Variant &p_value) {
+ if (p_path == SNAME("override_pose")) {
+ set_override_pose(p_value);
+ } else if (p_path == SNAME("override_mode")) {
+ set_override_mode(p_value);
+ } else if (p_path == SNAME("use_external_skeleton")) {
+ set_use_external_skeleton(p_value);
+ } else if (p_path == SNAME("external_skeleton")) {
+ set_external_skeleton(p_value);
+ }
+
+ return true;
+}
+
+bool BoneAttachment3D::_get(const StringName &p_path, Variant &r_ret) const {
+ if (p_path == SNAME("override_pose")) {
+ r_ret = get_override_pose();
+ } else if (p_path == SNAME("override_mode")) {
+ r_ret = get_override_mode();
+ } else if (p_path == SNAME("use_external_skeleton")) {
+ r_ret = get_use_external_skeleton();
+ } else if (p_path == SNAME("external_skeleton")) {
+ r_ret = get_external_skeleton();
+ }
+
+ return true;
+}
+
+void BoneAttachment3D::_get_property_list(List<PropertyInfo> *p_list) const {
+ p_list->push_back(PropertyInfo(Variant::BOOL, "override_pose", PROPERTY_HINT_NONE, ""));
+ if (override_pose) {
+ p_list->push_back(PropertyInfo(Variant::INT, "override_mode", PROPERTY_HINT_ENUM, "Global Pose Override, Local Pose Override, Custom Pose"));
+ }
+
+ p_list->push_back(PropertyInfo(Variant::BOOL, "use_external_skeleton", PROPERTY_HINT_NONE, ""));
+ if (use_external_skeleton) {
+ p_list->push_back(PropertyInfo(Variant::NODE_PATH, "external_skeleton", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton3D"));
+ }
+}
+
+TypedArray<String> BoneAttachment3D::get_configuration_warnings() const {
+ TypedArray<String> warnings = Node3D::get_configuration_warnings();
+
+ if (use_external_skeleton) {
+ if (external_skeleton_node_cache.is_null()) {
+ warnings.append(TTR("External Skeleton3D node not set! Please set a path to an external Skeleton3D node."));
+ }
+ } else {
+ Skeleton3D *parent = Object::cast_to<Skeleton3D>(get_parent());
+ if (!parent) {
+ warnings.append(TTR("Parent node is not a Skeleton3D node! Please use an extenral Skeleton3D if you intend to use the BoneAttachment3D without it being a child of a Skeleton3D node."));
+ }
+ }
+
+ if (bone_idx == -1) {
+ warnings.append(TTR("BoneAttachment3D node is not bound to any bones! Please select a bone to attach this node."));
+ }
+
+ return warnings;
+}
+
+void BoneAttachment3D::_update_external_skeleton_cache() {
+ external_skeleton_node_cache = ObjectID();
+ if (has_node(external_skeleton_node)) {
+ Node *node = get_node(external_skeleton_node);
+ ERR_FAIL_COND_MSG(!node, "Cannot update external skeleton cache: Node cannot be found!");
+
+ // Make sure it's a skeleton3D
+ Skeleton3D *sk = Object::cast_to<Skeleton3D>(node);
+ ERR_FAIL_COND_MSG(!sk, "Cannot update external skeleton cache: Skeleton3D Nodepath does not point to a Skeleton3D node!");
+
+ external_skeleton_node_cache = node->get_instance_id();
+ } else {
+ if (external_skeleton_node.is_empty()) {
+ BoneAttachment3D *parent_attachment = Object::cast_to<BoneAttachment3D>(get_parent());
+ if (parent_attachment) {
+ parent_attachment->_update_external_skeleton_cache();
+ if (parent_attachment->has_node(parent_attachment->external_skeleton_node)) {
+ Node *node = parent_attachment->get_node(parent_attachment->external_skeleton_node);
+ ERR_FAIL_COND_MSG(!node, "Cannot update external skeleton cache: Parent's Skeleton3D node cannot be found!");
+
+ // Make sure it's a skeleton3D
+ Skeleton3D *sk = Object::cast_to<Skeleton3D>(node);
+ ERR_FAIL_COND_MSG(!sk, "Cannot update external skeleton cache: Parent Skeleton3D Nodepath does not point to a Skeleton3D node!");
+
+ external_skeleton_node_cache = node->get_instance_id();
+ external_skeleton_node = get_path_to(node);
+ }
+ }
+ }
+ }
+}
+
void BoneAttachment3D::_check_bind() {
- Skeleton3D *sk = Object::cast_to<Skeleton3D>(get_parent());
- if (sk) {
- int idx = sk->find_bone(bone_name);
- if (idx != -1) {
- sk->bind_child_node_to_bone(idx, this);
- set_transform(sk->get_bone_global_pose(idx));
+ Skeleton3D *sk = _get_skeleton3d();
+
+ if (sk && !bound) {
+ if (bone_idx <= -1) {
+ bone_idx = sk->find_bone(bone_name);
+ }
+ if (bone_idx != -1) {
+ sk->call_deferred("connect", "bone_pose_changed", callable_mp(this, &BoneAttachment3D::on_bone_pose_update));
bound = true;
+ call_deferred(SNAME("on_bone_pose_update"), bone_idx);
+ }
+ }
+}
+
+Skeleton3D *BoneAttachment3D::_get_skeleton3d() {
+ if (use_external_skeleton) {
+ if (external_skeleton_node_cache.is_valid()) {
+ return Object::cast_to<Skeleton3D>(ObjectDB::get_instance(external_skeleton_node_cache));
+ } else {
+ _update_external_skeleton_cache();
+ if (external_skeleton_node_cache.is_valid()) {
+ return Object::cast_to<Skeleton3D>(ObjectDB::get_instance(external_skeleton_node_cache));
+ }
}
+ } else {
+ return Object::cast_to<Skeleton3D>(get_parent());
}
+ return nullptr;
}
void BoneAttachment3D::_check_unbind() {
if (bound) {
- Skeleton3D *sk = Object::cast_to<Skeleton3D>(get_parent());
+ Skeleton3D *sk = _get_skeleton3d();
+
if (sk) {
- int idx = sk->find_bone(bone_name);
- if (idx != -1) {
- sk->unbind_child_node_from_bone(idx, this);
- }
+ sk->disconnect(SNAME("bone_pose_changed"), callable_mp(this, &BoneAttachment3D::on_bone_pose_update));
}
bound = false;
}
}
+void BoneAttachment3D::_transform_changed() {
+ if (!is_inside_tree()) {
+ return;
+ }
+
+ if (override_pose) {
+ Skeleton3D *sk = _get_skeleton3d();
+
+ ERR_FAIL_COND_MSG(!sk, "Cannot override pose: Skeleton not found!");
+ ERR_FAIL_INDEX_MSG(bone_idx, sk->get_bone_count(), "Cannot override pose: Bone index is out of range!");
+
+ Transform3D our_trans = get_transform();
+ if (use_external_skeleton) {
+ our_trans = sk->world_transform_to_global_pose(get_global_transform());
+ }
+
+ if (override_mode == OVERRIDE_MODES::MODE_GLOBAL_POSE) {
+ sk->set_bone_global_pose_override(bone_idx, our_trans, 1.0, true);
+ } else if (override_mode == OVERRIDE_MODES::MODE_LOCAL_POSE) {
+ sk->set_bone_local_pose_override(bone_idx, sk->global_pose_to_local_pose(bone_idx, our_trans), 1.0, true);
+ } else if (override_mode == OVERRIDE_MODES::MODE_CUSTOM_POSE) {
+ sk->set_bone_custom_pose(bone_idx, sk->global_pose_to_local_pose(bone_idx, our_trans));
+ }
+ }
+}
+
void BoneAttachment3D::set_bone_name(const String &p_name) {
+ bone_name = p_name;
+ Skeleton3D *sk = _get_skeleton3d();
+ if (sk) {
+ set_bone_idx(sk->find_bone(bone_name));
+ }
+}
+
+String BoneAttachment3D::get_bone_name() const {
+ return bone_name;
+}
+
+void BoneAttachment3D::set_bone_idx(const int &p_idx) {
if (is_inside_tree()) {
_check_unbind();
}
- bone_name = p_name;
+ bone_idx = p_idx;
+
+ Skeleton3D *sk = _get_skeleton3d();
+ if (sk) {
+ if (bone_idx <= -1 || bone_idx >= sk->get_bone_count()) {
+ WARN_PRINT("Bone index out of range! Cannot connect BoneAttachment to node!");
+ bone_idx = -1;
+ } else {
+ bone_name = sk->get_bone_name(bone_idx);
+ }
+ }
if (is_inside_tree()) {
_check_bind();
}
+
+ notify_property_list_changed();
}
-String BoneAttachment3D::get_bone_name() const {
- return bone_name;
+int BoneAttachment3D::get_bone_idx() const {
+ return bone_idx;
+}
+
+void BoneAttachment3D::set_override_pose(bool p_override) {
+ override_pose = p_override;
+ set_notify_local_transform(override_pose);
+ set_process_internal(override_pose);
+
+ if (!override_pose) {
+ Skeleton3D *sk = _get_skeleton3d();
+ if (sk) {
+ if (override_mode == OVERRIDE_MODES::MODE_GLOBAL_POSE) {
+ sk->set_bone_global_pose_override(bone_idx, Transform3D(), 0.0, false);
+ } else if (override_mode == OVERRIDE_MODES::MODE_LOCAL_POSE) {
+ sk->set_bone_local_pose_override(bone_idx, Transform3D(), 0.0, false);
+ } else if (override_mode == OVERRIDE_MODES::MODE_CUSTOM_POSE) {
+ sk->set_bone_custom_pose(bone_idx, Transform3D());
+ }
+ }
+ _transform_changed();
+ }
+ notify_property_list_changed();
+}
+
+bool BoneAttachment3D::get_override_pose() const {
+ return override_pose;
+}
+
+void BoneAttachment3D::set_override_mode(int p_mode) {
+ if (override_pose) {
+ Skeleton3D *sk = _get_skeleton3d();
+ if (sk) {
+ if (override_mode == OVERRIDE_MODES::MODE_GLOBAL_POSE) {
+ sk->set_bone_global_pose_override(bone_idx, Transform3D(), 0.0, false);
+ } else if (override_mode == OVERRIDE_MODES::MODE_LOCAL_POSE) {
+ sk->set_bone_local_pose_override(bone_idx, Transform3D(), 0.0, false);
+ } else if (override_mode == OVERRIDE_MODES::MODE_CUSTOM_POSE) {
+ sk->set_bone_custom_pose(bone_idx, Transform3D());
+ }
+ }
+ override_mode = p_mode;
+ _transform_changed();
+ return;
+ }
+ override_mode = p_mode;
+}
+
+int BoneAttachment3D::get_override_mode() const {
+ return override_mode;
+}
+
+void BoneAttachment3D::set_use_external_skeleton(bool p_use_external) {
+ use_external_skeleton = p_use_external;
+
+ if (use_external_skeleton) {
+ _check_unbind();
+ _update_external_skeleton_cache();
+ _check_bind();
+ _transform_changed();
+ }
+
+ notify_property_list_changed();
+}
+
+bool BoneAttachment3D::get_use_external_skeleton() const {
+ return use_external_skeleton;
+}
+
+void BoneAttachment3D::set_external_skeleton(NodePath p_path) {
+ external_skeleton_node = p_path;
+ _update_external_skeleton_cache();
+ notify_property_list_changed();
+}
+
+NodePath BoneAttachment3D::get_external_skeleton() const {
+ return external_skeleton_node;
}
void BoneAttachment3D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
+ if (use_external_skeleton) {
+ _update_external_skeleton_cache();
+ }
_check_bind();
} break;
case NOTIFICATION_EXIT_TREE: {
_check_unbind();
} break;
+ case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
+ _transform_changed();
+ } break;
+ case NOTIFICATION_INTERNAL_PROCESS: {
+ if (_override_dirty) {
+ _override_dirty = false;
+ }
+ }
+ }
+}
+
+void BoneAttachment3D::on_bone_pose_update(int p_bone_index) {
+ if (bone_idx == p_bone_index) {
+ Skeleton3D *sk = _get_skeleton3d();
+ if (sk) {
+ if (!override_pose) {
+ if (use_external_skeleton) {
+ set_global_transform(sk->global_pose_to_world_transform(sk->get_bone_global_pose(bone_idx)));
+ } else {
+ set_transform(sk->get_bone_global_pose(bone_idx));
+ }
+ } else {
+ if (!_override_dirty) {
+ _transform_changed();
+ _override_dirty = true;
+ }
+ }
+ }
}
}
@@ -111,5 +385,21 @@ void BoneAttachment3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_bone_name", "bone_name"), &BoneAttachment3D::set_bone_name);
ClassDB::bind_method(D_METHOD("get_bone_name"), &BoneAttachment3D::get_bone_name);
+ ClassDB::bind_method(D_METHOD("set_bone_idx", "bone_idx"), &BoneAttachment3D::set_bone_idx);
+ ClassDB::bind_method(D_METHOD("get_bone_idx"), &BoneAttachment3D::get_bone_idx);
+
+ ClassDB::bind_method(D_METHOD("on_bone_pose_update", "bone_index"), &BoneAttachment3D::on_bone_pose_update);
+
+ ClassDB::bind_method(D_METHOD("set_override_pose", "override_pose"), &BoneAttachment3D::set_override_pose);
+ ClassDB::bind_method(D_METHOD("get_override_pose"), &BoneAttachment3D::get_override_pose);
+ ClassDB::bind_method(D_METHOD("set_override_mode", "override_mode"), &BoneAttachment3D::set_override_mode);
+ ClassDB::bind_method(D_METHOD("get_override_mode"), &BoneAttachment3D::get_override_mode);
+
+ ClassDB::bind_method(D_METHOD("set_use_external_skeleton", "use_external_skeleton"), &BoneAttachment3D::set_use_external_skeleton);
+ ClassDB::bind_method(D_METHOD("get_use_external_skeleton"), &BoneAttachment3D::get_use_external_skeleton);
+ ClassDB::bind_method(D_METHOD("set_external_skeleton", "external_skeleton"), &BoneAttachment3D::set_external_skeleton);
+ ClassDB::bind_method(D_METHOD("get_external_skeleton"), &BoneAttachment3D::get_external_skeleton);
+
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "bone_name"), "set_bone_name", "get_bone_name");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "bone_idx"), "set_bone_idx", "get_bone_idx");
}
diff --git a/scene/3d/bone_attachment_3d.h b/scene/3d/bone_attachment_3d.h
index 0c6d5f12b1..cf681cace8 100644
--- a/scene/3d/bone_attachment_3d.h
+++ b/scene/3d/bone_attachment_3d.h
@@ -38,20 +38,59 @@ class BoneAttachment3D : public Node3D {
bool bound = false;
String bone_name;
+ int bone_idx = -1;
+
+ bool override_pose = false;
+ int override_mode = 0;
+ bool _override_dirty = false;
+
+ enum OVERRIDE_MODES {
+ MODE_GLOBAL_POSE,
+ MODE_LOCAL_POSE,
+ MODE_CUSTOM_POSE
+ };
+
+ bool use_external_skeleton = false;
+ NodePath external_skeleton_node;
+ ObjectID external_skeleton_node_cache;
void _check_bind();
void _check_unbind();
+ void _transform_changed();
+ void _update_external_skeleton_cache();
+ Skeleton3D *_get_skeleton3d();
+
protected:
virtual void _validate_property(PropertyInfo &property) const override;
+ bool _get(const StringName &p_path, Variant &r_ret) const;
+ bool _set(const StringName &p_path, const Variant &p_value);
+ void _get_property_list(List<PropertyInfo> *p_list) const;
void _notification(int p_what);
static void _bind_methods();
public:
+ virtual TypedArray<String> get_configuration_warnings() const override;
+
void set_bone_name(const String &p_name);
String get_bone_name() const;
+ void set_bone_idx(const int &p_idx);
+ int get_bone_idx() const;
+
+ void set_override_pose(bool p_override);
+ bool get_override_pose() const;
+ void set_override_mode(int p_mode);
+ int get_override_mode() const;
+
+ void set_use_external_skeleton(bool p_external_skeleton);
+ bool get_use_external_skeleton() const;
+ void set_external_skeleton(NodePath p_skeleton);
+ NodePath get_external_skeleton() const;
+
+ virtual void on_bone_pose_update(int p_bone_index);
+
BoneAttachment3D();
};
diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp
index 12c91271d4..610974ff90 100644
--- a/scene/3d/physics_body_3d.cpp
+++ b/scene/3d/physics_body_3d.cpp
@@ -140,27 +140,27 @@ bool PhysicsBody3D::move_and_collide(const Vector3 &p_motion, PhysicsServer3D::M
}
// Check depth of recovery.
- real_t projected_length = r_result.motion.dot(motion_normal);
- Vector3 recovery = r_result.motion - motion_normal * projected_length;
+ real_t projected_length = r_result.travel.dot(motion_normal);
+ Vector3 recovery = r_result.travel - motion_normal * projected_length;
real_t recovery_length = recovery.length();
// Fixes cases where canceling slide causes the motion to go too deep into the ground,
// because we're only taking rest information into account and not general recovery.
if (recovery_length < (real_t)p_margin + precision) {
// Apply adjustment to motion.
- r_result.motion = motion_normal * projected_length;
- r_result.remainder = p_motion - r_result.motion;
+ r_result.travel = motion_normal * projected_length;
+ r_result.remainder = p_motion - r_result.travel;
}
}
}
for (int i = 0; i < 3; i++) {
if (locked_axis & (1 << i)) {
- r_result.motion[i] = 0;
+ r_result.travel[i] = 0;
}
}
if (!p_test_only) {
- gt.origin += r_result.motion;
+ gt.origin += r_result.travel;
set_global_transform(gt);
}
@@ -1079,7 +1079,7 @@ void RigidBody3D::_reload_physics_characteristics() {
//so, if you pass 45 as limit, avoid numerical precision errors when angle is 45.
#define FLOOR_ANGLE_THRESHOLD 0.01
-void CharacterBody3D::move_and_slide() {
+bool CharacterBody3D::move_and_slide() {
Vector3 body_velocity_normal = linear_velocity.normalized();
bool was_on_floor = on_floor;
@@ -1126,7 +1126,7 @@ void CharacterBody3D::move_and_slide() {
// No sliding on first attempt to keep floor motion stable when possible,
// when stop on slope is enabled.
- bool sliding_enabled = !stop_on_slope;
+ bool sliding_enabled = !floor_stop_on_slope;
for (int iteration = 0; iteration < max_slides; ++iteration) {
PhysicsServer3D::MotionResult result;
@@ -1141,17 +1141,17 @@ void CharacterBody3D::move_and_slide() {
motion_results.push_back(result);
_set_collision_direction(result);
- if (on_floor && stop_on_slope) {
+ if (on_floor && floor_stop_on_slope) {
if ((body_velocity_normal + up_direction).length() < 0.01) {
Transform3D gt = get_global_transform();
- if (result.motion.length() > margin) {
- gt.origin -= result.motion.slide(up_direction);
+ if (result.travel.length() > margin) {
+ gt.origin -= result.travel.slide(up_direction);
} else {
- gt.origin -= result.motion;
+ gt.origin -= result.travel;
}
set_global_transform(gt);
linear_velocity = Vector3();
- return;
+ return true;
}
}
@@ -1181,39 +1181,39 @@ void CharacterBody3D::move_and_slide() {
linear_velocity += current_floor_velocity;
}
- if (!was_on_floor || snap == Vector3()) {
- return;
- }
-
- // Apply snap.
- Transform3D gt = get_global_transform();
- PhysicsServer3D::MotionResult result;
- if (move_and_collide(snap, result, margin, true, false)) {
- bool apply = true;
- if (up_direction != Vector3()) {
- if (Math::acos(result.collision_normal.dot(up_direction)) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) {
- on_floor = true;
- floor_normal = result.collision_normal;
- on_floor_body = result.collider;
- floor_velocity = result.collider_velocity;
- if (stop_on_slope) {
- // move and collide may stray the object a bit because of pre un-stucking,
- // so only ensure that motion happens on floor direction in this case.
- if (result.motion.length() > margin) {
- result.motion = result.motion.project(up_direction);
- } else {
- result.motion = Vector3();
+ if (was_on_floor && snap != Vector3()) {
+ // Apply snap.
+ Transform3D gt = get_global_transform();
+ PhysicsServer3D::MotionResult result;
+ if (move_and_collide(snap, result, margin, true, false)) {
+ bool apply = true;
+ if (up_direction != Vector3()) {
+ if (result.get_angle(up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) {
+ on_floor = true;
+ floor_normal = result.collision_normal;
+ on_floor_body = result.collider;
+ floor_velocity = result.collider_velocity;
+ if (floor_stop_on_slope) {
+ // move and collide may stray the object a bit because of pre un-stucking,
+ // so only ensure that motion happens on floor direction in this case.
+ if (result.travel.length() > margin) {
+ result.travel = result.travel.project(up_direction);
+ } else {
+ result.travel = Vector3();
+ }
}
+ } else {
+ apply = false; //snapped with floor direction, but did not snap to a floor, do not snap.
}
- } else {
- apply = false; //snapped with floor direction, but did not snap to a floor, do not snap.
}
- }
- if (apply) {
- gt.origin += result.motion;
- set_global_transform(gt);
+ if (apply) {
+ gt.origin += result.travel;
+ set_global_transform(gt);
+ }
}
}
+
+ return motion_results.size() > 0;
}
void CharacterBody3D::_set_collision_direction(const PhysicsServer3D::MotionResult &p_result) {
@@ -1221,12 +1221,12 @@ void CharacterBody3D::_set_collision_direction(const PhysicsServer3D::MotionResu
//all is a wall
on_wall = true;
} else {
- if (Math::acos(p_result.collision_normal.dot(up_direction)) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //floor
+ if (p_result.get_angle(up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //floor
on_floor = true;
floor_normal = p_result.collision_normal;
on_floor_body = p_result.collider;
floor_velocity = p_result.collider_velocity;
- } else if (Math::acos(p_result.collision_normal.dot(-up_direction)) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //ceiling
+ } else if (p_result.get_angle(-up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //ceiling
on_ceiling = true;
} else {
on_wall = true;
@@ -1256,23 +1256,40 @@ bool CharacterBody3D::is_on_floor() const {
return on_floor;
}
+bool CharacterBody3D::is_on_floor_only() const {
+ return on_floor && !on_wall && !on_ceiling;
+}
+
bool CharacterBody3D::is_on_wall() const {
return on_wall;
}
+bool CharacterBody3D::is_on_wall_only() const {
+ return on_wall && !on_floor && !on_ceiling;
+}
+
bool CharacterBody3D::is_on_ceiling() const {
return on_ceiling;
}
+bool CharacterBody3D::is_on_ceiling_only() const {
+ return on_ceiling && !on_floor && !on_wall;
+}
+
Vector3 CharacterBody3D::get_floor_normal() const {
return floor_normal;
}
-Vector3 CharacterBody3D::get_floor_velocity() const {
+real_t CharacterBody3D::get_floor_angle(const Vector3 &p_up_direction) const {
+ ERR_FAIL_COND_V(p_up_direction == Vector3(), 0);
+ return Math::acos(floor_normal.dot(p_up_direction));
+}
+
+Vector3 CharacterBody3D::get_platform_velocity() const {
return floor_velocity;
}
-int CharacterBody3D::get_slide_count() const {
+int CharacterBody3D::get_slide_collision_count() const {
return motion_results.size();
}
@@ -1296,12 +1313,19 @@ Ref<KinematicCollision3D> CharacterBody3D::_get_slide_collision(int p_bounce) {
return slide_colliders[p_bounce];
}
-bool CharacterBody3D::is_stop_on_slope_enabled() const {
- return stop_on_slope;
+Ref<KinematicCollision3D> CharacterBody3D::_get_last_slide_collision() {
+ if (motion_results.size() == 0) {
+ return Ref<KinematicCollision3D>();
+ }
+ return _get_slide_collision(motion_results.size() - 1);
}
-void CharacterBody3D::set_stop_on_slope_enabled(bool p_enabled) {
- stop_on_slope = p_enabled;
+bool CharacterBody3D::is_floor_stop_on_slope_enabled() const {
+ return floor_stop_on_slope;
+}
+
+void CharacterBody3D::set_floor_stop_on_slope_enabled(bool p_enabled) {
+ floor_stop_on_slope = p_enabled;
}
int CharacterBody3D::get_max_slides() const {
@@ -1359,8 +1383,8 @@ void CharacterBody3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_safe_margin", "pixels"), &CharacterBody3D::set_safe_margin);
ClassDB::bind_method(D_METHOD("get_safe_margin"), &CharacterBody3D::get_safe_margin);
- ClassDB::bind_method(D_METHOD("is_stop_on_slope_enabled"), &CharacterBody3D::is_stop_on_slope_enabled);
- ClassDB::bind_method(D_METHOD("set_stop_on_slope_enabled", "enabled"), &CharacterBody3D::set_stop_on_slope_enabled);
+ ClassDB::bind_method(D_METHOD("is_floor_stop_on_slope_enabled"), &CharacterBody3D::is_floor_stop_on_slope_enabled);
+ ClassDB::bind_method(D_METHOD("set_floor_stop_on_slope_enabled", "enabled"), &CharacterBody3D::set_floor_stop_on_slope_enabled);
ClassDB::bind_method(D_METHOD("get_max_slides"), &CharacterBody3D::get_max_slides);
ClassDB::bind_method(D_METHOD("set_max_slides", "max_slides"), &CharacterBody3D::set_max_slides);
ClassDB::bind_method(D_METHOD("get_floor_max_angle"), &CharacterBody3D::get_floor_max_angle);
@@ -1371,20 +1395,26 @@ void CharacterBody3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_up_direction", "up_direction"), &CharacterBody3D::set_up_direction);
ClassDB::bind_method(D_METHOD("is_on_floor"), &CharacterBody3D::is_on_floor);
+ ClassDB::bind_method(D_METHOD("is_on_floor_only"), &CharacterBody3D::is_on_floor_only);
ClassDB::bind_method(D_METHOD("is_on_ceiling"), &CharacterBody3D::is_on_ceiling);
+ ClassDB::bind_method(D_METHOD("is_on_ceiling_only"), &CharacterBody3D::is_on_ceiling_only);
ClassDB::bind_method(D_METHOD("is_on_wall"), &CharacterBody3D::is_on_wall);
+ ClassDB::bind_method(D_METHOD("is_on_wall_only"), &CharacterBody3D::is_on_wall_only);
ClassDB::bind_method(D_METHOD("get_floor_normal"), &CharacterBody3D::get_floor_normal);
- ClassDB::bind_method(D_METHOD("get_floor_velocity"), &CharacterBody3D::get_floor_velocity);
+ ClassDB::bind_method(D_METHOD("get_floor_angle", "up_direction"), &CharacterBody3D::get_floor_angle, DEFVAL(Vector3(0.0, 1.0, 0.0)));
+ ClassDB::bind_method(D_METHOD("get_platform_velocity"), &CharacterBody3D::get_platform_velocity);
- ClassDB::bind_method(D_METHOD("get_slide_count"), &CharacterBody3D::get_slide_count);
+ ClassDB::bind_method(D_METHOD("get_slide_collision_count"), &CharacterBody3D::get_slide_collision_count);
ClassDB::bind_method(D_METHOD("get_slide_collision", "slide_idx"), &CharacterBody3D::_get_slide_collision);
+ ClassDB::bind_method(D_METHOD("get_last_slide_collision"), &CharacterBody3D::_get_last_slide_collision);
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "linear_velocity"), "set_linear_velocity", "get_linear_velocity");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stop_on_slope"), "set_stop_on_slope_enabled", "is_stop_on_slope_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "max_slides", PROPERTY_HINT_RANGE, "1,8,1,or_greater"), "set_max_slides", "get_max_slides");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_max_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians"), "set_floor_max_angle", "get_floor_max_angle");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "max_slides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_max_slides", "get_max_slides");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "snap"), "set_snap", "get_snap");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "up_direction"), "set_up_direction", "get_up_direction");
+ ADD_GROUP("Floor", "floor_");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_max_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians"), "set_floor_max_angle", "get_floor_max_angle");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "floor_stop_on_slope"), "set_floor_stop_on_slope_enabled", "is_floor_stop_on_slope_enabled");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision/safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001"), "set_safe_margin", "get_safe_margin");
}
@@ -1411,13 +1441,18 @@ Vector3 KinematicCollision3D::get_normal() const {
}
Vector3 KinematicCollision3D::get_travel() const {
- return result.motion;
+ return result.travel;
}
Vector3 KinematicCollision3D::get_remainder() const {
return result.remainder;
}
+real_t KinematicCollision3D::get_angle(const Vector3 &p_up_direction) const {
+ ERR_FAIL_COND_V(p_up_direction == Vector3(), 0);
+ return result.get_angle(p_up_direction);
+}
+
Object *KinematicCollision3D::get_local_shape() const {
if (!owner) {
return nullptr;
@@ -1472,6 +1507,7 @@ void KinematicCollision3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_normal"), &KinematicCollision3D::get_normal);
ClassDB::bind_method(D_METHOD("get_travel"), &KinematicCollision3D::get_travel);
ClassDB::bind_method(D_METHOD("get_remainder"), &KinematicCollision3D::get_remainder);
+ ClassDB::bind_method(D_METHOD("get_angle", "up_direction"), &KinematicCollision3D::get_angle, DEFVAL(Vector3(0.0, 1.0, 0.0)));
ClassDB::bind_method(D_METHOD("get_local_shape"), &KinematicCollision3D::get_local_shape);
ClassDB::bind_method(D_METHOD("get_collider"), &KinematicCollision3D::get_collider);
ClassDB::bind_method(D_METHOD("get_collider_id"), &KinematicCollision3D::get_collider_id);
@@ -2199,7 +2235,6 @@ void PhysicalBone3D::_notification(int p_what) {
if (parent_skeleton) {
if (-1 != bone_id) {
parent_skeleton->unbind_physical_bone_from_bone(bone_id);
- parent_skeleton->unbind_child_node_from_bone(bone_id, this);
bone_id = -1;
}
}
@@ -2656,7 +2691,6 @@ void PhysicalBone3D::update_bone_id() {
if (-1 != bone_id) {
// Assert the unbind from old node
parent_skeleton->unbind_physical_bone_from_bone(bone_id);
- parent_skeleton->unbind_child_node_from_bone(bone_id, this);
}
bone_id = new_bone_id;
diff --git a/scene/3d/physics_body_3d.h b/scene/3d/physics_body_3d.h
index b076560ead..9c40f92f06 100644
--- a/scene/3d/physics_body_3d.h
+++ b/scene/3d/physics_body_3d.h
@@ -278,7 +278,7 @@ class CharacterBody3D : public PhysicsBody3D {
private:
real_t margin = 0.001;
- bool stop_on_slope = false;
+ bool floor_stop_on_slope = false;
int max_slides = 4;
real_t floor_max_angle = Math::deg2rad((real_t)45.0);
Vector3 snap;
@@ -296,14 +296,15 @@ private:
Vector<Ref<KinematicCollision3D>> slide_colliders;
Ref<KinematicCollision3D> _get_slide_collision(int p_bounce);
+ Ref<KinematicCollision3D> _get_last_slide_collision();
void _set_collision_direction(const PhysicsServer3D::MotionResult &p_result);
void set_safe_margin(real_t p_margin);
real_t get_safe_margin() const;
- bool is_stop_on_slope_enabled() const;
- void set_stop_on_slope_enabled(bool p_enabled);
+ bool is_floor_stop_on_slope_enabled() const;
+ void set_floor_stop_on_slope_enabled(bool p_enabled);
int get_max_slides() const;
void set_max_slides(int p_max_slides);
@@ -322,18 +323,22 @@ protected:
static void _bind_methods();
public:
- void move_and_slide();
+ bool move_and_slide();
virtual Vector3 get_linear_velocity() const override;
void set_linear_velocity(const Vector3 &p_velocity);
bool is_on_floor() const;
+ bool is_on_floor_only() const;
bool is_on_wall() const;
+ bool is_on_wall_only() const;
bool is_on_ceiling() const;
+ bool is_on_ceiling_only() const;
Vector3 get_floor_normal() const;
- Vector3 get_floor_velocity() const;
+ real_t get_floor_angle(const Vector3 &p_up_direction = Vector3(0.0, 1.0, 0.0)) const;
+ Vector3 get_platform_velocity() const;
- int get_slide_count() const;
+ int get_slide_collision_count() const;
PhysicsServer3D::MotionResult get_slide_collision(int p_bounce) const;
CharacterBody3D();
@@ -356,6 +361,7 @@ public:
Vector3 get_normal() const;
Vector3 get_travel() const;
Vector3 get_remainder() const;
+ real_t get_angle(const Vector3 &p_up_direction = Vector3(0.0, 1.0, 0.0)) const;
Object *get_local_shape() const;
Object *get_collider() const;
ObjectID get_collider_id() const;
diff --git a/scene/3d/physics_joint_3d.cpp b/scene/3d/physics_joint_3d.cpp
index 59440bd1a8..12938946a0 100644
--- a/scene/3d/physics_joint_3d.cpp
+++ b/scene/3d/physics_joint_3d.cpp
@@ -259,11 +259,11 @@ real_t PinJoint3D::get_param(Param p_param) const {
void PinJoint3D::_configure_joint(RID p_joint, PhysicsBody3D *body_a, PhysicsBody3D *body_b) {
Vector3 pinpos = get_global_transform().origin;
- Vector3 local_a = body_a->get_global_transform().affine_inverse().xform(pinpos);
+ Vector3 local_a = body_a->to_local(pinpos);
Vector3 local_b;
if (body_b) {
- local_b = body_b->get_global_transform().affine_inverse().xform(pinpos);
+ local_b = body_b->to_local(pinpos);
} else {
local_b = pinpos;
}
diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp
index c996735761..857916e23d 100644
--- a/scene/3d/skeleton_3d.cpp
+++ b/scene/3d/skeleton_3d.cpp
@@ -31,7 +31,10 @@
#include "skeleton_3d.h"
#include "core/object/message_queue.h"
+#include "core/variant/type_info.h"
#include "scene/3d/physics_body_3d.h"
+#include "scene/resources/skeleton_modification_3d.h"
+#include "scene/resources/surface_tool.h"
#include "scene/scene_string_names.h"
void SkinReference::_skin_changed() {
@@ -68,6 +71,13 @@ SkinReference::~SkinReference() {
bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) {
String path = p_path;
+#ifndef _3D_DISABLED
+ if (path.begins_with("modification_stack")) {
+ set_modification_stack(p_value);
+ return true;
+ }
+#endif //_3D_DISABLED
+
if (!path.begins_with("bones/")) {
return false;
}
@@ -100,6 +110,13 @@ bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) {
bool Skeleton3D::_get(const StringName &p_path, Variant &r_ret) const {
String path = p_path;
+#ifndef _3D_DISABLED
+ if (path.begins_with("modification_stack")) {
+ r_ret = modification_stack;
+ return true;
+ }
+#endif //_3D_DISABLED
+
if (!path.begins_with("bones/")) {
return false;
}
@@ -135,6 +152,14 @@ void Skeleton3D::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::BOOL, prep + "enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prep + "pose", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
}
+
+#ifndef _3D_DISABLED
+ p_list->push_back(
+ PropertyInfo(Variant::OBJECT, "modification_stack",
+ PROPERTY_HINT_RESOURCE_TYPE,
+ "SkeletonModificationStack3D",
+ PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE));
+#endif //_3D_DISABLED
}
void Skeleton3D::_update_process_order() {
@@ -145,47 +170,29 @@ void Skeleton3D::_update_process_order() {
Bone *bonesptr = bones.ptrw();
int len = bones.size();
- process_order.resize(len);
- int *order = process_order.ptrw();
+ parentless_bones.clear();
+
for (int i = 0; i < len; i++) {
if (bonesptr[i].parent >= len) {
//validate this just in case
ERR_PRINT("Bone " + itos(i) + " has invalid parent: " + itos(bonesptr[i].parent));
bonesptr[i].parent = -1;
}
- order[i] = i;
- bonesptr[i].sort_index = i;
- }
- //now check process order
- int pass_count = 0;
- while (pass_count < len * len) {
- //using bubblesort because of simplicity, it won't run every frame though.
- //bublesort worst case is O(n^2), and this may be an infinite loop if cyclic
- bool swapped = false;
- for (int i = 0; i < len; i++) {
- int parent_idx = bonesptr[order[i]].parent;
- if (parent_idx < 0) {
- continue; //do nothing because it has no parent
- }
- //swap indices
- int parent_order = bonesptr[parent_idx].sort_index;
- if (parent_order > i) {
- bonesptr[order[i]].sort_index = parent_order;
- bonesptr[parent_idx].sort_index = i;
- //swap order
- SWAP(order[i], order[parent_order]);
- swapped = true;
- }
- }
+ bonesptr[i].child_bones.clear();
- if (!swapped) {
- break;
- }
- pass_count++;
- }
+ if (bonesptr[i].parent != -1) {
+ int parent_bone_idx = bonesptr[i].parent;
- if (pass_count == len * len) {
- ERR_PRINT("Skeleton3D parenthood graph is cyclic");
+ // Check to see if this node is already added to the parent:
+ if (bonesptr[parent_bone_idx].child_bones.find(i) < 0) {
+ // Add the child node
+ bonesptr[parent_bone_idx].child_bones.push_back(i);
+ } else {
+ ERR_PRINT("Skeleton3D parenthood graph is cyclic");
+ }
+ } else {
+ parentless_bones.push_back(i);
+ }
}
process_order_dirty = false;
@@ -196,78 +203,12 @@ void Skeleton3D::_notification(int p_what) {
case NOTIFICATION_UPDATE_SKELETON: {
RenderingServer *rs = RenderingServer::get_singleton();
Bone *bonesptr = bones.ptrw();
- int len = bones.size();
-
- _update_process_order();
-
- const int *order = process_order.ptr();
-
- for (int i = 0; i < len; i++) {
- Bone &b = bonesptr[order[i]];
-
- if (b.disable_rest) {
- if (b.enabled) {
- Transform3D pose = b.pose;
- if (b.custom_pose_enable) {
- pose = b.custom_pose * pose;
- }
- if (b.parent >= 0) {
- b.pose_global = bonesptr[b.parent].pose_global * pose;
- b.pose_global_no_override = bonesptr[b.parent].pose_global * pose;
- } else {
- b.pose_global = pose;
- b.pose_global_no_override = pose;
- }
- } else {
- if (b.parent >= 0) {
- b.pose_global = bonesptr[b.parent].pose_global;
- b.pose_global_no_override = bonesptr[b.parent].pose_global;
- } else {
- b.pose_global = Transform3D();
- b.pose_global_no_override = Transform3D();
- }
- }
-
- } else {
- if (b.enabled) {
- Transform3D pose = b.pose;
- if (b.custom_pose_enable) {
- pose = b.custom_pose * pose;
- }
- if (b.parent >= 0) {
- b.pose_global = bonesptr[b.parent].pose_global * (b.rest * pose);
- b.pose_global_no_override = bonesptr[b.parent].pose_global * (b.rest * pose);
- } else {
- b.pose_global = b.rest * pose;
- b.pose_global_no_override = b.rest * pose;
- }
- } else {
- if (b.parent >= 0) {
- b.pose_global = bonesptr[b.parent].pose_global * b.rest;
- b.pose_global_no_override = bonesptr[b.parent].pose_global * b.rest;
- } else {
- b.pose_global = b.rest;
- b.pose_global_no_override = b.rest;
- }
- }
- }
- if (b.global_pose_override_amount >= CMP_EPSILON) {
- b.pose_global = b.pose_global.interpolate_with(b.global_pose_override, b.global_pose_override_amount);
- }
-
- if (b.global_pose_override_reset) {
- b.global_pose_override_amount = 0.0;
- }
+ int len = bones.size();
+ dirty = false;
- for (const ObjectID &E : b.nodes_bound) {
- Object *obj = ObjectDB::get_instance(E);
- ERR_CONTINUE(!obj);
- Node3D *node_3d = Object::cast_to<Node3D>(obj);
- ERR_CONTINUE(!node_3d);
- node_3d->set_transform(b.pose_global);
- }
- }
+ // Update bone transforms
+ force_update_all_bone_transforms();
//update skins
for (Set<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) {
@@ -325,32 +266,53 @@ void Skeleton3D::_notification(int p_what) {
}
}
- dirty = false;
-
#ifdef TOOLS_ENABLED
emit_signal(SceneStringNames::get_singleton()->pose_updated);
#endif // TOOLS_ENABLED
} break;
+#ifndef _3D_DISABLED
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
// This is active only if the skeleton animates the physical bones
// and the state of the bone is not active.
- if (animate_physical_bones) {
- for (int i = 0; i < bones.size(); i += 1) {
- if (bones[i].physical_bone) {
- if (bones[i].physical_bone->is_simulating_physics() == false) {
- bones[i].physical_bone->reset_to_rest_position();
+ if (Engine::get_singleton()->is_editor_hint()) {
+ if (animate_physical_bones) {
+ for (int i = 0; i < bones.size(); i += 1) {
+ if (bones[i].physical_bone) {
+ if (bones[i].physical_bone->is_simulating_physics() == false) {
+ bones[i].physical_bone->reset_to_rest_position();
+ }
}
}
}
}
+
+ if (modification_stack.is_valid()) {
+ execute_modifications(get_physics_process_delta_time(), SkeletonModificationStack3D::EXECUTION_MODE::execution_mode_physics_process);
+ }
+
} break;
+#endif // _3D_DISABLED
+
+#ifndef _3D_DISABLED
+ case NOTIFICATION_INTERNAL_PROCESS: {
+ if (modification_stack.is_valid()) {
+ execute_modifications(get_process_delta_time(), SkeletonModificationStack3D::EXECUTION_MODE::execution_mode_process);
+ }
+ } break;
+#endif // _3D_DISABLED
+
+#ifndef _3D_DISABLED
case NOTIFICATION_READY: {
- if (Engine::get_singleton()->is_editor_hint()) {
- set_physics_process_internal(true);
+ set_physics_process_internal(true);
+ set_process_internal(true);
+
+ if (modification_stack.is_valid()) {
+ set_modification_stack(modification_stack);
}
} break;
+#endif // _3D_DISABLED
}
}
@@ -363,15 +325,23 @@ void Skeleton3D::clear_bones_global_pose_override() {
}
void Skeleton3D::set_bone_global_pose_override(int p_bone, const Transform3D &p_pose, real_t p_amount, bool p_persistent) {
- ERR_FAIL_INDEX(p_bone, bones.size());
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX(p_bone, bone_size);
bones.write[p_bone].global_pose_override_amount = p_amount;
bones.write[p_bone].global_pose_override = p_pose;
bones.write[p_bone].global_pose_override_reset = !p_persistent;
_make_dirty();
}
+Transform3D Skeleton3D::get_bone_global_pose_override(int p_bone) const {
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D());
+ return bones[p_bone].global_pose_override;
+}
+
Transform3D Skeleton3D::get_bone_global_pose(int p_bone) const {
- ERR_FAIL_INDEX_V(p_bone, bones.size(), Transform3D());
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D());
if (dirty) {
const_cast<Skeleton3D *>(this)->notification(NOTIFICATION_UPDATE_SKELETON);
}
@@ -379,13 +349,107 @@ Transform3D Skeleton3D::get_bone_global_pose(int p_bone) const {
}
Transform3D Skeleton3D::get_bone_global_pose_no_override(int p_bone) const {
- ERR_FAIL_INDEX_V(p_bone, bones.size(), Transform3D());
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D());
if (dirty) {
const_cast<Skeleton3D *>(this)->notification(NOTIFICATION_UPDATE_SKELETON);
}
return bones[p_bone].pose_global_no_override;
}
+void Skeleton3D::clear_bones_local_pose_override() {
+ for (int i = 0; i < bones.size(); i += 1) {
+ bones.write[i].local_pose_override_amount = 0;
+ }
+ _make_dirty();
+}
+
+void Skeleton3D::set_bone_local_pose_override(int p_bone, const Transform3D &p_pose, real_t p_amount, bool p_persistent) {
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX(p_bone, bone_size);
+ bones.write[p_bone].local_pose_override_amount = p_amount;
+ bones.write[p_bone].local_pose_override = p_pose;
+ bones.write[p_bone].local_pose_override_reset = !p_persistent;
+ _make_dirty();
+}
+
+Transform3D Skeleton3D::get_bone_local_pose_override(int p_bone) const {
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D());
+ return bones[p_bone].local_pose_override;
+}
+
+void Skeleton3D::update_bone_rest_forward_vector(int p_bone, bool p_force_update) {
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX(p_bone, bone_size);
+
+ if (bones[p_bone].rest_bone_forward_vector.length_squared() > 0 && p_force_update == false) {
+ update_bone_rest_forward_axis(p_bone, p_force_update);
+ }
+
+ // If it is a child/leaf bone...
+ if (get_bone_parent(p_bone) > 0) {
+ bones.write[p_bone].rest_bone_forward_vector = bones[p_bone].rest.origin.normalized();
+ } else {
+ // If it has children...
+ Vector<int> child_bones = get_bone_children(p_bone);
+ if (child_bones.size() > 0) {
+ Vector3 combined_child_dir = Vector3(0, 0, 0);
+ for (int i = 0; i < child_bones.size(); i++) {
+ combined_child_dir += bones[child_bones[i]].rest.origin.normalized();
+ }
+ combined_child_dir = combined_child_dir / child_bones.size();
+ bones.write[p_bone].rest_bone_forward_vector = combined_child_dir.normalized();
+ } else {
+ WARN_PRINT_ONCE("Cannot calculate forward direction for bone " + itos(p_bone));
+ WARN_PRINT_ONCE("Assuming direction of (0, 1, 0) for bone");
+ bones.write[p_bone].rest_bone_forward_vector = Vector3(0, 1, 0);
+ }
+ }
+ update_bone_rest_forward_axis(p_bone, p_force_update);
+}
+
+void Skeleton3D::update_bone_rest_forward_axis(int p_bone, bool p_force_update) {
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX(p_bone, bone_size);
+ if (bones[p_bone].rest_bone_forward_axis > -1 && p_force_update == false) {
+ return;
+ }
+
+ Vector3 forward_axis_absolute = bones[p_bone].rest_bone_forward_vector.abs();
+ if (forward_axis_absolute.x > forward_axis_absolute.y && forward_axis_absolute.x > forward_axis_absolute.z) {
+ if (bones[p_bone].rest_bone_forward_vector.x > 0) {
+ bones.write[p_bone].rest_bone_forward_axis = BONE_AXIS_X_FORWARD;
+ } else {
+ bones.write[p_bone].rest_bone_forward_axis = BONE_AXIS_NEGATIVE_X_FORWARD;
+ }
+ } else if (forward_axis_absolute.y > forward_axis_absolute.x && forward_axis_absolute.y > forward_axis_absolute.z) {
+ if (bones[p_bone].rest_bone_forward_vector.y > 0) {
+ bones.write[p_bone].rest_bone_forward_axis = BONE_AXIS_Y_FORWARD;
+ } else {
+ bones.write[p_bone].rest_bone_forward_axis = BONE_AXIS_NEGATIVE_Y_FORWARD;
+ }
+ } else {
+ if (bones[p_bone].rest_bone_forward_vector.z > 0) {
+ bones.write[p_bone].rest_bone_forward_axis = BONE_AXIS_Z_FORWARD;
+ } else {
+ bones.write[p_bone].rest_bone_forward_axis = BONE_AXIS_NEGATIVE_Z_FORWARD;
+ }
+ }
+}
+
+Vector3 Skeleton3D::get_bone_axis_forward_vector(int p_bone) {
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX_V(p_bone, bone_size, Vector3(0, 0, 0));
+ return bones[p_bone].rest_bone_forward_vector;
+}
+
+int Skeleton3D::get_bone_axis_forward_enum(int p_bone) {
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX_V(p_bone, bone_size, -1);
+ return bones[p_bone].rest_bone_forward_axis;
+}
+
// skeleton creation api
void Skeleton3D::add_bone(const String &p_name) {
ERR_FAIL_COND(p_name == "" || p_name.find(":") != -1 || p_name.find("/") != -1);
@@ -414,14 +478,15 @@ int Skeleton3D::find_bone(const String &p_name) const {
}
String Skeleton3D::get_bone_name(int p_bone) const {
- ERR_FAIL_INDEX_V(p_bone, bones.size(), "");
-
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX_V(p_bone, bone_size, "");
return bones[p_bone].name;
}
void Skeleton3D::set_bone_name(int p_bone, const String &p_name) {
- ERR_FAIL_INDEX(p_bone, bones.size());
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX(p_bone, bone_size);
- for (int i = 0; i < bones.size(); i++) {
+ for (int i = 0; i < bone_size; i++) {
if (i != p_bone) {
ERR_FAIL_COND(bones[i].name == p_name);
}
@@ -449,7 +514,8 @@ int Skeleton3D::get_bone_count() const {
}
void Skeleton3D::set_bone_parent(int p_bone, int p_parent) {
- ERR_FAIL_INDEX(p_bone, bones.size());
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX(p_bone, bone_size);
ERR_FAIL_COND(p_parent != -1 && (p_parent < 0));
bones.write[p_bone].parent = p_parent;
@@ -458,7 +524,8 @@ void Skeleton3D::set_bone_parent(int p_bone, int p_parent) {
}
void Skeleton3D::unparent_bone_and_rest(int p_bone) {
- ERR_FAIL_INDEX(p_bone, bones.size());
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX(p_bone, bone_size);
_update_process_order();
@@ -475,76 +542,93 @@ void Skeleton3D::unparent_bone_and_rest(int p_bone) {
}
void Skeleton3D::set_bone_disable_rest(int p_bone, bool p_disable) {
- ERR_FAIL_INDEX(p_bone, bones.size());
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX(p_bone, bone_size);
bones.write[p_bone].disable_rest = p_disable;
}
bool Skeleton3D::is_bone_rest_disabled(int p_bone) const {
- ERR_FAIL_INDEX_V(p_bone, bones.size(), false);
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX_V(p_bone, bone_size, false);
return bones[p_bone].disable_rest;
}
int Skeleton3D::get_bone_parent(int p_bone) const {
- ERR_FAIL_INDEX_V(p_bone, bones.size(), -1);
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX_V(p_bone, bone_size, -1);
return bones[p_bone].parent;
}
-void Skeleton3D::set_bone_rest(int p_bone, const Transform3D &p_rest) {
- ERR_FAIL_INDEX(p_bone, bones.size());
+Vector<int> Skeleton3D::get_bone_children(int p_bone) {
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX_V(p_bone, bone_size, Vector<int>());
+ return bones[p_bone].child_bones;
+}
- bones.write[p_bone].rest = p_rest;
+void Skeleton3D::set_bone_children(int p_bone, Vector<int> p_children) {
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX(p_bone, bone_size);
+ bones.write[p_bone].child_bones = p_children;
+
+ process_order_dirty = true;
_make_dirty();
}
-Transform3D Skeleton3D::get_bone_rest(int p_bone) const {
- ERR_FAIL_INDEX_V(p_bone, bones.size(), Transform3D());
- return bones[p_bone].rest;
+void Skeleton3D::add_bone_child(int p_bone, int p_child) {
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX(p_bone, bone_size);
+ bones.write[p_bone].child_bones.push_back(p_child);
+
+ process_order_dirty = true;
+ _make_dirty();
}
-void Skeleton3D::set_bone_enabled(int p_bone, bool p_enabled) {
- ERR_FAIL_INDEX(p_bone, bones.size());
+void Skeleton3D::remove_bone_child(int p_bone, int p_child) {
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX(p_bone, bone_size);
- bones.write[p_bone].enabled = p_enabled;
+ int child_idx = bones[p_bone].child_bones.find(p_child);
+ if (child_idx >= 0) {
+ bones.write[p_bone].child_bones.remove(child_idx);
+ } else {
+ WARN_PRINT("Cannot remove child bone: Child bone not found.");
+ }
+
+ process_order_dirty = true;
_make_dirty();
}
-bool Skeleton3D::is_bone_enabled(int p_bone) const {
- ERR_FAIL_INDEX_V(p_bone, bones.size(), false);
- return bones[p_bone].enabled;
+Vector<int> Skeleton3D::get_parentless_bones() {
+ return parentless_bones;
}
-void Skeleton3D::bind_child_node_to_bone(int p_bone, Node *p_node) {
- ERR_FAIL_NULL(p_node);
- ERR_FAIL_INDEX(p_bone, bones.size());
-
- ObjectID id = p_node->get_instance_id();
+void Skeleton3D::set_bone_rest(int p_bone, const Transform3D &p_rest) {
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX(p_bone, bone_size);
- for (const ObjectID &E : bones[p_bone].nodes_bound) {
- if (E == id) {
- return; // already here
- }
- }
+ bones.write[p_bone].rest = p_rest;
+ _make_dirty();
+}
+Transform3D Skeleton3D::get_bone_rest(int p_bone) const {
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D());
- bones.write[p_bone].nodes_bound.push_back(id);
+ return bones[p_bone].rest;
}
-void Skeleton3D::unbind_child_node_from_bone(int p_bone, Node *p_node) {
- ERR_FAIL_NULL(p_node);
- ERR_FAIL_INDEX(p_bone, bones.size());
+void Skeleton3D::set_bone_enabled(int p_bone, bool p_enabled) {
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX(p_bone, bone_size);
- ObjectID id = p_node->get_instance_id();
- bones.write[p_bone].nodes_bound.erase(id);
+ bones.write[p_bone].enabled = p_enabled;
+ _make_dirty();
}
-void Skeleton3D::get_bound_child_nodes_to_bone(int p_bone, List<Node *> *p_bound) const {
- ERR_FAIL_INDEX(p_bone, bones.size());
-
- for (const ObjectID &E : bones[p_bone].nodes_bound) {
- Object *obj = ObjectDB::get_instance(E);
- ERR_CONTINUE(!obj);
- p_bound->push_back(Object::cast_to<Node>(obj));
- }
+bool Skeleton3D::is_bone_enabled(int p_bone) const {
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX_V(p_bone, bone_size, false);
+ return bones[p_bone].enabled;
}
void Skeleton3D::clear_bones() {
@@ -557,7 +641,8 @@ void Skeleton3D::clear_bones() {
// posing api
void Skeleton3D::set_bone_pose(int p_bone, const Transform3D &p_pose) {
- ERR_FAIL_INDEX(p_bone, bones.size());
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX(p_bone, bone_size);
bones.write[p_bone].pose = p_pose;
if (is_inside_tree()) {
@@ -565,12 +650,14 @@ void Skeleton3D::set_bone_pose(int p_bone, const Transform3D &p_pose) {
}
}
Transform3D Skeleton3D::get_bone_pose(int p_bone) const {
- ERR_FAIL_INDEX_V(p_bone, bones.size(), Transform3D());
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D());
return bones[p_bone].pose;
}
void Skeleton3D::set_bone_custom_pose(int p_bone, const Transform3D &p_custom_pose) {
- ERR_FAIL_INDEX(p_bone, bones.size());
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX(p_bone, bone_size);
//ERR_FAIL_COND( !is_inside_scene() );
bones.write[p_bone].custom_pose_enable = (p_custom_pose != Transform3D());
@@ -580,7 +667,8 @@ void Skeleton3D::set_bone_custom_pose(int p_bone, const Transform3D &p_custom_po
}
Transform3D Skeleton3D::get_bone_custom_pose(int p_bone) const {
- ERR_FAIL_INDEX_V(p_bone, bones.size(), Transform3D());
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D());
return bones[p_bone].custom_pose;
}
@@ -593,24 +681,22 @@ void Skeleton3D::_make_dirty() {
dirty = true;
}
-int Skeleton3D::get_process_order(int p_idx) {
- ERR_FAIL_INDEX_V(p_idx, bones.size(), -1);
+void Skeleton3D::localize_rests() {
_update_process_order();
- return process_order[p_idx];
-}
-Vector<int> Skeleton3D::get_bone_process_orders() {
- _update_process_order();
- return process_order;
-}
+ Vector<int> bones_to_process = get_parentless_bones();
+ while (bones_to_process.size() > 0) {
+ int current_bone_idx = bones_to_process[0];
+ bones_to_process.erase(current_bone_idx);
-void Skeleton3D::localize_rests() {
- _update_process_order();
+ if (bones[current_bone_idx].parent >= 0) {
+ set_bone_rest(current_bone_idx, bones[bones[current_bone_idx].parent].rest.affine_inverse() * bones[current_bone_idx].rest);
+ }
- for (int i = bones.size() - 1; i >= 0; i--) {
- int idx = process_order[i];
- if (bones[idx].parent >= 0) {
- set_bone_rest(idx, bones[bones[idx].parent].rest.affine_inverse() * bones[idx].rest);
+ // Add the bone's children to the list of bones to be processed
+ int child_bone_size = bones[current_bone_idx].child_bones.size();
+ for (int i = 0; i < child_bone_size; i++) {
+ bones_to_process.push_back(bones[current_bone_idx].child_bones[i]);
}
}
}
@@ -637,7 +723,8 @@ bool Skeleton3D::get_animate_physical_bones() const {
}
void Skeleton3D::bind_physical_bone_to_bone(int p_bone, PhysicalBone3D *p_physical_bone) {
- ERR_FAIL_INDEX(p_bone, bones.size());
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX(p_bone, bone_size);
ERR_FAIL_COND(bones[p_bone].physical_bone);
ERR_FAIL_COND(!p_physical_bone);
bones.write[p_bone].physical_bone = p_physical_bone;
@@ -646,20 +733,23 @@ void Skeleton3D::bind_physical_bone_to_bone(int p_bone, PhysicalBone3D *p_physic
}
void Skeleton3D::unbind_physical_bone_from_bone(int p_bone) {
- ERR_FAIL_INDEX(p_bone, bones.size());
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX(p_bone, bone_size);
bones.write[p_bone].physical_bone = nullptr;
_rebuild_physical_bones_cache();
}
PhysicalBone3D *Skeleton3D::get_physical_bone(int p_bone) {
- ERR_FAIL_INDEX_V(p_bone, bones.size(), nullptr);
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX_V(p_bone, bone_size, nullptr);
return bones[p_bone].physical_bone;
}
PhysicalBone3D *Skeleton3D::get_physical_bone_parent(int p_bone) {
- ERR_FAIL_INDEX_V(p_bone, bones.size(), nullptr);
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX_V(p_bone, bone_size, nullptr);
if (bones[p_bone].cache_parent_physical_bone) {
return bones[p_bone].cache_parent_physical_bone;
@@ -669,7 +759,8 @@ PhysicalBone3D *Skeleton3D::get_physical_bone_parent(int p_bone) {
}
PhysicalBone3D *Skeleton3D::_get_physical_bone_parent(int p_bone) {
- ERR_FAIL_INDEX_V(p_bone, bones.size(), nullptr);
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX_V(p_bone, bone_size, nullptr);
const int parent_bone = bones[p_bone].parent;
if (0 > parent_bone) {
@@ -800,15 +891,20 @@ Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) {
// pose changed, rebuild cache of inverses
const Bone *bonesptr = bones.ptr();
int len = bones.size();
- const int *order = process_order.ptr();
// calculate global rests and invert them
- for (int i = 0; i < len; i++) {
- const Bone &b = bonesptr[order[i]];
+ Vector<int> bones_to_process = get_parentless_bones();
+ while (bones_to_process.size() > 0) {
+ int current_bone_idx = bones_to_process[0];
+ bones_to_process.erase(current_bone_idx);
+ const Bone &b = bonesptr[current_bone_idx];
+
+ // Note: the code below may not work by default. May need to track an integer for the bone pose index order
+ // in the while loop, instead of using current_bone_idx.
if (b.parent >= 0) {
- skin->set_bind_pose(order[i], skin->get_bind_pose(b.parent) * b.rest);
+ skin->set_bind_pose(current_bone_idx, skin->get_bind_pose(b.parent) * b.rest);
} else {
- skin->set_bind_pose(order[i], b.rest);
+ skin->set_bind_pose(current_bone_idx, b.rest);
}
}
@@ -839,17 +935,202 @@ Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) {
return skin_ref;
}
+void Skeleton3D::force_update_all_bone_transforms() {
+ _update_process_order();
+
+ for (int i = 0; i < parentless_bones.size(); i++) {
+ force_update_bone_children_transforms(parentless_bones[i]);
+ }
+}
+
+void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) {
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX(p_bone_idx, bone_size);
+
+ Bone *bonesptr = bones.ptrw();
+ List<int> bones_to_process = List<int>();
+ bones_to_process.push_back(p_bone_idx);
+
+ while (bones_to_process.size() > 0) {
+ int current_bone_idx = bones_to_process[0];
+ bones_to_process.erase(current_bone_idx);
+
+ Bone &b = bonesptr[current_bone_idx];
+
+ if (b.disable_rest) {
+ if (b.enabled) {
+ Transform3D pose = b.pose;
+ if (b.custom_pose_enable) {
+ pose = b.custom_pose * pose;
+ }
+ if (b.parent >= 0) {
+ b.pose_global = bonesptr[b.parent].pose_global * pose;
+ b.pose_global_no_override = b.pose_global;
+ } else {
+ b.pose_global = pose;
+ b.pose_global_no_override = b.pose_global;
+ }
+ } else {
+ if (b.parent >= 0) {
+ b.pose_global = bonesptr[b.parent].pose_global;
+ b.pose_global_no_override = b.pose_global;
+ } else {
+ b.pose_global = Transform3D();
+ b.pose_global_no_override = b.pose_global;
+ }
+ }
+
+ } else {
+ if (b.enabled) {
+ Transform3D pose = b.pose;
+ if (b.custom_pose_enable) {
+ pose = b.custom_pose * pose;
+ }
+ if (b.parent >= 0) {
+ b.pose_global = bonesptr[b.parent].pose_global * (b.rest * pose);
+ b.pose_global_no_override = b.pose_global;
+ } else {
+ b.pose_global = b.rest * pose;
+ b.pose_global_no_override = b.pose_global;
+ }
+ } else {
+ if (b.parent >= 0) {
+ b.pose_global = bonesptr[b.parent].pose_global * b.rest;
+ b.pose_global_no_override = b.pose_global;
+ } else {
+ b.pose_global = b.rest;
+ b.pose_global_no_override = b.pose_global;
+ }
+ }
+ }
+
+ if (b.local_pose_override_amount >= CMP_EPSILON) {
+ Transform3D override_local_pose;
+ if (b.parent >= 0) {
+ override_local_pose = bonesptr[b.parent].pose_global * (b.rest * b.local_pose_override);
+ } else {
+ override_local_pose = (b.rest * b.local_pose_override);
+ }
+ b.pose_global = b.pose_global.interpolate_with(override_local_pose, b.local_pose_override_amount);
+ }
+
+ if (b.global_pose_override_amount >= CMP_EPSILON) {
+ b.pose_global = b.pose_global.interpolate_with(b.global_pose_override, b.global_pose_override_amount);
+ }
+
+ if (b.local_pose_override_reset) {
+ b.local_pose_override_amount = 0.0;
+ }
+ if (b.global_pose_override_reset) {
+ b.global_pose_override_amount = 0.0;
+ }
+
+ // Add the bone's children to the list of bones to be processed
+ int child_bone_size = b.child_bones.size();
+ for (int i = 0; i < child_bone_size; i++) {
+ bones_to_process.push_back(b.child_bones[i]);
+ }
+
+ emit_signal(SceneStringNames::get_singleton()->bone_pose_changed, current_bone_idx);
+ }
+}
+
// helper functions
-Transform3D Skeleton3D::bone_transform_to_world_transform(Transform3D p_bone_transform) {
- return get_global_transform() * p_bone_transform;
+
+Transform3D Skeleton3D::global_pose_to_world_transform(Transform3D p_global_pose) {
+ return get_global_transform() * p_global_pose;
}
-Transform3D Skeleton3D::world_transform_to_bone_transform(Transform3D p_world_transform) {
+Transform3D Skeleton3D::world_transform_to_global_pose(Transform3D p_world_transform) {
return get_global_transform().affine_inverse() * p_world_transform;
}
+Transform3D Skeleton3D::global_pose_to_local_pose(int p_bone_idx, Transform3D p_global_pose) {
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX_V(p_bone_idx, bone_size, Transform3D());
+ if (bones[p_bone_idx].parent >= 0) {
+ int parent_bone_idx = bones[p_bone_idx].parent;
+ Transform3D conversion_transform = (bones[parent_bone_idx].pose_global * bones[p_bone_idx].rest);
+ return conversion_transform.affine_inverse() * p_global_pose;
+ } else {
+ return p_global_pose;
+ }
+}
+
+Transform3D Skeleton3D::local_pose_to_global_pose(int p_bone_idx, Transform3D p_local_pose) {
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX_V(p_bone_idx, bone_size, Transform3D());
+ if (bones[p_bone_idx].parent >= 0) {
+ int parent_bone_idx = bones[p_bone_idx].parent;
+ Transform3D conversion_transform = (bones[parent_bone_idx].pose_global * bones[p_bone_idx].rest);
+ return conversion_transform * p_local_pose;
+ } else {
+ return p_local_pose;
+ }
+}
+
+Basis Skeleton3D::global_pose_z_forward_to_bone_forward(int p_bone_idx, Basis p_basis) {
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX_V(p_bone_idx, bone_size, Basis());
+ Basis return_basis = p_basis;
+
+ if (bones[p_bone_idx].rest_bone_forward_axis < 0) {
+ update_bone_rest_forward_vector(p_bone_idx, true);
+ }
+
+ if (bones[p_bone_idx].rest_bone_forward_axis == BONE_AXIS_X_FORWARD) {
+ return_basis.rotate_local(Vector3(0, 1, 0), (Math_PI / 2.0));
+ } else if (bones[p_bone_idx].rest_bone_forward_axis == BONE_AXIS_NEGATIVE_X_FORWARD) {
+ return_basis.rotate_local(Vector3(0, 1, 0), -(Math_PI / 2.0));
+ } else if (bones[p_bone_idx].rest_bone_forward_axis == BONE_AXIS_Y_FORWARD) {
+ return_basis.rotate_local(Vector3(1, 0, 0), -(Math_PI / 2.0));
+ } else if (bones[p_bone_idx].rest_bone_forward_axis == BONE_AXIS_NEGATIVE_Y_FORWARD) {
+ return_basis.rotate_local(Vector3(1, 0, 0), (Math_PI / 2.0));
+ } else if (bones[p_bone_idx].rest_bone_forward_axis == BONE_AXIS_Z_FORWARD) {
+ // Do nothing!
+ } else if (bones[p_bone_idx].rest_bone_forward_axis == BONE_AXIS_NEGATIVE_Z_FORWARD) {
+ return_basis.rotate_local(Vector3(0, 0, 1), Math_PI);
+ }
+
+ return return_basis;
+}
+
+// Modifications
+
+#ifndef _3D_DISABLED
+
+void Skeleton3D::set_modification_stack(Ref<SkeletonModificationStack3D> p_stack) {
+ if (modification_stack.is_valid()) {
+ modification_stack->is_setup = false;
+ modification_stack->set_skeleton(nullptr);
+ }
+
+ modification_stack = p_stack;
+ if (modification_stack.is_valid()) {
+ modification_stack->set_skeleton(this);
+ modification_stack->setup();
+ }
+}
+Ref<SkeletonModificationStack3D> Skeleton3D::get_modification_stack() {
+ return modification_stack;
+}
+
+void Skeleton3D::execute_modifications(real_t p_delta, int p_execution_mode) {
+ if (!modification_stack.is_valid()) {
+ return;
+ }
+
+ // Needed to avoid the issue where the stack looses reference to the skeleton when the scene is saved.
+ if (modification_stack->skeleton != this) {
+ modification_stack->set_skeleton(this);
+ }
+
+ modification_stack->execute(p_delta, p_execution_mode);
+}
+
+#endif // _3D_DISABLED
+
void Skeleton3D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("get_bone_process_orders"), &Skeleton3D::get_bone_process_orders);
ClassDB::bind_method(D_METHOD("add_bone", "name"), &Skeleton3D::add_bone);
ClassDB::bind_method(D_METHOD("find_bone", "name"), &Skeleton3D::find_bone);
ClassDB::bind_method(D_METHOD("get_bone_name", "bone_idx"), &Skeleton3D::get_bone_name);
@@ -862,6 +1143,13 @@ void Skeleton3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("unparent_bone_and_rest", "bone_idx"), &Skeleton3D::unparent_bone_and_rest);
+ ClassDB::bind_method(D_METHOD("get_bone_children", "bone_idx"), &Skeleton3D::get_bone_children);
+ ClassDB::bind_method(D_METHOD("set_bone_children", "bone_idx", "bone_children"), &Skeleton3D::set_bone_children);
+ ClassDB::bind_method(D_METHOD("add_bone_child", "bone_idx", "child_bone_idx"), &Skeleton3D::add_bone_child);
+ ClassDB::bind_method(D_METHOD("remove_bone_child", "bone_idx", "child_bone_idx"), &Skeleton3D::remove_bone_child);
+
+ ClassDB::bind_method(D_METHOD("get_parentless_bones"), &Skeleton3D::get_parentless_bones);
+
ClassDB::bind_method(D_METHOD("get_bone_rest", "bone_idx"), &Skeleton3D::get_bone_rest);
ClassDB::bind_method(D_METHOD("set_bone_rest", "bone_idx", "rest"), &Skeleton3D::set_bone_rest);
@@ -879,14 +1167,26 @@ void Skeleton3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("clear_bones_global_pose_override"), &Skeleton3D::clear_bones_global_pose_override);
ClassDB::bind_method(D_METHOD("set_bone_global_pose_override", "bone_idx", "pose", "amount", "persistent"), &Skeleton3D::set_bone_global_pose_override, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("get_bone_global_pose_override", "bone_idx"), &Skeleton3D::get_bone_global_pose_override);
ClassDB::bind_method(D_METHOD("get_bone_global_pose", "bone_idx"), &Skeleton3D::get_bone_global_pose);
ClassDB::bind_method(D_METHOD("get_bone_global_pose_no_override", "bone_idx"), &Skeleton3D::get_bone_global_pose_no_override);
+ ClassDB::bind_method(D_METHOD("clear_bones_local_pose_override"), &Skeleton3D::clear_bones_local_pose_override);
+ ClassDB::bind_method(D_METHOD("set_bone_local_pose_override", "bone_idx", "pose", "amount", "persistent"), &Skeleton3D::set_bone_local_pose_override, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("get_bone_local_pose_override", "bone_idx"), &Skeleton3D::get_bone_local_pose_override);
+
ClassDB::bind_method(D_METHOD("get_bone_custom_pose", "bone_idx"), &Skeleton3D::get_bone_custom_pose);
ClassDB::bind_method(D_METHOD("set_bone_custom_pose", "bone_idx", "custom_pose"), &Skeleton3D::set_bone_custom_pose);
- ClassDB::bind_method(D_METHOD("bone_transform_to_world_transform", "bone_transform"), &Skeleton3D::bone_transform_to_world_transform);
- ClassDB::bind_method(D_METHOD("world_transform_to_bone_transform", "world_transform"), &Skeleton3D::world_transform_to_bone_transform);
+ ClassDB::bind_method(D_METHOD("force_update_all_bone_transforms"), &Skeleton3D::force_update_all_bone_transforms);
+ ClassDB::bind_method(D_METHOD("force_update_bone_child_transform", "bone_idx"), &Skeleton3D::force_update_bone_children_transforms);
+
+ // Helper functions
+ ClassDB::bind_method(D_METHOD("global_pose_to_world_transform", "global_pose"), &Skeleton3D::global_pose_to_world_transform);
+ ClassDB::bind_method(D_METHOD("world_transform_to_global_pose", "world_transform"), &Skeleton3D::world_transform_to_global_pose);
+ ClassDB::bind_method(D_METHOD("global_pose_to_local_pose", "bone_idx", "global_pose"), &Skeleton3D::global_pose_to_local_pose);
+ ClassDB::bind_method(D_METHOD("local_pose_to_global_pose", "bone_idx", "local_pose"), &Skeleton3D::local_pose_to_global_pose);
+ ClassDB::bind_method(D_METHOD("global_pose_z_forward_to_bone_forward", "bone_idx", "basis"), &Skeleton3D::global_pose_z_forward_to_bone_forward);
ClassDB::bind_method(D_METHOD("set_animate_physical_bones"), &Skeleton3D::set_animate_physical_bones);
ClassDB::bind_method(D_METHOD("get_animate_physical_bones"), &Skeleton3D::get_animate_physical_bones);
@@ -896,12 +1196,21 @@ void Skeleton3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("physical_bones_add_collision_exception", "exception"), &Skeleton3D::physical_bones_add_collision_exception);
ClassDB::bind_method(D_METHOD("physical_bones_remove_collision_exception", "exception"), &Skeleton3D::physical_bones_remove_collision_exception);
+ // Modifications
+ ClassDB::bind_method(D_METHOD("set_modification_stack", "modification_stack"), &Skeleton3D::set_modification_stack);
+ ClassDB::bind_method(D_METHOD("get_modification_stack"), &Skeleton3D::get_modification_stack);
+ ClassDB::bind_method(D_METHOD("execute_modifications", "delta", "execution_mode"), &Skeleton3D::execute_modifications);
+
+#ifndef _3D_DISABLED
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "animate_physical_bones"), "set_animate_physical_bones", "get_animate_physical_bones");
+#endif // _3D_DISABLED
#ifdef TOOLS_ENABLED
ADD_SIGNAL(MethodInfo("pose_updated"));
#endif // TOOLS_ENABLED
+ ADD_SIGNAL(MethodInfo("bone_pose_changed", PropertyInfo(Variant::INT, "bone_idx")));
+
BIND_CONSTANT(NOTIFICATION_UPDATE_SKELETON);
}
diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h
index bccad97d5f..c8a19db813 100644
--- a/scene/3d/skeleton_3d.h
+++ b/scene/3d/skeleton_3d.h
@@ -32,6 +32,7 @@
#define SKELETON_3D_H
#include "scene/3d/node_3d.h"
+#include "scene/resources/skeleton_modification_3d.h"
#include "scene/resources/skin.h"
typedef int BoneId;
@@ -61,6 +62,8 @@ public:
~SkinReference();
};
+class SkeletonModificationStack3D;
+
class Skeleton3D : public Node3D {
GDCLASS(Skeleton3D, Node3D);
@@ -70,9 +73,8 @@ private:
struct Bone {
String name;
- bool enabled = true;
- int parent = -1;
- int sort_index = 0; //used for re-sorting process order
+ bool enabled;
+ int parent;
bool disable_rest = false;
Transform3D rest;
@@ -91,7 +93,35 @@ private:
PhysicalBone3D *physical_bone = nullptr;
PhysicalBone3D *cache_parent_physical_bone = nullptr;
- List<ObjectID> nodes_bound;
+ real_t local_pose_override_amount;
+ bool local_pose_override_reset;
+ Transform3D local_pose_override;
+
+ Vector<int> child_bones;
+
+ // The forward direction vector and rest bone forward axis are cached because they do not change
+ // 99% of the time, but recalculating them can be expensive on models with many bones.
+ Vector3 rest_bone_forward_vector;
+ int rest_bone_forward_axis = -1;
+
+ Bone() {
+ parent = -1;
+ enabled = true;
+ disable_rest = false;
+ custom_pose_enable = false;
+ global_pose_override_amount = 0;
+ global_pose_override_reset = false;
+#ifndef _3D_DISABLED
+ physical_bone = nullptr;
+ cache_parent_physical_bone = nullptr;
+#endif // _3D_DISABLED
+ local_pose_override_amount = 0;
+ local_pose_override_reset = false;
+ child_bones = Vector<int>();
+
+ rest_bone_forward_vector = Vector3(0, 0, 0);
+ rest_bone_forward_axis = -1;
+ }
};
Set<SkinReference *> skin_bindings;
@@ -100,8 +130,9 @@ private:
bool animate_physical_bones = true;
Vector<Bone> bones;
- Vector<int> process_order;
- bool process_order_dirty = true;
+ bool process_order_dirty;
+
+ Vector<int> parentless_bones;
void _make_dirty();
bool dirty = false;
@@ -117,7 +148,20 @@ protected:
void _notification(int p_what);
static void _bind_methods();
+#ifndef _3D_DISABLED
+ Ref<SkeletonModificationStack3D> modification_stack;
+#endif // _3D_DISABLED
+
public:
+ enum Bone_Forward_Axis {
+ BONE_AXIS_X_FORWARD = 0,
+ BONE_AXIS_Y_FORWARD = 1,
+ BONE_AXIS_Z_FORWARD = 2,
+ BONE_AXIS_NEGATIVE_X_FORWARD = 3,
+ BONE_AXIS_NEGATIVE_Y_FORWARD = 4,
+ BONE_AXIS_NEGATIVE_Z_FORWARD = 5,
+ };
+
enum {
NOTIFICATION_UPDATE_SKELETON = 50
};
@@ -135,6 +179,12 @@ public:
void unparent_bone_and_rest(int p_bone);
+ Vector<int> get_bone_children(int p_bone);
+ void set_bone_children(int p_bone, Vector<int> p_children);
+ void add_bone_child(int p_bone, int p_child);
+ void remove_bone_child(int p_bone, int p_child);
+ Vector<int> get_parentless_bones();
+
void set_bone_disable_rest(int p_bone, bool p_disable);
bool is_bone_rest_disabled(int p_bone) const;
@@ -145,16 +195,8 @@ public:
Transform3D get_bone_global_pose(int p_bone) const;
Transform3D get_bone_global_pose_no_override(int p_bone) const;
- void clear_bones_global_pose_override();
- void set_bone_global_pose_override(int p_bone, const Transform3D &p_pose, real_t p_amount, bool p_persistent = false);
-
void set_bone_enabled(int p_bone, bool p_enabled);
bool is_bone_enabled(int p_bone) const;
-
- void bind_child_node_to_bone(int p_bone, Node *p_node);
- void unbind_child_node_from_bone(int p_bone, Node *p_node);
- void get_bound_child_nodes_to_bone(int p_bone, List<Node *> *p_bound) const;
-
void clear_bones();
// posing api
@@ -165,15 +207,40 @@ public:
void set_bone_custom_pose(int p_bone, const Transform3D &p_custom_pose);
Transform3D get_bone_custom_pose(int p_bone) const;
+ void clear_bones_global_pose_override();
+ Transform3D get_bone_global_pose_override(int p_bone) const;
+ void set_bone_global_pose_override(int p_bone, const Transform3D &p_pose, real_t p_amount, bool p_persistent = false);
+
+ void clear_bones_local_pose_override();
+ Transform3D get_bone_local_pose_override(int p_bone) const;
+ void set_bone_local_pose_override(int p_bone, const Transform3D &p_pose, real_t p_amount, bool p_persistent = false);
+
void localize_rests(); // used for loaders and tools
- int get_process_order(int p_idx);
- Vector<int> get_bone_process_orders();
Ref<SkinReference> register_skin(const Ref<Skin> &p_skin);
+ void force_update_all_bone_transforms();
+ void force_update_bone_children_transforms(int bone_idx);
+
+ void update_bone_rest_forward_vector(int p_bone, bool p_force_update = false);
+ void update_bone_rest_forward_axis(int p_bone, bool p_force_update = false);
+ Vector3 get_bone_axis_forward_vector(int p_bone);
+ int get_bone_axis_forward_enum(int p_bone);
+
// Helper functions
- Transform3D bone_transform_to_world_transform(Transform3D p_transform);
- Transform3D world_transform_to_bone_transform(Transform3D p_transform);
+ Transform3D global_pose_to_world_transform(Transform3D p_global_pose);
+ Transform3D world_transform_to_global_pose(Transform3D p_transform);
+ Transform3D global_pose_to_local_pose(int p_bone_idx, Transform3D p_global_pose);
+ Transform3D local_pose_to_global_pose(int p_bone_idx, Transform3D p_local_pose);
+
+ Basis global_pose_z_forward_to_bone_forward(int p_bone_idx, Basis p_basis);
+
+ // Modifications
+#ifndef _3D_DISABLED
+ Ref<SkeletonModificationStack3D> get_modification_stack();
+ void set_modification_stack(Ref<SkeletonModificationStack3D> p_stack);
+ void execute_modifications(real_t p_delta, int p_execution_mode);
+#endif // _3D_DISABLED
// Physical bone API
diff --git a/scene/SCsub b/scene/SCsub
index ccd2bab8ff..92288211bb 100644
--- a/scene/SCsub
+++ b/scene/SCsub
@@ -10,7 +10,8 @@ env.add_source_files(env.scene_sources, "*.cpp")
# Chain load SCsubs
SConscript("main/SCsub")
SConscript("gui/SCsub")
-SConscript("3d/SCsub")
+if not env["disable_3d"]:
+ SConscript("3d/SCsub")
SConscript("2d/SCsub")
SConscript("animation/SCsub")
SConscript("audio/SCsub")
diff --git a/scene/debugger/scene_debugger.cpp b/scene/debugger/scene_debugger.cpp
index 869f2f68f7..df1aece80a 100644
--- a/scene/debugger/scene_debugger.cpp
+++ b/scene/debugger/scene_debugger.cpp
@@ -90,7 +90,7 @@ Error SceneDebugger::parse_message(void *p_user, const String &p_msg, const Arra
ERR_FAIL_COND_V(p_args.size() < 1, ERR_INVALID_DATA);
Transform2D transform = p_args[1];
scene_tree->get_root()->set_canvas_transform_override(transform);
-
+#ifndef _3D_DISABLED
} else if (p_msg == "override_camera_3D:set") {
ERR_FAIL_COND_V(p_args.size() < 1, ERR_INVALID_DATA);
bool enable = p_args[0];
@@ -109,7 +109,7 @@ Error SceneDebugger::parse_message(void *p_user, const String &p_msg, const Arra
scene_tree->get_root()->set_camera_3d_override_orthogonal(size_or_fov, near, far);
}
scene_tree->get_root()->set_camera_3d_override_transform(transform);
-
+#endif // _3D_DISABLED
} else if (p_msg == "set_object_property") {
ERR_FAIL_COND_V(p_args.size() < 3, ERR_INVALID_DATA);
_set_object_property(p_args[0], p_args[1], p_args[2]);
diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp
index 871ad889ca..d252a4507c 100644
--- a/scene/gui/base_button.cpp
+++ b/scene/gui/base_button.cpp
@@ -145,10 +145,11 @@ void BaseButton::on_action_event(Ref<InputEvent> p_event) {
if (status.press_attempt && status.pressing_inside) {
if (toggle_mode) {
+ bool is_pressed = p_event->is_pressed();
if (Object::cast_to<InputEventShortcut>(*p_event)) {
- action_mode = ACTION_MODE_BUTTON_PRESS; // HACK.
+ is_pressed = false;
}
- if ((p_event->is_pressed() && action_mode == ACTION_MODE_BUTTON_PRESS) || (!p_event->is_pressed() && action_mode == ACTION_MODE_BUTTON_RELEASE)) {
+ if ((is_pressed && action_mode == ACTION_MODE_BUTTON_PRESS) || (!is_pressed && action_mode == ACTION_MODE_BUTTON_RELEASE)) {
if (action_mode == ACTION_MODE_BUTTON_PRESS) {
status.press_attempt = false;
status.pressing_inside = false;
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index 68e9171c15..2b3be1d5c2 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -577,8 +577,8 @@ void LineEdit::_notification(int p_what) {
#ifdef TOOLS_ENABLED
case NOTIFICATION_ENTER_TREE: {
if (Engine::get_singleton()->is_editor_hint() && !get_tree()->is_node_being_edited(this)) {
- set_caret_blink_enabled(EDITOR_DEF("text_editor/cursor/caret_blink", false));
- set_caret_blink_speed(EDITOR_DEF("text_editor/cursor/caret_blink_speed", 0.65));
+ set_caret_blink_enabled(EDITOR_DEF("text_editor/appearance/caret/caret_blink", false));
+ set_caret_blink_speed(EDITOR_DEF("text_editor/appearance/caret/caret_blink_speed", 0.65));
if (!EditorSettings::get_singleton()->is_connected("settings_changed", callable_mp(this, &LineEdit::_editor_settings_changed))) {
EditorSettings::get_singleton()->connect("settings_changed", callable_mp(this, &LineEdit::_editor_settings_changed));
@@ -1824,8 +1824,8 @@ PopupMenu *LineEdit::get_menu() const {
void LineEdit::_editor_settings_changed() {
#ifdef TOOLS_ENABLED
- set_caret_blink_enabled(EDITOR_DEF("text_editor/cursor/caret_blink", false));
- set_caret_blink_speed(EDITOR_DEF("text_editor/cursor/caret_blink_speed", 0.65));
+ set_caret_blink_enabled(EDITOR_DEF("text_editor/appearance/caret/caret_blink", false));
+ set_caret_blink_speed(EDITOR_DEF("text_editor/appearance/caret/caret_blink_speed", 0.65));
#endif
}
diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp
index 63b5793b3e..dc6c7fec28 100644
--- a/scene/gui/menu_button.cpp
+++ b/scene/gui/menu_button.cpp
@@ -86,6 +86,7 @@ void MenuButton::_popup_visibility_changed(bool p_visible) {
}
void MenuButton::pressed() {
+ emit_signal(SNAME("about_to_popup"));
Size2 size = get_size();
Point2 gp = get_screen_position();
diff --git a/scene/gui/shortcut.cpp b/scene/gui/shortcut.cpp
index 1c29870682..c0aac6d91a 100644
--- a/scene/gui/shortcut.cpp
+++ b/scene/gui/shortcut.cpp
@@ -43,7 +43,7 @@ Ref<InputEvent> Shortcut::get_event() const {
bool Shortcut::matches_event(const Ref<InputEvent> &p_event) const {
Ref<InputEventShortcut> ies = p_event;
- if (ies != nullptr) {
+ if (ies.is_valid()) {
if (ies->get_shortcut().ptr() == this) {
return true;
}
diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp
index cc41d961f6..53a2fe4d93 100644
--- a/scene/gui/tab_container.cpp
+++ b/scene/gui/tab_container.cpp
@@ -535,7 +535,7 @@ void TabContainer::_notification(int p_what) {
}
void TabContainer::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_index, float p_x) {
- Vector<Control *> tabs = _get_tabs();
+ Control *control = get_tab_control(p_index);
RID canvas = get_canvas_item();
Ref<Font> font = get_theme_font(SNAME("font"));
Color font_outline_color = get_theme_color(SNAME("font_outline_color"));
@@ -549,7 +549,6 @@ void TabContainer::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, in
p_tab_style->draw(canvas, tab_rect);
// Draw the tab contents.
- Control *control = Object::cast_to<Control>(tabs[p_index]);
String text = control->has_meta("_tab_name") ? String(atr(String(control->get_meta("_tab_name")))) : String(atr(control->get_name()));
int x_content = tab_rect.position.x + p_tab_style->get_margin(SIDE_LEFT);
@@ -620,10 +619,10 @@ void TabContainer::_repaint() {
if (tabs_visible) {
c->set_offset(SIDE_TOP, _get_top_margin());
}
- c->set_offset(Side(SIDE_TOP), c->get_offset(Side(SIDE_TOP)) + sb->get_margin(Side(SIDE_TOP)));
- c->set_offset(Side(SIDE_LEFT), c->get_offset(Side(SIDE_LEFT)) + sb->get_margin(Side(SIDE_LEFT)));
- c->set_offset(Side(SIDE_RIGHT), c->get_offset(Side(SIDE_RIGHT)) - sb->get_margin(Side(SIDE_RIGHT)));
- c->set_offset(Side(SIDE_BOTTOM), c->get_offset(Side(SIDE_BOTTOM)) - sb->get_margin(Side(SIDE_BOTTOM)));
+ c->set_offset(SIDE_TOP, c->get_offset(SIDE_TOP) + sb->get_margin(SIDE_TOP));
+ c->set_offset(SIDE_LEFT, c->get_offset(SIDE_LEFT) + sb->get_margin(SIDE_LEFT));
+ c->set_offset(SIDE_RIGHT, c->get_offset(SIDE_RIGHT) - sb->get_margin(SIDE_RIGHT));
+ c->set_offset(SIDE_BOTTOM, c->get_offset(SIDE_BOTTOM) - sb->get_margin(SIDE_BOTTOM));
} else {
c->hide();
@@ -682,7 +681,7 @@ Vector<Control *> TabContainer::_get_tabs() const {
Vector<Control *> controls;
for (int i = 0; i < get_child_count(); i++) {
Control *control = Object::cast_to<Control>(get_child(i));
- if (!control || control->is_top_level_control()) {
+ if (!control || control->is_set_as_top_level()) {
continue;
}
@@ -700,10 +699,7 @@ void TabContainer::add_child_notify(Node *p_child) {
Container::add_child_notify(p_child);
Control *c = Object::cast_to<Control>(p_child);
- if (!c) {
- return;
- }
- if (c->is_set_as_top_level()) {
+ if (!c || c->is_set_as_top_level()) {
return;
}
@@ -726,10 +722,10 @@ void TabContainer::add_child_notify(Node *p_child) {
c->set_offset(SIDE_TOP, _get_top_margin());
}
Ref<StyleBox> sb = get_theme_stylebox(SNAME("panel"));
- c->set_offset(Side(SIDE_TOP), c->get_offset(Side(SIDE_TOP)) + sb->get_margin(Side(SIDE_TOP)));
- c->set_offset(Side(SIDE_LEFT), c->get_offset(Side(SIDE_LEFT)) + sb->get_margin(Side(SIDE_LEFT)));
- c->set_offset(Side(SIDE_RIGHT), c->get_offset(Side(SIDE_RIGHT)) - sb->get_margin(Side(SIDE_RIGHT)));
- c->set_offset(Side(SIDE_BOTTOM), c->get_offset(Side(SIDE_BOTTOM)) - sb->get_margin(Side(SIDE_BOTTOM)));
+ c->set_offset(SIDE_TOP, c->get_offset(SIDE_TOP) + sb->get_margin(SIDE_TOP));
+ c->set_offset(SIDE_LEFT, c->get_offset(SIDE_LEFT) + sb->get_margin(SIDE_LEFT));
+ c->set_offset(SIDE_RIGHT, c->get_offset(SIDE_RIGHT) - sb->get_margin(SIDE_RIGHT));
+ c->set_offset(SIDE_BOTTOM, c->get_offset(SIDE_BOTTOM) - sb->get_margin(SIDE_BOTTOM));
update();
p_child->connect("renamed", callable_mp(this, &TabContainer::_child_renamed_callback));
if (first && is_inside_tree()) {
@@ -739,8 +735,13 @@ void TabContainer::add_child_notify(Node *p_child) {
void TabContainer::move_child_notify(Node *p_child) {
Container::move_child_notify(p_child);
- call_deferred(SNAME("_update_current_tab"));
- _refresh_texts();
+
+ Control *c = Object::cast_to<Control>(p_child);
+ if (!c || c->is_set_as_top_level()) {
+ return;
+ }
+
+ _update_current_tab();
update();
}
@@ -785,21 +786,18 @@ Control *TabContainer::get_tab_control(int p_idx) const {
}
Control *TabContainer::get_current_tab_control() const {
- Vector<Control *> tabs = _get_tabs();
- if (current >= 0 && current < tabs.size()) {
- return tabs[current];
- } else {
- return nullptr;
- }
+ return get_tab_control(current);
}
void TabContainer::remove_child_notify(Node *p_child) {
Container::remove_child_notify(p_child);
- if (!Object::cast_to<Control>(p_child)) {
+ Control *c = Object::cast_to<Control>(p_child);
+ if (!c || c->is_set_as_top_level()) {
return;
}
+ // Defer the call because tab is not yet removed (remove_child_notify is called right before p_child is actually removed).
call_deferred(SNAME("_update_current_tab"));
p_child->disconnect("renamed", callable_mp(this, &TabContainer::_child_renamed_callback));
@@ -808,10 +806,9 @@ void TabContainer::remove_child_notify(Node *p_child) {
}
void TabContainer::_update_current_tab() {
- Vector<Control *> tabs = _get_tabs();
_refresh_texts();
- int tc = tabs.size();
+ int tc = get_tab_count();
if (current >= tc) {
current = tc - 1;
}
@@ -1019,12 +1016,8 @@ bool TabContainer::is_all_tabs_in_front() const {
return all_tabs_in_front;
}
-Control *TabContainer::_get_tab(int p_idx) const {
- return get_tab_control(p_idx);
-}
-
void TabContainer::set_tab_title(int p_tab, const String &p_title) {
- Control *child = _get_tab(p_tab);
+ Control *child = get_tab_control(p_tab);
ERR_FAIL_COND(!child);
child->set_meta("_tab_name", p_title);
_refresh_texts();
@@ -1032,7 +1025,7 @@ void TabContainer::set_tab_title(int p_tab, const String &p_title) {
}
String TabContainer::get_tab_title(int p_tab) const {
- Control *child = _get_tab(p_tab);
+ Control *child = get_tab_control(p_tab);
ERR_FAIL_COND_V(!child, "");
if (child->has_meta("_tab_name")) {
return child->get_meta("_tab_name");
@@ -1042,14 +1035,14 @@ String TabContainer::get_tab_title(int p_tab) const {
}
void TabContainer::set_tab_icon(int p_tab, const Ref<Texture2D> &p_icon) {
- Control *child = _get_tab(p_tab);
+ Control *child = get_tab_control(p_tab);
ERR_FAIL_COND(!child);
child->set_meta("_tab_icon", p_icon);
update();
}
Ref<Texture2D> TabContainer::get_tab_icon(int p_tab) const {
- Control *child = _get_tab(p_tab);
+ Control *child = get_tab_control(p_tab);
ERR_FAIL_COND_V(!child, Ref<Texture2D>());
if (child->has_meta("_tab_icon")) {
return child->get_meta("_tab_icon");
@@ -1059,14 +1052,14 @@ Ref<Texture2D> TabContainer::get_tab_icon(int p_tab) const {
}
void TabContainer::set_tab_disabled(int p_tab, bool p_disabled) {
- Control *child = _get_tab(p_tab);
+ Control *child = get_tab_control(p_tab);
ERR_FAIL_COND(!child);
child->set_meta("_tab_disabled", p_disabled);
update();
}
bool TabContainer::get_tab_disabled(int p_tab) const {
- Control *child = _get_tab(p_tab);
+ Control *child = get_tab_control(p_tab);
ERR_FAIL_COND_V(!child, false);
if (child->has_meta("_tab_disabled")) {
return child->get_meta("_tab_disabled");
@@ -1076,7 +1069,7 @@ bool TabContainer::get_tab_disabled(int p_tab) const {
}
void TabContainer::set_tab_hidden(int p_tab, bool p_hidden) {
- Control *child = _get_tab(p_tab);
+ Control *child = get_tab_control(p_tab);
ERR_FAIL_COND(!child);
child->set_meta("_tab_hidden", p_hidden);
update();
@@ -1095,7 +1088,7 @@ void TabContainer::set_tab_hidden(int p_tab, bool p_hidden) {
}
bool TabContainer::get_tab_hidden(int p_tab) const {
- Control *child = _get_tab(p_tab);
+ Control *child = get_tab_control(p_tab);
ERR_FAIL_COND_V(!child, false);
if (child->has_meta("_tab_hidden")) {
return child->get_meta("_tab_hidden");
diff --git a/scene/gui/tab_container.h b/scene/gui/tab_container.h
index 4ed5255729..35f18eff8e 100644
--- a/scene/gui/tab_container.h
+++ b/scene/gui/tab_container.h
@@ -57,7 +57,6 @@ private:
bool menu_hovered = false;
int highlight_arrow = -1;
TabAlign align = ALIGN_CENTER;
- Control *_get_tab(int p_idx) const;
int _get_top_margin() const;
mutable ObjectID popup_obj_id;
bool drag_to_rearrange_enabled = false;
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 745920fcdd..12f0c9e89a 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -4332,7 +4332,9 @@ bool TextEdit::is_highlight_all_occurrences_enabled() const {
void TextEdit::set_draw_control_chars(bool p_draw_control_chars) {
if (draw_control_chars != p_draw_control_chars) {
draw_control_chars = p_draw_control_chars;
- menu->set_item_checked(menu->get_item_index(MENU_DISPLAY_UCC), draw_control_chars);
+ if (menu) {
+ menu->set_item_checked(menu->get_item_index(MENU_DISPLAY_UCC), draw_control_chars);
+ }
text.set_draw_control_chars(draw_control_chars);
text.invalidate_all();
update();
@@ -4904,6 +4906,11 @@ void TextEdit::_backspace() {
return;
}
+ if (has_selection()) {
+ delete_selection();
+ return;
+ }
+
int cc = get_caret_column();
int cl = get_caret_line();
@@ -4911,11 +4918,6 @@ void TextEdit::_backspace() {
return;
}
- if (has_selection()) {
- delete_selection();
- return;
- }
-
int prev_line = cc ? cl : cl - 1;
int prev_column = cc ? (cc - 1) : (text[cl - 1].length());
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index a76b6c91ee..5b707498a7 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -499,7 +499,7 @@ bool SceneTree::process(double p_time) {
_call_idle_callbacks();
#ifdef TOOLS_ENABLED
-
+#ifndef _3D_DISABLED
if (Engine::get_singleton()->is_editor_hint()) {
//simple hack to reload fallback environment if it changed from editor
String env_path = ProjectSettings::get_singleton()->get(SNAME("rendering/environment/defaults/default_environment"));
@@ -522,8 +522,8 @@ bool SceneTree::process(double p_time) {
get_root()->get_world_3d()->set_fallback_environment(fallback);
}
}
-
-#endif
+#endif // _3D_DISABLED
+#endif // TOOLS_ENABLED
return _quit;
}
@@ -1333,15 +1333,16 @@ SceneTree::SceneTree() {
root = memnew(Window);
root->set_name("root");
+#ifndef _3D_DISABLED
if (!root->get_world_3d().is_valid()) {
root->set_world_3d(Ref<World3D>(memnew(World3D)));
}
+ root->set_as_audio_listener_3d(true);
+#endif // _3D_DISABLED
// Initialize network state.
set_multiplayer(Ref<MultiplayerAPI>(memnew(MultiplayerAPI)));
- //root->set_world_2d( Ref<World2D>( memnew( World2D )));
- root->set_as_audio_listener_3d(true);
root->set_as_audio_listener_2d(true);
current_scene = nullptr;
@@ -1397,6 +1398,7 @@ SceneTree::SceneTree() {
ProjectSettings::get_singleton()->set_custom_property_info("rendering/2d/sdf/oversize", PropertyInfo(Variant::INT, "rendering/2d/sdf/oversize", PROPERTY_HINT_ENUM, "100%,120%,150%,200%"));
ProjectSettings::get_singleton()->set_custom_property_info("rendering/2d/sdf/scale", PropertyInfo(Variant::INT, "rendering/2d/sdf/scale", PROPERTY_HINT_ENUM, "100%,50%,25%"));
+#ifndef _3D_DISABLED
{ // Load default fallback environment.
// Get possible extensions.
List<String> exts;
@@ -1428,6 +1430,7 @@ SceneTree::SceneTree() {
}
}
}
+#endif // _3D_DISABLED
root->set_physics_object_picking(GLOBAL_DEF("physics/common/enable_object_picking", true));
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index b85f1cc0b8..78fa0985a9 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -36,10 +36,12 @@
#include "core/templates/pair.h"
#include "scene/2d/camera_2d.h"
#include "scene/2d/collision_object_2d.h"
+#ifndef _3D_DISABLED
#include "scene/3d/camera_3d.h"
#include "scene/3d/collision_object_3d.h"
#include "scene/3d/listener_3d.h"
#include "scene/3d/world_environment.h"
+#endif // _3D_DISABLED
#include "scene/gui/control.h"
#include "scene/gui/label.h"
#include "scene/gui/popup.h"
@@ -377,14 +379,17 @@ void Viewport::_notification(int p_what) {
current_canvas = find_world_2d()->get_canvas();
RenderingServer::get_singleton()->viewport_attach_canvas(viewport, current_canvas);
_update_listener_2d();
+#ifndef _3D_DISABLED
RenderingServer::get_singleton()->viewport_set_scenario(viewport, find_world_3d()->get_scenario());
_update_listener_3d();
+#endif // _3D_DISABLED
add_to_group("_viewports");
if (get_tree()->is_debugging_collisions_hint()) {
PhysicsServer2D::get_singleton()->space_set_debug_contacts(find_world_2d()->get_space(), get_tree()->get_collision_debug_contact_count());
contact_2d_debug = RenderingServer::get_singleton()->canvas_item_create();
RenderingServer::get_singleton()->canvas_item_set_parent(contact_2d_debug, find_world_2d()->get_canvas());
+#ifndef _3D_DISABLED
PhysicsServer3D::get_singleton()->space_set_debug_contacts(find_world_3d()->get_space(), get_tree()->get_collision_debug_contact_count());
contact_3d_debug_multimesh = RenderingServer::get_singleton()->multimesh_create();
RenderingServer::get_singleton()->multimesh_allocate_data(contact_3d_debug_multimesh, get_tree()->get_collision_debug_contact_count(), RS::MULTIMESH_TRANSFORM_3D, true);
@@ -394,10 +399,12 @@ void Viewport::_notification(int p_what) {
RenderingServer::get_singleton()->instance_set_base(contact_3d_debug_instance, contact_3d_debug_multimesh);
RenderingServer::get_singleton()->instance_set_scenario(contact_3d_debug_instance, find_world_3d()->get_scenario());
//RenderingServer::get_singleton()->instance_geometry_set_flag(contact_3d_debug_instance, RS::INSTANCE_FLAG_VISIBLE_IN_ALL_ROOMS, true);
+#endif // _3D_DISABLED
}
} break;
case NOTIFICATION_READY: {
+#ifndef _3D_DISABLED
if (listener_3d_set.size() && !listener_3d) {
Listener3D *first = nullptr;
for (Set<Listener3D *>::Element *E = listener_3d_set.front(); E; E = E->next()) {
@@ -424,7 +431,7 @@ void Viewport::_notification(int p_what) {
first->make_current();
}
}
-
+#endif // _3D_DISABLED
} break;
case NOTIFICATION_EXIT_TREE: {
_gui_cancel_tooltip();
@@ -462,6 +469,7 @@ void Viewport::_notification(int p_what) {
RenderingServer::get_singleton()->canvas_item_add_rect(contact_2d_debug, Rect2(points[i] - Vector2(2, 2), Vector2(5, 5)), ccol);
}
}
+#ifndef _3D_DISABLED
if (get_tree()->is_debugging_collisions_hint() && contact_3d_debug_multimesh.is_valid()) {
Vector<Vector3> points = PhysicsServer3D::get_singleton()->space_get_contacts(find_world_3d()->get_space());
int point_count = PhysicsServer3D::get_singleton()->space_get_contact_count(find_world_3d()->get_space());
@@ -474,6 +482,7 @@ void Viewport::_notification(int p_what) {
RS::get_singleton()->multimesh_instance_set_transform(contact_3d_debug_multimesh, i, point_transform);
}
}
+#endif // _3D_DISABLED
} break;
case NOTIFICATION_WM_MOUSE_EXIT: {
_drop_physics_mouseover();
@@ -506,10 +515,6 @@ void Viewport::_process_picking() {
_drop_physics_mouseover(true);
- Vector2 last_pos(1e20, 1e20);
- CollisionObject3D *last_object = nullptr;
- ObjectID last_id;
- PhysicsDirectSpaceState3D::RayResult result;
PhysicsDirectSpaceState2D *ss2d = PhysicsServer2D::get_singleton()->space_get_direct_state(find_world_2d()->get_space());
if (physics_has_last_mousepos) {
@@ -669,6 +674,11 @@ void Viewport::_process_picking() {
}
}
+#ifndef _3D_DISABLED
+ Vector2 last_pos(1e20, 1e20);
+ CollisionObject3D *last_object = nullptr;
+ ObjectID last_id;
+ PhysicsDirectSpaceState3D::RayResult result;
bool captured = false;
if (physics_object_capture.is_valid()) {
@@ -741,6 +751,7 @@ void Viewport::_process_picking() {
last_pos = pos;
}
}
+#endif // _3D_DISABLED
}
}
@@ -2204,6 +2215,7 @@ void Viewport::_drop_physics_mouseover(bool p_paused_only) {
_cleanup_mouseover_colliders(true, p_paused_only);
+#ifndef _3D_DISABLED
if (physics_object_over.is_valid()) {
CollisionObject3D *co = Object::cast_to<CollisionObject3D>(ObjectDB::get_instance(physics_object_over));
if (co) {
@@ -2214,6 +2226,7 @@ void Viewport::_drop_physics_mouseover(bool p_paused_only) {
}
}
}
+#endif // _3D_DISABLED
}
void Viewport::_cleanup_mouseover_colliders(bool p_clean_all_frames, bool p_paused_only, uint64_t p_frame_reference) {
@@ -3063,6 +3076,7 @@ Viewport::SDFScale Viewport::get_sdf_scale() const {
return sdf_scale;
}
+#ifndef _3D_DISABLED
Listener3D *Viewport::get_listener_3d() const {
return listener_3d;
}
@@ -3460,6 +3474,7 @@ void Viewport::set_use_xr(bool p_use_xr) {
bool Viewport::is_using_xr() {
return use_xr;
}
+#endif // _3D_DISABLED
void Viewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_world_2d", "world_2d"), &Viewport::set_world_2d);
@@ -3565,6 +3580,7 @@ void Viewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("_process_picking"), &Viewport::_process_picking);
+#ifndef _3D_DISABLED
ClassDB::bind_method(D_METHOD("set_world_3d", "world_3d"), &Viewport::set_world_3d);
ClassDB::bind_method(D_METHOD("get_world_3d"), &Viewport::get_world_3d);
ClassDB::bind_method(D_METHOD("find_world_3d"), &Viewport::find_world_3d);
@@ -3587,6 +3603,7 @@ void Viewport::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "audio_listener_enable_3d"), "set_as_audio_listener_3d", "is_audio_listener_3d");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "own_world_3d"), "set_use_own_world_3d", "is_using_own_world_3d");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "world_3d", PROPERTY_HINT_RESOURCE_TYPE, "World3D"), "set_world_3d", "get_world_3d");
+#endif // _3D_DISABLED
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "world_2d", PROPERTY_HINT_RESOURCE_TYPE, "World2D", PROPERTY_USAGE_NONE), "set_world_2d", "get_world_2d");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "transparent_bg"), "set_transparent_background", "has_transparent_background");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "handle_input_locally"), "set_handle_input_locally", "is_handling_input_locally");
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index 87cfef2e05..9c51f404d7 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -34,10 +34,12 @@
#include "scene/main/node.h"
#include "scene/resources/texture.h"
+#ifndef _3D_DISABLED
class Camera3D;
class CollisionObject3D;
class Listener3D;
class World3D;
+#endif // _3D_DISABLED
class Camera2D;
class CanvasItem;
@@ -573,6 +575,7 @@ public:
void pass_mouse_focus_to(Viewport *p_viewport, Control *p_control);
+#ifndef _3D_DISABLED
bool use_xr = false;
friend class Listener3D;
Listener3D *listener_3d = nullptr;
@@ -644,6 +647,7 @@ public:
void set_use_xr(bool p_use_xr);
bool is_using_xr();
+#endif // _3D_DISABLED
Viewport();
~Viewport();
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index 6995c77b8e..73eec73d75 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -300,7 +300,7 @@ void Window::_propagate_window_notification(Node *p_node, int p_notification) {
Node *child = p_node->get_child(i);
Window *window = Object::cast_to<Window>(child);
if (window) {
- break;
+ continue;
}
_propagate_window_notification(child, p_notification);
}
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 0c83c57de6..40ab439a51 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -148,7 +148,6 @@
#include "scene/resources/gradient.h"
#include "scene/resources/height_map_shape_3d.h"
#include "scene/resources/immediate_mesh.h"
-#include "scene/resources/line_shape_2d.h"
#include "scene/resources/material.h"
#include "scene/resources/mesh.h"
#include "scene/resources/mesh_data_tool.h"
@@ -169,7 +168,15 @@
#include "scene/resources/skeleton_modification_2d_physicalbones.h"
#include "scene/resources/skeleton_modification_2d_stackholder.h"
#include "scene/resources/skeleton_modification_2d_twoboneik.h"
+#include "scene/resources/skeleton_modification_3d.h"
+#include "scene/resources/skeleton_modification_3d_ccdik.h"
+#include "scene/resources/skeleton_modification_3d_fabrik.h"
+#include "scene/resources/skeleton_modification_3d_jiggle.h"
+#include "scene/resources/skeleton_modification_3d_lookat.h"
+#include "scene/resources/skeleton_modification_3d_stackholder.h"
+#include "scene/resources/skeleton_modification_3d_twoboneik.h"
#include "scene/resources/skeleton_modification_stack_2d.h"
+#include "scene/resources/skeleton_modification_stack_3d.h"
#include "scene/resources/sky.h"
#include "scene/resources/sky_material.h"
#include "scene/resources/sphere_shape_3d.h"
@@ -187,6 +194,7 @@
#include "scene/resources/visual_shader_sdf_nodes.h"
#include "scene/resources/world_2d.h"
#include "scene/resources/world_3d.h"
+#include "scene/resources/world_margin_shape_2d.h"
#include "scene/resources/world_margin_shape_3d.h"
#include "scene/scene_string_names.h"
@@ -754,6 +762,15 @@ void register_scene_types() {
GDREGISTER_CLASS(ConvexPolygonShape3D);
GDREGISTER_CLASS(ConcavePolygonShape3D);
+ ClassDB::register_class<SkeletonModificationStack3D>();
+ ClassDB::register_class<SkeletonModification3D>();
+ ClassDB::register_class<SkeletonModification3DLookAt>();
+ ClassDB::register_class<SkeletonModification3DCCDIK>();
+ ClassDB::register_class<SkeletonModification3DFABRIK>();
+ ClassDB::register_class<SkeletonModification3DJiggle>();
+ ClassDB::register_class<SkeletonModification3DTwoBoneIK>();
+ ClassDB::register_class<SkeletonModification3DStackHolder>();
+
OS::get_singleton()->yield(); //may take time to init
GDREGISTER_CLASS(VelocityTracker3D);
@@ -823,7 +840,7 @@ void register_scene_types() {
OS::get_singleton()->yield(); //may take time to init
GDREGISTER_VIRTUAL_CLASS(Shape2D);
- GDREGISTER_CLASS(LineShape2D);
+ GDREGISTER_CLASS(WorldMarginShape2D);
GDREGISTER_CLASS(SegmentShape2D);
GDREGISTER_CLASS(CircleShape2D);
GDREGISTER_CLASS(RectangleShape2D);
@@ -910,6 +927,7 @@ void register_scene_types() {
ClassDB::add_compatibility_class("KinematicBody2D", "CharacterBody2D");
ClassDB::add_compatibility_class("KinematicCollision", "KinematicCollision3D");
ClassDB::add_compatibility_class("Light", "Light3D");
+ ClassDB::add_compatibility_class("LineShape2D", "WorldMarginShape2D");
ClassDB::add_compatibility_class("Listener", "Listener3D");
ClassDB::add_compatibility_class("MeshInstance", "MeshInstance3D");
ClassDB::add_compatibility_class("MultiMeshInstance", "MultiMeshInstance3D");
diff --git a/scene/resources/canvas_item_material.cpp b/scene/resources/canvas_item_material.cpp
index 7ba57a6532..7501efea9e 100644
--- a/scene/resources/canvas_item_material.cpp
+++ b/scene/resources/canvas_item_material.cpp
@@ -30,6 +30,8 @@
#include "canvas_item_material.h"
+#include "core/version.h"
+
Mutex CanvasItemMaterial::material_mutex;
SelfList<CanvasItemMaterial>::List *CanvasItemMaterial::dirty_materials = nullptr;
Map<CanvasItemMaterial::MaterialKey, CanvasItemMaterial::ShaderData> CanvasItemMaterial::shader_map;
@@ -78,7 +80,10 @@ void CanvasItemMaterial::_update_shader() {
//must create a shader!
- String code = "shader_type canvas_item;\nrender_mode ";
+ // Add a comment to describe the shader origin (useful when converting to ShaderMaterial).
+ String code = "// NOTE: Shader automatically converted from " VERSION_NAME " " VERSION_FULL_CONFIG "'s CanvasItemMaterial.\n\n";
+
+ code += "shader_type canvas_item;\nrender_mode ";
switch (blend_mode) {
case BLEND_MODE_MIX:
code += "blend_mix";
diff --git a/scene/resources/default_theme/icon_grid_layout.png b/scene/resources/default_theme/icon_grid_layout.png
index 00a6179d5e..a249252a79 100644
--- a/scene/resources/default_theme/icon_grid_layout.png
+++ b/scene/resources/default_theme/icon_grid_layout.png
Binary files differ
diff --git a/scene/resources/default_theme/theme_data.h b/scene/resources/default_theme/theme_data.h
index 865ee86c76..4a53926066 100644
--- a/scene/resources/default_theme/theme_data.h
+++ b/scene/resources/default_theme/theme_data.h
@@ -178,11 +178,11 @@ static const unsigned char icon_folder_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x2e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x6, 0x78, 0x70, 0xf4, 0xc1, 0x7f, 0x24, 0x78, 0x18, 0x53, 0xc1, 0x7f, 0x54, 0x48, 0x50, 0xc1, 0x43, 0x1b, 0xbc, 0xa, 0x50, 0xad, 0x23, 0xa4, 0xe0, 0xff, 0x70, 0x52, 0x70, 0x18, 0x97, 0xf4, 0xfd, 0x43, 0xd4, 0x88, 0x4a, 0x0, 0x5a, 0xcb, 0x18, 0xab, 0x5e, 0xd9, 0x1a, 0x53, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
-static const unsigned char icon_grid_minimap_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xe, 0xc3, 0x0, 0x0, 0xe, 0xc3, 0x1, 0xc7, 0x6f, 0xa8, 0x64, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x0, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x6e, 0x6b, 0x73, 0x63, 0x61, 0x70, 0x65, 0x2e, 0x6f, 0x72, 0x67, 0x9b, 0xee, 0x3c, 0x1a, 0x0, 0x0, 0x2, 0xd, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0x75, 0x93, 0x31, 0x68, 0x14, 0x51, 0x10, 0x86, 0xbf, 0xd9, 0xd, 0xbb, 0xde, 0x76, 0x82, 0x21, 0xf8, 0xe0, 0xbc, 0x5d, 0x8b, 0x80, 0x69, 0x6c, 0xd2, 0x5a, 0x6a, 0x91, 0xc3, 0xd2, 0x46, 0x22, 0x8, 0x9, 0x89, 0x70, 0x85, 0x10, 0x41, 0xd, 0x24, 0x45, 0xb0, 0xb, 0x68, 0x11, 0x14, 0x24, 0x10, 0x22, 0x62, 0x21, 0x41, 0xe, 0x4b, 0x21, 0xa4, 0xb7, 0x49, 0x17, 0xb1, 0x8, 0xb9, 0xdd, 0xc7, 0x86, 0x33, 0x21, 0xe1, 0x3a, 0x8f, 0x64, 0x61, 0x6f, 0x2c, 0xbc, 0x3b, 0x36, 0xb9, 0xdc, 0xc0, 0x2b, 0xde, 0xcc, 0xfc, 0xf3, 0xff, 0xfc, 0xcc, 0x48, 0xa3, 0xd1, 0x78, 0x20, 0x22, 0x13, 0xbe, 0xef, 0xaf, 0xdf, 0xac, 0xd7, 0x1f, 0xe1, 0x38, 0xd3, 0xa8, 0x2a, 0xf0, 0x45, 0x6a, 0xb5, 0xcf, 0x5c, 0x11, 0xcd, 0x66, 0x33, 0x38, 0x3f, 0x3f, 0x9f, 0x13, 0x91, 0x7d, 0xb1, 0xd6, 0x6e, 0xaa, 0xea, 0xd3, 0xe0, 0xe8, 0xe8, 0xde, 0xe8, 0xee, 0xee, 0x37, 0xc0, 0xe9, 0xf6, 0x75, 0xf0, 0xfd, 0x9, 0x99, 0x9d, 0x6d, 0x15, 0x81, 0x59, 0x96, 0x3d, 0x3, 0x5e, 0x2, 0x63, 0x22, 0xf2, 0x69, 0xa4, 0x57, 0x1c, 0xdd, 0xdb, 0xfb, 0x5b, 0x0, 0x3, 0x38, 0x67, 0x41, 0x30, 0x11, 0xc7, 0xf1, 0x13, 0x0, 0x11, 0x71, 0xb2, 0x2c, 0x7b, 0xd8, 0xad, 0xad, 0x2, 0x6f, 0xb9, 0x0, 0x38, 0x3c, 0xfc, 0x5, 0x9c, 0xf6, 0xff, 0x22, 0x27, 0x27, 0xe3, 0xe3, 0x7f, 0xa, 0x3, 0x67, 0x45, 0xe4, 0xbb, 0xe7, 0x79, 0xb7, 0xc3, 0x30, 0x7c, 0xd7, 0x67, 0xe9, 0xe3, 0x67, 0x66, 0x5c, 0x60, 0x1, 0x50, 0x40, 0x51, 0x7d, 0x71, 0x6b, 0x72, 0xf2, 0x20, 0x8a, 0xa2, 0xf9, 0x28, 0x8a, 0xe6, 0x1, 0x3a, 0x9d, 0xce, 0x4f, 0x63, 0x4c, 0x3b, 0x4d, 0xd3, 0xd2, 0xc0, 0x80, 0x3c, 0xcf, 0xf, 0x92, 0xa9, 0xa9, 0x31, 0x60, 0x5, 0x58, 0x91, 0x5a, 0xed, 0xc7, 0x15, 0xfe, 0x95, 0xac, 0xb5, 0xcf, 0xf3, 0x3c, 0x3f, 0xe8, 0x25, 0x46, 0xa, 0xc5, 0xd, 0x11, 0x59, 0xb3, 0xd5, 0xea, 0x1b, 0xa0, 0x95, 0x54, 0xab, 0x5b, 0x97, 0xd1, 0x22, 0xb2, 0xa6, 0xaa, 0x6d, 0x60, 0xd, 0x58, 0xba, 0xa0, 0x20, 0xc, 0xc3, 0x65, 0xd7, 0x75, 0x23, 0xe0, 0x2e, 0xb0, 0x1, 0x5c, 0xbf, 0xf4, 0x0, 0xbe, 0xba, 0xae, 0x1b, 0x85, 0x61, 0xb8, 0x3c, 0xa0, 0x20, 0x4d, 0xd3, 0x52, 0xb9, 0x5c, 0x6e, 0xc5, 0x71, 0xbc, 0x23, 0x22, 0xd3, 0x61, 0x18, 0xde, 0x2f, 0xb2, 0x27, 0x49, 0xa2, 0xaa, 0xba, 0x53, 0x2e, 0x97, 0x5b, 0x69, 0x9a, 0x96, 0xf2, 0x3c, 0x1f, 0xf0, 0xc0, 0x5a, 0x6b, 0x5f, 0x1, 0x25, 0x86, 0x84, 0xe3, 0x38, 0x9e, 0xb5, 0x76, 0x2e, 0xcf, 0xf3, 0xfd, 0x1, 0x5, 0x22, 0xb2, 0xa1, 0xaa, 0x4b, 0x22, 0x72, 0xad, 0xcb, 0x38, 0xe0, 0x81, 0xaa, 0x7e, 0x0, 0xce, 0x44, 0xe4, 0xbd, 0xaa, 0xbe, 0xbe, 0xa0, 0xa0, 0x52, 0xa9, 0x2c, 0x7a, 0x9e, 0x17, 0x1, 0x3d, 0xe0, 0x55, 0x1e, 0x6c, 0x79, 0x9e, 0x17, 0x55, 0x2a, 0x95, 0xc5, 0x1, 0x5, 0xcd, 0x66, 0x33, 0x30, 0xc6, 0x9c, 0xc6, 0x71, 0xbc, 0x2d, 0x22, 0x8f, 0x87, 0x78, 0xb0, 0x6d, 0x8c, 0x39, 0xed, 0xae, 0x74, 0xdf, 0x83, 0x3a, 0x70, 0x9c, 0x65, 0x59, 0x23, 0x49, 0x92, 0x5, 0x11, 0x9, 0x86, 0x79, 0x20, 0x22, 0x41, 0x92, 0x24, 0xb, 0x59, 0x96, 0x35, 0x80, 0x63, 0xa0, 0x2e, 0x3d, 0xf6, 0xc2, 0x91, 0xdc, 0x0, 0x5c, 0x55, 0x5d, 0xbf, 0x4, 0x9e, 0x3, 0x72, 0xfe, 0xaf, 0xfb, 0xaa, 0xe7, 0x79, 0x1f, 0x8d, 0x31, 0x6d, 0x29, 0x36, 0xf5, 0xce, 0x14, 0xb8, 0x33, 0x44, 0xc4, 0x6f, 0xdf, 0xf7, 0xd7, 0x8d, 0x31, 0xed, 0x5e, 0xe2, 0x1f, 0xb, 0x5c, 0xe2, 0xcb, 0xd, 0x9b, 0x69, 0xcb, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+static const unsigned char icon_grid_layout_png[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x5, 0x52, 0x69, 0x54, 0x58, 0x74, 0x58, 0x4d, 0x4c, 0x3a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x78, 0x6d, 0x70, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x3f, 0x78, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x3d, 0x22, 0xef, 0xbb, 0xbf, 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x57, 0x35, 0x4d, 0x30, 0x4d, 0x70, 0x43, 0x65, 0x68, 0x69, 0x48, 0x7a, 0x72, 0x65, 0x53, 0x7a, 0x4e, 0x54, 0x63, 0x7a, 0x6b, 0x63, 0x39, 0x64, 0x22, 0x3f, 0x3e, 0xa, 0x3c, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x3d, 0x22, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x3a, 0x6e, 0x73, 0x3a, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x22, 0x20, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x74, 0x6b, 0x3d, 0x22, 0x58, 0x4d, 0x50, 0x20, 0x43, 0x6f, 0x72, 0x65, 0x20, 0x35, 0x2e, 0x35, 0x2e, 0x30, 0x22, 0x3e, 0xa, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x72, 0x64, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x31, 0x39, 0x39, 0x39, 0x2f, 0x30, 0x32, 0x2f, 0x32, 0x32, 0x2d, 0x72, 0x64, 0x66, 0x2d, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x2d, 0x6e, 0x73, 0x23, 0x22, 0x3e, 0xa, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x64, 0x66, 0x3a, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x22, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x64, 0x63, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x70, 0x75, 0x72, 0x6c, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x64, 0x63, 0x2f, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x31, 0x2e, 0x31, 0x2f, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x65, 0x78, 0x69, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x74, 0x69, 0x66, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6d, 0x70, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x6d, 0x6d, 0x2f, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x73, 0x74, 0x45, 0x76, 0x74, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x73, 0x54, 0x79, 0x70, 0x65, 0x2f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x23, 0x22, 0xa, 0x20, 0x20, 0x20, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x58, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x22, 0x31, 0x36, 0x22, 0xa, 0x20, 0x20, 0x20, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x59, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x22, 0x31, 0x36, 0x22, 0xa, 0x20, 0x20, 0x20, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x53, 0x70, 0x61, 0x63, 0x65, 0x3d, 0x22, 0x31, 0x22, 0xa, 0x20, 0x20, 0x20, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x3d, 0x22, 0x31, 0x36, 0x22, 0xa, 0x20, 0x20, 0x20, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3d, 0x22, 0x31, 0x36, 0x22, 0xa, 0x20, 0x20, 0x20, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x69, 0x74, 0x3d, 0x22, 0x32, 0x22, 0xa, 0x20, 0x20, 0x20, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x58, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3d, 0x22, 0x37, 0x32, 0x2e, 0x30, 0x22, 0xa, 0x20, 0x20, 0x20, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x59, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3d, 0x22, 0x37, 0x32, 0x2e, 0x30, 0x22, 0xa, 0x20, 0x20, 0x20, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x3a, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x3d, 0x22, 0x33, 0x22, 0xa, 0x20, 0x20, 0x20, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x3a, 0x49, 0x43, 0x43, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x3d, 0x22, 0x73, 0x52, 0x47, 0x42, 0x20, 0x49, 0x45, 0x43, 0x36, 0x31, 0x39, 0x36, 0x36, 0x2d, 0x32, 0x2e, 0x31, 0x22, 0xa, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x70, 0x3a, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x44, 0x61, 0x74, 0x65, 0x3d, 0x22, 0x32, 0x30, 0x32, 0x31, 0x2d, 0x30, 0x38, 0x2d, 0x31, 0x35, 0x54, 0x30, 0x39, 0x3a, 0x34, 0x31, 0x3a, 0x34, 0x30, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0x22, 0xa, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x70, 0x3a, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x44, 0x61, 0x74, 0x65, 0x3d, 0x22, 0x32, 0x30, 0x32, 0x31, 0x2d, 0x30, 0x38, 0x2d, 0x31, 0x35, 0x54, 0x30, 0x39, 0x3a, 0x34, 0x31, 0x3a, 0x34, 0x30, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0x22, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x3c, 0x64, 0x63, 0x3a, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x41, 0x6c, 0x74, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x6c, 0x69, 0x20, 0x78, 0x6d, 0x6c, 0x3a, 0x6c, 0x61, 0x6e, 0x67, 0x3d, 0x22, 0x78, 0x2d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x22, 0x3e, 0x47, 0x72, 0x69, 0x64, 0x4c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x6c, 0x69, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x41, 0x6c, 0x74, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x64, 0x63, 0x3a, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x53, 0x65, 0x71, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x6c, 0x69, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x45, 0x76, 0x74, 0x3a, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3d, 0x22, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x64, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x45, 0x76, 0x74, 0x3a, 0x73, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x41, 0x66, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x20, 0x44, 0x65, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x20, 0x31, 0x2e, 0x39, 0x2e, 0x31, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x45, 0x76, 0x74, 0x3a, 0x77, 0x68, 0x65, 0x6e, 0x3d, 0x22, 0x32, 0x30, 0x32, 0x31, 0x2d, 0x30, 0x38, 0x2d, 0x31, 0x35, 0x54, 0x30, 0x39, 0x3a, 0x34, 0x31, 0x3a, 0x34, 0x30, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0x22, 0x2f, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x53, 0x65, 0x71, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x3e, 0xa, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0xa, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x3e, 0xa, 0x3c, 0x2f, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x3e, 0xa, 0x3c, 0x3f, 0x78, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x65, 0x6e, 0x64, 0x3d, 0x22, 0x72, 0x22, 0x3f, 0x3e, 0x10, 0xfa, 0x51, 0xae, 0x0, 0x0, 0x1, 0x82, 0x69, 0x43, 0x43, 0x50, 0x73, 0x52, 0x47, 0x42, 0x20, 0x49, 0x45, 0x43, 0x36, 0x31, 0x39, 0x36, 0x36, 0x2d, 0x32, 0x2e, 0x31, 0x0, 0x0, 0x28, 0x91, 0x75, 0x91, 0xbf, 0x4b, 0x42, 0x51, 0x14, 0xc7, 0x3f, 0x5a, 0xa1, 0x94, 0x61, 0x50, 0x43, 0x43, 0x83, 0x84, 0x45, 0x83, 0x85, 0x15, 0x84, 0x2d, 0xd, 0x4a, 0xbf, 0xa0, 0x1a, 0xd4, 0x20, 0xab, 0x45, 0x9f, 0xbf, 0x2, 0xb5, 0xc7, 0x7b, 0x4a, 0x48, 0x6b, 0xd0, 0x2a, 0x14, 0x44, 0x2d, 0xfd, 0x1a, 0xea, 0x2f, 0xa8, 0x35, 0x68, 0xe, 0x82, 0xa2, 0x8, 0xa2, 0x2d, 0x68, 0x2e, 0x6a, 0xa9, 0x78, 0x9d, 0xa7, 0x82, 0x11, 0x79, 0x2e, 0xe7, 0x9e, 0xcf, 0xfd, 0xde, 0x7b, 0xe, 0xf7, 0x9e, 0xb, 0xd6, 0x70, 0x46, 0xc9, 0xea, 0x8d, 0x5e, 0xc8, 0xe6, 0xf2, 0x5a, 0x70, 0xd2, 0xef, 0x5a, 0x88, 0x2c, 0xba, 0x6c, 0xcf, 0xd8, 0x71, 0x62, 0xa3, 0xf, 0x5f, 0x54, 0xd1, 0xd5, 0xd9, 0xd0, 0x44, 0x98, 0xba, 0xf6, 0x71, 0x87, 0xc5, 0x8c, 0x37, 0xfd, 0x66, 0xad, 0xfa, 0xe7, 0xfe, 0xb5, 0x96, 0x78, 0x42, 0x57, 0xc0, 0x62, 0x17, 0x1e, 0x53, 0x54, 0x2d, 0x2f, 0x3c, 0x25, 0x3c, 0xb3, 0x96, 0x57, 0x4d, 0xde, 0x16, 0xee, 0x50, 0xd2, 0xd1, 0xb8, 0xf0, 0xa9, 0xb0, 0x47, 0x93, 0xb, 0xa, 0xdf, 0x9a, 0x7a, 0xac, 0xc2, 0x2f, 0x26, 0xa7, 0x2a, 0xfc, 0x65, 0xb2, 0x16, 0xe, 0x6, 0xc0, 0xda, 0x26, 0xec, 0x4a, 0xfd, 0xe2, 0xd8, 0x2f, 0x56, 0xd2, 0x5a, 0x56, 0x58, 0x5e, 0x8e, 0x3b, 0x9b, 0x29, 0x28, 0xd5, 0xfb, 0x98, 0x2f, 0x71, 0x24, 0x72, 0xf3, 0x21, 0x89, 0xdd, 0xe2, 0x5d, 0xe8, 0x4, 0x99, 0xc4, 0x8f, 0x8b, 0x69, 0xc6, 0x9, 0x30, 0xc2, 0x20, 0xa3, 0x32, 0x8f, 0xd0, 0xcf, 0x10, 0x3, 0xb2, 0xa2, 0x4e, 0xbe, 0xb7, 0x9c, 0x3f, 0xc7, 0xaa, 0xe4, 0x2a, 0x32, 0xab, 0x14, 0xd1, 0x58, 0x21, 0x45, 0x9a, 0x3c, 0x1e, 0x51, 0xb, 0x52, 0x3d, 0x21, 0x31, 0x29, 0x7a, 0x42, 0x46, 0x86, 0xa2, 0xd9, 0xff, 0xbf, 0x7d, 0xd5, 0x93, 0xc3, 0x43, 0x95, 0xea, 0xe, 0x3f, 0x34, 0x3d, 0x19, 0xc6, 0x5b, 0xf, 0xd8, 0xb6, 0xe0, 0xbb, 0x64, 0x18, 0x9f, 0x87, 0x86, 0xf1, 0x7d, 0x4, 0xd, 0x8f, 0x70, 0x91, 0xab, 0xe5, 0xaf, 0x1e, 0x80, 0xef, 0x5d, 0xf4, 0x52, 0x4d, 0x73, 0xef, 0x83, 0x73, 0x3, 0xce, 0x2e, 0x6b, 0x5a, 0x6c, 0x7, 0xce, 0x37, 0xa1, 0xf3, 0x41, 0x8d, 0x6a, 0xd1, 0xb2, 0xd4, 0x20, 0x6e, 0x4d, 0x26, 0xe1, 0xf5, 0x4, 0x5a, 0x23, 0xd0, 0x7e, 0xd, 0xcd, 0x4b, 0x95, 0x9e, 0x55, 0xf7, 0x39, 0xbe, 0x87, 0xf0, 0xba, 0x7c, 0xd5, 0x15, 0xec, 0xee, 0x41, 0xaf, 0x9c, 0x77, 0x2e, 0xff, 0x0, 0xa6, 0xc4, 0x68, 0x3, 0x1f, 0xd7, 0x32, 0xd8, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x1, 0x40, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0x9d, 0xd2, 0xbd, 0x4a, 0x5d, 0x41, 0x14, 0x86, 0xe1, 0xe7, 0x84, 0xa3, 0x51, 0x22, 0x82, 0x41, 0x8c, 0x12, 0x2, 0xeb, 0x1a, 0x2c, 0x84, 0x4, 0x52, 0x88, 0xb1, 0xd2, 0x26, 0x76, 0x62, 0x67, 0xeb, 0xd, 0x78, 0x19, 0x56, 0x69, 0x82, 0x62, 0x67, 0x25, 0x82, 0x60, 0x97, 0xc2, 0x52, 0x8, 0x24, 0x95, 0x16, 0xa9, 0x6, 0x8c, 0x18, 0xb5, 0x30, 0x88, 0xa7, 0x90, 0x88, 0x5a, 0xec, 0x39, 0x66, 0xd8, 0x7a, 0x54, 0x5c, 0xb0, 0xd8, 0xcc, 0xbb, 0xe7, 0xfb, 0x58, 0x3f, 0xd3, 0x50, 0x8b, 0x94, 0xd2, 0x6b, 0xcc, 0x60, 0x25, 0x22, 0xae, 0xea, 0xff, 0xeb, 0xd1, 0xbc, 0x47, 0xfc, 0xd, 0xbd, 0x58, 0x7e, 0x4c, 0xc, 0x8d, 0x42, 0x3c, 0x82, 0x9f, 0xe8, 0xc1, 0x26, 0xce, 0x3b, 0x68, 0xae, 0xf1, 0x25, 0x22, 0x76, 0xeb, 0x15, 0xf4, 0xa3, 0xf, 0x97, 0x18, 0xce, 0xdf, 0x4e, 0x6, 0xfd, 0x77, 0x2a, 0xc8, 0x55, 0x4, 0xb6, 0xf1, 0x1b, 0x1f, 0x23, 0xe2, 0xfa, 0x49, 0x2d, 0xa4, 0x94, 0x7a, 0x30, 0x98, 0xd9, 0x5b, 0x4c, 0x63, 0x49, 0x35, 0xb, 0x68, 0xe1, 0x2f, 0x46, 0xf0, 0x22, 0xb3, 0x93, 0x88, 0xb8, 0x68, 0x1f, 0x36, 0xb0, 0x9f, 0x73, 0x7, 0x87, 0xf8, 0x53, 0xb0, 0x23, 0x2c, 0xe0, 0xa0, 0x60, 0xeb, 0xa, 0xb7, 0x1, 0xac, 0x60, 0x34, 0x5f, 0x1a, 0x52, 0xcd, 0x67, 0x16, 0x73, 0xe8, 0xca, 0x6c, 0x3f, 0xdf, 0x59, 0xcd, 0x9a, 0x5b, 0x83, 0x67, 0x47, 0x7b, 0xb, 0xa7, 0x98, 0xcf, 0x9, 0xc7, 0xaa, 0x2d, 0xac, 0xe5, 0xf3, 0xbf, 0xcc, 0xde, 0xe1, 0x47, 0x66, 0x5b, 0xa5, 0xc1, 0x67, 0xff, 0x87, 0x78, 0xa5, 0x9a, 0xc1, 0x1a, 0x5e, 0x65, 0xd6, 0xc2, 0x24, 0x3e, 0xe4, 0x36, 0xe0, 0x84, 0xda, 0x1a, 0x1f, 0x8a, 0x94, 0xd2, 0x6, 0xc6, 0x30, 0x1e, 0x11, 0xbf, 0xda, 0xbc, 0x7c, 0x89, 0xc3, 0x58, 0x44, 0x77, 0x7, 0x8f, 0x2e, 0x4c, 0xa9, 0x1e, 0xd1, 0xa7, 0x88, 0xd8, 0x29, 0x5b, 0xa0, 0x7a, 0xc2, 0xf1, 0x80, 0x41, 0x23, 0xe7, 0x4b, 0xbc, 0x79, 0x6a, 0xe5, 0x65, 0xb, 0x5f, 0x53, 0x4a, 0x67, 0x29, 0xa5, 0xf7, 0x25, 0x6f, 0x76, 0x12, 0xdc, 0x13, 0x7b, 0x98, 0x88, 0x88, 0xef, 0x25, 0xbc, 0x1, 0x6c, 0x4d, 0x56, 0x9e, 0x2a, 0x4e, 0x48, 0xae, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
-static const unsigned char icon_grid_layout_png[] = {
+static const unsigned char icon_grid_minimap_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xe, 0xc3, 0x0, 0x0, 0xe, 0xc3, 0x1, 0xc7, 0x6f, 0xa8, 0x64, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x0, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x6e, 0x6b, 0x73, 0x63, 0x61, 0x70, 0x65, 0x2e, 0x6f, 0x72, 0x67, 0x9b, 0xee, 0x3c, 0x1a, 0x0, 0x0, 0x2, 0xd, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0x75, 0x93, 0x31, 0x68, 0x14, 0x51, 0x10, 0x86, 0xbf, 0xd9, 0xd, 0xbb, 0xde, 0x76, 0x82, 0x21, 0xf8, 0xe0, 0xbc, 0x5d, 0x8b, 0x80, 0x69, 0x6c, 0xd2, 0x5a, 0x6a, 0x91, 0xc3, 0xd2, 0x46, 0x22, 0x8, 0x9, 0x89, 0x70, 0x85, 0x10, 0x41, 0xd, 0x24, 0x45, 0xb0, 0xb, 0x68, 0x11, 0x14, 0x24, 0x10, 0x22, 0x62, 0x21, 0x41, 0xe, 0x4b, 0x21, 0xa4, 0xb7, 0x49, 0x17, 0xb1, 0x8, 0xb9, 0xdd, 0xc7, 0x86, 0x33, 0x21, 0xe1, 0x3a, 0x8f, 0x64, 0x61, 0x6f, 0x2c, 0xbc, 0x3b, 0x36, 0xb9, 0xdc, 0xc0, 0x2b, 0xde, 0xcc, 0xfc, 0xf3, 0xff, 0xfc, 0xcc, 0x48, 0xa3, 0xd1, 0x78, 0x20, 0x22, 0x13, 0xbe, 0xef, 0xaf, 0xdf, 0xac, 0xd7, 0x1f, 0xe1, 0x38, 0xd3, 0xa8, 0x2a, 0xf0, 0x45, 0x6a, 0xb5, 0xcf, 0x5c, 0x11, 0xcd, 0x66, 0x33, 0x38, 0x3f, 0x3f, 0x9f, 0x13, 0x91, 0x7d, 0xb1, 0xd6, 0x6e, 0xaa, 0xea, 0xd3, 0xe0, 0xe8, 0xe8, 0xde, 0xe8, 0xee, 0xee, 0x37, 0xc0, 0xe9, 0xf6, 0x75, 0xf0, 0xfd, 0x9, 0x99, 0x9d, 0x6d, 0x15, 0x81, 0x59, 0x96, 0x3d, 0x3, 0x5e, 0x2, 0x63, 0x22, 0xf2, 0x69, 0xa4, 0x57, 0x1c, 0xdd, 0xdb, 0xfb, 0x5b, 0x0, 0x3, 0x38, 0x67, 0x41, 0x30, 0x11, 0xc7, 0xf1, 0x13, 0x0, 0x11, 0x71, 0xb2, 0x2c, 0x7b, 0xd8, 0xad, 0xad, 0x2, 0x6f, 0xb9, 0x0, 0x38, 0x3c, 0xfc, 0x5, 0x9c, 0xf6, 0xff, 0x22, 0x27, 0x27, 0xe3, 0xe3, 0x7f, 0xa, 0x3, 0x67, 0x45, 0xe4, 0xbb, 0xe7, 0x79, 0xb7, 0xc3, 0x30, 0x7c, 0xd7, 0x67, 0xe9, 0xe3, 0x67, 0x66, 0x5c, 0x60, 0x1, 0x50, 0x40, 0x51, 0x7d, 0x71, 0x6b, 0x72, 0xf2, 0x20, 0x8a, 0xa2, 0xf9, 0x28, 0x8a, 0xe6, 0x1, 0x3a, 0x9d, 0xce, 0x4f, 0x63, 0x4c, 0x3b, 0x4d, 0xd3, 0xd2, 0xc0, 0x80, 0x3c, 0xcf, 0xf, 0x92, 0xa9, 0xa9, 0x31, 0x60, 0x5, 0x58, 0x91, 0x5a, 0xed, 0xc7, 0x15, 0xfe, 0x95, 0xac, 0xb5, 0xcf, 0xf3, 0x3c, 0x3f, 0xe8, 0x25, 0x46, 0xa, 0xc5, 0xd, 0x11, 0x59, 0xb3, 0xd5, 0xea, 0x1b, 0xa0, 0x95, 0x54, 0xab, 0x5b, 0x97, 0xd1, 0x22, 0xb2, 0xa6, 0xaa, 0x6d, 0x60, 0xd, 0x58, 0xba, 0xa0, 0x20, 0xc, 0xc3, 0x65, 0xd7, 0x75, 0x23, 0xe0, 0x2e, 0xb0, 0x1, 0x5c, 0xbf, 0xf4, 0x0, 0xbe, 0xba, 0xae, 0x1b, 0x85, 0x61, 0xb8, 0x3c, 0xa0, 0x20, 0x4d, 0xd3, 0x52, 0xb9, 0x5c, 0x6e, 0xc5, 0x71, 0xbc, 0x23, 0x22, 0xd3, 0x61, 0x18, 0xde, 0x2f, 0xb2, 0x27, 0x49, 0xa2, 0xaa, 0xba, 0x53, 0x2e, 0x97, 0x5b, 0x69, 0x9a, 0x96, 0xf2, 0x3c, 0x1f, 0xf0, 0xc0, 0x5a, 0x6b, 0x5f, 0x1, 0x25, 0x86, 0x84, 0xe3, 0x38, 0x9e, 0xb5, 0x76, 0x2e, 0xcf, 0xf3, 0xfd, 0x1, 0x5, 0x22, 0xb2, 0xa1, 0xaa, 0x4b, 0x22, 0x72, 0xad, 0xcb, 0x38, 0xe0, 0x81, 0xaa, 0x7e, 0x0, 0xce, 0x44, 0xe4, 0xbd, 0xaa, 0xbe, 0xbe, 0xa0, 0xa0, 0x52, 0xa9, 0x2c, 0x7a, 0x9e, 0x17, 0x1, 0x3d, 0xe0, 0x55, 0x1e, 0x6c, 0x79, 0x9e, 0x17, 0x55, 0x2a, 0x95, 0xc5, 0x1, 0x5, 0xcd, 0x66, 0x33, 0x30, 0xc6, 0x9c, 0xc6, 0x71, 0xbc, 0x2d, 0x22, 0x8f, 0x87, 0x78, 0xb0, 0x6d, 0x8c, 0x39, 0xed, 0xae, 0x74, 0xdf, 0x83, 0x3a, 0x70, 0x9c, 0x65, 0x59, 0x23, 0x49, 0x92, 0x5, 0x11, 0x9, 0x86, 0x79, 0x20, 0x22, 0x41, 0x92, 0x24, 0xb, 0x59, 0x96, 0x35, 0x80, 0x63, 0xa0, 0x2e, 0x3d, 0xf6, 0xc2, 0x91, 0xdc, 0x0, 0x5c, 0x55, 0x5d, 0xbf, 0x4, 0x9e, 0x3, 0x72, 0xfe, 0xaf, 0xfb, 0xaa, 0xe7, 0x79, 0x1f, 0x8d, 0x31, 0x6d, 0x29, 0x36, 0xf5, 0xce, 0x14, 0xb8, 0x33, 0x44, 0xc4, 0x6f, 0xdf, 0xf7, 0xd7, 0x8d, 0x31, 0xed, 0x5e, 0xe2, 0x1f, 0xb, 0x5c, 0xe2, 0xcb, 0xd, 0x9b, 0x69, 0xcb, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index 896a33e02e..1d2a2ef26c 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -31,6 +31,7 @@
#include "material.h"
#include "core/config/engine.h"
+#include "core/version.h"
#ifdef TOOLS_ENABLED
#include "editor/editor_settings.h"
@@ -469,7 +470,12 @@ void BaseMaterial3D::_update_shader() {
//must create a shader!
- String code = "shader_type spatial;\nrender_mode ";
+ // Add a comment to describe the shader origin (useful when converting to ShaderMaterial).
+ String code = vformat(
+ "// NOTE: Shader automatically converted from " VERSION_NAME " " VERSION_FULL_CONFIG "'s %s.\n\n",
+ orm ? "ORMMaterial3D" : "StandardMaterial3D");
+
+ code += "shader_type spatial;\nrender_mode ";
switch (blend_mode) {
case BLEND_MODE_MIX:
code += "blend_mix";
diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp
index eddbb9a842..54bfc427c4 100644
--- a/scene/resources/packed_scene.cpp
+++ b/scene/resources/packed_scene.cpp
@@ -162,12 +162,14 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
}
WARN_PRINT(vformat("Node %s of type %s cannot be created. A placeholder will be created instead.", snames[n.name], snames[n.type]).ascii().get_data());
if (n.parent >= 0 && n.parent < nc && ret_nodes[n.parent]) {
- if (Object::cast_to<Node3D>(ret_nodes[n.parent])) {
- obj = memnew(Node3D);
- } else if (Object::cast_to<Control>(ret_nodes[n.parent])) {
+ if (Object::cast_to<Control>(ret_nodes[n.parent])) {
obj = memnew(Control);
} else if (Object::cast_to<Node2D>(ret_nodes[n.parent])) {
obj = memnew(Node2D);
+#ifndef _3D_DISABLED
+ } else if (Object::cast_to<Node3D>(ret_nodes[n.parent])) {
+ obj = memnew(Node3D);
+#endif // _3D_DISABLED
}
}
diff --git a/scene/resources/particles_material.cpp b/scene/resources/particles_material.cpp
index 91569e65d6..34f4142a14 100644
--- a/scene/resources/particles_material.cpp
+++ b/scene/resources/particles_material.cpp
@@ -30,6 +30,8 @@
#include "particles_material.h"
+#include "core/version.h"
+
Mutex ParticlesMaterial::material_mutex;
SelfList<ParticlesMaterial>::List *ParticlesMaterial::dirty_materials = nullptr;
Map<ParticlesMaterial::MaterialKey, ParticlesMaterial::ShaderData> ParticlesMaterial::shader_map;
@@ -141,7 +143,10 @@ void ParticlesMaterial::_update_shader() {
//must create a shader!
- String code = "shader_type particles;\n";
+ // Add a comment to describe the shader origin (useful when converting to ShaderMaterial).
+ String code = "// NOTE: Shader automatically converted from " VERSION_NAME " " VERSION_FULL_CONFIG "'s ParticlesMaterial.\n\n";
+
+ code += "shader_type particles;\n";
if (collision_scale) {
code += "render_mode collision_use_scale;\n";
diff --git a/scene/resources/skeleton_modification_2d.cpp b/scene/resources/skeleton_modification_2d.cpp
index b52a60006a..2d5b42ddf4 100644
--- a/scene/resources/skeleton_modification_2d.cpp
+++ b/scene/resources/skeleton_modification_2d.cpp
@@ -166,13 +166,13 @@ void SkeletonModification2D::editor_draw_angle_constraints(Bone2D *p_operation_b
if (operation_bone_parent_bone) {
stack->skeleton->draw_set_transform(
- stack->skeleton->get_global_transform().affine_inverse().xform(p_operation_bone->get_global_position()),
+ stack->skeleton->to_local(p_operation_bone->get_global_position()),
operation_bone_parent_bone->get_global_rotation() - stack->skeleton->get_global_rotation());
} else {
- stack->skeleton->draw_set_transform(stack->skeleton->get_global_transform().affine_inverse().xform(p_operation_bone->get_global_position()));
+ stack->skeleton->draw_set_transform(stack->skeleton->to_local(p_operation_bone->get_global_position()));
}
} else {
- stack->skeleton->draw_set_transform(stack->skeleton->get_global_transform().affine_inverse().xform(p_operation_bone->get_global_position()));
+ stack->skeleton->draw_set_transform(stack->skeleton->to_local(p_operation_bone->get_global_position()));
}
if (p_constraint_inverted) {
@@ -186,7 +186,7 @@ void SkeletonModification2D::editor_draw_angle_constraints(Bone2D *p_operation_b
stack->skeleton->draw_line(Vector2(0, 0), Vector2(Math::cos(arc_angle_max), Math::sin(arc_angle_max)) * p_operation_bone->get_length(), bone_ik_color, 1.0);
} else {
- stack->skeleton->draw_set_transform(stack->skeleton->get_global_transform().affine_inverse().xform(p_operation_bone->get_global_position()));
+ stack->skeleton->draw_set_transform(stack->skeleton->to_local(p_operation_bone->get_global_position()));
stack->skeleton->draw_arc(Vector2(0, 0), p_operation_bone->get_length(), 0, Math_PI * 2, 32, bone_ik_color, 1.0);
stack->skeleton->draw_line(Vector2(0, 0), Vector2(1, 0) * p_operation_bone->get_length(), bone_ik_color, 1.0);
}
diff --git a/scene/resources/skeleton_modification_2d_ccdik.cpp b/scene/resources/skeleton_modification_2d_ccdik.cpp
index 7ea60e584e..6eab8d4afe 100644
--- a/scene/resources/skeleton_modification_2d_ccdik.cpp
+++ b/scene/resources/skeleton_modification_2d_ccdik.cpp
@@ -201,18 +201,18 @@ void SkeletonModification2DCCDIK::_execute_ccdik_joint(int p_joint_idx, Node2D *
if (ccdik_data.rotate_from_joint) {
// To rotate from the joint, simply look at the target!
operation_transform.set_rotation(
- operation_transform.looking_at(p_target->get_global_transform().get_origin()).get_rotation() - operation_bone->get_bone_angle());
+ operation_transform.looking_at(p_target->get_global_position()).get_rotation() - operation_bone->get_bone_angle());
} else {
// How to rotate from the tip: get the difference of rotation needed from the tip to the target, from the perspective of the joint.
// Because we are only using the offset, we do not need to account for the bone angle of the Bone2D node.
- float joint_to_tip = operation_transform.get_origin().angle_to_point(p_tip->get_global_transform().get_origin());
- float joint_to_target = operation_transform.get_origin().angle_to_point(p_target->get_global_transform().get_origin());
+ float joint_to_tip = operation_transform.get_origin().angle_to_point(p_tip->get_global_position());
+ float joint_to_target = operation_transform.get_origin().angle_to_point(p_target->get_global_position());
operation_transform.set_rotation(
operation_transform.get_rotation() + (joint_to_target - joint_to_tip));
}
// Reset scale
- operation_transform.set_scale(operation_bone->get_global_transform().get_scale());
+ operation_transform.set_scale(operation_bone->get_global_scale());
// Apply constraints in globalspace:
if (ccdik_data.enable_constraint && !ccdik_data.constraint_in_localspace) {
diff --git a/scene/resources/skeleton_modification_2d_fabrik.cpp b/scene/resources/skeleton_modification_2d_fabrik.cpp
index aef852f7e4..c03a92b503 100644
--- a/scene/resources/skeleton_modification_2d_fabrik.cpp
+++ b/scene/resources/skeleton_modification_2d_fabrik.cpp
@@ -161,25 +161,25 @@ void SkeletonModification2DFABRIK::_execute(float p_delta) {
}
Bone2D *final_bone2d_node = Object::cast_to<Bone2D>(ObjectDB::get_instance(fabrik_data_chain[fabrik_data_chain.size() - 1].bone2d_node_cache));
- float final_bone2d_angle = final_bone2d_node->get_global_transform().get_rotation();
+ float final_bone2d_angle = final_bone2d_node->get_global_rotation();
if (fabrik_data_chain[fabrik_data_chain.size() - 1].use_target_rotation) {
final_bone2d_angle = target_global_pose.get_rotation();
}
Vector2 final_bone2d_direction = Vector2(Math::cos(final_bone2d_angle), Math::sin(final_bone2d_angle));
float final_bone2d_length = final_bone2d_node->get_length() * MIN(final_bone2d_node->get_global_scale().x, final_bone2d_node->get_global_scale().y);
- float target_distance = (final_bone2d_node->get_global_transform().get_origin() + (final_bone2d_direction * final_bone2d_length)).distance_to(target->get_global_transform().get_origin());
+ float target_distance = (final_bone2d_node->get_global_position() + (final_bone2d_direction * final_bone2d_length)).distance_to(target->get_global_position());
chain_iterations = 0;
while (target_distance > chain_tolarance) {
chain_backwards();
chain_forwards();
- final_bone2d_angle = final_bone2d_node->get_global_transform().get_rotation();
+ final_bone2d_angle = final_bone2d_node->get_global_rotation();
if (fabrik_data_chain[fabrik_data_chain.size() - 1].use_target_rotation) {
final_bone2d_angle = target_global_pose.get_rotation();
}
final_bone2d_direction = Vector2(Math::cos(final_bone2d_angle), Math::sin(final_bone2d_angle));
- target_distance = (final_bone2d_node->get_global_transform().get_origin() + (final_bone2d_direction * final_bone2d_length)).distance_to(target->get_global_transform().get_origin());
+ target_distance = (final_bone2d_node->get_global_position() + (final_bone2d_direction * final_bone2d_length)).distance_to(target->get_global_position());
chain_iterations += 1;
if (chain_iterations >= chain_max_iterations) {
@@ -210,7 +210,7 @@ void SkeletonModification2DFABRIK::_execute(float p_delta) {
chain_trans.set_rotation(chain_trans.get_rotation() - joint_bone2d_node->get_bone_angle());
// Reset scale
- chain_trans.set_scale(joint_bone2d_node->get_global_transform().get_scale());
+ chain_trans.set_scale(joint_bone2d_node->get_global_scale());
// Apply to the bone, and to the override
joint_bone2d_node->set_global_transform(chain_trans);
diff --git a/scene/resources/skeleton_modification_2d_jiggle.cpp b/scene/resources/skeleton_modification_2d_jiggle.cpp
index 2547083336..84abc9d020 100644
--- a/scene/resources/skeleton_modification_2d_jiggle.cpp
+++ b/scene/resources/skeleton_modification_2d_jiggle.cpp
@@ -171,7 +171,7 @@ void SkeletonModification2DJiggle::_execute_jiggle_joint(int p_joint_idx, Node2D
}
Transform2D operation_bone_trans = operation_bone->get_global_transform();
- Vector2 target_position = p_target->get_global_transform().get_origin();
+ Vector2 target_position = p_target->get_global_position();
jiggle_data_chain.write[p_joint_idx].force = (target_position - jiggle_data_chain[p_joint_idx].dynamic_position) * jiggle_data_chain[p_joint_idx].stiffness * p_delta;
@@ -215,7 +215,7 @@ void SkeletonModification2DJiggle::_execute_jiggle_joint(int p_joint_idx, Node2D
operation_bone_trans.set_rotation(operation_bone_trans.get_rotation() - operation_bone->get_bone_angle());
// Reset scale
- operation_bone_trans.set_scale(operation_bone->get_global_transform().get_scale());
+ operation_bone_trans.set_scale(operation_bone->get_global_scale());
operation_bone->set_global_transform(operation_bone_trans);
stack->skeleton->set_bone_local_pose_override(jiggle_data_chain[p_joint_idx].bone_idx, operation_bone->get_transform(), stack->strength, true);
@@ -244,7 +244,7 @@ void SkeletonModification2DJiggle::_setup_modification(SkeletonModificationStack
int bone_idx = jiggle_data_chain[i].bone_idx;
if (bone_idx > 0 && bone_idx < stack->skeleton->get_bone_count()) {
Bone2D *bone2d_node = stack->skeleton->get_bone(bone_idx);
- jiggle_data_chain.write[i].dynamic_position = bone2d_node->get_global_transform().get_origin();
+ jiggle_data_chain.write[i].dynamic_position = bone2d_node->get_global_position();
}
}
}
diff --git a/scene/resources/skeleton_modification_2d_lookat.cpp b/scene/resources/skeleton_modification_2d_lookat.cpp
index fd5c8c7cc2..2da770f012 100644
--- a/scene/resources/skeleton_modification_2d_lookat.cpp
+++ b/scene/resources/skeleton_modification_2d_lookat.cpp
@@ -147,7 +147,7 @@ void SkeletonModification2DLookAt::_execute(float p_delta) {
// Look at the target!
operation_transform = operation_transform.looking_at(target_trans.get_origin());
// Apply whatever scale it had prior to looking_at
- operation_transform.set_scale(operation_bone->get_global_transform().get_scale());
+ operation_transform.set_scale(operation_bone->get_global_scale());
// Account for the direction the bone faces in:
operation_transform.set_rotation(operation_transform.get_rotation() - operation_bone->get_bone_angle());
diff --git a/scene/resources/skeleton_modification_2d_twoboneik.cpp b/scene/resources/skeleton_modification_2d_twoboneik.cpp
index 0a91290015..88d80a501f 100644
--- a/scene/resources/skeleton_modification_2d_twoboneik.cpp
+++ b/scene/resources/skeleton_modification_2d_twoboneik.cpp
@@ -142,7 +142,7 @@ void SkeletonModification2DTwoBoneIK::_execute(float p_delta) {
// http://theorangeduck.com/page/simple-two-joint
// https://www.alanzucconi.com/2018/05/02/ik-2d-2/
// With modifications by TwistedTwigleg
- Vector2 target_difference = target->get_global_transform().get_origin() - joint_one_bone->get_global_transform().get_origin();
+ Vector2 target_difference = target->get_global_position() - joint_one_bone->get_global_position();
float joint_one_to_target = target_difference.length();
float angle_atan = Math::atan2(target_difference.y, target_difference.x);
@@ -206,7 +206,7 @@ void SkeletonModification2DTwoBoneIK::_draw_editor_gizmo() {
return;
}
stack->skeleton->draw_set_transform(
- stack->skeleton->get_global_transform().affine_inverse().xform(operation_bone_one->get_global_position()),
+ stack->skeleton->to_local(operation_bone_one->get_global_position()),
operation_bone_one->get_global_rotation() - stack->skeleton->get_global_rotation());
Color bone_ik_color = Color(1.0, 0.65, 0.0, 0.4);
diff --git a/scene/resources/skeleton_modification_3d.cpp b/scene/resources/skeleton_modification_3d.cpp
new file mode 100644
index 0000000000..9306ee14cd
--- /dev/null
+++ b/scene/resources/skeleton_modification_3d.cpp
@@ -0,0 +1,170 @@
+/*************************************************************************/
+/* skeleton_modification_3d.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "skeleton_modification_3d.h"
+#include "scene/3d/skeleton_3d.h"
+
+void SkeletonModification3D::_execute(real_t p_delta) {
+ if (get_script_instance()) {
+ if (get_script_instance()->has_method("execute")) {
+ get_script_instance()->call("execute", p_delta);
+ }
+ }
+
+ if (!enabled)
+ return;
+}
+
+void SkeletonModification3D::_setup_modification(SkeletonModificationStack3D *p_stack) {
+ stack = p_stack;
+ if (stack) {
+ is_setup = true;
+ } else {
+ WARN_PRINT("Could not setup modification with name " + this->get_name());
+ }
+
+ if (get_script_instance()) {
+ if (get_script_instance()->has_method("setup_modification")) {
+ get_script_instance()->call("setup_modification", p_stack);
+ }
+ }
+}
+
+void SkeletonModification3D::set_enabled(bool p_enabled) {
+ enabled = p_enabled;
+}
+
+bool SkeletonModification3D::get_enabled() {
+ return enabled;
+}
+
+// Helper function. Needed for CCDIK.
+real_t SkeletonModification3D::clamp_angle(real_t p_angle, real_t p_min_bound, real_t p_max_bound, bool p_invert) {
+ // Map to the 0 to 360 range (in radians though) instead of the -180 to 180 range.
+ if (p_angle < 0) {
+ p_angle = Math_TAU + p_angle;
+ }
+
+ // Make min and max in the range of 0 to 360 (in radians), and make sure they are in the right order
+ if (p_min_bound < 0) {
+ p_min_bound = Math_TAU + p_min_bound;
+ }
+ if (p_max_bound < 0) {
+ p_max_bound = Math_TAU + p_max_bound;
+ }
+ if (p_min_bound > p_max_bound) {
+ real_t tmp = p_min_bound;
+ p_min_bound = p_max_bound;
+ p_max_bound = tmp;
+ }
+
+ // Note: May not be the most optimal way to clamp, but it always constraints to the nearest angle.
+ if (p_invert == false) {
+ if (p_angle < p_min_bound || p_angle > p_max_bound) {
+ Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound));
+ Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound));
+ Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle));
+
+ if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) {
+ p_angle = p_min_bound;
+ } else {
+ p_angle = p_max_bound;
+ }
+ }
+ } else {
+ if (p_angle > p_min_bound && p_angle < p_max_bound) {
+ Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound));
+ Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound));
+ Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle));
+
+ if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) {
+ p_angle = p_min_bound;
+ } else {
+ p_angle = p_max_bound;
+ }
+ }
+ }
+ return p_angle;
+}
+
+bool SkeletonModification3D::_print_execution_error(bool p_condition, String p_message) {
+ // If the modification is not setup, don't bother printing the error
+ if (!is_setup) {
+ return p_condition;
+ }
+
+ if (p_condition && !execution_error_found) {
+ ERR_PRINT(p_message);
+ execution_error_found = true;
+ }
+ return p_condition;
+}
+
+Ref<SkeletonModificationStack3D> SkeletonModification3D::get_modification_stack() {
+ return stack;
+}
+
+void SkeletonModification3D::set_is_setup(bool p_is_setup) {
+ is_setup = p_is_setup;
+}
+
+bool SkeletonModification3D::get_is_setup() const {
+ return is_setup;
+}
+
+void SkeletonModification3D::set_execution_mode(int p_mode) {
+ execution_mode = p_mode;
+}
+
+int SkeletonModification3D::get_execution_mode() const {
+ return execution_mode;
+}
+
+void SkeletonModification3D::_bind_methods() {
+ BIND_VMETHOD(MethodInfo("_execute", PropertyInfo(Variant::FLOAT, "delta")));
+ BIND_VMETHOD(MethodInfo("_setup_modification", PropertyInfo(Variant::OBJECT, "modification_stack", PROPERTY_HINT_RESOURCE_TYPE, "SkeletonModificationStack3D")));
+
+ ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &SkeletonModification3D::set_enabled);
+ ClassDB::bind_method(D_METHOD("get_enabled"), &SkeletonModification3D::get_enabled);
+ ClassDB::bind_method(D_METHOD("get_modification_stack"), &SkeletonModification3D::get_modification_stack);
+ ClassDB::bind_method(D_METHOD("set_is_setup", "is_setup"), &SkeletonModification3D::set_is_setup);
+ ClassDB::bind_method(D_METHOD("get_is_setup"), &SkeletonModification3D::get_is_setup);
+ ClassDB::bind_method(D_METHOD("set_execution_mode", "execution_mode"), &SkeletonModification3D::set_execution_mode);
+ ClassDB::bind_method(D_METHOD("get_execution_mode"), &SkeletonModification3D::get_execution_mode);
+ ClassDB::bind_method(D_METHOD("clamp_angle", "angle", "min", "max", "invert"), &SkeletonModification3D::clamp_angle);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "get_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "execution_mode", PROPERTY_HINT_ENUM, "process, physics_process"), "set_execution_mode", "get_execution_mode");
+}
+
+SkeletonModification3D::SkeletonModification3D() {
+ stack = nullptr;
+ is_setup = false;
+}
diff --git a/scene/resources/skeleton_modification_3d.h b/scene/resources/skeleton_modification_3d.h
new file mode 100644
index 0000000000..94ab0bf32c
--- /dev/null
+++ b/scene/resources/skeleton_modification_3d.h
@@ -0,0 +1,76 @@
+/*************************************************************************/
+/* skeleton_modification_3d.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef SKELETONMODIFICATION3D_H
+#define SKELETONMODIFICATION3D_H
+
+#include "scene/3d/skeleton_3d.h"
+#include "scene/resources/skeleton_modification_stack_3d.h"
+
+class SkeletonModificationStack3D;
+
+class SkeletonModification3D : public Resource {
+ GDCLASS(SkeletonModification3D, Resource);
+ friend class Skeleton3D;
+ friend class SkeletonModificationStack3D;
+
+protected:
+ static void _bind_methods();
+
+ SkeletonModificationStack3D *stack;
+ int execution_mode = 0; // 0 = process
+
+ bool enabled = true;
+ bool is_setup = false;
+ bool execution_error_found = false;
+
+ bool _print_execution_error(bool p_condition, String p_message);
+
+public:
+ virtual void _execute(real_t p_delta);
+ virtual void _setup_modification(SkeletonModificationStack3D *p_stack);
+
+ real_t clamp_angle(real_t p_angle, real_t p_min_bound, real_t p_max_bound, bool p_invert);
+
+ void set_enabled(bool p_enabled);
+ bool get_enabled();
+
+ void set_execution_mode(int p_mode);
+ int get_execution_mode() const;
+
+ Ref<SkeletonModificationStack3D> get_modification_stack();
+
+ void set_is_setup(bool p_setup);
+ bool get_is_setup() const;
+
+ SkeletonModification3D();
+};
+
+#endif // SKELETONMODIFICATION3D_H
diff --git a/scene/resources/skeleton_modification_3d_ccdik.cpp b/scene/resources/skeleton_modification_3d_ccdik.cpp
new file mode 100644
index 0000000000..6409022563
--- /dev/null
+++ b/scene/resources/skeleton_modification_3d_ccdik.cpp
@@ -0,0 +1,474 @@
+/*************************************************************************/
+/* skeleton_modification_3d_ccdik.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "scene/resources/skeleton_modification_3d_ccdik.h"
+#include "scene/3d/skeleton_3d.h"
+#include "scene/resources/skeleton_modification_3d.h"
+
+bool SkeletonModification3DCCDIK::_set(const StringName &p_path, const Variant &p_value) {
+ String path = p_path;
+
+ if (path.begins_with("joint_data/")) {
+ int ccdik_data_size = ccdik_data_chain.size();
+ int which = path.get_slicec('/', 1).to_int();
+ String what = path.get_slicec('/', 2);
+ ERR_FAIL_INDEX_V(which, ccdik_data_size, false);
+
+ if (what == "bone_name") {
+ set_ccdik_joint_bone_name(which, p_value);
+ } else if (what == "bone_index") {
+ set_ccdik_joint_bone_index(which, p_value);
+ } else if (what == "ccdik_axis") {
+ set_ccdik_joint_ccdik_axis(which, p_value);
+ } else if (what == "enable_joint_constraint") {
+ set_ccdik_joint_enable_constraint(which, p_value);
+ } else if (what == "joint_constraint_angle_min") {
+ set_ccdik_joint_constraint_angle_min(which, Math::deg2rad(real_t(p_value)));
+ } else if (what == "joint_constraint_angle_max") {
+ set_ccdik_joint_constraint_angle_max(which, Math::deg2rad(real_t(p_value)));
+ } else if (what == "joint_constraint_angles_invert") {
+ set_ccdik_joint_constraint_invert(which, p_value);
+ }
+ return true;
+ }
+ return true;
+}
+
+bool SkeletonModification3DCCDIK::_get(const StringName &p_path, Variant &r_ret) const {
+ String path = p_path;
+
+ if (path.begins_with("joint_data/")) {
+ const int ccdik_data_size = ccdik_data_chain.size();
+ int which = path.get_slicec('/', 1).to_int();
+ String what = path.get_slicec('/', 2);
+ ERR_FAIL_INDEX_V(which, ccdik_data_size, false);
+
+ if (what == "bone_name") {
+ r_ret = get_ccdik_joint_bone_name(which);
+ } else if (what == "bone_index") {
+ r_ret = get_ccdik_joint_bone_index(which);
+ } else if (what == "ccdik_axis") {
+ r_ret = get_ccdik_joint_ccdik_axis(which);
+ } else if (what == "enable_joint_constraint") {
+ r_ret = get_ccdik_joint_enable_constraint(which);
+ } else if (what == "joint_constraint_angle_min") {
+ r_ret = Math::rad2deg(get_ccdik_joint_constraint_angle_min(which));
+ } else if (what == "joint_constraint_angle_max") {
+ r_ret = Math::rad2deg(get_ccdik_joint_constraint_angle_max(which));
+ } else if (what == "joint_constraint_angles_invert") {
+ r_ret = get_ccdik_joint_constraint_invert(which);
+ }
+ return true;
+ }
+ return true;
+}
+
+void SkeletonModification3DCCDIK::_get_property_list(List<PropertyInfo> *p_list) const {
+ for (uint32_t i = 0; i < ccdik_data_chain.size(); i++) {
+ String base_string = "joint_data/" + itos(i) + "/";
+
+ p_list->push_back(PropertyInfo(Variant::STRING_NAME, base_string + "bone_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::INT, base_string + "bone_index", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+
+ p_list->push_back(PropertyInfo(Variant::INT, base_string + "ccdik_axis",
+ PROPERTY_HINT_ENUM, "X Axis, Y Axis, Z Axis", PROPERTY_USAGE_DEFAULT));
+
+ p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "enable_joint_constraint", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ if (ccdik_data_chain[i].enable_constraint) {
+ p_list->push_back(PropertyInfo(Variant::FLOAT, base_string + "joint_constraint_angle_min", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, base_string + "joint_constraint_angle_max", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "joint_constraint_angles_invert", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ }
+ }
+}
+
+void SkeletonModification3DCCDIK::_execute(real_t p_delta) {
+ ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr,
+ "Modification is not setup and therefore cannot execute!");
+ if (!enabled) {
+ return;
+ }
+
+ if (target_node_cache.is_null()) {
+ _print_execution_error(true, "Target cache is out of date. Attempting to update");
+ update_target_cache();
+ return;
+ }
+ if (tip_node_cache.is_null()) {
+ _print_execution_error(true, "Tip cache is out of date. Attempting to update");
+ update_tip_cache();
+ return;
+ }
+
+ // Reset the local bone overrides for CCDIK affected nodes
+ for (uint32_t i = 0; i < ccdik_data_chain.size(); i++) {
+ stack->skeleton->set_bone_local_pose_override(ccdik_data_chain[i].bone_idx,
+ stack->skeleton->get_bone_local_pose_override(ccdik_data_chain[i].bone_idx),
+ 0.0, false);
+ }
+
+ Node3D *node_target = Object::cast_to<Node3D>(ObjectDB::get_instance(target_node_cache));
+ Node3D *node_tip = Object::cast_to<Node3D>(ObjectDB::get_instance(tip_node_cache));
+
+ if (_print_execution_error(!node_target || !node_target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!")) {
+ return;
+ }
+ if (_print_execution_error(!node_tip || !node_tip->is_inside_tree(), "Tip node is not in the scene tree. Cannot execute modification!")) {
+ return;
+ }
+
+ if (use_high_quality_solve) {
+ for (uint32_t i = 0; i < ccdik_data_chain.size(); i++) {
+ for (uint32_t j = i; j < ccdik_data_chain.size(); j++) {
+ _execute_ccdik_joint(j, node_target, node_tip);
+ }
+ }
+ } else {
+ for (uint32_t i = 0; i < ccdik_data_chain.size(); i++) {
+ _execute_ccdik_joint(i, node_target, node_tip);
+ }
+ }
+
+ execution_error_found = false;
+}
+
+void SkeletonModification3DCCDIK::_execute_ccdik_joint(int p_joint_idx, Node3D *p_target, Node3D *p_tip) {
+ CCDIK_Joint_Data ccdik_data = ccdik_data_chain[p_joint_idx];
+
+ if (_print_execution_error(ccdik_data.bone_idx < 0 || ccdik_data.bone_idx > stack->skeleton->get_bone_count(),
+ "CCDIK joint: bone index for joint" + itos(p_joint_idx) + " not found. Cannot execute modification!")) {
+ return;
+ }
+
+ Transform3D bone_trans = stack->skeleton->global_pose_to_local_pose(ccdik_data.bone_idx, stack->skeleton->get_bone_global_pose(ccdik_data.bone_idx));
+ Transform3D tip_trans = stack->skeleton->global_pose_to_local_pose(ccdik_data.bone_idx, stack->skeleton->world_transform_to_global_pose(p_tip->get_global_transform()));
+ Transform3D target_trans = stack->skeleton->global_pose_to_local_pose(ccdik_data.bone_idx, stack->skeleton->world_transform_to_global_pose(p_target->get_global_transform()));
+
+ if (tip_trans.origin.distance_to(target_trans.origin) <= 0.01) {
+ return;
+ }
+
+ // Inspired (and very loosely based on) by the CCDIK algorithm made by Zalo on GitHub (https://github.com/zalo/MathUtilities)
+ // Convert the 3D position to a 2D position so we can use Atan2 (via the angle function)
+ // to know how much rotation we need on the given axis to place the tip at the target.
+ Vector2 tip_pos_2d;
+ Vector2 target_pos_2d;
+ if (ccdik_data.ccdik_axis == CCDIK_Axes::AXIS_X) {
+ tip_pos_2d = Vector2(tip_trans.origin.y, tip_trans.origin.z);
+ target_pos_2d = Vector2(target_trans.origin.y, target_trans.origin.z);
+ bone_trans.basis.rotate_local(Vector3(1, 0, 0), target_pos_2d.angle() - tip_pos_2d.angle());
+ } else if (ccdik_data.ccdik_axis == CCDIK_Axes::AXIS_Y) {
+ tip_pos_2d = Vector2(tip_trans.origin.z, tip_trans.origin.x);
+ target_pos_2d = Vector2(target_trans.origin.z, target_trans.origin.x);
+ bone_trans.basis.rotate_local(Vector3(0, 1, 0), target_pos_2d.angle() - tip_pos_2d.angle());
+ } else if (ccdik_data.ccdik_axis == CCDIK_Axes::AXIS_Z) {
+ tip_pos_2d = Vector2(tip_trans.origin.x, tip_trans.origin.y);
+ target_pos_2d = Vector2(target_trans.origin.x, target_trans.origin.y);
+ bone_trans.basis.rotate_local(Vector3(0, 0, 1), target_pos_2d.angle() - tip_pos_2d.angle());
+ } else {
+ // Should never happen, but...
+ ERR_FAIL_MSG("CCDIK joint: Unknown axis vector passed for joint" + itos(p_joint_idx) + ". Cannot execute modification!");
+ }
+
+ if (ccdik_data.enable_constraint) {
+ Vector3 rotation_axis;
+ real_t rotation_angle;
+ bone_trans.basis.get_axis_angle(rotation_axis, rotation_angle);
+
+ // Note: When the axis has a negative direction, the angle is OVER 180 degrees and therefore we need to account for this
+ // when constraining.
+ if (ccdik_data.ccdik_axis == CCDIK_Axes::AXIS_X) {
+ if (rotation_axis.x < 0) {
+ rotation_angle += Math_PI;
+ rotation_axis = Vector3(1, 0, 0);
+ }
+ } else if (ccdik_data.ccdik_axis == CCDIK_Axes::AXIS_Y) {
+ if (rotation_axis.y < 0) {
+ rotation_angle += Math_PI;
+ rotation_axis = Vector3(0, 1, 0);
+ }
+ } else if (ccdik_data.ccdik_axis == CCDIK_Axes::AXIS_Z) {
+ if (rotation_axis.z < 0) {
+ rotation_angle += Math_PI;
+ rotation_axis = Vector3(0, 0, 1);
+ }
+ } else {
+ // Should never happen, but...
+ ERR_FAIL_MSG("CCDIK joint: Unknown axis vector passed for joint" + itos(p_joint_idx) + ". Cannot execute modification!");
+ }
+ rotation_angle = clamp_angle(rotation_angle, ccdik_data.constraint_angle_min, ccdik_data.constraint_angle_max, ccdik_data.constraint_angles_invert);
+
+ bone_trans.basis.set_axis_angle(rotation_axis, rotation_angle);
+ }
+
+ stack->skeleton->set_bone_local_pose_override(ccdik_data.bone_idx, bone_trans, stack->strength, true);
+ stack->skeleton->force_update_bone_children_transforms(ccdik_data.bone_idx);
+}
+
+void SkeletonModification3DCCDIK::_setup_modification(SkeletonModificationStack3D *p_stack) {
+ stack = p_stack;
+ if (stack != nullptr) {
+ is_setup = true;
+ execution_error_found = false;
+ update_target_cache();
+ update_tip_cache();
+ }
+}
+
+void SkeletonModification3DCCDIK::update_target_cache() {
+ if (!is_setup || !stack) {
+ _print_execution_error(true, "Cannot update target cache: modification is not properly setup!");
+ return;
+ }
+
+ target_node_cache = ObjectID();
+ if (stack->skeleton) {
+ if (stack->skeleton->is_inside_tree()) {
+ if (stack->skeleton->has_node(target_node)) {
+ Node *node = stack->skeleton->get_node(target_node);
+ ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
+ "Cannot update target cache: node is this modification's skeleton or cannot be found!");
+ ERR_FAIL_COND_MSG(!node->is_inside_tree(),
+ "Cannot update target cache: node is not in scene tree!");
+ target_node_cache = node->get_instance_id();
+
+ execution_error_found = false;
+ }
+ }
+ }
+}
+
+void SkeletonModification3DCCDIK::update_tip_cache() {
+ if (!is_setup || !stack) {
+ _print_execution_error(true, "Cannot update tip cache: modification is not properly setup!");
+ return;
+ }
+
+ tip_node_cache = ObjectID();
+ if (stack->skeleton) {
+ if (stack->skeleton->is_inside_tree()) {
+ if (stack->skeleton->has_node(tip_node)) {
+ Node *node = stack->skeleton->get_node(tip_node);
+ ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
+ "Cannot update tip cache: node is this modification's skeleton or cannot be found!");
+ ERR_FAIL_COND_MSG(!node->is_inside_tree(),
+ "Cannot update tip cache: node is not in scene tree!");
+ tip_node_cache = node->get_instance_id();
+
+ execution_error_found = false;
+ }
+ }
+ }
+}
+
+void SkeletonModification3DCCDIK::set_target_node(const NodePath &p_target_node) {
+ target_node = p_target_node;
+ update_target_cache();
+}
+
+NodePath SkeletonModification3DCCDIK::get_target_node() const {
+ return target_node;
+}
+
+void SkeletonModification3DCCDIK::set_tip_node(const NodePath &p_tip_node) {
+ tip_node = p_tip_node;
+ update_tip_cache();
+}
+
+NodePath SkeletonModification3DCCDIK::get_tip_node() const {
+ return tip_node;
+}
+
+void SkeletonModification3DCCDIK::set_use_high_quality_solve(bool p_high_quality) {
+ use_high_quality_solve = p_high_quality;
+}
+
+bool SkeletonModification3DCCDIK::get_use_high_quality_solve() const {
+ return use_high_quality_solve;
+}
+
+// CCDIK joint data functions
+String SkeletonModification3DCCDIK::get_ccdik_joint_bone_name(int p_joint_idx) const {
+ const int bone_chain_size = ccdik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, String());
+ return ccdik_data_chain[p_joint_idx].bone_name;
+}
+
+void SkeletonModification3DCCDIK::set_ccdik_joint_bone_name(int p_joint_idx, String p_bone_name) {
+ const int bone_chain_size = ccdik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ ccdik_data_chain[p_joint_idx].bone_name = p_bone_name;
+
+ if (stack) {
+ if (stack->skeleton) {
+ ccdik_data_chain[p_joint_idx].bone_idx = stack->skeleton->find_bone(p_bone_name);
+ }
+ }
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+int SkeletonModification3DCCDIK::get_ccdik_joint_bone_index(int p_joint_idx) const {
+ const int bone_chain_size = ccdik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, -1);
+ return ccdik_data_chain[p_joint_idx].bone_idx;
+}
+
+void SkeletonModification3DCCDIK::set_ccdik_joint_bone_index(int p_joint_idx, int p_bone_idx) {
+ const int bone_chain_size = ccdik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!");
+ ccdik_data_chain[p_joint_idx].bone_idx = p_bone_idx;
+
+ if (stack) {
+ if (stack->skeleton) {
+ ccdik_data_chain[p_joint_idx].bone_name = stack->skeleton->get_bone_name(p_bone_idx);
+ }
+ }
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+int SkeletonModification3DCCDIK::get_ccdik_joint_ccdik_axis(int p_joint_idx) const {
+ const int bone_chain_size = ccdik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, -1);
+ return ccdik_data_chain[p_joint_idx].ccdik_axis;
+}
+
+void SkeletonModification3DCCDIK::set_ccdik_joint_ccdik_axis(int p_joint_idx, int p_axis) {
+ const int bone_chain_size = ccdik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ ERR_FAIL_COND_MSG(p_axis < 0, "CCDIK axis is out of range: The axis mode is too low!");
+ ccdik_data_chain[p_joint_idx].ccdik_axis = p_axis;
+ notify_property_list_changed();
+}
+
+bool SkeletonModification3DCCDIK::get_ccdik_joint_enable_constraint(int p_joint_idx) const {
+ const int bone_chain_size = ccdik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, false);
+ return ccdik_data_chain[p_joint_idx].enable_constraint;
+}
+
+void SkeletonModification3DCCDIK::set_ccdik_joint_enable_constraint(int p_joint_idx, bool p_enable) {
+ const int bone_chain_size = ccdik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ ccdik_data_chain[p_joint_idx].enable_constraint = p_enable;
+ notify_property_list_changed();
+}
+
+real_t SkeletonModification3DCCDIK::get_ccdik_joint_constraint_angle_min(int p_joint_idx) const {
+ const int bone_chain_size = ccdik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, false);
+ return ccdik_data_chain[p_joint_idx].constraint_angle_min;
+}
+
+void SkeletonModification3DCCDIK::set_ccdik_joint_constraint_angle_min(int p_joint_idx, real_t p_angle_min) {
+ const int bone_chain_size = ccdik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ ccdik_data_chain[p_joint_idx].constraint_angle_min = p_angle_min;
+}
+
+real_t SkeletonModification3DCCDIK::get_ccdik_joint_constraint_angle_max(int p_joint_idx) const {
+ const int bone_chain_size = ccdik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, false);
+ return ccdik_data_chain[p_joint_idx].constraint_angle_max;
+}
+
+void SkeletonModification3DCCDIK::set_ccdik_joint_constraint_angle_max(int p_joint_idx, real_t p_angle_max) {
+ const int bone_chain_size = ccdik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ ccdik_data_chain[p_joint_idx].constraint_angle_max = p_angle_max;
+}
+
+bool SkeletonModification3DCCDIK::get_ccdik_joint_constraint_invert(int p_joint_idx) const {
+ const int bone_chain_size = ccdik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, false);
+ return ccdik_data_chain[p_joint_idx].constraint_angles_invert;
+}
+
+void SkeletonModification3DCCDIK::set_ccdik_joint_constraint_invert(int p_joint_idx, bool p_invert) {
+ const int bone_chain_size = ccdik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ ccdik_data_chain[p_joint_idx].constraint_angles_invert = p_invert;
+}
+
+int SkeletonModification3DCCDIK::get_ccdik_data_chain_length() {
+ return ccdik_data_chain.size();
+}
+void SkeletonModification3DCCDIK::set_ccdik_data_chain_length(int p_length) {
+ ERR_FAIL_COND(p_length < 0);
+ ccdik_data_chain.resize(p_length);
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+void SkeletonModification3DCCDIK::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_target_node", "target_nodepath"), &SkeletonModification3DCCDIK::set_target_node);
+ ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonModification3DCCDIK::get_target_node);
+
+ ClassDB::bind_method(D_METHOD("set_tip_node", "tip_nodepath"), &SkeletonModification3DCCDIK::set_tip_node);
+ ClassDB::bind_method(D_METHOD("get_tip_node"), &SkeletonModification3DCCDIK::get_tip_node);
+
+ ClassDB::bind_method(D_METHOD("set_use_high_quality_solve", "high_quality_solve"), &SkeletonModification3DCCDIK::set_use_high_quality_solve);
+ ClassDB::bind_method(D_METHOD("get_use_high_quality_solve"), &SkeletonModification3DCCDIK::get_use_high_quality_solve);
+
+ // CCDIK joint data functions
+ ClassDB::bind_method(D_METHOD("get_ccdik_joint_bone_name", "joint_idx"), &SkeletonModification3DCCDIK::get_ccdik_joint_bone_name);
+ ClassDB::bind_method(D_METHOD("set_ccdik_joint_bone_name", "joint_idx", "bone_name"), &SkeletonModification3DCCDIK::set_ccdik_joint_bone_name);
+ ClassDB::bind_method(D_METHOD("get_ccdik_joint_bone_index", "joint_idx"), &SkeletonModification3DCCDIK::get_ccdik_joint_bone_index);
+ ClassDB::bind_method(D_METHOD("set_ccdik_joint_bone_index", "joint_idx", "bone_index"), &SkeletonModification3DCCDIK::set_ccdik_joint_bone_index);
+ ClassDB::bind_method(D_METHOD("get_ccdik_joint_ccdik_axis", "joint_idx"), &SkeletonModification3DCCDIK::get_ccdik_joint_ccdik_axis);
+ ClassDB::bind_method(D_METHOD("set_ccdik_joint_ccdik_axis", "joint_idx", "axis"), &SkeletonModification3DCCDIK::set_ccdik_joint_ccdik_axis);
+ ClassDB::bind_method(D_METHOD("get_ccdik_joint_enable_joint_constraint", "joint_idx"), &SkeletonModification3DCCDIK::get_ccdik_joint_enable_constraint);
+ ClassDB::bind_method(D_METHOD("set_ccdik_joint_enable_joint_constraint", "joint_idx", "enable"), &SkeletonModification3DCCDIK::set_ccdik_joint_enable_constraint);
+ ClassDB::bind_method(D_METHOD("get_ccdik_joint_constraint_angle_min", "joint_idx"), &SkeletonModification3DCCDIK::get_ccdik_joint_constraint_angle_min);
+ ClassDB::bind_method(D_METHOD("set_ccdik_joint_constraint_angle_min", "joint_idx", "min_angle"), &SkeletonModification3DCCDIK::set_ccdik_joint_constraint_angle_min);
+ ClassDB::bind_method(D_METHOD("get_ccdik_joint_constraint_angle_max", "joint_idx"), &SkeletonModification3DCCDIK::get_ccdik_joint_constraint_angle_max);
+ ClassDB::bind_method(D_METHOD("set_ccdik_joint_constraint_angle_max", "joint_idx", "max_angle"), &SkeletonModification3DCCDIK::set_ccdik_joint_constraint_angle_max);
+ ClassDB::bind_method(D_METHOD("get_ccdik_joint_constraint_invert", "joint_idx"), &SkeletonModification3DCCDIK::get_ccdik_joint_constraint_invert);
+ ClassDB::bind_method(D_METHOD("set_ccdik_joint_constraint_invert", "joint_idx", "invert"), &SkeletonModification3DCCDIK::set_ccdik_joint_constraint_invert);
+
+ ClassDB::bind_method(D_METHOD("set_ccdik_data_chain_length", "length"), &SkeletonModification3DCCDIK::set_ccdik_data_chain_length);
+ ClassDB::bind_method(D_METHOD("get_ccdik_data_chain_length"), &SkeletonModification3DCCDIK::get_ccdik_data_chain_length);
+
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node3D"), "set_target_node", "get_target_node");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "tip_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node3D"), "set_tip_node", "get_tip_node");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "high_quality_solve", PROPERTY_HINT_NONE, ""), "set_use_high_quality_solve", "get_use_high_quality_solve");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "ccdik_data_chain_length", PROPERTY_HINT_RANGE, "0,100,1"), "set_ccdik_data_chain_length", "get_ccdik_data_chain_length");
+}
+
+SkeletonModification3DCCDIK::SkeletonModification3DCCDIK() {
+ stack = nullptr;
+ is_setup = false;
+ enabled = true;
+}
+
+SkeletonModification3DCCDIK::~SkeletonModification3DCCDIK() {
+}
diff --git a/scene/resources/skeleton_modification_3d_ccdik.h b/scene/resources/skeleton_modification_3d_ccdik.h
new file mode 100644
index 0000000000..e7537cc5b0
--- /dev/null
+++ b/scene/resources/skeleton_modification_3d_ccdik.h
@@ -0,0 +1,114 @@
+/*************************************************************************/
+/* skeleton_modification_3d_ccdik.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "core/templates/local_vector.h"
+#include "scene/3d/skeleton_3d.h"
+#include "scene/resources/skeleton_modification_3d.h"
+
+#ifndef SKELETONMODIFICATION3DCCDIK_H
+#define SKELETONMODIFICATION3DCCDIK_H
+
+class SkeletonModification3DCCDIK : public SkeletonModification3D {
+ GDCLASS(SkeletonModification3DCCDIK, SkeletonModification3D);
+
+private:
+ enum CCDIK_Axes {
+ AXIS_X,
+ AXIS_Y,
+ AXIS_Z
+ };
+
+ struct CCDIK_Joint_Data {
+ String bone_name = "";
+ int bone_idx = -1;
+ int ccdik_axis = 0;
+
+ bool enable_constraint = false;
+ real_t constraint_angle_min = 0;
+ real_t constraint_angle_max = (2.0 * Math_PI);
+ bool constraint_angles_invert = false;
+ };
+
+ LocalVector<CCDIK_Joint_Data> ccdik_data_chain;
+ NodePath target_node;
+ ObjectID target_node_cache;
+
+ NodePath tip_node;
+ ObjectID tip_node_cache;
+
+ bool use_high_quality_solve = true;
+
+ void update_target_cache();
+ void update_tip_cache();
+
+ void _execute_ccdik_joint(int p_joint_idx, Node3D *p_target, Node3D *p_tip);
+
+protected:
+ static void _bind_methods();
+ bool _get(const StringName &p_path, Variant &r_ret) const;
+ bool _set(const StringName &p_path, const Variant &p_value);
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
+public:
+ virtual void _execute(real_t p_delta) override;
+ virtual void _setup_modification(SkeletonModificationStack3D *p_stack) override;
+
+ void set_target_node(const NodePath &p_target_node);
+ NodePath get_target_node() const;
+
+ void set_tip_node(const NodePath &p_tip_node);
+ NodePath get_tip_node() const;
+
+ void set_use_high_quality_solve(bool p_solve);
+ bool get_use_high_quality_solve() const;
+
+ String get_ccdik_joint_bone_name(int p_joint_idx) const;
+ void set_ccdik_joint_bone_name(int p_joint_idx, String p_bone_name);
+ int get_ccdik_joint_bone_index(int p_joint_idx) const;
+ void set_ccdik_joint_bone_index(int p_joint_idx, int p_bone_idx);
+ int get_ccdik_joint_ccdik_axis(int p_joint_idx) const;
+ void set_ccdik_joint_ccdik_axis(int p_joint_idx, int p_axis);
+ bool get_ccdik_joint_enable_constraint(int p_joint_idx) const;
+ void set_ccdik_joint_enable_constraint(int p_joint_idx, bool p_enable);
+ real_t get_ccdik_joint_constraint_angle_min(int p_joint_idx) const;
+ void set_ccdik_joint_constraint_angle_min(int p_joint_idx, real_t p_angle_min);
+ real_t get_ccdik_joint_constraint_angle_max(int p_joint_idx) const;
+ void set_ccdik_joint_constraint_angle_max(int p_joint_idx, real_t p_angle_max);
+ bool get_ccdik_joint_constraint_invert(int p_joint_idx) const;
+ void set_ccdik_joint_constraint_invert(int p_joint_idx, bool p_invert);
+
+ int get_ccdik_data_chain_length();
+ void set_ccdik_data_chain_length(int p_new_length);
+
+ SkeletonModification3DCCDIK();
+ ~SkeletonModification3DCCDIK();
+};
+
+#endif //SKELETONMODIFICATION3DCCDIK_H
diff --git a/scene/resources/skeleton_modification_3d_fabrik.cpp b/scene/resources/skeleton_modification_3d_fabrik.cpp
new file mode 100644
index 0000000000..69f75eb7b5
--- /dev/null
+++ b/scene/resources/skeleton_modification_3d_fabrik.cpp
@@ -0,0 +1,628 @@
+/*************************************************************************/
+/* skeleton_modification_3d_fabrik.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "scene/resources/skeleton_modification_3d_fabrik.h"
+#include "scene/3d/skeleton_3d.h"
+#include "scene/resources/skeleton_modification_3d.h"
+
+bool SkeletonModification3DFABRIK::_set(const StringName &p_path, const Variant &p_value) {
+ String path = p_path;
+
+ if (path.begins_with("joint_data/")) {
+ int fabrik_data_size = fabrik_data_chain.size();
+ int which = path.get_slicec('/', 1).to_int();
+ String what = path.get_slicec('/', 2);
+ ERR_FAIL_INDEX_V(which, fabrik_data_size, false);
+
+ if (what == "bone_name") {
+ set_fabrik_joint_bone_name(which, p_value);
+ } else if (what == "bone_index") {
+ set_fabrik_joint_bone_index(which, p_value);
+ } else if (what == "length") {
+ set_fabrik_joint_length(which, p_value);
+ } else if (what == "magnet_position") {
+ set_fabrik_joint_magnet(which, p_value);
+ } else if (what == "auto_calculate_length") {
+ set_fabrik_joint_auto_calculate_length(which, p_value);
+ } else if (what == "use_tip_node") {
+ set_fabrik_joint_use_tip_node(which, p_value);
+ } else if (what == "tip_node") {
+ set_fabrik_joint_tip_node(which, p_value);
+ } else if (what == "use_target_basis") {
+ set_fabrik_joint_use_target_basis(which, p_value);
+ } else if (what == "roll") {
+ set_fabrik_joint_roll(which, Math::deg2rad(real_t(p_value)));
+ }
+ return true;
+ }
+ return true;
+}
+
+bool SkeletonModification3DFABRIK::_get(const StringName &p_path, Variant &r_ret) const {
+ String path = p_path;
+
+ if (path.begins_with("joint_data/")) {
+ const int fabrik_data_size = fabrik_data_chain.size();
+ int which = path.get_slicec('/', 1).to_int();
+ String what = path.get_slicec('/', 2);
+ ERR_FAIL_INDEX_V(which, fabrik_data_size, false);
+
+ if (what == "bone_name") {
+ r_ret = get_fabrik_joint_bone_name(which);
+ } else if (what == "bone_index") {
+ r_ret = get_fabrik_joint_bone_index(which);
+ } else if (what == "length") {
+ r_ret = get_fabrik_joint_length(which);
+ } else if (what == "magnet_position") {
+ r_ret = get_fabrik_joint_magnet(which);
+ } else if (what == "auto_calculate_length") {
+ r_ret = get_fabrik_joint_auto_calculate_length(which);
+ } else if (what == "use_tip_node") {
+ r_ret = get_fabrik_joint_use_tip_node(which);
+ } else if (what == "tip_node") {
+ r_ret = get_fabrik_joint_tip_node(which);
+ } else if (what == "use_target_basis") {
+ r_ret = get_fabrik_joint_use_target_basis(which);
+ } else if (what == "roll") {
+ r_ret = Math::rad2deg(get_fabrik_joint_roll(which));
+ }
+ return true;
+ }
+ return true;
+}
+
+void SkeletonModification3DFABRIK::_get_property_list(List<PropertyInfo> *p_list) const {
+ for (uint32_t i = 0; i < fabrik_data_chain.size(); i++) {
+ String base_string = "joint_data/" + itos(i) + "/";
+
+ p_list->push_back(PropertyInfo(Variant::STRING_NAME, base_string + "bone_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::INT, base_string + "bone_index", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, base_string + "roll", PROPERTY_HINT_RANGE, "-360,360,0.01", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "auto_calculate_length", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+
+ if (!fabrik_data_chain[i].auto_calculate_length) {
+ p_list->push_back(PropertyInfo(Variant::FLOAT, base_string + "length", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ } else {
+ p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "use_tip_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ if (fabrik_data_chain[i].use_tip_node) {
+ p_list->push_back(PropertyInfo(Variant::NODE_PATH, base_string + "tip_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node3D", PROPERTY_USAGE_DEFAULT));
+ }
+ }
+
+ // Cannot apply magnet to the origin of the chain, as it will not do anything.
+ if (i > 0) {
+ p_list->push_back(PropertyInfo(Variant::VECTOR3, base_string + "magnet_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ }
+ // Only give the override basis option on the last bone in the chain, so only include it for the last bone.
+ if (i == fabrik_data_chain.size() - 1) {
+ p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "use_target_basis", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ }
+ }
+}
+
+void SkeletonModification3DFABRIK::_execute(real_t p_delta) {
+ ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr,
+ "Modification is not setup and therefore cannot execute!");
+ if (!enabled) {
+ return;
+ }
+
+ if (target_node_cache.is_null()) {
+ _print_execution_error(true, "Target cache is out of date. Attempting to update...");
+ update_target_cache();
+ return;
+ }
+
+ if (_print_execution_error(fabrik_data_chain.size() <= 1, "FABRIK requires at least two joints to operate. Cannot execute modification!")) {
+ return;
+ }
+
+ Node3D *node_target = Object::cast_to<Node3D>(ObjectDB::get_instance(target_node_cache));
+ if (_print_execution_error(!node_target || !node_target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!")) {
+ return;
+ }
+
+ // Verify that all joints have a valid bone ID, and that all bone lengths are zero or more
+ // Also, while we are here, apply magnet positions.
+ for (uint32_t i = 0; i < fabrik_data_chain.size(); i++) {
+ if (_print_execution_error(fabrik_data_chain[i].bone_idx < 0, "FABRIK Joint " + itos(i) + " has an invalid bone ID. Cannot execute!")) {
+ return;
+ }
+
+ if (fabrik_data_chain[i].length < 0 && fabrik_data_chain[i].auto_calculate_length) {
+ fabrik_joint_auto_calculate_length(i);
+ }
+ if (_print_execution_error(fabrik_data_chain[i].length < 0, "FABRIK Joint " + itos(i) + " has an invalid joint length. Cannot execute!")) {
+ return;
+ }
+
+ Transform3D local_pose_override = stack->skeleton->get_bone_local_pose_override(fabrik_data_chain[i].bone_idx);
+
+ // Apply magnet positions:
+ if (stack->skeleton->get_bone_parent(fabrik_data_chain[i].bone_idx) >= 0) {
+ int parent_bone_idx = stack->skeleton->get_bone_parent(fabrik_data_chain[i].bone_idx);
+ Transform3D conversion_transform = (stack->skeleton->get_bone_global_pose(parent_bone_idx) * stack->skeleton->get_bone_rest(parent_bone_idx));
+ local_pose_override.origin += conversion_transform.basis.xform_inv(fabrik_data_chain[i].magnet_position);
+ } else {
+ local_pose_override.origin += fabrik_data_chain[i].magnet_position;
+ }
+
+ stack->skeleton->set_bone_local_pose_override(fabrik_data_chain[i].bone_idx, local_pose_override, stack->strength, true);
+ }
+
+ target_global_pose = stack->skeleton->world_transform_to_global_pose(node_target->get_global_transform());
+ origin_global_pose = stack->skeleton->local_pose_to_global_pose(
+ fabrik_data_chain[0].bone_idx, stack->skeleton->get_bone_local_pose_override(fabrik_data_chain[0].bone_idx));
+
+ final_joint_idx = fabrik_data_chain.size() - 1;
+ real_t target_distance = stack->skeleton->global_pose_to_local_pose(fabrik_data_chain[final_joint_idx].bone_idx, target_global_pose).origin.length();
+ chain_iterations = 0;
+
+ while (target_distance > chain_tolerance) {
+ chain_backwards();
+ chain_forwards();
+
+ // update the target distance
+ target_distance = stack->skeleton->global_pose_to_local_pose(fabrik_data_chain[final_joint_idx].bone_idx, target_global_pose).origin.length();
+
+ // update chain iterations
+ chain_iterations += 1;
+ if (chain_iterations >= chain_max_iterations) {
+ break;
+ }
+ }
+ chain_apply();
+
+ execution_error_found = false;
+}
+
+void SkeletonModification3DFABRIK::chain_backwards() {
+ int final_bone_idx = fabrik_data_chain[final_joint_idx].bone_idx;
+ Transform3D final_joint_trans = stack->skeleton->local_pose_to_global_pose(final_bone_idx, stack->skeleton->get_bone_local_pose_override(final_bone_idx));
+
+ // Get the direction the final bone is facing in.
+ stack->skeleton->update_bone_rest_forward_vector(final_bone_idx);
+ Transform3D final_bone_direction_trans = final_joint_trans.looking_at(target_global_pose.origin, Vector3(0, 1, 0));
+ final_bone_direction_trans.basis = stack->skeleton->global_pose_z_forward_to_bone_forward(final_bone_idx, final_bone_direction_trans.basis);
+ Vector3 direction = final_bone_direction_trans.basis.xform(stack->skeleton->get_bone_axis_forward_vector(final_bone_idx)).normalized();
+
+ // If set to override, then use the target's Basis rather than the bone's
+ if (fabrik_data_chain[final_joint_idx].use_target_basis) {
+ direction = target_global_pose.basis.xform(stack->skeleton->get_bone_axis_forward_vector(final_bone_idx)).normalized();
+ }
+
+ // set the position of the final joint to the target position
+ final_joint_trans.origin = target_global_pose.origin - (direction * fabrik_data_chain[final_joint_idx].length);
+ final_joint_trans = stack->skeleton->global_pose_to_local_pose(final_bone_idx, final_joint_trans);
+ stack->skeleton->set_bone_local_pose_override(final_bone_idx, final_joint_trans, stack->strength, true);
+
+ // for all other joints, move them towards the target
+ int i = final_joint_idx;
+ while (i >= 1) {
+ int next_bone_idx = fabrik_data_chain[i].bone_idx;
+ Transform3D next_bone_trans = stack->skeleton->local_pose_to_global_pose(next_bone_idx, stack->skeleton->get_bone_local_pose_override(next_bone_idx));
+ i -= 1;
+ int current_bone_idx = fabrik_data_chain[i].bone_idx;
+ Transform3D current_trans = stack->skeleton->local_pose_to_global_pose(current_bone_idx, stack->skeleton->get_bone_local_pose_override(current_bone_idx));
+
+ real_t length = fabrik_data_chain[i].length / (next_bone_trans.origin - current_trans.origin).length();
+ current_trans.origin = next_bone_trans.origin.lerp(current_trans.origin, length);
+
+ // Apply it back to the skeleton
+ stack->skeleton->set_bone_local_pose_override(current_bone_idx, stack->skeleton->global_pose_to_local_pose(current_bone_idx, current_trans), stack->strength, true);
+ }
+}
+
+void SkeletonModification3DFABRIK::chain_forwards() {
+ // Set root at the initial position.
+ int origin_bone_idx = fabrik_data_chain[0].bone_idx;
+ Transform3D root_transform = stack->skeleton->local_pose_to_global_pose(origin_bone_idx, stack->skeleton->get_bone_local_pose_override(origin_bone_idx));
+ root_transform.origin = origin_global_pose.origin;
+ stack->skeleton->set_bone_local_pose_override(origin_bone_idx, stack->skeleton->global_pose_to_local_pose(origin_bone_idx, root_transform), stack->strength, true);
+
+ for (uint32_t i = 0; i < fabrik_data_chain.size() - 1; i++) {
+ int current_bone_idx = fabrik_data_chain[i].bone_idx;
+ Transform3D current_trans = stack->skeleton->local_pose_to_global_pose(current_bone_idx, stack->skeleton->get_bone_local_pose_override(current_bone_idx));
+ int next_bone_idx = fabrik_data_chain[i + 1].bone_idx;
+ Transform3D next_bone_trans = stack->skeleton->local_pose_to_global_pose(next_bone_idx, stack->skeleton->get_bone_local_pose_override(next_bone_idx));
+
+ real_t length = fabrik_data_chain[i].length / (current_trans.origin - next_bone_trans.origin).length();
+ next_bone_trans.origin = current_trans.origin.lerp(next_bone_trans.origin, length);
+
+ // Apply it back to the skeleton
+ stack->skeleton->set_bone_local_pose_override(next_bone_idx, stack->skeleton->global_pose_to_local_pose(next_bone_idx, next_bone_trans), stack->strength, true);
+ }
+}
+
+void SkeletonModification3DFABRIK::chain_apply() {
+ for (uint32_t i = 0; i < fabrik_data_chain.size(); i++) {
+ int current_bone_idx = fabrik_data_chain[i].bone_idx;
+ Transform3D current_trans = stack->skeleton->get_bone_local_pose_override(current_bone_idx);
+ current_trans = stack->skeleton->local_pose_to_global_pose(current_bone_idx, current_trans);
+
+ // If this is the last bone in the chain...
+ if (i == fabrik_data_chain.size() - 1) {
+ if (fabrik_data_chain[i].use_target_basis == false) { // Point to target...
+ // Get the forward direction that the basis is facing in right now.
+ stack->skeleton->update_bone_rest_forward_vector(current_bone_idx);
+ Vector3 forward_vector = stack->skeleton->get_bone_axis_forward_vector(current_bone_idx);
+ // Rotate the bone towards the target:
+ current_trans.basis.rotate_to_align(forward_vector, current_trans.origin.direction_to(target_global_pose.origin));
+ current_trans.basis.rotate_local(forward_vector, fabrik_data_chain[i].roll);
+ } else { // Use the target's Basis...
+ current_trans.basis = target_global_pose.basis.orthonormalized().scaled(current_trans.basis.get_scale());
+ }
+ } else { // every other bone in the chain...
+ int next_bone_idx = fabrik_data_chain[i + 1].bone_idx;
+ Transform3D next_trans = stack->skeleton->local_pose_to_global_pose(next_bone_idx, stack->skeleton->get_bone_local_pose_override(next_bone_idx));
+
+ // Get the forward direction that the basis is facing in right now.
+ stack->skeleton->update_bone_rest_forward_vector(current_bone_idx);
+ Vector3 forward_vector = stack->skeleton->get_bone_axis_forward_vector(current_bone_idx);
+ // Rotate the bone towards the next bone in the chain:
+ current_trans.basis.rotate_to_align(forward_vector, current_trans.origin.direction_to(next_trans.origin));
+ current_trans.basis.rotate_local(forward_vector, fabrik_data_chain[i].roll);
+ }
+ current_trans = stack->skeleton->global_pose_to_local_pose(current_bone_idx, current_trans);
+ current_trans.origin = Vector3(0, 0, 0);
+ stack->skeleton->set_bone_local_pose_override(current_bone_idx, current_trans, stack->strength, true);
+ }
+
+ // Update all the bones so the next modification has up-to-date data.
+ stack->skeleton->force_update_all_bone_transforms();
+}
+
+void SkeletonModification3DFABRIK::_setup_modification(SkeletonModificationStack3D *p_stack) {
+ stack = p_stack;
+ if (stack != nullptr) {
+ is_setup = true;
+ execution_error_found = false;
+ update_target_cache();
+
+ for (uint32_t i = 0; i < fabrik_data_chain.size(); i++) {
+ update_joint_tip_cache(i);
+ }
+ }
+}
+
+void SkeletonModification3DFABRIK::update_target_cache() {
+ if (!is_setup || !stack) {
+ _print_execution_error(true, "Cannot update target cache: modification is not properly setup!");
+ return;
+ }
+ target_node_cache = ObjectID();
+ if (stack->skeleton) {
+ if (stack->skeleton->is_inside_tree() && target_node.is_empty() == false) {
+ if (stack->skeleton->has_node(target_node)) {
+ Node *node = stack->skeleton->get_node(target_node);
+ ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
+ "Cannot update target cache: node is this modification's skeleton or cannot be found!");
+ ERR_FAIL_COND_MSG(!node->is_inside_tree(),
+ "Cannot update target cache: node is not in the scene tree!");
+ target_node_cache = node->get_instance_id();
+
+ execution_error_found = false;
+ }
+ }
+ }
+}
+
+void SkeletonModification3DFABRIK::update_joint_tip_cache(int p_joint_idx) {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX_MSG(p_joint_idx, bone_chain_size, "FABRIK joint not found");
+ if (!is_setup || !stack) {
+ _print_execution_error(true, "Cannot update tip cache: modification is not properly setup!");
+ return;
+ }
+ fabrik_data_chain[p_joint_idx].tip_node_cache = ObjectID();
+ if (stack->skeleton) {
+ if (stack->skeleton->is_inside_tree() && fabrik_data_chain[p_joint_idx].tip_node.is_empty() == false) {
+ if (stack->skeleton->has_node(fabrik_data_chain[p_joint_idx].tip_node)) {
+ Node *node = stack->skeleton->get_node(fabrik_data_chain[p_joint_idx].tip_node);
+ ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
+ "Cannot update tip cache for joint " + itos(p_joint_idx) + ": node is this modification's skeleton or cannot be found!");
+ ERR_FAIL_COND_MSG(!node->is_inside_tree(),
+ "Cannot update tip cache for joint " + itos(p_joint_idx) + ": node is not in scene tree!");
+ fabrik_data_chain[p_joint_idx].tip_node_cache = node->get_instance_id();
+
+ execution_error_found = false;
+ }
+ }
+ }
+}
+
+void SkeletonModification3DFABRIK::set_target_node(const NodePath &p_target_node) {
+ target_node = p_target_node;
+ update_target_cache();
+}
+
+NodePath SkeletonModification3DFABRIK::get_target_node() const {
+ return target_node;
+}
+
+int SkeletonModification3DFABRIK::get_fabrik_data_chain_length() {
+ return fabrik_data_chain.size();
+}
+
+void SkeletonModification3DFABRIK::set_fabrik_data_chain_length(int p_length) {
+ ERR_FAIL_COND(p_length < 0);
+ fabrik_data_chain.resize(p_length);
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+real_t SkeletonModification3DFABRIK::get_chain_tolerance() {
+ return chain_tolerance;
+}
+
+void SkeletonModification3DFABRIK::set_chain_tolerance(real_t p_tolerance) {
+ ERR_FAIL_COND_MSG(p_tolerance <= 0, "FABRIK chain tolerance must be more than zero!");
+ chain_tolerance = p_tolerance;
+}
+
+int SkeletonModification3DFABRIK::get_chain_max_iterations() {
+ return chain_max_iterations;
+}
+void SkeletonModification3DFABRIK::set_chain_max_iterations(int p_iterations) {
+ ERR_FAIL_COND_MSG(p_iterations <= 0, "FABRIK chain iterations must be at least one. Set enabled to false to disable the FABRIK chain.");
+ chain_max_iterations = p_iterations;
+}
+
+// FABRIK joint data functions
+String SkeletonModification3DFABRIK::get_fabrik_joint_bone_name(int p_joint_idx) const {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, String());
+ return fabrik_data_chain[p_joint_idx].bone_name;
+}
+
+void SkeletonModification3DFABRIK::set_fabrik_joint_bone_name(int p_joint_idx, String p_bone_name) {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ fabrik_data_chain[p_joint_idx].bone_name = p_bone_name;
+
+ if (stack) {
+ if (stack->skeleton) {
+ fabrik_data_chain[p_joint_idx].bone_idx = stack->skeleton->find_bone(p_bone_name);
+ }
+ }
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+int SkeletonModification3DFABRIK::get_fabrik_joint_bone_index(int p_joint_idx) const {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, -1);
+ return fabrik_data_chain[p_joint_idx].bone_idx;
+}
+
+void SkeletonModification3DFABRIK::set_fabrik_joint_bone_index(int p_joint_idx, int p_bone_idx) {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!");
+ fabrik_data_chain[p_joint_idx].bone_idx = p_bone_idx;
+
+ if (stack) {
+ if (stack->skeleton) {
+ fabrik_data_chain[p_joint_idx].bone_name = stack->skeleton->get_bone_name(p_bone_idx);
+ }
+ }
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+real_t SkeletonModification3DFABRIK::get_fabrik_joint_length(int p_joint_idx) const {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, -1);
+ return fabrik_data_chain[p_joint_idx].length;
+}
+
+void SkeletonModification3DFABRIK::set_fabrik_joint_length(int p_joint_idx, real_t p_bone_length) {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ ERR_FAIL_COND_MSG(p_bone_length < 0, "FABRIK joint length cannot be less than zero!");
+
+ if (!is_setup) {
+ fabrik_data_chain[p_joint_idx].length = p_bone_length;
+ return;
+ }
+
+ if (fabrik_data_chain[p_joint_idx].auto_calculate_length) {
+ WARN_PRINT("FABRIK Length not set: auto calculate length is enabled for this joint!");
+ fabrik_joint_auto_calculate_length(p_joint_idx);
+ } else {
+ fabrik_data_chain[p_joint_idx].length = p_bone_length;
+ }
+
+ execution_error_found = false;
+}
+
+Vector3 SkeletonModification3DFABRIK::get_fabrik_joint_magnet(int p_joint_idx) const {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, Vector3());
+ return fabrik_data_chain[p_joint_idx].magnet_position;
+}
+
+void SkeletonModification3DFABRIK::set_fabrik_joint_magnet(int p_joint_idx, Vector3 p_magnet) {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ fabrik_data_chain[p_joint_idx].magnet_position = p_magnet;
+}
+
+bool SkeletonModification3DFABRIK::get_fabrik_joint_auto_calculate_length(int p_joint_idx) const {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, false);
+ return fabrik_data_chain[p_joint_idx].auto_calculate_length;
+}
+
+void SkeletonModification3DFABRIK::set_fabrik_joint_auto_calculate_length(int p_joint_idx, bool p_auto_calculate) {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ fabrik_data_chain[p_joint_idx].auto_calculate_length = p_auto_calculate;
+ fabrik_joint_auto_calculate_length(p_joint_idx);
+ notify_property_list_changed();
+}
+
+void SkeletonModification3DFABRIK::fabrik_joint_auto_calculate_length(int p_joint_idx) {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ if (!fabrik_data_chain[p_joint_idx].auto_calculate_length) {
+ return;
+ }
+
+ if (!stack || !stack->skeleton || !is_setup) {
+ _print_execution_error(true, "Cannot auto calculate joint length: modification is not properly setup!");
+ return;
+ }
+ ERR_FAIL_INDEX_MSG(fabrik_data_chain[p_joint_idx].bone_idx, stack->skeleton->get_bone_count(),
+ "Bone for joint " + itos(p_joint_idx) + " is not set or points to an unknown bone!");
+
+ if (fabrik_data_chain[p_joint_idx].use_tip_node) { // Use the tip node to update joint length.
+
+ update_joint_tip_cache(p_joint_idx);
+
+ Node3D *tip_node = Object::cast_to<Node3D>(ObjectDB::get_instance(fabrik_data_chain[p_joint_idx].tip_node_cache));
+ ERR_FAIL_COND_MSG(!tip_node, "Tip node for joint " + itos(p_joint_idx) + "is not a Node3D-based node. Cannot calculate length...");
+ ERR_FAIL_COND_MSG(!tip_node->is_inside_tree(), "Tip node for joint " + itos(p_joint_idx) + "is not in the scene tree. Cannot calculate length...");
+
+ Transform3D node_trans = tip_node->get_global_transform();
+ node_trans = stack->skeleton->world_transform_to_global_pose(node_trans);
+ node_trans = stack->skeleton->global_pose_to_local_pose(fabrik_data_chain[p_joint_idx].bone_idx, node_trans);
+ fabrik_data_chain[p_joint_idx].length = node_trans.origin.length();
+ } else { // Use child bone(s) to update joint length, if possible
+ Vector<int> bone_children = stack->skeleton->get_bone_children(fabrik_data_chain[p_joint_idx].bone_idx);
+ if (bone_children.size() <= 0) {
+ ERR_FAIL_MSG("Cannot calculate length for joint " + itos(p_joint_idx) + "joint uses leaf bone. \nPlease manually set the bone length or use a tip node!");
+ return;
+ }
+
+ real_t final_length = 0;
+ for (int i = 0; i < bone_children.size(); i++) {
+ Transform3D child_transform = stack->skeleton->get_bone_global_pose(bone_children[i]);
+ final_length += stack->skeleton->global_pose_to_local_pose(fabrik_data_chain[p_joint_idx].bone_idx, child_transform).origin.length();
+ }
+ fabrik_data_chain[p_joint_idx].length = final_length / bone_children.size();
+ }
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+bool SkeletonModification3DFABRIK::get_fabrik_joint_use_tip_node(int p_joint_idx) const {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, false);
+ return fabrik_data_chain[p_joint_idx].use_tip_node;
+}
+
+void SkeletonModification3DFABRIK::set_fabrik_joint_use_tip_node(int p_joint_idx, bool p_use_tip_node) {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ fabrik_data_chain[p_joint_idx].use_tip_node = p_use_tip_node;
+ notify_property_list_changed();
+}
+
+NodePath SkeletonModification3DFABRIK::get_fabrik_joint_tip_node(int p_joint_idx) const {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, NodePath());
+ return fabrik_data_chain[p_joint_idx].tip_node;
+}
+
+void SkeletonModification3DFABRIK::set_fabrik_joint_tip_node(int p_joint_idx, NodePath p_tip_node) {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ fabrik_data_chain[p_joint_idx].tip_node = p_tip_node;
+ update_joint_tip_cache(p_joint_idx);
+}
+
+bool SkeletonModification3DFABRIK::get_fabrik_joint_use_target_basis(int p_joint_idx) const {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, false);
+ return fabrik_data_chain[p_joint_idx].use_target_basis;
+}
+
+void SkeletonModification3DFABRIK::set_fabrik_joint_use_target_basis(int p_joint_idx, bool p_use_target_basis) {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ fabrik_data_chain[p_joint_idx].use_target_basis = p_use_target_basis;
+}
+
+real_t SkeletonModification3DFABRIK::get_fabrik_joint_roll(int p_joint_idx) const {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, 0.0);
+ return fabrik_data_chain[p_joint_idx].roll;
+}
+
+void SkeletonModification3DFABRIK::set_fabrik_joint_roll(int p_joint_idx, real_t p_roll) {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ fabrik_data_chain[p_joint_idx].roll = p_roll;
+}
+
+void SkeletonModification3DFABRIK::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_target_node", "target_nodepath"), &SkeletonModification3DFABRIK::set_target_node);
+ ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonModification3DFABRIK::get_target_node);
+ ClassDB::bind_method(D_METHOD("set_fabrik_data_chain_length", "length"), &SkeletonModification3DFABRIK::set_fabrik_data_chain_length);
+ ClassDB::bind_method(D_METHOD("get_fabrik_data_chain_length"), &SkeletonModification3DFABRIK::get_fabrik_data_chain_length);
+ ClassDB::bind_method(D_METHOD("set_chain_tolerance", "tolerance"), &SkeletonModification3DFABRIK::set_chain_tolerance);
+ ClassDB::bind_method(D_METHOD("get_chain_tolerance"), &SkeletonModification3DFABRIK::get_chain_tolerance);
+ ClassDB::bind_method(D_METHOD("set_chain_max_iterations", "max_iterations"), &SkeletonModification3DFABRIK::set_chain_max_iterations);
+ ClassDB::bind_method(D_METHOD("get_chain_max_iterations"), &SkeletonModification3DFABRIK::get_chain_max_iterations);
+
+ // FABRIK joint data functions
+ ClassDB::bind_method(D_METHOD("get_fabrik_joint_bone_name", "joint_idx"), &SkeletonModification3DFABRIK::get_fabrik_joint_bone_name);
+ ClassDB::bind_method(D_METHOD("set_fabrik_joint_bone_name", "joint_idx", "bone_name"), &SkeletonModification3DFABRIK::set_fabrik_joint_bone_name);
+ ClassDB::bind_method(D_METHOD("get_fabrik_joint_bone_index", "joint_idx"), &SkeletonModification3DFABRIK::get_fabrik_joint_bone_index);
+ ClassDB::bind_method(D_METHOD("set_fabrik_joint_bone_index", "joint_idx", "bone_index"), &SkeletonModification3DFABRIK::set_fabrik_joint_bone_index);
+ ClassDB::bind_method(D_METHOD("get_fabrik_joint_length", "joint_idx"), &SkeletonModification3DFABRIK::get_fabrik_joint_length);
+ ClassDB::bind_method(D_METHOD("set_fabrik_joint_length", "joint_idx", "length"), &SkeletonModification3DFABRIK::set_fabrik_joint_length);
+ ClassDB::bind_method(D_METHOD("get_fabrik_joint_magnet", "joint_idx"), &SkeletonModification3DFABRIK::get_fabrik_joint_magnet);
+ ClassDB::bind_method(D_METHOD("set_fabrik_joint_magnet", "joint_idx", "magnet_position"), &SkeletonModification3DFABRIK::set_fabrik_joint_magnet);
+ ClassDB::bind_method(D_METHOD("get_fabrik_joint_auto_calculate_length", "joint_idx"), &SkeletonModification3DFABRIK::get_fabrik_joint_auto_calculate_length);
+ ClassDB::bind_method(D_METHOD("set_fabrik_joint_auto_calculate_length", "joint_idx", "auto_calculate_length"), &SkeletonModification3DFABRIK::set_fabrik_joint_auto_calculate_length);
+ ClassDB::bind_method(D_METHOD("fabrik_joint_auto_calculate_length", "joint_idx"), &SkeletonModification3DFABRIK::fabrik_joint_auto_calculate_length);
+ ClassDB::bind_method(D_METHOD("get_fabrik_joint_use_tip_node", "joint_idx"), &SkeletonModification3DFABRIK::get_fabrik_joint_use_tip_node);
+ ClassDB::bind_method(D_METHOD("set_fabrik_joint_use_tip_node", "joint_idx", "use_tip_node"), &SkeletonModification3DFABRIK::set_fabrik_joint_use_tip_node);
+ ClassDB::bind_method(D_METHOD("get_fabrik_joint_tip_node", "joint_idx"), &SkeletonModification3DFABRIK::get_fabrik_joint_tip_node);
+ ClassDB::bind_method(D_METHOD("set_fabrik_joint_tip_node", "joint_idx", "tip_node"), &SkeletonModification3DFABRIK::set_fabrik_joint_tip_node);
+ ClassDB::bind_method(D_METHOD("get_fabrik_joint_use_target_basis", "joint_idx"), &SkeletonModification3DFABRIK::get_fabrik_joint_use_target_basis);
+ ClassDB::bind_method(D_METHOD("set_fabrik_joint_use_target_basis", "joint_idx", "use_target_basis"), &SkeletonModification3DFABRIK::set_fabrik_joint_use_target_basis);
+
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node3D"), "set_target_node", "get_target_node");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "fabrik_data_chain_length", PROPERTY_HINT_RANGE, "0,100,1"), "set_fabrik_data_chain_length", "get_fabrik_data_chain_length");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "chain_tolerance", PROPERTY_HINT_RANGE, "0,100,0.001"), "set_chain_tolerance", "get_chain_tolerance");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "chain_max_iterations", PROPERTY_HINT_RANGE, "1,50,1"), "set_chain_max_iterations", "get_chain_max_iterations");
+}
+
+SkeletonModification3DFABRIK::SkeletonModification3DFABRIK() {
+ stack = nullptr;
+ is_setup = false;
+ enabled = true;
+}
+
+SkeletonModification3DFABRIK::~SkeletonModification3DFABRIK() {
+}
diff --git a/scene/resources/skeleton_modification_3d_fabrik.h b/scene/resources/skeleton_modification_3d_fabrik.h
new file mode 100644
index 0000000000..9b5da883d4
--- /dev/null
+++ b/scene/resources/skeleton_modification_3d_fabrik.h
@@ -0,0 +1,122 @@
+/*************************************************************************/
+/* skeleton_modification_3d_fabrik.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "core/templates/local_vector.h"
+#include "scene/3d/skeleton_3d.h"
+#include "scene/resources/skeleton_modification_3d.h"
+
+#ifndef SKELETONMODIFICATION3DFABRIK_H
+#define SKELETONMODIFICATION3DFABRIK_H
+
+class SkeletonModification3DFABRIK : public SkeletonModification3D {
+ GDCLASS(SkeletonModification3DFABRIK, SkeletonModification3D);
+
+private:
+ struct FABRIK_Joint_Data {
+ String bone_name = "";
+ int bone_idx = -1;
+ real_t length = -1;
+ Vector3 magnet_position = Vector3(0, 0, 0);
+
+ bool auto_calculate_length = true;
+ bool use_tip_node = false;
+ NodePath tip_node = NodePath();
+ ObjectID tip_node_cache;
+
+ bool use_target_basis = false;
+ real_t roll = 0;
+ };
+
+ LocalVector<FABRIK_Joint_Data> fabrik_data_chain;
+ NodePath target_node;
+ ObjectID target_node_cache;
+
+ real_t chain_tolerance = 0.01;
+ int chain_max_iterations = 10;
+ int chain_iterations = 0;
+
+ void update_target_cache();
+ void update_joint_tip_cache(int p_joint_idx);
+
+ int final_joint_idx = 0;
+ Transform3D target_global_pose = Transform3D();
+ Transform3D origin_global_pose = Transform3D();
+
+ void chain_backwards();
+ void chain_forwards();
+ void chain_apply();
+
+protected:
+ static void _bind_methods();
+ bool _get(const StringName &p_path, Variant &r_ret) const;
+ bool _set(const StringName &p_path, const Variant &p_value);
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
+public:
+ virtual void _execute(real_t p_delta) override;
+ virtual void _setup_modification(SkeletonModificationStack3D *p_stack) override;
+
+ void set_target_node(const NodePath &p_target_node);
+ NodePath get_target_node() const;
+
+ int get_fabrik_data_chain_length();
+ void set_fabrik_data_chain_length(int p_new_length);
+
+ real_t get_chain_tolerance();
+ void set_chain_tolerance(real_t p_tolerance);
+
+ int get_chain_max_iterations();
+ void set_chain_max_iterations(int p_iterations);
+
+ String get_fabrik_joint_bone_name(int p_joint_idx) const;
+ void set_fabrik_joint_bone_name(int p_joint_idx, String p_bone_name);
+ int get_fabrik_joint_bone_index(int p_joint_idx) const;
+ void set_fabrik_joint_bone_index(int p_joint_idx, int p_bone_idx);
+ real_t get_fabrik_joint_length(int p_joint_idx) const;
+ void set_fabrik_joint_length(int p_joint_idx, real_t p_bone_length);
+ Vector3 get_fabrik_joint_magnet(int p_joint_idx) const;
+ void set_fabrik_joint_magnet(int p_joint_idx, Vector3 p_magnet);
+ bool get_fabrik_joint_auto_calculate_length(int p_joint_idx) const;
+ void set_fabrik_joint_auto_calculate_length(int p_joint_idx, bool p_auto_calculate);
+ void fabrik_joint_auto_calculate_length(int p_joint_idx);
+ bool get_fabrik_joint_use_tip_node(int p_joint_idx) const;
+ void set_fabrik_joint_use_tip_node(int p_joint_idx, bool p_use_tip_node);
+ NodePath get_fabrik_joint_tip_node(int p_joint_idx) const;
+ void set_fabrik_joint_tip_node(int p_joint_idx, NodePath p_tip_node);
+ bool get_fabrik_joint_use_target_basis(int p_joint_idx) const;
+ void set_fabrik_joint_use_target_basis(int p_joint_idx, bool p_use_basis);
+ real_t get_fabrik_joint_roll(int p_joint_idx) const;
+ void set_fabrik_joint_roll(int p_joint_idx, real_t p_roll);
+
+ SkeletonModification3DFABRIK();
+ ~SkeletonModification3DFABRIK();
+};
+
+#endif //SKELETONMODIFICATION3DFABRIK_H
diff --git a/scene/resources/skeleton_modification_3d_jiggle.cpp b/scene/resources/skeleton_modification_3d_jiggle.cpp
new file mode 100644
index 0000000000..1fb7dad2ad
--- /dev/null
+++ b/scene/resources/skeleton_modification_3d_jiggle.cpp
@@ -0,0 +1,573 @@
+/*************************************************************************/
+/* skeleton_modification_3d_jiggle.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "scene/resources/skeleton_modification_3d_jiggle.h"
+#include "scene/3d/skeleton_3d.h"
+#include "scene/resources/skeleton_modification_3d.h"
+
+bool SkeletonModification3DJiggle::_set(const StringName &p_path, const Variant &p_value) {
+ String path = p_path;
+
+ if (path.begins_with("joint_data/")) {
+ const int jiggle_size = jiggle_data_chain.size();
+ int which = path.get_slicec('/', 1).to_int();
+ String what = path.get_slicec('/', 2);
+ ERR_FAIL_INDEX_V(which, jiggle_size, false);
+
+ if (what == "bone_name") {
+ set_jiggle_joint_bone_name(which, p_value);
+ } else if (what == "bone_index") {
+ set_jiggle_joint_bone_index(which, p_value);
+ } else if (what == "override_defaults") {
+ set_jiggle_joint_override(which, p_value);
+ } else if (what == "stiffness") {
+ set_jiggle_joint_stiffness(which, p_value);
+ } else if (what == "mass") {
+ set_jiggle_joint_mass(which, p_value);
+ } else if (what == "damping") {
+ set_jiggle_joint_damping(which, p_value);
+ } else if (what == "use_gravity") {
+ set_jiggle_joint_use_gravity(which, p_value);
+ } else if (what == "gravity") {
+ set_jiggle_joint_gravity(which, p_value);
+ } else if (what == "roll") {
+ set_jiggle_joint_roll(which, Math::deg2rad(real_t(p_value)));
+ }
+ return true;
+ } else {
+ if (path == "use_colliders") {
+ set_use_colliders(p_value);
+ } else if (path == "collision_mask") {
+ set_collision_mask(p_value);
+ }
+ return true;
+ }
+ return true;
+}
+
+bool SkeletonModification3DJiggle::_get(const StringName &p_path, Variant &r_ret) const {
+ String path = p_path;
+
+ if (path.begins_with("joint_data/")) {
+ const int jiggle_size = jiggle_data_chain.size();
+ int which = path.get_slicec('/', 1).to_int();
+ String what = path.get_slicec('/', 2);
+ ERR_FAIL_INDEX_V(which, jiggle_size, false);
+
+ if (what == "bone_name") {
+ r_ret = get_jiggle_joint_bone_name(which);
+ } else if (what == "bone_index") {
+ r_ret = get_jiggle_joint_bone_index(which);
+ } else if (what == "override_defaults") {
+ r_ret = get_jiggle_joint_override(which);
+ } else if (what == "stiffness") {
+ r_ret = get_jiggle_joint_stiffness(which);
+ } else if (what == "mass") {
+ r_ret = get_jiggle_joint_mass(which);
+ } else if (what == "damping") {
+ r_ret = get_jiggle_joint_damping(which);
+ } else if (what == "use_gravity") {
+ r_ret = get_jiggle_joint_use_gravity(which);
+ } else if (what == "gravity") {
+ r_ret = get_jiggle_joint_gravity(which);
+ } else if (what == "roll") {
+ r_ret = Math::rad2deg(get_jiggle_joint_roll(which));
+ }
+ return true;
+ } else {
+ if (path == "use_colliders") {
+ r_ret = get_use_colliders();
+ } else if (path == "collision_mask") {
+ r_ret = get_collision_mask();
+ }
+ return true;
+ }
+ return true;
+}
+
+void SkeletonModification3DJiggle::_get_property_list(List<PropertyInfo> *p_list) const {
+ p_list->push_back(PropertyInfo(Variant::BOOL, "use_colliders", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ if (use_colliders) {
+ p_list->push_back(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS, "", PROPERTY_USAGE_DEFAULT));
+ }
+
+ for (uint32_t i = 0; i < jiggle_data_chain.size(); i++) {
+ String base_string = "joint_data/" + itos(i) + "/";
+
+ p_list->push_back(PropertyInfo(Variant::STRING_NAME, base_string + "bone_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::INT, base_string + "bone_index", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, base_string + "roll", PROPERTY_HINT_RANGE, "-360,360,0.01", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "override_defaults", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+
+ if (jiggle_data_chain[i].override_defaults) {
+ p_list->push_back(PropertyInfo(Variant::FLOAT, base_string + "stiffness", PROPERTY_HINT_RANGE, "0, 1000, 0.01", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, base_string + "mass", PROPERTY_HINT_RANGE, "0, 1000, 0.01", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, base_string + "damping", PROPERTY_HINT_RANGE, "0, 1, 0.01", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "use_gravity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ if (jiggle_data_chain[i].use_gravity) {
+ p_list->push_back(PropertyInfo(Variant::VECTOR3, base_string + "gravity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ }
+ }
+ }
+}
+
+void SkeletonModification3DJiggle::_execute(real_t p_delta) {
+ ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr,
+ "Modification is not setup and therefore cannot execute!");
+ if (!enabled) {
+ return;
+ }
+ if (target_node_cache.is_null()) {
+ _print_execution_error(true, "Target cache is out of date. Attempting to update...");
+ update_cache();
+ return;
+ }
+ Node3D *target = Object::cast_to<Node3D>(ObjectDB::get_instance(target_node_cache));
+ _print_execution_error(!target || !target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!");
+
+ for (uint32_t i = 0; i < jiggle_data_chain.size(); i++) {
+ _execute_jiggle_joint(i, target, p_delta);
+ }
+
+ execution_error_found = false;
+}
+
+void SkeletonModification3DJiggle::_execute_jiggle_joint(int p_joint_idx, Node3D *p_target, real_t p_delta) {
+ // Adopted from: https://wiki.unity3d.com/index.php/JiggleBone
+ // With modifications by TwistedTwigleg.
+
+ if (jiggle_data_chain[p_joint_idx].bone_idx <= -2) {
+ jiggle_data_chain[p_joint_idx].bone_idx = stack->skeleton->find_bone(jiggle_data_chain[p_joint_idx].bone_name);
+ }
+ if (_print_execution_error(
+ jiggle_data_chain[p_joint_idx].bone_idx < 0 || jiggle_data_chain[p_joint_idx].bone_idx > stack->skeleton->get_bone_count(),
+ "Jiggle joint " + itos(p_joint_idx) + " bone index is invald. Cannot execute modification!")) {
+ return;
+ }
+
+ Transform3D new_bone_trans = stack->skeleton->local_pose_to_global_pose(jiggle_data_chain[p_joint_idx].bone_idx, stack->skeleton->get_bone_local_pose_override(jiggle_data_chain[p_joint_idx].bone_idx));
+ Vector3 target_position = stack->skeleton->world_transform_to_global_pose(p_target->get_global_transform()).origin;
+
+ jiggle_data_chain[p_joint_idx].force = (target_position - jiggle_data_chain[p_joint_idx].dynamic_position) * jiggle_data_chain[p_joint_idx].stiffness * p_delta;
+
+ if (jiggle_data_chain[p_joint_idx].use_gravity) {
+ Vector3 gravity_to_apply = new_bone_trans.basis.inverse().xform(jiggle_data_chain[p_joint_idx].gravity);
+ jiggle_data_chain[p_joint_idx].force += gravity_to_apply * p_delta;
+ }
+
+ jiggle_data_chain[p_joint_idx].acceleration = jiggle_data_chain[p_joint_idx].force / jiggle_data_chain[p_joint_idx].mass;
+ jiggle_data_chain[p_joint_idx].velocity += jiggle_data_chain[p_joint_idx].acceleration * (1 - jiggle_data_chain[p_joint_idx].damping);
+
+ jiggle_data_chain[p_joint_idx].dynamic_position += jiggle_data_chain[p_joint_idx].velocity + jiggle_data_chain[p_joint_idx].force;
+ jiggle_data_chain[p_joint_idx].dynamic_position += new_bone_trans.origin - jiggle_data_chain[p_joint_idx].last_position;
+ jiggle_data_chain[p_joint_idx].last_position = new_bone_trans.origin;
+
+ // Collision detection/response
+ if (use_colliders) {
+ if (execution_mode == SkeletonModificationStack3D::EXECUTION_MODE::execution_mode_physics_process) {
+ Ref<World3D> world_3d = stack->skeleton->get_world_3d();
+ ERR_FAIL_COND(world_3d.is_null());
+ PhysicsDirectSpaceState3D *space_state = PhysicsServer3D::get_singleton()->space_get_direct_state(world_3d->get_space());
+ PhysicsDirectSpaceState3D::RayResult ray_result;
+
+ // Convert to world transforms, which is what the physics server needs
+ Transform3D new_bone_trans_world = stack->skeleton->global_pose_to_world_transform(new_bone_trans);
+ Transform3D dynamic_position_world = stack->skeleton->global_pose_to_world_transform(Transform3D(Basis(), jiggle_data_chain[p_joint_idx].dynamic_position));
+
+ bool ray_hit = space_state->intersect_ray(new_bone_trans_world.origin, dynamic_position_world.get_origin(),
+ ray_result, Set<RID>(), collision_mask);
+
+ if (ray_hit) {
+ jiggle_data_chain[p_joint_idx].dynamic_position = jiggle_data_chain[p_joint_idx].last_noncollision_position;
+ jiggle_data_chain[p_joint_idx].acceleration = Vector3(0, 0, 0);
+ jiggle_data_chain[p_joint_idx].velocity = Vector3(0, 0, 0);
+ } else {
+ jiggle_data_chain[p_joint_idx].last_noncollision_position = jiggle_data_chain[p_joint_idx].dynamic_position;
+ }
+
+ } else {
+ WARN_PRINT_ONCE("Jiggle modifier: You cannot detect colliders without the stack mode being set to _physics_process!");
+ }
+ }
+
+ // Get the forward direction that the basis is facing in right now.
+ stack->skeleton->update_bone_rest_forward_vector(jiggle_data_chain[p_joint_idx].bone_idx);
+ Vector3 forward_vector = stack->skeleton->get_bone_axis_forward_vector(jiggle_data_chain[p_joint_idx].bone_idx);
+
+ // Rotate the bone using the dynamic position!
+ new_bone_trans.basis.rotate_to_align(forward_vector, new_bone_trans.origin.direction_to(jiggle_data_chain[p_joint_idx].dynamic_position));
+
+ // Roll
+ new_bone_trans.basis.rotate_local(forward_vector, jiggle_data_chain[p_joint_idx].roll);
+
+ new_bone_trans = stack->skeleton->global_pose_to_local_pose(jiggle_data_chain[p_joint_idx].bone_idx, new_bone_trans);
+ stack->skeleton->set_bone_local_pose_override(jiggle_data_chain[p_joint_idx].bone_idx, new_bone_trans, stack->strength, true);
+ stack->skeleton->force_update_bone_children_transforms(jiggle_data_chain[p_joint_idx].bone_idx);
+}
+
+void SkeletonModification3DJiggle::_update_jiggle_joint_data() {
+ for (uint32_t i = 0; i < jiggle_data_chain.size(); i++) {
+ if (!jiggle_data_chain[i].override_defaults) {
+ set_jiggle_joint_stiffness(i, stiffness);
+ set_jiggle_joint_mass(i, mass);
+ set_jiggle_joint_damping(i, damping);
+ set_jiggle_joint_use_gravity(i, use_gravity);
+ set_jiggle_joint_gravity(i, gravity);
+ }
+ }
+}
+
+void SkeletonModification3DJiggle::_setup_modification(SkeletonModificationStack3D *p_stack) {
+ stack = p_stack;
+
+ if (stack) {
+ is_setup = true;
+ execution_error_found = false;
+
+ if (stack->skeleton) {
+ for (uint32_t i = 0; i < jiggle_data_chain.size(); i++) {
+ int bone_idx = jiggle_data_chain[i].bone_idx;
+ if (bone_idx > 0 && bone_idx < stack->skeleton->get_bone_count()) {
+ jiggle_data_chain[i].dynamic_position = stack->skeleton->local_pose_to_global_pose(bone_idx, stack->skeleton->get_bone_local_pose_override(bone_idx)).origin;
+ }
+ }
+ }
+
+ update_cache();
+ }
+}
+
+void SkeletonModification3DJiggle::update_cache() {
+ if (!is_setup || !stack) {
+ _print_execution_error(true, "Cannot update target cache: modification is not properly setup!");
+ return;
+ }
+
+ target_node_cache = ObjectID();
+ if (stack->skeleton) {
+ if (stack->skeleton->is_inside_tree()) {
+ if (stack->skeleton->has_node(target_node)) {
+ Node *node = stack->skeleton->get_node(target_node);
+ ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
+ "Cannot update target cache: node is this modification's skeleton or cannot be found!");
+ ERR_FAIL_COND_MSG(!node->is_inside_tree(),
+ "Cannot update target cache: node is not in the scene tree!");
+ target_node_cache = node->get_instance_id();
+
+ execution_error_found = false;
+ }
+ }
+ }
+}
+
+void SkeletonModification3DJiggle::set_target_node(const NodePath &p_target_node) {
+ target_node = p_target_node;
+ update_cache();
+}
+
+NodePath SkeletonModification3DJiggle::get_target_node() const {
+ return target_node;
+}
+
+void SkeletonModification3DJiggle::set_stiffness(real_t p_stiffness) {
+ ERR_FAIL_COND_MSG(p_stiffness < 0, "Stiffness cannot be set to a negative value!");
+ stiffness = p_stiffness;
+ _update_jiggle_joint_data();
+}
+
+real_t SkeletonModification3DJiggle::get_stiffness() const {
+ return stiffness;
+}
+
+void SkeletonModification3DJiggle::set_mass(real_t p_mass) {
+ ERR_FAIL_COND_MSG(p_mass < 0, "Mass cannot be set to a negative value!");
+ mass = p_mass;
+ _update_jiggle_joint_data();
+}
+
+real_t SkeletonModification3DJiggle::get_mass() const {
+ return mass;
+}
+
+void SkeletonModification3DJiggle::set_damping(real_t p_damping) {
+ ERR_FAIL_COND_MSG(p_damping < 0, "Damping cannot be set to a negative value!");
+ ERR_FAIL_COND_MSG(p_damping > 1, "Damping cannot be more than one!");
+ damping = p_damping;
+ _update_jiggle_joint_data();
+}
+
+real_t SkeletonModification3DJiggle::get_damping() const {
+ return damping;
+}
+
+void SkeletonModification3DJiggle::set_use_gravity(bool p_use_gravity) {
+ use_gravity = p_use_gravity;
+ _update_jiggle_joint_data();
+}
+
+bool SkeletonModification3DJiggle::get_use_gravity() const {
+ return use_gravity;
+}
+
+void SkeletonModification3DJiggle::set_gravity(Vector3 p_gravity) {
+ gravity = p_gravity;
+ _update_jiggle_joint_data();
+}
+
+Vector3 SkeletonModification3DJiggle::get_gravity() const {
+ return gravity;
+}
+
+void SkeletonModification3DJiggle::set_use_colliders(bool p_use_collider) {
+ use_colliders = p_use_collider;
+ notify_property_list_changed();
+}
+
+bool SkeletonModification3DJiggle::get_use_colliders() const {
+ return use_colliders;
+}
+
+void SkeletonModification3DJiggle::set_collision_mask(int p_mask) {
+ collision_mask = p_mask;
+}
+
+int SkeletonModification3DJiggle::get_collision_mask() const {
+ return collision_mask;
+}
+
+// Jiggle joint data functions
+int SkeletonModification3DJiggle::get_jiggle_data_chain_length() {
+ return jiggle_data_chain.size();
+}
+
+void SkeletonModification3DJiggle::set_jiggle_data_chain_length(int p_length) {
+ ERR_FAIL_COND(p_length < 0);
+ jiggle_data_chain.resize(p_length);
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+void SkeletonModification3DJiggle::set_jiggle_joint_bone_name(int p_joint_idx, String p_name) {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+
+ jiggle_data_chain[p_joint_idx].bone_name = p_name;
+ if (stack && stack->skeleton) {
+ jiggle_data_chain[p_joint_idx].bone_idx = stack->skeleton->find_bone(p_name);
+ }
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+String SkeletonModification3DJiggle::get_jiggle_joint_bone_name(int p_joint_idx) const {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, "");
+ return jiggle_data_chain[p_joint_idx].bone_name;
+}
+
+int SkeletonModification3DJiggle::get_jiggle_joint_bone_index(int p_joint_idx) const {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, -1);
+ return jiggle_data_chain[p_joint_idx].bone_idx;
+}
+
+void SkeletonModification3DJiggle::set_jiggle_joint_bone_index(int p_joint_idx, int p_bone_idx) {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!");
+ jiggle_data_chain[p_joint_idx].bone_idx = p_bone_idx;
+
+ if (stack) {
+ if (stack->skeleton) {
+ jiggle_data_chain[p_joint_idx].bone_name = stack->skeleton->get_bone_name(p_bone_idx);
+ }
+ }
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+void SkeletonModification3DJiggle::set_jiggle_joint_override(int p_joint_idx, bool p_override) {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ jiggle_data_chain[p_joint_idx].override_defaults = p_override;
+ _update_jiggle_joint_data();
+ notify_property_list_changed();
+}
+
+bool SkeletonModification3DJiggle::get_jiggle_joint_override(int p_joint_idx) const {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, false);
+ return jiggle_data_chain[p_joint_idx].override_defaults;
+}
+
+void SkeletonModification3DJiggle::set_jiggle_joint_stiffness(int p_joint_idx, real_t p_stiffness) {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_COND_MSG(p_stiffness < 0, "Stiffness cannot be set to a negative value!");
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ jiggle_data_chain[p_joint_idx].stiffness = p_stiffness;
+}
+
+real_t SkeletonModification3DJiggle::get_jiggle_joint_stiffness(int p_joint_idx) const {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, -1);
+ return jiggle_data_chain[p_joint_idx].stiffness;
+}
+
+void SkeletonModification3DJiggle::set_jiggle_joint_mass(int p_joint_idx, real_t p_mass) {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_COND_MSG(p_mass < 0, "Mass cannot be set to a negative value!");
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ jiggle_data_chain[p_joint_idx].mass = p_mass;
+}
+
+real_t SkeletonModification3DJiggle::get_jiggle_joint_mass(int p_joint_idx) const {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, -1);
+ return jiggle_data_chain[p_joint_idx].mass;
+}
+
+void SkeletonModification3DJiggle::set_jiggle_joint_damping(int p_joint_idx, real_t p_damping) {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_COND_MSG(p_damping < 0, "Damping cannot be set to a negative value!");
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ jiggle_data_chain[p_joint_idx].damping = p_damping;
+}
+
+real_t SkeletonModification3DJiggle::get_jiggle_joint_damping(int p_joint_idx) const {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, -1);
+ return jiggle_data_chain[p_joint_idx].damping;
+}
+
+void SkeletonModification3DJiggle::set_jiggle_joint_use_gravity(int p_joint_idx, bool p_use_gravity) {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ jiggle_data_chain[p_joint_idx].use_gravity = p_use_gravity;
+ notify_property_list_changed();
+}
+
+bool SkeletonModification3DJiggle::get_jiggle_joint_use_gravity(int p_joint_idx) const {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, false);
+ return jiggle_data_chain[p_joint_idx].use_gravity;
+}
+
+void SkeletonModification3DJiggle::set_jiggle_joint_gravity(int p_joint_idx, Vector3 p_gravity) {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ jiggle_data_chain[p_joint_idx].gravity = p_gravity;
+}
+
+Vector3 SkeletonModification3DJiggle::get_jiggle_joint_gravity(int p_joint_idx) const {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, Vector3(0, 0, 0));
+ return jiggle_data_chain[p_joint_idx].gravity;
+}
+
+void SkeletonModification3DJiggle::set_jiggle_joint_roll(int p_joint_idx, real_t p_roll) {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ jiggle_data_chain[p_joint_idx].roll = p_roll;
+}
+
+real_t SkeletonModification3DJiggle::get_jiggle_joint_roll(int p_joint_idx) const {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, 0.0);
+ return jiggle_data_chain[p_joint_idx].roll;
+}
+
+void SkeletonModification3DJiggle::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_target_node", "target_nodepath"), &SkeletonModification3DJiggle::set_target_node);
+ ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonModification3DJiggle::get_target_node);
+
+ ClassDB::bind_method(D_METHOD("set_jiggle_data_chain_length", "length"), &SkeletonModification3DJiggle::set_jiggle_data_chain_length);
+ ClassDB::bind_method(D_METHOD("get_jiggle_data_chain_length"), &SkeletonModification3DJiggle::get_jiggle_data_chain_length);
+
+ ClassDB::bind_method(D_METHOD("set_stiffness", "stiffness"), &SkeletonModification3DJiggle::set_stiffness);
+ ClassDB::bind_method(D_METHOD("get_stiffness"), &SkeletonModification3DJiggle::get_stiffness);
+ ClassDB::bind_method(D_METHOD("set_mass", "mass"), &SkeletonModification3DJiggle::set_mass);
+ ClassDB::bind_method(D_METHOD("get_mass"), &SkeletonModification3DJiggle::get_mass);
+ ClassDB::bind_method(D_METHOD("set_damping", "damping"), &SkeletonModification3DJiggle::set_damping);
+ ClassDB::bind_method(D_METHOD("get_damping"), &SkeletonModification3DJiggle::get_damping);
+ ClassDB::bind_method(D_METHOD("set_use_gravity", "use_gravity"), &SkeletonModification3DJiggle::set_use_gravity);
+ ClassDB::bind_method(D_METHOD("get_use_gravity"), &SkeletonModification3DJiggle::get_use_gravity);
+ ClassDB::bind_method(D_METHOD("set_gravity", "gravity"), &SkeletonModification3DJiggle::set_gravity);
+ ClassDB::bind_method(D_METHOD("get_gravity"), &SkeletonModification3DJiggle::get_gravity);
+
+ ClassDB::bind_method(D_METHOD("set_use_colliders", "use_colliders"), &SkeletonModification3DJiggle::set_use_colliders);
+ ClassDB::bind_method(D_METHOD("get_use_colliders"), &SkeletonModification3DJiggle::get_use_colliders);
+ ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &SkeletonModification3DJiggle::set_collision_mask);
+ ClassDB::bind_method(D_METHOD("get_collision_mask"), &SkeletonModification3DJiggle::get_collision_mask);
+
+ // Jiggle joint data functions
+ ClassDB::bind_method(D_METHOD("set_jiggle_joint_bone_name", "joint_idx", "name"), &SkeletonModification3DJiggle::set_jiggle_joint_bone_name);
+ ClassDB::bind_method(D_METHOD("get_jiggle_joint_bone_name", "joint_idx"), &SkeletonModification3DJiggle::get_jiggle_joint_bone_name);
+ ClassDB::bind_method(D_METHOD("set_jiggle_joint_bone_index", "joint_idx", "bone_idx"), &SkeletonModification3DJiggle::set_jiggle_joint_bone_index);
+ ClassDB::bind_method(D_METHOD("get_jiggle_joint_bone_index", "joint_idx"), &SkeletonModification3DJiggle::get_jiggle_joint_bone_index);
+ ClassDB::bind_method(D_METHOD("set_jiggle_joint_override", "joint_idx", "override"), &SkeletonModification3DJiggle::set_jiggle_joint_override);
+ ClassDB::bind_method(D_METHOD("get_jiggle_joint_override", "joint_idx"), &SkeletonModification3DJiggle::get_jiggle_joint_override);
+ ClassDB::bind_method(D_METHOD("set_jiggle_joint_stiffness", "joint_idx", "stiffness"), &SkeletonModification3DJiggle::set_jiggle_joint_stiffness);
+ ClassDB::bind_method(D_METHOD("get_jiggle_joint_stiffness", "joint_idx"), &SkeletonModification3DJiggle::get_jiggle_joint_stiffness);
+ ClassDB::bind_method(D_METHOD("set_jiggle_joint_mass", "joint_idx", "mass"), &SkeletonModification3DJiggle::set_jiggle_joint_mass);
+ ClassDB::bind_method(D_METHOD("get_jiggle_joint_mass", "joint_idx"), &SkeletonModification3DJiggle::get_jiggle_joint_mass);
+ ClassDB::bind_method(D_METHOD("set_jiggle_joint_damping", "joint_idx", "damping"), &SkeletonModification3DJiggle::set_jiggle_joint_damping);
+ ClassDB::bind_method(D_METHOD("get_jiggle_joint_damping", "joint_idx"), &SkeletonModification3DJiggle::get_jiggle_joint_damping);
+ ClassDB::bind_method(D_METHOD("set_jiggle_joint_use_gravity", "joint_idx", "use_gravity"), &SkeletonModification3DJiggle::set_jiggle_joint_use_gravity);
+ ClassDB::bind_method(D_METHOD("get_jiggle_joint_use_gravity", "joint_idx"), &SkeletonModification3DJiggle::get_jiggle_joint_use_gravity);
+ ClassDB::bind_method(D_METHOD("set_jiggle_joint_gravity", "joint_idx", "gravity"), &SkeletonModification3DJiggle::set_jiggle_joint_gravity);
+ ClassDB::bind_method(D_METHOD("get_jiggle_joint_gravity", "joint_idx"), &SkeletonModification3DJiggle::get_jiggle_joint_gravity);
+ ClassDB::bind_method(D_METHOD("set_jiggle_joint_roll", "joint_idx", "roll"), &SkeletonModification3DJiggle::set_jiggle_joint_roll);
+ ClassDB::bind_method(D_METHOD("get_jiggle_joint_roll", "joint_idx"), &SkeletonModification3DJiggle::get_jiggle_joint_roll);
+
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node3D"), "set_target_node", "get_target_node");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "jiggle_data_chain_length", PROPERTY_HINT_RANGE, "0,100,1"), "set_jiggle_data_chain_length", "get_jiggle_data_chain_length");
+ ADD_GROUP("Default Joint Settings", "");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "stiffness"), "set_stiffness", "get_stiffness");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass"), "set_mass", "get_mass");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "damping", PROPERTY_HINT_RANGE, "0, 1, 0.01"), "set_damping", "get_damping");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_gravity"), "set_use_gravity", "get_use_gravity");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "gravity"), "set_gravity", "get_gravity");
+ ADD_GROUP("", "");
+}
+
+SkeletonModification3DJiggle::SkeletonModification3DJiggle() {
+ stack = nullptr;
+ is_setup = false;
+ jiggle_data_chain = Vector<Jiggle_Joint_Data>();
+ stiffness = 3;
+ mass = 0.75;
+ damping = 0.75;
+ use_gravity = false;
+ gravity = Vector3(0, -6.0, 0);
+ enabled = true;
+}
+
+SkeletonModification3DJiggle::~SkeletonModification3DJiggle() {
+}
diff --git a/scene/resources/skeleton_modification_3d_jiggle.h b/scene/resources/skeleton_modification_3d_jiggle.h
new file mode 100644
index 0000000000..c210c8fa73
--- /dev/null
+++ b/scene/resources/skeleton_modification_3d_jiggle.h
@@ -0,0 +1,138 @@
+/*************************************************************************/
+/* skeleton_modification_3d_jiggle.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "core/templates/local_vector.h"
+#include "scene/3d/skeleton_3d.h"
+#include "scene/resources/skeleton_modification_3d.h"
+
+#ifndef SKELETONMODIFICATION3DJIGGLE_H
+#define SKELETONMODIFICATION3DJIGGLE_H
+
+class SkeletonModification3DJiggle : public SkeletonModification3D {
+ GDCLASS(SkeletonModification3DJiggle, SkeletonModification3D);
+
+private:
+ struct Jiggle_Joint_Data {
+ String bone_name = "";
+ int bone_idx = -1;
+
+ bool override_defaults = false;
+ real_t stiffness = 3;
+ real_t mass = 0.75;
+ real_t damping = 0.75;
+ bool use_gravity = false;
+ Vector3 gravity = Vector3(0, -6.0, 0);
+ real_t roll = 0;
+
+ Vector3 cached_rotation = Vector3(0, 0, 0);
+ Vector3 force = Vector3(0, 0, 0);
+ Vector3 acceleration = Vector3(0, 0, 0);
+ Vector3 velocity = Vector3(0, 0, 0);
+ Vector3 last_position = Vector3(0, 0, 0);
+ Vector3 dynamic_position = Vector3(0, 0, 0);
+
+ Vector3 last_noncollision_position = Vector3(0, 0, 0);
+ };
+
+ NodePath target_node;
+ ObjectID target_node_cache;
+ LocalVector<Jiggle_Joint_Data> jiggle_data_chain;
+
+ real_t stiffness = 3;
+ real_t mass = 0.75;
+ real_t damping = 0.75;
+ bool use_gravity = false;
+ Vector3 gravity = Vector3(0, -6.0, 0);
+
+ bool use_colliders = false;
+ uint32_t collision_mask = 1;
+
+ void update_cache();
+ void _execute_jiggle_joint(int p_joint_idx, Node3D *p_target, real_t p_delta);
+ void _update_jiggle_joint_data();
+
+protected:
+ static void _bind_methods();
+ bool _get(const StringName &p_path, Variant &r_ret) const;
+ bool _set(const StringName &p_path, const Variant &p_value);
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
+public:
+ virtual void _execute(real_t p_delta) override;
+ virtual void _setup_modification(SkeletonModificationStack3D *p_stack) override;
+
+ void set_target_node(const NodePath &p_target_node);
+ NodePath get_target_node() const;
+
+ void set_stiffness(real_t p_stiffness);
+ real_t get_stiffness() const;
+ void set_mass(real_t p_mass);
+ real_t get_mass() const;
+ void set_damping(real_t p_damping);
+ real_t get_damping() const;
+
+ void set_use_gravity(bool p_use_gravity);
+ bool get_use_gravity() const;
+ void set_gravity(Vector3 p_gravity);
+ Vector3 get_gravity() const;
+
+ void set_use_colliders(bool p_use_colliders);
+ bool get_use_colliders() const;
+ void set_collision_mask(int p_mask);
+ int get_collision_mask() const;
+
+ int get_jiggle_data_chain_length();
+ void set_jiggle_data_chain_length(int p_new_length);
+
+ void set_jiggle_joint_bone_name(int p_joint_idx, String p_name);
+ String get_jiggle_joint_bone_name(int p_joint_idx) const;
+ void set_jiggle_joint_bone_index(int p_joint_idx, int p_idx);
+ int get_jiggle_joint_bone_index(int p_joint_idx) const;
+
+ void set_jiggle_joint_override(int p_joint_idx, bool p_override);
+ bool get_jiggle_joint_override(int p_joint_idx) const;
+ void set_jiggle_joint_stiffness(int p_joint_idx, real_t p_stiffness);
+ real_t get_jiggle_joint_stiffness(int p_joint_idx) const;
+ void set_jiggle_joint_mass(int p_joint_idx, real_t p_mass);
+ real_t get_jiggle_joint_mass(int p_joint_idx) const;
+ void set_jiggle_joint_damping(int p_joint_idx, real_t p_damping);
+ real_t get_jiggle_joint_damping(int p_joint_idx) const;
+ void set_jiggle_joint_use_gravity(int p_joint_idx, bool p_use_gravity);
+ bool get_jiggle_joint_use_gravity(int p_joint_idx) const;
+ void set_jiggle_joint_gravity(int p_joint_idx, Vector3 p_gravity);
+ Vector3 get_jiggle_joint_gravity(int p_joint_idx) const;
+ void set_jiggle_joint_roll(int p_joint_idx, real_t p_roll);
+ real_t get_jiggle_joint_roll(int p_joint_idx) const;
+
+ SkeletonModification3DJiggle();
+ ~SkeletonModification3DJiggle();
+};
+
+#endif //SKELETONMODIFICATION3DJIGGLE_H
diff --git a/scene/resources/skeleton_modification_3d_lookat.cpp b/scene/resources/skeleton_modification_3d_lookat.cpp
new file mode 100644
index 0000000000..afdb077e71
--- /dev/null
+++ b/scene/resources/skeleton_modification_3d_lookat.cpp
@@ -0,0 +1,265 @@
+/*************************************************************************/
+/* skeleton_modification_3d_lookat.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "scene/resources/skeleton_modification_3d_lookat.h"
+#include "scene/3d/skeleton_3d.h"
+#include "scene/resources/skeleton_modification_3d.h"
+
+bool SkeletonModification3DLookAt::_set(const StringName &p_path, const Variant &p_value) {
+ if (p_path == "lock_rotation_to_plane") {
+ set_lock_rotation_to_plane(p_value);
+ } else if (p_path == "lock_rotation_plane") {
+ set_lock_rotation_plane(p_value);
+ } else if (p_path == "additional_rotation") {
+ Vector3 tmp = p_value;
+ tmp.x = Math::deg2rad(tmp.x);
+ tmp.y = Math::deg2rad(tmp.y);
+ tmp.z = Math::deg2rad(tmp.z);
+ set_additional_rotation(tmp);
+ }
+
+ return true;
+}
+
+bool SkeletonModification3DLookAt::_get(const StringName &p_path, Variant &r_ret) const {
+ if (p_path == "lock_rotation_to_plane") {
+ r_ret = get_lock_rotation_to_plane();
+ } else if (p_path == "lock_rotation_plane") {
+ r_ret = get_lock_rotation_plane();
+ } else if (p_path == "additional_rotation") {
+ Vector3 tmp = get_additional_rotation();
+ tmp.x = Math::rad2deg(tmp.x);
+ tmp.y = Math::rad2deg(tmp.y);
+ tmp.z = Math::rad2deg(tmp.z);
+ r_ret = tmp;
+ }
+
+ return true;
+}
+
+void SkeletonModification3DLookAt::_get_property_list(List<PropertyInfo> *p_list) const {
+ p_list->push_back(PropertyInfo(Variant::BOOL, "lock_rotation_to_plane", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ if (lock_rotation_to_plane) {
+ p_list->push_back(PropertyInfo(Variant::INT, "lock_rotation_plane", PROPERTY_HINT_ENUM, "X plane, Y plane, Z plane", PROPERTY_USAGE_DEFAULT));
+ }
+ p_list->push_back(PropertyInfo(Variant::VECTOR3, "additional_rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+}
+
+void SkeletonModification3DLookAt::_execute(real_t p_delta) {
+ ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr,
+ "Modification is not setup and therefore cannot execute!");
+ if (!enabled) {
+ return;
+ }
+
+ if (target_node_cache.is_null()) {
+ _print_execution_error(true, "Target cache is out of date. Attempting to update...");
+ update_cache();
+ return;
+ }
+
+ if (bone_idx <= -2) {
+ bone_idx = stack->skeleton->find_bone(bone_name);
+ }
+
+ Node3D *target = Object::cast_to<Node3D>(ObjectDB::get_instance(target_node_cache));
+ if (_print_execution_error(!target || !target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!")) {
+ return;
+ }
+ if (_print_execution_error(bone_idx <= -1, "Bone index is invalid. Cannot execute modification!")) {
+ return;
+ }
+
+ Transform3D new_bone_trans = stack->skeleton->get_bone_local_pose_override(bone_idx);
+ Vector3 target_pos = stack->skeleton->global_pose_to_local_pose(bone_idx, stack->skeleton->world_transform_to_global_pose(target->get_global_transform())).origin;
+
+ // Lock the rotation to a plane relative to the bone by changing the target position
+ if (lock_rotation_to_plane) {
+ if (lock_rotation_plane == ROTATION_PLANE::ROTATION_PLANE_X) {
+ target_pos.x = new_bone_trans.origin.x;
+ } else if (lock_rotation_plane == ROTATION_PLANE::ROTATION_PLANE_Y) {
+ target_pos.y = new_bone_trans.origin.y;
+ } else if (lock_rotation_plane == ROTATION_PLANE::ROTATION_PLANE_Z) {
+ target_pos.z = new_bone_trans.origin.z;
+ }
+ }
+
+ // Look at the target!
+ new_bone_trans = new_bone_trans.looking_at(target_pos, Vector3(0, 1, 0));
+ // Convert from Z-forward to whatever direction the bone faces.
+ stack->skeleton->update_bone_rest_forward_vector(bone_idx);
+ new_bone_trans.basis = stack->skeleton->global_pose_z_forward_to_bone_forward(bone_idx, new_bone_trans.basis);
+
+ // Apply additional rotation
+ new_bone_trans.basis.rotate_local(Vector3(1, 0, 0), additional_rotation.x);
+ new_bone_trans.basis.rotate_local(Vector3(0, 1, 0), additional_rotation.y);
+ new_bone_trans.basis.rotate_local(Vector3(0, 0, 1), additional_rotation.z);
+
+ stack->skeleton->set_bone_local_pose_override(bone_idx, new_bone_trans, stack->strength, true);
+ stack->skeleton->force_update_bone_children_transforms(bone_idx);
+
+ // If we completed it successfully, then we can set execution_error_found to false
+ execution_error_found = false;
+}
+
+void SkeletonModification3DLookAt::_setup_modification(SkeletonModificationStack3D *p_stack) {
+ stack = p_stack;
+
+ if (stack != nullptr) {
+ is_setup = true;
+ execution_error_found = false;
+ update_cache();
+ }
+}
+
+void SkeletonModification3DLookAt::set_bone_name(String p_name) {
+ bone_name = p_name;
+ if (stack) {
+ if (stack->skeleton) {
+ bone_idx = stack->skeleton->find_bone(bone_name);
+ }
+ }
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+String SkeletonModification3DLookAt::get_bone_name() const {
+ return bone_name;
+}
+
+int SkeletonModification3DLookAt::get_bone_index() const {
+ return bone_idx;
+}
+
+void SkeletonModification3DLookAt::set_bone_index(int p_bone_idx) {
+ ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!");
+ bone_idx = p_bone_idx;
+
+ if (stack) {
+ if (stack->skeleton) {
+ bone_name = stack->skeleton->get_bone_name(p_bone_idx);
+ }
+ }
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+void SkeletonModification3DLookAt::update_cache() {
+ if (!is_setup || !stack) {
+ _print_execution_error(true, "Cannot update target cache: modification is not properly setup!");
+ return;
+ }
+
+ target_node_cache = ObjectID();
+ if (stack->skeleton) {
+ if (stack->skeleton->is_inside_tree()) {
+ if (stack->skeleton->has_node(target_node)) {
+ Node *node = stack->skeleton->get_node(target_node);
+ ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
+ "Cannot update target cache: Node is this modification's skeleton or cannot be found!");
+ ERR_FAIL_COND_MSG(!node->is_inside_tree(),
+ "Cannot update target cache: Node is not in the scene tree!");
+ target_node_cache = node->get_instance_id();
+
+ execution_error_found = false;
+ }
+ }
+ }
+}
+
+void SkeletonModification3DLookAt::set_target_node(const NodePath &p_target_node) {
+ target_node = p_target_node;
+ update_cache();
+}
+
+NodePath SkeletonModification3DLookAt::get_target_node() const {
+ return target_node;
+}
+
+Vector3 SkeletonModification3DLookAt::get_additional_rotation() const {
+ return additional_rotation;
+}
+
+void SkeletonModification3DLookAt::set_additional_rotation(Vector3 p_offset) {
+ additional_rotation = p_offset;
+}
+
+bool SkeletonModification3DLookAt::get_lock_rotation_to_plane() const {
+ return lock_rotation_plane;
+}
+
+void SkeletonModification3DLookAt::set_lock_rotation_to_plane(bool p_lock_rotation) {
+ lock_rotation_to_plane = p_lock_rotation;
+ notify_property_list_changed();
+}
+
+int SkeletonModification3DLookAt::get_lock_rotation_plane() const {
+ return lock_rotation_plane;
+}
+
+void SkeletonModification3DLookAt::set_lock_rotation_plane(int p_plane) {
+ lock_rotation_plane = p_plane;
+}
+
+void SkeletonModification3DLookAt::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_bone_name", "name"), &SkeletonModification3DLookAt::set_bone_name);
+ ClassDB::bind_method(D_METHOD("get_bone_name"), &SkeletonModification3DLookAt::get_bone_name);
+
+ ClassDB::bind_method(D_METHOD("set_bone_index", "bone_idx"), &SkeletonModification3DLookAt::set_bone_index);
+ ClassDB::bind_method(D_METHOD("get_bone_index"), &SkeletonModification3DLookAt::get_bone_index);
+
+ ClassDB::bind_method(D_METHOD("set_target_node", "target_nodepath"), &SkeletonModification3DLookAt::set_target_node);
+ ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonModification3DLookAt::get_target_node);
+
+ ClassDB::bind_method(D_METHOD("set_additional_rotation", "additional_rotation"), &SkeletonModification3DLookAt::set_additional_rotation);
+ ClassDB::bind_method(D_METHOD("get_additional_rotation"), &SkeletonModification3DLookAt::get_additional_rotation);
+
+ ClassDB::bind_method(D_METHOD("set_lock_rotation_to_plane", "lock_to_plane"), &SkeletonModification3DLookAt::set_lock_rotation_to_plane);
+ ClassDB::bind_method(D_METHOD("get_lock_rotation_to_plane"), &SkeletonModification3DLookAt::get_lock_rotation_to_plane);
+ ClassDB::bind_method(D_METHOD("set_lock_rotation_plane", "plane"), &SkeletonModification3DLookAt::set_lock_rotation_plane);
+ ClassDB::bind_method(D_METHOD("get_lock_rotation_plane"), &SkeletonModification3DLookAt::get_lock_rotation_plane);
+
+ ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "bone_name"), "set_bone_name", "get_bone_name");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "bone_index"), "set_bone_index", "get_bone_index");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node3D"), "set_target_node", "get_target_node");
+}
+
+SkeletonModification3DLookAt::SkeletonModification3DLookAt() {
+ stack = nullptr;
+ is_setup = false;
+ bone_name = "";
+ bone_idx = -2;
+ additional_rotation = Vector3();
+ lock_rotation_to_plane = false;
+ enabled = true;
+}
+
+SkeletonModification3DLookAt::~SkeletonModification3DLookAt() {
+}
diff --git a/scene/resources/skeleton_modification_3d_lookat.h b/scene/resources/skeleton_modification_3d_lookat.h
new file mode 100644
index 0000000000..5971e3f647
--- /dev/null
+++ b/scene/resources/skeleton_modification_3d_lookat.h
@@ -0,0 +1,89 @@
+/*************************************************************************/
+/* skeleton_modification_3d_lookat.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "scene/3d/skeleton_3d.h"
+#include "scene/resources/skeleton_modification_3d.h"
+
+#ifndef SKELETONMODIFICATION3DLOOKAT_H
+#define SKELETONMODIFICATION3DLOOKAT_H
+
+class SkeletonModification3DLookAt : public SkeletonModification3D {
+ GDCLASS(SkeletonModification3DLookAt, SkeletonModification3D);
+
+private:
+ String bone_name = "";
+ int bone_idx = -1;
+ NodePath target_node;
+ ObjectID target_node_cache;
+
+ Vector3 additional_rotation = Vector3(1, 0, 0);
+ bool lock_rotation_to_plane = false;
+ int lock_rotation_plane = ROTATION_PLANE_X;
+
+ void update_cache();
+
+protected:
+ static void _bind_methods();
+ bool _get(const StringName &p_path, Variant &r_ret) const;
+ bool _set(const StringName &p_path, const Variant &p_value);
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
+public:
+ enum ROTATION_PLANE {
+ ROTATION_PLANE_X,
+ ROTATION_PLANE_Y,
+ ROTATION_PLANE_Z
+ };
+
+ virtual void _execute(real_t p_delta) override;
+ virtual void _setup_modification(SkeletonModificationStack3D *p_stack) override;
+
+ void set_bone_name(String p_name);
+ String get_bone_name() const;
+
+ void set_bone_index(int p_idx);
+ int get_bone_index() const;
+
+ void set_target_node(const NodePath &p_target_node);
+ NodePath get_target_node() const;
+
+ void set_additional_rotation(Vector3 p_offset);
+ Vector3 get_additional_rotation() const;
+
+ void set_lock_rotation_to_plane(bool p_lock_to_plane);
+ bool get_lock_rotation_to_plane() const;
+ void set_lock_rotation_plane(int p_plane);
+ int get_lock_rotation_plane() const;
+
+ SkeletonModification3DLookAt();
+ ~SkeletonModification3DLookAt();
+};
+
+#endif //SKELETONMODIFICATION3DLOOKAT_H
diff --git a/scene/resources/skeleton_modification_3d_stackholder.cpp b/scene/resources/skeleton_modification_3d_stackholder.cpp
new file mode 100644
index 0000000000..56035a4def
--- /dev/null
+++ b/scene/resources/skeleton_modification_3d_stackholder.cpp
@@ -0,0 +1,104 @@
+/*************************************************************************/
+/* skeleton_modification_3d_stackholder.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "scene/resources/skeleton_modification_3d_stackholder.h"
+#include "scene/3d/skeleton_3d.h"
+#include "scene/resources/skeleton_modification_3d.h"
+
+bool SkeletonModification3DStackHolder::_set(const StringName &p_path, const Variant &p_value) {
+ String path = p_path;
+
+ if (path == "held_modification_stack") {
+ set_held_modification_stack(p_value);
+ }
+ return true;
+}
+
+bool SkeletonModification3DStackHolder::_get(const StringName &p_path, Variant &r_ret) const {
+ String path = p_path;
+
+ if (path == "held_modification_stack") {
+ r_ret = get_held_modification_stack();
+ }
+ return true;
+}
+
+void SkeletonModification3DStackHolder::_get_property_list(List<PropertyInfo> *p_list) const {
+ p_list->push_back(PropertyInfo(Variant::OBJECT, "held_modification_stack", PROPERTY_HINT_RESOURCE_TYPE, "SkeletonModificationStack3D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE));
+}
+
+void SkeletonModification3DStackHolder::_execute(real_t p_delta) {
+ ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr,
+ "Modification is not setup and therefore cannot execute!");
+
+ if (held_modification_stack.is_valid()) {
+ held_modification_stack->execute(p_delta, execution_mode);
+ }
+}
+
+void SkeletonModification3DStackHolder::_setup_modification(SkeletonModificationStack3D *p_stack) {
+ stack = p_stack;
+
+ if (stack != nullptr) {
+ is_setup = true;
+
+ if (held_modification_stack.is_valid()) {
+ held_modification_stack->set_skeleton(stack->get_skeleton());
+ held_modification_stack->setup();
+ }
+ }
+}
+
+void SkeletonModification3DStackHolder::set_held_modification_stack(Ref<SkeletonModificationStack3D> p_held_stack) {
+ held_modification_stack = p_held_stack;
+
+ if (is_setup && held_modification_stack.is_valid()) {
+ held_modification_stack->set_skeleton(stack->get_skeleton());
+ held_modification_stack->setup();
+ }
+}
+
+Ref<SkeletonModificationStack3D> SkeletonModification3DStackHolder::get_held_modification_stack() const {
+ return held_modification_stack;
+}
+
+void SkeletonModification3DStackHolder::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_held_modification_stack", "held_modification_stack"), &SkeletonModification3DStackHolder::set_held_modification_stack);
+ ClassDB::bind_method(D_METHOD("get_held_modification_stack"), &SkeletonModification3DStackHolder::get_held_modification_stack);
+}
+
+SkeletonModification3DStackHolder::SkeletonModification3DStackHolder() {
+ stack = nullptr;
+ is_setup = false;
+ enabled = true;
+}
+
+SkeletonModification3DStackHolder::~SkeletonModification3DStackHolder() {
+}
diff --git a/scene/resources/skeleton_modification_3d_stackholder.h b/scene/resources/skeleton_modification_3d_stackholder.h
new file mode 100644
index 0000000000..c765cd8de3
--- /dev/null
+++ b/scene/resources/skeleton_modification_3d_stackholder.h
@@ -0,0 +1,59 @@
+/*************************************************************************/
+/* skeleton_modification_3d_stackholder.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "scene/3d/skeleton_3d.h"
+#include "scene/resources/skeleton_modification_3d.h"
+
+#ifndef SKELETONMODIFICATION3DSTACKHOLDER_H
+#define SKELETONMODIFICATION3DSTACKHOLDER_H
+
+class SkeletonModification3DStackHolder : public SkeletonModification3D {
+ GDCLASS(SkeletonModification3DStackHolder, SkeletonModification3D);
+
+protected:
+ static void _bind_methods();
+ bool _get(const StringName &p_path, Variant &r_ret) const;
+ bool _set(const StringName &p_path, const Variant &p_value);
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
+public:
+ Ref<SkeletonModificationStack3D> held_modification_stack;
+
+ virtual void _execute(real_t p_delta) override;
+ virtual void _setup_modification(SkeletonModificationStack3D *p_stack) override;
+
+ void set_held_modification_stack(Ref<SkeletonModificationStack3D> p_held_stack);
+ Ref<SkeletonModificationStack3D> get_held_modification_stack() const;
+
+ SkeletonModification3DStackHolder();
+ ~SkeletonModification3DStackHolder();
+};
+
+#endif //SKELETONMODIFICATION3DSTACKHOLDER_H
diff --git a/scene/resources/skeleton_modification_3d_twoboneik.cpp b/scene/resources/skeleton_modification_3d_twoboneik.cpp
new file mode 100644
index 0000000000..ae7a3bab7e
--- /dev/null
+++ b/scene/resources/skeleton_modification_3d_twoboneik.cpp
@@ -0,0 +1,599 @@
+/*************************************************************************/
+/* skeleton_modification_3d_twoboneik.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "scene/resources/skeleton_modification_3d_twoboneik.h"
+#include "scene/3d/skeleton_3d.h"
+#include "scene/resources/skeleton_modification_3d.h"
+
+bool SkeletonModification3DTwoBoneIK::_set(const StringName &p_path, const Variant &p_value) {
+ String path = p_path;
+
+ if (path == "use_tip_node") {
+ set_use_tip_node(p_value);
+ } else if (path == "tip_node") {
+ set_tip_node(p_value);
+ } else if (path == "auto_calculate_joint_length") {
+ set_auto_calculate_joint_length(p_value);
+ } else if (path == "use_pole_node") {
+ set_use_pole_node(p_value);
+ } else if (path == "pole_node") {
+ set_pole_node(p_value);
+ } else if (path == "joint_one_length") {
+ set_joint_one_length(p_value);
+ } else if (path == "joint_two_length") {
+ set_joint_two_length(p_value);
+ } else if (path == "joint_one/bone_name") {
+ set_joint_one_bone_name(p_value);
+ } else if (path == "joint_one/bone_idx") {
+ set_joint_one_bone_idx(p_value);
+ } else if (path == "joint_one/roll") {
+ set_joint_one_roll(Math::deg2rad(real_t(p_value)));
+ } else if (path == "joint_two/bone_name") {
+ set_joint_two_bone_name(p_value);
+ } else if (path == "joint_two/bone_idx") {
+ set_joint_two_bone_idx(p_value);
+ } else if (path == "joint_two/roll") {
+ set_joint_two_roll(Math::deg2rad(real_t(p_value)));
+ }
+
+ return true;
+}
+
+bool SkeletonModification3DTwoBoneIK::_get(const StringName &p_path, Variant &r_ret) const {
+ String path = p_path;
+
+ if (path == "use_tip_node") {
+ r_ret = get_use_tip_node();
+ } else if (path == "tip_node") {
+ r_ret = get_tip_node();
+ } else if (path == "auto_calculate_joint_length") {
+ r_ret = get_auto_calculate_joint_length();
+ } else if (path == "use_pole_node") {
+ r_ret = get_use_pole_node();
+ } else if (path == "pole_node") {
+ r_ret = get_pole_node();
+ } else if (path == "joint_one_length") {
+ r_ret = get_joint_one_length();
+ } else if (path == "joint_two_length") {
+ r_ret = get_joint_two_length();
+ } else if (path == "joint_one/bone_name") {
+ r_ret = get_joint_one_bone_name();
+ } else if (path == "joint_one/bone_idx") {
+ r_ret = get_joint_one_bone_idx();
+ } else if (path == "joint_one/roll") {
+ r_ret = Math::rad2deg(get_joint_one_roll());
+ } else if (path == "joint_two/bone_name") {
+ r_ret = get_joint_two_bone_name();
+ } else if (path == "joint_two/bone_idx") {
+ r_ret = get_joint_two_bone_idx();
+ } else if (path == "joint_two/roll") {
+ r_ret = Math::rad2deg(get_joint_two_roll());
+ }
+
+ return true;
+}
+
+void SkeletonModification3DTwoBoneIK::_get_property_list(List<PropertyInfo> *p_list) const {
+ p_list->push_back(PropertyInfo(Variant::BOOL, "use_tip_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ if (use_tip_node) {
+ p_list->push_back(PropertyInfo(Variant::NODE_PATH, "tip_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node3D", PROPERTY_USAGE_DEFAULT));
+ }
+
+ p_list->push_back(PropertyInfo(Variant::BOOL, "auto_calculate_joint_length", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ if (!auto_calculate_joint_length) {
+ p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_one_length", PROPERTY_HINT_RANGE, "-1, 10000, 0.001", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_two_length", PROPERTY_HINT_RANGE, "-1, 10000, 0.001", PROPERTY_USAGE_DEFAULT));
+ }
+
+ p_list->push_back(PropertyInfo(Variant::BOOL, "use_pole_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ if (use_pole_node) {
+ p_list->push_back(PropertyInfo(Variant::NODE_PATH, "pole_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node3D", PROPERTY_USAGE_DEFAULT));
+ }
+
+ p_list->push_back(PropertyInfo(Variant::STRING_NAME, "joint_one/bone_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::INT, "joint_one/bone_idx", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_one/roll", PROPERTY_HINT_RANGE, "-360, 360, 0.01", PROPERTY_USAGE_DEFAULT));
+
+ p_list->push_back(PropertyInfo(Variant::STRING_NAME, "joint_two/bone_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::INT, "joint_two/bone_idx", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_two/roll", PROPERTY_HINT_RANGE, "-360, 360, 0.01", PROPERTY_USAGE_DEFAULT));
+}
+
+void SkeletonModification3DTwoBoneIK::_execute(real_t p_delta) {
+ ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr,
+ "Modification is not setup and therefore cannot execute!");
+
+ if (!enabled) {
+ return;
+ }
+
+ if (_print_execution_error(joint_one_bone_idx < 0 || joint_two_bone_idx < 0,
+ "One (or more) of the bones in the modification have invalid bone indexes. Cannot execute modification!")) {
+ return;
+ }
+
+ if (target_node_cache.is_null()) {
+ _print_execution_error(true, "Target cache is out of date. Attempting to update...");
+ update_cache_target();
+ return;
+ }
+
+ // Update joint lengths (if needed)
+ if (auto_calculate_joint_length && (joint_one_length < 0 || joint_two_length < 0)) {
+ calculate_joint_lengths();
+ }
+
+ // Adopted from the links below:
+ // http://theorangeduck.com/page/simple-two-joint
+ // https://www.alanzucconi.com/2018/05/02/ik-2d-2/
+ // With modifications by TwistedTwigleg
+ Node3D *target = Object::cast_to<Node3D>(ObjectDB::get_instance(target_node_cache));
+ if (_print_execution_error(!target || !target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!")) {
+ return;
+ }
+ Transform3D target_trans = stack->skeleton->world_transform_to_global_pose(target->get_global_transform());
+
+ Transform3D bone_one_trans;
+ Transform3D bone_two_trans;
+
+ // Make the first joint look at the pole, and the second look at the target. That way, the
+ // TwoBoneIK solver has to really only handle extension/contraction, which should make it align with the pole.
+ if (use_pole_node) {
+ if (pole_node_cache.is_null()) {
+ _print_execution_error(true, "Pole cache is out of date. Attempting to update...");
+ update_cache_pole();
+ return;
+ }
+
+ Node3D *pole = Object::cast_to<Node3D>(ObjectDB::get_instance(pole_node_cache));
+ if (_print_execution_error(!pole || !pole->is_inside_tree(), "Pole node is not in the scene tree. Cannot execute modification!")) {
+ return;
+ }
+ Transform3D pole_trans = stack->skeleton->world_transform_to_global_pose(pole->get_global_transform());
+
+ bone_one_trans = stack->skeleton->local_pose_to_global_pose(joint_one_bone_idx, stack->skeleton->get_bone_local_pose_override(joint_one_bone_idx));
+ bone_one_trans = bone_one_trans.looking_at(pole_trans.origin, Vector3(0, 1, 0));
+ bone_one_trans.basis = stack->skeleton->global_pose_z_forward_to_bone_forward(joint_one_bone_idx, bone_one_trans.basis);
+ stack->skeleton->update_bone_rest_forward_vector(joint_one_bone_idx);
+ bone_one_trans.basis.rotate_local(stack->skeleton->get_bone_axis_forward_vector(joint_one_bone_idx), joint_one_roll);
+ stack->skeleton->set_bone_local_pose_override(joint_one_bone_idx, stack->skeleton->global_pose_to_local_pose(joint_one_bone_idx, bone_one_trans), stack->strength, true);
+ stack->skeleton->force_update_bone_children_transforms(joint_one_bone_idx);
+
+ bone_two_trans = stack->skeleton->local_pose_to_global_pose(joint_two_bone_idx, stack->skeleton->get_bone_local_pose_override(joint_two_bone_idx));
+ bone_two_trans = bone_two_trans.looking_at(target_trans.origin, Vector3(0, 1, 0));
+ bone_two_trans.basis = stack->skeleton->global_pose_z_forward_to_bone_forward(joint_two_bone_idx, bone_two_trans.basis);
+ stack->skeleton->update_bone_rest_forward_vector(joint_two_bone_idx);
+ bone_two_trans.basis.rotate_local(stack->skeleton->get_bone_axis_forward_vector(joint_two_bone_idx), joint_two_roll);
+ stack->skeleton->set_bone_local_pose_override(joint_two_bone_idx, stack->skeleton->global_pose_to_local_pose(joint_two_bone_idx, bone_two_trans), stack->strength, true);
+ stack->skeleton->force_update_bone_children_transforms(joint_two_bone_idx);
+ } else {
+ bone_one_trans = stack->skeleton->local_pose_to_global_pose(joint_one_bone_idx, stack->skeleton->get_bone_local_pose_override(joint_one_bone_idx));
+ bone_two_trans = stack->skeleton->local_pose_to_global_pose(joint_two_bone_idx, stack->skeleton->get_bone_local_pose_override(joint_two_bone_idx));
+ }
+
+ Transform3D bone_two_tip_trans;
+ if (use_tip_node) {
+ if (tip_node_cache.is_null()) {
+ _print_execution_error(true, "Tip cache is out of date. Attempting to update...");
+ update_cache_tip();
+ return;
+ }
+ Node3D *tip = Object::cast_to<Node3D>(ObjectDB::get_instance(tip_node_cache));
+ if (_print_execution_error(!tip || !tip->is_inside_tree(), "Tip node is not in the scene tree. Cannot execute modification!")) {
+ return;
+ }
+ bone_two_tip_trans = stack->skeleton->world_transform_to_global_pose(tip->get_global_transform());
+ } else {
+ stack->skeleton->update_bone_rest_forward_vector(joint_two_bone_idx);
+ bone_two_tip_trans = bone_two_trans;
+ bone_two_tip_trans.origin += bone_two_trans.basis.xform(stack->skeleton->get_bone_axis_forward_vector(joint_two_bone_idx)).normalized() * joint_two_length;
+ }
+
+ real_t joint_one_to_target_length = bone_one_trans.origin.distance_to(target_trans.origin);
+ if (joint_one_length + joint_two_length < joint_one_to_target_length) {
+ // Set the target *just* out of reach to straighten the bones
+ joint_one_to_target_length = joint_one_length + joint_two_length + 0.01;
+ } else if (joint_one_to_target_length < joint_one_length) {
+ // Place the target in reach so the solver doesn't do crazy things
+ joint_one_to_target_length = joint_one_length;
+ }
+
+ // Get the square lengths for all three sides of the triangle we'll use to calculate the angles
+ real_t sqr_one_length = joint_one_length * joint_one_length;
+ real_t sqr_two_length = joint_two_length * joint_two_length;
+ real_t sqr_three_length = joint_one_to_target_length * joint_one_to_target_length;
+
+ // Calculate the angles for the first joint using the law of cosigns
+ real_t ac_ab_0 = Math::acos(CLAMP(bone_two_tip_trans.origin.direction_to(bone_one_trans.origin).dot(bone_two_trans.origin.direction_to(bone_one_trans.origin)), -1, 1));
+ real_t ac_at_0 = Math::acos(CLAMP(bone_one_trans.origin.direction_to(bone_two_tip_trans.origin).dot(bone_one_trans.origin.direction_to(target_trans.origin)), -1, 1));
+ real_t ac_ab_1 = Math::acos(CLAMP((sqr_two_length - sqr_one_length - sqr_three_length) / (-2.0 * joint_one_length * joint_one_to_target_length), -1, 1));
+
+ // Calculate the angles of rotation. Angle 0 is the extension/contraction axis, while angle 1 is the rotation axis to align the triangle to the target
+ Vector3 axis_0 = bone_one_trans.origin.direction_to(bone_two_tip_trans.origin).cross(bone_one_trans.origin.direction_to(bone_two_trans.origin));
+ Vector3 axis_1 = bone_one_trans.origin.direction_to(bone_two_tip_trans.origin).cross(bone_one_trans.origin.direction_to(target_trans.origin));
+
+ // Make a quaternion with the delta rotation needed to rotate the first joint into alignment and apply it to the transform.
+ Quaternion bone_one_quat = bone_one_trans.basis.get_rotation_quaternion();
+ Quaternion rot_0 = Quaternion(bone_one_quat.inverse().xform(axis_0).normalized(), (ac_ab_1 - ac_ab_0));
+ Quaternion rot_2 = Quaternion(bone_one_quat.inverse().xform(axis_1).normalized(), ac_at_0);
+ bone_one_trans.basis.set_quaternion(bone_one_quat * (rot_0 * rot_2));
+
+ stack->skeleton->update_bone_rest_forward_vector(joint_one_bone_idx);
+ bone_one_trans.basis.rotate_local(stack->skeleton->get_bone_axis_forward_vector(joint_one_bone_idx), joint_one_roll);
+
+ // Apply the rotation to the first joint
+ bone_one_trans = stack->skeleton->global_pose_to_local_pose(joint_one_bone_idx, bone_one_trans);
+ bone_one_trans.origin = Vector3(0, 0, 0);
+ stack->skeleton->set_bone_local_pose_override(joint_one_bone_idx, bone_one_trans, stack->strength, true);
+ stack->skeleton->force_update_bone_children_transforms(joint_one_bone_idx);
+
+ if (use_pole_node) {
+ // Update bone_two_trans so its at the latest position, with the rotation of bone_one_trans taken into account, then look at the target.
+ bone_two_trans = stack->skeleton->local_pose_to_global_pose(joint_two_bone_idx, stack->skeleton->get_bone_local_pose_override(joint_two_bone_idx));
+ stack->skeleton->update_bone_rest_forward_vector(joint_two_bone_idx);
+ Vector3 forward_vector = stack->skeleton->get_bone_axis_forward_vector(joint_two_bone_idx);
+ bone_two_trans.basis.rotate_to_align(forward_vector, bone_two_trans.origin.direction_to(target_trans.origin));
+
+ stack->skeleton->update_bone_rest_forward_vector(joint_two_bone_idx);
+ bone_two_trans.basis.rotate_local(stack->skeleton->get_bone_axis_forward_vector(joint_two_bone_idx), joint_two_roll);
+
+ bone_two_trans = stack->skeleton->global_pose_to_local_pose(joint_two_bone_idx, bone_two_trans);
+ stack->skeleton->set_bone_local_pose_override(joint_two_bone_idx, bone_two_trans, stack->strength, true);
+ stack->skeleton->force_update_bone_children_transforms(joint_two_bone_idx);
+ } else {
+ // Calculate the angles for the second joint using the law of cosigns, make a quaternion with the delta rotation needed to rotate the joint into
+ // alignment, and then apply it to the second joint.
+ real_t ba_bc_0 = Math::acos(CLAMP(bone_two_trans.origin.direction_to(bone_one_trans.origin).dot(bone_two_trans.origin.direction_to(bone_two_tip_trans.origin)), -1, 1));
+ real_t ba_bc_1 = Math::acos(CLAMP((sqr_three_length - sqr_one_length - sqr_two_length) / (-2.0 * joint_one_length * joint_two_length), -1, 1));
+ Quaternion bone_two_quat = bone_two_trans.basis.get_rotation_quaternion();
+ Quaternion rot_1 = Quaternion(bone_two_quat.inverse().xform(axis_0).normalized(), (ba_bc_1 - ba_bc_0));
+ bone_two_trans.basis.set_quaternion(bone_two_quat * rot_1);
+
+ stack->skeleton->update_bone_rest_forward_vector(joint_two_bone_idx);
+ bone_two_trans.basis.rotate_local(stack->skeleton->get_bone_axis_forward_vector(joint_two_bone_idx), joint_two_roll);
+
+ bone_two_trans = stack->skeleton->global_pose_to_local_pose(joint_two_bone_idx, bone_two_trans);
+ bone_two_trans.origin = Vector3(0, 0, 0);
+ stack->skeleton->set_bone_local_pose_override(joint_two_bone_idx, bone_two_trans, stack->strength, true);
+ stack->skeleton->force_update_bone_children_transforms(joint_two_bone_idx);
+ }
+}
+
+void SkeletonModification3DTwoBoneIK::_setup_modification(SkeletonModificationStack3D *p_stack) {
+ stack = p_stack;
+
+ if (stack != nullptr) {
+ is_setup = true;
+ execution_error_found = false;
+ update_cache_target();
+ update_cache_tip();
+ }
+}
+
+void SkeletonModification3DTwoBoneIK::update_cache_target() {
+ if (!is_setup || !stack) {
+ _print_execution_error(true, "Cannot update target cache: modification is not properly setup!");
+ return;
+ }
+
+ target_node_cache = ObjectID();
+ if (stack->skeleton) {
+ if (stack->skeleton->is_inside_tree() && target_node.is_empty() == false) {
+ if (stack->skeleton->has_node(target_node)) {
+ Node *node = stack->skeleton->get_node(target_node);
+ ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
+ "Cannot update target cache: Target node is this modification's skeleton or cannot be found. Cannot execute modification");
+ ERR_FAIL_COND_MSG(!node->is_inside_tree(),
+ "Cannot update target cache: Target node is not in the scene tree. Cannot execute modification!");
+ target_node_cache = node->get_instance_id();
+
+ execution_error_found = false;
+ }
+ }
+ }
+}
+
+void SkeletonModification3DTwoBoneIK::update_cache_tip() {
+ if (!is_setup || !stack) {
+ _print_execution_error(true, "Cannot update tip cache: modification is not properly setup!");
+ return;
+ }
+
+ tip_node_cache = ObjectID();
+ if (stack->skeleton) {
+ if (stack->skeleton->is_inside_tree()) {
+ if (stack->skeleton->has_node(tip_node)) {
+ Node *node = stack->skeleton->get_node(tip_node);
+ ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
+ "Cannot update tip cache: Tip node is this modification's skeleton or cannot be found!");
+ ERR_FAIL_COND_MSG(!node->is_inside_tree(),
+ "Cannot update tip cache: Tip node is not in the scene tree. Cannot execute modification!");
+ tip_node_cache = node->get_instance_id();
+
+ execution_error_found = false;
+ }
+ }
+ }
+}
+
+void SkeletonModification3DTwoBoneIK::update_cache_pole() {
+ if (!is_setup || !stack) {
+ _print_execution_error(true, "Cannot update pole cache: modification is not properly setup!");
+ return;
+ }
+
+ pole_node_cache = ObjectID();
+ if (stack->skeleton) {
+ if (stack->skeleton->is_inside_tree()) {
+ if (stack->skeleton->has_node(pole_node)) {
+ Node *node = stack->skeleton->get_node(pole_node);
+ ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
+ "Cannot update pole cache: Pole node is this modification's skeleton or cannot be found!");
+ ERR_FAIL_COND_MSG(!node->is_inside_tree(),
+ "Cannot update pole cache: Pole node is not in the scene tree. Cannot execute modification!");
+ pole_node_cache = node->get_instance_id();
+
+ execution_error_found = false;
+ }
+ }
+ }
+}
+
+void SkeletonModification3DTwoBoneIK::set_target_node(const NodePath &p_target_node) {
+ target_node = p_target_node;
+ update_cache_target();
+}
+
+NodePath SkeletonModification3DTwoBoneIK::get_target_node() const {
+ return target_node;
+}
+
+void SkeletonModification3DTwoBoneIK::set_use_tip_node(const bool p_use_tip_node) {
+ use_tip_node = p_use_tip_node;
+ notify_property_list_changed();
+}
+
+bool SkeletonModification3DTwoBoneIK::get_use_tip_node() const {
+ return use_tip_node;
+}
+
+void SkeletonModification3DTwoBoneIK::set_tip_node(const NodePath &p_tip_node) {
+ tip_node = p_tip_node;
+ update_cache_tip();
+}
+
+NodePath SkeletonModification3DTwoBoneIK::get_tip_node() const {
+ return tip_node;
+}
+
+void SkeletonModification3DTwoBoneIK::set_use_pole_node(const bool p_use_pole_node) {
+ use_pole_node = p_use_pole_node;
+ notify_property_list_changed();
+}
+
+bool SkeletonModification3DTwoBoneIK::get_use_pole_node() const {
+ return use_pole_node;
+}
+
+void SkeletonModification3DTwoBoneIK::set_pole_node(const NodePath &p_pole_node) {
+ pole_node = p_pole_node;
+ update_cache_pole();
+}
+
+NodePath SkeletonModification3DTwoBoneIK::get_pole_node() const {
+ return pole_node;
+}
+
+void SkeletonModification3DTwoBoneIK::set_auto_calculate_joint_length(bool p_calculate) {
+ auto_calculate_joint_length = p_calculate;
+ if (p_calculate) {
+ calculate_joint_lengths();
+ }
+ notify_property_list_changed();
+}
+
+bool SkeletonModification3DTwoBoneIK::get_auto_calculate_joint_length() const {
+ return auto_calculate_joint_length;
+}
+
+void SkeletonModification3DTwoBoneIK::calculate_joint_lengths() {
+ if (!is_setup) {
+ return; // fail silently, as we likely just loaded the scene.
+ }
+ ERR_FAIL_COND_MSG(!stack || stack->skeleton == nullptr,
+ "Modification is not setup and therefore cannot calculate joint lengths!");
+ ERR_FAIL_COND_MSG(joint_one_bone_idx <= -1 || joint_two_bone_idx <= -1,
+ "One of the bones in the TwoBoneIK modification are not set! Cannot calculate joint lengths!");
+
+ Transform3D bone_one_rest_trans = stack->skeleton->get_bone_global_pose(joint_one_bone_idx);
+ Transform3D bone_two_rest_trans = stack->skeleton->get_bone_global_pose(joint_two_bone_idx);
+
+ joint_one_length = bone_one_rest_trans.origin.distance_to(bone_two_rest_trans.origin);
+
+ if (use_tip_node) {
+ if (tip_node_cache.is_null()) {
+ update_cache_tip();
+ WARN_PRINT("Tip cache is out of date. Updating...");
+ }
+
+ Node3D *tip = Object::cast_to<Node3D>(ObjectDB::get_instance(tip_node_cache));
+ if (tip) {
+ Transform3D bone_tip_trans = stack->skeleton->world_transform_to_global_pose(tip->get_global_transform());
+ joint_two_length = bone_two_rest_trans.origin.distance_to(bone_tip_trans.origin);
+ }
+ } else {
+ // Attempt to use children bones to get the length
+ Vector<int> bone_two_children = stack->skeleton->get_bone_children(joint_two_bone_idx);
+ if (bone_two_children.size() > 0) {
+ joint_two_length = 0;
+ for (int i = 0; i < bone_two_children.size(); i++) {
+ joint_two_length += bone_two_rest_trans.origin.distance_to(
+ stack->skeleton->local_pose_to_global_pose(bone_two_children[i], stack->skeleton->get_bone_rest(bone_two_children[i])).origin);
+ }
+ joint_two_length = joint_two_length / bone_two_children.size();
+ } else {
+ WARN_PRINT("TwoBoneIK modification: Cannot auto calculate length for joint 2! Auto setting the length to 1...");
+ joint_two_length = 1.0;
+ }
+ }
+ execution_error_found = false;
+}
+
+void SkeletonModification3DTwoBoneIK::set_joint_one_bone_name(String p_bone_name) {
+ joint_one_bone_name = p_bone_name;
+ if (stack && stack->skeleton) {
+ joint_one_bone_idx = stack->skeleton->find_bone(p_bone_name);
+ }
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+String SkeletonModification3DTwoBoneIK::get_joint_one_bone_name() const {
+ return joint_one_bone_name;
+}
+
+void SkeletonModification3DTwoBoneIK::set_joint_one_bone_idx(int p_bone_idx) {
+ joint_one_bone_idx = p_bone_idx;
+ if (stack && stack->skeleton) {
+ joint_one_bone_name = stack->skeleton->get_bone_name(p_bone_idx);
+ }
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+int SkeletonModification3DTwoBoneIK::get_joint_one_bone_idx() const {
+ return joint_one_bone_idx;
+}
+
+void SkeletonModification3DTwoBoneIK::set_joint_one_length(real_t p_length) {
+ joint_one_length = p_length;
+}
+
+real_t SkeletonModification3DTwoBoneIK::get_joint_one_length() const {
+ return joint_one_length;
+}
+
+void SkeletonModification3DTwoBoneIK::set_joint_two_bone_name(String p_bone_name) {
+ joint_two_bone_name = p_bone_name;
+ if (stack && stack->skeleton) {
+ joint_two_bone_idx = stack->skeleton->find_bone(p_bone_name);
+ }
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+String SkeletonModification3DTwoBoneIK::get_joint_two_bone_name() const {
+ return joint_two_bone_name;
+}
+
+void SkeletonModification3DTwoBoneIK::set_joint_two_bone_idx(int p_bone_idx) {
+ joint_two_bone_idx = p_bone_idx;
+ if (stack && stack->skeleton) {
+ joint_two_bone_name = stack->skeleton->get_bone_name(p_bone_idx);
+ }
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+int SkeletonModification3DTwoBoneIK::get_joint_two_bone_idx() const {
+ return joint_two_bone_idx;
+}
+
+void SkeletonModification3DTwoBoneIK::set_joint_two_length(real_t p_length) {
+ joint_two_length = p_length;
+}
+
+real_t SkeletonModification3DTwoBoneIK::get_joint_two_length() const {
+ return joint_two_length;
+}
+
+void SkeletonModification3DTwoBoneIK::set_joint_one_roll(real_t p_roll) {
+ joint_one_roll = p_roll;
+}
+
+real_t SkeletonModification3DTwoBoneIK::get_joint_one_roll() const {
+ return joint_one_roll;
+}
+
+void SkeletonModification3DTwoBoneIK::set_joint_two_roll(real_t p_roll) {
+ joint_two_roll = p_roll;
+}
+
+real_t SkeletonModification3DTwoBoneIK::get_joint_two_roll() const {
+ return joint_two_roll;
+}
+
+void SkeletonModification3DTwoBoneIK::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_target_node", "target_nodepath"), &SkeletonModification3DTwoBoneIK::set_target_node);
+ ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonModification3DTwoBoneIK::get_target_node);
+
+ ClassDB::bind_method(D_METHOD("set_use_pole_node", "use_pole_node"), &SkeletonModification3DTwoBoneIK::set_use_pole_node);
+ ClassDB::bind_method(D_METHOD("get_use_pole_node"), &SkeletonModification3DTwoBoneIK::get_use_pole_node);
+ ClassDB::bind_method(D_METHOD("set_pole_node", "pole_nodepath"), &SkeletonModification3DTwoBoneIK::set_pole_node);
+ ClassDB::bind_method(D_METHOD("get_pole_node"), &SkeletonModification3DTwoBoneIK::get_pole_node);
+
+ ClassDB::bind_method(D_METHOD("set_use_tip_node", "use_tip_node"), &SkeletonModification3DTwoBoneIK::set_use_tip_node);
+ ClassDB::bind_method(D_METHOD("get_use_tip_node"), &SkeletonModification3DTwoBoneIK::get_use_tip_node);
+ ClassDB::bind_method(D_METHOD("set_tip_node", "tip_nodepath"), &SkeletonModification3DTwoBoneIK::set_tip_node);
+ ClassDB::bind_method(D_METHOD("get_tip_node"), &SkeletonModification3DTwoBoneIK::get_tip_node);
+
+ ClassDB::bind_method(D_METHOD("set_auto_calculate_joint_length", "auto_calculate_joint_length"), &SkeletonModification3DTwoBoneIK::set_auto_calculate_joint_length);
+ ClassDB::bind_method(D_METHOD("get_auto_calculate_joint_length"), &SkeletonModification3DTwoBoneIK::get_auto_calculate_joint_length);
+
+ ClassDB::bind_method(D_METHOD("set_joint_one_bone_name", "bone_name"), &SkeletonModification3DTwoBoneIK::set_joint_one_bone_name);
+ ClassDB::bind_method(D_METHOD("get_joint_one_bone_name"), &SkeletonModification3DTwoBoneIK::get_joint_one_bone_name);
+ ClassDB::bind_method(D_METHOD("set_joint_one_bone_idx", "bone_idx"), &SkeletonModification3DTwoBoneIK::set_joint_one_bone_idx);
+ ClassDB::bind_method(D_METHOD("get_joint_one_bone_idx"), &SkeletonModification3DTwoBoneIK::get_joint_one_bone_idx);
+ ClassDB::bind_method(D_METHOD("set_joint_one_length", "bone_length"), &SkeletonModification3DTwoBoneIK::set_joint_one_length);
+ ClassDB::bind_method(D_METHOD("get_joint_one_length"), &SkeletonModification3DTwoBoneIK::get_joint_one_length);
+
+ ClassDB::bind_method(D_METHOD("set_joint_two_bone_name", "bone_name"), &SkeletonModification3DTwoBoneIK::set_joint_two_bone_name);
+ ClassDB::bind_method(D_METHOD("get_joint_two_bone_name"), &SkeletonModification3DTwoBoneIK::get_joint_two_bone_name);
+ ClassDB::bind_method(D_METHOD("set_joint_two_bone_idx", "bone_idx"), &SkeletonModification3DTwoBoneIK::set_joint_two_bone_idx);
+ ClassDB::bind_method(D_METHOD("get_joint_two_bone_idx"), &SkeletonModification3DTwoBoneIK::get_joint_two_bone_idx);
+ ClassDB::bind_method(D_METHOD("set_joint_two_length", "bone_length"), &SkeletonModification3DTwoBoneIK::set_joint_two_length);
+ ClassDB::bind_method(D_METHOD("get_joint_two_length"), &SkeletonModification3DTwoBoneIK::get_joint_two_length);
+
+ ClassDB::bind_method(D_METHOD("set_joint_one_roll", "roll"), &SkeletonModification3DTwoBoneIK::set_joint_one_roll);
+ ClassDB::bind_method(D_METHOD("get_joint_one_roll"), &SkeletonModification3DTwoBoneIK::get_joint_one_roll);
+ ClassDB::bind_method(D_METHOD("set_joint_two_roll", "roll"), &SkeletonModification3DTwoBoneIK::set_joint_two_roll);
+ ClassDB::bind_method(D_METHOD("get_joint_two_roll"), &SkeletonModification3DTwoBoneIK::get_joint_two_roll);
+
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node3D"), "set_target_node", "get_target_node");
+ ADD_GROUP("", "");
+}
+
+SkeletonModification3DTwoBoneIK::SkeletonModification3DTwoBoneIK() {
+ stack = nullptr;
+ is_setup = false;
+}
+
+SkeletonModification3DTwoBoneIK::~SkeletonModification3DTwoBoneIK() {
+}
diff --git a/scene/resources/skeleton_modification_3d_twoboneik.h b/scene/resources/skeleton_modification_3d_twoboneik.h
new file mode 100644
index 0000000000..e62d6cc497
--- /dev/null
+++ b/scene/resources/skeleton_modification_3d_twoboneik.h
@@ -0,0 +1,118 @@
+/*************************************************************************/
+/* skeleton_modification_3d_twoboneik.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "scene/3d/skeleton_3d.h"
+#include "scene/resources/skeleton_modification_3d.h"
+
+#ifndef SKELETONMODIFICATION3DTWOBONEIK_H
+#define SKELETONMODIFICATION3DTWOBONEIK_H
+
+class SkeletonModification3DTwoBoneIK : public SkeletonModification3D {
+ GDCLASS(SkeletonModification3DTwoBoneIK, SkeletonModification3D);
+
+private:
+ NodePath target_node;
+ ObjectID target_node_cache;
+
+ bool use_tip_node = false;
+ NodePath tip_node;
+ ObjectID tip_node_cache;
+
+ bool use_pole_node = false;
+ NodePath pole_node;
+ ObjectID pole_node_cache;
+
+ String joint_one_bone_name = "";
+ int joint_one_bone_idx = -1;
+ String joint_two_bone_name = "";
+ int joint_two_bone_idx = -1;
+
+ bool auto_calculate_joint_length = false;
+ real_t joint_one_length = -1;
+ real_t joint_two_length = -1;
+
+ real_t joint_one_roll = 0;
+ real_t joint_two_roll = 0;
+
+ void update_cache_target();
+ void update_cache_tip();
+ void update_cache_pole();
+
+protected:
+ static void _bind_methods();
+ bool _get(const StringName &p_path, Variant &r_ret) const;
+ bool _set(const StringName &p_path, const Variant &p_value);
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
+public:
+ virtual void _execute(real_t p_delta) override;
+ virtual void _setup_modification(SkeletonModificationStack3D *p_stack) override;
+
+ void set_target_node(const NodePath &p_target_node);
+ NodePath get_target_node() const;
+
+ void set_use_tip_node(const bool p_use_tip_node);
+ bool get_use_tip_node() const;
+ void set_tip_node(const NodePath &p_tip_node);
+ NodePath get_tip_node() const;
+
+ void set_use_pole_node(const bool p_use_pole_node);
+ bool get_use_pole_node() const;
+ void set_pole_node(const NodePath &p_pole_node);
+ NodePath get_pole_node() const;
+
+ void set_auto_calculate_joint_length(bool p_calculate);
+ bool get_auto_calculate_joint_length() const;
+ void calculate_joint_lengths();
+
+ void set_joint_one_bone_name(String p_bone_name);
+ String get_joint_one_bone_name() const;
+ void set_joint_one_bone_idx(int p_bone_idx);
+ int get_joint_one_bone_idx() const;
+ void set_joint_one_length(real_t p_length);
+ real_t get_joint_one_length() const;
+
+ void set_joint_two_bone_name(String p_bone_name);
+ String get_joint_two_bone_name() const;
+ void set_joint_two_bone_idx(int p_bone_idx);
+ int get_joint_two_bone_idx() const;
+ void set_joint_two_length(real_t p_length);
+ real_t get_joint_two_length() const;
+
+ void set_joint_one_roll(real_t p_roll);
+ real_t get_joint_one_roll() const;
+ void set_joint_two_roll(real_t p_roll);
+ real_t get_joint_two_roll() const;
+
+ SkeletonModification3DTwoBoneIK();
+ ~SkeletonModification3DTwoBoneIK();
+};
+
+#endif //SKELETONMODIFICATION3DTWOBONEIK_H
diff --git a/scene/resources/skeleton_modification_stack_3d.cpp b/scene/resources/skeleton_modification_stack_3d.cpp
new file mode 100644
index 0000000000..3fce0e5dbd
--- /dev/null
+++ b/scene/resources/skeleton_modification_stack_3d.cpp
@@ -0,0 +1,222 @@
+/*************************************************************************/
+/* skeleton_modification_stack_3d.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "skeleton_modification_stack_3d.h"
+#include "scene/3d/skeleton_3d.h"
+
+///////////////////////////////////////
+// ModificationStack3D
+///////////////////////////////////////
+
+void SkeletonModificationStack3D::_get_property_list(List<PropertyInfo> *p_list) const {
+ for (uint32_t i = 0; i < modifications.size(); i++) {
+ p_list->push_back(
+ PropertyInfo(Variant::OBJECT, "modifications/" + itos(i),
+ PROPERTY_HINT_RESOURCE_TYPE,
+ "SkeletonModification3D",
+ PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE));
+ }
+}
+
+bool SkeletonModificationStack3D::_set(const StringName &p_path, const Variant &p_value) {
+ String path = p_path;
+
+ if (path.begins_with("modifications/")) {
+ int mod_idx = path.get_slicec('/', 1).to_int();
+ set_modification(mod_idx, p_value);
+ return true;
+ }
+ return true;
+}
+
+bool SkeletonModificationStack3D::_get(const StringName &p_path, Variant &r_ret) const {
+ String path = p_path;
+
+ if (path.begins_with("modifications/")) {
+ int mod_idx = path.get_slicec('/', 1).to_int();
+ r_ret = get_modification(mod_idx);
+ return true;
+ }
+ return true;
+}
+
+void SkeletonModificationStack3D::setup() {
+ if (is_setup) {
+ return;
+ }
+
+ if (skeleton != nullptr) {
+ is_setup = true;
+ for (uint32_t i = 0; i < modifications.size(); i++) {
+ if (!modifications[i].is_valid()) {
+ continue;
+ }
+ modifications[i]->_setup_modification(this);
+ }
+ } else {
+ WARN_PRINT("Cannot setup SkeletonModificationStack3D: no skeleton set!");
+ }
+}
+
+void SkeletonModificationStack3D::execute(real_t p_delta, int p_execution_mode) {
+ ERR_FAIL_COND_MSG(!is_setup || skeleton == nullptr || is_queued_for_deletion(),
+ "Modification stack is not properly setup and therefore cannot execute!");
+
+ if (!skeleton->is_inside_tree()) {
+ ERR_PRINT_ONCE("Skeleton is not inside SceneTree! Cannot execute modification!");
+ return;
+ }
+
+ if (!enabled) {
+ return;
+ }
+
+ for (uint32_t i = 0; i < modifications.size(); i++) {
+ if (!modifications[i].is_valid()) {
+ continue;
+ }
+
+ if (modifications[i]->get_execution_mode() == p_execution_mode) {
+ modifications[i]->_execute(p_delta);
+ }
+ }
+}
+
+void SkeletonModificationStack3D::enable_all_modifications(bool p_enabled) {
+ for (uint32_t i = 0; i < modifications.size(); i++) {
+ if (!modifications[i].is_valid()) {
+ continue;
+ }
+ modifications[i]->set_enabled(p_enabled);
+ }
+}
+
+Ref<SkeletonModification3D> SkeletonModificationStack3D::get_modification(int p_mod_idx) const {
+ const int modifications_size = modifications.size();
+ ERR_FAIL_INDEX_V(p_mod_idx, modifications_size, nullptr);
+ return modifications[p_mod_idx];
+}
+
+void SkeletonModificationStack3D::add_modification(Ref<SkeletonModification3D> p_mod) {
+ p_mod->_setup_modification(this);
+ modifications.push_back(p_mod);
+}
+
+void SkeletonModificationStack3D::delete_modification(int p_mod_idx) {
+ const int modifications_size = modifications.size();
+ ERR_FAIL_INDEX(p_mod_idx, modifications_size);
+ modifications.remove(p_mod_idx);
+}
+
+void SkeletonModificationStack3D::set_modification(int p_mod_idx, Ref<SkeletonModification3D> p_mod) {
+ const int modifications_size = modifications.size();
+ ERR_FAIL_INDEX(p_mod_idx, modifications_size);
+
+ if (p_mod == nullptr) {
+ modifications.remove(p_mod_idx);
+ } else {
+ p_mod->_setup_modification(this);
+ modifications[p_mod_idx] = p_mod;
+ }
+}
+
+void SkeletonModificationStack3D::set_modification_count(int p_count) {
+ modifications.resize(p_count);
+ notify_property_list_changed();
+}
+
+int SkeletonModificationStack3D::get_modification_count() const {
+ return modifications.size();
+}
+
+void SkeletonModificationStack3D::set_skeleton(Skeleton3D *p_skeleton) {
+ skeleton = p_skeleton;
+}
+
+Skeleton3D *SkeletonModificationStack3D::get_skeleton() const {
+ return skeleton;
+}
+
+bool SkeletonModificationStack3D::get_is_setup() const {
+ return is_setup;
+}
+
+void SkeletonModificationStack3D::set_enabled(bool p_enabled) {
+ enabled = p_enabled;
+
+ if (!enabled && is_setup && skeleton != nullptr) {
+ skeleton->clear_bones_local_pose_override();
+ }
+}
+
+bool SkeletonModificationStack3D::get_enabled() const {
+ return enabled;
+}
+
+void SkeletonModificationStack3D::set_strength(real_t p_strength) {
+ ERR_FAIL_COND_MSG(p_strength < 0, "Strength cannot be less than zero!");
+ ERR_FAIL_COND_MSG(p_strength > 1, "Strength cannot be more than one!");
+ strength = p_strength;
+}
+
+real_t SkeletonModificationStack3D::get_strength() const {
+ return strength;
+}
+
+void SkeletonModificationStack3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("setup"), &SkeletonModificationStack3D::setup);
+ ClassDB::bind_method(D_METHOD("execute", "delta", "execution_mode"), &SkeletonModificationStack3D::execute);
+
+ ClassDB::bind_method(D_METHOD("enable_all_modifications", "enabled"), &SkeletonModificationStack3D::enable_all_modifications);
+ ClassDB::bind_method(D_METHOD("get_modification", "mod_idx"), &SkeletonModificationStack3D::get_modification);
+ ClassDB::bind_method(D_METHOD("add_modification", "modification"), &SkeletonModificationStack3D::add_modification);
+ ClassDB::bind_method(D_METHOD("delete_modification", "mod_idx"), &SkeletonModificationStack3D::delete_modification);
+ ClassDB::bind_method(D_METHOD("set_modification", "mod_idx", "modification"), &SkeletonModificationStack3D::set_modification);
+
+ ClassDB::bind_method(D_METHOD("set_modification_count"), &SkeletonModificationStack3D::set_modification_count);
+ ClassDB::bind_method(D_METHOD("get_modification_count"), &SkeletonModificationStack3D::get_modification_count);
+
+ ClassDB::bind_method(D_METHOD("get_is_setup"), &SkeletonModificationStack3D::get_is_setup);
+
+ ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &SkeletonModificationStack3D::set_enabled);
+ ClassDB::bind_method(D_METHOD("get_enabled"), &SkeletonModificationStack3D::get_enabled);
+
+ ClassDB::bind_method(D_METHOD("set_strength", "strength"), &SkeletonModificationStack3D::set_strength);
+ ClassDB::bind_method(D_METHOD("get_strength"), &SkeletonModificationStack3D::get_strength);
+
+ ClassDB::bind_method(D_METHOD("get_skeleton"), &SkeletonModificationStack3D::get_skeleton);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "get_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "strength", PROPERTY_HINT_RANGE, "0, 1, 0.001"), "set_strength", "get_strength");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "modification_count", PROPERTY_HINT_RANGE, "0, 100, 1"), "set_modification_count", "get_modification_count");
+}
+
+SkeletonModificationStack3D::SkeletonModificationStack3D() {
+}
diff --git a/scene/resources/skeleton_modification_stack_3d.h b/scene/resources/skeleton_modification_stack_3d.h
new file mode 100644
index 0000000000..cbc8d4e0b9
--- /dev/null
+++ b/scene/resources/skeleton_modification_stack_3d.h
@@ -0,0 +1,91 @@
+/*************************************************************************/
+/* skeleton_modification_stack_3d.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef SKELETONMODIFICATIONSTACK3D_H
+#define SKELETONMODIFICATIONSTACK3D_H
+
+#include "core/templates/local_vector.h"
+#include "scene/3d/skeleton_3d.h"
+
+class Skeleton3D;
+class SkeletonModification3D;
+
+class SkeletonModificationStack3D : public Resource {
+ GDCLASS(SkeletonModificationStack3D, Resource);
+ friend class Skeleton3D;
+ friend class SkeletonModification3D;
+
+protected:
+ static void _bind_methods();
+ virtual void _get_property_list(List<PropertyInfo> *p_list) const;
+ virtual bool _set(const StringName &p_path, const Variant &p_value);
+ virtual bool _get(const StringName &p_path, Variant &r_ret) const;
+
+public:
+ Skeleton3D *skeleton = nullptr;
+ bool is_setup = false;
+ bool enabled = false;
+ real_t strength = 1.0;
+
+ enum EXECUTION_MODE {
+ execution_mode_process,
+ execution_mode_physics_process,
+ };
+
+ LocalVector<Ref<SkeletonModification3D>> modifications = LocalVector<Ref<SkeletonModification3D>>();
+ int modifications_count = 0;
+
+ virtual void setup();
+ virtual void execute(real_t p_delta, int p_execution_mode);
+
+ void enable_all_modifications(bool p_enable);
+ Ref<SkeletonModification3D> get_modification(int p_mod_idx) const;
+ void add_modification(Ref<SkeletonModification3D> p_mod);
+ void delete_modification(int p_mod_idx);
+ void set_modification(int p_mod_idx, Ref<SkeletonModification3D> p_mod);
+
+ void set_modification_count(int p_count);
+ int get_modification_count() const;
+
+ void set_skeleton(Skeleton3D *p_skeleton);
+ Skeleton3D *get_skeleton() const;
+
+ bool get_is_setup() const;
+
+ void set_enabled(bool p_enabled);
+ bool get_enabled() const;
+
+ void set_strength(real_t p_strength);
+ real_t get_strength() const;
+
+ SkeletonModificationStack3D();
+};
+
+#endif // SKELETONMODIFICATIONSTACK3D_H
diff --git a/scene/resources/sky_material.cpp b/scene/resources/sky_material.cpp
index ec00f9d7b7..39082b6f7a 100644
--- a/scene/resources/sky_material.cpp
+++ b/scene/resources/sky_material.cpp
@@ -30,6 +30,8 @@
#include "sky_material.h"
+#include "core/version.h"
+
Mutex ProceduralSkyMaterial::shader_mutex;
RID ProceduralSkyMaterial::shader;
@@ -204,7 +206,10 @@ void ProceduralSkyMaterial::_update_shader() {
if (shader.is_null()) {
shader = RS::get_singleton()->shader_create();
+ // Add a comment to describe the shader origin (useful when converting to ShaderMaterial).
RS::get_singleton()->shader_set_code(shader, R"(
+// NOTE: Shader automatically converted from )" VERSION_NAME " " VERSION_FULL_CONFIG R"('s ProceduralSkyMaterial.
+
shader_type sky;
uniform vec4 sky_top_color : hint_color = vec4(0.35, 0.46, 0.71, 1.0);
@@ -350,10 +355,13 @@ void PanoramaSkyMaterial::_update_shader() {
if (shader.is_null()) {
shader = RS::get_singleton()->shader_create();
+ // Add a comment to describe the shader origin (useful when converting to ShaderMaterial).
RS::get_singleton()->shader_set_code(shader, R"(
+// NOTE: Shader automatically converted from )" VERSION_NAME " " VERSION_FULL_CONFIG R"('s PanoramaSkyMaterial.
+
shader_type sky;
-uniform sampler2D source_panorama : filter_linear;
+uniform sampler2D source_panorama : filter_linear, hint_albedo;
void sky() {
COLOR = texture(source_panorama, SKY_COORDS).rgb;
@@ -561,7 +569,10 @@ void PhysicalSkyMaterial::_update_shader() {
if (shader.is_null()) {
shader = RS::get_singleton()->shader_create();
+ // Add a comment to describe the shader origin (useful when converting to ShaderMaterial).
RS::get_singleton()->shader_set_code(shader, R"(
+// NOTE: Shader automatically converted from )" VERSION_NAME " " VERSION_FULL_CONFIG R"('s PhysicalSkyMaterial.
+
shader_type sky;
uniform float rayleigh : hint_range(0, 64) = 2.0;
@@ -576,7 +587,7 @@ uniform vec4 ground_color : hint_color = vec4(1.0);
uniform float exposure : hint_range(0, 128) = 0.1;
uniform float dither_strength : hint_range(0, 10) = 1.0;
-uniform sampler2D night_sky : hint_black;
+uniform sampler2D night_sky : hint_black_albedo;
const vec3 UP = vec3( 0.0, 1.0, 0.0 );
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index 75dd7448e7..b00dcca004 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -3032,6 +3032,10 @@ String VisualShaderNodeUniform::get_uniform_name() const {
}
void VisualShaderNodeUniform::set_qualifier(VisualShaderNodeUniform::Qualifier p_qual) {
+ ERR_FAIL_INDEX(int(p_qual), int(QUAL_MAX));
+ if (qualifier == p_qual) {
+ return;
+ }
qualifier = p_qual;
emit_changed();
}
@@ -3061,6 +3065,7 @@ void VisualShaderNodeUniform::_bind_methods() {
BIND_ENUM_CONSTANT(QUAL_NONE);
BIND_ENUM_CONSTANT(QUAL_GLOBAL);
BIND_ENUM_CONSTANT(QUAL_INSTANCE);
+ BIND_ENUM_CONSTANT(QUAL_MAX);
}
String VisualShaderNodeUniform::_get_qual_str() const {
@@ -3072,6 +3077,8 @@ String VisualShaderNodeUniform::_get_qual_str() const {
return "global ";
case QUAL_INSTANCE:
return "instance ";
+ default:
+ break;
}
}
return String();
@@ -3094,6 +3101,8 @@ String VisualShaderNodeUniform::get_warning(Shader::Mode p_mode, VisualShader::T
case QUAL_INSTANCE:
qualifier_str = "instance";
break;
+ default:
+ break;
}
return vformat(TTR("This uniform type does not support the '%s' qualifier."), qualifier_str);
} else if (qualifier == Qualifier::QUAL_GLOBAL) {
@@ -3350,6 +3359,10 @@ bool VisualShaderNodeGroupBase::is_valid_port_name(const String &p_name) const {
}
void VisualShaderNodeGroupBase::add_input_port(int p_id, int p_type, const String &p_name) {
+ ERR_FAIL_COND(has_input_port(p_id));
+ ERR_FAIL_INDEX(p_type, int(PORT_TYPE_MAX));
+ ERR_FAIL_COND(!is_valid_port_name(p_name));
+
String str = itos(p_id) + "," + itos(p_type) + "," + p_name + ";";
Vector<String> inputs_strings = inputs.split(";", false);
int index = 0;
@@ -3422,6 +3435,10 @@ bool VisualShaderNodeGroupBase::has_input_port(int p_id) const {
}
void VisualShaderNodeGroupBase::add_output_port(int p_id, int p_type, const String &p_name) {
+ ERR_FAIL_COND(has_output_port(p_id));
+ ERR_FAIL_INDEX(p_type, int(PORT_TYPE_MAX));
+ ERR_FAIL_COND(!is_valid_port_name(p_name));
+
String str = itos(p_id) + "," + itos(p_type) + "," + p_name + ";";
Vector<String> outputs_strings = outputs.split(";", false);
int index = 0;
@@ -3503,7 +3520,7 @@ void VisualShaderNodeGroupBase::clear_output_ports() {
void VisualShaderNodeGroupBase::set_input_port_type(int p_id, int p_type) {
ERR_FAIL_COND(!has_input_port(p_id));
- ERR_FAIL_COND(p_type < 0 || p_type >= PORT_TYPE_MAX);
+ ERR_FAIL_INDEX(p_type, int(PORT_TYPE_MAX));
if (input_ports[p_id].type == p_type) {
return;
@@ -3575,7 +3592,7 @@ String VisualShaderNodeGroupBase::get_input_port_name(int p_id) const {
void VisualShaderNodeGroupBase::set_output_port_type(int p_id, int p_type) {
ERR_FAIL_COND(!has_output_port(p_id));
- ERR_FAIL_COND(p_type < 0 || p_type >= PORT_TYPE_MAX);
+ ERR_FAIL_INDEX(p_type, int(PORT_TYPE_MAX));
if (output_ports[p_id].type == p_type) {
return;
diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h
index abf55185ab..31651318ae 100644
--- a/scene/resources/visual_shader.h
+++ b/scene/resources/visual_shader.h
@@ -435,6 +435,7 @@ public:
QUAL_NONE,
QUAL_GLOBAL,
QUAL_INSTANCE,
+ QUAL_MAX,
};
private:
diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp
index a8ec78f63e..e45dfdcb1b 100644
--- a/scene/resources/visual_shader_nodes.cpp
+++ b/scene/resources/visual_shader_nodes.cpp
@@ -69,8 +69,11 @@ String VisualShaderNodeFloatConstant::generate_code(Shader::Mode p_mode, VisualS
return " " + p_output_vars[0] + " = " + vformat("%.6f", constant) + ";\n";
}
-void VisualShaderNodeFloatConstant::set_constant(float p_value) {
- constant = p_value;
+void VisualShaderNodeFloatConstant::set_constant(float p_constant) {
+ if (Math::is_equal_approx(constant, p_constant)) {
+ return;
+ }
+ constant = p_constant;
emit_changed();
}
@@ -85,7 +88,7 @@ Vector<StringName> VisualShaderNodeFloatConstant::get_editable_properties() cons
}
void VisualShaderNodeFloatConstant::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_constant", "value"), &VisualShaderNodeFloatConstant::set_constant);
+ ClassDB::bind_method(D_METHOD("set_constant", "constant"), &VisualShaderNodeFloatConstant::set_constant);
ClassDB::bind_method(D_METHOD("get_constant"), &VisualShaderNodeFloatConstant::get_constant);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "constant"), "set_constant", "get_constant");
@@ -128,8 +131,11 @@ String VisualShaderNodeIntConstant::generate_code(Shader::Mode p_mode, VisualSha
return " " + p_output_vars[0] + " = " + itos(constant) + ";\n";
}
-void VisualShaderNodeIntConstant::set_constant(int p_value) {
- constant = p_value;
+void VisualShaderNodeIntConstant::set_constant(int p_constant) {
+ if (constant == p_constant) {
+ return;
+ }
+ constant = p_constant;
emit_changed();
}
@@ -144,7 +150,7 @@ Vector<StringName> VisualShaderNodeIntConstant::get_editable_properties() const
}
void VisualShaderNodeIntConstant::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_constant", "value"), &VisualShaderNodeIntConstant::set_constant);
+ ClassDB::bind_method(D_METHOD("set_constant", "constant"), &VisualShaderNodeIntConstant::set_constant);
ClassDB::bind_method(D_METHOD("get_constant"), &VisualShaderNodeIntConstant::get_constant);
ADD_PROPERTY(PropertyInfo(Variant::INT, "constant"), "set_constant", "get_constant");
@@ -187,8 +193,11 @@ String VisualShaderNodeBooleanConstant::generate_code(Shader::Mode p_mode, Visua
return " " + p_output_vars[0] + " = " + (constant ? "true" : "false") + ";\n";
}
-void VisualShaderNodeBooleanConstant::set_constant(bool p_value) {
- constant = p_value;
+void VisualShaderNodeBooleanConstant::set_constant(bool p_constant) {
+ if (constant == p_constant) {
+ return;
+ }
+ constant = p_constant;
emit_changed();
}
@@ -203,7 +212,7 @@ Vector<StringName> VisualShaderNodeBooleanConstant::get_editable_properties() co
}
void VisualShaderNodeBooleanConstant::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_constant", "value"), &VisualShaderNodeBooleanConstant::set_constant);
+ ClassDB::bind_method(D_METHOD("set_constant", "constant"), &VisualShaderNodeBooleanConstant::set_constant);
ClassDB::bind_method(D_METHOD("get_constant"), &VisualShaderNodeBooleanConstant::get_constant);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "constant"), "set_constant", "get_constant");
@@ -257,8 +266,11 @@ String VisualShaderNodeColorConstant::generate_code(Shader::Mode p_mode, VisualS
return code;
}
-void VisualShaderNodeColorConstant::set_constant(Color p_value) {
- constant = p_value;
+void VisualShaderNodeColorConstant::set_constant(const Color &p_constant) {
+ if (constant.is_equal_approx(p_constant)) {
+ return;
+ }
+ constant = p_constant;
emit_changed();
}
@@ -273,7 +285,7 @@ Vector<StringName> VisualShaderNodeColorConstant::get_editable_properties() cons
}
void VisualShaderNodeColorConstant::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_constant", "value"), &VisualShaderNodeColorConstant::set_constant);
+ ClassDB::bind_method(D_METHOD("set_constant", "constant"), &VisualShaderNodeColorConstant::set_constant);
ClassDB::bind_method(D_METHOD("get_constant"), &VisualShaderNodeColorConstant::get_constant);
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "constant"), "set_constant", "get_constant");
@@ -316,8 +328,11 @@ String VisualShaderNodeVec3Constant::generate_code(Shader::Mode p_mode, VisualSh
return " " + p_output_vars[0] + " = " + vformat("vec3(%.6f, %.6f, %.6f)", constant.x, constant.y, constant.z) + ";\n";
}
-void VisualShaderNodeVec3Constant::set_constant(Vector3 p_value) {
- constant = p_value;
+void VisualShaderNodeVec3Constant::set_constant(const Vector3 &p_constant) {
+ if (constant.is_equal_approx(p_constant)) {
+ return;
+ }
+ constant = p_constant;
emit_changed();
}
@@ -332,7 +347,7 @@ Vector<StringName> VisualShaderNodeVec3Constant::get_editable_properties() const
}
void VisualShaderNodeVec3Constant::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_constant", "value"), &VisualShaderNodeVec3Constant::set_constant);
+ ClassDB::bind_method(D_METHOD("set_constant", "constant"), &VisualShaderNodeVec3Constant::set_constant);
ClassDB::bind_method(D_METHOD("get_constant"), &VisualShaderNodeVec3Constant::get_constant);
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "constant"), "set_constant", "get_constant");
@@ -383,8 +398,11 @@ String VisualShaderNodeTransformConstant::generate_code(Shader::Mode p_mode, Vis
return code;
}
-void VisualShaderNodeTransformConstant::set_constant(Transform3D p_value) {
- constant = p_value;
+void VisualShaderNodeTransformConstant::set_constant(const Transform3D &p_constant) {
+ if (constant.is_equal_approx(p_constant)) {
+ return;
+ }
+ constant = p_constant;
emit_changed();
}
@@ -399,7 +417,7 @@ Vector<StringName> VisualShaderNodeTransformConstant::get_editable_properties()
}
void VisualShaderNodeTransformConstant::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_constant", "value"), &VisualShaderNodeTransformConstant::set_constant);
+ ClassDB::bind_method(D_METHOD("set_constant", "constant"), &VisualShaderNodeTransformConstant::set_constant);
ClassDB::bind_method(D_METHOD("get_constant"), &VisualShaderNodeTransformConstant::get_constant);
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "constant"), "set_constant", "get_constant");
@@ -502,6 +520,8 @@ String VisualShaderNodeTexture::generate_global(Shader::Mode p_mode, VisualShade
case TYPE_NORMAL_MAP:
u += " : hint_normal";
break;
+ default:
+ break;
}
return u + ";\n";
}
@@ -685,8 +705,11 @@ String VisualShaderNodeTexture::generate_code(Shader::Mode p_mode, VisualShader:
}
void VisualShaderNodeTexture::set_source(Source p_source) {
- source = p_source;
- switch (source) {
+ ERR_FAIL_INDEX(int(p_source), int(SOURCE_MAX));
+ if (source == p_source) {
+ return;
+ }
+ switch (p_source) {
case SOURCE_TEXTURE:
simple_decl = true;
break;
@@ -705,7 +728,10 @@ void VisualShaderNodeTexture::set_source(Source p_source) {
case SOURCE_PORT:
simple_decl = false;
break;
+ default:
+ break;
}
+ source = p_source;
emit_changed();
emit_signal(SNAME("editor_refresh_request"));
}
@@ -714,8 +740,8 @@ VisualShaderNodeTexture::Source VisualShaderNodeTexture::get_source() const {
return source;
}
-void VisualShaderNodeTexture::set_texture(Ref<Texture2D> p_value) {
- texture = p_value;
+void VisualShaderNodeTexture::set_texture(Ref<Texture2D> p_texture) {
+ texture = p_texture;
emit_changed();
}
@@ -723,8 +749,12 @@ Ref<Texture2D> VisualShaderNodeTexture::get_texture() const {
return texture;
}
-void VisualShaderNodeTexture::set_texture_type(TextureType p_type) {
- texture_type = p_type;
+void VisualShaderNodeTexture::set_texture_type(TextureType p_texture_type) {
+ ERR_FAIL_INDEX(int(p_texture_type), int(TYPE_MAX));
+ if (texture_type == p_texture_type) {
+ return;
+ }
+ texture_type = p_texture_type;
emit_changed();
}
@@ -797,9 +827,12 @@ void VisualShaderNodeTexture::_bind_methods() {
BIND_ENUM_CONSTANT(SOURCE_2D_NORMAL);
BIND_ENUM_CONSTANT(SOURCE_DEPTH);
BIND_ENUM_CONSTANT(SOURCE_PORT);
+ BIND_ENUM_CONSTANT(SOURCE_MAX);
+
BIND_ENUM_CONSTANT(TYPE_DATA);
BIND_ENUM_CONSTANT(TYPE_COLOR);
BIND_ENUM_CONSTANT(TYPE_NORMAL_MAP);
+ BIND_ENUM_CONSTANT(TYPE_MAX);
}
VisualShaderNodeTexture::VisualShaderNodeTexture() {
@@ -1074,6 +1107,10 @@ String VisualShaderNodeSample3D::generate_code(Shader::Mode p_mode, VisualShader
}
void VisualShaderNodeSample3D::set_source(Source p_source) {
+ ERR_FAIL_INDEX(int(p_source), int(SOURCE_MAX));
+ if (source == p_source) {
+ return;
+ }
source = p_source;
emit_changed();
emit_signal(SNAME("editor_refresh_request"));
@@ -1091,6 +1128,7 @@ void VisualShaderNodeSample3D::_bind_methods() {
BIND_ENUM_CONSTANT(SOURCE_TEXTURE);
BIND_ENUM_CONSTANT(SOURCE_PORT);
+ BIND_ENUM_CONSTANT(SOURCE_MAX);
}
String VisualShaderNodeSample3D::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const {
@@ -1127,7 +1165,7 @@ String VisualShaderNodeTexture2DArray::get_input_port_name(int p_port) const {
Vector<VisualShader::DefaultTextureParam> VisualShaderNodeTexture2DArray::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const {
VisualShader::DefaultTextureParam dtp;
dtp.name = make_unique_id(p_type, p_id, "tex3d");
- dtp.param = texture;
+ dtp.param = texture_array;
Vector<VisualShader::DefaultTextureParam> ret;
ret.push_back(dtp);
return ret;
@@ -1140,13 +1178,13 @@ String VisualShaderNodeTexture2DArray::generate_global(Shader::Mode p_mode, Visu
return String();
}
-void VisualShaderNodeTexture2DArray::set_texture_array(Ref<Texture2DArray> p_value) {
- texture = p_value;
+void VisualShaderNodeTexture2DArray::set_texture_array(Ref<Texture2DArray> p_texture_array) {
+ texture_array = p_texture_array;
emit_changed();
}
Ref<Texture2DArray> VisualShaderNodeTexture2DArray::get_texture_array() const {
- return texture;
+ return texture_array;
}
Vector<StringName> VisualShaderNodeTexture2DArray::get_editable_properties() const {
@@ -1197,8 +1235,8 @@ String VisualShaderNodeTexture3D::generate_global(Shader::Mode p_mode, VisualSha
return String();
}
-void VisualShaderNodeTexture3D::set_texture(Ref<Texture3D> p_value) {
- texture = p_value;
+void VisualShaderNodeTexture3D::set_texture(Ref<Texture3D> p_texture) {
+ texture = p_texture;
emit_changed();
}
@@ -1301,6 +1339,8 @@ String VisualShaderNodeCubemap::generate_global(Shader::Mode p_mode, VisualShade
case TYPE_NORMAL_MAP:
u += " : hint_normal";
break;
+ default:
+ break;
}
return u + ";\n";
}
@@ -1364,6 +1404,10 @@ String VisualShaderNodeCubemap::get_input_port_default_hint(int p_port) const {
}
void VisualShaderNodeCubemap::set_source(Source p_source) {
+ ERR_FAIL_INDEX(int(p_source), int(SOURCE_MAX));
+ if (source == p_source) {
+ return;
+ }
source = p_source;
emit_changed();
emit_signal(SNAME("editor_refresh_request"));
@@ -1373,8 +1417,8 @@ VisualShaderNodeCubemap::Source VisualShaderNodeCubemap::get_source() const {
return source;
}
-void VisualShaderNodeCubemap::set_cube_map(Ref<Cubemap> p_value) {
- cube_map = p_value;
+void VisualShaderNodeCubemap::set_cube_map(Ref<Cubemap> p_cube_map) {
+ cube_map = p_cube_map;
emit_changed();
}
@@ -1382,8 +1426,12 @@ Ref<Cubemap> VisualShaderNodeCubemap::get_cube_map() const {
return cube_map;
}
-void VisualShaderNodeCubemap::set_texture_type(TextureType p_type) {
- texture_type = p_type;
+void VisualShaderNodeCubemap::set_texture_type(TextureType p_texture_type) {
+ ERR_FAIL_INDEX(int(p_texture_type), int(TYPE_MAX));
+ if (texture_type == p_texture_type) {
+ return;
+ }
+ texture_type = p_texture_type;
emit_changed();
}
@@ -1424,10 +1472,12 @@ void VisualShaderNodeCubemap::_bind_methods() {
BIND_ENUM_CONSTANT(SOURCE_TEXTURE);
BIND_ENUM_CONSTANT(SOURCE_PORT);
+ BIND_ENUM_CONSTANT(SOURCE_MAX);
BIND_ENUM_CONSTANT(TYPE_DATA);
BIND_ENUM_CONSTANT(TYPE_COLOR);
BIND_ENUM_CONSTANT(TYPE_NORMAL_MAP);
+ BIND_ENUM_CONSTANT(TYPE_MAX);
}
VisualShaderNodeCubemap::VisualShaderNodeCubemap() {
@@ -1497,12 +1547,17 @@ String VisualShaderNodeFloatOp::generate_code(Shader::Mode p_mode, VisualShader:
case OP_STEP:
code += "step(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
break;
+ default:
+ break;
}
-
return code;
}
void VisualShaderNodeFloatOp::set_operator(Operator p_op) {
+ ERR_FAIL_INDEX(int(p_op), int(OP_ENUM_SIZE));
+ if (op == p_op) {
+ return;
+ }
op = p_op;
emit_changed();
}
@@ -1533,6 +1588,7 @@ void VisualShaderNodeFloatOp::_bind_methods() {
BIND_ENUM_CONSTANT(OP_MIN);
BIND_ENUM_CONSTANT(OP_ATAN2);
BIND_ENUM_CONSTANT(OP_STEP);
+ BIND_ENUM_CONSTANT(OP_ENUM_SIZE);
}
VisualShaderNodeFloatOp::VisualShaderNodeFloatOp() {
@@ -1594,12 +1650,18 @@ String VisualShaderNodeIntOp::generate_code(Shader::Mode p_mode, VisualShader::T
case OP_MIN:
code += "min(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
break;
+ default:
+ break;
}
return code;
}
void VisualShaderNodeIntOp::set_operator(Operator p_op) {
+ ERR_FAIL_INDEX(int(p_op), OP_ENUM_SIZE);
+ if (op == p_op) {
+ return;
+ }
op = p_op;
emit_changed();
}
@@ -1627,6 +1689,7 @@ void VisualShaderNodeIntOp::_bind_methods() {
BIND_ENUM_CONSTANT(OP_MOD);
BIND_ENUM_CONSTANT(OP_MAX);
BIND_ENUM_CONSTANT(OP_MIN);
+ BIND_ENUM_CONSTANT(OP_ENUM_SIZE);
}
VisualShaderNodeIntOp::VisualShaderNodeIntOp() {
@@ -1703,12 +1766,18 @@ String VisualShaderNodeVectorOp::generate_code(Shader::Mode p_mode, VisualShader
case OP_STEP:
code += "step(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
break;
+ default:
+ break;
}
return code;
}
void VisualShaderNodeVectorOp::set_operator(Operator p_op) {
+ ERR_FAIL_INDEX(int(p_op), int(OP_ENUM_SIZE));
+ if (op == p_op) {
+ return;
+ }
op = p_op;
emit_changed();
}
@@ -1741,6 +1810,7 @@ void VisualShaderNodeVectorOp::_bind_methods() {
BIND_ENUM_CONSTANT(OP_ATAN2);
BIND_ENUM_CONSTANT(OP_REFLECT);
BIND_ENUM_CONSTANT(OP_STEP);
+ BIND_ENUM_CONSTANT(OP_ENUM_SIZE);
}
VisualShaderNodeVectorOp::VisualShaderNodeVectorOp() {
@@ -1844,14 +1914,19 @@ String VisualShaderNodeColorOp::generate_code(Shader::Mode p_mode, VisualShader:
}
} break;
+ default:
+ break;
}
return code;
}
void VisualShaderNodeColorOp::set_operator(Operator p_op) {
- op = p_op;
- switch (op) {
+ ERR_FAIL_INDEX(int(p_op), int(OP_MAX));
+ if (op == p_op) {
+ return;
+ }
+ switch (p_op) {
case OP_SCREEN:
simple_decl = true;
break;
@@ -1879,7 +1954,10 @@ void VisualShaderNodeColorOp::set_operator(Operator p_op) {
case OP_HARD_LIGHT:
simple_decl = false;
break;
+ default:
+ break;
}
+ op = p_op;
emit_changed();
}
@@ -1908,6 +1986,7 @@ void VisualShaderNodeColorOp::_bind_methods() {
BIND_ENUM_CONSTANT(OP_BURN);
BIND_ENUM_CONSTANT(OP_SOFT_LIGHT);
BIND_ENUM_CONSTANT(OP_HARD_LIGHT);
+ BIND_ENUM_CONSTANT(OP_MAX);
}
VisualShaderNodeColorOp::VisualShaderNodeColorOp() {
@@ -1971,7 +2050,10 @@ String VisualShaderNodeTransformOp::generate_code(Shader::Mode p_mode, VisualSha
}
void VisualShaderNodeTransformOp::set_operator(Operator p_op) {
- ERR_FAIL_INDEX(int(p_op), int(OP_LIMITER));
+ ERR_FAIL_INDEX(int(p_op), int(OP_MAX));
+ if (op == p_op) {
+ return;
+ }
op = p_op;
emit_changed();
}
@@ -2001,7 +2083,7 @@ void VisualShaderNodeTransformOp::_bind_methods() {
BIND_ENUM_CONSTANT(OP_B_MINUS_A);
BIND_ENUM_CONSTANT(OP_A_DIV_B);
BIND_ENUM_CONSTANT(OP_B_DIV_A);
- BIND_ENUM_CONSTANT(OP_LIMITER);
+ BIND_ENUM_CONSTANT(OP_MAX);
}
VisualShaderNodeTransformOp::VisualShaderNodeTransformOp() {
@@ -2052,6 +2134,10 @@ String VisualShaderNodeTransformVecMult::generate_code(Shader::Mode p_mode, Visu
}
void VisualShaderNodeTransformVecMult::set_operator(Operator p_op) {
+ ERR_FAIL_INDEX(int(p_op), int(OP_MAX));
+ if (op == p_op) {
+ return;
+ }
op = p_op;
emit_changed();
}
@@ -2076,6 +2162,7 @@ void VisualShaderNodeTransformVecMult::_bind_methods() {
BIND_ENUM_CONSTANT(OP_BxA);
BIND_ENUM_CONSTANT(OP_3x3_AxB);
BIND_ENUM_CONSTANT(OP_3x3_BxA);
+ BIND_ENUM_CONSTANT(OP_MAX);
}
VisualShaderNodeTransformVecMult::VisualShaderNodeTransformVecMult() {
@@ -2114,7 +2201,7 @@ String VisualShaderNodeFloatFunc::get_output_port_name(int p_port) const {
}
String VisualShaderNodeFloatFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- static const char *scalar_func_id[FUNC_ONEMINUS + 1] = {
+ static const char *functions[FUNC_MAX] = {
"sin($)",
"cos($)",
"tan($)",
@@ -2148,11 +2235,14 @@ String VisualShaderNodeFloatFunc::generate_code(Shader::Mode p_mode, VisualShade
"trunc($)",
"1.0 - $"
};
-
- return " " + p_output_vars[0] + " = " + String(scalar_func_id[func]).replace("$", p_input_vars[0]) + ";\n";
+ return " " + p_output_vars[0] + " = " + String(functions[func]).replace("$", p_input_vars[0]) + ";\n";
}
void VisualShaderNodeFloatFunc::set_function(Function p_func) {
+ ERR_FAIL_INDEX(int(p_func), int(FUNC_MAX));
+ if (func == p_func) {
+ return;
+ }
func = p_func;
emit_changed();
}
@@ -2205,6 +2295,7 @@ void VisualShaderNodeFloatFunc::_bind_methods() {
BIND_ENUM_CONSTANT(FUNC_ROUNDEVEN);
BIND_ENUM_CONSTANT(FUNC_TRUNC);
BIND_ENUM_CONSTANT(FUNC_ONEMINUS);
+ BIND_ENUM_CONSTANT(FUNC_MAX);
}
VisualShaderNodeFloatFunc::VisualShaderNodeFloatFunc() {
@@ -2242,16 +2333,20 @@ String VisualShaderNodeIntFunc::get_output_port_name(int p_port) const {
}
String VisualShaderNodeIntFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- static const char *int_func_id[FUNC_SIGN + 1] = {
+ static const char *functions[FUNC_MAX] = {
"abs($)",
"-($)",
"sign($)"
};
- return " " + p_output_vars[0] + " = " + String(int_func_id[func]).replace("$", p_input_vars[0]) + ";\n";
+ return " " + p_output_vars[0] + " = " + String(functions[func]).replace("$", p_input_vars[0]) + ";\n";
}
void VisualShaderNodeIntFunc::set_function(Function p_func) {
+ ERR_FAIL_INDEX(int(p_func), int(FUNC_MAX));
+ if (func == p_func) {
+ return;
+ }
func = p_func;
emit_changed();
}
@@ -2275,6 +2370,7 @@ void VisualShaderNodeIntFunc::_bind_methods() {
BIND_ENUM_CONSTANT(FUNC_ABS);
BIND_ENUM_CONSTANT(FUNC_NEGATE);
BIND_ENUM_CONSTANT(FUNC_SIGN);
+ BIND_ENUM_CONSTANT(FUNC_MAX);
}
VisualShaderNodeIntFunc::VisualShaderNodeIntFunc() {
@@ -2312,7 +2408,7 @@ String VisualShaderNodeVectorFunc::get_output_port_name(int p_port) const {
}
String VisualShaderNodeVectorFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- static const char *vec_func_id[FUNC_ONEMINUS + 1] = {
+ static const char *vec_func_id[FUNC_MAX] = {
"normalize($)",
"max(min($, vec3(1.0)), vec3(0.0))",
"-($)",
@@ -2378,14 +2474,18 @@ String VisualShaderNodeVectorFunc::generate_code(Shader::Mode p_mode, VisualShad
}
void VisualShaderNodeVectorFunc::set_function(Function p_func) {
- func = p_func;
- if (func == FUNC_RGB2HSV) {
+ ERR_FAIL_INDEX(int(p_func), int(FUNC_MAX));
+ if (func == p_func) {
+ return;
+ }
+ if (p_func == FUNC_RGB2HSV) {
simple_decl = false;
- } else if (func == FUNC_HSV2RGB) {
+ } else if (p_func == FUNC_HSV2RGB) {
simple_decl = false;
} else {
simple_decl = true;
}
+ func = p_func;
emit_changed();
}
@@ -2440,6 +2540,7 @@ void VisualShaderNodeVectorFunc::_bind_methods() {
BIND_ENUM_CONSTANT(FUNC_TANH);
BIND_ENUM_CONSTANT(FUNC_TRUNC);
BIND_ENUM_CONSTANT(FUNC_ONEMINUS);
+ BIND_ENUM_CONSTANT(FUNC_MAX);
}
VisualShaderNodeVectorFunc::VisualShaderNodeVectorFunc() {
@@ -2498,12 +2599,18 @@ String VisualShaderNodeColorFunc::generate_code(Shader::Mode p_mode, VisualShade
code += " " + p_output_vars[0] + " = vec3(r, g, b);\n";
code += " }\n";
break;
+ default:
+ break;
}
return code;
}
void VisualShaderNodeColorFunc::set_function(Function p_func) {
+ ERR_FAIL_INDEX(int(p_func), int(FUNC_MAX));
+ if (func == p_func) {
+ return;
+ }
func = p_func;
emit_changed();
}
@@ -2526,6 +2633,7 @@ void VisualShaderNodeColorFunc::_bind_methods() {
BIND_ENUM_CONSTANT(FUNC_GRAYSCALE);
BIND_ENUM_CONSTANT(FUNC_SEPIA);
+ BIND_ENUM_CONSTANT(FUNC_MAX);
}
VisualShaderNodeColorFunc::VisualShaderNodeColorFunc() {
@@ -2564,17 +2672,21 @@ String VisualShaderNodeTransformFunc::get_output_port_name(int p_port) const {
}
String VisualShaderNodeTransformFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- static const char *funcs[FUNC_TRANSPOSE + 1] = {
+ static const char *functions[FUNC_MAX] = {
"inverse($)",
"transpose($)"
};
String code;
- code += " " + p_output_vars[0] + " = " + String(funcs[func]).replace("$", p_input_vars[0]) + ";\n";
+ code += " " + p_output_vars[0] + " = " + String(functions[func]).replace("$", p_input_vars[0]) + ";\n";
return code;
}
void VisualShaderNodeTransformFunc::set_function(Function p_func) {
+ ERR_FAIL_INDEX(int(p_func), int(FUNC_MAX));
+ if (func == p_func) {
+ return;
+ }
func = p_func;
emit_changed();
}
@@ -2597,6 +2709,7 @@ void VisualShaderNodeTransformFunc::_bind_methods() {
BIND_ENUM_CONSTANT(FUNC_INVERSE);
BIND_ENUM_CONSTANT(FUNC_TRANSPOSE);
+ BIND_ENUM_CONSTANT(FUNC_MAX);
}
VisualShaderNodeTransformFunc::VisualShaderNodeTransformFunc() {
@@ -2639,8 +2752,6 @@ String VisualShaderNodeUVFunc::get_input_port_name(int p_port) const {
return "offset";
case FUNC_SCALING:
return "pivot";
- case FUNC_MAX:
- break;
default:
break;
}
@@ -2693,24 +2804,23 @@ String VisualShaderNodeUVFunc::generate_code(Shader::Mode p_mode, VisualShader::
case FUNC_SCALING: {
code += vformat(" %s = fma((%s - %s), %s, %s);\n", p_output_vars[0], uv, offset_pivot, scale, offset_pivot);
} break;
- case FUNC_MAX:
+ default:
break;
}
return code;
}
void VisualShaderNodeUVFunc::set_function(VisualShaderNodeUVFunc::Function p_func) {
- ERR_FAIL_INDEX(int(p_func), FUNC_MAX);
+ ERR_FAIL_INDEX(int(p_func), int(FUNC_MAX));
if (func == p_func) {
return;
}
- func = p_func;
-
if (p_func == FUNC_PANNING) {
set_input_port_default_value(2, Vector3()); // offset
} else { // FUNC_SCALING
set_input_port_default_value(2, Vector3(0.5, 0.5, 0.0)); // pivot
}
+ func = p_func;
emit_changed();
}
@@ -2886,18 +2996,22 @@ String VisualShaderNodeScalarDerivativeFunc::get_output_port_name(int p_port) co
}
String VisualShaderNodeScalarDerivativeFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- static const char *funcs[FUNC_Y + 1] = {
+ static const char *functions[FUNC_MAX] = {
"fwidth($)",
"dFdx($)",
"dFdy($)"
};
String code;
- code += " " + p_output_vars[0] + " = " + String(funcs[func]).replace("$", p_input_vars[0]) + ";\n";
+ code += " " + p_output_vars[0] + " = " + String(functions[func]).replace("$", p_input_vars[0]) + ";\n";
return code;
}
void VisualShaderNodeScalarDerivativeFunc::set_function(Function p_func) {
+ ERR_FAIL_INDEX(int(p_func), int(FUNC_MAX));
+ if (func == p_func) {
+ return;
+ }
func = p_func;
emit_changed();
}
@@ -2921,6 +3035,7 @@ void VisualShaderNodeScalarDerivativeFunc::_bind_methods() {
BIND_ENUM_CONSTANT(FUNC_SUM);
BIND_ENUM_CONSTANT(FUNC_X);
BIND_ENUM_CONSTANT(FUNC_Y);
+ BIND_ENUM_CONSTANT(FUNC_MAX);
}
VisualShaderNodeScalarDerivativeFunc::VisualShaderNodeScalarDerivativeFunc() {
@@ -2958,18 +3073,22 @@ String VisualShaderNodeVectorDerivativeFunc::get_output_port_name(int p_port) co
}
String VisualShaderNodeVectorDerivativeFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- static const char *funcs[FUNC_Y + 1] = {
+ static const char *functions[FUNC_MAX] = {
"fwidth($)",
"dFdx($)",
"dFdy($)"
};
String code;
- code += " " + p_output_vars[0] + " = " + String(funcs[func]).replace("$", p_input_vars[0]) + ";\n";
+ code += " " + p_output_vars[0] + " = " + String(functions[func]).replace("$", p_input_vars[0]) + ";\n";
return code;
}
void VisualShaderNodeVectorDerivativeFunc::set_function(Function p_func) {
+ ERR_FAIL_INDEX(int(p_func), int(FUNC_MAX));
+ if (func == p_func) {
+ return;
+ }
func = p_func;
emit_changed();
}
@@ -2993,6 +3112,7 @@ void VisualShaderNodeVectorDerivativeFunc::_bind_methods() {
BIND_ENUM_CONSTANT(FUNC_SUM);
BIND_ENUM_CONSTANT(FUNC_X);
BIND_ENUM_CONSTANT(FUNC_Y);
+ BIND_ENUM_CONSTANT(FUNC_MAX);
}
VisualShaderNodeVectorDerivativeFunc::VisualShaderNodeVectorDerivativeFunc() {
@@ -3057,7 +3177,7 @@ String VisualShaderNodeClamp::generate_code(Shader::Mode p_mode, VisualShader::T
}
void VisualShaderNodeClamp::set_op_type(OpType p_op_type) {
- ERR_FAIL_INDEX((int)p_op_type, OP_TYPE_MAX);
+ ERR_FAIL_INDEX((int)p_op_type, int(OP_TYPE_MAX));
if (op_type == p_op_type) {
return;
}
@@ -3095,7 +3215,7 @@ Vector<StringName> VisualShaderNodeClamp::get_editable_properties() const {
}
void VisualShaderNodeClamp::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_op_type", "type"), &VisualShaderNodeClamp::set_op_type);
+ ClassDB::bind_method(D_METHOD("set_op_type", "op_type"), &VisualShaderNodeClamp::set_op_type);
ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeClamp::get_op_type);
ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Float,Int,Vector"), "set_op_type", "get_op_type");
@@ -3262,7 +3382,7 @@ String VisualShaderNodeStep::get_output_port_name(int p_port) const {
}
void VisualShaderNodeStep::set_op_type(OpType p_op_type) {
- ERR_FAIL_INDEX((int)p_op_type, OP_TYPE_MAX);
+ ERR_FAIL_INDEX(int(p_op_type), int(OP_TYPE_MAX));
if (op_type == p_op_type) {
return;
}
@@ -3313,7 +3433,7 @@ Vector<StringName> VisualShaderNodeStep::get_editable_properties() const {
}
void VisualShaderNodeStep::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_op_type", "type"), &VisualShaderNodeStep::set_op_type);
+ ClassDB::bind_method(D_METHOD("set_op_type", "op_type"), &VisualShaderNodeStep::set_op_type);
ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeStep::get_op_type);
ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector,VectorScalar"), "set_op_type", "get_op_type");
@@ -3386,7 +3506,7 @@ String VisualShaderNodeSmoothStep::get_output_port_name(int p_port) const {
}
void VisualShaderNodeSmoothStep::set_op_type(OpType p_op_type) {
- ERR_FAIL_INDEX((int)p_op_type, OP_TYPE_MAX);
+ ERR_FAIL_INDEX(int(p_op_type), int(OP_TYPE_MAX));
if (op_type == p_op_type) {
return;
}
@@ -3440,7 +3560,7 @@ Vector<StringName> VisualShaderNodeSmoothStep::get_editable_properties() const {
}
void VisualShaderNodeSmoothStep::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_op_type", "type"), &VisualShaderNodeSmoothStep::set_op_type);
+ ClassDB::bind_method(D_METHOD("set_op_type", "op_type"), &VisualShaderNodeSmoothStep::set_op_type);
ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeSmoothStep::get_op_type);
ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector,VectorScalar"), "set_op_type", "get_op_type");
@@ -3608,7 +3728,7 @@ String VisualShaderNodeMix::get_output_port_name(int p_port) const {
}
void VisualShaderNodeMix::set_op_type(OpType p_op_type) {
- ERR_FAIL_INDEX((int)p_op_type, OP_TYPE_MAX);
+ ERR_FAIL_INDEX(int(p_op_type), int(OP_TYPE_MAX));
if (op_type == p_op_type) {
return;
}
@@ -3656,7 +3776,7 @@ Vector<StringName> VisualShaderNodeMix::get_editable_properties() const {
}
void VisualShaderNodeMix::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_op_type", "type"), &VisualShaderNodeMix::set_op_type);
+ ClassDB::bind_method(D_METHOD("set_op_type", "op_type"), &VisualShaderNodeMix::set_op_type);
ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeMix::get_op_type);
ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector,VectorScalar"), "set_op_type", "get_op_type");
@@ -3925,6 +4045,10 @@ bool VisualShaderNodeFloatUniform::is_use_prop_slots() const {
}
void VisualShaderNodeFloatUniform::set_hint(Hint p_hint) {
+ ERR_FAIL_INDEX(int(p_hint), int(HINT_MAX));
+ if (hint == p_hint) {
+ return;
+ }
hint = p_hint;
emit_changed();
}
@@ -3934,6 +4058,9 @@ VisualShaderNodeFloatUniform::Hint VisualShaderNodeFloatUniform::get_hint() cons
}
void VisualShaderNodeFloatUniform::set_min(float p_value) {
+ if (Math::is_equal_approx(hint_range_min, p_value)) {
+ return;
+ }
hint_range_min = p_value;
emit_changed();
}
@@ -3943,6 +4070,9 @@ float VisualShaderNodeFloatUniform::get_min() const {
}
void VisualShaderNodeFloatUniform::set_max(float p_value) {
+ if (Math::is_equal_approx(hint_range_max, p_value)) {
+ return;
+ }
hint_range_max = p_value;
emit_changed();
}
@@ -3952,6 +4082,9 @@ float VisualShaderNodeFloatUniform::get_max() const {
}
void VisualShaderNodeFloatUniform::set_step(float p_value) {
+ if (Math::is_equal_approx(hint_range_step, p_value)) {
+ return;
+ }
hint_range_step = p_value;
emit_changed();
}
@@ -3961,6 +4094,9 @@ float VisualShaderNodeFloatUniform::get_step() const {
}
void VisualShaderNodeFloatUniform::set_default_value_enabled(bool p_enabled) {
+ if (default_value_enabled == p_enabled) {
+ return;
+ }
default_value_enabled = p_enabled;
emit_changed();
}
@@ -3970,6 +4106,9 @@ bool VisualShaderNodeFloatUniform::is_default_value_enabled() const {
}
void VisualShaderNodeFloatUniform::set_default_value(float p_value) {
+ if (Math::is_equal_approx(default_value, p_value)) {
+ return;
+ }
default_value = p_value;
emit_changed();
}
@@ -4007,6 +4146,7 @@ void VisualShaderNodeFloatUniform::_bind_methods() {
BIND_ENUM_CONSTANT(HINT_NONE);
BIND_ENUM_CONSTANT(HINT_RANGE);
BIND_ENUM_CONSTANT(HINT_RANGE_STEP);
+ BIND_ENUM_CONSTANT(HINT_MAX);
}
bool VisualShaderNodeFloatUniform::is_qualifier_supported(Qualifier p_qual) const {
@@ -4096,6 +4236,10 @@ bool VisualShaderNodeIntUniform::is_use_prop_slots() const {
}
void VisualShaderNodeIntUniform::set_hint(Hint p_hint) {
+ ERR_FAIL_INDEX(int(p_hint), int(HINT_MAX));
+ if (hint == p_hint) {
+ return;
+ }
hint = p_hint;
emit_changed();
}
@@ -4105,6 +4249,9 @@ VisualShaderNodeIntUniform::Hint VisualShaderNodeIntUniform::get_hint() const {
}
void VisualShaderNodeIntUniform::set_min(int p_value) {
+ if (hint_range_min == p_value) {
+ return;
+ }
hint_range_min = p_value;
emit_changed();
}
@@ -4114,6 +4261,9 @@ int VisualShaderNodeIntUniform::get_min() const {
}
void VisualShaderNodeIntUniform::set_max(int p_value) {
+ if (hint_range_max == p_value) {
+ return;
+ }
hint_range_max = p_value;
emit_changed();
}
@@ -4123,6 +4273,9 @@ int VisualShaderNodeIntUniform::get_max() const {
}
void VisualShaderNodeIntUniform::set_step(int p_value) {
+ if (hint_range_step == p_value) {
+ return;
+ }
hint_range_step = p_value;
emit_changed();
}
@@ -4131,8 +4284,11 @@ int VisualShaderNodeIntUniform::get_step() const {
return hint_range_step;
}
-void VisualShaderNodeIntUniform::set_default_value_enabled(bool p_enabled) {
- default_value_enabled = p_enabled;
+void VisualShaderNodeIntUniform::set_default_value_enabled(bool p_default_value_enabled) {
+ if (default_value_enabled == p_default_value_enabled) {
+ return;
+ }
+ default_value_enabled = p_default_value_enabled;
emit_changed();
}
@@ -4140,8 +4296,11 @@ bool VisualShaderNodeIntUniform::is_default_value_enabled() const {
return default_value_enabled;
}
-void VisualShaderNodeIntUniform::set_default_value(int p_value) {
- default_value = p_value;
+void VisualShaderNodeIntUniform::set_default_value(int p_default_value) {
+ if (default_value == p_default_value) {
+ return;
+ }
+ default_value = p_default_value;
emit_changed();
}
@@ -4178,6 +4337,7 @@ void VisualShaderNodeIntUniform::_bind_methods() {
BIND_ENUM_CONSTANT(HINT_NONE);
BIND_ENUM_CONSTANT(HINT_RANGE);
BIND_ENUM_CONSTANT(HINT_RANGE_STEP);
+ BIND_ENUM_CONSTANT(HINT_MAX);
}
bool VisualShaderNodeIntUniform::is_qualifier_supported(Qualifier p_qual) const {
@@ -4238,8 +4398,11 @@ String VisualShaderNodeBooleanUniform::get_output_port_name(int p_port) const {
return ""; //no output port means the editor will be used as port
}
-void VisualShaderNodeBooleanUniform::set_default_value_enabled(bool p_enabled) {
- default_value_enabled = p_enabled;
+void VisualShaderNodeBooleanUniform::set_default_value_enabled(bool p_default_value_enabled) {
+ if (default_value_enabled == p_default_value_enabled) {
+ return;
+ }
+ default_value_enabled = p_default_value_enabled;
emit_changed();
}
@@ -4247,8 +4410,11 @@ bool VisualShaderNodeBooleanUniform::is_default_value_enabled() const {
return default_value_enabled;
}
-void VisualShaderNodeBooleanUniform::set_default_value(bool p_value) {
- default_value = p_value;
+void VisualShaderNodeBooleanUniform::set_default_value(bool p_default_value) {
+ if (default_value == p_default_value) {
+ return;
+ }
+ default_value = p_default_value;
emit_changed();
}
@@ -4343,6 +4509,9 @@ String VisualShaderNodeColorUniform::get_output_port_name(int p_port) const {
}
void VisualShaderNodeColorUniform::set_default_value_enabled(bool p_enabled) {
+ if (default_value_enabled == p_enabled) {
+ return;
+ }
default_value_enabled = p_enabled;
emit_changed();
}
@@ -4352,6 +4521,9 @@ bool VisualShaderNodeColorUniform::is_default_value_enabled() const {
}
void VisualShaderNodeColorUniform::set_default_value(const Color &p_value) {
+ if (default_value.is_equal_approx(p_value)) {
+ return;
+ }
default_value = p_value;
emit_changed();
}
@@ -4689,6 +4861,9 @@ String VisualShaderNodeTextureUniform::generate_global(Shader::Mode p_mode, Visu
case TYPE_ANISO:
code += " : hint_aniso;\n";
break;
+ default:
+ code += ";\n";
+ break;
}
return code;
@@ -4727,8 +4902,12 @@ String VisualShaderNodeTextureUniform::generate_code(Shader::Mode p_mode, Visual
return code;
}
-void VisualShaderNodeTextureUniform::set_texture_type(TextureType p_type) {
- texture_type = p_type;
+void VisualShaderNodeTextureUniform::set_texture_type(TextureType p_texture_type) {
+ ERR_FAIL_INDEX(int(p_texture_type), int(TYPE_MAX));
+ if (texture_type == p_texture_type) {
+ return;
+ }
+ texture_type = p_texture_type;
emit_changed();
}
@@ -4736,8 +4915,12 @@ VisualShaderNodeTextureUniform::TextureType VisualShaderNodeTextureUniform::get_
return texture_type;
}
-void VisualShaderNodeTextureUniform::set_color_default(ColorDefault p_default) {
- color_default = p_default;
+void VisualShaderNodeTextureUniform::set_color_default(ColorDefault p_color_default) {
+ ERR_FAIL_INDEX(int(p_color_default), int(COLOR_DEFAULT_MAX));
+ if (color_default == p_color_default) {
+ return;
+ }
+ color_default = p_color_default;
emit_changed();
}
@@ -4766,9 +4949,11 @@ void VisualShaderNodeTextureUniform::_bind_methods() {
BIND_ENUM_CONSTANT(TYPE_COLOR);
BIND_ENUM_CONSTANT(TYPE_NORMAL_MAP);
BIND_ENUM_CONSTANT(TYPE_ANISO);
+ BIND_ENUM_CONSTANT(TYPE_MAX);
BIND_ENUM_CONSTANT(COLOR_DEFAULT_WHITE);
BIND_ENUM_CONSTANT(COLOR_DEFAULT_BLACK);
+ BIND_ENUM_CONSTANT(COLOR_DEFAULT_MAX);
}
String VisualShaderNodeTextureUniform::get_input_port_default_hint(int p_port) const {
@@ -4786,6 +4971,8 @@ bool VisualShaderNodeTextureUniform::is_qualifier_supported(Qualifier p_qual) co
return true;
case Qualifier::QUAL_INSTANCE:
return false;
+ default:
+ break;
}
return false;
}
@@ -4951,6 +5138,9 @@ String VisualShaderNodeTexture2DArrayUniform::generate_global(Shader::Mode p_mod
case TYPE_ANISO:
code += " : hint_aniso;\n";
break;
+ default:
+ code += ";\n";
+ break;
}
return code;
@@ -5021,6 +5211,9 @@ String VisualShaderNodeTexture3DUniform::generate_global(Shader::Mode p_mode, Vi
case TYPE_ANISO:
code += " : hint_aniso;\n";
break;
+ default:
+ code += ";\n";
+ break;
}
return code;
@@ -5091,6 +5284,9 @@ String VisualShaderNodeCubemapUniform::generate_global(Shader::Mode p_mode, Visu
case TYPE_ANISO:
code += " : hint_aniso;\n";
break;
+ default:
+ code += ";\n";
+ break;
}
return code;
@@ -5247,7 +5443,7 @@ String VisualShaderNodeSwitch::get_output_port_name(int p_port) const {
}
void VisualShaderNodeSwitch::set_op_type(OpType p_op_type) {
- ERR_FAIL_INDEX((int)p_op_type, OP_TYPE_MAX);
+ ERR_FAIL_INDEX(int(p_op_type), int(OP_TYPE_MAX));
if (op_type == p_op_type) {
return;
}
@@ -5452,17 +5648,21 @@ String VisualShaderNodeIs::get_output_port_name(int p_port) const {
}
String VisualShaderNodeIs::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- static const char *funcs[FUNC_IS_NAN + 1] = {
+ static const char *functions[FUNC_MAX] = {
"isinf($)",
"isnan($)"
};
String code;
- code += " " + p_output_vars[0] + " = " + String(funcs[func]).replace("$", p_input_vars[0]) + ";\n";
+ code += " " + p_output_vars[0] + " = " + String(functions[func]).replace("$", p_input_vars[0]) + ";\n";
return code;
}
void VisualShaderNodeIs::set_function(Function p_func) {
+ ERR_FAIL_INDEX(int(p_func), int(FUNC_MAX));
+ if (func == p_func) {
+ return;
+ }
func = p_func;
emit_changed();
}
@@ -5485,6 +5685,7 @@ void VisualShaderNodeIs::_bind_methods() {
BIND_ENUM_CONSTANT(FUNC_IS_INF);
BIND_ENUM_CONSTANT(FUNC_IS_NAN);
+ BIND_ENUM_CONSTANT(FUNC_MAX);
}
VisualShaderNodeIs::VisualShaderNodeIs() {
@@ -5498,14 +5699,14 @@ String VisualShaderNodeCompare::get_caption() const {
}
int VisualShaderNodeCompare::get_input_port_count() const {
- if (ctype == CTYPE_SCALAR && (func == FUNC_EQUAL || func == FUNC_NOT_EQUAL)) {
+ if (comparison_type == CTYPE_SCALAR && (func == FUNC_EQUAL || func == FUNC_NOT_EQUAL)) {
return 3;
}
return 2;
}
VisualShaderNodeCompare::PortType VisualShaderNodeCompare::get_input_port_type(int p_port) const {
- switch (ctype) {
+ switch (comparison_type) {
case CTYPE_SCALAR:
return PORT_TYPE_SCALAR;
case CTYPE_SCALAR_INT:
@@ -5548,17 +5749,16 @@ String VisualShaderNodeCompare::get_output_port_name(int p_port) const {
}
String VisualShaderNodeCompare::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const {
- if (ctype == CTYPE_BOOLEAN || ctype == CTYPE_TRANSFORM) {
+ if (comparison_type == CTYPE_BOOLEAN || comparison_type == CTYPE_TRANSFORM) {
if (func > FUNC_NOT_EQUAL) {
return TTR("Invalid comparison function for that type.");
}
}
-
return "";
}
String VisualShaderNodeCompare::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- static const char *ops[FUNC_LESS_THAN_EQUAL + 1] = {
+ static const char *operators[FUNC_MAX] = {
"==",
"!=",
">",
@@ -5567,7 +5767,7 @@ String VisualShaderNodeCompare::generate_code(Shader::Mode p_mode, VisualShader:
"<=",
};
- static const char *funcs[FUNC_LESS_THAN_EQUAL + 1] = {
+ static const char *functions[FUNC_MAX] = {
"equal($)",
"notEqual($)",
"greaterThan($)",
@@ -5576,31 +5776,31 @@ String VisualShaderNodeCompare::generate_code(Shader::Mode p_mode, VisualShader:
"lessThanEqual($)",
};
- static const char *conds[COND_ANY + 1] = {
+ static const char *conditions[COND_MAX] = {
"all($)",
"any($)",
};
String code;
- switch (ctype) {
+ switch (comparison_type) {
case CTYPE_SCALAR:
if (func == FUNC_EQUAL) {
code += " " + p_output_vars[0] + " = (abs(" + p_input_vars[0] + " - " + p_input_vars[1] + ") < " + p_input_vars[2] + ");";
} else if (func == FUNC_NOT_EQUAL) {
code += " " + p_output_vars[0] + " = !(abs(" + p_input_vars[0] + " - " + p_input_vars[1] + ") < " + p_input_vars[2] + ");";
} else {
- code += " " + p_output_vars[0] + " = " + (p_input_vars[0] + " $ " + p_input_vars[1]).replace("$", ops[func]) + ";\n";
+ code += " " + p_output_vars[0] + " = " + (p_input_vars[0] + " $ " + p_input_vars[1]).replace("$", operators[func]) + ";\n";
}
break;
case CTYPE_SCALAR_INT:
- code += " " + p_output_vars[0] + " = " + (p_input_vars[0] + " $ " + p_input_vars[1]).replace("$", ops[func]) + ";\n";
+ code += " " + p_output_vars[0] + " = " + (p_input_vars[0] + " $ " + p_input_vars[1]).replace("$", operators[func]) + ";\n";
break;
case CTYPE_VECTOR:
code += " {\n";
- code += " bvec3 _bv = " + String(funcs[func]).replace("$", p_input_vars[0] + ", " + p_input_vars[1]) + ";\n";
- code += " " + p_output_vars[0] + " = " + String(conds[condition]).replace("$", "_bv") + ";\n";
+ code += " bvec3 _bv = " + String(functions[func]).replace("$", p_input_vars[0] + ", " + p_input_vars[1]) + ";\n";
+ code += " " + p_output_vars[0] + " = " + String(conditions[condition]).replace("$", "_bv") + ";\n";
code += " }\n";
break;
@@ -5608,14 +5808,14 @@ String VisualShaderNodeCompare::generate_code(Shader::Mode p_mode, VisualShader:
if (func > FUNC_NOT_EQUAL) {
return " " + p_output_vars[0] + " = false;\n";
}
- code += " " + p_output_vars[0] + " = " + (p_input_vars[0] + " $ " + p_input_vars[1]).replace("$", ops[func]) + ";\n";
+ code += " " + p_output_vars[0] + " = " + (p_input_vars[0] + " $ " + p_input_vars[1]).replace("$", operators[func]) + ";\n";
break;
case CTYPE_TRANSFORM:
if (func > FUNC_NOT_EQUAL) {
return " " + p_output_vars[0] + " = false;\n";
}
- code += " " + p_output_vars[0] + " = " + (p_input_vars[0] + " $ " + p_input_vars[1]).replace("$", ops[func]) + ";\n";
+ code += " " + p_output_vars[0] + " = " + (p_input_vars[0] + " $ " + p_input_vars[1]).replace("$", operators[func]) + ";\n";
break;
default:
@@ -5624,10 +5824,12 @@ String VisualShaderNodeCompare::generate_code(Shader::Mode p_mode, VisualShader:
return code;
}
-void VisualShaderNodeCompare::set_comparison_type(ComparisonType p_type) {
- ctype = p_type;
-
- switch (ctype) {
+void VisualShaderNodeCompare::set_comparison_type(ComparisonType p_comparison_type) {
+ ERR_FAIL_INDEX(int(p_comparison_type), int(CTYPE_MAX));
+ if (comparison_type == p_comparison_type) {
+ return;
+ }
+ switch (p_comparison_type) {
case CTYPE_SCALAR:
set_input_port_default_value(0, 0.0);
set_input_port_default_value(1, 0.0);
@@ -5653,15 +5855,22 @@ void VisualShaderNodeCompare::set_comparison_type(ComparisonType p_type) {
set_input_port_default_value(1, Transform3D());
simple_decl = true;
break;
+ default:
+ break;
}
+ comparison_type = p_comparison_type;
emit_changed();
}
VisualShaderNodeCompare::ComparisonType VisualShaderNodeCompare::get_comparison_type() const {
- return ctype;
+ return comparison_type;
}
void VisualShaderNodeCompare::set_function(Function p_func) {
+ ERR_FAIL_INDEX(int(p_func), int(FUNC_MAX));
+ if (func == p_func) {
+ return;
+ }
func = p_func;
emit_changed();
}
@@ -5670,8 +5879,12 @@ VisualShaderNodeCompare::Function VisualShaderNodeCompare::get_function() const
return func;
}
-void VisualShaderNodeCompare::set_condition(Condition p_cond) {
- condition = p_cond;
+void VisualShaderNodeCompare::set_condition(Condition p_condition) {
+ ERR_FAIL_INDEX(int(p_condition), int(COND_MAX));
+ if (condition == p_condition) {
+ return;
+ }
+ condition = p_condition;
emit_changed();
}
@@ -5683,7 +5896,7 @@ Vector<StringName> VisualShaderNodeCompare::get_editable_properties() const {
Vector<StringName> props;
props.push_back("type");
props.push_back("function");
- if (ctype == CTYPE_VECTOR) {
+ if (comparison_type == CTYPE_VECTOR) {
props.push_back("condition");
}
return props;
@@ -5708,6 +5921,7 @@ void VisualShaderNodeCompare::_bind_methods() {
BIND_ENUM_CONSTANT(CTYPE_VECTOR);
BIND_ENUM_CONSTANT(CTYPE_BOOLEAN);
BIND_ENUM_CONSTANT(CTYPE_TRANSFORM);
+ BIND_ENUM_CONSTANT(CTYPE_MAX);
BIND_ENUM_CONSTANT(FUNC_EQUAL);
BIND_ENUM_CONSTANT(FUNC_NOT_EQUAL);
@@ -5715,9 +5929,11 @@ void VisualShaderNodeCompare::_bind_methods() {
BIND_ENUM_CONSTANT(FUNC_GREATER_THAN_EQUAL);
BIND_ENUM_CONSTANT(FUNC_LESS_THAN);
BIND_ENUM_CONSTANT(FUNC_LESS_THAN_EQUAL);
+ BIND_ENUM_CONSTANT(FUNC_MAX);
BIND_ENUM_CONSTANT(COND_ALL);
BIND_ENUM_CONSTANT(COND_ANY);
+ BIND_ENUM_CONSTANT(COND_MAX);
}
VisualShaderNodeCompare::VisualShaderNodeCompare() {
@@ -5775,7 +5991,7 @@ String VisualShaderNodeMultiplyAdd::generate_code(Shader::Mode p_mode, VisualSha
}
void VisualShaderNodeMultiplyAdd::set_op_type(OpType p_op_type) {
- ERR_FAIL_INDEX((int)p_op_type, OP_TYPE_MAX);
+ ERR_FAIL_INDEX((int)p_op_type, int(OP_TYPE_MAX));
if (op_type == p_op_type) {
return;
}
@@ -5898,7 +6114,10 @@ bool VisualShaderNodeBillboard::is_show_prop_names() const {
}
void VisualShaderNodeBillboard::set_billboard_type(BillboardType p_billboard_type) {
- ERR_FAIL_INDEX((int)p_billboard_type, BILLBOARD_TYPE_MAX);
+ ERR_FAIL_INDEX(int(p_billboard_type), int(BILLBOARD_TYPE_MAX));
+ if (billboard_type == p_billboard_type) {
+ return;
+ }
billboard_type = p_billboard_type;
simple_decl = bool(billboard_type == BILLBOARD_TYPE_DISABLED);
set_disabled(simple_decl);
diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h
index 99617b46a3..2c952300fe 100644
--- a/scene/resources/visual_shader_nodes.h
+++ b/scene/resources/visual_shader_nodes.h
@@ -76,7 +76,7 @@ public:
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
- void set_constant(float p_value);
+ void set_constant(float p_constant);
float get_constant() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -106,7 +106,7 @@ public:
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
- void set_constant(int p_value);
+ void set_constant(int p_constant);
int get_constant() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -136,7 +136,7 @@ public:
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
- void set_constant(bool p_value);
+ void set_constant(bool p_constant);
bool get_constant() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -167,7 +167,7 @@ public:
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
- void set_constant(Color p_value);
+ void set_constant(const Color &p_constant);
Color get_constant() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -197,7 +197,7 @@ public:
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
- void set_constant(Vector3 p_value);
+ void set_constant(const Vector3 &p_constant);
Vector3 get_constant() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -227,7 +227,7 @@ public:
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
- void set_constant(Transform3D p_value);
+ void set_constant(const Transform3D &p_constant);
Transform3D get_constant() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -251,12 +251,14 @@ public:
SOURCE_2D_NORMAL,
SOURCE_DEPTH,
SOURCE_PORT,
+ SOURCE_MAX,
};
enum TextureType {
TYPE_DATA,
TYPE_COLOR,
TYPE_NORMAL_MAP,
+ TYPE_MAX,
};
private:
@@ -287,10 +289,10 @@ public:
void set_source(Source p_source);
Source get_source() const;
- void set_texture(Ref<Texture2D> p_value);
+ void set_texture(Ref<Texture2D> p_texture);
Ref<Texture2D> get_texture() const;
- void set_texture_type(TextureType p_type);
+ void set_texture_type(TextureType p_texture_type);
TextureType get_texture_type() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -327,7 +329,7 @@ public:
virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
- void set_texture(Ref<CurveTexture> p_value);
+ void set_texture(Ref<CurveTexture> p_texture);
Ref<CurveTexture> get_texture() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -360,7 +362,7 @@ public:
virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
- void set_texture(Ref<CurveXYZTexture> p_value);
+ void set_texture(Ref<CurveXYZTexture> p_texture);
Ref<CurveXYZTexture> get_texture() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -378,6 +380,7 @@ public:
enum Source {
SOURCE_TEXTURE,
SOURCE_PORT,
+ SOURCE_MAX,
};
protected:
@@ -410,7 +413,7 @@ VARIANT_ENUM_CAST(VisualShaderNodeSample3D::Source)
class VisualShaderNodeTexture2DArray : public VisualShaderNodeSample3D {
GDCLASS(VisualShaderNodeTexture2DArray, VisualShaderNodeSample3D);
- Ref<Texture2DArray> texture;
+ Ref<Texture2DArray> texture_array;
protected:
static void _bind_methods();
@@ -423,7 +426,7 @@ public:
virtual Vector<VisualShader::DefaultTextureParam> get_default_texture_parameters(VisualShader::Type p_type, int p_id) const override;
virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
- void set_texture_array(Ref<Texture2DArray> p_value);
+ void set_texture_array(Ref<Texture2DArray> p_texture_array);
Ref<Texture2DArray> get_texture_array() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -446,7 +449,7 @@ public:
virtual Vector<VisualShader::DefaultTextureParam> get_default_texture_parameters(VisualShader::Type p_type, int p_id) const override;
virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
- void set_texture(Ref<Texture3D> p_value);
+ void set_texture(Ref<Texture3D> p_texture);
Ref<Texture3D> get_texture() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -461,13 +464,15 @@ class VisualShaderNodeCubemap : public VisualShaderNode {
public:
enum Source {
SOURCE_TEXTURE,
- SOURCE_PORT
+ SOURCE_PORT,
+ SOURCE_MAX,
};
enum TextureType {
TYPE_DATA,
TYPE_COLOR,
- TYPE_NORMAL_MAP
+ TYPE_NORMAL_MAP,
+ TYPE_MAX,
};
private:
@@ -497,10 +502,10 @@ public:
void set_source(Source p_source);
Source get_source() const;
- void set_cube_map(Ref<Cubemap> p_value);
+ void set_cube_map(Ref<Cubemap> p_cube_map);
Ref<Cubemap> get_cube_map() const;
- void set_texture_type(TextureType p_type);
+ void set_texture_type(TextureType p_texture_type);
TextureType get_texture_type() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -530,7 +535,8 @@ public:
OP_MAX,
OP_MIN,
OP_ATAN2,
- OP_STEP
+ OP_STEP,
+ OP_ENUM_SIZE,
};
protected:
@@ -573,6 +579,7 @@ public:
OP_MOD,
OP_MAX,
OP_MIN,
+ OP_ENUM_SIZE,
};
protected:
@@ -619,7 +626,8 @@ public:
OP_CROSS,
OP_ATAN2,
OP_REFLECT,
- OP_STEP
+ OP_STEP,
+ OP_ENUM_SIZE,
};
protected:
@@ -665,7 +673,8 @@ public:
OP_DODGE,
OP_BURN,
OP_SOFT_LIGHT,
- OP_HARD_LIGHT
+ OP_HARD_LIGHT,
+ OP_MAX,
};
protected:
@@ -714,7 +723,7 @@ public:
OP_B_MINUS_A,
OP_A_DIV_B,
OP_B_DIV_A,
- OP_LIMITER,
+ OP_MAX,
};
protected:
@@ -758,6 +767,7 @@ public:
OP_BxA,
OP_3x3_AxB,
OP_3x3_BxA,
+ OP_MAX,
};
protected:
@@ -828,7 +838,8 @@ public:
FUNC_RECIPROCAL,
FUNC_ROUNDEVEN,
FUNC_TRUNC,
- FUNC_ONEMINUS
+ FUNC_ONEMINUS,
+ FUNC_MAX,
};
protected:
@@ -871,6 +882,7 @@ public:
FUNC_ABS,
FUNC_NEGATE,
FUNC_SIGN,
+ FUNC_MAX,
};
protected:
@@ -944,7 +956,8 @@ public:
FUNC_TAN,
FUNC_TANH,
FUNC_TRUNC,
- FUNC_ONEMINUS
+ FUNC_ONEMINUS,
+ FUNC_MAX,
};
protected:
@@ -985,7 +998,8 @@ class VisualShaderNodeColorFunc : public VisualShaderNode {
public:
enum Function {
FUNC_GRAYSCALE,
- FUNC_SEPIA
+ FUNC_SEPIA,
+ FUNC_MAX,
};
protected:
@@ -1026,7 +1040,8 @@ class VisualShaderNodeTransformFunc : public VisualShaderNode {
public:
enum Function {
FUNC_INVERSE,
- FUNC_TRANSPOSE
+ FUNC_TRANSPOSE,
+ FUNC_MAX,
};
protected:
@@ -1092,7 +1107,7 @@ public:
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
- void set_function(Function p_op);
+ void set_function(Function p_func);
Function get_function() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -1201,7 +1216,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
- void set_op_type(OpType p_type);
+ void set_op_type(OpType p_op_type);
OpType get_op_type() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -1224,7 +1239,8 @@ public:
enum Function {
FUNC_SUM,
FUNC_X,
- FUNC_Y
+ FUNC_Y,
+ FUNC_MAX,
};
protected:
@@ -1264,7 +1280,8 @@ public:
enum Function {
FUNC_SUM,
FUNC_X,
- FUNC_Y
+ FUNC_Y,
+ FUNC_MAX,
};
protected:
@@ -1371,7 +1388,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
- void set_op_type(OpType p_type);
+ void set_op_type(OpType p_op_type);
OpType get_op_type() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -1413,7 +1430,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
- void set_op_type(OpType p_type);
+ void set_op_type(OpType p_op_type);
OpType get_op_type() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -1501,7 +1518,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
- void set_op_type(OpType p_type);
+ void set_op_type(OpType p_op_type);
OpType get_op_type() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -1613,6 +1630,7 @@ public:
HINT_NONE,
HINT_RANGE,
HINT_RANGE_STEP,
+ HINT_MAX,
};
private:
@@ -1679,6 +1697,7 @@ public:
HINT_NONE,
HINT_RANGE,
HINT_RANGE_STEP,
+ HINT_MAX,
};
private:
@@ -1919,11 +1938,13 @@ public:
TYPE_COLOR,
TYPE_NORMAL_MAP,
TYPE_ANISO,
+ TYPE_MAX,
};
enum ColorDefault {
COLOR_DEFAULT_WHITE,
- COLOR_DEFAULT_BLACK
+ COLOR_DEFAULT_BLACK,
+ COLOR_DEFAULT_MAX,
};
protected:
@@ -2113,7 +2134,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
- void set_op_type(OpType p_type);
+ void set_op_type(OpType p_op_type);
OpType get_op_type() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -2161,6 +2182,7 @@ public:
enum Function {
FUNC_IS_INF,
FUNC_IS_NAN,
+ FUNC_MAX,
};
protected:
@@ -2206,6 +2228,7 @@ public:
CTYPE_VECTOR,
CTYPE_BOOLEAN,
CTYPE_TRANSFORM,
+ CTYPE_MAX,
};
enum Function {
@@ -2215,15 +2238,17 @@ public:
FUNC_GREATER_THAN_EQUAL,
FUNC_LESS_THAN,
FUNC_LESS_THAN_EQUAL,
+ FUNC_MAX,
};
enum Condition {
COND_ALL,
COND_ANY,
+ COND_MAX,
};
protected:
- ComparisonType ctype = CTYPE_SCALAR;
+ ComparisonType comparison_type = CTYPE_SCALAR;
Function func = FUNC_EQUAL;
Condition condition = COND_ALL;
@@ -2291,7 +2316,7 @@ public:
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
- void set_op_type(OpType p_type);
+ void set_op_type(OpType p_op_type);
OpType get_op_type() const;
virtual Vector<StringName> get_editable_properties() const override;
diff --git a/scene/resources/visual_shader_particle_nodes.cpp b/scene/resources/visual_shader_particle_nodes.cpp
index 2250cf8d95..5fe801e037 100644
--- a/scene/resources/visual_shader_particle_nodes.cpp
+++ b/scene/resources/visual_shader_particle_nodes.cpp
@@ -402,15 +402,16 @@ String VisualShaderNodeParticleRandomness::generate_code(Shader::Mode p_mode, Vi
}
void VisualShaderNodeParticleRandomness::set_op_type(OpType p_op_type) {
- ERR_FAIL_INDEX((int)p_op_type, OP_TYPE_MAX);
- if (p_op_type != op_type) {
- if (p_op_type == OP_TYPE_SCALAR) {
- set_input_port_default_value(0, 0.0);
- set_input_port_default_value(1, 1.0);
- } else {
- set_input_port_default_value(0, Vector3(-1.0, -1.0, -1.0));
- set_input_port_default_value(1, Vector3(1.0, 1.0, 1.0));
- }
+ ERR_FAIL_INDEX(int(p_op_type), int(OP_TYPE_MAX));
+ if (op_type == p_op_type) {
+ return;
+ }
+ if (p_op_type == OP_TYPE_SCALAR) {
+ set_input_port_default_value(0, 0.0);
+ set_input_port_default_value(1, 1.0);
+ } else {
+ set_input_port_default_value(0, Vector3(-1.0, -1.0, -1.0));
+ set_input_port_default_value(1, Vector3(1.0, 1.0, 1.0));
}
op_type = p_op_type;
emit_changed();
@@ -500,8 +501,6 @@ String VisualShaderNodeParticleAccelerator::generate_code(Shader::Mode p_mode, V
code += " __vec3_buff1 = cross(__ndiff, normalize(" + (p_input_vars[2].is_empty() ? "vec3" + (String)get_input_port_default_value(2) : p_input_vars[2]) + "));\n";
code += " " + p_output_vars[0] + " = length(__vec3_buff1) > 0.0 ? normalize(__vec3_buff1) * (" + (p_input_vars[0].is_empty() ? "vec3" + (String)get_input_port_default_value(0) : p_input_vars[0]) + " * mix(1.0, __rand_from_seed(__seed), " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ")) : vec3(0.0);\n";
break;
- case MODE_MAX:
- break;
default:
break;
}
@@ -510,6 +509,10 @@ String VisualShaderNodeParticleAccelerator::generate_code(Shader::Mode p_mode, V
}
void VisualShaderNodeParticleAccelerator::set_mode(Mode p_mode) {
+ ERR_FAIL_INDEX(int(p_mode), int(MODE_MAX));
+ if (mode == p_mode) {
+ return;
+ }
mode = p_mode;
emit_changed();
}
diff --git a/scene/resources/visual_shader_particle_nodes.h b/scene/resources/visual_shader_particle_nodes.h
index ecd187a885..f5435c3511 100644
--- a/scene/resources/visual_shader_particle_nodes.h
+++ b/scene/resources/visual_shader_particle_nodes.h
@@ -181,8 +181,8 @@ VARIANT_ENUM_CAST(VisualShaderNodeParticleRandomness::OpType)
// Process nodes
-class VisualShaderNodeParticleAccelerator : public VisualShaderNodeOutput {
- GDCLASS(VisualShaderNodeParticleAccelerator, VisualShaderNodeOutput);
+class VisualShaderNodeParticleAccelerator : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeParticleAccelerator, VisualShaderNode);
public:
enum Mode {
diff --git a/scene/resources/line_shape_2d.cpp b/scene/resources/world_margin_shape_2d.cpp
index d206f12287..3b43681528 100644
--- a/scene/resources/line_shape_2d.cpp
+++ b/scene/resources/world_margin_shape_2d.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* line_shape_2d.cpp */
+/* world_margin_shape_2d.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,13 +28,13 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "line_shape_2d.h"
+#include "world_margin_shape_2d.h"
#include "core/math/geometry_2d.h"
#include "servers/physics_server_2d.h"
#include "servers/rendering_server.h"
-bool LineShape2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const {
+bool WorldMarginShape2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const {
Vector2 point = get_distance() * get_normal();
Vector2 l[2][2] = { { point - get_normal().orthogonal() * 100, point + get_normal().orthogonal() * 100 }, { point, point + get_normal() * 30 } };
@@ -48,7 +48,7 @@ bool LineShape2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tol
return false;
}
-void LineShape2D::_update_shape() {
+void WorldMarginShape2D::_update_shape() {
Array arr;
arr.push_back(normal);
arr.push_back(distance);
@@ -56,25 +56,25 @@ void LineShape2D::_update_shape() {
emit_changed();
}
-void LineShape2D::set_normal(const Vector2 &p_normal) {
+void WorldMarginShape2D::set_normal(const Vector2 &p_normal) {
normal = p_normal;
_update_shape();
}
-void LineShape2D::set_distance(real_t p_distance) {
+void WorldMarginShape2D::set_distance(real_t p_distance) {
distance = p_distance;
_update_shape();
}
-Vector2 LineShape2D::get_normal() const {
+Vector2 WorldMarginShape2D::get_normal() const {
return normal;
}
-real_t LineShape2D::get_distance() const {
+real_t WorldMarginShape2D::get_distance() const {
return distance;
}
-void LineShape2D::draw(const RID &p_to_rid, const Color &p_color) {
+void WorldMarginShape2D::draw(const RID &p_to_rid, const Color &p_color) {
Vector2 point = get_distance() * get_normal();
Vector2 l1[2] = { point - get_normal().orthogonal() * 100, point + get_normal().orthogonal() * 100 };
@@ -83,7 +83,7 @@ void LineShape2D::draw(const RID &p_to_rid, const Color &p_color) {
RS::get_singleton()->canvas_item_add_line(p_to_rid, l2[0], l2[1], p_color, 3);
}
-Rect2 LineShape2D::get_rect() const {
+Rect2 WorldMarginShape2D::get_rect() const {
Vector2 point = get_distance() * get_normal();
Vector2 l1[2] = { point - get_normal().orthogonal() * 100, point + get_normal().orthogonal() * 100 };
@@ -96,22 +96,22 @@ Rect2 LineShape2D::get_rect() const {
return rect;
}
-real_t LineShape2D::get_enclosing_radius() const {
+real_t WorldMarginShape2D::get_enclosing_radius() const {
return distance;
}
-void LineShape2D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_normal", "normal"), &LineShape2D::set_normal);
- ClassDB::bind_method(D_METHOD("get_normal"), &LineShape2D::get_normal);
+void WorldMarginShape2D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_normal", "normal"), &WorldMarginShape2D::set_normal);
+ ClassDB::bind_method(D_METHOD("get_normal"), &WorldMarginShape2D::get_normal);
- ClassDB::bind_method(D_METHOD("set_distance", "distance"), &LineShape2D::set_distance);
- ClassDB::bind_method(D_METHOD("get_distance"), &LineShape2D::get_distance);
+ ClassDB::bind_method(D_METHOD("set_distance", "distance"), &WorldMarginShape2D::set_distance);
+ ClassDB::bind_method(D_METHOD("get_distance"), &WorldMarginShape2D::get_distance);
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "normal"), "set_normal", "get_normal");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance"), "set_distance", "get_distance");
}
-LineShape2D::LineShape2D() :
- Shape2D(PhysicsServer2D::get_singleton()->line_shape_create()) {
+WorldMarginShape2D::WorldMarginShape2D() :
+ Shape2D(PhysicsServer2D::get_singleton()->world_margin_shape_create()) {
_update_shape();
}
diff --git a/scene/resources/line_shape_2d.h b/scene/resources/world_margin_shape_2d.h
index 210a1aa9e6..3c1d593ffe 100644
--- a/scene/resources/line_shape_2d.h
+++ b/scene/resources/world_margin_shape_2d.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* line_shape_2d.h */
+/* world_margin_shape_2d.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,15 +28,15 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef LINE_SHAPE_2D_H
-#define LINE_SHAPE_2D_H
+#ifndef WORLD_MARGIN_SHAPE_2D_H
+#define WORLD_MARGIN_SHAPE_2D_H
#include "scene/resources/shape_2d.h"
-class LineShape2D : public Shape2D {
- GDCLASS(LineShape2D, Shape2D);
+class WorldMarginShape2D : public Shape2D {
+ GDCLASS(WorldMarginShape2D, Shape2D);
- // LineShape2D is often used for one-way platforms, where the normal pointing up makes sense.
+ // WorldMarginShape2D is often used for one-way platforms, where the normal pointing up makes sense.
Vector2 normal = Vector2(0, -1);
real_t distance = 0.0;
@@ -58,7 +58,7 @@ public:
virtual Rect2 get_rect() const override;
virtual real_t get_enclosing_radius() const override;
- LineShape2D();
+ WorldMarginShape2D();
};
-#endif // LINE_SHAPE_2D_H
+#endif // WORLD_MARGIN_SHAPE_2D_H
diff --git a/scene/scene_string_names.cpp b/scene/scene_string_names.cpp
index b8173c9623..5d89d295c2 100644
--- a/scene/scene_string_names.cpp
+++ b/scene/scene_string_names.cpp
@@ -63,6 +63,7 @@ SceneStringNames::SceneStringNames() {
animation_started = StaticCString::create("animation_started");
pose_updated = StaticCString::create("pose_updated");
+ bone_pose_changed = StaticCString::create("bone_pose_changed");
mouse_entered = StaticCString::create("mouse_entered");
mouse_exited = StaticCString::create("mouse_exited");
diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h
index eddb0c33eb..01f427ecd1 100644
--- a/scene/scene_string_names.h
+++ b/scene/scene_string_names.h
@@ -98,6 +98,7 @@ public:
StringName animation_started;
StringName pose_updated;
+ StringName bone_pose_changed;
StringName body_shape_entered;
StringName body_entered;
diff --git a/servers/physics_2d/collision_solver_2d_sat.cpp b/servers/physics_2d/collision_solver_2d_sat.cpp
index 45b9e6414d..30a99d3d74 100644
--- a/servers/physics_2d/collision_solver_2d_sat.cpp
+++ b/servers/physics_2d/collision_solver_2d_sat.cpp
@@ -1114,12 +1114,12 @@ static void _collision_convex_polygon_convex_polygon(const Shape2DSW *p_a, const
bool sat_2d_calculate_penetration(const Shape2DSW *p_shape_A, const Transform2D &p_transform_A, const Vector2 &p_motion_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, const Vector2 &p_motion_B, CollisionSolver2DSW::CallbackResult p_result_callback, void *p_userdata, bool p_swap, Vector2 *sep_axis, real_t p_margin_A, real_t p_margin_B) {
PhysicsServer2D::ShapeType type_A = p_shape_A->get_type();
- ERR_FAIL_COND_V(type_A == PhysicsServer2D::SHAPE_LINE, false);
+ ERR_FAIL_COND_V(type_A == PhysicsServer2D::SHAPE_WORLD_MARGIN, false);
ERR_FAIL_COND_V(p_shape_A->is_concave(), false);
PhysicsServer2D::ShapeType type_B = p_shape_B->get_type();
- ERR_FAIL_COND_V(type_B == PhysicsServer2D::SHAPE_LINE, false);
+ ERR_FAIL_COND_V(type_B == PhysicsServer2D::SHAPE_WORLD_MARGIN, false);
ERR_FAIL_COND_V(p_shape_B->is_concave(), false);
static const CollisionFunc collision_table[5][5] = {
diff --git a/servers/physics_2d/collision_solver_2d_sw.cpp b/servers/physics_2d/collision_solver_2d_sw.cpp
index ed398a24e5..8f8a4a862c 100644
--- a/servers/physics_2d/collision_solver_2d_sw.cpp
+++ b/servers/physics_2d/collision_solver_2d_sw.cpp
@@ -34,14 +34,14 @@
#define collision_solver sat_2d_calculate_penetration
//#define collision_solver gjk_epa_calculate_penetration
-bool CollisionSolver2DSW::solve_static_line(const Shape2DSW *p_shape_A, const Transform2D &p_transform_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result) {
- const LineShape2DSW *line = static_cast<const LineShape2DSW *>(p_shape_A);
- if (p_shape_B->get_type() == PhysicsServer2D::SHAPE_LINE) {
+bool CollisionSolver2DSW::solve_static_world_margin(const Shape2DSW *p_shape_A, const Transform2D &p_transform_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result) {
+ const WorldMarginShape2DSW *world_margin = static_cast<const WorldMarginShape2DSW *>(p_shape_A);
+ if (p_shape_B->get_type() == PhysicsServer2D::SHAPE_WORLD_MARGIN) {
return false;
}
- Vector2 n = p_transform_A.basis_xform(line->get_normal()).normalized();
- Vector2 p = p_transform_A.xform(line->get_normal() * line->get_d());
+ Vector2 n = p_transform_A.basis_xform(world_margin->get_normal()).normalized();
+ Vector2 p = p_transform_A.xform(world_margin->get_normal() * world_margin->get_d());
real_t d = n.dot(p);
Vector2 supports[2];
@@ -166,15 +166,15 @@ bool CollisionSolver2DSW::solve(const Shape2DSW *p_shape_A, const Transform2D &p
swap = true;
}
- if (type_A == PhysicsServer2D::SHAPE_LINE) {
- if (type_B == PhysicsServer2D::SHAPE_LINE) {
+ if (type_A == PhysicsServer2D::SHAPE_WORLD_MARGIN) {
+ if (type_B == PhysicsServer2D::SHAPE_WORLD_MARGIN) {
return false;
}
if (swap) {
- return solve_static_line(p_shape_B, p_transform_B, p_shape_A, p_transform_A, p_result_callback, p_userdata, true);
+ return solve_static_world_margin(p_shape_B, p_transform_B, p_shape_A, p_transform_A, p_result_callback, p_userdata, true);
} else {
- return solve_static_line(p_shape_A, p_transform_A, p_shape_B, p_transform_B, p_result_callback, p_userdata, false);
+ return solve_static_world_margin(p_shape_A, p_transform_A, p_shape_B, p_transform_B, p_result_callback, p_userdata, false);
}
} else if (concave_B) {
diff --git a/servers/physics_2d/collision_solver_2d_sw.h b/servers/physics_2d/collision_solver_2d_sw.h
index 4f12ca9e88..17c0c2fe70 100644
--- a/servers/physics_2d/collision_solver_2d_sw.h
+++ b/servers/physics_2d/collision_solver_2d_sw.h
@@ -38,7 +38,7 @@ public:
typedef void (*CallbackResult)(const Vector2 &p_point_A, const Vector2 &p_point_B, void *p_userdata);
private:
- static bool solve_static_line(const Shape2DSW *p_shape_A, const Transform2D &p_transform_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result);
+ static bool solve_static_world_margin(const Shape2DSW *p_shape_A, const Transform2D &p_transform_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result);
static void concave_callback(void *p_userdata, Shape2DSW *p_convex);
static bool solve_concave(const Shape2DSW *p_shape_A, const Transform2D &p_transform_A, const Vector2 &p_motion_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, const Vector2 &p_motion_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, Vector2 *sep_axis = nullptr, real_t p_margin_A = 0, real_t p_margin_B = 0);
static bool solve_raycast(const Shape2DSW *p_shape_A, const Vector2 &p_motion_A, const Transform2D &p_transform_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, Vector2 *sep_axis = nullptr);
diff --git a/servers/physics_2d/physics_server_2d_sw.cpp b/servers/physics_2d/physics_server_2d_sw.cpp
index 4dcc4a9cfd..88c097453e 100644
--- a/servers/physics_2d/physics_server_2d_sw.cpp
+++ b/servers/physics_2d/physics_server_2d_sw.cpp
@@ -42,8 +42,8 @@
RID PhysicsServer2DSW::_shape_create(ShapeType p_shape) {
Shape2DSW *shape = nullptr;
switch (p_shape) {
- case SHAPE_LINE: {
- shape = memnew(LineShape2DSW);
+ case SHAPE_WORLD_MARGIN: {
+ shape = memnew(WorldMarginShape2DSW);
} break;
case SHAPE_SEGMENT: {
shape = memnew(SegmentShape2DSW);
@@ -75,8 +75,8 @@ RID PhysicsServer2DSW::_shape_create(ShapeType p_shape) {
return id;
}
-RID PhysicsServer2DSW::line_shape_create() {
- return _shape_create(SHAPE_LINE);
+RID PhysicsServer2DSW::world_margin_shape_create() {
+ return _shape_create(SHAPE_WORLD_MARGIN);
}
RID PhysicsServer2DSW::segment_shape_create() {
diff --git a/servers/physics_2d/physics_server_2d_sw.h b/servers/physics_2d/physics_server_2d_sw.h
index e1ad6a56ee..3610f43f93 100644
--- a/servers/physics_2d/physics_server_2d_sw.h
+++ b/servers/physics_2d/physics_server_2d_sw.h
@@ -87,7 +87,7 @@ public:
Vector2 *ptr;
};
- virtual RID line_shape_create() override;
+ virtual RID world_margin_shape_create() override;
virtual RID segment_shape_create() override;
virtual RID circle_shape_create() override;
virtual RID rectangle_shape_create() override;
diff --git a/servers/physics_2d/physics_server_2d_wrap_mt.h b/servers/physics_2d/physics_server_2d_wrap_mt.h
index 7bc1096f93..b93178919d 100644
--- a/servers/physics_2d/physics_server_2d_wrap_mt.h
+++ b/servers/physics_2d/physics_server_2d_wrap_mt.h
@@ -79,7 +79,7 @@ public:
#include "servers/server_wrap_mt_common.h"
//FUNC1RID(shape,ShapeType); todo fix
- FUNCRID(line_shape)
+ FUNCRID(world_margin_shape)
FUNCRID(segment_shape)
FUNCRID(circle_shape)
FUNCRID(rectangle_shape)
diff --git a/servers/physics_2d/shape_2d_sw.cpp b/servers/physics_2d/shape_2d_sw.cpp
index b16efbfe64..b3e4ca84c3 100644
--- a/servers/physics_2d/shape_2d_sw.cpp
+++ b/servers/physics_2d/shape_2d_sw.cpp
@@ -88,15 +88,15 @@ Shape2DSW::~Shape2DSW() {
/*********************************************************/
/*********************************************************/
-void LineShape2DSW::get_supports(const Vector2 &p_normal, Vector2 *r_supports, int &r_amount) const {
+void WorldMarginShape2DSW::get_supports(const Vector2 &p_normal, Vector2 *r_supports, int &r_amount) const {
r_amount = 0;
}
-bool LineShape2DSW::contains_point(const Vector2 &p_point) const {
+bool WorldMarginShape2DSW::contains_point(const Vector2 &p_point) const {
return normal.dot(p_point) < d;
}
-bool LineShape2DSW::intersect_segment(const Vector2 &p_begin, const Vector2 &p_end, Vector2 &r_point, Vector2 &r_normal) const {
+bool WorldMarginShape2DSW::intersect_segment(const Vector2 &p_begin, const Vector2 &p_end, Vector2 &r_point, Vector2 &r_normal) const {
Vector2 segment = p_begin - p_end;
real_t den = normal.dot(segment);
@@ -118,11 +118,11 @@ bool LineShape2DSW::intersect_segment(const Vector2 &p_begin, const Vector2 &p_e
return true;
}
-real_t LineShape2DSW::get_moment_of_inertia(real_t p_mass, const Size2 &p_scale) const {
+real_t WorldMarginShape2DSW::get_moment_of_inertia(real_t p_mass, const Size2 &p_scale) const {
return 0;
}
-void LineShape2DSW::set_data(const Variant &p_data) {
+void WorldMarginShape2DSW::set_data(const Variant &p_data) {
ERR_FAIL_COND(p_data.get_type() != Variant::ARRAY);
Array arr = p_data;
ERR_FAIL_COND(arr.size() != 2);
@@ -131,7 +131,7 @@ void LineShape2DSW::set_data(const Variant &p_data) {
configure(Rect2(Vector2(-1e4, -1e4), Vector2(1e4 * 2, 1e4 * 2)));
}
-Variant LineShape2DSW::get_data() const {
+Variant WorldMarginShape2DSW::get_data() const {
Array arr;
arr.resize(2);
arr[0] = normal;
diff --git a/servers/physics_2d/shape_2d_sw.h b/servers/physics_2d/shape_2d_sw.h
index 3e4271b156..2de0f3cb9f 100644
--- a/servers/physics_2d/shape_2d_sw.h
+++ b/servers/physics_2d/shape_2d_sw.h
@@ -36,7 +36,7 @@
/*
-SHAPE_LINE, ///< plane:"plane"
+SHAPE_WORLD_MARGIN, ///< plane:"plane"
SHAPE_SEGMENT, ///< real_t:"length"
SHAPE_CIRCLE, ///< real_t:"radius"
SHAPE_RECTANGLE, ///< vec3:"extents"
@@ -152,7 +152,7 @@ public:
r_max = MAX(maxa, maxb); \
}
-class LineShape2DSW : public Shape2DSW {
+class WorldMarginShape2DSW : public Shape2DSW {
Vector2 normal;
real_t d;
@@ -160,7 +160,7 @@ public:
_FORCE_INLINE_ Vector2 get_normal() const { return normal; }
_FORCE_INLINE_ real_t get_d() const { return d; }
- virtual PhysicsServer2D::ShapeType get_type() const { return PhysicsServer2D::SHAPE_LINE; }
+ virtual PhysicsServer2D::ShapeType get_type() const { return PhysicsServer2D::SHAPE_WORLD_MARGIN; }
virtual void project_rangev(const Vector2 &p_normal, const Transform2D &p_transform, real_t &r_min, real_t &r_max) const { project_range(p_normal, p_transform, r_min, r_max); }
virtual void get_supports(const Vector2 &p_normal, Vector2 *r_supports, int &r_amount) const;
diff --git a/servers/physics_2d/space_2d_sw.cpp b/servers/physics_2d/space_2d_sw.cpp
index 65dc198592..f04f3ab583 100644
--- a/servers/physics_2d/space_2d_sw.cpp
+++ b/servers/physics_2d/space_2d_sw.cpp
@@ -560,7 +560,7 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
if (!shapes_found) {
if (r_result) {
*r_result = PhysicsServer2D::MotionResult();
- r_result->motion = p_motion;
+ r_result->travel = p_motion;
}
return false;
}
@@ -954,9 +954,9 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
Vector2 rel_vec = r_result->collision_point - body->get_transform().get_origin();
r_result->collider_velocity = Vector2(-body->get_angular_velocity() * rel_vec.y, body->get_angular_velocity() * rel_vec.x) + body->get_linear_velocity();
- r_result->motion = safe * p_motion;
+ r_result->travel = safe * p_motion;
r_result->remainder = p_motion - safe * p_motion;
- r_result->motion += (body_transform.get_origin() - p_from.get_origin());
+ r_result->travel += (body_transform.get_origin() - p_from.get_origin());
}
collided = true;
@@ -964,9 +964,9 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
}
if (!collided && r_result) {
- r_result->motion = p_motion;
+ r_result->travel = p_motion;
r_result->remainder = Vector2();
- r_result->motion += (body_transform.get_origin() - p_from.get_origin());
+ r_result->travel += (body_transform.get_origin() - p_from.get_origin());
}
return collided;
diff --git a/servers/physics_3d/area_3d_sw.cpp b/servers/physics_3d/area_3d_sw.cpp
index a9f5c4aec3..364f63e4ad 100644
--- a/servers/physics_3d/area_3d_sw.cpp
+++ b/servers/physics_3d/area_3d_sw.cpp
@@ -30,8 +30,16 @@
#include "area_3d_sw.h"
#include "body_3d_sw.h"
+#include "soft_body_3d_sw.h"
#include "space_3d_sw.h"
+Area3DSW::BodyKey::BodyKey(SoftBody3DSW *p_body, uint32_t p_body_shape, uint32_t p_area_shape) {
+ rid = p_body->get_self();
+ instance_id = p_body->get_instance_id();
+ body_shape = p_body_shape;
+ area_shape = p_area_shape;
+}
+
Area3DSW::BodyKey::BodyKey(Body3DSW *p_body, uint32_t p_body_shape, uint32_t p_area_shape) {
rid = p_body->get_self();
instance_id = p_body->get_instance_id();
diff --git a/servers/physics_3d/area_3d_sw.h b/servers/physics_3d/area_3d_sw.h
index 12f7545c08..5959ee1e95 100644
--- a/servers/physics_3d/area_3d_sw.h
+++ b/servers/physics_3d/area_3d_sw.h
@@ -38,6 +38,7 @@
class Space3DSW;
class Body3DSW;
+class SoftBody3DSW;
class Constraint3DSW;
class Area3DSW : public CollisionObject3DSW {
@@ -80,6 +81,7 @@ class Area3DSW : public CollisionObject3DSW {
}
_FORCE_INLINE_ BodyKey() {}
+ BodyKey(SoftBody3DSW *p_body, uint32_t p_body_shape, uint32_t p_area_shape);
BodyKey(Body3DSW *p_body, uint32_t p_body_shape, uint32_t p_area_shape);
BodyKey(Area3DSW *p_body, uint32_t p_body_shape, uint32_t p_area_shape);
};
@@ -91,6 +93,7 @@ class Area3DSW : public CollisionObject3DSW {
_FORCE_INLINE_ BodyState() { state = 0; }
};
+ Map<BodyKey, BodyState> monitored_soft_bodies;
Map<BodyKey, BodyState> monitored_bodies;
Map<BodyKey, BodyState> monitored_areas;
@@ -115,6 +118,9 @@ public:
_FORCE_INLINE_ void add_body_to_query(Body3DSW *p_body, uint32_t p_body_shape, uint32_t p_area_shape);
_FORCE_INLINE_ void remove_body_from_query(Body3DSW *p_body, uint32_t p_body_shape, uint32_t p_area_shape);
+ _FORCE_INLINE_ void add_soft_body_to_query(SoftBody3DSW *p_soft_body, uint32_t p_soft_body_shape, uint32_t p_area_shape);
+ _FORCE_INLINE_ void remove_soft_body_from_query(SoftBody3DSW *p_soft_body, uint32_t p_soft_body_shape, uint32_t p_area_shape);
+
_FORCE_INLINE_ void add_area_to_query(Area3DSW *p_area, uint32_t p_area_shape, uint32_t p_self_shape);
_FORCE_INLINE_ void remove_area_from_query(Area3DSW *p_area, uint32_t p_area_shape, uint32_t p_self_shape);
@@ -166,6 +172,22 @@ public:
~Area3DSW();
};
+void Area3DSW::add_soft_body_to_query(SoftBody3DSW *p_soft_body, uint32_t p_soft_body_shape, uint32_t p_area_shape) {
+ BodyKey bk(p_soft_body, p_soft_body_shape, p_area_shape);
+ monitored_soft_bodies[bk].inc();
+ if (!monitor_query_list.in_list()) {
+ _queue_monitor_update();
+ }
+}
+
+void Area3DSW::remove_soft_body_from_query(SoftBody3DSW *p_soft_body, uint32_t p_soft_body_shape, uint32_t p_area_shape) {
+ BodyKey bk(p_soft_body, p_soft_body_shape, p_area_shape);
+ monitored_soft_bodies[bk].dec();
+ if (!monitor_query_list.in_list()) {
+ _queue_monitor_update();
+ }
+}
+
void Area3DSW::add_body_to_query(Body3DSW *p_body, uint32_t p_body_shape, uint32_t p_area_shape) {
BodyKey bk(p_body, p_body_shape, p_area_shape);
monitored_bodies[bk].inc();
@@ -198,4 +220,16 @@ void Area3DSW::remove_area_from_query(Area3DSW *p_area, uint32_t p_area_shape, u
}
}
+struct AreaCMP {
+ Area3DSW *area;
+ int refCount;
+ _FORCE_INLINE_ bool operator==(const AreaCMP &p_cmp) const { return area->get_self() == p_cmp.area->get_self(); }
+ _FORCE_INLINE_ bool operator<(const AreaCMP &p_cmp) const { return area->get_priority() < p_cmp.area->get_priority(); }
+ _FORCE_INLINE_ AreaCMP() {}
+ _FORCE_INLINE_ AreaCMP(Area3DSW *p_area) {
+ area = p_area;
+ refCount = 1;
+ }
+};
+
#endif // AREA__SW_H
diff --git a/servers/physics_3d/area_pair_3d_sw.cpp b/servers/physics_3d/area_pair_3d_sw.cpp
index f43c2c965e..e740565da6 100644
--- a/servers/physics_3d/area_pair_3d_sw.cpp
+++ b/servers/physics_3d/area_pair_3d_sw.cpp
@@ -181,3 +181,85 @@ Area2Pair3DSW::~Area2Pair3DSW() {
area_a->remove_constraint(this);
area_b->remove_constraint(this);
}
+
+////////////////////////////////////////////////////
+
+bool AreaSoftBodyPair3DSW::setup(real_t p_step) {
+ bool result = false;
+ if (
+ area->interacts_with(soft_body) &&
+ CollisionSolver3DSW::solve_static(
+ soft_body->get_shape(soft_body_shape),
+ soft_body->get_transform() * soft_body->get_shape_transform(soft_body_shape),
+ area->get_shape(area_shape),
+ area->get_transform() * area->get_shape_transform(area_shape),
+ nullptr,
+ this)) {
+ result = true;
+ }
+
+ process_collision = false;
+ if (result != colliding) {
+ if (area->get_space_override_mode() != PhysicsServer3D::AREA_SPACE_OVERRIDE_DISABLED) {
+ process_collision = true;
+ } else if (area->has_monitor_callback()) {
+ process_collision = true;
+ }
+
+ colliding = result;
+ }
+
+ return process_collision;
+}
+
+bool AreaSoftBodyPair3DSW::pre_solve(real_t p_step) {
+ if (!process_collision) {
+ return false;
+ }
+
+ if (colliding) {
+ if (area->get_space_override_mode() != PhysicsServer3D::AREA_SPACE_OVERRIDE_DISABLED) {
+ soft_body->add_area(area);
+ }
+
+ if (area->has_monitor_callback()) {
+ area->add_soft_body_to_query(soft_body, soft_body_shape, area_shape);
+ }
+ } else {
+ if (area->get_space_override_mode() != PhysicsServer3D::AREA_SPACE_OVERRIDE_DISABLED) {
+ soft_body->remove_area(area);
+ }
+
+ if (area->has_monitor_callback()) {
+ area->remove_soft_body_from_query(soft_body, soft_body_shape, area_shape);
+ }
+ }
+
+ return false; // Never do any post solving.
+}
+
+void AreaSoftBodyPair3DSW::solve(real_t p_step) {
+ // Nothing to do.
+}
+
+AreaSoftBodyPair3DSW::AreaSoftBodyPair3DSW(SoftBody3DSW *p_soft_body, int p_soft_body_shape, Area3DSW *p_area, int p_area_shape) {
+ soft_body = p_soft_body;
+ area = p_area;
+ soft_body_shape = p_soft_body_shape;
+ area_shape = p_area_shape;
+ soft_body->add_constraint(this);
+ area->add_constraint(this);
+}
+
+AreaSoftBodyPair3DSW::~AreaSoftBodyPair3DSW() {
+ if (colliding) {
+ if (area->get_space_override_mode() != PhysicsServer3D::AREA_SPACE_OVERRIDE_DISABLED) {
+ soft_body->remove_area(area);
+ }
+ if (area->has_monitor_callback()) {
+ area->remove_soft_body_from_query(soft_body, soft_body_shape, area_shape);
+ }
+ }
+ soft_body->remove_constraint(this);
+ area->remove_constraint(this);
+}
diff --git a/servers/physics_3d/area_pair_3d_sw.h b/servers/physics_3d/area_pair_3d_sw.h
index 596d893082..8cc9e9ad63 100644
--- a/servers/physics_3d/area_pair_3d_sw.h
+++ b/servers/physics_3d/area_pair_3d_sw.h
@@ -34,6 +34,7 @@
#include "area_3d_sw.h"
#include "body_3d_sw.h"
#include "constraint_3d_sw.h"
+#include "soft_body_3d_sw.h"
class AreaPair3DSW : public Constraint3DSW {
Body3DSW *body;
@@ -69,4 +70,21 @@ public:
~Area2Pair3DSW();
};
+class AreaSoftBodyPair3DSW : public Constraint3DSW {
+ SoftBody3DSW *soft_body;
+ Area3DSW *area;
+ int soft_body_shape;
+ int area_shape;
+ bool colliding = false;
+ bool process_collision = false;
+
+public:
+ virtual bool setup(real_t p_step) override;
+ virtual bool pre_solve(real_t p_step) override;
+ virtual void solve(real_t p_step) override;
+
+ AreaSoftBodyPair3DSW(SoftBody3DSW *p_sof_body, int p_soft_body_shape, Area3DSW *p_area, int p_area_shape);
+ ~AreaSoftBodyPair3DSW();
+};
+
#endif // AREA_PAIR__SW_H
diff --git a/servers/physics_3d/body_3d_sw.cpp b/servers/physics_3d/body_3d_sw.cpp
index ea6064cb4c..0c4079332d 100644
--- a/servers/physics_3d/body_3d_sw.cpp
+++ b/servers/physics_3d/body_3d_sw.cpp
@@ -578,7 +578,7 @@ void Body3DSW::integrate_velocities(real_t p_step) {
real_t ang_vel = total_angular_velocity.length();
Transform3D transform = get_transform();
- if (ang_vel != 0.0) {
+ if (!Math::is_zero_approx(ang_vel)) {
Vector3 ang_vel_axis = total_angular_velocity / ang_vel;
Basis rot(ang_vel_axis, ang_vel * p_step);
Basis identity3(1, 0, 0, 0, 1, 0, 0, 0, 1);
diff --git a/servers/physics_3d/body_3d_sw.h b/servers/physics_3d/body_3d_sw.h
index bdc615ab6c..efb114a325 100644
--- a/servers/physics_3d/body_3d_sw.h
+++ b/servers/physics_3d/body_3d_sw.h
@@ -96,18 +96,6 @@ class Body3DSW : public CollisionObject3DSW {
Map<Constraint3DSW *, int> constraint_map;
- struct AreaCMP {
- Area3DSW *area;
- int refCount;
- _FORCE_INLINE_ bool operator==(const AreaCMP &p_cmp) const { return area->get_self() == p_cmp.area->get_self(); }
- _FORCE_INLINE_ bool operator<(const AreaCMP &p_cmp) const { return area->get_priority() < p_cmp.area->get_priority(); }
- _FORCE_INLINE_ AreaCMP() {}
- _FORCE_INLINE_ AreaCMP(Area3DSW *p_area) {
- area = p_area;
- refCount = 1;
- }
- };
-
Vector<AreaCMP> areas;
struct Contact {
diff --git a/servers/physics_3d/collision_solver_3d_sw.cpp b/servers/physics_3d/collision_solver_3d_sw.cpp
index 161e7e101b..dcecac1c73 100644
--- a/servers/physics_3d/collision_solver_3d_sw.cpp
+++ b/servers/physics_3d/collision_solver_3d_sw.cpp
@@ -102,6 +102,10 @@ void CollisionSolver3DSW::soft_body_contact_callback(const Vector3 &p_point_A, i
++cinfo.contact_count;
+ if (!cinfo.result_callback) {
+ return;
+ }
+
if (cinfo.swap_result) {
cinfo.result_callback(p_point_B, cinfo.node_index, p_point_A, p_index_A, cinfo.userdata);
} else {
diff --git a/servers/physics_3d/soft_body_3d_sw.cpp b/servers/physics_3d/soft_body_3d_sw.cpp
index 724125bea8..73b81444e1 100644
--- a/servers/physics_3d/soft_body_3d_sw.cpp
+++ b/servers/physics_3d/soft_body_3d_sw.cpp
@@ -928,6 +928,19 @@ void SoftBody3DSW::apply_forces() {
}
}
+void SoftBody3DSW::_compute_area_gravity(const Area3DSW *p_area) {
+ if (p_area->is_gravity_point()) {
+ if (p_area->get_gravity_distance_scale() > 0) {
+ Vector3 v = p_area->get_transform().xform(p_area->get_gravity_vector()) - get_transform().get_origin();
+ gravity += v.normalized() * (p_area->get_gravity() / Math::pow(v.length() * p_area->get_gravity_distance_scale() + 1, 2));
+ } else {
+ gravity += (p_area->get_transform().xform(p_area->get_gravity_vector()) - get_transform().get_origin()).normalized() * p_area->get_gravity();
+ }
+ } else {
+ gravity += p_area->get_gravity_vector() * p_area->get_gravity();
+ }
+}
+
void SoftBody3DSW::predict_motion(real_t p_delta) {
const real_t inv_delta = 1.0 / p_delta;
@@ -935,9 +948,35 @@ void SoftBody3DSW::predict_motion(real_t p_delta) {
Area3DSW *def_area = get_space()->get_default_area();
ERR_FAIL_COND(!def_area);
+ gravity = def_area->get_gravity_vector() * def_area->get_gravity();
+
+ int ac = areas.size();
+ bool stopped = false;
+
+ if (ac) {
+ areas.sort();
+ const AreaCMP *aa = &areas[0];
+ for (int i = ac - 1; i >= 0 && !stopped; i--) {
+ PhysicsServer3D::AreaSpaceOverrideMode mode = aa[i].area->get_space_override_mode();
+ switch (mode) {
+ case PhysicsServer3D::AREA_SPACE_OVERRIDE_COMBINE:
+ case PhysicsServer3D::AREA_SPACE_OVERRIDE_COMBINE_REPLACE: {
+ _compute_area_gravity(aa[i].area);
+ stopped = mode == PhysicsServer3D::AREA_SPACE_OVERRIDE_COMBINE_REPLACE;
+ } break;
+ case PhysicsServer3D::AREA_SPACE_OVERRIDE_REPLACE:
+ case PhysicsServer3D::AREA_SPACE_OVERRIDE_REPLACE_COMBINE: {
+ gravity = Vector3(0, 0, 0);
+ _compute_area_gravity(aa[i].area);
+ stopped = mode == PhysicsServer3D::AREA_SPACE_OVERRIDE_REPLACE;
+ } break;
+ default: {
+ }
+ }
+ }
+ }
// Apply forces.
- Vector3 gravity = def_area->get_gravity_vector() * def_area->get_gravity();
add_velocity(gravity * p_delta);
apply_forces();
diff --git a/servers/physics_3d/soft_body_3d_sw.h b/servers/physics_3d/soft_body_3d_sw.h
index ac8bcbf0b9..be0620c506 100644
--- a/servers/physics_3d/soft_body_3d_sw.h
+++ b/servers/physics_3d/soft_body_3d_sw.h
@@ -31,6 +31,7 @@
#ifndef SOFT_BODY_3D_SW_H
#define SOFT_BODY_3D_SW_H
+#include "area_3d_sw.h"
#include "collision_object_3d_sw.h"
#include "core/math/aabb.h"
@@ -100,14 +101,20 @@ class SoftBody3DSW : public CollisionObject3DSW {
real_t drag_coefficient = 0.0; // [0,1]
LocalVector<int> pinned_vertices;
+ Vector3 gravity;
+
SelfList<SoftBody3DSW> active_list;
Set<Constraint3DSW *> constraints;
+ Vector<AreaCMP> areas;
+
VSet<RID> exceptions;
uint64_t island_step = 0;
+ _FORCE_INLINE_ void _compute_area_gravity(const Area3DSW *p_area);
+
public:
SoftBody3DSW();
@@ -129,6 +136,25 @@ public:
_FORCE_INLINE_ uint64_t get_island_step() const { return island_step; }
_FORCE_INLINE_ void set_island_step(uint64_t p_step) { island_step = p_step; }
+ _FORCE_INLINE_ void add_area(Area3DSW *p_area) {
+ int index = areas.find(AreaCMP(p_area));
+ if (index > -1) {
+ areas.write[index].refCount += 1;
+ } else {
+ areas.ordered_insert(AreaCMP(p_area));
+ }
+ }
+
+ _FORCE_INLINE_ void remove_area(Area3DSW *p_area) {
+ int index = areas.find(AreaCMP(p_area));
+ if (index > -1) {
+ areas.write[index].refCount -= 1;
+ if (areas[index].refCount < 1) {
+ areas.remove(index);
+ }
+ }
+ }
+
virtual void set_space(Space3DSW *p_space);
void set_mesh(const Ref<Mesh> &p_mesh);
diff --git a/servers/physics_3d/space_3d_sw.cpp b/servers/physics_3d/space_3d_sw.cpp
index 730460d66a..f9e55ad525 100644
--- a/servers/physics_3d/space_3d_sw.cpp
+++ b/servers/physics_3d/space_3d_sw.cpp
@@ -600,7 +600,7 @@ bool Space3DSW::test_body_motion(Body3DSW *p_body, const Transform3D &p_from, co
if (!shapes_found) {
if (r_result) {
*r_result = PhysicsServer3D::MotionResult();
- r_result->motion = p_motion;
+ r_result->travel = p_motion;
}
return false;
@@ -879,9 +879,9 @@ bool Space3DSW::test_body_motion(Body3DSW *p_body, const Transform3D &p_from, co
Vector3 rel_vec = rcd.best_contact - (body->get_transform().origin + body->get_center_of_mass());
r_result->collider_velocity = body->get_linear_velocity() + (body->get_angular_velocity()).cross(rel_vec);
- r_result->motion = safe * p_motion;
+ r_result->travel = safe * p_motion;
r_result->remainder = p_motion - safe * p_motion;
- r_result->motion += (body_transform.get_origin() - p_from.get_origin());
+ r_result->travel += (body_transform.get_origin() - p_from.get_origin());
}
collided = true;
@@ -889,9 +889,9 @@ bool Space3DSW::test_body_motion(Body3DSW *p_body, const Transform3D &p_from, co
}
if (!collided && r_result) {
- r_result->motion = p_motion;
+ r_result->travel = p_motion;
r_result->remainder = Vector3();
- r_result->motion += (body_transform.get_origin() - p_from.get_origin());
+ r_result->travel += (body_transform.get_origin() - p_from.get_origin());
}
return collided;
@@ -921,7 +921,9 @@ void *Space3DSW::_broadphase_pair(CollisionObject3DSW *A, int p_subindex_A, Coll
Area2Pair3DSW *area2_pair = memnew(Area2Pair3DSW(area_b, p_subindex_B, area, p_subindex_A));
return area2_pair;
} else if (type_B == CollisionObject3DSW::TYPE_SOFT_BODY) {
- // Area/Soft Body, not supported.
+ SoftBody3DSW *softbody = static_cast<SoftBody3DSW *>(B);
+ AreaSoftBodyPair3DSW *soft_area_pair = memnew(AreaSoftBodyPair3DSW(softbody, p_subindex_B, area, p_subindex_A));
+ return soft_area_pair;
} else {
Body3DSW *body = static_cast<Body3DSW *>(B);
AreaPair3DSW *area_pair = memnew(AreaPair3DSW(body, p_subindex_B, area, p_subindex_A));
diff --git a/servers/physics_server_2d.cpp b/servers/physics_server_2d.cpp
index c2dedde0be..c96f85446c 100644
--- a/servers/physics_server_2d.cpp
+++ b/servers/physics_server_2d.cpp
@@ -422,11 +422,11 @@ void PhysicsDirectSpaceState2D::_bind_methods() {
///////////////////////////////
-Vector2 PhysicsTestMotionResult2D::get_motion() const {
- return result.motion;
+Vector2 PhysicsTestMotionResult2D::get_travel() const {
+ return result.travel;
}
-Vector2 PhysicsTestMotionResult2D::get_motion_remainder() const {
+Vector2 PhysicsTestMotionResult2D::get_remainder() const {
return result.remainder;
}
@@ -471,8 +471,8 @@ real_t PhysicsTestMotionResult2D::get_collision_unsafe_fraction() const {
}
void PhysicsTestMotionResult2D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("get_motion"), &PhysicsTestMotionResult2D::get_motion);
- ClassDB::bind_method(D_METHOD("get_motion_remainder"), &PhysicsTestMotionResult2D::get_motion_remainder);
+ ClassDB::bind_method(D_METHOD("get_travel"), &PhysicsTestMotionResult2D::get_travel);
+ ClassDB::bind_method(D_METHOD("get_remainder"), &PhysicsTestMotionResult2D::get_remainder);
ClassDB::bind_method(D_METHOD("get_collision_point"), &PhysicsTestMotionResult2D::get_collision_point);
ClassDB::bind_method(D_METHOD("get_collision_normal"), &PhysicsTestMotionResult2D::get_collision_normal);
ClassDB::bind_method(D_METHOD("get_collider_velocity"), &PhysicsTestMotionResult2D::get_collider_velocity);
@@ -484,8 +484,8 @@ void PhysicsTestMotionResult2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_collision_safe_fraction"), &PhysicsTestMotionResult2D::get_collision_safe_fraction);
ClassDB::bind_method(D_METHOD("get_collision_unsafe_fraction"), &PhysicsTestMotionResult2D::get_collision_unsafe_fraction);
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "motion"), "", "get_motion");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "motion_remainder"), "", "get_motion_remainder");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "travel"), "", "get_travel");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "remainder"), "", "get_remainder");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "collision_point"), "", "get_collision_point");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "collision_normal"), "", "get_collision_normal");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "collider_velocity"), "", "get_collider_velocity");
@@ -513,7 +513,7 @@ bool PhysicsServer2D::_body_test_motion(RID p_body, const Transform2D &p_from, c
}
void PhysicsServer2D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("line_shape_create"), &PhysicsServer2D::line_shape_create);
+ ClassDB::bind_method(D_METHOD("world_margin_shape_create"), &PhysicsServer2D::world_margin_shape_create);
ClassDB::bind_method(D_METHOD("segment_shape_create"), &PhysicsServer2D::segment_shape_create);
ClassDB::bind_method(D_METHOD("circle_shape_create"), &PhysicsServer2D::circle_shape_create);
ClassDB::bind_method(D_METHOD("rectangle_shape_create"), &PhysicsServer2D::rectangle_shape_create);
@@ -674,7 +674,7 @@ void PhysicsServer2D::_bind_methods() {
BIND_ENUM_CONSTANT(SPACE_PARAM_CONSTRAINT_DEFAULT_BIAS);
BIND_ENUM_CONSTANT(SPACE_PARAM_TEST_MOTION_MIN_CONTACT_DEPTH);
- BIND_ENUM_CONSTANT(SHAPE_LINE);
+ BIND_ENUM_CONSTANT(SHAPE_WORLD_MARGIN);
BIND_ENUM_CONSTANT(SHAPE_SEGMENT);
BIND_ENUM_CONSTANT(SHAPE_CIRCLE);
BIND_ENUM_CONSTANT(SHAPE_RECTANGLE);
diff --git a/servers/physics_server_2d.h b/servers/physics_server_2d.h
index dbbc128793..4c559dd7bd 100644
--- a/servers/physics_server_2d.h
+++ b/servers/physics_server_2d.h
@@ -219,7 +219,7 @@ public:
static PhysicsServer2D *get_singleton();
enum ShapeType {
- SHAPE_LINE, ///< plane:"plane"
+ SHAPE_WORLD_MARGIN, ///< plane:"plane"
SHAPE_SEGMENT, ///< float:"length"
SHAPE_CIRCLE, ///< float:"radius"
SHAPE_RECTANGLE, ///< vec3:"extents"
@@ -229,7 +229,7 @@ public:
SHAPE_CUSTOM, ///< Server-Implementation based custom shape, calling shape_create() with this value will result in an error
};
- virtual RID line_shape_create() = 0;
+ virtual RID world_margin_shape_create() = 0;
virtual RID segment_shape_create() = 0;
virtual RID circle_shape_create() = 0;
virtual RID rectangle_shape_create() = 0;
@@ -465,7 +465,7 @@ public:
virtual PhysicsDirectBodyState2D *body_get_direct_state(RID p_body) = 0;
struct MotionResult {
- Vector2 motion;
+ Vector2 travel;
Vector2 remainder;
Vector2 collision_point;
@@ -479,6 +479,10 @@ public:
RID collider;
int collider_shape = 0;
Variant collider_metadata;
+
+ real_t get_angle(Vector2 p_up_direction) const {
+ return Math::acos(collision_normal.dot(p_up_direction));
+ }
};
virtual bool body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, real_t p_margin = 0.08, MotionResult *r_result = nullptr, const Set<RID> &p_exclude = Set<RID>()) = 0;
@@ -588,8 +592,8 @@ protected:
public:
PhysicsServer2D::MotionResult *get_result_ptr() const { return const_cast<PhysicsServer2D::MotionResult *>(&result); }
- Vector2 get_motion() const;
- Vector2 get_motion_remainder() const;
+ Vector2 get_travel() const;
+ Vector2 get_remainder() const;
Vector2 get_collision_point() const;
Vector2 get_collision_normal() const;
diff --git a/servers/physics_server_3d.cpp b/servers/physics_server_3d.cpp
index 89987b5fe9..53863cea72 100644
--- a/servers/physics_server_3d.cpp
+++ b/servers/physics_server_3d.cpp
@@ -369,11 +369,11 @@ void PhysicsDirectSpaceState3D::_bind_methods() {
///////////////////////////////
-Vector3 PhysicsTestMotionResult3D::get_motion() const {
- return result.motion;
+Vector3 PhysicsTestMotionResult3D::get_travel() const {
+ return result.travel;
}
-Vector3 PhysicsTestMotionResult3D::get_motion_remainder() const {
+Vector3 PhysicsTestMotionResult3D::get_remainder() const {
return result.remainder;
}
@@ -418,8 +418,8 @@ real_t PhysicsTestMotionResult3D::get_collision_unsafe_fraction() const {
}
void PhysicsTestMotionResult3D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("get_motion"), &PhysicsTestMotionResult3D::get_motion);
- ClassDB::bind_method(D_METHOD("get_motion_remainder"), &PhysicsTestMotionResult3D::get_motion_remainder);
+ ClassDB::bind_method(D_METHOD("get_travel"), &PhysicsTestMotionResult3D::get_travel);
+ ClassDB::bind_method(D_METHOD("get_remainder"), &PhysicsTestMotionResult3D::get_remainder);
ClassDB::bind_method(D_METHOD("get_collision_point"), &PhysicsTestMotionResult3D::get_collision_point);
ClassDB::bind_method(D_METHOD("get_collision_normal"), &PhysicsTestMotionResult3D::get_collision_normal);
ClassDB::bind_method(D_METHOD("get_collider_velocity"), &PhysicsTestMotionResult3D::get_collider_velocity);
@@ -431,8 +431,8 @@ void PhysicsTestMotionResult3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_collision_safe_fraction"), &PhysicsTestMotionResult3D::get_collision_safe_fraction);
ClassDB::bind_method(D_METHOD("get_collision_unsafe_fraction"), &PhysicsTestMotionResult3D::get_collision_unsafe_fraction);
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "motion"), "", "get_motion");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "motion_remainder"), "", "get_motion_remainder");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "travel"), "", "get_travel");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "remainder"), "", "get_remainder");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "collision_point"), "", "get_collision_point");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "collision_normal"), "", "get_collision_normal");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "collider_velocity"), "", "get_collider_velocity");
diff --git a/servers/physics_server_3d.h b/servers/physics_server_3d.h
index 31ae352d9e..f806fd55be 100644
--- a/servers/physics_server_3d.h
+++ b/servers/physics_server_3d.h
@@ -473,7 +473,7 @@ public:
virtual PhysicsDirectBodyState3D *body_get_direct_state(RID p_body) = 0;
struct MotionResult {
- Vector3 motion;
+ Vector3 travel;
Vector3 remainder;
Vector3 collision_point;
@@ -487,6 +487,10 @@ public:
RID collider;
int collider_shape = 0;
Variant collider_metadata;
+
+ real_t get_angle(Vector3 p_up_direction) const {
+ return Math::acos(collision_normal.dot(p_up_direction));
+ }
};
virtual bool body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, real_t p_margin = 0.001, MotionResult *r_result = nullptr, const Set<RID> &p_exclude = Set<RID>()) = 0;
@@ -752,8 +756,8 @@ protected:
public:
PhysicsServer3D::MotionResult *get_result_ptr() const { return const_cast<PhysicsServer3D::MotionResult *>(&result); }
- Vector3 get_motion() const;
- Vector3 get_motion_remainder() const;
+ Vector3 get_travel() const;
+ Vector3 get_remainder() const;
Vector3 get_collision_point() const;
Vector3 get_collision_normal() const;
diff --git a/servers/rendering/renderer_rd/effects_rd.cpp b/servers/rendering/renderer_rd/effects_rd.cpp
index c609f25e31..f5b6be4795 100644
--- a/servers/rendering/renderer_rd/effects_rd.cpp
+++ b/servers/rendering/renderer_rd/effects_rd.cpp
@@ -739,6 +739,26 @@ void EffectsRD::make_mipmap(RID p_source_rd_texture, RID p_dest_texture, const S
RD::get_singleton()->compute_list_end();
}
+void EffectsRD::make_mipmap_raster(RID p_source_rd_texture, RID p_dest_framebuffer, const Size2i &p_size) {
+ ERR_FAIL_COND_MSG(!prefer_raster_effects, "Can't use the raster version of mipmap with the clustered renderer.");
+
+ memset(&blur_raster.push_constant, 0, sizeof(BlurRasterPushConstant));
+
+ BlurRasterMode mode = BLUR_MIPMAP;
+
+ blur_raster.push_constant.pixel_size[0] = 1.0 / float(p_size.x);
+ blur_raster.push_constant.pixel_size[1] = 1.0 / float(p_size.y);
+
+ RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
+ RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer)));
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_source_rd_texture), 0);
+ RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
+ RD::get_singleton()->draw_list_set_push_constant(draw_list, &blur_raster.push_constant, sizeof(BlurRasterPushConstant));
+
+ RD::get_singleton()->draw_list_draw(draw_list, true);
+ RD::get_singleton()->draw_list_end();
+}
+
void EffectsRD::copy_cubemap_to_dp(RID p_source_rd_texture, RID p_dst_framebuffer, const Rect2 &p_rect, float p_z_near, float p_z_far, bool p_dp_flip) {
CopyToDPPushConstant push_constant;
push_constant.screen_rect[0] = p_rect.position.x;
@@ -928,7 +948,7 @@ void EffectsRD::luminance_reduction_raster(RID p_source_texture, const Size2i p_
}
}
-void EffectsRD::bokeh_dof(RID p_base_texture, RID p_depth_texture, const Size2i &p_base_texture_size, RID p_secondary_texture, RID p_halfsize_texture1, RID p_halfsize_texture2, bool p_dof_far, float p_dof_far_begin, float p_dof_far_size, bool p_dof_near, float p_dof_near_begin, float p_dof_near_size, float p_bokeh_size, RenderingServer::DOFBokehShape p_bokeh_shape, RS::DOFBlurQuality p_quality, bool p_use_jitter, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal) {
+void EffectsRD::bokeh_dof(const BokehBuffers &p_buffers, bool p_dof_far, float p_dof_far_begin, float p_dof_far_size, bool p_dof_near, float p_dof_near_begin, float p_dof_near_size, float p_bokeh_size, RenderingServer::DOFBokehShape p_bokeh_shape, RS::DOFBlurQuality p_quality, bool p_use_jitter, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal) {
ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use compute version of BOKEH DOF with the mobile renderer.");
bokeh.push_constant.blur_far_active = p_dof_far;
@@ -957,22 +977,22 @@ void EffectsRD::bokeh_dof(RID p_base_texture, RID p_depth_texture, const Size2i
// The alpha channel of the source color texture is filled with the expected circle size
// If used for DOF far, the size is positive, if used for near, its negative.
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.pipelines[BOKEH_GEN_BLUR_SIZE]);
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.compute_pipelines[BOKEH_GEN_BLUR_SIZE]);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_base_texture), 0);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_depth_texture), 1);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_buffers.base_texture), 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_buffers.depth_texture), 1);
- bokeh.push_constant.size[0] = p_base_texture_size.x;
- bokeh.push_constant.size[1] = p_base_texture_size.y;
+ bokeh.push_constant.size[0] = p_buffers.base_texture_size.x;
+ bokeh.push_constant.size[1] = p_buffers.base_texture_size.y;
RD::get_singleton()->compute_list_set_push_constant(compute_list, &bokeh.push_constant, sizeof(BokehPushConstant));
- RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_base_texture_size.x, p_base_texture_size.y, 1);
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_buffers.base_texture_size.x, p_buffers.base_texture_size.y, 1);
RD::get_singleton()->compute_list_add_barrier(compute_list);
if (p_bokeh_shape == RS::DOF_BOKEH_BOX || p_bokeh_shape == RS::DOF_BOKEH_HEXAGON) {
//second pass
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.pipelines[p_bokeh_shape == RS::DOF_BOKEH_BOX ? BOKEH_GEN_BOKEH_BOX : BOKEH_GEN_BOKEH_HEXAGONAL]);
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.compute_pipelines[p_bokeh_shape == RS::DOF_BOKEH_BOX ? BOKEH_GEN_BOKEH_BOX : BOKEH_GEN_BOKEH_HEXAGONAL]);
static const int quality_samples[4] = { 6, 12, 12, 24 };
@@ -981,18 +1001,18 @@ void EffectsRD::bokeh_dof(RID p_base_texture, RID p_depth_texture, const Size2i
if (p_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || p_quality == RS::DOF_BLUR_QUALITY_LOW) {
//box and hexagon are more or less the same, and they can work in either half (very low and low quality) or full (medium and high quality_ sizes)
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_halfsize_texture1), 0);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_base_texture), 1);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_buffers.half_texture[0]), 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_buffers.base_texture), 1);
- bokeh.push_constant.size[0] = p_base_texture_size.x >> 1;
- bokeh.push_constant.size[1] = p_base_texture_size.y >> 1;
+ bokeh.push_constant.size[0] = p_buffers.base_texture_size.x >> 1;
+ bokeh.push_constant.size[1] = p_buffers.base_texture_size.y >> 1;
bokeh.push_constant.half_size = true;
bokeh.push_constant.blur_size *= 0.5;
} else {
//medium and high quality use full size
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_secondary_texture), 0);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_base_texture), 1);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_buffers.secondary_texture), 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_buffers.base_texture), 1);
}
RD::get_singleton()->compute_list_set_push_constant(compute_list, &bokeh.push_constant, sizeof(BokehPushConstant));
@@ -1004,11 +1024,11 @@ void EffectsRD::bokeh_dof(RID p_base_texture, RID p_depth_texture, const Size2i
bokeh.push_constant.second_pass = true;
if (p_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || p_quality == RS::DOF_BLUR_QUALITY_LOW) {
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_halfsize_texture2), 0);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_halfsize_texture1), 1);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_buffers.half_texture[1]), 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_buffers.half_texture[0]), 1);
} else {
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_base_texture), 0);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_secondary_texture), 1);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_buffers.base_texture), 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_buffers.secondary_texture), 1);
}
RD::get_singleton()->compute_list_set_push_constant(compute_list, &bokeh.push_constant, sizeof(BokehPushConstant));
@@ -1019,25 +1039,25 @@ void EffectsRD::bokeh_dof(RID p_base_texture, RID p_depth_texture, const Size2i
if (p_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || p_quality == RS::DOF_BLUR_QUALITY_LOW) {
//forth pass, upscale for low quality
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.pipelines[BOKEH_COMPOSITE]);
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.compute_pipelines[BOKEH_COMPOSITE]);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_base_texture), 0);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_halfsize_texture2), 1);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_buffers.base_texture), 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_buffers.half_texture[1]), 1);
- bokeh.push_constant.size[0] = p_base_texture_size.x;
- bokeh.push_constant.size[1] = p_base_texture_size.y;
+ bokeh.push_constant.size[0] = p_buffers.base_texture_size.x;
+ bokeh.push_constant.size[1] = p_buffers.base_texture_size.y;
bokeh.push_constant.half_size = false;
bokeh.push_constant.second_pass = false;
RD::get_singleton()->compute_list_set_push_constant(compute_list, &bokeh.push_constant, sizeof(BokehPushConstant));
- RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_base_texture_size.x, p_base_texture_size.y, 1);
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_buffers.base_texture_size.x, p_buffers.base_texture_size.y, 1);
}
} else {
//circle
//second pass
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.pipelines[BOKEH_GEN_BOKEH_CIRCULAR]);
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.compute_pipelines[BOKEH_GEN_BOKEH_CIRCULAR]);
static const float quality_scale[4] = { 8.0, 4.0, 1.0, 0.5 };
@@ -1046,11 +1066,11 @@ void EffectsRD::bokeh_dof(RID p_base_texture, RID p_depth_texture, const Size2i
//circle always runs in half size, otherwise too expensive
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_halfsize_texture1), 0);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_base_texture), 1);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_buffers.half_texture[0]), 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_buffers.base_texture), 1);
- bokeh.push_constant.size[0] = p_base_texture_size.x >> 1;
- bokeh.push_constant.size[1] = p_base_texture_size.y >> 1;
+ bokeh.push_constant.size[0] = p_buffers.base_texture_size.x >> 1;
+ bokeh.push_constant.size[1] = p_buffers.base_texture_size.y >> 1;
bokeh.push_constant.half_size = true;
RD::get_singleton()->compute_list_set_push_constant(compute_list, &bokeh.push_constant, sizeof(BokehPushConstant));
@@ -1062,93 +1082,195 @@ void EffectsRD::bokeh_dof(RID p_base_texture, RID p_depth_texture, const Size2i
// upscale
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.pipelines[BOKEH_COMPOSITE]);
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.compute_pipelines[BOKEH_COMPOSITE]);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_base_texture), 0);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_halfsize_texture1), 1);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_buffers.base_texture), 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_buffers.half_texture[0]), 1);
- bokeh.push_constant.size[0] = p_base_texture_size.x;
- bokeh.push_constant.size[1] = p_base_texture_size.y;
+ bokeh.push_constant.size[0] = p_buffers.base_texture_size.x;
+ bokeh.push_constant.size[1] = p_buffers.base_texture_size.y;
bokeh.push_constant.half_size = false;
bokeh.push_constant.second_pass = false;
RD::get_singleton()->compute_list_set_push_constant(compute_list, &bokeh.push_constant, sizeof(BokehPushConstant));
- RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_base_texture_size.x, p_base_texture_size.y, 1);
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_buffers.base_texture_size.x, p_buffers.base_texture_size.y, 1);
}
RD::get_singleton()->compute_list_end();
}
-void EffectsRD::blur_dof_raster(RID p_base_texture, RID p_depth_texture, const Size2i &p_base_texture_size, RID p_base_fb, RID p_secondary_texture, RID p_secondary_fb, bool p_dof_far, float p_dof_far_begin, float p_dof_far_size, bool p_dof_near, float p_dof_near_begin, float p_dof_near_size, float p_dof_blur_amount, RS::DOFBlurQuality p_quality, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal) {
+void EffectsRD::bokeh_dof_raster(const BokehBuffers &p_buffers, bool p_dof_far, float p_dof_far_begin, float p_dof_far_size, bool p_dof_near, float p_dof_near_begin, float p_dof_near_size, float p_dof_blur_amount, RenderingServer::DOFBokehShape p_bokeh_shape, RS::DOFBlurQuality p_quality, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal) {
ERR_FAIL_COND_MSG(!prefer_raster_effects, "Can't use blur DOF with the clustered renderer.");
- memset(&blur_raster.push_constant, 0, sizeof(BlurRasterPushConstant));
+ memset(&bokeh.push_constant, 0, sizeof(BokehPushConstant));
- BlurRasterMode blur_mode;
- int qsteps[4] = { 4, 4, 10, 20 };
- uint32_t base_flags = p_cam_orthogonal ? BLUR_FLAG_USE_ORTHOGONAL_PROJECTION : 0;
-
- Vector2 pixel_size = Vector2(1.0 / p_base_texture_size.width, 1.0 / p_base_texture_size.height);
+ bokeh.push_constant.orthogonal = p_cam_orthogonal;
+ bokeh.push_constant.size[0] = p_buffers.base_texture_size.width;
+ bokeh.push_constant.size[1] = p_buffers.base_texture_size.height;
+ bokeh.push_constant.z_far = p_cam_zfar;
+ bokeh.push_constant.z_near = p_cam_znear;
- blur_raster.push_constant.dof_radius = (p_dof_blur_amount * p_dof_blur_amount) / qsteps[p_quality];
- blur_raster.push_constant.pixel_size[0] = pixel_size.x;
- blur_raster.push_constant.pixel_size[1] = pixel_size.y;
- blur_raster.push_constant.camera_z_far = p_cam_zfar;
- blur_raster.push_constant.camera_z_near = p_cam_znear;
+ bokeh.push_constant.second_pass = false;
+ bokeh.push_constant.half_size = false;
+ bokeh.push_constant.blur_size = p_dof_blur_amount;
if (p_dof_far || p_dof_near) {
- if (p_quality == RS::DOF_BLUR_QUALITY_HIGH) {
- blur_mode = BLUR_MODE_DOF_HIGH;
- } else if (p_quality == RS::DOF_BLUR_QUALITY_MEDIUM) {
- blur_mode = BLUR_MODE_DOF_MEDIUM;
- } else { // for LOW or VERYLOW we use LOW
- blur_mode = BLUR_MODE_DOF_LOW;
- }
-
if (p_dof_far) {
- base_flags |= BLUR_FLAG_DOF_FAR;
- blur_raster.push_constant.dof_far_begin = p_dof_far_begin;
- blur_raster.push_constant.dof_far_end = p_dof_far_begin + p_dof_far_size;
+ bokeh.push_constant.blur_far_active = true;
+ bokeh.push_constant.blur_far_begin = p_dof_far_begin;
+ bokeh.push_constant.blur_far_end = p_dof_far_begin + p_dof_far_size;
}
if (p_dof_near) {
- base_flags |= BLUR_FLAG_DOF_NEAR;
- blur_raster.push_constant.dof_near_begin = p_dof_near_begin;
- blur_raster.push_constant.dof_near_end = p_dof_near_begin - p_dof_near_size;
+ bokeh.push_constant.blur_near_active = true;
+ bokeh.push_constant.blur_near_begin = p_dof_near_begin;
+ bokeh.push_constant.blur_near_end = p_dof_near_begin - p_dof_near_size;
}
- //HORIZONTAL
- RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_secondary_fb, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
- RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[blur_mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_secondary_fb)));
- RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_base_texture), 0);
- RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_depth_texture), 1);
- RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
+ {
+ // generate our depth data
+ RID framebuffer = p_buffers.base_weight_fb;
+ RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
+ RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[BOKEH_GEN_BLUR_SIZE].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_buffers.depth_texture), 0);
+ RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
- blur_raster.push_constant.flags = base_flags | BLUR_FLAG_HORIZONTAL;
- blur_raster.push_constant.dof_dir[0] = 1.0;
- blur_raster.push_constant.dof_dir[1] = 0.0;
+ RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant));
- RD::get_singleton()->draw_list_set_push_constant(draw_list, &blur_raster.push_constant, sizeof(BlurRasterPushConstant));
+ RD::get_singleton()->draw_list_draw(draw_list, true);
+ RD::get_singleton()->draw_list_end();
+ }
- RD::get_singleton()->draw_list_draw(draw_list, true);
- RD::get_singleton()->draw_list_end();
+ if (p_bokeh_shape == RS::DOF_BOKEH_BOX || p_bokeh_shape == RS::DOF_BOKEH_HEXAGON) {
+ // double pass approach
+ BokehMode mode = p_bokeh_shape == RS::DOF_BOKEH_BOX ? BOKEH_GEN_BOKEH_BOX : BOKEH_GEN_BOKEH_HEXAGONAL;
- //VERTICAL
- draw_list = RD::get_singleton()->draw_list_begin(p_base_fb, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
- RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[blur_mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_base_fb)));
- RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_secondary_texture), 0);
- RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_depth_texture), 1);
- RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
+ if (p_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || p_quality == RS::DOF_BLUR_QUALITY_LOW) {
+ //box and hexagon are more or less the same, and they can work in either half (very low and low quality) or full (medium and high quality_ sizes)
+ bokeh.push_constant.size[0] = p_buffers.base_texture_size.x >> 1;
+ bokeh.push_constant.size[1] = p_buffers.base_texture_size.y >> 1;
+ bokeh.push_constant.half_size = true;
+ bokeh.push_constant.blur_size *= 0.5;
+ }
- blur_raster.push_constant.flags = base_flags;
- blur_raster.push_constant.dof_dir[0] = 0.0;
- blur_raster.push_constant.dof_dir[1] = 1.0;
+ static const int quality_samples[4] = { 6, 12, 12, 24 };
+ bokeh.push_constant.blur_scale = 0.5;
+ bokeh.push_constant.steps = quality_samples[p_quality];
- RD::get_singleton()->draw_list_set_push_constant(draw_list, &blur_raster.push_constant, sizeof(BlurRasterPushConstant));
+ RID framebuffer = bokeh.push_constant.half_size ? p_buffers.half_fb[0] : p_buffers.secondary_fb;
- RD::get_singleton()->draw_list_draw(draw_list, true);
- RD::get_singleton()->draw_list_end();
+ // Pass 1
+ RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
+ RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_buffers.base_texture), 0);
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_buffers.weight_texture[0]), 1);
+ RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
+
+ RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant));
+
+ RD::get_singleton()->draw_list_draw(draw_list, true);
+ RD::get_singleton()->draw_list_end();
+
+ // Pass 2
+ if (!bokeh.push_constant.half_size) {
+ // do not output weight, we're writing back into our base buffer
+ mode = p_bokeh_shape == RS::DOF_BOKEH_BOX ? BOKEH_GEN_BOKEH_BOX_NOWEIGHT : BOKEH_GEN_BOKEH_HEXAGONAL_NOWEIGHT;
+ }
+ bokeh.push_constant.second_pass = true;
+
+ framebuffer = bokeh.push_constant.half_size ? p_buffers.half_fb[1] : p_buffers.base_fb;
+ RID texture = bokeh.push_constant.half_size ? p_buffers.half_texture[0] : p_buffers.secondary_texture;
+ RID weight = bokeh.push_constant.half_size ? p_buffers.weight_texture[2] : p_buffers.weight_texture[1];
+
+ draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
+ RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(texture), 0);
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(weight), 1);
+ RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
+
+ RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant));
+
+ RD::get_singleton()->draw_list_draw(draw_list, true);
+ RD::get_singleton()->draw_list_end();
+
+ if (bokeh.push_constant.half_size) {
+ // Compose pass
+ mode = BOKEH_COMPOSITE;
+ framebuffer = p_buffers.base_fb;
+
+ draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
+ RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_buffers.half_texture[1]), 0);
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_buffers.weight_texture[3]), 1);
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_buffers.weight_texture[0]), 2);
+ RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
+
+ RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant));
+
+ RD::get_singleton()->draw_list_draw(draw_list, true);
+ RD::get_singleton()->draw_list_end();
+ }
+
+ } else {
+ // circular is a single pass approach
+ BokehMode mode = BOKEH_GEN_BOKEH_CIRCULAR;
+
+ {
+ // circle always runs in half size, otherwise too expensive (though the code below does support making this optional)
+ bokeh.push_constant.size[0] = p_buffers.base_texture_size.x >> 1;
+ bokeh.push_constant.size[1] = p_buffers.base_texture_size.y >> 1;
+ bokeh.push_constant.half_size = true;
+ // bokeh.push_constant.blur_size *= 0.5;
+ }
+
+ static const float quality_scale[4] = { 8.0, 4.0, 1.0, 0.5 };
+ bokeh.push_constant.blur_scale = quality_scale[p_quality];
+ bokeh.push_constant.steps = 0.0;
+
+ RID framebuffer = bokeh.push_constant.half_size ? p_buffers.half_fb[0] : p_buffers.secondary_fb;
+
+ RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
+ RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_buffers.base_texture), 0);
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_buffers.weight_texture[0]), 1);
+ RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
+
+ RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant));
+
+ RD::get_singleton()->draw_list_draw(draw_list, true);
+ RD::get_singleton()->draw_list_end();
+
+ if (bokeh.push_constant.half_size) {
+ // Compose
+ mode = BOKEH_COMPOSITE;
+ framebuffer = p_buffers.base_fb;
+
+ draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
+ RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_buffers.half_texture[0]), 0);
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_buffers.weight_texture[2]), 1);
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_buffers.weight_texture[0]), 2);
+ RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
+
+ RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant));
+
+ RD::get_singleton()->draw_list_draw(draw_list, true);
+ RD::get_singleton()->draw_list_end();
+ } else {
+ // Just copy it back (we use our blur raster shader here)..
+ draw_list = RD::get_singleton()->draw_list_begin(p_buffers.base_fb, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
+ RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[BLUR_MODE_COPY].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_buffers.base_fb)));
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_buffers.secondary_texture), 0);
+ RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
+
+ memset(&blur_raster.push_constant, 0, sizeof(BlurRasterPushConstant));
+ RD::get_singleton()->draw_list_set_push_constant(draw_list, &blur_raster.push_constant, sizeof(BlurRasterPushConstant));
+
+ RD::get_singleton()->draw_list_draw(draw_list, true);
+ RD::get_singleton()->draw_list_end();
+ }
+ }
}
}
@@ -1771,12 +1893,11 @@ EffectsRD::EffectsRD(bool p_prefer_raster_effects) {
// init blur shader (on compute use copy shader)
Vector<String> blur_modes;
+ blur_modes.push_back("\n#define MODE_MIPMAP\n"); // BLUR_MIPMAP
blur_modes.push_back("\n#define MODE_GAUSSIAN_BLUR\n"); // BLUR_MODE_GAUSSIAN_BLUR
blur_modes.push_back("\n#define MODE_GAUSSIAN_GLOW\n"); // BLUR_MODE_GAUSSIAN_GLOW
blur_modes.push_back("\n#define MODE_GAUSSIAN_GLOW\n#define GLOW_USE_AUTO_EXPOSURE\n"); // BLUR_MODE_GAUSSIAN_GLOW_AUTO_EXPOSURE
- blur_modes.push_back("\n#define MODE_DOF_BLUR\n#define DOF_QUALITY_LOW\n"); // BLUR_MODE_DOF_LOW
- blur_modes.push_back("\n#define MODE_DOF_BLUR\n#define DOF_QUALITY_MEDIUM\n"); // BLUR_MODE_DOF_MEDIUM
- blur_modes.push_back("\n#define MODE_DOF_BLUR\n#define DOF_QUALITY_HIGH\n"); // BLUR_MODE_DOF_HIGH
+ blur_modes.push_back("\n#define MODE_COPY\n"); // BLUR_MODE_COPY
blur_raster.shader.initialize(blur_modes);
memset(&blur_raster.push_constant, 0, sizeof(BlurRasterPushConstant));
@@ -1956,23 +2077,39 @@ EffectsRD::EffectsRD(bool p_prefer_raster_effects) {
cube_to_dp.pipeline.setup(shader, RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), dss, RD::PipelineColorBlendState(), 0);
}
+ // Initialize bokeh
+ Vector<String> bokeh_modes;
+ bokeh_modes.push_back("\n#define MODE_GEN_BLUR_SIZE\n");
+ bokeh_modes.push_back("\n#define MODE_BOKEH_BOX\n#define OUTPUT_WEIGHT\n");
+ bokeh_modes.push_back("\n#define MODE_BOKEH_BOX\n");
+ bokeh_modes.push_back("\n#define MODE_BOKEH_HEXAGONAL\n#define OUTPUT_WEIGHT\n");
+ bokeh_modes.push_back("\n#define MODE_BOKEH_HEXAGONAL\n");
+ bokeh_modes.push_back("\n#define MODE_BOKEH_CIRCULAR\n#define OUTPUT_WEIGHT\n");
+ bokeh_modes.push_back("\n#define MODE_COMPOSITE_BOKEH\n");
if (prefer_raster_effects) {
- // not supported
- } else {
- // Initialize bokeh
- Vector<String> bokeh_modes;
- bokeh_modes.push_back("\n#define MODE_GEN_BLUR_SIZE\n");
- bokeh_modes.push_back("\n#define MODE_BOKEH_BOX\n");
- bokeh_modes.push_back("\n#define MODE_BOKEH_HEXAGONAL\n");
- bokeh_modes.push_back("\n#define MODE_BOKEH_CIRCULAR\n");
- bokeh_modes.push_back("\n#define MODE_COMPOSITE_BOKEH\n");
+ bokeh.raster_shader.initialize(bokeh_modes);
+
+ bokeh.shader_version = bokeh.raster_shader.version_create();
- bokeh.shader.initialize(bokeh_modes);
+ const int att_count[BOKEH_MAX] = { 1, 2, 1, 2, 1, 2, 1 };
+ for (int i = 0; i < BOKEH_MAX; i++) {
+ RD::PipelineColorBlendState blend_state = (i == BOKEH_COMPOSITE) ? RD::PipelineColorBlendState::create_blend(att_count[i]) : RD::PipelineColorBlendState::create_disabled(att_count[i]);
+ bokeh.raster_pipelines[i].setup(bokeh.raster_shader.version_get_shader(bokeh.shader_version, i), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), blend_state, 0);
+ }
+ } else {
+ bokeh.compute_shader.initialize(bokeh_modes);
+ bokeh.compute_shader.set_variant_enabled(BOKEH_GEN_BOKEH_BOX_NOWEIGHT, false);
+ bokeh.compute_shader.set_variant_enabled(BOKEH_GEN_BOKEH_HEXAGONAL_NOWEIGHT, false);
+ bokeh.shader_version = bokeh.compute_shader.version_create();
- bokeh.shader_version = bokeh.shader.version_create();
+ for (int i = 0; i < BOKEH_MAX; i++) {
+ if (bokeh.compute_shader.is_variant_enabled(i)) {
+ bokeh.compute_pipelines[i] = RD::get_singleton()->compute_pipeline_create(bokeh.compute_shader.version_get_shader(bokeh.shader_version, i));
+ }
+ }
for (int i = 0; i < BOKEH_MAX; i++) {
- bokeh.pipelines[i] = RD::get_singleton()->compute_pipeline_create(bokeh.shader.version_get_shader(bokeh.shader_version, i));
+ bokeh.raster_pipelines[i].clear();
}
}
@@ -2173,12 +2310,13 @@ EffectsRD::EffectsRD(bool p_prefer_raster_effects) {
if (prefer_raster_effects) {
filter.raster_shader.initialize(cubemap_filter_modes);
- filter.shader_version = filter.raster_shader.version_create();
// array variants are not supported in raster
filter.raster_shader.set_variant_enabled(FILTER_MODE_HIGH_QUALITY_ARRAY, false);
filter.raster_shader.set_variant_enabled(FILTER_MODE_LOW_QUALITY_ARRAY, false);
+ filter.shader_version = filter.raster_shader.version_create();
+
for (int i = 0; i < FILTER_MODE_MAX; i++) {
if (filter.raster_shader.is_variant_enabled(i)) {
filter.raster_pipelines[i].setup(filter.raster_shader.version_get_shader(filter.shader_version, i), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0);
@@ -2390,12 +2528,13 @@ EffectsRD::~EffectsRD() {
if (prefer_raster_effects) {
blur_raster.shader.version_free(blur_raster.shader_version);
+ bokeh.raster_shader.version_free(blur_raster.shader_version);
luminance_reduce_raster.shader.version_free(luminance_reduce_raster.shader_version);
roughness.raster_shader.version_free(roughness.shader_version);
cubemap_downsampler.raster_shader.version_free(cubemap_downsampler.shader_version);
filter.raster_shader.version_free(filter.shader_version);
} else {
- bokeh.shader.version_free(bokeh.shader_version);
+ bokeh.compute_shader.version_free(bokeh.shader_version);
luminance_reduce.shader.version_free(luminance_reduce.shader_version);
roughness.compute_shader.version_free(roughness.shader_version);
cubemap_downsampler.compute_shader.version_free(cubemap_downsampler.shader_version);
diff --git a/servers/rendering/renderer_rd/effects_rd.h b/servers/rendering/renderer_rd/effects_rd.h
index 79d9a8b738..0c9b2efb7f 100644
--- a/servers/rendering/renderer_rd/effects_rd.h
+++ b/servers/rendering/renderer_rd/effects_rd.h
@@ -35,6 +35,7 @@
#include "servers/rendering/renderer_rd/pipeline_cache_rd.h"
#include "servers/rendering/renderer_rd/shaders/blur_raster.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/bokeh_dof.glsl.gen.h"
+#include "servers/rendering/renderer_rd/shaders/bokeh_dof_raster.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/copy.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/copy_to_fb.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/cube_to_dp.glsl.gen.h"
@@ -69,13 +70,12 @@ private:
bool prefer_raster_effects;
enum BlurRasterMode {
+ BLUR_MIPMAP,
+
BLUR_MODE_GAUSSIAN_BLUR,
BLUR_MODE_GAUSSIAN_GLOW,
BLUR_MODE_GAUSSIAN_GLOW_AUTO_EXPOSURE,
-
- BLUR_MODE_DOF_LOW,
- BLUR_MODE_DOF_MEDIUM,
- BLUR_MODE_DOF_HIGH,
+ BLUR_MODE_COPY,
BLUR_MODE_MAX
};
@@ -84,8 +84,6 @@ private:
BLUR_FLAG_HORIZONTAL = (1 << 0),
BLUR_FLAG_USE_ORTHOGONAL_PROJECTION = (1 << 1),
BLUR_FLAG_GLOW_FIRST_PASS = (1 << 2),
- BLUR_FLAG_DOF_FAR = (1 << 3),
- BLUR_FLAG_DOF_NEAR = (1 << 4),
};
struct BlurRasterPushConstant {
@@ -103,19 +101,6 @@ private:
float glow_white;
float glow_luminance_cap;
float glow_auto_exposure_grey;
-
- //dof
- float dof_far_begin;
- float dof_far_end;
- float dof_near_begin;
- float dof_near_end;
-
- float dof_radius;
- float dof_pad[3];
-
- float dof_dir[2];
- float camera_z_far;
- float camera_z_near;
};
struct BlurRaster {
@@ -252,29 +237,29 @@ private:
};
struct TonemapPushConstant {
- float bcs[3];
- uint32_t use_bcs;
+ float bcs[3]; // 12 - 12
+ uint32_t use_bcs; // 4 - 16
- uint32_t use_glow;
- uint32_t use_auto_exposure;
- uint32_t use_color_correction;
- uint32_t tonemapper;
+ uint32_t use_glow; // 4 - 20
+ uint32_t use_auto_exposure; // 4 - 24
+ uint32_t use_color_correction; // 4 - 28
+ uint32_t tonemapper; // 4 - 32
- uint32_t glow_texture_size[2];
- float glow_intensity;
- uint32_t pad3;
+ uint32_t glow_texture_size[2]; // 8 - 40
+ float glow_intensity; // 4 - 44
+ uint32_t pad3; // 4 - 48
- uint32_t glow_mode;
- float glow_levels[7];
+ uint32_t glow_mode; // 4 - 52
+ float glow_levels[7]; // 28 - 80
- float exposure;
- float white;
- float auto_exposure_grey;
- uint32_t pad2;
+ float exposure; // 4 - 84
+ float white; // 4 - 88
+ float auto_exposure_grey; // 4 - 92
+ uint32_t pad2; // 4 - 96
- float pixel_size[2];
- uint32_t use_fxaa;
- uint32_t use_debanding;
+ float pixel_size[2]; // 8 - 104
+ uint32_t use_fxaa; // 4 - 108
+ uint32_t use_debanding; // 4 - 112
};
/* tonemap actually writes to a framebuffer, which is
@@ -375,7 +360,9 @@ private:
enum BokehMode {
BOKEH_GEN_BLUR_SIZE,
BOKEH_GEN_BOKEH_BOX,
+ BOKEH_GEN_BOKEH_BOX_NOWEIGHT,
BOKEH_GEN_BOKEH_HEXAGONAL,
+ BOKEH_GEN_BOKEH_HEXAGONAL_NOWEIGHT,
BOKEH_GEN_BOKEH_CIRCULAR,
BOKEH_COMPOSITE,
BOKEH_MAX
@@ -383,9 +370,11 @@ private:
struct Bokeh {
BokehPushConstant push_constant;
- BokehDofShaderRD shader;
+ BokehDofShaderRD compute_shader;
+ BokehDofRasterShaderRD raster_shader;
RID shader_version;
- RID pipelines[BOKEH_MAX];
+ RID compute_pipelines[BOKEH_MAX];
+ PipelineCacheRD raster_pipelines[BOKEH_MAX];
} bokeh;
enum SSAOMode {
@@ -780,12 +769,31 @@ public:
void cubemap_roughness(RID p_source_rd_texture, RID p_dest_texture, uint32_t p_face_id, uint32_t p_sample_count, float p_roughness, float p_size);
void cubemap_roughness_raster(RID p_source_rd_texture, RID p_dest_framebuffer, uint32_t p_face_id, uint32_t p_sample_count, float p_roughness, float p_size);
void make_mipmap(RID p_source_rd_texture, RID p_dest_texture, const Size2i &p_size);
+ void make_mipmap_raster(RID p_source_rd_texture, RID p_dest_framebuffer, const Size2i &p_size);
void copy_cubemap_to_dp(RID p_source_rd_texture, RID p_dest_texture, const Rect2 &p_rect, float p_z_near, float p_z_far, bool p_dp_flip);
void luminance_reduction(RID p_source_texture, const Size2i p_source_size, const Vector<RID> p_reduce, RID p_prev_luminance, float p_min_luminance, float p_max_luminance, float p_adjust, bool p_set = false);
void luminance_reduction_raster(RID p_source_texture, const Size2i p_source_size, const Vector<RID> p_reduce, Vector<RID> p_fb, RID p_prev_luminance, float p_min_luminance, float p_max_luminance, float p_adjust, bool p_set = false);
- void bokeh_dof(RID p_base_texture, RID p_depth_texture, const Size2i &p_base_texture_size, RID p_secondary_texture, RID p_bokeh_texture1, RID p_bokeh_texture2, bool p_dof_far, float p_dof_far_begin, float p_dof_far_size, bool p_dof_near, float p_dof_near_begin, float p_dof_near_size, float p_bokeh_size, RS::DOFBokehShape p_bokeh_shape, RS::DOFBlurQuality p_quality, bool p_use_jitter, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal);
- void blur_dof_raster(RID p_base_texture, RID p_depth_texture, const Size2i &p_base_texture_size, RID p_base_fb, RID p_secondary_texture, RID p_secondary_fb, bool p_dof_far, float p_dof_far_begin, float p_dof_far_size, bool p_dof_near, float p_dof_near_begin, float p_dof_near_size, float p_dof_blur_amount, RS::DOFBlurQuality p_quality, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal);
+ struct BokehBuffers {
+ // bokeh buffers
+
+ // textures
+ Size2i base_texture_size;
+ RID base_texture;
+ RID depth_texture;
+ RID secondary_texture;
+ RID half_texture[2];
+
+ // raster only
+ RID base_fb;
+ RID secondary_fb; // with weights
+ RID half_fb[2]; // with weights
+ RID base_weight_fb;
+ RID weight_texture[4];
+ };
+
+ void bokeh_dof(const BokehBuffers &p_buffers, bool p_dof_far, float p_dof_far_begin, float p_dof_far_size, bool p_dof_near, float p_dof_near_begin, float p_dof_near_size, float p_bokeh_size, RS::DOFBokehShape p_bokeh_shape, RS::DOFBlurQuality p_quality, bool p_use_jitter, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal);
+ void bokeh_dof_raster(const BokehBuffers &p_buffers, bool p_dof_far, float p_dof_far_begin, float p_dof_far_size, bool p_dof_near, float p_dof_near_begin, float p_dof_near_size, float p_dof_blur_amount, RenderingServer::DOFBokehShape p_bokeh_shape, RS::DOFBlurQuality p_quality, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal);
struct TonemapSettings {
bool use_glow = false;
diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
index 0d304556c2..5a0ad97c7c 100644
--- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
+++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
@@ -1533,6 +1533,16 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
}
}
+ if (scene_state.used_screen_texture) {
+ // Copy screen texture to backbuffer so we can read from it
+ _render_buffers_copy_screen_texture(p_render_data);
+ }
+
+ if (scene_state.used_depth_texture) {
+ // Copy depth texture to backbuffer so we can read from it
+ _render_buffers_copy_depth_texture(p_render_data);
+ }
+
RENDER_TIMESTAMP("Render Transparent Pass");
RD::get_singleton()->draw_command_begin_label("Render Transparent Pass");
@@ -2219,7 +2229,8 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend
RD::Uniform u;
u.binding = 9;
u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- RID texture = (false && rb && rb->depth.is_valid()) ? rb->depth : storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_WHITE);
+ RID dbt = rb ? render_buffers_get_back_depth_texture(p_render_data->render_buffers) : RID();
+ RID texture = (dbt.is_valid()) ? dbt : storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_WHITE);
u.ids.push_back(texture);
uniforms.push_back(u);
}
diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
index 89d14e871a..be18a73989 100644
--- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
+++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
@@ -131,9 +131,7 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) {
SceneShaderForwardClustered *shader_singleton = (SceneShaderForwardClustered *)SceneShaderForwardClustered::singleton;
Error err = shader_singleton->compiler.compile(RS::SHADER_SPATIAL, code, &actions, path, gen_code);
- if (err != OK) {
- return;
- }
+ ERR_FAIL_COND_MSG(err != OK, "Shader compilation failed.");
if (version.is_null()) {
version = shader_singleton->shader.version_create();
diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
index f810ae2454..96b3cdadd4 100644
--- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
+++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
@@ -414,7 +414,8 @@ RID RenderForwardMobile::_setup_render_pass_uniform_set(RenderListType p_render_
RD::Uniform u;
u.binding = 9;
u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- RID texture = (false && rb && rb->depth.is_valid()) ? rb->depth : storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_WHITE);
+ RID dbt = rb ? render_buffers_get_back_depth_texture(p_render_data->render_buffers) : RID();
+ RID texture = (dbt.is_valid()) ? dbt : storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_WHITE);
u.ids.push_back(texture);
uniforms.push_back(u);
}
@@ -664,6 +665,23 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
_pre_opaque_render(p_render_data, false, false, RID(), RID());
+ uint32_t spec_constant_base_flags = 0;
+
+ {
+ //figure out spec constants
+
+ if (p_render_data->directional_light_count > 0) {
+ if (p_render_data->directional_light_soft_shadows) {
+ spec_constant_base_flags |= 1 << SPEC_CONSTANT_USING_DIRECTIONAL_SOFT_SHADOWS;
+ }
+ } else {
+ spec_constant_base_flags |= 1 << SPEC_CONSTANT_DISABLE_DIRECTIONAL_LIGHTS;
+ }
+
+ if (!is_environment(p_render_data->environment) || environment_is_fog_enabled(p_render_data->environment)) {
+ spec_constant_base_flags |= 1 << SPEC_CONSTANT_DISABLE_FOG;
+ }
+ }
{
if (render_buffer) {
RD::get_singleton()->draw_command_begin_label("Render 3D Pass");
@@ -706,7 +724,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
}
RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(framebuffer);
- RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].element_info.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, PASS_MODE_COLOR, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->lod_camera_plane, p_render_data->lod_distance_multiplier, p_render_data->screen_lod_threshold, p_render_data->view_count);
+ RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].element_info.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, PASS_MODE_COLOR, rp_uniform_set, spec_constant_base_flags, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->lod_camera_plane, p_render_data->lod_distance_multiplier, p_render_data->screen_lod_threshold, p_render_data->view_count);
render_list_params.framebuffer_format = fb_format;
if ((uint32_t)render_list_params.element_count > render_list_thread_threshold && false) {
// secondary command buffers need more testing at this time
@@ -752,6 +770,16 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
RD::get_singleton()->draw_command_end_label(); // Render 3D Pass / Render Reflection Probe Pass
}
+ if (scene_state.used_screen_texture) {
+ // Copy screen texture to backbuffer so we can read from it
+ _render_buffers_copy_screen_texture(p_render_data);
+ }
+
+ if (scene_state.used_depth_texture) {
+ // Copy depth texture to backbuffer so we can read from it
+ _render_buffers_copy_depth_texture(p_render_data);
+ }
+
// transparent pass
RD::get_singleton()->draw_command_begin_label("Render Transparent Subpass");
@@ -760,7 +788,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
if (using_subpass_transparent) {
RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(framebuffer);
- RenderListParameters render_list_params(render_list[RENDER_LIST_ALPHA].elements.ptr(), render_list[RENDER_LIST_ALPHA].element_info.ptr(), render_list[RENDER_LIST_ALPHA].elements.size(), reverse_cull, PASS_MODE_COLOR, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->lod_camera_plane, p_render_data->lod_distance_multiplier, p_render_data->screen_lod_threshold, p_render_data->view_count);
+ RenderListParameters render_list_params(render_list[RENDER_LIST_ALPHA].elements.ptr(), render_list[RENDER_LIST_ALPHA].element_info.ptr(), render_list[RENDER_LIST_ALPHA].elements.size(), reverse_cull, PASS_MODE_COLOR, rp_uniform_set, spec_constant_base_flags, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->lod_camera_plane, p_render_data->lod_distance_multiplier, p_render_data->screen_lod_threshold, p_render_data->view_count);
render_list_params.framebuffer_format = fb_format;
if ((uint32_t)render_list_params.element_count > render_list_thread_threshold && false) {
// secondary command buffers need more testing at this time
@@ -797,7 +825,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
// _setup_environment(p_render_data, p_render_data->reflection_probe.is_valid(), screen_size, !p_render_data->reflection_probe.is_valid(), p_default_bg_color, false);
RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(framebuffer);
- RenderListParameters render_list_params(render_list[RENDER_LIST_ALPHA].elements.ptr(), render_list[RENDER_LIST_ALPHA].element_info.ptr(), render_list[RENDER_LIST_ALPHA].elements.size(), reverse_cull, PASS_MODE_COLOR, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->lod_camera_plane, p_render_data->lod_distance_multiplier, p_render_data->screen_lod_threshold, p_render_data->view_count);
+ RenderListParameters render_list_params(render_list[RENDER_LIST_ALPHA].elements.ptr(), render_list[RENDER_LIST_ALPHA].element_info.ptr(), render_list[RENDER_LIST_ALPHA].elements.size(), reverse_cull, PASS_MODE_COLOR, rp_uniform_set, spec_constant_base_flags, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->lod_camera_plane, p_render_data->lod_distance_multiplier, p_render_data->screen_lod_threshold, p_render_data->view_count);
render_list_params.framebuffer_format = fb_format;
if ((uint32_t)render_list_params.element_count > render_list_thread_threshold && false) {
// secondary command buffers need more testing at this time
@@ -924,7 +952,7 @@ void RenderForwardMobile::_render_shadow_end(uint32_t p_barrier) {
for (uint32_t i = 0; i < scene_state.shadow_passes.size(); i++) {
SceneState::ShadowPass &shadow_pass = scene_state.shadow_passes[i];
- RenderListParameters render_list_parameters(render_list[RENDER_LIST_SECONDARY].elements.ptr() + shadow_pass.element_from, render_list[RENDER_LIST_SECONDARY].element_info.ptr() + shadow_pass.element_from, shadow_pass.element_count, shadow_pass.flip_cull, shadow_pass.pass_mode, shadow_pass.rp_uniform_set, false, Vector2(), shadow_pass.camera_plane, shadow_pass.lod_distance_multiplier, shadow_pass.screen_lod_threshold, 1, shadow_pass.element_from, RD::BARRIER_MASK_NO_BARRIER);
+ RenderListParameters render_list_parameters(render_list[RENDER_LIST_SECONDARY].elements.ptr() + shadow_pass.element_from, render_list[RENDER_LIST_SECONDARY].element_info.ptr() + shadow_pass.element_from, shadow_pass.element_count, shadow_pass.flip_cull, shadow_pass.pass_mode, shadow_pass.rp_uniform_set, 0, false, Vector2(), shadow_pass.camera_plane, shadow_pass.lod_distance_multiplier, shadow_pass.screen_lod_threshold, 1, shadow_pass.element_from, RD::BARRIER_MASK_NO_BARRIER);
_render_list_with_threads(&render_list_parameters, shadow_pass.framebuffer, RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_DISCARD, shadow_pass.initial_depth_action, shadow_pass.final_depth_action, Vector<Color>(), 1.0, 0, shadow_pass.rect);
}
@@ -964,7 +992,7 @@ void RenderForwardMobile::_render_material(const Transform3D &p_cam_transform, c
RENDER_TIMESTAMP("Render Material");
{
- RenderListParameters render_list_params(render_list[RENDER_LIST_SECONDARY].elements.ptr(), render_list[RENDER_LIST_SECONDARY].element_info.ptr(), render_list[RENDER_LIST_SECONDARY].elements.size(), true, pass_mode, rp_uniform_set);
+ RenderListParameters render_list_params(render_list[RENDER_LIST_SECONDARY].elements.ptr(), render_list[RENDER_LIST_SECONDARY].element_info.ptr(), render_list[RENDER_LIST_SECONDARY].elements.size(), true, pass_mode, rp_uniform_set, 0);
//regular forward for now
Vector<Color> clear;
clear.push_back(Color(0, 0, 0, 0));
@@ -1005,7 +1033,7 @@ void RenderForwardMobile::_render_uv2(const PagedArray<GeometryInstance *> &p_in
RENDER_TIMESTAMP("Render Material");
{
- RenderListParameters render_list_params(render_list[RENDER_LIST_SECONDARY].elements.ptr(), render_list[RENDER_LIST_SECONDARY].element_info.ptr(), render_list[RENDER_LIST_SECONDARY].elements.size(), true, pass_mode, rp_uniform_set, true);
+ RenderListParameters render_list_params(render_list[RENDER_LIST_SECONDARY].elements.ptr(), render_list[RENDER_LIST_SECONDARY].element_info.ptr(), render_list[RENDER_LIST_SECONDARY].elements.size(), true, pass_mode, rp_uniform_set, true, 0);
//regular forward for now
Vector<Color> clear;
clear.push_back(Color(0, 0, 0, 0));
@@ -1079,7 +1107,7 @@ void RenderForwardMobile::_render_particle_collider_heightfield(RID p_fb, const
{
//regular forward for now
- RenderListParameters render_list_params(render_list[RENDER_LIST_SECONDARY].elements.ptr(), render_list[RENDER_LIST_SECONDARY].element_info.ptr(), render_list[RENDER_LIST_SECONDARY].elements.size(), false, pass_mode, rp_uniform_set);
+ RenderListParameters render_list_params(render_list[RENDER_LIST_SECONDARY].elements.ptr(), render_list[RENDER_LIST_SECONDARY].element_info.ptr(), render_list[RENDER_LIST_SECONDARY].elements.size(), false, pass_mode, rp_uniform_set, 0);
_render_list_with_threads(&render_list_params, p_fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ);
}
RD::get_singleton()->draw_command_end_label();
@@ -1722,7 +1750,7 @@ void RenderForwardMobile::_render_list_with_threads(RenderListParameters *p_para
}
}
-void RenderForwardMobile::_fill_push_constant_instance_indices(GeometryInstanceForwardMobile::PushConstant *p_push_constant, const GeometryInstanceForwardMobile *p_instance) {
+void RenderForwardMobile::_fill_push_constant_instance_indices(GeometryInstanceForwardMobile::PushConstant *p_push_constant, uint32_t &spec_constants, const GeometryInstanceForwardMobile *p_instance) {
// first zero out our indices
p_push_constant->omni_lights[0] = 0xFFFF;
@@ -1737,6 +1765,19 @@ void RenderForwardMobile::_fill_push_constant_instance_indices(GeometryInstanceF
p_push_constant->reflection_probes[0] = 0xFFFF;
p_push_constant->reflection_probes[1] = 0xFFFF;
+ if (p_instance->omni_light_count == 0) {
+ spec_constants |= 1 << SPEC_CONSTANT_DISABLE_OMNI_LIGHTS;
+ }
+ if (p_instance->spot_light_count == 0) {
+ spec_constants |= 1 << SPEC_CONSTANT_DISABLE_SPOT_LIGHTS;
+ }
+ if (p_instance->reflection_probe_count == 0) {
+ spec_constants |= 1 << SPEC_CONSTANT_DISABLE_REFLECTION_PROBES;
+ }
+ if (p_instance->decals_count == 0) {
+ spec_constants |= 1 << SPEC_CONSTANT_DISABLE_DECALS;
+ }
+
for (uint32_t i = 0; i < MAX_RDL_CULL; i++) {
uint32_t ofs = i < 4 ? 0 : 1;
uint32_t shift = (i & 0x3) << 3;
@@ -1784,6 +1825,8 @@ void RenderForwardMobile::_render_list_template(RenderingDevice::DrawListID p_dr
const RenderElementInfo &element_info = p_params->element_info[i];
const GeometryInstanceForwardMobile *inst = surf->owner;
+ uint32_t base_spec_constants = p_params->spec_constant_base_flags;
+
// GeometryInstanceForwardMobile::PushConstant push_constant = inst->push_constant;
GeometryInstanceForwardMobile::PushConstant push_constant;
@@ -1819,7 +1862,13 @@ void RenderForwardMobile::_render_list_template(RenderingDevice::DrawListID p_dr
mesh_surface = surf->surface_shadow;
} else {
- _fill_push_constant_instance_indices(&push_constant, inst);
+ if (inst->use_projector) {
+ base_spec_constants |= 1 << SPEC_CONSTANT_USING_PROJECTOR;
+ }
+ if (inst->use_soft_shadow) {
+ base_spec_constants |= 1 << SPEC_CONSTANT_USING_SOFT_SHADOWS;
+ }
+ _fill_push_constant_instance_indices(&push_constant, base_spec_constants, inst);
#ifdef DEBUG_ENABLED
if (unlikely(get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_LIGHTING)) {
@@ -1911,7 +1960,7 @@ void RenderForwardMobile::_render_list_template(RenderingDevice::DrawListID p_dr
prev_index_array_rd = index_array_rd;
}
- RID pipeline_rd = pipeline->get_render_pipeline(vertex_format, framebuffer_format, p_params->force_wireframe, p_params->subpass);
+ RID pipeline_rd = pipeline->get_render_pipeline(vertex_format, framebuffer_format, p_params->force_wireframe, p_params->subpass, base_spec_constants);
if (pipeline_rd != prev_pipeline_rd) {
// checking with prev shader does not make so much sense, as
@@ -2170,6 +2219,11 @@ void RenderForwardMobile::geometry_instance_pair_voxel_gi_instances(GeometryInst
}
void RenderForwardMobile::geometry_instance_set_softshadow_projector_pairing(GeometryInstance *p_geometry_instance, bool p_softshadow, bool p_projector) {
+ GeometryInstanceForwardMobile *ginstance = static_cast<GeometryInstanceForwardMobile *>(p_geometry_instance);
+ ERR_FAIL_COND(!ginstance);
+
+ ginstance->use_projector = p_projector;
+ ginstance->use_soft_shadow = p_softshadow;
}
void RenderForwardMobile::_geometry_instance_mark_dirty(GeometryInstance *p_geometry_instance) {
@@ -2548,12 +2602,12 @@ void RenderForwardMobile::_update_shader_quality_settings() {
spec_constants.push_back(sc);
sc.type = RD::PIPELINE_SPECIALIZATION_CONSTANT_TYPE_BOOL;
- sc.constant_id = SPEC_CONSTANT_DECAL_FILTER;
+ sc.constant_id = SPEC_CONSTANT_DECAL_USE_MIPMAPS;
sc.bool_value = decals_get_filter() == RS::DECAL_FILTER_NEAREST_MIPMAPS || decals_get_filter() == RS::DECAL_FILTER_LINEAR_MIPMAPS || decals_get_filter() == RS::DECAL_FILTER_LINEAR_MIPMAPS_ANISOTROPIC;
spec_constants.push_back(sc);
- sc.constant_id = SPEC_CONSTANT_PROJECTOR_FILTER;
+ sc.constant_id = SPEC_CONSTANT_PROJECTOR_USE_MIPMAPS;
sc.bool_value = light_projectors_get_filter() == RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS || light_projectors_get_filter() == RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS || light_projectors_get_filter() == RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS_ANISOTROPIC;
spec_constants.push_back(sc);
diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h
index b981592840..764d8e80df 100644
--- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h
+++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h
@@ -65,12 +65,27 @@ protected:
};
enum {
- SPEC_CONSTANT_SOFT_SHADOW_SAMPLES = 6,
- SPEC_CONSTANT_PENUMBRA_SHADOW_SAMPLES = 7,
- SPEC_CONSTANT_DIRECTIONAL_SOFT_SHADOW_SAMPLES = 8,
- SPEC_CONSTANT_DIRECTIONAL_PENUMBRA_SHADOW_SAMPLES = 9,
- SPEC_CONSTANT_DECAL_FILTER = 10,
- SPEC_CONSTANT_PROJECTOR_FILTER = 11,
+
+ SPEC_CONSTANT_USING_PROJECTOR = 0,
+ SPEC_CONSTANT_USING_SOFT_SHADOWS = 1,
+ SPEC_CONSTANT_USING_DIRECTIONAL_SOFT_SHADOWS = 2,
+
+ SPEC_CONSTANT_SOFT_SHADOW_SAMPLES = 3,
+ SPEC_CONSTANT_PENUMBRA_SHADOW_SAMPLES = 4,
+ SPEC_CONSTANT_DIRECTIONAL_SOFT_SHADOW_SAMPLES = 5,
+ SPEC_CONSTANT_DIRECTIONAL_PENUMBRA_SHADOW_SAMPLES = 6,
+
+ SPEC_CONSTANT_DECAL_USE_MIPMAPS = 7,
+ SPEC_CONSTANT_PROJECTOR_USE_MIPMAPS = 8,
+
+ SPEC_CONSTANT_DISABLE_OMNI_LIGHTS = 9,
+ SPEC_CONSTANT_DISABLE_SPOT_LIGHTS = 10,
+ SPEC_CONSTANT_DISABLE_REFLECTION_PROBES = 11,
+ SPEC_CONSTANT_DISABLE_DIRECTIONAL_LIGHTS = 12,
+
+ SPEC_CONSTANT_DISABLE_DECALS = 13,
+ SPEC_CONSTANT_DISABLE_FOG = 14,
+
};
enum {
@@ -159,6 +174,7 @@ protected:
bool force_wireframe = false;
Vector2 uv_offset;
Plane lod_plane;
+ uint32_t spec_constant_base_flags = 0;
float lod_distance_multiplier = 0.0;
float screen_lod_threshold = 0.0;
RD::FramebufferFormatID framebuffer_format = 0;
@@ -166,7 +182,7 @@ protected:
uint32_t barrier = RD::BARRIER_MASK_ALL;
uint32_t subpass = 0;
- RenderListParameters(GeometryInstanceSurfaceDataCache **p_elements, RenderElementInfo *p_element_info, int p_element_count, bool p_reverse_cull, PassMode p_pass_mode, RID p_render_pass_uniform_set, bool p_force_wireframe = false, const Vector2 &p_uv_offset = Vector2(), const Plane &p_lod_plane = Plane(), float p_lod_distance_multiplier = 0.0, float p_screen_lod_threshold = 0.0, uint32_t p_view_count = 1, uint32_t p_element_offset = 0, uint32_t p_barrier = RD::BARRIER_MASK_ALL) {
+ RenderListParameters(GeometryInstanceSurfaceDataCache **p_elements, RenderElementInfo *p_element_info, int p_element_count, bool p_reverse_cull, PassMode p_pass_mode, RID p_render_pass_uniform_set, uint32_t p_spec_constant_base_flags = 0, bool p_force_wireframe = false, const Vector2 &p_uv_offset = Vector2(), const Plane &p_lod_plane = Plane(), float p_lod_distance_multiplier = 0.0, float p_screen_lod_threshold = 0.0, uint32_t p_view_count = 1, uint32_t p_element_offset = 0, uint32_t p_barrier = RD::BARRIER_MASK_ALL) {
elements = p_elements;
element_info = p_element_info;
element_count = p_element_count;
@@ -182,6 +198,7 @@ protected:
screen_lod_threshold = p_screen_lod_threshold;
element_offset = p_element_offset;
barrier = p_barrier;
+ spec_constant_base_flags = p_spec_constant_base_flags;
}
};
@@ -526,6 +543,8 @@ protected:
RID transforms_uniform_set;
float depth = 0;
bool mirror = false;
+ bool use_projector = false;
+ bool use_soft_shadow = false;
Transform3D transform;
bool store_transform_cache = true; // if true we copy our transform into our PushConstant, if false we use our transforms UBO and clear our PushConstants transform
bool non_uniform_scale = false;
@@ -584,7 +603,7 @@ protected:
dirty_list_element(this) {}
};
- _FORCE_INLINE_ void _fill_push_constant_instance_indices(GeometryInstanceForwardMobile::PushConstant *p_push_constant, const GeometryInstanceForwardMobile *p_instance);
+ _FORCE_INLINE_ void _fill_push_constant_instance_indices(GeometryInstanceForwardMobile::PushConstant *p_push_constant, uint32_t &spec_constants, const GeometryInstanceForwardMobile *p_instance);
void _update_shader_quality_settings() override;
diff --git a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp
index bcdcb05653..735014a2ec 100644
--- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp
+++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp
@@ -135,8 +135,7 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) {
SceneShaderForwardMobile *shader_singleton = (SceneShaderForwardMobile *)SceneShaderForwardMobile::singleton;
Error err = shader_singleton->compiler.compile(RS::SHADER_SPATIAL, code, &actions, path, gen_code);
-
- ERR_FAIL_COND(err != OK);
+ ERR_FAIL_COND_MSG(err != OK, "Shader compilation failed.");
if (version.is_null()) {
version = shader_singleton->shader.version_create();
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
index 14b2768c29..f8aefdb29c 100644
--- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
@@ -1970,8 +1970,7 @@ void RendererCanvasRenderRD::ShaderData::set_code(const String &p_code) {
RendererCanvasRenderRD *canvas_singleton = (RendererCanvasRenderRD *)RendererCanvasRender::singleton;
Error err = canvas_singleton->shader.compiler.compile(RS::SHADER_CANVAS_ITEM, code, &actions, path, gen_code);
-
- ERR_FAIL_COND(err != OK);
+ ERR_FAIL_COND_MSG(err != OK, "Shader compilation failed.");
if (version.is_null()) {
version = canvas_singleton->shader.canvas_shader.version_create();
diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
index 057d108578..5016c99a1e 100644
--- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
@@ -1464,6 +1464,85 @@ void RendererSceneRenderRD::_allocate_blur_textures(RenderBuffers *rb) {
base_width = MAX(1, base_width >> 1);
base_height = MAX(1, base_height >> 1);
}
+
+ if (!_render_buffers_can_be_storage()) {
+ // create 4 weight textures, 2 full size, 2 half size
+
+ tf.format = RD::DATA_FORMAT_R16_SFLOAT; // We could probably use DATA_FORMAT_R8_SNORM if we don't pre-multiply by blur_size but that depends on whether we can remove DEPTH_GAP
+ tf.width = rb->width;
+ tf.height = rb->height;
+ tf.texture_type = rb->view_count > 1 ? RD::TEXTURE_TYPE_2D_ARRAY : RD::TEXTURE_TYPE_2D;
+ tf.array_layers = rb->view_count;
+ tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
+ tf.mipmaps = 1;
+ for (uint32_t i = 0; i < 4; i++) {
+ // associated blur texture
+ RID texture;
+ if (i == 0) {
+ texture = rb->texture;
+ } else if (i == 1) {
+ texture = rb->blur[0].mipmaps[0].texture;
+ } else if (i == 2) {
+ texture = rb->blur[1].mipmaps[0].texture;
+ } else if (i == 3) {
+ texture = rb->blur[0].mipmaps[1].texture;
+ }
+
+ // create weight texture
+ rb->weight_buffers[i].weight = RD::get_singleton()->texture_create(tf, RD::TextureView());
+
+ // create frame buffer
+ Vector<RID> fb;
+ fb.push_back(texture);
+ fb.push_back(rb->weight_buffers[i].weight);
+ rb->weight_buffers[i].fb = RD::get_singleton()->framebuffer_create(fb);
+
+ if (i == 1) {
+ // next 2 are half size
+ tf.width = MAX(1, tf.width >> 1);
+ tf.height = MAX(1, tf.height >> 1);
+ }
+ }
+
+ {
+ // and finally an FB for just our base weights
+ Vector<RID> fb;
+ fb.push_back(rb->weight_buffers[0].weight);
+ rb->base_weight_fb = RD::get_singleton()->framebuffer_create(fb);
+ }
+ }
+}
+
+void RendererSceneRenderRD::_allocate_depth_backbuffer_textures(RenderBuffers *rb) {
+ ERR_FAIL_COND(!rb->depth_back_texture.is_null());
+
+ {
+ RD::TextureFormat tf;
+ if (rb->view_count > 1) {
+ tf.texture_type = RD::TEXTURE_TYPE_2D_ARRAY;
+ }
+ // We're not using this as a depth stencil, just copying our data into this. May need to look into using a different format on mobile, maybe R16?
+ tf.format = RD::DATA_FORMAT_R32_SFLOAT;
+
+ tf.width = rb->width;
+ tf.height = rb->height;
+ tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT;
+ tf.array_layers = rb->view_count; // create a layer for every view
+
+ tf.usage_bits |= RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
+ tf.usage_bits |= RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; // set this as color attachment because we're copying data into it, it's not actually used as a depth buffer
+
+ rb->depth_back_texture = RD::get_singleton()->texture_create(tf, RD::TextureView());
+ }
+
+ if (!_render_buffers_can_be_storage()) {
+ // create framebuffer so we can write into this...
+
+ Vector<RID> fb;
+ fb.push_back(rb->depth_back_texture);
+
+ rb->depth_back_fb = RD::get_singleton()->framebuffer_create(fb, RD::INVALID_ID, rb->view_count);
+ }
}
void RendererSceneRenderRD::_allocate_luminance_textures(RenderBuffers *rb) {
@@ -1532,6 +1611,16 @@ void RendererSceneRenderRD::_free_render_buffer_data(RenderBuffers *rb) {
rb->depth_texture = RID();
}
+ if (rb->depth_back_fb.is_valid()) {
+ RD::get_singleton()->free(rb->depth_back_fb);
+ rb->depth_back_fb = RID();
+ }
+
+ if (rb->depth_back_texture.is_valid()) {
+ RD::get_singleton()->free(rb->depth_back_texture);
+ rb->depth_back_texture = RID();
+ }
+
for (int i = 0; i < 2; i++) {
for (int m = 0; m < rb->blur[i].mipmaps.size(); m++) {
// do we free the texture slice here? or is it enough to free the main texture?
@@ -1832,6 +1921,58 @@ void RendererSceneRenderRD::_process_ssao(RID p_render_buffers, RID p_environmen
storage->get_effects()->generate_ssao(rb->depth_texture, p_normal_buffer, rb->ssao.depth, rb->ssao.depth_slices, rb->ssao.ao_deinterleaved, rb->ssao.ao_deinterleaved_slices, rb->ssao.ao_pong, rb->ssao.ao_pong_slices, rb->ssao.ao_final, rb->ssao.importance_map[0], rb->ssao.importance_map[1], p_projection, settings, uniform_sets_are_invalid, rb->ssao.downsample_uniform_set, rb->ssao.gather_uniform_set, rb->ssao.importance_map_uniform_set);
}
+void RendererSceneRenderRD::_render_buffers_copy_screen_texture(const RenderDataRD *p_render_data) {
+ RenderBuffers *rb = render_buffers_owner.getornull(p_render_data->render_buffers);
+ ERR_FAIL_COND(!rb);
+
+ RD::get_singleton()->draw_command_begin_label("Copy screen texture");
+
+ if (rb->blur[0].texture.is_null()) {
+ _allocate_blur_textures(rb);
+ }
+
+ // @TODO IMPLEMENT MULTIVIEW, all effects need to support stereo buffers or effects are only applied to the left eye
+
+ bool can_use_storage = _render_buffers_can_be_storage();
+
+ if (can_use_storage) {
+ storage->get_effects()->copy_to_rect(rb->texture, rb->blur[0].mipmaps[0].texture, Rect2i(0, 0, rb->width, rb->height));
+ for (int i = 1; i < rb->blur[0].mipmaps.size(); i++) {
+ storage->get_effects()->make_mipmap(rb->blur[0].mipmaps[i - 1].texture, rb->blur[0].mipmaps[i].texture, Size2i(rb->blur[0].mipmaps[i].width, rb->blur[0].mipmaps[i].height));
+ }
+ } else {
+ storage->get_effects()->copy_to_fb_rect(rb->texture, rb->blur[0].mipmaps[0].fb, Rect2i(0, 0, rb->width, rb->height));
+ for (int i = 1; i < rb->blur[0].mipmaps.size(); i++) {
+ storage->get_effects()->make_mipmap_raster(rb->blur[0].mipmaps[i - 1].texture, rb->blur[0].mipmaps[i].fb, Size2i(rb->blur[0].mipmaps[i].width, rb->blur[0].mipmaps[i].height));
+ }
+ }
+
+ RD::get_singleton()->draw_command_end_label();
+}
+
+void RendererSceneRenderRD::_render_buffers_copy_depth_texture(const RenderDataRD *p_render_data) {
+ RenderBuffers *rb = render_buffers_owner.getornull(p_render_data->render_buffers);
+ ERR_FAIL_COND(!rb);
+
+ RD::get_singleton()->draw_command_begin_label("Copy depth texture");
+
+ if (rb->depth_back_texture.is_null()) {
+ _allocate_depth_backbuffer_textures(rb);
+ }
+
+ // @TODO IMPLEMENT MULTIVIEW, all effects need to support stereo buffers or effects are only applied to the left eye
+
+ bool can_use_storage = _render_buffers_can_be_storage();
+
+ if (can_use_storage) {
+ storage->get_effects()->copy_to_rect(rb->depth_texture, rb->depth_back_texture, Rect2i(0, 0, rb->width, rb->height));
+ } else {
+ storage->get_effects()->copy_to_fb_rect(rb->depth_texture, rb->depth_back_fb, Rect2i(0, 0, rb->width, rb->height));
+ }
+
+ RD::get_singleton()->draw_command_end_label();
+}
+
void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const RenderDataRD *p_render_data) {
RenderBuffers *rb = render_buffers_owner.getornull(p_render_data->render_buffers);
ERR_FAIL_COND(!rb);
@@ -1851,11 +1992,34 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
_allocate_blur_textures(rb);
}
+ EffectsRD::BokehBuffers buffers;
+
+ // textures we use
+ buffers.base_texture_size = Size2i(rb->width, rb->height);
+ buffers.base_texture = rb->texture;
+ buffers.depth_texture = rb->depth_texture;
+ buffers.secondary_texture = rb->blur[0].mipmaps[0].texture;
+ buffers.half_texture[0] = rb->blur[1].mipmaps[0].texture;
+ buffers.half_texture[1] = rb->blur[0].mipmaps[1].texture;
+
+ float bokeh_size = camfx->dof_blur_amount * 64.0;
if (can_use_storage) {
- float bokeh_size = camfx->dof_blur_amount * 64.0;
- storage->get_effects()->bokeh_dof(rb->texture, rb->depth_texture, Size2i(rb->width, rb->height), rb->blur[0].mipmaps[0].texture, rb->blur[1].mipmaps[0].texture, rb->blur[0].mipmaps[1].texture, camfx->dof_blur_far_enabled, camfx->dof_blur_far_distance, camfx->dof_blur_far_transition, camfx->dof_blur_near_enabled, camfx->dof_blur_near_distance, camfx->dof_blur_near_transition, bokeh_size, dof_blur_bokeh_shape, dof_blur_quality, dof_blur_use_jitter, p_render_data->z_near, p_render_data->z_far, p_render_data->cam_ortogonal);
+ storage->get_effects()->bokeh_dof(buffers, camfx->dof_blur_far_enabled, camfx->dof_blur_far_distance, camfx->dof_blur_far_transition, camfx->dof_blur_near_enabled, camfx->dof_blur_near_distance, camfx->dof_blur_near_transition, bokeh_size, dof_blur_bokeh_shape, dof_blur_quality, dof_blur_use_jitter, p_render_data->z_near, p_render_data->z_far, p_render_data->cam_ortogonal);
} else {
- storage->get_effects()->blur_dof_raster(rb->texture, rb->depth_texture, Size2i(rb->width, rb->height), rb->texture_fb, rb->blur[0].mipmaps[0].texture, rb->blur[0].mipmaps[0].fb, camfx->dof_blur_far_enabled, camfx->dof_blur_far_distance, camfx->dof_blur_far_transition, camfx->dof_blur_near_enabled, camfx->dof_blur_near_distance, camfx->dof_blur_near_transition, camfx->dof_blur_amount, dof_blur_quality, p_render_data->z_near, p_render_data->z_far, p_render_data->cam_ortogonal);
+ // set framebuffers
+ buffers.base_fb = rb->texture_fb;
+ buffers.secondary_fb = rb->weight_buffers[1].fb;
+ buffers.half_fb[0] = rb->weight_buffers[2].fb;
+ buffers.half_fb[1] = rb->weight_buffers[3].fb;
+ buffers.weight_texture[0] = rb->weight_buffers[0].weight;
+ buffers.weight_texture[1] = rb->weight_buffers[1].weight;
+ buffers.weight_texture[2] = rb->weight_buffers[2].weight;
+ buffers.weight_texture[3] = rb->weight_buffers[3].weight;
+
+ // set weight buffers
+ buffers.base_weight_fb = rb->base_weight_fb;
+
+ storage->get_effects()->bokeh_dof_raster(buffers, camfx->dof_blur_far_enabled, camfx->dof_blur_far_distance, camfx->dof_blur_far_transition, camfx->dof_blur_near_enabled, camfx->dof_blur_near_distance, camfx->dof_blur_near_transition, bokeh_size, dof_blur_bokeh_shape, dof_blur_quality, p_render_data->z_near, p_render_data->z_far, p_render_data->cam_ortogonal);
}
RD::get_singleton()->draw_command_end_label();
}
@@ -2157,6 +2321,15 @@ RID RendererSceneRenderRD::render_buffers_get_back_buffer_texture(RID p_render_b
return rb->blur[0].texture;
}
+RID RendererSceneRenderRD::render_buffers_get_back_depth_texture(RID p_render_buffers) {
+ RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers);
+ ERR_FAIL_COND_V(!rb, RID());
+ if (!rb->depth_back_texture.is_valid()) {
+ return RID(); //not valid at the moment
+ }
+ return rb->depth_back_texture;
+}
+
RID RendererSceneRenderRD::render_buffers_get_ao_texture(RID p_render_buffers) {
RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers);
ERR_FAIL_COND_V(!rb, RID());
diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.h b/servers/rendering/renderer_rd/renderer_scene_render_rd.h
index 336dd6e1a7..5306df36f0 100644
--- a/servers/rendering/renderer_rd/renderer_scene_render_rd.h
+++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.h
@@ -134,6 +134,8 @@ protected:
void _pre_opaque_render(RenderDataRD *p_render_data, bool p_use_ssao, bool p_use_gi, RID p_normal_roughness_buffer, RID p_voxel_gi_buffer);
+ void _render_buffers_copy_screen_texture(const RenderDataRD *p_render_data);
+ void _render_buffers_copy_depth_texture(const RenderDataRD *p_render_data);
void _render_buffers_post_process_and_tonemap(const RenderDataRD *p_render_data);
void _post_process_subpass(RID p_source_texture, RID p_framebuffer, const RenderDataRD *p_render_data);
void _disable_clear_request(const RenderDataRD *p_render_data);
@@ -483,6 +485,18 @@ private:
Blur blur[2]; //the second one starts from the first mipmap
+ struct WeightBuffers {
+ RID weight;
+ RID fb; // FB with both texture and weight
+ };
+
+ // 2 full size, 2 half size
+ WeightBuffers weight_buffers[4]; // Only used in raster
+ RID base_weight_fb; // base buffer for weight
+
+ RID depth_back_texture;
+ RID depth_back_fb; // only used on mobile
+
struct Luminance {
Vector<RID> reduce;
RID current;
@@ -526,6 +540,7 @@ private:
void _free_render_buffer_data(RenderBuffers *rb);
void _allocate_blur_textures(RenderBuffers *rb);
+ void _allocate_depth_backbuffer_textures(RenderBuffers *rb);
void _allocate_luminance_textures(RenderBuffers *rb);
void _render_buffers_debug_draw(RID p_render_buffers, RID p_shadow_atlas, RID p_occlusion_buffer);
@@ -1174,6 +1189,7 @@ public:
RID render_buffers_get_ao_texture(RID p_render_buffers);
RID render_buffers_get_back_buffer_texture(RID p_render_buffers);
+ RID render_buffers_get_back_depth_texture(RID p_render_buffers);
RID render_buffers_get_voxel_gi_buffer(RID p_render_buffers);
RID render_buffers_get_default_voxel_gi_buffer();
RID render_buffers_get_gi_ambient_texture(RID p_render_buffers);
diff --git a/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp
index 9331cab090..9e85608f1e 100644
--- a/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp
@@ -92,8 +92,7 @@ void RendererSceneSkyRD::SkyShaderData::set_code(const String &p_code) {
RendererSceneRenderRD *scene_singleton = (RendererSceneRenderRD *)RendererSceneRenderRD::singleton;
Error err = scene_singleton->sky.sky_shader.compiler.compile(RS::SHADER_SKY, code, &actions, path, gen_code);
-
- ERR_FAIL_COND(err != OK);
+ ERR_FAIL_COND_MSG(err != OK, "Shader compilation failed.");
if (version.is_null()) {
version = scene_singleton->sky.sky_shader.shader.version_create();
diff --git a/servers/rendering/renderer_rd/renderer_storage_rd.cpp b/servers/rendering/renderer_rd/renderer_storage_rd.cpp
index 096d5563e6..7d438345f1 100644
--- a/servers/rendering/renderer_rd/renderer_storage_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_storage_rd.cpp
@@ -5268,8 +5268,7 @@ void RendererStorageRD::ParticlesShaderData::set_code(const String &p_code) {
actions.uniforms = &uniforms;
Error err = base_singleton->particles_shader.compiler.compile(RS::SHADER_PARTICLES, code, &actions, path, gen_code);
-
- ERR_FAIL_COND(err != OK);
+ ERR_FAIL_COND_MSG(err != OK, "Shader compilation failed.");
if (version.is_null()) {
version = base_singleton->particles_shader.shader.version_create();
diff --git a/servers/rendering/renderer_rd/shader_rd.h b/servers/rendering/renderer_rd/shader_rd.h
index 44705b2bd6..529328f0ed 100644
--- a/servers/rendering/renderer_rd/shader_rd.h
+++ b/servers/rendering/renderer_rd/shader_rd.h
@@ -142,9 +142,7 @@ public:
ERR_FAIL_COND_V(!variants_enabled[p_variant], RID());
Version *version = version_owner.getornull(p_version);
- if (!version) {
- return RID();
- }
+ ERR_FAIL_COND_V(!version, RID());
if (version->dirty) {
_compile_version(version);
diff --git a/servers/rendering/renderer_rd/shaders/blur_raster.glsl b/servers/rendering/renderer_rd/shaders/blur_raster.glsl
index b1d1c2365e..0789a4b396 100644
--- a/servers/rendering/renderer_rd/shaders/blur_raster.glsl
+++ b/servers/rendering/renderer_rd/shaders/blur_raster.glsl
@@ -37,32 +37,6 @@ layout(set = 1, binding = 0) uniform sampler2D source_auto_exposure;
layout(location = 0) out vec4 frag_color;
-//DOF
-#ifdef MODE_DOF_BLUR
-
-layout(set = 1, binding = 0) uniform sampler2D dof_source_depth;
-
-#ifdef DOF_QUALITY_LOW
-const int dof_kernel_size = 5;
-const int dof_kernel_from = 2;
-const float dof_kernel[5] = float[](0.153388, 0.221461, 0.250301, 0.221461, 0.153388);
-#endif
-
-#ifdef DOF_QUALITY_MEDIUM
-const int dof_kernel_size = 11;
-const int dof_kernel_from = 5;
-const float dof_kernel[11] = float[](0.055037, 0.072806, 0.090506, 0.105726, 0.116061, 0.119726, 0.116061, 0.105726, 0.090506, 0.072806, 0.055037);
-
-#endif
-
-#ifdef DOF_QUALITY_HIGH
-const int dof_kernel_size = 21;
-const int dof_kernel_from = 10;
-const float dof_kernel[21] = float[](0.028174, 0.032676, 0.037311, 0.041944, 0.046421, 0.050582, 0.054261, 0.057307, 0.059587, 0.060998, 0.061476, 0.060998, 0.059587, 0.057307, 0.054261, 0.050582, 0.046421, 0.041944, 0.037311, 0.032676, 0.028174);
-#endif
-
-#endif
-
void main() {
#ifdef MODE_MIPMAP
@@ -155,74 +129,8 @@ void main() {
#endif
-#ifdef MODE_DOF_BLUR
-
- vec4 color_accum = vec4(0.0);
-
- float depth = texture(dof_source_depth, uv_interp, 0.0).r;
- depth = depth * 2.0 - 1.0;
-
- if (bool(blur.flags & FLAG_USE_ORTHOGONAL_PROJECTION)) {
- depth = ((depth + (blur.camera_z_far + blur.camera_z_near) / (blur.camera_z_far - blur.camera_z_near)) * (blur.camera_z_far - blur.camera_z_near)) / 2.0;
- } else {
- depth = 2.0 * blur.camera_z_near * blur.camera_z_far / (blur.camera_z_far + blur.camera_z_near - depth * (blur.camera_z_far - blur.camera_z_near));
- }
-
- // mix near and far blur amount
- float amount = 1.0;
- if (bool(blur.flags & FLAG_DOF_FAR)) {
- amount *= 1.0 - smoothstep(blur.dof_far_begin, blur.dof_far_end, depth);
- }
- if (bool(blur.flags & FLAG_DOF_NEAR)) {
- amount *= smoothstep(blur.dof_near_end, blur.dof_near_begin, depth);
- }
- amount = 1.0 - amount;
-
- if (amount > 0.0) {
- float k_accum = 0.0;
-
- for (int i = 0; i < dof_kernel_size; i++) {
- int int_ofs = i - dof_kernel_from;
- vec2 tap_uv = uv_interp + blur.dof_dir * float(int_ofs) * amount * blur.dof_radius;
-
- float tap_k = dof_kernel[i];
-
- float tap_depth = texture(dof_source_depth, tap_uv, 0.0).r;
- tap_depth = tap_depth * 2.0 - 1.0;
-
- if (bool(blur.flags & FLAG_USE_ORTHOGONAL_PROJECTION)) {
- tap_depth = ((tap_depth + (blur.camera_z_far + blur.camera_z_near) / (blur.camera_z_far - blur.camera_z_near)) * (blur.camera_z_far - blur.camera_z_near)) / 2.0;
- } else {
- tap_depth = 2.0 * blur.camera_z_near * blur.camera_z_far / (blur.camera_z_far + blur.camera_z_near - tap_depth * (blur.camera_z_far - blur.camera_z_near));
- }
-
- // mix near and far blur amount
- float tap_amount = 1.0;
- if (bool(blur.flags & FLAG_DOF_FAR)) {
- tap_amount *= mix(1.0 - smoothstep(blur.dof_far_begin, blur.dof_far_end, tap_depth), 0.0, int_ofs == 0);
- }
- if (bool(blur.flags & FLAG_DOF_NEAR)) {
- tap_amount *= mix(smoothstep(blur.dof_near_end, blur.dof_near_begin, tap_depth), 0.0, int_ofs == 0);
- }
- tap_amount = 1.0 - tap_amount;
-
- tap_amount *= tap_amount * tap_amount; //prevent undesired glow effect
-
- vec4 tap_color = texture(source_color, tap_uv, 0.0) * tap_k;
-
- k_accum += tap_k * tap_amount;
- color_accum += tap_color * tap_amount;
- }
-
- if (k_accum > 0.0) {
- color_accum /= k_accum;
- }
-
- frag_color = color_accum; ///k_accum;
- } else {
- // we are in focus, don't waste time
- frag_color = texture(source_color, uv_interp, 0.0);
- }
-
+#ifdef MODE_COPY
+ vec4 color = textureLod(source_color, uv_interp, 0.0);
+ frag_color = color;
#endif
}
diff --git a/servers/rendering/renderer_rd/shaders/blur_raster_inc.glsl b/servers/rendering/renderer_rd/shaders/blur_raster_inc.glsl
index 6ea968e595..52bf2886b5 100644
--- a/servers/rendering/renderer_rd/shaders/blur_raster_inc.glsl
+++ b/servers/rendering/renderer_rd/shaders/blur_raster_inc.glsl
@@ -1,8 +1,6 @@
#define FLAG_HORIZONTAL (1 << 0)
#define FLAG_USE_ORTHOGONAL_PROJECTION (1 << 1)
#define FLAG_GLOW_FIRST_PASS (1 << 2)
-#define FLAG_DOF_FAR (1 << 3)
-#define FLAG_DOF_NEAR (1 << 4)
layout(push_constant, binding = 1, std430) uniform Blur {
vec2 pixel_size;
@@ -19,18 +17,5 @@ layout(push_constant, binding = 1, std430) uniform Blur {
float glow_white;
float glow_luminance_cap;
float glow_auto_exposure_grey;
-
- // DOF.
- float dof_far_begin;
- float dof_far_end;
- float dof_near_begin;
- float dof_near_end;
-
- float dof_radius;
- float dof_pad[3];
-
- vec2 dof_dir;
- float camera_z_far;
- float camera_z_near;
}
blur;
diff --git a/servers/rendering/renderer_rd/shaders/bokeh_dof.glsl b/servers/rendering/renderer_rd/shaders/bokeh_dof.glsl
index b70e0b6bd5..0438671dd2 100644
--- a/servers/rendering/renderer_rd/shaders/bokeh_dof.glsl
+++ b/servers/rendering/renderer_rd/shaders/bokeh_dof.glsl
@@ -25,34 +25,7 @@ layout(set = 1, binding = 0) uniform sampler2D source_bokeh;
// based on https://www.shadertoy.com/view/Xd3GDl
-layout(push_constant, binding = 1, std430) uniform Params {
- ivec2 size;
- float z_far;
- float z_near;
-
- bool orthogonal;
- float blur_size;
- float blur_scale;
- int blur_steps;
-
- bool blur_near_active;
- float blur_near_begin;
- float blur_near_end;
- bool blur_far_active;
-
- float blur_far_begin;
- float blur_far_end;
- bool second_pass;
- bool half_size;
-
- bool use_jitter;
- float jitter_seed;
- uint pad[2];
-}
-params;
-
-//used to work around downsampling filter
-#define DEPTH_GAP 0.0
+#include "bokeh_dof_inc.glsl"
#ifdef MODE_GEN_BLUR_SIZE
@@ -80,15 +53,6 @@ float get_blur_size(float depth) {
#endif
-const float GOLDEN_ANGLE = 2.39996323;
-
-//note: uniform pdf rand [0;1[
-float hash12n(vec2 p) {
- p = fract(p * vec2(5.3987, 5.4421));
- p += dot(p.yx, p.xy + vec2(21.5351, 14.3137));
- return fract(p.x * p.y * 95.4307);
-}
-
#if defined(MODE_BOKEH_BOX) || defined(MODE_BOKEH_HEXAGONAL)
vec4 weighted_filter_dir(vec2 dir, vec2 uv, vec2 pixel_size) {
diff --git a/servers/rendering/renderer_rd/shaders/bokeh_dof_inc.glsl b/servers/rendering/renderer_rd/shaders/bokeh_dof_inc.glsl
new file mode 100644
index 0000000000..fadea1631c
--- /dev/null
+++ b/servers/rendering/renderer_rd/shaders/bokeh_dof_inc.glsl
@@ -0,0 +1,37 @@
+layout(push_constant, binding = 1, std430) uniform Params {
+ ivec2 size;
+ float z_far;
+ float z_near;
+
+ bool orthogonal;
+ float blur_size;
+ float blur_scale;
+ int blur_steps;
+
+ bool blur_near_active;
+ float blur_near_begin;
+ float blur_near_end;
+ bool blur_far_active;
+
+ float blur_far_begin;
+ float blur_far_end;
+ bool second_pass;
+ bool half_size;
+
+ bool use_jitter;
+ float jitter_seed;
+ uint pad[2];
+}
+params;
+
+//used to work around downsampling filter
+#define DEPTH_GAP 0.0
+
+const float GOLDEN_ANGLE = 2.39996323;
+
+//note: uniform pdf rand [0;1[
+float hash12n(vec2 p) {
+ p = fract(p * vec2(5.3987, 5.4421));
+ p += dot(p.yx, p.xy + vec2(21.5351, 14.3137));
+ return fract(p.x * p.y * 95.4307);
+}
diff --git a/servers/rendering/renderer_rd/shaders/bokeh_dof_raster.glsl b/servers/rendering/renderer_rd/shaders/bokeh_dof_raster.glsl
new file mode 100644
index 0000000000..43a2a29616
--- /dev/null
+++ b/servers/rendering/renderer_rd/shaders/bokeh_dof_raster.glsl
@@ -0,0 +1,253 @@
+/* clang-format off */
+#[vertex]
+
+#version 450
+
+#VERSION_DEFINES
+
+#include "bokeh_dof_inc.glsl"
+
+layout(location = 0) out vec2 uv_interp;
+/* clang-format on */
+
+void main() {
+ vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0));
+ uv_interp = base_arr[gl_VertexIndex];
+
+ gl_Position = vec4(uv_interp * 2.0 - 1.0, 0.0, 1.0);
+}
+
+/* clang-format off */
+#[fragment]
+
+#version 450
+
+#VERSION_DEFINES
+
+#include "bokeh_dof_inc.glsl"
+
+layout(location = 0) in vec2 uv_interp;
+/* clang-format on */
+
+#ifdef MODE_GEN_BLUR_SIZE
+layout(location = 0) out float weight;
+
+layout(set = 0, binding = 0) uniform sampler2D source_depth;
+#else
+layout(location = 0) out vec4 frag_color;
+#ifdef OUTPUT_WEIGHT
+layout(location = 1) out float weight;
+#endif
+
+layout(set = 0, binding = 0) uniform sampler2D source_color;
+layout(set = 1, binding = 0) uniform sampler2D source_weight;
+#ifdef MODE_COMPOSITE_BOKEH
+layout(set = 2, binding = 0) uniform sampler2D original_weight;
+#endif
+#endif
+
+//DOF
+// Bokeh single pass implementation based on http://tuxedolabs.blogspot.com/2018/05/bokeh-depth-of-field-in-single-pass.html
+
+#ifdef MODE_GEN_BLUR_SIZE
+
+float get_depth_at_pos(vec2 uv) {
+ float depth = textureLod(source_depth, uv, 0.0).x;
+ if (params.orthogonal) {
+ depth = ((depth + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0;
+ } else {
+ depth = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - depth * (params.z_far - params.z_near));
+ }
+ return depth;
+}
+
+float get_blur_size(float depth) {
+ if (params.blur_near_active && depth < params.blur_near_begin) {
+ return -(1.0 - smoothstep(params.blur_near_end, params.blur_near_begin, depth)) * params.blur_size - DEPTH_GAP; //near blur is negative
+ }
+
+ if (params.blur_far_active && depth > params.blur_far_begin) {
+ return smoothstep(params.blur_far_begin, params.blur_far_end, depth) * params.blur_size + DEPTH_GAP;
+ }
+
+ return 0.0;
+}
+
+#endif
+
+#if defined(MODE_BOKEH_BOX) || defined(MODE_BOKEH_HEXAGONAL)
+
+vec4 weighted_filter_dir(vec2 dir, vec2 uv, vec2 pixel_size) {
+ dir *= pixel_size;
+ vec4 color = texture(source_color, uv);
+ color.a = texture(source_weight, uv).r;
+
+ vec4 accum = color;
+ float total = 1.0;
+
+ float blur_scale = params.blur_size / float(params.blur_steps);
+
+ if (params.use_jitter) {
+ uv += dir * (hash12n(uv + params.jitter_seed) - 0.5);
+ }
+
+ for (int i = -params.blur_steps; i <= params.blur_steps; i++) {
+ if (i == 0) {
+ continue;
+ }
+ float radius = float(i) * blur_scale;
+ vec2 suv = uv + dir * radius;
+ radius = abs(radius);
+
+ vec4 sample_color = texture(source_color, suv);
+ sample_color.a = texture(source_weight, suv).r;
+ float limit;
+
+ if (sample_color.a < color.a) {
+ limit = abs(sample_color.a);
+ } else {
+ limit = abs(color.a);
+ }
+
+ limit -= DEPTH_GAP;
+
+ float m = smoothstep(radius - 0.5, radius + 0.5, limit);
+
+ accum += mix(color, sample_color, m);
+
+ total += 1.0;
+ }
+
+ return accum / total;
+}
+
+#endif
+
+void main() {
+ vec2 pixel_size = 1.0 / vec2(params.size);
+ vec2 uv = uv_interp;
+
+#ifdef MODE_GEN_BLUR_SIZE
+ uv += pixel_size * 0.5;
+ float center_depth = get_depth_at_pos(uv);
+ weight = get_blur_size(center_depth);
+#endif
+
+#ifdef MODE_BOKEH_BOX
+ //pixel_size*=0.5; //resolution is doubled
+ if (params.second_pass || !params.half_size) {
+ uv += pixel_size * 0.5; //half pixel to read centers
+ } else {
+ uv += pixel_size * 0.25; //half pixel to read centers from full res
+ }
+
+ float alpha = texture(source_color, uv).a; // retain this
+ vec2 dir = (params.second_pass ? vec2(0.0, 1.0) : vec2(1.0, 0.0));
+
+ vec4 color = weighted_filter_dir(dir, uv, pixel_size);
+
+ frag_color = color;
+ frag_color.a = alpha; // attempt to retain this in case we have a transparent background, ignored if half_size
+#ifdef OUTPUT_WEIGHT
+ weight = color.a;
+#endif
+
+#endif
+
+#ifdef MODE_BOKEH_HEXAGONAL
+
+ //pixel_size*=0.5; //resolution is doubled
+ if (params.second_pass || !params.half_size) {
+ uv += pixel_size * 0.5; //half pixel to read centers
+ } else {
+ uv += pixel_size * 0.25; //half pixel to read centers from full res
+ }
+
+ float alpha = texture(source_color, uv).a; // retain this
+
+ vec2 dir = (params.second_pass ? normalize(vec2(1.0, 0.577350269189626)) : vec2(0.0, 1.0));
+
+ vec4 color = weighted_filter_dir(dir, uv, pixel_size);
+
+ if (params.second_pass) {
+ dir = normalize(vec2(-1.0, 0.577350269189626));
+
+ vec4 color2 = weighted_filter_dir(dir, uv, pixel_size);
+
+ color.rgb = min(color.rgb, color2.rgb);
+ color.a = (color.a + color2.a) * 0.5;
+ }
+
+ frag_color = color;
+ frag_color.a = alpha; // attempt to retain this in case we have a transparent background, ignored if half_size
+#ifdef OUTPUT_WEIGHT
+ weight = color.a;
+#endif
+
+#endif
+
+#ifdef MODE_BOKEH_CIRCULAR
+ if (params.half_size) {
+ pixel_size *= 0.5; //resolution is doubled
+ }
+
+ uv += pixel_size * 0.5; //half pixel to read centers
+
+ vec4 color = texture(source_color, uv);
+ float alpha = color.a; // retain this
+ color.a = texture(source_weight, uv).r;
+
+ vec4 color_accum = color;
+ float accum = 1.0;
+
+ float radius = params.blur_scale;
+ for (float ang = 0.0; radius < params.blur_size; ang += GOLDEN_ANGLE) {
+ vec2 uv_adj = uv + vec2(cos(ang), sin(ang)) * pixel_size * radius;
+
+ vec4 sample_color = texture(source_color, uv_adj);
+ sample_color.a = texture(source_weight, uv_adj).r;
+
+ float limit;
+
+ if (sample_color.a < color.a) {
+ limit = abs(sample_color.a);
+ } else {
+ limit = abs(color.a);
+ }
+
+ limit -= DEPTH_GAP;
+
+ float m = smoothstep(radius - 0.5, radius + 0.5, limit);
+ color_accum += mix(color_accum / accum, sample_color, m);
+ accum += 1.0;
+
+ radius += params.blur_scale / radius;
+ }
+
+ color_accum = color_accum / accum;
+
+ frag_color.rgb = color_accum.rgb;
+ frag_color.a = alpha; // attempt to retain this in case we have a transparent background, ignored if half_size
+#ifdef OUTPUT_WEIGHT
+ weight = color_accum.a;
+#endif
+
+#endif
+
+#ifdef MODE_COMPOSITE_BOKEH
+ frag_color.rgb = texture(source_color, uv).rgb;
+
+ float center_weigth = texture(source_weight, uv).r;
+ float sample_weight = texture(original_weight, uv).r;
+
+ float mix_amount;
+ if (sample_weight < center_weigth) {
+ mix_amount = min(1.0, max(0.0, max(abs(center_weigth), abs(sample_weight)) - DEPTH_GAP));
+ } else {
+ mix_amount = min(1.0, max(0.0, abs(center_weigth) - DEPTH_GAP));
+ }
+
+ // let alpha blending take care of mixing
+ frag_color.a = mix_amount;
+#endif
+}
diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl
index 2babe92c1c..663100a0b3 100644
--- a/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl
+++ b/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl
@@ -373,26 +373,38 @@ void main() {
//use medium precision for floats on mobile.
precision mediump float;
+precision highp int;
/* Specialization Constants */
-/* Specialization Constants (Toggles) */
+#if !defined(MODE_RENDER_DEPTH)
+
+#if !defined(MODE_UNSHADED)
+
+layout(constant_id = 0) const bool sc_use_light_projector = false;
+layout(constant_id = 1) const bool sc_use_light_soft_shadows = false;
+layout(constant_id = 2) const bool sc_use_directional_soft_shadows = false;
-layout(constant_id = 0) const bool sc_use_forward_gi = false;
-layout(constant_id = 1) const bool sc_use_light_projector = false;
-layout(constant_id = 2) const bool sc_use_light_soft_shadows = false;
-layout(constant_id = 3) const bool sc_use_directional_soft_shadows = false;
+layout(constant_id = 3) const uint sc_soft_shadow_samples = 4;
+layout(constant_id = 4) const uint sc_penumbra_shadow_samples = 4;
-/* Specialization Constants (Values) */
+layout(constant_id = 5) const uint sc_directional_soft_shadow_samples = 4;
+layout(constant_id = 6) const uint sc_directional_penumbra_shadow_samples = 4;
-layout(constant_id = 6) const uint sc_soft_shadow_samples = 4;
-layout(constant_id = 7) const uint sc_penumbra_shadow_samples = 4;
+layout(constant_id = 8) const bool sc_projector_use_mipmaps = true;
-layout(constant_id = 8) const uint sc_directional_soft_shadow_samples = 4;
-layout(constant_id = 9) const uint sc_directional_penumbra_shadow_samples = 4;
+layout(constant_id = 9) const bool sc_disable_omni_lights = false;
+layout(constant_id = 10) const bool sc_disable_spot_lights = false;
+layout(constant_id = 11) const bool sc_disable_reflection_probes = false;
+layout(constant_id = 12) const bool sc_disable_directional_lights = false;
-layout(constant_id = 10) const bool sc_decal_use_mipmaps = true;
-layout(constant_id = 11) const bool sc_projector_use_mipmaps = true;
+#endif //!MODE_UNSHADED
+
+layout(constant_id = 7) const bool sc_decal_use_mipmaps = true;
+layout(constant_id = 13) const bool sc_disable_decals = false;
+layout(constant_id = 14) const bool sc_disable_fog = false;
+
+#endif //!MODE_RENDER_DEPTH
/* Include our forward mobile UBOs definitions etc. */
#include "scene_forward_mobile_inc.glsl"
@@ -735,7 +747,7 @@ void main() {
// to maximize VGPR usage
// Draw "fixed" fog before volumetric fog to ensure volumetric fog can appear in front of the sky.
- if (scene_data.fog_enabled) {
+ if (!sc_disable_fog && scene_data.fog_enabled) {
fog = fog_process(vertex);
}
@@ -753,7 +765,7 @@ void main() {
vec3 vertex_ddx = dFdx(vertex);
vec3 vertex_ddy = dFdy(vertex);
- { //Decals
+ if (!sc_disable_decals) { //Decals
// must implement
uint decal_indices = draw_call.decals.x;
@@ -774,25 +786,35 @@ void main() {
continue; //out of decal
}
- //we need ddx/ddy for mipmaps, so simulate them
- vec2 ddx = (decals.data[decal_index].xform * vec4(vertex_ddx, 0.0)).xz;
- vec2 ddy = (decals.data[decal_index].xform * vec4(vertex_ddy, 0.0)).xz;
-
float fade = pow(1.0 - (uv_local.y > 0.0 ? uv_local.y : -uv_local.y), uv_local.y > 0.0 ? decals.data[decal_index].upper_fade : decals.data[decal_index].lower_fade);
if (decals.data[decal_index].normal_fade > 0.0) {
fade *= smoothstep(decals.data[decal_index].normal_fade, 1.0, dot(normal_interp, decals.data[decal_index].normal) * 0.5 + 0.5);
}
+ //we need ddx/ddy for mipmaps, so simulate them
+ vec2 ddx = (decals.data[decal_index].xform * vec4(vertex_ddx, 0.0)).xz;
+ vec2 ddy = (decals.data[decal_index].xform * vec4(vertex_ddy, 0.0)).xz;
+
if (decals.data[decal_index].albedo_rect != vec4(0.0)) {
//has albedo
- vec4 decal_albedo = textureGrad(sampler2D(decal_atlas_srgb, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), uv_local.xz * decals.data[decal_index].albedo_rect.zw + decals.data[decal_index].albedo_rect.xy, ddx * decals.data[decal_index].albedo_rect.zw, ddy * decals.data[decal_index].albedo_rect.zw);
+ vec4 decal_albedo;
+ if (sc_decal_use_mipmaps) {
+ decal_albedo = textureGrad(sampler2D(decal_atlas_srgb, decal_sampler), uv_local.xz * decals.data[decal_index].albedo_rect.zw + decals.data[decal_index].albedo_rect.xy, ddx * decals.data[decal_index].albedo_rect.zw, ddy * decals.data[decal_index].albedo_rect.zw);
+ } else {
+ decal_albedo = textureLod(sampler2D(decal_atlas_srgb, decal_sampler), uv_local.xz * decals.data[decal_index].albedo_rect.zw + decals.data[decal_index].albedo_rect.xy, 0.0);
+ }
decal_albedo *= decals.data[decal_index].modulate;
decal_albedo.a *= fade;
albedo = mix(albedo, decal_albedo.rgb, decal_albedo.a * decals.data[decal_index].albedo_mix);
if (decals.data[decal_index].normal_rect != vec4(0.0)) {
- vec3 decal_normal = textureGrad(sampler2D(decal_atlas, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), uv_local.xz * decals.data[decal_index].normal_rect.zw + decals.data[decal_index].normal_rect.xy, ddx * decals.data[decal_index].normal_rect.zw, ddy * decals.data[decal_index].normal_rect.zw).xyz;
+ vec3 decal_normal;
+ if (sc_decal_use_mipmaps) {
+ decal_normal = textureGrad(sampler2D(decal_atlas, decal_sampler), uv_local.xz * decals.data[decal_index].normal_rect.zw + decals.data[decal_index].normal_rect.xy, ddx * decals.data[decal_index].normal_rect.zw, ddy * decals.data[decal_index].normal_rect.zw).xyz;
+ } else {
+ decal_normal = textureLod(sampler2D(decal_atlas, decal_sampler), uv_local.xz * decals.data[decal_index].normal_rect.zw + decals.data[decal_index].normal_rect.xy, 0.0).xyz;
+ }
decal_normal.xy = decal_normal.xy * vec2(2.0, -2.0) - vec2(1.0, -1.0); //users prefer flipped y normal maps in most authoring software
decal_normal.z = sqrt(max(0.0, 1.0 - dot(decal_normal.xy, decal_normal.xy)));
//convert to view space, use xzy because y is up
@@ -802,7 +824,12 @@ void main() {
}
if (decals.data[decal_index].orm_rect != vec4(0.0)) {
- vec3 decal_orm = textureGrad(sampler2D(decal_atlas, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), uv_local.xz * decals.data[decal_index].orm_rect.zw + decals.data[decal_index].orm_rect.xy, ddx * decals.data[decal_index].orm_rect.zw, ddy * decals.data[decal_index].orm_rect.zw).xyz;
+ vec3 decal_orm;
+ if (sc_decal_use_mipmaps) {
+ decal_orm = textureGrad(sampler2D(decal_atlas, decal_sampler), uv_local.xz * decals.data[decal_index].orm_rect.zw + decals.data[decal_index].orm_rect.xy, ddx * decals.data[decal_index].orm_rect.zw, ddy * decals.data[decal_index].orm_rect.zw).xyz;
+ } else {
+ decal_orm = textureLod(sampler2D(decal_atlas, decal_sampler), uv_local.xz * decals.data[decal_index].orm_rect.zw + decals.data[decal_index].orm_rect.xy, 0.0).xyz;
+ }
ao = mix(ao, decal_orm.r, decal_albedo.a);
roughness = mix(roughness, decal_orm.g, decal_albedo.a);
metallic = mix(metallic, decal_orm.b, decal_albedo.a);
@@ -811,7 +838,11 @@ void main() {
if (decals.data[decal_index].emission_rect != vec4(0.0)) {
//emission is additive, so its independent from albedo
- emission += textureGrad(sampler2D(decal_atlas_srgb, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), uv_local.xz * decals.data[decal_index].emission_rect.zw + decals.data[decal_index].emission_rect.xy, ddx * decals.data[decal_index].emission_rect.zw, ddy * decals.data[decal_index].emission_rect.zw).xyz * decals.data[decal_index].emission_energy * fade;
+ if (sc_decal_use_mipmaps) {
+ emission += textureGrad(sampler2D(decal_atlas_srgb, decal_sampler), uv_local.xz * decals.data[decal_index].emission_rect.zw + decals.data[decal_index].emission_rect.xy, ddx * decals.data[decal_index].emission_rect.zw, ddy * decals.data[decal_index].emission_rect.zw).xyz * decals.data[decal_index].emission_energy * fade;
+ } else {
+ emission += textureLod(sampler2D(decal_atlas_srgb, decal_sampler), uv_local.xz * decals.data[decal_index].emission_rect.zw + decals.data[decal_index].emission_rect.xy, 0.0).xyz * decals.data[decal_index].emission_energy * fade;
+ }
}
}
} //Decals
@@ -951,7 +982,7 @@ void main() {
// skipping ssao, do we remove ssao totally?
- { //Reflection probes
+ if (!sc_disable_reflection_probes) { //Reflection probes
vec4 reflection_accum = vec4(0.0, 0.0, 0.0, 0.0);
vec4 ambient_accum = vec4(0.0, 0.0, 0.0, 0.0);
@@ -1017,7 +1048,7 @@ void main() {
// LIGHTING
#if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
- { //directional light
+ if (!sc_disable_directional_lights) { //directional light
// Do shadow and lighting in two passes to reduce register pressure
uint shadow0 = 0;
@@ -1347,7 +1378,7 @@ void main() {
}
} //directional light
- { //omni lights
+ if (!sc_disable_omni_lights) { //omni lights
uint light_indices = draw_call.omni_lights.x;
for (uint i = 0; i < 8; i++) {
uint light_index = light_indices & 0xFF;
@@ -1394,7 +1425,7 @@ void main() {
}
} //omni lights
- { //spot lights
+ if (!sc_disable_spot_lights) { //spot lights
uint light_indices = draw_call.spot_lights.x;
for (uint i = 0; i < 8; i++) {
diff --git a/tests/test_physics_2d.cpp b/tests/test_physics_2d.cpp
index 40dc74e89c..61b8951afa 100644
--- a/tests/test_physics_2d.cpp
+++ b/tests/test_physics_2d.cpp
@@ -255,7 +255,7 @@ protected:
arr.push_back(p_normal);
arr.push_back(p_d);
- RID plane = ps->line_shape_create();
+ RID plane = ps->world_margin_shape_create();
ps->shape_set_data(plane, arr);
RID plane_body = ps->body_create();
diff --git a/thirdparty/README.md b/thirdparty/README.md
index e5b4af627a..1c310eae05 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -602,7 +602,7 @@ Godot. Please check the file to know what's new.
## spirv-reflect
- Upstream: https://github.com/KhronosGroup/SPIRV-Reflect
-- Version: git (c0ce03a43ca77fedb5abfd1976ae2fd0eeb0e611, 2021)
+- Version: git (272e050728de8d4a4ce9e7101c1244e6ff56e5b0, 2021)
- License: Apache 2.0
Files extracted from upstream source:
diff --git a/thirdparty/spirv-reflect/patches/specialization-constants.patch b/thirdparty/spirv-reflect/patches/specialization-constants.patch
index 8ff1dcc2e5..efd89a76af 100644
--- a/thirdparty/spirv-reflect/patches/specialization-constants.patch
+++ b/thirdparty/spirv-reflect/patches/specialization-constants.patch
@@ -1,18 +1,18 @@
diff --git a/thirdparty/spirv-reflect/spirv_reflect.c b/thirdparty/spirv-reflect/spirv_reflect.c
-index 0fc979a8a4..3e3643717a 100644
+index 1c94a2e00e..2786a7f3ad 100644
--- a/thirdparty/spirv-reflect/spirv_reflect.c
+++ b/thirdparty/spirv-reflect/spirv_reflect.c
-@@ -124,6 +124,9 @@ typedef struct Decorations {
- NumberDecoration location;
- NumberDecoration offset;
- NumberDecoration uav_counter_buffer;
+@@ -124,6 +124,9 @@ typedef struct SpvReflectPrvDecorations {
+ SpvReflectPrvNumberDecoration location;
+ SpvReflectPrvNumberDecoration offset;
+ SpvReflectPrvNumberDecoration uav_counter_buffer;
+// -- GODOT begin --
-+ NumberDecoration specialization_constant;
++ SpvReflectPrvNumberDecoration specialization_constant;
+// -- GODOT end --
- StringDecoration semantic;
- uint32_t array_stride;
- uint32_t matrix_stride;
-@@ -610,6 +613,9 @@ static SpvReflectResult ParseNodes(Parser* p_parser)
+ SpvReflectPrvStringDecoration semantic;
+ uint32_t array_stride;
+ uint32_t matrix_stride;
+@@ -629,6 +632,9 @@ static SpvReflectResult ParseNodes(SpvReflectPrvParser* p_parser)
p_parser->nodes[i].decorations.offset.value = (uint32_t)INVALID_VALUE;
p_parser->nodes[i].decorations.uav_counter_buffer.value = (uint32_t)INVALID_VALUE;
p_parser->nodes[i].decorations.built_in = (SpvBuiltIn)INVALID_VALUE;
@@ -22,7 +22,7 @@ index 0fc979a8a4..3e3643717a 100644
}
// Mark source file id node
p_parser->source_file_id = (uint32_t)INVALID_VALUE;
-@@ -800,10 +806,16 @@ static SpvReflectResult ParseNodes(Parser* p_parser)
+@@ -819,10 +825,16 @@ static SpvReflectResult ParseNodes(SpvReflectPrvParser* p_parser)
CHECKED_READU32(p_parser, p_node->word_offset + 2, p_node->result_id);
}
break;
@@ -41,7 +41,16 @@ index 0fc979a8a4..3e3643717a 100644
case SpvOpSpecConstantComposite:
case SpvOpSpecConstantOp: {
CHECKED_READU32(p_parser, p_node->word_offset + 1, p_node->result_type_id);
-@@ -1309,6 +1321,9 @@ static SpvReflectResult ParseDecorations(Parser* p_parser)
+@@ -854,7 +866,7 @@ static SpvReflectResult ParseNodes(SpvReflectPrvParser* p_parser)
+ CHECKED_READU32(p_parser, p_node->word_offset + 3, p_access_chain->base_id);
+ //
+ // SPIRV_ACCESS_CHAIN_INDEX_OFFSET (4) is the number of words up until the first index:
+- // [Node, Result Type Id, Result Id, Base Id, <Indexes>]
++ // [SpvReflectPrvNode, Result Type Id, Result Id, Base Id, <Indexes>]
+ //
+ p_access_chain->index_count = (node_word_count - SPIRV_ACCESS_CHAIN_INDEX_OFFSET);
+ if (p_access_chain->index_count > 0) {
+@@ -1334,6 +1346,9 @@ static SpvReflectResult ParseDecorations(SpvReflectPrvParser* p_parser)
skip = true;
}
break;
@@ -51,7 +60,7 @@ index 0fc979a8a4..3e3643717a 100644
case SpvDecorationBlock:
case SpvDecorationBufferBlock:
case SpvDecorationColMajor:
-@@ -1441,7 +1456,14 @@ static SpvReflectResult ParseDecorations(Parser* p_parser)
+@@ -1466,7 +1481,14 @@ static SpvReflectResult ParseDecorations(SpvReflectPrvParser* p_parser)
p_target_decorations->input_attachment_index.word_offset = word_offset;
}
break;
@@ -67,7 +76,7 @@ index 0fc979a8a4..3e3643717a 100644
case SpvReflectDecorationHlslCounterBufferGOOGLE: {
uint32_t word_offset = p_node->word_offset + member_offset+ 3;
CHECKED_READU32(p_parser, word_offset, p_target_decorations->uav_counter_buffer.value);
-@@ -1731,6 +1753,13 @@ static SpvReflectResult ParseType(
+@@ -1766,6 +1788,13 @@ static SpvReflectResult ParseType(
p_type->type_flags |= SPV_REFLECT_TYPE_FLAG_EXTERNAL_ACCELERATION_STRUCTURE;
}
break;
@@ -81,17 +90,17 @@ index 0fc979a8a4..3e3643717a 100644
}
if (result == SPV_REFLECT_RESULT_SUCCESS) {
-@@ -3187,6 +3216,69 @@ static SpvReflectResult ParseExecutionModes(Parser* p_parser, SpvReflectShaderMo
+@@ -3236,6 +3265,69 @@ static SpvReflectResult ParseExecutionModes(
return SPV_REFLECT_RESULT_SUCCESS;
}
+// -- GODOT begin --
-+static SpvReflectResult ParseSpecializationConstants(Parser* p_parser, SpvReflectShaderModule* p_module)
++static SpvReflectResult ParseSpecializationConstants(SpvReflectPrvParser* p_parser, SpvReflectShaderModule* p_module)
+{
+ p_module->specialization_constant_count = 0;
+ p_module->specialization_constants = NULL;
+ for (size_t i = 0; i < p_parser->node_count; ++i) {
-+ Node* p_node = &(p_parser->nodes[i]);
++ SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
+ if (p_node->op == SpvOpSpecConstantTrue || p_node->op == SpvOpSpecConstantFalse || p_node->op == SpvOpSpecConstant) {
+ p_module->specialization_constant_count++;
+ }
@@ -106,7 +115,7 @@ index 0fc979a8a4..3e3643717a 100644
+ uint32_t index = 0;
+
+ for (size_t i = 0; i < p_parser->node_count; ++i) {
-+ Node* p_node = &(p_parser->nodes[i]);
++ SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
+ switch(p_node->op) {
+ default: continue;
+ case SpvOpSpecConstantTrue: {
@@ -124,7 +133,7 @@ index 0fc979a8a4..3e3643717a 100644
+ IF_READU32(result, p_parser, p_node->word_offset + 1, element_type_id);
+ IF_READU32(result, p_parser, p_node->word_offset + 3, default_value);
+
-+ Node* p_next_node = FindNode(p_parser, element_type_id);
++ SpvReflectPrvNode* p_next_node = FindNode(p_parser, element_type_id);
+
+ if (p_next_node->op == SpvOpTypeInt) {
+ p_module->specialization_constants[index].constant_type = SPV_REFLECT_SPECIALIZATION_CONSTANT_INT;
@@ -148,10 +157,10 @@ index 0fc979a8a4..3e3643717a 100644
+}
+// -- GODOT end --
+
- static SpvReflectResult ParsePushConstantBlocks(Parser* p_parser, SpvReflectShaderModule* p_module)
- {
- for (size_t i = 0; i < p_parser->node_count; ++i) {
-@@ -3562,6 +3654,12 @@ SpvReflectResult spvReflectCreateShaderModule(
+ static SpvReflectResult ParsePushConstantBlocks(
+ SpvReflectPrvParser* p_parser,
+ SpvReflectShaderModule* p_module)
+@@ -3613,6 +3705,12 @@ SpvReflectResult spvReflectCreateShaderModule(
result = ParsePushConstantBlocks(&parser, p_module);
SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
}
@@ -164,7 +173,7 @@ index 0fc979a8a4..3e3643717a 100644
if (result == SPV_REFLECT_RESULT_SUCCESS) {
result = ParseEntryPoints(&parser, p_module);
SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
-@@ -3691,6 +3789,9 @@ void spvReflectDestroyShaderModule(SpvReflectShaderModule* p_module)
+@@ -3742,6 +3840,9 @@ void spvReflectDestroyShaderModule(SpvReflectShaderModule* p_module)
SafeFree(p_entry->used_push_constants);
}
SafeFree(p_module->entry_points);
@@ -174,14 +183,14 @@ index 0fc979a8a4..3e3643717a 100644
// Push constants
for (size_t i = 0; i < p_module->push_constant_block_count; ++i) {
-@@ -3959,6 +4060,38 @@ SpvReflectResult spvReflectEnumerateEntryPointInterfaceVariables(
+@@ -4010,6 +4111,38 @@ SpvReflectResult spvReflectEnumerateEntryPointInterfaceVariables(
return SPV_REFLECT_RESULT_SUCCESS;
}
+// -- GODOT begin --
+SpvReflectResult spvReflectEnumerateSpecializationConstants(
-+ const SpvReflectShaderModule* p_module,
-+ uint32_t* p_count,
++ const SpvReflectShaderModule* p_module,
++ uint32_t* p_count,
+ SpvReflectSpecializationConstant** pp_constants
+)
+{
@@ -214,7 +223,7 @@ index 0fc979a8a4..3e3643717a 100644
const SpvReflectShaderModule* p_module,
uint32_t* p_count,
diff --git a/thirdparty/spirv-reflect/spirv_reflect.h b/thirdparty/spirv-reflect/spirv_reflect.h
-index a5a956e9e8..21f8160770 100644
+index da05400973..50cc65222b 100644
--- a/thirdparty/spirv-reflect/spirv_reflect.h
+++ b/thirdparty/spirv-reflect/spirv_reflect.h
@@ -292,6 +292,28 @@ typedef struct SpvReflectTypeDescription {
@@ -247,9 +256,9 @@ index a5a956e9e8..21f8160770 100644
/*! @struct SpvReflectInterfaceVariable
@@ -439,6 +461,10 @@ typedef struct SpvReflectShaderModule {
- SpvReflectInterfaceVariable* interface_variables;
- uint32_t push_constant_block_count;
- SpvReflectBlockVariable* push_constant_blocks;
+ SpvReflectInterfaceVariable* interface_variables; // Uses value(s) from first entry point
+ uint32_t push_constant_block_count; // Uses value(s) from first entry point
+ SpvReflectBlockVariable* push_constant_blocks; // Uses value(s) from first entry point
+ // -- GODOT begin --
+ uint32_t specialization_constant_count;
+ SpvReflectSpecializationConstant* specialization_constants;
diff --git a/thirdparty/spirv-reflect/spirv_reflect.c b/thirdparty/spirv-reflect/spirv_reflect.c
index 8f614c8874..2786a7f3ad 100644
--- a/thirdparty/spirv-reflect/spirv_reflect.c
+++ b/thirdparty/spirv-reflect/spirv_reflect.c
@@ -76,153 +76,157 @@ enum {
// clang-format on
// clang-format off
-typedef struct ArrayTraits {
- uint32_t element_type_id;
- uint32_t length_id;
-} ArrayTraits;
+typedef struct SpvReflectPrvArrayTraits {
+ uint32_t element_type_id;
+ uint32_t length_id;
+} SpvReflectPrvArrayTraits;
// clang-format on
// clang-format off
-typedef struct ImageTraits {
- uint32_t sampled_type_id;
- SpvDim dim;
- uint32_t depth;
- uint32_t arrayed;
- uint32_t ms;
- uint32_t sampled;
- SpvImageFormat image_format;
-} ImageTraits;
+typedef struct SpvReflectPrvImageTraits {
+ uint32_t sampled_type_id;
+ SpvDim dim;
+ uint32_t depth;
+ uint32_t arrayed;
+ uint32_t ms;
+ uint32_t sampled;
+ SpvImageFormat image_format;
+} SpvReflectPrvImageTraits;
// clang-format on
// clang-format off
-typedef struct NumberDecoration {
- uint32_t word_offset;
- uint32_t value;
-} NumberDecoration;
+typedef struct SpvReflectPrvNumberDecoration {
+ uint32_t word_offset;
+ uint32_t value;
+} SpvReflectPrvNumberDecoration;
// clang-format on
// clang-format off
-typedef struct StringDecoration {
- uint32_t word_offset;
- const char* value;
-} StringDecoration;
+typedef struct SpvReflectPrvStringDecoration {
+ uint32_t word_offset;
+ const char* value;
+} SpvReflectPrvStringDecoration;
// clang-format on
// clang-format off
-typedef struct Decorations {
- bool is_block;
- bool is_buffer_block;
- bool is_row_major;
- bool is_column_major;
- bool is_built_in;
- bool is_noperspective;
- bool is_flat;
- bool is_non_writable;
- NumberDecoration set;
- NumberDecoration binding;
- NumberDecoration input_attachment_index;
- NumberDecoration location;
- NumberDecoration offset;
- NumberDecoration uav_counter_buffer;
+typedef struct SpvReflectPrvDecorations {
+ bool is_block;
+ bool is_buffer_block;
+ bool is_row_major;
+ bool is_column_major;
+ bool is_built_in;
+ bool is_noperspective;
+ bool is_flat;
+ bool is_non_writable;
+ SpvReflectPrvNumberDecoration set;
+ SpvReflectPrvNumberDecoration binding;
+ SpvReflectPrvNumberDecoration input_attachment_index;
+ SpvReflectPrvNumberDecoration location;
+ SpvReflectPrvNumberDecoration offset;
+ SpvReflectPrvNumberDecoration uav_counter_buffer;
// -- GODOT begin --
- NumberDecoration specialization_constant;
+ SpvReflectPrvNumberDecoration specialization_constant;
// -- GODOT end --
- StringDecoration semantic;
- uint32_t array_stride;
- uint32_t matrix_stride;
- SpvBuiltIn built_in;
-} Decorations;
+ SpvReflectPrvStringDecoration semantic;
+ uint32_t array_stride;
+ uint32_t matrix_stride;
+ SpvBuiltIn built_in;
+} SpvReflectPrvDecorations;
// clang-format on
// clang-format off
-typedef struct Node {
- uint32_t result_id;
- SpvOp op;
- uint32_t result_type_id;
- uint32_t type_id;
- SpvStorageClass storage_class;
- uint32_t word_offset;
- uint32_t word_count;
- bool is_type;
-
- ArrayTraits array_traits;
- ImageTraits image_traits;
- uint32_t image_type_id;
-
- const char* name;
- Decorations decorations;
- uint32_t member_count;
- const char** member_names;
- Decorations* member_decorations;
-} Node;
+typedef struct SpvReflectPrvNode {
+ uint32_t result_id;
+ SpvOp op;
+ uint32_t result_type_id;
+ uint32_t type_id;
+ SpvStorageClass storage_class;
+ uint32_t word_offset;
+ uint32_t word_count;
+ bool is_type;
+
+ SpvReflectPrvArrayTraits array_traits;
+ SpvReflectPrvImageTraits image_traits;
+ uint32_t image_type_id;
+
+ const char* name;
+ SpvReflectPrvDecorations decorations;
+ uint32_t member_count;
+ const char** member_names;
+ SpvReflectPrvDecorations* member_decorations;
+} SpvReflectPrvNode;
// clang-format on
// clang-format off
-typedef struct String {
- uint32_t result_id;
- const char* string;
-} String;
+typedef struct SpvReflectPrvString {
+ uint32_t result_id;
+ const char* string;
+} SpvReflectPrvString;
// clang-format on
// clang-format off
-typedef struct Function {
- uint32_t id;
- uint32_t callee_count;
- uint32_t* callees;
- struct Function** callee_ptrs;
- uint32_t accessed_ptr_count;
- uint32_t* accessed_ptrs;
-} Function;
+typedef struct SpvReflectPrvFunction {
+ uint32_t id;
+ uint32_t callee_count;
+ uint32_t* callees;
+ struct SpvReflectPrvFunction** callee_ptrs;
+ uint32_t accessed_ptr_count;
+ uint32_t* accessed_ptrs;
+} SpvReflectPrvFunction;
// clang-format on
// clang-format off
-typedef struct AccessChain {
- uint32_t result_id;
- uint32_t result_type_id;
+typedef struct SpvReflectPrvAccessChain {
+ uint32_t result_id;
+ uint32_t result_type_id;
//
// Pointing to the base of a composite object.
// Generally the id of descriptor block variable
- uint32_t base_id;
+ uint32_t base_id;
//
// From spec:
// The first index in Indexes will select the
// top-level member/element/component/element
// of the base composite
- uint32_t index_count;
- uint32_t* indexes;
-} AccessChain;
+ uint32_t index_count;
+ uint32_t* indexes;
+} SpvReflectPrvAccessChain;
// clang-format on
// clang-format off
-typedef struct Parser {
- size_t spirv_word_count;
- uint32_t* spirv_code;
- uint32_t string_count;
- String* strings;
- SpvSourceLanguage source_language;
- uint32_t source_language_version;
- uint32_t source_file_id;
- const char* source_embedded;
- size_t node_count;
- Node* nodes;
- uint32_t entry_point_count;
- uint32_t function_count;
- Function* functions;
- uint32_t access_chain_count;
- AccessChain* access_chains;
-
- uint32_t type_count;
- uint32_t descriptor_count;
- uint32_t push_constant_count;
-} Parser;
+typedef struct SpvReflectPrvParser {
+ size_t spirv_word_count;
+ uint32_t* spirv_code;
+ uint32_t string_count;
+ SpvReflectPrvString* strings;
+ SpvSourceLanguage source_language;
+ uint32_t source_language_version;
+ uint32_t source_file_id;
+ const char* source_embedded;
+ size_t node_count;
+ SpvReflectPrvNode* nodes;
+ uint32_t entry_point_count;
+ uint32_t function_count;
+ SpvReflectPrvFunction* functions;
+ uint32_t access_chain_count;
+ SpvReflectPrvAccessChain* access_chains;
+
+ uint32_t type_count;
+ uint32_t descriptor_count;
+ uint32_t push_constant_count;
+} SpvReflectPrvParser;
// clang-format on
-static uint32_t Max(uint32_t a, uint32_t b)
+static uint32_t Max(
+ uint32_t a,
+ uint32_t b)
{
return a > b ? a : b;
}
-static uint32_t RoundUp(uint32_t value, uint32_t multiple)
+static uint32_t RoundUp(
+ uint32_t value,
+ uint32_t multiple)
{
assert(multiple && ((multiple & (multiple - 1)) == 0));
return (value + multiple - 1) & ~(multiple - 1);
@@ -242,7 +246,9 @@ static uint32_t RoundUp(uint32_t value, uint32_t multiple)
} \
}
-static int SortCompareUint32(const void* a, const void* b)
+static int SortCompareUint32(
+ const void* a,
+ const void* b)
{
const uint32_t* p_a = (const uint32_t*)a;
const uint32_t* p_b = (const uint32_t*)b;
@@ -272,7 +278,10 @@ static size_t DedupSortedUint32(uint32_t* arr, size_t size)
return dedup_idx+1;
}
-static bool SearchSortedUint32(const uint32_t* arr, size_t size, uint32_t target)
+static bool SearchSortedUint32(
+ const uint32_t* arr,
+ size_t size,
+ uint32_t target)
{
size_t lo = 0;
size_t hi = size;
@@ -341,7 +350,9 @@ static SpvReflectResult IntersectSortedUint32(
}
-static bool InRange(const Parser* p_parser, uint32_t index)
+static bool InRange(
+ const SpvReflectPrvParser* p_parser,
+ uint32_t index)
{
bool in_range = false;
if (IsNotNull(p_parser)) {
@@ -350,7 +361,10 @@ static bool InRange(const Parser* p_parser, uint32_t index)
return in_range;
}
-static SpvReflectResult ReadU32(Parser* p_parser, uint32_t word_offset, uint32_t* p_value)
+static SpvReflectResult ReadU32(
+ SpvReflectPrvParser* p_parser,
+ uint32_t word_offset,
+ uint32_t* p_value)
{
assert(IsNotNull(p_parser));
assert(IsNotNull(p_parser->spirv_code));
@@ -399,12 +413,12 @@ static SpvReflectResult ReadU32(Parser* p_parser, uint32_t word_offset, uint32_t
}
static SpvReflectResult ReadStr(
- Parser* p_parser,
- uint32_t word_offset,
- uint32_t word_index,
- uint32_t word_count,
- uint32_t* p_buf_size,
- char* p_buf
+ SpvReflectPrvParser* p_parser,
+ uint32_t word_offset,
+ uint32_t word_index,
+ uint32_t word_count,
+ uint32_t* p_buf_size,
+ char* p_buf
)
{
uint32_t limit = (word_offset + word_count);
@@ -445,7 +459,7 @@ static SpvReflectResult ReadStr(
return result;
}
-static SpvReflectDecorationFlags ApplyDecorations(const Decorations* p_decoration_fields)
+static SpvReflectDecorationFlags ApplyDecorations(const SpvReflectPrvDecorations* p_decoration_fields)
{
SpvReflectDecorationFlags decorations = SPV_REFLECT_DECORATION_NONE;
if (p_decoration_fields->is_block) {
@@ -485,11 +499,13 @@ static void ApplyArrayTraits(const SpvReflectTypeDescription* p_type, SpvReflect
memcpy(p_array_traits, &p_type->traits.array, sizeof(p_type->traits.array));
}
-static Node* FindNode(Parser* p_parser, uint32_t result_id)
+static SpvReflectPrvNode* FindNode(
+ SpvReflectPrvParser* p_parser,
+ uint32_t result_id)
{
- Node* p_node = NULL;
+ SpvReflectPrvNode* p_node = NULL;
for (size_t i = 0; i < p_parser->node_count; ++i) {
- Node* p_elem = &(p_parser->nodes[i]);
+ SpvReflectPrvNode* p_elem = &(p_parser->nodes[i]);
if (p_elem->result_id == result_id) {
p_node = p_elem;
break;
@@ -511,7 +527,10 @@ static SpvReflectTypeDescription* FindType(SpvReflectShaderModule* p_module, uin
return p_type;
}
-static SpvReflectResult CreateParser(size_t size, void* p_code, Parser* p_parser)
+static SpvReflectResult CreateParser(
+ size_t size,
+ void* p_code,
+ SpvReflectPrvParser* p_parser)
{
if (p_code == NULL) {
return SPV_REFLECT_RESULT_ERROR_NULL_POINTER;
@@ -534,12 +553,12 @@ static SpvReflectResult CreateParser(size_t size, void* p_code, Parser* p_parser
return SPV_REFLECT_RESULT_SUCCESS;
}
-static void DestroyParser(Parser* p_parser)
+static void DestroyParser(SpvReflectPrvParser* p_parser)
{
if (!IsNull(p_parser->nodes)) {
// Free nodes
for (size_t i = 0; i < p_parser->node_count; ++i) {
- Node* p_node = &(p_parser->nodes[i]);
+ SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
if (IsNotNull(p_node->member_names)) {
SafeFree(p_node->member_names);
}
@@ -569,7 +588,7 @@ static void DestroyParser(Parser* p_parser)
}
}
-static SpvReflectResult ParseNodes(Parser* p_parser)
+static SpvReflectResult ParseNodes(SpvReflectPrvParser* p_parser)
{
assert(IsNotNull(p_parser));
assert(IsNotNull(p_parser->spirv_code));
@@ -599,7 +618,7 @@ static SpvReflectResult ParseNodes(Parser* p_parser)
// Allocate nodes
p_parser->node_count = node_count;
- p_parser->nodes = (Node*)calloc(p_parser->node_count, sizeof(*(p_parser->nodes)));
+ p_parser->nodes = (SpvReflectPrvNode*)calloc(p_parser->node_count, sizeof(*(p_parser->nodes)));
if (IsNull(p_parser->nodes)) {
return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
}
@@ -626,7 +645,7 @@ static SpvReflectResult ParseNodes(Parser* p_parser)
// Allocate access chain
if (p_parser->access_chain_count > 0) {
- p_parser->access_chains = (AccessChain*)calloc(p_parser->access_chain_count, sizeof(*(p_parser->access_chains)));
+ p_parser->access_chains = (SpvReflectPrvAccessChain*)calloc(p_parser->access_chain_count, sizeof(*(p_parser->access_chains)));
if (IsNull(p_parser->access_chains)) {
return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
}
@@ -641,7 +660,7 @@ static SpvReflectResult ParseNodes(Parser* p_parser)
SpvOp op = (SpvOp)(word & 0xFFFF);
uint32_t node_word_count = (word >> 16) & 0xFFFF;
- Node* p_node = &(p_parser->nodes[node_index]);
+ SpvReflectPrvNode* p_node = &(p_parser->nodes[node_index]);
p_node->op = op;
p_node->word_offset = spirv_word_index;
p_node->word_count = node_word_count;
@@ -841,13 +860,13 @@ static SpvReflectResult ParseNodes(Parser* p_parser)
case SpvOpAccessChain:
{
- AccessChain* p_access_chain = &(p_parser->access_chains[access_chain_index]);
+ SpvReflectPrvAccessChain* p_access_chain = &(p_parser->access_chains[access_chain_index]);
CHECKED_READU32(p_parser, p_node->word_offset + 1, p_access_chain->result_type_id);
CHECKED_READU32(p_parser, p_node->word_offset + 2, p_access_chain->result_id);
CHECKED_READU32(p_parser, p_node->word_offset + 3, p_access_chain->base_id);
//
// SPIRV_ACCESS_CHAIN_INDEX_OFFSET (4) is the number of words up until the first index:
- // [Node, Result Type Id, Result Id, Base Id, <Indexes>]
+ // [SpvReflectPrvNode, Result Type Id, Result Id, Base Id, <Indexes>]
//
p_access_chain->index_count = (node_word_count - SPIRV_ACCESS_CHAIN_INDEX_OFFSET);
if (p_access_chain->index_count > 0) {
@@ -861,7 +880,7 @@ static SpvReflectResult ParseNodes(Parser* p_parser)
uint32_t index_id = 0;
CHECKED_READU32(p_parser, p_node->word_offset + SPIRV_ACCESS_CHAIN_INDEX_OFFSET + index_index, index_id);
// Find OpConstant node that contains index value
- Node* p_index_value_node = FindNode(p_parser, index_id);
+ SpvReflectPrvNode* p_index_value_node = FindNode(p_parser, index_id);
if ((p_index_value_node != NULL) && (p_index_value_node->op == SpvOpConstant)) {
// Read index value
uint32_t index_value = UINT32_MAX;
@@ -890,7 +909,7 @@ static SpvReflectResult ParseNodes(Parser* p_parser)
case SpvOpLabel:
{
if (function_node != (uint32_t)INVALID_VALUE) {
- Node* p_func_node = &(p_parser->nodes[function_node]);
+ SpvReflectPrvNode* p_func_node = &(p_parser->nodes[function_node]);
CHECKED_READU32(p_parser, p_func_node->word_offset + 2, p_func_node->result_id);
++(p_parser->function_count);
}
@@ -914,7 +933,7 @@ static SpvReflectResult ParseNodes(Parser* p_parser)
return SPV_REFLECT_RESULT_SUCCESS;
}
-static SpvReflectResult ParseStrings(Parser* p_parser)
+static SpvReflectResult ParseStrings(SpvReflectPrvParser* p_parser)
{
assert(IsNotNull(p_parser));
assert(IsNotNull(p_parser->spirv_code));
@@ -927,11 +946,11 @@ static SpvReflectResult ParseStrings(Parser* p_parser)
if (IsNotNull(p_parser) && IsNotNull(p_parser->spirv_code) && IsNotNull(p_parser->nodes)) {
// Allocate string storage
- p_parser->strings = (String*)calloc(p_parser->string_count, sizeof(*(p_parser->strings)));
+ p_parser->strings = (SpvReflectPrvString*)calloc(p_parser->string_count, sizeof(*(p_parser->strings)));
uint32_t string_index = 0;
for (size_t i = 0; i < p_parser->node_count; ++i) {
- Node* p_node = &(p_parser->nodes[i]);
+ SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
if (p_node->op != SpvOpString) {
continue;
}
@@ -943,7 +962,7 @@ static SpvReflectResult ParseStrings(Parser* p_parser)
}
// Result id
- String* p_string = &(p_parser->strings[string_index]);
+ SpvReflectPrvString* p_string = &(p_parser->strings[string_index]);
CHECKED_READU32(p_parser, p_node->word_offset + 1, p_string->result_id);
// String
@@ -958,7 +977,7 @@ static SpvReflectResult ParseStrings(Parser* p_parser)
return SPV_REFLECT_RESULT_SUCCESS;
}
-static SpvReflectResult ParseSource(Parser* p_parser, SpvReflectShaderModule* p_module)
+static SpvReflectResult ParseSource(SpvReflectPrvParser* p_parser, SpvReflectShaderModule* p_module)
{
assert(IsNotNull(p_parser));
assert(IsNotNull(p_parser->spirv_code));
@@ -967,7 +986,7 @@ static SpvReflectResult ParseSource(Parser* p_parser, SpvReflectShaderModule* p_
// Source file
if (IsNotNull(p_parser->strings)) {
for (uint32_t i = 0; i < p_parser->string_count; ++i) {
- String* p_string = &(p_parser->strings[i]);
+ SpvReflectPrvString* p_string = &(p_parser->strings[i]);
if (p_string->result_id == p_parser->source_file_id) {
p_module->source_file = p_string->string;
break;
@@ -998,7 +1017,11 @@ static SpvReflectResult ParseSource(Parser* p_parser, SpvReflectShaderModule* p_
return SPV_REFLECT_RESULT_SUCCESS;
}
-static SpvReflectResult ParseFunction(Parser* p_parser, Node* p_func_node, Function* p_func, size_t first_label_index)
+static SpvReflectResult ParseFunction(
+ SpvReflectPrvParser* p_parser,
+ SpvReflectPrvNode* p_func_node,
+ SpvReflectPrvFunction* p_func,
+ size_t first_label_index)
{
p_func->id = p_func_node->result_id;
@@ -1006,7 +1029,7 @@ static SpvReflectResult ParseFunction(Parser* p_parser, Node* p_func_node, Funct
p_func->accessed_ptr_count = 0;
for (size_t i = first_label_index; i < p_parser->node_count; ++i) {
- Node* p_node = &(p_parser->nodes[i]);
+ SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
if (p_node->op == SpvOpFunctionEnd) {
break;
}
@@ -1056,7 +1079,7 @@ static SpvReflectResult ParseFunction(Parser* p_parser, Node* p_func_node, Funct
p_func->callee_count = 0;
p_func->accessed_ptr_count = 0;
for (size_t i = first_label_index; i < p_parser->node_count; ++i) {
- Node* p_node = &(p_parser->nodes[i]);
+ SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
if (p_node->op == SpvOpFunctionEnd) {
break;
}
@@ -1119,14 +1142,16 @@ static SpvReflectResult ParseFunction(Parser* p_parser, Node* p_func_node, Funct
return SPV_REFLECT_RESULT_SUCCESS;
}
-static int SortCompareFunctions(const void* a, const void* b)
+static int SortCompareFunctions(
+ const void* a,
+ const void* b)
{
- const Function* af = (const Function*)a;
- const Function* bf = (const Function*)b;
+ const SpvReflectPrvFunction* af = (const SpvReflectPrvFunction*)a;
+ const SpvReflectPrvFunction* bf = (const SpvReflectPrvFunction*)b;
return (int)af->id - (int)bf->id;
}
-static SpvReflectResult ParseFunctions(Parser* p_parser)
+static SpvReflectResult ParseFunctions(SpvReflectPrvParser* p_parser)
{
assert(IsNotNull(p_parser));
assert(IsNotNull(p_parser->spirv_code));
@@ -1137,15 +1162,15 @@ static SpvReflectResult ParseFunctions(Parser* p_parser)
return SPV_REFLECT_RESULT_SUCCESS;
}
- p_parser->functions = (Function*)calloc(p_parser->function_count,
- sizeof(*(p_parser->functions)));
+ p_parser->functions = (SpvReflectPrvFunction*)calloc(p_parser->function_count,
+ sizeof(*(p_parser->functions)));
if (IsNull(p_parser->functions)) {
return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
}
size_t function_index = 0;
for (size_t i = 0; i < p_parser->node_count; ++i) {
- Node* p_node = &(p_parser->nodes[i]);
+ SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
if (p_node->op != SpvOpFunction) {
continue;
}
@@ -1167,7 +1192,7 @@ static SpvReflectResult ParseFunctions(Parser* p_parser)
continue;
}
- Function* p_function = &(p_parser->functions[function_index]);
+ SpvReflectPrvFunction* p_function = &(p_parser->functions[function_index]);
SpvReflectResult result = ParseFunction(p_parser, p_node, p_function, i);
if (result != SPV_REFLECT_RESULT_SUCCESS) {
@@ -1183,12 +1208,12 @@ static SpvReflectResult ParseFunctions(Parser* p_parser)
// Once they're sorted, link the functions with pointers to improve graph
// traversal efficiency
for (size_t i = 0; i < p_parser->function_count; ++i) {
- Function* p_func = &(p_parser->functions[i]);
+ SpvReflectPrvFunction* p_func = &(p_parser->functions[i]);
if (p_func->callee_count == 0) {
continue;
}
- p_func->callee_ptrs = (Function**)calloc(p_func->callee_count,
- sizeof(*(p_func->callee_ptrs)));
+ p_func->callee_ptrs = (SpvReflectPrvFunction**)calloc(p_func->callee_count,
+ sizeof(*(p_func->callee_ptrs)));
for (size_t j = 0, k = 0; j < p_func->callee_count; ++j) {
while (p_parser->functions[k].id != p_func->callees[j]) {
++k;
@@ -1205,7 +1230,7 @@ static SpvReflectResult ParseFunctions(Parser* p_parser)
return SPV_REFLECT_RESULT_SUCCESS;
}
-static SpvReflectResult ParseMemberCounts(Parser* p_parser)
+static SpvReflectResult ParseMemberCounts(SpvReflectPrvParser* p_parser)
{
assert(IsNotNull(p_parser));
assert(IsNotNull(p_parser->spirv_code));
@@ -1213,7 +1238,7 @@ static SpvReflectResult ParseMemberCounts(Parser* p_parser)
if (IsNotNull(p_parser) && IsNotNull(p_parser->spirv_code) && IsNotNull(p_parser->nodes)) {
for (size_t i = 0; i < p_parser->node_count; ++i) {
- Node* p_node = &(p_parser->nodes[i]);
+ SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
if ((p_node->op != SpvOpMemberName) && (p_node->op != SpvOpMemberDecorate)) {
continue;
}
@@ -1222,7 +1247,7 @@ static SpvReflectResult ParseMemberCounts(Parser* p_parser)
uint32_t member_index = (uint32_t)INVALID_VALUE;
CHECKED_READU32(p_parser, p_node->word_offset + 1, target_id);
CHECKED_READU32(p_parser, p_node->word_offset + 2, member_index);
- Node* p_target_node = FindNode(p_parser, target_id);
+ SpvReflectPrvNode* p_target_node = FindNode(p_parser, target_id);
// Not all nodes get parsed, so FindNode returning NULL is expected.
if (IsNull(p_target_node)) {
continue;
@@ -1236,7 +1261,7 @@ static SpvReflectResult ParseMemberCounts(Parser* p_parser)
}
for (uint32_t i = 0; i < p_parser->node_count; ++i) {
- Node* p_node = &(p_parser->nodes[i]);
+ SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
if (p_node->member_count == 0) {
continue;
}
@@ -1246,7 +1271,7 @@ static SpvReflectResult ParseMemberCounts(Parser* p_parser)
return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
}
- p_node->member_decorations = (Decorations*)calloc(p_node->member_count, sizeof(*(p_node->member_decorations)));
+ p_node->member_decorations = (SpvReflectPrvDecorations*)calloc(p_node->member_count, sizeof(*(p_node->member_decorations)));
if (IsNull(p_node->member_decorations)) {
return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
}
@@ -1255,7 +1280,7 @@ static SpvReflectResult ParseMemberCounts(Parser* p_parser)
return SPV_REFLECT_RESULT_SUCCESS;
}
-static SpvReflectResult ParseNames(Parser* p_parser)
+static SpvReflectResult ParseNames(SpvReflectPrvParser* p_parser)
{
assert(IsNotNull(p_parser));
assert(IsNotNull(p_parser->spirv_code));
@@ -1263,14 +1288,14 @@ static SpvReflectResult ParseNames(Parser* p_parser)
if (IsNotNull(p_parser) && IsNotNull(p_parser->spirv_code) && IsNotNull(p_parser->nodes)) {
for (size_t i = 0; i < p_parser->node_count; ++i) {
- Node* p_node = &(p_parser->nodes[i]);
+ SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
if ((p_node->op != SpvOpName) && (p_node->op != SpvOpMemberName)) {
continue;
}
uint32_t target_id = 0;
CHECKED_READU32(p_parser, p_node->word_offset + 1, target_id);
- Node* p_target_node = FindNode(p_parser, target_id);
+ SpvReflectPrvNode* p_target_node = FindNode(p_parser, target_id);
// Not all nodes get parsed, so FindNode returning NULL is expected.
if (IsNull(p_target_node)) {
continue;
@@ -1289,10 +1314,10 @@ static SpvReflectResult ParseNames(Parser* p_parser)
return SPV_REFLECT_RESULT_SUCCESS;
}
-static SpvReflectResult ParseDecorations(Parser* p_parser)
+static SpvReflectResult ParseDecorations(SpvReflectPrvParser* p_parser)
{
for (uint32_t i = 0; i < p_parser->node_count; ++i) {
- Node* p_node = &(p_parser->nodes[i]);
+ SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
if (((uint32_t)p_node->op != (uint32_t)SpvOpDecorate) &&
((uint32_t)p_node->op != (uint32_t)SpvOpMemberDecorate) &&
@@ -1352,12 +1377,12 @@ static SpvReflectResult ParseDecorations(Parser* p_parser)
// Find target target node
uint32_t target_id = 0;
CHECKED_READU32(p_parser, p_node->word_offset + 1, target_id);
- Node* p_target_node = FindNode(p_parser, target_id);
+ SpvReflectPrvNode* p_target_node = FindNode(p_parser, target_id);
if (IsNull(p_target_node)) {
return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
}
// Get decorations
- Decorations* p_target_decorations = &(p_target_node->decorations);
+ SpvReflectPrvDecorations* p_target_decorations = &(p_target_node->decorations);
// Update pointer if this is a member member decoration
if (p_node->op == SpvOpMemberDecorate) {
uint32_t member_index = (uint32_t)INVALID_VALUE;
@@ -1507,11 +1532,11 @@ static SpvReflectResult EnumerateAllUniforms(
}
static SpvReflectResult ParseType(
- Parser* p_parser,
- Node* p_node,
- Decorations* p_struct_member_decorations,
- SpvReflectShaderModule* p_module,
- SpvReflectTypeDescription* p_type
+ SpvReflectPrvParser* p_parser,
+ SpvReflectPrvNode* p_node,
+ SpvReflectPrvDecorations* p_struct_member_decorations,
+ SpvReflectShaderModule* p_module,
+ SpvReflectTypeDescription* p_type
)
{
SpvReflectResult result = SPV_REFLECT_RESULT_SUCCESS;
@@ -1574,7 +1599,7 @@ static SpvReflectResult ParseType(
IF_READU32(result, p_parser, p_node->word_offset + 2, component_type_id);
IF_READU32(result, p_parser, p_node->word_offset + 3, p_type->traits.numeric.vector.component_count);
// Parse component type
- Node* p_next_node = FindNode(p_parser, component_type_id);
+ SpvReflectPrvNode* p_next_node = FindNode(p_parser, component_type_id);
if (IsNotNull(p_next_node)) {
result = ParseType(p_parser, p_next_node, NULL, p_module, p_type);
}
@@ -1590,7 +1615,7 @@ static SpvReflectResult ParseType(
uint32_t column_type_id = (uint32_t)INVALID_VALUE;
IF_READU32(result, p_parser, p_node->word_offset + 2, column_type_id);
IF_READU32(result, p_parser, p_node->word_offset + 3, p_type->traits.numeric.matrix.column_count);
- Node* p_next_node = FindNode(p_parser, column_type_id);
+ SpvReflectPrvNode* p_next_node = FindNode(p_parser, column_type_id);
if (IsNotNull(p_next_node)) {
result = ParseType(p_parser, p_next_node, NULL, p_module, p_type);
}
@@ -1609,6 +1634,15 @@ static SpvReflectResult ParseType(
case SpvOpTypeImage: {
p_type->type_flags |= SPV_REFLECT_TYPE_FLAG_EXTERNAL_IMAGE;
+ uint32_t sampled_type_id = (uint32_t)INVALID_VALUE;
+ IF_READU32(result, p_parser, p_node->word_offset + 2, sampled_type_id);
+ SpvReflectPrvNode* p_next_node = FindNode(p_parser, sampled_type_id);
+ if (IsNotNull(p_next_node)) {
+ result = ParseType(p_parser, p_next_node, NULL, p_module, p_type);
+ }
+ else {
+ result = SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+ }
IF_READU32_CAST(result, p_parser, p_node->word_offset + 3, SpvDim, p_type->traits.image.dim);
IF_READU32(result, p_parser, p_node->word_offset + 4, p_type->traits.image.depth);
IF_READU32(result, p_parser, p_node->word_offset + 5, p_type->traits.image.arrayed);
@@ -1627,7 +1661,7 @@ static SpvReflectResult ParseType(
p_type->type_flags |= SPV_REFLECT_TYPE_FLAG_EXTERNAL_SAMPLED_IMAGE;
uint32_t image_type_id = (uint32_t)INVALID_VALUE;
IF_READU32(result, p_parser, p_node->word_offset + 2, image_type_id);
- Node* p_next_node = FindNode(p_parser, image_type_id);
+ SpvReflectPrvNode* p_next_node = FindNode(p_parser, image_type_id);
if (IsNotNull(p_next_node)) {
result = ParseType(p_parser, p_next_node, NULL, p_module, p_type);
}
@@ -1649,7 +1683,7 @@ static SpvReflectResult ParseType(
// OpMemberDecorate, even if the array is apart of a struct.
p_type->traits.array.stride = p_node->decorations.array_stride;
// Get length for current dimension
- Node* p_length_node = FindNode(p_parser, length_id);
+ SpvReflectPrvNode* p_length_node = FindNode(p_parser, length_id);
if (IsNotNull(p_length_node)) {
if (p_length_node->op == SpvOpSpecConstant ||
p_length_node->op == SpvOpSpecConstantOp) {
@@ -1668,7 +1702,7 @@ static SpvReflectResult ParseType(
}
}
// Parse next dimension or element type
- Node* p_next_node = FindNode(p_parser, element_type_id);
+ SpvReflectPrvNode* p_next_node = FindNode(p_parser, element_type_id);
if (IsNotNull(p_next_node)) {
result = ParseType(p_parser, p_next_node, NULL, p_module, p_type);
}
@@ -1682,10 +1716,11 @@ static SpvReflectResult ParseType(
break;
case SpvOpTypeRuntimeArray: {
+ p_type->type_flags |= SPV_REFLECT_TYPE_FLAG_ARRAY;
uint32_t element_type_id = (uint32_t)INVALID_VALUE;
IF_READU32(result, p_parser, p_node->word_offset + 2, element_type_id);
// Parse next dimension or element type
- Node* p_next_node = FindNode(p_parser, element_type_id);
+ SpvReflectPrvNode* p_next_node = FindNode(p_parser, element_type_id);
if (IsNotNull(p_next_node)) {
result = ParseType(p_parser, p_next_node, NULL, p_module, p_type);
}
@@ -1705,7 +1740,7 @@ static SpvReflectResult ParseType(
uint32_t member_id = (uint32_t)INVALID_VALUE;
IF_READU32(result, p_parser, p_node->word_offset + word_index, member_id);
// Find member node
- Node* p_member_node = FindNode(p_parser, member_id);
+ SpvReflectPrvNode* p_member_node = FindNode(p_parser, member_id);
if (IsNull(p_member_node)) {
result = SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
SPV_REFLECT_ASSERT(false);
@@ -1713,7 +1748,7 @@ static SpvReflectResult ParseType(
}
// Member decorations
- Decorations* p_member_decorations = &p_node->member_decorations[member_index];
+ SpvReflectPrvDecorations* p_member_decorations = &p_node->member_decorations[member_index];
assert(member_index < p_type->member_count);
// Parse member type
@@ -1738,7 +1773,7 @@ static SpvReflectResult ParseType(
uint32_t type_id = (uint32_t)INVALID_VALUE;
IF_READU32(result, p_parser, p_node->word_offset + 3, type_id);
// Parse type
- Node* p_next_node = FindNode(p_parser, type_id);
+ SpvReflectPrvNode* p_next_node = FindNode(p_parser, type_id);
if (IsNotNull(p_next_node)) {
result = ParseType(p_parser, p_next_node, NULL, p_module, p_type);
}
@@ -1774,7 +1809,9 @@ static SpvReflectResult ParseType(
return result;
}
-static SpvReflectResult ParseTypes(Parser* p_parser, SpvReflectShaderModule* p_module)
+static SpvReflectResult ParseTypes(
+ SpvReflectPrvParser* p_parser,
+ SpvReflectShaderModule* p_module)
{
if (p_parser->type_count == 0) {
return SPV_REFLECT_RESULT_SUCCESS;
@@ -1797,7 +1834,7 @@ static SpvReflectResult ParseTypes(Parser* p_parser, SpvReflectShaderModule* p_m
size_t type_index = 0;
for (size_t i = 0; i < p_parser->node_count; ++i) {
- Node* p_node = &(p_parser->nodes[i]);
+ SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
if (! p_node->is_type) {
continue;
}
@@ -1826,11 +1863,13 @@ static int SortCompareDescriptorBinding(const void* a, const void* b)
return value;
}
-static SpvReflectResult ParseDescriptorBindings(Parser* p_parser, SpvReflectShaderModule* p_module)
+static SpvReflectResult ParseDescriptorBindings(
+ SpvReflectPrvParser* p_parser,
+ SpvReflectShaderModule* p_module)
{
p_module->descriptor_binding_count = 0;
for (size_t i = 0; i < p_parser->node_count; ++i) {
- Node* p_node = &(p_parser->nodes[i]);
+ SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
if ((p_node->op != SpvOpVariable) ||
((p_node->storage_class != SpvStorageClassUniform) &&
(p_node->storage_class != SpvStorageClassStorageBuffer) &&
@@ -1866,7 +1905,7 @@ static SpvReflectResult ParseDescriptorBindings(Parser* p_parser, SpvReflectShad
size_t descriptor_index = 0;
for (size_t i = 0; i < p_parser->node_count; ++i) {
- Node* p_node = &(p_parser->nodes[i]);
+ SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
if ((p_node->op != SpvOpVariable) ||
((p_node->storage_class != SpvStorageClassUniform) &&
(p_node->storage_class != SpvStorageClassStorageBuffer) &&
@@ -1888,7 +1927,7 @@ static SpvReflectResult ParseDescriptorBindings(Parser* p_parser, SpvReflectShad
if (p_type->op == SpvOpTypePointer) {
pointer_storage_class = p_type->storage_class;
// Find the type's node
- Node* p_type_node = FindNode(p_parser, p_type->id);
+ SpvReflectPrvNode* p_type_node = FindNode(p_parser, p_type->id);
if (IsNull(p_type_node)) {
return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
}
@@ -2116,7 +2155,7 @@ static SpvReflectResult ParseUAVCounterBindings(SpvReflectShaderModule* p_module
}
static SpvReflectResult ParseDescriptorBlockVariable(
- Parser* p_parser,
+ SpvReflectPrvParser* p_parser,
SpvReflectShaderModule* p_module,
SpvReflectTypeDescription* p_type,
SpvReflectBlockVariable* p_var
@@ -2131,7 +2170,7 @@ static SpvReflectResult ParseDescriptorBlockVariable(
return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
}
- Node* p_type_node = FindNode(p_parser, p_type->id);
+ SpvReflectPrvNode* p_type_node = FindNode(p_parser, p_type->id);
if (IsNull(p_type_node)) {
return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
}
@@ -2195,12 +2234,12 @@ static SpvReflectResult ParseDescriptorBlockVariable(
}
static SpvReflectResult ParseDescriptorBlockVariableSizes(
- Parser* p_parser,
- SpvReflectShaderModule* p_module,
- bool is_parent_root,
- bool is_parent_aos,
- bool is_parent_rta,
- SpvReflectBlockVariable* p_var
+ SpvReflectPrvParser* p_parser,
+ SpvReflectShaderModule* p_module,
+ bool is_parent_root,
+ bool is_parent_aos,
+ bool is_parent_rta,
+ SpvReflectBlockVariable* p_var
)
{
if (p_var->member_count == 0) {
@@ -2351,12 +2390,12 @@ static void MarkSelfAndAllMemberVarsAsUsed(SpvReflectBlockVariable* p_var)
}
static SpvReflectResult ParseDescriptorBlockVariableUsage(
- Parser* p_parser,
- SpvReflectShaderModule* p_module,
- AccessChain* p_access_chain,
- uint32_t index_index,
- SpvOp override_op_type,
- SpvReflectBlockVariable* p_var
+ SpvReflectPrvParser* p_parser,
+ SpvReflectShaderModule* p_module,
+ SpvReflectPrvAccessChain* p_access_chain,
+ uint32_t index_index,
+ SpvOp override_op_type,
+ SpvReflectBlockVariable* p_var
)
{
(void)p_parser;
@@ -2381,7 +2420,7 @@ static SpvReflectResult ParseDescriptorBlockVariableUsage(
SpvReflectTypeDescription* p_type = p_var->type_description;
while ((p_type->op == SpvOpTypeArray) && (index_index < p_access_chain->index_count)) {
// Find the array element type id
- Node* p_node = FindNode(p_parser, p_type->id);
+ SpvReflectPrvNode* p_node = FindNode(p_parser, p_type->id);
if (p_node == NULL) {
return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
}
@@ -2480,7 +2519,9 @@ static SpvReflectResult ParseDescriptorBlockVariableUsage(
return SPV_REFLECT_RESULT_SUCCESS;
}
-static SpvReflectResult ParseDescriptorBlocks(Parser* p_parser, SpvReflectShaderModule* p_module)
+static SpvReflectResult ParseDescriptorBlocks(
+ SpvReflectPrvParser* p_parser,
+ SpvReflectShaderModule* p_module)
{
if (p_module->descriptor_binding_count == 0) {
return SPV_REFLECT_RESULT_SUCCESS;
@@ -2504,7 +2545,7 @@ static SpvReflectResult ParseDescriptorBlocks(Parser* p_parser, SpvReflectShader
}
for (uint32_t access_chain_index = 0; access_chain_index < p_parser->access_chain_count; ++access_chain_index) {
- AccessChain* p_access_chain = &(p_parser->access_chains[access_chain_index]);
+ SpvReflectPrvAccessChain* p_access_chain = &(p_parser->access_chains[access_chain_index]);
// Skip any access chains that aren't touching this descriptor block
if (p_descriptor->spirv_id != p_access_chain->base_id) {
continue;
@@ -2557,6 +2598,8 @@ static SpvReflectResult ParseFormat(
case 4: *p_format = SPV_REFLECT_FORMAT_R32G32B32A32_SFLOAT; break;
}
}
+ break;
+
case 64: {
switch (component_count) {
case 2: *p_format = SPV_REFLECT_FORMAT_R64G64_SFLOAT; break;
@@ -2576,6 +2619,8 @@ static SpvReflectResult ParseFormat(
case 4: *p_format = signedness ? SPV_REFLECT_FORMAT_R32G32B32A32_SINT : SPV_REFLECT_FORMAT_R32G32B32A32_UINT; break;
}
}
+ break;
+
case 64: {
switch (component_count) {
case 2: *p_format = signedness ? SPV_REFLECT_FORMAT_R64G64_SINT : SPV_REFLECT_FORMAT_R64G64_UINT; break;
@@ -2616,15 +2661,15 @@ static SpvReflectResult ParseFormat(
}
static SpvReflectResult ParseInterfaceVariable(
- Parser* p_parser,
- const Decorations* p_type_node_decorations,
- SpvReflectShaderModule* p_module,
- SpvReflectTypeDescription* p_type,
- SpvReflectInterfaceVariable* p_var,
- bool* p_has_built_in
+ SpvReflectPrvParser* p_parser,
+ const SpvReflectPrvDecorations* p_type_node_decorations,
+ SpvReflectShaderModule* p_module,
+ SpvReflectTypeDescription* p_type,
+ SpvReflectInterfaceVariable* p_var,
+ bool* p_has_built_in
)
{
- Node* p_type_node = FindNode(p_parser, p_type->id);
+ SpvReflectPrvNode* p_type_node = FindNode(p_parser, p_type->id);
if (IsNull(p_type_node)) {
return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
}
@@ -2637,7 +2682,7 @@ static SpvReflectResult ParseInterfaceVariable(
}
for (uint32_t member_index = 0; member_index < p_type_node->member_count; ++member_index) {
- Decorations* p_member_decorations = &p_type_node->member_decorations[member_index];
+ SpvReflectPrvDecorations* p_member_decorations = &p_type_node->member_decorations[member_index];
SpvReflectTypeDescription* p_member_type = &p_type->members[member_index];
SpvReflectInterfaceVariable* p_member_var = &p_var->members[member_index];
SpvReflectResult result = ParseInterfaceVariable(p_parser, p_member_decorations, p_module, p_member_type, p_member_var, p_has_built_in);
@@ -2673,7 +2718,7 @@ static SpvReflectResult ParseInterfaceVariable(
}
static SpvReflectResult ParseInterfaceVariables(
- Parser* p_parser,
+ SpvReflectPrvParser* p_parser,
SpvReflectShaderModule* p_module,
SpvReflectEntryPoint* p_entry,
uint32_t interface_variable_count,
@@ -2689,7 +2734,7 @@ static SpvReflectResult ParseInterfaceVariables(
p_entry->output_variable_count = 0;
for (size_t i = 0; i < interface_variable_count; ++i) {
uint32_t var_result_id = *(p_interface_variable_ids + i);
- Node* p_node = FindNode(p_parser, var_result_id);
+ SpvReflectPrvNode* p_node = FindNode(p_parser, var_result_id);
if (IsNull(p_node)) {
return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
}
@@ -2727,7 +2772,7 @@ static SpvReflectResult ParseInterfaceVariables(
size_t output_index = 0;
for (size_t i = 0; i < interface_variable_count; ++i) {
uint32_t var_result_id = *(p_interface_variable_ids + i);
- Node* p_node = FindNode(p_parser, var_result_id);
+ SpvReflectPrvNode* p_node = FindNode(p_parser, var_result_id);
if (IsNull(p_node)) {
return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
}
@@ -2739,7 +2784,7 @@ static SpvReflectResult ParseInterfaceVariables(
// If the type is a pointer, resolve it
if (p_type->op == SpvOpTypePointer) {
// Find the type's node
- Node* p_type_node = FindNode(p_parser, p_type->id);
+ SpvReflectPrvNode* p_type_node = FindNode(p_parser, p_type->id);
if (IsNull(p_type_node)) {
return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
}
@@ -2750,7 +2795,7 @@ static SpvReflectResult ParseInterfaceVariables(
}
}
- Node* p_type_node = FindNode(p_parser, p_type->id);
+ SpvReflectPrvNode* p_type_node = FindNode(p_parser, p_type->id);
if (IsNull(p_type_node)) {
return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
}
@@ -2831,11 +2876,11 @@ static SpvReflectResult EnumerateAllPushConstants(
}
static SpvReflectResult TraverseCallGraph(
- Parser* p_parser,
- Function* p_func,
- size_t* p_func_count,
- uint32_t* p_func_ids,
- uint32_t depth
+ SpvReflectPrvParser* p_parser,
+ SpvReflectPrvFunction* p_func,
+ size_t* p_func_count,
+ uint32_t* p_func_ids,
+ uint32_t depth
)
{
if (depth > p_parser->function_count) {
@@ -2860,7 +2905,7 @@ static SpvReflectResult TraverseCallGraph(
}
static SpvReflectResult ParseStaticallyUsedResources(
- Parser* p_parser,
+ SpvReflectPrvParser* p_parser,
SpvReflectShaderModule* p_module,
SpvReflectEntryPoint* p_entry,
size_t uniform_count,
@@ -2870,7 +2915,7 @@ static SpvReflectResult ParseStaticallyUsedResources(
)
{
// Find function with the right id
- Function* p_func = NULL;
+ SpvReflectPrvFunction* p_func = NULL;
for (size_t i = 0; i < p_parser->function_count; ++i) {
if (p_parser->functions[i].id == p_entry->id) {
p_func = &(p_parser->functions[i]);
@@ -3004,7 +3049,9 @@ static SpvReflectResult ParseStaticallyUsedResources(
return SPV_REFLECT_RESULT_SUCCESS;
}
-static SpvReflectResult ParseEntryPoints(Parser* p_parser, SpvReflectShaderModule* p_module)
+static SpvReflectResult ParseEntryPoints(
+ SpvReflectPrvParser* p_parser,
+ SpvReflectShaderModule* p_module)
{
if (p_parser->entry_point_count == 0) {
return SPV_REFLECT_RESULT_SUCCESS;
@@ -3033,7 +3080,7 @@ static SpvReflectResult ParseEntryPoints(Parser* p_parser, SpvReflectShaderModul
size_t entry_point_index = 0;
for (size_t i = 0; entry_point_index < p_parser->entry_point_count && i < p_parser->node_count; ++i) {
- Node* p_node = &(p_parser->nodes[i]);
+ SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
if (p_node->op != SpvOpEntryPoint) {
continue;
}
@@ -3118,7 +3165,9 @@ static SpvReflectResult ParseEntryPoints(Parser* p_parser, SpvReflectShaderModul
return SPV_REFLECT_RESULT_SUCCESS;
}
-static SpvReflectResult ParseExecutionModes(Parser* p_parser, SpvReflectShaderModule* p_module)
+static SpvReflectResult ParseExecutionModes(
+ SpvReflectPrvParser* p_parser,
+ SpvReflectShaderModule* p_module)
{
assert(IsNotNull(p_parser));
assert(IsNotNull(p_parser->nodes));
@@ -3126,7 +3175,7 @@ static SpvReflectResult ParseExecutionModes(Parser* p_parser, SpvReflectShaderMo
if (IsNotNull(p_parser) && IsNotNull(p_parser->spirv_code) && IsNotNull(p_parser->nodes)) {
for (size_t node_idx = 0; node_idx < p_parser->node_count; ++node_idx) {
- Node* p_node = &(p_parser->nodes[node_idx]);
+ SpvReflectPrvNode* p_node = &(p_parser->nodes[node_idx]);
if (p_node->op != SpvOpExecutionMode) {
continue;
}
@@ -3217,12 +3266,12 @@ static SpvReflectResult ParseExecutionModes(Parser* p_parser, SpvReflectShaderMo
}
// -- GODOT begin --
-static SpvReflectResult ParseSpecializationConstants(Parser* p_parser, SpvReflectShaderModule* p_module)
+static SpvReflectResult ParseSpecializationConstants(SpvReflectPrvParser* p_parser, SpvReflectShaderModule* p_module)
{
p_module->specialization_constant_count = 0;
p_module->specialization_constants = NULL;
for (size_t i = 0; i < p_parser->node_count; ++i) {
- Node* p_node = &(p_parser->nodes[i]);
+ SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
if (p_node->op == SpvOpSpecConstantTrue || p_node->op == SpvOpSpecConstantFalse || p_node->op == SpvOpSpecConstant) {
p_module->specialization_constant_count++;
}
@@ -3237,7 +3286,7 @@ static SpvReflectResult ParseSpecializationConstants(Parser* p_parser, SpvReflec
uint32_t index = 0;
for (size_t i = 0; i < p_parser->node_count; ++i) {
- Node* p_node = &(p_parser->nodes[i]);
+ SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
switch(p_node->op) {
default: continue;
case SpvOpSpecConstantTrue: {
@@ -3255,7 +3304,7 @@ static SpvReflectResult ParseSpecializationConstants(Parser* p_parser, SpvReflec
IF_READU32(result, p_parser, p_node->word_offset + 1, element_type_id);
IF_READU32(result, p_parser, p_node->word_offset + 3, default_value);
- Node* p_next_node = FindNode(p_parser, element_type_id);
+ SpvReflectPrvNode* p_next_node = FindNode(p_parser, element_type_id);
if (p_next_node->op == SpvOpTypeInt) {
p_module->specialization_constants[index].constant_type = SPV_REFLECT_SPECIALIZATION_CONSTANT_INT;
@@ -3279,10 +3328,12 @@ static SpvReflectResult ParseSpecializationConstants(Parser* p_parser, SpvReflec
}
// -- GODOT end --
-static SpvReflectResult ParsePushConstantBlocks(Parser* p_parser, SpvReflectShaderModule* p_module)
+static SpvReflectResult ParsePushConstantBlocks(
+ SpvReflectPrvParser* p_parser,
+ SpvReflectShaderModule* p_module)
{
for (size_t i = 0; i < p_parser->node_count; ++i) {
- Node* p_node = &(p_parser->nodes[i]);
+ SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
if ((p_node->op != SpvOpVariable) || (p_node->storage_class != SpvStorageClassPushConstant)) {
continue;
}
@@ -3301,7 +3352,7 @@ static SpvReflectResult ParsePushConstantBlocks(Parser* p_parser, SpvReflectShad
uint32_t push_constant_index = 0;
for (size_t i = 0; i < p_parser->node_count; ++i) {
- Node* p_node = &(p_parser->nodes[i]);
+ SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
if ((p_node->op != SpvOpVariable) || (p_node->storage_class != SpvStorageClassPushConstant)) {
continue;
}
@@ -3313,7 +3364,7 @@ static SpvReflectResult ParsePushConstantBlocks(Parser* p_parser, SpvReflectShad
// If the type is a pointer, resolve it
if (p_type->op == SpvOpTypePointer) {
// Find the type's node
- Node* p_type_node = FindNode(p_parser, p_type->id);
+ SpvReflectPrvNode* p_type_node = FindNode(p_parser, p_type->id);
if (IsNull(p_type_node)) {
return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
}
@@ -3324,7 +3375,7 @@ static SpvReflectResult ParsePushConstantBlocks(Parser* p_parser, SpvReflectShad
}
}
- Node* p_type_node = FindNode(p_parser, p_type->id);
+ SpvReflectPrvNode* p_type_node = FindNode(p_parser, p_type->id);
if (IsNull(p_type_node)) {
return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
}
@@ -3577,7 +3628,7 @@ SpvReflectResult spvReflectCreateShaderModule(
}
memcpy(p_module->_internal->spirv_code, p_code, size);
- Parser parser = { 0 };
+ SpvReflectPrvParser parser = { 0 };
SpvReflectResult result = CreateParser(p_module->_internal->spirv_size,
p_module->_internal->spirv_code,
&parser);
@@ -4062,8 +4113,8 @@ SpvReflectResult spvReflectEnumerateEntryPointInterfaceVariables(
// -- GODOT begin --
SpvReflectResult spvReflectEnumerateSpecializationConstants(
- const SpvReflectShaderModule* p_module,
- uint32_t* p_count,
+ const SpvReflectShaderModule* p_module,
+ uint32_t* p_count,
SpvReflectSpecializationConstant** pp_constants
)
{
diff --git a/thirdparty/spirv-reflect/spirv_reflect.h b/thirdparty/spirv-reflect/spirv_reflect.h
index 21f8160770..50cc65222b 100644
--- a/thirdparty/spirv-reflect/spirv_reflect.h
+++ b/thirdparty/spirv-reflect/spirv_reflect.h
@@ -152,7 +152,7 @@ typedef enum SpvReflectFormat {
SPV_REFLECT_FORMAT_R64G64_SFLOAT = 115, // = VK_FORMAT_R64G64_SFLOAT
SPV_REFLECT_FORMAT_R64G64B64_UINT = 116, // = VK_FORMAT_R64G64B64_UINT
SPV_REFLECT_FORMAT_R64G64B64_SINT = 117, // = VK_FORMAT_R64G64B64_SINT
- SPV_REFLECT_FORMAT_R64G64B64_SFLOAT = 118, // = VK_FORMAT_R64G64B64_FLOAT
+ SPV_REFLECT_FORMAT_R64G64B64_SFLOAT = 118, // = VK_FORMAT_R64G64B64_SFLOAT
SPV_REFLECT_FORMAT_R64G64B64A64_UINT = 119, // = VK_FORMAT_R64G64B64A64_UINT
SPV_REFLECT_FORMAT_R64G64B64A64_SINT = 120, // = VK_FORMAT_R64G64B64A64_SINT
SPV_REFLECT_FORMAT_R64G64B64A64_SFLOAT = 121, // = VK_FORMAT_R64G64B64A64_SFLOAT
@@ -447,20 +447,20 @@ typedef struct SpvReflectShaderModule {
uint32_t source_language_version;
const char* source_file;
const char* source_source;
- SpvExecutionModel spirv_execution_model;
- SpvReflectShaderStageFlagBits shader_stage;
- uint32_t descriptor_binding_count;
- SpvReflectDescriptorBinding* descriptor_bindings;
- uint32_t descriptor_set_count;
- SpvReflectDescriptorSet descriptor_sets[SPV_REFLECT_MAX_DESCRIPTOR_SETS];
- uint32_t input_variable_count;
- SpvReflectInterfaceVariable** input_variables;
- uint32_t output_variable_count;
- SpvReflectInterfaceVariable** output_variables;
- uint32_t interface_variable_count;
- SpvReflectInterfaceVariable* interface_variables;
- uint32_t push_constant_block_count;
- SpvReflectBlockVariable* push_constant_blocks;
+ SpvExecutionModel spirv_execution_model; // Uses value(s) from first entry point
+ SpvReflectShaderStageFlagBits shader_stage; // Uses value(s) from first entry point
+ uint32_t descriptor_binding_count; // Uses value(s) from first entry point
+ SpvReflectDescriptorBinding* descriptor_bindings; // Uses value(s) from first entry point
+ uint32_t descriptor_set_count; // Uses value(s) from first entry point
+ SpvReflectDescriptorSet descriptor_sets[SPV_REFLECT_MAX_DESCRIPTOR_SETS]; // Uses value(s) from first entry point
+ uint32_t input_variable_count; // Uses value(s) from first entry point
+ SpvReflectInterfaceVariable** input_variables; // Uses value(s) from first entry point
+ uint32_t output_variable_count; // Uses value(s) from first entry point
+ SpvReflectInterfaceVariable** output_variables; // Uses value(s) from first entry point
+ uint32_t interface_variable_count; // Uses value(s) from first entry point
+ SpvReflectInterfaceVariable* interface_variables; // Uses value(s) from first entry point
+ uint32_t push_constant_block_count; // Uses value(s) from first entry point
+ SpvReflectBlockVariable* push_constant_blocks; // Uses value(s) from first entry point
// -- GODOT begin --
uint32_t specialization_constant_count;
SpvReflectSpecializationConstant* specialization_constants;
@@ -1426,6 +1426,9 @@ public:
ShaderModule(const std::vector<uint32_t>& code);
~ShaderModule();
+ ShaderModule(ShaderModule&& other);
+ ShaderModule& operator=(ShaderModule&& other);
+
SpvReflectResult GetResult() const;
const SpvReflectShaderModule& GetShaderModule() const;
@@ -1437,8 +1440,9 @@ public:
const char* GetSourceFile() const;
- uint32_t GetEntryPointCount() const;
- const char* GetEntryPointName(uint32_t index) const;
+ uint32_t GetEntryPointCount() const;
+ const char* GetEntryPointName(uint32_t index) const;
+ SpvReflectShaderStageFlagBits GetEntryPointShaderStage(uint32_t index) const;
SpvReflectShaderStageFlagBits GetShaderStage() const;
SPV_REFLECT_DEPRECATED("Renamed to GetShaderStage")
@@ -1568,6 +1572,20 @@ inline ShaderModule::~ShaderModule() {
}
+inline ShaderModule::ShaderModule(ShaderModule&& other)
+{
+ *this = std::move(other);
+}
+
+inline ShaderModule& ShaderModule::operator=(ShaderModule&& other)
+{
+ m_result = std::move(other.m_result);
+ m_module = std::move(other.m_module);
+
+ other.m_module = {};
+ return *this;
+}
+
/*! @fn GetResult
@return
@@ -1644,9 +1662,18 @@ inline const char* ShaderModule::GetEntryPointName(uint32_t index) const {
return m_module.entry_points[index].name;
}
+/*! @fn GetEntryPointShaderStage
+
+ @param index
+ @return Returns the shader stage for the entry point at \b index
+*/
+inline SpvReflectShaderStageFlagBits ShaderModule::GetEntryPointShaderStage(uint32_t index) const {
+ return m_module.entry_points[index].shader_stage;
+}
+
/*! @fn GetShaderStage
- @return Returns Vulkan shader stage
+ @return Returns shader stage for the first entry point
*/
inline SpvReflectShaderStageFlagBits ShaderModule::GetShaderStage() const {