@react-three/drei 11.0.0-alpha.6 → 11.0.0-alpha.7

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/webgpu/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import { forwardRef, useImperativeHandle, useMemo, useEffect, useCallback, useRef, useLayoutEffect, useState, Fragment, createContext, useContext } from 'react';
3
- import { useThree, useFrame, extend, createPortal, applyProps, useInstanceHandle, context as context$a, useLoader, addEffect, addAfterEffect } from '@react-three/fiber';
3
+ import { useThree, useFrame, extend, createPortal, applyProps, useInstanceHandle, context as context$b, useLoader, addEffect, addAfterEffect } from '@react-three/fiber';
4
4
  import * as THREE from 'three/webgpu';
5
5
  import { MeshBasicNodeMaterial, RenderTarget, MeshPhongNodeMaterial, MeshNormalNodeMaterial, QuadMesh, MeshPhysicalNodeMaterial, Frustum, Matrix4, CubeRenderTarget, Line2NodeMaterial, Vector3, Vector4, Vector2, Color, InstancedInterleavedBuffer, InterleavedBufferAttribute, Object3D, HalfFloatType, CubeCamera as CubeCamera$1, EventDispatcher, Euler, Quaternion, MathUtils, TextureLoader, Texture as Texture$1, MeshLambertMaterial, DynamicDrawUsage, CatmullRomCurve3, CubicBezierCurve3, QuadraticBezierCurve3, Shape as Shape$1, CanvasTexture, PerspectiveCamera as PerspectiveCamera$1, OrthographicCamera as OrthographicCamera$1, REVISION, AudioListener, AudioLoader, AnimationMixer, BufferAttribute, Box3, Sphere as Sphere$1, InstancedBufferAttribute, DoubleSide, DepthTexture, DepthFormat, UnsignedShortType, CubeTextureLoader, DefaultLoadingManager, Ray, Raycaster, Camera, CubeReflectionMapping, EquirectangularReflectionMapping, Scene, RepeatWrapping, SpriteNodeMaterial, AdditiveBlending, ShadowNode, CylinderGeometry } from 'three/webgpu';
6
6
  import { ArcballControls as ArcballControls$1 } from 'three/examples/jsm/controls/ArcballControls.js';
@@ -16,7 +16,7 @@ import { useGesture } from '@use-gesture/react';
16
16
  import * as ReactDOM from 'react-dom/client';
17
17
  import { TrackballControls as TrackballControls$1 } from 'three/examples/jsm/controls/TrackballControls.js';
18
18
  import { SimplexNoise } from 'three/examples/jsm/math/SimplexNoise.js';
19
- import { uniform, Fn, float, vec4, uniformTexture, uv, vec2 as vec2$2, texture, Discard, max, sub, mix, output, positionWorld, mul, add, negate, dot, refract, div, length, cross, select, vec3, normalize, normalWorld, cameraPosition, screenCoordinate, mx_fractal_noise_vec3, int, thickness, pow, roughness, Loop, ior, screenUV, screenSize, cameraProjectionMatrix, cameraViewMatrix, attenuationColor, attenuationDistance, diffuseColor, transmission, fract, sin, modelWorldMatrix, log, exp, DFGLUT, reflector, clamp, equirectUV, reflect, smoothstep, round, distance, positionLocal, mx_noise_vec3, cos, mat3, normalLocal, attribute, modelViewMatrix, varying, min, frontFacing, fwidth, abs, saturate, positionView, positionGeometry, reference, renderGroup, interleavedGradientNoise, vogelDiskSample, step } from 'three/tsl';
19
+ import { uniform, Fn, float, vec4, uniformTexture, uv, vec2 as vec2$2, Discard, materialReference, context as context$a, max, materialAlphaTest, sub, mix, output, positionWorld, mul, add, materialColor, negate, dot, refract, div, length, cross, select, vec3, normalize, normalWorld, cameraPosition, screenCoordinate, mx_fractal_noise_vec3, int, thickness, pow, roughness, Loop, ior, screenUV, screenSize, cameraProjectionMatrix, cameraViewMatrix, attenuationColor, attenuationDistance, diffuseColor, transmission, fract, sin, modelWorldMatrix, log, exp, DFGLUT, reflector, clamp, materialIOR, equirectUV, materialOpacity, reflect, smoothstep, round, distance, positionLocal, mx_noise_vec3, cos, mat3, normalLocal, attribute, modelViewMatrix, varying, min, frontFacing, fwidth, texture, abs, saturate, positionView, positionGeometry, reference, renderGroup, interleavedGradientNoise, vogelDiskSample, step } from 'three/tsl';
20
20
  import { Line2 } from 'three/examples/jsm/lines/webgpu/Line2.js';
21
21
  import { LineSegments2 } from 'three/examples/jsm/lines/webgpu/LineSegments2.js';
22
22
  import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry.js';
@@ -51,43 +51,150 @@ import { shallow } from 'zustand/shallow';
51
51
  import { hashBlur } from 'three/examples/jsm/tsl/display/hashBlur.js';
52
52
  import PhysicalLightingModel from 'three/src/nodes/functions/PhysicalLightingModel.js';
53
53
 
