Commit Graph

9 Commits

Author SHA1 Message Date
type-two
d4cd8e7ad7 remesh op: game-LOD TRELLIS heroes — solidify past voxel, voxel remesh, decimate floor 1/3, smart-UV, Cycles bake
Thin-shell physics learned the hard way: fabric thinner than the voxel
perforates in the remesh itself; decimating any thin slab below ~1/3
collapses its surfaces. Verified on the baseball cap: 483k -> 41k tris,
watertight, textured.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 21:28:40 +10:00
type-two
e35b613600 graft: mirror + bind=none + mixamoprep; masking fixed; THIS hand asset still not placeable
Delivering the honest state after a long debugging run. Real fixes that stand on their own:

· MASK RADIUS WAS COMPARED IN THE WRONG SPACE. Vertices were tested against a world-metre radius
  using local coordinates, so the mask silently found nothing whenever the object carried a
  scale — 8126 verts matched as FBX (mesh scale 100) and ZERO after a GLB round-trip. Now
  compared in world space.
· BODY MESH WAS SELECTED BY VERTEX-GROUP COUNT. A previously grafted part inherits every one of
  the body's groups from the weight transfer, so on a second pass the HAND was being chosen as
  "the body" — masking the other wrist then found nothing and the old hand silently survived
  under the new one. Now selected by vertex count among skinned meshes, and the choice is printed.
· join() leaves the part mesh's ARMATURE modifier dangling at None and re-parents it to whatever
  empty sat above it, collapsing it to the origin. Placement is now saved and re-bound explicitly.
· --mirror added (the asset is a .R hand; the .L needs it) with outward normal recalculation,
  since a negative-scale mirror leaves every face wound backwards.
· mixamoprep added: bakes graft MASK modifiers (a modifier is not a deletion — the geometry comes
  back in the export otherwise), joins to ONE mesh, strips the armature and stale vertex groups,
  scales to height, writes FBX. Mixamo's auto-rigger wants exactly that shape of input.

STILL NOT WORKING, and I am not going to claim otherwise: placing THIS hand asset. Its stored
rest geometry is not hand-shaped — measured 84 units tall with the armature modifier off versus
~0.08 with it on, so the rig is what poses it into a hand. Removing the modifier exports the
spiky rest mesh; applying it did not bake the shape either. Every headless attempt to detach,
transform and re-bake it has corrupted the geometry.

Recommendation rather than more blind iteration: open rigged_hand.glb once in the Blender GUI on
ultra, apply the pose/armature by hand, and export a clean STATIC hand.glb. Positioning a static
mesh is the part that already works. The asset is an awkward Sketchfab export (mesh and rig
nested several empties deep, non-hand rest pose); one manual pass makes everything downstream
trivial and repeatable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 01:31:16 +10:00
type-two
d6d69189ed graft --bind=part: fix the dangling rebind, but the bind-pose mismatch remains UNSOLVED
Instrumented the graft stage by stage on ultra rather than guessing further. Measured:
  mesh AFTER T    : (-0.434 .. -0.352)  <- correct, anchor was -0.393. The transform was always right.
  mesh AFTER join : (-0.024 ..  0.002)  <- snapped to origin
  mesh parent now : Armature   mods: [('ARMATURE', None)]

So bpy.ops.object.join() deletes the part's armature OBJECT, leaves the part mesh's ARMATURE
modifier pointing at None, and re-parents the mesh to whatever empty sat above it (Sketchfab
exports nest several). Fixed by saving each part mesh's world matrix before the join, then
re-binding the modifier to the body armature and restoring the placement.

That fixes the PLACEMENT — the hand now lands on the wrist — but exposes the real problem
underneath, which is NOT fixed: the hand mesh is violently stretched. The body armature carries
object scale 0.01 and the hand rig 1.0, so joining bakes the hand bones into the body's space
while the mesh's bind pose still expects the original bone positions. The armature modifier then
deforms it from a bind pose that no longer matches. Restoring the object matrix cannot fix this:
on a skinned mesh the bones drive the geometry, not the object transform.

