sdl-mcp 0.12.2 → 0.12.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/config/sdlmcp.config.example.json +53 -8
- package/config/sdlmcp.config.schema.json +530 -106
- package/dist/.tsbuildinfo +1 -1
- package/dist/benchmark/external-manifest.d.ts +162 -0
- package/dist/benchmark/external-manifest.d.ts.map +1 -0
- package/dist/benchmark/external-manifest.js +336 -0
- package/dist/benchmark/external-manifest.js.map +1 -0
- package/dist/benchmark/external-runner.d.ts +100 -0
- package/dist/benchmark/external-runner.d.ts.map +1 -0
- package/dist/benchmark/external-runner.js +1171 -0
- package/dist/benchmark/external-runner.js.map +1 -0
- package/dist/benchmark/output-file.d.ts +3 -0
- package/dist/benchmark/output-file.d.ts.map +1 -0
- package/dist/benchmark/output-file.js +8 -0
- package/dist/benchmark/output-file.js.map +1 -0
- package/dist/cli/argParsing.d.ts.map +1 -1
- package/dist/cli/argParsing.js +6 -0
- package/dist/cli/argParsing.js.map +1 -1
- package/dist/cli/commands/benchmark.d.ts.map +1 -1
- package/dist/cli/commands/benchmark.js +3 -2
- package/dist/cli/commands/benchmark.js.map +1 -1
- package/dist/cli/commands/index.d.ts +1 -0
- package/dist/cli/commands/index.d.ts.map +1 -1
- package/dist/cli/commands/index.js +13 -7
- package/dist/cli/commands/index.js.map +1 -1
- package/dist/cli/commands/init.d.ts +5 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +73 -3
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/serve.d.ts.map +1 -1
- package/dist/cli/commands/serve.js +2 -0
- package/dist/cli/commands/serve.js.map +1 -1
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/transport/http.d.ts +1 -35
- package/dist/cli/transport/http.d.ts.map +1 -1
- package/dist/cli/transport/http.js +151 -175
- package/dist/cli/transport/http.js.map +1 -1
- package/dist/cli/types.d.ts +1 -0
- package/dist/cli/types.d.ts.map +1 -1
- package/dist/config/constants.d.ts +30 -0
- package/dist/config/constants.d.ts.map +1 -1
- package/dist/config/constants.js +30 -0
- package/dist/config/constants.js.map +1 -1
- package/dist/config/types.d.ts +53 -1
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/types.js +62 -14
- package/dist/config/types.js.map +1 -1
- package/dist/db/ladybug-batching.d.ts +1 -0
- package/dist/db/ladybug-batching.d.ts.map +1 -1
- package/dist/db/ladybug-batching.js +1 -0
- package/dist/db/ladybug-batching.js.map +1 -1
- package/dist/db/ladybug-core.d.ts +3 -0
- package/dist/db/ladybug-core.d.ts.map +1 -1
- package/dist/db/ladybug-core.js +72 -2
- package/dist/db/ladybug-core.js.map +1 -1
- package/dist/db/ladybug-embeddings.d.ts +6 -1
- package/dist/db/ladybug-embeddings.d.ts.map +1 -1
- package/dist/db/ladybug-embeddings.js +6 -1
- package/dist/db/ladybug-embeddings.js.map +1 -1
- package/dist/db/ladybug-file-summaries.d.ts.map +1 -1
- package/dist/db/ladybug-file-summaries.js +23 -0
- package/dist/db/ladybug-file-summaries.js.map +1 -1
- package/dist/db/ladybug-queries.js +1 -1
- package/dist/db/ladybug-queries.js.map +1 -1
- package/dist/db/migrations/index.d.ts.map +1 -1
- package/dist/db/migrations/index.js +2 -0
- package/dist/db/migrations/index.js.map +1 -1
- package/dist/db/migrations/m007-copy-embeddings-to-symbol.d.ts +5 -9
- package/dist/db/migrations/m007-copy-embeddings-to-symbol.d.ts.map +1 -1
- package/dist/db/migrations/m007-copy-embeddings-to-symbol.js +24 -126
- package/dist/db/migrations/m007-copy-embeddings-to-symbol.js.map +1 -1
- package/dist/db/migrations/m021-remediate-symbol-embeddings.d.ts +5 -0
- package/dist/db/migrations/m021-remediate-symbol-embeddings.d.ts.map +1 -0
- package/dist/db/migrations/m021-remediate-symbol-embeddings.js +7 -0
- package/dist/db/migrations/m021-remediate-symbol-embeddings.js.map +1 -0
- package/dist/db/migrations/symbol-embedding-remediation.d.ts +57 -0
- package/dist/db/migrations/symbol-embedding-remediation.d.ts.map +1 -0
- package/dist/db/migrations/symbol-embedding-remediation.js +335 -0
- package/dist/db/migrations/symbol-embedding-remediation.js.map +1 -0
- package/dist/graph/layout/force-layout.d.ts +4 -0
- package/dist/graph/layout/force-layout.d.ts.map +1 -0
- package/dist/graph/layout/force-layout.js +127 -0
- package/dist/graph/layout/force-layout.js.map +1 -0
- package/dist/graph/layout/layout-service.d.ts +37 -0
- package/dist/graph/layout/layout-service.d.ts.map +1 -0
- package/dist/graph/layout/layout-service.js +172 -0
- package/dist/graph/layout/layout-service.js.map +1 -0
- package/dist/graph/layout/native-engine.d.ts +9 -0
- package/dist/graph/layout/native-engine.d.ts.map +1 -0
- package/dist/graph/layout/native-engine.js +59 -0
- package/dist/graph/layout/native-engine.js.map +1 -0
- package/dist/graph/layout/prng.d.ts +3 -0
- package/dist/graph/layout/prng.d.ts.map +1 -0
- package/dist/graph/layout/prng.js +19 -0
- package/dist/graph/layout/prng.js.map +1 -0
- package/dist/graph/layout/serializer.d.ts +3 -0
- package/dist/graph/layout/serializer.d.ts.map +1 -0
- package/dist/graph/layout/serializer.js +20 -0
- package/dist/graph/layout/serializer.js.map +1 -0
- package/dist/graph/layout/types.d.ts +30 -0
- package/dist/graph/layout/types.d.ts.map +1 -0
- package/dist/graph/layout/types.js +2 -0
- package/dist/graph/layout/types.js.map +1 -0
- package/dist/indexer/embeddings.d.ts +7 -0
- package/dist/indexer/embeddings.d.ts.map +1 -1
- package/dist/indexer/embeddings.js +44 -4
- package/dist/indexer/embeddings.js.map +1 -1
- package/dist/indexer/file-summary-embeddings.d.ts +12 -0
- package/dist/indexer/file-summary-embeddings.d.ts.map +1 -1
- package/dist/indexer/file-summary-embeddings.js +83 -21
- package/dist/indexer/file-summary-embeddings.js.map +1 -1
- package/dist/indexer/fileWalker.d.ts.map +1 -1
- package/dist/indexer/fileWalker.js +2 -1
- package/dist/indexer/fileWalker.js.map +1 -1
- package/dist/indexer/metrics-updater.d.ts.map +1 -1
- package/dist/indexer/metrics-updater.js +27 -4
- package/dist/indexer/metrics-updater.js.map +1 -1
- package/dist/indexer/watcher.d.ts.map +1 -1
- package/dist/indexer/watcher.js +2 -1
- package/dist/indexer/watcher.js.map +1 -1
- package/dist/mcp/context-response-projection.d.ts.map +1 -1
- package/dist/mcp/context-response-projection.js +49 -13
- package/dist/mcp/context-response-projection.js.map +1 -1
- package/dist/mcp/tools/search-edit/planner.d.ts.map +1 -1
- package/dist/mcp/tools/search-edit/planner.js +10 -3
- package/dist/mcp/tools/search-edit/planner.js.map +1 -1
- package/dist/observability/event-tap.d.ts +2 -1
- package/dist/observability/event-tap.d.ts.map +1 -1
- package/dist/observability/event-tap.js.map +1 -1
- package/dist/observability/service.d.ts +11 -4
- package/dist/observability/service.d.ts.map +1 -1
- package/dist/observability/service.js +83 -13
- package/dist/observability/service.js.map +1 -1
- package/dist/observability/types.d.ts +48 -2
- package/dist/observability/types.d.ts.map +1 -1
- package/dist/ui/.tsbuildinfo +1 -0
- package/dist/ui/vendor/fflate.js +2692 -0
- package/dist/ui/vendor/jsm/controls/ArcballControls.js +3543 -0
- package/dist/ui/vendor/jsm/controls/DragControls.js +452 -0
- package/dist/ui/vendor/jsm/controls/FirstPersonControls.js +488 -0
- package/dist/ui/vendor/jsm/controls/FlyControls.js +384 -0
- package/dist/ui/vendor/jsm/controls/MapControls.js +116 -0
- package/dist/ui/vendor/jsm/controls/OrbitControls.js +1963 -0
- package/dist/ui/vendor/jsm/controls/PointerLockControls.js +264 -0
- package/dist/ui/vendor/jsm/controls/TrackballControls.js +1001 -0
- package/dist/ui/vendor/jsm/controls/TransformControls.js +2003 -0
- package/dist/ui/vendor/jsm/loaders/3DMLoader.js +1836 -0
- package/dist/ui/vendor/jsm/loaders/3MFLoader.js +1621 -0
- package/dist/ui/vendor/jsm/loaders/AMFLoader.js +541 -0
- package/dist/ui/vendor/jsm/loaders/BVHLoader.js +484 -0
- package/dist/ui/vendor/jsm/loaders/ColladaLoader.js +153 -0
- package/dist/ui/vendor/jsm/loaders/DDSLoader.js +385 -0
- package/dist/ui/vendor/jsm/loaders/DRACOLoader.js +772 -0
- package/dist/ui/vendor/jsm/loaders/EXRLoader.js +3395 -0
- package/dist/ui/vendor/jsm/loaders/FBXLoader.js +4580 -0
- package/dist/ui/vendor/jsm/loaders/FontLoader.js +260 -0
- package/dist/ui/vendor/jsm/loaders/GCodeLoader.js +318 -0
- package/dist/ui/vendor/jsm/loaders/GLTFLoader.js +4860 -0
- package/dist/ui/vendor/jsm/loaders/HDRCubeTextureLoader.js +164 -0
- package/dist/ui/vendor/jsm/loaders/HDRLoader.js +463 -0
- package/dist/ui/vendor/jsm/loaders/IESLoader.js +379 -0
- package/dist/ui/vendor/jsm/loaders/KMZLoader.js +163 -0
- package/dist/ui/vendor/jsm/loaders/KTX2Loader.js +1276 -0
- package/dist/ui/vendor/jsm/loaders/KTXLoader.js +197 -0
- package/dist/ui/vendor/jsm/loaders/LDrawLoader.js +2510 -0
- package/dist/ui/vendor/jsm/loaders/LUT3dlLoader.js +205 -0
- package/dist/ui/vendor/jsm/loaders/LUTCubeLoader.js +190 -0
- package/dist/ui/vendor/jsm/loaders/LUTImageLoader.js +190 -0
- package/dist/ui/vendor/jsm/loaders/LWOLoader.js +1051 -0
- package/dist/ui/vendor/jsm/loaders/LottieLoader.js +130 -0
- package/dist/ui/vendor/jsm/loaders/MD2Loader.js +435 -0
- package/dist/ui/vendor/jsm/loaders/MDDLoader.js +147 -0
- package/dist/ui/vendor/jsm/loaders/MTLLoader.js +593 -0
- package/dist/ui/vendor/jsm/loaders/MaterialXLoader.js +1126 -0
- package/dist/ui/vendor/jsm/loaders/NRRDLoader.js +718 -0
- package/dist/ui/vendor/jsm/loaders/OBJLoader.js +955 -0
- package/dist/ui/vendor/jsm/loaders/PCDLoader.js +620 -0
- package/dist/ui/vendor/jsm/loaders/PDBLoader.js +272 -0
- package/dist/ui/vendor/jsm/loaders/PLYLoader.js +968 -0
- package/dist/ui/vendor/jsm/loaders/PVRLoader.js +270 -0
- package/dist/ui/vendor/jsm/loaders/RGBELoader.js +18 -0
- package/dist/ui/vendor/jsm/loaders/STLLoader.js +421 -0
- package/dist/ui/vendor/jsm/loaders/SVGLoader.js +3319 -0
- package/dist/ui/vendor/jsm/loaders/TDSLoader.js +1142 -0
- package/dist/ui/vendor/jsm/loaders/TGALoader.js +538 -0
- package/dist/ui/vendor/jsm/loaders/TIFFLoader.js +59 -0
- package/dist/ui/vendor/jsm/loaders/TTFLoader.js +261 -0
- package/dist/ui/vendor/jsm/loaders/USDLoader.js +303 -0
- package/dist/ui/vendor/jsm/loaders/USDZLoader.js +16 -0
- package/dist/ui/vendor/jsm/loaders/UltraHDRLoader.js +755 -0
- package/dist/ui/vendor/jsm/loaders/VOXLoader.js +919 -0
- package/dist/ui/vendor/jsm/loaders/VRMLLoader.js +3646 -0
- package/dist/ui/vendor/jsm/loaders/VTKLoader.js +1293 -0
- package/dist/ui/vendor/jsm/loaders/XYZLoader.js +143 -0
- package/dist/ui/vendor/jsm/loaders/collada/ColladaComposer.js +3044 -0
- package/dist/ui/vendor/jsm/loaders/collada/ColladaParser.js +1977 -0
- package/dist/ui/vendor/jsm/loaders/lwo/IFFParser.js +1217 -0
- package/dist/ui/vendor/jsm/loaders/lwo/LWO2Parser.js +414 -0
- package/dist/ui/vendor/jsm/loaders/lwo/LWO3Parser.js +373 -0
- package/dist/ui/vendor/jsm/loaders/usd/USDAParser.js +901 -0
- package/dist/ui/vendor/jsm/loaders/usd/USDCParser.js +1878 -0
- package/dist/ui/vendor/jsm/loaders/usd/USDComposer.js +4627 -0
- package/dist/ui/vendor/jsm/utils/BufferGeometryUtils.js +1501 -0
- package/dist/ui/vendor/jsm/utils/CameraUtils.js +82 -0
- package/dist/ui/vendor/jsm/utils/ColorUtils.js +76 -0
- package/dist/ui/vendor/jsm/utils/GeometryCompressionUtils.js +547 -0
- package/dist/ui/vendor/jsm/utils/GeometryUtils.js +226 -0
- package/dist/ui/vendor/jsm/utils/LDrawUtils.js +211 -0
- package/dist/ui/vendor/jsm/utils/SceneOptimizer.js +458 -0
- package/dist/ui/vendor/jsm/utils/SceneUtils.js +363 -0
- package/dist/ui/vendor/jsm/utils/ShadowMapViewer.js +244 -0
- package/dist/ui/vendor/jsm/utils/ShadowMapViewerGPU.js +233 -0
- package/dist/ui/vendor/jsm/utils/SkeletonUtils.js +496 -0
- package/dist/ui/vendor/jsm/utils/SortUtils.js +175 -0
- package/dist/ui/vendor/jsm/utils/UVsDebug.js +173 -0
- package/dist/ui/vendor/jsm/utils/WebGLTextureUtils.js +115 -0
- package/dist/ui/vendor/jsm/utils/WebGPUTextureUtils.js +81 -0
- package/dist/ui/vendor/jsm/utils/WorkerPool.js +167 -0
- package/dist/ui/vendor/three.core.min.js +6 -0
- package/dist/ui/vendor/three.module.min.js +6 -0
- package/dist/ui/viewer/ambient.js +39 -0
- package/dist/ui/viewer/api.js +93 -0
- package/dist/ui/viewer/chrome.js +36 -0
- package/dist/ui/viewer/edges.js +33 -0
- package/dist/ui/viewer/index.html +32 -0
- package/dist/ui/viewer/inspector.js +23 -0
- package/dist/ui/viewer/lenses/activity.js +24 -0
- package/dist/ui/viewer/lenses/community.js +5 -0
- package/dist/ui/viewer/lenses/edges.js +4 -0
- package/dist/ui/viewer/lenses/impact.js +6 -0
- package/dist/ui/viewer/lenses/search.js +69 -0
- package/dist/ui/viewer/lod.js +100 -0
- package/dist/ui/viewer/main.js +92 -0
- package/dist/ui/viewer/picking.js +45 -0
- package/dist/ui/viewer/scene.js +85 -0
- package/dist/ui/viewer/skins/default-skin.js +15 -0
- package/dist/ui/viewer/skins/loader.js +44 -0
- package/dist/ui/viewer/skins/manifest-schema.js +30 -0
- package/dist/ui/viewer/state.js +44 -0
- package/dist/ui/viewer/theme.js +23 -0
- package/dist/ui/viewer/universe.js +151 -0
- package/dist/ui/viewer/viewer.css +85 -0
- package/dist/util/safeRegex.d.ts.map +1 -1
- package/dist/util/safeRegex.js +221 -68
- package/dist/util/safeRegex.js.map +1 -1
- package/dist/viewer/legacy-graph.d.ts +40 -0
- package/dist/viewer/legacy-graph.d.ts.map +1 -0
- package/dist/viewer/legacy-graph.js +152 -0
- package/dist/viewer/legacy-graph.js.map +1 -0
- package/dist/viewer/routes.d.ts +10 -0
- package/dist/viewer/routes.d.ts.map +1 -0
- package/dist/viewer/routes.js +221 -0
- package/dist/viewer/routes.js.map +1 -0
- package/dist/viewer/service.d.ts +44 -0
- package/dist/viewer/service.d.ts.map +1 -0
- package/dist/viewer/service.js +160 -0
- package/dist/viewer/service.js.map +1 -0
- package/dist/viewer/types.d.ts +63 -0
- package/dist/viewer/types.d.ts.map +1 -0
- package/dist/viewer/types.js +2 -0
- package/dist/viewer/types.js.map +1 -0
- package/dist/viewer/viewer-config.d.ts +12 -0
- package/dist/viewer/viewer-config.d.ts.map +1 -0
- package/dist/viewer/viewer-config.js +36 -0
- package/dist/viewer/viewer-config.js.map +1 -0
- package/package.json +18 -9
- package/templates/SDL.md +19 -0
- package/templates/claude-code.json +8 -43
- package/dist/ui/graph.css +0 -108
- package/dist/ui/graph.d.ts +0 -2
- package/dist/ui/graph.d.ts.map +0 -1
- package/dist/ui/graph.html +0 -52
- package/dist/ui/graph.js +0 -259
- package/dist/ui/graph.js.map +0 -1
|
@@ -0,0 +1,901 @@
|
|
|
1
|
+
// Pre-compiled regex patterns for performance
|
|
2
|
+
const DEF_MATCH_REGEX = /^def\s+(?:(\w+)\s+)?"?([^"]+)"?$/;
|
|
3
|
+
const VARIANT_STRING_REGEX = /^string\s+(\w+)$/;
|
|
4
|
+
const ATTR_MATCH_REGEX = /^(?:uniform\s+)?(\w+(?:\[\])?)\s+(.+)$/;
|
|
5
|
+
|
|
6
|
+
// Spec types (must match USDCParser/USDComposer)
|
|
7
|
+
const SpecType = {
|
|
8
|
+
Attribute: 1,
|
|
9
|
+
Prim: 6,
|
|
10
|
+
Relationship: 8
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
class USDAParser {
|
|
14
|
+
|
|
15
|
+
parseText( text ) {
|
|
16
|
+
|
|
17
|
+
// Preprocess: strip comments and normalize multiline values
|
|
18
|
+
text = this._preprocess( text );
|
|
19
|
+
|
|
20
|
+
const root = {};
|
|
21
|
+
|
|
22
|
+
const lines = text.split( '\n' );
|
|
23
|
+
|
|
24
|
+
let string = null;
|
|
25
|
+
let target = root;
|
|
26
|
+
|
|
27
|
+
const stack = [ root ];
|
|
28
|
+
|
|
29
|
+
for ( const line of lines ) {
|
|
30
|
+
|
|
31
|
+
if ( line.includes( '=' ) ) {
|
|
32
|
+
|
|
33
|
+
// Find the first '=' that's not inside quotes
|
|
34
|
+
const eqIdx = this._findAssignmentOperator( line );
|
|
35
|
+
|
|
36
|
+
if ( eqIdx === - 1 ) {
|
|
37
|
+
|
|
38
|
+
string = line.trim();
|
|
39
|
+
continue;
|
|
40
|
+
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const lhs = line.slice( 0, eqIdx ).trim();
|
|
44
|
+
const rhs = line.slice( eqIdx + 1 ).trim();
|
|
45
|
+
|
|
46
|
+
if ( rhs.endsWith( '{' ) ) {
|
|
47
|
+
|
|
48
|
+
const group = {};
|
|
49
|
+
stack.push( group );
|
|
50
|
+
|
|
51
|
+
target[ lhs ] = group;
|
|
52
|
+
target = group;
|
|
53
|
+
|
|
54
|
+
} else if ( rhs.endsWith( '(' ) ) {
|
|
55
|
+
|
|
56
|
+
// see #28631
|
|
57
|
+
|
|
58
|
+
const values = rhs.slice( 0, - 1 );
|
|
59
|
+
target[ lhs ] = values;
|
|
60
|
+
|
|
61
|
+
const meta = {};
|
|
62
|
+
stack.push( meta );
|
|
63
|
+
|
|
64
|
+
target = meta;
|
|
65
|
+
|
|
66
|
+
} else {
|
|
67
|
+
|
|
68
|
+
target[ lhs ] = rhs;
|
|
69
|
+
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
} else if ( line.includes( ':' ) && ! line.includes( '=' ) ) {
|
|
73
|
+
|
|
74
|
+
// Handle dictionary entries like "0: [(...)...]" for timeSamples
|
|
75
|
+
const colonIdx = line.indexOf( ':' );
|
|
76
|
+
const key = line.slice( 0, colonIdx ).trim();
|
|
77
|
+
const value = line.slice( colonIdx + 1 ).trim();
|
|
78
|
+
|
|
79
|
+
// Only process if key looks like a number (timeSamples frame)
|
|
80
|
+
if ( /^[\d.]+$/.test( key ) ) {
|
|
81
|
+
|
|
82
|
+
target[ key ] = value;
|
|
83
|
+
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
} else if ( line.endsWith( '{' ) ) {
|
|
87
|
+
|
|
88
|
+
string = line.slice( 0, - 1 ).trim() || string;
|
|
89
|
+
|
|
90
|
+
const group = target[ string ] || {};
|
|
91
|
+
stack.push( group );
|
|
92
|
+
|
|
93
|
+
target[ string ] = group;
|
|
94
|
+
target = group;
|
|
95
|
+
|
|
96
|
+
} else if ( line.endsWith( '}' ) ) {
|
|
97
|
+
|
|
98
|
+
stack.pop();
|
|
99
|
+
|
|
100
|
+
if ( stack.length === 0 ) continue;
|
|
101
|
+
|
|
102
|
+
target = stack[ stack.length - 1 ];
|
|
103
|
+
|
|
104
|
+
} else if ( line.endsWith( '(' ) ) {
|
|
105
|
+
|
|
106
|
+
const meta = {};
|
|
107
|
+
stack.push( meta );
|
|
108
|
+
|
|
109
|
+
string = line.split( '(' )[ 0 ].trim() || string;
|
|
110
|
+
|
|
111
|
+
target[ string ] = meta;
|
|
112
|
+
target = meta;
|
|
113
|
+
|
|
114
|
+
} else if ( line.endsWith( ')' ) ) {
|
|
115
|
+
|
|
116
|
+
stack.pop();
|
|
117
|
+
|
|
118
|
+
target = stack[ stack.length - 1 ];
|
|
119
|
+
|
|
120
|
+
} else if ( line.trim() ) {
|
|
121
|
+
|
|
122
|
+
string = line.trim();
|
|
123
|
+
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return root;
|
|
129
|
+
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
_preprocess( text ) {
|
|
133
|
+
|
|
134
|
+
// Remove block comments /* ... */
|
|
135
|
+
text = this._stripBlockComments( text );
|
|
136
|
+
|
|
137
|
+
// Collapse triple-quoted strings into single lines
|
|
138
|
+
text = this._collapseTripleQuotedStrings( text );
|
|
139
|
+
|
|
140
|
+
// Remove line comments # ... (but preserve #usda header)
|
|
141
|
+
// Only remove # comments that aren't at the start of a line or after whitespace
|
|
142
|
+
const lines = text.split( '\n' );
|
|
143
|
+
const processed = [];
|
|
144
|
+
|
|
145
|
+
let inMultilineValue = false;
|
|
146
|
+
let bracketDepth = 0;
|
|
147
|
+
let parenDepth = 0;
|
|
148
|
+
let accumulated = '';
|
|
149
|
+
|
|
150
|
+
for ( let i = 0; i < lines.length; i ++ ) {
|
|
151
|
+
|
|
152
|
+
let line = lines[ i ];
|
|
153
|
+
|
|
154
|
+
// Strip inline comments (but not inside strings)
|
|
155
|
+
line = this._stripInlineComment( line );
|
|
156
|
+
|
|
157
|
+
// Track bracket/paren depth for multiline values
|
|
158
|
+
const trimmed = line.trim();
|
|
159
|
+
|
|
160
|
+
if ( inMultilineValue ) {
|
|
161
|
+
|
|
162
|
+
// Continue accumulating multiline value
|
|
163
|
+
accumulated += ' ' + trimmed;
|
|
164
|
+
|
|
165
|
+
// Update depths
|
|
166
|
+
for ( const ch of trimmed ) {
|
|
167
|
+
|
|
168
|
+
if ( ch === '[' ) bracketDepth ++;
|
|
169
|
+
else if ( ch === ']' ) bracketDepth --;
|
|
170
|
+
else if ( ch === '(' && bracketDepth > 0 ) parenDepth ++;
|
|
171
|
+
else if ( ch === ')' && bracketDepth > 0 ) parenDepth --;
|
|
172
|
+
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Check if multiline value is complete
|
|
176
|
+
if ( bracketDepth === 0 && parenDepth === 0 ) {
|
|
177
|
+
|
|
178
|
+
processed.push( accumulated );
|
|
179
|
+
accumulated = '';
|
|
180
|
+
inMultilineValue = false;
|
|
181
|
+
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
} else {
|
|
185
|
+
|
|
186
|
+
// Check if this line starts a multiline array value
|
|
187
|
+
// Look for patterns like "attr = [" or "attr = @path@[" without closing ]
|
|
188
|
+
if ( trimmed.includes( '=' ) ) {
|
|
189
|
+
|
|
190
|
+
const eqIdx = this._findAssignmentOperator( trimmed );
|
|
191
|
+
|
|
192
|
+
if ( eqIdx !== - 1 ) {
|
|
193
|
+
|
|
194
|
+
const rhs = trimmed.slice( eqIdx + 1 ).trim();
|
|
195
|
+
|
|
196
|
+
// Count brackets in the value part
|
|
197
|
+
let openBrackets = 0;
|
|
198
|
+
let closeBrackets = 0;
|
|
199
|
+
|
|
200
|
+
for ( const ch of rhs ) {
|
|
201
|
+
|
|
202
|
+
if ( ch === '[' ) openBrackets ++;
|
|
203
|
+
else if ( ch === ']' ) closeBrackets ++;
|
|
204
|
+
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if ( openBrackets > closeBrackets ) {
|
|
208
|
+
|
|
209
|
+
// Multiline array detected
|
|
210
|
+
inMultilineValue = true;
|
|
211
|
+
bracketDepth = openBrackets - closeBrackets;
|
|
212
|
+
parenDepth = 0;
|
|
213
|
+
accumulated = trimmed;
|
|
214
|
+
continue;
|
|
215
|
+
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
processed.push( trimmed );
|
|
223
|
+
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return processed.join( '\n' );
|
|
229
|
+
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
_stripBlockComments( text ) {
|
|
233
|
+
|
|
234
|
+
// Iteratively remove /* ... */ comments without regex backtracking
|
|
235
|
+
let result = '';
|
|
236
|
+
let i = 0;
|
|
237
|
+
|
|
238
|
+
while ( i < text.length ) {
|
|
239
|
+
|
|
240
|
+
// Check for block comment start
|
|
241
|
+
if ( text[ i ] === '/' && i + 1 < text.length && text[ i + 1 ] === '*' ) {
|
|
242
|
+
|
|
243
|
+
// Find the closing */
|
|
244
|
+
let j = i + 2;
|
|
245
|
+
|
|
246
|
+
while ( j < text.length ) {
|
|
247
|
+
|
|
248
|
+
if ( text[ j ] === '*' && j + 1 < text.length && text[ j + 1 ] === '/' ) {
|
|
249
|
+
|
|
250
|
+
// Found closing, skip past it
|
|
251
|
+
j += 2;
|
|
252
|
+
break;
|
|
253
|
+
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
j ++;
|
|
257
|
+
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Move past the comment (or to end if unclosed)
|
|
261
|
+
i = j;
|
|
262
|
+
|
|
263
|
+
} else {
|
|
264
|
+
|
|
265
|
+
result += text[ i ];
|
|
266
|
+
i ++;
|
|
267
|
+
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return result;
|
|
273
|
+
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
_collapseTripleQuotedStrings( text ) {
|
|
277
|
+
|
|
278
|
+
let result = '';
|
|
279
|
+
let i = 0;
|
|
280
|
+
|
|
281
|
+
while ( i < text.length ) {
|
|
282
|
+
|
|
283
|
+
if ( i + 2 < text.length ) {
|
|
284
|
+
|
|
285
|
+
const triple = text.slice( i, i + 3 );
|
|
286
|
+
|
|
287
|
+
if ( triple === '\'\'\'' || triple === '"""' ) {
|
|
288
|
+
|
|
289
|
+
const quoteChar = triple;
|
|
290
|
+
result += quoteChar;
|
|
291
|
+
i += 3;
|
|
292
|
+
|
|
293
|
+
while ( i < text.length ) {
|
|
294
|
+
|
|
295
|
+
if ( i + 2 < text.length && text.slice( i, i + 3 ) === quoteChar ) {
|
|
296
|
+
|
|
297
|
+
result += quoteChar;
|
|
298
|
+
i += 3;
|
|
299
|
+
break;
|
|
300
|
+
|
|
301
|
+
} else {
|
|
302
|
+
|
|
303
|
+
if ( text[ i ] === '\n' ) {
|
|
304
|
+
|
|
305
|
+
result += '\\n';
|
|
306
|
+
|
|
307
|
+
} else if ( text[ i ] !== '\r' ) {
|
|
308
|
+
|
|
309
|
+
result += text[ i ];
|
|
310
|
+
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
i ++;
|
|
314
|
+
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
continue;
|
|
320
|
+
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
result += text[ i ];
|
|
326
|
+
i ++;
|
|
327
|
+
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return result;
|
|
331
|
+
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
_stripInlineComment( line ) {
|
|
335
|
+
|
|
336
|
+
// Don't strip if line starts with #usda
|
|
337
|
+
if ( line.trim().startsWith( '#usda' ) ) return line;
|
|
338
|
+
|
|
339
|
+
// Find # that's not inside a string
|
|
340
|
+
let inString = false;
|
|
341
|
+
let stringChar = null;
|
|
342
|
+
let escaped = false;
|
|
343
|
+
|
|
344
|
+
for ( let i = 0; i < line.length; i ++ ) {
|
|
345
|
+
|
|
346
|
+
const ch = line[ i ];
|
|
347
|
+
|
|
348
|
+
if ( escaped ) {
|
|
349
|
+
|
|
350
|
+
escaped = false;
|
|
351
|
+
continue;
|
|
352
|
+
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if ( ch === '\\' ) {
|
|
356
|
+
|
|
357
|
+
escaped = true;
|
|
358
|
+
continue;
|
|
359
|
+
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
if ( ! inString && ( ch === '"' || ch === '\'' ) ) {
|
|
363
|
+
|
|
364
|
+
inString = true;
|
|
365
|
+
stringChar = ch;
|
|
366
|
+
|
|
367
|
+
} else if ( inString && ch === stringChar ) {
|
|
368
|
+
|
|
369
|
+
inString = false;
|
|
370
|
+
stringChar = null;
|
|
371
|
+
|
|
372
|
+
} else if ( ! inString && ch === '#' ) {
|
|
373
|
+
|
|
374
|
+
// Found comment start outside of string
|
|
375
|
+
return line.slice( 0, i ).trimEnd();
|
|
376
|
+
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return line;
|
|
382
|
+
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
_findAssignmentOperator( line ) {
|
|
386
|
+
|
|
387
|
+
// Find the first '=' that's not inside quotes
|
|
388
|
+
let inString = false;
|
|
389
|
+
let stringChar = null;
|
|
390
|
+
let escaped = false;
|
|
391
|
+
|
|
392
|
+
for ( let i = 0; i < line.length; i ++ ) {
|
|
393
|
+
|
|
394
|
+
const ch = line[ i ];
|
|
395
|
+
|
|
396
|
+
if ( escaped ) {
|
|
397
|
+
|
|
398
|
+
escaped = false;
|
|
399
|
+
continue;
|
|
400
|
+
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
if ( ch === '\\' ) {
|
|
404
|
+
|
|
405
|
+
escaped = true;
|
|
406
|
+
continue;
|
|
407
|
+
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if ( ! inString && ( ch === '"' || ch === '\'' ) ) {
|
|
411
|
+
|
|
412
|
+
inString = true;
|
|
413
|
+
stringChar = ch;
|
|
414
|
+
|
|
415
|
+
} else if ( inString && ch === stringChar ) {
|
|
416
|
+
|
|
417
|
+
inString = false;
|
|
418
|
+
stringChar = null;
|
|
419
|
+
|
|
420
|
+
} else if ( ! inString && ch === '=' ) {
|
|
421
|
+
|
|
422
|
+
return i;
|
|
423
|
+
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
return - 1;
|
|
429
|
+
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Parse USDA text and return raw spec data in specsByPath format.
|
|
434
|
+
* Used by USDComposer for unified scene composition.
|
|
435
|
+
*/
|
|
436
|
+
parseData( text ) {
|
|
437
|
+
|
|
438
|
+
const root = this.parseText( text );
|
|
439
|
+
const specsByPath = {};
|
|
440
|
+
|
|
441
|
+
// Parse root metadata
|
|
442
|
+
const rootFields = {};
|
|
443
|
+
if ( '#usda 1.0' in root ) {
|
|
444
|
+
|
|
445
|
+
const header = root[ '#usda 1.0' ];
|
|
446
|
+
|
|
447
|
+
if ( header.upAxis ) {
|
|
448
|
+
|
|
449
|
+
rootFields.upAxis = header.upAxis.replace( /"/g, '' );
|
|
450
|
+
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
if ( header.defaultPrim ) {
|
|
454
|
+
|
|
455
|
+
rootFields.defaultPrim = header.defaultPrim.replace( /"/g, '' );
|
|
456
|
+
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
if ( header.metersPerUnit !== undefined ) {
|
|
460
|
+
|
|
461
|
+
rootFields.metersPerUnit = parseFloat( header.metersPerUnit );
|
|
462
|
+
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
if ( header.framesPerSecond !== undefined ) {
|
|
466
|
+
|
|
467
|
+
rootFields.framesPerSecond = parseFloat( header.framesPerSecond );
|
|
468
|
+
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
if ( header.timeCodesPerSecond !== undefined ) {
|
|
472
|
+
|
|
473
|
+
rootFields.timeCodesPerSecond = parseFloat( header.timeCodesPerSecond );
|
|
474
|
+
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
specsByPath[ '/' ] = { specType: SpecType.Prim, fields: rootFields };
|
|
480
|
+
|
|
481
|
+
// Walk the tree and build specsByPath
|
|
482
|
+
const walkTree = ( data, parentPath ) => {
|
|
483
|
+
|
|
484
|
+
const primChildren = [];
|
|
485
|
+
|
|
486
|
+
for ( const key in data ) {
|
|
487
|
+
|
|
488
|
+
// Skip metadata
|
|
489
|
+
if ( key === '#usda 1.0' ) continue;
|
|
490
|
+
if ( key === 'variants' ) continue;
|
|
491
|
+
|
|
492
|
+
// Check for primitive definitions
|
|
493
|
+
// Matches both 'def TypeName "name"' and 'def "name"' (no type)
|
|
494
|
+
const defMatch = key.match( DEF_MATCH_REGEX );
|
|
495
|
+
if ( defMatch ) {
|
|
496
|
+
|
|
497
|
+
const typeName = defMatch[ 1 ] || '';
|
|
498
|
+
const name = defMatch[ 2 ];
|
|
499
|
+
const path = parentPath === '/' ? '/' + name : parentPath + '/' + name;
|
|
500
|
+
|
|
501
|
+
primChildren.push( name );
|
|
502
|
+
|
|
503
|
+
const primFields = { typeName };
|
|
504
|
+
const primData = data[ key ];
|
|
505
|
+
|
|
506
|
+
// Extract attributes and relationships from this prim
|
|
507
|
+
this._extractPrimData( primData, path, primFields, specsByPath, SpecType );
|
|
508
|
+
|
|
509
|
+
specsByPath[ path ] = { specType: SpecType.Prim, fields: primFields };
|
|
510
|
+
|
|
511
|
+
// Recurse into children
|
|
512
|
+
walkTree( primData, path );
|
|
513
|
+
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// Add primChildren to parent spec
|
|
519
|
+
if ( primChildren.length > 0 && specsByPath[ parentPath ] ) {
|
|
520
|
+
|
|
521
|
+
specsByPath[ parentPath ].fields.primChildren = primChildren;
|
|
522
|
+
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
walkTree( root, '/' );
|
|
528
|
+
|
|
529
|
+
// Fallback: infer elementSize for primvars:skel:jointIndices/jointWeights
|
|
530
|
+
// when not explicitly declared in the USDA text
|
|
531
|
+
this._inferSkelElementSize( specsByPath );
|
|
532
|
+
|
|
533
|
+
return { specsByPath };
|
|
534
|
+
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
_inferSkelElementSize( specsByPath ) {
|
|
538
|
+
|
|
539
|
+
// For each mesh prim with primvars:skel:jointIndices/jointWeights but no
|
|
540
|
+
// elementSize, infer it from the data: elementSize = array.length / numVertices.
|
|
541
|
+
for ( const path in specsByPath ) {
|
|
542
|
+
|
|
543
|
+
const spec = specsByPath[ path ];
|
|
544
|
+
if ( spec.specType !== SpecType.Prim || spec.fields.typeName !== 'Mesh' ) continue;
|
|
545
|
+
|
|
546
|
+
const pointsSpec = specsByPath[ path + '.points' ];
|
|
547
|
+
if ( ! pointsSpec || ! pointsSpec.fields.default ) continue;
|
|
548
|
+
|
|
549
|
+
const numVertices = pointsSpec.fields.default.length / 3;
|
|
550
|
+
if ( numVertices === 0 ) continue;
|
|
551
|
+
|
|
552
|
+
this._inferElementSize( specsByPath[ path + '.primvars:skel:jointIndices' ], numVertices );
|
|
553
|
+
this._inferElementSize( specsByPath[ path + '.primvars:skel:jointWeights' ], numVertices );
|
|
554
|
+
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
_inferElementSize( attrSpec, numVertices ) {
|
|
560
|
+
|
|
561
|
+
if ( ! attrSpec || attrSpec.fields.elementSize !== undefined || ! attrSpec.fields.default ) return;
|
|
562
|
+
|
|
563
|
+
const len = attrSpec.fields.default.length;
|
|
564
|
+
if ( len > 0 && len % numVertices === 0 ) attrSpec.fields.elementSize = len / numVertices;
|
|
565
|
+
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
_extractPrimData( data, path, primFields, specsByPath, SpecType ) {
|
|
569
|
+
|
|
570
|
+
if ( ! data || typeof data !== 'object' ) return;
|
|
571
|
+
|
|
572
|
+
for ( const key in data ) {
|
|
573
|
+
|
|
574
|
+
// Skip nested defs (handled by walkTree)
|
|
575
|
+
if ( key.startsWith( 'def ' ) ) continue;
|
|
576
|
+
|
|
577
|
+
if ( key === 'prepend references' ) {
|
|
578
|
+
|
|
579
|
+
primFields.references = [ data[ key ] ];
|
|
580
|
+
continue;
|
|
581
|
+
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
if ( key === 'payload' ) {
|
|
585
|
+
|
|
586
|
+
primFields.payload = data[ key ];
|
|
587
|
+
continue;
|
|
588
|
+
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
if ( key === 'variants' ) {
|
|
592
|
+
|
|
593
|
+
const variantSelection = {};
|
|
594
|
+
const variants = data[ key ];
|
|
595
|
+
|
|
596
|
+
for ( const vKey in variants ) {
|
|
597
|
+
|
|
598
|
+
const match = vKey.match( VARIANT_STRING_REGEX );
|
|
599
|
+
if ( match ) {
|
|
600
|
+
|
|
601
|
+
const variantSetName = match[ 1 ];
|
|
602
|
+
const variantValue = variants[ vKey ].replace( /"/g, '' );
|
|
603
|
+
variantSelection[ variantSetName ] = variantValue;
|
|
604
|
+
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
if ( Object.keys( variantSelection ).length > 0 ) {
|
|
610
|
+
|
|
611
|
+
primFields.variantSelection = variantSelection;
|
|
612
|
+
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
continue;
|
|
616
|
+
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
if ( key.startsWith( 'rel ' ) ) {
|
|
620
|
+
|
|
621
|
+
const relName = key.slice( 4 );
|
|
622
|
+
const relPath = path + '.' + relName;
|
|
623
|
+
const target = data[ key ].replace( /[<>]/g, '' );
|
|
624
|
+
specsByPath[ relPath ] = {
|
|
625
|
+
specType: SpecType.Relationship,
|
|
626
|
+
fields: { targetPaths: [ target ] }
|
|
627
|
+
};
|
|
628
|
+
continue;
|
|
629
|
+
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// Handle xformOpOrder
|
|
633
|
+
if ( key.includes( 'xformOpOrder' ) ) {
|
|
634
|
+
|
|
635
|
+
const ops = data[ key ]
|
|
636
|
+
.replace( /[\[\]]/g, '' )
|
|
637
|
+
.split( ',' )
|
|
638
|
+
.map( s => s.trim().replace( /"/g, '' ) );
|
|
639
|
+
primFields.xformOpOrder = ops;
|
|
640
|
+
continue;
|
|
641
|
+
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// Handle typed attributes
|
|
645
|
+
// Format: [qualifier] type attrName (e.g., "uniform token[] joints", "float3 position")
|
|
646
|
+
const attrMatch = key.match( ATTR_MATCH_REGEX );
|
|
647
|
+
if ( attrMatch ) {
|
|
648
|
+
|
|
649
|
+
const valueType = attrMatch[ 1 ];
|
|
650
|
+
const attrName = attrMatch[ 2 ];
|
|
651
|
+
const rawValue = data[ key ];
|
|
652
|
+
|
|
653
|
+
// Handle connection attributes (e.g., "inputs:normal.connect = </path>")
|
|
654
|
+
if ( attrName.endsWith( '.connect' ) ) {
|
|
655
|
+
|
|
656
|
+
const baseAttrName = attrName.slice( 0, - 8 ); // Remove '.connect'
|
|
657
|
+
const attrPath = path + '.' + baseAttrName;
|
|
658
|
+
|
|
659
|
+
// Parse connection path - extract from <path> format
|
|
660
|
+
let connPath = String( rawValue ).trim();
|
|
661
|
+
if ( connPath.startsWith( '<' ) ) connPath = connPath.slice( 1 );
|
|
662
|
+
if ( connPath.endsWith( '>' ) ) connPath = connPath.slice( 0, - 1 );
|
|
663
|
+
|
|
664
|
+
// Get or create the attribute spec
|
|
665
|
+
if ( ! specsByPath[ attrPath ] ) {
|
|
666
|
+
|
|
667
|
+
specsByPath[ attrPath ] = {
|
|
668
|
+
specType: SpecType.Attribute,
|
|
669
|
+
fields: { typeName: valueType }
|
|
670
|
+
};
|
|
671
|
+
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
specsByPath[ attrPath ].fields.connectionPaths = [ connPath ];
|
|
675
|
+
continue;
|
|
676
|
+
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// Handle timeSamples attributes specially
|
|
680
|
+
if ( attrName.endsWith( '.timeSamples' ) && typeof rawValue === 'object' ) {
|
|
681
|
+
|
|
682
|
+
const baseAttrName = attrName.slice( 0, - 12 ); // Remove '.timeSamples'
|
|
683
|
+
const attrPath = path + '.' + baseAttrName;
|
|
684
|
+
|
|
685
|
+
// Parse timeSamples dictionary into times and values arrays
|
|
686
|
+
const times = [];
|
|
687
|
+
const values = [];
|
|
688
|
+
|
|
689
|
+
for ( const frameKey in rawValue ) {
|
|
690
|
+
|
|
691
|
+
const frame = parseFloat( frameKey );
|
|
692
|
+
if ( isNaN( frame ) ) continue;
|
|
693
|
+
|
|
694
|
+
times.push( frame );
|
|
695
|
+
values.push( this._parseAttributeValue( valueType, rawValue[ frameKey ] ) );
|
|
696
|
+
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
// Sort by time
|
|
700
|
+
const sorted = times.map( ( t, i ) => ( { t, v: values[ i ] } ) ).sort( ( a, b ) => a.t - b.t );
|
|
701
|
+
|
|
702
|
+
specsByPath[ attrPath ] = {
|
|
703
|
+
specType: SpecType.Attribute,
|
|
704
|
+
fields: {
|
|
705
|
+
timeSamples: { times: sorted.map( s => s.t ), values: sorted.map( s => s.v ) },
|
|
706
|
+
typeName: valueType
|
|
707
|
+
}
|
|
708
|
+
};
|
|
709
|
+
|
|
710
|
+
} else {
|
|
711
|
+
|
|
712
|
+
// Parse value based on type
|
|
713
|
+
const parsedValue = this._parseAttributeValue( valueType, rawValue );
|
|
714
|
+
|
|
715
|
+
// Store as attribute spec, preserving any existing fields
|
|
716
|
+
// (e.g. connectionPaths set by an earlier `.connect` form)
|
|
717
|
+
const attrPath = path + '.' + attrName;
|
|
718
|
+
|
|
719
|
+
if ( specsByPath[ attrPath ] ) {
|
|
720
|
+
|
|
721
|
+
specsByPath[ attrPath ].fields.default = parsedValue;
|
|
722
|
+
specsByPath[ attrPath ].fields.typeName = valueType;
|
|
723
|
+
|
|
724
|
+
} else {
|
|
725
|
+
|
|
726
|
+
specsByPath[ attrPath ] = {
|
|
727
|
+
specType: SpecType.Attribute,
|
|
728
|
+
fields: { default: parsedValue, typeName: valueType }
|
|
729
|
+
};
|
|
730
|
+
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
_parseAttributeValue( valueType, rawValue ) {
|
|
742
|
+
|
|
743
|
+
if ( rawValue === undefined || rawValue === null ) return undefined;
|
|
744
|
+
|
|
745
|
+
const str = String( rawValue ).trim();
|
|
746
|
+
|
|
747
|
+
// Array types
|
|
748
|
+
if ( valueType.endsWith( '[]' ) ) {
|
|
749
|
+
|
|
750
|
+
let result;
|
|
751
|
+
|
|
752
|
+
// Parse JSON-like arrays
|
|
753
|
+
try {
|
|
754
|
+
|
|
755
|
+
// Handle arrays with parentheses like [(1,2,3), (4,5,6)]
|
|
756
|
+
// Remove trailing comma (valid in USDA but not JSON)
|
|
757
|
+
let cleaned = str.replace( /\(/g, '[' ).replace( /\)/g, ']' );
|
|
758
|
+
if ( cleaned.endsWith( ',' ) ) cleaned = cleaned.slice( 0, - 1 );
|
|
759
|
+
const parsed = JSON.parse( cleaned );
|
|
760
|
+
|
|
761
|
+
// Flatten nested arrays for types like point3f[]
|
|
762
|
+
result = Array.isArray( parsed ) && Array.isArray( parsed[ 0 ] ) ? parsed.flat() : parsed;
|
|
763
|
+
|
|
764
|
+
} catch ( e ) {
|
|
765
|
+
|
|
766
|
+
// Try simple array parsing
|
|
767
|
+
const cleaned = str.replace( /[\[\]]/g, '' );
|
|
768
|
+
result = cleaned.split( ',' ).map( s => {
|
|
769
|
+
|
|
770
|
+
const trimmed = s.trim();
|
|
771
|
+
const num = parseFloat( trimmed );
|
|
772
|
+
return isNaN( num ) ? trimmed.replace( /"/g, '' ) : num;
|
|
773
|
+
|
|
774
|
+
} );
|
|
775
|
+
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
//reorder (w, x, y, z) to (x, y, z, w)
|
|
779
|
+
if ( valueType.startsWith( 'quat' ) ) {
|
|
780
|
+
|
|
781
|
+
for ( let i = 0; i < result.length; i += 4 ) {
|
|
782
|
+
|
|
783
|
+
const w = result[ i ];
|
|
784
|
+
result[ i ] = result[ i + 1 ];
|
|
785
|
+
result[ i + 1 ] = result[ i + 2 ];
|
|
786
|
+
result[ i + 2 ] = result[ i + 3 ];
|
|
787
|
+
result[ i + 3 ] = w;
|
|
788
|
+
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
return result;
|
|
794
|
+
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
// Vector types (double3, float3, point3f, etc.)
|
|
798
|
+
if ( valueType.includes( '3' ) || valueType.includes( '2' ) || valueType.includes( '4' ) ) {
|
|
799
|
+
|
|
800
|
+
// Parse (x, y, z) format
|
|
801
|
+
const cleaned = str.replace( /[()]/g, '' );
|
|
802
|
+
const values = cleaned.split( ',' ).map( s => parseFloat( s.trim() ) );
|
|
803
|
+
return values;
|
|
804
|
+
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
// Quaternion types (quatf, quatd, quath)
|
|
808
|
+
// Text format is (w, x, y, z), convert to (x, y, z, w)
|
|
809
|
+
if ( valueType.startsWith( 'quat' ) ) {
|
|
810
|
+
|
|
811
|
+
const cleaned = str.replace( /[()]/g, '' );
|
|
812
|
+
const values = cleaned.split( ',' ).map( s => parseFloat( s.trim() ) );
|
|
813
|
+
return [ values[ 1 ], values[ 2 ], values[ 3 ], values[ 0 ] ];
|
|
814
|
+
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
// Matrix types
|
|
818
|
+
if ( valueType.includes( 'matrix' ) ) {
|
|
819
|
+
|
|
820
|
+
const cleaned = str.replace( /[()]/g, '' );
|
|
821
|
+
const values = cleaned.split( ',' ).map( s => parseFloat( s.trim() ) );
|
|
822
|
+
return values;
|
|
823
|
+
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
// Scalar numeric types
|
|
827
|
+
if ( valueType === 'float' || valueType === 'double' || valueType === 'int' ) {
|
|
828
|
+
|
|
829
|
+
return parseFloat( str );
|
|
830
|
+
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
// String/token types
|
|
834
|
+
if ( valueType === 'string' || valueType === 'token' ) {
|
|
835
|
+
|
|
836
|
+
return this._parseString( str );
|
|
837
|
+
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
// Asset path
|
|
841
|
+
if ( valueType === 'asset' ) {
|
|
842
|
+
|
|
843
|
+
return str.replace( /@/g, '' ).replace( /"/g, '' );
|
|
844
|
+
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
// Default: return as string with quotes removed
|
|
848
|
+
return this._parseString( str );
|
|
849
|
+
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
_parseString( str ) {
|
|
853
|
+
|
|
854
|
+
// Remove surrounding quotes
|
|
855
|
+
if ( ( str.startsWith( '"' ) && str.endsWith( '"' ) ) ||
|
|
856
|
+
( str.startsWith( '\'' ) && str.endsWith( '\'' ) ) ) {
|
|
857
|
+
|
|
858
|
+
str = str.slice( 1, - 1 );
|
|
859
|
+
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
// Handle escape sequences
|
|
863
|
+
let result = '';
|
|
864
|
+
let i = 0;
|
|
865
|
+
|
|
866
|
+
while ( i < str.length ) {
|
|
867
|
+
|
|
868
|
+
if ( str[ i ] === '\\' && i + 1 < str.length ) {
|
|
869
|
+
|
|
870
|
+
const next = str[ i + 1 ];
|
|
871
|
+
|
|
872
|
+
switch ( next ) {
|
|
873
|
+
|
|
874
|
+
case 'n': result += '\n'; break;
|
|
875
|
+
case 't': result += '\t'; break;
|
|
876
|
+
case 'r': result += '\r'; break;
|
|
877
|
+
case '\\': result += '\\'; break;
|
|
878
|
+
case '"': result += '"'; break;
|
|
879
|
+
case '\'': result += '\''; break;
|
|
880
|
+
default: result += next; break;
|
|
881
|
+
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
i += 2;
|
|
885
|
+
|
|
886
|
+
} else {
|
|
887
|
+
|
|
888
|
+
result += str[ i ];
|
|
889
|
+
i ++;
|
|
890
|
+
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
return result;
|
|
896
|
+
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
export { USDAParser };
|