@onerjs/loaders 8.56.1 → 8.56.2
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/SPLAT/gaussianSplattingStream.d.ts +31 -0
- package/SPLAT/gaussianSplattingStream.js +49 -0
- package/SPLAT/gaussianSplattingStream.js.map +1 -1
- package/USD/index.d.ts +2 -0
- package/USD/index.js +4 -0
- package/USD/index.js.map +1 -0
- package/USD/pure.d.ts +3 -0
- package/USD/pure.js +5 -0
- package/USD/pure.js.map +1 -0
- package/USD/usdCommandProtocol.d.ts +83 -0
- package/USD/usdCommandProtocol.js +173 -0
- package/USD/usdCommandProtocol.js.map +1 -0
- package/USD/usdFileLoader.d.ts +6 -0
- package/USD/usdFileLoader.js +9 -0
- package/USD/usdFileLoader.js.map +1 -0
- package/USD/usdFileLoader.metadata.d.ts +20 -0
- package/USD/usdFileLoader.metadata.js +18 -0
- package/USD/usdFileLoader.metadata.js.map +1 -0
- package/USD/usdFileLoader.pure.d.ts +102 -0
- package/USD/usdFileLoader.pure.js +341 -0
- package/USD/usdFileLoader.pure.js.map +1 -0
- package/USD/usdFileLoader.types.d.ts +11 -0
- package/USD/usdFileLoader.types.js +2 -0
- package/USD/usdFileLoader.types.js.map +1 -0
- package/USD/usdLoadingOptions.d.ts +123 -0
- package/USD/usdLoadingOptions.js +3 -0
- package/USD/usdLoadingOptions.js.map +1 -0
- package/USD/usdSceneMaterializer.d.ts +9 -0
- package/USD/usdSceneMaterializer.js +982 -0
- package/USD/usdSceneMaterializer.js.map +1 -0
- package/USD/usdWorkerMessages.d.ts +57 -0
- package/USD/usdWorkerMessages.js +3 -0
- package/USD/usdWorkerMessages.js.map +1 -0
- package/dynamic.js +10 -0
- package/dynamic.js.map +1 -1
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/index.js.map +1 -1
- package/package.json +3 -3
- package/pure.d.ts +1 -0
- package/pure.js +1 -0
- package/pure.js.map +1 -1
|
@@ -0,0 +1,982 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/naming-convention */
|
|
2
|
+
import { Animation } from "@onerjs/core/Animations/animation.pure.js";
|
|
3
|
+
import { AnimationGroup } from "@onerjs/core/Animations/animationGroup.pure.js";
|
|
4
|
+
import { AbstractAssetContainer, AssetContainer } from "@onerjs/core/assetContainer.js";
|
|
5
|
+
import { Bone } from "@onerjs/core/Bones/bone.js";
|
|
6
|
+
import { Skeleton } from "@onerjs/core/Bones/skeleton.js";
|
|
7
|
+
import { Color3, Color4 } from "@onerjs/core/Maths/math.color.pure.js";
|
|
8
|
+
import { Matrix, Quaternion, Vector3 } from "@onerjs/core/Maths/math.vector.pure.js";
|
|
9
|
+
import { Material } from "@onerjs/core/Materials/material.pure.js";
|
|
10
|
+
import { MultiMaterial } from "@onerjs/core/Materials/multiMaterial.pure.js";
|
|
11
|
+
import { PBRMaterial } from "@onerjs/core/Materials/PBR/pbrMaterial.pure.js";
|
|
12
|
+
import { Texture } from "@onerjs/core/Materials/Textures/texture.pure.js";
|
|
13
|
+
import { CreateFactorOperand, CreateTextureOperand, LerpTexturesAsync, TextureChannel, TextureColorSpace, } from "@onerjs/core/Materials/Textures/textureProcessor.js";
|
|
14
|
+
import { MorphTarget } from "@onerjs/core/Morph/morphTarget.js";
|
|
15
|
+
import { MorphTargetManager } from "@onerjs/core/Morph/morphTargetManager.js";
|
|
16
|
+
import { CreateBox } from "@onerjs/core/Meshes/Builders/boxBuilder.pure.js";
|
|
17
|
+
import { CreateCylinder } from "@onerjs/core/Meshes/Builders/cylinderBuilder.pure.js";
|
|
18
|
+
import { CreateSphere } from "@onerjs/core/Meshes/Builders/sphereBuilder.pure.js";
|
|
19
|
+
import { Mesh } from "@onerjs/core/Meshes/mesh.pure.js";
|
|
20
|
+
import { SubMesh } from "@onerjs/core/Meshes/subMesh.js";
|
|
21
|
+
import { TransformNode } from "@onerjs/core/Meshes/transformNode.pure.js";
|
|
22
|
+
import { VertexData } from "@onerjs/core/Meshes/mesh.vertexData.js";
|
|
23
|
+
import { Constants } from "@onerjs/core/Engines/constants.js";
|
|
24
|
+
import { MISSING_OFFSET, PayloadReader, readCommands, } from "./usdCommandProtocol.js";
|
|
25
|
+
class DirectAssetContainer extends AbstractAssetContainer {
|
|
26
|
+
dispose() {
|
|
27
|
+
for (const animationGroup of this.animationGroups.splice(0)) {
|
|
28
|
+
animationGroup.dispose();
|
|
29
|
+
}
|
|
30
|
+
for (const mesh of this.meshes.splice(0)) {
|
|
31
|
+
mesh.dispose();
|
|
32
|
+
}
|
|
33
|
+
for (const manager of this.morphTargetManagers.splice(0)) {
|
|
34
|
+
manager.dispose();
|
|
35
|
+
}
|
|
36
|
+
for (const skeleton of this.skeletons.splice(0)) {
|
|
37
|
+
skeleton.dispose();
|
|
38
|
+
}
|
|
39
|
+
for (const multiMaterial of this.multiMaterials.splice(0)) {
|
|
40
|
+
multiMaterial.dispose();
|
|
41
|
+
}
|
|
42
|
+
for (const material of this.materials.splice(0)) {
|
|
43
|
+
material.dispose();
|
|
44
|
+
}
|
|
45
|
+
for (const geometry of this.geometries.splice(0)) {
|
|
46
|
+
geometry.dispose();
|
|
47
|
+
}
|
|
48
|
+
for (const transformNode of this.transformNodes.splice(0)) {
|
|
49
|
+
transformNode.dispose();
|
|
50
|
+
}
|
|
51
|
+
for (const texture of this.textures.splice(0)) {
|
|
52
|
+
texture.dispose();
|
|
53
|
+
}
|
|
54
|
+
this.rootNodes.length = 0;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const decoder = new TextDecoder();
|
|
58
|
+
const NODE_MATRIX_PROPERTY = "__babylonUsdLocalMatrix";
|
|
59
|
+
function assertRange(data, offset, count, bytesPerElement, label) {
|
|
60
|
+
if (!Number.isInteger(offset) ||
|
|
61
|
+
!Number.isInteger(count) ||
|
|
62
|
+
offset < 0 ||
|
|
63
|
+
count < 0 ||
|
|
64
|
+
offset % Math.min(bytesPerElement, 4) !== 0 ||
|
|
65
|
+
count > Math.floor((data.byteLength - offset) / bytesPerElement)) {
|
|
66
|
+
throw new Error(`Invalid ${label} range in OpenUSD Babylon data buffer.`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function stringAt(data, offset, length) {
|
|
70
|
+
assertRange(data, offset, length, 1, "string");
|
|
71
|
+
return decoder.decode(new Uint8Array(data, offset, length));
|
|
72
|
+
}
|
|
73
|
+
function matrixAt(data, offset) {
|
|
74
|
+
assertRange(data, offset, 16, 4, "matrix");
|
|
75
|
+
const values = new Float32Array(data, offset, 16);
|
|
76
|
+
return Matrix.FromValues(values[0], values[1], values[2], values[3], values[4], values[5], values[6], values[7], values[8], values[9], values[10], values[11], values[12], values[13], values[14], values[15]);
|
|
77
|
+
}
|
|
78
|
+
function mimeType(value) {
|
|
79
|
+
switch (value) {
|
|
80
|
+
case 2:
|
|
81
|
+
return "image/jpeg";
|
|
82
|
+
case 3:
|
|
83
|
+
return "image/bmp";
|
|
84
|
+
case 4:
|
|
85
|
+
return "image/webp";
|
|
86
|
+
default:
|
|
87
|
+
return "image/png";
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function wrapMode(value) {
|
|
91
|
+
switch (value) {
|
|
92
|
+
case 0:
|
|
93
|
+
return Texture.CLAMP_ADDRESSMODE;
|
|
94
|
+
case 1:
|
|
95
|
+
return Texture.WRAP_ADDRESSMODE;
|
|
96
|
+
case 2:
|
|
97
|
+
return Texture.MIRROR_ADDRESSMODE;
|
|
98
|
+
default:
|
|
99
|
+
throw new Error(`Invalid texture wrap mode ${value}.`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
async function awaitAbortableAsync(promise, signal) {
|
|
103
|
+
if (!signal) {
|
|
104
|
+
return await promise;
|
|
105
|
+
}
|
|
106
|
+
let onAbort;
|
|
107
|
+
const aborted = new Promise((_resolve, reject) => {
|
|
108
|
+
onAbort = () => reject(signal.reason instanceof Error ? signal.reason : new Error("USD materialization was aborted."));
|
|
109
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
110
|
+
if (signal.aborted) {
|
|
111
|
+
onAbort();
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
try {
|
|
115
|
+
return await Promise.race([promise, aborted]);
|
|
116
|
+
}
|
|
117
|
+
finally {
|
|
118
|
+
if (onAbort) {
|
|
119
|
+
signal.removeEventListener("abort", onAbort);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
export async function materializeCommandBuffers(scene, commandBuffer, dataBuffer, addToScene, signal) {
|
|
124
|
+
signal?.throwIfAborted();
|
|
125
|
+
const started = performance.now();
|
|
126
|
+
const commands = readCommands(commandBuffer);
|
|
127
|
+
const container = addToScene ? new DirectAssetContainer() : new AssetContainer(scene);
|
|
128
|
+
const nodes = new Map();
|
|
129
|
+
const pendingParents = [];
|
|
130
|
+
const textures = new Map();
|
|
131
|
+
const materials = new Map();
|
|
132
|
+
const doubleSidedMaterials = new Map();
|
|
133
|
+
const skeletons = new Map();
|
|
134
|
+
const bones = new Map();
|
|
135
|
+
const geometries = new Map();
|
|
136
|
+
const meshes = new Map();
|
|
137
|
+
const morphTargetManagers = new Map();
|
|
138
|
+
const morphTargets = new Map();
|
|
139
|
+
const classicInstanceSources = new Set();
|
|
140
|
+
const thinInstanceSources = new Set();
|
|
141
|
+
const animationGroups = new Map();
|
|
142
|
+
const textureLoads = [];
|
|
143
|
+
const textureUrls = new Set();
|
|
144
|
+
const materialTextureBindings = [];
|
|
145
|
+
const processedTextures = new Map();
|
|
146
|
+
const assetContainer = container instanceof AssetContainer ? container : undefined;
|
|
147
|
+
let root;
|
|
148
|
+
let timeCodesPerSecond = 24;
|
|
149
|
+
let rollingBack = false;
|
|
150
|
+
const caps = scene.getEngine().getCaps();
|
|
151
|
+
const processedTextureType = caps.textureHalfFloatRender && caps.textureHalfFloatLinearFiltering
|
|
152
|
+
? Constants.TEXTURETYPE_HALF_FLOAT
|
|
153
|
+
: caps.textureFloatRender && caps.textureFloatLinearFiltering
|
|
154
|
+
? Constants.TEXTURETYPE_FLOAT
|
|
155
|
+
: Constants.TEXTURETYPE_UNSIGNED_BYTE;
|
|
156
|
+
const trackAsset = (assets, asset) => {
|
|
157
|
+
assets.push(asset);
|
|
158
|
+
if (assetContainer) {
|
|
159
|
+
asset._parentContainer = assetContainer;
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
const applyMeshOrientation = (mesh, flags) => {
|
|
163
|
+
const sourceIsRightHanded = !(flags & 2 /* MeshFlags.LeftHanded */);
|
|
164
|
+
mesh.sideOrientation = scene.useRightHandedSystem === sourceIsRightHanded ? Material.CounterClockWiseSideOrientation : Material.ClockWiseSideOrientation;
|
|
165
|
+
};
|
|
166
|
+
const materialForMesh = (id, doubleSided) => {
|
|
167
|
+
const material = materials.get(id);
|
|
168
|
+
if (!material || !doubleSided || !material.backFaceCulling) {
|
|
169
|
+
return material ?? null;
|
|
170
|
+
}
|
|
171
|
+
let variant = doubleSidedMaterials.get(id);
|
|
172
|
+
if (!variant) {
|
|
173
|
+
variant = material.clone(`${material.name} (double-sided)`);
|
|
174
|
+
trackAsset(container.materials, variant);
|
|
175
|
+
variant.backFaceCulling = false;
|
|
176
|
+
variant.twoSidedLighting = true;
|
|
177
|
+
doubleSidedMaterials.set(id, variant);
|
|
178
|
+
}
|
|
179
|
+
return variant;
|
|
180
|
+
};
|
|
181
|
+
const binding = (textureId, channel, label) => {
|
|
182
|
+
if (textureId === MISSING_OFFSET) {
|
|
183
|
+
if (channel !== MISSING_OFFSET) {
|
|
184
|
+
throw new Error(`USD ${label} binding has a channel without a texture.`);
|
|
185
|
+
}
|
|
186
|
+
return undefined;
|
|
187
|
+
}
|
|
188
|
+
const descriptor = textures.get(textureId);
|
|
189
|
+
if (!descriptor) {
|
|
190
|
+
throw new Error(`USD ${label} binding references missing texture ${textureId}.`);
|
|
191
|
+
}
|
|
192
|
+
if (channel < 0 /* TextureOutputChannel.R */ || channel > 4 /* TextureOutputChannel.RGB */) {
|
|
193
|
+
throw new Error(`USD ${label} binding has invalid output channel ${channel}.`);
|
|
194
|
+
}
|
|
195
|
+
return { descriptor, channel };
|
|
196
|
+
};
|
|
197
|
+
const channelIndex = (channel) => {
|
|
198
|
+
if (channel === 4 /* TextureOutputChannel.RGB */) {
|
|
199
|
+
throw new Error("A scalar USD texture binding cannot use the RGB output.");
|
|
200
|
+
}
|
|
201
|
+
return channel;
|
|
202
|
+
};
|
|
203
|
+
const processorChannel = (channel) => {
|
|
204
|
+
switch (channel) {
|
|
205
|
+
case 0 /* TextureOutputChannel.R */:
|
|
206
|
+
return TextureChannel.R;
|
|
207
|
+
case 1 /* TextureOutputChannel.G */:
|
|
208
|
+
return TextureChannel.G;
|
|
209
|
+
case 2 /* TextureOutputChannel.B */:
|
|
210
|
+
return TextureChannel.B;
|
|
211
|
+
case 3 /* TextureOutputChannel.A */:
|
|
212
|
+
return TextureChannel.A;
|
|
213
|
+
default:
|
|
214
|
+
return TextureChannel.RGBA;
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
const processorColorSpace = (descriptor) => descriptor.sourceColorSpace === 1 /* USDTextureColorSpace.Raw */ ? TextureColorSpace.Linear : TextureColorSpace.SRGB;
|
|
218
|
+
const scalarTextureIsLinear = (binding) => binding.descriptor.sourceColorSpace === 1 /* USDTextureColorSpace.Raw */;
|
|
219
|
+
const valueTransformIs = (descriptor, scale, bias) => scale.every((value, index) => descriptor.scale[index] === value) && bias.every((value, index) => descriptor.bias[index] === value);
|
|
220
|
+
const trackProcessedTexture = (texture) => {
|
|
221
|
+
trackAsset(container.textures, texture);
|
|
222
|
+
return texture;
|
|
223
|
+
};
|
|
224
|
+
const processTextureAsync = async (name, binding, scale, bias, channel = binding.channel, preserveRange = false) => {
|
|
225
|
+
const source = binding.descriptor;
|
|
226
|
+
const size = source.texture.getSize();
|
|
227
|
+
const supportsMipMaps = !scene.getEngine().needPOTTextures || ((size.width & (size.width - 1)) === 0 && (size.height & (size.height - 1)) === 0);
|
|
228
|
+
const outputOptions = {
|
|
229
|
+
textureType: preserveRange ? processedTextureType : Constants.TEXTURETYPE_UNSIGNED_BYTE,
|
|
230
|
+
samplingMode: supportsMipMaps ? Constants.TEXTURE_TRILINEAR_SAMPLINGMODE : Constants.TEXTURE_BILINEAR_SAMPLINGMODE,
|
|
231
|
+
generateMipMaps: supportsMipMaps,
|
|
232
|
+
};
|
|
233
|
+
if (preserveRange &&
|
|
234
|
+
processedTextureType === Constants.TEXTURETYPE_UNSIGNED_BYTE &&
|
|
235
|
+
scale.slice(0, 3).some((value, index) => Math.min(bias[index], bias[index] + value) < 0)) {
|
|
236
|
+
throw new Error(`USD texture processing for '${name}' requires a floating-point render target to preserve signed values.`);
|
|
237
|
+
}
|
|
238
|
+
const key = `${source.id}:${channel}:${scale.join(",")}:${bias.join(",")}:${preserveRange}`;
|
|
239
|
+
const cached = processedTextures.get(key);
|
|
240
|
+
if (cached) {
|
|
241
|
+
return await cached;
|
|
242
|
+
}
|
|
243
|
+
const processing = (async () => {
|
|
244
|
+
const a = new Color4(bias[0], bias[1], bias[2], bias[3]);
|
|
245
|
+
const b = new Color4(bias[0] + scale[0], bias[1] + scale[1], bias[2] + scale[2], bias[3] + scale[3]);
|
|
246
|
+
const previousBlockEntityCollection = scene._blockEntityCollection;
|
|
247
|
+
scene._blockEntityCollection = true;
|
|
248
|
+
let processingPromise;
|
|
249
|
+
try {
|
|
250
|
+
processingPromise = LerpTexturesAsync(name, CreateFactorOperand(a), CreateFactorOperand(b), CreateTextureOperand(source.texture, processorChannel(channel), processorColorSpace(source)), scene, TextureColorSpace.Linear, undefined, outputOptions);
|
|
251
|
+
}
|
|
252
|
+
finally {
|
|
253
|
+
scene._blockEntityCollection = previousBlockEntityCollection;
|
|
254
|
+
}
|
|
255
|
+
const processed = await processingPromise;
|
|
256
|
+
if (!processed.texture) {
|
|
257
|
+
throw new Error(`USD texture processing for '${name}' did not produce a texture.`);
|
|
258
|
+
}
|
|
259
|
+
if (rollingBack || signal?.aborted) {
|
|
260
|
+
processed.dispose?.();
|
|
261
|
+
if (signal?.aborted) {
|
|
262
|
+
signal.throwIfAborted();
|
|
263
|
+
}
|
|
264
|
+
throw new Error("USD materialization was rolled back.");
|
|
265
|
+
}
|
|
266
|
+
if (!assetContainer) {
|
|
267
|
+
scene.addTexture(processed.texture);
|
|
268
|
+
}
|
|
269
|
+
const texture = trackProcessedTexture(processed.texture);
|
|
270
|
+
texture.gammaSpace = false;
|
|
271
|
+
const transformedTexture = texture;
|
|
272
|
+
transformedTexture.uRotationCenter = source.texture.uRotationCenter;
|
|
273
|
+
transformedTexture.vRotationCenter = source.texture.vRotationCenter;
|
|
274
|
+
transformedTexture.wRotationCenter = source.texture.wRotationCenter;
|
|
275
|
+
transformedTexture.homogeneousRotationInUVTransform = source.texture.homogeneousRotationInUVTransform;
|
|
276
|
+
return texture;
|
|
277
|
+
})();
|
|
278
|
+
processedTextures.set(key, processing);
|
|
279
|
+
try {
|
|
280
|
+
return await processing;
|
|
281
|
+
}
|
|
282
|
+
catch (error) {
|
|
283
|
+
processedTextures.delete(key);
|
|
284
|
+
throw error;
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
const processScalarTextureAsync = async (name, binding) => {
|
|
288
|
+
const index = channelIndex(binding.channel);
|
|
289
|
+
const scale = binding.descriptor.scale[index];
|
|
290
|
+
const bias = binding.descriptor.bias[index];
|
|
291
|
+
return await processTextureAsync(name, binding, [scale, scale, scale, 1], [bias, bias, bias, 0], binding.channel);
|
|
292
|
+
};
|
|
293
|
+
const applyMaterialTextureBindingsAsync = async (bindings) => {
|
|
294
|
+
const { material, normalScale, base, opacity, normal, metallic, roughness, occlusion, emissive } = bindings;
|
|
295
|
+
if (base) {
|
|
296
|
+
if (base.channel !== 4 /* TextureOutputChannel.RGB */) {
|
|
297
|
+
throw new Error("A USD base-color texture must use the RGB output.");
|
|
298
|
+
}
|
|
299
|
+
material.albedoColor = Color3.White();
|
|
300
|
+
material.albedoTexture = valueTransformIs(base.descriptor, [1, 1, 1, 1], [0, 0, 0, 0])
|
|
301
|
+
? base.descriptor.texture
|
|
302
|
+
: await processTextureAsync(`${material.name} base color`, base, base.descriptor.scale, base.descriptor.bias);
|
|
303
|
+
}
|
|
304
|
+
if (emissive) {
|
|
305
|
+
if (emissive.channel !== 4 /* TextureOutputChannel.RGB */) {
|
|
306
|
+
throw new Error("A USD emissive texture must use the RGB output.");
|
|
307
|
+
}
|
|
308
|
+
const descriptor = emissive.descriptor;
|
|
309
|
+
const zeroBias = descriptor.bias.slice(0, 3).every((value) => value === 0);
|
|
310
|
+
if (zeroBias) {
|
|
311
|
+
material.emissiveColor = new Color3(descriptor.scale[0], descriptor.scale[1], descriptor.scale[2]);
|
|
312
|
+
material.emissiveTexture = descriptor.texture;
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
const factors = Array.from({ length: 3 }, (_, index) => Math.max(1, Math.abs(descriptor.bias[index]), Math.abs(descriptor.bias[index] + descriptor.scale[index])));
|
|
316
|
+
const scale = descriptor.scale.map((value, index) => (index < 3 ? value / factors[index] : value));
|
|
317
|
+
const bias = descriptor.bias.map((value, index) => (index < 3 ? value / factors[index] : value));
|
|
318
|
+
material.emissiveColor = new Color3(factors[0], factors[1], factors[2]);
|
|
319
|
+
material.emissiveTexture = await processTextureAsync(`${material.name} emissive`, emissive, scale, bias, emissive.channel, true);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
if (normal) {
|
|
323
|
+
if (normal.channel !== 4 /* TextureOutputChannel.RGB */) {
|
|
324
|
+
throw new Error("A USD normal texture must use the RGB output.");
|
|
325
|
+
}
|
|
326
|
+
const canonicalNormal = normal.descriptor.sourceColorSpace === 1 /* USDTextureColorSpace.Raw */ && valueTransformIs(normal.descriptor, [2, 2, 2, 1], [-1, -1, -1, 0]);
|
|
327
|
+
if (canonicalNormal) {
|
|
328
|
+
material.bumpTexture = normal.descriptor.texture;
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
const scale = Array.from(normal.descriptor.scale, (value) => value * 0.5);
|
|
332
|
+
const bias = Array.from(normal.descriptor.bias, (value, index) => (index < 3 ? (value + 1) * 0.5 : value));
|
|
333
|
+
material.bumpTexture = await processTextureAsync(`${material.name} normal`, normal, scale, bias);
|
|
334
|
+
}
|
|
335
|
+
material.bumpTexture.gammaSpace = false;
|
|
336
|
+
material.bumpTexture.level = normalScale;
|
|
337
|
+
}
|
|
338
|
+
const packedMetallicRoughness = metallic &&
|
|
339
|
+
roughness &&
|
|
340
|
+
metallic.descriptor === roughness.descriptor &&
|
|
341
|
+
(metallic.channel === 0 /* TextureOutputChannel.R */ || metallic.channel === 2 /* TextureOutputChannel.B */) &&
|
|
342
|
+
(roughness.channel === 1 /* TextureOutputChannel.G */ || roughness.channel === 3 /* TextureOutputChannel.A */) &&
|
|
343
|
+
scalarTextureIsLinear(metallic) &&
|
|
344
|
+
metallic.descriptor.bias[channelIndex(metallic.channel)] === 0 &&
|
|
345
|
+
roughness.descriptor.bias[channelIndex(roughness.channel)] === 0;
|
|
346
|
+
let packedOcclusion = false;
|
|
347
|
+
if (packedMetallicRoughness) {
|
|
348
|
+
const descriptor = metallic.descriptor;
|
|
349
|
+
material.metallicTexture = descriptor.texture;
|
|
350
|
+
material.metallic = (material.metallic ?? 1) * descriptor.scale[channelIndex(metallic.channel)];
|
|
351
|
+
material.roughness = (material.roughness ?? 1) * descriptor.scale[channelIndex(roughness.channel)];
|
|
352
|
+
material.useRoughnessFromMetallicTextureAlpha = roughness.channel === 3 /* TextureOutputChannel.A */;
|
|
353
|
+
material.useRoughnessFromMetallicTextureGreen = roughness.channel === 1 /* TextureOutputChannel.G */;
|
|
354
|
+
material.useMetallnessFromMetallicTextureBlue = metallic.channel === 2 /* TextureOutputChannel.B */;
|
|
355
|
+
packedOcclusion =
|
|
356
|
+
occlusion?.descriptor === descriptor &&
|
|
357
|
+
occlusion.channel === 0 /* TextureOutputChannel.R */ &&
|
|
358
|
+
descriptor.scale[0 /* TextureOutputChannel.R */] === 1 &&
|
|
359
|
+
descriptor.bias[0 /* TextureOutputChannel.R */] === 0;
|
|
360
|
+
material.useAmbientOcclusionFromMetallicTextureRed = packedOcclusion;
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
if (metallic) {
|
|
364
|
+
const index = channelIndex(metallic.channel);
|
|
365
|
+
const direct = scalarTextureIsLinear(metallic) &&
|
|
366
|
+
(metallic.channel === 0 /* TextureOutputChannel.R */ || metallic.channel === 2 /* TextureOutputChannel.B */) &&
|
|
367
|
+
metallic.descriptor.bias[index] === 0;
|
|
368
|
+
material.metallicTexture = direct ? metallic.descriptor.texture : await processScalarTextureAsync(`${material.name} metallic`, metallic);
|
|
369
|
+
material.useRoughnessFromMetallicTextureAlpha = false;
|
|
370
|
+
material.useRoughnessFromMetallicTextureGreen = false;
|
|
371
|
+
material.useMetallnessFromMetallicTextureBlue = direct && metallic.channel === 2 /* TextureOutputChannel.B */;
|
|
372
|
+
material.metallic = direct ? (material.metallic ?? 1) * metallic.descriptor.scale[index] : 1;
|
|
373
|
+
}
|
|
374
|
+
if (roughness) {
|
|
375
|
+
const index = channelIndex(roughness.channel);
|
|
376
|
+
const direct = scalarTextureIsLinear(roughness) && roughness.channel === 0 /* TextureOutputChannel.R */ && roughness.descriptor.bias[index] === 0;
|
|
377
|
+
material.microSurfaceTexture = direct ? roughness.descriptor.texture : await processScalarTextureAsync(`${material.name} roughness`, roughness);
|
|
378
|
+
material.microSurfaceTexture.gammaSpace = direct ? roughness.descriptor.texture.gammaSpace : false;
|
|
379
|
+
material.roughness = direct ? (material.roughness ?? 1) * roughness.descriptor.scale[index] : 1;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
if (occlusion && !packedOcclusion) {
|
|
383
|
+
const index = channelIndex(occlusion.channel);
|
|
384
|
+
const direct = scalarTextureIsLinear(occlusion) &&
|
|
385
|
+
occlusion.channel === 0 /* TextureOutputChannel.R */ &&
|
|
386
|
+
occlusion.descriptor.scale[index] === 1 &&
|
|
387
|
+
occlusion.descriptor.bias[index] === 0;
|
|
388
|
+
material.ambientTexture = direct ? occlusion.descriptor.texture : await processScalarTextureAsync(`${material.name} occlusion`, occlusion);
|
|
389
|
+
material.useAmbientInGrayScale = true;
|
|
390
|
+
}
|
|
391
|
+
if (opacity) {
|
|
392
|
+
if (base &&
|
|
393
|
+
material.albedoTexture &&
|
|
394
|
+
opacity.descriptor === base.descriptor &&
|
|
395
|
+
opacity.channel === 3 /* TextureOutputChannel.A */ &&
|
|
396
|
+
opacity.descriptor.scale[3 /* TextureOutputChannel.A */] === 1 &&
|
|
397
|
+
opacity.descriptor.bias[3 /* TextureOutputChannel.A */] === 0) {
|
|
398
|
+
material.albedoTexture.hasAlpha = true;
|
|
399
|
+
material.useAlphaFromAlbedoTexture = true;
|
|
400
|
+
}
|
|
401
|
+
else {
|
|
402
|
+
const direct = opacity.channel === 3 /* TextureOutputChannel.A */ && opacity.descriptor.scale[3 /* TextureOutputChannel.A */] === 1 && opacity.descriptor.bias[3 /* TextureOutputChannel.A */] === 0;
|
|
403
|
+
material.opacityTexture = direct ? opacity.descriptor.texture : await processScalarTextureAsync(`${material.name} opacity`, opacity);
|
|
404
|
+
material.opacityTexture.gammaSpace = direct ? opacity.descriptor.texture.gammaSpace : false;
|
|
405
|
+
material.opacityTexture.getAlphaFromRGB = !direct;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
const doubleSidedVariant = doubleSidedMaterials.get(bindings.id);
|
|
409
|
+
if (doubleSidedVariant) {
|
|
410
|
+
doubleSidedVariant.albedoColor.copyFrom(material.albedoColor);
|
|
411
|
+
doubleSidedVariant.albedoTexture = material.albedoTexture;
|
|
412
|
+
doubleSidedVariant.emissiveColor.copyFrom(material.emissiveColor);
|
|
413
|
+
doubleSidedVariant.emissiveTexture = material.emissiveTexture;
|
|
414
|
+
doubleSidedVariant.bumpTexture = material.bumpTexture;
|
|
415
|
+
doubleSidedVariant.metallic = material.metallic;
|
|
416
|
+
doubleSidedVariant.roughness = material.roughness;
|
|
417
|
+
doubleSidedVariant.metallicTexture = material.metallicTexture;
|
|
418
|
+
doubleSidedVariant.microSurfaceTexture = material.microSurfaceTexture;
|
|
419
|
+
doubleSidedVariant.ambientTexture = material.ambientTexture;
|
|
420
|
+
doubleSidedVariant.opacityTexture = material.opacityTexture;
|
|
421
|
+
doubleSidedVariant.alpha = material.alpha;
|
|
422
|
+
doubleSidedVariant.useAlphaFromAlbedoTexture = material.useAlphaFromAlbedoTexture;
|
|
423
|
+
doubleSidedVariant.useRoughnessFromMetallicTextureAlpha = material.useRoughnessFromMetallicTextureAlpha;
|
|
424
|
+
doubleSidedVariant.useRoughnessFromMetallicTextureGreen = material.useRoughnessFromMetallicTextureGreen;
|
|
425
|
+
doubleSidedVariant.useMetallnessFromMetallicTextureBlue = material.useMetallnessFromMetallicTextureBlue;
|
|
426
|
+
doubleSidedVariant.useAmbientOcclusionFromMetallicTextureRed = material.useAmbientOcclusionFromMetallicTextureRed;
|
|
427
|
+
doubleSidedVariant.useAmbientInGrayScale = material.useAmbientInGrayScale;
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
try {
|
|
431
|
+
const previousBlockEntityCollection = scene._blockEntityCollection;
|
|
432
|
+
if (!addToScene) {
|
|
433
|
+
scene._blockEntityCollection = true;
|
|
434
|
+
}
|
|
435
|
+
try {
|
|
436
|
+
for (const command of commands) {
|
|
437
|
+
const payload = new PayloadReader(commandBuffer, command.payloadOffset, command.payloadLength);
|
|
438
|
+
switch (command.opcode) {
|
|
439
|
+
case 1 /* Command.Scene */: {
|
|
440
|
+
const zUp = payload.u32() === 1;
|
|
441
|
+
const metersPerUnit = payload.f32();
|
|
442
|
+
timeCodesPerSecond = payload.f32() || 24;
|
|
443
|
+
root = new TransformNode("USD Root", scene);
|
|
444
|
+
trackAsset(container.transformNodes, root);
|
|
445
|
+
container.rootNodes.push(root);
|
|
446
|
+
root.rotationQuaternion = zUp ? Quaternion.FromArray([-0.7071068, 0, 0, 0.7071068]) : Quaternion.Identity();
|
|
447
|
+
root.scaling.copyFrom(scene.useRightHandedSystem
|
|
448
|
+
? new Vector3(metersPerUnit, metersPerUnit, metersPerUnit)
|
|
449
|
+
: zUp
|
|
450
|
+
? new Vector3(metersPerUnit, -metersPerUnit, metersPerUnit)
|
|
451
|
+
: new Vector3(metersPerUnit, metersPerUnit, -metersPerUnit));
|
|
452
|
+
break;
|
|
453
|
+
}
|
|
454
|
+
case 2 /* Command.Texture */: {
|
|
455
|
+
const id = payload.u32();
|
|
456
|
+
if (textures.has(id)) {
|
|
457
|
+
throw new Error(`Duplicate USD texture ${id}.`);
|
|
458
|
+
}
|
|
459
|
+
const nameOffset = payload.u32();
|
|
460
|
+
const nameLength = payload.u32();
|
|
461
|
+
const mime = mimeType(payload.u32());
|
|
462
|
+
const imageOffset = payload.u32();
|
|
463
|
+
const imageLength = payload.u32();
|
|
464
|
+
const coordinatesIndex = payload.u32();
|
|
465
|
+
const transformOffset = payload.u32();
|
|
466
|
+
const wrapU = payload.u32();
|
|
467
|
+
const wrapV = payload.u32();
|
|
468
|
+
const sourceColorSpace = payload.u32();
|
|
469
|
+
const valueTransformOffset = payload.u32();
|
|
470
|
+
assertRange(dataBuffer, transformOffset, 5, 4, "texture transform");
|
|
471
|
+
assertRange(dataBuffer, valueTransformOffset, 8, 4, "texture value transform");
|
|
472
|
+
assertRange(dataBuffer, imageOffset, imageLength, 1, "texture image");
|
|
473
|
+
if (sourceColorSpace < 0 /* USDTextureColorSpace.Auto */ || sourceColorSpace > 2 /* USDTextureColorSpace.SRGB */) {
|
|
474
|
+
throw new Error(`Invalid USD texture color space ${sourceColorSpace}.`);
|
|
475
|
+
}
|
|
476
|
+
const transform = new Float32Array(dataBuffer, transformOffset, 5);
|
|
477
|
+
const valueTransform = new Float32Array(dataBuffer, valueTransformOffset, 8);
|
|
478
|
+
if (![...transform, ...valueTransform].every(Number.isFinite)) {
|
|
479
|
+
throw new Error(`USD texture ${id} has a non-finite transform.`);
|
|
480
|
+
}
|
|
481
|
+
const bytes = new Uint8Array(dataBuffer, imageOffset, imageLength);
|
|
482
|
+
const url = URL.createObjectURL(new Blob([bytes], { type: mime }));
|
|
483
|
+
textureUrls.add(url);
|
|
484
|
+
let resolveLoad;
|
|
485
|
+
let rejectLoad;
|
|
486
|
+
const loaded = new Promise((resolve, reject) => {
|
|
487
|
+
resolveLoad = resolve;
|
|
488
|
+
rejectLoad = reject;
|
|
489
|
+
});
|
|
490
|
+
textureLoads.push(loaded);
|
|
491
|
+
const texture = new Texture(url, scene, false, false, Texture.TRILINEAR_SAMPLINGMODE, resolveLoad, (message, exception) => {
|
|
492
|
+
URL.revokeObjectURL(url);
|
|
493
|
+
rejectLoad(exception instanceof Error ? exception : new Error(message || `Could not load texture '${url}'.`));
|
|
494
|
+
});
|
|
495
|
+
trackAsset(container.textures, texture);
|
|
496
|
+
texture.onDisposeObservable.addOnce(() => URL.revokeObjectURL(url));
|
|
497
|
+
texture.name = stringAt(dataBuffer, nameOffset, nameLength);
|
|
498
|
+
texture.coordinatesIndex = coordinatesIndex;
|
|
499
|
+
texture.uScale = transform[0];
|
|
500
|
+
texture.vScale = transform[1];
|
|
501
|
+
texture.uRotationCenter = 0;
|
|
502
|
+
texture.vRotationCenter = 0;
|
|
503
|
+
texture.uOffset = transform[2] - transform[1] * Math.sin(transform[4]);
|
|
504
|
+
texture.vOffset = 1 - transform[1] * Math.cos(transform[4]) - transform[3];
|
|
505
|
+
texture.wAng = -transform[4];
|
|
506
|
+
texture.wrapU = wrapMode(wrapU);
|
|
507
|
+
texture.wrapV = wrapMode(wrapV);
|
|
508
|
+
texture.gammaSpace = sourceColorSpace !== 1 /* USDTextureColorSpace.Raw */;
|
|
509
|
+
textures.set(id, {
|
|
510
|
+
id,
|
|
511
|
+
texture,
|
|
512
|
+
sourceColorSpace,
|
|
513
|
+
scale: valueTransform.subarray(0, 4),
|
|
514
|
+
bias: valueTransform.subarray(4, 8),
|
|
515
|
+
});
|
|
516
|
+
break;
|
|
517
|
+
}
|
|
518
|
+
case 3 /* Command.Material */: {
|
|
519
|
+
const id = payload.u32();
|
|
520
|
+
const nameOffset = payload.u32();
|
|
521
|
+
const nameLength = payload.u32();
|
|
522
|
+
const baseOffset = payload.u32();
|
|
523
|
+
const emissiveOffset = payload.u32();
|
|
524
|
+
const metallic = payload.f32();
|
|
525
|
+
const roughness = payload.f32();
|
|
526
|
+
const normalScale = payload.f32();
|
|
527
|
+
const alphaCutoff = payload.f32();
|
|
528
|
+
const flags = payload.u32();
|
|
529
|
+
const textureIds = Array.from({ length: 7 }, () => payload.u32());
|
|
530
|
+
const textureChannels = Array.from({ length: 7 }, () => payload.u32());
|
|
531
|
+
assertRange(dataBuffer, baseOffset, 4, 4, "material base color");
|
|
532
|
+
assertRange(dataBuffer, emissiveOffset, 3, 4, "material emissive color");
|
|
533
|
+
const base = new Float32Array(dataBuffer, baseOffset, 4);
|
|
534
|
+
const emissive = new Float32Array(dataBuffer, emissiveOffset, 3);
|
|
535
|
+
const material = new PBRMaterial(stringAt(dataBuffer, nameOffset, nameLength), scene);
|
|
536
|
+
trackAsset(container.materials, material);
|
|
537
|
+
material.id = `usd-material-${id}`;
|
|
538
|
+
material.albedoColor = new Color3(base[0], base[1], base[2]);
|
|
539
|
+
material.alpha = base[3];
|
|
540
|
+
material.metallic = metallic;
|
|
541
|
+
material.roughness = roughness;
|
|
542
|
+
material.emissiveColor = new Color3(emissive[0], emissive[1], emissive[2]);
|
|
543
|
+
const doubleSided = Boolean(flags & 1 /* MaterialFlags.DoubleSided */);
|
|
544
|
+
material.backFaceCulling = !doubleSided;
|
|
545
|
+
material.twoSidedLighting = doubleSided;
|
|
546
|
+
material.unlit = Boolean(flags & 2 /* MaterialFlags.Unlit */);
|
|
547
|
+
if (flags & 4 /* MaterialFlags.AlphaBlend */) {
|
|
548
|
+
material.transparencyMode = 2;
|
|
549
|
+
}
|
|
550
|
+
if (alphaCutoff > 0) {
|
|
551
|
+
material.transparencyMode = 1;
|
|
552
|
+
material.alphaCutOff = alphaCutoff;
|
|
553
|
+
}
|
|
554
|
+
materialTextureBindings.push({
|
|
555
|
+
id,
|
|
556
|
+
material,
|
|
557
|
+
normalScale,
|
|
558
|
+
base: binding(textureIds[0], textureChannels[0], "base-color texture"),
|
|
559
|
+
opacity: binding(textureIds[1], textureChannels[1], "opacity texture"),
|
|
560
|
+
normal: binding(textureIds[2], textureChannels[2], "normal texture"),
|
|
561
|
+
metallic: binding(textureIds[3], textureChannels[3], "metallic texture"),
|
|
562
|
+
roughness: binding(textureIds[4], textureChannels[4], "roughness texture"),
|
|
563
|
+
occlusion: binding(textureIds[5], textureChannels[5], "occlusion texture"),
|
|
564
|
+
emissive: binding(textureIds[6], textureChannels[6], "emissive texture"),
|
|
565
|
+
});
|
|
566
|
+
materials.set(id, material);
|
|
567
|
+
break;
|
|
568
|
+
}
|
|
569
|
+
case 4 /* Command.TransformNode */: {
|
|
570
|
+
const id = payload.u32();
|
|
571
|
+
const parentId = payload.u32();
|
|
572
|
+
const nameOffset = payload.u32();
|
|
573
|
+
const nameLength = payload.u32();
|
|
574
|
+
const matrixOffset = payload.u32();
|
|
575
|
+
const node = new TransformNode(stringAt(dataBuffer, nameOffset, nameLength), scene);
|
|
576
|
+
trackAsset(container.transformNodes, node);
|
|
577
|
+
node.setPreTransformMatrix(matrixAt(dataBuffer, matrixOffset));
|
|
578
|
+
Object.defineProperty(node, NODE_MATRIX_PROPERTY, {
|
|
579
|
+
configurable: true,
|
|
580
|
+
get: () => node.getPivotMatrix(),
|
|
581
|
+
set: (matrix) => node.setPreTransformMatrix(matrix),
|
|
582
|
+
});
|
|
583
|
+
nodes.set(id, node);
|
|
584
|
+
if (parentId === MISSING_OFFSET) {
|
|
585
|
+
node.parent = root ?? null;
|
|
586
|
+
}
|
|
587
|
+
else {
|
|
588
|
+
pendingParents.push({ node, parentId });
|
|
589
|
+
}
|
|
590
|
+
break;
|
|
591
|
+
}
|
|
592
|
+
case 5 /* Command.Skeleton */: {
|
|
593
|
+
const id = payload.u32();
|
|
594
|
+
const nameOffset = payload.u32();
|
|
595
|
+
const nameLength = payload.u32();
|
|
596
|
+
const jointCount = payload.u32();
|
|
597
|
+
const jointsOffset = payload.u32();
|
|
598
|
+
const skeleton = new Skeleton(stringAt(dataBuffer, nameOffset, nameLength), `usd-skeleton-${id}`, scene);
|
|
599
|
+
trackAsset(container.skeletons, skeleton);
|
|
600
|
+
const jointView = new DataView(dataBuffer);
|
|
601
|
+
assertRange(dataBuffer, jointsOffset, jointCount * 6, 4, "skeleton joints");
|
|
602
|
+
const created = [];
|
|
603
|
+
for (let index = 0; index < jointCount; ++index) {
|
|
604
|
+
const offset = jointsOffset + index * 24;
|
|
605
|
+
const parentIndex = jointView.getUint32(offset, true);
|
|
606
|
+
const boneId = jointView.getUint32(offset + 4, true);
|
|
607
|
+
const jointNameOffset = jointView.getUint32(offset + 8, true);
|
|
608
|
+
const jointNameLength = jointView.getUint32(offset + 12, true);
|
|
609
|
+
const restMatrixOffset = jointView.getUint32(offset + 16, true);
|
|
610
|
+
const bindMatrixOffset = jointView.getUint32(offset + 20, true);
|
|
611
|
+
if (parentIndex !== MISSING_OFFSET && parentIndex >= index) {
|
|
612
|
+
throw new Error(`Skeleton ${id} has an invalid parent joint index.`);
|
|
613
|
+
}
|
|
614
|
+
const bone = new Bone(stringAt(dataBuffer, jointNameOffset, jointNameLength), skeleton, parentIndex === MISSING_OFFSET ? null : created[parentIndex], matrixAt(dataBuffer, restMatrixOffset), matrixAt(dataBuffer, restMatrixOffset), matrixAt(dataBuffer, bindMatrixOffset), index);
|
|
615
|
+
created.push(bone);
|
|
616
|
+
bones.set(boneId, bone);
|
|
617
|
+
}
|
|
618
|
+
skeletons.set(id, skeleton);
|
|
619
|
+
break;
|
|
620
|
+
}
|
|
621
|
+
case 6 /* Command.Geometry */: {
|
|
622
|
+
const id = payload.u32();
|
|
623
|
+
geometries.set(id, {
|
|
624
|
+
vertexCount: payload.u32(),
|
|
625
|
+
indexCount: payload.u32(),
|
|
626
|
+
flags: payload.u32(),
|
|
627
|
+
positions: payload.u32(),
|
|
628
|
+
normals: payload.u32(),
|
|
629
|
+
tangents: payload.u32(),
|
|
630
|
+
uv0: payload.u32(),
|
|
631
|
+
colors: payload.u32(),
|
|
632
|
+
joints0: payload.u32(),
|
|
633
|
+
weights0: payload.u32(),
|
|
634
|
+
joints1: payload.u32(),
|
|
635
|
+
weights1: payload.u32(),
|
|
636
|
+
indices: payload.u32(),
|
|
637
|
+
influences: payload.u32(),
|
|
638
|
+
});
|
|
639
|
+
break;
|
|
640
|
+
}
|
|
641
|
+
case 7 /* Command.Mesh */: {
|
|
642
|
+
const id = payload.u32();
|
|
643
|
+
const nodeId = payload.u32();
|
|
644
|
+
const geometryId = payload.u32();
|
|
645
|
+
const materialId = payload.u32();
|
|
646
|
+
const nameOffset = payload.u32();
|
|
647
|
+
const nameLength = payload.u32();
|
|
648
|
+
const flags = payload.u32();
|
|
649
|
+
const skeletonId = payload.u32();
|
|
650
|
+
const submeshesOffset = payload.u32();
|
|
651
|
+
const submeshCount = payload.u32();
|
|
652
|
+
const descriptor = geometries.get(geometryId);
|
|
653
|
+
if (!descriptor) {
|
|
654
|
+
throw new Error(`Mesh ${id} references missing geometry ${geometryId}.`);
|
|
655
|
+
}
|
|
656
|
+
const mesh = new Mesh(stringAt(dataBuffer, nameOffset, nameLength), scene);
|
|
657
|
+
trackAsset(container.meshes, mesh);
|
|
658
|
+
applyMeshOrientation(mesh, flags);
|
|
659
|
+
mesh.parent = nodes.get(nodeId) ?? root ?? null;
|
|
660
|
+
const vertexData = new VertexData();
|
|
661
|
+
assertRange(dataBuffer, descriptor.positions, descriptor.vertexCount * 3, 4, "positions");
|
|
662
|
+
vertexData.positions = new Float32Array(dataBuffer, descriptor.positions, descriptor.vertexCount * 3);
|
|
663
|
+
if (descriptor.flags & 1 /* GeometryFlags.Normals */) {
|
|
664
|
+
assertRange(dataBuffer, descriptor.normals, descriptor.vertexCount * 3, 4, "normals");
|
|
665
|
+
vertexData.normals = new Float32Array(dataBuffer, descriptor.normals, descriptor.vertexCount * 3);
|
|
666
|
+
}
|
|
667
|
+
if (descriptor.flags & 2 /* GeometryFlags.Tangents */) {
|
|
668
|
+
assertRange(dataBuffer, descriptor.tangents, descriptor.vertexCount * 4, 4, "tangents");
|
|
669
|
+
vertexData.tangents = new Float32Array(dataBuffer, descriptor.tangents, descriptor.vertexCount * 4);
|
|
670
|
+
}
|
|
671
|
+
if (descriptor.flags & 4 /* GeometryFlags.Uv0 */) {
|
|
672
|
+
assertRange(dataBuffer, descriptor.uv0, descriptor.vertexCount * 2, 4, "texture coordinates");
|
|
673
|
+
vertexData.uvs = new Float32Array(dataBuffer, descriptor.uv0, descriptor.vertexCount * 2);
|
|
674
|
+
}
|
|
675
|
+
if (descriptor.flags & 8 /* GeometryFlags.Colors */) {
|
|
676
|
+
assertRange(dataBuffer, descriptor.colors, descriptor.vertexCount * 4, 4, "vertex colors");
|
|
677
|
+
vertexData.colors = new Float32Array(dataBuffer, descriptor.colors, descriptor.vertexCount * 4);
|
|
678
|
+
}
|
|
679
|
+
if (descriptor.flags & 16 /* GeometryFlags.Skin0 */) {
|
|
680
|
+
assertRange(dataBuffer, descriptor.joints0, descriptor.vertexCount * 4, 2, "joint indices");
|
|
681
|
+
assertRange(dataBuffer, descriptor.weights0, descriptor.vertexCount * 4, 4, "joint weights");
|
|
682
|
+
vertexData.matricesIndices = Float32Array.from(new Uint16Array(dataBuffer, descriptor.joints0, descriptor.vertexCount * 4));
|
|
683
|
+
vertexData.matricesWeights = new Float32Array(dataBuffer, descriptor.weights0, descriptor.vertexCount * 4);
|
|
684
|
+
}
|
|
685
|
+
if (descriptor.flags & 32 /* GeometryFlags.Skin1 */) {
|
|
686
|
+
assertRange(dataBuffer, descriptor.joints1, descriptor.vertexCount * 4, 2, "extra joint indices");
|
|
687
|
+
assertRange(dataBuffer, descriptor.weights1, descriptor.vertexCount * 4, 4, "extra joint weights");
|
|
688
|
+
vertexData.matricesIndicesExtra = Float32Array.from(new Uint16Array(dataBuffer, descriptor.joints1, descriptor.vertexCount * 4));
|
|
689
|
+
vertexData.matricesWeightsExtra = new Float32Array(dataBuffer, descriptor.weights1, descriptor.vertexCount * 4);
|
|
690
|
+
}
|
|
691
|
+
assertRange(dataBuffer, descriptor.indices, descriptor.indexCount, 4, "indices");
|
|
692
|
+
vertexData.indices = new Uint32Array(dataBuffer, descriptor.indices, descriptor.indexCount);
|
|
693
|
+
vertexData.applyToMesh(mesh, true);
|
|
694
|
+
if (mesh.geometry) {
|
|
695
|
+
trackAsset(container.geometries, mesh.geometry);
|
|
696
|
+
}
|
|
697
|
+
mesh.numBoneInfluencers = Math.min(descriptor.influences, 8);
|
|
698
|
+
if (skeletonId !== MISSING_OFFSET) {
|
|
699
|
+
mesh.skeleton = skeletons.get(skeletonId) ?? null;
|
|
700
|
+
}
|
|
701
|
+
const submeshView = new DataView(dataBuffer);
|
|
702
|
+
assertRange(dataBuffer, submeshesOffset, submeshCount * 5, 4, "submeshes");
|
|
703
|
+
const doubleSided = Boolean(flags & 1 /* MeshFlags.DoubleSided */);
|
|
704
|
+
if (submeshCount === 1 && materialId !== MISSING_OFFSET) {
|
|
705
|
+
mesh.material = materialForMesh(materialId, doubleSided);
|
|
706
|
+
}
|
|
707
|
+
else {
|
|
708
|
+
const multi = new MultiMaterial(`${mesh.name} materials`, scene);
|
|
709
|
+
trackAsset(container.multiMaterials, multi);
|
|
710
|
+
for (let index = 0; index < submeshCount; ++index) {
|
|
711
|
+
const offset = submeshesOffset + index * 20;
|
|
712
|
+
multi.subMaterials.push(materialForMesh(submeshView.getUint32(offset, true), doubleSided));
|
|
713
|
+
}
|
|
714
|
+
mesh.material = multi;
|
|
715
|
+
}
|
|
716
|
+
mesh.releaseSubMeshes();
|
|
717
|
+
for (let index = 0; index < submeshCount; ++index) {
|
|
718
|
+
const offset = submeshesOffset + index * 20;
|
|
719
|
+
new SubMesh(index, submeshView.getUint32(offset + 12, true), submeshView.getUint32(offset + 16, true), submeshView.getUint32(offset + 4, true), submeshView.getUint32(offset + 8, true), mesh);
|
|
720
|
+
}
|
|
721
|
+
meshes.set(id, mesh);
|
|
722
|
+
break;
|
|
723
|
+
}
|
|
724
|
+
case 12 /* Command.MorphTarget */: {
|
|
725
|
+
const id = payload.u32();
|
|
726
|
+
const meshId = payload.u32();
|
|
727
|
+
const nameOffset = payload.u32();
|
|
728
|
+
const nameLength = payload.u32();
|
|
729
|
+
const vertexCount = payload.u32();
|
|
730
|
+
const positionsOffset = payload.u32();
|
|
731
|
+
const normalsOffset = payload.u32();
|
|
732
|
+
const influence = payload.f32();
|
|
733
|
+
const mesh = meshes.get(meshId);
|
|
734
|
+
if (!mesh) {
|
|
735
|
+
throw new Error(`Morph target references missing mesh ${meshId}.`);
|
|
736
|
+
}
|
|
737
|
+
if (morphTargets.has(id) || vertexCount !== mesh.getTotalVertices() || !Number.isFinite(influence)) {
|
|
738
|
+
throw new Error(`Morph target ${id} has invalid metadata.`);
|
|
739
|
+
}
|
|
740
|
+
assertRange(dataBuffer, positionsOffset, vertexCount * 3, 4, "morph target positions");
|
|
741
|
+
let manager = morphTargetManagers.get(meshId);
|
|
742
|
+
if (!manager) {
|
|
743
|
+
manager = new MorphTargetManager(scene, mesh.name);
|
|
744
|
+
manager.areUpdatesFrozen = true;
|
|
745
|
+
trackAsset(container.morphTargetManagers, manager);
|
|
746
|
+
morphTargetManagers.set(meshId, manager);
|
|
747
|
+
}
|
|
748
|
+
const target = new MorphTarget(stringAt(dataBuffer, nameOffset, nameLength), influence, scene, manager);
|
|
749
|
+
target.id = `usd-morph-target-${id}`;
|
|
750
|
+
target.setPositions(new Float32Array(dataBuffer, positionsOffset, vertexCount * 3));
|
|
751
|
+
if (normalsOffset !== MISSING_OFFSET) {
|
|
752
|
+
assertRange(dataBuffer, normalsOffset, vertexCount * 3, 4, "morph target normals");
|
|
753
|
+
target.setNormals(new Float32Array(dataBuffer, normalsOffset, vertexCount * 3));
|
|
754
|
+
}
|
|
755
|
+
manager.addTarget(target);
|
|
756
|
+
morphTargets.set(id, target);
|
|
757
|
+
break;
|
|
758
|
+
}
|
|
759
|
+
case 10 /* Command.AnalyticPrimitive */: {
|
|
760
|
+
const id = payload.u32();
|
|
761
|
+
const nodeId = payload.u32();
|
|
762
|
+
const type = payload.u32();
|
|
763
|
+
const materialId = payload.u32();
|
|
764
|
+
const nameOffset = payload.u32();
|
|
765
|
+
const nameLength = payload.u32();
|
|
766
|
+
const flags = payload.u32();
|
|
767
|
+
const axis = payload.u32();
|
|
768
|
+
const sizeOrRadius = payload.f32();
|
|
769
|
+
const height = payload.f32();
|
|
770
|
+
const tessellation = payload.u32();
|
|
771
|
+
const name = stringAt(dataBuffer, nameOffset, nameLength);
|
|
772
|
+
if (!Number.isFinite(sizeOrRadius) || sizeOrRadius <= 0) {
|
|
773
|
+
throw new Error(`Analytic primitive ${id} has an invalid size or radius.`);
|
|
774
|
+
}
|
|
775
|
+
if ((type === 2 /* AnalyticPrimitiveType.Cylinder */ || type === 3 /* AnalyticPrimitiveType.Cone */) && (!Number.isFinite(height) || height <= 0)) {
|
|
776
|
+
throw new Error(`Analytic primitive ${id} has an invalid height.`);
|
|
777
|
+
}
|
|
778
|
+
if (type !== 0 /* AnalyticPrimitiveType.Cube */ && (!Number.isInteger(tessellation) || tessellation < 3 || tessellation > 512)) {
|
|
779
|
+
throw new Error(`Analytic primitive ${id} has invalid tessellation.`);
|
|
780
|
+
}
|
|
781
|
+
let mesh;
|
|
782
|
+
switch (type) {
|
|
783
|
+
case 0 /* AnalyticPrimitiveType.Cube */:
|
|
784
|
+
mesh = CreateBox(name, { size: sizeOrRadius }, scene);
|
|
785
|
+
break;
|
|
786
|
+
case 1 /* AnalyticPrimitiveType.Sphere */:
|
|
787
|
+
mesh = CreateSphere(name, {
|
|
788
|
+
diameter: sizeOrRadius * 2,
|
|
789
|
+
segments: tessellation,
|
|
790
|
+
}, scene);
|
|
791
|
+
break;
|
|
792
|
+
case 2 /* AnalyticPrimitiveType.Cylinder */:
|
|
793
|
+
case 3 /* AnalyticPrimitiveType.Cone */:
|
|
794
|
+
mesh = CreateCylinder(name, {
|
|
795
|
+
height,
|
|
796
|
+
diameterTop: type === 3 /* AnalyticPrimitiveType.Cone */ ? 0 : sizeOrRadius * 2,
|
|
797
|
+
diameterBottom: sizeOrRadius * 2,
|
|
798
|
+
tessellation,
|
|
799
|
+
}, scene);
|
|
800
|
+
break;
|
|
801
|
+
default:
|
|
802
|
+
throw new Error(`Unsupported analytic primitive type ${type}.`);
|
|
803
|
+
}
|
|
804
|
+
trackAsset(container.meshes, mesh);
|
|
805
|
+
if (mesh.geometry) {
|
|
806
|
+
trackAsset(container.geometries, mesh.geometry);
|
|
807
|
+
}
|
|
808
|
+
// Babylon builders emit left-handed local winding. Reverse only for USD's
|
|
809
|
+
// default right-handed convention; normals already point outward.
|
|
810
|
+
if (!(flags & 2 /* MeshFlags.LeftHanded */)) {
|
|
811
|
+
const sourceIndices = mesh.getIndices();
|
|
812
|
+
if (!sourceIndices) {
|
|
813
|
+
throw new Error(`Analytic primitive ${id} has no generated indices.`);
|
|
814
|
+
}
|
|
815
|
+
const reversed = Array.from(sourceIndices);
|
|
816
|
+
for (let index = 0; index < reversed.length; index += 3) {
|
|
817
|
+
[reversed[index], reversed[index + 2]] = [reversed[index + 2], reversed[index]];
|
|
818
|
+
}
|
|
819
|
+
mesh.setIndices(reversed);
|
|
820
|
+
}
|
|
821
|
+
if (axis === 0 /* PrimitiveAxis.X */) {
|
|
822
|
+
mesh.rotationQuaternion = Quaternion.RotationAxis(new Vector3(0, 0, 1), -Math.PI / 2);
|
|
823
|
+
}
|
|
824
|
+
else if (axis === 2 /* PrimitiveAxis.Z */) {
|
|
825
|
+
mesh.rotationQuaternion = Quaternion.RotationAxis(new Vector3(1, 0, 0), Math.PI / 2);
|
|
826
|
+
}
|
|
827
|
+
else if (axis !== 1 /* PrimitiveAxis.Y */) {
|
|
828
|
+
throw new Error(`Unsupported analytic primitive axis ${axis}.`);
|
|
829
|
+
}
|
|
830
|
+
applyMeshOrientation(mesh, flags);
|
|
831
|
+
mesh.parent = nodes.get(nodeId) ?? root ?? null;
|
|
832
|
+
mesh.material = materialForMesh(materialId, Boolean(flags & 1 /* MeshFlags.DoubleSided */));
|
|
833
|
+
meshes.set(id, mesh);
|
|
834
|
+
break;
|
|
835
|
+
}
|
|
836
|
+
case 8 /* Command.Instance */: {
|
|
837
|
+
const sourceId = payload.u32();
|
|
838
|
+
const nodeId = payload.u32();
|
|
839
|
+
const nameOffset = payload.u32();
|
|
840
|
+
const nameLength = payload.u32();
|
|
841
|
+
const source = meshes.get(sourceId);
|
|
842
|
+
if (!source) {
|
|
843
|
+
throw new Error(`Instance references missing mesh ${sourceId}.`);
|
|
844
|
+
}
|
|
845
|
+
if (thinInstanceSources.has(sourceId)) {
|
|
846
|
+
throw new Error(`Mesh ${sourceId} cannot mix classic and thin instances.`);
|
|
847
|
+
}
|
|
848
|
+
classicInstanceSources.add(sourceId);
|
|
849
|
+
const instance = source.createInstance(stringAt(dataBuffer, nameOffset, nameLength));
|
|
850
|
+
trackAsset(container.meshes, instance);
|
|
851
|
+
instance.parent = nodes.get(nodeId) ?? root ?? null;
|
|
852
|
+
break;
|
|
853
|
+
}
|
|
854
|
+
case 11 /* Command.ThinInstances */: {
|
|
855
|
+
const sourceId = payload.u32();
|
|
856
|
+
const transformsOffset = payload.u32();
|
|
857
|
+
const instanceCount = payload.u32();
|
|
858
|
+
const source = meshes.get(sourceId);
|
|
859
|
+
if (!source) {
|
|
860
|
+
throw new Error(`Thin instances reference missing mesh ${sourceId}.`);
|
|
861
|
+
}
|
|
862
|
+
if (thinInstanceSources.has(sourceId) || classicInstanceSources.has(sourceId)) {
|
|
863
|
+
throw new Error(`Mesh ${sourceId} has duplicate or mixed thin instances.`);
|
|
864
|
+
}
|
|
865
|
+
assertRange(dataBuffer, transformsOffset, instanceCount * 16, 4, "thin instance transforms");
|
|
866
|
+
source.thinInstanceSetBuffer("matrix", new Float32Array(dataBuffer, transformsOffset, instanceCount * 16), 16, true);
|
|
867
|
+
source.thinInstanceEnablePicking = true;
|
|
868
|
+
thinInstanceSources.add(sourceId);
|
|
869
|
+
break;
|
|
870
|
+
}
|
|
871
|
+
case 9 /* Command.Animation */: {
|
|
872
|
+
const targetKind = payload.u32();
|
|
873
|
+
const targetId = payload.u32();
|
|
874
|
+
const property = payload.u32();
|
|
875
|
+
const trackIndex = payload.u32();
|
|
876
|
+
const keyCount = payload.u32();
|
|
877
|
+
const timesOffset = payload.u32();
|
|
878
|
+
const valuesOffset = payload.u32();
|
|
879
|
+
const stride = payload.u32();
|
|
880
|
+
if (targetKind !== 0 /* AnimationTarget.Node */ && targetKind !== 1 /* AnimationTarget.Bone */ && targetKind !== 2 /* AnimationTarget.MorphTarget */) {
|
|
881
|
+
throw new Error(`Invalid animation target kind ${targetKind}.`);
|
|
882
|
+
}
|
|
883
|
+
if ((targetKind === 2 /* AnimationTarget.MorphTarget */) !== (property === 4 /* AnimationProperty.Influence */)) {
|
|
884
|
+
throw new Error(`Invalid animation property ${property} for target kind ${targetKind}.`);
|
|
885
|
+
}
|
|
886
|
+
const expectedStride = property === 4 /* AnimationProperty.Influence */
|
|
887
|
+
? 1
|
|
888
|
+
: property === 0 /* AnimationProperty.Position */ || property === 2 /* AnimationProperty.Scaling */
|
|
889
|
+
? 3
|
|
890
|
+
: property === 1 /* AnimationProperty.RotationQuaternion */
|
|
891
|
+
? 4
|
|
892
|
+
: property === 3 /* AnimationProperty.Matrix */
|
|
893
|
+
? 16
|
|
894
|
+
: 0;
|
|
895
|
+
if (stride !== expectedStride) {
|
|
896
|
+
throw new Error(`Invalid animation value stride ${stride} for property ${property}.`);
|
|
897
|
+
}
|
|
898
|
+
const target = targetKind === 0 /* AnimationTarget.Node */ ? nodes.get(targetId) : targetKind === 1 /* AnimationTarget.Bone */ ? bones.get(targetId) : morphTargets.get(targetId);
|
|
899
|
+
if (!target) {
|
|
900
|
+
break;
|
|
901
|
+
}
|
|
902
|
+
const propertyName = property === 0 /* AnimationProperty.Position */
|
|
903
|
+
? "position"
|
|
904
|
+
: property === 1 /* AnimationProperty.RotationQuaternion */
|
|
905
|
+
? "rotationQuaternion"
|
|
906
|
+
: property === 2 /* AnimationProperty.Scaling */
|
|
907
|
+
? "scaling"
|
|
908
|
+
: property === 4 /* AnimationProperty.Influence */
|
|
909
|
+
? "influence"
|
|
910
|
+
: targetKind === 0 /* AnimationTarget.Node */
|
|
911
|
+
? NODE_MATRIX_PROPERTY
|
|
912
|
+
: "_matrix";
|
|
913
|
+
const dataType = property === 4 /* AnimationProperty.Influence */
|
|
914
|
+
? Animation.ANIMATIONTYPE_FLOAT
|
|
915
|
+
: property === 1 /* AnimationProperty.RotationQuaternion */
|
|
916
|
+
? Animation.ANIMATIONTYPE_QUATERNION
|
|
917
|
+
: property === 3 /* AnimationProperty.Matrix */
|
|
918
|
+
? Animation.ANIMATIONTYPE_MATRIX
|
|
919
|
+
: Animation.ANIMATIONTYPE_VECTOR3;
|
|
920
|
+
const animation = new Animation(`USD ${propertyName}`, propertyName, timeCodesPerSecond, dataType, Animation.ANIMATIONLOOPMODE_CYCLE);
|
|
921
|
+
assertRange(dataBuffer, timesOffset, keyCount, 4, "animation times");
|
|
922
|
+
assertRange(dataBuffer, valuesOffset, keyCount * stride, 4, "animation values");
|
|
923
|
+
const times = new Float32Array(dataBuffer, timesOffset, keyCount);
|
|
924
|
+
const values = new Float32Array(dataBuffer, valuesOffset, keyCount * stride);
|
|
925
|
+
animation.setKeys(Array.from({ length: keyCount }, (_, index) => ({
|
|
926
|
+
frame: times[index],
|
|
927
|
+
value: dataType === Animation.ANIMATIONTYPE_QUATERNION
|
|
928
|
+
? Quaternion.FromArray(values, index * stride)
|
|
929
|
+
: dataType === Animation.ANIMATIONTYPE_MATRIX
|
|
930
|
+
? Matrix.FromArray(values, index * stride)
|
|
931
|
+
: dataType === Animation.ANIMATIONTYPE_FLOAT
|
|
932
|
+
? values[index * stride]
|
|
933
|
+
: Vector3.FromArray(values, index * stride),
|
|
934
|
+
})));
|
|
935
|
+
let group = animationGroups.get(trackIndex);
|
|
936
|
+
if (!group) {
|
|
937
|
+
group = new AnimationGroup(`USD Animation ${trackIndex + 1}`, scene);
|
|
938
|
+
animationGroups.set(trackIndex, group);
|
|
939
|
+
trackAsset(container.animationGroups, group);
|
|
940
|
+
}
|
|
941
|
+
group.addTargetedAnimation(animation, target);
|
|
942
|
+
break;
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
for (const [meshId, manager] of morphTargetManagers) {
|
|
947
|
+
manager.areUpdatesFrozen = false;
|
|
948
|
+
if (!manager.isUsingTextureForTargets && manager.numTargets <= MorphTargetManager.MaxActiveMorphTargetsInVertexAttributeMode) {
|
|
949
|
+
manager.optimizeInfluencers = false;
|
|
950
|
+
manager.numMaxInfluencers = manager.numTargets;
|
|
951
|
+
}
|
|
952
|
+
meshes.get(meshId).morphTargetManager = manager;
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
finally {
|
|
956
|
+
if (!addToScene) {
|
|
957
|
+
scene._blockEntityCollection = previousBlockEntityCollection;
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
await awaitAbortableAsync(Promise.all(textureLoads), signal);
|
|
961
|
+
await awaitAbortableAsync(Promise.all(materialTextureBindings.map(async (bindings) => {
|
|
962
|
+
await applyMaterialTextureBindingsAsync(bindings);
|
|
963
|
+
})), signal);
|
|
964
|
+
for (const { node, parentId } of pendingParents) {
|
|
965
|
+
node.parent = nodes.get(parentId) ?? root ?? null;
|
|
966
|
+
}
|
|
967
|
+
return { container, materializeMs: performance.now() - started };
|
|
968
|
+
}
|
|
969
|
+
catch (error) {
|
|
970
|
+
rollingBack = true;
|
|
971
|
+
// Decode callbacks can arrive after rollback; observe their rejections without
|
|
972
|
+
// delaying cancellation on an image load that may never finish.
|
|
973
|
+
void Promise.allSettled(textureLoads);
|
|
974
|
+
void Promise.allSettled(processedTextures.values());
|
|
975
|
+
container.dispose();
|
|
976
|
+
for (const url of textureUrls) {
|
|
977
|
+
URL.revokeObjectURL(url);
|
|
978
|
+
}
|
|
979
|
+
throw error;
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
//# sourceMappingURL=usdSceneMaterializer.js.map
|