Track C: Codex with GPT-6.1-Terra end to end
agent (cont.)
+0:25exec_commandsource ~/Projects/lowmarch-bench/kit/env-c.sh && cat "$KIT/variant_build.py" && sed -n '725,825p' tools/vroid_portrait.py && sed -n '130,170p' tools/vrm.py (+2 more)
command
source ~/Projects/lowmarch-bench/kit/env-c.sh && cat "$KIT/variant_build.py" && sed -n '725,825p' tools/vroid_portrait.py && sed -n '130,170p' tools/vrm.py source ~/Projects/lowmarch-bench/kit/env-c.sh && sed -n '300,570p' review/vroid-02/index.html && sed -n '780,885p' review/vroid-02/index.html && sed -n '1330,1365p' review/vroid-02/index.html source ~/Projects/lowmarch-bench/kit/env-c.sh && rg -n 'rest[=:]|Fcl_ALL|Fcl_BRW|Fcl_MTH' tools review docs | head -160 && ls -la && rg --files | head -100
result, 696 lines
Script completed Wall time 0.5 seconds Output: Warning: truncated output (original token count: 10851) Total output lines: 732 """Build armory characters from the worktree in $LOWMARCH_REPO into a site dir, with an expression baked in. python3 variant_build.py SITE_COPY forward=0.13 pull=0.5 hair=0.0 NAME [NAME...] SITE_COPY is a copy of the built armory site (with the patched sheet rows); each NAME's .glb.json is replaced. """ import functools import os import sys os.environ["LOWMARCH_SITE"] = "armory" REPO = os.environ["LOWMARCH_REPO"] # the track's worktree sys.path[:0] = [os.path.join(REPO, "review", "vroid-02"), os.path.join(REPO, "tools"), os.path.join(REPO, "art", "cast")] import build # noqa: E402 import vroid_portrait as vp # noqa: E402 site = sys.argv[1] kw = dict(a.split("=") for a in sys.argv[2:] if "=" in a) names = [a for a in sys.argv[2:] if "=" not in a] hair = float(kw.pop("hair", 0.0)) rest = kw.pop("rest", None) # rest=Fcl_ALL_Joy:1,Fcl_MTH_A:0.5 (VRoid expression shapes) if rest is not None: for n in names: build.CAST[n]["shape"] = dict(build.CAST[n]["shape"], rest={} if rest == "none" else {k: float(v) for k, v in (kv.split(":") for kv in rest.split(";"))}) skin = kw.pop("skin", None) # skin=<tone in cast.SKINS> if skin is not None: import cast for n in names: build.CAST[n]["paint"] = dict(build.CAST[n].get("paint") or {}) if cast.SKINS[skin]: build.CAST[n]["paint"]["skin"] = cast.SKINS[skin] else: build.CAST[n]["paint"].pop("skin", None) if "marks" in kw: # marks=nose_dy,ry,rx/lip_dy,ry,rx/neck nose, lip, neck = kw.pop("marks").split("/") vp.SHADE_MARKS = dict(nose=tuple(map(float, nose.split(","))), lip=tuple(map(float, lip.split(","))), neck=float(neck)) vp.face_normals = functools.partial(vp.face_normals, **{k: float(v) for k, v in kw.items()}) vp.NEUTRAL["hair_normals"] = hair build.OUT = site build.GEOM = os.path.join(site, "_geom") os.makedirs(build.GEOM, exist_ok=True) import json cam_path = os.path.join(site, "camera.json") cam = json.load(open(cam_path)) for n in names: res = build.character((n, None)) for c in cam["characters"]: # the page reads paint, roles and cameras from camera.json if c["name"] == n: c.update(info=res["info"], cams=res["cams"]) print(n, res["size"]) json.dump(cam, open(cam_path, "w")) q[:, :2] *= sh["eye"] q[:, 1] += sh["eye_tilt"] * np.sign(c[0]) * q[:, 0] * 0.4 P[side] = c + q class Portrait: def __init__(self, path, morphs=None, pose=BUST_POSE, shape=None, hair_from=None, costume=None, mouth="curve", world=None, ref_pose=BUST_POSE, hair_trim=None): """mouth: how the closed mouth is drawn. 'texture' = VRoid's painted lip line, thickened; 'texture+lip' = that plus a lower-lip shade; 'drawn' = an FE8-style mouth between the projected mouth corners; 'none' = geometry only (the line disappears when closed).""" self.mouth = mouth self.v = VRM(path) sh = dict(NEUTRAL, **(shape or {})) mo = dict(sh["rest"]) for k_, w_ in (morphs or {}).items(): mo[k_] = min(1.0, mo.get(k_, 0.0) + w_) # everything is built in the reference pose (arms down), then posed through the skeleton target_pose = bone_pose(self.v, sh, pose) pose = bone_pose(self.v, sh, ref_pose) self.pose_now = target_pose self.pose_ref = pose self.shape = sh self.prims = self.v.primitives(mo, pose) if hair_from: # swap in another model's hair hv = VRM(hair_from) hp = hv.primitives(None, ref_pose) hroles = [role_of(p["material"]) for p in hp] src = landmarks(hp, hroles) dst = landmarks(self.prims, [role_of(p["material"]) for p in self.prims]) k = (abs(dst["eyeR"][0] - dst["eyeL"][0]) / abs(src["eyeR"][0] - src["eyeL"][0]) + (dst["eye_y"] - dst["chin"][1]) / (src["eye_y"] - src["chin"][1])) / 2 c_src = (src["eyeL"] + src["eyeR"]) / 2 c_dst = (dst["eyeL"] + dst["eyeR"]) / 2 hair = [p for p in hp if "_HAIR" in p["material"]] for p in hair: p["pos"] = c_dst + (p["pos"] - c_src) * k n_ = len(p["pos"]) # rides our head bone from here on p["J"], p["W"], p["jnames"] = np.zeros((n_, 4), np.int64), np.tile([1.0, 0, 0, 0], (n_, 1)), ["J_Bip_C_Head"] self.prims = [p for p in self.prims if "_HAIR" not in p["material"]] + hair self._hair_v = hv if hair_trim: # drop hair pieces reaching past |x| = hair_trim (e.g. twin tails) self.prims = [p for p in self.prims if "_HAIR" not in p["material"] or np.abs(p["pos"][np.unique(p["tri"]), 0]).max() <= hair_trim] self.roles = [p.get("role") or role_of(p["material"]) for p in self.prims] self.lm = landmarks(self.prims, self.roles) if costume: # replace VRoid's clothes with ours import outfit fit = outfit.Fit(self.v, pose, self.prims, self.roles) import rig fit.grip = rig.grip_points(self.v, sh, ref_pose) # where each fist closes (weapons sit there) keep_ = [k for k, r in enumerate(self.roles) if r != "cloth"] self.prims = [self.prims[k] for k in keep_] self.roles = [self.roles[k] for k in keep_] costume = outfit.COSTUMES[costume] if isinstance(costume, str) else costume fit.keep_skin = () # a costume may leave skin bare (fit.keep_skin = bones) for pc in costume(fit): self.prims.append(pc) self.roles.append(pc["role"]) # body skin below the neck is always under clothes in a bust; drop it so it can't poke through cy = self.lm["chin"][1] from vrm import bone_weight for p, r in zip(self.prims, self.roles): if r != "body": continue if costume: # keep head, neck and upper chest (the throat shows in the collar opening) wv = bone_weight(p, ("Head", "Neck", "UpperChest") + tuple(getattr(fit, "keep_skin", ()))) p["tri"] = p["tri"][(wv[p["tri"]] >= 0.5).all(1)] if getattr(fit, "drop_skin", None) is not None: # a costume may hide the skin it covers p["tri"] = p["tri"][~fit.drop_skin(p)[p["tri"]].all(1)] else: T = p["pos"][p["tri"]] head = T[:, :, 1].min(1) > cy - 0.02 neck = (T[:, :, 1].min(1) > cy - 0.14) & (np.abs(T[:, :, 0]).max(1) < 0.055) p["tri"] = p["tri"][head | neck] for pc in world or []: # scenery: does not turn with the character self.prims.append(pc) self.roles.append(pc["role"]) # framing uses the unshaped face at the base head size, so head-size changes still show self.span = (self.lm["eye_y"] - self.lm["chin"][1]) / sh["head"] reshape(self.prims, self.roles, self.lm, sh) strip_blush(self.prims, self.roles, self.v, sh["blush"]) face_lines(self.prims, self.roles, self.v, sh["age_lines"]) lm_now = landmarks(self.prims, self.roles) # after the sculpt names, Wn = self.v.nodes_by_name(), self.v.node_world(pose) neck = (Wn[names["J_Bip_C_Neck"]][:3, 3], Wn[names["J_Bip_C_Head"]][:3, 3]) \ if "J_Bip_C_Neck" in names and "J_Bip_C_Head" in names else None face_normals(self.prims, self.roles, lm_now, sh["face_normals"], sh["hair_normals"], neck=neck) for pc, rl in shadow_cards(self.prims, self.roles, lm_now, sh["shadow_cards"], neck): self.prims.append(pc) self.roles.append(rl) if pose is not target_pose and any(not np.allclose(pose.get(k_, np.eye(4)), target_pose.get(k_, np.eye(4))) for k_ in set(pose) | set(target_pose)): repose(self.v, [p for p in self.prims if not p.get("world")], pose, target_pose) names = self.v.nodes_by_name() Wn = self.v.node_world() self.eye = (Wn[names["J_Adj_L_FaceEye"]][:3, 3] + Wn[names["J_Adj_R_FaceEye"]][:3, 3]) / 2 self.chin_y = self.lm["chin"][1] self.tex = {} for k, p in enumerate(self.prims): if self.roles[k] in ("line", "brow", "iris", "highlight", "white", "hair", "mouth"): out.append(np.stack([posed[j] @ ibm[k] for k, j in enumerate(sk["joints"])])) return out def primitives(self, morphs=None, pose=None): """List of dicts: mesh, material (name), mi, pos (N,3), nrm (N,3), uv (N,2), tri (M,3). morphs: {target name substring: weight} applied to meshes that have targets. pose: {bone name: 3x3 rotation} -> linear blend skinning.""" out = [] mesh_skin = {n["mesh"]: n.get("skin") for n in self.j["nodes"] if "mesh" in n} jm = self.skin_matrices(pose) if pose else None for mi_, mesh in enumerate(self.j["meshes"]): names = mesh.get("extras", {}).get("targetNames") or \ mesh["primitives"][0].get("extras", {}).get("targetNames") or [] for pr in mesh["primitives"]: at = pr["attributes"] pos = self.accessor(at["POSITION"]).astype(np.float64).copy() nrm = self.accessor(at["NORMAL"]).astype(np.float64) if "NORMAL" in at else np.zeros_like(pos) uv = self.accessor(at["TEXCOORD_0"]).astype(np.float64) if "TEXCOORD_0" in at else np.zeros((len(pos), 2)) if morphs and pr.get("targets"): for key, wgt in morphs.items(): for ti, tn in enumerate(names): if tn.endswith(key) and "POSITION" in pr["targets"][ti]: pos += wgt * self.accessor(pr["targets"][ti]["POSITION"]) J = Wt = None sk = mesh_skin.get(mi_) if sk is not None and "JOINTS_0" in at: J = self.accessor(at["JOINTS_0"]).astype(np.int64) Wt = self.accessor(at["WEIGHTS_0"]).astype(np.float64) if jm is not None: M = (jm[sk][J] * Wt[:, :, None, None]).sum(1) # (N,4,4) pos = np.einsum("nij,nj->ni", M[:, :3, :3], pos) + M[:, :3, 3] nrm = np.einsum("nij,nj->ni", M[:, :3, :3], nrm) tri = self.accessor(pr["indices"]).reshape(-1, 3).astype(np.int64) mi = pr.get("material", 0) d = dict(mesh=mesh.get("name"), material=self.j["materials"][mi]["name"], mi=mi, pos=pos, nrm=nrm, uv=uv, tri=tri) if J is not None: d.update(J=J, W=Wt, jnames=[self.j["nodes"][n].get("name", "") for n in self.j["skins"][sk]["joints"]]) out.append(d) return out // ---- scene and camera ----------------------------------------------------------------------------------------------- const scene = new THREE.Scene(); const hemi = new THREE.HemisphereLight(0xfff6ee, 0x3a3450, 1.35); const sun = new THREE.DirectionalLight(0xffffff, 1.6); scene.add(hemi, sun); const camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0.1, 40); // ---- shaders -------------------------------------------------------------------------------------------------------- const SKIN_HEAD = `#include <common> #include <skinning_pars_vertex>`; const SKIN_BODY = `#include <beginnormal_vertex> #include <skinbase_vertex> #include <skinnormal_vertex> #include <begin_vertex> #include <skinning_vertex>`; const PAINT_VS = `${SKIN_HEAD} attribute float edge; attribute vec4 _plate; varying vec3 vN; varying vec2 vUv; varying vec3 vView; varying vec3 vObj; varying float vEdge; varying vec4 vPlate; void main() { ${SKIN_BODY} vN = normalize(normalMatrix * objectNormal); vUv = uv; vObj = position; vEdge = edge; vPlate = _plate; vec4 mv = modelViewMatrix * vec4(transformed, 1.0); vView = -mv.xyz; gl_Position = projectionMatrix * mv; }`; const HSV = ` vec3 rgb2hsv(vec3 c) { vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); float d = q.x - min(q.w, q.y); return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + 1e-10)), d / (q.x + 1e-10), q.x); } vec3 hsv2rgb(vec3 c) { vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); }`; // painted renderer, after the Unicorn Overlord study: tight terminators, hue-shifted ramps built from // one base colour per material, hard reflection bands on steel, strand highlights on hair, a stage // grade and a back rim const PAINT_FS = ` uniform int uKind; uniform vec3 uBase; uniform vec3 uKey; uniform sampler2D uMap; uniform int uAlpha; uniform int uPass; uniform vec4 uInfo; uniform vec3 uEye[3]; uniform vec3 uFlat; uniform vec3 uSkinRef; uniform vec3 uGold; uniform vec3 uAmbient; uniform vec3 uKeyCol; uniform vec3 uRimCol; uniform float uGrain; uniform int uFaceTex; uniform int uDecal; uniform vec3 uMotif; uniform vec3 uBand; uniform vec3 uSkinTone; uniform int uShadeOnly; uniform int uSkinRamp; uniform vec3 uSkinL; uniform vec3 uSkinS; uniform vec3 uSkinC; uniform sampler2D tCastInfo; uniform sampler2D tCastDepth; uniform vec2 uCastRes; uniform int uCastOn; uniform float uCastNear; uniform float uCastFar; uniform float uCastPpm; // Cast shadows on skin, in screen space (the "depth offset" shadows of anime toon shaders): walk from this pixel // toward the key light across the info pass already drawn; if the face (the jaw) or hair stands in front of that // ray, this pixel is in its shadow. The shape is the caster's own outline as the camera sees it, at any angle. float castShadow() { vec2 uv = gl_FragCoord.xy / uCastRes, px = 1.0 / uCastRes; float z0 = uCastNear + texture2D(tCastDepth, uv).x * (uCastFar - uCastNear); float sxy = length(uKey.xy); if (sxy < 0.05) return 0.0; vec2 dir = uKey.xy / sxy; float lz = uKey.z / sxy; // the ray's rise toward the camera per metre across for (int s = 1; s <= 14; s++) { float dm = float(s) * 0.003; // 3 mm steps across the screen, up to 4.2 cm vec2 q = uv + dir * dm * uCastPpm * px; vec4 ni = texture2D(tCastInfo, q); if (ni.a < 0.5) continue; int r = int(ni.r * 255.0 + 0.5); if (r != 1 && r != 3 && r != 11) continue; // casters: the face (jaw, chin) and hair float zq = uCastNear + texture2D(tCastDepth, q).x * (uCastFar - uCastNear); float zr = z0 - lz * dm; if (zq < zr - 0.004 && zq > zr - 0.20) return 1.0; } return 0.0; } varying vec3 vN; varying vec2 vUv; varying vec3 vView; varying vec3 vObj; varying float vEdge; varying vec4 vPlate; ${HSV} float hash(vec3 p) { p = fract(p * 0.3183099 + 0.1); p *= 17.0; return fract(p.x * p.y * p.z * (p.x + p.y + p.z)); } float vnoise(vec3 x) { vec3 i = floor(x), f = fract(x); f = f * f * (3.0 - 2.0 * f); return mix(mix(mix(hash(i), hash(i + vec3(1,0,0)), f.x), mix(hash(i + vec3(0,1,0)), hash(i + vec3(1,1,0)), f.x), f.y), mix(mix(hash(i + vec3(0,0,1)), hash(i + vec3(1,0,1)), f.x), mix(hash(i + vec3(0,1,1)), hash(i + vec3(1,1,1)), f.x), f.y), f.z); } float toward(float h, float target, float amt) { float d = target - h; d -= floor(d + 0.5); return fract(h + clamp(d, -amt, amt)); } vec3 tone(vec3 base, float hue, float amt, float sMul, float vMul) { vec3 h = rgb2hsv(base); return hsv2rgb(vec3(toward(h.x, hue, amt), clamp(h.y * sMul, 0.0, 1.0), clamp(h.z * vMul, 0.0, 1.0))); } float st(float x, float t) { return smoothstep(t - 0.03, t + 0.03, x); } // Steel and gold. Hard reflection bands (dark below the horizon, a dark core under a white streak) // read well where the surface turns fast: rims, tubes, lames. On a broad plate the reflection // barely changes, so a band there fills the whole plate (the blotches): bands fade by how sharply // the surface curves (normal change per metre on screen), and broad plates get a soft tone that // lightens up each outline plate (hgt: height in the plate 0..1, or -1 without plate data). vec3 metal(bool gold, vec3 base, vec3 n, vec3 v, float lit, float hgt) { vec3 r = reflect(-v, n); float ry = r.y + (vnoise(vObj * vec3(3.0, 14.0, 3.0)) - 0.5) * 0.14; // jitter the bands along their length, not into blobs float curv = length(fwidth(n)) / max(length(fwidth(vView)), 1e-5); float sharp = smoothstep(5.0, 14.0, curv); // a shaped plate's domes (over the bust, a crowned panel) turn the bands into closed rings: plates // keep only a little of them and take a crisp highlight toward the light instead if (hgt >= 0.0) sharp *= 0.25; else sharp = max(sharp, 0.3); vec3 s0, s1, s2, s3; if (!gold) { s0 = tone(base, 0.68, 0.10, 2.4, 0.32); s1 = tone(base, 0.68, 0.06, 1.7, 0.76); s2 = tone(base, 0.12, 0.0, 0.8, 0.88); s3 = vec3(0.97, 0.96, 0.92); } else { s0 = vec3(0.30, 0.20, 0.30); s1 = tone(base, 0.20, 0.03, 1.0, 0.72); s2 = base; s3 = tone(base, 0.16, 0.03, 0.5, 1.5); } vec3 hard = mix(s0, s1, st(ry, -0.42)); // facing down: dark hard = mix(hard, s2, st(ry, -0.08)); // facing us: the plate's own grey hard = mix(hard, s0, st(ry, 0.36)); // dark core under the streak hard = mix(hard, s3, st(ry, 0.47)); // the white streak hard = mix(hard, s2 * 1.06, st(ry, 0.70)); float k = smoothstep(-0.55, 0.30, r.y); // broad: darker toward the ground (no jitter: if (hgt >= 0.0) k = clamp(k * 0.45 + hgt * 0.80 - 0.18, 0.0, 1.0); // on a flat plate it streaks) else k = clamp(k * 0.6 + n.y * 0.35 + 0.05, 0.0, 1.0); // without plate data: lighter facing up vec3 soft = mix(s1 * 0.9, s2, k); float hl = dot(n, normalize(uKey + v)); // the key's highlight, a hard-edged shape if (hgt >= 0.0) soft = mix(soft, mix(s2, s3, 0.6), st(hl, 0.955) * smoothstep(0.45, 0.8, hgt)); // the upper part only vec3 col = mix(soft, hard, sharp); return mix(col * 0.72 + s0 * 0.28, col, st(lit, 0.30)); } void main() { vec4 tex = texture2D(uMap, vUv); if (uAlpha == 1 && tex.a < 0.5) discard; if (uAlpha == 2 && (tex.a < 0.5 || dot(tex.rgb, vec3(1.0 / 3.0)) >= 0.42)) discard; if (uPass == 1) { gl_FragColor = uInfo; return; } vec3 n = normalize(vN); if (!gl_FrontFacing) n = -n; vec3 v = normalize(vView); float lit = dot(n, uKey) * 0.5 + 0.5; if (uShadeOnly == 1) lit = min(lit, 0.37); // shadow cards only ever darken skin, to a soft shade else if (uCastOn == 1 && uKind == 1 && int(uInfo.x * 255.0 + 0.5) == 2 && castShadow() > 0.5) lit = min(lit, 0.37); // body skin under the jaw's or the hair's shadow float g1 = vnoise(vObj * 40.0), g2 = vnoise(vObj * 150.0); vec3 col; if (uKind == 0 || uKind == 5 || uKind == 6) { // cloth, leather, ground vec3 hb = rgb2hsv(uBase); bool cool = hb.x > 0.42 && hb.x < 0.86; float sh = uKind == 5 ? 0.93 : (cool ? 0.70 : 0.96); …851 tokens truncated… // R paint, G groove, B gold inlay float pm = smoothstep(0.3, 0.7, dm.r), gm = smoothstep(0.3, 0.7, dm.g), au = smoothstep(0.3, 0.7, dm.b); if (au > 0.0) col = mix(col, metal(true, uGold, n, v, lit, -1.0), au); if (pm > 0.0) { vec3 pc = mix(tone(uMotif, 0.70, 0.05, 1.10, 0.62), uMotif, st(lit, 0.42)); col = mix(col, mix(pc, tone(uMotif, 0.56, 0.03, 0.8, 1.25), st(lit, 0.92)), pm); } if (gm > 0.0) col = mix(col, col * 0.30, gm); } if (vEdge > 0.5) { // the cloth's border band vec3 gd = tone(uBand, 0.20, 0.03, 1.0, 0.72); col = mix(gd, uBand, st(lit, 0.45)); col = mix(col, tone(uBand, 0.16, 0.03, 0.5, 1.45), st(lit, 0.85)); } if (uKind <= 6) { vec3 grade = mix(uAmbient, uKeyCol, st(lit, 0.55)); col *= uKind == 1 && uSkinRamp == 1 ? mix(vec3(1.0), grade, 0.3) : grade; // a designed skin ramp takes less of it float fr = pow(1.0 - max(dot(n, v), 0.0), 4.0); float back = clamp(dot(n, normalize(vec3(0.55, 0.45, -0.35))), 0.0, 1.0); col += uRimCol * fr * smoothstep(0.1, 0.5, back); if (uKind != 1) col *= 1.0 + (g2 - 0.5) * 0.06; } gl_FragColor = vec4(clamp(col, 0.0, 1.0), 1.0); }`; const PAINT_COMP = ` uniform sampler2D tColor; uniform sampler2D tInfo; uniform sampler2D tDepth; uniform vec2 uRes; uniform float uNear; uniform float uFar; uniform float uGap; uniform float uSil; uniform float uInner; ${HSV} float z(vec2 p) { return uNear + texture2D(tDepth, p).x * (uFar - uNear); } int rid(vec4 i) { return int(i.r * 255.0 + 0.5); } float pid(vec4 i) { return floor(i.g * 255.0 + 0.5) + floor(i.b * 255.0 + 0.5) * 256.0; } vec3 ink(vec3 c) { vec3 h = rgb2hsv(c); return hsv2rgb(vec3(h.x, clamp(h.y, 0.20, 0.45), 0.19)); } void main() { vec2 uv = gl_FragCoord.xy / uRes, px = 1.0 / uRes; vec4 inf = texture2D(tInfo, uv); if (inf.a < 0.5) { // outer line: grow the silhouette outward vec4 best = vec4(0.0); vec2 bq = uv; for (int k = 0; k < 12; k++) { float a = float(k) * 0.5236; for (int s = 1; s <= 2; s++) { vec2 q = uv + vec2(cos(a), sin(a)) * uSil * float(s) * 0.5 * px; vec4 ni = texture2D(tInfo, q); if (ni.a > 0.5) { best = ni; bq = q; } } } if (best.a > 0.5) { int r = rid(best); if (r >= 4 && r <= 7) { gl_FragColor = vec4(0.0); return; } gl_FragColor = vec4(ink(texture2D(tColor, bq).rgb), 1.0); return; } gl_FragColor = vec4(0.0); return; } vec3 col = texture2D(tColor, uv).rgb; int r0 = rid(inf); float p0 = pid(inf); float z0 = z(uv); bool feature = r0 >= 4 && r0 <= 9; if (!feature) { for (int k = 0; k < 4; k++) { vec2 d = k == 0 ? vec2(1.0, 0.0) : (k == 1 ? vec2(-1.0, 0.0) : (k == 2 ? vec2(0.0, -1.0) : vec2(0.0, 1.0))); vec2 q = uv + d * px * uInner; vec4 ni = texture2D(tInfo, q); if (z(q) > z0 + uGap && pid(ni) != p0) { col = ink(col); break; } } float acc = 0.0, rad = uSil * 3.0; for (int k = 0; k < 8; k++) { float a = float(k) * 0.785398 + 0.39; vec2 q = uv + vec2(cos(a), sin(a)) * rad * px;296 more lines not shown.
exec_commandsource ~/Projects/lowmarch-bench/kit/env-c.sh && sed -n '570,610p' review/vroid-02/index.html && rg -n 'function allMeshes|function applySkin|targetsFor|const targets' review/vroid-02/index.html && cat .gitignore && sed -n '1,120p' "$KIT/front_sheet.py" (+1 more)
command
source ~/Projects/lowmarch-bench/kit/env-c.sh && sed -n '570,610p' review/vroid-02/index.html && rg -n 'function allMeshes|function applySkin|targetsFor|const targets' review/vroid-02/index.html && cat .gitignore && sed -n '1,120p' "$KIT/front_sheet.py" source ~/Projects/lowmarch-bench/kit/env-c.sh && ls "$WORK/shot_before/angry" && git log -1 --oneline && sed -n '1,120p' tools/gear.py
result, 234 lines
Script completed Wall time 0.3 seconds Output: const ctl = new OrbitControls(camera, R.domElement); ctl.enableDamping = true; ctl.zoomToCursor = true; ctl.screenSpacePanning = true; ctl.minZoom = 0.3; ctl.maxZoom = 12; let freeLook = false; ctl.addEventListener("start", () => { freeLook = true; }); R.domElement.addEventListener("dblclick", focusAt); const targets = { rt: null, rtInfo: null, key: "" }; function targetsFor(w, h) { const key = `${w}x${h}`; if (targets.key === key) return; [targets.rt, targets.rtInfo].forEach(t => t && t.dispose()); const o = { minFilter: THREE.NearestFilter, magFilter: THREE.NearestFilter }; targets.rt = new THREE.WebGLRenderTarget(w, h, o); targets.rtInfo = new THREE.WebGLRenderTarget(w, h, { ...o, depthTexture: new THREE.DepthTexture(w, h) }); targets.key = key; } // ---- cloth ------------------------------------------------------------------------------------------------------- // The skinned cape/tails stay hidden and provide the pinned row (they follow the animated body); // a plain display mesh in world space shows the simulated cloth. Colliders ride the bones. const _v = new THREE.Vector3(), _a = new THREE.Vector3(), _ab = new THREE.Vector3(), _cp = new THREE.Vector3(); const _A = new THREE.Vector3(), _B = new THREE.Vector3(); class Cloth { constructor(skinned, U, V, trims, capsules, bone, closed = false, hemOnly = false) { this.skinned = skinned; this.U = U; this.V = V; this.trims = trims; this.closed = closed; this.hemOnly = hemOnly; this.n = skinned.geometry.attributes.position.count; this.caps = capsules.map(c => ({ a: bone(c.bones[0]), b: bone(c.bones[1]), r: c.r + 0.006 })).filter(c => c.a && c.b); this.x = new Float32Array(this.n * 3); this.prev = new Float32Array(this.n * 3); const idx = (i, j) => i * V + j, links = [], bends = [], edge = new Float32Array(this.n); for (let i = 0; i < U; i++) for (let j = 0; j < V; j++) { // a closed cloth (a skirt) wraps around: the last column links back to the first if (i + 1 < U || closed) links.push([idx(i, j), idx((i + 1) % U, j)]); if (j + 1 < V) links.push([idx(i, j), idx(i, j + 1)]); if (i + 2 < U || closed) bends.push([idx(i, j), idx((i + 2) % U, j)]); if (j + 2 < V) bends.push([idx(i, j), idx(i, j + 2)]); edge[idx(i, j)] = ((j === 0 && !hemOnly) || j === V - 1 || (!closed && trims.length > 1 && (i === 0 || i === U - 1))) ? 1 : 0; } const src = skinned.geometry.attributes.position, r = src.array; const len = ([a, b]) => Math.hypot(r[3*a] - r[3*b], r[3*a+1] - r[3*b+1], r[3*a+2] - r[3*b+2]); this.links = links.map(l => [l[0], l[1], len(l)]); this.bends = bends.map(l => [l[0], l[1], len(l)]); 576:const targets = { rt: null, rtInfo: null, key: "" }; 577:function targetsFor(w, h) { 1318:function applySkin(meshes) { 1341: targetsFor(buf.x, buf.y); # Study references (third-party art) never live in this repo; see docs/study.md refs/ study/ __pycache__/ *.pyc review/out/ """Frontal expression sheet for one model: rows are rounds (before, r1, ...), columns the expressions, in two blocks of seven. Tiles are the frontal face tile of shoot_expr.sh shots. python3 front_sheet.py OUT.png NAME "Title" TAG:caption [TAG:caption ...] """ import os import sys from PIL import Image, ImageDraw, ImageFont out, name, title = sys.argv[1:4] rounds = [a.split(":", 1) for a in sys.argv[4:]] EXPR = [("neutral", "Neutral"), ("joy", "Joy"), ("smile", "Smile"), ("angry", "Angry"), ("sad", "Sad"), ("surprised", "Surprised"), ("shout", "Shout"), ("wink", "Wink"), ("smug", "Smug"), ("lip_A", "A"), ("lip_I", "I"), ("lip_U", "U"), ("lip_E", "E"), ("lip_O", "O")] T, PAD, LW = 250, 6, 170 F = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 17) Fh = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 24) Fs = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 14) BG, INK, SUB = (236, 233, 228), (28, 26, 36), (92, 88, 100) def tile(tag, e): im = Image.open(os.path.join(os.environ["WORK"], f"shot_{tag}/{e}/painted_{name}.png")).convert("RGB") gap, head = 6, 30 tw = (im.width - gap) // 2 - gap return im.crop((gap, gap + head, gap + tw, gap + head + tw)).resize((T, T), Image.LANCZOS) blocks = [EXPR[:7], EXPR[7:]] W = LW + 7 * (T + PAD) + PAD H = 50 + len(blocks) * (30 + len(rounds) * (T + PAD)) + 10 sh = Image.new("RGB", (W, H), BG) d = ImageDraw.Draw(sh) d.text((PAD, 12), title, font=Fh, fill=INK) y = 50 for blk in blocks: for k, (_, lab) in enumerate(blk): d.text((LW + k * (T + PAD) + 6, y + 6), lab, font=F, fill=INK) y += 30 for tag, cap in rounds: line, yy = "", y + T // 2 - 20 for w in cap.split(): if d.textlength(line + " " + w, font=Fs) > LW - 12: d.text((PAD, yy), line.strip(), font=Fs, fill=SUB) line, yy = "", yy + 18 line += " " + w d.text((PAD, yy), line.strip(), font=Fs, fill=SUB) for k, (e, _) in enumerate(blk): try: sh.paste(tile(tag, e), (LW + k * (T + PAD), y)) except FileNotFoundError: pass y += T + PAD sh.save(out) print(out, sh.size) painted_arm_f_slender.png painted_arm_m_youth.png 47dcd23 Armory: a skin picker over the new range (People and Fantasy), ?skin=<tone> for sheets """Gear for generic units (the enemy soldiers): helmets, hoods and coifs, quilted coats, mail, riveted vests, shields and weapons, built on the plate kit (plates.py) and the head chart (hair.py). Each function returns pieces bound to the skeleton; a class costume (mobs.py) combines them. Builds and faces for generic units are presets of the body's shape controls (BUILDS, FACES), so the same costume dresses a slim young soldier or a huge brute. """ import math import numpy as np import outfit as O from fitting import Surface from plates import TorsoChart, LimbChart, ConeChart, outline_plate, stiffen, flat_piece, taper, Canvas from hair import head_chart # ---- builds and faces: presets of vroid_portrait's shape controls ------------------------------------ BUILDS = { "slim": dict(chest=0.95, torso=0.94, arms=0.94, legs=0.94, neck=0.95), "average": dict(), "athletic": dict(chest=1.06, arms=1.07, legs=1.04, neck=1.08, shoulder_w=0.010), "stocky": dict(chest=1.18, torso=1.24, arms=1.18, legs=1.16, neck=1.28, shoulder_w=0.018, leg_len=0.95, height=0.97), "huge": dict(chest=1.34, torso=1.32, arms=1.40, legs=1.32, neck=1.55, hands=1.22, shoulder_w=0.042, height=1.12, head=0.92), } FACES = { # 3D-strength values (the sculpt moves anatomical points by millimetres per unit) "bare": dict(), # no sculpt here: the head is sculpted in its own file (tools/heads.py, the armory) "young": dict(face_len=-0.04, jaw=-0.4, chin_len=-0.5, eye=1.04), "plain": dict(face_len=0.05, jaw=0.6, brow_ridge=0.6, eye=0.95, rest={"Fcl_BRW_Angry": 0.3}), "veteran": dict(face_len=0.14, jaw=1.4, chin_w=1.2, chin_len=1.0, cheek=-1.0, brow_ridge=1.6, nose=1.4, eye=0.88, age_lines=0.8, iris=0.82, blush=0.0, mouth=1.1, rest={"Fcl_EYE_Angry": 0.6, "Fcl_EYE_Close": 0.12, "Fcl_EYE_Joy": 0.22, "Fcl_BRW_Angry": 0.65, "Fcl_MTH_Down": 0.35}), "brute": dict(face_len=0.10, jaw=2.6, chin_w=2.2, chin_len=0.8, cheek=1.2, brow_ridge=2.4, nose=0.8, nose_w=2.2, eye=0.86, mouth=1.25, rest={"Fcl_EYE_Close": 0.28, "Fcl_BRW_Angry": 0.75}), "gaunt": dict(face_len=0.18, jaw=0.3, chin_len=1.2, cheek=-1.6, nose=1.8, eye=0.88, rest={"Fcl_BRW_Angry": 0.5, "Fcl_EYE_Close": 0.25}), "round": dict(face_len=-0.03, jaw=1.0, chin_w=1.2, cheek=1.6, eye=0.93, mouth=1.08), "soft": dict(face_len=0.02, jaw=-0.2, cheek=0.4, eye=1.0), "stern": dict(face_len=0.07, jaw=0.6, chin_len=0.5, brow_ridge=0.8, eye=0.92, rest={"Fcl_BRW_Angry": 0.55}), } def shape(build="average", face="plain", **extra): """A shape dict for the cast: a build and a face combined (later keys win).""" out = dict(BUILDS[build]) f = dict(FACES[face]) extra = dict(extra) rest = {**out.pop("rest", {}), **f.pop("rest", {}), **extra.pop("rest", {})} out.update(f) out.update(extra) if rest: out["rest"] = rest return out def _bind(fit, pcs, bone): for pc in pcs: fit.bind(pc, rigid=bone) return pcs def _group(pcs, g): for pc in pcs: pc["group"] = g return pcs # ---- heads: helmets, coifs and hoods on the chart over the skull ---------------------------------------- def helmet(fit, kind="kettle", role="steel", lift=0.030, name="helmet", rivets=True, trim_role=None): """A helmet on the skull chart. kettle: a rounded bowl to just above the brows and ears, a wide brim sloping down. nasal: a taller bowl with a band at the rim and a nose guard down to the nose's tip. sallet: a bowl coming low over the forehead, sweeping into a long flared tail at the back. close: a bowl to the jaw all round, a face plate with an eye slit and breaths (a bucket helm).""" ch = head_chart(fit) out = [] if kind == "kettle": rim = lambda u: float(np.interp(abs(u), [0, 60, 100, 180], [74, 80, 86, 92])) elif kind == "nasal": rim = lambda u: float(np.interp(abs(u), [0, 60, 100, 180], [72, 82, 92, 100])) elif kind == "sallet": rim = lambda u: float(np.interp(abs(u), [0, 50, 90, 140, 180], [78, 86, 104, 124, 130])) else: # close rim = lambda u: float(np.interp(abs(u), [0, 60, 100, 180], [136, 134, 126, 122])) fl = None if kind == "sallet": # the tail flares out over the nape fl = lambda u, v, s, t: 0.030 * max(0.0, (abs(u) - 110) / 70) * (1 - t) ** 1.6 seams = [] if kind in ("nasal", "kettle"): # a raised comb from front to back seams = [[(0, 4), (0, rim(0) - 4)], [(180, 4), (180, rim(180) - 4)]] deco = [] if kind == "close": # the eye slit and the breaths deco.append(("line", [(-44, 97), (44, 97)], 0.008, "groove")) for u in (-24, -16, -8, 8, 16, 24): deco.append(("line", [(u, 112), (u, 122)], 0.003, "groove")) bowl, ex = outline_plate(f"{name}", role, ch, -180, 180, rim, lambda u: 0.0, offset=lift, flare=fl, thickness=0.004, nu=72, nv=22, roll=0.003, roll_edges=("bottom",), roll_role=trim_role or role, seams=seams, groove=(0.006, -0.004), panel_crown=0.0, decal=deco, decal_px=1200) out += [bowl] + ex if kind == "kettle": # the brim: out and down from the rim, all round U = np.linspace(-180, 180, 73) rimP = np.array([ch.point(u, rim(u), off=lift) for u in U]) d = rimP - ch.c d[:, 1] = 0 d /= np.linalg.norm(d, axis=1, keepdims=True) G = np.stack([rimP + d * 0.058 * t - np.array([0, 0.022 * t * t, 0]) for t in np.linspace(0, 1, 5)], 1) pos, tri, outer = O.thick_grid(G, 0.004, outward_from=ch.c - np.array([0, 0.4, 0])) brim = O.piece(f"{name}_brim", role, pos, tri) out += [brim, O.pipe(f"{name}_brim_roll", trim_role or role, outer[:, -1], r=0.003, seg=6)] if kind == "nasal": # the nose guard and a riveted band guard, gx = outline_plate(f"{name}_nasal", role, ch, -5, 5, lambda u: 104.0, lambda u: rim(0) - 8, offset=lift + 0.006, thickness=0.004, nu=8, nv=12, roll=0) out += [guard] band, bx = outline_plate(f"{name}_band", role, ch, -180, 180, lambda u: rim(u), lambda u: rim(u) - 9, offset=lift + 0.003, thickness=0.003, nu=72, nv=4, roll=0, rivets=(0.035, 0.009, 0.0035) if rivets else None, rivet_role=role) out += [band] + bx _bind(fit, out, "J_Bip_C_Head")exec_commandsource ~/Projects/lowmarch-bench/kit/env-c.sh && sed -n '1290,1340p' review/vroid-02/index.html && rg -n 'allMeshes' review/vroid-02/index.html && cat "$WORK/shot_before/neutral/../.." 2>/dev/null || true
command
source ~/Projects/lowmarch-bench/kit/env-c.sh && sed -n '1290,1340p' review/vroid-02/index.html && rg -n 'allMeshes' review/vroid-02/index.html && cat "$WORK/shot_before/neutral/../.." 2>/dev/null || true
result, 59 lines
Script completed Wall time 0.2 seconds Output: if (view === "lineup") { const i = LINEUP.order.indexOf(c.name); stage.add(c.turn); c.turn.position.set(lineupX(i), 0, 0); c.holder.position.set(0, 0, 0); } else if (view === "compare") { // placed by layoutCompare, once all are in stage.add(c.turn); c.holder.position.set(0, 0, 0); } else if (view === "duel") { const pl = CAM.duel.place[c.name]; stage.add(c.turn); c.turn.position.set(...pl.pos); c.turn.rotation.y = THREE.MathUtils.degToRad(pl.yaw); c.holder.position.set(0, 0, 0); } else { scene.add(c.turn); c.turn.position.set(v.pivot[0], 0, v.pivot[2]); c.holder.position.set(-v.pivot[0], 0, -v.pivot[2]); } } statusEl.hidden = true; if (view === "compare") layoutCompare(); buildParts(); applyMaterials(); size(); matchCamera(); setYaw(changed || view === "duel" ? VIEW_YAW[view] : state.yaw); const want = clip || (view === "duel" ? "clash" : (CLIP_VIEW[state.clip] === "battle" ? state.clip : "idle")); if (view === "duel") setClip("clash", SHEET ? null : "idle"); else if (view === "battle") setClip(want); else if (view === "lineup") setClip(clip && CLIP_VIEW[clip] === "battle" ? clip : "idle"); else setClip("idle"); animate(0); scene.updateMatrixWorld(true); cs.forEach(c => c.cloths.forEach(cl => { cl.reset(false); cl.write(); })); } catch (e) { console.error(e); statusEl.textContent = "The model didn't load."; } } // ---- rendering ------------------------------------------------------------------------------------------------------- function applySkin(meshes) { // the picked tone on every skin material (face, body, shadow cards); the built tone is kept to go back to const tone = state.skin && PAGE && PAGE.skins ? PAGE.skins.find(s => s.name === state.skin) : null; for (const o of meshes) { const m = o.userData.mats && o.userData.mats.painted, u = m && m.uniforms; if (!u || !u.uSkinRamp || u.uKind.value !== 1) continue; if (!o.userData.builtSkin) o.userData.builtSkin = [u.uSkinRamp.value, u.uSkinL.value.clone(), u.uSkinS.value.clone(), u.uSkinC.value.clone()]; if (tone) { u.uSkinRamp.value = 1; u.uSkinL.value.copy(hex(tone.ramp[0])); u.uSkinS.value.copy(hex(tone.ramp[1])); u.uSkinC.value.copy(hex(tone.ramp[2])); } else { const b = o.userData.builtSkin; u.uSkinRamp.value = b[0]; u.uSkinL.value.copy(b[1]); u.uSkinS.value.copy(b[2]); u.uSkinC.value.copy(b[3]); } } } function render() { const meshes = allMeshes(), cs = active(); applySkin(meshes); const cloths = cs.flatMap(c => c.cloths); if (state.mode === "3d") { meshes.forEach(o => o.material = o.userData.mat3d); cloths.forEach(c => c.lines3d.visible = trimsShown(c)); sun.position.copy(camera.position).add(new THREE.Vector3(-3, 4, 0)); sun.target.position.copy(ctl.target); sun.target.updateMatrixWorld(); R.setRenderTarget(null); R.setClearColor(0x191626, 1); R.clear(); R.render(scene, camera); R.setClearColor(0x000000, 0); return; } const buf = R.getDrawingBufferSize(new THREE.Vector2()); 932:const allMeshes = () => [...active().flatMap(c => [...c.meshes, ...c.cloths.map(cl => cl.display)]), 936: allMeshes().forEach(o => { 1190: const hit = ray.intersectObjects(allMeshes().filter(o => o.visible), false)[0]; 1330: const meshes = allMeshes(), cs = active();