From 66cfb76568f5d20622cfd232056a33341d1c2196 Mon Sep 17 00:00:00 2001 From: Licini Date: Thu, 3 Jul 2025 14:34:42 +0200 Subject: [PATCH] add support for scene --- requirements.txt | 3 +- scripts/scene.py | 22 ++++++ src/compas_usd/conversions/__init__.py | 2 + src/compas_usd/conversions/geometry.py | 4 +- src/compas_usd/conversions/scene.py | 102 +++++++++++++++++++++++++ 5 files changed, 130 insertions(+), 3 deletions(-) create mode 100644 scripts/scene.py create mode 100644 src/compas_usd/conversions/scene.py diff --git a/requirements.txt b/requirements.txt index f48a940..87eac41 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,2 @@ -usd-core \ No newline at end of file +usd-core +compas >= 2.0 \ No newline at end of file diff --git a/scripts/scene.py b/scripts/scene.py new file mode 100644 index 0000000..fd4af31 --- /dev/null +++ b/scripts/scene.py @@ -0,0 +1,22 @@ +import compas +from compas.scene import Scene +from compas.geometry import Box, Sphere, Translation +from compas.datastructures import Mesh +from compas_usd.conversions import stage_from_scene + +scene = Scene() +group1 = scene.add_group(name="Boxes") +group2 = scene.add_group(name="Spheres", transformation=Translation.from_vector([0, 5, 0])) + +mesh = Mesh.from_obj(compas.get("tubemesh.obj")) +mesh.flip_cycles() +scene.add(mesh, name="MeshObj", transformation=Translation.from_vector([-5, 0, 0])) + +for i in range(5): + group1.add(Box(0.5), name=f"Box{i}", transformation=Translation.from_vector([i, 0, 0])) + group2.add(Sphere(0.5), name=f"Sphere{i}", transformation=Translation.from_vector([i, 0, 0])) + +print(scene) + +stage = stage_from_scene(scene, "temp/scene.usda") +print(stage) \ No newline at end of file diff --git a/src/compas_usd/conversions/__init__.py b/src/compas_usd/conversions/__init__.py index 7ab3a5d..ab14f43 100644 --- a/src/compas_usd/conversions/__init__.py +++ b/src/compas_usd/conversions/__init__.py @@ -20,6 +20,7 @@ apply_rotate_and_translate_on_prim, frame_and_scale_from_prim, ) +from .scene import stage_from_scene __all__ = [ "prim_from_box", @@ -36,4 +37,5 @@ "apply_transformation_on_prim", "apply_rotate_and_translate_on_prim", "frame_and_scale_from_prim", + "stage_from_scene", ] diff --git a/src/compas_usd/conversions/geometry.py b/src/compas_usd/conversions/geometry.py index 0208ded..693770b 100644 --- a/src/compas_usd/conversions/geometry.py +++ b/src/compas_usd/conversions/geometry.py @@ -1,7 +1,7 @@ from pxr import UsdGeom from compas.geometry import Frame from compas.geometry import Box -from compas.utilities import flatten +from compas.itertools import flatten from compas.geometry import transpose_matrix from .transformations import apply_rotate_and_translate_on_prim @@ -76,7 +76,7 @@ def prim_from_sphere(stage, path, sphere): """ prim = UsdGeom.Sphere.Define(stage, path) prim.GetPrim().GetAttribute("radius").Set(sphere.radius) - UsdGeom.XformCommonAPI(prim).SetTranslate(tuple(sphere.point)) + UsdGeom.XformCommonAPI(prim).SetTranslate(tuple(sphere.frame.point)) return prim diff --git a/src/compas_usd/conversions/scene.py b/src/compas_usd/conversions/scene.py new file mode 100644 index 0000000..4ff230b --- /dev/null +++ b/src/compas_usd/conversions/scene.py @@ -0,0 +1,102 @@ +from compas.scene import Scene +from compas.scene import SceneObject +from compas.data import Data +from compas.geometry import Box +from compas.geometry import Sphere +from compas.geometry import Transformation +from compas.datastructures import Mesh +from compas_usd.conversions import prim_from_transformation +from compas_usd.conversions import prim_from_box +from compas_usd.conversions import prim_from_sphere +from compas_usd.conversions import prim_from_mesh + +from pxr import Usd, UsdGeom + + +def stage_from_scene(scene: Scene, file_path: str) -> Usd.Stage: + """ + Converts a :class:`compas.scene.Scene` to a USD stage. + + Parameters + ---------- + scene : :class:`compas.scene.Scene` + The scene to convert. + file_path : str + The file path to the USD stage. + + Returns + ------- + :class:`pxr.Usd.Stage` + The USD stage. + """ + stage = Usd.Stage.CreateNew(file_path) + UsdGeom.SetStageUpAxis(stage, UsdGeom.Tokens.z) + for obj in scene.root.children: + prim_from_sceneobject(stage, obj, parent_path=[scene.name]) + + stage.Save() + return stage + + +def prim_from_sceneobject(stage: Usd.Stage, sceneobject: SceneObject, parent_path=[]) -> Usd.Prim: + """ + Converts a :class:`compas.scene.SceneObject` to a USD prim. + + Parameters + ---------- + stage : :class:`pxr.Usd.Stage` + The USD stage. + sceneobject : :class:`compas.scene.SceneObject` + The scene object to convert. + parent_path : list[str], optional + The path to the parent prim. + + Returns + ------- + :class:`pxr.Usd.Prim` + The USD prim. + """ + path = parent_path + [f"{sceneobject.name}"] + + transformation = ( + sceneobject.transformation + if sceneobject.transformation is not None + else Transformation() + ) + + prim = prim_from_transformation(stage, "/" + "/".join(path), transformation) + if sceneobject.item is not None: + prim_from_item(stage, sceneobject.item, parent_path=path) + + for child in sceneobject.children: + prim_from_sceneobject(stage, child, parent_path=path) + + return prim + + +def prim_from_item(stage: Usd.Stage, item: Data, parent_path=[]) -> Usd.Prim: + """ + Converts a :class:`compas.scene.SceneObject` to a USD prim. + + Parameters + ---------- + stage : :class:`pxr.Usd.Stage` + The USD stage. + item : :class:`compas.scene.SceneObject` + The item to convert. + parent_path : list[str], optional + The path to the parent prim. + + Returns + ------- + :class:`pxr.Usd.Prim` + The USD prim. + """ + path = parent_path + [f"{item.name}"] + if isinstance(item, Box): + prim = prim_from_box(stage, "/" + "/".join(path), item) + elif isinstance(item, Sphere): + prim = prim_from_sphere(stage, "/" + "/".join(path), item) + elif isinstance(item, Mesh): + prim = prim_from_mesh(stage, "/" + "/".join(path), item) + return prim