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
@@ -1,4 +1,4 @@
1
- import * as THREE from 'three';
1
+ import * as THREE from 'three/webgpu';
2
2
  import { mergeGeometries } from '../utilities';
3
3
  import { ZincObject } from './zincObject';
4
4
 
@@ -31,7 +31,7 @@ const TubeLines = function () {
31
31
  this.createLineSegment = (geometryIn, materialIn, options) => {
32
32
  if (geometryIn && materialIn) {
33
33
  dataIn = { geometryIn, materialIn, options };
34
- const geometry = getTubeLinesGeometry(geometryIn.vertices);
34
+ const geometry = getTubeLinesGeometry(extractVertices(geometryIn));
35
35
  const material = new THREE.MeshStandardMaterial({ color: materialIn.color });
36
36
  const mesh = new THREE.Mesh(geometry, material);
37
37
  this.setMesh(mesh, options.localTimeEnabled, options.localMorphColour);
@@ -86,10 +86,27 @@ const TubeLines = function () {
86
86
  mesh.geometry.dispose();
87
87
 
88
88
  geometryConfig = Object.assign(geometryConfig, { radius, radialSegments });
89
- mesh.geometry = getTubeLinesGeometry(geometryIn.vertices);
89
+ mesh.geometry = getTubeLinesGeometry(extractVertices(geometryIn));
90
90
  }
91
91
  }
92
92
 
93
+ /**
94
+ * Read the position attribute of a BufferGeometry into an array of
95
+ * THREE.Vector3, since TubeGeometry/CatmullRomCurve3/LineCurve3 need
96
+ * actual Vector3 instances to work with, not a flat typed array.
97
+ *
98
+ * @param {THREE.BufferGeometry} geometry
99
+ * @returns {Array}
100
+ */
101
+ const extractVertices = (geometry) => {
102
+ const position = geometry.getAttribute('position');
103
+ const vertices = [];
104
+ for (let i = 0; i < position.count; i++) {
105
+ vertices.push(new THREE.Vector3().fromBufferAttribute(position, i));
106
+ }
107
+ return vertices;
108
+ }
109
+
93
110
  /**
94
111
  * Get merged geometry from list of geometry vertices
95
112
  *
@@ -1,4 +1,4 @@
1
- import * as THREE from 'three';
1
+ import * as THREE from 'three/webgpu';
2
2
  import {
3
3
  createBufferGeometry,
4
4
  getBoundingBox,
@@ -183,6 +183,10 @@ ZincObject.prototype.setMesh = function(mesh, localTimeEnabled, localMorphColour
183
183
  this.clipAction.loop = THREE.LoopRepeat;
184
184
  this.clipAction.clampWhenFinished = true;
185
185
  this.clipAction.play();
186
+ //Evaluate the clip immediately so morphTargetInfluences reflects
187
+ //time 0 right away, rather than staying at its all-zero default
188
+ //until the first mixer.update() from playback/setMorphTime.
189
+ this.mixer.update(0);
186
190
  }
187
191
  }
188
192
  }
@@ -200,6 +204,14 @@ ZincObject.prototype.setMesh = function(mesh, localTimeEnabled, localMorphColour
200
204
  geometry.setAttribute('morphTarget1', geometry.getAttribute( 'position' ) );
201
205
  }
202
206
  }
207
+ if (this.morphColour) {
208
+ //Make sure morphColor0/morphColor1 exist before this mesh is ever
209
+ //rendered. WebGPU builds its pipeline against whatever attributes the
210
+ //geometry has at first render, and adding these two afterwards doesn't
211
+ //get picked up by an already-built pipeline - the object would render
212
+ //black forever after, since the colour shader can no longer see them.
213
+ this.initiateMorphColor();
214
+ }
203
215
  this.boundingBoxUpdateRequired = true;
204
216
  }
205
217
 
package/src/region.js CHANGED
@@ -1,4 +1,4 @@
1
- import * as THREE from 'three';
1
+ import * as THREE from 'three/webgpu';
2
2
  import { Pointset } from './primitives/pointset';
3
3
  import { Lines2 } from './primitives/lines2';
4
4
  import { Geometry } from './primitives/geometry';
package/src/renderer.js CHANGED
@@ -1,5 +1,4 @@
1
- import * as THREE from 'three';
2
- //import { WebGPURenderer } from 'three/webgpu';
1
+ import * as THREE from 'three/webgpu';
3
2
  import { ResizeSensor } from 'css-element-queries';
4
3
  import { Scene } from './scene';
5
4
  /**
@@ -77,8 +76,9 @@ const Renderer = function (containerIn) {
77
76
  */
78
77
  this.onWindowResize = () => {
79
78
  currentScene.onWindowResize();
80
- const width = this.getDrawingWidth();
81
- const height = this.getDrawingHeight();
79
+ //Give it a miniumum size of 1 x 1
80
+ const width = this.getDrawingWidth() || 1;
81
+ const height = this.getDrawingHeight() || 1;
82
82
  if (renderer != undefined) {
83
83
  let localRect = undefined;
84
84
  if (container) {
@@ -109,7 +109,7 @@ const Renderer = function (containerIn) {
109
109
  /**
110
110
  * Initialise the renderer and its visualisations.
111
111
  */
112
- this.initialiseVisualisation = parameters => {
112
+ this.initialiseVisualisation = async (parameters) => {
113
113
  if (!isInitialised) {
114
114
  parameters = parameters || {};
115
115
  if (parameters['antialias'] === undefined) {
@@ -132,10 +132,10 @@ const Renderer = function (containerIn) {
132
132
  canvas = parameters["canvas"];
133
133
  }
134
134
  //parameters["forceWebGL"] = true;
135
- //renderer = new WebGPURenderer(parameters);
136
- //await renderer.init();
135
+ renderer = new THREE.WebGPURenderer(parameters);
136
+ await renderer.init();
137
137
 
138
- renderer = new THREE.WebGLRenderer(parameters);
138
+ //renderer = new THREE.WebGLRenderer(parameters);
139
139
  if (container !== undefined) {
140
140
  container.appendChild( renderer.domElement );
141
141
  }
@@ -208,10 +208,10 @@ const Renderer = function (containerIn) {
208
208
  * @return {Zinc.Scene}
209
209
  */
210
210
  this.getSceneByName = name => {
211
- return sceneMap[name];
212
- }
211
+ return sceneMap[name];
212
+ }
213
213
 
214
- /**
214
+ /*
215
215
  * Create a new scene with the provided name if scene with the same name exists,
216
216
  * return undefined.
217
217
  *
@@ -244,6 +244,10 @@ const Renderer = function (containerIn) {
244
244
  logoSprite.position.set(calculatedWidth, calculatedHeight, 1 );
245
245
  }
246
246
  }
247
+ return sceneMap[name];
248
+ }
249
+
250
+ /**
247
251
  };
248
252
 
249
253
  const updateOrthoCamera = () => {
@@ -573,11 +577,11 @@ const Renderer = function (containerIn) {
573
577
  }
574
578
 
575
579
  this.forceContextLoss = () => {
576
- renderer.forceContextLoss();
580
+ // renderer.forceContextLoss();
577
581
  }
578
582
 
579
583
  this.forceContextRestore= () => {
580
- renderer.forceContextRestore();
584
+ // renderer.forceContextRestore();
581
585
  }
582
586
 
583
587
  /**
package/src/scene.js CHANGED
@@ -1,13 +1,15 @@
1
- import * as THREE from 'three';
1
+ import * as THREE from 'three/webgpu';
2
2
  import { CameraControls, StereoEffect } from './controls';
3
3
  import { LineSegments } from './three/line/LineSegments';
4
4
  import { MarkerCluster } from './primitives/markerCluster';
5
5
  import { Minimap } from './minimap';
6
6
  import { Region } from './region';
7
- import { Points } from './three/Points';
7
+ import { InstancedPoints } from './three/InstancedPoints';
8
8
  import { SceneExporter } from './sceneExporter';
9
9
  import { SceneLoader } from './sceneLoader';
10
10
  import { Viewport } from './controls';
11
+ import { createPointQuadGeometry } from './primitives/pointset';
12
+ import { createInstancedPointsMaterial } from './tls/pointsMaterial';
11
13
  import {
12
14
  createBufferGeometry,
13
15
  createNewSpriteText,
@@ -1233,12 +1235,22 @@ const Scene = function (containerIn, rendererIn) {
1233
1235
  * with clearTemporaryPrimitives method.
1234
1236
  */
1235
1237
  this.addTemporaryPoints = (coords, colour) => {
1236
- const geometry = createBufferGeometry(coords.length, coords);
1237
- let material = new THREE.PointsMaterial({ alphaTest: 0.5, size: 15,
1238
- color: colour, sizeAttenuation: false });
1239
- const texture = getCircularTexture();
1240
- material.map = texture;
1241
- let point = new Points(geometry, material);
1238
+ const geometry = createPointQuadGeometry(coords.length);
1239
+ const material = createInstancedPointsMaterial(getCircularTexture());
1240
+ material.color.set(colour);
1241
+ material.size = 15;
1242
+ material.userData.uniforms.pointSize.value = 15;
1243
+ material.alphaTest = 0.5;
1244
+ const point = new InstancedPoints(geometry, material, coords.length);
1245
+ point.pointPositions = new Float32Array(coords.length * 3);
1246
+ const instancePosition = geometry.getAttribute('instancePosition');
1247
+ coords.forEach((coord, index) => {
1248
+ instancePosition.setXYZ(index, coord[0], coord[1], coord[2]);
1249
+ point.pointPositions[index * 3] = coord[0];
1250
+ point.pointPositions[index * 3 + 1] = coord[1];
1251
+ point.pointPositions[index * 3 + 2] = coord[2];
1252
+ });
1253
+ instancePosition.needsUpdate = true;
1242
1254
  tempGroup.add(point);
1243
1255
  return point;
1244
1256
  }
@@ -1,4 +1,4 @@
1
- import * as THREE from 'three';
1
+ import * as THREE from 'three/webgpu';
2
2
  import { GLTFToZincJSLoader } from './loaders/GLTFToZincJSLoader';
3
3
  import { OBJLoader } from './loaders/OBJLoader';
4
4
  import { PrimitivesLoader } from './loaders/primitivesLoader';
@@ -1,4 +1,4 @@
1
- import * as THREE from 'three';
1
+ import * as THREE from 'three/webgpu';
2
2
  const glslVersion = null;
3
3
 
4
4
  const fs =
@@ -1,4 +1,4 @@
1
- import * as THREE from 'three';
1
+ import * as THREE from 'three/webgpu';
2
2
  const glslVersion = THREE.GLSL3;
3
3
 
4
4
  const fs =
@@ -1,4 +1,4 @@
1
- import * as THREE from 'three';
1
+ import * as THREE from 'three/webgpu';
2
2
 
3
3
  /**
4
4
  * Base texture object for importing images and turning them into
@@ -1,4 +1,4 @@
1
- import * as THREE from 'three';
1
+ import * as THREE from 'three/webgpu';
2
2
  import { Texture } from './texture';
3
3
 
4
4
  /**
@@ -12,8 +12,15 @@ import { Texture } from './texture';
12
12
  const TextureArray = function () {
13
13
  Texture.call(this);
14
14
  this.isTextureArray = true;
15
+ this.imageData = undefined;
15
16
 
16
17
 
18
+ this.setDataTexture = (dataTexture) => {
19
+ this.impl = dataTexture;
20
+ console.log("setDataTexture", dataTexture);
21
+ this.imageData = dataTexture.image.data;
22
+ }
23
+
17
24
  /**
18
25
  * Read images from an array containg src locations.
19
26
  *
@@ -48,7 +55,7 @@ const TextureArray = function () {
48
55
  length += data.length;
49
56
  });
50
57
 
51
- this.impl = new THREE.DataArrayTexture(fullArray, w, h, d);
58
+ this.impl = new THREE.Data3DTexture(fullArray, w, h, d);
52
59
  this.size = {
53
60
  width: w,
54
61
  height: h,
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  BufferAttribute,
3
+ BufferGeometry,
3
4
  ClampToEdgeWrapping,
4
5
  DoubleSide,
5
6
  InterpolateDiscrete,
@@ -19,7 +20,7 @@ import {
19
20
  RepeatWrapping,
20
21
  Scene,
21
22
  Vector3
22
- } from 'three';
23
+ } from 'three/webgpu';
23
24
 
24
25
 
25
26
  class GLTFExporter {
@@ -1291,6 +1292,48 @@ class GLTFWriter {
1291
1292
 
1292
1293
  }
1293
1294
 
1295
+ /**
1296
+ * A Pointset's {@link InstancedPoints} draws every point as an instance of a
1297
+ * small camera facing quad - its `geometry` only carries that quad, the
1298
+ * actual point positions/colours live in `pointPositions`/`instanceColor`.
1299
+ * Temporarily substitute a plain points geometry built from that data (and
1300
+ * flag the object as `isPoints`) so the regular POINTS export path below
1301
+ * produces a correct round trip, then return a function that restores the
1302
+ * object to its original state.
1303
+ *
1304
+ * @param {InstancedPoints} object Object to substitute the geometry of
1305
+ * @return {Function} Restores `object` to its pre-substitution state
1306
+ */
1307
+ substituteInstancedPointsGeometry( object ) {
1308
+
1309
+ const count = object.count;
1310
+ const geometry = new BufferGeometry();
1311
+ geometry.setAttribute( 'position',
1312
+ new BufferAttribute( object.pointPositions.slice( 0, count * 3 ), 3 ) );
1313
+
1314
+ if ( object.instanceColor ) {
1315
+
1316
+ geometry.setAttribute( 'color',
1317
+ new BufferAttribute( object.instanceColor.array.slice( 0, count * 3 ), 3 ) );
1318
+
1319
+ }
1320
+
1321
+ const originalGeometry = object.geometry;
1322
+ const originalIsPoints = object.isPoints;
1323
+
1324
+ object.geometry = geometry;
1325
+ object.isPoints = true;
1326
+
1327
+ return function () {
1328
+
1329
+ object.geometry = originalGeometry;
1330
+ object.isPoints = originalIsPoints;
1331
+ geometry.dispose();
1332
+
1333
+ };
1334
+
1335
+ }
1336
+
1294
1337
  /**
1295
1338
  * Process mesh
1296
1339
  * @param {THREE.Mesh} mesh Mesh to process
@@ -1887,10 +1930,20 @@ class GLTFWriter {
1887
1930
 
1888
1931
  this.serializeUserData( object, nodeDef );
1889
1932
 
1890
- if ( object.isMesh || object.isLine || object.isPoints ) {
1933
+ if ( object.isMesh || object.isLine || object.isPoints || object.isInstancedPoints ) {
1934
+
1935
+ let restoreInstancedPoints;
1936
+
1937
+ if ( object.isInstancedPoints ) {
1938
+
1939
+ restoreInstancedPoints = this.substituteInstancedPointsGeometry( object );
1940
+
1941
+ }
1891
1942
 
1892
1943
  const meshIndex = this.processMesh( object );
1893
1944
 
1945
+ if ( restoreInstancedPoints ) restoreInstancedPoints();
1946
+
1894
1947
  if ( meshIndex !== null ) nodeDef.mesh = meshIndex;
1895
1948
 
1896
1949
  } else if ( object.isCamera ) {
@@ -0,0 +1,73 @@
1
+ import {
2
+ InstancedMesh,
3
+ Matrix4,
4
+ Ray,
5
+ Vector3,
6
+ } from 'three/webgpu';
7
+
8
+ const _inverseMatrix = /*@__PURE__*/ new Matrix4();
9
+ const _ray = /*@__PURE__*/ new Ray();
10
+ const _position = /*@__PURE__*/ new Vector3();
11
+
12
+ /**
13
+ * An {@link THREE.InstancedMesh} used to draw a {@link Pointset}. Every
14
+ * instance is a small quad billboarded towards the camera on the GPU (see
15
+ * {@link createInstancedPointsMaterial}), so the usual matrix/geometry based
16
+ * raycasting and bounding logic performed by `THREE.InstancedMesh` does not
17
+ * apply - the true, already time-blended location of every point is instead
18
+ * kept in `pointPositions` (local space) and used here directly, mirroring
19
+ * the distance-to-point approach the legacy `THREE.Points` override used.
20
+ */
21
+ class InstancedPoints extends InstancedMesh {
22
+
23
+ constructor(geometry, material, count) {
24
+ super(geometry, material, count);
25
+ this.type = 'InstancedPoints';
26
+ this.isInstancedPoints = true;
27
+ this.frustumCulled = false;
28
+ this.pointSize = 10;
29
+ this.sizePerPixel = 1;
30
+ //Flat [x0, y0, z0, x1, y1, z1, ...] array of the current, already
31
+ //time-blended local positions of every active point.
32
+ this.pointPositions = new Float32Array(0);
33
+ }
34
+
35
+ raycast(raycaster, intersects) {
36
+ const matrixWorld = this.matrixWorld;
37
+ const threshold = raycaster.params.Points ? raycaster.params.Points.threshold : 1;
38
+ const count = this.count;
39
+ const positions = this.pointPositions;
40
+
41
+ if (!count || !positions || positions.length < count * 3) return;
42
+
43
+ _inverseMatrix.copy(matrixWorld).invert();
44
+ _ray.copy(raycaster.ray).applyMatrix4(_inverseMatrix);
45
+
46
+ const averageScale = (this.scale.x + this.scale.y + this.scale.z) / 3;
47
+ const localThreshold = threshold / averageScale * this.pointSize * this.sizePerPixel;
48
+ const localThresholdSq = localThreshold * localThreshold;
49
+
50
+ for (let i = 0; i < count; i++) {
51
+ _position.fromArray(positions, i * 3);
52
+ const rayPointDistanceSq = _ray.distanceSqToPoint(_position);
53
+ if (rayPointDistanceSq < localThresholdSq) {
54
+ const intersectPoint = new Vector3();
55
+ _ray.closestPointToPoint(_position, intersectPoint);
56
+ intersectPoint.applyMatrix4(matrixWorld);
57
+ const distance = raycaster.ray.origin.distanceTo(intersectPoint);
58
+ if (distance < raycaster.near || distance > raycaster.far) continue;
59
+ intersects.push({
60
+ distance: distance,
61
+ distanceToRay: Math.sqrt(rayPointDistanceSq),
62
+ point: intersectPoint,
63
+ index: i,
64
+ face: null,
65
+ object: this,
66
+ });
67
+ }
68
+ }
69
+ }
70
+
71
+ }
72
+
73
+ export { InstancedPoints };
@@ -18,7 +18,7 @@ import {
18
18
  MaterialLoader,
19
19
  TextureLoader,
20
20
  Color
21
- } from 'three';
21
+ } from 'three/webgpu';
22
22
 
23
23
  /**
24
24
  * @author alteredq / http://alteredqualia.com/
@@ -7,7 +7,7 @@ import {
7
7
  Ray,
8
8
  Sphere,
9
9
  Vector3
10
- } from 'three';
10
+ } from 'three/webgpu';
11
11
 
12
12
 
13
13
  const _start = /*@__PURE__*/ new Vector3();
@@ -2,7 +2,7 @@ import { Line } from './Line.js';
2
2
  import {
3
3
  Float32BufferAttribute,
4
4
  Vector3
5
- } from 'three';
5
+ } from 'three/webgpu';
6
6
 
7
7
  const _start = /*@__PURE__*/ new Vector3();
8
8
  const _end = /*@__PURE__*/ new Vector3();
@@ -8,12 +8,11 @@ import {
8
8
  Mesh,
9
9
  Sphere,
10
10
  Vector3,
11
- Vector4
12
- } from 'three';
11
+ Vector4,
12
+ Line2NodeMaterial,
13
+ Vector2
14
+ } from 'three/webgpu';
13
15
  import { LineSegmentsGeometry } from './LineSegmentsGeometry.js';
14
- import { LineMaterial } from './LineMaterial.js';
15
-
16
- const _viewport = new Vector4();
17
16
 
18
17
  const _start = new Vector3();
19
18
  const _end = new Vector3();
@@ -30,6 +29,7 @@ const _closestPoint = new Vector3();
30
29
  const _box = new Box3();
31
30
  const _sphere = new Sphere();
32
31
  const _clipToWorldVector = new Vector4();
32
+ const _viewport = new Vector4();
33
33
 
34
34
  let _ray, _lineWidth;
35
35
 
@@ -94,10 +94,10 @@ function raycastWorldUnits( lineSegments, intersects ) {
94
94
  function raycastScreenSpace( lineSegments, camera, intersects ) {
95
95
 
96
96
  const projectionMatrix = camera.projectionMatrix;
97
- const material = lineSegments.material;
98
- const resolution = material.resolution;
99
97
  const matrixWorld = lineSegments.matrixWorld;
100
98
 
99
+ const resolution = lineSegments._resolution;
100
+
101
101
  const geometry = lineSegments.geometry;
102
102
  const instanceStart = geometry.attributes.instanceStart;
103
103
  const instanceEnd = geometry.attributes.instanceEnd;
@@ -231,22 +231,11 @@ function raycastScreenSpace( lineSegments, camera, intersects ) {
231
231
  * to be in world units. {@link Line2} extends this object, forming a polyline instead of individual
232
232
  * segments.
233
233
  *
234
- * This module can only be used with {@link WebGLRenderer}. When using {@link WebGPURenderer},
235
- * import the class from `lines/webgpu/LineSegments2.js`.
236
- *
237
- * ```js
238
- * const geometry = new LineSegmentsGeometry();
239
- * geometry.setPositions( positions );
240
- * geometry.setColors( colors );
241
- *
242
- * const material = new LineMaterial( { linewidth: 5, vertexColors: true } };
243
- *
244
- * const lineSegments = new LineSegments2( geometry, material );
245
- * scene.add( lineSegments );
246
- * ```
234
+ * This module can only be used with {@link WebGPURenderer}. When using {@link WebGLRenderer},
235
+ * import the class from `lines/LineSegments2.js`.
247
236
  *
248
237
  * @augments Mesh
249
- * @three_import import { LineSegments2 } from 'three/addons/lines/LineSegments2.js';
238
+ * @three_import import { LineSegments2 } from 'three/addons/lines/webgpu/LineSegments2.js';
250
239
  */
251
240
  class LineSegments2 extends Mesh {
252
241
 
@@ -254,9 +243,9 @@ class LineSegments2 extends Mesh {
254
243
  * Constructs a new wide line.
255
244
  *
256
245
  * @param {LineSegmentsGeometry} [geometry] - The line geometry.
257
- * @param {LineMaterial} [material] - The line material.
246
+ * @param {Line2NodeMaterial} [material] - The line material.
258
247
  */
259
- constructor( geometry = new LineSegmentsGeometry(), material = new LineMaterial( { color: Math.random() * 0xffffff } ) ) {
248
+ constructor( geometry = new LineSegmentsGeometry(), material = new Line2NodeMaterial( { color: Math.random() * 0xffffff } ) ) {
260
249
 
261
250
  super( geometry, material );
262
251
 
@@ -271,6 +260,8 @@ class LineSegments2 extends Mesh {
271
260
 
272
261
  this.type = 'LineSegments2';
273
262
 
263
+ this._resolution = new Vector2();
264
+
274
265
  }
275
266
 
276
267
  /**
@@ -309,6 +300,13 @@ class LineSegments2 extends Mesh {
309
300
 
310
301
  }
311
302
 
303
+ onBeforeRender( renderer ) {
304
+
305
+ renderer.getViewport( _viewport );
306
+ this._resolution.set( _viewport.z, _viewport.w );
307
+
308
+ }
309
+
312
310
  /**
313
311
  * Computes intersection points between a casted ray and this instance.
314
312
  *
@@ -328,7 +326,7 @@ class LineSegments2 extends Mesh {
328
326
 
329
327
  // early out if no resolution has been set (line was not rendered yet)
330
328
 
331
- if ( worldUnits === false && ( this.material.resolution.x === 0 || this.material.resolution.y === 0 ) ) {
329
+ if ( worldUnits === false && ( this._resolution.x === 0 || this._resolution.y === 0 ) ) {
332
330
 
333
331
  return;
334
332
 
@@ -362,7 +360,7 @@ class LineSegments2 extends Mesh {
362
360
  } else {
363
361
 
364
362
  const distanceToSphere = Math.max( camera.near, _sphere.distanceToPoint( _ray.origin ) );
365
- sphereMargin = getWorldSpaceHalfWidth( camera, distanceToSphere, material.resolution );
363
+ sphereMargin = getWorldSpaceHalfWidth( camera, distanceToSphere, this._resolution );
366
364
 
367
365
  }
368
366
 
@@ -392,7 +390,7 @@ class LineSegments2 extends Mesh {
392
390
  } else {
393
391
 
394
392
  const distanceToBox = Math.max( camera.near, _box.distanceToPoint( _ray.origin ) );
395
- boxMargin = getWorldSpaceHalfWidth( camera, distanceToBox, material.resolution );
393
+ boxMargin = getWorldSpaceHalfWidth( camera, distanceToBox, this._resolution );
396
394
 
397
395
  }
398
396
 
@@ -416,19 +414,6 @@ class LineSegments2 extends Mesh {
416
414
 
417
415
  }
418
416
 
419
- onBeforeRender( renderer ) {
420
-
421
- const uniforms = this.material.uniforms;
422
-
423
- if ( uniforms && uniforms.resolution ) {
424
-
425
- renderer.getViewport( _viewport );
426
- this.material.uniforms.resolution.value.set( _viewport.z, _viewport.w );
427
-
428
- }
429
-
430
- }
431
-
432
417
  }
433
418
 
434
419
  export { LineSegments2 };
@@ -7,7 +7,7 @@ import {
7
7
  Sphere,
8
8
  Vector3,
9
9
  WireframeGeometry
10
- } from 'three';
10
+ } from 'three/webgpu';
11
11
 
12
12
  const _box = new Box3();
13
13
  const _vector = new Vector3();
@@ -0,0 +1,65 @@
1
+ import * as THREE from 'three/webgpu';
2
+ import { Fn, attribute, materialColor, materialOpacity, mix, uniform, vec4 } from 'three/tsl';
3
+
4
+ //Maps a classic material's constructor name to its NodeMaterial
5
+ //equivalent, since only NodeMaterial exposes colorNode.
6
+ const NODE_MATERIAL_CLASSES = {
7
+ MeshBasicMaterial: THREE.MeshBasicNodeMaterial,
8
+ MeshLambertMaterial: THREE.MeshLambertNodeMaterial,
9
+ MeshPhongMaterial: THREE.MeshPhongNodeMaterial,
10
+ MeshStandardMaterial: THREE.MeshStandardNodeMaterial,
11
+ LineBasicMaterial: THREE.LineBasicNodeMaterial,
12
+ };
13
+
14
+ /**
15
+ * Give `material` a colorNode that blends between the two morph colour
16
+ * targets {@link updateMorphColorAttribute} (in utilities.js) keeps
17
+ * published as the 'morphColor0'/'morphColor1' geometry attributes -
18
+ * whichever two morph targets currently have non-zero influence.
19
+ *
20
+ * This replaces the legacy `material.onBeforeCompile = augmentMorphColor`
21
+ * approach, which patched raw GLSL into the classic WebGL shader and has no
22
+ * effect once a material is compiled through the WebGPU/TSL pipeline.
23
+ * three.js's own built-in WebGPU morph target system packs morph colour
24
+ * data into its morph texture (see three's Morph.js) but its blend loop
25
+ * only ever applies the result to position/normal, never colour, so this
26
+ * has to be done by hand.
27
+ *
28
+ * Converts `material` to its NodeMaterial equivalent first if it isn't one
29
+ * already, since only NodeMaterial exposes `colorNode`.
30
+ *
31
+ * @param {THREE.Material} material - Material to give the morph colour
32
+ * blend to; only used for its constructor type if not already a
33
+ * NodeMaterial - all of its usual properties (color, opacity, map, side,
34
+ * etc.) are preserved.
35
+ * @return {THREE.NodeMaterial} The (possibly newly converted) material.
36
+ * Its `userData.uniforms.morphColorMix` uniform drives the blend (0 = fully
37
+ * morphColor0, 1 = fully morphColor1) - kept in sync automatically by
38
+ * updateMorphColorAttribute.
39
+ */
40
+ const applyMorphColorNode = (material) => {
41
+ let target = material;
42
+ if (!target.isNodeMaterial) {
43
+ const NodeMaterialClass = NODE_MATERIAL_CLASSES[material.type] || THREE.MeshBasicNodeMaterial;
44
+ target = new NodeMaterialClass();
45
+ target.copy(material);
46
+ }
47
+
48
+ const morphColorMix = uniform(0);
49
+
50
+ //materialColor already folds in material.color and, if present, .map -
51
+ //multiplying it by the blended per vertex morph colour keeps that
52
+ //compositing instead of replacing it outright.
53
+ target.colorNode = Fn(() => {
54
+ const colorA = attribute('morphColor0', 'vec3');
55
+ const colorB = attribute('morphColor1', 'vec3');
56
+ const blended = mix(colorA, colorB, morphColorMix);
57
+ return vec4(blended.mul(materialColor), materialOpacity);
58
+ })();
59
+
60
+ target.userData.uniforms = { ...target.userData.uniforms, morphColorMix };
61
+
62
+ return target;
63
+ };
64
+
65
+ export { applyMorphColorNode };