1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-06-27 16:36:03 +00:00
This commit is contained in:
Lars Mueller 2025-05-29 02:13:52 +02:00
parent 513532a93c
commit f7067644a3
8 changed files with 107 additions and 92 deletions

View file

@ -4154,19 +4154,22 @@ For example:
Rotations Rotations
========= =========
As abusing vectors of euler angles is discouraged as error-prone,
Luanti provides a proper helper class for working with 3d rotations. Luanti provides a proper helper class for working with 3d rotations.
Using vectors of euler angles instead is discouraged as it is error-prone.
You must not rely on the specific type or imprecision of the current implementation. The precision of the implementation may change (improve) in the future.
Adhering to Luanti and Irrlicht conventions, rotations use **left-handed** conventions
with a rotation order of **XYZ** (X first, then Y, then Z).
Constructors Constructors
------------ ------------
* `Rotation.identity()`: Constructs a no-op rotation. * `Rotation.identity()`: Constructs a no-op rotation.
* `Rotation.quaternion(x, y, z, w)`: * `Rotation.quaternion(x, y, z, w)`:
Constructs a rotation from a quaternion (which need not be normalized) Constructs a rotation from a quaternion (which need not be normalized).
* `Rotation.axis_angle(axis, angle)`: * `Rotation.axis_angle(axis, angle)`:
Constructs a rotation around the given axis by the given angle Constructs a rotation around the given axis by the given angle.
* `axis` is a vector, which need not be normalized * `axis` is a vector, which need not be normalized
* `angle` is in radians * `angle` is in radians
* Shorthands for rotations around the respective axes: * Shorthands for rotations around the respective axes:
@ -4175,16 +4178,15 @@ Constructors
* `Rotation.z(roll)` * `Rotation.z(roll)`
* `Rotation.euler_angles(pitch, yaw, roll)` * `Rotation.euler_angles(pitch, yaw, roll)`
* All angles in radians. * All angles in radians.
* Rotation order is ZYX: First pitch is applied, then yaw, then roll. Equivalent to * Mathematically equivalent to `Rotation.compose(Rotation.z(roll), Rotation.y(yaw), Rotation.x(pitch))`.
`Rotation.compose(Rotation.z(roll), Rotation.y(yaw), Rotation.x(pitch))`. * Consistent with the euler angles that can be used for bones or attachments.
* Consistent with the euler angles that can be used for bones.
Conversions Conversions
----------- -----------
Corresponding to the constructors, quaternions can be converted Corresponding to the constructors, rotations can be converted
to different representations; note that you need not get the same values out - to different representations; note that you need not get the same values out -
you merely get values that produce the same rotation when passed to the corresponding constructor: you merely get values that produce a (roughly) equivalent rotation when passed to the corresponding constructor:
* `x, y, z, w = Rotation:to_quaternion()` * `x, y, z, w = Rotation:to_quaternion()`
* Returns the normalized quaternion representation. * Returns the normalized quaternion representation.
@ -4194,8 +4196,8 @@ you merely get values that produce the same rotation when passed to the correspo
* `pitch, yaw, roll = Rotation:to_euler_angles()` * `pitch, yaw, roll = Rotation:to_euler_angles()`
* Angles are all in radians. * Angles are all in radians.
* `pitch`, `yaw`, `roll`: Rotation around the X-, Y-, and Z-axis respectively. * `pitch`, `yaw`, `roll`: Rotation around the X-, Y-, and Z-axis respectively.
* Rotation order is ZYX: First pitch is applied, then yaw, then roll.
* Coordinate system is right-handed <!-- TODO --> Rotations can also be converted to matrices using `Matrix4.rotation(rot)`.
Methods Methods
------- -------
@ -4212,11 +4214,14 @@ Methods
* `Rotation:angle_to(other)`: Returns the absolute angle between two quaternions. * `Rotation:angle_to(other)`: Returns the absolute angle between two quaternions.
* Useful to measure similarity. * Useful to measure similarity.
Rotations implement `__tostring`. The format is only intended for human-readability,
not serialization, and may thus change.
Matrices Matrices
======== ========
Luanti uses 4x4 matrices to represent transformations of 3d vectors. Luanti uses 4x4 matrices to represent transformations of 3d vectors (embedded into 4d space).
The matrices use row-major conventions: The matrices use row-major conventions:
The first row is the image of the vector (1, 0, 0, 0), The first row is the image of the vector (1, 0, 0, 0),
the second row is the image of (0, 1, 0, 0), and so on. the second row is the image of (0, 1, 0, 0), and so on.
@ -4231,6 +4236,8 @@ Matrices are very suitable for constructing, composing and applying
linear transformations; they are not so useful for exact storage of transformations, linear transformations; they are not so useful for exact storage of transformations,
decomposition into rotation and scale will not be exact. decomposition into rotation and scale will not be exact.
Row and column indices range from `1` to `4`.
Constructors Constructors
------------ ------------
@ -4250,22 +4257,19 @@ Methods
Storage: Storage:
* `Matrix4:get(row, col)`: Get an entry. * `Matrix4:get(row, col)`
* `row` and `col` range from 1 to 4 * `Matrix4:set(row, col, number)`
* `Matrix4:set(row, col, element)`: Set an entry.
* `row` and `col` range from 1 to 4
* `x, y, z, w = Matrix4:get_row(row)` * `x, y, z, w = Matrix4:get_row(row)`
* `Matrix4:set_row(row, x, y, z, w)` * `Matrix4:set_row(row, x, y, z, w)`
* `x, y, z, w = Matrix4:get_column(col)` * `x, y, z, w = Matrix4:get_column(col)`
* `Matrix4:set_column(col, x, y, z, w)` * `Matrix4:set_column(col, x, y, z, w)`
* `Matrix4:copy()`: Copy the matrix. * `Matrix4:copy()`
* `... = Matrix4:unpack()`: Get the entries of the matrix in row-major order. * `... = Matrix4:unpack()`: Get the 16 numbers in the matrix in row-major order
(inverse of `Matrix4.new`).
Linear algebra: Linear algebra:
* `Matrix4.compose(...)`: Returns the composition of the given matrices. * Vector transformations:
* `Matrix4:transpose()`: Returns the transpose of the matrix.
* `Matrix4:invert()`: Returns the inverse, or `nil` if the matrix is (close to being) singular.
* `x, y, z, w = Matrix4:transform_4d(x, y, z, w)`: Apply the matrix to a 4d vector. * `x, y, z, w = Matrix4:transform_4d(x, y, z, w)`: Apply the matrix to a 4d vector.
* `Matrix4:transform_position(pos)`: * `Matrix4:transform_position(pos)`:
* Apply the matrix to a vector representing a position. * Apply the matrix to a vector representing a position.
@ -4273,8 +4277,14 @@ Linear algebra:
* `Matrix4:transform_direction(dir)`: * `Matrix4:transform_direction(dir)`:
* Apply the matrix to a vector representing a direction. * Apply the matrix to a vector representing a direction.
* Ignores the fourth row and column; does not apply the translation (w = 0). * Ignores the fourth row and column; does not apply the translation (w = 0).
* `Matrix4.compose(...)`: Returns the composition of the given matrices.
If `...` is empty, this is just the identity.
* `Matrix4:determinant()`: Returns the determinant.
* `Matrix4:invert()`: Returns a newly created inverse, or `nil` if the matrix is (close to being) singular.
* `Matrix4:transpose()`: Returns a transposed copy of the matrix.
* `Matrix4:equals(other, [tolerance = 0])`: * `Matrix4:equals(other, [tolerance = 0])`:
Returns whether all components differ in absolute value at most by the given tolerance. Returns whether all components differ in absolute value at most by the given tolerance.
* `m1 == m2`: Returns whether `m1` and `m2` are identical (`tolerance = 0`).
* `Matrix4:is_affine_transform([tolerance = 0])`: * `Matrix4:is_affine_transform([tolerance = 0])`:
Whether the matrix is an affine transformation in 3d space, Whether the matrix is an affine transformation in 3d space,
meaning it is a 3d linear transformation plus a translation. meaning it is a 3d linear transformation plus a translation.
@ -4282,36 +4292,33 @@ Linear algebra:
For working with affine transforms, the following methods are available: For working with affine transforms, the following methods are available:
* `Matrix4:get_translation()`: * `Matrix4:get_translation()`: Returns the translation as a vector.
Returns the translation as a vector. * `Matrix4:set_translation(vec)`: Sets (overwrites) the translation in the last row.
* `Matrix4:set_translation(vec)`
For TRS transforms specifically, For TRS transforms specifically,
let `self = Matrix4.compose(Matrix4.translation(t), Matrix4.rotation(r), Matrix4.scale(s))`. let `self = Matrix4.compose(Matrix4.translation(t), Matrix4.rotation(r), Matrix4.scale(s))`.
Then we can decompose `self` further. Note that `self` must not shear or reflect. Then we can decompose `self` further. Note that `self` must not shear or reflect.
* `rotation, scale = Matrix4:get_rs()`: * `rotation, scale = Matrix4:get_rs()`:
Extracts a `Rotation` equivalent to `r`. Extracts a `Rotation` equivalent to `r`,
along with the corresponding component-wise scaling factors as a vector. along with the corresponding component-wise scaling factors as a vector.
Operators Operators
--------- ---------
Similar to vectors, matrices define some arithmetic operators: Similar to vectors, matrices define some element-wise arithmetic operators:
* `m1 == m2`: Returns whether `m1` and `m2` are identical.
* `-m`: Returns the additive inverse.
* `m1 + m2`: Returns the sum of both matrices. * `m1 + m2`: Returns the sum of both matrices.
* `m1 - m2`: Shorthand for `m1 + (-m2)`. * `m1 - m2`: Shorthand for `m1 + (-m2)`.
* `-m`: Returns the additive inverse.
* `m * s` or `s * m`: Returns the matrix `m` scaled by the scalar `s`. * `m * s` or `s * m`: Returns the matrix `m` scaled by the scalar `s`.
* Note: *All* entries are scaled, including the last row. * Note: *All* entries are scaled, including the last column:
The matrix may not be an affine transform afterwards.
Matrices also define a `__tostring` metamethod. Matrices also define a `__tostring` metamethod.
This is only intended for human readability and not for serialization. This is only intended for human readability and not for serialization.
Helper functions Helper functions
================ ================

