83 lines
2.4 KiB
Python
83 lines
2.4 KiB
Python
"""Blender headless scene validation (collections and naming).
|
|
|
|
Usage (from repo root):
|
|
blender -b sensors/finger/r01/source/geometry.blend -P blender/scripts/validate.py -- sensors/finger/r01
|
|
|
|
Note: this script loads design.yaml with PyYAML. Prefer installing PyYAML into
|
|
Blender's Python, or run metadata-only checks via: orisys-design validate <revision>
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import bpy
|
|
|
|
try:
|
|
import yaml
|
|
except ImportError:
|
|
yaml = None
|
|
|
|
|
|
def _argv_after_double_dash() -> list[str]:
|
|
if "--" not in sys.argv:
|
|
return []
|
|
return sys.argv[sys.argv.index("--") + 1 :]
|
|
|
|
|
|
def _load_design(revision_dir: Path) -> dict:
|
|
if yaml is None:
|
|
raise RuntimeError(
|
|
"PyYAML is not installed in Blender's Python. "
|
|
"Run: orisys-design validate <revision> without --blender, "
|
|
"or pip install PyYAML into Blender's Python."
|
|
)
|
|
with (revision_dir / "design.yaml").open(encoding="utf-8") as handle:
|
|
data = yaml.safe_load(handle)
|
|
if not isinstance(data, dict):
|
|
raise ValueError("design.yaml must be a mapping")
|
|
return data
|
|
|
|
|
|
def main() -> int:
|
|
args = _argv_after_double_dash()
|
|
if not args:
|
|
print("validate.py: pass revision directory after --", file=sys.stderr)
|
|
return 2
|
|
|
|
revision_dir = Path(args[0]).resolve()
|
|
try:
|
|
design = _load_design(revision_dir)
|
|
except Exception as exc:
|
|
print(f"ERROR: {exc}", file=sys.stderr)
|
|
return 1
|
|
|
|
collections = design.get("collections", {})
|
|
objects = design.get("objects", {})
|
|
|
|
errors: list[str] = []
|
|
present = {col.name for col in bpy.data.collections}
|
|
|
|
for key, expected_name in collections.items():
|
|
if expected_name not in present:
|
|
errors.append(f"Missing collection {expected_name!r} (design key: {key})")
|
|
|
|
for key, expected_name in objects.items():
|
|
obj = bpy.data.objects.get(expected_name)
|
|
if obj is None:
|
|
errors.append(f"Missing object {expected_name!r} (design key: {key})")
|
|
|
|
if errors:
|
|
for message in errors:
|
|
print(f"ERROR: {message}", file=sys.stderr)
|
|
print(f"Present collections: {sorted(present)}", file=sys.stderr)
|
|
return 1
|
|
|
|
print(f"OK: Blender scene matches conventions for {revision_dir.name}")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|