Skip to content

Commit 36568d2

Browse files
committed
Normalize input quaternions inside quaternion.to<direction>()
This matches the behavior of `ll.Rot2<Direction>(<0,0,0,0>)` in LSL.
1 parent bec0b28 commit 36568d2

2 files changed

Lines changed: 36 additions & 12 deletions

File tree

VM/src/llsl.cpp

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1476,34 +1476,52 @@ static inline float quaternion_dot(const float* a, const float* b) {
14761476
constexpr float ONE_PART_IN_A_MILLION = 0.000001f;
14771477
constexpr float FP_MAG_THRESHOLD = 0.0000001f;
14781478

1479-
static int lua_quaternion_normalize(lua_State *L)
1479+
// This logic largely copied from indra
1480+
static inline void normalize_quaternion(const float* in, float* out)
14801481
{
1481-
const float* quat = luaSL_checkquaternion(L, 1);
1482-
// This logic largely copied from indra
1483-
float mag = sqrtf(quaternion_dot(quat, quat));
1482+
float mag = sqrtf(quaternion_dot(in, in));
14841483
if (mag > FP_MAG_THRESHOLD)
14851484
{
14861485
// Floating point error can prevent some quaternions from achieving
14871486
// exact unity length. When trying to renormalize such quaternions we
14881487
// can oscillate between multiple quantized states. To prevent such
14891488
// drifts we only renormalize if the length is far enough from unity.
1490-
if (fabs(1.f - mag) > ONE_PART_IN_A_MILLION)
1489+
if (fabsf(1.f - mag) > ONE_PART_IN_A_MILLION)
14911490
{
14921491
float oomag = 1.0f / mag;
1493-
luaSL_pushquaternion(L, quat[0] * oomag, quat[1] * oomag, quat[2] * oomag, quat[3] * oomag);
1492+
out[0] = in[0] * oomag;
1493+
out[1] = in[1] * oomag;
1494+
out[2] = in[2] * oomag;
1495+
out[3] = in[3] * oomag;
14941496
}
14951497
else
14961498
{
1497-
// We don't normalize in this case in indra, so push the original input
1498-
lua_pushvalue(L, 1);
1499+
out[0] = in[0];
1500+
out[1] = in[1];
1501+
out[2] = in[2];
1502+
out[3] = in[3];
14991503
}
15001504
}
15011505
else
15021506
{
15031507
// We were given a very bad quaternion so we set it to identity
1504-
luaSL_pushquaternion(L, 0, 0, 0, 1);
1508+
out[0] = 0.0f;
1509+
out[1] = 0.0f;
1510+
out[2] = 0.0f;
1511+
out[3] = 1.0f;
15051512
}
1513+
}
15061514

1515+
static int lua_quaternion_normalize(lua_State *L)
1516+
{
1517+
const float* quat = luaSL_checkquaternion(L, 1);
1518+
float res[4];
1519+
normalize_quaternion(quat, res);
1520+
// We don't normalize in this case in indra, so push the original input
1521+
if (res[0] == quat[0] && res[1] == quat[1] && res[2] == quat[2] && res[3] == quat[3])
1522+
lua_pushvalue(L, 1);
1523+
else
1524+
luaSL_pushquaternion(L, res[0], res[1], res[2], res[3]);
15071525
return 1;
15081526
}
15091527

@@ -1576,10 +1594,11 @@ static int lua_quaternion_conjugate(lua_State *L)
15761594

15771595
static inline void push_rotated_vector(lua_State *L, const float* vec) {
15781596
const float* quat = luaSL_checkquaternion(L, 1);
1597+
float nquat[4];
1598+
normalize_quaternion(quat, nquat);
15791599
float res[3] = {0.0f};
1580-
rot_vec(vec, quat, res);
1581-
float invSqrt = 1.0f / sqrtf(res[0] * res[0] + res[1] * res[1] + res[2] * res[2]);
1582-
lua_pushvector(L, res[0] * invSqrt, res[1] * invSqrt, res[2] * invSqrt);
1600+
rot_vec(vec, nquat, res);
1601+
lua_pushvector(L, res[0], res[1], res[2]);
15831602
}
15841603

15851604
static int lua_quaternion_tofwd(lua_State *L)

tests/conformance/quaternion.lua

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ assert(`{quaternion.tofwd(yaw_ninety)}` == "<0, 1, 0>")
2828
assert(`{quaternion.toleft(yaw_ninety)}` == "<-1, 0, 0>")
2929
assert(`{quaternion.toup(yaw_ninety)}` == "<0, 0, 1>")
3030

31+
-- Zero quaternion normalizes to identity before rotation (matches server)
32+
assert(`{quaternion.tofwd(quaternion(0, 0, 0, 0))}` == "<1, 0, 0>")
33+
assert(`{quaternion.toleft(quaternion(0, 0, 0, 0))}` == "<0, 1, 0>")
34+
assert(`{quaternion.toup(quaternion(0, 0, 0, 0))}` == "<0, 0, 1>")
35+
3136
-- Test that tostring doesn't lose precision for large component values
3237
assert(`{vector(123456.5, 0, 0)}` == "<123456.5, 0, 0>")
3338
assert(`{vector(1000001, 0, 0)}` == "<1000001, 0, 0>")

0 commit comments

Comments
 (0)