lightspeed.trex.asset_pipeline.core#

  • SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.

  • SPDX-License-Identifier: Apache-2.0

  • Licensed under the Apache License, Version 2.0 (the “License”);

  • you may not use this file except in compliance with the License.

  • You may obtain a copy of the License at

  • https://www.apache.org/licenses/LICENSE-2.0

  • Unless required by applicable law or agreed to in writing, software

  • distributed under the License is distributed on an “AS IS” BASIS,

  • WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

  • See the License for the specific language governing permissions and

  • limitations under the License.

class lightspeed.trex.asset_pipeline.core.AssetKind(*values)#

Top-level asset families supported by the Remix asset pipeline.

class lightspeed.trex.asset_pipeline.core.AssetPipelineCoreExtension#

Register process-owned texture- and mesh-optimization persistence codecs and Apply handlers.

on_shutdown() → None#

Keep codecs and handlers available until the queue core drains active jobs and destroys its registry.

on_startup(_ext_id: str) → None#

Register the texture- and mesh-optimization persistence codecs and Apply handlers.

Parameters:

_ext_id – Extension identifier supplied by Kit.

class lightspeed.trex.asset_pipeline.core.PipelineOutputPath(
work_path: Path,
output_path: Path,
)#

Runner-owned paths for one publishable pipeline output.

Steps write work_path and may inspect output_path for valid reuse. The runner later publishes work_path to the same output_path.

class lightspeed.trex.asset_pipeline.core.RemixAssetItem(
value: TValue,
kind: AssetKind,
source_path: pathlib.Path,
textures: list[TextureAsset] = <factory>,
texture_bindings: list[TextureBinding] = <factory>,
collected_dependencies: dict[pathlib.Path,
pathlib.Path] = <factory>,
)#

Stable item type for the whole Remix asset pipeline.

value is the current primary asset path. For a model, it starts as the source model and becomes the generated/collected USD path after import. For a texture, it remains the source texture path while textures[0].path becomes the processed texture path.

collected_dependencies maps original dependency paths to workspace copies. The collector supplies these runtime identities when it imports the model.

classmethod from_model(
path: Path,
) → RemixAssetItem#

Create a model item.

classmethod from_texture(
path: Path,
texture_type: TextureTypes,
) → RemixAssetItem#

Create a texture item with one typed texture record.

class lightspeed.trex.asset_pipeline.core.RemixAssetPipelineConfig(
output_dir: Path,
texture_type: TextureTypes | None,
)#

Constructor configuration shared by Remix asset pipeline steps.

Callers must pass explicit constructor options when building a pipeline. This is intentionally small; do not turn step configuration into an auto-discovered schema or a generic metadata dictionary.

output_dir#

Local directory receiving final pipeline outputs.

Type:

pathlib.Path

texture_type#

Semantic used only when synthesizing a missing texture record.

Type:

omni.flux.asset_importer.core.data_models.enums.TextureTypes | None

class lightspeed.trex.asset_pipeline.core.RemixAssetPipelineContext(
items: list[PipelineItemT] = <factory>,
execution_state: dict[str,
PipelineStepState] = <factory>,
source_root: pathlib.Path | None = None,
work_dir: pathlib.Path | None = None,
output_dir: pathlib.Path | None = None,
replace_udim_textures_by_empty: bool = False,
force_dds_reencode: bool = False,
referenced_layers: tuple[str,
...] = (),
texture_ledger: tuple[TextureLedgerEntry,
...] = (),
)#

Typed context for the linear Remix asset processing pipeline.

A caller must use this context inside async with. The first stage open leases one ingestion USD context. The outermost scope exit closes its stage, whether the block finished or raised, and returns the lease for reuse. A stage that fails to close stays in the context until the next open_stage on that context replaces it.

source_root#

Stable project or import root preserved under the output directory.

Type:

pathlib.Path | None

work_dir#

Runner-owned temporary workspace.

Type:

pathlib.Path | None

output_dir#

Final local publication directory.

Type:

pathlib.Path | None

replace_udim_textures_by_empty#

