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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/legacy/index.mjs CHANGED
@@ -2,9 +2,8 @@ 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';
5
- import { REVISION, WebGLRenderTarget, HalfFloatType, LinearFilter, Vector2, Scene, Camera, BufferGeometry, BufferAttribute, Mesh, MeshStandardMaterial, Plane as Plane$1, Vector3, Matrix4, Vector4, PerspectiveCamera as PerspectiveCamera$1, DepthTexture, DepthFormat, UnsignedShortType, Frustum, MeshPhysicalMaterial, RGBAFormat, UnsignedByteType, Color, InstancedInterleavedBuffer, InterleavedBufferAttribute, Object3D, WebGLCubeRenderTarget, CubeCamera as CubeCamera$1, EventDispatcher, Euler, Quaternion, MathUtils, TextureLoader, Texture as Texture$1, MeshLambertMaterial, DynamicDrawUsage, CatmullRomCurve3, CubicBezierCurve3, QuadraticBezierCurve3, Shape as Shape$1, CanvasTexture, DoubleSide, OrthographicCamera as OrthographicCamera$1, AudioListener, AudioLoader, AnimationMixer, Box3, Sphere as Sphere$1, InstancedBufferAttribute, CubeTextureLoader, DefaultLoadingManager, Ray, Raycaster, CubeReflectionMapping, EquirectangularReflectionMapping, RepeatWrapping, ShaderMaterial, CylinderGeometry, AdditiveBlending, Spherical } from 'three';
5
+ import { REVISION, WebGLRenderTarget, HalfFloatType, LinearFilter, Vector2, Scene, Camera, BufferGeometry, BufferAttribute, Mesh, MeshStandardMaterial, Plane as Plane$1, Vector3, Matrix4, Vector4, PerspectiveCamera as PerspectiveCamera$1, DepthTexture, DepthFormat, UnsignedShortType, Frustum, MeshPhysicalMaterial, RGBAFormat, UnsignedByteType, Color, InstancedInterleavedBuffer, InterleavedBufferAttribute, Object3D, WebGLCubeRenderTarget, CubeCamera as CubeCamera$1, EventDispatcher, Euler, Quaternion, MathUtils, TextureLoader, Texture as Texture$1, MeshLambertMaterial, DynamicDrawUsage, CatmullRomCurve3, CubicBezierCurve3, QuadraticBezierCurve3, Shape as Shape$1, CanvasTexture, OrthographicCamera as OrthographicCamera$1, AudioListener, AudioLoader, AnimationMixer, Box3, Sphere as Sphere$1, InstancedBufferAttribute, DoubleSide, CubeTextureLoader, DefaultLoadingManager, Ray, Raycaster, CubeReflectionMapping, EquirectangularReflectionMapping, RepeatWrapping, ShaderMaterial, CylinderGeometry, AdditiveBlending, Spherical } from 'three';
6
6
  import { ArcballControls as ArcballControls$1 } from 'three/examples/jsm/controls/ArcballControls.js';
7
- import { useGesture } from '@use-gesture/react';
8
7
  import { FirstPersonControls as FirstPersonControls$1 } from 'three/examples/jsm/controls/FirstPersonControls.js';
9
8
  import { FlyControls as FlyControls$1 } from 'three/examples/jsm/controls/FlyControls.js';
10
9
  import { create as create$1 } from 'zustand';
@@ -13,6 +12,7 @@ import { MapControls as MapControls$1 } from 'three/examples/jsm/controls/MapCon
13
12
  import { misc, easing } from 'maath';
14
13
  import { OrbitControls as OrbitControls$1 } from 'three/addons/controls/OrbitControls.js';
15
14
  import { PointerLockControls as PointerLockControls$1 } from 'three/examples/jsm/controls/PointerLockControls.js';
15
+ import { useGesture } from '@use-gesture/react';
16
16
  import * as ReactDOM from 'react-dom/client';
17
17
  import { TrackballControls as TrackballControls$1 } from 'three/examples/jsm/controls/TrackballControls.js';
18
18
  import { SimplexNoise } from 'three/examples/jsm/math/SimplexNoise.js';
@@ -31,7 +31,6 @@ import { TransformControls as TransformControls$1 } from 'three/examples/jsm/con
31
31
  import * as SkeletonUtils from 'three/examples/jsm/utils/SkeletonUtils.js';
32
32
  import { MeshSurfaceSampler } from 'three/examples/jsm/math/MeshSurfaceSampler.js';
33
33
  import { SVGLoader } from 'three/examples/jsm/loaders/SVGLoader.js';
34
- import { Events } from 'hls.js';
35
34
  import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader.js';
36
35
  import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
37
36
  import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
@@ -57,7 +56,7 @@ import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js';
57
56
  import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
58
57
  import { AsciiEffect } from 'three/examples/jsm/effects/AsciiEffect.js';
59
58
  import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass.js';
60
- import { shaderStructs, shaderIntersectFunction, MeshBVHUniformStruct } from 'three-mesh-bvh';
59
+ import { MeshBVHUniformStruct, MeshBVH, SAH, shaderStructs, shaderIntersectFunction } from 'three-mesh-bvh';
61
60
  import { Text as Text$1, preloadFont } from 'troika-three-text';
62
61
 
