Created
January 25, 2020 23:52
-
-
Save Yevano/a48ab1cc592cccda55cef1933c73e179 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| -- Rev 2 | |
| local sin = math.sin | |
| local cos = math.cos | |
| local tan = math.tan | |
| local abs = math.abs | |
| local min = math.min | |
| local max = math.max | |
| local floor = math.floor | |
| local sqrt = math.sqrt | |
| local clock = os.clock | |
| local term_setCursorPos = term.setCursorPos | |
| local term_blit = term.blit | |
| local table_concat = table.concat | |
| local STRATEGY_FULL_CELL = 0 | |
| local STRATEGY_HEX_CELL = 1 | |
| local STRATEGY_TWO_CELL = 2 | |
| local function vec2(x, y) | |
| return { x = x, y = y } | |
| end | |
| local function vec3(x, y, z) | |
| return { x = x, y = y, z = z } | |
| end | |
| local function vec4(x, y, z, w) | |
| return { x = x, y = y, z = z, w = w } | |
| end | |
| local function length2(v) | |
| return (v.x^2 + v.y^2)^0.5 | |
| end | |
| local function length3(v) | |
| return (v.x^2 + v.y^2 + v.z^2)^0.5 | |
| end | |
| local function length4(v) | |
| return (v.x^2 + v.y^2 + v.z^2 + v.w^2)^0.5 | |
| end | |
| local function normalize2(v) | |
| return div2s(v, length2(v)) | |
| end | |
| local function normalize3(v) | |
| return div3s(v, length3(v)) | |
| end | |
| local function normalize4(v) | |
| return div4s(v, length4(v)) | |
| end | |
| local function distance2(v1, v2) | |
| return ((v1.x - v2.x)^2 + (v1.y - v2.y)^2)^0.5 | |
| end | |
| local function distance3(v1, v2) | |
| return ((v1.x - v2.x)^2 + (v1.y - v2.y)^2 + (v1.z - v2.z)^2)^0.5 | |
| end | |
| local function distance4(v1, v2) | |
| return ((v1.x - v2.x)^2 + (v1.y - v2.y)^2 + (v1.z - v2.z)^2 + (v1.w - v2.w)^2)^0.5 | |
| end | |
| local function dot2(v1, v2) | |
| return v1.x*v2.x + v1.y*v2.y | |
| end | |
| local function dot3(v1, v2) | |
| return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z | |
| end | |
| local function dot4(v1, v2) | |
| return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z + v1.w*v2.w | |
| end | |
| local function add2(v1, v2) | |
| return vec2(v1.x+v2.x, v1.y+v2.y) | |
| end | |
| local function add3(v1, v2) | |
| return vec3(v1.x+v2.x, v1.y+v2.y, v1.z+v2.z) | |
| end | |
| local function add4(v1, v2) | |
| return vec4(v1.x+v2.x, v1.y+v2.y, v1.z+v2.z, v1.w+v2.w) | |
| end | |
| local function sub2(v1, v2) | |
| return vec2(v1.x-v2.x, v1.y-v2.y) | |
| end | |
| local function sub3(v1, v2) | |
| return vec3(v1.x-v2.x, v1.y-v2.y, v1.z-v2.z) | |
| end | |
| local function sub4(v1, v2) | |
| return vec4(v1.x-v2.x, v1.y-v2.y, v1.z-v2.z, v1.w-v2.w) | |
| end | |
| local function mul2(v1, v2) | |
| return vec2(v1.x*v2.x, v1.y*v2.y) | |
| end | |
| local function mul3(v1, v2) | |
| return vec3(v1.x*v2.x, v1.y*v2.y, v1.z*v2.z) | |
| end | |
| local function mul4(v1, v2) | |
| return vec4(v1.x*v2.x, v1.y*v2.y, v1.z*v2.z, v1.w*v2.w) | |
| end | |
| local function div2(v1, v2) | |
| return vec2(v1.x/v2.x, v1.y/v2.y) | |
| end | |
| local function div3(v1, v2) | |
| return vec3(v1.x/v2.x, v1.y/v2.y, v1.z/v2.z) | |
| end | |
| local function div4(v1, v2) | |
| return vec4(v1.x/v2.x, v1.y/v2.y, v1.z/v2.z, v1.w/v2.w) | |
| end | |
| local function add2s(v1, n) | |
| return vec2(v1.x+n, v1.y+n) | |
| end | |
| local function add3s(v1, n) | |
| return vec3(v1.x+n, v1.y+n, v1.z+n) | |
| end | |
| local function add4s(v1, n) | |
| return vec4(v1.x+n, v1.y+n, v1.z+n, v1.w+n) | |
| end | |
| local function sub2s(v1, n) | |
| return vec2(v1.x-n, v1.y-n) | |
| end | |
| local function sub3s(v1, n) | |
| return vec3(v1.x-n, v1.y-n, v1.z-n) | |
| end | |
| local function sub4s(v1, n) | |
| return vec4(v1.x-n, v1.y-n, v1.z-n, v1.w-n) | |
| end | |
| local function mul2s(v1, n) | |
| return vec2(v1.x*n, v1.y*n) | |
| end | |
| local function mul3s(v1, n) | |
| return vec3(v1.x*n, v1.y*n, v1.z*n) | |
| end | |
| local function mul4s(v1, n) | |
| return vec4(v1.x*n, v1.y*n, v1.z*n, v1.w*n) | |
| end | |
| local function div2s(v1, n) | |
| return vec2(v1.x/n, v1.y/n) | |
| end | |
| local function div3s(v1, n) | |
| return vec3(v1.x/n, v1.y/n, v1.z/n) | |
| end | |
| local function div4s(v1, n) | |
| return vec4(v1.x/n, v1.y/n, v1.z/n, v1.w/n) | |
| end | |
| local function mat2mulvec2(m, v) | |
| return vec2(m[1] * v.x + m[2] * v.y, | |
| m[3] * v.x + m[4] * v.y) | |
| end | |
| local function mat3mulvec3(m, v) | |
| return vec3(m[1] * v.x + m[2] * v.y + m[3] * v.z, | |
| m[4] * v.x + m[5] * v.y + m[6] * v.z, | |
| m[7] * v.x + m[8] * v.y + m[9] * v.z) | |
| end | |
| local function getRotMat2(a) | |
| return { cos(a), -sin(a), sin(a), cos(a) } | |
| end | |
| local function getRotMat3x(a) | |
| return { 1, 0, 0, | |
| 0, cos(a), -sin(a), | |
| 0, sin(a), cos(a) } | |
| end | |
| local function getRotMat3y(a) | |
| return { cos(a), 0, sin(a), | |
| 0, 1, 0, | |
| -sin(a), 0, cos(a) } | |
| end | |
| local function getRotMat3z(a) | |
| return { cos(a), -sin(a), 0, | |
| sin(a), cos(a), 0, | |
| 0, 0, 1 } | |
| end | |
| local function fract(n) | |
| return n - floor(n) | |
| end | |
| local function mix(x, y, a) | |
| return x * (1 - a) + y * a | |
| end | |
| local function clamp(a, s, l) | |
| return a < s and s or a > l and l or a | |
| end | |
| local function smoothstep(edge0, edge1, x) | |
| local t = clamp((x - edge0) / (edge1 - edge0), 0, 1) | |
| return t * t * (3 - 2 * t) | |
| end | |
| local function lab2xyz(lab) | |
| local y = (lab.x + 16.0) / 116.0 | |
| local y3 = y^3.0 | |
| local x = (lab.y / 500.0) + y | |
| local x3 = x^3.0 | |
| local z = y - (lab.z / 200.0) | |
| local z3 = z^3.0 | |
| if y3 > 0.008856 then | |
| y = y3 | |
| else | |
| y = (y - (16.0 / 116.0)) / 7.787 | |
| end | |
| if x3 > 0.008856 then | |
| x = x3 | |
| else | |
| x = (x - (16.0 / 116.0)) / 7.787 | |
| end | |
| if z3 > 0.008856 then | |
| z = z3 | |
| else | |
| z = (z - (16.0 / 116.0)) / 7.787 | |
| end | |
| return vec3(x * 95.0429, y * 100.0, z * 108.8900) | |
| end | |
| local Mi = {{ 3.2406, -1.5372, -0.4986}, | |
| {-0.9689, 1.8758, 0.0415}, | |
| { 0.0557, -0.2040, 1.0570}} | |
| local function xyz2rgb(xyz) | |
| local x = xyz.x / 100.0 | |
| local y = xyz.y / 100.0 | |
| local z = xyz.z / 100.0 | |
| local r = (x * Mi[1][1]) + (y * Mi[1][2]) + (z * Mi[1][3]) | |
| local g = (x * Mi[2][1]) + (y * Mi[2][2]) + (z * Mi[2][3]) | |
| local b = (x * Mi[3][1]) + (y * Mi[3][2]) + (z * Mi[3][3]) | |
| -- assume sRGB | |
| if r > 0.0031308 then | |
| r = ((1.055 * r^(1.0 / 2.4)) - 0.055) | |
| else | |
| r = (r * 12.92) | |
| end | |
| if g > 0.0031308 then | |
| g = ((1.055 * g^(1.0 / 2.4)) - 0.055) | |
| else | |
| g = (g * 12.92) | |
| end | |
| if b > 0.0031308 then | |
| b = ((1.055 * b^(1.0 / 2.4)) - 0.055) | |
| else | |
| b = (b * 12.92) | |
| end | |
| r = (r < 0) and 0 or r | |
| g = (g < 0) and 0 or g | |
| b = (b < 0) and 0 or b | |
| return vec3(r, g, b) | |
| end | |
| local function lab2rgb(lab) | |
| return xyz2rgb(lab2xyz(lab)) | |
| end | |
| local function rgb2lab(color) | |
| -- Original code: http://www.brucelindbloom.com | |
| -- Ported to Lua by Yevano. | |
| local r, g, b = color.x, color.y, color.z | |
| local X, Y, Z, fx, fy, fz, xr, yr, z | |
| local Ls, as, bs | |
| local eps = 216 / 24389 | |
| local k = 24389 / 27 | |
| local Xr = 0.964221 -- reference white D50 | |
| local Yr = 1 | |
| local Zr = 0.825211 | |
| -- assuming sRGB (D65) | |
| if r <= 0.04045 then | |
| r = r / 12 | |
| else | |
| r = ((r + 0.055) / 1.055)^2.4 | |
| end | |
| if g <= 0.04045 then | |
| g = g / 12 | |
| else | |
| g = ((g + 0.055) / 1.055)^2.4 | |
| end | |
| if b <= 0.04045 then | |
| b = b / 12 | |
| else | |
| b = ((b + 0.055) / 1.055)^2.4 | |
| end | |
| X = 0.436052025 * r + 0.385081593 * g + 0.143087414 * b | |
| Y = 0.222491598 * r + 0.71688606 * g + 0.060621486 * b | |
| Z = 0.013929122 * r + 0.097097002 * g + 0.71418547 * b | |
| -- XYZ to Lab | |
| xr = X / Xr | |
| yr = Y / Yr | |
| zr = Z / Zr | |
| if xr > eps then | |
| fx = xr^(1 / 3) | |
| else | |
| fx = ((k * xr + 16.) / 116.) | |
| end | |
| if yr > eps then | |
| fy = yr^(1 / 3) | |
| else | |
| fy = ((k * yr + 16.) / 116.) | |
| end | |
| if zr > eps then | |
| fz = zr^(1 / 3) | |
| else | |
| fz = ((k * zr + 16.) / 116) | |
| end | |
| Ls = (116 * fy) - 16 | |
| as = 500 * (fx - fy) | |
| bs = 200 * (fy - fz) | |
| return vec3(2.55 * Ls + .5, as + .5, bs + .5) | |
| end | |
| local function colorDistance(c1, c2) | |
| local lab1 = rgb2lab(c1) | |
| local lab2 = rgb2lab(c2) | |
| return distance3(lab1, lab2) | |
| end | |
| local function createPalette(indexTable, redPal, greenPal, bluePal, ldFunc) | |
| ldFunc = ldFunc or function() return 0.1 end | |
| local colorTable = { } | |
| local pal = { | |
| indexTable = indexTable, | |
| colorTable = colorTable, | |
| redPal = redPal, | |
| greenPal = greenPal, | |
| bluePal = bluePal | |
| } | |
| local s = os.clock() | |
| for r = 0, redPal - 1 do | |
| colorTable[r] = { } | |
| for g = 0, greenPal - 1 do | |
| colorTable[r][g] = { } | |
| for b = 0, bluePal - 1 do | |
| local minDist = 9999 | |
| local entry | |
| for i = 1, #indexTable do | |
| local dist = colorDistance(vec3(r/(redPal-1), g/(greenPal-1), b/(bluePal-1)), indexTable[i]) | |
| if dist < minDist then | |
| minDist = dist | |
| entry = i | |
| end | |
| end | |
| colorTable[r][g][b] = entry | |
| end | |
| end | |
| if os.clock() - s > ldFunc((r*greenPal*bluePal)/(redPal*greenPal*bluePal)) then | |
| sleep(0) | |
| s = os.clock() | |
| end | |
| end | |
| return pal | |
| end | |
| local function paletteConvert(palette, color) | |
| local x = color.x < 0 and 0 or color.x > 1 and 1 or color.x | |
| local y = color.y < 0 and 0 or color.y > 1 and 1 or color.y | |
| local z = color.z < 0 and 0 or color.z > 1 and 1 or color.z | |
| local r = floor(x * palette.redPal) | |
| local g = floor(y * palette.greenPal) | |
| local b = floor(z * palette.bluePal) | |
| if r == palette.redPal then r = r - 1 end | |
| if g == palette.greenPal then g = g - 1 end | |
| if b == palette.bluePal then b = b - 1 end | |
| local i = palette.colorTable[r][g][b] | |
| return i | |
| end | |
| local function createShaderProgram() | |
| local prog = { | |
| shaders = { } | |
| } | |
| return prog | |
| end | |
| local function addShader(program, shader) | |
| program.shaders[#program.shaders + 1] = shader | |
| end | |
| local function applyShaderProgram(program, frameBuffer) | |
| local pixelData = frameBuffer.pixelData | |
| local w = frameBuffer.size.x | |
| local h = frameBuffer.size.y | |
| for i = 1, #program.shaders do | |
| local shaderFunc = program.shaders[i].shaderFunc | |
| local inputs = program.shaders[i].inputs | |
| for x = 1, w do | |
| for y = 1, h do | |
| pixelData[x + (y - 1) * w] = shaderFunc(vec4(pixelData[x + (y - 1) * w]), vec2((x - 1), h - y), inputs) | |
| end | |
| end | |
| end | |
| end | |
| local function createShader(shaderFunc) | |
| local shader = { | |
| shaderFunc = shaderFunc, | |
| inputs = { } | |
| } | |
| return shader | |
| end | |
| local function setShaderInput(shader, i, v) | |
| shader.inputs[i] = v | |
| end | |
| local function createFrameBuffer(palette, charTable, size, renderStrategy, blit, scp) | |
| renderStrategy = renderStrategy or STRATEGY_FULL_CELL | |
| if renderStrategy == STRATEGY_HEX_CELL and (size.x % 2 ~= 0 or size.y % 3 ~= 0) then | |
| error("Frame Buffer using STRATEGY_HEX_CELL must have width multiple of 2 and height multiple of 3.") | |
| end | |
| if renderStrategy == STRATEGY_TWO_CELL then | |
| error("STRATEGY_TWO_CELL not yet implemented.") | |
| end | |
| local pixelData = { } | |
| local buf = { | |
| palette = palette, | |
| charTable = charTable, | |
| size = size, | |
| pixelData = pixelData, | |
| renderStrategy = renderStrategy, | |
| blit = blit or term_blit, | |
| scp = scp or term_setCursorPos | |
| } | |
| for i = 1, size.x * size.y do | |
| pixelData[i] = vec4(0, 0, 0, 0) | |
| end | |
| return buf | |
| end | |
| -- Thanks for writing this useful function, oli414. | |
| local function getDrawingCharacter(topLeft, topRight, left, right, bottomLeft, bottomRight) | |
| local data = 128 | |
| if not bottomRight then | |
| data = data + (topLeft and 1 or 0) | |
| data = data + (topRight and 2 or 0) | |
| data = data + (left and 4 or 0) | |
| data = data + (right and 8 or 0) | |
| data = data + (bottomLeft and 16 or 0) | |
| else | |
| data = data + (topLeft and 0 or 1) | |
| data = data + (topRight and 0 or 2) | |
| data = data + (left and 0 or 4) | |
| data = data + (right and 0 or 8) | |
| data = data + (bottomLeft and 0 or 16) | |
| end | |
| return string.char(data), bottomRight | |
| end | |
| local function drawFrameBuffer(buf, pos) | |
| local w, h = buf.size.x, buf.size.y | |
| local x, y = pos.x, pos.y | |
| local pixelData = buf.pixelData | |
| local palette = buf.palette | |
| local charTable = buf.charTable | |
| local blit = buf.blit | |
| local scp = buf.scp | |
| if buf.renderStrategy == STRATEGY_FULL_CELL then | |
| for i = y, y + h - 1 do | |
| local s = { } | |
| local b = { } | |
| local f = { } | |
| for j = (i-1)*w + 1, i*w do | |
| local colIdx = paletteConvert(palette, pixelData[j]) | |
| local charEntry = charTable[colIdx] | |
| s[#s + 1] = charEntry[1] | |
| b[#b + 1] = charEntry[2] | |
| f[#f + 1] = charEntry[3] | |
| end | |
| scp(x, i) | |
| blit(table_concat(s), table_concat(f), table_concat(b)) | |
| end | |
| elseif buf.renderStrategy == STRATEGY_HEX_CELL then | |
| for sy = 0, h-1, 3 do | |
| local s = { } | |
| local b = { } | |
| local f = { } | |
| for sx = 0, w-1, 2 do | |
| local uniqueColors = { } | |
| local colorCounts = { } | |
| local convColors = { } | |
| for j = 0, 2 do | |
| for i = 0, 1 do | |
| local col = charTable[paletteConvert(palette, pixelData[sx+i + (sy+j) * w + 1])][2] | |
| convColors[#convColors + 1] = col | |
| if colorCounts[col] then | |
| colorCounts[col] = colorCounts[col] + 1 | |
| else | |
| colorCounts[col] = 1 | |
| uniqueColors[#uniqueColors + 1] = col | |
| end | |
| end | |
| end | |
| table.sort(uniqueColors, function(a, b) return colorCounts[a] > colorCounts[b] end) | |
| local c0 = uniqueColors[1] | |
| local c1 = uniqueColors[2] or c0 | |
| local char, inv = getDrawingCharacter( | |
| convColors[1] ~= c1, | |
| convColors[2] ~= c1, | |
| convColors[3] ~= c1, | |
| convColors[4] ~= c1, | |
| convColors[5] ~= c1, | |
| convColors[6] ~= c1) | |
| s[#s + 1] = char | |
| if inv then | |
| b[#b + 1] = c0 | |
| f[#f + 1] = c1 | |
| else | |
| b[#b + 1] = c1 | |
| f[#f + 1] = c0 | |
| end | |
| end | |
| scp(x, y + sy/3) | |
| blit(table_concat(s), table_concat(f), table_concat(b)) | |
| end | |
| elseif buf.renderStrategy == STRATEGY_TWO_CELL then | |
| error("STRATEGY_TWO_CELL not yet implemented.") | |
| end | |
| end | |
| local function getUnstretchedResolution(buf) | |
| return vec2(buf.size.x, buf.size.y * 3/2) | |
| end | |
| local function clearFrameBuffer(buf, color) | |
| local pixelData = buf.pixelData | |
| for i = 1, #pixelData do | |
| pixelData[i] = color | |
| end | |
| end | |
| local g = getfenv() | |
| g.STRATEGY_FULL_CELL = STRATEGY_FULL_CELL | |
| g.STRATEGY_HEX_CELL = STRATEGY_HEX_CELL | |
| g.STRATEGY_TWO_CELL = STRATEGY_TWO_CELL | |
| g.vec2 = vec2 | |
| g.vec3 = vec3 | |
| g.vec4 = vec4 | |
| g.length2 = length2 | |
| g.length3 = length3 | |
| g.length4 = length4 | |
| g.normalize2 = normalize2 | |
| g.normalize3 = normalize3 | |
| g.normalize4 = normalize4 | |
| g.distance2 = distance2 | |
| g.distance3 = distance3 | |
| g.distance4 = distance4 | |
| g.dot2 = dot2 | |
| g.dot3 = dot3 | |
| g.dot4 = dot4 | |
| g.add2 = add2 | |
| g.add3 = add3 | |
| g.add4 = add4 | |
| g.sub2 = sub2 | |
| g.sub3 = sub3 | |
| g.sub4 = sub4 | |
| g.mul2 = mul2 | |
| g.mul3 = mul3 | |
| g.mul4 = mul4 | |
| g.div2 = div2 | |
| g.div3 = div3 | |
| g.div4 = div4 | |
| g.add2s = add2s | |
| g.add3s = add3s | |
| g.add4s = add4s | |
| g.sub2s = sub2s | |
| g.sub3s = sub3s | |
| g.sub4s = sub4s | |
| g.mul2s = mul2s | |
| g.mul3s = mul3s | |
| g.mul4s = mul4s | |
| g.div2s = div2s | |
| g.div3s = div3s | |
| g.div4s = div4s | |
| g.mat2mulvec2 = mat2mulvec2 | |
| g.mat3mulvec3 = mat3mulvec3 | |
| g.getRotMat2 = getRotMat2 | |
| g.getRotMat3x = getRotMat3x | |
| g.getRotMat3y = getRotMat3y | |
| g.getRotMat3z = getRotMat3z | |
| g.fract = fract | |
| g.mix = mix | |
| g.clamp = clamp | |
| g.smoothstep = smoothstep | |
| g.lab2xyz = lab2xyz | |
| g.xyz2rgb = xyz2rgb | |
| g.lab2rgb = lab2rgb | |
| g.rgb2lab = rgb2lab | |
| g.colorDistance = colorDistance | |
| g.createPalette = createPalette | |
| g.paletteConvert = paletteConvert | |
| g.createShaderProgram = createShaderProgram | |
| g.addShader = addShader | |
| g.applyShaderProgram = applyShaderProgram | |
| g.createShader = createShader | |
| g.setShaderInput = setShaderInput | |
| g.createFrameBuffer = createFrameBuffer | |
| g.drawFrameBuffer = drawFrameBuffer | |
| g.getUnstretchedResolution = getUnstretchedResolution | |
| g.clearFrameBuffer = clearFrameBuffer |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| os.loadAPI("gfx") | |
| -- For convenience. | |
| local sin = math.sin | |
| local cos = math.cos | |
| local tan = math.tan | |
| local abs = math.abs | |
| local min = math.min | |
| local max = math.max | |
| local floor = math.floor | |
| local sqrt = math.sqrt | |
| local clock = os.clock | |
| -- I got lazy so I just localized all of | |
| -- these functions here. | |
| local STRATEGY_FULL_CELL = gfx.STRATEGY_FULL_CELL | |
| local STRATEGY_HEX_CELL = gfx.STRATEGY_HEX_CELL | |
| local STRATEGY_TWO_CELL = gfx.STRATEGY_TWO_CELL | |
| local vec2 = gfx.vec2 | |
| local vec3 = gfx.vec3 | |
| local vec4 = gfx.vec4 | |
| local length2 = gfx.length2 | |
| local length3 = gfx.length3 | |
| local length4 = gfx.length4 | |
| local normalize2 = gfx.normalize2 | |
| local normalize3 = gfx.normalize3 | |
| local normalize4 = gfx.normalize4 | |
| local distance2 = gfx.distance2 | |
| local distance3 = gfx.distance3 | |
| local distance4 = gfx.distance4 | |
| local dot2 = gfx.dot2 | |
| local dot3 = gfx.dot3 | |
| local dot4 = gfx.dot4 | |
| local add2 = gfx.add2 | |
| local add3 = gfx.add3 | |
| local add4 = gfx.add4 | |
| local sub2 = gfx.sub2 | |
| local sub3 = gfx.sub3 | |
| local sub4 = gfx.sub4 | |
| local mul2 = gfx.mul2 | |
| local mul3 = gfx.mul3 | |
| local mul4 = gfx.mul4 | |
| local div2 = gfx.div2 | |
| local div3 = gfx.div3 | |
| local div4 = gfx.div4 | |
| local add2s = gfx.add2s | |
| local add3s = gfx.add3s | |
| local add4s = gfx.add4s | |
| local sub2s = gfx.sub2s | |
| local sub3s = gfx.sub3s | |
| local sub4s = gfx.sub4s | |
| local mul2s = gfx.mul2s | |
| local mul3s = gfx.mul3s | |
| local mul4s = gfx.mul4s | |
| local div2s = gfx.div2s | |
| local div3s = gfx.div3s | |
| local div4s = gfx.div4s | |
| local mat2mulvec2 = gfx.mat2mulvec2 | |
| local mat3mulvec3 = gfx.mat3mulvec3 | |
| local getRotMat2 = gfx.getRotMat2 | |
| local getRotMat3x = gfx.getRotMat3x | |
| local getRotMat3y = gfx.getRotMat3y | |
| local getRotMat3z = gfx.getRotMat3z | |
| local fract = gfx.fract | |
| local mix = gfx.mix | |
| local clamp = gfx.clamp | |
| local smoothstep = gfx.smoothstep | |
| local lab2xyz = gfx.lab2xyz | |
| local xyz2rgb = gfx.xyz2rgb | |
| local lab2rgb = gfx.lab2rgb | |
| local rgb2lab = gfx.rgb2lab | |
| local colorDistance = gfx.colorDistance | |
| local createPalette = gfx.createPalette | |
| local paletteConvert = gfx.paletteConvert | |
| local createShaderProgram = gfx.createShaderProgram | |
| local addShader = gfx.addShader | |
| local applyShaderProgram = gfx.applyShaderProgram | |
| local createShader = gfx.createShader | |
| local setShaderInput = gfx.setShaderInput | |
| local createFrameBuffer = gfx.createFrameBuffer | |
| local drawFrameBuffer = gfx.drawFrameBuffer | |
| local getUnstretchedResolution = gfx.getUnstretchedResolution | |
| local clearFrameBuffer = gfx.clearFrameBuffer | |
| -- This table contains the set of pixels we | |
| -- draw to our frame buffer. In this case, | |
| -- we will just use all the colors with no | |
| -- foreground text. | |
| local charTable = { | |
| {" ", "0", "0"}, | |
| {" ", "1", "0"}, | |
| {" ", "2", "0"}, | |
| {" ", "3", "0"}, | |
| {" ", "4", "0"}, | |
| {" ", "5", "0"}, | |
| {" ", "6", "0"}, | |
| {" ", "7", "0"}, | |
| {" ", "8", "0"}, | |
| {" ", "9", "0"}, | |
| {" ", "a", "0"}, | |
| {" ", "b", "0"}, | |
| {" ", "c", "0"}, | |
| {" ", "d", "0"}, | |
| {" ", "e", "0"}, | |
| {" ", "f", "0"} | |
| } | |
| -- This table contains the colors for each | |
| -- pixel in charTable. e.g., index 0 is used | |
| -- for colors.white. | |
| local indexTable = { | |
| vec3(0xF0/0xFF, 0xF0/0xFF, 0xF0/0xFF), | |
| vec3(0xF2/0xFF, 0xB2/0xFF, 0x33/0xFF), | |
| vec3(0xE5/0xFF, 0x7F/0xFF, 0xD8/0xFF), | |
| vec3(0x99/0xFF, 0xB2/0xFF, 0xF2/0xFF), | |
| vec3(0xDE/0xFF, 0xDE/0xFF, 0x6C/0xFF), | |
| vec3(0x7F/0xFF, 0xCC/0xFF, 0x19/0xFF), | |
| vec3(0xF2/0xFF, 0xB2/0xFF, 0xCC/0xFF), | |
| vec3(0x4C/0xFF, 0x4C/0xFF, 0x4C/0xFF), | |
| vec3(0x99/0xFF, 0x99/0xFF, 0x99/0xFF), | |
| vec3(0x4C/0xFF, 0x99/0xFF, 0xB2/0xFF), | |
| vec3(0xB2/0xFF, 0x66/0xFF, 0xE5/0xFF), | |
| vec3(0x33/0xFF, 0x66/0xFF, 0xCC/0xFF), | |
| vec3(0x7F/0xFF, 0x66/0xFF, 0x4C/0xFF), | |
| vec3(0x57/0xFF, 0xA6/0xFF, 0x4E/0xFF), | |
| vec3(0xCC/0xFF, 0x4C/0xFF, 0x4C/0xFF), | |
| vec3(0x19/0xFF, 0x19/0xFF, 0x19/0xFF) | |
| } | |
| -- Note that the above two tables must | |
| -- correspond by index. | |
| local function progressHook(prog) | |
| term.clear() | |
| term.setCursorPos(1, 1) | |
| print("Generating color palette... " .. tostring(prog * 100) .. "%") | |
| return 0.1 | |
| end | |
| local function Object(_type) | |
| local self = { } | |
| local m_type = _type | |
| local function type() | |
| return m_type | |
| end | |
| self.type = type | |
| return self | |
| end | |
| local len = 1 | |
| local blocks = { } | |
| local function blockExists(x, y, z) | |
| local a = blocks[x] | |
| if not a then return false end | |
| local b = a[y] | |
| if not b then return false end | |
| local c = b[z] | |
| if not c then return false end | |
| return true | |
| end | |
| local function getBlock(x, y, z) | |
| if blockExists(x, y, z) then return blocks[x][y][z] end | |
| end | |
| local function setBlock(x, y, z, value) | |
| local a = blocks[x] | |
| if not a then | |
| blocks[x] = { [y] = { [z] = value } } | |
| return | |
| end | |
| local b = a[y] | |
| if not b then | |
| a[y] = { [z] = value } | |
| return | |
| end | |
| b[z] = value | |
| end | |
| local function clearBlocks() | |
| blocks = { } | |
| end | |
| local blockLook | |
| local function Keyboard() | |
| local self = Object(Keyboard) | |
| local m_keysDown | |
| do | |
| m_keysDown = { } | |
| for i = 0, 255 do | |
| m_keysDown[i] = false | |
| end | |
| end | |
| local function getKeyDown(key) | |
| return m_keysDown[key] | |
| end | |
| local function setKeyDown(key, state) | |
| m_keysDown[key] = state | |
| end | |
| self.getKeyDown = getKeyDown | |
| self.setKeyDown = setKeyDown | |
| return self | |
| end | |
| local keyboard = Keyboard() | |
| local function Vec4_eq(u, v) | |
| return u.x() == v.x() and u.y() == v.y() and u.z() == v.z() and u.w() == v.w() | |
| end | |
| local function Vec4(_x, _y, _z, _w) | |
| local self = Object(Vec4) | |
| local m_x = _x or 0 | |
| local m_y = _y or 0 | |
| local m_z = _z or 0 | |
| local m_w = _w or 0 | |
| local function x(_x) | |
| m_x = _x or m_x | |
| return m_x | |
| end | |
| local function y(_y) | |
| m_y = _y or m_y | |
| return m_y | |
| end | |
| local function z(_z) | |
| m_z = _z or m_z | |
| return m_z | |
| end | |
| local function w(_w) | |
| m_w = _w or m_w | |
| return m_w | |
| end | |
| local function neg() | |
| return Vec4(-m_x, -m_y, -m_z, -m_w) | |
| end | |
| local function add(_, v) | |
| return Vec4(m_x + v.x(), m_y + v.y(), m_z + v.z(), m_w + v.w()) | |
| end | |
| local function sub(_, v) | |
| return Vec4(m_x - v.x(), m_y - v.y(), m_z - v.z(), m_w - v.w()) | |
| end | |
| local function mul(_, s) | |
| return Vec4(m_x * s, m_y * s, m_z * s, m_w * s) | |
| end | |
| local function div(_, s) | |
| return Vec4(m_x / s, m_y / s, m_z / s, m_w / s) | |
| end | |
| local function lerp(dest, lerpFactor) | |
| return (dest - self) * lerpFactor + self | |
| end | |
| local function length() | |
| return math.sqrt(self.dot(self)) | |
| end | |
| local function normalized() | |
| return self/length() | |
| end | |
| local function rotateQuat(rotation) | |
| local conjugate = rotation.conjugate(); | |
| local w = rotation * self * conjugate; | |
| return Vec4(w.x(), w.y(), w.z(), 1); | |
| end | |
| local function dot(r) | |
| return m_x*r.x() + m_y*r.y() + m_z*r.z() + m_w*r.w() | |
| end | |
| self.x = x | |
| self.y = y | |
| self.z = z | |
| self.w = w | |
| self.lerp = lerp | |
| self.length = length | |
| self.normalized = normalized | |
| self.rotateQuat = rotateQuat | |
| self.dot = dot | |
| return setmetatable(self, | |
| { __add = add, __sub = sub, __mul = mul, __div = div, __unm = neg, __eq = Vec4_eq }) | |
| end | |
| local function Mat4() | |
| local self = Object(Mat4) | |
| local m_m = | |
| {{0,0,0,0}, | |
| {0,0,0,0}, | |
| {0,0,0,0}, | |
| {0,0,0,0}} | |
| local function m(_m) | |
| m_m = _m or m_m | |
| return m_m | |
| end | |
| local function copy(src) | |
| m_m[1][1] = src.m()[1][1] m_m[1][2] = src.m()[1][2] m_m[1][3] = src.m()[1][3] m_m[1][4] = src.m()[1][4] | |
| m_m[2][1] = src.m()[2][1] m_m[2][2] = src.m()[2][2] m_m[2][3] = src.m()[2][3] m_m[2][4] = src.m()[2][4] | |
| m_m[3][1] = src.m()[3][1] m_m[3][2] = src.m()[3][2] m_m[3][3] = src.m()[3][3] m_m[3][4] = src.m()[3][4] | |
| m_m[4][1] = src.m()[4][1] m_m[4][2] = src.m()[4][2] m_m[4][3] = src.m()[4][3] m_m[4][4] = src.m()[4][4] | |
| return self | |
| end | |
| local function initIdentity() | |
| m_m[1][1] = 1 m_m[1][2] = 0 m_m[1][3] = 0 m_m[1][4] = 0 | |
| m_m[2][1] = 0 m_m[2][2] = 1 m_m[2][3] = 0 m_m[2][4] = 0 | |
| m_m[3][1] = 0 m_m[3][2] = 0 m_m[3][3] = 1 m_m[3][4] = 0 | |
| m_m[4][1] = 0 m_m[4][2] = 0 m_m[4][3] = 0 m_m[4][4] = 1 | |
| return self | |
| end | |
| local function initScreenSpaceTransform(halfWidth, halfHeight) | |
| m_m[1][1] = halfWidth m_m[1][2] = 0 m_m[1][3] = 0 m_m[1][4] = halfWidth - 0.5 | |
| m_m[2][1] = 0 m_m[2][2] = -halfHeight m_m[2][3] = 0 m_m[2][4] = halfHeight - 0.5 | |
| m_m[3][1] = 0 m_m[3][2] = 0 m_m[3][3] = 1 m_m[3][4] = 0 | |
| m_m[4][1] = 0 m_m[4][2] = 0 m_m[4][3] = 0 m_m[4][4] = 1 | |
| return self | |
| end | |
| local function initTranslation(x, y, z) | |
| m_m[1][1] = 1 m_m[1][2] = 0 m_m[1][3] = 0 m_m[1][4] = x | |
| m_m[2][1] = 0 m_m[2][2] = 1 m_m[2][3] = 0 m_m[2][4] = y | |
| m_m[3][1] = 0 m_m[3][2] = 0 m_m[3][3] = 1 m_m[3][4] = z | |
| m_m[4][1] = 0 m_m[4][2] = 0 m_m[4][3] = 0 m_m[4][4] = 1 | |
| return self | |
| end | |
| local function transform(r) | |
| return | |
| Vec4(m_m[1][1] * r.x() + m_m[1][2] * r.y() + m_m[1][3] * r.z() + m_m[1][4] * r.w(), | |
| m_m[2][1] * r.x() + m_m[2][2] * r.y() + m_m[2][3] * r.z() + m_m[2][4] * r.w(), | |
| m_m[3][1] * r.x() + m_m[3][2] * r.y() + m_m[3][3] * r.z() + m_m[3][4] * r.w(), | |
| m_m[4][1] * r.x() + m_m[4][2] * r.y() + m_m[4][3] * r.z() + m_m[4][4] * r.w()) | |
| end | |
| local function mul(_, r) | |
| local res = Mat4() | |
| local resm = res.m() | |
| r = r.m() | |
| for i = 1, 4 do | |
| for j = 1, 4 do | |
| resm[i][j] = | |
| m_m[i][1] * r[1][j] + | |
| m_m[i][2] * r[2][j] + | |
| m_m[i][3] * r[3][j] + | |
| m_m[i][4] * r[4][j] | |
| end | |
| end | |
| return res | |
| end | |
| local function initScale(x, y, z) | |
| m_m[1][1] = x; m_m[1][2] = 0; m_m[1][3] = 0; m_m[1][4] = 0; | |
| m_m[2][1] = 0; m_m[2][2] = y; m_m[2][3] = 0; m_m[2][4] = 0; | |
| m_m[3][1] = 0; m_m[3][2] = 0; m_m[3][3] = z; m_m[3][4] = 0; | |
| m_m[4][1] = 0; m_m[4][2] = 0; m_m[4][3] = 0; m_m[4][4] = 1; | |
| return self; | |
| end | |
| local function initRotation(x, y, z, angle) | |
| local sin = math.sin(angle) | |
| local cos = math.cos(angle) | |
| m[1][1] = cos+x*x*(1-cos) m[1][2] = x*y*(1-cos)-z*sin m[1][3] = x*z*(1-cos)+y*sin m[1][4] = 0 | |
| m[2][1] = y*x*(1-cos)+z*sin m[2][2] = cos+y*y*(1-cos) m[2][3] = y*z*(1-cos)-x*sin m[2][4] = 0 | |
| m[3][1] = z*x*(1-cos)-y*sin m[3][2] = z*y*(1-cos)+x*sin m[3][3] = cos+z*z*(1-cos) m[3][4] = 0 | |
| m[4][1] = 0 m[4][2] = 0 m[4][3] = 0 m[4][4] = 1 | |
| return self | |
| end | |
| local function initRotation3(x, y, z) | |
| local rx = Mat4() | |
| local ry = Mat4() | |
| local rz = Mat4() | |
| rz.m()[1][1] = math.cos(z) rz.m()[1][2] = -math.sin(z) rz.m()[1][3] = 0 rz.m()[1][4] = 0 | |
| rz.m()[2][1] = math.sin(z) rz.m()[2][2] = math.cos(z) rz.m()[2][3] = 0 rz.m()[2][4] = 0 | |
| rz.m()[3][1] = 0 rz.m()[3][2] = 0 rz.m()[3][3] = 1 rz.m()[3][4] = 0 | |
| rz.m()[4][1] = 0 rz.m()[4][2] = 0 rz.m()[4][3] = 0 rz.m()[4][4] = 1 | |
| rx.m()[1][1] = 1 rx.m()[1][2] = 0 rx.m()[1][3] = 0 rx.m()[1][4] = 0 | |
| rx.m()[2][1] = 0 rx.m()[2][2] = math.cos(x) rx.m()[2][3] = -math.sin(x) rx.m()[2][4] = 0 | |
| rx.m()[3][1] = 0 rx.m()[3][2] = math.sin(x) rx.m()[3][3] = math.cos(x) rx.m()[3][4] = 0 | |
| rx.m()[4][1] = 0 rx.m()[4][2] = 0 rx.m()[4][3] = 0 rx.m()[4][4] = 1 | |
| ry.m()[1][1] = math.cos(y) ry.m()[1][2] = 0 ry.m()[1][3] = -math.sin(y) ry.m()[1][4] = 0 | |
| ry.m()[2][1] = 0 ry.m()[2][2] = 1 ry.m()[2][3] = 0 ry.m()[2][4] = 0 | |
| ry.m()[3][1] = math.sin(y) ry.m()[3][2] = 0 ry.m()[3][3] = math.cos(y) ry.m()[3][4] = 0 | |
| ry.m()[4][1] = 0 ry.m()[4][2] = 0 ry.m()[4][3] = 0 ry.m()[4][4] = 1 | |
| m_m = (rx * ry * rz).m() | |
| return self | |
| end | |
| local function initPerspective(fov, aspectRatio, zNear, zFar) | |
| local tanHalfFOV = math.tan(fov / 2) | |
| local zRange = zNear - zFar | |
| m_m[1][1] = 1 / (tanHalfFOV * aspectRatio) m_m[1][2] = 0 m_m[1][3] = 0 m_m[1][4] = 0 | |
| m_m[2][1] = 0 m_m[2][2] = 1 / tanHalfFOV m_m[2][3] = 0 m_m[2][4] = 0 | |
| m_m[3][1] = 0 m_m[3][2] = 0 m_m[3][3] = (-zNear - zFar)/zRange m_m[3][4] = 2 * zFar * zNear / zRange | |
| m_m[4][1] = 0 m_m[4][2] = 0 m_m[4][3] = 1 m_m[4][4] = 0 | |
| return self | |
| end | |
| local function initRotationFUR(f, u, r) | |
| m_m[1][1] = r.x() m_m[1][2] = r.y() m_m[1][3] = r.z() m_m[1][4] = 0 | |
| m_m[2][1] = u.x() m_m[2][2] = u.y() m_m[2][3] = u.z() m_m[2][4] = 0 | |
| m_m[3][1] = f.x() m_m[3][2] = f.y() m_m[3][3] = f.z() m_m[3][4] = 0 | |
| m_m[4][1] = 0 m_m[4][2] = 0 m_m[4][3] = 0 m_m[4][4] = 1 | |
| return self | |
| end | |
| self.m = m | |
| self.copy = copy | |
| self.initIdentity = initIdentity | |
| self.initScreenSpaceTransform = initScreenSpaceTransform | |
| self.initTranslation = initTranslation | |
| self.transform = transform | |
| self.initScale = initScale | |
| self.initRotation3 = initRotation3 | |
| self.initPerspective = initPerspective | |
| self.initRotationFUR = initRotationFUR | |
| return setmetatable(self, { | |
| __mul = mul }) | |
| end | |
| local function Quaternion() | |
| local self = Object(Quaternion) | |
| local m_x | |
| local m_y | |
| local m_z | |
| local m_w | |
| local function x(_x) | |
| m_x = _x or m_x | |
| return m_x | |
| end | |
| local function y(_y) | |
| m_y = _y or m_y | |
| return m_y | |
| end | |
| local function z(_z) | |
| m_z = _z or m_z | |
| return m_z | |
| end | |
| local function w(_w) | |
| m_w = _w or m_w | |
| return m_w | |
| end | |
| local function initComponents(_x, _y, _z, _w) | |
| m_x = _x | |
| m_y = _y | |
| m_z = _z | |
| m_w = _w | |
| return self | |
| end | |
| local function initAxisAngle(axis, angle) | |
| local sinHalfAngle = math.sin(angle/2) | |
| local cosHalfAngle = math.cos(angle/2) | |
| m_x = axis.x() * sinHalfAngle | |
| m_y = axis.y() * sinHalfAngle | |
| m_z = axis.z() * sinHalfAngle | |
| m_w = cosHalfAngle | |
| return self | |
| end | |
| local function initMatrix(rot) | |
| local trace = rot.m()[1][1] + rot.m()[2][2] + rot.m()[3][3] | |
| if trace > 0 then | |
| local s = 0.5 / math.sqrt(trace + 1) | |
| m_w = 0.25 / s | |
| m_x = (rot.m()[2][3] - rot.m()[3][2]) * s | |
| m_y = (rot.m()[3][1] - rot.m()[1][3]) * s | |
| m_z = (rot.m()[1][2] - rot.m()[2][1]) * s | |
| else -- BELOW INC | |
| if rot.m()[1][1] > rot.m()[2][2] and rot.m()[1][1] > rot.m()[3][3] then | |
| local s = 2 * math.sqrt(1 + rot.m()[1][1] - rot.m()[2][2] - rot.m()[3][3]) | |
| m_w = (rot.m()[2][3] - rot.m()[3][2]) / s | |
| m_x = 0.25 * s | |
| m_y = (rot.m()[2][1] + rot.m()[1][2]) / s | |
| m_z = (rot.m()[3][1] + rot.m()[1][3]) / s | |
| elseif rot.m()[2][2] > rot.m()[3][3] then | |
| local s = 2 * math.sqrt(1 + rot.m()[2][2] - rot.m()[1][1] - rot.m()[3][3]) | |
| m_w = (rot.m()[3][1] - rot.m()[1][3]) / s | |
| m_x = (rot.m()[2][1] + rot.m()[1][2]) / s | |
| m_y = 0.25 * s | |
| m_z = (rot.m()[3][2] + rot.m()[2][3]) / s | |
| else | |
| local s = 2 * math.sqrt(1 + rot.m()[3][3] - rot.m()[1][1] - rot.m()[2][2]) | |
| m_w = (rot.m()[1][2] - rot.m()[2][1] ) / s | |
| m_x = (rot.m()[3][1] + rot.m()[1][3] ) / s | |
| m_y = (rot.m()[2][3] + rot.m()[3][2] ) / s | |
| m_z = 0.25 * s | |
| end | |
| end | |
| local length = math.sqrt(m_x * m_x + m_y * m_y + m_z * m_z + m_w * m_w) | |
| m_x = m_x/length | |
| m_y = m_y/length | |
| m_z = m_z/length | |
| m_w = m_w/length | |
| return self | |
| end | |
| local function length() | |
| return math.sqrt(m_x * m_x + m_y * m_y + m_z * m_z + m_w * m_w) | |
| end | |
| local function normalized() | |
| local len = length() | |
| return Quaternion().initComponents(m_x / len, m_y / len, m_z / len, m_w / len) | |
| end | |
| local function conjugate() | |
| return Quaternion().initComponents(-m_x, -m_y, -m_z, m_w) | |
| end | |
| local function toRotationMatrix() | |
| local forward = Vec4(2 * (m_x * m_z - m_w * m_y), 2 * (m_y * m_z + m_w * m_x), 1 - 2 * (m_x * m_x + m_y * m_y)) | |
| local up = Vec4(2 * (m_x * m_y + m_w * m_z), 1 - 2 * (m_x * m_x + m_z * m_z), 2 * (m_y * m_z - m_w * m_x)) | |
| local right = Vec4(1 - 2 * (m_y * m_y + m_z * m_z), 2 * (m_x * m_y - m_w * m_z), 2 * (m_x * m_z + m_w * m_y)) | |
| return Mat4().initRotationFUR(forward, up, right) | |
| end | |
| local function mulVec(r) | |
| local w = -m_x * r.x() - m_y * r.y() - m_z * r.z() | |
| local x = m_w * r.x() + m_y * r.z() - m_z * r.y() | |
| local y = m_w * r.y() + m_z * r.x() - m_x * r.z() | |
| local z = m_w * r.z() + m_x * r.y() - m_y * r.x() | |
| return Quaternion().initComponents(x, y, z, w) | |
| end | |
| local function mulQuat(r) | |
| local w = m_w * r.w() - m_x * r.x() - m_y * r.y() - m_z * r.z() | |
| local x = m_x * r.w() + m_w * r.x() + m_y * r.z() - m_z * r.y() | |
| local y = m_y * r.w() + m_w * r.y() + m_z * r.x() - m_x * r.z() | |
| local z = m_z * r.w() + m_w * r.z() + m_x * r.y() - m_y * r.x() | |
| return Quaternion().initComponents(x, y, z, w) | |
| end | |
| local function mul(_, r) | |
| if r.type() == Vec4 then | |
| return mulVec(r) | |
| elseif r.type() == Quaternion then | |
| return mulQuat(r) | |
| end | |
| end | |
| local function getForward() | |
| return Vec4(0,0,1,1).rotateQuat(self) | |
| end | |
| local function getBack() | |
| return Vec4(0,0,-1,1).rotateQuat(self) | |
| end | |
| local function getUp() | |
| return Vec4(0,1,0,1).rotateQuat(self) | |
| end | |
| local function getDown() | |
| return Vec4(0,-1,0,1).rotateQuat(self) | |
| end | |
| local function getRight() | |
| return Vec4(1,0,0,1).rotateQuat(self) | |
| end | |
| local function getLeft() | |
| return Vec4(-1,0,0,1).rotateQuat(self) | |
| end | |
| self.initComponents = initComponents | |
| self.initAxisAngle = initAxisAngle | |
| self.initMatrix = initMatrix | |
| self.length = length | |
| self.normalized = normalized | |
| self.conjugate = conjugate | |
| self.toRotationMatrix = toRotationMatrix | |
| self.x = x | |
| self.y = y | |
| self.z = z | |
| self.w = w | |
| self.getForward = getForward | |
| self.getBack = getBack | |
| self.getUp = getUp | |
| self.getDown = getDown | |
| self.getRight = getRight | |
| self.getLeft = getLeft | |
| return setmetatable(self, { | |
| __mul = mul }) | |
| end | |
| local function Transform(_pos, _rot, _scale) | |
| local self = Object(Transform) | |
| local m_pos = _pos or Vec4(0, 0, 0, 0) | |
| local m_rot = _rot or Quaternion().initComponents(0, 0, 0, 1) | |
| local m_scale = _scale or Vec4(1, 1, 1, 1) | |
| local function rotate(rotation) | |
| return Transform(m_pos, (rotation * m_rot).normalized(), m_scale) | |
| end | |
| local function getTransformedPos() | |
| return m_pos | |
| end | |
| local function getTransformedRot() | |
| return m_rot | |
| end | |
| local function getPos() | |
| return m_pos | |
| end | |
| local function getRot() | |
| return m_rot | |
| end | |
| local function setPos(pos) | |
| return Transform(pos, m_rot, m_scale) | |
| end | |
| self.rotate = rotate | |
| self.getTransformedPos = getTransformedPos | |
| self.getTransformedRot = getTransformedRot | |
| self.getPos = getPos | |
| self.getRot = getRot | |
| self.setPos = setPos | |
| return self | |
| end | |
| local function Camera(_projection) | |
| local self = Object(Camera) | |
| local Y_AXIS = Vec4(0, 1, 0) | |
| local m_transform = Transform() | |
| local m_projection = _projection | |
| local function getTransform() | |
| return m_transform | |
| end | |
| local function getViewProjection() | |
| local cameraRotation = m_transform.getTransformedRot().conjugate().toRotationMatrix() | |
| local cameraPos = -m_transform.getTransformedPos() | |
| local cameraTranslation = Mat4().initTranslation(cameraPos.x(), cameraPos.y(), cameraPos.z()) | |
| return m_projection * cameraRotation * cameraTranslation | |
| end | |
| local function move(dir, amt) | |
| m_transform = m_transform.setPos(m_transform.getPos() + dir * amt) | |
| end | |
| local function rotate(axis, angle) | |
| m_transform = m_transform.rotate(Quaternion().initAxisAngle(axis, angle)) | |
| end | |
| local function update(keyboard) | |
| local moveSpeed = 0.4 | |
| local rotSpeed = 0.1 | |
| if keyboard.getKeyDown(keys.w) then | |
| move(m_transform.getRot().getForward(), moveSpeed) | |
| end | |
| if keyboard.getKeyDown(keys.s) then | |
| move(m_transform.getRot().getForward(), -moveSpeed) | |
| end | |
| if keyboard.getKeyDown(keys.a) then | |
| move(m_transform.getRot().getLeft(), moveSpeed) | |
| end | |
| if keyboard.getKeyDown(keys.d) then | |
| move(m_transform.getRot().getRight(), moveSpeed) | |
| end | |
| if keyboard.getKeyDown(keys.space) then | |
| move(m_transform.getRot().getUp(), moveSpeed) | |
| end | |
| if keyboard.getKeyDown(keys.leftCtrl) then | |
| move(m_transform.getRot().getDown(), moveSpeed) | |
| end | |
| if keyboard.getKeyDown(keys.right) then | |
| rotate(Y_AXIS, rotSpeed) | |
| end | |
| if keyboard.getKeyDown(keys.left) then | |
| rotate(Y_AXIS, -rotSpeed) | |
| end | |
| if keyboard.getKeyDown(keys.up) then | |
| rotate(m_transform.getRot().getRight(), rotSpeed) | |
| end | |
| if keyboard.getKeyDown(keys.down) then | |
| rotate(m_transform.getRot().getRight(), -rotSpeed) | |
| end | |
| if keyboard.getKeyDown(keys.b) then | |
| if blockLook then | |
| local x = blockLook.x() | |
| local y = blockLook.y() | |
| local z = blockLook.z() | |
| setBlock(x, y, z, nil) | |
| end | |
| end | |
| end | |
| self.getTransform = getTransform | |
| self.getViewProjection = getViewProjection | |
| self.move = move | |
| self.rotate = rotate | |
| self.update = update | |
| return self | |
| end | |
| local function Vert(_pos, _texCoords, _normal) | |
| local self = Object(Vert) | |
| local m_pos = _pos | |
| local m_texCoords = _texCoords | |
| local m_normal = _normal | |
| local function pos(_pos) | |
| m_pos = _pos or m_pos | |
| return m_pos | |
| end | |
| local function texCoords(_color) | |
| m_texCoords = _texCoords or m_texCoords | |
| return m_texCoords | |
| end | |
| local function normal(_normal) | |
| m_normal = _normal or m_normal | |
| return m_normal | |
| end | |
| local function transform(transform) | |
| return Vert(transform.transform(m_pos), m_texCoords, m_normal) | |
| end | |
| local function perspectiveDivide(v) | |
| return Vert(Vec4(m_pos.x()/m_pos.w(), m_pos.y()/m_pos.w(), m_pos.z()/m_pos.w(), m_pos.w()), m_texCoords, m_normal) | |
| end | |
| local function lerp(other, lerpAmt) | |
| return Vert( | |
| m_pos.lerp(other.pos(), lerpAmt), | |
| m_texCoords.lerp(other.texCoords(), lerpAmt) | |
| --[[,m_normal.lerp(other.normal(), lerpAmt)]]) | |
| end | |
| local function isInsideViewFrustum() | |
| return | |
| math.abs(m_pos.x()) <= math.abs(m_pos.w()) and | |
| math.abs(m_pos.y()) <= math.abs(m_pos.w()) and | |
| math.abs(m_pos.z()) <= math.abs(m_pos.w()) | |
| end | |
| local function get(index) | |
| if index == 0 then | |
| return m_pos.x() | |
| elseif index == 1 then | |
| return m_pos.y() | |
| elseif index == 2 then | |
| return m_pos.z() | |
| elseif index == 3 then | |
| return m_pos.w() | |
| end | |
| end | |
| self.pos = pos | |
| self.texCoords = texCoords | |
| self.transform = transform | |
| self.perspectiveDivide = perspectiveDivide | |
| self.lerp = lerp | |
| self.isInsideViewFrustum = isInsideViewFrustum | |
| self.get = get | |
| self.normal = normal | |
| return self | |
| end | |
| local function Edge(gradients, minYVert, maxYVert, minYVertIndex) | |
| local self = Object(Edge) | |
| local m_x | |
| local m_xStep | |
| local m_yStart | |
| local m_yEnd | |
| local m_texCoordX | |
| local m_texCoordXStep | |
| local m_texCoordY | |
| local m_texCoordYStep | |
| local m_oneOverZ | |
| local m_oneOverZStep | |
| local m_depth | |
| local m_depthStep | |
| --local m_light | |
| --local m_lightStep | |
| local m_lastX = 0 | |
| local function x(_x) | |
| m_x = _x or m_x | |
| return m_x | |
| end | |
| local function yStart(_yStart) | |
| m_yStart = _yStart or m_yStart | |
| return m_yStart | |
| end | |
| local function yEnd(_yEnd) | |
| m_yEnd = _yEnd or m_yEnd | |
| return m_yEnd | |
| end | |
| local function texCoordX(_texCoordX) | |
| m_texCoordX = _texCoordX or m_texCoordX | |
| return m_texCoordX | |
| end | |
| local function texCoordXStep(_texCoordXStep) | |
| m_texCoordXStep = _texCoordXStep or m_texCoordXStep | |
| return m_texCoordXStep | |
| end | |
| local function texCoordY(_texCoordY) | |
| m_texCoordY = _texCoordY or m_texCoordY | |
| return m_texCoordY | |
| end | |
| local function texCoordYStep(_texCoordYStep) | |
| m_texCoordYStep = _texCoordYStep or m_texCoordYStep | |
| return m_texCoordYStep | |
| end | |
| local function oneOverZ(_oneOverZ) | |
| m_oneOverZ = _oneOverZ or m_oneOverZ | |
| return m_oneOverZ | |
| end | |
| local function oneOverZStep(_oneOverZStep) | |
| m_oneOverZStep = _oneOverZStep or m_oneOverZStep | |
| return m_oneOverZStep | |
| end | |
| local function depth(_depth) | |
| m_depth = _depth or m_depth | |
| return m_depth | |
| end | |
| local function depthStep(_depthStep) | |
| m_depthStep = _depthStep or m_depthStep | |
| return m_depthStep | |
| end | |
| local function lastX() | |
| return m_lastX | |
| end | |
| --[[local function light(_light) | |
| m_light = _light or m_light | |
| return m_light | |
| end | |
| local function lightStep(_lightStep) | |
| m_lightStep = _lightStep or m_lightStep | |
| return m_lightStep | |
| end]] | |
| do | |
| m_yStart = math.ceil(minYVert.pos().y())-- | |
| m_yEnd = math.ceil(maxYVert.pos().y()) | |
| local xDist = maxYVert.pos().x() - minYVert.pos().x() | |
| local yDist = maxYVert.pos().y() - minYVert.pos().y() | |
| local yPrestep = m_yStart - minYVert.pos().y() | |
| m_xStep = xDist/yDist | |
| m_x = minYVert.pos().x() + yPrestep * m_xStep | |
| m_lastX = m_x | |
| local xPrestep = m_x - minYVert.pos().x() | |
| m_texCoordX = gradients.getTexCoordX(minYVertIndex) + | |
| gradients.texCoordXXStep() * xPrestep + | |
| gradients.texCoordXYStep() * yPrestep | |
| m_texCoordXStep = gradients.texCoordXYStep() + gradients.texCoordXXStep() * m_xStep; | |
| m_texCoordY = gradients.getTexCoordY(minYVertIndex) + | |
| gradients.texCoordYXStep() * xPrestep + | |
| gradients.texCoordYYStep() * yPrestep | |
| m_texCoordYStep = gradients.texCoordYYStep() + gradients.texCoordYXStep() * m_xStep; | |
| m_oneOverZ = gradients.getOneOverZ(minYVertIndex) + | |
| gradients.oneOverZXStep() * xPrestep + | |
| gradients.oneOverZYStep() * yPrestep | |
| m_oneOverZStep = gradients.oneOverZYStep() + gradients.oneOverZXStep() * m_xStep; | |
| m_depth = gradients.getDepth(minYVertIndex) + | |
| gradients.depthXStep() * xPrestep + | |
| gradients.depthYStep() * yPrestep | |
| m_depthStep = gradients.depthYStep() + gradients.depthXStep() * m_xStep; | |
| --[[m_light = gradients.getLight(minYVertIndex) + | |
| gradients.lightXStep() * xPrestep + | |
| gradients.lightYStep() * yPrestep | |
| m_lightStep = gradients.lightYStep() + gradients.lightXStep() * m_xStep;]] | |
| end | |
| local function step() | |
| m_lastX = m_x | |
| m_x = m_x + m_xStep | |
| m_texCoordX = m_texCoordX + m_texCoordXStep | |
| m_texCoordY = m_texCoordY + m_texCoordYStep | |
| m_oneOverZ = m_oneOverZ + m_oneOverZStep | |
| m_depth = m_depth + m_depthStep | |
| --m_light = m_light + m_lightStep | |
| end | |
| self.x = x | |
| self.yStart = yStart | |
| self.yEnd = yEnd | |
| self.texCoordX = texCoordX | |
| self.texCoordXStep = texCoordXStep | |
| self.texCoordY = texCoordY | |
| self.texCoordYStep = texCoordYStep | |
| self.oneOverZ = oneOverZ | |
| self.oneOverZStep = oneOverZStep | |
| self.step = step | |
| self.depth = depth | |
| self.depthStep = depthStep | |
| self.lastX = lastX | |
| --[[self.light = light | |
| self.lightStep = lightStep]] | |
| return self | |
| end | |
| local function Gradients(minYVert, midYVert, maxYVert) | |
| local self = Object(Gradients) | |
| local m_texCoordX | |
| local m_texCoordY | |
| local m_oneOverZ | |
| local m_depth | |
| --local m_light | |
| local m_texCoordXXStep | |
| local m_texCoordXYStep | |
| local m_texCoordYXStep | |
| local m_texCoordYYStep | |
| local m_oneOverZXStep | |
| local m_oneOverZYStep | |
| local m_depthXStep | |
| local m_depthYStep | |
| --local m_lightXStep | |
| --local m_lightYStep | |
| local function getTexCoordX(loc) | |
| return m_texCoordX[loc] | |
| end | |
| local function getTexCoordY(loc) | |
| return m_texCoordY[loc] | |
| end | |
| local function getOneOverZ(loc) | |
| return m_oneOverZ[loc] | |
| end | |
| local function getDepth(loc) | |
| return m_depth[loc] | |
| end | |
| --[[local function getLight(loc) | |
| return m_light[loc] | |
| end]] | |
| local function texCoordXXStep(_texCoordXXStep) | |
| m_texCoordXXStep = _texCoordXXStep or m_texCoordXXStep | |
| return m_texCoordXXStep | |
| end | |
| local function texCoordXYStep(_texCoordXYStep) | |
| m_texCoordXYStep = _texCoordXYStep or m_texCoordXYStep | |
| return m_texCoordXYStep | |
| end | |
| local function texCoordYXStep(_texCoordYXStep) | |
| m_texCoordYXStep = _texCoordYXStep or m_texCoordYXStep | |
| return m_texCoordYXStep | |
| end | |
| local function texCoordYYStep(_texCoordYYStep) | |
| m_texCoordYYStep = _texCoordYYStep or m_texCoordYYStep | |
| return m_texCoordYYStep | |
| end | |
| local function oneOverZXStep(_oneOverZXStep) | |
| m_oneOverZXStep = _oneOverZXStep or m_oneOverZXStep | |
| return m_oneOverZXStep | |
| end | |
| local function oneOverZYStep(_oneOverZYStep) | |
| m_oneOverZYStep = _oneOverZYStep or m_oneOverZYStep | |
| return m_oneOverZYStep | |
| end | |
| local function depthXStep(_depthXStep) | |
| m_depthXStep = _depthXStep or m_depthXStep | |
| return m_depthXStep | |
| end | |
| local function depthYStep(_depthYStep) | |
| m_depthYStep = _depthYStep or m_depthYStep | |
| return m_depthYStep | |
| end | |
| --[[local function lightXStep(_lightXStep) | |
| m_lightXStep = _lightXStep or m_lightXStep | |
| return m_lightXStep | |
| end | |
| local function lightYStep(_lightYStep) | |
| m_lightYStep = _lightYStep or m_lightYStep | |
| return m_lightYStep | |
| end]] | |
| local function calcXStep(values, minYVert, midYVert, maxYVert, oneOverDX) | |
| return | |
| (((values[2] - values[3]) * | |
| (minYVert.pos().y() - maxYVert.pos().y())) - | |
| ((values[1] - values[3]) * | |
| (midYVert.pos().y() - maxYVert.pos().y()))) * oneOverDX | |
| end | |
| local function calcYStep(values, minYVert, midYVert, maxYVert, oneOverDY) | |
| return | |
| (((values[2] - values[3]) * | |
| (minYVert.pos().x() - maxYVert.pos().x())) - | |
| ((values[1] - values[3]) * | |
| (midYVert.pos().x() - maxYVert.pos().x()))) * oneOverDY | |
| end | |
| local function saturate(val) | |
| --[[if val < 0 then | |
| return 0 | |
| end | |
| if val > 1 then | |
| return 1 | |
| end | |
| return val]] | |
| return val < 0 and 0 or val > 1 and 1 or val | |
| end | |
| do | |
| m_oneOverZ = { } | |
| m_texCoordX = { } | |
| m_texCoordY = { } | |
| m_depth = { } | |
| --m_light = { } | |
| --[[local lightDir = Vec4(0, 0, 1).normalized() | |
| m_light[1] = saturate(minYVert.normal().dot(lightDir)) | |
| m_light[2] = saturate(midYVert.normal().dot(lightDir)) | |
| m_light[3] = saturate(maxYVert.normal().dot(lightDir))]] | |
| m_depth[1] = minYVert.pos().z() | |
| m_depth[2] = midYVert.pos().z() | |
| m_depth[3] = maxYVert.pos().z() | |
| m_oneOverZ[1] = 1/minYVert.pos().w() | |
| m_oneOverZ[2] = 1/midYVert.pos().w() | |
| m_oneOverZ[3] = 1/maxYVert.pos().w() | |
| m_texCoordX[1] = minYVert.texCoords().x() * m_oneOverZ[1] | |
| m_texCoordX[2] = midYVert.texCoords().x() * m_oneOverZ[2] | |
| m_texCoordX[3] = maxYVert.texCoords().x() * m_oneOverZ[3] | |
| m_texCoordY[1] = minYVert.texCoords().y() * m_oneOverZ[1] | |
| m_texCoordY[2] = midYVert.texCoords().y() * m_oneOverZ[2] | |
| m_texCoordY[3] = maxYVert.texCoords().y() * m_oneOverZ[3] | |
| local oneOverDX = 1 / | |
| (((midYVert.pos().x() - maxYVert.pos().x()) * | |
| (minYVert.pos().y() - maxYVert.pos().y())) - | |
| ((minYVert.pos().x() - maxYVert.pos().x()) * | |
| (midYVert.pos().y() - maxYVert.pos().y()))); | |
| local oneOverDY = -oneOverDX; | |
| m_texCoordXXStep = calcXStep(m_texCoordX, minYVert, midYVert, maxYVert, oneOverDX) | |
| m_texCoordXYStep = calcYStep(m_texCoordX, minYVert, midYVert, maxYVert, oneOverDY) | |
| m_texCoordYXStep = calcXStep(m_texCoordY, minYVert, midYVert, maxYVert, oneOverDX) | |
| m_texCoordYYStep = calcYStep(m_texCoordY, minYVert, midYVert, maxYVert, oneOverDY) | |
| m_oneOverZXStep = calcXStep(m_oneOverZ, minYVert, midYVert, maxYVert, oneOverDX) | |
| m_oneOverZYStep = calcYStep(m_oneOverZ, minYVert, midYVert, maxYVert, oneOverDY) | |
| m_depthXStep = calcXStep(m_depth, minYVert, midYVert, maxYVert, oneOverDX) | |
| m_depthYStep = calcYStep(m_depth, minYVert, midYVert, maxYVert, oneOverDY) | |
| --[[m_lightXStep = calcXStep(m_light, minYVert, midYVert, maxYVert, oneOverDX) | |
| m_lightYStep = calcYStep(m_light, minYVert, midYVert, maxYVert, oneOverDY)]] | |
| end | |
| self.getTexCoordX = getTexCoordX | |
| self.getTexCoordY = getTexCoordY | |
| self.getOneOverZ = getOneOverZ | |
| self.getDepth = getDepth | |
| self.texCoordXXStep = texCoordXXStep | |
| self.texCoordXYStep = texCoordXYStep | |
| self.texCoordYXStep = texCoordYXStep | |
| self.texCoordYYStep = texCoordYYStep | |
| self.oneOverZXStep = oneOverZXStep | |
| self.oneOverZYStep = oneOverZYStep | |
| self.depthXStep = depthXStep | |
| self.depthYStep = depthYStep | |
| self.getLight = getLight | |
| --[[self.lightXStep = lightXStep | |
| self.lightYStep = lightYStep]] | |
| return self | |
| end | |
| local function Triangle(_v1, _v2, _v3, _texture, _callback, _info) | |
| local self = Object(Triangle) | |
| local m_vertices | |
| local m_texture | |
| local m_callback | |
| local m_info | |
| local vertices | |
| local vertex | |
| local texture | |
| local callback | |
| local info | |
| local isInsideViewFrustum | |
| do | |
| m_vertices = { _v1, _v2, _v3 } | |
| m_texture = _texture | |
| m_callback = _callback | |
| m_info = _info or { } | |
| end | |
| function vertices() | |
| return unpack(m_vertices) | |
| end | |
| function vertex(i) | |
| return m_vertices[i] | |
| end | |
| function texture() | |
| return m_texture | |
| end | |
| function callback() | |
| return m_callback | |
| end | |
| function info() | |
| return m_info | |
| end | |
| function isInsideViewFrustum() | |
| for i = 1, 3 do | |
| if not m_vertices[i].isInsideViewFrustum() then | |
| return false | |
| end | |
| end | |
| return true | |
| end | |
| self.vertices = vertices | |
| self.vertex = vertex | |
| self.texture = texture | |
| self.callback = callback | |
| self.info = info | |
| self.isInsideViewFrustum = isInsideViewFrustum | |
| return self | |
| end | |
| local function RenderContext(_renderTarget) | |
| local self = Object(RenderContext) | |
| local m_renderTarget = _renderTarget | |
| local m_zBuffer = { } | |
| local m_drawList = { } | |
| do | |
| for i = 1, m_renderTarget.size.x * m_renderTarget.size.y do | |
| m_zBuffer[i] = 0 | |
| end | |
| end | |
| local function addTriangle(v1, v2, v3, texture, callback, info) | |
| local tri = Triangle(v1, v2, v3, texture, callback, info) | |
| m_drawList[#m_drawList + 1] = tri | |
| end | |
| local function plot(x, y, color) | |
| m_renderTarget.pixelData[x + y * m_renderTarget.size.x + 1] = color | |
| end | |
| local function clearDrawList() | |
| for i = 1, #m_drawList do | |
| m_drawList[i] = nil | |
| end | |
| end | |
| local function clearDepthBuffer() | |
| for i = 1, m_renderTarget.size.x * m_renderTarget.size.y do | |
| m_zBuffer[i] = math.huge | |
| end | |
| end | |
| local function clipPolygonComponent(vertices, componentIndex, componentFactor, result) | |
| local previousVertex = vertices[#vertices] | |
| local previousComponent = previousVertex.get(componentIndex) * componentFactor | |
| local previousInside = previousComponent <= previousVertex.pos().w() | |
| for i = 1, #vertices do | |
| local currentVertex = vertices[i] | |
| local currentComponent = currentVertex.get(componentIndex) * componentFactor | |
| local currentInside = currentComponent <= currentVertex.pos().w() | |
| if currentInside ~= previousInside then | |
| local lerpAmt = (previousVertex.pos().w() - previousComponent)/ | |
| ((previousVertex.pos().w() - previousComponent) - | |
| (currentVertex.pos().w() - currentComponent)) | |
| result[#result + 1] = previousVertex.lerp(currentVertex, lerpAmt) | |
| end | |
| if currentInside then | |
| result[#result + 1] = currentVertex | |
| end | |
| previousVertex = currentVertex | |
| previousComponent = currentComponent | |
| previousInside = currentInside | |
| end | |
| end | |
| local function clearTable(t) | |
| for i = 1, #t do | |
| t[i] = nil | |
| end | |
| end | |
| local function clipPolygonAxis(vertices, auxillaryList, componentIndex) | |
| clipPolygonComponent(vertices, componentIndex, 1, auxillaryList) | |
| clearTable(vertices) | |
| if #auxillaryList == 0 then | |
| return false | |
| end | |
| clipPolygonComponent(auxillaryList, componentIndex, -1, vertices) | |
| clearTable(auxillaryList) | |
| return #vertices ~= 0 | |
| end | |
| local function drawScanLine(left, right, j, triangle) | |
| local texture = triangle.texture() | |
| local callback = triangle.callback() | |
| local xMin = math.ceil(left.x()) | |
| local xMax = math.ceil(right.x()) | |
| local xPrestep = xMin - left.x() | |
| local xDist = right.x() - left.x() | |
| local texCoordXXStep = (right.texCoordX() - left.texCoordX())/xDist | |
| local texCoordYXStep = (right.texCoordY() - left.texCoordY())/xDist | |
| local oneOverZXStep = (right.oneOverZ() - left.oneOverZ()) /xDist | |
| local depthXStep = (right.depth() - left.depth()) /xDist | |
| --local lightXStep = (right.light() - left.light()) /xDist | |
| local texCoordX = left.texCoordX() + texCoordXXStep * xPrestep | |
| local texCoordY = left.texCoordY() + texCoordYXStep * xPrestep | |
| local oneOverZ = left.oneOverZ() + oneOverZXStep * xPrestep | |
| local depth = left.depth() + depthXStep * xPrestep | |
| --local light = left.light() + lightXStep * xPrestep | |
| local texWidth = texture.size.x | |
| local texHeight = texture.size.y | |
| for i = xMin, xMax - 1 do | |
| local index = i + j * m_renderTarget.size.x | |
| if depth < m_zBuffer[index + 1] then | |
| m_zBuffer[index + 1] = depth | |
| local z = 1/oneOverZ | |
| local srcX = math.floor((texCoordX * z) * texWidth) | |
| local srcY = math.floor((texCoordY * z) * texHeight) | |
| local color = texture.pixelData[srcX + srcY * texWidth + 1] | |
| local r, g, b, a = color.x, color.y, color.z, color.w | |
| local ambientLight = 0.5 | |
| local viewDistance = 10 | |
| --plot(i, j, vec4(r*((1-smoothstep(1, viewDistance+1, z+1))*(1-ambientLight) + ambientLight), g*((1-smoothstep(1, viewDistance+1, z+1))*(1-ambientLight) + ambientLight), b*((1-smoothstep(1, viewDistance+1, z+1))*(1-ambientLight) + ambientLight), a)) | |
| if i == centerW and j == centerH then | |
| centerHit = true | |
| end | |
| --fill[#fill + 1] = { i, j, vec4(r, g, b, a) } | |
| local inColor = vec4(r, g, b, a) | |
| local info = { } | |
| info.screenPos = vec2(i, j) | |
| info.color = inColor | |
| info.z = math.log(depth) | |
| info.triangle = triangle | |
| setmetatable(info, { __index = triangle.info() }) | |
| plot(i, j, callback(info) or inColor) | |
| end | |
| oneOverZ = oneOverZ + oneOverZXStep | |
| texCoordX = texCoordX + texCoordXXStep | |
| texCoordY = texCoordY + texCoordYXStep | |
| depth = depth + depthXStep | |
| --light = light + lightXStep | |
| end | |
| end | |
| local function scanEdges(a, b, handedness, triangle) | |
| local left = a | |
| local right = b | |
| if handedness then | |
| local temp = left | |
| left = right | |
| right = temp | |
| end | |
| local yStart = b.yStart() | |
| local yEnd = b.yEnd() | |
| local centerHit = false | |
| local fills = { } | |
| for j = yStart, yEnd - 1 do | |
| local ch, fill = drawScanLine(left, right, j, triangle) | |
| if ch then centerHit = true end | |
| fills[#fills + 1] = fill | |
| left.step() | |
| right.step() | |
| end | |
| return centerHit, fills | |
| end | |
| local function scanTriangle(triangle, handedness) | |
| local minYVert, midYVert, maxYVert = triangle.vertices() | |
| local gradients = Gradients(minYVert, midYVert, maxYVert) | |
| local maxToMin = Edge(gradients, minYVert, maxYVert, 1) | |
| local maxToMid = Edge(gradients, minYVert, midYVert, 1) | |
| local midToMin = Edge(gradients, midYVert, maxYVert, 2) | |
| local centerHit = false | |
| local triFills = { } | |
| local fills | |
| ch, fills = scanEdges(maxToMin, maxToMid, handedness, triangle) | |
| if ch then centerHit = true end | |
| triFills[1] = fills | |
| ch, fills = scanEdges(maxToMin, midToMin, handedness, triangle) | |
| if ch then centerHit = true end | |
| triFills[2] = fills | |
| for k = 1, 2 do | |
| fills = triFills[k] | |
| if false and centerHit then | |
| for i = 1, #fills do | |
| local fill = fills[i] | |
| for j = 1, #fill do | |
| plot(fill[j][1], fill[j][2], vec4(1, 0, 1, 1)) | |
| end | |
| end | |
| else | |
| for i = 1, #fills do | |
| local fill = fills[i] | |
| for j = 1, #fill do | |
| local f = fill[j] | |
| if triangle.callback() then | |
| local info = { } | |
| info.screenPos = vec2(f[1], f[2]) | |
| info.color = f[3] | |
| local outColor = triangle.callback()(info) | |
| plot(f[1], f[2], outColor or f[3]) | |
| else | |
| plot(unpack(f)) | |
| end | |
| end | |
| end | |
| end | |
| end | |
| end | |
| local function triangleTwiceArea(a, b, c) | |
| local x1 = b.pos().x() - a.pos().x() | |
| local y1 = b.pos().y() - a.pos().y() | |
| local x2 = c.pos().x() - a.pos().x() | |
| local y2 = c.pos().y() - a.pos().y() | |
| return x1 * y2 - x2 * y1 | |
| end | |
| local function fillTriangle(triangle) | |
| local v1, v2, v3 = triangle.vertices() | |
| local sst = Mat4().initScreenSpaceTransform(m_renderTarget.size.x/2, m_renderTarget.size.y/2) | |
| local minYVert = v1.transform(sst).perspectiveDivide() | |
| local midYVert = v2.transform(sst).perspectiveDivide() | |
| local maxYVert = v3.transform(sst).perspectiveDivide() | |
| if triangleTwiceArea(minYVert, maxYVert, midYVert) >= 0 then | |
| return | |
| end | |
| if maxYVert.pos().y() < midYVert.pos().y() then | |
| local temp = maxYVert | |
| maxYVert = midYVert | |
| midYVert = temp | |
| end | |
| if midYVert.pos().y() < minYVert.pos().y() then | |
| local temp = midYVert | |
| midYVert = minYVert | |
| minYVert = temp | |
| end | |
| if maxYVert.pos().y() < midYVert.pos().y() then | |
| local temp = maxYVert | |
| maxYVert = midYVert | |
| midYVert = temp | |
| end | |
| local handedness = triangleTwiceArea(minYVert, maxYVert, midYVert) >= 0 | |
| local newTriangle = Triangle(minYVert, midYVert, maxYVert, triangle.texture(), triangle.callback(), triangle.info()) | |
| scanTriangle(newTriangle, handedness) | |
| end | |
| local function drawTriangle(triangle) | |
| if triangle.isInsideViewFrustum() then | |
| fillTriangle(triangle) | |
| return | |
| end | |
| local vertices = { triangle.vertex(1), triangle.vertex(2), triangle.vertex(3) } | |
| local auxillaryList = { } | |
| if | |
| clipPolygonAxis(vertices, auxillaryList, 0) and | |
| clipPolygonAxis(vertices, auxillaryList, 1) and | |
| clipPolygonAxis(vertices, auxillaryList, 2) then | |
| local initialVertex = vertices[1] | |
| for i = 2, #vertices - 1 do | |
| local newTriangle = Triangle( | |
| initialVertex, vertices[i], vertices[i + 1], | |
| triangle.texture(), triangle.callback(), triangle.info()) | |
| fillTriangle(newTriangle) | |
| end | |
| end | |
| end | |
| local rotCounter = 0 | |
| local v1, t1 = Vec4(-1, 1, 1, 1), Vec4(0, 1, 0, 0) | |
| local v2, t2 = Vec4(1, 1, 1, 1), Vec4(1, 1, 0, 0) | |
| local v3, t3 = Vec4(1, -1, 1, 1), Vec4(1, 0, 0, 0) | |
| local v4, t4 = Vec4(-1, -1, 1, 1), Vec4(0, 0, 1, 1) | |
| local v5 = Vec4(-1, 1, -1, 1) | |
| local v6 = Vec4(1, 1, -1, 1) | |
| local v7 = Vec4(1, -1, -1, 1) | |
| local v8 = Vec4(-1, -1, -1, 1) | |
| local matrices = { Mat4().initIdentity() } | |
| local m_transform = matrices[1] | |
| local function pushMatrix(m) | |
| matrices[#matrices + 1] = matrices[#matrices] * m | |
| m_transform = matrices[#matrices] | |
| end | |
| local function popMatrix() | |
| matrices[#matrices] = nil | |
| m_transform = matrices[#matrices] | |
| end | |
| local charToColor = { | |
| [1] = vec3(0xF0/0xFF, 0xF0/0xFF, 0xF0/0xFF), | |
| [2] = vec3(0xF2/0xFF, 0xB2/0xFF, 0x33/0xFF), | |
| [4] = vec3(0xE5/0xFF, 0x7F/0xFF, 0xD8/0xFF), | |
| [8] = vec3(0x99/0xFF, 0xB2/0xFF, 0xF2/0xFF), | |
| [16] = vec3(0xDE/0xFF, 0xDE/0xFF, 0x6C/0xFF), | |
| [32] = vec3(0x7F/0xFF, 0xCC/0xFF, 0x19/0xFF), | |
| [64] = vec3(0xF2/0xFF, 0xB2/0xFF, 0xCC/0xFF), | |
| [128] = vec3(0x4C/0xFF, 0x4C/0xFF, 0x4C/0xFF), | |
| [256] = vec3(0x99/0xFF, 0x99/0xFF, 0x99/0xFF), | |
| [512] = vec3(0x4C/0xFF, 0x99/0xFF, 0xB2/0xFF), | |
| [1024] = vec3(0xB2/0xFF, 0x66/0xFF, 0xE5/0xFF), | |
| [2048] = vec3(0x33/0xFF, 0x66/0xFF, 0xCC/0xFF), | |
| [4096] = vec3(0x7F/0xFF, 0x66/0xFF, 0x4C/0xFF), | |
| [8192] = vec3(0x57/0xFF, 0xA6/0xFF, 0x4E/0xFF), | |
| [16384] = vec3(0xCC/0xFF, 0x4C/0xFF, 0x4C/0xFF), | |
| [32768] = vec3(0x19/0xFF, 0x19/0xFF, 0x19/0xFF) | |
| } | |
| local function loadTexture(file, frameBuffer, width, height) | |
| local img = paintutils.loadImage(file) | |
| local data = frameBuffer.pixelData | |
| for y = 1, height do | |
| local row = img[y] | |
| if row then | |
| for x = 1, width do | |
| data[x + (height-y) * width] = row[x] and charToColor[row[x]] or charToColor[colors.black] | |
| end | |
| end | |
| end | |
| end | |
| local function drawCube(t1, t2, t3, t4, cubeTexture, drawCallback) | |
| addTriangle( | |
| Vert(v2, t2).transform(m_transform), | |
| Vert(v1, t1).transform(m_transform), | |
| Vert(v3, t3).transform(m_transform), cubeTexture, drawCallback) | |
| addTriangle( | |
| Vert(v3, t3).transform(m_transform), | |
| Vert(v1, t1).transform(m_transform), | |
| Vert(v4, t4).transform(m_transform), cubeTexture, drawCallback) | |
| addTriangle( | |
| Vert(v6, t1).transform(m_transform), | |
| Vert(v2, t2).transform(m_transform), | |
| Vert(v3, t3).transform(m_transform), cubeTexture, drawCallback) | |
| addTriangle( | |
| Vert(v6, t1).transform(m_transform), | |
| Vert(v3, t3).transform(m_transform), | |
| Vert(v7, t4).transform(m_transform), cubeTexture, drawCallback) | |
| addTriangle( | |
| Vert(v5, t1).transform(m_transform), | |
| Vert(v6, t2).transform(m_transform), | |
| Vert(v7, t3).transform(m_transform), cubeTexture, drawCallback) | |
| addTriangle( | |
| Vert(v5, t1).transform(m_transform), | |
| Vert(v7, t3).transform(m_transform), | |
| Vert(v8, t4).transform(m_transform), cubeTexture, drawCallback) | |
| addTriangle( | |
| Vert(v1, t1).transform(m_transform), | |
| Vert(v5, t2).transform(m_transform), | |
| Vert(v4, t4).transform(m_transform), cubeTexture, drawCallback) | |
| addTriangle( | |
| Vert(v4, t4).transform(m_transform), | |
| Vert(v5, t2).transform(m_transform), | |
| Vert(v8, t3).transform(m_transform), cubeTexture, drawCallback) | |
| addTriangle( | |
| Vert(v1, t1).transform(m_transform), | |
| Vert(v2, t2).transform(m_transform), | |
| Vert(v5, t4).transform(m_transform), cubeTexture, drawCallback) | |
| addTriangle( | |
| Vert(v2, t2).transform(m_transform), | |
| Vert(v6, t3).transform(m_transform), | |
| Vert(v5, t4).transform(m_transform), cubeTexture, drawCallback) | |
| addTriangle( | |
| Vert(v8, t4).transform(m_transform), | |
| Vert(v3, t2).transform(m_transform), | |
| Vert(v4, t1).transform(m_transform), cubeTexture, drawCallback) | |
| addTriangle( | |
| Vert(v3, t2).transform(m_transform), | |
| Vert(v8, t4).transform(m_transform), | |
| Vert(v7, t3).transform(m_transform), cubeTexture, drawCallback) | |
| end | |
| local camera = Camera(Mat4().initPerspective(70/180*math.pi, m_renderTarget.size.x/m_renderTarget.size.y, 0.1, 1000)) | |
| camera.move(Vec4(0, 0, -1, 0), 3) | |
| local rotCounter = 0 | |
| local function getTexture(path) | |
| local fb = createFrameBuffer(palette, charTable, vec2(16, 16), STRATEGY_FULL_CELL) | |
| loadTexture(path, fb, 16, 16) | |
| return fb | |
| end | |
| local blockTextures = { | |
| getTexture("textures/cobble"), | |
| getTexture("textures/redstone") | |
| } | |
| local function averagePoints(tri) | |
| local a = tri.vertex(1).pos() | |
| local b = tri.vertex(2).pos() | |
| local c = tri.vertex(3).pos() | |
| local ret = Vec4( | |
| (a.x() + b.x() + c.x())/3, | |
| (a.y() + b.y() + c.y())/3, | |
| (a.z() + b.z() + c.z())/3) | |
| return ret | |
| end | |
| local function sortTriangles(a, b) | |
| return a.dist < b.dist | |
| end | |
| local function drawTriangles() | |
| -- TODO: Decide whether this improves performance on average cases. | |
| if false then | |
| for i = 1, #m_drawList do | |
| m_drawList[i].dist = averagePoints(m_drawList[i]).length() | |
| end | |
| table.sort(m_drawList, sortTriangles) | |
| end | |
| for i = 1, #m_drawList do | |
| drawTriangle(m_drawList[i]) | |
| end | |
| end | |
| for x = -len, len do | |
| for y = -len, len do | |
| for z = -len, len do | |
| setBlock(x, y, z, math.random(1, 2)) | |
| end | |
| end | |
| end | |
| local function renderScene() | |
| camera.update(keyboard) | |
| --clearBlocks() | |
| clearDrawList() | |
| clearDepthBuffer() | |
| clearFrameBuffer(m_renderTarget, vec4(0, 0, 0, 1)) | |
| rotCounter = rotCounter + 0.05 | |
| pushMatrix(camera.getViewProjection()) | |
| --[[pushMatrix(Mat4().initRotation3(0, os.clock(), 0)) | |
| pushMatrix(Mat4().initTranslation(0, 0, 0)) | |
| drawCube(t1, t2, t3, t4, cubeTexture) | |
| popMatrix() | |
| for i = 1, 3 do | |
| pushMatrix(Mat4().initTranslation(2.5 * i, 0, 0)) | |
| drawCube(t1, t2, t3, t4, cubeTexture) | |
| popMatrix() | |
| pushMatrix(Mat4().initTranslation(-2.5 * i, 0, 0)) | |
| drawCube(t1, t2, t3, t4, cubeTexture) | |
| popMatrix() | |
| end | |
| popMatrix()]] | |
| local function drawCallback(info) | |
| local c = info.color | |
| local x = info.screenPos.x | |
| local y = info.screenPos.y | |
| local centerW = math.ceil(m_renderTarget.size.x/2) | |
| local centerH = math.ceil(m_renderTarget.size.y/2) | |
| --print(tostring(centerW) .. ", " .. tostring(x) .. ", " .. tostring(centerH) .. ", " .. tostring(y)) | |
| --error() | |
| if centerW == x and centerH == y then | |
| blockLook = info.blockPos | |
| end | |
| if blockLook == info.blockPos then | |
| return vec4(1 - c.x, 1 - c.y, 1 - c.z, 1) | |
| end | |
| return c | |
| end | |
| --[[ | |
| local t = os.clock() | |
| for x = -(len/2), len/2 - 1 do | |
| for y = -(len/2), len/2 - 1 do | |
| for z = -(len/2), len/2 - 1 do | |
| if Vec4(x, y, z).length() < len/2 then | |
| setBlock( | |
| x + 0*math.floor(math.sin(t) * 4), | |
| y + 0*math.floor(math.cos(t) * 4), z, 2) | |
| end | |
| end | |
| end | |
| end | |
| ]] | |
| for x, a in pairs(blocks) do | |
| for y, b in pairs(a) do | |
| for z, block in pairs(b) do | |
| pushMatrix(Mat4().initTranslation(x * 2, y * 2, z * 2 + len + 5)) | |
| if not blockExists(x, y, z + 1) then | |
| addTriangle( | |
| Vert(v2, t2).transform(m_transform), | |
| Vert(v1, t1).transform(m_transform), | |
| Vert(v3, t3).transform(m_transform), blockTextures[block], drawCallback, { blockPos = Vec4(x, y, z) }) | |
| addTriangle( | |
| Vert(v3, t3).transform(m_transform), | |
| Vert(v1, t1).transform(m_transform), | |
| Vert(v4, t4).transform(m_transform), blockTextures[block], drawCallback, { blockPos = Vec4(x, y, z) }) | |
| end | |
| if not blockExists(x + 1, y, z) then | |
| addTriangle( | |
| Vert(v6, t1).transform(m_transform), | |
| Vert(v2, t2).transform(m_transform), | |
| Vert(v3, t3).transform(m_transform), blockTextures[block], drawCallback, { blockPos = Vec4(x, y, z) }) | |
| addTriangle( | |
| Vert(v6, t1).transform(m_transform), | |
| Vert(v3, t3).transform(m_transform), | |
| Vert(v7, t4).transform(m_transform), blockTextures[block], drawCallback, { blockPos = Vec4(x, y, z) }) | |
| end | |
| if not blockExists(x, y, z - 1) then | |
| addTriangle( | |
| Vert(v5, t1).transform(m_transform), | |
| Vert(v6, t2).transform(m_transform), | |
| Vert(v7, t3).transform(m_transform), blockTextures[block], drawCallback, { blockPos = Vec4(x, y, z) }) | |
| addTriangle( | |
| Vert(v5, t1).transform(m_transform), | |
| Vert(v7, t3).transform(m_transform), | |
| Vert(v8, t4).transform(m_transform), blockTextures[block], drawCallback, { blockPos = Vec4(x, y, z) }) | |
| end | |
| if not blockExists(x - 1, y, z) then | |
| addTriangle( | |
| Vert(v1, t1).transform(m_transform), | |
| Vert(v5, t2).transform(m_transform), | |
| Vert(v4, t4).transform(m_transform), blockTextures[block], drawCallback, { blockPos = Vec4(x, y, z) }) | |
| addTriangle( | |
| Vert(v4, t4).transform(m_transform), | |
| Vert(v5, t2).transform(m_transform), | |
| Vert(v8, t3).transform(m_transform), blockTextures[block], drawCallback, { blockPos = Vec4(x, y, z) }) | |
| end | |
| if not blockExists(x, y + 1, z) then | |
| addTriangle( | |
| Vert(v1, t1).transform(m_transform), | |
| Vert(v2, t2).transform(m_transform), | |
| Vert(v5, t4).transform(m_transform), blockTextures[block], drawCallback, { blockPos = Vec4(x, y, z) }) | |
| addTriangle( | |
| Vert(v2, t2).transform(m_transform), | |
| Vert(v6, t3).transform(m_transform), | |
| Vert(v5, t4).transform(m_transform), blockTextures[block], drawCallback, { blockPos = Vec4(x, y, z) }) | |
| end | |
| if not blockExists(x, y - 1, z) then | |
| addTriangle( | |
| Vert(v8, t4).transform(m_transform), | |
| Vert(v3, t2).transform(m_transform), | |
| Vert(v4, t1).transform(m_transform), blockTextures[block], drawCallback, { blockPos = Vec4(x, y, z) }) | |
| addTriangle( | |
| Vert(v3, t2).transform(m_transform), | |
| Vert(v8, t4).transform(m_transform), | |
| Vert(v7, t3).transform(m_transform), blockTextures[block], drawCallback, { blockPos = Vec4(x, y, z) }) | |
| end | |
| popMatrix() | |
| end | |
| end | |
| end | |
| popMatrix() | |
| drawTriangles() | |
| end | |
| self.renderScene = renderScene | |
| return self | |
| end | |
| local function main() | |
| local mon = peripheral.wrap("right") | |
| mon.setTextScale(0.5) | |
| -- Create the color palette. This is what | |
| -- will convert our RGB colors into pixels | |
| -- which can be written to the screen. | |
| local palette = createPalette(indexTable, 8, 8, 8, progressHook) | |
| term.clear() | |
| -- Create the frame buffer. This is what we | |
| -- apply the shaders to and can be quickly | |
| -- blitted onto the screen. | |
| local tw, th = term.getSize() | |
| print(tw, ", ", th) | |
| local frameBuffer = createFrameBuffer(palette, charTable, vec2(tw*2, th*3), STRATEGY_HEX_CELL--[[, mon.blit, mon.setCursorPos]]) | |
| local ctx = RenderContext(frameBuffer) | |
| local start = clock() | |
| while true do | |
| ctx.renderScene(frameBuffer) | |
| -- Draw the frame buffer onto the screen. | |
| drawFrameBuffer(frameBuffer, vec2(1, 1)) | |
| -- Here I'm just doing a trick to make the | |
| -- drawing run as fast as possible. You | |
| -- limit the number of frames drawn if you | |
| -- do this on a server. | |
| --os.queueEvent("") | |
| --os.pullEvent() | |
| os.queueEvent("__dummy") | |
| local e, k, h | |
| repeat | |
| if keyboard.getKeyDown(keys.tab) then | |
| term.clear() | |
| return | |
| end | |
| e, k, h = os.pullEvent() | |
| if e == "key_up" then | |
| keyboard.setKeyDown(k, false) | |
| elseif e == "key" and not h then | |
| keyboard.setKeyDown(k, true) | |
| end | |
| until e == "__dummy" | |
| end | |
| end | |
| -- Generic trace code. | |
| local function errHandler(e) | |
| term.setTextColor(colors.red) | |
| local errors = { } | |
| local ok, err, name | |
| local i = 4 | |
| print(e) | |
| while true do | |
| ok, err = pcall(error, "", i) | |
| if err:sub(1, 10) == "TRACE_ROOT" then | |
| break | |
| end | |
| print(err) | |
| i = i + 1 | |
| end | |
| term.setTextColor(colors.white) | |
| end | |
| local args = { ... } | |
| local function init(main, args) | |
| main(unpack(args)) | |
| end | |
| local bc = string.dump(init) | |
| xpcall(function() loadstring(bc, "TRACE_ROOT")(main, args) end, errHandler) | |
| os.unloadAPI("gfx") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment