113 lines
3.9 KiB
Python
113 lines
3.9 KiB
Python
"""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())
|