zincjs 2.4.0 → 2.4.1-beta.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.js +3 -486
- package/build/zinc.js.map +1 -1
- package/package.json +3 -4
- package/src/controls.js +1 -1
- package/src/geometryCSG.js +1 -1
- package/src/glyphsetCSG.js +1 -1
- package/src/loaders/GLTFToZincJSLoader.js +1 -1
- package/src/loaders/JSONLoader.js +204 -377
- package/src/loaders/OBJLoader.js +1 -1
- package/src/loaders/STLLoader.js +1 -1
- package/src/loaders/niftiReader.js +6 -4
- package/src/loaders/primitivesLoader.js +7 -9
- package/src/minimap.js +1 -1
- package/src/primitives/geometry.js +11 -29
- package/src/primitives/glyph.js +1 -1
- package/src/primitives/glyphset.js +2 -4
- package/src/primitives/label.js +1 -1
- package/src/primitives/lines.js +12 -5
- package/src/primitives/lines2.js +4 -8
- package/src/primitives/lod.js +9 -3
- package/src/primitives/marker.js +1 -1
- package/src/primitives/markerCluster.js +1 -1
- package/src/primitives/pointset.js +368 -52
- package/src/primitives/textureSlides.js +59 -16
- package/src/primitives/textureVolume.js +1 -1
- package/src/primitives/tubeLines.js +20 -3
- package/src/primitives/zincObject.js +13 -1
- package/src/region.js +1 -1
- package/src/renderer.js +17 -13
- package/src/scene.js +20 -8
- package/src/sceneLoader.js +1 -1
- package/src/shaders/rgbVolumeRender.js +1 -1
- package/src/shaders/textureSlide.js +1 -1
- package/src/texture/texture.js +1 -1
- package/src/texture/textureArray.js +9 -2
- package/src/three/GLTFExporter.js +55 -2
- package/src/three/InstancedPoints.js +73 -0
- package/src/three/Loader.js +1 -1
- package/src/three/line/Line.js +1 -1
- package/src/three/line/LineSegments.js +1 -1
- package/src/three/line/LineSegments2.js +24 -39
- package/src/three/line/LineSegmentsGeometry.js +1 -1
- package/src/tls/morphColorMaterial.js +65 -0
- package/src/tls/pointsMaterial.js +79 -0
- package/src/tls/textureSlides.js +126 -0
- package/src/utilities.js +47 -70
- package/src/videoHandler.js +1 -2
- package/src/workers/geometryCSG.worker.js +1 -1
- package/src/workers/geometryCSGInternal.js +1 -1
- package/src/zinc.js +1 -1
- package/vite.config.js +3 -1
- package/src/primitives/augmentShader.js +0 -45
- package/src/three/Geometry.js +0 -2176
- package/src/three/Points.js +0 -224
- package/src/three/line/LineMaterial.js +0 -726
|
@@ -1,15 +1,97 @@
|
|
|
1
|
-
import * as THREE from 'three';
|
|
1
|
+
import * as THREE from 'three/webgpu';
|
|
2
2
|
import { Label } from './label';
|
|
3
|
-
import {
|
|
3
|
+
import { InstancedPoints } from '../three/InstancedPoints';
|
|
4
4
|
import { ZincObject } from './zincObject';
|
|
5
5
|
import { getCircularTexture, toBufferGeometry } from '../utilities';
|
|
6
|
-
import {
|
|
6
|
+
import { createInstancedPointsMaterial } from '../tls/pointsMaterial';
|
|
7
|
+
|
|
8
|
+
//Maximum number of points a programmatically built (addPoints) Pointset can
|
|
9
|
+
//hold - the InstancedMesh backing it is allocated once with this capacity.
|
|
10
|
+
const DEFAULT_CAPACITY = 500;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Build the shared quad geometry every point instance is drawn with plus
|
|
14
|
+
* the per instance `instancePosition` attribute used to place it - the
|
|
15
|
+
* actual on screen billboard offset and constant pixel sizing is computed
|
|
16
|
+
* in {@link createInstancedPointsMaterial}.
|
|
17
|
+
*/
|
|
18
|
+
const createPointQuadGeometry = (capacity) => {
|
|
19
|
+
const geometry = new THREE.PlaneGeometry(1, 1);
|
|
20
|
+
const instancePosition = new THREE.InstancedBufferAttribute(
|
|
21
|
+
new Float32Array(capacity * 3), 3);
|
|
22
|
+
instancePosition.setUsage(THREE.DynamicDrawUsage);
|
|
23
|
+
geometry.setAttribute('instancePosition', instancePosition);
|
|
24
|
+
return geometry;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Extract the per time step, flat [x0, y0, z0, x1, y1, z1, ...] position
|
|
29
|
+
* arrays from a (possibly time varying) BufferGeometry. Index 0 is always
|
|
30
|
+
* the base frame, subsequent entries come from `morphAttributes.position`.
|
|
31
|
+
*/
|
|
32
|
+
const extractPositionFrames = (geometry, localTimeEnabled) => {
|
|
33
|
+
const positionAttribute = geometry.getAttribute('position');
|
|
34
|
+
const frames = [Float32Array.from(positionAttribute.array)];
|
|
35
|
+
const morphPositions = geometry.morphAttributes.position;
|
|
36
|
+
if (localTimeEnabled && morphPositions && morphPositions.length > 0) {
|
|
37
|
+
const relative = geometry.morphTargetsRelative;
|
|
38
|
+
morphPositions.forEach((attribute) => {
|
|
39
|
+
if (relative) {
|
|
40
|
+
const frame = new Float32Array(positionAttribute.array.length);
|
|
41
|
+
for (let i = 0; i < frame.length; i++) {
|
|
42
|
+
frame[i] = positionAttribute.array[i] + attribute.array[i];
|
|
43
|
+
}
|
|
44
|
+
frames.push(frame);
|
|
45
|
+
} else {
|
|
46
|
+
frames.push(Float32Array.from(attribute.array));
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
return frames;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Extract the per time step, flat [r0, g0, b0, r1, g1, b1, ...] colour
|
|
55
|
+
* arrays from a time varying BufferGeometry. Returns undefined unless
|
|
56
|
+
* localMorphColour is requested and the geometry actually carries morph
|
|
57
|
+
* colour targets, mirroring the legacy vertexColors/onBeforeCompile gating.
|
|
58
|
+
*/
|
|
59
|
+
const extractColorFrames = (geometry, localMorphColour) => {
|
|
60
|
+
const morphColors = geometry.morphAttributes.color;
|
|
61
|
+
if (localMorphColour && morphColors && morphColors.length > 0) {
|
|
62
|
+
const colorAttribute = geometry.getAttribute('color');
|
|
63
|
+
const frames = [];
|
|
64
|
+
frames.push(colorAttribute ? Float32Array.from(colorAttribute.array) :
|
|
65
|
+
new Float32Array(morphColors[0].array.length));
|
|
66
|
+
morphColors.forEach((attribute) => {
|
|
67
|
+
frames.push(Float32Array.from(attribute.array));
|
|
68
|
+
});
|
|
69
|
+
return frames;
|
|
70
|
+
}
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Extract the plain, non-morph per-vertex 'color' attribute (if any) so a
|
|
76
|
+
* Pointset still shows static vertex colours from the file when there is
|
|
77
|
+
* no colour animation to blend between.
|
|
78
|
+
*/
|
|
79
|
+
const extractStaticColorFrame = (geometry) => {
|
|
80
|
+
const colorAttribute = geometry.getAttribute('color');
|
|
81
|
+
return colorAttribute ? Float32Array.from(colorAttribute.array) : undefined;
|
|
82
|
+
}
|
|
7
83
|
|
|
8
84
|
/**
|
|
9
85
|
* Provides an object which stores points and provides method which controls its position.
|
|
10
86
|
* This is created when a valid json file containing point is read into a {@link Zinc.Scene}
|
|
11
87
|
* object.
|
|
12
88
|
*
|
|
89
|
+
* Points are rendered as an instanced mesh - every point is one instance of a
|
|
90
|
+
* small quad billboarded towards the camera and kept at a constant size in
|
|
91
|
+
* pixels on the GPU. Time varying vertex colour and morph vertex positions
|
|
92
|
+
* are supported by blending between per time step position/colour arrays on
|
|
93
|
+
* the CPU and writing the result into the instance attributes every frame.
|
|
94
|
+
*
|
|
13
95
|
* @class
|
|
14
96
|
* @author Alan Wu
|
|
15
97
|
* @return {Pointset}
|
|
@@ -22,30 +104,131 @@ const Pointset = function () {
|
|
|
22
104
|
let labelDepthTest = false;
|
|
23
105
|
let fontWeight = 500;
|
|
24
106
|
let labelVisibility = true;
|
|
107
|
+
let positionFrames = [];
|
|
108
|
+
let colorFrames = undefined;
|
|
109
|
+
let morphVertices = false;
|
|
110
|
+
let morphColours = false;
|
|
111
|
+
const _tempColor = new THREE.Color();
|
|
25
112
|
|
|
26
113
|
/**
|
|
27
|
-
*
|
|
114
|
+
* Blend positionFrames/colorFrames based on the current inbuildTime and
|
|
115
|
+
* write the result into the instancePosition/instanceColor attributes.
|
|
116
|
+
*/
|
|
117
|
+
const updateMorphPointset = () => {
|
|
118
|
+
const mesh = this.getMorph();
|
|
119
|
+
if (!mesh || positionFrames.length === 0) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const activeCount = mesh.count;
|
|
123
|
+
const totalSteps = positionFrames.length;
|
|
124
|
+
let bottomFrame = 0;
|
|
125
|
+
let topFrame = 0;
|
|
126
|
+
let proportion = 1;
|
|
127
|
+
if (totalSteps > 1) {
|
|
128
|
+
const currentTime = this.inbuildTime / this.duration * (totalSteps - 1);
|
|
129
|
+
bottomFrame = Math.floor(currentTime);
|
|
130
|
+
topFrame = Math.ceil(currentTime);
|
|
131
|
+
proportion = 1 - (currentTime - bottomFrame);
|
|
132
|
+
}
|
|
133
|
+
if (morphVertices) {
|
|
134
|
+
const bottomPositions = positionFrames[bottomFrame];
|
|
135
|
+
const topPositions = positionFrames[topFrame];
|
|
136
|
+
const positions = mesh.pointPositions;
|
|
137
|
+
const length = activeCount * 3;
|
|
138
|
+
for (let i = 0; i < length; i++) {
|
|
139
|
+
positions[i] = proportion * bottomPositions[i] + (1 - proportion) * topPositions[i];
|
|
140
|
+
}
|
|
141
|
+
const instancePosition = mesh.geometry.getAttribute('instancePosition');
|
|
142
|
+
instancePosition.array.set(positions.subarray(0, length));
|
|
143
|
+
instancePosition.needsUpdate = true;
|
|
144
|
+
this.boundingBoxUpdateRequired = true;
|
|
145
|
+
}
|
|
146
|
+
if (morphColours && colorFrames) {
|
|
147
|
+
const bottomColors = colorFrames[Math.min(bottomFrame, colorFrames.length - 1)];
|
|
148
|
+
const topColors = colorFrames[Math.min(topFrame, colorFrames.length - 1)];
|
|
149
|
+
for (let i = 0; i < activeCount; i++) {
|
|
150
|
+
const index = i * 3;
|
|
151
|
+
_tempColor.setRGB(
|
|
152
|
+
proportion * bottomColors[index] + (1 - proportion) * topColors[index],
|
|
153
|
+
proportion * bottomColors[index + 1] + (1 - proportion) * topColors[index + 1],
|
|
154
|
+
proportion * bottomColors[index + 2] + (1 - proportion) * topColors[index + 2],
|
|
155
|
+
);
|
|
156
|
+
mesh.setColorAt(i, _tempColor);
|
|
157
|
+
}
|
|
158
|
+
mesh.instanceColor.needsUpdate = true;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Create the pointset using geometry and material.
|
|
28
164
|
*
|
|
29
165
|
* @param {THREE.Geomtry} geometryIn - Geometry of points to be rendered.
|
|
30
|
-
* @param {THREE.Material} materialIn - Material to be set for the
|
|
166
|
+
* @param {THREE.Material} materialIn - Material to be set for the points,
|
|
167
|
+
* only its colour/opacity/alphaTest are used.
|
|
31
168
|
* @param {Object} options - Provide various options
|
|
32
|
-
* @param {Boolean} options.localTimeEnabled - A flag to indicate either the
|
|
33
|
-
* time dependent.
|
|
169
|
+
* @param {Boolean} options.localTimeEnabled - A flag to indicate either the points'
|
|
170
|
+
* positions are time dependent.
|
|
34
171
|
* @param {Boolean} options.localMorphColour - A flag to indicate either the colour is
|
|
35
172
|
* time dependent.
|
|
36
173
|
*/
|
|
37
174
|
this.createMesh = (geometryIn, materialIn, options) => {
|
|
38
|
-
if (geometryIn
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
175
|
+
if (!geometryIn || !materialIn) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
const geometry = toBufferGeometry(geometryIn, options);
|
|
179
|
+
positionFrames = extractPositionFrames(geometry, options.localTimeEnabled);
|
|
180
|
+
colorFrames = extractColorFrames(geometry, options.localMorphColour);
|
|
181
|
+
morphVertices = positionFrames.length > 1;
|
|
182
|
+
morphColours = !!colorFrames;
|
|
183
|
+
this.timeEnabled = morphVertices;
|
|
184
|
+
this.morphColour = morphColours;
|
|
185
|
+
|
|
186
|
+
const numberOfPoints = positionFrames[0].length / 3;
|
|
187
|
+
const quadGeometry = createPointQuadGeometry(numberOfPoints);
|
|
188
|
+
const material = createInstancedPointsMaterial(getCircularTexture());
|
|
189
|
+
if (materialIn.color) {
|
|
190
|
+
material.color.copy(materialIn.color);
|
|
191
|
+
}
|
|
192
|
+
if (materialIn.opacity !== undefined) {
|
|
193
|
+
material.opacity = materialIn.opacity;
|
|
194
|
+
}
|
|
195
|
+
material.transparent = !!materialIn.transparent;
|
|
196
|
+
material.alphaTest = materialIn.alphaTest !== undefined ? materialIn.alphaTest : 0.5;
|
|
197
|
+
|
|
198
|
+
const mesh = new InstancedPoints(quadGeometry, material, numberOfPoints);
|
|
199
|
+
mesh.count = numberOfPoints;
|
|
200
|
+
mesh.pointPositions = new Float32Array(numberOfPoints * 3);
|
|
201
|
+
mesh.pointPositions.set(positionFrames[0]);
|
|
202
|
+
mesh.userData = this;
|
|
203
|
+
this.drawRange = numberOfPoints;
|
|
204
|
+
|
|
205
|
+
const instancePosition = quadGeometry.getAttribute('instancePosition');
|
|
206
|
+
instancePosition.array.set(positionFrames[0]);
|
|
207
|
+
instancePosition.needsUpdate = true;
|
|
208
|
+
|
|
209
|
+
const initialColorFrame = colorFrames ? colorFrames[0] : extractStaticColorFrame(geometry);
|
|
210
|
+
if (initialColorFrame) {
|
|
211
|
+
for (let i = 0; i < numberOfPoints; i++) {
|
|
212
|
+
_tempColor.setRGB(initialColorFrame[i * 3], initialColorFrame[i * 3 + 1], initialColorFrame[i * 3 + 2]);
|
|
213
|
+
mesh.setColorAt(i, _tempColor);
|
|
44
214
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
this.setMorph(mesh);
|
|
218
|
+
geometry.dispose();
|
|
219
|
+
this.boundingBoxUpdateRequired = true;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Adapt a plain THREE.Points object (e.g. one parsed from a GLTF file)
|
|
224
|
+
* into this Pointset's instanced mesh representation.
|
|
225
|
+
*/
|
|
226
|
+
this.setMesh = (mesh, localTimeEnabled, localMorphColour) => {
|
|
227
|
+
this.createMesh(mesh.geometry, mesh.material,
|
|
228
|
+
{ localTimeEnabled, localMorphColour });
|
|
229
|
+
const newMesh = this.getMorph();
|
|
230
|
+
if (newMesh) {
|
|
231
|
+
newMesh.name = mesh.name;
|
|
49
232
|
}
|
|
50
233
|
}
|
|
51
234
|
|
|
@@ -88,31 +271,45 @@ const Pointset = function () {
|
|
|
88
271
|
*/
|
|
89
272
|
this.addPoints = (coords, labels, colour) => {
|
|
90
273
|
if (coords && coords.length > 0) {
|
|
91
|
-
let current = this.drawRange;
|
|
92
|
-
if (current === -1) {
|
|
93
|
-
current = 0;
|
|
94
|
-
}
|
|
95
|
-
const geometry = this.addVertices(coords);
|
|
96
274
|
let mesh = this.getMorph();
|
|
275
|
+
let current = this.drawRange;
|
|
97
276
|
if (!mesh) {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
277
|
+
current = 0;
|
|
278
|
+
this.drawRange = 0;
|
|
279
|
+
const quadGeometry = createPointQuadGeometry(DEFAULT_CAPACITY);
|
|
280
|
+
const material = createInstancedPointsMaterial(getCircularTexture());
|
|
281
|
+
material.color.set(colour);
|
|
282
|
+
material.alphaTest = 0.5;
|
|
283
|
+
mesh = new InstancedPoints(quadGeometry, material, DEFAULT_CAPACITY);
|
|
284
|
+
mesh.pointPositions = new Float32Array(DEFAULT_CAPACITY * 3);
|
|
285
|
+
mesh.count = 0;
|
|
286
|
+
mesh.userData = this;
|
|
287
|
+
this.setMorph(mesh);
|
|
103
288
|
}
|
|
289
|
+
const instancePosition = mesh.geometry.getAttribute('instancePosition');
|
|
290
|
+
coords.forEach(coord => {
|
|
291
|
+
if (this.drawRange < DEFAULT_CAPACITY) {
|
|
292
|
+
instancePosition.setXYZ(this.drawRange, coord[0], coord[1], coord[2]);
|
|
293
|
+
mesh.pointPositions[this.drawRange * 3] = coord[0];
|
|
294
|
+
mesh.pointPositions[this.drawRange * 3 + 1] = coord[1];
|
|
295
|
+
mesh.pointPositions[this.drawRange * 3 + 2] = coord[2];
|
|
296
|
+
this.drawRange++;
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
instancePosition.needsUpdate = true;
|
|
300
|
+
mesh.count = this.drawRange;
|
|
104
301
|
|
|
105
302
|
let end = current + coords.length;
|
|
106
303
|
let index = 0;
|
|
107
304
|
if ((Array.isArray(labels) && labels.length === coords.length) ||
|
|
108
305
|
(typeof labels === "string")) {
|
|
109
|
-
const size = labelSets.length;
|
|
110
306
|
for (current; current + index < end;) {
|
|
111
307
|
const labelText = typeof labels === "string" ? labels : labels[index];
|
|
112
308
|
addLabel(current + index, coords[index], labelText, this._lod._material.color);
|
|
113
309
|
index++;
|
|
114
310
|
}
|
|
115
311
|
}
|
|
312
|
+
this.boundingBoxUpdateRequired = true;
|
|
116
313
|
if (this.region) this.region.pickableUpdateRequired = true;
|
|
117
314
|
}
|
|
118
315
|
}
|
|
@@ -206,14 +403,15 @@ const Pointset = function () {
|
|
|
206
403
|
}
|
|
207
404
|
}
|
|
208
405
|
/**
|
|
209
|
-
* Set the size of the points.
|
|
406
|
+
* Set the size of the points in pixels.
|
|
210
407
|
*
|
|
211
408
|
* @param {Number} size - size to be set.
|
|
212
409
|
*/
|
|
213
410
|
this.setSize = size => {
|
|
214
411
|
if (this.morph && this.morph.material) {
|
|
215
412
|
this.morph.material.size = size;
|
|
216
|
-
this.morph.material.
|
|
413
|
+
this.morph.material.userData.uniforms.pointSize.value = size;
|
|
414
|
+
this.morph.pointSize = size;
|
|
217
415
|
}
|
|
218
416
|
}
|
|
219
417
|
|
|
@@ -226,7 +424,7 @@ const Pointset = function () {
|
|
|
226
424
|
this.setSizeAttenuation = flag => {
|
|
227
425
|
if (this.morph && this.morph.material) {
|
|
228
426
|
this.morph.material.sizeAttenuation = flag;
|
|
229
|
-
this.morph.material.
|
|
427
|
+
this.morph.material.userData.uniforms.sizeAttenuation.value = flag ? 1 : 0;
|
|
230
428
|
}
|
|
231
429
|
}
|
|
232
430
|
|
|
@@ -234,12 +432,12 @@ const Pointset = function () {
|
|
|
234
432
|
* Get vertices at index
|
|
235
433
|
*/
|
|
236
434
|
this.getVerticesByIndex = function(index) {
|
|
237
|
-
|
|
238
|
-
|
|
435
|
+
const mesh = this.getMorph();
|
|
436
|
+
if (mesh && index >= 0 && this.drawRange > index) {
|
|
239
437
|
return [
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
438
|
+
mesh.pointPositions[index * 3],
|
|
439
|
+
mesh.pointPositions[index * 3 + 1],
|
|
440
|
+
mesh.pointPositions[index * 3 + 2],
|
|
243
441
|
];
|
|
244
442
|
}
|
|
245
443
|
return undefined;
|
|
@@ -250,24 +448,32 @@ const Pointset = function () {
|
|
|
250
448
|
*/
|
|
251
449
|
this.editVertices = function(coords, i) {
|
|
252
450
|
if (coords && coords.length) {
|
|
253
|
-
|
|
451
|
+
const mesh = this.getMorph();
|
|
254
452
|
const maxIndex = i + coords.length - 1;
|
|
255
453
|
if (!mesh || 0 > i || maxIndex >= this.drawRange) {
|
|
256
454
|
return;
|
|
257
455
|
} else {
|
|
258
|
-
const
|
|
456
|
+
const instancePosition = mesh.geometry.getAttribute('instancePosition');
|
|
259
457
|
let index = i;
|
|
260
458
|
coords.forEach(coord => {
|
|
261
459
|
const label = labelSets[index];
|
|
262
460
|
if (label) {
|
|
263
461
|
label.setPosition(coord[0], coord[1], coord[2]);
|
|
264
462
|
}
|
|
265
|
-
|
|
266
|
-
|
|
463
|
+
instancePosition.setXYZ(index, coord[0], coord[1], coord[2]);
|
|
464
|
+
mesh.pointPositions[index * 3] = coord[0];
|
|
465
|
+
mesh.pointPositions[index * 3 + 1] = coord[1];
|
|
466
|
+
mesh.pointPositions[index * 3 + 2] = coord[2];
|
|
467
|
+
//Keep the base frame consistent for any pointset that also
|
|
468
|
+
//happens to be time varying.
|
|
469
|
+
if (positionFrames[0]) {
|
|
470
|
+
positionFrames[0][index * 3] = coord[0];
|
|
471
|
+
positionFrames[0][index * 3 + 1] = coord[1];
|
|
472
|
+
positionFrames[0][index * 3 + 2] = coord[2];
|
|
473
|
+
}
|
|
474
|
+
index++;
|
|
267
475
|
});
|
|
268
|
-
|
|
269
|
-
mesh.geometry.computeBoundingBox();
|
|
270
|
-
mesh.geometry.computeBoundingSphere();
|
|
476
|
+
instancePosition.needsUpdate = true;
|
|
271
477
|
this.boundingBoxUpdateRequired = true;
|
|
272
478
|
}
|
|
273
479
|
}
|
|
@@ -277,10 +483,23 @@ const Pointset = function () {
|
|
|
277
483
|
* Delete a vertex in index.
|
|
278
484
|
*/
|
|
279
485
|
this.deleteVertices = function(index) {
|
|
280
|
-
|
|
281
|
-
if (
|
|
282
|
-
|
|
486
|
+
const mesh = this.getMorph();
|
|
487
|
+
if (!mesh || 0 > index || index >= this.drawRange) {
|
|
488
|
+
return this.drawRange;
|
|
489
|
+
}
|
|
490
|
+
const instancePosition = mesh.geometry.getAttribute('instancePosition');
|
|
491
|
+
const end = this.drawRange - 1;
|
|
492
|
+
instancePosition.array.copyWithin(index * 3, (index + 1) * 3, this.drawRange * 3);
|
|
493
|
+
mesh.pointPositions.copyWithin(index * 3, (index + 1) * 3, this.drawRange * 3);
|
|
494
|
+
if (mesh.instanceColor) {
|
|
495
|
+
mesh.instanceColor.array.copyWithin(index * 3, (index + 1) * 3, this.drawRange * 3);
|
|
496
|
+
mesh.instanceColor.needsUpdate = true;
|
|
283
497
|
}
|
|
498
|
+
instancePosition.needsUpdate = true;
|
|
499
|
+
this.drawRange = end;
|
|
500
|
+
mesh.count = this.drawRange;
|
|
501
|
+
removeLabel(index);
|
|
502
|
+
this.boundingBoxUpdateRequired = true;
|
|
284
503
|
return this.drawRange;
|
|
285
504
|
}
|
|
286
505
|
|
|
@@ -299,21 +518,118 @@ const Pointset = function () {
|
|
|
299
518
|
})
|
|
300
519
|
}
|
|
301
520
|
|
|
521
|
+
/**
|
|
522
|
+
* Get the index of the closest vertex to centroid.
|
|
523
|
+
*/
|
|
524
|
+
this.getClosestVertexIndex = function() {
|
|
525
|
+
let closestIndex = -1;
|
|
526
|
+
const mesh = this.getMorph();
|
|
527
|
+
if (mesh && this.drawRange > 0) {
|
|
528
|
+
this._b1.setFromArray(mesh.pointPositions.subarray(0, this.drawRange * 3));
|
|
529
|
+
this._b1.getCenter(this._v1);
|
|
530
|
+
let distance = -1;
|
|
531
|
+
for (let i = 0; i < this.drawRange; i++) {
|
|
532
|
+
this._v2.fromArray(mesh.pointPositions, i * 3);
|
|
533
|
+
const currentDistance = this._v1.distanceTo(this._v2);
|
|
534
|
+
if (distance === -1 || currentDistance < distance) {
|
|
535
|
+
distance = currentDistance;
|
|
536
|
+
closestIndex = i;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
return closestIndex;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* Get the closest vertex to centroid.
|
|
545
|
+
*/
|
|
546
|
+
this.getClosestVertex = function(applyMatrixWorld) {
|
|
547
|
+
const mesh = this.getMorph();
|
|
548
|
+
if (this.closestVertexIndex === -1) {
|
|
549
|
+
this.closestVertexIndex = this.getClosestVertexIndex();
|
|
550
|
+
}
|
|
551
|
+
const position = new THREE.Vector3();
|
|
552
|
+
if (mesh && this.closestVertexIndex >= 0) {
|
|
553
|
+
position.fromArray(mesh.pointPositions, this.closestVertexIndex * 3);
|
|
554
|
+
return applyMatrixWorld ? position.applyMatrix4(mesh.matrixWorld) : position;
|
|
555
|
+
}
|
|
556
|
+
this.getBoundingBox();
|
|
557
|
+
position.copy(this.center);
|
|
558
|
+
return position;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
/**
|
|
562
|
+
* Get the bounding box for the whole set of points.
|
|
563
|
+
*
|
|
564
|
+
* @return {THREE.Box3}
|
|
565
|
+
*/
|
|
566
|
+
this.getBoundingBox = function() {
|
|
567
|
+
const mesh = this.getMorph();
|
|
568
|
+
if (this.visible && mesh && mesh.visible && this.drawRange > 0) {
|
|
569
|
+
if (this.boundingBoxUpdateRequired) {
|
|
570
|
+
this._b1.setFromArray(mesh.pointPositions.subarray(0, this.drawRange * 3));
|
|
571
|
+
this.cachedBoundingBox.copy(this._b1);
|
|
572
|
+
mesh.updateWorldMatrix(true, true);
|
|
573
|
+
this.cachedBoundingBox.applyMatrix4(mesh.matrixWorld);
|
|
574
|
+
this.cachedBoundingBox.getCenter(this.center);
|
|
575
|
+
this.radius = this.center.distanceTo(this.cachedBoundingBox.max);
|
|
576
|
+
this.boundingBoxUpdateRequired = false;
|
|
577
|
+
}
|
|
578
|
+
return this.cachedBoundingBox;
|
|
579
|
+
}
|
|
580
|
+
return undefined;
|
|
581
|
+
}
|
|
302
582
|
|
|
303
583
|
/**
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
584
|
+
* Check if the pointset is time varying.
|
|
585
|
+
*
|
|
586
|
+
* @return {Boolean}
|
|
587
|
+
*/
|
|
588
|
+
this.isTimeVarying = () => {
|
|
589
|
+
return morphVertices || morphColours;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* Set the local time of this pointset.
|
|
594
|
+
*
|
|
595
|
+
* @param {Number} time - Can be any value between 0 to duration.
|
|
596
|
+
*/
|
|
597
|
+
this.setMorphTime = time => {
|
|
598
|
+
if (time > this.duration) {
|
|
599
|
+
this.inbuildTime = this.duration;
|
|
600
|
+
} else if (0 > time) {
|
|
601
|
+
this.inbuildTime = 0;
|
|
602
|
+
} else {
|
|
603
|
+
this.inbuildTime = time;
|
|
604
|
+
}
|
|
605
|
+
if (morphVertices || morphColours) {
|
|
606
|
+
updateMorphPointset();
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* Update the geometry and colours depending on the internal time and
|
|
612
|
+
* refresh the marker/LOD state for the render.
|
|
308
613
|
*/
|
|
309
614
|
this.render = (delta, playAnimation, cameraControls, options) => {
|
|
310
615
|
if (this.morph && cameraControls) {
|
|
311
616
|
this.morph.sizePerPixel = cameraControls.pixelHeight;
|
|
312
617
|
}
|
|
313
|
-
|
|
618
|
+
if (this.visible) {
|
|
619
|
+
this._lod.update(cameraControls, this.center);
|
|
620
|
+
}
|
|
621
|
+
if (playAnimation === true && (morphVertices || morphColours)) {
|
|
622
|
+
let targetTime = this.inbuildTime + delta;
|
|
623
|
+
if (targetTime > this.duration) {
|
|
624
|
+
targetTime = targetTime - this.duration;
|
|
625
|
+
}
|
|
626
|
+
this.inbuildTime = targetTime;
|
|
627
|
+
updateMorphPointset();
|
|
628
|
+
}
|
|
629
|
+
this.updateMarker(playAnimation, options);
|
|
314
630
|
}
|
|
315
631
|
|
|
316
632
|
}
|
|
317
633
|
|
|
318
634
|
Pointset.prototype = Object.create(ZincObject.prototype);
|
|
319
|
-
export { Pointset };
|
|
635
|
+
export { Pointset, createPointQuadGeometry };
|
|
@@ -1,7 +1,27 @@
|
|
|
1
|
-
import * as THREE from 'three';
|
|
2
|
-
import * as shader from '../shaders/textureSlide.js';
|
|
1
|
+
import * as THREE from 'three/webgpu';
|
|
2
|
+
//import * as shader from '../shaders/textureSlide.js';
|
|
3
|
+
import { createWebGPUMaterial } from '../tls/textureSlides.js';
|
|
3
4
|
import { TexturePrimitive } from './texturePrimitive';
|
|
4
5
|
|
|
6
|
+
|
|
7
|
+
const cloneData3DTexture = (sourceTex) => {
|
|
8
|
+
const image = sourceTex.image;
|
|
9
|
+
const width = image.width;
|
|
10
|
+
const height = image.height;
|
|
11
|
+
const depth = image.depth;
|
|
12
|
+
const clonedData = image.data;
|
|
13
|
+
const targetTex = new THREE.Data3DTexture(clonedData, width, height, depth);
|
|
14
|
+
targetTex.format = sourceTex.format;
|
|
15
|
+
targetTex.type = sourceTex.type;
|
|
16
|
+
targetTex.minFilter = sourceTex.minFilter;
|
|
17
|
+
targetTex.magFilter = sourceTex.magFilter;
|
|
18
|
+
targetTex.wrapS = sourceTex.wrapS;
|
|
19
|
+
targetTex.wrapT = sourceTex.wrapT;
|
|
20
|
+
targetTex.wrapR = sourceTex.wrapR;
|
|
21
|
+
targetTex.needsUpdate = true;
|
|
22
|
+
return targetTex;
|
|
23
|
+
}
|
|
24
|
+
|
|
5
25
|
/**
|
|
6
26
|
* Provides a class which create a texture stacks in a block
|
|
7
27
|
* with shaders allowing slices of texture to be displayed.
|
|
@@ -63,7 +83,7 @@ const TextureSlides = function (textureIn) {
|
|
|
63
83
|
*/
|
|
64
84
|
const setUniformSlideSettingsOfMesh = (mesh, settings) => {
|
|
65
85
|
const material = mesh.material;
|
|
66
|
-
const uniforms = material.uniforms;
|
|
86
|
+
const uniforms = material.userData.uniforms;
|
|
67
87
|
mesh.rotation.x = 0;
|
|
68
88
|
mesh.rotation.y = 0;
|
|
69
89
|
mesh.rotation.z = 0;
|
|
@@ -92,7 +112,6 @@ const TextureSlides = function (textureIn) {
|
|
|
92
112
|
default:
|
|
93
113
|
break;
|
|
94
114
|
}
|
|
95
|
-
material.needsUpdate = true;
|
|
96
115
|
this.boundingBoxUpdateRequired = true;
|
|
97
116
|
}
|
|
98
117
|
|
|
@@ -122,6 +141,7 @@ const TextureSlides = function (textureIn) {
|
|
|
122
141
|
if (settings && settings.direction && settings.value !== undefined) {
|
|
123
142
|
const geometry = new THREE.PlaneGeometry(1, 1);
|
|
124
143
|
geometry.translate(0.5, 0.5, 0);
|
|
144
|
+
/*
|
|
125
145
|
const uniforms = shader.getUniforms();
|
|
126
146
|
uniforms.brightness.value = brightness;
|
|
127
147
|
uniforms.contrast.value = contrast;
|
|
@@ -142,8 +162,23 @@ const TextureSlides = function (textureIn) {
|
|
|
142
162
|
side: THREE.DoubleSide,
|
|
143
163
|
transparent: false
|
|
144
164
|
};
|
|
145
|
-
|
|
146
|
-
material
|
|
165
|
+
|
|
166
|
+
//const material = this.texture.getMaterial(options);
|
|
167
|
+
*/
|
|
168
|
+
const material = createWebGPUMaterial();
|
|
169
|
+
material.userData.uniforms.brightness.value = brightness;
|
|
170
|
+
material.userData.uniforms.contrast.value = contrast;
|
|
171
|
+
material.userData.uniforms.diffuse0.value = cloneData3DTexture(this.texture.impl);
|
|
172
|
+
material.userData.uniforms.diffuse1.value = cloneData3DTexture(this.texture.impl);
|
|
173
|
+
material.userData.uniforms.discardAlpha.value = discardAlpha;
|
|
174
|
+
material.userData.uniforms.depth.value = this.texture.size.depth;
|
|
175
|
+
material.userData.uniforms.flipY.value = flipY;
|
|
176
|
+
material.userData.uniforms.flipZ.value = flipZ;
|
|
177
|
+
if (maskTexture) {
|
|
178
|
+
material.userData.uniforms.mask.value = maskTexture;
|
|
179
|
+
}
|
|
180
|
+
material.userData.uniforms.maskEnabled.value = maskEnabled;
|
|
181
|
+
material.userData.uniforms.nChannels.value = nChannels;
|
|
147
182
|
const mesh = new THREE.Mesh(geometry, material);
|
|
148
183
|
mesh.name = this.groupName;
|
|
149
184
|
mesh.userData = this;
|
|
@@ -288,7 +323,7 @@ const TextureSlides = function (textureIn) {
|
|
|
288
323
|
this.cachedBoundingBox.makeEmpty();
|
|
289
324
|
const vector = new THREE.Vector3(0, 0, 0);
|
|
290
325
|
this.morph.children.forEach(slide => {
|
|
291
|
-
expandBoxWithSettings(this.cachedBoundingBox, slide.material.uniforms,
|
|
326
|
+
expandBoxWithSettings(this.cachedBoundingBox, slide.material.userData.uniforms,
|
|
292
327
|
vector);
|
|
293
328
|
});
|
|
294
329
|
this.morph.updateMatrixWorld (true, true);
|
|
@@ -368,10 +403,9 @@ const TextureSlides = function (textureIn) {
|
|
|
368
403
|
this.setUniformsValue = (name, val) => {
|
|
369
404
|
this.morph.children.forEach((mesh) => {
|
|
370
405
|
const material = mesh.material;
|
|
371
|
-
if (material.
|
|
372
|
-
const uniforms = material.uniforms;
|
|
406
|
+
if (material.userData.uniforms) {
|
|
407
|
+
const uniforms = material.userData.uniforms;
|
|
373
408
|
uniforms[name].value = val;
|
|
374
|
-
material.needsUpdate = true;
|
|
375
409
|
}
|
|
376
410
|
});
|
|
377
411
|
}
|
|
@@ -421,7 +455,15 @@ const TextureSlides = function (textureIn) {
|
|
|
421
455
|
this.setMask = (maskTextureIn) => {
|
|
422
456
|
maskTexture = maskTextureIn;
|
|
423
457
|
maskEnabled = maskTexture ? true : false;
|
|
424
|
-
this.
|
|
458
|
+
this.morph.children.forEach((mesh) => {
|
|
459
|
+
const material = mesh.material;
|
|
460
|
+
if (material.userData.uniforms) {
|
|
461
|
+
const uniforms = material.userData.uniforms;
|
|
462
|
+
if (maskTexture) {
|
|
463
|
+
uniforms.mask.value = maskTexture;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
});
|
|
425
467
|
this.setUniformsValue("maskEnabled", maskEnabled);
|
|
426
468
|
}
|
|
427
469
|
|
|
@@ -441,16 +483,17 @@ const TextureSlides = function (textureIn) {
|
|
|
441
483
|
const ratio = iTime - t0;
|
|
442
484
|
this.morph.children.forEach((mesh) => {
|
|
443
485
|
const material = mesh.material;
|
|
444
|
-
if (material.
|
|
445
|
-
const uniforms = material.uniforms;
|
|
486
|
+
if (material.userData.uniforms) {
|
|
487
|
+
const uniforms = material.userData.uniforms;
|
|
446
488
|
if (lt0 !== t0) {
|
|
447
|
-
uniforms.diffuse0.value = this.textureList[t0].
|
|
489
|
+
uniforms.diffuse0.value.image.data = this.textureList[t0].imageData;
|
|
490
|
+
uniforms.diffuse0.value.needsUpdate = true;
|
|
448
491
|
}
|
|
449
492
|
if (lt1 !== t1) {
|
|
450
|
-
uniforms.diffuse1.value = this.textureList[t1].
|
|
493
|
+
uniforms.diffuse1.value.image.data = this.textureList[t1].imageData;
|
|
494
|
+
uniforms.diffuse1.value.needsUpdate = true;
|
|
451
495
|
}
|
|
452
496
|
uniforms.time.value = ratio;
|
|
453
|
-
material.needsUpdate = true;
|
|
454
497
|
}
|
|
455
498
|
});
|
|
456
499
|
lt0 = t0;
|