63
62
  const ContactShadows = /* @__PURE__ */ React.forwardRef(
@@ -799,6 +798,12 @@ const Caustics = /* @__PURE__ */ React.forwardRef(
799
798
  const helper = useHelper(debug && camera, THREE.CameraHelper);
800
799
  const normalTarget = useFBO(resolution, resolution, NORMALPROPS);
801
800
  const normalTargetB = useFBO(resolution, resolution, NORMALPROPS);
801
+ if (!gl.extensions.get("OES_texture_float_linear")) {
802
+ console.warn(
803
+ "[Caustics]: `OES_texture_float_linear` extension not supported, using HalfFloatType instead of FloatType"
804
+ );
805
+ CAUSTICPROPS.type = THREE.HalfFloatType;
806
+ }
802
807
  const causticsTarget = useFBO(resolution, resolution, CAUSTICPROPS);
803
808
  const causticsTargetB = useFBO(resolution, resolution, CAUSTICPROPS);
804
809
  const [normalMat] = React.useState(() => createNormalMaterial());
@@ -1085,7 +1090,7 @@ class MeshTransmissionMaterialImpl extends THREE.MeshPhysicalMaterial {
1085
1090
  vec3 getVolumeTransmissionRay( const in vec3 n, const in vec3 v, const in float thickness, const in float ior, const in mat4 modelMatrix ) {
1086
1091
  // Direction of refracted light.
1087
1092
  vec3 refractionVector = refract( - v, normalize( n ), 1.0 / ior );
1088
- // Compute rotation-independant scaling of the model matrix.
1093
+ // Compute rotation-independent scaling of the model matrix.
1089
1094
  vec3 modelScale;
1090
1095
  modelScale.x = length( vec3( modelMatrix[ 0 ].xyz ) );
1091
1096
  modelScale.y = length( vec3( modelMatrix[ 1 ].xyz ) );
@@ -1869,7 +1874,7 @@ const MeshReflectorMaterial = /* @__PURE__ */ React.forwardRef(
1869
1874
  }
1870
1875
  );
1871
1876
 
1872
- const MeshRefractionMaterial = /* @__PURE__ */ shaderMaterial(
1877
+ const MeshRefractionMaterial_ = /* @__PURE__ */ shaderMaterial(
1873
1878
  {
1874
1879
  envMap: null,
1875
1880
  bounces: 3,
@@ -2030,6 +2035,56 @@ const MeshRefractionMaterial = /* @__PURE__ */ shaderMaterial(
2030
2035
  #include <${version$1 >= 154 ? "colorspace_fragment" : "encodings_fragment"}>
2031
2036
  }`
2032
2037
  );
2038
+ const isCubeTexture = (def) => def && def.isCubeTexture;
2039
+ const MeshRefractionMaterial = /* @__PURE__ */ React.forwardRef(
2040
+ ({ aberrationStrength = 0, fastChroma = true, envMap, ...props }, ref) => {
2041
+ extend({ MeshRefractionMaterial_ });
2042
+ const material = React.useRef(null);
2043
+ const { size } = useThree();
2044
+ const defines = React.useMemo(() => {
2045
+ const temp = {};
2046
+ const isCubeMap = isCubeTexture(envMap);
2047
+ const w = (isCubeMap ? envMap.image[0]?.width : envMap.image.width) ?? 1024;
2048
+ const cubeSize = w / 4;
2049
+ const _lodMax = Math.floor(Math.log2(cubeSize));
2050
+ const _cubeSize = Math.pow(2, _lodMax);
2051
+ const width = 3 * Math.max(_cubeSize, 16 * 7);
2052
+ const height = 4 * _cubeSize;
2053
+ if (isCubeMap) temp.ENVMAP_TYPE_CUBEM = "";
2054
+ temp.CUBEUV_TEXEL_WIDTH = `${1 / width}`;
2055
+ temp.CUBEUV_TEXEL_HEIGHT = `${1 / height}`;
2056
+ temp.CUBEUV_MAX_MIP = `${_lodMax}.0`;
2057
+ if (aberrationStrength > 0) temp.CHROMATIC_ABERRATIONS = "";
2058
+ if (fastChroma) temp.FAST_CHROMA = "";
2059
+ return temp;
2060
+ }, [aberrationStrength, fastChroma, envMap]);
2061
+ React.useLayoutEffect(() => {
2062
+ const geometry = material.current?.__r3f?.parent?.object?.geometry;
2063
+ if (geometry) {
2064
+ const geometryNi = geometry.index ? geometry.clone().toNonIndexed() : geometry.clone();
2065
+ material.current.bvh = new MeshBVHUniformStruct();
2066
+ material.current.bvh.updateFrom(new MeshBVH(geometryNi, { strategy: SAH }));
2067
+ }
2068
+ }, []);
2069
+ useFrame(({ camera }) => {
2070
+ material.current.viewMatrixInverse = camera.matrixWorld;
2071
+ material.current.projectionMatrixInverse = camera.projectionMatrixInverse;
2072
+ });
2073
+ React.useImperativeHandle(ref, () => material.current, []);
2074
+ return /* @__PURE__ */ React.createElement(
2075
+ "meshRefractionMaterial_",
2076
+ {
2077
+ key: JSON.stringify(defines),
2078
+ defines,
2079
+ ref: material,
2080
+ resolution: [size.width, size.height],
2081
+ aberrationStrength,
2082
+ envMap,
2083
+ ...props
2084
+ }
2085
+ );
2086
+ }
2087
+ );
2033
2088
 
2034
2089
  function useIntersect(onChange) {
2035
2090
  const { isLegacy } = useThree();
@@ -3053,7 +3108,7 @@ const isWebGL2Available = () => {
3053
3108
  const Effects = /* @__PURE__ */ React.forwardRef(
3054
3109
  ({
3055
3110
  children,
3056
- multisamping = 8,
3111
+ multisampling = 8,
3057
3112
  renderIndex = 1,
3058
3113
  disableRender,
3059
3114
  disableGamma,
@@ -3080,7 +3135,7 @@ const Effects = /* @__PURE__ */ React.forwardRef(
3080
3135
  if (type === UnsignedByteType && colorSpace != null) {
3081
3136
  t.texture.colorSpace = colorSpace;
3082
3137
  }
3083
- t.samples = multisamping;
3138
+ t.samples = multisampling;
3084
3139
  return t;
3085
3140
  });
3086
3141
  React.useEffect(() => {
@@ -3690,11 +3745,11 @@ const DeviceOrientationControls = /* @__PURE__ */ React.forwardRef(
3690
3745
  );
3691
3746
 
3692
3747
  const initialModelPosition = /* @__PURE__ */ new THREE.Vector3();
3693
- const mousePosition2D = /* @__PURE__ */ new THREE.Vector2();
3694
3748
  const mousePosition3D = /* @__PURE__ */ new THREE.Vector3();
3695
3749
  const dragOffset = /* @__PURE__ */ new THREE.Vector3();
3696
3750
  const dragPlaneNormal = /* @__PURE__ */ new THREE.Vector3();
3697
3751
  const dragPlane = /* @__PURE__ */ new THREE.Plane();
3752
+ const startPointerPos = /* @__PURE__ */ new THREE.Vector3();
3698
3753
  const DragControls = React.forwardRef(
3699
3754
  ({
3700
3755
  autoTransform = true,
@@ -3710,90 +3765,132 @@ const DragControls = React.forwardRef(
3710
3765
  ...props
3711
3766
  }, fRef) => {
3712
3767
  const defaultControls = useThree((state) => state.controls);
3713
- const { camera, size, raycaster, invalidate } = useThree();
3768
+ const { camera, invalidate } = useThree();
3714
3769
  const ref = React.useRef(null);
3715
- const bind = useGesture(
3716
- {
3717
- onHover: ({ hovering }) => onHover && onHover(hovering ?? false),
3718
- onDragStart: ({ event }) => {
3719
- if (defaultControls) defaultControls.enabled = false;
3720
- const { point } = event;
3721
- ref.current.matrix.decompose(initialModelPosition, new THREE.Quaternion(), new THREE.Vector3());
3722
- mousePosition3D.copy(point);
3723
- dragOffset.copy(mousePosition3D).sub(initialModelPosition);
3724
- onDragStart && onDragStart(initialModelPosition);
3725
- invalidate();
3726
- },
3727
- onDrag: ({ xy: [dragX, dragY], intentional, event }) => {
3728
- if (!intentional) return;
3729
- if (dragConfig && dragConfig.preventOverlap) event.preventDefault();
3730
- const normalizedMouseX = (dragX - size.left) / size.width * 2 - 1;
3731
- const normalizedMouseY = -((dragY - size.top) / size.height) * 2 + 1;
3732
- mousePosition2D.set(normalizedMouseX, normalizedMouseY);
3733
- raycaster.setFromCamera(mousePosition2D, camera);
3734
- if (!axisLock) {
3735
- camera.getWorldDirection(dragPlaneNormal).negate();
3736
- } else {
3737
- switch (axisLock) {
3738
- case "x":
3739
- dragPlaneNormal.set(1, 0, 0);
3740
- break;
3741
- case "y":
3742
- dragPlaneNormal.set(0, 1, 0);
3743
- break;
3744
- case "z":
3745
- dragPlaneNormal.set(0, 0, 1);
3746
- break;
3747
- }
3748
- }
3749
- dragPlane.setFromNormalAndCoplanarPoint(dragPlaneNormal, mousePosition3D);
3750
- raycaster.ray.intersectPlane(dragPlane, mousePosition3D);
3751
- const previousLocalMatrix = ref.current.matrix.clone();
3752
- const previousWorldMatrix = ref.current.matrixWorld.clone();
3753
- const intendedNewPosition = new THREE.Vector3(
3754
- mousePosition3D.x - dragOffset.x,
3755
- mousePosition3D.y - dragOffset.y,
3756
- mousePosition3D.z - dragOffset.z
3757
- );
3758
- if (dragLimits) {
3759
- intendedNewPosition.x = dragLimits[0] ? Math.max(Math.min(intendedNewPosition.x, dragLimits[0][1]), dragLimits[0][0]) : intendedNewPosition.x;
3760
- intendedNewPosition.y = dragLimits[1] ? Math.max(Math.min(intendedNewPosition.y, dragLimits[1][1]), dragLimits[1][0]) : intendedNewPosition.y;
3761
- intendedNewPosition.z = dragLimits[2] ? Math.max(Math.min(intendedNewPosition.z, dragLimits[2][1]), dragLimits[2][0]) : intendedNewPosition.z;
3762
- }
3763
- if (autoTransform) {
3764
- ref.current.matrix.setPosition(intendedNewPosition);
3765
- const deltaLocalMatrix = ref.current.matrix.clone().multiply(previousLocalMatrix.invert());
3766
- const deltaWorldMatrix = ref.current.matrix.clone().multiply(previousWorldMatrix.invert());
3767
- onDrag && onDrag(ref.current.matrix, deltaLocalMatrix, ref.current.matrixWorld, deltaWorldMatrix);
3768
- } else {
3769
- const tempMatrix = new THREE.Matrix4().copy(ref.current.matrix);
3770
- tempMatrix.setPosition(intendedNewPosition);
3771
- const deltaLocalMatrix = tempMatrix.clone().multiply(previousLocalMatrix.invert());
3772
- const deltaWorldMatrix = tempMatrix.clone().multiply(previousWorldMatrix.invert());
3773
- onDrag && onDrag(tempMatrix, deltaLocalMatrix, ref.current.matrixWorld, deltaWorldMatrix);
3770
+ const dragState = React.useRef({
3771
+ active: false,
3772
+ intentional: false,
3773
+ // Has moved past threshold
3774
+ pointerId: -1
3775
+ });
3776
+ const threshold = dragConfig?.threshold ?? 0;
3777
+ const filterTaps = dragConfig?.filterTaps ?? true;
3778
+ const handlePointerDown = React.useCallback(
3779
+ (event) => {
3780
+ event.stopPropagation();
3781
+ event.target.setPointerCapture(event.pointerId);
3782
+ dragState.current.active = true;
3783
+ dragState.current.intentional = threshold === 0;
3784
+ dragState.current.pointerId = event.pointerId;
3785
+ if (defaultControls) defaultControls.enabled = false;
3786
+ ref.current.matrix.decompose(initialModelPosition, new THREE.Quaternion(), new THREE.Vector3());
3787
+ mousePosition3D.copy(event.point);
3788
+ startPointerPos.copy(event.point);
3789
+ dragOffset.copy(mousePosition3D).sub(initialModelPosition);
3790
+ if (!axisLock) {
3791
+ camera.getWorldDirection(dragPlaneNormal).negate();
3792
+ } else {
3793
+ switch (axisLock) {
3794
+ case "x":
3795
+ dragPlaneNormal.set(1, 0, 0);
3796
+ break;
3797
+ case "y":
3798
+ dragPlaneNormal.set(0, 1, 0);
3799
+ break;
3800
+ case "z":
3801
+ dragPlaneNormal.set(0, 0, 1);
3802
+ break;
3774
3803
  }
3775
- invalidate();
3776
- },
3777
- onDragEnd: () => {
3778
- if (defaultControls) defaultControls.enabled = true;
3779
- onDragEnd && onDragEnd();
3780
- invalidate();
3781
3804
  }
3805
+ dragPlane.setFromNormalAndCoplanarPoint(dragPlaneNormal, mousePosition3D);
3806
+ if (dragState.current.intentional) {
3807
+ onDragStart && onDragStart(initialModelPosition);
3808
+ }
3809
+ invalidate();
3782
3810
  },
3783
- {
3784
- drag: {
3785
- filterTaps: true,
3786
- threshold: 1,
3787
- ...typeof dragConfig === "object" ? dragConfig : {}
3811
+ [defaultControls, camera, axisLock, threshold, onDragStart, invalidate]
3812
+ );
3813
+ const handlePointerMove = React.useCallback(
3814
+ (event) => {
3815
+ if (!dragState.current.active || event.pointerId !== dragState.current.pointerId) return;
3816
+ const intersection = event.ray.intersectPlane(dragPlane, mousePosition3D);
3817
+ if (!intersection) return;
3818
+ if (!dragState.current.intentional) {
3819
+ const distance = mousePosition3D.distanceTo(startPointerPos);
3820
+ if (distance < threshold) return;
3821
+ dragState.current.intentional = true;
3822
+ onDragStart && onDragStart(initialModelPosition);
3788
3823
  }
3789
- }
3824
+ const previousLocalMatrix = ref.current.matrix.clone();
3825
+ const previousWorldMatrix = ref.current.matrixWorld.clone();
3826
+ const intendedNewPosition = new THREE.Vector3(
3827
+ mousePosition3D.x - dragOffset.x,
3828
+ mousePosition3D.y - dragOffset.y,
3829
+ mousePosition3D.z - dragOffset.z
3830
+ );
3831
+ if (dragLimits) {
3832
+ intendedNewPosition.x = dragLimits[0] ? Math.max(Math.min(intendedNewPosition.x, dragLimits[0][1]), dragLimits[0][0]) : intendedNewPosition.x;
3833
+ intendedNewPosition.y = dragLimits[1] ? Math.max(Math.min(intendedNewPosition.y, dragLimits[1][1]), dragLimits[1][0]) : intendedNewPosition.y;
3834
+ intendedNewPosition.z = dragLimits[2] ? Math.max(Math.min(intendedNewPosition.z, dragLimits[2][1]), dragLimits[2][0]) : intendedNewPosition.z;
3835
+ }
3836
+ if (autoTransform) {
3837
+ ref.current.matrix.setPosition(intendedNewPosition);
3838
+ const deltaLocalMatrix = ref.current.matrix.clone().multiply(previousLocalMatrix.invert());
3839
+ const deltaWorldMatrix = ref.current.matrix.clone().multiply(previousWorldMatrix.invert());
3840
+ onDrag && onDrag(ref.current.matrix, deltaLocalMatrix, ref.current.matrixWorld, deltaWorldMatrix);
3841
+ } else {
3842
+ const tempMatrix = new THREE.Matrix4().copy(ref.current.matrix);
3843
+ tempMatrix.setPosition(intendedNewPosition);
3844
+ const deltaLocalMatrix = tempMatrix.clone().multiply(previousLocalMatrix.invert());
3845
+ const deltaWorldMatrix = tempMatrix.clone().multiply(previousWorldMatrix.invert());
3846
+ onDrag && onDrag(tempMatrix, deltaLocalMatrix, ref.current.matrixWorld, deltaWorldMatrix);
3847
+ }
3848
+ invalidate();
3849
+ },
3850
+ [autoTransform, dragLimits, threshold, onDragStart, onDrag, invalidate]
3790
3851
  );
3852
+ const handlePointerUp = React.useCallback(
3853
+ (event) => {
3854
+ if (!dragState.current.active || event.pointerId !== dragState.current.pointerId) return;
3855
+ event.target.releasePointerCapture(event.pointerId);
3856
+ const wasIntentional = dragState.current.intentional;
3857
+ dragState.current.active = false;
3858
+ dragState.current.intentional = false;
3859
+ dragState.current.pointerId = -1;
3860
+ if (defaultControls) defaultControls.enabled = true;
3861
+ if (wasIntentional || !filterTaps) {
3862
+ onDragEnd && onDragEnd();
3863
+ }
3864
+ invalidate();
3865
+ },
3866
+ [defaultControls, filterTaps, onDragEnd, invalidate]
3867
+ );
3868
+ const handlePointerEnter = React.useCallback(() => {
3869
+ onHover && onHover(true);
3870
+ }, [onHover]);
3871
+ const handlePointerLeave = React.useCallback(() => {
3872
+ onHover && onHover(false);
3873
+ }, [onHover]);
3791
3874
  React.useImperativeHandle(fRef, () => ref.current, []);
3792
3875
  React.useLayoutEffect(() => {
3793
3876
  if (!matrix) return;
3794
3877
  ref.current.matrix = matrix;
3795
3878
  }, [matrix]);
3796
- return /* @__PURE__ */ React.createElement("group", { ref, ...bind(), matrix, matrixAutoUpdate: false, ...props }, children);
3879
+ return /* @__PURE__ */ React.createElement(
3880
+ "group",
3881
+ {
3882
+ ref,
3883
+ onPointerDown: handlePointerDown,
3884
+ onPointerMove: handlePointerMove,
3885
+ onPointerUp: handlePointerUp,
3886
+ onPointerEnter: handlePointerEnter,
3887
+ onPointerLeave: handlePointerLeave,
3888
+ matrix,
3889
+ matrixAutoUpdate: false,
3890
+ ...props
3891
+ },
3892
+ children
3893
+ );
3797
3894
  }
3798
3895
  );
3799
3896
 
@@ -3911,7 +4008,7 @@ function KeyboardControls({ map, children, onChange, domElement }) {
3911
4008
  source.removeEventListener("keyup", upHandler);
3912
4009
  window.removeEventListener("blur", blurHandler);
3913
4010
  };
3914
- }, [domElement, key]);
4011
+ }, [domElement, key, onChange]);
3915
4012
  return /* @__PURE__ */ React.createElement(context$8.Provider, { value: api, children });
3916
4013
  }
3917
4014
  function useKeyboardControls(sel) {
@@ -3933,20 +4030,28 @@ const MapControls = /* @__PURE__ */ React.forwardRef((props = { enableDamping: t
3933
4030
  const controls = React.useMemo(() => new MapControls$1(explCamera), [explCamera]);
3934
4031
  React.useEffect(() => {
3935
4032
  controls.connect(explDomElement);
4033
+ return () => void controls.dispose();
4034
+ }, [explDomElement, controls]);
4035
+ React.useEffect(() => {
3936
4036
  const callback = (e) => {
3937
4037
  invalidate();
3938
4038
  if (onChange) onChange(e);
3939
4039
  };
4040
+ const onStartCb = (e) => {
4041
+ if (onStart) onStart(e);
4042
+ };
4043
+ const onEndCb = (e) => {
4044
+ if (onEnd) onEnd(e);
4045
+ };
3940
4046
  controls.addEventListener("change", callback);
3941
- if (onStart) controls.addEventListener("start", onStart);
3942
- if (onEnd) controls.addEventListener("end", onEnd);
4047
+ controls.addEventListener("start", onStartCb);
4048
+ controls.addEventListener("end", onEndCb);
3943
4049
  return () => {
3944
- controls.dispose();
4050
+ controls.removeEventListener("start", onStartCb);
4051
+ controls.removeEventListener("end", onEndCb);
3945
4052
  controls.removeEventListener("change", callback);
3946
- if (onStart) controls.removeEventListener("start", onStart);
3947
- if (onEnd) controls.removeEventListener("end", onEnd);
3948
4053
  };
3949
- }, [onChange, onStart, onEnd, controls, invalidate, explDomElement]);
4054
+ }, [onChange, onStart, onEnd, controls, invalidate]);
3950
4055
  React.useEffect(() => {
3951
4056
  if (makeDefault) {
3952
4057
  const old = get().controls;
@@ -3954,7 +4059,9 @@ const MapControls = /* @__PURE__ */ React.forwardRef((props = { enableDamping: t
3954
4059
  return () => set({ controls: old });
3955
4060
  }
3956
4061
  }, [makeDefault, controls]);
3957
- useFrame(() => controls.update(), -1);
4062
+ useFrame(() => {
4063
+ if (controls.enabled) controls.update();
4064
+ }, -1);
3958
4065
  return /* @__PURE__ */ React.createElement("primitive", { ref, object: controls, enableDamping: true, ...rest });
3959
4066
  });
3960
4067
 
@@ -4080,20 +4187,16 @@ const OrbitControls = /* @__PURE__ */ React.forwardRef(
4080
4187
  onEnd,
4081
4188
  ...restProps
4082
4189
  }, ref) => {
4083
- const invalidate = useThree((state) => state.invalidate);
4084
- const defaultCamera = useThree((state) => state.camera);
4085
- const gl = useThree((state) => state.gl);
4086
- const events = useThree((state) => state.events);
4087
- const setEvents = useThree((state) => state.setEvents);
4088
- const set = useThree((state) => state.set);
4089
- const get = useThree((state) => state.get);
4090
- const performance = useThree((state) => state.performance);
4190
+ const { invalidate, camera: defaultCamera, renderer, events, setEvents, set, get, performance } = useThree();
4091
4191
  const explCamera = camera || defaultCamera;
4092
- const explDomElement = domElement || events.connected || gl.domElement;
4192
+ const explDomElement = domElement || events.connected || renderer.domElement;
4093
4193
  const controls = React.useMemo(() => new OrbitControls$1(explCamera), [explCamera]);
4094
- useFrame(() => {
4095
- if (controls.enabled) controls.update();
4096
- }, -1);
4194
+ useFrame(
4195
+ () => {
4196
+ if (controls.enabled) controls.update();
4197
+ },
4198
+ { before: "update" }
4199
+ );
4097
4200
  React.useEffect(() => {
4098
4201
  if (keyEvents) {
4099
4202
  controls.connect(keyEvents === true ? explDomElement : keyEvents);
@@ -4207,9 +4310,8 @@ function PresentationControls({
4207
4310
  azimuth = [-Infinity, Infinity],
4208
4311
  damping = 0.25
4209
4312
  }) {
4210
- const events = useThree((state) => state.events);
4211
- const gl = useThree((state) => state.gl);
4212
- const explDomElement = domElement || events.connected || gl.domElement;
4313
+ const { events, renderer, invalidate } = useThree();
4314
+ const explDomElement = domElement || events.connected || renderer.domElement;
4213
4315
  const { size } = useThree();
4214
4316
  const rPolar = React.useMemo(
4215
4317
  () => [rotation[0] + polar[0], rotation[0] + polar[1]],
@@ -4226,18 +4328,20 @@ function PresentationControls({
4226
4328
  React.useEffect(() => {
4227
4329
  if (global && cursor && enabled) {
4228
4330
  explDomElement.style.cursor = "grab";
4229
- gl.domElement.style.cursor = "";
4331
+ renderer.domElement.style.cursor = "";
4230
4332
  return () => {
4231
4333
  explDomElement.style.cursor = "default";
4232
- gl.domElement.style.cursor = "default";
4334
+ renderer.domElement.style.cursor = "default";
4233
4335
  };
4234
4336
  }
4235
4337
  }, [global, cursor, explDomElement, enabled]);
4236
4338
  const [animation] = React.useState({ scale: 1, rotation: rInitial, damping });
4237
4339
  const ref = React.useRef(null);
4238
4340
  useFrame((state, delta) => {
4239
- easing.damp3(ref.current.scale, animation.scale, animation.damping, delta);
4240
- easing.dampE(ref.current.rotation, animation.rotation, animation.damping, delta);
4341
+ let changed = false;
4342
+ changed ||= easing.damp3(ref.current.scale, animation.scale, animation.damping, delta);
4343
+ changed ||= easing.dampE(ref.current.rotation, animation.rotation, animation.damping, delta);
4344
+ if (changed) state.invalidate();
4241
4345
  });
4242
4346
  const bind = useGesture(
4243
4347
  {
@@ -4245,6 +4349,7 @@ function PresentationControls({
4245
4349
  if (cursor && !global && enabled) explDomElement.style.cursor = last ? "auto" : "grab";
4246
4350
  },
4247
4351
  onDrag: ({ down, delta: [x, y], memo: [oldY, oldX] = animation.rotation || rInitial }) => {
4352
+ invalidate();
4248
4353
  if (!enabled) return [y, x];
4249
4354
  if (cursor) explDomElement.style.cursor = down ? "grabbing" : "grab";
4250
4355
  x = MathUtils.clamp(oldX + x / size.width * Math.PI * speed, ...rAzimuth);
@@ -4253,9 +4358,12 @@ function PresentationControls({
4253
4358
  animation.rotation = snap && !down ? rInitial : [y, x, 0];
4254
4359
  animation.damping = snap && !down && typeof snap !== "boolean" ? snap : damping;
4255
4360
  return [y, x];
4361
+ },
4362
+ onDragEnd: () => {
4363
+ invalidate();
4256
4364
  }
4257
4365
  },
4258
- { target: global ? explDomElement : void 0 }
4366
+ { target: global ? explDomElement : void 0, enabled }
4259
4367
  );
4260
4368
  return /* @__PURE__ */ React.createElement("group", { ref, ...bind?.() }, children);
4261
4369
  }
@@ -4264,6 +4372,10 @@ function roundToPixelRatio(value) {
4264
4372
  const ratio = window.devicePixelRatio || 1;
4265
4373
  return Math.round(value * ratio) / ratio;
4266
4374
  }
4375
+ function getMaxAnisotropy(renderer) {
4376
+ const r = renderer;
4377
+ return r?.getMaxAnisotropy?.() ?? r?.capabilities?.getMaxAnisotropy?.() ?? 1;
4378
+ }
4267
4379
 
4268
4380
  const context$7 = /* @__PURE__ */ React.createContext(null);
4269
4381
  const rootCache = /* @__PURE__ */ new WeakMap();
@@ -4290,6 +4402,7 @@ function ScrollControls({
4290
4402
  const [fixed] = React.useState(() => document.createElement("div"));
4291
4403
  const target = gl.domElement.parentNode;
4292
4404
  const scroll = React.useRef(0);
4405
+ const disableScrollUpdate = React.useRef(false);
4293
4406
  const state = React.useMemo(() => {
4294
4407
  const state2 = {
4295
4408
  el,
@@ -4371,16 +4484,20 @@ function ScrollControls({
4371
4484
  const scrollLength = el[horizontal ? "scrollWidth" : "scrollHeight"];
4372
4485
  const scrollThreshold = scrollLength - containerLength;
4373
4486
  let current = 0;
4374
- let disableScroll = true;
4487
+ let disableScroll = false;
4375
4488
  let firstRun = true;
4376
4489
  const onScroll = () => {
4377
4490
  if (!enabled || firstRun) return;
4491
+ if (disableScrollUpdate.current) {
4492
+ disableScrollUpdate.current = false;
4493
+ return;
4494
+ }
4378
4495
  invalidate();
4379
4496
  current = el[horizontal ? "scrollLeft" : "scrollTop"];
4380
4497
  scroll.current = current / scrollThreshold;
4381
4498
  if (infinite) {
4382
4499
  if (!disableScroll) {
4383
- if (current >= scrollThreshold) {
4500
+ if (current >= scrollThreshold - 1) {
4384
4501
  const damp = 1 - state.offset;
4385
4502
  el[horizontal ? "scrollLeft" : "scrollTop"] = 1;
4386
4503
  scroll.current = state.offset = -damp;
@@ -4397,11 +4514,34 @@ function ScrollControls({
4397
4514
  };
4398
4515
  el.addEventListener("scroll", onScroll, { passive: true });
4399
4516
  requestAnimationFrame(() => firstRun = false);
4400
- const onWheel = (e) => el.scrollLeft += e.deltaY / 2;
4401
- if (horizontal) el.addEventListener("wheel", onWheel, { passive: true });
4517
+ const onResize = () => {
4518
+ if (!el) return;
4519
+ const scrollLength2 = el[horizontal ? "scrollWidth" : "scrollHeight"];
4520
+ const containerLength2 = el[horizontal ? "clientWidth" : "clientHeight"];
4521
+ disableScrollUpdate.current = true;
4522
+ el[horizontal ? "scrollLeft" : "scrollTop"] = state.offset * (scrollLength2 - containerLength2);
4523
+ };
4524
+ const onWheel = (e) => {
4525
+ if (horizontal) {
4526
+ const previous = el.scrollLeft;
4527
+ el.scrollLeft += e.deltaY / 2;
4528
+ if (infinite && previous === el.scrollLeft) {
4529
+ onScroll();
4530
+ }
4531
+ } else if (infinite) {
4532
+ if (el.scrollTop <= 1 && e.deltaY < 0) {
4533
+ onScroll();
4534
+ } else if (el.scrollTop >= el.scrollHeight - el.clientHeight - 1 && e.deltaY > 0) {
4535
+ onScroll();
4536
+ }
4537
+ }
4538
+ };
4539
+ if (horizontal || infinite) el.addEventListener("wheel", onWheel, { passive: true });
4540
+ if (horizontal) window.addEventListener("resize", onResize);
4402
4541
  return () => {
4403
4542
  el.removeEventListener("scroll", onScroll);
4404
- if (horizontal) el.removeEventListener("wheel", onWheel);
4543
+ if (horizontal || infinite) el.removeEventListener("wheel", onWheel);
4544
+ if (horizontal) window.removeEventListener("resize", onResize);
4405
4545
  };
4406
4546
  }
4407
4547
  }, [el, events, size, infinite, state, invalidate, horizontal, enabled]);
@@ -4818,10 +4958,10 @@ const CloudInstance = /* @__PURE__ */ React.forwardRef(
4818
4958
  return /* @__PURE__ */ React.createElement("group", { ref, ...props });
4819
4959
  }
4820
4960
  );
4821
- const Cloud = /* @__PURE__ */ React.forwardRef((props, fref) => {
4961
+ const Cloud = /* @__PURE__ */ React.forwardRef(({ materialFactory, ...props }, fref) => {
4822
4962
  const parent = React.useContext(context$6);
4823
4963
  if (parent) return /* @__PURE__ */ React.createElement(CloudInstance, { ref: fref, ...props });
4824
- return /* @__PURE__ */ React.createElement(Clouds, null, /* @__PURE__ */ React.createElement(CloudInstance, { ref: fref, ...props }));
4964
+ return /* @__PURE__ */ React.createElement(Clouds, { materialFactory }, /* @__PURE__ */ React.createElement(CloudInstance, { ref: fref, ...props }));
4825
4965
  });
4826
4966
 
4827
4967
  const CatmullRomLine = /* @__PURE__ */ React.forwardRef(function CatmullRomLine2({
@@ -5343,7 +5483,7 @@ const Text3D = /* @__PURE__ */ React.forwardRef(
5343
5483
  return {
5344
5484
  font,
5345
5485
  size,
5346
- height,
5486
+ depth: height,
5347
5487
  bevelThickness,
5348
5488
  bevelSize,
5349
5489
  bevelEnabled,
@@ -5526,7 +5666,7 @@ const FaceMaterial = ({
5526
5666
  "meshBasicMaterial",
5527
5667
  {
5528
5668
  map: texture,
5529
- "map-anisotropy": renderer.getMaxAnisotropy() || 1,
5669
+ "map-anisotropy": getMaxAnisotropy(renderer),
5530
5670
  attach: `material-${index}`,
5531
5671
  color: hover ? hoverColor : "white",
5532
5672
  transparent: true,
@@ -5637,7 +5777,7 @@ function AxisHead({
5637
5777
  "spriteMaterial",
5638
5778
  {
5639
5779
  map: texture,
5640
- "map-anisotropy": renderer.getMaxAnisotropy() || 1,
5780
+ "map-anisotropy": getMaxAnisotropy(renderer),
5641
5781
  alphaTest: 0.3,
5642
5782
  opacity: label ? 1 : 0.75,
5643
5783
  toneMapped: false
@@ -5782,6 +5922,7 @@ const Html = /* @__PURE__ */ React.forwardRef(
5782
5922
  }, ref) => {
5783
5923
  const { gl, camera, scene, size, raycaster, events, viewport } = useThree();
5784
5924
  const forceEven = useThree((state) => state.internal.forceEven);
5925
+ const { frustum } = useThree();
5785
5926
  const [el] = React.useState(() => document.createElement(as));
5786
5927
  const root = React.useRef(null);
5787
5928
  const group = React.useRef(null);
@@ -5811,22 +5952,18 @@ const Html = /* @__PURE__ */ React.forwardRef(
5811
5952
  if (group.current) {
5812
5953
  if (!root.current) {
5813
5954
  root.current = ReactDOM.createRoot(el);
5814
- }
5815
- scene.updateMatrixWorld();
5816
- if (transform) {
5817
- el.style.cssText = `position:absolute;top:0;left:0;pointer-events:none;overflow:hidden;`;
5818
- } else {
5819
- el.style.cssText = `position:absolute;top:0;left:0;transform:translate3d(0,-9999px,0);transform-origin:0 0;`;
5955
+ scene.updateMatrixWorld();
5956
+ if (transform) {
5957
+ el.style.cssText = `position:absolute;top:0;left:0;pointer-events:none;overflow:hidden;`;
5958
+ } else {
5959
+ el.style.cssText = `position:absolute;top:0;left:0;transform:translate3d(0,-9999px,0);transform-origin:0 0;`;
5960
+ }
5820
5961
  }
5821
5962
  if (target) {
5822
5963
  if (prepend) target.prepend(el);
5823
5964
  else target.appendChild(el);
5824
5965
  }
5825
5966
  return () => {
5826
- if (root.current) {
5827
- root.current.unmount();
5828
- root.current = null;
5829
- }
5830
5967
  if (target && target.contains(el)) {
5831
5968
  target.removeChild(el);
5832
5969
  }
@@ -5960,8 +6097,7 @@ const Html = /* @__PURE__ */ React.forwardRef(
5960
6097
  if (transformOuterRef.current) {
5961
6098
  const el2 = transformOuterRef.current.children[0];
5962
6099
  if (el2?.clientWidth && el2?.clientHeight) {
5963
- const { isOrthographicCamera } = camera;
5964
- if (isOrthographicCamera || geometry) {
6100
+ if (geometry) {
5965
6101
  if (props.scale) {
5966
6102
  if (!Array.isArray(props.scale)) {
5967
6103
  occlusionMeshRef.current.scale.setScalar(1 / props.scale);
@@ -5993,64 +6129,7 @@ const Html = /* @__PURE__ */ React.forwardRef(
5993
6129
  }
5994
6130
  }
5995
6131
  });
5996
- const shaders = React.useMemo(
5997
- () => ({
5998
- vertexShader: !transform ? (
5999
- /* glsl */
6000
- `
6001
- /*
6002
- This shader is from the THREE's SpriteMaterial.
6003
- We need to turn the backing plane into a Sprite
6004
- (make it always face the camera) if "transfrom"
6005
- is false.
6006
- */
6007
- #include <common>
6008
-
6009
- void main() {
6010
- vec2 center = vec2(0., 1.);
6011
- float rotation = 0.0;
6012
-
6013
- // This is somewhat arbitrary, but it seems to work well
6014
- // Need to figure out how to derive this dynamically if it even matters
6015
- float size = 0.03;
6016
-
6017
- vec4 mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );
6018
- vec2 scale;
6019
- scale.x = length( vec3( modelMatrix[ 0 ].x, modelMatrix[ 0 ].y, modelMatrix[ 0 ].z ) );
6020
- scale.y = length( vec3( modelMatrix[ 1 ].x, modelMatrix[ 1 ].y, modelMatrix[ 1 ].z ) );
6021
-
6022
- bool isPerspective = isPerspectiveMatrix( projectionMatrix );
6023
- if ( isPerspective ) scale *= - mvPosition.z;
6024
-
6025
- vec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale * size;
6026
- vec2 rotatedPosition;
6027
- rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;
6028
- rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;
6029
- mvPosition.xy += rotatedPosition;
6030
-
6031
- gl_Position = projectionMatrix * mvPosition;
6032
- }
6033
- `
6034
- ) : void 0,
6035
- fragmentShader: (
6036
- /* glsl */
6037
- `
6038
- void main() {
6039
- gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
6040
- }
6041
- `
6042
- )
6043
- }),
6044
- [transform]
6045
- );
6046
- return /* @__PURE__ */ React.createElement("group", { ...props, ref: group }, occlude && !isRayCastOcclusion && /* @__PURE__ */ React.createElement("mesh", { castShadow, receiveShadow, ref: occlusionMeshRef }, geometry || /* @__PURE__ */ React.createElement("planeGeometry", null), material || /* @__PURE__ */ React.createElement(
6047
- "shaderMaterial",
6048
- {
6049
- side: DoubleSide,
6050
- vertexShader: shaders.vertexShader,
6051
- fragmentShader: shaders.fragmentShader
6052
- }
6053
- )));
6132
+ return /* @__PURE__ */ React.createElement("group", { ...props, ref: group }, occlude && !isRayCastOcclusion && /* @__PURE__ */ React.createElement("mesh", { castShadow, receiveShadow, ref: occlusionMeshRef }, geometry || /* @__PURE__ */ React.createElement("planeGeometry", null), material));
6054
6133
  }
6055
6134
  );
6056
6135
 
@@ -6903,6 +6982,7 @@ const mW = /* @__PURE__ */ new THREE.Matrix4();
6903
6982
  const mL = /* @__PURE__ */ new THREE.Matrix4();
6904
6983
  const mL0Inv = /* @__PURE__ */ new THREE.Matrix4();
6905
6984
  const mdL = /* @__PURE__ */ new THREE.Matrix4();
6985
+ const mdW = /* @__PURE__ */ new THREE.Matrix4();
6906
6986
  const mG = /* @__PURE__ */ new THREE.Matrix4();
6907
6987
  const bb = /* @__PURE__ */ new THREE.Box3();
6908
6988
  const bbObj = /* @__PURE__ */ new THREE.Box3();
@@ -7017,7 +7097,8 @@ const PivotControls = /* @__PURE__ */ React.forwardRef(
7017
7097
  onDragStart && onDragStart(props2);
7018
7098
  invalidate();
7019
7099
  },
7020
- onDrag: (mdW) => {
7100
+ onDrag: (mdW_) => {
7101
+ mdW.copy(mdW_);
7021
7102
  mP.copy(parentRef.current.matrixWorld);
7022
7103
  mPInv.copy(mP).invert();
7023
7104
  mW.copy(mW0).premultiply(mdW);
@@ -7029,12 +7110,12 @@ const PivotControls = /* @__PURE__ */ React.forwardRef(
7029
7110
  }
7030
7111
  const target = resolveObject(object);
7031
7112
  if (target) target.matrix.copy(mL);
7032
- onDrag && onDrag(mL, mdL, mW, mdW);
7113
+ onDrag && onDrag({ local: mL, deltaLocal: mdL, world: mW, deltaWorld: mdW });
7033
7114
  invalidate();
7034
7115
  },
7035
7116
  onDragEnd: () => {
7036
7117
  dragState.current = null;
7037
- if (onDragEnd) onDragEnd();
7118
+ if (onDragEnd) onDragEnd({ local: mL, deltaLocal: mdL, world: mW, deltaWorld: mdW });
7038
7119
  invalidate();
7039
7120
  },
7040
7121
  onHover: (props2) => {
@@ -7145,7 +7226,9 @@ const TransformControls = /* @__PURE__ */ React.forwardRef(
7145
7226
  const group = React.useRef(null);
7146
7227
  React.useLayoutEffect(() => {
7147
7228
  if (object) {
7148
- controls.attach(object instanceof THREE.Object3D ? object : object.current);
7229
+ controls.attach(
7230
+ object instanceof THREE.Object3D ? object : object.current
7231
+ );
7149
7232
  } else if (group.current instanceof THREE.Object3D) {
7150
7233
  controls.attach(group.current);
7151
7234
  }
@@ -7241,7 +7324,7 @@ const PointMaterial = /* @__PURE__ */ React.forwardRef((props, ref) => {
7241
7324
  return /* @__PURE__ */ React.createElement("primitive", { ...props, object: material, ref, attach: "material" });
7242
7325
  });
7243
7326
 
7244
- const PositionalAudio = /* @__PURE__ */ React.forwardRef(({ url, distance = 1, loop = true, autoplay, ...props }, ref) => {
7327
+ const PositionalAudio = /* @__PURE__ */ React.forwardRef(({ url, distance = 1, loop = false, autoplay, ...props }, ref) => {
7245
7328
  const sound = React.useRef(null);
7246
7329
  React.useImperativeHandle(ref, () => sound.current, []);
7247
7330
  const camera = useThree(({ camera: camera2 }) => camera2);
@@ -7253,7 +7336,9 @@ const PositionalAudio = /* @__PURE__ */ React.forwardRef(({ url, distance = 1, l
7253
7336
  _sound.setBuffer(Array.isArray(buffer) ? buffer[0] : buffer);
7254
7337
  _sound.setRefDistance(distance);
7255
7338
  _sound.setLoop(loop);
7256
- if (autoplay && !_sound.isPlaying) _sound.play();
7339
+ if (autoplay && !_sound.isPlaying) {
7340
+ _sound.play();
7341
+ }
7257
7342
  }
7258
7343
  }, [buffer, camera, distance, loop]);
7259
7344
  React.useEffect(() => {
@@ -7322,7 +7407,6 @@ const tempMatrix = /* @__PURE__ */ new THREE.Matrix4();
7322
7407
  const translation = /* @__PURE__ */ new THREE.Vector3();
7323
7408
  const rotation = /* @__PURE__ */ new THREE.Quaternion();
7324
7409
  const scale = /* @__PURE__ */ new THREE.Vector3();
7325
- const isInstancedBufferAttribute = (attr) => attr.isInstancedBufferAttribute;
7326
7410
  const Instance = /* @__PURE__ */ React.forwardRef(({ context, children, ...props }, ref) => {
7327
7411
  React.useMemo(() => extend({ PositionMesh }), []);
7328
7412
  const group = React.useRef(null);
@@ -7342,24 +7426,20 @@ const Instances = /* @__PURE__ */ React.forwardRef(({ context, children, range,
7342
7426
  const parentRef = React.useRef(null);
7343
7427
  React.useImperativeHandle(ref, () => parentRef.current, []);
7344
7428
  const [instances, setInstances] = React.useState([]);
7429
+ const boundsDirty = React.useRef(true);
7345
7430
  const [[matrices, colors]] = React.useState(() => {
7346
7431
  const mArray = new Float32Array(limit * 16);
7347
7432
  for (let i = 0; i < limit; i++) tempMatrix.identity().toArray(mArray, i * 16);
7348
7433
  return [mArray, new Float32Array([...new Array(limit * 3)].map(() => 1))];
7349
7434
  });
7350
7435
  React.useEffect(() => {
7351
- parentRef.current.instanceMatrix.needsUpdate = true;
7436
+ if (parentRef.current) parentRef.current.instanceMatrix.needsUpdate = true;
7352
7437
  });
7353
7438
  let iterations = 0;
7354
7439
  let count = 0;
7355
- const attributes = React.useRef([]);
7356
- React.useLayoutEffect(() => {
7357
- attributes.current = Object.entries(parentRef.current.geometry.attributes).filter(
7358
- ([_name, value]) => isInstancedBufferAttribute(value)
7359
- );
7360
- });
7361
7440
  useFrame(() => {
7362
7441
  if (frames === Infinity || iterations < frames) {
7442
+ if (!parentRef.current) return;
7363
7443
  parentRef.current.updateMatrix();
7364
7444
  parentRef.current.updateMatrixWorld();
7365
7445
  parentMatrix.copy(parentRef.current.matrixWorld).invert();
@@ -7380,13 +7460,22 @@ const Instances = /* @__PURE__ */ React.forwardRef(({ context, children, range,
7380
7460
  }
7381
7461
  iterations++;
7382
7462
  }
7463
+ if (boundsDirty.current && (frames === Infinity || iterations === frames)) {
7464
+ boundsDirty.current = false;
7465
+ if (parentRef.current.boundingBox) parentRef.current.computeBoundingBox();
7466
+ if (parentRef.current.boundingSphere) parentRef.current.computeBoundingSphere();
7467
+ }
7383
7468
  });
7384
7469
  const api = React.useMemo(
7385
7470
  () => ({
7386
7471
  getParent: () => parentRef,
7387
7472
  subscribe: (ref2) => {
7388
7473
  setInstances((instances2) => [...instances2, ref2]);
7389
- return () => setInstances((instances2) => instances2.filter((item) => item.current !== ref2.current));
7474
+ boundsDirty.current = true;
7475
+ return () => {
7476
+ setInstances((instances2) => instances2.filter((item) => item.current !== ref2.current));
7477
+ boundsDirty.current = true;
7478
+ };
7390
7479
  }
7391
7480
  }),
7392
7481
  []
@@ -7411,10 +7500,23 @@ const Merged = /* @__PURE__ */ React.forwardRef(function Merged2({ meshes, child
7411
7500
  if (!isArray) {
7412
7501
  for (const key of Object.keys(meshes)) if (!meshes[key].isMesh) delete meshes[key];
7413
7502
  }
7414
- const render = (args) => isArray ? children(...args) : children(
7415
- Object.keys(meshes).filter((key) => meshes[key].isMesh).reduce((acc, key, i) => ({ ...acc, [key]: args[i] }), {})
7416
- );
7417
- const components = (isArray ? meshes : Object.values(meshes)).map(({ geometry, material }) => /* @__PURE__ */ React.createElement(Instances, { key: geometry.uuid, geometry, material, ...props }));
7503
+ const render = (args) => {
7504
+ if (isArray) {
7505
+ return children(
7506
+ ...args
7507
+ );
7508
+ }
7509
+ const instanceMap = {};
7510
+ const keys = Object.keys(meshes).filter((key) => meshes[key].isMesh);
7511
+ keys.forEach((key, i) => {
7512
+ instanceMap[key] = args[i];
7513
+ });
7514
+ return children(instanceMap);
7515
+ };
7516
+ const components = (isArray ? meshes : Object.values(meshes)).map((mesh) => {
7517
+ const { geometry, material } = mesh;
7518
+ return /* @__PURE__ */ React.createElement(Instances, { key: geometry.uuid, geometry, material, ...props });
7519
+ });
7418
7520
  return /* @__PURE__ */ React.createElement("group", { ref }, renderRecursive(render, components));
7419
7521
  });
7420
7522
  function renderRecursive(render, components, results = []) {
@@ -8488,12 +8590,13 @@ function Sampler({ children, weight, transform, instances, mesh, count = 16, ...
8488
8590
  return /* @__PURE__ */ React.createElement("group", { ref: group, ...props }, children);
8489
8591
  }
8490
8592
 
8593
+ const pathStyle = (path) => path.userData?.style;
8491
8594
  const Svg = /* @__PURE__ */ forwardRef(
8492
8595
  function R3FSvg({ src, skipFill, skipStrokes, fillMaterial, strokeMaterial, fillMeshProps, strokeMeshProps, ...props }, ref) {
8493
8596
  const svg = useLoader(SVGLoader, !src.startsWith("<svg") ? src : `data:image/svg+xml;utf8,${src}`);
8494
8597
  const strokeGeometries = useMemo(
8495
8598
  () => skipStrokes ? [] : svg.paths.map(
8496
- (path) => path.userData?.style.stroke === void 0 || path.userData.style.stroke === "none" ? null : path.subPaths.map((subPath) => SVGLoader.pointsToStroke(subPath.getPoints(), path.userData.style))
8599
+ (path) => pathStyle(path)?.stroke === void 0 || pathStyle(path).stroke === "none" ? null : path.subPaths.map((subPath) => SVGLoader.pointsToStroke(subPath.getPoints(), pathStyle(path)))
8497
8600
  ),
8498
8601
  [svg, skipStrokes]
8499
8602
  );
@@ -8501,21 +8604,21 @@ const Svg = /* @__PURE__ */ forwardRef(
8501
8604
  return () => strokeGeometries.forEach((group) => group && group.map((g) => g.dispose()));
8502
8605
  }, [strokeGeometries]);
8503
8606
  let renderOrder = 0;
8504
- return /* @__PURE__ */ React.createElement("object3D", { ref, ...props }, /* @__PURE__ */ React.createElement("object3D", { scale: [1, -1, 1] }, svg.paths.map((path, p) => /* @__PURE__ */ React.createElement(Fragment, { key: p }, !skipFill && path.userData?.style.fill !== void 0 && path.userData.style.fill !== "none" && SVGLoader.createShapes(path).map((shape, s) => /* @__PURE__ */ React.createElement("mesh", { key: s, ...fillMeshProps, renderOrder: renderOrder++ }, /* @__PURE__ */ React.createElement("shapeGeometry", { args: [shape] }), /* @__PURE__ */ React.createElement(
8607
+ return /* @__PURE__ */ React.createElement("object3D", { ref, ...props }, /* @__PURE__ */ React.createElement("object3D", { scale: [1, -1, 1] }, svg.paths.map((path, p) => /* @__PURE__ */ React.createElement(Fragment, { key: p }, !skipFill && pathStyle(path)?.fill !== void 0 && pathStyle(path).fill !== "none" && SVGLoader.createShapes(path).map((shape, s) => /* @__PURE__ */ React.createElement("mesh", { key: s, ...fillMeshProps, renderOrder: renderOrder++ }, /* @__PURE__ */ React.createElement("shapeGeometry", { args: [shape] }), /* @__PURE__ */ React.createElement(
8505
8608
  "meshBasicMaterial",
8506
8609
  {
8507
- color: path.userData.style.fill,
8508
- opacity: path.userData.style.fillOpacity,
8610
+ color: pathStyle(path).fill,
8611
+ opacity: pathStyle(path).fillOpacity,
8509
8612
  transparent: true,
8510
8613
  side: DoubleSide,
8511
8614
  depthWrite: false,
8512
8615
  ...fillMaterial
8513
8616
  }
8514
- ))), !skipStrokes && path.userData?.style.stroke !== void 0 && path.userData.style.stroke !== "none" && path.subPaths.map((_subPath, s) => /* @__PURE__ */ React.createElement("mesh", { key: s, geometry: strokeGeometries[p][s], ...strokeMeshProps, renderOrder: renderOrder++ }, /* @__PURE__ */ React.createElement(
8617
+ ))), !skipStrokes && pathStyle(path)?.stroke !== void 0 && pathStyle(path).stroke !== "none" && path.subPaths.map((_subPath, s) => /* @__PURE__ */ React.createElement("mesh", { key: s, geometry: strokeGeometries[p][s], ...strokeMeshProps, renderOrder: renderOrder++ }, /* @__PURE__ */ React.createElement(
8515
8618
  "meshBasicMaterial",
8516
8619
  {
8517
- color: path.userData.style.stroke,
8518
- opacity: path.userData.style.strokeOpacity,
8620
+ color: pathStyle(path).stroke,
8621
+ opacity: pathStyle(path).strokeOpacity,
8519
8622
  transparent: true,
8520
8623
  side: DoubleSide,
8521
8624
  depthWrite: false,
@@ -8738,8 +8841,8 @@ function useVideoTexture(srcOrSrcObject, {
8738
8841
  });
8739
8842
  if (src && IS_BROWSER && src.endsWith(".m3u8")) {
8740
8843
  const hls = hlsRef.current = await getHls(hlsConfig);
8741
- if (hls) {
8742
- hls.on(Events.MEDIA_ATTACHED, () => void hls.loadSource(src));
8844
+ if (hls && _HLSModule) {
8845
+ hls.on(_HLSModule.Events.MEDIA_ATTACHED, () => void hls.loadSource(src));
8743
8846
  hls.attachMedia(video2);
8744
8847
  }
8745
8848
  }
@@ -8959,9 +9062,125 @@ function Fbx({
8959
9062
  return /* @__PURE__ */ React.createElement(Clone, { ...props, object });
8960
9063
  }
8961
9064
 
9065
+ const SERVICE_KEY = "__drei_ktx2_service__";
9066
+ const DEFAULT_TRANSCODER_PATH = "https://cdn.jsdelivr.net/gh/pmndrs/drei-assets@master/basis/";
9067
+ class KTX2LoaderServiceImpl {
9068
+ loader = null;
9069
+ renderer = null;
9070
+ _isReady = false;
9071
+ transcoderPath = DEFAULT_TRANSCODER_PATH;
9072
+ pendingCallbacks = [];
9073
+ /**
9074
+ * Get or create the singleton KTX2Loader instance.
9075
+ * The loader is created lazily on first access.
9076
+ */
9077
+ getLoader() {
9078
+ if (!this.loader) {
9079
+ this.loader = new KTX2Loader();
9080
+ this.loader.setTranscoderPath(this.transcoderPath);
9081
+ }
9082
+ return this.loader;
9083
+ }
9084
+ /**
9085
+ * Register a renderer and call detectSupport.
9086
+ * Should be called from within React context (useLayoutEffect).
9087
+ * Flushes any queued callbacks.
9088
+ */
9089
+ registerRenderer(renderer) {
9090
+ if (this._isReady && this.renderer === renderer) {
9091
+ return;
9092
+ }
9093
+ this.renderer = renderer;
9094
+ const loader = this.getLoader();
9095
+ loader.detectSupport(renderer);
9096
+ this._isReady = true;
9097
+ this.flushPendingCallbacks();
9098
+ }
9099
+ /**
9100
+ * Check if the service is ready (has renderer registered).
9101
+ */
9102
+ isReady() {
9103
+ return this._isReady;
9104
+ }
9105
+ /**
9106
+ * Get the registered renderer, if any.
9107
+ */
9108
+ getRenderer() {
9109
+ return this.renderer;
9110
+ }
9111
+ /**
9112
+ * Register a callback to run when renderer becomes available.
9113
+ * If renderer is already available, runs immediately.
9114
+ */
9115
+ onReady(callback) {
9116
+ if (this._isReady) {
9117
+ callback();
9118
+ } else {
9119
+ this.pendingCallbacks.push(callback);
9120
+ }
9121
+ }
9122
+ /**
9123
+ * Process all pending callbacks.
9124
+ */
9125
+ flushPendingCallbacks() {
9126
+ const callbacks = this.pendingCallbacks;
9127
+ this.pendingCallbacks = [];
9128
+ for (const callback of callbacks) {
9129
+ try {
9130
+ callback();
9131
+ } catch (error) {
9132
+ console.error("KTX2LoaderService: Error in pending callback:", error);
9133
+ }
9134
+ }
9135
+ }
9136
+ /**
9137
+ * Set the transcoder path for basis/ktx2 decoding.
9138
+ */
9139
+ setTranscoderPath(path) {
9140
+ this.transcoderPath = path;
9141
+ if (this.loader) {
9142
+ this.loader.setTranscoderPath(path);
9143
+ }
9144
+ }
9145
+ /**
9146
+ * Get the current transcoder path.
9147
+ */
9148
+ getTranscoderPath() {
9149
+ return this.transcoderPath;
9150
+ }
9151
+ /**
9152
+ * Dispose of the loader and reset state.
9153
+ * Useful for testing or cleanup.
9154
+ */
9155
+ dispose() {
9156
+ if (this.loader) {
9157
+ this.loader.dispose();
9158
+ }
9159
+ const path = this.transcoderPath;
9160
+ this.loader = null;
9161
+ this.renderer = null;
9162
+ this._isReady = false;
9163
+ this.transcoderPath = path;
9164
+ this.pendingCallbacks = [];
9165
+ }
9166
+ }
9167
+ const KTX2LoaderService = globalThis[SERVICE_KEY] || (globalThis[SERVICE_KEY] = new KTX2LoaderServiceImpl());
9168
+
8962
9169
  let dracoLoader = null;
8963
9170
  let decoderPath = "https://www.gstatic.com/draco/versioned/decoders/1.5.5/";
8964
- function extensions(useDraco = true, useMeshopt = true, extendLoader) {
9171
+ let legacyApiWarned = false;
9172
+ function isOptionsObject(arg) {
9173
+ return typeof arg === "object" && arg !== null && !Array.isArray(arg);
9174
+ }
9175
+ function warnLegacyApi() {
9176
+ if (!legacyApiWarned) {
9177
+ legacyApiWarned = true;
9178
+ console.warn(
9179
+ "useGLTF: Boolean arguments are deprecated. Use options object instead:\n useGLTF(path, { draco: true, meshopt: true, ktx2: true })"
9180
+ );
9181
+ }
9182
+ }
9183
+ function extensions(useDraco = true, useMeshopt = true, useKTX2 = false, extendLoader, renderer) {
8965
9184
  return (loader) => {
8966
9185
  if (extendLoader) {
8967
9186
  extendLoader(loader);
@@ -8977,36 +9196,110 @@ function extensions(useDraco = true, useMeshopt = true, extendLoader) {
8977
9196
  const decoder = typeof MeshoptDecoder === "function" ? MeshoptDecoder() : MeshoptDecoder;
8978
9197
  loader.setMeshoptDecoder(decoder);
8979
9198
  }
9199
+ if (useKTX2) {
9200
+ const ktx2Loader = KTX2LoaderService.getLoader();
9201
+ if (typeof useKTX2 === "string") {
9202
+ ktx2Loader.setTranscoderPath(useKTX2);
9203
+ }
9204
+ if (renderer) {
9205
+ ktx2Loader.detectSupport(renderer);
9206
+ }
9207
+ loader.setKTX2Loader(ktx2Loader);
9208
+ }
8980
9209
  };
8981
9210
  }
8982
- const useGLTF = (path, useDraco, useMeshopt, extendLoader) => useLoader(GLTFLoader, path, extensions(useDraco, useMeshopt, extendLoader));
8983
- useGLTF.preload = (path, useDraco, useMeshopt, extendLoader) => useLoader.preload(GLTFLoader, path, extensions(useDraco, useMeshopt, extendLoader));
9211
+ function useGLTF(path, optionsOrDraco, useMeshoptArg, useKTX2Arg, extendLoaderArg) {
9212
+ let draco = true;
9213
+ let meshopt = true;
9214
+ let ktx2 = false;
9215
+ let extendLoader;
9216
+ if (isOptionsObject(optionsOrDraco)) {
9217
+ draco = optionsOrDraco.draco ?? true;
9218
+ meshopt = optionsOrDraco.meshopt ?? true;
9219
+ ktx2 = optionsOrDraco.ktx2 ?? false;
9220
+ extendLoader = optionsOrDraco.extendLoader;
9221
+ } else if (optionsOrDraco !== void 0) {
9222
+ warnLegacyApi();
9223
+ draco = optionsOrDraco;
9224
+ meshopt = useMeshoptArg ?? true;
9225
+ ktx2 = useKTX2Arg ?? false;
9226
+ extendLoader = extendLoaderArg;
9227
+ }
9228
+ const renderer = useThree((state) => state.renderer);
9229
+ useLayoutEffect(() => {
9230
+ if (ktx2) {
9231
+ KTX2LoaderService.registerRenderer(renderer);
9232
+ }
9233
+ }, [ktx2, renderer]);
9234
+ return useLoader(GLTFLoader, path, extensions(draco, meshopt, ktx2, extendLoader, ktx2 ? renderer : void 0));
9235
+ }
9236
+ useGLTF.preload = (path, optionsOrDraco, useMeshoptArg, useKTX2Arg, extendLoaderArg) => {
9237
+ let draco = true;
9238
+ let meshopt = true;
9239
+ let ktx2 = false;
9240
+ let extendLoader;
9241
+ if (isOptionsObject(optionsOrDraco)) {
9242
+ draco = optionsOrDraco.draco ?? true;
9243
+ meshopt = optionsOrDraco.meshopt ?? true;
9244
+ ktx2 = optionsOrDraco.ktx2 ?? false;
9245
+ extendLoader = optionsOrDraco.extendLoader;
9246
+ } else if (optionsOrDraco !== void 0) {
9247
+ warnLegacyApi();
9248
+ draco = optionsOrDraco;
9249
+ meshopt = useMeshoptArg ?? true;
9250
+ ktx2 = useKTX2Arg ?? false;
9251
+ extendLoader = extendLoaderArg;
9252
+ }
9253
+ if (ktx2) {
9254
+ KTX2LoaderService.onReady(() => {
9255
+ const renderer = KTX2LoaderService.getRenderer();
9256
+ useLoader.preload(GLTFLoader, path, extensions(draco, meshopt, ktx2, extendLoader, renderer));
9257
+ });
9258
+ } else {
9259
+ useLoader.preload(GLTFLoader, path, extensions(draco, meshopt, false, extendLoader));
9260
+ }
9261
+ };
8984
9262
  useGLTF.clear = (path) => useLoader.clear(GLTFLoader, path);
8985
9263
  useGLTF.setDecoderPath = (path) => {
8986
9264
  decoderPath = path;
8987
9265
  };
9266
+ useGLTF.setKTX2TranscoderPath = (path) => {
9267
+ KTX2LoaderService.setTranscoderPath(path);
9268
+ };
8988
9269
  const Gltf = /* @__PURE__ */ React.forwardRef(
8989
- ({ src, useDraco, useMeshOpt, extendLoader, ...props }, ref) => {
8990
- const { scene } = useGLTF(src, useDraco, useMeshOpt, extendLoader);
9270
+ ({ src, useDraco, useMeshOpt, extendLoader, options, ...props }, ref) => {
9271
+ const opts = options ?? {
9272
+ draco: useDraco,
9273
+ meshopt: useMeshOpt,
9274
+ extendLoader
9275
+ };
9276
+ const { scene } = useGLTF(src, opts);
8991
9277
  return /* @__PURE__ */ React.createElement(Clone, { ref, ...props, object: scene });
8992
9278
  }
8993
9279
  );
8994
9280
 
8995
- let transcoderPath = "https://cdn.jsdelivr.net/gh/pmndrs/drei-assets@master/basis/";
8996
9281
  function useKTX2(input, basisPath = true, extendLoader) {
8997
9282
  const renderer = useThree((state) => state.renderer);
9283
+ useLayoutEffect(() => {
9284
+ KTX2LoaderService.registerRenderer(renderer);
9285
+ }, [renderer]);
8998
9286
  const textures = useLoader(KTX2Loader, IsObject(input) ? Object.values(input) : input, (loader) => {
8999
9287
  if (extendLoader) {
9000
9288
  extendLoader(loader);
9001
9289
  }
9002
9290
  if (basisPath) {
9003
- loader.setTranscoderPath(typeof basisPath === "string" ? basisPath : transcoderPath);
9291
+ const path = typeof basisPath === "string" ? basisPath : KTX2LoaderService.getTranscoderPath();
9292
+ loader.setTranscoderPath(path);
9004
9293
  }
9005
9294
  loader.detectSupport(renderer);
9006
9295
  });
9007
9296
  useEffect(() => {
9008
9297
  const array = Array.isArray(textures) ? textures : [textures];
9009
- array.forEach((texture) => renderer.initTexture(texture));
9298
+ array.forEach((texture) => {
9299
+ if ("initTexture" in renderer) {
9300
+ renderer.initTexture(texture);
9301
+ }
9302
+ });
9010
9303
  }, [renderer, textures]);
9011
9304
  if (IsObject(input)) {
9012
9305
  const keys = Object.keys(input);
@@ -9017,23 +9310,81 @@ function useKTX2(input, basisPath = true, extendLoader) {
9017
9310
  return textures;
9018
9311
  }
9019
9312
  }
9020
- useKTX2.preload = (url, basisPath = true, extendLoader) => useLoader.preload(KTX2Loader, url, (loader) => {
9021
- if (extendLoader) {
9022
- extendLoader(loader);
9023
- }
9024
- if (basisPath) {
9025
- loader.setTranscoderPath(typeof basisPath === "string" ? basisPath : transcoderPath);
9026
- }
9027
- });
9313
+ useKTX2.preload = (url, basisPath = true, extendLoader) => {
9314
+ KTX2LoaderService.onReady(() => {
9315
+ const renderer = KTX2LoaderService.getRenderer();
9316
+ useLoader.preload(KTX2Loader, url, (loader) => {
9317
+ if (extendLoader) {
9318
+ extendLoader(loader);
9319
+ }
9320
+ if (basisPath) {
9321
+ const path = typeof basisPath === "string" ? basisPath : KTX2LoaderService.getTranscoderPath();
9322
+ loader.setTranscoderPath(path);
9323
+ }
9324
+ if (renderer) {
9325
+ loader.detectSupport(renderer);
9326
+ }
9327
+ });
9328
+ });
9329
+ };
9028
9330
  useKTX2.clear = (input) => useLoader.clear(KTX2Loader, input);
9029
9331
  useKTX2.setTranscoderPath = (path) => {
9030
- transcoderPath = path;
9332
+ KTX2LoaderService.setTranscoderPath(path);
9031
9333
  };
9032
9334
  const Ktx2 = ({ children, input, basisPath, extendLoader }) => {
9033
9335
  const texture = useKTX2(input, basisPath, extendLoader);
9034
9336
  return /* @__PURE__ */ React.createElement(React.Fragment, null, children?.(texture));
9035
9337
  };
9036
9338
 
9339
+ const useVariants = (model, inVariant) => {
9340
+ const [activeVariant, setActiveVariant] = useState(null);
9341
+ const [materials, setMaterials] = useState([]);
9342
+ useEffect(() => {
9343
+ model.parser.getDependencies("material").then((materials2) => {
9344
+ setMaterials(materials2);
9345
+ });
9346
+ }, [model]);
9347
+ const variants = useMemo(() => {
9348
+ return model.userData.gltfExtensions?.KHR_materials_variants?.variants;
9349
+ }, [model]);
9350
+ const applyVariant = useCallback(
9351
+ (variantIndex) => {
9352
+ model.scene.traverse(async (object) => {
9353
+ if (!("isMesh" in object) || !object.userData.gltfExtensions) return;
9354
+ const meshObject = object;
9355
+ const meshVariantDef = meshObject.userData.gltfExtensions.KHR_materials_variants;
9356
+ if (!meshVariantDef) return;
9357
+ if (!meshObject.userData.originalMaterial) meshObject.userData.originalMaterial = meshObject.material;
9358
+ const mapping = meshVariantDef.mappings.find((mapping2) => mapping2.variants.includes(variantIndex));
9359
+ if (mapping) {
9360
+ meshObject.material = await model.parser.getDependency("material", mapping.material);
9361
+ model.parser.assignFinalMaterial(meshObject);
9362
+ } else meshObject.material = meshObject.userData.originalMaterial;
9363
+ if (Array.isArray(meshObject.material)) for (const mat of meshObject.material) mat.needsUpdate = true;
9364
+ else meshObject.material.needsUpdate = true;
9365
+ });
9366
+ },
9367
+ [model]
9368
+ );
9369
+ const setVariant = useCallback(
9370
+ (variantName) => {
9371
+ if (!variants) return;
9372
+ const newVariant = variantName && variants.some((v) => v.name === variantName) ? variantName : null;
9373
+ setActiveVariant(newVariant);
9374
+ },
9375
+ [variants]
9376
+ );
9377
+ useEffect(() => {
9378
+ if (!variants) return;
9379
+ const variantIndex = variants.findIndex((v) => v.name === activeVariant);
9380
+ applyVariant(variantIndex > -1 ? variantIndex : 0);
9381
+ }, [variants, activeVariant, applyVariant]);
9382
+ useEffect(() => {
9383
+ setVariant(inVariant ?? null);
9384
+ }, [inVariant, setVariant]);
9385
+ return { variants, activeVariant, setVariant, materials };
9386
+ };
9387
+
9037
9388
  const WebcamVideoTexture = /* @__PURE__ */ forwardRef(
9038
9389
  ({
9039
9390
  constraints = {
@@ -9221,6 +9572,7 @@ function useEffectfulState(fn, deps = [], cb) {
9221
9572
  }
9222
9573
 
9223
9574
  function Stats({ showPanel = 0, className, parent }) {
9575
+ React.useEffect(() => void console.warn("drei: <Stats /> is deprecated, use <StatsGl /> instead."), []);
9224
9576
  const stats = useEffectfulState(() => new StatsImpl(), []);
9225
9577
  React.useEffect(() => {
9226
9578
  if (stats) {
@@ -9249,6 +9601,7 @@ const StatsGl = /* @__PURE__ */ React.forwardRef(
9249
9601
  const stats2 = new Stats$1({
9250
9602
  ...props
9251
9603
  });
9604
+ stats2.init(renderer);
9252
9605
  return stats2;
9253
9606
  }, [renderer]);
9254
9607
  React.useImperativeHandle(fref, () => stats.domElement, [stats]);
@@ -9266,7 +9619,7 @@ const StatsGl = /* @__PURE__ */ React.forwardRef(
9266
9619
  return () => {
9267
9620
  if (classNames.length) stats.domElement.classList.remove(...classNames);
9268
9621
  node?.removeChild(stats.domElement);
9269
- stats.end();
9622
+ stats.dispose();
9270
9623
  };
9271
9624
  }
9272
9625
  }, [parent, stats, className, id, clearStatsGlStyle]);
@@ -9274,7 +9627,7 @@ const StatsGl = /* @__PURE__ */ React.forwardRef(
9274
9627
  () => {
9275
9628
  stats.begin();
9276
9629
  },
9277
- { before: "render" }
9630
+ { before: "update" }
9278
9631
  );
9279
9632
  useFrame(
9280
9633
  () => {
@@ -9324,11 +9677,11 @@ const RenderCubeTexture = /* @__PURE__ */ React.forwardRef(
9324
9677
  depthBuffer,
9325
9678
  generateMipmaps
9326
9679
  };
9327
- const fboA2 = new THREE.WebGLCubeRenderTarget(fboSize, settings);
9680
+ const fboA2 = new WebGLCubeRenderTarget(fboSize, settings);
9328
9681
  fboA2.texture.isRenderTargetTexture = !flip;
9329
9682
  fboA2.texture.flipY = true;
9330
9683
  fboA2.texture.type = THREE.HalfFloatType;
9331
- const fboB2 = new THREE.WebGLCubeRenderTarget(fboSize, settings);
9684
+ const fboB2 = new WebGLCubeRenderTarget(fboSize, settings);
9332
9685
  fboB2.texture.isRenderTargetTexture = !flip;
9333
9686
  fboB2.texture.flipY = true;
9334
9687
  fboB2.texture.type = THREE.HalfFloatType;
@@ -9657,8 +10010,9 @@ const TUNNEL_KEY = "__drei_view_tunnel__";
9657
10010
  const tracked = globalThis[TUNNEL_KEY] || (globalThis[TUNNEL_KEY] = tunnel());
9658
10011
  function computeContainerPosition(canvasSize, trackRect, forceEven) {
9659
10012
  const { right, top: trackTop, left: trackLeft, bottom: trackBottom, width, height } = trackRect;
9660
- const isOffscreen = trackRect.bottom < 0 || trackTop > canvasSize.height || right < 0 || trackRect.left > canvasSize.width;
9661
10013
  const canvasBottom = canvasSize.top + canvasSize.height;
10014
+ const canvasRight = canvasSize.left + canvasSize.width;
10015
+ const isOffscreen = trackBottom < canvasSize.top || trackTop > canvasBottom || right < canvasSize.left || trackLeft > canvasRight;
9662
10016
  const rawBottom = canvasBottom - trackBottom;
9663
10017
  const rawLeft = trackLeft - canvasSize.left;
9664
10018
  const rawTop = trackTop - canvasSize.top;
@@ -10218,12 +10572,12 @@ useEnvironment.preload = (preloadOptions) => {
10218
10572
  if (extensions) extensions(loader2);
10219
10573
  });
10220
10574
  };
10221
- const clearDefaultOptins = {
10575
+ const clearDefaultOptions = {
10222
10576
  files: defaultFiles,
10223
10577
  preset: void 0
10224
10578
  };
10225
10579
  useEnvironment.clear = (clearOptions) => {
10226
- const options = { ...clearDefaultOptins, ...clearOptions };
10580
+ const options = { ...clearDefaultOptions, ...clearOptions };
10227
10581
  let { files } = options;
10228
10582
  const { preset } = options;
10229
10583
  if (preset) {
@@ -10917,9 +11271,14 @@ function Select({
10917
11271
  const oldControlsEnabled = controls?.enabled;
10918
11272
  let isDown = false;
10919
11273
  function prepareRay(event, vec) {
10920
- const { offsetX, offsetY } = event;
11274
+ const canvas = gl.domElement;
11275
+ const rect = canvas.getBoundingClientRect();
11276
+ const x = event.clientX - rect.left;
11277
+ const y = event.clientY - rect.top;
10921
11278
  const { width, height } = size;
10922
- vec.set(offsetX / width * 2 - 1, -(offsetY / height) * 2 + 1);
11279
+ const normalizedX = x / width * 2 - 1;
11280
+ const normalizedY = -(y / height) * 2 + 1;
11281
+ vec.set(normalizedX, normalizedY, -1);
10923
11282
  }
10924
11283
  function onSelectStart(event) {
10925
11284
  if (controls) controls.enabled = false;
@@ -11653,8 +12012,8 @@ vec3 highPassRandRGB(vec2 uv) {
11653
12012
 
11654
12013
 
11655
12014
  vec2 vogelDiskSample(int sampleIndex, int sampleCount, float angle) {
11656
- const float goldenAngle = 2.399963f; // radians
11657
- float r = sqrt(float(sampleIndex) + 0.5f) / sqrt(float(sampleCount));
12015
+ float goldenAngle = 2.399963;
12016
+ float r = sqrt(float(sampleIndex) + 0.5) / sqrt(float(sampleCount));
11658
12017
  float theta = float(sampleIndex) * goldenAngle + angle;
11659
12018
  float sine = sin(theta);
11660
12019
  float cosine = cos(theta);
@@ -11675,7 +12034,7 @@ float findBlocker(sampler2D shadowMap, vec2 uv, float compare, float angle) {
11675
12034
  #pragma unroll_loop_start
11676
12035
  for(int i = 0; i < ${samples}; i ++) {
11677
12036
  offset = (vogelDiskSample(j, ${samples}, angle) * texelSize) * 2.0 * PENUMBRA_FILTER_SIZE;
11678
- depth = unpackRGBAToDepth( texture2D( shadowMap, uv + offset));
12037
+ depth = texture2D( shadowMap, uv + offset ).r;
11679
12038
  if (depth < compare) {
11680
12039
  blockerDepthSum += depth;
11681
12040
  blockers++;
@@ -11693,7 +12052,7 @@ float findBlocker(sampler2D shadowMap, vec2 uv, float compare, float angle) {
11693
12052
 
11694
12053
  float vogelFilter(sampler2D shadowMap, vec2 uv, float zReceiver, float filterRadius, float angle) {
11695
12054
  float texelSize = 1.0 / float(textureSize(shadowMap, 0).x);
11696
- float shadow = 0.0f;
12055
+ float shadow = 0.0;
11697
12056
  int j = 0;
11698
12057
  vec2 vogelSample = vec2(0.0);
11699
12058
  vec2 offset = vec2(0.0);
@@ -11701,7 +12060,7 @@ float vogelFilter(sampler2D shadowMap, vec2 uv, float zReceiver, float filterRad
11701
12060
  for (int i = 0; i < ${samples}; i++) {
11702
12061
  vogelSample = vogelDiskSample(j, ${samples}, angle) * texelSize;
11703
12062
  offset = vogelSample * (1.0 + filterRadius * float(${size}));
11704
- shadow += step( zReceiver, unpackRGBAToDepth( texture2D( shadowMap, uv + offset ) ) );
12063
+ shadow += step( zReceiver, texture2D( shadowMap, uv + offset ).r );
11705
12064
  j++;
11706
12065
  }
11707
12066
  #pragma unroll_loop_end
@@ -11710,7 +12069,7 @@ float vogelFilter(sampler2D shadowMap, vec2 uv, float zReceiver, float filterRad
11710
12069
 
11711
12070
  float PCSS (sampler2D shadowMap, vec4 coords) {
11712
12071
  vec2 uv = coords.xy;
11713
- float zReceiver = coords.z; // Assumed to be eye-space z in this code
12072
+ float zReceiver = coords.z;
11714
12073
  float angle = highPassRandRGB(gl_FragCoord.xy).r * PI2;
11715
12074
  float avgBlockerDepth = findBlocker(shadowMap, uv, zReceiver, angle);
11716
12075
  if (avgBlockerDepth == -1.0) {
@@ -11734,14 +12093,27 @@ function SoftShadows({ focus = 0, samples = 10, size = 25 }) {
11734
12093
  const scene = useThree((state) => state.scene);
11735
12094
  const camera = useThree((state) => state.camera);
11736
12095
  React.useEffect(() => {
11737
- const original = THREE.ShaderChunk.shadowmap_pars_fragment;
11738
- THREE.ShaderChunk.shadowmap_pars_fragment = THREE.ShaderChunk.shadowmap_pars_fragment.replace("#ifdef USE_SHADOWMAP", "#ifdef USE_SHADOWMAP\n" + pcss({ size, samples, focus })).replace(
11739
- "#if defined( SHADOWMAP_TYPE_PCF )",
11740
- "\nreturn PCSS(shadowMap, shadowCoord);\n#if defined( SHADOWMAP_TYPE_PCF )"
11741
- );
12096
+ const originalShader = THREE.ShaderChunk.shadowmap_pars_fragment;
12097
+ const originalShadowMapType = gl.shadowMap.type;
12098
+ gl.shadowMap.type = THREE.BasicShadowMap;
12099
+ let shader = THREE.ShaderChunk.shadowmap_pars_fragment;
12100
+ shader = shader.replace("#ifdef USE_SHADOWMAP", "#ifdef USE_SHADOWMAP\n" + pcss({ size, samples, focus }));
12101
+ const basicIfBlockRegex = /if\s*\(\s*frustumTest\s*\)\s*\{\s*float\s+depth\s*=\s*texture2D\s*\(\s*shadowMap\s*,\s*shadowCoord\.xy\s*\)\.r;\s*#ifdef\s+USE_REVERSED_DEPTH_BUFFER\s*shadow\s*=\s*step\s*\(\s*depth\s*,\s*shadowCoord\.z\s*\)\s*;\s*#else\s*shadow\s*=\s*step\s*\(\s*shadowCoord\.z\s*,\s*depth\s*\)\s*;\s*#endif\s*\}/;
12102
+ if (basicIfBlockRegex.test(shader)) {
12103
+ shader = shader.replace(
12104
+ basicIfBlockRegex,
12105
+ `if ( frustumTest ) {
12106
+ shadow = PCSS( shadowMap, shadowCoord );
12107
+ }`
12108
+ );
12109
+ } else {
12110
+ console.warn("[SoftShadows] Could not find injection point in shader!");
12111
+ }
12112
+ THREE.ShaderChunk.shadowmap_pars_fragment = shader;
11742
12113
  reset(gl, scene, camera);
11743
12114
  return () => {
11744
- THREE.ShaderChunk.shadowmap_pars_fragment = original;
12115
+ THREE.ShaderChunk.shadowmap_pars_fragment = originalShader;
12116
+ gl.shadowMap.type = originalShadowMapType;
11745
12117
  reset(gl, scene, camera);
11746
12118
  };
11747
12119
  }, [focus, size, samples]);
@@ -11847,6 +12219,60 @@ class SpotLightMaterial extends THREE.ShaderMaterial {
11847
12219
  }
11848
12220
  }
11849
12221
 
12222
+ const HtmlMaterial = ({ transform }) => {
12223
+ const shaders = useMemo(
12224
+ () => ({
12225
+ vertexShader: !transform ? (
12226
+ /* glsl */
12227
+ `
12228
+ /*
12229
+ This shader is from the THREE's SpriteMaterial.
12230
+ We need to turn the backing plane into a Sprite
12231
+ (make it always face the camera) if "transform"
12232
+ is false.
12233
+ */
12234
+ #include <common>
12235
+
12236
+ void main() {
12237
+ vec2 center = vec2(0., 1.);
12238
+ float rotation = 0.0;
12239
+
12240
+ // This is somewhat arbitrary, but it seems to work well
12241
+ // Need to figure out how to derive this dynamically if it even matters
12242
+ float size = 0.03;
12243
+
12244
+ vec4 mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );
12245
+ vec2 scale;
12246
+ scale.x = length( vec3( modelMatrix[ 0 ].x, modelMatrix[ 0 ].y, modelMatrix[ 0 ].z ) );
12247
+ scale.y = length( vec3( modelMatrix[ 1 ].x, modelMatrix[ 1 ].y, modelMatrix[ 1 ].z ) );
12248
+
12249
+ bool isPerspective = isPerspectiveMatrix( projectionMatrix );
12250
+ if ( isPerspective ) scale *= - mvPosition.z;
12251
+
12252
+ vec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale * size;
12253
+ vec2 rotatedPosition;
12254
+ rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;
12255
+ rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;
12256
+ mvPosition.xy += rotatedPosition;
12257
+
12258
+ gl_Position = projectionMatrix * mvPosition;
12259
+ }
12260
+ `
12261
+ ) : void 0,
12262
+ fragmentShader: (
12263
+ /* glsl */
12264
+ `
12265
+ void main() {
12266
+ gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
12267
+ }
12268
+ `
12269
+ )
12270
+ }),
12271
+ [transform]
12272
+ );
12273
+ return /* @__PURE__ */ React.createElement("shaderMaterial", { side: DoubleSide, vertexShader: shaders.vertexShader, fragmentShader: shaders.fragmentShader });
12274
+ };
12275
+
11850
12276
  class StarfieldMaterial extends ShaderMaterial {
11851
12277
  constructor() {
11852
12278
  super({
@@ -11921,8 +12347,8 @@ class SparklesMaterial extends ShaderMaterial {
11921
12347
  modelPosition.z += cos(time * speed + modelPosition.x * noise.y * 100.0) * 0.2;
11922
12348
  modelPosition.x += cos(time * speed + modelPosition.x * noise.z * 100.0) * 0.2;
11923
12349
  vec4 viewPosition = viewMatrix * modelPosition;
11924
- vec4 projectionPostion = projectionMatrix * viewPosition;
11925
- gl_Position = projectionPostion;
12350
+ vec4 projectionPosition = projectionMatrix * viewPosition;
12351
+ gl_Position = projectionPosition;
11926
12352
  gl_PointSize = size * 25. * pixelRatio;
11927
12353
  gl_PointSize *= (1.0 / - viewPosition.z);
11928
12354
  vColor = color;
@@ -11937,8 +12363,9 @@ class SparklesMaterial extends ShaderMaterial {
11937
12363
  varying float vOpacity;
11938
12364
  void main() {
11939
12365
  float distanceToCenter = distance(gl_PointCoord, vec2(0.5));
11940
- float strength = 0.05 / distanceToCenter - 0.1;
12366
+ float strength = clamp(0.05 / distanceToCenter - 0.1, 0.0, 1.0);
11941
12367
  gl_FragColor = vec4(vColor, strength * vOpacity);
12368
+ // Note: tonemapping may shift hue (e.g. orange \u2192 yellow) compared to the WebGPU version
11942
12369
  #include <tonemapping_fragment>
11943
12370
  #include <${version >= 154 ? "colorspace_fragment" : "encodings_fragment"}>
11944
12371
  }
@@ -12628,4 +13055,4 @@ const Text = /* @__PURE__ */ React.forwardRef(
12628
13055
  }
12629
13056
  );
12630
13057
 
12631
- export { AccumulativeShadows, AdaptiveDpr, AdaptiveEvents, ArcballControls, AsciiRenderer, BBAnchor, Backdrop, BakeShadows, Billboard, BlurPass, Bounds, Box, CameraShake, Capsule, CatmullRomLine, Caustics, Center, Circle, Clone, Cloud, CloudInstance, CloudMaterial, Clouds, ComputedAttribute, Cone, ContactShadows, ConvolutionMaterial, CubeCamera, CubeTexture, CubicBezierLine, CurveModifier, CycleRaycast, Cylinder, Decal, Detailed, DetectGPU, DeviceOrientationControls, DiscardMaterial, Dodecahedron, DragControls, Edges, Effects, Environment, EnvironmentCube, EnvironmentMap, EnvironmentPortal, Example, Extrude, 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, MeshDiscardMaterial, MeshDistortMaterial, MeshPortalMaterial, MeshReflectorMaterial, 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, SpotLightShadow, SpriteAnimator, Stage, StarfieldMaterial, Stars, Stats, StatsGl, Svg, Tetrahedron, Text, Text3D, Texture, Torus, TorusKnot, TrackballControls, Trail, TrailTexture, TransformControls, Tube, VideoTexture, View, WebcamVideoTexture, Wireframe, WireframeMaterial, WireframeMaterialShaders, accumulativeContext, calcPosFromAngles, checkIfFrameIsEmpty, createInstances, getFirstFrame, isWebGL2Available, meshBounds, presetsObj, setWireframeOverride, shaderMaterial, useAnimations, useAspect, useBounds, useBoxProjectedEnv, 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 };
13058
+ export { AccumulativeShadows, AdaptiveDpr, AdaptiveEvents, ArcballControls, AsciiRenderer, BBAnchor, Backdrop, BakeShadows, Billboard, BlurPass, Bounds, Box, CameraShake, Capsule, CatmullRomLine, Caustics, Center, Circle, Clone, Cloud, CloudInstance, CloudMaterial, Clouds, ComputedAttribute, Cone, ContactShadows, ConvolutionMaterial, CubeCamera, CubeTexture, CubicBezierLine, CurveModifier, CycleRaycast, Cylinder, Decal, Detailed, DetectGPU, DeviceOrientationControls, DiscardMaterial, Dodecahedron, DragControls, Edges, Effects, Environment, EnvironmentCube, EnvironmentMap, EnvironmentPortal, Example, Extrude, Fbo, Fbx, FirstPersonControls, Fisheye, Float, FlyControls, GizmoHelper, GizmoViewcube, GizmoViewport, Gltf, GradientTexture, GradientType, Grid, Helper, Html, HtmlMaterial, Hud, Icosahedron, Image, Instance, InstancedAttribute, Instances, IsObject, KeyboardControls, Ktx2, Lathe, Lightformer, Line, Loader, MapControls, Mask, MatcapTexture, Merged, MeshDiscardMaterial, MeshDistortMaterial, MeshPortalMaterial, MeshReflectorMaterial, 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, SpotLightShadow, SpriteAnimator, Stage, StarfieldMaterial, Stars, Stats, StatsGl, Svg, Tetrahedron, Text, Text3D, Texture, Torus, TorusKnot, TrackballControls, Trail, TrailTexture, TransformControls, Tube, VideoTexture, View, WebcamVideoTexture, Wireframe, WireframeMaterial, WireframeMaterialShaders, accumulativeContext, calcPosFromAngles, checkIfFrameIsEmpty, createInstances, getFirstFrame, isWebGL2Available, meshBounds, presetsObj, setWireframeOverride, shaderMaterial, useAnimations, useAspect, useBounds, useBoxProjectedEnv, useContextBridge, useCubeCamera, useCubeTexture, useCursor, useCustomRaycast, useDepthBuffer, useDetectGPU, useEnvironment, useFBO, useFBX, useFont, useGLTF, useGizmoContext, useHelper, useIntersect, useKTX2, useKeyboardControls, useMask, useMatcapTexture, useMotion, useNormalTexture, usePerformanceMonitor, useProgress, useScroll, useSelect, useSpriteAnimator, useSpriteLoader, useSurfaceSampler, useTexture, useTrail, useTrailTexture, useVariants, useVideoTexture, useWireframeUniforms };