Files
animation/characters/work/lena_leafbikini/README.md
T
jeremy eae8327811 feat(characters): ship lena_leafbikini_base_v01 as female Ariki default
Lena with the leaf bikini sculpted into the mesh — a separate character
descending from her own Tripo original, not a lena_base_v02. Full-res
(1,029,360 v) at 1.777 m, now Ariki_Female_QuatSkin.glb + LOD1.

First nearest-surface graft in the rig lane: AccuRig had to be fed a 20:1
decimated bait, so the index-exact graft the two earlier ships used was
impossible. The bait recovers AccuRig's 9.346 mm offset in closed form, which
makes it a required input forever — copied into the ship folder, and rig-work's
"disposable" rule amended to say so. Mesh moved 0 mm, 0 unweighted verts.

lena_base_v01 goes superseded, not rolled-back: she lost the default slot but
Ariki_Female_QuatSkin_Nude.glb is untouched, so per rule 9 her folder stays put.

Also lands the leaf-cut lane (work/lena_leafbikini): hue-keyed seam detection
that removes the leaf shell and leaves both holes open by request, registered
as a lane milestone with its sha256. Corrects the rig-work trap note — baits do
carry embedded textures; it is AccuRig that strips them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 16:09:45 -07:00

7.3 KiB
Raw Blame History

work/lena_leafbikini — the leaf-bikini lena lane

Grammar-exempt (REGISTRY rule 11). This lane has already produced one ship — characters/female/lena_leafbikini_base_v01, the game's female default since 2026-08-13, which is frozen (REGISTRY rule 2). Everything still here is lane material governed by .agents/rules/working-files.md; when the next body ships it graduates into characters/female/lena_leafbikini_base_v02/ and gets its own Ships row.

Parent original: characters/originals/female/female_lena_leafbikini_tripo.glb (sha256 977104ba…66cf29, 1,029,360 v / 1,992,503 f, 0.9792 units tall → 1 unit = 1.815 of a 1.777 m body). Read-only, and re-hashed by every stage that opens it.

Layout

