Merge pull request #100984 from TokageItLab/bone-constraint
Implement `BoneConstraint3D` with `CopyTransform`/`ConvertTransform`/`Aim` Modifiers
This commit is contained in:
commit
5935356962
80
doc/classes/AimModifier3D.xml
Normal file
80
doc/classes/AimModifier3D.xml
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<class name="AimModifier3D" inherits="BoneConstraint3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
|
||||||
|
<brief_description>
|
||||||
|
The [AimModifier3D] rotates a bone to look at a reference bone.
|
||||||
|
</brief_description>
|
||||||
|
<description>
|
||||||
|
This is a simple version of [LookAtModifier3D] that only allows bone to the reference without advanced options such as angle limitation or time-based interpolation.
|
||||||
|
The feature is simplified, but instead it is implemented with smooth tracking without euler, see [method set_use_euler].
|
||||||
|
</description>
|
||||||
|
<tutorials>
|
||||||
|
</tutorials>
|
||||||
|
<methods>
|
||||||
|
<method name="get_forward_axis" qualifiers="const">
|
||||||
|
<return type="int" enum="SkeletonModifier3D.BoneAxis" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<description>
|
||||||
|
Returns the forward axis of the bone.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="get_primary_rotation_axis" qualifiers="const">
|
||||||
|
<return type="int" enum="Vector3.Axis" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<description>
|
||||||
|
Returns the axis of the first rotation. It is enabled only if [method is_using_euler] is [code]true[/code].
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="is_using_euler" qualifiers="const">
|
||||||
|
<return type="bool" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<description>
|
||||||
|
Returns [code]true[/code] if it provides rotation with using euler.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="is_using_secondary_rotation" qualifiers="const">
|
||||||
|
<return type="bool" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<description>
|
||||||
|
Returns [code]true[/code] if it provides rotation by two axes. It is enabled only if [method is_using_euler] is [code]true[/code].
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="set_forward_axis">
|
||||||
|
<return type="void" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<param index="1" name="axis" type="int" enum="SkeletonModifier3D.BoneAxis" />
|
||||||
|
<description>
|
||||||
|
Sets the forward axis of the bone.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="set_primary_rotation_axis">
|
||||||
|
<return type="void" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<param index="1" name="axis" type="int" enum="Vector3.Axis" />
|
||||||
|
<description>
|
||||||
|
Sets the axis of the first rotation. It is enabled only if [method is_using_euler] is [code]true[/code].
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="set_use_euler">
|
||||||
|
<return type="void" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<param index="1" name="enabled" type="bool" />
|
||||||
|
<description>
|
||||||
|
If sets [param enabled] to [code]true[/code], it provides rotation with using euler.
|
||||||
|
If sets [param enabled] to [code]false[/code], it provides rotation with using rotation by arc generated from the forward axis vector and the vector toward the reference.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="set_use_secondary_rotation">
|
||||||
|
<return type="void" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<param index="1" name="enabled" type="bool" />
|
||||||
|
<description>
|
||||||
|
If sets [param enabled] to [code]true[/code], it provides rotation by two axes. It is enabled only if [method is_using_euler] is [code]true[/code].
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
</methods>
|
||||||
|
<members>
|
||||||
|
<member name="setting_count" type="int" setter="set_setting_count" getter="get_setting_count" default="0">
|
||||||
|
The number of settings in the modifier.
|
||||||
|
</member>
|
||||||
|
</members>
|
||||||
|
</class>
|
111
doc/classes/BoneConstraint3D.xml
Normal file
111
doc/classes/BoneConstraint3D.xml
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<class name="BoneConstraint3D" inherits="SkeletonModifier3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
|
||||||
|
<brief_description>
|
||||||
|
A node that may modify Skeleton3D's bone with associating the two bones.
|
||||||
|
</brief_description>
|
||||||
|
<description>
|
||||||
|
Base class of [SkeletonModifier3D] that modifies the bone set in [method set_apply_bone] based on the transform of the bone retrieved by [method get_reference_bone].
|
||||||
|
</description>
|
||||||
|
<tutorials>
|
||||||
|
</tutorials>
|
||||||
|
<methods>
|
||||||
|
<method name="clear_setting">
|
||||||
|
<return type="void" />
|
||||||
|
<description>
|
||||||
|
Clear all settings.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="get_amount" qualifiers="const">
|
||||||
|
<return type="float" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<description>
|
||||||
|
Returns the apply amount of the setting at [param index].
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="get_apply_bone" qualifiers="const">
|
||||||
|
<return type="int" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<description>
|
||||||
|
Returns the apply bone of the setting at [param index]. This bone will be modified.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="get_apply_bone_name" qualifiers="const">
|
||||||
|
<return type="String" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<description>
|
||||||
|
Returns the apply bone name of the setting at [param index]. This bone will be modified.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="get_reference_bone" qualifiers="const">
|
||||||
|
<return type="int" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<description>
|
||||||
|
Returns the reference bone of the setting at [param index].
|
||||||
|
This bone will be only referenced and not modified by this modifier.
|
||||||
|
</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 of the setting at [param index].
|
||||||
|
This bone will be only referenced and not modified by this modifier.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="get_setting_count" qualifiers="const">
|
||||||
|
<return type="int" />
|
||||||
|
<description>
|
||||||
|
Returns the number of settings in the modifier.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="set_amount">
|
||||||
|
<return type="void" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<param index="1" name="amount" type="float" />
|
||||||
|
<description>
|
||||||
|
Sets the apply amount of the setting at [param index] to [param amount].
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="set_apply_bone">
|
||||||
|
<return type="void" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<param index="1" name="bone" type="int" />
|
||||||
|
<description>
|
||||||
|
Sets the apply bone of the setting at [param index] to [param bone]. This bone will be modified.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="set_apply_bone_name">
|
||||||
|
<return type="void" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<param index="1" name="bone_name" type="String" />
|
||||||
|
<description>
|
||||||
|
Sets the apply bone of the setting at [param index] to [param bone_name]. This bone will be modified.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="set_reference_bone">
|
||||||
|
<return type="void" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<param index="1" name="bone" type="int" />
|
||||||
|
<description>
|
||||||
|
Sets the reference bone of the setting at [param index] to [param bone].
|
||||||
|
This bone will be only referenced and not modified by this modifier.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="set_reference_bone_name">
|
||||||
|
<return type="void" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<param index="1" name="bone_name" type="String" />
|
||||||
|
<description>
|
||||||
|
Sets the reference bone of the setting at [param index] to [param bone_name].
|
||||||
|
This bone will be only referenced and not modified by this modifier.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="set_setting_count">
|
||||||
|
<return type="void" />
|
||||||
|
<param index="0" name="count" type="int" />
|
||||||
|
<description>
|
||||||
|
Sets the number of settings in the modifier.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
</methods>
|
||||||
|
</class>
|
192
doc/classes/ConvertTransformModifier3D.xml
Normal file
192
doc/classes/ConvertTransformModifier3D.xml
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<class name="ConvertTransformModifier3D" inherits="BoneConstraint3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
|
||||||
|
<brief_description>
|
||||||
|
A [SkeletonModifier3D] that apply transform to the bone which converted from reference.
|
||||||
|
</brief_description>
|
||||||
|
<description>
|
||||||
|
Apply the copied transform of the bone set by [method BoneConstraint3D.set_reference_bone] to the bone set by [method BoneConstraint3D.set_apply_bone] about the specific axis with remapping it with some options.
|
||||||
|
There are 4 ways to apply the transform, depending on the combination of [method set_relative] and [method set_additive].
|
||||||
|
[b]Relative + Additive:[/b]
|
||||||
|
- Extract reference pose relative to the rest and add it to the apply bone's pose.
|
||||||
|
[b]Relative + Not Additive:[/b]
|
||||||
|
- Extract reference pose relative to the rest and add it to the apply bone's rest.
|
||||||
|
[b]Not Relative + Additive:[/b]
|
||||||
|
- Extract reference pose absolutely and add it to the apply bone's pose.
|
||||||
|
[b]Not Relative + Not Additive:[/b]
|
||||||
|
- Extract reference pose absolutely and the apply bone's pose is replaced with it.
|
||||||
|
</description>
|
||||||
|
<tutorials>
|
||||||
|
</tutorials>
|
||||||
|
<methods>
|
||||||
|
<method name="get_apply_axis" qualifiers="const">
|
||||||
|
<return type="int" enum="Vector3.Axis" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<description>
|
||||||
|
Returns the axis of the remapping destination transform.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="get_apply_range_max" qualifiers="const">
|
||||||
|
<return type="float" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<description>
|
||||||
|
Returns the maximum value of the remapping destination range.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="get_apply_range_min" qualifiers="const">
|
||||||
|
<return type="float" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<description>
|
||||||
|
Returns the minimum value of the remapping destination range.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="get_apply_transform_mode" qualifiers="const">
|
||||||
|
<return type="int" enum="ConvertTransformModifier3D.TransformMode" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<description>
|
||||||
|
Returns the operation of the remapping destination transform.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="get_reference_axis" qualifiers="const">
|
||||||
|
<return type="int" enum="Vector3.Axis" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<description>
|
||||||
|
Returns the axis of the remapping source transform.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="get_reference_range_max" qualifiers="const">
|
||||||
|
<return type="float" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<description>
|
||||||
|
Returns the maximum value of the remapping source range.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="get_reference_range_min" qualifiers="const">
|
||||||
|
<return type="float" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<description>
|
||||||
|
Returns the minimum value of the remapping source range.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="get_reference_transform_mode" qualifiers="const">
|
||||||
|
<return type="int" enum="ConvertTransformModifier3D.TransformMode" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<description>
|
||||||
|
Returns the operation of the remapping source transform.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="is_additive" qualifiers="const">
|
||||||
|
<return type="bool" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<description>
|
||||||
|
Returns [code]true[/code] if the additive option is enabled in the setting at [param index].
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="is_relative" qualifiers="const">
|
||||||
|
<return type="bool" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<description>
|
||||||
|
Returns [code]true[/code] if the relative option is enabled in the setting at [param index].
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="set_additive">
|
||||||
|
<return type="void" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<param index="1" name="enabled" type="bool" />
|
||||||
|
<description>
|
||||||
|
Sets additive option in the setting at [param index] to [param enabled]. This mainly affects the process of applying transform to the [method BoneConstraint3D.set_apply_bone].
|
||||||
|
If sets [param enabled] to [code]true[/code], the processed transform is added to the pose of the current apply bone.
|
||||||
|
If sets [param enabled] to [code]false[/code], the pose of the current apply bone is replaced with the processed transform. However, if set [method set_relative] to [code]true[/code], the transform is relative to rest.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="set_apply_axis">
|
||||||
|
<return type="void" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<param index="1" name="axis" type="int" enum="Vector3.Axis" />
|
||||||
|
<description>
|
||||||
|
Sets the axis of the remapping destination transform.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="set_apply_range_max">
|
||||||
|
<return type="void" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<param index="1" name="range_max" type="float" />
|
||||||
|
<description>
|
||||||
|
Sets the maximum value of the remapping destination range.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="set_apply_range_min">
|
||||||
|
<return type="void" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<param index="1" name="range_min" type="float" />
|
||||||
|
<description>
|
||||||
|
Sets the minimum value of the remapping destination range.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="set_apply_transform_mode">
|
||||||
|
<return type="void" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<param index="1" name="transform_mode" type="int" enum="ConvertTransformModifier3D.TransformMode" />
|
||||||
|
<description>
|
||||||
|
Sets the operation of the remapping destination transform.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="set_reference_axis">
|
||||||
|
<return type="void" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<param index="1" name="axis" type="int" enum="Vector3.Axis" />
|
||||||
|
<description>
|
||||||
|
Sets the axis of the remapping source transform.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="set_reference_range_max">
|
||||||
|
<return type="void" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<param index="1" name="range_max" type="float" />
|
||||||
|
<description>
|
||||||
|
Sets the maximum value of the remapping source range.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="set_reference_range_min">
|
||||||
|
<return type="void" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<param index="1" name="range_min" type="float" />
|
||||||
|
<description>
|
||||||
|
Sets the minimum value of the remapping source range.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="set_reference_transform_mode">
|
||||||
|
<return type="void" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<param index="1" name="transform_mode" type="int" enum="ConvertTransformModifier3D.TransformMode" />
|
||||||
|
<description>
|
||||||
|
Sets the operation of the remapping source transform.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="set_relative">
|
||||||
|
<return type="void" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<param index="1" name="enabled" type="bool" />
|
||||||
|
<description>
|
||||||
|
Sets relative option in the setting at [param index] to [param enabled].
|
||||||
|
If sets [param enabled] to [code]true[/code], the extracted and applying transform is relative to the rest.
|
||||||
|
If sets [param enabled] to [code]false[/code], the extracted transform is absolute.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
</methods>
|
||||||
|
<members>
|
||||||
|
<member name="setting_count" type="int" setter="set_setting_count" getter="get_setting_count" default="0">
|
||||||
|
The number of settings in the modifier.
|
||||||
|
</member>
|
||||||
|
</members>
|
||||||
|
<constants>
|
||||||
|
<constant name="TRANSFORM_MODE_POSITION" value="0" enum="TransformMode">
|
||||||
|
Convert with position. Transfer the difference.
|
||||||
|
</constant>
|
||||||
|
<constant name="TRANSFORM_MODE_ROTATION" value="1" enum="TransformMode">
|
||||||
|
Convert with rotation. The angle is the roll for the specified axis.
|
||||||
|
</constant>
|
||||||
|
<constant name="TRANSFORM_MODE_SCALE" value="2" enum="TransformMode">
|
||||||
|
Convert with scale. Transfers the ratio, not the difference.
|
||||||
|
</constant>
|
||||||
|
</constants>
|
||||||
|
</class>
|
270
doc/classes/CopyTransformModifier3D.xml
Normal file
270
doc/classes/CopyTransformModifier3D.xml
Normal file
@ -0,0 +1,270 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<class name="CopyTransformModifier3D" inherits="BoneConstraint3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
|
||||||
|
<brief_description>
|
||||||
|
A [SkeletonModifier3D] that apply transform to the bone which copied from reference.
|
||||||
|
</brief_description>
|
||||||
|
<description>
|
||||||
|
Apply the copied transform of the bone set by [method BoneConstraint3D.set_reference_bone] to the bone set by [method BoneConstraint3D.set_apply_bone] with processing it with some masks and options.
|
||||||
|
There are 4 ways to apply the transform, depending on the combination of [method set_relative] and [method set_additive].
|
||||||
|
[b]Relative + Additive:[/b]
|
||||||
|
- Extract reference pose relative to the rest and add it to the apply bone's pose.
|
||||||
|
[b]Relative + Not Additive:[/b]
|
||||||
|
- Extract reference pose relative to the rest and add it to the apply bone's rest.
|
||||||
|
[b]Not Relative + Additive:[/b]
|
||||||
|
- Extract reference pose absolutely and add it to the apply bone's pose.
|
||||||
|
[b]Not Relative + Not Additive:[/b]
|
||||||
|
- Extract reference pose absolutely and the apply bone's pose is replaced with it.
|
||||||
|
</description>
|
||||||
|
<tutorials>
|
||||||
|
</tutorials>
|
||||||
|
<methods>
|
||||||
|
<method name="get_axis_flags" qualifiers="const">
|
||||||
|
<return type="int" enum="CopyTransformModifier3D.AxisFlag" is_bitfield="true" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<description>
|
||||||
|
Returns the axis flags of the setting at [param index].
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="get_copy_flags" qualifiers="const">
|
||||||
|
<return type="int" enum="CopyTransformModifier3D.TransformFlag" is_bitfield="true" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<description>
|
||||||
|
Returns the copy flags of the setting at [param index].
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="get_invert_flags" qualifiers="const">
|
||||||
|
<return type="int" enum="CopyTransformModifier3D.AxisFlag" is_bitfield="true" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<description>
|
||||||
|
Returns the invert flags of the setting at [param index].
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="is_additive" qualifiers="const">
|
||||||
|
<return type="bool" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<description>
|
||||||
|
Returns [code]true[/code] if the additive option is enabled in the setting at [param index].
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="is_axis_x_enabled" qualifiers="const">
|
||||||
|
<return type="bool" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<description>
|
||||||
|
Returns [code]true[/code] if the enable flags has the flag for the X-axis in the setting at [param index]. See also [method set_axis_flags].
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="is_axis_x_inverted" qualifiers="const">
|
||||||
|
<return type="bool" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<description>
|
||||||
|
Returns [code]true[/code] if the invert flags has the flag for the X-axis in the setting at [param index]. See also [method set_invert_flags].
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="is_axis_y_enabled" qualifiers="const">
|
||||||
|
<return type="bool" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<description>
|
||||||
|
Returns [code]true[/code] if the enable flags has the flag for the Y-axis in the setting at [param index]. See also [method set_axis_flags].
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="is_axis_y_inverted" qualifiers="const">
|
||||||
|
<return type="bool" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<description>
|
||||||
|
Returns [code]true[/code] if the invert flags has the flag for the Y-axis in the setting at [param index]. See also [method set_invert_flags].
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="is_axis_z_enabled" qualifiers="const">
|
||||||
|
<return type="bool" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<description>
|
||||||
|
Returns [code]true[/code] if the enable flags has the flag for the Z-axis in the setting at [param index]. See also [method set_axis_flags].
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="is_axis_z_inverted" qualifiers="const">
|
||||||
|
<return type="bool" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<description>
|
||||||
|
Returns [code]true[/code] if the invert flags has the flag for the Z-axis in the setting at [param index]. See also [method set_invert_flags].
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="is_position_copying" qualifiers="const">
|
||||||
|
<return type="bool" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<description>
|
||||||
|
Returns [code]true[/code] if the copy flags has the flag for the position in the setting at [param index]. See also [method set_copy_flags].
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="is_relative" qualifiers="const">
|
||||||
|
<return type="bool" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<description>
|
||||||
|
Returns [code]true[/code] if the relative option is enabled in the setting at [param index].
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="is_rotation_copying" qualifiers="const">
|
||||||
|
<return type="bool" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<description>
|
||||||
|
Returns [code]true[/code] if the copy flags has the flag for the rotation in the setting at [param index]. See also [method set_copy_flags].
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="is_scale_copying" qualifiers="const">
|
||||||
|
<return type="bool" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<description>
|
||||||
|
Returns [code]true[/code] if the copy flags has the flag for the scale in the setting at [param index]. See also [method set_copy_flags].
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="set_additive">
|
||||||
|
<return type="void" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<param index="1" name="enabled" type="bool" />
|
||||||
|
<description>
|
||||||
|
Sets additive option in the setting at [param index] to [param enabled]. This mainly affects the process of applying transform to the [method BoneConstraint3D.set_apply_bone].
|
||||||
|
If sets [param enabled] to [code]true[/code], the processed transform is added to the pose of the current apply bone.
|
||||||
|
If sets [param enabled] to [code]false[/code], the pose of the current apply bone is replaced with the processed transform. However, if set [method set_relative] to [code]true[/code], the transform is relative to rest.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="set_axis_flags">
|
||||||
|
<return type="void" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<param index="1" name="axis_flags" type="int" enum="CopyTransformModifier3D.AxisFlag" is_bitfield="true" />
|
||||||
|
<description>
|
||||||
|
Sets the flags to copy axes. If the flag is valid, the axis is copied.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="set_axis_x_enabled">
|
||||||
|
<return type="void" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<param index="1" name="enabled" type="bool" />
|
||||||
|
<description>
|
||||||
|
If sets [param enabled] to [code]true[/code], the X-axis will be copied.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="set_axis_x_inverted">
|
||||||
|
<return type="void" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<param index="1" name="enabled" type="bool" />
|
||||||
|
<description>
|
||||||
|
If sets [param enabled] to [code]true[/code], the X-axis will be inverted.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="set_axis_y_enabled">
|
||||||
|
<return type="void" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<param index="1" name="enabled" type="bool" />
|
||||||
|
<description>
|
||||||
|
If sets [param enabled] to [code]true[/code], the Y-axis will be copied.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="set_axis_y_inverted">
|
||||||
|
<return type="void" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<param index="1" name="enabled" type="bool" />
|
||||||
|
<description>
|
||||||
|
If sets [param enabled] to [code]true[/code], the Y-axis will be inverted.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="set_axis_z_enabled">
|
||||||
|
<return type="void" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<param index="1" name="enabled" type="bool" />
|
||||||
|
<description>
|
||||||
|
If sets [param enabled] to [code]true[/code], the Z-axis will be copied.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="set_axis_z_inverted">
|
||||||
|
<return type="void" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<param index="1" name="enabled" type="bool" />
|
||||||
|
<description>
|
||||||
|
If sets [param enabled] to [code]true[/code], the Z-axis will be inverted.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="set_copy_flags">
|
||||||
|
<return type="void" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<param index="1" name="copy_flags" type="int" enum="CopyTransformModifier3D.TransformFlag" is_bitfield="true" />
|
||||||
|
<description>
|
||||||
|
Sets the flags to process the transform operations. If the flag is valid, the transform operation is processed.
|
||||||
|
[b]Note:[/b] If the rotation is valid for only one axis, it respects the roll of the valid axis. If the rotation is valid for two axes, it discards the roll of the invalid axis.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="set_copy_position">
|
||||||
|
<return type="void" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<param index="1" name="enabled" type="bool" />
|
||||||
|
<description>
|
||||||
|
If sets [param enabled] to [code]true[/code], the position will be copied.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="set_copy_rotation">
|
||||||
|
<return type="void" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<param index="1" name="enabled" type="bool" />
|
||||||
|
<description>
|
||||||
|
If sets [param enabled] to [code]true[/code], the rotation will be copied.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="set_copy_scale">
|
||||||
|
<return type="void" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<param index="1" name="enabled" type="bool" />
|
||||||
|
<description>
|
||||||
|
If sets [param enabled] to [code]true[/code], the scale will be copied.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="set_invert_flags">
|
||||||
|
<return type="void" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<param index="1" name="axis_flags" type="int" enum="CopyTransformModifier3D.AxisFlag" is_bitfield="true" />
|
||||||
|
<description>
|
||||||
|
Sets the flags to inverte axes. If the flag is valid, the axis is copied.
|
||||||
|
[b]Note:[/b] An inverted scale means an inverse number, not a negative scale. For example, inverting [code]2.0[/code] means [code]0.5[/code].
|
||||||
|
[b]Note:[/b] An inverted rotation flips the elements of the quaternion. For example, a two-axis inversion will flip the roll of each axis, and a three-axis inversion will flip the final orientation. However, be aware that flipping only one axis may cause unintended rotation by the unflipped axes, due to the characteristics of the quaternion.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="set_relative">
|
||||||
|
<return type="void" />
|
||||||
|
<param index="0" name="index" type="int" />
|
||||||
|
<param index="1" name="enabled" type="bool" />
|
||||||
|
<description>
|
||||||
|
Sets relative option in the setting at [param index] to [param enabled].
|
||||||
|
If sets [param enabled] to [code]true[/code], the extracted and applying transform is relative to the rest.
|
||||||
|
If sets [param enabled] to [code]false[/code], the extracted transform is absolute.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
</methods>
|
||||||
|
<members>
|
||||||
|
<member name="setting_count" type="int" setter="set_setting_count" getter="get_setting_count" default="0">
|
||||||
|
The number of settings in the modifier.
|
||||||
|
</member>
|
||||||
|
</members>
|
||||||
|
<constants>
|
||||||
|
<constant name="TRANSFORM_FLAG_POSITION" value="1" enum="TransformFlag" is_bitfield="true">
|
||||||
|
If set, allows to copy the position.
|
||||||
|
</constant>
|
||||||
|
<constant name="TRANSFORM_FLAG_ROTATION" value="2" enum="TransformFlag" is_bitfield="true">
|
||||||
|
If set, allows to copy the rotation.
|
||||||
|
</constant>
|
||||||
|
<constant name="TRANSFORM_FLAG_SCALE" value="4" enum="TransformFlag" is_bitfield="true">
|
||||||
|
If set, allows to copy the scale.
|
||||||
|
</constant>
|
||||||
|
<constant name="TRANSFORM_FLAG_ALL" value="7" enum="TransformFlag" is_bitfield="true">
|
||||||
|
If set, allows to copy the position/rotation/scale.
|
||||||
|
</constant>
|
||||||
|
<constant name="AXIS_FLAG_X" value="1" enum="AxisFlag" is_bitfield="true">
|
||||||
|
If set, allows to process the X-axis.
|
||||||
|
</constant>
|
||||||
|
<constant name="AXIS_FLAG_Y" value="2" enum="AxisFlag" is_bitfield="true">
|
||||||
|
If set, allows to process the Y-axis.
|
||||||
|
</constant>
|
||||||
|
<constant name="AXIS_FLAG_Z" value="4" enum="AxisFlag" is_bitfield="true">
|
||||||
|
If set, allows to process the Z-axis.
|
||||||
|
</constant>
|
||||||
|
<constant name="AXIS_FLAG_ALL" value="7" enum="AxisFlag" is_bitfield="true">
|
||||||
|
If set, allows to process the all axes.
|
||||||
|
</constant>
|
||||||
|
</constants>
|
||||||
|
</class>
|
1
editor/icons/AimModifier3D.svg
Normal file
1
editor/icons/AimModifier3D.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><g fill="#fc7f7f"><path d="M2.55 9.942c.61-.92 1.634-1.469 2.739-1.469.363 0 .72.06 1.059.175l2.301-2.302c-.067-.197-.115-.4-.143-.606-.248-1.787.996-3.44 2.777-3.702-.086-.022-.169-.053-.256-.065-.175-1.251-1.329-2.124-2.581-1.95-1.251.173-2.124 1.328-1.95 2.58.047.34.17.665.36.952l-3.301 3.3c-1.052-.699-2.472-.414-3.171.64-.7 1.052-.414 2.47.638 3.17.286.189.611.313.952.361.014.101.044.194.07.289.063-.476.222-.946.506-1.373zM12.208 9.5c-1.386 0-3.138 1.056-3.767 3.092-.027.096-.027.198 0 .296.604 2.145 2.415 3.112 3.767 3.112 1.355 0 3.161-.967 3.771-3.1.029-.098.029-.201 0-.301-.596-2.049-2.387-3.099-3.771-3.099zm0 5.419c-1.195 0-2.164-.974-2.164-2.169s.969-2.167 2.164-2.167c1.198 0 2.168.972 2.168 2.167s-.97 2.169-2.168 2.169z"/><circle cx="12.208" cy="12.75" r="1.081"/><path d="m7.478 13.157c-.077-.276-.077-.566.007-.86.771-2.493 2.959-3.797 4.723-3.797.806 0 1.688.277 2.487.794.36-.173.685-.434.922-.79.699-1.051.414-2.471-.638-3.17-.287-.19-.613-.313-.952-.361-.174-1.251-1.329-2.125-2.581-1.951-1.251.174-2.124 1.329-1.95 2.581.047.34.17.666.36.952l-3.301 3.301c-1.053-.699-2.473-.415-3.172.639-.699 1.052-.414 2.471.639 3.171.285.189.61.313.951.361.174 1.251 1.33 2.124 2.581 1.95.461-.064.87-.264 1.195-.55-.551-.585-1.007-1.332-1.271-2.27z"/></g></svg>
|
After Width: | Height: | Size: 1.3 KiB |
1
editor/icons/BoneConstraint3D.svg
Normal file
1
editor/icons/BoneConstraint3D.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><g fill="#b56d6d"><path d="M2.55 9.942c.61-.92 1.634-1.469 2.739-1.469.363 0 .72.06 1.059.175l2.301-2.302c-.067-.197-.115-.4-.143-.606-.248-1.787.996-3.44 2.777-3.702-.086-.022-.169-.053-.256-.065-.175-1.251-1.329-2.124-2.581-1.95-1.251.173-2.124 1.328-1.95 2.58.047.34.17.665.36.952l-3.301 3.3c-1.052-.699-2.472-.414-3.171.64-.7 1.052-.414 2.47.638 3.17.286.189.611.313.952.361.014.101.044.194.07.289.063-.476.222-.946.506-1.373zM11.87 10.784c.308-.043.62.01.896.153l.927-.927c.357-.359.938-.359 1.298 0 .358.357.358.938 0 1.298l-1.483 1.482c-.271.308-.74.337-1.047.065-.023-.021-.045-.042-.065-.065-.147-.142-.382-.138-.524.009-.139.144-.139.372 0 .516.561.597 1.5.627 2.097.064.021-.021.043-.042.064-.064l1.483-1.482c.647-.648.647-1.699 0-2.347-.648-.648-1.699-.648-2.347 0z"/><path d="m13.077 14.216c-.307.044-.62-.01-.895-.154l-.928.927c-.358.36-.938.36-1.299 0-.357-.357-.357-.938 0-1.297l1.484-1.483c.271-.307.739-.337 1.048-.065.021.021.044.042.064.065.147.143.382.139.524-.009.139-.144.139-.371 0-.516-.561-.597-1.499-.625-2.097-.063-.023.02-.043.042-.064.063l-1.483 1.483c-.647.648-.647 1.698 0 2.346.648.649 1.699.649 2.347 0z"/><path d="m8.482 12.71c.088-.116.188-.224.296-.32l3.611-3.611c.836-.932 2.246-1.028 3.21-.253.006-.008.013-.014.018-.021.699-1.051.414-2.471-.638-3.17-.287-.19-.613-.313-.952-.361-.174-1.251-1.329-2.125-2.581-1.951-1.251.174-2.124 1.329-1.95 2.581.047.34.17.666.36.952l-3.301 3.3c-1.053-.699-2.473-.415-3.172.639-.699 1.052-.414 2.471.639 3.171.285.189.61.313.951.361.174 1.251 1.33 2.124 2.581 1.95.361-.05.685-.189.967-.381-.659-.822-.707-2.011-.039-2.886z"/></g></svg>
|
After Width: | Height: | Size: 1.6 KiB |
1
editor/icons/ConvertTransformModifier3D.svg
Normal file
1
editor/icons/ConvertTransformModifier3D.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><g fill="#fc7f7f"><path d="M2.55 9.942c.61-.92 1.634-1.469 2.739-1.469.363 0 .72.06 1.059.175l2.301-2.302c-.067-.197-.115-.4-.143-.606-.248-1.787.996-3.44 2.777-3.702-.086-.022-.169-.053-.256-.065-.175-1.251-1.329-2.124-2.581-1.95-1.251.173-2.124 1.328-1.95 2.58.047.34.17.665.36.952l-3.301 3.3c-1.052-.699-2.472-.414-3.171.64-.7 1.052-.414 2.47.638 3.17.286.189.611.313.952.361.014.101.044.194.07.289.063-.476.222-.946.506-1.373zM12.5 9v1h-1.001c-1.38 0-2.499 1.119-2.499 2.5 0 .542.176 1.068.501 1.5l.706-.707c-.438-.702-.224-1.629.479-2.067.245-.151.525-.229.813-.226h1.001v.999l2-1.499zm2.999 2-.706.707c.438.702.224 1.629-.479 2.068-.246.15-.525.229-.813.225h-1.001v-.999l-2 1.499 2 1.5v-1.002h1.002c1.381 0 2.498-1.117 2.498-2.498 0-.542-.174-1.067-.501-1.5z"/><path d="m8.702 14.602c-.452-.601-.702-1.348-.702-2.102 0-1.93 1.569-3.5 3.499-3.5h.001c0-.379.214-.725.553-.895.141-.07.295-.105.447-.105.212 0 .424.068.6.2l1.515 1.136c.395-.169.747-.448 1.003-.832.699-1.051.414-2.471-.638-3.17-.287-.19-.613-.313-.952-.361-.174-1.251-1.329-2.125-2.581-1.951-1.251.174-2.124 1.329-1.95 2.581.047.34.17.666.36.952l-3.302 3.301c-1.053-.699-2.473-.415-3.172.639-.699 1.052-.414 2.471.639 3.171.285.189.61.313.951.361.174 1.251 1.33 2.124 2.581 1.95.697-.097 1.268-.504 1.614-1.057-.181-.065-.348-.162-.466-.318z"/></g></svg>
|
After Width: | Height: | Size: 1.4 KiB |
1
editor/icons/CopyTransformModifier3D.svg
Normal file
1
editor/icons/CopyTransformModifier3D.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><g fill="#fc7f7f"><path d="M2.55 9.942c.61-.92 1.634-1.469 2.739-1.469.363 0 .72.06 1.059.175l2.301-2.302c-.067-.197-.115-.4-.143-.606-.248-1.787.996-3.44 2.777-3.702-.086-.022-.169-.053-.256-.065-.175-1.251-1.329-2.124-2.581-1.95-1.251.173-2.124 1.328-1.95 2.58.047.34.17.665.36.952l-3.301 3.3c-1.052-.699-2.472-.414-3.171.64-.7 1.052-.414 2.47.638 3.17.286.189.611.313.952.361.014.101.044.194.07.289.063-.476.222-.946.506-1.373zM10.142 9.146l-1.001 1.002c-.191.199-.186.516.012.709.194.186.503.186.695 0l.147-.147v4.298h4.295l-.146.144c-.191.2-.186.518.014.709.194.188.501.188.693 0l1.003-1.002c.195-.196.195-.511 0-.707l-1.003-1c-.197-.194-.514-.189-.707.012-.187.193-.187.501 0 .696l.146.146h-2.587l2.795-2.797v.209c0 .276.224.5.5.5.275 0 .5-.224.5-.5v-1.418c0-.275-.225-.499-.5-.499h-1.417c-.275 0-.499.224-.499.499 0 .277.224.501.499.501h.209l-2.796 2.794v-2.587l.147.147c.199.19.516.187.708-.013.188-.196.188-.501 0-.695l-1-1.003c-.196-.193-.512-.193-.707.002z"/><path d="m8.995 15.007v-3.099c-.198-.071-.383-.185-.543-.338-.586-.575-.601-1.523-.019-2.13l1.003-1.004c.284-.281.66-.436 1.06-.436.397 0 .772.154 1.062.439l.758.76c.266-.419.733-.697 1.266-.697h1.417c.189 0 .369.039.536.104.026-.035.059-.063.083-.101.699-1.051.414-2.471-.638-3.17-.287-.19-.613-.313-.952-.361-.174-1.251-1.329-2.125-2.581-1.951-1.251.174-2.124 1.329-1.95 2.581.047.34.17.666.36.952l-3.302 3.3c-1.053-.699-2.473-.415-3.172.639-.699 1.052-.414 2.471.639 3.171.285.189.61.313.951.361.174 1.251 1.33 2.124 2.581 1.95.59-.082 1.09-.387 1.441-.813z"/></g></svg>
|
After Width: | Height: | Size: 1.6 KiB |
238
scene/3d/aim_modifier_3d.cpp
Normal file
238
scene/3d/aim_modifier_3d.cpp
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
/**************************************************************************/
|
||||||
|
/* aim_modifier_3d.cpp */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||||
|
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* */
|
||||||
|
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||||
|
/* a copy of this software and associated documentation files (the */
|
||||||
|
/* "Software"), to deal in the Software without restriction, including */
|
||||||
|
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||||
|
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||||
|
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||||
|
/* the following conditions: */
|
||||||
|
/* */
|
||||||
|
/* The above copyright notice and this permission notice shall be */
|
||||||
|
/* included in all copies or substantial portions of the Software. */
|
||||||
|
/* */
|
||||||
|
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||||
|
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||||
|
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||||
|
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||||
|
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||||
|
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||||
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||||
|
/**************************************************************************/
|
||||||
|
|
||||||
|
#include "aim_modifier_3d.h"
|
||||||
|
|
||||||
|
bool AimModifier3D::_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, settings.size(), false);
|
||||||
|
|
||||||
|
if (what == "forward_axis") {
|
||||||
|
set_forward_axis(which, static_cast<BoneAxis>((int)p_value));
|
||||||
|
} else if (what == "use_euler") {
|
||||||
|
set_use_euler(which, p_value);
|
||||||
|
} else if (what == "primary_rotation_axis") {
|
||||||
|
set_primary_rotation_axis(which, static_cast<Vector3::Axis>((int)p_value));
|
||||||
|
} else if (what == "use_secondary_rotation") {
|
||||||
|
set_use_secondary_rotation(which, p_value);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AimModifier3D::_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, settings.size(), false);
|
||||||
|
|
||||||
|
if (what == "forward_axis") {
|
||||||
|
r_ret = (int)get_forward_axis(which);
|
||||||
|
} else if (what == "use_euler") {
|
||||||
|
r_ret = is_using_euler(which);
|
||||||
|
} else if (what == "primary_rotation_axis") {
|
||||||
|
r_ret = (int)get_primary_rotation_axis(which);
|
||||||
|
} else if (what == "use_secondary_rotation") {
|
||||||
|
r_ret = is_using_secondary_rotation(which);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AimModifier3D::_validate_dynamic_prop(PropertyInfo &p_property) const {
|
||||||
|
PackedStringArray split = p_property.name.split("/");
|
||||||
|
if (split.size() == 3 && split[0] == "settings" && (split[2] == "primary_rotation_axis" || split[2] == "use_secondary_rotation")) {
|
||||||
|
int which = split[1].to_int();
|
||||||
|
if (!is_using_euler(which)) {
|
||||||
|
p_property.usage = PROPERTY_USAGE_NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AimModifier3D::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||||
|
BoneConstraint3D::get_property_list(p_list);
|
||||||
|
LocalVector<PropertyInfo> props;
|
||||||
|
for (int i = 0; i < settings.size(); i++) {
|
||||||
|
String path = "settings/" + itos(i) + "/";
|
||||||
|
props.push_back(PropertyInfo(Variant::INT, path + "forward_axis", PROPERTY_HINT_ENUM, "+X,-X,+Y,-Y,+Z,-Z"));
|
||||||
|
props.push_back(PropertyInfo(Variant::BOOL, path + "use_euler"));
|
||||||
|
props.push_back(PropertyInfo(Variant::INT, path + "primary_rotation_axis", PROPERTY_HINT_ENUM, "X,Y,Z"));
|
||||||
|
props.push_back(PropertyInfo(Variant::BOOL, path + "use_secondary_rotation"));
|
||||||
|
}
|
||||||
|
for (PropertyInfo &p : props) {
|
||||||
|
_validate_dynamic_prop(p);
|
||||||
|
p_list->push_back(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PackedStringArray AimModifier3D::get_configuration_warnings() const {
|
||||||
|
PackedStringArray warnings = BoneConstraint3D::get_configuration_warnings();
|
||||||
|
for (int i = 0; i < settings.size(); i++) {
|
||||||
|
if (is_using_euler(i) && get_axis_from_bone_axis(get_forward_axis(i)) == get_primary_rotation_axis(i)) {
|
||||||
|
warnings.push_back(vformat(RTR("Forward axis and primary rotation axis must not be parallel in setting %s."), itos(i)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return warnings;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AimModifier3D::_validate_setting(int p_index) {
|
||||||
|
settings.write[p_index] = memnew(AimModifier3DSetting);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AimModifier3D::set_forward_axis(int p_index, BoneAxis p_axis) {
|
||||||
|
ERR_FAIL_INDEX(p_index, settings.size());
|
||||||
|
AimModifier3DSetting *setting = static_cast<AimModifier3DSetting *>(settings[p_index]);
|
||||||
|
setting->forward_axis = p_axis;
|
||||||
|
update_configuration_warnings();
|
||||||
|
}
|
||||||
|
|
||||||
|
SkeletonModifier3D::BoneAxis AimModifier3D::get_forward_axis(int p_index) const {
|
||||||
|
ERR_FAIL_INDEX_V(p_index, settings.size(), BONE_AXIS_PLUS_Y);
|
||||||
|
AimModifier3DSetting *setting = static_cast<AimModifier3DSetting *>(settings[p_index]);
|
||||||
|
return setting->forward_axis;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AimModifier3D::set_use_euler(int p_index, bool p_enabled) {
|
||||||
|
ERR_FAIL_INDEX(p_index, settings.size());
|
||||||
|
AimModifier3DSetting *setting = static_cast<AimModifier3DSetting *>(settings[p_index]);
|
||||||
|
setting->use_euler = p_enabled;
|
||||||
|
notify_property_list_changed();
|
||||||
|
update_configuration_warnings();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AimModifier3D::is_using_euler(int p_index) const {
|
||||||
|
ERR_FAIL_INDEX_V(p_index, settings.size(), false);
|
||||||
|
AimModifier3DSetting *setting = static_cast<AimModifier3DSetting *>(settings[p_index]);
|
||||||
|
return setting->use_euler;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AimModifier3D::set_primary_rotation_axis(int p_index, Vector3::Axis p_axis) {
|
||||||
|
ERR_FAIL_INDEX(p_index, settings.size());
|
||||||
|
AimModifier3DSetting *setting = static_cast<AimModifier3DSetting *>(settings[p_index]);
|
||||||
|
setting->primary_rotation_axis = p_axis;
|
||||||
|
update_configuration_warnings();
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3::Axis AimModifier3D::get_primary_rotation_axis(int p_index) const {
|
||||||
|
ERR_FAIL_INDEX_V(p_index, settings.size(), Vector3::AXIS_X);
|
||||||
|
AimModifier3DSetting *setting = static_cast<AimModifier3DSetting *>(settings[p_index]);
|
||||||
|
return setting->primary_rotation_axis;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AimModifier3D::set_use_secondary_rotation(int p_index, bool p_enabled) {
|
||||||
|
ERR_FAIL_INDEX(p_index, settings.size());
|
||||||
|
AimModifier3DSetting *setting = static_cast<AimModifier3DSetting *>(settings[p_index]);
|
||||||
|
setting->use_secondary_rotation = p_enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AimModifier3D::is_using_secondary_rotation(int p_index) const {
|
||||||
|
ERR_FAIL_INDEX_V(p_index, settings.size(), false);
|
||||||
|
AimModifier3DSetting *setting = static_cast<AimModifier3DSetting *>(settings[p_index]);
|
||||||
|
return setting->use_secondary_rotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AimModifier3D::_bind_methods() {
|
||||||
|
ClassDB::bind_method(D_METHOD("set_forward_axis", "index", "axis"), &AimModifier3D::set_forward_axis);
|
||||||
|
ClassDB::bind_method(D_METHOD("get_forward_axis", "index"), &AimModifier3D::get_forward_axis);
|
||||||
|
ClassDB::bind_method(D_METHOD("set_use_euler", "index", "enabled"), &AimModifier3D::set_use_euler);
|
||||||
|
ClassDB::bind_method(D_METHOD("is_using_euler", "index"), &AimModifier3D::is_using_euler);
|
||||||
|
ClassDB::bind_method(D_METHOD("set_primary_rotation_axis", "index", "axis"), &AimModifier3D::set_primary_rotation_axis);
|
||||||
|
ClassDB::bind_method(D_METHOD("get_primary_rotation_axis", "index"), &AimModifier3D::get_primary_rotation_axis);
|
||||||
|
ClassDB::bind_method(D_METHOD("set_use_secondary_rotation", "index", "enabled"), &AimModifier3D::set_use_secondary_rotation);
|
||||||
|
ClassDB::bind_method(D_METHOD("is_using_secondary_rotation", "index"), &AimModifier3D::is_using_secondary_rotation);
|
||||||
|
|
||||||
|
ADD_ARRAY_COUNT("Settings", "setting_count", "set_setting_count", "get_setting_count", "settings/");
|
||||||
|
}
|
||||||
|
|
||||||
|
void AimModifier3D::_process_constraint(int p_index, Skeleton3D *p_skeleton, int p_apply_bone, int p_reference_bone, float p_amount) {
|
||||||
|
if (p_apply_bone == p_reference_bone) {
|
||||||
|
ERR_PRINT_ONCE_ED(vformat("In setting %s, the reference bone must not be same with the apply bone.", itos(p_index)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AimModifier3DSetting *setting = static_cast<AimModifier3DSetting *>(settings[p_index]);
|
||||||
|
|
||||||
|
// Prepare forward_vector and rest.
|
||||||
|
Vector3 reference_origin = p_skeleton->get_bone_global_pose(p_reference_bone).origin;
|
||||||
|
Transform3D src_bone_rest = p_skeleton->get_bone_rest(p_apply_bone);
|
||||||
|
Transform3D bone_rest_space;
|
||||||
|
int parent_bone = p_skeleton->get_bone_parent(p_apply_bone);
|
||||||
|
if (parent_bone < 0) {
|
||||||
|
bone_rest_space.translate_local(src_bone_rest.origin);
|
||||||
|
} else {
|
||||||
|
bone_rest_space = p_skeleton->get_bone_global_pose(parent_bone);
|
||||||
|
bone_rest_space.translate_local(src_bone_rest.origin);
|
||||||
|
}
|
||||||
|
Vector3 forward_vector = bone_rest_space.basis.get_rotation_quaternion().xform_inv(reference_origin - bone_rest_space.origin);
|
||||||
|
if (forward_vector.is_zero_approx()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
forward_vector.normalize();
|
||||||
|
|
||||||
|
// Calculate look at rotation.
|
||||||
|
Quaternion destination;
|
||||||
|
if (setting->use_euler) {
|
||||||
|
Vector3 current_vector = LookAtModifier3D::get_basis_vector_from_bone_axis(src_bone_rest.basis, setting->forward_axis).normalized();
|
||||||
|
Vector2 src_vec2 = LookAtModifier3D::get_projection_vector(src_bone_rest.basis.xform_inv(forward_vector), setting->primary_rotation_axis).normalized();
|
||||||
|
Vector2 dst_vec2 = LookAtModifier3D::get_projection_vector(src_bone_rest.basis.xform_inv(current_vector), setting->primary_rotation_axis).normalized();
|
||||||
|
real_t calculated_angle = src_vec2.angle_to(dst_vec2);
|
||||||
|
Transform3D primary_result = src_bone_rest.rotated_local(get_vector_from_axis(setting->primary_rotation_axis), calculated_angle);
|
||||||
|
Transform3D current_result = primary_result;
|
||||||
|
if (setting->use_secondary_rotation) {
|
||||||
|
Vector3::Axis secondary_rotation_axis = LookAtModifier3D::get_secondary_rotation_axis(setting->forward_axis, setting->primary_rotation_axis);
|
||||||
|
current_vector = LookAtModifier3D::get_basis_vector_from_bone_axis(primary_result.basis, setting->forward_axis).normalized();
|
||||||
|
src_vec2 = LookAtModifier3D::get_projection_vector(primary_result.basis.xform_inv(forward_vector), secondary_rotation_axis).normalized();
|
||||||
|
dst_vec2 = LookAtModifier3D::get_projection_vector(primary_result.basis.xform_inv(current_vector), secondary_rotation_axis).normalized();
|
||||||
|
calculated_angle = src_vec2.angle_to(dst_vec2);
|
||||||
|
current_result = primary_result.rotated_local(get_vector_from_axis(secondary_rotation_axis), calculated_angle);
|
||||||
|
}
|
||||||
|
destination = current_result.basis.get_rotation_quaternion();
|
||||||
|
} else {
|
||||||
|
Vector3 current_vector = LookAtModifier3D::get_basis_vector_from_bone_axis(src_bone_rest.basis, setting->forward_axis).normalized();
|
||||||
|
destination = Quaternion(current_vector, forward_vector) * src_bone_rest.basis.get_rotation_quaternion();
|
||||||
|
}
|
||||||
|
|
||||||
|
p_skeleton->set_bone_pose_rotation(p_apply_bone, p_skeleton->get_bone_pose_rotation(p_apply_bone).slerp(destination, p_amount));
|
||||||
|
}
|
||||||
|
|
||||||
|
AimModifier3D::~AimModifier3D() {
|
||||||
|
clear_settings();
|
||||||
|
}
|
70
scene/3d/aim_modifier_3d.h
Normal file
70
scene/3d/aim_modifier_3d.h
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/**************************************************************************/
|
||||||
|
/* aim_modifier_3d.h */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||||
|
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* */
|
||||||
|
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||||
|
/* a copy of this software and associated documentation files (the */
|
||||||
|
/* "Software"), to deal in the Software without restriction, including */
|
||||||
|
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||||
|
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||||
|
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||||
|
/* the following conditions: */
|
||||||
|
/* */
|
||||||
|
/* The above copyright notice and this permission notice shall be */
|
||||||
|
/* included in all copies or substantial portions of the Software. */
|
||||||
|
/* */
|
||||||
|
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||||
|
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||||
|
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||||
|
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||||
|
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||||
|
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||||
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||||
|
/**************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "scene/3d/bone_constraint_3d.h"
|
||||||
|
#include "scene/3d/look_at_modifier_3d.h"
|
||||||
|
|
||||||
|
class AimModifier3D : public BoneConstraint3D {
|
||||||
|
GDCLASS(AimModifier3D, BoneConstraint3D);
|
||||||
|
|
||||||
|
public:
|
||||||
|
struct AimModifier3DSetting : public BoneConstraint3DSetting {
|
||||||
|
BoneAxis forward_axis = BONE_AXIS_PLUS_Y;
|
||||||
|
bool use_euler = false;
|
||||||
|
Vector3::Axis primary_rotation_axis = Vector3::AXIS_X;
|
||||||
|
bool use_secondary_rotation = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool _get(const StringName &p_path, Variant &r_ret) const;
|
||||||
|
bool _set(const StringName &p_path, const Variant &p_value);
|
||||||
|
virtual PackedStringArray get_configuration_warnings() const override;
|
||||||
|
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||||
|
void _validate_dynamic_prop(PropertyInfo &p_property) const;
|
||||||
|
|
||||||
|
static void _bind_methods();
|
||||||
|
|
||||||
|
virtual void _process_constraint(int p_index, Skeleton3D *p_skeleton, int p_apply_bone, int p_reference_bone, float p_amount) override;
|
||||||
|
virtual void _validate_setting(int p_index) override;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void set_forward_axis(int p_index, BoneAxis p_axis);
|
||||||
|
BoneAxis get_forward_axis(int p_index) const;
|
||||||
|
void set_use_euler(int p_index, bool p_enabled);
|
||||||
|
bool is_using_euler(int p_index) const;
|
||||||
|
void set_primary_rotation_axis(int p_index, Vector3::Axis p_axis);
|
||||||
|
Vector3::Axis get_primary_rotation_axis(int p_index) const;
|
||||||
|
void set_use_secondary_rotation(int p_index, bool p_enabled);
|
||||||
|
bool is_using_secondary_rotation(int p_index) const;
|
||||||
|
|
||||||
|
~AimModifier3D();
|
||||||
|
};
|
314
scene/3d/bone_constraint_3d.cpp
Normal file
314
scene/3d/bone_constraint_3d.cpp
Normal file
@ -0,0 +1,314 @@
|
|||||||
|
/**************************************************************************/
|
||||||
|
/* bone_constraint_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_constraint_3d.h"
|
||||||
|
|
||||||
|
bool BoneConstraint3D::_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, settings.size(), false);
|
||||||
|
|
||||||
|
if (what == "amount") {
|
||||||
|
set_amount(which, p_value);
|
||||||
|
} else if (what == "apply_bone_name") {
|
||||||
|
set_apply_bone_name(which, p_value);
|
||||||
|
} else if (what == "reference_bone_name") {
|
||||||
|
set_reference_bone_name(which, p_value);
|
||||||
|
} else if (what == "apply_bone") {
|
||||||
|
set_apply_bone(which, p_value);
|
||||||
|
} else if (what == "reference_bone") {
|
||||||
|
set_reference_bone(which, p_value);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BoneConstraint3D::_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, settings.size(), false);
|
||||||
|
|
||||||
|
if (what == "amount") {
|
||||||
|
r_ret = get_amount(which);
|
||||||
|
} else if (what == "apply_bone_name") {
|
||||||
|
r_ret = get_apply_bone_name(which);
|
||||||
|
} else if (what == "reference_bone_name") {
|
||||||
|
r_ret = get_reference_bone_name(which);
|
||||||
|
} else if (what == "apply_bone") {
|
||||||
|
r_ret = get_apply_bone(which);
|
||||||
|
} else if (what == "reference_bone") {
|
||||||
|
r_ret = get_reference_bone(which);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BoneConstraint3D::get_property_list(List<PropertyInfo> *p_list) const {
|
||||||
|
String enum_hint;
|
||||||
|
Skeleton3D *skeleton = get_skeleton();
|
||||||
|
if (skeleton) {
|
||||||
|
enum_hint = skeleton->get_concatenated_bone_names();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < settings.size(); i++) {
|
||||||
|
String path = "settings/" + itos(i) + "/";
|
||||||
|
p_list->push_back(PropertyInfo(Variant::FLOAT, path + "amount", PROPERTY_HINT_RANGE, "0,1,0.001"));
|
||||||
|
p_list->push_back(PropertyInfo(Variant::STRING, path + "apply_bone_name", PROPERTY_HINT_ENUM_SUGGESTION, enum_hint));
|
||||||
|
p_list->push_back(PropertyInfo(Variant::INT, path + "apply_bone", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
|
||||||
|
p_list->push_back(PropertyInfo(Variant::STRING, path + "reference_bone_name", PROPERTY_HINT_ENUM_SUGGESTION, enum_hint));
|
||||||
|
p_list->push_back(PropertyInfo(Variant::INT, path + "reference_bone", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BoneConstraint3D::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.resize(p_count);
|
||||||
|
delta++;
|
||||||
|
|
||||||
|
if (delta > 1) {
|
||||||
|
for (int i = 1; i < delta; i++) {
|
||||||
|
_validate_setting(p_count - i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
notify_property_list_changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
int BoneConstraint3D::get_setting_count() const {
|
||||||
|
return settings.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BoneConstraint3D::_validate_setting(int p_index) {
|
||||||
|
settings.write[p_index] = memnew(BoneConstraint3DSetting);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BoneConstraint3D::clear_settings() {
|
||||||
|
set_setting_count(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BoneConstraint3D::set_amount(int p_index, float p_amount) {
|
||||||
|
ERR_FAIL_INDEX(p_index, settings.size());
|
||||||
|
settings[p_index]->amount = p_amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
float BoneConstraint3D::get_amount(int p_index) const {
|
||||||
|
ERR_FAIL_INDEX_V(p_index, settings.size(), 0.0);
|
||||||
|
return settings[p_index]->amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BoneConstraint3D::set_apply_bone_name(int p_index, const String &p_bone_name) {
|
||||||
|
ERR_FAIL_INDEX(p_index, settings.size());
|
||||||
|
settings[p_index]->apply_bone_name = p_bone_name;
|
||||||
|
Skeleton3D *sk = get_skeleton();
|
||||||
|
if (sk) {
|
||||||
|
set_apply_bone(p_index, sk->find_bone(settings[p_index]->apply_bone_name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String BoneConstraint3D::get_apply_bone_name(int p_index) const {
|
||||||
|
ERR_FAIL_INDEX_V(p_index, settings.size(), String());
|
||||||
|
return settings[p_index]->apply_bone_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BoneConstraint3D::set_apply_bone(int p_index, int p_bone) {
|
||||||
|
ERR_FAIL_INDEX(p_index, settings.size());
|
||||||
|
settings[p_index]->apply_bone = p_bone;
|
||||||
|
Skeleton3D *sk = get_skeleton();
|
||||||
|
if (sk) {
|
||||||
|
if (settings[p_index]->apply_bone <= -1 || settings[p_index]->apply_bone >= sk->get_bone_count()) {
|
||||||
|
WARN_PRINT("apply bone index out of range!");
|
||||||
|
settings[p_index]->apply_bone = -1;
|
||||||
|
} else {
|
||||||
|
settings[p_index]->apply_bone_name = sk->get_bone_name(settings[p_index]->apply_bone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int BoneConstraint3D::get_apply_bone(int p_index) const {
|
||||||
|
ERR_FAIL_INDEX_V(p_index, settings.size(), -1);
|
||||||
|
return settings[p_index]->apply_bone;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BoneConstraint3D::set_reference_bone_name(int p_index, const String &p_bone_name) {
|
||||||
|
ERR_FAIL_INDEX(p_index, settings.size());
|
||||||
|
settings[p_index]->reference_bone_name = p_bone_name;
|
||||||
|
Skeleton3D *sk = get_skeleton();
|
||||||
|
if (sk) {
|
||||||
|
set_reference_bone(p_index, sk->find_bone(settings[p_index]->reference_bone_name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String BoneConstraint3D::get_reference_bone_name(int p_index) const {
|
||||||
|
ERR_FAIL_INDEX_V(p_index, settings.size(), String());
|
||||||
|
return settings[p_index]->reference_bone_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BoneConstraint3D::set_reference_bone(int p_index, int p_bone) {
|
||||||
|
ERR_FAIL_INDEX(p_index, settings.size());
|
||||||
|
settings[p_index]->reference_bone = p_bone;
|
||||||
|
Skeleton3D *sk = get_skeleton();
|
||||||
|
if (sk) {
|
||||||
|
if (settings[p_index]->reference_bone <= -1 || settings[p_index]->reference_bone >= sk->get_bone_count()) {
|
||||||
|
WARN_PRINT("reference bone index out of range!");
|
||||||
|
settings[p_index]->reference_bone = -1;
|
||||||
|
} else {
|
||||||
|
settings[p_index]->reference_bone_name = sk->get_bone_name(settings[p_index]->reference_bone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int BoneConstraint3D::get_reference_bone(int p_index) const {
|
||||||
|
ERR_FAIL_INDEX_V(p_index, settings.size(), -1);
|
||||||
|
return settings[p_index]->reference_bone;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BoneConstraint3D::_bind_methods() {
|
||||||
|
ClassDB::bind_method(D_METHOD("set_amount", "index", "amount"), &BoneConstraint3D::set_amount);
|
||||||
|
ClassDB::bind_method(D_METHOD("get_amount", "index"), &BoneConstraint3D::get_amount);
|
||||||
|
ClassDB::bind_method(D_METHOD("set_apply_bone_name", "index", "bone_name"), &BoneConstraint3D::set_apply_bone_name);
|
||||||
|
ClassDB::bind_method(D_METHOD("get_apply_bone_name", "index"), &BoneConstraint3D::get_apply_bone_name);
|
||||||
|
ClassDB::bind_method(D_METHOD("set_apply_bone", "index", "bone"), &BoneConstraint3D::set_apply_bone);
|
||||||
|
ClassDB::bind_method(D_METHOD("get_apply_bone", "index"), &BoneConstraint3D::get_apply_bone);
|
||||||
|
ClassDB::bind_method(D_METHOD("set_reference_bone_name", "index", "bone_name"), &BoneConstraint3D::set_reference_bone_name);
|
||||||
|
ClassDB::bind_method(D_METHOD("get_reference_bone_name", "index"), &BoneConstraint3D::get_reference_bone_name);
|
||||||
|
ClassDB::bind_method(D_METHOD("set_reference_bone", "index", "bone"), &BoneConstraint3D::set_reference_bone);
|
||||||
|
ClassDB::bind_method(D_METHOD("get_reference_bone", "index"), &BoneConstraint3D::get_reference_bone);
|
||||||
|
|
||||||
|
ClassDB::bind_method(D_METHOD("set_setting_count", "count"), &BoneConstraint3D::set_setting_count);
|
||||||
|
ClassDB::bind_method(D_METHOD("get_setting_count"), &BoneConstraint3D::get_setting_count);
|
||||||
|
ClassDB::bind_method(D_METHOD("clear_setting"), &BoneConstraint3D::clear_settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BoneConstraint3D::_validate_bone_names() {
|
||||||
|
for (int i = 0; i < settings.size(); i++) {
|
||||||
|
// Prior bone name.
|
||||||
|
if (!settings[i]->apply_bone_name.is_empty()) {
|
||||||
|
set_apply_bone_name(i, settings[i]->apply_bone_name);
|
||||||
|
} else if (settings[i]->apply_bone != -1) {
|
||||||
|
set_apply_bone(i, settings[i]->apply_bone);
|
||||||
|
}
|
||||||
|
// Prior bone name.
|
||||||
|
if (!settings[i]->reference_bone_name.is_empty()) {
|
||||||
|
set_reference_bone_name(i, settings[i]->reference_bone_name);
|
||||||
|
} else if (settings[i]->reference_bone != -1) {
|
||||||
|
set_reference_bone(i, settings[i]->reference_bone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BoneConstraint3D::_process_modification(double p_delta) {
|
||||||
|
Skeleton3D *skeleton = get_skeleton();
|
||||||
|
if (!skeleton) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < settings.size(); i++) {
|
||||||
|
int apply_bone = settings[i]->apply_bone;
|
||||||
|
if (apply_bone < 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int reference_bone = settings[i]->reference_bone;
|
||||||
|
if (reference_bone < 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
float amount = settings[i]->amount;
|
||||||
|
if (amount <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
_process_constraint(i, skeleton, apply_bone, reference_bone, amount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BoneConstraint3D::_process_constraint(int p_index, Skeleton3D *p_skeleton, int p_apply_bone, int p_reference_bone, float p_amount) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
double BoneConstraint3D::symmetrize_angle(double p_angle) {
|
||||||
|
double angle = Math::fposmod(p_angle, Math::TAU);
|
||||||
|
return angle > Math::PI ? angle - Math::TAU : angle;
|
||||||
|
}
|
||||||
|
|
||||||
|
double BoneConstraint3D::get_roll_angle(const Quaternion &p_rotation, const Vector3 &p_roll_axis) {
|
||||||
|
// Ensure roll axis is normalized.
|
||||||
|
Vector3 roll_axis = p_roll_axis.normalized();
|
||||||
|
|
||||||
|
// Project the quaternion rotation onto the roll axis.
|
||||||
|
// This gives us the component of rotation around that axis.
|
||||||
|
double dot = p_rotation.x * roll_axis.x +
|
||||||
|
p_rotation.y * roll_axis.y +
|
||||||
|
p_rotation.z * roll_axis.z;
|
||||||
|
|
||||||
|
// Create a quaternion representing just the roll component.
|
||||||
|
Quaternion roll_component;
|
||||||
|
roll_component.x = roll_axis.x * dot;
|
||||||
|
roll_component.y = roll_axis.y * dot;
|
||||||
|
roll_component.z = roll_axis.z * dot;
|
||||||
|
roll_component.w = p_rotation.w;
|
||||||
|
|
||||||
|
// Normalize this component.
|
||||||
|
double length = roll_component.length();
|
||||||
|
if (length > CMP_EPSILON) {
|
||||||
|
roll_component = roll_component / length;
|
||||||
|
} else {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract the angle.
|
||||||
|
double angle = 2.0 * Math::acos(CLAMP(roll_component.w, -1.0, 1.0));
|
||||||
|
|
||||||
|
// Determine the sign.
|
||||||
|
double direction = (roll_component.x * roll_axis.x + roll_component.y * roll_axis.y + roll_component.z * roll_axis.z > 0) ? 1.0 : -1.0;
|
||||||
|
|
||||||
|
return symmetrize_angle(angle * direction);
|
||||||
|
}
|
||||||
|
|
||||||
|
BoneConstraint3D::~BoneConstraint3D() {
|
||||||
|
clear_settings();
|
||||||
|
}
|
90
scene/3d/bone_constraint_3d.h
Normal file
90
scene/3d/bone_constraint_3d.h
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
/**************************************************************************/
|
||||||
|
/* bone_constraint_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 BoneConstraint3D : public SkeletonModifier3D {
|
||||||
|
GDCLASS(BoneConstraint3D, SkeletonModifier3D);
|
||||||
|
|
||||||
|
public:
|
||||||
|
struct BoneConstraint3DSetting {
|
||||||
|
float amount = 1.0;
|
||||||
|
|
||||||
|
String apply_bone_name;
|
||||||
|
int apply_bone = -1;
|
||||||
|
|
||||||
|
String reference_bone_name;
|
||||||
|
int reference_bone = -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Vector<BoneConstraint3DSetting *> settings;
|
||||||
|
|
||||||
|
bool _get(const StringName &p_path, Variant &r_ret) const;
|
||||||
|
bool _set(const StringName &p_path, const Variant &p_value);
|
||||||
|
|
||||||
|
// Define get_property_list() instead of _get_property_list()
|
||||||
|
// to merge child class properties into parent class array inspector.
|
||||||
|
void get_property_list(List<PropertyInfo> *p_list) const; // Will be called by child classes.
|
||||||
|
|
||||||
|
virtual void _validate_bone_names() override;
|
||||||
|
static void _bind_methods();
|
||||||
|
|
||||||
|
virtual void _process_modification(double p_delta) override;
|
||||||
|
|
||||||
|
virtual void _process_constraint(int p_index, Skeleton3D *p_skeleton, int p_apply_bone, int p_reference_bone, float p_amount);
|
||||||
|
virtual void _validate_setting(int p_index);
|
||||||
|
|
||||||
|
public:
|
||||||
|
void set_amount(int p_index, float p_amount);
|
||||||
|
float get_amount(int p_index) const;
|
||||||
|
|
||||||
|
void set_apply_bone_name(int p_index, const String &p_bone_name);
|
||||||
|
String get_apply_bone_name(int p_index) const;
|
||||||
|
void set_apply_bone(int p_index, int p_bone);
|
||||||
|
int get_apply_bone(int p_index) const;
|
||||||
|
|
||||||
|
void set_reference_bone_name(int p_index, const String &p_bone_name);
|
||||||
|
String get_reference_bone_name(int p_index) const;
|
||||||
|
void set_reference_bone(int p_index, int p_bone);
|
||||||
|
int get_reference_bone(int p_index) const;
|
||||||
|
|
||||||
|
void set_setting_count(int p_count);
|
||||||
|
int get_setting_count() const;
|
||||||
|
|
||||||
|
void clear_settings();
|
||||||
|
|
||||||
|
static double symmetrize_angle(double p_angle); // Helper to make angle 0->TAU become -PI->PI.
|
||||||
|
static double get_roll_angle(const Quaternion &p_rotation, const Vector3 &p_roll_axis);
|
||||||
|
|
||||||
|
~BoneConstraint3D();
|
||||||
|
};
|
433
scene/3d/convert_transform_modifier_3d.cpp
Normal file
433
scene/3d/convert_transform_modifier_3d.cpp
Normal file
@ -0,0 +1,433 @@
|
|||||||
|
/**************************************************************************/
|
||||||
|
/* convert_transform_modifier_3d.cpp */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||||
|
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* */
|
||||||
|
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||||
|
/* a copy of this software and associated documentation files (the */
|
||||||
|
/* "Software"), to deal in the Software without restriction, including */
|
||||||
|
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||||
|
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||||
|
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||||
|
/* the following conditions: */
|
||||||
|
/* */
|
||||||
|
/* The above copyright notice and this permission notice shall be */
|
||||||
|
/* included in all copies or substantial portions of the Software. */
|
||||||
|
/* */
|
||||||
|
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||||
|
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||||
|
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||||
|
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||||
|
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||||
|
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||||
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||||
|
/**************************************************************************/
|
||||||
|
|
||||||
|
#include "convert_transform_modifier_3d.h"
|
||||||
|
|
||||||
|
constexpr const char *HINT_POSITION = "-10,10,0.01,or_greater,or_less,suffix:m";
|
||||||
|
constexpr const char *HINT_ROTATION = "-180,180,0.01,radians_as_degrees";
|
||||||
|
constexpr const char *HINT_SCALE = "0,10,0.01,or_greater";
|
||||||
|
|
||||||
|
bool ConvertTransformModifier3D::_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 where = path.get_slicec('/', 2);
|
||||||
|
ERR_FAIL_INDEX_V(which, settings.size(), false);
|
||||||
|
String what = path.get_slicec('/', 3);
|
||||||
|
|
||||||
|
if (where == "apply") {
|
||||||
|
if (what == "transform_mode") {
|
||||||
|
set_apply_transform_mode(which, static_cast<TransformMode>((int)p_value));
|
||||||
|
} else if (what == "axis") {
|
||||||
|
set_apply_axis(which, static_cast<Vector3::Axis>((int)p_value));
|
||||||
|
} else if (what == "range_min") {
|
||||||
|
set_apply_range_min(which, p_value);
|
||||||
|
} else if (what == "range_max") {
|
||||||
|
set_apply_range_max(which, p_value);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (where == "reference") {
|
||||||
|
if (what == "transform_mode") {
|
||||||
|
set_reference_transform_mode(which, static_cast<TransformMode>((int)p_value));
|
||||||
|
} else if (what == "axis") {
|
||||||
|
set_reference_axis(which, static_cast<Vector3::Axis>((int)p_value));
|
||||||
|
} else if (what == "range_min") {
|
||||||
|
set_reference_range_min(which, p_value);
|
||||||
|
} else if (what == "range_max") {
|
||||||
|
set_reference_range_max(which, p_value);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (where == "relative") {
|
||||||
|
set_relative(which, p_value);
|
||||||
|
} else if (where == "additive") {
|
||||||
|
set_additive(which, p_value);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConvertTransformModifier3D::_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 where = path.get_slicec('/', 2);
|
||||||
|
ERR_FAIL_INDEX_V(which, settings.size(), false);
|
||||||
|
String what = path.get_slicec('/', 3);
|
||||||
|
|
||||||
|
if (where == "apply") {
|
||||||
|
if (what == "transform_mode") {
|
||||||
|
r_ret = (int)get_apply_transform_mode(which);
|
||||||
|
} else if (what == "axis") {
|
||||||
|
r_ret = (int)get_apply_axis(which);
|
||||||
|
} else if (what == "range_min") {
|
||||||
|
r_ret = get_apply_range_min(which);
|
||||||
|
} else if (what == "range_max") {
|
||||||
|
r_ret = get_apply_range_max(which);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (where == "reference") {
|
||||||
|
if (what == "transform_mode") {
|
||||||
|
r_ret = (int)get_reference_transform_mode(which);
|
||||||
|
} else if (what == "axis") {
|
||||||
|
r_ret = (int)get_reference_axis(which);
|
||||||
|
} else if (what == "range_min") {
|
||||||
|
r_ret = get_reference_range_min(which);
|
||||||
|
} else if (what == "range_max") {
|
||||||
|
r_ret = get_reference_range_max(which);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (where == "relative") {
|
||||||
|
r_ret = is_relative(which);
|
||||||
|
} else if (where == "additive") {
|
||||||
|
r_ret = is_additive(which);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConvertTransformModifier3D::_validate_dynamic_prop(PropertyInfo &p_property) const {
|
||||||
|
PackedStringArray split = p_property.name.split("/");
|
||||||
|
if (split.size() == 4 && split[0] == "settings") {
|
||||||
|
int which = split[1].to_int();
|
||||||
|
bool hide = false;
|
||||||
|
if (split[2] == "apply") {
|
||||||
|
if (split[3] == "range_min" || split[3] == "range_max") {
|
||||||
|
if (get_apply_transform_mode(which) == TRANSFORM_MODE_POSITION) {
|
||||||
|
p_property.hint_string = HINT_POSITION;
|
||||||
|
} else if (get_apply_transform_mode(which) == TRANSFORM_MODE_ROTATION) {
|
||||||
|
p_property.hint_string = HINT_ROTATION;
|
||||||
|
} else {
|
||||||
|
p_property.hint_string = HINT_SCALE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (split[2] == "reference") {
|
||||||
|
if (split[3] == "range_min" || split[3] == "range_max") {
|
||||||
|
if (get_reference_transform_mode(which) == TRANSFORM_MODE_POSITION) {
|
||||||
|
p_property.hint_string = HINT_POSITION;
|
||||||
|
} else if (get_reference_transform_mode(which) == TRANSFORM_MODE_ROTATION) {
|
||||||
|
p_property.hint_string = HINT_ROTATION;
|
||||||
|
} else {
|
||||||
|
p_property.hint_string = HINT_SCALE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hide) {
|
||||||
|
p_property.usage = PROPERTY_USAGE_NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConvertTransformModifier3D::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||||
|
BoneConstraint3D::get_property_list(p_list);
|
||||||
|
LocalVector<PropertyInfo> props;
|
||||||
|
for (int i = 0; i < settings.size(); i++) {
|
||||||
|
String path = "settings/" + itos(i) + "/";
|
||||||
|
|
||||||
|
props.push_back(PropertyInfo(Variant::INT, path + "apply/transform_mode", PROPERTY_HINT_ENUM, "Position,Rotation,Scale"));
|
||||||
|
props.push_back(PropertyInfo(Variant::INT, path + "apply/axis", PROPERTY_HINT_ENUM, "X,Y,Z"));
|
||||||
|
props.push_back(PropertyInfo(Variant::FLOAT, path + "apply/range_min", PROPERTY_HINT_RANGE, HINT_POSITION));
|
||||||
|
props.push_back(PropertyInfo(Variant::FLOAT, path + "apply/range_max", PROPERTY_HINT_RANGE, HINT_POSITION));
|
||||||
|
|
||||||
|
props.push_back(PropertyInfo(Variant::INT, path + "reference/transform_mode", PROPERTY_HINT_ENUM, "Position,Rotation,Scale"));
|
||||||
|
props.push_back(PropertyInfo(Variant::INT, path + "reference/axis", PROPERTY_HINT_ENUM, "X,Y,Z"));
|
||||||
|
props.push_back(PropertyInfo(Variant::FLOAT, path + "reference/range_min", PROPERTY_HINT_RANGE, HINT_POSITION));
|
||||||
|
props.push_back(PropertyInfo(Variant::FLOAT, path + "reference/range_max", PROPERTY_HINT_RANGE, HINT_POSITION));
|
||||||
|
|
||||||
|
props.push_back(PropertyInfo(Variant::BOOL, path + "relative"));
|
||||||
|
props.push_back(PropertyInfo(Variant::BOOL, path + "additive"));
|
||||||
|
}
|
||||||
|
for (PropertyInfo &p : props) {
|
||||||
|
_validate_dynamic_prop(p);
|
||||||
|
p_list->push_back(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConvertTransformModifier3D::_validate_setting(int p_index) {
|
||||||
|
settings.write[p_index] = memnew(ConvertTransform3DSetting);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConvertTransformModifier3D::set_apply_transform_mode(int p_index, TransformMode p_transform_mode) {
|
||||||
|
ERR_FAIL_INDEX(p_index, settings.size());
|
||||||
|
ConvertTransform3DSetting *setting = static_cast<ConvertTransform3DSetting *>(settings[p_index]);
|
||||||
|
setting->apply_transform_mode = p_transform_mode;
|
||||||
|
notify_property_list_changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
ConvertTransformModifier3D::TransformMode ConvertTransformModifier3D::get_apply_transform_mode(int p_index) const {
|
||||||
|
ERR_FAIL_INDEX_V(p_index, settings.size(), TRANSFORM_MODE_POSITION);
|
||||||
|
ConvertTransform3DSetting *setting = static_cast<ConvertTransform3DSetting *>(settings[p_index]);
|
||||||
|
return setting->apply_transform_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConvertTransformModifier3D::set_apply_axis(int p_index, Vector3::Axis p_axis) {
|
||||||
|
ERR_FAIL_INDEX(p_index, settings.size());
|
||||||
|
ConvertTransform3DSetting *setting = static_cast<ConvertTransform3DSetting *>(settings[p_index]);
|
||||||
|
setting->apply_axis = p_axis;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3::Axis ConvertTransformModifier3D::get_apply_axis(int p_index) const {
|
||||||
|
ERR_FAIL_INDEX_V(p_index, settings.size(), Vector3::AXIS_X);
|
||||||
|
ConvertTransform3DSetting *setting = static_cast<ConvertTransform3DSetting *>(settings[p_index]);
|
||||||
|
return setting->apply_axis;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConvertTransformModifier3D::set_apply_range_min(int p_index, float p_range_min) {
|
||||||
|
ERR_FAIL_INDEX(p_index, settings.size());
|
||||||
|
ConvertTransform3DSetting *setting = static_cast<ConvertTransform3DSetting *>(settings[p_index]);
|
||||||
|
setting->apply_range_min = p_range_min;
|
||||||
|
}
|
||||||
|
|
||||||
|
float ConvertTransformModifier3D::get_apply_range_min(int p_index) const {
|
||||||
|
ERR_FAIL_INDEX_V(p_index, settings.size(), 0);
|
||||||
|
ConvertTransform3DSetting *setting = static_cast<ConvertTransform3DSetting *>(settings[p_index]);
|
||||||
|
return setting->apply_range_min;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConvertTransformModifier3D::set_apply_range_max(int p_index, float p_range_max) {
|
||||||
|
ERR_FAIL_INDEX(p_index, settings.size());
|
||||||
|
ConvertTransform3DSetting *setting = static_cast<ConvertTransform3DSetting *>(settings[p_index]);
|
||||||
|
setting->apply_range_max = p_range_max;
|
||||||
|
}
|
||||||
|
|
||||||
|
float ConvertTransformModifier3D::get_apply_range_max(int p_index) const {
|
||||||
|
ERR_FAIL_INDEX_V(p_index, settings.size(), 0);
|
||||||
|
ConvertTransform3DSetting *setting = static_cast<ConvertTransform3DSetting *>(settings[p_index]);
|
||||||
|
return setting->apply_range_max;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConvertTransformModifier3D::set_reference_transform_mode(int p_index, TransformMode p_transform_mode) {
|
||||||
|
ERR_FAIL_INDEX(p_index, settings.size());
|
||||||
|
ConvertTransform3DSetting *setting = static_cast<ConvertTransform3DSetting *>(settings[p_index]);
|
||||||
|
setting->reference_transform_mode = p_transform_mode;
|
||||||
|
notify_property_list_changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
ConvertTransformModifier3D::TransformMode ConvertTransformModifier3D::get_reference_transform_mode(int p_index) const {
|
||||||
|
ERR_FAIL_INDEX_V(p_index, settings.size(), TRANSFORM_MODE_POSITION);
|
||||||
|
ConvertTransform3DSetting *setting = static_cast<ConvertTransform3DSetting *>(settings[p_index]);
|
||||||
|
return setting->reference_transform_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConvertTransformModifier3D::set_reference_axis(int p_index, Vector3::Axis p_axis) {
|
||||||
|
ERR_FAIL_INDEX(p_index, settings.size());
|
||||||
|
ConvertTransform3DSetting *setting = static_cast<ConvertTransform3DSetting *>(settings[p_index]);
|
||||||
|
setting->reference_axis = p_axis;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3::Axis ConvertTransformModifier3D::get_reference_axis(int p_index) const {
|
||||||
|
ERR_FAIL_INDEX_V(p_index, settings.size(), Vector3::AXIS_X);
|
||||||
|
ConvertTransform3DSetting *setting = static_cast<ConvertTransform3DSetting *>(settings[p_index]);
|
||||||
|
return setting->reference_axis;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConvertTransformModifier3D::set_reference_range_min(int p_index, float p_range_min) {
|
||||||
|
ERR_FAIL_INDEX(p_index, settings.size());
|
||||||
|
ConvertTransform3DSetting *setting = static_cast<ConvertTransform3DSetting *>(settings[p_index]);
|
||||||
|
setting->reference_range_min = p_range_min;
|
||||||
|
}
|
||||||
|
|
||||||
|
float ConvertTransformModifier3D::get_reference_range_min(int p_index) const {
|
||||||
|
ERR_FAIL_INDEX_V(p_index, settings.size(), 0);
|
||||||
|
ConvertTransform3DSetting *setting = static_cast<ConvertTransform3DSetting *>(settings[p_index]);
|
||||||
|
return setting->reference_range_min;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConvertTransformModifier3D::set_reference_range_max(int p_index, float p_range_max) {
|
||||||
|
ERR_FAIL_INDEX(p_index, settings.size());
|
||||||
|
ConvertTransform3DSetting *setting = static_cast<ConvertTransform3DSetting *>(settings[p_index]);
|
||||||
|
setting->reference_range_max = p_range_max;
|
||||||
|
}
|
||||||
|
|
||||||
|
float ConvertTransformModifier3D::get_reference_range_max(int p_index) const {
|
||||||
|
ERR_FAIL_INDEX_V(p_index, settings.size(), 0);
|
||||||
|
ConvertTransform3DSetting *setting = static_cast<ConvertTransform3DSetting *>(settings[p_index]);
|
||||||
|
return setting->reference_range_max;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConvertTransformModifier3D::set_relative(int p_index, bool p_enabled) {
|
||||||
|
ERR_FAIL_INDEX(p_index, settings.size());
|
||||||
|
ConvertTransform3DSetting *setting = static_cast<ConvertTransform3DSetting *>(settings[p_index]);
|
||||||
|
setting->relative = p_enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConvertTransformModifier3D::is_relative(int p_index) const {
|
||||||
|
ERR_FAIL_INDEX_V(p_index, settings.size(), 0);
|
||||||
|
ConvertTransform3DSetting *setting = static_cast<ConvertTransform3DSetting *>(settings[p_index]);
|
||||||
|
return setting->relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConvertTransformModifier3D::set_additive(int p_index, bool p_enabled) {
|
||||||
|
ERR_FAIL_INDEX(p_index, settings.size());
|
||||||
|
ConvertTransform3DSetting *setting = static_cast<ConvertTransform3DSetting *>(settings[p_index]);
|
||||||
|
setting->additive = p_enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConvertTransformModifier3D::is_additive(int p_index) const {
|
||||||
|
ERR_FAIL_INDEX_V(p_index, settings.size(), 0);
|
||||||
|
ConvertTransform3DSetting *setting = static_cast<ConvertTransform3DSetting *>(settings[p_index]);
|
||||||
|
return setting->additive;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConvertTransformModifier3D::_bind_methods() {
|
||||||
|
ClassDB::bind_method(D_METHOD("set_apply_transform_mode", "index", "transform_mode"), &ConvertTransformModifier3D::set_apply_transform_mode);
|
||||||
|
ClassDB::bind_method(D_METHOD("get_apply_transform_mode", "index"), &ConvertTransformModifier3D::get_apply_transform_mode);
|
||||||
|
ClassDB::bind_method(D_METHOD("set_apply_axis", "index", "axis"), &ConvertTransformModifier3D::set_apply_axis);
|
||||||
|
ClassDB::bind_method(D_METHOD("get_apply_axis", "index"), &ConvertTransformModifier3D::get_apply_axis);
|
||||||
|
ClassDB::bind_method(D_METHOD("set_apply_range_min", "index", "range_min"), &ConvertTransformModifier3D::set_apply_range_min);
|
||||||
|
ClassDB::bind_method(D_METHOD("get_apply_range_min", "index"), &ConvertTransformModifier3D::get_apply_range_min);
|
||||||
|
ClassDB::bind_method(D_METHOD("set_apply_range_max", "index", "range_max"), &ConvertTransformModifier3D::set_apply_range_max);
|
||||||
|
ClassDB::bind_method(D_METHOD("get_apply_range_max", "index"), &ConvertTransformModifier3D::get_apply_range_max);
|
||||||
|
|
||||||
|
ClassDB::bind_method(D_METHOD("set_reference_transform_mode", "index", "transform_mode"), &ConvertTransformModifier3D::set_reference_transform_mode);
|
||||||
|
ClassDB::bind_method(D_METHOD("get_reference_transform_mode", "index"), &ConvertTransformModifier3D::get_reference_transform_mode);
|
||||||
|
ClassDB::bind_method(D_METHOD("set_reference_axis", "index", "axis"), &ConvertTransformModifier3D::set_reference_axis);
|
||||||
|
ClassDB::bind_method(D_METHOD("get_reference_axis", "index"), &ConvertTransformModifier3D::get_reference_axis);
|
||||||
|
ClassDB::bind_method(D_METHOD("set_reference_range_min", "index", "range_min"), &ConvertTransformModifier3D::set_reference_range_min);
|
||||||
|
ClassDB::bind_method(D_METHOD("get_reference_range_min", "index"), &ConvertTransformModifier3D::get_reference_range_min);
|
||||||
|
ClassDB::bind_method(D_METHOD("set_reference_range_max", "index", "range_max"), &ConvertTransformModifier3D::set_reference_range_max);
|
||||||
|
ClassDB::bind_method(D_METHOD("get_reference_range_max", "index"), &ConvertTransformModifier3D::get_reference_range_max);
|
||||||
|
|
||||||
|
ClassDB::bind_method(D_METHOD("set_relative", "index", "enabled"), &ConvertTransformModifier3D::set_relative);
|
||||||
|
ClassDB::bind_method(D_METHOD("is_relative", "index"), &ConvertTransformModifier3D::is_relative);
|
||||||
|
ClassDB::bind_method(D_METHOD("set_additive", "index", "enabled"), &ConvertTransformModifier3D::set_additive);
|
||||||
|
ClassDB::bind_method(D_METHOD("is_additive", "index"), &ConvertTransformModifier3D::is_additive);
|
||||||
|
|
||||||
|
ADD_ARRAY_COUNT("Settings", "setting_count", "set_setting_count", "get_setting_count", "settings/");
|
||||||
|
|
||||||
|
BIND_ENUM_CONSTANT(TRANSFORM_MODE_POSITION);
|
||||||
|
BIND_ENUM_CONSTANT(TRANSFORM_MODE_ROTATION);
|
||||||
|
BIND_ENUM_CONSTANT(TRANSFORM_MODE_SCALE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConvertTransformModifier3D::_process_constraint(int p_index, Skeleton3D *p_skeleton, int p_apply_bone, int p_reference_bone, float p_amount) {
|
||||||
|
ConvertTransform3DSetting *setting = static_cast<ConvertTransform3DSetting *>(settings[p_index]);
|
||||||
|
|
||||||
|
Transform3D destination = p_skeleton->get_bone_pose(p_reference_bone);
|
||||||
|
if (setting->relative) {
|
||||||
|
Vector3 scl_relative = destination.basis.get_scale() / p_skeleton->get_bone_rest(p_reference_bone).basis.get_scale();
|
||||||
|
destination.basis = p_skeleton->get_bone_rest(p_reference_bone).basis.get_rotation_quaternion().inverse() * destination.basis.get_rotation_quaternion();
|
||||||
|
destination.basis.scale_local(scl_relative);
|
||||||
|
destination.origin = destination.origin - p_skeleton->get_bone_rest(p_reference_bone).origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve point from reference.
|
||||||
|
double point = 0.0;
|
||||||
|
int axis = (int)setting->reference_axis;
|
||||||
|
switch (setting->reference_transform_mode) {
|
||||||
|
case TRANSFORM_MODE_POSITION: {
|
||||||
|
point = destination.origin[axis];
|
||||||
|
} break;
|
||||||
|
case TRANSFORM_MODE_ROTATION: {
|
||||||
|
Quaternion tgt_rot = destination.basis.get_rotation_quaternion();
|
||||||
|
point = get_roll_angle(tgt_rot, get_vector_from_axis(setting->reference_axis));
|
||||||
|
} break;
|
||||||
|
case TRANSFORM_MODE_SCALE: {
|
||||||
|
point = destination.basis.get_scale()[axis];
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
// Convert point to apply.
|
||||||
|
destination = p_skeleton->get_bone_pose(p_apply_bone);
|
||||||
|
if (Math::is_equal_approx(setting->reference_range_min, setting->reference_range_max)) {
|
||||||
|
point = point <= (double)setting->reference_range_min ? 0 : 1;
|
||||||
|
} else {
|
||||||
|
point = Math::inverse_lerp((double)setting->reference_range_min, (double)setting->reference_range_max, point);
|
||||||
|
}
|
||||||
|
point = Math::lerp((double)setting->apply_range_min, (double)setting->apply_range_max, CLAMP(point, 0, 1));
|
||||||
|
axis = (int)setting->apply_axis;
|
||||||
|
switch (setting->apply_transform_mode) {
|
||||||
|
case TRANSFORM_MODE_POSITION: {
|
||||||
|
if (setting->additive) {
|
||||||
|
point = p_skeleton->get_bone_pose(p_apply_bone).origin[axis] + point;
|
||||||
|
} else if (setting->relative) {
|
||||||
|
point = p_skeleton->get_bone_rest(p_apply_bone).origin[axis] + point;
|
||||||
|
}
|
||||||
|
destination.origin[axis] = point;
|
||||||
|
} break;
|
||||||
|
case TRANSFORM_MODE_ROTATION: {
|
||||||
|
Vector3 rot_axis = get_vector_from_axis(setting->apply_axis);
|
||||||
|
Vector3 dest_scl = destination.basis.get_scale();
|
||||||
|
if (influence < 1.0 || p_amount < 1.0) {
|
||||||
|
point = CLAMP(point, CMP_EPSILON - Math::PI, Math::PI - CMP_EPSILON); // Hack to consistent slerp (interpolate_with) orientation since -180/180 deg rot is mixed in slerp.
|
||||||
|
}
|
||||||
|
Quaternion rot = Quaternion(rot_axis, point);
|
||||||
|
if (setting->additive) {
|
||||||
|
destination.basis = p_skeleton->get_bone_pose(p_apply_bone).basis.get_rotation_quaternion() * rot;
|
||||||
|
} else if (setting->relative) {
|
||||||
|
destination.basis = p_skeleton->get_bone_rest(p_apply_bone).basis.get_rotation_quaternion() * rot;
|
||||||
|
} else {
|
||||||
|
destination.basis = rot;
|
||||||
|
}
|
||||||
|
// Scale may not have meaning, but it might affect when it is negative.
|
||||||
|
destination.basis.scale_local(dest_scl);
|
||||||
|
} break;
|
||||||
|
case TRANSFORM_MODE_SCALE: {
|
||||||
|
Vector3 dest_scl = Vector3(1, 1, 1);
|
||||||
|
if (setting->additive) {
|
||||||
|
dest_scl = p_skeleton->get_bone_pose(p_apply_bone).basis.get_scale();
|
||||||
|
dest_scl[axis] = dest_scl[axis] * point;
|
||||||
|
} else if (setting->relative) {
|
||||||
|
dest_scl = p_skeleton->get_bone_rest(p_apply_bone).basis.get_scale();
|
||||||
|
dest_scl[axis] = dest_scl[axis] * point;
|
||||||
|
} else {
|
||||||
|
dest_scl = p_skeleton->get_bone_pose(p_apply_bone).basis.get_scale();
|
||||||
|
dest_scl[axis] = point;
|
||||||
|
}
|
||||||
|
destination.basis = destination.basis.orthonormalized().scaled_local(dest_scl);
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
// Process interpolation depends on the amount.
|
||||||
|
destination = p_skeleton->get_bone_pose(p_apply_bone).interpolate_with(destination, p_amount);
|
||||||
|
// Apply transform depends on the mode.
|
||||||
|
switch (setting->apply_transform_mode) {
|
||||||
|
case TRANSFORM_MODE_POSITION: {
|
||||||
|
p_skeleton->set_bone_pose_position(p_apply_bone, destination.origin);
|
||||||
|
} break;
|
||||||
|
case TRANSFORM_MODE_ROTATION: {
|
||||||
|
p_skeleton->set_bone_pose_rotation(p_apply_bone, destination.basis.get_rotation_quaternion());
|
||||||
|
} break;
|
||||||
|
case TRANSFORM_MODE_SCALE: {
|
||||||
|
p_skeleton->set_bone_pose_scale(p_apply_bone, destination.basis.get_scale());
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ConvertTransformModifier3D::~ConvertTransformModifier3D() {
|
||||||
|
clear_settings();
|
||||||
|
}
|
99
scene/3d/convert_transform_modifier_3d.h
Normal file
99
scene/3d/convert_transform_modifier_3d.h
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
/**************************************************************************/
|
||||||
|
/* convert_transform_modifier_3d.h */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||||
|
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* */
|
||||||
|
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||||
|
/* a copy of this software and associated documentation files (the */
|
||||||
|
/* "Software"), to deal in the Software without restriction, including */
|
||||||
|
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||||
|
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||||
|
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||||
|
/* the following conditions: */
|
||||||
|
/* */
|
||||||
|
/* The above copyright notice and this permission notice shall be */
|
||||||
|
/* included in all copies or substantial portions of the Software. */
|
||||||
|
/* */
|
||||||
|
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||||
|
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||||
|
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||||
|
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||||
|
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||||
|
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||||
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||||
|
/**************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "scene/3d/bone_constraint_3d.h"
|
||||||
|
|
||||||
|
class ConvertTransformModifier3D : public BoneConstraint3D {
|
||||||
|
GDCLASS(ConvertTransformModifier3D, BoneConstraint3D);
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum TransformMode {
|
||||||
|
TRANSFORM_MODE_POSITION,
|
||||||
|
TRANSFORM_MODE_ROTATION,
|
||||||
|
TRANSFORM_MODE_SCALE,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ConvertTransform3DSetting : public BoneConstraint3DSetting {
|
||||||
|
TransformMode apply_transform_mode = TRANSFORM_MODE_POSITION;
|
||||||
|
Vector3::Axis apply_axis = Vector3::AXIS_X;
|
||||||
|
float apply_range_min = 0.0;
|
||||||
|
float apply_range_max = 0.0;
|
||||||
|
|
||||||
|
TransformMode reference_transform_mode = TRANSFORM_MODE_POSITION;
|
||||||
|
Vector3::Axis reference_axis = Vector3::AXIS_X;
|
||||||
|
float reference_range_min = 0.0;
|
||||||
|
float reference_range_max = 0.0;
|
||||||
|
|
||||||
|
bool relative = true;
|
||||||
|
bool additive = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
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;
|
||||||
|
|
||||||
|
static void _bind_methods();
|
||||||
|
|
||||||
|
virtual void _process_constraint(int p_index, Skeleton3D *p_skeleton, int p_apply_bone, int p_reference_bone, float p_amount) override;
|
||||||
|
virtual void _validate_setting(int p_index) override;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void set_apply_transform_mode(int p_index, TransformMode p_transform_mode);
|
||||||
|
TransformMode get_apply_transform_mode(int p_index) const;
|
||||||
|
void set_apply_axis(int p_index, Vector3::Axis p_axis);
|
||||||
|
Vector3::Axis get_apply_axis(int p_index) const;
|
||||||
|
void set_apply_range_min(int p_index, float p_range_min);
|
||||||
|
float get_apply_range_min(int p_index) const;
|
||||||
|
void set_apply_range_max(int p_index, float p_range_max);
|
||||||
|
float get_apply_range_max(int p_index) const;
|
||||||
|
|
||||||
|
void set_reference_transform_mode(int p_index, TransformMode p_transform_mode);
|
||||||
|
TransformMode get_reference_transform_mode(int p_index) const;
|
||||||
|
void set_reference_axis(int p_index, Vector3::Axis p_axis);
|
||||||
|
Vector3::Axis get_reference_axis(int p_index) const;
|
||||||
|
void set_reference_range_min(int p_index, float p_range_min);
|
||||||
|
float get_reference_range_min(int p_index) const;
|
||||||
|
void set_reference_range_max(int p_index, float p_range_max);
|
||||||
|
float get_reference_range_max(int p_index) const;
|
||||||
|
|
||||||
|
void set_relative(int p_index, bool p_enabled);
|
||||||
|
bool is_relative(int p_index) const;
|
||||||
|
|
||||||
|
void set_additive(int p_index, bool p_enabled);
|
||||||
|
bool is_additive(int p_index) const;
|
||||||
|
|
||||||
|
~ConvertTransformModifier3D();
|
||||||
|
};
|
||||||
|
|
||||||
|
VARIANT_ENUM_CAST(ConvertTransformModifier3D::TransformMode);
|
456
scene/3d/copy_transform_modifier_3d.cpp
Normal file
456
scene/3d/copy_transform_modifier_3d.cpp
Normal file
@ -0,0 +1,456 @@
|
|||||||
|
/**************************************************************************/
|
||||||
|
/* copy_transform_modifier_3d.cpp */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||||
|
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* */
|
||||||
|
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||||
|
/* a copy of this software and associated documentation files (the */
|
||||||
|
/* "Software"), to deal in the Software without restriction, including */
|
||||||
|
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||||
|
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||||
|
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||||
|
/* the following conditions: */
|
||||||
|
/* */
|
||||||
|
/* The above copyright notice and this permission notice shall be */
|
||||||
|
/* included in all copies or substantial portions of the Software. */
|
||||||
|
/* */
|
||||||
|
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||||
|
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||||
|
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||||
|
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||||
|
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||||
|
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||||
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||||
|
/**************************************************************************/
|
||||||
|
|
||||||
|
#include "copy_transform_modifier_3d.h"
|
||||||
|
|
||||||
|
bool CopyTransformModifier3D::_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, settings.size(), false);
|
||||||
|
|
||||||
|
if (what == "copy") {
|
||||||
|
set_copy_flags(which, static_cast<BitField<TransformFlag>>((int)p_value));
|
||||||
|
} else if (what == "axes") {
|
||||||
|
set_axis_flags(which, static_cast<BitField<AxisFlag>>((int)p_value));
|
||||||
|
} else if (what == "invert") {
|
||||||
|
set_invert_flags(which, static_cast<BitField<AxisFlag>>((int)p_value));
|
||||||
|
} else if (what == "relative") {
|
||||||
|
set_relative(which, p_value);
|
||||||
|
} else if (what == "additive") {
|
||||||
|
set_additive(which, p_value);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CopyTransformModifier3D::_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, settings.size(), false);
|
||||||
|
|
||||||
|
if (what == "copy") {
|
||||||
|
r_ret = (int)get_copy_flags(which);
|
||||||
|
} else if (what == "axes") {
|
||||||
|
r_ret = (int)get_axis_flags(which);
|
||||||
|
} else if (what == "invert") {
|
||||||
|
r_ret = (int)get_invert_flags(which);
|
||||||
|
} else if (what == "relative") {
|
||||||
|
r_ret = is_relative(which);
|
||||||
|
} else if (what == "additive") {
|
||||||
|
r_ret = is_additive(which);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CopyTransformModifier3D::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||||
|
BoneConstraint3D::get_property_list(p_list);
|
||||||
|
|
||||||
|
for (int i = 0; i < settings.size(); i++) {
|
||||||
|
String path = "settings/" + itos(i) + "/";
|
||||||
|
p_list->push_back(PropertyInfo(Variant::INT, path + "copy", PROPERTY_HINT_FLAGS, "Position,Rotation,Scale"));
|
||||||
|
p_list->push_back(PropertyInfo(Variant::INT, path + "axes", PROPERTY_HINT_FLAGS, "X,Y,Z"));
|
||||||
|
p_list->push_back(PropertyInfo(Variant::INT, path + "invert", PROPERTY_HINT_FLAGS, "X,Y,Z"));
|
||||||
|
p_list->push_back(PropertyInfo(Variant::BOOL, path + "relative"));
|
||||||
|
p_list->push_back(PropertyInfo(Variant::BOOL, path + "additive"));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (PropertyInfo &E : *p_list) {
|
||||||
|
_validate_property(E);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CopyTransformModifier3D::_validate_setting(int p_index) {
|
||||||
|
settings.write[p_index] = memnew(CopyTransform3DSetting);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CopyTransformModifier3D::set_copy_flags(int p_index, BitField<TransformFlag> p_copy_flags) {
|
||||||
|
ERR_FAIL_INDEX(p_index, settings.size());
|
||||||
|
CopyTransform3DSetting *setting = static_cast<CopyTransform3DSetting *>(settings[p_index]);
|
||||||
|
setting->copy_flags = p_copy_flags;
|
||||||
|
notify_property_list_changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
BitField<CopyTransformModifier3D::TransformFlag> CopyTransformModifier3D::get_copy_flags(int p_index) const {
|
||||||
|
ERR_FAIL_INDEX_V(p_index, settings.size(), 0);
|
||||||
|
CopyTransform3DSetting *setting = static_cast<CopyTransform3DSetting *>(settings[p_index]);
|
||||||
|
return setting->copy_flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CopyTransformModifier3D::set_axis_flags(int p_index, BitField<AxisFlag> p_axis_flags) {
|
||||||
|
ERR_FAIL_INDEX(p_index, settings.size());
|
||||||
|
CopyTransform3DSetting *setting = static_cast<CopyTransform3DSetting *>(settings[p_index]);
|
||||||
|
setting->axis_flags = p_axis_flags;
|
||||||
|
notify_property_list_changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
BitField<CopyTransformModifier3D::AxisFlag> CopyTransformModifier3D::get_axis_flags(int p_index) const {
|
||||||
|
ERR_FAIL_INDEX_V(p_index, settings.size(), 0);
|
||||||
|
CopyTransform3DSetting *setting = static_cast<CopyTransform3DSetting *>(settings[p_index]);
|
||||||
|
return setting->axis_flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CopyTransformModifier3D::set_invert_flags(int p_index, BitField<AxisFlag> p_axis_flags) {
|
||||||
|
ERR_FAIL_INDEX(p_index, settings.size());
|
||||||
|
CopyTransform3DSetting *setting = static_cast<CopyTransform3DSetting *>(settings[p_index]);
|
||||||
|
setting->invert_flags = p_axis_flags;
|
||||||
|
notify_property_list_changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
BitField<CopyTransformModifier3D::AxisFlag> CopyTransformModifier3D::get_invert_flags(int p_index) const {
|
||||||
|
ERR_FAIL_INDEX_V(p_index, settings.size(), false);
|
||||||
|
CopyTransform3DSetting *setting = static_cast<CopyTransform3DSetting *>(settings[p_index]);
|
||||||
|
return setting->invert_flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CopyTransformModifier3D::set_copy_position(int p_index, bool p_enabled) {
|
||||||
|
ERR_FAIL_INDEX(p_index, settings.size());
|
||||||
|
CopyTransform3DSetting *setting = static_cast<CopyTransform3DSetting *>(settings[p_index]);
|
||||||
|
if (p_enabled) {
|
||||||
|
setting->copy_flags.set_flag(TRANSFORM_FLAG_POSITION);
|
||||||
|
} else {
|
||||||
|
setting->copy_flags.clear_flag(TRANSFORM_FLAG_POSITION);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CopyTransformModifier3D::is_position_copying(int p_index) const {
|
||||||
|
ERR_FAIL_INDEX_V(p_index, settings.size(), false);
|
||||||
|
CopyTransform3DSetting *setting = static_cast<CopyTransform3DSetting *>(settings[p_index]);
|
||||||
|
return setting->copy_flags.has_flag(TRANSFORM_FLAG_POSITION);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CopyTransformModifier3D::set_copy_rotation(int p_index, bool p_enabled) {
|
||||||
|
ERR_FAIL_INDEX(p_index, settings.size());
|
||||||
|
CopyTransform3DSetting *setting = static_cast<CopyTransform3DSetting *>(settings[p_index]);
|
||||||
|
if (p_enabled) {
|
||||||
|
setting->copy_flags.set_flag(TRANSFORM_FLAG_ROTATION);
|
||||||
|
} else {
|
||||||
|
setting->copy_flags.clear_flag(TRANSFORM_FLAG_ROTATION);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CopyTransformModifier3D::is_rotation_copying(int p_index) const {
|
||||||
|
ERR_FAIL_INDEX_V(p_index, settings.size(), false);
|
||||||
|
CopyTransform3DSetting *setting = static_cast<CopyTransform3DSetting *>(settings[p_index]);
|
||||||
|
return setting->copy_flags.has_flag(TRANSFORM_FLAG_ROTATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CopyTransformModifier3D::set_copy_scale(int p_index, bool p_enabled) {
|
||||||
|
ERR_FAIL_INDEX(p_index, settings.size());
|
||||||
|
CopyTransform3DSetting *setting = static_cast<CopyTransform3DSetting *>(settings[p_index]);
|
||||||
|
if (p_enabled) {
|
||||||
|
setting->copy_flags.set_flag(TRANSFORM_FLAG_SCALE);
|
||||||
|
} else {
|
||||||
|
setting->copy_flags.clear_flag(TRANSFORM_FLAG_SCALE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CopyTransformModifier3D::is_scale_copying(int p_index) const {
|
||||||
|
ERR_FAIL_INDEX_V(p_index, settings.size(), false);
|
||||||
|
CopyTransform3DSetting *setting = static_cast<CopyTransform3DSetting *>(settings[p_index]);
|
||||||
|
return setting->copy_flags.has_flag(TRANSFORM_FLAG_SCALE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CopyTransformModifier3D::set_axis_x_enabled(int p_index, bool p_enabled) {
|
||||||
|
ERR_FAIL_INDEX(p_index, settings.size());
|
||||||
|
CopyTransform3DSetting *setting = static_cast<CopyTransform3DSetting *>(settings[p_index]);
|
||||||
|
if (p_enabled) {
|
||||||
|
setting->axis_flags.set_flag(AXIS_FLAG_X);
|
||||||
|
} else {
|
||||||
|
setting->axis_flags.clear_flag(AXIS_FLAG_X);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CopyTransformModifier3D::is_axis_x_enabled(int p_index) const {
|
||||||
|
ERR_FAIL_INDEX_V(p_index, settings.size(), false);
|
||||||
|
CopyTransform3DSetting *setting = static_cast<CopyTransform3DSetting *>(settings[p_index]);
|
||||||
|
return setting->axis_flags.has_flag(AXIS_FLAG_X);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CopyTransformModifier3D::set_axis_y_enabled(int p_index, bool p_enabled) {
|
||||||
|
ERR_FAIL_INDEX(p_index, settings.size());
|
||||||
|
CopyTransform3DSetting *setting = static_cast<CopyTransform3DSetting *>(settings[p_index]);
|
||||||
|
if (p_enabled) {
|
||||||
|
setting->axis_flags.set_flag(AXIS_FLAG_Y);
|
||||||
|
} else {
|
||||||
|
setting->axis_flags.clear_flag(AXIS_FLAG_Y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CopyTransformModifier3D::is_axis_y_enabled(int p_index) const {
|
||||||
|
ERR_FAIL_INDEX_V(p_index, settings.size(), false);
|
||||||
|
CopyTransform3DSetting *setting = static_cast<CopyTransform3DSetting *>(settings[p_index]);
|
||||||
|
return setting->axis_flags.has_flag(AXIS_FLAG_Y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CopyTransformModifier3D::set_axis_z_enabled(int p_index, bool p_enabled) {
|
||||||
|
ERR_FAIL_INDEX(p_index, settings.size());
|
||||||
|
CopyTransform3DSetting *setting = static_cast<CopyTransform3DSetting *>(settings[p_index]);
|
||||||
|
if (p_enabled) {
|
||||||
|
setting->axis_flags.set_flag(AXIS_FLAG_Z);
|
||||||
|
} else {
|
||||||
|
setting->axis_flags.clear_flag(AXIS_FLAG_Z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CopyTransformModifier3D::is_axis_z_enabled(int p_index) const {
|
||||||
|
ERR_FAIL_INDEX_V(p_index, settings.size(), false);
|
||||||
|
CopyTransform3DSetting *setting = static_cast<CopyTransform3DSetting *>(settings[p_index]);
|
||||||
|
return setting->axis_flags.has_flag(AXIS_FLAG_Z);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CopyTransformModifier3D::set_axis_x_inverted(int p_index, bool p_enabled) {
|
||||||
|
ERR_FAIL_INDEX(p_index, settings.size());
|
||||||
|
CopyTransform3DSetting *setting = static_cast<CopyTransform3DSetting *>(settings[p_index]);
|
||||||
|
if (p_enabled) {
|
||||||
|
setting->invert_flags.set_flag(AXIS_FLAG_X);
|
||||||
|
} else {
|
||||||
|
setting->invert_flags.clear_flag(AXIS_FLAG_X);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CopyTransformModifier3D::is_axis_x_inverted(int p_index) const {
|
||||||
|
ERR_FAIL_INDEX_V(p_index, settings.size(), false);
|
||||||
|
CopyTransform3DSetting *setting = static_cast<CopyTransform3DSetting *>(settings[p_index]);
|
||||||
|
return setting->invert_flags.has_flag(AXIS_FLAG_X);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CopyTransformModifier3D::set_axis_y_inverted(int p_index, bool p_enabled) {
|
||||||
|
ERR_FAIL_INDEX(p_index, settings.size());
|
||||||
|
CopyTransform3DSetting *setting = static_cast<CopyTransform3DSetting *>(settings[p_index]);
|
||||||
|
if (p_enabled) {
|
||||||
|
setting->invert_flags.set_flag(AXIS_FLAG_Y);
|
||||||
|
} else {
|
||||||
|
setting->invert_flags.clear_flag(AXIS_FLAG_Y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CopyTransformModifier3D::is_axis_y_inverted(int p_index) const {
|
||||||
|
ERR_FAIL_INDEX_V(p_index, settings.size(), false);
|
||||||
|
CopyTransform3DSetting *setting = static_cast<CopyTransform3DSetting *>(settings[p_index]);
|
||||||
|
return setting->invert_flags.has_flag(AXIS_FLAG_Y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CopyTransformModifier3D::set_axis_z_inverted(int p_index, bool p_enabled) {
|
||||||
|
ERR_FAIL_INDEX(p_index, settings.size());
|
||||||
|
CopyTransform3DSetting *setting = static_cast<CopyTransform3DSetting *>(settings[p_index]);
|
||||||
|
if (p_enabled) {
|
||||||
|
setting->invert_flags.set_flag(AXIS_FLAG_Z);
|
||||||
|
} else {
|
||||||
|
setting->invert_flags.clear_flag(AXIS_FLAG_Z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CopyTransformModifier3D::is_axis_z_inverted(int p_index) const {
|
||||||
|
ERR_FAIL_INDEX_V(p_index, settings.size(), false);
|
||||||
|
CopyTransform3DSetting *setting = static_cast<CopyTransform3DSetting *>(settings[p_index]);
|
||||||
|
return setting->invert_flags.has_flag(AXIS_FLAG_Z);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CopyTransformModifier3D::set_relative(int p_index, bool p_enabled) {
|
||||||
|
ERR_FAIL_INDEX(p_index, settings.size());
|
||||||
|
CopyTransform3DSetting *setting = static_cast<CopyTransform3DSetting *>(settings[p_index]);
|
||||||
|
setting->relative = p_enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CopyTransformModifier3D::is_relative(int p_index) const {
|
||||||
|
ERR_FAIL_INDEX_V(p_index, settings.size(), 0);
|
||||||
|
CopyTransform3DSetting *setting = static_cast<CopyTransform3DSetting *>(settings[p_index]);
|
||||||
|
return setting->relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CopyTransformModifier3D::set_additive(int p_index, bool p_enabled) {
|
||||||
|
ERR_FAIL_INDEX(p_index, settings.size());
|
||||||
|
CopyTransform3DSetting *setting = static_cast<CopyTransform3DSetting *>(settings[p_index]);
|
||||||
|
setting->additive = p_enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CopyTransformModifier3D::is_additive(int p_index) const {
|
||||||
|
ERR_FAIL_INDEX_V(p_index, settings.size(), 0);
|
||||||
|
CopyTransform3DSetting *setting = static_cast<CopyTransform3DSetting *>(settings[p_index]);
|
||||||
|
return setting->additive;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CopyTransformModifier3D::_bind_methods() {
|
||||||
|
ClassDB::bind_method(D_METHOD("set_copy_flags", "index", "copy_flags"), &CopyTransformModifier3D::set_copy_flags);
|
||||||
|
ClassDB::bind_method(D_METHOD("get_copy_flags", "index"), &CopyTransformModifier3D::get_copy_flags);
|
||||||
|
ClassDB::bind_method(D_METHOD("set_axis_flags", "index", "axis_flags"), &CopyTransformModifier3D::set_axis_flags);
|
||||||
|
ClassDB::bind_method(D_METHOD("get_axis_flags", "index"), &CopyTransformModifier3D::get_axis_flags);
|
||||||
|
ClassDB::bind_method(D_METHOD("set_invert_flags", "index", "axis_flags"), &CopyTransformModifier3D::set_invert_flags);
|
||||||
|
ClassDB::bind_method(D_METHOD("get_invert_flags", "index"), &CopyTransformModifier3D::get_invert_flags);
|
||||||
|
|
||||||
|
ClassDB::bind_method(D_METHOD("set_copy_position", "index", "enabled"), &CopyTransformModifier3D::set_copy_position);
|
||||||
|
ClassDB::bind_method(D_METHOD("is_position_copying", "index"), &CopyTransformModifier3D::is_position_copying);
|
||||||
|
ClassDB::bind_method(D_METHOD("set_copy_rotation", "index", "enabled"), &CopyTransformModifier3D::set_copy_rotation);
|
||||||
|
ClassDB::bind_method(D_METHOD("is_rotation_copying", "index"), &CopyTransformModifier3D::is_rotation_copying);
|
||||||
|
ClassDB::bind_method(D_METHOD("set_copy_scale", "index", "enabled"), &CopyTransformModifier3D::set_copy_scale);
|
||||||
|
ClassDB::bind_method(D_METHOD("is_scale_copying", "index"), &CopyTransformModifier3D::is_scale_copying);
|
||||||
|
|
||||||
|
ClassDB::bind_method(D_METHOD("set_axis_x_enabled", "index", "enabled"), &CopyTransformModifier3D::set_axis_x_enabled);
|
||||||
|
ClassDB::bind_method(D_METHOD("is_axis_x_enabled", "index"), &CopyTransformModifier3D::is_axis_x_enabled);
|
||||||
|
ClassDB::bind_method(D_METHOD("set_axis_y_enabled", "index", "enabled"), &CopyTransformModifier3D::set_axis_y_enabled);
|
||||||
|
ClassDB::bind_method(D_METHOD("is_axis_y_enabled", "index"), &CopyTransformModifier3D::is_axis_y_enabled);
|
||||||
|
ClassDB::bind_method(D_METHOD("set_axis_z_enabled", "index", "enabled"), &CopyTransformModifier3D::set_axis_z_enabled);
|
||||||
|
ClassDB::bind_method(D_METHOD("is_axis_z_enabled", "index"), &CopyTransformModifier3D::is_axis_z_enabled);
|
||||||
|
|
||||||
|
ClassDB::bind_method(D_METHOD("set_axis_x_inverted", "index", "enabled"), &CopyTransformModifier3D::set_axis_x_inverted);
|
||||||
|
ClassDB::bind_method(D_METHOD("is_axis_x_inverted", "index"), &CopyTransformModifier3D::is_axis_x_inverted);
|
||||||
|
ClassDB::bind_method(D_METHOD("set_axis_y_inverted", "index", "enabled"), &CopyTransformModifier3D::set_axis_y_inverted);
|
||||||
|
ClassDB::bind_method(D_METHOD("is_axis_y_inverted", "index"), &CopyTransformModifier3D::is_axis_y_inverted);
|
||||||
|
ClassDB::bind_method(D_METHOD("set_axis_z_inverted", "index", "enabled"), &CopyTransformModifier3D::set_axis_z_inverted);
|
||||||
|
ClassDB::bind_method(D_METHOD("is_axis_z_inverted", "index"), &CopyTransformModifier3D::is_axis_z_inverted);
|
||||||
|
|
||||||
|
ClassDB::bind_method(D_METHOD("set_relative", "index", "enabled"), &CopyTransformModifier3D::set_relative);
|
||||||
|
ClassDB::bind_method(D_METHOD("is_relative", "index"), &CopyTransformModifier3D::is_relative);
|
||||||
|
ClassDB::bind_method(D_METHOD("set_additive", "index", "enabled"), &CopyTransformModifier3D::set_additive);
|
||||||
|
ClassDB::bind_method(D_METHOD("is_additive", "index"), &CopyTransformModifier3D::is_additive);
|
||||||
|
|
||||||
|
ADD_ARRAY_COUNT("Settings", "setting_count", "set_setting_count", "get_setting_count", "settings/");
|
||||||
|
|
||||||
|
BIND_BITFIELD_FLAG(TRANSFORM_FLAG_POSITION);
|
||||||
|
BIND_BITFIELD_FLAG(TRANSFORM_FLAG_ROTATION);
|
||||||
|
BIND_BITFIELD_FLAG(TRANSFORM_FLAG_SCALE);
|
||||||
|
BIND_BITFIELD_FLAG(TRANSFORM_FLAG_ALL);
|
||||||
|
|
||||||
|
BIND_BITFIELD_FLAG(AXIS_FLAG_X);
|
||||||
|
BIND_BITFIELD_FLAG(AXIS_FLAG_Y);
|
||||||
|
BIND_BITFIELD_FLAG(AXIS_FLAG_Z);
|
||||||
|
BIND_BITFIELD_FLAG(AXIS_FLAG_ALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CopyTransformModifier3D::_process_constraint(int p_index, Skeleton3D *p_skeleton, int p_apply_bone, int p_reference_bone, float p_amount) {
|
||||||
|
CopyTransform3DSetting *setting = static_cast<CopyTransform3DSetting *>(settings[p_index]);
|
||||||
|
|
||||||
|
Transform3D destination = p_skeleton->get_bone_pose(p_reference_bone);
|
||||||
|
if (setting->relative) {
|
||||||
|
Vector3 scl_relative = destination.basis.get_scale() / p_skeleton->get_bone_rest(p_reference_bone).basis.get_scale();
|
||||||
|
destination.basis = p_skeleton->get_bone_rest(p_reference_bone).basis.get_rotation_quaternion().inverse() * destination.basis.get_rotation_quaternion();
|
||||||
|
destination.basis.scale_local(scl_relative);
|
||||||
|
destination.origin = destination.origin - p_skeleton->get_bone_rest(p_reference_bone).origin;
|
||||||
|
}
|
||||||
|
Vector3 dest_pos = destination.origin;
|
||||||
|
Quaternion dest_rot = destination.basis.get_rotation_quaternion();
|
||||||
|
Vector3 dest_scl = destination.basis.get_scale();
|
||||||
|
|
||||||
|
// Mask pos and scale.
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
if (!setting->axis_flags.has_flag(static_cast<AxisFlag>(1 << i))) {
|
||||||
|
dest_pos[i] = 0.0;
|
||||||
|
dest_scl[i] = 1.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mask rot.
|
||||||
|
switch (static_cast<int>(setting->axis_flags)) {
|
||||||
|
case 0: {
|
||||||
|
dest_rot = Quaternion();
|
||||||
|
} break;
|
||||||
|
case AXIS_FLAG_X: {
|
||||||
|
Vector3 axis = get_vector_from_axis(Vector3::AXIS_X);
|
||||||
|
dest_rot = Quaternion(axis, get_roll_angle(dest_rot, axis));
|
||||||
|
} break;
|
||||||
|
case AXIS_FLAG_Y: {
|
||||||
|
Vector3 axis = get_vector_from_axis(Vector3::AXIS_Y);
|
||||||
|
dest_rot = Quaternion(axis, get_roll_angle(dest_rot, axis));
|
||||||
|
} break;
|
||||||
|
case AXIS_FLAG_Z: {
|
||||||
|
Vector3 axis = get_vector_from_axis(Vector3::AXIS_Z);
|
||||||
|
dest_rot = Quaternion(axis, get_roll_angle(dest_rot, axis));
|
||||||
|
} break;
|
||||||
|
case AXIS_FLAG_X | AXIS_FLAG_Y: {
|
||||||
|
Vector3 axis = get_vector_from_axis(Vector3::AXIS_Z);
|
||||||
|
dest_rot = dest_rot * Quaternion(axis, get_roll_angle(dest_rot, axis)).inverse();
|
||||||
|
} break;
|
||||||
|
case AXIS_FLAG_Y | AXIS_FLAG_Z: {
|
||||||
|
Vector3 axis = get_vector_from_axis(Vector3::AXIS_X);
|
||||||
|
dest_rot = dest_rot * Quaternion(axis, get_roll_angle(dest_rot, axis)).inverse();
|
||||||
|
} break;
|
||||||
|
case AXIS_FLAG_Z | AXIS_FLAG_X: {
|
||||||
|
Vector3 axis = get_vector_from_axis(Vector3::AXIS_Y);
|
||||||
|
dest_rot = dest_rot * Quaternion(axis, get_roll_angle(dest_rot, axis)).inverse();
|
||||||
|
} break;
|
||||||
|
case AXIS_FLAG_ALL: {
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process inversion.
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
AxisFlag axis = static_cast<AxisFlag>(1 << i);
|
||||||
|
if (setting->axis_flags.has_flag(axis) && setting->invert_flags.has_flag(axis)) {
|
||||||
|
dest_pos[i] *= -1;
|
||||||
|
dest_rot[i] *= -1;
|
||||||
|
dest_scl[i] = 1.0 / dest_scl[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dest_rot.normalize();
|
||||||
|
|
||||||
|
if (setting->additive) {
|
||||||
|
destination.origin = p_skeleton->get_bone_pose_position(p_apply_bone) + dest_pos;
|
||||||
|
destination.basis = p_skeleton->get_bone_pose_rotation(p_apply_bone) * Basis(dest_rot);
|
||||||
|
destination.basis.scale_local(p_skeleton->get_bone_pose_scale(p_apply_bone) * dest_scl);
|
||||||
|
} else if (setting->relative) {
|
||||||
|
Transform3D rest = p_skeleton->get_bone_rest(p_apply_bone);
|
||||||
|
destination.origin = rest.origin + dest_pos;
|
||||||
|
destination.basis = rest.basis.get_rotation_quaternion() * Basis(dest_rot);
|
||||||
|
destination.basis.scale_local(rest.basis.get_scale() * dest_scl);
|
||||||
|
} else {
|
||||||
|
destination.origin = dest_pos;
|
||||||
|
destination.basis = Basis(dest_rot);
|
||||||
|
destination.basis.scale_local(dest_scl);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process interpolation depends on the amount.
|
||||||
|
destination = p_skeleton->get_bone_pose(p_apply_bone).interpolate_with(destination, p_amount);
|
||||||
|
|
||||||
|
// Apply transform depends on the element mask.
|
||||||
|
if (setting->copy_flags.has_flag(TRANSFORM_FLAG_POSITION)) {
|
||||||
|
p_skeleton->set_bone_pose_position(p_apply_bone, destination.origin);
|
||||||
|
}
|
||||||
|
if (setting->copy_flags.has_flag(TRANSFORM_FLAG_ROTATION)) {
|
||||||
|
p_skeleton->set_bone_pose_rotation(p_apply_bone, destination.basis.get_rotation_quaternion());
|
||||||
|
}
|
||||||
|
if (setting->copy_flags.has_flag(TRANSFORM_FLAG_SCALE)) {
|
||||||
|
p_skeleton->set_bone_pose_scale(p_apply_bone, destination.basis.get_scale());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CopyTransformModifier3D::~CopyTransformModifier3D() {
|
||||||
|
clear_settings();
|
||||||
|
}
|
112
scene/3d/copy_transform_modifier_3d.h
Normal file
112
scene/3d/copy_transform_modifier_3d.h
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
/**************************************************************************/
|
||||||
|
/* copy_transform_modifier_3d.h */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||||
|
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* */
|
||||||
|
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||||
|
/* a copy of this software and associated documentation files (the */
|
||||||
|
/* "Software"), to deal in the Software without restriction, including */
|
||||||
|
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||||
|
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||||
|
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||||
|
/* the following conditions: */
|
||||||
|
/* */
|
||||||
|
/* The above copyright notice and this permission notice shall be */
|
||||||
|
/* included in all copies or substantial portions of the Software. */
|
||||||
|
/* */
|
||||||
|
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||||
|
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||||
|
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||||
|
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||||
|
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||||
|
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||||
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||||
|
/**************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "scene/3d/bone_constraint_3d.h"
|
||||||
|
|
||||||
|
class CopyTransformModifier3D : public BoneConstraint3D {
|
||||||
|
GDCLASS(CopyTransformModifier3D, BoneConstraint3D);
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum TransformFlag {
|
||||||
|
TRANSFORM_FLAG_POSITION = 1,
|
||||||
|
TRANSFORM_FLAG_ROTATION = 2,
|
||||||
|
TRANSFORM_FLAG_SCALE = 4,
|
||||||
|
TRANSFORM_FLAG_ALL = TRANSFORM_FLAG_POSITION | TRANSFORM_FLAG_ROTATION | TRANSFORM_FLAG_SCALE,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum AxisFlag {
|
||||||
|
AXIS_FLAG_X = 1,
|
||||||
|
AXIS_FLAG_Y = 2,
|
||||||
|
AXIS_FLAG_Z = 4,
|
||||||
|
AXIS_FLAG_ALL = AXIS_FLAG_X | AXIS_FLAG_Y | AXIS_FLAG_Z,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CopyTransform3DSetting : public BoneConstraint3DSetting {
|
||||||
|
BitField<TransformFlag> copy_flags = TRANSFORM_FLAG_ALL;
|
||||||
|
BitField<AxisFlag> axis_flags = AXIS_FLAG_ALL;
|
||||||
|
BitField<AxisFlag> invert_flags = 0;
|
||||||
|
bool relative = true;
|
||||||
|
bool additive = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
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;
|
||||||
|
|
||||||
|
static void _bind_methods();
|
||||||
|
|
||||||
|
virtual void _process_constraint(int p_index, Skeleton3D *p_skeleton, int p_apply_bone, int p_reference_bone, float p_amount) override;
|
||||||
|
virtual void _validate_setting(int p_index) override;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void set_copy_flags(int p_index, BitField<TransformFlag> p_copy_flags);
|
||||||
|
BitField<TransformFlag> get_copy_flags(int p_index) const;
|
||||||
|
|
||||||
|
void set_copy_position(int p_index, bool p_enabled);
|
||||||
|
bool is_position_copying(int p_index) const;
|
||||||
|
void set_copy_rotation(int p_index, bool p_enabled);
|
||||||
|
bool is_rotation_copying(int p_index) const;
|
||||||
|
void set_copy_scale(int p_index, bool p_enabled);
|
||||||
|
bool is_scale_copying(int p_index) const;
|
||||||
|
|
||||||
|
void set_axis_flags(int p_index, BitField<AxisFlag> p_axis_flags);
|
||||||
|
BitField<AxisFlag> get_axis_flags(int p_index) const;
|
||||||
|
|
||||||
|
void set_axis_x_enabled(int p_index, bool p_enabled);
|
||||||
|
bool is_axis_x_enabled(int p_index) const;
|
||||||
|
void set_axis_y_enabled(int p_index, bool p_enabled);
|
||||||
|
bool is_axis_y_enabled(int p_index) const;
|
||||||
|
void set_axis_z_enabled(int p_index, bool p_enabled);
|
||||||
|
bool is_axis_z_enabled(int p_index) const;
|
||||||
|
|
||||||
|
void set_invert_flags(int p_index, BitField<AxisFlag> p_axis_flags);
|
||||||
|
BitField<AxisFlag> get_invert_flags(int p_index) const;
|
||||||
|
|
||||||
|
void set_axis_x_inverted(int p_index, bool p_enabled);
|
||||||
|
bool is_axis_x_inverted(int p_index) const;
|
||||||
|
void set_axis_y_inverted(int p_index, bool p_enabled);
|
||||||
|
bool is_axis_y_inverted(int p_index) const;
|
||||||
|
void set_axis_z_inverted(int p_index, bool p_enabled);
|
||||||
|
bool is_axis_z_inverted(int p_index) const;
|
||||||
|
|
||||||
|
void set_relative(int p_index, bool p_enabled);
|
||||||
|
bool is_relative(int p_index) const;
|
||||||
|
|
||||||
|
void set_additive(int p_index, bool p_enabled);
|
||||||
|
bool is_additive(int p_index) const;
|
||||||
|
|
||||||
|
~CopyTransformModifier3D();
|
||||||
|
};
|
||||||
|
|
||||||
|
VARIANT_BITFIELD_CAST(CopyTransformModifier3D::TransformFlag);
|
||||||
|
VARIANT_BITFIELD_CAST(CopyTransformModifier3D::AxisFlag);
|
@ -214,10 +214,14 @@
|
|||||||
#endif // NAVIGATION_2D_DISABLED
|
#endif // NAVIGATION_2D_DISABLED
|
||||||
|
|
||||||
#ifndef _3D_DISABLED
|
#ifndef _3D_DISABLED
|
||||||
|
#include "scene/3d/aim_modifier_3d.h"
|
||||||
#include "scene/3d/audio_listener_3d.h"
|
#include "scene/3d/audio_listener_3d.h"
|
||||||
#include "scene/3d/audio_stream_player_3d.h"
|
#include "scene/3d/audio_stream_player_3d.h"
|
||||||
#include "scene/3d/bone_attachment_3d.h"
|
#include "scene/3d/bone_attachment_3d.h"
|
||||||
|
#include "scene/3d/bone_constraint_3d.h"
|
||||||
#include "scene/3d/camera_3d.h"
|
#include "scene/3d/camera_3d.h"
|
||||||
|
#include "scene/3d/convert_transform_modifier_3d.h"
|
||||||
|
#include "scene/3d/copy_transform_modifier_3d.h"
|
||||||
#include "scene/3d/cpu_particles_3d.h"
|
#include "scene/3d/cpu_particles_3d.h"
|
||||||
#include "scene/3d/decal.h"
|
#include "scene/3d/decal.h"
|
||||||
#include "scene/3d/fog_volume.h"
|
#include "scene/3d/fog_volume.h"
|
||||||
@ -650,6 +654,10 @@ void register_scene_types() {
|
|||||||
GDREGISTER_CLASS(SpringBoneCollisionSphere3D);
|
GDREGISTER_CLASS(SpringBoneCollisionSphere3D);
|
||||||
GDREGISTER_CLASS(SpringBoneCollisionCapsule3D);
|
GDREGISTER_CLASS(SpringBoneCollisionCapsule3D);
|
||||||
GDREGISTER_CLASS(SpringBoneCollisionPlane3D);
|
GDREGISTER_CLASS(SpringBoneCollisionPlane3D);
|
||||||
|
GDREGISTER_VIRTUAL_CLASS(BoneConstraint3D);
|
||||||
|
GDREGISTER_CLASS(CopyTransformModifier3D);
|
||||||
|
GDREGISTER_CLASS(ConvertTransformModifier3D);
|
||||||
|
GDREGISTER_CLASS(AimModifier3D);
|
||||||
|
|
||||||
OS::get_singleton()->yield(); // may take time to init
|
OS::get_singleton()->yield(); // may take time to init
|
||||||
|
|
||||||
|
258
tests/scene/test_convert_transform_modifier_3d.h
Normal file
258
tests/scene/test_convert_transform_modifier_3d.h
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
/**************************************************************************/
|
||||||
|
/* test_convert_transform_modifier_3d.h */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||||
|
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* */
|
||||||
|
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||||
|
/* a copy of this software and associated documentation files (the */
|
||||||
|
/* "Software"), to deal in the Software without restriction, including */
|
||||||
|
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||||
|
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||||
|
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||||
|
/* the following conditions: */
|
||||||
|
/* */
|
||||||
|
/* The above copyright notice and this permission notice shall be */
|
||||||
|
/* included in all copies or substantial portions of the Software. */
|
||||||
|
/* */
|
||||||
|
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||||
|
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||||
|
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||||
|
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||||
|
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||||
|
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||||
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||||
|
/**************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "tests/test_macros.h"
|
||||||
|
|
||||||
|
#include "scene/3d/bone_attachment_3d.h"
|
||||||
|
#include "scene/3d/convert_transform_modifier_3d.h"
|
||||||
|
|
||||||
|
namespace TestConvertTransformModifier3D {
|
||||||
|
|
||||||
|
Transform3D make_random_transform_3d(int p_seed) {
|
||||||
|
RandomNumberGenerator rng;
|
||||||
|
rng.set_seed(p_seed);
|
||||||
|
|
||||||
|
Vector3 pos;
|
||||||
|
pos.x = rng.randf_range(-10.0, 10.0);
|
||||||
|
rng.set_seed(++p_seed);
|
||||||
|
pos.y = rng.randf_range(-10.0, 10.0);
|
||||||
|
rng.set_seed(++p_seed);
|
||||||
|
pos.z = rng.randf_range(-10.0, 10.0);
|
||||||
|
rng.set_seed(++p_seed);
|
||||||
|
|
||||||
|
Quaternion rot;
|
||||||
|
rot.x = rng.randf_range(-1.0, 1.0);
|
||||||
|
rng.set_seed(++p_seed);
|
||||||
|
rot.y = rng.randf_range(-1.0, 1.0);
|
||||||
|
rng.set_seed(++p_seed);
|
||||||
|
rot.z = rng.randf_range(-1.0, 1.0);
|
||||||
|
rng.set_seed(++p_seed);
|
||||||
|
rot.w = rng.randf_range(-1.0, 1.0);
|
||||||
|
rng.set_seed(++p_seed);
|
||||||
|
rot = rot.normalized();
|
||||||
|
|
||||||
|
Vector3 scl;
|
||||||
|
scl.x = rng.randf_range(0.5, 2.0);
|
||||||
|
rng.set_seed(++p_seed);
|
||||||
|
scl.y = rng.randf_range(0.5, 2.0);
|
||||||
|
rng.set_seed(++p_seed);
|
||||||
|
scl.z = rng.randf_range(0.5, 2.0);
|
||||||
|
rng.set_seed(++p_seed);
|
||||||
|
|
||||||
|
return Transform3D(Basis(rot).scaled(scl), pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("[SceneTree][ConvertTransformModifier3D]") {
|
||||||
|
SceneTree *tree = SceneTree::get_singleton();
|
||||||
|
int seed = 12345;
|
||||||
|
Skeleton3D *skeleton = memnew(Skeleton3D);
|
||||||
|
ConvertTransformModifier3D *mod = memnew(ConvertTransformModifier3D);
|
||||||
|
|
||||||
|
// Instead of awaiting the process to wait to finish deferred process and watch "skeleton_updated" signal,
|
||||||
|
// force notify NOTIFICATION_UPDATE_SKELETON and get the modified pose from the BoneAttachment's transform.
|
||||||
|
BoneAttachment3D *modified = memnew(BoneAttachment3D);
|
||||||
|
|
||||||
|
tree->get_root()->add_child(skeleton);
|
||||||
|
|
||||||
|
int root = skeleton->add_bone("root");
|
||||||
|
skeleton->set_bone_rest(root, make_random_transform_3d(++seed));
|
||||||
|
skeleton->set_bone_pose(root, make_random_transform_3d(++seed));
|
||||||
|
|
||||||
|
int apl_root = skeleton->add_bone("apl_root");
|
||||||
|
skeleton->set_bone_parent(apl_root, root);
|
||||||
|
skeleton->set_bone_rest(apl_root, make_random_transform_3d(++seed));
|
||||||
|
skeleton->set_bone_pose(apl_root, make_random_transform_3d(++seed));
|
||||||
|
|
||||||
|
int apl_bone = skeleton->add_bone("apl_bone");
|
||||||
|
skeleton->set_bone_parent(apl_bone, apl_root);
|
||||||
|
skeleton->set_bone_rest(apl_bone, make_random_transform_3d(++seed));
|
||||||
|
skeleton->set_bone_pose(apl_bone, make_random_transform_3d(++seed));
|
||||||
|
|
||||||
|
int tgt_root = skeleton->add_bone("tgt_root");
|
||||||
|
skeleton->set_bone_parent(tgt_root, root);
|
||||||
|
skeleton->set_bone_rest(tgt_root, make_random_transform_3d(++seed));
|
||||||
|
skeleton->set_bone_pose(tgt_root, make_random_transform_3d(++seed));
|
||||||
|
|
||||||
|
int tgt_bone = skeleton->add_bone("tgt_bone");
|
||||||
|
skeleton->set_bone_parent(tgt_bone, tgt_root);
|
||||||
|
skeleton->set_bone_rest(tgt_bone, make_random_transform_3d(++seed));
|
||||||
|
skeleton->set_bone_pose(tgt_bone, make_random_transform_3d(++seed));
|
||||||
|
|
||||||
|
skeleton->add_child(mod);
|
||||||
|
skeleton->add_child(modified);
|
||||||
|
modified->set_rotation_edit_mode(Node3D::ROTATION_EDIT_MODE_QUATERNION);
|
||||||
|
modified->set_bone_idx(apl_bone);
|
||||||
|
|
||||||
|
mod->set_setting_count(1);
|
||||||
|
mod->set_reference_bone(0, tgt_bone);
|
||||||
|
mod->set_apply_bone(0, apl_bone);
|
||||||
|
|
||||||
|
mod->set_reference_axis(0, Vector3::AXIS_X);
|
||||||
|
mod->set_apply_axis(0, Vector3::AXIS_Y);
|
||||||
|
|
||||||
|
// ===== [ConvertTransformModifier3D] Position x to y =====
|
||||||
|
mod->set_reference_transform_mode(0, ConvertTransformModifier3D::TRANSFORM_MODE_POSITION);
|
||||||
|
mod->set_reference_range_min(0, -100.0);
|
||||||
|
mod->set_reference_range_max(0, 100.0);
|
||||||
|
mod->set_apply_transform_mode(0, ConvertTransformModifier3D::TRANSFORM_MODE_POSITION);
|
||||||
|
mod->set_apply_range_min(0, -100.0);
|
||||||
|
mod->set_apply_range_max(0, 100.0);
|
||||||
|
|
||||||
|
SUBCASE("[ConvertTransformModifier3D] Position x to y, additive=false, relative=false") {
|
||||||
|
mod->set_additive(0, false);
|
||||||
|
mod->set_relative(0, false);
|
||||||
|
skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);
|
||||||
|
CHECK(Math::is_equal_approx(
|
||||||
|
skeleton->get_bone_pose_position(tgt_bone).x,
|
||||||
|
(skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("[ConvertTransformModifier3D] Position x to y, additive=true, relative=false") {
|
||||||
|
mod->set_additive(0, true);
|
||||||
|
mod->set_relative(0, false);
|
||||||
|
skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);
|
||||||
|
CHECK(Math::is_equal_approx(
|
||||||
|
skeleton->get_bone_pose_position(tgt_bone).x,
|
||||||
|
((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_pose_position(apl_bone)).y));
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("[ConvertTransformModifier3D] Position x to y, additive=false, relative=true") {
|
||||||
|
mod->set_additive(0, false);
|
||||||
|
mod->set_relative(0, true);
|
||||||
|
skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);
|
||||||
|
CHECK(Math::is_equal_approx(
|
||||||
|
(skeleton->get_bone_pose_position(tgt_bone) - skeleton->get_bone_rest(tgt_bone).origin).x,
|
||||||
|
((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_rest(apl_bone).origin).y));
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("[ConvertTransformModifier3D] Position x to y, additive=true, relative=true") {
|
||||||
|
mod->set_additive(0, true);
|
||||||
|
mod->set_relative(0, true);
|
||||||
|
skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);
|
||||||
|
CHECK(Math::is_equal_approx(
|
||||||
|
(skeleton->get_bone_pose_position(tgt_bone) - skeleton->get_bone_rest(tgt_bone).origin).x,
|
||||||
|
((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_pose_position(apl_bone)).y));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== [ConvertTransformModifier3D] Rotation (roll) x to y =====
|
||||||
|
mod->set_reference_transform_mode(0, ConvertTransformModifier3D::TRANSFORM_MODE_ROTATION);
|
||||||
|
mod->set_reference_range_min(0, -180.0);
|
||||||
|
mod->set_reference_range_max(0, 180.0);
|
||||||
|
mod->set_apply_transform_mode(0, ConvertTransformModifier3D::TRANSFORM_MODE_ROTATION);
|
||||||
|
mod->set_apply_range_min(0, -180.0);
|
||||||
|
mod->set_apply_range_max(0, 180.0);
|
||||||
|
|
||||||
|
SUBCASE("[ConvertTransformModifier3D] Rotation (roll) x to y, additive=false, relative=false") {
|
||||||
|
mod->set_additive(0, false);
|
||||||
|
mod->set_relative(0, false);
|
||||||
|
skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);
|
||||||
|
CHECK(Math::is_equal_approx(
|
||||||
|
BoneConstraint3D::get_roll_angle(skeleton->get_bone_pose_rotation(tgt_bone), BoneConstraint3D::get_vector_from_axis(Vector3::AXIS_X)),
|
||||||
|
BoneConstraint3D::get_roll_angle((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion(), BoneConstraint3D::get_vector_from_axis(Vector3::AXIS_Y))));
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("[ConvertTransformModifier3D] Rotation (roll) x to y, additive=true, relative=false") {
|
||||||
|
mod->set_additive(0, true);
|
||||||
|
mod->set_relative(0, false);
|
||||||
|
skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);
|
||||||
|
CHECK(Math::is_equal_approx(
|
||||||
|
BoneConstraint3D::get_roll_angle(skeleton->get_bone_pose_rotation(tgt_bone), BoneConstraint3D::get_vector_from_axis(Vector3::AXIS_X)),
|
||||||
|
BoneConstraint3D::get_roll_angle(skeleton->get_bone_pose_rotation(apl_bone).inverse() * (skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion(), BoneConstraint3D::get_vector_from_axis(Vector3::AXIS_Y))));
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("[ConvertTransformModifier3D] Rotation (roll) x to y, additive=false, relative=true") {
|
||||||
|
mod->set_additive(0, false);
|
||||||
|
mod->set_relative(0, true);
|
||||||
|
skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);
|
||||||
|
CHECK(Math::is_equal_approx(
|
||||||
|
BoneConstraint3D::get_roll_angle(skeleton->get_bone_rest(tgt_bone).basis.get_rotation_quaternion().inverse() * skeleton->get_bone_pose_rotation(tgt_bone), BoneConstraint3D::get_vector_from_axis(Vector3::AXIS_X)),
|
||||||
|
BoneConstraint3D::get_roll_angle(skeleton->get_bone_rest(apl_bone).basis.get_rotation_quaternion().inverse() * (skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion(), BoneConstraint3D::get_vector_from_axis(Vector3::AXIS_Y))));
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("[ConvertTransformModifier3D] Rotation (roll) x to y, additive=true, relative=true") {
|
||||||
|
mod->set_additive(0, true);
|
||||||
|
mod->set_relative(0, true);
|
||||||
|
skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);
|
||||||
|
CHECK(Math::is_equal_approx(
|
||||||
|
BoneConstraint3D::get_roll_angle(skeleton->get_bone_rest(tgt_bone).basis.get_rotation_quaternion().inverse() * skeleton->get_bone_pose_rotation(tgt_bone), BoneConstraint3D::get_vector_from_axis(Vector3::AXIS_X)),
|
||||||
|
BoneConstraint3D::get_roll_angle(skeleton->get_bone_pose_rotation(apl_bone).inverse() * (skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion(), BoneConstraint3D::get_vector_from_axis(Vector3::AXIS_Y))));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== [ConvertTransformModifier3D] Scale x to y =====
|
||||||
|
mod->set_reference_transform_mode(0, ConvertTransformModifier3D::TRANSFORM_MODE_SCALE);
|
||||||
|
mod->set_reference_range_min(0, 0);
|
||||||
|
mod->set_reference_range_max(0, 10.0);
|
||||||
|
mod->set_apply_transform_mode(0, ConvertTransformModifier3D::TRANSFORM_MODE_SCALE);
|
||||||
|
mod->set_apply_range_min(0, 0);
|
||||||
|
mod->set_apply_range_max(0, 10.0);
|
||||||
|
|
||||||
|
SUBCASE("[ConvertTransformModifier3D] Scale x to y, additive=false, relative=false") {
|
||||||
|
mod->set_additive(0, false);
|
||||||
|
mod->set_relative(0, false);
|
||||||
|
skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);
|
||||||
|
CHECK(Math::is_equal_approx(
|
||||||
|
skeleton->get_bone_pose_scale(tgt_bone).x,
|
||||||
|
(skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale().y));
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("[ConvertTransformModifier3D] Scale x to y, additive=true, relative=false") {
|
||||||
|
mod->set_additive(0, true);
|
||||||
|
mod->set_relative(0, false);
|
||||||
|
skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);
|
||||||
|
CHECK(Math::is_equal_approx(
|
||||||
|
skeleton->get_bone_pose_scale(tgt_bone).x,
|
||||||
|
((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_pose_scale(apl_bone)).y));
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("[ConvertTransformModifier3D] Scale x to y, additive=false, relative=true") {
|
||||||
|
mod->set_additive(0, false);
|
||||||
|
mod->set_relative(0, true);
|
||||||
|
skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);
|
||||||
|
CHECK(Math::is_equal_approx(
|
||||||
|
(skeleton->get_bone_pose_scale(tgt_bone) / skeleton->get_bone_rest(tgt_bone).basis.get_scale()).x,
|
||||||
|
((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_rest(apl_bone).basis.get_scale()).y));
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("[ConvertTransformModifier3D] Scale x to y, additive=true, relative=true") {
|
||||||
|
mod->set_additive(0, true);
|
||||||
|
mod->set_relative(0, true);
|
||||||
|
skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);
|
||||||
|
CHECK(Math::is_equal_approx(
|
||||||
|
(skeleton->get_bone_pose_scale(tgt_bone) / skeleton->get_bone_rest(tgt_bone).basis.get_scale()).x,
|
||||||
|
((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_pose_scale(apl_bone)).y));
|
||||||
|
}
|
||||||
|
|
||||||
|
memdelete(modified);
|
||||||
|
memdelete(mod);
|
||||||
|
memdelete(skeleton);
|
||||||
|
}
|
||||||
|
} // namespace TestConvertTransformModifier3D
|
529
tests/scene/test_copy_transform_modifier_3d.h
Normal file
529
tests/scene/test_copy_transform_modifier_3d.h
Normal file
@ -0,0 +1,529 @@
|
|||||||
|
/**************************************************************************/
|
||||||
|
/* test_copy_transform_modifier_3d.h */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||||
|
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* */
|
||||||
|
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||||
|
/* a copy of this software and associated documentation files (the */
|
||||||
|
/* "Software"), to deal in the Software without restriction, including */
|
||||||
|
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||||
|
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||||
|
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||||
|
/* the following conditions: */
|
||||||
|
/* */
|
||||||
|
/* The above copyright notice and this permission notice shall be */
|
||||||
|
/* included in all copies or substantial portions of the Software. */
|
||||||
|
/* */
|
||||||
|
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||||
|
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||||
|
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||||
|
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||||
|
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||||
|
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||||
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||||
|
/**************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "tests/test_macros.h"
|
||||||
|
|
||||||
|
#include "scene/3d/bone_attachment_3d.h"
|
||||||
|
#include "scene/3d/copy_transform_modifier_3d.h"
|
||||||
|
|
||||||
|
namespace TestCopyTransformModifier3D {
|
||||||
|
|
||||||
|
Transform3D make_random_transform_3d(int p_seed) {
|
||||||
|
RandomNumberGenerator rng;
|
||||||
|
rng.set_seed(p_seed);
|
||||||
|
|
||||||
|
Vector3 pos;
|
||||||
|
pos.x = rng.randf_range(-10.0, 10.0);
|
||||||
|
rng.set_seed(++p_seed);
|
||||||
|
pos.y = rng.randf_range(-10.0, 10.0);
|
||||||
|
rng.set_seed(++p_seed);
|
||||||
|
pos.z = rng.randf_range(-10.0, 10.0);
|
||||||
|
rng.set_seed(++p_seed);
|
||||||
|
|
||||||
|
Quaternion rot;
|
||||||
|
rot.x = rng.randf_range(-1.0, 1.0);
|
||||||
|
rng.set_seed(++p_seed);
|
||||||
|
rot.y = rng.randf_range(-1.0, 1.0);
|
||||||
|
rng.set_seed(++p_seed);
|
||||||
|
rot.z = rng.randf_range(-1.0, 1.0);
|
||||||
|
rng.set_seed(++p_seed);
|
||||||
|
rot.w = rng.randf_range(-1.0, 1.0);
|
||||||
|
rng.set_seed(++p_seed);
|
||||||
|
rot = rot.normalized();
|
||||||
|
|
||||||
|
Vector3 scl;
|
||||||
|
scl.x = rng.randf_range(0.5, 2.0);
|
||||||
|
rng.set_seed(++p_seed);
|
||||||
|
scl.y = rng.randf_range(0.5, 2.0);
|
||||||
|
rng.set_seed(++p_seed);
|
||||||
|
scl.z = rng.randf_range(0.5, 2.0);
|
||||||
|
rng.set_seed(++p_seed);
|
||||||
|
|
||||||
|
return Transform3D(Basis(rot).scaled(scl), pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 flip_x(Vector3 p_pos) {
|
||||||
|
return Vector3(-p_pos.x, p_pos.y, p_pos.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 flip_xy(Vector3 p_pos) {
|
||||||
|
return Vector3(-p_pos.x, -p_pos.y, p_pos.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 flip_all(Vector3 p_pos) {
|
||||||
|
return -p_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quaternion's phase can be confused by inversion, it is aligned via casting to Basis.
|
||||||
|
|
||||||
|
Quaternion flip_x(Quaternion p_rot) {
|
||||||
|
return Basis(Quaternion(-p_rot.x, p_rot.y, p_rot.z, p_rot.w).normalized()).get_rotation_quaternion();
|
||||||
|
}
|
||||||
|
|
||||||
|
Quaternion flip_xy(Quaternion p_rot) {
|
||||||
|
return Basis(Quaternion(-p_rot.x, -p_rot.y, p_rot.z, p_rot.w).normalized()).get_rotation_quaternion();
|
||||||
|
}
|
||||||
|
|
||||||
|
Quaternion flip_all(Quaternion p_rot) {
|
||||||
|
return Basis(p_rot.inverse()).get_rotation_quaternion();
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 inv_x(Vector3 p_scl) {
|
||||||
|
return Vector3(1.0 / p_scl.x, p_scl.y, p_scl.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 inv_xy(Vector3 p_scl) {
|
||||||
|
return Vector3(1.0 / p_scl.x, 1.0 / p_scl.y, p_scl.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 inv_all(Vector3 p_scl) {
|
||||||
|
return Vector3(1.0, 1.0, 1.0) / p_scl;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("[SceneTree][CopyTransformModifier3D]") {
|
||||||
|
SceneTree *tree = SceneTree::get_singleton();
|
||||||
|
int seed = 12345;
|
||||||
|
Skeleton3D *skeleton = memnew(Skeleton3D);
|
||||||
|
CopyTransformModifier3D *mod = memnew(CopyTransformModifier3D);
|
||||||
|
|
||||||
|
// Instead of awaiting the process to wait to finish deferred process and watch "skeleton_updated" signal,
|
||||||
|
// force notify NOTIFICATION_UPDATE_SKELETON and get the modified pose from the BoneAttachment's transform.
|
||||||
|
BoneAttachment3D *modified = memnew(BoneAttachment3D);
|
||||||
|
|
||||||
|
tree->get_root()->add_child(skeleton);
|
||||||
|
|
||||||
|
int root = skeleton->add_bone("root");
|
||||||
|
skeleton->set_bone_rest(root, make_random_transform_3d(++seed));
|
||||||
|
skeleton->set_bone_pose(root, make_random_transform_3d(++seed));
|
||||||
|
|
||||||
|
int apl_root = skeleton->add_bone("apl_root");
|
||||||
|
skeleton->set_bone_parent(apl_root, root);
|
||||||
|
skeleton->set_bone_rest(apl_root, make_random_transform_3d(++seed));
|
||||||
|
skeleton->set_bone_pose(apl_root, make_random_transform_3d(++seed));
|
||||||
|
|
||||||
|
int apl_bone = skeleton->add_bone("apl_bone");
|
||||||
|
skeleton->set_bone_parent(apl_bone, apl_root);
|
||||||
|
skeleton->set_bone_rest(apl_bone, make_random_transform_3d(++seed));
|
||||||
|
skeleton->set_bone_pose(apl_bone, make_random_transform_3d(++seed));
|
||||||
|
|
||||||
|
int tgt_root = skeleton->add_bone("tgt_root");
|
||||||
|
skeleton->set_bone_parent(tgt_root, root);
|
||||||
|
skeleton->set_bone_rest(tgt_root, make_random_transform_3d(++seed));
|
||||||
|
skeleton->set_bone_pose(tgt_root, make_random_transform_3d(++seed));
|
||||||
|
|
||||||
|
int tgt_bone = skeleton->add_bone("tgt_bone");
|
||||||
|
skeleton->set_bone_parent(tgt_bone, tgt_root);
|
||||||
|
skeleton->set_bone_rest(tgt_bone, make_random_transform_3d(++seed));
|
||||||
|
skeleton->set_bone_pose(tgt_bone, make_random_transform_3d(++seed));
|
||||||
|
|
||||||
|
skeleton->add_child(mod);
|
||||||
|
skeleton->add_child(modified);
|
||||||
|
modified->set_rotation_edit_mode(Node3D::ROTATION_EDIT_MODE_QUATERNION);
|
||||||
|
modified->set_bone_idx(apl_bone);
|
||||||
|
|
||||||
|
mod->set_setting_count(1);
|
||||||
|
mod->set_reference_bone(0, tgt_bone);
|
||||||
|
mod->set_apply_bone(0, apl_bone);
|
||||||
|
|
||||||
|
mod->set_copy_position(0, true);
|
||||||
|
mod->set_copy_rotation(0, true);
|
||||||
|
mod->set_copy_scale(0, true);
|
||||||
|
|
||||||
|
// ===== [CopyTransformModifier3D] Enable 1 axis =====
|
||||||
|
mod->set_axis_x_enabled(0, true);
|
||||||
|
mod->set_axis_y_enabled(0, false);
|
||||||
|
mod->set_axis_z_enabled(0, false);
|
||||||
|
mod->set_axis_x_inverted(0, false);
|
||||||
|
mod->set_axis_y_inverted(0, false);
|
||||||
|
mod->set_axis_z_inverted(0, false);
|
||||||
|
|
||||||
|
SUBCASE("[CopyTransformModifier3D] Enable 1 axis, additive=false, relative=false") {
|
||||||
|
mod->set_additive(0, false);
|
||||||
|
mod->set_relative(0, false);
|
||||||
|
skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);
|
||||||
|
CHECK_MESSAGE(Math::is_equal_approx(
|
||||||
|
skeleton->get_bone_pose_position(tgt_bone).x,
|
||||||
|
(skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin.x),
|
||||||
|
"Position x is copied correctly.");
|
||||||
|
CHECK_MESSAGE(Math::is_equal_approx(
|
||||||
|
BoneConstraint3D::get_roll_angle(skeleton->get_bone_pose_rotation(tgt_bone), BoneConstraint3D::get_vector_from_axis(Vector3::AXIS_X)),
|
||||||
|
BoneConstraint3D::get_roll_angle((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion(), BoneConstraint3D::get_vector_from_axis(Vector3::AXIS_X))),
|
||||||
|
"Rotation x (roll x) is copied correctly.");
|
||||||
|
CHECK_MESSAGE(Math::is_equal_approx(
|
||||||
|
skeleton->get_bone_pose_scale(tgt_bone).x,
|
||||||
|
(skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale().x),
|
||||||
|
"Scale x is copied correctly.");
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("[CopyTransformModifier3D] Enable 1 axis, additive=true, relative=false") {
|
||||||
|
mod->set_additive(0, true);
|
||||||
|
mod->set_relative(0, false);
|
||||||
|
skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);
|
||||||
|
CHECK_MESSAGE(Math::is_equal_approx(
|
||||||
|
skeleton->get_bone_pose_position(tgt_bone).x,
|
||||||
|
((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_pose_position(apl_bone)).x),
|
||||||
|
"Position x is copied correctly.");
|
||||||
|
CHECK_MESSAGE(Math::is_equal_approx(
|
||||||
|
BoneConstraint3D::get_roll_angle(skeleton->get_bone_pose_rotation(tgt_bone), BoneConstraint3D::get_vector_from_axis(Vector3::AXIS_X)),
|
||||||
|
BoneConstraint3D::get_roll_angle(skeleton->get_bone_pose_rotation(apl_bone).inverse() * (skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion(), BoneConstraint3D::get_vector_from_axis(Vector3::AXIS_X))),
|
||||||
|
"Rotation x (roll x) is copied correctly.");
|
||||||
|
CHECK_MESSAGE(Math::is_equal_approx(
|
||||||
|
skeleton->get_bone_pose_scale(tgt_bone).x,
|
||||||
|
((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_pose_scale(apl_bone)).x),
|
||||||
|
"Scale x is copied correctly.");
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("[CopyTransformModifier3D] Enable 1 axis, additive=false, relative=true") {
|
||||||
|
mod->set_additive(0, false);
|
||||||
|
mod->set_relative(0, true);
|
||||||
|
skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);
|
||||||
|
CHECK_MESSAGE(Math::is_equal_approx(
|
||||||
|
(skeleton->get_bone_pose_position(tgt_bone) - skeleton->get_bone_rest(tgt_bone).origin).x,
|
||||||
|
((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_rest(apl_bone).origin).x),
|
||||||
|
"Position x is copied correctly.");
|
||||||
|
CHECK_MESSAGE(Math::is_equal_approx(
|
||||||
|
BoneConstraint3D::get_roll_angle(skeleton->get_bone_rest(tgt_bone).basis.get_rotation_quaternion().inverse() * skeleton->get_bone_pose_rotation(tgt_bone), BoneConstraint3D::get_vector_from_axis(Vector3::AXIS_X)),
|
||||||
|
BoneConstraint3D::get_roll_angle(skeleton->get_bone_rest(apl_bone).basis.get_rotation_quaternion().inverse() * (skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion(), BoneConstraint3D::get_vector_from_axis(Vector3::AXIS_X))),
|
||||||
|
"Rotation x (roll x) is copied correctly.");
|
||||||
|
CHECK_MESSAGE(Math::is_equal_approx(
|
||||||
|
(skeleton->get_bone_pose_scale(tgt_bone) / skeleton->get_bone_rest(tgt_bone).basis.get_scale()).x,
|
||||||
|
((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_rest(apl_bone).basis.get_scale()).x),
|
||||||
|
"Scale x is copied correctly.");
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("[CopyTransformModifier3D] Enable 1 axis, additive=true, relative=true") {
|
||||||
|
mod->set_additive(0, true);
|
||||||
|
mod->set_relative(0, true);
|
||||||
|
skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);
|
||||||
|
CHECK_MESSAGE(Math::is_equal_approx(
|
||||||
|
(skeleton->get_bone_pose_position(tgt_bone) - skeleton->get_bone_rest(tgt_bone).origin).x,
|
||||||
|
((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_pose_position(apl_bone)).x),
|
||||||
|
"Position x is copied correctly.");
|
||||||
|
CHECK_MESSAGE(Math::is_equal_approx(
|
||||||
|
BoneConstraint3D::get_roll_angle(skeleton->get_bone_rest(tgt_bone).basis.get_rotation_quaternion().inverse() * skeleton->get_bone_pose_rotation(tgt_bone), BoneConstraint3D::get_vector_from_axis(Vector3::AXIS_X)),
|
||||||
|
BoneConstraint3D::get_roll_angle(skeleton->get_bone_pose_rotation(apl_bone).inverse() * (skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion(), BoneConstraint3D::get_vector_from_axis(Vector3::AXIS_X))),
|
||||||
|
"Rotation x (roll x) is copied correctly.");
|
||||||
|
CHECK_MESSAGE(Math::is_equal_approx(
|
||||||
|
(skeleton->get_bone_pose_scale(tgt_bone) / skeleton->get_bone_rest(tgt_bone).basis.get_scale()).x,
|
||||||
|
((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_pose_scale(apl_bone)).x),
|
||||||
|
"Scale x is copied correctly.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== [CopyTransformModifier3D] Enable 2 axes =====
|
||||||
|
mod->set_axis_x_enabled(0, true);
|
||||||
|
mod->set_axis_y_enabled(0, true);
|
||||||
|
mod->set_axis_z_enabled(0, false);
|
||||||
|
mod->set_axis_x_inverted(0, false);
|
||||||
|
mod->set_axis_y_inverted(0, false);
|
||||||
|
mod->set_axis_z_inverted(0, false);
|
||||||
|
|
||||||
|
SUBCASE("[CopyTransformModifier3D] Enable 2 axes, additive=false, relative=false") {
|
||||||
|
mod->set_additive(0, false);
|
||||||
|
mod->set_relative(0, false);
|
||||||
|
skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);
|
||||||
|
CHECK_MESSAGE(Math::is_equal_approx(
|
||||||
|
skeleton->get_bone_pose_position(tgt_bone).x,
|
||||||
|
(skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin.x),
|
||||||
|
"Position x is copied correctly.");
|
||||||
|
CHECK_MESSAGE(Math::is_equal_approx(
|
||||||
|
skeleton->get_bone_pose_position(tgt_bone).y,
|
||||||
|
(skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin.y),
|
||||||
|
"Position y is copied correctly.");
|
||||||
|
CHECK_MESSAGE(Math::is_zero_approx(
|
||||||
|
BoneConstraint3D::get_roll_angle((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion(), BoneConstraint3D::get_vector_from_axis(Vector3::AXIS_Z))),
|
||||||
|
"Rotation z (roll z) is zero correctly.");
|
||||||
|
CHECK_MESSAGE(Math::is_equal_approx(
|
||||||
|
skeleton->get_bone_pose_scale(tgt_bone).x,
|
||||||
|
(skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale().x),
|
||||||
|
"Scale x is copied correctly.");
|
||||||
|
CHECK_MESSAGE(Math::is_equal_approx(
|
||||||
|
skeleton->get_bone_pose_scale(tgt_bone).y,
|
||||||
|
(skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale().y),
|
||||||
|
"Scale y is copied correctly.");
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("[CopyTransformModifier3D] Enable 2 axes, additive=true, relative=false") {
|
||||||
|
mod->set_additive(0, true);
|
||||||
|
mod->set_relative(0, false);
|
||||||
|
skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);
|
||||||
|
CHECK_MESSAGE(Math::is_equal_approx(
|
||||||
|
skeleton->get_bone_pose_position(tgt_bone).x,
|
||||||
|
((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_pose_position(apl_bone)).x),
|
||||||
|
"Position x is copied correctly.");
|
||||||
|
CHECK_MESSAGE(Math::is_equal_approx(
|
||||||
|
skeleton->get_bone_pose_position(tgt_bone).y,
|
||||||
|
((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_pose_position(apl_bone)).y),
|
||||||
|
"Position y is copied correctly.");
|
||||||
|
CHECK_MESSAGE(Math::is_zero_approx(
|
||||||
|
BoneConstraint3D::get_roll_angle(skeleton->get_bone_pose_rotation(apl_bone).inverse() * (skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion(), BoneConstraint3D::get_vector_from_axis(Vector3::AXIS_Z))),
|
||||||
|
"Rotation z (roll z) is zero correctly.");
|
||||||
|
CHECK_MESSAGE(Math::is_equal_approx(
|
||||||
|
skeleton->get_bone_pose_scale(tgt_bone).x,
|
||||||
|
((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_pose_scale(apl_bone)).x),
|
||||||
|
"Scale x is copied correctly.");
|
||||||
|
CHECK_MESSAGE(Math::is_equal_approx(
|
||||||
|
skeleton->get_bone_pose_scale(tgt_bone).y,
|
||||||
|
((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_pose_scale(apl_bone)).y),
|
||||||
|
"Scale y is copied correctly.");
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("[CopyTransformModifier3D] Enable 2 axes, additive=false, relative=true") {
|
||||||
|
mod->set_additive(0, false);
|
||||||
|
mod->set_relative(0, true);
|
||||||
|
skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);
|
||||||
|
CHECK_MESSAGE(Math::is_equal_approx(
|
||||||
|
(skeleton->get_bone_pose_position(tgt_bone) - skeleton->get_bone_rest(tgt_bone).origin).x,
|
||||||
|
((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_rest(apl_bone).origin).x),
|
||||||
|
"Position x is copied correctly.");
|
||||||
|
CHECK_MESSAGE(Math::is_equal_approx(
|
||||||
|
(skeleton->get_bone_pose_position(tgt_bone) - skeleton->get_bone_rest(tgt_bone).origin).y,
|
||||||
|
((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_rest(apl_bone).origin).y),
|
||||||
|
"Position y is copied correctly.");
|
||||||
|
CHECK_MESSAGE(Math::is_zero_approx(
|
||||||
|
BoneConstraint3D::get_roll_angle(skeleton->get_bone_rest(apl_bone).basis.get_rotation_quaternion().inverse() * (skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion(), BoneConstraint3D::get_vector_from_axis(Vector3::AXIS_Z))),
|
||||||
|
"Rotation z (roll z) is zero correctly.");
|
||||||
|
CHECK_MESSAGE(Math::is_equal_approx(
|
||||||
|
(skeleton->get_bone_pose_scale(tgt_bone) / skeleton->get_bone_rest(tgt_bone).basis.get_scale()).x,
|
||||||
|
((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_rest(apl_bone).basis.get_scale()).x),
|
||||||
|
"Scale x is copied correctly.");
|
||||||
|
CHECK_MESSAGE(Math::is_equal_approx(
|
||||||
|
(skeleton->get_bone_pose_scale(tgt_bone) / skeleton->get_bone_rest(tgt_bone).basis.get_scale()).y,
|
||||||
|
((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_rest(apl_bone).basis.get_scale()).y),
|
||||||
|
"Scale y is copied correctly.");
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("[CopyTransformModifier3D] Enable 2 axes, additive=true, relative=true") {
|
||||||
|
mod->set_additive(0, true);
|
||||||
|
mod->set_relative(0, true);
|
||||||
|
skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);
|
||||||
|
CHECK_MESSAGE(Math::is_equal_approx(
|
||||||
|
(skeleton->get_bone_pose_position(tgt_bone) - skeleton->get_bone_rest(tgt_bone).origin).x,
|
||||||
|
((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_pose_position(apl_bone)).x),
|
||||||
|
"Position x is copied correctly.");
|
||||||
|
CHECK_MESSAGE(Math::is_equal_approx(
|
||||||
|
(skeleton->get_bone_pose_position(tgt_bone) - skeleton->get_bone_rest(tgt_bone).origin).y,
|
||||||
|
((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_pose_position(apl_bone)).y),
|
||||||
|
"Position y is copied correctly.");
|
||||||
|
CHECK_MESSAGE(Math::is_zero_approx(
|
||||||
|
BoneConstraint3D::get_roll_angle(skeleton->get_bone_pose_rotation(apl_bone).inverse() * (skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion(), BoneConstraint3D::get_vector_from_axis(Vector3::AXIS_Z))),
|
||||||
|
"Rotation z (roll z) is zero correctly.");
|
||||||
|
CHECK_MESSAGE(Math::is_equal_approx(
|
||||||
|
(skeleton->get_bone_pose_scale(tgt_bone) / skeleton->get_bone_rest(tgt_bone).basis.get_scale()).x,
|
||||||
|
((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_pose_scale(apl_bone)).x),
|
||||||
|
"Scale x is copied correctly.");
|
||||||
|
CHECK_MESSAGE(Math::is_equal_approx(
|
||||||
|
(skeleton->get_bone_pose_scale(tgt_bone) / skeleton->get_bone_rest(tgt_bone).basis.get_scale()).y,
|
||||||
|
((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_pose_scale(apl_bone)).y),
|
||||||
|
"Scale y is copied correctly.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== [CopyTransformModifier3D] Enable all axes =====
|
||||||
|
mod->set_axis_x_enabled(0, true);
|
||||||
|
mod->set_axis_y_enabled(0, true);
|
||||||
|
mod->set_axis_z_enabled(0, true);
|
||||||
|
mod->set_axis_x_inverted(0, false);
|
||||||
|
mod->set_axis_y_inverted(0, false);
|
||||||
|
mod->set_axis_z_inverted(0, false);
|
||||||
|
|
||||||
|
SUBCASE("[CopyTransformModifier3D] Enable all axes, additive=false, relative=false") {
|
||||||
|
mod->set_additive(0, false);
|
||||||
|
mod->set_relative(0, false);
|
||||||
|
skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);
|
||||||
|
CHECK_MESSAGE(skeleton->get_bone_pose_position(tgt_bone).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin), "Position is copied correctly.");
|
||||||
|
CHECK_MESSAGE(Basis(skeleton->get_bone_pose_rotation(tgt_bone)).is_equal_approx(Basis((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion())), "Rotation is copied correctly.");
|
||||||
|
CHECK_MESSAGE(skeleton->get_bone_pose_scale(tgt_bone).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale()), "Scale is copied correctly.");
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("[CopyTransformModifier3D] Enable all axes, additive=true, relative=false") {
|
||||||
|
mod->set_additive(0, true);
|
||||||
|
mod->set_relative(0, false);
|
||||||
|
skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);
|
||||||
|
CHECK_MESSAGE(skeleton->get_bone_pose_position(tgt_bone).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_pose_position(apl_bone)), "Position is copied correctly.");
|
||||||
|
CHECK_MESSAGE(Basis(skeleton->get_bone_pose_rotation(tgt_bone)).is_equal_approx(skeleton->get_bone_pose_rotation(apl_bone).inverse() * (skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion()), "Rotation is copied correctly.");
|
||||||
|
CHECK_MESSAGE(skeleton->get_bone_pose_scale(tgt_bone).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_pose_scale(apl_bone)), "Scale is copied correctly.");
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("[CopyTransformModifier3D] Enable all axes, additive=false, relative=true") {
|
||||||
|
mod->set_additive(0, false);
|
||||||
|
mod->set_relative(0, true);
|
||||||
|
skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);
|
||||||
|
CHECK_MESSAGE((skeleton->get_bone_pose_position(tgt_bone) - skeleton->get_bone_rest(tgt_bone).origin).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_rest(apl_bone).origin), "Position is copied correctly.");
|
||||||
|
CHECK_MESSAGE(Basis(skeleton->get_bone_rest(tgt_bone).basis.get_rotation_quaternion().inverse() * skeleton->get_bone_pose_rotation(tgt_bone)).is_equal_approx(Basis(skeleton->get_bone_rest(apl_bone).basis.get_rotation_quaternion().inverse() * (skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion())), "Rotation is copied correctly.");
|
||||||
|
CHECK_MESSAGE((skeleton->get_bone_pose_scale(tgt_bone) / skeleton->get_bone_rest(tgt_bone).basis.get_scale()).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_rest(apl_bone).basis.get_scale()), "Scale is copied correctly.");
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("[CopyTransformModifier3D] Enable all axes, additive=true, relative=true") {
|
||||||
|
mod->set_additive(0, true);
|
||||||
|
mod->set_relative(0, true);
|
||||||
|
skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);
|
||||||
|
CHECK_MESSAGE((skeleton->get_bone_pose_position(tgt_bone) - skeleton->get_bone_rest(tgt_bone).origin).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_pose_position(apl_bone)), "Position is copied correctly.");
|
||||||
|
CHECK_MESSAGE(Basis(skeleton->get_bone_rest(tgt_bone).basis.get_rotation_quaternion().inverse() * skeleton->get_bone_pose_rotation(tgt_bone)).is_equal_approx(skeleton->get_bone_pose_rotation(apl_bone).inverse() * (skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion()), "Rotation is copied correctly.");
|
||||||
|
CHECK_MESSAGE((skeleton->get_bone_pose_scale(tgt_bone) / skeleton->get_bone_rest(tgt_bone).basis.get_scale()).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_pose_scale(apl_bone)), "Scale is copied correctly.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== [CopyTransformModifier3D] Enable all axes, invert 1 axis =====
|
||||||
|
mod->set_axis_x_enabled(0, true);
|
||||||
|
mod->set_axis_y_enabled(0, true);
|
||||||
|
mod->set_axis_z_enabled(0, true);
|
||||||
|
mod->set_axis_x_inverted(0, true);
|
||||||
|
mod->set_axis_y_inverted(0, false);
|
||||||
|
mod->set_axis_z_inverted(0, false);
|
||||||
|
|
||||||
|
SUBCASE("[CopyTransformModifier3D] Enable all axes, invert 1 axis, additive=false, relative=false") {
|
||||||
|
mod->set_additive(0, false);
|
||||||
|
mod->set_relative(0, false);
|
||||||
|
skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);
|
||||||
|
CHECK_MESSAGE(flip_x(skeleton->get_bone_pose_position(tgt_bone)).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin), "Position is copied/inverted correctly.");
|
||||||
|
CHECK_MESSAGE(flip_x(skeleton->get_bone_pose_rotation(tgt_bone)).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion()), "Rotation is copied/inverted correctly.");
|
||||||
|
CHECK_MESSAGE(inv_x(skeleton->get_bone_pose_scale(tgt_bone)).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale()), "Scale is copied/inverted correctly.");
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("[CopyTransformModifier3D] Enable all axes, invert 1 axis, additive=true, relative=false") {
|
||||||
|
mod->set_additive(0, true);
|
||||||
|
mod->set_relative(0, false);
|
||||||
|
skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);
|
||||||
|
CHECK_MESSAGE(flip_x(skeleton->get_bone_pose_position(tgt_bone)).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_pose_position(apl_bone)), "Position is copied/inverted correctly.");
|
||||||
|
CHECK_MESSAGE(flip_x(skeleton->get_bone_pose_rotation(tgt_bone)).is_equal_approx(Basis(skeleton->get_bone_pose_rotation(apl_bone).inverse() * (skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion()).get_rotation_quaternion()), "Rotation is copied/inverted correctly.");
|
||||||
|
CHECK_MESSAGE(inv_x(skeleton->get_bone_pose_scale(tgt_bone)).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_pose_scale(apl_bone)), "Scale is copied/inverted correctly.");
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("[CopyTransformModifier3D] Enable all axes, invert 1 axis, additive=false, relative=true") {
|
||||||
|
mod->set_additive(0, false);
|
||||||
|
mod->set_relative(0, true);
|
||||||
|
skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);
|
||||||
|
CHECK_MESSAGE(flip_x(skeleton->get_bone_pose_position(tgt_bone) - skeleton->get_bone_rest(tgt_bone).origin).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_rest(apl_bone).origin), "Position is copied/inverted correctly.");
|
||||||
|
CHECK_MESSAGE(flip_x(skeleton->get_bone_rest(tgt_bone).basis.get_rotation_quaternion().inverse() * skeleton->get_bone_pose_rotation(tgt_bone)).is_equal_approx(Basis(skeleton->get_bone_rest(apl_bone).basis.get_rotation_quaternion().inverse() * (skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion()).get_rotation_quaternion()), "Rotation is copied/inverted correctly.");
|
||||||
|
CHECK_MESSAGE(inv_x(skeleton->get_bone_pose_scale(tgt_bone) / skeleton->get_bone_rest(tgt_bone).basis.get_scale()).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_rest(apl_bone).basis.get_scale()), "Scale is copied/inverted correctly.");
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("[CopyTransformModifier3D] Enable all axes, invert 1 axis, additive=true, relative=true") {
|
||||||
|
mod->set_additive(0, true);
|
||||||
|
mod->set_relative(0, true);
|
||||||
|
skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);
|
||||||
|
CHECK_MESSAGE(flip_x(skeleton->get_bone_pose_position(tgt_bone) - skeleton->get_bone_rest(tgt_bone).origin).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_pose_position(apl_bone)), "Position is copied/inverted correctly.");
|
||||||
|
CHECK_MESSAGE(flip_x(skeleton->get_bone_rest(tgt_bone).basis.get_rotation_quaternion().inverse() * skeleton->get_bone_pose_rotation(tgt_bone)).is_equal_approx(Basis(skeleton->get_bone_pose_rotation(apl_bone).inverse() * (skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion()).get_rotation_quaternion()), "Rotation is copied/inverted correctly.");
|
||||||
|
CHECK_MESSAGE(inv_x(skeleton->get_bone_pose_scale(tgt_bone) / skeleton->get_bone_rest(tgt_bone).basis.get_scale()).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_pose_scale(apl_bone)), "Scale is copied/inverted correctly.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== [CopyTransformModifier3D] Enable all axes, invert 2 axes =====
|
||||||
|
mod->set_axis_x_enabled(0, true);
|
||||||
|
mod->set_axis_y_enabled(0, true);
|
||||||
|
mod->set_axis_z_enabled(0, true);
|
||||||
|
mod->set_axis_x_inverted(0, true);
|
||||||
|
mod->set_axis_y_inverted(0, true);
|
||||||
|
mod->set_axis_z_inverted(0, false);
|
||||||
|
|
||||||
|
SUBCASE("[CopyTransformModifier3D] Enable all axes, invert 2 axes, additive=false, relative=false") {
|
||||||
|
mod->set_additive(0, false);
|
||||||
|
mod->set_relative(0, false);
|
||||||
|
skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);
|
||||||
|
CHECK_MESSAGE(flip_xy(skeleton->get_bone_pose_position(tgt_bone)).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin), "Position is copied/inverted correctly.");
|
||||||
|
CHECK_MESSAGE(flip_xy(skeleton->get_bone_pose_rotation(tgt_bone)).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion()), "Rotation is copied/inverted correctly.");
|
||||||
|
CHECK_MESSAGE(inv_xy(skeleton->get_bone_pose_scale(tgt_bone)).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale()), "Scale is copied/inverted correctly.");
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("[CopyTransformModifier3D] Enable all axes, invert 2 axes, additive=true, relative=false") {
|
||||||
|
mod->set_additive(0, true);
|
||||||
|
mod->set_relative(0, false);
|
||||||
|
skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);
|
||||||
|
CHECK_MESSAGE(flip_xy(skeleton->get_bone_pose_position(tgt_bone)).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_pose_position(apl_bone)), "Position is copied/inverted correctly.");
|
||||||
|
CHECK_MESSAGE(flip_xy(skeleton->get_bone_pose_rotation(tgt_bone)).is_equal_approx(Basis(skeleton->get_bone_pose_rotation(apl_bone).inverse() * (skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion()).get_rotation_quaternion()), "Rotation is copied/inverted correctly.");
|
||||||
|
CHECK_MESSAGE(inv_xy(skeleton->get_bone_pose_scale(tgt_bone)).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_pose_scale(apl_bone)), "Scale is copied/inverted correctly.");
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("[CopyTransformModifier3D] Enable all axes, invert 2 axes, additive=false, relative=true") {
|
||||||
|
mod->set_additive(0, false);
|
||||||
|
mod->set_relative(0, true);
|
||||||
|
skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);
|
||||||
|
CHECK_MESSAGE(flip_xy(skeleton->get_bone_pose_position(tgt_bone) - skeleton->get_bone_rest(tgt_bone).origin).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_rest(apl_bone).origin), "Position is copied/inverted correctly.");
|
||||||
|
CHECK_MESSAGE(flip_xy(skeleton->get_bone_rest(tgt_bone).basis.get_rotation_quaternion().inverse() * skeleton->get_bone_pose_rotation(tgt_bone)).is_equal_approx(Basis(skeleton->get_bone_rest(apl_bone).basis.get_rotation_quaternion().inverse() * (skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion()).get_rotation_quaternion()), "Rotation is copied/inverted correctly.");
|
||||||
|
CHECK_MESSAGE(inv_xy(skeleton->get_bone_pose_scale(tgt_bone) / skeleton->get_bone_rest(tgt_bone).basis.get_scale()).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_rest(apl_bone).basis.get_scale()), "Scale is copied/inverted correctly.");
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("[CopyTransformModifier3D] Enable all axes, invert 2 axes, additive=true, relative=true") {
|
||||||
|
mod->set_additive(0, true);
|
||||||
|
mod->set_relative(0, true);
|
||||||
|
skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);
|
||||||
|
CHECK_MESSAGE(flip_xy(skeleton->get_bone_pose_position(tgt_bone) - skeleton->get_bone_rest(tgt_bone).origin).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_pose_position(apl_bone)), "Position is copied/inverted correctly.");
|
||||||
|
CHECK_MESSAGE(flip_xy(skeleton->get_bone_rest(tgt_bone).basis.get_rotation_quaternion().inverse() * skeleton->get_bone_pose_rotation(tgt_bone)).is_equal_approx(Basis(skeleton->get_bone_pose_rotation(apl_bone).inverse() * (skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion()).get_rotation_quaternion()), "Rotation is copied/inverted correctly.");
|
||||||
|
CHECK_MESSAGE(inv_xy(skeleton->get_bone_pose_scale(tgt_bone) / skeleton->get_bone_rest(tgt_bone).basis.get_scale()).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_pose_scale(apl_bone)), "Scale is copied/inverted correctly.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== [CopyTransformModifier3D] Enable all axes, invert all axes =====
|
||||||
|
mod->set_axis_x_enabled(0, true);
|
||||||
|
mod->set_axis_y_enabled(0, true);
|
||||||
|
mod->set_axis_z_enabled(0, true);
|
||||||
|
mod->set_axis_x_inverted(0, true);
|
||||||
|
mod->set_axis_y_inverted(0, true);
|
||||||
|
mod->set_axis_z_inverted(0, true);
|
||||||
|
|
||||||
|
SUBCASE("[CopyTransformModifier3D] Enable all axes, invert all axes, additive=false, relative=false") {
|
||||||
|
mod->set_additive(0, false);
|
||||||
|
mod->set_relative(0, false);
|
||||||
|
skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);
|
||||||
|
CHECK_MESSAGE(flip_all(skeleton->get_bone_pose_position(tgt_bone)).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin), "Position is copied/inverted correctly.");
|
||||||
|
CHECK_MESSAGE(flip_all(skeleton->get_bone_pose_rotation(tgt_bone)).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion()), "Rotation is copied/inverted correctly.");
|
||||||
|
CHECK_MESSAGE(inv_all(skeleton->get_bone_pose_scale(tgt_bone)).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale()), "Scale is copied/inverted correctly.");
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("[CopyTransformModifier3D] Enable all axes, invert all axes, additive=true, relative=false") {
|
||||||
|
mod->set_additive(0, true);
|
||||||
|
mod->set_relative(0, false);
|
||||||
|
skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);
|
||||||
|
CHECK_MESSAGE(flip_all(skeleton->get_bone_pose_position(tgt_bone)).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_pose_position(apl_bone)), "Position is copied/inverted correctly.");
|
||||||
|
CHECK_MESSAGE(flip_all(skeleton->get_bone_pose_rotation(tgt_bone)).is_equal_approx(Basis(skeleton->get_bone_pose_rotation(apl_bone).inverse() * (skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion()).get_rotation_quaternion()), "Rotation is copied/inverted correctly.");
|
||||||
|
CHECK_MESSAGE(inv_all(skeleton->get_bone_pose_scale(tgt_bone)).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_pose_scale(apl_bone)), "Scale is copied/inverted correctly.");
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("[CopyTransformModifier3D] Enable all axes, invert all axes, additive=false, relative=true") {
|
||||||
|
mod->set_additive(0, false);
|
||||||
|
mod->set_relative(0, true);
|
||||||
|
skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);
|
||||||
|
CHECK_MESSAGE(flip_all(skeleton->get_bone_pose_position(tgt_bone) - skeleton->get_bone_rest(tgt_bone).origin).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_rest(apl_bone).origin), "Position is copied/inverted correctly.");
|
||||||
|
CHECK_MESSAGE(flip_all(skeleton->get_bone_rest(tgt_bone).basis.get_rotation_quaternion().inverse() * skeleton->get_bone_pose_rotation(tgt_bone)).is_equal_approx(Basis(skeleton->get_bone_rest(apl_bone).basis.get_rotation_quaternion().inverse() * (skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion()).get_rotation_quaternion()), "Rotation is copied/inverted correctly.");
|
||||||
|
CHECK_MESSAGE(inv_all(skeleton->get_bone_pose_scale(tgt_bone) / skeleton->get_bone_rest(tgt_bone).basis.get_scale()).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_rest(apl_bone).basis.get_scale()), "Scale is copied/inverted correctly.");
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("[CopyTransformModifier3D] Enable all axes, invert all axes, additive=true, relative=true") {
|
||||||
|
mod->set_additive(0, true);
|
||||||
|
mod->set_relative(0, true);
|
||||||
|
skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);
|
||||||
|
CHECK_MESSAGE(flip_all(skeleton->get_bone_pose_position(tgt_bone) - skeleton->get_bone_rest(tgt_bone).origin).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_pose_position(apl_bone)), "Position is copied/inverted correctly.");
|
||||||
|
CHECK_MESSAGE(flip_all(skeleton->get_bone_rest(tgt_bone).basis.get_rotation_quaternion().inverse() * skeleton->get_bone_pose_rotation(tgt_bone)).is_equal_approx(Basis(skeleton->get_bone_pose_rotation(apl_bone).inverse() * (skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion()).get_rotation_quaternion()), "Rotation is copied/inverted correctly.");
|
||||||
|
CHECK_MESSAGE(inv_all(skeleton->get_bone_pose_scale(tgt_bone) / skeleton->get_bone_rest(tgt_bone).basis.get_scale()).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_pose_scale(apl_bone)), "Scale is copied/inverted correctly.");
|
||||||
|
}
|
||||||
|
|
||||||
|
memdelete(modified);
|
||||||
|
memdelete(mod);
|
||||||
|
memdelete(skeleton);
|
||||||
|
}
|
||||||
|
} // namespace TestCopyTransformModifier3D
|
@ -168,6 +168,8 @@
|
|||||||
#include "tests/core/math/test_triangle_mesh.h"
|
#include "tests/core/math/test_triangle_mesh.h"
|
||||||
#include "tests/scene/test_arraymesh.h"
|
#include "tests/scene/test_arraymesh.h"
|
||||||
#include "tests/scene/test_camera_3d.h"
|
#include "tests/scene/test_camera_3d.h"
|
||||||
|
#include "tests/scene/test_convert_transform_modifier_3d.h"
|
||||||
|
#include "tests/scene/test_copy_transform_modifier_3d.h"
|
||||||
#include "tests/scene/test_gltf_document.h"
|
#include "tests/scene/test_gltf_document.h"
|
||||||
#include "tests/scene/test_path_3d.h"
|
#include "tests/scene/test_path_3d.h"
|
||||||
#include "tests/scene/test_path_follow_3d.h"
|
#include "tests/scene/test_path_follow_3d.h"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user