Skip to content

Instantly share code, notes, and snippets.

@spotco
Created February 2, 2019 09:56
Show Gist options
  • Select an option

  • Save spotco/a245903185623abac7ce105beb90c50c to your computer and use it in GitHub Desktop.

Select an option

Save spotco/a245903185623abac7ce105beb90c50c to your computer and use it in GitHub Desktop.
LCFrameOpt.ModuleScript.lua
-- dependancies
local LVector3 = require(game.ReplicatedStorage.Shared.LVector3)
-- CFrame class
local TYPE_LCFRAME = "LCFrame"
local LCFrame = {
__type = TYPE_LCFRAME,
TYPE_LCFRAME = TYPE_LCFRAME
};
local mt = {__index = LCFrame};
local prettyPrint = true; -- mainly for debug (not a feature in real CFrames)
local function update_lookvector(v)
v.lookVector = LVector3.new(-v.m13, -v.m23, -v.m33);
end
-- built-in functions
local pi = math.pi;
local max = math.max;
local cos = math.cos;
local sin = math.sin;
local acos = math.acos;
local asin = math.asin;
local sqrt = math.sqrt;
local atan2 = math.atan2;
local unpack = unpack;
local concat = table.concat;
-- some variables
local IDENTITY_MATRIX = {
m11 = 1, m12 = 0, m13 = 0,
m21 = 0, m22 = 1, m23 = 0,
m31 = 0, m32 = 0, m33 = 1
};
local M41, M42, M43, M44 = 0, 0, 0, 1;
local IDENTITY_CFRAME = {
0, 0, 0, 1,
0, 0, 0, 1,
0, 0, 0, 1
};
local RIGHT_UNIT = LVector3.new(1, 0, 0);
local TOP_UNIT = LVector3.new(0, 1, 0);
local BACK_UNIT = LVector3.new(0, 0, 1);
-- private functions
local function fromAxisAngle(aaxis, vector, theta)
-- http://wiki.roblox.com/index.php?title=User:EgoMoose/Articles/Quaternions_and_slerping#Rodriguez_Rotation_formula:_Axis-angle_rotations
local laxis = aaxis:Unit();
return vector * cos(theta) + vector:Dot(laxis) * laxis * (1 - cos(theta)) + laxis:Cross(vector) * sin(theta);
end;
local function cfTimesv3(cf, v3)
local _, _, _, m11, m12, m13, m21, m22, m23, m31, m32, m33 = cf:components();
local right = LVector3.new(m11, m21, m31);
local top = LVector3.new(m12, m22, m32);
local back = LVector3.new(m13, m23, m33);
return cf.p + v3.X * right + v3.Y * top + v3.Z * back;
end;
local function fourByfour(a, b)
local a11, a12, a13, a14, a21, a22, a23, a24, a31, a32, a33, a34, a41, a42, a43, a44 = unpack(a);
local b11, b12, b13, b14, b21, b22, b23, b24, b31, b32, b33, b34, b41, b42, b43, b44 = unpack(b);
-- 4x4 matrix multiplication
local m11 = a11*b11 + a12*b21 + a13*b31 + a14*b41;
local m12 = a11*b12 + a12*b22 + a13*b32 + a14*b42;
local m13 = a11*b13 + a12*b23 + a13*b33 + a14*b43;
local m14 = a11*b14 + a12*b24 + a13*b34 + a14*b44;
local m21 = a21*b11 + a22*b21 + a23*b31 + a24*b41;
local m22 = a21*b12 + a22*b22 + a23*b32 + a24*b42;
local m23 = a21*b13 + a22*b23 + a23*b33 + a24*b43;
local m24 = a21*b14 + a22*b24 + a23*b34 + a24*b44;
local m31 = a31*b11 + a32*b21 + a33*b31 + a34*b41;
local m32 = a31*b12 + a32*b22 + a33*b32 + a34*b42;
local m33 = a31*b13 + a32*b23 + a33*b33 + a34*b43;
local m34 = a31*b14 + a32*b24 + a33*b34 + a34*b44;
local m41 = a41*b11 + a42*b21 + a43*b31 + a44*b41;
local m42 = a41*b12 + a42*b22 + a43*b32 + a44*b42;
local m43 = a41*b13 + a42*b23 + a43*b33 + a44*b43;
local m44 = a41*b14 + a42*b24 + a43*b34 + a44*b44;
-- return the components
return m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44;
end;
local _cfTimescf_outcf_a = {}
local _cfTimescf_outcf_b = {}
local function cfTimescf_outcf(cf1, cf2, outcf)
local a14, a24, a34, a11, a12, a13, a21, a22, a23, a31, a32, a33 = cf1:components();
local b14, b24, b34, b11, b12, b13, b21, b22, b23, b31, b32, b33 = cf2:components();
_cfTimescf_outcf_a[1] = a11
_cfTimescf_outcf_a[2] = a12
_cfTimescf_outcf_a[3] = a13
_cfTimescf_outcf_a[4] = a14
_cfTimescf_outcf_a[5] = a21
_cfTimescf_outcf_a[6] = a22
_cfTimescf_outcf_a[7] = a23
_cfTimescf_outcf_a[8] = a24
_cfTimescf_outcf_a[9] = a31
_cfTimescf_outcf_a[10] = a32
_cfTimescf_outcf_a[11] = a33
_cfTimescf_outcf_a[12] = a34
_cfTimescf_outcf_a[13] = M41
_cfTimescf_outcf_a[14] = M42
_cfTimescf_outcf_a[15] = M43
_cfTimescf_outcf_a[16] = M44
_cfTimescf_outcf_b[1] = b11
_cfTimescf_outcf_b[2] = b12
_cfTimescf_outcf_b[3] = b13
_cfTimescf_outcf_b[4] = b14
_cfTimescf_outcf_b[5] = b21
_cfTimescf_outcf_b[6] = b22
_cfTimescf_outcf_b[7] = b23
_cfTimescf_outcf_b[8] = b24
_cfTimescf_outcf_b[9] = b31
_cfTimescf_outcf_b[10] = b32
_cfTimescf_outcf_b[11] = b33
_cfTimescf_outcf_b[12] = b34
_cfTimescf_outcf_b[13] = M41
_cfTimescf_outcf_b[14] = M42
_cfTimescf_outcf_b[15] = M43
_cfTimescf_outcf_b[16] = M44
local m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44 = fourByfour(
_cfTimescf_outcf_a,
_cfTimescf_outcf_b
);
return outcf:set_components(m14, m24, m34, m11, m12, m13, m21, m22, m23, m31, m32, m33);
end
local function cfTimescf(cf1, cf2)
local rtv = LCFrame.new()
return cfTimescf_outcf(cf1, cf2, rtv)
end;
local function getDeterminant(cf)
local a14, a24, a34, a11, a12, a13, a21, a22, a23, a31, a32, a33 = cf:components();
local m41, m42, m43, m44 = 0, 0, 0, 1;
local det = a11*a22*a33*m44 + a11*a23*a34*m42 + a11*a24*a32*m43
+ a12*a21*a34*m43 + a12*a23*a31*m44 + a12*a24*a33*m41
+ a13*a21*a32*m44 + a13*a22*a34*m41 + a13*a24*a31*m42
+ a14*a21*a33*m42 + a14*a22*a31*m43 + a14*a23*a32*m41
- a11*a22*a34*m43 - a11*a23*a32*m44 - a11*a24*a33*m42
- a12*a21*a33*m44 - a12*a23*a34*m41 - a12*a24*a31*m43
- a13*a21*a34*m42 - a13*a22*a31*m44 - a13*a24*a32*m41
- a14*a21*a32*m43 - a14*a22*a33*m41 - a14*a23*a31*m42;
return det;
end;
local function invert4x4(cf)
-- this is linear algebra. We're inverting a 4x4 matrix
-- see: http://www.cg.info.hiroshima-cu.ac.jp/~miyazaki/knowledge/teche23.html
-- it is very possible that the built-in CFrame class does not use this method as computers tend to use elimination methods
-- regardless, both functions should return the same answer
local a14, a24, a34, a11, a12, a13, a21, a22, a23, a31, a32, a33 = cf:components();
local det = getDeterminant(cf);
if (det == 0) then return cf; end;
local b11 = (a22*a33*M44 + a23*a34*M42 + a24*a32*M43 - a22*a34*M43 - a23*a32*M44 - a24*a33*M42) / det;
local b12 = (a12*a34*M43 + a13*a32*M44 + a14*a33*M42 - a12*a33*M44 - a13*a34*M42 - a14*a32*M43) / det;
local b13 = (a12*a23*M44 + a13*a24*M42 + a14*a22*M43 - a12*a24*M43 - a13*a22*M44 - a14*a23*M42) / det;
local b14 = (a12*a24*a33 + a13*a22*a34 + a14*a23*a32 - a12*a23*a34 - a13*a24*a32 - a14*a22*a33) / det;
local b21 = (a21*a34*M43 + a23*a31*M44 + a24*a33*M41 - a21*a33*M44 - a23*a34*M41 - a24*a31*M43) / det;
local b22 = (a11*a33*M44 + a13*a34*M41 + a14*a31*M43 - a11*a34*M43 - a13*a31*M44 - a14*a33*M41) / det;
local b23 = (a11*a24*M43 + a13*a21*M44 + a14*a23*M41 - a11*a23*M44 - a13*a24*M41 - a14*a21*M43) / det;
local b24 = (a11*a23*a34 + a13*a24*a31 + a14*a21*a33 - a11*a24*a33 - a13*a21*a34 - a14*a23*a31) / det;
local b31 = (a21*a32*M44 + a22*a34*M41 + a24*a31*M42 - a21*a34*M42 - a22*a31*M44 - a24*a32*M41) / det;
local b32 = (a11*a34*M42 + a12*a31*M44 + a14*a32*M41 - a11*a32*M44 - a12*a34*M41 - a14*a31*M42) / det;
local b33 = (a11*a22*M44 + a12*a24*M41 + a14*a21*M42 - a11*a24*M42 - a12*a21*M44 - a14*a22*M41) / det;
local b34 = (a11*a24*a32 + a12*a21*a34 + a14*a22*a31 - a11*a22*a34 - a12*a24*a31 - a14*a21*a32) / det;
local b41 = (a21*a33*M42 + a22*a31*M43 + a23*a32*M41 - a21*a32*M43 - a22*a33*M41 - a23*a31*M42) / det;
local b42 = (a11*a32*M43 + a12*a33*M41 + a13*a31*M42 - a11*a33*M42 - a12*a31*M43 - a13*a32*M41) / det;
local b43 = (a11*a23*M42 + a12*a21*M43 + a13*a22*M41 - a11*a22*M43 - a12*a23*M41 - a13*a21*M42) / det;
local b44 = (a11*a22*a33 + a12*a23*a31 + a13*a21*a32 - a11*a23*a32 - a12*a21*a33 - a13*a22*a31) / det;
return LCFrame.new(b14, b24, b34, b11, b12, b13, b21, b22, b23, b31, b32, b33);
end;
local function quaternionToMatrix(i, j, k, w)
local m11 = 1 - 2*j^2 - 2*k^2;
local m12 = 2*(i*j - k*w);
local m13 = 2*(i*k + j*w);
local m21 = 2*(i*j + k*w);
local m22 = 1 - 2*i^2 - 2*k^2;
local m23 = 2*(j*k - i*w);
local m31 = 2*(i*k - j*w);
local m32 = 2*(j*k + i*w);
local m33 = 1 - 2*i^2 - 2*j^2;
return {0, 0, 0, m11, m12, m13, m21, m22, m23, m31, m32, m33};
end;
local function quaternionFromCFrame(cf)
-- taken from: http://wiki.roblox.com/index.php?title=Quaternions_for_rotation#Quaternion_from_a_Rotation_Matrix
local mx, my, mz, m11, m12, m13, m21, m22, m23, m31, m32, m33 = cf:components();
local trace = m11 + m22 + m33;
if (trace > 0) then
local s = sqrt(1 + trace);
local r = 0.5 / s;
return s * 0.5, LVector3.new((m32 - m23) * r, (m13 - m31) * r, (m21 - m12) * r);
else -- find the largest diagonal element
local big = max(m11, m22, m33);
if big == m11 then
local s = sqrt(1 + m11 - m22 - m33);
local r = 0.5 / s;
return (m32 - m23) * r, LVector3.new(0.5 * s, (m21 + m12) * r, (m13 + m31) * r);
elseif big == m22 then
local s = sqrt(1 - m11 + m22 - m33);
local r = 0.5 / s;
return (m13 - m31) * r, LVector3.new((m21 + m12) * r, 0.5 * s, (m32 + m23) * r);
elseif big == m33 then
local s = sqrt(1 - m11 - m22 + m33);
local r = 0.5 / s;
return (m21 - m12) * r, LVector3.new((m13 + m31) * r, (m32 + m23) * r, 0.5 * s);
end;
end;
end;
local function lerp(a, b, t)
-- I have no idea what the internal implemenation is
-- get the difference in CFrames, convert to quaternion, convert to axis angle, slerp
local cf = a:inverse() * b;
local w, v = quaternionFromCFrame(cf);
local theta = acos(w) * 2;
local p = a.p:Lerp(b.p, t);
if theta ~= 0 then
local rot = a * LCFrame.fromAxisAngle(v, theta * t);
local _, _, _, m11, m12, m13, m21, m22, m23, m31, m32, m33 = rot:components();
return LCFrame.new(p.X, p.Y, p.Z, m11, m12, m13, m21, m22, m23, m31, m32, m33);
else
local _, _, _, m11, m12, m13, m21, m22, m23, m31, m32, m33 = a:components();
return LCFrame.new(p.X, p.Y, p.Z, m11, m12, m13, m21, m22, m23, m31, m32, m33);
end;
end;
-- meta-methods
function mt.__add(a, b)
local x, y, z, m11, m12, m13, m21, m22, m23, m31, m32, m33 = a:components();
return LCFrame.new(x + b.X, y + b.Y, z + b.Z, m11, m12, m13, m21, m22, m23, m31, m32, m33);
end;
function mt.__sub(a, b)
local x, y, z, m11, m12, m13, m21, m22, m23, m31, m32, m33 = a:components();
return LCFrame.new(x - b.X, y - b.Y, z - b.Z, m11, m12, m13, m21, m22, m23, m31, m32, m33);
end;
function mt.__mul(a, b)
local aIsCFrame = type(a) == "table" and a.__type and a.__type == TYPE_LCFRAME;
local bIsCFrame = type(b) == "table" and b.__type and b.__type == TYPE_LCFRAME;
local aIsVector = type(a) == "table" and a.__type and a.__type == LVector3.TYPE_LVECTOR3;
local bIsVector = type(b) == "table" and b.__type and b.__type == LVector3.TYPE_LVECTOR3;
if (aIsCFrame and bIsVector) then
return cfTimesv3(a, b);
elseif (aIsCFrame and bIsCFrame) then
return cfTimescf(a, b);
elseif (aIsCFrame) then
local t = type(a);
local cust = t == "table" and a.__type or t;
error("bad argument #2 to '?' (LVector expected, got " .. cust .. " )");
elseif (bIsCFrame) then
local t = type(a);
local cust = t == "table" and a.__type or t;
error("bad argument #1 to '?' (CFrame expected, got " .. cust .. " )");
end;
end;
function mt.__tostring(t)
local components = {t:components()};
if prettyPrint then
local s = "";
for i = 1, 12 do
s = s .. ((i > 1 and i % 3 == 1 and "\n") or "") .. components[i] .. (i < 12 and ", " or "");
end;
return s;
else
return concat(components, ", ");
end;
end;
mt.__metatable = false;
-- public class
function LCFrame.new(...)
local self = {};
self.p = LVector3.new(0, 0, 0);
for k, v in next, IDENTITY_MATRIX do
self[k] = v;
end;
-- most of this function is error handling from bad userinput
local t = {...};
local length = #t;
if length > 12 then
error("Invalid number of arguments: " .. length);
elseif (length == 1) then -- single LVector case
local v = t[1];
local isVector = type(v) == "table" and v.__type and v.__type == LVector3.TYPE_LVECTOR3;
if (not isVector) then
local pt = type(v);
local cust = pt == "table" and v.__type or pt;
error("bad argument #1 to 'new' (LVector3 expected, got" .. cust .. ")");
end;
-- make a copy to avoid user changing the original vector
self.p = LVector3.new(v.X, v.Y, v.Z);
elseif (length == 2) then -- two LVector3 case, we much build a lookAt matrix
local eye, look = t[1], t[2];
local eyeIsVector = type(eye) == "table" and eye.__type and eye.__type == LVector3.TYPE_LVECTOR3;
local lookIsVector = type(look) == "table" and look.__type and look.__type == LVector3.TYPE_LVECTOR3;
if (not eyeIsVector and not lookIsVector) then
local pt = type(eye);
local cust = pt == "table" and eye.__type or pt;
error("bad argument #1 to 'new' (LVector3 expected, got" .. cust .. ")");
end;
local zaxis = (eye - look):Unit();
local xaxis = TOP_UNIT:Cross(zaxis):Unit();
local yaxis = zaxis:Cross(xaxis):Unit();
if (xaxis:Magnitude() == 0) then -- edge cases
if zaxis.Y < 0 then
xaxis = LVector3.new(0, 0, -1);
yaxis = LVector3.new(1, 0, 0);
zaxis = LVector3.new(0, -1, 0);
else
xaxis = LVector3.new(0, 0, 1);
yaxis = LVector3.new(1, 0, 0);
zaxis = LVector3.new(0, 1, 0);
end;
end;
self.p = LVector3.new(eye.X, eye.Y, eye.Z);
self.m11, self.m12, self.m13 = xaxis.X, yaxis.X, zaxis.X;
self.m21, self.m22, self.m23 = xaxis.Y, yaxis.Y, zaxis.Y;
self.m31, self.m32, self.m33 = xaxis.Z, yaxis.Z, zaxis.Z;
elseif (length == 3) then -- x, y, z
for i = 1, length do
local pt = type(t[i]);
local cust = pt;
if cust ~= "number" then error("bad argument #" .. i .. " to 'new' (Number expected, got " .. cust .. ")"); end;
end;
self.p = LVector3.new(t[1], t[2], t[3]);
elseif (length == 7) then -- x, y, z, quaternion
for i = 1, length do
local pt = type(t[i]);
local cust = pt;
if cust ~= "number" then error("bad argument #" .. i .. " to 'new' (Number expected, got " .. cust .. ")"); end;
end;
local m = quaternionToMatrix(t[4], t[5], t[6], t[7]);
self.p = LVector3.new(t[1], t[2], t[3]);
self.m11, self.m12, self.m13 = m[4], m[5], m[6];
self.m21, self.m22, self.m23 = m[7], m[8], m[9];
self.m31, self.m32, self.m33 = m[10], m[11], m[12];
elseif (length == 12) then -- all components provided
for i = 1, length do
local pt = type(t[i]);
local cust = pt;
if cust ~= "number" then error("bad argument #" .. i .. " to 'new' (Number expected, got " .. cust .. ")"); end;
end;
self.p = LVector3.new(t[1], t[2], t[3]);
self.m11, self.m12, self.m13 = t[4], t[5], t[6];
self.m21, self.m22, self.m23 = t[7], t[8], t[9];
self.m31, self.m32, self.m33 = t[10], t[11], t[12];
elseif length > 0 then -- more than zero components
for i = 1, length do
local pt = type(t[i]);
local cust = pt;
if cust ~= "number" then error("bad argument #" .. i .. " to 'new' (Number expected, got " .. cust .. ")"); end;
end;
error("bad argument #" .. (length + 1) .. " to 'new' (Number expected, got nil)");
end;
update_lookvector(self)
return setmetatable(self, mt);
end;
--SPCHANGE (new methods)
function LCFrame:set_identity()
self.p:set(0,0,0)
for key, value in next, IDENTITY_MATRIX do
self[key] = value;
end;
update_lookvector(self);
return self;
end
function LCFrame:set_components(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12)
self.p:set(a1, a2, a3);
self.m11, self.m12, self.m13 = a4, a5, a6;
self.m21, self.m22, self.m23 = a7, a8, a9;
self.m31, self.m32, self.m33 = a10, a11, a12;
update_lookvector(self);
return self;
end
function LCFrame:from_cframe(rcf)
local a_x, a_y, a_z, a_r00, a_r01, a_r02, a_r10, a_r11, a_r12, a_r20, a_r21, a_r22 = rcf:components();
self:set_components(a_x, a_y, a_z, a_r00, a_r01, a_r02, a_r10, a_r11, a_r12, a_r20, a_r21, a_r22);
return self
end
function LCFrame:to_cframe()
return CFrame.new(
self.p.X, self.p.Y, self.p.Z,
self.m11, self.m12, self.m13,
self.m21, self.m22, self.m23,
self.m31, self.m32, self.m33
)
end
function LCFrame:store_mul_result(a,b)
cfTimescf_outcf(a, b, self);
update_lookvector(self);
return self;
end
function LCFrame:set_angles(x,y,z)
local m11 = cos(y) * cos(z);
local m12 = -cos(y) * sin(z);
local m13 = sin(y);
local m21 = cos(z) * sin(x) * sin(y) + cos(x) * sin(z);
local m22 = cos(x) * cos(z) - sin(x) * sin(y) * sin(z);
local m23 = -cos(y) * sin(x);
local m31 = sin(x) * sin(z) - cos(x) * cos(z) * sin(y);
local m32 = cos(z) * sin(x) + cos(x) * sin(y) * sin(z);
local m33 = cos(x) * cos(y);
self:set_components(0, 0, 0, m11, m12, m13, m21, m22, m23, m31, m32, m33);
update_lookvector(self);
return self;
end
local _cached_set_eye_look_zaxis = LVector3.new()
local _cached_set_eye_look_xaxis = LVector3.new()
local _cached_set_eye_look_yaxis = LVector3.new()
function LCFrame:set_eye_look(eye, look)
local eyeIsVector = type(eye) == "table" and eye.__type and eye.__type == LVector3.TYPE_LVECTOR3;
local lookIsVector = type(look) == "table" and look.__type and look.__type == LVector3.TYPE_LVECTOR3;
if (not eyeIsVector and not lookIsVector) then
local t = type(eye);
local cust = t == "table" and eye.__type or t;
error("bad argument #1 to 'new' (LVector3 expected, got" .. cust .. ")");
end;
_cached_set_eye_look_zaxis:store_sub_result(eye, look);
_cached_set_eye_look_zaxis:store_normalized(_cached_set_eye_look_zaxis);
_cached_set_eye_look_xaxis:store_cross_result(TOP_UNIT, _cached_set_eye_look_zaxis);
_cached_set_eye_look_xaxis:store_normalized(_cached_set_eye_look_xaxis);
_cached_set_eye_look_yaxis:store_cross_result(_cached_set_eye_look_zaxis, _cached_set_eye_look_xaxis);
_cached_set_eye_look_yaxis:store_normalized(_cached_set_eye_look_yaxis);
if _cached_set_eye_look_xaxis:Magnitude() == 0 then
if _cached_set_eye_look_zaxis.Y < 0 then
_cached_set_eye_look_xaxis:set(0, 0, -1);
_cached_set_eye_look_yaxis:set(1, 0, 0);
_cached_set_eye_look_zaxis:set(0, -1, 0);
else
_cached_set_eye_look_xaxis:set(0, 0, 1);
_cached_set_eye_look_yaxis:set(1, 0, 0);
_cached_set_eye_look_zaxis:set(0, 1, 0);
end;
end
self.p:set(eye.X, eye.Y, eye.Z);
self.m11, self.m12, self.m13 = _cached_set_eye_look_xaxis.X, _cached_set_eye_look_yaxis.X, _cached_set_eye_look_zaxis.X;
self.m21, self.m22, self.m23 = _cached_set_eye_look_xaxis.Y, _cached_set_eye_look_yaxis.Y, _cached_set_eye_look_zaxis.Y;
self.m31, self.m32, self.m33 = _cached_set_eye_look_xaxis.Z, _cached_set_eye_look_yaxis.Z, _cached_set_eye_look_zaxis.Z;
update_lookvector(self);
return self
end
--END SPCHANGE
function LCFrame.fromAxisAngle(aaxis, theta)
local axis = aaxis:Unit();
local r = fromAxisAngle(axis, RIGHT_UNIT, theta);
local t = fromAxisAngle(axis, TOP_UNIT, theta);
local b = fromAxisAngle(axis, BACK_UNIT, theta);
return LCFrame.new(
0, 0, 0,
r.X, t.X, b.X,
r.Y, t.Y, b.Y,
r.Z, t.Z, b.Z
);
end;
function LCFrame.Angles(x, y, z)
-- two implemenations possible. The commented one is what is what is used in the real CFrame constructor (to my knowledge)
-- the uncommented implemenation is easier for me to keep track of and I think makes more sense, but you can use either
-- the method (presumably) used in the real constructor
-- How to find this matrix: http://i.imgur.com/IWDMw0m.png
-- https://en.wikipedia.org/wiki/Rotation_matrix#In_three_dimensions
--[[
local m11 = cos(y) * cos(z);
local m12 = -cos(y) * sin(z);
local m13 = sin(y);
local m21 = cos(z) * sin(x) * sin(y) + cos(x) * sin(z);
local m22 = cos(x) * cos(z) - sin(x) * sin(y) * sin(z);
local m23 = -cos(y) * sin(x);
local m31 = sin(x) * sin(z) - cos(x) * cos(z) * sin(y);
local m32 = cos(z) * sin(x) + cos(x) * sin(y) * sin(z);
local m33 = cos(x) * cos(y);
return cframe.new(0, 0, 0, m11, m12, m13, m21, m22, m23, m31, m32, m33);
--]]
-- the method I prefer
local cfx = LCFrame.fromAxisAngle(RIGHT_UNIT, x);
local cfy = LCFrame.fromAxisAngle(TOP_UNIT, y);
local cfz = LCFrame.fromAxisAngle(BACK_UNIT, z);
return cfx * cfy * cfz;
end;
function LCFrame.fromEulerAnglesXYZ(x, y, z)
return LCFrame.Angles(x, y, z);
end;
function LCFrame:inverse()
return invert4x4(self);
end;
function LCFrame:lerp(self2, t)
return lerp(self, self2, t);
end;
function LCFrame:toWorldSpace(self2)
return self * self2;
end;
function LCFrame:toWorldSpace(self2)
return self * self2;
end;
function LCFrame:toObjectSpace(self2)
return self:inverse() * self2;
end;
function LCFrame:pointToWorldSpace(v3)
return self * v3;
end;
function LCFrame:pointToObjectSpace(v3)
return self:inverse() * v3;
end;
function LCFrame:vectorToWorldSpace(v3)
return (self - self.p) * v3;
end;
function LCFrame:vectorToObjectSpace(v3)
return (self - self.p):inverse() * v3;
end;
function LCFrame:components()
return self.p.X, self.p.Y, self.p.Z, self.m11, self.m12, self.m13, self.m21, self.m22, self.m23, self.m31, self.m32, self.m33;
end;
function LCFrame:toEulerAnglesXYZ()
-- based off the method (presumably) used in the real constructor for cframe.Angles
local _, _, _, m11, m12, m13, m21, m22, m23, m31, m32, m33 = self:components();
local x = atan2(-m23, m33);
local y = asin(m13);
local z = atan2(-m12, m11);
return x, y, z;
end;
return LCFrame;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment