@react-three/drei 11.0.0-alpha.3 → 11.0.0-alpha.5

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
@@ -2,11 +2,10 @@ import * as React from 'react';
2
2
  import { forwardRef, useImperativeHandle, useMemo, useEffect, useCallback, useRef, useLayoutEffect, useState, Fragment, createContext, useContext } from 'react';
3
3
  import { useThree, useFrame, extend, createPortal, applyProps, useInstanceHandle, context as context$a, useLoader, addEffect, addAfterEffect } from '@react-three/fiber';
4
4
  import * as THREE from 'three/webgpu';
5
- import { MeshBasicNodeMaterial, REVISION, RenderTarget, MeshNormalNodeMaterial, QuadMesh, MeshPhysicalNodeMaterial, Frustum, Matrix4, Line2NodeMaterial, Vector3, Vector4, Vector2, Color, InstancedInterleavedBuffer, InterleavedBufferAttribute, Object3D, WebGLCubeRenderTarget, HalfFloatType, CubeCamera as CubeCamera$1, MathUtils, TextureLoader, Texture as Texture$1, MeshLambertMaterial, Quaternion, DynamicDrawUsage, CatmullRomCurve3, CubicBezierCurve3, QuadraticBezierCurve3, Shape as Shape$1, CanvasTexture, DoubleSide, PerspectiveCamera as PerspectiveCamera$1, OrthographicCamera as OrthographicCamera$1, AudioListener, AudioLoader, AnimationMixer, BufferAttribute, Box3, Sphere as Sphere$1, InstancedBufferAttribute, DepthTexture, DepthFormat, UnsignedShortType, CubeTextureLoader, DefaultLoadingManager, Ray, Raycaster, Camera, CubeReflectionMapping, EquirectangularReflectionMapping, Scene, RepeatWrapping, WebGLRenderTarget, LinearFilter, BufferGeometry, Mesh, SpriteNodeMaterial, AdditiveBlending, CylinderGeometry } from 'three/webgpu';
5
+ import { MeshBasicNodeMaterial, RenderTarget, MeshNormalNodeMaterial, QuadMesh, MeshPhysicalNodeMaterial, Frustum, Matrix4, Line2NodeMaterial, Vector3, Vector4, Vector2, Color, InstancedInterleavedBuffer, InterleavedBufferAttribute, Object3D, WebGLCubeRenderTarget, HalfFloatType, CubeCamera as CubeCamera$1, EventDispatcher, Euler, Quaternion, MathUtils, TextureLoader, Texture as Texture$1, MeshLambertMaterial, DynamicDrawUsage, CatmullRomCurve3, CubicBezierCurve3, QuadraticBezierCurve3, Shape as Shape$1, CanvasTexture, DoubleSide, PerspectiveCamera as PerspectiveCamera$1, OrthographicCamera as OrthographicCamera$1, REVISION, AudioListener, AudioLoader, AnimationMixer, BufferAttribute, Box3, Sphere as Sphere$1, InstancedBufferAttribute, DepthTexture, DepthFormat, UnsignedShortType, CubeTextureLoader, DefaultLoadingManager, Ray, Raycaster, Camera, CubeReflectionMapping, EquirectangularReflectionMapping, Scene, RepeatWrapping, SpriteNodeMaterial, AdditiveBlending, CylinderGeometry } from 'three/webgpu';
6
6
  import { ArcballControls as ArcballControls$1 } from 'three/examples/jsm/controls/ArcballControls.js';
7
- import { DeviceOrientationControls as DeviceOrientationControls$1 } from 'three-stdlib';
8
7
  import { useGesture } from '@use-gesture/react';
9
- import { FirstPersonControls as FirstPersonControls$1 } from 'three/examples/jsm/controls/FirstPersonControls';
8
+ import { FirstPersonControls as FirstPersonControls$1 } from 'three/examples/jsm/controls/FirstPersonControls.js';
10
9
  import { FlyControls as FlyControls$1 } from 'three/examples/jsm/controls/FlyControls.js';
11
10
  import { create as create$1 } from 'zustand';
12
11
  import { subscribeWithSelector } from 'zustand/middleware';
@@ -17,38 +16,38 @@ import { PointerLockControls as PointerLockControls$1 } from 'three/examples/jsm
17
16
  import * as ReactDOM from 'react-dom/client';
18
17
  import { TrackballControls as TrackballControls$1 } from 'three/examples/jsm/controls/TrackballControls.js';
19
18
  import { SimplexNoise } from 'three/examples/jsm/math/SimplexNoise.js';
20
- import { uniform, Fn, float, vec4, uniformTexture, uv, vec2 as vec2$2, texture, positionWorld, mul, add, negate, dot, refract, div, sub, length, cross, select, max, vec3, normalize, normalWorld, Discard, screenCoordinate, cameraPosition, pow, mx_fractal_noise_vec3, int, Loop, cameraProjectionMatrix, cameraViewMatrix, diffuseColor, mix, fract, sin, log, exp, reflector, clamp, reflect, smoothstep, round, distance, positionLocal, mx_noise_vec3, cos, mat3, normalLocal, attribute, modelViewMatrix, varying, min, abs, saturate, positionView, frontFacing, fwidth, modelWorldMatrix, positionGeometry } from 'three/tsl';
19
+ import { uniform, Fn, float, vec4, uniformTexture, uv, vec2 as vec2$2, texture, positionWorld, mul, add, negate, dot, refract, div, sub, length, cross, select, max, vec3, normalize, normalWorld, Discard, screenCoordinate, cameraPosition, pow, mx_fractal_noise_vec3, int, Loop, cameraProjectionMatrix, cameraViewMatrix, diffuseColor, mix, fract, sin, log, exp, reflector, clamp, reflect, smoothstep, round, distance, positionLocal, mx_noise_vec3, cos, mat3, normalLocal, attribute, modelViewMatrix, varying, min, frontFacing, fwidth, abs, saturate, positionView, modelWorldMatrix, positionGeometry } from 'three/tsl';
21
20
  import { Line2 } from 'three/examples/jsm/lines/webgpu/Line2.js';
22
21
  import { LineSegments2 } from 'three/examples/jsm/lines/webgpu/LineSegments2.js';
23
22
  import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry.js';
24
23
  import { LineSegmentsGeometry } from 'three/examples/jsm/lines/LineSegmentsGeometry.js';
25
- import { Flow } from 'three/examples/jsm/modifiers/CurveModifier';
24
+ import { Flow } from 'three/examples/jsm/modifiers/CurveModifier.js';
26
25
  import { DecalGeometry } from 'three/examples/jsm/geometries/DecalGeometry.js';
27
- import { toCreasedNormals, mergeVertices } from 'three/examples/jsm/utils/BufferGeometryUtils';
28
- import { TextGeometry } from 'three/examples/jsm/geometries/TextGeometry';
29
- import { FontLoader } from 'three/examples/jsm/loaders/FontLoader';
26
+ import { toCreasedNormals, mergeVertices } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
27
+ import { TextGeometry } from 'three/examples/jsm/geometries/TextGeometry.js';
28
+ import { FontLoader } from 'three/examples/jsm/loaders/FontLoader.js';
30
29
  import { suspend, preload, clear as clear$1 } from 'suspend-react';
31
- import { TransformControls as TransformControls$1 } from 'three/examples/jsm/controls/TransformControls';
32
- import * as SkeletonUtils from 'three/examples/jsm/utils/SkeletonUtils';
30
+ import { TransformControls as TransformControls$1 } from 'three/examples/jsm/controls/TransformControls.js';
31
+ import * as SkeletonUtils from 'three/examples/jsm/utils/SkeletonUtils.js';
33
32
  import { MeshSurfaceSampler } from 'three/examples/jsm/math/MeshSurfaceSampler.js';
34
- import { SVGLoader } from 'three/examples/jsm/loaders/SVGLoader';
33
+ import { SVGLoader } from 'three/examples/jsm/loaders/SVGLoader.js';
35
34
  import { Events } from 'hls.js';
36
- import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader';
37
- import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
38
- import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
35
+ import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader.js';
36
+ import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
37
+ import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
39
38
  import { MeshoptDecoder } from 'three/addons/libs/meshopt_decoder.module.js';
40
- import { KTX2Loader } from 'three/examples/jsm/loaders/KTX2Loader';
39
+ import { KTX2Loader } from 'three/examples/jsm/loaders/KTX2Loader.js';
41
40
  import { getGPUTier } from 'detect-gpu';
42
41
  import StatsImpl from 'stats.js';
43
42
  import Stats$1 from 'stats-gl';
44
43
  import tunnel from 'tunnel-rat';
45
- import { GroundedSkybox } from 'three/examples/jsm/objects/GroundedSkybox';
46
- import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader';
47
- import { EXRLoader } from 'three/examples/jsm/loaders/EXRLoader';
44
+ import { GroundedSkybox } from 'three/examples/jsm/objects/GroundedSkybox.js';
45
+ import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js';
46
+ import { EXRLoader } from 'three/examples/jsm/loaders/EXRLoader.js';
48
47
  import { HDRJPGLoader, GainMapLoader } from '@monogrid/gainmap-js';
49
48
  import { Sky as Sky$1 } from 'three/examples/jsm/objects/Sky.js';
50
49
  import { SkyMesh } from 'three/examples/jsm/objects/SkyMesh.js';
51
- import { SelectionBox } from 'three/examples/jsm/interactive/SelectionBox';
50
+ import { SelectionBox } from 'three/examples/jsm/interactive/SelectionBox.js';
52
51
  import { shallow } from 'zustand/shallow';
53
52
  import { hashBlur } from 'three/examples/jsm/tsl/display/hashBlur.js';
54
53
 
@@ -239,318 +238,28 @@ const ContactShadows = /* @__PURE__ */ React.forwardRef(
239
238
  }
240
239
  );
241
240
 
242
- function shaderMaterial(uniforms, vertexShader, fragmentShader, onInit) {
243
- return class extends THREE.ShaderMaterial {
244
- static key = THREE.MathUtils.generateUUID();
245
- constructor(parameters) {
246
- super({ vertexShader, fragmentShader, ...parameters });
247
- for (const key in uniforms) {
248
- this.uniforms[key] = new THREE.Uniform(uniforms[key]);
249
- Object.defineProperty(this, key, {
250
- get() {
251
- return this.uniforms[key].value;
252
- },
253
- set(value) {
254
- this.uniforms[key].value = value;
255
- }
256
- });
257
- }
258
- this.uniforms = THREE.UniformsUtils.clone(this.uniforms);
259
- }
260
- };
261
- }
262
-
263
- const DiscardMaterial$1 = /* @__PURE__ */ shaderMaterial(
264
- {},
265
- "void main() { }",
266
- "void main() { gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); discard; }"
267
- );
268
-
269
- const getVersion = () => parseInt(REVISION.replace(/\D+/g, ""));
270
- const version = /* @__PURE__ */ getVersion();
271
-
272
- function isLight(object) {
273
- return object.isLight;
274
- }
275
- function isGeometry$1(object) {
276
- return !!object.geometry;
277
- }
278
241
  const accumulativeContext = /* @__PURE__ */ React.createContext(
279
242
  null
280
243
  );
281
- const SoftShadowMaterial = /* @__PURE__ */ shaderMaterial(
282
- {
283
- color: /* @__PURE__ */ new THREE.Color(),
284
- blend: 2,
285
- alphaTest: 0.75,
286
- opacity: 0,
287
- map: null
288
- },
289
- `varying vec2 vUv;
290
- void main() {
291
- gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.);
292
- vUv = uv;
293
- }`,
294
- `varying vec2 vUv;
295
- uniform sampler2D map;
296
- uniform vec3 color;
297
- uniform float opacity;
298
- uniform float alphaTest;
299
- uniform float blend;
300
- void main() {
301
- vec4 sampledDiffuseColor = texture2D(map, vUv);
302
- gl_FragColor = vec4(color * sampledDiffuseColor.r * blend, max(0.0, (1.0 - (sampledDiffuseColor.r + sampledDiffuseColor.g + sampledDiffuseColor.b) / alphaTest)) * opacity);
303
- #include <tonemapping_fragment>
304
- #include <${version >= 154 ? "colorspace_fragment" : "encodings_fragment"}>
305
- }`
306
- );
307
- const AccumulativeShadows = /* @__PURE__ */ React.forwardRef(
308
- ({
309
- children,
310
- temporal,
311
- frames = 40,
312
- limit = Infinity,
313
- blend = 20,
314
- scale = 10,
315
- opacity = 1,
316
- alphaTest = 0.75,
317
- color = "black",
318
- colorBlend = 2,
319
- resolution = 1024,
320
- toneMapped = true,
321
- ...props
322
- }, forwardRef) => {
323
- extend({ SoftShadowMaterial });
324
- const gl = useThree((state) => state.gl);
325
- const scene = useThree((state) => state.scene);
326
- const camera = useThree((state) => state.camera);
327
- const invalidate = useThree((state) => state.invalidate);
328
- const gPlane = React.useRef(
329
- null
330
- );
331
- const gLights = React.useRef(null);
332
- const [plm] = React.useState(() => new ProgressiveLightMap(gl, scene, resolution));
333
- React.useLayoutEffect(() => {
334
- plm.configure(gPlane.current);
335
- }, []);
336
- const api = React.useMemo(
337
- () => ({
338
- lights: /* @__PURE__ */ new Map(),
339
- temporal: !!temporal,
340
- frames: Math.max(2, frames),
341
- blend: Math.max(2, frames === Infinity ? blend : frames),
342
- count: 0,
343
- getMesh: () => gPlane.current,
344
- reset: () => {
345
- plm.clear();
346
- const material = gPlane.current.material;
347
- material.opacity = 0;
348
- material.alphaTest = 0;
349
- api.count = 0;
350
- },
351
- update: (frames2 = 1) => {
352
- const material = gPlane.current.material;
353
- if (!api.temporal) {
354
- material.opacity = opacity;
355
- material.alphaTest = alphaTest;
356
- } else {
357
- material.opacity = Math.min(opacity, material.opacity + opacity / api.blend);
358
- material.alphaTest = Math.min(alphaTest, material.alphaTest + alphaTest / api.blend);
359
- }
360
- gLights.current.visible = true;
361
- plm.prepare();
362
- for (let i = 0; i < frames2; i++) {
363
- api.lights.forEach((light) => light.update());
364
- plm.update(camera, api.blend);
365
- }
366
- gLights.current.visible = false;
367
- plm.finish();
368
- }
369
- }),
370
- [plm, camera, scene, temporal, frames, blend, opacity, alphaTest]
371
- );
372
- React.useLayoutEffect(() => {
373
- api.reset();
374
- if (!api.temporal && api.frames !== Infinity) api.update(api.blend);
375
- });
376
- React.useImperativeHandle(forwardRef, () => api, [api]);
377
- useFrame(() => {
378
- if ((api.temporal || api.frames === Infinity) && api.count < api.frames && api.count < limit) {
379
- invalidate();
380
- api.update();
381
- api.count++;
382
- }
383
- });
384
- return /* @__PURE__ */ React.createElement("group", { ...props }, /* @__PURE__ */ React.createElement("group", { traverse: () => null, ref: gLights }, /* @__PURE__ */ React.createElement(accumulativeContext.Provider, { value: api }, children)), /* @__PURE__ */ React.createElement("mesh", { receiveShadow: true, ref: gPlane, scale, rotation: [-Math.PI / 2, 0, 0] }, /* @__PURE__ */ React.createElement("planeGeometry", null), /* @__PURE__ */ React.createElement(
385
- "softShadowMaterial",
386
- {
387
- transparent: true,
388
- depthWrite: false,
389
- toneMapped,
390
- color,
391
- blend: colorBlend,
392
- map: plm.progressiveLightMap2.texture
393
- }
394
- )));
395
- }
396
- );
397
- const RandomizedLight = /* @__PURE__ */ React.forwardRef(
398
- ({
399
- castShadow = true,
400
- bias = 1e-3,
401
- mapSize = 512,
402
- size = 5,
403
- near = 0.5,
404
- far = 500,
405
- frames = 1,
406
- position = [0, 0, 0],
407
- radius = 1,
408
- amount = 8,
409
- intensity = version >= 155 ? Math.PI : 1,
410
- ambient = 0.5,
411
- ...props
412
- }, forwardRef) => {
413
- const gLights = React.useRef(null);
414
- const length = new THREE.Vector3(...position).length();
415
- const parent = React.useContext(accumulativeContext);
416
- const update = React.useCallback(() => {
417
- let light;
418
- if (gLights.current) {
419
- for (let l = 0; l < gLights.current.children.length; l++) {
420
- light = gLights.current.children[l];
421
- if (Math.random() > ambient) {
422
- light.position.set(
423
- position[0] + THREE.MathUtils.randFloatSpread(radius),
424
- position[1] + THREE.MathUtils.randFloatSpread(radius),
425
- position[2] + THREE.MathUtils.randFloatSpread(radius)
426
- );
427
- } else {
428
- let lambda = Math.acos(2 * Math.random() - 1) - Math.PI / 2;
429
- let phi = 2 * Math.PI * Math.random();
430
- light.position.set(
431
- Math.cos(lambda) * Math.cos(phi) * length,
432
- Math.abs(Math.cos(lambda) * Math.sin(phi) * length),
433
- Math.sin(lambda) * length
434
- );
435
- }
436
- }
437
- }
438
- }, [radius, ambient, length, ...position]);
439
- const api = React.useMemo(() => ({ update }), [update]);
440
- React.useImperativeHandle(forwardRef, () => api, [api]);
441
- React.useLayoutEffect(() => {
442
- const group = gLights.current;
443
- if (parent) parent.lights?.set(group.uuid, api);
444
- return () => void parent?.lights?.delete(group.uuid);
445
- }, [parent, api]);
446
- return /* @__PURE__ */ React.createElement("group", { ref: gLights, ...props }, Array.from({ length: amount }, (_, index) => /* @__PURE__ */ React.createElement(
447
- "directionalLight",
448
- {
449
- key: index,
450
- castShadow,
451
- "shadow-bias": bias,
452
- "shadow-mapSize": [mapSize, mapSize],
453
- intensity: intensity / amount
454
- },
455
- /* @__PURE__ */ React.createElement("orthographicCamera", { attach: "shadow-camera", args: [-size, size, size, -size, near, far] })
456
- )));
457
- }
458
- );
459
- class ProgressiveLightMap {
460
- renderer;
461
- res;
462
- scene;
463
- object;
464
- buffer1Active;
465
- progressiveLightMap1;
466
- progressiveLightMap2;
467
- discardMat;
468
- targetMat;
469
- previousShadowMap;
470
- averagingWindow;
471
- clearColor;
472
- clearAlpha;
473
- lights;
474
- meshes;
475
- constructor(renderer, scene, res = 1024) {
476
- this.renderer = renderer;
477
- this.res = res;
478
- this.scene = scene;
479
- this.buffer1Active = false;
480
- this.lights = [];
481
- this.meshes = [];
482
- this.object = null;
483
- this.clearColor = new THREE.Color();
484
- this.clearAlpha = 0;
485
- const textureParams = {
486
- type: THREE.HalfFloatType,
487
- magFilter: THREE.NearestFilter,
488
- minFilter: THREE.NearestFilter
489
- };
490
- this.progressiveLightMap1 = new THREE.WebGLRenderTarget(this.res, this.res, textureParams);
491
- this.progressiveLightMap2 = new THREE.WebGLRenderTarget(this.res, this.res, textureParams);
492
- this.discardMat = new DiscardMaterial$1();
493
- this.targetMat = new THREE.MeshLambertMaterial({ fog: false });
494
- this.previousShadowMap = { value: this.progressiveLightMap1.texture };
495
- this.averagingWindow = { value: 100 };
496
- this.targetMat.onBeforeCompile = (shader) => {
497
- shader.vertexShader = "varying vec2 vUv;\n" + shader.vertexShader.slice(0, -1) + "vUv = uv; gl_Position = vec4((uv - 0.5) * 2.0, 1.0, 1.0); }";
498
- const bodyStart = shader.fragmentShader.indexOf("void main() {");
499
- shader.fragmentShader = "varying vec2 vUv;\n" + shader.fragmentShader.slice(0, bodyStart) + "uniform sampler2D previousShadowMap;\n uniform float averagingWindow;\n" + shader.fragmentShader.slice(bodyStart - 1, -1) + `
500
- vec3 texelOld = texture2D(previousShadowMap, vUv).rgb;
501
- gl_FragColor.rgb = mix(texelOld, gl_FragColor.rgb, 1.0/ averagingWindow);
502
- }`;
503
- shader.uniforms.previousShadowMap = this.previousShadowMap;
504
- shader.uniforms.averagingWindow = this.averagingWindow;
505
- };
506
- }
507
- clear() {
508
- this.renderer.getClearColor(this.clearColor);
509
- this.clearAlpha = this.renderer.getClearAlpha();
510
- this.renderer.setClearColor("black", 1);
511
- this.renderer.setRenderTarget(this.progressiveLightMap1);
512
- this.renderer.clear();
513
- this.renderer.setRenderTarget(this.progressiveLightMap2);
514
- this.renderer.clear();
515
- this.renderer.setRenderTarget(null);
516
- this.renderer.setClearColor(this.clearColor, this.clearAlpha);
517
- this.lights = [];
518
- this.meshes = [];
519
- this.scene.traverse((object) => {
520
- if (isGeometry$1(object)) {
521
- this.meshes.push({ object, material: object.material });
522
- } else if (isLight(object)) {
523
- this.lights.push({ object, intensity: object.intensity });
524
- }
525
- });
526
- }
527
- prepare() {
528
- this.lights.forEach((light) => light.object.intensity = 0);
529
- this.meshes.forEach((mesh) => mesh.object.material = this.discardMat);
530
- }
531
- finish() {
532
- this.lights.forEach((light) => light.object.intensity = light.intensity);
533
- this.meshes.forEach((mesh) => mesh.object.material = mesh.material);
534
- }
535
- configure(object) {
536
- this.object = object;
537
- }
538
- update(camera, blendWindow = 100) {
539
- if (!this.object) return;
540
- this.averagingWindow.value = blendWindow;
541
- this.object.material = this.targetMat;
542
- const activeMap = this.buffer1Active ? this.progressiveLightMap1 : this.progressiveLightMap2;
543
- const inactiveMap = this.buffer1Active ? this.progressiveLightMap2 : this.progressiveLightMap1;
544
- const oldBg = this.scene.background;
545
- this.scene.background = null;
546
- this.renderer.setRenderTarget(activeMap);
547
- this.previousShadowMap.value = inactiveMap.texture;
548
- this.buffer1Active = !this.buffer1Active;
549
- this.renderer.render(this.scene, camera);
550
- this.renderer.setRenderTarget(null);
551
- this.scene.background = oldBg;
244
+ let hasWarned$1 = false;
245
+ function warnOnce$1(message) {
246
+ if (!hasWarned$1) {
247
+ console.warn(message);
248
+ hasWarned$1 = true;
552
249
  }
553
250
  }
251
+ const AccumulativeShadows = /* @__PURE__ */ React.forwardRef((props, ref) => {
252
+ warnOnce$1(
253
+ "drei: AccumulativeShadows is not yet supported in WebGPU. It uses GLSL shaders that need to be converted to TSL. Consider using ContactShadows instead, or import from @react-three/drei/legacy for WebGL."
254
+ );
255
+ return null;
256
+ });
257
+ const RandomizedLight = /* @__PURE__ */ React.forwardRef((props, ref) => {
258
+ warnOnce$1(
259
+ "drei: RandomizedLight is not yet supported in WebGPU. It requires AccumulativeShadows which uses GLSL shaders. Import from @react-three/drei/legacy for WebGL."
260
+ );
261
+ return null;
262
+ });
554
263
 
555
264
  function useFBO(width, height, settings) {
556
265
  const size = useThree((state) => state.size);
@@ -1477,10 +1186,11 @@ let MeshReflectorMaterial$1 = class MeshReflectorMaterial extends THREE.MeshStan
1477
1186
  premultipliedAlpha: false
1478
1187
  });
1479
1188
  const reflectionColor = mix(reflectionSample.rgb, reflectionBlurred.rgb, clamp(this.hasBlurUniform, 0, 1));
1189
+ const reflectionClamped = clamp(reflectionColor, 0, 1);
1480
1190
  const contrastAdjusted = vec3(
1481
- reflectionColor.x.sub(0.5).mul(this.mixContrastUniform).add(0.5),
1482
- reflectionColor.y.sub(0.5).mul(this.mixContrastUniform).add(0.5),
1483
- reflectionColor.z.sub(0.5).mul(this.mixContrastUniform).add(0.5)
1191
+ reflectionClamped.x.sub(0.5).mul(this.mixContrastUniform).add(0.5),
1192
+ reflectionClamped.y.sub(0.5).mul(this.mixContrastUniform).add(0.5),
1193
+ reflectionClamped.z.sub(0.5).mul(this.mixContrastUniform).add(0.5)
1484
1194
  );
1485
1195
  const mirrorClamped = clamp(this.mirrorUniform, 0, 1);
1486
1196
  return vec4(vec3(1).sub(vec3(mirrorClamped)).add(contrastAdjusted.mul(this.mixStrengthUniform)), 1);
@@ -3176,12 +2886,12 @@ const Segments = /* @__PURE__ */ React.forwardRef((props, forwardedRef) => {
3176
2886
  for (let i = 0; i < limit; i++) {
3177
2887
  const segment = segments[i]?.current;
3178
2888
  if (segment) {
3179
- positions[i * 6 + 0] = segment.start.x;
3180
- positions[i * 6 + 1] = segment.start.y;
3181
- positions[i * 6 + 2] = segment.start.z;
3182
- positions[i * 6 + 3] = segment.end.x;
3183
- positions[i * 6 + 4] = segment.end.y;
3184
- positions[i * 6 + 5] = segment.end.z;
2889
+ positions[i * 6 + 0] = segment.start?.x || 0;
2890
+ positions[i * 6 + 1] = segment.start?.y || 0;
2891
+ positions[i * 6 + 2] = segment.start?.z || 0;
2892
+ positions[i * 6 + 3] = segment.end?.x || 0;
2893
+ positions[i * 6 + 4] = segment.end?.y || 0;
2894
+ positions[i * 6 + 5] = segment.end?.z || 0;
3185
2895
  colors[i * 6 + 0] = segment.color.r;
3186
2896
  colors[i * 6 + 1] = segment.color.g;
3187
2897
  colors[i * 6 + 2] = segment.color.b;
@@ -3216,7 +2926,10 @@ class SegmentObject {
3216
2926
  this.end = new THREE.Vector3(0, 0, 0);
3217
2927
  }
3218
2928
  }
3219
- const normPos = (pos) => pos instanceof THREE.Vector3 ? pos : new THREE.Vector3(...typeof pos === "number" ? [pos, pos, pos] : pos);
2929
+ const normPos = (pos) => {
2930
+ if (!pos) return new THREE.Vector3(0, 0, 0);
2931
+ return pos instanceof THREE.Vector3 ? pos : new THREE.Vector3(...typeof pos === "number" ? [pos, pos, pos] : pos);
2932
+ };
3220
2933
  const Segment = /* @__PURE__ */ React.forwardRef(({ color, start, end }, forwardedRef) => {
3221
2934
  const api = React.useContext(context$9);
3222
2935
  if (!api) throw "Segment must used inside Segments component.";
@@ -3540,6 +3253,81 @@ const ArcballControls = /* @__PURE__ */ forwardRef(
3540
3253
  }
3541
3254
  );
3542
3255
 
3256
+ let DeviceOrientationControls$1 = class DeviceOrientationControls extends EventDispatcher {
3257
+ object;
3258
+ changeEvent = { type: "change" };
3259
+ EPS = 1e-6;
3260
+ enabled = true;
3261
+ deviceOrientation = { alpha: 0, beta: 0, gamma: 0 };
3262
+ screenOrientation = 0;
3263
+ alphaOffset = 0;
3264
+ // radians
3265
+ constructor(object) {
3266
+ super();
3267
+ this.object = object;
3268
+ this.object.rotation.reorder("YXZ");
3269
+ this.connect();
3270
+ }
3271
+ onDeviceOrientationChangeEvent = (event) => {
3272
+ this.deviceOrientation = event;
3273
+ };
3274
+ onScreenOrientationChangeEvent = () => {
3275
+ this.screenOrientation = window.orientation || 0;
3276
+ };
3277
+ // The angles alpha, beta and gamma form a set of intrinsic Tait-Bryan angles of type Z-X'-Y''
3278
+ zee = new Vector3(0, 0, 1);
3279
+ euler = new Euler();
3280
+ q0 = new Quaternion();
3281
+ q1 = new Quaternion(-Math.sqrt(0.5), 0, 0, Math.sqrt(0.5));
3282
+ // - PI/2 around the x-axis
3283
+ setObjectQuaternion = (quaternion, alpha, beta, gamma, orient) => {
3284
+ this.euler.set(beta, alpha, -gamma, "YXZ");
3285
+ quaternion.setFromEuler(this.euler);
3286
+ quaternion.multiply(this.q1);
3287
+ quaternion.multiply(this.q0.setFromAxisAngle(this.zee, -orient));
3288
+ };
3289
+ connect = () => {
3290
+ this.onScreenOrientationChangeEvent();
3291
+ if (window.DeviceOrientationEvent !== void 0 && // @ts-ignore
3292
+ typeof window.DeviceOrientationEvent.requestPermission === "function") {
3293
+ window.DeviceOrientationEvent.requestPermission().then((response) => {
3294
+ if (response == "granted") {
3295
+ window.addEventListener("orientationchange", this.onScreenOrientationChangeEvent);
3296
+ window.addEventListener("deviceorientation", this.onDeviceOrientationChangeEvent);
3297
+ }
3298
+ }).catch((error) => {
3299
+ console.error("THREE.DeviceOrientationControls: Unable to use DeviceOrientation API:", error);
3300
+ });
3301
+ } else {
3302
+ window.addEventListener("orientationchange", this.onScreenOrientationChangeEvent);
3303
+ window.addEventListener("deviceorientation", this.onDeviceOrientationChangeEvent);
3304
+ }
3305
+ this.enabled = true;
3306
+ };
3307
+ disconnect = () => {
3308
+ window.removeEventListener("orientationchange", this.onScreenOrientationChangeEvent);
3309
+ window.removeEventListener("deviceorientation", this.onDeviceOrientationChangeEvent);
3310
+ this.enabled = false;
3311
+ };
3312
+ lastQuaternion = new Quaternion();
3313
+ update = () => {
3314
+ if (this.enabled === false) return;
3315
+ const device = this.deviceOrientation;
3316
+ if (device) {
3317
+ const alpha = device.alpha ? MathUtils.degToRad(device.alpha) + this.alphaOffset : 0;
3318
+ const beta = device.beta ? MathUtils.degToRad(device.beta) : 0;
3319
+ const gamma = device.gamma ? MathUtils.degToRad(device.gamma) : 0;
3320
+ const orient = this.screenOrientation ? MathUtils.degToRad(this.screenOrientation) : 0;
3321
+ this.setObjectQuaternion(this.object.quaternion, alpha, beta, gamma, orient);
3322
+ if (8 * (1 - this.lastQuaternion.dot(this.object.quaternion)) > this.EPS) {
3323
+ this.lastQuaternion.copy(this.object.quaternion);
3324
+ this.dispatchEvent(this.changeEvent);
3325
+ }
3326
+ }
3327
+ };
3328
+ dispose = () => this.disconnect();
3329
+ };
3330
+
3543
3331
  const DeviceOrientationControls = /* @__PURE__ */ React.forwardRef(
3544
3332
  (props, ref) => {
3545
3333
  const { camera, onChange, makeDefault, ...rest } = props;
@@ -3609,8 +3397,9 @@ const DragControls = React.forwardRef(
3609
3397
  onDragStart && onDragStart(initialModelPosition);
3610
3398
  invalidate();
3611
3399
  },
3612
- onDrag: ({ xy: [dragX, dragY], intentional }) => {
3400
+ onDrag: ({ xy: [dragX, dragY], intentional, event }) => {
3613
3401
  if (!intentional) return;
3402
+ if (dragConfig && dragConfig.preventOverlap) event.preventDefault();
3614
3403
  const normalizedMouseX = (dragX - size.left) / size.width * 2 - 1;
3615
3404
  const normalizedMouseY = -((dragY - size.top) / size.height) * 2 + 1;
3616
3405
  mousePosition2D.set(normalizedMouseX, normalizedMouseY);
@@ -3779,12 +3568,21 @@ function KeyboardControls({ map, children, onChange, domElement }) {
3779
3568
  obj.pressed = false;
3780
3569
  if (up) fn(false);
3781
3570
  };
3571
+ const blurHandler = () => {
3572
+ const resetState = map.reduce((prev, cur) => ({ ...prev, [cur.name]: false }), {});
3573
+ set(resetState);
3574
+ Object.values(keyMap).forEach((obj) => {
3575
+ obj.pressed = false;
3576
+ });
3577
+ };
3782
3578
  const source = domElement || window;
3783
3579
  source.addEventListener("keydown", downHandler, { passive: true });
3784
3580
  source.addEventListener("keyup", upHandler, { passive: true });
3581
+ window.addEventListener("blur", blurHandler);
3785
3582
  return () => {
3786
3583
  source.removeEventListener("keydown", downHandler);
3787
3584
  source.removeEventListener("keyup", upHandler);
3585
+ window.removeEventListener("blur", blurHandler);
3788
3586
  };
3789
3587
  }, [domElement, key]);
3790
3588
  return /* @__PURE__ */ React.createElement(context$8.Provider, { value: api, children });
@@ -4135,7 +3933,13 @@ function PresentationControls({
4135
3933
  return /* @__PURE__ */ React.createElement("group", { ref, ...bind?.() }, children);
4136
3934
  }
4137
3935
 
3936
+ function roundToPixelRatio(value) {
3937
+ const ratio = window.devicePixelRatio || 1;
3938
+ return Math.round(value * ratio) / ratio;
3939
+ }
3940
+
4138
3941
  const context$7 = /* @__PURE__ */ React.createContext(null);
3942
+ const rootCache = /* @__PURE__ */ new WeakMap();
4139
3943
  function useScroll() {
4140
3944
  return React.useContext(context$7);
4141
3945
  }
@@ -4149,6 +3953,7 @@ function ScrollControls({
4149
3953
  damping = 0.25,
4150
3954
  maxSpeed = Infinity,
4151
3955
  prepend = false,
3956
+ pixelPerfect = true,
4152
3957
  style = {},
4153
3958
  children
4154
3959
  }) {
@@ -4170,6 +3975,7 @@ function ScrollControls({
4170
3975
  delta: 0,
4171
3976
  scroll,
4172
3977
  pages,
3978
+ pixelPerfect,
4173
3979
  // 0-1 for a range between from -> from + distance
4174
3980
  range(from, distance2, margin = 0) {
4175
3981
  const start = from - margin;
@@ -4188,7 +3994,7 @@ function ScrollControls({
4188
3994
  }
4189
3995
  };
4190
3996
  return state2;
4191
- }, [eps, damping, horizontal, pages]);
3997
+ }, [eps, damping, horizontal, pages, pixelPerfect]);
4192
3998
  React.useEffect(() => {
4193
3999
  el.style.position = "absolute";
4194
4000
  el.style.width = "100%";
@@ -4301,10 +4107,23 @@ const ScrollHtml = React.forwardRef(
4301
4107
  React.useImperativeHandle(ref, () => group.current, []);
4302
4108
  const { width, height } = useThree((state2) => state2.size);
4303
4109
  const fiberState = React.useContext(context$a);
4304
- const root = React.useMemo(() => ReactDOM.createRoot(state.fixed), [state.fixed]);
4110
+ const root = React.useMemo(() => {
4111
+ let root2 = rootCache.get(state.fixed);
4112
+ if (!root2) {
4113
+ root2 = ReactDOM.createRoot(state.fixed);
4114
+ rootCache.set(state.fixed, root2);
4115
+ }
4116
+ return root2;
4117
+ }, [state.fixed]);
4305
4118
  useFrame(() => {
4306
4119
  if (state.delta > state.eps) {
4307
- group.current.style.transform = `translate3d(${state.horizontal ? -width * (state.pages - 1) * state.offset : 0}px,${state.horizontal ? 0 : height * (state.pages - 1) * -state.offset}px,0)`;
4120
+ let x = state.horizontal ? -width * (state.pages - 1) * state.offset : 0;
4121
+ let y = state.horizontal ? 0 : height * (state.pages - 1) * -state.offset;
4122
+ if (state.pixelPerfect) {
4123
+ x = roundToPixelRatio(x);
4124
+ y = roundToPixelRatio(y);
4125
+ }
4126
+ group.current.style.transform = `translate3d(${x}px,${y}px,0)`;
4308
4127
  }
4309
4128
  });
4310
4129
  root.render(
@@ -5233,377 +5052,6 @@ const Text3D = /* @__PURE__ */ React.forwardRef(
5233
5052
  }
5234
5053
  );
5235
5054
 
5236
- const WireframeMaterialShaders$1 = {
5237
- uniforms: {
5238
- strokeOpacity: 1,
5239
- fillOpacity: 0.25,
5240
- fillMix: 0,
5241
- thickness: 0.05,
5242
- colorBackfaces: false,
5243
- dashInvert: true,
5244
- dash: false,
5245
- dashRepeats: 4,
5246
- dashLength: 0.5,
5247
- squeeze: false,
5248
- squeezeMin: 0.2,
5249
- squeezeMax: 1,
5250
- stroke: /* @__PURE__ */ new THREE.Color("#ff0000"),
5251
- backfaceStroke: /* @__PURE__ */ new THREE.Color("#0000ff"),
5252
- fill: /* @__PURE__ */ new THREE.Color("#00ff00")
5253
- },
5254
- vertex: (
5255
- /* glsl */
5256
- `
5257
- attribute vec3 barycentric;
5258
-
5259
- varying vec3 v_edges_Barycentric;
5260
- varying vec3 v_edges_Position;
5261
-
5262
- void initWireframe() {
5263
- v_edges_Barycentric = barycentric;
5264
- v_edges_Position = position.xyz;
5265
- }
5266
- `
5267
- ),
5268
- fragment: (
5269
- /* glsl */
5270
- `
5271
- #ifndef PI
5272
- #define PI 3.1415926535897932384626433832795
5273
- #endif
5274
-
5275
- varying vec3 v_edges_Barycentric;
5276
- varying vec3 v_edges_Position;
5277
-
5278
- uniform float strokeOpacity;
5279
- uniform float fillOpacity;
5280
- uniform float fillMix;
5281
- uniform float thickness;
5282
- uniform bool colorBackfaces;
5283
-
5284
- // Dash
5285
- uniform bool dashInvert;
5286
- uniform bool dash;
5287
- uniform bool dashOnly;
5288
- uniform float dashRepeats;
5289
- uniform float dashLength;
5290
-
5291
- // Squeeze
5292
- uniform bool squeeze;
5293
- uniform float squeezeMin;
5294
- uniform float squeezeMax;
5295
-
5296
- // Colors
5297
- uniform vec3 stroke;
5298
- uniform vec3 backfaceStroke;
5299
- uniform vec3 fill;
5300
-
5301
- // This is like
5302
- float wireframe_aastep(float threshold, float dist) {
5303
- float afwidth = fwidth(dist) * 0.5;
5304
- return smoothstep(threshold - afwidth, threshold + afwidth, dist);
5305
- }
5306
-
5307
- float wireframe_map(float value, float min1, float max1, float min2, float max2) {
5308
- return min2 + (value - min1) * (max2 - min2) / (max1 - min1);
5309
- }
5310
-
5311
- float getWireframe() {
5312
- vec3 barycentric = v_edges_Barycentric;
5313
-
5314
- // Distance from center of each triangle to its edges.
5315
- float d = min(min(barycentric.x, barycentric.y), barycentric.z);
5316
-
5317
- // for dashed rendering, we can use this to get the 0 .. 1 value of the line length
5318
- float positionAlong = max(barycentric.x, barycentric.y);
5319
- if (barycentric.y < barycentric.x && barycentric.y < barycentric.z) {
5320
- positionAlong = 1.0 - positionAlong;
5321
- }
5322
-
5323
- // the thickness of the stroke
5324
- float computedThickness = wireframe_map(thickness, 0.0, 1.0, 0.0, 0.34);
5325
-
5326
- // if we want to shrink the thickness toward the center of the line segment
5327
- if (squeeze) {
5328
- computedThickness *= mix(squeezeMin, squeezeMax, (1.0 - sin(positionAlong * PI)));
5329
- }
5330
-
5331
- // Create dash pattern
5332
- if (dash) {
5333
- // here we offset the stroke position depending on whether it
5334
- // should overlap or not
5335
- float offset = 1.0 / dashRepeats * dashLength / 2.0;
5336
- if (!dashInvert) {
5337
- offset += 1.0 / dashRepeats / 2.0;
5338
- }
5339
-
5340
- // if we should animate the dash or not
5341
- // if (dashAnimate) {
5342
- // offset += time * 0.22;
5343
- // }
5344
-
5345
- // create the repeating dash pattern
5346
- float pattern = fract((positionAlong + offset) * dashRepeats);
5347
- computedThickness *= 1.0 - wireframe_aastep(dashLength, pattern);
5348
- }
5349
-
5350
- // compute the anti-aliased stroke edge
5351
- float edge = 1.0 - wireframe_aastep(computedThickness, d);
5352
-
5353
- return edge;
5354
- }
5355
- `
5356
- )
5357
- };
5358
- const WireframeMaterial$1 = /* @__PURE__ */ shaderMaterial(
5359
- WireframeMaterialShaders$1.uniforms,
5360
- WireframeMaterialShaders$1.vertex + /* glsl */
5361
- `
5362
- void main() {
5363
- initWireframe();
5364
- gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
5365
- }
5366
- `,
5367
- WireframeMaterialShaders$1.fragment + /* glsl */
5368
- `
5369
- void main () {
5370
- // Compute color
5371
-
5372
- float edge = getWireframe();
5373
- vec4 colorStroke = vec4(stroke, edge);
5374
-
5375
- #ifdef FLIP_SIDED
5376
- colorStroke.rgb = backfaceStroke;
5377
- #endif
5378
-
5379
- vec4 colorFill = vec4(fill, fillOpacity);
5380
- vec4 outColor = mix(colorFill, colorStroke, edge * strokeOpacity);
5381
-
5382
- gl_FragColor = outColor;
5383
- }
5384
- `
5385
- );
5386
- function setWireframeOverride$1(material, uniforms) {
5387
- material.onBeforeCompile = (shader) => {
5388
- shader.uniforms = {
5389
- ...shader.uniforms,
5390
- ...uniforms
5391
- };
5392
- shader.vertexShader = shader.vertexShader.replace(
5393
- "void main() {",
5394
- `
5395
- ${WireframeMaterialShaders$1.vertex}
5396
- void main() {
5397
- initWireframe();
5398
- `
5399
- );
5400
- shader.fragmentShader = shader.fragmentShader.replace(
5401
- "void main() {",
5402
- `
5403
- ${WireframeMaterialShaders$1.fragment}
5404
- void main() {
5405
- `
5406
- );
5407
- shader.fragmentShader = shader.fragmentShader.replace(
5408
- "#include <color_fragment>",
5409
- /* glsl */
5410
- `
5411
- #include <color_fragment>
5412
- float edge = getWireframe();
5413
- vec4 colorStroke = vec4(stroke, edge);
5414
- #ifdef FLIP_SIDED
5415
- colorStroke.rgb = backfaceStroke;
5416
- #endif
5417
- vec4 colorFill = vec4(mix(diffuseColor.rgb, fill, fillMix), mix(diffuseColor.a, fillOpacity, fillMix));
5418
- vec4 outColor = mix(colorFill, colorStroke, edge * strokeOpacity);
5419
-
5420
- diffuseColor.rgb = outColor.rgb;
5421
- diffuseColor.a *= outColor.a;
5422
- `
5423
- );
5424
- };
5425
- material.side = THREE.DoubleSide;
5426
- material.transparent = true;
5427
- }
5428
- function useWireframeUniforms$1(uniforms, props) {
5429
- React.useEffect(
5430
- () => void (uniforms.fillOpacity.value = props.fillOpacity ?? uniforms.fillOpacity.value),
5431
- [props.fillOpacity]
5432
- );
5433
- React.useEffect(() => void (uniforms.fillMix.value = props.fillMix ?? uniforms.fillMix.value), [props.fillMix]);
5434
- React.useEffect(
5435
- () => void (uniforms.strokeOpacity.value = props.strokeOpacity ?? uniforms.strokeOpacity.value),
5436
- [props.strokeOpacity]
5437
- );
5438
- React.useEffect(
5439
- () => void (uniforms.thickness.value = props.thickness ?? uniforms.thickness.value),
5440
- [props.thickness]
5441
- );
5442
- React.useEffect(() => void (uniforms.colorBackfaces.value = !!props.colorBackfaces), [props.colorBackfaces]);
5443
- React.useEffect(() => void (uniforms.dash.value = !!props.dash), [props.dash]);
5444
- React.useEffect(() => void (uniforms.dashInvert.value = !!props.dashInvert), [props.dashInvert]);
5445
- React.useEffect(
5446
- () => void (uniforms.dashRepeats.value = props.dashRepeats ?? uniforms.dashRepeats.value),
5447
- [props.dashRepeats]
5448
- );
5449
- React.useEffect(
5450
- () => void (uniforms.dashLength.value = props.dashLength ?? uniforms.dashLength.value),
5451
- [props.dashLength]
5452
- );
5453
- React.useEffect(() => void (uniforms.squeeze.value = !!props.squeeze), [props.squeeze]);
5454
- React.useEffect(
5455
- () => void (uniforms.squeezeMin.value = props.squeezeMin ?? uniforms.squeezeMin.value),
5456
- [props.squeezeMin]
5457
- );
5458
- React.useEffect(
5459
- () => void (uniforms.squeezeMax.value = props.squeezeMax ?? uniforms.squeezeMax.value),
5460
- [props.squeezeMax]
5461
- );
5462
- React.useEffect(
5463
- () => void (uniforms.stroke.value = props.stroke ? new THREE.Color(props.stroke) : uniforms.stroke.value),
5464
- [props.stroke]
5465
- );
5466
- React.useEffect(
5467
- () => void (uniforms.fill.value = props.fill ? new THREE.Color(props.fill) : uniforms.fill.value),
5468
- [props.fill]
5469
- );
5470
- React.useEffect(
5471
- () => void (uniforms.backfaceStroke.value = props.backfaceStroke ? new THREE.Color(props.backfaceStroke) : uniforms.backfaceStroke.value),
5472
- [props.backfaceStroke]
5473
- );
5474
- }
5475
-
5476
- function isWithGeometry(object) {
5477
- return !!object?.geometry;
5478
- }
5479
- function isGeometry(object) {
5480
- return !!object?.isBufferGeometry;
5481
- }
5482
- function isRefObject$1(object) {
5483
- return !!object?.current;
5484
- }
5485
- function isRef$1(object) {
5486
- return object?.current !== void 0;
5487
- }
5488
- function isWireframeGeometry(geometry) {
5489
- return geometry.type === "WireframeGeometry";
5490
- }
5491
- function getUniforms() {
5492
- const u = {};
5493
- for (const key in WireframeMaterialShaders$1.uniforms) {
5494
- u[key] = { value: WireframeMaterialShaders$1.uniforms[key] };
5495
- }
5496
- return u;
5497
- }
5498
- function getBarycentricCoordinates(geometry, removeEdge) {
5499
- const position = geometry.getAttribute("position");
5500
- const count = position.count;
5501
- const barycentric = [];
5502
- for (let i = 0; i < count; i++) {
5503
- const even = i % 2 === 0;
5504
- const Q = removeEdge ? 1 : 0;
5505
- if (even) {
5506
- barycentric.push(0, 0, 1, 0, 1, 0, 1, 0, Q);
5507
- } else {
5508
- barycentric.push(0, 1, 0, 0, 0, 1, 1, 0, Q);
5509
- }
5510
- }
5511
- return new THREE.BufferAttribute(Float32Array.from(barycentric), 3);
5512
- }
5513
- function getInputGeometry(inputGeometry) {
5514
- const geo = isRefObject$1(inputGeometry) ? inputGeometry.current : inputGeometry;
5515
- if (!isGeometry(geo)) {
5516
- if (isWireframeGeometry(geo)) {
5517
- throw new Error("Wireframe: WireframeGeometry is not supported.");
5518
- }
5519
- const parent = geo.parent;
5520
- if (isWithGeometry(parent)) {
5521
- if (isWireframeGeometry(parent.geometry)) {
5522
- throw new Error("Wireframe: WireframeGeometry is not supported.");
5523
- }
5524
- return parent.geometry;
5525
- }
5526
- } else {
5527
- return geo;
5528
- }
5529
- }
5530
- function setBarycentricCoordinates$1(geometry, simplify) {
5531
- if (geometry.index) {
5532
- console.warn("Wireframe: Requires non-indexed geometry, converting to non-indexed geometry.");
5533
- const nonIndexedGeo = geometry.toNonIndexed();
5534
- geometry.copy(nonIndexedGeo);
5535
- geometry.setIndex(null);
5536
- }
5537
- const newBarycentric = getBarycentricCoordinates(geometry, simplify);
5538
- geometry.setAttribute("barycentric", newBarycentric);
5539
- }
5540
- function WireframeWithCustomGeo({
5541
- geometry: customGeometry,
5542
- simplify = false,
5543
- ...props
5544
- }) {
5545
- extend({ MeshWireframeMaterial: WireframeMaterial$1 });
5546
- const [geometry, setGeometry] = React.useState(null);
5547
- React.useLayoutEffect(() => {
5548
- const geom = getInputGeometry(customGeometry);
5549
- if (!geom) {
5550
- throw new Error("Wireframe: geometry prop must be a BufferGeometry or a ref to a BufferGeometry.");
5551
- }
5552
- setBarycentricCoordinates$1(geom, simplify);
5553
- if (isRef$1(customGeometry)) {
5554
- setGeometry(geom);
5555
- }
5556
- }, [simplify, customGeometry]);
5557
- const drawnGeo = isRef$1(customGeometry) ? geometry : customGeometry;
5558
- return /* @__PURE__ */ React.createElement(React.Fragment, null, drawnGeo && /* @__PURE__ */ React.createElement("mesh", { geometry: drawnGeo }, /* @__PURE__ */ React.createElement(
5559
- "meshWireframeMaterial",
5560
- {
5561
- attach: "material",
5562
- transparent: true,
5563
- side: THREE.DoubleSide,
5564
- polygonOffset: true,
5565
- polygonOffsetFactor: -4,
5566
- ...props
5567
- }
5568
- )));
5569
- }
5570
- function WireframeWithoutCustomGeo({
5571
- simplify = false,
5572
- ...props
5573
- }) {
5574
- const objectRef = React.useRef(null);
5575
- const uniforms = React.useMemo(() => getUniforms(), [WireframeMaterialShaders$1.uniforms]);
5576
- useWireframeUniforms$1(uniforms, props);
5577
- React.useLayoutEffect(() => {
5578
- const geom = getInputGeometry(objectRef);
5579
- if (!geom) {
5580
- throw new Error("Wireframe: Must be a child of a Mesh, Line or Points object or specify a geometry prop.");
5581
- }
5582
- const og = geom.clone();
5583
- setBarycentricCoordinates$1(geom, simplify);
5584
- return () => {
5585
- geom.copy(og);
5586
- og.dispose();
5587
- };
5588
- }, [simplify]);
5589
- React.useLayoutEffect(() => {
5590
- const parentMesh = objectRef.current.parent;
5591
- const og = parentMesh.material.clone();
5592
- setWireframeOverride$1(parentMesh.material, uniforms);
5593
- return () => {
5594
- parentMesh.material.dispose();
5595
- parentMesh.material = og;
5596
- };
5597
- }, []);
5598
- return /* @__PURE__ */ React.createElement("object3D", { ref: objectRef });
5599
- }
5600
- function Wireframe({ geometry: customGeometry, ...props }) {
5601
- if (customGeometry) {
5602
- return /* @__PURE__ */ React.createElement(WireframeWithCustomGeo, { geometry: customGeometry, ...props });
5603
- }
5604
- return /* @__PURE__ */ React.createElement(WireframeWithoutCustomGeo, { ...props });
5605
- }
5606
-
5607
5055
  const Context = /* @__PURE__ */ React.createContext({});
5608
5056
  const useGizmoContext = () => {
5609
5057
  return React.useContext(Context);
@@ -5899,6 +5347,11 @@ const GizmoViewport = ({
5899
5347
  return /* @__PURE__ */ React.createElement("group", { scale: 40, ...props }, /* @__PURE__ */ React.createElement(Axis, { color: colorX, rotation: [0, 0, 0], scale: axisScale }), /* @__PURE__ */ React.createElement(Axis, { color: colorY, rotation: [0, 0, Math.PI / 2], scale: axisScale }), /* @__PURE__ */ React.createElement(Axis, { color: colorZ, rotation: [0, -Math.PI / 2, 0], scale: axisScale }), !hideAxisHeads && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(AxisHead, { arcStyle: colorX, position: [1, 0, 0], label: labels[0], ...axisHeadProps }), /* @__PURE__ */ React.createElement(AxisHead, { arcStyle: colorY, position: [0, 1, 0], label: labels[1], ...axisHeadProps }), /* @__PURE__ */ React.createElement(AxisHead, { arcStyle: colorZ, position: [0, 0, 1], label: labels[2], ...axisHeadProps }), !hideNegativeAxes && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(AxisHead, { arcStyle: colorX, position: [-1, 0, 0], ...axisHeadProps }), /* @__PURE__ */ React.createElement(AxisHead, { arcStyle: colorY, position: [0, -1, 0], ...axisHeadProps }), /* @__PURE__ */ React.createElement(AxisHead, { arcStyle: colorZ, position: [0, 0, -1], ...axisHeadProps }))));
5900
5348
  };
5901
5349
 
5350
+ const roundEven = (n) => {
5351
+ const rounded = Math.ceil(n);
5352
+ return rounded % 2 === 0 ? rounded : rounded + 1;
5353
+ };
5354
+
5902
5355
  const v1 = /* @__PURE__ */ new Vector3();
5903
5356
  const v2 = /* @__PURE__ */ new Vector3();
5904
5357
  const v3 = /* @__PURE__ */ new Vector3();
@@ -5970,7 +5423,7 @@ const getCameraCSSMatrix = /* @__PURE__ */ ((multipliers) => {
5970
5423
  const getObjectCSSMatrix = /* @__PURE__ */ ((scaleMultipliers) => {
5971
5424
  return (matrix, factor) => getCSSMatrix(matrix, scaleMultipliers(factor), "translate(-50%,-50%)");
5972
5425
  })((f) => [1 / f, 1 / f, 1 / f, 1, -1 / f, -1 / f, -1 / f, -1, 1 / f, 1 / f, 1 / f, 1, 1, 1, 1, 1]);
5973
- function isRefObject(ref) {
5426
+ function isRefObject$1(ref) {
5974
5427
  return ref && typeof ref === "object" && "current" in ref;
5975
5428
  }
5976
5429
  const Html = /* @__PURE__ */ React.forwardRef(
@@ -5997,9 +5450,11 @@ const Html = /* @__PURE__ */ React.forwardRef(
5997
5450
  as = "div",
5998
5451
  wrapperClass,
5999
5452
  pointerEvents = "auto",
5453
+ pixelPerfect = true,
6000
5454
  ...props
6001
5455
  }, ref) => {
6002
5456
  const { gl, camera, scene, size, raycaster, events, viewport } = useThree();
5457
+ const forceEven = useThree((state) => state.internal.forceEven);
6003
5458
  const [el] = React.useState(() => document.createElement(as));
6004
5459
  const root = React.useRef(null);
6005
5460
  const group = React.useRef(null);
@@ -6011,7 +5466,7 @@ const Html = /* @__PURE__ */ React.forwardRef(
6011
5466
  const occlusionMeshRef = React.useRef(null);
6012
5467
  const isMeshSizeSet = React.useRef(false);
6013
5468
  const isRayCastOcclusion = React.useMemo(() => {
6014
- return occlude && occlude !== "blending" || Array.isArray(occlude) && occlude.length && isRefObject(occlude[0]);
5469
+ return occlude && occlude !== "blending" || Array.isArray(occlude) && occlude.length && isRefObject$1(occlude[0]);
6015
5470
  }, [occlude]);
6016
5471
  React.useLayoutEffect(() => {
6017
5472
  const el2 = gl.domElement;
@@ -6024,38 +5479,54 @@ const Html = /* @__PURE__ */ React.forwardRef(
6024
5479
  el2.style.position = null;
6025
5480
  el2.style.pointerEvents = null;
6026
5481
  }
6027
- }, [occlude]);
5482
+ }, [occlude, zIndexRange, gl.domElement]);
6028
5483
  React.useLayoutEffect(() => {
6029
5484
  if (group.current) {
6030
- const currentRoot = root.current = ReactDOM.createRoot(el);
5485
+ if (!root.current) {
5486
+ root.current = ReactDOM.createRoot(el);
5487
+ }
6031
5488
  scene.updateMatrixWorld();
6032
5489
  if (transform) {
6033
5490
  el.style.cssText = `position:absolute;top:0;left:0;pointer-events:none;overflow:hidden;`;
6034
5491
  } else {
6035
- const vec = calculatePosition(group.current, camera, size);
6036
- el.style.cssText = `position:absolute;top:0;left:0;transform:translate3d(${vec[0]}px,${vec[1]}px,0);transform-origin:0 0;`;
5492
+ el.style.cssText = `position:absolute;top:0;left:0;transform:translate3d(0,-9999px,0);transform-origin:0 0;`;
6037
5493
  }
6038
5494
  if (target) {
6039
5495
  if (prepend) target.prepend(el);
6040
5496
  else target.appendChild(el);
6041
5497
  }
6042
5498
  return () => {
6043
- if (target) target.removeChild(el);
6044
- currentRoot.unmount();
5499
+ if (root.current) {
5500
+ root.current.unmount();
5501
+ root.current = null;
5502
+ }
5503
+ if (target && target.contains(el)) {
5504
+ target.removeChild(el);
5505
+ }
6045
5506
  };
6046
5507
  }
6047
- }, [target, transform]);
5508
+ }, [target, transform, el, prepend, scene]);
6048
5509
  React.useLayoutEffect(() => {
6049
5510
  if (wrapperClass) el.className = wrapperClass;
6050
- }, [wrapperClass]);
5511
+ }, [wrapperClass, el]);
5512
+ React.useLayoutEffect(() => {
5513
+ return () => {
5514
+ if (root.current) {
5515
+ root.current.unmount();
5516
+ root.current = null;
5517
+ }
5518
+ };
5519
+ }, []);
6051
5520
  const styles = React.useMemo(() => {
5521
+ const width = forceEven ? roundEven(size.width) : size.width;
5522
+ const height = forceEven ? roundEven(size.height) : size.height;
6052
5523
  if (transform) {
6053
5524
  return {
6054
5525
  position: "absolute",
6055
5526
  top: 0,
6056
5527
  left: 0,
6057
- width: size.width,
6058
- height: size.height,
5528
+ width,
5529
+ height,
6059
5530
  transformStyle: "preserve-3d",
6060
5531
  pointerEvents: "none"
6061
5532
  };
@@ -6064,15 +5535,15 @@ const Html = /* @__PURE__ */ React.forwardRef(
6064
5535
  position: "absolute",
6065
5536
  transform: center ? "translate3d(-50%,-50%,0)" : "none",
6066
5537
  ...fullscreen && {
6067
- top: -size.height / 2,
6068
- left: -size.width / 2,
6069
- width: size.width,
6070
- height: size.height
5538
+ top: forceEven ? roundEven(-size.height / 2) : -size.height / 2,
5539
+ left: forceEven ? roundEven(-size.width / 2) : -size.width / 2,
5540
+ width,
5541
+ height
6071
5542
  },
6072
5543
  ...style
6073
5544
  };
6074
5545
  }
6075
- }, [style, center, fullscreen, size, transform]);
5546
+ }, [style, center, fullscreen, size, transform, forceEven]);
6076
5547
  const transformInnerStyles = React.useMemo(
6077
5548
  () => ({ position: "absolute", pointerEvents }),
6078
5549
  [pointerEvents]
@@ -6086,7 +5557,7 @@ const Html = /* @__PURE__ */ React.forwardRef(
6086
5557
  } else {
6087
5558
  root.current?.render(/* @__PURE__ */ React.createElement("div", { ref, style: styles, className, children }));
6088
5559
  }
6089
- });
5560
+ }, [transform, styles, transformInnerStyles, ref, className, style, children]);
6090
5561
  const visible = React.useRef(true);
6091
5562
  useFrame((gl2) => {
6092
5563
  if (group.current) {
@@ -6118,7 +5589,12 @@ const Html = /* @__PURE__ */ React.forwardRef(
6118
5589
  const zRange = occlude ? isRayCastOcclusion ? [zIndexRange[0], halfRange] : [halfRange - 1, 0] : zIndexRange;
6119
5590
  el.style.zIndex = `${objectZIndex(group.current, camera, zRange)}`;
6120
5591
  if (transform) {
6121
- const [widthHalf, heightHalf] = [size.width / 2, size.height / 2];
5592
+ let widthHalf = forceEven ? roundEven(size.width / 2) : size.width / 2;
5593
+ let heightHalf = forceEven ? roundEven(size.height / 2) : size.height / 2;
5594
+ if (pixelPerfect) {
5595
+ widthHalf = roundToPixelRatio(widthHalf);
5596
+ heightHalf = roundToPixelRatio(heightHalf);
5597
+ }
6122
5598
  const fov = camera.projectionMatrix.elements[5] * heightHalf;
6123
5599
  const { isOrthographicCamera, top, left, bottom, right } = camera;
6124
5600
  const cameraMatrix = getCameraCSSMatrix(camera.matrixWorldInverse);
@@ -6129,8 +5605,10 @@ const Html = /* @__PURE__ */ React.forwardRef(
6129
5605
  matrix.elements[3] = matrix.elements[7] = matrix.elements[11] = 0;
6130
5606
  matrix.elements[15] = 1;
6131
5607
  }
6132
- el.style.width = size.width + "px";
6133
- el.style.height = size.height + "px";
5608
+ const elWidth = forceEven ? roundEven(size.width) : size.width;
5609
+ const elHeight = forceEven ? roundEven(size.height) : size.height;
5610
+ el.style.width = elWidth + "px";
5611
+ el.style.height = elHeight + "px";
6134
5612
  el.style.perspective = isOrthographicCamera ? "" : `${fov}px`;
6135
5613
  if (transformOuterRef.current && transformInnerRef.current) {
6136
5614
  transformOuterRef.current.style.transform = `${cameraTransform}${cameraMatrix}translate(${widthHalf}px,${heightHalf}px)`;
@@ -6138,7 +5616,13 @@ const Html = /* @__PURE__ */ React.forwardRef(
6138
5616
  }
6139
5617
  } else {
6140
5618
  const scale = distanceFactor === void 0 ? 1 : objectScale(group.current, camera) * distanceFactor;
6141
- el.style.transform = `translate3d(${vec[0]}px,${vec[1]}px,0) scale(${scale})`;
5619
+ let x = forceEven ? roundEven(vec[0]) : vec[0];
5620
+ let y = forceEven ? roundEven(vec[1]) : vec[1];
5621
+ if (pixelPerfect) {
5622
+ x = roundToPixelRatio(x);
5623
+ y = roundToPixelRatio(y);
5624
+ }
5625
+ el.style.transform = `translate3d(${x}px,${y}px,0) scale(${scale})`;
6142
5626
  }
6143
5627
  oldPosition.current = vec;
6144
5628
  oldZoom.current = camera.zoom;
@@ -6244,6 +5728,11 @@ const Html = /* @__PURE__ */ React.forwardRef(
6244
5728
  );
6245
5729
 
6246
5730
  const context$4 = /* @__PURE__ */ React.createContext(null);
5731
+ const resolveObject = (object, fallback) => {
5732
+ if (!object) return fallback;
5733
+ if ("current" in object) return object.current;
5734
+ return object;
5735
+ };
6247
5736
 
6248
5737
  const vec1$1 = /* @__PURE__ */ new THREE.Vector3();
6249
5738
  const vec2$1 = /* @__PURE__ */ new THREE.Vector3();
@@ -6278,6 +5767,8 @@ const AxisArrow = ({ direction, axis }) => {
6278
5767
  onDragStart,
6279
5768
  onDrag,
6280
5769
  onDragEnd,
5770
+ onHover,
5771
+ dragState,
6281
5772
  userData,
6282
5773
  LineComponent: Line
6283
5774
  } = React.useContext(context$4);
@@ -6309,7 +5800,13 @@ const AxisArrow = ({ direction, axis }) => {
6309
5800
  const onPointerMove = React.useCallback(
6310
5801
  (e) => {
6311
5802
  e.stopPropagation();
6312
- if (!isHovered) setIsHovered(true);
5803
+ if (!isHovered) {
5804
+ const drag = dragState.current;
5805
+ if (drag === null || drag.component === "Arrow" && drag.axis === axis) {
5806
+ setIsHovered(true);
5807
+ onHover({ component: "Arrow", axis, hovering: true });
5808
+ }
5809
+ }
6313
5810
  if (clickInfo.current) {
6314
5811
  const { clickPoint, dir } = clickInfo.current;
6315
5812
  const [min, max] = translationLimits?.[axis] || [void 0, void 0];
@@ -6328,7 +5825,7 @@ const AxisArrow = ({ direction, axis }) => {
6328
5825
  onDrag(offsetMatrix$1);
6329
5826
  }
6330
5827
  },
6331
- [annotations, onDrag, isHovered, translation, translationLimits, axis]
5828
+ [annotations, onDrag, onHover, dragState, isHovered, translation, translationLimits, axis]
6332
5829
  );
6333
5830
  const onPointerUp = React.useCallback(
6334
5831
  (e) => {
@@ -6343,10 +5840,16 @@ const AxisArrow = ({ direction, axis }) => {
6343
5840
  },
6344
5841
  [annotations, camControls, onDragEnd]
6345
5842
  );
6346
- const onPointerOut = React.useCallback((e) => {
6347
- e.stopPropagation();
6348
- setIsHovered(false);
6349
- }, []);
5843
+ const onPointerOut = React.useCallback(
5844
+ (e) => {
5845
+ e.stopPropagation();
5846
+ if (isHovered) {
5847
+ setIsHovered(false);
5848
+ onHover({ component: "Arrow", axis, hovering: false });
5849
+ }
5850
+ },
5851
+ [onHover, axis, isHovered]
5852
+ );
6350
5853
  const { cylinderLength, coneWidth, coneLength, matrixL } = React.useMemo(() => {
6351
5854
  const coneWidth2 = fixed ? lineWidth / scale * 1.6 : scale / 20;
6352
5855
  const coneLength2 = fixed ? 0.2 : scale / 5;
@@ -6450,6 +5953,8 @@ const rotMatrix = /* @__PURE__ */ new THREE.Matrix4();
6450
5953
  const posNew = /* @__PURE__ */ new THREE.Vector3();
6451
5954
  const ray$1 = /* @__PURE__ */ new THREE.Ray();
6452
5955
  const intersection$1 = /* @__PURE__ */ new THREE.Vector3();
5956
+ const viewPlane = /* @__PURE__ */ new THREE.Plane();
5957
+ const viewIntersection = /* @__PURE__ */ new THREE.Vector3();
6453
5958
  const AxisRotator = ({
6454
5959
  dir1,
6455
5960
  dir2,
@@ -6470,6 +5975,8 @@ const AxisRotator = ({
6470
5975
  onDragStart,
6471
5976
  onDrag,
6472
5977
  onDragEnd,
5978
+ onHover,
5979
+ dragState,
6473
5980
  userData,
6474
5981
  LineComponent: Line
6475
5982
  } = React.useContext(context$4);
@@ -6503,14 +6010,29 @@ const AxisRotator = ({
6503
6010
  const onPointerMove = React.useCallback(
6504
6011
  (e) => {
6505
6012
  e.stopPropagation();
6506
- if (!isHovered) setIsHovered(true);
6013
+ if (!isHovered) {
6014
+ const drag = dragState.current;
6015
+ if (drag === null || drag.component === "Rotator" && drag.axis === axis) {
6016
+ setIsHovered(true);
6017
+ onHover({ component: "Rotator", axis, hovering: true });
6018
+ }
6019
+ }
6507
6020
  if (clickInfo.current) {
6508
6021
  const { clickPoint, origin, e1, e2, normal, plane } = clickInfo.current;
6509
6022
  const [min, max] = rotationLimits?.[axis] || [void 0, void 0];
6510
6023
  ray$1.copy(e.ray);
6511
- ray$1.intersectPlane(plane, intersection$1);
6512
- ray$1.direction.negate();
6513
- ray$1.intersectPlane(plane, intersection$1);
6024
+ const dotProduct = Math.abs(ray$1.direction.dot(normal));
6025
+ if (dotProduct < 0.1) {
6026
+ viewPlane.setFromNormalAndCoplanarPoint(ray$1.direction.clone().negate(), origin);
6027
+ ray$1.intersectPlane(viewPlane, viewIntersection);
6028
+ const toIntersection = viewIntersection.clone().sub(origin);
6029
+ const projectedDist = toIntersection.dot(normal);
6030
+ intersection$1.copy(viewIntersection).sub(normal.clone().multiplyScalar(projectedDist));
6031
+ } else {
6032
+ ray$1.intersectPlane(plane, intersection$1);
6033
+ ray$1.direction.negate();
6034
+ ray$1.intersectPlane(plane, intersection$1);
6035
+ }
6514
6036
  let deltaAngle = calculateAngle(clickPoint, intersection$1, origin, e1, e2);
6515
6037
  let degrees = toDegrees(deltaAngle);
6516
6038
  if (e.shiftKey) {
@@ -6536,7 +6058,7 @@ const AxisRotator = ({
6536
6058
  onDrag(rotMatrix);
6537
6059
  }
6538
6060
  },
6539
- [annotations, onDrag, isHovered, rotationLimits, axis]
6061
+ [annotations, onDrag, onHover, dragState, isHovered, rotationLimits, axis]
6540
6062
  );
6541
6063
  const onPointerUp = React.useCallback(
6542
6064
  (e) => {
@@ -6552,10 +6074,16 @@ const AxisRotator = ({
6552
6074
  },
6553
6075
  [annotations, camControls, onDragEnd]
6554
6076
  );
6555
- const onPointerOut = React.useCallback((e) => {
6556
- e.stopPropagation();
6557
- setIsHovered(false);
6558
- }, []);
6077
+ const onPointerOut = React.useCallback(
6078
+ (e) => {
6079
+ e.stopPropagation();
6080
+ if (isHovered) {
6081
+ setIsHovered(false);
6082
+ onHover({ component: "Rotator", axis, hovering: false });
6083
+ }
6084
+ },
6085
+ [onHover, axis, isHovered]
6086
+ );
6559
6087
  const matrixL = React.useMemo(() => {
6560
6088
  const dir1N = dir1.clone().normalize();
6561
6089
  const dir2N = dir2.clone().normalize();
@@ -6656,6 +6184,8 @@ const PlaneSlider = ({
6656
6184
  onDragStart,
6657
6185
  onDrag,
6658
6186
  onDragEnd,
6187
+ onHover,
6188
+ dragState,
6659
6189
  userData,
6660
6190
  LineComponent: Line
6661
6191
  } = React.useContext(context$4);
@@ -6691,7 +6221,13 @@ const PlaneSlider = ({
6691
6221
  const onPointerMove = React.useCallback(
6692
6222
  (e) => {
6693
6223
  e.stopPropagation();
6694
- if (!isHovered) setIsHovered(true);
6224
+ if (!isHovered) {
6225
+ const drag = dragState.current;
6226
+ if (drag === null || drag.component === "Slider" && drag.axis === axis) {
6227
+ setIsHovered(true);
6228
+ onHover({ component: "Slider", axis, hovering: true });
6229
+ }
6230
+ }
6695
6231
  if (clickInfo.current) {
6696
6232
  const { clickPoint, e1, e2, plane } = clickInfo.current;
6697
6233
  const [minX, maxX] = translationLimits?.[(axis + 1) % 3] || [void 0, void 0];
@@ -6727,7 +6263,7 @@ const PlaneSlider = ({
6727
6263
  onDrag(offsetMatrix);
6728
6264
  }
6729
6265
  },
6730
- [annotations, onDrag, isHovered, translation, translationLimits, axis]
6266
+ [annotations, onDrag, onHover, dragState, isHovered, translation, translationLimits, axis]
6731
6267
  );
6732
6268
  const onPointerUp = React.useCallback(
6733
6269
  (e) => {
@@ -6742,10 +6278,16 @@ const PlaneSlider = ({
6742
6278
  },
6743
6279
  [annotations, camControls, onDragEnd]
6744
6280
  );
6745
- const onPointerOut = React.useCallback((e) => {
6746
- e.stopPropagation();
6747
- setIsHovered(false);
6748
- }, []);
6281
+ const onPointerOut = React.useCallback(
6282
+ (e) => {
6283
+ e.stopPropagation();
6284
+ if (isHovered) {
6285
+ setIsHovered(false);
6286
+ onHover({ component: "Slider", axis, hovering: false });
6287
+ }
6288
+ },
6289
+ [onHover, axis, isHovered]
6290
+ );
6749
6291
  const matrixL = React.useMemo(() => {
6750
6292
  const dir1N = dir1.clone().normalize();
6751
6293
  const dir2N = dir2.clone().normalize();
@@ -6883,6 +6425,8 @@ const ScalingSphere = ({ direction, axis }) => {
6883
6425
  onDragStart,
6884
6426
  onDrag,
6885
6427
  onDragEnd,
6428
+ onHover,
6429
+ dragState,
6886
6430
  userData
6887
6431
  } = React.useContext(context$4);
6888
6432
  const size = useThree((state) => state.size);
@@ -6919,7 +6463,13 @@ const ScalingSphere = ({ direction, axis }) => {
6919
6463
  const onPointerMove = React.useCallback(
6920
6464
  (e) => {
6921
6465
  e.stopPropagation();
6922
- if (!isHovered) setIsHovered(true);
6466
+ if (!isHovered) {
6467
+ const drag = dragState.current;
6468
+ if (drag === null || drag.component === "Sphere" && drag.axis === axis) {
6469
+ setIsHovered(true);
6470
+ onHover({ component: "Sphere", axis, hovering: true });
6471
+ }
6472
+ }
6923
6473
  if (clickInfo.current) {
6924
6474
  const { clickPoint, dir, mPLG, mPLGInv, offsetMultiplier } = clickInfo.current;
6925
6475
  const [min, max] = scaleLimits?.[axis] || [1e-5, void 0];
@@ -6945,7 +6495,7 @@ const ScalingSphere = ({ direction, axis }) => {
6945
6495
  onDrag(scaleMatrix);
6946
6496
  }
6947
6497
  },
6948
- [annotations, position, onDrag, isHovered, scaleLimits, axis]
6498
+ [annotations, position, onDrag, onHover, dragState, isHovered, scaleLimits, axis]
6949
6499
  );
6950
6500
  const onPointerUp = React.useCallback(
6951
6501
  (e) => {
@@ -6962,10 +6512,16 @@ const ScalingSphere = ({ direction, axis }) => {
6962
6512
  },
6963
6513
  [annotations, camControls, onDragEnd, position]
6964
6514
  );
6965
- const onPointerOut = React.useCallback((e) => {
6966
- e.stopPropagation();
6967
- setIsHovered(false);
6968
- }, []);
6515
+ const onPointerOut = React.useCallback(
6516
+ (e) => {
6517
+ e.stopPropagation();
6518
+ if (isHovered) {
6519
+ setIsHovered(false);
6520
+ onHover({ component: "Sphere", axis, hovering: false });
6521
+ }
6522
+ },
6523
+ [onHover, axis, isHovered]
6524
+ );
6969
6525
  const { radius, matrixL } = React.useMemo(() => {
6970
6526
  const radius2 = fixed ? lineWidth / scale * 1.8 : scale / 22.5;
6971
6527
  const quaternion = new THREE.Quaternion().setFromUnitVectors(upV, direction.clone().normalize());
@@ -7035,9 +6591,11 @@ const PivotControls = /* @__PURE__ */ React.forwardRef(
7035
6591
  ({
7036
6592
  enabled = true,
7037
6593
  matrix,
6594
+ object,
7038
6595
  onDragStart,
7039
6596
  onDrag,
7040
6597
  onDragEnd,
6598
+ onHover,
7041
6599
  autoTransform = true,
7042
6600
  anchor,
7043
6601
  disableAxes = false,
@@ -7073,14 +6631,35 @@ const PivotControls = /* @__PURE__ */ React.forwardRef(
7073
6631
  const gizmoRef = React.useRef(null);
7074
6632
  const childrenRef = React.useRef(null);
7075
6633
  const translation = React.useRef([0, 0, 0]);
6634
+ const dragState = React.useRef(null);
7076
6635
  const cameraScale = React.useRef(new THREE.Vector3(1, 1, 1));
7077
6636
  const gizmoScale = React.useRef(new THREE.Vector3(1, 1, 1));
6637
+ React.useEffect(() => {
6638
+ if (object) {
6639
+ const target = resolveObject(object);
6640
+ if (target) {
6641
+ const pivot = ref.current;
6642
+ const didAutoUpdate = target.matrixAutoUpdate;
6643
+ target.updateWorldMatrix(true, true);
6644
+ target.matrixAutoUpdate = false;
6645
+ pivot.matrix.copy(target.matrix);
6646
+ return () => {
6647
+ if (didAutoUpdate) {
6648
+ target.matrixAutoUpdate = true;
6649
+ target.matrix.decompose(target.position, target.quaternion, target.scale);
6650
+ }
6651
+ };
6652
+ }
6653
+ }
6654
+ }, [object]);
7078
6655
  React.useLayoutEffect(() => {
7079
6656
  if (!anchor) return;
7080
- childrenRef.current.updateWorldMatrix(true, true);
7081
- mPInv.copy(childrenRef.current.matrixWorld).invert();
6657
+ const anchorTarget = resolveObject(object, childrenRef.current);
6658
+ if (!anchorTarget) return;
6659
+ anchorTarget.updateWorldMatrix(true, true);
6660
+ mPInv.copy(anchorTarget.matrixWorld).invert();
7082
6661
  bb.makeEmpty();
7083
- childrenRef.current.traverse((obj) => {
6662
+ anchorTarget.traverse((obj) => {
7084
6663
  if (!obj.geometry) return;
7085
6664
  if (!obj.geometry.boundingBox) obj.geometry.computeBoundingBox();
7086
6665
  mL.copy(obj.matrixWorld).premultiply(mPInv);
@@ -7095,9 +6674,17 @@ const PivotControls = /* @__PURE__ */ React.forwardRef(
7095
6674
  gizmoRef.current.position.copy(vPosition);
7096
6675
  invalidate();
7097
6676
  });
6677
+ const mergedUserData = React.useMemo(
6678
+ () => ({
6679
+ ...userData,
6680
+ ...depthTest === false && { interactivePriority: 1 }
6681
+ }),
6682
+ [userData, depthTest]
6683
+ );
7098
6684
  const config = React.useMemo(
7099
6685
  () => ({
7100
6686
  onDragStart: (props2) => {
6687
+ dragState.current = props2;
7101
6688
  mL0.copy(ref.current.matrix);
7102
6689
  mW0.copy(ref.current.matrixWorld);
7103
6690
  onDragStart && onDragStart(props2);
@@ -7113,13 +6700,20 @@ const PivotControls = /* @__PURE__ */ React.forwardRef(
7113
6700
  if (autoTransform) {
7114
6701
  ref.current.matrix.copy(mL);
7115
6702
  }
6703
+ const target = resolveObject(object);
6704
+ if (target) target.matrix.copy(mL);
7116
6705
  onDrag && onDrag(mL, mdL, mW, mdW);
7117
6706
  invalidate();
7118
6707
  },
7119
6708
  onDragEnd: () => {
6709
+ dragState.current = null;
7120
6710
  if (onDragEnd) onDragEnd();
7121
6711
  invalidate();
7122
6712
  },
6713
+ onHover: (props2) => {
6714
+ if (onHover) onHover(props2);
6715
+ },
6716
+ dragState,
7123
6717
  translation,
7124
6718
  translationLimits,
7125
6719
  rotationLimits,
@@ -7131,7 +6725,7 @@ const PivotControls = /* @__PURE__ */ React.forwardRef(
7131
6725
  fixed,
7132
6726
  depthTest,
7133
6727
  renderOrder,
7134
- userData,
6728
+ userData: mergedUserData,
7135
6729
  annotations,
7136
6730
  annotationsClass,
7137
6731
  LineComponent: LineComp
@@ -7140,6 +6734,7 @@ const PivotControls = /* @__PURE__ */ React.forwardRef(
7140
6734
  onDragStart,
7141
6735
  onDrag,
7142
6736
  onDragEnd,
6737
+ onHover,
7143
6738
  translation,
7144
6739
  translationLimits,
7145
6740
  rotationLimits,
@@ -7151,11 +6746,12 @@ const PivotControls = /* @__PURE__ */ React.forwardRef(
7151
6746
  ...axisColors,
7152
6747
  hoveredColor,
7153
6748
  opacity,
7154
- userData,
6749
+ mergedUserData,
7155
6750
  autoTransform,
7156
6751
  annotations,
7157
6752
  annotationsClass,
7158
- LineComp
6753
+ LineComp,
6754
+ object
7159
6755
  ]
7160
6756
  );
7161
6757
  const vec = new THREE.Vector3();
@@ -7290,6 +6886,9 @@ const TransformControls = /* @__PURE__ */ React.forwardRef(
7290
6886
  }
7291
6887
  );
7292
6888
 
6889
+ const getVersion = () => parseInt(REVISION.replace(/\D+/g, ""));
6890
+ const version = /* @__PURE__ */ getVersion();
6891
+
7293
6892
  const opaque_fragment = version >= 154 ? "opaque_fragment" : "output_fragment";
7294
6893
  class PointMaterialImpl extends THREE.PointsMaterial {
7295
6894
  constructor(props) {
@@ -7517,7 +7116,7 @@ function createInstances() {
7517
7116
  ];
7518
7117
  }
7519
7118
  const InstancedAttribute = React.forwardRef(
7520
- ({ name, defaultValue, normalized, usage = THREE.DynamicDrawUsage }, fref) => {
7119
+ ({ name, defaultValue, normalized, usage = THREE.DynamicDrawUsage, type = "float" }, fref) => {
7521
7120
  const ref = React.useRef(null);
7522
7121
  React.useImperativeHandle(fref, () => ref.current, []);
7523
7122
  React.useLayoutEffect(() => {
@@ -7525,7 +7124,26 @@ const InstancedAttribute = React.forwardRef(
7525
7124
  parent.geometry.attributes[name] = ref.current;
7526
7125
  const value = Array.isArray(defaultValue) ? defaultValue : [defaultValue];
7527
7126
  const array = Array.from({ length: parent.userData.limit }, () => value).flat();
7528
- ref.current.array = new Float32Array(array);
7127
+ switch (type) {
7128
+ case "float":
7129
+ ref.current.array = new Float32Array(array);
7130
+ break;
7131
+ case "int":
7132
+ ref.current.array = new Int32Array(array);
7133
+ break;
7134
+ case "uint":
7135
+ ref.current.array = new Uint32Array(array);
7136
+ break;
7137
+ case "short":
7138
+ ref.current.array = new Int16Array(array);
7139
+ break;
7140
+ case "ushort":
7141
+ ref.current.array = new Uint16Array(array);
7142
+ break;
7143
+ case "byte":
7144
+ ref.current.array = new Uint8Array(array);
7145
+ break;
7146
+ }
7529
7147
  ref.current.itemSize = value.length;
7530
7148
  ref.current.count = array.length / ref.current.itemSize;
7531
7149
  return () => {
@@ -9050,12 +8668,17 @@ const Gltf = /* @__PURE__ */ React.forwardRef(
9050
8668
  }
9051
8669
  );
9052
8670
 
9053
- const cdn = "https://cdn.jsdelivr.net/gh/pmndrs/drei-assets@master";
9054
- function useKTX2(input, basisPath = `${cdn}/basis/`) {
8671
+ let transcoderPath = "https://cdn.jsdelivr.net/gh/pmndrs/drei-assets@master/basis/";
8672
+ function useKTX2(input, basisPath = true, extendLoader) {
9055
8673
  const renderer = useThree((state) => state.renderer);
9056
8674
  const textures = useLoader(KTX2Loader, IsObject(input) ? Object.values(input) : input, (loader) => {
8675
+ if (extendLoader) {
8676
+ extendLoader(loader);
8677
+ }
8678
+ if (basisPath) {
8679
+ loader.setTranscoderPath(typeof basisPath === "string" ? basisPath : transcoderPath);
8680
+ }
9057
8681
  loader.detectSupport(renderer);
9058
- loader.setTranscoderPath(basisPath);
9059
8682
  });
9060
8683
  useEffect(() => {
9061
8684
  const array = Array.isArray(textures) ? textures : [textures];
@@ -9070,16 +8693,20 @@ function useKTX2(input, basisPath = `${cdn}/basis/`) {
9070
8693
  return textures;
9071
8694
  }
9072
8695
  }
9073
- useKTX2.preload = (url, basisPath = `${cdn}/basis/`) => useLoader.preload(KTX2Loader, url, (loader) => {
9074
- loader.setTranscoderPath(basisPath);
8696
+ useKTX2.preload = (url, basisPath = true, extendLoader) => useLoader.preload(KTX2Loader, url, (loader) => {
8697
+ if (extendLoader) {
8698
+ extendLoader(loader);
8699
+ }
8700
+ if (basisPath) {
8701
+ loader.setTranscoderPath(typeof basisPath === "string" ? basisPath : transcoderPath);
8702
+ }
9075
8703
  });
9076
8704
  useKTX2.clear = (input) => useLoader.clear(KTX2Loader, input);
9077
- const Ktx2 = ({
9078
- children,
9079
- input,
9080
- basisPath
9081
- }) => {
9082
- const texture = useKTX2(input, basisPath);
8705
+ useKTX2.setTranscoderPath = (path) => {
8706
+ transcoderPath = path;
8707
+ };
8708
+ const Ktx2 = ({ children, input, basisPath, extendLoader }) => {
8709
+ const texture = useKTX2(input, basisPath, extendLoader);
9083
8710
  return /* @__PURE__ */ React.createElement(React.Fragment, null, children?.(texture));
9084
8711
  };
9085
8712
 
@@ -9366,18 +8993,21 @@ function Fisheye({
9366
8993
  const normal = new THREE.Vector3();
9367
8994
  const sph = new THREE.Sphere(new THREE.Vector3(), radius);
9368
8995
  const normalMatrix = new THREE.Matrix3();
9369
- const compute = React.useCallback((event, state, prev) => {
9370
- state.pointer.set(event.offsetX / state.size.width * 2 - 1, -(event.offsetY / state.size.height) * 2 + 1);
9371
- state.raycaster.setFromCamera(state.pointer, orthoC);
9372
- if (!state.raycaster.ray.intersectSphere(sph, normal)) return;
9373
- else normal.normalize();
9374
- normalMatrix.getNormalMatrix(cubeApi.current.camera.matrixWorld);
9375
- cubeApi.current.camera.getWorldPosition(state.raycaster.ray.origin);
9376
- state.raycaster.ray.direction.set(0, 0, 1).reflect(normal);
9377
- state.raycaster.ray.direction.x *= -1;
9378
- state.raycaster.ray.direction.applyNormalMatrix(normalMatrix).multiplyScalar(-1);
9379
- return void 0;
9380
- }, []);
8996
+ const compute = React.useCallback(
8997
+ (event, state, prev) => {
8998
+ state.pointer.set(event.offsetX / state.size.width * 2 - 1, -(event.offsetY / state.size.height) * 2 + 1);
8999
+ state.raycaster.setFromCamera(state.pointer, orthoC);
9000
+ if (!state.raycaster.ray.intersectSphere(sph, normal)) return;
9001
+ else normal.normalize();
9002
+ normalMatrix.getNormalMatrix(cubeApi.current.camera.matrixWorld);
9003
+ cubeApi.current.camera.getWorldPosition(state.raycaster.ray.origin);
9004
+ state.raycaster.ray.direction.set(0, 0, 1).reflect(normal);
9005
+ state.raycaster.ray.direction.x *= -1;
9006
+ state.raycaster.ray.direction.applyNormalMatrix(normalMatrix).multiplyScalar(-1);
9007
+ return void 0;
9008
+ },
9009
+ [width, height]
9010
+ );
9381
9011
  useFrame(
9382
9012
  ({ gl }) => {
9383
9013
  if (renderPriority) gl.render(sphere.current, orthoC);
@@ -9483,14 +9113,22 @@ const isWebGPURenderer = (def) => !!def.isWebGPURenderer;
9483
9113
  const col = /* @__PURE__ */ new THREE.Color();
9484
9114
  const TUNNEL_KEY = "__drei_view_tunnel__";
9485
9115
  const tracked = globalThis[TUNNEL_KEY] || (globalThis[TUNNEL_KEY] = tunnel());
9486
- function computeContainerPosition(canvasSize, trackRect) {
9116
+ function computeContainerPosition(canvasSize, trackRect, forceEven) {
9487
9117
  const { right, top: trackTop, left: trackLeft, bottom: trackBottom, width, height } = trackRect;
9488
9118
  const isOffscreen = trackRect.bottom < 0 || trackTop > canvasSize.height || right < 0 || trackRect.left > canvasSize.width;
9489
9119
  const canvasBottom = canvasSize.top + canvasSize.height;
9490
- const bottom = canvasBottom - trackBottom;
9491
- const left = trackLeft - canvasSize.left;
9492
- const top = trackTop - canvasSize.top;
9493
- return { position: { width, height, left, top, bottom, right }, isOffscreen };
9120
+ const rawBottom = canvasBottom - trackBottom;
9121
+ const rawLeft = trackLeft - canvasSize.left;
9122
+ const rawTop = trackTop - canvasSize.top;
9123
+ const position = forceEven ? {
9124
+ width: roundEven(width),
9125
+ height: roundEven(height),
9126
+ left: roundEven(rawLeft),
9127
+ top: roundEven(rawTop),
9128
+ bottom: roundEven(rawBottom),
9129
+ right: roundEven(right)
9130
+ } : { width, height, left: rawLeft, top: rawTop, bottom: rawBottom, right };
9131
+ return { position, isOffscreen };
9494
9132
  }
9495
9133
  function prepareSkissor(renderer, camera, {
9496
9134
  left,
@@ -9530,6 +9168,7 @@ function clear(renderer) {
9530
9168
  }
9531
9169
  function Container({ visible = true, canvasSize, scene, index, children, frames, rect, track }) {
9532
9170
  const { scene: portalScene, camera: portalCamera, renderer, get, setEvents } = useThree();
9171
+ const forceEven = useThree((state) => state.internal.forceEven);
9533
9172
  const [isOffscreen, setOffscreen] = React.useState(false);
9534
9173
  const mountedRef = React.useRef(true);
9535
9174
  React.useEffect(() => {
@@ -9547,7 +9186,7 @@ function Container({ visible = true, canvasSize, scene, index, children, frames,
9547
9186
  frameCount++;
9548
9187
  }
9549
9188
  if (rect.current) {
9550
- const { position, isOffscreen: _isOffscreen } = computeContainerPosition(canvasSize, rect.current);
9189
+ const { position, isOffscreen: _isOffscreen } = computeContainerPosition(canvasSize, rect.current, forceEven);
9551
9190
  if (isOffscreen !== _isOffscreen) setOffscreen(_isOffscreen);
9552
9191
  if (visible && !_isOffscreen && rect.current) {
9553
9192
  const targetScene = children ? portalScene : scene;
@@ -9569,14 +9208,14 @@ function Container({ visible = true, canvasSize, scene, index, children, frames,
9569
9208
  React.useLayoutEffect(() => {
9570
9209
  const curRect = rect.current;
9571
9210
  if (curRect && (!visible || !isOffscreen)) {
9572
- const { position, isOffscreen: _isOffscreen } = computeContainerPosition(canvasSize, curRect);
9211
+ const { position, isOffscreen: _isOffscreen } = computeContainerPosition(canvasSize, curRect, forceEven);
9573
9212
  if (!_isOffscreen) {
9574
9213
  const autoClear = prepareSkissor(renderer, portalCamera, position);
9575
9214
  clear(renderer);
9576
9215
  finishSkissor(renderer, autoClear);
9577
9216
  }
9578
9217
  }
9579
- }, [visible, isOffscreen, renderer, portalCamera, canvasSize]);
9218
+ }, [visible, isOffscreen, renderer, portalCamera, canvasSize, forceEven]);
9580
9219
  React.useEffect(() => {
9581
9220
  if (!track) return;
9582
9221
  const curRect = rect.current;
@@ -9584,7 +9223,7 @@ function Container({ visible = true, canvasSize, scene, index, children, frames,
9584
9223
  setEvents({ connected: track.current });
9585
9224
  return () => {
9586
9225
  if (curRect) {
9587
- const { position, isOffscreen: _isOffscreen } = computeContainerPosition(canvasSize, curRect);
9226
+ const { position, isOffscreen: _isOffscreen } = computeContainerPosition(canvasSize, curRect, forceEven);
9588
9227
  if (!_isOffscreen) {
9589
9228
  const autoClear = prepareSkissor(renderer, portalCamera, position);
9590
9229
  clear(renderer);
@@ -9593,13 +9232,14 @@ function Container({ visible = true, canvasSize, scene, index, children, frames,
9593
9232
  }
9594
9233
  setEvents({ connected: old });
9595
9234
  };
9596
- }, [track, renderer, portalCamera, canvasSize, get, setEvents]);
9235
+ }, [track, renderer, portalCamera, canvasSize, get, setEvents, forceEven]);
9597
9236
  return /* @__PURE__ */ React.createElement(React.Fragment, null, children, /* @__PURE__ */ React.createElement("group", { onPointerOver: () => null }));
9598
9237
  }
9599
9238
  const CanvasView = /* @__PURE__ */ React.forwardRef(
9600
9239
  ({ track, visible = true, index = 1, id, style, className, frames = Infinity, children, ...props }, fref) => {
9601
9240
  const rect = React.useRef(null);
9602
9241
  const { size, scene } = useThree();
9242
+ const forceEven = useThree((state) => state.internal.forceEven);
9603
9243
  const [virtualScene] = React.useState(() => new THREE.Scene());
9604
9244
  const [ready, toggle] = React.useReducer(() => true, false);
9605
9245
  const compute = React.useCallback(
@@ -9636,12 +9276,12 @@ const CanvasView = /* @__PURE__ */ React.forwardRef(
9636
9276
  {
9637
9277
  events: { compute, priority: index },
9638
9278
  size: {
9639
- width: rect.current?.width,
9640
- height: rect.current?.height,
9279
+ width: forceEven && rect.current?.width ? roundEven(rect.current.width) : rect.current?.width,
9280
+ height: forceEven && rect.current?.height ? roundEven(rect.current.height) : rect.current?.height,
9641
9281
  // @ts-ignore
9642
- top: rect.current?.top,
9282
+ top: forceEven && rect.current?.top ? roundEven(rect.current.top) : rect.current?.top,
9643
9283
  // @ts-ignore
9644
- left: rect.current?.left
9284
+ left: forceEven && rect.current?.left ? roundEven(rect.current.left) : rect.current?.left
9645
9285
  }
9646
9286
  }
9647
9287
  ));
@@ -10068,8 +9708,8 @@ function getLoader(extension) {
10068
9708
  return loader;
10069
9709
  }
10070
9710
 
10071
- const isRef = (obj) => obj.current && obj.current.isScene;
10072
- const resolveScene = (scene) => isRef(scene) ? scene.current : scene;
9711
+ const isRef$1 = (obj) => obj.current && obj.current.isScene;
9712
+ const resolveScene = (scene) => isRef$1(scene) ? scene.current : scene;
10073
9713
  function setEnvProps(background, scene, defaultScene, texture, sceneProps = {}) {
10074
9714
  sceneProps = {
10075
9715
  backgroundBlurriness: 0,
@@ -10816,1118 +10456,905 @@ function useSelect() {
10816
10456
  return React.useContext(context);
10817
10457
  }
10818
10458
 
10819
- function BakeShadows() {
10820
- const gl = useThree((state) => state.gl);
10821
- useEffect(() => {
10822
- gl.shadowMap.autoUpdate = false;
10823
- gl.shadowMap.needsUpdate = true;
10824
- return () => {
10825
- gl.shadowMap.autoUpdate = gl.shadowMap.needsUpdate = true;
10826
- };
10827
- }, [gl.shadowMap]);
10828
- return null;
10829
- }
10830
-
10831
- let ConvolutionMaterial$1 = class ConvolutionMaterial extends THREE.ShaderMaterial {
10832
- kernel;
10833
- constructor(texelSize = new THREE.Vector2()) {
10834
- super({
10835
- uniforms: {
10836
- inputBuffer: new THREE.Uniform(null),
10837
- depthBuffer: new THREE.Uniform(null),
10838
- resolution: new THREE.Uniform(new THREE.Vector2()),
10839
- texelSize: new THREE.Uniform(new THREE.Vector2()),
10840
- halfTexelSize: new THREE.Uniform(new THREE.Vector2()),
10841
- kernel: new THREE.Uniform(0),
10842
- scale: new THREE.Uniform(1),
10843
- cameraNear: new THREE.Uniform(0),
10844
- cameraFar: new THREE.Uniform(1),
10845
- minDepthThreshold: new THREE.Uniform(0),
10846
- maxDepthThreshold: new THREE.Uniform(1),
10847
- depthScale: new THREE.Uniform(0),
10848
- depthToBlurRatioBias: new THREE.Uniform(0.25)
10849
- },
10850
- fragmentShader: `#include <common>
10851
- #include <dithering_pars_fragment>
10852
- uniform sampler2D inputBuffer;
10853
- uniform sampler2D depthBuffer;
10854
- uniform float cameraNear;
10855
- uniform float cameraFar;
10856
- uniform float minDepthThreshold;
10857
- uniform float maxDepthThreshold;
10858
- uniform float depthScale;
10859
- uniform float depthToBlurRatioBias;
10860
- varying vec2 vUv;
10861
- varying vec2 vUv0;
10862
- varying vec2 vUv1;
10863
- varying vec2 vUv2;
10864
- varying vec2 vUv3;
10865
-
10866
- void main() {
10867
- float depthFactor = 0.0;
10868
-
10869
- #ifdef USE_DEPTH
10870
- vec4 depth = texture2D(depthBuffer, vUv);
10871
- depthFactor = smoothstep(minDepthThreshold, maxDepthThreshold, 1.0-(depth.r * depth.a));
10872
- depthFactor *= depthScale;
10873
- depthFactor = max(0.0, min(1.0, depthFactor + 0.25));
10874
- #endif
10875
-
10876
- vec4 sum = texture2D(inputBuffer, mix(vUv0, vUv, depthFactor));
10877
- sum += texture2D(inputBuffer, mix(vUv1, vUv, depthFactor));
10878
- sum += texture2D(inputBuffer, mix(vUv2, vUv, depthFactor));
10879
- sum += texture2D(inputBuffer, mix(vUv3, vUv, depthFactor));
10880
- gl_FragColor = sum * 0.25 ;
10881
-
10882
- #include <dithering_fragment>
10883
- #include <tonemapping_fragment>
10884
- #include <${version >= 154 ? "colorspace_fragment" : "encodings_fragment"}>
10885
- }`,
10886
- vertexShader: `uniform vec2 texelSize;
10887
- uniform vec2 halfTexelSize;
10888
- uniform float kernel;
10889
- uniform float scale;
10890
- varying vec2 vUv;
10891
- varying vec2 vUv0;
10892
- varying vec2 vUv1;
10893
- varying vec2 vUv2;
10894
- varying vec2 vUv3;
10895
-
10896
- void main() {
10897
- vec2 uv = position.xy * 0.5 + 0.5;
10898
- vUv = uv;
10899
-
10900
- vec2 dUv = (texelSize * vec2(kernel) + halfTexelSize) * scale;
10901
- vUv0 = vec2(uv.x - dUv.x, uv.y + dUv.y);
10902
- vUv1 = vec2(uv.x + dUv.x, uv.y + dUv.y);
10903
- vUv2 = vec2(uv.x + dUv.x, uv.y - dUv.y);
10904
- vUv3 = vec2(uv.x - dUv.x, uv.y - dUv.y);
10905
-
10906
- gl_Position = vec4(position.xy, 1.0, 1.0);
10907
- }`,
10908
- blending: THREE.NoBlending,
10909
- depthWrite: false,
10910
- depthTest: false
10911
- });
10912
- this.toneMapped = false;
10913
- this.setTexelSize(texelSize.x, texelSize.y);
10914
- this.kernel = new Float32Array([0, 1, 2, 2, 3]);
10459
+ const aastep = /* @__PURE__ */ Fn((inputs) => {
10460
+ const [threshold, dist] = inputs;
10461
+ const afwidth = fwidth(dist).mul(0.5);
10462
+ return smoothstep(threshold.sub(afwidth), threshold.add(afwidth), dist);
10463
+ });
10464
+ const remap = /* @__PURE__ */ Fn((inputs) => {
10465
+ const [value, min1, max1, min2, max2] = inputs;
10466
+ return min2.add(value.sub(min1).mul(max2.sub(min2)).div(max1.sub(min1)));
10467
+ });
10468
+ class WireframeMaterialImpl extends MeshBasicNodeMaterial {
10469
+ //* Private Uniform Nodes --
10470
+ _strokeOpacity;
10471
+ _fillOpacity;
10472
+ _fillMix;
10473
+ _thickness;
10474
+ _colorBackfaces;
10475
+ // Using number as bool (0/1)
10476
+ _dashInvert;
10477
+ _dash;
10478
+ _dashRepeats;
10479
+ _dashLength;
10480
+ _squeeze;
10481
+ _squeezeMin;
10482
+ _squeezeMax;
10483
+ _stroke;
10484
+ _backfaceStroke;
10485
+ _fill;
10486
+ /** Type flag for identification */
10487
+ isWireframeMaterial = true;
10488
+ constructor() {
10489
+ super();
10490
+ this._strokeOpacity = uniform(1);
10491
+ this._fillOpacity = uniform(0.25);
10492
+ this._fillMix = uniform(0);
10493
+ this._thickness = uniform(0.05);
10494
+ this._colorBackfaces = uniform(0);
10495
+ this._dashInvert = uniform(1);
10496
+ this._dash = uniform(0);
10497
+ this._dashRepeats = uniform(4);
10498
+ this._dashLength = uniform(0.5);
10499
+ this._squeeze = uniform(0);
10500
+ this._squeezeMin = uniform(0.2);
10501
+ this._squeezeMax = uniform(1);
10502
+ this._stroke = uniform(new THREE.Color("#ff0000"));
10503
+ this._backfaceStroke = uniform(new THREE.Color("#0000ff"));
10504
+ this._fill = uniform(new THREE.Color("#00ff00"));
10505
+ this.transparent = true;
10506
+ this.side = THREE.DoubleSide;
10507
+ this._buildWireframeShader();
10915
10508
  }
10916
- setTexelSize(x, y) {
10917
- this.uniforms.texelSize.value.set(x, y);
10918
- this.uniforms.halfTexelSize.value.set(x, y).multiplyScalar(0.5);
10509
+ _buildWireframeShader() {
10510
+ const strokeOpacityUniform = this._strokeOpacity;
10511
+ const fillOpacityUniform = this._fillOpacity;
10512
+ const thicknessUniform = this._thickness;
10513
+ const colorBackfacesUniform = this._colorBackfaces;
10514
+ const dashInvertUniform = this._dashInvert;
10515
+ const dashUniform = this._dash;
10516
+ const dashRepeatsUniform = this._dashRepeats;
10517
+ const dashLengthUniform = this._dashLength;
10518
+ const squeezeUniform = this._squeeze;
10519
+ const squeezeMinUniform = this._squeezeMin;
10520
+ const squeezeMaxUniform = this._squeezeMax;
10521
+ const strokeUniform = this._stroke;
10522
+ const backfaceStrokeUniform = this._backfaceStroke;
10523
+ const fillUniform = this._fill;
10524
+ const barycentricAttr = attribute("barycentric", "vec3");
10525
+ const vBarycentric = varying(barycentricAttr, "v_barycentric");
10526
+ this.outputNode = Fn(() => {
10527
+ const barycentric = vBarycentric;
10528
+ const d = min(min(barycentric.x, barycentric.y), barycentric.z);
10529
+ const positionAlong = float(0).toVar();
10530
+ const maxXY = max(barycentric.x, barycentric.y);
10531
+ const isYSmallest = barycentric.y.lessThan(barycentric.x).and(barycentric.y.lessThan(barycentric.z));
10532
+ positionAlong.assign(select(isYSmallest, float(1).sub(maxXY), maxXY));
10533
+ const computedThickness = remap(thicknessUniform, float(0), float(1), float(0), float(0.34)).toVar();
10534
+ const squeezeEnabled = squeezeUniform.greaterThan(0.5);
10535
+ const squeezeFactor = mix(squeezeMinUniform, squeezeMaxUniform, float(1).sub(sin(positionAlong.mul(Math.PI))));
10536
+ computedThickness.assign(select(squeezeEnabled, computedThickness.mul(squeezeFactor), computedThickness));
10537
+ const dashEnabled = dashUniform.greaterThan(0.5);
10538
+ const dashInverted = dashInvertUniform.greaterThan(0.5);
10539
+ const baseOffset = float(1).div(dashRepeatsUniform).mul(dashLengthUniform).mul(0.5);
10540
+ const additionalOffset = float(1).div(dashRepeatsUniform).mul(0.5);
10541
+ const dashOffset = select(dashInverted, baseOffset, baseOffset.add(additionalOffset));
10542
+ const pattern = fract(positionAlong.add(dashOffset).mul(dashRepeatsUniform));
10543
+ const dashMask = float(1).sub(aastep(dashLengthUniform, pattern));
10544
+ computedThickness.assign(select(dashEnabled, computedThickness.mul(dashMask), computedThickness));
10545
+ const edge = float(1).sub(aastep(computedThickness, d));
10546
+ const useBackfaceColor = colorBackfacesUniform.greaterThan(0.5);
10547
+ const isFrontFace = frontFacing;
10548
+ const currentStroke = select(useBackfaceColor.and(isFrontFace.not()), backfaceStrokeUniform, strokeUniform);
10549
+ const colorStroke = vec4(currentStroke, edge);
10550
+ const colorFill = vec4(fillUniform, fillOpacityUniform);
10551
+ const outColor = mix(colorFill, colorStroke, edge.mul(strokeOpacityUniform));
10552
+ return outColor;
10553
+ })();
10919
10554
  }
10920
- setResolution(resolution) {
10921
- this.uniforms.resolution.value.copy(resolution);
10555
+ //* Uniform Accessors ==============================
10556
+ /** Stroke (edge) opacity, default: 1 */
10557
+ get strokeOpacity() {
10558
+ return this._strokeOpacity.value;
10922
10559
  }
10923
- };
10924
-
10925
- class BlurPass {
10926
- renderTargetA;
10927
- renderTargetB;
10928
- convolutionMaterial;
10929
- scene;
10930
- camera;
10931
- screen;
10932
- renderToScreen = false;
10933
- constructor({
10934
- gl,
10935
- resolution,
10936
- width = 500,
10937
- height = 500,
10938
- minDepthThreshold = 0,
10939
- maxDepthThreshold = 1,
10940
- depthScale = 0,
10941
- depthToBlurRatioBias = 0.25
10942
- }) {
10943
- this.renderTargetA = new WebGLRenderTarget(resolution, resolution, {
10944
- minFilter: LinearFilter,
10945
- magFilter: LinearFilter,
10946
- stencilBuffer: false,
10947
- depthBuffer: false,
10948
- type: HalfFloatType
10949
- });
10950
- this.renderTargetB = this.renderTargetA.clone();
10951
- this.convolutionMaterial = new ConvolutionMaterial$1();
10952
- this.convolutionMaterial.setTexelSize(1 / width, 1 / height);
10953
- this.convolutionMaterial.setResolution(new Vector2(width, height));
10954
- this.scene = new Scene();
10955
- this.camera = new Camera();
10956
- this.convolutionMaterial.uniforms.minDepthThreshold.value = minDepthThreshold;
10957
- this.convolutionMaterial.uniforms.maxDepthThreshold.value = maxDepthThreshold;
10958
- this.convolutionMaterial.uniforms.depthScale.value = depthScale;
10959
- this.convolutionMaterial.uniforms.depthToBlurRatioBias.value = depthToBlurRatioBias;
10960
- this.convolutionMaterial.defines.USE_DEPTH = depthScale > 0;
10961
- const vertices = new Float32Array([-1, -1, 0, 3, -1, 0, -1, 3, 0]);
10962
- const uvs = new Float32Array([0, 0, 2, 0, 0, 2]);
10963
- const geometry = new BufferGeometry();
10964
- geometry.setAttribute("position", new BufferAttribute(vertices, 3));
10965
- geometry.setAttribute("uv", new BufferAttribute(uvs, 2));
10966
- this.screen = new Mesh(geometry, this.convolutionMaterial);
10967
- this.screen.frustumCulled = false;
10968
- this.scene.add(this.screen);
10969
- }
10970
- render(renderer, inputBuffer, outputBuffer) {
10971
- const scene = this.scene;
10972
- const camera = this.camera;
10973
- const renderTargetA = this.renderTargetA;
10974
- const renderTargetB = this.renderTargetB;
10975
- let material = this.convolutionMaterial;
10976
- let uniforms = material.uniforms;
10977
- uniforms.depthBuffer.value = inputBuffer.depthTexture;
10978
- const kernel = material.kernel;
10979
- let lastRT = inputBuffer;
10980
- let destRT;
10981
- let i, l;
10982
- for (i = 0, l = kernel.length - 1; i < l; ++i) {
10983
- destRT = (i & 1) === 0 ? renderTargetA : renderTargetB;
10984
- uniforms.kernel.value = kernel[i];
10985
- uniforms.inputBuffer.value = lastRT.texture;
10986
- renderer.setRenderTarget(destRT);
10987
- renderer.render(scene, camera);
10988
- lastRT = destRT;
10989
- }
10990
- uniforms.kernel.value = kernel[i];
10991
- uniforms.inputBuffer.value = lastRT.texture;
10992
- renderer.setRenderTarget(this.renderToScreen ? null : outputBuffer);
10993
- renderer.render(scene, camera);
10560
+ set strokeOpacity(v) {
10561
+ this._strokeOpacity.value = v;
10994
10562
  }
10995
- }
10996
-
10997
- class ConvolutionMaterial extends MeshBasicNodeMaterial {
10998
- //* Private Uniform Nodes --
10999
- _inputBuffer;
11000
- _depthBuffer;
11001
- _resolution;
11002
- _texelSize;
11003
- _halfTexelSize;
11004
- _kernel;
11005
- _scale;
11006
- _cameraNear;
11007
- _cameraFar;
11008
- _minDepthThreshold;
11009
- _maxDepthThreshold;
11010
- _depthScale;
11011
- _depthToBlurRatioBias;
11012
- _useDepth;
11013
- /** Kernel weights for multi-pass blur */
11014
- kernel;
11015
- constructor(texelSize = new THREE.Vector2()) {
11016
- super();
11017
- this._inputBuffer = uniformTexture(new THREE.Texture());
11018
- this._depthBuffer = uniformTexture(new THREE.Texture());
11019
- this._resolution = uniform(new THREE.Vector2());
11020
- this._texelSize = uniform(new THREE.Vector2());
11021
- this._halfTexelSize = uniform(new THREE.Vector2());
11022
- this._kernel = uniform(0);
11023
- this._scale = uniform(1);
11024
- this._cameraNear = uniform(0);
11025
- this._cameraFar = uniform(1);
11026
- this._minDepthThreshold = uniform(0);
11027
- this._maxDepthThreshold = uniform(1);
11028
- this._depthScale = uniform(0);
11029
- this._depthToBlurRatioBias = uniform(0.25);
11030
- this._useDepth = uniform(0);
11031
- this.blending = THREE.NoBlending;
11032
- this.depthWrite = false;
11033
- this.depthTest = false;
11034
- this.toneMapped = false;
11035
- this.kernel = new Float32Array([0, 1, 2, 2, 3]);
11036
- this.setTexelSize(texelSize.x, texelSize.y);
11037
- this._buildShader();
10563
+ /** Fill opacity (background), default: 0.25 */
10564
+ get fillOpacity() {
10565
+ return this._fillOpacity.value;
11038
10566
  }
11039
- _buildShader() {
11040
- const inputBufferTex = this._inputBuffer;
11041
- const depthBufferTex = this._depthBuffer;
11042
- const texelSizeUniform = this._texelSize;
11043
- const halfTexelSizeUniform = this._halfTexelSize;
11044
- const kernelUniform = this._kernel;
11045
- const scaleUniform = this._scale;
11046
- const minDepthThresholdUniform = this._minDepthThreshold;
11047
- const maxDepthThresholdUniform = this._maxDepthThreshold;
11048
- const depthScaleUniform = this._depthScale;
11049
- const useDepthUniform = this._useDepth;
11050
- const baseUv = varying(uv(), "vUv");
11051
- const dUv = varying(
11052
- Fn(() => {
11053
- return texelSizeUniform.mul(kernelUniform).add(halfTexelSizeUniform).mul(scaleUniform);
11054
- })(),
11055
- "vDuv"
11056
- );
11057
- const vUv0 = varying(Fn(() => vec2$2(baseUv.x.sub(dUv.x), baseUv.y.add(dUv.y)))(), "vUv0");
11058
- const vUv1 = varying(Fn(() => vec2$2(baseUv.x.add(dUv.x), baseUv.y.add(dUv.y)))(), "vUv1");
11059
- const vUv2 = varying(Fn(() => vec2$2(baseUv.x.add(dUv.x), baseUv.y.sub(dUv.y)))(), "vUv2");
11060
- const vUv3 = varying(Fn(() => vec2$2(baseUv.x.sub(dUv.x), baseUv.y.sub(dUv.y)))(), "vUv3");
11061
- this.colorNode = Fn(() => {
11062
- const depthFactor = float(0).toVar();
11063
- const isUsingDepth = useDepthUniform.greaterThan(0.5);
11064
- const depthSample = texture(depthBufferTex, baseUv);
11065
- const rawDepth = float(1).sub(depthSample.r.mul(depthSample.a));
11066
- const smoothedDepth = smoothstep(minDepthThresholdUniform, maxDepthThresholdUniform, rawDepth);
11067
- const scaledDepth = smoothedDepth.mul(depthScaleUniform);
11068
- const clampedDepth = max(float(0), min(float(1), scaledDepth.add(0.25)));
11069
- depthFactor.assign(select(isUsingDepth, clampedDepth, float(0)));
11070
- const sample0 = texture(inputBufferTex, mix(vUv0, baseUv, depthFactor));
11071
- const sample1 = texture(inputBufferTex, mix(vUv1, baseUv, depthFactor));
11072
- const sample2 = texture(inputBufferTex, mix(vUv2, baseUv, depthFactor));
11073
- const sample3 = texture(inputBufferTex, mix(vUv3, baseUv, depthFactor));
11074
- const sum = sample0.add(sample1).add(sample2).add(sample3);
11075
- return sum.mul(0.25);
11076
- })();
11077
- }
11078
- //* Utility Methods ==============================
11079
- /** Set texel size for blur calculations */
11080
- setTexelSize(x, y) {
11081
- this._texelSize.value.set(x, y);
11082
- this._halfTexelSize.value.set(x * 0.5, y * 0.5);
11083
- }
11084
- /** Set render resolution */
11085
- setResolution(resolution) {
11086
- this._resolution.value.copy(resolution);
10567
+ set fillOpacity(v) {
10568
+ this._fillOpacity.value = v;
11087
10569
  }
11088
- //* Uniform Accessors ==============================
11089
- /** Input color buffer to blur */
11090
- get inputBuffer() {
11091
- return this._inputBuffer.value;
10570
+ /** Mix factor between material color and fill color, default: 0 */
10571
+ get fillMix() {
10572
+ return this._fillMix.value;
11092
10573
  }
11093
- set inputBuffer(v) {
11094
- this._inputBuffer.value = v ?? new THREE.Texture();
10574
+ set fillMix(v) {
10575
+ this._fillMix.value = v;
11095
10576
  }
11096
- /** Depth buffer for depth-aware blur */
11097
- get depthBuffer() {
11098
- return this._depthBuffer.value;
10577
+ /** Edge thickness 0-1, default: 0.05 */
10578
+ get thickness() {
10579
+ return this._thickness.value;
11099
10580
  }
11100
- set depthBuffer(v) {
11101
- this._depthBuffer.value = v ?? new THREE.Texture();
10581
+ set thickness(v) {
10582
+ this._thickness.value = v;
11102
10583
  }
11103
- /** Render resolution */
11104
- get resolution() {
11105
- return this._resolution.value;
10584
+ /** Use different color for backfaces */
10585
+ get colorBackfaces() {
10586
+ return this._colorBackfaces.value > 0.5;
11106
10587
  }
11107
- set resolution(v) {
11108
- this._resolution.value = v;
10588
+ set colorBackfaces(v) {
10589
+ this._colorBackfaces.value = v ? 1 : 0;
11109
10590
  }
11110
- /** Texel size (1/resolution) */
11111
- get texelSize() {
11112
- return this._texelSize.value;
10591
+ /** Invert dash pattern */
10592
+ get dashInvert() {
10593
+ return this._dashInvert.value > 0.5;
11113
10594
  }
11114
- set texelSize(v) {
11115
- this._texelSize.value = v;
10595
+ set dashInvert(v) {
10596
+ this._dashInvert.value = v ? 1 : 0;
11116
10597
  }
11117
- /** Half texel size for offset calculations */
11118
- get halfTexelSize() {
11119
- return this._halfTexelSize.value;
10598
+ /** Enable dashed lines */
10599
+ get dash() {
10600
+ return this._dash.value > 0.5;
11120
10601
  }
11121
- set halfTexelSize(v) {
11122
- this._halfTexelSize.value = v;
10602
+ set dash(v) {
10603
+ this._dash.value = v ? 1 : 0;
11123
10604
  }
11124
- /** Kernel offset multiplier (changes per blur pass) */
11125
- get kernelValue() {
11126
- return this._kernel.value;
10605
+ /** Number of dash repeats, default: 4 */
10606
+ get dashRepeats() {
10607
+ return this._dashRepeats.value;
11127
10608
  }
11128
- set kernelValue(v) {
11129
- this._kernel.value = v;
10609
+ set dashRepeats(v) {
10610
+ this._dashRepeats.value = v;
11130
10611
  }
11131
- /** Scale factor for blur spread */
11132
- get scale() {
11133
- return this._scale.value;
10612
+ /** Dash length 0-1, default: 0.5 */
10613
+ get dashLength() {
10614
+ return this._dashLength.value;
11134
10615
  }
11135
- set scale(v) {
11136
- this._scale.value = v;
10616
+ set dashLength(v) {
10617
+ this._dashLength.value = v;
11137
10618
  }
11138
- /** Camera near plane for depth calculations */
11139
- get cameraNear() {
11140
- return this._cameraNear.value;
10619
+ /** Squeeze thickness toward line center */
10620
+ get squeeze() {
10621
+ return this._squeeze.value > 0.5;
11141
10622
  }
11142
- set cameraNear(v) {
11143
- this._cameraNear.value = v;
10623
+ set squeeze(v) {
10624
+ this._squeeze.value = v ? 1 : 0;
11144
10625
  }
11145
- /** Camera far plane for depth calculations */
11146
- get cameraFar() {
11147
- return this._cameraFar.value;
10626
+ /** Minimum squeeze factor, default: 0.2 */
10627
+ get squeezeMin() {
10628
+ return this._squeezeMin.value;
11148
10629
  }
11149
- set cameraFar(v) {
11150
- this._cameraFar.value = v;
10630
+ set squeezeMin(v) {
10631
+ this._squeezeMin.value = v;
11151
10632
  }
11152
- /** Minimum depth threshold for blur (objects closer than this get less blur) */
11153
- get minDepthThreshold() {
11154
- return this._minDepthThreshold.value;
10633
+ /** Maximum squeeze factor, default: 1 */
10634
+ get squeezeMax() {
10635
+ return this._squeezeMax.value;
11155
10636
  }
11156
- set minDepthThreshold(v) {
11157
- this._minDepthThreshold.value = v;
10637
+ set squeezeMax(v) {
10638
+ this._squeezeMax.value = v;
11158
10639
  }
11159
- /** Maximum depth threshold for blur (objects farther than this get full blur) */
11160
- get maxDepthThreshold() {
11161
- return this._maxDepthThreshold.value;
10640
+ /** Stroke (edge) color */
10641
+ get stroke() {
10642
+ return this._stroke.value;
11162
10643
  }
11163
- set maxDepthThreshold(v) {
11164
- this._maxDepthThreshold.value = v;
10644
+ set stroke(v) {
10645
+ if (v instanceof THREE.Color) this._stroke.value = v;
10646
+ else this._stroke.value = new THREE.Color(v);
11165
10647
  }
11166
- /** Scale factor for depth-based blur adjustment */
11167
- get depthScale() {
11168
- return this._depthScale.value;
10648
+ /** Backface stroke color */
10649
+ get backfaceStroke() {
10650
+ return this._backfaceStroke.value;
11169
10651
  }
11170
- set depthScale(v) {
11171
- this._depthScale.value = v;
10652
+ set backfaceStroke(v) {
10653
+ if (v instanceof THREE.Color) this._backfaceStroke.value = v;
10654
+ else this._backfaceStroke.value = new THREE.Color(v);
11172
10655
  }
11173
- /** Bias added to depth-to-blur ratio */
11174
- get depthToBlurRatioBias() {
11175
- return this._depthToBlurRatioBias.value;
10656
+ /** Fill (background) color */
10657
+ get fill() {
10658
+ return this._fill.value;
11176
10659
  }
11177
- set depthToBlurRatioBias(v) {
11178
- this._depthToBlurRatioBias.value = v;
10660
+ set fill(v) {
10661
+ if (v instanceof THREE.Color) this._fill.value = v;
10662
+ else this._fill.value = new THREE.Color(v);
11179
10663
  }
11180
- /** Enable/disable depth-based blur (0 = off, 1 = on) */
11181
- get useDepth() {
11182
- return this._useDepth.value > 0.5;
10664
+ }
10665
+ function setBarycentricCoordinates(geometry) {
10666
+ const position = geometry.getAttribute("position");
10667
+ const count = position.count;
10668
+ if (geometry.index) {
10669
+ geometry = geometry.toNonIndexed();
11183
10670
  }
11184
- set useDepth(v) {
11185
- this._useDepth.value = v ? 1 : 0;
10671
+ const barycentricArray = new Float32Array(count * 3);
10672
+ for (let i = 0; i < count; i += 3) {
10673
+ barycentricArray[i * 3 + 0] = 1;
10674
+ barycentricArray[i * 3 + 1] = 0;
10675
+ barycentricArray[i * 3 + 2] = 0;
10676
+ barycentricArray[(i + 1) * 3 + 0] = 0;
10677
+ barycentricArray[(i + 1) * 3 + 1] = 1;
10678
+ barycentricArray[(i + 1) * 3 + 2] = 0;
10679
+ barycentricArray[(i + 2) * 3 + 0] = 0;
10680
+ barycentricArray[(i + 2) * 3 + 1] = 0;
10681
+ barycentricArray[(i + 2) * 3 + 2] = 1;
11186
10682
  }
10683
+ geometry.setAttribute("barycentric", new THREE.BufferAttribute(barycentricArray, 3));
10684
+ return geometry;
11187
10685
  }
11188
-
11189
- var GradientType = /* @__PURE__ */ ((GradientType2) => {
11190
- GradientType2["Linear"] = "linear";
11191
- GradientType2["Radial"] = "radial";
11192
- return GradientType2;
11193
- })(GradientType || {});
11194
- function GradientTexture({
11195
- stops,
11196
- colors,
11197
- size = 1024,
11198
- width = 16,
11199
- type = "linear" /* Linear */,
11200
- innerCircleRadius = 0,
11201
- outerCircleRadius = "auto",
11202
- ...props
11203
- }) {
11204
- const gl = useThree((state) => state.gl);
11205
- const canvas = React.useMemo(() => {
11206
- const canvas2 = document.createElement("canvas");
11207
- const context = canvas2.getContext("2d");
11208
- canvas2.width = width;
11209
- canvas2.height = size;
11210
- let gradient;
11211
- if (type === "linear" /* Linear */) {
11212
- gradient = context.createLinearGradient(0, 0, 0, size);
11213
- } else {
11214
- const canvasCenterX = canvas2.width / 2;
11215
- const canvasCenterY = canvas2.height / 2;
11216
- const radius = outerCircleRadius !== "auto" ? Math.abs(Number(outerCircleRadius)) : Math.sqrt(canvasCenterX ** 2 + canvasCenterY ** 2);
11217
- gradient = context.createRadialGradient(
11218
- canvasCenterX,
11219
- canvasCenterY,
11220
- Math.abs(innerCircleRadius),
11221
- canvasCenterX,
11222
- canvasCenterY,
11223
- radius
11224
- );
11225
- }
11226
- const tempColor = new THREE.Color();
11227
- let i = stops.length;
11228
- while (i--) {
11229
- gradient.addColorStop(stops[i], tempColor.set(colors[i]).getStyle());
11230
- }
11231
- context.save();
11232
- context.fillStyle = gradient;
11233
- context.fillRect(0, 0, width, size);
11234
- context.restore();
11235
- return canvas2;
11236
- }, [stops]);
11237
- return /* @__PURE__ */ React.createElement("canvasTexture", { colorSpace: gl.outputColorSpace, args: [canvas], attach: "map", ...props });
11238
- }
11239
-
11240
- const LUMA = /* @__PURE__ */ vec3(0.299, 0.587, 0.114);
11241
- const toGrayscale = /* @__PURE__ */ Fn(
11242
- ({ inputColor, intensity }) => {
11243
- const gray = vec3(dot(inputColor.rgb, LUMA));
11244
- return vec4(mix(inputColor.rgb, gray, intensity), inputColor.a);
11245
- }
11246
- );
11247
- const aspect = (size) => size.div(min(size.x, size.y));
11248
- const udRoundBox = /* @__PURE__ */ Fn(
11249
- ({ p, b, r }) => {
11250
- return length(max(abs(p).sub(b).add(r), float(0))).sub(r);
11251
- }
11252
- );
11253
- function useImageMaterial(tex, options) {
11254
- const uniforms = React.useMemo(
11255
- () => ({
11256
- color: uniform(new THREE.Color("white")),
11257
- scale: uniform(new THREE.Vector2(1, 1)),
11258
- imageBounds: uniform(new THREE.Vector2(1, 1)),
11259
- resolution: uniform(1024),
11260
- zoom: uniform(1),
11261
- radius: uniform(0),
11262
- grayscale: uniform(0),
11263
- opacity: uniform(1)
11264
- }),
11265
- []
11266
- );
11267
- const material = React.useMemo(() => {
11268
- const mat = new THREE.MeshBasicNodeMaterial();
11269
- mat.colorNode = Fn(() => {
11270
- const uvCoord = uv();
11271
- const s = aspect(uniforms.scale);
11272
- const i = aspect(uniforms.imageBounds);
11273
- const rs = s.x.div(s.y);
11274
- const ri = i.x.div(i.y);
11275
- const newX = select(rs.lessThan(ri), i.x.mul(s.y).div(i.y), s.x);
11276
- const newY = select(rs.lessThan(ri), s.y, i.y.mul(s.x).div(i.x));
11277
- const newSize = vec2$2(newX, newY);
11278
- const offsetX = select(rs.lessThan(ri), newX.sub(s.x).div(float(2)).div(newX), float(0));
11279
- const offsetY = select(rs.lessThan(ri), float(0), newY.sub(s.y).div(float(2)).div(newY));
11280
- const offset = vec2$2(offsetX, offsetY);
11281
- const transformedUv = uvCoord.mul(s).div(newSize).add(offset);
11282
- const zUv = transformedUv.sub(vec2$2(0.5, 0.5)).div(uniforms.zoom).add(vec2$2(0.5, 0.5));
11283
- const res = uniforms.scale.mul(uniforms.resolution);
11284
- const halfRes = res.mul(0.5);
11285
- const dist = udRoundBox({
11286
- p: uvCoord.mul(res).sub(halfRes),
11287
- b: halfRes,
11288
- r: uniforms.resolution.mul(uniforms.radius)
11289
- });
11290
- const edgeAlpha = smoothstep(float(1), float(0), dist);
11291
- const texColor = texture(tex, zUv);
11292
- const tintedRgb = texColor.rgb.mul(uniforms.color);
11293
- const finalAlpha = texColor.a.mul(uniforms.opacity).mul(edgeAlpha);
11294
- const tinted = vec4(tintedRgb, finalAlpha);
11295
- return toGrayscale({ inputColor: tinted, intensity: uniforms.grayscale });
11296
- })();
11297
- return mat;
11298
- }, [tex, uniforms]);
11299
- React.useLayoutEffect(() => {
11300
- uniforms.color.value.set(options.color);
11301
- uniforms.scale.value.set(options.scale[0], options.scale[1]);
11302
- uniforms.imageBounds.value.set(options.imageBounds[0], options.imageBounds[1]);
11303
- uniforms.resolution.value = options.resolution;
11304
- uniforms.zoom.value = options.zoom;
11305
- uniforms.radius.value = options.radius;
11306
- uniforms.grayscale.value = options.grayscale;
11307
- uniforms.opacity.value = options.opacity;
11308
- }, [
11309
- uniforms,
11310
- options.color,
11311
- options.scale[0],
11312
- options.scale[1],
11313
- options.imageBounds[0],
11314
- options.imageBounds[1],
11315
- options.resolution,
11316
- options.zoom,
11317
- options.radius,
11318
- options.grayscale,
11319
- options.opacity
11320
- ]);
11321
- React.useLayoutEffect(() => {
11322
- material.toneMapped = options.toneMapped ?? true;
11323
- material.transparent = options.transparent ?? false;
11324
- if (options.side !== void 0) material.side = options.side;
11325
- material.needsUpdate = true;
11326
- }, [material, options.toneMapped, options.transparent, options.side]);
11327
- return material;
11328
- }
11329
- const ImageBase = /* @__PURE__ */ React.forwardRef(
10686
+ const WireframeMaterial = /* @__PURE__ */ React.forwardRef(
11330
10687
  ({
11331
- children,
11332
- color = "white",
11333
- segments = 1,
11334
- scale = 1,
11335
- zoom = 1,
11336
- grayscale = 0,
11337
- opacity = 1,
11338
- radius = 0,
11339
- texture,
11340
- toneMapped,
11341
- transparent,
11342
- side,
10688
+ fillOpacity = 0.25,
10689
+ fillMix = 0,
10690
+ strokeOpacity = 1,
10691
+ thickness = 0.05,
10692
+ colorBackfaces = false,
10693
+ dashInvert = true,
10694
+ dash = false,
10695
+ dashRepeats = 4,
10696
+ dashLength = 0.5,
10697
+ squeeze = false,
10698
+ squeezeMin = 0.2,
10699
+ squeezeMax = 1,
10700
+ stroke = "#ff0000",
10701
+ backfaceStroke = "#0000ff",
10702
+ fill = "#00ff00",
11343
10703
  ...props
11344
10704
  }, fref) => {
10705
+ extend({ WireframeMaterial: WireframeMaterialImpl });
11345
10706
  const ref = React.useRef(null);
11346
- const size = useThree((state) => state.size);
11347
- const planeBounds = Array.isArray(scale) ? [scale[0], scale[1]] : [scale, scale];
11348
- const img = texture.image;
11349
- const imageBounds = [img.width, img.height];
11350
- const resolution = Math.max(size.width, size.height);
11351
- const material = useImageMaterial(texture, {
11352
- color,
11353
- zoom,
11354
- radius,
11355
- grayscale,
11356
- opacity,
11357
- scale: planeBounds,
11358
- imageBounds,
11359
- resolution,
11360
- toneMapped,
11361
- transparent,
11362
- side
11363
- });
11364
10707
  React.useImperativeHandle(fref, () => ref.current, []);
11365
- return /* @__PURE__ */ React.createElement("mesh", { ref, scale: Array.isArray(scale) ? [...scale, 1] : scale, ...props }, /* @__PURE__ */ React.createElement("planeGeometry", { args: [1, 1, segments, segments] }), /* @__PURE__ */ React.createElement("primitive", { object: material, attach: "material" }), children);
11366
- }
11367
- );
11368
- const ImageWithUrl = /* @__PURE__ */ React.forwardRef(
11369
- ({ url, ...props }, ref) => {
11370
- const texture = useTexture(url);
11371
- return /* @__PURE__ */ React.createElement(ImageBase, { ...props, texture, ref });
10708
+ return /* @__PURE__ */ React.createElement(
10709
+ "wireframeMaterial",
10710
+ {
10711
+ ref,
10712
+ ...props,
10713
+ fillOpacity,
10714
+ fillMix,
10715
+ strokeOpacity,
10716
+ thickness,
10717
+ colorBackfaces,
10718
+ dashInvert,
10719
+ dash,
10720
+ dashRepeats,
10721
+ dashLength,
10722
+ squeeze,
10723
+ squeezeMin,
10724
+ squeezeMax,
10725
+ stroke: stroke instanceof THREE.Color ? stroke : new THREE.Color(stroke),
10726
+ backfaceStroke: backfaceStroke instanceof THREE.Color ? backfaceStroke : new THREE.Color(backfaceStroke),
10727
+ fill: fill instanceof THREE.Color ? fill : new THREE.Color(fill)
10728
+ }
10729
+ );
11372
10730
  }
11373
10731
  );
11374
- const ImageWithTexture = /* @__PURE__ */ React.forwardRef(({ url: _url, ...props }, ref) => {
11375
- return /* @__PURE__ */ React.createElement(ImageBase, { ...props, ref });
11376
- });
11377
- const Image = /* @__PURE__ */ React.forwardRef((props, ref) => {
11378
- if (props.url) return /* @__PURE__ */ React.createElement(ImageWithUrl, { ...props, ref });
11379
- else if (props.texture) return /* @__PURE__ */ React.createElement(ImageWithTexture, { ...props, ref });
11380
- else throw new Error("<Image /> requires a url or texture");
11381
- });
11382
-
11383
- const pcss = ({ focus = 0, size = 25, samples = 10 } = {}) => `
11384
- #define PENUMBRA_FILTER_SIZE float(${size})
11385
- #define RGB_NOISE_FUNCTION(uv) (randRGB(uv))
11386
- vec3 randRGB(vec2 uv) {
11387
- return vec3(
11388
- fract(sin(dot(uv, vec2(12.75613, 38.12123))) * 13234.76575),
11389
- fract(sin(dot(uv, vec2(19.45531, 58.46547))) * 43678.23431),
11390
- fract(sin(dot(uv, vec2(23.67817, 78.23121))) * 93567.23423)
11391
- );
10732
+ const WireframeMaterialShaders = {
10733
+ uniforms: {
10734
+ strokeOpacity: 1,
10735
+ fillOpacity: 0.25,
10736
+ fillMix: 0,
10737
+ thickness: 0.05,
10738
+ colorBackfaces: false,
10739
+ dashInvert: true,
10740
+ dash: false,
10741
+ dashRepeats: 4,
10742
+ dashLength: 0.5,
10743
+ squeeze: false,
10744
+ squeezeMin: 0.2,
10745
+ squeezeMax: 1,
10746
+ stroke: /* @__PURE__ */ new THREE.Color("#ff0000"),
10747
+ backfaceStroke: /* @__PURE__ */ new THREE.Color("#0000ff"),
10748
+ fill: /* @__PURE__ */ new THREE.Color("#00ff00")
10749
+ },
10750
+ vertex: "// Converted to TSL - see WireframeMaterial",
10751
+ fragment: "// Converted to TSL - see WireframeMaterial"
10752
+ };
10753
+ function setWireframeOverride(_material, _uniforms) {
10754
+ console.warn("setWireframeOverride is deprecated. Use WireframeMaterial directly with TSL for WebGPU.");
10755
+ }
10756
+ function useWireframeUniforms(_uniforms, _props) {
10757
+ console.warn("useWireframeUniforms is deprecated. Use WireframeMaterial props directly with TSL for WebGPU.");
11392
10758
  }
11393
10759
 
11394
- vec3 lowPassRandRGB(vec2 uv) {
11395
- // 3x3 convolution (average)
11396
- // can be implemented as separable with an extra buffer for a total of 6 samples instead of 9
11397
- vec3 result = vec3(0);
11398
- result += RGB_NOISE_FUNCTION(uv + vec2(-1.0, -1.0));
11399
- result += RGB_NOISE_FUNCTION(uv + vec2(-1.0, 0.0));
11400
- result += RGB_NOISE_FUNCTION(uv + vec2(-1.0, +1.0));
11401
- result += RGB_NOISE_FUNCTION(uv + vec2( 0.0, -1.0));
11402
- result += RGB_NOISE_FUNCTION(uv + vec2( 0.0, 0.0));
11403
- result += RGB_NOISE_FUNCTION(uv + vec2( 0.0, +1.0));
11404
- result += RGB_NOISE_FUNCTION(uv + vec2(+1.0, -1.0));
11405
- result += RGB_NOISE_FUNCTION(uv + vec2(+1.0, 0.0));
11406
- result += RGB_NOISE_FUNCTION(uv + vec2(+1.0, +1.0));
11407
- result *= 0.111111111; // 1.0 / 9.0
11408
- return result;
10760
+ function isWithGeometry(object) {
10761
+ return !!object?.geometry;
10762
+ }
10763
+ function isGeometry(object) {
10764
+ return !!object?.isBufferGeometry;
11409
10765
  }
11410
- vec3 highPassRandRGB(vec2 uv) {
11411
- // by subtracting the low-pass signal from the original signal, we're being left with the high-pass signal
11412
- // hp(x) = x - lp(x)
11413
- return RGB_NOISE_FUNCTION(uv) - lowPassRandRGB(uv) + 0.5;
10766
+ function isRefObject(object) {
10767
+ return !!object?.current;
11414
10768
  }
11415
-
11416
-
11417
- vec2 vogelDiskSample(int sampleIndex, int sampleCount, float angle) {
11418
- const float goldenAngle = 2.399963f; // radians
11419
- float r = sqrt(float(sampleIndex) + 0.5f) / sqrt(float(sampleCount));
11420
- float theta = float(sampleIndex) * goldenAngle + angle;
11421
- float sine = sin(theta);
11422
- float cosine = cos(theta);
11423
- return vec2(cosine, sine) * r;
10769
+ function isRef(object) {
10770
+ return object?.current !== void 0;
11424
10771
  }
11425
- float penumbraSize( const in float zReceiver, const in float zBlocker ) { // Parallel plane estimation
11426
- return (zReceiver - zBlocker) / zBlocker;
10772
+ function isWireframeGeometry(geometry) {
10773
+ return geometry.type === "WireframeGeometry";
11427
10774
  }
11428
- float findBlocker(sampler2D shadowMap, vec2 uv, float compare, float angle) {
11429
- float texelSize = 1.0 / float(textureSize(shadowMap, 0).x);
11430
- float blockerDepthSum = float(${focus});
11431
- float blockers = 0.0;
11432
-
11433
- int j = 0;
11434
- vec2 offset = vec2(0.);
11435
- float depth = 0.;
11436
-
11437
- #pragma unroll_loop_start
11438
- for(int i = 0; i < ${samples}; i ++) {
11439
- offset = (vogelDiskSample(j, ${samples}, angle) * texelSize) * 2.0 * PENUMBRA_FILTER_SIZE;
11440
- depth = unpackRGBAToDepth( texture2D( shadowMap, uv + offset));
11441
- if (depth < compare) {
11442
- blockerDepthSum += depth;
11443
- blockers++;
10775
+ function getInputGeometry(inputGeometry) {
10776
+ const geo = isRefObject(inputGeometry) ? inputGeometry.current : inputGeometry;
10777
+ if (!isGeometry(geo)) {
10778
+ if (isWireframeGeometry(geo)) {
10779
+ throw new Error("Wireframe: WireframeGeometry is not supported.");
11444
10780
  }
11445
- j++;
11446
- }
11447
- #pragma unroll_loop_end
11448
-
11449
- if (blockers > 0.0) {
11450
- return blockerDepthSum / blockers;
11451
- }
11452
- return -1.0;
11453
- }
11454
-
11455
-
11456
- float vogelFilter(sampler2D shadowMap, vec2 uv, float zReceiver, float filterRadius, float angle) {
11457
- float texelSize = 1.0 / float(textureSize(shadowMap, 0).x);
11458
- float shadow = 0.0f;
11459
- int j = 0;
11460
- vec2 vogelSample = vec2(0.0);
11461
- vec2 offset = vec2(0.0);
11462
- #pragma unroll_loop_start
11463
- for (int i = 0; i < ${samples}; i++) {
11464
- vogelSample = vogelDiskSample(j, ${samples}, angle) * texelSize;
11465
- offset = vogelSample * (1.0 + filterRadius * float(${size}));
11466
- shadow += step( zReceiver, unpackRGBAToDepth( texture2D( shadowMap, uv + offset ) ) );
11467
- j++;
11468
- }
11469
- #pragma unroll_loop_end
11470
- return shadow * 1.0 / ${samples}.0;
11471
- }
11472
-
11473
- float PCSS (sampler2D shadowMap, vec4 coords) {
11474
- vec2 uv = coords.xy;
11475
- float zReceiver = coords.z; // Assumed to be eye-space z in this code
11476
- float angle = highPassRandRGB(gl_FragCoord.xy).r * PI2;
11477
- float avgBlockerDepth = findBlocker(shadowMap, uv, zReceiver, angle);
11478
- if (avgBlockerDepth == -1.0) {
11479
- return 1.0;
11480
- }
11481
- float penumbraRatio = penumbraSize(zReceiver, avgBlockerDepth);
11482
- return vogelFilter(shadowMap, uv, zReceiver, 1.25 * penumbraRatio, angle);
11483
- }`;
11484
- function reset(gl, scene, camera) {
11485
- scene.traverse((object) => {
11486
- if (object.material) {
11487
- gl.properties.remove(object.material);
11488
- object.material.dispose?.();
10781
+ const parent = geo.parent;
10782
+ if (isWithGeometry(parent)) {
10783
+ if (isWireframeGeometry(parent.geometry)) {
10784
+ throw new Error("Wireframe: WireframeGeometry is not supported.");
10785
+ }
10786
+ return parent.geometry;
11489
10787
  }
11490
- });
11491
- gl.info.programs.length = 0;
11492
- gl.compile(scene, camera);
10788
+ } else {
10789
+ return geo;
10790
+ }
11493
10791
  }
11494
- function SoftShadows({ focus = 0, samples = 10, size = 25 }) {
11495
- const gl = useThree((state) => state.gl);
11496
- const scene = useThree((state) => state.scene);
11497
- const camera = useThree((state) => state.camera);
11498
- React.useEffect(() => {
11499
- const original = THREE.ShaderChunk.shadowmap_pars_fragment;
11500
- THREE.ShaderChunk.shadowmap_pars_fragment = THREE.ShaderChunk.shadowmap_pars_fragment.replace("#ifdef USE_SHADOWMAP", "#ifdef USE_SHADOWMAP\n" + pcss({ size, samples, focus })).replace(
11501
- "#if defined( SHADOWMAP_TYPE_PCF )",
11502
- "\nreturn PCSS(shadowMap, shadowCoord);\n#if defined( SHADOWMAP_TYPE_PCF )"
11503
- );
11504
- reset(gl, scene, camera);
11505
- return () => {
11506
- THREE.ShaderChunk.shadowmap_pars_fragment = original;
11507
- reset(gl, scene, camera);
11508
- };
11509
- }, [focus, size, samples]);
11510
- return null;
10792
+ function WireframeWithCustomGeo({
10793
+ geometry: customGeometry,
10794
+ simplify = false,
10795
+ ...props
10796
+ }) {
10797
+ const [geometry, setGeometry] = React.useState(null);
10798
+ React.useLayoutEffect(() => {
10799
+ let geom = getInputGeometry(customGeometry);
10800
+ if (!geom) {
10801
+ throw new Error("Wireframe: geometry prop must be a BufferGeometry or a ref to a BufferGeometry.");
10802
+ }
10803
+ geom = setBarycentricCoordinates(geom);
10804
+ if (isRef(customGeometry)) {
10805
+ setGeometry(geom);
10806
+ }
10807
+ }, [simplify, customGeometry]);
10808
+ const drawnGeo = isRef(customGeometry) ? geometry : customGeometry;
10809
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, drawnGeo && /* @__PURE__ */ React.createElement("mesh", { geometry: drawnGeo }, /* @__PURE__ */ React.createElement(WireframeMaterial, { ...props })));
11511
10810
  }
11512
-
11513
- class SpotLightMaterial extends MeshBasicNodeMaterial {
11514
- //* Private Uniform Nodes --
11515
- _depth;
11516
- _opacity;
11517
- _attenuation;
11518
- _anglePower;
11519
- _spotPosition;
11520
- _lightColor;
10811
+ function WireframeWithoutCustomGeo({
10812
+ simplify = false,
10813
+ ...props
10814
+ }) {
10815
+ const objectRef = React.useRef(null);
10816
+ React.useLayoutEffect(() => {
10817
+ let geom = getInputGeometry(objectRef);
10818
+ if (!geom) {
10819
+ throw new Error("Wireframe: Must be a child of a Mesh, Line or Points object or specify a geometry prop.");
10820
+ }
10821
+ const og = geom.clone();
10822
+ geom = setBarycentricCoordinates(geom);
10823
+ const parent = objectRef.current.parent;
10824
+ if (parent && parent.geometry !== geom) {
10825
+ parent.geometry = geom;
10826
+ }
10827
+ return () => {
10828
+ if (parent) {
10829
+ parent.geometry.dispose();
10830
+ parent.geometry = og;
10831
+ }
10832
+ };
10833
+ }, [simplify]);
10834
+ return /* @__PURE__ */ React.createElement("object3D", { ref: objectRef });
10835
+ }
10836
+ function Wireframe({ geometry: customGeometry, ...props }) {
10837
+ if (customGeometry) {
10838
+ return /* @__PURE__ */ React.createElement(WireframeWithCustomGeo, { geometry: customGeometry, ...props });
10839
+ }
10840
+ return /* @__PURE__ */ React.createElement(WireframeWithoutCustomGeo, { ...props });
10841
+ }
10842
+
10843
+ function BakeShadows() {
10844
+ const gl = useThree((state) => state.gl);
10845
+ useEffect(() => {
10846
+ gl.shadowMap.autoUpdate = false;
10847
+ gl.shadowMap.needsUpdate = true;
10848
+ return () => {
10849
+ gl.shadowMap.autoUpdate = gl.shadowMap.needsUpdate = true;
10850
+ };
10851
+ }, [gl.shadowMap]);
10852
+ return null;
10853
+ }
10854
+
10855
+ class ConvolutionMaterial extends MeshBasicNodeMaterial {
10856
+ //* Private Uniform Nodes --
10857
+ _inputBuffer;
10858
+ _depthBuffer;
10859
+ _resolution;
10860
+ _texelSize;
10861
+ _halfTexelSize;
10862
+ _kernel;
10863
+ _scale;
11521
10864
  _cameraNear;
11522
10865
  _cameraFar;
11523
- _resolution;
11524
- constructor() {
10866
+ _minDepthThreshold;
10867
+ _maxDepthThreshold;
10868
+ _depthScale;
10869
+ _depthToBlurRatioBias;
10870
+ _useDepth;
10871
+ /** Kernel weights for multi-pass blur */
10872
+ kernel;
10873
+ constructor(texelSize = new THREE.Vector2()) {
11525
10874
  super();
11526
- this._depth = uniformTexture(new THREE.Texture());
11527
- this._opacity = uniform(1);
11528
- this._attenuation = uniform(5);
11529
- this._anglePower = uniform(5);
11530
- this._spotPosition = uniform(new THREE.Vector3(0, 0, 0));
11531
- this._lightColor = uniform(new THREE.Color("white"));
11532
- this._cameraNear = uniform(0.1);
11533
- this._cameraFar = uniform(100);
11534
- this._resolution = uniform(new THREE.Vector2(0, 0));
11535
- this.transparent = true;
10875
+ this._inputBuffer = uniformTexture(new THREE.Texture());
10876
+ this._depthBuffer = uniformTexture(new THREE.Texture());
10877
+ this._resolution = uniform(new THREE.Vector2());
10878
+ this._texelSize = uniform(new THREE.Vector2());
10879
+ this._halfTexelSize = uniform(new THREE.Vector2());
10880
+ this._kernel = uniform(0);
10881
+ this._scale = uniform(1);
10882
+ this._cameraNear = uniform(0);
10883
+ this._cameraFar = uniform(1);
10884
+ this._minDepthThreshold = uniform(0);
10885
+ this._maxDepthThreshold = uniform(1);
10886
+ this._depthScale = uniform(0);
10887
+ this._depthToBlurRatioBias = uniform(0.25);
10888
+ this._useDepth = uniform(0);
10889
+ this.blending = THREE.NoBlending;
11536
10890
  this.depthWrite = false;
11537
- this.side = THREE.DoubleSide;
10891
+ this.depthTest = false;
10892
+ this.toneMapped = false;
10893
+ this.kernel = new Float32Array([0, 1, 2, 2, 3]);
10894
+ this.setTexelSize(texelSize.x, texelSize.y);
11538
10895
  this._buildShader();
11539
10896
  }
11540
- //* Build TSL Shader ==============================
11541
10897
  _buildShader() {
11542
- const depthTex = this._depth;
11543
- const opacityUniform = this._opacity;
11544
- const attenuationUniform = this._attenuation;
11545
- const anglePowerUniform = this._anglePower;
11546
- const spotPositionUniform = this._spotPosition;
11547
- const lightColorUniform = this._lightColor;
11548
- const cameraNearUniform = this._cameraNear;
11549
- const cameraFarUniform = this._cameraFar;
11550
- const resolutionUniform = this._resolution;
11551
- const vNormal = varying(normalize(modelViewMatrix.transformDirection(normalLocal)), "vNormal");
11552
- const vIntensity = varying(
11553
- saturate(float(1).sub(distance(positionWorld, spotPositionUniform).div(attenuationUniform))),
11554
- "vIntensity"
10898
+ const inputBufferTex = this._inputBuffer;
10899
+ const depthBufferTex = this._depthBuffer;
10900
+ const texelSizeUniform = this._texelSize;
10901
+ const halfTexelSizeUniform = this._halfTexelSize;
10902
+ const kernelUniform = this._kernel;
10903
+ const scaleUniform = this._scale;
10904
+ const minDepthThresholdUniform = this._minDepthThreshold;
10905
+ const maxDepthThresholdUniform = this._maxDepthThreshold;
10906
+ const depthScaleUniform = this._depthScale;
10907
+ const useDepthUniform = this._useDepth;
10908
+ const baseUv = varying(uv(), "vUv");
10909
+ const dUv = varying(
10910
+ Fn(() => {
10911
+ return texelSizeUniform.mul(kernelUniform).add(halfTexelSizeUniform).mul(scaleUniform);
10912
+ })(),
10913
+ "vDuv"
11555
10914
  );
11556
- const vViewZ = varying(positionView.z, "vViewZ");
10915
+ const vUv0 = varying(Fn(() => vec2$2(baseUv.x.sub(dUv.x), baseUv.y.add(dUv.y)))(), "vUv0");
10916
+ const vUv1 = varying(Fn(() => vec2$2(baseUv.x.add(dUv.x), baseUv.y.add(dUv.y)))(), "vUv1");
10917
+ const vUv2 = varying(Fn(() => vec2$2(baseUv.x.add(dUv.x), baseUv.y.sub(dUv.y)))(), "vUv2");
10918
+ const vUv3 = varying(Fn(() => vec2$2(baseUv.x.sub(dUv.x), baseUv.y.sub(dUv.y)))(), "vUv3");
11557
10919
  this.colorNode = Fn(() => {
11558
- const angleIntensity = pow(abs(vNormal.z), anglePowerUniform);
11559
- const intensity = vIntensity.mul(angleIntensity).toVar();
11560
- const hasDepth = resolutionUniform.x.greaterThan(0);
11561
- const screenUV = vec2$2(
11562
- screenCoordinate.x.div(resolutionUniform.x),
11563
- float(1).sub(screenCoordinate.y.div(resolutionUniform.y))
11564
- );
11565
- const sampledDepth = texture(depthTex, screenUV).r;
11566
- const near = cameraNearUniform;
11567
- const far = cameraFarUniform;
11568
- const sceneViewZ = near.mul(far).div(far.sub(near).mul(sampledDepth).sub(far));
11569
- const depthDiff = vViewZ.sub(sceneViewZ);
11570
- const softFactor = smoothstep(float(0), float(1), depthDiff);
11571
- intensity.assign(select(hasDepth, intensity.mul(softFactor), intensity));
11572
- return vec4(lightColorUniform, intensity.mul(opacityUniform));
10920
+ const depthFactor = float(0).toVar();
10921
+ const isUsingDepth = useDepthUniform.greaterThan(0.5);
10922
+ const depthSample = texture(depthBufferTex, baseUv);
10923
+ const rawDepth = float(1).sub(depthSample.r.mul(depthSample.a));
10924
+ const smoothedDepth = smoothstep(minDepthThresholdUniform, maxDepthThresholdUniform, rawDepth);
10925
+ const scaledDepth = smoothedDepth.mul(depthScaleUniform);
10926
+ const clampedDepth = max(float(0), min(float(1), scaledDepth.add(0.25)));
10927
+ depthFactor.assign(select(isUsingDepth, clampedDepth, float(0)));
10928
+ const sample0 = texture(inputBufferTex, mix(vUv0, baseUv, depthFactor));
10929
+ const sample1 = texture(inputBufferTex, mix(vUv1, baseUv, depthFactor));
10930
+ const sample2 = texture(inputBufferTex, mix(vUv2, baseUv, depthFactor));
10931
+ const sample3 = texture(inputBufferTex, mix(vUv3, baseUv, depthFactor));
10932
+ const sum = sample0.add(sample1).add(sample2).add(sample3);
10933
+ return sum.mul(0.25);
11573
10934
  })();
11574
10935
  }
10936
+ //* Utility Methods ==============================
10937
+ /** Set texel size for blur calculations */
10938
+ setTexelSize(x, y) {
10939
+ this._texelSize.value.set(x, y);
10940
+ this._halfTexelSize.value.set(x * 0.5, y * 0.5);
10941
+ }
10942
+ /** Set render resolution */
10943
+ setResolution(resolution) {
10944
+ this._resolution.value.copy(resolution);
10945
+ }
11575
10946
  //* Uniform Accessors ==============================
11576
- get depth() {
11577
- return this._depth?.value;
10947
+ /** Input color buffer to blur */
10948
+ get inputBuffer() {
10949
+ return this._inputBuffer.value;
11578
10950
  }
11579
- set depth(v) {
11580
- if (this._depth) this._depth.value = v ?? new THREE.Texture();
10951
+ set inputBuffer(v) {
10952
+ this._inputBuffer.value = v ?? new THREE.Texture();
11581
10953
  }
11582
- get opacity() {
11583
- return this._opacity?.value;
10954
+ /** Depth buffer for depth-aware blur */
10955
+ get depthBuffer() {
10956
+ return this._depthBuffer.value;
11584
10957
  }
11585
- set opacity(v) {
11586
- if (this._opacity) this._opacity.value = v;
10958
+ set depthBuffer(v) {
10959
+ this._depthBuffer.value = v ?? new THREE.Texture();
11587
10960
  }
11588
- get attenuation() {
11589
- return this._attenuation?.value;
10961
+ /** Render resolution */
10962
+ get resolution() {
10963
+ return this._resolution.value;
11590
10964
  }
11591
- set attenuation(v) {
11592
- if (this._attenuation) this._attenuation.value = v;
10965
+ set resolution(v) {
10966
+ this._resolution.value = v;
11593
10967
  }
11594
- get anglePower() {
11595
- return this._anglePower?.value;
10968
+ /** Texel size (1/resolution) */
10969
+ get texelSize() {
10970
+ return this._texelSize.value;
11596
10971
  }
11597
- set anglePower(v) {
11598
- if (this._anglePower) this._anglePower.value = v;
10972
+ set texelSize(v) {
10973
+ this._texelSize.value = v;
11599
10974
  }
11600
- get spotPosition() {
11601
- return this._spotPosition?.value;
10975
+ /** Half texel size for offset calculations */
10976
+ get halfTexelSize() {
10977
+ return this._halfTexelSize.value;
11602
10978
  }
11603
- set spotPosition(v) {
11604
- if (this._spotPosition) this._spotPosition.value = v;
10979
+ set halfTexelSize(v) {
10980
+ this._halfTexelSize.value = v;
11605
10981
  }
11606
- get lightColor() {
11607
- return this._lightColor?.value;
10982
+ /** Kernel offset multiplier (changes per blur pass) */
10983
+ get kernelValue() {
10984
+ return this._kernel.value;
11608
10985
  }
11609
- set lightColor(v) {
11610
- if (this._lightColor) this._lightColor.value = v;
10986
+ set kernelValue(v) {
10987
+ this._kernel.value = v;
10988
+ }
10989
+ /** Scale factor for blur spread */
10990
+ get scale() {
10991
+ return this._scale.value;
10992
+ }
10993
+ set scale(v) {
10994
+ this._scale.value = v;
11611
10995
  }
10996
+ /** Camera near plane for depth calculations */
11612
10997
  get cameraNear() {
11613
- return this._cameraNear?.value;
10998
+ return this._cameraNear.value;
11614
10999
  }
11615
11000
  set cameraNear(v) {
11616
- if (this._cameraNear) this._cameraNear.value = v;
11001
+ this._cameraNear.value = v;
11617
11002
  }
11003
+ /** Camera far plane for depth calculations */
11618
11004
  get cameraFar() {
11619
- return this._cameraFar?.value;
11005
+ return this._cameraFar.value;
11620
11006
  }
11621
11007
  set cameraFar(v) {
11622
- if (this._cameraFar) this._cameraFar.value = v;
11008
+ this._cameraFar.value = v;
11623
11009
  }
11624
- get resolution() {
11625
- return this._resolution?.value;
11010
+ /** Minimum depth threshold for blur (objects closer than this get less blur) */
11011
+ get minDepthThreshold() {
11012
+ return this._minDepthThreshold.value;
11626
11013
  }
11627
- set resolution(v) {
11628
- if (this._resolution) this._resolution.value = v;
11014
+ set minDepthThreshold(v) {
11015
+ this._minDepthThreshold.value = v;
11629
11016
  }
11630
- }
11631
-
11632
- const aastep = /* @__PURE__ */ Fn((inputs) => {
11633
- const [threshold, dist] = inputs;
11634
- const afwidth = fwidth(dist).mul(0.5);
11635
- return smoothstep(threshold.sub(afwidth), threshold.add(afwidth), dist);
11636
- });
11637
- const remap = /* @__PURE__ */ Fn((inputs) => {
11638
- const [value, min1, max1, min2, max2] = inputs;
11639
- return min2.add(value.sub(min1).mul(max2.sub(min2)).div(max1.sub(min1)));
11640
- });
11641
- class WireframeMaterialImpl extends MeshBasicNodeMaterial {
11642
- //* Private Uniform Nodes --
11643
- _strokeOpacity;
11644
- _fillOpacity;
11645
- _fillMix;
11646
- _thickness;
11647
- _colorBackfaces;
11648
- // Using number as bool (0/1)
11649
- _dashInvert;
11650
- _dash;
11651
- _dashRepeats;
11652
- _dashLength;
11653
- _squeeze;
11654
- _squeezeMin;
11655
- _squeezeMax;
11656
- _stroke;
11657
- _backfaceStroke;
11658
- _fill;
11659
- /** Type flag for identification */
11660
- isWireframeMaterial = true;
11661
- constructor() {
11662
- super();
11663
- this._strokeOpacity = uniform(1);
11664
- this._fillOpacity = uniform(0.25);
11665
- this._fillMix = uniform(0);
11666
- this._thickness = uniform(0.05);
11667
- this._colorBackfaces = uniform(0);
11668
- this._dashInvert = uniform(1);
11669
- this._dash = uniform(0);
11670
- this._dashRepeats = uniform(4);
11671
- this._dashLength = uniform(0.5);
11672
- this._squeeze = uniform(0);
11673
- this._squeezeMin = uniform(0.2);
11674
- this._squeezeMax = uniform(1);
11675
- this._stroke = uniform(new THREE.Color("#ff0000"));
11676
- this._backfaceStroke = uniform(new THREE.Color("#0000ff"));
11677
- this._fill = uniform(new THREE.Color("#00ff00"));
11678
- this.transparent = true;
11679
- this.side = THREE.DoubleSide;
11680
- this._buildWireframeShader();
11681
- }
11682
- _buildWireframeShader() {
11683
- const strokeOpacityUniform = this._strokeOpacity;
11684
- const fillOpacityUniform = this._fillOpacity;
11685
- const thicknessUniform = this._thickness;
11686
- const colorBackfacesUniform = this._colorBackfaces;
11687
- const dashInvertUniform = this._dashInvert;
11688
- const dashUniform = this._dash;
11689
- const dashRepeatsUniform = this._dashRepeats;
11690
- const dashLengthUniform = this._dashLength;
11691
- const squeezeUniform = this._squeeze;
11692
- const squeezeMinUniform = this._squeezeMin;
11693
- const squeezeMaxUniform = this._squeezeMax;
11694
- const strokeUniform = this._stroke;
11695
- const backfaceStrokeUniform = this._backfaceStroke;
11696
- const fillUniform = this._fill;
11697
- const barycentricAttr = attribute("barycentric", "vec3");
11698
- const vBarycentric = varying(barycentricAttr, "v_barycentric");
11699
- this.outputNode = Fn(() => {
11700
- const barycentric = vBarycentric;
11701
- const d = min(min(barycentric.x, barycentric.y), barycentric.z);
11702
- const positionAlong = float(0).toVar();
11703
- const maxXY = max(barycentric.x, barycentric.y);
11704
- const isYSmallest = barycentric.y.lessThan(barycentric.x).and(barycentric.y.lessThan(barycentric.z));
11705
- positionAlong.assign(select(isYSmallest, float(1).sub(maxXY), maxXY));
11706
- const computedThickness = remap(thicknessUniform, float(0), float(1), float(0), float(0.34)).toVar();
11707
- const squeezeEnabled = squeezeUniform.greaterThan(0.5);
11708
- const squeezeFactor = mix(squeezeMinUniform, squeezeMaxUniform, float(1).sub(sin(positionAlong.mul(Math.PI))));
11709
- computedThickness.assign(select(squeezeEnabled, computedThickness.mul(squeezeFactor), computedThickness));
11710
- const dashEnabled = dashUniform.greaterThan(0.5);
11711
- const dashInverted = dashInvertUniform.greaterThan(0.5);
11712
- const baseOffset = float(1).div(dashRepeatsUniform).mul(dashLengthUniform).mul(0.5);
11713
- const additionalOffset = float(1).div(dashRepeatsUniform).mul(0.5);
11714
- const dashOffset = select(dashInverted, baseOffset, baseOffset.add(additionalOffset));
11715
- const pattern = fract(positionAlong.add(dashOffset).mul(dashRepeatsUniform));
11716
- const dashMask = float(1).sub(aastep(dashLengthUniform, pattern));
11717
- computedThickness.assign(select(dashEnabled, computedThickness.mul(dashMask), computedThickness));
11718
- const edge = float(1).sub(aastep(computedThickness, d));
11719
- const useBackfaceColor = colorBackfacesUniform.greaterThan(0.5);
11720
- const isFrontFace = frontFacing;
11721
- const currentStroke = select(useBackfaceColor.and(isFrontFace.not()), backfaceStrokeUniform, strokeUniform);
11722
- const colorStroke = vec4(currentStroke, edge);
11723
- const colorFill = vec4(fillUniform, fillOpacityUniform);
11724
- const outColor = mix(colorFill, colorStroke, edge.mul(strokeOpacityUniform));
11725
- return outColor;
11726
- })();
11727
- }
11728
- //* Uniform Accessors ==============================
11729
- /** Stroke (edge) opacity, default: 1 */
11730
- get strokeOpacity() {
11731
- return this._strokeOpacity.value;
11732
- }
11733
- set strokeOpacity(v) {
11734
- this._strokeOpacity.value = v;
11735
- }
11736
- /** Fill opacity (background), default: 0.25 */
11737
- get fillOpacity() {
11738
- return this._fillOpacity.value;
11017
+ /** Maximum depth threshold for blur (objects farther than this get full blur) */
11018
+ get maxDepthThreshold() {
11019
+ return this._maxDepthThreshold.value;
11739
11020
  }
11740
- set fillOpacity(v) {
11741
- this._fillOpacity.value = v;
11021
+ set maxDepthThreshold(v) {
11022
+ this._maxDepthThreshold.value = v;
11742
11023
  }
11743
- /** Mix factor between material color and fill color, default: 0 */
11744
- get fillMix() {
11745
- return this._fillMix.value;
11024
+ /** Scale factor for depth-based blur adjustment */
11025
+ get depthScale() {
11026
+ return this._depthScale.value;
11746
11027
  }
11747
- set fillMix(v) {
11748
- this._fillMix.value = v;
11028
+ set depthScale(v) {
11029
+ this._depthScale.value = v;
11749
11030
  }
11750
- /** Edge thickness 0-1, default: 0.05 */
11751
- get thickness() {
11752
- return this._thickness.value;
11031
+ /** Bias added to depth-to-blur ratio */
11032
+ get depthToBlurRatioBias() {
11033
+ return this._depthToBlurRatioBias.value;
11753
11034
  }
11754
- set thickness(v) {
11755
- this._thickness.value = v;
11035
+ set depthToBlurRatioBias(v) {
11036
+ this._depthToBlurRatioBias.value = v;
11756
11037
  }
11757
- /** Use different color for backfaces */
11758
- get colorBackfaces() {
11759
- return this._colorBackfaces.value > 0.5;
11038
+ /** Enable/disable depth-based blur (0 = off, 1 = on) */
11039
+ get useDepth() {
11040
+ return this._useDepth.value > 0.5;
11760
11041
  }
11761
- set colorBackfaces(v) {
11762
- this._colorBackfaces.value = v ? 1 : 0;
11042
+ set useDepth(v) {
11043
+ this._useDepth.value = v ? 1 : 0;
11763
11044
  }
11764
- /** Invert dash pattern */
11765
- get dashInvert() {
11766
- return this._dashInvert.value > 0.5;
11045
+ }
11046
+
11047
+ var GradientType = /* @__PURE__ */ ((GradientType2) => {
11048
+ GradientType2["Linear"] = "linear";
11049
+ GradientType2["Radial"] = "radial";
11050
+ return GradientType2;
11051
+ })(GradientType || {});
11052
+ function GradientTexture({
11053
+ stops,
11054
+ colors,
11055
+ size = 1024,
11056
+ width = 16,
11057
+ type = "linear" /* Linear */,
11058
+ innerCircleRadius = 0,
11059
+ outerCircleRadius = "auto",
11060
+ ...props
11061
+ }) {
11062
+ const gl = useThree((state) => state.gl);
11063
+ const canvas = React.useMemo(() => {
11064
+ const canvas2 = document.createElement("canvas");
11065
+ const context = canvas2.getContext("2d");
11066
+ canvas2.width = width;
11067
+ canvas2.height = size;
11068
+ let gradient;
11069
+ if (type === "linear" /* Linear */) {
11070
+ gradient = context.createLinearGradient(0, 0, 0, size);
11071
+ } else {
11072
+ const canvasCenterX = canvas2.width / 2;
11073
+ const canvasCenterY = canvas2.height / 2;
11074
+ const radius = outerCircleRadius !== "auto" ? Math.abs(Number(outerCircleRadius)) : Math.sqrt(canvasCenterX ** 2 + canvasCenterY ** 2);
11075
+ gradient = context.createRadialGradient(
11076
+ canvasCenterX,
11077
+ canvasCenterY,
11078
+ Math.abs(innerCircleRadius),
11079
+ canvasCenterX,
11080
+ canvasCenterY,
11081
+ radius
11082
+ );
11083
+ }
11084
+ const tempColor = new THREE.Color();
11085
+ let i = stops.length;
11086
+ while (i--) {
11087
+ gradient.addColorStop(stops[i], tempColor.set(colors[i]).getStyle());
11088
+ }
11089
+ context.save();
11090
+ context.fillStyle = gradient;
11091
+ context.fillRect(0, 0, width, size);
11092
+ context.restore();
11093
+ return canvas2;
11094
+ }, [stops]);
11095
+ return /* @__PURE__ */ React.createElement("canvasTexture", { colorSpace: gl.outputColorSpace, args: [canvas], attach: "map", ...props });
11096
+ }
11097
+
11098
+ const LUMA = /* @__PURE__ */ vec3(0.299, 0.587, 0.114);
11099
+ const toGrayscale = /* @__PURE__ */ Fn(
11100
+ ({ inputColor, intensity }) => {
11101
+ const gray = vec3(dot(inputColor.rgb, LUMA));
11102
+ return vec4(mix(inputColor.rgb, gray, intensity), inputColor.a);
11767
11103
  }
11768
- set dashInvert(v) {
11769
- this._dashInvert.value = v ? 1 : 0;
11104
+ );
11105
+ const aspect = (size) => size.div(min(size.x, size.y));
11106
+ const udRoundBox = /* @__PURE__ */ Fn(
11107
+ ({ p, b, r }) => {
11108
+ return length(max(abs(p).sub(b).add(r), float(0))).sub(r);
11770
11109
  }
11771
- /** Enable dashed lines */
11772
- get dash() {
11773
- return this._dash.value > 0.5;
11110
+ );
11111
+ function useImageMaterial(tex, options) {
11112
+ const uniforms = React.useMemo(
11113
+ () => ({
11114
+ color: uniform(new THREE.Color("white")),
11115
+ scale: uniform(new THREE.Vector2(1, 1)),
11116
+ imageBounds: uniform(new THREE.Vector2(1, 1)),
11117
+ resolution: uniform(1024),
11118
+ zoom: uniform(1),
11119
+ radius: uniform(0),
11120
+ grayscale: uniform(0),
11121
+ opacity: uniform(1)
11122
+ }),
11123
+ []
11124
+ );
11125
+ const material = React.useMemo(() => {
11126
+ const mat = new THREE.MeshBasicNodeMaterial();
11127
+ mat.colorNode = Fn(() => {
11128
+ const uvCoord = uv();
11129
+ const s = aspect(uniforms.scale);
11130
+ const i = aspect(uniforms.imageBounds);
11131
+ const rs = s.x.div(s.y);
11132
+ const ri = i.x.div(i.y);
11133
+ const newX = select(rs.lessThan(ri), i.x.mul(s.y).div(i.y), s.x);
11134
+ const newY = select(rs.lessThan(ri), s.y, i.y.mul(s.x).div(i.x));
11135
+ const newSize = vec2$2(newX, newY);
11136
+ const offsetX = select(rs.lessThan(ri), newX.sub(s.x).div(float(2)).div(newX), float(0));
11137
+ const offsetY = select(rs.lessThan(ri), float(0), newY.sub(s.y).div(float(2)).div(newY));
11138
+ const offset = vec2$2(offsetX, offsetY);
11139
+ const transformedUv = uvCoord.mul(s).div(newSize).add(offset);
11140
+ const zUv = transformedUv.sub(vec2$2(0.5, 0.5)).div(uniforms.zoom).add(vec2$2(0.5, 0.5));
11141
+ const res = uniforms.scale.mul(uniforms.resolution);
11142
+ const halfRes = res.mul(0.5);
11143
+ const dist = udRoundBox({
11144
+ p: uvCoord.mul(res).sub(halfRes),
11145
+ b: halfRes,
11146
+ r: uniforms.resolution.mul(uniforms.radius)
11147
+ });
11148
+ const edgeAlpha = smoothstep(float(1), float(0), dist);
11149
+ const texColor = texture(tex, zUv);
11150
+ const tintedRgb = texColor.rgb.mul(uniforms.color);
11151
+ const finalAlpha = texColor.a.mul(uniforms.opacity).mul(edgeAlpha);
11152
+ const tinted = vec4(tintedRgb, finalAlpha);
11153
+ return toGrayscale({ inputColor: tinted, intensity: uniforms.grayscale });
11154
+ })();
11155
+ return mat;
11156
+ }, [tex, uniforms]);
11157
+ React.useLayoutEffect(() => {
11158
+ uniforms.color.value.set(options.color);
11159
+ uniforms.scale.value.set(options.scale[0], options.scale[1]);
11160
+ uniforms.imageBounds.value.set(options.imageBounds[0], options.imageBounds[1]);
11161
+ uniforms.resolution.value = options.resolution;
11162
+ uniforms.zoom.value = options.zoom;
11163
+ uniforms.radius.value = options.radius;
11164
+ uniforms.grayscale.value = options.grayscale;
11165
+ uniforms.opacity.value = options.opacity;
11166
+ }, [
11167
+ uniforms,
11168
+ options.color,
11169
+ options.scale[0],
11170
+ options.scale[1],
11171
+ options.imageBounds[0],
11172
+ options.imageBounds[1],
11173
+ options.resolution,
11174
+ options.zoom,
11175
+ options.radius,
11176
+ options.grayscale,
11177
+ options.opacity
11178
+ ]);
11179
+ React.useLayoutEffect(() => {
11180
+ material.toneMapped = options.toneMapped ?? true;
11181
+ material.transparent = options.transparent ?? false;
11182
+ if (options.side !== void 0) material.side = options.side;
11183
+ material.needsUpdate = true;
11184
+ }, [material, options.toneMapped, options.transparent, options.side]);
11185
+ return material;
11186
+ }
11187
+ const ImageBase = /* @__PURE__ */ React.forwardRef(
11188
+ ({
11189
+ children,
11190
+ color = "white",
11191
+ segments = 1,
11192
+ scale = 1,
11193
+ zoom = 1,
11194
+ grayscale = 0,
11195
+ opacity = 1,
11196
+ radius = 0,
11197
+ texture,
11198
+ toneMapped,
11199
+ transparent,
11200
+ side,
11201
+ ...props
11202
+ }, fref) => {
11203
+ const ref = React.useRef(null);
11204
+ const size = useThree((state) => state.size);
11205
+ const planeBounds = Array.isArray(scale) ? [scale[0], scale[1]] : [scale, scale];
11206
+ const img = texture.image;
11207
+ const imageBounds = [img.width, img.height];
11208
+ const resolution = Math.max(size.width, size.height);
11209
+ const material = useImageMaterial(texture, {
11210
+ color,
11211
+ zoom,
11212
+ radius,
11213
+ grayscale,
11214
+ opacity,
11215
+ scale: planeBounds,
11216
+ imageBounds,
11217
+ resolution,
11218
+ toneMapped,
11219
+ transparent,
11220
+ side
11221
+ });
11222
+ React.useImperativeHandle(fref, () => ref.current, []);
11223
+ return /* @__PURE__ */ React.createElement("mesh", { ref, scale: Array.isArray(scale) ? [...scale, 1] : scale, ...props }, /* @__PURE__ */ React.createElement("planeGeometry", { args: [1, 1, segments, segments] }), /* @__PURE__ */ React.createElement("primitive", { object: material, attach: "material" }), children);
11774
11224
  }
11775
- set dash(v) {
11776
- this._dash.value = v ? 1 : 0;
11225
+ );
11226
+ const ImageWithUrl = /* @__PURE__ */ React.forwardRef(
11227
+ ({ url, ...props }, ref) => {
11228
+ const texture = useTexture(url);
11229
+ return /* @__PURE__ */ React.createElement(ImageBase, { ...props, texture, ref });
11777
11230
  }
11778
- /** Number of dash repeats, default: 4 */
11779
- get dashRepeats() {
11780
- return this._dashRepeats.value;
11231
+ );
11232
+ const ImageWithTexture = /* @__PURE__ */ React.forwardRef(({ url: _url, ...props }, ref) => {
11233
+ return /* @__PURE__ */ React.createElement(ImageBase, { ...props, ref });
11234
+ });
11235
+ const Image = /* @__PURE__ */ React.forwardRef((props, ref) => {
11236
+ if (props.url) return /* @__PURE__ */ React.createElement(ImageWithUrl, { ...props, ref });
11237
+ else if (props.texture) return /* @__PURE__ */ React.createElement(ImageWithTexture, { ...props, ref });
11238
+ else throw new Error("<Image /> requires a url or texture");
11239
+ });
11240
+
11241
+ class SpotLightMaterial extends MeshBasicNodeMaterial {
11242
+ //* Private Uniform Nodes --
11243
+ _depth;
11244
+ _opacity;
11245
+ _attenuation;
11246
+ _anglePower;
11247
+ _spotPosition;
11248
+ _lightColor;
11249
+ _cameraNear;
11250
+ _cameraFar;
11251
+ _resolution;
11252
+ constructor() {
11253
+ super();
11254
+ this._depth = uniformTexture(new THREE.Texture());
11255
+ this._opacity = uniform(1);
11256
+ this._attenuation = uniform(5);
11257
+ this._anglePower = uniform(5);
11258
+ this._spotPosition = uniform(new THREE.Vector3(0, 0, 0));
11259
+ this._lightColor = uniform(new THREE.Color("white"));
11260
+ this._cameraNear = uniform(0.1);
11261
+ this._cameraFar = uniform(100);
11262
+ this._resolution = uniform(new THREE.Vector2(0, 0));
11263
+ this.transparent = true;
11264
+ this.depthWrite = false;
11265
+ this.side = THREE.DoubleSide;
11266
+ this._buildShader();
11781
11267
  }
11782
- set dashRepeats(v) {
11783
- this._dashRepeats.value = v;
11268
+ //* Build TSL Shader ==============================
11269
+ _buildShader() {
11270
+ const depthTex = this._depth;
11271
+ const opacityUniform = this._opacity;
11272
+ const attenuationUniform = this._attenuation;
11273
+ const anglePowerUniform = this._anglePower;
11274
+ const spotPositionUniform = this._spotPosition;
11275
+ const lightColorUniform = this._lightColor;
11276
+ const cameraNearUniform = this._cameraNear;
11277
+ const cameraFarUniform = this._cameraFar;
11278
+ const resolutionUniform = this._resolution;
11279
+ const vNormal = varying(normalize(modelViewMatrix.transformDirection(normalLocal)), "vNormal");
11280
+ const vIntensity = varying(
11281
+ saturate(float(1).sub(distance(positionWorld, spotPositionUniform).div(attenuationUniform))),
11282
+ "vIntensity"
11283
+ );
11284
+ const vViewZ = varying(positionView.z, "vViewZ");
11285
+ this.colorNode = Fn(() => {
11286
+ const angleIntensity = pow(abs(vNormal.z), anglePowerUniform);
11287
+ const intensity = vIntensity.mul(angleIntensity).toVar();
11288
+ const hasDepth = resolutionUniform.x.greaterThan(0);
11289
+ const screenUV = vec2$2(
11290
+ screenCoordinate.x.div(resolutionUniform.x),
11291
+ float(1).sub(screenCoordinate.y.div(resolutionUniform.y))
11292
+ );
11293
+ const sampledDepth = texture(depthTex, screenUV).r;
11294
+ const near = cameraNearUniform;
11295
+ const far = cameraFarUniform;
11296
+ const sceneViewZ = near.mul(far).div(far.sub(near).mul(sampledDepth).sub(far));
11297
+ const depthDiff = vViewZ.sub(sceneViewZ);
11298
+ const softFactor = smoothstep(float(0), float(1), depthDiff);
11299
+ intensity.assign(select(hasDepth, intensity.mul(softFactor), intensity));
11300
+ return vec4(lightColorUniform, intensity.mul(opacityUniform));
11301
+ })();
11784
11302
  }
11785
- /** Dash length 0-1, default: 0.5 */
11786
- get dashLength() {
11787
- return this._dashLength.value;
11303
+ //* Uniform Accessors ==============================
11304
+ get depth() {
11305
+ return this._depth?.value;
11788
11306
  }
11789
- set dashLength(v) {
11790
- this._dashLength.value = v;
11307
+ set depth(v) {
11308
+ if (this._depth) this._depth.value = v ?? new THREE.Texture();
11791
11309
  }
11792
- /** Squeeze thickness toward line center */
11793
- get squeeze() {
11794
- return this._squeeze.value > 0.5;
11310
+ get opacity() {
11311
+ return this._opacity?.value;
11795
11312
  }
11796
- set squeeze(v) {
11797
- this._squeeze.value = v ? 1 : 0;
11313
+ set opacity(v) {
11314
+ if (this._opacity) this._opacity.value = v;
11798
11315
  }
11799
- /** Minimum squeeze factor, default: 0.2 */
11800
- get squeezeMin() {
11801
- return this._squeezeMin.value;
11316
+ get attenuation() {
11317
+ return this._attenuation?.value;
11802
11318
  }
11803
- set squeezeMin(v) {
11804
- this._squeezeMin.value = v;
11319
+ set attenuation(v) {
11320
+ if (this._attenuation) this._attenuation.value = v;
11805
11321
  }
11806
- /** Maximum squeeze factor, default: 1 */
11807
- get squeezeMax() {
11808
- return this._squeezeMax.value;
11322
+ get anglePower() {
11323
+ return this._anglePower?.value;
11809
11324
  }
11810
- set squeezeMax(v) {
11811
- this._squeezeMax.value = v;
11325
+ set anglePower(v) {
11326
+ if (this._anglePower) this._anglePower.value = v;
11812
11327
  }
11813
- /** Stroke (edge) color */
11814
- get stroke() {
11815
- return this._stroke.value;
11328
+ get spotPosition() {
11329
+ return this._spotPosition?.value;
11816
11330
  }
11817
- set stroke(v) {
11818
- if (v instanceof THREE.Color) this._stroke.value = v;
11819
- else this._stroke.value = new THREE.Color(v);
11331
+ set spotPosition(v) {
11332
+ if (this._spotPosition) this._spotPosition.value = v;
11820
11333
  }
11821
- /** Backface stroke color */
11822
- get backfaceStroke() {
11823
- return this._backfaceStroke.value;
11334
+ get lightColor() {
11335
+ return this._lightColor?.value;
11824
11336
  }
11825
- set backfaceStroke(v) {
11826
- if (v instanceof THREE.Color) this._backfaceStroke.value = v;
11827
- else this._backfaceStroke.value = new THREE.Color(v);
11337
+ set lightColor(v) {
11338
+ if (this._lightColor) this._lightColor.value = v;
11828
11339
  }
11829
- /** Fill (background) color */
11830
- get fill() {
11831
- return this._fill.value;
11340
+ get cameraNear() {
11341
+ return this._cameraNear?.value;
11832
11342
  }
11833
- set fill(v) {
11834
- if (v instanceof THREE.Color) this._fill.value = v;
11835
- else this._fill.value = new THREE.Color(v);
11343
+ set cameraNear(v) {
11344
+ if (this._cameraNear) this._cameraNear.value = v;
11836
11345
  }
11837
- }
11838
- function setBarycentricCoordinates(geometry) {
11839
- const position = geometry.getAttribute("position");
11840
- const count = position.count;
11841
- if (geometry.index) {
11842
- geometry = geometry.toNonIndexed();
11346
+ get cameraFar() {
11347
+ return this._cameraFar?.value;
11843
11348
  }
11844
- const barycentricArray = new Float32Array(count * 3);
11845
- for (let i = 0; i < count; i += 3) {
11846
- barycentricArray[i * 3 + 0] = 1;
11847
- barycentricArray[i * 3 + 1] = 0;
11848
- barycentricArray[i * 3 + 2] = 0;
11849
- barycentricArray[(i + 1) * 3 + 0] = 0;
11850
- barycentricArray[(i + 1) * 3 + 1] = 1;
11851
- barycentricArray[(i + 1) * 3 + 2] = 0;
11852
- barycentricArray[(i + 2) * 3 + 0] = 0;
11853
- barycentricArray[(i + 2) * 3 + 1] = 0;
11854
- barycentricArray[(i + 2) * 3 + 2] = 1;
11349
+ set cameraFar(v) {
11350
+ if (this._cameraFar) this._cameraFar.value = v;
11855
11351
  }
11856
- geometry.setAttribute("barycentric", new THREE.BufferAttribute(barycentricArray, 3));
11857
- return geometry;
11858
- }
11859
- const WireframeMaterial = /* @__PURE__ */ React.forwardRef(
11860
- ({
11861
- fillOpacity = 0.25,
11862
- fillMix = 0,
11863
- strokeOpacity = 1,
11864
- thickness = 0.05,
11865
- colorBackfaces = false,
11866
- dashInvert = true,
11867
- dash = false,
11868
- dashRepeats = 4,
11869
- dashLength = 0.5,
11870
- squeeze = false,
11871
- squeezeMin = 0.2,
11872
- squeezeMax = 1,
11873
- stroke = "#ff0000",
11874
- backfaceStroke = "#0000ff",
11875
- fill = "#00ff00",
11876
- ...props
11877
- }, fref) => {
11878
- extend({ WireframeMaterial: WireframeMaterialImpl });
11879
- const ref = React.useRef(null);
11880
- React.useImperativeHandle(fref, () => ref.current, []);
11881
- return /* @__PURE__ */ React.createElement(
11882
- "wireframeMaterial",
11883
- {
11884
- ref,
11885
- ...props,
11886
- fillOpacity,
11887
- fillMix,
11888
- strokeOpacity,
11889
- thickness,
11890
- colorBackfaces,
11891
- dashInvert,
11892
- dash,
11893
- dashRepeats,
11894
- dashLength,
11895
- squeeze,
11896
- squeezeMin,
11897
- squeezeMax,
11898
- stroke: stroke instanceof THREE.Color ? stroke : new THREE.Color(stroke),
11899
- backfaceStroke: backfaceStroke instanceof THREE.Color ? backfaceStroke : new THREE.Color(backfaceStroke),
11900
- fill: fill instanceof THREE.Color ? fill : new THREE.Color(fill)
11901
- }
11902
- );
11352
+ get resolution() {
11353
+ return this._resolution?.value;
11354
+ }
11355
+ set resolution(v) {
11356
+ if (this._resolution) this._resolution.value = v;
11903
11357
  }
11904
- );
11905
- const WireframeMaterialShaders = {
11906
- uniforms: {
11907
- strokeOpacity: 1,
11908
- fillOpacity: 0.25,
11909
- fillMix: 0,
11910
- thickness: 0.05,
11911
- colorBackfaces: false,
11912
- dashInvert: true,
11913
- dash: false,
11914
- dashRepeats: 4,
11915
- dashLength: 0.5,
11916
- squeeze: false,
11917
- squeezeMin: 0.2,
11918
- squeezeMax: 1,
11919
- stroke: /* @__PURE__ */ new THREE.Color("#ff0000"),
11920
- backfaceStroke: /* @__PURE__ */ new THREE.Color("#0000ff"),
11921
- fill: /* @__PURE__ */ new THREE.Color("#00ff00")
11922
- },
11923
- vertex: "// Converted to TSL - see WireframeMaterial",
11924
- fragment: "// Converted to TSL - see WireframeMaterial"
11925
- };
11926
- function setWireframeOverride(_material, _uniforms) {
11927
- console.warn("setWireframeOverride is deprecated. Use WireframeMaterial directly with TSL for WebGPU.");
11928
- }
11929
- function useWireframeUniforms(_uniforms, _props) {
11930
- console.warn("useWireframeUniforms is deprecated. Use WireframeMaterial props directly with TSL for WebGPU.");
11931
11358
  }
11932
11359
 
11933
11360
  class StarfieldMaterial extends SpriteNodeMaterial {
@@ -12286,6 +11713,20 @@ const Grid = /* @__PURE__ */ React.forwardRef(
12286
11713
  }
12287
11714
  );
12288
11715
 
11716
+ let hasWarned = false;
11717
+ function warnOnce(message) {
11718
+ if (!hasWarned) {
11719
+ console.warn(message);
11720
+ hasWarned = true;
11721
+ }
11722
+ }
11723
+ function SoftShadows(props) {
11724
+ warnOnce(
11725
+ "drei: SoftShadows is not yet supported in WebGPU. It uses ShaderChunk to inject PCSS shaders which is WebGL-only. Import from @react-three/drei/legacy for WebGL."
11726
+ );
11727
+ return null;
11728
+ }
11729
+
12289
11730
  let spotlightIdCounter = 0;
12290
11731
  const generateSpotlightId = () => `spotlight_${spotlightIdCounter++}`;
12291
11732
  function VolumetricMesh({
@@ -12544,4 +11985,4 @@ const Sparkles = /* @__PURE__ */ React.forwardRef(({ noise = 1, count = 100, spe
12544
11985
  );
12545
11986
  });
12546
11987
 
12547
- 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, 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, 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, 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, useVideoTexture, useWireframeUniforms };
11988
+ 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, 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, 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, useVideoTexture, useWireframeUniforms };