Track C: Codex with GPT-6.1-Terra end to end
agent
+18:57The current render pass is nearing completion. I’ll compare the finished sheets against the starting faces and inspect the angled wink and sadness directly.
waitcell 52
input
{ "cell_id": "52", "yield_time_ms": 20000 }result, 4 lines
Script completed Wall time 5.4 seconds Output: {"chunk_id":"2b7b08","wall_time_seconds":40.018299056,"exit_code":0,"original_token_count":151,"output":"arm_f_slender ~/Projects/lowmarch-bench/work/c/shot_r2/surprised/painted_arm_f_slender.png 12s\narm_m_youth ~/Projects/lowmarch-bench/work/c/shot_r2/surprised/painted_arm_m_youth.png 13s\narm_f_slender ~/Projects/lowmarch-bench/work/c/shot_r2/wink/painted_arm_f_slender.png 15s\narm_m_youth ~/Projects/lowmarch-bench/work/c/shot_r2/wink/painted_arm_m_youth.png 15s\nshot r2\n~/Projects/lowmarch-bench/work/c/sheet_r2_woman.png (1968, 1144)\n~/Projects/lowmarch-bench/work/c/sheet_r2_youth.png (1968, 1144)\n2026-10-30T13:03:30+08:00\n"}view_image~/Projects/lowmarch-bench/work/c/sheet_r2_woman.png (+3 more)
input
const results=await Promise.all([ tools.view_image({path:"~/Projects/lowmarch-bench/work/c/sheet_r2_woman.png",detail:"original"}), tools.view_image({path:"~/Projects/lowmarch-bench/work/c/sheet_r2_youth.png",detail:"original"}), tools.view_image({path:"~/Projects/lowmarch-bench/work/c/shot_r2/wink/painted_arm_m_youth.png",detail:"original"}), tools.view_image({path:"~/Projects/lowmarch-bench/work/c/shot_r2/sad/painted_arm_f_slender.png",detail:"original"}) ]);for(const r of results)image(r.image_url);result, 4 lines
Script completed Wall time 0.2 seconds Output:
exec_commandsource ~/Projects/lowmarch-bench/kit/env-c.sh && python3 - <<'PY'
command
source ~/Projects/lowmarch-bench/kit/env-c.sh && python3 - <<'PY' import os,json,base64,struct from pathlib import Path p=Path(os.environ['WORK'])/'exb/angry/arm_f_slender.glb.json' a=json.loads(p.read_text()); print(a.keys()) b=base64.b64decode(a['glb']); l,typ=struct.unpack_from('<II',b,12); j=json.loads(b[20:20+l]); print([(i,x.get('name'),x.get('baseColorTexture')) for i,x in enumerate(j['materials'])]);print('images:',j['images']) PYresult, 7 lines
Script completed Wall time 0.4 seconds Output: dict_keys(['glb']) [(0, 'body:mouth', None), (1, 'body:iris', None), (2, 'body:highlight', None), (3, 'body:face', None), (4, 'body:white', None), (5, 'body:brow', None), (6, 'body:line', None), (7, 'body:line', None), (8, 'body:body', None), (9, 'hair:hair', None), (10, 'hair:hair', None), (11, 'underwear:cloth', None), (12, 'underwear:cloth', None), (13, 'body:shadow', None)] images: [{'bufferView': 6, 'mimeType': 'image/png'}, {'bufferView': 13, 'mimeType': 'image/png'}, {'bufferView': 20, 'mimeType': 'image/png'}, {'bufferView': 27, 'mimeType': 'image/png'}, {'bufferView': 34, 'mimeType': 'image/png'}, {'bufferView': 41, 'mimeType': 'image/png'}, {'bufferView': 48, 'mimeType': 'image/png'}, {'bufferView': 55, 'mimeType': 'image/png'}, {'bufferView': 67, 'mimeType': 'image/png'}, {'bufferView': 74, 'mimeType': 'image/png'}]exec_commandsource ~/Projects/lowmarch-bench/kit/env-c.sh && python3 - <<'PY'
command
source ~/Projects/lowmarch-bench/kit/env-c.sh && python3 - <<'PY' import os,json,base64,struct,io from pathlib import Path from PIL import Image p=Path(os.environ['WORK'])/'exb/angry/arm_f_slender.glb.json'; b=base64.b64decode(json.loads(p.read_text())['glb']); l,_=struct.unpack_from('<II',b,12); j=json.loads(b[20:20+l]); base=20+l+8 for i,m in enumerate(j['materials']): if 'face' in m['name']: print(m); ti=m['pbrMetallicRoughness']['baseColorTexture']['index']; im=j['images'][j['textures'][ti]['source']]; v=j['bufferViews'][im['bufferView']]; data=b[base+v.get('byteOffset',0):base+v.get('byteOffset',0)+v['byteLength']]; img=Image.open(io.BytesIO(data)); print('face img',img.size); img.save(os.environ['WORK']+'/face_texture.png') PYresult, 6 lines
Script completed Wall time 0.2 seconds Output: {'name': 'body:face', 'doubleSided': True, 'pbrMetallicRoughness': {'metallicFactor': 0.0, 'roughnessFactor': 0.9, 'baseColorTexture': {'index': 3}}} face img (512, 512)view_image~/Projects/lowmarch-bench/work/c/face_texture.png
input
const im=await tools.view_image({path:"~/Projects/lowmarch-bench/work/c/face_texture.png",detail:"original"});image(im.image_url);result, 4 lines
Script completed Wall time 0.0 seconds Output:
exec_commandsource ~/Projects/lowmarch-bench/kit/env-c.sh && python3 - <<'PY'
command
source ~/Projects/lowmarch-bench/kit/env-c.sh && python3 - <<'PY' import os from PIL import Image im=Image.open(os.environ['WORK']+'/face_texture.png') for y in [370,382,385,387,388,389,390,391,395,400]: print(y, im.getpixel((256,y)),im.getpixel((200,y))) PY
result, 14 lines
Script completed Wall time 0.5 seconds Output: 370 (250, 235, 222, 255) (250, 235, 221, 255) 382 (250, 235, 222, 255) (250, 235, 222, 255) 385 (245, 227, 213, 255) (250, 235, 222, 255) 387 (196, 148, 142, 255) (250, 235, 222, 255) 388 (207, 166, 159, 255) (250, 235, 222, 255) 389 (237, 215, 203, 255) (250, 235, 222, 255) 390 (249, 234, 221, 255) (250, 235, 222, 255) 391 (250, 235, 222, 255) (250, 235, 222, 255) 395 (250, 235, 222, 255) (250, 235, 222, 255) 400 (250, 235, 222, 255) (250, 235, 222, 255)
agent
+19:40Round two fixes the youth’s wink and closes the joyful eyes at both views. The remaining weakness is the low-contrast painted mouth line, especially for anger, so I’m testing a localized contrast adjustment and removing the small highlight specks from closed joyful eyes.
exec_commandsource ~/Projects/lowmarch-bench/kit/env-c.sh && cat >> "$WORK/NOTES.md" <<'EOF'
command
source ~/Projects/lowmarch-bench/kit/env-c.sh && cat >> "$WORK/NOTES.md" <<'EOF' ## Round 2 — 12:56:54–13:03:30 (6m 36s) Reduced the through-hair brow opacity from 85% to 55%; used the dedicated angry and sorrow mouth shapes, enlarged the surprise mouth, added a little eye-close to joy, and extrapolated the one-sided close for wink. Rebuilt the five changed expressions and rendered the full set. As in round 1, resumed missing captures after the initial capture command was terminated. The sheets show a calmer neutral, a more distinct surprised mouth and cleaner closed eyes on the youth. The 30-degree youth wink and joy now stay closed instead of exposing an iris strip. The woman's wink closes but its lashes are jagged. Sadness now has an open downward mouth; angry has the right downward curve, but the pink texture line is very faint. Closed joy still has tiny highlight specks. Smug and smile are understated, and E still resembles a smile. EOF python3 - <<'PY' from pathlib import Path p=Path('review/vroid-02/index.html') s=p.read_text().replace(' if (uFaceTex == 1) col *= clamp(tex.rgb / uSkinRef, 0.55, 1.05);', ''' if (uFaceTex == 1) { col *= clamp(tex.rgb / uSkinRef, 0.55, 1.05); // The shared VRoid face atlas puts the painted lip line in this small window. // Preserve its hue while giving the darkest lip texels enough contrast to read at game size. if (vUv.x > 0.32 && vUv.x < 0.68 && vUv.y > 0.70 && vUv.y < 0.86) { float lipInk = 1.0 - smoothstep(0.78, 0.91, tex.r); col *= 1.0 - 0.48 * lipInk; } }''') s=s.replace(' col = mix(col, brow.rgb, 0.55);',' col = mix(col, brow.rgb, 0.55 * (1.0 - smoothstep(0.035, 0.045, bz - z0)));') p.write_text(s) p=Path('art/expressions/armory.txt') s=p.read_text().replace('Fcl_EYE_Close:0.35;Fcl_MTH_Joy:0.8','Fcl_EYE_Close:0.35;Fcl_EYE_Highlight_Hide:1;Fcl_MTH_Joy:0.8') p.write_text(s) PY cp art/expressions/armory.txt "$WORK/expr_list.txt" && $KIT/build_expr.sh "$WORK/expr_list.txt" joy && date -Isresult, 4 lines
Script completed Wall time 1.2 seconds Output: {"chunk_id":"60b69f","wall_time_seconds":1.000991168,"session_id":3018,"original_token_count":0,"output":""}write_stdinpoll session 3018
input
const r=await tools.write_stdin({session_id:3018,chars:"",yield_time_ms:20000,max_output_tokens:3000});text(JSON.stringify(r));result, 4 lines
Script completed Wall time 17.9 seconds Output: {"chunk_id":"7869a5","wall_time_seconds":17.916216391,"exit_code":0,"original_token_count":9,"output":"built joy\n2026-10-30T13:04:37+08:00\n"}exec_commandsource ~/Projects/lowmarch-bench/kit/env-c.sh && $KIT/shoot_expr.sh "$LOWMARCH_REPO/review/vroid-02/index.html" r3 && $KIT/sheets.sh r3 'Round 3: clearer lip line and closed-eye cleanup' && date -Is
command
source ~/Projects/lowmarch-bench/kit/env-c.sh && $KIT/shoot_expr.sh "$LOWMARCH_REPO/review/vroid-02/index.html" r3 && $KIT/sheets.sh r3 'Round 3: clearer lip line and closed-eye cleanup' && date -Is
result, 4 lines
Script completed Wall time 1.2 seconds Output: {"chunk_id":"dfc0dd","wall_time_seconds":1.004764721,"session_id":18792,"original_token_count":0,"output":""}write_stdinpoll session 18792
input
const r=await tools.write_stdin({session_id:18792,chars:"",yield_time_ms:40000,max_output_tokens:2000});text(JSON.stringify(r));result, 4 lines
Script running with cell ID 61 Wall time 31.0 seconds Output:
waitcell 61
input
{ "cell_id": "61", "yield_time_ms": 12000 }result, 4 lines
Script completed Wall time 6.3 seconds Output: {"chunk_id":"7a0357","wall_time_seconds":40.001092115,"session_id":18792,"original_token_count":0,"output":""}view_image~/Projects/lowmarch-bench/work/c/shot_r3/angry/painted_arm_f_slender.png
input
const im=await tools.view_image({path:"~/Projects/lowmarch-bench/work/c/shot_r3/angry/painted_arm_f_slender.png",detail:"original"});image(im.image_url);result, 4 lines
Script completed Wall time 0.2 seconds Output:
exec_commandsource ~/Projects/lowmarch-bench/kit/env-c.sh && python3 - <<'PY'
command
source ~/Projects/lowmarch-bench/kit/env-c.sh && python3 - <<'PY' import os from PIL import Image,ImageChops w=os.environ['WORK']; a=Image.open(w+'/shot_r2/angry/painted_arm_f_slender.png').convert('RGB'); b=Image.open(w+'/shot_r3/angry/painted_arm_f_slender.png').convert('RGB'); print('diff bbox',ImageChops.difference(a,b).getbbox()) PYresult, 5 lines
Script completed Wall time 0.6 seconds Output: diff bbox (435, 376, 1312, 741)
exec_commandsource ~/Projects/lowmarch-bench/kit/env-c.sh && python3 - <<'PY'
command
source ~/Projects/lowmarch-bench/kit/env-c.sh && python3 - <<'PY' import os from PIL import Image,ImageChops w=os.environ['WORK'];a=Image.open(w+'/shot_r2/angry/painted_arm_f_slender.png').convert('RGB');b=Image.open(w+'/shot_r3/angry/painted_arm_f_slender.png').convert('RGB'); d=ImageChops.difference(a,b); print('mouth',d.crop((380,700,600,800)).getbbox(), 'extrema', d.crop((380,700,600,800)).getextrema()) PYresult, 5 lines
Script completed Wall time 1.1 seconds Output: mouth (55, 35, 157, 41) extrema ((0, 63), (0, 49), (0, 47))
exec_commandsource ~/Projects/lowmarch-bench/kit/env-c.sh && python3 - <<'PY'
command
source ~/Projects/lowmarch-bench/kit/env-c.sh && python3 - <<'PY' import os from PIL import Image w=os.environ['WORK']; a=Image.open(w+'/shot_r3/angry/painted_arm_f_slender.png');a.crop((390,700,570,775)).resize((1260,525)).save(w+'/mouth_debug.png') PY
result, 4 lines
Script completed Wall time 0.7 seconds Output:
view_image~/Projects/lowmarch-bench/work/c/mouth_debug.png
input
const im=await tools.view_image({path:"~/Projects/lowmarch-bench/work/c/mouth_debug.png",detail:"original"});image(im.image_url);result, 4 lines
Script completed Wall time 0.0 seconds Output:
exec_commandsource ~/Projects/lowmarch-bench/kit/env-c.sh && rg -n 'mouth_curve|mouth_bone|info.ink|\.bone|bones' review/vroid-02/index.html | head -100 && sed -n '885,930p' review/vroid-02/index.html && sed -n '960,1015p' review/vroid-02/index.html && rg -n 'FLIP' tools/export_glb.py | head
command
source ~/Projects/lowmarch-bench/kit/env-c.sh && rg -n 'mouth_curve|mouth_bone|info.ink|\.bone|bones' review/vroid-02/index.html | head -100 && sed -n '885,930p' review/vroid-02/index.html && sed -n '960,1015p' review/vroid-02/index.html && rg -n 'FLIP' tools/export_glb.py | head
result, 127 lines
Script completed Wall time 0.3 seconds Output: 201: <button class="btn" type="button" id="physics" aria-pressed="true">Spring bones</button> 604:// a plain display mesh in world space shows the simulated cloth. Colliders ride the bones. 611: 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); 917: const caps = V.capsules.filter(c => !info.collide || info.collide.some(k => c.bones[0].includes(k))); 963:// A clip only carries the bones it moves away from the reference pose, so each one starts from the 1034:// ---- motion and spring bones ---------------------------------------------------------------------------------------- 1036:// to side, so the spring bones (VRoid's breasts, on the women) show how they move 1052:// VRM 0.x spring bones as UniVRM runs them: each joint's tail keeps its momentum (less the drag), is pulled back 1060: g.spheres = (g.colliders || []).map(s => ({ bone: c.root.getObjectByName(s.bone), // turns the model) 1061: offset: new THREE.Vector3(-s.offset[0], s.offset[1], -s.offset[2]), radius: s.radius })).filter(s => s.bone); 1063: for (const g of groups) for (const rootName of g.bones) { 1099: const cp = s.bone.localToWorld(s.offset.clone()), r = s.radius + (j.g.radius || 0), d = next.distanceTo(cp); 1665: if (state.physics && !SHEET) { // spring bones at a fixed 120 Hz, as the cloth m.color = hex(V.paint.hair.base); m.onBeforeCompile = sh => { sh.fragmentShader = sh.fragmentShader.replace("#include <map_fragment>", `#ifdef USE_MAP vec4 texel_ = texture2D(map, vMapUv); diffuseColor.rgb *= mix(1.0, clamp(dot(texel_.rgb, vec3(0.3333)) / 0.5, 0.45, 1.35), 0.6); diffuseColor.a *= texel_.a; #endif`); }; m.customProgramCacheKey = () => "hairlum"; } } o.userData.mats = { clay, toon, painted: paintMaterial(V, role, raw, meshIndex, infoRole, decal) }; meshIndex++; o.userData.group = o.userData.group || "body"; o.userData.role = role; o.frustumCulled = false; } async function loadCharacter(name) { if (chars.has(name)) return chars.get(name); const def = charDef(name), V = def.info, ci = CAM.characters.indexOf(def); const gltf = await parseGlb(`${name}.glb.json`); const turn = new THREE.Group(), holder = new THREE.Group(); turn.add(holder); holder.add(gltf.scene); const meshes = []; gltf.scene.traverse(o => { if (o.isMesh) { prepMesh(o, V, { hairB: ci > 0 }); meshes.push(o); } }); gltf.scene.updateMatrixWorld(true); const bone = n => gltf.scene.getObjectByName(n); const cloths = [], world = new THREE.Group(); for (const [cname, info] of Object.entries(V.cloth || {})) { const skinned = meshes.find(m => m.userData.material === cname); if (!skinned) continue; const trims = meshes.filter(m => info.trims.includes(m.userData.material)); const caps = V.capsules.filter(c => !info.collide || info.collide.some(k => c.bones[0].includes(k))); const cl = info.closed ? new SkirtRig(skinned, info.u, info.v, trims, caps, bone, info.edges === "hem") : new Cloth(skinned, info.u, info.v, trims, caps, bone, false, info.edges === "hem"); const band = info.band || "gold", BR = V.roles[band] || V.roles.gold; const gold = hex(BR.bands ? BR.bands[Math.min(1, BR.bands.length - 1)][1] : BR.color); const PB = (V.paint || {})[band]; if (PB && PB.base) skinned.userData.mats.painted.uniforms.uBand.value = hex(PB.base); // gold stays gold for decals cl.lines3d = new THREE.LineSegments(cl.lines.geometry, new THREE.LineBasicMaterial({ color: gold })); cl.lines3d.frustumCulled = false; world.add(cl.display, cl.lines3d); cloths.push(cl); } const mixer = new THREE.AnimationMixer(gltf.scene); const actions = Object.fromEntries(gltf.animations.map(c => [c.name, mixer.clipAction(c)])); const trimsShown = c => state.cloth && !state.hidden.has("trim") && !state.hidden.has(c.skinned.userData.group); // ---- animation ------------------------------------------------------------------------------------------------------ // A clip only carries the bones it moves away from the reference pose, so each one starts from the // reference pose (otherwise "arms out" would keep the idle's legs). const CLIP_VIEW = { clash: "duel", idle: "battle", attack: "battle", apose: "battle" }; const lineupX = i => (i - (LINEUP.order.length - 1) / 2) * LINEUP.spacing; function setClip(name, blendFrom = null) { for (const c of active()) { const n = c.actions[name] ? name : "idle"; if (!blendFrom) c.skeleton && c.skeleton.pose(); c.clip = n; c.t = 0; Object.entries(c.actions).forEach(([k, a]) => { if (k === n) { a.reset(); a.setEffectiveWeight(1); a.play(); } else a.stop(); }); // ease in from another clip: legs stepping out gradually push the cloth (a long skirt) instead of // jumping through it c.blend = blendFrom && c.actions[blendFrom] ? { from: blendFrom, t: 0, dur: 0.8 } : null; } state.clip = name; document.querySelectorAll("[data-clip]").forEach(b => b.setAttribute("aria-pressed", String(b.dataset.clip === name && state.view !== "bust"))); } function animate(dt) { for (const c of active()) { if (ARMORY_PAGE && !SHEET) { c.mixer.stopAllAction(); c.skeleton && c.skeleton.pose(); applyPose(c); applyMotion(c, dt); continue; } if (state.view === "bust") { c.mixer.stopAllAction(); c.skeleton && c.skeleton.pose(); continue; } const a = c.actions[c.clip]; if (!a) continue; if (!a.isRunning()) a.play(); const dur = a.getClip().duration; c.t += dt; if (c.clip === "attack" && c.t > dur + 0.25) { setClip("idle"); return animate(0); } const t = state.stepped ? Math.floor(c.t * 12) / 12 : c.t; a.time = c.clip === "attack" ? Math.min(t, dur) : t % dur; // the others loop if (c.blend) { c.blend.t += dt; const w = Math.min(1, c.blend.t / c.blend.dur), b = c.actions[c.blend.from]; if (!b.isRunning()) b.play(); b.time = t % b.getClip().duration; b.setEffectiveWeight(1 - w); a.setEffectiveWeight(w); if (w >= 1) { b.stop(); a.setEffectiveWeight(1); c.blend = null; } } c.mixer.update(0); } } // ---- the armory's poses and its side-by-side comparison -------------------------------------------------------------- // From the reference (bind) pose, which is the T-pose: each upper arm (and for "up" the collarbone) turns about the // body's front axis. Which way lowers an arm is read from where its hand points, so no bone's rest angle is assumed. const POSES = { up: { arm: -78, shoulder: -14 }, out: { arm: 0, shoulder: 0 }, down: { arm: 74, shoulder: 0 } }; const v3 = () => new THREE.Vector3(), q4 = () => new THREE.Quaternion(); function armSide(c, side) { // +1 when the arm points along the model's +x if (!c.armSide) { c.skeleton && c.skeleton.pose(); c.root.updateMatrixWorld(true); const inv = c.root.matrixWorld.clone().invert(), at = n => c.root.getObjectByName(n)?.getWorldPosition(v3()).applyMatrix4(inv); c.armSide = Object.fromEntries(["L", "R"].map(s => { const a = at(`J_Bip_${s}_UpperArm`), h = at(`J_Bip_${s}_Hand`); return [s, a && h ? Math.sign(h.x - a.x) || 1 : 1]; })); } return c.armSide[side]; } 17:FLIP = np.array([-1.0, 1.0, -1.0]) 115: pos = (p["pos"][used] * FLIP).astype(np.float32) 116: nrm = p["nrm"][used] * FLIP 118: tri = inv.reshape(-1, 3).astype(np.uint32) # FLIP is a 180-degree turn: winding holds 217: caps.append(dict(a=(A * FLIP).tolist(), b=(B * FLIP).tolist(), r=float(np.percentile(d, 70) + 0.012), 233: curve = ((portrait.mouth_curve + alln[i] * 0.0015) * FLIP).tolist() 392: pos = (p["pos"][usedv] * FLIP).astype(np.float32) 393: nrm = p["nrm"][usedv] * FLIPexec_commandsource ~/Projects/lowmarch-bench/kit/env-c.sh && sed -n '1,180p' tools/export_glb.py && sed -n '335,445p' tools/export_glb.py && date -Is
command
source ~/Projects/lowmarch-bench/kit/env-c.sh && sed -n '1,180p' tools/export_glb.py && sed -n '335,445p' tools/export_glb.py && date -Is
result, 296 lines
Script completed Wall time 0.4 seconds Output: """Export a composed character (VRoid base + our shapes, costume, props, scenery) to GLB for the browser 3D viewer. Each primitive becomes a mesh named by its group (body, hair, costume pieces, sword, terrain) so the viewer can toggle and x-ray them. Face, eye, line, brow, mouth and hair keep VRoid's textures (downscaled) for alpha and detail; everything else gets the character's palette colour. The model is turned to face +z, the direction a default three.js camera looks from. """ import io import math import json import struct import numpy as np from PIL import Image FLIP = np.array([-1.0, 1.0, -1.0]) TEXTURED = ("face", "iris", "white", "highlight", "line", "brow", "mouth", "hair") ROLE_TONE = {"face": "5", "body": "4", "shadow": "3", "hair": "D", "brow": "B", "coat": "M", "cape": "W", "metal": "8", "signature": "M", "doublet": "S", "heraldry": "N", "steel": "8", "gold": "H", "leather": "v", "boot": "v", "glove": "x", "cloth": "M", "grass": "c", "dirt": "f", "stone": "l", "mouth": "2", "line": "o", "iris": "s", "white": "w", "highlight": "w"} def group_of(p, role): if p.get("world"): return "terrain" if p.get("group"): return p["group"] if role in ("face", "body", "shadow", "iris", "white", "highlight", "line", "brow", "mouth"): return "body" if role == "hair": return "hair" name = p["material"].replace("OUTFIT_", "") for key in ("trim", "edge", "hem", "placket", "buckle", "clasp"): if key in name: return "trim" for key in ("pauldron", "cape", "collar", "tunic", "tails", "belt", "trousers", "boots", "gloves"): if name.startswith(key): return key if name in ("blade", "guard", "grip", "pommel", "fuller"): return "sword" return "costume" def skin_ramp(h): """A skin tone's lit, shade and deep-shade colours from its lit colour. The shade is darker and a little more saturated, its hue nudged toward red on human tones (so deep skin stays rich and warm, not ashy) and toward violet on the fantasy blues, greens and greys. Hex strings, light to dark.""" import colorsys r, g, b = (int(h[i:i + 2], 16) / 255 for i in (1, 3, 5)) hue, sat, val = colorsys.rgb_to_hsv(r, g, b) warm = hue < 0.17 or hue > 0.9 dh = -0.015 if warm else (0.025 if hue < 0.75 else -0.025) def tone(k, dv, ds): rr, gg, bb = colorsys.hsv_to_rgb((hue + k * dh) % 1.0, min(1.0, sat + (1 - sat) * ds), val * dv) return "#%02x%02x%02x" % tuple(int(round(c * 255)) for c in (rr, gg, bb)) return [h, tone(1, 0.80, 0.12), tone(2, 0.50, 0.22)] def hex_rgb(h): h = h.lstrip("#") return [int(h[i:i + 2], 16) / 255 for i in (0, 2, 4)] def export(portrait, palette, path, max_tex=512, only_world=False): """portrait: vroid_portrait.Portrait. palette: full char -> hex map. only_world: export just the scenery (the character goes out skinned through export_skinned).""" bin_ = bytearray() views, accessors, meshes, nodes, materials, textures, images = [], [], [], [], [], [], [] tex_index = {} def add_view(data, target=None): while len(bin_) % 4: bin_.append(0) off = len(bin_) bin_.extend(data) v = dict(buffer=0, byteOffset=off, byteLength=len(data)) if target: v["target"] = target views.append(v) return len(views) - 1 def add_acc(arr, kind, comp, target): arr = np.ascontiguousarray(arr) vi = add_view(arr.tobytes(), target) a = dict(bufferView=vi, componentType=comp, count=int(arr.shape[0]), type=kind) if kind == "VEC3" and comp == 5126: a["min"] = arr.min(0).tolist() a["max"] = arr.max(0).tolist() accessors.append(a) return len(accessors) - 1 def add_texture(key, rgba): if key in tex_index: return tex_index[key] im = Image.fromarray(rgba) if max(im.size) > max_tex: k = max_tex / max(im.size) im = im.resize((max(1, int(im.width * k)), max(1, int(im.height * k))), Image.LANCZOS) b = io.BytesIO() im.save(b, "PNG", optimize=True) vi = add_view(b.getvalue()) images.append(dict(bufferView=vi, mimeType="image/png")) textures.append(dict(source=len(images) - 1, sampler=0)) tex_index[key] = len(textures) - 1 return tex_index[key] for k, (p, role) in enumerate(zip(portrait.prims, portrait.roles)): if role == "skip" or not len(p["tri"]) or (only_world and not p.get("world")): continue used, inv = np.unique(p["tri"], return_inverse=True) pos = (p["pos"][used] * FLIP).astype(np.float32) nrm = p["nrm"][used] * FLIP nrm = (nrm / np.maximum(np.linalg.norm(nrm, axis=1, keepdims=True), 1e-9)).astype(np.float32) tri = inv.reshape(-1, 3).astype(np.uint32) # FLIP is a 180-degree turn: winding holds attrs = dict(POSITION=add_acc(pos, "VEC3", 5126, 34962), NORMAL=add_acc(nrm, "VEC3", 5126, 34962)) mat = dict(name=f"{group_of(p, role)}:{role}", doubleSided=True, pbrMetallicRoughness=dict(metallicFactor=0.0, roughnessFactor=0.9)) tone = ROLE_TONE.get(role) color = hex_rgb(palette[tone]) if tone in palette else [0.8, 0.8, 0.8] tex = portrait.tex.get(k) if role == "face": tex = portrait.v.material_texture(p["mi"]) if role in TEXTURED and tex is not None: uv = p["uv"][used].astype(np.float32) attrs["TEXCOORD_0"] = add_acc(uv, "VEC2", 5126, 34962) src = portrait._hair_v if role == "hair" and getattr(portrait, "_hair_v", None) is not None \ and "_HAIR" in p["material"] else portrait.v raw = src.material_texture(p["mi"]) mat["pbrMetallicRoughness"]["baseColorTexture"] = dict(index=add_texture((id(src), p["mi"], role), raw)) if role == "hair": # strands are grey in the texture: tint them mat["pbrMetallicRoughness"]["baseColorFactor"] = color + [1.0] if role in ("hair", "line", "brow", "iris", "highlight"): mat["alphaMode"] = "MASK" mat["alphaCutoff"] = 0.5 else: mat["pbrMetallicRoughness"]["baseColorFactor"] = color + [1.0] if role in ("metal", "steel", "gold"): mat["pbrMetallicRoughness"].update(metallicFactor=0.6, roughnessFactor=0.35) materials.append(mat) meshes.append(dict(name=group_of(p, role), primitives=[dict( attributes=attrs, indices=add_acc(tri.reshape(-1), "SCALAR", 5125, 34963), material=len(materials) - 1)])) nodes.append(dict(name=f"{group_of(p, role)}/{p['material']}", mesh=len(meshes) - 1, extras=dict(group=group_of(p, role), role=role, world=bool(p.get("world")), material=p["material"]))) gltf = dict(asset=dict(version="2.0", generator="lowmarch export_glb"), scene=0, scenes=[dict(nodes=list(range(len(nodes))))], nodes=nodes, meshes=meshes, materials=materials, accessors=accessors, bufferViews=views, buffers=[dict(byteLength=len(bin_))], samplers=[dict(magFilter=9729, minFilter=9987)]) if textures: gltf.update(textures=textures, images=images) js = json.dumps(gltf, separators=(",", ":")).encode() js += b" " * ((4 - len(js) % 4) % 4) while len(bin_) % 4: bin_.append(0) out = struct.pack("<4sII", b"glTF", 2, 12 + 8 + len(js) + 8 + len(bin_)) out += struct.pack("<I4s", len(js), b"JSON") + js + struct.pack("<I4s", len(bin_), b"BIN\x00") + bytes(bin_) open(path, "wb").write(out) return len(out) # ---- data for the real-time viewer ---------------------------------------------------------- CAPSULES = [("J_Bip_C_Hips", "J_Bip_C_Spine"), ("J_Bip_C_Spine", "J_Bip_C_Chest"), ("J_Bip_C_Chest", "J_Bip_C_UpperChest"), ("J_Bip_C_UpperChest", "J_Bip_C_Neck"), ("J_Bip_C_Neck", "J_Bip_C_Head")] + \ [(f"J_Bip_{s}_{a}", f"J_Bip_{s}_{b}") for s in "LR" for a, b in (("UpperArm", "LowerArm"), ("LowerArm", "Hand"), ("UpperLeg", "LowerLeg"), ("LowerLeg", "Foot"))] # simulated cloth: piece -> (its static trims, hidden while the cloth simulates; the role whose colour # the simulated border band takes; the bones whose colliders it meets, None for all). Panels hanging # from the belt meet the legs only: the hip collider's round end pushes them out behind. LEGS = ("UpperLeg", "LowerLeg") CLOTH = {"OUTFIT_cape": (("OUTFIT_cape_trim", "OUTFIT_cape_hem", "OUTFIT_cape_edge0", "OUTFIT_cape_edge1"), "gold", None), "OUTFIT_tails": (("OUTFIT_tails_hem",), "gold", LEGS), "OUTFIT_tabard": (("OUTFIT_tabard_hem", "OUTFIT_tabard_edge0", "OUTFIT_tabard_edge1"), "heraldry", LEGS), "OUTFIT_tabard_back": (("OUTFIT_tabard_back_hem", "OUTFIT_tabard_back_edge0", "OUTFIT_tabard_back_edge1"), "heraldry", LEGS), "OUTFIT_skirt": (("OUTFIT_skirt_hem",), "heraldry", LEGS)} CLOTH_HEM_ONLY = {"OUTFIT_skirt"} # border band on the hem row only (its waist has a gold trim) views, accessors, meshes, gnodes, materials, textures, images, anims = [], [], [], [], [], [], [], [] tex_index = {} def add_view(data, target=None): while len(bin_) % 4: bin_.append(0) off = len(bin_) bin_.extend(data) vw = dict(buffer=0, byteOffset=off, byteLength=len(data)) if target: vw["target"] = target views.append(vw) return len(views) - 1 def add_acc(arr, kind, comp, target=None, minmax=False): arr = np.ascontiguousarray(arr) a = dict(bufferView=add_view(arr.tobytes(), target), componentType=comp, count=int(arr.shape[0]), type=kind) if minmax or (kind == "VEC3" and comp == 5126): a["min"] = np.atleast_1d(arr.min(0)).tolist() a["max"] = np.atleast_1d(arr.max(0)).tolist() accessors.append(a) return len(accessors) - 1 def add_texture(key, rgba, limit=None): if key in tex_index: return tex_index[key] im = Image.fromarray(rgba) lim = limit or max_tex if max(im.size) > lim: k = lim / max(im.size) im = im.resize((max(1, int(im.width * k)), max(1, int(im.height * k))), Image.LANCZOS) b = io.BytesIO() im.save(b, "PNG", optimize=True) images.append(dict(bufferView=add_view(b.getvalue()), mimeType="image/png")) textures.append(dict(source=len(images) - 1, sampler=0)) tex_index[key] = len(textures) - 1 return tex_index[key] # joint nodes node_of = {} for i in order: t, q, sc = _trs(local_ref[i]) node_of[i] = len(gnodes) gnodes.append(dict(name=nodes_v[i].get("name"), translation=t.tolist(), rotation=q.tolist(), scale=sc.tolist())) for i in order: kids = [node_of[c] for c in nodes_v[i].get("children", []) if c in joints] if kids: gnodes[node_of[i]]["children"] = kids roots = [node_of[i] for i in order if parent.get(i) not in joints] ibm = np.stack([np.linalg.inv(wref(i)).T for i in order]).astype(np.float32) # column-major skin = dict(joints=[node_of[i] for i in order], inverseBindMatrices=add_acc(ibm.reshape(-1, 16), "MAT4", 5126), skeleton=roots[0]) mesh_nodes = [] for k, (p, role) in enumerate(zip(portrait.prims, portrait.roles)): if role == "skip" or not len(p["tri"]) or p.get("world") or "J" not in p: continue usedv, inv = np.unique(p["tri"], return_inverse=True) pos = (p["pos"][usedv] * FLIP).astype(np.float32) nrm = p["nrm"][usedv] * FLIP nrm = (nrm / np.maximum(np.linalg.norm(nrm, axis=1, keepdims=True), 1e-9)).astype(np.float32) remap = np.array([jidx.get(n, jidx.get("J_Bip_C_Head")) for n in p["jnames"]]) J = remap[p["J"][usedv]].astype(np.uint16) W = p["W"][usedv].astype(np.float32) W /= np.maximum(W.sum(1, keepdims=True), 1e-9) attrs = dict(POSITION=add_acc(pos, "VEC3", 5126, 34962), NORMAL=add_acc(nrm, "VEC3", 5126, 34962), JOINTS_0=add_acc(J, "VEC4", 5123, 34962), WEIGHTS_0=add_acc(W, "VEC4", 5126, 34962)) if "plate" in p: # outline plates (tools/plates.py): border, engraving, gradient attrs["_PLATE"] = add_acc(np.asarray(p["plate"])[usedv].astype(np.float32), "VEC4", 5126, 34962) mat = dict(name=f"{group_of(p, role)}:{role}", doubleSided=True, pbrMetallicRoughness=dict(metallicFactor=0.0, roughnessFactor=0.9)) tone = ROLE_TONE.get(role) extra = getattr(portrait, "paint", None) or {} color = hex_rgb(palette[tone]) if tone in palette else \ hex_rgb(extra[role]) if isinstance(extra.get(role), str) else [0.8, 0.8, 0.8] if role in TEXTURED: src = portrait._hair_v if role == "hair" and getattr(portrait, "_hair_v", None) is not None \ and "_HAIR" in p["material"] else portrait.v raw = src.material_texture(p["mi"]) if p["mi"] >= 0 else None if raw is not None: attrs["TEXCOORD_0"] = add_acc(p["uv"][usedv].astype(np.float32), "VEC2", 5126, 34962) mat["pbrMetallicRoughness"]["baseColorTexture"] = dict(index=add_texture((id(src), p["mi"]), raw)) if role == "hair": mat["pbrMetallicRoughness"]["baseColorFactor"] = color + [1.0] if role in ("hair", "line", "brow", "iris", "highlight"): mat["alphaMode"], mat["alphaCutoff"] = "MASK", 0.5 else: mat["pbrMetallicRoughness"]["baseColorFactor"] = color + [1.0] else: mat["pbrMetallicRoughness"]["baseColorFactor"] = color + [1.0] if role in ("metal", "steel", "gold"): mat["pbrMetallicRoughness"].update(metallicFactor=0.6, roughnessFactor=0.35) if p.get("strands"): # kit hair (tools/hair.py): one shared strand texture import hair as _hair attrs["TEXCOORD_0"] = add_acc(np.asarray(p["uv"], np.float32)[usedv], "VEC2", 5126, 34962) mat["pbrMetallicRoughness"]["baseColorTexture"] = dict(index=add_texture(("strands",), _hair.STRANDS)) if "decal" in p: # a design in the piece's own coordinates (tools/plates.py Canvas) uvd = np.asarray(p["uv"], np.float32)[usedv].copy() uvd[:, 1] = 1 - uvd[:, 1] # the canvas draws t up; glTF's texture origin is the top left attrs["TEXCOORD_0"] = add_acc(uvd, "VEC2", 5126, 34962) mat["pbrMetallicRoughness"]["baseColorTexture"] = dict(index=add_texture(("decal", k), p["decal"], limit=1024)) mat["extras"] = dict(decal=True) materials.append(mat) meshes.append(dict(name=group_of(p, role), primitives=[dict(attributes=attrs, indices=add_acc(inv.reshape(-1).astype(np.uint32), "SCALAR", 5125, 34963), material=len(materials) - 1)])) mesh_nodes.append(len(gnodes)) gnodes.append(dict(name=f"{group_of(p, role)}/{p['material']}", mesh=len(meshes) - 1, skin=0, extras=dict(group=group_of(p, role), role=role, material=p["material"]))) # animations for name, keys in (clips or {}).items(): times = np.array([t for t, _ in keys], np.float32) tacc = add_acc(times.reshape(-1, 1), "SCALAR", 5126, minmax=True) 2026-10-30T13:06:06+08:00exec_commandsource ~/Projects/lowmarch-bench/kit/env-c.sh && python3 - <<'PY'
command
source ~/Projects/lowmarch-bench/kit/env-c.sh && python3 - <<'PY' import os,json,struct,base64,numpy as np from pathlib import Path for p in sorted((Path(os.environ['WORK'])/'exb').glob('*/arm_f_slender.glb.json')): b=base64.b64decode(json.loads(p.read_text())['glb']);l,_=struct.unpack_from('<II',b,12);j=json.loads(b[20:20+l]);base=20+l+8 for m in j['meshes']: for prim in m['primitives']: if j['materials'][prim['material']]['name']=='body:mouth': a=j['accessors'][prim['attributes']['POSITION']];v=j['bufferViews'][a['bufferView']];o=base+v.get('byteOffset',0)+a.get('byteOffset',0);z=np.frombuffer(b,dtype='<f4',count=a['count']*3,offset=o).reshape(-1,3);print(p.parent.name, np.round(np.ptp(z,axis=0),4),np.round(np.mean(z,axis=0),4));break PYresult, 18 lines
Script completed Wall time 1.4 seconds Output: angry [0.0443 0.0241 0.0355] [0. 1.4676 0.1098] joy [0.0498 0.0322 0.0394] [0. 1.4661 0.107 ] lip_A [0.0444 0.0382 0.0376] [0. 1.4656 0.1083] lip_E [0.0608 0.0256 0.0383] [0. 1.4668 0.107 ] lip_I [0.0546 0.027 0.0363] [0. 1.4669 0.1082] lip_O [0.0471 0.0369 0.0451] [0. 1.4647 0.105 ] lip_U [0.0277 0.0334 0.0338] [0. 1.4654 0.1117] neutral [0.0443 0.0241 0.0354] [0. 1.4677 0.1098] sad [0.038 0.0373 0.0374] [0. 1.4648 0.1089] shout [0.0444 0.0382 0.0376] [0. 1.4656 0.1083] smile [0.055 0.0241 0.0367] [0. 1.4691 0.1085] smug [0.0443 0.024 0.0354] [0. 1.474 0.1107] surprised [0.0462 0.033 0.0416] [0. 1.4656 0.1064] wink [0.055 0.0241 0.0367] [0. 1.4691 0.1085]
exec_commandsource ~/Projects/lowmarch-bench/kit/env-c.sh && python3 - <<'PY'
command
source ~/Projects/lowmarch-bench/kit/env-c.sh && python3 - <<'PY' import json,os from pathlib import Path for expr in ('neutral','angry','sad','smile','lip_A'): j=json.loads((Path(os.environ['WORK'])/'exb'/expr/'camera.json').read_text());c=next(x for x in j['characters'] if x['name']=='arm_f_slender');a=c['info']['mouth_curve'];print(expr,len(a) if a else 0, [a[0],a[len(a)//2],a[-1]] if a else None) PYresult, 9 lines
Script completed Wall time 0.5 seconds Output: neutral 24 [[-0.014581488773168689, 1.4643919402481076, 0.12312928945980658], [1.0896176690847992e-05, 1.464114095079355, 0.12532450292923122], [0.014572115832557044, 1.464395772930578, 0.12313205710773222]] angry 24 [[-0.011696772227902373, 1.4596187281255044, 0.12454405745112222], [1.0642881233981287e-05, 1.462608548886009, 0.12578639505172437], [0.011696882895042464, 1.459618368882468, 0.12454408656044441]] sad 24 [[-0.009240951702354235, 1.4607284756056977, 0.12307793045740822], [1.1171731091375958e-05, 1.4652834835698696, 0.12672959552319374], [0.009240366874039329, 1.4607172635529218, 0.12307494159479648]] smile 24 [[-0.017938125235338997, 1.4664554797878184, 0.12210636852048233], [1.0896619202988065e-05, 1.4648401142629983, 0.12489365955721478], [0.01795255891606419, 1.4664489245431351, 0.122101755939408]] lip_A 24 [[-0.01791961148607161, 1.4659682461797463, 0.11735763784853463], [1.207264400251487e-05, 1.4712123524094713, 0.12562198844084532], [0.017943145903356446, 1.4659543792941523, 0.11734879507154458]]
agent
+22:04The contrast test is affecting the mouth texture, but the frown’s center remains thin because the deformed texture compresses it. I’m checking whether the existing exported mouth curve can provide a more reliable line without affecting open mouths or the default face.
write_stdinpoll session 18792
input
const r=await tools.write_stdin({session_id:18792,chars:"",yield_time_ms:50000,max_output_tokens:2000});text(JSON.stringify(r));result, 4 lines
Script completed Wall time 10.2 seconds Output: {"chunk_id":"7b346b","wall_time_seconds":10.165574461,"exit_code":143,"original_token_count":0,"output":""}exec_commandsource ~/Projects/lowmarch-bench/kit/env-c.sh && bash "$WORK/shoot_resume.sh" "$LOWMARCH_REPO/review/vroid-02/index.html" r3 && "$KIT/sheets.sh" r3 'Round 3: clearer lip line and closed-eye cleanup' && date -Is
command
source ~/Projects/lowmarch-bench/kit/env-c.sh && bash "$WORK/shoot_resume.sh" "$LOWMARCH_REPO/review/vroid-02/index.html" r3 && "$KIT/sheets.sh" r3 'Round 3: clearer lip line and closed-eye cleanup' && date -Is
result, 4 lines
Script completed Wall time 1.2 seconds Output: {"chunk_id":"ff1c1c","wall_time_seconds":1.000410454,"session_id":21889,"original_token_count":0,"output":""}view_image~/Projects/lowmarch-bench/work/c/shot_r3/joy/painted_arm_m_youth.png
input
const im=await tools.view_image({path:"~/Projects/lowmarch-bench/work/c/shot_r3/joy/painted_arm_m_youth.png",detail:"original"});image(im.image_url);result, 4 lines
Script completed Wall time 0.1 seconds Output:
write_stdinpoll session 21889
input
const r=await tools.write_stdin({session_id:21889,chars:"",yield_time_ms:50000,max_output_tokens:3000});text(JSON.stringify(r));result, 4 lines
Script running with cell ID 74 Wall time 31.0 seconds Output:
waitcell 74
input
{ "cell_id": "74", "yield_time_ms": 20000 }result, 4 lines
Script completed Wall time 14.2 seconds Output: {"chunk_id":"0d6ed8","wall_time_seconds":50.000640279,"session_id":21889,"original_token_count":102,"output":"arm_f_slender ~/Projects/lowmarch-bench/work/c/shot_r3/lip_I/painted_arm_f_slender.png 14s\narm_m_youth ~/Projects/lowmarch-bench/work/c/shot_r3/lip_I/painted_arm_m_youth.png 10s\narm_f_slender ~/Projects/lowmarch-bench/work/c/shot_r3/lip_O/painted_arm_f_slender.png 14s\narm_m_youth ~/Projects/lowmarch-bench/work/c/shot_r3/lip_O/painted_arm_m_youth.png 12s\n"}write_stdinpoll session 21889
input
const r=await tools.write_stdin({session_id:21889,chars:"",yield_time_ms:50000,max_output_tokens:2000});text(JSON.stringify(r));result, 4 lines
Script running with cell ID 75 Wall time 31.0 seconds Output:







