# Scene asset tools Offline tools for the three-dimensional view. They never run in the application: the app loads a **compiled** package (`.oryscene`) and nothing else. | tool | what it is for | |---|---| | `compile_scene.py` | a Blender export (+ optional housing GLB) → one `.oryscene` package | | `validate_scene.py` | the checks the loader and the compiler must agree on; the release gate | | `probe_gl.py` | what this machine's OpenGL actually is (exit 0 GPU, 3 software, 4 no context) | | `bench_scene.py` | per-stage p50/p95 of the running view (`--cpu-only` for the pure CPU cost) | | `soak_scene.py` | long run: RSS trend and the queue bound | Run them with the repository's own interpreter, from `orisys-app`: ```powershell $py = '.venv-build\Scripts\python.exe' ``` ## Compiling the shipped finger sensor The **runtime** asset is `sensors/finger/assets/30fps-0703`: it is what `sensors/finger/manifest.json` names (`assets_dir`), and its unroll map (`unroll_map_uv_s0.4.npz`, out size 300×280, `source_scale` 0.4) matches `camera.resolution_stitch` in `sensor.json`, which is the grid the live optical flow is computed on. Do **not** compile `assets/visualization/flatten_remap.npz`: it is a 1024×1024 domain, so every live frame would be refused with `field_domain_mismatch` (300x280 vs 1024x1024). That map belongs to offline visualisation. ```powershell $sdk = 'D:\orisys_ws\orisys-sdk\sensors\finger\assets\30fps-0703' # or the copy in apps/studio/config # 1) state the unit (the importer never guesses it) and read what it measured & $py tools\scene_assets\validate_scene.py --legacy-dir $sdk --map "$sdk\unroll_map_uv_s0.4.npz" # 2) compile & $py tools\scene_assets\compile_scene.py ` --legacy-dir $sdk ` --map "$sdk\unroll_map_uv_s0.4.npz" ` --meters-per-source-unit 0.001 ` --output apps\studio\config\sensors\finger\scene\finger-display-1.0.0.oryscene # 3) verify & $py tools\scene_assets\validate_scene.py apps\studio\config\sensors\finger\scene\finger-display-1.0.0.oryscene ``` **`--meters-per-source-unit` is a physical statement about the sensor, not a formatting choice.** The measured mesh extent is 15.81 × 15.0 × 7.5 source units (1032 vertices, 1974 triangles); the shipped finger surface is about 15.8 mm across, so one source unit is one millimetre and the value is `0.001`. If the mechanical drawing says otherwise, state that number instead — nothing downstream can tell the difference, and a wrong factor looks plausible. Expected result: `1032 vertices`, `1974 triangles`, `geometry_id` starting `1a2e0e92…`, a 56 KB package, and `validate_scene.py` reporting every check `ok`. ## Where the application looks for a package 1. `preference.json` → `"scene3d_package": ""` (an absolute path; also the way to point a developer build at a freshly compiled package); 2. `/sensors//scene/*.oryscene` — for the studio app that is `apps/studio/config/sensors/finger/scene/`; 3. nothing: the pane reports the directory it searched and the compile command above, and the two-dimensional views keep working. The SDK's own `sensors/` tree is **not** searched (the app prefers its local `config/sensors` bundles). Compile into the app's `scene/` directory, or set `scene3d_package`. ## Release packages (signature, optional encryption, platform lock) All three are optional and independent of the plain package above: ```powershell & $py tools\scene_assets\compile_scene.py --legacy-dir $sdk --map "$sdk\unroll_map_uv_s0.4.npz" ` --meters-per-source-unit 0.001 ` --sign-key keys\build-ed25519.pem --sign-key-id build-2024 ` --write-verify-key keys\build-ed25519.pub.pem ` --encrypt-key-id finger-display --key-file keys\finger-display.key ` --lock-from-probe docs\architecture\gl-report-windows-dev.json ` --output apps\studio\config\sensors\finger\scene\finger-display-1.0.0.oryscene ``` - `--sign-key` signs the manifest (and therefore every entry hash, the compatibility information and the platform lock). The private key stays on the build machine; the client ships only `--write-verify-key`'s public half. - `--encrypt-key-id` + `--key-file` additionally seal the content with AES-256-GCM. Encryption never replaces signing. Reading a sealed package needs the `protected-assets` extra (`cryptography`); a plain package does not. - `--lock-from-probe` takes the platform lock (OS, architecture, GL profile, vendor, renderer, minimum texture size, "no software rasteriser") from a `probe_gl.py` report. A machine the lock excludes loses the 3D pane, with the reason, and keeps everything else. - `--encrypt-key-id` without `--key-file`, or with no crypto backend installed, is **refused** rather than silently writing a plain package that looks protected. Release gate: ```powershell & $py tools\scene_assets\validate_scene.py ` --key-file keys\finger-display.key --key-id finger-display ` --verify-key keys\build-ed25519.pub.pem --require-signature ``` ## Performance and long runs ```powershell # per-stage p50/p95 of the running view (needs a real window system) $env:QT_QPA_PLATFORM = 'windows' & $py tools\scene_assets\bench_scene.py --frames 600 --output perf.json & $py tools\scene_assets\bench_scene.py --cpu-only --frames 500 --vertices 20000 --output perf-cpu.json & $py tools\scene_assets\bench_scene.py --package --key-file keys\finger-display.key ` --key-id finger-display --verify-key keys\build-ed25519.pub.pem --output perf-real.json # long run: the release gate is two hours in the GUI mode (it is the stricter one) & $py tools\scene_assets\soak_scene.py --minutes 120 --fps 30 --output soak.json & $py tools\scene_assets\soak_scene.py --no-window --minutes 10 --fps 60 --output soak-cpu.json ``` The committed reports for the developer machine are `docs/architecture/perf-report-windows-dev.json` and `docs/architecture/soak-report-windows-dev.json`; the stage-by-stage acceptance, the failure samples and the compatibility notes are in `docs/architecture/studio-3d-stages.md`.