feat: clothing lane, character sources, and DCC bridges
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>
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
# Stage 35 (read-only): does the approved concept turnaround describe OUR body?
|
||||
#
|
||||
# blender --background --python 35_silhouette.py -- <concept_front.png> <our_front.png>
|
||||
#
|
||||
# This decides how the turnaround can be used. If the silhouettes match, the concept renders could
|
||||
# be projected onto the mesh as a texture source. If they do not, they are a style and tone
|
||||
# reference only — and quietly projecting them would smear one body's paint onto another's shape.
|
||||
# Widths are normalised by figure height, so framing and resolution cancel.
|
||||
import bpy, sys, os
|
||||
import numpy as np
|
||||
|
||||
argv = sys.argv[sys.argv.index("--") + 1:]
|
||||
|
||||
|
||||
def load(p):
|
||||
im = bpy.data.images.load(p)
|
||||
w, h = im.size
|
||||
b = np.empty(w * h * 4, dtype=np.float32)
|
||||
im.pixels.foreach_get(b)
|
||||
a = b.reshape(h, w, 4)[:, :, :3].copy()
|
||||
bpy.data.images.remove(im)
|
||||
return a[::-1]
|
||||
|
||||
|
||||
def profile(p):
|
||||
A = load(p)
|
||||
sat = A.max(axis=2) - A.min(axis=2)
|
||||
m = (sat > 0.06) & (A.max(axis=2) > 0.15)
|
||||
ys, xs = np.nonzero(m)
|
||||
y0, y1 = ys.min(), ys.max()
|
||||
Hf = float(y1 - y0)
|
||||
cx = xs.mean()
|
||||
out = []
|
||||
for t in np.arange(0.02, 1.0, 0.02):
|
||||
r = int(round(y0 + t * Hf))
|
||||
row = np.nonzero(m[r])[0]
|
||||
if len(row) < 2:
|
||||
out.append((t, np.nan, np.nan))
|
||||
continue
|
||||
# full extent (includes arms where they are out), and the CENTRAL run (the torso/leg)
|
||||
full = (row.max() - row.min()) / Hf
|
||||
# central run: walk out from the pixel nearest the body's x centre
|
||||
k = row[np.argmin(np.abs(row - cx))]
|
||||
lo = k
|
||||
while lo - 1 in set(row.tolist()) if False else (lo - 1 >= 0 and m[r, lo - 1]):
|
||||
lo -= 1
|
||||
hi = k
|
||||
while hi + 1 < m.shape[1] and m[r, hi + 1]:
|
||||
hi += 1
|
||||
out.append((t, full, (hi - lo) / Hf))
|
||||
return np.array(out), Hf
|
||||
|
||||
|
||||
A, ha = profile(argv[0])
|
||||
B, hb = profile(argv[1])
|
||||
print(f"concept figure height {ha:.0f} px ours {hb:.0f} px")
|
||||
print("\n t concept_full ours_full concept_core ours_core core_diff")
|
||||
diffs = []
|
||||
for (t, fa, ca), (_, fb, cb) in zip(A, B):
|
||||
if np.isnan(ca) or np.isnan(cb):
|
||||
continue
|
||||
d = cb - ca
|
||||
# the central run is only meaningful where it is a single body part:
|
||||
# below the arms (t>0.30) and above the ankles
|
||||
flag = ""
|
||||
if 0.30 < t < 0.95:
|
||||
diffs.append(d)
|
||||
if abs(d) > 0.02:
|
||||
flag = " <-- differs"
|
||||
print(f" {t:.2f} {fa:9.3f} {fb:9.3f} {ca:10.3f} {cb:9.3f} {d:+8.3f}{flag}")
|
||||
if diffs:
|
||||
diffs = np.array(diffs)
|
||||
print(f"\nCORE WIDTH (torso/legs, t 0.30-0.95), ours minus concept, as fraction of height:")
|
||||
print(f" mean {diffs.mean():+.4f} mean|d| {np.abs(diffs).mean():.4f} "
|
||||
f"max|d| {np.abs(diffs).max():.4f}")
|
||||
print(f" bands differing by >2% of height: {int((np.abs(diffs)>0.02).sum())} of {len(diffs)}")
|
||||
print("SILHOUETTE_DONE")
|
||||
Reference in New Issue
Block a user