STATUS, stated plainly:
  --bind=body  WORKS. Detailed geometry on the existing rig, no finger articulation.
  --bind=part  places correctly, deforms wrongly. DO NOT USE.

The better route, and the one to take given John is happy to re-rig: graft the hand geometry with
--bind=body, then send the combined mesh back through Mixamo for a fresh full auto-rig. That
sidesteps the cross-armature rebind entirely, lets the tool that is actually good at binding do
the binding, and returns mixamorig-NAMED finger bones — which the existing clip bank can drive,
unlike this hand's _rootJoint/thumb_base.R_03 naming, which nothing in the bank references.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 00:59:28 +10:00
type-two
c55d7c5b8d graft: add an articulating bind mode; body-bind works, part-bind mesh placement does NOT yet
Tested against John's real assets: chubs-trelli-30k-sol-rigged.fbx (Mixamo, 33 bones, only 8
finger bones across BOTH hands) and whitgrl/base/rigged_hand.glb (69 bones, 65 of them fingers).

That gap is the whole point: transferring weights from the body gives detailed hand GEOMETRY
still driven by 4 wrist bones, so the fingers cannot move. Hence two modes:

  --bind=body (default, WORKING): mask the body's old hand, weight-transfer the new geometry onto
    the existing rig. Verified end to end on chubs — correct scale (auto-corrected a x11 gap),
    correct anchor, 9203 verts masked, renders correctly.
  --bind=part (rig merge WORKING, mesh placement NOT): joins the part's own skeleton and parents
    its root under the target bone. Verified: body rig 33 -> 102 bones, parented under
    mixamorig:RightHand, and it refuses to join if the two rigs share bone names (safe here only
    because the hand uses _rootJoint/thumb_base.R_03, not mixamorig).
    KNOWN BROKEN: the hand MESH does not land on the wrist — it stays near the origin as a stray
    sliver while the bones land correctly. The asset is a Sketchfab export nesting mesh and rig
    several empties deep (RootNode > Sketchfab_model > *.fbx > ...); transforming the top-most
    ancestor did NOT fix it, so my diagnosis of that step is still wrong. Do not trust
    --bind=part until this is resolved.

Two real bugs found and fixed along the way, both from holding Blender references across
mutations — the same class as the earlier ReferenceError:
· bpy.ops.object.join() REALLOCATES arm.data.bones, so a held Bone pointer silently refers to a
  different entry afterwards. A graft aimed at mixamorig:RightHand ended up parented under
  mixamorig:RightHandIndex4. Names are captured up front now.
· a part file is often a whole donor character, so --mesh= selects and the op prints what the
  file held and what it chose rather than silently grafting a torso.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 00:15:40 +10:00
type-two
a682bd871b graft op: swap crude AI body parts for detailed ones, without the cut-and-bridge dead end
John wants to attach better hands/feet/faces to Mixamo-rigged TRELLIS characters and asked
whether it can be automated. It can, but not the way the standard tutorial workflow says.

Measured on his actual file (chubs-trelli-30k-sol-rigged.fbx): 240,615 verts, 33 bones, and only
EIGHT finger bones across both hands — Mixamo's reduced skeleton, which is exactly why the hands
look crude. Armature scale 0.01 against mesh scale 100.0, the classic cm/m mismatch.

Three things in the usual advice are wrong for these meshes specifically:
· "Bridge Edge Loops" needs matching vertex counts on both boundary loops. A TRELLIS cut edge is
  arbitrary unstructured soup, so bridging to a clean hand needs manual retopo — that step is
  where the evening goes.
· "Ctrl+P > With Automatic Weights" on the joined mesh re-weights the WHOLE character, destroying
  the skinning Mixamo just produced.
· "Ctrl+J the two armatures" collides every mixamorig bone name; Blender suffixes the newcomer
  .001 and the weights silently point at bones that no longer exist. That is the same failure
  that left a harvested garment frozen in bind pose earlier today.

