Hi, I have a question about your 3D render engine. I’ve watched the youtube video that you have linked and I got to the end of the tutorial. The cube does show up on the CanvasDraw screen but the cube is squashed. I’m not sure if you actually based your 3D render engine code on the code of the video that you linked but if you did then maybe you know what I did wrong. Here’s my code and explorer:
--!strict
local ScreenWidth : number = 256
local ScreenHeight : number = 240 --// In tutorial 240
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
local Screen = script.Parent
local CanvasFrame = Screen.CanvasFrame
local CanvasDraw = require(Screen.CanvasDraw)
local VectorModule = require(Screen.VectorModule)
local RunService = game:GetService("RunService")
type vec3d = {["X"] : number, ["Y"] : number, ["Z"] : number, ["Magnitude"] : number}
type pArray = {[number] : vec3d}
type triangle = {["p"] : pArray}
type triangleArray = {[number?] : triangle}
type mesh = {["tris"] : triangleArray}
type matRow = {[number] : number}
type mat4x4 = {[number] : matRow}
--// Construct
local function Construct_vec3d(...) : vec3d
if #{...} == 0 then return VectorModule.zero end
return VectorModule.new(unpack{...})
end
local function Construct_triangle(...) : triangle
local Params = {...}
if #Params == 0 then return {p = {Construct_vec3d(), Construct_vec3d(), Construct_vec3d()}} end
return {p = {Params[1] or Construct_vec3d(), Params[2] or Construct_vec3d(), Params[3] or Construct_vec3d()}}
end
local function Construct_mesh(...) : mesh
local Params = {...}
if #Params == 0 then return {tris = {}} end
return {tris = Params}
end
local function Construct_mat4x4(x : number?) : mat4x4
--// x is a default
local d = 0
if type(x) == "number" then d = x end
local NewMatrix = {
{d, d, d, d};
{d, d, d, d};
{d, d, d, d};
{d, d, d, d};
}
return NewMatrix
end
--// Methods
local function MultiplyMatrixVector(i : vec3d, m : mat4x4) : vec3d
local o : vec3d = Construct_vec3d()
o.X = i.X * m[1][1] + i.Y * m[2][1] + i.Z * m[3][1] + m[4][1]
o.Y = i.X * m[1][2] + i.Y * m[2][2] + i.Z * m[3][1] + m[4][2]
o.Z = i.X * m[1][3] + i.Y * m[2][3] + i.Z * m[3][3] + m[4][3]
local w : number = i.X * m[1][4] + i.Y * m[2][4] + i.Z * m[3][4] + m[4][4]
if w ~= 0 then
o.X /= w
o.Y /= w
o.Z /= w
end
return o
end
local function CopyTable(Target : any) : any
local Result : any = {}
for Key, Value in Target do
Result[Key] = Value
end
return Result
end
local meshCube = Construct_mesh()
local matProj : mat4x4 = Construct_mat4x4()
local function Initialize() : nil --// main
local Resolution = Vector2.new(ScreenWidth, ScreenHeight)
CanvasDraw.CreateCanvas(CanvasFrame, Resolution, Color3.fromRGB(0,0,0), false)
meshCube.tris = {
--// South
Construct_triangle(Construct_vec3d(0, 0, 0), Construct_vec3d(0, 1, 0), Construct_vec3d(1, 1, 0));
Construct_triangle(Construct_vec3d(0, 0, 0), Construct_vec3d(1, 1, 0), Construct_vec3d(1, 0, 0));
--// East
Construct_triangle(Construct_vec3d(1, 0, 0), Construct_vec3d(1, 1, 0), Construct_vec3d(1, 1, 1));
Construct_triangle(Construct_vec3d(1, 0, 0), Construct_vec3d(1, 1, 1), Construct_vec3d(1, 0, 1));
--// North
Construct_triangle(Construct_vec3d(1, 0, 1), Construct_vec3d(1, 1, 1), Construct_vec3d(0, 1, 1));
Construct_triangle(Construct_vec3d(1, 0, 1), Construct_vec3d(0, 1, 1), Construct_vec3d(0, 0, 1));
--// West
Construct_triangle(Construct_vec3d(0, 0, 1), Construct_vec3d(0, 1, 1), Construct_vec3d(0, 1, 0));
Construct_triangle(Construct_vec3d(0, 0, 1), Construct_vec3d(0, 1, 0), Construct_vec3d(0, 0, 0));
--// Top
Construct_triangle(Construct_vec3d(0, 1, 0), Construct_vec3d(0, 1, 1), Construct_vec3d(1, 1, 1));
Construct_triangle(Construct_vec3d(0, 1, 0), Construct_vec3d(1, 1, 1), Construct_vec3d(1, 1, 0));
--// Bottom
Construct_triangle(Construct_vec3d(1, 0, 1), Construct_vec3d(0, 0, 1), Construct_vec3d(0, 0, 0));
Construct_triangle(Construct_vec3d(1, 0, 1), Construct_vec3d(0, 0, 0), Construct_vec3d(1, 0, 0));
}
return
end
--// OnUserCreate
Initialize()
local fNear = 0.1
local fFar = 1000
local fFov = 90
local fAspectRartio = ScreenHeight / ScreenWidth
local fFovRad = 1 / math.tan(math.rad(fFov * 0.5))
local fTheta = 0
matProj[1][1] = fAspectRartio * fFovRad
matProj[2][2] = fFovRad
matProj[3][3] = fFar / (fFar - fNear)
matProj[4][3] = (-fFar * fNear) / (fFar - fNear)
matProj[3][4] = 1
matProj[4][4] = 0
RunService.RenderStepped:Connect(function() --// OnUserUpdate
CanvasDraw.ClearCanvas()
local matRotZ : mat4x4, matRotX : mat4x4 = Construct_mat4x4(), Construct_mat4x4()
fTheta += 0.01
--// Rotation Z
matRotZ[1][1] = math.cos(fTheta)
matRotZ[1][2] = math.sin(fTheta)
matRotZ[2][1] = -math.sin(fTheta)
matRotZ[2][2] = math.cos(fTheta)
matRotZ[3][3] = 1
matRotZ[4][4] = 1
--// Rotation X
matRotX[1][1] = 1
matRotX[2][2] = math.cos(fTheta * 0.5)
matRotX[2][3] = math.sin(fTheta * 0.5)
matRotX[3][2] = -math.sin(fTheta * 0.5)
matRotX[3][3] = math.cos(fTheta * 0.5)
matRotX[4][4] = 1
--// Draw Triangles
for _, tri : triangle in meshCube.tris do
local triProjected : triangle = Construct_triangle()
local triRotatedZ : triangle = Construct_triangle()
local triRotatedZX : triangle = Construct_triangle()
triRotatedZ.p[1] = MultiplyMatrixVector(tri.p[1], matRotZ)
triRotatedZ.p[2] = MultiplyMatrixVector(tri.p[2], matRotZ)
triRotatedZ.p[3] = MultiplyMatrixVector(tri.p[3], matRotZ)
triRotatedZX.p[1] = MultiplyMatrixVector(triRotatedZ.p[1], matRotX)
triRotatedZX.p[2] = MultiplyMatrixVector(triRotatedZ.p[2], matRotX)
triRotatedZX.p[3] = MultiplyMatrixVector(triRotatedZ.p[3], matRotX)
local triTranslated : triangle = Construct_triangle(
Construct_vec3d(triRotatedZX.p[1].X, triRotatedZX.p[1].Y, triRotatedZX.p[1].Z),
Construct_vec3d(triRotatedZX.p[2].X, triRotatedZX.p[2].Y, triRotatedZX.p[2].Z),
Construct_vec3d(triRotatedZX.p[3].X, triRotatedZX.p[3].Y, triRotatedZX.p[3].Z)
)
triTranslated.p[1].Z = triRotatedZX.p[1].Z + 3
triTranslated.p[2].Z = triRotatedZX.p[2].Z + 3
triTranslated.p[3].Z = triRotatedZX.p[3].Z + 3
triProjected.p[1] = MultiplyMatrixVector(triTranslated.p[1], matProj)
triProjected.p[2] = MultiplyMatrixVector(triTranslated.p[2], matProj)
triProjected.p[3] = MultiplyMatrixVector(triTranslated.p[3], matProj)
--// Scale into view
triProjected.p[1].X += 1; triProjected.p[1].Y += 1
triProjected.p[2].X += 1; triProjected.p[2].Y += 1
triProjected.p[3].X += 1; triProjected.p[3].Y += 1
triProjected.p[1].X *= 0.5 * ScreenWidth; triProjected.p[1].Y *= 0.5 * ScreenHeight
triProjected.p[2].X *= 0.5 * ScreenWidth; triProjected.p[2].Y *= 0.5 * ScreenHeight
triProjected.p[3].X *= 0.5 * ScreenWidth; triProjected.p[3].Y *= 0.5 * ScreenHeight
CanvasDraw.DrawTriangle(Vector2.new(triProjected.p[1].X , triProjected.p[1].Y ), -- You have to shift everything by 1 on both axes because the canvas ...
-- actually starts on 1, 1 and not 0, 0 :(
Vector2.new(triProjected.p[2].X , triProjected.p[2].Y ),
Vector2.new(triProjected.p[3].X , triProjected.p[3].Y ),
Color3.fromRGB(255,255,255), false
)
end
end)
Beautiful video of a squashed cube: