You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-21 14:57:09 +00:00
Add LimitAngularVelocityModifier3D
This commit is contained in:
102
doc/classes/LimitAngularVelocityModifier3D.xml
Normal file
102
doc/classes/LimitAngularVelocityModifier3D.xml
Normal file
@@ -0,0 +1,102 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="LimitAngularVelocityModifier3D" inherits="SkeletonModifier3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
|
||||
<brief_description>
|
||||
Limit bone rotation angular velocity.
|
||||
</brief_description>
|
||||
<description>
|
||||
This modifier limits bone rotation angular velocity by comparing poses between previous and current frame.
|
||||
You can add bone chains by specifying their root and end bones, then add the bones between them to a list. Modifier processes either that list or the bones excluding those in the list depending on the option [member exclude].
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
<methods>
|
||||
<method name="clear_chains">
|
||||
<return type="void" />
|
||||
<description>
|
||||
Clear all chains.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_end_bone" qualifiers="const">
|
||||
<return type="int" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<description>
|
||||
Returns the end bone index of the bone chain.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_end_bone_name" qualifiers="const">
|
||||
<return type="String" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<description>
|
||||
Returns the end bone name of the bone chain.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_root_bone" qualifiers="const">
|
||||
<return type="int" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<description>
|
||||
Returns the root bone index of the bone chain.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_root_bone_name" qualifiers="const">
|
||||
<return type="String" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<description>
|
||||
Returns the root bone name of the bone chain.
|
||||
</description>
|
||||
</method>
|
||||
<method name="reset">
|
||||
<return type="void" />
|
||||
<description>
|
||||
Sets the reference pose for angle comparison to the current pose with the influence of constraints removed. This function is automatically triggered when joints change or upon activation.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_end_bone">
|
||||
<return type="void" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<param index="1" name="bone" type="int" />
|
||||
<description>
|
||||
Sets the end bone index of the bone chain.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_end_bone_name">
|
||||
<return type="void" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<param index="1" name="bone_name" type="String" />
|
||||
<description>
|
||||
Sets the end bone name of the bone chain.
|
||||
[b]Note:[/b] End bone must be the root bone or a child of the root bone.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_root_bone">
|
||||
<return type="void" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<param index="1" name="bone" type="int" />
|
||||
<description>
|
||||
Sets the root bone index of the bone chain.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_root_bone_name">
|
||||
<return type="void" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<param index="1" name="bone_name" type="String" />
|
||||
<description>
|
||||
Sets the root bone name of the bone chain.
|
||||
</description>
|
||||
</method>
|
||||
</methods>
|
||||
<members>
|
||||
<member name="chain_count" type="int" setter="set_chain_count" getter="get_chain_count" default="0">
|
||||
The number of chains.
|
||||
</member>
|
||||
<member name="exclude" type="bool" setter="set_exclude" getter="is_exclude" default="false">
|
||||
If [code]true[/code], the modifier processes bones not included in the bone list.
|
||||
If [code]false[/code], the bones processed by the modifier are equal to the bone list.
|
||||
</member>
|
||||
<member name="joint_count" type="int" setter="" getter="_get_joint_count" default="0">
|
||||
The number of joints in the list which created by chains dynamically.
|
||||
</member>
|
||||
<member name="max_angular_velocity" type="float" setter="set_max_angular_velocity" getter="get_max_angular_velocity" default="6.2831855">
|
||||
The maximum angular velocity per second.
|
||||
</member>
|
||||
</members>
|
||||
</class>
|
||||
1
editor/icons/LimitAngularVelocityModifier3D.svg
Normal file
1
editor/icons/LimitAngularVelocityModifier3D.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><g fill="#fc7f7f"><path d="m9.427 16-.252-.252c-1.563-1.557-1.567-4.095-.011-5.658 1.559-1.563 4.098-1.567 5.66-.01.756.754 1.174 1.758 1.176 2.827.002 1.067-.411 2.074-1.168 2.831l-.252.253-.263-.243-.857-.857.507-.505.592.59c.398-.494.644-1.086.709-1.713h-.982v-.716h.98c-.068-.628-.315-1.217-.717-1.706l-.582.583-.507-.505.582-.58c-.498-.396-1.085-.625-1.686-.691v.981h-.715v-.982c-.601.066-1.188.295-1.685.691l.579.58-.505.505-.582-.583c-.408.501-.647 1.097-.715 1.706h.979v.716h-.98c.064.611.301 1.21.708 1.713l.591-.59.505.505z"/><path d="M11.499 12.578c-.189.266-.129.636.138.825.266.188.633.127.822-.139.06-.08.095-.176.107-.276l.274-1.733-.386-.146zM8.456 9.384c.622-.625 1.388-1.042 2.217-1.266l.502-.501c1.134.753 2.665.445 3.419-.689s.446-2.665-.688-3.419c-.309-.205-.66-.338-1.026-.389-.188-1.349-1.433-2.291-2.782-2.103s-2.29 1.433-2.103 2.782c.051.367.184.717.389 1.026l-3.56 3.56c-1.134-.755-2.665-.447-3.419.687s-.446 2.664.688 3.419c.308.204.659.338 1.026.389.188 1.349 1.433 2.29 2.782 2.103.519-.072.976-.303 1.333-.634-.509-1.703-.117-3.62 1.222-4.965z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
412
scene/3d/limit_angular_velocity_modifier_3d.cpp
Normal file
412
scene/3d/limit_angular_velocity_modifier_3d.cpp
Normal file
@@ -0,0 +1,412 @@
|
||||
/**************************************************************************/
|
||||
/* limit_angular_velocity_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 "limit_angular_velocity_modifier_3d.h"
|
||||
|
||||
bool LimitAngularVelocityModifier3D::_set(const StringName &p_path, const Variant &p_value) {
|
||||
String path = p_path;
|
||||
|
||||
if (path.begins_with("chains/")) {
|
||||
int which = path.get_slicec('/', 1).to_int();
|
||||
String what = path.get_slicec('/', 2);
|
||||
ERR_FAIL_INDEX_V(which, (int)chains.size(), false);
|
||||
|
||||
if (what == "root_bone_name") {
|
||||
set_root_bone_name(which, p_value);
|
||||
} else if (what == "root_bone") {
|
||||
set_root_bone(which, p_value);
|
||||
} else if (what == "end_bone_name") {
|
||||
set_end_bone_name(which, p_value);
|
||||
} else if (what == "end_bone") {
|
||||
set_end_bone(which, p_value);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LimitAngularVelocityModifier3D::_get(const StringName &p_path, Variant &r_ret) const {
|
||||
String path = p_path;
|
||||
|
||||
if (path.begins_with("chains/")) {
|
||||
int which = path.get_slicec('/', 1).to_int();
|
||||
String what = path.get_slicec('/', 2);
|
||||
ERR_FAIL_INDEX_V(which, (int)chains.size(), false);
|
||||
|
||||
if (what == "root_bone_name") {
|
||||
r_ret = get_root_bone_name(which);
|
||||
} else if (what == "root_bone") {
|
||||
r_ret = get_root_bone(which);
|
||||
} else if (what == "end_bone_name") {
|
||||
r_ret = get_end_bone_name(which);
|
||||
} else if (what == "end_bone") {
|
||||
r_ret = get_end_bone(which);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (path.begins_with("joints/")) {
|
||||
int which = path.get_slicec('/', 1).to_int();
|
||||
String what = path.get_slicec('/', 2);
|
||||
ERR_FAIL_COND_V(!joints.has(which), false);
|
||||
if (what == "bone_name") {
|
||||
r_ret = _get_joint_bone_name(which);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void LimitAngularVelocityModifier3D::_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 (uint32_t i = 0; i < chains.size(); i++) {
|
||||
String path = "chains/" + itos(i) + "/";
|
||||
p_list->push_back(PropertyInfo(Variant::STRING, path + "root_bone_name", PROPERTY_HINT_ENUM_SUGGESTION, enum_hint));
|
||||
p_list->push_back(PropertyInfo(Variant::INT, path + "root_bone", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
|
||||
p_list->push_back(PropertyInfo(Variant::STRING, path + "end_bone_name", PROPERTY_HINT_ENUM_SUGGESTION, enum_hint));
|
||||
p_list->push_back(PropertyInfo(Variant::INT, path + "end_bone", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
|
||||
}
|
||||
|
||||
for (const KeyValue<int, StringName> &E : joints) {
|
||||
String path = "joints/" + itos(E.key) + "/";
|
||||
p_list->push_back(PropertyInfo(Variant::STRING, path + "bone_name", PROPERTY_HINT_ENUM_SUGGESTION, enum_hint, PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY));
|
||||
}
|
||||
}
|
||||
|
||||
void LimitAngularVelocityModifier3D::_validate_property(PropertyInfo &p_property) const {
|
||||
if (p_property.name == "joint_count") {
|
||||
p_property.usage = PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ARRAY | PROPERTY_USAGE_READ_ONLY;
|
||||
p_property.class_name = "Joints,joints/,static,const";
|
||||
}
|
||||
}
|
||||
|
||||
void LimitAngularVelocityModifier3D::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
_make_joints_dirty();
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
// Setting.
|
||||
|
||||
void LimitAngularVelocityModifier3D::set_root_bone_name(int p_index, const String &p_bone_name) {
|
||||
ERR_FAIL_INDEX(p_index, (int)chains.size());
|
||||
chains[p_index].root_bone.name = p_bone_name;
|
||||
Skeleton3D *sk = get_skeleton();
|
||||
if (sk) {
|
||||
set_root_bone(p_index, sk->find_bone(chains[p_index].root_bone.name));
|
||||
}
|
||||
}
|
||||
|
||||
String LimitAngularVelocityModifier3D::get_root_bone_name(int p_index) const {
|
||||
ERR_FAIL_INDEX_V(p_index, (int)chains.size(), String());
|
||||
return chains[p_index].root_bone.name;
|
||||
}
|
||||
|
||||
void LimitAngularVelocityModifier3D::set_root_bone(int p_index, int p_bone) {
|
||||
ERR_FAIL_INDEX(p_index, (int)chains.size());
|
||||
bool changed = chains[p_index].root_bone.bone != p_bone;
|
||||
chains[p_index].root_bone.bone = p_bone;
|
||||
Skeleton3D *sk = get_skeleton();
|
||||
if (sk) {
|
||||
if (chains[p_index].root_bone.bone <= -1 || chains[p_index].root_bone.bone >= sk->get_bone_count()) {
|
||||
WARN_PRINT("Root bone index out of range!");
|
||||
chains[p_index].root_bone.bone = -1;
|
||||
} else {
|
||||
chains[p_index].root_bone.name = sk->get_bone_name(chains[p_index].root_bone.bone);
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
_make_joints_dirty();
|
||||
}
|
||||
}
|
||||
|
||||
int LimitAngularVelocityModifier3D::get_root_bone(int p_index) const {
|
||||
ERR_FAIL_INDEX_V(p_index, (int)chains.size(), -1);
|
||||
return chains[p_index].root_bone.bone;
|
||||
}
|
||||
|
||||
void LimitAngularVelocityModifier3D::set_end_bone_name(int p_index, const String &p_bone_name) {
|
||||
ERR_FAIL_INDEX(p_index, (int)chains.size());
|
||||
chains[p_index].end_bone.name = p_bone_name;
|
||||
Skeleton3D *sk = get_skeleton();
|
||||
if (sk) {
|
||||
set_end_bone(p_index, sk->find_bone(chains[p_index].end_bone.name));
|
||||
}
|
||||
}
|
||||
|
||||
String LimitAngularVelocityModifier3D::get_end_bone_name(int p_index) const {
|
||||
ERR_FAIL_INDEX_V(p_index, (int)chains.size(), String());
|
||||
return chains[p_index].end_bone.name;
|
||||
}
|
||||
|
||||
void LimitAngularVelocityModifier3D::set_end_bone(int p_index, int p_bone) {
|
||||
ERR_FAIL_INDEX(p_index, (int)chains.size());
|
||||
bool changed = chains[p_index].end_bone.bone != p_bone;
|
||||
chains[p_index].end_bone.bone = p_bone;
|
||||
Skeleton3D *sk = get_skeleton();
|
||||
if (sk) {
|
||||
if (chains[p_index].end_bone.bone <= -1 || chains[p_index].end_bone.bone >= sk->get_bone_count()) {
|
||||
WARN_PRINT("End bone index out of range!");
|
||||
chains[p_index].end_bone.bone = -1;
|
||||
} else {
|
||||
chains[p_index].end_bone.name = sk->get_bone_name(chains[p_index].end_bone.bone);
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
_make_joints_dirty();
|
||||
}
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
int LimitAngularVelocityModifier3D::get_end_bone(int p_index) const {
|
||||
ERR_FAIL_INDEX_V(p_index, (int)chains.size(), -1);
|
||||
return chains[p_index].end_bone.bone;
|
||||
}
|
||||
|
||||
void LimitAngularVelocityModifier3D::set_chain_count(int p_count) {
|
||||
ERR_FAIL_COND(p_count < 0);
|
||||
chains.resize(p_count);
|
||||
_make_joints_dirty();
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
int LimitAngularVelocityModifier3D::get_chain_count() const {
|
||||
return chains.size();
|
||||
}
|
||||
|
||||
void LimitAngularVelocityModifier3D::clear_chains() {
|
||||
set_chain_count(0);
|
||||
}
|
||||
|
||||
String LimitAngularVelocityModifier3D::_get_joint_bone_name(int p_bone) const {
|
||||
ERR_FAIL_COND_V(!joints.has(p_bone), String());
|
||||
return joints[p_bone];
|
||||
}
|
||||
|
||||
int LimitAngularVelocityModifier3D::_get_joint_count() const {
|
||||
return joints.size();
|
||||
}
|
||||
|
||||
void LimitAngularVelocityModifier3D::set_max_angular_velocity(double p_angular_velocity) {
|
||||
max_angular_velocity = p_angular_velocity;
|
||||
}
|
||||
|
||||
double LimitAngularVelocityModifier3D::get_max_angular_velocity() const {
|
||||
return max_angular_velocity;
|
||||
}
|
||||
|
||||
void LimitAngularVelocityModifier3D::set_exclude(bool p_exclude) {
|
||||
exclude = p_exclude;
|
||||
}
|
||||
|
||||
bool LimitAngularVelocityModifier3D::is_exclude() const {
|
||||
return exclude;
|
||||
}
|
||||
|
||||
void LimitAngularVelocityModifier3D::_bind_methods() {
|
||||
// Setting.
|
||||
ClassDB::bind_method(D_METHOD("set_root_bone_name", "index", "bone_name"), &LimitAngularVelocityModifier3D::set_root_bone_name);
|
||||
ClassDB::bind_method(D_METHOD("get_root_bone_name", "index"), &LimitAngularVelocityModifier3D::get_root_bone_name);
|
||||
ClassDB::bind_method(D_METHOD("set_root_bone", "index", "bone"), &LimitAngularVelocityModifier3D::set_root_bone);
|
||||
ClassDB::bind_method(D_METHOD("get_root_bone", "index"), &LimitAngularVelocityModifier3D::get_root_bone);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_end_bone_name", "index", "bone_name"), &LimitAngularVelocityModifier3D::set_end_bone_name);
|
||||
ClassDB::bind_method(D_METHOD("get_end_bone_name", "index"), &LimitAngularVelocityModifier3D::get_end_bone_name);
|
||||
ClassDB::bind_method(D_METHOD("set_end_bone", "index", "bone"), &LimitAngularVelocityModifier3D::set_end_bone);
|
||||
ClassDB::bind_method(D_METHOD("get_end_bone", "index"), &LimitAngularVelocityModifier3D::get_end_bone);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_chain_count", "count"), &LimitAngularVelocityModifier3D::set_chain_count);
|
||||
ClassDB::bind_method(D_METHOD("get_chain_count"), &LimitAngularVelocityModifier3D::get_chain_count);
|
||||
ClassDB::bind_method(D_METHOD("clear_chains"), &LimitAngularVelocityModifier3D::clear_chains);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_max_angular_velocity", "angular_velocity"), &LimitAngularVelocityModifier3D::set_max_angular_velocity);
|
||||
ClassDB::bind_method(D_METHOD("get_max_angular_velocity"), &LimitAngularVelocityModifier3D::get_max_angular_velocity);
|
||||
ClassDB::bind_method(D_METHOD("set_exclude", "exclude"), &LimitAngularVelocityModifier3D::set_exclude);
|
||||
ClassDB::bind_method(D_METHOD("is_exclude"), &LimitAngularVelocityModifier3D::is_exclude);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("reset"), &LimitAngularVelocityModifier3D::reset);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("_get_joint_count"), &LimitAngularVelocityModifier3D::_get_joint_count);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_angular_velocity", PROPERTY_HINT_RANGE, "0,720,or_greater,radians_as_degrees,suffix:" + String(U"°") + "/s"), "set_max_angular_velocity", "get_max_angular_velocity");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "exclude"), "set_exclude", "is_exclude");
|
||||
ADD_ARRAY_COUNT("Chains", "chain_count", "set_chain_count", "get_chain_count", "chains/");
|
||||
ADD_ARRAY_COUNT("Joints", "joint_count", "", "_get_joint_count", "joints/");
|
||||
}
|
||||
|
||||
void LimitAngularVelocityModifier3D::_set_active(bool p_active) {
|
||||
if (p_active) {
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
void LimitAngularVelocityModifier3D::_skeleton_changed(Skeleton3D *p_old, Skeleton3D *p_new) {
|
||||
_make_joints_dirty();
|
||||
}
|
||||
|
||||
void LimitAngularVelocityModifier3D::_validate_bone_names() {
|
||||
for (uint32_t i = 0; i < chains.size(); i++) {
|
||||
// Prior bone name.
|
||||
if (!chains[i].root_bone.name.is_empty()) {
|
||||
set_root_bone_name(i, chains[i].root_bone.name);
|
||||
} else if (chains[i].root_bone.bone != -1) {
|
||||
set_root_bone(i, chains[i].root_bone.bone);
|
||||
}
|
||||
// Prior bone name.
|
||||
if (!chains[i].end_bone.name.is_empty()) {
|
||||
set_end_bone_name(i, chains[i].end_bone.name);
|
||||
} else if (chains[i].end_bone.bone != -1) {
|
||||
set_end_bone(i, chains[i].end_bone.bone);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LimitAngularVelocityModifier3D::_make_joints_dirty() {
|
||||
if (joints_dirty) {
|
||||
return;
|
||||
}
|
||||
joints_dirty = true;
|
||||
callable_mp(this, &LimitAngularVelocityModifier3D::_update_joints).call_deferred();
|
||||
}
|
||||
|
||||
void LimitAngularVelocityModifier3D::_update_joints() {
|
||||
joints.clear();
|
||||
bones.clear();
|
||||
|
||||
Skeleton3D *sk = get_skeleton();
|
||||
if (!sk) {
|
||||
joints_dirty = false;
|
||||
return;
|
||||
}
|
||||
|
||||
LocalVector<int> tmp_joints;
|
||||
for (uint32_t i = 0; i < chains.size(); i++) {
|
||||
tmp_joints.clear();
|
||||
Chain cn = chains[i];
|
||||
int current_bone = cn.end_bone.bone;
|
||||
int root_bone = cn.root_bone.bone;
|
||||
if (current_bone < 0 || root_bone < 0) {
|
||||
continue;
|
||||
}
|
||||
// Validation.
|
||||
bool valid = false;
|
||||
while (current_bone >= 0) {
|
||||
if (current_bone == root_bone) {
|
||||
valid = true;
|
||||
break;
|
||||
}
|
||||
current_bone = sk->get_bone_parent(current_bone);
|
||||
}
|
||||
if (!valid) {
|
||||
ERR_FAIL_EDMSG("Chains[" + itos(i) + "]: End bone must be the same as or a child of root bone.");
|
||||
continue;
|
||||
}
|
||||
current_bone = cn.end_bone.bone;
|
||||
while (current_bone != root_bone) {
|
||||
tmp_joints.push_back(current_bone);
|
||||
current_bone = sk->get_bone_parent(current_bone);
|
||||
}
|
||||
tmp_joints.push_back(current_bone);
|
||||
for (uint32_t j = 0; j < tmp_joints.size(); j++) {
|
||||
int bn = tmp_joints[j];
|
||||
if (!joints.has(bn)) {
|
||||
joints.insert(bn, sk->get_bone_name(bn));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (exclude) {
|
||||
for (int b = 0; b < sk->get_bone_count(); b++) {
|
||||
if (joints.has(b)) {
|
||||
continue;
|
||||
}
|
||||
BoneRot br;
|
||||
br.first = b;
|
||||
br.second = sk->get_bone_pose_rotation(b);
|
||||
bones.push_back(br);
|
||||
}
|
||||
} else {
|
||||
for (const KeyValue<int, StringName> &E : joints) {
|
||||
BoneRot br;
|
||||
br.first = E.key;
|
||||
br.second = sk->get_bone_pose_rotation(E.key);
|
||||
bones.push_back(br);
|
||||
}
|
||||
}
|
||||
|
||||
joints_dirty = false;
|
||||
reset();
|
||||
}
|
||||
|
||||
void LimitAngularVelocityModifier3D::_process_modification(double p_delta) {
|
||||
Skeleton3D *skeleton = get_skeleton();
|
||||
if (!skeleton) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (init_needed) {
|
||||
// Note:
|
||||
// The pose retrieval within `_update_joints()` is done outside the skeleton's update process,
|
||||
// so it ignores the pose resulting from the previous modifier's modification.
|
||||
// This causes unintended initialization when `active` is set to true, so it must be initialized here.
|
||||
for (uint32_t i = 0; i < bones.size(); i++) {
|
||||
bones[i].second = skeleton->get_bone_pose_rotation(bones[i].first);
|
||||
}
|
||||
init_needed = false;
|
||||
}
|
||||
|
||||
double limit_in_frame = max_angular_velocity * p_delta;
|
||||
for (uint32_t i = 0; i < bones.size(); i++) {
|
||||
int bn = bones[i].first;
|
||||
Quaternion dest = skeleton->get_bone_pose_rotation(bn);
|
||||
double diff = bones[i].second.angle_to(dest);
|
||||
if (!Math::is_zero_approx(diff)) {
|
||||
bones[i].second = bones[i].second.slerp(dest, MIN(1.0, limit_in_frame / diff));
|
||||
}
|
||||
skeleton->set_bone_pose_rotation(bn, bones[i].second);
|
||||
}
|
||||
}
|
||||
|
||||
void LimitAngularVelocityModifier3D::reset() {
|
||||
init_needed = true;
|
||||
}
|
||||
|
||||
LimitAngularVelocityModifier3D::~LimitAngularVelocityModifier3D() {
|
||||
clear_chains();
|
||||
}
|
||||
110
scene/3d/limit_angular_velocity_modifier_3d.h
Normal file
110
scene/3d/limit_angular_velocity_modifier_3d.h
Normal file
@@ -0,0 +1,110 @@
|
||||
/**************************************************************************/
|
||||
/* limit_angular_velocity_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/skeleton_modifier_3d.h"
|
||||
|
||||
class LimitAngularVelocityModifier3D : public SkeletonModifier3D {
|
||||
GDCLASS(LimitAngularVelocityModifier3D, SkeletonModifier3D);
|
||||
|
||||
public:
|
||||
struct BoneJoint {
|
||||
StringName name;
|
||||
int bone = -1;
|
||||
};
|
||||
|
||||
struct Chain {
|
||||
BoneJoint root_bone;
|
||||
BoneJoint end_bone;
|
||||
};
|
||||
|
||||
typedef Pair<int, Quaternion> BoneRot;
|
||||
|
||||
private:
|
||||
bool exclude = false;
|
||||
double max_angular_velocity = Math::TAU;
|
||||
|
||||
LocalVector<Chain> chains;
|
||||
RBMap<int, StringName> joints;
|
||||
LocalVector<BoneRot> bones;
|
||||
|
||||
bool joints_dirty = false;
|
||||
bool init_needed = true;
|
||||
|
||||
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_property(PropertyInfo &p_property) const;
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
void _notification(int p_what);
|
||||
|
||||
virtual void _set_active(bool p_active) override;
|
||||
virtual void _skeleton_changed(Skeleton3D *p_old, Skeleton3D *p_new) override;
|
||||
|
||||
virtual void _validate_bone_names() override;
|
||||
|
||||
void _make_joints_dirty();
|
||||
void _update_joints();
|
||||
|
||||
// For editor.
|
||||
String _get_joint_bone_name(int p_bone) const;
|
||||
int _get_joint_count() const;
|
||||
|
||||
virtual void _process_modification(double p_delta) override;
|
||||
|
||||
public:
|
||||
void set_root_bone_name(int p_index, const String &p_bone_name);
|
||||
String get_root_bone_name(int p_index) const;
|
||||
void set_root_bone(int p_index, int p_bone);
|
||||
int get_root_bone(int p_index) const;
|
||||
|
||||
void set_end_bone_name(int p_index, const String &p_bone_name);
|
||||
String get_end_bone_name(int p_index) const;
|
||||
void set_end_bone(int p_index, int p_bone);
|
||||
int get_end_bone(int p_index) const;
|
||||
|
||||
void set_chain_count(int p_count);
|
||||
int get_chain_count() const;
|
||||
void clear_chains();
|
||||
|
||||
void set_max_angular_velocity(double p_angular_velocity);
|
||||
double get_max_angular_velocity() const;
|
||||
|
||||
void set_exclude(bool p_exclude);
|
||||
bool is_exclude() const;
|
||||
|
||||
void reset();
|
||||
|
||||
~LimitAngularVelocityModifier3D();
|
||||
};
|
||||
@@ -239,6 +239,7 @@
|
||||
#include "scene/3d/light_3d.h"
|
||||
#include "scene/3d/lightmap_gi.h"
|
||||
#include "scene/3d/lightmap_probe.h"
|
||||
#include "scene/3d/limit_angular_velocity_modifier_3d.h"
|
||||
#include "scene/3d/look_at_modifier_3d.h"
|
||||
#include "scene/3d/marker_3d.h"
|
||||
#include "scene/3d/mesh_instance_3d.h"
|
||||
@@ -682,6 +683,7 @@ void register_scene_types() {
|
||||
GDREGISTER_CLASS(FABRIK3D);
|
||||
GDREGISTER_CLASS(CCDIK3D);
|
||||
GDREGISTER_CLASS(JacobianIK3D);
|
||||
GDREGISTER_CLASS(LimitAngularVelocityModifier3D);
|
||||
|
||||
#ifndef XR_DISABLED
|
||||
GDREGISTER_CLASS(XRCamera3D);
|
||||
|
||||
Reference in New Issue
Block a user