Files
animation/characters/work/lena/README_lane_history.md
T
jeremy 3d8825f5a9 reorg(characters): ship-time folders — lena_base_v01 ships, lane moves to work/lena
REGISTRY rewritten around the central rule: a character folder is born only
when a body ships to ariki-game (<character>_base_v<NN> = ship ordinal).
lena_nude dissolves accordingly:
- characters/female/lena_base_v01/ — SHIPPED 2026-08-10: AccuRig GLB carrier,
  T-pose/rig FBX + JSON, previews, frozen README
- characters/work/lena/ — the live lane: recipes 01-47 (incl. new 36-47:
  refill/sheets/clay/despeckle/musculature/spin/AccuRig export/graft/pose QC),
  masters (athletic_v04 blend + textures, accurig blend), lane-history README
- hires_claude/hires_work intermediates (blends, logs, probes) pruned

Supporting docs: AGENTS.md, working-files rule, rig-graft plan addendum,
originals README, prune_lane.py.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-12 07:17:15 -07:00

24 KiB
Raw Blame History

lena_nude — nude body variant (WIP, not released)

Lena's body with the underwear removed: no bra, no briefs, breasts with volume and no nipples, smooth featureless crotch. Staged here until it is approved for the game.

file role
lena_nude_quatskin_glb_v01.glb the body — 65-bone Quaternius game skeleton, texture embedded (self-contained)
lena_nude_basecolor.png the baked albedo, loose, for further iteration

Ship name at release is Ariki_Female_QuatSkin_Nude.glb in ariki-game/assets/quaternius/derived-bodies/. Per ../../REGISTRY.md rule 8 that rename happens exactly once, on import — never create Ariki_* files in this repo.

How it was made

Generators live in this repo (moved out of ariki-game 2026-08-06 — they author a character, so ../../REGISTRY.md governs them; see .agents/wiki/ARCHITECTURE.md "Tool provenance"). They still read the canonical rigged bodies from the game repo (Ariki_Female_QuatSkin.glb and the _ChestV1 breast-sculpt donor), resolved via $ARIKI_GAME or a sibling ariki-game/ checkout — if neither resolves they abort with the path they tried, rather than reading the wrong file. Run from C:\Users\Jeremy\tinqs\animation:

blender --background --python tools/_bake_nude_body_texture.py
    # defaults to writing lena_nude_basecolor.png into THIS folder

blender --background --python tools/make_lena_nude_body.py -- \
    --out     characters/female/lena_nude/lena_nude_quatskin_glb_v01.glb \
    --size    1.0 \
    --texture characters/female/lena_nude/lena_nude_basecolor.png

python tools/verify_body_variant.py \
    characters/female/lena_nude/lena_nude_quatskin_glb_v01.glb

--size is breast volume as a multiple of the _ChestV1 sculpt (0.75 / 1.0 / 1.25 were rendered for review; v01 ships 1.0). A new size is a version bump, not a new variant — same recipe, different parameter. (This README and ../../REGISTRY.md both called the flag --scale until 2026-08-06; the script has always parsed --size.)

Facts worth knowing before editing this

  • The underwear is the body surface. A ray-crossing census finds exactly two surface crossings at every height — thigh, belly, bra cup, briefs alike. There is no skin underneath, so the garment can only be flattened into the skin, never deleted. Deleting it opens a hole, which is precisely the defect in the game repo's Ariki_Female_QuatSkin_Bare.glb (black cavity across the chest, visible even untextured). Do not use _Bare geometry for anything.
  • The base mesh is not watertight (3,809 boundary edges). The painted underwear was hiding open slits; with it gone they read as a dotted line of black triangles, so the builder welds them shut in the torso band. That is where this file's lower vertex count comes from.
  • Use clay renders to judge geometry. tools/_render_body_closeup.py --clay drops the material; it is the only reliable way to tell a mesh defect from a painted one. Several dead ends here came from mistaking painted bra shadow for a dent, and vice versa.

Working files

