Skip to content

Instantly share code, notes, and snippets.

@mutoo
Last active June 16, 2020 14:24
Show Gist options
  • Save mutoo/858df40c7ff0f9576158f685fb1bf260 to your computer and use it in GitHub Desktop.
Save mutoo/858df40c7ff0f9576158f685fb1bf260 to your computer and use it in GitHub Desktop.
draw random triangles in aseprite
local image = app.activeCel.image:clone()
local black = Color { r = 0, g = 0, b = 0, a = 255 }
local white = Color { r = 255, g = 255, b = 255, a = 255 }
local width = image.width
local height = image.height
local size = width * height
local nearest = 0.1
local farest = 1000
function area(p0, p1, p)
return (p1[1] - p0[1]) * (p[2] - p0[2]) - (p1[2] - p0[2]) * (p[1] - p0[1])
end
function createBuffer(size, fill)
local buffer = {}
for i = 0, size - 1 do
buffer[i] = fill
end
return buffer
end
local frameBuffer = createBuffer(size, nil)
local zBuffer = createBuffer(size, farest)
local camera = { 0, 0, 3 }
function getBoundingBox(p0, p1, p2)
local t, r, b, l
t = math.floor(math.max(0, math.min(p0[2], p1[2], p2[2])))
r = math.floor(math.min(math.max(p0[1], p1[1], p2[1]), width - 1))
b = math.floor(math.min(math.max(p0[2], p1[2], p2[2]), height - 1))
l = math.floor(math.max(0, math.min(p0[1], p1[1], p2[1])))
return { t, r, b, l }
end
function worldToCamera(p)
local x = p[1] - camera[1]
local y = p[2] - camera[2]
local z = p[3] - camera[3]
return { x, y, z }
end
function cameraToScreen(p)
local x = nearest * p[1] / -p[3]
local y = nearest * p[2] / -p[3]
local z = -p[3]
return { x, y, z }
end
local t = 2
local l = -2
local b = -2
local r = 2
function screenToNDC(p)
local x = 2 * p[1] / (r - l) - (r + l) / (r - l);
local y = 2 * p[2] / (t - b) - (t + b) / (t - b);
return { x, y }
end
function NDCToCanvas(p)
local x = (p[1] + 1) / 2 * width
local y = (p[2] + 1) / 2 * height
local z = p[3]
return { x, y, z }
end
local triangles = {}
for i = 1, 1000 do
local p0 = { math.random(-50, 50), math.random(-50, 50), -math.random(1, 10) }
local p1 = { math.random(-50, 50), math.random(-50, 50), -math.random(1, 10) }
local p2 = { math.random(-50, 50), math.random(-50, 50), -math.random(1, 10) }
table.insert(triangles, { p0, p1, p2 })
end
function drawPoint(buffer, p, c)
local x = math.floor(p[1])
local y = math.floor(p[2])
buffer[x + y * width] = c
end
for _, triangle in ipairs(triangles) do
local t0, t1, t2 = table.unpack(triangle)
local p0 = NDCToCanvas(screenToNDC(cameraToScreen(worldToCamera(t0))))
local p1 = NDCToCanvas(screenToNDC(cameraToScreen(worldToCamera(t1))))
local p2 = NDCToCanvas(screenToNDC(cameraToScreen(worldToCamera(t2))))
-- local p0, p1, p2 = table.unpack(triangle)
local w = area(p0, p1, p2)
if w >= 0 then
local bbox = getBoundingBox(p0, p1, p2)
local c = Color { r = math.random(0, 255), g = math.random(0, 255), b = math.random(0, 255), a = 255 }
-- drawPoint(frameBuffer, p0, c)
-- drawPoint(frameBuffer, p1, c)
-- drawPoint(frameBuffer, p2, c)
for row = bbox[1], bbox[3] do
for col = bbox[4], bbox[2] do
local p = { col, row }
local w0 = area(p0, p1, p)
local w1 = area(p1, p2, p)
local w2 = area(p2, p0, p)
if w0 >= 0 and w1 >= 0 and w2 >= 0 then
local oneOverZ = p0[2] * w0 + p1[2] * w1 + p2[2] * w2;
local z = 1 / oneOverZ
local idx = row * image.width + col
if z < zBuffer[idx] then
zBuffer[idx] = z
frameBuffer[idx] = c.rgbaPixel
end
end
end
end
end
end
for it in image:pixels() do
local c = it()
it(frameBuffer[it.y * image.width + it.x] or black.rgbaPixel)
end
app.activeCel.image = image
app.refresh()
@mutoo
Copy link
Author

mutoo commented Jun 16, 2020

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment