zincjs 1.19.3 → 1.20.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/build/zinc.frontend.js +1 -1
- package/build/zinc.js +5 -5
- package/build/zinc.js.map +1 -1
- package/package.json +1 -1
- package/src/loaders/niftiReader.js +80 -193
- package/src/primitives/textureSlides.js +43 -22
- package/src/primitives/textureVolume.js +5 -0
- package/src/shaders/textureSlide.js +23 -8
- package/src/three/GLTFExporter.js +2 -2
package/package.json
CHANGED
|
@@ -15,6 +15,7 @@ const defaultTextureSettings = {
|
|
|
15
15
|
"position": [0, 0, 0],
|
|
16
16
|
"scale": [1, 1, 1],
|
|
17
17
|
"flipY": false,
|
|
18
|
+
"flipZ": false,
|
|
18
19
|
"reference_point": "corner"
|
|
19
20
|
}
|
|
20
21
|
],
|
|
@@ -38,7 +39,6 @@ const defaultTextureSettings = {
|
|
|
38
39
|
};
|
|
39
40
|
|
|
40
41
|
const defaultOptions = {
|
|
41
|
-
hideWhitePixel: false,
|
|
42
42
|
hideBlackPixel: true,
|
|
43
43
|
keepScalePosition: true,
|
|
44
44
|
filterByValue: true,
|
|
@@ -126,197 +126,81 @@ function readNIFTI(data) {
|
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
|
|
129
|
-
function
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
if (maskWidth === width && maskHeight === height && maskDepth === depth) {
|
|
144
|
-
const maskedTypedData = getTypedData(maskHeader, maskImage);
|
|
145
|
-
maskData = maskedTypedData.typedData;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
let slope = niftiHeader.scl_slope || 1;
|
|
149
|
-
let intercept = niftiHeader.scl_inter || 0;
|
|
129
|
+
function convertNiftiToUint8Array(niftiHeader, niftiImage) {
|
|
130
|
+
// 1. Parse the raw array using the correct native typed array view
|
|
131
|
+
let rawData;
|
|
132
|
+
switch (niftiHeader.datatypeCode) {
|
|
133
|
+
case nifti.NIFTI1.TYPE_UINT8: rawData = new Uint8Array(niftiImage); break;
|
|
134
|
+
case nifti.NIFTI1.TYPE_INT8: rawData = new Int8Array(niftiImage); break;
|
|
135
|
+
case nifti.NIFTI1.TYPE_UINT16: rawData = new Uint16Array(niftiImage); break;
|
|
136
|
+
case nifti.NIFTI1.TYPE_INT16: rawData = new Int16Array(niftiImage); break;
|
|
137
|
+
case nifti.NIFTI1.TYPE_UINT32: rawData = new Uint32Array(niftiImage); break;
|
|
138
|
+
case nifti.NIFTI1.TYPE_INT32: rawData = new Int32Array(niftiImage); break;
|
|
139
|
+
case nifti.NIFTI1.TYPE_FLOAT32: rawData = new Float32Array(niftiImage); break;
|
|
140
|
+
case nifti.NIFTI1.TYPE_FLOAT64: rawData = new Float64Array(niftiImage); break;
|
|
141
|
+
default: throw new Error("Unsupported NIfTI data type");
|
|
142
|
+
}
|
|
150
143
|
|
|
151
|
-
|
|
152
|
-
|
|
144
|
+
const slope = niftiHeader.scl_slope || 1.0;
|
|
145
|
+
const intercept = niftiHeader.scl_inter || 0.0;
|
|
153
146
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
let val = (typedData[i] * slope) + intercept;
|
|
157
|
-
if (val < min) min = val;
|
|
158
|
-
if (val > max) max = val;
|
|
159
|
-
}
|
|
160
|
-
let range = max - min;
|
|
147
|
+
let min = Infinity;
|
|
148
|
+
let max = -Infinity;
|
|
161
149
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
let trueVal = typedData[offset] * slope + intercept;
|
|
169
|
-
let value = 0;
|
|
170
|
-
if (range !== 0) {
|
|
171
|
-
// Map [min, max] to [0, 255]
|
|
172
|
-
value = ((trueVal - min) / range) * 255;
|
|
173
|
-
}
|
|
174
|
-
fullArray[offset * 4] = value;
|
|
175
|
-
fullArray[offset * 4 + 1] = value;
|
|
176
|
-
fullArray[offset * 4 + 2] = value;
|
|
177
|
-
fullArray[offset * 4 + 3] = 255;
|
|
178
|
-
if (maskData) {
|
|
179
|
-
const maskedValue = maskData[offset];
|
|
180
|
-
if (options.hideBlackPixel) {
|
|
181
|
-
//if (maskedValue === 0 && 20 > value) {
|
|
182
|
-
if (maskedValue === 0) {
|
|
183
|
-
fullArray[offset * 4 + 3] = 0;
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
} else if (options.filterByValue) {
|
|
187
|
-
if (options.hideWhitePixel && value === 255) {
|
|
188
|
-
fullArray[offset * 4 + 3] = 0;
|
|
189
|
-
}
|
|
190
|
-
if (options.hideBlackPixel && 2 >= value) {
|
|
191
|
-
fullArray[offset * 4] = 240;
|
|
192
|
-
fullArray[offset * 4 + 1] = 240;
|
|
193
|
-
fullArray[offset * 4 + 2] = 240;
|
|
194
|
-
fullArray[offset * 4 + 3] = 1.0;
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
}
|
|
150
|
+
const len = rawData.length;
|
|
151
|
+
for (let i = 0; i < len; i++) {
|
|
152
|
+
let val = rawData[i];
|
|
153
|
+
if (val < min) min = val;
|
|
154
|
+
if (val > max) max = val;
|
|
155
|
+
}
|
|
200
156
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
157
|
+
const uint8Data = new Uint8Array(len);
|
|
158
|
+
const range = max - min;
|
|
159
|
+
|
|
160
|
+
for (let i = 0; i < len; i++) {
|
|
161
|
+
let physicalVal = rawData[i];
|
|
162
|
+
let normalised = (physicalVal - min) / range;
|
|
163
|
+
normalised = Math.max(0.0, Math.min(1.0, normalised));
|
|
164
|
+
uint8Data[i] = Math.round(normalised * 255.0);
|
|
207
165
|
}
|
|
208
|
-
|
|
166
|
+
|
|
167
|
+
return uint8Data;
|
|
209
168
|
}
|
|
210
|
-
|
|
211
|
-
function createSources(niftiHeader, niftiImage, maskHeader, maskImage
|
|
169
|
+
|
|
170
|
+
function createSources(niftiHeader, niftiImage, maskHeader, maskImage) {
|
|
212
171
|
if (niftiHeader?.dims && niftiHeader.dims[0] === 3) {
|
|
213
172
|
const width = niftiHeader.dims[1];
|
|
214
173
|
const height = niftiHeader.dims[2];
|
|
215
174
|
const depth = niftiHeader.dims[3];
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
175
|
+
let isRGB = false;
|
|
176
|
+
if (niftiHeader.intent_code === 1007 || niftiHeader.intent_code === 1008 ||
|
|
177
|
+
niftiHeader.datatypeCode === 128 || niftiHeader.datatypeCode === 2304) {
|
|
178
|
+
isRGB = true;
|
|
179
|
+
}
|
|
180
|
+
const typedData = convertNiftiToUint8Array(niftiHeader, niftiImage);
|
|
220
181
|
let maskData = undefined;
|
|
221
182
|
if (maskHeader && maskImage) {
|
|
222
183
|
const maskWidth = maskHeader.dims[1];
|
|
223
184
|
const maskHeight = maskHeader.dims[2];
|
|
224
185
|
const maskDepth = maskHeader.dims[3];
|
|
225
186
|
if (maskWidth === width && maskHeight === height && maskDepth === depth) {
|
|
226
|
-
const maskedTypedData =
|
|
227
|
-
maskData = maskedTypedData
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
let scale = 1;
|
|
231
|
-
let valueOffset = 0.0;
|
|
232
|
-
if (dataType === "float") {
|
|
233
|
-
//It should be like
|
|
234
|
-
// scl_slope = 0, intensity will be stored in value between 0, 1
|
|
235
|
-
// Otherwise the following
|
|
236
|
-
//y = scl_slope * x + scl_inter
|
|
237
|
-
if (niftiHeader.scl_slope === 0) {
|
|
238
|
-
scale = 255;
|
|
239
|
-
} else {
|
|
240
|
-
valueOffset = niftiHeader.scl_inter;
|
|
241
|
-
}
|
|
242
|
-
} else if (dataType === "uint") {
|
|
243
|
-
scale = 255;
|
|
244
|
-
} else if (dataType === "int16") {
|
|
245
|
-
//scale = 1 / 255;
|
|
246
|
-
if (niftiHeader.scl_slope === 0) {
|
|
247
|
-
scale = 255;
|
|
248
|
-
} else {
|
|
249
|
-
valueOffset = niftiHeader.scl_inter;
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
for (let slice = 0; slice < depth; slice++) {
|
|
253
|
-
const sliceOffset = sliceSize * slice;
|
|
254
|
-
for (let row = 0; row < height; row++) {
|
|
255
|
-
const rowOffset = row * width;
|
|
256
|
-
for (let col = 0; col < width; col++) {
|
|
257
|
-
const offset = sliceOffset + rowOffset + col;
|
|
258
|
-
let value = typedData[offset] * scale + valueOffset;
|
|
259
|
-
if (value < 0) value = 0;
|
|
260
|
-
fullArray[offset * 4] = value;
|
|
261
|
-
fullArray[offset * 4 + 1] = value;
|
|
262
|
-
fullArray[offset * 4 + 2] = value;
|
|
263
|
-
fullArray[offset * 4 + 3] = 255;
|
|
264
|
-
if (maskData) {
|
|
265
|
-
const maskedValue = maskData[offset];
|
|
266
|
-
if (options.hideBlackPixel) {
|
|
267
|
-
//if (maskedValue === 0 && 20 > value) {
|
|
268
|
-
if (maskedValue === 0) {
|
|
269
|
-
fullArray[offset * 4 + 3] = 0;
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
} else if (options.filterByValue) {
|
|
273
|
-
if (options.hideWhitePixel && value === 255) {
|
|
274
|
-
fullArray[offset * 4 + 3] = 0;
|
|
275
|
-
}
|
|
276
|
-
if (options.hideBlackPixel && 2 >= value) {
|
|
277
|
-
fullArray[offset * 4] = 240;
|
|
278
|
-
fullArray[offset * 4 + 1] = 240;
|
|
279
|
-
fullArray[offset * 4 + 2] = 240;
|
|
280
|
-
fullArray[offset * 4 + 3] = 1.0;
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
}
|
|
187
|
+
const maskedTypedData = convertNiftiToUint8Array(maskHeader, maskImage);
|
|
188
|
+
maskData = maskedTypedData;
|
|
284
189
|
}
|
|
285
190
|
}
|
|
191
|
+
|
|
286
192
|
return {
|
|
287
|
-
data:
|
|
193
|
+
data: typedData,
|
|
194
|
+
maskData: maskData,
|
|
288
195
|
width,
|
|
289
196
|
height,
|
|
290
197
|
depth,
|
|
198
|
+
isRGB,
|
|
291
199
|
};
|
|
292
200
|
}
|
|
293
201
|
return undefined;
|
|
294
202
|
}
|
|
295
203
|
|
|
296
|
-
*/
|
|
297
|
-
|
|
298
|
-
function getTypedData(niftiHeader, niftiImage) {
|
|
299
|
-
if (niftiHeader.datatypeCode === nifti.NIFTI1.TYPE_UINT8) {
|
|
300
|
-
return { typedData: new Uint8Array(niftiImage), dataType: "uint" };
|
|
301
|
-
} else if (niftiHeader.datatypeCode === nifti.NIFTI1.TYPE_INT16) {
|
|
302
|
-
return { typedData: new Int16Array(niftiImage), dataType: "int16" };
|
|
303
|
-
} else if (niftiHeader.datatypeCode === nifti.NIFTI1.TYPE_INT32) {
|
|
304
|
-
return { typedData: new Int32Array(niftiImage), dataType: "int32" };
|
|
305
|
-
} else if (niftiHeader.datatypeCode === nifti.NIFTI1.TYPE_FLOAT32) {
|
|
306
|
-
return { typedData: new Float32Array(niftiImage), dataType: "float" };
|
|
307
|
-
} else if (niftiHeader.datatypeCode === nifti.NIFTI1.TYPE_FLOAT64) {
|
|
308
|
-
return { typedData: new Float64Array(niftiImage), dataType: "float" };
|
|
309
|
-
} else if (niftiHeader.datatypeCode === nifti.NIFTI1.TYPE_INT8) {
|
|
310
|
-
return { typedData: new Int8Array(niftiImage), dataType: "int" };
|
|
311
|
-
} else if (niftiHeader.datatypeCode === nifti.NIFTI1.TYPE_UINT16) {
|
|
312
|
-
return { typedData: new Uint16Array(niftiImage), dataType: "int" };
|
|
313
|
-
} else if (niftiHeader.datatypeCode === nifti.NIFTI1.TYPE_UINT32) {
|
|
314
|
-
return { typedData: new Uint32Array(niftiImage), dataType: "int" };
|
|
315
|
-
} else {
|
|
316
|
-
return;
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
|
|
320
204
|
function getSFormTransformation(header, options) {
|
|
321
205
|
if (header?.affine && header.dims) {
|
|
322
206
|
const affine = header.affine;
|
|
@@ -348,26 +232,41 @@ function getTransformationFromHeader(header, options) {
|
|
|
348
232
|
return {position: undefined, scale: undefined}
|
|
349
233
|
}
|
|
350
234
|
|
|
235
|
+
function createDataTexture(data, width, height, depth, isRGB) {
|
|
236
|
+
const dataTexture = new THREE.DataTexture2DArray(
|
|
237
|
+
data, width, height, depth);
|
|
238
|
+
dataTexture.anisotropy = 4;
|
|
239
|
+
if (!isRGB) {
|
|
240
|
+
dataTexture.format = THREE.RedFormat;
|
|
241
|
+
}
|
|
242
|
+
dataTexture.minFilter = THREE.NearestFilter;
|
|
243
|
+
dataTexture.magFilter = THREE.NearestFilter;
|
|
244
|
+
dataTexture.needsUpdate = true;
|
|
245
|
+
return dataTexture;
|
|
246
|
+
}
|
|
247
|
+
|
|
351
248
|
function createTextureArray(sources) {
|
|
352
249
|
if (sources?.data) {
|
|
353
250
|
const tArray = new TextureArray();
|
|
354
|
-
tArray.impl = new
|
|
251
|
+
tArray.impl = new createDataTexture(
|
|
355
252
|
sources.data, sources.width, sources.height, sources.depth);
|
|
356
|
-
tArray.impl.anisotropy = 4;
|
|
357
253
|
tArray.size = {
|
|
358
254
|
width: sources.width,
|
|
359
255
|
height: sources.height,
|
|
360
256
|
depth: sources.depth,
|
|
361
257
|
};
|
|
362
258
|
tArray.isLoading = false;
|
|
363
|
-
tArray.impl.needsUpdate = true;
|
|
364
259
|
return tArray;
|
|
365
260
|
}
|
|
366
261
|
return undefined;
|
|
367
262
|
}
|
|
368
263
|
|
|
369
|
-
function createTexturePrimitives(niftiHeader, sources, useHeaderInfo, textureSettings,
|
|
264
|
+
function createTexturePrimitives(niftiHeader, sources, useHeaderInfo, textureSettings, optionsIn) {
|
|
370
265
|
if (sources?.data) {
|
|
266
|
+
const options = {...defaultOptions};
|
|
267
|
+
if (optionsIn) {
|
|
268
|
+
Object.assign(options, optionsIn);
|
|
269
|
+
}
|
|
371
270
|
const newTexture = new TextureSlides();
|
|
372
271
|
const tArray = createTextureArray(sources);
|
|
373
272
|
if (tArray) {
|
|
@@ -384,36 +283,23 @@ function createTexturePrimitives(niftiHeader, sources, useHeaderInfo, textureSet
|
|
|
384
283
|
settings.locations[0].scale = scale;
|
|
385
284
|
settings.locations[0].position = position;
|
|
386
285
|
}
|
|
387
|
-
/*
|
|
388
|
-
if (niftiHeader.qoffset_x) {
|
|
389
|
-
settings.locations[0].position[0] = niftiHeader.qoffset_x;
|
|
390
|
-
}
|
|
391
|
-
if (niftiHeader.qoffset_y) {
|
|
392
|
-
settings.locations[0].position[1] = niftiHeader.qoffset_y;
|
|
393
|
-
}
|
|
394
|
-
if (niftiHeader.qoffset_y) {
|
|
395
|
-
settings.locations[0].position[2] = niftiHeader.qoffset_z;
|
|
396
|
-
}
|
|
397
|
-
if (niftiHeader.dims) {
|
|
398
|
-
settings.locations[0].scale[0] = niftiHeader.dims[1];
|
|
399
|
-
settings.locations[0].scale[1] = niftiHeader.dims[2];
|
|
400
|
-
settings.locations[0].scale[2] = niftiHeader.dims[3];
|
|
401
|
-
}
|
|
402
|
-
*/
|
|
403
286
|
}
|
|
404
287
|
newTexture.initialise(settings, undefined);
|
|
405
288
|
newTexture.showEdges(0x999999);
|
|
289
|
+
if (sources.maskData && !options.timeEnabled) {
|
|
290
|
+
const maskTexture = createDataTexture(sources.maskData,
|
|
291
|
+
sources.width, sources.height, sources.depth, false);
|
|
292
|
+
newTexture.setMask(maskTexture);
|
|
293
|
+
}
|
|
294
|
+
newTexture.setNumberOfChannels(sources.isRGB ? 3 : 1);
|
|
295
|
+
|
|
406
296
|
return newTexture;
|
|
407
297
|
}
|
|
408
298
|
}
|
|
409
299
|
return undefined;
|
|
410
300
|
}
|
|
411
301
|
|
|
412
|
-
async function getImagesFromURL(url, maskURL
|
|
413
|
-
const options = {...defaultOptions};
|
|
414
|
-
if (optionsIn) {
|
|
415
|
-
Object.assign(options, optionsIn);
|
|
416
|
-
}
|
|
302
|
+
async function getImagesFromURL(url, maskURL) {
|
|
417
303
|
|
|
418
304
|
// try {
|
|
419
305
|
let maskHeader = undefined, maskImage = undefined;
|
|
@@ -427,13 +313,13 @@ async function getImagesFromURL(url, maskURL, optionsIn) {
|
|
|
427
313
|
const response = await fetch(url);
|
|
428
314
|
const buffer = await response.arrayBuffer();
|
|
429
315
|
const {niftiHeader, niftiImage} = readNIFTI(buffer);
|
|
430
|
-
const sources = createSources(niftiHeader, niftiImage, maskHeader, maskImage
|
|
431
|
-
|
|
316
|
+
const sources = createSources(niftiHeader, niftiImage, maskHeader, maskImage);
|
|
432
317
|
return {sources, niftiHeader};
|
|
433
318
|
}
|
|
434
319
|
|
|
435
320
|
|
|
436
|
-
async function createPrimitivesFromNIFTI(
|
|
321
|
+
async function createPrimitivesFromNIFTI(
|
|
322
|
+
url, useHeaderInfo, maskURL, textureSettings, optionsIn) {
|
|
437
323
|
let timeEnabled = false;
|
|
438
324
|
let textureP = undefined;
|
|
439
325
|
let firstURL = url;
|
|
@@ -454,6 +340,7 @@ async function createPrimitivesFromNIFTI(url, useHeaderInfo, maskURL, textureSet
|
|
|
454
340
|
}
|
|
455
341
|
textureP.timeEnabled = true;
|
|
456
342
|
}
|
|
343
|
+
|
|
457
344
|
return textureP;
|
|
458
345
|
}
|
|
459
346
|
|
|
@@ -21,8 +21,12 @@ const TextureSlides = function (textureIn) {
|
|
|
21
21
|
this.morph.userData = this;
|
|
22
22
|
let edgesLine = undefined;
|
|
23
23
|
let flipY = true;
|
|
24
|
+
let flipZ = false;
|
|
24
25
|
let brightness = 0.0;
|
|
25
26
|
let contrast = 1.0;
|
|
27
|
+
let nChannels = 1;
|
|
28
|
+
let maskTexture = undefined;
|
|
29
|
+
let maskEnabled = false;
|
|
26
30
|
let discardAlpha = true;
|
|
27
31
|
let lt0 = 0;
|
|
28
32
|
let lt1 = 0;
|
|
@@ -124,6 +128,10 @@ const TextureSlides = function (textureIn) {
|
|
|
124
128
|
uniforms.discardAlpha.value = discardAlpha;
|
|
125
129
|
uniforms.depth.value = this.texture.size.depth;
|
|
126
130
|
uniforms.flipY.value = flipY;
|
|
131
|
+
uniforms.flipZ.value = flipZ;
|
|
132
|
+
uniforms.mask.value = maskTexture;
|
|
133
|
+
uniforms.maskEnabled.value = maskEnabled;
|
|
134
|
+
uniforms.nChannels.value = nChannels;
|
|
127
135
|
const options = {
|
|
128
136
|
fs: shader.fs,
|
|
129
137
|
vs: shader.vs,
|
|
@@ -330,6 +338,9 @@ const TextureSlides = function (textureIn) {
|
|
|
330
338
|
if ("flipY" in locations[0]) {
|
|
331
339
|
flipY = locations[0].flipY;
|
|
332
340
|
}
|
|
341
|
+
if ("flipZ" in locations[0]) {
|
|
342
|
+
flipZ = locations[0].flipZ;
|
|
343
|
+
}
|
|
333
344
|
}
|
|
334
345
|
this.createSlides(textureData.settings.slides);
|
|
335
346
|
if (finishCallback != undefined && (typeof finishCallback == 'function')) {
|
|
@@ -351,36 +362,34 @@ const TextureSlides = function (textureIn) {
|
|
|
351
362
|
edgesLine.visible = true;
|
|
352
363
|
}
|
|
353
364
|
|
|
354
|
-
this.isAlphaPixelDiscarded = () => {
|
|
355
|
-
return discardAlpha;
|
|
356
|
-
}
|
|
357
365
|
|
|
358
|
-
this.
|
|
359
|
-
discardAlpha = flag;
|
|
366
|
+
this.setUniformsValue = (name, val) => {
|
|
360
367
|
this.morph.children.forEach((mesh) => {
|
|
361
368
|
const material = mesh.material;
|
|
362
369
|
if (material.type === "ShaderMaterial") {
|
|
363
370
|
const uniforms = material.uniforms;
|
|
364
|
-
uniforms.
|
|
371
|
+
uniforms[name].value = val;
|
|
365
372
|
material.needsUpdate = true;
|
|
366
373
|
}
|
|
367
374
|
});
|
|
368
375
|
}
|
|
369
376
|
|
|
377
|
+
this.isAlphaPixelDiscarded = () => {
|
|
378
|
+
return discardAlpha;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
this.discardAlphaPixel = (flag) => {
|
|
382
|
+
discardAlpha = flag;
|
|
383
|
+
this.setUniformsValue("discardAlpha", discardAlpha);
|
|
384
|
+
}
|
|
385
|
+
|
|
370
386
|
this.getBrightness = () => {
|
|
371
387
|
return brightness;
|
|
372
388
|
}
|
|
373
389
|
|
|
374
390
|
this.setBrightness = (brightnessIn) => {
|
|
375
391
|
brightness = brightnessIn;
|
|
376
|
-
this.
|
|
377
|
-
const material = mesh.material;
|
|
378
|
-
if (material.type === "ShaderMaterial") {
|
|
379
|
-
const uniforms = material.uniforms;
|
|
380
|
-
uniforms.brightness.value = brightness;
|
|
381
|
-
material.needsUpdate = true;
|
|
382
|
-
}
|
|
383
|
-
});
|
|
392
|
+
this.setUniformsValue("brightness", brightness);
|
|
384
393
|
}
|
|
385
394
|
|
|
386
395
|
this.getContrast = () => {
|
|
@@ -390,17 +399,29 @@ const TextureSlides = function (textureIn) {
|
|
|
390
399
|
this.setContrast = (contrastIn) => {
|
|
391
400
|
if (contrast >= 0 ) {
|
|
392
401
|
contrast = contrastIn;
|
|
393
|
-
this.
|
|
394
|
-
const material = mesh.material;
|
|
395
|
-
if (material.type === "ShaderMaterial") {
|
|
396
|
-
const uniforms = material.uniforms;
|
|
397
|
-
uniforms.contrast.value = contrast;
|
|
398
|
-
material.needsUpdate = true;
|
|
399
|
-
}
|
|
400
|
-
});
|
|
402
|
+
this.setUniformsValue("contrast", contrast);
|
|
401
403
|
}
|
|
402
404
|
}
|
|
403
405
|
|
|
406
|
+
this.getNumberOfChannels = () => {
|
|
407
|
+
return nChannels;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
this.setNumberOfChannels = (numbersIn) => {
|
|
411
|
+
nChannels = numbersIn;
|
|
412
|
+
this.setUniformsValue("nChannels", nChannels);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
this.getMask = () => {
|
|
416
|
+
return maskTexture;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
this.setMask = (maskTextureIn) => {
|
|
420
|
+
maskTexture = maskTextureIn;
|
|
421
|
+
this.setUniformsValue("mask", maskTexture);
|
|
422
|
+
this.setUniformsValue("maskEnabled", maskTexture ? true : false);
|
|
423
|
+
}
|
|
424
|
+
|
|
404
425
|
this.hideEdges = () => {
|
|
405
426
|
if (edgesLine) {
|
|
406
427
|
edgesLine.visible = false;
|
|
@@ -20,6 +20,7 @@ const TextureSlides = function (textureIn) {
|
|
|
20
20
|
this.morph.userData = this;
|
|
21
21
|
let edgesLine = undefined;
|
|
22
22
|
let flipY = true;
|
|
23
|
+
let flipZ = false;
|
|
23
24
|
|
|
24
25
|
/**
|
|
25
26
|
@typedef SLIDE_SETTINGS
|
|
@@ -113,6 +114,7 @@ const TextureSlides = function (textureIn) {
|
|
|
113
114
|
uniforms.diffuse.value = this.texture.impl;
|
|
114
115
|
uniforms.depth.value = this.texture.size.depth;
|
|
115
116
|
uniforms.flipY.value = flipY;
|
|
117
|
+
uniforms.flipZ.value = flipZ;
|
|
116
118
|
|
|
117
119
|
const options = {
|
|
118
120
|
fs: shader.fs,
|
|
@@ -284,6 +286,9 @@ const TextureSlides = function (textureIn) {
|
|
|
284
286
|
if ("flipY" in locations[0]) {
|
|
285
287
|
flipY = locations[0].flipY;
|
|
286
288
|
}
|
|
289
|
+
if ("flipZ" in locations[0]) {
|
|
290
|
+
flipZ = locations[0].flipZ;
|
|
291
|
+
}
|
|
287
292
|
}
|
|
288
293
|
this.createSlides(textureData.settings.slides);
|
|
289
294
|
if (finishCallback != undefined && (typeof finishCallback == 'function')) {
|
|
@@ -10,27 +10,35 @@ precision highp sampler2DArray;
|
|
|
10
10
|
|
|
11
11
|
uniform sampler2DArray diffuse0;
|
|
12
12
|
uniform sampler2DArray diffuse1;
|
|
13
|
+
uniform sampler2DArray mask;
|
|
13
14
|
uniform bool discardAlpha;
|
|
15
|
+
uniform bool maskEnabled;
|
|
14
16
|
uniform float brightness;
|
|
15
17
|
uniform float contrast;
|
|
16
18
|
uniform float time;
|
|
19
|
+
uniform int nChannels;
|
|
17
20
|
in vec3 vUw;
|
|
18
21
|
|
|
19
22
|
out vec4 outColor;
|
|
20
23
|
|
|
21
24
|
void main() {
|
|
22
25
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
float color0 = texture( diffuse0, vUw ).r;
|
|
27
|
+
float color1 = texture( diffuse1, vUw ).r;
|
|
28
|
+
float color = mix(color0, color1, time);
|
|
29
|
+
// discard if alpha is zero
|
|
30
|
+
if (maskEnabled && discardAlpha) {
|
|
31
|
+
float mask = texture(mask, vUw).r;
|
|
32
|
+
if (mask == 0.0) discard;
|
|
33
|
+
}
|
|
26
34
|
|
|
27
35
|
// discard if alpha is zero
|
|
28
|
-
if (discardAlpha && color.a == 0.0) discard;
|
|
36
|
+
//if (discardAlpha && color.a == 0.0) discard;
|
|
29
37
|
// Apply brightness
|
|
30
|
-
vec3 brightenedColor = color
|
|
38
|
+
vec3 brightenedColor = vec3(color) + vec3(brightness);
|
|
31
39
|
// Apply contrast
|
|
32
40
|
vec3 contrastedColor = (brightenedColor - vec3(0.5)) * contrast + vec3(0.5);
|
|
33
|
-
outColor = vec4(contrastedColor,
|
|
41
|
+
outColor = vec4(contrastedColor, 1.0);
|
|
34
42
|
}
|
|
35
43
|
`;
|
|
36
44
|
|
|
@@ -41,6 +49,7 @@ uniform float depth;
|
|
|
41
49
|
uniform vec3 slide;
|
|
42
50
|
uniform int direction;
|
|
43
51
|
uniform bool flipY;
|
|
52
|
+
uniform bool flipZ;
|
|
44
53
|
|
|
45
54
|
void main() {
|
|
46
55
|
|
|
@@ -56,6 +65,8 @@ void main() {
|
|
|
56
65
|
|
|
57
66
|
if (flipY)
|
|
58
67
|
slidePos.y = 1.0 - slidePos.y;
|
|
68
|
+
if (flipZ)
|
|
69
|
+
slidePos.z = 1.0 - slidePos.z;
|
|
59
70
|
|
|
60
71
|
vUw.xyz = vec3(slidePos.x, slidePos.y, slidePos.z * depth);
|
|
61
72
|
|
|
@@ -70,8 +81,12 @@ const getUniforms = function() {
|
|
|
70
81
|
discardAlpha: {value: true},
|
|
71
82
|
diffuse0: { value: undefined },
|
|
72
83
|
diffuse1: { value: undefined },
|
|
73
|
-
direction: {value: 1},
|
|
74
|
-
flipY: { value: true},
|
|
84
|
+
direction: { value: 1 },
|
|
85
|
+
flipY: { value: true },
|
|
86
|
+
flipZ: { value: false },
|
|
87
|
+
nChannels: { value: 1 },
|
|
88
|
+
mask: { value: undefined },
|
|
89
|
+
maskEnabled: { value: false },
|
|
75
90
|
slide: { value: new THREE.Vector3( 0, 0, 1 ) },
|
|
76
91
|
time: { value: 0 }
|
|
77
92
|
};
|
|
@@ -1455,7 +1455,7 @@ class GLTFWriter {
|
|
|
1455
1455
|
let warned = false;
|
|
1456
1456
|
|
|
1457
1457
|
for ( const attributeName in geometry.morphAttributes ) {
|
|
1458
|
-
|
|
1458
|
+
|
|
1459
1459
|
|
|
1460
1460
|
// glTF 2.0 morph supports only POSITION/NORMAL/TANGENT.
|
|
1461
1461
|
// Three.js doesn't support TANGENT yet.
|
|
@@ -1502,7 +1502,7 @@ class GLTFWriter {
|
|
|
1502
1502
|
if ( ! geometry.morphTargetsRelative ) {
|
|
1503
1503
|
|
|
1504
1504
|
if (baseAttribute) {
|
|
1505
|
-
|
|
1505
|
+
|
|
1506
1506
|
for ( let j = 0, jl = attribute.count; j < jl; j ++ ) {
|
|
1507
1507
|
|
|
1508
1508
|
if (baseAttribute.count > j) {
|