This lane follows .agents/rules/working-files.md: a version is a milestone, not a step. Fixing the bra produces one saved file at the end, not one per cut.

hires_claude/.lanekeep pins the only .blend files that survive here — the masters named in the Derived-artifacts table of ../../REGISTRY.md, plus the live chain head. Everything else a step script wrote is scratch: the NN_*.py recipe plus masks.npz regenerates it from the master above it.

python tools/prune_lane.py characters/female/lena_nude --recursive   # dry run

Two things this lane learned the hard way:

  • Check for a running Blender before pruning. A heal step takes ~7 minutes and writes only at the end; the lane gained a new head (17_healed.blend) during the audit that produced this section.
  • hires_work/ is the abandoned GLM-agent attempt (see its glm_run_attempt*.log), superseded by hires_claude/. It has no .lanekeep, so every .blend in it classifies as scratch.

The root-level lena_sculpt_glb_v01-nude-Zephyr.blend is hand-authored — no step script rebuilds it — so the pruner reports it as UNKNOWN and never touches it. Decide its fate by hand.

Status

Verified against the canonical body: 1 primitive, identical attribute set (no tangents), material MI_Body_Lena, 65 joints in identical order, height 1.777 m, inverse-bind drift 4.6e-6, weights normalised, texture embedded. Posed sweep under a UAL clip is clean (no spikes or tearing).

Known cosmetic limits: a faint tonal seam where the fill meets untouched skin at extreme zoom, and no skin-pore detail inside the filled area — that smoothness is also what guarantees no nipple or crotch detail can appear.

Full history and the reasoning behind each choice: ~/.claude/plans/i-have-a-lena-velvet-ripple.md.

v04 — bust matched to the approved concept turnaround (2026-08-06)

lena_nude_sculpt_glb_v04.glb, master hires_claude/34_v04.blend. Recipes 32_ref_measure.py (measure) → 33_bust_variants.py (size study) → 34_apply_bust.py (k=0.5).

Guiding art: exchange/INBOX/lena-base-turnaround/ — front/side/back T-pose renders, GPT Image 2, approved by Can 2026-08-06.

The reference is the same character, not a new one. Measured against the front/side silhouettes:

landmark reference v03 mesh
heads tall 6.43 6.08
waist / hip 0.504 0.505
waist width 0.1260 H 0.1318 H
hip width 0.2500 H 0.2608 H
bust projection +0.0156 H +0.0210 H

Proportions already agree and the widths sit inside silhouette-threshold noise for generated art, so only the bust was changed: the breast forms are rescaled to 50% of their projection off a bi-harmonic chest wall, landing at +0.0155 H. Nothing outside the chest region moves (verified 0.0000 mm) and height is unchanged, so every v03 heal survives.

Two traps for whoever tunes this next:

  • The projection metric saturates below k≈0.5 — k=0.35 measures +0.0154, indistinguishable from k=0.5. Below that the "deepest slice" row migrates and stops tracking breast volume, so the number cannot be pushed lower meaningfully. Treat 0.5 as a floor, not a midpoint. The reference reading may also be inflated by the T-pose arm crossing the chest in a side view, which would mean the real reference bust is smaller still.
  • Front renders cannot tell the sizes apart. k=0.5, 0.65 and 0.8 look identical head-on because the frontal key light flattens the chest. Judge from the side view only. Variants are in hires_claude/review_33/k{100,080,065,050,035}/.

Re-running at another k takes seconds: _bust_wall_cache.npy holds the 8-minute chest-wall solve.

Not addressed, and the largest remaining gap to the concept art: the reference is a smooth mannequin with no scan noise, while this mesh still carries speckles and dents on the hips and thighs, plus the untouched original head.

Decimated for the retexture / re-atlas lane (2026-08-06)

