From 5811a456208cbf4fc6b1c3e1125a80acb40e110f Mon Sep 17 00:00:00 2001 From: "Silc Lizard (Tokage) Renew" <61938263+TokageItLab@users.noreply.github.com> Date: Sun, 20 Oct 2024 11:15:51 +0900 Subject: [PATCH] a --- doc/classes/LookAtModifier3D.xml | 47 +++++ scene/3d/look_at_modifier_3d.cpp | 320 +++++++++++++++++++++++++++++++ scene/3d/look_at_modifier_3d.h | 115 +++++++++++ scene/register_scene_types.cpp | 2 + 4 files changed, 484 insertions(+) create mode 100644 doc/classes/LookAtModifier3D.xml create mode 100644 scene/3d/look_at_modifier_3d.cpp create mode 100644 scene/3d/look_at_modifier_3d.h diff --git a/doc/classes/LookAtModifier3D.xml b/doc/classes/LookAtModifier3D.xml new file mode 100644 index 000000000000..6ee76d7a7d12 --- /dev/null +++ b/doc/classes/LookAtModifier3D.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scene/3d/look_at_modifier_3d.cpp b/scene/3d/look_at_modifier_3d.cpp new file mode 100644 index 000000000000..f987e82233e4 --- /dev/null +++ b/scene/3d/look_at_modifier_3d.cpp @@ -0,0 +1,320 @@ +/**************************************************************************/ +/* look_at_modifier_3d.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "look_at_modifier_3d.h" + +void LookAtModifier3D::_validate_property(PropertyInfo &p_property) const { + SkeletonModifier3D::_validate_property(p_property); + + if (p_property.name == "bone") { + Skeleton3D *skeleton = get_skeleton(); + if (skeleton) { + p_property.hint = PROPERTY_HINT_ENUM; + p_property.hint_string = skeleton->get_concatenated_bone_names(); + } else { + p_property.hint = PROPERTY_HINT_NONE; + p_property.hint_string = ""; + } + } +} + +void LookAtModifier3D::set_bone(int p_bone) { + bone = p_bone; +} + +int LookAtModifier3D::get_bone() const { + return bone; +} + +void LookAtModifier3D::set_forward_axis(LookAtModifier3D::BoneAxis p_axis) { + forward_axis = p_axis; +} + +LookAtModifier3D::BoneAxis LookAtModifier3D::get_forward_axis() const { + return forward_axis; +} + +void LookAtModifier3D::set_primary_rotation(Vector3::Axis p_axis) { + primary_rotation = p_axis; +} + +Vector3::Axis LookAtModifier3D::get_primary_rotation() const { + return primary_rotation; +} + +void LookAtModifier3D::set_use_secondary_rotation(bool p_enabled) { + use_secondary_rotation = p_enabled; +} + +bool LookAtModifier3D::is_using_secondary_rotation() const { + return use_secondary_rotation; +} + +void LookAtModifier3D::set_target_node(NodePath p_target_node) { + target_node = p_target_node; +} + +NodePath LookAtModifier3D::get_target_node() const { + return target_node; +} + +// For time-based interpolation. + +void LookAtModifier3D::set_duration(float p_duration) { + duration = p_duration; +} + +float LookAtModifier3D::get_duration() const { + return duration; +} + +void LookAtModifier3D::set_transition_type(Tween::TransitionType p_transition_type) { + transition_type = p_transition_type; +} + +Tween::TransitionType LookAtModifier3D::get_transition_type() const { + return transition_type; +} + +void LookAtModifier3D::set_ease_type(Tween::EaseType p_ease_type) { + ease_type = p_ease_type; +} + +Tween::EaseType LookAtModifier3D::get_ease_type() const { + return ease_type; +} + +// For angle limitation. + +void LookAtModifier3D::set_use_angle_limitation(bool p_enabled) { + use_angle_limitation = p_enabled; + notify_property_list_changed(); +} + +bool LookAtModifier3D::is_using_angle_limitation() const { + return use_angle_limitation; +} + +void LookAtModifier3D::set_limit_angle(float p_angle) { + limit_angle = p_angle; +} + +float LookAtModifier3D::get_limit_angle() const { + return limit_angle; +} + +void LookAtModifier3D::set_dump_power(float p_power) { + dump_power = p_power; +} + +float LookAtModifier3D::get_dump_power() const { + return dump_power; +} + +// General API. + +void LookAtModifier3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_target_node", "target_node"), &LookAtModifier3D::set_target_node); + ClassDB::bind_method(D_METHOD("get_target_node"), &LookAtModifier3D::get_target_node); + + ClassDB::bind_method(D_METHOD("set_bone", "bone"), &LookAtModifier3D::set_bone); + ClassDB::bind_method(D_METHOD("get_bone"), &LookAtModifier3D::get_bone); + ClassDB::bind_method(D_METHOD("set_forward_axis", "forward_axis"), &LookAtModifier3D::set_forward_axis); + ClassDB::bind_method(D_METHOD("get_forward_axis"), &LookAtModifier3D::get_forward_axis); + ClassDB::bind_method(D_METHOD("set_primary_rotation", "axis"), &LookAtModifier3D::set_primary_rotation); + ClassDB::bind_method(D_METHOD("get_primary_rotation"), &LookAtModifier3D::get_primary_rotation); + ClassDB::bind_method(D_METHOD("set_use_secondary_rotation", "enabled"), &LookAtModifier3D::set_use_secondary_rotation); + ClassDB::bind_method(D_METHOD("is_using_secondary_rotation"), &LookAtModifier3D::is_using_secondary_rotation); + + ClassDB::bind_method(D_METHOD("set_duration", "duration"), &LookAtModifier3D::set_duration); + ClassDB::bind_method(D_METHOD("get_duration"), &LookAtModifier3D::get_duration); + ClassDB::bind_method(D_METHOD("set_transition_type", "transition_type"), &LookAtModifier3D::set_transition_type); + ClassDB::bind_method(D_METHOD("get_transition_type"), &LookAtModifier3D::get_transition_type); + ClassDB::bind_method(D_METHOD("set_ease_type", "ease_type"), &LookAtModifier3D::set_ease_type); + ClassDB::bind_method(D_METHOD("get_ease_type"), &LookAtModifier3D::get_ease_type); + + ClassDB::bind_method(D_METHOD("set_use_angle_limitation", "enabled"), &LookAtModifier3D::set_use_angle_limitation); + ClassDB::bind_method(D_METHOD("is_using_angle_limitation"), &LookAtModifier3D::is_using_angle_limitation); + ClassDB::bind_method(D_METHOD("set_limit_angle", "angle"), &LookAtModifier3D::set_limit_angle); + ClassDB::bind_method(D_METHOD("get_limit_angle"), &LookAtModifier3D::get_limit_angle); + ClassDB::bind_method(D_METHOD("set_dump_power", "power"), &LookAtModifier3D::set_dump_power); + ClassDB::bind_method(D_METHOD("get_dump_power"), &LookAtModifier3D::get_dump_power); + + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_node", PROPERTY_HINT_NODE_TYPE, "Node3D"), "set_target_node", "get_target_node"); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "bone", PROPERTY_HINT_ENUM, ""), "set_bone", "get_bone"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "forward_axis", PROPERTY_HINT_ENUM, "+X,-X,+Y,-Y,+Z,-Z"), "set_forward_axis", "get_forward_axis"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "primary_rotation", PROPERTY_HINT_ENUM, "X,Y,Z"), "set_primary_rotation", "get_primary_rotation"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_secondary_rotation"), "set_use_secondary_rotation", "is_using_secondary_rotation"); + + ADD_GROUP("Time Based Interpolation", ""); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "duration", PROPERTY_HINT_RANGE, "0,10,0.001,or_greater,suffix:s"), "set_duration", "get_duration"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "transition_type", PROPERTY_HINT_ENUM, "Linear,Sine,Quint,Quart,Quad,Expo,Elastic,Cubic,Circ,Bounce,Back,Spring"), "set_transition_type", "get_transition_type"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "ease_type", PROPERTY_HINT_ENUM, "In,Out,InOut,OutIn"), "set_ease_type", "get_ease_type"); + + ADD_GROUP("Angle Limitation", ""); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_angle_limitation"), "set_use_angle_limitation", "is_using_angle_limitation"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "limit_angle", PROPERTY_HINT_RANGE, "0,360,0.01,radians_as_degrees"), "set_limit_angle", "get_limit_angle"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dump_power", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_dump_power", "get_dump_power"); + + BIND_ENUM_CONSTANT(BONE_AXIS_PLUS_X); + BIND_ENUM_CONSTANT(BONE_AXIS_MINUS_X); + BIND_ENUM_CONSTANT(BONE_AXIS_PLUS_Y); + BIND_ENUM_CONSTANT(BONE_AXIS_MINUS_Y); + BIND_ENUM_CONSTANT(BONE_AXIS_PLUS_Z); + BIND_ENUM_CONSTANT(BONE_AXIS_MINUS_Z); +} + +void LookAtModifier3D::_process_modification() { + Skeleton3D *skeleton = get_skeleton(); + if (!skeleton) { + return; + } + + Node3D *target = cast_to(get_node_or_null(target_node)); + if (!target) { + return; + } + + Transform3D destination = look_at_with_axes(skeleton->get_bone_pose(bone), target->get_transform().get_origin()); + skeleton->set_bone_pose(bone, destination); +} + +Vector3 LookAtModifier3D::get_basis_vector_from_bone_axis(Basis p_basis, LookAtModifier3D::BoneAxis p_axis) { + Vector3 ret; + switch (p_axis) { + case BONE_AXIS_PLUS_X: { + ret = p_basis.get_column(0).normalized(); + } break; + case BONE_AXIS_MINUS_X: { + ret = -p_basis.get_column(0).normalized(); + } break; + case BONE_AXIS_PLUS_Y: { + ret = p_basis.get_column(1).normalized(); + } break; + case BONE_AXIS_MINUS_Y: { + ret = -p_basis.get_column(1).normalized(); + } break; + case BONE_AXIS_PLUS_Z: { + ret = p_basis.get_column(2).normalized(); + } break; + case BONE_AXIS_MINUS_Z: { + ret = -p_basis.get_column(2).normalized(); + } break; + } + return ret; +} + +Vector3 LookAtModifier3D::get_vector_from_bone_axis(LookAtModifier3D::BoneAxis p_axis) { + Vector3 ret; + switch (p_axis) { + case BONE_AXIS_PLUS_X: { + ret = Vector3(1, 0, 0); + } break; + case BONE_AXIS_MINUS_X: { + ret = Vector3(-1, 0, 0); + } break; + case BONE_AXIS_PLUS_Y: { + ret = Vector3(0, 1, 0); + } break; + case BONE_AXIS_MINUS_Y: { + ret = Vector3(0, -1, 0); + } break; + case BONE_AXIS_PLUS_Z: { + ret = Vector3(0, 0, 1); + } break; + case BONE_AXIS_MINUS_Z: { + ret = Vector3(0, 0, -1); + } break; + } + return ret; +} + +Vector3 LookAtModifier3D::get_vector_from_axis(Vector3::Axis p_axis) { + Vector3 ret; + switch (p_axis) { + case Vector3::AXIS_X: { + ret = Vector3(1, 0, 0); + } break; + case Vector3::AXIS_Y: { + ret = Vector3(0, 1, 0); + } break; + case Vector3::AXIS_Z: { + ret = Vector3(0, 0, 1); + } break; + } + return ret.normalized(); +} + +Vector2 LookAtModifier3D::get_projection_vector(Vector3 p_vector, Vector3::Axis p_axis) { + // NOTE: axis is swapped between 2D and 3D. + // TODO: projection from specific plane, current state support only horizontal/vertical pararel plane. + Vector2 ret; + switch (p_axis) { + case Vector3::AXIS_X: { + ret = Vector2(p_vector.z, p_vector.y); + } break; + case Vector3::AXIS_Y: { + ret = Vector2(p_vector.x, p_vector.z); + } break; + case Vector3::AXIS_Z: { + ret = Vector2(p_vector.y, p_vector.x); + } break; + } + return ret.normalized(); +} + +Transform3D LookAtModifier3D::look_at_with_axes(Transform3D p_from, Vector3 p_target) { + Vector3 forward_vector = (p_target - p_from.origin).normalized(); + + // Primary rotation by projection to 2D plane. + Vector3 current_vector = get_basis_vector_from_bone_axis(p_from.basis, forward_axis); + Vector2 primary_src = get_projection_vector(forward_vector, primary_rotation); + Vector2 primary_dst = get_projection_vector(current_vector, primary_rotation); + real_t primary_angle = primary_src.angle_to(primary_dst); + + Transform3D primary_result = p_from.rotated_local(get_vector_from_axis(primary_rotation), primary_angle); + + if (!use_secondary_rotation) { + return primary_result; + } + + Vector3 secondary_axis = get_vector_from_bone_axis(forward_axis) + get_vector_from_axis(primary_rotation); + Vector3::Axis secondary_rotation = Math::is_zero_approx(secondary_axis.x) ? Vector3::AXIS_X : (Math::is_zero_approx(secondary_axis.y) ? Vector3::AXIS_Y : Vector3::AXIS_Z); + + current_vector = get_basis_vector_from_bone_axis(primary_result.basis, forward_axis); + Vector2 secondary_src = get_projection_vector(forward_vector, secondary_rotation); + Vector2 secondary_dst = get_projection_vector(current_vector, secondary_rotation); + real_t secondary_angle = secondary_src.angle_to(secondary_dst); + + Transform3D secondary_result = primary_result.rotated_local(get_vector_from_axis(secondary_rotation), secondary_angle); + + return secondary_result; +} diff --git a/scene/3d/look_at_modifier_3d.h b/scene/3d/look_at_modifier_3d.h new file mode 100644 index 000000000000..1db1608cccf0 --- /dev/null +++ b/scene/3d/look_at_modifier_3d.h @@ -0,0 +1,115 @@ +/**************************************************************************/ +/* look_at_modifier_3d.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef LOOK_AT_MODIFIER_3D_H +#define LOOK_AT_MODIFIER_3D_H + +#include "scene/3d/skeleton_modifier_3d.h" +#include "scene/animation/tween.h" + +class LookAtModifier3D : public SkeletonModifier3D { + GDCLASS(LookAtModifier3D, SkeletonModifier3D); + +public: + enum BoneAxis { + BONE_AXIS_PLUS_X, + BONE_AXIS_MINUS_X, + BONE_AXIS_PLUS_Y, + BONE_AXIS_MINUS_Y, + BONE_AXIS_PLUS_Z, + BONE_AXIS_MINUS_Z, + }; + +private: + int bone = 0; + + BoneAxis forward_axis = BONE_AXIS_PLUS_Z; + Vector3::Axis primary_rotation = Vector3::AXIS_Y; + bool use_secondary_rotation = true; + + NodePath target_node; + + float duration = 0; + Tween::TransitionType transition_type = Tween::TRANS_LINEAR; + Tween::EaseType ease_type = Tween::EASE_IN; + + bool use_angle_limitation = false; + float limit_angle = 360; + float dump_power = 2.0; + + Quaternion from_q; // For time-based interpolation. + Quaternion prev_q; // For detecting flipping by angle limit. + float duration_left = 0; + + Vector3 get_basis_vector_from_bone_axis(Basis p_basis, BoneAxis p_axis); + Vector3 get_vector_from_bone_axis(BoneAxis p_axis); + Vector3 get_vector_from_axis(Vector3::Axis p_axis); + Vector2 get_projection_vector(Vector3 p_vector, Vector3::Axis p_axis); + Transform3D look_at_with_axes(Transform3D p_from, Vector3 p_target); + +protected: + void _validate_property(PropertyInfo &p_property) const; + + static void _bind_methods(); + + virtual void _process_modification() override; + +public: + void set_bone(int p_bone); + int get_bone() const; + + void set_forward_axis(BoneAxis p_axis); + BoneAxis get_forward_axis() const; + void set_primary_rotation(Vector3::Axis p_axis); + Vector3::Axis get_primary_rotation() const; + void set_use_secondary_rotation(bool p_enabled); + bool is_using_secondary_rotation() const; + + void set_target_node(NodePath p_target_node); + NodePath get_target_node() const; + + void set_duration(float p_duration); + float get_duration() const; + void set_transition_type(Tween::TransitionType p_transition_type); + Tween::TransitionType get_transition_type() const; + void set_ease_type(Tween::EaseType p_ease_type); + Tween::EaseType get_ease_type() const; + + void set_use_angle_limitation(bool p_enabled); + bool is_using_angle_limitation() const; + void set_limit_angle(float p_angle); + float get_limit_angle() const; + void set_dump_power(float p_power); + float get_dump_power() const; +}; + +VARIANT_ENUM_CAST(LookAtModifier3D::BoneAxis); + +#endif // LOOK_AT_MODIFIER_3D_H diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 6b1ce2b4ca7f..287dcfdbb799 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -243,6 +243,7 @@ #include "scene/3d/light_3d.h" #include "scene/3d/lightmap_gi.h" #include "scene/3d/lightmap_probe.h" +#include "scene/3d/look_at_modifier_3d.h" #include "scene/3d/marker_3d.h" #include "scene/3d/mesh_instance_3d.h" #include "scene/3d/multimesh_instance_3d.h" @@ -608,6 +609,7 @@ void register_scene_types() { GDREGISTER_CLASS(SkeletonIK3D); GDREGISTER_CLASS(BoneAttachment3D); + GDREGISTER_CLASS(LookAtModifier3D); GDREGISTER_CLASS(VehicleBody3D); GDREGISTER_CLASS(VehicleWheel3D);