Author an empty asset path for a UDIM texture instead of a <UDIM> pattern. Model ingestion sets this to True to match the legacy model_ingestion.json schema. Standalone texture optimization leaves it False.

Type:

bool

force_dds_reencode#

Re-encode a source texture that is already a .dds file. By default a DDS source is copied to its output unchanged, so an encoded texture is never compressed twice.

Type:

bool

referenced_layers#

Every layer identifier the model composes, recorded by the discovery step.

Type:

tuple[str, …]

texture_ledger#

Every texture binding discovered, stored so the prepare job can freeze it into PrepareOptimizationResult.

Type:

tuple[TextureLedgerEntry, …]

sub_usd_lineage#

Every (source_path, published_path) pair for sub-USD layers the runner published alongside the model. Populated by the publication step so the mesh job can include them in the complete asset lineage.

Type:

list[tuple[pathlib.Path, pathlib.Path]]

clear_output_paths() → None#

Clear final output path reservations for a fresh pipeline run.

async close_stage() → None#

Close the open stage and keep the leased ingestion context alive.

This is the counterpart of open_stage. The context stays alive, so the next open_stage call reuses it. A step that runs one pass for each layer must close between rounds, because a context that still holds a layer keeps stale in-memory state after the step saves that layer to disk.

The USD context holds a reference to every layer of the open stage, so this releases the root layer before it closes the stage. Layers that stay held break a later stage open in the same process.

copy_to_work_dir(
path: Path,
) → Path#

Copy a source file into this pipeline run’s collision-safe workspace.

copy_to_work_path(
path: Path,
work_path: Path,
) → Path#

Copy a source file into an explicit runner-owned workspace path.

get_output_path(
work_path: Path,
*,
source_path: Path | None = None,
) → Path#

Return the final collision-safe output path for a workspace file.

Steps may use this to find reusable outputs from earlier runs. The runner uses the same reservation table when publishing final files, so steps never need to hand-roll deduplication suffixes.

get_work_path(
source_path: Path,
*,
stem: str | None = None,
stem_suffix: str = '',
suffix: str | None = None,
create_parent: bool = True,
) → Path#

Return a runner-owned workspace path for a step output.

The context owns collision isolation so steps do not hand-roll temp filenames. The returned filename stays readable while the parent directory is keyed by the absolute source path.

stem replaces the source filename stem. A step needs this when the legacy output name rewrites part of the stem instead of appending to it.

is_in_work_dir(path: Path) → bool#

Return whether the path already belongs to this pipeline run’s workspace.

async open_stage(
stage_path: Path,
) → Stage#

Open a USD stage in this scope’s leased ingestion context.

The scope leases the context exclusively, so ingestion never replaces the interactive stage in the app’s default context. The context holds one stage at a time, so a later call with a different path replaces the stage this call returned. A caller must not hold a stage, prim, or layer handle across a call with a different path.

Raises:

RuntimeError – If the caller is outside an async with scope, or if the stage cannot be opened.

reserve_output_path(
input_path: Path,
*,
source_path: Path | None = None,
stem_suffix: str = '',
suffix: str | None = None,
create_parent: bool = True,
) → PipelineOutputPath#

Reserve collision-safe workspace and final paths for one output.

input_path controls the readable output filename and source-scoped workspace directory. source_path can provide a stable original input for final-name deduplication when input_path is an intermediate.

async save_stage() → None#

Save the stage that is open in the leased ingestion context.

Raises:

RuntimeError – If no stage is open in the leased context.

property textures: Iterator[TextureAsset]#

Yield every texture record currently owned by the pipeline items.

validate_output_dir(
step_name: str,
) → list[str]#

Return validation errors for steps that need runner-owned final output paths.

validate_work_dir(
step_name: str,
) → list[str]#

Return validation errors for steps that need runner-owned workspace files.

class lightspeed.trex.asset_pipeline.core.TextureAsset(
path: Path,
texture_type: TextureTypes,
key: str = '',
original_path: Path | None = None,
udim_tiles: tuple[Path, ...] = (),
channel: str | None = None,
factor: tuple[float, ...] | None = None,
)#