54
- class DepthNodeMaterial extends MeshBasicNodeMaterial {
55
- _opacity;
54
+ function assignUniform(node, factory, v) {
55
+ const current = node.value;
56
+ if (v === null || v === void 0) {
57
+ if (current && current.isTexture) node.value = factory().value;
58
+ else node.value = v;
59
+ return;
60
+ }
61
+ if (typeof current === "number") {
62
+ node.value = typeof v === "boolean" ? v ? 1 : 0 : v;
63
+ return;
64
+ }
65
+ if (typeof current === "boolean") {
66
+ node.value = !!v;
67
+ return;
68
+ }
69
+ if (current && typeof current === "object") {
70
+ if (typeof v === "object" && !Array.isArray(v)) {
71
+ node.value = v;
72
+ return;
73
+ }
74
+ if (Array.isArray(v) && typeof current.fromArray === "function") {
75
+ current.fromArray(v);
76
+ return;
77
+ }
78
+ if (current.isColor) {
79
+ current.set(v);
80
+ return;
81
+ }
82
+ if (typeof v === "number" && typeof current.setScalar === "function") {
83
+ current.setScalar(v);
84
+ return;
85
+ }
86
+ }
87
+ node.value = v;
88
+ }
89
+ function withUniforms(Base, table) {
90
+ const keys = Object.keys(table);
91
+ const factories = table;
92
+ let installed = false;
93
+ const install = (instance) => {
94
+ for (const key of keys) {
95
+ if (key.startsWith("_") || key in instance) {
96
+ throw new Error(
97
+ `withUniforms: "${key}" is reserved on ${Base.name}. Read three's own node for it (materialOpacity, materialColor, materialReference(...)) instead of declaring a uniform named "${key}".`
98
+ );
99
+ }
100
+ }
101
+ for (const key of keys) {
102
+ Object.defineProperty(Wrapped.prototype, key, {
103
+ get() {
104
+ return this._uniforms?.[key]?.value;
105
+ },
106
+ set(v) {
107
+ const node = this._uniforms?.[key];
108
+ if (node) assignUniform(node, factories[key], v);
109
+ },
110
+ enumerable: false,
111
+ configurable: true
112
+ });
113
+ }
114
+ installed = true;
115
+ };
116
+ const splitParameters = (args) => {
117
+ const params = args[0];
118
+ if (!params || typeof params !== "object" || Array.isArray(params)) return { args, own: null };
119
+ const own = {};
120
+ const rest = {};
121
+ let hasOwn = false;
122
+ for (const k in params) {
123
+ if (Object.prototype.hasOwnProperty.call(factories, k)) {
124
+ own[k] = params[k];
125
+ hasOwn = true;
126
+ } else rest[k] = params[k];
127
+ }
128
+ return hasOwn ? { args: [rest, ...args.slice(1)], own } : { args, own: null };
129
+ };
130
+ class Wrapped extends Base {
131
+ constructor(...args) {
132
+ const { args: baseArgs, own } = splitParameters(args);
133
+ super(...baseArgs);
134
+ if (!installed) install(this);
135
+ const nodes = {};
136
+ for (const key of keys) nodes[key] = factories[key]();
137
+ Object.defineProperty(this, "_uniforms", { value: nodes, enumerable: false, writable: false });
138
+ if (own) for (const key in own) this[key] = own[key];
139
+ }
140
+ get uniforms() {
141
+ return this._uniforms;
142
+ }
143
+ /** Copies values for inherited accessors that NodeMaterial.copy does not visit. */
144
+ copy(source) {
145
+ super.copy(source);
146
+ const from = source._uniforms;
147
+ if (!from) return this;
148
+ for (const key of keys) {
149
+ const value = from[key]?.value;
150
+ const node = this._uniforms[key];
151
+ const own = node.value;
152
+ if (value && value.isTexture) node.value = value;
153
+ else if (own && typeof own.copy === "function") own.copy(value);
154
+ else node.value = value;
155
+ }
156
+ return this;
157
+ }
158
+ }
159
+ return Wrapped;
160
+ }
161
+
162
+ class DepthNodeMaterial extends withUniforms(MeshBasicNodeMaterial, {
163
+ /** Shadow strength, 1 = full darkening, 0 = none */
164
+ shadowOpacity: () => uniform(1)
165
+ }) {
56
166
  constructor(opacity = 1) {
57
167
  super();
58
- this._opacity = uniform(opacity);
168
+ this.shadowOpacity = opacity;
59
169
  this.depthTest = false;
60
170
  this.depthWrite = false;
61
171
  this._buildShader();
62
172
  }
63
173
  _buildShader() {
64
- const opacityUniform = this._opacity;
174
+ const { shadowOpacity } = this.uniforms;
65
175
  this.colorNode = Fn(() => {
66
- const shadowStrength = opacityUniform.mul(0.7);
176
+ const shadowStrength = shadowOpacity.mul(0.7);
67
177
  const gray = float(1).sub(shadowStrength);
68
178
  return vec4(gray, gray, gray, 1);
69
179
  })();
70
180
  }
71
- set shadowOpacity(value) {
72
- this._opacity.value = value;
73
- }
74
181
  }
75
- class BlurNodeMaterial extends MeshBasicNodeMaterial {
76
- _inputTexture;
77
- _blurAmount;
182
+ class BlurNodeMaterial extends withUniforms(MeshBasicNodeMaterial, {
183
+ /** Texture to blur */
184
+ inputTexture: () => uniformTexture(new THREE.Texture()),
185
+ /** Per-tap UV offset */
186
+ blurAmount: () => uniform(0)
187
+ }) {
78
188
  _direction;
79
189
  constructor(direction = "horizontal") {
80
190
  super();
81
- this._inputTexture = uniformTexture(new THREE.Texture());
82
- this._blurAmount = uniform(0);
83
191
  this._direction = direction;
84
192
  this.depthTest = false;
85
193
  this.depthWrite = false;
86
194
  this._buildShader();
87
195
  }
88
196
  _buildShader() {
89
- const inputTex = this._inputTexture;
90
- const blurAmountUniform = this._blurAmount;
197
+ const { inputTexture: inputTex, blurAmount: blurAmountUniform } = this.uniforms;
91
198
  const isHorizontal = this._direction === "horizontal";
92
199
  const weights = [0.051, 0.0918, 0.12245, 0.1531, 0.1633, 0.1531, 0.12245, 0.0918, 0.051];
93
200
  this.colorNode = Fn(() => {
@@ -96,18 +203,12 @@ class BlurNodeMaterial extends MeshBasicNodeMaterial {
96
203
  for (let i = 0; i < 9; i++) {
97
204
  const offset = float(i - 4).mul(blurAmountUniform);
98
205
  const sampleUV = isHorizontal ? texCoord.add(vec2$2(offset, 0)) : texCoord.add(vec2$2(0, offset));
99
- const sampledColor = texture(inputTex, sampleUV);
206
+ const sampledColor = inputTex.sample(sampleUV);
100
207
  result.addAssign(sampledColor.mul(weights[i]));
101
208
  }
102
209
  return result;
103
210
  })();
104
211
  }
105
- set inputTexture(value) {
106
- this._inputTexture.value = value;
107
- }
108
- set blurAmount(value) {
109
- this._blurAmount.value = value;
110
- }
111
212
  }
112
213
  const ContactShadows = /* @__PURE__ */ React.forwardRef(
113
214
  ({
@@ -242,9 +343,13 @@ const discardFragment = /* @__PURE__ */ Fn(() => {
242
343
  Discard();
243
344
  return vec4(0, 0, 0, 0);
244
345
  });
245
- const DiscardMaterial = /* @__PURE__ */ new MeshBasicNodeMaterial({
246
- fragmentNode: discardFragment()
247
- });
346
+ class DiscardNodeMaterial extends MeshBasicNodeMaterial {
347
+ constructor() {
348
+ super();
349
+ this.fragmentNode = discardFragment();
350
+ }
351
+ }
352
+ const DiscardMaterial = /* @__PURE__ */ new DiscardNodeMaterial();
248
353
 
249
354
  function isLight(object) {
250
355
  return object.isLight;
@@ -255,105 +360,51 @@ function isGeometry$1(object) {
255
360
  const accumulativeContext = /* @__PURE__ */ React.createContext(
256
361
  null
257
362
  );
258
- class SoftShadowMaterialImpl extends MeshBasicNodeMaterial {
259
- _color;
260
- _blend;
261
- _alphaTest;
262
- _opacity;
263
- _map;
363
+ class SoftShadowMaterialImpl extends withUniforms(MeshBasicNodeMaterial, {
364
+ /** Shadow color, black */
365
+ shadowColor: () => uniform(new THREE.Color()),
366
+ /** Colorblend, how much colors turn to black, 0 is black */
367
+ blend: () => uniform(2)
368
+ }) {
264
369
  constructor() {
265
370
  super();
266
- this._color = uniform(new THREE.Color());
267
- this._blend = uniform(2);
268
- this._alphaTest = uniform(0.75);
269
- this._opacity = uniform(0);
270
- this._map = uniformTexture(new THREE.Texture());
271
371
  this.transparent = true;
272
372
  this.depthWrite = false;
373
+ this.opacity = 0;
374
+ this.alphaTest = 0.75;
375
+ this.map = new THREE.Texture();
376
+ this.alphaTestNode = float(0);
273
377
  this._buildShader();
274
378
  }
275
379
  _buildShader() {
276
- const colorUniform = this._color;
277
- const blendUniform = this._blend;
278
- const alphaTestUniform = this._alphaTest;
279
- const opacityUniform = this._opacity;
280
- const mapTex = this._map;
380
+ const { shadowColor, blend } = this.uniforms;
381
+ const map = materialReference("map", "texture", this);
281
382
  this.colorNode = Fn(() => {
282
- const texCoord = uv();
283
- const sampled = texture(mapTex, texCoord);
284
- const rgb = colorUniform.mul(sampled.r).mul(blendUniform);
383
+ const sampled = context$a(map, { getUV: () => uv() });
384
+ const rgb = shadowColor.mul(sampled.r).mul(blend);
285
385
  const sum = sampled.r.add(sampled.g).add(sampled.b);
286
- const alpha = max(float(0), float(1).sub(sum.div(alphaTestUniform))).mul(opacityUniform);
386
+ const alpha = max(float(0), float(1).sub(sum.div(materialAlphaTest)));
287
387
  return vec4(rgb, alpha);
288
388
  })();
289
389
  }
290
- get shadowColor() {
291
- return this._color.value;
292
- }
293
- set shadowColor(v) {
294
- if (v instanceof THREE.Color) {
295
- this._color.value.copy(v);
296
- } else {
297
- this._color.value.set(v);
298
- }
299
- }
300
- get blend() {
301
- return this._blend.value;
302
- }
303
- set blend(v) {
304
- this._blend.value = v;
305
- }
306
- get alphaTest() {
307
- return this._alphaTest.value;
308
- }
309
- set alphaTest(v) {
310
- this._alphaTest.value = v;
311
- }
312
- get opacity() {
313
- return this._opacity.value;
314
- }
315
- set opacity(v) {
316
- if (!this._opacity) this._opacity = uniform(v);
317
- else this._opacity.value = v;
318
- }
319
- get map() {
320
- if (!this._map) {
321
- this._map = uniformTexture(new THREE.Texture());
322
- }
323
- return this._map.value;
324
- }
325
- set map(v) {
326
- this._map.value = v;
327
- }
328
390
  }
329
- class ProgressiveShadowMaterial extends MeshPhongNodeMaterial {
330
- _previousShadowMap;
331
- _averagingWindow;
391
+ class ProgressiveShadowMaterial extends withUniforms(MeshPhongNodeMaterial, {
392
+ /** Accumulation buffer from the previous frame, blended with the current shading */
393
+ previousShadowMap: () => uniformTexture(new THREE.Texture()),
394
+ /** Mix 1/averagingWindow of the new shading into the accumulated frames */
395
+ averagingWindow: () => uniform(100)
396
+ }) {
332
397
  constructor(initialTexture) {
333
398
  super();
334
- this._previousShadowMap = texture(initialTexture);
335
- this._averagingWindow = uniform(100);
399
+ this.previousShadowMap = initialTexture;
336
400
  this.fog = false;
337
401
  this._buildShader();
338
402
  }
339
403
  _buildShader() {
340
- const previousMapTex = this._previousShadowMap;
341
- const averagingWindowUniform = this._averagingWindow;
404
+ const { previousShadowMap, averagingWindow } = this.uniforms;
342
405
  const uvNode = uv();
343
406
  this.vertexNode = vec4(sub(uvNode, vec2$2(0.5)).mul(2), 1, 1);
344
- this.outputNode = vec4(mix(previousMapTex.sample(uv()), output, float(1).div(averagingWindowUniform)));
345
- }
346
- get previousShadowMap() {
347
- return this._previousShadowMap.value;
348
- }
349
- set previousShadowMap(v) {
350
- this._previousShadowMap.value = v;
351
- }
352
- get averagingWindow() {
353
- return this._averagingWindow.value;
354
- }
355
- set averagingWindow(v) {
356
- this._averagingWindow.value = v;
407
+ this.outputNode = vec4(mix(previousShadowMap.sample(uv()), output, float(1).div(averagingWindow)));
357
408
  }
358
409
  }
359
410
  class ProgressiveLightMap {
@@ -470,7 +521,7 @@ const AccumulativeShadows = /* @__PURE__ */ React.forwardRef(
470
521
  plm.configure(gPlane.current);
471
522
  }, [plm]);
472
523
  React.useEffect(() => {
473
- softShadowMat.shadowColor = color;
524
+ softShadowMat.shadowColor.set(color);
474
525
  softShadowMat.blend = colorBlend;
475
526
  softShadowMat.map = plm.progressiveLightMap2.texture;
476
527
  softShadowMat.toneMapped = toneMapped;
@@ -683,19 +734,18 @@ const Edges = /* @__PURE__ */ React.forwardRef(
683
734
  }
684
735
  );
685
736
 
686
- class CausticsProjectionMaterial extends MeshBasicNodeMaterial {
687
- _causticsTexture;
688
- _causticsTextureB;
689
- _color;
690
- _lightProjMatrix;
691
- _lightViewMatrix;
737
+ class CausticsProjectionMaterial extends withUniforms(MeshBasicNodeMaterial, {
738
+ /** Front-face caustics, rendered by CausticsMaterial */
739
+ causticsTexture: () => uniformTexture(new THREE.Texture()),
740
+ /** Back-face caustics, rendered by CausticsMaterial when `backside` is enabled */
741
+ causticsTextureB: () => uniformTexture(new THREE.Texture()),
742
+ /** Projection matrix of the light camera */
743
+ lightProjMatrix: () => uniform(new THREE.Matrix4()),
744
+ /** View matrix (matrixWorldInverse) of the light camera */
745
+ lightViewMatrix: () => uniform(new THREE.Matrix4())
746
+ }) {
692
747
  constructor() {
693
748
  super();
694
- this._causticsTexture = uniformTexture(new THREE.Texture());
695
- this._causticsTextureB = uniformTexture(new THREE.Texture());
696
- this._color = uniform(new THREE.Color(1, 1, 1));
697
- this._lightProjMatrix = uniform(new THREE.Matrix4());
698
- this._lightViewMatrix = uniform(new THREE.Matrix4());
699
749
  this.transparent = true;
700
750
  this.blending = THREE.CustomBlending;
701
751
  this.blendSrc = THREE.OneFactor;
@@ -704,100 +754,68 @@ class CausticsProjectionMaterial extends MeshBasicNodeMaterial {
704
754
  this._buildShader();
705
755
  }
706
756
  _buildShader() {
707
- const causticsTexture = this._causticsTexture;
708
- const causticsTextureB = this._causticsTextureB;
709
- const colorUniform = this._color;
710
- const lightProjMatrix = this._lightProjMatrix;
711
- const lightViewMatrix = this._lightViewMatrix;
757
+ const { causticsTexture, causticsTextureB, lightProjMatrix, lightViewMatrix } = this.uniforms;
712
758
  this.colorNode = Fn(() => {
713
759
  const worldPos = positionWorld;
714
760
  const worldPos4 = vec4(worldPos, 1);
715
761
  const lightSpacePos = mul(lightProjMatrix, mul(lightViewMatrix, worldPos4)).toVar();
716
762
  const xyz = lightSpacePos.xyz.div(lightSpacePos.w);
717
763
  const lightUv = xyz.mul(0.5).add(0.5).xy;
718
- const front = texture(causticsTexture, lightUv).rgb;
719
- const back = texture(causticsTextureB, lightUv).rgb;
720
- return vec4(add(front, back).mul(colorUniform), 1);
764
+ const front = causticsTexture.sample(lightUv).rgb;
765
+ const back = causticsTextureB.sample(lightUv).rgb;
766
+ return vec4(add(front, back).mul(materialColor), 1);
721
767
  })();
722
768
  }
723
- //* Uniform Accessors ==============================
724
- get causticsTexture() {
725
- return this._causticsTexture.value;
726
- }
727
- set causticsTexture(v) {
728
- this._causticsTexture.value = v;
729
- }
730
- get causticsTextureB() {
731
- return this._causticsTextureB.value;
732
- }
733
- set causticsTextureB(v) {
734
- this._causticsTextureB.value = v;
735
- }
736
- get color() {
737
- return this._color?.value;
738
- }
739
- set color(v) {
740
- if (this._color) this._color.value = v;
741
- }
742
- get lightProjMatrix() {
743
- return this._lightProjMatrix.value;
744
- }
745
- set lightProjMatrix(v) {
746
- this._lightProjMatrix.value = v;
747
- }
748
- get lightViewMatrix() {
749
- return this._lightViewMatrix.value;
750
- }
751
- set lightViewMatrix(v) {
752
- this._lightViewMatrix.value = v;
753
- }
754
769
  }
755
- class CausticsMaterial extends MeshBasicNodeMaterial {
756
- _cameraMatrixWorld;
757
- _cameraProjectionMatrixInv;
758
- _normalTexture;
759
- _depthTexture;
760
- _lightDir;
761
- _lightPlaneNormal;
762
- _lightPlaneConstant;
763
- _near;
764
- _far;
765
- _worldRadius;
766
- _ior;
767
- _resolution;
768
- _size;
769
- _intensity;
770
+ class CausticsMaterial extends withUniforms(MeshBasicNodeMaterial, {
771
+ /** World matrix of the light camera */
772
+ cameraMatrixWorld: () => uniform(new THREE.Matrix4()),
773
+ /** Inverse projection matrix of the light camera, for depth reconstruction */
774
+ cameraProjectionMatrixInv: () => uniform(new THREE.Matrix4()),
775
+ /** World-space normals of the refractive geometry, rendered by CausticsNormalMaterial */
776
+ normalTexture: () => uniformTexture(new THREE.Texture()),
777
+ /** Depth of the refractive geometry, from the same render target as `normalTexture` */
778
+ depthTexture: () => uniformTexture(new THREE.Texture()),
779
+ /** Direction light travels in (inverse of the light position direction) */
780
+ lightDir: () => uniform(new THREE.Vector3(0, 1, 0)),
781
+ /** Normal of the receiving plane in light space */
782
+ lightPlaneNormal: () => uniform(new THREE.Vector3(0, 1, 0)),
783
+ /** Plane constant of the receiving plane */
784
+ lightPlaneConstant: () => uniform(0),
785
+ /** Light camera near plane */
786
+ near: () => uniform(0.1),
787
+ /** Light camera far plane */
788
+ far: () => uniform(100),
789
+ /** The texel size in world units */
790
+ worldRadius: () => uniform(1 / 40),
791
+ /** Refraction index of the surface */
792
+ ior: () => uniform(1.1),
793
+ /** Buffer resolution in pixels */
794
+ resolution: () => uniform(1024),
795
+ /** Half-extent of the light camera frustum */
796
+ size: () => uniform(10),
797
+ /** Intensity of the projected caustics */
798
+ intensity: () => uniform(0.5)
799
+ }) {
770
800
  constructor() {
771
801
  super();
772
- this._cameraMatrixWorld = uniform(new THREE.Matrix4());
773
- this._cameraProjectionMatrixInv = uniform(new THREE.Matrix4());
774
- this._normalTexture = uniformTexture(new THREE.Texture());
775
- this._depthTexture = uniformTexture(new THREE.Texture());
776
- this._lightDir = uniform(new THREE.Vector3(0, 1, 0));
777
- this._lightPlaneNormal = uniform(new THREE.Vector3(0, 1, 0));
778
- this._lightPlaneConstant = uniform(0);
779
- this._near = uniform(0.1);
780
- this._far = uniform(100);
781
- this._worldRadius = uniform(1 / 40);
782
- this._ior = uniform(1.1);
783
- this._resolution = uniform(1024);
784
- this._size = uniform(10);
785
- this._intensity = uniform(0.5);
786
802
  this._buildShader();
787
803
  }
788
804
  _buildShader() {
789
- const cameraMatrixWorld = this._cameraMatrixWorld;
790
- const cameraProjectionMatrixInv = this._cameraProjectionMatrixInv;
791
- const normalTexture = this._normalTexture;
792
- const depthTexture = this._depthTexture;
793
- const lightDir = this._lightDir;
794
- const lightPlaneNormal = this._lightPlaneNormal;
795
- const lightPlaneConstant = this._lightPlaneConstant;
796
- const worldRadius = this._worldRadius;
797
- const ior = this._ior;
798
- const resolution = this._resolution;
799
- const size = this._size;
800
- const intensity = this._intensity;
805
+ const {
806
+ cameraMatrixWorld,
807
+ cameraProjectionMatrixInv,
808
+ normalTexture,
809
+ depthTexture,
810
+ lightDir,
811
+ lightPlaneNormal,
812
+ lightPlaneConstant,
813
+ worldRadius,
814
+ ior,
815
+ resolution,
816
+ size,
817
+ intensity
818
+ } = this.uniforms;
801
819
  const worldPosFromDepth = Fn(({ depth, coord }) => {
802
820
  const z = depth.mul(2).sub(1);
803
821
  const clipSpacePos = vec4(coord.mul(2).sub(1), z, 1);
@@ -824,7 +842,7 @@ class CausticsMaterial extends MeshBasicNodeMaterial {
824
842
  );
825
843
  this.colorNode = Fn(() => {
826
844
  const baseUv = uv();
827
- const centerDepth = texture(depthTexture, baseUv).x;
845
+ const centerDepth = depthTexture.sample(baseUv).x;
828
846
  const causticTexelSize = div(float(1), resolution).mul(size).mul(2);
829
847
  const texelsNeeded = div(worldRadius, causticTexelSize);
830
848
  const sampleRadius = div(texelsNeeded, resolution);
@@ -836,14 +854,14 @@ class CausticsMaterial extends MeshBasicNodeMaterial {
836
854
  const uv2 = add(baseUv, mul(offset2, sampleRadius));
837
855
  const uv3 = add(baseUv, mul(offset3, sampleRadius));
838
856
  const uv4 = add(baseUv, mul(offset4, sampleRadius));
839
- const normal1 = sub(mul(texture(normalTexture, uv1).rgb, 2), 1);
840
- const normal2 = sub(mul(texture(normalTexture, uv2).rgb, 2), 1);
841
- const normal3 = sub(mul(texture(normalTexture, uv3).rgb, 2), 1);
842
- const normal4 = sub(mul(texture(normalTexture, uv4).rgb, 2), 1);
843
- const depth1 = texture(depthTexture, uv1).x;
844
- const depth2 = texture(depthTexture, uv2).x;
845
- const depth3 = texture(depthTexture, uv3).x;
846
- const depth4 = texture(depthTexture, uv4).x;
857
+ const normal1 = sub(mul(normalTexture.sample(uv1).rgb, 2), 1);
858
+ const normal2 = sub(mul(normalTexture.sample(uv2).rgb, 2), 1);
859
+ const normal3 = sub(mul(normalTexture.sample(uv3).rgb, 2), 1);
860
+ const normal4 = sub(mul(normalTexture.sample(uv4).rgb, 2), 1);
861
+ const depth1 = depthTexture.sample(uv1).x;
862
+ const depth2 = depthTexture.sample(uv2).x;
863
+ const depth3 = depthTexture.sample(uv3).x;
864
+ const depth4 = depthTexture.sample(uv4).x;
847
865
  const pos1 = worldPosFromDepth({ depth: depth1, coord: uv1 });
848
866
  const pos2 = worldPosFromDepth({ depth: depth2, coord: uv2 });
849
867
  const pos3 = worldPosFromDepth({ depth: depth3, coord: uv3 });
@@ -882,91 +900,6 @@ class CausticsMaterial extends MeshBasicNodeMaterial {
882
900
  return vec4(vec3(finalCaustic), 1);
883
901
  })();
884
902
  }
885
- //* Uniform Accessors ==============================
886
- get cameraMatrixWorld() {
887
- return this._cameraMatrixWorld.value;
888
- }
889
- set cameraMatrixWorld(v) {
890
- this._cameraMatrixWorld.value = v;
891
- }
892
- get cameraProjectionMatrixInv() {
893
- return this._cameraProjectionMatrixInv.value;
894
- }
895
- set cameraProjectionMatrixInv(v) {
896
- this._cameraProjectionMatrixInv.value = v;
897
- }
898
- get normalTexture() {
899
- return this._normalTexture.value;
900
- }
901
- set normalTexture(v) {
902
- this._normalTexture.value = v ?? new THREE.Texture();
903
- }
904
- get depthTexture() {
905
- return this._depthTexture.value;
906
- }
907
- set depthTexture(v) {
908
- this._depthTexture.value = v ?? new THREE.Texture();
909
- }
910
- get lightDir() {
911
- return this._lightDir.value;
912
- }
913
- set lightDir(v) {
914
- this._lightDir.value = v;
915
- }
916
- get lightPlaneNormal() {
917
- return this._lightPlaneNormal.value;
918
- }
919
- set lightPlaneNormal(v) {
920
- this._lightPlaneNormal.value = v;
921
- }
922
- get lightPlaneConstant() {
923
- return this._lightPlaneConstant.value;
924
- }
925
- set lightPlaneConstant(v) {
926
- this._lightPlaneConstant.value = v;
927
- }
928
- get near() {
929
- return this._near.value;
930
- }
931
- set near(v) {
932
- this._near.value = v;
933
- }
934
- get far() {
935
- return this._far.value;
936
- }
937
- set far(v) {
938
- this._far.value = v;
939
- }
940
- get worldRadius() {
941
- return this._worldRadius.value;
942
- }
943
- set worldRadius(v) {
944
- this._worldRadius.value = v;
945
- }
946
- get ior() {
947
- return this._ior.value;
948
- }
949
- set ior(v) {
950
- this._ior.value = v;
951
- }
952
- get resolution() {
953
- return this._resolution.value;
954
- }
955
- set resolution(v) {
956
- this._resolution.value = v;
957
- }
958
- get size() {
959
- return this._size.value;
960
- }
961
- set size(v) {
962
- this._size.value = v;
963
- }
964
- get intensity() {
965
- return this._intensity.value;
966
- }
967
- set intensity(v) {
968
- this._intensity.value = v;
969
- }
970
903
  }
971
904
  class CausticsNormalMaterial extends MeshNormalNodeMaterial {
972
905
  constructor(side = THREE.FrontSide) {
@@ -977,18 +910,6 @@ class CausticsNormalMaterial extends MeshNormalNodeMaterial {
977
910
  })();
978
911
  }
979
912
  }
980
- const NORMAL_FBO_PROPS = {
981
- depthBuffer: true,
982
- minFilter: THREE.LinearFilter,
983
- magFilter: THREE.LinearFilter,
984
- type: THREE.UnsignedByteType
985
- };
986
- const CAUSTIC_FBO_PROPS = {
987
- minFilter: THREE.LinearMipmapLinearFilter,
988
- magFilter: THREE.LinearFilter,
989
- type: THREE.FloatType,
990
- generateMipmaps: true
991
- };
992
913
  const Caustics = /* @__PURE__ */ React.forwardRef(
993
914
  ({
994
915
  debug,
@@ -1012,10 +933,30 @@ const Caustics = /* @__PURE__ */ React.forwardRef(
1012
933
  const plane = React.useRef(null);
1013
934
  const gl = useThree((state) => state.gl);
1014
935
  const helper = useHelper(debug && camera, THREE.CameraHelper);
1015
- const normalTarget = useFBO(resolution, resolution, NORMAL_FBO_PROPS);
1016
- const normalTargetB = useFBO(resolution, resolution, NORMAL_FBO_PROPS);
1017
- const causticsTarget = useFBO(resolution, resolution, CAUSTIC_FBO_PROPS);
1018
- const causticsTargetB = useFBO(resolution, resolution, CAUSTIC_FBO_PROPS);
936
+ const normalTarget = useFBO(resolution, resolution, {
937
+ depthBuffer: true,
938
+ minFilter: THREE.LinearFilter,
939
+ magFilter: THREE.LinearFilter,
940
+ type: THREE.UnsignedByteType
941
+ });
942
+ const normalTargetB = useFBO(resolution, resolution, {
943
+ depthBuffer: true,
944
+ minFilter: THREE.LinearFilter,
945
+ magFilter: THREE.LinearFilter,
946
+ type: THREE.UnsignedByteType
947
+ });
948
+ const causticsTarget = useFBO(resolution, resolution, {
949
+ minFilter: THREE.LinearMipmapLinearFilter,
950
+ magFilter: THREE.LinearFilter,
951
+ type: THREE.FloatType,
952
+ generateMipmaps: true
953
+ });
954
+ const causticsTargetB = useFBO(resolution, resolution, {
955
+ minFilter: THREE.LinearMipmapLinearFilter,
956
+ magFilter: THREE.LinearFilter,
957
+ type: THREE.FloatType,
958
+ generateMipmaps: true
959
+ });
1019
960
  const [normalMat] = React.useState(() => new CausticsNormalMaterial(THREE.FrontSide));
1020
961
  const [normalMatB] = React.useState(() => new CausticsNormalMaterial(THREE.BackSide));
1021
962
  const [causticsMaterial] = React.useState(() => new CausticsMaterial());
@@ -1287,9 +1228,9 @@ class TransmissionLightingModel extends PhysicalLightingModel {
1287
1228
  const uvR = getRefractionUV(sampleNorm, v, iorR, sampleThickness, position);
1288
1229
  const uvG = getRefractionUV(sampleNorm, v, iorG, sampleThickness, position);
1289
1230
  const uvB = getRefractionUV(sampleNorm, v, iorB, sampleThickness, position);
1290
- const sampleR = texture(bufferTex, uvR);
1291
- const sampleG = texture(bufferTex, uvG);
1292
- const sampleB = texture(bufferTex, uvB);
1231
+ const sampleR = bufferTex.sample(uvR);
1232
+ const sampleG = bufferTex.sample(uvG);
1233
+ const sampleB = bufferTex.sample(uvB);
1293
1234
  transmissionAccum.x.addAssign(sampleR.r);
1294
1235
  transmissionAccum.y.addAssign(sampleG.g);
1295
1236
  transmissionAccum.z.addAssign(sampleB.b);
@@ -1299,7 +1240,7 @@ class TransmissionLightingModel extends PhysicalLightingModel {
1299
1240
  const debugNdotV = n.dot(v);
1300
1241
  const isNeg = debugNdotV.lessThan(0);
1301
1242
  const sampleUVFlipped = vec2$2(screenUV.x, screenUV.y.oneMinus()).clamp(1e-3, 0.999);
1302
- const directSampleFlipped = texture(bufferTex, sampleUVFlipped);
1243
+ const directSampleFlipped = bufferTex.sample(sampleUVFlipped);
1303
1244
  const testRay = getVolumeTransmissionRay(n, v, thickness, ior);
1304
1245
  const rayLen = length(testRay);
1305
1246
  const manualUV = screenCoordinate.xy.div(screenSize);
@@ -1307,9 +1248,9 @@ class TransmissionLightingModel extends PhysicalLightingModel {
1307
1248
  const flippedUV = vec2$2(screenUV.x, screenUV.y.oneMinus()).clamp(1e-3, 0.999);
1308
1249
  const useFlipped = screenUV.x.greaterThan(0.5);
1309
1250
  const splitSampleUV = select(useFlipped, flippedUV, noFlipUV);
1310
- const splitSample = texture(bufferTex, splitSampleUV);
1311
- const directSampleNoFlip = texture(bufferTex, screenUV.clamp(1e-3, 0.999));
1312
- const centerSample = texture(bufferTex, vec2$2(0.5, 0.5));
1251
+ const splitSample = bufferTex.sample(splitSampleUV);
1252
+ const directSampleNoFlip = bufferTex.sample(screenUV.clamp(1e-3, 0.999));
1253
+ const centerSample = bufferTex.sample(vec2$2(0.5, 0.5));
1313
1254
  const transmissionRayDbg = getVolumeTransmissionRay(n, v, thickness, ior);
1314
1255
  const refractedRayExitDbg = position.add(transmissionRayDbg);
1315
1256
  const ndcPosDbg = cameraProjectionMatrix.mul(cameraViewMatrix.mul(vec4(refractedRayExitDbg, 1)));
@@ -1326,7 +1267,7 @@ class TransmissionLightingModel extends PhysicalLightingModel {
1326
1267
  projUVDbg.divAssign(2);
1327
1268
  projUVDbg.assign(vec2$2(projUVDbg.x, projUVDbg.y.oneMinus()));
1328
1269
  projUVDbg.assign(projUVDbg.clamp(1e-3, 0.999));
1329
- const projectedSample = texture(bufferTex, projUVDbg);
1270
+ const projectedSample = bufferTex.sample(projUVDbg);
1330
1271
  const wNorm = clipPosDbg.w.div(10).add(0.5).clamp(0, 1);
1331
1272
  const isBehind = clipPosDbg.w.lessThan(0);
1332
1273
  const rawNdcX = clipPosDbg.x.div(safeWDbg2);
@@ -1449,18 +1390,29 @@ class TransmissionLightingModel extends PhysicalLightingModel {
1449
1390
  }
1450
1391
  }
1451
1392
  }
1452
- class MeshTransmissionMaterialImpl extends MeshPhysicalNodeMaterial {
1453
- // Custom uniforms
1454
- _chromaticAberration;
1455
- _anisotropicBlur;
1456
- _time;
1457
- _distortion;
1458
- _distortionScale;
1459
- _temporalDistortion;
1460
- _buffer;
1461
- // Texture node
1462
- _transmissionValue;
1463
- _debugMode;
1393
+ function createPlaceholderTexture() {
1394
+ const placeholder = new THREE.DataTexture(new Uint8Array([255, 255, 255, 255]), 1, 1);
1395
+ placeholder.needsUpdate = true;
1396
+ return placeholder;
1397
+ }
1398
+ class MeshTransmissionMaterialImpl extends withUniforms(MeshPhysicalNodeMaterial, {
1399
+ /** Chromatic aberration, default: 0.05 */
1400
+ chromaticAberration: () => uniform(0.05),
1401
+ /** Anisotropic blur, default: 0.1 */
1402
+ anisotropicBlur: () => uniform(0.1),
1403
+ /** Elapsed time, drives the temporal distortion */
1404
+ time: () => uniform(0),
1405
+ /** Distortion, default: 0 */
1406
+ distortion: () => uniform(0),
1407
+ /** Distortion scale, default: 0.5 */
1408
+ distortionScale: () => uniform(0.5),
1409
+ /** Temporal distortion (speed of movement), default: 0 */
1410
+ temporalDistortion: () => uniform(0),
1411
+ /** The scene rendered into a texture (use it to share a texture between materials) */
1412
+ buffer: () => uniformTexture(createPlaceholderTexture()),
1413
+ /** Debug mode for development (0 = normal, 1-19 = various debug outputs), default: 0 */
1414
+ debugMode: () => uniform(0)
1415
+ }) {
1464
1416
  /** Type flag for identification */
1465
1417
  isMeshTransmissionMaterial = true;
1466
1418
  _samples;
@@ -1469,27 +1421,26 @@ class MeshTransmissionMaterialImpl extends MeshPhysicalNodeMaterial {
1469
1421
  super();
1470
1422
  this._samples = samples;
1471
1423
  this._transmissionSampler = transmissionSampler;
1472
- this._chromaticAberration = uniform(0.05);
1473
- this._anisotropicBlur = uniform(0.1);
1474
- this._time = uniform(0);
1475
- this._distortion = uniform(0);
1476
- this._distortionScale = uniform(0.5);
1477
- this._temporalDistortion = uniform(0);
1478
- this._debugMode = uniform(0);
1479
- const placeholderTexture = new THREE.DataTexture(new Uint8Array([255, 255, 255, 255]), 1, 1);
1480
- placeholderTexture.needsUpdate = true;
1481
- this._buffer = uniformTexture(placeholderTexture);
1482
- this._transmissionValue = uniform(1);
1483
- this.transmission = transmissionSampler ? 1 : 1e-3;
1424
+ this.transmission = 1;
1484
1425
  this.roughness = 0;
1485
1426
  this.thickness = 0;
1486
1427
  this.ior = 1.5;
1487
1428
  this.transparent = true;
1488
1429
  }
1489
1430
  setupLightingModel() {
1490
- if (this._transmissionSampler) {
1431
+ if (this._transmissionSampler || !this.useTransmission) {
1491
1432
  return super.setupLightingModel();
1492
1433
  }
1434
+ const {
1435
+ buffer,
1436
+ chromaticAberration,
1437
+ anisotropicBlur,
1438
+ time,
1439
+ distortion,
1440
+ distortionScale,
1441
+ temporalDistortion,
1442
+ debugMode
1443
+ } = this.uniforms;
1493
1444
  return new TransmissionLightingModel(
1494
1445
  {
1495
1446
  clearcoat: this.useClearcoat,
@@ -1499,82 +1450,18 @@ class MeshTransmissionMaterialImpl extends MeshPhysicalNodeMaterial {
1499
1450
  dispersion: this.useDispersion
1500
1451
  },
1501
1452
  {
1502
- bufferTex: this._buffer,
1503
- chromaticAberration: this._chromaticAberration,
1504
- anisotropicBlur: this._anisotropicBlur,
1505
- timeUniform: this._time,
1506
- distortion: this._distortion,
1507
- distortionScale: this._distortionScale,
1508
- temporalDistortion: this._temporalDistortion,
1453
+ bufferTex: buffer,
1454
+ chromaticAberration,
1455
+ anisotropicBlur,
1456
+ timeUniform: time,
1457
+ distortion,
1458
+ distortionScale,
1459
+ temporalDistortion,
1509
1460
  samples: this._samples,
1510
- debugMode: this._debugMode
1461
+ debugMode
1511
1462
  }
1512
1463
  );
1513
1464
  }
1514
- //* Property Accessors ==============================
1515
- get time() {
1516
- return this._time.value;
1517
- }
1518
- set time(v) {
1519
- this._time.value = v;
1520
- }
1521
- get buffer() {
1522
- return this._buffer.value;
1523
- }
1524
- set buffer(v) {
1525
- this._buffer.value = v;
1526
- }
1527
- get chromaticAberration() {
1528
- return this._chromaticAberration.value;
1529
- }
1530
- set chromaticAberration(v) {
1531
- this._chromaticAberration.value = v;
1532
- }
1533
- get anisotropicBlur() {
1534
- return this._anisotropicBlur.value;
1535
- }
1536
- set anisotropicBlur(v) {
1537
- this._anisotropicBlur.value = v;
1538
- }
1539
- get distortion() {
1540
- return this._distortion.value;
1541
- }
1542
- set distortion(v) {
1543
- this._distortion.value = v;
1544
- }
1545
- get distortionScale() {
1546
- return this._distortionScale.value;
1547
- }
1548
- set distortionScale(v) {
1549
- this._distortionScale.value = v;
1550
- }
1551
- get temporalDistortion() {
1552
- return this._temporalDistortion.value;
1553
- }
1554
- set temporalDistortion(v) {
1555
- this._temporalDistortion.value = v;
1556
- }
1557
- get debugMode() {
1558
- return this._debugMode.value;
1559
- }
1560
- set debugMode(v) {
1561
- this._debugMode.value = v;
1562
- }
1563
- // Expose _transmission for legacy compatibility
1564
- // This stores the user's intended transmission value, separate from the base class's transmission
1565
- // which is kept at 0.001 to enable the transmission code path without triggering Three's internal rendering
1566
- get _transmission() {
1567
- if (!this._transmissionValue) {
1568
- this._transmissionValue = uniform(1);
1569
- }
1570
- return this._transmissionValue.value;
1571
- }
1572
- set _transmission(v) {
1573
- if (!this._transmissionValue) {
1574
- this._transmissionValue = uniform(1);
1575
- }
1576
- this._transmissionValue.value = v;
1577
- }
1578
1465
  }
1579
1466
  const MeshTransmissionMaterial = /* @__PURE__ */ React.forwardRef(
1580
1467
  ({
@@ -1707,7 +1594,7 @@ const MeshTransmissionMaterial = /* @__PURE__ */ React.forwardRef(
1707
1594
  ref,
1708
1595
  ...props,
1709
1596
  buffer: buffer || fboMain.texture,
1710
- _transmission: transmission2,
1597
+ transmission: transmission2,
1711
1598
  anisotropicBlur: anisotropicBlur ?? anisotropy,
1712
1599
  thickness: thickness2,
1713
1600
  side,
@@ -1717,23 +1604,36 @@ const MeshTransmissionMaterial = /* @__PURE__ */ React.forwardRef(
1717
1604
  }
1718
1605
  );
1719
1606
 
1720
- let MeshReflectorMaterial$1 = class MeshReflectorMaterial extends THREE.MeshStandardNodeMaterial {
1607
+ let MeshReflectorMaterial$1 = class MeshReflectorMaterial extends withUniforms(THREE.MeshStandardNodeMaterial, {
1608
+ /** Mirror strength, 0 = base material only, 1 = pure reflection */
1609
+ mirror: () => uniform(0),
1610
+ /** Positive values enable the blurred reflection sample */
1611
+ mixBlur: () => uniform(0),
1612
+ /** Strength of the reflection added on top of the base colour */
1613
+ mixStrength: () => uniform(1),
1614
+ /** Lower depth threshold for depth-based blur */
1615
+ minDepthThreshold: () => uniform(0.9),
1616
+ /** Upper depth threshold for depth-based blur */
1617
+ maxDepthThreshold: () => uniform(1),
1618
+ /** Depth scale for depth-based blur, 0 = disabled */
1619
+ depthScale: () => uniform(0),
1620
+ /** Bias added to the depth-to-blur ratio */
1621
+ depthToBlurRatioBias: () => uniform(0.25),
1622
+ /** Distortion strength */
1623
+ distortion: () => uniform(1),
1624
+ /** Contrast applied to the (clamped) reflection colour */
1625
+ mixContrast: () => uniform(1),
1626
+ /** Blur radius in the 0-1 range, scaled to 0.01-0.15 in the shader */
1627
+ blurRadius: () => uniform(0.1)
1628
+ }) {
1721
1629
  //* Reflector Node ----------------------------------------
1722
1630
  reflection;
1723
- //* Uniforms ----------------------------------------
1724
- mirrorUniform;
1725
- mixBlurUniform;
1726
- mixStrengthUniform;
1727
- minDepthThresholdUniform;
1728
- maxDepthThresholdUniform;
1729
- depthScaleUniform;
1730
- depthToBlurRatioBiasUniform;
1731
- distortionUniform;
1732
- mixContrastUniform;
1733
- blurRadiusUniform;
1734
- // Feature flags
1735
- hasBlurUniform;
1736
- hasDepthUniform;
1631
+ /**
1632
+ * Add this target to the scene and align it with the reflective surface.
1633
+ */
1634
+ get reflectorTarget() {
1635
+ return this.reflection?.target;
1636
+ }
1737
1637
  constructor(options = {}, parameters = {}) {
1738
1638
  super();
1739
1639
  const { distortionMap, ...validParams } = parameters;
@@ -1757,109 +1657,37 @@ let MeshReflectorMaterial$1 = class MeshReflectorMaterial extends THREE.MeshStan
1757
1657
  resolutionScale,
1758
1658
  generateMipmaps: true
1759
1659
  });
1760
- this.mirrorUniform = uniform(mirror);
1761
- this.mixBlurUniform = uniform(mixBlur);
1762
- this.mixStrengthUniform = uniform(mixStrength);
1763
- this.minDepthThresholdUniform = uniform(minDepthThreshold);
1764
- this.maxDepthThresholdUniform = uniform(maxDepthThreshold);
1765
- this.depthScaleUniform = uniform(depthScale);
1766
- this.depthToBlurRatioBiasUniform = uniform(depthToBlurRatioBias);
1767
- this.distortionUniform = uniform(distortion);
1768
- this.mixContrastUniform = uniform(mixContrast);
1769
- this.blurRadiusUniform = uniform(0.1);
1770
- this.hasBlurUniform = uniform(mixBlur > 0 ? 1 : 0);
1771
- this.hasDepthUniform = uniform(depthScale > 0 ? 1 : 0);
1660
+ this.mirror = mirror;
1661
+ this.mixBlur = mixBlur;
1662
+ this.mixStrength = mixStrength;
1663
+ this.minDepthThreshold = minDepthThreshold;
1664
+ this.maxDepthThreshold = maxDepthThreshold;
1665
+ this.depthScale = depthScale;
1666
+ this.depthToBlurRatioBias = depthToBlurRatioBias;
1667
+ this.distortion = distortion;
1668
+ this.mixContrast = mixContrast;
1772
1669
  this.setupNodes();
1773
1670
  }
1774
1671
  setupNodes() {
1672
+ const { mirror, mixBlur, mixStrength, mixContrast, blurRadius } = this.uniforms;
1775
1673
  this.colorNode = Fn(() => {
1776
1674
  const reflectionSample = this.reflection.sample(this.reflection.uvNode);
1777
- const blurRadius = mix(float(0.01), float(0.15), this.blurRadiusUniform);
1778
- const reflectionBlurred = hashBlur(this.reflection, blurRadius, {
1675
+ const radius = mix(float(0.01), float(0.15), blurRadius);
1676
+ const reflectionBlurred = hashBlur(this.reflection, radius, {
1779
1677
  repeats: float(20),
1780
1678
  premultipliedAlpha: false
1781
1679
  });
1782
- const reflectionColor = mix(reflectionSample.rgb, reflectionBlurred.rgb, clamp(this.hasBlurUniform, 0, 1));
1680
+ const reflectionColor = select(mixBlur.greaterThan(0), reflectionBlurred.rgb, reflectionSample.rgb);
1783
1681
  const reflectionClamped = clamp(reflectionColor, 0, 1);
1784
1682
  const contrastAdjusted = vec3(
1785
- reflectionClamped.x.sub(0.5).mul(this.mixContrastUniform).add(0.5),
1786
- reflectionClamped.y.sub(0.5).mul(this.mixContrastUniform).add(0.5),
1787
- reflectionClamped.z.sub(0.5).mul(this.mixContrastUniform).add(0.5)
1683
+ reflectionClamped.x.sub(0.5).mul(mixContrast).add(0.5),
1684
+ reflectionClamped.y.sub(0.5).mul(mixContrast).add(0.5),
1685
+ reflectionClamped.z.sub(0.5).mul(mixContrast).add(0.5)
1788
1686
  );
1789
- const mirrorClamped = clamp(this.mirrorUniform, 0, 1);
1790
- return vec4(vec3(1).sub(vec3(mirrorClamped)).add(contrastAdjusted.mul(this.mixStrengthUniform)), 1);
1687
+ const mirrorClamped = clamp(mirror, 0, 1);
1688
+ return vec4(vec3(1).sub(vec3(mirrorClamped)).add(contrastAdjusted.mul(mixStrength)), 1);
1791
1689
  })();
1792
1690
  }
1793
- //* Getters and Setters ----------------------------------------
1794
- get mirror() {
1795
- return this.mirrorUniform.value;
1796
- }
1797
- set mirror(v) {
1798
- this.mirrorUniform.value = v;
1799
- }
1800
- get mixBlur() {
1801
- return this.mixBlurUniform.value;
1802
- }
1803
- set mixBlur(v) {
1804
- this.mixBlurUniform.value = v;
1805
- this.hasBlurUniform.value = v > 0 ? 1 : 0;
1806
- }
1807
- get mixStrength() {
1808
- return this.mixStrengthUniform.value;
1809
- }
1810
- set mixStrength(v) {
1811
- this.mixStrengthUniform.value = v;
1812
- }
1813
- get minDepthThreshold() {
1814
- return this.minDepthThresholdUniform.value;
1815
- }
1816
- set minDepthThreshold(v) {
1817
- this.minDepthThresholdUniform.value = v;
1818
- }
1819
- get maxDepthThreshold() {
1820
- return this.maxDepthThresholdUniform.value;
1821
- }
1822
- set maxDepthThreshold(v) {
1823
- this.maxDepthThresholdUniform.value = v;
1824
- }
1825
- get depthScale() {
1826
- return this.depthScaleUniform.value;
1827
- }
1828
- set depthScale(v) {
1829
- this.depthScaleUniform.value = v;
1830
- this.hasDepthUniform.value = v > 0 ? 1 : 0;
1831
- }
1832
- get depthToBlurRatioBias() {
1833
- return this.depthToBlurRatioBiasUniform.value;
1834
- }
1835
- set depthToBlurRatioBias(v) {
1836
- this.depthToBlurRatioBiasUniform.value = v;
1837
- }
1838
- get distortion() {
1839
- return this.distortionUniform.value;
1840
- }
1841
- set distortion(v) {
1842
- this.distortionUniform.value = v;
1843
- }
1844
- get mixContrast() {
1845
- return this.mixContrastUniform.value;
1846
- }
1847
- set mixContrast(v) {
1848
- this.mixContrastUniform.value = v;
1849
- }
1850
- get blurRadius() {
1851
- return this.blurRadiusUniform.value;
1852
- }
1853
- set blurRadius(v) {
1854
- this.blurRadiusUniform.value = v;
1855
- }
1856
- /**
1857
- * Get the reflector target mesh that needs to be added to the scene.
1858
- * The target should be positioned and rotated to match the reflective surface.
1859
- */
1860
- get reflectorTarget() {
1861
- return this.reflection.target;
1862
- }
1863
1691
  };
1864
1692
 
1865
1693
  const MeshReflectorMaterial = /* @__PURE__ */ React.forwardRef(
@@ -1981,138 +1809,64 @@ const approximateRefraction = /* @__PURE__ */ Fn(([rayDir, normal, iorValue, bou
1981
1809
  });
1982
1810
  return select(entryValid, currentDir, reflect(rayDir, normal));
1983
1811
  });
1984
- class MeshRefractionMaterialImpl extends MeshPhysicalNodeMaterial {
1985
- //* Private Uniform Nodes --
1986
- _envMap;
1987
- _bounces;
1988
- _ior;
1989
- _fresnel;
1990
- _aberrationStrength;
1991
- _color;
1992
- _opacityValue;
1812
+ class MeshRefractionMaterialImpl extends withUniforms(MeshPhysicalNodeMaterial, {
1813
+ /** Number of internal bounces, default: 3 */
1814
+ bounces: () => uniform(3),
1815
+ /** Fresnel intensity, default: 0 */
1816
+ fresnel: () => uniform(0),
1817
+ /** Chromatic aberration strength, default: 0.01 */
1818
+ aberrationStrength: () => uniform(0.01),
1819
+ /** Tint applied to the refracted environment, default: white */
1820
+ tintColor: () => uniform(new THREE.Color("white"))
1821
+ }) {
1993
1822
  _fastChroma;
1994
1823
  /** Type flag for identification */
1995
1824
  isMeshRefractionMaterial = true;
1996
1825
  constructor(fastChroma = true) {
1997
1826
  super();
1998
1827
  this._fastChroma = fastChroma;
1999
- this._envMap = uniformTexture(new THREE.Texture());
2000
- this._bounces = uniform(3);
2001
- this._ior = uniform(2.4);
2002
- this._fresnel = uniform(0);
2003
- this._aberrationStrength = uniform(0.01);
2004
- this._color = uniform(new THREE.Color("white"));
2005
- this._opacityValue = uniform(1);
1828
+ this.ior = 2.4;
1829
+ this.envMap = new THREE.Texture();
1830
+ this.lights = false;
2006
1831
  this.transparent = true;
2007
1832
  this.side = THREE.FrontSide;
2008
1833
  this._buildRefractionShader();
2009
1834
  }
2010
1835
  _buildRefractionShader() {
2011
- const envMapTex = this._envMap;
2012
- const bouncesUniform = this._bounces;
2013
- const iorUniform = this._ior;
2014
- const fresnelUniform = this._fresnel;
2015
- const aberrationUniform = this._aberrationStrength;
2016
- const colorUniform = this._color;
2017
- const opacityUniform = this._opacityValue;
1836
+ const { bounces, fresnel, aberrationStrength, tintColor } = this.uniforms;
2018
1837
  const fastChroma = this._fastChroma;
1838
+ const envSample = (uvNode) => context$a(materialReference("envMap", "texture", this), { getUV: () => uvNode });
2019
1839
  this.outputNode = Fn(() => {
2020
1840
  const worldPos = positionWorld;
2021
1841
  const worldNormal = normalize(normalWorld);
2022
1842
  const viewDir = normalize(worldPos.sub(cameraPosition));
2023
- const baseColor = colorUniform.toVar();
2024
- const refractedDirG = approximateRefraction(viewDir, worldNormal, max(iorUniform, 1), bouncesUniform);
2025
- const aberration = aberrationUniform;
1843
+ const baseColor = tintColor.toVar();
1844
+ const iorValue = max(materialIOR, 1);
1845
+ const refractedDirG = approximateRefraction(viewDir, worldNormal, iorValue, bounces);
1846
+ const aberration = aberrationStrength;
2026
1847
  const refractedDirR = vec3(0, 0, 0).toVar();
2027
1848
  const refractedDirB = vec3(0, 0, 0).toVar();
2028
- const useFastChroma = fastChroma;
2029
- if (useFastChroma) {
1849
+ if (fastChroma) {
2030
1850
  refractedDirR.assign(normalize(refractedDirG.add(vec3(aberration.mul(0.5)))));
2031
1851
  refractedDirB.assign(normalize(refractedDirG.sub(vec3(aberration.mul(0.5)))));
2032
1852
  } else {
2033
1853
  refractedDirR.assign(
2034
- approximateRefraction(
2035
- viewDir,
2036
- worldNormal,
2037
- max(iorUniform.mul(float(1).sub(aberration)), 1),
2038
- bouncesUniform
2039
- )
1854
+ approximateRefraction(viewDir, worldNormal, max(materialIOR.mul(float(1).sub(aberration)), 1), bounces)
2040
1855
  );
2041
1856
  refractedDirB.assign(
2042
- approximateRefraction(
2043
- viewDir,
2044
- worldNormal,
2045
- max(iorUniform.mul(float(1).add(aberration)), 1),
2046
- bouncesUniform
2047
- )
1857
+ approximateRefraction(viewDir, worldNormal, max(materialIOR.mul(float(1).add(aberration)), 1), bounces)
2048
1858
  );
2049
1859
  }
2050
- const uvR = equirectUV(refractedDirR);
2051
- const uvG = equirectUV(refractedDirG);
2052
- const uvB = equirectUV(refractedDirB);
2053
- const envColorR = texture(envMapTex, uvR).r;
2054
- const envColorG = texture(envMapTex, uvG).g;
2055
- const envColorB = texture(envMapTex, uvB).b;
1860
+ const envColorR = envSample(equirectUV(refractedDirR)).r;
1861
+ const envColorG = envSample(equirectUV(refractedDirG)).g;
1862
+ const envColorB = envSample(equirectUV(refractedDirB)).b;
2056
1863
  const envColor = vec3(envColorR, envColorG, envColorB);
2057
1864
  const tintedColor = envColor.mul(baseColor);
2058
- const fresnelValue = fresnelEffect(viewDir, worldNormal, float(10)).mul(fresnelUniform);
1865
+ const fresnelValue = fresnelEffect(viewDir, worldNormal, float(10)).mul(fresnel);
2059
1866
  const finalColor = mix(tintedColor, vec3(1), fresnelValue);
2060
- return vec4(finalColor, opacityUniform);
1867
+ return vec4(finalColor, materialOpacity);
2061
1868
  })();
2062
1869
  }
2063
- //* Uniform Accessors ==============================
2064
- /** Environment map texture */
2065
- get envMap() {
2066
- return this._envMap.value;
2067
- }
2068
- set envMap(v) {
2069
- if (v) {
2070
- this._envMap.value = v;
2071
- }
2072
- }
2073
- /** Number of internal bounces, default: 3 */
2074
- get bounces() {
2075
- return this._bounces.value;
2076
- }
2077
- set bounces(v) {
2078
- this._bounces.value = v;
2079
- }
2080
- /** Index of refraction, default: 2.4 (diamond) */
2081
- get ior() {
2082
- return this._ior.value;
2083
- }
2084
- set ior(v) {
2085
- this._ior.value = v;
2086
- }
2087
- /** Fresnel intensity, default: 0 */
2088
- get fresnel() {
2089
- return this._fresnel.value;
2090
- }
2091
- set fresnel(v) {
2092
- this._fresnel.value = v;
2093
- }
2094
- /** Chromatic aberration strength, default: 0.01 */
2095
- get aberrationStrength() {
2096
- return this._aberrationStrength.value;
2097
- }
2098
- set aberrationStrength(v) {
2099
- this._aberrationStrength.value = v;
2100
- }
2101
- /** Tint color - use tintColor to avoid base class conflict */
2102
- get tintColor() {
2103
- return this._color.value;
2104
- }
2105
- set tintColor(v) {
2106
- if (v instanceof THREE.Color) this._color.value = v;
2107
- else this._color.value = new THREE.Color(v);
2108
- }
2109
- /** Opacity, default: 1 */
2110
- get opacity() {
2111
- return this._opacityValue.value;
2112
- }
2113
- set opacity(v) {
2114
- this._opacityValue.value = v;
2115
- }
2116
1870
  }
2117
1871
  const MeshRefractionMaterial = /* @__PURE__ */ React.forwardRef(
2118
1872
  ({
@@ -2335,175 +2089,88 @@ function Container$2({
2335
2089
  return /* @__PURE__ */ React.createElement(React.Fragment, null, children);
2336
2090
  }
2337
2091
 
2338
- class PortalMaterialImpl extends MeshBasicNodeMaterial {
2339
- _blur;
2340
- _map;
2341
- _sdf;
2342
- _blend;
2343
- _size;
2344
- _resolution;
2092
+ class PortalMaterialImpl extends withUniforms(MeshBasicNodeMaterial, {
2093
+ /** Edge fade blur, 0 = no blur */
2094
+ blur: () => uniform(0),
2095
+ /** Signed distance field of the parent geometry, generated on demand */
2096
+ sdf: () => uniformTexture(new THREE.Texture()),
2097
+ /** 0 = world scene, 0.5 = both, 1 = portal scene */
2098
+ blend: () => uniform(0),
2099
+ /** Largest SDF distance, used to normalise the field */
2100
+ size: () => uniform(0),
2101
+ /** Drawing-buffer size in pixels */
2102
+ resolution: () => uniform(new THREE.Vector2())
2103
+ }) {
2345
2104
  constructor() {
2346
2105
  super();
2347
- this._blur = uniform(0);
2348
- this._map = uniformTexture(new THREE.Texture());
2349
- this._sdf = uniformTexture(new THREE.Texture());
2350
- this._blend = uniform(0);
2351
- this._size = uniform(0);
2352
- this._resolution = uniform(new THREE.Vector2());
2106
+ this.map = new THREE.Texture();
2353
2107
  this._buildShader();
2354
2108
  }
2355
2109
  _buildShader() {
2356
- const blurUniform = this._blur;
2357
- const mapTex = this._map;
2358
- const sdfTex = this._sdf;
2359
- const sizeUniform = this._size;
2360
- const resolutionUniform = this._resolution;
2110
+ const { blur, sdf, size, resolution } = this.uniforms;
2111
+ const map = materialReference("map", "texture", this);
2361
2112
  this.colorNode = Fn(() => {
2362
- const screenUv = screenCoordinate.xy.div(resolutionUniform);
2363
- const t = texture(mapTex, screenUv);
2364
- const meshUv = uv();
2365
- const d = texture(sdfTex, meshUv).r.div(sizeUniform);
2366
- const k = blurUniform;
2367
- const edgeFactor = clamp(d.div(k).add(1), 0, 1);
2113
+ const screenUv = screenCoordinate.xy.div(resolution);
2114
+ const t = context$a(map, { getUV: () => screenUv });
2115
+ const d = sdf.sample(uv()).r.div(size);
2116
+ const edgeFactor = clamp(d.div(blur).add(1), 0, 1);
2368
2117
  const alpha = float(1).sub(smoothstep(float(0), float(1), edgeFactor));
2369
- const isBlurZero = k.equal(0);
2370
- const finalAlpha = select(isBlurZero, t.a, t.a.mul(alpha));
2118
+ const finalAlpha = select(blur.equal(0), t.a, t.a.mul(alpha));
2371
2119
  return vec4(t.rgb, finalAlpha);
2372
2120
  })();
2373
2121
  }
2374
- //* Uniform Accessors --
2375
- get blur() {
2376
- return this._blur.value;
2377
- }
2378
- set blur(v) {
2379
- this._blur.value = v;
2380
- }
2381
- get map() {
2382
- return this._map?.value;
2383
- }
2384
- set map(v) {
2385
- if (this._map) this._map.value = v ?? new THREE.Texture();
2386
- }
2387
- get sdf() {
2388
- return this._sdf.value;
2389
- }
2390
- set sdf(v) {
2391
- if (v) this._sdf.value = v;
2392
- }
2393
- get blend() {
2394
- return this._blend.value;
2395
- }
2396
- set blend(v) {
2397
- this._blend.value = v;
2398
- }
2399
- get size() {
2400
- return this._size.value;
2401
- }
2402
- set size(v) {
2403
- this._size.value = v;
2404
- }
2405
- get resolution() {
2406
- return this._resolution.value;
2407
- }
2408
- set resolution(v) {
2409
- if (Array.isArray(v)) this._resolution.value.set(v[0], v[1]);
2410
- else this._resolution.value.copy(v);
2411
- }
2412
2122
  }
2413
- class BlendMaterial extends MeshBasicNodeMaterial {
2414
- _textureA;
2415
- _textureB;
2416
- _blend;
2123
+ class BlendMaterial extends withUniforms(MeshBasicNodeMaterial, {
2124
+ textureA: () => uniformTexture(new THREE.Texture()),
2125
+ textureB: () => uniformTexture(new THREE.Texture()),
2126
+ blend: () => uniform(0)
2127
+ }) {
2417
2128
  constructor() {
2418
2129
  super();
2419
- this._textureA = uniformTexture(new THREE.Texture());
2420
- this._textureB = uniformTexture(new THREE.Texture());
2421
- this._blend = uniform(0);
2422
- this._buildShader();
2423
- }
2424
- _buildShader() {
2425
- const texA = this._textureA;
2426
- const texB = this._textureB;
2427
- const blendUniform = this._blend;
2130
+ const { textureA, textureB, blend } = this.uniforms;
2428
2131
  this.colorNode = Fn(() => {
2429
2132
  const uvCoord = uv();
2430
- const ta = texture(texA, uvCoord);
2431
- const tb = texture(texB, uvCoord);
2432
- return mix(tb, ta, blendUniform);
2133
+ return mix(textureB.sample(uvCoord), textureA.sample(uvCoord), blend);
2433
2134
  })();
2434
2135
  }
2435
- get textureA() {
2436
- return this._textureA.value;
2437
- }
2438
- set textureA(v) {
2439
- this._textureA.value = v;
2440
- }
2441
- get textureB() {
2442
- return this._textureB.value;
2443
- }
2444
- set textureB(v) {
2445
- this._textureB.value = v;
2446
- }
2447
- get blend() {
2448
- return this._blend.value;
2449
- }
2450
- set blend(v) {
2451
- this._blend.value = v;
2452
- }
2453
2136
  }
2454
- class UVRenderMaterial extends MeshBasicNodeMaterial {
2455
- _tex;
2456
- _inside;
2137
+ class UVRenderMaterial extends withUniforms(MeshBasicNodeMaterial, {
2138
+ tex: () => uniformTexture(new THREE.Texture()),
2139
+ /** 1 = pack the inside of the mask, 0 = the outside */
2140
+ inside: () => uniform(0)
2141
+ }) {
2457
2142
  constructor(inside = false) {
2458
- super();
2459
- this._tex = uniformTexture(new THREE.Texture());
2460
- this._inside = uniform(inside ? 1 : 0);
2461
- this._buildShader();
2462
- }
2463
- _buildShader() {
2464
- const tex = this._tex;
2465
- const insideUniform = this._inside;
2466
- this.colorNode = Fn(() => {
2143
+ super({ toneMapped: false });
2144
+ this.inside = inside ? 1 : 0;
2145
+ const { tex, inside: insideUniform } = this.uniforms;
2146
+ this.fragmentNode = Fn(() => {
2467
2147
  const uvCoord = uv();
2468
- const mask = texture(tex, uvCoord).x;
2469
- const roundedMask = round(mask);
2148
+ const roundedMask = round(tex.sample(uvCoord).x);
2470
2149
  const isInside = insideUniform.greaterThan(0.5);
2471
2150
  const factor = select(isInside, float(1).sub(roundedMask), roundedMask);
2472
2151
  const packedUv = uvCoord.mul(factor);
2473
2152
  return vec4(packedUv.x, packedUv.y, 0, 1);
2474
2153
  })();
2475
2154
  }
2476
- get tex() {
2477
- return this._tex.value;
2478
- }
2479
- set tex(v) {
2480
- this._tex.value = v;
2481
- }
2482
2155
  }
2483
- class JumpFloodMaterial extends MeshBasicNodeMaterial {
2484
- _tex;
2485
- _offset;
2486
- _texelSize;
2156
+ class JumpFloodMaterial extends withUniforms(MeshBasicNodeMaterial, {
2157
+ tex: () => uniformTexture(new THREE.Texture()),
2158
+ offset: () => uniform(0),
2159
+ texelSize: () => uniform(new THREE.Vector2(1, 1))
2160
+ }) {
2487
2161
  constructor(clientWidth, clientHeight) {
2488
- super();
2489
- this._tex = uniformTexture(new THREE.Texture());
2490
- this._offset = uniform(0);
2491
- this._texelSize = uniform(new THREE.Vector2(1 / clientWidth, 1 / clientHeight));
2492
- this._buildShader();
2493
- }
2494
- _buildShader() {
2495
- const tex = this._tex;
2496
- const offsetUniform = this._offset;
2497
- const texelSizeUniform = this._texelSize;
2498
- this.colorNode = Fn(() => {
2162
+ super({ toneMapped: false });
2163
+ this.uniforms.texelSize.value.set(1 / clientWidth, 1 / clientHeight);
2164
+ const { tex, offset, texelSize } = this.uniforms;
2165
+ this.fragmentNode = Fn(() => {
2499
2166
  const uvCoord = uv();
2500
2167
  const closestDist = float(99999999e-1).toVar();
2501
2168
  const closestPos = vec2$2(0, 0).toVar();
2502
2169
  Loop({ start: int(-1), end: int(2), type: "int", condition: "<" }, ({ i: x }) => {
2503
2170
  Loop({ start: int(-1), end: int(2), type: "int", condition: "<" }, ({ i: y }) => {
2504
- const sampleOffset = vec2$2(float(x), float(y)).mul(texelSizeUniform).mul(offsetUniform);
2171
+ const sampleOffset = vec2$2(float(x), float(y)).mul(texelSize).mul(offset);
2505
2172
  const sampleUv = uvCoord.add(sampleOffset);
2506
- const pos = texture(tex, sampleUv).xy;
2173
+ const pos = tex.sample(sampleUv).xy;
2507
2174
  const dist = distance(pos, uvCoord);
2508
2175
  const isValid = pos.x.notEqual(0).and(pos.y.notEqual(0));
2509
2176
  const isCloser = dist.lessThan(closestDist);
@@ -2515,88 +2182,40 @@ class JumpFloodMaterial extends MeshBasicNodeMaterial {
2515
2182
  return vec4(closestPos, 0, 1);
2516
2183
  })();
2517
2184
  }
2518
- get tex() {
2519
- return this._tex.value;
2520
- }
2521
- set tex(v) {
2522
- this._tex.value = v;
2523
- }
2524
- get offset() {
2525
- return this._offset.value;
2526
- }
2527
- set offset(v) {
2528
- this._offset.value = v;
2529
- }
2530
2185
  }
2531
- class DistanceFieldMaterial extends MeshBasicNodeMaterial {
2532
- _tex;
2533
- _size;
2186
+ class DistanceFieldMaterial extends withUniforms(MeshBasicNodeMaterial, {
2187
+ tex: () => uniformTexture(new THREE.Texture()),
2188
+ size: () => uniform(new THREE.Vector2(1, 1))
2189
+ }) {
2534
2190
  constructor(clientWidth, clientHeight) {
2535
- super();
2536
- this._tex = uniformTexture(new THREE.Texture());
2537
- this._size = uniform(new THREE.Vector2(clientWidth, clientHeight));
2538
- this._buildShader();
2539
- }
2540
- _buildShader() {
2541
- const tex = this._tex;
2542
- const sizeUniform = this._size;
2543
- this.colorNode = Fn(() => {
2191
+ super({ toneMapped: false });
2192
+ this.uniforms.size.value.set(clientWidth, clientHeight);
2193
+ const { tex, size } = this.uniforms;
2194
+ this.fragmentNode = Fn(() => {
2544
2195
  const uvCoord = uv();
2545
- const pos = texture(tex, uvCoord).xy;
2546
- const dist = distance(sizeUniform.mul(pos), sizeUniform.mul(uvCoord));
2196
+ const pos = tex.sample(uvCoord).xy;
2197
+ const dist = distance(size.mul(pos), size.mul(uvCoord));
2547
2198
  return vec4(dist, 0, 0, 1);
2548
2199
  })();
2549
2200
  }
2550
- get tex() {
2551
- return this._tex.value;
2552
- }
2553
- set tex(v) {
2554
- this._tex.value = v;
2555
- }
2556
2201
  }
2557
- class CompositeMaterial extends MeshBasicNodeMaterial {
2558
- _inside;
2559
- _outside;
2560
- _mask;
2202
+ class CompositeMaterial extends withUniforms(MeshBasicNodeMaterial, {
2203
+ inside: () => uniformTexture(new THREE.Texture()),
2204
+ outside: () => uniformTexture(new THREE.Texture()),
2205
+ mask: () => uniformTexture(new THREE.Texture())
2206
+ }) {
2561
2207
  constructor() {
2562
- super();
2563
- this._inside = uniformTexture(new THREE.Texture());
2564
- this._outside = uniformTexture(new THREE.Texture());
2565
- this._mask = uniformTexture(new THREE.Texture());
2566
- this._buildShader();
2567
- }
2568
- _buildShader() {
2569
- const insideTex = this._inside;
2570
- const outsideTex = this._outside;
2571
- const maskTex = this._mask;
2572
- this.colorNode = Fn(() => {
2208
+ super({ toneMapped: false });
2209
+ const { inside, outside, mask } = this.uniforms;
2210
+ this.fragmentNode = Fn(() => {
2573
2211
  const uvCoord = uv();
2574
- const i = texture(insideTex, uvCoord).x;
2575
- const o = texture(outsideTex, uvCoord).x;
2576
- const maskVal = texture(maskTex, uvCoord).x;
2577
- const isOutside = maskVal.equal(0);
2578
- const result = select(isOutside, o, i.negate());
2212
+ const i = inside.sample(uvCoord).x;
2213
+ const o = outside.sample(uvCoord).x;
2214
+ const maskVal = mask.sample(uvCoord).x;
2215
+ const result = select(maskVal.equal(0), o, i.negate());
2579
2216
  return vec4(result, 0, 0, 1);
2580
2217
  })();
2581
2218
  }
2582
- get inside() {
2583
- return this._inside.value;
2584
- }
2585
- set inside(v) {
2586
- this._inside.value = v;
2587
- }
2588
- get outside() {
2589
- return this._outside.value;
2590
- }
2591
- set outside(v) {
2592
- this._outside.value = v;
2593
- }
2594
- get mask() {
2595
- return this._mask.value;
2596
- }
2597
- set mask(v) {
2598
- this._mask.value = v;
2599
- }
2600
2219
  }
2601
2220
  const makeSDFGenerator = (clientWidth, clientHeight, renderer) => {
2602
2221
  const finalTarget = new RenderTarget(clientWidth, clientHeight, {
@@ -2644,7 +2263,7 @@ const makeSDFGenerator = (clientWidth, clientHeight, renderer) => {
2644
2263
  const jumpFloodQuad = new QuadMesh(jumpFloodMat);
2645
2264
  const distanceFieldQuad = new QuadMesh(distanceFieldMat);
2646
2265
  const compositeQuad = new QuadMesh(compositeMat);
2647
- return (image) => {
2266
+ const generate = (image) => {
2648
2267
  image.minFilter = THREE.NearestFilter;
2649
2268
  image.magFilter = THREE.NearestFilter;
2650
2269
  uvRenderMat.tex = image;
@@ -2686,9 +2305,28 @@ const makeSDFGenerator = (clientWidth, clientHeight, renderer) => {
2686
2305
  compositeMat.outside = outsideRenderTargetFinal.texture;
2687
2306
  compositeMat.mask = image;
2688
2307
  compositeQuad.render(renderer);
2689
- renderer.setRenderTarget(null);
2690
2308
  return finalTarget;
2691
2309
  };
2310
+ return {
2311
+ generate,
2312
+ dispose() {
2313
+ for (const resource of [
2314
+ finalTarget,
2315
+ outsideRenderTarget,
2316
+ insideRenderTarget,
2317
+ outsideRenderTarget2,
2318
+ insideRenderTarget2,
2319
+ outsideRenderTargetFinal,
2320
+ insideRenderTargetFinal,
2321
+ uvRenderMat,
2322
+ uvRenderInsideMat,
2323
+ jumpFloodMat,
2324
+ distanceFieldMat,
2325
+ compositeMat
2326
+ ])
2327
+ resource.dispose();
2328
+ }
2329
+ };
2692
2330
  };
2693
2331
  const MeshPortalMaterial = /* @__PURE__ */ React.forwardRef(
2694
2332
  ({
@@ -2703,7 +2341,7 @@ const MeshPortalMaterial = /* @__PURE__ */ React.forwardRef(
2703
2341
  }, fref) => {
2704
2342
  extend({ PortalMaterialImpl });
2705
2343
  const ref = React.useRef(null);
2706
- const { scene, gl, size, viewport, setEvents } = useThree();
2344
+ const { scene, renderer, size, viewport, setEvents, invalidate } = useThree();
2707
2345
  const maskRenderTarget = useFBO(resolution, resolution);
2708
2346
  const [priority, setPriority] = React.useState(0);
2709
2347
  useFrame(() => {
@@ -2714,43 +2352,69 @@ const MeshPortalMaterial = /* @__PURE__ */ React.forwardRef(
2714
2352
  if (events !== void 0) setEvents({ enabled: !events });
2715
2353
  }, [events]);
2716
2354
  const [visible, setVisible] = React.useState(true);
2355
+ const needsSdf = blur !== 0;
2717
2356
  const parent = useIntersect(setVisible);
2718
2357
  React.useLayoutEffect(() => {
2719
2358
  parent.current = ref.current?.__r3f?.parent?.object;
2720
2359
  }, []);
2721
2360
  React.useLayoutEffect(() => {
2722
- if (!parent.current) return;
2723
- if (blur && ref.current.sdf === null) {
2724
- const tempMesh = new THREE.Mesh(parent.current.geometry, new THREE.MeshBasicMaterial());
2725
- const boundingBox = new THREE.Box3().setFromBufferAttribute(
2726
- tempMesh.geometry.attributes.position
2727
- );
2728
- const orthoCam = new THREE.OrthographicCamera(
2729
- boundingBox.min.x * (1 + 2 / resolution),
2730
- boundingBox.max.x * (1 + 2 / resolution),
2731
- boundingBox.max.y * (1 + 2 / resolution),
2732
- boundingBox.min.y * (1 + 2 / resolution),
2733
- 0.1,
2734
- 1e3
2735
- );
2736
- orthoCam.position.set(0, 0, 1);
2737
- orthoCam.lookAt(0, 0, 0);
2738
- gl.setRenderTarget(maskRenderTarget);
2739
- gl.render(tempMesh, orthoCam);
2740
- const sg = makeSDFGenerator(resolution, resolution, gl);
2741
- const sdf = sg(maskRenderTarget.texture);
2742
- const readSdf = new Float32Array(resolution * resolution);
2743
- gl.readRenderTargetPixels(sdf, 0, 0, resolution, resolution, readSdf);
2361
+ if (!parent.current || !needsSdf) return;
2362
+ const material = ref.current;
2363
+ const previousSdf = material.sdf;
2364
+ const previousSize = material.size;
2365
+ const gpuRenderer = renderer;
2366
+ const tempMesh = new THREE.Mesh(parent.current.geometry, new THREE.MeshBasicMaterial());
2367
+ const boundingBox = new THREE.Box3().setFromBufferAttribute(
2368
+ tempMesh.geometry.attributes.position
2369
+ );
2370
+ const orthoCam = new THREE.OrthographicCamera(
2371
+ boundingBox.min.x * (1 + 2 / resolution),
2372
+ boundingBox.max.x * (1 + 2 / resolution),
2373
+ boundingBox.max.y * (1 + 2 / resolution),
2374
+ boundingBox.min.y * (1 + 2 / resolution),
2375
+ 0.1,
2376
+ 1e3
2377
+ );
2378
+ orthoCam.position.set(0, 0, 1);
2379
+ orthoCam.lookAt(0, 0, 0);
2380
+ const sg = makeSDFGenerator(resolution, resolution, gpuRenderer);
2381
+ const previousTarget = gpuRenderer.getRenderTarget();
2382
+ const previousClearColor = gpuRenderer.getClearColor(new THREE.Color());
2383
+ const previousClearAlpha = gpuRenderer.getClearAlpha();
2384
+ let sdf;
2385
+ try {
2386
+ gpuRenderer.setClearColor(0, 0);
2387
+ gpuRenderer.setRenderTarget(maskRenderTarget);
2388
+ gpuRenderer.render(tempMesh, orthoCam);
2389
+ sdf = sg.generate(maskRenderTarget.texture);
2390
+ } catch (error) {
2391
+ sg.dispose();
2392
+ throw error;
2393
+ } finally {
2394
+ gpuRenderer.setRenderTarget(previousTarget);
2395
+ gpuRenderer.setClearColor(previousClearColor, previousClearAlpha);
2396
+ tempMesh.material.dispose();
2397
+ }
2398
+ let cancelled = false;
2399
+ gpuRenderer.readRenderTargetPixelsAsync(sdf, 0, 0, resolution, resolution).then((readSdf) => {
2400
+ if (cancelled) return;
2744
2401
  let min = Infinity;
2745
2402
  for (let i = 0; i < readSdf.length; i++) {
2746
2403
  if (readSdf[i] < min) min = readSdf[i];
2747
2404
  }
2748
- min = -min;
2749
- ref.current.size = min;
2750
- ref.current.sdf = sdf.texture;
2751
- gl.setRenderTarget(null);
2752
- }
2753
- }, [resolution, blur]);
2405
+ material.size = -min;
2406
+ material.sdf = sdf.texture;
2407
+ invalidate();
2408
+ }).catch((error) => {
2409
+ if (!cancelled) console.error("MeshPortalMaterial: SDF readback failed.", error);
2410
+ });
2411
+ return () => {
2412
+ cancelled = true;
2413
+ material.sdf = previousSdf;
2414
+ material.size = previousSize;
2415
+ sg.dispose();
2416
+ };
2417
+ }, [resolution, needsSdf, renderer, maskRenderTarget, invalidate]);
2754
2418
  React.useImperativeHandle(fref, () => ref.current);
2755
2419
  const compute = React.useCallback(
2756
2420
  (event, state, _previous) => {
@@ -2855,44 +2519,27 @@ function ManagePortalScene({
2855
2519
  return /* @__PURE__ */ React.createElement(React.Fragment, null);
2856
2520
  }
2857
2521
 
2858
- class DistortMaterialImplWebGPU extends THREE.MeshPhysicalNodeMaterial {
2859
- timeUniform;
2860
- distortUniform;
2861
- radiusUniform;
2522
+ class DistortMaterialImplWebGPU extends withUniforms(THREE.MeshPhysicalNodeMaterial, {
2523
+ /** Animation time, advanced by the component each frame */
2524
+ time: () => uniform(0),
2525
+ /** Distortion intensity */
2526
+ distort: () => uniform(0.4),
2527
+ /** Base scale the distortion is added to */
2528
+ radius: () => uniform(1)
2529
+ }) {
2862
2530
  constructor(parameters = {}) {
2863
2531
  super(parameters);
2864
- this.setValues(parameters);
2865
- this.timeUniform = uniform(0);
2866
- this.distortUniform = uniform(0.4);
2867
- this.radiusUniform = uniform(1);
2532
+ const { time, distort, radius } = this.uniforms;
2868
2533
  this.positionNode = Fn(() => {
2869
2534
  const pos = positionLocal.toVar();
2870
- const updateTime = this.timeUniform.div(50);
2535
+ const updateTime = time.div(50);
2871
2536
  const noiseInput = pos.div(2).add(updateTime.mul(5));
2872
2537
  const noiseVec = mx_noise_vec3(noiseInput);
2873
2538
  const noise = noiseVec.x;
2874
- const distortFactor = noise.mul(pow(this.distortUniform, float(2))).add(this.radiusUniform);
2539
+ const distortFactor = noise.mul(pow(distort, float(2))).add(radius);
2875
2540
  return pos.mul(distortFactor);
2876
2541
  })();
2877
2542
  }
2878
- get time() {
2879
- return this.timeUniform.value;
2880
- }
2881
- set time(v) {
2882
- this.timeUniform.value = v;
2883
- }
2884
- get distort() {
2885
- return this.distortUniform.value;
2886
- }
2887
- set distort(v) {
2888
- this.distortUniform.value = v;
2889
- }
2890
- get radius() {
2891
- return this.radiusUniform.value;
2892
- }
2893
- set radius(v) {
2894
- this.radiusUniform.value = v;
2895
- }
2896
2543
  }
2897
2544
  const MeshDistortMaterial = /* @__PURE__ */ React.forwardRef(({ speed = 1, ...props }, ref) => {
2898
2545
  const [material] = React.useState(() => new DistortMaterialImplWebGPU());
@@ -2900,17 +2547,18 @@ const MeshDistortMaterial = /* @__PURE__ */ React.forwardRef(({ speed = 1, ...pr
2900
2547
  return /* @__PURE__ */ React.createElement("primitive", { object: material, ref, attach: "material", ...props });
2901
2548
  });
2902
2549
 
2903
- class WobbleMaterialImpl extends THREE.MeshStandardNodeMaterial {
2904
- timeUniform;
2905
- factorUniform;
2550
+ class WobbleMaterialImpl extends withUniforms(THREE.MeshStandardNodeMaterial, {
2551
+ /** Animation time, advanced by the component each frame */
2552
+ time: () => uniform(0),
2553
+ /** Wobble strength */
2554
+ factor: () => uniform(1)
2555
+ }) {
2906
2556
  constructor(parameters = {}) {
2907
2557
  super(parameters);
2908
- this.setValues(parameters);
2909
- this.timeUniform = uniform(0);
2910
- this.factorUniform = uniform(1);
2558
+ const { time, factor } = this.uniforms;
2911
2559
  this.positionNode = Fn(() => {
2912
2560
  const pos = positionLocal.toVar();
2913
- const theta = sin(this.timeUniform.add(pos.y)).div(2).mul(this.factorUniform);
2561
+ const theta = sin(time.add(pos.y)).div(2).mul(factor);
2914
2562
  const c = cos(theta);
2915
2563
  const s = sin(theta);
2916
2564
  const rotMatrix = mat3(c, float(0), s, float(0), float(1), float(0), s.negate(), float(0), c);
@@ -2919,25 +2567,13 @@ class WobbleMaterialImpl extends THREE.MeshStandardNodeMaterial {
2919
2567
  this.normalNode = Fn(() => {
2920
2568
  const pos = positionLocal.toVar();
2921
2569
  const norm = normalLocal.toVar();
2922
- const theta = sin(this.timeUniform.add(pos.y)).div(2).mul(this.factorUniform);
2570
+ const theta = sin(time.add(pos.y)).div(2).mul(factor);
2923
2571
  const c = cos(theta);
2924
2572
  const s = sin(theta);
2925
2573
  const rotMatrix = mat3(c, float(0), s, float(0), float(1), float(0), s.negate(), float(0), c);
2926
2574
  return norm.mul(rotMatrix);
2927
2575
  })();
2928
2576
  }
2929
- get time() {
2930
- return this.timeUniform.value;
2931
- }
2932
- set time(v) {
2933
- this.timeUniform.value = v;
2934
- }
2935
- get factor() {
2936
- return this.factorUniform.value;
2937
- }
2938
- set factor(v) {
2939
- this.factorUniform.value = v;
2940
- }
2941
2577
  }
2942
2578
  const MeshWobbleMaterial = /* @__PURE__ */ React.forwardRef(({ speed = 1, ...props }, ref) => {
2943
2579
  const [material] = React.useState(() => new WobbleMaterialImpl());
@@ -2956,84 +2592,36 @@ function FakeCloudMaterial(BaseMaterial) {
2956
2592
  return mat;
2957
2593
  }
2958
2594
 
2959
- class OutlinesMaterial extends MeshBasicNodeMaterial {
2960
- //* Private Uniform Nodes --
2961
- _screenspace;
2962
- // Using number as bool (0/1)
2963
- _color;
2964
- _opacityValue;
2965
- _thickness;
2966
- _size;
2595
+ class OutlinesMaterial extends withUniforms(MeshBasicNodeMaterial, {
2596
+ /** Whether thickness is constant in screen pixels */
2597
+ screenspace: () => uniform(false),
2598
+ /** Outline thickness */
2599
+ thickness: () => uniform(0.05),
2600
+ /** Drawing-buffer size, for screenspace thickness */
2601
+ size: () => uniform(new THREE.Vector2(1, 1))
2602
+ }) {
2967
2603
  /** Type flag for identification */
2968
2604
  isOutlinesMaterial = true;
2969
2605
  constructor() {
2970
2606
  super();
2971
- this._screenspace = uniform(0);
2972
- this._color = uniform(new THREE.Color("black"));
2973
- this._opacityValue = uniform(1);
2974
- this._thickness = uniform(0.05);
2975
- this._size = uniform(new THREE.Vector2(1, 1));
2607
+ this.color.set("black");
2976
2608
  this.side = THREE.BackSide;
2977
2609
  this.transparent = false;
2978
2610
  this._buildOutlineShader();
2979
2611
  }
2980
2612
  _buildOutlineShader() {
2981
- const screenspaceUniform = this._screenspace;
2982
- const colorUniform = this._color;
2983
- const opacityUniform = this._opacityValue;
2984
- const thicknessUniform = this._thickness;
2985
- const sizeUniform = this._size;
2613
+ const { screenspace, thickness, size } = this.uniforms;
2986
2614
  this.positionNode = Fn(() => {
2987
2615
  const position = positionLocal.toVar();
2988
2616
  const normal = normalLocal.toVar();
2989
- const isScreenspace = screenspaceUniform.greaterThan(0.5);
2990
- const screenspacePos = position.add(normal.mul(thicknessUniform));
2617
+ const screenspacePos = position.add(normal.mul(thickness));
2991
2618
  const clipPos = cameraProjectionMatrix.mul(modelViewMatrix.mul(vec4(position, 1)));
2992
2619
  const clipNormal = cameraProjectionMatrix.mul(modelViewMatrix.mul(vec4(normal, 0)));
2993
- normalize(clipNormal.xy).mul(thicknessUniform).div(sizeUniform).mul(clipPos.w).mul(2);
2994
- const worldOffset = normal.mul(thicknessUniform);
2995
- return select(isScreenspace, screenspacePos, position.add(worldOffset));
2996
- })();
2997
- this.outputNode = Fn(() => {
2998
- return vec4(colorUniform, opacityUniform);
2620
+ normalize(clipNormal.xy).mul(thickness).div(size).mul(clipPos.w).mul(2);
2621
+ const worldOffset = normal.mul(thickness);
2622
+ return select(screenspace, screenspacePos, position.add(worldOffset));
2999
2623
  })();
3000
- }
3001
- //* Uniform Accessors ==============================
3002
- /** Whether to use screenspace thickness */
3003
- get screenspace() {
3004
- return this._screenspace.value > 0.5;
3005
- }
3006
- set screenspace(v) {
3007
- this._screenspace.value = v ? 1 : 0;
3008
- }
3009
- /** Outline color */
3010
- get color() {
3011
- return this._color.value;
3012
- }
3013
- set color(v) {
3014
- if (v instanceof THREE.Color) this._color.value = v;
3015
- else this._color.value = new THREE.Color(v);
3016
- }
3017
- /** Outline opacity */
3018
- get opacity() {
3019
- return this._opacityValue.value;
3020
- }
3021
- set opacity(v) {
3022
- this._opacityValue.value = v;
3023
- }
3024
- /** Outline thickness */
3025
- get thickness() {
3026
- return this._thickness.value;
3027
- }
3028
- set thickness(v) {
3029
- this._thickness.value = v;
3030
- }
3031
- /** Screen size for screenspace calculations */
3032
- get size() {
3033
- return this._size.value;
3034
- }
3035
- set size(v) {
3036
- this._size.value = v;
2624
+ this.outputNode = Fn(() => vec4(materialColor, materialOpacity))();
3037
2625
  }
3038
2626
  }
3039
2627
  function Outlines({
@@ -4743,7 +4331,7 @@ const ScrollHtml = React.forwardRef(
4743
4331
  const group = React.useRef(null);
4744
4332
  React.useImperativeHandle(ref, () => group.current, []);
4745
4333
  const { width, height } = useThree((state2) => state2.size);
4746
- const fiberState = React.useContext(context$a);
4334
+ const fiberState = React.useContext(context$b);
4747
4335
  const root = React.useMemo(() => {
4748
4336
  let root2 = rootCache.get(state.fixed);
4749
4337
  if (!root2) {
@@ -4771,7 +4359,7 @@ const ScrollHtml = React.forwardRef(
4771
4359
  style: { ...style, position: "absolute", top: 0, left: 0, willChange: "transform" },
4772
4360
  ...props
4773
4361
  },
4774
- /* @__PURE__ */ React.createElement(context$7.Provider, { value: state }, /* @__PURE__ */ React.createElement(context$a.Provider, { value: fiberState }, children))
4362
+ /* @__PURE__ */ React.createElement(context$7.Provider, { value: state }, /* @__PURE__ */ React.createElement(context$b.Provider, { value: fiberState }, children))
4775
4363
  )
4776
4364
  );
4777
4365
  return null;
@@ -10159,7 +9747,7 @@ const HtmlView = /* @__PURE__ */ React.forwardRef(
10159
9747
  );
10160
9748
  const View = /* @__PURE__ */ (() => {
10161
9749
  const _View = React.forwardRef((props, fref) => {
10162
- const store = React.useContext(context$a);
9750
+ const store = React.useContext(context$b);
10163
9751
  if (!store) return /* @__PURE__ */ React.createElement(HtmlView, { ref: fref, ...props });
10164
9752
  else return /* @__PURE__ */ React.createElement(CanvasView, { ref: fref, ...props });
10165
9753
  });
@@ -11321,62 +10909,63 @@ const remap = /* @__PURE__ */ Fn((inputs) => {
11321
10909
  const [value, min1, max1, min2, max2] = inputs;
11322
10910
  return min2.add(value.sub(min1).mul(max2.sub(min2)).div(max1.sub(min1)));
11323
10911
  });
11324
- class WireframeMaterialImpl extends MeshBasicNodeMaterial {
11325
- //* Private Uniform Nodes --
11326
- _strokeOpacity;
11327
- _fillOpacity;
11328
- _fillMix;
11329
- _thickness;
11330
- _colorBackfaces;
11331
- // Using number as bool (0/1)
11332
- _dashInvert;
11333
- _dash;
11334
- _dashRepeats;
11335
- _dashLength;
11336
- _squeeze;
11337
- _squeezeMin;
11338
- _squeezeMax;
11339
- _stroke;
11340
- _backfaceStroke;
11341
- _fill;
10912
+ class WireframeMaterialImpl extends withUniforms(MeshBasicNodeMaterial, {
10913
+ /** Stroke (edge) opacity, default: 1 */
10914
+ strokeOpacity: () => uniform(1),
10915
+ /** Fill opacity (background), default: 0.25 */
10916
+ fillOpacity: () => uniform(0.25),
10917
+ /** Mix factor between material color and fill color, default: 0 */
10918
+ fillMix: () => uniform(0),
10919
+ /** Edge thickness 0-1, default: 0.05 */
10920
+ thickness: () => uniform(0.05),
10921
+ /** Use different color for backfaces, default: false */
10922
+ colorBackfaces: () => uniform(false),
10923
+ /** Invert dash pattern, default: true */
10924
+ dashInvert: () => uniform(true),
10925
+ /** Enable dashed lines, default: false */
10926
+ dash: () => uniform(false),
10927
+ /** Number of dash repeats, default: 4 */
10928
+ dashRepeats: () => uniform(4),
10929
+ /** Dash length 0-1, default: 0.5 */
10930
+ dashLength: () => uniform(0.5),
10931
+ /** Squeeze thickness toward line center, default: false */
10932
+ squeeze: () => uniform(false),
10933
+ /** Minimum squeeze factor, default: 0.2 */
10934
+ squeezeMin: () => uniform(0.2),
10935
+ /** Maximum squeeze factor, default: 1 */
10936
+ squeezeMax: () => uniform(1),
10937
+ /** Stroke (edge) color */
10938
+ stroke: () => uniform(new THREE.Color("#ff0000")),
10939
+ /** Backface stroke color */
10940
+ backfaceStroke: () => uniform(new THREE.Color("#0000ff")),
10941
+ /** Fill (background) color */
10942
+ fill: () => uniform(new THREE.Color("#00ff00"))
10943
+ }) {
11342
10944
  /** Type flag for identification */
11343
10945
  isWireframeMaterial = true;
11344
10946
  constructor() {
11345
10947
  super();
11346
- this._strokeOpacity = uniform(1);
11347
- this._fillOpacity = uniform(0.25);
11348
- this._fillMix = uniform(0);
11349
- this._thickness = uniform(0.05);
11350
- this._colorBackfaces = uniform(0);
11351
- this._dashInvert = uniform(1);
11352
- this._dash = uniform(0);
11353
- this._dashRepeats = uniform(4);
11354
- this._dashLength = uniform(0.5);
11355
- this._squeeze = uniform(0);
11356
- this._squeezeMin = uniform(0.2);
11357
- this._squeezeMax = uniform(1);
11358
- this._stroke = uniform(new THREE.Color("#ff0000"));
11359
- this._backfaceStroke = uniform(new THREE.Color("#0000ff"));
11360
- this._fill = uniform(new THREE.Color("#00ff00"));
11361
10948
  this.transparent = true;
11362
10949
  this.side = THREE.DoubleSide;
11363
10950
  this._buildWireframeShader();
11364
10951
  }
11365
10952
  _buildWireframeShader() {
11366
- const strokeOpacityUniform = this._strokeOpacity;
11367
- const fillOpacityUniform = this._fillOpacity;
11368
- const thicknessUniform = this._thickness;
11369
- const colorBackfacesUniform = this._colorBackfaces;
11370
- const dashInvertUniform = this._dashInvert;
11371
- const dashUniform = this._dash;
11372
- const dashRepeatsUniform = this._dashRepeats;
11373
- const dashLengthUniform = this._dashLength;
11374
- const squeezeUniform = this._squeeze;
11375
- const squeezeMinUniform = this._squeezeMin;
11376
- const squeezeMaxUniform = this._squeezeMax;
11377
- const strokeUniform = this._stroke;
11378
- const backfaceStrokeUniform = this._backfaceStroke;
11379
- const fillUniform = this._fill;
10953
+ const {
10954
+ strokeOpacity: strokeOpacityUniform,
10955
+ fillOpacity: fillOpacityUniform,
10956
+ thickness: thicknessUniform,
10957
+ colorBackfaces: colorBackfacesUniform,
10958
+ dashInvert: dashInvertUniform,
10959
+ dash: dashUniform,
10960
+ dashRepeats: dashRepeatsUniform,
10961
+ dashLength: dashLengthUniform,
10962
+ squeeze: squeezeUniform,
10963
+ squeezeMin: squeezeMinUniform,
10964
+ squeezeMax: squeezeMaxUniform,
10965
+ stroke: strokeUniform,
10966
+ backfaceStroke: backfaceStrokeUniform,
10967
+ fill: fillUniform
10968
+ } = this.uniforms;
11380
10969
  const barycentricAttr = attribute("barycentric", "vec3");
11381
10970
  const vBarycentric = varying(barycentricAttr, "v_barycentric");
11382
10971
  this.outputNode = Fn(() => {
@@ -11387,136 +10976,23 @@ class WireframeMaterialImpl extends MeshBasicNodeMaterial {
11387
10976
  const isYSmallest = barycentric.y.lessThan(barycentric.x).and(barycentric.y.lessThan(barycentric.z));
11388
10977
  positionAlong.assign(select(isYSmallest, float(1).sub(maxXY), maxXY));
11389
10978
  const computedThickness = remap(thicknessUniform, float(0), float(1), float(0), float(0.34)).toVar();
11390
- const squeezeEnabled = squeezeUniform.greaterThan(0.5);
11391
10979
  const squeezeFactor = mix(squeezeMinUniform, squeezeMaxUniform, float(1).sub(sin(positionAlong.mul(Math.PI))));
11392
- computedThickness.assign(select(squeezeEnabled, computedThickness.mul(squeezeFactor), computedThickness));
11393
- const dashEnabled = dashUniform.greaterThan(0.5);
11394
- const dashInverted = dashInvertUniform.greaterThan(0.5);
10980
+ computedThickness.assign(select(squeezeUniform, computedThickness.mul(squeezeFactor), computedThickness));
11395
10981
  const baseOffset = float(1).div(dashRepeatsUniform).mul(dashLengthUniform).mul(0.5);
11396
10982
  const additionalOffset = float(1).div(dashRepeatsUniform).mul(0.5);
11397
- const dashOffset = select(dashInverted, baseOffset, baseOffset.add(additionalOffset));
10983
+ const dashOffset = select(dashInvertUniform, baseOffset, baseOffset.add(additionalOffset));
11398
10984
  const pattern = fract(positionAlong.add(dashOffset).mul(dashRepeatsUniform));
11399
10985
  const dashMask = float(1).sub(aastep(dashLengthUniform, pattern));
11400
- computedThickness.assign(select(dashEnabled, computedThickness.mul(dashMask), computedThickness));
10986
+ computedThickness.assign(select(dashUniform, computedThickness.mul(dashMask), computedThickness));
11401
10987
  const edge = float(1).sub(aastep(computedThickness, d));
11402
- const useBackfaceColor = colorBackfacesUniform.greaterThan(0.5);
11403
10988
  const isFrontFace = frontFacing;
11404
- const currentStroke = select(useBackfaceColor.and(isFrontFace.not()), backfaceStrokeUniform, strokeUniform);
10989
+ const currentStroke = select(colorBackfacesUniform.and(isFrontFace.not()), backfaceStrokeUniform, strokeUniform);
11405
10990
  const colorStroke = vec4(currentStroke, edge);
11406
10991
  const colorFill = vec4(fillUniform, fillOpacityUniform);
11407
10992
  const outColor = mix(colorFill, colorStroke, edge.mul(strokeOpacityUniform));
11408
10993
  return outColor;
11409
10994
  })();
11410
10995
  }
11411
- //* Uniform Accessors ==============================
11412
- /** Stroke (edge) opacity, default: 1 */
11413
- get strokeOpacity() {
11414
- return this._strokeOpacity.value;
11415
- }
11416
- set strokeOpacity(v) {
11417
- this._strokeOpacity.value = v;
11418
- }
11419
- /** Fill opacity (background), default: 0.25 */
11420
- get fillOpacity() {
11421
- return this._fillOpacity.value;
11422
- }
11423
- set fillOpacity(v) {
11424
- this._fillOpacity.value = v;
11425
- }
11426
- /** Mix factor between material color and fill color, default: 0 */
11427
- get fillMix() {
11428
- return this._fillMix.value;
11429
- }
11430
- set fillMix(v) {
11431
- this._fillMix.value = v;
11432
- }
11433
- /** Edge thickness 0-1, default: 0.05 */
11434
- get thickness() {
11435
- return this._thickness.value;
11436
- }
11437
- set thickness(v) {
11438
- this._thickness.value = v;
11439
- }
11440
- /** Use different color for backfaces */
11441
- get colorBackfaces() {
11442
- return this._colorBackfaces.value > 0.5;
11443
- }
11444
- set colorBackfaces(v) {
11445
- this._colorBackfaces.value = v ? 1 : 0;
11446
- }
11447
- /** Invert dash pattern */
11448
- get dashInvert() {
11449
- return this._dashInvert.value > 0.5;
11450
- }
11451
- set dashInvert(v) {
11452
- this._dashInvert.value = v ? 1 : 0;
11453
- }
11454
- /** Enable dashed lines */
11455
- get dash() {
11456
- return this._dash.value > 0.5;
11457
- }
11458
- set dash(v) {
11459
- this._dash.value = v ? 1 : 0;
11460
- }
11461
- /** Number of dash repeats, default: 4 */
11462
- get dashRepeats() {
11463
- return this._dashRepeats.value;
11464
- }
11465
- set dashRepeats(v) {
11466
- this._dashRepeats.value = v;
11467
- }
11468
- /** Dash length 0-1, default: 0.5 */
11469
- get dashLength() {
11470
- return this._dashLength.value;
11471
- }
11472
- set dashLength(v) {
11473
- this._dashLength.value = v;
11474
- }
11475
- /** Squeeze thickness toward line center */
11476
- get squeeze() {
11477
- return this._squeeze.value > 0.5;
11478
- }
11479
- set squeeze(v) {
11480
- this._squeeze.value = v ? 1 : 0;
11481
- }
11482
- /** Minimum squeeze factor, default: 0.2 */
11483
- get squeezeMin() {
11484
- return this._squeezeMin.value;
11485
- }
11486
- set squeezeMin(v) {
11487
- this._squeezeMin.value = v;
11488
- }
11489
- /** Maximum squeeze factor, default: 1 */
11490
- get squeezeMax() {
11491
- return this._squeezeMax.value;
11492
- }
11493
- set squeezeMax(v) {
11494
- this._squeezeMax.value = v;
11495
- }
11496
- /** Stroke (edge) color */
11497
- get stroke() {
11498
- return this._stroke.value;
11499
- }
11500
- set stroke(v) {
11501
- if (v instanceof THREE.Color) this._stroke.value = v;
11502
- else this._stroke.value = new THREE.Color(v);
11503
- }
11504
- /** Backface stroke color */
11505
- get backfaceStroke() {
11506
- return this._backfaceStroke.value;
11507
- }
11508
- set backfaceStroke(v) {
11509
- if (v instanceof THREE.Color) this._backfaceStroke.value = v;
11510
- else this._backfaceStroke.value = new THREE.Color(v);
11511
- }
11512
- /** Fill (background) color */
11513
- get fill() {
11514
- return this._fill.value;
11515
- }
11516
- set fill(v) {
11517
- if (v instanceof THREE.Color) this._fill.value = v;
11518
- else this._fill.value = new THREE.Color(v);
11519
- }
11520
10996
  }
11521
10997
  function setBarycentricCoordinates(geometry) {
11522
10998
  const position = geometry.getAttribute("position");
@@ -11578,9 +11054,9 @@ const WireframeMaterial = /* @__PURE__ */ React.forwardRef(
11578
11054
  squeeze,
11579
11055
  squeezeMin,
11580
11056
  squeezeMax,
11581
- stroke: stroke instanceof THREE.Color ? stroke : new THREE.Color(stroke),
11582
- backfaceStroke: backfaceStroke instanceof THREE.Color ? backfaceStroke : new THREE.Color(backfaceStroke),
11583
- fill: fill instanceof THREE.Color ? fill : new THREE.Color(fill)
11057
+ stroke,
11058
+ backfaceStroke,
11059
+ fill
11584
11060
  }
11585
11061
  );
11586
11062
  }
@@ -11696,52 +11172,74 @@ function Wireframe({ geometry: customGeometry, ...props }) {
11696
11172
  return /* @__PURE__ */ React.createElement(WireframeWithoutCustomGeo, { ...props });
11697
11173
  }
11698
11174
 
11175
+ function isShadowCastingLight(object) {
11176
+ const light = object;
11177
+ return light.isLight === true && light.castShadow === true && light.shadow != null;
11178
+ }
11699
11179
  function BakeShadows() {
11700
- const gl = useThree((state) => state.gl);
11701
- useEffect(() => {
11702
- gl.shadowMap.autoUpdate = false;
11703
- gl.shadowMap.needsUpdate = true;
11180
+ const scene = useThree((state) => state.scene);
11181
+ const priorRef = React.useRef(/* @__PURE__ */ new WeakMap());
11182
+ useFrame(() => {
11183
+ const prior = priorRef.current;
11184
+ scene.traverse((object) => {
11185
+ if (!isShadowCastingLight(object)) return;
11186
+ const shadow = object.shadow;
11187
+ if (prior.has(shadow)) return;
11188
+ prior.set(shadow, { autoUpdate: shadow.autoUpdate, needsUpdate: shadow.needsUpdate });
11189
+ shadow.autoUpdate = false;
11190
+ shadow.needsUpdate = true;
11191
+ });
11192
+ });
11193
+ React.useEffect(() => {
11194
+ const prior = priorRef.current;
11704
11195
  return () => {
11705
- gl.shadowMap.autoUpdate = gl.shadowMap.needsUpdate = true;
11196
+ scene.traverse((object) => {
11197
+ if (!isShadowCastingLight(object)) return;
11198
+ const previous = prior.get(object.shadow);
11199
+ if (previous === void 0) return;
11200
+ object.shadow.autoUpdate = previous.autoUpdate;
11201
+ object.shadow.needsUpdate = previous.needsUpdate;
11202
+ prior.delete(object.shadow);
11203
+ });
11706
11204
  };
11707
- }, [gl.shadowMap]);
11205
+ }, [scene]);
11708
11206
  return null;
11709
11207
  }
11710
11208
 
11711
- class ConvolutionMaterial extends MeshBasicNodeMaterial {
11712
- //* Private Uniform Nodes --
11713
- _inputBuffer;
11714
- _depthBuffer;
11715
- _resolution;
11716
- _texelSize;
11717
- _halfTexelSize;
11718
- _kernel;
11719
- _scale;
11720
- _cameraNear;
11721
- _cameraFar;
11722
- _minDepthThreshold;
11723
- _maxDepthThreshold;
11724
- _depthScale;
11725
- _depthToBlurRatioBias;
11726
- _useDepth;
11209
+ class ConvolutionMaterial extends withUniforms(MeshBasicNodeMaterial, {
11210
+ /** Input color buffer to blur */
11211
+ inputBuffer: () => uniformTexture(new THREE.Texture()),
11212
+ /** Depth buffer for depth-aware blur */
11213
+ depthBuffer: () => uniformTexture(new THREE.Texture()),
11214
+ /** Render resolution */
11215
+ resolution: () => uniform(new THREE.Vector2()),
11216
+ /** Texel size (1/resolution) */
11217
+ texelSize: () => uniform(new THREE.Vector2()),
11218
+ /** Half texel size for offset calculations */
11219
+ halfTexelSize: () => uniform(new THREE.Vector2()),
11220
+ /** Kernel offset multiplier (changes per blur pass) */
11221
+ kernelValue: () => uniform(0),
11222
+ /** Scale factor for blur spread */
11223
+ scale: () => uniform(1),
11224
+ /** Camera near plane for depth calculations */
11225
+ cameraNear: () => uniform(0),
11226
+ /** Camera far plane for depth calculations */
11227
+ cameraFar: () => uniform(1),
11228
+ /** Minimum depth threshold for blur (objects closer than this get less blur) */
11229
+ minDepthThreshold: () => uniform(0),
11230
+ /** Maximum depth threshold for blur (objects farther than this get full blur) */
11231
+ maxDepthThreshold: () => uniform(1),
11232
+ /** Scale factor for depth-based blur adjustment */
11233
+ depthScale: () => uniform(0),
11234
+ /** Bias added to depth-to-blur ratio */
11235
+ depthToBlurRatioBias: () => uniform(0.25),
11236
+ /** Enable depth-based blur */
11237
+ useDepth: () => uniform(false)
11238
+ }) {
11727
11239
  /** Kernel weights for multi-pass blur */
11728
11240
  kernel;
11729
11241
  constructor(texelSize = new THREE.Vector2()) {
11730
11242
  super();
11731
- this._inputBuffer = uniformTexture(new THREE.Texture());
11732
- this._depthBuffer = uniformTexture(new THREE.Texture());
11733
- this._resolution = uniform(new THREE.Vector2());
11734
- this._texelSize = uniform(new THREE.Vector2());
11735
- this._halfTexelSize = uniform(new THREE.Vector2());
11736
- this._kernel = uniform(0);
11737
- this._scale = uniform(1);
11738
- this._cameraNear = uniform(0);
11739
- this._cameraFar = uniform(1);
11740
- this._minDepthThreshold = uniform(0);
11741
- this._maxDepthThreshold = uniform(1);
11742
- this._depthScale = uniform(0);
11743
- this._depthToBlurRatioBias = uniform(0.25);
11744
- this._useDepth = uniform(0);
11745
11243
  this.blending = THREE.NoBlending;
11746
11244
  this.depthWrite = false;
11747
11245
  this.depthTest = false;
@@ -11751,16 +11249,19 @@ class ConvolutionMaterial extends MeshBasicNodeMaterial {
11751
11249
  this._buildShader();
11752
11250
  }
11753
11251
  _buildShader() {
11754
- const inputBufferTex = this._inputBuffer;
11755
- const depthBufferTex = this._depthBuffer;
11756
- const texelSizeUniform = this._texelSize;
11757
- const halfTexelSizeUniform = this._halfTexelSize;
11758
- const kernelUniform = this._kernel;
11759
- const scaleUniform = this._scale;
11760
- const minDepthThresholdUniform = this._minDepthThreshold;
11761
- const maxDepthThresholdUniform = this._maxDepthThreshold;
11762
- const depthScaleUniform = this._depthScale;
11763
- const useDepthUniform = this._useDepth;
11252
+ const {
11253
+ inputBuffer: inputBufferTex,
11254
+ depthBuffer: depthBufferTex,
11255
+ texelSize: texelSizeUniform,
11256
+ halfTexelSize: halfTexelSizeUniform,
11257
+ kernelValue: kernelUniform,
11258
+ scale: scaleUniform,
11259
+ minDepthThreshold: minDepthThresholdUniform,
11260
+ maxDepthThreshold: maxDepthThresholdUniform,
11261
+ depthScale: depthScaleUniform,
11262
+ depthToBlurRatioBias: depthToBlurRatioBiasUniform,
11263
+ useDepth: useDepthUniform
11264
+ } = this.uniforms;
11764
11265
  const baseUv = varying(uv(), "vUv");
11765
11266
  const dUv = varying(
11766
11267
  Fn(() => {
@@ -11774,17 +11275,16 @@ class ConvolutionMaterial extends MeshBasicNodeMaterial {
11774
11275
  const vUv3 = varying(Fn(() => vec2$2(baseUv.x.sub(dUv.x), baseUv.y.sub(dUv.y)))(), "vUv3");
11775
11276
  this.colorNode = Fn(() => {
11776
11277
  const depthFactor = float(0).toVar();
11777
- const isUsingDepth = useDepthUniform.greaterThan(0.5);
11778
- const depthSample = texture(depthBufferTex, baseUv);
11278
+ const depthSample = depthBufferTex.sample(baseUv);
11779
11279
  const rawDepth = float(1).sub(depthSample.r.mul(depthSample.a));
11780
11280
  const smoothedDepth = smoothstep(minDepthThresholdUniform, maxDepthThresholdUniform, rawDepth);
11781
11281
  const scaledDepth = smoothedDepth.mul(depthScaleUniform);
11782
- const clampedDepth = max(float(0), min(float(1), scaledDepth.add(0.25)));
11783
- depthFactor.assign(select(isUsingDepth, clampedDepth, float(0)));
11784
- const sample0 = texture(inputBufferTex, mix(vUv0, baseUv, depthFactor));
11785
- const sample1 = texture(inputBufferTex, mix(vUv1, baseUv, depthFactor));
11786
- const sample2 = texture(inputBufferTex, mix(vUv2, baseUv, depthFactor));
11787
- const sample3 = texture(inputBufferTex, mix(vUv3, baseUv, depthFactor));
11282
+ const clampedDepth = max(float(0), min(float(1), scaledDepth.add(depthToBlurRatioBiasUniform)));
11283
+ depthFactor.assign(select(useDepthUniform, clampedDepth, float(0)));
11284
+ const sample0 = inputBufferTex.sample(mix(vUv0, baseUv, depthFactor));
11285
+ const sample1 = inputBufferTex.sample(mix(vUv1, baseUv, depthFactor));
11286
+ const sample2 = inputBufferTex.sample(mix(vUv2, baseUv, depthFactor));
11287
+ const sample3 = inputBufferTex.sample(mix(vUv3, baseUv, depthFactor));
11788
11288
  const sum = sample0.add(sample1).add(sample2).add(sample3);
11789
11289
  return sum.mul(0.25);
11790
11290
  })();
@@ -11792,111 +11292,90 @@ class ConvolutionMaterial extends MeshBasicNodeMaterial {
11792
11292
  //* Utility Methods ==============================
11793
11293
  /** Set texel size for blur calculations */
11794
11294
  setTexelSize(x, y) {
11795
- this._texelSize.value.set(x, y);
11796
- this._halfTexelSize.value.set(x * 0.5, y * 0.5);
11295
+ this.uniforms.texelSize.value.set(x, y);
11296
+ this.uniforms.halfTexelSize.value.set(x * 0.5, y * 0.5);
11797
11297
  }
11798
11298
  /** Set render resolution */
11799
11299
  setResolution(resolution) {
11800
- this._resolution.value.copy(resolution);
11801
- }
11802
- //* Uniform Accessors ==============================
11803
- /** Input color buffer to blur */
11804
- get inputBuffer() {
11805
- return this._inputBuffer.value;
11806
- }
11807
- set inputBuffer(v) {
11808
- this._inputBuffer.value = v ?? new THREE.Texture();
11809
- }
11810
- /** Depth buffer for depth-aware blur */
11811
- get depthBuffer() {
11812
- return this._depthBuffer.value;
11813
- }
11814
- set depthBuffer(v) {
11815
- this._depthBuffer.value = v ?? new THREE.Texture();
11816
- }
11817
- /** Render resolution */
11818
- get resolution() {
11819
- return this._resolution.value;
11820
- }
11821
- set resolution(v) {
11822
- this._resolution.value = v;
11823
- }
11824
- /** Texel size (1/resolution) */
11825
- get texelSize() {
11826
- return this._texelSize.value;
11827
- }
11828
- set texelSize(v) {
11829
- this._texelSize.value = v;
11830
- }
11831
- /** Half texel size for offset calculations */
11832
- get halfTexelSize() {
11833
- return this._halfTexelSize.value;
11834
- }
11835
- set halfTexelSize(v) {
11836
- this._halfTexelSize.value = v;
11837
- }
11838
- /** Kernel offset multiplier (changes per blur pass) */
11839
- get kernelValue() {
11840
- return this._kernel.value;
11841
- }
11842
- set kernelValue(v) {
11843
- this._kernel.value = v;
11844
- }
11845
- /** Scale factor for blur spread */
11846
- get scale() {
11847
- return this._scale.value;
11848
- }
11849
- set scale(v) {
11850
- this._scale.value = v;
11851
- }
11852
- /** Camera near plane for depth calculations */
11853
- get cameraNear() {
11854
- return this._cameraNear.value;
11855
- }
11856
- set cameraNear(v) {
11857
- this._cameraNear.value = v;
11858
- }
11859
- /** Camera far plane for depth calculations */
11860
- get cameraFar() {
11861
- return this._cameraFar.value;
11862
- }
11863
- set cameraFar(v) {
11864
- this._cameraFar.value = v;
11865
- }
11866
- /** Minimum depth threshold for blur (objects closer than this get less blur) */
11867
- get minDepthThreshold() {
11868
- return this._minDepthThreshold.value;
11300
+ this.uniforms.resolution.value.copy(resolution);
11869
11301
  }
11870
- set minDepthThreshold(v) {
11871
- this._minDepthThreshold.value = v;
11872
- }
11873
- /** Maximum depth threshold for blur (objects farther than this get full blur) */
11874
- get maxDepthThreshold() {
11875
- return this._maxDepthThreshold.value;
11876
- }
11877
- set maxDepthThreshold(v) {
11878
- this._maxDepthThreshold.value = v;
11879
- }
11880
- /** Scale factor for depth-based blur adjustment */
11881
- get depthScale() {
11882
- return this._depthScale.value;
11883
- }
11884
- set depthScale(v) {
11885
- this._depthScale.value = v;
11886
- }
11887
- /** Bias added to depth-to-blur ratio */
11888
- get depthToBlurRatioBias() {
11889
- return this._depthToBlurRatioBias.value;
11890
- }
11891
- set depthToBlurRatioBias(v) {
11892
- this._depthToBlurRatioBias.value = v;
11302
+ }
11303
+
11304
+ class BlurPass {
11305
+ renderTargetA;
11306
+ renderTargetB;
11307
+ convolutionMaterial;
11308
+ /** Fullscreen triangle the convolution material is drawn with */
11309
+ screen;
11310
+ renderToScreen = false;
11311
+ constructor({
11312
+ resolution,
11313
+ width = 500,
11314
+ height = 500,
11315
+ minDepthThreshold = 0,
11316
+ maxDepthThreshold = 1,
11317
+ depthScale = 0,
11318
+ depthToBlurRatioBias = 0.25
11319
+ }) {
11320
+ this.renderTargetA = new THREE.RenderTarget(resolution, resolution, {
11321
+ minFilter: THREE.LinearFilter,
11322
+ magFilter: THREE.LinearFilter,
11323
+ stencilBuffer: false,
11324
+ depthBuffer: false,
11325
+ type: THREE.HalfFloatType
11326
+ });
11327
+ this.renderTargetB = this.renderTargetA.clone();
11328
+ const material = new ConvolutionMaterial();
11329
+ material.setTexelSize(1 / width, 1 / height);
11330
+ material.setResolution(new THREE.Vector2(width, height));
11331
+ material.minDepthThreshold = minDepthThreshold;
11332
+ material.maxDepthThreshold = maxDepthThreshold;
11333
+ material.depthScale = depthScale;
11334
+ material.depthToBlurRatioBias = depthToBlurRatioBias;
11335
+ material.useDepth = depthScale > 0;
11336
+ this.convolutionMaterial = material;
11337
+ this.screen = new QuadMesh(material);
11338
+ this.screen.frustumCulled = false;
11893
11339
  }
11894
- /** Enable/disable depth-based blur (0 = off, 1 = on) */
11895
- get useDepth() {
11896
- return this._useDepth.value > 0.5;
11340
+ /**
11341
+ * Runs the blur.
11342
+ *
11343
+ * Reads `inputBuffer`, ping-pongs through the internal targets, and writes the
11344
+ * final pass into `outputBuffer` (or the canvas when `renderToScreen` is true).
11345
+ * The render target is left bound, matching the legacy pass — restore it with
11346
+ * `renderer.setRenderTarget(null)` when you are done.
11347
+ */
11348
+ render(renderer, inputBuffer, outputBuffer = null) {
11349
+ const material = this.convolutionMaterial;
11350
+ material.depthBuffer = inputBuffer.depthTexture;
11351
+ const kernel = material.kernel;
11352
+ let lastRT = inputBuffer;
11353
+ let i = 0;
11354
+ const last = kernel.length - 1;
11355
+ for (; i < last; ++i) {
11356
+ const destRT = (i & 1) === 0 ? this.renderTargetA : this.renderTargetB;
11357
+ material.kernelValue = kernel[i];
11358
+ material.inputBuffer = lastRT.texture;
11359
+ renderer.setRenderTarget(destRT);
11360
+ this.screen.render(renderer);
11361
+ lastRT = destRT;
11362
+ }
11363
+ material.kernelValue = kernel[i];
11364
+ material.inputBuffer = lastRT.texture;
11365
+ renderer.setRenderTarget(this.renderToScreen ? null : outputBuffer);
11366
+ this.screen.render(renderer);
11367
+ }
11368
+ /** Resize the intermediate targets and recompute the texel size */
11369
+ setSize(width, height) {
11370
+ this.renderTargetA.setSize(width, height);
11371
+ this.renderTargetB.setSize(width, height);
11372
+ this.convolutionMaterial.setTexelSize(1 / width, 1 / height);
11373
+ this.convolutionMaterial.setResolution(new THREE.Vector2(width, height));
11897
11374
  }
11898
- set useDepth(v) {
11899
- this._useDepth.value = v ? 1 : 0;
11375
+ dispose() {
11376
+ this.renderTargetA.dispose();
11377
+ this.renderTargetB.dispose();
11378
+ this.convolutionMaterial.dispose();
11900
11379
  }
11901
11380
  }
11902
11381
 
@@ -12094,28 +11573,31 @@ const Image = /* @__PURE__ */ React.forwardRef((props, ref) => {
12094
11573
  else throw new Error("<Image /> requires a url or texture");
12095
11574
  });
12096
11575
 
12097
- class SpotLightMaterial extends MeshBasicNodeMaterial {
12098
- //* Private Uniform Nodes --
12099
- _depth;
12100
- _opacity;
12101
- _attenuation;
12102
- _anglePower;
12103
- _spotPosition;
12104
- _lightColor;
12105
- _cameraNear;
12106
- _cameraFar;
12107
- _resolution;
11576
+ const MeshDiscardMaterial = /* @__PURE__ */ React.forwardRef((props, fref) => {
11577
+ const [material] = React.useState(() => new DiscardNodeMaterial());
11578
+ return /* @__PURE__ */ React.createElement("primitive", { object: material, ref: fref, attach: "material", ...props });
11579
+ });
11580
+
11581
+ class SpotLightMaterial extends withUniforms(MeshBasicNodeMaterial, {
11582
+ /** Scene depth (in the R channel) for soft intersection with scene geometry */
11583
+ depth: () => uniformTexture(new THREE.Texture()),
11584
+ /** Distance attenuation factor, default: 5 */
11585
+ attenuation: () => uniform(5),
11586
+ /** Angle falloff power - higher = sharper edges, default: 5 */
11587
+ anglePower: () => uniform(5),
11588
+ /** World position of the cone tip */
11589
+ spotPosition: () => uniform(new THREE.Vector3(0, 0, 0)),
11590
+ /** Light color, default: white */
11591
+ lightColor: () => uniform(new THREE.Color("white")),
11592
+ /** Camera near plane, for depth reconstruction */
11593
+ cameraNear: () => uniform(0.1),
11594
+ /** Camera far plane, for depth reconstruction */
11595
+ cameraFar: () => uniform(100),
11596
+ /** Drawing-buffer size in pixels. Zero disables depth-based soft edges. */
11597
+ resolution: () => uniform(new THREE.Vector2(0, 0))
11598
+ }) {
12108
11599
  constructor() {
12109
11600
  super();
12110
- this._depth = uniformTexture(new THREE.Texture());
12111
- this._opacity = uniform(1);
12112
- this._attenuation = uniform(5);
12113
- this._anglePower = uniform(5);
12114
- this._spotPosition = uniform(new THREE.Vector3(0, 0, 0));
12115
- this._lightColor = uniform(new THREE.Color("white"));
12116
- this._cameraNear = uniform(0.1);
12117
- this._cameraFar = uniform(100);
12118
- this._resolution = uniform(new THREE.Vector2(0, 0));
12119
11601
  this.transparent = true;
12120
11602
  this.depthWrite = false;
12121
11603
  this.side = THREE.DoubleSide;
@@ -12123,15 +11605,16 @@ class SpotLightMaterial extends MeshBasicNodeMaterial {
12123
11605
  }
12124
11606
  //* Build TSL Shader ==============================
12125
11607
  _buildShader() {
12126
- const depthTex = this._depth;
12127
- const opacityUniform = this._opacity;
12128
- const attenuationUniform = this._attenuation;
12129
- const anglePowerUniform = this._anglePower;
12130
- const spotPositionUniform = this._spotPosition;
12131
- const lightColorUniform = this._lightColor;
12132
- const cameraNearUniform = this._cameraNear;
12133
- const cameraFarUniform = this._cameraFar;
12134
- const resolutionUniform = this._resolution;
11608
+ const {
11609
+ depth: depthTex,
11610
+ attenuation: attenuationUniform,
11611
+ anglePower: anglePowerUniform,
11612
+ spotPosition: spotPositionUniform,
11613
+ lightColor: lightColorUniform,
11614
+ cameraNear: cameraNearUniform,
11615
+ cameraFar: cameraFarUniform,
11616
+ resolution: resolutionUniform
11617
+ } = this.uniforms;
12135
11618
  const vNormal = varying(normalize(modelViewMatrix.transformDirection(normalLocal)), "vNormal");
12136
11619
  const vIntensity = varying(
12137
11620
  saturate(float(1).sub(distance(positionWorld, spotPositionUniform).div(attenuationUniform))),
@@ -12146,71 +11629,16 @@ class SpotLightMaterial extends MeshBasicNodeMaterial {
12146
11629
  screenCoordinate.x.div(resolutionUniform.x),
12147
11630
  float(1).sub(screenCoordinate.y.div(resolutionUniform.y))
12148
11631
  );
12149
- const sampledDepth = texture(depthTex, screenUV).r;
11632
+ const sampledDepth = depthTex.sample(screenUV).r;
12150
11633
  const near = cameraNearUniform;
12151
11634
  const far = cameraFarUniform;
12152
11635
  const sceneViewZ = near.mul(far).div(far.sub(near).mul(sampledDepth).sub(far));
12153
11636
  const depthDiff = vViewZ.sub(sceneViewZ);
12154
11637
  const softFactor = smoothstep(float(0), float(1), depthDiff);
12155
11638
  intensity.assign(select(hasDepth, intensity.mul(softFactor), intensity));
12156
- return vec4(lightColorUniform, intensity.mul(opacityUniform));
11639
+ return vec4(lightColorUniform, intensity.mul(materialOpacity));
12157
11640
  })();
12158
11641
  }
12159
- //* Uniform Accessors ==============================
12160
- get depth() {
12161
- return this._depth?.value;
12162
- }
12163
- set depth(v) {
12164
- if (this._depth) this._depth.value = v ?? new THREE.Texture();
12165
- }
12166
- get opacity() {
12167
- return this._opacity?.value;
12168
- }
12169
- set opacity(v) {
12170
- if (this._opacity) this._opacity.value = v;
12171
- }
12172
- get attenuation() {
12173
- return this._attenuation?.value;
12174
- }
12175
- set attenuation(v) {
12176
- if (this._attenuation) this._attenuation.value = v;
12177
- }
12178
- get anglePower() {
12179
- return this._anglePower?.value;
12180
- }
12181
- set anglePower(v) {
12182
- if (this._anglePower) this._anglePower.value = v;
12183
- }
12184
- get spotPosition() {
12185
- return this._spotPosition?.value;
12186
- }
12187
- set spotPosition(v) {
12188
- if (this._spotPosition) this._spotPosition.value = v;
12189
- }
12190
- get lightColor() {
12191
- return this._lightColor?.value;
12192
- }
12193
- set lightColor(v) {
12194
- if (this._lightColor) this._lightColor.value = v;
12195
- }
12196
- get cameraNear() {
12197
- return this._cameraNear?.value;
12198
- }
12199
- set cameraNear(v) {
12200
- if (this._cameraNear) this._cameraNear.value = v;
12201
- }
12202
- get cameraFar() {
12203
- return this._cameraFar?.value;
12204
- }
12205
- set cameraFar(v) {
12206
- if (this._cameraFar) this._cameraFar.value = v;
12207
- }
12208
- get resolution() {
12209
- return this._resolution?.value;
12210
- }
12211
- set resolution(v) {
12212
- if (this._resolution) this._resolution.value = v;
12213
- }
12214
11642
  }
12215
11643
 
12216
11644
  function createHtmlNodeMaterial(transform) {
@@ -12231,12 +11659,15 @@ const HtmlMaterial = ({ transform }) => {
12231
11659
  return /* @__PURE__ */ React.createElement("primitive", { object: material, attach: "material" });
12232
11660
  };
12233
11661
 
12234
- class StarfieldMaterial extends SpriteNodeMaterial {
12235
- //* Uniforms ==============================
12236
- _time = uniform(0);
12237
- _fade = uniform(1);
11662
+ class StarfieldMaterial extends withUniforms(SpriteNodeMaterial, {
11663
+ /** Animation time, advanced by the component each frame */
11664
+ time: () => uniform(0),
11665
+ /** 1 = soft circular fade, 0 = hard-edged circle */
11666
+ fade: () => uniform(1)
11667
+ }) {
12238
11668
  constructor() {
12239
11669
  super();
11670
+ const { time, fade } = this.uniforms;
12240
11671
  this.transparent = true;
12241
11672
  this.depthWrite = false;
12242
11673
  this.blending = AdditiveBlending;
@@ -12250,7 +11681,7 @@ class StarfieldMaterial extends SpriteNodeMaterial {
12250
11681
  const worldPos = modelWorldMatrix.mul(vec4(this.positionNode, 1));
12251
11682
  const viewPos = cameraViewMatrix.mul(worldPos);
12252
11683
  const distanceAttenuation = float(30).div(viewPos.z.negate());
12253
- const timeScale = float(3.5).add(sin(this._time.add(100)).mul(0.3));
11684
+ const timeScale = float(3.5).add(sin(time.add(100)).mul(0.3));
12254
11685
  const size = particleSize.mul(distanceAttenuation).mul(timeScale).mul(0.04);
12255
11686
  return vec2$2(size);
12256
11687
  })();
@@ -12259,38 +11690,28 @@ class StarfieldMaterial extends SpriteNodeMaterial {
12259
11690
  const d = length(quadUV.sub(vec2$2(0.5)));
12260
11691
  const softFade = float(1).div(float(1).add(exp(float(16).mul(d.sub(0.25)))));
12261
11692
  const hardMask = float(1).sub(d.smoothstep(0.3, 0.5));
12262
- const circularOpacity = select(this._fade.equal(1), softFade, hardMask);
11693
+ const circularOpacity = select(fade.equal(1), softFade, hardMask);
12263
11694
  const opacity = circularOpacity.mul(0.7);
12264
11695
  return vec4(particleColor, opacity);
12265
11696
  })();
12266
11697
  }
12267
- //* Uniform Accessors ==============================
12268
11698
  setTime(time) {
12269
- this._time.value = time;
11699
+ this.time = time;
12270
11700
  }
12271
11701
  setFade(fade) {
12272
- this._fade.value = fade;
12273
- }
12274
- get time() {
12275
- return this._time.value;
12276
- }
12277
- set time(value) {
12278
- this._time.value = value;
12279
- }
12280
- get fade() {
12281
- return this._fade.value;
12282
- }
12283
- set fade(value) {
12284
- this._fade.value = value;
11702
+ this.fade = fade;
12285
11703
  }
12286
11704
  }
12287
11705
 
12288
- class SparklesMaterial extends SpriteNodeMaterial {
12289
- // Uniforms exposed for external updates
12290
- _time = uniform(0);
12291
- _pixelRatio = uniform(1);
11706
+ class SparklesMaterial extends withUniforms(SpriteNodeMaterial, {
11707
+ /** Animation time, advanced by the component each frame */
11708
+ time: () => uniform(0),
11709
+ /** Device pixel ratio, scales the point size */
11710
+ pixelRatio: () => uniform(1)
11711
+ }) {
12292
11712
  constructor() {
12293
11713
  super();
11714
+ const { time, pixelRatio } = this.uniforms;
12294
11715
  this.transparent = true;
12295
11716
  this.depthWrite = false;
12296
11717
  const particlePosition = attribute("particlePosition", "vec3");
@@ -12301,13 +11722,13 @@ class SparklesMaterial extends SpriteNodeMaterial {
12301
11722
  const particleColor = attribute("color", "vec3");
12302
11723
  this.positionNode = Fn(() => {
12303
11724
  const pos = particlePosition.toVar();
12304
- pos.y.addAssign(sin(this._time.mul(particleSpeed).add(pos.x.mul(particleNoise.x).mul(100))).mul(0.2));
12305
- pos.z.addAssign(cos(this._time.mul(particleSpeed).add(pos.x.mul(particleNoise.y).mul(100))).mul(0.2));
12306
- pos.x.addAssign(cos(this._time.mul(particleSpeed).add(pos.x.mul(particleNoise.z).mul(100))).mul(0.2));
11725
+ pos.y.addAssign(sin(time.mul(particleSpeed).add(pos.x.mul(particleNoise.x).mul(100))).mul(0.2));
11726
+ pos.z.addAssign(cos(time.mul(particleSpeed).add(pos.x.mul(particleNoise.y).mul(100))).mul(0.2));
11727
+ pos.x.addAssign(cos(time.mul(particleSpeed).add(pos.x.mul(particleNoise.z).mul(100))).mul(0.2));
12307
11728
  return pos;
12308
11729
  })();
12309
11730
  this.scaleNode = Fn(() => {
12310
- const pixelSize = particleSize.mul(25).mul(this._pixelRatio);
11731
+ const pixelSize = particleSize.mul(25).mul(pixelRatio);
12311
11732
  const projY = cameraProjectionMatrix.element(1).y;
12312
11733
  const viewportH = screenSize.y;
12313
11734
  const size = pixelSize.mul(2).div(projY.mul(viewportH));
@@ -12320,82 +11741,67 @@ class SparklesMaterial extends SpriteNodeMaterial {
12320
11741
  return vec4(particleColor, strength.mul(particleOpacity));
12321
11742
  })();
12322
11743
  }
12323
- //* Uniform Accessors --------------------------------
12324
- get time() {
12325
- return this._time.value;
12326
- }
12327
- set time(value) {
12328
- this._time.value = value;
12329
- }
12330
- get pixelRatio() {
12331
- return this._pixelRatio.value;
12332
- }
12333
- set pixelRatio(value) {
12334
- this._pixelRatio.value = value;
12335
- }
12336
- }
12337
-
12338
- class GridMaterialImpl extends MeshBasicNodeMaterial {
12339
- //* Private Uniform Nodes --
12340
- _cellSize;
12341
- _sectionSize;
12342
- _fadeDistance;
12343
- _fadeStrength;
12344
- _fadeFrom;
12345
- _cellThickness;
12346
- _sectionThickness;
12347
- _cellColor;
12348
- _sectionColor;
12349
- _infiniteGrid;
12350
- // bool as 0/1
12351
- _followCamera;
12352
- // bool as 0/1
12353
- _worldCamProjPosition;
12354
- _worldPlanePosition;
11744
+ }
11745
+
11746
+ class GridMaterialImpl extends withUniforms(MeshBasicNodeMaterial, {
11747
+ /** Cell size, default: 0.5 */
11748
+ cellSize: () => uniform(0.5),
11749
+ /** Section size, default: 1 */
11750
+ sectionSize: () => uniform(1),
11751
+ /** Fade distance, default: 100 */
11752
+ fadeDistance: () => uniform(100),
11753
+ /** Fade strength, default: 1 */
11754
+ fadeStrength: () => uniform(1),
11755
+ /** Fade from camera (1) or origin (0), default: 1 */
11756
+ fadeFrom: () => uniform(1),
11757
+ /** Cell thickness, default: 0.5 */
11758
+ cellThickness: () => uniform(0.5),
11759
+ /** Section thickness, default: 1 */
11760
+ sectionThickness: () => uniform(1),
11761
+ /** Cell color, default: black */
11762
+ cellColor: () => uniform(new THREE.Color("#000000")),
11763
+ /** Section color, default: #2080ff */
11764
+ sectionColor: () => uniform(new THREE.Color("#2080ff")),
11765
+ /** Display the grid infinitely */
11766
+ infiniteGrid: () => uniform(false),
11767
+ /** Follow camera position */
11768
+ followCamera: () => uniform(false),
11769
+ /** Camera projection position on plane (set by component) */
11770
+ worldCamProjPosition: () => uniform(new THREE.Vector3()),
11771
+ /** Plane world position (set by component) */
11772
+ worldPlanePosition: () => uniform(new THREE.Vector3())
11773
+ }) {
12355
11774
  /** Type flag for identification */
12356
11775
  isGridMaterial = true;
12357
11776
  constructor() {
12358
11777
  super();
12359
- this._cellSize = uniform(0.5);
12360
- this._sectionSize = uniform(1);
12361
- this._fadeDistance = uniform(100);
12362
- this._fadeStrength = uniform(1);
12363
- this._fadeFrom = uniform(1);
12364
- this._cellThickness = uniform(0.5);
12365
- this._sectionThickness = uniform(1);
12366
- this._cellColor = uniform(new THREE.Color("#000000"));
12367
- this._sectionColor = uniform(new THREE.Color("#2080ff"));
12368
- this._infiniteGrid = uniform(0);
12369
- this._followCamera = uniform(0);
12370
- this._worldCamProjPosition = uniform(new THREE.Vector3());
12371
- this._worldPlanePosition = uniform(new THREE.Vector3());
12372
11778
  this.transparent = true;
12373
11779
  this.side = THREE.BackSide;
12374
11780
  this._buildGridShader();
12375
11781
  }
12376
11782
  _buildGridShader() {
12377
- const cellSizeUniform = this._cellSize;
12378
- const sectionSizeUniform = this._sectionSize;
12379
- const fadeDistanceUniform = this._fadeDistance;
12380
- const fadeStrengthUniform = this._fadeStrength;
12381
- const fadeFromUniform = this._fadeFrom;
12382
- const cellThicknessUniform = this._cellThickness;
12383
- const sectionThicknessUniform = this._sectionThickness;
12384
- const cellColorUniform = this._cellColor;
12385
- const sectionColorUniform = this._sectionColor;
12386
- const infiniteGridUniform = this._infiniteGrid;
12387
- const followCameraUniform = this._followCamera;
12388
- const worldCamProjPositionUniform = this._worldCamProjPosition;
12389
- const worldPlanePositionUniform = this._worldPlanePosition;
11783
+ const {
11784
+ cellSize: cellSizeUniform,
11785
+ sectionSize: sectionSizeUniform,
11786
+ fadeDistance: fadeDistanceUniform,
11787
+ fadeStrength: fadeStrengthUniform,
11788
+ fadeFrom: fadeFromUniform,
11789
+ cellThickness: cellThicknessUniform,
11790
+ sectionThickness: sectionThicknessUniform,
11791
+ cellColor: cellColorUniform,
11792
+ sectionColor: sectionColorUniform,
11793
+ infiniteGrid: infiniteGridUniform,
11794
+ followCamera: followCameraUniform,
11795
+ worldCamProjPosition: worldCamProjPositionUniform,
11796
+ worldPlanePosition: worldPlanePositionUniform
11797
+ } = this.uniforms;
12390
11798
  const computeLocalPosition = Fn(() => {
12391
11799
  const pos = positionGeometry;
12392
11800
  let localPos = vec3(pos.x, pos.z, pos.y).toVar();
12393
- const isInfinite = infiniteGridUniform.greaterThan(0.5);
12394
11801
  const scaleFactor = float(1).add(fadeDistanceUniform);
12395
- localPos.assign(select(isInfinite, localPos.mul(scaleFactor), localPos));
12396
- const isFollowCamera = followCameraUniform.greaterThan(0.5);
11802
+ localPos.assign(select(infiniteGridUniform, localPos.mul(scaleFactor), localPos));
12397
11803
  const cameraOffset = worldCamProjPositionUniform.sub(worldPlanePositionUniform);
12398
- localPos.assign(select(isFollowCamera, localPos.add(cameraOffset), localPos));
11804
+ localPos.assign(select(followCameraUniform, localPos.add(cameraOffset), localPos));
12399
11805
  return localPos;
12400
11806
  });
12401
11807
  const vLocalPosition = varying(computeLocalPosition(), "vLocalPosition");
@@ -12407,12 +11813,10 @@ class GridMaterialImpl extends MeshBasicNodeMaterial {
12407
11813
  this.positionNode = Fn(() => {
12408
11814
  const pos = positionGeometry;
12409
11815
  let localPos = vec3(pos.x, pos.z, pos.y).toVar();
12410
- const isInfinite = infiniteGridUniform.greaterThan(0.5);
12411
11816
  const scaleFactor = float(1).add(fadeDistanceUniform);
12412
- localPos.assign(select(isInfinite, localPos.mul(scaleFactor), localPos));
12413
- const isFollowCamera = followCameraUniform.greaterThan(0.5);
11817
+ localPos.assign(select(infiniteGridUniform, localPos.mul(scaleFactor), localPos));
12414
11818
  const cameraOffset = worldCamProjPositionUniform.sub(worldPlanePositionUniform);
12415
- localPos.assign(select(isFollowCamera, localPos.add(cameraOffset), localPos));
11819
+ localPos.assign(select(followCameraUniform, localPos.add(cameraOffset), localPos));
12416
11820
  return localPos;
12417
11821
  })();
12418
11822
  const getGrid = Fn((inputs) => {
@@ -12438,100 +11842,6 @@ class GridMaterialImpl extends MeshBasicNodeMaterial {
12438
11842
  return vec4(gridColor, finalAlpha);
12439
11843
  })();
12440
11844
  }
12441
- //* Uniform Accessors ==============================
12442
- /** Cell size, default: 0.5 */
12443
- get cellSize() {
12444
- return this._cellSize.value;
12445
- }
12446
- set cellSize(v) {
12447
- this._cellSize.value = v;
12448
- }
12449
- /** Section size, default: 1 */
12450
- get sectionSize() {
12451
- return this._sectionSize.value;
12452
- }
12453
- set sectionSize(v) {
12454
- this._sectionSize.value = v;
12455
- }
12456
- /** Fade distance, default: 100 */
12457
- get fadeDistance() {
12458
- return this._fadeDistance.value;
12459
- }
12460
- set fadeDistance(v) {
12461
- this._fadeDistance.value = v;
12462
- }
12463
- /** Fade strength, default: 1 */
12464
- get fadeStrength() {
12465
- return this._fadeStrength.value;
12466
- }
12467
- set fadeStrength(v) {
12468
- this._fadeStrength.value = v;
12469
- }
12470
- /** Fade from camera (1) or origin (0), default: 1 */
12471
- get fadeFrom() {
12472
- return this._fadeFrom.value;
12473
- }
12474
- set fadeFrom(v) {
12475
- this._fadeFrom.value = v;
12476
- }
12477
- /** Cell thickness, default: 0.5 */
12478
- get cellThickness() {
12479
- return this._cellThickness.value;
12480
- }
12481
- set cellThickness(v) {
12482
- this._cellThickness.value = v;
12483
- }
12484
- /** Section thickness, default: 1 */
12485
- get sectionThickness() {
12486
- return this._sectionThickness.value;
12487
- }
12488
- set sectionThickness(v) {
12489
- this._sectionThickness.value = v;
12490
- }
12491
- /** Cell color */
12492
- get cellColor() {
12493
- return this._cellColor.value;
12494
- }
12495
- set cellColor(v) {
12496
- if (v instanceof THREE.Color) this._cellColor.value = v;
12497
- else this._cellColor.value = new THREE.Color(v);
12498
- }
12499
- /** Section color */
12500
- get sectionColor() {
12501
- return this._sectionColor.value;
12502
- }
12503
- set sectionColor(v) {
12504
- if (v instanceof THREE.Color) this._sectionColor.value = v;
12505
- else this._sectionColor.value = new THREE.Color(v);
12506
- }
12507
- /** Display the grid infinitely */
12508
- get infiniteGrid() {
12509
- return this._infiniteGrid.value > 0.5;
12510
- }
12511
- set infiniteGrid(v) {
12512
- this._infiniteGrid.value = v ? 1 : 0;
12513
- }
12514
- /** Follow camera position */
12515
- get followCamera() {
12516
- return this._followCamera.value > 0.5;
12517
- }
12518
- set followCamera(v) {
12519
- this._followCamera.value = v ? 1 : 0;
12520
- }
12521
- /** Camera projection position on plane (set by component) */
12522
- get worldCamProjPosition() {
12523
- return this._worldCamProjPosition.value;
12524
- }
12525
- set worldCamProjPosition(v) {
12526
- this._worldCamProjPosition.value = v;
12527
- }
12528
- /** Plane world position (set by component) */
12529
- get worldPlanePosition() {
12530
- return this._worldPlanePosition.value;
12531
- }
12532
- set worldPlanePosition(v) {
12533
- this._worldPlanePosition.value = v;
12534
- }
12535
11845
  }
12536
11846
  const Grid = /* @__PURE__ */ React.forwardRef(
12537
11847
  ({
@@ -12937,4 +12247,181 @@ const Sparkles = /* @__PURE__ */ React.forwardRef(({ noise = 1, count = 100, spe
12937
12247
  );
12938
12248
  });
12939
12249
 
12940
- export { AccumulativeShadows, AdaptiveDpr, AdaptiveEvents, ArcballControls, BBAnchor, Backdrop, BakeShadows, Billboard, Bounds, Box, CameraShake, Capsule, CatmullRomLine, Caustics, CausticsMaterial, CausticsNormalMaterial, CausticsProjectionMaterial, Center, Circle, Clone, Cloud, CloudInstance, Clouds, ComputedAttribute, Cone, ContactShadows, ConvolutionMaterial, CubeCamera, CubeTexture, CubicBezierLine, CurveModifier, CycleRaycast, Cylinder, Decal, Detailed, DetectGPU, DeviceOrientationControls, DiscardMaterial, Dodecahedron, DragControls, Edges, Environment, EnvironmentCube, EnvironmentMap, EnvironmentPortal, Example, Extrude, FakeCloudMaterial, Fbo, Fbx, FirstPersonControls, Fisheye, Float, FlyControls, GizmoHelper, GizmoViewcube, GizmoViewport, Gltf, GradientTexture, GradientType, Grid, Helper, Html, HtmlMaterial, Hud, Icosahedron, Image, Instance, InstancedAttribute, Instances, IsObject, KeyboardControls, Ktx2, Lathe, Lightformer, Line, Loader, MapControls, Mask, MatcapTexture, Merged, MeshDistortMaterial, MeshPortalMaterial, MeshReflectorMaterial, MeshReflectorMaterial$1 as MeshReflectorMaterialImpl, MeshRefractionMaterial, MeshTransmissionMaterial, MeshWobbleMaterial, MotionPathControls, NormalTexture, Octahedron, OrbitControls, OrthographicCamera, Outlines, PCSSShadowNode, PerformanceMonitor, PerspectiveCamera, PivotControls, Plane, Point, PointMaterial, PointMaterialImpl, PointerLockControls, Points, PointsBuffer, Polyhedron, PositionMesh, PositionPoint, PositionalAudio, Preload, PresentationControls, Progress, QuadraticBezierLine, RandomizedLight, RenderCubeTexture, RenderTexture, Resize, Ring, RoundedBox, RoundedBoxGeometry, Sampler, ScreenQuad, ScreenSizer, ScreenSpace, ScreenVideoTexture, Scroll, ScrollControls, Segment, SegmentObject, Segments, Select, Shadow, Shape, Sky, SoftShadows, Sparkles, SparklesMaterial, Sphere, SpotLight, SpotLightMaterial, SpriteAnimator, Stage, StarfieldMaterial, Stars, Stats, StatsGl, Svg, Tetrahedron, Text3D, Texture, Torus, TorusKnot, TrackballControls, Trail, TrailTexture, TransformControls, Tube, VideoTexture, View, WebcamVideoTexture, Wireframe, WireframeMaterial, WireframeMaterialShaders, accumulativeContext, calcPosFromAngles, checkIfFrameIsEmpty, createInstances, getFirstFrame, meshBounds, presetsObj, setBarycentricCoordinates, setWireframeOverride, useAnimations, useAspect, useBounds, useContextBridge, useCubeCamera, useCubeTexture, useCursor, useCustomRaycast, useDepthBuffer, useDetectGPU, useEnvironment, useFBO, useFBX, useFont, useGLTF, useGizmoContext, useHelper, useIntersect, useKTX2, useKeyboardControls, useMask, useMatcapTexture, useMotion, useNormalTexture, usePerformanceMonitor, useProgress, useScroll, useSelect, useSpriteAnimator, useSpriteLoader, useSurfaceSampler, useTexture, useTrail, useTrailTexture, useVariants, useVideoTexture, useWireframeUniforms };
12250
+ if (typeof window !== "undefined") {
12251
+ await import('three/examples/jsm/inspector/tabs/Settings.js');
12252
+ }
12253
+ const InspectorContext = React.createContext(void 0);
12254
+ function useInspectorContext() {
12255
+ const inspector = React.useContext(InspectorContext);
12256
+ if (inspector === void 0) throw new Error("useInspectorControls must be used inside <Inspector>.");
12257
+ return inspector;
12258
+ }
12259
+ function Inspector({ children, hide = false }) {
12260
+ const renderer = useThree((state) => state.renderer);
12261
+ const isLegacy = useThree((state) => state.isLegacy);
12262
+ const frameloop = useThree((state) => state.frameloop);
12263
+ const eventSource = useThree((state) => state.events.connected);
12264
+ const invalidate = useThree((state) => state.invalidate);
12265
+ const [inspector, setInspector] = React.useState(null);
12266
+ const [error, setError] = React.useState(null);
12267
+ const session = React.useRef(null);
12268
+ const hidden = React.useRef(hide);
12269
+ hidden.current = hide;
12270
+ React.useEffect(() => {
12271
+ if (isLegacy) {
12272
+ setError(new Error("Inspector requires an R3F v10 WebGPU renderer. Use <Canvas renderer={true}>."));
12273
+ return;
12274
+ }
12275
+ if (frameloop !== "always") {
12276
+ setError(
12277
+ new Error('Inspector currently requires frameloop="always". Demand and manual rendering are not supported.')
12278
+ );
12279
+ return;
12280
+ }
12281
+ const controller = new AbortController();
12282
+ let current;
12283
+ Promise.all([import('./chunks/inspector-runtime.mjs'), renderer.init()]).then(async ([{ attachInspector }]) => {
12284
+ if (controller.signal.aborted) return;
12285
+ current = await attachInspector(renderer, eventSource, controller.signal);
12286
+ if (controller.signal.aborted) {
12287
+ current.release();
12288
+ return;
12289
+ }
12290
+ current.inspector.domElement.style.display = hidden.current ? "none" : "";
12291
+ session.current = current;
12292
+ setInspector(current.inspector);
12293
+ invalidate();
12294
+ }).catch((reason) => {
12295
+ if (!controller.signal.aborted) setError(reason instanceof Error ? reason : new Error(String(reason)));
12296
+ });
12297
+ return () => {
12298
+ controller.abort();
12299
+ session.current = null;
12300
+ setInspector(null);
12301
+ current?.release();
12302
+ };
12303
+ }, [renderer, isLegacy, frameloop, eventSource, invalidate]);
12304
+ React.useEffect(() => {
12305
+ if (inspector) inspector.domElement.style.display = hide ? "none" : "";
12306
+ }, [inspector, hide]);
12307
+ useFrame(() => session.current?.begin(), { phase: "start", priority: Infinity });
12308
+ useFrame(() => session.current?.finish(), { phase: "finish", priority: -Infinity });
12309
+ if (error) throw error;
12310
+ return /* @__PURE__ */ React.createElement(InspectorContext.Provider, { value: inspector }, children);
12311
+ }
12312
+
12313
+ function isFolder(entry) {
12314
+ return entry.schema !== void 0;
12315
+ }
12316
+ function isColorControl({ color, value }) {
12317
+ return color === true || value?.isColor === true || typeof value === "string" && /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i.test(value);
12318
+ }
12319
+ function readValue(control, value = control.value) {
12320
+ if (isColorControl(control) && typeof control.value === "string") {
12321
+ if (typeof value === "number") return "#" + value.toString(16).padStart(6, "0");
12322
+ if (typeof value === "string") {
12323
+ return value.length === 4 ? "#" + [...value.slice(1)].map((c) => c + c).join("") : value.toLowerCase();
12324
+ }
12325
+ }
12326
+ return value?.isColor ? value.clone() : value;
12327
+ }
12328
+ function buildInitialValues(schema) {
12329
+ return Object.fromEntries(
12330
+ Object.entries(schema).map(([key, entry]) => [
12331
+ key,
12332
+ isFolder(entry) ? buildInitialValues(entry.schema) : readValue(entry)
12333
+ ])
12334
+ );
12335
+ }
12336
+ function setIn(obj, path, value) {
12337
+ const [head, ...rest] = path;
12338
+ return {
12339
+ ...obj,
12340
+ [head]: rest.length === 0 ? value : setIn(obj[head], rest, value)
12341
+ };
12342
+ }
12343
+ function useInspectorControls(schema, options = {}) {
12344
+ const inspector = useInspectorContext();
12345
+ const invalidate = useThree((state) => state.invalidate);
12346
+ const { title, collapsed } = options;
12347
+ const schemaRef = useRef(schema);
12348
+ schemaRef.current = schema;
12349
+ const [values, setValues] = useState(() => buildInitialValues(schema));
12350
+ const valuesRef = useRef(values);
12351
+ const rootRef = useRef(null);
12352
+ useEffect(() => {
12353
+ if (!inspector) return;
12354
+ const parameters = inspector.parameters;
12355
+ const root = parameters.createGroup(title ?? "Controls");
12356
+ rootRef.current = root;
12357
+ const editors = [];
12358
+ let active = true;
12359
+ function populate(group, currentSchema, currentValues, path) {
12360
+ for (const key of Object.keys(currentSchema)) {
12361
+ const entry = currentSchema[key];
12362
+ if (isFolder(entry)) {
12363
+ const folder = group.addFolder(entry.label ?? key);
12364
+ populate(folder, entry.schema, currentValues[key], [...path, key]);
12365
+ if (entry.collapsed) folder.close();
12366
+ continue;
12367
+ }
12368
+ const control = { ...entry, value: currentValues[key] };
12369
+ const backing = { [key]: readValue(control) };
12370
+ let editor;
12371
+ if (control.options !== void 0) {
12372
+ editor = group.add(backing, key, control.options);
12373
+ } else if (isColorControl(control)) {
12374
+ editor = group.addColor(backing, key);
12375
+ } else if (typeof control.value === "number") {
12376
+ if (control.min !== void 0 && control.max !== void 0) {
12377
+ editor = group.add(backing, key, control.min, control.max, control.step);
12378
+ } else {
12379
+ const number = group.addNumber(backing, key, control.min, control.max);
12380
+ if (control.step !== void 0) number.input.step = String(control.step);
12381
+ editor = number;
12382
+ }
12383
+ } else {
12384
+ editor = group.add(backing, key);
12385
+ }
12386
+ editor.name(control.label ?? key);
12387
+ editors.push(editor);
12388
+ const fullPath = [...path, key];
12389
+ editor.onChange((value) => {
12390
+ if (!active) return;
12391
+ const next = readValue(entry, value);
12392
+ valuesRef.current = setIn(valuesRef.current, fullPath, next);
12393
+ setValues(valuesRef.current);
12394
+ invalidate();
12395
+ let node = schemaRef.current;
12396
+ for (let i = 0; i < fullPath.length - 1 && node; i++) {
12397
+ node = node[fullPath[i]]?.schema;
12398
+ }
12399
+ node?.[key]?.onChange?.(next);
12400
+ });
12401
+ }
12402
+ }
12403
+ populate(root, schemaRef.current, valuesRef.current, []);
12404
+ if (collapsed) root.close();
12405
+ parameters.show();
12406
+ return () => {
12407
+ active = false;
12408
+ rootRef.current = null;
12409
+ for (const editor of editors) editor.onChange(() => {
12410
+ });
12411
+ parameters.paramList.remove(root.paramList);
12412
+ const index = parameters.groups.indexOf(root);
12413
+ if (index !== -1) parameters.groups.splice(index, 1);
12414
+ };
12415
+ }, [inspector, invalidate]);
12416
+ useEffect(() => {
12417
+ rootRef.current?.name(title ?? "Controls");
12418
+ }, [title, inspector]);
12419
+ useEffect(() => {
12420
+ const root = rootRef.current;
12421
+ if (collapsed) root?.close();
12422
+ else if (root && !root.paramList.isOpen) root.paramList.toggle();
12423
+ }, [collapsed, inspector]);
12424
+ return values;
12425
+ }
12426
+
12427
+ export { AccumulativeShadows, AdaptiveDpr, AdaptiveEvents, ArcballControls, BBAnchor, Backdrop, BakeShadows, Billboard, BlurPass, Bounds, Box, CameraShake, Capsule, CatmullRomLine, Caustics, CausticsMaterial, CausticsNormalMaterial, CausticsProjectionMaterial, Center, Circle, Clone, Cloud, CloudInstance, Clouds, ComputedAttribute, Cone, ContactShadows, ConvolutionMaterial, CubeCamera, CubeTexture, CubicBezierLine, CurveModifier, CycleRaycast, Cylinder, Decal, Detailed, DetectGPU, DeviceOrientationControls, DiscardMaterial, DiscardNodeMaterial, Dodecahedron, DragControls, Edges, Environment, EnvironmentCube, EnvironmentMap, EnvironmentPortal, Example, Extrude, FakeCloudMaterial, Fbo, Fbx, FirstPersonControls, Fisheye, Float, FlyControls, GizmoHelper, GizmoViewcube, GizmoViewport, Gltf, GradientTexture, GradientType, Grid, Helper, Html, HtmlMaterial, Hud, Icosahedron, Image, Inspector, Instance, InstancedAttribute, Instances, IsObject, KeyboardControls, Ktx2, Lathe, Lightformer, Line, Loader, MapControls, Mask, MatcapTexture, Merged, MeshDiscardMaterial, MeshDistortMaterial, MeshPortalMaterial, MeshReflectorMaterial, MeshReflectorMaterial$1 as MeshReflectorMaterialImpl, MeshRefractionMaterial, MeshTransmissionMaterial, MeshWobbleMaterial, MotionPathControls, NormalTexture, Octahedron, OrbitControls, OrthographicCamera, Outlines, PCSSShadowNode, PerformanceMonitor, PerspectiveCamera, PivotControls, Plane, Point, PointMaterial, PointMaterialImpl, PointerLockControls, Points, PointsBuffer, Polyhedron, PositionMesh, PositionPoint, PositionalAudio, Preload, PresentationControls, Progress, QuadraticBezierLine, RandomizedLight, RenderCubeTexture, RenderTexture, Resize, Ring, RoundedBox, RoundedBoxGeometry, Sampler, ScreenQuad, ScreenSizer, ScreenSpace, ScreenVideoTexture, Scroll, ScrollControls, Segment, SegmentObject, Segments, Select, Shadow, Shape, Sky, SoftShadows, Sparkles, SparklesMaterial, Sphere, SpotLight, SpotLightMaterial, SpriteAnimator, Stage, StarfieldMaterial, Stars, Stats, StatsGl, Svg, Tetrahedron, Text3D, Texture, Torus, TorusKnot, TrackballControls, Trail, TrailTexture, TransformControls, Tube, VideoTexture, View, WebcamVideoTexture, Wireframe, WireframeMaterial, WireframeMaterialImpl, WireframeMaterialShaders, accumulativeContext, calcPosFromAngles, checkIfFrameIsEmpty, createInstances, getFirstFrame, meshBounds, presetsObj, setBarycentricCoordinates, setWireframeOverride, useAnimations, useAspect, useBounds, useContextBridge, useCubeCamera, useCubeTexture, useCursor, useCustomRaycast, useDepthBuffer, useDetectGPU, useEnvironment, useFBO, useFBX, useFont, useGLTF, useGizmoContext, useHelper, useInspectorControls, useIntersect, useKTX2, useKeyboardControls, useMask, useMatcapTexture, useMotion, useNormalTexture, usePerformanceMonitor, useProgress, useScroll, useSelect, useSpriteAnimator, useSpriteLoader, useSurfaceSampler, useTexture, useTrail, useTrailTexture, useVariants, useVideoTexture, useWireframeUniforms, withUniforms };