Skip to main content

Command Palette

Search for a command to run...

teaching the renderer to loop

the parts existed for two weeks before anything drew them

Updated
•3 min read•View as Markdown

the data side had been done for a while. load a twelve material model and all twelve materials were sitting there in memory, correctly split, each with its own vertices and its own colours

and the screen still showed one grey blob

that gap bothered me more than it should have. everything was right and nothing looked right. the renderer was still doing what it always did, one draw call for the whole model with one texture bound, completely unaware that the geometry underneath it had grown a structure

the loop

the change itself is small. instead of drawing the geometry, walk its parts and draw each one

for each part, apply its state, draw it, put the state back. that last bit matters more than it sounds. if a sketch calls texture(myTex) and then draws a model, the model's own textures must not leak out and silently repaint whatever comes next. so each part is drawn inside a push() and pop(), which is the same mechanism a user would reach for, not a custom reset function that would need to remember every field

a single material model is one part, so the loop runs once, and the result is byte for byte what it always was. that was the thing i kept checking. zero regression is not a nice property here, it is the whole reason this design was allowed to exist

an eight material model rendering with each face its own colour

eight materials on one model, one model() call. this is the test fixture that used to come out as a single flat shape

strokes

i had done fills and quietly assumed strokes came along for free

they did not. review caught it, and the phrasing was gentle in a way that made it obvious i had missed something structural rather than a typo. fills were per part. strokes were still whole model. so a multi material model with stroke() on would draw its outlines from the aggregate geometry while its faces came from the parts, and those two things do not necessarily agree

added per part stroke drawing and a spy test so it stays that way

the small ones

a few things from the same round that were quick but worth writing down

the fallback for a geometry with no parts got deleted. there was a branch handling that case, and when i went looking for a scenario that could reach it, there was not one. the constructor always sets parts = [this], and a loader can only replace that with a longer list. dead code that looked defensive

a test was wrapping loadModel() in a promise. it already returns one. removed the wrapper

another test was passing an instance count to model() while testing something unrelated. i had put it there to confirm the count reaches each part, which is a real thing to test, just not in that test. split it out

none of that is interesting on its own. together it is the difference between code that works and code someone else can read

next

everything renders now, colours and all. what it does not do yet is textures beyond the basic one, and there is a longer review thread waiting that i have not fully worked through

more soon