So graft never cuts. It MASKS the body geometry near the target bone (vertex group + inverted
MASK modifier — reversible, and no boundary loop required), aligns the part with a single matrix
that scales about its own bbox centre onto the bone, transfers weights from the body with the
same data_transfer `fit` uses, and parents to the EXISTING armature.

Two bugs found while testing, both mine:
· scaling the part's root objects compounded with the donor's own armature/empty transforms and
  flung the part far enough to blow out the scene bbox (×111 instead of ×11). Now one matrix
  applied to the meshes themselves, after detaching them from the donor hierarchy world-position-safe.
· after bpy.data.objects.remove(), even reading o.name on a dropped object raises
  ReferenceError: StructRNA has been removed. Names are now resolved before removal.

Also: a "part" file is often a whole donor character — character_kit/rigged/hand1.glb holds a
prop, a full body AND a widget — so --mesh=<substr> picks the mesh and the op PRINTS what the
file contained and what it chose, rather than silently grafting a torso.

Runs on ultra per John's steer; this laptop's Blender is crash-prone (it segfaulted on me earlier
in this session).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 23:30:27 +10:00
type-two
305fbf9447 Phase 5: the dressing room — thumbnails, a manifest, and the rig/LOD/dress bench on one page
The wardrobe had grown past the point where a list of filenames is usable: 20 bodies and a
garments list you had to read rather than look at. Three things close that.

THUMBNAILS. New `thumb` blender op — fixed front ortho camera, EEVEE, transparent film, bind
pose (a thumb caught mid-clip is unreadable), framed on the real mesh with helper widgets
excluded or a bone widget decides the crop. /thumb?p= renders lazily and caches on a hash of
the source path. 2D layers skip Blender entirely and downscale through PIL — 12ms vs a process
launch. First paint of a fresh grid is slow because every 3D card spawns a render; after that
they're cached. Worth knowing before it looks broken.

