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.
Files changed (55) hide show
  1. package/build/zinc.js +3 -486
  2. package/build/zinc.js.map +1 -1
  3. package/package.json +3 -4
  4. package/src/controls.js +1 -1
  5. package/src/geometryCSG.js +1 -1
  6. package/src/glyphsetCSG.js +1 -1
  7. package/src/loaders/GLTFToZincJSLoader.js +1 -1
  8. package/src/loaders/JSONLoader.js +204 -377
  9. package/src/loaders/OBJLoader.js +1 -1
  10. package/src/loaders/STLLoader.js +1 -1
  11. package/src/loaders/niftiReader.js +6 -4
  12. package/src/loaders/primitivesLoader.js +7 -9
  13. package/src/minimap.js +1 -1
  14. package/src/primitives/geometry.js +11 -29
  15. package/src/primitives/glyph.js +1 -1
  16. package/src/primitives/glyphset.js +2 -4
  17. package/src/primitives/label.js +1 -1
  18. package/src/primitives/lines.js +12 -5
  19. package/src/primitives/lines2.js +4 -8
  20. package/src/primitives/lod.js +9 -3
  21. package/src/primitives/marker.js +1 -1
  22. package/src/primitives/markerCluster.js +1 -1
  23. package/src/primitives/pointset.js +368 -52
  24. package/src/primitives/textureSlides.js +59 -16
  25. package/src/primitives/textureVolume.js +1 -1
  26. package/src/primitives/tubeLines.js +20 -3
  27. package/src/primitives/zincObject.js +13 -1
  28. package/src/region.js +1 -1
  29. package/src/renderer.js +17 -13
  30. package/src/scene.js +20 -8
  31. package/src/sceneLoader.js +1 -1
  32. package/src/shaders/rgbVolumeRender.js +1 -1
  33. package/src/shaders/textureSlide.js +1 -1
  34. package/src/texture/texture.js +1 -1
  35. package/src/texture/textureArray.js +9 -2
  36. package/src/three/GLTFExporter.js +55 -2
  37. package/src/three/InstancedPoints.js +73 -0
  38. package/src/three/Loader.js +1 -1
  39. package/src/three/line/Line.js +1 -1
  40. package/src/three/line/LineSegments.js +1 -1
  41. package/src/three/line/LineSegments2.js +24 -39
  42. package/src/three/line/LineSegmentsGeometry.js +1 -1
  43. package/src/tls/morphColorMaterial.js +65 -0
  44. package/src/tls/pointsMaterial.js +79 -0
  45. package/src/tls/textureSlides.js +126 -0
  46. package/src/utilities.js +47 -70
  47. package/src/videoHandler.js +1 -2
  48. package/src/workers/geometryCSG.worker.js +1 -1
  49. package/src/workers/geometryCSGInternal.js +1 -1
  50. package/src/zinc.js +1 -1
  51. package/vite.config.js +3 -1
  52. package/src/primitives/augmentShader.js +0 -45
  53. package/src/three/Geometry.js +0 -2176
  54. package/src/three/Points.js +0 -224
  55. package/src/three/line/LineMaterial.js +0 -726
