mirror of
https://github.com/lighttransport/tinyusdz.git
synced 2026-01-18 01:11:17 +01:00
imporove USD <-> JSON conversion
This commit is contained in:
266
primspec-search-demo.md
Normal file
266
primspec-search-demo.md
Normal file
@@ -0,0 +1,266 @@
|
|||||||
|
# PrimSpec JavaScript Functions Demo
|
||||||
|
|
||||||
|
This demonstrates the new JavaScript functions that have been added to the TinyUSDZ JavaScript scripting interface for working with PrimSpecs:
|
||||||
|
|
||||||
|
1. `findPrimSpecByPath()` - Search for PrimSpec by absolute path
|
||||||
|
2. `getPrimSpecMetadata()` - Get PrimSpec metadata by absolute path
|
||||||
|
|
||||||
|
## Function Overview
|
||||||
|
|
||||||
|
### 1. findPrimSpecByPath(pathString)
|
||||||
|
|
||||||
|
Searches for and retrieves basic PrimSpec information by absolute path string.
|
||||||
|
|
||||||
|
**Function Signature:**
|
||||||
|
```javascript
|
||||||
|
findPrimSpecByPath(pathString) -> PrimSpec object | null
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Parameters**:
|
||||||
|
- `pathString` (string): Absolute USD path string (e.g., "/root/child/prim")
|
||||||
|
- **Returns**:
|
||||||
|
- PrimSpec object if found
|
||||||
|
- `null` if not found or invalid path
|
||||||
|
|
||||||
|
### 2. getPrimSpecMetadata(pathString)
|
||||||
|
|
||||||
|
Retrieves detailed metadata information for a PrimSpec by absolute path string.
|
||||||
|
|
||||||
|
**Function Signature:**
|
||||||
|
```javascript
|
||||||
|
getPrimSpecMetadata(pathString) -> Metadata object | null
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Parameters**:
|
||||||
|
- `pathString` (string): Absolute USD path string (e.g., "/root/child/prim")
|
||||||
|
- **Returns**:
|
||||||
|
- Metadata object if found
|
||||||
|
- `null` if not found or invalid path
|
||||||
|
|
||||||
|
### PrimSpec Object Structure
|
||||||
|
When found, the function returns a JSON object with the following structure:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"name": "primName", // Name of the PrimSpec
|
||||||
|
"typeName": "Mesh", // Type name (e.g., "Mesh", "Xform", etc.)
|
||||||
|
"specifier": "def", // Specifier: "def", "over", "class", or "invalid"
|
||||||
|
"propertyCount": 5, // Number of properties
|
||||||
|
"childrenCount": 2, // Number of child PrimSpecs
|
||||||
|
"propertyNames": [ // Array of property names
|
||||||
|
"points",
|
||||||
|
"faceVertexIndices",
|
||||||
|
"extent"
|
||||||
|
],
|
||||||
|
"childrenNames": [ // Array of child PrimSpec names
|
||||||
|
"material",
|
||||||
|
"childPrim"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Metadata Object Structure
|
||||||
|
When found, the `getPrimSpecMetadata()` function returns a JSON object with the following structure:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"active": true, // Active state (true/false/null)
|
||||||
|
"hidden": false, // Hidden state (true/false/null)
|
||||||
|
"instanceable": null, // Instanceable state (true/false/null)
|
||||||
|
"kind": "component", // USD Kind (string)
|
||||||
|
"documentation": "This is a mesh", // Documentation (string/null)
|
||||||
|
"comment": "Auto-generated", // Comment (string/null)
|
||||||
|
"displayName": "My Mesh", // Display name (string/null)
|
||||||
|
"sceneName": "Scene1", // Scene name (string/null)
|
||||||
|
"hasReferences": true, // Whether prim has references
|
||||||
|
"referencesCount": 2, // Number of references
|
||||||
|
"hasPayload": false, // Whether prim has payload
|
||||||
|
"payloadCount": 0, // Number of payloads
|
||||||
|
"hasInherits": false, // Whether prim has inherits
|
||||||
|
"inheritsCount": 0, // Number of inherits
|
||||||
|
"hasVariants": true, // Whether prim has variants
|
||||||
|
"variantsCount": 2, // Number of variant selections
|
||||||
|
"variantNames": ["modelingVariant", "shadingVariant"], // Variant names
|
||||||
|
"hasVariantSets": true, // Whether prim has variant sets
|
||||||
|
"variantSetsCount": 1, // Number of variant sets
|
||||||
|
"variantSetNames": ["material"], // Variant set names
|
||||||
|
"hasCustomData": true, // Whether prim has custom data
|
||||||
|
"hasAssetInfo": false, // Whether prim has asset info
|
||||||
|
"unregisteredMetasCount": 3, // Number of unregistered metadata
|
||||||
|
"unregisteredMetaNames": ["myCustomMeta1", "myCustomMeta2"], // Custom metadata names
|
||||||
|
"authored": true // Whether metadata is authored
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage Example
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Search for a specific PrimSpec
|
||||||
|
var rootPrim = findPrimSpecByPath("/Root");
|
||||||
|
if (rootPrim) {
|
||||||
|
console.log("Found root prim:", rootPrim.name);
|
||||||
|
console.log("Type:", rootPrim.typeName);
|
||||||
|
console.log("Properties:", rootPrim.propertyNames);
|
||||||
|
console.log("Children:", rootPrim.childrenNames);
|
||||||
|
} else {
|
||||||
|
console.log("Root prim not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get metadata for the same PrimSpec
|
||||||
|
var rootMeta = getPrimSpecMetadata("/Root");
|
||||||
|
if (rootMeta) {
|
||||||
|
console.log("Root prim is active:", rootMeta.active);
|
||||||
|
console.log("Kind:", rootMeta.kind);
|
||||||
|
console.log("Has references:", rootMeta.hasReferences);
|
||||||
|
console.log("Variant sets:", rootMeta.variantSetNames);
|
||||||
|
|
||||||
|
if (rootMeta.documentation) {
|
||||||
|
console.log("Documentation:", rootMeta.documentation);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rootMeta.unregisteredMetasCount > 0) {
|
||||||
|
console.log("Custom metadata:", rootMeta.unregisteredMetaNames);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log("Root prim metadata not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search for a nested PrimSpec and its metadata
|
||||||
|
var meshPrim = findPrimSpecByPath("/Root/Geometry/Mesh");
|
||||||
|
var meshMeta = getPrimSpecMetadata("/Root/Geometry/Mesh");
|
||||||
|
|
||||||
|
if (meshPrim && meshMeta) {
|
||||||
|
console.log("Found mesh with", meshPrim.propertyCount, "properties");
|
||||||
|
console.log("Mesh is instanceable:", meshMeta.instanceable);
|
||||||
|
console.log("Mesh variants:", meshMeta.variantNames);
|
||||||
|
} else {
|
||||||
|
console.log("Mesh or its metadata not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example: Check if a prim has composition arcs
|
||||||
|
var composedPrimMeta = getPrimSpecMetadata("/ComposedPrim");
|
||||||
|
if (composedPrimMeta) {
|
||||||
|
var hasComposition = composedPrimMeta.hasReferences ||
|
||||||
|
composedPrimMeta.hasPayload ||
|
||||||
|
composedPrimMeta.hasInherits;
|
||||||
|
|
||||||
|
if (hasComposition) {
|
||||||
|
console.log("Prim has composition arcs:");
|
||||||
|
if (composedPrimMeta.hasReferences) {
|
||||||
|
console.log("- References:", composedPrimMeta.referencesCount);
|
||||||
|
}
|
||||||
|
if (composedPrimMeta.hasPayload) {
|
||||||
|
console.log("- Payloads:", composedPrimMeta.payloadCount);
|
||||||
|
}
|
||||||
|
if (composedPrimMeta.hasInherits) {
|
||||||
|
console.log("- Inherits:", composedPrimMeta.inheritsCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## C++ Integration
|
||||||
|
|
||||||
|
To use these functions from C++, you need to:
|
||||||
|
|
||||||
|
1. Create or load a USD Layer
|
||||||
|
2. Use `RunJSScriptWithLayer()` to execute JavaScript code with access to the Layer
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include "src/tydra/js-script.hh"
|
||||||
|
|
||||||
|
// Assuming you have a Layer object
|
||||||
|
tinyusdz::Layer layer;
|
||||||
|
// ... populate layer with PrimSpecs ...
|
||||||
|
|
||||||
|
std::string jsCode = R"(
|
||||||
|
// Find PrimSpec basic info
|
||||||
|
var prim = findPrimSpecByPath("/myPrim");
|
||||||
|
if (prim) {
|
||||||
|
console.log("Found:", prim.name, "type:", prim.typeName);
|
||||||
|
console.log("Properties:", prim.propertyCount);
|
||||||
|
console.log("Children:", prim.childrenCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get detailed metadata
|
||||||
|
var meta = getPrimSpecMetadata("/myPrim");
|
||||||
|
if (meta) {
|
||||||
|
console.log("Active:", meta.active);
|
||||||
|
console.log("Kind:", meta.kind);
|
||||||
|
console.log("Has composition:",
|
||||||
|
meta.hasReferences || meta.hasPayload || meta.hasInherits);
|
||||||
|
|
||||||
|
if (meta.hasVariants) {
|
||||||
|
console.log("Variants:", meta.variantNames);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
std::string err;
|
||||||
|
bool success = tinyusdz::tydra::RunJSScriptWithLayer(jsCode, &layer, err);
|
||||||
|
if (!success) {
|
||||||
|
std::cerr << "JavaScript error: " << err << std::endl;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Implementation Details
|
||||||
|
|
||||||
|
Both functions are implemented in `src/tydra/js-script.cc` and include:
|
||||||
|
|
||||||
|
### Common Features:
|
||||||
|
- **Path validation**: Ensures the input string is a valid USD path
|
||||||
|
- **Layer search**: Uses the existing `Layer::find_primspec_at()` method
|
||||||
|
- **Error handling**: Returns `null` for invalid paths or missing PrimSpecs
|
||||||
|
|
||||||
|
### findPrimSpecByPath():
|
||||||
|
- **JSON conversion**: Converts basic PrimSpec data to JSON
|
||||||
|
- **Structure info**: Provides name, type, properties, and children info
|
||||||
|
|
||||||
|
### getPrimSpecMetadata():
|
||||||
|
- **Comprehensive metadata**: Extracts all USD metadata fields
|
||||||
|
- **Composition info**: Details about references, payloads, inherits
|
||||||
|
- **Variant info**: Information about variants and variant sets
|
||||||
|
- **Custom metadata**: Handles unregistered metadata fields
|
||||||
|
- **Boolean flags**: Convenient flags for common checks
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- TinyUSDZ must be built with `TINYUSDZ_WITH_QJS=ON` to enable QuickJS support
|
||||||
|
- The functions are only available when using `RunJSScriptWithLayer()`
|
||||||
|
|
||||||
|
## Error Cases
|
||||||
|
|
||||||
|
Both functions return `null` in these cases:
|
||||||
|
- Invalid path string (empty, malformed)
|
||||||
|
- Path not found in the Layer
|
||||||
|
- Relative paths (not yet supported)
|
||||||
|
- No Layer context available
|
||||||
|
|
||||||
|
## Metadata Fields Reference
|
||||||
|
|
||||||
|
The `getPrimSpecMetadata()` function provides access to the following USD metadata:
|
||||||
|
|
||||||
|
### Core Metadata:
|
||||||
|
- `active` - Whether the prim is active in the scene
|
||||||
|
- `hidden` - Whether the prim is hidden from traversal
|
||||||
|
- `instanceable` - Whether the prim can be instanced
|
||||||
|
- `kind` - USD Kind classification (model, component, assembly, etc.)
|
||||||
|
|
||||||
|
### Documentation:
|
||||||
|
- `documentation` - Formal documentation string
|
||||||
|
- `comment` - Informal comment string
|
||||||
|
- `displayName` - Human-readable display name (extension)
|
||||||
|
- `sceneName` - Scene name (USDZ extension)
|
||||||
|
|
||||||
|
### Composition Arcs:
|
||||||
|
- `hasReferences` / `referencesCount` - Reference composition
|
||||||
|
- `hasPayload` / `payloadCount` - Payload composition
|
||||||
|
- `hasInherits` / `inheritsCount` - Inheritance composition
|
||||||
|
|
||||||
|
### Variants:
|
||||||
|
- `hasVariants` / `variantsCount` / `variantNames` - Variant selections
|
||||||
|
- `hasVariantSets` / `variantSetsCount` / `variantSetNames` - Variant set definitions
|
||||||
|
|
||||||
|
### Custom Data:
|
||||||
|
- `hasCustomData` - Whether prim has custom data dictionary
|
||||||
|
- `hasAssetInfo` - Whether prim has asset info dictionary
|
||||||
|
- `unregisteredMetasCount` / `unregisteredMetaNames` - Custom metadata fields
|
||||||
|
- `authored` - Whether any metadata is authored
|
||||||
@@ -151,6 +151,7 @@ static std::string LayerMetasToJSON(const LayerMetas* metas) {
|
|||||||
|
|
||||||
static const LayerMetas* g_current_layer_metas = nullptr;
|
static const LayerMetas* g_current_layer_metas = nullptr;
|
||||||
static const Attribute* g_current_attribute = nullptr;
|
static const Attribute* g_current_attribute = nullptr;
|
||||||
|
static const class Layer* g_current_layer = nullptr;
|
||||||
|
|
||||||
// JavaScript fp16 library and TUSDZFloat16Array implementation
|
// JavaScript fp16 library and TUSDZFloat16Array implementation
|
||||||
static const char* fp16_library_js = R"(
|
static const char* fp16_library_js = R"(
|
||||||
@@ -845,6 +846,223 @@ static std::string AttributeToJSON(const Attribute* attr) {
|
|||||||
return oss.str();
|
return oss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::string PrimMetasToJSON(const PrimMeta* metas) {
|
||||||
|
if (!metas) {
|
||||||
|
return "null";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << std::setprecision(17);
|
||||||
|
oss << "{";
|
||||||
|
|
||||||
|
// Basic metadata flags
|
||||||
|
oss << "\"active\":";
|
||||||
|
if (metas->active.has_value()) {
|
||||||
|
oss << (metas->active.value() ? "true" : "false");
|
||||||
|
} else {
|
||||||
|
oss << "null";
|
||||||
|
}
|
||||||
|
oss << ",";
|
||||||
|
|
||||||
|
oss << "\"hidden\":";
|
||||||
|
if (metas->hidden.has_value()) {
|
||||||
|
oss << (metas->hidden.value() ? "true" : "false");
|
||||||
|
} else {
|
||||||
|
oss << "null";
|
||||||
|
}
|
||||||
|
oss << ",";
|
||||||
|
|
||||||
|
oss << "\"instanceable\":";
|
||||||
|
if (metas->instanceable.has_value()) {
|
||||||
|
oss << (metas->instanceable.value() ? "true" : "false");
|
||||||
|
} else {
|
||||||
|
oss << "null";
|
||||||
|
}
|
||||||
|
oss << ",";
|
||||||
|
|
||||||
|
// Kind
|
||||||
|
oss << "\"kind\":\"" << metas->get_kind() << "\",";
|
||||||
|
|
||||||
|
// Documentation and comment
|
||||||
|
oss << "\"documentation\":";
|
||||||
|
if (metas->doc.has_value()) {
|
||||||
|
oss << "\"" << metas->doc.value().value << "\"";
|
||||||
|
} else {
|
||||||
|
oss << "null";
|
||||||
|
}
|
||||||
|
oss << ",";
|
||||||
|
|
||||||
|
oss << "\"comment\":";
|
||||||
|
if (metas->comment.has_value()) {
|
||||||
|
oss << "\"" << metas->comment.value().value << "\"";
|
||||||
|
} else {
|
||||||
|
oss << "null";
|
||||||
|
}
|
||||||
|
oss << ",";
|
||||||
|
|
||||||
|
// Display name and scene name (extensions)
|
||||||
|
oss << "\"displayName\":";
|
||||||
|
if (metas->displayName.has_value()) {
|
||||||
|
oss << "\"" << metas->displayName.value() << "\"";
|
||||||
|
} else {
|
||||||
|
oss << "null";
|
||||||
|
}
|
||||||
|
oss << ",";
|
||||||
|
|
||||||
|
oss << "\"sceneName\":";
|
||||||
|
if (metas->sceneName.has_value()) {
|
||||||
|
oss << "\"" << metas->sceneName.value() << "\"";
|
||||||
|
} else {
|
||||||
|
oss << "null";
|
||||||
|
}
|
||||||
|
oss << ",";
|
||||||
|
|
||||||
|
// References count
|
||||||
|
oss << "\"hasReferences\":";
|
||||||
|
if (metas->references.has_value() && !metas->references.value().second.empty()) {
|
||||||
|
oss << "true,";
|
||||||
|
oss << "\"referencesCount\":" << metas->references.value().second.size();
|
||||||
|
} else {
|
||||||
|
oss << "false,";
|
||||||
|
oss << "\"referencesCount\":0";
|
||||||
|
}
|
||||||
|
oss << ",";
|
||||||
|
|
||||||
|
// Payload count
|
||||||
|
oss << "\"hasPayload\":";
|
||||||
|
if (metas->payload.has_value() && !metas->payload.value().second.empty()) {
|
||||||
|
oss << "true,";
|
||||||
|
oss << "\"payloadCount\":" << metas->payload.value().second.size();
|
||||||
|
} else {
|
||||||
|
oss << "false,";
|
||||||
|
oss << "\"payloadCount\":0";
|
||||||
|
}
|
||||||
|
oss << ",";
|
||||||
|
|
||||||
|
// Inherits count
|
||||||
|
oss << "\"hasInherits\":";
|
||||||
|
if (metas->inherits.has_value() && !metas->inherits.value().second.empty()) {
|
||||||
|
oss << "true,";
|
||||||
|
oss << "\"inheritsCount\":" << metas->inherits.value().second.size();
|
||||||
|
} else {
|
||||||
|
oss << "false,";
|
||||||
|
oss << "\"inheritsCount\":0";
|
||||||
|
}
|
||||||
|
oss << ",";
|
||||||
|
|
||||||
|
// Variants info
|
||||||
|
oss << "\"hasVariants\":";
|
||||||
|
if (metas->variants.has_value() && !metas->variants.value().empty()) {
|
||||||
|
oss << "true,";
|
||||||
|
oss << "\"variantsCount\":" << metas->variants.value().size() << ",";
|
||||||
|
oss << "\"variantNames\":[";
|
||||||
|
bool first = true;
|
||||||
|
for (const auto& variant : metas->variants.value()) {
|
||||||
|
if (!first) oss << ",";
|
||||||
|
oss << "\"" << variant.first << "\"";
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
oss << "]";
|
||||||
|
} else {
|
||||||
|
oss << "false,";
|
||||||
|
oss << "\"variantsCount\":0,";
|
||||||
|
oss << "\"variantNames\":[]";
|
||||||
|
}
|
||||||
|
oss << ",";
|
||||||
|
|
||||||
|
// VariantSets info
|
||||||
|
oss << "\"hasVariantSets\":";
|
||||||
|
if (metas->variantSets.has_value() && !metas->variantSets.value().second.empty()) {
|
||||||
|
oss << "true,";
|
||||||
|
oss << "\"variantSetsCount\":" << metas->variantSets.value().second.size() << ",";
|
||||||
|
oss << "\"variantSetNames\":[";
|
||||||
|
bool first = true;
|
||||||
|
for (const auto& varSet : metas->variantSets.value().second) {
|
||||||
|
if (!first) oss << ",";
|
||||||
|
oss << "\"" << varSet << "\"";
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
oss << "]";
|
||||||
|
} else {
|
||||||
|
oss << "false,";
|
||||||
|
oss << "\"variantSetsCount\":0,";
|
||||||
|
oss << "\"variantSetNames\":[]";
|
||||||
|
}
|
||||||
|
oss << ",";
|
||||||
|
|
||||||
|
// Custom data and unregistered metas
|
||||||
|
oss << "\"hasCustomData\":" << (metas->customData.has_value() ? "true" : "false") << ",";
|
||||||
|
oss << "\"hasAssetInfo\":" << (metas->assetInfo.has_value() ? "true" : "false") << ",";
|
||||||
|
oss << "\"unregisteredMetasCount\":" << metas->unregisteredMetas.size() << ",";
|
||||||
|
|
||||||
|
// Unregistered metadata names
|
||||||
|
oss << "\"unregisteredMetaNames\":[";
|
||||||
|
bool first = true;
|
||||||
|
for (const auto& meta : metas->unregisteredMetas) {
|
||||||
|
if (!first) oss << ",";
|
||||||
|
oss << "\"" << meta.first << "\"";
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
oss << "],";
|
||||||
|
|
||||||
|
// Authored flag
|
||||||
|
oss << "\"authored\":" << (metas->authored() ? "true" : "false");
|
||||||
|
|
||||||
|
oss << "}";
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string PrimSpecToJSON(const PrimSpec* ps) {
|
||||||
|
if (!ps) {
|
||||||
|
return "null";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << std::setprecision(17);
|
||||||
|
oss << "{";
|
||||||
|
|
||||||
|
// Basic PrimSpec info
|
||||||
|
oss << "\"name\":\"" << ps->name() << "\",";
|
||||||
|
oss << "\"typeName\":\"" << ps->typeName() << "\",";
|
||||||
|
oss << "\"specifier\":\"";
|
||||||
|
switch (ps->specifier()) {
|
||||||
|
case Specifier::Def: oss << "def"; break;
|
||||||
|
case Specifier::Over: oss << "over"; break;
|
||||||
|
case Specifier::Class: oss << "class"; break;
|
||||||
|
case Specifier::Invalid: oss << "invalid"; break;
|
||||||
|
}
|
||||||
|
oss << "\",";
|
||||||
|
|
||||||
|
// Add property count
|
||||||
|
oss << "\"propertyCount\":" << ps->props().size() << ",";
|
||||||
|
|
||||||
|
// Add children count
|
||||||
|
oss << "\"childrenCount\":" << ps->children().size() << ",";
|
||||||
|
|
||||||
|
// Property names array
|
||||||
|
oss << "\"propertyNames\":[";
|
||||||
|
bool first = true;
|
||||||
|
for (const auto& prop : ps->props()) {
|
||||||
|
if (!first) oss << ",";
|
||||||
|
oss << "\"" << prop.first << "\"";
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
oss << "],";
|
||||||
|
|
||||||
|
// Children names array
|
||||||
|
oss << "\"childrenNames\":[";
|
||||||
|
first = true;
|
||||||
|
for (const auto& child : ps->children()) {
|
||||||
|
if (!first) oss << ",";
|
||||||
|
oss << "\"" << child.name() << "\"";
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
oss << "]";
|
||||||
|
|
||||||
|
oss << "}";
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
static JSValue js_getLayerMetas(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic, JSValueConst *func_data) {
|
static JSValue js_getLayerMetas(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic, JSValueConst *func_data) {
|
||||||
std::string json = LayerMetasToJSON(g_current_layer_metas);
|
std::string json = LayerMetasToJSON(g_current_layer_metas);
|
||||||
|
|
||||||
@@ -865,6 +1083,88 @@ static JSValue js_getAttribute(JSContext *ctx, JSValueConst this_val, int argc,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static JSValue js_findPrimSpecByPath(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic, JSValueConst *func_data) {
|
||||||
|
if (!g_current_layer) {
|
||||||
|
return JS_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argc < 1) {
|
||||||
|
return JS_ThrowTypeError(ctx, "findPrimSpecByPath requires 1 argument (path string)");
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *path_str = JS_ToCString(ctx, argv[0]);
|
||||||
|
if (!path_str) {
|
||||||
|
return JS_EXCEPTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the path string into a Path object
|
||||||
|
tinyusdz::Path path(path_str);
|
||||||
|
if (!path.is_valid()) {
|
||||||
|
JS_FreeCString(ctx, path_str);
|
||||||
|
return JS_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the PrimSpec
|
||||||
|
const PrimSpec *ps = nullptr;
|
||||||
|
std::string err;
|
||||||
|
bool found = g_current_layer->find_primspec_at(path, &ps, &err);
|
||||||
|
|
||||||
|
JS_FreeCString(ctx, path_str);
|
||||||
|
|
||||||
|
if (!found || !ps) {
|
||||||
|
return JS_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert PrimSpec to JSON and parse it
|
||||||
|
std::string json = PrimSpecToJSON(ps);
|
||||||
|
JSValue result = JS_ParseJSON(ctx, json.c_str(), json.length(), "<primspec>");
|
||||||
|
if (JS_IsException(result)) {
|
||||||
|
return JS_EXCEPTION;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSValue js_getPrimSpecMetadata(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic, JSValueConst *func_data) {
|
||||||
|
if (!g_current_layer) {
|
||||||
|
return JS_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argc < 1) {
|
||||||
|
return JS_ThrowTypeError(ctx, "getPrimSpecMetadata requires 1 argument (path string)");
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *path_str = JS_ToCString(ctx, argv[0]);
|
||||||
|
if (!path_str) {
|
||||||
|
return JS_EXCEPTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the path string into a Path object
|
||||||
|
tinyusdz::Path path(path_str);
|
||||||
|
if (!path.is_valid()) {
|
||||||
|
JS_FreeCString(ctx, path_str);
|
||||||
|
return JS_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the PrimSpec
|
||||||
|
const PrimSpec *ps = nullptr;
|
||||||
|
std::string err;
|
||||||
|
bool found = g_current_layer->find_primspec_at(path, &ps, &err);
|
||||||
|
|
||||||
|
JS_FreeCString(ctx, path_str);
|
||||||
|
|
||||||
|
if (!found || !ps) {
|
||||||
|
return JS_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert PrimSpec metadata to JSON and parse it
|
||||||
|
std::string json = PrimMetasToJSON(&ps->metas());
|
||||||
|
JSValue result = JS_ParseJSON(ctx, json.c_str(), json.length(), "<primspec_metadata>");
|
||||||
|
if (JS_IsException(result)) {
|
||||||
|
return JS_EXCEPTION;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
bool RunJSScript(const std::string &js_code, std::string &err) {
|
bool RunJSScript(const std::string &js_code, std::string &err) {
|
||||||
JSRuntime *rt = JS_NewRuntime();
|
JSRuntime *rt = JS_NewRuntime();
|
||||||
if (!rt) {
|
if (!rt) {
|
||||||
@@ -1007,6 +1307,57 @@ bool RunJSScriptWithAttribute(const std::string &js_code, const Attribute* attri
|
|||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RunJSScriptWithLayer(const std::string &js_code, const class Layer* layer, std::string &err) {
|
||||||
|
JSRuntime *rt = JS_NewRuntime();
|
||||||
|
if (!rt) {
|
||||||
|
err = "Failed to create JavaScript runtime";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSContext *ctx = JS_NewContext(rt);
|
||||||
|
if (!ctx) {
|
||||||
|
err = "Failed to create JavaScript context";
|
||||||
|
JS_FreeRuntime(rt);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the global Layer pointer and add functions to the global context
|
||||||
|
g_current_layer = layer;
|
||||||
|
JSValue global_obj = JS_GetGlobalObject(ctx);
|
||||||
|
|
||||||
|
JSValue findFunc = JS_NewCFunctionData(ctx, js_findPrimSpecByPath, 1, 0, 0, nullptr);
|
||||||
|
JS_SetPropertyStr(ctx, global_obj, "findPrimSpecByPath", findFunc);
|
||||||
|
|
||||||
|
JSValue metaFunc = JS_NewCFunctionData(ctx, js_getPrimSpecMetadata, 1, 0, 0, nullptr);
|
||||||
|
JS_SetPropertyStr(ctx, global_obj, "getPrimSpecMetadata", metaFunc);
|
||||||
|
|
||||||
|
JS_FreeValue(ctx, global_obj);
|
||||||
|
|
||||||
|
JSValue result = JS_Eval(ctx, js_code.c_str(), js_code.length(),
|
||||||
|
"<eval>", JS_EVAL_TYPE_GLOBAL);
|
||||||
|
|
||||||
|
bool success = true;
|
||||||
|
if (JS_IsException(result)) {
|
||||||
|
success = false;
|
||||||
|
|
||||||
|
JSValue exception = JS_GetException(ctx);
|
||||||
|
const char *error_str = JS_ToCString(ctx, exception);
|
||||||
|
if (error_str) {
|
||||||
|
err = std::string("JavaScript error: ") + error_str;
|
||||||
|
JS_FreeCString(ctx, error_str);
|
||||||
|
} else {
|
||||||
|
err = "JavaScript error: unable to get error message";
|
||||||
|
}
|
||||||
|
JS_FreeValue(ctx, exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
JS_FreeValue(ctx, result);
|
||||||
|
JS_FreeContext(ctx);
|
||||||
|
JS_FreeRuntime(rt);
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(__clang__)
|
#if defined(__clang__)
|
||||||
#pragma clang diagnostic pop
|
#pragma clang diagnostic pop
|
||||||
#endif
|
#endif
|
||||||
@@ -1036,6 +1387,13 @@ bool RunJSScriptWithAttribute(const std::string &js_code, const Attribute* attri
|
|||||||
(void)attribute;
|
(void)attribute;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RunJSScriptWithLayer(const std::string &js_code, const class Layer* layer, std::string &err) {
|
||||||
|
err = "JavaScript is not supported in this build.\n";
|
||||||
|
(void)js_code;
|
||||||
|
(void)layer;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
} // namespace tydra
|
} // namespace tydra
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ bool RunJSScriptWithLayerMetas(const std::string &js_code, const LayerMetas* lay
|
|||||||
|
|
||||||
bool RunJSScriptWithAttribute(const std::string &js_code, const Attribute* attribute, std::string &err);
|
bool RunJSScriptWithAttribute(const std::string &js_code, const Attribute* attribute, std::string &err);
|
||||||
|
|
||||||
|
bool RunJSScriptWithLayer(const std::string &js_code, const class Layer* layer, std::string &err);
|
||||||
|
|
||||||
|
|
||||||
} // namespace tydra
|
} // namespace tydra
|
||||||
} // namespace tinyusdz
|
} // namespace tinyusdz
|
||||||
|
|||||||
@@ -86,24 +86,19 @@ std::string SerializeArrayToBase64(const std::vector<T>& array) {
|
|||||||
return base64_encode(bytes, static_cast<unsigned int>(byte_size));
|
return base64_encode(bytes, static_cast<unsigned int>(byte_size));
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
// Specialized versions for different types
|
// Specialized versions for different types
|
||||||
std::string SerializeIntArrayToBase64(const std::vector<int>& array) {
|
std::string SerializeIntArrayToBase64(const std::vector<int>& array) {
|
||||||
return SerializeArrayToBase64(array);
|
return SerializeArrayToBase64(array);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
std::string SerializeFloatArrayToBase64(const std::vector<float>& array) {
|
std::string SerializeFloatArrayToBase64(const std::vector<float>& array) {
|
||||||
return SerializeArrayToBase64(array);
|
return SerializeArrayToBase64(array);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
std::string SerializeDoubleArrayToBase64(const std::vector<double>& array) {
|
std::string SerializeDoubleArrayToBase64(const std::vector<double>& array) {
|
||||||
return SerializeArrayToBase64(array);
|
return SerializeArrayToBase64(array);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
// Helper functions for mixed-mode serialization
|
// Helper functions for mixed-mode serialization
|
||||||
template<typename T>
|
template<typename T>
|
||||||
json SerializeArrayData(const std::vector<T>& array, USDToJSONContext* context,
|
json SerializeArrayData(const std::vector<T>& array, USDToJSONContext* context,
|
||||||
@@ -138,7 +133,6 @@ json SerializeArrayData(const std::vector<T>& array, USDToJSONContext* context,
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
// Helper function to serialize attribute metadata
|
// Helper function to serialize attribute metadata
|
||||||
json SerializeAttributeMetadata(const AttrMetas& metas) {
|
json SerializeAttributeMetadata(const AttrMetas& metas) {
|
||||||
@@ -261,8 +255,11 @@ json SerializeAttributeMetadata(const AttrMetas& metas) {
|
|||||||
return metadata;
|
return metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
// Specialized array serialization functions
|
// Specialized array serialization functions
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wunused-function"
|
||||||
|
#pragma clang diagnostic ignored "-Wunused-template"
|
||||||
|
|
||||||
json SerializeIntArray(const std::vector<int>& array, USDToJSONContext* context = nullptr) {
|
json SerializeIntArray(const std::vector<int>& array, USDToJSONContext* context = nullptr) {
|
||||||
return SerializeArrayData(array, context, "UNSIGNED_INT", "SCALAR");
|
return SerializeArrayData(array, context, "UNSIGNED_INT", "SCALAR");
|
||||||
}
|
}
|
||||||
@@ -274,9 +271,7 @@ json SerializeFloatArray(const std::vector<float>& array, USDToJSONContext* cont
|
|||||||
json SerializeDoubleArray(const std::vector<double>& array, USDToJSONContext* context = nullptr) {
|
json SerializeDoubleArray(const std::vector<double>& array, USDToJSONContext* context = nullptr) {
|
||||||
return SerializeArrayData(array, context, "FLOAT", "SCALAR"); // Note: JSON doesn't distinguish float/double
|
return SerializeArrayData(array, context, "FLOAT", "SCALAR"); // Note: JSON doesn't distinguish float/double
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
// Overloaded functions with attribute metadata support
|
// Overloaded functions with attribute metadata support
|
||||||
template<typename T>
|
template<typename T>
|
||||||
json SerializeArrayDataWithMetadata(const std::vector<T>& array, const AttrMetas* metas, USDToJSONContext* context,
|
json SerializeArrayDataWithMetadata(const std::vector<T>& array, const AttrMetas* metas, USDToJSONContext* context,
|
||||||
@@ -293,22 +288,16 @@ json SerializeArrayDataWithMetadata(const std::vector<T>& array, const AttrMetas
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
// Metadata-aware array serialization functions
|
// Metadata-aware array serialization functions
|
||||||
json SerializeIntArrayWithMetadata(const std::vector<int>& array, const AttrMetas* metas = nullptr, USDToJSONContext* context = nullptr) {
|
json SerializeIntArrayWithMetadata(const std::vector<int>& array, const AttrMetas* metas = nullptr, USDToJSONContext* context = nullptr) {
|
||||||
return SerializeArrayDataWithMetadata(array, metas, context, "UNSIGNED_INT", "SCALAR");
|
return SerializeArrayDataWithMetadata(array, metas, context, "UNSIGNED_INT", "SCALAR");
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
json SerializeFloatArrayWithMetadata(const std::vector<float>& array, const AttrMetas* metas = nullptr, USDToJSONContext* context = nullptr) {
|
json SerializeFloatArrayWithMetadata(const std::vector<float>& array, const AttrMetas* metas = nullptr, USDToJSONContext* context = nullptr) {
|
||||||
return SerializeArrayDataWithMetadata(array, metas, context, "FLOAT", "SCALAR");
|
return SerializeArrayDataWithMetadata(array, metas, context, "FLOAT", "SCALAR");
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
json SerializeDoubleArrayWithMetadata(const std::vector<double>& array, const AttrMetas* metas = nullptr, USDToJSONContext* context = nullptr) {
|
json SerializeDoubleArrayWithMetadata(const std::vector<double>& array, const AttrMetas* metas = nullptr, USDToJSONContext* context = nullptr) {
|
||||||
return SerializeArrayDataWithMetadata(array, metas, context, "FLOAT", "SCALAR");
|
return SerializeArrayDataWithMetadata(array, metas, context, "FLOAT", "SCALAR");
|
||||||
}
|
}
|
||||||
@@ -696,7 +685,6 @@ json SerializeHalf4Array(const std::vector<value::half4>& vectors, USDToJSONCont
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
// Metadata-aware vector serialization functions
|
// Metadata-aware vector serialization functions
|
||||||
json SerializePoint3fArrayWithMetadata(const std::vector<value::point3f>& points, const AttrMetas* metas = nullptr, USDToJSONContext* context = nullptr) {
|
json SerializePoint3fArrayWithMetadata(const std::vector<value::point3f>& points, const AttrMetas* metas = nullptr, USDToJSONContext* context = nullptr) {
|
||||||
@@ -748,7 +736,6 @@ json SerializePoint3fArrayWithMetadata(const std::vector<value::point3f>& points
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
json SerializeNormal3fArrayWithMetadata(const std::vector<value::normal3f>& normals, const AttrMetas* metas = nullptr, USDToJSONContext* context = nullptr) {
|
json SerializeNormal3fArrayWithMetadata(const std::vector<value::normal3f>& normals, const AttrMetas* metas = nullptr, USDToJSONContext* context = nullptr) {
|
||||||
if (normals.empty()) {
|
if (normals.empty()) {
|
||||||
return json::object();
|
return json::object();
|
||||||
@@ -797,9 +784,7 @@ json SerializeNormal3fArrayWithMetadata(const std::vector<value::normal3f>& norm
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
// Matrix array serialization
|
// Matrix array serialization
|
||||||
template<typename MatrixType>
|
template<typename MatrixType>
|
||||||
std::string SerializeMatrixArrayToBase64(const std::vector<MatrixType>& array) {
|
std::string SerializeMatrixArrayToBase64(const std::vector<MatrixType>& array) {
|
||||||
@@ -908,7 +893,11 @@ std::string SerializeMatrix4dArrayToBase64(const std::vector<value::matrix4d>& a
|
|||||||
}
|
}
|
||||||
return SerializeDoubleArrayToBase64(double_data);
|
return SerializeDoubleArrayToBase64(double_data);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wunused-function"
|
||||||
|
|
||||||
json ToJSON(tinyusdz::Xform& xform) {
|
json ToJSON(tinyusdz::Xform& xform) {
|
||||||
json j;
|
json j;
|
||||||
@@ -934,7 +923,6 @@ json ToJSON(tinyusdz::GeomMesh& mesh) {
|
|||||||
return ToJSON(mesh, nullptr); // Use base64 mode by default
|
return ToJSON(mesh, nullptr); // Use base64 mode by default
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
json ToJSON(tinyusdz::GeomMesh& mesh, USDToJSONContext* context) {
|
json ToJSON(tinyusdz::GeomMesh& mesh, USDToJSONContext* context) {
|
||||||
json j;
|
json j;
|
||||||
|
|
||||||
@@ -1040,7 +1028,6 @@ json ToJSON(tinyusdz::GeomMesh& mesh, USDToJSONContext* context) {
|
|||||||
|
|
||||||
return j;
|
return j;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
json ToJSON(tinyusdz::GeomBasisCurves& curves) {
|
json ToJSON(tinyusdz::GeomBasisCurves& curves) {
|
||||||
|
|
||||||
@@ -1385,6 +1372,8 @@ bool to_json_string(const tinyusdz::Layer &layer, std::string *json_str, std::st
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
|
||||||
bool to_json_string(const tinyusdz::Layer &layer, const USDToJSONOptions& options, std::string *json_str, std::string *warn, std::string *err) {
|
bool to_json_string(const tinyusdz::Layer &layer, const USDToJSONOptions& options, std::string *json_str, std::string *warn, std::string *err) {
|
||||||
|
|
||||||
// TODO: options
|
// TODO: options
|
||||||
@@ -1394,5 +1383,280 @@ bool to_json_string(const tinyusdz::Layer &layer, const USDToJSONOptions& option
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ================================================================
|
||||||
|
// Property, Attribute, and Relationship to JSON conversion
|
||||||
|
// ================================================================
|
||||||
|
|
||||||
|
json ToJSON(const tinyusdz::Attribute& attribute, USDToJSONContext* /* context */) {
|
||||||
|
json j;
|
||||||
|
|
||||||
|
// Basic attribute information
|
||||||
|
j["name"] = attribute.name();
|
||||||
|
j["typeName"] = attribute.type_name();
|
||||||
|
|
||||||
|
// Variability
|
||||||
|
switch (attribute.variability()) {
|
||||||
|
case Variability::Varying:
|
||||||
|
j["variability"] = "varying";
|
||||||
|
break;
|
||||||
|
case Variability::Uniform:
|
||||||
|
j["variability"] = "uniform";
|
||||||
|
break;
|
||||||
|
case Variability::Config:
|
||||||
|
j["variability"] = "config";
|
||||||
|
break;
|
||||||
|
case Variability::Invalid:
|
||||||
|
j["variability"] = "invalid";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interpolation (from metadata)
|
||||||
|
if (attribute.metas().interpolation) {
|
||||||
|
switch (attribute.metas().interpolation.value()) {
|
||||||
|
case Interpolation::Invalid:
|
||||||
|
j["interpolation"] = "invalid";
|
||||||
|
break;
|
||||||
|
case Interpolation::Constant:
|
||||||
|
j["interpolation"] = "constant";
|
||||||
|
break;
|
||||||
|
case Interpolation::Uniform:
|
||||||
|
j["interpolation"] = "uniform";
|
||||||
|
break;
|
||||||
|
case Interpolation::Varying:
|
||||||
|
j["interpolation"] = "varying";
|
||||||
|
break;
|
||||||
|
case Interpolation::Vertex:
|
||||||
|
j["interpolation"] = "vertex";
|
||||||
|
break;
|
||||||
|
case Interpolation::FaceVarying:
|
||||||
|
j["interpolation"] = "faceVarying";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attribute metadata
|
||||||
|
if (attribute.metas().authored()) {
|
||||||
|
j["metadata"] = SerializeAttributeMetadata(attribute.metas());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connection information
|
||||||
|
if (attribute.is_connection()) {
|
||||||
|
j["isConnection"] = true;
|
||||||
|
auto connections = attribute.connections();
|
||||||
|
if (connections.size() == 1) {
|
||||||
|
j["connection"] = connections[0].full_path_name();
|
||||||
|
} else if (connections.size() > 1) {
|
||||||
|
json connections_array = json::array();
|
||||||
|
for (const auto& conn : connections) {
|
||||||
|
connections_array.push_back(conn.full_path_name());
|
||||||
|
}
|
||||||
|
j["connections"] = connections_array;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
j["isConnection"] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value information
|
||||||
|
if (attribute.is_blocked()) {
|
||||||
|
j["hasValue"] = false;
|
||||||
|
j["valueType"] = "blocked";
|
||||||
|
j["value"] = nullptr;
|
||||||
|
} else {
|
||||||
|
// Check if attribute has value by accessing the internal value container
|
||||||
|
const auto& var = attribute.get_var();
|
||||||
|
if (var.is_valid()) {
|
||||||
|
j["hasValue"] = true;
|
||||||
|
j["valueType"] = "data";
|
||||||
|
|
||||||
|
// For now, serialize as a string representation
|
||||||
|
// TODO: Implement proper value type serialization based on type_id
|
||||||
|
j["value"] = "[Attribute value - serialization not yet implemented]";
|
||||||
|
|
||||||
|
// Store type information for debugging
|
||||||
|
j["valueTypeName"] = attribute.type_name();
|
||||||
|
} else {
|
||||||
|
j["hasValue"] = false;
|
||||||
|
j["valueType"] = "empty";
|
||||||
|
j["value"] = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Time samples information
|
||||||
|
if (attribute.is_timesamples()) {
|
||||||
|
j["hasTimeSamples"] = true;
|
||||||
|
// TODO: Serialize time sample data
|
||||||
|
j["timeSamples"] = "[TimeSamples data - not yet serialized]";
|
||||||
|
} else {
|
||||||
|
j["hasTimeSamples"] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
|
||||||
|
json ToJSON(const tinyusdz::Relationship& relationship) {
|
||||||
|
json j;
|
||||||
|
|
||||||
|
j["type"] = "relationship";
|
||||||
|
|
||||||
|
// List edit qualifier
|
||||||
|
switch (relationship.get_listedit_qual()) {
|
||||||
|
case ListEditQual::ResetToExplicit:
|
||||||
|
j["listEditQual"] = "resetToExplicit";
|
||||||
|
break;
|
||||||
|
case ListEditQual::Append:
|
||||||
|
j["listEditQual"] = "append";
|
||||||
|
break;
|
||||||
|
case ListEditQual::Add:
|
||||||
|
j["listEditQual"] = "add";
|
||||||
|
break;
|
||||||
|
case ListEditQual::Delete:
|
||||||
|
j["listEditQual"] = "delete";
|
||||||
|
break;
|
||||||
|
case ListEditQual::Prepend:
|
||||||
|
j["listEditQual"] = "prepend";
|
||||||
|
break;
|
||||||
|
case ListEditQual::Order:
|
||||||
|
j["listEditQual"] = "order";
|
||||||
|
break;
|
||||||
|
case ListEditQual::Invalid:
|
||||||
|
j["listEditQual"] = "invalid";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Relationship value type and targets
|
||||||
|
switch (relationship.type) {
|
||||||
|
case Relationship::Type::DefineOnly:
|
||||||
|
j["valueType"] = "defineOnly";
|
||||||
|
j["hasTargets"] = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Relationship::Type::Path:
|
||||||
|
j["valueType"] = "path";
|
||||||
|
j["hasTargets"] = true;
|
||||||
|
j["target"] = relationship.targetPath.full_path_name();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Relationship::Type::PathVector:
|
||||||
|
{
|
||||||
|
j["valueType"] = "pathVector";
|
||||||
|
j["hasTargets"] = true;
|
||||||
|
json targets_array = json::array();
|
||||||
|
for (const auto& path : relationship.targetPathVector) {
|
||||||
|
targets_array.push_back(path.full_path_name());
|
||||||
|
}
|
||||||
|
j["targets"] = targets_array;
|
||||||
|
j["targetCount"] = relationship.targetPathVector.size();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Relationship::Type::ValueBlock:
|
||||||
|
j["valueType"] = "valueBlock";
|
||||||
|
j["hasTargets"] = false;
|
||||||
|
j["blocked"] = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
|
||||||
|
json ToJSON(const tinyusdz::Property& property, USDToJSONContext* context) {
|
||||||
|
json j;
|
||||||
|
|
||||||
|
// Property type
|
||||||
|
switch (property.get_property_type()) {
|
||||||
|
case Property::Type::EmptyAttrib:
|
||||||
|
j["propertyType"] = "emptyAttribute";
|
||||||
|
j["typeName"] = property.value_type_name();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Property::Type::Attrib:
|
||||||
|
j["propertyType"] = "attribute";
|
||||||
|
j["attribute"] = ToJSON(property.get_attribute(), context);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Property::Type::Relation:
|
||||||
|
j["propertyType"] = "relationship";
|
||||||
|
j["relationship"] = ToJSON(property.get_relationship());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Property::Type::NoTargetsRelation:
|
||||||
|
j["propertyType"] = "noTargetsRelationship";
|
||||||
|
j["relationship"] = ToJSON(property.get_relationship());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Property::Type::Connection:
|
||||||
|
j["propertyType"] = "connection";
|
||||||
|
j["attribute"] = ToJSON(property.get_attribute(), context);
|
||||||
|
j["valueTypeName"] = property.value_type_name();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom flag
|
||||||
|
j["isCustom"] = property.has_custom();
|
||||||
|
|
||||||
|
// List edit qualifier (mainly for relationships)
|
||||||
|
switch (property.get_listedit_qual()) {
|
||||||
|
case ListEditQual::ResetToExplicit:
|
||||||
|
j["listEditQual"] = "resetToExplicit";
|
||||||
|
break;
|
||||||
|
case ListEditQual::Append:
|
||||||
|
j["listEditQual"] = "append";
|
||||||
|
break;
|
||||||
|
case ListEditQual::Add:
|
||||||
|
j["listEditQual"] = "add";
|
||||||
|
break;
|
||||||
|
case ListEditQual::Delete:
|
||||||
|
j["listEditQual"] = "delete";
|
||||||
|
break;
|
||||||
|
case ListEditQual::Prepend:
|
||||||
|
j["listEditQual"] = "prepend";
|
||||||
|
break;
|
||||||
|
case ListEditQual::Order:
|
||||||
|
j["listEditQual"] = "order";
|
||||||
|
break;
|
||||||
|
case ListEditQual::Invalid:
|
||||||
|
j["listEditQual"] = "invalid";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convenience methods for relationships
|
||||||
|
if (property.is_relationship()) {
|
||||||
|
auto target = property.get_relationTarget();
|
||||||
|
if (target) {
|
||||||
|
j["relationTarget"] = target->full_path_name();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto targets = property.get_relationTargets();
|
||||||
|
if (!targets.empty()) {
|
||||||
|
json targets_array = json::array();
|
||||||
|
for (const auto& t : targets) {
|
||||||
|
targets_array.push_back(t.full_path_name());
|
||||||
|
}
|
||||||
|
j["relationTargets"] = targets_array;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper flags
|
||||||
|
j["isAttribute"] = property.is_attribute();
|
||||||
|
j["isRelationship"] = property.is_relationship();
|
||||||
|
j["isEmpty"] = property.is_empty();
|
||||||
|
j["isAttributeConnection"] = property.is_attribute_connection();
|
||||||
|
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
|
||||||
|
json PropertiesToJSON(const std::map<std::string, tinyusdz::Property>& properties, USDToJSONContext* context) {
|
||||||
|
json j = json::object();
|
||||||
|
|
||||||
|
for (const auto& prop_pair : properties) {
|
||||||
|
const std::string& prop_name = prop_pair.first;
|
||||||
|
const Property& property = prop_pair.second;
|
||||||
|
|
||||||
|
j[prop_name] = ToJSON(property, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace tinyusdz
|
} // namespace tinyusdz
|
||||||
|
|||||||
@@ -130,4 +130,24 @@ bool to_json_string(const tinyusdz::Layer &layer, const USDToJSONOptions& option
|
|||||||
///
|
///
|
||||||
nlohmann::json ToJSON(tinyusdz::GeomMesh& mesh, USDToJSONContext* context);
|
nlohmann::json ToJSON(tinyusdz::GeomMesh& mesh, USDToJSONContext* context);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Convert Attribute to JSON
|
||||||
|
///
|
||||||
|
nlohmann::json ToJSON(const tinyusdz::Attribute& attribute, USDToJSONContext* context = nullptr);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Convert Relationship to JSON
|
||||||
|
///
|
||||||
|
nlohmann::json ToJSON(const tinyusdz::Relationship& relationship);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Convert Property to JSON
|
||||||
|
///
|
||||||
|
nlohmann::json ToJSON(const tinyusdz::Property& property, USDToJSONContext* context = nullptr);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Convert Properties map to JSON
|
||||||
|
///
|
||||||
|
nlohmann::json PropertiesToJSON(const std::map<std::string, tinyusdz::Property>& properties, USDToJSONContext* context = nullptr);
|
||||||
|
|
||||||
} // namespace tinyusdz
|
} // namespace tinyusdz
|
||||||
|
|||||||
Reference in New Issue
Block a user