← HOME
Building a 3D World with Playable Characters
/ 105
FIT
JOURNAL 01 // TPS ARENA // PROTOTYPE3D WORLDS
Engineering journal

Building a 3D World with Playable Characters

September to October 2026 · The first step toward 3D worlds people can walk through: how we built a playable third-person game on Three.js and Electron, and what it took to make its characters and its world hold up.
PARTS05
CHAPTERS27
BUILDv0.38
ENGINETHREE.JS
LAST UPDATED 11 OCTOBER 2026CASE STUDY: A THIRD-PERSON GAME
ENGINEERING JOURNALTPS ARENA
--Overview

Building a 3D World with Playable Characters

September to October 2026 · The first step toward 3D worlds people can walk through: how we built a playable third-person
game on Three.js and Electron, and what it took to make its characters and its world hold up.

This is the engineering journal of TPS Arena. It covers what we built, what broke, which routes we weighed, what we
picked and what it cost. Each chapter is written as problems, causes and solutions. Where our first diagnosis was
wrong, the chapter says so and gives the real cause.

The short version is that getting a shooter to run is not the hard part. The first playable build came quickly. Everything
after that is the distance between a build that runs and a game that feels right. That distance is made of problems like
a jump that only worked one frame in three, hair bound to a character's hands, a sword that took twenty iterations to
sit in a fist, and a frame-rate meter that reported the wrong number.

Why a game first

A game is the hardest everyday test of a 3D world. Its characters have to move, collide, be lit and hold a frame rate
while real people use them. Everything in this journal, from making a generated model fit for real time to keeping a
five-a-side match above 100 fps, is groundwork for the worlds we want to build next: digital buildings that mirror real
landmarks, places to walk through and shop in, and previews that let a property developer show a project before it is
built, in time in VR and AR as well as on a screen.

TPS Arena is the case study. This journal is the record of what it took, problem by problem.

The tools, in the order they joined

OrderToolThe bottleneck it removedWhat it did not fix
1Three.js + ElectronA playable game in the browser and as a
desktop build
Characters made of boxes
2Meshy (image to 3D)Sculpted, textured characters without a
character artist
One fused shell: no separate hair, no
clean topology
3Mixamo (skeleton +
animation clips)
Real motion in place of poses written as
numbers
Clips are generic. A character's own
moves still need authoring
4Blender, scriptedPlacing a sword by dragging it once, not by
twenty numeric iterations
The result still has to be judged by eye
5Higgsfield (pictures and
3D inside Blender)
Concept art and map pieces in one windowCredits. And generation was the
wrong tool for architecture
6Local models on the GPU
(ComfyUI, Hunyuan3D)
Picture edits and rough 3D at no credit costA 6 GB card is the ceiling
7CascadeurPlanned: hand-authored combat animationInstalled, not used yet
8KTX2 / Basis Universal
(compressed textures)
Character textures stay compressed on the
graphics card: four times less video memory at
the same map sizes
Draw calls. Memory was no longer the
limit; the number of things drawn per
frame is
BUILDING A 3D WORLDPG 03
ENGINEERING JOURNALTPS ARENA

Chapters

The chapters are in build order, grouped into five parts. Page 00 sets out what we built and the tools behind it. Part V
holds the two reference pages, and the roadmap is always the last chapter.

#ChapterWhat happenedMilestone
Part I · Engine, gameplay and first characters
01Three.js setup: jump input
and pointer lock
We chose Three.js and Electron, fixed a jump that only
registered one frame in three, added a post-
processing stack and fixed a mouse capture that
failed silently.
v0.01 to v0.05. A playable build and
a portable exe.
02Generated mesh to
rigged game character
We replaced code-built characters with a Meshy
model on a skeleton. We reduced a 2.3 million triangle
mesh without smearing its texture and re-bound hair
that the automatic rig had tied to the hands.
v0.06 to v0.14. Agent E runs in game
as a rigged GLB (v0.10).
03Weapon grip in hand and
a fake Mixamo rig
Agent S's grip took about twenty numeric iterations.
We diagnosed the real problem: art was being
authored as numbers in code. We also found that a
"Mixamo rig" from Meshy was not Mixamo's skeleton.
v0.15 to v0.16. Waist twist down
from 177 degrees to at most 18.
DEVELOPER_GUIDE.md
written.
04Mixamo clips: fingers,
feet, sword flips
Agent E and Agent Zero moved onto Mixamo's
skeleton. We fixed open fingers caused by a bone
write cache, a sword that flipped between clips, and
feet that sat at the wrong angle.
v0.17 to v0.18. A 13-rule asset
contract
(tools/herocheck.mjs) and
tools/heroprep.mjs
.
05Lag fixes, and NPCs built
on player code
We traced freezes to dynamic lights and to bullets
tested against skinned bodies. We found 48 colliders
that were never drawn. We rebuilt NPCs so they run
the character's own code.
v0.19 to v0.30. Bot extends
Player
(v0.23). A pickup freeze of
up to 4,689 ms became 17 to 25 ms.
Part II · Art tools in the pipeline
06Blender hand sockets, Git
LFS, review rules
The grip was solved by placing a socket by hand in
Blender. We moved assets to Git LFS, built Agent L
and his kit, and set the rule that a diagnosis is
reported before any fix.
v0.32. The game matches the
Blender socket to three decimals.
tools/kitcheck.mjs
.
07Clone abilities, aim offset,
hand-keyed draw
We rebuilt Agent L's clone as an ability, not a
character. We turned the camera so strikes meet the
crosshair. We authored the sword draw by hand, frame
by frame.
v0.32.45 to v0.33.12. Camera turn
fixed at 12 degrees. The draw's cut
lands 0.23 s after the click.
08Reaching 120 fps and
measuring frames right
We measured first, then pooled clones, added
bounding-volume trees and baked shadows. We
found the frame meter was counting loop ticks, not
finished frames.
v0.33.4. Low went from 29 fps to 116
to 137 fps in 5 v 5. npm run
bench
.
09Map pipeline: greybox,
then a modelled kit
Olympus shipped as a greybox first. We then spent
about 250 credits generating pieces that should have
been modelled in Blender for nothing.
v0.33 to v0.35.3. Olympus V1 and
V2. docs/LESSONS.md started.
10High-to-low baking and
local AI on 6 GB
Straight decimation shattered a statue's blade. We
replaced it with a high-to-low bake. We set up
ComfyUI and Hunyuan3D on a 6 GB card.
v0.35.5 to v0.36. All eight gods
stand on Olympus V2.
tools/blender/hero_bake.py
.
Part III · The character quality problem
11Unarmed character:
decimation, pose, drift
The first character with no weapon. We reduced his
mesh from 655,622 to 59,996 triangles with the face
protected, fixed a rest-pose mismatch in the Blender
preview and a 12-degree sideways drift.
v0.37 and v0.37.1. One movement
rule for every character.
docs/HERO_KITS.md
.
BUILDING A 3D WORLDPG 04
ENGINEERING JOURNALTPS ARENA
#ChapterWhat happenedMilestone
12Fused-shell hair: why it
tears when it moves
The first healer. Her kit was quick. Shortening her hair
exposed that the generated model has no back under
the hair.
v0.37.2 on her own branch. Kit
checker: 49 abilities, 0 mismatches.
13Character quality gap:
cause and four routes
We compared how our characters are constructed
with how Fortnite and Overwatch characters are
constructed, and weighed four routes to close the
gap.
A five-step plan: hair bake-off, per-
clump hair chains, character rework,
netcode, stay on Three.js.
Part IV · Making a generated character game-ready
14Agent E rebuild: lineup,
timeline and cost
Four days and more than a hundred named builds
rebuilding our first character, Agent E. This chapter
gives the lineup and the cost; the ten that follow take
one problem each: why her face looked worse in the
game, seams and a collar that moved, the master we
skipped, colour only an eye can fix, a shoulder hole
that was in the source, the monster neck of a one-
shell route, the full-resolution body, the head weld (W1
to W7), and the road to game size. 35 to 48 hours of
active work, much of it by hand in Blender.
v0.38. W7, the new benchmark for
Agent E: 47,557 triangles in game,
textures compressed from 213 MB
to 53 MB on the GPU. Rule One: the
eye marks, the script measures.
15Faceting: weld before
decimating
Why the in-game face looked worse than its sculpt:
the wedges were our own unwelded reduction. Twelve
free repaints and three remeshes ranked by eye.
Weld before reducing, bake after.
Face from T8, hair paint from T3.
16Seam gaps and a capped
collar
The first part-by-part rebuilds, holes at every seam,
and a collar that took eleven builds.
R8fix v6, on par with the old model
(56,875 triangles).
17Master first, then reduceThe full-resolution master we had skipped, and the
R8x family reduced from it.
R8x at 39,344 triangles. Master
first, then reduce.
18Fixing AI paint errors by
hand
Red paint on her collarbones, three automatic fixes
that looked like burn marks, and the brush that
replaced them.
R8x hand-painted: the pick.
19Texture atlas, first rig,
source holes
A 63 MB atlas, the first rig, and a shoulder hole that
had been in the source all along.
Streetwear X in the game (46,709
triangles).
20Voxel remesh fills: the
monster neck
Collar welds, two routes built and rejected in an
afternoon, the monster neck of a one-shell route, and
Meshy's remesh made into a finished figure.
B7 kept as the benchmark body.
M8x in the game. Rule One.
21Full-res body: hood,
shoulders, normals
The T8 sculpt at full detail as the body, a hood top
nobody had seen, and a shoulder that took eleven
attempts, much of it by hand in Blender.
Option 2, the body every later build
stands on.
22Welding a new head onto
a body
A new head welded to the body so face and neck read
as one person (W1 to W7).
W7, the new benchmark for Agent E.
23Game budget, KTX2 and
skinning spikes
From the 475,573-triangle benchmark to game size by
hand, rigged, with textures compressed for the
graphics card.
v0.38. 47,557 triangles, 213 MB
down to 53 MB on the GPU.
24Build cost and the rules it
left
The machine, the process, and the 35 lessons the
rebuild wrote into our documents.
The next build ran in 56 seconds.
Part V · Reference
25Architecture referenceReference. The decisions that shape the game, how a
character is made, how it is tested, and the
performance budget.
About 15,000 lines of plain
JavaScript. Characters, abilities,
weapons and maps are data.
BUILDING A 3D WORLDPG 05
ENGINEERING JOURNALTPS ARENA
#ChapterWhat happenedMilestone
26Engineering rules and
their incidents
Reference. Every rule we follow, with the incident that
produced it.
Measure before changing. Decide
looks by eye, in the game.
27RoadmapAlways the last chapter. What is next and why:
carrying Agent E's recipe to the rest of the roster,
performance on a mid-range card, netcode for LAN
play and the remaining map work.
Target: six characters, one finished
map, playable on LAN.
CH 00Front matter

About this journal

What this journal is, what we built, and the tools behind it.

What it is

This is the engineering journal of TPS Arena, a third-person shooter built on Three.js and Electron. It covers the build
from the first playable version to v0.38. It is a record of problem solving. Each chapter takes a stretch of the build and
explains what we made, what went wrong, why it went wrong and how we fixed it.

Every figure in it was measured. Frame times, triangle counts, joint angles and damage numbers come from our own
test harness, our benchmark and the patch notes.

What we built

AreaWhere it stands
The gameA third-person shooter that runs in a browser or as a desktop app.
Versionsv0.01 to v0.38. About 170 entries in docs/PATCH_NOTES.md, each with what changed and why.
Code history478 commits and 42 tags.
CharactersAgent E, Agent S, Agent Zero, Agent L, Agent B, Agent J and Agent C.
MapsOlympus, in two playable versions.
Project documents13 files: developer guide, handoff, characters, maps, lessons, character kits, clips, the Agent L
draw, Blender setup and lore.

The busiest series by far is v0.32, with 63 entries. It covers Agent S's grip, Agent L, the clones and the camera.

The kind of work changed part way through. The early chapters are code, with many small commits. The later chapters
are art in Blender, with few commits and long stretches of modelling.

The stack and the tools

ToolThe bottleneck it removedWhat it did not fix
Three.js + ElectronA playable game in the browser and as a desktop
build
Characters made of boxes
BUILDING A 3D WORLDPG 06
ENGINEERING JOURNALTPS ARENA
ToolThe bottleneck it removedWhat it did not fix
Meshy (image to 3D)Sculpted, textured characters without a character
artist
One fused shell: no separate
hair, no clean topology
Mixamo (skeleton + animation clips)Real motion in place of poses written as numbersClips are generic. A character's
own moves still need authoring
Blender, scriptedPlacing a sword by dragging it once, not by twenty
numeric iterations
The result still has to be judged
by eye
Higgsfield (pictures and 3D inside
Blender)
Concept art and map pieces in one windowCredits. And generation was the
wrong tool for architecture
Local models on the GPU (ComfyUI,
Hunyuan3D)
Picture edits and rough 3D at no credit costA 6 GB card is the ceiling
CascadeurPlanned: hand-authored combat animationInstalled, not used yet
KTX2 / Basis Universal (compressed
textures)
Character textures stay compressed on the graphics
card: four times less video memory at the same map
sizes
Draw calls. Memory was no
longer the limit
BUILDING A 3D WORLDPG 07
ENGINEERING JOURNALTPS ARENA
PART I5 chapters
I

Engine, gameplay and first characters

  1. 01Three.js setup: jump input and pointer lock
  2. 02Generated mesh to rigged game character
  3. 03Weapon grip in hand and a fake Mixamo rig
  4. 04Mixamo clips: fingers, feet, sword flips
  5. 05Lag fixes, and NPCs built on player code
BUILDING A 3D WORLDPG 08
ENGINEERING JOURNALTPS ARENA
CH 01Part I · Engine, gameplay and first characters

Three.js setup: jump input and pointer lock

September 2026 · v0.01 to v0.05 · A playable shooter, the engine decision, and the first bugs.

What we builtA playable third-person shooter in the browser, with a portable desktop exe, code-built characters,
bots, a post-processing stack and graphics presets.
The challengeA jump that ignored two key presses out of three. A mouse capture that failed silently. And the
question of whether the engine was the limit on how the game looked.
Key decisionsThree.js in the browser, wrapped in Electron. Stay on it and do not move to Unreal. Log every
change as a patch note from the start.
The resultA build that plays correctly, an engine decision recorded as final, and a clear finding: the gap was
character assets, not the renderer.

1. The brief and the stack

The brief was a third-person shooter with customisable characters. Agents have skills and abilities, as in Valorant. The
camera is third person, as in Marvel Rivals. New agent models would be added later from reference pictures.

The brief did not name an engine. We chose Three.js in the browser, wrapped in Electron for a desktop build.
Characters were built in code and the enemies were bots. That gave us a playable build and a portable exe.

2. The jump

Problem. A player standing on a box could not reliably jump off it. Jumping and bunny hopping are core movement in
this genre, so this had to be right.

Cause. We found it by reading the physics, not by guessing. On a box, the ground check only registered once every
three frames. Gravity had to sink the capsule into the box before it counted as grounded. Space was ignored two
times out of three.

Solution. A real floor probe every frame, plus coyote time, jump buffering and bunny hopping.

The same round of play set our quality bar for everything after it.

AreaOur bar
SoundGunfire that sounds real and is pleasant to listen to.
AnimationThe first animation read as blocky. It has to look like a character, not a block figure.
MapsA map must look like a place, not an aim trainer.

3. Is the gap Unreal Engine?

Problem. Our goal is animation in the class of Overwatch. An Overwatch-lite or Fortnite-lite result is good enough for
a beta. The build was far from that. We had to decide whether the engine was the gap, and whether the existing code
would carry over or need a rewrite.

BUILDING A 3D WORLDPG 09
ENGINEERING JOURNALTPS ARENA
OptionWhat it givesTrade-offVerdict
Switch to UnrealNative AAA toolingThe existing code is thrown away. The work
moves into a graphical editor that cannot be
read, changed and tested as plain code, so most
character work becomes manual.
Rejected
Stay on Three.jsPBR, shadows, skinned animation
and post-processing. Plain code in
one repo that can be read, changed
and tested.
We hand-build cheaper versions of what Unreal
ships with.
Chosen

Cause. The engine is not the gap. The gap was character assets first, because every character was built from boxes
in code. Then animation. Then lighting and effects.

Solution. We stayed on Three.js. Our project rules record the engine decision as final. Chapter 13 puts that answer
under test again.

4. The first named character, and graphics before models

Agent E was the first named character. She carries a giant blade and has no teleports, in the style of Magik from
Marvel Rivals or Vendetta from Overwatch. She has a red ponytail.

We chose to raise the rendering quality before taking on 3D models. The game gained a post-processing stack:
ambient occlusion, bloom, tone mapping and anti-aliasing. It also gained High, Medium and Low presets.

5. The cursor that would not lock

Problem. The pause menu was dead and the crosshair would not move.

Cause. An ordering problem. On Deploy, the game generated all its procedural textures before asking the browser to
capture the mouse. That took seconds. The browser's click permission expired and the capture failed silently.

Solution. Lock first, and pre-warm the textures earlier.

Problem. The browser was stealing keys. Ctrl+D bookmarked the page when the player crouched.

Solution. Every key is swallowed while a match runs.

At this point the repo moved to GitHub, so the work could continue across machines.

6. Patch notes from the start

We set one habit early. Every change gets an entry in docs/PATCH_NOTES.md, the way a shipped game publishes
patch updates. Those entries are meant to become the public patch notes at release. The character select screen
renders that file in-game.

7. The limit of code-built characters

Problem. The figures looked like toy block figures. Our bar is a character in the style of Fortnite.

First attempt. We painted faces onto the heads. It made them worse. The faces looked unsettling, and no face was
better than that face.

Solution for now. The other characters went back to blank faces. Agent E kept hers for one more round. That
round is where chapter 02 starts.

BUILDING A 3D WORLDPG 10
ENGINEERING JOURNALTPS ARENA

What this gave us

A game that runs and plays correctly, a fixed engine choice, and a ranked list of what actually stood between us and
the look we wanted: character assets, then animation, then lighting and effects.

CH 02Part I · Engine, gameplay and first characters

Generated mesh to rigged game character

September 2026 · v0.06 to v0.14 · Meshy, a rig, and hair bound to her hands.

What we builtA loader for rigged GLB characters, the first generated character (Agent E) running in game, a
height rule for every character, two-bone inverse kinematics for arms and legs, and a hair re-
binding tool (tools/hairbones.mjs).
The challengeA generated model arrives at 2.3 million triangles, as one fused shell. Its clips are padded. Its hair
is bound to whatever bone was nearest.
Key decisionsImage-to-3D plus a skeleton, not more tuning of procedural faces. Reduce the mesh locally to
keep the texture mapping. Hair is a stiff mass at gameplay distance. Free-flowing motion is kept
for a cape.
The resultAgent E at 245k triangles and 17 MB, rigged and running in the game (v0.10). Real clips take over
wherever one exists (v0.12).

1. The turning point

Problem. After another round on Agent E's painted face, it still did not read as a character.

Cause. It was not a tuning problem. Eyes painted on a sphere with capsule hair will never read as a sculpted
character.

The pipeline we weighed against it was: concept art, then an image-to-3D generator, then Mixamo for the skeleton,
then a GLB file into Three.js.

OptionWhat it givesTrade-offVerdict
Keep tuning the procedural faceFreeCannot reach a sculpted look, however long
it is tuned.
Rejected
Image-to-3D (Meshy) + a Mixamo
skeleton
Sculpted, textured, rigged
meshes
The generation steps are manual, in an
external tool.
Chosen

Solution. We made the engine load a rigged GLB. We tested it on a free placeholder model, which is still in the
roster as the character "Proto". This is the first tool added to the workflow, and the first of the jumps in speed
described in chapter 13.

2. Getting a usable model out of Meshy

The first model came back white, with no texture. It was in an action pose with the sword fused to the body. We
regenerated it in an A-pose with empty hands.

Problem. The model was 2.3 million triangles. Meshy's limit for rigging is 300k. A higher count is not automatically
better. It has to be reduced without damaging the texture.

BUILDING A 3D WORLDPG 11
ENGINEERING JOURNALTPS ARENA
OptionWhat it givesResult
Meshy's own remesh to 30kUnder Meshy's 300k
limit for rigging
Smeared the hair texture across her face and chest.
Reduce locally (weld + simplify)Keeps the texture
mapping
A first pass at 72k left red spots on the face where hair had
collapsed onto skin. A stricter pass at 245k kept the face clean.

Why our 72k model looked worse than a 30k Overwatch model. An Overwatch model is built by hand,
with every triangle placed on purpose. A generated one is not. Chapter 13 covers this in full.

Result. Agent E was rigged and running in the game at 245k triangles and 17 MB. That build is v0.10, the first with a
3D model.

3. Heights

Problem. Agent E looked tiny next to the other characters.

Our rule. Characters range from 1.55 m to 1.95 m. Each has a true stature that drives the rig, the hitbox and the
camera.

Our first fix was wrong. The old code-built characters drew at about 1.19 times their stated height. We enlarged
Agent E to match them, which put her at 1.96 m in world height.

Solution. Agent E is 1.65 m, as specified. Everyone else was made proportionally smaller.

4. Animation: 5 out of 100

Problem. With a real model on screen, the hand-written animation looked worse, not better. On our own scale, where
80 out of 100 is smooth and realistic animation, the ability animations sat at about 5. They needed a major rework.

Cause. Joint rotations had been guessed axis by axis. Knees and elbows bent the wrong way.

Solution. We rebuilt the arms and legs on proper two-bone inverse kinematics. We wired the loader so that real
animation clips take over wherever one exists. That was v0.12. It was still nowhere near 80.

The walk that reset

ProblemCauseSolution
Agent E took a few natural
steps, then the animation
reset.
Our first guess was wrong. Measuring found it. Meshy's clips start their
keys two frames late and pad the end. Each cycle froze for about five
frames and popped.
Fixed once the clip
was measured.
Every few steps her leg
twitched for one frame.
Different bones' tracks ended on different frames.Fixed on the second
attempt.

5. The hair, first time round

Problem. In Mixamo's preview, Agent E's long hair was stuck to her hands.

Cause. The automatic rig had bound the hair to whatever bone was nearest. We measured the weights.

Hair weighted toShare
Head41%
Arms and shoulders30%
Hips and spine21%
BUILDING A 3D WORLDPG 12
ENGINEERING JOURNALTPS ARENA
Hair weighted toShare
Legs6%

What we tried.

AttemptResult
1. Find hair by its red pixels, re-bind it to head and spineA brown strand stayed stuck to her. Not fixed.
2. Hue test plus fill, blended seamShards poking into the body at the waist and wrists.
3. Hard cutTwo dots left at the hips.
4. Split mixed triangles, drop scraps, add hair bones with
physics
Dots gone, but the hair hung like a drape.

Stepping back. We stopped iterating and looked at how Fortnite handles hair. We also saw that we had been fixing
something the game did not show. The hair stuck to the arms in Mixamo's preview, and that is not our game
environment.

Solution. Hair is a stiff, bunched mass that barely moves, the way Fortnite does it at gameplay distance. Free-
flowing motion is kept for a cape.

That decision held until Agent C (chapter 12). It is the compromise that chapter 13 takes apart. The hair is stiff because
it is welded to the body, and it cannot move further without tearing.

What this gave us

A real character in the game, a loader that every later character uses, a height rule, and a first understanding of what a
generated model is: one fused shell that has to be repaired before it can move.

CH 03Part I · Engine, gameplay and first characters

Weapon grip in hand and a fake Mixamo rig

September 2026 · v0.15 to v0.16 · Agent S, a grip tuned by numbers, and a rig that was not what its bone names said.

What we builtAgent S, the second character. A physics cloak from its own model. A greatsword that splits into
twin blades. His first combo on real Mixamo clips. DEVELOPER_GUIDE.md. One version scheme
for the patch notes.
The challengePlacing a prop in a hand by typing numbers and reviewing screenshots. And a skeleton from
Meshy that carried Mixamo's bone names but not Mixamo's bone directions.
Key decisionsArt must be authored in Blender, not as numbers in code. Characters are rigged on Mixamo's own
skeleton. Props are generated as their own models.
The resultWaist twist down from 177 degrees to at most 18. A four-hit combo on real clips. A ranked list of
where the visual gap really was.

1. A second character, and what the generator copies

Agent S's specification: humanity's strongest gladiator, 185 cm, an athletic mesomorph build but not a bodybuilder.

BUILDING A 3D WORLDPG 13
ENGINEERING JOURNALTPS ARENA

Problem. Meshy's first results were slim and flat-shaded. We are building characters in the style of Overwatch and
Fortnite, not 2D anime characters.

Cause. Meshy copies what it sees. The lesson was about the input picture, not the text description.

Solution. The concept picture itself has to look like a 3D render.

2. The cape: use the right tool

Problem. Meshy kept modelling his cloak folded open. We built the cape in code. It came out as a flat 2D piece that
looked like a cardboard cutout.

Cause. Code was the wrong tool for a sculpted garment.

Solution. We generated the cloak as its own model. The engine gave it a chain of bones and physics.

It still took corrections.

  • The clasp side had been read as the front. It is the back.
  • The top flared out like wings.
  • The collar height took three tries. It sat above the collar and must sit below it.

His kit is rage mode, an air dash, a wall run, and a greatsword that splits into twin blades. The mechanics were the
easy part.

3. The grip

Problem. The hard part was his hand. The wrist twisted the wrong way and the elbow distorted.

What we tried. Two rebuilds of the hand logic. Both were reverted. What was left was one number, the roll of the
right hand, tuned by eye.

IterationRight hand roll
130 degrees
260 degrees
3The other way
445 degrees
555 degrees
652 degrees
7Lower the sword 5 cm
8Back to 45 degrees

It took about twelve iterations in all. Our own measurement of "palm toward body" turned out to be inverted. After
that, the direction was set by eye in the game, not by the measurement.

The twin blades

Problem. In dual-sword mode Agent S held two full greatswords. Each hand should hold one half of the greatsword,
cut down the centre.

Our first cut was wrong. It went through the blade the flat way. That halved its thickness and looked identical
from the camera.

BUILDING A 3D WORLDPG 14
ENGINEERING JOURNALTPS ARENA

Solution. The second cut went across the profile (tools/splitsword.mjs).

4. A method that does not scale

We stepped back and assessed where we stood.

  • Against a proper Overwatch-lite goal, we were about 5% there in appearance.
  • It was taking twenty iterations to seat one sword in one character's hand. That does not scale.

