Skip to content

Instantly share code, notes, and snippets.

@lucatronica
Last active July 4, 2020 17:06
Show Gist options
  • Save lucatronica/88a06e719172efe43984378354369a3a to your computer and use it in GitHub Desktop.
Save lucatronica/88a06e719172efe43984378354369a3a to your computer and use it in GitHub Desktop.
-- gumtree.lua
-- pico-8 3d/voxel library
--
-- coordinates: x,y = ground; z = vertical
--
-- how it works:
-- to make sure shapes in the right order, we need to sort them from back to front.
-- to do this shapes are drawn only once the scene has been populated.
-- we represent shapes as tables, created using gumtree functions.
--
-- how to use:
-- function _draw()
-- -- setup camera
-- gt_cam.x=0
-- gt_cam.y=0
-- gt_cam.s=t()/4
--
-- -- call gt_ draw methods between gt_start() and gt_end().
-- gt_start()
--
-- for i=0,15 do
-- gt_draw_new_voxel(i*0.2,0,0,1,i)
-- end
--
-- gt_end()
-- end
-- camera
local gt_cam={
-- position
x=0,
y=0,
z=0,
-- spin angle
s=0,
-- tilt angle
t=1/8,
-- scale (1 means 1:1)
k=8,
-- perspective (number > 0, or nil for orthographic)
p=80,
}
-- [internal] buckets
local gt_mind=nil
local gt_maxd=nil
local gt_buckets={}
-- [internal] save an object to the gumtree buckets
function gt_save_bucket(o,z)
-- save to bucket
-- depth resolution is int, can multiply
-- before flr() to increase resolution
-- (more buckets, but may perform worse)
local d=flr(z)
if gt_mind==nil then
gt_mind=d
gt_maxd=d
else
gt_mind=min(gt_mind,d)
gt_maxd=max(gt_maxd,d)
end
local b=gt_buckets[d]
if b==nil then
b={}
gt_buckets[d]=b
end
add(b,o)
end
-- transform a 3D point to screen space
-- returns multiple values: (screen_x, screen_y, screen_z, scale)
-- returns nil if point is outside the screen
function gt_transform(x,y,z)
local cs=gt_cam.cs
local ss=gt_cam.ss
local ct=gt_cam.ct
local st=gt_cam.st
local k=gt_cam.k
x=x-gt_cam.x
y=y-gt_cam.y
z=z-gt_cam.z
x,y=x*cs-y*ss,x*ss+y*cs
-- depth
local dz=(z*ct-y*st)*k
-- perspective
local p=gt_cam.p
if p then
if dz>=p then
return
else
k/=(1-dz/p)
end
end
return x*k+64,(y*ct+z*st)*k+64,dz,k
end
-- voxels
-- create a voxel object
function gt_voxel(x,y,z,w,c)
return {x=x,y=y,z=z,w=w,c=c,t=0}
end
-- create a voxel object and draw it
function gt_draw_new_voxel(x,y,z,w,c)
gt_draw_voxel({x=x,y=y,z=z,w=w,c=c,t=0})
end
-- draw a voxel object
function gt_draw_voxel(v)
-- save render position to object
local z,k
v.rx,v.ry,z,k=gt_transform(v.x,v.y,v.z)
if k==nil then
v.r=false
return
end
v.r=true
v.rw=v.w*k
gt_save_bucket(v,z)
end
-- lines
-- lines uses the mid-point for ordering
-- create a line object
function gt_line(x1,y1,z1,x2,y2,z2,c)
return {x1=x1,y1=y1,z1=z1,x2=x2,y2=y2,z2=z2,c=c,t=1}
end
-- create and draw a line object
function gt_draw_new_line(x1,y1,z1,x2,y2,z2,c)
gt_draw_line({x1=x1,y1=y1,z1=z1,x2=x2,y2=y2,z2=z2,c=c,t=1})
end
-- draw a line object
function gt_draw_line(l)
local z1,z2
l.rx1,l.ry1,z1=gt_transform(l.x1,l.y1,l.z1)
l.rx2,l.ry2,z2=gt_transform(l.x2,l.y2,l.z2)
if z1==nil or z2==nil then
l.r=false
return
end
l.r=true
--use center of line for depth
gt_save_bucket(l,(z1+z2)/2)
end
-- main functions
-- call before drawing objects, after camera parameters have been set
function gt_start()
gt_buckets={}
gt_mind=nil
gt_maxd=nil
gt_cam.cs=cos(gt_cam.s)
gt_cam.ss=sin(gt_cam.s)
gt_cam.ct=cos(gt_cam.t)
gt_cam.st=sin(gt_cam.t)
end
-- draws objects to the screen. call after all draw calls have been made
function gt_end()
if gt_mind then
for d=gt_mind,gt_maxd do
local b=gt_buckets[d]
if b then
for i=1,#b do
local o=b[i]
local t=o.t
if t==0 then
--voxel
local x=o.rx
local y=o.ry
local w=o.rw
rectfill(x-w,y-w,x+w,y+w,o.c)
elseif t==1 then
--line
line(o.rx1,o.ry1,o.rx2,o.ry2,o.c)
end
end
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment