From 951ae7b11db0376dd0dbdf080e2f2e3652ac33ed Mon Sep 17 00:00:00 2001 From: Relintai Date: Mon, 30 Sep 2024 17:04:00 +0200 Subject: [PATCH] Backported from godot4: Add the ability to look-at in model-space. This is a much simpler attempt to solve the same problem as #76060, but without breaking any compatibility. * Adds a description of what model space is in the Vector3 enums (MODEL_* constants). This has the proper axes laid out for imported 3D assets. * Adds the option to `look_at` using model_space, which uses Vector3.MODEL_FRONT as forward vector. The attempt of this PR is to still break the assumption that there is a single direction of forward (which is not the case in Godot) and make it easier to understand where 3D models are facing, as well as orienting them via look_at. - reduz https://github.com/godotengine/godot/commit/5fdc1232eff45e31ee53f58e618de6c58d3f7203 Also bound the new Basis helper methods. --- core/math/basis.cpp | 29 +++++++++++++++++++---------- core/math/basis.h | 9 ++++----- core/math/transform.cpp | 8 ++++---- core/math/transform.h | 4 ++-- core/variant/variant_call.cpp | 15 +++++++++++++-- doc/classes/Basis.xml | 19 +++++++++++++++++++ doc/classes/Spatial.xml | 8 +++++--- doc/classes/Transform.xml | 3 ++- doc/classes/Vector3.xml | 20 +++++++++++++++++++- scene/main/spatial.cpp | 20 +++++++++----------- scene/main/spatial.h | 4 ++-- 11 files changed, 98 insertions(+), 41 deletions(-) diff --git a/core/math/basis.cpp b/core/math/basis.cpp index 5401d2da0..1089ac5b7 100644 --- a/core/math/basis.cpp +++ b/core/math/basis.cpp @@ -217,12 +217,17 @@ Basis Basis::transposed() const { return tr; } -Basis Basis::create_looking_at(const Vector3 &p_target, const Vector3 &p_up) { +Basis Basis::create_looking_at(const Vector3 &p_target, const Vector3 &p_up, bool p_use_model_front) { #ifdef MATH_CHECKS ERR_FAIL_COND_V_MSG(p_target.is_equal_approx(Vector3()), Basis(), "The target vector can't be zero."); ERR_FAIL_COND_V_MSG(p_up.is_equal_approx(Vector3()), Basis(), "The up vector can't be zero."); #endif - Vector3 v_z = -p_target.normalized(); + Vector3 v_z = p_target.normalized(); + + if (!p_use_model_front) { + v_z = -v_z; + } + Vector3 v_x = p_up.cross(v_z); #ifdef MATH_CHECKS ERR_FAIL_COND_V_MSG(v_x.is_equal_approx(Vector3()), Basis(), "The target vector and up vector can't be parallel to each other."); @@ -235,19 +240,20 @@ Basis Basis::create_looking_at(const Vector3 &p_target, const Vector3 &p_up) { return basis; } -Basis Basis::create_from_scale(const Vector3 &p_scale) { - return Basis(p_scale.x, 0, 0, 0, p_scale.y, 0, 0, 0, p_scale.z); +Basis Basis::looking_at(const Vector3 &p_target, const Vector3 &p_up, bool p_use_model_front) { + return Basis::create_looking_at(p_target, p_up, p_use_model_front); } - -Basis Basis::looking_at(const Vector3 &p_target, const Vector3 &p_up) { - return Basis::create_looking_at(p_target, p_up); -} -void Basis::set_look_at(const Vector3 &p_target, const Vector3 &p_up) { +void Basis::set_look_at(const Vector3 &p_target, const Vector3 &p_up, bool p_use_model_front) { #ifdef MATH_CHECKS ERR_FAIL_COND_MSG(p_target.is_equal_approx(Vector3()), "The target vector can't be zero."); ERR_FAIL_COND_MSG(p_up.is_equal_approx(Vector3()), "The up vector can't be zero."); #endif - Vector3 v_z = -p_target.normalized(); + Vector3 v_z = p_target.normalized(); + + if (!p_use_model_front) { + v_z = -v_z; + } + Vector3 v_x = p_up.cross(v_z); #ifdef MATH_CHECKS ERR_FAIL_COND_MSG(v_x.is_equal_approx(Vector3()), "The target vector and up vector can't be parallel to each other."); @@ -258,6 +264,9 @@ void Basis::set_look_at(const Vector3 &p_target, const Vector3 &p_up) { set_columns(v_x, v_y, v_z); } +Basis Basis::create_from_scale(const Vector3 &p_scale) { + return Basis(p_scale.x, 0, 0, 0, p_scale.y, 0, 0, 0, p_scale.z); +} Basis Basis::from_scale(const Vector3 &p_scale) { return Basis::create_from_scale(p_scale); } diff --git a/core/math/basis.h b/core/math/basis.h index 9c32b7fb4..730187ff1 100644 --- a/core/math/basis.h +++ b/core/math/basis.h @@ -286,12 +286,11 @@ struct _NO_DISCARD_CLASS_ Basis { // only be used in cases of single normals, or when the basis changes each time. Vector3 xform_normal(const Vector3 &p_vector) const { return get_normal_xform_basis().xform_normal_fast(p_vector); } - static Basis create_looking_at(const Vector3 &p_target, const Vector3 &p_up = Vector3(0, 1, 0)); + static Basis create_looking_at(const Vector3 &p_target, const Vector3 &p_up = Vector3(0, 1, 0), bool p_use_model_front = false); + Basis looking_at(const Vector3 &p_target, const Vector3 &p_up = Vector3(0, 1, 0), bool p_use_model_front = false); + void set_look_at(const Vector3 &p_target, const Vector3 &p_up = Vector3(0, 1, 0), bool p_use_model_front = false); + static Basis create_from_scale(const Vector3 &p_scale); - - Basis looking_at(const Vector3 &p_target, const Vector3 &p_up = Vector3(0, 1, 0)); - void set_look_at(const Vector3 &p_target, const Vector3 &p_up = Vector3(0, 1, 0)); - Basis from_scale(const Vector3 &p_scale); operator Quaternion() const { return get_quaternion(); } diff --git a/core/math/transform.cpp b/core/math/transform.cpp index ec9d69431..f3f3e87a3 100644 --- a/core/math/transform.cpp +++ b/core/math/transform.cpp @@ -82,14 +82,14 @@ void Transform::rotate_basis(const Vector3 &p_axis, real_t p_phi) { basis.rotate(p_axis, p_phi); } -void Transform::set_look_at(const Vector3 &p_eye, const Vector3 &p_target, const Vector3 &p_up) { - basis = Basis::create_looking_at(p_target - p_eye, p_up); +void Transform::set_look_at(const Vector3 &p_eye, const Vector3 &p_target, const Vector3 &p_up, bool p_use_model_front) { + basis = Basis::create_looking_at(p_target - p_eye, p_up, p_use_model_front); origin = p_eye; } -Transform Transform::looking_at(const Vector3 &p_target, const Vector3 &p_up) const { +Transform Transform::looking_at(const Vector3 &p_target, const Vector3 &p_up, bool p_use_model_front) const { Transform t = *this; - t.set_look_at(origin, p_target, p_up); + t.set_look_at(origin, p_target, p_up,p_use_model_front); return t; } diff --git a/core/math/transform.h b/core/math/transform.h index 135b207b4..28b86ef7a 100644 --- a/core/math/transform.h +++ b/core/math/transform.h @@ -55,8 +55,8 @@ struct _NO_DISCARD_CLASS_ Transform { void rotate_local(const Vector3 &p_axis, real_t p_phi); void rotate_basis(const Vector3 &p_axis, real_t p_phi); - void set_look_at(const Vector3 &p_eye, const Vector3 &p_target, const Vector3 &p_up); - Transform looking_at(const Vector3 &p_target, const Vector3 &p_up) const; + void set_look_at(const Vector3 &p_eye, const Vector3 &p_target, const Vector3 &p_up, bool p_use_model_front = false); + Transform looking_at(const Vector3 &p_target, const Vector3 &p_up, bool p_use_model_front = false) const; void scale(const Vector3 &p_scale); Transform scaled(const Vector3 &p_scale) const; diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index 79f625c36..49b3c8c28 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -1541,6 +1541,8 @@ struct _VariantCall { VCALL_PTR0R(Basis, orthogonalized); VCALL_PTR0R(Basis, is_symmetric); VCALL_PTR0R(Basis, diagonalize); + VCALL_PTR3R(Basis, looking_at); + VCALL_PTR3(Basis, set_look_at); static void _call_Basis_xform(Variant &r_ret, Variant &p_self, const Variant **p_args) { switch (p_args[0]->type) { @@ -3375,6 +3377,8 @@ void register_variant_methods() { ADDFUNC0R(BASIS, BASIS, Basis, orthogonalized, varray()); ADDFUNC0R(BASIS, BOOL, Basis, is_symmetric, varray()); ADDFUNC0R(BASIS, BASIS, Basis, diagonalize, varray()); + ADDFUNC3R(BASIS, BASIS, Basis, looking_at, VECTOR3, "target", VECTOR3, "up", BOOL, "use_model_front", varray(Vector3(0, 1, 0), false)); + ADDFUNC3(BASIS, NIL, Basis, set_look_at, VECTOR3, "target", VECTOR3, "up", BOOL, "use_model_front", varray(Vector3(0, 1, 0), false)); ADDFUNC1R(BASIS, VECTOR3, Basis, xform, NIL, "v3_or_v3i", varray()); ADDFUNC1R(BASIS, VECTOR3, Basis, xform_inv, NIL, "v3_or_v3i", varray()); @@ -3387,8 +3391,8 @@ void register_variant_methods() { ADDFUNC2(TRANSFORM, NIL, Transform, rotate, VECTOR3, "axis", REAL, "phi", varray()); ADDFUNC2(TRANSFORM, NIL, Transform, rotate_local, VECTOR3, "axis", REAL, "phi", varray()); ADDFUNC2(TRANSFORM, NIL, Transform, rotate_basis, VECTOR3, "axis", REAL, "phi", varray()); - ADDFUNC3(TRANSFORM, NIL, Transform, set_look_at, VECTOR3, "eye", VECTOR3, "target", VECTOR3, "up", varray()); - ADDFUNC2R(TRANSFORM, TRANSFORM, Transform, looking_at, VECTOR3, "target", VECTOR3, "up", varray()); + ADDFUNC4(TRANSFORM, NIL, Transform, set_look_at, VECTOR3, "eye", VECTOR3, "target", VECTOR3, "up", BOOL, "use_model_front", varray(false)); + ADDFUNC3R(TRANSFORM, TRANSFORM, Transform, looking_at, VECTOR3, "target", VECTOR3, "up", BOOL, "use_model_front", varray(false)); ADDFUNC1(TRANSFORM, NIL, Transform, scale, VECTOR3, "scale", varray()); ADDFUNC1R(TRANSFORM, TRANSFORM, Transform, scaled, VECTOR3, "scale", varray()); ADDFUNC1R(TRANSFORM, TRANSFORM, Transform, scaled_local, VECTOR3, "scale", varray()); @@ -3530,6 +3534,13 @@ void register_variant_methods() { _VariantCall::add_variant_constant(Variant::VECTOR3, "FORWARD", Vector3(0, 0, -1)); _VariantCall::add_variant_constant(Variant::VECTOR3, "BACK", Vector3(0, 0, 1)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "MODEL_LEFT", Vector3(1, 0, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "MODEL_RIGHT", Vector3(-1, 0, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "MODEL_TOP", Vector3(0, 1, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "MODEL_BOTTOM", Vector3(0, -1, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "MODEL_FRONT", Vector3(0, 0, 1)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "MODEL_REAR", Vector3(0, 0, -1)); + _VariantCall::add_constant(Variant::VECTOR3I, "AXIS_X", Vector3i::AXIS_X); _VariantCall::add_constant(Variant::VECTOR3I, "AXIS_Y", Vector3i::AXIS_Y); _VariantCall::add_constant(Variant::VECTOR3I, "AXIS_Z", Vector3i::AXIS_Z); diff --git a/doc/classes/Basis.xml b/doc/classes/Basis.xml index 72bc323fa..8b764e02e 100644 --- a/doc/classes/Basis.xml +++ b/doc/classes/Basis.xml @@ -225,6 +225,25 @@ + + + + + + + Creates a Basis with a rotation such that the forward axis (-Z) points towards the [param target] position. + The up axis (+Y) points as close to the [param up] vector as possible while staying perpendicular to the forward axis. The resulting Basis is orthonormalized. The [param target] and [param up] vectors cannot be zero, and cannot be parallel to each other. + + + + + + + + Sets this Basis with a rotation such that the forward axis (-Z) points towards the [param target] position. + The up axis (+Y) points as close to the [param up] vector as possible while staying perpendicular to the forward axis. The resulting Basis is orthonormalized. The [param target] and [param up] vectors cannot be zero, and cannot be parallel to each other. + + diff --git a/doc/classes/Spatial.xml b/doc/classes/Spatial.xml index 3bb8935fa..182cd78d9 100644 --- a/doc/classes/Spatial.xml +++ b/doc/classes/Spatial.xml @@ -125,9 +125,10 @@ - + + - Rotates the node so that the local forward axis (-Z) points toward the [code]target[/code] position. + Rotates the node so that the local forward axis (-Z, [constant Vector3.FORWARD]) points toward the [code]target[/code] position. If the [param use_model_front] options is specified, then the model is oriented in reverse, towards the model front axis (+Z, [constant Vector3.MODEL_FRONT]), which is more useful for orienting 3D models. The local up axis (+Y) points as close to the [code]up[/code] vector as possible while staying perpendicular to the local forward axis. The resulting transform is orthogonal, and the scale is preserved. Non-uniform scaling may not work correctly. The [code]target[/code] position cannot be the same as the node's position, the [code]up[/code] vector cannot be zero, and the direction from the node's position to the [code]target[/code] vector cannot be parallel to the [code]up[/code] vector. Operations take place in global space. @@ -137,7 +138,8 @@ - + + Moves the node to the specified [code]position[/code], and then rotates itself to point toward the [code]target[/code] as per [method look_at]. Operations take place in global space. diff --git a/doc/classes/Transform.xml b/doc/classes/Transform.xml index 791481d54..b5653739a 100644 --- a/doc/classes/Transform.xml +++ b/doc/classes/Transform.xml @@ -104,8 +104,9 @@ + - Returns a copy of the transform rotated such that its -Z axis points towards the [code]target[/code] position. + Returns a copy of the transform rotated such that its (-Z, [constant Vector3.FORWARD]) axis points towards the [code]target[/code] position. If the [param use_model_front] options is specified, then the model is oriented in reverse, towards the model front axis (+Z, [constant Vector3.MODEL_FRONT]), which is more useful for orienting 3D models. The transform will first be rotated around the given [code]up[/code] vector, and then fully aligned to the target by a further rotation around an axis perpendicular to both the [code]target[/code] and [code]up[/code] vectors. Operations take place in global space. diff --git a/doc/classes/Vector3.xml b/doc/classes/Vector3.xml index 5afada672..c7c43145f 100644 --- a/doc/classes/Vector3.xml +++ b/doc/classes/Vector3.xml @@ -389,10 +389,28 @@ Down unit vector. - Forward unit vector. Represents the local direction of forward, and the global direction of north. + Forward unit vector. Represents the local direction of forward, and the global direction of north. Keep in mind that the forward direction for lights, cameras, etc is different from 3D assets like characters, which face towards the camera by convention. Use [constant Vector3.MODEL_FRONT] and similar constants when working in 3D asset space. Back unit vector. Represents the local direction of back, and the global direction of south. + + Unit vector pointing towards the left side of imported 3D assets. + + + Unit vector pointing towards the right side of imported 3D assets. + + + Unit vector pointing towards the top side (up) of imported 3D assets. + + + Unit vector pointing towards the bottom side (down) of imported 3D assets. + + + Unit vector pointing towards the front side (facing forward) of imported 3D assets. + + + Unit vector pointing towards the rear side (back) of imported 3D assets. + diff --git a/scene/main/spatial.cpp b/scene/main/spatial.cpp index e628fe6b2..de84516e2 100644 --- a/scene/main/spatial.cpp +++ b/scene/main/spatial.cpp @@ -900,22 +900,20 @@ void Spatial::set_identity() { set_transform(Transform()); } -void Spatial::look_at(const Vector3 &p_target, const Vector3 &p_up) { +void Spatial::look_at(const Vector3 &p_target, const Vector3 &p_up, bool p_use_model_front) { Vector3 origin(get_global_transform().origin); - look_at_from_position(origin, p_target, p_up); + look_at_from_position(origin, p_target, p_up, p_use_model_front); } -void Spatial::look_at_from_position(const Vector3 &p_pos, const Vector3 &p_target, const Vector3 &p_up) { +void Spatial::look_at_from_position(const Vector3 &p_pos, const Vector3 &p_target, const Vector3 &p_up, bool p_use_model_front) { ERR_FAIL_COND_MSG(p_pos == p_target, "Node origin and target are in the same position, look_at() failed."); ERR_FAIL_COND_MSG(p_up == Vector3(), "The up vector can't be zero, look_at() failed."); ERR_FAIL_COND_MSG(p_up.cross(p_target - p_pos) == Vector3(), "Up vector and direction between node origin and target are aligned, look_at() failed."); - Transform lookat; - lookat.origin = p_pos; - - Vector3 original_scale(get_scale()); - lookat = lookat.looking_at(p_target, p_up); - set_global_transform(lookat); + Vector3 forward = p_target - p_pos; + Basis lookat_basis = Basis::create_looking_at(forward, p_up, p_use_model_front); + Vector3 original_scale = get_scale(); + set_global_transform(Transform(lookat_basis, p_pos)); set_scale(original_scale); } @@ -1048,8 +1046,8 @@ void Spatial::_bind_methods() { ClassDB::bind_method(D_METHOD("orthonormalize"), &Spatial::orthonormalize); ClassDB::bind_method(D_METHOD("set_identity"), &Spatial::set_identity); - ClassDB::bind_method(D_METHOD("look_at", "target", "up"), &Spatial::look_at); - ClassDB::bind_method(D_METHOD("look_at_from_position", "position", "target", "up"), &Spatial::look_at_from_position); + ClassDB::bind_method(D_METHOD("look_at", "target", "up", "use_model_front"), &Spatial::look_at, DEFVAL(Vector3(0, 1, 0)), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("look_at_from_position", "position", "target", "up", "use_model_front"), &Spatial::look_at_from_position, DEFVAL(Vector3(0, 1, 0)), DEFVAL(false)); ClassDB::bind_method(D_METHOD("set_merging_mode", "mode"), &Spatial::set_merging_mode); ClassDB::bind_method(D_METHOD("get_merging_mode"), &Spatial::get_merging_mode); diff --git a/scene/main/spatial.h b/scene/main/spatial.h index 579ebe5be..84f79cbac 100644 --- a/scene/main/spatial.h +++ b/scene/main/spatial.h @@ -237,8 +237,8 @@ public: void global_scale(const Vector3 &p_scale); void global_translate(const Vector3 &p_offset); - void look_at(const Vector3 &p_target, const Vector3 &p_up); - void look_at_from_position(const Vector3 &p_pos, const Vector3 &p_target, const Vector3 &p_up); + void look_at(const Vector3 &p_target, const Vector3 &p_up, bool p_use_model_front = false); + void look_at_from_position(const Vector3 &p_pos, const Vector3 &p_target, const Vector3 &p_up, bool p_use_model_front = false); Vector3 to_local(Vector3 p_global) const; Vector3 to_global(Vector3 p_local) const;