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 Attribute* g_current_attribute = nullptr;
|
||||
static const class Layer* g_current_layer = nullptr;
|
||||
|
||||
// JavaScript fp16 library and TUSDZFloat16Array implementation
|
||||
static const char* fp16_library_js = R"(
|
||||
@@ -845,6 +846,223 @@ static std::string AttributeToJSON(const Attribute* attr) {
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
JSRuntime *rt = JS_NewRuntime();
|
||||
if (!rt) {
|
||||
@@ -1007,6 +1307,57 @@ bool RunJSScriptWithAttribute(const std::string &js_code, const Attribute* attri
|
||||
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__)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
@@ -1036,6 +1387,13 @@ bool RunJSScriptWithAttribute(const std::string &js_code, const Attribute* attri
|
||||
(void)attribute;
|
||||
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
|
||||
|
||||
} // 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 RunJSScriptWithLayer(const std::string &js_code, const class Layer* layer, std::string &err);
|
||||
|
||||
|
||||
} // namespace tydra
|
||||
} // namespace tinyusdz
|
||||
|
||||
@@ -86,24 +86,19 @@ std::string SerializeArrayToBase64(const std::vector<T>& array) {
|
||||
return base64_encode(bytes, static_cast<unsigned int>(byte_size));
|
||||
}
|
||||
|
||||
#if 0
|
||||
// Specialized versions for different types
|
||||
std::string SerializeIntArrayToBase64(const std::vector<int>& array) {
|
||||
return SerializeArrayToBase64(array);
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string SerializeFloatArrayToBase64(const std::vector<float>& array) {
|
||||
return SerializeArrayToBase64(array);
|
||||
}
|
||||
|
||||
#if 0
|
||||
std::string SerializeDoubleArrayToBase64(const std::vector<double>& array) {
|
||||
return SerializeArrayToBase64(array);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
// Helper functions for mixed-mode serialization
|
||||
template<typename T>
|
||||
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
|
||||
json SerializeAttributeMetadata(const AttrMetas& metas) {
|
||||
@@ -261,8 +255,11 @@ json SerializeAttributeMetadata(const AttrMetas& metas) {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
#if 0
|
||||
// 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) {
|
||||
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) {
|
||||
return SerializeArrayData(array, context, "FLOAT", "SCALAR"); // Note: JSON doesn't distinguish float/double
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
// Overloaded functions with attribute metadata support
|
||||
template<typename T>
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
// Metadata-aware array serialization functions
|
||||
json SerializeIntArrayWithMetadata(const std::vector<int>& array, const AttrMetas* metas = nullptr, USDToJSONContext* context = nullptr) {
|
||||
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) {
|
||||
return SerializeArrayDataWithMetadata(array, metas, context, "FLOAT", "SCALAR");
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
json SerializeDoubleArrayWithMetadata(const std::vector<double>& array, const AttrMetas* metas = nullptr, USDToJSONContext* context = nullptr) {
|
||||
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
|
||||
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;
|
||||
}
|
||||
|
||||
#if 0
|
||||
json SerializeNormal3fArrayWithMetadata(const std::vector<value::normal3f>& normals, const AttrMetas* metas = nullptr, USDToJSONContext* context = nullptr) {
|
||||
if (normals.empty()) {
|
||||
return json::object();
|
||||
@@ -797,9 +784,7 @@ json SerializeNormal3fArrayWithMetadata(const std::vector<value::normal3f>& norm
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
// Matrix array serialization
|
||||
template<typename MatrixType>
|
||||
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);
|
||||
}
|
||||
#endif
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-function"
|
||||
|
||||
json ToJSON(tinyusdz::Xform& xform) {
|
||||
json j;
|
||||
@@ -934,7 +923,6 @@ json ToJSON(tinyusdz::GeomMesh& mesh) {
|
||||
return ToJSON(mesh, nullptr); // Use base64 mode by default
|
||||
}
|
||||
|
||||
#if 0
|
||||
json ToJSON(tinyusdz::GeomMesh& mesh, USDToJSONContext* context) {
|
||||
json j;
|
||||
|
||||
@@ -1040,7 +1028,6 @@ json ToJSON(tinyusdz::GeomMesh& mesh, USDToJSONContext* context) {
|
||||
|
||||
return j;
|
||||
}
|
||||
#endif
|
||||
|
||||
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) {
|
||||
|
||||
// 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
|
||||
|
||||
@@ -130,4 +130,24 @@ bool to_json_string(const tinyusdz::Layer &layer, const USDToJSONOptions& option
|
||||
///
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user