mirror of
https://github.com/luau-lang/luau.git
synced 2024-11-16 06:45:44 +08:00
d01addc625
Co-authored-by: Rodactor <rodactor@roblox.com>
382 lines
8.8 KiB
Lua
382 lines
8.8 KiB
Lua
-- 3D Cube Rotation
|
|
-- http://www.speich.net/computer/moztesting/3d.htm
|
|
-- Created by Simon Speich
|
|
|
|
local bench = script and require(script.Parent.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;
|
|
|
|
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
|
|
|
|
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
|
|
|
|
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
|
|
|
|
function CreateP(X,Y,Z)
|
|
local result = {}
|
|
result.V = {X,Y,Z,1};
|
|
return result
|
|
end
|
|
|
|
-- multiplies two matrices
|
|
function MMulti(M1, M2)
|
|
local M = {{},{},{},{}};
|
|
local i = 1;
|
|
local j = 1;
|
|
while i <= 4 do
|
|
j = 1;
|
|
while j <= 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]; j = j + 1
|
|
end
|
|
|
|
i = i + 1
|
|
end
|
|
return M;
|
|
end
|
|
|
|
-- multiplies matrix with vector
|
|
function VMulti(M, V)
|
|
local Vect = {};
|
|
local i = 1;
|
|
while i <= 4 do Vect[i] = M[i][1] * V[1] + M[i][2] * V[2] + M[i][3] * V[3] + M[i][4] * V[4]; i = i + 1 end
|
|
return Vect;
|
|
end
|
|
|
|
function VMulti2(M, V)
|
|
local Vect = {};
|
|
local i = 1;
|
|
while i < 4 do Vect[i] = M[i][1] * V[1] + M[i][2] * V[2] + M[i][3] * V[3]; i = i + 1 end
|
|
return Vect;
|
|
end
|
|
|
|
-- add to matrices
|
|
function MAdd(M1, M2)
|
|
local M = {{},{},{},{}};
|
|
local i = 1;
|
|
local j = 1;
|
|
while i <= 4 do
|
|
j = 1;
|
|
while j <= 4 do M[i][j] = M1[i][j] + M2[i][j]; j = j + 1 end
|
|
|
|
i = i + 1
|
|
end
|
|
return M;
|
|
end
|
|
|
|
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
|
|
|
|
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
|
|
|
|
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
|
|
|
|
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
|
|
|
|
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
|
|
|
|
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
|
|
|
|
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")
|