abledbody [Lexaloffle Blog Feed]https://www.lexaloffle.com/bbs/?uid=93379 Rainbow Tylenol <p>A cover of Kitsune^2's Rainbow Tylenol for Picotron.<br /> <table><tr><td> <a href="/bbs/?pid=168985#p"> <img src="/bbs/thumbs/pico64_rainbow_tylenol-1.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=168985#p"> rainbow_tylenol</a><br><br> by <a href="/bbs/?uid=93379"> abledbody</a> <br><br><br> <a href="/bbs/?pid=168985#p"> [Click to Play]</a> </td></tr></table> </p> https://www.lexaloffle.com/bbs/?tid=149741 https://www.lexaloffle.com/bbs/?tid=149741 Sun, 22 Jun 2025 23:41:04 UTC Abledbody's External Development Template <p>Repository: <a href="https://github.com/abledbody/picotron-external-template">https://github.com/abledbody/picotron-external-template</a><br /> Direct download: <a href="https://github.com/abledbody/picotron-external-template/archive/refs/heads/main.zip">https://github.com/abledbody/picotron-external-template/archive/refs/heads/main.zip</a></p> <h2>How to use</h2> <ol> <li>Copy this directory to wherever you want the project to be.</li> <li>Rename the directory and template.p64 to match the name of the project.</li> <li>Open <code>template.p64/main.lua</code> and...<br /> - Change this line: <code>cd(&quot;/projects/template&quot;)</code> to point at the directory of your project, keeping the leading slash.<br /> - Change this line: <code>DATP = &quot;template.p64/&quot;</code> to match the name of the cartridge, keeping the trailing slash.</li> </ol> <p>If you're using Visual Studio Code and would like to use LuaCATS definitions for Picotron, you can either put them in <code>C:\lualib\picotron-definitions</code>, or you can edit <code>settings.json</code> to point to the directory of your choice. I personally recommend cloning <a href="https://github.com/Rayquaza01/picotron-definitions">Rayquaza01's definition files</a>, but bear in mind that at time of writing, there are no fully complete and accurate definitions for Picotron, and Picotron itself is still in active development, so you may occasionally see warnings for things that are actually valid in Picotron.</p> <h2>Important stuff</h2> <p>The entry point for your project's code is <code>src/main.lua</code>, rather than <code>template.p64/main.lua</code>. All scripts go into <code>src/</code> so that they are unaffected by Picotron's metadata management, and can live outside of <code>ram/cart</code> so that iterating on them does not require loading the cartridge more than once. When you are ready to distribute the cartridge, all you need to do is make sure there is a copy of your <code>src</code> directory at the root of the cartridge in <code>ram/cart</code> before you save or export it.</p> <p>Whenever you want to refer to a file that lives in the cartridge, use <code>DATP..</code> as a prefix, such as <code>fetch(DATP..&quot;gfx/0.gfx&quot;)</code>. This is because the working directory is different depending on where your <code>src</code> directory is. If <code>src</code> is at the root of the cartridge, the working directory will be <code>ram/cart</code>. Otherwise, it will be whatever you set it to in <code>main.lua</code>. <code>DATP</code> compensates for this by always pointing to the cartridge root.</p> <h2>Trivia</h2> <p>This template is designed to be used with Sumneko's Lua language server extension for VSCode, and the <a href="https://luals.github.io/wiki/annotations/">Lua Comments And Type System (LuaCATS) annotations</a> it provides.</p> <p>This template comes with:</p> <ul> <li><a href="https://www.lexaloffle.com/bbs/?pid=147383#p">Kira's Error Explorer</a> (with thanks), because I find it invaluable, since as of 0.2.0d, Picotron's error handling is garbage.</li> <li>A <code>require</code> function, to work around how the lua language server does not recognize Picotron's <code>include</code> function. It's also just nicer for dependency management than <code>include</code>.</li> </ul> <p>Picotron-specific operators such as <code>a \ b</code>, <code>a ..= b</code>, and <code>?x</code> are completely unrecognized by the Lua language server, and so will show errors, even if Picotron itself recognizes them. Each has a simple substitute. <code>a // b</code>, <code>a = a .. b</code>, and <code>print(x)</code>, respectively. One-line if statements such as <code>if (x) print(y)</code> are also unrecognized.</p> <p>The ruler setting in <code>.vscode/settings.json</code> indicates where the horizontal boundary of the <code>code.p64</code> app is when using its monospace font, in case that's something you care about.</p> https://www.lexaloffle.com/bbs/?tid=149123 https://www.lexaloffle.com/bbs/?tid=149123 Wed, 21 May 2025 19:10:49 UTC Scrub: That animation tool <p>Scrub is an animation data editor which allows you to make and preview <code>.anm</code> files.</p> <p><code>load #scrub</code><br /> <table><tr><td> <a href="/bbs/?pid=165294#p"> <img src="/bbs/thumbs/pico64_scrub-2.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=165294#p"> scrub</a><br><br> by <a href="/bbs/?uid=93379"> abledbody</a> <br><br><br> <a href="/bbs/?pid=165294#p"> [Click to Play]</a> </td></tr></table> </p> <h2>File format</h2> <p>The <code>.anm</code> file format consists of a table with string keys and values containing animations. Each animation contains string keys with arrays of data, each the same length. The only required key in an animation is <code>duration</code>, which is a series of numbers indicating the amount of time it takes for each frame to elapse. There is an optional <code>events</code> key, where the values are a dictionary of strings keys and arbitrary values, representing each event that occurs at the start of its respective frame.</p> <p>Within the Scrub cart, the script src/animation.lua contains everything you need to play the animations in the <code>.anm</code> files in your own projects.</p> <h2>Features</h2> <p>Scrub will automatically detect the properties <code>sprite</code> and <code>pivot</code>, and will show a sprite in the viewport that reflects them.</p> <p>Scrub supports properties and events with values that are:</p> <ul> <li>numbers, such as <code>3.5</code></li> <li>userdata vectors, such as <code>(3.5,8)</code></li> <li>strings, such as <code>foo</code></li> <li>booleans, written as <code>true</code> or <code>false</code></li> <li>nil, written as <code>nil</code> (properties only)</li> </ul> <p>Scrub supports custom palettes in the same format that OkPal uses.</p> <h2>Hotkeys</h2> <ul> <li>Space - Play</li> <li>Left - Select last frame</li> <li>Right - Select next frame</li> <li>Insert - Insert a new frame</li> <li>Delete - Delete selected frames</li> <li>Shift+Click - Frame multi-select</li> </ul> <h2>Planned features</h2> <ul> <li>Allow previewing sprites from files other than <code>0.gfx</code></li> <li>Automatic sprite sequences</li> <li>Show 2D vectors, and be able to edit them and the pivot in the viewport</li> <li>Undo</li> <li>Buttons on the transport for last frame, next frame, start, end, and playback speed</li> <li>Specify which properties are the sprite/pivot</li> <li>Show multiple sprites on the same frame</li> </ul> <h2>Known bugs</h2> <p>None at the moment.</p> <h2>Changelog</h2> <h3>v2</h3> <ul> <li>Added frame events</li> </ul> <h3>v1.1</h3> <ul> <li>Fixed spaces being added when you click on properties</li> <li>Fixed program crashing when it can't find <code>ram/cart/gfx/gfx.0</code> (probably)</li> <li>Fixed left/right hotkeys causing the properties window to lag behind the selection</li> <li>Added support for boolean properties</li> <li>Added whitespace tolerance for all non-string property values</li> </ul> https://www.lexaloffle.com/bbs/?tid=148336 https://www.lexaloffle.com/bbs/?tid=148336 Thu, 10 Apr 2025 05:21:10 UTC Advanced f64 userdata arithmetic (0.1.1D) <p>f64 userdatas have the following arithmetic operations:<br /> <code>add, sub, mul, div, mod, min, max, idiv</code><br /> We can create a few additional useful operations using just these.</p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>local function ud_floor(x) return x-x:mod(1) end local function ud_floor(x) return x:idiv(1) end -- Alternative. Faster for userdatas smaller than ~270 elements. local function ud_round(x) x += 0.5 return x-x:mod(1) end local function ud_ceil(x) return (0-x):mod(1)-x end local function ud_abs(x) return x:max(0-x) end local function ud_sign(x) return x/(x:max(0-x)) end -- Not zero-safe. local function ud_positive(x) return x:max(0)/x end -- Not zero-safe. local function ud_negative(x) return x:min(0)/x end -- Not zero-safe. local function ud_tern(c,a,b) return a*c+b*(1-c) end</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>Here are some other useful userdata math tricks:</p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>local function sum(x) return vec(0):add(x,true,0,0,1,1,0,#x)[0] end local function prod(x) return vec(1):mul(x,true,0,0,1,1,0,#x)[0] end -- If you have a matrix with a vector in each row, and you'd like to get their dot products -- with a single vector, you can do so like this: local function dot_all(v,m) return m:matmul(v:transpose()) end -- luchak discovered this prefix sum operation. local function psum(v) return v:add(v,true,0,1,1,1,1,#v-1) end -- which is useful for creating a linear sequence with a specific slope. -- v[n] = init+n*step local function linstep(v,init,step) return v:copy(step,true):copy(init,true,0,0,1):add(v,true,0,1,1,1,1,#v-1) end</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> https://www.lexaloffle.com/bbs/?tid=146271 https://www.lexaloffle.com/bbs/?tid=146271 Tue, 24 Dec 2024 08:42:42 UTC Sphere Impostor Demo <p>It's like a circle, but in three dimensions, but without the third dimension.<br /> <table><tr><td> <a href="/bbs/?pid=159057#p"> <img src="/bbs/thumbs/pico64_sphere_impostor-4.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=159057#p"> sphere_impostor</a><br><br> by <a href="/bbs/?uid=93379"> abledbody</a> <br><br><br> <a href="/bbs/?pid=159057#p"> [Click to Play]</a> </td></tr></table> </p> https://www.lexaloffle.com/bbs/?tid=146027 https://www.lexaloffle.com/bbs/?tid=146027 Wed, 11 Dec 2024 10:02:58 UTC Blade3D Teapot Demo <p>I made this demo to quickly demonstrate how to use Blade3D, a 3D rendering API I'm working on. It doesn't make full use of the API, but it does give you a starting point if you want to make your own projects using (this version of) Blade3D. Hopefully I can make a slightly more comprehensive introduction to the API with full documentation in the near future, but for anyone willing to look at the underlying code, it should be pretty easy to accomplish whatever you need with just this template.</p> <p> <table><tr><td> <a href="/bbs/?pid=153792#p"> <img src="/bbs/thumbs/pico64_b3d_teapot-11.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=153792#p"> b3d_teapot</a><br><br> by <a href="/bbs/?uid=93379"> abledbody</a> <br><br><br> <a href="/bbs/?pid=153792#p"> [Click to Play]</a> </td></tr></table> </p> <p>Something that isn't included in the template is a .obj to .ptm converter, which is provided here. It expects the .obj to include material references (not the .mtl file itself) and for the mesh to be triangulated.<br /> <div><div><input type="button" value=" Show " onClick="if (this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display != '') { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = ''; this.innerText = ''; this.value = ' Hide '; } else { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = 'none'; this.innerText = ''; this.value = ' Show '; }"></div><div><div style="display: none;"></p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre> --[[pod_format=&quot;raw&quot;,created=&quot;2024-06-23 21:43:33&quot;,modified=&quot;2024-07-03 20:32:21&quot;,revision=46]] local path = env().argv[1] local output_path = string.sub(path,1,string.find(path,&quot;.obj$&quot;))..&quot;ptm&quot; local material_lookup = {} local material = nil local obj = { v = {}, vt = {}, vp = {}, f = {}, } -- Each element in an obj starts with an identifier. -- We can use this identifier to determine how to interpret -- the rest of the line. local interps = { v = function(ln) add(obj.v,vec(ln[2],ln[3],ln[4],1)) end, vt = function(ln) add(obj.vt,vec(ln[2],ln[3])) end, vp = function(ln) add(obj.vp,vec(ln[2],ln[3],ln[4])) end, usemtl = function(ln) material = ln[2] end, f = function(ln) local f = {} if material_lookup[material] then f.mat_index = material_lookup[material] else add(material_lookup,material) material_lookup[material] = #material_lookup f.mat_index = #material_lookup end for i = #ln,2,-1 do local indices = split(ln[i],&quot;/&quot;) local v = {} v.v = indices[1] if indices[2] ~= &quot;&quot; then v.vt = indices[2] end v.vn = indices[3] add(f,v) end add(obj.f,f) end, } for l in all(split(fetch(path),&quot;\n&quot;)) do local tokens = split(l,&quot; &quot;) local interpreter = interps[tokens[1] ] if interpreter then interpreter(tokens) end end local pts = userdata(&quot;f64&quot;,4,#obj.v) for i = 1,#obj.v do local v = obj.v[i] pts:set(0,i-1,v[0],v[1],v[2],v[3]) end local uvs = userdata(&quot;f64&quot;,2,#obj.vt) for i = 1,#obj.vt do local vt = obj.vt[i] uvs:set(0,i-1,vt[0],1-vt[1]) -- Top to bottom instead of bottom to top. end local materials = {} for i = 1,#material_lookup do add(materials,material_lookup[i]) end local pt_indices = userdata(&quot;i32&quot;,3,#obj.f) local uv_indices = userdata(&quot;i32&quot;,3,#obj.f) local material_indices = userdata(&quot;i32&quot;,#obj.f) for i = 1,#obj.f do local face = obj.f[i] material_indices[i-1] = face.mat_index local v1,v2,v3 = face[1],face[2],face[3] -- Subtract 1 from each index to convert to 0-based. pt_indices:set(0,i-1,v1.v-1,v2.v-1,v3.v-1) uv_indices:set(0,i-1,v1.vt-1,v2.vt-1,v3.vt-1) end local output = { pts = pts, uvs = uvs, materials = materials, pt_indices = pt_indices, uv_indices = uv_indices, material_indices = material_indices, } local icon = userdata(&quot;u8&quot;,16,16,&quot;000000000000010101010000000000000000000000010707070701000000000000000000010707070101070100000000000000010707070107070107010000000000010707070707010701070701000000010d0707010107070107070706010000010d0701070101070707070706010000010d0d07070701070707070606010000010d0d0d070107070707060606010000010d0d0d0d0707070706060606010000010d0d0d0d0d0606060606060601000000010d0d0d0d0d0606060606010000000000010d0d0d0d060606060100000000000000010d0d0d06060601000000000000000000010d0d060601000000000000000000000001010101000000000000&quot;) store(output_path,pod(output,0b111),{icon = icon}) </pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p></div></div></div></p> https://www.lexaloffle.com/bbs/?tid=144056 https://www.lexaloffle.com/bbs/?tid=144056 Thu, 05 Sep 2024 19:16:08 UTC abledbody's instrument pack 2 <p>An <em>additional</em> 32 synthesized instruments for your composing pleasure.<br /> <code>#able_ipack_02</code></p> <img loading="lazy" style="margin-bottom:16px" border=0 src="/bbs/cposts/ab/able_ipack_02-0.p64.png" alt="" /> <p>Available under the Creative Commons 0 license.</p> <p>If you've got requests for the next instrument pack, let me know!</p> https://www.lexaloffle.com/bbs/?tid=143833 https://www.lexaloffle.com/bbs/?tid=143833 Sat, 24 Aug 2024 02:12:15 UTC Second order dynamics demo <p>Arrow keys to edit the variables.<br /> Click (and hold, if you like,) to set the target position.<br /> <table><tr><td> <a href="/bbs/?pid=151375#p"> <img src="/bbs/thumbs/pico64_sod_demo-0.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=151375#p"> sod_demo</a><br><br> by <a href="/bbs/?uid=93379"> abledbody</a> <br><br><br> <a href="/bbs/?pid=151375#p"> [Click to Play]</a> </td></tr></table> </p> <p>If you're just interested in the API, you can grab it here:<br /> <div><div><input type="button" value=" Show " onClick="if (this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display != '') { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = ''; this.innerText = ''; this.value = ' Hide '; } else { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = 'none'; this.innerText = ''; this.value = ' Show '; }"></div><div><div style="display: none;"></p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>local Sod = {} Sod.__index = Sod function Sod:update(dt) local target_vel = (self.target-self.prev_target)/dt self.pos += self.vel*dt self.vel += ( self.target +self.k3*target_vel -self.pos -self.k1*self.vel )*dt/self.k2 self.prev_target = self.target end function Sod:config(frequency_rads,damping,response) local ifreq = 1/frequency_rads self.k1,self.k2,self.k3 = damping*ifreq*2, ifreq*ifreq, response*damping*ifreq end function sod(pos,frequency_rads,damping,response) local o = { pos = pos, prev_target = pos, target = pos, -- In case pos is a vector, or any other arithmetically capable data -- type, *0 guarantees we get the zero position of whatever type pos is. vel = pos*0, } setmetatable(o,Sod) o:config(frequency_rads,damping,response) return o end</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p></div></div></div></p> https://www.lexaloffle.com/bbs/?tid=143166 https://www.lexaloffle.com/bbs/?tid=143166 Tue, 16 Jul 2024 00:36:42 UTC Sort Our Ship <p>A clone of <a href="https://panthema.net/2013/sound-of-sorting/">The Sound of Sorting</a> I made just to test and diagnose my QuickSort algorithm.<br /> It may sound a little ridiculous on web, since the array access sounds tend to get lumped together into far fewer frames.</p> <ul> <li>Press z to step</li> <li>Hold z to disable step mode</li> <li>Press x to switch sorting algorithm</li> <li>Use up and down to control the number of entries.</li> <li>Use left and right to control the speed.</li> </ul> <p> <table><tr><td> <a href="/bbs/?pid=148518#p"> <img src="/bbs/thumbs/pico64_sort_our_ship-5.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=148518#p"> sort_our_ship</a><br><br> by <a href="/bbs/?uid=93379"> abledbody</a> <br><br><br> <a href="/bbs/?pid=148518#p"> [Click to Play]</a> </td></tr></table> </p> https://www.lexaloffle.com/bbs/?tid=142305 https://www.lexaloffle.com/bbs/?tid=142305 Fri, 17 May 2024 03:02:05 UTC The Lab Grows prototype <p>The response to this has been pretty positive, so I'm throwing this demo up here on the forums.<br /> This cart is a prototype for a point and click adventure with pre-rendered 3D graphics and a custom palette.<br /> <table><tr><td> <a href="/bbs/?pid=147629#p"> <img src="/bbs/thumbs/pico64_the_lab_grows-2.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=147629#p"> the_lab_grows</a><br><br> by <a href="/bbs/?uid=93379"> abledbody</a> <br><br><br> <a href="/bbs/?pid=147629#p"> [Click to Play]</a> </td></tr></table> <br /> Included in this cart are <a href="https://www.lexaloffle.com/bbs/?tid=140784">elgopher's require module</a> and <a href="https://www.lexaloffle.com/bbs/?pid=147383#p">snowkittykira's error explorer module</a></p> https://www.lexaloffle.com/bbs/?tid=142042 https://www.lexaloffle.com/bbs/?tid=142042 Wed, 01 May 2024 00:41:11 UTC abledbody's instrument pack 1 <p>32 synthesized instruments for your composing pleasure.</p> <p><code>#able_ipack_01</code></p> <img loading="lazy" style="margin-bottom:16px" border=0 src="/bbs/cposts/ab/able_ipack_01-2.p64.png" alt="" /> <p>Available under the Creative Commons 0 license.</p> <p>If you've got requests for the next instrument pack, let me know!</p> <p>00 - Wist<br /> 01 - Industrial Guitar<br /> 02 - Bell<br /> 03 - Industrial Bass<br /> 04 - Farout<br /> 05 - Closed Hat<br /> 06 - Open Hat<br /> 07 - Retro Snare<br /> 08 - Flat Kick<br /> 09 - Dial<br /> 10 - Glass Piano<br /> 11 - Flute<br /> 12 - Vibran<br /> 13 - Horn<br /> 14 - Grind Pad<br /> 15 - Smack Tom<br /> 16 - State Approved<br /> 17 - Disturbation<br /> 18 - Acoustic Snare<br /> 19 - Sweep Bass<br /> 20 - Bell 2<br /> 21 - Tropic Pan<br /> 22 - Funky Bass<br /> 23 - Crack<br /> 24 - Steel Guitar<br /> 25 - Saxophone<br /> 26 - Anchor<br /> 27 - Undercurrent Pad<br /> 28 - Sidestick<br /> 29 - Pop Bass<br /> 30 - Everywhere<br /> 31 - True Synth</p> https://www.lexaloffle.com/bbs/?tid=141982 https://www.lexaloffle.com/bbs/?tid=141982 Fri, 26 Apr 2024 21:42:19 UTC able's ECS framework <p>I made it as easy to use and as performant as I could. You certainly won't be getting the performance boost normally associated with an ECS, since that particular optimization doesn't exist in picotron, but you'll still get the architecture.</p> <p>Here's a demo which showcases its use.<br /> <table><tr><td> <a href="/bbs/?pid=146516#p"> <img src="/bbs/thumbs/pico64_able_ecs-2.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=146516#p"> able_ecs</a><br><br> by <a href="/bbs/?uid=93379"> abledbody</a> <br><br><br> <a href="/bbs/?pid=146516#p"> [Click to Play]</a> </td></tr></table> </p> <p>You can find the latest version and the documentation on github.<br /> <a href="https://github.com/abledbody/picotron-ECS-framework/releases/">https://github.com/abledbody/picotron-ECS-framework/releases/</a><br /> <a href="https://github.com/abledbody/picotron-ECS-framework/blob/main/README.md">https://github.com/abledbody/picotron-ECS-framework/blob/main/README.md</a></p> https://www.lexaloffle.com/bbs/?tid=141689 https://www.lexaloffle.com/bbs/?tid=141689 Sat, 13 Apr 2024 04:37:05 UTC abledbody's Profiler <p>Does what it says on the tin. Lets you see what's eating up your CPU time. If you make an extension to this, I'd love to see it.</p> <h3>API:</h3> <p><code>profile.enabled(detailed,cpu)</code> Turns on and off profiling tools.<br /> <code>detailed</code>: Whether or not to display results of <code>profile</code> calls.<br /> <code>cpu</code>: Whether or not to display total CPU usage.</p> <p><code>profile(name,linger)</code> Starts or stops a profile. Accumulates between <code>profile.draw</code> calls. Extremely cheap when profiling is disabled.<br /> <code>name</code>: Arbitrary display name indicating which profile to start or stop.<br /> <code>linger</code>: Whether or not the profile should linger even if the profile is never called between draws.</p> <p><code>profile.draw()</code> Draws profile and CPU information to the screen if they are enabled. Extremely cheap when profiling is disabled.</p> <p><code>profile.clear_lingers()</code> Clears any lingering profile information.</p> <h3>Example:</h3> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>include(&quot;profiler.lua&quot;) profile.enabled(true,true) local frames = 0 function _update() profile(&quot;_update&quot;) frames = frames+1 profile(&quot;_update&quot;) end function _draw() profile(&quot;_draw&quot;) if frames%2 == 0 then profile(&quot;displaying frame count&quot;,true) print(frames,80,0,7) profile(&quot;displaying frame count&quot;,true) end profile.draw() profile(&quot;_draw&quot;) end</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <h2>Full script:</h2> <p><div><div><input type="button" value=" Show " onClick="if (this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display != '') { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = ''; this.innerText = ''; this.value = ' Hide '; } else { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = 'none'; this.innerText = ''; this.value = ' Show '; }"></div><div><div style="display: none;"></p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>--[[pod_format=&quot;raw&quot;,created=&quot;2024-04-09 22:52:04&quot;,modified=&quot;2024-04-11 17:26:16&quot;,revision=1003]] -- abledbody's profiler v1.1 local function do_nothing() end -- The metatable here is to make profile() possible. -- Why use a table at all? Because otherwise lua will try to cache the function call, -- which by default is do_nothing. local profile_meta = {__call = do_nothing} profile = {draw = do_nothing} setmetatable(profile,profile_meta) local running = {} -- All incomplete profiles -- All complete profiles. Note that if the profiles haven't been drawn yet, it will -- not be cleared, and further profiles of the same name will add to the usage metric. local profiles = {} -- All completed lingering profiles. These are never automatically cleared. local lingers = {} -- start_profile, stop_profile, and stop_linger are all internal functions, -- serving as paths for _profile to take. Lingers share start_profile. local function start_profile(name,linger) local source = profiles[name] running[name] = { linger = linger, } local active = running[name] active.start = stat(1) --Delaying CPU usage grab until the last possible second. end local function stop_profile(name,active,delta) local profile = profiles[name] if profile then profile.time = delta+profile.time else profiles[name] = { time = delta, name = name, } add(profiles,profiles[name]) end end local function stop_linger(name,active,delta) local profile = lingers[name] if profile then profile.time = profile.this_frame and delta+profile.time or delta profile.this_frame = true else lingers[name] = { time = delta, this_frame = true, } end end -- The main functionality lives here. -- Takes in the name of what you're profiling, and whether or not to -- make the profile linger. local function _profile(_,name,linger) local t = stat(1) local active = running[name] if active then local delta = t-active.start if active.linger then stop_linger(name,active,delta) else stop_profile(name,active,delta) end running[name] = nil else start_profile(name,linger) end end -- Clears all lingering profiles. function profile.clear_lingers() lingers = {} end local function draw_cpu() print(&quot;cpu:&quot;..string.sub(stat(1)*100,1,5)..&quot;%&quot;,1,1,7) end -- This draws the profiles, and then resets everything for the next frame. -- If it is not called, usage metrics will accumulate. -- Lingering profiles are always displayed after persistent profiles. local function display_profiles() local i = 1 for prof in all(profiles) do local usage = string.sub(prof.time*100,1,5)..&quot;%&quot; local to_print = prof.name..&quot;:&quot;..usage print(to_print,1,1+i*9,7) i = i+1 end for name,prof in pairs(lingers) do local usage = string.sub(prof.time*100,1,5)..&quot;%&quot; local to_print = name..(prof.this_frame and &quot;[X]:&quot; or &quot;[ ]:&quot;)..usage print(to_print,1,1+i*9,7) prof.this_frame = false i = i+1 end profiles = {} end local function display_both() draw_cpu() display_profiles() end -- This swaps out function calls depending on whether or not you want to have -- profiling. This is to make it as much as possible so that you don't have to -- think about cleaning up profile calls for efficiency. -- The first boolean is for detailed profiling, the second is for CPU usage. function profile.enabled(detailed,cpu) profile_meta.__call = detailed and _profile or do_nothing profile.draw = detailed and (cpu and display_both or display_profiles) or (cpu and draw_cpu or do_nothing) end</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p></div></div></div></p> https://www.lexaloffle.com/bbs/?tid=141662 https://www.lexaloffle.com/bbs/?tid=141662 Fri, 12 Apr 2024 04:30:18 UTC