lena_nude_decimated_glb_v01.glb — v03 collapsed to the game-body budget (31,964 v / 64,957 tris, ratio 0.037728) as input for hires_claude/24_seams.py. Recipe 30_decimate.py + 30b_fix_winding.py. Read HANDOFF_decimated_v01.md before working on it — in particular, its carried UVs are scrap (collapse shreds Tripo's 5,870-chart atlas) and it ships with 25 known backfacing triangles.

RIGGED — lena_nude_accurig_glb_v01.glb (2026-08-07)

First rigged nude Lena. v10's mesh, byte-identical, plus a 101-bone AccuRig skeleton. 45_accurig_export.py (carrier out) → AccuRig (manual) → 46_graft.py (skeleton in) → 47_pose_qc.py. Master rig/lena_nude_accurig_glb_v01.blend.

No rig bait. tools/rigbait_decimate.py exists because the source GLBs are 1.9M tris and AccuRig refuses them; v10 is 62,929 tris, under the lane's own 78k bait budget, so it went in whole. AccuRig returned the same 31,111 vertices in the same order, so weights copy index-to-index — which deletes the decimated→full-res nearest-surface transfer where the July lane mangled the fingers.

Three traps, all of which produced a confidently wrong result before being caught:

  1. AccuRig ships a T-Pose ACTION. Blender's importer binds it as animation data, and an action re-applies its pose on every evaluation and every file load. Clearing pose values while it exists is futile: the in-session assertion passes, the saved file comes back posed, and the body deforms 1.25 units into the air with every edge length unchanged. Rigid motion — which reads as "weights are fine, the model is gone". animation_data_clear() first, then clear the pose, then assert.
  2. Verify the DEFORMED result, not the rest mesh. The first graft asserted vertices hadn't moved and passed — while the armature modifier displaced the whole body. The rest mesh proves nothing; the modifier is what the engine runs.
  3. Never pose bones by substring. "Upperarm" also matches UpperarmTwist01/02; rotating a twist helper by its parent's angle compounds it and reports 14× edge stretch that looks like broken weights and is entirely self-inflicted.

Scale (0.576058) and the carrier's 11.77 mm offset are recovered by least-squares fit, not hardcoded, so a re-rig at a different height still lands.

Still to come: the AccuRig→Quaternius conversion. This carries 101 CC_Base_* bones; the game standard is the 65-bone Quaternius contract.

Athletic build — v10 (2026-08-07), textures/geometry base

lena_nude_decimated_glb_v10.glb, master hires_claude/athletic/athletic_v04.blend. v09's look, with a second geometry despeckle underneath it. Recipe: 39_despeckle.py --apply (SHARP_DEG=55 CLUSTER_MAX=20) on refill_v04/final_v06.blend42_musculature.py (strength 1.0, SPINE=0.34).

Ready to look at without any scripting: open athletic/lena_v10_turntable.blend and press spacebar — the 50-second turntable is baked into the file.

Two things this pass settled:

  • Pick the threshold by sweep, not by feel. At 55° the speckle caught rises 181 → 441 verts while the anatomy classification is unchanged (8 long folds, exactly as at 70°). At 45° it falls to 6 folds — real creases have started merging into the speckle set. 55° is the last threshold that is still free.
  • Dissolve the stubborn clusters, do not pointmerge them. Merging a cluster to a single point leaves a fan vertex in the middle of where the flap was, and on a ring that is not planar that vertex is a new spike: measured, it made folds >70° worse (361 → 444). Dissolving the vertices and letting the ring close over improved every band — >30° 2138→1763, >50° 741→524,

    70° 361→304, >90° 268→196.

Converged: a third iteration is a wash (>70° 304 → 312). Mesh 31,504 → 31,111 v, height unchanged. What is left at >70° is mostly the real creases the isolation test is protecting.

Athletic build — v09 (2026-08-07), superseded by v10

lena_nude_decimated_glb_v09.glb, master hires_claude/athletic/athletic_v03.blend. v07's anatomy (v08's additions rejected, left behind the V08_EXTRAS flag) with the back seam fixed and a subtle spinal furrow. 42_musculature.py at strength 1.0, SPINE=0.34.