One image file to process into a Remix-ready texture.

The conversion steps do not care whether this came from ComfyUI, Asset Library, a referenced model texture, or an extracted embedded model texture.

For a non-UDIM texture udim_tiles is an empty tuple and path is the sole concrete output. For a UDIM texture udim_tiles carries every converted concrete tile and path is the first tile, so downstream steps and publication walk the complete set.

property source_path: Path#

Return the stable source path used for output naming and reuse checks.

class lightspeed.trex.asset_pipeline.core.TextureBinding(
shader_path: Path,
input_name: str,
original_asset_path: AssetPath,
texture: TextureAsset,
material_path: str = '',
)#

Typed USD binding from a model shader input to one TextureAsset.

This is model rewrite state, not texture conversion state. It lets the final model step replace the original USD asset path with the processed texture path.

material_path is the correlation identity. Material conversion rewrites the shader between the prepare walk and the mesh walk, so only the owning material prim path survives both. See lightspeed.trex.asset_pipeline.core.jobs.models.derive_texture_key().

lightspeed.trex.asset_pipeline.core.build_prepare_optimization_steps(
config: RemixAssetPipelineConfig,
) → list[PipelineStep]#

Build the model-preparation step list.

The returned steps import the model, clean up and convert its materials to AperturePBR, then discover its textures and record every composed layer. Conversion runs before discovery, as the legacy schema ran MaterialShaders before ConvertToDDS, so discovery reads the AperturePBR input names and every source-shader texture reaches the texture job.

Parameters:

config – Run configuration consumed by the standardize-input step.

Returns:

The prepare phase steps, in order.

lightspeed.trex.asset_pipeline.core.build_remix_mesh_pipeline(
config: RemixAssetPipelineConfig,
processed_textures: dict[tuple[str, TextureTypes], ProcessedTexture],
) → list[PipelineStep]#

Build the mesh-optimization step list that consumes the processed textures.

The import step runs first, because each queue job owns its own workspace. The prepare job’s workspace is gone by now, so this job materializes the model and its textures again before any step reads them.

Material cleanup and conversion ran in the prepare phase. They run again here because each job materializes the model afresh, and both skip a model that is already AperturePBR. The legacy model ingestion schema never triangulated meshes, so this pipeline keeps the authored topology. Texture application runs after material conversion, so the collected shader paths and input names are the post-conversion ones.

An empty map means the model has no processed textures, so the step keeps the bindings the model already authored.

Parameters:
  • config – Run configuration consumed by the import step.

  • processed_textures – Map from (material_path, texture_type) to the resolved ProcessedTexture. Must be built by resolve_processed_textures() from the ledger and texture result before this builder is called.

Returns:

The mesh phase steps, in order.

lightspeed.trex.asset_pipeline.core.build_remix_texture_pipeline() → list[PipelineStep]#

Build the texture-processing step list for one standalone batch.

Float source standardization, channel extraction, normal conversion, and DDS encoding process each texture item. The model-only steps are omitted rather than relied on to skip themselves.

Returns:

The texture phase steps, in order.

async lightspeed.trex.asset_pipeline.core.run_remix_asset_pipeline(
config: RemixAssetPipelineConfig,
context: RemixAssetPipelineContext,
steps: Iterable[PipelineStep],
*,
on_step_started: Callable[[PipelineStep, int, int], Awaitable[None]] | None = None,
on_item_completed: Callable[[RemixAssetItem, int, int], Awaitable[None]] | None = None,
) → None#

Run the Remix asset pipeline and publish its outputs as the final reported phase.

Parameters:
  • config – Canonical pipeline configuration.

  • context – Remix items and runner-owned paths for this run.

  • steps – Configured processing steps. The caller must state its step list.

  • on_step_started – Async callback awaited before each runnable phase.

  • on_item_completed – Async callback awaited after each source item finishes processing.

Raises:
  • PipelineValidationError – If the configured steps cannot run against context.

  • OSError – If the output directory or temporary workspace cannot be created or removed.