mirror of
https://github.com/godotengine/godot.git
synced 2025-12-07 13:49:54 +00:00
Add BoneTwistDisperser3D to propagate IK target's twist
This commit is contained in:
parent
7ed0b61676
commit
6ff3833c33
6 changed files with 1249 additions and 0 deletions
268
doc/classes/BoneTwistDisperser3D.xml
Normal file
268
doc/classes/BoneTwistDisperser3D.xml
Normal file
|
|
@ -0,0 +1,268 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="BoneTwistDisperser3D" inherits="SkeletonModifier3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
|
||||
<brief_description>
|
||||
A node that propagates and disperses the child bone's twist to the parent bones.
|
||||
</brief_description>
|
||||
<description>
|
||||
This [BoneTwistDisperser3D] allows for smooth twist interpolation between multiple bones by dispersing the end bone's twist to the parents. This only changes the twist without changing the global position of each joint.
|
||||
This is useful for smoothly twisting bones in combination with [CopyTransformModifier3D] and IK.
|
||||
[b]Note:[/b] If an extracted twist is greater than 180 degrees, flipping occurs. This is similar to [ConvertTransformModifier3D].
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
<methods>
|
||||
<method name="clear_settings">
|
||||
<return type="void" />
|
||||
<description>
|
||||
Clears all settings.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_damping_curve" qualifiers="const">
|
||||
<return type="Curve" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<description>
|
||||
Returns the damping curve when [method get_disperse_mode] is [constant DISPERSE_MODE_CUSTOM].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_disperse_mode" qualifiers="const">
|
||||
<return type="int" enum="BoneTwistDisperser3D.DisperseMode" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<description>
|
||||
Returns whether to use automatic amount assignment or to allow manual assignment.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_end_bone" qualifiers="const">
|
||||
<return type="int" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<description>
|
||||
Returns the end bone index of the bone chain.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_end_bone_direction" qualifiers="const">
|
||||
<return type="int" enum="SkeletonModifier3D.BoneDirection" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<description>
|
||||
Returns the tail direction of the end bone of the bone chain when [method is_end_bone_extended] is [code]true[/code].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_end_bone_name" qualifiers="const">
|
||||
<return type="String" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<description>
|
||||
Returns the end bone name of the bone chain.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_joint_bone" qualifiers="const">
|
||||
<return type="int" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<param index="1" name="joint" type="int" />
|
||||
<description>
|
||||
Returns the bone index at [param joint] in the bone chain's joint list.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_joint_bone_name" qualifiers="const">
|
||||
<return type="String" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<param index="1" name="joint" type="int" />
|
||||
<description>
|
||||
Returns the bone name at [param joint] in the bone chain's joint list.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_joint_count" qualifiers="const">
|
||||
<return type="int" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<description>
|
||||
Returns the joint count of the bone chain's joint list.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_joint_twist_amount" qualifiers="const">
|
||||
<return type="float" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<param index="1" name="joint" type="int" />
|
||||
<description>
|
||||
Returns the twist amount at [param joint] in the bone chain's joint list when [method get_disperse_mode] is [constant DISPERSE_MODE_CUSTOM].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_reference_bone" qualifiers="const">
|
||||
<return type="int" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<description>
|
||||
Returns the reference bone to extract twist of the setting at [param index].
|
||||
This bone is either the end of the chain or its parent, depending on [method is_end_bone_extended].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_reference_bone_name" qualifiers="const">
|
||||
<return type="String" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<description>
|
||||
Returns the reference bone name to extract twist of the setting at [param index].
|
||||
This bone is either the end of the chain or its parent, depending on [method is_end_bone_extended].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_root_bone" qualifiers="const">
|
||||
<return type="int" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<description>
|
||||
Returns the root bone index of the bone chain.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_root_bone_name" qualifiers="const">
|
||||
<return type="String" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<description>
|
||||
Returns the root bone name of the bone chain.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_twist_from" qualifiers="const">
|
||||
<return type="Quaternion" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<description>
|
||||
Returns the rotation to an arbitrary state before twisting for the current bone pose to extract the twist when [method is_twist_from_rest] is [code]false[/code].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_weight_position" qualifiers="const">
|
||||
<return type="float" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<description>
|
||||
Returns the position at which to divide the segment between joints for weight assignment when [method get_disperse_mode] is [constant DISPERSE_MODE_WEIGHTED].
|
||||
</description>
|
||||
</method>
|
||||
<method name="is_end_bone_extended" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<description>
|
||||
Returns [code]true[/code] if the end bone is extended to have a tail.
|
||||
</description>
|
||||
</method>
|
||||
<method name="is_twist_from_rest" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<description>
|
||||
Returns [code]true[/code] if extracting the twist amount from the difference between the bone rest and the current bone pose.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_damping_curve">
|
||||
<return type="void" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<param index="1" name="curve" type="Curve" />
|
||||
<description>
|
||||
Sets the damping curve when [method get_disperse_mode] is [constant DISPERSE_MODE_CUSTOM].
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_disperse_mode">
|
||||
<return type="void" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<param index="1" name="disperse_mode" type="int" enum="BoneTwistDisperser3D.DisperseMode" />
|
||||
<description>
|
||||
Sets whether to use automatic amount assignment or to allow manual assignment.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_end_bone">
|
||||
<return type="void" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<param index="1" name="bone" type="int" />
|
||||
<description>
|
||||
Sets the end bone index of the bone chain.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_end_bone_direction">
|
||||
<return type="void" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<param index="1" name="bone_direction" type="int" enum="SkeletonModifier3D.BoneDirection" />
|
||||
<description>
|
||||
Sets the end bone tail direction of the bone chain when [method is_end_bone_extended] is [code]true[/code].
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_end_bone_name">
|
||||
<return type="void" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<param index="1" name="bone_name" type="String" />
|
||||
<description>
|
||||
Sets the end bone name of the bone chain.
|
||||
[b]Note:[/b] The end bone must be a child of the root bone.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_extend_end_bone">
|
||||
<return type="void" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<param index="1" name="enabled" type="bool" />
|
||||
<description>
|
||||
If [param enabled] is [code]true[/code], the end bone is extended to have a tail.
|
||||
If [param enabled] is [code]false[/code], [method get_reference_bone] becomes a parent of the end bone and it uses the vector to the end bone as a twist axis.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_joint_twist_amount">
|
||||
<return type="void" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<param index="1" name="joint" type="int" />
|
||||
<param index="2" name="twist_amount" type="float" />
|
||||
<description>
|
||||
Sets the twist amount at [param joint] in the bone chain's joint list when [method get_disperse_mode] is [constant DISPERSE_MODE_CUSTOM].
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_root_bone">
|
||||
<return type="void" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<param index="1" name="bone" type="int" />
|
||||
<description>
|
||||
Sets the root bone index of the bone chain.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_root_bone_name">
|
||||
<return type="void" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<param index="1" name="bone_name" type="String" />
|
||||
<description>
|
||||
Sets the root bone name of the bone chain.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_twist_from">
|
||||
<return type="void" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<param index="1" name="from" type="Quaternion" />
|
||||
<description>
|
||||
Sets the rotation to an arbitrary state before twisting for the current bone pose to extract the twist when [method is_twist_from_rest] is [code]false[/code].
|
||||
In other words, by calling [method set_twist_from] by [signal SkeletonModifier3D.modification_processed] of a specific [SkeletonModifier3D], you can extract only the twists generated by modifiers processed after that but before this [BoneTwistDisperser3D].
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_twist_from_rest">
|
||||
<return type="void" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<param index="1" name="enabled" type="bool" />
|
||||
<description>
|
||||
If [param enabled] is [code]true[/code], it extracts the twist amount from the difference between the bone rest and the current bone pose.
|
||||
If [param enabled] is [code]false[/code], it extracts the twist amount from the difference between [method get_twist_from] and the current bone pose. See also [method set_twist_from].
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_weight_position">
|
||||
<return type="void" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<param index="1" name="weight_position" type="float" />
|
||||
<description>
|
||||
Sets the position at which to divide the segment between joints for weight assignment when [method get_disperse_mode] is [constant DISPERSE_MODE_WEIGHTED].
|
||||
For example, when [param weight_position] is [code]0.5[/code], if two bone segments with a length of [code]1.0[/code] exist between three joints, weights are assigned to each joint from root to end at ratios of [code]0.5[/code], [code]1.0[/code], and [code]0.5[/code]. Then amounts become [code]0.25[/code], [code]0.75[/code], and [code]1.0[/code] respectively.
|
||||
</description>
|
||||
</method>
|
||||
</methods>
|
||||
<members>
|
||||
<member name="mutable_bone_axes" type="bool" setter="set_mutable_bone_axes" getter="are_bone_axes_mutable" default="true">
|
||||
If [code]true[/code], the solver retrieves the bone axis from the bone pose every frame.
|
||||
If [code]false[/code], the solver retrieves the bone axis from the bone rest and caches it.
|
||||
</member>
|
||||
<member name="setting_count" type="int" setter="set_setting_count" getter="get_setting_count" default="0">
|
||||
The number of settings.
|
||||
</member>
|
||||
</members>
|
||||
<constants>
|
||||
<constant name="DISPERSE_MODE_EVEN" value="0" enum="DisperseMode">
|
||||
Assign amounts so that they monotonically increase from [code]0.0[/code] to [code]1.0[/code], ensuring all weights are equal. For example, with five joints, the amounts would be [code]0.2[/code], [code]0.4[/code], [code]0.6[/code], [code]0.8[/code], and [code]1.0[/code] starting from the root bone.
|
||||
</constant>
|
||||
<constant name="DISPERSE_MODE_WEIGHTED" value="1" enum="DisperseMode">
|
||||
Assign amounts so that they monotonically increase from [code]0.0[/code] to [code]1.0[/code], based on the length of the bones between joint segments. See also [method set_weight_position].
|
||||
</constant>
|
||||
<constant name="DISPERSE_MODE_CUSTOM" value="2" enum="DisperseMode">
|
||||
You can assign arbitrary amounts to the joint list. See also [method set_joint_twist_amount].
|
||||
When [method is_end_bone_extended] is [code]false[/code], a child of the reference bone exists solely to determine the twist axis, so its custom amount has absolutely no effect at all.
|
||||
</constant>
|
||||
</constants>
|
||||
</class>
|
||||
|
|
@ -15,6 +15,7 @@
|
|||
[b]Not Relative + Not Additive:[/b]
|
||||
- Extract reference pose absolutely and the apply bone's pose is replaced with it.
|
||||
[b]Note:[/b] Relative option is available only in the case [method BoneConstraint3D.get_reference_type] is [constant BoneConstraint3D.REFERENCE_TYPE_BONE]. See also [enum BoneConstraint3D.ReferenceType].
|
||||
[b]Note:[/b] If there is a rotation greater than [code]180[/code] degrees with constrained axes, flipping may occur.
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
|
|
|
|||
1
editor/icons/BoneTwistDisperser3D.svg
Normal file
1
editor/icons/BoneTwistDisperser3D.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#fc7f7f" d="M1.344 11.332v1.668l1.654-.49v.373c-.784.156-1.292.92-1.134 1.705.158.783.921 1.291 1.705 1.135.212-.045.414-.135.588-.266.638.482 1.545.354 2.028-.281.481-.641.355-1.547-.283-2.029-.173-.131-.375-.223-.588-.264v-1.061l1.654-.49v-1.664zM1.344 8v1.666l.003.001 5.622-1.667v-1.668zM5.314 3.49v-.371c.786-.156 1.294-.92 1.136-1.703-.158-.785-.923-1.295-1.707-1.139-.212.045-.413.137-.587.268-.638-.482-1.546-.356-2.029.283-.481.637-.355 1.545.284 2.027.174.131.375.221.587.264v1.057l-1.654.49v1.666l5.625-1.666v-1.666zM10.656 15v-10h-2l3-4 3 4h-2v10z"/><path fill="#b56d6d" d="m4.156 5.5-2.812.832 2.813.836 2.813-.836zm0 3.332-2.812.836 2.812.832 2.813-.832z"/></svg>
|
||||
|
After Width: | Height: | Size: 753 B |
814
scene/3d/bone_twist_disperser_3d.cpp
Normal file
814
scene/3d/bone_twist_disperser_3d.cpp
Normal file
|
|
@ -0,0 +1,814 @@
|
|||
/**************************************************************************/
|
||||
/* bone_twist_disperser_3d.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "bone_twist_disperser_3d.h"
|
||||
|
||||
bool BoneTwistDisperser3D::_set(const StringName &p_path, const Variant &p_value) {
|
||||
String path = p_path;
|
||||
|
||||
if (path.begins_with("settings/")) {
|
||||
int which = path.get_slicec('/', 1).to_int();
|
||||
String what = path.get_slicec('/', 2);
|
||||
ERR_FAIL_INDEX_V(which, (int)settings.size(), false);
|
||||
|
||||
if (what == "root_bone_name") {
|
||||
set_root_bone_name(which, p_value);
|
||||
} else if (what == "root_bone") {
|
||||
set_root_bone(which, p_value);
|
||||
} else if (what == "end_bone_name") {
|
||||
set_end_bone_name(which, p_value);
|
||||
} else if (what == "end_bone") {
|
||||
set_end_bone(which, p_value);
|
||||
} else if (what == "end_bone_direction") {
|
||||
set_end_bone_direction(which, static_cast<BoneDirection>((int)p_value));
|
||||
} else if (what == "extend_end_bone") {
|
||||
set_extend_end_bone(which, p_value);
|
||||
} else if (what == "twist_from_rest") {
|
||||
set_twist_from_rest(which, p_value);
|
||||
} else if (what == "twist_from") {
|
||||
set_twist_from(which, p_value);
|
||||
} else if (what == "disperse_mode") {
|
||||
set_disperse_mode(which, static_cast<DisperseMode>((int)p_value));
|
||||
} else if (what == "weight_position") {
|
||||
set_weight_position(which, p_value);
|
||||
} else if (what == "damping_curve") {
|
||||
set_damping_curve(which, p_value);
|
||||
} else if (what == "joint_count") {
|
||||
set_joint_count(which, p_value);
|
||||
} else if (what == "joints") {
|
||||
int idx = path.get_slicec('/', 3).to_int();
|
||||
String prop = path.get_slicec('/', 4);
|
||||
if (prop == "bone_name") {
|
||||
set_joint_bone_name(which, idx, p_value);
|
||||
} else if (prop == "bone") {
|
||||
set_joint_bone(which, idx, p_value);
|
||||
} else if (prop == "twist_amount") {
|
||||
set_joint_twist_amount(which, idx, p_value);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BoneTwistDisperser3D::_get(const StringName &p_path, Variant &r_ret) const {
|
||||
String path = p_path;
|
||||
|
||||
if (path.begins_with("settings/")) {
|
||||
int which = path.get_slicec('/', 1).to_int();
|
||||
String what = path.get_slicec('/', 2);
|
||||
ERR_FAIL_INDEX_V(which, (int)settings.size(), false);
|
||||
|
||||
if (what == "root_bone_name") {
|
||||
r_ret = get_root_bone_name(which);
|
||||
} else if (what == "root_bone") {
|
||||
r_ret = get_root_bone(which);
|
||||
} else if (what == "end_bone_name") {
|
||||
r_ret = get_end_bone_name(which);
|
||||
} else if (what == "end_bone") {
|
||||
r_ret = get_end_bone(which);
|
||||
} else if (what == "end_bone_direction") {
|
||||
r_ret = (int)get_end_bone_direction(which);
|
||||
} else if (what == "reference_bone_name") {
|
||||
r_ret = get_reference_bone_name(which);
|
||||
} else if (what == "extend_end_bone") {
|
||||
r_ret = is_end_bone_extended(which);
|
||||
} else if (what == "twist_from_rest") {
|
||||
r_ret = is_twist_from_rest(which);
|
||||
} else if (what == "twist_from") {
|
||||
r_ret = get_twist_from(which);
|
||||
} else if (what == "disperse_mode") {
|
||||
r_ret = (int)get_disperse_mode(which);
|
||||
} else if (what == "weight_position") {
|
||||
r_ret = get_weight_position(which);
|
||||
} else if (what == "damping_curve") {
|
||||
r_ret = get_damping_curve(which);
|
||||
} else if (what == "joint_count") {
|
||||
r_ret = get_joint_count(which);
|
||||
} else if (what == "joints") {
|
||||
int idx = path.get_slicec('/', 3).to_int();
|
||||
String prop = path.get_slicec('/', 4);
|
||||
if (prop == "bone_name") {
|
||||
r_ret = get_joint_bone_name(which, idx);
|
||||
} else if (prop == "bone") {
|
||||
r_ret = get_joint_bone(which, idx);
|
||||
} else if (prop == "twist_amount") {
|
||||
r_ret = get_joint_twist_amount(which, idx);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void BoneTwistDisperser3D::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||
String enum_hint;
|
||||
Skeleton3D *skeleton = get_skeleton();
|
||||
if (skeleton) {
|
||||
enum_hint = skeleton->get_concatenated_bone_names();
|
||||
}
|
||||
|
||||
LocalVector<PropertyInfo> props;
|
||||
|
||||
for (uint32_t i = 0; i < settings.size(); i++) {
|
||||
String path = "settings/" + itos(i) + "/";
|
||||
props.push_back(PropertyInfo(Variant::STRING, path + "root_bone_name", PROPERTY_HINT_ENUM_SUGGESTION, enum_hint));
|
||||
props.push_back(PropertyInfo(Variant::INT, path + "root_bone", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
|
||||
props.push_back(PropertyInfo(Variant::STRING, path + "end_bone_name", PROPERTY_HINT_ENUM_SUGGESTION, enum_hint));
|
||||
props.push_back(PropertyInfo(Variant::INT, path + "end_bone", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
|
||||
props.push_back(PropertyInfo(Variant::BOOL, path + "extend_end_bone"));
|
||||
props.push_back(PropertyInfo(Variant::INT, path + "end_bone_direction", PROPERTY_HINT_ENUM, SkeletonModifier3D::get_hint_bone_direction()));
|
||||
|
||||
props.push_back(PropertyInfo(Variant::STRING, path + "reference_bone_name", PROPERTY_HINT_ENUM_SUGGESTION, enum_hint, PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY));
|
||||
props.push_back(PropertyInfo(Variant::BOOL, path + "twist_from_rest"));
|
||||
props.push_back(PropertyInfo(Variant::QUATERNION, path + "twist_from"));
|
||||
props.push_back(PropertyInfo(Variant::INT, path + "disperse_mode", PROPERTY_HINT_ENUM, "Even,Weighted,Custom"));
|
||||
props.push_back(PropertyInfo(Variant::FLOAT, path + "weight_position", PROPERTY_HINT_RANGE, "0,1,0.001"));
|
||||
props.push_back(PropertyInfo(Variant::OBJECT, path + "damping_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"));
|
||||
|
||||
props.push_back(PropertyInfo(Variant::INT, path + "joint_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ARRAY, "Joints," + path + "joints/,static,const"));
|
||||
for (uint32_t j = 0; j < settings[i]->joints.size(); j++) {
|
||||
String joint_path = path + "joints/" + itos(j) + "/";
|
||||
props.push_back(PropertyInfo(Variant::STRING, joint_path + "bone_name", PROPERTY_HINT_ENUM_SUGGESTION, enum_hint, PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY));
|
||||
props.push_back(PropertyInfo(Variant::INT, joint_path + "bone", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_READ_ONLY));
|
||||
props.push_back(PropertyInfo(Variant::FLOAT, joint_path + "twist_amount", PROPERTY_HINT_RANGE, "0,1,0.001,or_greater,or_less"));
|
||||
}
|
||||
}
|
||||
|
||||
for (PropertyInfo &p : props) {
|
||||
_validate_dynamic_prop(p);
|
||||
p_list->push_back(p);
|
||||
}
|
||||
}
|
||||
|
||||
void BoneTwistDisperser3D::_validate_dynamic_prop(PropertyInfo &p_property) const {
|
||||
PackedStringArray split = p_property.name.split("/");
|
||||
if (split.size() > 2 && split[0] == "settings") {
|
||||
int which = split[1].to_int();
|
||||
|
||||
// Extended end bone option.
|
||||
bool force_hide = false;
|
||||
if (split[2] == "extend_end_bone" && get_end_bone(which) == -1) {
|
||||
p_property.usage = PROPERTY_USAGE_NONE;
|
||||
force_hide = true;
|
||||
}
|
||||
if (force_hide || (split[2] == "end_bone_direction" && !is_end_bone_extended(which))) {
|
||||
p_property.usage = PROPERTY_USAGE_NONE;
|
||||
}
|
||||
|
||||
if (split[2] == "twist_from" && is_twist_from_rest(which)) {
|
||||
p_property.usage = PROPERTY_USAGE_NONE;
|
||||
}
|
||||
|
||||
if (split[2] == "weight_position" && get_disperse_mode(which) != DISPERSE_MODE_WEIGHTED) {
|
||||
p_property.usage = PROPERTY_USAGE_NONE;
|
||||
}
|
||||
|
||||
if (split[2] == "damping_curve" && get_disperse_mode(which) != DISPERSE_MODE_CUSTOM) {
|
||||
p_property.usage = PROPERTY_USAGE_NONE;
|
||||
}
|
||||
|
||||
if (split[2] == "joints" && split[4] == "twist_amount") {
|
||||
bool mutable_amount = true;
|
||||
if (get_disperse_mode(which) != DISPERSE_MODE_CUSTOM) {
|
||||
mutable_amount = false;
|
||||
} else if (!is_end_bone_extended(which)) {
|
||||
int joint = split[3].to_int();
|
||||
mutable_amount = joint < get_joint_count(which) - 1; // Hide child of reference bone.
|
||||
}
|
||||
if (get_damping_curve(which).is_valid()) {
|
||||
p_property.usage |= PROPERTY_USAGE_READ_ONLY;
|
||||
}
|
||||
if (!mutable_amount) {
|
||||
p_property.usage = PROPERTY_USAGE_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BoneTwistDisperser3D::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
_make_all_joints_dirty();
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void BoneTwistDisperser3D::_set_active(bool p_active) {
|
||||
if (p_active) {
|
||||
_make_all_joints_dirty();
|
||||
}
|
||||
}
|
||||
|
||||
void BoneTwistDisperser3D::_skeleton_changed(Skeleton3D *p_old, Skeleton3D *p_new) {
|
||||
_make_all_joints_dirty();
|
||||
}
|
||||
|
||||
// Setting.
|
||||
|
||||
void BoneTwistDisperser3D::set_mutable_bone_axes(bool p_enabled) {
|
||||
mutable_bone_axes = p_enabled;
|
||||
}
|
||||
|
||||
bool BoneTwistDisperser3D::are_bone_axes_mutable() const {
|
||||
return mutable_bone_axes;
|
||||
}
|
||||
|
||||
void BoneTwistDisperser3D::set_root_bone_name(int p_index, const String &p_bone_name) {
|
||||
ERR_FAIL_INDEX(p_index, (int)settings.size());
|
||||
settings[p_index]->root_bone.name = p_bone_name;
|
||||
Skeleton3D *sk = get_skeleton();
|
||||
if (sk) {
|
||||
set_root_bone(p_index, sk->find_bone(settings[p_index]->root_bone.name));
|
||||
}
|
||||
}
|
||||
|
||||
String BoneTwistDisperser3D::get_root_bone_name(int p_index) const {
|
||||
ERR_FAIL_INDEX_V(p_index, (int)settings.size(), String());
|
||||
return settings[p_index]->root_bone.name;
|
||||
}
|
||||
|
||||
void BoneTwistDisperser3D::set_root_bone(int p_index, int p_bone) {
|
||||
ERR_FAIL_INDEX(p_index, (int)settings.size());
|
||||
bool changed = settings[p_index]->root_bone.bone != p_bone;
|
||||
settings[p_index]->root_bone.bone = p_bone;
|
||||
Skeleton3D *sk = get_skeleton();
|
||||
if (sk) {
|
||||
if (settings[p_index]->root_bone.bone <= -1 || settings[p_index]->root_bone.bone >= sk->get_bone_count()) {
|
||||
WARN_PRINT("Root bone index out of range!");
|
||||
settings[p_index]->root_bone.bone = -1;
|
||||
} else {
|
||||
settings[p_index]->root_bone.name = sk->get_bone_name(settings[p_index]->root_bone.bone);
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
_make_joints_dirty(p_index);
|
||||
}
|
||||
}
|
||||
|
||||
int BoneTwistDisperser3D::get_root_bone(int p_index) const {
|
||||
ERR_FAIL_INDEX_V(p_index, (int)settings.size(), -1);
|
||||
return settings[p_index]->root_bone.bone;
|
||||
}
|
||||
|
||||
void BoneTwistDisperser3D::set_end_bone_name(int p_index, const String &p_bone_name) {
|
||||
ERR_FAIL_INDEX(p_index, (int)settings.size());
|
||||
settings[p_index]->end_bone.name = p_bone_name;
|
||||
Skeleton3D *sk = get_skeleton();
|
||||
if (sk) {
|
||||
set_end_bone(p_index, sk->find_bone(settings[p_index]->end_bone.name));
|
||||
}
|
||||
}
|
||||
|
||||
String BoneTwistDisperser3D::get_end_bone_name(int p_index) const {
|
||||
ERR_FAIL_INDEX_V(p_index, (int)settings.size(), String());
|
||||
return settings[p_index]->end_bone.name;
|
||||
}
|
||||
|
||||
void BoneTwistDisperser3D::set_end_bone(int p_index, int p_bone) {
|
||||
ERR_FAIL_INDEX(p_index, (int)settings.size());
|
||||
bool changed = settings[p_index]->end_bone.bone != p_bone;
|
||||
settings[p_index]->end_bone.bone = p_bone;
|
||||
Skeleton3D *sk = get_skeleton();
|
||||
if (sk) {
|
||||
if (settings[p_index]->end_bone.bone <= -1 || settings[p_index]->end_bone.bone >= sk->get_bone_count()) {
|
||||
WARN_PRINT("End bone index out of range!");
|
||||
settings[p_index]->end_bone.bone = -1;
|
||||
} else {
|
||||
settings[p_index]->end_bone.name = sk->get_bone_name(settings[p_index]->end_bone.bone);
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
_make_joints_dirty(p_index);
|
||||
}
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
int BoneTwistDisperser3D::get_end_bone(int p_index) const {
|
||||
ERR_FAIL_INDEX_V(p_index, (int)settings.size(), -1);
|
||||
return settings[p_index]->end_bone.bone;
|
||||
}
|
||||
|
||||
void BoneTwistDisperser3D::set_extend_end_bone(int p_index, bool p_enabled) {
|
||||
ERR_FAIL_INDEX(p_index, (int)settings.size());
|
||||
settings[p_index]->extend_end_bone = p_enabled;
|
||||
_update_reference_bone(p_index);
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
bool BoneTwistDisperser3D::is_end_bone_extended(int p_index) const {
|
||||
ERR_FAIL_INDEX_V(p_index, (int)settings.size(), false);
|
||||
return settings[p_index]->extend_end_bone;
|
||||
}
|
||||
|
||||
void BoneTwistDisperser3D::set_end_bone_direction(int p_index, BoneDirection p_bone_direction) {
|
||||
ERR_FAIL_INDEX(p_index, (int)settings.size());
|
||||
settings[p_index]->end_bone_direction = p_bone_direction;
|
||||
}
|
||||
|
||||
SkeletonModifier3D::BoneDirection BoneTwistDisperser3D::get_end_bone_direction(int p_index) const {
|
||||
ERR_FAIL_INDEX_V(p_index, (int)settings.size(), BONE_DIRECTION_FROM_PARENT);
|
||||
return settings[p_index]->end_bone_direction;
|
||||
}
|
||||
|
||||
void BoneTwistDisperser3D::set_twist_from_rest(int p_index, bool p_enabled) {
|
||||
ERR_FAIL_INDEX(p_index, (int)settings.size());
|
||||
settings[p_index]->twist_from_rest = p_enabled;
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
bool BoneTwistDisperser3D::is_twist_from_rest(int p_index) const {
|
||||
ERR_FAIL_INDEX_V(p_index, (int)settings.size(), true);
|
||||
return settings[p_index]->twist_from_rest;
|
||||
}
|
||||
|
||||
void BoneTwistDisperser3D::set_twist_from(int p_index, const Quaternion &p_from) {
|
||||
ERR_FAIL_INDEX(p_index, (int)settings.size());
|
||||
settings[p_index]->twist_from = p_from;
|
||||
}
|
||||
|
||||
Quaternion BoneTwistDisperser3D::get_twist_from(int p_index) const {
|
||||
ERR_FAIL_INDEX_V(p_index, (int)settings.size(), Quaternion());
|
||||
return settings[p_index]->twist_from;
|
||||
}
|
||||
|
||||
void BoneTwistDisperser3D::_update_reference_bone(int p_index) {
|
||||
ERR_FAIL_INDEX(p_index, (int)settings.size());
|
||||
LocalVector<DisperseJointSetting> &joints = settings[p_index]->joints;
|
||||
if (joints.size() >= 2) {
|
||||
if (settings[p_index]->extend_end_bone) {
|
||||
settings[p_index]->reference_bone = settings[p_index]->end_bone;
|
||||
_update_curve(p_index);
|
||||
return;
|
||||
} else {
|
||||
Skeleton3D *sk = get_skeleton();
|
||||
if (sk) {
|
||||
int parent = sk->get_bone_parent(settings[p_index]->end_bone.bone);
|
||||
if (parent >= 0) {
|
||||
settings[p_index]->reference_bone.bone = parent;
|
||||
settings[p_index]->reference_bone.name = sk->get_bone_name(parent);
|
||||
_update_curve(p_index);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
settings[p_index]->reference_bone.bone = -1;
|
||||
settings[p_index]->reference_bone.name = String();
|
||||
}
|
||||
|
||||
void BoneTwistDisperser3D::_update_curve(int p_index) {
|
||||
Ref<Curve> curve = settings[p_index]->damping_curve;
|
||||
if (curve.is_null()) {
|
||||
return;
|
||||
}
|
||||
LocalVector<DisperseJointSetting> &joints = settings[p_index]->joints;
|
||||
float unit = (int)joints.size() > 0 ? (1.0 / float((int)joints.size() - 1)) : 0.0;
|
||||
for (uint32_t i = 0; i < joints.size(); i++) {
|
||||
joints[i].custom_amount = curve->sample_baked(i * unit);
|
||||
}
|
||||
}
|
||||
|
||||
String BoneTwistDisperser3D::get_reference_bone_name(int p_index) const {
|
||||
ERR_FAIL_INDEX_V(p_index, (int)settings.size(), String());
|
||||
return settings[p_index]->reference_bone.name;
|
||||
}
|
||||
|
||||
int BoneTwistDisperser3D::get_reference_bone(int p_index) const {
|
||||
ERR_FAIL_INDEX_V(p_index, (int)settings.size(), -1);
|
||||
return settings[p_index]->reference_bone.bone;
|
||||
}
|
||||
|
||||
void BoneTwistDisperser3D::set_disperse_mode(int p_index, DisperseMode p_disperse_mode) {
|
||||
ERR_FAIL_INDEX(p_index, (int)settings.size());
|
||||
settings[p_index]->disperse_mode = p_disperse_mode;
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
BoneTwistDisperser3D::DisperseMode BoneTwistDisperser3D::get_disperse_mode(int p_index) const {
|
||||
ERR_FAIL_INDEX_V(p_index, (int)settings.size(), DISPERSE_MODE_EVEN);
|
||||
return settings[p_index]->disperse_mode;
|
||||
}
|
||||
|
||||
void BoneTwistDisperser3D::set_weight_position(int p_index, float p_position) {
|
||||
ERR_FAIL_INDEX(p_index, (int)settings.size());
|
||||
settings[p_index]->weight_position = p_position;
|
||||
}
|
||||
|
||||
float BoneTwistDisperser3D::get_weight_position(int p_index) const {
|
||||
ERR_FAIL_INDEX_V(p_index, (int)settings.size(), 0.0);
|
||||
return settings[p_index]->weight_position;
|
||||
}
|
||||
|
||||
void BoneTwistDisperser3D::set_damping_curve(int p_index, const Ref<Curve> &p_damping_curve) {
|
||||
ERR_FAIL_INDEX(p_index, (int)settings.size());
|
||||
bool changed = settings[p_index]->damping_curve != p_damping_curve;
|
||||
if (settings[p_index]->damping_curve.is_valid()) {
|
||||
settings[p_index]->damping_curve->disconnect_changed(callable_mp(this, &BoneTwistDisperser3D::_update_curve));
|
||||
}
|
||||
settings[p_index]->damping_curve = p_damping_curve;
|
||||
if (settings[p_index]->damping_curve.is_valid()) {
|
||||
settings[p_index]->damping_curve->connect_changed(callable_mp(this, &BoneTwistDisperser3D::_update_curve).bind(p_index));
|
||||
}
|
||||
if (changed) {
|
||||
_make_joints_dirty(p_index);
|
||||
}
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
Ref<Curve> BoneTwistDisperser3D::get_damping_curve(int p_index) const {
|
||||
ERR_FAIL_INDEX_V(p_index, (int)settings.size(), Ref<Curve>());
|
||||
return settings[p_index]->damping_curve;
|
||||
}
|
||||
|
||||
// Individual joints.
|
||||
|
||||
void BoneTwistDisperser3D::set_joint_bone_name(int p_index, int p_joint, const String &p_bone_name) {
|
||||
// Exists only for indicate bone name on the inspector, no needs to make dirty joint array.
|
||||
ERR_FAIL_INDEX(p_index, (int)settings.size());
|
||||
LocalVector<DisperseJointSetting> &joints = settings[p_index]->joints;
|
||||
ERR_FAIL_INDEX(p_joint, (int)joints.size());
|
||||
joints[p_joint].joint.name = p_bone_name;
|
||||
Skeleton3D *sk = get_skeleton();
|
||||
if (sk) {
|
||||
set_joint_bone(p_index, p_joint, sk->find_bone(joints[p_joint].joint.name));
|
||||
}
|
||||
}
|
||||
|
||||
String BoneTwistDisperser3D::get_joint_bone_name(int p_index, int p_joint) const {
|
||||
ERR_FAIL_INDEX_V(p_index, (int)settings.size(), String());
|
||||
const LocalVector<DisperseJointSetting> &joints = settings[p_index]->joints;
|
||||
ERR_FAIL_INDEX_V(p_joint, (int)joints.size(), String());
|
||||
return joints[p_joint].joint.name;
|
||||
}
|
||||
|
||||
void BoneTwistDisperser3D::set_joint_bone(int p_index, int p_joint, int p_bone) {
|
||||
ERR_FAIL_INDEX(p_index, (int)settings.size());
|
||||
LocalVector<DisperseJointSetting> &joints = settings[p_index]->joints;
|
||||
ERR_FAIL_INDEX(p_joint, (int)joints.size());
|
||||
joints[p_joint].joint.bone = p_bone;
|
||||
Skeleton3D *sk = get_skeleton();
|
||||
if (sk) {
|
||||
if (joints[p_joint].joint.bone <= -1 || joints[p_joint].joint.bone >= sk->get_bone_count()) {
|
||||
WARN_PRINT("Joint bone index out of range!");
|
||||
joints[p_joint].joint.bone = -1;
|
||||
} else {
|
||||
joints[p_joint].joint.name = sk->get_bone_name(joints[p_joint].joint.bone);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int BoneTwistDisperser3D::get_joint_bone(int p_index, int p_joint) const {
|
||||
ERR_FAIL_INDEX_V(p_index, (int)settings.size(), -1);
|
||||
const LocalVector<DisperseJointSetting> &joints = settings[p_index]->joints;
|
||||
ERR_FAIL_INDEX_V(p_joint, (int)joints.size(), -1);
|
||||
return joints[p_joint].joint.bone;
|
||||
}
|
||||
|
||||
void BoneTwistDisperser3D::set_joint_count(int p_index, int p_count) {
|
||||
ERR_FAIL_INDEX(p_index, (int)settings.size());
|
||||
ERR_FAIL_COND(p_count < 0);
|
||||
LocalVector<DisperseJointSetting> &joints = settings[p_index]->joints;
|
||||
joints.resize(p_count);
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
int BoneTwistDisperser3D::get_joint_count(int p_index) const {
|
||||
ERR_FAIL_INDEX_V(p_index, (int)settings.size(), 0);
|
||||
const LocalVector<DisperseJointSetting> &joints = settings[p_index]->joints;
|
||||
return joints.size();
|
||||
}
|
||||
|
||||
void BoneTwistDisperser3D::set_joint_twist_amount(int p_index, int p_joint, float p_amount) {
|
||||
ERR_FAIL_INDEX(p_index, (int)settings.size());
|
||||
LocalVector<DisperseJointSetting> &joints = settings[p_index]->joints;
|
||||
ERR_FAIL_INDEX(p_joint, (int)joints.size());
|
||||
joints[p_joint].custom_amount = p_amount;
|
||||
}
|
||||
|
||||
float BoneTwistDisperser3D::get_joint_twist_amount(int p_index, int p_joint) const {
|
||||
ERR_FAIL_INDEX_V(p_index, (int)settings.size(), 0);
|
||||
const LocalVector<DisperseJointSetting> &joints = settings[p_index]->joints;
|
||||
ERR_FAIL_INDEX_V(p_joint, (int)joints.size(), 0);
|
||||
return joints[p_joint].custom_amount;
|
||||
}
|
||||
|
||||
void BoneTwistDisperser3D::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_setting_count", "count"), &BoneTwistDisperser3D::set_setting_count);
|
||||
ClassDB::bind_method(D_METHOD("get_setting_count"), &BoneTwistDisperser3D::get_setting_count);
|
||||
ClassDB::bind_method(D_METHOD("clear_settings"), &BoneTwistDisperser3D::clear_settings);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_mutable_bone_axes", "enabled"), &BoneTwistDisperser3D::set_mutable_bone_axes);
|
||||
ClassDB::bind_method(D_METHOD("are_bone_axes_mutable"), &BoneTwistDisperser3D::are_bone_axes_mutable);
|
||||
|
||||
// Setting.
|
||||
ClassDB::bind_method(D_METHOD("set_root_bone_name", "index", "bone_name"), &BoneTwistDisperser3D::set_root_bone_name);
|
||||
ClassDB::bind_method(D_METHOD("get_root_bone_name", "index"), &BoneTwistDisperser3D::get_root_bone_name);
|
||||
ClassDB::bind_method(D_METHOD("set_root_bone", "index", "bone"), &BoneTwistDisperser3D::set_root_bone);
|
||||
ClassDB::bind_method(D_METHOD("get_root_bone", "index"), &BoneTwistDisperser3D::get_root_bone);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_end_bone_name", "index", "bone_name"), &BoneTwistDisperser3D::set_end_bone_name);
|
||||
ClassDB::bind_method(D_METHOD("get_end_bone_name", "index"), &BoneTwistDisperser3D::get_end_bone_name);
|
||||
ClassDB::bind_method(D_METHOD("set_end_bone", "index", "bone"), &BoneTwistDisperser3D::set_end_bone);
|
||||
ClassDB::bind_method(D_METHOD("get_end_bone", "index"), &BoneTwistDisperser3D::get_end_bone);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_reference_bone_name", "index"), &BoneTwistDisperser3D::get_reference_bone_name);
|
||||
ClassDB::bind_method(D_METHOD("get_reference_bone", "index"), &BoneTwistDisperser3D::get_reference_bone);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_extend_end_bone", "index", "enabled"), &BoneTwistDisperser3D::set_extend_end_bone);
|
||||
ClassDB::bind_method(D_METHOD("is_end_bone_extended", "index"), &BoneTwistDisperser3D::is_end_bone_extended);
|
||||
ClassDB::bind_method(D_METHOD("set_end_bone_direction", "index", "bone_direction"), &BoneTwistDisperser3D::set_end_bone_direction);
|
||||
ClassDB::bind_method(D_METHOD("get_end_bone_direction", "index"), &BoneTwistDisperser3D::get_end_bone_direction);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_twist_from_rest", "index", "enabled"), &BoneTwistDisperser3D::set_twist_from_rest);
|
||||
ClassDB::bind_method(D_METHOD("is_twist_from_rest", "index"), &BoneTwistDisperser3D::is_twist_from_rest);
|
||||
ClassDB::bind_method(D_METHOD("set_twist_from", "index", "from"), &BoneTwistDisperser3D::set_twist_from);
|
||||
ClassDB::bind_method(D_METHOD("get_twist_from", "index"), &BoneTwistDisperser3D::get_twist_from);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_disperse_mode", "index", "disperse_mode"), &BoneTwistDisperser3D::set_disperse_mode);
|
||||
ClassDB::bind_method(D_METHOD("get_disperse_mode", "index"), &BoneTwistDisperser3D::get_disperse_mode);
|
||||
ClassDB::bind_method(D_METHOD("set_weight_position", "index", "weight_position"), &BoneTwistDisperser3D::set_weight_position);
|
||||
ClassDB::bind_method(D_METHOD("get_weight_position", "index"), &BoneTwistDisperser3D::get_weight_position);
|
||||
ClassDB::bind_method(D_METHOD("set_damping_curve", "index", "curve"), &BoneTwistDisperser3D::set_damping_curve);
|
||||
ClassDB::bind_method(D_METHOD("get_damping_curve", "index"), &BoneTwistDisperser3D::get_damping_curve);
|
||||
|
||||
// Individual joints.
|
||||
ClassDB::bind_method(D_METHOD("get_joint_bone_name", "index", "joint"), &BoneTwistDisperser3D::get_joint_bone_name);
|
||||
ClassDB::bind_method(D_METHOD("get_joint_bone", "index", "joint"), &BoneTwistDisperser3D::get_joint_bone);
|
||||
ClassDB::bind_method(D_METHOD("get_joint_twist_amount", "index", "joint"), &BoneTwistDisperser3D::get_joint_twist_amount);
|
||||
ClassDB::bind_method(D_METHOD("set_joint_twist_amount", "index", "joint", "twist_amount"), &BoneTwistDisperser3D::set_joint_twist_amount);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_joint_count", "index"), &BoneTwistDisperser3D::get_joint_count);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "mutable_bone_axes"), "set_mutable_bone_axes", "are_bone_axes_mutable");
|
||||
ADD_ARRAY_COUNT("Settings", "setting_count", "set_setting_count", "get_setting_count", "settings/");
|
||||
|
||||
BIND_ENUM_CONSTANT(DISPERSE_MODE_EVEN);
|
||||
BIND_ENUM_CONSTANT(DISPERSE_MODE_WEIGHTED);
|
||||
BIND_ENUM_CONSTANT(DISPERSE_MODE_CUSTOM);
|
||||
}
|
||||
|
||||
void BoneTwistDisperser3D::_validate_bone_names() {
|
||||
for (uint32_t i = 0; i < settings.size(); i++) {
|
||||
// Prior bone name.
|
||||
if (!settings[i]->root_bone.name.is_empty()) {
|
||||
set_root_bone_name(i, settings[i]->root_bone.name);
|
||||
} else if (settings[i]->root_bone.bone != -1) {
|
||||
set_root_bone(i, settings[i]->root_bone.bone);
|
||||
}
|
||||
// Prior bone name.
|
||||
if (!settings[i]->end_bone.name.is_empty()) {
|
||||
set_end_bone_name(i, settings[i]->end_bone.name);
|
||||
} else if (settings[i]->end_bone.bone != -1) {
|
||||
set_end_bone(i, settings[i]->end_bone.bone);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BoneTwistDisperser3D::_make_all_joints_dirty() {
|
||||
for (uint32_t i = 0; i < settings.size(); i++) {
|
||||
_make_joints_dirty(i);
|
||||
}
|
||||
}
|
||||
|
||||
void BoneTwistDisperser3D::_make_joints_dirty(int p_index) {
|
||||
ERR_FAIL_INDEX(p_index, (int)settings.size());
|
||||
if (settings[p_index]->joints_dirty) {
|
||||
return;
|
||||
}
|
||||
settings[p_index]->joints_dirty = true;
|
||||
callable_mp(this, &BoneTwistDisperser3D::_update_joints).call_deferred(p_index);
|
||||
}
|
||||
|
||||
void BoneTwistDisperser3D::_update_joints(int p_index) {
|
||||
Skeleton3D *sk = get_skeleton();
|
||||
int current_bone = settings[p_index]->end_bone.bone;
|
||||
int root_bone = settings[p_index]->root_bone.bone;
|
||||
if (!sk || current_bone < 0 || root_bone < 0) {
|
||||
set_joint_count(p_index, 0);
|
||||
settings[p_index]->joints_dirty = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Validation.
|
||||
bool valid = false;
|
||||
while (current_bone >= 0) {
|
||||
current_bone = sk->get_bone_parent(current_bone);
|
||||
if (current_bone == root_bone) {
|
||||
valid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!valid) {
|
||||
set_joint_count(p_index, 0);
|
||||
_update_reference_bone(p_index);
|
||||
settings[p_index]->joints_dirty = false;
|
||||
ERR_FAIL_EDMSG("End bone must be a child of the root bone.");
|
||||
}
|
||||
|
||||
Vector<int> new_joints;
|
||||
current_bone = settings[p_index]->end_bone.bone;
|
||||
while (current_bone != root_bone) {
|
||||
new_joints.push_back(current_bone);
|
||||
current_bone = sk->get_bone_parent(current_bone);
|
||||
}
|
||||
new_joints.push_back(current_bone);
|
||||
new_joints.reverse();
|
||||
|
||||
set_joint_count(p_index, new_joints.size());
|
||||
for (uint32_t i = 0; i < new_joints.size(); i++) {
|
||||
set_joint_bone(p_index, i, new_joints[i]);
|
||||
}
|
||||
|
||||
_update_reference_bone(p_index);
|
||||
settings[p_index]->joints_dirty = false;
|
||||
}
|
||||
|
||||
int BoneTwistDisperser3D::get_setting_count() const {
|
||||
return (int)settings.size();
|
||||
}
|
||||
|
||||
void BoneTwistDisperser3D::set_setting_count(int p_count) {
|
||||
ERR_FAIL_COND(p_count < 0);
|
||||
int delta = p_count - settings.size();
|
||||
if (delta < 0) {
|
||||
for (int i = delta; i < 0; i++) {
|
||||
memdelete(settings[settings.size() + i]);
|
||||
settings[settings.size() + i] = nullptr;
|
||||
}
|
||||
}
|
||||
settings.resize(p_count);
|
||||
delta++;
|
||||
if (delta > 1) {
|
||||
for (int i = 1; i < delta; i++) {
|
||||
settings[p_count - i] = memnew(BoneTwistDisperser3DSetting);
|
||||
}
|
||||
}
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
void BoneTwistDisperser3D::clear_settings() {
|
||||
set_setting_count(0);
|
||||
}
|
||||
|
||||
void BoneTwistDisperser3D::_process_modification(double p_delta) {
|
||||
Skeleton3D *skeleton = get_skeleton();
|
||||
if (!skeleton) {
|
||||
return;
|
||||
}
|
||||
for (BoneTwistDisperser3DSetting *setting : settings) {
|
||||
if (!setting || setting->reference_bone.bone < 0) {
|
||||
continue;
|
||||
}
|
||||
LocalVector<DisperseJointSetting> &joints = setting->joints;
|
||||
// Calc amount.
|
||||
int actual_joint_size = setting->extend_end_bone ? (int)joints.size() : (int)joints.size() - 1;
|
||||
if (actual_joint_size <= 1) {
|
||||
continue;
|
||||
}
|
||||
if (setting->disperse_mode == DISPERSE_MODE_EVEN) {
|
||||
double div = 1.0 / actual_joint_size;
|
||||
for (int i = 0; i < actual_joint_size; i++) {
|
||||
joints[i].amount = ((double)i + 1.0) * div;
|
||||
}
|
||||
} else if (setting->disperse_mode == DISPERSE_MODE_WEIGHTED) {
|
||||
// Assign length for each bone.
|
||||
double total_length = 0.0;
|
||||
double weight_sub = 1.0 - setting->weight_position;
|
||||
if (mutable_bone_axes) {
|
||||
for (int i = 0; i < actual_joint_size; i++) {
|
||||
double length = 0.0;
|
||||
if (i == 0) {
|
||||
length = skeleton->get_bone_pose_position(joints[i + 1].joint.bone).length() * setting->weight_position;
|
||||
} else if (i == actual_joint_size - 1) {
|
||||
length = skeleton->get_bone_pose_position(joints[i].joint.bone).length() * weight_sub;
|
||||
} else {
|
||||
length = skeleton->get_bone_pose_position(joints[i].joint.bone).length() * setting->weight_position + skeleton->get_bone_pose_position(joints[i + 1].joint.bone).length() * weight_sub;
|
||||
}
|
||||
total_length += length;
|
||||
joints[i].amount = total_length;
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < actual_joint_size; i++) {
|
||||
double length = 0.0;
|
||||
if (i == 0) {
|
||||
length = skeleton->get_bone_rest(joints[i + 1].joint.bone).origin.length() * setting->weight_position;
|
||||
} else if (i == actual_joint_size - 1) {
|
||||
length = skeleton->get_bone_rest(joints[i].joint.bone).origin.length() * weight_sub;
|
||||
} else {
|
||||
length = skeleton->get_bone_rest(joints[i].joint.bone).origin.length() * setting->weight_position + skeleton->get_bone_rest(joints[i + 1].joint.bone).origin.length() * weight_sub;
|
||||
}
|
||||
total_length += length;
|
||||
joints[i].amount = total_length;
|
||||
}
|
||||
}
|
||||
if (Math::is_zero_approx(total_length)) {
|
||||
continue;
|
||||
}
|
||||
// Normalize.
|
||||
double div = 1.0 / total_length;
|
||||
for (int i = 0; i < actual_joint_size; i++) {
|
||||
joints[i].amount *= div;
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < actual_joint_size; i++) {
|
||||
joints[i].amount = joints[i].custom_amount;
|
||||
}
|
||||
}
|
||||
int end = actual_joint_size - 1;
|
||||
joints[end].amount -= 1.0; // Remove twist from current pose.
|
||||
|
||||
// Retrieve axes.
|
||||
if (mutable_bone_axes) {
|
||||
for (int i = 0; i < end; i++) {
|
||||
joints[i].axis = skeleton->get_bone_pose_position(joints[i + 1].joint.bone).normalized();
|
||||
if (joints[i].axis.is_zero_approx() && i > 0) {
|
||||
joints[i].axis = joints[i - 1].axis;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < end; i++) {
|
||||
joints[i].axis = skeleton->get_bone_rest(joints[i + 1].joint.bone).origin.normalized();
|
||||
if (joints[i].axis.is_zero_approx() && i > 0) {
|
||||
joints[i].axis = joints[i - 1].axis;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!setting->extend_end_bone) {
|
||||
joints[end].axis = mutable_bone_axes ? skeleton->get_bone_pose_position(setting->end_bone.bone) : skeleton->get_bone_rest(setting->end_bone.bone).origin;
|
||||
joints[end].axis.normalize();
|
||||
} else if (setting->end_bone_direction == BONE_DIRECTION_FROM_PARENT) {
|
||||
joints[end].axis = skeleton->get_bone_rest(setting->end_bone.bone).basis.xform_inv(mutable_bone_axes ? skeleton->get_bone_pose_position(setting->end_bone.bone) : skeleton->get_bone_rest(setting->end_bone.bone).origin);
|
||||
joints[end].axis.normalize();
|
||||
} else {
|
||||
joints[end].axis = get_vector_from_bone_axis(static_cast<BoneAxis>((int)setting->end_bone_direction));
|
||||
}
|
||||
if (joints[end].axis.is_zero_approx() && end > 0) {
|
||||
joints[end].axis = joints[end - 1].axis;
|
||||
}
|
||||
|
||||
// Extract twist.
|
||||
Quaternion twist_rest = setting->twist_from_rest ? skeleton->get_bone_rest(setting->reference_bone.bone).basis.get_rotation_quaternion() : setting->twist_from.normalized();
|
||||
Quaternion ref_rot = twist_rest.inverse() * skeleton->get_bone_pose_rotation(setting->reference_bone.bone);
|
||||
ref_rot.normalize();
|
||||
double twist = get_roll_angle(ref_rot, joints[end].axis);
|
||||
|
||||
// Apply twist for each bone by their amount.
|
||||
// Twist parent, then cancel all twists caused by this modifier in child, and re-apply accumulated twist.
|
||||
Quaternion prev_rot;
|
||||
if (mutable_bone_axes) {
|
||||
for (int i = 0; i < actual_joint_size; i++) {
|
||||
int bn = joints[i].joint.bone;
|
||||
Quaternion cur_rot = Quaternion(joints[i].axis, twist * joints[i].amount);
|
||||
skeleton->set_bone_pose_rotation(bn, prev_rot.inverse() * skeleton->get_bone_pose_rotation(bn) * cur_rot);
|
||||
prev_rot = cur_rot;
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < actual_joint_size; i++) {
|
||||
int bn = joints[i].joint.bone;
|
||||
Quaternion cur_rot = Quaternion(joints[i].axis, twist * joints[i].amount);
|
||||
skeleton->set_bone_pose_rotation(bn, prev_rot.inverse() * skeleton->get_bone_pose_rotation(bn) * cur_rot);
|
||||
prev_rot = cur_rot;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BoneTwistDisperser3D::~BoneTwistDisperser3D() {
|
||||
clear_settings();
|
||||
}
|
||||
163
scene/3d/bone_twist_disperser_3d.h
Normal file
163
scene/3d/bone_twist_disperser_3d.h
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
/**************************************************************************/
|
||||
/* bone_twist_disperser_3d.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene/3d/skeleton_modifier_3d.h"
|
||||
|
||||
class BoneTwistDisperser3D : public SkeletonModifier3D {
|
||||
GDCLASS(BoneTwistDisperser3D, SkeletonModifier3D);
|
||||
|
||||
bool mutable_bone_axes = true;
|
||||
|
||||
public:
|
||||
enum DisperseMode {
|
||||
DISPERSE_MODE_EVEN,
|
||||
DISPERSE_MODE_WEIGHTED,
|
||||
DISPERSE_MODE_CUSTOM,
|
||||
};
|
||||
|
||||
struct BoneJoint {
|
||||
StringName name;
|
||||
int bone = -1;
|
||||
};
|
||||
|
||||
struct DisperseJointSetting {
|
||||
BoneJoint joint;
|
||||
double custom_amount = 1.0;
|
||||
// For processing.
|
||||
double amount = 1.0;
|
||||
Vector3 axis;
|
||||
};
|
||||
|
||||
struct BoneTwistDisperser3DSetting {
|
||||
bool joints_dirty = false;
|
||||
|
||||
DisperseMode disperse_mode = DISPERSE_MODE_EVEN;
|
||||
bool twist_from_rest = true;
|
||||
Quaternion twist_from;
|
||||
|
||||
BoneJoint root_bone;
|
||||
BoneJoint end_bone;
|
||||
LocalVector<DisperseJointSetting> joints;
|
||||
|
||||
bool extend_end_bone = false;
|
||||
BoneDirection end_bone_direction = BONE_DIRECTION_FROM_PARENT;
|
||||
|
||||
float weight_position = 0.5;
|
||||
Ref<Curve> damping_curve;
|
||||
|
||||
BoneJoint reference_bone; // To cache.
|
||||
|
||||
~BoneTwistDisperser3DSetting() {
|
||||
joints.clear();
|
||||
damping_curve.unref();
|
||||
}
|
||||
};
|
||||
|
||||
protected:
|
||||
LocalVector<BoneTwistDisperser3DSetting *> settings;
|
||||
|
||||
bool _get(const StringName &p_path, Variant &r_ret) const;
|
||||
bool _set(const StringName &p_path, const Variant &p_value);
|
||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||
void _validate_dynamic_prop(PropertyInfo &p_property) const;
|
||||
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
virtual void _set_active(bool p_active) override;
|
||||
virtual void _skeleton_changed(Skeleton3D *p_old, Skeleton3D *p_new) override;
|
||||
virtual void _validate_bone_names() override;
|
||||
|
||||
void _make_all_joints_dirty();
|
||||
|
||||
void _make_joints_dirty(int p_index);
|
||||
void _update_joints(int p_index);
|
||||
void _update_reference_bone(int p_index);
|
||||
void _update_curve(int p_index);
|
||||
|
||||
virtual void _process_modification(double p_delta) override;
|
||||
|
||||
public:
|
||||
void set_mutable_bone_axes(bool p_enabled);
|
||||
bool are_bone_axes_mutable() const;
|
||||
|
||||
int get_setting_count() const;
|
||||
void set_setting_count(int p_count);
|
||||
void clear_settings();
|
||||
|
||||
// Setting.
|
||||
void set_root_bone_name(int p_index, const String &p_bone_name);
|
||||
String get_root_bone_name(int p_index) const;
|
||||
void set_root_bone(int p_index, int p_bone);
|
||||
int get_root_bone(int p_index) const;
|
||||
|
||||
void set_end_bone_name(int p_index, const String &p_bone_name);
|
||||
String get_end_bone_name(int p_index) const;
|
||||
void set_end_bone(int p_index, int p_bone);
|
||||
int get_end_bone(int p_index) const;
|
||||
|
||||
void set_extend_end_bone(int p_index, bool p_enabled);
|
||||
bool is_end_bone_extended(int p_index) const;
|
||||
void set_end_bone_direction(int p_index, BoneDirection p_bone_direction);
|
||||
BoneDirection get_end_bone_direction(int p_index) const;
|
||||
|
||||
void set_twist_from_rest(int p_index, bool p_enabled);
|
||||
bool is_twist_from_rest(int p_index) const;
|
||||
void set_twist_from(int p_index, const Quaternion &p_from);
|
||||
Quaternion get_twist_from(int p_index) const;
|
||||
|
||||
String get_reference_bone_name(int p_index) const;
|
||||
int get_reference_bone(int p_index) const;
|
||||
|
||||
void set_disperse_mode(int p_index, DisperseMode p_disperse_mode);
|
||||
DisperseMode get_disperse_mode(int p_index) const;
|
||||
void set_weight_position(int p_index, float p_position);
|
||||
float get_weight_position(int p_index) const;
|
||||
void set_damping_curve(int p_index, const Ref<Curve> &p_damping_curve);
|
||||
Ref<Curve> get_damping_curve(int p_index) const;
|
||||
|
||||
// Individual joints.
|
||||
void set_joint_bone_name(int p_index, int p_joint, const String &p_bone_name);
|
||||
String get_joint_bone_name(int p_index, int p_joint) const;
|
||||
void set_joint_bone(int p_index, int p_joint, int p_bone);
|
||||
int get_joint_bone(int p_index, int p_joint) const;
|
||||
|
||||
void set_joint_twist_amount(int p_index, int p_joint, float p_amount);
|
||||
float get_joint_twist_amount(int p_index, int p_joint) const;
|
||||
|
||||
void set_joint_count(int p_index, int p_count);
|
||||
int get_joint_count(int p_index) const;
|
||||
|
||||
~BoneTwistDisperser3D();
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(BoneTwistDisperser3D::DisperseMode);
|
||||
|
|
@ -220,6 +220,7 @@
|
|||
#include "scene/3d/audio_stream_player_3d.h"
|
||||
#include "scene/3d/bone_attachment_3d.h"
|
||||
#include "scene/3d/bone_constraint_3d.h"
|
||||
#include "scene/3d/bone_twist_disperser_3d.h"
|
||||
#include "scene/3d/camera_3d.h"
|
||||
#include "scene/3d/ccd_ik_3d.h"
|
||||
#include "scene/3d/chain_ik_3d.h"
|
||||
|
|
@ -684,6 +685,7 @@ void register_scene_types() {
|
|||
GDREGISTER_CLASS(CCDIK3D);
|
||||
GDREGISTER_CLASS(JacobianIK3D);
|
||||
GDREGISTER_CLASS(LimitAngularVelocityModifier3D);
|
||||
GDREGISTER_CLASS(BoneTwistDisperser3D);
|
||||
|
||||
#ifndef XR_DISABLED
|
||||
GDREGISTER_CLASS(XRCamera3D);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue