"""Compile a legacy export into a runtime package (stage 2 of the 3D plan). Offline only: this is the tool that bakes the coordinate transform, the unit scale, the UV sampling table and the identity hashes into one file, so the runtime never repeats that work. Units are an argument, never a default. The legacy mesh is 15.8 x 15.0 x 7.5 source units; whether one of those is a millimetre is a physical claim about the sensor, and ``validate_scene.py`` prints the measured candidates so a person can decide. Example:: python tools/scene_assets/compile_scene.py \\ --legacy-dir sensors/finger/assets/30fps-0703 \\ --map sensors/finger/assets/30fps-0703/unroll_map_uv_s0.4.npz \\ --meters-per-source-unit 0.001 \\ --output build/finger-display-1.0.0.oryscene """ from __future__ import annotations import argparse import json import sys from pathlib import Path def _bootstrap_paths() -> None: here = Path(__file__).resolve() repo = here.parents[2] for path in (repo / "apps" / "studio" / "src", repo / "shared" / "src"): if path.is_dir() and str(path) not in sys.path: sys.path.insert(0, str(path)) def main(argv: list[str] | None = None) -> int: parser = argparse.ArgumentParser(description=__doc__.splitlines()[0]) parser.add_argument("--legacy-dir", type=Path, required=True, help="legacy NPZ export") parser.add_argument("--map", type=Path, required=True, help="field map (npz)") parser.add_argument( "--meters-per-source-unit", type=float, required=True, help="stated unit scale (the importer never guesses it)", ) parser.add_argument("--output", type=Path, required=True, help="package to write") parser.add_argument("--asset-id", default="finger-display") parser.add_argument("--version", default="1.0.0") parser.add_argument("--lod-id", default="lod0") parser.add_argument("--sensor-model", default="finger") parser.add_argument( "--transform", type=Path, default=None, help="optional 4x4 rigid transform (npy) from source to sensor coordinates", ) parser.add_argument("--json", action="store_true") args = parser.parse_args(argv) _bootstrap_paths() import numpy as np from orisys_app_shared.scene3d.contracts import ThreeDError from orisys_app_shared.scene3d.legacy import LegacyFingerImporter from orisys_app_shared.scene3d.package import write_package transform = ( np.load(args.transform, allow_pickle=False) if args.transform is not None else np.eye(4) ) try: assets = LegacyFingerImporter().convert( args.legacy_dir, args.map, transform_sensor_from_source=transform, meters_per_source_unit=args.meters_per_source_unit, asset_id=args.asset_id, asset_version=args.version, lod_id=args.lod_id, ) report = write_package( assets, args.output, sensor_model=args.sensor_model, hardware_revisions=("*",), ) except ThreeDError as exc: print(f"rejected: {exc.diagnostic.code}: {exc.diagnostic.message}", file=sys.stderr) return 2 summary = { "status": "ok", "output": str(report.output), "asset_hash": report.asset_hash, "vertices": report.part_vertex_counts, "triangles": report.part_triangle_counts, "geometry_id": assets.geometry_id, "field_domain_id": assets.field_domain_id, } if args.json: print(json.dumps(summary, indent=2, ensure_ascii=False)) else: print( f"wrote {report.output} vertices={report.part_vertex_counts} " f"triangles={report.part_triangle_counts}" ) print(f" geometry_id={assets.geometry_id}") print(f" asset_hash={report.asset_hash}") return 0 if __name__ == "__main__": sys.exit(main())