Path: blob/master/scene/3d/bone_twist_disperser_3d.cpp
14709 views
/**************************************************************************/1/* bone_twist_disperser_3d.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "bone_twist_disperser_3d.h"3132bool BoneTwistDisperser3D::_set(const StringName &p_path, const Variant &p_value) {33String path = p_path;3435if (path.begins_with("settings/")) {36int which = path.get_slicec('/', 1).to_int();37String what = path.get_slicec('/', 2);38ERR_FAIL_INDEX_V(which, (int)settings.size(), false);3940if (what == "root_bone_name") {41set_root_bone_name(which, p_value);42} else if (what == "root_bone") {43set_root_bone(which, p_value);44} else if (what == "end_bone_name") {45set_end_bone_name(which, p_value);46} else if (what == "end_bone") {47set_end_bone(which, p_value);48} else if (what == "end_bone_direction") {49set_end_bone_direction(which, static_cast<BoneDirection>((int)p_value));50} else if (what == "extend_end_bone") {51set_extend_end_bone(which, p_value);52} else if (what == "twist_from_rest") {53set_twist_from_rest(which, p_value);54} else if (what == "twist_from") {55set_twist_from(which, p_value);56} else if (what == "disperse_mode") {57set_disperse_mode(which, static_cast<DisperseMode>((int)p_value));58} else if (what == "weight_position") {59set_weight_position(which, p_value);60} else if (what == "damping_curve") {61set_damping_curve(which, p_value);62} else if (what == "joint_count") {63set_joint_count(which, p_value);64} else if (what == "joints") {65int idx = path.get_slicec('/', 3).to_int();66String prop = path.get_slicec('/', 4);67if (prop == "bone_name") {68set_joint_bone_name(which, idx, p_value);69} else if (prop == "bone") {70set_joint_bone(which, idx, p_value);71} else if (prop == "twist_amount") {72set_joint_twist_amount(which, idx, p_value);73} else {74return false;75}76} else {77return false;78}79}80return true;81}8283bool BoneTwistDisperser3D::_get(const StringName &p_path, Variant &r_ret) const {84String path = p_path;8586if (path.begins_with("settings/")) {87int which = path.get_slicec('/', 1).to_int();88String what = path.get_slicec('/', 2);89ERR_FAIL_INDEX_V(which, (int)settings.size(), false);9091if (what == "root_bone_name") {92r_ret = get_root_bone_name(which);93} else if (what == "root_bone") {94r_ret = get_root_bone(which);95} else if (what == "end_bone_name") {96r_ret = get_end_bone_name(which);97} else if (what == "end_bone") {98r_ret = get_end_bone(which);99} else if (what == "end_bone_direction") {100r_ret = (int)get_end_bone_direction(which);101} else if (what == "reference_bone_name") {102r_ret = get_reference_bone_name(which);103} else if (what == "extend_end_bone") {104r_ret = is_end_bone_extended(which);105} else if (what == "twist_from_rest") {106r_ret = is_twist_from_rest(which);107} else if (what == "twist_from") {108r_ret = get_twist_from(which);109} else if (what == "disperse_mode") {110r_ret = (int)get_disperse_mode(which);111} else if (what == "weight_position") {112r_ret = get_weight_position(which);113} else if (what == "damping_curve") {114r_ret = get_damping_curve(which);115} else if (what == "joint_count") {116r_ret = get_joint_count(which);117} else if (what == "joints") {118int idx = path.get_slicec('/', 3).to_int();119String prop = path.get_slicec('/', 4);120if (prop == "bone_name") {121r_ret = get_joint_bone_name(which, idx);122} else if (prop == "bone") {123r_ret = get_joint_bone(which, idx);124} else if (prop == "twist_amount") {125r_ret = get_joint_twist_amount(which, idx);126} else {127return false;128}129} else {130return false;131}132}133return true;134}135136void BoneTwistDisperser3D::_get_property_list(List<PropertyInfo> *p_list) const {137String enum_hint;138Skeleton3D *skeleton = get_skeleton();139if (skeleton) {140enum_hint = skeleton->get_concatenated_bone_names();141}142143LocalVector<PropertyInfo> props;144145for (uint32_t i = 0; i < settings.size(); i++) {146String path = "settings/" + itos(i) + "/";147props.push_back(PropertyInfo(Variant::STRING, path + "root_bone_name", PROPERTY_HINT_ENUM_SUGGESTION, enum_hint));148props.push_back(PropertyInfo(Variant::INT, path + "root_bone", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));149props.push_back(PropertyInfo(Variant::STRING, path + "end_bone_name", PROPERTY_HINT_ENUM_SUGGESTION, enum_hint));150props.push_back(PropertyInfo(Variant::INT, path + "end_bone", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));151props.push_back(PropertyInfo(Variant::BOOL, path + "extend_end_bone"));152props.push_back(PropertyInfo(Variant::INT, path + "end_bone_direction", PROPERTY_HINT_ENUM, SkeletonModifier3D::get_hint_bone_direction()));153154props.push_back(PropertyInfo(Variant::STRING, path + "reference_bone_name", PROPERTY_HINT_ENUM_SUGGESTION, enum_hint, PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY));155props.push_back(PropertyInfo(Variant::BOOL, path + "twist_from_rest"));156props.push_back(PropertyInfo(Variant::QUATERNION, path + "twist_from"));157props.push_back(PropertyInfo(Variant::INT, path + "disperse_mode", PROPERTY_HINT_ENUM, "Even,Weighted,Custom"));158props.push_back(PropertyInfo(Variant::FLOAT, path + "weight_position", PROPERTY_HINT_RANGE, "0,1,0.001"));159props.push_back(PropertyInfo(Variant::OBJECT, path + "damping_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"));160161props.push_back(PropertyInfo(Variant::INT, path + "joint_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ARRAY, "Joints," + path + "joints/,static,const"));162for (uint32_t j = 0; j < settings[i]->joints.size(); j++) {163String joint_path = path + "joints/" + itos(j) + "/";164props.push_back(PropertyInfo(Variant::STRING, joint_path + "bone_name", PROPERTY_HINT_ENUM_SUGGESTION, enum_hint, PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY));165props.push_back(PropertyInfo(Variant::INT, joint_path + "bone", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_READ_ONLY));166props.push_back(PropertyInfo(Variant::FLOAT, joint_path + "twist_amount", PROPERTY_HINT_RANGE, "0,1,0.001,or_greater,or_less"));167}168}169170for (PropertyInfo &p : props) {171_validate_dynamic_prop(p);172p_list->push_back(p);173}174}175176void BoneTwistDisperser3D::_validate_dynamic_prop(PropertyInfo &p_property) const {177PackedStringArray split = p_property.name.split("/");178if (split.size() > 2 && split[0] == "settings") {179int which = split[1].to_int();180181// Extended end bone option.182bool force_hide = false;183if (split[2] == "extend_end_bone" && get_end_bone(which) == -1) {184p_property.usage = PROPERTY_USAGE_NONE;185force_hide = true;186}187if (force_hide || (split[2] == "end_bone_direction" && !is_end_bone_extended(which))) {188p_property.usage = PROPERTY_USAGE_NONE;189}190191if (split[2] == "twist_from" && is_twist_from_rest(which)) {192p_property.usage = PROPERTY_USAGE_NONE;193}194195if (split[2] == "weight_position" && get_disperse_mode(which) != DISPERSE_MODE_WEIGHTED) {196p_property.usage = PROPERTY_USAGE_NONE;197}198199if (split[2] == "damping_curve" && get_disperse_mode(which) != DISPERSE_MODE_CUSTOM) {200p_property.usage = PROPERTY_USAGE_NONE;201}202203if (split[2] == "joints" && split[4] == "twist_amount") {204bool mutable_amount = true;205if (get_disperse_mode(which) != DISPERSE_MODE_CUSTOM) {206mutable_amount = false;207} else if (!is_end_bone_extended(which)) {208int joint = split[3].to_int();209mutable_amount = joint < get_joint_count(which) - 1; // Hide child of reference bone.210}211if (get_damping_curve(which).is_valid()) {212p_property.usage |= PROPERTY_USAGE_READ_ONLY;213}214if (!mutable_amount) {215p_property.usage = PROPERTY_USAGE_NONE;216}217}218}219}220221void BoneTwistDisperser3D::_notification(int p_what) {222switch (p_what) {223case NOTIFICATION_ENTER_TREE: {224_make_all_joints_dirty();225} break;226}227}228229void BoneTwistDisperser3D::_set_active(bool p_active) {230if (p_active) {231_make_all_joints_dirty();232}233}234235void BoneTwistDisperser3D::_skeleton_changed(Skeleton3D *p_old, Skeleton3D *p_new) {236_make_all_joints_dirty();237}238239// Setting.240241void BoneTwistDisperser3D::set_mutable_bone_axes(bool p_enabled) {242mutable_bone_axes = p_enabled;243}244245bool BoneTwistDisperser3D::are_bone_axes_mutable() const {246return mutable_bone_axes;247}248249void BoneTwistDisperser3D::set_root_bone_name(int p_index, const String &p_bone_name) {250ERR_FAIL_INDEX(p_index, (int)settings.size());251settings[p_index]->root_bone.name = p_bone_name;252Skeleton3D *sk = get_skeleton();253if (sk) {254set_root_bone(p_index, sk->find_bone(settings[p_index]->root_bone.name));255}256}257258String BoneTwistDisperser3D::get_root_bone_name(int p_index) const {259ERR_FAIL_INDEX_V(p_index, (int)settings.size(), String());260return settings[p_index]->root_bone.name;261}262263void BoneTwistDisperser3D::set_root_bone(int p_index, int p_bone) {264ERR_FAIL_INDEX(p_index, (int)settings.size());265bool changed = settings[p_index]->root_bone.bone != p_bone;266settings[p_index]->root_bone.bone = p_bone;267Skeleton3D *sk = get_skeleton();268if (sk) {269if (settings[p_index]->root_bone.bone <= -1 || settings[p_index]->root_bone.bone >= sk->get_bone_count()) {270WARN_PRINT("Root bone index out of range!");271settings[p_index]->root_bone.bone = -1;272} else {273settings[p_index]->root_bone.name = sk->get_bone_name(settings[p_index]->root_bone.bone);274}275}276if (changed) {277_make_joints_dirty(p_index);278}279}280281int BoneTwistDisperser3D::get_root_bone(int p_index) const {282ERR_FAIL_INDEX_V(p_index, (int)settings.size(), -1);283return settings[p_index]->root_bone.bone;284}285286void BoneTwistDisperser3D::set_end_bone_name(int p_index, const String &p_bone_name) {287ERR_FAIL_INDEX(p_index, (int)settings.size());288settings[p_index]->end_bone.name = p_bone_name;289Skeleton3D *sk = get_skeleton();290if (sk) {291set_end_bone(p_index, sk->find_bone(settings[p_index]->end_bone.name));292}293}294295String BoneTwistDisperser3D::get_end_bone_name(int p_index) const {296ERR_FAIL_INDEX_V(p_index, (int)settings.size(), String());297return settings[p_index]->end_bone.name;298}299300void BoneTwistDisperser3D::set_end_bone(int p_index, int p_bone) {301ERR_FAIL_INDEX(p_index, (int)settings.size());302bool changed = settings[p_index]->end_bone.bone != p_bone;303settings[p_index]->end_bone.bone = p_bone;304Skeleton3D *sk = get_skeleton();305if (sk) {306if (settings[p_index]->end_bone.bone <= -1 || settings[p_index]->end_bone.bone >= sk->get_bone_count()) {307WARN_PRINT("End bone index out of range!");308settings[p_index]->end_bone.bone = -1;309} else {310settings[p_index]->end_bone.name = sk->get_bone_name(settings[p_index]->end_bone.bone);311}312}313if (changed) {314_make_joints_dirty(p_index);315}316notify_property_list_changed();317}318319int BoneTwistDisperser3D::get_end_bone(int p_index) const {320ERR_FAIL_INDEX_V(p_index, (int)settings.size(), -1);321return settings[p_index]->end_bone.bone;322}323324void BoneTwistDisperser3D::set_extend_end_bone(int p_index, bool p_enabled) {325ERR_FAIL_INDEX(p_index, (int)settings.size());326settings[p_index]->extend_end_bone = p_enabled;327_update_reference_bone(p_index);328notify_property_list_changed();329}330331bool BoneTwistDisperser3D::is_end_bone_extended(int p_index) const {332ERR_FAIL_INDEX_V(p_index, (int)settings.size(), false);333return settings[p_index]->extend_end_bone;334}335336void BoneTwistDisperser3D::set_end_bone_direction(int p_index, BoneDirection p_bone_direction) {337ERR_FAIL_INDEX(p_index, (int)settings.size());338settings[p_index]->end_bone_direction = p_bone_direction;339}340341SkeletonModifier3D::BoneDirection BoneTwistDisperser3D::get_end_bone_direction(int p_index) const {342ERR_FAIL_INDEX_V(p_index, (int)settings.size(), BONE_DIRECTION_FROM_PARENT);343return settings[p_index]->end_bone_direction;344}345346void BoneTwistDisperser3D::set_twist_from_rest(int p_index, bool p_enabled) {347ERR_FAIL_INDEX(p_index, (int)settings.size());348settings[p_index]->twist_from_rest = p_enabled;349notify_property_list_changed();350}351352bool BoneTwistDisperser3D::is_twist_from_rest(int p_index) const {353ERR_FAIL_INDEX_V(p_index, (int)settings.size(), true);354return settings[p_index]->twist_from_rest;355}356357void BoneTwistDisperser3D::set_twist_from(int p_index, const Quaternion &p_from) {358ERR_FAIL_INDEX(p_index, (int)settings.size());359settings[p_index]->twist_from = p_from;360}361362Quaternion BoneTwistDisperser3D::get_twist_from(int p_index) const {363ERR_FAIL_INDEX_V(p_index, (int)settings.size(), Quaternion());364return settings[p_index]->twist_from;365}366367void BoneTwistDisperser3D::_update_reference_bone(int p_index) {368ERR_FAIL_INDEX(p_index, (int)settings.size());369LocalVector<DisperseJointSetting> &joints = settings[p_index]->joints;370if (joints.size() >= 2) {371if (settings[p_index]->extend_end_bone) {372settings[p_index]->reference_bone = settings[p_index]->end_bone;373_update_curve(p_index);374return;375} else {376Skeleton3D *sk = get_skeleton();377if (sk) {378int parent = sk->get_bone_parent(settings[p_index]->end_bone.bone);379if (parent >= 0) {380settings[p_index]->reference_bone.bone = parent;381settings[p_index]->reference_bone.name = sk->get_bone_name(parent);382_update_curve(p_index);383return;384}385}386}387}388settings[p_index]->reference_bone.bone = -1;389settings[p_index]->reference_bone.name = String();390}391392void BoneTwistDisperser3D::_update_curve(int p_index) {393Ref<Curve> curve = settings[p_index]->damping_curve;394if (curve.is_null()) {395return;396}397LocalVector<DisperseJointSetting> &joints = settings[p_index]->joints;398float unit = (int)joints.size() > 0 ? (1.0 / float((int)joints.size() - 1)) : 0.0;399for (uint32_t i = 0; i < joints.size(); i++) {400joints[i].custom_amount = curve->sample_baked(i * unit);401}402}403404String BoneTwistDisperser3D::get_reference_bone_name(int p_index) const {405ERR_FAIL_INDEX_V(p_index, (int)settings.size(), String());406return settings[p_index]->reference_bone.name;407}408409int BoneTwistDisperser3D::get_reference_bone(int p_index) const {410ERR_FAIL_INDEX_V(p_index, (int)settings.size(), -1);411return settings[p_index]->reference_bone.bone;412}413414void BoneTwistDisperser3D::set_disperse_mode(int p_index, DisperseMode p_disperse_mode) {415ERR_FAIL_INDEX(p_index, (int)settings.size());416settings[p_index]->disperse_mode = p_disperse_mode;417notify_property_list_changed();418}419420BoneTwistDisperser3D::DisperseMode BoneTwistDisperser3D::get_disperse_mode(int p_index) const {421ERR_FAIL_INDEX_V(p_index, (int)settings.size(), DISPERSE_MODE_EVEN);422return settings[p_index]->disperse_mode;423}424425void BoneTwistDisperser3D::set_weight_position(int p_index, float p_position) {426ERR_FAIL_INDEX(p_index, (int)settings.size());427settings[p_index]->weight_position = p_position;428}429430float BoneTwistDisperser3D::get_weight_position(int p_index) const {431ERR_FAIL_INDEX_V(p_index, (int)settings.size(), 0.0);432return settings[p_index]->weight_position;433}434435void BoneTwistDisperser3D::set_damping_curve(int p_index, const Ref<Curve> &p_damping_curve) {436ERR_FAIL_INDEX(p_index, (int)settings.size());437bool changed = settings[p_index]->damping_curve != p_damping_curve;438if (settings[p_index]->damping_curve.is_valid()) {439settings[p_index]->damping_curve->disconnect_changed(callable_mp(this, &BoneTwistDisperser3D::_update_curve));440}441settings[p_index]->damping_curve = p_damping_curve;442if (settings[p_index]->damping_curve.is_valid()) {443settings[p_index]->damping_curve->connect_changed(callable_mp(this, &BoneTwistDisperser3D::_update_curve).bind(p_index));444}445if (changed) {446_make_joints_dirty(p_index);447}448notify_property_list_changed();449}450451Ref<Curve> BoneTwistDisperser3D::get_damping_curve(int p_index) const {452ERR_FAIL_INDEX_V(p_index, (int)settings.size(), Ref<Curve>());453return settings[p_index]->damping_curve;454}455456// Individual joints.457458void BoneTwistDisperser3D::set_joint_bone_name(int p_index, int p_joint, const String &p_bone_name) {459// Exists only for indicate bone name on the inspector, no needs to make dirty joint array.460ERR_FAIL_INDEX(p_index, (int)settings.size());461LocalVector<DisperseJointSetting> &joints = settings[p_index]->joints;462ERR_FAIL_INDEX(p_joint, (int)joints.size());463joints[p_joint].joint.name = p_bone_name;464Skeleton3D *sk = get_skeleton();465if (sk) {466set_joint_bone(p_index, p_joint, sk->find_bone(joints[p_joint].joint.name));467}468}469470String BoneTwistDisperser3D::get_joint_bone_name(int p_index, int p_joint) const {471ERR_FAIL_INDEX_V(p_index, (int)settings.size(), String());472const LocalVector<DisperseJointSetting> &joints = settings[p_index]->joints;473ERR_FAIL_INDEX_V(p_joint, (int)joints.size(), String());474return joints[p_joint].joint.name;475}476477void BoneTwistDisperser3D::set_joint_bone(int p_index, int p_joint, int p_bone) {478ERR_FAIL_INDEX(p_index, (int)settings.size());479LocalVector<DisperseJointSetting> &joints = settings[p_index]->joints;480ERR_FAIL_INDEX(p_joint, (int)joints.size());481joints[p_joint].joint.bone = p_bone;482Skeleton3D *sk = get_skeleton();483if (sk) {484if (joints[p_joint].joint.bone <= -1 || joints[p_joint].joint.bone >= sk->get_bone_count()) {485WARN_PRINT("Joint bone index out of range!");486joints[p_joint].joint.bone = -1;487} else {488joints[p_joint].joint.name = sk->get_bone_name(joints[p_joint].joint.bone);489}490}491}492493int BoneTwistDisperser3D::get_joint_bone(int p_index, int p_joint) const {494ERR_FAIL_INDEX_V(p_index, (int)settings.size(), -1);495const LocalVector<DisperseJointSetting> &joints = settings[p_index]->joints;496ERR_FAIL_INDEX_V(p_joint, (int)joints.size(), -1);497return joints[p_joint].joint.bone;498}499500void BoneTwistDisperser3D::set_joint_count(int p_index, int p_count) {501ERR_FAIL_INDEX(p_index, (int)settings.size());502ERR_FAIL_COND(p_count < 0);503LocalVector<DisperseJointSetting> &joints = settings[p_index]->joints;504joints.resize(p_count);505notify_property_list_changed();506}507508int BoneTwistDisperser3D::get_joint_count(int p_index) const {509ERR_FAIL_INDEX_V(p_index, (int)settings.size(), 0);510const LocalVector<DisperseJointSetting> &joints = settings[p_index]->joints;511return joints.size();512}513514void BoneTwistDisperser3D::set_joint_twist_amount(int p_index, int p_joint, float p_amount) {515ERR_FAIL_INDEX(p_index, (int)settings.size());516LocalVector<DisperseJointSetting> &joints = settings[p_index]->joints;517ERR_FAIL_INDEX(p_joint, (int)joints.size());518joints[p_joint].custom_amount = p_amount;519}520521float BoneTwistDisperser3D::get_joint_twist_amount(int p_index, int p_joint) const {522ERR_FAIL_INDEX_V(p_index, (int)settings.size(), 0);523const LocalVector<DisperseJointSetting> &joints = settings[p_index]->joints;524ERR_FAIL_INDEX_V(p_joint, (int)joints.size(), 0);525return joints[p_joint].custom_amount;526}527528void BoneTwistDisperser3D::_bind_methods() {529ClassDB::bind_method(D_METHOD("set_setting_count", "count"), &BoneTwistDisperser3D::set_setting_count);530ClassDB::bind_method(D_METHOD("get_setting_count"), &BoneTwistDisperser3D::get_setting_count);531ClassDB::bind_method(D_METHOD("clear_settings"), &BoneTwistDisperser3D::clear_settings);532533ClassDB::bind_method(D_METHOD("set_mutable_bone_axes", "enabled"), &BoneTwistDisperser3D::set_mutable_bone_axes);534ClassDB::bind_method(D_METHOD("are_bone_axes_mutable"), &BoneTwistDisperser3D::are_bone_axes_mutable);535536// Setting.537ClassDB::bind_method(D_METHOD("set_root_bone_name", "index", "bone_name"), &BoneTwistDisperser3D::set_root_bone_name);538ClassDB::bind_method(D_METHOD("get_root_bone_name", "index"), &BoneTwistDisperser3D::get_root_bone_name);539ClassDB::bind_method(D_METHOD("set_root_bone", "index", "bone"), &BoneTwistDisperser3D::set_root_bone);540ClassDB::bind_method(D_METHOD("get_root_bone", "index"), &BoneTwistDisperser3D::get_root_bone);541542ClassDB::bind_method(D_METHOD("set_end_bone_name", "index", "bone_name"), &BoneTwistDisperser3D::set_end_bone_name);543ClassDB::bind_method(D_METHOD("get_end_bone_name", "index"), &BoneTwistDisperser3D::get_end_bone_name);544ClassDB::bind_method(D_METHOD("set_end_bone", "index", "bone"), &BoneTwistDisperser3D::set_end_bone);545ClassDB::bind_method(D_METHOD("get_end_bone", "index"), &BoneTwistDisperser3D::get_end_bone);546547ClassDB::bind_method(D_METHOD("get_reference_bone_name", "index"), &BoneTwistDisperser3D::get_reference_bone_name);548ClassDB::bind_method(D_METHOD("get_reference_bone", "index"), &BoneTwistDisperser3D::get_reference_bone);549550ClassDB::bind_method(D_METHOD("set_extend_end_bone", "index", "enabled"), &BoneTwistDisperser3D::set_extend_end_bone);551ClassDB::bind_method(D_METHOD("is_end_bone_extended", "index"), &BoneTwistDisperser3D::is_end_bone_extended);552ClassDB::bind_method(D_METHOD("set_end_bone_direction", "index", "bone_direction"), &BoneTwistDisperser3D::set_end_bone_direction);553ClassDB::bind_method(D_METHOD("get_end_bone_direction", "index"), &BoneTwistDisperser3D::get_end_bone_direction);554555ClassDB::bind_method(D_METHOD("set_twist_from_rest", "index", "enabled"), &BoneTwistDisperser3D::set_twist_from_rest);556ClassDB::bind_method(D_METHOD("is_twist_from_rest", "index"), &BoneTwistDisperser3D::is_twist_from_rest);557ClassDB::bind_method(D_METHOD("set_twist_from", "index", "from"), &BoneTwistDisperser3D::set_twist_from);558ClassDB::bind_method(D_METHOD("get_twist_from", "index"), &BoneTwistDisperser3D::get_twist_from);559560ClassDB::bind_method(D_METHOD("set_disperse_mode", "index", "disperse_mode"), &BoneTwistDisperser3D::set_disperse_mode);561ClassDB::bind_method(D_METHOD("get_disperse_mode", "index"), &BoneTwistDisperser3D::get_disperse_mode);562ClassDB::bind_method(D_METHOD("set_weight_position", "index", "weight_position"), &BoneTwistDisperser3D::set_weight_position);563ClassDB::bind_method(D_METHOD("get_weight_position", "index"), &BoneTwistDisperser3D::get_weight_position);564ClassDB::bind_method(D_METHOD("set_damping_curve", "index", "curve"), &BoneTwistDisperser3D::set_damping_curve);565ClassDB::bind_method(D_METHOD("get_damping_curve", "index"), &BoneTwistDisperser3D::get_damping_curve);566567// Individual joints.568ClassDB::bind_method(D_METHOD("get_joint_bone_name", "index", "joint"), &BoneTwistDisperser3D::get_joint_bone_name);569ClassDB::bind_method(D_METHOD("get_joint_bone", "index", "joint"), &BoneTwistDisperser3D::get_joint_bone);570ClassDB::bind_method(D_METHOD("get_joint_twist_amount", "index", "joint"), &BoneTwistDisperser3D::get_joint_twist_amount);571ClassDB::bind_method(D_METHOD("set_joint_twist_amount", "index", "joint", "twist_amount"), &BoneTwistDisperser3D::set_joint_twist_amount);572573ClassDB::bind_method(D_METHOD("get_joint_count", "index"), &BoneTwistDisperser3D::get_joint_count);574575ADD_PROPERTY(PropertyInfo(Variant::BOOL, "mutable_bone_axes"), "set_mutable_bone_axes", "are_bone_axes_mutable");576ADD_ARRAY_COUNT("Settings", "setting_count", "set_setting_count", "get_setting_count", "settings/");577578BIND_ENUM_CONSTANT(DISPERSE_MODE_EVEN);579BIND_ENUM_CONSTANT(DISPERSE_MODE_WEIGHTED);580BIND_ENUM_CONSTANT(DISPERSE_MODE_CUSTOM);581}582583void BoneTwistDisperser3D::_validate_bone_names() {584for (uint32_t i = 0; i < settings.size(); i++) {585// Prior bone name.586if (!settings[i]->root_bone.name.is_empty()) {587set_root_bone_name(i, settings[i]->root_bone.name);588} else if (settings[i]->root_bone.bone != -1) {589set_root_bone(i, settings[i]->root_bone.bone);590}591// Prior bone name.592if (!settings[i]->end_bone.name.is_empty()) {593set_end_bone_name(i, settings[i]->end_bone.name);594} else if (settings[i]->end_bone.bone != -1) {595set_end_bone(i, settings[i]->end_bone.bone);596}597}598}599600void BoneTwistDisperser3D::_make_all_joints_dirty() {601for (uint32_t i = 0; i < settings.size(); i++) {602_make_joints_dirty(i);603}604}605606void BoneTwistDisperser3D::_make_joints_dirty(int p_index) {607ERR_FAIL_INDEX(p_index, (int)settings.size());608if (settings[p_index]->joints_dirty) {609return;610}611settings[p_index]->joints_dirty = true;612callable_mp(this, &BoneTwistDisperser3D::_update_joints).call_deferred(p_index);613}614615void BoneTwistDisperser3D::_update_joints(int p_index) {616Skeleton3D *sk = get_skeleton();617int current_bone = settings[p_index]->end_bone.bone;618int root_bone = settings[p_index]->root_bone.bone;619if (!sk || current_bone < 0 || root_bone < 0) {620set_joint_count(p_index, 0);621settings[p_index]->joints_dirty = false;622return;623}624625// Validation.626bool valid = false;627while (current_bone >= 0) {628current_bone = sk->get_bone_parent(current_bone);629if (current_bone == root_bone) {630valid = true;631break;632}633}634635if (!valid) {636set_joint_count(p_index, 0);637_update_reference_bone(p_index);638settings[p_index]->joints_dirty = false;639ERR_FAIL_EDMSG("End bone must be a child of the root bone.");640}641642Vector<int> new_joints;643current_bone = settings[p_index]->end_bone.bone;644while (current_bone != root_bone) {645new_joints.push_back(current_bone);646current_bone = sk->get_bone_parent(current_bone);647}648new_joints.push_back(current_bone);649new_joints.reverse();650651set_joint_count(p_index, new_joints.size());652for (uint32_t i = 0; i < new_joints.size(); i++) {653set_joint_bone(p_index, i, new_joints[i]);654}655656_update_reference_bone(p_index);657settings[p_index]->joints_dirty = false;658}659660int BoneTwistDisperser3D::get_setting_count() const {661return (int)settings.size();662}663664void BoneTwistDisperser3D::set_setting_count(int p_count) {665ERR_FAIL_COND(p_count < 0);666int delta = p_count - settings.size();667if (delta < 0) {668for (int i = delta; i < 0; i++) {669memdelete(settings[settings.size() + i]);670settings[settings.size() + i] = nullptr;671}672}673settings.resize(p_count);674delta++;675if (delta > 1) {676for (int i = 1; i < delta; i++) {677settings[p_count - i] = memnew(BoneTwistDisperser3DSetting);678}679}680notify_property_list_changed();681}682683void BoneTwistDisperser3D::clear_settings() {684set_setting_count(0);685}686687void BoneTwistDisperser3D::_process_modification(double p_delta) {688Skeleton3D *skeleton = get_skeleton();689if (!skeleton) {690return;691}692for (BoneTwistDisperser3DSetting *setting : settings) {693if (!setting || setting->reference_bone.bone < 0) {694continue;695}696LocalVector<DisperseJointSetting> &joints = setting->joints;697// Calc amount.698int actual_joint_size = setting->extend_end_bone ? (int)joints.size() : (int)joints.size() - 1;699if (actual_joint_size <= 1) {700continue;701}702if (setting->disperse_mode == DISPERSE_MODE_EVEN) {703double div = 1.0 / actual_joint_size;704for (int i = 0; i < actual_joint_size; i++) {705joints[i].amount = ((double)i + 1.0) * div;706}707} else if (setting->disperse_mode == DISPERSE_MODE_WEIGHTED) {708// Assign length for each bone.709double total_length = 0.0;710double weight_sub = 1.0 - setting->weight_position;711if (mutable_bone_axes) {712for (int i = 0; i < actual_joint_size; i++) {713double length = 0.0;714if (i == 0) {715length = skeleton->get_bone_pose_position(joints[i + 1].joint.bone).length() * setting->weight_position;716} else if (i == actual_joint_size - 1) {717length = skeleton->get_bone_pose_position(joints[i].joint.bone).length() * weight_sub;718} else {719length = skeleton->get_bone_pose_position(joints[i].joint.bone).length() * setting->weight_position + skeleton->get_bone_pose_position(joints[i + 1].joint.bone).length() * weight_sub;720}721total_length += length;722joints[i].amount = total_length;723}724} else {725for (int i = 0; i < actual_joint_size; i++) {726double length = 0.0;727if (i == 0) {728length = skeleton->get_bone_rest(joints[i + 1].joint.bone).origin.length() * setting->weight_position;729} else if (i == actual_joint_size - 1) {730length = skeleton->get_bone_rest(joints[i].joint.bone).origin.length() * weight_sub;731} else {732length = skeleton->get_bone_rest(joints[i].joint.bone).origin.length() * setting->weight_position + skeleton->get_bone_rest(joints[i + 1].joint.bone).origin.length() * weight_sub;733}734total_length += length;735joints[i].amount = total_length;736}737}738if (Math::is_zero_approx(total_length)) {739continue;740}741// Normalize.742double div = 1.0 / total_length;743for (int i = 0; i < actual_joint_size; i++) {744joints[i].amount *= div;745}746} else {747for (int i = 0; i < actual_joint_size; i++) {748joints[i].amount = joints[i].custom_amount;749}750}751int end = actual_joint_size - 1;752joints[end].amount -= 1.0; // Remove twist from current pose.753754// Retrieve axes.755if (mutable_bone_axes) {756for (int i = 0; i < end; i++) {757joints[i].axis = skeleton->get_bone_pose_position(joints[i + 1].joint.bone).normalized();758if (joints[i].axis.is_zero_approx() && i > 0) {759joints[i].axis = joints[i - 1].axis;760}761}762} else {763for (int i = 0; i < end; i++) {764joints[i].axis = skeleton->get_bone_rest(joints[i + 1].joint.bone).origin.normalized();765if (joints[i].axis.is_zero_approx() && i > 0) {766joints[i].axis = joints[i - 1].axis;767}768}769}770771if (!setting->extend_end_bone) {772joints[end].axis = mutable_bone_axes ? skeleton->get_bone_pose_position(setting->end_bone.bone) : skeleton->get_bone_rest(setting->end_bone.bone).origin;773joints[end].axis.normalize();774} else if (setting->end_bone_direction == BONE_DIRECTION_FROM_PARENT) {775joints[end].axis = skeleton->get_bone_rest(setting->end_bone.bone).basis.xform_inv(mutable_bone_axes ? skeleton->get_bone_pose_position(setting->end_bone.bone) : skeleton->get_bone_rest(setting->end_bone.bone).origin);776joints[end].axis.normalize();777} else {778joints[end].axis = get_vector_from_bone_axis(static_cast<BoneAxis>((int)setting->end_bone_direction));779}780if (joints[end].axis.is_zero_approx() && end > 0) {781joints[end].axis = joints[end - 1].axis;782}783784// Extract twist.785Quaternion twist_rest = setting->twist_from_rest ? skeleton->get_bone_rest(setting->reference_bone.bone).basis.get_rotation_quaternion() : setting->twist_from.normalized();786Quaternion ref_rot = twist_rest.inverse() * skeleton->get_bone_pose_rotation(setting->reference_bone.bone);787ref_rot.normalize();788double twist = get_roll_angle(ref_rot, joints[end].axis);789790// Apply twist for each bone by their amount.791// Twist parent, then cancel all twists caused by this modifier in child, and re-apply accumulated twist.792Quaternion prev_rot;793if (mutable_bone_axes) {794for (int i = 0; i < actual_joint_size; i++) {795int bn = joints[i].joint.bone;796Quaternion cur_rot = Quaternion(joints[i].axis, twist * joints[i].amount);797skeleton->set_bone_pose_rotation(bn, prev_rot.inverse() * skeleton->get_bone_pose_rotation(bn) * cur_rot);798prev_rot = cur_rot;799}800} else {801for (int i = 0; i < actual_joint_size; i++) {802int bn = joints[i].joint.bone;803Quaternion cur_rot = Quaternion(joints[i].axis, twist * joints[i].amount);804skeleton->set_bone_pose_rotation(bn, prev_rot.inverse() * skeleton->get_bone_pose_rotation(bn) * cur_rot);805prev_rot = cur_rot;806}807}808}809}810811BoneTwistDisperser3D::~BoneTwistDisperser3D() {812clear_settings();813}814815816