Menu
Projects

Private work in progress

Brooklyn Dead

A game whose art is written, not modelled. Blender Python → glTF → Godot 4.

A night street in the mob lab, a group of zombies lit by a wall lamp

The detail tier at playing distance, in the mob lab at night. Every asset in the frame was generated from Python.

BKZ lab log · 3 entriesBlender · Python · glTF 2.0 · Godot 4

In simple terms

A survival game I am building in private, whose 3D art is written as code rather than modelled by hand.

The interesting problem in a game this size is not any single asset. It is that there are hundreds of them and one person making them. So a Python file describes an asset and the 3D software builds it: characters, weapons, mobs, furniture, vehicles and crafting stations, along with their materials, modifiers and animations.

Four stages, each with a contract at its edge. Code generates the geometry, the 3D software exports a standard interchange file, a browser preview renders it for inspection without opening the game engine, and the engine imports the same file the preview did.

The impact. Every asset is a source file rather than a binary, so it can be diffed and reviewed like code, and changing one rule rebuilds every asset that follows it.

The game

Brooklyn Dead is a survival game I am building in private, in Godot 4. It has the shape you would expect from the genre: characters you outfit, mobs in tiers, weapons in a taxonomy, and crafting stations that get better as you do. The design work sits in spec documents. The part I can put on record here is everything underneath it, because the interesting problem in a game this size is not any one asset. It is that there are hundreds of them and one person making them.

So none of them are modelled by hand. A Python file describes an asset and Blender builds it. Characters, weapons, mobs, furniture, vehicles and crafting stations all come out of code, along with their materials, their modifiers and their animations.

What the pipeline is

Four stages, each with a contract at its edge. Python generates geometry through Blender's API. Blender exports glTF 2.0. A browser preview layer built on Three.js renders the result for inspection without opening the engine. Godot imports the same file the preview did.

Every asset is therefore a source file, not a binary. It diffs. It reviews. A palette change or a rule change rebuilds the whole set, and two people can work on the same character without either of them owning a .blend file nobody else can open. Preview tooling in the browser is the kind of thing you normally only see where a tools team exists; I built it for myself so iteration does not cost an engine round trip.

Generated art needs gates

This is the part that makes it a pipeline rather than a folder of scripts. Assets that are built rather than saved can regress silently: someone changes a shared constant, every character rebuilds, and nothing tells you that eleven of them are now subtly wrong. Hand-modelled art fails loudly, in a viewport, in front of the person who made it. Generated art fails at three in the morning in a build log.

So the generators are gated. Validators run on every rebuild and check the things a human would have noticed by eye: that hair covers the scalp, that geometry has not sunk inside the body it sits on, that triangle budgets hold, that attachment sockets still point the way the engine expects.

Writing those tests turns out to be harder and more interesting than writing the generators. A test for generated geometry has to measure against something, and choosing what it measures against is the whole game.

What that looks like when it goes wrong

The clearest example I have: a coverage gate that had returned a perfect score for months while the render disagreed with it. Both images below come from the same script, the same camera and the same lights. The only thing that changed is the surface the hair was built against.

The buzz hair style rendered from the front, before the fixbefore
The buzz hair style rendered from the front, after the fixafter
buzz, from the front. On the left, a dome that swallows the forehead. On the right, the same generator seated on the real skull. No art direction changed between them.

The lab log

Findings like that one get written up rather than fixed and forgotten. The BKZ lab log is where the methodology lives: what broke, how it was measured, what the numbers said before and after, and what I priced and then refused. It is the record I would want if I came back to this codebase in a year.

Animation, in code

Motion is authored the same way the meshes are. A helper layer wraps keyframing so a drawer, a door, a drop-front or a looping crafting cycle is described by its behaviour: Bezier easing, NLA assembly, staggered timing across parts that have to move together. Interaction metadata rides along with it, so hitboxes and facing empties arrive in the engine already named and placed.

Cross-tool contracts

Three tools have to agree about which way is up, where a weapon attaches, and what a socket is called. None of that is left implied. Axes and attachment sockets are specified and named by convention, and the naming is enforced by the same gates that check the geometry, because a contract nothing verifies is a comment.

How I describe the skill set

  • Procedural modelling
  • Blender Python
  • Technical art
  • glTF 2.0
  • Godot 4
  • Three.js
  • Keyframe / NLA animation
  • PBR materials
  • Pipeline engineering
  • Test design

Most people either make art or write code. I write code that makes art, and then I write the tests that decide whether the art is right. The assets are the output. The pipeline is the thing I actually built.