@kitware/vtk.js 34.0.0-beta.1 → 34.0.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/IO/Geometry/GLTFImporter/Decoder.js +35 -21
- package/IO/Geometry/GLTFImporter/Parser.js +15 -6
- package/IO/Geometry/GLTFImporter/Reader.js +7 -10
- package/IO/Geometry/GLTFImporter/Utils.js +9 -27
- package/Rendering/Core/Texture.d.ts +81 -29
- package/Rendering/Core/Texture.js +171 -86
- package/Rendering/WebGPU/CellArrayMapper.js +139 -199
- package/Rendering/WebGPU/PolyDataMapper.js +35 -15
- package/Rendering/WebGPU/Renderer.js +1 -1
- package/Rendering/WebGPU/Texture.js +106 -97
- package/Rendering/WebGPU/TextureManager.js +37 -6
- package/package.json +1 -1
- package/IO/Geometry/GLTFImporter/ORMTexture.worker.js +0 -42
- package/_virtual/rollup-plugin-worker-loader__module_Sources/IO/Geometry/GLTFImporter/ORMTexture.worker.js +0 -296
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { m as macro } from '../../macros2.js';
|
|
2
2
|
|
|
3
|
+
/* eslint-disable no-bitwise */
|
|
4
|
+
|
|
3
5
|
// ----------------------------------------------------------------------------
|
|
4
6
|
// vtkTexture methods
|
|
5
7
|
// ----------------------------------------------------------------------------
|
|
@@ -23,11 +25,29 @@ function vtkTexture(publicAPI, model) {
|
|
|
23
25
|
publicAPI.setInputConnection(null);
|
|
24
26
|
model.image = null;
|
|
25
27
|
model.canvas = null;
|
|
28
|
+
model.imageBitmap = null;
|
|
26
29
|
}
|
|
27
30
|
model.jsImageData = imageData;
|
|
28
31
|
model.imageLoaded = true;
|
|
29
32
|
publicAPI.modified();
|
|
30
33
|
};
|
|
34
|
+
publicAPI.setImageBitmap = imageBitmap => {
|
|
35
|
+
if (model.imageBitmap === imageBitmap) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// clear other entries
|
|
40
|
+
if (imageBitmap !== null) {
|
|
41
|
+
publicAPI.setInputData(null);
|
|
42
|
+
publicAPI.setInputConnection(null);
|
|
43
|
+
model.image = null;
|
|
44
|
+
model.canvas = null;
|
|
45
|
+
model.jsImageData = null;
|
|
46
|
+
}
|
|
47
|
+
model.imageBitmap = imageBitmap;
|
|
48
|
+
model.imageLoaded = true;
|
|
49
|
+
publicAPI.modified();
|
|
50
|
+
};
|
|
31
51
|
publicAPI.setCanvas = canvas => {
|
|
32
52
|
if (model.canvas === canvas) {
|
|
33
53
|
return;
|
|
@@ -38,6 +58,7 @@ function vtkTexture(publicAPI, model) {
|
|
|
38
58
|
publicAPI.setInputData(null);
|
|
39
59
|
publicAPI.setInputConnection(null);
|
|
40
60
|
model.image = null;
|
|
61
|
+
model.imageBitmap = null;
|
|
41
62
|
model.jsImageData = null;
|
|
42
63
|
}
|
|
43
64
|
model.canvas = canvas;
|
|
@@ -54,6 +75,7 @@ function vtkTexture(publicAPI, model) {
|
|
|
54
75
|
publicAPI.setInputConnection(null);
|
|
55
76
|
model.canvas = null;
|
|
56
77
|
model.jsImageData = null;
|
|
78
|
+
model.imageBitmap = null;
|
|
57
79
|
}
|
|
58
80
|
model.image = image;
|
|
59
81
|
model.imageLoaded = false;
|
|
@@ -86,13 +108,20 @@ function vtkTexture(publicAPI, model) {
|
|
|
86
108
|
width = model.image.width;
|
|
87
109
|
height = model.image.height;
|
|
88
110
|
}
|
|
111
|
+
if (model.imageBitmap) {
|
|
112
|
+
width = model.imageBitmap.width;
|
|
113
|
+
height = model.imageBitmap.height;
|
|
114
|
+
}
|
|
89
115
|
const dimensionality = (width > 1) + (height > 1) + (depth > 1);
|
|
90
116
|
return dimensionality;
|
|
91
117
|
};
|
|
92
118
|
publicAPI.getInputAsJsImageData = () => {
|
|
93
119
|
if (!model.imageLoaded || publicAPI.getInputData()) return null;
|
|
94
120
|
if (model.jsImageData) {
|
|
95
|
-
return model.jsImageData
|
|
121
|
+
return model.jsImageData;
|
|
122
|
+
}
|
|
123
|
+
if (model.imageBitmap) {
|
|
124
|
+
return model.imageBitmap;
|
|
96
125
|
}
|
|
97
126
|
if (model.canvas) {
|
|
98
127
|
const context = model.canvas.getContext('2d');
|
|
@@ -100,104 +129,159 @@ function vtkTexture(publicAPI, model) {
|
|
|
100
129
|
return imageData;
|
|
101
130
|
}
|
|
102
131
|
if (model.image) {
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
canvas
|
|
132
|
+
const width = model.image.width;
|
|
133
|
+
const height = model.image.height;
|
|
134
|
+
const canvas = new OffscreenCanvas(width, height);
|
|
106
135
|
const context = canvas.getContext('2d');
|
|
107
|
-
context.translate(0,
|
|
136
|
+
context.translate(0, height);
|
|
108
137
|
context.scale(1, -1);
|
|
109
|
-
context.drawImage(model.image, 0, 0,
|
|
110
|
-
const imageData = context.getImageData(0, 0,
|
|
138
|
+
context.drawImage(model.image, 0, 0, width, height);
|
|
139
|
+
const imageData = context.getImageData(0, 0, width, height);
|
|
111
140
|
return imageData;
|
|
112
141
|
}
|
|
113
142
|
return null;
|
|
114
143
|
};
|
|
115
144
|
}
|
|
116
145
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
let shift = 0;
|
|
142
|
-
for (let p = 0; p < imageData.length; p += hs) {
|
|
143
|
-
if (p % vs === 0) {
|
|
144
|
-
shift += 2 * hs * currentWidth;
|
|
146
|
+
/**
|
|
147
|
+
* Generates mipmaps for a given GPU texture using a compute shader.
|
|
148
|
+
*
|
|
149
|
+
* This function iteratively generates each mip level for the provided texture,
|
|
150
|
+
* using a bilinear downsampling compute shader implemented in WGSL. It creates
|
|
151
|
+
* the necessary pipeline, bind groups, and dispatches compute passes for each
|
|
152
|
+
* mip level.
|
|
153
|
+
*
|
|
154
|
+
* @param {GPUDevice} device - The WebGPU device used to create resources and submit commands.
|
|
155
|
+
* @param {GPUTexture} texture - The GPU texture for which mipmaps will be generated. Must be created with mip levels.
|
|
156
|
+
* @param {number} mipLevelCount - The total number of mip levels to generate (including the base level).
|
|
157
|
+
*/
|
|
158
|
+
const generateMipmaps = (device, texture, mipLevelCount) => {
|
|
159
|
+
const computeShaderCode = `
|
|
160
|
+
@group(0) @binding(0) var inputTexture: texture_2d<f32>;
|
|
161
|
+
@group(0) @binding(1) var outputTexture: texture_storage_2d<rgba8unorm, write>;
|
|
162
|
+
|
|
163
|
+
@compute @workgroup_size(8, 8)
|
|
164
|
+
fn main(@builtin(global_invocation_id) global_id: vec3<u32>) {
|
|
165
|
+
let texelCoord = vec2<i32>(global_id.xy);
|
|
166
|
+
let outputSize = textureDimensions(outputTexture);
|
|
167
|
+
|
|
168
|
+
if (texelCoord.x >= i32(outputSize.x) || texelCoord.y >= i32(outputSize.y)) {
|
|
169
|
+
return;
|
|
145
170
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
171
|
+
|
|
172
|
+
let inputSize = textureDimensions(inputTexture);
|
|
173
|
+
let scale = vec2<f32>(inputSize) / vec2<f32>(outputSize);
|
|
174
|
+
|
|
175
|
+
// Compute the floating-point source coordinate
|
|
176
|
+
let srcCoord = (vec2<f32>(texelCoord) + 0.5) * scale - 0.5;
|
|
177
|
+
|
|
178
|
+
// Get integer coordinates for the four surrounding texels
|
|
179
|
+
let x0 = i32(floor(srcCoord.x));
|
|
180
|
+
let x1 = min(x0 + 1, i32(inputSize.x) - 1);
|
|
181
|
+
let y0 = i32(floor(srcCoord.y));
|
|
182
|
+
let y1 = min(y0 + 1, i32(inputSize.y) - 1);
|
|
183
|
+
|
|
184
|
+
// Compute the weights
|
|
185
|
+
let wx = srcCoord.x - f32(x0);
|
|
186
|
+
let wy = srcCoord.y - f32(y0);
|
|
187
|
+
|
|
188
|
+
// Fetch the four texels
|
|
189
|
+
let c00 = textureLoad(inputTexture, vec2<i32>(x0, y0), 0);
|
|
190
|
+
let c10 = textureLoad(inputTexture, vec2<i32>(x1, y0), 0);
|
|
191
|
+
let c01 = textureLoad(inputTexture, vec2<i32>(x0, y1), 0);
|
|
192
|
+
let c11 = textureLoad(inputTexture, vec2<i32>(x1, y1), 0);
|
|
193
|
+
|
|
194
|
+
// Bilinear interpolation
|
|
195
|
+
let color = mix(
|
|
196
|
+
mix(c00, c10, wx),
|
|
197
|
+
mix(c01, c11, wx),
|
|
198
|
+
wy
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
textureStore(outputTexture, texelCoord, color);
|
|
202
|
+
}
|
|
203
|
+
`;
|
|
204
|
+
const computeShader = device.createShaderModule({
|
|
205
|
+
code: computeShaderCode
|
|
206
|
+
});
|
|
207
|
+
const bindGroupLayout = device.createBindGroupLayout({
|
|
208
|
+
entries: [{
|
|
209
|
+
binding: 0,
|
|
210
|
+
// eslint-disable-next-line no-undef
|
|
211
|
+
visibility: GPUShaderStage.COMPUTE,
|
|
212
|
+
texture: {
|
|
213
|
+
sampleType: 'float'
|
|
153
214
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
let x = -(kernel.length - 1) / 2;
|
|
162
|
-
let kw = kernelWeight;
|
|
163
|
-
let value = 0.0;
|
|
164
|
-
for (let k = 0; k < kernel.length; k++) {
|
|
165
|
-
let index = p + c + x * hs;
|
|
166
|
-
const lineShift = index % vs - (p + c) % vs;
|
|
167
|
-
if (lineShift > hs) index += vs;
|
|
168
|
-
if (lineShift < -hs) index -= vs;
|
|
169
|
-
if (dataCopy[index]) {
|
|
170
|
-
value += dataCopy[index] * kernel[k];
|
|
171
|
-
} else {
|
|
172
|
-
kw -= kernel[k];
|
|
173
|
-
}
|
|
174
|
-
x += 1;
|
|
175
|
-
}
|
|
176
|
-
imageData[p + c] = value / kw;
|
|
215
|
+
}, {
|
|
216
|
+
binding: 1,
|
|
217
|
+
// eslint-disable-next-line no-undef
|
|
218
|
+
visibility: GPUShaderStage.COMPUTE,
|
|
219
|
+
storageTexture: {
|
|
220
|
+
format: 'rgba8unorm',
|
|
221
|
+
access: 'write-only'
|
|
177
222
|
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
let kw = kernelWeight;
|
|
185
|
-
let value = 0.0;
|
|
186
|
-
for (let k = 0; k < kernel.length; k++) {
|
|
187
|
-
const index = p + c + x * vs;
|
|
188
|
-
if (dataCopy[index]) {
|
|
189
|
-
value += dataCopy[index] * kernel[k];
|
|
190
|
-
} else {
|
|
191
|
-
kw -= kernel[k];
|
|
192
|
-
}
|
|
193
|
-
x += 1;
|
|
194
|
-
}
|
|
195
|
-
imageData[p + c] = value / kw;
|
|
223
|
+
}, {
|
|
224
|
+
binding: 2,
|
|
225
|
+
// eslint-disable-next-line no-undef
|
|
226
|
+
visibility: GPUShaderStage.COMPUTE,
|
|
227
|
+
sampler: {
|
|
228
|
+
type: 'filtering'
|
|
196
229
|
}
|
|
230
|
+
}]
|
|
231
|
+
});
|
|
232
|
+
const pipelineLayout = device.createPipelineLayout({
|
|
233
|
+
bindGroupLayouts: [bindGroupLayout]
|
|
234
|
+
});
|
|
235
|
+
const pipeline = device.createComputePipeline({
|
|
236
|
+
label: 'ComputeMipmapPipeline',
|
|
237
|
+
layout: pipelineLayout,
|
|
238
|
+
compute: {
|
|
239
|
+
module: computeShader,
|
|
240
|
+
entryPoint: 'main'
|
|
197
241
|
}
|
|
198
|
-
|
|
242
|
+
});
|
|
243
|
+
const sampler = device.createSampler({
|
|
244
|
+
magFilter: 'linear',
|
|
245
|
+
minFilter: 'linear'
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
// Generate each mip level
|
|
249
|
+
for (let mipLevel = 1; mipLevel < mipLevelCount; mipLevel++) {
|
|
250
|
+
const srcView = texture.createView({
|
|
251
|
+
baseMipLevel: mipLevel - 1,
|
|
252
|
+
mipLevelCount: 1
|
|
253
|
+
});
|
|
254
|
+
const dstView = texture.createView({
|
|
255
|
+
baseMipLevel: mipLevel,
|
|
256
|
+
mipLevelCount: 1
|
|
257
|
+
});
|
|
258
|
+
const bindGroup = device.createBindGroup({
|
|
259
|
+
layout: pipeline.getBindGroupLayout(0),
|
|
260
|
+
entries: [{
|
|
261
|
+
binding: 0,
|
|
262
|
+
resource: srcView
|
|
263
|
+
}, {
|
|
264
|
+
binding: 1,
|
|
265
|
+
resource: dstView
|
|
266
|
+
}, {
|
|
267
|
+
binding: 2,
|
|
268
|
+
resource: sampler
|
|
269
|
+
}]
|
|
270
|
+
});
|
|
271
|
+
const commandEncoder = device.createCommandEncoder({
|
|
272
|
+
label: `MipmapGenerateCommandEncoder`
|
|
273
|
+
});
|
|
274
|
+
const computePass = commandEncoder.beginComputePass();
|
|
275
|
+
computePass.setPipeline(pipeline);
|
|
276
|
+
computePass.setBindGroup(0, bindGroup);
|
|
277
|
+
const mipWidth = Math.max(1, texture.width >> mipLevel);
|
|
278
|
+
const mipHeight = Math.max(1, texture.height >> mipLevel);
|
|
279
|
+
const workgroupsX = Math.ceil(mipWidth / 8);
|
|
280
|
+
const workgroupsY = Math.ceil(mipHeight / 8);
|
|
281
|
+
computePass.dispatchWorkgroups(workgroupsX, workgroupsY);
|
|
282
|
+
computePass.end();
|
|
283
|
+
device.queue.submit([commandEncoder.finish()]);
|
|
199
284
|
}
|
|
200
|
-
return maps;
|
|
201
285
|
};
|
|
202
286
|
|
|
203
287
|
// ----------------------------------------------------------------------------
|
|
@@ -208,6 +292,7 @@ const DEFAULT_VALUES = {
|
|
|
208
292
|
image: null,
|
|
209
293
|
canvas: null,
|
|
210
294
|
jsImageData: null,
|
|
295
|
+
imageBitmap: null,
|
|
211
296
|
imageLoaded: false,
|
|
212
297
|
repeat: false,
|
|
213
298
|
interpolate: false,
|
|
@@ -225,7 +310,7 @@ function extend(publicAPI, model) {
|
|
|
225
310
|
// Build VTK API
|
|
226
311
|
macro.obj(publicAPI, model);
|
|
227
312
|
macro.algo(publicAPI, model, 6, 0);
|
|
228
|
-
macro.get(publicAPI, model, ['canvas', 'image', 'jsImageData', 'imageLoaded', 'resizable']);
|
|
313
|
+
macro.get(publicAPI, model, ['canvas', 'image', 'jsImageData', 'imageBitmap', 'imageLoaded', 'resizable']);
|
|
229
314
|
macro.setGet(publicAPI, model, ['repeat', 'edgeClamp', 'interpolate', 'mipLevel']);
|
|
230
315
|
vtkTexture(publicAPI, model);
|
|
231
316
|
}
|