mirror of
https://github.com/lune-org/lune.git
synced 2024-12-12 13:00:37 +00:00
Fix CFrame math
This commit is contained in:
parent
93b83a5874
commit
a82624023f
3 changed files with 139 additions and 32 deletions
|
@ -8,6 +8,12 @@ All notable changes to this project will be documented in this file.
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## Unreleased
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fixed issues with CFrame math operations
|
||||||
|
|
||||||
## `0.6.4` - March 26th, 2023
|
## `0.6.4` - March 26th, 2023
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
|
@ -42,38 +42,36 @@ impl CFrame {
|
||||||
// Strict args constructors
|
// Strict args constructors
|
||||||
datatype_table.set(
|
datatype_table.set(
|
||||||
"lookAt",
|
"lookAt",
|
||||||
lua.create_function(
|
lua.create_function(|_, (from, to, up): (Vector3, Vector3, Option<Vector3>)| {
|
||||||
|_, (at, look_at, up): (Vector3, Vector3, Option<Vector3>)| {
|
Ok(CFrame(look_at(
|
||||||
Ok(CFrame(Mat4::look_at_rh(
|
from.0,
|
||||||
at.0,
|
to.0,
|
||||||
look_at.0,
|
|
||||||
up.unwrap_or(Vector3(Vec3::Y)).0,
|
up.unwrap_or(Vector3(Vec3::Y)).0,
|
||||||
)))
|
)))
|
||||||
},
|
})?,
|
||||||
)?,
|
|
||||||
)?;
|
)?;
|
||||||
datatype_table.set(
|
datatype_table.set(
|
||||||
"fromEulerAnglesXYZ",
|
"fromEulerAnglesXYZ",
|
||||||
lua.create_function(|_, (rx, ry, rz): (f32, f32, f32)| {
|
lua.create_function(|_, (rx, ry, rz): (f32, f32, f32)| {
|
||||||
Ok(CFrame(Mat4::from_euler(EulerRot::ZYX, rx, ry, rz)))
|
Ok(CFrame(Mat4::from_euler(EulerRot::XYZ, rx, ry, rz)))
|
||||||
})?,
|
})?,
|
||||||
)?;
|
)?;
|
||||||
datatype_table.set(
|
datatype_table.set(
|
||||||
"fromEulerAnglesYXZ",
|
"fromEulerAnglesYXZ",
|
||||||
lua.create_function(|_, (rx, ry, rz): (f32, f32, f32)| {
|
lua.create_function(|_, (rx, ry, rz): (f32, f32, f32)| {
|
||||||
Ok(CFrame(Mat4::from_euler(EulerRot::ZXY, rx, ry, rz)))
|
Ok(CFrame(Mat4::from_euler(EulerRot::YXZ, ry, rx, rz)))
|
||||||
})?,
|
})?,
|
||||||
)?;
|
)?;
|
||||||
datatype_table.set(
|
datatype_table.set(
|
||||||
"Angles",
|
"Angles",
|
||||||
lua.create_function(|_, (rx, ry, rz): (f32, f32, f32)| {
|
lua.create_function(|_, (rx, ry, rz): (f32, f32, f32)| {
|
||||||
Ok(CFrame(Mat4::from_euler(EulerRot::ZYX, rx, ry, rz)))
|
Ok(CFrame(Mat4::from_euler(EulerRot::XYZ, rx, ry, rz)))
|
||||||
})?,
|
})?,
|
||||||
)?;
|
)?;
|
||||||
datatype_table.set(
|
datatype_table.set(
|
||||||
"fromOrientation",
|
"fromOrientation",
|
||||||
lua.create_function(|_, (rx, ry, rz): (f32, f32, f32)| {
|
lua.create_function(|_, (rx, ry, rz): (f32, f32, f32)| {
|
||||||
Ok(CFrame(Mat4::from_euler(EulerRot::ZXY, rx, ry, rz)))
|
Ok(CFrame(Mat4::from_euler(EulerRot::YXZ, ry, rx, rz)))
|
||||||
})?,
|
})?,
|
||||||
)?;
|
)?;
|
||||||
datatype_table.set(
|
datatype_table.set(
|
||||||
|
@ -99,7 +97,7 @@ impl CFrame {
|
||||||
)?;
|
)?;
|
||||||
// Dynamic args constructor
|
// Dynamic args constructor
|
||||||
type ArgsPos = Vector3;
|
type ArgsPos = Vector3;
|
||||||
type ArgsLook = (Vector3, Vector3);
|
type ArgsLook = (Vector3, Vector3, Option<Vector3>);
|
||||||
type ArgsPosXYZ = (f32, f32, f32);
|
type ArgsPosXYZ = (f32, f32, f32);
|
||||||
type ArgsPosXYZQuat = (f32, f32, f32, f32, f32, f32, f32);
|
type ArgsPosXYZQuat = (f32, f32, f32, f32, f32, f32, f32);
|
||||||
type ArgsMatrix = (f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32);
|
type ArgsMatrix = (f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32);
|
||||||
|
@ -110,8 +108,12 @@ impl CFrame {
|
||||||
Ok(CFrame(Mat4::IDENTITY))
|
Ok(CFrame(Mat4::IDENTITY))
|
||||||
} else if let Ok(pos) = ArgsPos::from_lua_multi(args.clone(), lua) {
|
} else if let Ok(pos) = ArgsPos::from_lua_multi(args.clone(), lua) {
|
||||||
Ok(CFrame(Mat4::from_translation(pos.0)))
|
Ok(CFrame(Mat4::from_translation(pos.0)))
|
||||||
} else if let Ok((pos, look_at)) = ArgsLook::from_lua_multi(args.clone(), lua) {
|
} else if let Ok((from, to, up)) = ArgsLook::from_lua_multi(args.clone(), lua) {
|
||||||
Ok(CFrame(Mat4::look_at_rh(pos.0, look_at.0, Vec3::Y)))
|
Ok(CFrame(look_at(
|
||||||
|
from.0,
|
||||||
|
to.0,
|
||||||
|
up.unwrap_or(Vector3(Vec3::Y)).0,
|
||||||
|
)))
|
||||||
} else if let Ok((x, y, z)) = ArgsPosXYZ::from_lua_multi(args.clone(), lua) {
|
} else if let Ok((x, y, z)) = ArgsPosXYZ::from_lua_multi(args.clone(), lua) {
|
||||||
Ok(CFrame(Mat4::from_translation(Vec3::new(x, y, z))))
|
Ok(CFrame(Mat4::from_translation(Vec3::new(x, y, z))))
|
||||||
} else if let Ok((x, y, z, qx, qy, qz, qw)) =
|
} else if let Ok((x, y, z, qx, qy, qz, qw)) =
|
||||||
|
@ -208,20 +210,22 @@ impl LuaUserData for CFrame {
|
||||||
let pos = this.position();
|
let pos = this.position();
|
||||||
let (rx, ry, rz) = this.orientation();
|
let (rx, ry, rz) = this.orientation();
|
||||||
Ok((
|
Ok((
|
||||||
pos.x, pos.y, pos.z,
|
pos.x, pos.y, -pos.z,
|
||||||
rx.x, rx.y, rx.z,
|
rx.x, rx.y, rx.z,
|
||||||
ry.x, ry.y, ry.z,
|
ry.x, ry.y, ry.z,
|
||||||
rz.x, rz.y, rz.z,
|
rz.x, rz.y, rz.z,
|
||||||
))
|
))
|
||||||
});
|
});
|
||||||
methods.add_method("ToEulerAnglesXYZ", |_, this, ()| {
|
methods.add_method("ToEulerAnglesXYZ", |_, this, ()| {
|
||||||
Ok(Quat::from_mat4(&this.0).to_euler(EulerRot::ZYX))
|
Ok(Quat::from_mat4(&this.0).to_euler(EulerRot::XYZ))
|
||||||
});
|
});
|
||||||
methods.add_method("ToEulerAnglesYXZ", |_, this, ()| {
|
methods.add_method("ToEulerAnglesYXZ", |_, this, ()| {
|
||||||
Ok(Quat::from_mat4(&this.0).to_euler(EulerRot::ZXY))
|
let (ry, rx, rz) = Quat::from_mat4(&this.0).to_euler(EulerRot::YXZ);
|
||||||
|
Ok((rx, ry, rz))
|
||||||
});
|
});
|
||||||
methods.add_method("ToOrientation", |_, this, ()| {
|
methods.add_method("ToOrientation", |_, this, ()| {
|
||||||
Ok(Quat::from_mat4(&this.0).to_euler(EulerRot::ZXY))
|
let (ry, rx, rz) = Quat::from_mat4(&this.0).to_euler(EulerRot::YXZ);
|
||||||
|
Ok((rx, ry, rz))
|
||||||
});
|
});
|
||||||
methods.add_method("ToAxisAngle", |_, this, ()| {
|
methods.add_method("ToAxisAngle", |_, this, ()| {
|
||||||
let (axis, angle) = Quat::from_mat4(&this.0).to_axis_angle();
|
let (axis, angle) = Quat::from_mat4(&this.0).to_axis_angle();
|
||||||
|
@ -329,3 +333,22 @@ impl From<CFrame> for DomCFrame {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Creates a matrix at the position `from`, looking towards `to`.
|
||||||
|
|
||||||
|
[`glam`] does provide functions such as [`look_at_lh`], [`look_at_rh`] and more but
|
||||||
|
they all create view matrices for camera transforms which is not what we want here.
|
||||||
|
*/
|
||||||
|
fn look_at(from: Vec3, to: Vec3, up: Vec3) -> Mat4 {
|
||||||
|
let dir = (to - from).normalize();
|
||||||
|
let xaxis = up.cross(dir).normalize();
|
||||||
|
let yaxis = dir.cross(xaxis).normalize();
|
||||||
|
|
||||||
|
Mat4::from_cols(
|
||||||
|
Vec3::new(xaxis.x, yaxis.x, dir.x).extend(0.0),
|
||||||
|
Vec3::new(xaxis.y, yaxis.y, dir.y).extend(0.0),
|
||||||
|
Vec3::new(xaxis.z, yaxis.z, dir.z).extend(0.0),
|
||||||
|
from.extend(1.0),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,42 @@ local roblox = require("@lune/roblox") :: any
|
||||||
local CFrame = roblox.CFrame
|
local CFrame = roblox.CFrame
|
||||||
local Vector3 = roblox.Vector3
|
local Vector3 = roblox.Vector3
|
||||||
|
|
||||||
|
local COMPONENT_NAMES =
|
||||||
|
{ "X", "Y", "Z", "R00", "R01", "R02", "R10", "R11", "R12", "R20", "R21", "R22" }
|
||||||
|
local function assertEq(actual, expected)
|
||||||
|
local actComps: { number } = { actual:GetComponents() }
|
||||||
|
local expComps: { number } = { expected:GetComponents() }
|
||||||
|
for index, actComp in actComps do
|
||||||
|
local expComp = expComps[index]
|
||||||
|
if math.abs(expComp - actComp) >= (1 / 512) then
|
||||||
|
local r0 = Vector3.new(actual:ToOrientation())
|
||||||
|
local r1 = Vector3.new(expected:ToOrientation())
|
||||||
|
error(
|
||||||
|
string.format(
|
||||||
|
"Expected component '%s' to be %.2f, got %.2f"
|
||||||
|
.. "\nActual: %.2f, %.2f, %.2f | %.2f, %.2f, %.2f"
|
||||||
|
.. "\nExpected: %.2f, %.2f, %.2f | %.2f, %.2f, %.2f",
|
||||||
|
COMPONENT_NAMES[index],
|
||||||
|
expComp,
|
||||||
|
actComp,
|
||||||
|
actual.Position.X,
|
||||||
|
actual.Position.Y,
|
||||||
|
actual.Position.Z,
|
||||||
|
math.deg(r0.X),
|
||||||
|
math.deg(r0.Y),
|
||||||
|
math.deg(r0.Z),
|
||||||
|
expected.Position.X,
|
||||||
|
expected.Position.Y,
|
||||||
|
expected.Position.Z,
|
||||||
|
math.deg(r1.X),
|
||||||
|
math.deg(r1.Y),
|
||||||
|
math.deg(r1.Z)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- Constructors & properties
|
-- Constructors & properties
|
||||||
|
|
||||||
CFrame.new()
|
CFrame.new()
|
||||||
|
@ -23,20 +59,62 @@ assert(CFrame.new(1, 2, 3).X == 1)
|
||||||
assert(CFrame.new(1, 2, 3).Y == 2)
|
assert(CFrame.new(1, 2, 3).Y == 2)
|
||||||
assert(CFrame.new(1, 2, 3).Z == 3)
|
assert(CFrame.new(1, 2, 3).Z == 3)
|
||||||
|
|
||||||
|
assertEq(
|
||||||
|
CFrame.fromMatrix(
|
||||||
|
Vector3.new(1, 2, 3),
|
||||||
|
Vector3.new(1, 0, 0),
|
||||||
|
Vector3.new(0, 1, 0),
|
||||||
|
Vector3.new(0, 0, 1)
|
||||||
|
),
|
||||||
|
CFrame.new(1, 2, 3)
|
||||||
|
)
|
||||||
|
|
||||||
-- Constants
|
-- Constants
|
||||||
|
|
||||||
assert(CFrame.identity == CFrame.new())
|
assertEq(CFrame.identity, CFrame.new())
|
||||||
assert(CFrame.identity == CFrame.new(0, 0, 0))
|
assertEq(CFrame.identity, CFrame.new(0, 0, 0))
|
||||||
assert(CFrame.identity == CFrame.Angles(0, 0, 0))
|
assertEq(CFrame.identity, CFrame.Angles(0, 0, 0))
|
||||||
assert(CFrame.identity == CFrame.fromOrientation(0, 0, 0))
|
assertEq(CFrame.identity, CFrame.fromOrientation(0, 0, 0))
|
||||||
|
|
||||||
-- Ops
|
-- Ops
|
||||||
|
|
||||||
assert(CFrame.new(2, 4, 8) + Vector3.new(1, 1, 2) == CFrame.new(3, 5, 10))
|
assertEq(CFrame.new(2, 4, 8) + Vector3.new(1, 1, 2), CFrame.new(3, 5, 10))
|
||||||
assert(CFrame.new(2, 4, 8) - Vector3.new(1, 1, 2) == CFrame.new(1, 3, 6))
|
assertEq(CFrame.new(2, 4, 8) - Vector3.new(1, 1, 2), CFrame.new(1, 3, 6))
|
||||||
assert(CFrame.new(2, 4, 8) * CFrame.new(1, 1, 2) == CFrame.new(3, 5, 10))
|
assertEq(CFrame.new(2, 4, 8) * CFrame.new(1, 1, 2), CFrame.new(3, 5, 10))
|
||||||
assert(CFrame.new(2, 4, 8) * Vector3.new(1, 1, 2) == Vector3.new(3, 5, 10))
|
assert(CFrame.new(2, 4, 8) * Vector3.new(1, 1, 2) == Vector3.new(3, 5, 10))
|
||||||
|
|
||||||
-- TODO: Check mult ops with rotated CFrames
|
-- Mult ops with rotated CFrames
|
||||||
|
|
||||||
-- TODO: Methods
|
assertEq(
|
||||||
|
CFrame.fromOrientation(0, math.rad(90), 0) * CFrame.fromOrientation(math.rad(5), 0, 0),
|
||||||
|
CFrame.fromOrientation(math.rad(5), math.rad(90), 0)
|
||||||
|
)
|
||||||
|
assertEq(
|
||||||
|
CFrame.fromOrientation(0, math.rad(90), 0) * CFrame.new(0, 0, -5),
|
||||||
|
CFrame.new(-5, 0, 0) * CFrame.fromOrientation(0, math.rad(90), 0)
|
||||||
|
)
|
||||||
|
|
||||||
|
-- World & object space conversions
|
||||||
|
|
||||||
|
local offset = CFrame.new(0, 0, -5)
|
||||||
|
assert(offset:ToWorldSpace(offset).Z == offset.Z * 2)
|
||||||
|
assert(offset:ToObjectSpace(offset).Z == 0)
|
||||||
|
|
||||||
|
local world = CFrame.fromOrientation(0, math.rad(90), 0) * CFrame.new(0, 0, -5)
|
||||||
|
local world2 = CFrame.fromOrientation(0, -math.rad(90), 0) * CFrame.new(0, 0, -5)
|
||||||
|
assertEq(CFrame.identity:ToObjectSpace(world), world)
|
||||||
|
assertEq(
|
||||||
|
world:ToObjectSpace(world2),
|
||||||
|
CFrame.fromOrientation(0, math.rad(180), 0) * CFrame.new(0, 0, -10)
|
||||||
|
)
|
||||||
|
|
||||||
|
-- Look
|
||||||
|
|
||||||
|
assertEq(CFrame.fromOrientation(0, math.rad(90), 0), CFrame.lookAt(Vector3.zero, -Vector3.xAxis))
|
||||||
|
assertEq(CFrame.fromOrientation(0, -math.rad(90), 0), CFrame.lookAt(Vector3.zero, Vector3.xAxis))
|
||||||
|
assertEq(
|
||||||
|
CFrame.new(0, 0, -5) * CFrame.fromOrientation(0, math.rad(90), 0),
|
||||||
|
CFrame.lookAt(Vector3.new(0, 0, -5), Vector3.new(0, 0, -5) - Vector3.xAxis)
|
||||||
|
)
|
||||||
|
|
||||||
|
-- TODO: More methods
|
||||||
|
|
Loading…
Reference in a new issue