-- [[ -- Author: Alepacho -- ]] local Window = require("window") local Object = require("object") function clamp(val, min, max) return math.min(max, math.max(val, min)) end -- 4x4 Matrix local Matrix = Object:extend() do -- [[ -- [ 1, 2, 3, 4] | [ 11, 12, 13, 14] -- [ 5, 6, 7, 8] | [ 21, 22, 23, 24] -- [ 9, 10, 11, 12] | [ 31, 32, 33, 34] -- [13, 14, 15, 16] | [ 41, 42, 43, 44] -- ]] function Matrix:constructor() -- correct ratio self.ratio = 2 end function Matrix:create(d) local m = {} if not d then m = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 } else local i, j for j = 1, 4 do for i = 1, 4 do m[(j-1)*4+i] = d[(j-1)*4+i] end end end return m end function Matrix:proj(fov, near, far) local m = self:create() local scale = 1 / math.tan((fov / 2) * 0.5 * math.pi / 180) m[ 1] = scale * self.ratio m[ 6] = scale m[11] = -far / (far - near) m[15] = -far * near / (far - near) m[12] = -1 m[16] = 0 return m end function Matrix:print(m, noprint) if not m then print('nil') return 'nil' end local i, j local s = '' for j = 1, 4 do s = s..'[' for i = 1, 3 do s = s..m[(j-1)*4+i]..', ' end s = s..m[(j-1)*4+4]..']' if not noprint then print(s) end s = s..'\n' end return s end function Matrix:translate(v, m) if not m then m = self:create() end m[ 4] = v.x m[ 8] = v.y m[12] = v.z return m end function Matrix:scale(v, m) if not m then m = self:create() end m[ 1] = - v.x m[ 6] = - v.y m[11] = v.z return m end function Matrix:rotate(axis, angle, m) local a = math.rad(angle) local x, y, z, c, s = axis.x, axis.y, axis.z, math.cos(a), math.sin(a) if not m then m = self:create() end m[ 1] = (c + x*x * (1-c) ) m[ 2] = ( y*x * (1-c) + z * s) m[ 3] = ( z*x * (1-c) - y * s) m[ 5] = ( x*y * (1-c) - z * s) m[ 6] = (c + y*y * (1-c) ) m[ 7] = ( z*y * (1-c) + x * s) m[ 9] = ( x*z * (1-c) + y * s) m[10] = ( y*z * (1-c) - x * s) m[11] = (c + z*z * (1-c) ) return m end function Matrix:multiply(a, b) if not b then b = self:create() end local i, j, k local c = self:create() local sum for i = 1, 4 do for j = 1, 4 do sum = 0 for k = 1, 4 do sum = sum + a[(k-1)*4+i] * b[(j-1)*4+k] end c[(j-1)*4+i] = sum end end return c end function Matrix:mul2vec(v, m) local a, b, c, w if not m then m = self:create() end a = v.x * m[ 1] + v.y * m[ 2] + v.z * m[ 3] + m[ 4] b = v.x * m[ 5] + v.y * m[ 6] + v.z * m[ 7] + m[ 8] c = v.x * m[ 9] + v.y * m[10] + v.z * m[11] + m[12] w = v.x * m[13] + v.y * m[14] + v.z * m[15] + m[16] return { x = a / w, y = b / w, z = c / w } end end local color = { gray = 0, black = 1, white = 2, aqua = 3, red = 4, green = 5, blue = 6, blown = 7 } local vertices = { { x = -1, y = -1, z = -1 }, -- 1 2-----------6 { x = -1, y = -1, z = 1 }, -- 2 /| /| { x = -1, y = 1, z = 1 }, -- 3 1-----------7 | { x = 1, y = 1, z = -1 }, -- 4 | | | | { x = -1, y = 1, z = -1 }, -- 5 | | | | { x = 1, y = -1, z = 1 }, -- 6 | 3_________|_8 { x = 1, y = -1, z = -1 }, -- 7 | / |/ { x = 1, y = 1, z = 1 }, -- 8 5/__________4 } local data = { 2, 8, 6, color.blue, -- back 2, 3, 8, color.blue, 1, 7, 5, color.red, -- front 4, 5, 7, color.red, 2, 6, 1, color.blown, -- top 7, 1, 6, color.blown, 3, 5, 4, color.green, -- bottom 3, 4, 8, color.green, 7, 6, 4, color.white, -- right 8, 4, 6, color.white, 2, 1, 3, color.black, -- left 5, 3, 1, color.black, } local matrix = Matrix:new() local m = matrix:create() local r = matrix:create() local p = matrix:proj(90, 0.01, 100) function _init() vieww, viewh = view.size() local initw, inith = 110, 60 win = Window:new("3D Cube", vieww / 2 - initw / 2, viewh / 2 - inith / 2, initw, inith) win.resizable = true win.onclose = function() sys.exit() end sys.stepinterval(0) end function _step(t) win:step(t) local ww, hh = view.size(win.mainvp) view.active(win.mainvp) local rs = t / 24 r = matrix:rotate({x = 0, y = 1, z = 0}, rs) r = matrix:multiply(matrix:rotate({x = 0, y = 1, z = 0}, rs), r) r = matrix:multiply(matrix:rotate({x = 0, y = 0, z = 1}, rs), r) m = matrix:create() local scale = 10-- + math.sin(t / 1000) * 2.5 m = matrix:scale({x = scale, y = scale, z = scale}, m) m = matrix:multiply(m, r) m = matrix:translate({x = 0, y = 0, z = 200}, m) _render(t) end function _render(t) gfx.cls() local i local scale = 20 local ww, wh = view.size(win.mainvp) local cw, ch = ww / 2, wh / 2 for i = 1, #data, 4 do local v1 = vertices[data[i ]] local v2 = vertices[data[i+1]] local v3 = vertices[data[i+2]] local tc = data[i+3] -- apply projection matrix local mm = matrix:multiply(m, p) v1 = matrix:mul2vec(v1, mm) v2 = matrix:mul2vec(v2, mm) v3 = matrix:mul2vec(v3, mm) local backface = ((v2.x - v1.x) * (v3.y - v2.y)) - ((v2.y - v1.y) * (v3.x - v2.x)) if backface >= 0 then v1.x = clamp(v1.x, -ww, ww); v2.x = clamp(v2.x, -ww, ww); v3.x = clamp(v3.x, -ww, ww); v1.y = clamp(v1.y, -wh, wh); v2.y = clamp(v2.y, -wh, wh); v3.y = clamp(v3.y, -wh, wh); gfx.fgcolor(tc) gfx.tri(cw + v1.x, ch + v1.y, cw + v2.x, ch + v2.y, cw + v3.x, ch + v3.y) end end end