Skip to content

Commit

Permalink
Backported from godot4: Add the ability to look-at in model-space.
Browse files Browse the repository at this point in the history
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

godotengine/godot@5fdc123

Also bound the new Basis helper methods.
  • Loading branch information
Relintai committed Sep 30, 2024
1 parent 422314a commit 951ae7b
Show file tree
Hide file tree
Showing 11 changed files with 98 additions and 41 deletions.
29 changes: 19 additions & 10 deletions core/math/basis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.");
Expand All @@ -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) {
return Basis::create_looking_at(p_target, p_up);
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);
}
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.");
Expand All @@ -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);
}
Expand Down
9 changes: 4 additions & 5 deletions core/math/basis.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_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));
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 from_scale(const Vector3 &p_scale);

operator Quaternion() const { return get_quaternion(); }
Expand Down
8 changes: 4 additions & 4 deletions core/math/transform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
4 changes: 2 additions & 2 deletions core/math/transform.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
15 changes: 13 additions & 2 deletions core/variant/variant_call.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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());

Expand All @@ -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());
Expand Down Expand Up @@ -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);
Expand Down
19 changes: 19 additions & 0 deletions doc/classes/Basis.xml
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,25 @@
<description>
</description>
</method>
<method name="looking_at">
<return type="Basis" />
<argument index="0" name="target" type="Vector3" />
<argument index="1" name="up" type="Vector3" default="Vector3(0, 1, 0)" />
<argument index="2" name="use_model_front" type="bool" default="false" />
<description>
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.
</description>
</method>
<method name="set_look_at">
<argument index="0" name="target" type="Vector3" />
<argument index="1" name="up" type="Vector3" default="Vector3(0, 1, 0)" />
<argument index="2" name="use_model_front" type="bool" default="false" />
<description>
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.
</description>
</method>
<method name="orthogonalize">
<description>
</description>
Expand Down
8 changes: 5 additions & 3 deletions doc/classes/Spatial.xml
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,10 @@
<method name="look_at">
<return type="void" />
<argument index="0" name="target" type="Vector3" />
<argument index="1" name="up" type="Vector3" />
<argument index="1" name="up" type="Vector3" default="Vector3(0, 1, 0)" />
<argument index="2" name="use_model_front" type="bool" default="false" />
<description>
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.
Expand All @@ -137,7 +138,8 @@
<return type="void" />
<argument index="0" name="position" type="Vector3" />
<argument index="1" name="target" type="Vector3" />
<argument index="2" name="up" type="Vector3" />
<argument index="2" name="up" type="Vector3" default="Vector3(0, 1, 0)" />
<argument index="3" name="use_model_front" type="bool" default="false" />
<description>
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.
</description>
Expand Down
3 changes: 2 additions & 1 deletion doc/classes/Transform.xml
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,9 @@
<return type="Transform" />
<argument index="0" name="target" type="Vector3" />
<argument index="1" name="up" type="Vector3" />
<argument index="2" name="use_model_front" type="bool" default="false" />
<description>
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.
</description>
Expand Down
20 changes: 19 additions & 1 deletion doc/classes/Vector3.xml
Original file line number Diff line number Diff line change
Expand Up @@ -389,10 +389,28 @@
Down unit vector.
</constant>
<constant name="FORWARD" value="Vector3( 0, 0, -1 )">
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.
</constant>
<constant name="BACK" value="Vector3( 0, 0, 1 )">
Back unit vector. Represents the local direction of back, and the global direction of south.
</constant>
<constant name="MODEL_LEFT" value="Vector3(1, 0, 0)">
Unit vector pointing towards the left side of imported 3D assets.
</constant>
<constant name="MODEL_RIGHT" value="Vector3(-1, 0, 0)">
Unit vector pointing towards the right side of imported 3D assets.
</constant>
<constant name="MODEL_TOP" value="Vector3(0, 1, 0)">
Unit vector pointing towards the top side (up) of imported 3D assets.
</constant>
<constant name="MODEL_BOTTOM" value="Vector3(0, -1, 0)">
Unit vector pointing towards the bottom side (down) of imported 3D assets.
</constant>
<constant name="MODEL_FRONT" value="Vector3(0, 0, 1)">
Unit vector pointing towards the front side (facing forward) of imported 3D assets.
</constant>
<constant name="MODEL_REAR" value="Vector3(0, 0, -1)">
Unit vector pointing towards the rear side (back) of imported 3D assets.
</constant>
</constants>
</class>
20 changes: 9 additions & 11 deletions scene/main/spatial.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down Expand Up @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions scene/main/spatial.h
Original file line number Diff line number Diff line change
Expand Up @@ -237,8 +237,8 @@ class Spatial : 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);
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;
Expand Down

0 comments on commit 951ae7b

Please sign in to comment.