The seam down her back was self-inflicted, and the cause is worth remembering. The spinal groove sits at x = 0 — which is exactly where 24_seams.py cuts the back midline. So the two sides of the spine live on opposite edges of the torso chart, and the atlas-space gradient was differentiating into padding rather than across her body. It drew a hard ragged line down her whole back.

Fixed by taking the height field's gradient in 3D and projecting it into per-texel tangent frames. That never asks the atlas who a texel's neighbour is, so it is seam-correct by construction — for the spine and equally for the inner-leg and arm seams, which would have hit the same wall. Tangent maps are smoothed before use: rasterised per face they are piecewise-constant, and a faceted tangent basis facets the normal map.

Two checks worth repeating on any future field:

  • Prove which line is yours. Rendering with SPINE=0.0 gives a back identical to v06, which is what established the remaining soft line is the furrow and not a leftover artifact.
  • A falling max tilt can be good news. It went 53.8° → 10.9°. The 53.8° was the seam discontinuity, not anatomy; the real relief was never that steep.

Furrow trimmed to u 0.5200.795 so it fades at the sacrum. Run it to 0.470 and it reaches the glute cleft, meets the crease already in the mesh, and reads ragged.

Athletic build — v08 (2026-08-07), REJECTED — second definition pass

lena_nude_decimated_glb_v08.glb, master hires_claude/athletic/athletic_v02.blend, 42_musculature.py at strength 1.25. Adds clavicles, lower rib arch, the inguinal furrow ("V"), glutes with cleft and fold, hamstrings, patella, tibialis, achilles and forearm mass.

Two shape-function fixes mattered more than any of the new anatomy, and both are worth remembering whenever a field is turned into a normal map — because a normal map is the derivative of the field, so anything that kinks the field draws a line:

  • band() used a linear clip ramp, whose derivative jumps at both ends of the window. Every window boundary was drawing a crease. It is a smoothstep now, which reaches each end with zero slope.
  • The front/back selector was the boolean y < 0. That terminates every front- or back-only field abruptly at the silhouette — which is what drew the sickle lines down her shins. It is a smooth blend over ~4 cm now.
  • The per-leg np.sign(sn) == sg selector was dropped: it put a hard boundary at x=0 and was redundant, since every leg field is already a function of |x| - legc.

Athletic build — v07 (2026-08-07), first pass, superseded by v08

lena_nude_decimated_glb_v07.glb, master hires_claude/athletic/athletic_v01.blend. Recipe 42_musculature.py <in> <out> [glb] [strength]. Maps only — geometry untouched, so this is fully reversible and v06 remains the smooth build.

Relief goes into the normal map, not painted shading. Darkening the albedo to fake a muscle bakes one lighting direction into the skin: it reads as dirt the moment the key light moves and is wrong in every pose. The albedo takes only a 5.5% cavity term, which is what subsurface actually does and which survives relighting.

This is where the 14-chart atlas pays off a second time. A tangent-space normal is the surface gradient in (u,v), so taking the gradient of a height field in atlas space gives one directly — but only if texel density is uniform, or the same slope means different things in different charts. This atlas measures 1.00x spread; on Tripo's 1.8x soup it would have needed per-chart correction.

Two traps, both of which produced visible garbage before they were fixed:

  • Evaluate the anatomy per TEXEL, not per vertex. Building it per vertex and interpolating gave crumpled-paper noise: with ~10 mm edges an ab block is 2-3 vertices across, so the field's gradient is piecewise-constant per triangle and the normal map follows the triangulation. Rasterise position, then evaluate the anatomy at texel resolution.
  • Measure the landmarks. The navel was assumed at u 0.523 and is actually at ~0.560 (the front-midline profile peaks at 0.56 and the waist pulls in from 0.57), which put the whole ab stack a band low. The quad centre line was assumed to drift inward going down; her stance has the feet apart so it drifts outward (x 0.037 at the hip → 0.106 at the ankle) — 24_seams.py had already measured it, and using the guess laid the muscle bellies along the edges of the thigh and left contour lines down the leg.

