| |
| """Extract the canonical in-game voxel render rig into a standalone JS file. |
| |
| The app's render rig lives in `src/buddy_fusion/voxel_embed.py` as the |
| `VOXEL_SETUP_JS` string (Three.js scene, studio lighting, real shadows, ACES |
| tone mapping, turntable, the `window.voxel*` builders, and `setupVoxel` which |
| boots any `.buddy-voxel-root` from its `.buddyVoxelConfig` JSON). The Tools |
| report pages (`tools/parts-catalog/`, `tools/compendium/`) render live 3D |
| creatures with that exact rig instead of static thumbnails. |
| |
| Rather than copy-paste (and drift), this script imports `voxel_embed`, grabs |
| `VOXEL_SETUP_JS` verbatim, and writes `tools/shared/voxel-rig.js` with a |
| generated-file header plus a standalone boot (observer + initial scan + a cheap |
| retry poll). The app render path in voxel_embed.py is NOT touched. |
| |
| Run: python tools/shared/sync_voxel_rig.py |
| """ |
| from __future__ import annotations |
|
|
| import sys |
| from pathlib import Path |
|
|
| ROOT = Path(__file__).resolve().parents[2] |
| sys.path.insert(0, str(ROOT / "src")) |
|
|
| from buddy_fusion import voxel_embed |
|
|
| OUT = Path(__file__).resolve().parent / "voxel-rig.js" |
|
|
| HEADER = """\ |
| // GENERATED FILE — do not edit by hand. |
| // Source of truth: src/buddy_fusion/voxel_embed.py (VOXEL_SETUP_JS). |
| // Regenerate with: python tools/shared/sync_voxel_rig.py |
| // |
| // This is the production in-game voxel render rig (Three.js scene, studio |
| // lighting, real shape-based shadows, ACES tone mapping, turntable, OrbitControls, |
| // and the window.voxel* builders). Booting it scans the document for |
| // .buddy-voxel-root nodes and renders each from its .buddyVoxelConfig JSON. |
| |
| """ |
|
|
| |
| |
| |
| |
| BOOT = """ |
| |
| // --- standalone boot (reports only; app uses its own VOXEL_JS_ON_LOAD) --- |
| // A page with many cards sets window.__VOXEL_NO_AUTOBOOT before loading this rig |
| // and drives setupVoxel itself through an IntersectionObserver context-cap |
| // manager (one live WebGL context per visible card would blow the browser's |
| // ~16-context limit). Pages with a handful of cards just let it auto-boot. |
| if (!window.__VOXEL_NO_AUTOBOOT) { |
| installVoxelObserver(); |
| scanAndSetupVoxels(); |
| setInterval(scanAndSetupVoxels, 400); |
| } |
| """ |
|
|
|
|
| def main() -> None: |
| js = voxel_embed.VOXEL_SETUP_JS |
| for name in ("setupVoxel", "voxelsFromBoxes", "voxelBuildMesh", |
| "installVoxelObserver", "scanAndSetupVoxels"): |
| if name not in js: |
| raise SystemExit(f"VOXEL_SETUP_JS is missing expected symbol: {name}") |
| OUT.write_text(HEADER + js + BOOT, encoding="utf-8") |
| print(f"wrote {OUT} ({len(OUT.read_text(encoding='utf-8'))} bytes)") |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|