mirror of
https://github.com/lighttransport/tinyusdz.git
synced 2026-01-18 01:11:17 +01:00
Add Python bindings and comprehensive variant documentation
Python Bindings: - Expose VariantOption, VariantSet, VariantGroup, VariantSelection classes - Expose VariantConverter for extracting variants from USD files - Expose DefaultVariantManager for querying and selecting variants - Expose VariantStatistics for complexity metrics - All classes accessible via tinyusdz.tydra module - Full API support: selection, querying, statistics, reset Documentation: - VARIANT_USAGE_GUIDE.md: Comprehensive guide covering: * Quick start for C++, Python, and CLI tools * Detailed architecture and data structures * Complete API reference * Python bindings documentation * Best practices and performance tips * 3 detailed usage examples - VARIANT_BEST_PRACTICES.md: Best practices covering: * Design principles (clarity, single responsibility, explicit defaults) * Variant hierarchy design patterns * Naming conventions and style guidelines * Performance optimization strategies * Integration patterns (UI, undo, presets, export) * Common pitfalls and how to avoid them * Testing and validation approaches Examples: - python/examples/variant_example.py: Complete example showing: * Loading USD files * Extracting variants with VariantConverter * Listing available variants * Selecting different variants * Querying variant statistics * Resetting to defaults 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
587
doc/VARIANT_BEST_PRACTICES.md
Normal file
587
doc/VARIANT_BEST_PRACTICES.md
Normal file
@@ -0,0 +1,587 @@
|
||||
# USD Variant Best Practices Guide
|
||||
|
||||
This guide provides best practices for designing, implementing, and using USD variants in TinyUSDZ applications.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Design Principles](#design-principles)
|
||||
2. [Structure Guidelines](#structure-guidelines)
|
||||
3. [Naming Conventions](#naming-conventions)
|
||||
4. [Performance Optimization](#performance-optimization)
|
||||
5. [Integration Patterns](#integration-patterns)
|
||||
6. [Common Pitfalls](#common-pitfalls)
|
||||
7. [Testing and Validation](#testing-and-validation)
|
||||
|
||||
## Design Principles
|
||||
|
||||
### 1. Clarity Over Brevity
|
||||
|
||||
Variants should be self-documenting. Use clear, descriptive names that indicate purpose:
|
||||
|
||||
```
|
||||
✓ GOOD:
|
||||
geometry_lod
|
||||
material_finish
|
||||
assembly_type
|
||||
locale_variant
|
||||
|
||||
✗ BAD:
|
||||
var1, var2, v3
|
||||
opt_a, opt_b
|
||||
geom, mat
|
||||
```
|
||||
|
||||
### 2. Single Responsibility
|
||||
|
||||
Each variant set should represent a single dimension of variation:
|
||||
|
||||
```
|
||||
✓ GOOD (separate concerns):
|
||||
VariantSet "level_of_detail" → [high, medium, low]
|
||||
VariantSet "material_type" → [plastic, metal, rubber]
|
||||
VariantSet "damage" → [pristine, scratched, broken]
|
||||
|
||||
✗ BAD (mixed concerns):
|
||||
VariantSet "variations" → [high_plastic, low_metal, broken_rubber]
|
||||
```
|
||||
|
||||
### 3. Explicit Defaults
|
||||
|
||||
Always explicitly set and document default variant selections:
|
||||
|
||||
```cpp
|
||||
struct VariantSet {
|
||||
std::string name;
|
||||
std::vector<VariantOption> options;
|
||||
int32_t default_option_index = 0; // Always explicit
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
### 4. Consistent Ordering
|
||||
|
||||
Maintain consistent ordering of options within a variant set:
|
||||
|
||||
```
|
||||
✓ GOOD (logical ordering):
|
||||
lod: [high, medium, low] // Descending detail
|
||||
quality: [maximum, high, medium, low] // Descending quality
|
||||
|
||||
✗ BAD (random ordering):
|
||||
lod: [low, high, medium] // Confusing order
|
||||
```
|
||||
|
||||
### 5. Avoid Over-nesting
|
||||
|
||||
While USD supports unlimited nesting, limit to 3 levels for maintainability:
|
||||
|
||||
```
|
||||
✓ GOOD (3 levels):
|
||||
Level 1: Asset variant sets
|
||||
Level 2: LOD option variants
|
||||
Level 3: Material option variants
|
||||
|
||||
✗ BAD (excessive nesting):
|
||||
Level 1 → Level 2 → Level 3 → Level 4 → Level 5
|
||||
```
|
||||
|
||||
## Structure Guidelines
|
||||
|
||||
### Variant Hierarchy Design
|
||||
|
||||
Design variant hierarchies bottom-up, starting with dependencies:
|
||||
|
||||
```
|
||||
Character Asset
|
||||
├── VariantSet "character_type" [primary dimension]
|
||||
│ ├── Option "hero"
|
||||
│ │ └── VariantSet "skin_tone"
|
||||
│ │ ├── "light"
|
||||
│ │ ├── "medium"
|
||||
│ │ └── "dark"
|
||||
│ ├── Option "villain"
|
||||
│ │ └── VariantSet "damage_level"
|
||||
│ │ ├── "pristine"
|
||||
│ │ └── "damaged"
|
||||
│ └── Option "extra"
|
||||
│ └── VariantSet "costume"
|
||||
│ ├── "red"
|
||||
│ └── "blue"
|
||||
└── VariantSet "cloth_material" [independent dimension]
|
||||
├── "cotton"
|
||||
├── "silk"
|
||||
└── "leather"
|
||||
```
|
||||
|
||||
### Content Organization
|
||||
|
||||
Organize content (geometry, materials) to align with variant structure:
|
||||
|
||||
```
|
||||
/Characters/Hero
|
||||
├── Geometry (LOD variants)
|
||||
│ ├── high_poly (for "high" LOD)
|
||||
│ ├── medium_poly (for "medium" LOD)
|
||||
│ └── low_poly (for "low" LOD)
|
||||
├── Materials (material variants)
|
||||
│ ├── Material_Metal
|
||||
│ ├── Material_Plastic
|
||||
│ └── Material_Rubber
|
||||
└── Animations (animation variants)
|
||||
├── Anim_Run_Fast
|
||||
├── Anim_Run_Slow
|
||||
└── Anim_Walk
|
||||
```
|
||||
|
||||
### Variant Composition Strategy
|
||||
|
||||
**Strategy 1: Content Swapping (Recommended)**
|
||||
|
||||
Different geometry/materials per option:
|
||||
|
||||
```
|
||||
✓ Efficient for: Material variations, LOD transitions
|
||||
✗ Inefficient for: Many similar options with minor differences
|
||||
```
|
||||
|
||||
**Strategy 2: Property Overrides**
|
||||
|
||||
Same content with different properties:
|
||||
|
||||
```
|
||||
✓ Efficient for: Color variants, parameter adjustments
|
||||
✗ Inefficient for: Major structural changes
|
||||
```
|
||||
|
||||
**Strategy 3: Hybrid Approach**
|
||||
|
||||
Combine content swapping and property overrides:
|
||||
|
||||
```
|
||||
✓ Use content swapping for major differences (LOD, materials)
|
||||
✓ Use property overrides for minor adjustments (colors, scales)
|
||||
```
|
||||
|
||||
## Naming Conventions
|
||||
|
||||
### Variant Set Names
|
||||
|
||||
Use snake_case for variant set names:
|
||||
|
||||
```
|
||||
✓ GOOD:
|
||||
level_of_detail
|
||||
material_finish
|
||||
assembly_configuration
|
||||
regional_variant
|
||||
|
||||
✗ BAD:
|
||||
LevelOfDetail
|
||||
materialFinish
|
||||
assembly-configuration
|
||||
```
|
||||
|
||||
### Option Names
|
||||
|
||||
Use descriptive, lowercase names:
|
||||
|
||||
```
|
||||
✓ GOOD:
|
||||
high, medium, low (clarity)
|
||||
plastic, metal, rubber (material types)
|
||||
damaged, scratched, pristine (conditions)
|
||||
|
||||
✗ BAD:
|
||||
h, m, l (abbreviations)
|
||||
mtl1, mtl2, mtl3 (cryptic)
|
||||
var_a, var_b, var_c (meaningless)
|
||||
```
|
||||
|
||||
### Description Fields
|
||||
|
||||
Always provide descriptions for complex or non-obvious options:
|
||||
|
||||
```cpp
|
||||
VariantOption option;
|
||||
option.name = "ultra_high_poly";
|
||||
option.description = "Ultra-high detail geometry (50M polygons, 4K textures)";
|
||||
option.description += " - Recommended for close-up shots and hero renders";
|
||||
```
|
||||
|
||||
## Performance Optimization
|
||||
|
||||
### 1. Minimize Variant Combinations
|
||||
|
||||
Excessive combinations hurt performance:
|
||||
|
||||
```
|
||||
✗ BAD (96 combinations):
|
||||
lod: [high, medium, low] (3 options)
|
||||
material: [gold, silver, bronze] (3 options)
|
||||
color: [red, green, blue, black] (4 options)
|
||||
size: [small, medium, large] (4 options)
|
||||
finish: [matte, glossy, metallic] (3 options)
|
||||
Total: 3 × 3 × 4 × 4 × 3 = 432 combinations
|
||||
|
||||
✓ GOOD (12 combinations):
|
||||
lod: [high, medium, low] (3 options)
|
||||
variant: [gold, silver, bronze] (3 options)
|
||||
Total: 3 × 3 = 9 combinations
|
||||
|
||||
(Other variations via materials/properties)
|
||||
```
|
||||
|
||||
### 2. Lazy Load Variant Content
|
||||
|
||||
Load variant content only when selected:
|
||||
|
||||
```cpp
|
||||
class SmartVariantManager {
|
||||
std::unordered_map<std::string, ContentCache> cache;
|
||||
|
||||
void SelectVariant(const std::string& variant) {
|
||||
if (cache.find(variant) == cache.end()) {
|
||||
// Load on-demand
|
||||
cache[variant] = LoadVariantContent(variant);
|
||||
}
|
||||
// Apply cached content
|
||||
ApplyContent(cache[variant]);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### 3. Use Indices Instead of Names
|
||||
|
||||
In performance-critical code, use indices:
|
||||
|
||||
```cpp
|
||||
// ✓ GOOD (O(1) lookup):
|
||||
manager.SelectVariantByIndex(group_id, set_id, option_index);
|
||||
|
||||
// ✗ SLOWER (string comparison):
|
||||
manager.SelectVariant(group_id, set_name, option_name);
|
||||
```
|
||||
|
||||
### 4. Batch Variant Selections
|
||||
|
||||
When changing multiple variants, batch the selections:
|
||||
|
||||
```cpp
|
||||
// ✓ GOOD (single state update):
|
||||
std::vector<VariantSelection> selections = {
|
||||
{group_0, set_0, index_a},
|
||||
{group_1, set_1, index_b},
|
||||
{group_2, set_2, index_c}
|
||||
};
|
||||
manager.ApplySelections(selections);
|
||||
|
||||
// ✗ INEFFICIENT (multiple updates):
|
||||
manager.SelectVariant(group_0, set_0_name, option_a);
|
||||
manager.SelectVariant(group_1, set_1_name, option_b);
|
||||
manager.SelectVariant(group_2, set_2_name, option_c);
|
||||
```
|
||||
|
||||
### 5. Profile Variant Operations
|
||||
|
||||
Measure variant switching performance:
|
||||
|
||||
```cpp
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
manager.SelectVariant(group_id, set_name, option_name);
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
|
||||
std::cout << "Selection took: " << duration.count() << "ms\n";
|
||||
```
|
||||
|
||||
## Integration Patterns
|
||||
|
||||
### Pattern 1: Variant UI Panel
|
||||
|
||||
Create a UI for variant selection:
|
||||
|
||||
```python
|
||||
class VariantPanel:
|
||||
def __init__(self, manager: VariantManager):
|
||||
self.manager = manager
|
||||
self.groups = manager.get_variant_groups()
|
||||
|
||||
def render_variants(self):
|
||||
for i, group in enumerate(self.groups):
|
||||
print(f"\n{group.prim_path}")
|
||||
for j, var_set in enumerate(group.variant_sets):
|
||||
print(f" {var_set.name}:")
|
||||
for k, option in enumerate(var_set.options):
|
||||
selected = "✓" if self.is_selected(i, j, k) else " "
|
||||
print(f" [{selected}] {option.name}")
|
||||
|
||||
def on_variant_clicked(self, group_id, set_id, option_id):
|
||||
self.manager.select_variant_by_index(group_id, set_id, option_id)
|
||||
```
|
||||
|
||||
### Pattern 2: Variant History/Undo
|
||||
|
||||
Maintain variant selection history:
|
||||
|
||||
```cpp
|
||||
class VariantHistory {
|
||||
std::vector<std::vector<VariantSelection>> history;
|
||||
size_t current_index = 0;
|
||||
|
||||
void Push(const std::vector<VariantSelection>& selections) {
|
||||
// Remove forward history if we're not at the end
|
||||
history.erase(history.begin() + current_index + 1, history.end());
|
||||
history.push_back(selections);
|
||||
current_index++;
|
||||
}
|
||||
|
||||
bool Undo() {
|
||||
if (current_index > 0) {
|
||||
current_index--;
|
||||
ApplySelections(history[current_index]);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Redo() {
|
||||
if (current_index < history.size() - 1) {
|
||||
current_index++;
|
||||
ApplySelections(history[current_index]);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Pattern 3: Variant Presets
|
||||
|
||||
Save and restore variant configurations:
|
||||
|
||||
```python
|
||||
class VariantPreset:
|
||||
def __init__(self, name: str):
|
||||
self.name = name
|
||||
self.selections = []
|
||||
|
||||
def save(self, manager: DefaultVariantManager):
|
||||
self.selections = list(manager.get_all_selections())
|
||||
|
||||
def apply(self, manager: DefaultVariantManager):
|
||||
for selection in self.selections:
|
||||
manager.apply_selection(selection)
|
||||
|
||||
def to_json(self):
|
||||
return json.dumps([{
|
||||
'group_id': s.variant_group_id,
|
||||
'set_id': s.variant_set_id,
|
||||
'option': s.selected_option_index
|
||||
} for s in self.selections])
|
||||
```
|
||||
|
||||
### Pattern 4: Variant Export
|
||||
|
||||
Export to different formats based on variant selection:
|
||||
|
||||
```cpp
|
||||
void ExportWithVariant(const std::string& variant,
|
||||
const std::string& output_format) {
|
||||
// Select variant
|
||||
manager.SelectVariant(0, "material", variant);
|
||||
|
||||
// Export based on format
|
||||
if (output_format == "glb") {
|
||||
ExportGLB(scene, "model_" + variant + ".glb");
|
||||
} else if (output_format == "usda") {
|
||||
ExportUSDA(stage, "model_" + variant + ".usda");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
### Pitfall 1: Case-Sensitive Names
|
||||
|
||||
Variant names are case-sensitive:
|
||||
|
||||
```cpp
|
||||
// These are DIFFERENT:
|
||||
manager.SelectVariant(0, "color", "Red"); // May fail
|
||||
manager.SelectVariant(0, "color", "red"); // Correct
|
||||
```
|
||||
|
||||
**Solution:** Use lowercase consistently.
|
||||
|
||||
### Pitfall 2: Forgetting Default Index
|
||||
|
||||
Not setting default_option_index:
|
||||
|
||||
```cpp
|
||||
// ✗ WRONG (no default):
|
||||
variant_set.options = [{name: "high"}, {name: "low"}];
|
||||
|
||||
// ✓ CORRECT:
|
||||
variant_set.options = [{name: "high"}, {name: "low"}];
|
||||
variant_set.default_option_index = 0; // "high" is default
|
||||
```
|
||||
|
||||
### Pitfall 3: Invalid Index Access
|
||||
|
||||
Accessing variant options with wrong indices:
|
||||
|
||||
```cpp
|
||||
// ✗ WRONG (may crash):
|
||||
auto option = variant_set.options[999];
|
||||
|
||||
// ✓ CORRECT (bounds checking):
|
||||
if (option_index >= 0 && option_index < variant_set.options.size()) {
|
||||
auto option = variant_set.options[option_index];
|
||||
}
|
||||
```
|
||||
|
||||
### Pitfall 4: Modifying Shared References
|
||||
|
||||
Modifying variant data that's referenced elsewhere:
|
||||
|
||||
```cpp
|
||||
// ✗ WRONG (modifying shared data):
|
||||
auto* group = manager.FindVariantGroup(path);
|
||||
group->prim_path = "new_path"; // Affects other references
|
||||
|
||||
// ✓ CORRECT (work with copies):
|
||||
auto groups = manager.GetVariantGroups();
|
||||
for (auto& group : groups) {
|
||||
// Read-only operations, or
|
||||
// Copy data before modifying
|
||||
}
|
||||
```
|
||||
|
||||
### Pitfall 5: Excessive Nesting Depth
|
||||
|
||||
Using too many nesting levels:
|
||||
|
||||
```
|
||||
✗ BAD (5 levels):
|
||||
Level 1: Character type
|
||||
Level 2: Outfit type
|
||||
Level 3: Color
|
||||
Level 4: Texture quality
|
||||
Level 5: Special effects
|
||||
|
||||
✓ GOOD (2-3 levels):
|
||||
Level 1: Character type → Outfit type
|
||||
Level 2: Color variant
|
||||
|
||||
(Texture quality as property override, not nested variant)
|
||||
```
|
||||
|
||||
## Testing and Validation
|
||||
|
||||
### Test 1: Variant Coverage
|
||||
|
||||
Ensure all variant paths are tested:
|
||||
|
||||
```cpp
|
||||
TEST(VariantTest, AllVariantCombinations) {
|
||||
for (const auto& group : scene.variant_groups) {
|
||||
for (const auto& var_set : group.variant_sets) {
|
||||
for (const auto& option : var_set.options) {
|
||||
EXPECT_TRUE(manager.SelectVariant(
|
||||
group.id, var_set.name, option.name));
|
||||
ValidateVariantContent(scene);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Test 2: Selection Validation
|
||||
|
||||
Verify selections are applied correctly:
|
||||
|
||||
```cpp
|
||||
TEST(VariantTest, SelectionValidation) {
|
||||
manager.SelectVariant(0, "color", "red");
|
||||
const auto* sel = manager.GetCurrentSelection(0);
|
||||
EXPECT_NE(sel, nullptr);
|
||||
EXPECT_EQ(sel->selected_option_index, 0); // "red" is index 0
|
||||
}
|
||||
```
|
||||
|
||||
### Test 3: Performance Benchmarking
|
||||
|
||||
Measure variant switching performance:
|
||||
|
||||
```cpp
|
||||
TEST(VariantTest, PerformanceBenchmark) {
|
||||
const int ITERATIONS = 1000;
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
for (int i = 0; i < ITERATIONS; i++) {
|
||||
manager.SelectVariant(0, "lod", "high");
|
||||
manager.SelectVariant(0, "lod", "low");
|
||||
}
|
||||
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
|
||||
auto avg = duration.count() / (double)(ITERATIONS * 2);
|
||||
EXPECT_LT(avg, 1.0); // Should be < 1ms per selection
|
||||
}
|
||||
```
|
||||
|
||||
### Test 4: Variant Data Integrity
|
||||
|
||||
Ensure variant data is correctly extracted:
|
||||
|
||||
```cpp
|
||||
TEST(VariantConverterTest, DataIntegrity) {
|
||||
const auto& group = scene.variant_groups[0];
|
||||
EXPECT_FALSE(group.prim_path.empty());
|
||||
EXPECT_FALSE(group.variant_sets.empty());
|
||||
|
||||
for (const auto& var_set : group.variant_sets) {
|
||||
EXPECT_FALSE(var_set.name.empty());
|
||||
EXPECT_FALSE(var_set.options.empty());
|
||||
EXPECT_GE(var_set.default_option_index, 0);
|
||||
EXPECT_LT(var_set.default_option_index, var_set.options.size());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Test 5: Nested Variant Validation
|
||||
|
||||
Verify nested variants are correctly handled:
|
||||
|
||||
```cpp
|
||||
TEST(VariantTest, NestedVariantValidation) {
|
||||
for (const auto& group : scene.variant_groups) {
|
||||
for (const auto& var_set : group.variant_sets) {
|
||||
for (const auto& option : var_set.options) {
|
||||
// Check nesting depth
|
||||
uint32_t depth = 1;
|
||||
for (const auto& nested : option.nested_variant_sets) {
|
||||
EXPECT_LE(++depth, MAX_NESTING_DEPTH);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
Key takeaways for variant best practices:
|
||||
|
||||
1. **Design for clarity** - Use self-documenting structures
|
||||
2. **Limit complexity** - Keep nesting and combinations reasonable
|
||||
3. **Follow conventions** - Use consistent naming and ordering
|
||||
4. **Optimize performance** - Minimize variant combinations and use indices
|
||||
5. **Integrate thoughtfully** - Use patterns for UI, undo, presets, export
|
||||
6. **Avoid pitfalls** - Watch for case sensitivity, bounds checking, defaults
|
||||
7. **Test thoroughly** - Validate all variant combinations and paths
|
||||
|
||||
## See Also
|
||||
|
||||
- [VARIANT_USAGE_GUIDE.md](VARIANT_USAGE_GUIDE.md) - Comprehensive API reference
|
||||
- USD Variant Specification: https://openusd.org/docs/api/class_usd_variant_set.html
|
||||
- TinyUSDZ Variant Examples: `python/examples/variant_example.py`
|
||||
599
doc/VARIANT_USAGE_GUIDE.md
Normal file
599
doc/VARIANT_USAGE_GUIDE.md
Normal file
@@ -0,0 +1,599 @@
|
||||
# USD Variant Support in TinyUSDZ
|
||||
|
||||
This guide covers how to work with USD variants using TinyUSDZ's comprehensive variant support system.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Overview](#overview)
|
||||
2. [Quick Start](#quick-start)
|
||||
3. [Architecture](#architecture)
|
||||
4. [API Reference](#api-reference)
|
||||
5. [CLI Tools](#cli-tools)
|
||||
6. [Python Bindings](#python-bindings)
|
||||
7. [Best Practices](#best-practices)
|
||||
8. [Examples](#examples)
|
||||
|
||||
## Overview
|
||||
|
||||
USD variants are a powerful feature that allow you to create multiple, mutually exclusive representations of a USD asset. TinyUSDZ provides comprehensive support for:
|
||||
|
||||
- **Variant Extraction**: Convert USD variant structures into RenderScene variant groups
|
||||
- **Variant Analysis**: Query and analyze variant complexity metrics
|
||||
- **Variant Selection**: Dynamically select different variants for rendering
|
||||
- **Nested Variants**: Support for USD's nested variant hierarchy (up to 3 levels)
|
||||
- **Change Tracking**: Track what content changes when variants are selected
|
||||
|
||||
### Common Use Cases
|
||||
|
||||
- **Level of Detail (LOD)**: Provide different geometry complexity levels for different viewing distances
|
||||
- **Material Variants**: Offer different material or color options for assets
|
||||
- **Geometry Variations**: Support different shapes, configurations, or assembly options
|
||||
- **Asset Variants**: Multiple versions of characters, props, or environments
|
||||
- **Locale Variants**: Different versions for different regions or languages
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Using the Command-Line Tools
|
||||
|
||||
#### List variants in a USD file:
|
||||
|
||||
```bash
|
||||
variant-lister model.usda
|
||||
variant-lister model.usda --summary
|
||||
variant-lister model.usda --json
|
||||
```
|
||||
|
||||
#### Analyze variant complexity:
|
||||
|
||||
```bash
|
||||
variant-analyzer model.usda
|
||||
variant-analyzer model.usda --detailed
|
||||
variant-analyzer model.usda --json
|
||||
```
|
||||
|
||||
### Using C++ API
|
||||
|
||||
```cpp
|
||||
#include "tinyusdz.hh"
|
||||
#include "tydra/variant-support.hh"
|
||||
#include "tydra/variant-converter.hh"
|
||||
|
||||
using namespace tinyusdz;
|
||||
using namespace tinyusdz::tydra;
|
||||
|
||||
// Load USD file
|
||||
Stage stage;
|
||||
std::string err;
|
||||
bool ret = LoadUSDFromFile("model.usda", &stage, nullptr, &err);
|
||||
|
||||
// Extract variants
|
||||
RenderScene scene;
|
||||
VariantConverter converter;
|
||||
if (converter.ConvertVariants(stage, &scene, &err)) {
|
||||
// Work with variants
|
||||
DefaultVariantManager manager;
|
||||
manager.SetVariantGroups(scene.variant_groups);
|
||||
|
||||
// Select a variant
|
||||
manager.SelectVariant(0, "color", "red");
|
||||
|
||||
// Get statistics
|
||||
auto stats = manager.GetStatistics();
|
||||
std::cout << "Found " << stats.num_variant_groups << " variant groups\n";
|
||||
}
|
||||
```
|
||||
|
||||
### Using Python API
|
||||
|
||||
```python
|
||||
import tinyusdz
|
||||
|
||||
# Load USD file
|
||||
stage = tinyusdz.load_usd_from_file("model.usda")
|
||||
|
||||
# Extract variants
|
||||
scene = tinyusdz.tydra.RenderScene()
|
||||
converter = tinyusdz.tydra.VariantConverter()
|
||||
converter.convert_variants(stage, scene)
|
||||
|
||||
# Create manager and select variants
|
||||
manager = tinyusdz.tydra.DefaultVariantManager()
|
||||
manager.set_variant_groups(scene.variant_groups)
|
||||
|
||||
# List available options
|
||||
for group in scene.variant_groups:
|
||||
print(f"Prim: {group.prim_path}")
|
||||
for var_set in group.variant_sets:
|
||||
options = [opt.name for opt in var_set.options]
|
||||
print(f" {var_set.name}: {options}")
|
||||
|
||||
# Select a variant
|
||||
manager.select_variant(0, "color", "red")
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
### Core Data Structures
|
||||
|
||||
#### VariantOption
|
||||
Represents a single variant option within a variant set.
|
||||
|
||||
```cpp
|
||||
struct VariantOption {
|
||||
std::string name; // Option name
|
||||
std::string description; // Optional description
|
||||
std::vector<int32_t> mesh_ids; // Associated mesh IDs
|
||||
std::vector<int32_t> material_ids; // Associated material IDs
|
||||
std::vector<int32_t> node_ids; // Affected node IDs
|
||||
std::vector<int32_t> animation_ids; // Animation IDs
|
||||
std::map<std::string, std::string> property_overrides; // Property changes
|
||||
std::vector<std::shared_ptr<VariantSet>> nested_variant_sets; // Nested variants
|
||||
};
|
||||
```
|
||||
|
||||
#### VariantSet
|
||||
A set of mutually exclusive variant options.
|
||||
|
||||
```cpp
|
||||
struct VariantSet {
|
||||
std::string name; // Set name (e.g., "color", "lod")
|
||||
std::vector<VariantOption> options; // Available options
|
||||
int32_t default_option_index{0}; // Default option index
|
||||
int32_t parent_prim_id{-1}; // Parent for nested variants
|
||||
std::string parent_variant_option_name; // Parent option name
|
||||
};
|
||||
```
|
||||
|
||||
#### VariantGroup
|
||||
All variant sets for a specific prim/node.
|
||||
|
||||
```cpp
|
||||
struct VariantGroup {
|
||||
std::string prim_path; // USD prim path
|
||||
std::vector<VariantSet> variant_sets; // All variant sets for this prim
|
||||
int32_t affected_node_id{-1}; // Primary node affected
|
||||
std::vector<int32_t> secondary_node_ids; // Secondary nodes affected
|
||||
};
|
||||
```
|
||||
|
||||
#### VariantSelection
|
||||
Records which variant option is currently selected.
|
||||
|
||||
```cpp
|
||||
struct VariantSelection {
|
||||
int32_t variant_group_id; // Which group
|
||||
int32_t variant_set_id; // Which set in the group
|
||||
int32_t selected_option_index; // Which option is selected
|
||||
};
|
||||
```
|
||||
|
||||
### Variant Manager
|
||||
|
||||
The `DefaultVariantManager` provides high-level APIs for working with variants:
|
||||
|
||||
**Key Methods:**
|
||||
- `HasVariants()` - Check if any variants exist
|
||||
- `FindVariantGroup(prim_path)` - Get variant group by prim path
|
||||
- `SelectVariant(group_id, set_name, option_name)` - Select a variant option
|
||||
- `SelectVariantByIndex(group_id, set_id, option_index)` - Select by index
|
||||
- `GetCurrentSelection(group_id)` - Get current selection
|
||||
- `ResetToDefaults()` - Reset to default options
|
||||
- `GetStatistics()` - Get variant metrics
|
||||
|
||||
### Variant Converter
|
||||
|
||||
The `VariantConverter` extracts USD variant information and maps it to RenderScene:
|
||||
|
||||
```cpp
|
||||
class VariantConverter {
|
||||
public:
|
||||
// Main entry point: convert all variants from USD Stage to RenderScene
|
||||
bool ConvertVariants(const Stage& stage, RenderScene* scene, std::string* err);
|
||||
|
||||
// Configure maximum nesting depth to process
|
||||
void SetMaxNestingDepth(uint32_t depth);
|
||||
};
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### C++ API
|
||||
|
||||
#### VariantConverter
|
||||
|
||||
```cpp
|
||||
// Extract variant information from a USD Stage
|
||||
bool ConvertVariants(const Stage& stage, RenderScene* scene, std::string* err);
|
||||
|
||||
// Set maximum nesting depth (default: 3)
|
||||
void SetMaxNestingDepth(uint32_t depth);
|
||||
```
|
||||
|
||||
#### DefaultVariantManager
|
||||
|
||||
```cpp
|
||||
// Initialize with variant groups
|
||||
void SetVariantGroups(const std::vector<VariantGroup>& groups);
|
||||
|
||||
// Query variant information
|
||||
bool HasVariants() const;
|
||||
VariantGroup* FindVariantGroup(const std::string& prim_path);
|
||||
VariantSet* FindVariantSet(int32_t group_id, const std::string& set_name);
|
||||
VariantOption* FindVariantOption(int32_t group_id, int32_t set_id,
|
||||
const std::string& option_name);
|
||||
|
||||
// Select variants
|
||||
bool SelectVariant(int32_t group_id, const std::string& set_name,
|
||||
const std::string& option_name, std::string* err = nullptr);
|
||||
bool SelectVariantByIndex(int32_t group_id, int32_t set_id,
|
||||
int32_t option_index, std::string* err = nullptr);
|
||||
|
||||
// Query selections
|
||||
const VariantSelection* GetCurrentSelection(int32_t group_id) const;
|
||||
const std::vector<VariantSelection>& GetAllSelections() const;
|
||||
|
||||
// Reset state
|
||||
bool ResetToDefaults(std::string* err = nullptr);
|
||||
|
||||
// Statistics
|
||||
VariantStatistics GetStatistics() const;
|
||||
```
|
||||
|
||||
### Python API
|
||||
|
||||
All C++ classes are exposed to Python with snake_case method names:
|
||||
|
||||
```python
|
||||
manager.has_variants()
|
||||
manager.find_variant_group(prim_path)
|
||||
manager.select_variant(group_id, set_name, option_name)
|
||||
manager.get_current_selection(group_id)
|
||||
manager.get_statistics()
|
||||
manager.reset_to_defaults()
|
||||
```
|
||||
|
||||
## CLI Tools
|
||||
|
||||
### variant-lister
|
||||
|
||||
Lists all variants in a USD file with tree-format display.
|
||||
|
||||
```bash
|
||||
# Basic usage
|
||||
variant-lister model.usda
|
||||
|
||||
# Show summary statistics
|
||||
variant-lister model.usda --summary
|
||||
|
||||
# JSON output
|
||||
variant-lister model.usda --json
|
||||
|
||||
# Verbose output
|
||||
variant-lister model.usda --verbose
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```
|
||||
=== Variants in model.usda ===
|
||||
|
||||
├─ Prim: /Root/Character
|
||||
└─ VariantSet: "lod"
|
||||
├─ Default: high
|
||||
├─ "high" [+nested: 1]
|
||||
│ └─ VariantSet: "material" (2 options)
|
||||
└─ "low" [+nested: 1]
|
||||
└─ VariantSet: "material" (1 options)
|
||||
```
|
||||
|
||||
### variant-analyzer
|
||||
|
||||
Analyzes variant complexity and provides recommendations.
|
||||
|
||||
```bash
|
||||
# Basic analysis
|
||||
variant-analyzer model.usda
|
||||
|
||||
# Detailed breakdown
|
||||
variant-analyzer model.usda --detailed
|
||||
|
||||
# JSON output
|
||||
variant-analyzer model.usda --json
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```
|
||||
=== Variant Complexity Analysis ===
|
||||
File: model.usda
|
||||
|
||||
=== Summary ===
|
||||
Prims with variants: 2
|
||||
Total variant sets: 3
|
||||
Total variant options: 8
|
||||
|
||||
=== Statistics ===
|
||||
Max variants per prim: 2
|
||||
Max options per set: 4
|
||||
Avg options per set: 2.67
|
||||
Max nesting depth: 2
|
||||
Total combinations: 12
|
||||
|
||||
=== Complexity Assessment ===
|
||||
✓ Low nesting depth (2)
|
||||
✓ Manageable variant combinations (12)
|
||||
⚡ Medium-sized variant set (4 options)
|
||||
```
|
||||
|
||||
## Python Bindings
|
||||
|
||||
### Installation
|
||||
|
||||
Python bindings are included in the standard TinyUSDZ package:
|
||||
|
||||
```bash
|
||||
pip install tinyusdz
|
||||
```
|
||||
|
||||
### Available Classes
|
||||
|
||||
- `tinyusdz.tydra.VariantOption` - Variant option definition
|
||||
- `tinyusdz.tydra.VariantSet` - Set of variant options
|
||||
- `tinyusdz.tydra.VariantGroup` - All variants for a prim
|
||||
- `tinyusdz.tydra.VariantSelection` - Current variant selection
|
||||
- `tinyusdz.tydra.VariantConverter` - Extract variants from USD
|
||||
- `tinyusdz.tydra.DefaultVariantManager` - Manage variant selections
|
||||
- `tinyusdz.tydra.VariantStatistics` - Variant metrics
|
||||
|
||||
### Example Usage
|
||||
|
||||
```python
|
||||
import tinyusdz
|
||||
|
||||
# Load USD
|
||||
stage = tinyusdz.load_usd_from_file("model.usda")
|
||||
|
||||
# Extract variants
|
||||
scene = tinyusdz.tydra.RenderScene()
|
||||
converter = tinyusdz.tydra.VariantConverter()
|
||||
converter.convert_variants(stage, scene)
|
||||
|
||||
# Manage variants
|
||||
manager = tinyusdz.tydra.DefaultVariantManager()
|
||||
manager.set_variant_groups(scene.variant_groups)
|
||||
|
||||
# Select variant
|
||||
if manager.has_variants():
|
||||
groups = manager.get_variant_groups()
|
||||
for i, group in enumerate(groups):
|
||||
print(f"Group {i}: {group.prim_path}")
|
||||
for var_set in group.variant_sets:
|
||||
print(f" {var_set.name}: {[o.name for o in var_set.options]}")
|
||||
|
||||
# Select first option from first set
|
||||
if group.variant_sets:
|
||||
var_set = group.variant_sets[0]
|
||||
option_name = var_set.options[0].name if var_set.options else None
|
||||
if option_name:
|
||||
manager.select_variant(i, var_set.name, option_name)
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Organize Variants Hierarchically
|
||||
|
||||
Use nested variants for complex variation patterns:
|
||||
|
||||
```
|
||||
Asset/
|
||||
├── VariantSet "lod"
|
||||
│ ├── "high" (nested: material variant)
|
||||
│ ├── "medium" (nested: material variant)
|
||||
│ └── "low" (nested: material variant)
|
||||
└── VariantSet "color"
|
||||
├── "red"
|
||||
├── "blue"
|
||||
└── "green"
|
||||
```
|
||||
|
||||
### 2. Use Meaningful Names
|
||||
|
||||
Choose clear, descriptive names:
|
||||
|
||||
```
|
||||
✓ Good names:
|
||||
- "lod", "variant_lod", "lod_level"
|
||||
- "material_type", "material_variant"
|
||||
- "color_variant", "color"
|
||||
|
||||
✗ Avoid:
|
||||
- "v1", "v2", "var1"
|
||||
- "opt1", "option_a", "option_b"
|
||||
```
|
||||
|
||||
### 3. Set Appropriate Defaults
|
||||
|
||||
Always specify meaningful default selections:
|
||||
|
||||
```cpp
|
||||
variant_set.default_option_index = 0; // First option is default
|
||||
```
|
||||
|
||||
### 4. Validate Variant Selections
|
||||
|
||||
Always check return values:
|
||||
|
||||
```cpp
|
||||
std::string err;
|
||||
if (!manager.SelectVariant(0, "color", "red", &err)) {
|
||||
std::cerr << "Failed to select variant: " << err << "\n";
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Limit Nesting Depth
|
||||
|
||||
Keep variant nesting to 3 levels maximum for performance:
|
||||
|
||||
```
|
||||
Level 1: Prim variant sets
|
||||
Level 2: Option variant sets
|
||||
Level 3: Nested option variant sets
|
||||
```
|
||||
|
||||
### 6. Document Variant Content
|
||||
|
||||
Add descriptions to important variants:
|
||||
|
||||
```cpp
|
||||
option.description = "High-detail geometry (10M polygons)";
|
||||
```
|
||||
|
||||
### 7. Cache Variant Selections
|
||||
|
||||
For frequently accessed variants, store selections:
|
||||
|
||||
```python
|
||||
# Cache variant selection
|
||||
current_lod = manager.get_current_selection(0)
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Load and List Variants
|
||||
|
||||
**C++:**
|
||||
```cpp
|
||||
#include "tinyusdz.hh"
|
||||
#include "tydra/variant-converter.hh"
|
||||
|
||||
using namespace tinyusdz;
|
||||
|
||||
Stage stage;
|
||||
LoadUSDFromFile("model.usda", &stage, nullptr, nullptr);
|
||||
|
||||
RenderScene scene;
|
||||
VariantConverter converter;
|
||||
converter.ConvertVariants(stage, &scene, nullptr);
|
||||
|
||||
std::cout << "Found " << scene.variant_groups.size() << " variant groups:\n";
|
||||
for (const auto& group : scene.variant_groups) {
|
||||
std::cout << group.prim_path << ":\n";
|
||||
for (const auto& var_set : group.variant_sets) {
|
||||
std::cout << " " << var_set.name << ": ";
|
||||
for (const auto& opt : var_set.options) {
|
||||
std::cout << opt.name << " ";
|
||||
}
|
||||
std::cout << "\n";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Python:**
|
||||
```python
|
||||
import tinyusdz
|
||||
|
||||
stage = tinyusdz.load_usd_from_file("model.usda")
|
||||
scene = tinyusdz.tydra.RenderScene()
|
||||
converter = tinyusdz.tydra.VariantConverter()
|
||||
converter.convert_variants(stage, scene)
|
||||
|
||||
print(f"Found {len(scene.variant_groups)} variant groups:")
|
||||
for group in scene.variant_groups:
|
||||
print(f"{group.prim_path}:")
|
||||
for var_set in group.variant_sets:
|
||||
options = [opt.name for opt in var_set.options]
|
||||
print(f" {var_set.name}: {options}")
|
||||
```
|
||||
|
||||
### Example 2: Select Variants
|
||||
|
||||
**C++:**
|
||||
```cpp
|
||||
DefaultVariantManager manager;
|
||||
manager.SetVariantGroups(scene.variant_groups);
|
||||
|
||||
// Select "high" LOD
|
||||
if (manager.SelectVariant(0, "lod", "high")) {
|
||||
std::cout << "Switched to high LOD\n";
|
||||
}
|
||||
|
||||
// Get current selection
|
||||
const auto* selection = manager.GetCurrentSelection(0);
|
||||
if (selection) {
|
||||
std::cout << "Current selection index: " << selection->selected_option_index << "\n";
|
||||
}
|
||||
```
|
||||
|
||||
**Python:**
|
||||
```python
|
||||
manager = tinyusdz.tydra.DefaultVariantManager()
|
||||
manager.set_variant_groups(scene.variant_groups)
|
||||
|
||||
# Select "high" LOD
|
||||
if manager.select_variant(0, "lod", "high"):
|
||||
print("Switched to high LOD")
|
||||
|
||||
# Get current selection
|
||||
selection = manager.get_current_selection(0)
|
||||
if selection:
|
||||
print(f"Current selection: {selection.selected_option_index}")
|
||||
```
|
||||
|
||||
### Example 3: Analyze Complexity
|
||||
|
||||
```bash
|
||||
# Analyze variant complexity
|
||||
variant-analyzer model.usda --detailed
|
||||
|
||||
# Output:
|
||||
# Prim 1: /Characters/Hero
|
||||
# Variant sets: 2
|
||||
# - "lod": 3 options [with nested variants]
|
||||
# Possible combinations: 6
|
||||
# - "material": 2 options
|
||||
# Possible combinations: 2
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### No variants found in file
|
||||
|
||||
**Cause:** File doesn't contain variant definitions or they use unsupported structure
|
||||
|
||||
**Solution:**
|
||||
1. Verify file contains variant sets in USD
|
||||
2. Check with `variant-lister model.usda`
|
||||
3. Ensure variants are defined in the prim spec, not overrides
|
||||
|
||||
### Variant selection fails
|
||||
|
||||
**Cause:** Invalid variant set or option name, or selection already active
|
||||
|
||||
**Solution:**
|
||||
1. List available options: `variant-lister model.usda`
|
||||
2. Check for typos in names (case-sensitive)
|
||||
3. Verify variant group ID is valid (0-based index)
|
||||
|
||||
### Slow variant operations
|
||||
|
||||
**Cause:** High nesting depth or many variants
|
||||
|
||||
**Solution:**
|
||||
1. Check complexity: `variant-analyzer model.usda`
|
||||
2. Reduce nesting depth if possible
|
||||
3. Consolidate related variants
|
||||
|
||||
## Performance Characteristics
|
||||
|
||||
| Operation | Complexity | Notes |
|
||||
|-----------|-----------|-------|
|
||||
| Load USD file | O(n) | n = file size |
|
||||
| Convert variants | O(p * v) | p = prims, v = avg variants |
|
||||
| Select variant | O(1) | Hash-based lookup |
|
||||
| Query selection | O(1) | Direct array access |
|
||||
| Get statistics | O(p * v) | One-time calculation |
|
||||
| Reset defaults | O(s) | s = num selections |
|
||||
|
||||
## See Also
|
||||
|
||||
- [USD Variant Documentation](https://openusd.org/docs/api/class_usd_variant_set.html)
|
||||
- [glTF Material Variants Extension](https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_materials_variants)
|
||||
- TinyUSDZ Main Documentation
|
||||
121
python/examples/variant_example.py
Normal file
121
python/examples/variant_example.py
Normal file
@@ -0,0 +1,121 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Example: Working with USD Variants using TinyUSDZ Python Bindings
|
||||
|
||||
This example demonstrates how to:
|
||||
1. Load a USD file
|
||||
2. Extract variant information using VariantConverter
|
||||
3. List all available variants
|
||||
4. Select different variants
|
||||
5. Query variant statistics
|
||||
"""
|
||||
|
||||
import tinyusdz
|
||||
|
||||
def list_variants(stage):
|
||||
"""Extract and list all variants in a USD stage."""
|
||||
scene = tinyusdz.tydra.RenderScene()
|
||||
converter = tinyusdz.tydra.VariantConverter()
|
||||
|
||||
if not converter.convert_variants(stage, scene):
|
||||
print("Failed to convert variants")
|
||||
return None
|
||||
|
||||
print(f"Found {len(scene.variant_groups)} variant groups:\n")
|
||||
|
||||
for group in scene.variant_groups:
|
||||
print(f"Prim: {group.prim_path}")
|
||||
print(f" Variant sets: {len(group.variant_sets)}")
|
||||
|
||||
for variant_set in group.variant_sets:
|
||||
print(f" - {variant_set.name}")
|
||||
print(f" Options: {[opt.name for opt in variant_set.options]}")
|
||||
print(f" Default: {variant_set.options[variant_set.default_option_index].name}")
|
||||
print()
|
||||
|
||||
return scene
|
||||
|
||||
|
||||
def work_with_variants(scene):
|
||||
"""Demonstrate how to use the VariantManager API."""
|
||||
manager = tinyusdz.tydra.DefaultVariantManager()
|
||||
manager.set_variant_groups(scene.variant_groups)
|
||||
|
||||
# Get variant statistics
|
||||
stats = manager.get_statistics()
|
||||
print("Variant Statistics:")
|
||||
print(f" Total groups: {stats.num_variant_groups}")
|
||||
print(f" Total sets: {stats.num_variant_sets}")
|
||||
print(f" Total options: {stats.num_variant_options}")
|
||||
print(f" Max nesting depth: {stats.max_nesting_depth}\n")
|
||||
|
||||
# Find specific variant group
|
||||
if manager.has_variants():
|
||||
# Get first variant group
|
||||
groups = manager.get_variant_groups()
|
||||
if groups:
|
||||
first_group = groups[0]
|
||||
print(f"First variant group: {first_group.prim_path}")
|
||||
|
||||
# Try to select a variant
|
||||
if first_group.variant_sets:
|
||||
var_set = first_group.variant_sets[0]
|
||||
if var_set.options:
|
||||
option_name = var_set.options[0].name
|
||||
print(f" Selecting '{option_name}' from variant set '{var_set.name}'")
|
||||
|
||||
# Select the variant
|
||||
success = manager.select_variant(0, var_set.name, option_name)
|
||||
if success:
|
||||
print(f" ✓ Variant selection successful")
|
||||
|
||||
# Get current selection
|
||||
selection = manager.get_current_selection(0)
|
||||
if selection:
|
||||
print(f" Current selection: {selection.selected_option_index}")
|
||||
else:
|
||||
print(f" ✗ Failed to select variant")
|
||||
|
||||
# Reset to defaults
|
||||
print("\n Resetting to defaults...")
|
||||
manager.reset_to_defaults()
|
||||
print(" ✓ Reset complete")
|
||||
|
||||
|
||||
def main():
|
||||
"""Main example demonstrating variant API usage."""
|
||||
import sys
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: python variant_example.py <usd_file>")
|
||||
print("\nExample:")
|
||||
print(" python variant_example.py model.usda")
|
||||
sys.exit(1)
|
||||
|
||||
usd_file = sys.argv[1]
|
||||
|
||||
# Load USD file
|
||||
print(f"Loading USD file: {usd_file}\n")
|
||||
stage = tinyusdz.load_usd_from_file(usd_file)
|
||||
if not stage:
|
||||
print(f"Error: Failed to load {usd_file}")
|
||||
sys.exit(1)
|
||||
|
||||
# Extract and list variants
|
||||
print("=" * 60)
|
||||
print("VARIANT LISTING")
|
||||
print("=" * 60 + "\n")
|
||||
scene = list_variants(stage)
|
||||
|
||||
if scene and scene.variant_groups:
|
||||
# Work with variants
|
||||
print("=" * 60)
|
||||
print("VARIANT MANAGEMENT")
|
||||
print("=" * 60 + "\n")
|
||||
work_with_variants(scene)
|
||||
else:
|
||||
print("No variants found in the USD file.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -7,6 +7,8 @@
|
||||
#include "tinyusdz.hh"
|
||||
#include "prim-pprint.hh"
|
||||
#include "tydra/render-data.hh"
|
||||
#include "tydra/variant-support.hh"
|
||||
#include "tydra/variant-converter.hh"
|
||||
//
|
||||
#include "value-type-macros.inc"
|
||||
|
||||
@@ -508,5 +510,94 @@ PYBIND11_MODULE(ctinyusdz, m) {
|
||||
m_tydra.def("to_render_scene", [](const Stage &stage) {
|
||||
py::print("TODO");
|
||||
}, py::arg("config") = tydra::RenderSceneConverterConfig());
|
||||
|
||||
// Variant support bindings
|
||||
py::class_<tydra::VariantOption>(m_tydra, "VariantOption")
|
||||
.def(py::init<>())
|
||||
.def_readwrite("name", &tydra::VariantOption::name)
|
||||
.def_readwrite("description", &tydra::VariantOption::description)
|
||||
.def_readwrite("mesh_ids", &tydra::VariantOption::mesh_ids)
|
||||
.def_readwrite("material_ids", &tydra::VariantOption::material_ids)
|
||||
.def_readwrite("node_ids", &tydra::VariantOption::node_ids)
|
||||
.def_readwrite("animation_ids", &tydra::VariantOption::animation_ids)
|
||||
.def_readwrite("property_overrides", &tydra::VariantOption::property_overrides)
|
||||
;
|
||||
|
||||
py::class_<tydra::VariantSet>(m_tydra, "VariantSet")
|
||||
.def(py::init<>())
|
||||
.def_readwrite("name", &tydra::VariantSet::name)
|
||||
.def_readwrite("options", &tydra::VariantSet::options)
|
||||
.def_readwrite("default_option_index", &tydra::VariantSet::default_option_index)
|
||||
.def_readwrite("parent_prim_id", &tydra::VariantSet::parent_prim_id)
|
||||
.def_readwrite("parent_variant_option_name", &tydra::VariantSet::parent_variant_option_name)
|
||||
;
|
||||
|
||||
py::class_<tydra::VariantGroup>(m_tydra, "VariantGroup")
|
||||
.def(py::init<>())
|
||||
.def_readwrite("prim_path", &tydra::VariantGroup::prim_path)
|
||||
.def_readwrite("variant_sets", &tydra::VariantGroup::variant_sets)
|
||||
.def_readwrite("affected_node_id", &tydra::VariantGroup::affected_node_id)
|
||||
.def_readwrite("secondary_node_ids", &tydra::VariantGroup::secondary_node_ids)
|
||||
;
|
||||
|
||||
py::class_<tydra::VariantSelection>(m_tydra, "VariantSelection")
|
||||
.def(py::init<>())
|
||||
.def_readwrite("variant_group_id", &tydra::VariantSelection::variant_group_id)
|
||||
.def_readwrite("variant_set_id", &tydra::VariantSelection::variant_set_id)
|
||||
.def_readwrite("selected_option_index", &tydra::VariantSelection::selected_option_index)
|
||||
;
|
||||
|
||||
py::class_<tydra::VariantConverter>(m_tydra, "VariantConverter")
|
||||
.def(py::init<>())
|
||||
.def("convert_variants", [](tydra::VariantConverter &converter, const Stage &stage, RenderScene &scene) -> bool {
|
||||
std::string err;
|
||||
return converter.ConvertVariants(stage, &scene, &err);
|
||||
}, py::arg("stage"), py::arg("scene"))
|
||||
;
|
||||
|
||||
// VariantStatistics
|
||||
py::class_<tydra::VariantStatistics>(m_tydra, "VariantStatistics")
|
||||
.def(py::init<>())
|
||||
.def_readwrite("num_variant_groups", &tydra::VariantStatistics::num_variant_groups)
|
||||
.def_readwrite("num_variant_sets", &tydra::VariantStatistics::num_variant_sets)
|
||||
.def_readwrite("num_variant_options", &tydra::VariantStatistics::num_variant_options)
|
||||
.def_readwrite("max_nesting_depth", &tydra::VariantStatistics::max_nesting_depth)
|
||||
;
|
||||
|
||||
py::class_<tydra::DefaultVariantManager>(m_tydra, "DefaultVariantManager")
|
||||
.def(py::init<>())
|
||||
.def("has_variants", &tydra::DefaultVariantManager::HasVariants)
|
||||
.def("find_variant_group", &tydra::DefaultVariantManager::FindVariantGroup,
|
||||
py::arg("prim_path"), py::return_value_policy::reference)
|
||||
.def("find_variant_set", [](tydra::DefaultVariantManager &mgr, int32_t group_id, const std::string &set_name) -> tydra::VariantSet* {
|
||||
return mgr.FindVariantSet(group_id, set_name);
|
||||
}, py::arg("group_id"), py::arg("set_name"), py::return_value_policy::reference)
|
||||
.def("find_variant_option", [](tydra::DefaultVariantManager &mgr, int32_t group_id, int32_t set_id, const std::string &option_name) -> tydra::VariantOption* {
|
||||
return mgr.FindVariantOption(group_id, set_id, option_name);
|
||||
}, py::arg("group_id"), py::arg("set_id"), py::arg("option_name"), py::return_value_policy::reference)
|
||||
.def("select_variant", [](tydra::DefaultVariantManager &mgr, int32_t group_id, const std::string &set_name, const std::string &option_name) -> bool {
|
||||
std::string err;
|
||||
return mgr.SelectVariant(group_id, set_name, option_name, &err);
|
||||
}, py::arg("group_id"), py::arg("set_name"), py::arg("option_name"))
|
||||
.def("select_variant_by_index", [](tydra::DefaultVariantManager &mgr, int32_t group_id, int32_t set_id, int32_t option_index) -> bool {
|
||||
std::string err;
|
||||
return mgr.SelectVariantByIndex(group_id, set_id, option_index, &err);
|
||||
}, py::arg("group_id"), py::arg("set_id"), py::arg("option_index"))
|
||||
.def("get_current_selection", &tydra::DefaultVariantManager::GetCurrentSelection,
|
||||
py::arg("group_id"), py::return_value_policy::reference)
|
||||
.def("get_all_selections", &tydra::DefaultVariantManager::GetAllSelections,
|
||||
py::return_value_policy::reference)
|
||||
.def("reset_to_defaults", [](tydra::DefaultVariantManager &mgr) -> bool {
|
||||
std::string err;
|
||||
return mgr.ResetToDefaults(&err);
|
||||
})
|
||||
.def("get_mutable_variant_groups", &tydra::DefaultVariantManager::GetMutableVariantGroups,
|
||||
py::return_value_policy::reference)
|
||||
.def("get_variant_groups", &tydra::DefaultVariantManager::GetVariantGroups,
|
||||
py::return_value_policy::reference)
|
||||
.def("set_variant_groups", &tydra::DefaultVariantManager::SetVariantGroups,
|
||||
py::arg("groups"))
|
||||
.def("get_statistics", &tydra::DefaultVariantManager::GetStatistics)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user