This repo is housing sample scripts and snippets to show developers how they could solve specific problems when creating configurators.
It is not meant as complete solutions (if it works for you out of the box - great), but more like a source of inspiration for your own solutions.
Refer to the End-to-End Configurator Example Guide for instructions on creating a configurator from start to finish.
Available scripts and snippets:
- Run Variants (cache/run_variants.py) - Finds all variants and runs through them. Waits the stage to be fully ready before each new one is set. If a caching graph is found (
OmniGraph
prim with nameCacheGeneration
), a signal (GenerateCache
) is emitted for the graph to receive and to run through the authored options. - Copy Configurator (cache/copy_configurator.py) - Copy the configurator to another folder on the local disk. This is done to test the UJITSO cache from a different locations from where it was authored.
- Validate Log (cache/validate_log.py) - Find UJITSO errors in log file.
- Generate & Validate Cache (cache/generate_validate_cache.bat) - This batch script uses the three scripts above to fully automate cache generation and cache validation.
- Optimize File (optimize_file.py) - Conform a DELTAGEN export to Omniverse best practices
- Visibility Switches (visibility_switches.py) - Modify the switch variant functionality from DELTAGEN exports to visibility toggles
- CSV Material Replacements (csv_material_replacements.py) - A data driven material replacement workflow
- CSV Material Variants (csv_material_variants.py) - A data driven material variant creation workflow
- CSV to Json (csv_to_json.py) - Option packages data generation
- Reference Variants to Visibility (reference_variants_to_visibility.py) - Change variants that swap reference path to visibility switch
- Switch Variant (switch_variant.py) - Create visibility variants for each "switch variant"
- Resize Textures (resize_textures.py) - Resize images on hard drive
- Enable Streaming Extensions (enable_streaming_extensions.py) - snippet to show how to load extensions via Python
- Picking Mode (picking_mode.py) - snippet to set what is selectable in viewport
Scripts containing a bit more involved suggestions on how to solve a particular workflow problem or how to make something more efficient or better performing.
Generate and validate UJITSO cache for your asset running a single batch script
(scripts/cache/generate_validate_cache.bat)
Uses the three scripts below to (1
) generate the cache, (2
) copy the configurator to a different location, (3
) run all the options with flags to log any UJITSO cache issues, and (4
) parse the log and print out any issues.
To generate the cache, the kit app is ran with the following UJITSO flags and by running all the variants in the file after it has been loaded:
--/UJITSO/datastore/localCachePath="{path_to_cache_directory}"
--/UJITSO/writeCacheWithAssetRoot="{path_to_source_directory}"
After the configurator directory has been copied elsewhere, the log get's generated by running the kit app with the following UJITSO flags (these can be found in the generate_validate_cache.bat file), and by running all the variants in the file after it has been loaded:
--/UJITSO/failedDepLoadingLogging=true
--/UJITSO/readCacheWithAssetRoot={path_to_the_copy_root_dir}
--/UJITSO/datastore/localCachePath={path_to_the_copy_cache_dir}
Details can be seen by editing the scripts/cache/generate_validate_cache.bat file.
Run all variants in a stage awaiting the stage to be ready between each variant being set.
(scripts/cache/run_variants.py)
Finds all variants and runs through them. Waits the stage to be ready before each new one is set. If a caching graph is found (OmniGraph
prim with name CacheGeneration
), a signal (GenerateCache
) is emitted for the graph to receive and to run through the authored options. This lets you author which options get ran and cached out. You may have a lot of variants that are not in use.
Copy the configurator to another folder on the local disk.
(scripts/cache/copy_configurator.py)
Copy configurator from one local folder to another folder. Provide collected top level file and the root directory that you want to copy the files to. Optionally, you can provide an --overwrite
flag to overwrite the target root directory if it exists.
Find UJITSO errors in log file.
(scripts/cache/validate_log.py)
Provide the log path and the script will dig out any UJITSO Errors from the log file.
Conform a DELTAGEN export to Omniverse best practices
(scripts/deltagen/optimize_file.py)
Execute this script on the top-level USD (the script will modify all dependencies in place).
The USD files exported from DELTAGEN will be conformed to OV standards by reparenting all layers under "/World" root primitive, updating all asset paths to UNIX format and setting root primitive as default.
Modify the switch variant functionality from DELTAGEN exports to visibility toggles
(scripts/deltagen/visibility_switches.py)
This script will modify the behavior of switch variants from DELTAGEN USD exports so that they become visibility toggles.
Execute this script directly on an opened stage that contains switch variants. For DELTAGEN exports, this is the 'model'
export that contains geometry.
Data driven material replacement workflow
(scripts/csv_material_replacements.py)
Replace materials with other materials in a repeatable way. The use case this script was created for was to replace
USD Preview Surface materials coming out of DELTAGEN with mdl materials from the Omniverse Automotive library.
NOTE: You could export mdl materials that are custom built for your specific setup and replace with that library.
Example Table:
source | target | new_instance | modifications | material_name |
---|---|---|---|---|
/World/Mustang_Stellar_materials/rubber_black_semigloss | https://omniverse-content-production.s3.us-west-2.amazonaws.com/Materials/2023_2_1/Automotive/Pristine/Tires/Pristine_Tire_Rubber_Clean.mdl | {"inputs:diffuse_reflection_color":(0.2, 0.2, 0.2)} | ||
/World/Mustang_Stellar_materials/V_carpaint | https://omniverse-content-production.s3.us-west-2.amazonaws.com/Materials/2023_2_1/Automotive/Pristine/Carpaint/Carpaint_05.mdl | {"inputs:enable_flakes":0} | Carpaint_Body | |
/World/Mustang_Stellar_materials/white_rubber | https://omniverse-content-production.s3.us-west-2.amazonaws.com/Materials/2023_2_1/Automotive/Pristine/Tires/Pristine_Tire_Rubber_Clean.mdl | TRUE | {"inputs:enable_layer_1":1} |
Writes a csv file with all the material prim paths in the open stage. If the file already exists, only materials that do not already exist in the file will be added and any replacements and modifications would be retained.
TIP: Because of the function's additive nature, you can build a project wide material replacement csv file that can be applied to many stages.
Parses the csv file and creates the materials that will be used to replace the original materials that are written out in the first step.
This is useful, because it gives us a clean material USD file that we can also run the variant creation on. This can be sub layered into a file, and then we can run the material replacement function, that will now use the existing materials instead of also creating the materials, which is another option.
Read csv file and create materials under the MATERIAL_ROOT_PATH
that you will find at the top of the script file.
Defaults to MATERIAL_ROOT_PATH = '/World/Looks'
Optionally, it will create a new shader, even if the shader already exists (otherwise it will automatically re-use)
Example from the csv "new_instance" column - TRUE
Optionally, it will also apply modifications to the new shaders if a dict with property name and values are encoded.
Example from the csv "modifications" column - {"inputs:coat_color":(0.0, 0.0, 0.0), "inputs:enable_flakes":0}
NOTE: make sure that the dictionary is encoded correctly - the string is evaluated into a dictionary and will report an error if syntax is incorrect.
TIP: If you are going to create variants for certain properties on the shader, avoid creating a local opinion on that property at this step.
Optionally, you can also provide a new name for the shader. There is no need to flag "new_instance" when you do this the first time, only if you want to keep the same provided base name and create a new instance for modification reasons.
Example from the csv "shader_name" column - Glass_Reflectors_Base_Dark_Yellow
TIP: Make a new sub-layer and set it to "current authoring layer" before running this script. This will put the material replacements and the materials in a new usd file that you can add in at any point in your project setup.
Reads the csv file and replace all materials in the current stage if they have a replacement encoded.
If the replacement material does not exist in the current stage, it will be created.
Optionally, it will create a new shader, even if the shader already exists (otherwise it will automatically re-use)
Example from the csv "new_instance" column - TRUE
Optionally, it will also apply modifications to the new shaders if a dict with property name and values are encoded.
Example from the csv "modifications" column - {"inputs:coat_color":(0.0, 0.0, 0.0), "inputs:enable_flakes":0}
NOTE: make sure that the dictionary is encoded correctly - the string is evaluated into a dictionary and will report an error if syntax is incorrect.
Optionally, you can also provide a new name for the shader. There is no need to flag "new_instance" when you do this the first time, only if you want to keep the same provided base name and create a new instance for modification reasons. Example from the csv "shader_name" column - Glass_Reflectors_Base_Dark_Yellow
TIP: Make a new sub-layer and set it to "current authoring layer" before running this script. This will put the material replacements in a new usd file that you can add in at any point in your project setup. If you created a material library, you can add that in as a sub layer before running this and the materials from that sub layer will be used.
Data driven material variant creation
(scripts/csv_material_variants.py)
A script that can create material variants from csv data.
Read csv file and create variants.
Example Table:
source | target | new_instance | modifications | material_name |
---|---|---|---|---|
/World/Mustang_Stellar_materials/rubber_black_semigloss | https://omniverse-content-production.s3.us-west-2.amazonaws.com/Materials/2023_2_1/Automotive/Pristine/Tires/Pristine_Tire_Rubber_Clean.mdl | {"inputs:diffuse_reflection_color":(0.2, 0.2, 0.2)} | ||
/World/Mustang_Stellar_materials/V_carpaint | https://omniverse-content-production.s3.us-west-2.amazonaws.com/Materials/2023_2_1/Automotive/Pristine/Carpaint/Carpaint_05.mdl | {"inputs:enable_flakes":0} | Carpaint_Body | |
/World/Mustang_Stellar_materials/white_rubber | https://omniverse-content-production.s3.us-west-2.amazonaws.com/Materials/2023_2_1/Automotive/Pristine/Tires/Pristine_Tire_Rubber_Clean.mdl | TRUE | {"inputs:enable_layer_1":1} |
The csv file is encoded like this (with example data):
column:material_prim_path - value:/World/Looks/Carpaint_05
- The path to the material to add the variant to.
column:variant_name - value:black
- The variant set name to add.
column:variant_values - {"inputs:diffuse_reflection_color":(0.023102053, 0.023102075, 0.023102283), "inputs:coat_color":(0, 0, 0)}
- The variant data to encode in the form of a dictionary.
NOTE: make sure that the dictionary is encoded correctly - the string is evaluated into a dictionary and will report an error if syntax is incorrect.
Package info generation
(scripts/csv_to_json.py)
A script that will convert csv files containing configurator package information to json for use by a React application. The csv files define what data is needed and how it needs to be structured in order to be converted to a usable json format. This script requires two csv files - an "Options" csv and a "Packages" csv.
Contains every possible option from the stage. These options need to be referenced by the Packages csv.
Here are the rules for how this file is structured:
- The first row is a header, and therefore shouldn’t contain any options. Although the columns have distinct meanings, what’s entered into this row won't impact the resulting json output.
- Column 1 should contain a unique ID for the option (NOTE: each row represents an option).
- Column 2 should contain a prim path
- Column 3 should contain a variant set. This variant set should exist on the prim from column 2.
- Column 4 should optionally contain a label for the variant set. If a label isn’t needed, this column still needs to exist, but the values can be empty.
- Column 5 should contain a variant. This variant should be an available option on the variant set from column 3.
- Column 6 should optionally contain a label for the variant. If a label isn’t needed, this column still needs to exist, but the values can be empty.
- Column 7 should contain an optional graphic.
- Column 8 should contain an optional event
Example Table:
id | Prim Path | Variant Set | Option / Category (Optional) | Variant | Value Display Name (Optional) | Graphic (Optional) | Event (Optional) |
---|---|---|---|---|---|---|---|
0 | /World/Prim | Color | Color | Red | Red | ||
1 | /World/Prim | Color | Color | Green | Green | ||
2 | /World/Prim | Color | Color | Blue | Blue | ||
3 | /World/Prim | Mesh | Mesh | Cube | Cube | ||
4 | /World/Prim | Mesh | Mesh | Sphere | Sphere | ||
5 | /World/Prim | Mesh | Mesh | Cylinder | Cylinder |
Contains groupings of one or more option references (from the Options csv file), which together forms a 'package'. A name for the package is required, followed by an arbitrary number of Ids from the Options csv to associate with that package. Here are the rules for how this file is structured:
- The first row is a header and shouldn’t contain any options. Although the columns have distinct meanings, what’s entered into this row won't impact the resulting json output.
- Column 1 should contain a unique ID for the Package
- Column 2 should contain a unique name for the Package.
- Columns 3 and onward should contain an Id from the Options csv. Any number of columns may be added.
Example Table:
Id | Name | Color | Mesh |
---|---|---|---|
0 | Red Cube | 0 | 3 |
1 | Green Sphere | 1 | 4 |
2 | Blue Cylinder | 2 | 5 |
Creates and writes to disk the json containing package information for use by a React application.
Creates the data that's used for the packages JSON file
Returns a dictionary that combines Options csv data with Packages csv data
Returns a dictionary containing the values from a Packages csv
Returns a dictionary containing the values from an Options csv
Change variants that swap reference path to visibility switch
(scripts/reference_variants_to_visibility.py)
Find all variants that add a payload or reference to a prim and move them directly onto a new child prim.
It then modifies the variant to toggle on the visibility of the prim that holds the reference, and visibility off on the prims that hold the remaining references.
Optionally allows to convert payloads to references, or references to payloads.
Convert - def convert(convert_payloads_to_refs: bool=False, convert_references_to_payloads: bool=False) -> None:
Script's entry point. Accepts two Booleans for converting payloads to references and references to payloads.
Creates child prims for each variant and moves references & payloads to them.
Contains the main logic for performing the moving references and payloads to a new prim.
Applies the visibility toggling behavior for variants.
Create visibility variants for each switch variant
(scripts/switch_variant.py)
This module will build switch variants using existing prims in a stage. A "switch variant" is a term used in certain apps such as DELTAGEN, which are variants that are used for toggling on the visibility of a prim within a set and toggling off the visibility of the other prims in the set.
When executed, a new variant set will be added to all prims that have matching names with the provided switch_prims list.
The variant set will contain one visibility toggle variant for each child prim.
Create - def create(switch_prims: List[str] = ["Switch"], new_variant_set: str = "switchVariant") -> None:
Script's entry point. Receives a list of prim names to perform this operation against and a variant set name to be created on those prims.
Resize images on hard drive
(scripts/resize_textures.py)
Resize textures sizes from hard drive. Made to act on a Configurator Published folder on your local hard drive to optimize configurator performance before packaging it for GDN.
At the bottom of the script, modify the target_root directory to the directory you want the script to act on. The files list uses a function that will find all files of a type - the default is png files.
Print Info - def print_info(image_files: List[str], max_size: int = 2048, include_square: bool = True, include_non_square: bool = True, inform_single_color_images: bool = False, single_color_image_max_size: int = 128):
Scout function to console print all files found larger than the passed in max size argument and files with a higher bit depth than 8.
(Optional) Filter out square or non square images.
(Optional) Detect and report single color images over the passed in size (defaults to 128).
Down Res - def down_res(image_files: List[str], max_size: int = 1024, enforce_8_bit_depth: bool = False, include_square: bool = True, include_non_square: bool = True, enforce_single_color_image_size: bool = False, single_color_image_max_size: int = 128):
Down res files to the passed in max size integer.
(Optional) Enforce 8 bit images.
(Optional) Filter out square or non square images.
(Optional) Detect and down res single color images.
Bite sized code snippets to show you how to achieve a specific task.
(snippets/enable_streaming_extensions.py)
Demonstrates how to load the streaming extensions via Python. Just change the extension list to modify this to load any extensions you like via Python.
import omni.kit.app
manager = omni.kit.app.get_app().get_extension_manager()
extensions = ['omni.kit.livestream.messaging', 'omni.kit.livestream.webrtc.setup', 'omni.services.streamclient.webrtc']
for extension in extensions:
manager.set_extension_enabled_immediate(extension, True)
(snippets/picking_mode.py)
Demonstrates how to set what is selectable via mouse click within a kit viewport.
When executed, only the prims associated with the supplied prim type(s) will be selectable via mouse
click in the viewport.
import carb
ALL = "type:ALL"
MESH = "type:Mesh"
CAMERA = "type:Camera"
LIGHTS = "type:CylinderLight;type:DiskLight;type:DistantLight;type:DomeLight;type:GeometryLight;type:Light;type:RectLight;type:SphereLight"
if __name__ == "__main__":
# change MESH to ALL, CAMERA or LIGHTS for those prim types to be selectable
carb.settings.get_settings().set("/persistent/app/viewport/pickingMode", MESH)