Path: blob/master/scene/3d/convert_transform_modifier_3d.cpp
20987 views
/**************************************************************************/1/* convert_transform_modifier_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 "convert_transform_modifier_3d.h"3132constexpr const char *HINT_POSITION = "-10,10,0.01,or_greater,or_less,suffix:m";33constexpr const char *HINT_ROTATION = "-180,180,0.01,radians_as_degrees";34constexpr const char *HINT_SCALE = "0,10,0.01,or_greater";3536bool ConvertTransformModifier3D::_set(const StringName &p_path, const Variant &p_value) {37String path = p_path;3839if (path.begins_with("settings/")) {40int which = path.get_slicec('/', 1).to_int();41String where = path.get_slicec('/', 2);42ERR_FAIL_INDEX_V(which, (int)settings.size(), false);43String what = path.get_slicec('/', 3);4445if (where == "apply") {46if (what == "transform_mode") {47set_apply_transform_mode(which, static_cast<TransformMode>((int)p_value));48} else if (what == "axis") {49set_apply_axis(which, static_cast<Vector3::Axis>((int)p_value));50} else if (what == "range_min") {51set_apply_range_min(which, p_value);52} else if (what == "range_max") {53set_apply_range_max(which, p_value);54} else {55return false;56}57} else if (where == "reference") {58if (what == "transform_mode") {59set_reference_transform_mode(which, static_cast<TransformMode>((int)p_value));60} else if (what == "axis") {61set_reference_axis(which, static_cast<Vector3::Axis>((int)p_value));62} else if (what == "range_min") {63set_reference_range_min(which, p_value);64} else if (what == "range_max") {65set_reference_range_max(which, p_value);66} else {67return false;68}69} else if (where == "relative") {70set_relative(which, p_value);71} else if (where == "additive") {72set_additive(which, p_value);73} else {74return false;75}76}77return true;78}7980bool ConvertTransformModifier3D::_get(const StringName &p_path, Variant &r_ret) const {81String path = p_path;8283if (path.begins_with("settings/")) {84int which = path.get_slicec('/', 1).to_int();85String where = path.get_slicec('/', 2);86ERR_FAIL_INDEX_V(which, (int)settings.size(), false);87String what = path.get_slicec('/', 3);8889if (where == "apply") {90if (what == "transform_mode") {91r_ret = (int)get_apply_transform_mode(which);92} else if (what == "axis") {93r_ret = (int)get_apply_axis(which);94} else if (what == "range_min") {95r_ret = get_apply_range_min(which);96} else if (what == "range_max") {97r_ret = get_apply_range_max(which);98} else {99return false;100}101} else if (where == "reference") {102if (what == "transform_mode") {103r_ret = (int)get_reference_transform_mode(which);104} else if (what == "axis") {105r_ret = (int)get_reference_axis(which);106} else if (what == "range_min") {107r_ret = get_reference_range_min(which);108} else if (what == "range_max") {109r_ret = get_reference_range_max(which);110} else {111return false;112}113} else if (where == "relative") {114r_ret = is_relative(which);115} else if (where == "additive") {116r_ret = is_additive(which);117} else {118return false;119}120}121return true;122}123124void ConvertTransformModifier3D::_get_property_list(List<PropertyInfo> *p_list) const {125BoneConstraint3D::get_property_list(p_list);126127LocalVector<PropertyInfo> props;128129for (uint32_t i = 0; i < settings.size(); i++) {130String path = "settings/" + itos(i) + "/";131132String hint_apply_range;133if (get_apply_transform_mode(i) == TRANSFORM_MODE_POSITION) {134hint_apply_range = HINT_POSITION;135} else if (get_apply_transform_mode(i) == TRANSFORM_MODE_ROTATION) {136hint_apply_range = HINT_ROTATION;137} else {138hint_apply_range = HINT_SCALE;139}140props.push_back(PropertyInfo(Variant::INT, path + "apply/transform_mode", PROPERTY_HINT_ENUM, "Position,Rotation,Scale"));141props.push_back(PropertyInfo(Variant::INT, path + "apply/axis", PROPERTY_HINT_ENUM, "X,Y,Z"));142props.push_back(PropertyInfo(Variant::FLOAT, path + "apply/range_min", PROPERTY_HINT_RANGE, hint_apply_range));143props.push_back(PropertyInfo(Variant::FLOAT, path + "apply/range_max", PROPERTY_HINT_RANGE, hint_apply_range));144145String hint_reference_range;146if (get_reference_transform_mode(i) == TRANSFORM_MODE_POSITION) {147hint_reference_range = HINT_POSITION;148} else if (get_reference_transform_mode(i) == TRANSFORM_MODE_ROTATION) {149hint_reference_range = HINT_ROTATION;150} else {151hint_reference_range = HINT_SCALE;152}153props.push_back(PropertyInfo(Variant::INT, path + "reference/transform_mode", PROPERTY_HINT_ENUM, "Position,Rotation,Scale"));154props.push_back(PropertyInfo(Variant::INT, path + "reference/axis", PROPERTY_HINT_ENUM, "X,Y,Z"));155props.push_back(PropertyInfo(Variant::FLOAT, path + "reference/range_min", PROPERTY_HINT_RANGE, hint_reference_range));156props.push_back(PropertyInfo(Variant::FLOAT, path + "reference/range_max", PROPERTY_HINT_RANGE, hint_reference_range));157158props.push_back(PropertyInfo(Variant::BOOL, path + "relative"));159props.push_back(PropertyInfo(Variant::BOOL, path + "additive"));160}161162for (PropertyInfo &p : props) {163_validate_dynamic_prop(p);164p_list->push_back(p);165}166}167168void ConvertTransformModifier3D::_validate_dynamic_prop(PropertyInfo &p_property) const {169PackedStringArray split = p_property.name.split("/");170if (split.size() > 2 && split[0] == "settings") {171int which = split[1].to_int();172if (split[2].begins_with("relative") && get_reference_type(which) != REFERENCE_TYPE_BONE) {173p_property.usage = PROPERTY_USAGE_NONE;174}175}176}177178void ConvertTransformModifier3D::_validate_setting(int p_index) {179settings[p_index] = memnew(ConvertTransform3DSetting);180}181182void ConvertTransformModifier3D::set_apply_transform_mode(int p_index, TransformMode p_transform_mode) {183ERR_FAIL_INDEX(p_index, (int)settings.size());184ConvertTransform3DSetting *setting = static_cast<ConvertTransform3DSetting *>(settings[p_index]);185setting->apply_transform_mode = p_transform_mode;186notify_property_list_changed();187}188189ConvertTransformModifier3D::TransformMode ConvertTransformModifier3D::get_apply_transform_mode(int p_index) const {190ERR_FAIL_INDEX_V(p_index, (int)settings.size(), TRANSFORM_MODE_POSITION);191ConvertTransform3DSetting *setting = static_cast<ConvertTransform3DSetting *>(settings[p_index]);192return setting->apply_transform_mode;193}194195void ConvertTransformModifier3D::set_apply_axis(int p_index, Vector3::Axis p_axis) {196ERR_FAIL_INDEX(p_index, (int)settings.size());197ConvertTransform3DSetting *setting = static_cast<ConvertTransform3DSetting *>(settings[p_index]);198setting->apply_axis = p_axis;199}200201Vector3::Axis ConvertTransformModifier3D::get_apply_axis(int p_index) const {202ERR_FAIL_INDEX_V(p_index, (int)settings.size(), Vector3::AXIS_X);203ConvertTransform3DSetting *setting = static_cast<ConvertTransform3DSetting *>(settings[p_index]);204return setting->apply_axis;205}206207void ConvertTransformModifier3D::set_apply_range_min(int p_index, float p_range_min) {208ERR_FAIL_INDEX(p_index, (int)settings.size());209ConvertTransform3DSetting *setting = static_cast<ConvertTransform3DSetting *>(settings[p_index]);210setting->apply_range_min = p_range_min;211}212213float ConvertTransformModifier3D::get_apply_range_min(int p_index) const {214ERR_FAIL_INDEX_V(p_index, (int)settings.size(), 0);215ConvertTransform3DSetting *setting = static_cast<ConvertTransform3DSetting *>(settings[p_index]);216return setting->apply_range_min;217}218219void ConvertTransformModifier3D::set_apply_range_max(int p_index, float p_range_max) {220ERR_FAIL_INDEX(p_index, (int)settings.size());221ConvertTransform3DSetting *setting = static_cast<ConvertTransform3DSetting *>(settings[p_index]);222setting->apply_range_max = p_range_max;223}224225float ConvertTransformModifier3D::get_apply_range_max(int p_index) const {226ERR_FAIL_INDEX_V(p_index, (int)settings.size(), 0);227ConvertTransform3DSetting *setting = static_cast<ConvertTransform3DSetting *>(settings[p_index]);228return setting->apply_range_max;229}230231void ConvertTransformModifier3D::set_reference_transform_mode(int p_index, TransformMode p_transform_mode) {232ERR_FAIL_INDEX(p_index, (int)settings.size());233ConvertTransform3DSetting *setting = static_cast<ConvertTransform3DSetting *>(settings[p_index]);234setting->reference_transform_mode = p_transform_mode;235notify_property_list_changed();236}237238ConvertTransformModifier3D::TransformMode ConvertTransformModifier3D::get_reference_transform_mode(int p_index) const {239ERR_FAIL_INDEX_V(p_index, (int)settings.size(), TRANSFORM_MODE_POSITION);240ConvertTransform3DSetting *setting = static_cast<ConvertTransform3DSetting *>(settings[p_index]);241return setting->reference_transform_mode;242}243244void ConvertTransformModifier3D::set_reference_axis(int p_index, Vector3::Axis p_axis) {245ERR_FAIL_INDEX(p_index, (int)settings.size());246ConvertTransform3DSetting *setting = static_cast<ConvertTransform3DSetting *>(settings[p_index]);247setting->reference_axis = p_axis;248}249250Vector3::Axis ConvertTransformModifier3D::get_reference_axis(int p_index) const {251ERR_FAIL_INDEX_V(p_index, (int)settings.size(), Vector3::AXIS_X);252ConvertTransform3DSetting *setting = static_cast<ConvertTransform3DSetting *>(settings[p_index]);253return setting->reference_axis;254}255256void ConvertTransformModifier3D::set_reference_range_min(int p_index, float p_range_min) {257ERR_FAIL_INDEX(p_index, (int)settings.size());258ConvertTransform3DSetting *setting = static_cast<ConvertTransform3DSetting *>(settings[p_index]);259setting->reference_range_min = p_range_min;260}261262float ConvertTransformModifier3D::get_reference_range_min(int p_index) const {263ERR_FAIL_INDEX_V(p_index, (int)settings.size(), 0);264ConvertTransform3DSetting *setting = static_cast<ConvertTransform3DSetting *>(settings[p_index]);265return setting->reference_range_min;266}267268void ConvertTransformModifier3D::set_reference_range_max(int p_index, float p_range_max) {269ERR_FAIL_INDEX(p_index, (int)settings.size());270ConvertTransform3DSetting *setting = static_cast<ConvertTransform3DSetting *>(settings[p_index]);271setting->reference_range_max = p_range_max;272}273274float ConvertTransformModifier3D::get_reference_range_max(int p_index) const {275ERR_FAIL_INDEX_V(p_index, (int)settings.size(), 0);276ConvertTransform3DSetting *setting = static_cast<ConvertTransform3DSetting *>(settings[p_index]);277return setting->reference_range_max;278}279280void ConvertTransformModifier3D::set_relative(int p_index, bool p_enabled) {281ERR_FAIL_INDEX(p_index, (int)settings.size());282ConvertTransform3DSetting *setting = static_cast<ConvertTransform3DSetting *>(settings[p_index]);283setting->relative = p_enabled;284}285286bool ConvertTransformModifier3D::is_relative(int p_index) const {287ERR_FAIL_INDEX_V(p_index, (int)settings.size(), 0);288ConvertTransform3DSetting *setting = static_cast<ConvertTransform3DSetting *>(settings[p_index]);289return setting->is_relative();290}291292void ConvertTransformModifier3D::set_additive(int p_index, bool p_enabled) {293ERR_FAIL_INDEX(p_index, (int)settings.size());294ConvertTransform3DSetting *setting = static_cast<ConvertTransform3DSetting *>(settings[p_index]);295setting->additive = p_enabled;296}297298bool ConvertTransformModifier3D::is_additive(int p_index) const {299ERR_FAIL_INDEX_V(p_index, (int)settings.size(), 0);300ConvertTransform3DSetting *setting = static_cast<ConvertTransform3DSetting *>(settings[p_index]);301return setting->additive;302}303304void ConvertTransformModifier3D::_bind_methods() {305ClassDB::bind_method(D_METHOD("set_apply_transform_mode", "index", "transform_mode"), &ConvertTransformModifier3D::set_apply_transform_mode);306ClassDB::bind_method(D_METHOD("get_apply_transform_mode", "index"), &ConvertTransformModifier3D::get_apply_transform_mode);307ClassDB::bind_method(D_METHOD("set_apply_axis", "index", "axis"), &ConvertTransformModifier3D::set_apply_axis);308ClassDB::bind_method(D_METHOD("get_apply_axis", "index"), &ConvertTransformModifier3D::get_apply_axis);309ClassDB::bind_method(D_METHOD("set_apply_range_min", "index", "range_min"), &ConvertTransformModifier3D::set_apply_range_min);310ClassDB::bind_method(D_METHOD("get_apply_range_min", "index"), &ConvertTransformModifier3D::get_apply_range_min);311ClassDB::bind_method(D_METHOD("set_apply_range_max", "index", "range_max"), &ConvertTransformModifier3D::set_apply_range_max);312ClassDB::bind_method(D_METHOD("get_apply_range_max", "index"), &ConvertTransformModifier3D::get_apply_range_max);313314ClassDB::bind_method(D_METHOD("set_reference_transform_mode", "index", "transform_mode"), &ConvertTransformModifier3D::set_reference_transform_mode);315ClassDB::bind_method(D_METHOD("get_reference_transform_mode", "index"), &ConvertTransformModifier3D::get_reference_transform_mode);316ClassDB::bind_method(D_METHOD("set_reference_axis", "index", "axis"), &ConvertTransformModifier3D::set_reference_axis);317ClassDB::bind_method(D_METHOD("get_reference_axis", "index"), &ConvertTransformModifier3D::get_reference_axis);318ClassDB::bind_method(D_METHOD("set_reference_range_min", "index", "range_min"), &ConvertTransformModifier3D::set_reference_range_min);319ClassDB::bind_method(D_METHOD("get_reference_range_min", "index"), &ConvertTransformModifier3D::get_reference_range_min);320ClassDB::bind_method(D_METHOD("set_reference_range_max", "index", "range_max"), &ConvertTransformModifier3D::set_reference_range_max);321ClassDB::bind_method(D_METHOD("get_reference_range_max", "index"), &ConvertTransformModifier3D::get_reference_range_max);322323ClassDB::bind_method(D_METHOD("set_relative", "index", "enabled"), &ConvertTransformModifier3D::set_relative);324ClassDB::bind_method(D_METHOD("is_relative", "index"), &ConvertTransformModifier3D::is_relative);325ClassDB::bind_method(D_METHOD("set_additive", "index", "enabled"), &ConvertTransformModifier3D::set_additive);326ClassDB::bind_method(D_METHOD("is_additive", "index"), &ConvertTransformModifier3D::is_additive);327328ADD_ARRAY_COUNT("Settings", "setting_count", "set_setting_count", "get_setting_count", "settings/");329330BIND_ENUM_CONSTANT(TRANSFORM_MODE_POSITION);331BIND_ENUM_CONSTANT(TRANSFORM_MODE_ROTATION);332BIND_ENUM_CONSTANT(TRANSFORM_MODE_SCALE);333}334335void ConvertTransformModifier3D::_process_constraint_by_bone(int p_index, Skeleton3D *p_skeleton, int p_apply_bone, int p_reference_bone, float p_amount) {336ConvertTransform3DSetting *setting = static_cast<ConvertTransform3DSetting *>(settings[p_index]);337Transform3D destination = p_skeleton->get_bone_pose(p_reference_bone);338if (setting->is_relative()) {339Vector3 scl_relative = destination.basis.get_scale() / p_skeleton->get_bone_rest(p_reference_bone).basis.get_scale();340destination.basis = p_skeleton->get_bone_rest(p_reference_bone).basis.get_rotation_quaternion().inverse() * destination.basis.get_rotation_quaternion();341destination.basis.scale_local(scl_relative);342destination.origin = destination.origin - p_skeleton->get_bone_rest(p_reference_bone).origin;343}344_process_convert(p_index, p_skeleton, p_apply_bone, destination, p_amount);345}346347void ConvertTransformModifier3D::_process_constraint_by_node(int p_index, Skeleton3D *p_skeleton, int p_apply_bone, const NodePath &p_reference_node, float p_amount) {348Node3D *nd = Object::cast_to<Node3D>(get_node_or_null(p_reference_node));349if (!nd) {350return;351}352Transform3D skel_tr = p_skeleton->get_global_transform_interpolated();353int parent = p_skeleton->get_bone_parent(p_apply_bone);354if (parent >= 0) {355skel_tr = skel_tr * p_skeleton->get_bone_global_pose(parent);356}357Transform3D dest_tr = nd->get_global_transform_interpolated();358Transform3D reference_dest = skel_tr.affine_inverse() * dest_tr;359_process_convert(p_index, p_skeleton, p_apply_bone, reference_dest, p_amount);360}361362void ConvertTransformModifier3D::_process_convert(int p_index, Skeleton3D *p_skeleton, int p_apply_bone, const Transform3D &p_destination, float p_amount) {363ConvertTransform3DSetting *setting = static_cast<ConvertTransform3DSetting *>(settings[p_index]);364365Transform3D destination = p_destination;366367// Retrieve point from reference.368double point = 0.0;369int axis = (int)setting->reference_axis;370switch (setting->reference_transform_mode) {371case TRANSFORM_MODE_POSITION: {372point = destination.origin[axis];373} break;374case TRANSFORM_MODE_ROTATION: {375Quaternion tgt_rot = destination.basis.get_rotation_quaternion();376point = get_roll_angle(tgt_rot, get_vector_from_axis(setting->reference_axis));377} break;378case TRANSFORM_MODE_SCALE: {379point = destination.basis.get_scale()[axis];380} break;381}382// Convert point to apply.383destination = p_skeleton->get_bone_pose(p_apply_bone);384if (Math::is_equal_approx(setting->reference_range_min, setting->reference_range_max)) {385point = point <= (double)setting->reference_range_min ? 0 : 1;386} else {387point = Math::inverse_lerp((double)setting->reference_range_min, (double)setting->reference_range_max, point);388}389point = Math::lerp((double)setting->apply_range_min, (double)setting->apply_range_max, CLAMP(point, 0, 1));390axis = (int)setting->apply_axis;391switch (setting->apply_transform_mode) {392case TRANSFORM_MODE_POSITION: {393if (setting->additive) {394point = p_skeleton->get_bone_pose(p_apply_bone).origin[axis] + point;395} else if (setting->is_relative()) {396point = p_skeleton->get_bone_rest(p_apply_bone).origin[axis] + point;397}398destination.origin[axis] = point;399} break;400case TRANSFORM_MODE_ROTATION: {401Vector3 rot_axis = get_vector_from_axis(setting->apply_axis);402Vector3 dest_scl = destination.basis.get_scale();403if (influence < 1.0 || p_amount < 1.0) {404point = CLAMP(point, CMP_EPSILON - Math::PI, Math::PI - CMP_EPSILON); // Hack to consistent slerp (interpolate_with) orientation since -180/180 deg rot is mixed in slerp.405}406Quaternion rot = Quaternion(rot_axis, point);407if (setting->additive) {408destination.basis = p_skeleton->get_bone_pose(p_apply_bone).basis.get_rotation_quaternion() * rot;409} else if (setting->is_relative()) {410destination.basis = p_skeleton->get_bone_rest(p_apply_bone).basis.get_rotation_quaternion() * rot;411} else {412destination.basis = rot;413}414// Scale may not have meaning, but it might affect when it is negative.415destination.basis.scale_local(dest_scl);416} break;417case TRANSFORM_MODE_SCALE: {418Vector3 dest_scl = Vector3(1, 1, 1);419if (setting->additive) {420dest_scl = p_skeleton->get_bone_pose(p_apply_bone).basis.get_scale();421dest_scl[axis] = dest_scl[axis] * point;422} else if (setting->is_relative()) {423dest_scl = p_skeleton->get_bone_rest(p_apply_bone).basis.get_scale();424dest_scl[axis] = dest_scl[axis] * point;425} else {426dest_scl = p_skeleton->get_bone_pose(p_apply_bone).basis.get_scale();427dest_scl[axis] = point;428}429destination.basis = destination.basis.orthonormalized().scaled_local(dest_scl);430} break;431}432// Process interpolation depends on the amount.433destination = p_skeleton->get_bone_pose(p_apply_bone).interpolate_with(destination, p_amount);434// Apply transform depends on the mode.435switch (setting->apply_transform_mode) {436case TRANSFORM_MODE_POSITION: {437p_skeleton->set_bone_pose_position(p_apply_bone, destination.origin);438} break;439case TRANSFORM_MODE_ROTATION: {440p_skeleton->set_bone_pose_rotation(p_apply_bone, destination.basis.get_rotation_quaternion());441} break;442case TRANSFORM_MODE_SCALE: {443p_skeleton->set_bone_pose_scale(p_apply_bone, destination.basis.get_scale());444} break;445}446}447448ConvertTransformModifier3D::~ConvertTransformModifier3D() {449clear_settings();450}451452453