```--[[ vgfx.lua
vector graphics drawing
=== todo: ===
-polygon outlines
-more triangulation functions
-tween/interpolate between two vector sprites/polygons
-animation playback
-compression for vector data
-maybe try writing directly to graphics memory if it's faster
also:
-make a vector sprite editor/animator app
=== docs: ===
"vector sprite": an array of polygons
"polygon": a table with the elements:
- col (color)
- col2 (color 2)
- col3 (outline color)
- verts (vertices)
- outline (also vertices)
"vertices": 1d f66 userdata numbers (x0,y0, y1,y2, ...etc)
vertices list should be triangulated, sprv() will NOT triangulate arbitrary polygons
tri() and triraster() are decent triangle drawing functions, for lightweight
vector drawing, skip the vector sprite stuff and just use these
=== changes: ===
- polygon vertices now use userdata
- wire rendering uses same colour rules as filled triangles
]]
vgfx_wire=false--enable for debug view of polygon triangles
vgfx_precise=false--enable if you have issues with edge seams
function make_polygon(c1,c2, vertices, outline, c3)
local polygon={col=c1,col2=c2,col3=c3, verts=userdata("f64",#vertices),outline=nil}
for i=1,#vertices do
set(polygon.verts, i-1, vertices[i])
end
if outline==true then
--this doesn't work yet!
local oline = get_outline(vertices)
polygon.outline = userdata("f64",#oline)
for i=1,#oline do
set(polygon.outline, i-1, oline[i])
end
elseif outline!=nil then
polygon.outline = userdata("f64",#outline)
for i=1,#outline do
set(polygon.outline, i-1, outline[i])
end
end
return polygon
end
function edgematch(a0x,a0y, a1x,a1y, b0x,b0y, b1x,b1y)
if (a0x==b0x and a0y==b0y) then
return (a1x==b1x and a1y==b1y)
end
if (a1x==b0x and a1y==b0y) then
return (a0x==b1x and a0y==b1y)
end
return false
end
function get_outline(triverts)
--this doesn't work yet.
--even though it should.
--it's misbehaving.
--don't give it any cookies.
local oline={}
for ta=1,#triverts+6,6 do
local add_a = true
local add_b = true
local add_c = true
for tb=ta+6,#triverts+6,6 do
if (edgematch(triverts[ta], triverts[ta+1], triverts[ta+2], triverts[ta+3],
triverts[tb], triverts[tb+1], triverts[tb+2], triverts[tb+3])) then
add_a = false
end
if (edgematch(triverts[ta], triverts[ta+1], triverts[ta+2], triverts[ta+3],
triverts[tb+2], triverts[tb+3], triverts[tb+4], triverts[tb+5])) then
add_a = false
end
if (edgematch(triverts[ta], triverts[ta+1], triverts[ta+2], triverts[ta+3],
triverts[tb+4], triverts[tb+5], triverts[tb], triverts[tb+1])) then
add_a = false
end
if (edgematch(triverts[ta+2], triverts[ta+3], triverts[ta+4], triverts[ta+5],
triverts[tb], triverts[tb+1], triverts[tb+2], triverts[tb+3])) then
add_b = false
end
if (edgematch(triverts[ta+2], triverts[ta+3], triverts[ta+4], triverts[ta+5],
triverts[tb+2], triverts[tb+3], triverts[tb+4], triverts[tb+5])) then
add_b = false
end
if (edgematch(triverts[ta+2], triverts[ta+3], triverts[ta+4], triverts[ta+5],
triverts[tb+4], triverts[tb+5], triverts[tb], triverts[tb+1])) then
add_b = false
end
if (edgematch(triverts[ta+4], triverts[ta+5], triverts[ta], triverts[ta+1],
triverts[tb], triverts[tb+1], triverts[tb+2], triverts[tb+3])) then
add_c = false
end
if (edgematch(triverts[ta+4], triverts[ta+5], triverts[ta], triverts[ta+1],
triverts[tb+2], triverts[tb+3], triverts[tb+4], triverts[tb+5])) then
add_c = false
end
if (edgematch(triverts[ta+4], triverts[ta+5], triverts[ta], triverts[ta+1],
triverts[tb+4], triverts[tb+5], triverts[tb], triverts[tb+1])) then
add_c = false
end
end
if add_a then
add(oline, triverts[ta])
add(oline, triverts[ta+1])
add(oline, triverts[ta+2])
add(oline, triverts[ta+3])
end
if add_b then
add(oline, triverts[ta+2])
add(oline, triverts[ta+3])
add(oline, triverts[ta+4])
add(oline, triverts[ta+5])
end
if add_c then
add(oline, triverts[ta+4])
add(oline, triverts[ta+5])
add(oline, triverts[ta])
add(oline, triverts[ta+1])
end
end
return oline
end
--draw a vector sprite
function sprv(v, x,y, rot,scale, col)
local s, cs = 0,0
if rot and rot!=0 then
s = sin(rot)
cs = cos(rot)
end
local outline = false
local sinxy = vec(0,0,0,0,0,0)
local cosxy = vec(0,0,0,0,0,0)
local triv = userdata("f64",6)
local tpos = vec(x,y,x,y,x,y)
--local tscale = vec(scale,scale,scale,scale,scale,scale)
if (not scale) scale = 1
if (col) color(col)
for p=1,#v do--loop through each polygon in the vector sprite
--prep to draw this polygon's colours
if not col then
fillp(0b1010010110100101)
color((v[p].col<<8)+v[p].col2)
end
--draw each tri now
local b=0
while b<#v[p].verts do
set(triv, 0, v[p].verts:get(b,6))
b+=6
triv *= scale
if rot and rot!=0 then
sinxy = trivs
cosxy = trivcs
set(triv, 0,
cosxy[0]-sinxy[1], sinxy[0]+cosxy[1],
cosxy[2]-sinxy[3], sinxy[2]+cosxy[3],
cosxy[4]-sinxy[5], sinxy[4]+cosxy[5])
end
triv += tpos
tri(triv[0],triv[1],triv[2],triv[3],triv[4],triv[5])
if (v[p].outline) outline = true
end
end
if outline then
for p=1,#v do
if v[p].outline then
b=0
local oline=vec(0,0,0,0)
if (not col) color((v[p].col3<<8)+v[p].col3)
while b<#v[p].outline do
set(oline, 0, v[p].outline:get(b,4))
b+=4
--fillp()
--color()
oline = scale
if rot and rot!=0 then
sinxy = olines
cosxy = oline*cs
set(oline, 0,
cosxy[0]-sinxy[1], sinxy[0]+cosxy[1],
cosxy[2]-sinxy[3], sinxy[2]+cosxy[3])
end
oline += tpos
line(oline[0],oline[1],oline[2],oline[3])
end
end
end
end
end
function fan_triangulate(points)
local v,i={},3
while i<=#points do
add(v,points[i].x)
add(v,points[i].y)
add(v,points[i-1].x)
add(v,points[i-1].y)
add(v,points[1].x)
add(v,points[1].y)
i+=1
end
return v
end
function strip_triangulate(points)
if (#points<=3) return points
local v,i={},4
add(v,points[1].x)
add(v,points[1].y)
add(v,points[2].x)
add(v,points[2].y)
add(v,points[3].x)
add(v,points[3].y)
while i<=#points do
add(v,points[i].x)
add(v,points[i].y)
add(v,points[i-1].x)
add(v,points[i-1].y)
add(v,points[i-2].x)
add(v,points[i-2].y)
i+=1
end
return v
end
vgfx_trisdrawn=0
function tri(x0,y0, x1,y1, x2,y2)
vgfx_trisdrawn += 1
if (vgfx_precise) x0,y0,x1,y1,x2,y2 = flr(x0),flr(y0),flr(x1),flr(y1),flr(x2),flr(y2)
--wireframe
if vgfx_wire then
fillp()
line(x0,y0, x1,y1)
line(x1,y1, x2,y2)
line(x2,y2, x0,y0)
return
end
--order the vertices so they are descending from top to bottom
--we need this since we are drawing it as two triangles:
--one with a flat base, one with a flat top
if (y1<y0) x0,x1=x1,x0; y0,y1=y1,y0
if (y2<y1) x1,x2=x2,x1; y1,y2=y2,y1
if (y1<y0) x0,x1=x1,x0; y0,y1=y1,y0
--draw the top half
local hh=y1-y0--height of the half
local x3=x0+hh*(x2-x0)/(y2-y0)--slicing the tri in two makes another vertex
if (y0!=y1) triraster(y0,y1, (x3-x0)/hh,(x1-x0)/hh, x0,x0)
--draw the bottom half
hh=y2-y1
if (y1!=y2) triraster(y1,y2, (x2-x1)/hh,(x2-x3)/hh, x1,x3)
end
--draws a filled triangle line-by-line, top-to-bottom
--args: top, bottom, step left, step right, left pixel, right pixel
function triraster(t,b, sl,sr, pl,pr)
for y=t,b do
rectfill(pl,y,pr,y)
pl+=sl
pr+=sr
end
end```