The suggested missing step was a bridge between our code tooling and Blender, through Higgsfield.

OptionWhat it givesTrade-off
Higgsfield + Blender bridgeAn official Blender add-on, and a
bridge that lets scripts act in the
open Blender scene.
Half right. Higgsfield's 3D is largely a wrapper over the
tools we already use. It is Meshy underneath.
Generations burn plan credits.
Stay on Meshy + Mixamo
only
Already working.Every prop keeps being fitted by numbers and
screenshots.

Cause. The missing step was not any one product. Art was being authored as numbers in code and reviewed by
screenshot. That is the wrong tool for art.

Solution. Author in Blender, where a sword is placed by dragging it once. We wrote this into DEVELOPER_GUIDE.md.
Blender joined the workflow in chapter 06.

The guide also ranked where the visual gap really was.

#GapOurs thenShare of the gap
1Environment artUntextured boxes, one ramp, flat ground~45%
2LightingOne directional light, flat ambient, plain sky~20%
3VFXColoured geometry and simple sprites~15%
4AnimationMixamo locomotion plus poses written in code~15%
5Materials / postDefault PBR, flat colours, bloom only~5%

5. A "Mixamo rig" that was not Mixamo's

Problem. The first real Mixamo animation clip went onto Agent S. The clip played, but his legs looked broken.

#CauseWhereSolution
1A finished clip was never faded out.EngineFixed in the engine.
2The old code poses were running on top of the clip.EngineFixed in the engine.
3His waist twisted by up to 177 degrees.AssetRe-rig on Mixamo's own skeleton. See below.

What we measured. Agent S's skeleton from Meshy had 28 bones against Mixamo's 65. The bones carried
Mixamo's names, but they pointed in different directions. The arms were 95 and 103 degrees off. A clip recorded on
Mixamo's skeleton cannot play correctly on that.

OptionWhat it givesTrade-off
Retarget in BlenderKeeps the Meshy rig.A tool we did not have yet.
BUILDING A 3D WORLDPG 15
ENGINEERING JOURNALTPS ARENA
OptionWhat it givesTrade-off
Rig him on Mixamo itselfThe native skeleton, so every
Mixamo clip fits.
Mixamo refused the file: "unable to map your skeleton". The
file already had one.

Solution. We wrote a small tool that strips the model down to a bare mesh that Mixamo will accept
(tools/glb2objzip.mjs). We rigged him there.

BeforeAfter
SkeletonMeshy, 28 bonesMixamo, 65 bones
Worst waist twist177 degrees18 degrees

From then on his combo was real clips: three slashes at 42 / 42 / 57 damage and a fourth at 84.

One route we tried and removed. We converted those clips onto Agent E's old Meshy rig. It did not hold up in
game and we removed it. She would have to be re-rigged on Mixamo too (chapter 04).

6. Version numbers

Problem. The patch notes had grown as "v0.1, v0.2, Patch 0.2.1". We wanted one uniform scheme with no confusion.

What we tried. Splitting the notes into several files. We reverted it, because the point was to reduce clutter.

Solution. One file, one scheme. We also checked the early history for padding. The prototype had been listed as nine
versions. We collapsed it to six honest ones, v0.01 to v0.06.

What this gave us

A second character on a skeleton that every Mixamo clip fits, and the diagnosis that shaped the rest of the project:
place art by hand in an art tool, and let the game play it back.

CH 04Part I · Engine, gameplay and first characters

Mixamo clips: fingers, feet, sword flips

September 2026 · v0.17 to v0.18 · Agent E, Agent Zero and Agent B move onto real clips, and the asset library gets a contract.

What we builtAgent E on Mixamo's skeleton with her own greatsword clips. Agent Zero imported and corrected.
Agent B with her own move set. A character asset contract (tools/herocheck.mjs, 13 rules)
and one preparation tool (tools/heroprep.mjs).
The challengeFists that would not close, a sword that flipped, a comment that silently disabled a combo, and a
run of import defects on every new model.
Key decisionsUse the original animation and fix the engine, not the fingers. A character's quirk is an asset or
pipeline problem, so fix the file or the tool. Never add a per-character flag in the engine. Organise
for fifty characters, not four.
The result0 sword flips in a test run. Fists sit 0.3 to 4.7 cm from the handle. Every character file passes the
same 13 rules.
BUILDING A 3D WORLDPG 16
ENGINEERING JOURNALTPS ARENA

1. Open fingers and an upside-down sword

Once Agent S's clips worked, we moved Agent E onto Mixamo's skeleton too, with her own greatsword clips. Her hair
had to be rebuilt again. It took two tries. The first mask swept in tanned skin, which made the top read as short hair
and split the lower half of the long hair away from it.

Problem. Her fingers stayed open around the hilt. The sword sat off her back hand. Sometimes it flipped upside
down.

First approach, wrong. We tried to curl the fingers in code. It took about six rounds and never looked like a
closed hand. The better instinct was to use the original animation as recorded, and ask why it was not arriving.

Cause. The fault was in our own engine code, not the animation. Three.js only writes a bone when its value has
changed. Our rig reset the fingers to their rest pose every frame. The idle clip holds the fists perfectly still. So from the
library's point of view nothing had changed, and the fists were never written.

Solution. We invalidate that cache each frame.

ProblemCauseSolution
Fingers stay open
on the hilt
Rig resets fingers to rest each frame. The clip's still fists look
unchanged to Three.js, so they are never written.
Invalidate the cache each frame.
Sword flips upside
down
The blade's direction was decided once and carried between clips.Vote on the direction every
frame, from both fists.

A test run showed 0 flips. The fists sit 0.3 to 4.7 cm from the handle.

2. One comment, two broken characters

Problem. Agent E's primary fire dropped back to its fallback mode.

Cause. A // comment had been added in the middle of her settings line. Everything after it on that line was silently
switched off: her clip combo, its speed-up, its hit timings. The same mistake later broke Agent Zero.

Solution. Comments go at the end of a line and never in the middle. We wrote it down as a rule because it happened
twice.

3. Twelve rounds on one ability

Critical X is a leap, a spin and a beam. Agent E and Agent Zero share the same clip. His is mirrored. We tuned its feel by
play. Jump height went 1.5 m, then 1.0, 0.5, 0.8 and finally 1.2. Hang time and fall speed changed six times or more.

OptionWhat it givesVerdict
Fall at 8 m/s²A gentle landing at 4.0 m/sToo floaty once the hang was extended
Fall at 18 m/s²Landing at 5.7 m/sRejected. The final fall should use the
game's own gravity.
Normal gravity (24) plus a weightless
hover up to the apex
The beam leaves at 1.03 s at the top
of a 1.20 m jump
Chosen

Two measurement errors we corrected. We first measured the blade's sweep through our own grip code and
not the skeleton. That led to a wrong 9% speed-up, which we removed. We also recorded the slash as running to 1.65
s. Watching it showed the slash ends in the air, almost at the moment the beam leaves. 1.65 s was the time slot, not
the motion.

BUILDING A 3D WORLDPG 17
ENGINEERING JOURNALTPS ARENA

An accident that became a feature. Pressing Space and then E sent the character to 1.88 m, against 1.20 m for
the cast alone. It happens because the cast raises upward speed to a minimum and does not replace it. It was already
useful in play, so we kept it and logged it as an official mechanic. It is in the v0.17 notes as leap stacking.

4. Agent Zero: twelve generations, then the feet

Agent Zero's look took twelve generations on Meshy. The versions with the correct look came out with short legs. We
tried stretching his legs in the file. At 48% the legs were too long in preview, and we reverted it. Then the import
problems came one after another.

ProblemCauseSolution
He hovered 0.9 m above
the floor on the
character select screen
Meshy exports have their origin at the
centre of the body. Agent Zero had
skipped the step that moves it to the
feet.
Our first fix was a per-character flag. It was wrong twice.
We replaced it with a tool that fixes the file.
His hands had no pinky
fingers (24 finger bones,
not 30)
Our first two diagnoses were wrong.
The real cause: the glove was sculpted
as fused fingers.
Diagnosed as a property of the source model, not the rig.
His heels were upA clip carries the foot angle of the
skeleton it was recorded on. His was
12.9 degrees off.
Our first fix only dropped his feet lower. It did not flatten
the toes. The second corrects the ankle angle, measured
per character and applied by the engine with no per-
character data.

5. A clean library, built for fifty characters

After Agent Zero we reorganised the project for fifty characters and not four. Our bar was to patch in the cleanest way
possible. We planned it in phases and approved them one at a time.

PhaseWhat it doesCost / risk
1. Clip names. 2. An asset contract and toolsOnly adds thingsLow
3. Per-character foldersMoves 46 files into assets/heroes/<id>/Touches working paths
4. Trim the docsThe handoff from 336 to 203 linesNeeds review
5. Orphan files14 files, 299 MBParked, not deleted

The result is a contract a character file must pass (tools/herocheck.mjs, 13 rules) and one tool that prepares a
model for Mixamo and brings it back (tools/heroprep.mjs).

The standing rule. A character's quirk is an asset or pipeline problem. Fix the file or the tool. Never add a per-
character flag in the engine plus a paragraph explaining it.

6. Agent B is not Agent E

Problem. Agent B was a placeholder on Agent E's body. We copied too much and gave her Agent E's whole move
set, where only the body was meant to be shared.

Solution. She is a female swordsman, but she is her own character. We restored her own walk, jump and abilities.
Only the three-hit combo is shared.

Rule. Character changes stay on the character they were made for.

BUILDING A 3D WORLDPG 18
ENGINEERING JOURNALTPS ARENA

What this gave us

Three characters running on real clips with closed fists and a stable sword. A measured, engine-level foot correction
that needs no per-character data. And a library where a new character is checked by a tool against 13 rules, not
explained by a paragraph.

CH 05Part I · Engine, gameplay and first characters

Lag fixes, and NPCs built on player code

September 2026 · v0.19 to v0.30 · Lag, invisible walls, knockback, and the decision that an NPC is the character code.

What we builtVersus mode with up to five NPCs a side. A pooled light system. Capsule hitboxes. Bot extends
Player
. A post-match scoreboard with assists. One knockback path, Player.shove.
The challengeMulti-second freezes, 48 colliders with nothing drawn, NPCs that played by their own rules, and a
shove that behaved differently on the ground and in the air.
Key decisionsMeasure slow frames before guessing. Bullets test capsules, not rigged bodies. If a player cannot
do it, an NPC cannot either. Difficulty changes reaction, never damage.
The resultA health-pack pickup went from up to 4,689 ms to 17 to 25 ms. NPCs read the same tables as the
player. Ground and air knockback match 1:1.

1. One default speed for everyone

We wanted melee characters to outpace gunners. The default walk dropped from 6.2 to 4.0 m/s, and melee characters
kept 6.2 by name. Bots had their own speeds per difficulty. We removed them. Bots scale down to the defaults and
have no special speeds. That change is the seed of the biggest architectural decision in the project.

2. Lag, cause one: lights

Problem. Versus mode arrived in v0.19, with up to five NPCs a side. With it came freezes, worst when shooters were
in the match.

What we measured. We added a slow-frame logger to find where the time went. It found two separate causes.

Cause. Every muzzle flash, health-pack glow and projectile added its own light to the scene. In Three.js the number
of lights is part of every material's shader. Each time the count changed, every material was rebuilt.

Solution. A fixed pool of lights that effects borrow from. The light count never changes.

Picking up a health packFrame time
Before, three measurements2,637 ms, 2,154 ms, 4,689 ms
After, with pooled lights17 to 25 ms

3. Lag, cause two: bullets against bodies

What we measured. Of 260 slow frames, 256 were game logic, not drawing. The cost was in the hit test.

One bullet ray tested againstCost
The map0.02 ms
BUILDING A 3D WORLDPG 19
ENGINEERING JOURNALTPS ARENA
One bullet ray tested againstCost
A capsule0.09 ms
Four rigged character bodies3.6 ms

Cause. Bullets were raycast against each character's rigged body. No shipping game does that.

Solution. Every character now carries an invisible capsule and a head sphere. Bullets test those.

4. Forty-eight walls made of air

Problem. Characters ran into obstacles that were not there. The map had invisible walls.

Cause. It had been in the game since the first build. The decoration groups were being added to themselves instead
of to the world. Every pillar, barrel, lamp, tree and sign was built, given a collider, and never drawn. Forty-eight colliders
with nothing to see.

Solution. We attached the groups to the world. That also let us merge 650 small meshes into a handful (v0.20).

With the props finally visible, we judged the map honestly. The target was a map like Overwatch. What we had looked
like Minecraft. The Practice map went bare again, and Central Park was rebuilt as a plaza.

5. The decision: an NPC is the character code

Problem. The NPCs kept cheating in small ways. They moved at their own speeds and swung at their own rate. NPC
Agent E had grown a leap ability no player had. And Agent Zero, with 500 HP including armour, died in a few bullets.

Cause. An audit confirmed it. NPC bullets left the eye horizontally at about 1.6 m. Against a 1.80 m character, every
shot landed in the head zone. Four bullets at 39 each was 156 damage.

Solution. We went further than a patch and made the rule structural.

Before (v0.22)After (v0.23)
A bot is its own class with its own mover,
swing timer and damage table
Bot extends Player: the NPC is the character's body. Its brain only fills in a
fake keyboard and mouse.
Difficulty changes damage, speed and hit
points
Difficulty is only reaction time, burst length and pause. Damage, attack rate,
cooldowns and speed come from the same tables the player reads.

The rule in our engineering rules reads: if a player cannot do it, an NPC cannot either. We made it for fairness, since
this is meant to be a PvP game one day. It also turned out to matter for networking (chapter 13). A remote player is the
same body driven by a third kind of keyboard.

The same work gave the project its vocabulary rule. A "jump attack" is Space plus left click, whatever animation plays.
We had been naming moves after their clips. Moves are now named by their input.

6. Scoreboard and assists

v0.24 added the post-match table: kills, deaths, assists, damage, healing, mitigation. We wrote our own assist rules.

An assist is credited forWindow
50 damageWithin 8 seconds
Crowd control that endedWithin 3 seconds
A shield or healWithin 5 seconds
BUILDING A 3D WORLDPG 20
ENGINEERING JOURNALTPS ARENA

We also took a cost lesson here. A simple question, how other games count assists, deserves a simple search and not
a deep research pass.

7. Immortal Fury: sixteen passes on one ability

Problem. Agent S's spin was supposed to drag enemies in and finish with a cut. Between v0.26.2 and v0.28.4 it was
a shove, then a vacuum, then a vortex, then a radial pull with a sideways swirl. The finisher's timing alone took eight
releases (v0.27.7 to v0.27.14). It played choppy, and it felt like two abilities and not one connected move set.

Solution. Two playback rates. Slow into the hit and fast out of it.

In the middle of the tuning, a change to his dash and pull strength went in without sign-off. We laid out the options
afterwards.

OptionWhat it givesTrade-off / verdict
Revert to the approved state (dash 9 m/s through the
spin, pull 5)
The signed-off behaviourThe pull stays weak
Keep the unapproved change (dash stops early, pull 7)A stronger pullTakes away his movement
Dash untouched, pull 8, hits limited to a 2.2 m height bandBoth the pull and the
movement
Chosen. Shipped as v0.27.4 and
frozen.

The same failure later, with a clip instead of a number, produced Rule Zero (chapter 06).

8. Knockback on the ground and in the air

Problem. A shove that sent an airborne enemy 2 to 3 m moved a grounded one about 0.35 m.

Cause. Ground friction (16 per second) ate the push in roughly 0.05 s.

Requirement. Ground and air should match.

OptionWhat it givesTrade-off / verdict
A knockback state: for 0.15 s a shoved character
ignores friction and their own keys
A grounded shove travelsShipped as v0.28. The air still carried
further.
One global juggle numberOne line of code, ground
untouched
Our first recommendation. Not chosen.
Air drag that only applies while knocked, and
every push re-tuned
Ground and air match 1:1,
dashes untouched
Chosen. Many numbers to retune (the basic
push went from 6 to 13).

There is one door for all knockback now, Player.shove. Immortal Fury was deliberately left out of the drag. A more
rugged feel suits it better.

What this gave us

Freezes traced to two measured causes and removed, a map where every collider can be seen, and NPCs that are the
character code with a different input source. That last decision later carried straight into networking.

BUILDING A 3D WORLDPG 21
ENGINEERING JOURNALTPS ARENA
PART II5 chapters
II

Art tools in the pipeline

  1. 06Blender hand sockets, Git LFS, review rules
  2. 07Clone abilities, aim offset, hand-keyed draw
  3. 08Reaching 120 fps and measuring frames right
  4. 09Map pipeline: greybox, then a modelled kit
  5. 10High-to-low baking and local AI on 6 GB
BUILDING A 3D WORLDPG 22
ENGINEERING JOURNALTPS ARENA
CH 06Part II · Art tools in the pipeline

Blender hand sockets, Git LFS, review rules

September 2026 · v0.32 to v0.32.26 · A grip placed by hand, Blender in the pipeline, asset storage, a new character, and our
first rule.

What we builtA Blender socket workflow for weapon grips. Blender, scripted, plus Higgsfield for generation. Git
LFS for game assets. tools/kitcheck.mjs. Agent L, a striker with a sheathable nodachi, and
his full kit.
The challengeA grip that numbers could not solve. Features that grew past their scope. A working Mixamo clip
that was replaced when it only needed slowing down.
Key decisionsArt is decided in a viewport, not in code. Keep the repo and turn LFS on from here. Descriptions
are checked against ability data by a tool. Rule Zero: diagnose, stop and report before any fix.
The resultThe game matches the Blender socket to three decimals in idle, walk and run. Agent L shipped at
41,851 triangles with a complete kit and a timing reference in docs/ABILITY_TIMING.md.

1. The grip, solved by hand

Problem. After twenty iterations to seat one sword in a hand, Agent S's grip was still not right. The fix our developer
guide had named, a socket placed in Blender, had never been built.

Solution. We built it, starting with Agent S. A script sets up the scene: Agent S at his real height, the sword attached
to the hand bone, and a panel with a Save button. The sword is then dragged into the hand directly.

The first attempts were rough. An undo removed the whole scene and left a box in its place. One placement was lost
when the window was reopened before saving. Then the sword was placed by hand and saved.

Result. The game read the saved socket and matched Blender to three decimals in idle, walk and run. That was
v0.32. Direct manipulation did what placing a prop by numbers had not. It is the second big jump in speed for the
project: art decided in a viewport, by eye.

2. Two bridges into Blender

Next we wanted Blender itself scripted live, and generation available inside it.

OptionWhat it givesCost / risk
BlenderMCP (free)Scripted work, live, in the open
Blender scene
A server and an add-on, both needed
Higgsfield MCP + its Blender
add-on
Generation of pictures and 3D inside
Blender
Needs credits. The trial charges automatically after
three days.

We installed both. They wanted the same network port, so we moved one. Higgsfield's sign-in took three attempts.
With no credits yet it could do nothing, so it waited until chapter 09.

3. Where assets live

Problem. Models and textures were in plain git. Standard practice is to keep game assets, textures, 3D models,
audio, video and datasets in Git LFS. We had not set it up from the start.

BUILDING A 3D WORLDPG 23
ENGINEERING JOURNALTPS ARENA
OptionWhat it givesTrade-off / verdict
A fresh repo nowA clean start and a smaller clone (about 285
MB against 545 MB)
Loses every commit and the
work log
Rewrite history to move old files into LFSA smaller repo with history keptCostly, and could break
things
Keep the repo, turn LFS on from here, start a
fresh repo at v1.0.0
Nothing lost. The old repo becomes the archive.Chosen

The rule since. Code and docs in git. Game-ready models and textures through LFS. Raw downloads and Blender
project files never in git. A work log tool written alongside counted 248 commits.

4. Things we built and then removed

Not everything stayed. Three things were undone. Each is a lesson about scope.

WhatWhat went wrongWhat we did
Agent S's free
arm
His left arm hung like a lazy arm. About six
rounds of tuning in code produced a stiff
swing and a bug that made his sword flail at
a sprint.
A line-for-line revert, done twice. The files were verified
identical to the old commit. Motion like that belongs in an
animation tool, not in code.
A decimation
tool for Agent
L
We needed a lighter model and started
building a head-aware reducer. It was too
much time on a small feature.
Cleaned up and undone. A remesh on Meshy did the job in
two minutes.
Leftover
lifesteal
Agent S was healing about 2,000 a fight
from a line written in v0.03, when Agent E
was the only melee character. Lifesteal is a
rare ability and not meant to be on many
characters.
Removed. An audit found nine character-select
descriptions that did not match the code. We made it a hard
rule: tools/kitcheck.mjs now compares every
description with the ability's data.

5. Agent L

Agent L is a striker with a long nodachi. Our requirement from the start: the blade must be sheathable, and his default
position is the blade sheathed.

OptionWhat it givesTrade-off / verdict
Three models: sword, empty sheath, sheathed swordA simple visual swapThree generations that may not
match each other
Two models: the nodachi and an empty scabbard. "Sheathed" is
the sword placed in the scabbard in Blender.
One sword reused,
fittings always match
Needs Blender placement.
Chosen.

Generating his body taught us to change only the named part of a generation description. One reworded version made
him look like a sailor. Another rewrite dropped the blue coat, which was part of the design. We now edit only the part
that needs to change and leave the rest of the wording alone.

6. The incident behind Rule Zero

Agent L came back from Mixamo rigged, at 41,851 triangles, and went into the game on a temporary kit. His sword arm
looked locked and his jump looked wrong.

Problem one, the arm. He was a placeholder on Agent S's settings, which lock the arm. That was the cause, and it
was the one we suspected.

BUILDING A 3D WORLDPG 24
ENGINEERING JOURNALTPS ARENA

Problem two, the jump. Our first diagnosis was wrong. We judged the Jumping Up clip from Mixamo unusable,
replaced it with a jump made in code, and committed both changes together. Measured again, the clip was complete.
The feet tuck through its whole length. It only needed to play slower.

Solution. We restored the clip and slowed it down. Then we stopped and wrote the rule that now sits at the top of
our engineering rules.

Rule Zero. Do not fix anything until the fix has been decided. Diagnose, then stop and report. The decision on what
happens next comes after the report.

A question is a question, not a work order. The cause of a bug is reported with options, and the fix waits for a yes.

7. His kit

With that settled the kit went quickly.

AbilityWhat it does
Air LeapTwice the force of a jump (17.6 m/s)
DashDouble-tap
Lightning PierceA travelling strike, capped by distance
Giga SlashThree cuts that send blue waves

The tuning was the slow part. Giga Slash's hang time took eight rounds. A version that halved it was too exact. He
dropped too fast and the animation looked rushed, so we reverted it. We kept the timing tables, which is why
docs/ABILITY_TIMING.md
exists.

Problem. From a high jump, Pierce carried Agent L across a small map.

Solution. The fix caps the travel by distance and brakes on exit. It behaves the same on the ground and in the air.
We did it properly because the design has to survive an eventual migration to Unity or Unreal.

What this gave us

A grip workflow that takes minutes in a viewport. A storage rule that keeps the repository lean. A tool that stops
descriptions drifting from data. A fourth character. And Rule Zero, which has shaped every fix since.

CH 07Part II · Art tools in the pipeline

Clone abilities, aim offset, hand-keyed draw

September 2026 · v0.32.45 to v0.33.12 · Agent L's clones, the third-person camera, and a sword draw authored frame by
frame.

What we builtTwo clone abilities for Agent L: Shadow Mirage on Q and Phantom Mirror on F. A camera turned
toward the character so strikes meet the crosshair. A scabbard and nodachi made on Higgsfield.
The project's first hand-authored animation, the sword draw.
The challengeA clone that dealt no damage. Swings that landed beside the crosshair. Clips recorded in a T-pose
on a body that rests in an A-pose. A sheath that broke abilities on Olympus.
BUILDING A 3D WORLDPG 25
ENGINEERING JOURNALTPS ARENA
Key decisionsA clone is an ability, not a character. One camera angle for all characters, capped at 12 degrees.
Price a generation job before submitting it. Move the prop, not the body.
The resultEvery checked bone agrees to 0.0 degrees after rest-pose matching. The draw cut lands 0.23 s
after the click, and the draw itself is his first hit.

1. A clone that did no damage

Problem. Agent L's Q made a copy of him that repeats his moves. In the Practice Range, Giga Slash dealt 240
damage with the clone and 240 without it, even though six waves flew instead of three. The mirage was there but it
was not dealing the doubled damage.

We followed Rule Zero. No code changed until the design was approved.

OptionWhat it givesCost / risk
A. A clone that stays at Agent L's right and attacks
whatever he aims at
Double damage on everything. Enemies
see two of him.
It must reposition when he
aims across it
B. A clone that behaves like an easy NPCSimpleDoes not hit what he aims at
Mirror clone (opposite left/right, same
forward/back)
Placement becomes a skillObstacles and distance limits
can strand it

Decision. We chose A, and corrected the design underneath it. Our first build made the clone a separate character
with its own ability charges. That was wrong. Clones are not characters. They are an ability, derived from Agent L. A
clone is a mimic with no ID, not a twin.

We could already see where the idea ran out, so we did not argue the mirror version on paper. We built it as a second
ability on F so both could be tested side by side. Q is Shadow Mirage. F is Phantom Mirror, a lighter body that can be
hit but has no brain of its own.

2. Swings that land beside the crosshair

Problem. We put a Marvel Rivals screenshot next to ours. Our character stood far to the left of the crosshair: 351
pixels, 18% of the screen width. Many sword hits looked like they did not land where they should. It felt mildly off.

OptionWhat it givesTrade-off / verdict
Turn the camera a few degrees toward the
character (one angle for all)
The character's strikes meet
the crosshair
Chosen. Past 12 degrees the walk looks
like a crab-walk.
Rotate each character modelPer-character controlCharacters share animations. Advised
against.
Set the camera height from each character's real
head height
Every character framed alikeBuilt, then rejected. It made Agent L look
tall and Agent S tiny.

It took eight versions (v0.32.51 to v0.32.58) to settle.

SettingValues triedFinal
Shoulder offset0.9 m, 0.55 m, 0.7 m0.7 m
Camera turn15.6 degrees, 12 degrees12 degrees

Two rules we kept. The turn never goes past 12 degrees, or the walk reads as a crab-walk. And the lower the head
sits from the crosshair, the smaller the character feels. We treat that as law for framing.

BUILDING A 3D WORLDPG 26
ENGINEERING JOURNALTPS ARENA

