summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/object/undo_redo.cpp5
-rw-r--r--doc/classes/Animation.xml58
-rw-r--r--doc/classes/AnimationTree.xml4
-rw-r--r--doc/classes/Skeleton3D.xml74
-rw-r--r--doc/classes/TileSetAtlasSource.xml22
-rw-r--r--editor/animation_track_editor.cpp296
-rw-r--r--editor/animation_track_editor.h2
-rw-r--r--editor/icons/KeyTrackPosition.svg47
-rw-r--r--editor/icons/KeyTrackRotation.svg47
-rw-r--r--editor/icons/KeyTrackScale.svg47
-rw-r--r--editor/icons/KeyXPosition.svg43
-rw-r--r--editor/icons/KeyXRotation.svg44
-rw-r--r--editor/icons/KeyXScale.svg44
-rw-r--r--editor/import/editor_import_collada.cpp148
-rw-r--r--editor/import/editor_importer_bake_reset.cpp234
-rw-r--r--editor/import/editor_importer_bake_reset.h54
-rw-r--r--editor/import/resource_importer_scene.cpp87
-rw-r--r--editor/import/resource_importer_scene.h7
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp6
-rw-r--r--editor/plugins/skeleton_3d_editor_plugin.cpp44
-rw-r--r--editor/plugins/skeleton_3d_editor_plugin.h4
-rw-r--r--editor/plugins/tiles/tile_atlas_view.cpp94
-rw-r--r--editor/plugins/tiles/tile_atlas_view.h4
-rw-r--r--editor/plugins/tiles/tile_data_editors.cpp20
-rw-r--r--editor/plugins/tiles/tile_data_editors.h4
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.cpp92
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.h6
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp2
-rw-r--r--modules/fbx/data/fbx_skeleton.cpp7
-rw-r--r--modules/fbx/editor_scene_importer_fbx.cpp55
-rw-r--r--modules/gltf/gltf_document.cpp113
-rw-r--r--modules/lightmapper_rd/lm_common_inc.glsl4
-rw-r--r--modules/lightmapper_rd/lm_compute.glsl24
-rw-r--r--modules/lightmapper_rd/lm_raster.glsl12
-rw-r--r--platform/linuxbsd/freedesktop_screensaver.cpp4
-rw-r--r--scene/2d/gpu_particles_2d.cpp4
-rw-r--r--scene/2d/tile_map.cpp23
-rw-r--r--scene/2d/tile_map.h2
-rw-r--r--scene/3d/bone_attachment_3d.cpp6
-rw-r--r--scene/3d/bone_attachment_3d.h1
-rw-r--r--scene/3d/mesh_instance_3d.cpp4
-rw-r--r--scene/3d/skeleton_3d.cpp256
-rw-r--r--scene/3d/skeleton_3d.h35
-rw-r--r--scene/3d/vehicle_body_3d.cpp4
-rw-r--r--scene/animation/animation_cache.cpp314
-rw-r--r--scene/animation/animation_cache.h84
-rw-r--r--scene/animation/animation_player.cpp168
-rw-r--r--scene/animation/animation_player.h7
-rw-r--r--scene/animation/animation_tree.cpp253
-rw-r--r--scene/animation/animation_tree.h5
-rw-r--r--scene/resources/animation.cpp977
-rw-r--r--scene/resources/animation.h74
-rw-r--r--scene/resources/skeleton_modification_3d_fabrik.cpp2
-rw-r--r--scene/resources/skeleton_modification_3d_twoboneik.cpp2
-rw-r--r--scene/resources/tile_set.cpp83
-rw-r--r--scene/resources/tile_set.h8
-rw-r--r--scene/resources/visual_shader.cpp52
-rw-r--r--scene/resources/visual_shader.h2
-rw-r--r--scene/resources/visual_shader_particle_nodes.cpp20
-rw-r--r--scene/resources/visual_shader_particle_nodes.h5
-rw-r--r--servers/rendering/renderer_rd/shader_compiler_rd.cpp1
-rw-r--r--servers/rendering/shader_language.cpp271
-rw-r--r--servers/rendering/shader_language.h11
63 files changed, 2525 insertions, 1907 deletions
diff --git a/core/object/undo_redo.cpp b/core/object/undo_redo.cpp
index 289caf5247..9c84c2add7 100644
--- a/core/object/undo_redo.cpp
+++ b/core/object/undo_redo.cpp
@@ -110,15 +110,16 @@ void UndoRedo::create_action(const String &p_name, MergeMode p_mode) {
actions.write[actions.size() - 1].last_tick = ticks;
+ merge_mode = p_mode;
merging = true;
} else {
Action new_action;
new_action.name = p_name;
new_action.last_tick = ticks;
actions.push_back(new_action);
- }
- merge_mode = p_mode;
+ merge_mode = MERGE_DISABLE;
+ }
}
action_level++;
diff --git a/doc/classes/Animation.xml b/doc/classes/Animation.xml
index d2ecbdde26..a35b784106 100644
--- a/doc/classes/Animation.xml
+++ b/doc/classes/Animation.xml
@@ -253,6 +253,14 @@
Returns the arguments values to be called on a method track for a given key in a given track.
</description>
</method>
+ <method name="position_track_insert_key">
+ <return type="int" />
+ <argument index="0" name="track_idx" type="int" />
+ <argument index="1" name="time" type="float" />
+ <argument index="2" name="position" type="Vector3" />
+ <description>
+ </description>
+ </method>
<method name="remove_track">
<return type="void" />
<argument index="0" name="track_idx" type="int" />
@@ -260,6 +268,22 @@
Removes a track by specifying the track index.
</description>
</method>
+ <method name="rotation_track_insert_key">
+ <return type="int" />
+ <argument index="0" name="track_idx" type="int" />
+ <argument index="1" name="time" type="float" />
+ <argument index="2" name="rotation" type="Quaternion" />
+ <description>
+ </description>
+ </method>
+ <method name="scale_track_insert_key">
+ <return type="int" />
+ <argument index="0" name="track_idx" type="int" />
+ <argument index="1" name="time" type="float" />
+ <argument index="2" name="scale" type="Vector3" />
+ <description>
+ </description>
+ </method>
<method name="track_find_key" qualifiers="const">
<return type="int" />
<argument index="0" name="track_idx" type="int" />
@@ -466,25 +490,6 @@
Swaps the track [code]idx[/code]'s index position with the track [code]with_idx[/code].
</description>
</method>
- <method name="transform_track_insert_key">
- <return type="int" />
- <argument index="0" name="track_idx" type="int" />
- <argument index="1" name="time" type="float" />
- <argument index="2" name="location" type="Vector3" />
- <argument index="3" name="rotation" type="Quaternion" />
- <argument index="4" name="scale" type="Vector3" />
- <description>
- Insert a transform key for a transform track.
- </description>
- </method>
- <method name="transform_track_interpolate" qualifiers="const">
- <return type="Array" />
- <argument index="0" name="track_idx" type="int" />
- <argument index="1" name="time_sec" type="float" />
- <description>
- Returns the interpolated value of a transform track at a given time (in seconds). An array consisting of 3 elements: position ([Vector3]), rotation ([Quaternion]) and scale ([Vector3]).
- </description>
- </method>
<method name="value_track_get_key_indices" qualifiers="const">
<return type="PackedInt32Array" />
<argument index="0" name="track_idx" type="int" />
@@ -541,19 +546,22 @@
<constant name="TYPE_VALUE" value="0" enum="TrackType">
Value tracks set values in node properties, but only those which can be Interpolated.
</constant>
- <constant name="TYPE_TRANSFORM3D" value="1" enum="TrackType">
- Transform3D tracks are used to change node local transforms or skeleton pose bones of 3D nodes. Transitions are interpolated.
+ <constant name="TYPE_POSITION_3D" value="1" enum="TrackType">
+ </constant>
+ <constant name="TYPE_ROTATION_3D" value="2" enum="TrackType">
+ </constant>
+ <constant name="TYPE_SCALE_3D" value="3" enum="TrackType">
</constant>
- <constant name="TYPE_METHOD" value="2" enum="TrackType">
+ <constant name="TYPE_METHOD" value="4" enum="TrackType">
Method tracks call functions with given arguments per key.
</constant>
- <constant name="TYPE_BEZIER" value="3" enum="TrackType">
+ <constant name="TYPE_BEZIER" value="5" enum="TrackType">
Bezier tracks are used to interpolate a value using custom curves. They can also be used to animate sub-properties of vectors and colors (e.g. alpha value of a [Color]).
</constant>
- <constant name="TYPE_AUDIO" value="4" enum="TrackType">
+ <constant name="TYPE_AUDIO" value="6" enum="TrackType">
Audio tracks are used to play an audio stream with either type of [AudioStreamPlayer]. The stream can be trimmed and previewed in the animation.
</constant>
- <constant name="TYPE_ANIMATION" value="5" enum="TrackType">
+ <constant name="TYPE_ANIMATION" value="7" enum="TrackType">
Animation tracks play animations in other [AnimationPlayer] nodes.
</constant>
<constant name="INTERPOLATION_NEAREST" value="0" enum="InterpolationType">
diff --git a/doc/classes/AnimationTree.xml b/doc/classes/AnimationTree.xml
index 3d112e258e..40dcd950d7 100644
--- a/doc/classes/AnimationTree.xml
+++ b/doc/classes/AnimationTree.xml
@@ -22,7 +22,7 @@
<method name="get_root_motion_transform" qualifiers="const">
<return type="Transform3D" />
<description>
- Retrieve the motion of the [member root_motion_track] as a [Transform3D] that can be used elsewhere. If [member root_motion_track] is not a path to a track of type [constant Animation.TYPE_TRANSFORM3D], returns an identity transformation. See also [member root_motion_track] and [RootMotionView].
+ Retrieve the motion of the [member root_motion_track] as a [Transform3D] that can be used elsewhere. If [member root_motion_track] is not a path to a track of type [constant Animation.TYPE_POSITION_3D], [constant Animation.TYPE_SCALE_3D] or [constant Animation.TYPE_ROTATION_3D], returns an identity transformation. See also [member root_motion_track] and [RootMotionView].
</description>
</method>
<method name="rename_parameter">
@@ -45,7 +45,7 @@
</member>
<member name="root_motion_track" type="NodePath" setter="set_root_motion_track" getter="get_root_motion_track" default="NodePath(&quot;&quot;)">
The path to the Animation track used for root motion. Paths must be valid scene-tree paths to a node, and must be specified starting from the parent node of the node that will reproduce the animation. To specify a track that controls properties or bones, append its name after the path, separated by [code]":"[/code]. For example, [code]"character/skeleton:ankle"[/code] or [code]"character/mesh:transform/local"[/code].
- If the track has type [constant Animation.TYPE_TRANSFORM3D], the transformation will be cancelled visually, and the animation will appear to stay in place. See also [method get_root_motion_transform] and [RootMotionView].
+ If the track has type [constant Animation.TYPE_POSITION_3D], [constant Animation.TYPE_ROTATION_3D] or [constant Animation.TYPE_SCALE_3D] the transformation will be cancelled visually, and the animation will appear to stay in place. See also [method get_root_motion_transform] and [RootMotionView].
</member>
<member name="tree_root" type="AnimationNode" setter="set_tree_root" getter="get_tree_root">
The root animation node of this [AnimationTree]. See [AnimationNode].
diff --git a/doc/classes/Skeleton3D.xml b/doc/classes/Skeleton3D.xml
index 6ab5ed55a3..e804e7bf24 100644
--- a/doc/classes/Skeleton3D.xml
+++ b/doc/classes/Skeleton3D.xml
@@ -47,6 +47,11 @@
Removes the local pose override on all bones in the skeleton.
</description>
</method>
+ <method name="create_skin_from_rest_transforms">
+ <return type="Skin" />
+ <description>
+ </description>
+ </method>
<method name="execute_modifications">
<return type="void" />
<argument index="0" name="delta" type="float" />
@@ -88,13 +93,6 @@
Returns the amount of bones in the skeleton.
</description>
</method>
- <method name="get_bone_custom_pose" qualifiers="const">
- <return type="Transform3D" />
- <argument index="0" name="bone_idx" type="int" />
- <description>
- Returns the custom pose of the specified bone. Custom pose is applied on top of the rest pose.
- </description>
- </method>
<method name="get_bone_global_pose" qualifiers="const">
<return type="Transform3D" />
<argument index="0" name="bone_idx" type="int" />
@@ -144,6 +142,24 @@
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_pose_position" qualifiers="const">
+ <return type="Vector3" />
+ <argument index="0" name="bone_idx" type="int" />
+ <description>
+ </description>
+ </method>
+ <method name="get_bone_pose_rotation" qualifiers="const">
+ <return type="Quaternion" />
+ <argument index="0" name="bone_idx" type="int" />
+ <description>
+ </description>
+ </method>
+ <method name="get_bone_pose_scale" qualifiers="const">
+ <return type="Vector3" />
+ <argument index="0" name="bone_idx" type="int" />
+ <description>
+ </description>
+ </method>
<method name="get_bone_rest" qualifiers="const">
<return type="Transform3D" />
<argument index="0" name="bone_idx" type="int" />
@@ -196,13 +212,6 @@
Returns whether the bone pose for the bone at [code]bone_idx[/code] is enabled.
</description>
</method>
- <method name="is_bone_rest_disabled" qualifiers="const">
- <return type="bool" />
- <argument index="0" name="bone_idx" type="int" />
- <description>
- 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" />
@@ -272,23 +281,6 @@
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_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">
- <return type="void" />
- <argument index="0" name="bone_idx" type="int" />
- <argument index="1" name="disable" type="bool" />
- <description>
- Disables the rest pose for the bone at [code]bone_idx[/code] if [code]true[/code], enables the bone rest if [code]false[/code].
- </description>
- </method>
<method name="set_bone_enabled">
<return type="void" />
<argument index="0" name="bone_idx" type="int" />
@@ -337,13 +329,25 @@
[b]Note:[/b] [code]parent_idx[/code] must be less than [code]bone_idx[/code].
</description>
</method>
- <method name="set_bone_pose">
+ <method name="set_bone_pose_position">
<return type="void" />
<argument index="0" name="bone_idx" type="int" />
- <argument index="1" name="pose" type="Transform3D" />
+ <argument index="1" name="position" type="Vector3" />
+ <description>
+ </description>
+ </method>
+ <method name="set_bone_pose_rotation">
+ <return type="void" />
+ <argument index="0" name="bone_idx" type="int" />
+ <argument index="1" name="rotation" type="Quaternion" />
+ <description>
+ </description>
+ </method>
+ <method name="set_bone_pose_scale">
+ <return type="void" />
+ <argument index="0" name="bone_idx" type="int" />
+ <argument index="1" name="scale" type="Vector3" />
<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_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">
diff --git a/doc/classes/TileSetAtlasSource.xml b/doc/classes/TileSetAtlasSource.xml
index d12ac840f4..881a1c3d07 100644
--- a/doc/classes/TileSetAtlasSource.xml
+++ b/doc/classes/TileSetAtlasSource.xml
@@ -15,12 +15,6 @@
<tutorials>
</tutorials>
<methods>
- <method name="clear_tiles_outside_texture">
- <return type="void" />
- <description>
- Clears all tiles that are defined outside the texture boundaries.
- </description>
- </method>
<method name="create_alternative_tile">
<return type="int" />
<argument index="0" name="atlas_coords" type="Vector2i" />
@@ -124,6 +118,16 @@
Returns a tile's texture region in the atlas texture. For animated tiles, a [code]frame[/code] argument might be provided for the different frames of the animation.
</description>
</method>
+ <method name="get_tiles_to_be_removed_on_change">
+ <return type="PackedVector2Array" />
+ <argument index="0" name="texture" type="Texture2D" />
+ <argument index="1" name="margins" type="Vector2i" />
+ <argument index="2" name="separation" type="Vector2i" />
+ <argument index="3" name="texture_region_size" type="Vector2i" />
+ <description>
+ Returns an array of tiles coordinates ID that will be automatically removed when modifying one or several of those properties: [code]texture[/code], [code]margins[/code], [code]separation[/code] or [code]texture_region_size[/code]. This can be used to undo changes that would have caused tiles data loss.
+ </description>
+ </method>
<method name="has_room_for_tile" qualifiers="const">
<return type="bool" />
<argument index="0" name="atlas_coords" type="Vector2i" />
@@ -136,12 +140,6 @@
Returns whether there is enough room in an atlas to create/modify a tile with the given properties. If [code]ignored_tile[/code] is provided, act as is the given tile was not present in the atlas. This may be used when you want to modify a tile's properties.
</description>
</method>
- <method name="has_tiles_outside_texture">
- <return type="bool" />
- <description>
- Returns if this atlas has tiles outside of its texture.
- </description>
- </method>
<method name="move_tile_in_atlas">
<return type="void" />
<argument index="0" name="atlas_coords" type="Vector2i" />
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index 14ca35a664..2ee7f0ffa5 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -165,20 +165,38 @@ public:
}
switch (animation->track_get_type(track)) {
- case Animation::TYPE_TRANSFORM3D: {
- Dictionary d_old = animation->track_get_key_value(track, key);
- Dictionary d_new = d_old.duplicate();
- d_new[p_name] = p_value;
- setting = true;
- undo_redo->create_action(TTR("Anim Change Transform"));
- undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, d_new);
- undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, d_old);
- undo_redo->add_do_method(this, "_update_obj", animation);
- undo_redo->add_undo_method(this, "_update_obj", animation);
- undo_redo->commit_action();
+ case Animation::TYPE_POSITION_3D:
+ case Animation::TYPE_ROTATION_3D:
+ case Animation::TYPE_SCALE_3D: {
+ if (name == "position" || name == "rotation" || name == "scale") {
+ Variant old = animation->track_get_key_value(track, key);
+ setting = true;
+ String chan;
+ switch (animation->track_get_type(track)) {
+ case Animation::TYPE_POSITION_3D:
+ chan = "Position3D";
+ break;
+ case Animation::TYPE_ROTATION_3D:
+ chan = "Rotation3D";
+ break;
+ case Animation::TYPE_SCALE_3D:
+ chan = "Scale3D";
+ break;
+ default: {
+ }
+ }
+
+ undo_redo->create_action(vformat(TTR("Anim Change %s"), chan));
+ undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, p_value);
+ undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, old);
+ undo_redo->add_do_method(this, "_update_obj", animation);
+ undo_redo->add_undo_method(this, "_update_obj", animation);
+ undo_redo->commit_action();
+
+ setting = false;
+ return true;
+ }
- setting = false;
- return true;
} break;
case Animation::TYPE_VALUE: {
if (name == "value") {
@@ -412,12 +430,13 @@ public:
}
switch (animation->track_get_type(track)) {
- case Animation::TYPE_TRANSFORM3D: {
- Dictionary d = animation->track_get_key_value(track, key);
- ERR_FAIL_COND_V(!d.has(name), false);
- r_ret = d[p_name];
- return true;
-
+ case Animation::TYPE_POSITION_3D:
+ case Animation::TYPE_ROTATION_3D:
+ case Animation::TYPE_SCALE_3D: {
+ if (name == "position" || name == "rotation" || name == "scale") {
+ r_ret = animation->track_get_key_value(track, key);
+ return true;
+ }
} break;
case Animation::TYPE_VALUE: {
if (name == "value") {
@@ -523,11 +542,14 @@ public:
}
switch (animation->track_get_type(track)) {
- case Animation::TYPE_TRANSFORM3D: {
- p_list->push_back(PropertyInfo(Variant::VECTOR3, "location"));
- p_list->push_back(PropertyInfo(Variant::QUATERNION, "rotation"));
+ case Animation::TYPE_POSITION_3D: {
+ p_list->push_back(PropertyInfo(Variant::VECTOR3, "position"));
+ } break;
+ case Animation::TYPE_ROTATION_3D: {
+ p_list->push_back(PropertyInfo(Variant::VECTOR3, "rotation"));
+ } break;
+ case Animation::TYPE_SCALE_3D: {
p_list->push_back(PropertyInfo(Variant::VECTOR3, "scale"));
-
} break;
case Animation::TYPE_VALUE: {
Variant v = animation->track_get_key_value(track, key);
@@ -779,17 +801,31 @@ public:
}
switch (animation->track_get_type(track)) {
- case Animation::TYPE_TRANSFORM3D: {
- Dictionary d_old = animation->track_get_key_value(track, key);
- Dictionary d_new = d_old.duplicate();
- d_new[p_name] = p_value;
-
+ case Animation::TYPE_POSITION_3D:
+ case Animation::TYPE_ROTATION_3D:
+ case Animation::TYPE_SCALE_3D: {
+ Variant old = animation->track_get_key_value(track, key);
if (!setting) {
+ String chan;
+ switch (animation->track_get_type(track)) {
+ case Animation::TYPE_POSITION_3D:
+ chan = "Position3D";
+ break;
+ case Animation::TYPE_ROTATION_3D:
+ chan = "Rotation3D";
+ break;
+ case Animation::TYPE_SCALE_3D:
+ chan = "Scale3D";
+ break;
+ default: {
+ }
+ }
+
setting = true;
- undo_redo->create_action(TTR("Anim Multi Change Transform"));
+ undo_redo->create_action(vformat(TTR("Anim Multi Change %s"), chan));
}
- undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, d_new);
- undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, d_old);
+ undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, p_value);
+ undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, old);
update_obj = true;
} break;
case Animation::TYPE_VALUE: {
@@ -1009,11 +1045,13 @@ public:
}
switch (animation->track_get_type(track)) {
- case Animation::TYPE_TRANSFORM3D: {
- Dictionary d = animation->track_get_key_value(track, key);
- ERR_FAIL_COND_V(!d.has(name), false);
- r_ret = d[p_name];
- return true;
+ case Animation::TYPE_POSITION_3D:
+ case Animation::TYPE_ROTATION_3D:
+ case Animation::TYPE_SCALE_3D: {
+ if (name == "position" || name == "rotation" || name == "scale") {
+ r_ret = animation->track_get_key_value(track, key);
+ return true;
+ }
} break;
case Animation::TYPE_VALUE: {
@@ -1159,9 +1197,13 @@ public:
if (same_track_type) {
switch (animation->track_get_type(first_track)) {
- case Animation::TYPE_TRANSFORM3D: {
- p_list->push_back(PropertyInfo(Variant::VECTOR3, "location"));
- p_list->push_back(PropertyInfo(Variant::QUATERNION, "rotation"));
+ case Animation::TYPE_POSITION_3D: {
+ p_list->push_back(PropertyInfo(Variant::VECTOR3, "position"));
+ } break;
+ case Animation::TYPE_ROTATION_3D: {
+ p_list->push_back(PropertyInfo(Variant::QUATERNION, "scale"));
+ } break;
+ case Animation::TYPE_SCALE_3D: {
p_list->push_back(PropertyInfo(Variant::VECTOR3, "scale"));
} break;
case Animation::TYPE_VALUE: {
@@ -1359,7 +1401,9 @@ void AnimationTimelineEdit::_notification(int p_what) {
add_track->get_popup()->clear();
add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyValue"), SNAME("EditorIcons")), TTR("Property Track"));
- add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyXform"), SNAME("EditorIcons")), TTR("3D Transform Track"));
+ add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyXPosition"), SNAME("EditorIcons")), TTR("3D Position Track"));
+ add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyXRotation"), SNAME("EditorIcons")), TTR("3D Rotation Track"));
+ add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyXScale"), SNAME("EditorIcons")), TTR("3D Scale Track"));
add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyCall"), SNAME("EditorIcons")), TTR("Call Method Track"));
add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyBezier"), SNAME("EditorIcons")), TTR("Bezier Curve Track"));
add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyAudio"), SNAME("EditorIcons")), TTR("Audio Playback Track"));
@@ -1828,9 +1872,11 @@ void AnimationTrackEdit::_notification(int p_what) {
Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Label"));
int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Label"));
Color color = get_theme_color(SNAME("font_color"), SNAME("Label"));
- Ref<Texture2D> type_icons[6] = {
+ Ref<Texture2D> type_icons[8] = {
get_theme_icon(SNAME("KeyValue"), SNAME("EditorIcons")),
- get_theme_icon(SNAME("KeyXform"), SNAME("EditorIcons")),
+ get_theme_icon(SNAME("KeyTrackPosition"), SNAME("EditorIcons")),
+ get_theme_icon(SNAME("KeyTrackRotation"), SNAME("EditorIcons")),
+ get_theme_icon(SNAME("KeyTrackScale"), SNAME("EditorIcons")),
get_theme_icon(SNAME("KeyCall"), SNAME("EditorIcons")),
get_theme_icon(SNAME("KeyBezier"), SNAME("EditorIcons")),
get_theme_icon(SNAME("KeyAudio"), SNAME("EditorIcons")),
@@ -2021,7 +2067,7 @@ void AnimationTrackEdit::_notification(int p_what) {
interp_mode_rect.position.y = int(get_size().height - icon->get_height()) / 2;
interp_mode_rect.size = icon->get_size();
- if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_TRANSFORM3D) {
+ if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_POSITION_3D || animation->track_get_type(track) == Animation::TYPE_SCALE_3D || animation->track_get_type(track) == Animation::TYPE_ROTATION_3D) {
draw_texture(icon, interp_mode_rect.position);
}
// Make it easier to click.
@@ -2031,7 +2077,7 @@ void AnimationTrackEdit::_notification(int p_what) {
ofs += icon->get_width() + hsep;
interp_mode_rect.size.x += hsep;
- if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_TRANSFORM3D) {
+ if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_POSITION_3D || animation->track_get_type(track) == Animation::TYPE_SCALE_3D || animation->track_get_type(track) == Animation::TYPE_ROTATION_3D) {
draw_texture(down_icon, Vector2(ofs, int(get_size().height - down_icon->get_height()) / 2));
interp_mode_rect.size.x += down_icon->get_width();
} else {
@@ -2054,7 +2100,7 @@ void AnimationTrackEdit::_notification(int p_what) {
loop_mode_rect.position.y = int(get_size().height - icon->get_height()) / 2;
loop_mode_rect.size = icon->get_size();
- if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_TRANSFORM3D) {
+ if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_POSITION_3D || animation->track_get_type(track) == Animation::TYPE_SCALE_3D || animation->track_get_type(track) == Animation::TYPE_ROTATION_3D) {
draw_texture(icon, loop_mode_rect.position);
}
@@ -2064,7 +2110,7 @@ void AnimationTrackEdit::_notification(int p_what) {
ofs += icon->get_width() + hsep;
loop_mode_rect.size.x += hsep;
- if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_TRANSFORM3D) {
+ if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_POSITION_3D || animation->track_get_type(track) == Animation::TYPE_SCALE_3D || animation->track_get_type(track) == Animation::TYPE_ROTATION_3D) {
draw_texture(down_icon, Vector2(ofs, int(get_size().height - down_icon->get_height()) / 2));
loop_mode_rect.size.x += down_icon->get_width();
} else {
@@ -2284,9 +2330,11 @@ void AnimationTrackEdit::set_animation_and_track(const Ref<Animation> &p_animati
track = p_track;
update();
- Ref<Texture2D> type_icons[6] = {
+ Ref<Texture2D> type_icons[8] = {
get_theme_icon(SNAME("KeyValue"), SNAME("EditorIcons")),
- get_theme_icon(SNAME("KeyXform"), SNAME("EditorIcons")),
+ get_theme_icon(SNAME("KeyXPosition"), SNAME("EditorIcons")),
+ get_theme_icon(SNAME("KeyXRotation"), SNAME("EditorIcons")),
+ get_theme_icon(SNAME("KeyXScale"), SNAME("EditorIcons")),
get_theme_icon(SNAME("KeyCall"), SNAME("EditorIcons")),
get_theme_icon(SNAME("KeyBezier"), SNAME("EditorIcons")),
get_theme_icon(SNAME("KeyAudio"), SNAME("EditorIcons")),
@@ -2456,17 +2504,17 @@ String AnimationTrackEdit::get_tooltip(const Point2 &p_pos) const {
if (key_idx != -1) {
String text = TTR("Time (s): ") + rtos(animation->track_get_key_time(track, key_idx)) + "\n";
switch (animation->track_get_type(track)) {
- case Animation::TYPE_TRANSFORM3D: {
- Dictionary d = animation->track_get_key_value(track, key_idx);
- if (d.has("location")) {
- text += "Pos: " + String(d["location"]) + "\n";
- }
- if (d.has("rotation")) {
- text += "Rot: " + String(d["rotation"]) + "\n";
- }
- if (d.has("scale")) {
- text += "Scale: " + String(d["scale"]) + "\n";
- }
+ case Animation::TYPE_POSITION_3D: {
+ Vector3 t = animation->track_get_key_value(track, key_idx);
+ text += "Position: " + String(t) + "\n";
+ } break;
+ case Animation::TYPE_ROTATION_3D: {
+ Quaternion t = animation->track_get_key_value(track, key_idx);
+ text += "Rotation: " + String(t) + "\n";
+ } break;
+ case Animation::TYPE_SCALE_3D: {
+ Vector3 t = animation->track_get_key_value(track, key_idx);
+ text += "Scale: " + String(t) + "\n";
} break;
case Animation::TYPE_VALUE: {
const Variant &v = animation->track_get_key_value(track, key_idx);
@@ -3323,7 +3371,11 @@ static bool track_type_is_resettable(Animation::TrackType p_type) {
[[fallthrough]];
case Animation::TYPE_BEZIER:
[[fallthrough]];
- case Animation::TYPE_TRANSFORM3D:
+ case Animation::TYPE_POSITION_3D:
+ [[fallthrough]];
+ case Animation::TYPE_ROTATION_3D:
+ [[fallthrough]];
+ case Animation::TYPE_SCALE_3D:
return true;
default:
return false;
@@ -3483,33 +3535,50 @@ void AnimationTrackEditor::insert_transform_key(Node3D *p_node, const String &p_
NodePath np = path;
- int track_idx = -1;
+ int position_idx = -1;
+ int rotation_idx = -1;
+ int scale_idx = -1;
for (int i = 0; i < animation->get_track_count(); i++) {
- if (animation->track_get_type(i) != Animation::TYPE_TRANSFORM3D) {
- continue;
- }
if (animation->track_get_path(i) != np) {
continue;
}
- track_idx = i;
- break;
+ if (animation->track_get_type(i) == Animation::TYPE_POSITION_3D) {
+ position_idx = i;
+ }
+ if (animation->track_get_type(i) == Animation::TYPE_ROTATION_3D) {
+ rotation_idx = i;
+ }
+ if (animation->track_get_type(i) == Animation::TYPE_SCALE_3D) {
+ scale_idx = i;
+ }
}
InsertData id;
- Dictionary val;
-
id.path = np;
- id.track_idx = track_idx;
- id.value = p_xform;
- id.type = Animation::TYPE_TRANSFORM3D;
// TRANSLATORS: This describes the target of new animation track, will be inserted into another string.
id.query = vformat(TTR("node '%s'"), p_node->get_name());
id.advance = false;
- // Dialog insert.
- _query_insert(id);
+ {
+ id.track_idx = position_idx;
+ id.value = p_xform.origin;
+ id.type = Animation::TYPE_POSITION_3D;
+ _query_insert(id);
+ }
+ {
+ id.track_idx = rotation_idx;
+ id.value = p_xform.basis.get_rotation_quaternion();
+ id.type = Animation::TYPE_ROTATION_3D;
+ _query_insert(id);
+ }
+ {
+ id.track_idx = scale_idx;
+ id.value = p_xform.basis.get_scale();
+ id.type = Animation::TYPE_SCALE_3D;
+ _query_insert(id);
+ }
}
bool AnimationTrackEditor::has_transform_track(Node3D *p_node, const String &p_sub) {
@@ -3530,7 +3599,7 @@ bool AnimationTrackEditor::has_transform_track(Node3D *p_node, const String &p_s
}
int track_id = animation->find_track(path);
if (track_id >= 0) {
- if (animation->track_get_type(track_id) == Animation::TYPE_TRANSFORM3D) {
+ if (animation->track_get_type(track_id) == Animation::TYPE_POSITION_3D || animation->track_get_type(track_id) == Animation::TYPE_ROTATION_3D || animation->track_get_type(track_id) == Animation::TYPE_SCALE_3D) {
return true;
}
}
@@ -3977,18 +4046,13 @@ AnimationTrackEditor::TrackIndices AnimationTrackEditor::_confirm_insert(InsertD
Variant value;
switch (p_id.type) {
+ case Animation::TYPE_POSITION_3D:
+ case Animation::TYPE_ROTATION_3D:
+ case Animation::TYPE_SCALE_3D:
case Animation::TYPE_VALUE: {
value = p_id.value;
} break;
- case Animation::TYPE_TRANSFORM3D: {
- Transform3D tr = p_id.value;
- Dictionary d;
- d["location"] = tr.origin;
- d["scale"] = tr.basis.get_scale();
- d["rotation"] = Quaternion(tr.basis);
- value = d;
- } break;
case Animation::TYPE_BEZIER: {
Array array;
array.resize(5);
@@ -4404,8 +4468,8 @@ void AnimationTrackEditor::_new_track_node_selected(NodePath p_path) {
ERR_FAIL_COND(!node);
NodePath path_to = root->get_path_to(node);
- if (adding_track_type == Animation::TYPE_TRANSFORM3D && !node->is_class("Node3D")) {
- EditorNode::get_singleton()->show_warning(TTR("Transform3D tracks only apply to 3D-based nodes."));
+ if ((adding_track_type == Animation::TYPE_POSITION_3D || adding_track_type == Animation::TYPE_ROTATION_3D || adding_track_type == Animation::TYPE_SCALE_3D) && !node->is_class("Node3D")) {
+ EditorNode::get_singleton()->show_warning(TTR("Position/Rotation/Scale 3D tracks only apply to 3D-based nodes."));
return;
}
@@ -4415,7 +4479,9 @@ void AnimationTrackEditor::_new_track_node_selected(NodePath p_path) {
prop_selector->set_type_filter(Vector<Variant::Type>());
prop_selector->select_property_from_instance(node);
} break;
- case Animation::TYPE_TRANSFORM3D:
+ case Animation::TYPE_POSITION_3D:
+ case Animation::TYPE_ROTATION_3D:
+ case Animation::TYPE_SCALE_3D:
case Animation::TYPE_METHOD: {
undo_redo->create_action(TTR("Add Track"));
undo_redo->add_do_method(animation.ptr(), "add_track", adding_track_type);
@@ -4584,7 +4650,27 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) {
}
switch (animation->track_get_type(p_track)) {
- case Animation::TYPE_TRANSFORM3D: {
+ case Animation::TYPE_POSITION_3D: {
+ if (!root->has_node(animation->track_get_path(p_track))) {
+ EditorNode::get_singleton()->show_warning(TTR("Track path is invalid, so can't add a key."));
+ return;
+ }
+ Node3D *base = Object::cast_to<Node3D>(root->get_node(animation->track_get_path(p_track)));
+
+ if (!base) {
+ EditorNode::get_singleton()->show_warning(TTR("Track is not of type Node3D, can't insert key"));
+ return;
+ }
+
+ Vector3 pos = base->get_position();
+
+ undo_redo->create_action(TTR("Add Position Key"));
+ undo_redo->add_do_method(animation.ptr(), "position_track_insert_key", p_track, p_ofs, pos);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", p_track, p_ofs);
+ undo_redo->commit_action();
+
+ } break;
+ case Animation::TYPE_ROTATION_3D: {
if (!root->has_node(animation->track_get_path(p_track))) {
EditorNode::get_singleton()->show_warning(TTR("Track path is invalid, so can't add a key."));
return;
@@ -4596,14 +4682,30 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) {
return;
}
- Transform3D xf = base->get_transform();
+ Quaternion rot = base->get_transform().basis.operator Quaternion();
+
+ undo_redo->create_action(TTR("Add Rotation Key"));
+ undo_redo->add_do_method(animation.ptr(), "rotation_track_insert_key", p_track, p_ofs, rot);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", p_track, p_ofs);
+ undo_redo->commit_action();
+
+ } break;
+ case Animation::TYPE_SCALE_3D: {
+ if (!root->has_node(animation->track_get_path(p_track))) {
+ EditorNode::get_singleton()->show_warning(TTR("Track path is invalid, so can't add a key."));
+ return;
+ }
+ Node3D *base = Object::cast_to<Node3D>(root->get_node(animation->track_get_path(p_track)));
+
+ if (!base) {
+ EditorNode::get_singleton()->show_warning(TTR("Track is not of type Node3D, can't insert key"));
+ return;
+ }
- Vector3 loc = xf.get_origin();
- Vector3 scale = xf.basis.get_scale_local();
- Quaternion rot = xf.basis;
+ Vector3 scale = base->get_scale();
- undo_redo->create_action(TTR("Add Transform Track Key"));
- undo_redo->add_do_method(animation.ptr(), "transform_track_insert_key", p_track, p_ofs, loc, rot, scale);
+ undo_redo->create_action(TTR("Add Scale Key"));
+ undo_redo->add_do_method(animation.ptr(), "scale_track_insert_key", p_track, p_ofs, scale);
undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", p_track, p_ofs);
undo_redo->commit_action();
@@ -5277,8 +5379,14 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
}
switch (animation->track_get_type(i)) {
- case Animation::TYPE_TRANSFORM3D:
- text += " (Transform)";
+ case Animation::TYPE_POSITION_3D:
+ text += " (Position)";
+ break;
+ case Animation::TYPE_ROTATION_3D:
+ text += " (Rotation)";
+ break;
+ case Animation::TYPE_SCALE_3D:
+ text += " (Scale)";
break;
case Animation::TYPE_METHOD:
text += " (Methods)";
diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h
index 576edef0c8..2555901557 100644
--- a/editor/animation_track_editor.h
+++ b/editor/animation_track_editor.h
@@ -35,7 +35,7 @@
#include "editor/editor_spin_slider.h"
#include "editor/property_editor.h"
#include "editor/property_selector.h"
-#include "scene/animation/animation_cache.h"
+
#include "scene/gui/control.h"
#include "scene/gui/file_dialog.h"
#include "scene/gui/menu_button.h"
diff --git a/editor/icons/KeyTrackPosition.svg b/editor/icons/KeyTrackPosition.svg
new file mode 100644
index 0000000000..05c6e88841
--- /dev/null
+++ b/editor/icons/KeyTrackPosition.svg
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ height="10"
+ viewBox="0 0 10 10"
+ width="10"
+ version="1.1"
+ id="svg12"
+ sodipodi:docname="KeyTrackPosition.svg"
+ inkscape:version="1.1 (1:1.1+202106031931+af4d65493e)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs16" />
+ <sodipodi:namedview
+ id="namedview14"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ showgrid="false"
+ inkscape:zoom="42.2"
+ inkscape:cx="12.78436"
+ inkscape:cy="6.1729858"
+ inkscape:window-width="1848"
+ inkscape:window-height="1016"
+ inkscape:window-x="72"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg12" />
+ <ellipse
+ style="fill:none;fill-opacity:0.401212;stroke:none;stroke-width:4.7811;stroke-linejoin:round"
+ id="path921"
+ cx="-0.88986981"
+ cy="6.0959954"
+ rx="1.2495773"
+ ry="1.0867468" />
+ <path
+ d="M 4.949661,0.60426977 A 0.6444116,0.6444116 0 0 0 4.504153,0.79178767 L 3.215459,2.0804819 4.12663,2.9916532 4.95977,2.1585124 5.792911,2.9916532 6.704083,2.0804819 5.415388,0.79178767 A 0.6444116,0.6444116 0 0 0 4.949744,0.60426977 Z M 1.926771,3.3691634 0.638077,4.6578577 a 0.6444116,0.6444116 0 0 0 0,0.9111713 L 1.926771,6.8577233 2.837942,5.946552 2.004801,5.1134111 2.837942,4.2802702 1.926771,3.3690989 Z m 6.065948,0 -0.911171,0.9111713 0.83314,0.8331409 -0.83314,0.8331408 0.911171,0.9111714 1.288694,-1.2886944 a 0.6444116,0.6444116 0 0 0 0,-0.9111713 L 7.992719,3.3692278 Z M 4.959777,3.8247361 A 1.2886943,1.2886943 0 0 0 3.671083,5.1134305 1.2886943,1.2886943 0 0 0 4.959777,6.4021248 1.2886943,1.2886943 0 0 0 6.248471,5.1134305 1.2886943,1.2886943 0 0 0 4.959777,3.8247361 Z m -0.83314,3.4105296 -0.911172,0.9111715 1.288694,1.288694 a 0.6444116,0.6444116 0 0 0 0.911171,0 L 6.704025,8.1464372 5.792853,7.2352657 4.959712,8.0684062 4.126572,7.2352657 Z"
+ fill="#e0e0e0"
+ fill-opacity="0.99608"
+ id="path1400"
+ style="fill:#ea7940;fill-opacity:1;stroke-width:0.644347" />
+</svg>
diff --git a/editor/icons/KeyTrackRotation.svg b/editor/icons/KeyTrackRotation.svg
new file mode 100644
index 0000000000..d05e381eb2
--- /dev/null
+++ b/editor/icons/KeyTrackRotation.svg
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ height="10"
+ viewBox="0 0 10 10"
+ width="10"
+ version="1.1"
+ id="svg12"
+ sodipodi:docname="KeyTrackRotation.svg"
+ inkscape:version="1.1 (1:1.1+202106031931+af4d65493e)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs16" />
+ <sodipodi:namedview
+ id="namedview14"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ showgrid="false"
+ inkscape:zoom="42.2"
+ inkscape:cx="1.9075829"
+ inkscape:cy="5.8175355"
+ inkscape:window-width="1848"
+ inkscape:window-height="1016"
+ inkscape:window-x="72"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg12" />
+ <ellipse
+ style="fill:none;fill-opacity:0.401212;stroke:none;stroke-width:4.7811;stroke-linejoin:round"
+ id="path921"
+ cx="-0.88986981"
+ cy="6.0959954"
+ rx="1.2495773"
+ ry="1.0867468" />
+ <path
+ d="m 5.0711986,0.88214062 a 4.1086779,4.1086779 0 0 0 -0.178839,0.00115 4.1086779,4.1086779 0 0 0 -0.409265,0.033245 A 4.1086779,4.1086779 0 0 0 0.99001362,4.1883346 4.1086779,4.1086779 0 0 0 2.1467236,7.9244144 h -0.648877 v 1.1739077 h 2.347816 a 0.58701268,0.58701268 0 0 0 0.569756,-0.729119 l -0.586953,-2.3478164 -1.139514,0.2854534 0.165082,0.6580347 a 2.9347699,2.9347699 0 0 1 -0.769204,-1.9752178 2.9347699,2.9347699 0 0 1 2.93477,-2.93477 2.9347699,2.9347699 0 0 1 2.93477,2.93477 2.9347699,2.9347699 0 0 1 -0.860944,2.0738257 l 0.831127,0.8311266 A 4.1086779,4.1086779 0 0 0 8.7040866,3.1726236 4.1086779,4.1086779 0 0 0 5.0711336,0.88215292 Z m -0.05159,2.93359608 a 1.173908,1.173908 0 0 0 -1.173907,1.173908 1.173908,1.173908 0 0 0 1.173907,1.173908 1.173908,1.173908 0 0 0 1.173909,-1.173908 1.173908,1.173908 0 0 0 -1.173909,-1.173908 z"
+ fill="#e0e0e0"
+ fill-opacity="0.99608"
+ id="path1165"
+ style="fill:#ff2b88;fill-opacity:1;stroke-width:0.586954" />
+</svg>
diff --git a/editor/icons/KeyTrackScale.svg b/editor/icons/KeyTrackScale.svg
new file mode 100644
index 0000000000..9269ccbca2
--- /dev/null
+++ b/editor/icons/KeyTrackScale.svg
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ height="10"
+ viewBox="0 0 10 10"
+ width="10"
+ version="1.1"
+ id="svg12"
+ sodipodi:docname="KeyTrackScale.svg"
+ inkscape:version="1.1 (1:1.1+202106031931+af4d65493e)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs16" />
+ <sodipodi:namedview
+ id="namedview14"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ showgrid="false"
+ inkscape:zoom="42.2"
+ inkscape:cx="1.9075829"
+ inkscape:cy="5.8175355"
+ inkscape:window-width="1848"
+ inkscape:window-height="1016"
+ inkscape:window-x="72"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg12" />
+ <ellipse
+ style="fill:none;fill-opacity:0.401212;stroke:none;stroke-width:4.7811;stroke-linejoin:round"
+ id="path921"
+ cx="-0.88986981"
+ cy="6.0959954"
+ rx="1.2495773"
+ ry="1.0867468" />
+ <path
+ d="M 5.5742494,0.94786723 A 0.58774585,0.58774585 0 0 0 4.9865036,1.535613 0.58774585,0.58774585 0 0 0 5.5742494,2.1233589 h 1.519852 L 6.334146,2.8833142 7.1652774,3.7144456 7.9252328,2.9544903 V 4.4743422 A 0.58774585,0.58774585 0 0 0 8.5129787,5.0620881 0.58774585,0.58774585 0 0 0 9.1007245,4.4743422 V 1.535613 A 0.58780462,0.58780462 0 0 0 8.5129787,0.94786723 Z M 4.9865036,3.8865964 A 1.1754917,1.1754917 0 0 0 3.8110119,5.0620881 1.1754917,1.1754917 0 0 0 4.9865036,6.2375798 1.1754917,1.1754917 0 0 0 6.1619953,5.0620881 1.1754917,1.1754917 0 0 0 4.9865036,3.8865964 Z M 1.4600285,5.0620881 A 0.58774585,0.58774585 0 0 0 0.8722826,5.6498339 V 8.5885638 A 0.58780462,0.58780462 0 0 0 1.4600285,9.1763092 H 4.3987577 A 0.58774585,0.58774585 0 0 0 4.9865036,8.5885638 0.58774585,0.58774585 0 0 0 4.3987577,8.0008173 H 2.8789057 L 3.6388611,7.2408619 2.8077297,6.4097305 2.0477743,7.1696859 V 5.6498339 A 0.58774585,0.58774585 0 0 0 1.4600285,5.0620881 Z"
+ fill="#e0e0e0"
+ fill-opacity="0.99608"
+ id="path7"
+ style="fill:#eac840;fill-opacity:1;stroke-width:0.587745" />
+</svg>
diff --git a/editor/icons/KeyXPosition.svg b/editor/icons/KeyXPosition.svg
new file mode 100644
index 0000000000..5816a241c9
--- /dev/null
+++ b/editor/icons/KeyXPosition.svg
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ height="10"
+ viewBox="0 0 10 10"
+ width="10"
+ version="1.1"
+ id="svg1678"
+ sodipodi:docname="KeyXPosition.svg"
+ inkscape:version="1.1 (1:1.1+202106031931+af4d65493e)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs1682" />
+ <sodipodi:namedview
+ id="namedview1680"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ showgrid="false"
+ inkscape:zoom="84.4"
+ inkscape:cx="4.9940758"
+ inkscape:cy="5.0059242"
+ inkscape:window-width="1848"
+ inkscape:window-height="1016"
+ inkscape:window-x="72"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg1678" />
+ <rect
+ fill="#ea7940"
+ height="6.1027"
+ ry=".76286"
+ transform="matrix(.70710678 -.70710678 .70710678 .70710678 0 -1042.4)"
+ width="6.1027"
+ x="-740.13947"
+ y="741.10779"
+ id="rect1676" />
+</svg>
diff --git a/editor/icons/KeyXRotation.svg b/editor/icons/KeyXRotation.svg
new file mode 100644
index 0000000000..6cd5a67ac0
--- /dev/null
+++ b/editor/icons/KeyXRotation.svg
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ height="10"
+ viewBox="0 0 10 10"
+ width="10"
+ version="1.1"
+ id="svg1678"
+ sodipodi:docname="KeyXRotation.svg"
+ inkscape:version="1.1 (1:1.1+202106031931+af4d65493e)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs1682" />
+ <sodipodi:namedview
+ id="namedview1680"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ showgrid="false"
+ inkscape:zoom="84.4"
+ inkscape:cx="0.32582938"
+ inkscape:cy="5.0059242"
+ inkscape:window-width="1848"
+ inkscape:window-height="1016"
+ inkscape:window-x="72"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg1678" />
+ <rect
+ fill="#ea7940"
+ height="6.1027002"
+ ry="0.76286"
+ transform="rotate(-45,-1258.2881,-521.2)"
+ width="6.1030002"
+ x="-740.13947"
+ y="741.10779"
+ id="rect1676"
+ style="fill:#ee3c94;fill-opacity:1" />
+</svg>
diff --git a/editor/icons/KeyXScale.svg b/editor/icons/KeyXScale.svg
new file mode 100644
index 0000000000..588fa5d2f3
--- /dev/null
+++ b/editor/icons/KeyXScale.svg
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ height="10"
+ viewBox="0 0 10 10"
+ width="10"
+ version="1.1"
+ id="svg1678"
+ sodipodi:docname="KeyXScale.svg"
+ inkscape:version="1.1 (1:1.1+202106031931+af4d65493e)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs1682" />
+ <sodipodi:namedview
+ id="namedview1680"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ showgrid="false"
+ inkscape:zoom="84.4"
+ inkscape:cx="2.6658768"
+ inkscape:cy="5.0059242"
+ inkscape:window-width="1473"
+ inkscape:window-height="752"
+ inkscape:window-x="122"
+ inkscape:window-y="114"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg1678" />
+ <rect
+ fill="#ea7940"
+ height="6.1027002"
+ ry="0.76286"
+ transform="rotate(-45,-1258.2881,-521.2)"
+ width="6.1030002"
+ x="-740.13947"
+ y="741.10779"
+ id="rect1676"
+ style="fill:#dbbf4f;fill-opacity:1" />
+</svg>
diff --git a/editor/import/editor_import_collada.cpp b/editor/import/editor_import_collada.cpp
index 4b01595028..d1bacf54de 100644
--- a/editor/import/editor_import_collada.cpp
+++ b/editor/import/editor_import_collada.cpp
@@ -91,8 +91,8 @@ struct ColladaImport {
Error _create_mesh_surfaces(bool p_optimize, Ref<ImporterMesh> &p_mesh, const Map<String, Collada::NodeGeometry::Material> &p_material_map, const Collada::MeshData &meshdata, const Transform3D &p_local_xform, const Vector<int> &bone_remap, const Collada::SkinControllerData *p_skin_controller, const Collada::MorphControllerData *p_morph_data, Vector<Ref<ImporterMesh>> p_morph_meshes = Vector<Ref<ImporterMesh>>(), bool p_use_compression = false, bool p_use_mesh_material = false);
Error load(const String &p_path, int p_flags, bool p_force_make_tangents = false, bool p_use_compression = false);
void _fix_param_animation_tracks();
- void create_animation(int p_clip, bool p_make_tracks_in_all_bones, bool p_import_value_tracks);
- void create_animations(bool p_make_tracks_in_all_bones, bool p_import_value_tracks);
+ void create_animation(int p_clip, bool p_import_value_tracks);
+ void create_animations(bool p_import_value_tracks);
Set<String> tracks_in_clips;
Vector<String> missing_textures;
@@ -120,6 +120,15 @@ Error ColladaImport::_populate_skeleton(Skeleton3D *p_skeleton, Collada::Node *p
skeleton_bone_map[p_skeleton][joint->sid] = r_bone;
+ {
+ Transform3D xform = joint->compute_transform(collada);
+ collada.fix_transform(xform) * joint->post_transform;
+
+ p_skeleton->set_bone_pose_position(r_bone, xform.origin);
+ p_skeleton->set_bone_pose_rotation(r_bone, xform.basis.get_rotation_quaternion());
+ p_skeleton->set_bone_pose_scale(r_bone, xform.basis.get_scale());
+ }
+
if (collada.state.bone_rest_map.has(joint->sid)) {
p_skeleton->set_bone_rest(r_bone, collada.fix_transform(collada.state.bone_rest_map[joint->sid]));
//should map this bone to something for animation?
@@ -1384,7 +1393,7 @@ void ColladaImport::_fix_param_animation_tracks() {
}
}
-void ColladaImport::create_animations(bool p_make_tracks_in_all_bones, bool p_import_value_tracks) {
+void ColladaImport::create_animations(bool p_import_value_tracks) {
_fix_param_animation_tracks();
for (int i = 0; i < collada.state.animation_clips.size(); i++) {
for (int j = 0; j < collada.state.animation_clips[i].tracks.size(); j++) {
@@ -1417,13 +1426,13 @@ void ColladaImport::create_animations(bool p_make_tracks_in_all_bones, bool p_im
}
}
- create_animation(-1, p_make_tracks_in_all_bones, p_import_value_tracks);
+ create_animation(-1, p_import_value_tracks);
for (int i = 0; i < collada.state.animation_clips.size(); i++) {
- create_animation(i, p_make_tracks_in_all_bones, p_import_value_tracks);
+ create_animation(i, p_import_value_tracks);
}
}
-void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones, bool p_import_value_tracks) {
+void ColladaImport::create_animation(int p_clip, bool p_import_value_tracks) {
Ref<Animation> animation = Ref<Animation>(memnew(Animation));
if (p_clip == -1) {
@@ -1522,10 +1531,55 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones
continue;
}
- animation->add_track(Animation::TYPE_TRANSFORM3D);
- int track = animation->get_track_count() - 1;
- animation->track_set_path(track, path);
- animation->track_set_imported(track, true); //helps merging later
+ bool has_position = false;
+ bool has_rotation = false;
+ bool has_scale = false;
+
+ for (int i = 0; cn->xform_list.size(); i++) {
+ switch (cn->xform_list[i].op) {
+ case Collada::Node::XForm::OP_ROTATE: {
+ has_rotation = true;
+ } break;
+ case Collada::Node::XForm::OP_SCALE: {
+ has_scale = true;
+ } break;
+ case Collada::Node::XForm::OP_TRANSLATE: {
+ has_position = true;
+ } break;
+ case Collada::Node::XForm::OP_MATRIX: {
+ has_position = true;
+ has_rotation = true;
+ has_scale = true;
+ } break;
+ case Collada::Node::XForm::OP_VISIBILITY: {
+ } break;
+ }
+ }
+
+ int base_track = animation->get_track_count();
+ int position_idx = -1;
+ if (has_position) {
+ position_idx = animation->get_track_count();
+ animation->add_track(Animation::TYPE_POSITION_3D);
+ animation->track_set_path(position_idx, path);
+ animation->track_set_imported(position_idx, true); //helps merging later
+ }
+
+ int rotation_idx = -1;
+ if (has_rotation) {
+ rotation_idx = animation->get_track_count();
+ animation->add_track(Animation::TYPE_ROTATION_3D);
+ animation->track_set_path(rotation_idx, path);
+ animation->track_set_imported(rotation_idx, true); //helps merging later
+ }
+
+ int scale_idx = -1;
+ if (has_scale) {
+ scale_idx = animation->get_track_count();
+ animation->add_track(Animation::TYPE_SCALE_3D);
+ animation->track_set_path(scale_idx, path);
+ animation->track_set_imported(scale_idx, true); //helps merging later
+ }
Vector<real_t> snapshots = base_snapshots;
@@ -1594,22 +1648,21 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones
Transform3D xform = cn->compute_transform(collada);
xform = collada.fix_transform(xform) * cn->post_transform;
- if (nm.bone >= 0) {
- //make bone transform relative to rest (in case of skeleton)
- Skeleton3D *sk = Object::cast_to<Skeleton3D>(nm.node);
- if (sk) {
- xform = sk->get_bone_rest(nm.bone).affine_inverse() * xform;
- } else {
- ERR_PRINT("Collada: Invalid skeleton");
- }
- }
-
Vector3 s = xform.basis.get_scale();
bool singular_matrix = Math::is_zero_approx(s.x) || Math::is_zero_approx(s.y) || Math::is_zero_approx(s.z);
- Quaternion q = singular_matrix ? Quaternion() : xform.basis.get_rotation_quaternion();
+ Quaternion q = singular_matrix ? Quaternion() :
+ xform.basis.get_rotation_quaternion();
Vector3 l = xform.origin;
- animation->transform_track_insert_key(track, snapshots[i], l, q, s);
+ if (position_idx >= 0) {
+ animation->position_track_insert_key(position_idx, snapshots[i], l);
+ }
+ if (rotation_idx >= 0) {
+ animation->rotation_track_insert_key(rotation_idx, snapshots[i], q);
+ }
+ if (scale_idx >= 0) {
+ animation->scale_track_insert_key(scale_idx, snapshots[i], s);
+ }
}
if (nm.bone >= 0) {
@@ -1621,48 +1674,15 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones
if (found_anim) {
tracks_found = true;
} else {
- animation->remove_track(track);
- }
- }
-
- if (p_make_tracks_in_all_bones) {
- //some bones may lack animation, but since we don't store pose as a property, we must add keyframes!
- for (const KeyValue<String, bool> &E : bones_with_animation) {
- if (E.value) {
- continue;
+ if (position_idx >= 0) {
+ animation->remove_track(base_track);
}
-
- NodeMap &nm = node_map[E.key];
- String path = scene->get_path_to(nm.node);
- ERR_CONTINUE(nm.bone < 0);
- Skeleton3D *sk = static_cast<Skeleton3D *>(nm.node);
- String name = sk->get_bone_name(nm.bone);
- path = path + ":" + name;
-
- Collada::Node *cn = collada.state.scene_map[E.key];
- if (cn->ignore_anim) {
- WARN_PRINT("Collada: Ignoring animation on node: " + path);
- continue;
+ if (rotation_idx >= 0) {
+ animation->remove_track(base_track);
+ }
+ if (scale_idx >= 0) {
+ animation->remove_track(base_track);
}
-
- animation->add_track(Animation::TYPE_TRANSFORM3D);
- int track = animation->get_track_count() - 1;
- animation->track_set_path(track, path);
- animation->track_set_imported(track, true); //helps merging later
-
- Transform3D xform = cn->compute_transform(collada);
- xform = collada.fix_transform(xform) * cn->post_transform;
-
- xform = sk->get_bone_rest(nm.bone).affine_inverse() * xform;
-
- Vector3 s = xform.basis.get_scale();
- bool singular_matrix = Math::is_zero_approx(s.x) || Math::is_zero_approx(s.y) || Math::is_zero_approx(s.z);
- Quaternion q = singular_matrix ? Quaternion() : xform.basis.get_rotation_quaternion();
- Vector3 l = xform.origin;
-
- animation->transform_track_insert_key(track, 0, l, q, s);
-
- tracks_found = true;
}
}
@@ -1773,7 +1793,7 @@ Node *EditorSceneImporterCollada::import_scene(const String &p_path, uint32_t p_
}
if (p_flags & IMPORT_ANIMATION) {
- state.create_animations(true, true);
+ state.create_animations(true);
AnimationPlayer *ap = memnew(AnimationPlayer);
for (int i = 0; i < state.animations.size(); i++) {
String name;
@@ -1800,7 +1820,7 @@ Ref<Animation> EditorSceneImporterCollada::import_animation(const String &p_path
Error err = state.load(p_path, Collada::IMPORT_FLAG_ANIMATION, p_flags & EditorSceneImporter::IMPORT_GENERATE_TANGENT_ARRAYS);
ERR_FAIL_COND_V_MSG(err != OK, RES(), "Cannot load animation from file '" + p_path + "'.");
- state.create_animations(true, true);
+ state.create_animations(true);
if (state.scene) {
memdelete(state.scene);
}
diff --git a/editor/import/editor_importer_bake_reset.cpp b/editor/import/editor_importer_bake_reset.cpp
deleted file mode 100644
index 451a07351c..0000000000
--- a/editor/import/editor_importer_bake_reset.cpp
+++ /dev/null
@@ -1,234 +0,0 @@
-/*************************************************************************/
-/* editor_importer_bake_reset.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 "editor/import/editor_importer_bake_reset.h"
-
-#include "core/error/error_list.h"
-#include "core/error/error_macros.h"
-#include "core/math/transform_3d.h"
-#include "resource_importer_scene.h"
-#include "scene/3d/importer_mesh_instance_3d.h"
-#include "scene/3d/mesh_instance_3d.h"
-#include "scene/3d/node_3d.h"
-#include "scene/3d/skeleton_3d.h"
-#include "scene/animation/animation_player.h"
-
-// Given that an engineering team has made a reference character, one wants ten animators to create animations.
-// Currently, a tech artist needs to combine the ten files into one exported gltf2 to import into Godot Engine.
-// We bake the RESET animation and then set it to identity,
-// so that rigs with corresponding RESET animation can have their animations transferred with ease.
-//
-// The original algorithm for the code was used to change skeleton bone rolls to be parent to child.
-//
-// Reference https://github.com/godotengine/godot-proposals/issues/2961
-void BakeReset::_bake_animation_pose(Node *scene, const String &p_bake_anim) {
- Map<StringName, BakeResetRestBone> r_rest_bones;
- Vector<Node3D *> r_meshes;
- List<Node *> queue;
- queue.push_back(scene);
- while (!queue.is_empty()) {
- List<Node *>::Element *E = queue.front();
- Node *node = E->get();
- AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(node);
- // Step 1: import scene with animations into the rest bones data structure.
- _fetch_reset_animation(ap, r_rest_bones, p_bake_anim);
-
- int child_count = node->get_child_count();
- for (int i = 0; i < child_count; i++) {
- queue.push_back(node->get_child(i));
- }
- queue.pop_front();
- }
-
- queue.push_back(scene);
- while (!queue.is_empty()) {
- List<Node *>::Element *E = queue.front();
- Node *node = E->get();
- ImporterMeshInstance3D *editor_mesh_3d = scene->cast_to<ImporterMeshInstance3D>(node);
- MeshInstance3D *mesh_3d = scene->cast_to<MeshInstance3D>(node);
- if (scene->cast_to<Skeleton3D>(node)) {
- Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(node);
-
- // Step 2: Bake the RESET animation from the RestBone to the skeleton.
- _fix_skeleton(skeleton, r_rest_bones);
- }
- if (editor_mesh_3d) {
- NodePath path = editor_mesh_3d->get_skeleton_path();
- if (!path.is_empty() && editor_mesh_3d->get_node_or_null(path) && Object::cast_to<Skeleton3D>(editor_mesh_3d->get_node_or_null(path))) {
- r_meshes.push_back(editor_mesh_3d);
- }
- } else if (mesh_3d) {
- NodePath path = mesh_3d->get_skeleton_path();
- if (!path.is_empty() && mesh_3d->get_node_or_null(path) && Object::cast_to<Skeleton3D>(mesh_3d->get_node_or_null(path))) {
- r_meshes.push_back(mesh_3d);
- }
- }
- int child_count = node->get_child_count();
- for (int i = 0; i < child_count; i++) {
- queue.push_back(node->get_child(i));
- }
- queue.pop_front();
- }
-
- queue.push_back(scene);
- while (!queue.is_empty()) {
- List<Node *>::Element *E = queue.front();
- Node *node = E->get();
- AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(node);
- if (ap) {
- // Step 3: Key all RESET animation frames to identity.
- _align_animations(ap, r_rest_bones);
- }
-
- int child_count = node->get_child_count();
- for (int i = 0; i < child_count; i++) {
- queue.push_back(node->get_child(i));
- }
- queue.pop_front();
- }
-}
-
-void BakeReset::_align_animations(AnimationPlayer *p_ap, const Map<StringName, BakeResetRestBone> &r_rest_bones) {
- ERR_FAIL_NULL(p_ap);
- List<StringName> anim_names;
- p_ap->get_animation_list(&anim_names);
- for (List<StringName>::Element *anim_i = anim_names.front(); anim_i; anim_i = anim_i->next()) {
- Ref<Animation> a = p_ap->get_animation(anim_i->get());
- ERR_CONTINUE(a.is_null());
- for (const KeyValue<StringName, BakeResetRestBone> &rest_bone_i : r_rest_bones) {
- int track = a->find_track(NodePath(rest_bone_i.key));
- if (track == -1) {
- continue;
- }
- int new_track = a->add_track(Animation::TYPE_TRANSFORM3D);
- NodePath new_path = NodePath(rest_bone_i.key);
- const BakeResetRestBone rest_bone = rest_bone_i.value;
- a->track_set_path(new_track, new_path);
- for (int key_i = 0; key_i < a->track_get_key_count(track); key_i++) {
- Vector3 loc;
- Quaternion rot;
- Vector3 scale;
- Error err = a->transform_track_get_key(track, key_i, &loc, &rot, &scale);
- ERR_CONTINUE(err);
- real_t time = a->track_get_key_time(track, key_i);
- rot.normalize();
- loc = loc - rest_bone.loc;
- rot = rest_bone.rest_delta.get_rotation_quaternion().inverse() * rot;
- rot.normalize();
- scale = Vector3(1, 1, 1) - (rest_bone.rest_delta.get_scale() - scale);
- // Apply the reverse of the rest changes to make the key be close to identity transform.
- a->transform_track_insert_key(new_track, time, loc, rot, scale);
- }
- a->remove_track(track);
- }
- }
-}
-
-void BakeReset::_fetch_reset_animation(AnimationPlayer *p_ap, Map<StringName, BakeResetRestBone> &r_rest_bones, const String &p_bake_anim) {
- if (!p_ap) {
- return;
- }
- List<StringName> anim_names;
- p_ap->get_animation_list(&anim_names);
- Node *root = p_ap->get_owner();
- ERR_FAIL_NULL(root);
- if (!p_ap->has_animation(p_bake_anim)) {
- return;
- }
- Ref<Animation> a = p_ap->get_animation(p_bake_anim);
- if (a.is_null()) {
- return;
- }
- for (int32_t track = 0; track < a->get_track_count(); track++) {
- NodePath path = a->track_get_path(track);
- String string_path = path;
- Skeleton3D *skeleton = root->cast_to<Skeleton3D>(root->get_node(string_path.get_slice(":", 0)));
- if (!skeleton) {
- continue;
- }
- String bone_name = string_path.get_slice(":", 1);
- for (int key_i = 0; key_i < a->track_get_key_count(track); key_i++) {
- Vector3 loc;
- Quaternion rot;
- Vector3 scale;
- Error err = a->transform_track_get_key(track, key_i, &loc, &rot, &scale);
- if (err != OK) {
- ERR_PRINT_ONCE("Reset animation baker can't get key.");
- continue;
- }
- rot.normalize();
- Basis rot_basis = Basis(rot, scale);
- BakeResetRestBone rest_bone;
- rest_bone.rest_delta = rot_basis;
- rest_bone.loc = loc;
- // Store the animation into the RestBone.
- r_rest_bones[StringName(String(skeleton->get_owner()->get_path_to(skeleton)) + ":" + bone_name)] = rest_bone;
- break;
- }
- }
-}
-
-void BakeReset::_fix_skeleton(Skeleton3D *p_skeleton, Map<StringName, BakeReset::BakeResetRestBone> &r_rest_bones) {
- int bone_count = p_skeleton->get_bone_count();
-
- // First iterate through all the bones and update the RestBone.
- for (int j = 0; j < bone_count; j++) {
- StringName final_path = String(p_skeleton->get_owner()->get_path_to(p_skeleton)) + String(":") + p_skeleton->get_bone_name(j);
- BakeResetRestBone &rest_bone = r_rest_bones[final_path];
- rest_bone.rest_local = p_skeleton->get_bone_rest(j);
- }
- for (int i = 0; i < bone_count; i++) {
- int parent_bone = p_skeleton->get_bone_parent(i);
- String path = p_skeleton->get_owner()->get_path_to(p_skeleton);
- StringName final_path = String(path) + String(":") + p_skeleton->get_bone_name(parent_bone);
- if (parent_bone >= 0) {
- r_rest_bones[path].children.push_back(i);
- }
- }
-
- // When we apply transform to a bone, we also have to move all of its children in the opposite direction.
- for (int i = 0; i < bone_count; i++) {
- StringName final_path = String(p_skeleton->get_owner()->get_path_to(p_skeleton)) + String(":") + p_skeleton->get_bone_name(i);
- r_rest_bones[final_path].rest_local = r_rest_bones[final_path].rest_local * Transform3D(r_rest_bones[final_path].rest_delta, r_rest_bones[final_path].loc);
- // Iterate through the children and move in the opposite direction.
- for (int j = 0; j < r_rest_bones[final_path].children.size(); j++) {
- int child_index = r_rest_bones[final_path].children[j];
- StringName children_path = String(p_skeleton->get_name()) + String(":") + p_skeleton->get_bone_name(child_index);
- r_rest_bones[children_path].rest_local = Transform3D(r_rest_bones[final_path].rest_delta, r_rest_bones[final_path].loc).affine_inverse() * r_rest_bones[children_path].rest_local;
- }
- }
-
- for (int i = 0; i < bone_count; i++) {
- StringName final_path = String(p_skeleton->get_owner()->get_path_to(p_skeleton)) + String(":") + p_skeleton->get_bone_name(i);
- ERR_CONTINUE(!r_rest_bones.has(final_path));
- Transform3D rest_transform = r_rest_bones[final_path].rest_local;
- p_skeleton->set_bone_rest(i, rest_transform);
- }
-}
diff --git a/editor/import/editor_importer_bake_reset.h b/editor/import/editor_importer_bake_reset.h
deleted file mode 100644
index e36ae86181..0000000000
--- a/editor/import/editor_importer_bake_reset.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*************************************************************************/
-/* editor_importer_bake_reset.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 RESOURCE_IMPORTER_BAKE_RESET_H
-#define RESOURCE_IMPORTER_BAKE_RESET_H
-
-#include "scene/main/node.h"
-
-class Skeleton3D;
-class AnimationPlayer;
-class BakeReset {
- struct BakeResetRestBone {
- Transform3D rest_local;
- Basis rest_delta;
- Vector3 loc;
- Vector<int> children;
- };
-
-public:
- void _bake_animation_pose(Node *scene, const String &p_bake_anim);
-
-private:
- void _fix_skeleton(Skeleton3D *p_skeleton, Map<StringName, BakeReset::BakeResetRestBone> &r_rest_bones);
- void _align_animations(AnimationPlayer *p_ap, const Map<StringName, BakeResetRestBone> &r_rest_bones);
- void _fetch_reset_animation(AnimationPlayer *p_ap, Map<StringName, BakeResetRestBone> &r_rest_bones, const String &p_bake_anim);
-};
-#endif
diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp
index 1e93113488..35f1533dd0 100644
--- a/editor/import/resource_importer_scene.cpp
+++ b/editor/import/resource_importer_scene.cpp
@@ -32,7 +32,7 @@
#include "core/io/resource_saver.h"
#include "editor/editor_node.h"
-#include "editor/import/editor_importer_bake_reset.h"
+
#include "editor/import/scene_import_settings.h"
#include "scene/3d/area_3d.h"
#include "scene/3d/collision_shape_3d.h"
@@ -851,42 +851,57 @@ void ResourceImporterScene::_create_clips(AnimationPlayer *anim, const Array &p_
new_anim->track_set_path(dtrack, default_anim->track_get_path(j));
if (kt > (from + 0.01) && k > 0) {
- if (default_anim->track_get_type(j) == Animation::TYPE_TRANSFORM3D) {
- Quaternion q;
+ if (default_anim->track_get_type(j) == Animation::TYPE_POSITION_3D) {
Vector3 p;
+ default_anim->position_track_interpolate(j, from, &p);
+ new_anim->position_track_insert_key(dtrack, 0, p);
+ } else if (default_anim->track_get_type(j) == Animation::TYPE_ROTATION_3D) {
+ Quaternion r;
+ default_anim->rotation_track_interpolate(j, from, &r);
+ new_anim->rotation_track_insert_key(dtrack, 0, r);
+ } else if (default_anim->track_get_type(j) == Animation::TYPE_SCALE_3D) {
Vector3 s;
- default_anim->transform_track_interpolate(j, from, &p, &q, &s);
- new_anim->transform_track_insert_key(dtrack, 0, p, q, s);
- }
- if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
+ default_anim->scale_track_interpolate(j, from, &s);
+ new_anim->scale_track_insert_key(dtrack, 0, s);
+ } else if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
Variant var = default_anim->value_track_interpolate(j, from);
new_anim->track_insert_key(dtrack, 0, var);
}
}
}
- if (default_anim->track_get_type(j) == Animation::TYPE_TRANSFORM3D) {
- Quaternion q;
+ if (default_anim->track_get_type(j) == Animation::TYPE_POSITION_3D) {
Vector3 p;
+ default_anim->position_track_get_key(j, k, &p);
+ new_anim->position_track_insert_key(dtrack, kt - from, p);
+ } else if (default_anim->track_get_type(j) == Animation::TYPE_ROTATION_3D) {
+ Quaternion r;
+ default_anim->rotation_track_get_key(j, k, &r);
+ new_anim->rotation_track_insert_key(dtrack, kt - from, r);
+ } else if (default_anim->track_get_type(j) == Animation::TYPE_SCALE_3D) {
Vector3 s;
- default_anim->transform_track_get_key(j, k, &p, &q, &s);
- new_anim->transform_track_insert_key(dtrack, kt - from, p, q, s);
- }
- if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
+ default_anim->scale_track_get_key(j, k, &s);
+ new_anim->scale_track_insert_key(dtrack, kt - from, s);
+ } else if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
Variant var = default_anim->track_get_key_value(j, k);
new_anim->track_insert_key(dtrack, kt - from, var);
}
}
if (dtrack != -1 && kt >= to) {
- if (default_anim->track_get_type(j) == Animation::TYPE_TRANSFORM3D) {
- Quaternion q;
+ if (default_anim->track_get_type(j) == Animation::TYPE_POSITION_3D) {
Vector3 p;
+ default_anim->position_track_interpolate(j, to, &p);
+ new_anim->position_track_insert_key(dtrack, to - from, p);
+ } else if (default_anim->track_get_type(j) == Animation::TYPE_ROTATION_3D) {
+ Quaternion r;
+ default_anim->rotation_track_interpolate(j, to, &r);
+ new_anim->rotation_track_insert_key(dtrack, to - from, r);
+ } else if (default_anim->track_get_type(j) == Animation::TYPE_SCALE_3D) {
Vector3 s;
- default_anim->transform_track_interpolate(j, to, &p, &q, &s);
- new_anim->transform_track_insert_key(dtrack, to - from, p, q, s);
- }
- if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
+ default_anim->scale_track_interpolate(j, to, &s);
+ new_anim->scale_track_insert_key(dtrack, to - from, s);
+ } else if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
Variant var = default_anim->value_track_interpolate(j, to);
new_anim->track_insert_key(dtrack, to - from, var);
}
@@ -897,16 +912,25 @@ void ResourceImporterScene::_create_clips(AnimationPlayer *anim, const Array &p_
new_anim->add_track(default_anim->track_get_type(j));
dtrack = new_anim->get_track_count() - 1;
new_anim->track_set_path(dtrack, default_anim->track_get_path(j));
- if (default_anim->track_get_type(j) == Animation::TYPE_TRANSFORM3D) {
- Quaternion q;
+ if (default_anim->track_get_type(j) == Animation::TYPE_POSITION_3D) {
Vector3 p;
+ default_anim->position_track_interpolate(j, from, &p);
+ new_anim->position_track_insert_key(dtrack, 0, p);
+ default_anim->position_track_interpolate(j, to, &p);
+ new_anim->position_track_insert_key(dtrack, to - from, p);
+ } else if (default_anim->track_get_type(j) == Animation::TYPE_ROTATION_3D) {
+ Quaternion r;
+ default_anim->rotation_track_interpolate(j, from, &r);
+ new_anim->rotation_track_insert_key(dtrack, 0, r);
+ default_anim->rotation_track_interpolate(j, to, &r);
+ new_anim->rotation_track_insert_key(dtrack, to - from, r);
+ } else if (default_anim->track_get_type(j) == Animation::TYPE_SCALE_3D) {
Vector3 s;
- default_anim->transform_track_interpolate(j, from, &p, &q, &s);
- new_anim->transform_track_insert_key(dtrack, 0, p, q, s);
- default_anim->transform_track_interpolate(j, to, &p, &q, &s);
- new_anim->transform_track_insert_key(dtrack, to - from, p, q, s);
- }
- if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
+ default_anim->scale_track_interpolate(j, from, &s);
+ new_anim->scale_track_insert_key(dtrack, 0, s);
+ default_anim->scale_track_interpolate(j, to, &s);
+ new_anim->scale_track_insert_key(dtrack, to - from, s);
+ } else if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
Variant var = default_anim->value_track_interpolate(j, from);
new_anim->track_insert_key(dtrack, 0, var);
Variant to_var = default_anim->value_track_interpolate(j, to);
@@ -1000,6 +1024,9 @@ void ResourceImporterScene::get_internal_import_options(InternalImportCategory p
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_linear_error"), 0.05));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_angular_error"), 0.01));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_angle"), 22));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "import_tracks/position", PROPERTY_HINT_ENUM, "IfPresent,IfPresentForAll,Always,Never"), 1));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "import_tracks/rotation", PROPERTY_HINT_ENUM, "IfPresent,IfPresentForAll,Always,Never"), 1));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "import_tracks/scale", PROPERTY_HINT_ENUM, "IfPresent,IfPresentForAll,Always,Never"), 1));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slices/amount", PROPERTY_HINT_RANGE, "0,256,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 0));
for (int i = 0; i < 256; i++) {
@@ -1171,7 +1198,6 @@ void ResourceImporterScene::get_import_options(List<ImportOption> *r_options, in
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "meshes/lightmap_texel_size", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 0.1));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "skins/use_named_skins"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/import"), true));
- r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/bake_reset_animation"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "animation/fps", PROPERTY_HINT_RANGE, "1,120,1"), 15));
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "import_script/path", PROPERTY_HINT_FILE, script_ext_hint), ""));
@@ -1533,11 +1559,6 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
_pre_fix_node(scene, scene, collision_map);
_post_fix_node(scene, scene, collision_map, scanned_meshes, node_data, material_data, animation_data, fps);
- bool use_bake_reset_animation = p_options["animation/bake_reset_animation"];
- if (use_bake_reset_animation) {
- BakeReset bake_reset;
- bake_reset._bake_animation_pose(scene, "RESET");
- }
String root_type = p_options["nodes/root_type"];
root_type = root_type.split(" ")[0]; // full root_type is "ClassName (filename.gd)" for a script global class.
diff --git a/editor/import/resource_importer_scene.h b/editor/import/resource_importer_scene.h
index 2a67fa9aae..eb17193c00 100644
--- a/editor/import/resource_importer_scene.h
+++ b/editor/import/resource_importer_scene.h
@@ -65,6 +65,13 @@ public:
IMPORT_USE_NAMED_SKIN_BINDS = 16,
};
+ enum AnimationImportBoneTracks {
+ ANIMATION_IMPORT_BONE_TRACKS_IF_PRESENT,
+ ANIMATION_IMPORT_BONE_TRACKS_IF_PRESENT_FOR_ALL,
+ ANIMATION_IMPORT_BONE_TRACKS_ALWAYS,
+ ANIMATION_IMPORT_BONE_TRACKS_NEVER,
+ };
+
virtual uint32_t get_import_flags() const;
virtual void get_extensions(List<String> *r_extensions) const;
virtual Node *import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err = nullptr);
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index ea6ef8ab84..8600ef3ae8 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -3863,7 +3863,7 @@ void Node3DEditorViewport::assign_pending_data_pointers(Node3D *p_preview_node,
}
Vector3 Node3DEditorViewport::_get_instance_position(const Point2 &p_pos) const {
- const float MAX_DISTANCE = 10;
+ const float MAX_DISTANCE = 50.0;
Vector3 world_ray = _get_ray(p_pos);
Vector3 world_pos = _get_ray_pos(p_pos);
@@ -6469,7 +6469,7 @@ void Node3DEditor::snap_selected_nodes_to_floor() {
// We add a bit of margin to the from position to avoid it from snapping
// when the spatial is already on a floor and there's another floor under
// it
- from = from + Vector3(0.0, 0.2, 0.0);
+ from = from + Vector3(0.0, 1, 0.0);
Dictionary d;
@@ -6485,7 +6485,7 @@ void Node3DEditor::snap_selected_nodes_to_floor() {
Array keys = snap_data.keys();
// The maximum height an object can travel to be snapped
- const float max_snap_height = 20.0;
+ const float max_snap_height = 500.0;
// Will be set to `true` if at least one node from the selection was successfully snapped
bool snapped_to_floor = false;
diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp
index 531ffc6a73..f9c46d35c4 100644
--- a/editor/plugins/skeleton_3d_editor_plugin.cpp
+++ b/editor/plugins/skeleton_3d_editor_plugin.cpp
@@ -200,12 +200,7 @@ void BoneTransformEditor::_value_changed_transform(const String p_property_name,
}
void BoneTransformEditor::_change_transform(Transform3D p_new_transform) {
- if (property.get_slicec('/', 0) == "bones" && property.get_slicec('/', 2) == "custom_pose") {
- undo_redo->create_action(TTR("Set Custom Bone Pose Transform"), UndoRedo::MERGE_ENDS);
- undo_redo->add_undo_method(skeleton, "set_bone_custom_pose", property.get_slicec('/', 1).to_int(), skeleton->get_bone_custom_pose(property.get_slicec('/', 1).to_int()));
- undo_redo->add_do_method(skeleton, "set_bone_custom_pose", property.get_slicec('/', 1).to_int(), p_new_transform);
- undo_redo->commit_action();
- } else if (property.get_slicec('/', 0) == "bones") {
+ if (property.get_slicec('/', 0) == "bones") {
undo_redo->create_action(TTR("Set Bone Transform"), UndoRedo::MERGE_ENDS);
undo_redo->add_undo_property(skeleton, property, skeleton->get(property));
undo_redo->add_do_property(skeleton, property, p_new_transform);
@@ -236,21 +231,6 @@ void BoneTransformEditor::_update_properties() {
_update_transform_properties(tform);
}
-void BoneTransformEditor::_update_custom_pose_properties() {
- if (updating) {
- return;
- }
-
- if (!skeleton) {
- return;
- }
-
- updating = true;
-
- Transform3D tform = skeleton->get_bone_custom_pose(property.to_int());
- _update_transform_properties(tform);
-}
-
void BoneTransformEditor::_update_transform_properties(Transform3D tform) {
Basis rotation_basis = tform.get_basis();
Vector3 rotation_radians = rotation_basis.get_rotation_euler();
@@ -463,9 +443,7 @@ void Skeleton3DEditor::pose_to_rest() {
ur->add_do_method(skeleton, "set_bone_pose", selected_bone, Transform3D());
ur->add_undo_method(skeleton, "set_bone_pose", selected_bone, skeleton->get_bone_pose(selected_bone));
- ur->add_do_method(skeleton, "set_bone_custom_pose", selected_bone, Transform3D());
- ur->add_undo_method(skeleton, "set_bone_custom_pose", selected_bone, skeleton->get_bone_custom_pose(selected_bone));
- ur->add_do_method(skeleton, "set_bone_rest", selected_bone, skeleton->get_bone_rest(selected_bone) * skeleton->get_bone_custom_pose(selected_bone) * skeleton->get_bone_pose(selected_bone));
+ ur->add_do_method(skeleton, "set_bone_rest", selected_bone, skeleton->get_bone_rest(selected_bone) * skeleton->get_bone_pose(selected_bone));
ur->add_undo_method(skeleton, "set_bone_rest", selected_bone, skeleton->get_bone_rest(selected_bone));
ur->commit_action();
@@ -654,11 +632,9 @@ void Skeleton3DEditor::_joint_tree_selection_changed() {
pose_editor->set_target(bone_path + "pose");
rest_editor->set_target(bone_path + "rest");
- custom_pose_editor->set_target(bone_path + "custom_pose");
pose_editor->set_visible(true);
rest_editor->set_visible(true);
- custom_pose_editor->set_visible(true);
selected_bone = b_idx;
}
@@ -679,9 +655,6 @@ void Skeleton3DEditor::_update_properties() {
if (pose_editor) {
pose_editor->_update_properties();
}
- if (custom_pose_editor) {
- custom_pose_editor->_update_custom_pose_properties();
- }
_update_gizmo_transform();
}
@@ -820,12 +793,6 @@ void Skeleton3DEditor::create_editors() {
rest_editor->set_visible(false);
add_child(rest_editor);
rest_editor->set_transform_read_only(true);
-
- custom_pose_editor = memnew(BoneTransformEditor(skeleton));
- custom_pose_editor->set_label(TTR("Bone Custom Pose"));
- custom_pose_editor->set_visible(false);
- add_child(custom_pose_editor);
- custom_pose_editor->set_transform_read_only(true);
}
void Skeleton3DEditor::_notification(int p_what) {
@@ -1289,7 +1256,6 @@ void Skeleton3DGizmoPlugin::set_subgizmo_transform(const EditorNode3DGizmo *p_gi
if (parent_idx >= 0) {
original_to_local = original_to_local * skeleton->get_bone_global_pose(parent_idx);
}
- original_to_local = original_to_local * skeleton->get_bone_rest(p_id) * skeleton->get_bone_custom_pose(p_id);
Basis to_local = original_to_local.get_basis().inverse();
// Prepare transform.
@@ -1305,7 +1271,9 @@ void Skeleton3DGizmoPlugin::set_subgizmo_transform(const EditorNode3DGizmo *p_gi
t.origin = orig + to_local.xform(sub);
// Apply transform.
- skeleton->set_bone_pose(p_id, t);
+ skeleton->set_bone_pose_position(p_id, t.origin);
+ skeleton->set_bone_pose_rotation(p_id, t.basis.operator Quaternion());
+ skeleton->set_bone_pose_scale(p_id, t.basis.get_scale());
}
void Skeleton3DGizmoPlugin::commit_subgizmos(const EditorNode3DGizmo *p_gizmo, const Vector<int> &p_ids, const Vector<Transform3D> &p_restore, bool p_cancel) {
@@ -1516,5 +1484,5 @@ void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
}
Ref<ArrayMesh> m = surface_tool->commit();
- p_gizmo->add_mesh(m, Ref<Material>(), Transform3D(), skeleton->register_skin(Ref<Skin>()));
+ p_gizmo->add_mesh(m, Ref<Material>(), Transform3D(), skeleton->register_skin(skeleton->create_skin_from_rest_transforms()));
}
diff --git a/editor/plugins/skeleton_3d_editor_plugin.h b/editor/plugins/skeleton_3d_editor_plugin.h
index e2a1d9a628..2c21aab739 100644
--- a/editor/plugins/skeleton_3d_editor_plugin.h
+++ b/editor/plugins/skeleton_3d_editor_plugin.h
@@ -102,13 +102,12 @@ public:
void set_label(const String &p_label) { label = p_label; }
void _update_properties();
- void _update_custom_pose_properties();
void _update_transform_properties(Transform3D p_transform);
// Transform can be keyed, whether or not to show the button.
void set_keyable(const bool p_keyable);
- // When rest mode, pose and custom_pose editor are diasbled.
+ // When rest mode, pose editor are diasbled.
void set_properties_read_only(const bool p_readonly);
void set_transform_read_only(const bool p_readonly);
@@ -151,7 +150,6 @@ class Skeleton3DEditor : public VBoxContainer {
Tree *joint_tree = nullptr;
BoneTransformEditor *rest_editor = nullptr;
BoneTransformEditor *pose_editor = nullptr;
- BoneTransformEditor *custom_pose_editor = nullptr;
VSeparator *separator;
MenuButton *skeleton_options = nullptr;
diff --git a/editor/plugins/tiles/tile_atlas_view.cpp b/editor/plugins/tiles/tile_atlas_view.cpp
index f21d5098d3..c064073b77 100644
--- a/editor/plugins/tiles/tile_atlas_view.cpp
+++ b/editor/plugins/tiles/tile_atlas_view.cpp
@@ -97,15 +97,6 @@ Size2i TileAtlasView::_compute_base_tiles_control_size() {
if (texture.is_valid()) {
size = texture->get_size();
}
-
- // Extend the size to all existing tiles.
- Size2i grid_size = tile_set_atlas_source->get_atlas_grid_size();
- for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
- Vector2i tile_id = tile_set_atlas_source->get_tile_id(i);
- grid_size = grid_size.max(tile_id + Vector2i(1, 1));
- }
- size = size.max(grid_size * (tile_set_atlas_source->get_texture_region_size() + tile_set_atlas_source->get_separation()) + tile_set_atlas_source->get_margins());
-
return size;
}
@@ -213,43 +204,56 @@ void TileAtlasView::_draw_base_tiles() {
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
if (texture.is_valid()) {
Vector2i margins = tile_set_atlas_source->get_margins();
+ Vector2i separation = tile_set_atlas_source->get_separation();
Vector2i texture_region_size = tile_set_atlas_source->get_texture_region_size();
-
- // Draw the texture, square by square.
Size2i grid_size = tile_set_atlas_source->get_atlas_grid_size();
+
+ // Draw the texture where there is no tile.
for (int x = 0; x < grid_size.x; x++) {
for (int y = 0; y < grid_size.y; y++) {
Vector2i coords = Vector2i(x, y);
if (tile_set_atlas_source->get_tile_at_coords(coords) == TileSetSource::INVALID_ATLAS_COORDS) {
- Rect2i rect = Rect2i(texture_region_size * coords + margins, texture_region_size);
- base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
+ Rect2i rect = Rect2i((texture_region_size + separation) * coords + margins, texture_region_size + separation);
+ rect = rect.intersection(Rect2i(Vector2(), texture->get_size()));
+ if (rect.size.x > 0 && rect.size.y > 0) {
+ base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
+ base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));
+ }
}
}
}
// Draw the texture around the grid.
Rect2i rect;
+
// Top.
rect.position = Vector2i();
rect.set_end(Vector2i(texture->get_size().x, margins.y));
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
+ base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));
+
// Bottom
- int bottom_border = margins.y + (grid_size.y * texture_region_size.y);
+ int bottom_border = margins.y + (grid_size.y * (texture_region_size.y + separation.y));
if (bottom_border < texture->get_size().y) {
rect.position = Vector2i(0, bottom_border);
rect.set_end(texture->get_size());
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
+ base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));
}
+
// Left
rect.position = Vector2i(0, margins.y);
- rect.set_end(Vector2i(margins.x, margins.y + (grid_size.y * texture_region_size.y)));
+ rect.set_end(Vector2i(margins.x, margins.y + (grid_size.y * (texture_region_size.y + separation.y))));
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
+ base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));
+
// Right.
- int right_border = margins.x + (grid_size.x * texture_region_size.x);
+ int right_border = margins.x + (grid_size.x * (texture_region_size.x + separation.x));
if (right_border < texture->get_size().x) {
rect.position = Vector2i(right_border, margins.y);
- rect.set_end(Vector2i(texture->get_size().x, margins.y + (grid_size.y * texture_region_size.y)));
+ rect.set_end(Vector2i(texture->get_size().x, margins.y + (grid_size.y * (texture_region_size.y + separation.y))));
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
+ base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));
}
// Draw actual tiles, using their properties (modulation, etc...)
@@ -258,12 +262,30 @@ void TileAtlasView::_draw_base_tiles() {
for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(atlas_coords); frame++) {
// Update the y to max value.
- int animation_columns = tile_set_atlas_source->get_tile_animation_columns(atlas_coords);
- Vector2i frame_coords = atlas_coords + (tile_set_atlas_source->get_tile_size_in_atlas(atlas_coords) + tile_set_atlas_source->get_tile_animation_separation(atlas_coords)) * ((animation_columns > 0) ? Vector2i(frame % animation_columns, frame / animation_columns) : Vector2i(frame, 0));
- Vector2i offset_pos = (margins + (frame_coords * texture_region_size) + tile_set_atlas_source->get_tile_texture_region(atlas_coords, frame).size / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, 0));
+ Rect2i base_frame_rect = tile_set_atlas_source->get_tile_texture_region(atlas_coords, frame);
+ Vector2i offset_pos = base_frame_rect.get_center() + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, 0);
// Draw the tile.
TileMap::draw_tile(base_tiles_draw->get_canvas_item(), offset_pos, tile_set, source_id, atlas_coords, 0, frame);
+
+ // Draw, the texture in the separation areas
+ if (separation.x > 0) {
+ Rect2i right_sep_rect = Rect2i(base_frame_rect.get_position() + Vector2i(base_frame_rect.size.x, 0), Vector2i(separation.x, base_frame_rect.size.y));
+ right_sep_rect = right_sep_rect.intersection(Rect2i(Vector2(), texture->get_size()));
+ if (right_sep_rect.size.x > 0 && right_sep_rect.size.y > 0) {
+ base_tiles_draw->draw_texture_rect_region(texture, right_sep_rect, right_sep_rect);
+ base_tiles_draw->draw_rect(right_sep_rect, Color(0.0, 0.0, 0.0, 0.5));
+ }
+ }
+
+ if (separation.y > 0) {
+ Rect2i bottom_sep_rect = Rect2i(base_frame_rect.get_position() + Vector2i(0, base_frame_rect.size.y), Vector2i(base_frame_rect.size.x + separation.x, separation.y));
+ bottom_sep_rect = bottom_sep_rect.intersection(Rect2i(Vector2(), texture->get_size()));
+ if (bottom_sep_rect.size.x > 0 && bottom_sep_rect.size.y > 0) {
+ base_tiles_draw->draw_texture_rect_region(texture, bottom_sep_rect, bottom_sep_rect);
+ base_tiles_draw->draw_rect(bottom_sep_rect, Color(0.0, 0.0, 0.0, 0.5));
+ }
+ }
}
}
}
@@ -299,30 +321,6 @@ void TileAtlasView::_draw_base_tiles_texture_grid() {
}
}
-void TileAtlasView::_draw_base_tiles_dark() {
- Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
- if (texture.is_valid()) {
- Vector2i margins = tile_set_atlas_source->get_margins();
- Vector2i separation = tile_set_atlas_source->get_separation();
- Vector2i texture_region_size = tile_set_atlas_source->get_texture_region_size();
-
- Size2i grid_size = tile_set_atlas_source->get_atlas_grid_size();
-
- // Draw each tile texture region.
- for (int x = 0; x < grid_size.x; x++) {
- for (int y = 0; y < grid_size.y; y++) {
- Vector2i origin = margins + (Vector2i(x, y) * (texture_region_size + separation));
- Vector2i base_tile_coords = tile_set_atlas_source->get_tile_at_coords(Vector2i(x, y));
-
- if (base_tile_coords == TileSetSource::INVALID_ATLAS_COORDS) {
- // Draw the grid.
- base_tiles_dark->draw_rect(Rect2i(origin, texture_region_size), Color(0.0, 0.0, 0.0, 0.5), true);
- }
- }
- }
- }
-}
-
void TileAtlasView::_draw_base_tiles_shape_grid() {
// Draw the shapes.
Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color");
@@ -453,7 +451,6 @@ void TileAtlasView::set_atlas_source(TileSet *p_tile_set, TileSetAtlasSource *p_
base_tiles_draw->update();
base_tiles_texture_grid->update();
base_tiles_shape_grid->update();
- base_tiles_dark->update();
alternatives_draw->update();
background_left->update();
background_right->update();
@@ -544,7 +541,6 @@ void TileAtlasView::update() {
base_tiles_draw->update();
base_tiles_texture_grid->update();
base_tiles_shape_grid->update();
- base_tiles_dark->update();
alternatives_draw->update();
background_left->update();
background_right->update();
@@ -660,12 +656,6 @@ TileAtlasView::TileAtlasView() {
base_tiles_shape_grid->connect("draw", callable_mp(this, &TileAtlasView::_draw_base_tiles_shape_grid));
base_tiles_drawing_root->add_child(base_tiles_shape_grid);
- base_tiles_dark = memnew(Control);
- base_tiles_dark->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
- base_tiles_dark->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
- base_tiles_dark->connect("draw", callable_mp(this, &TileAtlasView::_draw_base_tiles_dark));
- base_tiles_drawing_root->add_child(base_tiles_dark);
-
// Alternative tiles.
Label *alternative_tiles_label = memnew(Label);
alternative_tiles_label->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
diff --git a/editor/plugins/tiles/tile_atlas_view.h b/editor/plugins/tiles/tile_atlas_view.h
index 5b0df366ae..e1ca3eebee 100644
--- a/editor/plugins/tiles/tile_atlas_view.h
+++ b/editor/plugins/tiles/tile_atlas_view.h
@@ -93,9 +93,6 @@ private:
Control *base_tiles_shape_grid;
void _draw_base_tiles_shape_grid();
- Control *base_tiles_dark;
- void _draw_base_tiles_dark();
-
Size2i _compute_base_tiles_control_size();
// Right side.
@@ -124,7 +121,6 @@ public:
// Left side.
void set_texture_grid_visible(bool p_visible) { base_tiles_texture_grid->set_visible(p_visible); };
- void set_dark_visible(bool p_visible) { base_tiles_dark->set_visible(p_visible); };
void set_tile_shape_grid_visible(bool p_visible) { base_tiles_shape_grid->set_visible(p_visible); };
Vector2i get_atlas_tile_coords_at_pos(const Vector2 p_pos) const;
diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp
index 1a69d19d3c..104f46f773 100644
--- a/editor/plugins/tiles/tile_data_editors.cpp
+++ b/editor/plugins/tiles/tile_data_editors.cpp
@@ -38,8 +38,16 @@
#include "editor/editor_properties.h"
#include "editor/editor_scale.h"
-void TileDataEditor::_call_tile_set_changed() {
- _tile_set_changed();
+void TileDataEditor::_tile_set_changed_plan_update() {
+ _tile_set_changed_update_needed = true;
+ call_deferred("_tile_set_changed_deferred_update");
+}
+
+void TileDataEditor::_tile_set_changed_deferred_update() {
+ if (_tile_set_changed_update_needed) {
+ _tile_set_changed();
+ _tile_set_changed_update_needed = false;
+ }
}
TileData *TileDataEditor::_get_tile_data(TileMapCell p_cell) {
@@ -59,18 +67,20 @@ TileData *TileDataEditor::_get_tile_data(TileMapCell p_cell) {
}
void TileDataEditor::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_tile_set_changed_deferred_update"), &TileDataEditor::_tile_set_changed_deferred_update);
+
ADD_SIGNAL(MethodInfo("needs_redraw"));
}
void TileDataEditor::set_tile_set(Ref<TileSet> p_tile_set) {
if (tile_set.is_valid()) {
- tile_set->disconnect("changed", callable_mp(this, &TileDataEditor::_call_tile_set_changed));
+ tile_set->disconnect("changed", callable_mp(this, &TileDataEditor::_tile_set_changed_plan_update));
}
tile_set = p_tile_set;
if (tile_set.is_valid()) {
- tile_set->connect("changed", callable_mp(this, &TileDataEditor::_call_tile_set_changed));
+ tile_set->connect("changed", callable_mp(this, &TileDataEditor::_tile_set_changed_plan_update));
}
- _call_tile_set_changed();
+ _tile_set_changed_plan_update();
}
bool DummyObject::_set(const StringName &p_name, const Variant &p_value) {
diff --git a/editor/plugins/tiles/tile_data_editors.h b/editor/plugins/tiles/tile_data_editors.h
index 99998dc779..acb4c5882d 100644
--- a/editor/plugins/tiles/tile_data_editors.h
+++ b/editor/plugins/tiles/tile_data_editors.h
@@ -45,7 +45,9 @@ class TileDataEditor : public VBoxContainer {
GDCLASS(TileDataEditor, VBoxContainer);
private:
- void _call_tile_set_changed();
+ bool _tile_set_changed_update_needed = false;
+ void _tile_set_changed_plan_update();
+ void _tile_set_changed_deferred_update();
protected:
Ref<TileSet> tile_set;
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
index 3fbd315aec..c43a854d9a 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
@@ -520,9 +520,6 @@ void TileSetAtlasSourceEditor::_update_tile_id_label() {
void TileSetAtlasSourceEditor::_update_source_inspector() {
// Update the proxy object.
atlas_source_proxy_object->edit(tile_set, tile_set_atlas_source, tile_set_atlas_source_id);
-
- // Update the "clear outside texture" button.
- tool_advanced_menu_buttom->get_popup()->set_item_disabled(0, !tile_set_atlas_source->has_tiles_outside_texture());
}
void TileSetAtlasSourceEditor::_update_fix_selected_and_hovered_tiles() {
@@ -763,7 +760,11 @@ void TileSetAtlasSourceEditor::_update_tile_data_editors() {
// Update visibility.
bool is_visible = tools_button_group->get_pressed_button() == tool_paint_button;
tile_data_editor_dropdown_button->set_visible(is_visible);
- tile_data_editor_dropdown_button->set_text(TTR("Select a property editor"));
+ if (tile_data_editors_tree->get_selected()) {
+ tile_data_editor_dropdown_button->set_text(tile_data_editors_tree->get_selected()->get_text(0));
+ } else {
+ tile_data_editor_dropdown_button->set_text(TTR("Select a property editor"));
+ }
tile_data_editors_label->set_visible(is_visible);
}
@@ -959,7 +960,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
current_tile_data_editor->forward_painting_atlas_gui_input(tile_atlas_view, tile_set_atlas_source, p_event);
}
// Update only what's needed.
- tile_set_atlas_source_changed_needs_update = false;
+ tile_set_changed_needs_update = false;
tile_atlas_control->update();
tile_atlas_control_unscaled->update();
@@ -1061,7 +1062,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
drag_current_tile = coords;
// Update only what's needed.
- tile_set_atlas_source_changed_needs_update = false;
+ tile_set_changed_needs_update = false;
_update_tile_inspector();
_update_atlas_view();
_update_tile_id_label();
@@ -1101,7 +1102,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
drag_current_tile = new_rect.position;
// Update only what's needed.
- tile_set_atlas_source_changed_needs_update = false;
+ tile_set_changed_needs_update = false;
_update_tile_inspector();
_update_atlas_view();
_update_tile_id_label();
@@ -1600,9 +1601,6 @@ void TileSetAtlasSourceEditor::_menu_option(int p_option) {
undo_redo->commit_action();
_update_tile_id_label();
} break;
- case ADVANCED_CLEANUP_TILES_OUTSIDE_TEXTURE: {
- tile_set_atlas_source->clear_tiles_outside_texture();
- } break;
case ADVANCED_AUTO_CREATE_TILES: {
_auto_create_tiles();
} break;
@@ -2012,12 +2010,12 @@ void TileSetAtlasSourceEditor::_tile_alternatives_control_unscaled_draw() {
}
}
-void TileSetAtlasSourceEditor::_tile_set_atlas_source_changed() {
- tile_set_atlas_source_changed_needs_update = true;
+void TileSetAtlasSourceEditor::_tile_set_changed() {
+ tile_set_changed_needs_update = true;
}
void TileSetAtlasSourceEditor::_tile_proxy_object_changed(String p_what) {
- tile_set_atlas_source_changed_needs_update = false; // Avoid updating too many things.
+ tile_set_changed_needs_update = false; // Avoid updating too many things.
_update_atlas_view();
}
@@ -2035,30 +2033,66 @@ void TileSetAtlasSourceEditor::_undo_redo_inspector_callback(Object *p_undo_redo
#define ADD_UNDO(obj, property) undo_redo->add_undo_property(obj, property, obj->get(property));
- AtlasTileProxyObject *tile_data = Object::cast_to<AtlasTileProxyObject>(p_edited);
- if (tile_data) {
+ undo_redo->start_force_keep_in_merge_ends();
+ AtlasTileProxyObject *tile_data_proxy = Object::cast_to<AtlasTileProxyObject>(p_edited);
+ if (tile_data_proxy) {
Vector<String> components = String(p_property).split("/", true, 2);
if (components.size() == 2 && components[1] == "polygons_count") {
int layer_index = components[0].trim_prefix("physics_layer_").to_int();
int new_polygons_count = p_new_value;
- int old_polygons_count = tile_data->get(vformat("physics_layer_%d/polygons_count", layer_index));
+ int old_polygons_count = tile_data_proxy->get(vformat("physics_layer_%d/polygons_count", layer_index));
if (new_polygons_count < old_polygons_count) {
for (int i = new_polygons_count - 1; i < old_polygons_count; i++) {
- ADD_UNDO(tile_data, vformat("physics_layer_%d/polygon_%d/points", layer_index, i));
- ADD_UNDO(tile_data, vformat("physics_layer_%d/polygon_%d/one_way", layer_index, i));
- ADD_UNDO(tile_data, vformat("physics_layer_%d/polygon_%d/one_way_margin", layer_index, i));
+ ADD_UNDO(tile_data_proxy, vformat("physics_layer_%d/polygon_%d/points", layer_index, i));
+ ADD_UNDO(tile_data_proxy, vformat("physics_layer_%d/polygon_%d/one_way", layer_index, i));
+ ADD_UNDO(tile_data_proxy, vformat("physics_layer_%d/polygon_%d/one_way_margin", layer_index, i));
}
}
} else if (p_property == "terrain_set") {
- int current_terrain_set = tile_data->get("terrain_set");
+ int current_terrain_set = tile_data_proxy->get("terrain_set");
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
if (tile_set->is_valid_peering_bit_terrain(current_terrain_set, bit)) {
- ADD_UNDO(tile_data, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]));
+ ADD_UNDO(tile_data_proxy, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]));
}
}
}
}
+
+ TileSetAtlasSourceProxyObject *atlas_source_proxy = Object::cast_to<TileSetAtlasSourceProxyObject>(p_edited);
+ if (atlas_source_proxy) {
+ TileSetAtlasSource *atlas_source = atlas_source_proxy->get_edited();
+ ERR_FAIL_COND(!atlas_source);
+
+ PackedVector2Array arr;
+ if (p_property == "texture") {
+ arr = atlas_source->get_tiles_to_be_removed_on_change(p_new_value, atlas_source->get_margins(), atlas_source->get_separation(), atlas_source->get_texture_region_size());
+ } else if (p_property == "margins") {
+ arr = atlas_source->get_tiles_to_be_removed_on_change(atlas_source->get_texture(), p_new_value, atlas_source->get_separation(), atlas_source->get_texture_region_size());
+ } else if (p_property == "separation") {
+ arr = atlas_source->get_tiles_to_be_removed_on_change(atlas_source->get_texture(), atlas_source->get_margins(), p_new_value, atlas_source->get_texture_region_size());
+ } else if (p_property == "texture_region_size") {
+ arr = atlas_source->get_tiles_to_be_removed_on_change(atlas_source->get_texture(), atlas_source->get_margins(), atlas_source->get_separation(), p_new_value);
+ }
+
+ if (!arr.is_empty()) {
+ // Get all properties assigned to a tile.
+ List<PropertyInfo> properties;
+ atlas_source->get_property_list(&properties);
+
+ for (int i = 0; i < arr.size(); i++) {
+ Vector2i coords = arr[i];
+ String prefix = vformat("%d:%d/", coords.x, coords.y);
+ for (PropertyInfo pi : properties) {
+ if (pi.name.begins_with(prefix)) {
+ ADD_UNDO(atlas_source, pi.name);
+ }
+ }
+ }
+ }
+ }
+ undo_redo->end_force_keep_in_merge_ends();
+
#undef ADD_UNDO
}
@@ -2073,8 +2107,8 @@ void TileSetAtlasSourceEditor::edit(Ref<TileSet> p_tile_set, TileSetAtlasSource
}
// Remove listener for old objects.
- if (tile_set_atlas_source) {
- tile_set_atlas_source->disconnect("changed", callable_mp(this, &TileSetAtlasSourceEditor::_tile_set_atlas_source_changed));
+ if (tile_set.is_valid()) {
+ tile_set->disconnect("changed", callable_mp(this, &TileSetAtlasSourceEditor::_tile_set_changed));
}
// Clear the selection.
@@ -2086,8 +2120,8 @@ void TileSetAtlasSourceEditor::edit(Ref<TileSet> p_tile_set, TileSetAtlasSource
tile_set_atlas_source_id = p_source_id;
// Add the listener again.
- if (tile_set_atlas_source) {
- tile_set_atlas_source->connect("changed", callable_mp(this, &TileSetAtlasSourceEditor::_tile_set_atlas_source_changed));
+ if (tile_set.is_valid()) {
+ tile_set->connect("changed", callable_mp(this, &TileSetAtlasSourceEditor::_tile_set_changed));
}
// Update everything.
@@ -2228,7 +2262,7 @@ void TileSetAtlasSourceEditor::_notification(int p_what) {
resize_handle_disabled = get_theme_icon(SNAME("EditorHandleDisabled"), SNAME("EditorIcons"));
break;
case NOTIFICATION_INTERNAL_PROCESS:
- if (tile_set_atlas_source_changed_needs_update) {
+ if (tile_set_changed_needs_update) {
// Update everything.
_update_source_inspector();
@@ -2241,7 +2275,7 @@ void TileSetAtlasSourceEditor::_notification(int p_what) {
_update_tile_data_editors();
_update_current_tile_data_editor();
- tile_set_atlas_source_changed_needs_update = false;
+ tile_set_changed_needs_update = false;
}
break;
default:
@@ -2406,8 +2440,6 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
tool_advanced_menu_buttom = memnew(MenuButton);
tool_advanced_menu_buttom->set_flat(true);
- tool_advanced_menu_buttom->get_popup()->add_item(TTR("Cleanup Tiles Outside Texture"), ADVANCED_CLEANUP_TILES_OUTSIDE_TEXTURE);
- tool_advanced_menu_buttom->get_popup()->set_item_disabled(0, true);
tool_advanced_menu_buttom->get_popup()->add_item(TTR("Create Tiles in Non-Transparent Texture Regions"), ADVANCED_AUTO_CREATE_TILES);
tool_advanced_menu_buttom->get_popup()->add_item(TTR("Remove Tiles in Fully Transparent Texture Regions"), ADVANCED_AUTO_REMOVE_TILES);
tool_advanced_menu_buttom->get_popup()->connect("id_pressed", callable_mp(this, &TileSetAtlasSourceEditor::_menu_option));
@@ -2481,6 +2513,8 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
tile_atlas_view_missing_source_label->set_v_size_flags(SIZE_EXPAND_FILL);
tile_atlas_view_missing_source_label->hide();
right_panel->add_child(tile_atlas_view_missing_source_label);
+
+ EditorNode::get_singleton()->get_editor_data().add_undo_redo_inspector_hook_callback(callable_mp(this, &TileSetAtlasSourceEditor::_undo_redo_inspector_callback));
}
TileSetAtlasSourceEditor::~TileSetAtlasSourceEditor() {
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.h b/editor/plugins/tiles/tile_set_atlas_source_editor.h
index 6448b55feb..ea6f2847ae 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.h
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.h
@@ -78,6 +78,7 @@ private:
int get_id();
void edit(Ref<TileSet> p_tile_set, TileSetAtlasSource *p_tile_set_atlas_source, int p_source_id);
+ TileSetAtlasSource *get_edited() { return tile_set_atlas_source; };
};
// -- Proxy object for a tile, needed by the inspector --
@@ -112,7 +113,7 @@ private:
UndoRedo *undo_redo = EditorNode::get_undo_redo();
- bool tile_set_atlas_source_changed_needs_update = false;
+ bool tile_set_changed_needs_update = false;
// -- Properties painting --
VBoxContainer *tile_data_painting_editor_container;
@@ -189,7 +190,6 @@ private:
TILE_CREATE_ALTERNATIVE,
TILE_DELETE,
- ADVANCED_CLEANUP_TILES_OUTSIDE_TEXTURE,
ADVANCED_AUTO_CREATE_TILES,
ADVANCED_AUTO_REMOVE_TILES,
};
@@ -263,7 +263,7 @@ private:
void _auto_remove_tiles();
AcceptDialog *confirm_auto_create_tiles;
- void _tile_set_atlas_source_changed();
+ void _tile_set_changed();
void _tile_proxy_object_changed(String p_what);
void _atlas_source_proxy_object_changed(String p_what);
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index b1b64564bb..48f64e041c 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -777,7 +777,7 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) {
expand->connect("pressed", callable_mp(VisualShaderEditor::get_singleton(), &VisualShaderEditor::_expand_output_port), varray(p_id, i, !vsnode->_is_output_port_expanded(i)), CONNECT_DEFERRED);
hb->add_child(expand);
}
- if (visual_shader->get_shader_type() == VisualShader::TYPE_FRAGMENT && port_right != VisualShaderNode::PORT_TYPE_TRANSFORM && port_right != VisualShaderNode::PORT_TYPE_SAMPLER) {
+ if (vsnode->has_output_port_preview(i) && port_right != VisualShaderNode::PORT_TYPE_TRANSFORM && port_right != VisualShaderNode::PORT_TYPE_SAMPLER) {
TextureButton *preview = memnew(TextureButton);
preview->set_toggle_mode(true);
preview->set_normal_texture(VisualShaderEditor::get_singleton()->get_theme_icon(SNAME("GuiVisibilityHidden"), SNAME("EditorIcons")));
diff --git a/modules/fbx/data/fbx_skeleton.cpp b/modules/fbx/data/fbx_skeleton.cpp
index 3dc163964c..11eed2576f 100644
--- a/modules/fbx/data/fbx_skeleton.cpp
+++ b/modules/fbx/data/fbx_skeleton.cpp
@@ -104,6 +104,13 @@ void FBXSkeleton::init_skeleton(const ImportState &state) {
print_verbose("working on bone: " + itos(bone_index) + " bone name:" + bone->bone_name);
skeleton->set_bone_rest(bone->godot_bone_id, get_unscaled_transform(bone->node->pivot_transform->LocalTransform, state.scale));
+ {
+ Transform3D base_xform = bone->node->pivot_transform->LocalTransform;
+
+ skeleton->set_bone_pose_position(bone_index, base_xform.origin);
+ skeleton->set_bone_pose_rotation(bone_index, base_xform.basis.get_rotation_quaternion());
+ skeleton->set_bone_pose_scale(bone_index, base_xform.basis.get_scale());
+ }
// lookup parent ID
if (bone->valid_parent && state.fbx_bone_map.has(bone->parent_bone_id)) {
diff --git a/modules/fbx/editor_scene_importer_fbx.cpp b/modules/fbx/editor_scene_importer_fbx.cpp
index e4de204cf1..879f281292 100644
--- a/modules/fbx/editor_scene_importer_fbx.cpp
+++ b/modules/fbx/editor_scene_importer_fbx.cpp
@@ -1011,9 +1011,7 @@ Node3D *EditorSceneImporterFBX::_generate_scene(
// track count is 5.
// next track id is 5.
const uint64_t target_id = track.key;
- int track_idx = animation->add_track(Animation::TYPE_TRANSFORM3D);
- // animation->track_set_path(track_idx, node_path);
Ref<FBXBone> bone;
// note we must not run the below code if the entry doesn't exist, it will create dummy entries which is very bad.
@@ -1037,22 +1035,21 @@ Node3D *EditorSceneImporterFBX::_generate_scene(
// if this is a skeleton mapped track we can just set the path for the track.
// todo: implement node paths here at some
+ NodePath track_path;
if (state.fbx_bone_map.size() > 0 && state.fbx_bone_map.has(target_id)) {
if (bone->fbx_skeleton.is_valid() && bone.is_valid()) {
Ref<FBXSkeleton> fbx_skeleton = bone->fbx_skeleton;
String bone_path = state.root->get_path_to(fbx_skeleton->skeleton);
bone_path += ":" + fbx_skeleton->skeleton->get_bone_name(bone->godot_bone_id);
print_verbose("[doc] track bone path: " + bone_path);
- NodePath path = bone_path;
- animation->track_set_path(track_idx, path);
+ track_path = bone_path;
}
} else if (state.fbx_target_map.has(target_id)) {
//print_verbose("[doc] we have a valid target for a node animation");
Ref<FBXNode> target_node = state.fbx_target_map[target_id];
if (target_node.is_valid() && target_node->godot_node != nullptr) {
String node_path = state.root->get_path_to(target_node->godot_node);
- NodePath path = node_path;
- animation->track_set_path(track_idx, path);
+ track_path = node_path;
//print_verbose("[doc] node animation path: " + node_path);
}
} else {
@@ -1186,6 +1183,30 @@ Node3D *EditorSceneImporterFBX::_generate_scene(
const Vector3 def_scale = scale_keys.has_default ? scale_keys.default_value : bone_rest.basis.get_scale();
print_verbose("track defaults: p(" + def_pos + ") s(" + def_scale + ") r(" + def_rot + ")");
+ int position_idx = -1;
+ if (pos_values.size()) {
+ position_idx = animation->get_track_count();
+ animation->add_track(Animation::TYPE_POSITION_3D);
+ animation->track_set_path(position_idx, track_path);
+ animation->track_set_imported(position_idx, true);
+ }
+
+ int rotation_idx = -1;
+ if (pos_values.size()) {
+ rotation_idx = animation->get_track_count();
+ animation->add_track(Animation::TYPE_ROTATION_3D);
+ animation->track_set_path(rotation_idx, track_path);
+ animation->track_set_imported(rotation_idx, true);
+ }
+
+ int scale_idx = -1;
+ if (pos_values.size()) {
+ scale_idx = animation->get_track_count();
+ animation->add_track(Animation::TYPE_SCALE_3D);
+ animation->track_set_path(scale_idx, track_path);
+ animation->track_set_imported(scale_idx, true);
+ }
+
while (true) {
Vector3 pos = def_pos;
Quaternion rot = def_rot;
@@ -1206,21 +1227,15 @@ Node3D *EditorSceneImporterFBX::_generate_scene(
AssetImportAnimation::INTERP_LINEAR);
}
- // node animations must also include pivots
- if (skeleton_bone >= 0) {
- Transform3D xform = Transform3D();
- xform.basis.set_quaternion_scale(rot, scale);
- xform.origin = pos;
- const Transform3D t = bone_rest.affine_inverse() * xform;
-
- // populate this again
- rot = t.basis.get_rotation_quaternion();
- rot.normalize();
- scale = t.basis.get_scale();
- pos = t.origin;
+ if (position_idx >= 0) {
+ animation->position_track_insert_key(position_idx, time, pos);
+ }
+ if (rotation_idx >= 0) {
+ animation->rotation_track_insert_key(rotation_idx, time, rot);
+ }
+ if (scale_idx >= 0) {
+ animation->scale_track_insert_key(scale_idx, time, scale);
}
-
- animation->transform_track_insert_key(track_idx, time, pos, rot, scale);
if (last) {
break;
diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp
index ba98592600..6fd542ca68 100644
--- a/modules/gltf/gltf_document.cpp
+++ b/modules/gltf/gltf_document.cpp
@@ -4319,6 +4319,9 @@ Error GLTFDocument::_create_skeletons(Ref<GLTFState> state) {
skeleton->add_bone(node->get_name());
skeleton->set_bone_rest(bone_index, node->xform);
+ skeleton->set_bone_pose_position(bone_index, node->position);
+ skeleton->set_bone_pose_rotation(bone_index, node->rotation.normalized());
+ skeleton->set_bone_pose_scale(bone_index, node->scale);
if (node->parent >= 0 && state->nodes[node->parent]->skeleton == skel_i) {
const int bone_parent = skeleton->find_bone(state->nodes[node->parent]->get_name());
@@ -5470,7 +5473,7 @@ void GLTFDocument::_convert_skeleton_to_gltf(Skeleton3D *p_skeleton3d, Ref<GLTFS
// Note that we cannot use _gen_unique_bone_name here, because glTF spec requires all node
// names to be unique regardless of whether or not they are used as joints.
joint_node->set_name(_gen_unique_name(state, skeleton->get_bone_name(bone_i)));
- Transform3D xform = skeleton->get_bone_rest(bone_i) * skeleton->get_bone_pose(bone_i);
+ Transform3D xform = skeleton->get_bone_pose(bone_i);
joint_node->scale = xform.basis.get_scale();
joint_node->rotation = xform.basis.get_rotation_quaternion();
joint_node->position = xform.origin;
@@ -5733,7 +5736,7 @@ struct EditorSceneImporterGLTFInterpolate<Quaternion> {
template <class T>
T GLTFDocument::_interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, const float p_time, const GLTFAnimation::Interpolation p_interp) {
ERR_FAIL_COND_V(!p_values.size(), T());
- if (p_times.size() != p_values.size()) {
+ if (p_times.size() != (p_values.size() / (p_interp == GLTFAnimation::INTERP_CUBIC_SPLINE ? 3 : 1))) {
ERR_PRINT_ONCE("The interpolated values are not corresponding to its times.");
return p_values[0];
}
@@ -5868,9 +5871,67 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
const bool transform_affects_skinned_mesh_instance = gltf_node->skeleton < 0 && gltf_node->skin >= 0;
if ((track.rotation_track.values.size() || track.position_track.values.size() || track.scale_track.values.size()) && !transform_affects_skinned_mesh_instance) {
//make transform track
- int track_idx = animation->get_track_count();
- animation->add_track(Animation::TYPE_TRANSFORM3D);
- animation->track_set_path(track_idx, transform_node_path);
+ int base_idx = animation->get_track_count();
+ int position_idx = -1;
+ int rotation_idx = -1;
+ int scale_idx = -1;
+
+ if (track.position_track.values.size()) {
+ Vector3 base_pos = state->nodes[track_i.key]->position;
+ bool not_default = false; //discard the track if all it contains is default values
+ for (int i = 0; i < track.position_track.times.size(); i++) {
+ Vector3 value = track.position_track.values[track.position_track.interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE ? (1 + i * 3) : i];
+ if (!value.is_equal_approx(base_pos)) {
+ not_default = true;
+ break;
+ }
+ }
+ if (not_default) {
+ position_idx = base_idx;
+ animation->add_track(Animation::TYPE_POSITION_3D);
+ animation->track_set_path(position_idx, transform_node_path);
+ animation->track_set_imported(position_idx, true); //helps merging later
+
+ base_idx++;
+ }
+ }
+ if (track.rotation_track.values.size()) {
+ Quaternion base_rot = state->nodes[track_i.key]->rotation.normalized();
+ bool not_default = false; //discard the track if all it contains is default values
+ for (int i = 0; i < track.rotation_track.times.size(); i++) {
+ Quaternion value = track.rotation_track.values[track.rotation_track.interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE ? (1 + i * 3) : i].normalized();
+ if (!value.is_equal_approx(base_rot)) {
+ not_default = true;
+ break;
+ }
+ }
+ if (not_default) {
+ rotation_idx = base_idx;
+ animation->add_track(Animation::TYPE_ROTATION_3D);
+ animation->track_set_path(rotation_idx, transform_node_path);
+ animation->track_set_imported(rotation_idx, true); //helps merging later
+ base_idx++;
+ }
+ }
+ if (track.scale_track.values.size()) {
+ Vector3 base_scale = state->nodes[track_i.key]->scale;
+ bool not_default = false; //discard the track if all it contains is default values
+ for (int i = 0; i < track.scale_track.times.size(); i++) {
+ Vector3 value = track.scale_track.values[track.scale_track.interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE ? (1 + i * 3) : i];
+ if (!value.is_equal_approx(base_scale)) {
+ not_default = true;
+ break;
+ }
+ }
+ if (not_default) {
+ scale_idx = base_idx;
+ animation->add_track(Animation::TYPE_SCALE_3D);
+ animation->track_set_path(scale_idx, transform_node_path);
+ animation->track_set_imported(scale_idx, true); //helps merging later
+ base_idx++;
+ }
+ }
+
//first determine animation length
const double increment = 1.0 / bake_fps;
@@ -5880,15 +5941,15 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
Quaternion base_rot;
Vector3 base_scale = Vector3(1, 1, 1);
- if (!track.rotation_track.values.size()) {
+ if (rotation_idx == -1) {
base_rot = state->nodes[track_i.key]->rotation.normalized();
}
- if (!track.position_track.values.size()) {
+ if (position_idx == -1) {
base_pos = state->nodes[track_i.key]->position;
}
- if (!track.scale_track.values.size()) {
+ if (scale_idx == -1) {
base_scale = state->nodes[track_i.key]->scale;
}
@@ -5898,35 +5959,21 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
Quaternion rot = base_rot;
Vector3 scale = base_scale;
- if (track.position_track.times.size()) {
+ if (position_idx >= 0) {
pos = _interpolate_track<Vector3>(track.position_track.times, track.position_track.values, time, track.position_track.interpolation);
+ animation->position_track_insert_key(position_idx, time, pos);
}
- if (track.rotation_track.times.size()) {
+ if (rotation_idx >= 0) {
rot = _interpolate_track<Quaternion>(track.rotation_track.times, track.rotation_track.values, time, track.rotation_track.interpolation);
+ animation->rotation_track_insert_key(rotation_idx, time, rot);
}
- if (track.scale_track.times.size()) {
+ if (scale_idx >= 0) {
scale = _interpolate_track<Vector3>(track.scale_track.times, track.scale_track.values, time, track.scale_track.interpolation);
+ animation->scale_track_insert_key(scale_idx, time, scale);
}
- if (gltf_node->skeleton >= 0) {
- Transform3D xform;
- xform.basis.set_quaternion_scale(rot, scale);
- xform.origin = pos;
-
- const Skeleton3D *skeleton = state->skeletons[gltf_node->skeleton]->godot_skeleton;
- const int bone_idx = skeleton->find_bone(gltf_node->get_name());
- xform = skeleton->get_bone_rest(bone_idx).affine_inverse() * xform;
-
- rot = xform.basis.get_rotation_quaternion();
- rot.normalize();
- scale = xform.basis.get_scale();
- pos = xform.origin;
- }
-
- animation->transform_track_insert_key(track_idx, time, pos, rot, scale);
-
if (last) {
break;
}
@@ -6042,7 +6089,7 @@ void GLTFDocument::_convert_mesh_instances(Ref<GLTFState> state) {
} else {
if (skin.is_null()) {
// Note that gltf_skin_key should remain null, so these can share a reference.
- skin = skeleton->register_skin(nullptr)->get_skin();
+ skin = skeleton->create_skin_from_rest_transforms();
}
gltf_skin.instantiate();
gltf_skin->godot_skin = skin;
@@ -6146,6 +6193,10 @@ void GLTFDocument::_process_mesh_instances(Ref<GLTFState> state, Node *scene_roo
}
GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state, GLTFAnimation::Track p_track, Ref<Animation> p_animation, Transform3D p_bone_rest, int32_t p_track_i, GLTFNodeIndex p_node_i) {
+#ifndef _MSC_VER
+#warning this needs to be redone
+#endif
+#if 0
Animation::InterpolationType interpolation = p_animation->track_get_interpolation_type(p_track_i);
GLTFAnimation::Interpolation gltf_interpolation = GLTFAnimation::INTERP_LINEAR;
@@ -6164,6 +6215,8 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state
for (int32_t key_i = 0; key_i < key_count; key_i++) {
times.write[key_i] = p_animation->track_get_key_time(p_track_i, key_i);
}
+
+
if (track_type == Animation::TYPE_TRANSFORM3D) {
p_track.position_track.times = times;
p_track.position_track.interpolation = gltf_interpolation;
@@ -6323,7 +6376,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state
}
}
}
-
+#endif
return p_track;
}
diff --git a/modules/lightmapper_rd/lm_common_inc.glsl b/modules/lightmapper_rd/lm_common_inc.glsl
index 22172d50e4..58523dc1f8 100644
--- a/modules/lightmapper_rd/lm_common_inc.glsl
+++ b/modules/lightmapper_rd/lm_common_inc.glsl
@@ -81,3 +81,7 @@ layout(set = 0, binding = 8) uniform texture2DArray albedo_tex;
layout(set = 0, binding = 9) uniform texture2DArray emission_tex;
layout(set = 0, binding = 10) uniform sampler linear_sampler;
+
+// Fragment action constants
+const uint FA_NONE = 0;
+const uint FA_SMOOTHEN_POSITION = 1;
diff --git a/modules/lightmapper_rd/lm_compute.glsl b/modules/lightmapper_rd/lm_compute.glsl
index 25b334c5eb..6e5c9f25ba 100644
--- a/modules/lightmapper_rd/lm_compute.glsl
+++ b/modules/lightmapper_rd/lm_compute.glsl
@@ -148,7 +148,7 @@ uint trace_ray(vec3 p_from, vec3 p_to
ivec3 icell = ivec3(from_cell);
ivec3 iendcell = ivec3(to_cell);
vec3 dir_cell = normalize(rel_cell);
- vec3 delta = abs(1.0 / dir_cell); //vec3(length(rel_cell)) / rel_cell);
+ vec3 delta = min(abs(1.0 / dir_cell), params.grid_size); // use params.grid_size as max to prevent infinity values
ivec3 step = ivec3(sign(rel_cell));
vec3 side = (sign(rel_cell) * (vec3(icell) - from_cell) + (sign(rel_cell) * 0.5) + 0.5) * delta;
@@ -420,20 +420,22 @@ void main() {
light = textureLod(sampler2DArray(source_light, linear_sampler), uvw, 0.0).rgb;
active_rays += 1.0;
- } else if (trace_result == RAY_MISS && params.env_transform[0][3] == 0.0) { // Use env_transform[0][3] to indicate when we are computing the first bounce
- // Did not hit a triangle, reach out for the sky
- vec3 sky_dir = normalize(mat3(params.env_transform) * ray_dir);
+ } else if (trace_result == RAY_MISS) {
+ if (params.env_transform[0][3] == 0.0) { // Use env_transform[0][3] to indicate when we are computing the first bounce
+ // Did not hit a triangle, reach out for the sky
+ vec3 sky_dir = normalize(mat3(params.env_transform) * ray_dir);
- vec2 st = vec2(
- atan(sky_dir.x, sky_dir.z),
- acos(sky_dir.y));
+ vec2 st = vec2(
+ atan(sky_dir.x, sky_dir.z),
+ acos(sky_dir.y));
- if (st.x < 0.0)
- st.x += PI * 2.0;
+ if (st.x < 0.0)
+ st.x += PI * 2.0;
- st /= vec2(PI * 2.0, PI);
+ st /= vec2(PI * 2.0, PI);
- light = textureLod(sampler2D(environment, linear_sampler), st, 0.0).rgb;
+ light = textureLod(sampler2D(environment, linear_sampler), st, 0.0).rgb;
+ }
active_rays += 1.0;
}
diff --git a/modules/lightmapper_rd/lm_raster.glsl b/modules/lightmapper_rd/lm_raster.glsl
index 55ca193cc1..a86968a4f3 100644
--- a/modules/lightmapper_rd/lm_raster.glsl
+++ b/modules/lightmapper_rd/lm_raster.glsl
@@ -12,6 +12,7 @@ layout(location = 2) out vec2 uv_interp;
layout(location = 3) out vec3 barycentric;
layout(location = 4) flat out uvec3 vertex_indices;
layout(location = 5) flat out vec3 face_normal;
+layout(location = 6) flat out uint fragment_action;
layout(push_constant, binding = 0, std430) uniform Params {
vec2 atlas_size;
@@ -49,6 +50,14 @@ void main() {
face_normal = -normalize(cross((vertices.data[vertex_indices.x].position - vertices.data[vertex_indices.y].position), (vertices.data[vertex_indices.x].position - vertices.data[vertex_indices.z].position)));
+ {
+ const float FLAT_THRESHOLD = 0.99;
+ const vec3 norm_a = vec3(vertices.data[vertex_indices.x].normal_xy, vertices.data[vertex_indices.x].normal_z);
+ const vec3 norm_b = vec3(vertices.data[vertex_indices.y].normal_xy, vertices.data[vertex_indices.y].normal_z);
+ const vec3 norm_c = vec3(vertices.data[vertex_indices.z].normal_xy, vertices.data[vertex_indices.z].normal_z);
+ fragment_action = (dot(norm_a, norm_b) < FLAT_THRESHOLD || dot(norm_a, norm_c) < FLAT_THRESHOLD || dot(norm_b, norm_c) < FLAT_THRESHOLD) ? FA_SMOOTHEN_POSITION : FA_NONE;
+ }
+
gl_Position = vec4((uv_interp + params.uv_offset) * 2.0 - 1.0, 0.0001, 1.0);
}
@@ -78,6 +87,7 @@ layout(location = 2) in vec2 uv_interp;
layout(location = 3) in vec3 barycentric;
layout(location = 4) in flat uvec3 vertex_indices;
layout(location = 5) in flat vec3 face_normal;
+layout(location = 6) in flat uint fragment_action;
layout(location = 0) out vec4 position;
layout(location = 1) out vec4 normal;
@@ -86,7 +96,7 @@ layout(location = 2) out vec4 unocclude;
void main() {
vec3 vertex_pos = vertex_interp;
- {
+ if (fragment_action == FA_SMOOTHEN_POSITION) {
// smooth out vertex position by interpolating its projection in the 3 normal planes (normal plane is created by vertex pos and normal)
// because we don't want to interpolate inwards, normals found pointing inwards are pushed out.
vec3 pos_a = vertices.data[vertex_indices.x].position;
diff --git a/platform/linuxbsd/freedesktop_screensaver.cpp b/platform/linuxbsd/freedesktop_screensaver.cpp
index a6a3b27d76..3973d43d49 100644
--- a/platform/linuxbsd/freedesktop_screensaver.cpp
+++ b/platform/linuxbsd/freedesktop_screensaver.cpp
@@ -50,6 +50,7 @@ void FreeDesktopScreenSaver::inhibit() {
DBusConnection *bus = dbus_bus_get(DBUS_BUS_SESSION, &error);
if (dbus_error_is_set(&error)) {
+ dbus_error_free(&error);
unsupported = true;
return;
}
@@ -72,6 +73,7 @@ void FreeDesktopScreenSaver::inhibit() {
DBusMessage *reply = dbus_connection_send_with_reply_and_block(bus, message, 50, &error);
dbus_message_unref(message);
if (dbus_error_is_set(&error)) {
+ dbus_error_free(&error);
dbus_connection_unref(bus);
unsupported = false;
return;
@@ -96,6 +98,7 @@ void FreeDesktopScreenSaver::uninhibit() {
DBusConnection *bus = dbus_bus_get(DBUS_BUS_SESSION, &error);
if (dbus_error_is_set(&error)) {
+ dbus_error_free(&error);
unsupported = true;
return;
}
@@ -110,6 +113,7 @@ void FreeDesktopScreenSaver::uninhibit() {
DBusMessage *reply = dbus_connection_send_with_reply_and_block(bus, message, 50, &error);
if (dbus_error_is_set(&error)) {
+ dbus_error_free(&error);
dbus_connection_unref(bus);
unsupported = true;
return;
diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp
index 5bce705dd5..3637594e1b 100644
--- a/scene/2d/gpu_particles_2d.cpp
+++ b/scene/2d/gpu_particles_2d.cpp
@@ -163,8 +163,8 @@ void GPUParticles2D::set_trail_sections(int p_sections) {
}
void GPUParticles2D::set_trail_section_subdivisions(int p_subdivisions) {
- ERR_FAIL_COND(trail_section_subdivisions < 1);
- ERR_FAIL_COND(trail_section_subdivisions > 1024);
+ ERR_FAIL_COND(p_subdivisions < 1);
+ ERR_FAIL_COND(p_subdivisions > 1024);
trail_section_subdivisions = p_subdivisions;
update();
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index a791d99c39..c2f150ce00 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -811,8 +811,9 @@ void TileMap::_rendering_cleanup_layer(int p_layer) {
ERR_FAIL_INDEX(p_layer, (int)layers.size());
RenderingServer *rs = RenderingServer::get_singleton();
- if (!layers[p_layer].canvas_item.is_valid()) {
+ if (layers[p_layer].canvas_item.is_valid()) {
rs->free(layers[p_layer].canvas_item);
+ layers[p_layer].canvas_item = RID();
}
}
@@ -848,9 +849,11 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List
Color modulate = get_self_modulate();
modulate *= get_layer_modulate(q.layer);
if (selected_layer >= 0) {
- if (q.layer < selected_layer) {
+ int z1 = get_layer_z_index(q.layer);
+ int z2 = get_layer_z_index(selected_layer);
+ if (z1 < z2 || (z1 == z2 && q.layer < selected_layer)) {
modulate = modulate.darkened(0.5);
- } else if (q.layer > selected_layer) {
+ } else if (z1 > z2 || (z1 == z2 && q.layer > selected_layer)) {
modulate = modulate.darkened(0.5);
modulate.a *= 0.3;
}
@@ -3092,6 +3095,8 @@ void TileMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_tile_data", "layer"), &TileMap::_set_tile_data);
ClassDB::bind_method(D_METHOD("_get_tile_data", "layer"), &TileMap::_get_tile_data);
+ ClassDB::bind_method(D_METHOD("_tile_set_changed_deferred_update"), &TileMap::_tile_set_changed_deferred_update);
+
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tile_set", PROPERTY_HINT_RESOURCE_TYPE, "TileSet"), "set_tileset", "get_tileset");
ADD_PROPERTY(PropertyInfo(Variant::INT, "cell_quadrant_size", PROPERTY_HINT_RANGE, "1,128,1"), "set_quadrant_size", "get_quadrant_size");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collision_animatable"), "set_collision_animatable", "is_collision_animatable");
@@ -3111,8 +3116,16 @@ void TileMap::_bind_methods() {
void TileMap::_tile_set_changed() {
emit_signal(SNAME("changed"));
- _clear_internals();
- _recreate_internals();
+ _tile_set_changed_deferred_update_needed = true;
+ call_deferred("_tile_set_changed_deferred_update");
+}
+
+void TileMap::_tile_set_changed_deferred_update() {
+ if (_tile_set_changed_deferred_update_needed) {
+ _clear_internals();
+ _recreate_internals();
+ _tile_set_changed_deferred_update_needed = false;
+ }
}
TileMap::TileMap() {
diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h
index b1fbdfdf8a..e5809deabb 100644
--- a/scene/2d/tile_map.h
+++ b/scene/2d/tile_map.h
@@ -285,6 +285,8 @@ private:
Vector<int> _get_tile_data(int p_layer) const;
void _tile_set_changed();
+ bool _tile_set_changed_deferred_update_needed = false;
+ void _tile_set_changed_deferred_update();
protected:
bool _set(const StringName &p_name, const Variant &p_value);
diff --git a/scene/3d/bone_attachment_3d.cpp b/scene/3d/bone_attachment_3d.cpp
index afd11482e3..8e89f4fc54 100644
--- a/scene/3d/bone_attachment_3d.cpp
+++ b/scene/3d/bone_attachment_3d.cpp
@@ -215,8 +215,6 @@ void BoneAttachment3D::_transform_changed() {
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));
}
}
}
@@ -273,8 +271,6 @@ void BoneAttachment3D::set_override_pose(bool p_override) {
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();
@@ -294,8 +290,6 @@ void BoneAttachment3D::set_override_mode(int p_mode) {
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;
diff --git a/scene/3d/bone_attachment_3d.h b/scene/3d/bone_attachment_3d.h
index cf681cace8..57b9854e0e 100644
--- a/scene/3d/bone_attachment_3d.h
+++ b/scene/3d/bone_attachment_3d.h
@@ -47,7 +47,6 @@ class BoneAttachment3D : public Node3D {
enum OVERRIDE_MODES {
MODE_GLOBAL_POSE,
MODE_LOCAL_POSE,
- MODE_CUSTOM_POSE
};
bool use_external_skeleton = false;
diff --git a/scene/3d/mesh_instance_3d.cpp b/scene/3d/mesh_instance_3d.cpp
index 67f4a88228..cfd90e5da0 100644
--- a/scene/3d/mesh_instance_3d.cpp
+++ b/scene/3d/mesh_instance_3d.cpp
@@ -146,11 +146,13 @@ void MeshInstance3D::_resolve_skeleton_path() {
if (!skeleton_path.is_empty()) {
Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(get_node(skeleton_path));
if (skeleton) {
- new_skin_reference = skeleton->register_skin(skin_internal);
if (skin_internal.is_null()) {
+ new_skin_reference = skeleton->register_skin(skeleton->create_skin_from_rest_transforms());
//a skin was created for us
skin_internal = new_skin_reference->get_skin();
notify_property_list_changed();
+ } else {
+ new_skin_reference = skeleton->register_skin(skin_internal);
}
}
}
diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp
index 2b52f034b2..9b28d7aff8 100644
--- a/scene/3d/skeleton_3d.cpp
+++ b/scene/3d/skeleton_3d.cpp
@@ -99,8 +99,12 @@ bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) {
set_bone_rest(which, p_value);
} else if (what == "enabled") {
set_bone_enabled(which, p_value);
- } else if (what == "pose") {
- set_bone_pose(which, p_value);
+ } else if (what == "position") {
+ set_bone_pose_position(which, p_value);
+ } else if (what == "rotation") {
+ set_bone_pose_rotation(which, p_value);
+ } else if (what == "scale") {
+ set_bone_pose_scale(which, p_value);
} else {
return false;
}
@@ -135,8 +139,12 @@ bool Skeleton3D::_get(const StringName &p_path, Variant &r_ret) const {
r_ret = get_bone_rest(which);
} else if (what == "enabled") {
r_ret = is_bone_enabled(which);
- } else if (what == "pose") {
- r_ret = get_bone_pose(which);
+ } else if (what == "position") {
+ r_ret = get_bone_pose_position(which);
+ } else if (what == "rotation") {
+ r_ret = get_bone_pose_rotation(which);
+ } else if (what == "scale") {
+ r_ret = get_bone_pose_scale(which);
} else {
return false;
}
@@ -151,7 +159,9 @@ void Skeleton3D::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::INT, prep + "parent", PROPERTY_HINT_RANGE, "-1," + itos(bones.size() - 1) + ",1", PROPERTY_USAGE_NOEDITOR));
p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prep + "rest", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
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));
+ p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + "position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ p_list->push_back(PropertyInfo(Variant::QUATERNION, prep + "rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + "scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
}
#ifndef _3D_DISABLED
@@ -547,18 +557,6 @@ void Skeleton3D::unparent_bone_and_rest(int p_bone) {
_make_dirty();
}
-void Skeleton3D::set_bone_disable_rest(int p_bone, bool p_disable) {
- 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 {
- 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 {
const int bone_size = bones.size();
ERR_FAIL_INDEX_V(p_bone, bone_size, -1);
@@ -657,36 +655,60 @@ void Skeleton3D::clear_bones() {
// Posing api
-void Skeleton3D::set_bone_pose(int p_bone, const Transform3D &p_pose) {
+void Skeleton3D::set_bone_pose_position(int p_bone, const Vector3 &p_position) {
const int bone_size = bones.size();
ERR_FAIL_INDEX(p_bone, bone_size);
- bones.write[p_bone].pose = p_pose;
+ bones.write[p_bone].pose_position = p_position;
+ bones.write[p_bone].pose_cache_dirty = true;
if (is_inside_tree()) {
_make_dirty();
}
}
-Transform3D Skeleton3D::get_bone_pose(int p_bone) const {
+void Skeleton3D::set_bone_pose_rotation(int p_bone, const Quaternion &p_rotation) {
const int bone_size = bones.size();
- ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D());
- return bones[p_bone].pose;
-}
+ ERR_FAIL_INDEX(p_bone, bone_size);
-void Skeleton3D::set_bone_custom_pose(int p_bone, const Transform3D &p_custom_pose) {
+ bones.write[p_bone].pose_rotation = p_rotation;
+ bones.write[p_bone].pose_cache_dirty = true;
+ if (is_inside_tree()) {
+ _make_dirty();
+ }
+}
+void Skeleton3D::set_bone_pose_scale(int p_bone, const Vector3 &p_scale) {
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());
- bones.write[p_bone].custom_pose = p_custom_pose;
+ bones.write[p_bone].pose_scale = p_scale;
+ bones.write[p_bone].pose_cache_dirty = true;
+ if (is_inside_tree()) {
+ _make_dirty();
+ }
+}
+
+Vector3 Skeleton3D::get_bone_pose_position(int p_bone) const {
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX_V(p_bone, bone_size, Vector3());
+ return bones[p_bone].pose_position;
+}
- _make_dirty();
+Quaternion Skeleton3D::get_bone_pose_rotation(int p_bone) const {
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX_V(p_bone, bone_size, Quaternion());
+ return bones[p_bone].pose_rotation;
}
-Transform3D Skeleton3D::get_bone_custom_pose(int p_bone) const {
+Vector3 Skeleton3D::get_bone_pose_scale(int p_bone) const {
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX_V(p_bone, bone_size, Vector3());
+ return bones[p_bone].pose_scale;
+}
+
+Transform3D Skeleton3D::get_bone_pose(int p_bone) const {
const int bone_size = bones.size();
ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D());
- return bones[p_bone].custom_pose;
+ ((Skeleton3D *)this)->bones.write[p_bone].update_pose_cache();
+ return bones[p_bone].pose_cache;
}
void Skeleton3D::_make_dirty() {
@@ -887,59 +909,57 @@ void Skeleton3D::_skin_changed() {
_make_dirty();
}
-Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) {
- for (Set<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) {
- if (E->get()->skin == p_skin) {
- return Ref<SkinReference>(E->get());
+Ref<Skin> Skeleton3D::create_skin_from_rest_transforms() {
+ Ref<Skin> skin;
+
+ skin.instantiate();
+ skin->set_bind_count(bones.size());
+ _update_process_order(); // Just in case.
+
+ // Pose changed, rebuild cache of inverses.
+ const Bone *bonesptr = bones.ptr();
+ int len = bones.size();
+
+ // Calculate global rests and invert them.
+ LocalVector<int> bones_to_process;
+ bones_to_process = get_parentless_bones();
+ while (bones_to_process.size() > 0) {
+ int current_bone_idx = bones_to_process[0];
+ const Bone &b = bonesptr[current_bone_idx];
+ bones_to_process.erase(current_bone_idx);
+ LocalVector<int> child_bones_vector;
+ child_bones_vector = get_bone_children(current_bone_idx);
+ int child_bones_size = child_bones_vector.size();
+ if (b.parent < 0) {
+ skin->set_bind_pose(current_bone_idx, b.rest);
+ }
+ for (int i = 0; i < child_bones_size; i++) {
+ int child_bone_idx = child_bones_vector[i];
+ const Bone &cb = bonesptr[child_bone_idx];
+ skin->set_bind_pose(child_bone_idx, skin->get_bind_pose(current_bone_idx) * cb.rest);
+ // Add the bone's children to the list of bones to be processed.
+ bones_to_process.push_back(child_bones_vector[i]);
}
}
- Ref<Skin> skin = p_skin;
-
- if (skin.is_null()) {
- // Need to create one from existing code, this is for compatibility only
- // when skeletons did not support skins. It is also used by gizmo
- // to display the skeleton.
-
- skin.instantiate();
- skin->set_bind_count(bones.size());
- _update_process_order(); // Just in case.
-
- // Pose changed, rebuild cache of inverses.
- const Bone *bonesptr = bones.ptr();
- int len = bones.size();
-
- // Calculate global rests and invert them.
- LocalVector<int> bones_to_process;
- bones_to_process = get_parentless_bones();
- while (bones_to_process.size() > 0) {
- int current_bone_idx = bones_to_process[0];
- const Bone &b = bonesptr[current_bone_idx];
- bones_to_process.erase(current_bone_idx);
- LocalVector<int> child_bones_vector;
- child_bones_vector = get_bone_children(current_bone_idx);
- int child_bones_size = child_bones_vector.size();
- if (b.parent < 0) {
- skin->set_bind_pose(current_bone_idx, b.rest);
- }
- for (int i = 0; i < child_bones_size; i++) {
- int child_bone_idx = child_bones_vector[i];
- const Bone &cb = bonesptr[child_bone_idx];
- skin->set_bind_pose(child_bone_idx, skin->get_bind_pose(current_bone_idx) * cb.rest);
- // Add the bone's children to the list of bones to be processed.
- bones_to_process.push_back(child_bones_vector[i]);
- }
- }
+ for (int i = 0; i < len; i++) {
+ // The inverse is what is actually required.
+ skin->set_bind_bone(i, i);
+ skin->set_bind_pose(i, skin->get_bind_pose(i).affine_inverse());
+ }
+
+ return skin;
+}
+
+Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) {
+ ERR_FAIL_COND_V(p_skin.is_null(), Ref<SkinReference>());
- for (int i = 0; i < len; i++) {
- // The inverse is what is actually required.
- skin->set_bind_bone(i, i);
- skin->set_bind_pose(i, skin->get_bind_pose(i).affine_inverse());
+ for (Set<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) {
+ if (E->get()->skin == p_skin) {
+ return Ref<SkinReference>(E->get());
}
}
- ERR_FAIL_COND_V(skin.is_null(), Ref<SkinReference>());
-
Ref<SkinReference> skin_ref;
skin_ref.instantiate();
@@ -947,11 +967,11 @@ Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) {
skin_ref->bind_count = 0;
skin_ref->skeleton = RenderingServer::get_singleton()->skeleton_create();
skin_ref->skeleton_node = this;
- skin_ref->skin = skin;
+ skin_ref->skin = p_skin;
skin_bindings.insert(skin_ref.operator->());
- skin->connect("changed", Callable(skin_ref.operator->(), "_skin_changed"));
+ skin_ref->skin->connect("changed", Callable(skin_ref.operator->(), "_skin_changed"));
_make_dirty(); // Skin needs to be updated, so update skeleton.
@@ -987,59 +1007,33 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) {
Bone &b = bonesptr[current_bone_idx];
bool bone_enabled = b.enabled && !show_rest_only;
- if (b.disable_rest) {
- if (bone_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;
- }
+ if (bone_enabled) {
+ b.update_pose_cache();
+ Transform3D pose = b.pose_cache;
+
+ if (b.parent >= 0) {
+ b.pose_global = bonesptr[b.parent].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;
- }
+ b.pose_global = pose;
+ b.pose_global_no_override = b.pose_global;
}
-
} else {
- if (bone_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;
- }
+ if (b.parent >= 0) {
+ b.pose_global = bonesptr[b.parent].pose_global * b.rest;
+ 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;
- }
+ 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);
+ override_local_pose = bonesptr[b.parent].pose_global * b.local_pose_override;
} else {
- override_local_pose = (b.rest * b.local_pose_override);
+ override_local_pose = b.local_pose_override;
}
b.pose_global = b.pose_global.interpolate_with(override_local_pose, b.local_pose_override_amount);
}
@@ -1080,8 +1074,8 @@ Transform3D Skeleton3D::global_pose_to_local_pose(int p_bone_idx, Transform3D p_
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;
+ Transform3D conversion_transform = bones[parent_bone_idx].pose_global.affine_inverse();
+ return conversion_transform * p_global_pose;
} else {
return p_global_pose;
}
@@ -1092,8 +1086,7 @@ Transform3D Skeleton3D::local_pose_to_global_pose(int p_bone_idx, Transform3D p_
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;
+ return bones[parent_bone_idx].pose_global * p_local_pose;
} else {
return p_local_pose;
}
@@ -1183,17 +1176,21 @@ void Skeleton3D::_bind_methods() {
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);
+ ClassDB::bind_method(D_METHOD("create_skin_from_rest_transforms"), &Skeleton3D::create_skin_from_rest_transforms);
ClassDB::bind_method(D_METHOD("register_skin", "skin"), &Skeleton3D::register_skin);
ClassDB::bind_method(D_METHOD("localize_rests"), &Skeleton3D::localize_rests);
- ClassDB::bind_method(D_METHOD("set_bone_disable_rest", "bone_idx", "disable"), &Skeleton3D::set_bone_disable_rest);
- ClassDB::bind_method(D_METHOD("is_bone_rest_disabled", "bone_idx"), &Skeleton3D::is_bone_rest_disabled);
-
ClassDB::bind_method(D_METHOD("clear_bones"), &Skeleton3D::clear_bones);
ClassDB::bind_method(D_METHOD("get_bone_pose", "bone_idx"), &Skeleton3D::get_bone_pose);
- ClassDB::bind_method(D_METHOD("set_bone_pose", "bone_idx", "pose"), &Skeleton3D::set_bone_pose);
+ ClassDB::bind_method(D_METHOD("set_bone_pose_position", "bone_idx", "position"), &Skeleton3D::set_bone_pose_position);
+ ClassDB::bind_method(D_METHOD("set_bone_pose_rotation", "bone_idx", "rotation"), &Skeleton3D::set_bone_pose_rotation);
+ ClassDB::bind_method(D_METHOD("set_bone_pose_scale", "bone_idx", "scale"), &Skeleton3D::set_bone_pose_scale);
+
+ ClassDB::bind_method(D_METHOD("get_bone_pose_position", "bone_idx"), &Skeleton3D::get_bone_pose_position);
+ ClassDB::bind_method(D_METHOD("get_bone_pose_rotation", "bone_idx"), &Skeleton3D::get_bone_pose_rotation);
+ ClassDB::bind_method(D_METHOD("get_bone_pose_scale", "bone_idx"), &Skeleton3D::get_bone_pose_scale);
ClassDB::bind_method(D_METHOD("is_bone_enabled", "bone_idx"), &Skeleton3D::is_bone_enabled);
ClassDB::bind_method(D_METHOD("set_bone_enabled", "bone_idx", "enabled"), &Skeleton3D::set_bone_enabled, DEFVAL(true));
@@ -1208,9 +1205,6 @@ void Skeleton3D::_bind_methods() {
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("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);
diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h
index 9aa4fc823f..f3cf551af7 100644
--- a/scene/3d/skeleton_3d.h
+++ b/scene/3d/skeleton_3d.h
@@ -76,16 +76,24 @@ private:
bool enabled;
int parent;
- bool disable_rest = false;
Transform3D rest;
- Transform3D pose;
+ _FORCE_INLINE_ void update_pose_cache() {
+ if (pose_cache_dirty) {
+ pose_cache.basis.set_quaternion_scale(pose_rotation, pose_scale);
+ pose_cache.origin = pose_position;
+ pose_cache_dirty = false;
+ }
+ }
+ bool pose_cache_dirty = true;
+ Transform3D pose_cache;
+ Vector3 pose_position;
+ Quaternion pose_rotation;
+ Vector3 pose_scale = Vector3(1, 1, 1);
+
Transform3D pose_global;
Transform3D pose_global_no_override;
- bool custom_pose_enable = false;
- Transform3D custom_pose;
-
real_t global_pose_override_amount = 0.0;
bool global_pose_override_reset = false;
Transform3D global_pose_override;
@@ -107,8 +115,6 @@ private:
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
@@ -187,9 +193,6 @@ public:
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;
-
int get_bone_count() const;
void set_bone_rest(int p_bone, const Transform3D &p_rest);
@@ -206,11 +209,15 @@ public:
// posing api
- void set_bone_pose(int p_bone, const Transform3D &p_pose);
+ void set_bone_pose_position(int p_bone, const Vector3 &p_position);
+ void set_bone_pose_rotation(int p_bone, const Quaternion &p_rotation);
+ void set_bone_pose_scale(int p_bone, const Vector3 &p_scale);
+
Transform3D get_bone_pose(int p_bone) const;
- void set_bone_custom_pose(int p_bone, const Transform3D &p_custom_pose);
- Transform3D get_bone_custom_pose(int p_bone) const;
+ Vector3 get_bone_pose_position(int p_bone) const;
+ Quaternion get_bone_pose_rotation(int p_bone) const;
+ Vector3 get_bone_pose_scale(int p_bone) const;
void clear_bones_global_pose_override();
Transform3D get_bone_global_pose_override(int p_bone) const;
@@ -222,6 +229,8 @@ public:
void localize_rests(); // used for loaders and tools
+ Ref<Skin> create_skin_from_rest_transforms();
+
Ref<SkinReference> register_skin(const Ref<Skin> &p_skin);
void force_update_all_dirty_bones();
diff --git a/scene/3d/vehicle_body_3d.cpp b/scene/3d/vehicle_body_3d.cpp
index 9a2aaa8be2..6761fdd944 100644
--- a/scene/3d/vehicle_body_3d.cpp
+++ b/scene/3d/vehicle_body_3d.cpp
@@ -275,7 +275,7 @@ void VehicleWheel3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_steering"), &VehicleWheel3D::get_steering);
ADD_GROUP("Per-Wheel Motion", "");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "engine_force", PROPERTY_HINT_RANGE, "0.00,1024.0,0.01,or_greater"), "set_engine_force", "get_engine_force");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "engine_force", PROPERTY_HINT_RANGE, "-1024,1024.0,0.01,or_greater"), "set_engine_force", "get_engine_force");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "brake", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_brake", "get_brake");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "steering", PROPERTY_HINT_RANGE, "-180,180.0,0.01"), "set_steering", "get_steering");
ADD_GROUP("VehicleBody3D Motion", "");
@@ -914,7 +914,7 @@ void VehicleBody3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_steering"), &VehicleBody3D::get_steering);
ADD_GROUP("Motion", "");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "engine_force", PROPERTY_HINT_RANGE, "0.00,1024.0,0.01,or_greater"), "set_engine_force", "get_engine_force");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "engine_force", PROPERTY_HINT_RANGE, "-1024,1024.0,0.01,or_greater"), "set_engine_force", "get_engine_force");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "brake", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_brake", "get_brake");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "steering", PROPERTY_HINT_RANGE, "-180,180.0,0.01"), "set_steering", "get_steering");
}
diff --git a/scene/animation/animation_cache.cpp b/scene/animation/animation_cache.cpp
deleted file mode 100644
index 56743007e4..0000000000
--- a/scene/animation/animation_cache.cpp
+++ /dev/null
@@ -1,314 +0,0 @@
-/*************************************************************************/
-/* animation_cache.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 "animation_cache.h"
-
-void AnimationCache::_node_exit_tree(Node *p_node) {
- //it is one shot, so it disconnects upon arrival
-
- ERR_FAIL_COND(!connected_nodes.has(p_node));
-
- connected_nodes.erase(p_node);
-
- for (int i = 0; i < path_cache.size(); i++) {
- if (path_cache[i].node != p_node) {
- continue;
- }
-
- path_cache.write[i].valid = false; //invalidate path cache
- }
-}
-
-void AnimationCache::_animation_changed() {
- _clear_cache();
-}
-
-void AnimationCache::_clear_cache() {
- while (connected_nodes.size()) {
- connected_nodes.front()->get()->disconnect("tree_exiting", callable_mp(this, &AnimationCache::_node_exit_tree));
- connected_nodes.erase(connected_nodes.front());
- }
- path_cache.clear();
- cache_valid = false;
- cache_dirty = true;
-}
-
-void AnimationCache::_update_cache() {
- cache_valid = false;
-
- ERR_FAIL_COND(!root);
- ERR_FAIL_COND(!root->is_inside_tree());
- ERR_FAIL_COND(animation.is_null());
-
- for (int i = 0; i < animation->get_track_count(); i++) {
- NodePath np = animation->track_get_path(i);
-
- Node *node = root->get_node(np);
- if (!node) {
- path_cache.push_back(Path());
- ERR_CONTINUE_MSG(!node, "Invalid track path in animation '" + np + "'.");
- }
-
- Path path;
-
- Ref<Resource> res;
-
- if (animation->track_get_type(i) == Animation::TYPE_TRANSFORM3D) {
-#ifndef _3D_DISABLED
- if (np.get_subname_count() > 1) {
- path_cache.push_back(Path());
- ERR_CONTINUE_MSG(animation->track_get_type(i) == Animation::TYPE_TRANSFORM3D, "Transform tracks can't have a subpath '" + np + "'.");
- }
-
- Node3D *sp = Object::cast_to<Node3D>(node);
-
- if (!sp) {
- path_cache.push_back(Path());
- ERR_CONTINUE_MSG(!sp, "Transform track not of type Node3D '" + np + "'.");
- }
-
- if (np.get_subname_count() == 1) {
- StringName property = np.get_subname(0);
- String ps = property;
-
- Skeleton3D *sk = Object::cast_to<Skeleton3D>(node);
- if (!sk) {
- path_cache.push_back(Path());
- ERR_CONTINUE_MSG(!sk, "Property defined in Transform track, but not a Skeleton! '" + np + "'.");
- }
-
- int idx = sk->find_bone(ps);
- if (idx == -1) {
- path_cache.push_back(Path());
- ERR_CONTINUE_MSG(idx == -1, "Property defined in Transform track, but not a Skeleton Bone! '" + np + "'.");
- }
-
- path.bone_idx = idx;
- path.skeleton = sk;
- }
-
- path.node_3d = sp;
-#endif // _3D_DISABLED
- } else {
- if (np.get_subname_count() > 0) {
- RES res2;
- Vector<StringName> leftover_subpath;
-
- // We don't want to cache the last resource unless it is a method call
- bool is_method = animation->track_get_type(i) == Animation::TYPE_METHOD;
- root->get_node_and_resource(np, res2, leftover_subpath, is_method);
-
- if (res2.is_valid()) {
- path.resource = res2;
- } else {
- path.node = node;
- }
- path.object = res2.is_valid() ? res2.ptr() : (Object *)node;
- path.subpath = leftover_subpath;
-
- } else {
- path.node = node;
- path.object = node;
- path.subpath = np.get_subnames();
- }
- }
-
- if (animation->track_get_type(i) == Animation::TYPE_VALUE) {
- if (np.get_subname_count() == 0) {
- path_cache.push_back(Path());
- ERR_CONTINUE_MSG(np.get_subname_count() == 0, "Value Track lacks property: " + np + ".");
- }
-
- } else if (animation->track_get_type(i) == Animation::TYPE_METHOD) {
- if (path.subpath.size() != 0) { // Trying to call a method of a non-resource
-
- path_cache.push_back(Path());
- ERR_CONTINUE_MSG(path.subpath.size() != 0, "Method Track has property: " + np + ".");
- }
- }
-
- path.valid = true;
-
- path_cache.push_back(path);
-
- if (!connected_nodes.has(path.node)) {
- connected_nodes.insert(path.node);
- path.node->connect("tree_exiting", callable_mp(this, &AnimationCache::_node_exit_tree), Node::make_binds(path.node), CONNECT_ONESHOT);
- }
- }
-
- cache_dirty = false;
- cache_valid = true;
-}
-
-void AnimationCache::set_track_transform(int p_idx, const Transform3D &p_transform) {
- if (cache_dirty) {
- _update_cache();
- }
-
- ERR_FAIL_COND(!cache_valid);
- ERR_FAIL_INDEX(p_idx, path_cache.size());
- Path &p = path_cache.write[p_idx];
- if (!p.valid) {
- return;
- }
-
-#ifndef _3D_DISABLED
- ERR_FAIL_COND(!p.node);
- ERR_FAIL_COND(!p.node_3d);
-
- if (p.skeleton) {
- p.skeleton->set_bone_pose(p.bone_idx, p_transform);
- } else {
- p.node_3d->set_transform(p_transform);
- }
-#endif // _3D_DISABLED
-}
-
-void AnimationCache::set_track_value(int p_idx, const Variant &p_value) {
- if (cache_dirty) {
- _update_cache();
- }
-
- ERR_FAIL_COND(!cache_valid);
- ERR_FAIL_INDEX(p_idx, path_cache.size());
- Path &p = path_cache.write[p_idx];
- if (!p.valid) {
- return;
- }
-
- ERR_FAIL_COND(!p.object);
- p.object->set_indexed(p.subpath, p_value);
-}
-
-void AnimationCache::call_track(int p_idx, const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
- if (cache_dirty) {
- _update_cache();
- }
-
- ERR_FAIL_COND(!cache_valid);
- ERR_FAIL_INDEX(p_idx, path_cache.size());
- Path &p = path_cache.write[p_idx];
- if (!p.valid) {
- return;
- }
-
- ERR_FAIL_COND(!p.object);
- p.object->call(p_method, p_args, p_argcount, r_error);
-}
-
-void AnimationCache::set_all(float p_time, float p_delta) {
- if (cache_dirty) {
- _update_cache();
- }
-
- ERR_FAIL_COND(!cache_valid);
-
- int tc = animation->get_track_count();
- for (int i = 0; i < tc; i++) {
- switch (animation->track_get_type(i)) {
- case Animation::TYPE_TRANSFORM3D: {
- Vector3 loc, scale;
- Quaternion rot;
- animation->transform_track_interpolate(i, p_time, &loc, &rot, &scale);
- Transform3D tr(Basis(rot), loc);
- tr.basis.scale(scale);
-
- set_track_transform(i, tr);
-
- } break;
- case Animation::TYPE_VALUE: {
- if (animation->value_track_get_update_mode(i) == Animation::UPDATE_CONTINUOUS || (animation->value_track_get_update_mode(i) == Animation::UPDATE_DISCRETE && p_delta == 0)) {
- Variant v = animation->value_track_interpolate(i, p_time);
- set_track_value(i, v);
- } else {
- List<int> indices;
- animation->value_track_get_key_indices(i, p_time, p_delta, &indices);
-
- for (int &E : indices) {
- Variant v = animation->track_get_key_value(i, E);
- set_track_value(i, v);
- }
- }
-
- } break;
- case Animation::TYPE_METHOD: {
- List<int> indices;
- animation->method_track_get_key_indices(i, p_time, p_delta, &indices);
-
- for (int &E : indices) {
- Vector<Variant> args = animation->method_track_get_params(i, E);
- StringName name = animation->method_track_get_name(i, E);
- Callable::CallError err;
-
- if (!args.size()) {
- call_track(i, name, nullptr, 0, err);
- } else {
- Vector<const Variant *> argptrs;
- argptrs.resize(args.size());
- for (int j = 0; j < args.size(); j++) {
- argptrs.write[j] = &args.write[j];
- }
-
- call_track(i, name, (const Variant **)&argptrs[0], args.size(), err);
- }
- }
-
- } break;
- default: {
- }
- }
- }
-}
-
-void AnimationCache::set_animation(const Ref<Animation> &p_animation) {
- _clear_cache();
-
- if (animation.is_valid()) {
- animation->disconnect("changed", callable_mp(this, &AnimationCache::_animation_changed));
- }
-
- animation = p_animation;
-
- if (animation.is_valid()) {
- animation->connect("changed", callable_mp(this, &AnimationCache::_animation_changed));
- }
-}
-
-void AnimationCache::_bind_methods() {
-}
-
-void AnimationCache::set_root(Node *p_root) {
- _clear_cache();
- root = p_root;
-}
-
-AnimationCache::AnimationCache() {
-}
diff --git a/scene/animation/animation_cache.h b/scene/animation/animation_cache.h
deleted file mode 100644
index c856e644f7..0000000000
--- a/scene/animation/animation_cache.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/*************************************************************************/
-/* animation_cache.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 ANIMATION_CACHE_H
-#define ANIMATION_CACHE_H
-
-#include "scene/3d/skeleton_3d.h"
-#include "scene/resources/animation.h"
-
-class AnimationCache : public Object {
- GDCLASS(AnimationCache, Object);
-
- struct Path {
- RES resource;
- Object *object = nullptr;
-#ifndef _3D_DISABLED
- Skeleton3D *skeleton = nullptr;
- Node3D *node_3d = nullptr;
-#endif // _3D_DISABLED
- Node *node = nullptr;
-
- int bone_idx = -1;
- Vector<StringName> subpath;
- bool valid = false;
- };
-
- Set<Node *> connected_nodes;
- Vector<Path> path_cache;
-
- Node *root = nullptr;
- Ref<Animation> animation;
- bool cache_dirty = true;
- bool cache_valid = false;
-
- void _node_exit_tree(Node *p_node);
-
- void _clear_cache();
- void _update_cache();
- void _animation_changed();
-
-protected:
- static void _bind_methods();
-
-public:
- void set_track_transform(int p_idx, const Transform3D &p_transform);
- void set_track_value(int p_idx, const Variant &p_value);
- void call_track(int p_idx, const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error);
-
- void set_all(float p_time, float p_delta = 0);
-
- void set_animation(const Ref<Animation> &p_animation);
- void set_root(Node *p_root);
-
- AnimationCache();
-};
-
-#endif // ANIMATION_CACHE_H
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index 2c8c4ee788..8407e0dd99 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -60,7 +60,12 @@ void AnimatedValuesBackup::restore() const {
if (entry->bone_idx == -1) {
entry->object->set_indexed(entry->subpath, entry->value);
} else {
- Object::cast_to<Skeleton3D>(entry->object)->set_bone_pose(entry->bone_idx, entry->value);
+ Array arr = entry->value;
+ if (arr.size() == 3) {
+ Object::cast_to<Skeleton3D>(entry->object)->set_bone_pose_position(entry->bone_idx, arr[0]);
+ Object::cast_to<Skeleton3D>(entry->object)->set_bone_pose_rotation(entry->bone_idx, arr[1]);
+ Object::cast_to<Skeleton3D>(entry->object)->set_bone_pose_scale(entry->bone_idx, arr[0]);
+ }
}
}
}
@@ -242,6 +247,8 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov
p_anim->node_cache.resize(a->get_track_count());
+ setup_pass++;
+
for (int i = 0; i < a->get_track_count(); i++) {
p_anim->node_cache.write[i] = nullptr;
RES resource;
@@ -275,46 +282,68 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov
node_cache_map[key] = TrackNodeCache();
}
- p_anim->node_cache.write[i] = &node_cache_map[key];
- p_anim->node_cache[i]->path = a->track_get_path(i);
- p_anim->node_cache[i]->node = child;
- p_anim->node_cache[i]->resource = resource;
- p_anim->node_cache[i]->node_2d = Object::cast_to<Node2D>(child);
+ TrackNodeCache *node_cache = &node_cache_map[key];
+ p_anim->node_cache.write[i] = node_cache;
+
+ node_cache->path = a->track_get_path(i);
+ node_cache->node = child;
+ node_cache->resource = resource;
+ node_cache->node_2d = Object::cast_to<Node2D>(child);
#ifndef _3D_DISABLED
- if (a->track_get_type(i) == Animation::TYPE_TRANSFORM3D) {
+ if (a->track_get_type(i) == Animation::TYPE_POSITION_3D || a->track_get_type(i) == Animation::TYPE_ROTATION_3D || a->track_get_type(i) == Animation::TYPE_SCALE_3D) {
// special cases and caches for transform tracks
+ if (node_cache->last_setup_pass != setup_pass) {
+ node_cache->loc_used = false;
+ node_cache->rot_used = false;
+ node_cache->scale_used = false;
+ }
+
// cache node_3d
- p_anim->node_cache[i]->node_3d = Object::cast_to<Node3D>(child);
+ node_cache->node_3d = Object::cast_to<Node3D>(child);
// cache skeleton
- p_anim->node_cache[i]->skeleton = Object::cast_to<Skeleton3D>(child);
- if (p_anim->node_cache[i]->skeleton) {
+ node_cache->skeleton = Object::cast_to<Skeleton3D>(child);
+ if (node_cache->skeleton) {
if (a->track_get_path(i).get_subname_count() == 1) {
StringName bone_name = a->track_get_path(i).get_subname(0);
- p_anim->node_cache[i]->bone_idx = p_anim->node_cache[i]->skeleton->find_bone(bone_name);
- if (p_anim->node_cache[i]->bone_idx < 0) {
+ node_cache->bone_idx = node_cache->skeleton->find_bone(bone_name);
+ if (node_cache->bone_idx < 0) {
// broken track (nonexistent bone)
- p_anim->node_cache[i]->skeleton = nullptr;
- p_anim->node_cache[i]->node_3d = nullptr;
- ERR_CONTINUE(p_anim->node_cache[i]->bone_idx < 0);
+ node_cache->skeleton = nullptr;
+ node_cache->node_3d = nullptr;
+ ERR_CONTINUE(node_cache->bone_idx < 0);
}
} else {
// no property, just use spatialnode
- p_anim->node_cache[i]->skeleton = nullptr;
+ node_cache->skeleton = nullptr;
+ }
+ }
+
+ switch (a->track_get_type(i)) {
+ case Animation::TYPE_POSITION_3D: {
+ node_cache->loc_used = true;
+ } break;
+ case Animation::TYPE_ROTATION_3D: {
+ node_cache->rot_used = true;
+ } break;
+ case Animation::TYPE_SCALE_3D: {
+ node_cache->scale_used = true;
+ } break;
+ default: {
}
}
}
#endif // _3D_DISABLED
if (a->track_get_type(i) == Animation::TYPE_VALUE) {
- if (!p_anim->node_cache[i]->property_anim.has(a->track_get_path(i).get_concatenated_subnames())) {
+ if (!node_cache->property_anim.has(a->track_get_path(i).get_concatenated_subnames())) {
TrackNodeCache::PropertyAnim pa;
pa.subpath = leftover_path;
pa.object = resource.is_valid() ? (Object *)resource.ptr() : (Object *)child;
pa.special = SP_NONE;
pa.owner = p_anim->node_cache[i];
- if (false && p_anim->node_cache[i]->node_2d) {
+ if (false && node_cache->node_2d) {
if (leftover_path.size() == 1 && leftover_path[0] == SceneStringNames::get_singleton()->transform_pos) {
pa.special = SP_NODE2D_POS;
} else if (leftover_path.size() == 1 && leftover_path[0] == SceneStringNames::get_singleton()->transform_rot) {
@@ -323,20 +352,22 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov
pa.special = SP_NODE2D_SCALE;
}
}
- p_anim->node_cache[i]->property_anim[a->track_get_path(i).get_concatenated_subnames()] = pa;
+ node_cache->property_anim[a->track_get_path(i).get_concatenated_subnames()] = pa;
}
}
if (a->track_get_type(i) == Animation::TYPE_BEZIER && leftover_path.size()) {
- if (!p_anim->node_cache[i]->bezier_anim.has(a->track_get_path(i).get_concatenated_subnames())) {
+ if (!node_cache->bezier_anim.has(a->track_get_path(i).get_concatenated_subnames())) {
TrackNodeCache::BezierAnim ba;
ba.bezier_property = leftover_path;
ba.object = resource.is_valid() ? (Object *)resource.ptr() : (Object *)child;
ba.owner = p_anim->node_cache[i];
- p_anim->node_cache[i]->bezier_anim[a->track_get_path(i).get_concatenated_subnames()] = ba;
+ node_cache->bezier_anim[a->track_get_path(i).get_concatenated_subnames()] = ba;
}
}
+
+ node_cache->last_setup_pass = setup_pass;
}
}
@@ -369,17 +400,15 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
}
switch (a->track_get_type(i)) {
- case Animation::TYPE_TRANSFORM3D: {
+ case Animation::TYPE_POSITION_3D: {
#ifndef _3D_DISABLED
if (!nc->node_3d) {
continue;
}
Vector3 loc;
- Quaternion rot;
- Vector3 scale;
- Error err = a->transform_track_interpolate(i, p_time, &loc, &rot, &scale);
+ Error err = a->position_track_interpolate(i, p_time, &loc);
//ERR_CONTINUE(err!=OK); //used for testing, should be removed
if (err != OK) {
@@ -391,12 +420,63 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
cache_update[cache_update_size++] = nc;
nc->accum_pass = accum_pass;
nc->loc_accum = loc;
- nc->rot_accum = rot;
- nc->scale_accum = scale;
-
+ nc->rot_accum = Quaternion();
+ nc->scale_accum = Vector3();
} else {
nc->loc_accum = nc->loc_accum.lerp(loc, p_interp);
+ }
+#endif // _3D_DISABLED
+ } break;
+ case Animation::TYPE_ROTATION_3D: {
+#ifndef _3D_DISABLED
+ if (!nc->node_3d) {
+ continue;
+ }
+
+ Quaternion rot;
+
+ Error err = a->rotation_track_interpolate(i, p_time, &rot);
+ //ERR_CONTINUE(err!=OK); //used for testing, should be removed
+
+ if (err != OK) {
+ continue;
+ }
+
+ if (nc->accum_pass != accum_pass) {
+ ERR_CONTINUE(cache_update_size >= NODE_CACHE_UPDATE_MAX);
+ cache_update[cache_update_size++] = nc;
+ nc->accum_pass = accum_pass;
+ nc->loc_accum = Vector3();
+ nc->rot_accum = rot;
+ nc->scale_accum = Vector3();
+ } else {
nc->rot_accum = nc->rot_accum.slerp(rot, p_interp);
+ }
+#endif // _3D_DISABLED
+ } break;
+ case Animation::TYPE_SCALE_3D: {
+#ifndef _3D_DISABLED
+ if (!nc->node_3d) {
+ continue;
+ }
+
+ Vector3 scale;
+
+ Error err = a->scale_track_interpolate(i, p_time, &scale);
+ //ERR_CONTINUE(err!=OK); //used for testing, should be removed
+
+ if (err != OK) {
+ continue;
+ }
+
+ if (nc->accum_pass != accum_pass) {
+ ERR_CONTINUE(cache_update_size >= NODE_CACHE_UPDATE_MAX);
+ cache_update[cache_update_size++] = nc;
+ nc->accum_pass = accum_pass;
+ nc->loc_accum = Vector3();
+ nc->rot_accum = Quaternion();
+ nc->scale_accum = scale;
+ } else {
nc->scale_accum = nc->scale_accum.lerp(scale, p_interp);
}
#endif // _3D_DISABLED
@@ -855,15 +935,30 @@ void AnimationPlayer::_animation_update_transforms() {
TrackNodeCache *nc = cache_update[i];
ERR_CONTINUE(nc->accum_pass != accum_pass);
-
- t.origin = nc->loc_accum;
- t.basis.set_quaternion_scale(nc->rot_accum, nc->scale_accum);
#ifndef _3D_DISABLED
if (nc->skeleton && nc->bone_idx >= 0) {
- nc->skeleton->set_bone_pose(nc->bone_idx, t);
+ if (nc->loc_used) {
+ nc->skeleton->set_bone_pose_position(nc->bone_idx, nc->loc_accum);
+ }
+ if (nc->rot_used) {
+ nc->skeleton->set_bone_pose_rotation(nc->bone_idx, nc->rot_accum);
+ }
+ if (nc->scale_used) {
+ nc->skeleton->set_bone_pose_scale(nc->bone_idx, nc->scale_accum);
+ }
+
} else if (nc->node_3d) {
- nc->node_3d->set_transform(t);
+ if (nc->loc_used) {
+ nc->node_3d->set_position(nc->loc_accum);
+ }
+ if (nc->rot_used) {
+ nc->node_3d->set_rotation(nc->rot_accum.get_euler());
+ }
+ if (nc->scale_used) {
+ nc->node_3d->set_scale(nc->scale_accum);
+ }
}
+
#endif // _3D_DISABLED
}
}
@@ -1527,7 +1622,12 @@ Ref<AnimatedValuesBackup> AnimationPlayer::backup_animated_values(Node *p_root_o
AnimatedValuesBackup::Entry entry;
entry.object = nc->skeleton;
entry.bone_idx = nc->bone_idx;
- entry.value = nc->skeleton->get_bone_pose(nc->bone_idx);
+ Array arr;
+ arr.resize(3);
+ arr[0] = nc->skeleton->get_bone_pose_position(nc->bone_idx);
+ arr[1] = nc->skeleton->get_bone_pose_rotation(nc->bone_idx);
+ arr[2] = nc->skeleton->get_bone_pose_scale(nc->bone_idx);
+ entry.value = nc;
backup->entries.push_back(entry);
} else {
if (nc->node_3d) {
diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h
index b693e29bdf..f8e72a67b6 100644
--- a/scene/animation/animation_player.h
+++ b/scene/animation/animation_player.h
@@ -88,6 +88,8 @@ private:
SP_NODE2D_SCALE,
};
+ uint32_t setup_pass = 1;
+
struct TrackNodeCache {
NodePath path;
uint32_t id = 0;
@@ -101,6 +103,10 @@ private:
int bone_idx = -1;
// accumulated transforms
+ bool loc_used = false;
+ bool rot_used = false;
+ bool scale_used = false;
+
Vector3 loc_accum;
Quaternion rot_accum;
Vector3 scale_accum;
@@ -134,6 +140,7 @@ private:
Map<StringName, BezierAnim> bezier_anim;
+ uint32_t last_setup_pass = 0;
TrackNodeCache() {}
};
diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp
index 9ca8d478b1..dd5fe46223 100644
--- a/scene/animation/animation_tree.cpp
+++ b/scene/animation/animation_tree.cpp
@@ -544,13 +544,18 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
NodePath path = anim->track_get_path(i);
Animation::TrackType track_type = anim->track_get_type(i);
+ Animation::TrackType track_cache_type = track_type;
+ if (track_cache_type == Animation::TYPE_POSITION_3D || track_cache_type == Animation::TYPE_ROTATION_3D || track_cache_type == Animation::TYPE_SCALE_3D) {
+ track_cache_type = Animation::TYPE_POSITION_3D; //reference them as position3D tracks, even if they modify rotation or scale
+ }
+
TrackCache *track = nullptr;
if (track_cache.has(path)) {
track = track_cache.get(path);
}
//if not valid, delete track
- if (track && (track->type != track_type || ObjectDB::get_instance(track->object_id) == nullptr)) {
+ if (track && (track->type != track_cache_type || ObjectDB::get_instance(track->object_id) == nullptr)) {
playing_caches.erase(track);
memdelete(track);
track_cache.erase(path);
@@ -587,7 +592,9 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
track = track_value;
} break;
- case Animation::TYPE_TRANSFORM3D: {
+ case Animation::TYPE_POSITION_3D:
+ case Animation::TYPE_ROTATION_3D:
+ case Animation::TYPE_SCALE_3D: {
#ifndef _3D_DISABLED
Node3D *node_3d = Object::cast_to<Node3D>(child);
@@ -597,6 +604,7 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
}
TrackCacheTransform *track_xform = memnew(TrackCacheTransform);
+ track_xform->type = Animation::TYPE_POSITION_3D;
track_xform->node_3d = node_3d;
track_xform->skeleton = nullptr;
@@ -615,6 +623,21 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
track_xform->object_id = track_xform->object->get_instance_id();
track = track_xform;
+
+ switch (track_type) {
+ case Animation::TYPE_POSITION_3D: {
+ track_xform->loc_used = true;
+ } break;
+ case Animation::TYPE_ROTATION_3D: {
+ track_xform->rot_used = true;
+ } break;
+ case Animation::TYPE_SCALE_3D: {
+ track_xform->scale_used = true;
+ } break;
+ default: {
+ }
+ }
+
#endif // _3D_DISABLED
} break;
case Animation::TYPE_METHOD: {
@@ -670,6 +693,26 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
}
track_cache[path] = track;
+ } else if (track_cache_type == Animation::TYPE_POSITION_3D) {
+ TrackCacheTransform *track_xform = static_cast<TrackCacheTransform *>(track);
+ if (track->setup_pass != setup_pass) {
+ track_xform->loc_used = false;
+ track_xform->rot_used = false;
+ track_xform->scale_used = false;
+ }
+ switch (track_type) {
+ case Animation::TYPE_POSITION_3D: {
+ track_xform->loc_used = true;
+ } break;
+ case Animation::TYPE_ROTATION_3D: {
+ track_xform->rot_used = true;
+ } break;
+ case Animation::TYPE_SCALE_3D: {
+ track_xform->scale_used = true;
+ } break;
+ default: {
+ }
+ }
}
track->setup_pass = setup_pass;
@@ -831,8 +874,11 @@ void AnimationTree::_process_graph(real_t p_delta) {
ERR_CONTINUE(!track_cache.has(path));
TrackCache *track = track_cache[path];
- if (track->type != a->track_get_type(i)) {
- continue; //may happen should not
+
+ Animation::TrackType ttype = a->track_get_type(i);
+ if (ttype != Animation::TYPE_POSITION_3D && ttype != Animation::TYPE_ROTATION_3D && ttype != Animation::TYPE_SCALE_3D && track->type != ttype) {
+ //broken animation, but avoid error spamming
+ continue;
}
track->root_motion = root_motion_track == path;
@@ -848,20 +894,81 @@ void AnimationTree::_process_graph(real_t p_delta) {
continue; //nothing to blend
}
- switch (track->type) {
- case Animation::TYPE_TRANSFORM3D: {
+ switch (ttype) {
+ case Animation::TYPE_POSITION_3D: {
#ifndef _3D_DISABLED
TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
+ if (t->process_pass != process_pass) {
+ t->process_pass = process_pass;
+ t->loc = Vector3();
+ t->rot = Quaternion();
+ t->rot_blend_accum = 0;
+ t->scale = Vector3(1, 1, 1);
+ }
+
if (track->root_motion) {
- if (t->process_pass != process_pass) {
- t->process_pass = process_pass;
- t->loc = Vector3();
- t->rot = Quaternion();
- t->rot_blend_accum = 0;
- t->scale = Vector3(1, 1, 1);
+ real_t prev_time = time - delta;
+ if (prev_time < 0) {
+ if (!a->has_loop()) {
+ prev_time = 0;
+ } else {
+ prev_time = a->get_length() + prev_time;
+ }
}
+ Vector3 loc[2];
+
+ if (prev_time > time) {
+ Error err = a->position_track_interpolate(i, prev_time, &loc[0]);
+ if (err != OK) {
+ continue;
+ }
+
+ a->position_track_interpolate(i, a->get_length(), &loc[1]);
+
+ t->loc += (loc[1] - loc[0]) * blend;
+ prev_time = 0;
+ }
+
+ Error err = a->position_track_interpolate(i, prev_time, &loc[0]);
+ if (err != OK) {
+ continue;
+ }
+
+ a->position_track_interpolate(i, time, &loc[1]);
+
+ t->loc += (loc[1] - loc[0]) * blend;
+
+ prev_time = 0;
+
+ } else {
+ Vector3 loc;
+
+ Error err = a->position_track_interpolate(i, time, &loc);
+ //ERR_CONTINUE(err!=OK); //used for testing, should be removed
+
+ if (err != OK) {
+ continue;
+ }
+
+ t->loc = t->loc.lerp(loc, blend);
+ }
+#endif // _3D_DISABLED
+ } break;
+ case Animation::TYPE_ROTATION_3D: {
+#ifndef _3D_DISABLED
+ TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
+
+ if (t->process_pass != process_pass) {
+ t->process_pass = process_pass;
+ t->loc = Vector3();
+ t->rot = Quaternion();
+ t->rot_blend_accum = 0;
+ t->scale = Vector3(1, 1, 1);
+ }
+
+ if (track->root_motion) {
real_t prev_time = time - delta;
if (prev_time < 0) {
if (!a->has_loop()) {
@@ -871,61 +978,44 @@ void AnimationTree::_process_graph(real_t p_delta) {
}
}
- Vector3 loc[2];
Quaternion rot[2];
- Vector3 scale[2];
if (prev_time > time) {
- Error err = a->transform_track_interpolate(i, prev_time, &loc[0], &rot[0], &scale[0]);
+ Error err = a->rotation_track_interpolate(i, prev_time, &rot[0]);
if (err != OK) {
continue;
}
- a->transform_track_interpolate(i, a->get_length(), &loc[1], &rot[1], &scale[1]);
+ a->rotation_track_interpolate(i, a->get_length(), &rot[1]);
- t->loc += (loc[1] - loc[0]) * blend;
- t->scale += (scale[1] - scale[0]) * blend;
Quaternion q = Quaternion().slerp(rot[0].normalized().inverse() * rot[1].normalized(), blend).normalized();
t->rot = (t->rot * q).normalized();
prev_time = 0;
}
- Error err = a->transform_track_interpolate(i, prev_time, &loc[0], &rot[0], &scale[0]);
+ Error err = a->rotation_track_interpolate(i, prev_time, &rot[0]);
if (err != OK) {
continue;
}
- a->transform_track_interpolate(i, time, &loc[1], &rot[1], &scale[1]);
+ a->rotation_track_interpolate(i, time, &rot[1]);
- t->loc += (loc[1] - loc[0]) * blend;
- t->scale += (scale[1] - scale[0]) * blend;
Quaternion q = Quaternion().slerp(rot[0].normalized().inverse() * rot[1].normalized(), blend).normalized();
t->rot = (t->rot * q).normalized();
prev_time = 0;
} else {
- Vector3 loc;
Quaternion rot;
- Vector3 scale;
- Error err = a->transform_track_interpolate(i, time, &loc, &rot, &scale);
+ Error err = a->rotation_track_interpolate(i, time, &rot);
//ERR_CONTINUE(err!=OK); //used for testing, should be removed
- if (t->process_pass != process_pass) {
- t->process_pass = process_pass;
- t->loc = loc;
- t->rot = rot;
- t->rot_blend_accum = 0;
- t->scale = scale;
- }
-
if (err != OK) {
continue;
}
- t->loc = t->loc.lerp(loc, blend);
if (t->rot_blend_accum == 0) {
t->rot = rot;
t->rot_blend_accum = blend;
@@ -934,6 +1024,67 @@ void AnimationTree::_process_graph(real_t p_delta) {
t->rot = rot.slerp(t->rot, t->rot_blend_accum / rot_total).normalized();
t->rot_blend_accum = rot_total;
}
+ }
+#endif // _3D_DISABLED
+ } break;
+ case Animation::TYPE_SCALE_3D: {
+#ifndef _3D_DISABLED
+ TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
+
+ if (t->process_pass != process_pass) {
+ t->process_pass = process_pass;
+ t->loc = Vector3();
+ t->rot = Quaternion();
+ t->rot_blend_accum = 0;
+ t->scale = Vector3(1, 1, 1);
+ }
+
+ if (track->root_motion) {
+ real_t prev_time = time - delta;
+ if (prev_time < 0) {
+ if (!a->has_loop()) {
+ prev_time = 0;
+ } else {
+ prev_time = a->get_length() + prev_time;
+ }
+ }
+
+ Vector3 scale[2];
+
+ if (prev_time > time) {
+ Error err = a->scale_track_interpolate(i, prev_time, &scale[0]);
+ if (err != OK) {
+ continue;
+ }
+
+ a->scale_track_interpolate(i, a->get_length(), &scale[1]);
+
+ t->scale += (scale[1] - scale[0]) * blend;
+
+ prev_time = 0;
+ }
+
+ Error err = a->scale_track_interpolate(i, prev_time, &scale[0]);
+ if (err != OK) {
+ continue;
+ }
+
+ a->scale_track_interpolate(i, time, &scale[1]);
+
+ t->scale += (scale[1] - scale[0]) * blend;
+
+ prev_time = 0;
+
+ } else {
+ Vector3 scale;
+
+ Error err = a->scale_track_interpolate(i, time, &scale);
+ //ERR_CONTINUE(err!=OK); //used for testing, should be removed
+
+ if (err != OK) {
+ continue;
+ }
+
t->scale = t->scale.lerp(scale, blend);
}
#endif // _3D_DISABLED
@@ -1198,26 +1349,38 @@ void AnimationTree::_process_graph(real_t p_delta) {
}
switch (track->type) {
- case Animation::TYPE_TRANSFORM3D: {
+ case Animation::TYPE_POSITION_3D: {
#ifndef _3D_DISABLED
TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
- Transform3D xform;
- xform.origin = t->loc;
-
- xform.basis.set_quaternion_scale(t->rot, t->scale);
-
if (t->root_motion) {
+ Transform3D xform;
+ xform.origin = t->loc;
+ xform.basis.set_quaternion_scale(t->rot, t->scale);
+
root_motion_transform = xform;
- if (t->skeleton && t->bone_idx >= 0) {
- root_motion_transform = (t->skeleton->get_bone_rest(t->bone_idx) * root_motion_transform) * t->skeleton->get_bone_rest(t->bone_idx).affine_inverse();
- }
} else if (t->skeleton && t->bone_idx >= 0) {
- t->skeleton->set_bone_pose(t->bone_idx, xform);
+ if (t->loc_used) {
+ t->skeleton->set_bone_pose_position(t->bone_idx, t->loc);
+ }
+ if (t->rot_used) {
+ t->skeleton->set_bone_pose_rotation(t->bone_idx, t->rot);
+ }
+ if (t->scale_used) {
+ t->skeleton->set_bone_pose_scale(t->bone_idx, t->scale);
+ }
} else if (!t->skeleton) {
- t->node_3d->set_transform(xform);
+ if (t->loc_used) {
+ t->node_3d->set_position(t->loc);
+ }
+ if (t->rot_used) {
+ t->node_3d->set_rotation(t->rot.get_euler());
+ }
+ if (t->scale_used) {
+ t->node_3d->set_scale(t->scale);
+ }
}
#endif // _3D_DISABLED
} break;
diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h
index 1e0267682e..ed207dfe0c 100644
--- a/scene/animation/animation_tree.h
+++ b/scene/animation/animation_tree.h
@@ -197,13 +197,16 @@ private:
Skeleton3D *skeleton = nullptr;
#endif // _3D_DISABLED
int bone_idx = -1;
+ bool loc_used = false;
+ bool rot_used = false;
+ bool scale_used = false;
Vector3 loc;
Quaternion rot;
real_t rot_blend_accum = 0.0;
Vector3 scale;
TrackCacheTransform() {
- type = Animation::TYPE_TRANSFORM3D;
+ type = Animation::TYPE_POSITION_3D;
}
};
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp
index b4eec2530b..6612c5ff3c 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -43,8 +43,12 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
if (tracks.size() == track && what == "type") {
String type = p_value;
- if (type == "transform" || type == "transform3d") {
- add_track(TYPE_TRANSFORM3D);
+ if (type == "position_3d") {
+ add_track(TYPE_POSITION_3D);
+ } else if (type == "rotation_3d") {
+ add_track(TYPE_ROTATION_3D);
+ } else if (type == "scale_3d") {
+ add_track(TYPE_SCALE_3D);
} else if (type == "value") {
add_track(TYPE_VALUE);
} else if (type == "method") {
@@ -75,35 +79,72 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
} else if (what == "enabled") {
track_set_enabled(track, p_value);
} else if (what == "keys" || what == "key_values") {
- if (track_get_type(track) == TYPE_TRANSFORM3D) {
- TransformTrack *tt = static_cast<TransformTrack *>(tracks[track]);
+ if (track_get_type(track) == TYPE_POSITION_3D) {
+ PositionTrack *tt = static_cast<PositionTrack *>(tracks[track]);
Vector<real_t> values = p_value;
int vcount = values.size();
- ERR_FAIL_COND_V(vcount % TRANSFORM_TRACK_SIZE, false);
+ ERR_FAIL_COND_V(vcount % POSITION_TRACK_SIZE, false);
const real_t *r = values.ptr();
- int64_t count = vcount / TRANSFORM_TRACK_SIZE;
- tt->transforms.resize(count);
+ int64_t count = vcount / POSITION_TRACK_SIZE;
+ tt->positions.resize(count);
+ TKey<Vector3> *tw = tt->positions.ptrw();
for (int i = 0; i < count; i++) {
- TKey<TransformKey> &tk = tt->transforms.write[i];
- const real_t *ofs = &r[i * TRANSFORM_TRACK_SIZE];
+ TKey<Vector3> &tk = tw[i];
+ const real_t *ofs = &r[i * POSITION_TRACK_SIZE];
tk.time = ofs[0];
tk.transition = ofs[1];
- tk.value.loc.x = ofs[2];
- tk.value.loc.y = ofs[3];
- tk.value.loc.z = ofs[4];
+ tk.value.x = ofs[2];
+ tk.value.y = ofs[3];
+ tk.value.z = ofs[4];
+ }
+ } else if (track_get_type(track) == TYPE_ROTATION_3D) {
+ RotationTrack *rt = static_cast<RotationTrack *>(tracks[track]);
+ Vector<real_t> values = p_value;
+ int vcount = values.size();
+ ERR_FAIL_COND_V(vcount % ROTATION_TRACK_SIZE, false);
+
+ const real_t *r = values.ptr();
+
+ int64_t count = vcount / ROTATION_TRACK_SIZE;
+ rt->rotations.resize(count);
+
+ TKey<Quaternion> *rw = rt->rotations.ptrw();
+ for (int i = 0; i < count; i++) {
+ TKey<Quaternion> &rk = rw[i];
+ const real_t *ofs = &r[i * ROTATION_TRACK_SIZE];
+ rk.time = ofs[0];
+ rk.transition = ofs[1];
+
+ rk.value.x = ofs[2];
+ rk.value.y = ofs[3];
+ rk.value.z = ofs[4];
+ rk.value.w = ofs[5];
+ }
+ } else if (track_get_type(track) == TYPE_SCALE_3D) {
+ ScaleTrack *st = static_cast<ScaleTrack *>(tracks[track]);
+ Vector<real_t> values = p_value;
+ int vcount = values.size();
+ ERR_FAIL_COND_V(vcount % SCALE_TRACK_SIZE, false);
- tk.value.rot.x = ofs[5];
- tk.value.rot.y = ofs[6];
- tk.value.rot.z = ofs[7];
- tk.value.rot.w = ofs[8];
+ const real_t *r = values.ptr();
+
+ int64_t count = vcount / SCALE_TRACK_SIZE;
+ st->scales.resize(count);
- tk.value.scale.x = ofs[9];
- tk.value.scale.y = ofs[10];
- tk.value.scale.z = ofs[11];
+ TKey<Vector3> *sw = st->scales.ptrw();
+ for (int i = 0; i < count; i++) {
+ TKey<Vector3> &sk = sw[i];
+ const real_t *ofs = &r[i * SCALE_TRACK_SIZE];
+ sk.time = ofs[0];
+ sk.transition = ofs[1];
+
+ sk.value.x = ofs[2];
+ sk.value.y = ofs[3];
+ sk.value.z = ofs[4];
}
} else if (track_get_type(track) == TYPE_VALUE) {
@@ -319,8 +360,14 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
ERR_FAIL_INDEX_V(track, tracks.size(), false);
if (what == "type") {
switch (track_get_type(track)) {
- case TYPE_TRANSFORM3D:
- r_ret = "transform";
+ case TYPE_POSITION_3D:
+ r_ret = "position_3d";
+ break;
+ case TYPE_ROTATION_3D:
+ r_ret = "rotation_3d";
+ break;
+ case TYPE_SCALE_3D:
+ r_ret = "scale_3d";
break;
case TYPE_VALUE:
r_ret = "value";
@@ -352,31 +399,64 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
} else if (what == "enabled") {
r_ret = track_is_enabled(track);
} else if (what == "keys") {
- if (track_get_type(track) == TYPE_TRANSFORM3D) {
+ if (track_get_type(track) == TYPE_POSITION_3D) {
Vector<real_t> keys;
int kk = track_get_key_count(track);
- keys.resize(kk * TRANSFORM_TRACK_SIZE);
+ keys.resize(kk * POSITION_TRACK_SIZE);
real_t *w = keys.ptrw();
int idx = 0;
for (int i = 0; i < track_get_key_count(track); i++) {
Vector3 loc;
- Quaternion rot;
- Vector3 scale;
- transform_track_get_key(track, i, &loc, &rot, &scale);
+ position_track_get_key(track, i, &loc);
w[idx++] = track_get_key_time(track, i);
w[idx++] = track_get_key_transition(track, i);
w[idx++] = loc.x;
w[idx++] = loc.y;
w[idx++] = loc.z;
+ }
+ r_ret = keys;
+ return true;
+ } else if (track_get_type(track) == TYPE_ROTATION_3D) {
+ Vector<real_t> keys;
+ int kk = track_get_key_count(track);
+ keys.resize(kk * ROTATION_TRACK_SIZE);
+
+ real_t *w = keys.ptrw();
+
+ int idx = 0;
+ for (int i = 0; i < track_get_key_count(track); i++) {
+ Quaternion rot;
+ rotation_track_get_key(track, i, &rot);
+
+ w[idx++] = track_get_key_time(track, i);
+ w[idx++] = track_get_key_transition(track, i);
w[idx++] = rot.x;
w[idx++] = rot.y;
w[idx++] = rot.z;
w[idx++] = rot.w;
+ }
+ r_ret = keys;
+ return true;
+
+ } else if (track_get_type(track) == TYPE_SCALE_3D) {
+ Vector<real_t> keys;
+ int kk = track_get_key_count(track);
+ keys.resize(kk * SCALE_TRACK_SIZE);
+
+ real_t *w = keys.ptrw();
+
+ int idx = 0;
+ for (int i = 0; i < track_get_key_count(track); i++) {
+ Vector3 scale;
+ scale_track_get_key(track, i, &scale);
+
+ w[idx++] = track_get_key_time(track, i);
+ w[idx++] = track_get_key_transition(track, i);
w[idx++] = scale.x;
w[idx++] = scale.y;
w[idx++] = scale.z;
@@ -384,7 +464,6 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
r_ret = keys;
return true;
-
} else if (track_get_type(track) == TYPE_VALUE) {
const ValueTrack *vt = static_cast<const ValueTrack *>(tracks[track]);
@@ -591,10 +670,18 @@ int Animation::add_track(TrackType p_type, int p_at_pos) {
}
switch (p_type) {
- case TYPE_TRANSFORM3D: {
- TransformTrack *tt = memnew(TransformTrack);
+ case TYPE_POSITION_3D: {
+ PositionTrack *tt = memnew(PositionTrack);
tracks.insert(p_at_pos, tt);
} break;
+ case TYPE_ROTATION_3D: {
+ RotationTrack *rt = memnew(RotationTrack);
+ tracks.insert(p_at_pos, rt);
+ } break;
+ case TYPE_SCALE_3D: {
+ ScaleTrack *st = memnew(ScaleTrack);
+ tracks.insert(p_at_pos, st);
+ } break;
case TYPE_VALUE: {
tracks.insert(p_at_pos, memnew(ValueTrack));
@@ -629,9 +716,19 @@ void Animation::remove_track(int p_track) {
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM3D: {
- TransformTrack *tt = static_cast<TransformTrack *>(t);
- _clear(tt->transforms);
+ case TYPE_POSITION_3D: {
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+ _clear(tt->positions);
+
+ } break;
+ case TYPE_ROTATION_3D: {
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+ _clear(rt->rotations);
+
+ } break;
+ case TYPE_SCALE_3D: {
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ _clear(st->scales);
} break;
case TYPE_VALUE: {
@@ -672,7 +769,7 @@ int Animation::get_track_count() const {
}
Animation::TrackType Animation::track_get_type(int p_track) const {
- ERR_FAIL_INDEX_V(p_track, tracks.size(), TYPE_TRANSFORM3D);
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), TYPE_VALUE);
return tracks[p_track]->type;
}
@@ -720,31 +817,6 @@ bool Animation::track_get_interpolation_loop_wrap(int p_track) const {
return tracks[p_track]->loop_wrap;
}
-// transform
-/*
-template<class T>
-int Animation::_insert_pos(double p_time, T& p_keys) {
- // simple, linear time inset that should be fast enough in reality.
-
- int idx=p_keys.size();
-
- while(true) {
-
-
- if (idx==0 || p_keys[idx-1].time < p_time) {
- //condition for insertion.
- p_keys.insert(idx,T());
- return idx;
- } else if (p_keys[idx-1].time == p_time) {
- // condition for replacing.
- return idx-1;
- }
-
- idx--;
- }
-}
-
-*/
template <class T, class V>
int Animation::_insert(double p_time, T &p_keys, const V &p_value) {
int idx = p_keys.size();
@@ -774,45 +846,153 @@ void Animation::_clear(T &p_keys) {
p_keys.clear();
}
-Error Animation::transform_track_get_key(int p_track, int p_key, Vector3 *r_loc, Quaternion *r_rot, Vector3 *r_scale) const {
+////
+
+int Animation::position_track_insert_key(int p_track, double p_time, const Vector3 &p_position) {
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), -1);
+ Track *t = tracks[p_track];
+ ERR_FAIL_COND_V(t->type != TYPE_POSITION_3D, -1);
+
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+
+ TKey<Vector3> tkey;
+ tkey.time = p_time;
+ tkey.value = p_position;
+
+ int ret = _insert(p_time, tt->positions, tkey);
+ emit_changed();
+ return ret;
+}
+
+Error Animation::position_track_get_key(int p_track, int p_key, Vector3 *r_position) const {
ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
Track *t = tracks[p_track];
- TransformTrack *tt = static_cast<TransformTrack *>(t);
- ERR_FAIL_COND_V(t->type != TYPE_TRANSFORM3D, ERR_INVALID_PARAMETER);
- ERR_FAIL_INDEX_V(p_key, tt->transforms.size(), ERR_INVALID_PARAMETER);
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+ ERR_FAIL_COND_V(t->type != TYPE_POSITION_3D, ERR_INVALID_PARAMETER);
+ ERR_FAIL_INDEX_V(p_key, tt->positions.size(), ERR_INVALID_PARAMETER);
- if (r_loc) {
- *r_loc = tt->transforms[p_key].value.loc;
- }
- if (r_rot) {
- *r_rot = tt->transforms[p_key].value.rot;
- }
- if (r_scale) {
- *r_scale = tt->transforms[p_key].value.scale;
+ *r_position = tt->positions[p_key].value;
+
+ return OK;
+}
+
+Error Animation::position_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation) const {
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
+ Track *t = tracks[p_track];
+ ERR_FAIL_COND_V(t->type != TYPE_POSITION_3D, ERR_INVALID_PARAMETER);
+
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+
+ bool ok = false;
+
+ Vector3 tk = _interpolate(tt->positions, p_time, tt->interpolation, tt->loop_wrap, &ok);
+
+ if (!ok) {
+ return ERR_UNAVAILABLE;
}
+ *r_interpolation = tk;
+ return OK;
+}
+
+////
+
+int Animation::rotation_track_insert_key(int p_track, double p_time, const Quaternion &p_rotation) {
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), -1);
+ Track *t = tracks[p_track];
+ ERR_FAIL_COND_V(t->type != TYPE_ROTATION_3D, -1);
+
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+
+ TKey<Quaternion> tkey;
+ tkey.time = p_time;
+ tkey.value = p_rotation;
+
+ int ret = _insert(p_time, rt->rotations, tkey);
+ emit_changed();
+ return ret;
+}
+
+Error Animation::rotation_track_get_key(int p_track, int p_key, Quaternion *r_rotation) const {
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
+ Track *t = tracks[p_track];
+
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+ ERR_FAIL_COND_V(t->type != TYPE_ROTATION_3D, ERR_INVALID_PARAMETER);
+ ERR_FAIL_INDEX_V(p_key, rt->rotations.size(), ERR_INVALID_PARAMETER);
+
+ *r_rotation = rt->rotations[p_key].value;
return OK;
}
-int Animation::transform_track_insert_key(int p_track, double p_time, const Vector3 &p_loc, const Quaternion &p_rot, const Vector3 &p_scale) {
+Error Animation::rotation_track_interpolate(int p_track, double p_time, Quaternion *r_interpolation) const {
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
+ Track *t = tracks[p_track];
+ ERR_FAIL_COND_V(t->type != TYPE_ROTATION_3D, ERR_INVALID_PARAMETER);
+
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+
+ bool ok = false;
+
+ Quaternion tk = _interpolate(rt->rotations, p_time, rt->interpolation, rt->loop_wrap, &ok);
+
+ if (!ok) {
+ return ERR_UNAVAILABLE;
+ }
+ *r_interpolation = tk;
+ return OK;
+}
+
+////
+
+int Animation::scale_track_insert_key(int p_track, double p_time, const Vector3 &p_scale) {
ERR_FAIL_INDEX_V(p_track, tracks.size(), -1);
Track *t = tracks[p_track];
- ERR_FAIL_COND_V(t->type != TYPE_TRANSFORM3D, -1);
+ ERR_FAIL_COND_V(t->type != TYPE_SCALE_3D, -1);
- TransformTrack *tt = static_cast<TransformTrack *>(t);
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
- TKey<TransformKey> tkey;
+ TKey<Vector3> tkey;
tkey.time = p_time;
- tkey.value.loc = p_loc;
- tkey.value.rot = p_rot;
- tkey.value.scale = p_scale;
+ tkey.value = p_scale;
- int ret = _insert(p_time, tt->transforms, tkey);
+ int ret = _insert(p_time, st->scales, tkey);
emit_changed();
return ret;
}
+Error Animation::scale_track_get_key(int p_track, int p_key, Vector3 *r_scale) const {
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
+ Track *t = tracks[p_track];
+
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ ERR_FAIL_COND_V(t->type != TYPE_SCALE_3D, ERR_INVALID_PARAMETER);
+ ERR_FAIL_INDEX_V(p_key, st->scales.size(), ERR_INVALID_PARAMETER);
+
+ *r_scale = st->scales[p_key].value;
+
+ return OK;
+}
+
+Error Animation::scale_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation) const {
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
+ Track *t = tracks[p_track];
+ ERR_FAIL_COND_V(t->type != TYPE_SCALE_3D, ERR_INVALID_PARAMETER);
+
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
+
+ bool ok = false;
+
+ Vector3 tk = _interpolate(st->scales, p_time, st->interpolation, st->loop_wrap, &ok);
+
+ if (!ok) {
+ return ERR_UNAVAILABLE;
+ }
+ *r_interpolation = tk;
+ return OK;
+}
+
void Animation::track_remove_key_at_time(int p_track, double p_time) {
int idx = track_find_key(p_track, p_time, true);
ERR_FAIL_COND(idx < 0);
@@ -824,10 +1004,22 @@ void Animation::track_remove_key(int p_track, int p_idx) {
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM3D: {
- TransformTrack *tt = static_cast<TransformTrack *>(t);
- ERR_FAIL_INDEX(p_idx, tt->transforms.size());
- tt->transforms.remove(p_idx);
+ case TYPE_POSITION_3D: {
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+ ERR_FAIL_INDEX(p_idx, tt->positions.size());
+ tt->positions.remove(p_idx);
+
+ } break;
+ case TYPE_ROTATION_3D: {
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+ ERR_FAIL_INDEX(p_idx, rt->rotations.size());
+ rt->rotations.remove(p_idx);
+
+ } break;
+ case TYPE_SCALE_3D: {
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ ERR_FAIL_INDEX(p_idx, st->scales.size());
+ st->scales.remove(p_idx);
} break;
case TYPE_VALUE: {
@@ -870,13 +1062,37 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const {
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM3D: {
- TransformTrack *tt = static_cast<TransformTrack *>(t);
- int k = _find(tt->transforms, p_time);
- if (k < 0 || k >= tt->transforms.size()) {
+ case TYPE_POSITION_3D: {
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+ int k = _find(tt->positions, p_time);
+ if (k < 0 || k >= tt->positions.size()) {
return -1;
}
- if (tt->transforms[k].time != p_time && p_exact) {
+ if (tt->positions[k].time != p_time && p_exact) {
+ return -1;
+ }
+ return k;
+
+ } break;
+ case TYPE_ROTATION_3D: {
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+ int k = _find(rt->rotations, p_time);
+ if (k < 0 || k >= rt->rotations.size()) {
+ return -1;
+ }
+ if (rt->rotations[k].time != p_time && p_exact) {
+ return -1;
+ }
+ return k;
+
+ } break;
+ case TYPE_SCALE_3D: {
+ ScaleTrack *rt = static_cast<ScaleTrack *>(t);
+ int k = _find(rt->scales, p_time);
+ if (k < 0 || k >= rt->scales.size()) {
+ return -1;
+ }
+ if (rt->scales[k].time != p_time && p_exact) {
return -1;
}
return k;
@@ -952,24 +1168,21 @@ void Animation::track_insert_key(int p_track, double p_time, const Variant &p_ke
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM3D: {
- Dictionary d = p_key;
- Vector3 loc;
- if (d.has("location")) {
- loc = d["location"];
- }
-
- Quaternion rot;
- if (d.has("rotation")) {
- rot = d["rotation"];
- }
+ case TYPE_POSITION_3D: {
+ ERR_FAIL_COND((p_key.get_type() != Variant::VECTOR3) && (p_key.get_type() != Variant::VECTOR3I));
+ int idx = position_track_insert_key(p_track, p_time, p_key);
+ track_set_key_transition(p_track, idx, p_transition);
- Vector3 scale;
- if (d.has("scale")) {
- scale = d["scale"];
- }
+ } break;
+ case TYPE_ROTATION_3D: {
+ ERR_FAIL_COND((p_key.get_type() != Variant::QUATERNION) && (p_key.get_type() != Variant::BASIS));
+ int idx = rotation_track_insert_key(p_track, p_time, p_key);
+ track_set_key_transition(p_track, idx, p_transition);
- int idx = transform_track_insert_key(p_track, p_time, loc, rot, scale);
+ } break;
+ case TYPE_SCALE_3D: {
+ ERR_FAIL_COND((p_key.get_type() != Variant::VECTOR3) && (p_key.get_type() != Variant::VECTOR3I));
+ int idx = scale_track_insert_key(p_track, p_time, p_key);
track_set_key_transition(p_track, idx, p_transition);
} break;
@@ -1054,9 +1267,17 @@ int Animation::track_get_key_count(int p_track) const {
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM3D: {
- TransformTrack *tt = static_cast<TransformTrack *>(t);
- return tt->transforms.size();
+ case TYPE_POSITION_3D: {
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+ return tt->positions.size();
+ } break;
+ case TYPE_ROTATION_3D: {
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+ return rt->rotations.size();
+ } break;
+ case TYPE_SCALE_3D: {
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ return st->scales.size();
} break;
case TYPE_VALUE: {
ValueTrack *vt = static_cast<ValueTrack *>(t);
@@ -1089,16 +1310,23 @@ Variant Animation::track_get_key_value(int p_track, int p_key_idx) const {
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM3D: {
- TransformTrack *tt = static_cast<TransformTrack *>(t);
- ERR_FAIL_INDEX_V(p_key_idx, tt->transforms.size(), Variant());
+ case TYPE_POSITION_3D: {
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+ ERR_FAIL_INDEX_V(p_key_idx, tt->positions.size(), Variant());
- Dictionary d;
- d["location"] = tt->transforms[p_key_idx].value.loc;
- d["rotation"] = tt->transforms[p_key_idx].value.rot;
- d["scale"] = tt->transforms[p_key_idx].value.scale;
+ return tt->positions[p_key_idx].value;
+ } break;
+ case TYPE_ROTATION_3D: {
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+ ERR_FAIL_INDEX_V(p_key_idx, rt->rotations.size(), Variant());
- return d;
+ return rt->rotations[p_key_idx].value;
+ } break;
+ case TYPE_SCALE_3D: {
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ ERR_FAIL_INDEX_V(p_key_idx, st->scales.size(), Variant());
+
+ return st->scales[p_key_idx].value;
} break;
case TYPE_VALUE: {
ValueTrack *vt = static_cast<ValueTrack *>(t);
@@ -1157,10 +1385,20 @@ double Animation::track_get_key_time(int p_track, int p_key_idx) const {
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM3D: {
- TransformTrack *tt = static_cast<TransformTrack *>(t);
- ERR_FAIL_INDEX_V(p_key_idx, tt->transforms.size(), -1);
- return tt->transforms[p_key_idx].time;
+ case TYPE_POSITION_3D: {
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+ ERR_FAIL_INDEX_V(p_key_idx, tt->positions.size(), -1);
+ return tt->positions[p_key_idx].time;
+ } break;
+ case TYPE_ROTATION_3D: {
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+ ERR_FAIL_INDEX_V(p_key_idx, rt->rotations.size(), -1);
+ return rt->rotations[p_key_idx].time;
+ } break;
+ case TYPE_SCALE_3D: {
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ ERR_FAIL_INDEX_V(p_key_idx, st->scales.size(), -1);
+ return st->scales[p_key_idx].time;
} break;
case TYPE_VALUE: {
ValueTrack *vt = static_cast<ValueTrack *>(t);
@@ -1202,13 +1440,31 @@ void Animation::track_set_key_time(int p_track, int p_key_idx, double p_time) {
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM3D: {
- TransformTrack *tt = static_cast<TransformTrack *>(t);
- ERR_FAIL_INDEX(p_key_idx, tt->transforms.size());
- TKey<TransformKey> key = tt->transforms[p_key_idx];
+ case TYPE_POSITION_3D: {
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, tt->positions.size());
+ TKey<Vector3> key = tt->positions[p_key_idx];
key.time = p_time;
- tt->transforms.remove(p_key_idx);
- _insert(p_time, tt->transforms, key);
+ tt->positions.remove(p_key_idx);
+ _insert(p_time, tt->positions, key);
+ return;
+ }
+ case TYPE_ROTATION_3D: {
+ RotationTrack *tt = static_cast<RotationTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, tt->rotations.size());
+ TKey<Quaternion> key = tt->rotations[p_key_idx];
+ key.time = p_time;
+ tt->rotations.remove(p_key_idx);
+ _insert(p_time, tt->rotations, key);
+ return;
+ }
+ case TYPE_SCALE_3D: {
+ ScaleTrack *tt = static_cast<ScaleTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, tt->scales.size());
+ TKey<Vector3> key = tt->scales[p_key_idx];
+ key.time = p_time;
+ tt->scales.remove(p_key_idx);
+ _insert(p_time, tt->scales, key);
return;
}
case TYPE_VALUE: {
@@ -1266,10 +1522,20 @@ real_t Animation::track_get_key_transition(int p_track, int p_key_idx) const {
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM3D: {
- TransformTrack *tt = static_cast<TransformTrack *>(t);
- ERR_FAIL_INDEX_V(p_key_idx, tt->transforms.size(), -1);
- return tt->transforms[p_key_idx].transition;
+ case TYPE_POSITION_3D: {
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+ ERR_FAIL_INDEX_V(p_key_idx, tt->positions.size(), -1);
+ return tt->positions[p_key_idx].transition;
+ } break;
+ case TYPE_ROTATION_3D: {
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+ ERR_FAIL_INDEX_V(p_key_idx, rt->rotations.size(), -1);
+ return rt->rotations[p_key_idx].transition;
+ } break;
+ case TYPE_SCALE_3D: {
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ ERR_FAIL_INDEX_V(p_key_idx, st->scales.size(), -1);
+ return st->scales[p_key_idx].transition;
} break;
case TYPE_VALUE: {
ValueTrack *vt = static_cast<ValueTrack *>(t);
@@ -1302,21 +1568,28 @@ void Animation::track_set_key_value(int p_track, int p_key_idx, const Variant &p
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM3D: {
- TransformTrack *tt = static_cast<TransformTrack *>(t);
- ERR_FAIL_INDEX(p_key_idx, tt->transforms.size());
+ case TYPE_POSITION_3D: {
+ ERR_FAIL_COND((p_value.get_type() != Variant::VECTOR3) && (p_value.get_type() != Variant::VECTOR3I));
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, tt->positions.size());
- Dictionary d = p_value;
+ tt->positions.write[p_key_idx].value = p_value;
- if (d.has("location")) {
- tt->transforms.write[p_key_idx].value.loc = d["location"];
- }
- if (d.has("rotation")) {
- tt->transforms.write[p_key_idx].value.rot = d["rotation"];
- }
- if (d.has("scale")) {
- tt->transforms.write[p_key_idx].value.scale = d["scale"];
- }
+ } break;
+ case TYPE_ROTATION_3D: {
+ ERR_FAIL_COND((p_value.get_type() != Variant::QUATERNION) && (p_value.get_type() != Variant::BASIS));
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, rt->rotations.size());
+
+ rt->rotations.write[p_key_idx].value = p_value;
+
+ } break;
+ case TYPE_SCALE_3D: {
+ ERR_FAIL_COND((p_value.get_type() != Variant::VECTOR3) && (p_value.get_type() != Variant::VECTOR3I));
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, st->scales.size());
+
+ st->scales.write[p_key_idx].value = p_value;
} break;
case TYPE_VALUE: {
@@ -1385,10 +1658,20 @@ void Animation::track_set_key_transition(int p_track, int p_key_idx, real_t p_tr
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM3D: {
- TransformTrack *tt = static_cast<TransformTrack *>(t);
- ERR_FAIL_INDEX(p_key_idx, tt->transforms.size());
- tt->transforms.write[p_key_idx].transition = p_transition;
+ case TYPE_POSITION_3D: {
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, tt->positions.size());
+ tt->positions.write[p_key_idx].transition = p_transition;
+ } break;
+ case TYPE_ROTATION_3D: {
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, rt->rotations.size());
+ rt->rotations.write[p_key_idx].transition = p_transition;
+ } break;
+ case TYPE_SCALE_3D: {
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, st->scales.size());
+ st->scales.write[p_key_idx].transition = p_transition;
} break;
case TYPE_VALUE: {
ValueTrack *vt = static_cast<ValueTrack *>(t);
@@ -1450,15 +1733,6 @@ int Animation::_find(const Vector<K> &p_keys, double p_time) const {
return middle;
}
-Animation::TransformKey Animation::_interpolate(const Animation::TransformKey &p_a, const Animation::TransformKey &p_b, real_t p_c) const {
- TransformKey ret;
- ret.loc = _interpolate(p_a.loc, p_b.loc, p_c);
- ret.rot = _interpolate(p_a.rot, p_b.rot, p_c);
- ret.scale = _interpolate(p_a.scale, p_b.scale, p_c);
-
- return ret;
-}
-
Vector3 Animation::_interpolate(const Vector3 &p_a, const Vector3 &p_b, real_t p_c) const {
return p_a.lerp(p_b, p_c);
}
@@ -1477,16 +1751,6 @@ real_t Animation::_interpolate(const real_t &p_a, const real_t &p_b, real_t p_c)
return p_a * (1.0 - p_c) + p_b * p_c;
}
-Animation::TransformKey Animation::_cubic_interpolate(const Animation::TransformKey &p_pre_a, const Animation::TransformKey &p_a, const Animation::TransformKey &p_b, const Animation::TransformKey &p_post_b, real_t p_c) const {
- Animation::TransformKey tk;
-
- tk.loc = p_a.loc.cubic_interpolate(p_b.loc, p_pre_a.loc, p_post_b.loc, p_c);
- tk.scale = p_a.scale.cubic_interpolate(p_b.scale, p_pre_a.scale, p_post_b.scale, p_c);
- tk.rot = p_a.rot.cubic_slerp(p_b.rot, p_pre_a.rot, p_post_b.rot, p_c);
-
- return tk;
-}
-
Vector3 Animation::_cubic_interpolate(const Vector3 &p_pre_a, const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_post_b, real_t p_c) const {
return p_a.cubic_interpolate(p_b, p_pre_a, p_post_b, p_c);
}
@@ -1729,36 +1993,6 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, Interpol
// do a barrel roll
}
-Error Animation::transform_track_interpolate(int p_track, double p_time, Vector3 *r_loc, Quaternion *r_rot, Vector3 *r_scale) const {
- ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
- Track *t = tracks[p_track];
- ERR_FAIL_COND_V(t->type != TYPE_TRANSFORM3D, ERR_INVALID_PARAMETER);
-
- TransformTrack *tt = static_cast<TransformTrack *>(t);
-
- bool ok = false;
-
- TransformKey tk = _interpolate(tt->transforms, p_time, tt->interpolation, tt->loop_wrap, &ok);
-
- if (!ok) {
- return ERR_UNAVAILABLE;
- }
-
- if (r_loc) {
- *r_loc = tk.loc;
- }
-
- if (r_rot) {
- *r_rot = tk.rot;
- }
-
- if (r_scale) {
- *r_scale = tk.scale;
- }
-
- return OK;
-}
-
Variant Animation::value_track_interpolate(int p_track, double p_time) const {
ERR_FAIL_INDEX_V(p_track, tracks.size(), 0);
Track *t = tracks[p_track];
@@ -1933,10 +2167,22 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
// handle loop by splitting
switch (t->type) {
- case TYPE_TRANSFORM3D: {
- const TransformTrack *tt = static_cast<const TransformTrack *>(t);
- _track_get_key_indices_in_range(tt->transforms, from_time, length, p_indices);
- _track_get_key_indices_in_range(tt->transforms, 0, to_time, p_indices);
+ case TYPE_POSITION_3D: {
+ const PositionTrack *tt = static_cast<const PositionTrack *>(t);
+ _track_get_key_indices_in_range(tt->positions, from_time, length, p_indices);
+ _track_get_key_indices_in_range(tt->positions, 0, to_time, p_indices);
+
+ } break;
+ case TYPE_ROTATION_3D: {
+ const RotationTrack *rt = static_cast<const RotationTrack *>(t);
+ _track_get_key_indices_in_range(rt->rotations, from_time, length, p_indices);
+ _track_get_key_indices_in_range(rt->rotations, 0, to_time, p_indices);
+
+ } break;
+ case TYPE_SCALE_3D: {
+ const ScaleTrack *st = static_cast<const ScaleTrack *>(t);
+ _track_get_key_indices_in_range(st->scales, from_time, length, p_indices);
+ _track_get_key_indices_in_range(st->scales, 0, to_time, p_indices);
} break;
case TYPE_VALUE: {
@@ -1989,9 +2235,19 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
}
switch (t->type) {
- case TYPE_TRANSFORM3D: {
- const TransformTrack *tt = static_cast<const TransformTrack *>(t);
- _track_get_key_indices_in_range(tt->transforms, from_time, to_time, p_indices);
+ case TYPE_POSITION_3D: {
+ const PositionTrack *tt = static_cast<const PositionTrack *>(t);
+ _track_get_key_indices_in_range(tt->positions, from_time, to_time, p_indices);
+
+ } break;
+ case TYPE_ROTATION_3D: {
+ const RotationTrack *rt = static_cast<const RotationTrack *>(t);
+ _track_get_key_indices_in_range(rt->rotations, from_time, to_time, p_indices);
+
+ } break;
+ case TYPE_SCALE_3D: {
+ const ScaleTrack *st = static_cast<const ScaleTrack *>(t);
+ _track_get_key_indices_in_range(st->scales, from_time, to_time, p_indices);
} break;
case TYPE_VALUE: {
@@ -2608,7 +2864,10 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("track_set_enabled", "track_idx", "enabled"), &Animation::track_set_enabled);
ClassDB::bind_method(D_METHOD("track_is_enabled", "track_idx"), &Animation::track_is_enabled);
- ClassDB::bind_method(D_METHOD("transform_track_insert_key", "track_idx", "time", "location", "rotation", "scale"), &Animation::transform_track_insert_key);
+ ClassDB::bind_method(D_METHOD("position_track_insert_key", "track_idx", "time", "position"), &Animation::position_track_insert_key);
+ ClassDB::bind_method(D_METHOD("rotation_track_insert_key", "track_idx", "time", "rotation"), &Animation::rotation_track_insert_key);
+ ClassDB::bind_method(D_METHOD("scale_track_insert_key", "track_idx", "time", "scale"), &Animation::scale_track_insert_key);
+
ClassDB::bind_method(D_METHOD("track_insert_key", "track_idx", "time", "key", "transition"), &Animation::track_insert_key, DEFVAL(1));
ClassDB::bind_method(D_METHOD("track_remove_key", "track_idx", "key_idx"), &Animation::track_remove_key);
ClassDB::bind_method(D_METHOD("track_remove_key_at_time", "track_idx", "time"), &Animation::track_remove_key_at_time);
@@ -2628,7 +2887,6 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("track_set_interpolation_loop_wrap", "track_idx", "interpolation"), &Animation::track_set_interpolation_loop_wrap);
ClassDB::bind_method(D_METHOD("track_get_interpolation_loop_wrap", "track_idx"), &Animation::track_get_interpolation_loop_wrap);
- ClassDB::bind_method(D_METHOD("transform_track_interpolate", "track_idx", "time_sec"), &Animation::_transform_track_interpolate);
ClassDB::bind_method(D_METHOD("value_track_set_update_mode", "track_idx", "mode"), &Animation::value_track_set_update_mode);
ClassDB::bind_method(D_METHOD("value_track_get_update_mode", "track_idx"), &Animation::value_track_get_update_mode);
@@ -2682,7 +2940,9 @@ void Animation::_bind_methods() {
ADD_SIGNAL(MethodInfo("tracks_changed"));
BIND_ENUM_CONSTANT(TYPE_VALUE);
- BIND_ENUM_CONSTANT(TYPE_TRANSFORM3D);
+ BIND_ENUM_CONSTANT(TYPE_POSITION_3D);
+ BIND_ENUM_CONSTANT(TYPE_ROTATION_3D);
+ BIND_ENUM_CONSTANT(TYPE_SCALE_3D);
BIND_ENUM_CONSTANT(TYPE_METHOD);
BIND_ENUM_CONSTANT(TYPE_BEZIER);
BIND_ENUM_CONSTANT(TYPE_AUDIO);
@@ -2709,193 +2969,215 @@ void Animation::clear() {
emit_signal(SceneStringNames::get_singleton()->tracks_changed);
}
-bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, const TKey<TransformKey> &t1, const TKey<TransformKey> &t2, real_t p_alowed_linear_err, real_t p_alowed_angular_err, real_t p_max_optimizable_angle, const Vector3 &p_norm) {
- real_t c = (t1.time - t0.time) / (t2.time - t0.time);
- real_t t[3] = { -1, -1, -1 };
+bool Animation::_position_track_optimize_key(const TKey<Vector3> &t0, const TKey<Vector3> &t1, const TKey<Vector3> &t2, real_t p_allowed_linear_err, real_t p_allowed_angular_error, const Vector3 &p_norm) {
+ const Vector3 &v0 = t0.value;
+ const Vector3 &v1 = t1.value;
+ const Vector3 &v2 = t2.value;
- { //translation
+ if (v0.is_equal_approx(v2)) {
+ //0 and 2 are close, let's see if 1 is close
+ if (!v0.is_equal_approx(v1)) {
+ //not close, not optimizable
+ return false;
+ }
- const Vector3 &v0 = t0.value.loc;
- const Vector3 &v1 = t1.value.loc;
- const Vector3 &v2 = t2.value.loc;
+ } else {
+ Vector3 pd = (v2 - v0);
+ real_t d0 = pd.dot(v0);
+ real_t d1 = pd.dot(v1);
+ real_t d2 = pd.dot(v2);
+ if (d1 < d0 || d1 > d2) {
+ return false;
+ }
- if (v0.is_equal_approx(v2)) {
- //0 and 2 are close, let's see if 1 is close
- if (!v0.is_equal_approx(v1)) {
- //not close, not optimizable
- return false;
- }
+ Vector3 s[2] = { v0, v2 };
+ real_t d = Geometry3D::get_closest_point_to_segment(v1, s).distance_to(v1);
- } else {
- Vector3 pd = (v2 - v0);
- real_t d0 = pd.dot(v0);
- real_t d1 = pd.dot(v1);
- real_t d2 = pd.dot(v2);
- if (d1 < d0 || d1 > d2) {
- return false;
- }
+ if (d > pd.length() * p_allowed_linear_err) {
+ return false; //beyond allowed error for collinearity
+ }
- Vector3 s[2] = { v0, v2 };
- real_t d = Geometry3D::get_closest_point_to_segment(v1, s).distance_to(v1);
+ if (p_norm != Vector3() && Math::acos(pd.normalized().dot(p_norm)) > p_allowed_angular_error) {
+ return false;
+ }
+ }
- if (d > pd.length() * p_alowed_linear_err) {
- return false; //beyond allowed error for collinearity
- }
+ return true;
+}
- if (p_norm != Vector3() && Math::acos(pd.normalized().dot(p_norm)) > p_alowed_angular_err) {
- return false;
- }
+bool Animation::_rotation_track_optimize_key(const TKey<Quaternion> &t0, const TKey<Quaternion> &t1, const TKey<Quaternion> &t2, real_t p_allowed_angular_error, float p_max_optimizable_angle) {
+ const Quaternion &q0 = t0.value;
+ const Quaternion &q1 = t1.value;
+ const Quaternion &q2 = t2.value;
+
+ //localize both to rotation from q0
- t[0] = (d1 - d0) / (d2 - d0);
+ if (q0.is_equal_approx(q2)) {
+ if (!q0.is_equal_approx(q1)) {
+ return false;
}
- }
- { //rotation
+ } else {
+ Quaternion r02 = (q0.inverse() * q2).normalized();
+ Quaternion r01 = (q0.inverse() * q1).normalized();
- const Quaternion &q0 = t0.value.rot;
- const Quaternion &q1 = t1.value.rot;
- const Quaternion &q2 = t2.value.rot;
+ Vector3 v02, v01;
+ real_t a02, a01;
- //localize both to rotation from q0
+ r02.get_axis_angle(v02, a02);
+ r01.get_axis_angle(v01, a01);
- if (q0.is_equal_approx(q2)) {
- if (!q0.is_equal_approx(q1)) {
- return false;
- }
+ if (Math::abs(a02) > p_max_optimizable_angle) {
+ return false;
+ }
- } else {
- Quaternion r02 = (q0.inverse() * q2).normalized();
- Quaternion r01 = (q0.inverse() * q1).normalized();
+ if (v01.dot(v02) < 0) {
+ //make sure both rotations go the same way to compare
+ v02 = -v02;
+ a02 = -a02;
+ }
- Vector3 v02, v01;
- real_t a02, a01;
+ real_t err_01 = Math::acos(v01.normalized().dot(v02.normalized())) / Math_PI;
+ if (err_01 > p_allowed_angular_error) {
+ //not rotating in the same axis
+ return false;
+ }
- r02.get_axis_angle(v02, a02);
- r01.get_axis_angle(v01, a01);
+ if (a01 * a02 < 0) {
+ //not rotating in the same direction
+ return false;
+ }
- if (Math::abs(a02) > p_max_optimizable_angle) {
- return false;
- }
+ real_t tr = a01 / a02;
+ if (tr < 0 || tr > 1) {
+ return false; //rotating too much or too less
+ }
+ }
- if (v01.dot(v02) < 0) {
- //make sure both rotations go the same way to compare
- v02 = -v02;
- a02 = -a02;
- }
+ return true;
+}
- real_t err_01 = Math::acos(v01.normalized().dot(v02.normalized())) / Math_PI;
- if (err_01 > p_alowed_angular_err) {
- //not rotating in the same axis
- return false;
- }
+bool Animation::_scale_track_optimize_key(const TKey<Vector3> &t0, const TKey<Vector3> &t1, const TKey<Vector3> &t2, real_t p_allowed_linear_error) {
+ const Vector3 &v0 = t0.value;
+ const Vector3 &v1 = t1.value;
+ const Vector3 &v2 = t2.value;
- if (a01 * a02 < 0) {
- //not rotating in the same direction
- return false;
- }
+ if (v0.is_equal_approx(v2)) {
+ //0 and 2 are close, let's see if 1 is close
+ if (!v0.is_equal_approx(v1)) {
+ //not close, not optimizable
+ return false;
+ }
- real_t tr = a01 / a02;
- if (tr < 0 || tr > 1) {
- return false; //rotating too much or too less
- }
+ } else {
+ Vector3 pd = (v2 - v0);
+ real_t d0 = pd.dot(v0);
+ real_t d1 = pd.dot(v1);
+ real_t d2 = pd.dot(v2);
+ if (d1 < d0 || d1 > d2) {
+ return false; //beyond segment range
+ }
- t[1] = tr;
+ Vector3 s[2] = { v0, v2 };
+ real_t d = Geometry3D::get_closest_point_to_segment(v1, s).distance_to(v1);
+
+ if (d > pd.length() * p_allowed_linear_error) {
+ return false; //beyond allowed error for colinearity
}
}
- { //scale
+ return true;
+}
- const Vector3 &v0 = t0.value.scale;
- const Vector3 &v1 = t1.value.scale;
- const Vector3 &v2 = t2.value.scale;
+void Animation::_position_track_optimize(int p_idx, real_t p_allowed_linear_err, real_t p_allowed_angular_err) {
+ ERR_FAIL_INDEX(p_idx, tracks.size());
+ ERR_FAIL_COND(tracks[p_idx]->type != TYPE_POSITION_3D);
+ PositionTrack *tt = static_cast<PositionTrack *>(tracks[p_idx]);
+ bool prev_erased = false;
+ TKey<Vector3> first_erased;
- if (v0.is_equal_approx(v2)) {
- //0 and 2 are close, let's see if 1 is close
- if (!v0.is_equal_approx(v1)) {
- //not close, not optimizable
- return false;
- }
+ Vector3 norm;
- } else {
- Vector3 pd = (v2 - v0);
- real_t d0 = pd.dot(v0);
- real_t d1 = pd.dot(v1);
- real_t d2 = pd.dot(v2);
- if (d1 < d0 || d1 > d2) {
- return false; //beyond segment range
- }
+ for (int i = 1; i < tt->positions.size() - 1; i++) {
+ TKey<Vector3> &t0 = tt->positions.write[i - 1];
+ TKey<Vector3> &t1 = tt->positions.write[i];
+ TKey<Vector3> &t2 = tt->positions.write[i + 1];
- Vector3 s[2] = { v0, v2 };
- real_t d = Geometry3D::get_closest_point_to_segment(v1, s).distance_to(v1);
+ bool erase = _position_track_optimize_key(t0, t1, t2, p_allowed_linear_err, p_allowed_angular_err, norm);
+ if (erase && !prev_erased) {
+ norm = (t2.value - t1.value).normalized();
+ }
+
+ if (prev_erased && !_position_track_optimize_key(t0, first_erased, t2, p_allowed_linear_err, p_allowed_angular_err, norm)) {
+ //avoid error to go beyond first erased key
+ erase = false;
+ }
- if (d > pd.length() * p_alowed_linear_err) {
- return false; //beyond allowed error for collinearity
+ if (erase) {
+ if (!prev_erased) {
+ first_erased = t1;
+ prev_erased = true;
}
- t[2] = (d1 - d0) / (d2 - d0);
+ tt->positions.remove(i);
+ i--;
+
+ } else {
+ prev_erased = false;
+ norm = Vector3();
}
}
+}
- bool erase = false;
- if (t[0] == -1 && t[1] == -1 && t[2] == -1) {
- erase = true;
- } else {
- erase = true;
- real_t lt = -1.0;
- for (int j = 0; j < 3; j++) {
- //search for t on first, one must be it
- if (t[j] != -1) {
- lt = t[j]; //official t
- //validate rest
- for (int k = j + 1; k < 3; k++) {
- if (t[k] == -1) {
- continue;
- }
+void Animation::_rotation_track_optimize(int p_idx, real_t p_allowed_angular_err, real_t p_max_optimizable_angle) {
+ ERR_FAIL_INDEX(p_idx, tracks.size());
+ ERR_FAIL_COND(tracks[p_idx]->type != TYPE_ROTATION_3D);
+ RotationTrack *tt = static_cast<RotationTrack *>(tracks[p_idx]);
+ bool prev_erased = false;
+ TKey<Quaternion> first_erased;
- if (Math::abs(lt - t[k]) > p_alowed_linear_err) {
- erase = false;
- break;
- }
- }
- break;
- }
- }
+ for (int i = 1; i < tt->rotations.size() - 1; i++) {
+ TKey<Quaternion> &t0 = tt->rotations.write[i - 1];
+ TKey<Quaternion> &t1 = tt->rotations.write[i];
+ TKey<Quaternion> &t2 = tt->rotations.write[i + 1];
- ERR_FAIL_COND_V(lt == -1, false);
+ bool erase = _rotation_track_optimize_key(t0, t1, t2, p_allowed_angular_err, p_max_optimizable_angle);
+
+ if (prev_erased && !_rotation_track_optimize_key(t0, first_erased, t2, p_allowed_angular_err, p_max_optimizable_angle)) {
+ //avoid error to go beyond first erased key
+ erase = false;
+ }
if (erase) {
- if (Math::abs(lt - c) > p_alowed_linear_err) {
- //todo, evaluate changing the transition if this fails?
- //this could be done as a second pass and would be
- //able to optimize more
- erase = false;
+ if (!prev_erased) {
+ first_erased = t1;
+ prev_erased = true;
}
+
+ tt->rotations.remove(i);
+ i--;
+
+ } else {
+ prev_erased = false;
}
}
-
- return erase;
}
-void Animation::_transform_track_optimize(int p_idx, real_t p_allowed_linear_err, real_t p_allowed_angular_err, real_t p_max_optimizable_angle) {
+void Animation::_scale_track_optimize(int p_idx, real_t p_allowed_linear_err) {
ERR_FAIL_INDEX(p_idx, tracks.size());
- ERR_FAIL_COND(tracks[p_idx]->type != TYPE_TRANSFORM3D);
- TransformTrack *tt = static_cast<TransformTrack *>(tracks[p_idx]);
+ ERR_FAIL_COND(tracks[p_idx]->type != TYPE_SCALE_3D);
+ ScaleTrack *tt = static_cast<ScaleTrack *>(tracks[p_idx]);
bool prev_erased = false;
- TKey<TransformKey> first_erased;
+ TKey<Vector3> first_erased;
- Vector3 norm;
+ for (int i = 1; i < tt->scales.size() - 1; i++) {
+ TKey<Vector3> &t0 = tt->scales.write[i - 1];
+ TKey<Vector3> &t1 = tt->scales.write[i];
+ TKey<Vector3> &t2 = tt->scales.write[i + 1];
- for (int i = 1; i < tt->transforms.size() - 1; i++) {
- TKey<TransformKey> &t0 = tt->transforms.write[i - 1];
- TKey<TransformKey> &t1 = tt->transforms.write[i];
- TKey<TransformKey> &t2 = tt->transforms.write[i + 1];
+ bool erase = _scale_track_optimize_key(t0, t1, t2, p_allowed_linear_err);
- bool erase = _transform_track_optimize_key(t0, t1, t2, p_allowed_linear_err, p_allowed_angular_err, p_max_optimizable_angle, norm);
- if (erase && !prev_erased) {
- norm = (t2.value.loc - t1.value.loc).normalized();
- }
-
- if (prev_erased && !_transform_track_optimize_key(t0, first_erased, t2, p_allowed_linear_err, p_allowed_angular_err, p_max_optimizable_angle, norm)) {
+ if (prev_erased && !_scale_track_optimize_key(t0, first_erased, t2, p_allowed_linear_err)) {
//avoid error to go beyond first erased key
erase = false;
}
@@ -2906,20 +3188,23 @@ void Animation::_transform_track_optimize(int p_idx, real_t p_allowed_linear_err
prev_erased = true;
}
- tt->transforms.remove(i);
+ tt->scales.remove(i);
i--;
} else {
prev_erased = false;
- norm = Vector3();
}
}
}
void Animation::optimize(real_t p_allowed_linear_err, real_t p_allowed_angular_err, real_t p_max_optimizable_angle) {
for (int i = 0; i < tracks.size(); i++) {
- if (tracks[i]->type == TYPE_TRANSFORM3D) {
- _transform_track_optimize(i, p_allowed_linear_err, p_allowed_angular_err, p_max_optimizable_angle);
+ if (tracks[i]->type == TYPE_POSITION_3D) {
+ _position_track_optimize(i, p_allowed_linear_err, p_allowed_angular_err);
+ } else if (tracks[i]->type == TYPE_ROTATION_3D) {
+ _rotation_track_optimize(i, p_allowed_angular_err, p_max_optimizable_angle);
+ } else if (tracks[i]->type == TYPE_SCALE_3D) {
+ _scale_track_optimize(i, p_allowed_linear_err);
}
}
}
diff --git a/scene/resources/animation.h b/scene/resources/animation.h
index 9a410bd566..614e12a560 100644
--- a/scene/resources/animation.h
+++ b/scene/resources/animation.h
@@ -42,7 +42,9 @@ class Animation : public Resource {
public:
enum TrackType {
TYPE_VALUE, ///< Set a value in a property, can be interpolated.
- TYPE_TRANSFORM3D, ///< Transform a node or a bone.
+ TYPE_POSITION_3D, ///< Position 3D track
+ TYPE_ROTATION_3D, ///< Rotation 3D track
+ TYPE_SCALE_3D, ///< Scale 3D track
TYPE_METHOD, ///< Call any method on a specific node.
TYPE_BEZIER, ///< Bezier curve
TYPE_AUDIO,
@@ -86,21 +88,31 @@ private:
T value;
};
- struct TransformKey {
- Vector3 loc;
- Quaternion rot;
- Vector3 scale;
+ const int32_t POSITION_TRACK_SIZE = 5;
+ const int32_t ROTATION_TRACK_SIZE = 6;
+ const int32_t SCALE_TRACK_SIZE = 5;
+
+ /* POSITION TRACK */
+
+ struct PositionTrack : public Track {
+ Vector<TKey<Vector3>> positions;
+
+ PositionTrack() { type = TYPE_POSITION_3D; }
};
- // Not necessarily the same size as Transform3D. The amount of numbers in Animation::Key and TransformKey.
- const int32_t TRANSFORM_TRACK_SIZE = 12;
+ /* ROTATION TRACK */
- /* TRANSFORM TRACK */
+ struct RotationTrack : public Track {
+ Vector<TKey<Quaternion>> rotations;
- struct TransformTrack : public Track {
- Vector<TKey<TransformKey>> transforms;
+ RotationTrack() { type = TYPE_ROTATION_3D; }
+ };
- TransformTrack() { type = TYPE_TRANSFORM3D; }
+ /* SCALE TRACK */
+
+ struct ScaleTrack : public Track {
+ Vector<TKey<Vector3>> scales;
+ ScaleTrack() { type = TYPE_SCALE_3D; }
};
/* PROPERTY VALUE TRACK */
@@ -186,14 +198,11 @@ private:
template <class K>
inline int _find(const Vector<K> &p_keys, double p_time) const;
- _FORCE_INLINE_ Animation::TransformKey _interpolate(const Animation::TransformKey &p_a, const Animation::TransformKey &p_b, real_t p_c) const;
-
_FORCE_INLINE_ Vector3 _interpolate(const Vector3 &p_a, const Vector3 &p_b, real_t p_c) const;
_FORCE_INLINE_ Quaternion _interpolate(const Quaternion &p_a, const Quaternion &p_b, real_t p_c) const;
_FORCE_INLINE_ Variant _interpolate(const Variant &p_a, const Variant &p_b, real_t p_c) const;
_FORCE_INLINE_ real_t _interpolate(const real_t &p_a, const real_t &p_b, real_t p_c) const;
- _FORCE_INLINE_ Animation::TransformKey _cubic_interpolate(const Animation::TransformKey &p_pre_a, const Animation::TransformKey &p_a, const Animation::TransformKey &p_b, const Animation::TransformKey &p_post_b, real_t p_c) const;
_FORCE_INLINE_ Vector3 _cubic_interpolate(const Vector3 &p_pre_a, const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_post_b, real_t p_c) const;
_FORCE_INLINE_ Quaternion _cubic_interpolate(const Quaternion &p_pre_a, const Quaternion &p_a, const Quaternion &p_b, const Quaternion &p_post_b, real_t p_c) const;
_FORCE_INLINE_ Variant _cubic_interpolate(const Variant &p_pre_a, const Variant &p_a, const Variant &p_b, const Variant &p_post_b, real_t p_c) const;
@@ -214,18 +223,6 @@ private:
// bind helpers
private:
- Array _transform_track_interpolate(int p_track, double p_time) const {
- Vector3 loc;
- Quaternion rot;
- Vector3 scale;
- transform_track_interpolate(p_track, p_time, &loc, &rot, &scale);
- Array ret;
- ret.push_back(loc);
- ret.push_back(rot);
- ret.push_back(scale);
- return ret;
- }
-
Vector<int> _value_track_get_key_indices(int p_track, double p_time, double p_delta) const {
List<int> idxs;
value_track_get_key_indices(p_track, p_time, p_delta, &idxs);
@@ -247,8 +244,13 @@ private:
return idxr;
}
- bool _transform_track_optimize_key(const TKey<TransformKey> &t0, const TKey<TransformKey> &t1, const TKey<TransformKey> &t2, real_t p_alowed_linear_err, real_t p_alowed_angular_err, real_t p_max_optimizable_angle, const Vector3 &p_norm);
- void _transform_track_optimize(int p_idx, real_t p_allowed_linear_err = 0.05, real_t p_allowed_angular_err = 0.01, real_t p_max_optimizable_angle = Math_PI * 0.125);
+ bool _position_track_optimize_key(const TKey<Vector3> &t0, const TKey<Vector3> &t1, const TKey<Vector3> &t2, real_t p_alowed_linear_err, real_t p_allowed_angular_error, const Vector3 &p_norm);
+ bool _rotation_track_optimize_key(const TKey<Quaternion> &t0, const TKey<Quaternion> &t1, const TKey<Quaternion> &t2, real_t p_allowed_angular_error, float p_max_optimizable_angle);
+ bool _scale_track_optimize_key(const TKey<Vector3> &t0, const TKey<Vector3> &t1, const TKey<Vector3> &t2, real_t p_allowed_linear_error);
+
+ void _position_track_optimize(int p_idx, real_t p_allowed_linear_err, real_t p_allowed_angular_err);
+ void _rotation_track_optimize(int p_idx, real_t p_allowed_angular_err, real_t p_max_optimizable_angle);
+ void _scale_track_optimize(int p_idx, real_t p_allowed_linear_err);
protected:
bool _set(const StringName &p_name, const Variant &p_value);
@@ -294,8 +296,18 @@ public:
double track_get_key_time(int p_track, int p_key_idx) const;
real_t track_get_key_transition(int p_track, int p_key_idx) const;
- int transform_track_insert_key(int p_track, double p_time, const Vector3 &p_loc, const Quaternion &p_rot = Quaternion(), const Vector3 &p_scale = Vector3());
- Error transform_track_get_key(int p_track, int p_key, Vector3 *r_loc, Quaternion *r_rot, Vector3 *r_scale) const;
+ int position_track_insert_key(int p_track, double p_time, const Vector3 &p_position);
+ Error position_track_get_key(int p_track, int p_key, Vector3 *r_position) const;
+ Error position_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation) const;
+
+ int rotation_track_insert_key(int p_track, double p_time, const Quaternion &p_rotation);
+ Error rotation_track_get_key(int p_track, int p_key, Quaternion *r_rotation) const;
+ Error rotation_track_interpolate(int p_track, double p_time, Quaternion *r_interpolation) const;
+
+ int scale_track_insert_key(int p_track, double p_time, const Vector3 &p_scale);
+ Error scale_track_get_key(int p_track, int p_key, Vector3 *r_scale) const;
+ Error scale_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation) const;
+
void track_set_interpolation_type(int p_track, InterpolationType p_interp);
InterpolationType track_get_interpolation_type(int p_track) const;
@@ -324,8 +336,6 @@ public:
void track_set_interpolation_loop_wrap(int p_track, bool p_enable);
bool track_get_interpolation_loop_wrap(int p_track) const;
- Error transform_track_interpolate(int p_track, double p_time, Vector3 *r_loc, Quaternion *r_rot, Vector3 *r_scale) const;
-
Variant value_track_interpolate(int p_track, double p_time) const;
void value_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices) const;
void value_track_set_update_mode(int p_track, UpdateMode p_mode);
diff --git a/scene/resources/skeleton_modification_3d_fabrik.cpp b/scene/resources/skeleton_modification_3d_fabrik.cpp
index e615615924..20ebbda256 100644
--- a/scene/resources/skeleton_modification_3d_fabrik.cpp
+++ b/scene/resources/skeleton_modification_3d_fabrik.cpp
@@ -168,7 +168,7 @@ void SkeletonModification3DFABRIK::_execute(real_t p_delta) {
// 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));
+ Transform3D conversion_transform = (stack->skeleton->get_bone_global_pose(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;
diff --git a/scene/resources/skeleton_modification_3d_twoboneik.cpp b/scene/resources/skeleton_modification_3d_twoboneik.cpp
index ae7a3bab7e..c1a71148a7 100644
--- a/scene/resources/skeleton_modification_3d_twoboneik.cpp
+++ b/scene/resources/skeleton_modification_3d_twoboneik.cpp
@@ -455,7 +455,7 @@ void SkeletonModification3DTwoBoneIK::calculate_joint_lengths() {
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);
+ stack->skeleton->get_bone_global_pose(bone_two_children[i]).origin);
}
joint_two_length = joint_two_length / bone_two_children.size();
} else {
diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp
index f5e52b70e6..275b430f2a 100644
--- a/scene/resources/tile_set.cpp
+++ b/scene/resources/tile_set.cpp
@@ -3066,6 +3066,7 @@ void TileSetAtlasSource::reset_state() {
void TileSetAtlasSource::set_texture(Ref<Texture2D> p_texture) {
texture = p_texture;
+ _clear_tiles_outside_texture();
emit_changed();
}
@@ -3081,6 +3082,7 @@ void TileSetAtlasSource::set_margins(Vector2i p_margins) {
margins = p_margins;
}
+ _clear_tiles_outside_texture();
emit_changed();
}
Vector2i TileSetAtlasSource::get_margins() const {
@@ -3095,6 +3097,7 @@ void TileSetAtlasSource::set_separation(Vector2i p_separation) {
separation = p_separation;
}
+ _clear_tiles_outside_texture();
emit_changed();
}
Vector2i TileSetAtlasSource::get_separation() const {
@@ -3109,6 +3112,7 @@ void TileSetAtlasSource::set_texture_region_size(Vector2i p_tile_size) {
texture_region_size = p_tile_size;
}
+ _clear_tiles_outside_texture();
emit_changed();
}
Vector2i TileSetAtlasSource::get_texture_region_size() const {
@@ -3354,7 +3358,7 @@ void TileSetAtlasSource::create_tile(const Vector2i p_atlas_coords, const Vector
ERR_FAIL_COND(p_size.x <= 0 || p_size.y <= 0);
bool room_for_tile = has_room_for_tile(p_atlas_coords, p_size, 1, Vector2i(), 1);
- ERR_FAIL_COND_MSG(!room_for_tile, "Cannot create tile, tiles are already present in the space the tile would cover.");
+ ERR_FAIL_COND_MSG(!room_for_tile, "Cannot create tile. The tile is outside the texture or tiles are already present in the space the tile would cover.");
// Initialize the tile data.
TileAlternativesData tad;
@@ -3552,9 +3556,7 @@ bool TileSetAtlasSource::has_room_for_tile(Vector2i p_atlas_coords, Vector2i p_s
return false;
}
if (coords.x >= atlas_grid_size.x || coords.y >= atlas_grid_size.y) {
- if (!(_coords_mapping_cache.has(coords) && _coords_mapping_cache[coords] == p_ignored_tile)) {
- return false; // Only accept tiles outside the atlas if they are part of the ignored tile.
- }
+ return false;
}
}
}
@@ -3562,6 +3564,33 @@ bool TileSetAtlasSource::has_room_for_tile(Vector2i p_atlas_coords, Vector2i p_s
return true;
}
+PackedVector2Array TileSetAtlasSource::get_tiles_to_be_removed_on_change(Ref<Texture2D> p_texture, Vector2i p_margins, Vector2i p_separation, Vector2i p_texture_region_size) {
+ // Compute the new atlas grid size.
+ Size2 new_grid_size;
+ if (p_texture.is_valid()) {
+ Size2i valid_area = p_texture->get_size() - p_margins;
+
+ // Compute the number of valid tiles in the tiles atlas
+ if (valid_area.x >= p_texture_region_size.x && valid_area.y >= p_texture_region_size.y) {
+ valid_area -= p_texture_region_size;
+ new_grid_size = Size2i(1, 1) + valid_area / (p_texture_region_size + p_separation);
+ }
+ }
+
+ Vector<Vector2> output;
+ for (KeyValue<Vector2i, TileAlternativesData> &E : tiles) {
+ for (unsigned int frame = 0; frame < E.value.animation_frames_durations.size(); frame++) {
+ Vector2i frame_coords = E.key + (E.value.size_in_atlas + E.value.animation_separation) * ((E.value.animation_columns > 0) ? Vector2i(frame % E.value.animation_columns, frame / E.value.animation_columns) : Vector2i(frame, 0));
+ frame_coords += E.value.size_in_atlas;
+ if (frame_coords.x > new_grid_size.x || frame_coords.y > new_grid_size.y) {
+ output.push_back(E.key);
+ break;
+ }
+ }
+ }
+ return output;
+}
+
Rect2i TileSetAtlasSource::get_tile_texture_region(Vector2i p_atlas_coords, int p_frame) const {
ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), Rect2i(), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
ERR_FAIL_INDEX_V(p_frame, (int)tiles[p_atlas_coords].animation_frames_durations.size(), Rect2i());
@@ -3626,34 +3655,6 @@ void TileSetAtlasSource::move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_
emit_signal(SNAME("changed"));
}
-bool TileSetAtlasSource::has_tiles_outside_texture() {
- Vector2i grid_size = get_atlas_grid_size();
- Vector<Vector2i> to_remove;
-
- for (const KeyValue<Vector2i, TileSetAtlasSource::TileAlternativesData> &E : tiles) {
- if (E.key.x >= grid_size.x || E.key.y >= grid_size.y) {
- return true;
- }
- }
-
- return false;
-}
-
-void TileSetAtlasSource::clear_tiles_outside_texture() {
- Vector2i grid_size = get_atlas_grid_size();
- Vector<Vector2i> to_remove;
-
- for (const KeyValue<Vector2i, TileSetAtlasSource::TileAlternativesData> &E : tiles) {
- if (E.key.x >= grid_size.x || E.key.y >= grid_size.y) {
- to_remove.append(E.key);
- }
- }
-
- for (int i = 0; i < to_remove.size(); i++) {
- remove_tile(to_remove[i]);
- }
-}
-
int TileSetAtlasSource::create_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_id_override) {
ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), TileSetSource::INVALID_TILE_ALTERNATIVE, vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
ERR_FAIL_COND_V_MSG(p_alternative_id_override >= 0 && tiles[p_atlas_coords].alternatives.has(p_alternative_id_override), TileSetSource::INVALID_TILE_ALTERNATIVE, vformat("Cannot create alternative tile. Another alternative exists with id %d.", p_alternative_id_override));
@@ -3754,7 +3755,7 @@ void TileSetAtlasSource::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_tile_size_in_atlas", "atlas_coords"), &TileSetAtlasSource::get_tile_size_in_atlas);
ClassDB::bind_method(D_METHOD("has_room_for_tile", "atlas_coords", "size", "animation_columns", "animation_separation", "frames_count", "ignored_tile"), &TileSetAtlasSource::has_room_for_tile, DEFVAL(INVALID_ATLAS_COORDS));
-
+ ClassDB::bind_method(D_METHOD("get_tiles_to_be_removed_on_change", "texture", "margins", "separation", "texture_region_size"), &TileSetAtlasSource::get_tiles_to_be_removed_on_change);
ClassDB::bind_method(D_METHOD("get_tile_at_coords", "atlas_coords"), &TileSetAtlasSource::get_tile_at_coords);
ClassDB::bind_method(D_METHOD("set_tile_animation_columns", "atlas_coords", "frame_columns"), &TileSetAtlasSource::set_tile_animation_columns);
@@ -3779,8 +3780,6 @@ void TileSetAtlasSource::_bind_methods() {
// Helpers.
ClassDB::bind_method(D_METHOD("get_atlas_grid_size"), &TileSetAtlasSource::get_atlas_grid_size);
- ClassDB::bind_method(D_METHOD("has_tiles_outside_texture"), &TileSetAtlasSource::has_tiles_outside_texture);
- ClassDB::bind_method(D_METHOD("clear_tiles_outside_texture"), &TileSetAtlasSource::clear_tiles_outside_texture);
ClassDB::bind_method(D_METHOD("get_tile_texture_region", "atlas_coords", "frame"), &TileSetAtlasSource::get_tile_texture_region, DEFVAL(0));
}
@@ -3854,6 +3853,20 @@ void TileSetAtlasSource::_create_coords_mapping_cache(Vector2i p_atlas_coords) {
}
}
+void TileSetAtlasSource::_clear_tiles_outside_texture() {
+ LocalVector<Vector2i> to_remove;
+
+ for (const KeyValue<Vector2i, TileSetAtlasSource::TileAlternativesData> &E : tiles) {
+ if (!has_room_for_tile(E.key, E.value.size_in_atlas, E.value.animation_columns, E.value.animation_separation, E.value.animation_frames_durations.size(), E.key)) {
+ to_remove.push_back(E.key);
+ }
+ }
+
+ for (unsigned int i = 0; i < to_remove.size(); i++) {
+ remove_tile(to_remove[i]);
+ }
+}
+
/////////////////////////////// TileSetScenesCollectionSource //////////////////////////////////////
void TileSetScenesCollectionSource::_compute_next_alternative_id() {
diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h
index 716b66405f..351bdff89d 100644
--- a/scene/resources/tile_set.h
+++ b/scene/resources/tile_set.h
@@ -479,8 +479,10 @@ private:
void _compute_next_alternative_id(const Vector2i p_atlas_coords);
- void _create_coords_mapping_cache(Vector2i p_atlas_coords);
void _clear_coords_mapping_cache(Vector2i p_atlas_coords);
+ void _create_coords_mapping_cache(Vector2i p_atlas_coords);
+
+ void _clear_tiles_outside_texture();
protected:
bool _set(const StringName &p_name, const Variant &p_value);
@@ -534,7 +536,7 @@ public:
virtual Vector2i get_tile_id(int p_index) const override;
bool has_room_for_tile(Vector2i p_atlas_coords, Vector2i p_size, int p_animation_columns, Vector2i p_animation_separation, int p_frames_count, Vector2i p_ignored_tile = INVALID_ATLAS_COORDS) const;
-
+ PackedVector2Array get_tiles_to_be_removed_on_change(Ref<Texture2D> p_texture, Vector2i p_margins, Vector2i p_separation, Vector2i p_texture_region_size);
Vector2i get_tile_at_coords(Vector2i p_atlas_coords) const;
// Animation.
@@ -565,8 +567,6 @@ public:
// Helpers.
Vector2i get_atlas_grid_size() const;
- bool has_tiles_outside_texture();
- void clear_tiles_outside_texture();
Rect2i get_tile_texture_region(Vector2i p_atlas_coords, int p_frame = 0) const;
Vector2i get_tile_effective_texture_offset(Vector2i p_atlas_coords, int p_alternative_tile) const;
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index 934d16bd7e..0dc83b4bb8 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -113,6 +113,10 @@ bool VisualShaderNode::is_output_port_expandable(int p_port) const {
return false;
}
+bool VisualShaderNode::has_output_port_preview(int p_port) const {
+ return true;
+}
+
void VisualShaderNode::_set_output_ports_expanded(const Array &p_values) {
for (int i = 0; i < p_values.size(); i++) {
expanded_output_ports[p_values[i]] = true;
@@ -2307,7 +2311,21 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
};
const VisualShaderNodeInput::Port VisualShaderNodeInput::preview_ports[] = {
+ // Spatial, Vertex
+
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0, 0.0, 1.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "vec3(0.0, 1.0, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "binormal", "vec3(1.0, 0.0, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "vec3(UV, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "viewport_size", "vec3(1.0, 1.0, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+
// Spatial, Fragment
+
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.rgb" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0, 0.0, 1.0)" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "vec3(0.0, 1.0, 0.0)" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "binormal", "vec3(1.0, 0.0, 0.0)" },
@@ -2315,44 +2333,59 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::preview_ports[] = {
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "vec3(UV, 0.0)" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" },
-
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "side", "1.0" },
-
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "viewport_size", "vec3(1.0, 1.0, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
// Spatial, Light
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0, 0.0, 1.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.rgb" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0, 0.0, 1.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "vec3(UV, 0.0)" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "viewport_size", "vec3(1.0, 1.0, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+
// Canvas Item, Vertex
+
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "vec3(VERTEX, 0.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+
// Canvas Item, Fragment
+
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.rgb" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+
// Canvas Item, Light
+
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.rgb" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0, 0.0, 1.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" },
-
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ // Sky
+
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+
// Particles
+
{ Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+
{ Shader::MODE_MAX, VisualShader::TYPE_MAX, VisualShaderNode::PORT_TYPE_TRANSFORM, nullptr, nullptr },
};
@@ -2413,13 +2446,10 @@ String VisualShaderNodeInput::generate_code(Shader::Mode p_mode, VisualShader::T
case PORT_TYPE_VECTOR: {
code = " " + p_output_vars[0] + " = vec3(0.0);\n";
} break;
- case PORT_TYPE_TRANSFORM: {
- code = " " + p_output_vars[0] + " = mat4(vec4(1.0, 0.0, 0.0, 0.0), vec4(0.0, 1.0, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n";
- } break;
case PORT_TYPE_BOOLEAN: {
code = " " + p_output_vars[0] + " = false;\n";
} break;
- default: //default (none found) is scalar
+ default:
break;
}
}
diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h
index b3efac02aa..f910f28536 100644
--- a/scene/resources/visual_shader.h
+++ b/scene/resources/visual_shader.h
@@ -254,6 +254,8 @@ public:
void set_input_port_connected(int p_port, bool p_connected);
virtual bool is_generate_input_var(int p_port) const;
+ virtual bool has_output_port_preview(int p_port) const;
+
virtual bool is_output_port_expandable(int p_port) const;
void _set_output_ports_expanded(const Array &p_data);
Array _get_output_ports_expanded() const;
diff --git a/scene/resources/visual_shader_particle_nodes.cpp b/scene/resources/visual_shader_particle_nodes.cpp
index 5fe801e037..18b933e5cf 100644
--- a/scene/resources/visual_shader_particle_nodes.cpp
+++ b/scene/resources/visual_shader_particle_nodes.cpp
@@ -47,6 +47,10 @@ String VisualShaderNodeParticleEmitter::get_output_port_name(int p_port) const {
return String();
}
+bool VisualShaderNodeParticleEmitter::has_output_port_preview(int p_port) const {
+ return false;
+}
+
VisualShaderNodeParticleEmitter::VisualShaderNodeParticleEmitter() {
}
@@ -265,6 +269,10 @@ Vector<StringName> VisualShaderNodeParticleMultiplyByAxisAngle::get_editable_pro
return props;
}
+bool VisualShaderNodeParticleMultiplyByAxisAngle::has_output_port_preview(int p_port) const {
+ return false;
+}
+
VisualShaderNodeParticleMultiplyByAxisAngle::VisualShaderNodeParticleMultiplyByAxisAngle() {
set_input_port_default_value(1, Vector3(1, 0, 0));
set_input_port_default_value(2, 0.0);
@@ -313,6 +321,10 @@ String VisualShaderNodeParticleConeVelocity::get_output_port_name(int p_port) co
return String();
}
+bool VisualShaderNodeParticleConeVelocity::has_output_port_preview(int p_port) const {
+ return false;
+}
+
String VisualShaderNodeParticleConeVelocity::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 {
String code;
code += " __radians = radians(" + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ");\n";
@@ -421,6 +433,10 @@ VisualShaderNodeParticleRandomness::OpType VisualShaderNodeParticleRandomness::g
return op_type;
}
+bool VisualShaderNodeParticleRandomness::has_output_port_preview(int p_port) const {
+ return false;
+}
+
VisualShaderNodeParticleRandomness::VisualShaderNodeParticleRandomness() {
set_input_port_default_value(0, 0.0);
set_input_port_default_value(1, 1.0);
@@ -521,6 +537,10 @@ VisualShaderNodeParticleAccelerator::Mode VisualShaderNodeParticleAccelerator::g
return mode;
}
+bool VisualShaderNodeParticleAccelerator::has_output_port_preview(int p_port) const {
+ return false;
+}
+
VisualShaderNodeParticleAccelerator::VisualShaderNodeParticleAccelerator() {
set_input_port_default_value(0, Vector3(1, 1, 1));
set_input_port_default_value(1, 0.0);
diff --git a/scene/resources/visual_shader_particle_nodes.h b/scene/resources/visual_shader_particle_nodes.h
index f5435c3511..b8bc7992cc 100644
--- a/scene/resources/visual_shader_particle_nodes.h
+++ b/scene/resources/visual_shader_particle_nodes.h
@@ -42,6 +42,7 @@ public:
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
+ virtual bool has_output_port_preview(int p_port) const override;
VisualShaderNodeParticleEmitter();
};
@@ -112,6 +113,7 @@ public:
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
+ virtual bool has_output_port_preview(int p_port) 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;
@@ -135,6 +137,7 @@ public:
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
+ virtual bool has_output_port_preview(int p_port) 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;
@@ -168,6 +171,7 @@ public:
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
+ virtual bool has_output_port_preview(int p_port) 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;
@@ -209,6 +213,7 @@ public:
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
+ virtual bool has_output_port_preview(int p_port) 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;
diff --git a/servers/rendering/renderer_rd/shader_compiler_rd.cpp b/servers/rendering/renderer_rd/shader_compiler_rd.cpp
index 4c26941dae..42d11d8bc1 100644
--- a/servers/rendering/renderer_rd/shader_compiler_rd.cpp
+++ b/servers/rendering/renderer_rd/shader_compiler_rd.cpp
@@ -1463,6 +1463,7 @@ void ShaderCompilerRD::initialize(DefaultIdentifierActions p_actions) {
texture_functions.insert("textureLod");
texture_functions.insert("textureProjLod");
texture_functions.insert("textureGrad");
+ texture_functions.insert("textureGather");
texture_functions.insert("textureSize");
texture_functions.insert("texelFetch");
}
diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp
index 81a9e00d7c..9c38bf7606 100644
--- a/servers/rendering/shader_language.cpp
+++ b/servers/rendering/shader_language.cpp
@@ -2456,6 +2456,23 @@ const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = {
{ "textureGrad", TYPE_VEC4, { TYPE_SAMPLERCUBE, TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords", "dPdx", "dPdy" }, TAG_GLOBAL, true },
{ "textureGrad", TYPE_VEC4, { TYPE_SAMPLERCUBEARRAY, TYPE_VEC4, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords", "dPdx", "dPdy" }, TAG_GLOBAL, true },
+ // textureGather
+
+ { "textureGather", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC2, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true },
+ { "textureGather", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC2, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true },
+ { "textureGather", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC2, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true },
+ { "textureGather", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC2, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "comp" }, TAG_GLOBAL, true },
+ { "textureGather", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC2, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "comp" }, TAG_GLOBAL, true },
+ { "textureGather", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC2, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "comp" }, TAG_GLOBAL, true },
+ { "textureGather", TYPE_VEC4, { TYPE_SAMPLER2DARRAY, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true },
+ { "textureGather", TYPE_IVEC4, { TYPE_ISAMPLER2DARRAY, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true },
+ { "textureGather", TYPE_UVEC4, { TYPE_USAMPLER2DARRAY, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true },
+ { "textureGather", TYPE_VEC4, { TYPE_SAMPLER2DARRAY, TYPE_VEC3, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "comp" }, TAG_GLOBAL, true },
+ { "textureGather", TYPE_IVEC4, { TYPE_ISAMPLER2DARRAY, TYPE_VEC3, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "comp" }, TAG_GLOBAL, true },
+ { "textureGather", TYPE_UVEC4, { TYPE_USAMPLER2DARRAY, TYPE_VEC3, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "comp" }, TAG_GLOBAL, true },
+ { "textureGather", TYPE_VEC4, { TYPE_SAMPLERCUBE, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true },
+ { "textureGather", TYPE_VEC4, { TYPE_SAMPLERCUBE, TYPE_VEC3, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "comp" }, TAG_GLOBAL, true },
+
// dFdx
{ "dFdx", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "p" }, TAG_GLOBAL, true },
@@ -2576,6 +2593,20 @@ const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = {
{ "findMSB", TYPE_UVEC3, { TYPE_UVEC3, TYPE_VOID }, { "value" }, TAG_GLOBAL, true },
{ "findMSB", TYPE_UVEC4, { TYPE_UVEC4, TYPE_VOID }, { "value" }, TAG_GLOBAL, true },
+ // umulExtended
+
+ { "umulExtended", TYPE_VOID, { TYPE_UINT, TYPE_UINT, TYPE_UINT, TYPE_UINT, TYPE_VOID }, { "x", "y", "msb", "lsb" }, TAG_GLOBAL, true },
+ { "umulExtended", TYPE_VOID, { TYPE_UVEC2, TYPE_UVEC2, TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, { "x", "y", "msb", "lsb" }, TAG_GLOBAL, true },
+ { "umulExtended", TYPE_VOID, { TYPE_UVEC3, TYPE_UVEC3, TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, { "x", "y", "msb", "lsb" }, TAG_GLOBAL, true },
+ { "umulExtended", TYPE_VOID, { TYPE_UVEC4, TYPE_UVEC4, TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, { "x", "y", "msb", "lsb" }, TAG_GLOBAL, true },
+
+ // imulExtended
+
+ { "imulExtended", TYPE_VOID, { TYPE_INT, TYPE_INT, TYPE_INT, TYPE_INT, TYPE_VOID }, { "x", "y", "msb", "lsb" }, TAG_GLOBAL, true },
+ { "imulExtended", TYPE_VOID, { TYPE_IVEC2, TYPE_IVEC2, TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, { "x", "y", "msb", "lsb" }, TAG_GLOBAL, true },
+ { "imulExtended", TYPE_VOID, { TYPE_IVEC3, TYPE_IVEC3, TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, { "x", "y", "msb", "lsb" }, TAG_GLOBAL, true },
+ { "imulExtended", TYPE_VOID, { TYPE_IVEC4, TYPE_IVEC4, TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, { "x", "y", "msb", "lsb" }, TAG_GLOBAL, true },
+
// uaddCarry
{ "uaddCarry", TYPE_UINT, { TYPE_UINT, TYPE_UINT, TYPE_UINT, TYPE_VOID }, { "x", "y", "carry" }, TAG_GLOBAL, true },
@@ -2590,15 +2621,37 @@ const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = {
{ "usubBorrow", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, { "x", "y", "borrow" }, TAG_GLOBAL, true },
{ "usubBorrow", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, { "x", "y", "borrow" }, TAG_GLOBAL, true },
+ // ldexp
+
+ { "ldexp", TYPE_FLOAT, { TYPE_FLOAT, TYPE_INT, TYPE_VOID }, { "x", "exp" }, TAG_GLOBAL, true },
+ { "ldexp", TYPE_VEC2, { TYPE_VEC2, TYPE_IVEC2, TYPE_VOID }, { "x", "exp" }, TAG_GLOBAL, true },
+ { "ldexp", TYPE_VEC3, { TYPE_VEC3, TYPE_IVEC3, TYPE_VOID }, { "x", "exp" }, TAG_GLOBAL, true },
+ { "ldexp", TYPE_VEC4, { TYPE_VEC4, TYPE_IVEC4, TYPE_VOID }, { "x", "exp" }, TAG_GLOBAL, true },
+
+ // frexp
+
+ { "frexp", TYPE_FLOAT, { TYPE_FLOAT, TYPE_INT, TYPE_VOID }, { "x", "exp" }, TAG_GLOBAL, true },
+ { "frexp", TYPE_VEC2, { TYPE_VEC2, TYPE_IVEC2, TYPE_VOID }, { "x", "exp" }, TAG_GLOBAL, true },
+ { "frexp", TYPE_VEC3, { TYPE_VEC3, TYPE_IVEC3, TYPE_VOID }, { "x", "exp" }, TAG_GLOBAL, true },
+ { "frexp", TYPE_VEC4, { TYPE_VEC4, TYPE_IVEC4, TYPE_VOID }, { "x", "exp" }, TAG_GLOBAL, true },
+
{ nullptr, TYPE_VOID, { TYPE_VOID }, { "" }, TAG_GLOBAL, false }
};
const ShaderLanguage::BuiltinFuncOutArgs ShaderLanguage::builtin_func_out_args[] = {
- //constructors
- { "modf", 1 },
- { "uaddCarry", 2 },
- { "usubBorrow", 2 },
- { nullptr, 0 }
+ { "modf", { 1, -1 } },
+ { "umulExtended", { 2, 3 } },
+ { "imulExtended", { 2, 3 } },
+ { "uaddCarry", { 2, -1 } },
+ { "usubBorrow", { 2, -1 } },
+ { "ldexp", { 1, -1 } },
+ { "frexp", { 1, -1 } },
+ { nullptr, { 0, -1 } }
+};
+
+const ShaderLanguage::BuiltinFuncConstArgs ShaderLanguage::builtin_func_const_args[] = {
+ { "textureGather", 2, 0, 3 },
+ { nullptr, 0, 0, 0 }
};
bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionInfo &p_function_info, OperatorNode *p_func, DataType *r_ret_type, StringName *r_ret_type_str) {
@@ -2692,100 +2745,152 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI
}
if (!fail) {
- //make sure its not an out argument used in the wrong way
- int outarg_idx = 0;
- while (builtin_func_out_args[outarg_idx].name) {
- if (String(name) == builtin_func_out_args[outarg_idx].name) {
- int arg_idx = builtin_func_out_args[outarg_idx].argument;
-
- if (arg_idx < argcount) {
- if (p_func->arguments[arg_idx + 1]->type != Node::TYPE_VARIABLE && p_func->arguments[arg_idx + 1]->type != Node::TYPE_MEMBER && p_func->arguments[arg_idx + 1]->type != Node::TYPE_ARRAY) {
- _set_error("Argument " + itos(arg_idx + 1) + " of function '" + String(name) + "' is not a variable, array or member.");
- return false;
+ {
+ int constarg_idx = 0;
+ while (builtin_func_const_args[constarg_idx].name) {
+ if (String(name) == builtin_func_const_args[constarg_idx].name) {
+ int arg = builtin_func_const_args[constarg_idx].arg + 1;
+ if (p_func->arguments.size() <= arg) {
+ break;
}
- if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_ARRAY) {
- ArrayNode *mn = static_cast<ArrayNode *>(p_func->arguments[arg_idx + 1]);
- if (mn->is_const) {
- fail = true;
- }
- } else if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_MEMBER) {
- MemberNode *mn = static_cast<MemberNode *>(p_func->arguments[arg_idx + 1]);
- if (mn->basetype_const) {
- fail = true;
+ int min = builtin_func_const_args[constarg_idx].min;
+ int max = builtin_func_const_args[constarg_idx].max;
+
+ bool error = false;
+ if (p_func->arguments[arg]->type == Node::TYPE_VARIABLE) {
+ const VariableNode *vn = (VariableNode *)p_func->arguments[arg];
+
+ bool is_const = false;
+ ConstantNode::Value value;
+
+ _find_identifier(p_block, false, p_function_info, vn->name, nullptr, nullptr, &is_const, nullptr, nullptr, &value);
+ if (!is_const || value.sint < min || value.sint > max) {
+ error = true;
}
- } else { // TYPE_VARIABLE
- VariableNode *vn = static_cast<VariableNode *>(p_func->arguments[arg_idx + 1]);
- if (vn->is_const) {
- fail = true;
- } else {
- StringName varname = vn->name;
- if (shader->uniforms.has(varname)) {
- fail = true;
- } else {
- if (shader->varyings.has(varname)) {
- _set_error(vformat("Varyings cannot be passed for '%s' parameter!", "out"));
- return false;
- }
- if (p_function_info.built_ins.has(varname)) {
- BuiltInInfo info = p_function_info.built_ins[varname];
- if (info.constant) {
- fail = true;
- }
+ } else {
+ if (p_func->arguments[arg]->type == Node::TYPE_CONSTANT) {
+ ConstantNode *cn = (ConstantNode *)p_func->arguments[arg];
+
+ if (cn->get_datatype() == TYPE_INT && cn->values.size() == 1) {
+ int value = cn->values[0].sint;
+
+ if (value < min || value > max) {
+ error = true;
}
+ } else {
+ error = true;
}
+ } else {
+ error = true;
}
}
- if (fail) {
- _set_error(vformat("Constant value cannot be passed for '%s' parameter!", "out"));
+ if (error) {
+ _set_error(vformat("Expected integer constant within %s..%s range.", min, max));
return false;
}
+ }
+ constarg_idx++;
+ }
+ }
- StringName var_name;
- if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_ARRAY) {
- var_name = static_cast<const ArrayNode *>(p_func->arguments[arg_idx + 1])->name;
- } else if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_MEMBER) {
- Node *n = static_cast<const MemberNode *>(p_func->arguments[arg_idx + 1])->owner;
- while (n->type == Node::TYPE_MEMBER) {
- n = static_cast<const MemberNode *>(n)->owner;
- }
- if (n->type != Node::TYPE_VARIABLE && n->type != Node::TYPE_ARRAY) {
+ //make sure its not an out argument used in the wrong way
+ int outarg_idx = 0;
+ while (builtin_func_out_args[outarg_idx].name) {
+ if (String(name) == builtin_func_out_args[outarg_idx].name) {
+ for (int arg = 0; arg < BuiltinFuncOutArgs::MAX_ARGS; arg++) {
+ int arg_idx = builtin_func_out_args[outarg_idx].arguments[arg];
+ if (arg_idx == -1) {
+ break;
+ }
+ if (arg_idx < argcount) {
+ if (p_func->arguments[arg_idx + 1]->type != Node::TYPE_VARIABLE && p_func->arguments[arg_idx + 1]->type != Node::TYPE_MEMBER && p_func->arguments[arg_idx + 1]->type != Node::TYPE_ARRAY) {
_set_error("Argument " + itos(arg_idx + 1) + " of function '" + String(name) + "' is not a variable, array or member.");
return false;
}
- if (n->type == Node::TYPE_VARIABLE) {
- var_name = static_cast<const VariableNode *>(n)->name;
- } else { // TYPE_ARRAY
- var_name = static_cast<const ArrayNode *>(n)->name;
+
+ if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_ARRAY) {
+ ArrayNode *mn = static_cast<ArrayNode *>(p_func->arguments[arg_idx + 1]);
+ if (mn->is_const) {
+ fail = true;
+ }
+ } else if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_MEMBER) {
+ MemberNode *mn = static_cast<MemberNode *>(p_func->arguments[arg_idx + 1]);
+ if (mn->basetype_const) {
+ fail = true;
+ }
+ } else { // TYPE_VARIABLE
+ VariableNode *vn = static_cast<VariableNode *>(p_func->arguments[arg_idx + 1]);
+ if (vn->is_const) {
+ fail = true;
+ } else {
+ StringName varname = vn->name;
+ if (shader->uniforms.has(varname)) {
+ fail = true;
+ } else {
+ if (shader->varyings.has(varname)) {
+ _set_error(vformat("Varyings cannot be passed for '%s' parameter!", "out"));
+ return false;
+ }
+ if (p_function_info.built_ins.has(varname)) {
+ BuiltInInfo info = p_function_info.built_ins[varname];
+ if (info.constant) {
+ fail = true;
+ }
+ }
+ }
+ }
}
- } else { // TYPE_VARIABLE
- var_name = static_cast<const VariableNode *>(p_func->arguments[arg_idx + 1])->name;
- }
- const BlockNode *b = p_block;
- bool valid = false;
- while (b) {
- if (b->variables.has(var_name) || p_function_info.built_ins.has(var_name)) {
- valid = true;
- break;
+ if (fail) {
+ _set_error(vformat("Constant value cannot be passed for '%s' parameter!", "out"));
+ return false;
}
- if (b->parent_function) {
- for (int i = 0; i < b->parent_function->arguments.size(); i++) {
- if (b->parent_function->arguments[i].name == var_name) {
- valid = true;
- break;
+
+ StringName var_name;
+ if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_ARRAY) {
+ var_name = static_cast<const ArrayNode *>(p_func->arguments[arg_idx + 1])->name;
+ } else if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_MEMBER) {
+ Node *n = static_cast<const MemberNode *>(p_func->arguments[arg_idx + 1])->owner;
+ while (n->type == Node::TYPE_MEMBER) {
+ n = static_cast<const MemberNode *>(n)->owner;
+ }
+ if (n->type != Node::TYPE_VARIABLE && n->type != Node::TYPE_ARRAY) {
+ _set_error("Argument " + itos(arg_idx + 1) + " of function '" + String(name) + "' is not a variable, array or member.");
+ return false;
+ }
+ if (n->type == Node::TYPE_VARIABLE) {
+ var_name = static_cast<const VariableNode *>(n)->name;
+ } else { // TYPE_ARRAY
+ var_name = static_cast<const ArrayNode *>(n)->name;
+ }
+ } else { // TYPE_VARIABLE
+ var_name = static_cast<const VariableNode *>(p_func->arguments[arg_idx + 1])->name;
+ }
+ const BlockNode *b = p_block;
+ bool valid = false;
+ while (b) {
+ if (b->variables.has(var_name) || p_function_info.built_ins.has(var_name)) {
+ valid = true;
+ break;
+ }
+ if (b->parent_function) {
+ for (int i = 0; i < b->parent_function->arguments.size(); i++) {
+ if (b->parent_function->arguments[i].name == var_name) {
+ valid = true;
+ break;
+ }
}
}
+ b = b->parent_block;
}
- b = b->parent_block;
- }
- if (!valid) {
- _set_error("Argument " + itos(arg_idx + 1) + " of function '" + String(name) + "' can only take a local variable, array or member.");
- return false;
+ if (!valid) {
+ _set_error("Argument " + itos(arg_idx + 1) + " of function '" + String(name) + "' can only take a local variable, array or member.");
+ return false;
+ }
}
}
}
-
outarg_idx++;
}
//implicitly convert values if possible
@@ -9157,10 +9262,16 @@ Error ShaderLanguage::complete(const String &p_code, const Map<StringName, Funct
}
int idx2 = 0;
- int out_arg = -1;
+ Set<int> out_args;
while (builtin_func_out_args[idx2].name != nullptr) {
if (builtin_func_out_args[idx2].name == builtin_func_defs[idx].name) {
- out_arg = builtin_func_out_args[idx2].argument;
+ for (int i = 0; i < BuiltinFuncOutArgs::MAX_ARGS; i++) {
+ int arg = builtin_func_out_args[idx2].arguments[i];
+ if (arg == -1) {
+ break;
+ }
+ out_args.insert(arg);
+ }
break;
}
idx2++;
@@ -9197,7 +9308,7 @@ Error ShaderLanguage::complete(const String &p_code, const Map<StringName, Funct
calltip += char32_t(0xFFFF);
}
- if (out_arg >= 0 && i == out_arg) {
+ if (out_args.has(i)) {
calltip += "out ";
}
diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h
index 4740da0c37..c82f71d10d 100644
--- a/servers/rendering/shader_language.h
+++ b/servers/rendering/shader_language.h
@@ -949,8 +949,16 @@ private:
};
struct BuiltinFuncOutArgs { //arguments used as out in built in functions
+ enum { MAX_ARGS = 2 };
const char *name;
- int argument;
+ const int arguments[MAX_ARGS];
+ };
+
+ struct BuiltinFuncConstArgs {
+ const char *name;
+ int arg;
+ int min;
+ int max;
};
CompletionType completion_type;
@@ -966,6 +974,7 @@ private:
bool _get_completable_identifier(BlockNode *p_block, CompletionType p_type, StringName &identifier);
static const BuiltinFuncDef builtin_func_defs[];
static const BuiltinFuncOutArgs builtin_func_out_args[];
+ static const BuiltinFuncConstArgs builtin_func_const_args[];
Error _validate_datatype(DataType p_type);
bool _compare_datatypes(DataType p_datatype_a, String p_datatype_name_a, int p_array_size_a, DataType p_datatype_b, String p_datatype_name_b, int p_array_size_b);