3ba86b2ea8
Bulk import of the working lanes that were living untracked on the PC. Content: - characters/ Lena/male body lanes, bakes, texture work, run logs - clothing/ garment pipeline, configs, gates, contract docs - garments/ MD-authored garment sources (.zprj/.zpac) - UAL-Lib/ Universal Animation Library 2 source (.blend/.fbx/.glb) - tools/ blender_bridge, iclone_bridge, md_bridge, tailor, glm_agent - docs/, plans/, dev/, .agents/plans/ Repo hygiene: - .gitattributes: LFS now covers .blend, .zprj, .zpac, .obj, .npy and the Reallusion .iAvatar/.ccAvatar/.ccRestore containers. Without this the ~3.8 GB in this commit would land as raw blobs. .png/.jpg are left out on purpose — ~250 are already tracked raw and converting them would rewrite every one without shrinking history. - .gitignore: exclude /accurig/ (~1 GB AccuRig program files, redistributable from Reallusion, nothing authored here) and /dev/null/ (git-lfs hook copies dropped by a `>/dev/null` redirect on Windows). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
148 lines
7.3 KiB
Python
148 lines
7.3 KiB
Python
# Piupiu v3 — re-cut for leg clearance.
|
|
#
|
|
# python tools/md_bridge.py --file tools/tailor/md_piupiu_v3.py --timeout 600
|
|
#
|
|
# WHY v3 (v2 stays untouched; backup in tools/tailor/backup/2026-07-31-pre-v3/):
|
|
# v2's waist edge is 500 mm/panel = 1000 mm total, against Lena's 1095 mm hips —
|
|
# cut TIGHTER than the hip it must pass over, so it drapes as a taut pencil wrap
|
|
# on the thighs. Downstream measures the result: G3 finds 8 mm penetration AT
|
|
# REST, and G5 finds 216-231 verts up to 8.5 cm deep across Idle/Walk, because
|
|
# `weights: "dress"` leaves the sub-hip skirt pelvis-only — the thigh swings, the
|
|
# skirt does not.
|
|
#
|
|
# WHAT THIS CHANGES vs md_piupiu.py:
|
|
# 1. Waist cut to hip + ease (570/panel = 1140 mm) instead of hip - 95 mm, so the
|
|
# skirt is not stretched over the hips, and waist ELASTIC holds it up instead
|
|
# of tension. Doctrine: elastic mid-settle, never from frame 0.
|
|
# 2. Hem flared 1640 -> 2300 mm circumference for standoff at the knee.
|
|
# 3. Additional Thickness - Collision (10 mm) so MD physically resolves the cloth
|
|
# standing off the skin. This is strictly better than the downstream `fit`
|
|
# normal-push (shell_mm 4 / offset_mm 3), which self-intersects in concave
|
|
# regions — i.e. exactly between Lena's touching thighs.
|
|
#
|
|
# HONEST LIMIT: a knee-length pelvis-locked skirt cannot be fixed by clearance
|
|
# alone. Hem target 0.45 m sits 0.495 m below the hip pivot, so a 30 deg hip
|
|
# flexion sweeps the leg ~25 cm forward — more than any believable piupiu
|
|
# silhouette clears. This buys the runtime skirt-bone capsules (`weights:
|
|
# "skirt_bones"`, the concurrent session's lane) room to work, and it should
|
|
# eliminate the rest-pose failure outright. It is not a solo fix.
|
|
#
|
|
# QC targets (unchanged, tools/tailor/qc_placement.py verifies):
|
|
# waistband top 1.05 m +-3 cm, hem 0.45 m +-4 cm (just below knee)
|
|
# G1 measured v2 at +8 cm / +14 cm off these, so v3 should improve placement too.
|
|
import os
|
|
import tempfile
|
|
|
|
# ── parameters — the whole point of this block is one-edit iteration ──────────
|
|
W_TOP = 570.0 # mm per panel at the waist. x2 = 1140 = hip 1095 + 45 ease.
|
|
W_HEM = 1150.0 # mm per panel at the hem. x2 = 2300 (v2 was 1640).
|
|
HEIGHT = 600.0 # waist 1.05 m -> hem 0.45 m.
|
|
COLLISION_MM = 10.0 # Additional Thickness - Collision. 0 disables.
|
|
ELASTIC_TOTAL = 360.0 # mm per panel to cinch the waist edge to. x2 = 720 vs
|
|
# measured waist 675. None disables elastic.
|
|
ZFAB = r"C:\Users\Public\Documents\MarvelousDesigner\New Assets\Fabric\(Default for Simulation).zfab"
|
|
# If the flare collapses into vertical folds, swap to a stiff woven — a real
|
|
# piupiu is dried flax strands and holds a bell. Stiffest stock presets:
|
|
# V2_Woven_Canvas_1.zfab < V2_Woven_Denim_1.zfab < V2_Non-Fabric_Tyvek_1.zfab
|
|
EXPORT = False # flip to True only once a snapshot looks right.
|
|
VERSION = "v3"
|
|
|
|
AVATAR_FBX = r"C:\Users\Jeremy\tinqs\animation\tools\tailor\avatar\Lena_QuatSkin_Avatar.fbx"
|
|
TEX = r"C:\Users\Jeremy\tinqs\animation\tools\tailor\textures\piupiu.png" # dpi 43.3 = 600 mm
|
|
OUT = r"C:\Users\Jeremy\tinqs\animation\tools\tailor"
|
|
SHOTS = r"C:\Users\Jeremy\tinqs\animation\tools\tailor\screenshots"
|
|
|
|
report = {"params": {"W_TOP": W_TOP, "W_HEM": W_HEM, "HEIGHT": HEIGHT,
|
|
"COLLISION_MM": COLLISION_MM, "ELASTIC_TOTAL": ELASTIC_TOTAL,
|
|
"zfab": os.path.basename(ZFAB), "EXPORT": EXPORT}}
|
|
|
|
utility_api.NewProject() # NB: this deletes the avatar — import after.
|
|
op = ApiTypes.ImportExportOption()
|
|
op.scale = 10.0 # Blender-exported FBX avatars import 10x small.
|
|
op.bAddArrangementPoints = True # without this there is nowhere to hang cloth.
|
|
op.bAutoTranslate = True
|
|
report["avatar"] = import_api.ImportFBX(AVATAR_FBX, op)
|
|
|
|
# Trapezoid, hem at y=0 and waist at y=HEIGHT: 2D y+ maps to UP in 3D. Drafting
|
|
# y-down drapes the garment upside-down over the head.
|
|
# Line indices that follow from this point order: 0 = waist, 1 = side, 2 = hem,
|
|
# 3 = side. Side seams pair the SAME index on both panels with (False, False).
|
|
def panel(dx):
|
|
inset = (W_HEM - W_TOP) / 2.0
|
|
pts = [(inset, HEIGHT), (inset + W_TOP, HEIGHT), (W_HEM, 0.0), (0.0, 0.0)]
|
|
return pattern_api.CreatePatternWithPoints([(x + dx, y, 0) for (x, y) in pts])
|
|
|
|
sf = panel(0.0)
|
|
sb = panel(W_HEM + 400.0)
|
|
report["ids"] = [sf, sb]
|
|
pattern_api.AddSeamlinePairGroup(sf, 1, sb, 1, False, False)
|
|
pattern_api.AddSeamlinePairGroup(sf, 3, sb, 3, False, False)
|
|
|
|
fab = fabric_api.AddFabric(ZFAB) # never reuse index 0
|
|
fabric_api.SetBaseTextureMapImageGivenFilePath(TEX, fab) # path is arg0
|
|
for p in (sf, sb):
|
|
pattern_api.SetPatternPieceFabricIndex(p, fab)
|
|
|
|
# Collision thickness before the drape, so the settle resolves with it.
|
|
if COLLISION_MM:
|
|
try:
|
|
for p in (sf, sb):
|
|
pattern_api.SetAddlThicknessCollision(p, COLLISION_MM)
|
|
report["collision"] = [pattern_api.GetAddlThicknessCollisionValue(p)
|
|
for p in (sf, sb)]
|
|
except Exception as e:
|
|
report["collision_error"] = "%s: %s" % (type(e).__name__, e)
|
|
|
|
arr = {a["ArrangementName"]: int(a["ArrangementIndex"])
|
|
for a in pattern_api.GetArrangementList()} # indices regenerate per import
|
|
pattern_api.SetArrangement(sf, arr["Body_Front_Waist"])
|
|
pattern_api.SetArrangement(sb, arr["Body_Back_Waist"])
|
|
pattern_api.SetArrangementPosition(sf, 50, 30, 50)
|
|
pattern_api.SetArrangementPosition(sb, 0, 30, 50)
|
|
|
|
pattern_api.SetPatternStrengthen(sf, True) # stiff so cloth wraps, not crumples
|
|
pattern_api.SetPatternStrengthen(sb, True)
|
|
utility_api.ResetClothArrangement() # SetArrangement only assigns
|
|
|
|
# The diagnostic that unsticks everything: where the panels actually start, before
|
|
# physics muddies it. Zero simulation frames.
|
|
utility_api.Refresh3DWindow()
|
|
utility_api.SetCamViewPoint(2) # front view, comparable shots
|
|
pre = os.path.join(tempfile.gettempdir(), "tinqs_md_piupiu_%s_presim.png" % VERSION)
|
|
export_api.ExportSnapshot3D(pre)
|
|
report["presim_snap"] = pre
|
|
|
|
report["sim1"] = utility_api.Simulate(250) # main settle, still stiff
|
|
|
|
# Elastic mid-settle: applied before the cloth has wrapped it cinches off one hip.
|
|
if ELASTIC_TOTAL:
|
|
try:
|
|
for p in (sf, sb):
|
|
pattern_api.SetPatternPieceElastic(p, 0, True)
|
|
pattern_api.SetPatternPieceElasticTotalLength(p, 0, ELASTIC_TOTAL)
|
|
report["sim_elastic"] = utility_api.Simulate(80)
|
|
except Exception as e:
|
|
report["elastic_error"] = "%s: %s" % (type(e).__name__, e)
|
|
|
|
pattern_api.SetPatternStrengthen(sf, False) # relax into natural folds
|
|
pattern_api.SetPatternStrengthen(sb, False)
|
|
report["sim2"] = utility_api.Simulate(50)
|
|
utility_api.Refresh3DWindow()
|
|
|
|
utility_api.SetCamViewPoint(2)
|
|
snap = os.path.join(SHOTS, "lena_piupiu_%s.png" % VERSION)
|
|
export_api.ExportSnapshot3D(snap) # required repo review render
|
|
report["snap"] = snap
|
|
|
|
if EXPORT:
|
|
base = os.path.join(OUT, "lena_piupiu_%s" % VERSION)
|
|
eop = ApiTypes.ImportExportOption()
|
|
eop.bExportGarment = True
|
|
eop.bExportAvatar = False
|
|
report["zprj"] = export_api.ExportZPrj(base + ".zprj")
|
|
report["fbx"] = export_api.ExportFBX(base + "_garment.fbx", eop)
|
|
report["obj"] = export_api.ExportOBJ(base + "_garment.obj", eop)
|
|
report["zpac"] = export_api.ExportZPac(base + ".zpac")
|
|
|
|
result = report
|