-- 3D Cube Rotation -- http://www.speich.net/computer/moztesting/3d.htm -- Created by Simon Speich local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../../bench_support") function test() local Q = {} local MTrans = {}; -- transformation matrix local MQube = {} -- position information of qube local I = {} -- entity matrix local Origin = {} local Testing = {} local LoopTimer; local validation = { [20] = 2889, [40] = 2889, [80] = 2889, [160] = 2889 }; local DisplArea = {} DisplArea.Width = 300; DisplArea.Height = 300; local function DrawLine(From, To) local x1 = From.V[1]; local x2 = To.V[1]; local y1 = From.V[2]; local y2 = To.V[2]; local dx = math.abs(x2 - x1); local dy = math.abs(y2 - y1); local x = x1; local y = y1; local IncX1, IncY1; local IncX2, IncY2; local Den; local Num; local NumAdd; local NumPix; if (x2 >= x1) then IncX1 = 1; IncX2 = 1; else IncX1 = -1; IncX2 = -1; end if (y2 >= y1) then IncY1 = 1; IncY2 = 1; else IncY1 = -1; IncY2 = -1; end if (dx >= dy) then IncX1 = 0; IncY2 = 0; Den = dx; Num = dx / 2; NumAdd = dy; NumPix = dx; else IncX2 = 0; IncY1 = 0; Den = dy; Num = dy / 2; NumAdd = dx; NumPix = dy; end NumPix = math.floor(Q.LastPx + NumPix + 0.5); local i = Q.LastPx; while i < NumPix do Num = Num + NumAdd; if (Num >= Den) then Num = Num - Den; x = x + IncX1; y = y + IncY1; end x = x + IncX2; y = y + IncY2; i = i + 1; end Q.LastPx = NumPix; end local function CalcCross(V0, V1) local Cross = {}; Cross[1] = V0[2]*V1[3] - V0[3]*V1[2]; Cross[2] = V0[3]*V1[1] - V0[1]*V1[3]; Cross[3] = V0[1]*V1[2] - V0[2]*V1[1]; return Cross; end local function CalcNormal(V0, V1, V2) local A = {}; local B = {}; for i = 1,3 do A[i] = V0[i] - V1[i]; B[i] = V2[i] - V1[i]; end A = CalcCross(A, B); local Length = math.sqrt(A[1]*A[1] + A[2]*A[2] + A[3]*A[3]); for i = 1,3 do A[i] = A[i] / Length; end A[4] = 1; return A; end local function CreateP(X,Y,Z) local result = {} result.V = {X,Y,Z,1}; return result end -- multiplies two matrices local function MMulti(M1, M2) local M = {{},{},{},{}}; for i = 1,4 do for j = 1,4 do M[i][j] = M1[i][1] * M2[1][j] + M1[i][2] * M2[2][j] + M1[i][3] * M2[3][j] + M1[i][4] * M2[4][j]; end end return M; end -- multiplies matrix with vector local function VMulti(M, V) local Vect = {}; for i = 1,4 do Vect[i] = M[i][1] * V[1] + M[i][2] * V[2] + M[i][3] * V[3] + M[i][4] * V[4]; end return Vect; end local function VMulti2(M, V) local Vect = {}; for i = 1,3 do Vect[i] = M[i][1] * V[1] + M[i][2] * V[2] + M[i][3] * V[3]; end return Vect; end -- add to matrices local function MAdd(M1, M2) local M = {{},{},{},{}}; for i = 1,4 do for j = 1,4 do M[i][j] = M1[i][j] + M2[i][j]; end end return M; end local function Translate(M, Dx, Dy, Dz) local T = { {1,0,0,Dx}, {0,1,0,Dy}, {0,0,1,Dz}, {0,0,0,1} }; return MMulti(T, M); end local function RotateX(M, Phi) local a = Phi; a = a * math.pi / 180; local Cos = math.cos(a); local Sin = math.sin(a); local R = { {1,0,0,0}, {0,Cos,-Sin,0}, {0,Sin,Cos,0}, {0,0,0,1} }; return MMulti(R, M); end local function RotateY(M, Phi) local a = Phi; a = a * math.pi / 180; local Cos = math.cos(a); local Sin = math.sin(a); local R = { {Cos,0,Sin,0}, {0,1,0,0}, {-Sin,0,Cos,0}, {0,0,0,1} }; return MMulti(R, M); end local function RotateZ(M, Phi) local a = Phi; a = a * math.pi / 180; local Cos = math.cos(a); local Sin = math.sin(a); local R = { {Cos,-Sin,0,0}, {Sin,Cos,0,0}, {0,0,1,0}, {0,0,0,1} }; return MMulti(R, M); end local function DrawQube() -- calc current normals local CurN = {}; local i = 5; Q.LastPx = 0; while i > -1 do CurN[i+1] = VMulti2(MQube, Q.Normal[i+1]); i = i - 1 end if (CurN[1][3] < 0) then if (not Q.Line[1]) then DrawLine(Q[1], Q[2]); Q.Line[1] = true; end if (not Q.Line[2]) then DrawLine(Q[2], Q[3]); Q.Line[2] = true; end if (not Q.Line[3]) then DrawLine(Q[3], Q[4]); Q.Line[3] = true; end if (not Q.Line[4]) then DrawLine(Q[4], Q[1]); Q.Line[4] = true; end end if (CurN[2][3] < 0) then if (not Q.Line[3]) then DrawLine(Q[4], Q[3]); Q.Line[3] = true; end if (not Q.Line[10]) then DrawLine(Q[3], Q[7]); Q.Line[10] = true; end if (not Q.Line[7]) then DrawLine(Q[7], Q[8]); Q.Line[7] = true; end if (not Q.Line[11]) then DrawLine(Q[8], Q[4]); Q.Line[11] = true; end end if (CurN[3][3] < 0) then if (not Q.Line[5]) then DrawLine(Q[5], Q[6]); Q.Line[6] = true; end if (not Q.Line[6]) then DrawLine(Q[6], Q[7]); Q.Line[6] = true; end if (not Q.Line[7]) then DrawLine(Q[7], Q[8]); Q.Line[7] = true; end if (not Q.Line[8]) then DrawLine(Q[8], Q[5]); Q.Line[8] = true; end end if (CurN[4][3] < 0) then if (not Q.Line[5]) then DrawLine(Q[5], Q[6]); Q.Line[5] = true; end if (not Q.Line[9]) then DrawLine(Q[6], Q[2]); Q.Line[9] = true; end if (not Q.Line[1]) then DrawLine(Q[2], Q[1]); Q.Line[1] = true; end if (not Q.Line[12]) then DrawLine(Q[1], Q[5]); Q.Line[12] = true; end end if (CurN[5][3] < 0) then if (not Q.Line[12]) then DrawLine(Q[5], Q[1]); Q.Line[12] = true; end if (not Q.Line[4]) then DrawLine(Q[1], Q[4]); Q.Line[4] = true; end if (not Q.Line[11]) then DrawLine(Q[4], Q[8]); Q.Line[11] = true; end if (not Q.Line[8]) then DrawLine(Q[8], Q[5]); Q.Line[8] = true; end end if (CurN[6][3] < 0) then if (not Q.Line[9]) then DrawLine(Q[2], Q[6]); Q.Line[9] = true; end if (not Q.Line[6]) then DrawLine(Q[6], Q[7]); Q.Line[6] = true; end if (not Q.Line[10]) then DrawLine(Q[7], Q[3]); Q.Line[10] = true; end if (not Q.Line[2]) then DrawLine(Q[3], Q[2]); Q.Line[2] = true; end end Q.Line = {false,false,false,false,false,false,false,false,false,false,false,false} Q.LastPx = 0; end local function Loop() if (Testing.LoopCount > Testing.LoopMax) then return; end local TestingStr = tostring(Testing.LoopCount); while (#TestingStr < 3) do TestingStr = "0" .. TestingStr; end MTrans = Translate(I, -Q[9].V[1], -Q[9].V[2], -Q[9].V[3]); MTrans = RotateX(MTrans, 1); MTrans = RotateY(MTrans, 3); MTrans = RotateZ(MTrans, 5); MTrans = Translate(MTrans, Q[9].V[1], Q[9].V[2], Q[9].V[3]); MQube = MMulti(MTrans, MQube); local i = 8; while i > -1 do Q[i+1].V = VMulti(MTrans, Q[i+1].V); i = i - 1 end DrawQube(); Testing.LoopCount = Testing.LoopCount + 1; Loop(); end local function Init(CubeSize) -- init/reset vars Origin.V = {150,150,20,1}; Testing.LoopCount = 0; Testing.LoopMax = 50; Testing.TimeMax = 0; Testing.TimeAvg = 0; Testing.TimeMin = 0; Testing.TimeTemp = 0; Testing.TimeTotal = 0; Testing.Init = false; -- transformation matrix MTrans = { {1,0,0,0}, {0,1,0,0}, {0,0,1,0}, {0,0,0,1} }; -- position information of qube MQube = { {1,0,0,0}, {0,1,0,0}, {0,0,1,0}, {0,0,0,1} }; -- entity matrix I = { {1,0,0,0}, {0,1,0,0}, {0,0,1,0}, {0,0,0,1} }; -- create qube Q[1] = CreateP(-CubeSize,-CubeSize, CubeSize); Q[2] = CreateP(-CubeSize, CubeSize, CubeSize); Q[3] = CreateP( CubeSize, CubeSize, CubeSize); Q[4] = CreateP( CubeSize,-CubeSize, CubeSize); Q[5] = CreateP(-CubeSize,-CubeSize,-CubeSize); Q[6] = CreateP(-CubeSize, CubeSize,-CubeSize); Q[7] = CreateP( CubeSize, CubeSize,-CubeSize); Q[8] = CreateP( CubeSize,-CubeSize,-CubeSize); -- center of gravity Q[9] = CreateP(0, 0, 0); -- anti-clockwise edge check Q.Edge = {{1,2,3},{4,5,7},{8,7,6},{5,6,2},{5,1,4},{2,6,7}}; -- calculate squad normals Q.Normal = {}; for i = 1,#Q.Edge do Q.Normal[i] = CalcNormal(Q[Q.Edge[i][1]].V, Q[Q.Edge[i][2]].V, Q[Q.Edge[i][3]].V); end -- line drawn ? Q.Line = {false,false,false,false,false,false,false,false,false,false,false,false}; -- create line pixels Q.NumPx = 9 * 2 * CubeSize; for i = 1,Q.NumPx do CreateP(0,0,0); end MTrans = Translate(MTrans, Origin.V[1], Origin.V[2], Origin.V[3]); MQube = MMulti(MTrans, MQube); local i = 0; while i < 9 do Q[i+1].V = VMulti(MTrans, Q[i+1].V); i = i + 1 end DrawQube(); Testing.Init = true; Loop(); -- Perform a simple sum-based verification. local sum = 0; for i = 1,#Q do local vector = Q[i].V; for j = 1,#vector do sum = sum + vector[j]; end end if (math.floor(sum) ~= validation[CubeSize]) then assert(false, "Error: bad vector sum for CubeSize = " .. CubeSize .. "; expected " .. validation[CubeSize] .. " but got " .. math.floor(sum)) end end local i = 20 while i <= 160 do Init(i); i = i * 2 end Q = nil; MTrans = nil; MQube = nil; I = nil; Origin = nil; Testing = nil; LoopTime = nil; DisplArea = nil; end bench.runCode(test, "3d-cube")