@kitware/vtk.js 33.0.0-beta.2 → 33.0.0-beta.4
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/DataArray.d.ts +21 -0
- package/Common/Core/DataArray.js +39 -0
- package/Common/Core/Math/index.js +1 -1
- package/Common/Core/Math.js +1 -1
- package/Common/Core/URLExtract.js +2 -6
- package/Common/DataModel/Line.js +1 -0
- package/Common/DataModel/PolyLine.js +4 -0
- package/Filters/Core/ThresholdPoints.d.ts +72 -0
- package/Filters/Core/ThresholdPoints.js +219 -0
- package/Filters/General/ContourTriangulator/helper.js +1 -1
- package/IO/Core/DataAccessHelper/JSZipDataAccessHelper.js +1 -1
- package/IO/Geometry/DracoReader.d.ts +4 -4
- package/IO/Geometry/DracoReader.js +154 -105
- 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 +518 -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/IFCImporter.d.ts +163 -0
- package/IO/Geometry/IFCImporter.js +270 -0
- package/IO/Geometry/STLReader.d.ts +14 -0
- package/IO/Geometry/STLReader.js +57 -1
- package/IO/Geometry.js +5 -1
- package/IO/Image/HDRReader/Utils.js +1 -1
- package/IO/Image/HDRReader.js +1 -1
- package/IO/Image/TGAReader/Constants.js +28 -0
- package/IO/Image/TGAReader.d.ts +121 -0
- package/IO/Image/TGAReader.js +418 -0
- package/IO/Image/TIFFReader.d.ts +133 -0
- package/IO/Image/TIFFReader.js +144 -0
- package/IO/Image.js +5 -1
- package/IO/XML/XMLPolyDataWriter.js +1 -0
- package/Interaction/Manipulators/MouseCameraTrackballRollManipulator.js +1 -1
- package/Interaction/Style/InteractorStyleTrackballCamera.js +1 -1
- package/Rendering/Core/AbstractImageMapper.d.ts +81 -0
- package/Rendering/Core/AbstractImageMapper.js +5 -2
- package/Rendering/Core/AbstractPicker.d.ts +13 -13
- package/Rendering/Core/AbstractPicker.js +1 -1
- package/Rendering/Core/Actor2D.d.ts +22 -0
- package/Rendering/Core/Actor2D.js +1 -1
- package/Rendering/Core/CellPicker.js +4 -1
- package/Rendering/Core/Glyph3DMapper.d.ts +45 -29
- package/Rendering/Core/ImageCPRMapper.js +6 -5
- package/Rendering/Core/ImageProperty.d.ts +42 -1
- package/Rendering/Core/ImageProperty.js +7 -5
- package/Rendering/Core/ImageResliceMapper.d.ts +1 -2
- package/Rendering/Core/ImageResliceMapper.js +5 -4
- package/Rendering/Core/PointPicker.js +10 -1
- package/Rendering/Core/Prop3D.js +1 -1
- package/Rendering/Core/RenderWindowInteractor.d.ts +1 -1
- package/Rendering/Core/RenderWindowInteractor.js +1 -1
- package/Rendering/Core/Viewport.js +13 -3
- package/Rendering/Core/VolumeMapper.d.ts +70 -0
- package/Rendering/Core/VolumeMapper.js +10 -5
- package/Rendering/Core/VolumeProperty.d.ts +20 -1
- package/Rendering/Core/VolumeProperty.js +7 -5
- package/Rendering/Misc/CanvasView.js +4 -2
- package/Rendering/Misc/RemoteView.d.ts +9 -3
- package/Rendering/Misc/RemoteView.js +7 -3
- package/Rendering/Misc/SynchronizableRenderWindow/BehaviorManager/CameraSynchronizer.js +2 -2
- package/Rendering/Misc/SynchronizableRenderWindow/ObjectManager.d.ts +1 -1
- package/Rendering/OpenGL/ImageCPRMapper.js +18 -2
- package/Rendering/OpenGL/ImageMapper.js +42 -11
- package/Rendering/OpenGL/ImageResliceMapper.js +20 -4
- package/Rendering/OpenGL/Renderer.js +1 -1
- package/Rendering/OpenGL/Texture/supportsNorm16Linear.js +97 -0
- package/Rendering/OpenGL/Texture.d.ts +29 -8
- package/Rendering/OpenGL/Texture.js +172 -34
- package/Rendering/OpenGL/VolumeMapper.js +22 -4
- package/Rendering/SceneGraph/ViewNode.js +12 -2
- package/Rendering/WebXR/RenderWindowHelper.js +9 -0
- package/Widgets/Core/WidgetManager.d.ts +12 -1
- package/Widgets/Representations/WidgetRepresentation.d.ts +1 -7
- package/Widgets/Widgets3D/AngleWidget/behavior.js +2 -0
- package/Widgets/Widgets3D/InteractiveOrientationWidget.js +1 -1
- package/Widgets/Widgets3D/ResliceCursorWidget/behavior.js +17 -0
- package/Widgets/Widgets3D/ResliceCursorWidget/helpers.js +1 -0
- package/Widgets/Widgets3D/ResliceCursorWidget.d.ts +1 -8
- package/Widgets/Widgets3D/ShapeWidget/behavior.js +3 -0
- package/_virtual/rollup-plugin-worker-loader__module_Sources/IO/Geometry/GLTFImporter/ORMTexture.worker.js +296 -0
- package/index.d.ts +5 -0
- package/package.json +19 -17
|
@@ -0,0 +1,518 @@
|
|
|
1
|
+
import { m as macro } from '../../../macros2.js';
|
|
2
|
+
import { A as degreesFromRadians } from '../../../Common/Core/Math/index.js';
|
|
3
|
+
import vtkActor from '../../../Rendering/Core/Actor.js';
|
|
4
|
+
import vtkCamera from '../../../Rendering/Core/Camera.js';
|
|
5
|
+
import vtkDataArray from '../../../Common/Core/DataArray.js';
|
|
6
|
+
import vtkPolyData from '../../../Common/DataModel/PolyData.js';
|
|
7
|
+
import vtkMapper from '../../../Rendering/Core/Mapper.js';
|
|
8
|
+
import vtkCellArray from '../../../Common/Core/CellArray.js';
|
|
9
|
+
import vtkTransform from '../../../Common/Transform/Transform.js';
|
|
10
|
+
import GLTFParser from './Parser.js';
|
|
11
|
+
import { ALPHA_MODE, SEMANTIC_ATTRIBUTE_MAP, MODES } from './Constants.js';
|
|
12
|
+
import { loadImage, createVTKTextureFromGLTFTexture } from './Utils.js';
|
|
13
|
+
import { handleKHRMaterialsSpecular, handleKHRMaterialsIor, handleKHRMaterialsUnlit, handleKHRMaterialsVariants, handleKHRLightsPunctual, handleKHRDracoMeshCompression } from './Extensions.js';
|
|
14
|
+
import { mat4, vec3, quat } from 'gl-matrix';
|
|
15
|
+
|
|
16
|
+
const {
|
|
17
|
+
vtkWarningMacro,
|
|
18
|
+
vtkDebugMacro
|
|
19
|
+
} = macro;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Parses a GLTF objects
|
|
23
|
+
* @param {Object} gltf - The GLTF object to parse
|
|
24
|
+
* @returns {glTF} The parsed GLTF object
|
|
25
|
+
*/
|
|
26
|
+
async function parseGLTF(gltf, options) {
|
|
27
|
+
const parser = new GLTFParser(gltf, options);
|
|
28
|
+
const tree = await parser.parse();
|
|
29
|
+
return tree;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Creates VTK polydata from a GLTF mesh primitive
|
|
34
|
+
* @param {GLTFPrimitive} primitive - The GLTF mesh primitive
|
|
35
|
+
* @returns {vtkPolyData} The created VTK polydata
|
|
36
|
+
*/
|
|
37
|
+
async function createPolyDataFromGLTFMesh(primitive) {
|
|
38
|
+
if (!primitive || !primitive.attributes) {
|
|
39
|
+
vtkWarningMacro('Primitive has no position data, skipping');
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
if (primitive.extensions?.KHR_draco_mesh_compression) {
|
|
43
|
+
return handleKHRDracoMeshCompression(primitive.extensions.KHR_draco_mesh_compression);
|
|
44
|
+
}
|
|
45
|
+
const polyData = vtkPolyData.newInstance();
|
|
46
|
+
const cells = vtkCellArray.newInstance();
|
|
47
|
+
const pointData = polyData.getPointData();
|
|
48
|
+
const attrs = Object.entries(primitive.attributes);
|
|
49
|
+
attrs.forEach(async _ref => {
|
|
50
|
+
let [attributeName, accessor] = _ref;
|
|
51
|
+
switch (attributeName) {
|
|
52
|
+
case SEMANTIC_ATTRIBUTE_MAP.POSITION:
|
|
53
|
+
{
|
|
54
|
+
const position = primitive.attributes.position.value;
|
|
55
|
+
polyData.getPoints().setData(position, primitive.attributes.position.component);
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
case SEMANTIC_ATTRIBUTE_MAP.NORMAL:
|
|
59
|
+
{
|
|
60
|
+
const normals = primitive.attributes.normal.value;
|
|
61
|
+
pointData.setNormals(vtkDataArray.newInstance({
|
|
62
|
+
name: 'Normals',
|
|
63
|
+
values: normals,
|
|
64
|
+
numberOfComponents: primitive.attributes.normal.components
|
|
65
|
+
}));
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
case SEMANTIC_ATTRIBUTE_MAP.COLOR_0:
|
|
69
|
+
{
|
|
70
|
+
const color = primitive.attributes.color.value;
|
|
71
|
+
pointData.setScalars(vtkDataArray.newInstance({
|
|
72
|
+
name: 'Scalars',
|
|
73
|
+
values: color,
|
|
74
|
+
numberOfComponents: primitive.attributes.color.components
|
|
75
|
+
}));
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
case SEMANTIC_ATTRIBUTE_MAP.TEXCOORD_0:
|
|
79
|
+
{
|
|
80
|
+
const tcoords0 = primitive.attributes.texcoord0.value;
|
|
81
|
+
const da = vtkDataArray.newInstance({
|
|
82
|
+
name: 'TEXCOORD_0',
|
|
83
|
+
values: tcoords0,
|
|
84
|
+
numberOfComponents: primitive.attributes.texcoord0.components
|
|
85
|
+
});
|
|
86
|
+
pointData.addArray(da);
|
|
87
|
+
pointData.setActiveTCoords(da.getName());
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
case SEMANTIC_ATTRIBUTE_MAP.TEXCOORD_1:
|
|
91
|
+
{
|
|
92
|
+
const tcoords = primitive.attributes.texcoord1.value;
|
|
93
|
+
const dac = vtkDataArray.newInstance({
|
|
94
|
+
name: 'TEXCOORD_1',
|
|
95
|
+
values: tcoords,
|
|
96
|
+
numberOfComponents: primitive.attributes.texcoord1.components
|
|
97
|
+
});
|
|
98
|
+
pointData.addArray(dac);
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
case SEMANTIC_ATTRIBUTE_MAP.TANGENT:
|
|
102
|
+
{
|
|
103
|
+
const tangent = primitive.attributes.tangent.value;
|
|
104
|
+
const dat = vtkDataArray.newInstance({
|
|
105
|
+
name: 'Tangents',
|
|
106
|
+
values: tangent,
|
|
107
|
+
numberOfComponents: primitive.attributes.tangent.components
|
|
108
|
+
});
|
|
109
|
+
pointData.addArray(dat);
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
default:
|
|
113
|
+
vtkWarningMacro(`Unhandled attribute: ${attributeName}`);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Handle indices if available
|
|
118
|
+
if (primitive.indices != null) {
|
|
119
|
+
const indices = primitive.indices.value;
|
|
120
|
+
const nCells = indices.length - 2;
|
|
121
|
+
switch (primitive.mode) {
|
|
122
|
+
case MODES.GL_LINE_STRIP:
|
|
123
|
+
case MODES.GL_TRIANGLE_STRIP:
|
|
124
|
+
case MODES.GL_LINE_LOOP:
|
|
125
|
+
vtkWarningMacro('GL_LINE_LOOP not implemented');
|
|
126
|
+
break;
|
|
127
|
+
default:
|
|
128
|
+
cells.resize(4 * indices.length / 3);
|
|
129
|
+
for (let cellId = 0; cellId < nCells; cellId += 3) {
|
|
130
|
+
const cell = indices.slice(cellId, cellId + 3);
|
|
131
|
+
cells.insertNextCell(cell);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
switch (primitive.mode) {
|
|
136
|
+
case MODES.GL_TRIANGLES:
|
|
137
|
+
case MODES.GL_TRIANGLE_FAN:
|
|
138
|
+
polyData.setPolys(cells);
|
|
139
|
+
break;
|
|
140
|
+
case MODES.GL_LINES:
|
|
141
|
+
case MODES.GL_LINE_STRIP:
|
|
142
|
+
case MODES.GL_LINE_LOOP:
|
|
143
|
+
polyData.setLines(cells);
|
|
144
|
+
break;
|
|
145
|
+
case MODES.GL_POINTS:
|
|
146
|
+
polyData.setVerts(cells);
|
|
147
|
+
break;
|
|
148
|
+
case MODES.GL_TRIANGLE_STRIP:
|
|
149
|
+
polyData.setStrips(cells);
|
|
150
|
+
break;
|
|
151
|
+
default:
|
|
152
|
+
vtkWarningMacro('Invalid primitive draw mode. Ignoring connectivity.');
|
|
153
|
+
}
|
|
154
|
+
return polyData;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Creates a VTK property from a GLTF material
|
|
159
|
+
* @param {object} model - The vtk model object
|
|
160
|
+
* @param {GLTFMaterial} material - The GLTF material
|
|
161
|
+
* @param {vtkActor} actor - The VTK actor
|
|
162
|
+
*/
|
|
163
|
+
async function createPropertyFromGLTFMaterial(model, material, actor) {
|
|
164
|
+
let metallicFactor = 1.0;
|
|
165
|
+
let roughnessFactor = 1.0;
|
|
166
|
+
const emissiveFactor = material.emissiveFactor;
|
|
167
|
+
const property = actor.getProperty();
|
|
168
|
+
const pbr = material.pbrMetallicRoughness;
|
|
169
|
+
if (pbr != null) {
|
|
170
|
+
if (!pbr?.metallicFactor || pbr?.metallicFactor <= 0 || pbr?.metallicFactor >= 1) {
|
|
171
|
+
vtkDebugMacro('Invalid material.pbrMetallicRoughness.metallicFactor value. Using default value instead.');
|
|
172
|
+
} else metallicFactor = pbr.metallicFactor;
|
|
173
|
+
if (!pbr?.roughnessFactor || pbr?.roughnessFactor <= 0 || pbr?.roughnessFactor >= 1) {
|
|
174
|
+
vtkDebugMacro('Invalid material.pbrMetallicRoughness.roughnessFactor value. Using default value instead.');
|
|
175
|
+
} else roughnessFactor = pbr.roughnessFactor;
|
|
176
|
+
const color = pbr.baseColorFactor;
|
|
177
|
+
if (color != null) {
|
|
178
|
+
property.setDiffuseColor(color[0], color[1], color[2]);
|
|
179
|
+
property.setOpacity(color[3]);
|
|
180
|
+
}
|
|
181
|
+
property.setMetallic(metallicFactor);
|
|
182
|
+
property.setRoughness(roughnessFactor);
|
|
183
|
+
property.setEmission(emissiveFactor);
|
|
184
|
+
if (pbr.baseColorTexture) {
|
|
185
|
+
pbr.baseColorTexture.extensions;
|
|
186
|
+
const tex = pbr.baseColorTexture.texture;
|
|
187
|
+
if (tex.extensions != null) {
|
|
188
|
+
const extensionsNames = Object.keys(tex.extensions);
|
|
189
|
+
extensionsNames.forEach(extensionName => {
|
|
190
|
+
// TODO: Handle KHR_texture_basisu extension
|
|
191
|
+
// const extension = tex.extensions[extensionName];
|
|
192
|
+
switch (extensionName) {
|
|
193
|
+
default:
|
|
194
|
+
vtkWarningMacro(`Unhandled extension: ${extensionName}`);
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
const sampler = tex.sampler;
|
|
199
|
+
const image = await loadImage(tex.source);
|
|
200
|
+
const diffuseTex = createVTKTextureFromGLTFTexture(image, sampler);
|
|
201
|
+
|
|
202
|
+
// FIXME: Workaround for textures not showing up in WebGL
|
|
203
|
+
const viewAPI = model.renderer.getRenderWindow();
|
|
204
|
+
const isWebGL = viewAPI.getViews()[0].isA('vtkOpenGLRenderWindow');
|
|
205
|
+
if (isWebGL) {
|
|
206
|
+
actor.addTexture(diffuseTex);
|
|
207
|
+
} else {
|
|
208
|
+
property.setDiffuseTexture(diffuseTex);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
if (pbr.metallicRoughnessTexture) {
|
|
212
|
+
pbr.metallicRoughnessTexture.extensions;
|
|
213
|
+
const tex = pbr.metallicRoughnessTexture.texture;
|
|
214
|
+
const sampler = tex.sampler;
|
|
215
|
+
const metallicImage = await loadImage(tex.source, 'b');
|
|
216
|
+
const metallicTex = createVTKTextureFromGLTFTexture(metallicImage, sampler);
|
|
217
|
+
property.setMetallicTexture(metallicTex);
|
|
218
|
+
const roughnessImage = await loadImage(tex.source, 'g');
|
|
219
|
+
const roughnessTex = createVTKTextureFromGLTFTexture(roughnessImage, sampler);
|
|
220
|
+
property.setRoughnessTexture(roughnessTex);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Handle ambient occlusion texture (occlusionTexture)
|
|
224
|
+
if (material.occlusionTexture) {
|
|
225
|
+
material.occlusionTexture.extensions;
|
|
226
|
+
const tex = material.occlusionTexture.texture;
|
|
227
|
+
const sampler = tex.sampler;
|
|
228
|
+
const aoImage = await loadImage(tex.source, 'r');
|
|
229
|
+
const aoTex = createVTKTextureFromGLTFTexture(aoImage, sampler);
|
|
230
|
+
property.setAmbientOcclusionTexture(aoTex);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Handle emissive texture (emissiveTexture)
|
|
234
|
+
if (material.emissiveTexture) {
|
|
235
|
+
material.emissiveTexture.extensions;
|
|
236
|
+
const tex = material.emissiveTexture.texture;
|
|
237
|
+
const sampler = tex.sampler;
|
|
238
|
+
const emissiveImage = await loadImage(tex.source);
|
|
239
|
+
const emissiveTex = createVTKTextureFromGLTFTexture(emissiveImage, sampler);
|
|
240
|
+
property.setEmissionTexture(emissiveTex);
|
|
241
|
+
|
|
242
|
+
// Handle mutiple Uvs
|
|
243
|
+
if (material.emissiveTexture.texCoord != null) {
|
|
244
|
+
const pd = actor.getMapper().getInputData().getPointData();
|
|
245
|
+
pd.setActiveTCoords(`TEXCOORD_${material.emissiveTexture.texCoord}`);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Handle normal texture (normalTexture)
|
|
250
|
+
if (material.normalTexture) {
|
|
251
|
+
material.normalTexture.extensions;
|
|
252
|
+
const tex = material.normalTexture.texture;
|
|
253
|
+
const sampler = tex.sampler;
|
|
254
|
+
const normalImage = await loadImage(tex.source);
|
|
255
|
+
const normalTex = createVTKTextureFromGLTFTexture(normalImage, sampler);
|
|
256
|
+
property.setNormalTexture(normalTex);
|
|
257
|
+
if (material.normalTexture.scale != null) {
|
|
258
|
+
property.setNormalStrength(material.normalTexture.scale);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Material extensions
|
|
264
|
+
if (material.extensions != null) {
|
|
265
|
+
const extensionsNames = Object.keys(material.extensions);
|
|
266
|
+
extensionsNames.forEach(extensionName => {
|
|
267
|
+
const extension = material.extensions[extensionName];
|
|
268
|
+
switch (extensionName) {
|
|
269
|
+
case 'KHR_materials_unlit':
|
|
270
|
+
handleKHRMaterialsUnlit(extension, property);
|
|
271
|
+
break;
|
|
272
|
+
case 'KHR_materials_ior':
|
|
273
|
+
handleKHRMaterialsIor(extension, property);
|
|
274
|
+
break;
|
|
275
|
+
case 'KHR_materials_specular':
|
|
276
|
+
handleKHRMaterialsSpecular(extension, property);
|
|
277
|
+
break;
|
|
278
|
+
default:
|
|
279
|
+
vtkWarningMacro(`Unhandled extension: ${extensionName}`);
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
if (material.alphaMode !== ALPHA_MODE.OPAQUE) {
|
|
284
|
+
actor.setForceTranslucent(true);
|
|
285
|
+
}
|
|
286
|
+
property.setBackfaceCulling(!material.doubleSided);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Handles primitive extensions
|
|
291
|
+
* @param {string} nodeId The GLTF node id
|
|
292
|
+
* @param {*} extensions The extensions object
|
|
293
|
+
* @param {*} model The vtk model object
|
|
294
|
+
*/
|
|
295
|
+
function handlePrimitiveExtensions(nodeId, extensions, model) {
|
|
296
|
+
const extensionsNames = Object.keys(extensions);
|
|
297
|
+
extensionsNames.forEach(extensionName => {
|
|
298
|
+
const extension = extensions[extensionName];
|
|
299
|
+
switch (extensionName) {
|
|
300
|
+
case 'KHR_materials_variants':
|
|
301
|
+
model.variantMappings.set(nodeId, extension.mappings);
|
|
302
|
+
break;
|
|
303
|
+
default:
|
|
304
|
+
vtkWarningMacro(`Unhandled extension: ${extensionName}`);
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Creates a VTK actor from a GLTF mesh
|
|
311
|
+
* @param {GLTFMesh} mesh - The GLTF mesh
|
|
312
|
+
* @returns {vtkActor} The created VTK actor
|
|
313
|
+
*/
|
|
314
|
+
async function createActorFromGTLFNode(worldMatrix) {
|
|
315
|
+
const actor = vtkActor.newInstance();
|
|
316
|
+
const mapper = vtkMapper.newInstance();
|
|
317
|
+
mapper.setColorModeToDirectScalars();
|
|
318
|
+
actor.setMapper(mapper);
|
|
319
|
+
actor.setUserMatrix(worldMatrix);
|
|
320
|
+
const polydata = vtkPolyData.newInstance();
|
|
321
|
+
mapper.setInputData(polydata);
|
|
322
|
+
return actor;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Creates a VTK actor from a GLTF mesh
|
|
327
|
+
* @param {GLTFMesh} mesh - The GLTF mesh
|
|
328
|
+
* @returns {vtkActor} The created VTK actor
|
|
329
|
+
*/
|
|
330
|
+
async function createActorFromGTLFPrimitive(model, primitive, worldMatrix) {
|
|
331
|
+
const actor = vtkActor.newInstance();
|
|
332
|
+
const mapper = vtkMapper.newInstance();
|
|
333
|
+
mapper.setColorModeToDirectScalars();
|
|
334
|
+
actor.setMapper(mapper);
|
|
335
|
+
actor.setUserMatrix(worldMatrix);
|
|
336
|
+
const polydata = await createPolyDataFromGLTFMesh(primitive);
|
|
337
|
+
mapper.setInputData(polydata);
|
|
338
|
+
|
|
339
|
+
// Support for materials
|
|
340
|
+
if (primitive.material != null) {
|
|
341
|
+
await createPropertyFromGLTFMaterial(model, primitive.material, actor);
|
|
342
|
+
}
|
|
343
|
+
if (primitive.extensions != null) {
|
|
344
|
+
handlePrimitiveExtensions(`${primitive.name}`, primitive.extensions, model);
|
|
345
|
+
}
|
|
346
|
+
return actor;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Creates a GLTF animation object
|
|
351
|
+
* @param {GLTFAnimation} animation
|
|
352
|
+
* @returns
|
|
353
|
+
*/
|
|
354
|
+
function createGLTFAnimation(animation) {
|
|
355
|
+
vtkDebugMacro('Creating animation:', animation);
|
|
356
|
+
return {
|
|
357
|
+
name: animation.name,
|
|
358
|
+
channels: animation.channels,
|
|
359
|
+
samplers: animation.samplers,
|
|
360
|
+
getChannelByTargetNode(nodeIndex) {
|
|
361
|
+
return this.channels.filter(channel => channel.target.node === nodeIndex);
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Gets the transformation matrix for a GLTF node
|
|
368
|
+
* @param {GLTFNode} node - The GLTF node
|
|
369
|
+
* @returns {mat4} The transformation matrix
|
|
370
|
+
*/
|
|
371
|
+
function getTransformationMatrix(node) {
|
|
372
|
+
// TRS
|
|
373
|
+
const translation = node.translation ?? vec3.create();
|
|
374
|
+
const rotation = node.rotation ?? quat.create();
|
|
375
|
+
const scale = node.scale ?? vec3.fromValues(1.0, 1.0, 1.0);
|
|
376
|
+
const matrix = node.matrix != null ? mat4.clone(node.matrix) : mat4.fromRotationTranslationScale(mat4.create(), rotation, translation, scale);
|
|
377
|
+
return matrix;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Processes a GLTF node
|
|
382
|
+
* @param {GLTFnode} node - The GLTF node
|
|
383
|
+
* @param {object} model The model object
|
|
384
|
+
* @param {vtkActor} parentActor The parent actor
|
|
385
|
+
* @param {mat4} parentMatrix The parent matrix
|
|
386
|
+
*/
|
|
387
|
+
async function processNode(node, model) {
|
|
388
|
+
let parentActor = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
|
|
389
|
+
let parentMatrix = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : mat4.create();
|
|
390
|
+
node.transform = getTransformationMatrix(node);
|
|
391
|
+
const worldMatrix = mat4.multiply(mat4.create(), parentMatrix, node.transform);
|
|
392
|
+
|
|
393
|
+
// Create actor for the current node
|
|
394
|
+
if (node.mesh != null) {
|
|
395
|
+
const nodeActor = await createActorFromGTLFNode(worldMatrix);
|
|
396
|
+
if (parentActor) {
|
|
397
|
+
nodeActor.setParentProp(parentActor);
|
|
398
|
+
}
|
|
399
|
+
model.actors.set(`${node.id}`, nodeActor);
|
|
400
|
+
await Promise.all(node.mesh.primitives.map(async (primitive, i) => {
|
|
401
|
+
const actor = await createActorFromGTLFPrimitive(model, primitive, worldMatrix);
|
|
402
|
+
actor.setParentProp(nodeActor);
|
|
403
|
+
model.actors.set(`${node.id}_${primitive.name}`, actor);
|
|
404
|
+
}));
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// Handle KHRLightsPunctual extension
|
|
408
|
+
if (node.extensions?.KHR_lights_punctual) {
|
|
409
|
+
handleKHRLightsPunctual(node.extensions.KHR_lights_punctual, node.transform, model);
|
|
410
|
+
}
|
|
411
|
+
if (node.children && Array.isArray(node.children) && node.children.length > 0) {
|
|
412
|
+
await Promise.all(node.children.map(async child => {
|
|
413
|
+
const parent = model.actors.get(node.id);
|
|
414
|
+
await processNode(child, model, parent, worldMatrix);
|
|
415
|
+
}));
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Creates VTK actors from a GLTF object
|
|
421
|
+
* @param {glTF} glTF - The GLTF object
|
|
422
|
+
* @param {number} sceneId - The scene index to create actors for
|
|
423
|
+
* @returns {vtkActor[]} The created VTK actors
|
|
424
|
+
*/
|
|
425
|
+
async function createVTKObjects(model) {
|
|
426
|
+
model.animations = model.glTFTree.animations?.map(createGLTFAnimation);
|
|
427
|
+
const extensionsNames = Object.keys(model.glTFTree?.extensions || []);
|
|
428
|
+
extensionsNames.forEach(extensionName => {
|
|
429
|
+
const extension = model.glTFTree.extensions[extensionName];
|
|
430
|
+
switch (extensionName) {
|
|
431
|
+
case 'KHR_materials_variants':
|
|
432
|
+
handleKHRMaterialsVariants(extension, model);
|
|
433
|
+
break;
|
|
434
|
+
case 'KHR_draco_mesh_compression':
|
|
435
|
+
break;
|
|
436
|
+
default:
|
|
437
|
+
vtkWarningMacro(`Unhandled extension: ${extensionName}`);
|
|
438
|
+
}
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
// Get the sceneId to process
|
|
442
|
+
const sceneId = model.sceneId ?? model.glTFTree.scene;
|
|
443
|
+
if (model.glTFTree.scenes?.length && model.glTFTree.scenes[sceneId]?.nodes) {
|
|
444
|
+
await Promise.all(model.glTFTree.scenes[sceneId].nodes.map(async node => {
|
|
445
|
+
if (node) {
|
|
446
|
+
await processNode(node, model);
|
|
447
|
+
} else {
|
|
448
|
+
vtkWarningMacro(`Node not found in glTF.nodes`);
|
|
449
|
+
}
|
|
450
|
+
}));
|
|
451
|
+
} else {
|
|
452
|
+
vtkWarningMacro('No valid scenes found in the glTF data');
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Sets up the camera for a vtk renderer based on the bounds of the given actors.
|
|
458
|
+
*
|
|
459
|
+
* @param {GLTCamera} camera - The GLTF camera object
|
|
460
|
+
*/
|
|
461
|
+
function GLTFCameraToVTKCamera(glTFCamera) {
|
|
462
|
+
const camera = vtkCamera.newInstance();
|
|
463
|
+
if (glTFCamera.type === 'perspective') {
|
|
464
|
+
const {
|
|
465
|
+
yfov,
|
|
466
|
+
znear,
|
|
467
|
+
zfar
|
|
468
|
+
} = glTFCamera.perspective;
|
|
469
|
+
camera.setClippingRange(znear, zfar);
|
|
470
|
+
camera.setParallelProjection(false);
|
|
471
|
+
camera.setViewAngle(degreesFromRadians(yfov));
|
|
472
|
+
} else if (glTFCamera.type === 'orthographic') {
|
|
473
|
+
const {
|
|
474
|
+
ymag,
|
|
475
|
+
znear,
|
|
476
|
+
zfar
|
|
477
|
+
} = glTFCamera.orthographic;
|
|
478
|
+
camera.setClippingRange(znear, zfar);
|
|
479
|
+
camera.setParallelProjection(true);
|
|
480
|
+
camera.setParallelScale(ymag);
|
|
481
|
+
} else {
|
|
482
|
+
throw new Error('Unsupported camera type');
|
|
483
|
+
}
|
|
484
|
+
return camera;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
*
|
|
489
|
+
* @param {vtkCamera} camera
|
|
490
|
+
* @param {*} transformMatrix
|
|
491
|
+
*/
|
|
492
|
+
function applyTransformToCamera(camera, transformMatrix) {
|
|
493
|
+
if (!camera || !transformMatrix) {
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// At identity, camera position is origin, +y up, -z view direction
|
|
498
|
+
const position = [0, 0, 0];
|
|
499
|
+
const viewUp = [0, 1, 0];
|
|
500
|
+
const focus = [0, 0, -1];
|
|
501
|
+
const t = vtkTransform.newInstance();
|
|
502
|
+
t.setMatrix(transformMatrix);
|
|
503
|
+
|
|
504
|
+
// Transform position
|
|
505
|
+
t.transformPoint(position, position);
|
|
506
|
+
t.transformPoints(viewUp, viewUp);
|
|
507
|
+
t.transformPoints(focus, focus);
|
|
508
|
+
focus[0] += position[0];
|
|
509
|
+
focus[1] += position[1];
|
|
510
|
+
focus[2] += position[2];
|
|
511
|
+
|
|
512
|
+
// Apply the transformed values to the camera
|
|
513
|
+
camera.setPosition(position);
|
|
514
|
+
camera.setFocalPoint(focus);
|
|
515
|
+
camera.setViewUp(viewUp);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
export { GLTFCameraToVTKCamera, applyTransformToCamera, createPropertyFromGLTFMaterial, createVTKObjects, parseGLTF };
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import WebworkerPromise from 'webworker-promise';
|
|
2
|
+
import { m as macro } from '../../../macros2.js';
|
|
3
|
+
import vtkTexture from '../../../Rendering/Core/Texture.js';
|
|
4
|
+
import { W as WorkerFactory } from '../../../_virtual/rollup-plugin-worker-loader__module_Sources/IO/Geometry/GLTFImporter/ORMTexture.worker.js';
|
|
5
|
+
import { ARRAY_TYPES, COMPONENTS, BYTES, GL_SAMPLER } from './Constants.js';
|
|
6
|
+
|
|
7
|
+
const {
|
|
8
|
+
vtkWarningMacro,
|
|
9
|
+
vtkErrorMacro
|
|
10
|
+
} = macro;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Get GL enum from sampler parameter
|
|
14
|
+
* @param {*} parameter The sampler parameter
|
|
15
|
+
* @returns The GL enum
|
|
16
|
+
*/
|
|
17
|
+
function getGLEnumFromSamplerParameter(parameter) {
|
|
18
|
+
const GL_TEXTURE_MAG_FILTER = 0x2800;
|
|
19
|
+
const GL_TEXTURE_MIN_FILTER = 0x2801;
|
|
20
|
+
const GL_TEXTURE_WRAP_S = 0x2802;
|
|
21
|
+
const GL_TEXTURE_WRAP_T = 0x2803;
|
|
22
|
+
const Mapping = {
|
|
23
|
+
magFilter: GL_TEXTURE_MAG_FILTER,
|
|
24
|
+
minFilter: GL_TEXTURE_MIN_FILTER,
|
|
25
|
+
wrapS: GL_TEXTURE_WRAP_S,
|
|
26
|
+
wrapT: GL_TEXTURE_WRAP_T
|
|
27
|
+
};
|
|
28
|
+
return Mapping[parameter];
|
|
29
|
+
}
|
|
30
|
+
function getAccessorArrayTypeAndLength(accessor, bufferView) {
|
|
31
|
+
const ArrayType = ARRAY_TYPES[accessor.componentType];
|
|
32
|
+
const components = COMPONENTS[accessor.type];
|
|
33
|
+
const bytesPerComponent = BYTES[accessor.componentType];
|
|
34
|
+
const length = accessor.count * components;
|
|
35
|
+
const byteLength = accessor.count * components * bytesPerComponent;
|
|
36
|
+
return {
|
|
37
|
+
ArrayType,
|
|
38
|
+
length,
|
|
39
|
+
byteLength
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Resolves a URL based on the original path
|
|
45
|
+
* @param {*} url The URL to resolve
|
|
46
|
+
* @param {*} originalPath The original path to resolve the URL against
|
|
47
|
+
* @returns The resolved URL or an empty string if the URL is invalid
|
|
48
|
+
*/
|
|
49
|
+
function resolveUrl(url, originalPath) {
|
|
50
|
+
// Invalid URL
|
|
51
|
+
if (typeof url !== 'string' || url === '') return '';
|
|
52
|
+
try {
|
|
53
|
+
// Data URI
|
|
54
|
+
if (url.startsWith('data:')) return url;
|
|
55
|
+
|
|
56
|
+
// Blob URL
|
|
57
|
+
if (url.startsWith('blob:')) return url;
|
|
58
|
+
|
|
59
|
+
// Create URL object from the original path
|
|
60
|
+
const baseUrl = new URL(originalPath);
|
|
61
|
+
if (!baseUrl.pathname.includes('.') && !baseUrl.pathname.endsWith('/')) {
|
|
62
|
+
baseUrl.pathname += '/';
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Absolute URL (http://, https://, //)
|
|
66
|
+
if (url.startsWith('http:') || url.startsWith('https:') || url.startsWith('//')) {
|
|
67
|
+
return new URL(url, baseUrl).href;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Host Relative URL
|
|
71
|
+
if (url.startsWith('/')) {
|
|
72
|
+
return new URL(url, baseUrl).href;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Relative URL
|
|
76
|
+
return new URL(url, baseUrl).href;
|
|
77
|
+
} catch (error) {
|
|
78
|
+
vtkErrorMacro('Error resolving URL:', error);
|
|
79
|
+
return '';
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Loads image from buffer or URI
|
|
85
|
+
* @param {*} image
|
|
86
|
+
* @param {*} channel
|
|
87
|
+
* @returns
|
|
88
|
+
*/
|
|
89
|
+
async function loadImage(image, channel) {
|
|
90
|
+
let forceReLoad = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
|
|
91
|
+
// Initialize cache if it doesn't exist
|
|
92
|
+
if (!image.cache) {
|
|
93
|
+
image.cache = {};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Return cached result for the channel if available and not forced to reload
|
|
97
|
+
if (!forceReLoad && image.cache[channel]) {
|
|
98
|
+
return image.cache[channel];
|
|
99
|
+
}
|
|
100
|
+
const worker = new WebworkerPromise(new WorkerFactory());
|
|
101
|
+
if (image.bufferView) {
|
|
102
|
+
return worker.postMessage({
|
|
103
|
+
imageBuffer: image.bufferView.data,
|
|
104
|
+
mimeType: image.mimeType,
|
|
105
|
+
channel
|
|
106
|
+
}).then(result => {
|
|
107
|
+
// Cache the bitmap based on the channel
|
|
108
|
+
image.cache[channel] = result.bitmap;
|
|
109
|
+
return result.bitmap;
|
|
110
|
+
}).finally(() => {
|
|
111
|
+
worker.terminate();
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
if (image.uri) {
|
|
115
|
+
vtkWarningMacro('Falling back to image uri', image.uri);
|
|
116
|
+
return new Promise((resolve, reject) => {
|
|
117
|
+
const img = new Image();
|
|
118
|
+
img.crossOrigin = 'Anonymous';
|
|
119
|
+
img.onload = () => {
|
|
120
|
+
image.cache[channel] = img; // Cache the loaded image based on the channel
|
|
121
|
+
resolve(img);
|
|
122
|
+
};
|
|
123
|
+
img.onerror = reject;
|
|
124
|
+
img.src = image.uri;
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
*
|
|
132
|
+
* @param {*} image
|
|
133
|
+
* @param {*} sampler
|
|
134
|
+
* @param {*} extensions
|
|
135
|
+
* @returns
|
|
136
|
+
*/
|
|
137
|
+
function createVTKTextureFromGLTFTexture(image, sampler, extensions) {
|
|
138
|
+
const texture = vtkTexture.newInstance();
|
|
139
|
+
// Apply sampler settings
|
|
140
|
+
if (sampler) {
|
|
141
|
+
if ('wrapS' in sampler && 'wrapT' in sampler || 'minFilter' in sampler && 'magFilter' in sampler) {
|
|
142
|
+
if (sampler.wrapS === GL_SAMPLER.CLAMP_TO_EDGE || sampler.wrapT === GL_SAMPLER.CLAMP_TO_EDGE) {
|
|
143
|
+
texture.setRepeat(false);
|
|
144
|
+
texture.setEdgeClamp(true);
|
|
145
|
+
} else if (sampler.wrapS === GL_SAMPLER.REPEAT || sampler.wrapT === GL_SAMPLER.REPEAT) {
|
|
146
|
+
texture.setRepeat(true);
|
|
147
|
+
texture.setEdgeClamp(false);
|
|
148
|
+
} else {
|
|
149
|
+
vtkWarningMacro('Mirrored texture wrapping is not supported!');
|
|
150
|
+
}
|
|
151
|
+
const linearFilters = [GL_SAMPLER.LINEAR, GL_SAMPLER.LINEAR_MIPMAP_NEAREST, GL_SAMPLER.NEAREST_MIPMAP_LINEAR, GL_SAMPLER.LINEAR_MIPMAP_LINEAR];
|
|
152
|
+
if (linearFilters.includes(sampler.minFilter) || linearFilters.includes(sampler.magFilter)) {
|
|
153
|
+
texture.setInterpolate(true);
|
|
154
|
+
}
|
|
155
|
+
} else {
|
|
156
|
+
texture.setMipLevel(8);
|
|
157
|
+
texture.setInterpolate(true);
|
|
158
|
+
texture.setEdgeClamp(true);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
texture.setJsImageData(image);
|
|
162
|
+
return texture;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export { createVTKTextureFromGLTFTexture, getAccessorArrayTypeAndLength, getGLEnumFromSamplerParameter, loadImage, resolveUrl };
|