Path: blob/master/scene/resources/2d/skeleton/skeleton_modification_2d_lookat.cpp
9904 views
/**************************************************************************/1/* skeleton_modification_2d_lookat.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 "skeleton_modification_2d_lookat.h"31#include "scene/2d/skeleton_2d.h"3233bool SkeletonModification2DLookAt::_set(const StringName &p_path, const Variant &p_value) {34String path = p_path;3536if (path.begins_with("enable_constraint")) {37set_enable_constraint(p_value);38} else if (path.begins_with("constraint_angle_min")) {39set_constraint_angle_min(Math::deg_to_rad(float(p_value)));40} else if (path.begins_with("constraint_angle_max")) {41set_constraint_angle_max(Math::deg_to_rad(float(p_value)));42} else if (path.begins_with("constraint_angle_invert")) {43set_constraint_angle_invert(p_value);44} else if (path.begins_with("constraint_in_localspace")) {45set_constraint_in_localspace(p_value);46} else if (path.begins_with("additional_rotation")) {47set_additional_rotation(Math::deg_to_rad(float(p_value)));48}49#ifdef TOOLS_ENABLED50else if (path.begins_with("editor/draw_gizmo")) {51set_editor_draw_gizmo(p_value);52}53#endif // TOOLS_ENABLED54else {55return false;56}5758return true;59}6061bool SkeletonModification2DLookAt::_get(const StringName &p_path, Variant &r_ret) const {62String path = p_path;6364if (path.begins_with("enable_constraint")) {65r_ret = get_enable_constraint();66} else if (path.begins_with("constraint_angle_min")) {67r_ret = Math::rad_to_deg(get_constraint_angle_min());68} else if (path.begins_with("constraint_angle_max")) {69r_ret = Math::rad_to_deg(get_constraint_angle_max());70} else if (path.begins_with("constraint_angle_invert")) {71r_ret = get_constraint_angle_invert();72} else if (path.begins_with("constraint_in_localspace")) {73r_ret = get_constraint_in_localspace();74} else if (path.begins_with("additional_rotation")) {75r_ret = Math::rad_to_deg(get_additional_rotation());76}77#ifdef TOOLS_ENABLED78else if (path.begins_with("editor/draw_gizmo")) {79r_ret = get_editor_draw_gizmo();80}81#endif // TOOLS_ENABLED82else {83return false;84}8586return true;87}8889void SkeletonModification2DLookAt::_get_property_list(List<PropertyInfo> *p_list) const {90p_list->push_back(PropertyInfo(Variant::BOOL, "enable_constraint", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));91if (enable_constraint) {92p_list->push_back(PropertyInfo(Variant::FLOAT, "constraint_angle_min", PROPERTY_HINT_RANGE, "-360, 360, 0.01", PROPERTY_USAGE_DEFAULT));93p_list->push_back(PropertyInfo(Variant::FLOAT, "constraint_angle_max", PROPERTY_HINT_RANGE, "-360, 360, 0.01", PROPERTY_USAGE_DEFAULT));94p_list->push_back(PropertyInfo(Variant::BOOL, "constraint_angle_invert", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));95p_list->push_back(PropertyInfo(Variant::BOOL, "constraint_in_localspace", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));96}97p_list->push_back(PropertyInfo(Variant::FLOAT, "additional_rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));9899#ifdef TOOLS_ENABLED100if (Engine::get_singleton()->is_editor_hint()) {101p_list->push_back(PropertyInfo(Variant::BOOL, "editor/draw_gizmo", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));102}103#endif // TOOLS_ENABLED104}105106void SkeletonModification2DLookAt::_execute(float p_delta) {107ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr,108"Modification is not setup and therefore cannot execute!");109if (!enabled) {110return;111}112113if (target_node_cache.is_null()) {114WARN_PRINT_ONCE("Target cache is out of date. Attempting to update...");115update_target_cache();116return;117}118119if (bone2d_node_cache.is_null() && !bone2d_node.is_empty()) {120update_bone2d_cache();121WARN_PRINT_ONCE("Bone2D node cache is out of date. Attempting to update...");122return;123}124125if (target_node_reference == nullptr) {126target_node_reference = ObjectDB::get_instance<Node2D>(target_node_cache);127}128if (!target_node_reference || !target_node_reference->is_inside_tree()) {129ERR_PRINT_ONCE("Target node is not in the scene tree. Cannot execute modification!");130return;131}132if (bone_idx <= -1) {133ERR_PRINT_ONCE("Bone index is invalid. Cannot execute modification!");134return;135}136137Bone2D *operation_bone = stack->skeleton->get_bone(bone_idx);138if (operation_bone == nullptr) {139ERR_PRINT_ONCE("bone_idx for modification does not point to a valid bone! Cannot execute modification");140return;141}142143Transform2D operation_transform = operation_bone->get_global_transform();144Transform2D target_trans = target_node_reference->get_global_transform();145146// Look at the target!147operation_transform = operation_transform.looking_at(target_trans.get_origin());148// Apply whatever scale it had prior to looking_at149operation_transform.set_scale(operation_bone->get_global_scale());150151// Account for the direction the bone faces in:152operation_transform.set_rotation(operation_transform.get_rotation() - operation_bone->get_bone_angle());153154// Apply additional rotation155operation_transform.set_rotation(operation_transform.get_rotation() + additional_rotation);156157// Apply constraints in globalspace:158if (enable_constraint && !constraint_in_localspace) {159operation_transform.set_rotation(clamp_angle(operation_transform.get_rotation(), constraint_angle_min, constraint_angle_max, constraint_angle_invert));160}161162// Convert from a global transform to a local transform via the Bone2D node163operation_bone->set_global_transform(operation_transform);164operation_transform = operation_bone->get_transform();165166// Apply constraints in localspace:167if (enable_constraint && constraint_in_localspace) {168operation_transform.set_rotation(clamp_angle(operation_transform.get_rotation(), constraint_angle_min, constraint_angle_max, constraint_angle_invert));169}170171// Set the local pose override, and to make sure child bones are also updated, set the transform of the bone.172stack->skeleton->set_bone_local_pose_override(bone_idx, operation_transform, stack->strength, true);173operation_bone->set_transform(operation_transform);174}175176void SkeletonModification2DLookAt::_setup_modification(SkeletonModificationStack2D *p_stack) {177stack = p_stack;178179if (stack != nullptr) {180is_setup = true;181update_target_cache();182update_bone2d_cache();183}184}185186void SkeletonModification2DLookAt::_draw_editor_gizmo() {187if (!enabled || !is_setup) {188return;189}190191Bone2D *operation_bone = stack->skeleton->get_bone(bone_idx);192editor_draw_angle_constraints(operation_bone, constraint_angle_min, constraint_angle_max,193enable_constraint, constraint_in_localspace, constraint_angle_invert);194}195196void SkeletonModification2DLookAt::update_bone2d_cache() {197if (!is_setup || !stack) {198if (is_setup) {199ERR_PRINT_ONCE("Cannot update Bone2D cache: modification is not properly setup!");200}201return;202}203204bone2d_node_cache = ObjectID();205if (stack->skeleton) {206if (stack->skeleton->is_inside_tree()) {207if (stack->skeleton->has_node(bone2d_node)) {208Node *node = stack->skeleton->get_node(bone2d_node);209ERR_FAIL_COND_MSG(!node || stack->skeleton == node,210"Cannot update Bone2D cache: node is this modification's skeleton or cannot be found!");211ERR_FAIL_COND_MSG(!node->is_inside_tree(),212"Cannot update Bone2D cache: node is not in the scene tree!");213bone2d_node_cache = node->get_instance_id();214215Bone2D *bone = Object::cast_to<Bone2D>(node);216if (bone) {217bone_idx = bone->get_index_in_skeleton();218} else {219ERR_FAIL_MSG("Error Bone2D cache: Nodepath to Bone2D is not a Bone2D node!");220}221222// Set this to null so we update it223target_node_reference = nullptr;224}225}226}227}228229void SkeletonModification2DLookAt::set_bone2d_node(const NodePath &p_target_node) {230bone2d_node = p_target_node;231update_bone2d_cache();232}233234NodePath SkeletonModification2DLookAt::get_bone2d_node() const {235return bone2d_node;236}237238int SkeletonModification2DLookAt::get_bone_index() const {239return bone_idx;240}241242void SkeletonModification2DLookAt::set_bone_index(int p_bone_idx) {243ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!");244245if (is_setup && stack) {246if (stack->skeleton) {247ERR_FAIL_INDEX_MSG(p_bone_idx, stack->skeleton->get_bone_count(), "Passed-in Bone index is out of range!");248bone_idx = p_bone_idx;249bone2d_node_cache = stack->skeleton->get_bone(p_bone_idx)->get_instance_id();250bone2d_node = stack->skeleton->get_path_to(stack->skeleton->get_bone(p_bone_idx));251} else {252WARN_PRINT("Cannot verify the bone index for this modification...");253bone_idx = p_bone_idx;254}255} else {256bone_idx = p_bone_idx;257}258259notify_property_list_changed();260}261262void SkeletonModification2DLookAt::update_target_cache() {263if (!is_setup || !stack) {264if (is_setup) {265ERR_PRINT_ONCE("Cannot update target cache: modification is not properly setup!");266}267return;268}269270target_node_cache = ObjectID();271if (stack->skeleton) {272if (stack->skeleton->is_inside_tree()) {273if (stack->skeleton->has_node(target_node)) {274Node *node = stack->skeleton->get_node(target_node);275ERR_FAIL_COND_MSG(!node || stack->skeleton == node,276"Cannot update target cache: node is this modification's skeleton or cannot be found!");277ERR_FAIL_COND_MSG(!node->is_inside_tree(),278"Cannot update target cache: node is not in the scene tree!");279target_node_cache = node->get_instance_id();280}281}282}283}284285void SkeletonModification2DLookAt::set_target_node(const NodePath &p_target_node) {286target_node = p_target_node;287update_target_cache();288}289290NodePath SkeletonModification2DLookAt::get_target_node() const {291return target_node;292}293294float SkeletonModification2DLookAt::get_additional_rotation() const {295return additional_rotation;296}297298void SkeletonModification2DLookAt::set_additional_rotation(float p_rotation) {299additional_rotation = p_rotation;300}301302void SkeletonModification2DLookAt::set_enable_constraint(bool p_constraint) {303enable_constraint = p_constraint;304notify_property_list_changed();305#ifdef TOOLS_ENABLED306if (stack && is_setup) {307stack->set_editor_gizmos_dirty(true);308}309#endif // TOOLS_ENABLED310}311312bool SkeletonModification2DLookAt::get_enable_constraint() const {313return enable_constraint;314}315316void SkeletonModification2DLookAt::set_constraint_angle_min(float p_angle_min) {317constraint_angle_min = p_angle_min;318#ifdef TOOLS_ENABLED319if (stack && is_setup) {320stack->set_editor_gizmos_dirty(true);321}322#endif // TOOLS_ENABLED323}324325float SkeletonModification2DLookAt::get_constraint_angle_min() const {326return constraint_angle_min;327}328329void SkeletonModification2DLookAt::set_constraint_angle_max(float p_angle_max) {330constraint_angle_max = p_angle_max;331#ifdef TOOLS_ENABLED332if (stack && is_setup) {333stack->set_editor_gizmos_dirty(true);334}335#endif // TOOLS_ENABLED336}337338float SkeletonModification2DLookAt::get_constraint_angle_max() const {339return constraint_angle_max;340}341342void SkeletonModification2DLookAt::set_constraint_angle_invert(bool p_invert) {343constraint_angle_invert = p_invert;344#ifdef TOOLS_ENABLED345if (stack && is_setup) {346stack->set_editor_gizmos_dirty(true);347}348#endif // TOOLS_ENABLED349}350351bool SkeletonModification2DLookAt::get_constraint_angle_invert() const {352return constraint_angle_invert;353}354355void SkeletonModification2DLookAt::set_constraint_in_localspace(bool p_constraint_in_localspace) {356constraint_in_localspace = p_constraint_in_localspace;357#ifdef TOOLS_ENABLED358if (stack && is_setup) {359stack->set_editor_gizmos_dirty(true);360}361#endif // TOOLS_ENABLED362}363364bool SkeletonModification2DLookAt::get_constraint_in_localspace() const {365return constraint_in_localspace;366}367368void SkeletonModification2DLookAt::_bind_methods() {369ClassDB::bind_method(D_METHOD("set_bone2d_node", "bone2d_nodepath"), &SkeletonModification2DLookAt::set_bone2d_node);370ClassDB::bind_method(D_METHOD("get_bone2d_node"), &SkeletonModification2DLookAt::get_bone2d_node);371ClassDB::bind_method(D_METHOD("set_bone_index", "bone_idx"), &SkeletonModification2DLookAt::set_bone_index);372ClassDB::bind_method(D_METHOD("get_bone_index"), &SkeletonModification2DLookAt::get_bone_index);373374ClassDB::bind_method(D_METHOD("set_target_node", "target_nodepath"), &SkeletonModification2DLookAt::set_target_node);375ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonModification2DLookAt::get_target_node);376377ClassDB::bind_method(D_METHOD("set_additional_rotation", "rotation"), &SkeletonModification2DLookAt::set_additional_rotation);378ClassDB::bind_method(D_METHOD("get_additional_rotation"), &SkeletonModification2DLookAt::get_additional_rotation);379380ClassDB::bind_method(D_METHOD("set_enable_constraint", "enable_constraint"), &SkeletonModification2DLookAt::set_enable_constraint);381ClassDB::bind_method(D_METHOD("get_enable_constraint"), &SkeletonModification2DLookAt::get_enable_constraint);382ClassDB::bind_method(D_METHOD("set_constraint_angle_min", "angle_min"), &SkeletonModification2DLookAt::set_constraint_angle_min);383ClassDB::bind_method(D_METHOD("get_constraint_angle_min"), &SkeletonModification2DLookAt::get_constraint_angle_min);384ClassDB::bind_method(D_METHOD("set_constraint_angle_max", "angle_max"), &SkeletonModification2DLookAt::set_constraint_angle_max);385ClassDB::bind_method(D_METHOD("get_constraint_angle_max"), &SkeletonModification2DLookAt::get_constraint_angle_max);386ClassDB::bind_method(D_METHOD("set_constraint_angle_invert", "invert"), &SkeletonModification2DLookAt::set_constraint_angle_invert);387ClassDB::bind_method(D_METHOD("get_constraint_angle_invert"), &SkeletonModification2DLookAt::get_constraint_angle_invert);388389ADD_PROPERTY(PropertyInfo(Variant::INT, "bone_index"), "set_bone_index", "get_bone_index");390ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "bone2d_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Bone2D"), "set_bone2d_node", "get_bone2d_node");391ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node2D"), "set_target_node", "get_target_node");392}393394SkeletonModification2DLookAt::SkeletonModification2DLookAt() {395stack = nullptr;396is_setup = false;397bone_idx = -1;398additional_rotation = 0;399enable_constraint = false;400constraint_angle_min = 0;401constraint_angle_max = Math::PI * 2;402constraint_angle_invert = false;403enabled = true;404405editor_draw_gizmo = true;406}407408SkeletonModification2DLookAt::~SkeletonModification2DLookAt() {409}410411412