Advertisement
CloneTrooper1019

CFrameSerializer.lua

Mar 7th, 2019
557
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 5.70 KB | None | 0 0
  1. ---------------------------------------------------------------------------------------------------------------------------------------
  2. -- CFrameSerializer.lua
  3. -- Written by CloneTrooper1019
  4. ---------------------------------------------------------------------------------------------------------------------------------------
  5. -- Usage:
  6. --         string CFrameSerializer:Encode(CFrame cf) <- Encodes a CFrame into a compressed string.
  7. --         CFrame CFrameSerializer:Decode(string cf) <- Decodes a compressed string into a CFrame.
  8. ---------------------------------------------------------------------------------------------------------------------------------------
  9. -- Description:
  10. --
  11. -- The goal of this library is to provide a light-weight compression scheme for CFrame data.
  12. -- It isn't the most compression that can be done to a CFrame, but its very portable
  13. -- and will guarantee to encode CFrames with a length shorter than 'tostring(CFrame)'
  14. --
  15. -- There are a few key optimization techniques at play here:
  16. --
  17. --     • Truncated floating point numbers, with trailing zeros removed.
  18. --     • Storing axis-aligned CFrame rotations with a single integer, which is
  19. --       the product of the NormalId values of a CFrame's UpVector and RightVector.
  20. --     • Using quaternions instead of rotation matrices for non axis-aligned CFrames.  
  21. --
  22. -- This encoding works best when rotations are aligned in 90° increments, and positions are grid-aligned.
  23. --
  24. ---------------------------------------------------------------------------------------------------------------------------------------
  25. -- Utility Functions
  26. ---------------------------------------------------------------------------------------------------------------------------------------
  27.  
  28. local function fuzzyEquals(a, b)
  29.     return math.abs(b - a) < 0.001
  30. end
  31.  
  32. local function packNumber(num)
  33.     local floor = math.floor(num)
  34.    
  35.     if fuzzyEquals(num, floor) then
  36.         return tostring(floor)
  37.     else
  38.         local result = string.format("%.3f", num)
  39.             :gsub("%.?0+$", "")
  40.        
  41.         return result
  42.     end
  43. end
  44.  
  45. local function packNumbers(...)
  46.     local array = {...}
  47.    
  48.     for k,v in pairs(array) do
  49.         array[k] = packNumber(v)
  50.     end
  51.    
  52.     return table.concat(array, " ")
  53. end
  54.  
  55. local function isAxisAligned(cf)
  56.     local matrix = { cf:GetComponents() }
  57.     local s0, s1 = 0, 0
  58.    
  59.     for i = 4, 12 do
  60.         local t = matrix[i]
  61.         local abs = math.abs(t)
  62.        
  63.         if fuzzyEquals(abs, 1) then
  64.             s1 = s1 + 1
  65.         elseif fuzzyEquals(abs, 0) then
  66.             s0 = s0 + 1
  67.         end
  68.     end
  69.    
  70.     return (s0 == 6) and (s1 == 3)
  71. end
  72.  
  73. local function toNormalId(vec)
  74.     for _,normalId in pairs(Enum.NormalId:GetEnumItems()) do
  75.         local normal = Vector3.FromNormalId(normalId)
  76.         local dotProd = normal:Dot(vec)
  77.        
  78.         if fuzzyEquals(dotProd, 1) then
  79.             return normalId.Value
  80.         end
  81.     end
  82.    
  83.     return -1
  84. end
  85.  
  86. local function isLegalOrientId(orientId)
  87.     local xNormalAbs = math.floor(orientId / 6) % 3;
  88.     local yNormalAbs = orientId % 3;
  89.    
  90.     return (xNormalAbs ~= yNormalAbs);
  91. end
  92.  
  93. local function getOrientId(cf)
  94.     if not isAxisAligned(cf) then
  95.         return -1
  96.     end
  97.    
  98.     local xNormal = toNormalId(cf.RightVector)
  99.     local yNormal = toNormalId(cf.UpVector)
  100.    
  101.     local orientId = (6 * xNormal) + yNormal
  102.    
  103.     if not isLegalOrientId(orientId) then
  104.         return -1
  105.     end
  106.    
  107.     return orientId
  108. end
  109.  
  110. local function fromOrientId(pos, orientId)
  111.     if not isLegalOrientId(orientId) then
  112.         orientId = 1
  113.     end
  114.  
  115.     local xn = orientId / 6
  116.     local yn = orientId % 6
  117.    
  118.     local vx = Vector3.FromNormalId(xn)
  119.     local vy = Vector3.FromNormalId(yn)
  120.    
  121.     return CFrame.fromMatrix(pos, vx, vy)
  122. end
  123.  
  124. local function toQuaternion(cf)
  125.     local w, x, y, z
  126.    
  127.     local m11, m12, m13, m21, m22, m23, m31, m32, m33 = select(4, cf:GetComponents())
  128.     local trace = m11 + m22 + m33
  129.    
  130.     if trace > 0 then
  131.         local s = math.sqrt(1 + trace)
  132.         local r = 0.5 / s
  133.  
  134.         w = s * 0.5;
  135.         x = (m32 - m23) * r;
  136.         y = (m13 - m31) * r;
  137.         z = (m21 - m12) * r;
  138.     else
  139.         local big = math.max(m11, m22, m33)
  140.        
  141.         if big == m11 then
  142.             local s = math.sqrt(1 + m11 - m22 - m33)
  143.             local r = 0.5 / s
  144.            
  145.             w = (m32 - m23) * r;
  146.             x = 0.5 * s;
  147.             y = (m21 + m12) * r;
  148.             z = (m13 + m31) * r;
  149.         elseif big == m22 then
  150.             local s = math.sqrt(1 - m11 + m22 - m33)
  151.             local r = 0.5 / s
  152.            
  153.             w = (m13 - m31) * r;
  154.             x = (m21 + m12) * r;
  155.             y = 0.5 * s;
  156.             z = (m32 + m23) * r;
  157.         elseif big == m33 then
  158.             local s = math.sqrt(1 - m11 - m22 + m33)
  159.             local r = 0.5 / s
  160.            
  161.             w = (m21 - m12) * r;
  162.             x = (m13 + m31) * r;
  163.             y = (m32 + m23) * r;
  164.             z = 0.5 * s;
  165.         end
  166.     end
  167.    
  168.     return x, y, z, w
  169. end
  170.  
  171. ---------------------------------------------------------------------------------------------------------------------------------------
  172. -- Serializer
  173. ---------------------------------------------------------------------------------------------------------------------------------------
  174.  
  175. local CFrameSerializer = {}
  176.  
  177. function CFrameSerializer:Encode(cf)
  178.     local pos = cf.Position
  179.    
  180.     local orientId = getOrientId(cf)
  181.     local x, y, z = pos.X, pos.Y, pos.Z
  182.    
  183.     if orientId < 0 then
  184.         local qx, qy, qz, qw = toQuaternion(cf)
  185.         return packNumbers(x, y, z, qx, qy, qz, qw)
  186.     else
  187.         return packNumbers(x, y, z, orientId)
  188.     end
  189. end
  190.  
  191. function CFrameSerializer:Decode(serialCF)
  192.     local data = {}
  193.    
  194.     for str in serialCF:gmatch("[^ ]+") do
  195.         local num = tonumber(str)
  196.         table.insert(data, num)
  197.     end
  198.    
  199.     if #data == 4 then
  200.         local x, y, z, orientId = unpack(data)
  201.         local pos = Vector3.new(x, y, z)
  202.        
  203.         return fromOrientId(pos, orientId)
  204.     else
  205.         return CFrame.new(unpack(data))
  206.     end
  207. end
  208.  
  209. return CFrameSerializer
  210.  
  211. ---------------------------------------------------------------------------------------------------------------------------------------
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement