diff --git a/core/math/basis.cpp b/core/math/basis.cpp index bfd902c7e287..1481dbc32edd 100644 --- a/core/math/basis.cpp +++ b/core/math/basis.cpp @@ -1016,12 +1016,15 @@ void Basis::rotate_sh(real_t *p_values) { p_values[8] = d4 * s_scale_dst4; } -Basis Basis::looking_at(const Vector3 &p_target, const Vector3 &p_up) { +Basis Basis::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_zero_approx(), Basis(), "The target vector can't be zero."); ERR_FAIL_COND_V_MSG(p_up.is_zero_approx(), 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_zero_approx(), Basis(), "The target vector and up vector can't be parallel to each other."); diff --git a/core/math/basis.h b/core/math/basis.h index bbc1d404697e..1a68bee6861a 100644 --- a/core/math/basis.h +++ b/core/math/basis.h @@ -217,7 +217,7 @@ struct _NO_DISCARD_ Basis { operator Quaternion() const { return get_quaternion(); } - static Basis looking_at(const Vector3 &p_target, const Vector3 &p_up = Vector3(0, 1, 0)); + static Basis looking_at(const Vector3 &p_target, const Vector3 &p_up = Vector3(0, 1, 0), bool p_use_model_front = false); Basis(const Quaternion &p_quaternion) { set_quaternion(p_quaternion); }; Basis(const Quaternion &p_quaternion, const Vector3 &p_scale) { set_quaternion_scale(p_quaternion, p_scale); } diff --git a/core/math/transform_3d.cpp b/core/math/transform_3d.cpp index 8d497209f1df..cdc94676c9f5 100644 --- a/core/math/transform_3d.cpp +++ b/core/math/transform_3d.cpp @@ -77,20 +77,20 @@ void Transform3D::rotate_basis(const Vector3 &p_axis, real_t p_angle) { basis.rotate(p_axis, p_angle); } -Transform3D Transform3D::looking_at(const Vector3 &p_target, const Vector3 &p_up) const { +Transform3D Transform3D::looking_at(const Vector3 &p_target, const Vector3 &p_up, bool p_use_model_front) const { #ifdef MATH_CHECKS ERR_FAIL_COND_V_MSG(origin.is_equal_approx(p_target), Transform3D(), "The transform's origin and target can't be equal."); #endif Transform3D t = *this; - t.basis = Basis::looking_at(p_target - origin, p_up); + t.basis = Basis::looking_at(p_target - origin, p_up, p_use_model_front); return t; } -void Transform3D::set_look_at(const Vector3 &p_eye, const Vector3 &p_target, const Vector3 &p_up) { +void Transform3D::set_look_at(const Vector3 &p_eye, const Vector3 &p_target, const Vector3 &p_up, bool p_use_model_front) { #ifdef MATH_CHECKS ERR_FAIL_COND_MSG(p_eye.is_equal_approx(p_target), "The eye and target vectors can't be equal."); #endif - basis = Basis::looking_at(p_target - p_eye, p_up); + basis = Basis::looking_at(p_target - p_eye, p_up, p_use_model_front); origin = p_eye; } diff --git a/core/math/transform_3d.h b/core/math/transform_3d.h index bf1b4cdb6362..70141a3dbe3f 100644 --- a/core/math/transform_3d.h +++ b/core/math/transform_3d.h @@ -52,8 +52,8 @@ struct _NO_DISCARD_ Transform3D { void rotate(const Vector3 &p_axis, real_t p_angle); void rotate_basis(const Vector3 &p_axis, real_t p_angle); - void set_look_at(const Vector3 &p_eye, const Vector3 &p_target, const Vector3 &p_up = Vector3(0, 1, 0)); - Transform3D looking_at(const Vector3 &p_target, const Vector3 &p_up = Vector3(0, 1, 0)) const; + void set_look_at(const Vector3 &p_eye, const Vector3 &p_target, const Vector3 &p_up = Vector3(0, 1, 0), bool p_use_model_front = false); + Transform3D looking_at(const Vector3 &p_target, const Vector3 &p_up = Vector3(0, 1, 0), bool p_use_model_front = false) const; void scale(const Vector3 &p_scale); Transform3D scaled(const Vector3 &p_scale) const; diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index 0a836c125a35..dad91832168d 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -2101,7 +2101,7 @@ static void _register_variant_builtin_methods() { bind_method(Basis, is_equal_approx, sarray("b"), varray()); bind_method(Basis, is_finite, sarray(), varray()); bind_method(Basis, get_rotation_quaternion, sarray(), varray()); - bind_static_method(Basis, looking_at, sarray("target", "up"), varray(Vector3(0, 1, 0))); + bind_static_method(Basis, looking_at, sarray("target", "up", "use_model_front"), varray(Vector3(0, 1, 0), false)); bind_static_method(Basis, from_scale, sarray("scale"), varray()); bind_static_method(Basis, from_euler, sarray("euler", "order"), varray((int64_t)EulerOrder::YXZ)); @@ -2144,7 +2144,7 @@ static void _register_variant_builtin_methods() { bind_method(Transform3D, scaled_local, sarray("scale"), varray()); bind_method(Transform3D, translated, sarray("offset"), varray()); bind_method(Transform3D, translated_local, sarray("offset"), varray()); - bind_method(Transform3D, looking_at, sarray("target", "up"), varray(Vector3(0, 1, 0))); + bind_method(Transform3D, looking_at, sarray("target", "up", "use_model_front"), varray(Vector3(0, 1, 0), false)); bind_method(Transform3D, interpolate_with, sarray("xform", "weight"), varray()); bind_method(Transform3D, is_equal_approx, sarray("xform"), varray()); bind_method(Transform3D, is_finite, sarray(), varray()); @@ -2532,6 +2532,13 @@ static void _register_variant_builtin_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::VECTOR4, "AXIS_X", Vector4::AXIS_X); _VariantCall::add_constant(Variant::VECTOR4, "AXIS_Y", Vector4::AXIS_Y); _VariantCall::add_constant(Variant::VECTOR4, "AXIS_Z", Vector4::AXIS_Z); diff --git a/doc/classes/Basis.xml b/doc/classes/Basis.xml index 53dde5a286f8..7de626e4777c 100644 --- a/doc/classes/Basis.xml +++ b/doc/classes/Basis.xml @@ -123,6 +123,7 @@ + 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. diff --git a/doc/classes/Node3D.xml b/doc/classes/Node3D.xml index b4857bacde61..cad0e056e7b0 100644 --- a/doc/classes/Node3D.xml +++ b/doc/classes/Node3D.xml @@ -113,8 +113,9 @@ + - Rotates the node so that the local forward axis (-Z) points toward the [param target] position. + Rotates the node so that the local forward axis (-Z, [constant Vector3.FORWARD]) points toward the [param target] 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 [param up] 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 [param target] position cannot be the same as the node's position, the [param up] vector cannot be zero, and the direction from the node's position to the [param target] vector cannot be parallel to the [param up] vector. Operations take place in global space, which means that the node must be in the scene tree. @@ -125,6 +126,7 @@ + Moves the node to the specified [param position], and then rotates the node to point toward the [param target] as per [method look_at]. Operations take place in global space. diff --git a/doc/classes/Transform3D.xml b/doc/classes/Transform3D.xml index fb5c8559b661..d375838a687c 100644 --- a/doc/classes/Transform3D.xml +++ b/doc/classes/Transform3D.xml @@ -93,6 +93,7 @@ + Returns a copy of the transform rotated 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 transform is orthonormalized. The existing rotation, scale, and skew information from the original transform is discarded. The [param target] and [param up] vectors cannot be zero, cannot be parallel to each other, and are defined in global/parent space. diff --git a/doc/classes/Vector3.xml b/doc/classes/Vector3.xml index d55e31f7b7ab..511d84d24d8d 100644 --- a/doc/classes/Vector3.xml +++ b/doc/classes/Vector3.xml @@ -404,11 +404,29 @@ 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/3d/node_3d.cpp b/scene/3d/node_3d.cpp index 80289bac5251..4f2ee5a3b2d9 100644 --- a/scene/3d/node_3d.cpp +++ b/scene/3d/node_3d.cpp @@ -908,22 +908,23 @@ void Node3D::set_identity() { set_transform(Transform3D()); } -void Node3D::look_at(const Vector3 &p_target, const Vector3 &p_up) { +void Node3D::look_at(const Vector3 &p_target, const Vector3 &p_up, bool p_use_model_front) { ERR_THREAD_GUARD; ERR_FAIL_COND_MSG(!is_inside_tree(), "Node not inside tree. Use look_at_from_position() instead."); 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 Node3D::look_at_from_position(const Vector3 &p_pos, const Vector3 &p_target, const Vector3 &p_up) { +void Node3D::look_at_from_position(const Vector3 &p_pos, const Vector3 &p_target, const Vector3 &p_up, bool p_use_model_front) { ERR_THREAD_GUARD; ERR_FAIL_COND_MSG(p_pos.is_equal_approx(p_target), "Node origin and target are in the same position, look_at() failed."); ERR_FAIL_COND_MSG(p_up.is_zero_approx(), "The up vector can't be zero, look_at() failed."); ERR_FAIL_COND_MSG(p_up.cross(p_target - p_pos).is_zero_approx(), "Up vector and direction between node origin and target are aligned, look_at() failed."); - Transform3D lookat = Transform3D(Basis::looking_at(p_target - p_pos, p_up), p_pos); + Vector3 forward = p_target - p_pos; + Basis lookat_basis = Basis::looking_at(forward, p_up, p_use_model_front); Vector3 original_scale = get_scale(); - set_global_transform(lookat); + set_global_transform(Transform3D(lookat_basis, p_pos)); set_scale(original_scale); } @@ -1166,8 +1167,8 @@ void Node3D::_bind_methods() { ClassDB::bind_method(D_METHOD("orthonormalize"), &Node3D::orthonormalize); ClassDB::bind_method(D_METHOD("set_identity"), &Node3D::set_identity); - ClassDB::bind_method(D_METHOD("look_at", "target", "up"), &Node3D::look_at, DEFVAL(Vector3(0, 1, 0))); - ClassDB::bind_method(D_METHOD("look_at_from_position", "position", "target", "up"), &Node3D::look_at_from_position, DEFVAL(Vector3(0, 1, 0))); + ClassDB::bind_method(D_METHOD("look_at", "target", "up", "use_model_front"), &Node3D::look_at, DEFVAL(Vector3(0, 1, 0)), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("look_at_from_position", "position", "target", "up", "use_model_front"), &Node3D::look_at_from_position, DEFVAL(Vector3(0, 1, 0)), DEFVAL(false)); ClassDB::bind_method(D_METHOD("to_local", "global_point"), &Node3D::to_local); ClassDB::bind_method(D_METHOD("to_global", "local_point"), &Node3D::to_global); diff --git a/scene/3d/node_3d.h b/scene/3d/node_3d.h index b274a6af8808..935f0b149a3b 100644 --- a/scene/3d/node_3d.h +++ b/scene/3d/node_3d.h @@ -250,8 +250,8 @@ class Node3D : public Node { 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 = Vector3(0, 1, 0)); - void look_at_from_position(const Vector3 &p_pos, const Vector3 &p_target, const Vector3 &p_up = Vector3(0, 1, 0)); + void look_at(const Vector3 &p_target, const Vector3 &p_up = Vector3(0, 1, 0), bool p_use_model_front = false); + void look_at_from_position(const Vector3 &p_pos, const Vector3 &p_target, const Vector3 &p_up = Vector3(0, 1, 0), bool p_use_model_front = false); Vector3 to_local(Vector3 p_global) const; Vector3 to_global(Vector3 p_local) const;