@@ -0,0 +1,79 @@
1
+ import * as THREE from 'three/webgpu';
2
+ import {
3
+ Fn,
4
+ attribute,
5
+ cameraProjectionMatrix,
6
+ materialColor,
7
+ materialOpacity,
8
+ mix,
9
+ modelViewMatrix,
10
+ positionGeometry,
11
+ screenDPR,
12
+ texture,
13
+ uniform,
14
+ uv,
15
+ vec4,
16
+ viewportSize,
17
+ } from 'three/tsl';
18
+
19
+ /**
20
+ * Provide the node material used to render a {@link Pointset}.
21
+ *
22
+ * Every point is a single instance of a small quad which is billboarded
23
+ * towards the camera and kept at a constant size in pixels (mirroring the
24
+ * behaviour of the legacy `THREE.PointsMaterial` with `sizeAttenuation`
25
+ * set to `false`). Per instance position comes from the `instancePosition`
26
+ * buffer attribute rather than `instanceMatrix`, since the on screen offset
27
+ * of the quad's corners is entirely computed on the GPU and does not need
28
+ * a per instance transform.
29
+ *
30
+ * @param {THREE.Texture} mapTexture - Texture used to draw a circular dot,
31
+ * its alpha channel is used for the alpha test cut out.
32
+ * @return {THREE.MeshBasicNodeMaterial}
33
+ */
34
+ const _rendererSize = /*@__PURE__*/ new THREE.Vector2();
35
+
36
+ const createInstancedPointsMaterial = (mapTexture) => {
37
+ const material = new THREE.MeshBasicNodeMaterial();
38
+
39
+ const uniforms = {
40
+ pointSize: uniform(10),
41
+ sizeAttenuation: uniform(0),
42
+ viewportScale: uniform(1).onFrameUpdate(function ({ renderer }) {
43
+ const size = renderer.getSize(_rendererSize);
44
+ this.value = 0.5 * size.y;
45
+ }),
46
+ };
47
+
48
+ material.vertexNode = Fn(() => {
49
+ const instancePosition = attribute('instancePosition', 'vec3');
50
+ const mvPosition = modelViewMatrix.mul(vec4(instancePosition, 1.0));
51
+
52
+ let pointSize = uniforms.pointSize.mul(screenDPR);
53
+ const attenuatedSize = pointSize.mul(uniforms.viewportScale.div(mvPosition.z.negate()));
54
+ pointSize = mix(pointSize, attenuatedSize, uniforms.sizeAttenuation);
55
+
56
+ let offset = positionGeometry.xy.mul(pointSize);
57
+ offset = offset.div(viewportSize.div(2));
58
+
59
+ const clip = cameraProjectionMatrix.mul(mvPosition);
60
+ offset = offset.mul(clip.w);
61
+
62
+ return vec4(clip.xy.add(offset), clip.z, clip.w);
63
+ })();
64
+
65
+ material.colorNode = Fn(() => {
66
+ const texColor = texture(mapTexture, uv());
67
+ return vec4(texColor.rgb.mul(materialColor), texColor.a.mul(materialOpacity));
68
+ })();
69
+
70
+ material.userData.uniforms = uniforms;
71
+ //Plain, introspectable mirrors of the uniforms above - matches the
72
+ //legacy THREE.PointsMaterial API (material.size/sizeAttenuation).
73
+ material.size = uniforms.pointSize.value;
74
+ material.sizeAttenuation = !!uniforms.sizeAttenuation.value;
75
+
76
+ return material;
77
+ };
78
+
79
+ export { createInstancedPointsMaterial };
@@ -0,0 +1,126 @@
1
+ import * as THREE from 'three/webgpu';
2
+ import {
3
+ Fn,
4
+ uniform,
5
+ vec3,
6
+ vec4,
7
+ texture3D,
8
+ mix,
9
+ positionLocal,
10
+ Discard,
11
+ If,
12
+ varying,
13
+ float,
14
+ } from 'three/tsl';
15
+
16
+ export function createWebGPUMaterial() {
17
+ const material = new THREE.MeshBasicNodeMaterial({
18
+ side: THREE.DoubleSide
19
+ });
20
+
21
+ const dummyData = new Uint8Array(32);
22
+
23
+ const placeholderTexture = new THREE.Data3DTexture(dummyData, 2, 2, 2);
24
+ placeholderTexture.image = {
25
+ data: dummyData,
26
+ width: 2,
27
+ height: 2,
28
+ depth: 2,
29
+ };
30
+
31
+ placeholderTexture.format = THREE.RGBAFormat;
32
+ placeholderTexture.type = THREE.UnsignedByteType; // Be explicit for WebGPU
33
+ placeholderTexture.minFilter = THREE.LinearFilter;
34
+ placeholderTexture.magFilter = THREE.LinearFilter;
35
+ placeholderTexture.generateMipmaps = false; // WebGPU cannot auto-generate mipmaps for custom Data3D
36
+ placeholderTexture.unpackAlignment = 1;
37
+ placeholderTexture.needsUpdate = true;
38
+
39
+ // Define Uniforms
40
+ const uniforms = {
41
+ brightness: uniform(0),
42
+ contrast: uniform(1),
43
+ depth: uniform(1),
44
+ discardAlpha: uniform(true),
45
+ direction: uniform(1),
46
+ flipY: uniform(true),
47
+ flipZ: uniform(false),
48
+ nChannels: uniform(1),
49
+ maskEnabled: uniform(false),
50
+ slide: uniform(new THREE.Vector3(0, 0, 1)),
51
+ time: uniform(0)
52
+ };
53
+
54
+ // Share Varyings via TSL
55
+ // A TSL varying automatically wires itself from the vertex stage to the fragment stage.
56
+ const vUw = varying(vec3(0.0), 'vUw');
57
+ uniforms.diffuse0 = texture3D(placeholderTexture, vUw);
58
+ uniforms.diffuse1 = texture3D(placeholderTexture, vUw);
59
+ uniforms.mask = texture3D(placeholderTexture, vUw);
60
+
61
+ // Vertex Node
62
+ const vertexNode = Fn(() => {
63
+ // positionLocal provides position.xyz out-of-the-box
64
+ const slidePos = vec3(positionLocal).toVar();
65
+
66
+ // Handle direction branching
67
+ If(uniforms.direction.equal(1), () => {
68
+ slidePos.assign(vec3(uniforms.slide.x, positionLocal.y, positionLocal.x));
69
+ });
70
+ If(uniforms.direction.equal(2), () => {
71
+ slidePos.assign(vec3(positionLocal.x, uniforms.slide.y, positionLocal.y));
72
+ });
73
+ If(uniforms.direction.equal(3), () => {
74
+ slidePos.assign(vec3(positionLocal.x, positionLocal.y, uniforms.slide.z));
75
+ });
76
+
77
+ // Handle flipping
78
+ If(uniforms.flipY, () => {
79
+ slidePos.y.assign(float(1.0).sub(slidePos.y));
80
+ });
81
+ If(uniforms.flipZ, () => {
82
+ slidePos.z.assign(float(1.0).sub(slidePos.z));
83
+ });
84
+
85
+ // Write to our varying
86
+ vUw.assign(vec3(slidePos.x, slidePos.y, slidePos.z));
87
+
88
+ return positionLocal;
89
+ });
90
+
91
+
92
+ // Fragment Node
93
+ const fragmentNode = Fn(() => {
94
+ // diffuse0/diffuse1/mask are already-built texture3D sampling nodes.
95
+ const color0 = uniforms.diffuse0.r;
96
+ const color1 = uniforms.diffuse1.r;
97
+
98
+ const color = mix(color0, color1, uniforms.time).toVar();
99
+
100
+ // Mask implementation & discard logic
101
+ If(uniforms.maskEnabled.and(uniforms.discardAlpha), () => {
102
+ const maskVal = uniforms.mask.r;
103
+ If(maskVal.equal(0.0), () => {
104
+ Discard();
105
+ });
106
+ });
107
+
108
+ // Brightness calculations
109
+ const brightenedColor = vec3(color).add(vec3(uniforms.brightness));
110
+
111
+ // Contrast calculations
112
+ const contrastedColor = brightenedColor.sub(vec3(0.5)).mul(uniforms.contrast).add(vec3(0.5));
113
+
114
+ return vec4(contrastedColor, 1.0);
115
+ });
116
+
117
+ // Assign to NodeMaterial slots
118
+ // TSL handles the base vertex transformation automatically, positionNode hooks custom vertex behaviour.
119
+ material.positionNode = vertexNode();
120
+ material.colorNode = fragmentNode();
121
+
122
+ // Expose the uniforms reference so your application loop can update them via material.userData
123
+ material.userData.uniforms = uniforms;
124
+
125
+ return material;
126
+ }
package/src/utilities.js CHANGED
@@ -1,5 +1,4 @@
1
- import * as THREE from 'three';
2
- import { Geometry as THREEGeometry } from './three/Geometry';
1
+ import * as THREE from 'three/webgpu';
3
2
  import SpriteTextModule from 'three-spritetext';