MANIFEST (library/index.json, written atomically so a half-write can't blank the wardrobe).
Canonical slots and a layer int that drives BOTH the 2D doll stack and 3D ordering, so a
garment sorts the same in either product. Slots are GUESSED from the filename as a convenience
and the guess is marked as such — the UI shows `head?` with a "confirm it" note, and setting a
slot explicitly clears the inferred flag. A guess presented as fact is how a library quietly
fills with wrong data. Props (boombox, camera, thermos) correctly come back unset rather than
being forced into a garment slot.

THE BENCH, in the order you actually work: fix units -> rig (MIRPAMO) -> LOD -> dress. LOD is
four preset buttons at the tiers the games budget for (hero 60k / npc 18k / crowd 6k / tiny 2k)
instead of typing tri counts. /api/rig shells out to MIRPAMO, and the UI states its real
limitation up front: 22 bones, no fingers — fine for locomotion, but a 65-bone donor garment
will leave cuffs under-driven, which is exactly the mismatch that bit us in Phase 4.

Grid, slot tabs with counts, search over name+tags, metadata editor, LOD presets and the bench
buttons all verified in-browser with no JS errors. nsfw tagging from the previous commit carries
through: tagged assets stay hidden behind the toggle and out of the grid.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 20:25:44 +10:00
type-two
037f6ef0af Fold NPCFACTORY in: vendor three.js, take the banks, and fix units without flattening body types
NPCFACTORY and wardrobegod were the same product — wardrobegod had already absorbed its reskin
engine, and keeping two benches means two half-libraries and two export paths. Folded, keeping
the wardrobegod name and codebase (370 lines there vs ~1,100 here).

· Vendored three.js r175 from NPCFACTORY, replacing the unpkg CDN importmap. This was real
  drift, not tidiness: a CDN import breaks offline and can't be lifted into a game build.
  NPCFACTORY lacked OrbitControls (it used PointerLock), so that one addon was fetched at the
  matching revision. Added a guarded /vendor/ static route. Verified in-browser: zero CDN
  requests, 5 vendor files, model still loads.
· Took the 17 rigged walk-animated NPCs and 6 parts. Bodies 4 -> 20.
· New `unitfix` op. Deliberately NOT scale-to-height: a 0.06m human is a UNIT error, but
  normalising everything to 1.72m would erase the small/medium/large/obese range the library is
  meant to carry. So it only corrects heights outside 0.5-3.0m — physically impossible for a
  human — and leaves real proportions alone as data. Verified both ways: hum_character 0.0576m
  -> 1.72m, tradie 1.000m left untouched.

Three bugs found while building it, two of them pre-existing:
· `is_helper`/`real_meshes`/`bbox_of` factored out. Material-less bone widgets (a radius-1
  42-vert Icosphere, so exactly 2.0 units tall) were being measured INSTEAD of the character —
  every body reported 2.000m. This poisoned the `scale` op too, which has been measuring
  widgets all along; NPCFACTORY's render_plates.py had independently worked around the same
  thing by framing on the dominant mesh.
· `transform_apply` under temp_override(selected_editable_objects=...) SEGFAULTS Blender 5.1.2
  on rigs with parented children. A segfault can't be caught, so it's avoided rather than
  handled: glTF encodes node scale natively, so setting the root transform is sufficient and
  every downstream measurement still reads correctly. Confirmed `scale` still round-trips
  (1.00m -> 1.72m, re-measured).
· My own bulk edit replaced only ONE of the two crash-prone call sites and reported "replaced 1"
  — I didn't check for a second, which is why `scale` worked while `unitfix` kept crashing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 19:36:03 +10:00
type-two
8119e2de4c Phase 4: donor harvest — real cloth that bends, plus three assemble bugs it exposed
harvest op: split a garment off an already-rigged clothed character by material. Blender's
mesh.separate(type='MATERIAL') carries the vertex groups onto the separated piece, so the
garment lands ALREADY skinned to the donor's skeleton — no fit, no weight transfer, and it
deforms correctly at the elbow because it was authored to. Unused groups are purged after.

Triaged all 43 clothed donors on ultra (headless Blender, run where the files are rather than
moving 3 GB). Measured what the LoRA bench could only guess at: only 11/43 (25%) are separable.
The rest bake skin and cloth onto one fused atlas and cannot be split at all. The best targets
are the Ready Player Me avatars (Wolf3D_Outfit_Top/Bottom/Footwear — a clean 3-way split) and
the Mixamo characters (Topmat/Bottommat/Shoesmat); most of the man_*/woman_* set is fused.
Triage also over-reported at first: the skin regex missed 'mouth', so mouth materials showed up
as garments. Widened.

Proving it end to end exposed three real bugs in `assemble`, none of which erred — they all
failed silently, which is why they had survived:
· Bone names are NOT uniform across the fleet, contrary to the "one skeleton" contract in the
  README. character_kit and NPCFACTORY use 'mixamorig:Hips'; Ready Player Me donors use bare
  'Hips'; Blender suffixes '.001' on any import collision. Vertex groups therefore pointed at
  bones that did not exist and the garment sat frozen in bind pose while the body animated.
  Now matched on the bare name, so a garment harvested off one rig drives another.
· Donors are not authored at a common scale. character_kit rigs are ~0.06 m tall, a Ready
  Player Me donor ~1.5 m, so the harvested top arrived 27.41x oversized and swallowed the
  scene. Rescaled by a shared hips->head bone span, the same trick character_kit's
  assemble.fit_part uses.
· Material-less bone-widget meshes (a 42-vert 'Icosphere' in character_kit rigs) rode along and
  wrecked the scene bbox, which drives both the HUD height and the viewer's auto-framing — one
  stray widget framed the camera on the widget and rendered the character as a speck.
  NPCFACTORY's render_plates.py had already worked around the same thing.

Verified: a garment harvested from a Ready Player Me donor, assembled onto character_kit's
hum_character, deforming correctly mid-stride through a Running clip. Honest caveat unchanged —
the garment inherits its donor's proportions, so it reads loose on a slimmer body and there is
no cloth sim to save it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 19:07:32 +10:00
type-two
58354d9b66 WARDROBEGOD v1 — wardrobe generator bench (bodies, fits, garment gen pipeline)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 20:53:43 +10:00