86 lines
9.2 KiB
Markdown
86 lines
9.2 KiB
Markdown
|
|
# GLM advice (glm-5.2 via api.z.ai)
|
|||
|
|
|
|||
|
|
## 1. Weld+decimate 4.3M→1.5k: better approach
|
|||
|
|
|
|||
|
|
Collapse-decimating unwelded MD soup at ~3000:1 will eat pleats. Three concrete changes:
|
|||
|
|
|
|||
|
|
**(a) Reorder the pipeline: weld → fit (shrinkwrap high-poly) → decimate → bake.** Fitting before decimating lets Shrinkwrap use real pleat geometry; fitting a faceted 1500-tri proxy to a 25k body gives poor contact and lost silhouette. This is the single biggest fix to the plan.
|
|||
|
|
|
|||
|
|
**(b) Use Decimate in DISSOLVE (planar) mode for flat-panel parts**, not COLLAPSE. MD panels are nearly planar, so `DecimateModifier(decimate_mode='DISSOLVE', angle_limit=rad(5°))` merges coplanar faces and preserves panel boundaries + UVs far better than collapse. Use 8–10° for the bow. Collapse mode is only acceptable on truly curved regions.
|
|||
|
|
|
|||
|
|
**(c) For the pleated skirt, do not decimate the MD mesh — build a low-poly proxy.** Cylinder, 28–32 segments, 3–4 vertical loops, knee length, denser waistband ring. `ShrinkwrapModifier(wrap_mode='NEAREST_SURFACE', offset=0.002–0.003)` to the high-poly skirt. Bake normals high→low (see Q5). At 1500 tris, geometric pleats read as noise; normal-map pleats read as pleats.
|
|||
|
|
|
|||
|
|
Reject voxel remesh: destroys UVs, loses 6–8mm pleats at any feasible voxel resolution (you'd need ~0.5mm voxels = tens of millions). Reject Quad Remesher add-on: not headless-stable, license-bound. If you can re-export from MD, lower particle distance to 5–7mm and reduce pleat segment counts before sim — gives a 50–100k mesh that decimates cleanly.
|
|||
|
|
|
|||
|
|
Keep the high-poly around (hidden, excluded from export) for baking.
|
|||
|
|
|
|||
|
|
## 2. Skirt weights without extra bones
|
|||
|
|
|
|||
|
|
Nearest-vertex KDTree is the wrong tool — it snaps to the closer leg, giving a hard medial seam. Standard recipe is a **weight-transfer proxy**:
|
|||
|
|
|
|||
|
|
1. **Build a skirt cone**: duplicate Lena's body, keep pelvis→mid-thigh, bridge the leg gap into a tapered cylinder covering the skirt volume (waist ring at pelvis, knee ring at hem). Or extrude the pelvis ring downward to a knee-level ring scaled to skirt radius.
|
|||
|
|
2. **Weight the cone by Z-gradient**: top ring = copy body weights via `DataTransferModifier(vert_mapping='NEAREST_SURFACE', data_type='VGROUP_WEIGHTS')`. Bottom ring = 100% pelvis, 0% thighs. Blend linearly by world-Z between waist and hem. Add a small L/R thigh bias by X-sign so the hem still leans slightly with the stance leg.
|
|||
|
|
3. **Transfer cone→skirt**: `DataTransferModifier(vert_mapping='NEAREST_SURFACE', layers='ALL')`, apply, then `bpy.ops.object.vertex_group_smooth(repeat=3, factor=0.5)`.
|
|||
|
|
|
|||
|
|
This gives smooth L/R blend at the crotch (continuous cone surface) and pelvis-dominant hem. No hard seam. The Z-gradient is forward-compatible with future skirt bones — you can blend skirt-bone weights in by Z-band without redoing the transfer.
|
|||
|
|
|
|||
|
|
Fallback if you can't build the cone: per-vertex, compute distance to each bone segment (pelvis, thigh L, thigh R) with Gaussian falloff (σ≈15cm), normalize. Fast, scriptable, fuzzier than the cone.
|
|||
|
|
|
|||
|
|
## 3. Multi-layer shrinkwrap pitfalls
|
|||
|
|
|
|||
|
|
Three issues:
|
|||
|
|
|
|||
|
|
**(a) Offset vs. low-poly faceting.** At 1500 tris, facet normals deviate from the true surface; 6–8mm along vertex normals can still poke through at facet edges on curved regions (bust, scapula, glutes). Use `wrap_mode='NEAREST_SURFACE'` with offset, and keep the shrinkwrap **target** high-poly (original MD mesh or Lena's 25k body), not the decimated garment. Apply shrinkwrap before decimation (Q1).
|
|||
|
|
|
|||
|
|
**(b) Layer-to-layer wrapping.** Don't shrinkwrap the bow to the body — it'll sit inside the bodice. Stack: bow → bodice (offset = bodice thickness + 1mm), bodice → body (offset = 6–8mm). Or wrap all to body with cumulative offsets (body+6mm, body+10mm, body+14mm) — simpler to script but breaks if layers aren't concentric.
|
|||
|
|
|
|||
|
|
**(c) Self-intersection at boundaries.** Where bow meets bodice, low-poly faceting causes z-fighting. Add 1–2mm depth_bias per layer in Godot material, or ensure geometric offset exceeds the low-poly facet amplitude (~2mm at 1500 tris on a 30cm-radius curve).
|
|||
|
|
|
|||
|
|
Also: shrinkwrap inverts normals on concave regions (armpits, under-bust). After applying, run `bpy.ops.mesh.normals_make_consistent(inside=False)` and check for flipped faces.
|
|||
|
|
|
|||
|
|
## 4. Re-posing sleeves to T-pose (v2)
|
|||
|
|
|
|||
|
|
Two scripted options:
|
|||
|
|
|
|||
|
|
**(a) Rigid rotation (fast, OK for slight arms-down ~30–45°):** detect sleeve vertices by material or bbox (|X| beyond shoulder ± threshold). Shoulder pivot = Lena's `upper_arm_L/R` bone head. Build rotation matrix: axis = forward (Y), angle = -(current_arm_angle − 90°). Apply via `mesh.transform(matrix, vertex_group='sleeve_L')`. Fine if sleeves are near-cylindrical; test the cap — if it kinks, use (b).
|
|||
|
|
|
|||
|
|
**(b) 2-bone armature per sleeve (cleaner):** create a 3-bone chain (shoulder→elbow→wrist) per arm along the sleeve's medial axis (PCA on sleeve vertices, or sample). Parent sleeve vertices to nearest bone with `bpy.ops.object.parent_set(type='ARMATURE_AUTO')` restricted to the sleeve vertex group. Rotate elbow+wrist to bring cuff to T-pose height. `bpy.ops.pose.armature_apply(selected=False)`, apply armature modifier. Preserves sleeve length and cap shape.
|
|||
|
|
|
|||
|
|
For "slightly arms-down", (a) is ~10 lines of script. For full A-pose or reaching, use (b). Pitfall: if the jacket torso has shoulder wrinkles from the arms-down pose, rigid rotation leaves them — they'll bake into the normal map, which may be fine.
|
|||
|
|
|
|||
|
|
## 5. Baking normals/AO at 1.5k tris
|
|||
|
|
|
|||
|
|
Worth it. At 1500 tris, geometric pleats are noise; normal-map pleats read as pleats. AO grounds the garment. Do it.
|
|||
|
|
|
|||
|
|
Workflow notes for MD unwelded sources:
|
|||
|
|
|
|||
|
|
- **UVs**: MD gives per-panel islands (one per pattern piece). After welding they're still clean — good. But decimated low-poly UVs will distort. **Create a second UV map** ("bake_uv") on the low-poly, `bpy.ops.uv.smart_project(angle_limit=1.15)`, bake to that. Keep original MD UVs as UV1 for future texture work. Godot 4.7 supports 2 UV channels.
|
|||
|
|
- **Normals**: MD unwelded output has inconsistent normals across seams. On the high-poly: `mesh.normals_make_consistent(inside=False)`, then `mesh.smooth_normals()`. Bake `type='NORMAL'`, `space='TANGENT'`, `use_selected_to_active=True`, `cage_extrusion=0.0005–0.001` (0.5–1mm). Cycles.
|
|||
|
|
- **AO**: bake separately, `type='AO'`, `samples=64`. Multiply in shader at 0.5–0.7. Or bake Combined with world light disabled for AO+curvature in one texture.
|
|||
|
|
- **Layer bleed**: multi-layer cloth — bake each layer separately with others hidden, or cage_extrusion < layer separation. MD layers are 2–5mm apart; use cage_extrusion=1mm.
|
|||
|
|
- **Color**: flat MD material colors → assign directly as base color on the low-poly material. No bake needed. One material per part: base color + normal map + AO multiply.
|
|||
|
|
|
|||
|
|
Texture size: 512² for bodice/bow, 512×1024 for skirt (pleats need vertical resolution). 1500-tri parts don't justify 2K.
|
|||
|
|
|
|||
|
|
## 6. Overall plan critique
|
|||
|
|
|
|||
|
|
Do differently:
|
|||
|
|
|
|||
|
|
**(a) Reorder: fit before decimate, bake after decimate.** Current plan decimates then fits. Pipeline should be: split → weld → (build proxy for skirt) → fit high-poly to body → decimate → bake high→low → weight transfer → export. This is the biggest issue.
|
|||
|
|
|
|||
|
|
**(b) Per-part tri budget should vary.** 1500 for bow is wasteful; 1500 for skirt is starving. Suggest: bow 300–500, bodice 1000–1200, skirt 2000–2500. If 1500 is a hard total-per-character crowd budget, state it as a total and allocate.
|
|||
|
|
|
|||
|
|
**(c) Poke-through is under-addressed.** 6–8mm offset on 1500-tri garments in walk/dance will poke at crotch, armpits, under-bust, and back of skirt during forward leg swing. Build an **inner shell**: a 3–5mm inflated copy of Lena's body; shrinkwrap garments to the shell, not the body. Test in the in-game test bed immediately after weight transfer, not at the end.
|
|||
|
|
|
|||
|
|
**(d) Weight transfer: use Data Transfer (Nearest Surface), not KDTree nearest-vertex, for body-conforming parts.** KDTree is fine for the snug bodice but fails on overhangs (collar, bow tails) where the nearest body vertex is on the wrong side. KDTree is also non-deterministic at equidistant vertices — add a lowest-index tiebreak if you keep it.
|
|||
|
|
|
|||
|
|
**(e) Shrinkwrap mask must be scripted.** Build the "fit influence" vertex group from world-Z relative to pelvis bone head: 1.0 above waist, linear ramp to 0.0 at hip-crotch height. Don't hand-paint in a headless pipeline.
|
|||
|
|
|
|||
|
|
**(f) Missing: LOD strategy.** 1500 tris is LOD0 for a hero, LOD1 for crowd. If crowd-scale, plan LOD2 (400–500 tris) with aggressive decimate + no normal map. Mention now even if deferred.
|
|||
|
|
|
|||
|
|
**(g) Missing: determinism audit.** KDTree, Smart UV Project, and auto-weights have non-deterministic corners. Pin `bpy.context.scene.cycles.seed` and `random.seed`, and add a regression check: hash exported GLB vertex positions; fail the pipeline if the hash changes between runs on the same input.
|
|||
|
|
|
|||
|
|
**(h) 12 materials → 3 parts: verify contiguity.** MD sometimes assigns multiple materials to one panel (trim + shell). After splitting, check `bmesh` connectivity; if a "part" has >1 connected component, merge materials or split further.
|
|||
|
|
|
|||
|
|
**(i) Test bed timing.** After weight transfer, do a quick GLB round-trip into Godot with the walk cycle before finalizing bake/export. Catch tearing early — don't wait until the end.
|