@kitware/vtk.js 33.0.0-beta.2 → 33.0.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Common/Core/DataArray.d.ts +4 -0
- package/Common/Core/DataArray.js +3 -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/Glyph3DMapper.d.ts +45 -29
- package/Rendering/Core/ImageCPRMapper.js +1 -1
- package/Rendering/Core/ImageProperty.d.ts +22 -0
- 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/Misc/CanvasView.js +4 -2
- package/Rendering/Misc/RemoteView.d.ts +9 -3
- package/Rendering/Misc/RemoteView.js +7 -3
- package/Rendering/Misc/SynchronizableRenderWindow/ObjectManager.d.ts +1 -1
- package/Rendering/OpenGL/ImageMapper.js +14 -7
- package/Rendering/OpenGL/Texture/supportsNorm16Linear.js +97 -0
- package/Rendering/OpenGL/Texture.js +18 -11
- 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/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 +12 -10
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import DataAccessHelper from '../Core/DataAccessHelper.js';
|
|
2
1
|
import { m as macro } from '../../macros2.js';
|
|
2
|
+
import DataAccessHelper from '../Core/DataAccessHelper.js';
|
|
3
3
|
import vtkCellArray from '../../Common/Core/CellArray.js';
|
|
4
4
|
import vtkDataArray from '../../Common/Core/DataArray.js';
|
|
5
5
|
import vtkPolyData from '../../Common/DataModel/PolyData.js';
|
|
6
|
+
import vtkPolyDataNormals from '../../Filters/Core/PolyDataNormals.js';
|
|
6
7
|
import '../Core/DataAccessHelper/LiteHttpDataAccessHelper.js';
|
|
7
8
|
|
|
8
9
|
// import 'vtk.js/Sources/IO/Core/DataAccessHelper/HttpDataAccessHelper'; // HTTP + zip
|
|
@@ -12,8 +13,7 @@ import '../Core/DataAccessHelper/LiteHttpDataAccessHelper.js';
|
|
|
12
13
|
const {
|
|
13
14
|
vtkErrorMacro
|
|
14
15
|
} = macro;
|
|
15
|
-
let decoderModule =
|
|
16
|
-
|
|
16
|
+
let decoderModule = null;
|
|
17
17
|
// ----------------------------------------------------------------------------
|
|
18
18
|
// static methods
|
|
19
19
|
// ----------------------------------------------------------------------------
|
|
@@ -46,8 +46,13 @@ function setWasmBinary(url, binaryName) {
|
|
|
46
46
|
xhr.send(null);
|
|
47
47
|
});
|
|
48
48
|
}
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Set the Draco decoder module
|
|
52
|
+
* @param {*} dracoDecoder
|
|
53
|
+
*/
|
|
54
|
+
async function setDracoDecoder(dracoDecoder) {
|
|
55
|
+
decoderModule = await dracoDecoder({});
|
|
51
56
|
}
|
|
52
57
|
function getDracoDecoder() {
|
|
53
58
|
return decoderModule;
|
|
@@ -56,115 +61,141 @@ function getDracoDecoder() {
|
|
|
56
61
|
// ----------------------------------------------------------------------------
|
|
57
62
|
// vtkDracoReader methods
|
|
58
63
|
// ----------------------------------------------------------------------------
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
64
|
+
function getDracoDataType(attributeType) {
|
|
65
|
+
switch (attributeType) {
|
|
66
|
+
case Float32Array:
|
|
67
|
+
return decoderModule.DT_FLOAT32;
|
|
68
|
+
case Int8Array:
|
|
69
|
+
return decoderModule.DT_INT8;
|
|
70
|
+
case Int16Array:
|
|
71
|
+
return decoderModule.DT_INT16;
|
|
72
|
+
case Int32Array:
|
|
73
|
+
return decoderModule.DT_INT32;
|
|
74
|
+
case Uint8Array:
|
|
75
|
+
return decoderModule.DT_UINT8;
|
|
76
|
+
case Uint16Array:
|
|
77
|
+
return decoderModule.DT_UINT16;
|
|
78
|
+
case Uint32Array:
|
|
79
|
+
return decoderModule.DT_UINT32;
|
|
80
|
+
default:
|
|
81
|
+
return decoderModule.DT_FLOAT32;
|
|
75
82
|
}
|
|
76
|
-
decoderModule.destroy(decoderBuffer);
|
|
77
|
-
decoderModule.destroy(decoder);
|
|
78
|
-
return dracoGeometry;
|
|
79
83
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Decode a single attribute
|
|
87
|
+
* @param {*} decoder The Draco decoder
|
|
88
|
+
* @param {*} dracoGeometry The geometry to decode
|
|
89
|
+
* @param {*} attributeName The name of the attribute
|
|
90
|
+
* @param {*} attributeType The type of the attribute
|
|
91
|
+
* @param {*} attribute The attribute to decode
|
|
92
|
+
* @returns object with name, array, itemSize
|
|
93
|
+
*/
|
|
94
|
+
function decodeAttribute(decoder, dracoGeometry, attributeName, attributeType, attribute) {
|
|
95
|
+
const numComponents = attribute.num_components();
|
|
96
|
+
const numPoints = dracoGeometry.num_points();
|
|
97
|
+
const numValues = numPoints * numComponents;
|
|
98
|
+
const byteLength = numValues * attributeType.BYTES_PER_ELEMENT;
|
|
99
|
+
const dataType = getDracoDataType(attributeType);
|
|
100
|
+
const ptr = decoderModule._malloc(byteLength);
|
|
101
|
+
decoder.GetAttributeDataArrayForAllPoints(dracoGeometry, attribute, dataType, byteLength, ptr);
|
|
102
|
+
|
|
103
|
+
// eslint-disable-next-line new-cap
|
|
104
|
+
const array = new attributeType(decoderModule.HEAPF32.buffer, ptr, numValues).slice();
|
|
105
|
+
decoderModule._free(ptr);
|
|
106
|
+
return {
|
|
107
|
+
name: attributeName,
|
|
108
|
+
array,
|
|
109
|
+
itemSize: numComponents
|
|
110
|
+
};
|
|
93
111
|
}
|
|
94
|
-
function getPolyDataFromDracoGeometry(dracoGeometry) {
|
|
95
|
-
const decoder = new decoderModule.Decoder();
|
|
96
112
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const
|
|
113
|
+
/**
|
|
114
|
+
* Decode the indices of the geometry
|
|
115
|
+
* @param {*} decoder The Draco decoder
|
|
116
|
+
* @param {*} dracoGeometry The geometry to decode
|
|
117
|
+
* @returns The indices array of the geometry
|
|
118
|
+
*/
|
|
119
|
+
function decodeIndices(decoder, dracoGeometry) {
|
|
120
|
+
const numFaces = dracoGeometry.num_faces();
|
|
121
|
+
const numIndices = numFaces * 3;
|
|
122
|
+
const byteLength = numIndices * 4;
|
|
123
|
+
const ptr = decoderModule._malloc(byteLength);
|
|
124
|
+
decoder.GetTrianglesUInt32Array(dracoGeometry, byteLength, ptr);
|
|
125
|
+
const indices = new Uint32Array(decoderModule.HEAPF32.buffer, ptr, numIndices).slice();
|
|
126
|
+
decoderModule._free(ptr);
|
|
127
|
+
return indices;
|
|
128
|
+
}
|
|
106
129
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
130
|
+
/**
|
|
131
|
+
* Get the polyData from the Draco geometry
|
|
132
|
+
* @param {*} decoder The Draco decoder
|
|
133
|
+
* @param {*} dracoGeometry The geometry to decode
|
|
134
|
+
* @returns {vtkPolyData} The polyData of the geometry
|
|
135
|
+
*/
|
|
136
|
+
function getPolyDataFromDracoGeometry(decoder, dracoGeometry) {
|
|
137
|
+
const indices = decodeIndices(decoder, dracoGeometry);
|
|
138
|
+
const nCells = indices.length - 2;
|
|
139
|
+
const cells = vtkCellArray.newInstance();
|
|
140
|
+
cells.resize(4 * indices.length / 3);
|
|
141
|
+
for (let cellId = 0; cellId < nCells; cellId += 3) {
|
|
142
|
+
const cell = indices.slice(cellId, cellId + 3);
|
|
143
|
+
cells.insertNextCell(cell);
|
|
118
144
|
}
|
|
119
|
-
|
|
120
|
-
// Create polyData and add positions and indinces
|
|
121
|
-
const cellArray = vtkCellArray.newInstance({
|
|
122
|
-
values: indices
|
|
123
|
-
});
|
|
124
145
|
const polyData = vtkPolyData.newInstance({
|
|
125
|
-
polys:
|
|
146
|
+
polys: cells
|
|
126
147
|
});
|
|
127
|
-
polyData.getPoints().setData(positionArray);
|
|
128
|
-
|
|
129
|
-
// Look for other attributes
|
|
130
|
-
const pointData = polyData.getPointData();
|
|
131
148
|
|
|
132
|
-
//
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
149
|
+
// Look for attributes
|
|
150
|
+
const attributeIDs = {
|
|
151
|
+
points: 'POSITION',
|
|
152
|
+
normals: 'NORMAL',
|
|
153
|
+
scalars: 'COLOR',
|
|
154
|
+
tcoords: 'TEX_COORD'
|
|
155
|
+
};
|
|
156
|
+
Object.keys(attributeIDs).forEach(attributeName => {
|
|
157
|
+
const attributeType = Float32Array;
|
|
158
|
+
const attributeID = decoder.GetAttributeId(dracoGeometry, decoderModule[attributeIDs[attributeName]]);
|
|
159
|
+
if (attributeID === -1) return;
|
|
160
|
+
const attribute = decoder.GetAttribute(dracoGeometry, attributeID);
|
|
161
|
+
const attributeResult = decodeAttribute(decoder, dracoGeometry, attributeName, attributeType, attribute);
|
|
162
|
+
const pointData = polyData.getPointData();
|
|
163
|
+
switch (attributeName) {
|
|
164
|
+
case 'points':
|
|
165
|
+
polyData.getPoints().setData(attributeResult.array, attributeResult.itemSize);
|
|
166
|
+
break;
|
|
167
|
+
case 'normals':
|
|
168
|
+
pointData.setNormals(vtkDataArray.newInstance({
|
|
169
|
+
numberOfComponents: attributeResult.itemSize,
|
|
170
|
+
values: attributeResult.array,
|
|
171
|
+
name: 'Normals'
|
|
172
|
+
}));
|
|
173
|
+
break;
|
|
174
|
+
case 'scalars':
|
|
175
|
+
pointData.setScalars(vtkDataArray.newInstance({
|
|
176
|
+
numberOfComponents: attributeResult.itemSize,
|
|
177
|
+
values: attributeResult.array,
|
|
178
|
+
name: 'Scalars'
|
|
179
|
+
}));
|
|
180
|
+
break;
|
|
181
|
+
case 'tcoords':
|
|
182
|
+
pointData.setTCoords(vtkDataArray.newInstance({
|
|
183
|
+
numberOfComponents: attributeResult.itemSize,
|
|
184
|
+
values: attributeResult.array,
|
|
185
|
+
name: 'TCoords'
|
|
186
|
+
}));
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
});
|
|
155
190
|
|
|
156
|
-
//
|
|
157
|
-
const
|
|
158
|
-
if (
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
name: 'Scalars'
|
|
164
|
-
});
|
|
165
|
-
pointData.setScalars(scalars);
|
|
191
|
+
// we will generate normals if they're missing
|
|
192
|
+
const hasNormals = polyData.getPointData().getNormals();
|
|
193
|
+
if (!hasNormals) {
|
|
194
|
+
const pdn = vtkPolyDataNormals.newInstance();
|
|
195
|
+
pdn.setInputData(polyData);
|
|
196
|
+
pdn.setComputePointNormals(true);
|
|
197
|
+
return pdn.getOutputData();
|
|
166
198
|
}
|
|
167
|
-
decoderModule.destroy(decoder);
|
|
168
199
|
return polyData;
|
|
169
200
|
}
|
|
170
201
|
function vtkDracoReader(publicAPI, model) {
|
|
@@ -235,9 +266,27 @@ function vtkDracoReader(publicAPI, model) {
|
|
|
235
266
|
return;
|
|
236
267
|
}
|
|
237
268
|
model.parseData = content;
|
|
238
|
-
const
|
|
239
|
-
const
|
|
269
|
+
const byteArray = new Int8Array(content);
|
|
270
|
+
const decoder = new decoderModule.Decoder();
|
|
271
|
+
const buffer = new decoderModule.DecoderBuffer();
|
|
272
|
+
buffer.Init(byteArray, byteArray.length);
|
|
273
|
+
const geometryType = decoder.GetEncodedGeometryType(buffer);
|
|
274
|
+
let dracoGeometry;
|
|
275
|
+
if (geometryType === decoderModule.TRIANGULAR_MESH) {
|
|
276
|
+
dracoGeometry = new decoderModule.Mesh();
|
|
277
|
+
const status = decoder.DecodeBufferToMesh(buffer, dracoGeometry);
|
|
278
|
+
if (!status.ok()) {
|
|
279
|
+
vtkErrorMacro(`Could not decode Draco file: ${status.error_msg()}`);
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
} else {
|
|
283
|
+
vtkErrorMacro('Wrong geometry type, expected mesh, got point cloud.');
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
const polyData = getPolyDataFromDracoGeometry(decoder, dracoGeometry);
|
|
240
287
|
decoderModule.destroy(dracoGeometry);
|
|
288
|
+
decoderModule.destroy(buffer);
|
|
289
|
+
decoderModule.destroy(decoder);
|
|
241
290
|
model.output[0] = polyData;
|
|
242
291
|
};
|
|
243
292
|
publicAPI.requestData = () => {
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { m as macro } from '../../../macros2.js';
|
|
2
|
+
import { A as degreesFromRadians } from '../../../Common/Core/Math/index.js';
|
|
3
|
+
import { quat, vec3 } from 'gl-matrix';
|
|
4
|
+
|
|
5
|
+
const {
|
|
6
|
+
vtkDebugMacro,
|
|
7
|
+
vtkWarningMacro
|
|
8
|
+
} = macro;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Create an animation channel
|
|
12
|
+
* @param {glTFChannel} glTFChannel
|
|
13
|
+
* @param {glTFChannel[]} glTFSamplers
|
|
14
|
+
* @returns
|
|
15
|
+
*/
|
|
16
|
+
function createAnimationChannel(glTFChannel, glTFSamplers) {
|
|
17
|
+
const path = glTFChannel.target.path;
|
|
18
|
+
const node = glTFChannel.target.node;
|
|
19
|
+
function applyAnimation(value) {
|
|
20
|
+
let axisAngle;
|
|
21
|
+
let w;
|
|
22
|
+
let nq;
|
|
23
|
+
switch (path) {
|
|
24
|
+
case 'translation':
|
|
25
|
+
node.setPosition(value[0], value[1], value[2]);
|
|
26
|
+
break;
|
|
27
|
+
case 'rotation':
|
|
28
|
+
// Convert quaternion to axis-angle representation
|
|
29
|
+
nq = quat.normalize(quat.create(), value);
|
|
30
|
+
axisAngle = new Float64Array(3);
|
|
31
|
+
w = quat.getAxisAngle(axisAngle, nq);
|
|
32
|
+
// Apply rotation using rotateWXYZ
|
|
33
|
+
node.rotateWXYZ(degreesFromRadians(w), axisAngle[0], axisAngle[1], axisAngle[2]);
|
|
34
|
+
break;
|
|
35
|
+
case 'scale':
|
|
36
|
+
node.setScale(value[0], value[1], value[2]);
|
|
37
|
+
break;
|
|
38
|
+
default:
|
|
39
|
+
vtkWarningMacro(`Unsupported animation path: ${path}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function animate(currentTime) {
|
|
43
|
+
const sampler = glTFSamplers[glTFChannel.sampler];
|
|
44
|
+
const value = sampler.evaluate(currentTime, path);
|
|
45
|
+
applyAnimation(value);
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
...glTFChannel,
|
|
49
|
+
animate
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Create an animation sampler
|
|
55
|
+
* @param {glTFSampler} glTFSampler
|
|
56
|
+
* @returns
|
|
57
|
+
*/
|
|
58
|
+
function createAnimationSampler(glTFSampler) {
|
|
59
|
+
let lastKeyframeIndex = 0;
|
|
60
|
+
function findKeyframes(time) {
|
|
61
|
+
let i1 = lastKeyframeIndex;
|
|
62
|
+
while (i1 < glTFSampler.input.length - 1 && glTFSampler.input[i1] <= time) {
|
|
63
|
+
i1++;
|
|
64
|
+
}
|
|
65
|
+
const i0 = Math.max(0, i1 - 1);
|
|
66
|
+
lastKeyframeIndex = i0;
|
|
67
|
+
return [glTFSampler.input[i0], glTFSampler.input[i1], i0, i1];
|
|
68
|
+
}
|
|
69
|
+
function stepInterpolate(path, i0) {
|
|
70
|
+
const startIndex = i0 * 3;
|
|
71
|
+
const v0 = new Array(3);
|
|
72
|
+
for (let i = 0; i < 3; ++i) {
|
|
73
|
+
v0[i] = glTFSampler.output[startIndex + i];
|
|
74
|
+
}
|
|
75
|
+
return v0;
|
|
76
|
+
}
|
|
77
|
+
function linearInterpolate(path, t0, t1, i0, i1, t) {
|
|
78
|
+
const ratio = (t - t0) / (t1 - t0);
|
|
79
|
+
const startIndex = i0 * 4;
|
|
80
|
+
const endIndex = i1 * 4;
|
|
81
|
+
const v0 = new Array(4);
|
|
82
|
+
const v1 = new Array(4);
|
|
83
|
+
for (let i = 0; i < 4; ++i) {
|
|
84
|
+
v0[i] = glTFSampler.output[startIndex + i];
|
|
85
|
+
v1[i] = glTFSampler.output[endIndex + i];
|
|
86
|
+
}
|
|
87
|
+
switch (path) {
|
|
88
|
+
case 'translation':
|
|
89
|
+
case 'scale':
|
|
90
|
+
return vec3.lerp(vec3.create(), v0, v1, ratio);
|
|
91
|
+
case 'rotation':
|
|
92
|
+
return quat.slerp(quat.create(), v0, v1, ratio);
|
|
93
|
+
default:
|
|
94
|
+
vtkWarningMacro(`Unsupported animation path: ${path}`);
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function cubicSplineInterpolate(path, t0, t1, i0, i1, time) {
|
|
99
|
+
const dt = t1 - t0;
|
|
100
|
+
const t = (time - t0) / dt;
|
|
101
|
+
const t2 = t * t;
|
|
102
|
+
const t3 = t2 * t;
|
|
103
|
+
const p0 = glTFSampler.output[i0 * 3 + 1];
|
|
104
|
+
const m0 = dt * glTFSampler.output[i0 * 3 + 2];
|
|
105
|
+
const p1 = glTFSampler.output[i1 * 3 + 1];
|
|
106
|
+
const m1 = dt * glTFSampler.output[i1 * 3];
|
|
107
|
+
if (Array.isArray(p0)) {
|
|
108
|
+
return p0.map((v, j) => {
|
|
109
|
+
const a = 2 * t3 - 3 * t2 + 1;
|
|
110
|
+
const b = t3 - 2 * t2 + t;
|
|
111
|
+
const c = -2 * t3 + 3 * t2;
|
|
112
|
+
const d = t3 - t2;
|
|
113
|
+
return a * v + b * m0[j] + c * p1[j] + d * m1[j];
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
const a = 2 * t3 - 3 * t2 + 1;
|
|
117
|
+
const b = t3 - 2 * t2 + t;
|
|
118
|
+
const c = -2 * t3 + 3 * t2;
|
|
119
|
+
const d = t3 - t2;
|
|
120
|
+
return a * p0 + b * m0 + c * p1 + d * m1;
|
|
121
|
+
}
|
|
122
|
+
function evaluate(time, path) {
|
|
123
|
+
const [t0, t1, i0, i1] = findKeyframes(time);
|
|
124
|
+
let result;
|
|
125
|
+
switch (glTFSampler.interpolation) {
|
|
126
|
+
case 'STEP':
|
|
127
|
+
result = stepInterpolate(path, i0);
|
|
128
|
+
break;
|
|
129
|
+
case 'LINEAR':
|
|
130
|
+
result = linearInterpolate(path, t0, t1, i0, i1, time);
|
|
131
|
+
break;
|
|
132
|
+
case 'CUBICSPLINE':
|
|
133
|
+
result = cubicSplineInterpolate(path, t0, t1, i0, i1, time);
|
|
134
|
+
break;
|
|
135
|
+
default:
|
|
136
|
+
vtkWarningMacro(`Unknown interpolation method: ${glTFSampler.interpolation}`);
|
|
137
|
+
}
|
|
138
|
+
return result;
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
...glTFSampler,
|
|
142
|
+
evaluate
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Create an animation
|
|
148
|
+
* @param {glTFAnimation} glTFAnimation
|
|
149
|
+
* @param {Map} nodes
|
|
150
|
+
* @returns
|
|
151
|
+
*/
|
|
152
|
+
function createAnimation(glTFAnimation, nodes) {
|
|
153
|
+
glTFAnimation.samplers = glTFAnimation.samplers.map(sampler => createAnimationSampler(sampler));
|
|
154
|
+
glTFAnimation.channels = glTFAnimation.channels.map(channel => {
|
|
155
|
+
channel.target.node = nodes.get(`node-${channel.target.node}`);
|
|
156
|
+
return createAnimationChannel(channel, glTFAnimation.samplers);
|
|
157
|
+
});
|
|
158
|
+
function update(currentTime) {
|
|
159
|
+
glTFAnimation.channels.forEach(channel => channel.animate(currentTime));
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
...glTFAnimation,
|
|
163
|
+
update
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Create an animation mixer
|
|
169
|
+
* @param {Map} nodes
|
|
170
|
+
* @param {*} accessors
|
|
171
|
+
* @returns
|
|
172
|
+
*/
|
|
173
|
+
function createAnimationMixer(nodes, accessors) {
|
|
174
|
+
const animations = new Map();
|
|
175
|
+
const activeAnimations = new Map();
|
|
176
|
+
function addAnimation(glTFAnimation) {
|
|
177
|
+
const annimation = createAnimation(glTFAnimation, nodes);
|
|
178
|
+
animations.set(glTFAnimation.id, annimation);
|
|
179
|
+
vtkDebugMacro(`Animation "${glTFAnimation.id}" added to mixer`);
|
|
180
|
+
}
|
|
181
|
+
function play(name) {
|
|
182
|
+
let weight = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
|
|
183
|
+
if (!animations.has(name)) {
|
|
184
|
+
vtkWarningMacro(`Animation "${name}" not found in mixer`);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
activeAnimations.set(name, {
|
|
188
|
+
animation: animations.get(name),
|
|
189
|
+
weight,
|
|
190
|
+
time: 0
|
|
191
|
+
});
|
|
192
|
+
vtkDebugMacro(`Playing animation "${name}" with weight ${weight}`);
|
|
193
|
+
}
|
|
194
|
+
function stop(name) {
|
|
195
|
+
if (activeAnimations.delete(name)) {
|
|
196
|
+
vtkWarningMacro(`Stopped animation "${name}"`);
|
|
197
|
+
} else {
|
|
198
|
+
vtkWarningMacro(`Animation "${name}" was not playing`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
function stopAll() {
|
|
202
|
+
activeAnimations.clear();
|
|
203
|
+
vtkWarningMacro('Stopped all animations');
|
|
204
|
+
}
|
|
205
|
+
function update(deltaTime) {
|
|
206
|
+
// Normalize weights
|
|
207
|
+
const totalWeight = Array.from(activeAnimations.values()).reduce((sum, _ref) => {
|
|
208
|
+
let {
|
|
209
|
+
weight
|
|
210
|
+
} = _ref;
|
|
211
|
+
return sum + weight;
|
|
212
|
+
}, 0);
|
|
213
|
+
activeAnimations.forEach((_ref2, name) => {
|
|
214
|
+
let {
|
|
215
|
+
animation,
|
|
216
|
+
weight,
|
|
217
|
+
time
|
|
218
|
+
} = _ref2;
|
|
219
|
+
const normalizedWeight = totalWeight > 0 ? weight / totalWeight : 0;
|
|
220
|
+
const newTime = time + deltaTime;
|
|
221
|
+
activeAnimations.set(name, {
|
|
222
|
+
animation,
|
|
223
|
+
weight,
|
|
224
|
+
time: newTime
|
|
225
|
+
});
|
|
226
|
+
vtkDebugMacro(`Updating animation "${name}" at time ${newTime.toFixed(3)} with normalized weight ${normalizedWeight.toFixed(3)}`);
|
|
227
|
+
animation.update(newTime, normalizedWeight);
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
return {
|
|
231
|
+
addAnimation,
|
|
232
|
+
play,
|
|
233
|
+
stop,
|
|
234
|
+
stopAll,
|
|
235
|
+
update
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export { createAnimation, createAnimationChannel, createAnimationMixer, createAnimationSampler };
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
const BINARY_HEADER_MAGIC = 'glTF';
|
|
2
|
+
const BINARY_HEADER_LENGTH = 12;
|
|
3
|
+
const BINARY_CHUNK_TYPES = {
|
|
4
|
+
JSON: 0x4e4f534a,
|
|
5
|
+
BIN: 0x004e4942
|
|
6
|
+
};
|
|
7
|
+
const BINARY_HEADER_INTS = 3;
|
|
8
|
+
const BINARY_CHUNK_HEADER_INTS = 2;
|
|
9
|
+
const MIN_LIGHT_ATTENUATION = 0.01;
|
|
10
|
+
const COMPONENTS = {
|
|
11
|
+
SCALAR: 1,
|
|
12
|
+
VEC2: 2,
|
|
13
|
+
VEC3: 3,
|
|
14
|
+
VEC4: 4,
|
|
15
|
+
MAT2: 4,
|
|
16
|
+
MAT3: 9,
|
|
17
|
+
MAT4: 16
|
|
18
|
+
};
|
|
19
|
+
const BYTES = {
|
|
20
|
+
5120: 1,
|
|
21
|
+
// BYTE
|
|
22
|
+
5121: 1,
|
|
23
|
+
// UNSIGNED_BYTE
|
|
24
|
+
5122: 2,
|
|
25
|
+
// SHORT
|
|
26
|
+
5123: 2,
|
|
27
|
+
// UNSIGNED_SHORT
|
|
28
|
+
5125: 4,
|
|
29
|
+
// UNSIGNED_INT
|
|
30
|
+
5126: 4 // FLOAT
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const MODES = {
|
|
34
|
+
GL_POINTS: 0,
|
|
35
|
+
GL_LINES: 1,
|
|
36
|
+
GL_LINE_LOOP: 2,
|
|
37
|
+
GL_LINE_STRIP: 3,
|
|
38
|
+
GL_TRIANGLES: 4,
|
|
39
|
+
GL_TRIANGLE_STRIP: 5,
|
|
40
|
+
GL_TRIANGLE_FAN: 6
|
|
41
|
+
};
|
|
42
|
+
const ARRAY_TYPES = {
|
|
43
|
+
5120: Int8Array,
|
|
44
|
+
5121: Uint8Array,
|
|
45
|
+
5122: Int16Array,
|
|
46
|
+
5123: Uint16Array,
|
|
47
|
+
5125: Uint32Array,
|
|
48
|
+
5126: Float32Array
|
|
49
|
+
};
|
|
50
|
+
const GL_SAMPLER = {
|
|
51
|
+
NEAREST: 9728,
|
|
52
|
+
LINEAR: 9729,
|
|
53
|
+
NEAREST_MIPMAP_NEAREST: 9984,
|
|
54
|
+
LINEAR_MIPMAP_NEAREST: 9985,
|
|
55
|
+
NEAREST_MIPMAP_LINEAR: 9986,
|
|
56
|
+
LINEAR_MIPMAP_LINEAR: 9987,
|
|
57
|
+
REPEAT: 10497,
|
|
58
|
+
CLAMP_TO_EDGE: 33071,
|
|
59
|
+
MIRRORED_REPEAT: 33648,
|
|
60
|
+
TEXTURE_MAG_FILTER: 10240,
|
|
61
|
+
TEXTURE_MIN_FILTER: 10241,
|
|
62
|
+
TEXTURE_WRAP_S: 10242,
|
|
63
|
+
TEXTURE_WRAP_T: 10243
|
|
64
|
+
};
|
|
65
|
+
const DEFAULT_SAMPLER = {
|
|
66
|
+
magFilter: GL_SAMPLER.NEAREST,
|
|
67
|
+
minFilter: GL_SAMPLER.LINEAR_MIPMAP_LINEAR,
|
|
68
|
+
wrapS: GL_SAMPLER.REPEAT,
|
|
69
|
+
wrapT: GL_SAMPLER.REPEAT
|
|
70
|
+
};
|
|
71
|
+
const SEMANTIC_ATTRIBUTE_MAP = {
|
|
72
|
+
NORMAL: 'normal',
|
|
73
|
+
POSITION: 'position',
|
|
74
|
+
TEXCOORD_0: 'texcoord0',
|
|
75
|
+
TEXCOORD_1: 'texcoord1',
|
|
76
|
+
WEIGHTS_0: 'weight',
|
|
77
|
+
JOINTS_0: 'joint',
|
|
78
|
+
COLOR_0: 'color',
|
|
79
|
+
TANGENT: 'tangent'
|
|
80
|
+
};
|
|
81
|
+
const ALPHA_MODE = {
|
|
82
|
+
OPAQUE: 'OPAQUE',
|
|
83
|
+
MASK: 'MASK',
|
|
84
|
+
BLEND: 'BLEND'
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export { ALPHA_MODE, ARRAY_TYPES, BINARY_CHUNK_HEADER_INTS, BINARY_CHUNK_TYPES, BINARY_HEADER_INTS, BINARY_HEADER_LENGTH, BINARY_HEADER_MAGIC, BYTES, COMPONENTS, DEFAULT_SAMPLER, GL_SAMPLER, MIN_LIGHT_ATTENUATION, MODES, SEMANTIC_ATTRIBUTE_MAP };
|