NN_*.py the recipes. These are the history — kilobytes each, and they regenerate any state below from the original
leaf_mask.npz the leaf mask (stage 04): per welded point, plus the vertex→welded map and the hue/sat/val the key was cut from. Regenerable, kept because stage 05 consumes it and re-deriving it costs a full import
v01/ stage output. gitignored (characters/work/*/v0[0-9]/) — registered artifacts inside it are git add -f'd by hand
v01/review/ QA renders. gitignored, regenerable, reused rather than re-minted

The stages

stage what it does
01_graft.py grafts the AccuRig skeleton onto the pristine GLB (nearest-surface, not index-exact — the bait was decimated 20:1). This is what produced lena_leafbikini_base_v01. Independent of 0205
02_probe_leaves.py what are the leaves? Reports the albedo key candidates and per-vertex proudness
03_render_leaves.py textured and clay turnaround of any mesh in the lane. The clay pass is what settled the question
04_leaf_mask.py builds and proves the leaf mask: hue key → component filter → close → hole fill → grow, baked to vertex colour and rendered
05_cut_leaves.py deletes the masked faces. Holes left open, on purpose

What the leaves turned out to be

The clay render (stage 03) shows every leaf, curled tip and hip vine with all materials stripped, so:

  1. They are real geometry, not paint. This is the opposite of Lena's game-body underwear, where a ray-crossing census found the bra was the skin and deleting it opened a hole — the whole reason tools/make_lena_nude_body.py re-fairs a rim band instead of cutting. Here there is a solid shell to remove.
  2. They are welded into the body. One Tripo mesh, one material, one UV map: the leaves are a bulge in the body surface, not a separable object, material slot or UV island. Nothing can be selected "by object"; the seam has to be found.
  3. The seam is both a colour edge and a crease — where a leaf meets skin the surface folds back on itself.

Why the key is hue

Stage 02 first keyed on green dominance, G - max(R, B), and topped out at 0.20: Tripo painted these leaves a dark desaturated olive (sRGB ~0.27/0.35/0.20), so the channel gap is only ~0.050.09 and any threshold catching the leaf also catches shadow noise on skin. Hue separates them outright — skin 2040°, leaf 6080° — and is invariant to exactly the thing that ruins the channel gap, which is how dark the pixel is.

Everything after the key is repaired on the mesh, over position-welded adjacency, not in texture space:

step why
min-comp 200 drops JPEG speckle
close 6 rings fills specular blowouts inside a leaf, where the highlight clips to near-white and its hue becomes noise. At --val-max 0.62 the hip vine leaves came back half-grey
enclosed-hole fill a mask hole fully surrounded by leaf is a mask defect, not a skin island
grow 2 rings lands the cut just outside the rim. A hole one ring too big is invisible; a leftover leaf stub is not
z ≤ 0.85 of height her irises and eyebrows are green too. Without this the key takes her eyes (929 verts)

Result: 239,561 welded points in exactly two islands — bust (z 0.6310.755 of height) and briefs (z 0.4550.594). Two islands is itself the check that nothing else keyed.

Welded adjacency is not optional anywhere here: the glTF importer splits every UV seam into separate Blender vertices, so on the raw mesh a face on a texture-chart border is indistinguishable from a face on a hole rim, and ring operations would leak along the seams (33,202 of the 1,029,360 verts are seam duplicates).

The cut rule

A face dies only if all of its vertices are masked. With the 2-ring grow that puts the surviving rim about one ring outside the green, i.e. just onto skin. Both directions of that asymmetry are deliberate: "any vertex masked" erodes a further ring into her skin and leaves single triangles hanging off every zig-zag in the mask edge, while a cut one ring short leaves leaf root standing proud — the one outcome that would make the file useless for judging the seam.

Then two passes that can only remove things that are already not part of a smooth surface: dangling-face erosion (a face with ≥2 welded-boundary edges is joined to the surface by at most one edge; no such face exists on a closed surface, so it cannot bite into skin) and detached-shell removal. On this mesh they found 2 faces and 0 shells — the colour cut was already clean, which is the strongest evidence the mask was right.

Regenerating v01/lena_leafbikini_leafcut_sculpt_glb_v01.glb

B="/c/Program Files/Blender Foundation/Blender 5.1/blender.exe"
L=characters/work/lena_leafbikini
"$B" --background --factory-startup --python $L/04_leaf_mask.py -- \
     characters/originals/female/female_lena_leafbikini_tripo.glb $L/v01/review
"$B" --background --factory-startup --python $L/05_cut_leaves.py -- \
     characters/originals/female/female_lena_leafbikini_tripo.glb $L/leaf_mask.npz \
     $L/v01/lena_leafbikini_leafcut_sculpt_glb_v01.glb
"$B" --background --factory-startup --python $L/03_render_leaves.py -- \
     $L/v01/lena_leafbikini_leafcut_sculpt_glb_v01.glb $L/v01/review cut

Stage 05's gates, all reported and the first two fatal:

gate result
every surviving vertex position present in the input 0 strays — this is a deletion and only a deletion
loose vertices 0
surviving shells 1
removed geometry's extent z 0.4550.755 of height (bust + briefs bands only)

Open

  • The holes are not filled, by request. Filling is a separate and harder decision: the crotch/gusset history in work/lena/06*.py is what happens when a membrane spans a wide footprint — it flattens the anatomy it spans, which is why the nude lane only ever faired a narrow rim band. Whatever fills these has to rebuild bust and crotch anatomy, not just span them.
  • The base-colour map still carries the leaves and their baked contact shadows. The rim of each hole is skin painted with leaf shadow, so it reads darker than her surrounding tone.
  • The cut is unrigged, because it was authored on the pristine original. It transfers to the shipped body for free: 01_graft.py moved the mesh 0.000000 mm, so lena_leafbikini_base_v01 carries the same 1,029,360 vertices in the same order and leaf_mask.npz indexes it directly. Cutting the shipped GLB is running stage 05 with a different first argument — but the result is a new ship folder, never an edit to the frozen one.