A side effect we had not tested for. Both clones aimed from Agent L's eye, which no longer pointed where the
camera did. They stood at the old angle while Agent L stood at the new one. Their aim now starts at the camera.

3. Credits and approvals

We bought 1,000 Higgsfield credits to make Agent L's scabbard and nodachi.

Problem. The scabbard job was priced at 25 credits and submitted in the same step.

Rule. Show the job and the cost, and wait for a yes. Pricing and submitting are separate steps.

The nodachi needed two generations. The first came out with a scimitar's curve. We then narrowed the blade and
guard when the right first move was to scale the whole sword down without touching its proportions. For the second
generation we priced the options first.

OptionWhat it givesCost
1. Meshy 7 image-to-3D, UltraBest geometry44 credits
2. Meshy 7 standardSame model, slightly less detail38 credits. Chosen.
3. Hunyuan 3D v3From the image only, no text description15 credits
4. Tripo H3.1From the image only, no text description9 credits

Fitting the hilt into the sheath took many small corrections in Blender. The pattern of them is the lesson. Unrequested
changes made it worse: too much cut off the collar, a ring flattened until it looked broken and wrinkled, parts scaled
that were not in scope, such as the grip. In art tuning, one person leads by eye. Each step is one named change, then a
check.

4. T-pose clips on an A-pose body

Problem. A clip copied straight across put Agent L's hands inside his body.

First diagnosis, wrong. We first assumed the wrong Blender window was being viewed. It was the right window.
The transfer was what was wrong.

Cause. Mixamo records its clips on a skeleton standing with arms straight out. Agent L rests with his arms down at an
angle. The difference at the arms is 53 degrees.

Solution. Match the rest poses before transferring. Once matched, every checked bone agreed to 0.0 degrees. The
same correction went into the game's loader. The same trap caught Agent J's clips later (chapter 11).

5. Drawing the sword

The draw is the first animation in the project authored by hand, frame by frame. We measured three Mixamo clips for
how close the palm comes to the grip.

Our principles. Move the prop, not the body. The first thing to try in any instance is to reposition the sheath. And
the sheath should swing, because it does in real life: it goes up violently as a result of momentum.

OptionWhat it givesCost / risk
Move the hands to the sheathSimpleThe arm motion was already right for
a nodachi
Move the sheath to the hand: swing it from the hip to behind
the back by frame 11
Physically plausibleMany keyframes to tune
BUILDING A 3D WORLDPG 27
ENGINEERING JOURNALTPS ARENA
OptionWhat it givesCost / risk
Use the second clip, which grabs lowerA closer grab by the
numbers
Needs a longer swing

Decision. Use V1, the first clip. Then came the per-frame work.

  • The wrist bend at frame 11 went from the clip's 22 degrees up to 62.8 and back to 58. A later note took a wrist
    bend from 15 degrees back to 10.
  • The sheath's resting spot was raised twice.
  • Three frames were pushed out by an inch or half an inch.

The intent for each frame is written in our draw notes file, so later work does not have to guess.

Draw playback in gameVerdict
Full length, 1.3 secondsFirst version
Three times speedToo fast
Twice speedChosen. The cut lands 0.23 s after the click.

The draw itself is his first hit.

6. Bugs the sheath caused

On Olympus, Agent L's abilities stopped working after the sheath shipped. We suspected the new code. That was right
for one of two causes.

ProblemCauseSolution
The Q clone could not be
placed while sheathed
While sheathed, the code blocked the whole left-click
path. That threw away the click that places the clone.
Fixed in the sheath code, where the
click was being dropped.
The Q clone could be
placed over empty air
Olympus is a map of floating islands. In the test the
clone sat 5.6 m below his floor.
The cast is refused with a red ring and
no cooldown when there is no floor.
Swapping places with the F
clone killed Agent L
The clone could walk off a cliff. It had no fall-death
check and kept falling until its timer ran out.
One line added the check (v0.33.12).

What this gave us

A clear model for clones as abilities. A camera rule that makes melee read correctly for every character. A rest-pose
correction built into the loader. And a working method for hand-authored animation, with the intent of each frame
written down.

BUILDING A 3D WORLDPG 28
ENGINEERING JOURNALTPS ARENA
CH 08Part II · Art tools in the pipeline

Reaching 120 fps and measuring frames right

September 2026 · v0.33.4 to v0.35.2 · A frame-rate target, a frame counter that over-reported, and one repeatable benchmark.

What we builtPooled clones, bounding-volume trees on every world mesh, baked shadows on Medium and Low,
an FPS overlay that measures finished GPU frames, a warm-up loading screen, and npm run
bench
.
The challengeFive-a-side matches lagged on a greybox map with no art in it. Real 3D art was still to come.
Key decisionsMeasure before changing anything. Build nothing mid-match. Count finished frames, not loop
ticks. Compare every art step against the last release with one benchmark.
The resultLow went from 29 fps to 116–137 fps. High went from 14 fps to about 55–60 fps. The worst frame
during a Agent L cast went from up to 10 s to 16–74 ms.

1. The requirement

Problem. Five-a-side matches lagged, even on a greybox map. If an empty map could not hold a frame rate, a fully
modelled and textured one had no chance.

Our bar. 120 fps as the target and 80 to 100 fps as the floor, on a mid-range PC (Ryzen 7 3700X, RTX 2060), at Low
and Medium. Below that, a shooter stops feeling like a shooter.

2. Measure first

We changed nothing until it was measured. The game had no frame counter, so we added one before anything else.

What we measuredFinding
MemoryNot the problem.
CPUMost of the cost sat on one CPU core.
GPUAlso over budget.
Agent LA real outlier. His clone was rebuilt from scratch on every cast, with single frames
as long as 10 seconds.

Solution. v0.33.4 shipped four changes.

  • Clones are built once during loading and pooled.
  • Every world mesh got a bounding-volume tree, so ray tests never walk triangles.
  • On Medium and Low the map's shadows are baked once at load and characters get blob shadows. Only High
    redraws shadows live.
  • An FPS overlay and a frame limit setting.
5 v 5 on Olympus V1, 1920×1080, a Agent L on each sideBeforeAfter v0.33.4
Low29 fps116–137 fps
Mediumn/aabout 100 fps
High14 fpsabout 55–60 fps
BUILDING A 3D WORLDPG 29
ENGINEERING JOURNALTPS ARENA
5 v 5 on Olympus V1, 1920×1080, a Agent L on each sideBeforeAfter v0.33.4
Worst frame while Agent L castsup to 10 s16–74 ms

That is roughly a 5x improvement in play.

3. Shader stutter at match start

Problem. The first seconds of a match still stuttered.

Cause. Shaders were compiling the first time each effect appeared on screen.

Solution. The loading screen now stays up for at least four seconds and runs a short hidden fight to warm
everything. Our rule: up to ten seconds of loading is acceptable if nothing is built mid-match.

4. The meter that lied

Problem. On High, the in-game meter showed 80 to 100+ fps. The experience on screen was closer to 10 fps.

Cause. The meter was counting how often the game loop asked for a frame. With vsync and the frame cap removed
in v0.33.4, the loop kept ticking while the graphics card fell behind. Timed directly, the meter read 60 while the real
rate was 42. High had also been rendering at up to twice the screen's pixels on high-DPI displays.

Solution. The meter now measures finished frames on the GPU. We then listed the costs on High as numbered items
and re-cut the presets from that list. Items 1, 2 and 4 became a new Ultra preset. Items 1 to 5 became High. Medium
and Low were left alone, since they are the presets our target is set on.

5. One honest benchmark

Problem. The first set of real surface textures on Olympus cost about 20% of the frame rate at once. Nothing had
compared that step against the previous build.

Solution. One command, npm run bench (tools/bench.cjs). It runs the same scene from the same camera at
2560×1440, times the GPU, then runs a ten-second bot fight. Its first numbers were High 72 fps (limited by the GPU)
and Medium 137 fps. Every art step is now compared against the last release with it.

The texture cost was brought back from about 20% to about 6% by merging pieces and dropping normal maps where
nobody would see them.

6. The performance budget

What costs frames hereThe rule we follow
Draw calls: every visible object is drawn once per passStatic map art is merged per material at load. Repeated
props are instanced.
Ray tests against detailed meshesBullets test capsules. The world uses bounding-volume
trees. NPC sight uses boxes.
Live shadowsBaked once on Medium and Low.
Lights: every point light is paid for by every lit pixel, and changing the
count recompiles every material
Effects borrow from a fixed pool. Nothing owns a light.
Anything built or compiled during a matchThe loading screen builds, compiles and draws everything
once.
BUILDING A 3D WORLDPG 30
ENGINEERING JOURNALTPS ARENA

What this gave us

The game's own code runs on one CPU core. So the budget is draw calls and script time per frame, not triangle counts
or memory. That is the opposite of what one would guess, and it is why a dense map is acceptable as long as it is
merged. We also have a meter we can trust and a benchmark that catches a regression at the step that caused it.

CH 09Part II · Art tools in the pipeline

Map pipeline: greybox, then a modelled kit

September 2026 · v0.33 to v0.35.3 · The first map built to look like a place, and what its art pipeline taught us.

What we builtOlympus V1 and V2 as playable maps, an art strategy in docs/MAPS.md, a seamless painted sky,
and docs/LESSONS.md.
The challengeGeneration credits are money. We spent about 250 of them on pieces that Blender could have
modelled for 0.
Key decisionsLayout before art. Lock the style before any 3D. Build piece by piece, not one arena model. Meshy
7 for anything seen close. Model architecture in Blender and generate only organic and showcase
pieces.
The resultA corrected pipeline, a 0-credit plinth as proof, and a written list of mistakes so each one is made
once.

1. Layout first, because credits are money

Problem. Olympus was the first map meant to look like a place and not a test room. The first estimate for its art was
about 390 credits for a 22-piece kit. An art budget of 390 to 450 credits is roughly half of a $50 plan.

Solution. We built the layout in code first, the same way as the earlier maps, and generated nothing. Olympus
shipped as a playable greybox in v0.33. The layout could be judged before any art was paid for.

Problem. On the first try, NPCs walked off the edges and froze at the jumps.

Solution. We fixed navigation until none fell in a 60-second run.

A 1.5x version followed, with a Stairway to Heaven and a Garden of Eden. We kept both. The original is Olympus V1 and
the larger one is Olympus V2. Both are playable, for A/B testing.

2. What must come before any 3D asset

Before any art, we settled the non-negotiables. They became the art strategy in docs/MAPS.md, written down as part
of the master plan and the map plan.

RuleWhat it means
Lock the style firstNo 3D work until the look is fixed.
Kit piecesStatues, plinths, columns, stair modules. They do not depend on the layout, so they can be
made early.
Set piecesThey must wait until their area stops changing.

For the style lock we made two concept pictures, one per image model, for 8.5 credits together. Concept A won
clearly. Its sky was the deciding element.

BUILDING A 3D WORLDPG 31
ENGINEERING JOURNALTPS ARENA

3. Why not the whole arena as one 3D model?

OptionWhat it givesTrade-offVerdict
One image-to-3D of the whole arenaOne object, about 30k
triangles, one texture
Mush up close, wrong scale. It
would judge concept A unfairly.
Rejected
Arena kit piece by piece (floor, dais, balustrade,
plinths in Blender; islands generated)
An arena with characters
visible in it
About 30 credits for step 1, without
statues or throne
Chosen

Which generator to use was settled by looking at the result. The first temple island from Hunyuan3D (15 credits) came
out waxy and rounded. So Meshy 7 (38 credits) became the rule for anything seen close, and Hunyuan3D is used for
far background only.

4. Every reference picture is reviewed before it goes to 3D

Problem. One island was submitted to 3D on an earlier approval, without a review of the picture itself. A 3D job costs
credits whether or not the picture was right.

Solution. A fixed gate. No 3D job is submitted until its reference picture has been looked at and approved for that
job. The gate works: one distant island was rejected four times in a row (white, white, too brown, too grey) and never
went to 3D.

To make review easy, we reorganised the Blender scene into four tabs: the playground, pending references, other
references, and modelling. Every finished model goes straight into the playground.

5. The sky, piece by piece

Problem. The painted sky had a hard bottom edge. The cut-off broke the immersion of the scene.

Fixing it took several passes. Each one exposed the next fault.

FaultCause
Fog read orange, not golden hourThe fog colour was off.
Two sunsHalf a panorama had been repeated round the circle.
Light from the wrong directionThe scene light did not come from the painted sun.
A blue arc overheadThe old sky dome showed through.
Cloud colour wrong three timesIt was matched on the source picture, not on the rendered pixels after tone
mapping.

Solution. Match colours on the rendered output. The cloud colour ended at #f7ca88 against the picture's horizon at
#fbcc8c
.

6. The finding: we had been using generation for too much

Problem. We looked into how the popular "whole scene from one text description" Blender demos are made. The
answer was uncomfortable. No whole-scene generator exists. Those scenes are scripted Blender Python for the
architecture, plus PBR materials, plus an HDRI sky. AI 3D is used only for props. Higgsfield's own free Blender
instructions already said so: blocking is 0 credits, generation is for showcase props only.

Cause. We had not read those instructions before spending.

BUILDING A 3D WORLDPG 32
ENGINEERING JOURNALTPS ARENA
What we didWhat it costWhat we should have done
Every piece: picture →
image-to-3D → placed in
the game
About 250 credits by the later count (Zeus,
Athena, throne and the green island alone
were 152)
Model architecture and simple props in Blender for
0 credits. Generate only organic and showcase
pieces.

Solution. We proved the new route at once with a plinth modelled in Blender: 3.5k triangles, a gold Greek key on a
marble band, 155 KB, 0 credits.

We also decided to document every mistake, so we grow from each one. That file is docs/LESSONS.md.

7. Smaller lessons from the same work