4
3
  const SpriteText = SpriteTextModule.default || SpriteTextModule;
5
4
  import discPNG from './assets/disc.png';
@@ -135,40 +134,63 @@ const updateMorphColorAttribute = function(targetGeometry, morph) {
135
134
  const morphColors = targetGeometry.morphAttributes[ "color" ];
136
135
  const influences = morph.morphTargetInfluences;
137
136
  const length = influences.length;
138
- targetGeometry.deleteAttribute( 'morphColor0' );
139
- targetGeometry.deleteAttribute( 'morphColor1' );
140
- let bound = 0;
141
137
  let morphArray = [];
142
- for (let i = 0; (1 > bound) || (i < length); i++) {
138
+ for (let i = 0; i < length; i++) {
143
139
  if (influences[i] > 0) {
144
- bound++;
145
140
  morphArray.push([i, influences[i]]);
146
141
  }
147
142
  }
148
- if (morphArray.length == 2) {
149
- targetGeometry.setAttribute('morphColor0', morphColors[ morphArray[0][0] ] );
150
- targetGeometry.setAttribute('morphColor1', morphColors[ morphArray[1][0] ] );
143
+ //morphColorMix (0 = fully morphColor0, 1 = fully morphColor1) drives the
144
+ //TSL colorNode set up by applyMorphColorNode - keep it in sync with
145
+ //whichever two morph targets currently have non-zero influence.
146
+ let mix = 0;
147
+ let index0 = 0;
148
+ let index1 = 0;
149
+ if (morphArray.length >= 2) {
150
+ index0 = morphArray[0][0];
151
+ index1 = morphArray[1][0];
152
+ const total = morphArray[0][1] + morphArray[1][1];
153
+ mix = total > 0 ? morphArray[1][1] / total : 0;
151
154
  } else if (morphArray.length == 1) {
152
- targetGeometry.setAttribute('morphColor0', morphColors[ morphArray[0][0] ] );
153
- targetGeometry.setAttribute('morphColor1', morphColors[ morphArray[0][0] ] );
155
+ index0 = index1 = morphArray[0][0];
156
+ }
157
+ //Keep morphColor0/morphColor1 as two persistent attributes and mutate
158
+ //their contents in place (array.set + needsUpdate) rather than
159
+ //swapping which BufferAttribute object is bound under those names
160
+ //every call. WebGPU's pipeline is built once against whichever
161
+ //attribute object first occupies a given slot; a brand new object
162
+ //bound in later (as deleteAttribute/setAttribute would do every frame)
163
+ //isn't guaranteed to be picked up by an already-built pipeline, which
164
+ //silently renders black. Mutating a single long-lived attribute is the
165
+ //standard, well tested update path both the WebGL and WebGPU
166
+ //backends use everywhere else.
167
+ let attribute0 = targetGeometry.getAttribute('morphColor0');
168
+ let attribute1 = targetGeometry.getAttribute('morphColor1');
169
+ if (!attribute0 || !attribute1) {
170
+ attribute0 = new THREE.Float32BufferAttribute(
171
+ new Float32Array(morphColors[0].array.length), morphColors[0].itemSize);
172
+ attribute1 = new THREE.Float32BufferAttribute(
173
+ new Float32Array(morphColors[0].array.length), morphColors[0].itemSize);
174
+ attribute0.setUsage(THREE.DynamicDrawUsage);
175
+ attribute1.setUsage(THREE.DynamicDrawUsage);
176
+ targetGeometry.setAttribute('morphColor0', attribute0);
177
+ targetGeometry.setAttribute('morphColor1', attribute1);
178
+ }
179
+ attribute0.array.set(morphColors[index0].array);
180
+ attribute0.needsUpdate = true;
181
+ attribute1.array.set(morphColors[index1].array);
182
+ attribute1.needsUpdate = true;
183
+ const morphColorMix = morph.material && morph.material.userData &&
184
+ morph.material.userData.uniforms && morph.material.userData.uniforms.morphColorMix;
185
+ if (morphColorMix) {
186
+ morphColorMix.value = mix;
154
187
  }
155
188
  }
156
189
  }
157
190
 
158
191
 
159
- const toBufferGeometry = (geometryIn, options) => {
160
- let geometry = undefined;
161
- if (geometryIn instanceof THREEGeometry) {
162
- if (options.localTimeEnabled && !geometryIn.morphNormalsReady &&
163
- (geometryIn.morphNormals == undefined || geometryIn.morphNormals.length == 0))
164
- geometryIn.computeMorphNormals();
165
- geometry = geometryIn.toIndexedBufferGeometry();
166
- if (options.localMorphColour) {
167
- copyMorphColorsToIndexedBufferGeometry(geometryIn, geometry);
168
- }
169
- } else if (geometryIn instanceof THREE.BufferGeometry) {
170
- geometry = geometryIn.clone();
171
- }
192
+ const toBufferGeometry = (geometryIn) => {
193
+ const geometry = geometryIn.clone();
172
194
  geometry.colorsNeedUpdate = true;
173
195
  geometry.computeBoundingBox();
174
196
  geometry.computeBoundingSphere();
@@ -177,50 +199,6 @@ const toBufferGeometry = (geometryIn, options) => {
177
199
  return geometry;
178
200
  }
179
201
 
180
- const copyMorphColorsToBufferGeometry = (geometry, bufferGeometry) => {
181
- if (geometry && geometry.morphColors && geometry.morphColors.length > 0 ) {
182
- let array = [];
183
- let morphColors = geometry.morphColors;
184
- for ( var i = 0, l = morphColors.length; i < l; i ++ ) {
185
- let morphColor = morphColors[ i ];
186
- let colorArray = [];
187
- for ( var j = 0; j < geometry.faces.length; j ++ ) {
188
- let face = geometry.faces[j];
189
- let color = getColorsRGB(morphColor.colors, face.a);
190
- colorArray.push(color[0], color[1], color[2]);
191
- color = getColorsRGB(morphColor.colors, face.b);
192
- colorArray.push(color[0], color[1], color[2]);
193
- color = getColorsRGB(morphColor.colors, face.c);
194
- colorArray.push(color[0], color[1], color[2]);
195
- }
196
- var attribute = new THREE.Float32BufferAttribute( geometry.faces.length * 3 * 3, 3 );
197
- attribute.name = morphColor.name;
198
- array.push( attribute.copyArray( colorArray ) );
199
- }
200
- bufferGeometry.morphAttributes[ "color" ] = array;
201
- }
202
- }
203
-
204
-
205
- const copyMorphColorsToIndexedBufferGeometry = (geometry, bufferGeometry) => {
206
- if (geometry && geometry.morphColors && geometry.morphColors.length > 0 ) {
207
- let array = [];
208
- let morphColors = geometry.morphColors;
209
- for ( let i = 0, l = morphColors.length; i < l; i ++ ) {
210
- const morphColor = morphColors[ i ];
211
- const colorArray = [];
212
- for ( let j = 0; j < morphColor.colors.length * 3; j ++ ) {
213
- let color = getColorsRGB(morphColor.colors, j);
214
- colorArray.push(color[0], color[1], color[2]);
215
- }
216
- const attribute = new THREE.Float32BufferAttribute( colorArray, 3 );
217
- attribute.name = morphColor.name;
218
- array.push( attribute );
219
- }
220
- bufferGeometry.morphAttributes[ "color" ] = array;
221
- }
222
- }
223
-
224
202
  /**
225
203
  * Merges a set of attributes into a single instance. All attributes must have compatible properties and types.
226
204
  * Instances of {@link InterleavedBufferAttribute} are not supported.
@@ -884,7 +862,6 @@ function copyColorsArray(attribute, colors) {
884
862
  }
885
863
 
886
864
  export {
887
- copyMorphColorsToBufferGeometry,
888
865
  copyColorsArray,
889
866
  copyVector2sArray,
890
867
  copyVector3sArray,
@@ -1,4 +1,4 @@
1
- import * as THREE from 'three';
1
+ import * as THREE from 'three/webgpu';
2
2
 
3
3
  /**
4
4
  * Provide basic functionality to display video as texture.
@@ -75,7 +75,6 @@ const VideoHandler = function(srcIn) {
75
75
  _this.videoTexture = new THREE.VideoTexture( _this.video );
76
76
  _this.videoTexture.minFilter = THREE.LinearFilter;
77
77
  _this.videoTexture.magFilter = THREE.LinearFilter;
78
- _this.videoTexture.format = THREE.RGBFormat;
79
78
  _this.video.currentTime = 0;
80
79
  return _this.videoTexture;
81
80
  }
@@ -1,7 +1,7 @@
1
1
 
2
2
  import { Geometry } from '../primitives/geometry';
3
3
  import { GeometryCSGInternal } from './geometryCSGInternal';
4
- import * as THREE from 'three';
4
+ import * as THREE from 'three/webgpu';
5
5
 
6
6
  const JSONLoader = THREE.BufferGeometryLoader;
7
7
  let core = undefined;
@@ -1,4 +1,4 @@
1
- import * as THREE from 'three';
1
+ import * as THREE from 'three/webgpu';
2
2
  import { ThreeBSPWrapper } from '../three-js-csg';
3
3
  const ThreeBSP = ThreeBSPWrapper(THREE);
4
4
 
package/src/zinc.js CHANGED
@@ -1,4 +1,4 @@
1
- import * as THREE from 'three';
1
+ import * as THREE from 'three/webgpu';
2
2
  import PACKAGE from '../package.json';
3
3
 
4
4
  // Import all internal primitives and modules
package/vite.config.js CHANGED
@@ -23,7 +23,9 @@ export default defineConfig({
23
23
  extend: true,
24
24
  globals: {
25
25
  'css-element-queries': 'cssElememtQueries',
26
- three: 'THREE',
26
+ 'three': 'THREE',
27
+ 'three/tsl': 'THREE',
28
+ 'three/webgpu': 'THREE',
27
29
  'three-spritetext': 'SpriteText',
28
30
  'three/examples/jsm/loaders/GLTFLoader': 'GLTFLoader',
29
31
  'fflate': 'fflate'
@@ -1,45 +0,0 @@
1
-
2
- /**
3
- * Provide additional shaders to render time dependent color.
4
- */
5
-
6
- const augmentMorphColor = function(shader) {
7
- shader.vertexShader = shader.vertexShader.replace(
8
- '#include <morphcolor_vertex>',
9
- `
10
- #if defined( USE_MORPHCOLORS )
11
-
12
- // morphTargetBaseInfluence is set based on BufferGeometry.morphTargetsRelative value:
13
- // When morphTargetsRelative is false, this is set to 1 - sum(influences); this results in normal = sum((target - base) * influence)
14
- // When morphTargetsRelative is true, this is set to 1; as a result, all morph targets are simply added to the base after weighting
15
- vColor *= morphTargetBaseInfluence;
16
-
17
- for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) {
18
-
19
- #if defined( USE_COLOR_ALPHA )
20
-
21
- if ( morphTargetInfluences[ i ] != 0.0 ) vColor += getMorph( gl_VertexID, i, 2 ) * morphTargetInfluences[ i ];
22
-
23
- #elif defined( USE_COLOR )
24
-
25
- if ( morphTargetInfluences[ i ] != 0.0 ) {
26
- vColor.rgb += getMorph( gl_VertexID, i, 2 ).rgb * morphTargetInfluences[ i ];
27
- vColor.a = 1.0;
28
- }
29
-
30
- #endif
31
-
32
- }
33
-
34
- #endif
35
- `
36
- );
37
- }
38
-
39
- const printShader = function (shader) {
40
- console.log(shader.vertexShader);
41
- console.log(shader.fragmentShader);
42
- }
43
-
44
-
45
- export { augmentMorphColor, printShader };