From 2f38abe0d47aedccbe94ee626ed865c3adfa3d23 Mon Sep 17 00:00:00 2001 From: Kistaro Windrider Date: Fri, 4 Aug 2023 16:38:36 -0700 Subject: [PATCH] initial commit. --- README.md | 3 + kistaro-compositor.lua | 255 +++++++++++++++++++++++++++++++++++++++++ kistaro-compositor.tic | Bin 0 -> 5425 bytes 3 files changed, 258 insertions(+) create mode 100644 README.md create mode 100644 kistaro-compositor.lua create mode 100644 kistaro-compositor.tic diff --git a/README.md b/README.md new file mode 100644 index 0000000..764a57f --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# Kistaro's tic-80 compositor + +TIC-80 port of my Pico-8 compositor, including a lot of Pico-8 camera and clip behaviors that aren't available in TIC-80. diff --git a/kistaro-compositor.lua b/kistaro-compositor.lua new file mode 100644 index 0000000..6dc544a --- /dev/null +++ b/kistaro-compositor.lua @@ -0,0 +1,255 @@ +-- title: kistaro-compositor +-- author: Kistaro Windrider (kistaro@gmail.com) +-- desc: Rendering compositor library. Includes Pico-8 camera features. +-- site: https://dragon.style/@kistaro +-- license: MIT License +-- version: 0.1 +-- script: lua + + +function BOOT() +end + +function TIC() +end + +--stdlib-- + +-- generate standard "overlay" +-- constructor for type tt. +-- if more is defined, generated +-- new calls more(ret) after ret is +-- definitely not nil, before calling +-- setmetatable. Use to initialize +-- mutable items. +function MkNew(tt, more) + local mt = {__index=tt} + -- check "more" only once ever + if more then + tt.new = function(ret) + ret = ret or {} + more(ret) + setmetatable(ret, mt) + return ret + end + else + tt.new = function(ret) + ret = ret or {} + setmetatable(ret, mt) + return ret + end + end +end + +--Window compositor-- + +CamCtl = { + XOff = 0, + YOff = 0, + XAbsClip = 0, + YAbsClip = 0, + W = 240, + H = 136, + OriginStack = {}, + ClipStack = {}, +} + +-- Overwrite current camera origin. +-- (0, 0) is now this pixel coordinate. +function CamCtl:SetAbsOrigin(x, y) + self.XOff, self.YOff = x, y +end + +-- Push current camera origin onto +-- the history stack and set the +-- new origin. +function CamCtl:PushAbsOrigin(x, y) + table.insert(self.OriginStack, {self.XOff, self.YOff}) + self.XOff, self.YOff = x, y +end + +function CamCtl:SetRelOrigin(x, y) + self.XOff = self.XOff + x + self.YOff = self.Yoff + y +end + +function CamCtl:PushRelOrigin(x, y) + table.insert(self.OriginStack, {self.XOff, self.YOff}) + self.XOff = self.XOff + x + self.YOff = self.YOff + Y +end + +function CamCtl:SetAbsCam(x, y) + self.XOff, self.YOff = -x, -y +end + +function CamCtl:PushAbsCam(x, y) + table.insert(self.OriginStack, {self.XOff, self.YOff}) + self.XOff, self.YOff = -x, -y +end + +function CamCtl:SetRelCam(x, y) + self.XOff = self.XOff - x + self.YOff = self.YOff - y +end + +function CamCtl:PushRelCam(x, y) + table.insert(self.OriginStack, {self.XOff, self.YOff}) + self.XOff = self.XOff - x + self.YOff = self.YOff - y +end + +function CamCtl:PopOrigin() + if #self.OriginStack == 0 then + self.XOff, self.YOff = 0, 0 + return + end + self.XOff, self.YOff = unpack(table.Remove(self.OriginStack)) +end +CamCtl.PopCam = CamCtl.PopOrigin + +function CamCtl:SetNewClip(x, y, w, h) + if x then + self.XAbsClip = self.XOff + x + self.YAbsClip = self.YOff + y + self.W, self.H = w, h + clip(self.XAbsClip, self.YAbsClip, self.W, self.H) + return + end + self.XAbsClip, self.YAbsClip, self.W, self.H = 0,0,240,136 + clip() +end + +function CamCtl:PushNewClip(x, y, w, h) + table.insert(self.ClipStack, {self.XAbsClip, self.YAbsClip, self.W, self.H} + self:SetNewClip(x, y, w, h) +end + +function CamCtl:SetReClip(x,y,w,h) + if not x then + return + end + + x = x + self.XOff + local x2 = math.min(x + w, self.XAbsClip + self.W) + self.XAbsClip = math.max(x, self.XAbsClip) + self.W = max(0, x2 - self.XAbsClip) + + y = y + self.YOff + local y2 = math.min(y+h, self.YAbsClip + self.H) + self.YAbsClip = math.max(y, self.YAbsClip) + self.H = max(0, y2 - self.YAbsClip) + + clip(self.XAbsClip, self.YAbsClip, self.W, self.H) +end + +function CamCtl:PushReClip(x,y,w,h) + table.insert(self.ClipStack, {self.XAbsClip, self.YAbsClip, self.W, self.H} + self.SetReClip(x,y,w,h) +end + +function CamCtl:PopClip() + if #self.ClipStack == 0 then + self.XAbsClip, self.YAbsClip, self.W, self.H = 0,0,240,136 + clip() + return + end + local x,y,w,h = unpack(table.remove(self.ClipStack))) + self.XAbsClip, self.YAbsClip, self.W, self.H = x,y,w,h + clip(x,y,w,h) +end +-- Install +-- (swizzle all drawing functions!) + +function CamCtl:Install() + local adjust_leading_2 = { + "circ", "circb", + "elli", "ellib", + "pix", + "rect", "rectb", + } + for _, fn in ipairs(adjust_leading_2) do + local f_orig = _G[fn] + _G[fn] = function(x, y, ...) + return f_orig(x + self.XOff, y + self.YOff, ...) + end + end + + local keep_first_adjust_2_opt = { + "spr", "font", "print" + } + for _, fn in ipairs(keep_first_adjust_2_opt) do + local f_orig = _G[fn] + _G[fn] = function(v, x, y, ...) + if x then + x = x + self.XOff + end + if y then + y = y + self.YOff + end + return f_orig(v, x, y, ...) + end + end + + local tri_family = {"tri", "trib", "textri"} + + for _, fn in ipairs(tri_family) do + local f_orig = _G[fn] + _G[fn] = function(x1, y1, x2, y2, x3, y3, ...) + return f_orig( + x1 + self.XOff, y1 + self.YOff, + x2 + self.XOff, y2 + self.YOff, + x3 + self.XOff, y3 + self.YOff, + ... + ) + end + end + + local map_orig = map + map = function(mx, my, w, h, sx, sy, ...) + XXX(kistaro): implement! + -- if called with no args, calculate new map + -- coordinates, clipping at 0, to respect + -- camera movement! this will require full tile + -- movement to be reflected in mx/my, w and h to + -- stay 30 x 17 unless they go to 31 x 18 for + -- partial tiles at edges, and sx and sy to become + -- offsets. If sx and sy are provided, do the + -- obvious thing instead, which is much easier. + end + + local line_orig = line + line = function(x0, y0, x1, y1, color) + return line_orig( + x0 + self.XOff, y0 + self.YOff, + x1 + self.XOff, y1 + self.YOff, + color + ) + end +end +-- +-- 000:0222220021111110212121102122211021121110211111100111110000000000 +-- 001:0bbbbb00b9999980b999b980b99bb980b9bbb980b99999800888880000000000 +-- 002:0555550056666670565566705655567056655670566666700777770000000000 +-- 003:0444440043333320433433204344432043434320433333200222220000000000 +-- 004:0ddddd00deeeeef0dededef0deedeef0dededef0deeeeef00fffff0000000000 +-- + +-- +-- 000:00000000ffffffff00000000ffffffff +-- 001:0123456789abcdeffedcba9876543210 +-- 002:0123456789abcdef0123456789abcdef +-- + +-- +-- 000:000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000304000000000 +-- + +-- +-- 000:100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +-- + +-- +-- 000:1a1c2c5d275db13e53ef7d57ffcd75a7f07038b76425717929366f3b5dc941a6f673eff7f4f4f4be69ce61309940205d +-- + diff --git a/kistaro-compositor.tic b/kistaro-compositor.tic new file mode 100644 index 0000000000000000000000000000000000000000..e0a28696964f9e633377dcfafa824a428af676d4 GIT binary patch literal 5425 zcmb_g%a0pL8TSOxu3L!R3^&B#i&qF+({?-$EzzJ^Wtkv0f@PvS$V6yl^|Y()BK_L# z^4OhdB#z5}Ks0waaDfwoNZdIr;tycqFPP09qUA>TeN|oU$9R%if~9i3zWScO`l_nG z-mTTX^=xw?Tx!fzy9+-{`2gU-~DRv!*^C}-`1B-YBg#!YO9woudJ@FuB5DCo0X{{8*aPiv>2efHMr>FHai|14|tKY#I}{+_1z zpZ~t{)o-lSYAav+*EehJHYuq>u}wq|Wv;v|Y5Pf(Cb?8e=3v?zsbP{~{>NsT?#sBJ z$-c;_S+2M~h&&l~u&%```Xcw4AKex)$YeaADN73FVdiB;hi=AxI07x*l77;@M!pw` z%%ebfYLtn*!>pi%R*i;ArTO;NtNqLyBylHKMJTRbFXc03D18y?UHXaX@c5C) za+$>2)a|Tmj(#Rn1@*#_=QvIT$b~{&n=-zHS%w`~B zD60g)WpUrj`qW4uD)fp5Py0!nt8C=MhZLZwA{C^Rwn+vQC7B?Z!&8BbMc=L1^?7kD z#&AX$=9;dV3DqJmP;fY!SjIGZC7cr$6eo&e8M<^RAWG<^@HtcELPbJ($~z22hwkOj zAt4z{CB0BS5=;_}^ell_1fN&_`0(LR#ki@ItCeUu^)P`>6e-%F$GsjfASOFX9XoX} z@rS~HNDVftK}igylGqm{;1Q>8?Nvh&J9YK~8@NMOls3?**R^#!`X3&949@jBbF}mI zS!;L@swpT|ucJN6VqS?JN3br!T)adU@e-)O|D`NCk3BKfB74ZtNNKluHzbHxwPzS0 zyI!=bLiUtH`+GrvR@Zgt!L+&m{$aiw%GA!xckg3(V~dx3fY$ovyJ+oYavErwtW<<$_4R8B+)JCdv~V<3A5 z3&x!7J3>KCqepY%QqgjNurTPbiLPmuCi1NH5ZxN(!{xdFCMsbR0viM+uw4q?Q7{p^ zis8#MOu&$_`di>osTcD3LLZngm#I9Wu`Igr%Ybk zu6Yo?Ot^{KjO=NcW<%Cq9y0b#`xPOZ;r-fn&Bg20I?>D#1~rh1wr?eA2~l7i8TlP; z@648<9pot9ZLvdB%+5QMlLlr@K6HpP;CZB@IK?h*8q05s2#4dDUDz_mtL5$Z0 z$<&is-dq&dqJ9G2Mwg(M;5G^kdO!Mk5FcRNG%#ZAD06$K(}4vxIAj*;y>9@IO^C~; z-Cw*XT`Je;14{o{7d#Xq?FG{V){W!aF-*-K1W*b|SIWv7a=e8iOQTNGJ&$~sDAvKM%f#8VC^X`ssv zL4}B;784$UUzUe{y825yW3mocpiDMUqf9nYqb!F31A`{(Hu~lvQdE7721B#4KvVT+ zXf_vUs{WJ)YA`=WWaTqB!GJpQQVVM|9pr3&-&J^y5akGA#+k*(1D`_EpzrVR+s`Sj zZIV$MiU@DFD-eX;k2v!e0nw+iR6{)4$jb(~%Y*(Xf-!ilK>e3p-wcd1tr1i4T-zAOdst|jtdK$;~-_+o_*Eq%zti&8r^IXsfdh!tV) z;HjvP84;eL^oRU3jz*}3mrIc$=yQ1_#Ah2DYHkAo_@52-1g