ProblemCauseSolution
The lawnThe generated green islands had gaps in
their leafy tops. We covered them with a
flat lawn. The lawn ruined the look.
A grass texture matched to the model's own leaf colour
(#738e3d against #768f3e). Rule: a surface players stand
on is solid and readable, and a patch uses the model's own
colours.
The frozen
bake
A lighting bake run in the live Blender
window froze it for more than 15 minutes. It
had to be killed, and unsaved work was
lost.
The same bake took 8.5 seconds in a headless Blender.
Heavy jobs now always run headless.
The playdough
shield
Statues were halved from 31k to 15k
triangles with JPEG normal maps. They
looked soft up close.
Showcase pieces are now baked from high to low detail and
keep lossless normal maps (chapter 10).

8. Reviewing the whole pipeline

We then reviewed the whole body of map work end to end, looking for where it went wrong and how to build more
efficiently. The finding was the one above: credits were spent before the free instructions were read.

OptionTrade-offVerdict
Downloadable asset
packs
We reviewed them. They do not match the level we are aiming for, which is Overwatch
level.
Rejected
Build our ownMore work, at the quality bar we set.Chosen

The credit balance tells the story in one line. It went from 912 to 297.5 across this stretch of work.

What this gave us

Two playable versions of Olympus, a style lock, and a pipeline with the costs in the right places. Architecture and
simple props are modelled in Blender for 0 credits. Generation is reserved for organic and showcase pieces, and only
after the reference picture is approved. Every mistake on the way is written in docs/LESSONS.md.

BUILDING A 3D WORLDPG 33
ENGINEERING JOURNALTPS ARENA
CH 10Part II · Art tools in the pipeline

High-to-low baking and local AI on 6 GB

October 2026 · v0.35.5 to v0.36 · Statues that survive a close look, and generation that costs no credits.

What we builtEight god statues on Olympus V2, a high-to-low bake pipeline
(tools/blender/hero_bake.py), and a local ComfyUI setup with SDXL, ControlNet and
Hunyuan3D.
The challengeA 306k-triangle statue has to run in a game at about 20k without losing its detail. Generation
credits were the constraint on everything.
Key decisionsBake, never just decimate. Statues face the altar. Records show the final approved design only.
ComfyUI over Automatic1111, because it can be scripted.
The resultv0.36: all eight gods stand on Olympus V2. Picture edits, texture close-ups and image-to-3D now
run locally for 0 credits, within the limits of a 6 GB card.

1. Decimate is not a pipeline

Problem. Olympus V2 needed its gods on their plinths. Ares came first. Before spending, we weighed two routes:
generate at high resolution and scale down, or generate his sword and shield separately. Ares came back from Meshy
7 (ultra, 44 credits) at 306k triangles. Straight decimation to 15k shattered his blade into shards.

Cause. Decimating the raw generated mesh throws away fine geometry with nothing to carry the detail. It is the same
shortcut that had produced the "playdough" shield on Zeus and Athena.

OptionResult on AresVerdict
Straight decimate, 306k → 15kBlade broke into
shards. Fine detail
gone.
Dropped
Bake: watertight remesh, decimate the clean copy,
new UVs, bake colour / normal / metal from the
high model at 2048, normal map as PNG
About 20k triangles, 7
MB, detail carried by
the maps
The rule for every showcase piece since
(tools/blender/hero_bake.py)

Solution. We re-baked Athena through the new route and left Zeus alone, so the two could be compared side by
side. Then we re-baked Zeus too.

2. Facing the right way

Problem. Ares was not looking at the colosseum. His facing did not match the side his nameplate was on.

Solution. Every statue now faces the plinth side that looks at the altar.

Poseidon, Hades, Artemis, Apollo and Hermes followed. Each reference picture was revised until approved before it
went to 3D. Each model cost 44 credits. v0.36 is the release where all eight gods stand on Olympus V2.

3. Records show the final design only

Getting a design right takes many pictures. That iteration is internal. Once a design is ready, it is the design, and we
stick with it. Documents, patch notes and commits record the final approved design only. A local commit and tag that
said more were removed and redone before the push. This journal follows the same rule.

BUILDING A 3D WORLDPG 34
ENGINEERING JOURNALTPS ARENA

4. A wrong assumption about credits

We had assumed that a job rejected by the generator still costs credits. We checked the balances. A rejected job costs
nothing.

5. The first local generator

Problem. Credits were the constraint on all of the map art. The next tool had to cost none. It also had to be
scriptable end to end, with no manual steps in a tool's interface.

OptionWhat it givesTrade-offVerdict
ComfyUIA local API we can script directlySetup on a 6 GB cardChosen
Automatic1111The older, more familiar interfaceSlower on 6 GB and harder to automateRejected

Solution. ComfyUI went onto the RTX 2060 with an SDXL model and a ControlNet.

Getting it to start was its own small fight.

  • Launching it from the terminal failed.
  • A stuck process ran into a 30-minute limit.
  • The card needed a different CUDA build.

In the end it is started by double-clicking a batch file.

What it bought: picture edits and texture close-ups at no credit cost. An approved face is kept pixel-identical by
repainting only a masked area.

6. Local image-to-3D

Hunyuan3D, running inside ComfyUI, produced a full body from one picture in about five minutes for 0 credits. The
shape was plausible. The face was poor.

We then pushed for maximum quality with no limit on generation time.

Local Hunyuan3D job on the RTX 2060TimeCredits
Full body, first passabout 5 minutes0
Body at the card's maximum10 minutes0
Separate head at the card's maximum18 minutes0

The body and the head are merged in Blender.

The limit is the hardware. One job ran 14 minutes because the game was using the same GPU. Generation and
play-testing cannot share the card. A bigger card is under consideration. The setup is also written down so that a new
machine knows exactly what it needs to download.

What this gave us

A statue pipeline that holds up close: generate high, bake to about 20k triangles, keep the detail in lossless maps. And
a second generation route that costs time, not credits. Paid generation is now reserved for the pieces where its quality
is needed.

BUILDING A 3D WORLDPG 35
ENGINEERING JOURNALTPS ARENA
PART III3 chapters
III

The character quality problem

  1. 11Unarmed character: decimation, pose, drift
  2. 12Fused-shell hair: why it tears when it moves
  3. 13Character quality gap: cause and four routes
BUILDING A 3D WORLDPG 36
ENGINEERING JOURNALTPS ARENA
CH 11Part III · The character quality problem

Unarmed character: decimation, pose, drift

October 2026 · his own branch, merged to main as v0.37 and v0.37.1 · An unarmed fighter in an engine that assumed every
character holds something.

What we builtAgent J, a fast off-tank who fights with fists and kicks. Also a git worktree workflow for parallel
character and map work, and docs/HERO_KITS.md.
The challengeThe engine assumed a weapon in the hand. A Blender preview showed the wrong pose. A 12-
degree camera offset pulled his combo sideways.
Key decisionsReal unarmed clips, not hidden-sword moves. Check proportions before rigging. A strike travels
along the character's body, for every character alike. A change stays on the character it was made
for.
The resultAgent J shipped in two entries, v0.37 and v0.37.1, from 655,622 triangles down to 59,996, with a
128-damage combo cycle.

1. Two workstreams, one folder

Problem. The map work was running on main. We wanted to start a character in parallel.

Cause. A branch alone is not enough. Both workstreams share one folder, so switching branch for one would silently
switch the other.

Solution. Agent J got a git worktree: his own branch in his own folder.

The cost. It showed up later. His copy of the game had no statues on Olympus until main was merged into it.

2. The model

Agent J came back from Meshy at 655,622 triangles. We brought him to 59,996 with the face protected. Facial detail
is the priority when downscaling.

RegionTriangles kept
Head17%
Body7%

Problem. Strands of his fringe were skin-coloured.

Cause. The face picture had been projected onto the fringe.

Solution. A manual step and a scripted step together. We painted over the bad patches by hand in Blender. Then we
compared the saved texture with the original and refilled those areas from neighbouring hair. The first refill colour was
too light and was redone.

Problem. We had already shipped one character with the wrong proportions (Agent L). We did not want a second.

Solution. Before rigging, we compared Agent J's height proportions to the other melee characters. His legs were
41% of his height, against 47 to 50% for the others. We turned down a preset fix and tuned neutral sliders on the bare
mesh. The tuned body went to Mixamo with the proportions already baked in.

BUILDING A 3D WORLDPG 37
ENGINEERING JOURNALTPS ARENA

3. No sword

Problem. To get Agent J moving, we had first lent him Agent Zero's sword and kit. But Agent J has no weapon. He is
a fighter with just his fists.

Cause. Every character before him held something, and the engine assumed it. The sword arm stayed locked in its
carry pose while he ran.

OptionWhat it givesTrade-offVerdict
Hide the sword, keep Agent Zero's movesMinutes of workGreatsword swings with empty fists
look wrong
Rejected
Real fists: punch and kick clips, arms owned
by the animation
The first true unarmed
character
Engine workChosen

Solution. Boxing and kicking clips from Mixamo. His combo became a jab-cross, a knee and four punches (128
damage a cycle), with a Flame Kick and a Flame Sweep.

A check on clip speed. We suspected the speed settings chosen in Mixamo had been lost on his Q and E abilities.
We measured every clip against its file. They matched to the hundredth of a second. We then applied the speed-ups
we wanted by name (Jab Cross 1.5x, Punch Combo 2x), because Agent J is a fast fighter-type off-tank.

4. Tuning the wrong arms

Problem. The jab looked cramped, with the elbows tucked into the body. We built a slider panel in Blender to tune it
by eye, and tuned it.

Cause. The preview was wrong. Mixamo saved the clip on a skeleton with its arms level. Agent J rests with his arms
47 degrees lower. We had loaded the clip straight onto Agent J's rig, and Blender applied it relative to the wrong rest
pose. It was the same trap as Agent L's draw (chapter 07), walked into again.

Elbow angleOld Blender viewGame
Guard, frame 154°73.6°
Jab, frame 10122°142°

Every arm had shown about 20 degrees more bent than the game. The elbows that looked tucked into his body were
the preview's error. The tuning had only extended the elbow back to where the original clip already was. We went full
circle, because the original was already close to perfect.

Solution. A standing rule. Before any Blender scene is used for a decision, one elbow angle is measured there and in
the game, and they must match.

5. The 12-degree drift

Problem. Holding W through his combo carried Agent J 13.4 m. We dampened the forward key. He then drifted
sideways.

Cause. It goes back to chapter 07. The camera looks 12 degrees to the left of where the body faces. The dampening
had been applied along the body, but the keys push along the camera.

OptionWhat it givesTrade-offVerdict
1. Measure the key dampening
along the camera
No sideways pull
from the keys
NoneKept
BUILDING A 3D WORLDPG 38
ENGINEERING JOURNALTPS ARENA
OptionWhat it givesTrade-offVerdict
2. Make his strikes travel
where the camera looks
Moves go straight
up the screen
Agent J becomes the only character whose body
points off his travel, and his effects look angled
Tried, then
undone

Solution. We built both and judged them in play. Option 1 stayed. Option 2 broke consistency across the roster. It is
now a standing rule: a strike travels along the character's body, for every character alike. If that is ever changed, it
changes for all of them together.

6. Changes stay on the character they were made for

Problem. Agent J's guard-plus-click kick did nothing.

Cause. Our first diagnosis was incomplete. It found one cause, and the fix built on it did not work. There were two. The
guard pose pinned his feet. And holding guard cancels any basic attack, which the kick counted as.

A second mistake. The fix was first applied to every character. That was wrong. Agent L's guard must still be able
to cancel.

Solution. The change was scoped back to Agent J through his own data. Rule: a change made for one character
stays on that character.

Problem. In game, the feet in his boxing guard looked wrong next to the same clip on Mixamo's site. On Mixamo the
feet move slightly. In game they looked glued to an anchor point.

Cause. Not the clip. It matched the file to 0.1 degrees. What moved his feet were two engine-wide corrections that
read the idle clip. One keeps the lower foot on the floor. One corrects ankle angles. His new idle started on the ball of
the foot.

Solution. A rearrangement of his stances. The old idle stays. The fighting stance is the guard. A boxer's step-and-
drag is the guard walk.

7. Versions, the merge, and Character kits

Versions. Version numbers were moving too fast. Character changes are now batched together. All of Agent J is two
entries, v0.37 and v0.37.1.

The merge. A worktree cannot touch main's folder, so the merge was run from main. It was a clean fast-forward.

Character kits. We also wrote docs/HERO_KITS.md. It describes every character as he plays today, in plain
language, so the cast could be rebuilt in another engine years from now. It holds the latest version of each kit only,
with no history.

What this gave us

An engine that no longer assumes a weapon, and three rules that apply to every character after Agent J. Verify a
Blender preview against the game before deciding anything from it. Strikes travel along the body for the whole roster.
A fix for one character is scoped to that character's data.

CH 12Part III · The character quality problem

Fused-shell hair: why it tears when it moves

October 2026 · her own branch in its own worktree, v0.37.2, not merged · Our first healer, and a limit of generated character

BUILDING A 3D WORLDPG 39
ENGINEERING JOURNALTPS ARENA

models.

What we builtAgent C, the first healer, as a 0-credit placeholder: Agent E's body with pink hair, a wand made in
code, and a full healing kit.
The challengeShortening her hair exposed a hollow back. Her hair is one fused shell with the body, so it either
stays stiff or opens holes.
Key decisionsRetarget the walk onto her own skeleton. Compare variants in Blender before anything goes into
the game. Judge visual fixes in a live match. Stop at variant 2e and commit.
The resultThe kit checker passed 49 abilities with 0 mismatches. The hollow went from 254 see-through
pixels to 146. The hair is still not right, and we know why.

1. The kit was the quick part

Agent C is our sixth character and the first healer. The requirement was a placeholder at no credit cost: Agent E's body
with pink hair and a wand made in code.

Her kit:

  • Light bolts that heal or hurt for 25.
  • A heavy orb.
  • A cleanse.
  • A launch.
  • A 20 m healing circle.

The kit was built and measured quickly. Every number that the design had not specified was listed out for review. The
kit checker passed 49 abilities with 0 mismatches.

That is the pattern of this whole project in one character. The mechanics were the quick part. Her hair took far longer
and is still not right.

2. A borrowed walk

Constraints. No greatsword walk on Agent C. No new Mixamo downloads onto her skeleton.

Problem. We gave her the shared girl's walk. Her legs were fine below the knee and twisted at the hip.

Cause. That walk had been recorded on Agent E's older rig. Its thigh bones sit about half a turn round their own
length from Agent C's skeleton.

Solution. We retargeted the clip onto her own skeleton, the route Agent B already uses. The motion came through
clean.

3. The hollow back

Problem. Her hair was to be shorter than Agent E's. After the cut the hair looked good, but her upper back was
hollow.

Cause. Something nobody had measured. On the generated model, the long hair was the surface of her upper back.
There was no back underneath.

We ranked the options and tried them in order.

BUILDING A 3D WORLDPG 40
ENGINEERING JOURNALTPS ARENA
AttemptWhat it didResult
PatchGlued the inner layer of hair
onto her back
Fine on the character select screen. Wrong in a match, where the patch and
the swinging hair slid against each other as two pieces.
Bind all hair to
the body
Bound all the hair to the
body, not just the patch
Did not hold up in a live match either.
Lower the cutThe original hair covers the
hole
Kept. The simplest fix was the best one.

We measured the result by counting see-through pixels on her back.

ModelSee-through pixels on the back
Short hair, before the lower cut254
Short hair, lower cut146
Untouched long-hair model8

Two rules came out of this. Measure an asset before editing it. And a visual fix is judged in a live match, never claimed
from a static view.

4. Hair that follows the head, or the body

Problem. On a turn, the lower half of her hair rode her back like a backpack.

Cause. The hair on her skull followed the head and the hair below followed the chest. The chest twists 32 degrees in
her run and the head 5. That is 27 degrees of disagreement in one piece of hair.

VariantHow the hair is boundResult
CurrentHead on the skull, body belowThe steep L shape
1Head onlyStiff
270% head, 30% bodyThe best of the first set. 70/30 accepted.
2bA gradient from 70% to 90%No visible difference
2c, 2d, 2e, 2fFiner gradients from crown to below the neck2e kept for now

How we compared them. The variants were played side by side in Blender, on a recording of a real walk and sprint
captured from the game.

BUILDING A 3D WORLDPG 41
ENGINEERING JOURNALTPS ARENA

Three Agent C hair variants running side by side in Blender
Options 2e, 2d and 2c on the recorded run, from behind: the view that decided the hair, and the one that sent us back to Agent E's
own model first (chapter 14).

A process mistake. Two of the variants (2d and 2e) were put straight into the game. That became a rule. Options
are compared in Blender first. Nothing goes into the game until one has been picked. A test never swaps the live files.

We stopped there on purpose. Variant 2e stays for now, and everything was committed as v0.37.2.

5. Over-scoped

Problem. Finishing Agent C meant one thing: swapping in her character model. We treated the whole open list in our
notes as the job, and started building a test suite, an NPC healer and a benchmark switch.

Solution. We stopped that work. She was committed to her branch as she stood. One real bug did come out of it: a
match crashed when Agent Zero marked an NPC Agent C. But the scope was wrong. The lesson is to confirm the size
of a task before starting it.

6. Why this is a wall and not a bug

Six variants of weights did not fix Agent C's hair, because the weights were never the problem. Her hair is part of one
fused shell with nothing beneath it.

ChoiceTrade-off
Hair bound stifflyThe shell stays intact. The hair does not move.
Hair allowed to moveIt moves. Holes open in the shell.

This is the same compromise we made for Agent E (chapter 02), reached again from the other side.

BUILDING A 3D WORLDPG 42
ENGINEERING JOURNALTPS ARENA

What this gave us

A working healer kit, a clean retargeting route for borrowed clips, and a precise statement of the limit. A generated
character whose hair and body are one shell cannot have hair that both moves and stays closed. Chapter 13 takes on
that question.

CH 13Part III · The character quality problem

Character quality gap: cause and four routes

October 2026 · Why our characters are not production-grade yet, and the routes that could close the gap.

What we builtA root-cause analysis of character quality, a comparison of four routes to fix it, and a five-step
plan.
The challengeThe game had seven characters (Agent C still on her own branch) and 476 commits across its
branches. At that pace, an Overwatch or Fortnite level game was one to two years away. Our
schedule is three months.
Key decisionsThe problem is how the model is constructed, not the engine. We do not build on another game's
extracted assets. We stay on Three.js. Netcode starts in parallel.
The resultA reachable target: a vertical slice of six characters, one finished map, playable on LAN. The plan
is a recommendation. Nothing has been built from it yet and the model route is not yet chosen.

1. The pace problem

Problem. The schedule for a production-grade shooter is three months. At the current pace it would take one to two
years. That is not acceptable.

What we observed. Every jump in speed so far came from adding a tool, not from working harder. Each new tool
made the workflow many times quicker. So the question became which tool, or which method, comes next.

2. What was wrong with the characters

The complaint was specific.

CharacterState
Agent J, Agent ZeroAcceptable
Agent L95% there
Agent E and the othersNot acceptable
Agent C's hair4 out of 10, where 10 is the minimum we accept

Hair was the clearest symptom. Our hair physics are a patch over a deeper fault. They are fine for now. They are not
fine for production-grade material.

BUILDING A 3D WORLDPG 43
ENGINEERING JOURNALTPS ARENA

3. A route we rejected: another game's extracted assets

The modding community makes very good custom Fortnite characters. The method uses FortnitePorting, UModel and
Blender. It pulls the official character models, skeletons and textures out of the game files, loads them into Blender,
and modifies them. We evaluated it as a route to better characters. It fails on three counts.

ReasonDetail
LicenceThose meshes, textures and skeletons are Epic's property. Any character built on them would
have to be thrown away the day the game leaves our own PC. We do not ship work built on
assets we do not own. This alone decides it.
It gives us Fortnite's
characters, not ours
Turning one into Agent E or Agent L means sculpting new accessories on top of the base
models. That is exactly the expert Blender work we do not have.
It is not an engine featureThose models look good because a character artist spent months on each one, not because
of Unreal. A Meshy model dropped into Unreal has the same hair gaps.

A correction to our own guide. DEVELOPER_GUIDE.md section 1 says the Meshy characters are already close to
the right tier. That holds at gameplay distance. It does not hold on the character select screen, or for anything that has
to move, like hair and cloth.

4. The root cause: how the model is constructed

The evaluation did point at the real difference. It is the construction of the model.

Production character (Fortnite,
Overwatch)
Our characters today
ConstructionSeparate parts: body, hair clumps, each
garment
One fused shell from Meshy. Hair is welded to the scalp and
shoulders
Hair motionEach clump has its own bone chain,
hand-painted weights, and collision
against head and shoulders
tools/hairbones.mjs finds hair by colour and binds it to one
Hair*
chain, limited to 4-12° of swing (CHAIN_PRESETS.hair
in src/rigged.js)
Under the
surface
Scalp under hair, body under clothesNothing. Move the hair further and holes open
TopologyEdge loops at joints, so bends are cleanTriangle soup (Agent J: 655k decimated to 60k), so bends pinch
and skin shows

Problem. Hair barely moves, and it cannot be made to move more.

Cause. The 4-12° limit is the patch. We capped the swing because a fused shell tears if it moves more.

Solution. No physics tuning fixes that. The hair has to be a separate object over an intact head.

5. Options we considered

RouteFixes hair and gaps?Our effortRisk
A. Meshy body + separate hair.
Generate the character bald. Generate or
build hair as its own mesh. Split it into
clumps, with a bone chain per clump and
real spring physics with colliders
Hair yes. Body
proportions and skin
gaps only partly
Low. The Blender work is
scripted
A generated hair piece is
still a shell, just a
movable one
BUILDING A 3D WORLDPG 44
ENGINEERING JOURNALTPS ARENA
RouteFixes hair and gaps?Our effortRisk
B. VRoid Studio. Free, slider-based,
anime style. Exports VRM, which is glTF,
and three.js has a spring-bone hair library
for it
Yes. Hair is authored as
strands with physics
built in, and there is a
full body under the
clothes
A few hours per character
in a GUI, no Blender skill
The look shifts to anime
(it does suit our
characters). Armour must
be attached as separate
Meshy pieces
C. Commission or buy a licensed
stylised character from a character artist
Yes. The only route to
no flaws at all
Money and 2-4 weeks per
character
Cost, and the art style
must be locked first
D. Port to UnrealNo. Same assets, same
flaws
The whole 15k-line game
rewritten. The work would
live in binary editor assets,
which are far harder to
script than plain code
Eats most of the
remaining two months

Cascadeur is installed and not yet used. It stays the right tool for motion. It does nothing for model quality.

6. What the remaining time can and cannot buy

Overwatch polish is roughly 3-6 artist-months per character. No tool closes that gap in 70 days. What is reachable is a
vertical slice: six characters whose look and physics read as intentional, one finished map, playable on LAN.

Characters are not even the biggest schedule risk. There is no netcode at all, and LAN play needs roughly 2-3 weeks
of it. The architecture helps here. An NPC is already a Player driven by a fake keyboard (chapter 05). So a remote
player is a Player driven by a network one. After that, map art is still the largest visual gap.

7. The plan

StepWhatWhy
1Bake-off on one character (2-3 days). Take Agent C. Put the current model,
route A and route B side by side in Blender on recorded game motion. Pick the winner by
eye
Agent C has the worst hair, so
she is the hardest test
2Rebuild the hair system (2-3 days). The single chain becomes one chain per
clump, with head and shoulder colliders
Whichever route wins, every
character benefits
3Re-do characters in order of dissatisfaction. Agent E first. Agent J and
Agent Zero only get the hair pass
Effort goes where the gap is
largest
4Start netcode in parallel from week twoIt is the item that cannot be
compressed at the end
5Stay on Three.jsA port fixes none of the asset
flaws

The plan is a recommendation. Nothing has been built from it, and the route is not yet chosen.

What this gave us

A precise diagnosis. Our characters fall short because each one is a single fused shell, not because of the renderer or
the engine. That turns a vague quality complaint into a construction problem with four priced routes, a test to choose
between them, and a scope that fits the schedule.

BUILDING A 3D WORLDPG 45
ENGINEERING JOURNALTPS ARENA
PART IV11 chapters
IV

Making a generated character game-
ready

  1. 14Agent E rebuild: lineup, timeline and cost
  2. 15Faceting: weld before decimating
  3. 16Seam gaps and a capped collar
  4. 17Master first, then reduce
  5. 18Fixing AI paint errors by hand
  6. 19Texture atlas, first rig, source holes
  7. 20Voxel remesh fills: the monster neck
  8. 21Full-res body: hood, shoulders, normals
  9. 22Welding a new head onto a body
  10. 23Game budget, KTX2 and skinning spikes
  11. 24Build cost and the rules it left
BUILDING A 3D WORLDPG 46
ENGINEERING JOURNALTPS ARENA
CH 14Part IV · Making a generated character game-ready

Agent E rebuild: lineup, timeline and cost

5 to 9 October 2026 · Agent E, our first character, rebuilt from her Meshy sources and put back in the game as v0.38 · Four
days and more than a hundred named builds, judged side by side in Blender, then in the game.

What we builtA new Agent E, W7: a full-resolution benchmark model with a new head welded to the body, a
clean hood and shoulders, and one skin tone. From it, a game version at 47,557 triangles, rigged on
her existing Mixamo skeleton, with her textures compressed for the graphics card (213 MB down
to 53 MB). Along the way: three earlier game models (R8fix, R8x, M8x), a painting and sculpting
toolkit inside Blender, and a texture-compression path the whole roster can use.
The challengeChapter 13 asked how far a generated model can be pushed towards a Fortnite-level character.
Agent E had wedges on her face and belly, hair fused to her body, holes behind the shoulders and
paint in the wrong places. A generated model looks finished in the generator's own viewer. Taking
it apart, reducing it and making it move exposes everything it never built.
Key decisionsWeld before reducing, bake after. Keep a full-resolution master and fix it there. Let the eye decide
every visual question, and give the eye tools: a brush, a marker, an eraser, sliders. When a junction
keeps failing, take the piece from a source that has no junction. Judge geometry bald, and judge a
rig in motion.
The resultW7, the new benchmark for Agent E, in the game as her default look: 47,557 triangles, 53 MB on
the GPU. 35 to 48 hours of active work over four days, and a recipe written down for every
character after her.

1. What we were trying to do

Agent C's hair (chapter 12) had shown that a generated model's hair and body are one shell. Before building a new hair
system on top of that, we went back to the first character and looked at her up close. The goal was simple to state:
the same character, at the same game budget, with a smooth body and a sharp face. Chapter 13 had planned a bake-
off of routes. In practice it became a rebuild.

Two constraints held throughout. Nothing is judged from a script's own render: every candidate stands in a row in one
Blender window, and the decision is made by eye. And a candidate only counts once it holds up in the game, moving,
on the character select screen and in a match.

2. The four days at a glance

WhenThe problem we were solvingWhat we builtWhere it ended
5-6 OctWhy does the in-game face look
worse than the sculpt? (chapter 15)
Four shading-only faces, a split-parts
download, twelve Meshy repaints (T1-T12),
three Meshy remeshes, the first rebuilds
R, R8 and R10
The wedges were our
unwelded reduction. Face
from T8, hair paint from T3
6-7 OctSeams that opened and a collar that
moved (chapter 14)
R8fix v1 to v7, the TurtleneckOn par with the old model
(v6, 56,875 triangles)
7 OctThe step we skipped: master first
(chapter 17)
Masters F8 and F10, R8x and R10x, the
two routes
R8x at 39,344 triangles
7 OctColour a script cannot fix (chapter
18)
A paint file with one-click buttons, a
marker-and-diff workflow
R8x hand-painted: the pick
BUILDING A 3D WORLDPG 47
ENGINEERING JOURNALTPS ARENA
WhenThe problem we were solvingWhat we builtWhere it ended
7-8 OctInto the game, and a hole that was
not the rig (chapter 19)
A 63 MB atlas, the first rig, the shoulders
rebuilt from the mould
Streetwear X in the game:
the only game-ready build
that was not broken
8 OctGeometry, not colour (chapter 20)Collar welds v1-v7, Route B (one voxel
shell) and Route C, M8x as the final figure
B7's body kept as the
benchmark body; M8x in
the game; Rule One
9 OctBack to full resolution (chapter 21)The T8 body at 319,000 triangles with
B7's back, a rebuilt hood top, the
shoulders fixed
Option 2, the body every
later build stands on
9 OctJoining the head so she reads as
one person (chapter 22)
Neck welds W1 to W7, a solved neck
width, one skin tone, a choker variant
W7, the new benchmark for
Agent E
9 OctFrom benchmark to game on a 6 GB
card (chapter 23)
Per-part budgets, a hand reduction, the
rig, compressed textures
47,557 triangles, 53 MB, in
the game

3. The lineup

Every build stood in the window next to the ones before it. These are the families, with the verdict each one got.

BuildHow it was madeTrianglesVerdict
A (the old game
model)
The original Streetwear, decimated once, unwelded28,758The face and hair read well, but
the unwelded reduction had
scarred her legs, belly and face
R8 / R10T8 (T10) cut into eleven parts, each reduced unwelded,
re-baked
53,799Right face and colour. Holes at
every seam
R8fix v6Welded parts, voxel-remeshed clothes, hoodie from
Meshy's Ultra remesh, all baked
56,875The first rebuild to match the old
model's look without its wedges
F8 / F10The full model with T3's hair paint through a second UV
map
605,392The masters
R8xF8's parts welded and reduced to budgets, skin baked,
cloth on Meshy's texels, painted by hand
39,344Of the three routes, the one we
judged best to carry on fixing
Streetwear XR8x baked to one atlas, rigged, shoulders rebuilt from
the mould
46,709The only game-ready build with
nothing broken, so it was the one
that went into the game (8 Oct)
B7Every sculpt piece voxel-fused into one closed shellabout
50,000
A perfect body with a monster
neck. We kept the body as the
benchmark and left the neck to
be solved separately
M8xMeshy's Ultra remesh with B7's back grafted on and
the new hair
43,563In the game as a skin (8-9 Oct)
Option 2The T8 body at full resolution, B7's back, a voxel hood
top, hand sculpting
318,734The base for the weld
W7Option 2 with the new head welded at a solved neck
width, one skin tone
475,573Strong enough to replace
every earlier build as the
new benchmark for Agent
E
W7 gameW7 cut part by part, reduced by hand, rigged, textures
compressed
47,557In the game as her default look
(v0.38)
BUILDING A 3D WORLDPG 48
ENGINEERING JOURNALTPS ARENA

4. What it cost

Measured from the timestamps of the working sessions. Active time counts every stretch where work was happening;
a gap of more than 20 minutes (lower figure) or 90 minutes (upper figure) counts as a break. The upper figure is the
closer one: many long gaps were spent painting and sculpting by hand in Blender, which leaves no timestamp.

DayActive timeWhat the day produced
5 Oct (evening)0.6 hTool scan, baseline, four faces
6 Oct2.0 to 4.1 hTwelve repaints ranked; R8 / R10; M8 / M8u / M8x; R8fix v1-v4
7 Oct10.4 to 12.0 hR8fix v6-v8; F8 / F10; R8x; the paint file; the atlas; the first rig
8 Oct9.5 to 15.0 hShoulders; collar welds; Routes B and C; M8x; the head transplant
9 Oct12.9 to 16.6 hOption 2; W1-W7; W7C; W7 game; the rig; KTX2
Total35.4 to 48.3 h, over
97.5 h on the clock
About 2,200 files, 28 GB: 233 Blender files, 179 GLB builds, 294 build scripts,
over 1,000 check renders

At least eleven of those hours were hands-on in Blender: painting the jacket and skin (7 October; hands-on stretches
of 55 minutes, 1 h 36 and 3 h 51), marking the collar (8 October, an hour), erasing, sculpting and flattening the hood
and shoulders (9 October, about three hours), trimming the new head, and the final reduction to game size. By the
night of 9 October the last 7,854 triangles of the game model were removed by hand, part by part.

Money: almost none. Meshy's retextures were free rerolls, an Ultra remesh costs about 44 credits, and the standing
budget set during this work is 500 credits per character without a question, 1,000 at the cap. The cost was time, and it
went where the table says. For scale: refining Agent E took three times longer than any feature built since the start of
the game, which is why we wrote the recipe down, so the characters after her do not pay that cost again.

5. Where it stands

W7 game is Agent E's default look in v0.38, first in her skin picker, with Streetwear, Streetwear X and M8x still
selectable. Its textures load compressed (BC7 on a PC graphics card). The full-resolution W7 is the master: any later
fix is made on it and carried down. Two things are open: the seam at the back of her neck stretches up to 4.6 cm in a
sprint, and her hair still uses the swing settings tuned for the old, longer hair.

The honest summary of four days: a generated model can be taken to a clean, game-size character. The recipe that
works is the one chapter 10 had already found for the statues: reduce the welded mesh, then bake. Everything we
tried around that cost a build and taught us a rule (chapter 24). And the decisions that moved the model furthest were
not code. They were a brush stroke, a marker on a hood, and the switch from hunting for a flaw to working from a
screenshot of it.

The chapters that follow in this part take one problem each: why the game face looked worse than its sculpt; seams
that opened and a collar that moved; the step we skipped; colour that only an eye can fix; into the game, and a hole
that was not the rig; geometry, not colour; back to full resolution; joining the head; from benchmark to game; and
what it cost, with the rules it left.

CH 15Part IV · Making a generated character game-ready

Faceting: weld before decimating

5 and 6 October · The question that started the rebuild, the sources we gathered to answer it, and the three facts that shaped

BUILDING A 3D WORLDPG 49
ENGINEERING JOURNALTPS ARENA

everything after: weld before reducing, bake after reducing, and give the face its own budget.

What we builtA baseline measurement of the in-game face, four shading-only test faces, Meshy's split-parts
model used as a part label, twelve free repaints (T1 to T12) and three Meshy remeshes, ranked by
eye.
The challengeThe game model's face was faceted, with wedges on her cheeks and belly, while the same sculpt
looked smooth in Meshy's own viewer.
Key decisionsWeld before reducing. Stop moving colour between models: build the game model out of the
painted model and bake each part from its own source. Give the face a budget of its own.
The resultThe wedges were our unwelded reduction, not the sculpt. A welded leg at 1,424 triangles came
out flawless where the old one at 2,900 had cracked. Face from T8, hair paint from T3.

1. The face, measured

The game model's face: no detail
The game model as it was: a face with no detail, flat eyes.

Problem. The Streetwear model in the game had a faceted face, painted-on eyes, fringe colour smeared onto the
forehead and wedges on her cheeks and belly. We started with a baseline in headless Blender under neutral light.

Agent E StreetwearAgent C
Triangles, whole character28,75824,898
Triangles above the head joint (face and the hair on the
head together)
4,3544,354
Colour texture2048 × 20482048 × 2048
Share of that texture used by the head and its hair6.3% (about 263,000
pixels)
same
BUILDING A 3D WORLDPG 50
ENGINEERING JOURNALTPS ARENA
Agent E StreetwearAgent C
Texture density on the headabout 11 pixels per cmsame
Hair bonesone chain of 5one chain of 5, unused (hair follows
the body)

The face itself was 1,345 triangles and about 62,000 texels, scattered across the sheet. The dark patch over her nose
and mouth was not in the texture: flat-lit, the skin there is one clean colour. It was geometry. 1,315 corner points of the
face were duplicated, and neighbouring face triangles met at 33 degrees.

A first, cheap pass changed shading only:

VersionWhat changed
ANowThe file as the game loads it
BWelded + smoothThe face's split corner points joined, so light blends across it
CB + soft shading 60%Face shading pulled 60% toward a smooth sphere round the head
DB + soft shading 100%Face shading taken wholly from that sphere

It helped, and it was a stopgap. The real question was why a sculpt that looks smooth in Meshy's viewer comes out
faceted in ours.

2. The cause: the wedges were ours, not Meshy's

Cause. At full resolution (605,392 triangles) the sculpt is smooth everywhere. Meshy exports every vertex split along
its UV seams, and our original reduction had decimated that unwelded mesh by more than 90 percent. Each seam was
an open edge: the decimate protected the edges and chewed the surfaces between them. The wedges, and the holes
the base model had, were the seams. The face had also been given only 3 percent of the triangles, because a single
decimate hands out triangles by surface area.

Solution. Weld first (remove_doubles: 327,905 vertices to 302,642), then reduce. A welded leg at 1,424 triangles
came out flawless; the old one at 2,900 had cracked. That one fact moved everything else in this part.

3. Separable parts: Meshy's split-parts download

The tool scan we ran first found nothing that turns a picture into a rigged, part-separated character. It did find that
Meshy can return a model as separate pieces. Re-rendering the original with "split parts" instead of "texture" gave us
this:

Result
Parts11 separate pieces: hair, head, hoodie, midriff, shorts, two legs, two shoes, two hands
HairIts own closed piece, with properly sculpted locks and fringe
Each partWatertight (no open edges)
Triangles648,585 in total; the hair alone is 249,134
TextureNone, and no UV layout
SkeletonNone

No colour, and nothing under the hair. We measured what is behind it:

BUILDING A 3D WORLDPG 51
ENGINEERING JOURNALTPS ARENA
HeightIs there body under the hair?How far the hair stands off it
1.52 m and up (back of the head)None at alln/a
1.36 to 1.48 m (back of the neck, top of the hood)Mostly none3 to 9 cm where there is any
1.32 m and below (her back)Yes, everywhere5 cm at the hood, 21 cm at the waist

So we used the split model as a label, not a mesh: it says which triangle of the painted model belongs to which part.
Where the hair comes off, a scalp liner in hair colour closes the gap.

4. Colour: three projections that failed

Problem. The first rebuild (R, 53,410 triangles, 6,000 on the face, about 17 texels per cm instead of 11) showed that
the geometry was now right: the abs, legs and lower face came out perfect. The colour was not. Carried over from the
old game model, it made her look alien: smudged eyes, a speckled neck, pale hems.

Cause. The two surfaces were 3.4 mm apart at the legs and 13.5 mm at the arms. Projecting colour across that gap
reads the wrong texel. We added a safety check (no bake if the surfaces are more than 3 mm apart). When it stopped
the next bake at 5.9 mm, we first said Meshy had regenerated the geometry. That was wrong: with a proper fit the gap
was 0.07 mm.

Solution. Stop moving colour between models. Build the game model out of the painted model itself, reduce it part
by part, and bake each part from its own source. The gap is then zero by construction.

5. The sources: twelve paints and three remeshes

A Meshy retexture: better face, odd eyes
A Meshy retexture at full resolution: a better face, the eyes still odd.

BUILDING A 3D WORLDPG 52
ENGINEERING JOURNALTPS ARENA

Meshy can repaint a model without touching its geometry, and the rerolls were free. We had the Streetwear sculpt
repainted twelve times. Every T has the same 605,392 triangles in the same order; only the paint differs. Ranked by
eye, face first, then hair from behind, in a window sorted 2K / 4K / 8K so nobody had to memorise numbers.

Colour
texture
FaceSkin warmth, 1 = palest, 12 =
warmest
Hair from behind
T1 (2k)2KBanned: botched eyes, fringe
painted on skin
3Acceptable
T2 (2k)2KBorderline acceptable7Acceptable
T3 (2k)2KBanned: botched eyes, fringe
painted on skin
8Best detail
T4 (2k)2KBanned: botched eyes, fringe
painted on skin
1Acceptable
T5 (2k)2KBorderline acceptable6Acceptable
T6 (4k)4KPass: sharp eyes, face for a new
character
9Bad: weird left-side
detail only
T7 (4k)4KPass: soft eyes, face for a new
character
10Bad: weird left-side
detail only
T8
(4k)
4KPass: fierce eyes, suits
Agent E
11Bad: weird left-side
detail only
T9 (4k)4KSofter Agent E, runner-up5Acceptable
T10
(4k)
4KTop contender, like T812, the warmestBad: weird left-side
detail only
T11 (8k)8KBanned: botched eyes, fringe
painted on skin
2Bad: weird left-side
detail only
T12 (8k)8KPass: big eyes, mixed opinion, new
character?
4Acceptable

The decision: face and body from T8 (R8) or T10 (R10), hair paint from T3. A bigger texture did not mean a better paint:
the 4K and 8K hair was worse than the 2K, and the game ships 2048 anyway.

BUILDING A 3D WORLDPG 53
ENGINEERING JOURNALTPS ARENA

Close-up of a Meshy-painted face with hair colour in the forehead skin
What a Meshy-painted face is up close, hair hidden: the fringe was painted into the forehead skin, brows and all. No retexture
escapes this; it is how an image-to-3D painter sees a fringe.

Hair painted onto the face
Hair paint smeared onto the face, on Meshy's flagship model.

We also had Meshy remesh T8 three ways. Its own retopology, one fused shell each:

BUILDING A 3D WORLDPG 54
ENGINEERING JOURNALTPS ARENA
ModelTrianglesMapsNormal
map
Verdict
M8 (remesh)29,3684K colour +
metal/rough
noneClothes and hair good. Skin lost her roundness; the face a
blob at game size.
M8u (remesh +
unwrap UV)
40,9694KnoneSame, cleaner islands.
M8x (Ultra)104,8884KnoneIts belly button texture was noticeably better than the
others'. Hair and hoodie geometry later came from it.

Reading the files themselves explained why M8 looked worse in Blender than in Meshy's viewer: the remesh drops the
normal map (two images in the file, not three), and Meshy's viewer lights it more kindly. We concluded that with a
normal map the remeshes would have been the obvious choice. Without one, our own rebuilds still won where it
mattered most for a body: they kept her roundness and had none of the veins and ridges the remeshes left on her
legs. What the remeshes did very well was the clothing and the hair, so that is what we took from them.

Fifteen Agent E dolls in one Blender window
The window at its widest: the retextures, the old game model, the rebuilds and the masters. A clone army, assembled only to pick
one face, one hair and one hoodie.

6. Two ways to put colour on a reduced mesh

This is the fact behind both the fused eyes and the black teeth at the hoodie hem that come up in chapter 17, and it
took a day to see that they were the same fact.

RouteWhat happens to the paintWhere it showed
Keep Meshy's
UVs through the
decimate
Every edge collapse interpolates the UV coordinates.
Exact on a flat surface; on dense paint it slides the
texels. A thin dark band in the texture (the painted
shadow under a hem, a lash line) is stretched into
wedges by the big triangles.
R8x: inner eyelids pulled to the centre; black
wedges running down from the hem onto the
belly. The same two spikes in five builds while the
geometry under them changed every time; the
clay render never had them.
Reduce, unwrap
fresh, bake from
the painted
source
Every texel is read from the painted surface at the
point it lands on. Colour follows the painted hem and
lash line whatever the triangles do. A 4K or 8K source
gives a 2048 bake more than it can use; a 2K source
slightly starves it.
R8, every R8fix, and R8x the moment its head and
skin were baked.

The rule: reduce, then bake. Keep the original texels only at full resolution.

7. Where the triangles should go

A single decimate hands out triangles by surface area, and Meshy painted her whole face into 3 percent of the mesh.
The counts, measured with the same part test on each file:

BUILDING A 3D WORLDPG 55
ENGINEERING JOURNALTPS ARENA
PartFullR8 / R10R8x one shellR8fix v6Budget settled on
Hair243,77914,11020,91917,28912,000 to 16,000
Hoodie106,62010,31610,01214,78310,000 to 13,000
Shorts99,8205,2988,5147,4285,500 to 6,500
Shoes (2)36,514 + 36,5072,030 + 2,0293,282 + 3,2652,019 + 2,0112,000 each
Head and face18,8405,8411,7164,2396,000 to 7,000
Legs (2)13,566 + 13,1722,911 + 2,8421,424 + 1,4351,551 + 1,5681,500 each
Belly12,1692,8851,1311,4531,500
Hands (2)12,223 + 12,1821,470 + 1,4631,056 + 1,0481,007 + 9131,100 each
Scalp liner-2,604- (hair fused)2,6141,100 to 2,600
Total605,39253,79953,80256,875under 60,000

Two things the table says. Legs and belly at 1,100 to 1,500 are flawless once welded; the old 2,900 bought nothing.
And the face needs a budget of its own, around a third of its full detail, or the eyes fuse. We also decided not to aim
for the 60,000 ceiling: most characters sit well below it, and every triangle we do not need is one less that can make
the game lag.

What this gave us

A diagnosis that held for the next four days: the generated sculpt was fine, our reduction was not. A source for each
part (T8's face, T3's hair paint, M8x's clothes and hair shape). And three rules that every later build followed: weld
before reducing, bake after reducing, and decide the face's budget before anything else.

CH 16Part IV · Making a generated character game-ready

Seam gaps and a capped collar

6 and 7 October · From the first part-by-part rebuild (R8) to R8fix v6, on par with the old model · The longest single fight of the
remake was one junction: where the hoodie meets the neck. It took eleven builds, and the answer was not another rule.

What we builtR8 and R10, the first part-by-part rebuilds, then R8fix v1 to v6: welded parts, a few large texture
islands, 2.5 mm overlaps at the seams and voxel-remeshed clothes.
The challengeHoles at every seam, then a collar that either left a gap at the neck or wrapped it. The collar alone
took eleven builds.
Key decisionsWeld each part before reducing it. Colour every face by what a rule would do before writing the
rule. When a junction keeps failing, ask what the original did right: take the hoodie from M8x,
which has no cap.
The resultR8fix v6 at 56,875 triangles, on par with the old model: the first rebuild that matched the old
model's look without its wedges.
BUILDING A 3D WORLDPG 56
ENGINEERING JOURNALTPS ARENA

1. R8 and R10: the right face, and holes at every seam

T1, the game model A, R8 and R10 side by side
The first row: a 2K retexture (T1), the game model (A), and the first two rebuilds (R8, R10). A's shine is the old game material; the
rebuilds already carry the retextures' paint.

R8 and R10 were built straight from the painted model: T8 (or T10) cut into eleven parts by the split-parts test, each
part reduced to its own budget, unwrapped fresh, and T8's paint (T3's for the hair) baked onto it.

R8R10
Face and bodyT8's paintT10's paint
HairT3's paint on both
Triangles53,79953,799
Face6,000 triangles
HairIts own piece, 14,000 triangles, with the hair-coloured scalp under it
MapsColour, normal and roughness, 2K each, baked from Meshy's own

They looked much better than the game model, because the face and colour now came straight from the painted
model instead of being carried across from another mesh. What they still lacked was closure: holes on her inner shin,
at the hip and around the shorts.

First attempts. Pinning the cut edges (R8b) took the model from 53,799 to 76,678 triangles, doubled the hair and
made black needle triangles. Overlapping the parts by 2.5 mm (R8c) still left black slivers.

Cause. Found by looking at the baked textures, not at renders. The unwrap had made thousands of tiny islands (most
of the 2048 sheet was black gaps), and Meshy exports every vertex split at its UV seams, so each part was reduced
as thousands of loose scraps.

Solution. Weld each part before reducing it, and pack a few large islands per part with a 16-pixel margin, every texel
pushed 24 pixels out over the black. That was R8fix.

BUILDING A 3D WORLDPG 57
ENGINEERING JOURNALTPS ARENA

2. R8fix, step by step

StepR8 originalR8fixWhat it fixed
1. WeldingEach part reduced as Meshy exported it:
every vertex split at the texture seams,
so the reducer saw thousands of loose
scraps
Each part welded into one surface
before reduction
Smoother reduction; the
black slivers at island
edges; most of the scarred
look
2. Texture
layout
Thousands of tiny islands; most of the
sheet was black gaps
A few large islands per part, 16-pixel
bake margin, every texel pushed 24
pixels out over the black
Island edges no longer
sample black; more real
resolution on skin
3. Overlap
at seams
Parts butted edge to edge; the reducer
pulled edges apart (shin, waistband)
Legs tucked into the shorts and shoes,
belly under the hem and waistband,
hands into the sleeves, each pushed 2.5
mm inward
The holes
4. Clothes
and shoes
Cut from the painted model, reducedRebuilt from the sculpt's closed pieces
through a voxel remesh, then reduced
Even curves on the hoodie,
shorts and shoes, like
Meshy's remeshes
5. Trim-Remeshed clothing faces whose
nearest painted triangle is skin or hair
are removed
The streaked band at the
collar (the sculpt's torn
hoodie edge)
6. BudgetHoodie 10k, hair 14k, legs 3k each,
hands 1.5k each
Hoodie 12k, hair 16k, legs 2.5k each,
hands 1.2k each
Triangles moved to the
parts that show

With v3 (55,931 triangles) we judged the clothes, skin and smoothness done: all of them looked much better, so every
later build left them alone. What was left was the collar.

A slit on the belly after reduction
A leftover of the hole problem: a slit on the belly after one reduction, a fold in an overlap strip. It went when the strips went.

BUILDING A 3D WORLDPG 58
ENGINEERING JOURNALTPS ARENA

3. The collar, eleven times

The collar gap on an R8fix build
The gap: where the trimmed hoodie no longer met the neck, with the torn cap edge showing black.

Problem. From v3 on, the hoodie either left a gap at the neck or wrapped it.

Cause. R8 never had the problem because its hoodie was cut out of the painted model: one continuous shell, hoodie
rim to inner collar to neck skin. The voxel route made the hoodie a separate closed object, and Meshy closes each split
piece with a cap that follows the body underneath. Every trim rule drew the line between cap and hoodie somewhere
wrong. We wrote four rules before asking what R8 had done right.

The diagnostic that ended it: colour every face of the remeshed hoodie by what a rule would do to it.

ColourWhat it isFaces
greenreal hoodie: within 4 mm of the painted hoodie surface10,714
redMeshy's cap: the split sculpt closes the hoodie over the neck hole and the hood cavity; nowhere near any
painted hoodie
1,023
bluethe rim ring where the hoodie meets the skin (< 2.5 mm from skin)712
yellowfaces whose bake ray would hit skin or hair218

The builds in order, and why each one failed:

BuildRuleResult
v3Trim clothing faces whose nearest
painted triangle is skin or hair
A pale wedge inside the collar (the hoodie's inner shell)
v4 draftv3 + Ultra-remesh hair at 16k with T3
paint, inner shell removed, liner neck
black
The collar gap: the trim took the hoodie faces that meet the
neck
BUILDING A 3D WORLDPG 59
ENGINEERING JOURNALTPS ARENA
BuildRuleResult
Turtleneckv4 with deletion off: the cap keptA black sleeve wrapping her neck. Wrong for this outfit, but it
had a style of its own, so we saved it as a future skin instead of
throwing it away
v4Delete the cap where it is more than 4
mm from faces labelled hoodie; tuck the
rim 3 mm
The collar gap was still there on the right side, and the hoodie
still had a hole in it. The labels lied: the vote had called the inner
collar head and the hood lining hair
v5Cap = more than 4 mm from any painted
surface, plus the tuck
Built, never judged: the options table below replaced it
v6No rule. The hoodie cut from M8x,
Meshy's Ultra remesh, which is one
fused shell and has no cap
On par with the old model at last, without its
wedges.
56,875 triangles

The options we weighed before v6:

WhatCollarCloth smoothnessCost
v5
(running)
draft + tuck rule for the hoodieshould close, but it's still a
stitched edge
voxel smooth8 min
hybridremeshed hoodie everywhere
except a band near the neck
taken from the painted cut
(continuous with the skin)
continuous, but an overlap seam a
few cm down
voxel smooth1 h / 2 h
M8x
clothes
hoodie, shorts, shoes cut from
the Meshy Ultra remesh of T8
by the same part test we used
for its hair; T8 colour and
normal baked on
no cap exists: M8x is one shell,
the collar comes out continuous
like R8, and the 2.5 mm under-
overlap we already use for skin
parts does the rest
Meshy's remesh,
whose clothing was
already judged good
~30 min
optimistic / 1.5 h
conservative

The narrowing that made it work: the voxel-smoothed clothes were already good everywhere except the neckline, so
swapping all of them for M8x's would have thrown away work that was right to fix a problem that lived in one place.
Only the hoodie came from M8x. The neck part extends two rings under it.

4. What a build costs, and why it took eight minutes

A build took eight minutes, and with eleven collar builds in a row we needed to know why it was not instant, and
whether the GPU or the memory was the limit. Where the time went, stage by stage:

StageWhatRough share of
the 8 min
import + fitT8 (605k triangles), the split sculpt, M8x; 10 rounds of nearest-point fitting~1 min
classificationevery one of the 605k painted triangles checks all 11 sculpt parts for the nearest one
(6.6 M lookups), 3 vote rounds, then the same for M8x's 100k faces
~2-3 min, pure
Python, single core
partsvoxel remesh of shorts and shoes, cutting, welding, decimating 11 parts, UV unwrap
and pack
~1 min
bakes6 Cycles bakes at 2048² (normal, colour, metal/rough for body and hair), on the CPU~2-3 min
textures +
export
24-round dilation in numpy, PNG writes, GLB, 7 check renders~1 min

The GPU was off limits: three open Blender windows owned the RTX 2060's 6 GB.

BUILDING A 3D WORLDPG 60
ENGINEERING JOURNALTPS ARENA

5. The process mistakes, and the rules they left

RuleThe incident
Version every build before it runs: own folder, own
number, own file. Review and approve it before
starting the next, so a failure is caught on the build
that caused it
Two patch scripts failed silently; v5 was reported while only v4 existed, so
no build could be trusted to match its number
Never touch an approved buildA rebuild chain started replacing R8 and R10, both already approved; an
approved build is the reference every new one is judged against, and
replacing it removes the yardstick
A deletion that leaves a hole is never a fixThe trim ate the collar faces and left holes in the hoodie: a hole on her is a
worse defect than the gap the deletion was meant to close
Change only the part that was namedA collar fix dropped the shorts rule and left blue denim on her skin line,
though only the small collar gap had needed fixing and everything else in
that build was already good
Measure before writing a geometry rule: colour the
faces a rule would keep, drop or move
Four trim rules written without that picture; the fifth picture showed the
cap in red in one image
When a junction keeps failing, ask what the original
did right
The answer to the collar was in R8 all along: one continuous shell

What this gave us

R8fix v6, the first rebuild that matched the old model's look without its wedges. And the pattern that came back on 8
and 9 October: a junction between two separately made pieces is an invented edge, and the cure is a source in which
the edge does not exist.

CH 17Part IV · Making a generated character game-ready

Master first, then reduce

7 October · Masters F8 and F10, the R8x family, and the two routes on record · The plan had been to put T3's hair on T8 and
keep the full model. What came back was a game-size build. Going back for the full model we had skipped taught us more
than any build before it.

What we builtMasters F8 and F10 (T8 or T10 with T3's hair paint read through a second UV map, 605,392
triangles), and R8x, reduced from the master part by part.
The challengeWe had skipped the full-resolution master and gone straight to a game-size build. Reduced from
the master, the eyes looked fused and black teeth ran down from the hoodie hem.
Key decisionsMaster first, then reduce. The face gets its own budget (7,000 triangles). Every part that carries
skin or a face is baked; cloth and hair keep Meshy's texels.
The resultR8x at 39,344 triangles, with a body flawless at fewer triangles than any build before it, and a
written order of work for every character after Agent E.
BUILDING A 3D WORLDPG 61
ENGINEERING JOURNALTPS ARENA

1. The question

Problem. When R8 and R10 were first made, the plan was to put T3's hair on T8. The expected result was a full
model with no other change, to be reduced afterwards. What came back was a game-size build, chopped, reduced and
baked in one go. We realised the order we had always meant to follow was merge the T variants → get one perfect
variant → repaint minor flaws and downscale to game size. The shortcut had quietly become a whole other route, so
we now had two, and no master behind either.

So we built the step we had skipped. Since every T is the same mesh in the same face order (face centres differ by 0.0
mm), the merge is a second UV map: F8 is T8 with T3's hair paint read through T3's own layout on the 243,871 hair
faces. F10 is the same on T10. 37 MB each. Both looked right, which confirmed the merge costs nothing in quality: it is
only a second UV map, so every texel stays original.

MasterR8fix v6
MeshT8's 605k, one shell56.9k, 11 parts, hair separate +
liner
PaintT8 4K + T3 2K, original texels2048 bakes of those
Hood redin T8's texturebaked copy
Hair skin-colour
patches
absent (a v6 artefact: M8x strands sit where T3's surface is skin, the
bake picks skin)
present
Usesource for painting, the cinematic tier, the originalthe game model

Which of R8's reasons for chopping into parts still apply once the master exists:

Reason R8 chopped into partsStill needed?
put T3's hair paint on T8's body: the UV layouts differno: F8 carries T3's UVs as a second map
the hair must be its own object (hair physics chains)yes
per-part budgets: face 6k, hair 16k, hands 1.2k, instead of a
uniform 9% everywhere
yes, to spend triangles where they show
the decimate must not slide edges across a part border (hoodie
and skin, hair and face), which smears colours at the seam
yes, mildly
the scalp liner (a hole appears under the hair once the hair is
separate)
yes, once the hair is separate
re-unwrap + bake to 2048 mapsno: that was only because the parts were decimated
unwelded; welded parts keep Meshy's UVs and textures

2. A whole model reduced at once: R8x

The first R8x was F8 welded and decimated in one go to R8's count. R8x and R10x came out surprisingly good: no
flaws and no holes in the body, where R8 and R10 had both. That made the one weakness stand out: the eyes looked
fused.

PartR8 / R10R8x / R10xR8fix v6
hair14,11020,91917,289
hoodie10,31610,01214,783
head + face5,8411,7164,239
shorts5,2988,5147,428
BUILDING A 3D WORLDPG 62
ENGINEERING JOURNALTPS ARENA
PartR8 / R10R8x / R10xR8fix v6
belly2,8851,1311,453
legs (2)2,911 + 2,8421,424 + 1,4351,551 + 1,568
shoes (2)2,030 + 2,0293,282 + 3,2652,019 + 2,011
hands (2)1,470 + 1,4631,056 + 1,0481,007 + 913
scalp liner2,604- (hair fused)2,614
total53,79953,80256,875

We also noticed that their legs had half the triangles of R8 and R10, yet looked flawless and without holes. That told
us that welding, not triangle count, decided smoothness, and that a uniform decimate starves the face: 18,840 face
triangles are 3 percent of the model, so a uniform cut leaves 1,716.

The parts version took budgets per part instead, set by eye:

PartFullR8x (parts)
head + face21,2597,000
hair244,08311,999
hoodie105,9959,999
shorts99,8976,499
shoes36.6k each2,000 each
legs~15.2k each1,500 each
belly14,5031,500
hands~12.7k each1,099 each
liner-1,146
total605,39247,340, 12 objects, no bake, Meshy's 4K/2K textures

3. Eyes that warped, and teeth at the hem

Problem. Even at 7,000 triangles the eyes still looked warped, and a thin slit appeared on the belly with black teeth
running down from the hoodie hem.

We offered five options for the head:

WhatFixesRisk
1. Reprojectwelded decimate as now, then fresh unwrap + T8
baked onto the reduced head (2048)
the texture slide
(most of the warp)
eyelid rims still collapse a
little; geometry only
2. Seam lock +
reproject
before decimating, the vertices on Meshy's UV-
island borders (eye and mouth islands) get a low
collapse weight; then reproject
slide + rim collapseneedles if the lock is
absolute; so weight 0.2,
not 0
3. Hard-protect
the features
eyes, lips, brows at weight 0, a graded ring around
them, the rest of the head takes the cut; reproject
rims untouchedcheeks and jaw get
coarser at the same 7k
4. SymmetryX-mirror on the decimate so both eyes collapse
identically
lopsided eyesnone; stacks with any of
the above
BUILDING A 3D WORLDPG 63
ENGINEERING JOURNALTPS ARENA
WhatFixesRisk
5. Bigger head
budget
10k instead of 7k, nothing elsefewer collapses
everywhere
spends 3k triangles on
what is mostly a texture
problem

The answer was none of them. The head had already been fixed once, on R8fix, so it made more sense to study how
than to invent a new technique, and going back through every R8fix variant closely showed it. R8fix's head was
welded, decimated plainly to 6,000, unwrapped fresh and baked from T8. The bake, not a geometry trick, had fixed the
face, so the five options had been solving the wrong problem. Applied at 7,000: the face was good and the slit was
gone.

The hem teeth followed the same path. We tried overlap strips (they made a shelf: 215 belly faces facing inward
against 28 before), snapped borders, then shared border anchors every 6 mm. Then the comparison that settled it:

ModelWhy the hem was clean or not
F8 / F10nothing reduced; one shared edge at 605k
R8 / R10parts decimated unwelded (hem = open edge, protected) + re-baked
R8fix v6different meshes but re-baked
R8x / R10xwelded + original UVs + no bake: each side draws its own ~10 mm chords through the wavy hem

Solution. Bake every part that carries skin or a face: head at 2048, belly and legs at 1024, hands at 512, fresh
unwraps from T8. Cloth and hair keep Meshy's texels. R8x final: 39,344 triangles. Everything that went through a bake
behaved; everything that kept Meshy's texels through a reduction bit us.

4. Two routes, and how each family is achieved

RouteStepsWhat it givesWhat it costs
Merge first (the
expected one)
Merge the T variants into one full
model (T8's body, T3's hair, T12's
hoodie ...) → fix the paint on that
master → reduce and bake once to
game size.
One master that is the
truth. Every paint fix is
made once, at 4K, and
every later game build
inherits it.
Fifteen minutes to make the
master: the merge is a second UV
map per donor.
Reduce first (the
one we took)
Chop T8 into parts, reduce, unwrap,
bake T8 onto the body and T3 onto
the hair in the same step → fix
what the bake shows (R8fix v3 to
v7).
A game model straight
away. The merge happens
inside the bake, by
pointing each part at its
donor.
Every paint fix is a rule in the bake
or a repaint of a 2048 map, redone
per build. Nothing to paint on at full
resolution until the master exists.
R8 / R10R8fix v6 / v7R8x / R10x
Parts frompainted T8, split-parts
test
skin from T8; hair and hoodie from M8x; shorts
and shoes voxel
the F8 master
Weldednoyesyes
Reduceper part, budgetsper part, budgetsper part, budgets, shared seam
anchors
Texturesall baked (2048 body +
2048 hair)
all baked; v7 hoodie from T3 + collar inpaintskin baked; cloth and hair keep
Meshy's texels
HairT8's, cut 93%M8x at 16kT8's at 12k
BUILDING A 3D WORLDPG 64
ENGINEERING JOURNALTPS ARENA
R8 / R10R8fix v6 / v7R8x / R10x
Total53,79956,87539,344
Known
flaw
seam cracksinner-hood red (T8's paint)the same inner-hood red: both use
T8's hoodie

They converge. R8fix's bake already reads two donors; reading from a merged master changes one line. The difference
is where the paint fixes live: on the master, once, or in the recipe, every time. For every character after Agent E the
order is master first.

5. Three routes for every character after her

We set the three final approaches side by side to decide which one every future character should follow.

RouteWhat it givesCost per character
1Reduce-then-bake,
one body
(R8 → R8fix)
parts cut from the painted model, welded, reduced,
re-unwrapped, everything baked to one 2048 body
map + hair map
fewest files, smoothest skin, but
every paint fix is a rule in the recipe,
and cloth texels are resampled
2Merge-first master,
parts, selective bake,
brush
(R8x, the pick)
master = best T's merged by UV map; parts welded +
budgets; skin re-baked, cloth and hair on Meshy's
own texels; whatever Meshy's painter got wrong is
painted by hand, by part
one more file (the paint .blend),
sharper cloth, and the fixes are ours,
which is the part that actually
decides the look
3Meshy remesh as the
base
(M8x)
Meshy's retopology for everythingclean hair and cloth for free, but the
skin lost her roundness; a donor for
hair and hoodie geometry, not the
character

The order for the next character: generate retextures → rank faces and hair in the window → merge the winners into
the master → cut, weld, reduce (the face never below a third of its detail) → re-bake the skin only → paint file → rig.

What this gave us

A master to paint on, a reduced model whose body was flawless at fewer triangles than any build before it, and a
written order of work. It also fixed what a merge means for us: putting one variant's part on another produces the full
model first, not a shortcut to the game.

CH 18Part IV · Making a generated character game-ready

Fixing AI paint errors by hand

7 October · Red paint on her collarbones, three automatic fixes that looked like burn marks, and the brush that replaced them ·
The day colour stopped being a rule in a script and became a stroke made by a person.

What we builtA paint file in Blender with one-click buttons per part, a black brush and a skin brush, and a
marker-and-diff workflow in which the saved strokes are the selection.
The challengeT8 ran hair-red paint about a centimetre onto her collarbone skin. Three automatic fixes left marks
that read as burns.
BUILDING A 3D WORLDPG 65
ENGINEERING JOURNALTPS ARENA
Key decisionsDiagnose and rank the options before acting. Colour on a face is a judgement: the eye marks, the
script converts exactly the marked texels, and nothing else changes.
The resultR8x, hand-painted, became the branch for Agent E Streetwear: the best of the three fixes. The
fringe painted into her forehead was parked.

1. Red where it does not belong

Hair-red paint on the collarbone skin
The red: T8's inner-collar paint running onto the skin, on faces the part test rightly calls neck.

Problem. T8 paints the inside of the collar and the hood lining hair-red, and ran that red about a centimetre past the
fabric onto the collarbone skin. T1 to T5 go straight from skin to black.

Cause. Meshy's painter, not our build: the red is in T8's own texture, on the master too.

Since every T is the same mesh, the hoodie could read its paint from any of them. The choice of hoodie source:

Hoodie
source
MapsCollarWhat the 2048 bake gets
T32Kskin then
black
About 900² of texels for the hoodie: slightly starved. Chosen first (v7, 106,299 faces
re-pointed).
T128KcorrectMore than the bake can use. The sharper option.
T84Khair-red
inside
What v6 had.

2. Three automatic fixes, three burn marks

Fixes made straight away had been costing us builds, so we decided that from here on every fix starts as a diagnosis
with ranked recommendations, and nothing changes until one is chosen. We set out four options (rule inpaint, collar
skin from T3, hand paint, black) and the rule inpaint was chosen first.

BUILDING A 3D WORLDPG 66
ENGINEERING JOURNALTPS ARENA
AttemptWhat it didVerdict
InpaintInside a collar zone from the chest to just under
the jaw, hair-red texels take the colour of their
skin neighbours: 165,427 texels
Better, but not yet right
Inpaint on T12The same on the sharper hoodie: 162,489 texelsOverdone: the collarbone skin had been
clean to begin with, and the fill turned it
into what read as burn marks
Two-class fillSkin-side red becomes skin, fabric-side red
becomes black (counts below)
Botched: splitting the red into skin and
black still left burn marks where the two
met
ModelRed texels → skinRed texels → black
R8fix (T3 hoodie)49,887122,397
R8fix (T12 hoodie)61,75093,966
R8x (T12 hoodie)20,03938,911

Cause. Colour on a face is a judgement. A script sees texels; where a region borders two colours, any automatic fill
mixes them, and the eye reads the mix as a bruise. Agent J's hair had been fixed the same week by hand painting
(chapter 11). And we realised the split parts gave us the order a painter would use: the body can be painted on its own
first, before the jacket goes on, so a fix on the skin never touches the cloth.

3. The brush

Solution. A paint file for R8x, where every part is its own object with its own map: one-click buttons per part (paint
skin, paint jacket, paint belly, paint shorts, paint hair, show all, look at face) that hide what is in the way and put the
right map under the brush; a black brush and a skin brush; and a SAVE button. The workflow:

#StepWho
1Paint a marker stroke, in any colour, over what is wrongPerson, in
Blender
2SavePerson
3Diff the saved map against the previous save: the changed texels are the markScript
4Convert exactly those texels to the target set for that mark: flat black for the jacket strip, a shadow
fading over skin at the neckline, skin colour, hair red
Script
5Re-export into the comparison window, judged in rendered lightScript, then
person
BUILDING A 3D WORLDPG 67
ENGINEERING JOURNALTPS ARENA

The paint file: a marker stroke on the jacket, the map beside it
The paint file. A marker stroke on the jacket (any colour will do), the texture it lands on at the right, and the one-click panel.
Whatever is marked becomes what we say it is.

What the passes did, in order:

PassTexelsNote
First skin strokes79,546A colour test caught only 1,736: the strokes were cream, not white. A diff of
saves finds strokes; a colour test does not
Flat skin fill (#FBD0C0, the
median of 34,409 ring samples)
78,987
Black strip under the jacket50,287
Shadow band at the neckline70,040100% black at the strip fading to 80% read too light against the jacket, so
we deepened it to 92% over skin
Jacket blobs5,4763,999 specks reverted: noise from re-encoding on save
Jacket touch-up25,18514 blobs
Thin waistband string13,430Undone: the string is so thin that the change could not be subtle, and the
square edges of the brush showed, so the original paint was better left as it
was
Hair fringes to hair red (#701D25,
the median of T3's hair texels)
10,726, then
10,278
26 blobs, then 24

Two things had to be learned on the way. The brush projects through the mesh's active UV map, so the jacket strokes
landed at random on T12's 8K map until that layer was switched. And Blender's brush colours are linear, not display
colours, so a brush set to black first painted grey.

The verdict: of the three fixes, the brush was the best. The eye put each colour exactly where it belonged,
which no rule had managed. R8x, hand-painted, became the branch for Agent E Streetwear.

BUILDING A 3D WORLDPG 68
ENGINEERING JOURNALTPS ARENA

4. What a brush cannot fix: the face under the fringe

Problem. Meshy painted hair colour into the forehead and brow texels. Painting it out by hand failed: the hair colour
sat in the same texels as the brows and forehead, so there was no clean skin underneath to bring back. That made us
ask how a studio artist avoids the problem in the first place:

Them (Overwatch, Fortnite)Us (Meshy)
Headsculpted bald, clean skin everywhere, even under the hairone fused shell, no scalp, fringe is the
forehead surface
Hairseparate asset: hair cards (textured strips with alpha) or groomed
strands, placed over the head
part of the shell; cut off afterwards by
a part test
Skin texturepainted in layers (Substance Painter / Photoshop): base skin, blush,
brows as their own layer, makeup, dirt
one flattened image from Meshy's
painter
Browsa layer on the face material, or small alpha cards on the head meshpainted texels mixed with the fringe
Fixing a
mistake
hide a layer, repaint one layer, re-exportrepaint texels by hand

We parked it: with her hair on, it blends in at this level of quality. Two days later the whole question went away,
because her head was replaced (chapter 22).

What this gave us

A working division of labour that every visual task since has used: the eye marks, the script converts exactly what was
marked, and nothing else changes. And a rule we had been circling since chapter 12: colour is decided by a person
looking at it, in the light the game will use.

CH 19Part IV · Making a generated character game-ready

Texture atlas, first rig, source holes

7 and 8 October · Getting the hand-painted R8x to 50 MB-class size, rigging it, and finding out that the hole in her shoulder
had been in the source all along · The first time the game, not Blender, was the judge.

What we builtOne game atlas for R8x (body 2048, hair 1024), a first rig on her existing skeleton, and her
shoulders rebuilt from the split-parts sculpt.
The challengeThe painted R8x carried 27 maps and 698 MB of GPU memory. In the game the rig webbed her
shorts and stuck her hair to her elbows, and her left shoulder had a hole.
Key decisionsBring every character to the 50 MB range. Judge geometry bald, from the angle in question,
before anything goes on top. Revert to the 90% state instead of fixing the fixes.
The resultStreetwear X in the game on 8 October: 46,709 triangles, 63 MB on the GPU, 0 contract failures,
the only build of her that was game ready and not broken. The hole had been in the source all
along.
BUILDING A 3D WORLDPG 69
ENGINEERING JOURNALTPS ARENA

1. How heavy is a character?

We noticed that Agent L, Agent J and Agent Zero looked the best in the game at the time while having the fewest
vertices, which told us that the method used to bake and reduce matters more than the count. The current Agent E
looked a tenth of Agent L's quality while using more. The game was already starting to lag and was unplayable on a
Mac, so we set the end goal of bringing every character down to the 50 MB range, not 100 MB+.

FileTrianglesMapsLargestGPU texture
memory
In-game average (Agent E, Agent Zero, Agent B, Agent
C, Agent S)
22-25
MB
25-33k34096²101 MB
Agent J, Agent L (baked route)11-24 MB42-60k32048²50 MB
Current Agent E Streetwear in game24.8 MB28,75834096²101 MB
R8fix v7 as-is25.5 MB56,87862048²101 MB
R8x full-res63.5 MB37,955278192²698 MB
R8x, maps capped at 204826.9 MB37,955272048²245 MB
M8 remesh, whole18.4 MB29,36824096²134 MB

Problem. The painted R8x carried 27 maps, three of them whole-body Meshy atlases (T12 at 8K, T8 at 4K, T3 at 2K)
of which the hoodie, shorts, shoes and hair used slivers: 698 MB on the GPU. We had picked 8K out of habit, not for a
reason: the extra resolution had never protected a build through reduction, where something went wrong every time.

Solution. A bake: for every texel of one new 2048 map, a ray reads what is painted on the part surface underneath,
whatever map that part was using. The body became one object with one fresh unwrap and one 2048 set; the hair its
own object on a 1024 set. Nothing geometric moved.

R8x full-
res
R8x, maps capped at
2048
R8x game atlasAgent L
(reference)
File63.5 MB26.9 MB13.9 MB24 MB
Triangles37,95537,95539,101 (27,203 body + 10,752 hair + 1,146
liner)
41,851
Maps27276 (body 2048 ×3, hair 1024 ×3)3
GPU
memory
698 MB245 MB63 MB50 MB

What it cost: texel density. The hoodie went from a fifth of an 8K atlas to a quarter of a 2048; the head from its own
2048 to a fifth of one; the hair from 2048 to 1024. We judged the loss acceptable for the memory it saved, with one
bake miss, a single dot filled from its neighbours. Below 50 MB is not a modelling job: every route ends at one 2048
set. GPU-compressed textures would be the next floor (chapter 23).

The three game-size options as they then stood:

R8fix (baked; skin from the painted cut,
hair and hoodie from the Ultra remesh)
R8x (parts from the
master, cloth on Meshy's
texels, hand paint, atlas
bake at the end)
R8fix + M8 clothes
and hair (Meshy's
remesh for every part
that is not skin)
In game57k triangles (budgets can lower it), one 2048
set, 50 MB. Hair separate, liner: the chains
work.
39k triangles, one 2048 set,
50 MB. Same hair and liner.
Lightest of the three to skin.
About 45k triangles, 50
MB. Same.
BUILDING A 3D WORLDPG 70
ENGINEERING JOURNALTPS ARENA
R8fix (baked; skin from the painted cut,
hair and hoodie from the Ultra remesh)
R8x (parts from the
master, cloth on Meshy's
texels, hand paint, atlas
bake at the end)
R8fix + M8 clothes
and hair (Meshy's
remesh for every part
that is not skin)
LookOn par. Everything resampled once by the
bake. Fixes are rules, and every rule fill was a
burn mark.
The one we picked: sharper
cloth texels, the strokes
exactly where the brush put
them.
Meshy's topology never
tears. Not as smooth and
even, but it holds up at
low counts.
Modelling, per
character
One command, about 8 minutes, no painting.
Needs retextures, split parts, an Ultra remesh.
Risk: a rule misfires where nobody can see it
until the build is done (the collar: eleven builds).
One command, about 2
minutes, plus the brush (Agent
E: about an hour), plus a 5-
minute atlas bake. No remesh.
As R8fix, plus a remesh
per character (Ultra:
about 44 credits).
Other
characters
The most automatic: a batch could run
overnight. Right for placeholders and bots.
Needs an eye per character.
That eye is what made Agent E
the best of the three.
Credits per character;
otherwise R8fix.
Where it failsColour.Our time.Meshy's bill, and skin.

The verdict: R8x for every character that matters, R8fix as the batch route, M8 as a donor when a part tears under
reduction. On credits, a 44-credit remesh is small next to the budget we set: 500 Meshy credits per character without
review, and 1,000 at the cap. We still decided not to force a remesh on every character, because it takes time and is
clunky; it earns its place as an option to try when a part is not working.

2. The first rig: copied, not solved

The atlas went into the game on her existing skeleton (52 bones plus a 5-bone hair chain), with weights copied by
nearest surface from the old Streetwear mesh. The file contract passed: 13 rules, 0 failures. The game showed what
Blender had not.

What we sawWhat it was
Shorts joined between the legsShorts vertices at the crotch took weights from the nearest old surface, which there is
often the other leg; posed, each leg pulls the fabric toward the other: a web
Hair stuck to both elbowsStrands hanging beside the arms in the rest pose took arm weights
Inner collar lip jagged, fine in
Blender
Adjacent lip vertices alternate between head, neck and spine weights; the idle pose turns
the head and the lip zigzags
The paint looking like hot glueThe strip and shadow are black in the colour map, but the roughness and normal
underneath are still skin, smooth and glossy. Black + glossy reads as wet

The first three fixes worked: hair weights only from the old mesh's hair, the hoodie rim rebuilt with its dense border, the
shoes baked into their own maps. Then a smoothing pass on every weight, and she came out looking cracked, skin
and clothes both broken. That taught us the lesson that set the rule for the rest of the week: all three models had
started with few problems, about 90% there, and each round of fixes took them down to 80%, 70% and lower, because
every fix created new problems to mend. So we look for the root cause and prevent it in the first place, instead of
mending problems one by one. We reverted to the 90% state.

3. The hole in her shoulder

Problem. In the game her left shoulder had a hole. Three rig passes chased it (a plain transfer, hair from the old hair
only, a per-side transfer with smoothing). Then two geometry guesses: hair winning the tie in the part test (a rule
returned 7,552 faces from hair to body), and the thin double layer of the hood collapsing in the decimate.

BUILDING A 3D WORLDPG 71
ENGINEERING JOURNALTPS ARENA

Cause. Found by a different test: we put a bald Agent E in Blender, from behind at 45 degrees, three sources side by
side. With the hair out of the way, the F8 master clearly had holes in it.

The painted master with its hair hidden: hair paint down the neck, hollow behind
The painted master with the hair hidden. Under the hair, the hair was the only surface Meshy ever painted.

The split-parts mould, bald: the shoulders exist as a flat plate on the hoodie piece
The split-parts mould, bald. The shoulders exist here: as a flat plate stuck on the hoodie piece.

BUILDING A 3D WORLDPG 72
ENGINEERING JOURNALTPS ARENA
Source, hair hiddenRear shoulders
F8 painted master (605k)hollow, both sides
R8x, M8x (cut or remeshed from it)hollow
Split-parts sculpt: hoodie piece + arm piecesclosed (a fill plate on the hoodie; the arm pieces are only forearms and hands)

We had earlier ruled out holes in F8 from a thumbnail, and a thumbnail with the hair on could never have shown them.
Every build cut from the painted shell inherited the hollow.

Solution. Hoodie and both arms from the sculpt pieces: a 2 mm voxel remesh first (the raw solid is double-layered at
the hood and a plain decimate tears it), then three ray tests per face. Paint above within 8 cm: keep. No paint above
and more than 4 mm from any paint: the closing cap, dropped (3,837 hoodie faces). Paint above is hair: keep and fill
flat, hoodie black or arm skin.

The rebuilt rear shoulder in the game, before the brush: a blotch of red and black
The rebuilt shoulder in the game, before the brush: no hole, but a blotch of red and black.

Better than a hole, but still not passable. The design had always been a backless top with a hoodie attached to the
neck, so the rebuilt shoulder had to read as skin above the sleeves and as black only on the hood. So the last step
was the brush again: the strip above each sleeve marked as skin (11,072 texels), the hood back as black (122,840
texels, the reddish ones refilled from genuine black), and a live slider on the skin patch to set the tone by eye.

That passed, and we moved on: this version of Agent E was the only one that was game ready and not broken. In the
game on 8 October: 46,709 triangles, six maps (body 2048, hair 1024), 63 MB on the GPU, 16.5 MB file, 0 contract
failures.

4. The order that worked

#StepWhy
1Bald body in Blender, from
the angle in question
The hair hides holes; a thumbnail hides everything
2Bald body in the gameThe game's lights and pose show what Blender's rest pose does not
3Approval by eyeA bald, coloured body with no holes on the shoulder has to be seen before a build is
trusted: the file contract had passed with the hole in it
4Then the hair goes onHair looks like a mush even in Fortnite and Overwatch, so we accept it there. What the
eye reads is a smooth body and a sharp face, and clothes must not have literal holes
BUILDING A 3D WORLDPG 73
ENGINEERING JOURNALTPS ARENA

What this gave us

A 50 MB-class target for every character, the first rebuilt Agent E in the game (Streetwear X), and two rules that held
from then on: a rig is judged in motion, and a hole is looked for in the source before it is blamed on the stage that
revealed it.

CH 20Part IV · Making a generated character game-ready

Voxel remesh fills: the monster neck

8 October · A jagged collar, the day we wrote Rule One, two whole new routes (B and C) built and rejected in an afternoon, and
Meshy's own remesh made into the final figure · About forty named builds in one day.

What we builtCollar welds v1 to v7, two new routes built and rejected in one afternoon (B, one voxel shell; C, A
fused around its own head), and M8x made into a finished figure with B7's back and new hair.
The challengeA jagged collar and a sticker on the hoodie that renders could not find, then a body that was
perfect except for a monster neck.
Key decisionsRule One: the eye marks, the script measures. Build the scene and let a person mark the spot.
Check every build before showing it. No fill pieces.
The resultB7 kept as the benchmark body, M8x in the game as a skin (43,563 triangles), and the cause of
the monster neck: the voxel remesh fused the hood's capped opening into her neck.

1. A sticker and a jagged lip

Character select: the jagged front collar lip of Streetwear X
Streetwear X on the character select screen: the shoulder hole gone, the front collar lip jagged.

BUILDING A 3D WORLDPG 74
ENGINEERING JOURNALTPS ARENA

Character select from the side: a flat pale patch on the hoodie at the shoulder
The sticker: a flat patch on the black jacket at the shoulder.

Problem. The shoulder hole was fixed, but looking at the model we could still see a flat patch on her hoodie (the
sticker), the front of the hoodie near her neck looked like botched black remnants scattered over it, and the front
hoodie lip was jagged.

Cause. The hoodie swapped in from the sculpt kept its inside hood walls: 564 faces, about 50 cm², standing 1 to 2 cm
off Meshy's painted surface, and a second collar lip about 2 cm outside the neck piece's own. We had checked the
swap only from behind.

2. The day we wrote Rule One

We then spent half an hour hunting the sticker with renders, ray tests and 24-angle grids. A person looking at the
model found it in seconds. That set a different way of working for anything visual: the eye finds the problem and marks
it, and the scripts measure and fix exactly what was marked.

It went to the top of our working rules the same minute, above Rule Zero (chapter 06). From then on a visual problem
starts with a scene: the model loaded, framed and lit, with a red marker brush and pins, and the spot is marked by the
person who can see it.

3. The collar, welded

With the marks to aim at, the collar took seven versions in four hours.

VersionWhat changedMeasured
v1The 564 front hood walls cut12,436 hoodie faces left
v283 edge points tucked 1.5 mm under the neck piece, 67 easedGap at the marks: median 3.4 mm, widest
7.5 mm before. Smaller, but we could still
see the gaps, so tucking alone was not
enough
v3Every hoodie collar edge laid on the neck piece, sunk 1 mmSeam: median 3.8 mm, widest 14.9 mm
before; 1.0 mm and 1.0 mm after
weldWelded at the step where the parts merge432 edge points welded; 70 not, all on
the hood rim more than 8 mm away
v4672 open-point pairs merged within 3 mm near the strokesOpen points within 1 cm of the strokes:
1,308 before, 368 after
v5, v6Texture rebuilt, 7 neck holes filled, the crusty surface relaxed (437
points, 26,702 texels)
The smoothing opened see-through slits:
rejected
BUILDING A 3D WORLDPG 75
ENGINEERING JOURNALTPS ARENA
VersionWhat changedMeasured
v7v4's shape, only the surface map flattened, the red band behind the
head replaced by the sculpt's own hair piece
Better, but we did not judge it a
commercial-grade finish, so the collar
was not yet the end

It had never been welded, and that was an architectural choice. Eleven parts, each welded only within itself; the
seams were held by 6 mm border anchors, which gives two matching edges, never one joined edge. The anchors had
then been switched off for the hem, and the mould's hoodie came from separate sculpt geometry that shared no seam
at all. A weld belongs at the step where the parts merge.

4. Route B: everything in one shell

The joins we expected, since this was a multi-part build, but we counted far more flaws than the joins could explain.
The causes: Meshy's shell has nothing under the hair; a 13x cut (605,000 to about 46,000 triangles) tears thin areas;
parts from mixed sources were never made to meet; and fixes by rules, made without seeing. We concluded that
colour was the easy part, because Agent E's colours are quite straightforward, and that the flaw was the geometry, so
the next route had to rebuild the geometry rather than keep repainting it.

RouteWhatEstimate
AKeep patching this build: the sculpt's hair piece behind the neck, back
to v4's hoodie shape, fix each new hole as it is marked
Open-ended
BVoxel-remesh each sculpt piece into one closed shell (body, hoodie,
shorts, shoes, hair), reduce evenly, paint flat colours, bake only the face,
eyes, hair and drawstrings
Optimistic: one session to a bald
body. Conservative: two to three
sessions to the game

The optimistic estimate held. B1 was the sculpt pieces fused into 36,000 triangles with 0 open edges. B2 baked
colour from A (only 242 of 36,000 faces had no source within 1.5 cm). B3 took its surface detail from a 494,000-face
clean shell and its face from the original 4K head. B5 and B7 pulled the neck back onto the painted original (110 points,
then 13). So far the voxel remesh had done well: we found B superior to A in many ways, including the texture on her
jacket from behind, which told us the route was worth finishing.

B7, about 50,000 triangles, rigged: a body we judged perfect except for one fault, the neck. It had
swollen into a monster neck that no hand work could fix and no number of revisions would
save, so we stopped revising it and went after the cause.

First diagnoses. The fused shell; then the hoodie collar filling the space. B8 (the hoodie as its own shell) came
back with a black face; B9 (B7 with A's head and neck) with a face badly damaged by many large triangles.

Cause. Meshy's hoodie piece is a closed solid whose neck hole is sealed with a flat cap. The voxel remesh fused that
cap into the body and turned it into neck: her neck came out at the diameter of the hood's opening, 54 to 70 mm
against A's 30 mm, two to three times the original diameter.

Route C was the other way round: A without its head, the whole body fused, A's head put back. Its neck was 35 mm,
close, but A's open parts became 2 mm double-walled slabs that a reduction cannot collapse: about 150,000
triangles, a mess of shards.

CheckC3A
Neck radius35 mm30 mm
See-through spots169, mostly at the sides of the face where A's head was cut20
Body trianglesabout 150,00036,000 target
BUILDING A 3D WORLDPG 76
ENGINEERING JOURNALTPS ARENA

Three builds (B8's black face, B9's torn face, C3's shards) had been shown without being checked first, and each one
wasted a review on a fault a basic check would have caught. The rule since: every build is checked before it is shown,
and a failed build is deleted, not displayed.

The lineup at the end of the afternoon, every iteration on display:

ModelStatus
ARoute A v7
B7One shell, monster neck
B8Hoodie detached; face bake is black
B9B7 with A's head; holes at the neck and face
B10Neck hole opened, but the neck is still 54 mm and the face is black
C1A fused into one shell, about 150,000 triangles
C2C1 with the inner walls stripped; torn
C3A fused, about 150,000 triangles; shards
C4A second attempt to get C3 to game size, still about 150,000 triangles
M8Meshy's remesh, 29,000 triangles. Static: not rigged, hair is part of the mesh
M8uMeshy's remesh with unwrap, 41,000 triangles. Static
M8xMeshy's Ultra remesh, reduced to game size: 48,000 triangles, maps baked at 2048. Static

B and C were dropped except one: we kept B7 as our benchmark for a perfect body with a failed head and neck, with
the method that made it, so that later bodies have a clean one to be measured against.

5. Meshy's remesh as the final figure

That pointed us to a simpler option: use M8x, M8u or M8 as the end figure. Meshy's remeshes had none of the flaws
we had spent the day chasing, so the only repair we expected was their hollow shoulder. Three steps made it work.

The hair split, without fills. Every face of the remesh goes to its nearest sculpt piece; where hair and body
overlap within 1.5 mm, the body keeps the face. The first version filled the hollows under the removed hair from the
fused shell, and pasted pieces of B's monster neck in front of her neck.

ModelBodyHairSee-through spots, with fillWithout fill
M821,4009,70033959
M8u28,90013,70033765
M8x, at game size33,40016,20031046

The see-through spots came from the fill piece itself, a step our own rules had added, and it was also what carried B's
monster neck into the remesh. Without it the spots fell from over 300 to under 70 on every model, so we dropped it
for good: no fill pieces.

B7's back. We wanted the remeshes' backs to match the B variant's, because B7's back was the cleanest torso we
had; only the torso back, though, not the head or neck, which were exactly where B had failed. B7's faces from the
waist to the shoulder tops replace the remesh's, grown 18 mm so they tuck under its edge, and welded. Seen from
behind, the graft showed no seams. Open points left on the back: M8 43, M8u 38, M8x 26.

BUILDING A 3D WORLDPG 77
ENGINEERING JOURNALTPS ARENA

New hair. A fresh 2K remesh of T3 (M3, 10,121 triangles), fitted to the sculpt within 0.17 mm. Next to the old hair the
difference was night and day: redder, fresher and far less distorted, so we put it on every model.

ModelPartVerticesTriangles
M8xOwn body and hoodie25,07527,954
B7 back and shoulders4,4285,329
B7 face-material bits236159
Body total29,73933,442
M3 hair12,47010,121
Whole model42,20943,563

We checked B7's back and shoulders and the M8 and A bodies and found no holes or cracks in any of them, so we
kept them as parts for later builds instead of discarding them with the routes they came from.

6. The hair in the game

Problem. In the game the hair looked glued to her shoulders and clipped into them when she looked up.

Cause. Measured: the hair over the neck and shoulders was 38% bound to the head bone, so it turned rigidly with the
head; and Agent E used the shared hair settings, which tie the lower hair 70 to 85% to the spine with 4 to 12 degrees
of swing.

Solution. Weights by height down the chain (root 1.39 m, then 1.22, 1.05, 0.89, 0.73), the front locks on the chest, and
her own hair settings: spine follow 8 to 50%, swing 6 to 26 degrees, more fall, a wider body capsule. Clipping in a
sprint was left for a later patch.

That night the hood's blood-red stain was painted black, a dark pimple on M8x's nose was found by hand-painted skin
dabs where our scan of 433 texels had found nothing and overwritten with a 231-texel disc of surrounding skin, and
5,589 peach texels in her hair went red. A face paint that wiped her brows made her look like a different person, so we
undid it: keeping her likeness mattered more than an even skin. M8x went into the game as a skin (43,563 triangles,
contract 0 failures).

What this gave us

Rule One. A benchmark body (B7) and the method that made it. The cause of the monster neck, which shaped every
voxel step after it. And the realisation that closed the day: M8x's mesh had too many scars to be the end, and a clean
finish would have to start from more detail, not from a reduced remesh. The next morning we went back to the full-
resolution sculpt (chapter 21).

CH 21Part IV · Making a generated character game-ready

Full-res body: hood, shoulders, normals

9 October, morning · The T8 sculpt at full detail as the body, B7's back grafted on, a hood top that had never been visible, and a
shoulder that took eleven attempts · The first day much of the shaping was done by hand in Blender, with tools built for the

BUILDING A 3D WORLDPG 78
ENGINEERING JOURNALTPS ARENA

job.

What we builtOption 2: the T8 body at full resolution (318,734 faces) with B7's back, a voxel-banded hood top,
and hand tools inside Blender: an eraser, sculpt brushes, heal and paint, point edits.
The challengeA smoothing step that left her crusty with holes, a hood top no earlier model had ever shown, and
a right shoulder that stayed lumpy through eleven attempts.
Key decisionsEvery join is made at full resolution and reduced once, at the end. Never smooth or delete by a
region rule without saying so first. Hands in Blender do the shaping.
The resultThe body every later build stands on. The shoulder's real causes were stored normals, a stale
normal map and a skin tone a few values off; the left shoulder mirrored onto the right finished it.

1. Why go back up

We had meant to use the T8 mesh entirely. M8x's mesh had too many scars to finish from, and we wanted a proper
high-definition weld between the head and body made before anything was reduced to game size, while there was
still full detail to weld. The plan, in four steps: the T8 body; B7's back for the shoulders; the paint (shoulders, hood,
inner collar); then a new high-resolution head fused on. From then on we welded at full resolution only: every join is
made at full detail, and reduced once, at the end.

The look of the hoodie was a choice too. Next to A's, B7's hoodie looked glossy and high-resolution, and we traced the
difference to its settings:

SettingA and M8x's own hoodieB7
RoughnessMeshy's roughness map, average 0.75, rougher in
patches
One flat 0.70 everywhere
Surface-detail
map
Baked from Meshy's painted shellsBaked from B7's own clean shell of about 494,000
faces
GeometryCut and reduced partsSmooth voxel shell
Metallic00

We chose B7's surface. After step 3 the body stood at 319,000 triangles with B7's back welded on (51 points), the
hood stain painted out (61,898 texels) and the collar repainted (13,039). Everything looked as good as it once had, but
the new lighting showed us flaws the old lighting had hidden: the shoulder skin geometry needed some smoothing.

BUILDING A 3D WORLDPG 79
ENGINEERING JOURNALTPS ARENA

The full-resolution body from behind: hood and rear shoulder
Step 3 from behind, in the new lighting: the hood and the rear shoulder where B7's back meets T8's skin.

2. Step 4: the smoothing failure

Problem. Step 4 put a new head on (the full-resolution head from a second Meshy generation of Agent E, with its
hair) and smoothed the shoulders on the way. We saw the same crusty look this smoothing method had produced
every time before: parts of the model botched, with holes everywhere. That told us the step had done more than it
said, so we stopped and traced exactly what it had changed before going any further.

Cause. Step 4 moved 105 shoulder points halfway toward their neighbours, eight times, with no blend into the fixed
edge; and its neck cut deleted every face within about 6.5 cm of the neck centre above the slice without checking
what it hit, which could bite the hood and collar.

Solution. Back to step 3. The rule went into our lessons the same minute: never smooth or delete by a region rule
without saying so first.

3. A hood top nobody had ever seen

We could see jagged lines along the top of the hood. We wanted it smooth, so that the hood would read as intact and
in shape even with no head inside it. We had no reference to work from: none of our prior models had shown that area,
because hair had always covered it.

The first diagnosis blamed the old hair cut. Measured by angle, the T8 hood rim was level; the ragged edge was the
top border of B7's graft, about 1.5 cm above and 3 cm below the rim. Two options:

Option 1: plane cutOption 2: voxel band
WhatOne tilted plane just under the deepest dip; only
hood faces above it deleted, never skin; a short lip
folds down and in, painted hood black; no existing
point moved
The band behind the neck copied out, given fabric
thickness, voxel-remeshed at 6 mm (B7 and T8 layers
merge into one shell), smoothed on its own, laid back
over the band
BUILDING A 3D WORLDPG 80
ENGINEERING JOURNALTPS ARENA
Option 1: plane cutOption 2: voxel band
Estimate30 min optimistic, 1.5 h conservative45 min optimistic, 2.5 h conservative
Faces removed4,3712,496
Faces added630 lip faces2,916 shell faces
Most height
removed
about 6 cm at the backnone; the shell tops out at the old height
Triangles316k322k

Option 2 won by far visually, so we took it. Option 1 had a practical case, since a lower hood minimises clipping, but it
left visible gaps and holes; we kept the method for places where clipping matters more than looks. Option 2 was
almost acceptable: a few bumps were left on the sealed hoodie, and judging those by eye called for an eraser rather
than another script.

The voxel-banded hood top in wireframe, a few stray spikes along the top edge
Option 2's hood top: an even voxel mesh, with a few stray spikes along the edge that had to be pressed down, not deleted.

4. Tools for the hands

From here much of the work was done by hand, with tools built for the job inside the same window:

ToolWhat it doesWhy we needed it
3D eraserCircle-select faces, delete, done; every erase saved as
a timestamped copy
The few bumps on the sealed hood
were easier to judge and remove by
eye than by any rule
Sculpt smooth, flatten, inflate,
relax
Blender's sculpt brushes, one click from the panelSmoothing worked well, but a few
stubborn spikes survived it and
needed brushes that act only where
they touch
BUILDING A 3D WORLDPG 81
ENGINEERING JOURNALTPS ARENA
ToolWhat it doesWhy we needed it
Heal (clone, blur, smear) and
paint
Texture brushes with a colour pickerTexture flaws needed a healing
brush like the one in Meshy, so they
could be painted out from the colour
around them
Edit points, smooth selected,
collapse selected
Point-level edits without opening holesSpikes that left a hole when deleted
Shortcut tablesSelect, dissolve, merge, hide, orbit, in plain wordsHand work in Blender runs on its
shortcuts, and they were new to the
hands doing it

Of the brushes, flatten was the one that fixed the hoodie. Red remnants on the inner hoodie were marked with the heal
brush as a selection and only the red texels inside the strokes turned black: 2,384 of 19,016, then 1,262 of 36,047,
everything else put back exactly. The shorts hem glued between the legs was deleted by hand, and the microscopic
holes it left were closed with one face per hole in flat denim colour, after a first patch in skin and red was rejected (we
wanted it blue and white like the jeans, so the patch would disappear into the denim) and a second used too many
triangles.

One bug cost paint before it was found: the SAVE button wrote the new PNG but packed the old texture into the
.blend, so every reopen lost the latest strokes.

5. The right shoulder, eleven attempts

The rear shoulder in wireframe: coarse graft triangles beside dense sculpt triangles
Where B7's graft (left, coarse) meets T8's skin (right, dense): about 10 mm triangles against 2 to 3 mm.

Problem. The back of her right shoulder looked lumpy next to the left, even after hand sculpting.

BUILDING A 3D WORLDPG 82
ENGINEERING JOURNALTPS ARENA
AttemptWhat it didVerdict
RS1Each coarse graft face split once; new points on a smooth curve; no
existing point moved (327,508 triangles)
All three rejected: more density did
not make the shoulder look
smoother, a first sign that the shape
was not the cause
RS2Split twice, close to the density of the rest of the delts (337,168)
RS3RS2 plus gentle smoothing fading to zero within 2 cm (8,144 points
moved)
TS1Smoothing without shrinking: 831 points, typically 1.7 mm, at most 9.5 mmRejected: no visible improvement,
so smoothing was not the answer
either
TS2Split twice first, then gentler: typically 0.3 mm, at most 3 mm
Normals +
tone
Stored custom normals cleared; the graft's old normal map unplugged;
the graft's skin matched to T8's (1.5 less green, 7 less blue, out of 255,
over 1.44 million texels)
Good: the first change we could see
working, which pointed at shading
rather than shape
Mirror, 2 cm
fade / 0.75
cm / none
The left shoulder mirrored onto the right: 3,186 points, typically 0.6 to 1.1
mm, at most 5.2 mm, no new holes
Clearly better; we kept the 0 cm
version, a straight mirror with no
fade

Cause. Not the geometry. Our first three diagnoses were the voxel band, a hidden second layer, and a shadow
artefact. The real ones were three: stored custom normals that never update after sculpting, a normal map baked for
B7 still plugged into the graft, and a skin tone a few values off.

SideGraft skin (RGB)T8 skin next to it
Her left253, 213, 205253, 210, 194
Her right253, 212, 200253, 211, 195

Hand sculpting had helped too, with a cost the numbers explained: flatten and smooth shrink convex shapes, so the
shoulder lost some roundness. The mirror kept the left shoulder's roundness on both sides.

6. Working safely on someone else's hands

RuleThe incident
Never close a window that shows unsaved changes,
whatever our checks say
A comparison of faces and paint called two saves identical; a sculpt only
moves points. A shoulder sculpt was nearly lost
The window always loads the newest saveA rebuild started before the latest save landed
Buttons look objects up by name at the moment of
the click
Ctrl+Z invalidated stored references: "error smoothing"
Keep the figures 1.2 m apart and each in its own slotSpacing cut to 0.8 m, then 1.0 m: hands overlapped and one figure landed
on another's spot
A rule-based cut before a face-by-face searchRebuilding the head's missing chin by matching 322,000 faces one by
one ran past ten minutes; the rule cut took 14 seconds

What this gave us

Option 2: the full-resolution body, 318,734 faces, with an intact hood top, closed shorts and two matching shoulders,
on which every later build stands. And a different kind of collaboration from the one this part started with: by the
afternoon of 9 October the hands in Blender were doing the shaping, and the scripts were doing what hands cannot
(measuring, mirroring, matching tones to the value).

BUILDING A 3D WORLDPG 83
ENGINEERING JOURNALTPS ARENA
CH 22Part IV · Making a generated character game-ready

Welding a new head onto a body

8 and 9 October · A new head for Agent E, welded to the body so that face and neck read as one person · More than a dozen
named welds on two models, scored out of ten along the way, until W7 became the new benchmark for Agent E.

What we builtA head transplant from a second Meshy generation, welds W1 to W7 on the full-resolution body, a
solved neck width, one skin tone, and a choker variant (W7C).
The challengeThe 2 cm of neck where two different models must become one surface: botched chins,
mismatched rings, wrinkles, and a blotch that five fixes could not find.
Key decisionsCut both necks flat, then bridge. Solve the neck width as a number instead of tuning it. The body
keeps its tone and the head moves halfway toward it.
The resultW7, 475,573 triangles, became the new benchmark for Agent E. The weld method is written down
step by step.

1. Why a new head

The face under the fringe could not be repainted (chapter 18): Meshy had painted the fringe into the forehead skin. A
second Meshy generation of Agent E came with a cleaner head, its own hair, and its own maps. Rather than repaint a
face, we transplanted one: her head and all her hair as one piece onto our body, cut at mid-neck. The hard part was
never the head. It was the 2 cm of neck where two different models have to become one surface.

2. First attempts, on the reduced model

The first transplants went onto M8x at game size. Each failure taught a constraint:

AttemptWhat happenedWhat it taught
Hair onlyHer hair fitted onto M8x's head by position and turn; it
pointed wrongly about its pivot, and we learned that
the dark band under the crown is part of the hair, so
removing it had been a mistake
Move the head and hair together, as one
piece
Head + hair, cut at mid-
neck
The old chin stayed floating in front like a mask, and
the head had a hole: her scalp is painted as hair, so
separating hair from head opened it
Remove the whole old head; never split
her scalp from her hair
Neck join v1Zippered two jagged rings 5 mm apart, so it folded
back on itself
Cut both necks flat first, then bridge
Neck join v2Its flat cut sliced through her nape hair, and its rings
grabbed collar edges
Neck join v3M8x's neck cut flat at 1.43 m, hers at 1.462 m through
skin faces only; Blender's Bridge Edge Loops across
the 3 cm gap, 4 cuts: 1,136 bridge faces
Better, not perfect: for the first time the
topology read as a neck instead of
random shards, which told us flat cuts
and a bridge were the right route
Neck ringRadiusHeight
M8x's neck31 mm1.455 m
Her neck38 mm1.450 m
BUILDING A 3D WORLDPG 84
ENGINEERING JOURNALTPS ARENA

A bridged neck before recolouring: the bridge faces show scraps of texture from elsewhere
A bridged neck before recolouring: each new face reads its texture from wherever its corners landed. The fix was one neck-skin
texel for every bridge face.

The same method at full resolution (474,578 triangles) was so clearly better than the reduced versions that there was
nothing to compare, and we deleted them. That was the moment the rule became: welds are made at full resolution,
and reduced once, at the end. Two more full-resolution welds built on the step-4 body (n5a, n5b) were rejected, and
we set a rule that has held since: a failed model is deleted and cleared, never put on show.

3. W1 to W7

The real welds were made on Option 2 (chapter 21). Each was scored.

WeldWhat changedVerdict
W1Head's neck cut flat at 1.458 m to drop the choker, body's stump at 1.450 m, the
two rings (214 and 164 points) joined by one row of neck skin
7/10: a point off for lines left from
the choker, two for a chin patched
with filler. The chin had been
botched in every weld so far, which
told us it had to be protected before
the cut rather than repaired after it
W2,
W3
The cut follows the neck's shape, not a width: only surface within 5 cm of the
measured neck centre. The chin masked (16,599 downward-facing front skin
faces protected, the same count before and after). Cut above the choker's
highest point all round. Open edges near the neck 262 → 10
8/10: a point off each for a
misaligned neck and for remnants
sticking out of the body's neck. But
cutting above the choker solved the
choker problem, and masking the
chin kept it intact
W4One missing chin triangle (already missing on Option 2) restored, with 26 other
single dropped faces; a Neck width slider, BODY ON / OFF, the loose head
editable
The same weld, ready for hand work
W5Rebuilt from the head as trimmed by hand (Neck width 0.139, size 0.91, 1.5 cm
back, 1 cm down); only 942 faces left below the cut
9.5/10: no major problems and no
holes. But the body's neck and the
head's neck did not meet at the
same circumference, so a noticeable
bump showed when zoomed in:
what was left was to match the two
rings 1:1
BUILDING A 3D WORLDPG 85
ENGINEERING JOURNALTPS ARENA
WeldWhat changedVerdict
W6The head's neck stretched to match the body's ring (532 points, typically 0.4
mm), seam points reset; skin toned to the face
The diameters now matched, but the
stretch left the surface scarred and
rough: better, though not yet game
ready
W7
(W7s)
The neck width solved instead of stretched: 0.08 gives a 31.5 mm head neck at
the join, equal to the body's (0.139 had given 31.0 mm), then the same
smoothed stretch, up to 2.3 mm
The best weld by a clear
margin: matching diameters
and a smooth surface at
once

W6's wrinkles, measured. The head's neck was more wrinkled in the band just above the join, and we measured
why. The body's ring is jagged and slightly oval and off-centre; the head's is rounder. Stretching each point to its
partner gave neighbours different stretches.

Unevenness between neighbouring pointsFirst W6New W6
Typical0.13 mm0.05 mm
Worst 5%about 1.2 mm0.41 mm
Largest2.93 mm1.70 mm

Better, and still not right. A hand fix on W6 was tried and abandoned: nothing good came of it. The better route came
from the cause itself. The stretch only existed because the head's neck had been sized down and then stretched back
up, so it made more sense to compute the neck width that sizes it properly in the first place than to keep smoothing
the stretch. The neck width was solved, not tuned.

ModelNeckStretch
W60.139 + smoothed stretchup to 2.7 mm, typically 0.5 mm
W7solved 0.08, no stretchnone, so where the shapes differ, the seam row leans in or out
W7ssolved 0.08 + the same smoothed stretchup to 2.3 mm, typically 0.3 mm, for a 1:1 diameter

W7s became W7. Methods we considered and declined, for the record: matching vertex counts then bridging (the
bridge already joins 214 to 164), merge-by-distance everywhere (her fringe touches her chest, and merging everything
would glue the two together, where they must stay separate), a Boolean union (needs watertight shapes), and a
normals transfer (kept as a last polish only).

4. One skin tone

Her body was too pale beside her face, so we set the target for one skin tone: somewhere between the two, or the
face's own colour.

Measured onSkin colour (0-255)
Face229, 171, 160
Body before253, 209, 194

We moved the body toward the face, because the face carries identity (a face paint the night before had made her
look like a different person): a multiplier on 3.16 million body texels and 1.44 million graft texels. The tone came out a
95% match, close enough to call the tone itself solved. But the fix made a new fault: a dark band at her neck that read
as a choker. The fade across the join had left the seam row darker than both sides.

BUILDING A 3D WORLDPG 86
ENGINEERING JOURNALTPS ARENA
HeightRed channel
1.430 m, body230
1.440 m235
1.450-1.454 m, seam row239
1.456 m, head side240
1.470 m247
1.478 m, head249

That table is after the fix. The rule it left: after any colour fade, measure the colour by height in 2 mm steps.

The final W7 took the other direction, the one later chosen for the choker variant: the body keeps its own tone, and the
head goes halfway toward it with a soft ceiling, so the face's bright channels do not clip.

W7Skin colour (0-255)
Bodyits own original tone, 253, 209, 194, untouched
Headhalfway from the face toward the body
Neck, body side up to the head side254 down to 248, smoothly, with no step at the join

W7 became the new benchmark for Agent E: the model the later builds are measured against.

5. The choker variant, and a blotch that was not paint

W7C is W7 with a black choker band from the new head. Its first versions moved the choker (24.5 mm higher; then
25% wider, 98% of it outside the neck), and then three faults came back at once: a face paler than the body (its red
channel had to rise 10.5% and clipped), 92 hair-red patches on the neck, and a choker top standing 11.5 mm off the
neck. Those three were fixed. The fourth was not: the neck between the choker and the head stayed blotchy, and we
wanted it one even colour.

BUILDING A 3D WORLDPG 87
ENGINEERING JOURNALTPS ARENA

The neck above the choker: two tones of skin in patches
The neck above the choker, lit: two tones in patches.

We flattened the colour (30,842 texels to one tone, then 37,653), replaced the stored normals on 4,035 faces,
smoothed the choker's top edge (spikes 19.6 mm to 0.6 mm). None of it changed how the neck looked, which was the
first sign that the fault was not in the colour at all. Then the screenshot that settled it:

The same neck in solid grey view: the patches are still there
The same neck with no colour at all, solid view: the marking is still there. Not paint, not roughness, not UVs. The mesh.

Neck patchTriangle sizeBend between neighboursLooks
Large trianglesabout 4 mmabout 2.6°the smooth, darker patch
BUILDING A 3D WORLDPG 88
ENGINEERING JOURNALTPS ARENA
Neck patchTriangle sizeBend between neighboursLooks
Tiny sliver trianglesabout 1 mm7 to 20°rough

Fix after fix had missed, so we stopped fixing and stepped back. If the fault was in the mesh, more colour work would
never reach it, and the answer might be a different weld altogether. We set out the options:

#OptionTrade-off
1W7 plus a choker. Take W7 with its approved smooth neck and add the
choker from the untrimmed head, fitted by height. No new neck work
Our pick, and the one chosen
2Rebuild W7C's neck strip as an even mesh with a flat toneA visible line at the jaw is likely
3Re-weld W7C the W7 way, choker back on topMore work for the same result
4Graft W7's neck from the jaw downFiddly, gains nothing over 1

The final band: cut from the original head on two fitted planes, sitting 2.5 mm outside the neck with its edges folded
in, moved 1 cm higher, with its own black material (painting it into the shared head texture would have blackened 119 of
W7's own neck faces).

What this gave us

W7, the benchmark. A neck join method that is now written down step by step (cut by shape, mask the chin, solve the
width, fade the tone, measure by height). And the strongest case yet for Rule One: the blotch that five fixes could not
find was obvious the moment a person looked at it with the colour switched off.

CH 23Part IV · Making a generated character game-ready

Game budget, KTX2 and skinning spikes

9 October, afternoon to midnight · W7 at 475,573 triangles taken to 47,557 without touching her face, rigged, compressed for
the graphics card and put in the game as v0.38 · The last reduction was done by hand.

What we builtW7 game: budgets per part, a hair-blob fix, a hand reduction with a REDUCE SELECTED button, a
rig on her existing skeleton, and a KTX2 texture loader the whole roster can use.
The challengeTake 475,573 triangles to game size without touching her face or opening holes, then fit her
textures on a 6 GB card. In the game, spikes came out of the ends of her hair.
Key decisionsThe face is never reduced. Hair is found by its texels, never by position. Character textures ship
compressed for the graphics card. A rig is measured in motion.
The resultAgent E in the game as v0.38: 47,557 triangles, textures down from 213 MB to 53 MB on the GPU,
and nothing below 1.35 m stretching more than 2 cm in a sprint.

1. The brief

The brief: a game-ready W7, with the face preserved and no holes in the body, allowed to run up to 50% over the usual
game budget for a first pass. We gave the first pass that headroom on purpose: it was better to see that nothing went
wrong at a generous size, and tighten afterwards, than to break her face or open holes chasing the final number at
once.

BUILDING A 3D WORLDPG 89
ENGINEERING JOURNALTPS ARENA
CheckFirst W7 game
Triangles475,573 down to 69,979, against 44,000 to 48,000 for the current game models
Facefully protected: all 6,682 face triangles kept, with a 1 cm soft border
Holesnone new. Open edges went from 2,424 down to 2,166, because collapsing edges can merge existing
gaps but never cuts new ones
Texturesthe same ones as W7, unchanged

It was almost there: the hair held up apart from one or two skin-coloured blobs. We decided the next pass should give
slightly less to the face and less again to the hair. Hair does not need fine geometry to read well: even AAA games
render hair as a mass, and what sells it is the physics, so its triangles were better spent elsewhere.

2. Budgets by part, and a mask nobody intended

A second build cut the model in three passes, part by part:

PartTriangles beforeTriangles after
Face6,6825,689 (-15%)
Hair141,86923,315
Body skin83,24022,111
Shorts143,8479,327
Hoodie torso56,0884,568
Other (shoes and so on)12,7233,981
Sleeves26,6681,666
Head and neck skin4,456678
Total475,57371,335

Problem. The new build was botched: the bottom half of her face had turned dark red, so she looked as if she were
wearing a mask. A face is the one part that cannot regress, so rather than patch the new build we went back to the
previous W7 game, pixel for pixel.

Cause. The step that fixed skin-coloured blobs in the hair decided what was hair by position: head material, outside a
face box that ended at 1.50 m. The lower half of her face sat below 1.50 m, so it counted as hair and was recoloured.

Solution. The previous build was restored byte for byte. The rule: hair is defined by its texels, never as the head
minus a box, and every recolour step counts how many faces it touched inside the face and refuses to save unless
that count is zero.

3. The budget, set by eye

Measured on the restored build, then set part by part:

PartNow (triangles)ChangeTargetBuilt (v4)
Face9,074-20%7,2597,270
Hair22,633-50%11,31711,333
Hoodie11,275-30%7,8938,171
BUILDING A 3D WORLDPG 90
ENGINEERING JOURNALTPS ARENA
PartNow (triangles)ChangeTargetBuilt (v4)
Shorts11,109-30%7,7767,583
Stomach2,604+50%3,9064,031
Back (skin)459 pointsto 2,500 pointsabout 3,0502,417 points
Legs, shoes, hands, shoulders, neck, othermaintainas nowas now
Total69,979about 54,00055,411

The hair blobs. In the reduced hair, big triangles stretch across skin-coloured texels while their centre still lands on
red. Three attempts: judging each face by its centre texel only; searching only near earlier strokes; and a skin test so
loose that hair highlights passed as skin. The fourth sampled each face at seven points and used a strict peach test.

CheckResult
Blobs found and fixed611
Still showing skin colour0
Face or neck faces touched0

Her hair from behind with small skin-coloured blobs between the strands
The blobs: skin-coloured texels showing through where reduced hair triangles stretch across the paint.

Then a paint job by hand, in marker colours: red strokes where hair showed skin, black on the hoodie, skin on a leg
patch. The red strokes were reverted to the original hair pixels (6,340 texels) and the 211 hair faces under them given
real hair texture; 52 within 2 mm of the face skin were left alone. An apparent hole on her right shin, which no
measurement could find, turned out on a closer look to be not a hole at all but a black marking on one of the big
triangles; that is why the hole checks never saw it, and it was painted out by hand.

BUILDING A 3D WORLDPG 91
ENGINEERING JOURNALTPS ARENA

4. The last 7,854 triangles, by hand

TargetTriangles to removePoints to remove, roughly
48,000, like Streetwear Xabout 7,400about 3,800
46,000, the middleabout 9,400about 4,900
44,000, like M8xabout 11,400about 5,900

A REDUCE SELECTED button with a ratio slider was added to the panel: select a region, reduce only that, and nothing
outside the selection or its border row changes. The reduction itself was made by hand, part by part:

PartPoints beforePoints afterTriangles beforeTriangles afterChange
Face4,0664,0667,0897,089-
Hair6,5486,54811,66711,667-
Hoodie5,4355,4358,1728,172-
Stomach2,9392,5264,0313,406-16%
Back (skin)2,4171,0464,0721,562-62%
Shoulders7907901,1081,108-
Shorts5,9665,1427,5826,590-13%
Legs1,3828402,4311,434-41%
Shoes2,9661,5145,1812,583-50%
Hands1,4341,4342,6332,633-
Neck1401406767-
Other2,4592,2161,3781,246-10%
Total28,63924,71255,41147,557-14%

The cuts went where nobody looks. Her back had never been strong in the original T8 body, because it was meant to
be covered, so it made sense to reduce it most rather than spend triangles on a surface that was not built to be seen;
the back hidden inside her hoodie lost many points for the same reason. Face, hair, hoodie, hands and shoulders were
untouched. No new holes: 1,990 open edges before and after. The two last saves, one with a repainted back, were
compared side by side under seven lighting setups (preview, studio, noon sun, back light, evening, night, flat game
light). Both held up in every light, and we chose the 22:57 save as the one to rig for the game.

5. The rig, and her shorter hair

W7 game went onto her existing Mixamo skeleton, with body weights copied from M8x's body side by side (her left
from the left half, her right from the right). Her new hair is much shorter than the old: it runs from 1.18 m (the upper
back) to her crown, where the old hair reached her waist. So only the top two links of the hair chain carry it: the crown
follows the head, the lower hair swings on the chain, and the front locks, which now end at her chin, follow the head
instead of the chest.

The first rig run put her 6 m from her skeleton and gave most of her body a finger bone: the saved file carried the
preview window's row offset on the object, and our script baked it into the mesh. The weight report caught it before
anything shipped (every point unweighted), and the rule since is to print an object's transform and bounds before
rigging it.

BUILDING A 3D WORLDPG 92
ENGINEERING JOURNALTPS ARENA

6. Fitting a 6 GB card

Before adding her, we needed to know whether she was the biggest character on the roster, and whether the game
could still run without lag on a 2060, our target mid-range card. By triangles, she is not the biggest. By video memory,
she is.

CharacterTrianglesTexture memory on the graphics card
Agent E W747,557213 MB (one 4096 map + six 2048)
Agent E Streetwear X / M8x47,904 / 43,563192 MB
Agent Zero, Agent S, Agent C, Agent B25-33k128 MB
Agent J59,943 (most)64 MB
Agent L41,85164 MB

The 4096 map is her whole body below the neck (26,720 of her 47,557 triangles: hoodie, shorts, stomach, hands,
shoes, legs, back, shoulders); face and hair share their own 2048. A graphics card stores an ordinary texture as raw
pixels, 4 bytes each, plus a third more for the smaller copies it uses at distance: 21 MB for a 2048 map, 85 MB for a
4096. Compressed formats the card reads directly (BC7 on a PC) take 1 byte a pixel.

Solution. Every map re-encoded as KTX2 (Basis Universal, high-quality mode, with mipmaps), at the same sizes. The
current release of the encoder we used refuses any image over about 12 megapixels, and her 4096 map is 16.8, so we
pinned the release before that limit. Each compressed map was decoded back by the game's own decoder and shown
in Blender beside the uncompressed model. They looked the same, so the compression cost nothing visible, and we
added the loader and switched her over. The game gained a KTX2 loader that works offline, for every character; her
maps load as BC7.

BeforeAfter
Texture memory on the graphics card213 MB53 MB
File31.3 MB33.3 MB
Map sizes4096 + six 2048the same

What is left for the 2060 is no longer memory. Ten characters at about 50 MB each fit easily on 6 GB. The test run
counted about 1,000 draw calls a frame, and each character costs one per material slot, more again for shadows. The
cheap next steps: drop her 2-triangle face material (one draw call and two 2048 maps for two triangles), compress
the other characters the same way, and measure on the 2060 itself before anything bigger.

BUILDING A 3D WORLDPG 93
ENGINEERING JOURNALTPS ARENA

7. Spikes that only moved in the game

Thin skin-coloured spikes coming out of the ends of her hair on the character select screen
The character select screen: thin skin-coloured spikes from the ends of her hair. In Blender, at rest, there were none.

Problem. Sharp spikes came out of Agent E in the game, but not in Blender. Since the mesh was the same in both
places and only the game moved her, we suspected the rig.

Cause. It was the rig. 25 points near the ends of her hair strands were still tied to her upper spine (about 65% Spine1,
26% Spine2). The rig script decided what was hair by the colour at each face's centre; thin faces inside the strands
show skin-coloured texels, failed the test and kept the body weights copied from M8x. In the game the hair chain
swings the hair while the spine holds those points, and the faces between stretch into spikes. Blender only showed
her at rest.

Solution. Every head-material point outside the face and neck that still carried body weight now follows the hair
chain. Measured in the game, sprinting: nothing below 1.35 m stretches more than 2 cm. Still open: the seam where
her hair meets her neck skin (1.40 to 1.45 m) stretches up to 4.6 cm in a sprint or when she looks up. The rule: a rig is
measured in motion before it is called done, because the rest pose proves nothing.

The milestone was committed as v0.38: the rigged model, the loader, the skins, the docs and a screenshot. The raw
work (233 Blender files, 28 GB) stays outside the repository.

What this gave us

Agent E in the game at 47,557 triangles and 53 MB, from a 475,573-triangle benchmark, with the face untouched by
every reduction. A compression path the whole roster can use. And the clearest picture yet of where the frame time
goes on a mid-range card, which is the next problem.

BUILDING A 3D WORLDPG 94
ENGINEERING JOURNALTPS ARENA
CH 24Part IV · Making a generated character game-ready

Build cost and the rules it left

5 to 9 October · The machine, the process, and the 35 lessons the remake wrote into our documents · Most of the cost of
these four days was not geometry. It was the way we worked, until we changed it.

What we builtAn account of where the four days went, problem by problem, and the rules the rebuild wrote into
our lessons file: 35 numbered lessons (E1 to E35) and two sets of standing rules.
The challengeA build that took eight minutes took seventy and then crashed. Most of the cost of the four days
was the way we worked, not the geometry.
Key decisionsFree the machine before blaming the code. Change exactly what was marked, and nothing else.
The resultThe next build ran in 56 seconds, and the rules are grouped by how we review, build, judge and
test.

1. The machine

Problem. A build that took eight minutes took seventy, then crashed. We blamed the new code.

Cause. The code took two seconds in isolation. The machine (16 cores, 32 GB of memory, an RTX 2060 with 6 GB)
was at its ceiling.

ProcessRAMGPU
memory
GPU load
The retexture comparison window (16 figures, two 605k masters, 12 textured
models, animation playing)
12.3 GB3.6 GB30 %
The R8 comparison window (playing)4.6 GB0.1 GBlow
Agent L's sheathe file, Agent J's kick file (unsaved work)3.7 + 1.2
GB
0.2 GB0
Our project dashboard app1.0 GB0.5 GB55 %
Chrome (all tabs)about 4
GB
1.2 GB-
The build itself0.9 to 4
GB
00 (CPU
bakes)
4 GB of 32 free, 60.6 of 65 GB committed. A game had been running during the stall.

Solution. Playback off in both windows; the twelve retextures cut to the five best faces; the build stamped per part
so a stall names its line. The next build ran in 56 seconds.

2. Where the time went

Problem (chapter)Clock time,
start to
finish
(approx.)
Named buildsWhat decided it
Why her face looked worse (chapter
15)
about 3 hR, R8, R10, R8b, R8c, M8, M8u,
M8x; 12 repaints
Looking at the baked textures, not
at renders
BUILDING A 3D WORLDPG 95
ENGINEERING JOURNALTPS ARENA
Problem (chapter)Clock time,
start to
finish
(approx.)
Named buildsWhat decided it
Seams and the collar (chapter 14)about 3 hR8fix v1-v6, TurtleneckA face-coloured diagnostic that
showed the cap
The step we skipped (chapter 17)about 6 hF8, F10, R8x v1-v5 + finalDigging the old recipe instead of
inventing a new one
Colour by eye (chapter 18)about 8 h,
mostly
painting
about 8 paint passesThe brush and the save diff
Into the game (chapter 19)about 3.5 hatlas, 5 rig passes, mould v1-v3A bald model from behind
Geometry, not colour (chapter 20)about 14 h over
a day
collar v1-v7, B1-B10, C1-C4, M8
/ M8u / M8x with grafts and
hair
Rule One; the capped neck hole
Back to full resolution (chapter 21)about 4.5 hsteps 1-4, hood options 1-2,
RS1-3, TS1-2, three mirrors
Hands in Blender; the stored
normals
Joining the head (chapter 22)about 6.5 hneck v1-v3, n5a / n5b, W1-W7,
W7C (about 8 builds)
A solved number instead of a tuned
one; a screenshot with the colour
off
Benchmark to game (chapter 23)about 4.5 hW7 game (4 builds), hair fix (4),
2 rig runs, 2 encodes
The reduction by hand

3. The rules, with the incidents that wrote them

Each of these is now in our lessons file, most with a numbered entry describing what happened and what it cost.
Together they are 35 numbered lessons (E1 to E35) and two sets of standing rules, from these four days alone.

How we review and approve

RuleThe incident
Rule One. The eye marks, the script
measures.
For a visual problem, build the scene
and ask for the spot to be marked; then measure
what was marked
Half an hour of renders, ray tests and 24-angle grids hunting a sticker-like
mark that a person spots in two seconds (chapter 20)
Consult first, with the best recommendation; no
action on a diagnosis
Six R8x builds chased one hem, each acting on a diagnosis nobody had
reviewed. We decided that every change is now proposed first, with our
best recommendation, because one agreed plan costs less than six builds
in the wrong direction (chapters 17, 18)
Show it in the window before analysing it, and
version it before it runs
A v5 reported while only v4 existed; two silent patch failures (chapter 14)
Check every build before showing it; delete a failed
build, never display it
A black face, a torn face and a mess of shards shown unchecked, and
rejected on sight: each cost a review that a check would have saved
(chapter 20)
A mod replaces the figure in place; a variant stands
beside it only on a real fork, and only after asking
Clones of clones filled the window with redundant figures, until it was
hard to tell which one was current (chapter 17)
Change only the part that was namedA collar fix that also changed the shorts (chapter 14)

How we build

BUILDING A 3D WORLDPG 96
ENGINEERING JOURNALTPS ARENA
RuleThe incident
Weld before reducing; reduce, then bakeThe wedges; the hem teeth; the fused eyes (chapters 15, 17)
Never use a closed split piece where it touches skin;
take it from a fused shell
The collar, eleven builds (chapter 14)
A voxel remesh fills everything inside a closed
shape; leave the hood out
The monster neck: a neck as wide as the hood's opening (chapter 20)
Joins are made at full resolution, by a flat cut on
each side and a bridge, and reduced once at the end
Neck joins on the reduced model folded and tore (chapter 22)
Head and hair from a generated model are one
piece; never split the scalp
A hole in the head where the scalp was painted as hair (chapter 22)
Never move or delete points by a region rule on a
finished body; name every geometry change before
it runs
Step 4: a region rule moved points on the finished body, and the build
failed outright with a crusty surface (chapter 21)
A deletion that leaves a hole is never a fix; fills and
paint go exactly where they were marked, nowhere
else
The collar trim; the fill pieces in front of her neck; a face paint that made
her look like a different person (chapters 16, 20)
Hair is found by its texels, never as the head minus a
box; a recolour counts the face faces it touched and
must count zero
The lower face turned dark red, as if she wore a mask (chapter 23)
After a colour fade, measure the colour by height in
2 mm steps
A fade that painted a choker (chapter 22)

How we judge, and how we test

RuleThe incident
Judge geometry bald, from the angle in question,
before anything goes on top
F8 reported as having no holes, judged from a thumbnail, which could not
show them (chapter 19)
A rig is judged in motion, in the game, never at restThe first rig (chapter 19); the spikes (chapter 23)
Print a model's transform and bounds before rigging
it
A rig that put her 6 m from her skeleton (chapter 23)
Never close a window that shows unsaved changes;
a test never touches a user's save file
A shoulder sculpt nearly lost; a fit file deleted by a self-test (chapter 21)
Pick a source resolution only where a bake
consumes it
8K textures carried into a build with no bake: 698 MB (chapter 19)
Credits are cheaper than hours: 500 per character
without a question, 1,000 at the cap; a remesh is
offered as a try, never forced
A 44-credit remesh weighed against a day of rules (chapter 19)

4. The pattern

Read together, the rules say three things. The generated model is a good sculpt and a bad construction: weld it, part it,
and keep the face's budget. The eye decides, and it needs tools, not reports: a brush, a marker, an eraser, a slider, a
row of figures in the same light. And every fix we made by rule, unrequested and unseen, made her worse: the models
were already about 90% there, and each unrequested fix took them lower, to 80%, then 70%. Once a model is that
close, a fix nobody marked has more to break than to gain. The builds that moved her forward were the ones that
changed exactly what was marked, and nothing else.

BUILDING A 3D WORLDPG 97
ENGINEERING JOURNALTPS ARENA
PART V2 chapters
V

Reference

  1. 25Architecture reference
  2. 26Engineering rules and their incidents
BUILDING A 3D WORLDPG 98
ENGINEERING JOURNALTPS ARENA
CH 25Part V · Reference

Architecture reference

October 2026 · Reference page. Main is at v0.37.1, with Agent C on her own branch at v0.37.2.

The game in one paragraph

TPS Arena is a third-person shooter that runs offline in a browser or as a desktop app. It is about 15,000 lines of plain
JavaScript on Three.js, with no build step and no network at runtime. Characters, abilities, weapons and maps are
tables of data. Seven characters have real rigged models: Agent E, Agent S, Agent Zero, Agent L, Agent J, Agent B as a
placeholder, and Agent C on her branch. Six gun characters are still built from code.

Decisions that shape everything

DecisionWhat we weighedWhy it went this wayChapter
Three.js, offlineUnreal, Unity, GodotIt is plain code in one repo that we can read, change and
test with scripts. Unreal's work lives in binary assets and
an editor that is hard to automate. The renderer was never
why the game looked flat
01, 13
Characters are rigged GLB
files on Mixamo's skeleton
Characters built in
code. Meshy's own rig
Painted faces on spheres never read as characters.
Meshy's "Mixamo rig" had bones up to 177 degrees off, so
no shared clip fitted it
02, 03
An NPC is the character's
own code, driven by a fake
keyboard
A separate bot class
with its own numbers
Bots cheated in small ways. This is meant to be PvP one
day, so nothing about a bot may differ from a player
05
Art is placed in Blender, the
game plays it back
Grip and pose
numbers in code,
reviewed by
screenshot
Twenty iterations to seat one sword in a hand by numbers,
against twenty minutes of dragging it into place
03, 06
Bullets test capsules, not
bodies
Ray tests against the
rendered mesh
3.6 ms a ray against four skinned bodies. 0.09 ms against a
capsule
05
One door for knockbackEach ability pushing in
its own way
Ground and air behaved differently. With one door a retune
is one number
05
A clone is an ability, not a
character
A second bot with its
own charges
It did not double damage, appeared on the scoreboard and
could be farmed for kills
07
Strikes travel along the
body, for every character
Sending one
character's strikes up
the camera line
It made him the only character who looked angled11
Map architecture is
modelled. Generation is for
character and organic
pieces
Every piece from a
picture through
image-to-3D
About 250 credits spent on the wrong route09
Showcase pieces go from
high to low detail by baking
Straight decimationIt shattered a blade and made a "playdough" shield10
Code in git, game-ready
binaries through LFS, raw
files never in git
A fresh repo.
Rewriting history
Nothing lost. A clean repo is planned for v1.0.006
BUILDING A 3D WORLDPG 99
ENGINEERING JOURNALTPS ARENA

How a character is made today

StepWhat happens
1A concept picture, in an A-pose with empty hands. It is approved before anything goes to 3D.
2Meshy turns the picture into a textured model. Props (sword, scabbard, cloak) are generated separately, because a fused
sword comes out as a lump.
3Proportions are tuned on sliders in Blender, on the bare mesh, and baked in.
4tools/heroprep.mjs prepares the mesh. Mixamo gives it the 65-bone skeleton. The tool brings it back.
tools/herocheck.mjs
checks it against a 13-rule contract. Each rule is a bug that already cost us time.
5Hair is re-bound by tools/hairbones.mjs, because the automatic rig ties long hair to arms and hips.
6Clips come from Mixamo, played as exported unless we set a speed. A character's own moves are authored in Blender.
7Grips and sockets are placed by hand in Blender and saved to a small file the game reads.
8The kit is data in src/agents.js. tools/kitcheck.mjs fails if any description promises something the code does
not do.

Steps 2 and 5 are where chapter 13's bottleneck sits. The model arrives as one fused shell, and the hair fix can only
ever be a patch on that.

How it is tested

An Electron harness opens the game muted and runs a scripted list of steps. It saves a screenshot and the returned
numbers after each one. Almost every figure in this journal was measured that way.

What is being checkedHow we check it
LogicThe Electron harness. It measures logic well.
Frame rateA separate benchmark that times the GPU at the real play resolution (chapter 08). The
harness measures rendering badly.
How something looksA visual change is reported as measurements and pictures. The final judgement is made
by eye, in the running game. The harness does not replace that.

The performance budget

Our bar: 120 fps on Low and Medium on an RTX 2060, with 80 to 100 as the floor. The game's code runs on one CPU
core, so the budget is draw calls and script time per frame.

ItemWorking limit
Characters30 to 60 thousand triangles
Props5 to 15 thousand triangles
Map pieces1 to 5 thousand triangles
LoadingUp to ten seconds, so that nothing is built or compiled during a match
BUILDING A 3D WORLDPG 100
ENGINEERING JOURNALTPS ARENA
CH 26Part V · Reference

Engineering rules and their incidents

Reference page. The rules we work by, and the problem that produced each one.

None of these rules was written in advance. Each one exists because something went wrong first. We document every
mistake as well as every fix, so that we grow. The chapter where each incident is described is given in brackets.

How we review and approve work

RuleWhere it came from
Rule One. The eye marks, the script
measures.
For a visual problem, build the scene
(the model loaded, framed and lit) and ask for the
spot to be marked with a brush, pins or a
screenshot. Then measure what was marked
Half an hour of renders, ray tests and 24-angle grids hunting a flaw on
Agent E's hood that a person spots in two seconds. Written above Rule
Zero the same minute. (Chapter 20)
Rule Zero. Diagnose before fixing. Report the
cause and the options, and agree the fix, before
anything is changed. An open question is a question,
not a work order
We judged Agent L's jump clip unusable, replaced it and committed the
change without a review. The clip was whole. It only needed slowing
down. (Chapter 06)
When a Meshy description is revised, change only
the part under revision
A full rewrite of the description swapped the blue coat we wanted for
something else. (Chapter 06)
Every credit spend is approved first, job by jobA 25-credit job was submitted as soon as its price came back, with no
approval step. (Chapter 07)
Every picture is reviewed by eye before it goes to 3DAn island went to 3D on an earlier approval, with the final picture unseen.
(Chapter 09)
In art tuning, do the one named step, then stop and
review
A sword grip kept being reshaped beyond the step that was agreed.
(Chapter 07)
A change made for one character stays on that
character
A guard fix for Agent J was applied to everyone and broke Agent L's guard
cancel. (Chapter 11)
Never call a visual change fixed. Report
measurements and pictures. The judgement is made
by eye, in the game
Agent C's hair was reported done several times and failed review in the
game each time. Measurements cannot see what eyes see. (Chapter 12)
Variants are compared in Blender first. Nothing goes
into the game until one is picked
Two hair variants were put straight into the live game files. (Chapter 12)
Every build is checked before it is shown. A failed
build is deleted, never displayed
A black face, a torn face and a build broken into shards were shown
unchecked. (Chapter 20)
Version every build before it runs, and show it before
starting the next. A change to a build replaces it in
place; a variant stands beside it only on a real fork,
and only after asking
A "v5" was reported while only v4 existed; clones of clones filled the
comparison window. (Chapters 16, 17)
Change only the part that was namedA collar fix also changed the shorts. (Chapter 14)
Design iterations stay private. The journal records
the final design
Set as our policy when the statues were being designed. (Chapter 10)
BUILDING A 3D WORLDPG 101
ENGINEERING JOURNALTPS ARENA

How we build

RuleWhere it came from
Every change gets a patch noteWe wanted AAA-style patch notes from the earliest builds. They are why
this journal exists. (Chapter 01)
A character's quirk is an asset or pipeline problem.
Fix the file or the tool. Never add a per-character
flag
We added two wrong per-character flags while importing Agent Zero.
(Chapter 04)
An NPC carries no number of its ownOur bots had their own speed, their own damage and a free headshot on
every bullet. (Chapter 05)
Name moves by what the player does. A "jump
attack" is Space plus left click, never the name of
the clip that plays
Moves had been named after their animations. (Chapter 05)
A character-select description must match what the
ability does. A script checks it
Descriptions kept promising lifesteal that had been removed. (Chapter 06)
One movement rule for all charactersAgent J alone was sent up the camera line and looked angled. (Chapter 11)
Weld a generated mesh before reducing it. Reduce,
then bake; keep the original texels only at full
resolution
The wedges on Agent E's face and belly were our unwelded reduction, not
the sculpt. (Chapter 15)
Joins between two models are made at full
resolution, by a flat cut on each side and a bridge,
and reduced once at the end
Neck joins on the reduced model folded and tore. (Chapter 22)
Never move or delete points by a region rule on a
finished body. Name every geometry change before
it runs
A smoothing step and an unchecked neck cut left Agent E's surface
rough, with holes. (Chapter 21)
A deletion that leaves a hole is never a fix. Fills and
paint go exactly where they were marked
A collar trim, fill pieces pasted in front of a neck, a face paint that
changed who the character looked like. (Chapters 16, 20)
No comment in the middle of a line of settingsA // silently switched off the rest of the line. It broke Agent E, then
Agent Zero, then Agent S's main ability. We misread the last one at first as
a balance problem. (Chapter 04)

How we use the tools

RuleWhere it came from
Read a tool's own free instructions before spending
credits on it
We spent about 250 credits of image-to-3D on pieces that should have
been modelled for nothing. (Chapter 09)
Heavy Blender jobs run headless, never in the live
window
A bake froze Blender for 15 minutes and lost unsaved work. Headless, it
took 8.5 seconds. (Chapter 09)
Measure the GPU, not the loop, at the real play
resolution
The meter said 80 to 100 fps while the screen showed about 10. (Chapter
08)
Measure every art step against the last releaseOne texture pass cost about 20% of the frame rate. (Chapter 08)
Decimation is not a pipeline. Showcase pieces are
baked from high to low
Decimation gave us a shattered blade and a playdough shield. (Chapter
10)
Match colours on rendered pixels, not on the source
picture
Clouds came out orange three times after tone mapping. (Chapter 09)
BUILDING A 3D WORLDPG 102
ENGINEERING JOURNALTPS ARENA
RuleWhere it came from
Before a Blender scene is used for a decision, one
joint angle is compared with the game
Arms were tuned in a preview that was 20 degrees off the game. The
tuning was wasted. (Chapter 11)
A report that a move does not work checks three
things before naming a cause: the clip plays, the hit
lands, the pose shows
We reported half a diagnosis as the cause. The fix built on it did not work.
(Chapter 11)
Measure what is under an asset before cutting itCutting Agent C's hair exposed that there was no back beneath it.
(Chapter 12)
Judge geometry bald, from the angle in question,
before anything goes on top
Holes were ruled out from a thumbnail; the painted master was hollow
behind both shoulders. (Chapter 19)
A rig is judged in motion, in the game, never at restSpikes from the ends of Agent E's hair appeared only when she moved.
(Chapter 23)
Never close a window that shows unsaved changes.
A test never touches a user's save
A hand-sculpted shoulder was nearly lost; a self-test deleted a saved fit.
(Chapter 21)
Character textures ship compressed for the
graphics card
Agent E's maps took 213 MB of video memory; compressed, 53 MB, at the
same sizes. (Chapter 23)
One game window at a timeTwo or three copies of the game open at once made it unclear which build
was under test. (Chapter 07)

The pattern

Read together, most of these say one of three things. Measure before you change anything. Get approval before you
spend or fix. And when the question is how something looks, it is decided by eye, in the running game, and the eye is
given tools to mark exactly what it sees. Our most expensive mistakes were not coding errors. They were changes
made on an unreviewed judgement, where a review was what counted.

CH 27Back matter

Roadmap

10 October 2026 · Always the last chapter. What is next, and why.

The target

Our schedule is three months. What is reachable in the remaining time, by the assessment in chapter 13, is a vertical
slice: six characters whose look and motion read as intentional, one finished map, and play over LAN.

What changed since the last version of this page

ItemThenNow
The character model routeUndecided between
four routes (chapter 13)
Measured on one character. Agent E was rebuilt from her
generated sources into a clean, game-size model (chapter 14),
and the recipe is written down
Agent COn her own branchMerged into the main game (v0.37.2). Her hair stays on variant
2E for now
BUILDING A 3D WORLDPG 103
ENGINEERING JOURNALTPS ARENA
ItemThenNow
Compressed texturesPlanned, not startedDone for Agent E (v0.38): the game reads KTX2 textures, 213
MB down to 53 MB on the graphics card

Decisions that come first

DecisionWhat depends on it
Which character goes through Agent E's recipe nextFour days for the first character; the next should be faster,
because the rules and tools now exist
When netcode startsLAN play. Nothing exists yet, and it is the item that cannot be
squeezed at the end
Hardware: a bigger graphics cardHow far the free local models can go (chapter 10)

Work in progress

ItemWhere it standsWhat is left
Agent EW7 in the game as her default look: 47,557 triangles,
53 MB on the graphics card
The seam at the back of her neck
stretches up to 4.6 cm in a sprint; her hair
swing is still tuned for the old, longer hair;
a face material that covers 2 triangles
Agent CMerged. Hair on variant 2EThe remaining hair slits, a jump clip and
the wand angle
Agent L's drawAt 90% by our own measure, in game at twice speed.
The Blender file and the frame-by-frame intent are
kept in our draw notes file, so it can be picked up
again
The last 10%
Agent BStill a placeholder on Agent E's bodyA model of her own
Agent L and Agent JStill borrow a guard or an ultimate from other
characters, marked as borrowed in
docs/HERO_KITS.md
Their own guard and ultimate
Olympus V2The arena kit and all eight gods are inBaked lightmaps, instancing for repeats,
and distance versions of the models
WarehouseRedesigned on paper as a covered warehouse with a
17-piece kit
The build
CascadeurInstalled, not yet usedIt is the planned tool for character attack
animation

Performance on a mid-range card

Our target machine runs an RTX 2060 with 6 GB. With textures compressed, memory is no longer the limit: ten
characters at about 50 MB each fit easily. The next limit is the number of things drawn each frame.

#StepWhy
1Measure on the 2060 itself (npm run bench)A real frame rate before anything bigger
2Compress every other character the way Agent E's wereThe other characters use 64 to 192 MB of
textures each, uncompressed
BUILDING A 3D WORLDPG 104
ENGINEERING JOURNALTPS ARENA
#StepWhy
3Cut draw calls: about 1,000 a frame in a 5 v 5 test. Merge small
material slots, starting with Agent E's 2-triangle face material
Each material slot is a draw call per character,
and more again for shadows

Planned engine work

From the developer guide's own plan. None is started.

  • Flipbook effects for smoke and fire.
  • A particle engine to replace the hand-written one.
  • A sound pass.

The order, and why

#NextWhy
1Carry Agent E's recipe to the next
character
The vertical slice needs six characters whose look reads as intentional, and the
route now exists
2Start netcodeLAN play needs it. It is the item that cannot be squeezed at the end
3Finish one mapThe vertical slice needs one finished map

Performance work runs alongside all three.

Beyond the game

The game is the first world, not the last. The same pipeline, generated models made game-ready, a budget that holds
on a mid-range card, maps built piece by piece, is what a walkable copy of a real building needs. That is the direction
after the vertical slice: places that mirror real landmarks, previews of projects that are not built yet, and the same
worlds in VR and AR.

BUILDING A 3D WORLDPG 105