Path: blob/master/scene/resources/2d/skeleton/skeleton_modification_2d_lookat.cpp
21677 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}142143real_t angle_to_target = operation_bone->get_angle_to(target_node_reference->get_global_position());144145// Account for the direction the bone faces in146angle_to_target -= operation_bone->get_bone_angle();147148// Apply additional rotation149angle_to_target += additional_rotation;150151if (enable_constraint) {152real_t new_angle = angle_to_target;153154if (constraint_in_localspace) {155new_angle += operation_bone->get_rotation();156new_angle = clamp_angle(new_angle, constraint_angle_min, constraint_angle_max, constraint_angle_invert);157operation_bone->set_rotation(new_angle);158} else {159new_angle += operation_bone->get_global_rotation();160new_angle = clamp_angle(new_angle, constraint_angle_min, constraint_angle_max, constraint_angle_invert);161operation_bone->set_global_rotation(new_angle);162}163} else {164operation_bone->rotate(angle_to_target);165}166167// Set the local pose override, and to make sure child bones are also updated, set the transform of the bone.168stack->skeleton->set_bone_local_pose_override(bone_idx, operation_bone->get_transform(), stack->strength, true);169}170171void SkeletonModification2DLookAt::_setup_modification(SkeletonModificationStack2D *p_stack) {172stack = p_stack;173174if (stack != nullptr) {175is_setup = true;176update_target_cache();177update_bone2d_cache();178}179}180181void SkeletonModification2DLookAt::_draw_editor_gizmo() {182if (!enabled || !is_setup || bone_idx < 0) {183return;184}185186Bone2D *operation_bone = stack->skeleton->get_bone(bone_idx);187editor_draw_angle_constraints(operation_bone, constraint_angle_min, constraint_angle_max,188enable_constraint, constraint_in_localspace, constraint_angle_invert);189}190191void SkeletonModification2DLookAt::update_bone2d_cache() {192if (!is_setup || !stack) {193if (is_setup) {194ERR_PRINT_ONCE("Cannot update Bone2D cache: modification is not properly setup!");195}196return;197}198199bone2d_node_cache = ObjectID();200if (stack->skeleton) {201if (stack->skeleton->is_inside_tree()) {202if (stack->skeleton->has_node(bone2d_node)) {203Node *node = stack->skeleton->get_node(bone2d_node);204ERR_FAIL_COND_MSG(!node || stack->skeleton == node,205"Cannot update Bone2D cache: node is this modification's skeleton or cannot be found!");206ERR_FAIL_COND_MSG(!node->is_inside_tree(),207"Cannot update Bone2D cache: node is not in the scene tree!");208bone2d_node_cache = node->get_instance_id();209210Bone2D *bone = Object::cast_to<Bone2D>(node);211if (bone) {212bone_idx = bone->get_index_in_skeleton();213} else {214ERR_FAIL_MSG("Error Bone2D cache: Nodepath to Bone2D is not a Bone2D node!");215}216217// Set this to null so we update it218target_node_reference = nullptr;219}220}221}222}223224void SkeletonModification2DLookAt::set_bone2d_node(const NodePath &p_target_node) {225bone2d_node = p_target_node;226update_bone2d_cache();227}228229NodePath SkeletonModification2DLookAt::get_bone2d_node() const {230return bone2d_node;231}232233int SkeletonModification2DLookAt::get_bone_index() const {234return bone_idx;235}236237void SkeletonModification2DLookAt::set_bone_index(int p_bone_idx) {238ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!");239240if (is_setup && stack) {241if (stack->skeleton) {242ERR_FAIL_INDEX_MSG(p_bone_idx, stack->skeleton->get_bone_count(), "Passed-in Bone index is out of range!");243bone_idx = p_bone_idx;244bone2d_node_cache = stack->skeleton->get_bone(p_bone_idx)->get_instance_id();245bone2d_node = stack->skeleton->get_path_to(stack->skeleton->get_bone(p_bone_idx));246} else {247WARN_PRINT("Cannot verify the bone index for this modification...");248bone_idx = p_bone_idx;249}250} else {251bone_idx = p_bone_idx;252}253254notify_property_list_changed();255}256257void SkeletonModification2DLookAt::update_target_cache() {258if (!is_setup || !stack) {259if (is_setup) {260ERR_PRINT_ONCE("Cannot update target cache: modification is not properly setup!");261}262return;263}264265target_node_cache = ObjectID();266if (stack->skeleton) {267if (stack->skeleton->is_inside_tree()) {268if (stack->skeleton->has_node(target_node)) {269Node *node = stack->skeleton->get_node(target_node);270ERR_FAIL_COND_MSG(!node || stack->skeleton == node,271"Cannot update target cache: node is this modification's skeleton or cannot be found!");272ERR_FAIL_COND_MSG(!node->is_inside_tree(),273"Cannot update target cache: node is not in the scene tree!");274target_node_cache = node->get_instance_id();275}276}277}278}279280void SkeletonModification2DLookAt::set_target_node(const NodePath &p_target_node) {281target_node = p_target_node;282update_target_cache();283}284285NodePath SkeletonModification2DLookAt::get_target_node() const {286return target_node;287}288289float SkeletonModification2DLookAt::get_additional_rotation() const {290return additional_rotation;291}292293void SkeletonModification2DLookAt::set_additional_rotation(float p_rotation) {294additional_rotation = p_rotation;295}296297void SkeletonModification2DLookAt::set_enable_constraint(bool p_constraint) {298enable_constraint = p_constraint;299notify_property_list_changed();300#ifdef TOOLS_ENABLED301if (stack && is_setup) {302stack->set_editor_gizmos_dirty(true);303}304#endif // TOOLS_ENABLED305}306307bool SkeletonModification2DLookAt::get_enable_constraint() const {308return enable_constraint;309}310311void SkeletonModification2DLookAt::set_constraint_angle_min(float p_angle_min) {312constraint_angle_min = p_angle_min;313#ifdef TOOLS_ENABLED314if (stack && is_setup) {315stack->set_editor_gizmos_dirty(true);316}317#endif // TOOLS_ENABLED318}319320float SkeletonModification2DLookAt::get_constraint_angle_min() const {321return constraint_angle_min;322}323324void SkeletonModification2DLookAt::set_constraint_angle_max(float p_angle_max) {325constraint_angle_max = p_angle_max;326#ifdef TOOLS_ENABLED327if (stack && is_setup) {328stack->set_editor_gizmos_dirty(true);329}330#endif // TOOLS_ENABLED331}332333float SkeletonModification2DLookAt::get_constraint_angle_max() const {334return constraint_angle_max;335}336337void SkeletonModification2DLookAt::set_constraint_angle_invert(bool p_invert) {338constraint_angle_invert = p_invert;339#ifdef TOOLS_ENABLED340if (stack && is_setup) {341stack->set_editor_gizmos_dirty(true);342}343#endif // TOOLS_ENABLED344}345346bool SkeletonModification2DLookAt::get_constraint_angle_invert() const {347return constraint_angle_invert;348}349350void SkeletonModification2DLookAt::set_constraint_in_localspace(bool p_constraint_in_localspace) {351constraint_in_localspace = p_constraint_in_localspace;352#ifdef TOOLS_ENABLED353if (stack && is_setup) {354stack->set_editor_gizmos_dirty(true);355}356#endif // TOOLS_ENABLED357}358359bool SkeletonModification2DLookAt::get_constraint_in_localspace() const {360return constraint_in_localspace;361}362363void SkeletonModification2DLookAt::_bind_methods() {364ClassDB::bind_method(D_METHOD("set_bone2d_node", "bone2d_nodepath"), &SkeletonModification2DLookAt::set_bone2d_node);365ClassDB::bind_method(D_METHOD("get_bone2d_node"), &SkeletonModification2DLookAt::get_bone2d_node);366ClassDB::bind_method(D_METHOD("set_bone_index", "bone_idx"), &SkeletonModification2DLookAt::set_bone_index);367ClassDB::bind_method(D_METHOD("get_bone_index"), &SkeletonModification2DLookAt::get_bone_index);368369ClassDB::bind_method(D_METHOD("set_target_node", "target_nodepath"), &SkeletonModification2DLookAt::set_target_node);370ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonModification2DLookAt::get_target_node);371372ClassDB::bind_method(D_METHOD("set_additional_rotation", "rotation"), &SkeletonModification2DLookAt::set_additional_rotation);373ClassDB::bind_method(D_METHOD("get_additional_rotation"), &SkeletonModification2DLookAt::get_additional_rotation);374375ClassDB::bind_method(D_METHOD("set_enable_constraint", "enable_constraint"), &SkeletonModification2DLookAt::set_enable_constraint);376ClassDB::bind_method(D_METHOD("get_enable_constraint"), &SkeletonModification2DLookAt::get_enable_constraint);377ClassDB::bind_method(D_METHOD("set_constraint_angle_min", "angle_min"), &SkeletonModification2DLookAt::set_constraint_angle_min);378ClassDB::bind_method(D_METHOD("get_constraint_angle_min"), &SkeletonModification2DLookAt::get_constraint_angle_min);379ClassDB::bind_method(D_METHOD("set_constraint_angle_max", "angle_max"), &SkeletonModification2DLookAt::set_constraint_angle_max);380ClassDB::bind_method(D_METHOD("get_constraint_angle_max"), &SkeletonModification2DLookAt::get_constraint_angle_max);381ClassDB::bind_method(D_METHOD("set_constraint_angle_invert", "invert"), &SkeletonModification2DLookAt::set_constraint_angle_invert);382ClassDB::bind_method(D_METHOD("get_constraint_angle_invert"), &SkeletonModification2DLookAt::get_constraint_angle_invert);383384ADD_PROPERTY(PropertyInfo(Variant::INT, "bone_index"), "set_bone_index", "get_bone_index");385ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "bone2d_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Bone2D"), "set_bone2d_node", "get_bone2d_node");386ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node2D"), "set_target_node", "get_target_node");387}388389SkeletonModification2DLookAt::SkeletonModification2DLookAt() {390stack = nullptr;391is_setup = false;392bone_idx = -1;393additional_rotation = 0;394enable_constraint = false;395constraint_angle_min = 0;396constraint_angle_max = Math::PI * 2;397constraint_angle_invert = false;398enabled = true;399400editor_draw_gizmo = true;401}402403SkeletonModification2DLookAt::~SkeletonModification2DLookAt() {404}405406407