Hello guys. I’m trying to learn quaternions, and combine them with buffers. But while doing so, I got 1 problem: in some cases, 6th and 10th components of CFrame are inversed, aka was 1 and becomes -1. Can someone tell me, why this occurs, and how I can prevent this?
Example of such CFrame:
-63.350399, 4.56043625, 23.2463398, -5.39922257e-06, -0.00118275138, -0.999999285, -0.000830398756, 0.999989152, -0.00118275813, 0.999999642, 0.000830383622, -6.38138181e-06
becomes
-63.3505859, 4.56030273, 23.2463379, 4.00296358e-07, 0.000215795357, 1.00000012, 0.00461801607, 0.999989331, -0.000215795357, -0.99998945, 0.00461801607, -5.96046448e-07
(Shorter variant, with rounding up to 0.01):
-63.35, 4.56, 23.25, 0, 0, -1, 0, 1, 0, 1, 0, 0
and
-63.35, 4.56, 23.25, 0, 0, 1, 0, 1, 0, -1, 0, 0
Quaternion code:
local QuaternionMeta = {}
QuaternionMeta.__index = QuaternionMeta
QuaternionMeta.__tostring = function(self)
return tostring(self.w) .. ", " .. tostring(self.x) .. ", " .. tostring(self.y) .. ", " .. tostring(self.z)
end
function QuaternionMeta.new(w, x, y, z)
local magnitude = (x^2 + y^2 + z^2 + w^2)^0.5
local self = {
w = w / magnitude,
x = x / magnitude,
y = y / magnitude,
z = z / magnitude,
}
return setmetatable(self, QuaternionMeta)
end
function QuaternionMeta.fromOrientation(x, y, z):{}
local c1 = math.cos(y/2)
local c2 = math.cos(z/2)
local c3 = math.cos(x/2)
local s1 = math.sin(y/2)
local s2 = math.sin(z/2)
local s3 = math.sin(x/2)
local w = c1 * c2 * c3 - s1 * s2 * s3
local x = s1 * s2 * c3 + c1 * c2 * s3
local y = s1 * c2 * c3 + c1 * s2 * s3
local z = c1 * s2 * c3 - s1 * c2 * s3
return QuaternionMeta.new(w, x, y, z)
end
function QuaternionMeta.fromCFrame(CF)
local _, _, _, R11, _, _, _, R22, _, _, _, R33 = CF:GetComponents()
local qx = (1 + R11 - R22 - R33)^0.5 / 2
local qy = (1 - R11 + R22 - R33)^0.5 / 2
local qz = (1 - R11 - R22 + R33)^0.5 / 2
local qw = (1 + R11 + R22 + R33)^0.5 / 2
local magnitude = (qx^2 + qy^2 + qz^2 + qw^2)^0.5
return QuaternionMeta.new(qw / magnitude, qx / magnitude, qy / magnitude, qz / magnitude)
end
function QuaternionMeta:ToCFrame(x, y, z)
return CFrame.new(x or 0, y or 0, z or 0, self.x, self.y, self.z, self.w)
end
return QuaternionMeta
Code which uses quaternions and somewhy in this 2 places it gives diffirent results:
--1st part, correct CFrame
local FOV = script.FieldOfView.Value
local HalfFovRadians = math.rad(FOV / 2)
local RayDirection = CameraStart.WorldCFrame.LookVector
local RayRight = CameraStart.WorldCFrame.RightVector * math.tan(HalfFovRadians)
local RayUp = -CameraStart.WorldCFrame.UpVector * math.tan(HalfFovRadians)
local CamStart = CameraStart.WorldPosition
local CameraData = buffer.create(21) --1+4*3+2*4 (FOV (0-255), Position, Quaternion data)
buffer.writeu8(CameraData, 0, math.clamp(math.floor(FOV), 0, 255))
buffer.writei32(CameraData, 1, math.clamp(math.floor(CamStart.X * PosRounding), -2147483648, 2147483647))
buffer.writei32(CameraData, 5, math.clamp(math.floor(CamStart.Y * PosRounding), -2147483648, 2147483647))
buffer.writei32(CameraData, 9, math.clamp(math.floor(CamStart.Z * PosRounding), -2147483648, 2147483647))
local CameraQuaternion = Quaternion.fromCFrame(CameraStart.WorldCFrame)
buffer.writei16(CameraData, 13, math.clamp(math.floor(CameraQuaternion.w * QuatRounding), -32768, 32767))
buffer.writei16(CameraData, 15, math.clamp(math.floor(CameraQuaternion.x * QuatRounding), -32768, 32767))
buffer.writei16(CameraData, 17, math.clamp(math.floor(CameraQuaternion.y * QuatRounding), -32768, 32767))
buffer.writei16(CameraData, 19, math.clamp(math.floor(CameraQuaternion.z * QuatRounding), -32768, 32767))
warn(CFrame.new(0, 0, 0, math.floor(CameraQuaternion.x * QuatRounding) / QuatRounding, math.floor(CameraQuaternion.y * QuatRounding) / QuatRounding, math.floor(CameraQuaternion.z * QuatRounding) / QuatRounding, math.floor(CameraQuaternion.w * QuatRounding) / QuatRounding):FuzzyEq(CameraQuaternion:ToCFrame(), 0.01))
print(tostring(CameraQuaternion))
print(CameraStart.WorldCFrame)
--local CameraData = {FOV, CameraStart.WorldCFrame}
self.CameraData[#self.CameraData + 1] = CameraData
--2nd part, inverted result
FrameIndex = FrameIndex or #self.ColorData
local ColorData = self.ColorData[FrameIndex]
local DistanceData = self.DistanceData[FrameIndex]
local AttractionData = self.AttractionData[FrameIndex]
local NormalsData = self.NormalsData[FrameIndex]
local CameraData = self.CameraData[FrameIndex]
local FOV = buffer.readu8(CameraData, 0)
local PosX, PosY, PosZ = buffer.readi32(CameraData, 1) / PosRounding, buffer.readi32(CameraData, 5) / PosRounding, buffer.readi32(CameraData, 9) / PosRounding
local QuatW, QuatX, QuatY, QuatZ = buffer.readi16(CameraData, 13) / QuatRounding, buffer.readi16(CameraData, 15) / QuatRounding, buffer.readi16(CameraData, 17) / QuatRounding, buffer.readi16(CameraData, 19) / QuatRounding
local CameraCFrame = Quaternion.new(QuatW, QuatX, QuatY, QuatZ):ToCFrame(PosX, PosY, PosZ) --CFrame.new(PosX, PosY, PosZ, QuatX, QuatY, QuatZ, QuatW)
print(tostring(Quaternion.new(QuatW, QuatX, QuatY, QuatZ)))
print(CameraCFrame)
I spent day to find why it don’t works, and can’t find solution. Please, help…