Breast and crotch are excluded by mask, so the featureless brief survives. Strength 0.55 / 1.0 / 1.45 rendered for review (beauty_str_055, beauty_athletic_v01, beauty_str_145); v07 ships 1.0. This departs from the approved turnaround, which is a smooth mannequin.

Smooth body — v06 (2026-08-07)

lena_nude_decimated_glb_v06.glb, master hires_claude/refill_v04/final_v06.blend. A second 40_texspeckle.py pass on v05; geometry and UVs unchanged.

Texture despeckling is done — do not run a third pass. The passes converge fast and then stop mattering:

pass texels touched effect on the render
1 111,212 (0.663%) flecks visibly gone from chest and belly
2 12,521 (0.075%) 3,738 pixels over five views, max Δ 0.059
3 (measured, not run) 4,426 (0.026%) would be invisible

Every mark still visible is geometric — the 361 edges still folding over 70°, mostly in tight concave creases where collapsing them would cost real shape. More texture passes cannot reach those, and past this point each pass trades impurity for plasticity by eroding the grain 36_refill.py deliberately transplanted.

Body — v05 (2026-08-07), superseded by v06

lena_nude_decimated_glb_v05.glb, master hires_claude/refill_v04/final_v05.blend. Recipes 39_despeckle.py --apply40_texspeckle.py.

The speckle was two different defects wearing the same face, and a clay render (38_clay.py) is what separated them — as this README has said all along.

  • Geometry. 892 edges folding over 70° in 220 isolated clusters. Detection needs sharpness AND isolation together: deviation-from-smooth alone flags 33% of the body at this density — her hips score like a defect — and sharpness alone would cut the real crease under the buttock. Smoothing cleared 891 → 335 verts, then stalled: the survivors are folded flaps, whose Laplacian target sits inside the fold, so relaxation cannot escape it. Those 126 clusters were collapsed by pointmerge instead. Result 892 → 361 edges >70°, mesh 31,964 → 31,504 v.
  • Paint. The rest was Tripo scan noise baked into the albedo and resampled faithfully by the re-atlas. 31,457 texels of small local-contrast outliers inpainted, with blobs over 400 px and the entire head excluded so lips, brows and eyes are never candidates.

Body — v04 (2026-08-06), superseded by v05

lena_nude_decimated_glb_v04.glbv03 with the breast and underwear region re-authored. Texture-only; geometry and UVs are v03's. Recipe 36_refill.py.

The fill's low frequencies are a harmonic solve on the mesh with Dirichlet boundaries, not an average of nearby skin. That distinction is the whole point: an IDW average (what 05_texture.py did) does not agree with the skin it abuts, so it lands as a decal with a tone step at the old bra/briefs outline — the ghost. A harmonic fill equals her real skin at the mask edge by construction, so a step is impossible rather than merely small. Solving on the mesh rather than in the atlas also keeps the fill continuous across chart seams; the crotch alone straddles the torso and leg charts.

Grain is transplanted back from clean skin tiles so it is not plastic, and the normal map is flattened with rm set to the surrounding-skin median over the same texels — that flatness is what guarantees no nipple or crotch detail can reappear.

Kills the rusty crotch patch. Against v03: mean pixel |Δ| 0.0028, nothing over 0.15, the change concentrated in hip and chest. Her original texture is untouched outside the region.

Re-atlased body — v03 (2026-08-06), superseded by v04

lena_nude_decimated_glb_v03.glbthe v04 bust-matched sculpt, decimated and re-atlased in one milestone. Supersedes both files in the two sections below. 14 anatomical charts, material exported doubleSided=true. Recipe 30_decimate.py24_seams.py31_reatlas.py (~4 min).

The grey tears across her face and underbust are fixed, and they were never holes — they were backface culling on inward-wound patches. Those patches are wound consistently within themselves, so normals_make_consistent cannot see them (which is why 30b_fix_winding.py oscillated and settled at 25 stray triangles, far too few to explain a 15 cm band). Read HANDOFF_reatlas_v03.md before re-treading any of it.

