@kitware/vtk.js 32.6.2 → 32.7.0
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/Common/Core/Math/index.js +1 -1
- package/Common/Core/Math.js +1 -1
- package/IO/Geometry/GLTFImporter/Animations.js +239 -0
- package/IO/Geometry/GLTFImporter/Constants.js +87 -0
- package/IO/Geometry/GLTFImporter/Decoder.js +69 -0
- package/IO/Geometry/GLTFImporter/Extensions.js +110 -0
- package/IO/Geometry/GLTFImporter/ORMTexture.worker.js +42 -0
- package/IO/Geometry/GLTFImporter/Parser.js +359 -0
- package/IO/Geometry/GLTFImporter/Reader.js +507 -0
- package/IO/Geometry/GLTFImporter/Utils.js +165 -0
- package/IO/Geometry/GLTFImporter.d.ts +266 -0
- package/IO/Geometry/GLTFImporter.js +245 -0
- package/IO/Geometry.js +3 -1
- package/IO/Image/HDRReader/Utils.js +1 -1
- package/IO/Image/HDRReader.js +1 -1
- package/Interaction/Manipulators/MouseCameraTrackballRollManipulator.js +1 -1
- package/Interaction/Style/InteractorStyleTrackballCamera.js +1 -1
- package/Rendering/Core/Prop3D.js +1 -1
- package/Rendering/Core/RenderWindowInteractor.js +1 -1
- package/_virtual/rollup-plugin-worker-loader__module_Sources/IO/Geometry/GLTFImporter/ORMTexture.worker.js +296 -0
- package/index.d.ts +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import { m as macro } from '../../../macros2.js';
|
|
2
|
+
import { SEMANTIC_ATTRIBUTE_MAP, MODES, ALPHA_MODE, BYTES, COMPONENTS, DEFAULT_SAMPLER, GL_SAMPLER } from './Constants.js';
|
|
3
|
+
import { getAccessorArrayTypeAndLength, resolveUrl, getGLEnumFromSamplerParameter } from './Utils.js';
|
|
4
|
+
|
|
5
|
+
/* eslint-disable guard-for-in */
|
|
6
|
+
const {
|
|
7
|
+
vtkDebugMacro,
|
|
8
|
+
vtkWarningMacro
|
|
9
|
+
} = macro;
|
|
10
|
+
class GLTFParser {
|
|
11
|
+
constructor(glTF) {
|
|
12
|
+
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
13
|
+
const {
|
|
14
|
+
json,
|
|
15
|
+
baseUri = ''
|
|
16
|
+
} = glTF;
|
|
17
|
+
this.glTF = glTF;
|
|
18
|
+
this.options = options;
|
|
19
|
+
this.baseUri = baseUri;
|
|
20
|
+
this.json = json;
|
|
21
|
+
this.extensions = json.extensions || {};
|
|
22
|
+
this.extensionsUsed = json.extensionsUsed || [];
|
|
23
|
+
}
|
|
24
|
+
async parse() {
|
|
25
|
+
const buffers = this.json.buffers || [];
|
|
26
|
+
this.buffers = new Array(buffers.length).fill(null);
|
|
27
|
+
const images = this.json.images || [];
|
|
28
|
+
this.images = new Array(images.length).fill({});
|
|
29
|
+
await this.loadBuffers();
|
|
30
|
+
await this.loadImages();
|
|
31
|
+
this.resolveTree();
|
|
32
|
+
return this.glTF.json;
|
|
33
|
+
}
|
|
34
|
+
resolveTree() {
|
|
35
|
+
this.json.scenes = this.json.scenes?.map((scene, idx) => this.resolveScene(scene, idx));
|
|
36
|
+
this.json.cameras = this.json.cameras?.map((camera, idx) => this.resolveCamera(camera, idx));
|
|
37
|
+
this.json.bufferViews = this.json.bufferViews?.map((bufView, idx) => this.resolveBufferView(bufView, idx));
|
|
38
|
+
this.json.images = this.json.images?.map((image, idx) => this.resolveImage(image, idx));
|
|
39
|
+
this.json.samplers = this.json.samplers?.map((sampler, idx) => this.resolveSampler(sampler, idx));
|
|
40
|
+
this.json.textures = this.json.textures?.map((texture, idx) => this.resolveTexture(texture, idx));
|
|
41
|
+
this.json.accessors = this.json.accessors?.map((accessor, idx) => this.resolveAccessor(accessor, idx));
|
|
42
|
+
this.json.materials = this.json.materials?.map((material, idx) => this.resolveMaterial(material, idx));
|
|
43
|
+
this.json.meshes = this.json.meshes?.map((mesh, idx) => this.resolveMesh(mesh, idx));
|
|
44
|
+
this.json.nodes = this.json.nodes?.map((node, idx) => this.resolveNode(node, idx));
|
|
45
|
+
this.json.skins = this.json.skins?.map((skin, idx) => this.resolveSkin(skin, idx));
|
|
46
|
+
this.json.animations = this.json.animations?.map((animation, idx) => this.resolveAnimation(animation, idx));
|
|
47
|
+
}
|
|
48
|
+
get(array, index) {
|
|
49
|
+
// check if already resolved
|
|
50
|
+
if (typeof index === 'object') {
|
|
51
|
+
return index;
|
|
52
|
+
}
|
|
53
|
+
const object = this.json[array] && this.json[array][index];
|
|
54
|
+
if (!object) {
|
|
55
|
+
vtkWarningMacro(`glTF file error: Could not find ${array}[${index}]`);
|
|
56
|
+
}
|
|
57
|
+
return object;
|
|
58
|
+
}
|
|
59
|
+
resolveScene(scene, index) {
|
|
60
|
+
scene.id = scene.id || `scene-${index}`;
|
|
61
|
+
scene.nodes = (scene.nodes || []).map(node => this.get('nodes', node));
|
|
62
|
+
return scene;
|
|
63
|
+
}
|
|
64
|
+
resolveNode(node, index) {
|
|
65
|
+
node.id = node.id || `node-${index}`;
|
|
66
|
+
if (node.children) {
|
|
67
|
+
node.children = node.children.map(child => this.get('nodes', child));
|
|
68
|
+
}
|
|
69
|
+
if (node.mesh !== undefined) {
|
|
70
|
+
node.mesh = this.get('meshes', node.mesh);
|
|
71
|
+
} else if (node.meshes !== undefined && node.meshes.length) {
|
|
72
|
+
node.mesh = node.meshes.reduce((accum, meshIndex) => {
|
|
73
|
+
const mesh = this.get('meshes', meshIndex);
|
|
74
|
+
accum.id = mesh.id;
|
|
75
|
+
accum.primitives = accum.primitives.concat(mesh.primitives);
|
|
76
|
+
return accum;
|
|
77
|
+
}, {
|
|
78
|
+
primitives: []
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
if (node.camera !== undefined) {
|
|
82
|
+
node.camera = this.get('cameras', node.camera);
|
|
83
|
+
}
|
|
84
|
+
if (node.skin !== undefined) {
|
|
85
|
+
node.skin = this.get('skins', node.skin);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Fill punctual lights objects
|
|
89
|
+
if (node.extensions?.KHR_lights_punctual) {
|
|
90
|
+
node.extensions.KHR_lights_punctual.light = this.extensions?.KHR_lights_punctual.lights[node.extensions.KHR_lights_punctual.light];
|
|
91
|
+
}
|
|
92
|
+
return node;
|
|
93
|
+
}
|
|
94
|
+
resolveSkin(skin, index) {
|
|
95
|
+
skin.id = skin.id || `skin-${index}`;
|
|
96
|
+
skin.inverseBindMatrices = this.get('accessors', skin.inverseBindMatrices);
|
|
97
|
+
return skin;
|
|
98
|
+
}
|
|
99
|
+
resolveMesh(mesh, index) {
|
|
100
|
+
mesh.id = mesh.id || `mesh-${index}`;
|
|
101
|
+
if (mesh.primitives) {
|
|
102
|
+
mesh.primitives = mesh.primitives.map((primitive, idx) => {
|
|
103
|
+
const attributes = primitive.attributes;
|
|
104
|
+
primitive.name = `submesh-${idx}`;
|
|
105
|
+
primitive.attributes = {};
|
|
106
|
+
for (const attribute in attributes) {
|
|
107
|
+
const attr = SEMANTIC_ATTRIBUTE_MAP[attribute];
|
|
108
|
+
primitive.attributes[attr] = this.get('accessors', attributes[attribute]);
|
|
109
|
+
}
|
|
110
|
+
if (primitive.indices !== undefined) {
|
|
111
|
+
primitive.indices = this.get('accessors', primitive.indices);
|
|
112
|
+
}
|
|
113
|
+
if (primitive.material !== undefined) {
|
|
114
|
+
primitive.material = this.get('materials', primitive.material);
|
|
115
|
+
}
|
|
116
|
+
if (primitive.mode === undefined) {
|
|
117
|
+
primitive.mode = MODES.GL_TRIANGLES; // Default one
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (primitive.extensions?.KHR_draco_mesh_compression) {
|
|
121
|
+
vtkDebugMacro('Using Draco mesh compression');
|
|
122
|
+
const bufferView = this.get('bufferViews', primitive.extensions.KHR_draco_mesh_compression.bufferView);
|
|
123
|
+
primitive.extensions.KHR_draco_mesh_compression.bufferView = bufferView.data;
|
|
124
|
+
}
|
|
125
|
+
return primitive;
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
return mesh;
|
|
129
|
+
}
|
|
130
|
+
resolveMaterial(material, index) {
|
|
131
|
+
material.id = material.id || `material-${index}`;
|
|
132
|
+
if (material.alphaMode === undefined) material.alphaMode = ALPHA_MODE.OPAQUE;
|
|
133
|
+
if (material.doubleSided === undefined) material.doubleSided = false;
|
|
134
|
+
if (material.alphaCutoff === undefined) material.alphaCutoff = 0.5;
|
|
135
|
+
if (material.normalTexture) {
|
|
136
|
+
material.normalTexture = {
|
|
137
|
+
...material.normalTexture
|
|
138
|
+
};
|
|
139
|
+
material.normalTexture.texture = this.get('textures', material.normalTexture.index);
|
|
140
|
+
}
|
|
141
|
+
if (material.occlusionTexture) {
|
|
142
|
+
material.occlusionTexture = {
|
|
143
|
+
...material.occlusionTexture
|
|
144
|
+
};
|
|
145
|
+
material.occlusionTexture.texture = this.get('textures', material.occlusionTexture.index);
|
|
146
|
+
}
|
|
147
|
+
if (material.emissiveTexture) {
|
|
148
|
+
material.emissiveTexture = {
|
|
149
|
+
...material.emissiveTexture
|
|
150
|
+
};
|
|
151
|
+
material.emissiveTexture.texture = this.get('textures', material.emissiveTexture.index);
|
|
152
|
+
}
|
|
153
|
+
if (!material.emissiveFactor) {
|
|
154
|
+
material.emissiveFactor = material.emissiveTexture ? 1 : 0;
|
|
155
|
+
} else material.emissiveFactor = material.emissiveFactor[0];
|
|
156
|
+
if (material.pbrMetallicRoughness) {
|
|
157
|
+
material.pbrMetallicRoughness = {
|
|
158
|
+
...material.pbrMetallicRoughness
|
|
159
|
+
};
|
|
160
|
+
const mr = material.pbrMetallicRoughness;
|
|
161
|
+
if (mr.baseColorTexture) {
|
|
162
|
+
mr.baseColorTexture = {
|
|
163
|
+
...mr.baseColorTexture
|
|
164
|
+
};
|
|
165
|
+
mr.baseColorTexture.texture = this.get('textures', mr.baseColorTexture.index);
|
|
166
|
+
}
|
|
167
|
+
if (mr.metallicRoughnessTexture) {
|
|
168
|
+
mr.metallicRoughnessTexture = {
|
|
169
|
+
...mr.metallicRoughnessTexture
|
|
170
|
+
};
|
|
171
|
+
mr.metallicRoughnessTexture.texture = this.get('textures', mr.metallicRoughnessTexture.index);
|
|
172
|
+
}
|
|
173
|
+
} else {
|
|
174
|
+
material.pbrMetallicRoughness = {
|
|
175
|
+
baseColorFactor: [1, 1, 1, 1],
|
|
176
|
+
metallicFactor: 1.0,
|
|
177
|
+
roughnessFactor: 1.0
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
return material;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Take values of particular accessor from interleaved buffer various parts of
|
|
185
|
+
* the buffer
|
|
186
|
+
*/
|
|
187
|
+
getValueFromInterleavedBuffer(buffer, byteOffset, byteStride, bytesPerElement, count) {
|
|
188
|
+
const result = new Uint8Array(count * bytesPerElement);
|
|
189
|
+
for (let i = 0; i < count; i++) {
|
|
190
|
+
const elementOffset = byteOffset + i * byteStride;
|
|
191
|
+
result.set(new Uint8Array(buffer.arrayBuffer.slice(elementOffset, elementOffset + bytesPerElement)), i * bytesPerElement);
|
|
192
|
+
}
|
|
193
|
+
return result.buffer;
|
|
194
|
+
}
|
|
195
|
+
resolveAccessor(accessor, index) {
|
|
196
|
+
accessor.id = accessor.id || `accessor-${index}`;
|
|
197
|
+
if (accessor.bufferView !== undefined) {
|
|
198
|
+
// Draco encoded meshes don't have bufferView
|
|
199
|
+
accessor.bufferView = this.get('bufferViews', accessor.bufferView);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Look up enums
|
|
203
|
+
accessor.bytesPerComponent = BYTES[accessor.componentType];
|
|
204
|
+
accessor.components = COMPONENTS[accessor.type];
|
|
205
|
+
accessor.bytesPerElement = accessor.bytesPerComponent * accessor.components;
|
|
206
|
+
|
|
207
|
+
// Create TypedArray for the accessor
|
|
208
|
+
// Note: The canonical way to instantiate is to ignore this array and create
|
|
209
|
+
// WebGLBuffer's using the bufferViews.
|
|
210
|
+
if (accessor.bufferView) {
|
|
211
|
+
const buffer = accessor.bufferView.buffer;
|
|
212
|
+
const {
|
|
213
|
+
ArrayType,
|
|
214
|
+
byteLength
|
|
215
|
+
} = getAccessorArrayTypeAndLength(accessor, accessor.bufferView);
|
|
216
|
+
const byteOffset = (accessor.bufferView.byteOffset || 0) + (accessor.byteOffset || 0) + buffer.byteOffset;
|
|
217
|
+
let slicedBufffer = buffer.arrayBuffer.slice(byteOffset, byteOffset + byteLength);
|
|
218
|
+
if (accessor.bufferView.byteStride) {
|
|
219
|
+
slicedBufffer = this.getValueFromInterleavedBuffer(buffer, byteOffset, accessor.bufferView.byteStride, accessor.bytesPerElement, accessor.count);
|
|
220
|
+
}
|
|
221
|
+
accessor.value = new ArrayType(slicedBufffer);
|
|
222
|
+
}
|
|
223
|
+
return accessor;
|
|
224
|
+
}
|
|
225
|
+
resolveTexture(texture, index) {
|
|
226
|
+
texture.id = texture.id || `texture-${index}`;
|
|
227
|
+
texture.sampler = 'sampler' in texture ? this.get('samplers', texture.sampler) : DEFAULT_SAMPLER;
|
|
228
|
+
texture.source = this.get('images', texture.source);
|
|
229
|
+
|
|
230
|
+
// Handle texture extensions sources
|
|
231
|
+
if (texture.extensions !== undefined) {
|
|
232
|
+
const extensionsNames = Object.keys(texture.extensions);
|
|
233
|
+
extensionsNames.forEach(extensionName => {
|
|
234
|
+
const extension = texture.extensions[extensionName];
|
|
235
|
+
switch (extensionName) {
|
|
236
|
+
case 'KHR_texture_basisu':
|
|
237
|
+
case 'EXT_texture_webp':
|
|
238
|
+
case 'EXT_texture_avif':
|
|
239
|
+
texture.source = this.get('images', extension.source);
|
|
240
|
+
break;
|
|
241
|
+
default:
|
|
242
|
+
vtkWarningMacro(`Unhandled extension: ${extensionName}`);
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
return texture;
|
|
247
|
+
}
|
|
248
|
+
resolveSampler(sampler, index) {
|
|
249
|
+
sampler.id = sampler.id || `sampler-${index}`;
|
|
250
|
+
if (!Object.hasOwn(sampler, 'wrapS')) sampler.wrapS = GL_SAMPLER.REPEAT;
|
|
251
|
+
if (!Object.hasOwn(sampler, 'wrapT')) sampler.wrapT = GL_SAMPLER.REPEAT;
|
|
252
|
+
if (!Object.hasOwn(sampler, 'minFilter')) sampler.minFilter = GL_SAMPLER.LINEAR_MIPMAP_LINEAR;
|
|
253
|
+
if (!Object.hasOwn(sampler, 'magFilter')) sampler.magFilter = GL_SAMPLER.NEAREST;
|
|
254
|
+
|
|
255
|
+
// Map textual parameters to GL parameter values
|
|
256
|
+
sampler.parameters = {};
|
|
257
|
+
for (const key in sampler) {
|
|
258
|
+
const glEnum = getGLEnumFromSamplerParameter(key);
|
|
259
|
+
if (glEnum !== undefined) {
|
|
260
|
+
sampler.parameters[glEnum] = sampler[key];
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return sampler;
|
|
264
|
+
}
|
|
265
|
+
resolveImage(image, index) {
|
|
266
|
+
image.id = image.id || `image-${index}`;
|
|
267
|
+
if (image.bufferView !== undefined) {
|
|
268
|
+
image.bufferView = this.get('bufferViews', image.bufferView);
|
|
269
|
+
}
|
|
270
|
+
return image;
|
|
271
|
+
}
|
|
272
|
+
resolveBufferView(bufferView, index) {
|
|
273
|
+
bufferView.id = bufferView.id || `bufferView-${index}`;
|
|
274
|
+
const bufferIndex = bufferView.buffer;
|
|
275
|
+
bufferView.buffer = this.buffers[bufferIndex];
|
|
276
|
+
const arrayBuffer = this.buffers[bufferIndex].arrayBuffer;
|
|
277
|
+
let byteOffset = this.buffers[bufferIndex].byteOffset || 0;
|
|
278
|
+
if ('byteOffset' in bufferView) {
|
|
279
|
+
byteOffset += bufferView.byteOffset;
|
|
280
|
+
}
|
|
281
|
+
bufferView.data = new Uint8Array(arrayBuffer, byteOffset, bufferView.byteLength);
|
|
282
|
+
return bufferView;
|
|
283
|
+
}
|
|
284
|
+
resolveCamera(camera, index) {
|
|
285
|
+
camera.id = camera.id || `camera-${index}`;
|
|
286
|
+
return camera;
|
|
287
|
+
}
|
|
288
|
+
resolveAnimation(animation, index) {
|
|
289
|
+
animation.id = animation.id || `animation-${index}`;
|
|
290
|
+
animation.samplers.map(sampler => {
|
|
291
|
+
sampler.input = this.get('accessors', sampler.input).value;
|
|
292
|
+
sampler.output = this.get('accessors', sampler.output).value;
|
|
293
|
+
return sampler;
|
|
294
|
+
});
|
|
295
|
+
return animation;
|
|
296
|
+
}
|
|
297
|
+
loadBuffers() {
|
|
298
|
+
const promises = this.json.buffers.map((buffer, idx) => this.loadBuffer(buffer, idx).then(() => {
|
|
299
|
+
delete buffer.uri;
|
|
300
|
+
}));
|
|
301
|
+
return Promise.all(promises);
|
|
302
|
+
}
|
|
303
|
+
async loadBuffer(buffer, index) {
|
|
304
|
+
let arrayBuffer = buffer;
|
|
305
|
+
if (buffer.uri) {
|
|
306
|
+
vtkDebugMacro('Loading uri', buffer.uri);
|
|
307
|
+
const uri = resolveUrl(buffer.uri, this.options.baseUri);
|
|
308
|
+
const response = await fetch(uri);
|
|
309
|
+
arrayBuffer = await response.arrayBuffer();
|
|
310
|
+
} else if (this.glTF.glbBuffers) {
|
|
311
|
+
arrayBuffer = this.glTF.glbBuffers[index];
|
|
312
|
+
}
|
|
313
|
+
this.buffers[index] = {
|
|
314
|
+
arrayBuffer,
|
|
315
|
+
byteOffset: 0,
|
|
316
|
+
byteLength: arrayBuffer.byteLength
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
loadImages() {
|
|
320
|
+
const images = this.json.images || [];
|
|
321
|
+
const promises = [];
|
|
322
|
+
return new Promise((resolve, reject) => {
|
|
323
|
+
for (let i = 0; i < images.length; ++i) {
|
|
324
|
+
promises.push(Promise.resolve(this.loadImage(images[i], i).then(() => {
|
|
325
|
+
vtkDebugMacro('Texture loaded ', images[i]);
|
|
326
|
+
})));
|
|
327
|
+
}
|
|
328
|
+
Promise.all(promises).then(() => resolve(this.images));
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
async loadImage(image, index) {
|
|
332
|
+
let arrayBuffer;
|
|
333
|
+
let buffer;
|
|
334
|
+
if (image.uri) {
|
|
335
|
+
vtkDebugMacro('Loading texture', image.uri);
|
|
336
|
+
const uri = resolveUrl(image.uri, this.options.baseUri);
|
|
337
|
+
const response = await fetch(uri);
|
|
338
|
+
arrayBuffer = await response.arrayBuffer();
|
|
339
|
+
image.uri = uri;
|
|
340
|
+
image.bufferView = {
|
|
341
|
+
data: arrayBuffer
|
|
342
|
+
};
|
|
343
|
+
} else if (image.bufferView) {
|
|
344
|
+
const bufferView = this.get('bufferViews', image.bufferView);
|
|
345
|
+
buffer = this.get('buffers', bufferView.buffer);
|
|
346
|
+
|
|
347
|
+
// GLB buffer
|
|
348
|
+
if (this.glTF.glbBuffers) {
|
|
349
|
+
buffer = this.glTF.glbBuffers[bufferView.buffer];
|
|
350
|
+
arrayBuffer = buffer.slice(bufferView.byteOffset, bufferView.byteOffset + bufferView.byteLength);
|
|
351
|
+
}
|
|
352
|
+
image.bufferView = {
|
|
353
|
+
data: arrayBuffer
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
export { GLTFParser as default };
|