Installation#

Python Package#

As of Kit 106.0, the application is available via python package.

pip install omniverse-kit

Kit App Template#

As of kit 106.0 release this extension can be called from one of the Sample App Templates. Installing the base kit_base_editor template will work for running the command line version of Scene Optimizer as dependencies will be pulled on first launch of the CLI command.

Legacy Standalone Application#

Note

105.1.19 is the last supported version of this application.

This is the standalone application available in the exchange of the Omniverse Launcher.

Running the Scene Optimizer App#

In the examples for Python Package and Kit App Template a python script is needed to execute the Scene Optimizer operations. A Sample Python Script is provided in the documentation below and also takes a JSON configuration file as an argument.

Python Package#

python3 -m omni.kit_app --enable 'omni.scene.optimizer.core' --enable 'omni.usd' --exec '/path/to/python_script.py /path/to/input_dir /path/to/output_dir /path/to/json_config.json --input_glob=*.usd'

Kit App Template#

  • Linux

    ./kit --enable 'omni.scene.optimizer.core' --enable 'omni.usd' --exec '/path/to/python_script.py /path/to/input_dir /path/to/output_dir /path/to/json_config.json --input_glob=*.usd'
    
  • Windows

    .\kit.exe --enable 'omni.scene.optimizer.core' --enable 'omni.usd' --exec '/path/to/python_script.py /path/to/input_dir /path/to/output_dir /path/to/json_config.json --input_glob=*.usd'
    

Note

Please use the kit version inside the _build directory of the applicable kit app template

Legacy Standalone Application#

  • Linux

    ~/.local/share/ov/pkg/scene_optimizer-105.1.19/omni.app.scene.optimizer.batch.kit.sh
    
  • Windows

    C:\Users\<name>\AppData\Local\ov\pkg\scene_optimizer-105.1.19\omni.app.scene.optimizer.batch.kit.bat
    

Caution

If installation of Omniverse and related content is not using default settings, please use appropriate paths.

Running the tool using just the command line above will print output indicating the arguments needed for the command. There are three arguments required:

  • Input USD file name
    • --/app/sceneOptimizer/input

  • Output USD file name
    • --/app/sceneOptimizer/output

  • JSON config file
    • --/app/sceneOptimizer/config

The config file specifies the optimizations to be performed on the input file. The result of the optimization will be written to the output file.

It is also possible to define all the above arguments in a single file that can be loaded when the app starts.

Example Run of Legacy Standalone Application#

  • Linux

    ~/.local/share/ov/pkg/scene_optimizer-105.1.0/omni.app.scene.optimizer.batch.kit.sh  --/app/sceneOptimizer/input=/tmp/testScene.usd --/app/sceneOptimizer/output=/tmp/merged.usd --/app/sceneOptimizer/config=/tmp/merge_all.json
    
  • Windows

    C:\Users\<name>\AppData\Local\ov\pkg\scene_optimizer-105.1.0\omni.app.scene.optimizer.batch.kit.bat  --/app/sceneOptimizer/input=testScene.usd --/app/sceneOptimizer/output=merged.usd --/app/sceneOptimizer/config=merge_all.json
    

Caution

If your installation of Omniverse and related content is not using default settings, please use appropriate paths.

Config File Description#

The config file is a JSON file that describes one or more optimizations to apply to the data.

Below is an example config file that first optimizes scene materials by replacing identical materials with a single material, then deduplicates geometry by replacing identical versions with geometry instances.

[
{
    "operation": "optimizeMaterials",
    "materialPrimPaths": [],
    "optimizeMaterialsMode": 0
},
{
    "operation": "deduplicateGeometry",
    "meshPrimPaths": [],
    "considerDeepTransforms": true,
        "tolerance": 0.001,
        "duplicateMethod": 2,
        "fuzzy": false,
        "useGpu": false,
        "allowScaling": false,
}
]

Generating Config Files#

The file can be written by hand, but the easier way is to use the Scene Optimizer Kit extension to generate the file. The optimization steps can be defined in the Scene Optimizer UI, then saved to a JSON file.

See details on how to generate the JSON file from the Scene Optimizer Kit extension

Sample Python Script#

The sample python script can be used to batch process directories of USD files with varying config files:

"""Script to batch process USD files with Scene Optimizer operations"""

import argparse
from pathlib import Path

import omni
import omni.usd
from pxr import UsdUtils


def optimize_files_in_dir(input_dir, output_dir, optimizer_json, input_glob="*"):
    """Runs optimization configurations over all USD files in single directory"""

    input_files = list(Path(input_dir).glob(input_glob))

    for i, input_file in enumerate(input_files):
        output_file = Path(output_dir, input_file.name)
        output_file = str(output_file)
        input_file = str(input_file)

        if not omni.usd.get_context().open_stage(input_file):
            raise ValueError(f"Could not open file {input_file}")

        stage = omni.usd.get_context().get_stage()

        # Check to see if native prims exist along side payloads or references
        for prim in stage.Traverse():
            # check to see if prim is a payload or reference
            if not (prim.HasAuthoredPayloads() or prim.HasAuthoredReferences()):
                omni.log.warn(f"++ [{i}/{len(input_files)}] Optimizing {input_file} > {output_file}")

                context = omni.scene.optimizer.core.ExecutionContext()
                context.usdStageId = UsdUtils.StageCache().Get().Insert(stage).ToLongInt()
                context.generateReport = 1
                context.captureStats = 1

                # If a config file exists for a specific file use that instead of default input
                optimizer_json_file = Path(input_dir, Path(input_file).stem + ".json")

                if optimizer_json_file.is_file():
                    myArgs = {"jsonFile": str(optimizer_json_file)}
                else:
                    myArgs = {"jsonFile": optimizer_json}

                omni.kit.commands.execute("SceneOptimizerJsonParser", context=context, args=myArgs)

            # Export stage to a new file
            stage.GetRootLayer().Export(output_file)

            # Close each stage to avoid kit crashes
            omni.usd.get_context().close_stage()
            break


def main():
    parser = argparse.ArgumentParser(
        description="Optimize USD files in a directory.",
        epilog="Example: /path/to/kit --enable 'omni.scene.optimizer.core' --enable 'omni.usd' --exec 'python_script_name.py /path/to/input /path/to/output /path/to/config.json --input_glob=*.usd' ",
    )
    parser.add_argument("input_dir", help="Directory containing input files")
    parser.add_argument("output_dir", help="Directory to save optimized files")
    parser.add_argument("json_config", help="JSON configuration file for the optimizer")
    parser.add_argument("--input_glob", default="*", help="Glob pattern to match input files (default: *)")

    args = parser.parse_args()

    optimize_files_in_dir(args.input_dir, args.output_dir, args.json_config, input_glob=args.input_glob)


if __name__ == "__main__":
    main()