Re-atlased (2026-08-06) — superseded by v03

lena_nude_decimated_glb_v02.glbv01's geometry with a new UV atlas and resampled maps. Same 31,964 v / 64,957 tris; Tripo's 5,870 charts replaced by 14 anatomical ones (coverage 62.0 → 68.6%, texel-density spread 1.8× → 1.00×). Recipe hires_claude/24_seams.pyhires_claude/31_reatlas.py. It deliberately looks identical to v01 (mean pixel |Δ| 0.0012 across five views) — the win is a paintable, uniform-density layout, not a new look. Read HANDOFF_reatlas_v02.md, especially the colorspace and unwrap-solver traps, and note that the grey tears on the face/underbust are v01's dangling-flap defect and are still there.

Hi-res sculpt v03 (2026-08-06) — Jeremy's three notes on v02

lena_nude_sculpt_glb_v03.glb, masters hires_claude/27_clean.blend (geometry) and 29b_final.blend (textured). Recipes: 17_line_heal.py26_finish.py25_speckle.py --apply29_tone3d.py08_export.py.

note measured outcome
cut lines shading kinks >12° 9704 → ~6900; flipped faces (the black dashes) 1511 → 10. Ribcage and thigh lines gone frontally; waistband + underbust still legible under raking light
discoloured bra/crotch on-body tone gap, patch interior vs surrounding skin: 0.0102 → 0.0068 luma. The pale panels in beauty_v02_healed/ are gone in beauty_v03b/
cleavage sternum fillet radius 9.5 mm → no sharp sample at any height; notch depth 44 → 31 mm

Four dead ends this version ruled out — do not repeat them

  1. The lines are not cracks; do not weld. Stage 15 assumed the panel seams were disconnected vertex runs. They are not: the mesh has only 830 boundary edges in 689 loops of 35 edges, and for kink verts the nearest vertex outside the 3-ring sits at 1.58× the local edge length (median) against 1.73× for control skin. Welding at eps 1.2 mm — about the 1.85 mm mean edge length — merged ordinary neighbours and tore the mesh, 830 → 24k boundary edges.
  2. They are not in the custom normals either. Corner-vs-vertex deviation is 0.008° mean, 15 loops above 5°; clearing custom split normals changes nothing.
  3. A membrane is the wrong operator, and widening it is worse. The relief is ONE VERTEX WIDE (0.23 mm median at the centre, back to the 0.074 mm background by ring 2), so a collar-fixed membrane pins itself to the defect's own shoulders. GROW 4 and 8 were no better than GROW 2 while moving material up to 38 mm. A ring median is correct: it deletes a 1-vertex outlier and returns anything broader unchanged, so the underbust fold, clavicles and navel survive by construction instead of by a tuned threshold.
  4. Never level tone from atlas-adjacent texels. A Poisson solve whose boundary condition was the mismatch against UV neighbours turned the patches into pale grey panels — UV adjacency is not body adjacency, so the border mismatch came from other body parts and gutter. This is the warning already in 05_texture.py's header. Sample on the body (KD over skin verts in 3D), smooth the correction over the mesh graph, then rasterise.

Two process traps worth keeping: restore the real material before saving18/20/25 originally rendered clay last and then saved, so the file on disk kept the clay material and lost its texture wiring, which is why the first tone attempt aborted with "could not find the wired basecolor". And the pristine textures are not inside working blends: Blender purges the orphan .002/.003 copies on save, so read them from 00_welded.blend.

Still open: 1649 non-manifold and 830 boundary edges — bmesh.ops.holes_fill, the edit-mode mesh.fill_holes operator, and a per-loop pass all refused every one, which is why normals_make_consistent could only reach 1198 of 1511 flipped faces before explicit per-face reversal finished the job. None of this is propagated to the game body (lena_nude_quatskin_glb_v01.glb), which is built independently on the 65-bone Quaternius rig.