isaac-sim / OmniIsaacGymEnvs

Reinforcement Learning Environments for Omniverse Isaac Gym
Other
848 stars 218 forks source link

Creating An Instanceable USD Assets #55

Open Zenif-NIght opened 1 year ago

Zenif-NIght commented 1 year ago

Issue: Problem with converting USD files to Instanceable Assets

Description

I am encountering an issue while attempting to convert normal USD files to Instanceable USD Assets. I have followed the provided guide and tried using the URDF file and the converter, but I have been unsuccessful in achieving the desired results.

Background

My first attempt at converting the files was to use the URDF file and the provided converter. However, the resulting go1.py and go1.usd files, which can be found here and here, respectively, seemed to be fixed in space in some way. I also tried a different approach using the larger NVIDIA USD for go1 file from this location it works, but is too large to work with. I need to use Instanceable USD Assets for my project.

I have reached out on Discord and asked a question regarding this issue, which you can find here.

Issue Details

I am new to creating USD files, and I attempted to create an Instanceable Go1 asset following the guide provided here. Unfortunately, I did not achieve the desired outcome.

I downloaded the original go1.usd file from Isaac Sim, and after running the code provided in the create_instanceable_assets.py script, I obtained go1_instanceable.usd and go1_instanceable_meshes.usd files. However, both of these files have the same size as the original Go1.usd file, which indicates that the conversion process did not work as intended.

image

To convert the asset to an Instanceable Asset, I ran the following code in the Script Editor:


    import omni.usd
    import omni.client

    from pxr import UsdGeom, Sdf

    def update_reference(source_prim_path, source_reference_path, target_reference_path):
        stage = omni.usd.get_context().get_stage()

        prims = [stage.GetPrimAtPath(source_prim_path)]
        while len(prims) > 0:
            prim = prims.pop(0)
            prim_spec = stage.GetRootLayer().GetPrimAtPath(prim.GetPath())
            reference_list = prim_spec.referenceList
            refs = reference_list.GetAddedOrExplicitItems()
            if len(refs) > 0:
                for ref in refs:
                    if ref.assetPath == source_reference_path:
                        prim.GetReferences().RemoveReference(ref)
                        prim.GetReferences().AddReference(assetPath=target_reference_path, primPath=prim.GetPath())

            prims = prims + prim.GetChildren()

    def create_parent_xforms(asset_usd_path, source_prim_path, save_as_path=None):
        """ Adds a new UsdGeom.Xform prim for each Mesh/Geometry prim under source_prim_path.
            Moves material assignment to new parent prim if any exists on the Mesh/Geometry prim.

            Args:
                asset_usd_path (str): USD file path for asset
                source_prim_path (str): USD path of root prim
                save_as_path (str): USD file path for modified USD stage. Defaults to None, will save in same file.
        """
        omni.usd.get_context().open_stage(asset_usd_path)
        stage = omni.usd.get_context().get_stage()

        prims = [stage.GetPrimAtPath(source_prim_path)]
        edits = Sdf.BatchNamespaceEdit()
        while len(prims) > 0:
            prim = prims.pop(0)
            print(prim)
            if prim.GetTypeName() in ["Mesh", "Capsule", "Sphere", "Box"]:
                new_xform = UsdGeom.Xform.Define(stage, str(prim.GetPath()) + "_xform")
                print(prim, new_xform)
                edits.Add(Sdf.NamespaceEdit.Reparent(prim.GetPath(), new_xform.GetPath(), 0))
                continue

            children_prims = prim.GetChildren()
            prims = prims + children_prims

        stage.GetRootLayer().Apply(edits)

        if save_as_path is None:
            omni.usd.get_context().save_stage()
        else:
            omni.usd.get_context().save_as_stage(save_as_path)

    def convert_asset_instanceable(asset_usd_path, source_prim_path, save_as_path=None, create_xforms=True):
        """ Makes all mesh/geometry prims instanceable.
            Can optionally add UsdGeom.Xform prim as parent for all mesh/geometry prims.
            Makes a copy of the asset USD file, which will be used for referencing. 
            Updates asset file to convert all parent prims of mesh/geometry prims to reference cloned USD file.

            Args:
                asset_usd_path (str): USD file path for asset
                source_prim_path (str): USD path of root prim
                save_as_path (str): USD file path for modified USD stage. Defaults to None, will save in same file.
                create_xforms (bool): Whether to add new UsdGeom.Xform prims to mesh/geometry prims.
        """

        if create_xforms:
            create_parent_xforms(asset_usd_path, source_prim_path, save_as_path)
            asset_usd_path = save_as_path

        instance_usd_path = ".".join(asset_usd_path.split(".")[:-1]) + "_meshes.usd"
        omni.client.copy(asset_usd_path, instance_usd_path)
        omni.usd.get_context().open_stage(asset_usd_path)
        stage = omni.usd.get_context().get_stage()

        prims = [stage.GetPrimAtPath(source_prim_path)]
        while len(prims) > 0:
            prim = prims.pop(0)
            if prim:
                if prim.GetTypeName() in ["Mesh", "Capsule", "Sphere", "Box"]:
                    parent_prim = prim.GetParent()
                    if parent_prim and not parent_prim.IsInstance():
                        parent_prim.GetReferences().AddReference(assetPath=instance_usd_path, primPath=str(parent_prim.GetPath()))
                        parent_prim.SetInstanceable(True)
                        continue

                children_prims = prim.GetChildren()
                prims = prims + children_prims

        if save_as_path is None:
            omni.usd.get_context().save_stage()
        else:
            omni.usd.get_context().save_as_stage(save_as_path)

    ASSET_USD_PATH = "/home/ctaw/.local/share/ov/pkg/isaac_sim-2022.2.1/GO1_USD/go1.usd"
    SOURCE_PRIM_PATH = "/go1_description"
    SAVE_AS_PATH = "/home/ctaw/.local/share/ov/pkg/isaac_sim-2022.2.1/GO1_USD/go1_instanceable.usd"

    convert_asset_instanceable(
        asset_usd_path=ASSET_USD_PATH, 
        source_prim_path=SOURCE_PRIM_PATH, 
        save_as_path=SAVE_AS_PATH,
        create_xforms=True
    )

Steps to Reproduce

Download the necessary files and dependencies:

  1. Download the go1.usd file from Isaac Sim [link to the file].
  2. Copy and paste the code snippet from the create_instanceable_assets.py script into the editor.
  3. Modify the code snippet with the correct file paths:
  4. Update the ASSET_USD_PATH variable with the correct path to the go1.usd file.
  5. Ensure that the SOURCE_PRIM_PATH variable is set to the correct USD path of the root prim.
  6. Run the code
  7. Check the directory where the conversion script was run and verify that the go1_instanceable.usd and go1_instanceable_meshes.usd files have been created.
  8. Compare the file sizes: Check the file sizes of the resulting instanceable USD files (go1_instanceable.usd and go1_instanceable_meshes.usd).

Additional Information

Ubuntu 20.04.6 LTS, CPU: AMD Ryzen 9 5900X 12-Core Processor, Mem: 62GB, NVIDIA GeForce RTX 3090

kellyguo11 commented 1 year ago

Hi there, it is expected that the two files are of the same size as the original file. The utility script is only updating the references of the meshes of the original asset and marking certain objects in the file as instanceable. This will be processed by the USD framework when loaded in Isaac Sim, which will reduce the memory consumption required for the asset.