View file

@ -1663,6 +1663,7 @@ inline void CMatrix4<T>::getTransposed(CMatrix4<T> &o) const
template <class T> template <class T>
std::ostream& operator<<(std::ostream& os, const CMatrix4<T>& matrix) std::ostream& operator<<(std::ostream& os, const CMatrix4<T>& matrix)
{ {
os << "(\n";
for (int row = 0; row < 4; ++row) { for (int row = 0; row < 4; ++row) {
for (int col = 0; col < 4; ++col) { for (int col = 0; col < 4; ++col) {
os << "\t"; os << "\t";
@ -1670,6 +1671,7 @@ std::ostream& operator<<(std::ostream& os, const CMatrix4<T>& matrix)
} }
os << "\n"; os << "\n";
} }
os << ")";
return os; return os;
} }

View file

@ -9,6 +9,8 @@
#include "matrix4.h" #include "matrix4.h"
#include "vector3d.h" #include "vector3d.h"
#include <ostream>
// NOTE: You *only* need this when updating an application from Irrlicht before 1.8 to Irrlicht 1.8 or later. // NOTE: You *only* need this when updating an application from Irrlicht before 1.8 to Irrlicht 1.8 or later.
// Between Irrlicht 1.7 and Irrlicht 1.8 the quaternion-matrix conversions changed. // Between Irrlicht 1.7 and Irrlicht 1.8 the quaternion-matrix conversions changed.
// Before the fix they had mixed left- and right-handed rotations. // Before the fix they had mixed left- and right-handed rotations.
@ -215,6 +217,12 @@ public:
f32 W; // real part f32 W; // real part
}; };
std::ostream& operator<<(std::ostream& os, const quaternion& q)
{
os << q.X << "\t" << q.Y << "\t" << q.Z << "\t" << q.W;
return os;
}
// Constructor which converts Euler angles to a quaternion // Constructor which converts Euler angles to a quaternion
inline quaternion::quaternion(f32 x, f32 y, f32 z) inline quaternion::quaternion(f32 x, f32 y, f32 z)
{ {

View file

@ -13,7 +13,6 @@ extern "C" {
#include <irr_v3d.h> #include <irr_v3d.h>
#include <string_view> #include <string_view>
#include "c_converter.h" #include "c_converter.h"
#include "c_types.h"
/* /*
* Read template functions * Read template functions
@ -50,12 +49,12 @@ f64 LuaHelper::readParam(lua_State *L, int index)
} }
template <> template <>
LuaHelper::Finite<f32> LuaHelper::readParam(lua_State *L, int index) f32 LuaHelper::readFiniteParam(lua_State *L, int index)
{ {
f64 original_value = luaL_checknumber(L, index); f64 original_value = luaL_checknumber(L, index);
f32 v = static_cast<f32>(original_value); f32 v = static_cast<f32>(original_value);
if (std::isfinite(v)) if (std::isfinite(v))
return {v}; return v;
if (std::isnan(original_value)) if (std::isnan(original_value))
luaL_argerror(L, index, "number is NaN"); luaL_argerror(L, index, "number is NaN");
if (!std::isfinite(original_value)) if (!std::isfinite(original_value))
@ -66,11 +65,11 @@ LuaHelper::Finite<f32> LuaHelper::readParam(lua_State *L, int index)
} }
template <> template <>
LuaHelper::Finite<f64> LuaHelper::readParam(lua_State *L, int index) f64 LuaHelper::readFiniteParam(lua_State *L, int index)
{ {
f64 v = luaL_checknumber(L, index); f64 v = luaL_checknumber(L, index);
if (std::isfinite(v)) if (std::isfinite(v))
return {v}; return v;
if (std::isnan(v)) if (std::isnan(v))
luaL_argerror(L, index, "number is NaN"); luaL_argerror(L, index, "number is NaN");
luaL_argerror(L, index, "number is not finite"); luaL_argerror(L, index, "number is not finite");

View file

@ -4,7 +4,6 @@
#pragma once #pragma once
#include <cmath>
#include <string_view> #include <string_view>
extern "C" { extern "C" {
@ -25,11 +24,12 @@ protected:
template <typename T> template <typename T>
static T readParam(lua_State *L, int index); static T readParam(lua_State *L, int index);
/// Type to represent a restriction to finite floats /**
* @brief Read a value, but restrict to finite floats.
* @see readParam
*/
template <typename T> template <typename T>
struct Finite { static T readFiniteParam(lua_State *L, int index);
T value;
};
/** /**
* Read a value using a template type T from Lua state L at index * Read a value using a template type T from Lua state L at index

View file

@ -14,19 +14,18 @@
#include "quaternion.h" #include "quaternion.h"
#include <cmath> #include <cmath>
#include <cstring>
#include <lauxlib.h> #include <lauxlib.h>
#include <lua.h> #include <lua.h>
#include <luajit-2.1/lauxlib.h> #include <luajit-2.1/lauxlib.h>
#include <sstream> #include <sstream>
template<int max = 4> template<int MAX>
static int read_index(lua_State *L, int index) int LuaMatrix4::readIndex(lua_State *L, int index)
{ {
f64 value = luaL_checknumber(L, index); f64 value = readParam<f64>(L, index);
if (std::floor(value) != value) if (std::floor(value) != value)
luaL_argerror(L, index, "index must be integer"); luaL_argerror(L, index, "index must be integer");
if (value < 1 || value > max) if (value < 1 || value > MAX)
luaL_argerror(L, index, "index out of range"); luaL_argerror(L, index, "index out of range");
return static_cast<int>(value) - 1; return static_cast<int>(value) - 1;
} }
@ -52,7 +51,7 @@ int LuaMatrix4::l_identity(lua_State *L)
int LuaMatrix4::l_all(lua_State *L) int LuaMatrix4::l_all(lua_State *L)
{ {
f32 v = luaL_checknumber(L, 1); f32 v = readParam<f32>(L, 1);
create(L) = v; create(L) = v;
return 1; return 1;
} }
@ -63,7 +62,7 @@ int LuaMatrix4::l_new(lua_State *L)
luaL_error(L, "expected 16 arguments"); luaL_error(L, "expected 16 arguments");
core::matrix4 &matrix = create(L); core::matrix4 &matrix = create(L);
for (int i = 0; i < 16; ++i) for (int i = 0; i < 16; ++i)
matrix[i] = luaL_checknumber(L, 1 + i); matrix[i] = readParam<f32>(L, 1 + i);
return 1; return 1;
} }
@ -118,8 +117,8 @@ int LuaMatrix4::l_reflection(lua_State *L)
int LuaMatrix4::l_get(lua_State *L) int LuaMatrix4::l_get(lua_State *L)
{ {
const auto &matrix = check(L, 1); const auto &matrix = check(L, 1);
int row = read_index(L, 2); int row = readIndex(L, 2);
int col = read_index(L, 3); int col = readIndex(L, 3);
lua_pushnumber(L, matrix(row, col)); lua_pushnumber(L, matrix(row, col));
return 1; return 1;
} }
@ -127,9 +126,9 @@ int LuaMatrix4::l_get(lua_State *L)
int LuaMatrix4::l_set(lua_State *L) int LuaMatrix4::l_set(lua_State *L)
{ {
auto &matrix = check(L, 1); auto &matrix = check(L, 1);
int row = read_index(L, 2); int row = readIndex(L, 2);
int col = read_index(L, 3); int col = readIndex(L, 3);
f64 value = luaL_checknumber(L, 4); f64 value = readParam<f64>(L, 4);
matrix(row, col) = value; matrix(row, col) = value;
return 0; return 0;
} }
@ -137,7 +136,7 @@ int LuaMatrix4::l_set(lua_State *L)
int LuaMatrix4::l_get_row(lua_State *L) int LuaMatrix4::l_get_row(lua_State *L)
{ {
const auto &matrix = check(L, 1); const auto &matrix = check(L, 1);
int row = read_index(L, 2); int row = readIndex(L, 2);
for (int col = 0; col < 4; ++col) for (int col = 0; col < 4; ++col)
lua_pushnumber(L, matrix(row, col)); lua_pushnumber(L, matrix(row, col));
return 4; return 4;
@ -146,11 +145,11 @@ int LuaMatrix4::l_get_row(lua_State *L)
int LuaMatrix4::l_set_row(lua_State *L) int LuaMatrix4::l_set_row(lua_State *L)
{ {
auto &matrix = check(L, 1); auto &matrix = check(L, 1);
int row = read_index(L, 2); int row = readIndex(L, 2);
f32 x = luaL_checknumber(L, 3); f32 x = readParam<f32>(L, 3);
f32 y = luaL_checknumber(L, 4); f32 y = readParam<f32>(L, 4);
f32 z = luaL_checknumber(L, 5); f32 z = readParam<f32>(L, 5);
f32 w = luaL_checknumber(L, 6); f32 w = readParam<f32>(L, 6);
matrix(row, 0) = x; matrix(row, 0) = x;
matrix(row, 1) = y; matrix(row, 1) = y;
matrix(row, 2) = z; matrix(row, 2) = z;
@ -161,7 +160,7 @@ int LuaMatrix4::l_set_row(lua_State *L)
int LuaMatrix4::l_get_column(lua_State *L) int LuaMatrix4::l_get_column(lua_State *L)
{ {
const auto &matrix = check(L, 1); const auto &matrix = check(L, 1);
int col = read_index(L, 2); int col = readIndex(L, 2);
for (int row = 0; row < 4; ++row) for (int row = 0; row < 4; ++row)
lua_pushnumber(L, matrix(row, col)); lua_pushnumber(L, matrix(row, col));
return 4; return 4;
@ -170,11 +169,11 @@ int LuaMatrix4::l_get_column(lua_State *L)
int LuaMatrix4::l_set_column(lua_State *L) int LuaMatrix4::l_set_column(lua_State *L)
{ {
auto &matrix = check(L, 1); auto &matrix = check(L, 1);
int col = read_index(L, 2); int col = readIndex(L, 2);
f32 x = luaL_checknumber(L, 3); f32 x = readParam<f32>(L, 3);
f32 y = luaL_checknumber(L, 4); f32 y = readParam<f32>(L, 4);
f32 z = luaL_checknumber(L, 5); f32 z = readParam<f32>(L, 5);
f32 w = luaL_checknumber(L, 6); f32 w = readParam<f32>(L, 6);
matrix(0, col) = x; matrix(0, col) = x;
matrix(1, col) = y; matrix(1, col) = y;
matrix(2, col) = z; matrix(2, col) = z;
@ -205,7 +204,7 @@ int LuaMatrix4::l_transform_4d(lua_State *L)
const auto &matrix = check(L, 1); const auto &matrix = check(L, 1);
f32 vec4[4]; f32 vec4[4];
for (int i = 0; i < 4; ++i) for (int i = 0; i < 4; ++i)
vec4[i] = luaL_checknumber(L, i + 2); vec4[i] = readParam<f32>(L, i + 2);
f32 res[4]; f32 res[4];
matrix.transformVec4(res, vec4); matrix.transformVec4(res, vec4);
for (int i = 0; i < 4; ++i) for (int i = 0; i < 4; ++i)
@ -354,12 +353,12 @@ int LuaMatrix4::mt_unm(lua_State *L)
int LuaMatrix4::mt_mul(lua_State *L) int LuaMatrix4::mt_mul(lua_State *L)
{ {
if (lua_isnumber(L, 1)) { if (lua_isnumber(L, 1)) {
f32 scalar = luaL_checknumber(L, 1); f32 scalar = readParam<f32>(L, 1);
const auto &matrix = check(L, 2); const auto &matrix = check(L, 2);
create(L) = scalar * matrix; create(L) = scalar * matrix;
} else { } else {
const auto &matrix = check(L, 1); const auto &matrix = check(L, 1);
f32 scalar = luaL_checknumber(L, 2); f32 scalar = readParam<f32>(L, 2);
create(L) = matrix * scalar; create(L) = matrix * scalar;
} }
return 1; return 1;

View file

@ -100,6 +100,9 @@ private:
static void *packIn(lua_State *L, int idx); static void *packIn(lua_State *L, int idx);
static void packOut(lua_State *L, void *ptr); static void packOut(lua_State *L, void *ptr);
template<int max = 4>
static int readIndex(lua_State *L, int index);
public: public:
// Constructor. Leaves the value on top of the stack. // Constructor. Leaves the value on top of the stack.

View file

@ -10,10 +10,9 @@
#include "irr_v3d.h" #include "irr_v3d.h"
#include "quaternion.h" #include "quaternion.h"
#include <cmath>
#include <cstring>
#include <lauxlib.h> #include <lauxlib.h>
#include <lua.h> #include <lua.h>
#include <sstream>
core::quaternion &LuaRotation::check(lua_State *L, int index) core::quaternion &LuaRotation::check(lua_State *L, int index)
{ {
@ -38,12 +37,10 @@ int LuaRotation::l_identity(lua_State *L)
int LuaRotation::l_quaternion(lua_State *L) int LuaRotation::l_quaternion(lua_State *L)
{ {
// TODO be more strict. f32 x = readFiniteParam<f32>(L, 1);
f64 x = luaL_checknumber(L, 1); f32 y = readFiniteParam<f32>(L, 2);
f64 y = luaL_checknumber(L, 2); f32 z = readFiniteParam<f32>(L, 3);
f64 z = luaL_checknumber(L, 3); f32 w = readFiniteParam<f32>(L, 4);
f64 w = luaL_checknumber(L, 4);
// Note: Converted to f32
core::quaternion q(x, y, z, w); core::quaternion q(x, y, z, w);
q.normalize(); q.normalize();
create(L, q); create(L, q);
@ -53,9 +50,8 @@ int LuaRotation::l_quaternion(lua_State *L)
int LuaRotation::l_axis_angle(lua_State *L) int LuaRotation::l_axis_angle(lua_State *L)
{ {
v3f axis = readParam<v3f>(L, 1); v3f axis = readParam<v3f>(L, 1);
f64 angle = luaL_checknumber(L, 2); f32 angle = readFiniteParam<f32>(L, 2);
core::quaternion quaternion; core::quaternion quaternion;
// Note: Axis converted to f32
axis.normalize(); axis.normalize();
quaternion.fromAngleAxis(angle, axis); quaternion.fromAngleAxis(angle, axis);
create(L, quaternion); create(L, quaternion);
@ -65,8 +61,7 @@ int LuaRotation::l_axis_angle(lua_State *L)
template<float v3f::* C> template<float v3f::* C>
int LuaRotation::l_fixed_axis_angle(lua_State *L) int LuaRotation::l_fixed_axis_angle(lua_State *L)
{ {
f64 angle = luaL_checknumber(L, 1); f32 angle = readFiniteParam<f32>(L, 1);
// Note: Angle converted to f32
v3f axis; v3f axis;
axis.*C = 1.0f; axis.*C = 1.0f;
create(L, core::quaternion().fromAngleAxis(angle, axis)); create(L, core::quaternion().fromAngleAxis(angle, axis));
@ -77,7 +72,6 @@ int LuaRotation::l_euler_angles(lua_State *L)
{ {
v3f euler = readParam<v3f>(L, 1); v3f euler = readParam<v3f>(L, 1);
core::quaternion quaternion; core::quaternion quaternion;
// Note: Euler angles converted to f32
quaternion.set(euler.X, euler.Y, euler.Z); quaternion.set(euler.X, euler.Y, euler.Z);
create(L, quaternion); create(L, quaternion);
return 1; return 1;
@ -148,7 +142,7 @@ int LuaRotation::l_slerp(lua_State *L)
{ {
const auto &from = check(L, 1); const auto &from = check(L, 1);
const auto &to = check(L, 2); const auto &to = check(L, 2);
f32 time = readParam<Finite<f32>>(L, 3).value; f32 time = readFiniteParam<f32>(L, 3);
core::quaternion result; core::quaternion result;
result.slerp(from, to, time); result.slerp(from, to, time);
create(L, result); create(L, result);
@ -169,7 +163,10 @@ int LuaRotation::l_angle_to(lua_State *L)
int LuaRotation::mt_tostring(lua_State *L) int LuaRotation::mt_tostring(lua_State *L)
{ {
const auto &q = check(L, 1); const auto &q = check(L, 1);
lua_pushfstring(L, "(%f\t%f\t%f\t%f)", q.X, q.Y, q.Z, q.W); std::stringstream ss;
ss << q;
std::string str = ss.str();
lua_pushlstring(L, str.c_str(), str.size());
return 1; return 1;
} }