mirror of
https://github.com/luanti-org/luanti.git
synced 2025-08-01 17:38:41 +00:00
Merge 99c4823af3
into aba2b6638e
This commit is contained in:
commit
eb464d7b96
4 changed files with 87 additions and 32 deletions
|
@ -3934,6 +3934,28 @@ The following functions provide escape sequences:
|
||||||
* Removes all color escape sequences.
|
* Removes all color escape sequences.
|
||||||
|
|
||||||
|
|
||||||
|
Coordinate System
|
||||||
|
=================
|
||||||
|
|
||||||
|
Luanti uses a **left-handed** coordinate system: Y is "up", X is "right", Z is "forward".
|
||||||
|
This is the convention used by Unity, DirectX and Irrlicht.
|
||||||
|
It means that when you're pointing in +Z direction in-game ("forward"), +X is to your right; +Y is up.
|
||||||
|
|
||||||
|
Consistently, rotation is **left-handed** as well:
|
||||||
|
When your thumb points in the direction of the rotation axis,
|
||||||
|
your curled fingers point in the direction the rotation goes.
|
||||||
|
The rotation order is XYZ: First rotation around the X-axis is applied, then Y, then Z.
|
||||||
|
(Note: As a product of rotation matrices, this will be written in reverse, so ZYX.)
|
||||||
|
|
||||||
|
Attachment and bone override rotations both use these conventions.
|
||||||
|
|
||||||
|
There is an exception, however: Object rotation (`ObjectRef:set_rotation`, `ObjectRef:get_rotation`, `automatic_rotate`)
|
||||||
|
**does not** use left-handed XYZ rotations. Instead, it uses **right-handed ZXY** rotations:
|
||||||
|
First roll (Z) is applied, then pitch (X); yaw (Y) is applied last.
|
||||||
|
|
||||||
|
See [Scratchapixel](https://www.scratchapixel.com/lessons/mathematics-physics-for-computer-graphics/geometry/coordinate-systems.html)
|
||||||
|
or [Wikipedia](https://en.wikipedia.org/wiki/Cartesian_coordinate_system#Orientation_and_handedness)
|
||||||
|
for a more detailed and pictorial explanation of these terms.
|
||||||
|
|
||||||
|
|
||||||
Spatial Vectors
|
Spatial Vectors
|
||||||
|
@ -8561,9 +8583,10 @@ child will follow movement and rotation of that bone.
|
||||||
* `acc` is a vector
|
* `acc` is a vector
|
||||||
* `get_acceleration()`: returns the acceleration, a vector
|
* `get_acceleration()`: returns the acceleration, a vector
|
||||||
* `set_rotation(rot)`
|
* `set_rotation(rot)`
|
||||||
* Sets the rotation
|
|
||||||
* `rot` is a vector (radians). X is pitch (elevation), Y is yaw (heading)
|
* `rot` is a vector (radians). X is pitch (elevation), Y is yaw (heading)
|
||||||
and Z is roll (bank).
|
and Z is roll (bank).
|
||||||
|
* Sets the **right-handed ZXY** rotation:
|
||||||
|
First roll (Z) is applied, then pitch (X); yaw (Y) is applied last.
|
||||||
* Does not reset rotation incurred through `automatic_rotate`.
|
* Does not reset rotation incurred through `automatic_rotate`.
|
||||||
Remove & re-add your objects to force a certain rotation.
|
Remove & re-add your objects to force a certain rotation.
|
||||||
* `get_rotation()`: returns the rotation, a vector (radians)
|
* `get_rotation()`: returns the rotation, a vector (radians)
|
||||||
|
@ -9475,7 +9498,7 @@ Player properties need to be saved manually.
|
||||||
-- (see node sound definition for details).
|
-- (see node sound definition for details).
|
||||||
|
|
||||||
automatic_rotate = 0,
|
automatic_rotate = 0,
|
||||||
-- Set constant rotation in radians per second, positive or negative.
|
-- Set constant right-handed rotation in radians per second, positive or negative.
|
||||||
-- Object rotates along the local Y-axis, and works with set_rotation.
|
-- Object rotates along the local Y-axis, and works with set_rotation.
|
||||||
-- Set to 0 to disable constant rotation.
|
-- Set to 0 to disable constant rotation.
|
||||||
|
|
||||||
|
|
|
@ -1144,6 +1144,8 @@ int ObjectRef::l_set_rotation(lua_State *L)
|
||||||
|
|
||||||
v3f rotation = check_v3f(L, 2) * core::RADTODEG;
|
v3f rotation = check_v3f(L, 2) * core::RADTODEG;
|
||||||
|
|
||||||
|
// Note: These angles are inverted before being applied using setPitchYawRoll,
|
||||||
|
// hence we end up with a right-handed rotation
|
||||||
entitysao->setRotation(rotation);
|
entitysao->setRotation(rotation);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
#include "irrMath.h"
|
#include "irrMath.h"
|
||||||
#include "matrix4.h"
|
#include "matrix4.h"
|
||||||
#include "irr_v3d.h"
|
#include "irr_v3d.h"
|
||||||
|
#include "util/numeric.h"
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
using matrix4 = core::matrix4;
|
using matrix4 = core::matrix4;
|
||||||
|
|
||||||
|
@ -17,8 +19,58 @@ constexpr v3f x{1, 0, 0};
|
||||||
constexpr v3f y{0, 1, 0};
|
constexpr v3f y{0, 1, 0};
|
||||||
constexpr v3f z{0, 0, 1};
|
constexpr v3f z{0, 0, 1};
|
||||||
|
|
||||||
|
constexpr f32 QUARTER_TURN = core::PI / 2;
|
||||||
|
|
||||||
|
static void LEFT_HANDED(const std::function<void(core::matrix4 &m, const v3f &rot_rad)> &f) {
|
||||||
|
SECTION("rotation is left-handed") {
|
||||||
|
SECTION("around the X-axis") {
|
||||||
|
matrix4 X;
|
||||||
|
f(X, {QUARTER_TURN, 0 , 0});
|
||||||
|
CHECK(X.transformVect(x).equals(x));
|
||||||
|
CHECK(X.transformVect(y).equals(z));
|
||||||
|
CHECK(X.transformVect(z).equals(-y));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("around the Y-axis") {
|
||||||
|
matrix4 Y;
|
||||||
|
f(Y, {0, QUARTER_TURN, 0});
|
||||||
|
CHECK(Y.transformVect(y).equals(y));
|
||||||
|
CHECK(Y.transformVect(x).equals(-z));
|
||||||
|
CHECK(Y.transformVect(z).equals(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("around the Z-axis") {
|
||||||
|
matrix4 Z;
|
||||||
|
f(Z, {0, 0, QUARTER_TURN});
|
||||||
|
CHECK(Z.transformVect(z).equals(z));
|
||||||
|
CHECK(Z.transformVect(x).equals(y));
|
||||||
|
CHECK(Z.transformVect(y).equals(-x));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("matrix4") {
|
TEST_CASE("matrix4") {
|
||||||
|
|
||||||
|
// This is in numeric.h rather than matrix4.h, but is conceptually a matrix4 method as well
|
||||||
|
SECTION("setPitchYawRollRad") {
|
||||||
|
SECTION("rotation order is YXZ (matrix notation)") {
|
||||||
|
v3f rot{1, 2, 3};
|
||||||
|
matrix4 X, Y, Z, YXZ;
|
||||||
|
setPitchYawRollRad(X, {rot.X, 0, 0});
|
||||||
|
setPitchYawRollRad(Y, {0, rot.Y, 0});
|
||||||
|
setPitchYawRollRad(Z, {0, 0, rot.Z});
|
||||||
|
setPitchYawRollRad(YXZ, rot);
|
||||||
|
CHECK(!matrix_equals(X * Y * Z, YXZ));
|
||||||
|
CHECK(!matrix_equals(X * Z * Y, YXZ));
|
||||||
|
CHECK(matrix_equals(Y * X * Z, YXZ));
|
||||||
|
CHECK(!matrix_equals(Y * Z * X, YXZ));
|
||||||
|
CHECK(!matrix_equals(Z * X * Y, YXZ));
|
||||||
|
CHECK(!matrix_equals(Z * Y * X, YXZ));
|
||||||
|
}
|
||||||
|
|
||||||
|
LEFT_HANDED(setPitchYawRollRad);
|
||||||
|
}
|
||||||
|
|
||||||
SECTION("setRotationRadians") {
|
SECTION("setRotationRadians") {
|
||||||
SECTION("rotation order is ZYX (matrix notation)") {
|
SECTION("rotation order is ZYX (matrix notation)") {
|
||||||
v3f rot{1, 2, 3};
|
v3f rot{1, 2, 3};
|
||||||
|
@ -35,36 +87,12 @@ SECTION("setRotationRadians") {
|
||||||
CHECK(matrix_equals(Z * Y * X, ZYX));
|
CHECK(matrix_equals(Z * Y * X, ZYX));
|
||||||
}
|
}
|
||||||
|
|
||||||
const f32 quarter_turn = core::PI / 2;
|
|
||||||
|
|
||||||
// See https://en.wikipedia.org/wiki/Right-hand_rule#/media/File:Cartesian_coordinate_system_handedness.svg
|
// See https://en.wikipedia.org/wiki/Right-hand_rule#/media/File:Cartesian_coordinate_system_handedness.svg
|
||||||
// for a visualization of what handedness means for rotations
|
// for a visualization of what handedness means for rotations
|
||||||
|
|
||||||
SECTION("rotation is right-handed") {
|
LEFT_HANDED([](core::matrix4 &m, const v3f &rot_rad) {
|
||||||
SECTION("rotation around the X-axis is Z-up, counter-clockwise") {
|
m.setRotationRadians(rot_rad);
|
||||||
matrix4 X;
|
});
|
||||||
X.setRotationRadians({quarter_turn, 0, 0});
|
|
||||||
CHECK(X.transformVect(x).equals(x));
|
|
||||||
CHECK(X.transformVect(y).equals(z));
|
|
||||||
CHECK(X.transformVect(z).equals(-y));
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("rotation around the Y-axis is Z-up, clockwise") {
|
|
||||||
matrix4 Y;
|
|
||||||
Y.setRotationRadians({0, quarter_turn, 0});
|
|
||||||
CHECK(Y.transformVect(y).equals(y));
|
|
||||||
CHECK(Y.transformVect(x).equals(-z));
|
|
||||||
CHECK(Y.transformVect(z).equals(x));
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("rotation around the Z-axis is Y-up, counter-clockwise") {
|
|
||||||
matrix4 Z;
|
|
||||||
Z.setRotationRadians({0, 0, quarter_turn});
|
|
||||||
CHECK(Z.transformVect(z).equals(z));
|
|
||||||
CHECK(Z.transformVect(x).equals(y));
|
|
||||||
CHECK(Z.transformVect(y).equals(-x));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("getScale") {
|
SECTION("getScale") {
|
||||||
|
|
|
@ -478,6 +478,7 @@ inline void wrappedApproachShortest(T ¤t, const T target, const T stepsize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @note Uses YXZ (matrix notation) rotation order, left-handed rotation
|
||||||
void setPitchYawRollRad(core::matrix4 &m, v3f rot);
|
void setPitchYawRollRad(core::matrix4 &m, v3f rot);
|
||||||
|
|
||||||
inline void setPitchYawRoll(core::matrix4 &m, v3f rot)
|
inline void setPitchYawRoll(core::matrix4 &m, v3f rot)
|
||||||
|
@ -485,6 +486,7 @@ inline void setPitchYawRoll(core::matrix4 &m, v3f rot)
|
||||||
setPitchYawRollRad(m, rot * core::DEGTORAD);
|
setPitchYawRollRad(m, rot * core::DEGTORAD);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @see setPitchYawRollRad
|
||||||
v3f getPitchYawRollRad(const core::matrix4 &m);
|
v3f getPitchYawRollRad(const core::matrix4 &m);
|
||||||
|
|
||||||
inline v3f getPitchYawRoll(const core::matrix4 &m)
|
inline v3f getPitchYawRoll(const core::matrix4 &m)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue