Path: blob/master/scene/3d/bone_twist_disperser_3d.cpp
21117 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 == "twist_amount") {68set_joint_twist_amount(which, idx, p_value);69} else {70return false;71}72} else {73return false;74}75}76return true;77}7879bool BoneTwistDisperser3D::_get(const StringName &p_path, Variant &r_ret) const {80String path = p_path;8182if (path.begins_with("settings/")) {83int which = path.get_slicec('/', 1).to_int();84String what = path.get_slicec('/', 2);85ERR_FAIL_INDEX_V(which, (int)settings.size(), false);8687if (what == "root_bone_name") {88r_ret = get_root_bone_name(which);89} else if (what == "root_bone") {90r_ret = get_root_bone(which);91} else if (what == "end_bone_name") {92r_ret = get_end_bone_name(which);93} else if (what == "end_bone") {94r_ret = get_end_bone(which);95} else if (what == "end_bone_direction") {96r_ret = (int)get_end_bone_direction(which);97} else if (what == "reference_bone_name") {98r_ret = get_reference_bone_name(which);99} else if (what == "extend_end_bone") {100r_ret = is_end_bone_extended(which);101} else if (what == "twist_from_rest") {102r_ret = is_twist_from_rest(which);103} else if (what == "twist_from") {104r_ret = get_twist_from(which);105} else if (what == "disperse_mode") {106r_ret = (int)get_disperse_mode(which);107} else if (what == "weight_position") {108r_ret = get_weight_position(which);109} else if (what == "damping_curve") {110r_ret = get_damping_curve(which);111} else if (what == "joint_count") {112r_ret = get_joint_count(which);113} else if (what == "joints") {114int idx = path.get_slicec('/', 3).to_int();115String prop = path.get_slicec('/', 4);116if (prop == "bone_name") {117r_ret = get_joint_bone_name(which, idx);118} else if (prop == "bone") {119r_ret = get_joint_bone(which, idx);120} else if (prop == "twist_amount") {121r_ret = get_joint_twist_amount(which, idx);122} else {123return false;124}125} else {126return false;127}128}129return true;130}131132void BoneTwistDisperser3D::_get_property_list(List<PropertyInfo> *p_list) const {133String enum_hint;134Skeleton3D *skeleton = get_skeleton();135if (skeleton) {136enum_hint = skeleton->get_concatenated_bone_names();137}138139LocalVector<PropertyInfo> props;140141for (uint32_t i = 0; i < settings.size(); i++) {142String path = "settings/" + itos(i) + "/";143props.push_back(PropertyInfo(Variant::STRING, path + "root_bone_name", PROPERTY_HINT_ENUM_SUGGESTION, enum_hint));144props.push_back(PropertyInfo(Variant::INT, path + "root_bone", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));145props.push_back(PropertyInfo(Variant::STRING, path + "end_bone_name", PROPERTY_HINT_ENUM_SUGGESTION, enum_hint));146props.push_back(PropertyInfo(Variant::INT, path + "end_bone", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));147props.push_back(PropertyInfo(Variant::BOOL, path + "extend_end_bone"));148props.push_back(PropertyInfo(Variant::INT, path + "end_bone_direction", PROPERTY_HINT_ENUM, SkeletonModifier3D::get_hint_bone_direction()));149150props.push_back(PropertyInfo(Variant::STRING, path + "reference_bone_name", PROPERTY_HINT_ENUM_SUGGESTION, enum_hint, PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY));151props.push_back(PropertyInfo(Variant::BOOL, path + "twist_from_rest"));152props.push_back(PropertyInfo(Variant::QUATERNION, path + "twist_from"));153props.push_back(PropertyInfo(Variant::INT, path + "disperse_mode", PROPERTY_HINT_ENUM, "Even,Weighted,Custom"));154props.push_back(PropertyInfo(Variant::FLOAT, path + "weight_position", PROPERTY_HINT_RANGE, "0,1,0.001"));155props.push_back(PropertyInfo(Variant::OBJECT, path + "damping_curve", PROPERTY_HINT_RESOURCE_TYPE, Curve::get_class_static()));156157props.push_back(PropertyInfo(Variant::INT, path + "joint_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ARRAY, "Joints," + path + "joints/,static,const"));158for (uint32_t j = 0; j < settings[i]->joints.size(); j++) {159String joint_path = path + "joints/" + itos(j) + "/";160props.push_back(PropertyInfo(Variant::STRING, joint_path + "bone_name", PROPERTY_HINT_ENUM_SUGGESTION, enum_hint, PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY));161props.push_back(PropertyInfo(Variant::INT, joint_path + "bone", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_READ_ONLY));162props.push_back(PropertyInfo(Variant::FLOAT, joint_path + "twist_amount", PROPERTY_HINT_RANGE, "0,1,0.001,or_greater,or_less"));163}164}165166for (PropertyInfo &p : props) {167_validate_dynamic_prop(p);168p_list->push_back(p);169}170}171172void BoneTwistDisperser3D::_validate_dynamic_prop(PropertyInfo &p_property) const {173PackedStringArray split = p_property.name.split("/");174if (split.size() > 2 && split[0] == "settings") {175int which = split[1].to_int();176177// Extended end bone option.178bool force_hide = false;179if (split[2] == "extend_end_bone" && get_end_bone(which) == -1) {180p_property.usage = PROPERTY_USAGE_NONE;181force_hide = true;182}183if (force_hide || (split[2] == "end_bone_direction" && !is_end_bone_extended(which))) {184p_property.usage = PROPERTY_USAGE_NONE;185}186187if (split[2] == "twist_from" && is_twist_from_rest(which)) {188p_property.usage = PROPERTY_USAGE_NONE;189}190191if (split[2] == "weight_position" && get_disperse_mode(which) != DISPERSE_MODE_WEIGHTED) {192p_property.usage = PROPERTY_USAGE_NONE;193}194195if (split[2] == "damping_curve" && get_disperse_mode(which) != DISPERSE_MODE_CUSTOM) {196p_property.usage = PROPERTY_USAGE_NONE;197}198199if (split[2] == "joints" && split[4] == "twist_amount") {200bool mutable_amount = true;201if (get_disperse_mode(which) != DISPERSE_MODE_CUSTOM) {202mutable_amount = false;203} else if (!is_end_bone_extended(which)) {204int joint = split[3].to_int();205mutable_amount = joint < get_joint_count(which) - 1; // Hide child of reference bone.206}207if (get_damping_curve(which).is_valid()) {208p_property.usage |= PROPERTY_USAGE_READ_ONLY;209}210if (!mutable_amount) {211p_property.usage = PROPERTY_USAGE_NONE;212}213}214}215}216217void BoneTwistDisperser3D::_notification(int p_what) {218switch (p_what) {219case NOTIFICATION_ENTER_TREE: {220_make_all_joints_dirty();221} break;222}223}224225void BoneTwistDisperser3D::_set_active(bool p_active) {226if (p_active) {227_make_all_joints_dirty();228}229}230231void BoneTwistDisperser3D::_skeleton_changed(Skeleton3D *p_old, Skeleton3D *p_new) {232_make_all_joints_dirty();233}234235// Setting.236237void BoneTwistDisperser3D::set_mutable_bone_axes(bool p_enabled) {238mutable_bone_axes = p_enabled;239}240241bool BoneTwistDisperser3D::are_bone_axes_mutable() const {242return mutable_bone_axes;243}244245void BoneTwistDisperser3D::set_root_bone_name(int p_index, const String &p_bone_name) {246ERR_FAIL_INDEX(p_index, (int)settings.size());247settings[p_index]->root_bone.name = p_bone_name;248Skeleton3D *sk = get_skeleton();249if (sk) {250set_root_bone(p_index, sk->find_bone(settings[p_index]->root_bone.name));251}252}253254String BoneTwistDisperser3D::get_root_bone_name(int p_index) const {255ERR_FAIL_INDEX_V(p_index, (int)settings.size(), String());256return settings[p_index]->root_bone.name;257}258259void BoneTwistDisperser3D::set_root_bone(int p_index, int p_bone) {260ERR_FAIL_INDEX(p_index, (int)settings.size());261bool changed = settings[p_index]->root_bone.bone != p_bone;262settings[p_index]->root_bone.bone = p_bone;263Skeleton3D *sk = get_skeleton();264if (sk) {265if (settings[p_index]->root_bone.bone <= -1 || settings[p_index]->root_bone.bone >= sk->get_bone_count()) {266WARN_PRINT("Root bone index out of range!");267settings[p_index]->root_bone.bone = -1;268} else {269settings[p_index]->root_bone.name = sk->get_bone_name(settings[p_index]->root_bone.bone);270}271}272if (changed) {273_make_joints_dirty(p_index);274}275}276277int BoneTwistDisperser3D::get_root_bone(int p_index) const {278ERR_FAIL_INDEX_V(p_index, (int)settings.size(), -1);279return settings[p_index]->root_bone.bone;280}281282void BoneTwistDisperser3D::set_end_bone_name(int p_index, const String &p_bone_name) {283ERR_FAIL_INDEX(p_index, (int)settings.size());284settings[p_index]->end_bone.name = p_bone_name;285Skeleton3D *sk = get_skeleton();286if (sk) {287set_end_bone(p_index, sk->find_bone(settings[p_index]->end_bone.name));288}289}290291String BoneTwistDisperser3D::get_end_bone_name(int p_index) const {292ERR_FAIL_INDEX_V(p_index, (int)settings.size(), String());293return settings[p_index]->end_bone.name;294}295296void BoneTwistDisperser3D::set_end_bone(int p_index, int p_bone) {297ERR_FAIL_INDEX(p_index, (int)settings.size());298bool changed = settings[p_index]->end_bone.bone != p_bone;299settings[p_index]->end_bone.bone = p_bone;300Skeleton3D *sk = get_skeleton();301if (sk) {302if (settings[p_index]->end_bone.bone <= -1 || settings[p_index]->end_bone.bone >= sk->get_bone_count()) {303WARN_PRINT("End bone index out of range!");304settings[p_index]->end_bone.bone = -1;305} else {306settings[p_index]->end_bone.name = sk->get_bone_name(settings[p_index]->end_bone.bone);307}308}309if (changed) {310_make_joints_dirty(p_index);311}312notify_property_list_changed();313}314315int BoneTwistDisperser3D::get_end_bone(int p_index) const {316ERR_FAIL_INDEX_V(p_index, (int)settings.size(), -1);317return settings[p_index]->end_bone.bone;318}319320void BoneTwistDisperser3D::set_extend_end_bone(int p_index, bool p_enabled) {321ERR_FAIL_INDEX(p_index, (int)settings.size());322settings[p_index]->extend_end_bone = p_enabled;323_update_reference_bone(p_index);324notify_property_list_changed();325}326327bool BoneTwistDisperser3D::is_end_bone_extended(int p_index) const {328ERR_FAIL_INDEX_V(p_index, (int)settings.size(), false);329return settings[p_index]->extend_end_bone;330}331332void BoneTwistDisperser3D::set_end_bone_direction(int p_index, BoneDirection p_bone_direction) {333ERR_FAIL_INDEX(p_index, (int)settings.size());334settings[p_index]->end_bone_direction = p_bone_direction;335}336337SkeletonModifier3D::BoneDirection BoneTwistDisperser3D::get_end_bone_direction(int p_index) const {338ERR_FAIL_INDEX_V(p_index, (int)settings.size(), BONE_DIRECTION_FROM_PARENT);339return settings[p_index]->end_bone_direction;340}341342void BoneTwistDisperser3D::set_twist_from_rest(int p_index, bool p_enabled) {343ERR_FAIL_INDEX(p_index, (int)settings.size());344settings[p_index]->twist_from_rest = p_enabled;345notify_property_list_changed();346}347348bool BoneTwistDisperser3D::is_twist_from_rest(int p_index) const {349ERR_FAIL_INDEX_V(p_index, (int)settings.size(), true);350return settings[p_index]->twist_from_rest;351}352353void BoneTwistDisperser3D::set_twist_from(int p_index, const Quaternion &p_from) {354ERR_FAIL_INDEX(p_index, (int)settings.size());355settings[p_index]->twist_from = p_from;356}357358Quaternion BoneTwistDisperser3D::get_twist_from(int p_index) const {359ERR_FAIL_INDEX_V(p_index, (int)settings.size(), Quaternion());360return settings[p_index]->twist_from;361}362363void BoneTwistDisperser3D::_update_reference_bone(int p_index) {364ERR_FAIL_INDEX(p_index, (int)settings.size());365LocalVector<DisperseJointSetting> &joints = settings[p_index]->joints;366if (joints.size() >= 2) {367if (settings[p_index]->extend_end_bone) {368settings[p_index]->reference_bone = settings[p_index]->end_bone;369_update_curve(p_index);370return;371} else {372Skeleton3D *sk = get_skeleton();373if (sk) {374int parent = sk->get_bone_parent(settings[p_index]->end_bone.bone);375if (parent >= 0) {376settings[p_index]->reference_bone.bone = parent;377settings[p_index]->reference_bone.name = sk->get_bone_name(parent);378_update_curve(p_index);379return;380}381}382}383}384settings[p_index]->reference_bone.bone = -1;385settings[p_index]->reference_bone.name = String();386}387388void BoneTwistDisperser3D::_update_curve(int p_index) {389Ref<Curve> curve = settings[p_index]->damping_curve;390if (curve.is_null()) {391return;392}393LocalVector<DisperseJointSetting> &joints = settings[p_index]->joints;394float unit = (int)joints.size() > 0 ? (1.0 / float((int)joints.size() - 1)) : 0.0;395for (uint32_t i = 0; i < joints.size(); i++) {396joints[i].custom_amount = curve->sample_baked(i * unit);397}398}399400String BoneTwistDisperser3D::get_reference_bone_name(int p_index) const {401ERR_FAIL_INDEX_V(p_index, (int)settings.size(), String());402return settings[p_index]->reference_bone.name;403}404405int BoneTwistDisperser3D::get_reference_bone(int p_index) const {406ERR_FAIL_INDEX_V(p_index, (int)settings.size(), -1);407return settings[p_index]->reference_bone.bone;408}409410void BoneTwistDisperser3D::set_disperse_mode(int p_index, DisperseMode p_disperse_mode) {411ERR_FAIL_INDEX(p_index, (int)settings.size());412settings[p_index]->disperse_mode = p_disperse_mode;413notify_property_list_changed();414}415416BoneTwistDisperser3D::DisperseMode BoneTwistDisperser3D::get_disperse_mode(int p_index) const {417ERR_FAIL_INDEX_V(p_index, (int)settings.size(), DISPERSE_MODE_EVEN);418return settings[p_index]->disperse_mode;419}420421void BoneTwistDisperser3D::set_weight_position(int p_index, float p_position) {422ERR_FAIL_INDEX(p_index, (int)settings.size());423settings[p_index]->weight_position = p_position;424}425426float BoneTwistDisperser3D::get_weight_position(int p_index) const {427ERR_FAIL_INDEX_V(p_index, (int)settings.size(), 0.0);428return settings[p_index]->weight_position;429}430431void BoneTwistDisperser3D::set_damping_curve(int p_index, const Ref<Curve> &p_damping_curve) {432ERR_FAIL_INDEX(p_index, (int)settings.size());433bool changed = settings[p_index]->damping_curve != p_damping_curve;434if (settings[p_index]->damping_curve.is_valid()) {435settings[p_index]->damping_curve->disconnect_changed(callable_mp(this, &BoneTwistDisperser3D::_update_curve));436}437settings[p_index]->damping_curve = p_damping_curve;438if (settings[p_index]->damping_curve.is_valid()) {439settings[p_index]->damping_curve->connect_changed(callable_mp(this, &BoneTwistDisperser3D::_update_curve).bind(p_index));440}441if (changed) {442_make_joints_dirty(p_index);443}444notify_property_list_changed();445}446447Ref<Curve> BoneTwistDisperser3D::get_damping_curve(int p_index) const {448ERR_FAIL_INDEX_V(p_index, (int)settings.size(), Ref<Curve>());449return settings[p_index]->damping_curve;450}451452// Individual joints.453454String BoneTwistDisperser3D::get_joint_bone_name(int p_index, int p_joint) const {455ERR_FAIL_INDEX_V(p_index, (int)settings.size(), String());456const LocalVector<DisperseJointSetting> &joints = settings[p_index]->joints;457ERR_FAIL_INDEX_V(p_joint, (int)joints.size(), String());458return joints[p_joint].joint.name;459}460461void BoneTwistDisperser3D::_set_joint_bone(int p_index, int p_joint, int p_bone) {462ERR_FAIL_INDEX(p_index, (int)settings.size());463LocalVector<DisperseJointSetting> &joints = settings[p_index]->joints;464ERR_FAIL_INDEX(p_joint, (int)joints.size());465joints[p_joint].joint.bone = p_bone;466Skeleton3D *sk = get_skeleton();467if (sk) {468if (joints[p_joint].joint.bone <= -1 || joints[p_joint].joint.bone >= sk->get_bone_count()) {469WARN_PRINT("Joint bone index out of range!");470joints[p_joint].joint.bone = -1;471} else {472joints[p_joint].joint.name = sk->get_bone_name(joints[p_joint].joint.bone);473}474}475}476477int BoneTwistDisperser3D::get_joint_bone(int p_index, int p_joint) const {478ERR_FAIL_INDEX_V(p_index, (int)settings.size(), -1);479const LocalVector<DisperseJointSetting> &joints = settings[p_index]->joints;480ERR_FAIL_INDEX_V(p_joint, (int)joints.size(), -1);481return joints[p_joint].joint.bone;482}483484void BoneTwistDisperser3D::set_joint_count(int p_index, int p_count) {485ERR_FAIL_INDEX(p_index, (int)settings.size());486ERR_FAIL_COND(p_count < 0);487LocalVector<DisperseJointSetting> &joints = settings[p_index]->joints;488joints.resize(p_count);489notify_property_list_changed();490}491492int BoneTwistDisperser3D::get_joint_count(int p_index) const {493ERR_FAIL_INDEX_V(p_index, (int)settings.size(), 0);494const LocalVector<DisperseJointSetting> &joints = settings[p_index]->joints;495return joints.size();496}497498void BoneTwistDisperser3D::set_joint_twist_amount(int p_index, int p_joint, float p_amount) {499ERR_FAIL_INDEX(p_index, (int)settings.size());500LocalVector<DisperseJointSetting> &joints = settings[p_index]->joints;501ERR_FAIL_INDEX(p_joint, (int)joints.size());502joints[p_joint].custom_amount = p_amount;503}504505float BoneTwistDisperser3D::get_joint_twist_amount(int p_index, int p_joint) const {506ERR_FAIL_INDEX_V(p_index, (int)settings.size(), 0);507const LocalVector<DisperseJointSetting> &joints = settings[p_index]->joints;508ERR_FAIL_INDEX_V(p_joint, (int)joints.size(), 0);509return joints[p_joint].custom_amount;510}511512void BoneTwistDisperser3D::_bind_methods() {513ClassDB::bind_method(D_METHOD("set_setting_count", "count"), &BoneTwistDisperser3D::set_setting_count);514ClassDB::bind_method(D_METHOD("get_setting_count"), &BoneTwistDisperser3D::get_setting_count);515ClassDB::bind_method(D_METHOD("clear_settings"), &BoneTwistDisperser3D::clear_settings);516517ClassDB::bind_method(D_METHOD("set_mutable_bone_axes", "enabled"), &BoneTwistDisperser3D::set_mutable_bone_axes);518ClassDB::bind_method(D_METHOD("are_bone_axes_mutable"), &BoneTwistDisperser3D::are_bone_axes_mutable);519520// Setting.521ClassDB::bind_method(D_METHOD("set_root_bone_name", "index", "bone_name"), &BoneTwistDisperser3D::set_root_bone_name);522ClassDB::bind_method(D_METHOD("get_root_bone_name", "index"), &BoneTwistDisperser3D::get_root_bone_name);523ClassDB::bind_method(D_METHOD("set_root_bone", "index", "bone"), &BoneTwistDisperser3D::set_root_bone);524ClassDB::bind_method(D_METHOD("get_root_bone", "index"), &BoneTwistDisperser3D::get_root_bone);525526ClassDB::bind_method(D_METHOD("set_end_bone_name", "index", "bone_name"), &BoneTwistDisperser3D::set_end_bone_name);527ClassDB::bind_method(D_METHOD("get_end_bone_name", "index"), &BoneTwistDisperser3D::get_end_bone_name);528ClassDB::bind_method(D_METHOD("set_end_bone", "index", "bone"), &BoneTwistDisperser3D::set_end_bone);529ClassDB::bind_method(D_METHOD("get_end_bone", "index"), &BoneTwistDisperser3D::get_end_bone);530531ClassDB::bind_method(D_METHOD("get_reference_bone_name", "index"), &BoneTwistDisperser3D::get_reference_bone_name);532ClassDB::bind_method(D_METHOD("get_reference_bone", "index"), &BoneTwistDisperser3D::get_reference_bone);533534ClassDB::bind_method(D_METHOD("set_extend_end_bone", "index", "enabled"), &BoneTwistDisperser3D::set_extend_end_bone);535ClassDB::bind_method(D_METHOD("is_end_bone_extended", "index"), &BoneTwistDisperser3D::is_end_bone_extended);536ClassDB::bind_method(D_METHOD("set_end_bone_direction", "index", "bone_direction"), &BoneTwistDisperser3D::set_end_bone_direction);537ClassDB::bind_method(D_METHOD("get_end_bone_direction", "index"), &BoneTwistDisperser3D::get_end_bone_direction);538539ClassDB::bind_method(D_METHOD("set_twist_from_rest", "index", "enabled"), &BoneTwistDisperser3D::set_twist_from_rest);540ClassDB::bind_method(D_METHOD("is_twist_from_rest", "index"), &BoneTwistDisperser3D::is_twist_from_rest);541ClassDB::bind_method(D_METHOD("set_twist_from", "index", "from"), &BoneTwistDisperser3D::set_twist_from);542ClassDB::bind_method(D_METHOD("get_twist_from", "index"), &BoneTwistDisperser3D::get_twist_from);543544ClassDB::bind_method(D_METHOD("set_disperse_mode", "index", "disperse_mode"), &BoneTwistDisperser3D::set_disperse_mode);545ClassDB::bind_method(D_METHOD("get_disperse_mode", "index"), &BoneTwistDisperser3D::get_disperse_mode);546ClassDB::bind_method(D_METHOD("set_weight_position", "index", "weight_position"), &BoneTwistDisperser3D::set_weight_position);547ClassDB::bind_method(D_METHOD("get_weight_position", "index"), &BoneTwistDisperser3D::get_weight_position);548ClassDB::bind_method(D_METHOD("set_damping_curve", "index", "curve"), &BoneTwistDisperser3D::set_damping_curve);549ClassDB::bind_method(D_METHOD("get_damping_curve", "index"), &BoneTwistDisperser3D::get_damping_curve);550551// Individual joints.552ClassDB::bind_method(D_METHOD("get_joint_bone_name", "index", "joint"), &BoneTwistDisperser3D::get_joint_bone_name);553ClassDB::bind_method(D_METHOD("get_joint_bone", "index", "joint"), &BoneTwistDisperser3D::get_joint_bone);554ClassDB::bind_method(D_METHOD("get_joint_twist_amount", "index", "joint"), &BoneTwistDisperser3D::get_joint_twist_amount);555ClassDB::bind_method(D_METHOD("set_joint_twist_amount", "index", "joint", "twist_amount"), &BoneTwistDisperser3D::set_joint_twist_amount);556557ClassDB::bind_method(D_METHOD("get_joint_count", "index"), &BoneTwistDisperser3D::get_joint_count);558559ADD_PROPERTY(PropertyInfo(Variant::BOOL, "mutable_bone_axes"), "set_mutable_bone_axes", "are_bone_axes_mutable");560ADD_ARRAY_COUNT("Settings", "setting_count", "set_setting_count", "get_setting_count", "settings/");561562BIND_ENUM_CONSTANT(DISPERSE_MODE_EVEN);563BIND_ENUM_CONSTANT(DISPERSE_MODE_WEIGHTED);564BIND_ENUM_CONSTANT(DISPERSE_MODE_CUSTOM);565}566567void BoneTwistDisperser3D::_validate_bone_names() {568for (uint32_t i = 0; i < settings.size(); i++) {569// Prior bone name.570if (!settings[i]->root_bone.name.is_empty()) {571set_root_bone_name(i, settings[i]->root_bone.name);572} else if (settings[i]->root_bone.bone != -1) {573set_root_bone(i, settings[i]->root_bone.bone);574}575// Prior bone name.576if (!settings[i]->end_bone.name.is_empty()) {577set_end_bone_name(i, settings[i]->end_bone.name);578} else if (settings[i]->end_bone.bone != -1) {579set_end_bone(i, settings[i]->end_bone.bone);580}581}582}583584void BoneTwistDisperser3D::_make_all_joints_dirty() {585for (uint32_t i = 0; i < settings.size(); i++) {586_make_joints_dirty(i);587}588}589590void BoneTwistDisperser3D::_make_joints_dirty(int p_index) {591ERR_FAIL_INDEX(p_index, (int)settings.size());592if (settings[p_index]->joints_dirty) {593return;594}595settings[p_index]->joints_dirty = true;596callable_mp(this, &BoneTwistDisperser3D::_update_joints).call_deferred(p_index);597}598599void BoneTwistDisperser3D::_update_joints(int p_index) {600Skeleton3D *sk = get_skeleton();601int current_bone = settings[p_index]->end_bone.bone;602int root_bone = settings[p_index]->root_bone.bone;603if (!sk || current_bone < 0 || root_bone < 0) {604set_joint_count(p_index, 0);605settings[p_index]->joints_dirty = false;606return;607}608609// Validation.610bool valid = false;611while (current_bone >= 0) {612current_bone = sk->get_bone_parent(current_bone);613if (current_bone == root_bone) {614valid = true;615break;616}617}618619if (!valid) {620set_joint_count(p_index, 0);621_update_reference_bone(p_index);622settings[p_index]->joints_dirty = false;623ERR_FAIL_EDMSG("End bone must be a child of the root bone.");624}625626Vector<int> new_joints;627current_bone = settings[p_index]->end_bone.bone;628while (current_bone != root_bone) {629new_joints.push_back(current_bone);630current_bone = sk->get_bone_parent(current_bone);631}632new_joints.push_back(current_bone);633new_joints.reverse();634635set_joint_count(p_index, new_joints.size());636for (uint32_t i = 0; i < new_joints.size(); i++) {637_set_joint_bone(p_index, i, new_joints[i]);638}639640_update_reference_bone(p_index);641settings[p_index]->joints_dirty = false;642}643644int BoneTwistDisperser3D::get_setting_count() const {645return (int)settings.size();646}647648void BoneTwistDisperser3D::set_setting_count(int p_count) {649ERR_FAIL_COND(p_count < 0);650int delta = p_count - settings.size();651if (delta < 0) {652for (int i = delta; i < 0; i++) {653memdelete(settings[settings.size() + i]);654settings[settings.size() + i] = nullptr;655}656}657settings.resize(p_count);658delta++;659if (delta > 1) {660for (int i = 1; i < delta; i++) {661settings[p_count - i] = memnew(BoneTwistDisperser3DSetting);662}663}664notify_property_list_changed();665}666667void BoneTwistDisperser3D::clear_settings() {668set_setting_count(0);669}670671void BoneTwistDisperser3D::_process_modification(double p_delta) {672Skeleton3D *skeleton = get_skeleton();673if (!skeleton) {674return;675}676for (BoneTwistDisperser3DSetting *setting : settings) {677if (!setting || setting->reference_bone.bone < 0) {678continue;679}680LocalVector<DisperseJointSetting> &joints = setting->joints;681// Calc amount.682int actual_joint_size = setting->extend_end_bone ? (int)joints.size() : (int)joints.size() - 1;683if (actual_joint_size <= 1) {684continue;685}686if (setting->disperse_mode == DISPERSE_MODE_EVEN) {687double div = 1.0 / actual_joint_size;688for (int i = 0; i < actual_joint_size; i++) {689joints[i].amount = ((double)i + 1.0) * div;690}691} else if (setting->disperse_mode == DISPERSE_MODE_WEIGHTED) {692// Assign length for each bone.693double total_length = 0.0;694double weight_sub = 1.0 - setting->weight_position;695if (mutable_bone_axes) {696for (int i = 0; i < actual_joint_size; i++) {697double length = 0.0;698if (i == 0) {699length = skeleton->get_bone_pose_position(joints[i + 1].joint.bone).length() * setting->weight_position;700} else if (i == actual_joint_size - 1) {701length = skeleton->get_bone_pose_position(joints[i].joint.bone).length() * weight_sub;702} else {703length = 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;704}705total_length += length;706joints[i].amount = total_length;707}708} else {709for (int i = 0; i < actual_joint_size; i++) {710double length = 0.0;711if (i == 0) {712length = skeleton->get_bone_rest(joints[i + 1].joint.bone).origin.length() * setting->weight_position;713} else if (i == actual_joint_size - 1) {714length = skeleton->get_bone_rest(joints[i].joint.bone).origin.length() * weight_sub;715} else {716length = 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;717}718total_length += length;719joints[i].amount = total_length;720}721}722if (Math::is_zero_approx(total_length)) {723continue;724}725// Normalize.726double div = 1.0 / total_length;727for (int i = 0; i < actual_joint_size; i++) {728joints[i].amount *= div;729}730} else {731for (int i = 0; i < actual_joint_size; i++) {732joints[i].amount = joints[i].custom_amount;733}734}735int end = actual_joint_size - 1;736joints[end].amount -= 1.0; // Remove twist from current pose.737738// Retrieve axes.739if (mutable_bone_axes) {740for (int i = 0; i < end; i++) {741joints[i].axis = skeleton->get_bone_pose_position(joints[i + 1].joint.bone).normalized();742if (joints[i].axis.is_zero_approx() && i > 0) {743joints[i].axis = joints[i - 1].axis;744}745}746} else {747for (int i = 0; i < end; i++) {748joints[i].axis = skeleton->get_bone_rest(joints[i + 1].joint.bone).origin.normalized();749if (joints[i].axis.is_zero_approx() && i > 0) {750joints[i].axis = joints[i - 1].axis;751}752}753}754755if (!setting->extend_end_bone) {756joints[end].axis = mutable_bone_axes ? skeleton->get_bone_pose_position(setting->end_bone.bone) : skeleton->get_bone_rest(setting->end_bone.bone).origin;757joints[end].axis.normalize();758} else if (setting->end_bone_direction == BONE_DIRECTION_FROM_PARENT) {759joints[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);760joints[end].axis.normalize();761} else {762joints[end].axis = get_vector_from_bone_axis(static_cast<BoneAxis>((int)setting->end_bone_direction));763}764if (joints[end].axis.is_zero_approx() && end > 0) {765joints[end].axis = joints[end - 1].axis;766}767768// Extract twist.769Quaternion twist_rest = setting->twist_from_rest ? skeleton->get_bone_rest(setting->reference_bone.bone).basis.get_rotation_quaternion() : setting->twist_from.normalized();770Quaternion ref_rot = twist_rest.inverse() * skeleton->get_bone_pose_rotation(setting->reference_bone.bone);771ref_rot.normalize();772double twist = get_roll_angle(ref_rot, joints[end].axis);773774// Apply twist for each bone by their amount.775// Twist parent, then cancel all twists caused by this modifier in child, and re-apply accumulated twist.776Quaternion prev_rot;777if (mutable_bone_axes) {778for (int i = 0; i < actual_joint_size; i++) {779int bn = joints[i].joint.bone;780Quaternion cur_rot = Quaternion(joints[i].axis, twist * joints[i].amount);781skeleton->set_bone_pose_rotation(bn, prev_rot.inverse() * skeleton->get_bone_pose_rotation(bn) * cur_rot);782prev_rot = cur_rot;783}784} else {785for (int i = 0; i < actual_joint_size; i++) {786int bn = joints[i].joint.bone;787Quaternion cur_rot = Quaternion(joints[i].axis, twist * joints[i].amount);788skeleton->set_bone_pose_rotation(bn, prev_rot.inverse() * skeleton->get_bone_pose_rotation(bn) * cur_rot);789prev_rot = cur_rot;790}791}792}793}794795BoneTwistDisperser3D::~BoneTwistDisperser3D() {796clear_settings();797}798799800