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

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,10 +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, MathUtils, TextureLoader, Texture as Texture$1, MeshLambertMaterial, Quaternion, 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 { DeviceOrientationControls as DeviceOrientationControls$1 } from 'three-stdlib';
8
- import { useGesture } from '@use-gesture/react';
9
7
  import { FirstPersonControls as FirstPersonControls$1 } from 'three/examples/jsm/controls/FirstPersonControls.js';
10
8
  import { FlyControls as FlyControls$1 } from 'three/examples/jsm/controls/FlyControls.js';
11
9
  import { create as create$1 } from 'zustand';
@@ -14,6 +12,7 @@ import { MapControls as MapControls$1 } from 'three/examples/jsm/controls/MapCon
14
12
  import { misc, easing } from 'maath';
15
13
  import { OrbitControls as OrbitControls$1 } from 'three/addons/controls/OrbitControls.js';
16
14
  import { PointerLockControls as PointerLockControls$1 } from 'three/examples/jsm/controls/PointerLockControls.js';
15
+ import { useGesture } from '@use-gesture/react';
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';
@@ -32,7 +31,6 @@ import { TransformControls as TransformControls$1 } from 'three/examples/jsm/con
32
31
  import * as SkeletonUtils from 'three/examples/jsm/utils/SkeletonUtils.js';
33
32
  import { MeshSurfaceSampler } from 'three/examples/jsm/math/MeshSurfaceSampler.js';
34
33
  import { SVGLoader } from 'three/examples/jsm/loaders/SVGLoader.js';
35
- import { Events } from 'hls.js';
36
34
  import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader.js';
37
35
  import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
38
36
  import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
@@ -58,7 +56,7 @@ import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js';
58
56
  import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
59
57
  import { AsciiEffect } from 'three/examples/jsm/effects/AsciiEffect.js';
60
58
  import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass.js';
61
- import { shaderStructs, shaderIntersectFunction, MeshBVHUniformStruct } from 'three-mesh-bvh';
59
+ import { MeshBVHUniformStruct, MeshBVH, SAH, shaderStructs, shaderIntersectFunction } from 'three-mesh-bvh';
62
60
  import { Text as Text$1, preloadFont } from 'troika-three-text';
63
61
 
64
62
  const ContactShadows = /* @__PURE__ */ React.forwardRef(
@@ -188,6 +186,7 @@ function shaderMaterial(uniforms, vertexShader, fragmentShader, onInit) {
188
186
  });
189
187
  }
190
188
  this.uniforms = THREE.UniformsUtils.clone(this.uniforms);
189
+ Object.assign(this, parameters);
191
190
  onInit?.(this);
192
191
  }
193
192
  };
@@ -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 ) );
@@ -1517,6 +1522,7 @@ let MeshReflectorMaterial$1 = class MeshReflectorMaterial extends MeshStandardMa
1517
1522
  uniform float depthScale;
1518
1523
  uniform float depthToBlurRatioBias;
1519
1524
  varying vec4 my_vUv;
1525
+ #include <tonemapping_pars_fragment>
1520
1526
  ${shader.fragmentShader}`;
1521
1527
  shader.fragmentShader = shader.fragmentShader.replace(
1522
1528
  "#include <emissivemap_fragment>",
@@ -1577,6 +1583,9 @@ let MeshReflectorMaterial$1 = class MeshReflectorMaterial extends MeshStandardMa
1577
1583
  merge = mix(merge, blur, blurFactor);
1578
1584
  #endif
1579
1585
 
1586
+ // Apply tone mapping to HDR reflection values before contrast adjustment
1587
+ merge.rgb = toneMapping(merge.rgb);
1588
+
1580
1589
  vec4 newMerge = vec4(0.0, 0.0, 0.0, 1.0);
1581
1590
  newMerge.r = (merge.r - 0.5) * mixContrast + 0.5;
1582
1591
  newMerge.g = (merge.g - 0.5) * mixContrast + 0.5;
@@ -1835,6 +1844,7 @@ const MeshReflectorMaterial = /* @__PURE__ */ React.forwardRef(
1835
1844
  useFrame(() => {
1836
1845
  const parent = materialRef.current.parent || materialRef.current?.__r3f.parent?.object;
1837
1846
  if (!parent) return;
1847
+ if (gl.xr.isPresenting) return;
1838
1848
  parent.visible = false;
1839
1849
  const currentXrEnabled = gl.xr.enabled;
1840
1850
  const currentShadowAutoUpdate = gl.shadowMap.autoUpdate;
@@ -1864,7 +1874,7 @@ const MeshReflectorMaterial = /* @__PURE__ */ React.forwardRef(
1864
1874
  }
1865
1875
  );
1866
1876
 
1867
- const MeshRefractionMaterial = /* @__PURE__ */ shaderMaterial(
1877
+ const MeshRefractionMaterial_ = /* @__PURE__ */ shaderMaterial(
1868
1878
  {
1869
1879
  envMap: null,
1870
1880
  bounces: 3,
@@ -2025,6 +2035,56 @@ const MeshRefractionMaterial = /* @__PURE__ */ shaderMaterial(
2025
2035
  #include <${version$1 >= 154 ? "colorspace_fragment" : "encodings_fragment"}>
2026
2036
  }`
2027
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
+ );
2028
2088
 
2029
2089
  function useIntersect(onChange) {
2030
2090
  const { isLegacy } = useThree();
@@ -2248,6 +2308,8 @@ const MeshPortalMaterial = /* @__PURE__ */ React.forwardRef(
2248
2308
  children,
2249
2309
  events = void 0,
2250
2310
  blur = 0,
2311
+ blend = 0,
2312
+ samples = 8,
2251
2313
  eventPriority = 0,
2252
2314
  renderPriority = 0,
2253
2315
  worldUnits = false,
@@ -2260,7 +2322,7 @@ const MeshPortalMaterial = /* @__PURE__ */ React.forwardRef(
2260
2322
  const maskRenderTarget = useFBO(resolution, resolution);
2261
2323
  const [priority, setPriority] = React.useState(0);
2262
2324
  useFrame(() => {
2263
- const p = ref.current.blend > 0 ? Math.max(1, renderPriority) : 0;
2325
+ const p = (ref.current.blend ?? 0) > 0 ? Math.max(1, renderPriority) : 0;
2264
2326
  if (priority !== p) setPriority(p);
2265
2327
  });
2266
2328
  React.useEffect(() => {
@@ -2334,7 +2396,8 @@ const MeshPortalMaterial = /* @__PURE__ */ React.forwardRef(
2334
2396
  frames: visible ? Infinity : 0,
2335
2397
  eventPriority,
2336
2398
  renderPriority,
2337
- compute
2399
+ compute,
2400
+ samples
2338
2401
  },
2339
2402
  children,
2340
2403
  /* @__PURE__ */ React.createElement(
@@ -2414,7 +2477,7 @@ function ManagePortalScene({
2414
2477
  scene.matrixWorld.copy(parent.matrixWorld);
2415
2478
  } else scene.matrixWorld.identity();
2416
2479
  if (priority) {
2417
- if (material.current?.blend > 0 && material.current?.blend < 1) {
2480
+ if (material.current?.blend && material.current?.blend > 0 && material.current?.blend < 1) {
2418
2481
  blend.value = material.current.blend;
2419
2482
  state.gl.setRenderTarget(buffer1);
2420
2483
  state.gl.render(scene, state.camera);
@@ -3045,7 +3108,7 @@ const isWebGL2Available = () => {
3045
3108
  const Effects = /* @__PURE__ */ React.forwardRef(
3046
3109
  ({
3047
3110
  children,
3048
- multisamping = 8,
3111
+ multisampling = 8,
3049
3112
  renderIndex = 1,
3050
3113
  disableRender,
3051
3114
  disableGamma,
@@ -3072,7 +3135,7 @@ const Effects = /* @__PURE__ */ React.forwardRef(
3072
3135
  if (type === UnsignedByteType && colorSpace != null) {
3073
3136
  t.texture.colorSpace = colorSpace;
3074
3137
  }
3075
- t.samples = multisamping;
3138
+ t.samples = multisampling;
3076
3139
  return t;
3077
3140
  });
3078
3141
  React.useEffect(() => {
@@ -3207,12 +3270,12 @@ const Segments = /* @__PURE__ */ React.forwardRef((props, forwardedRef) => {
3207
3270
  for (let i = 0; i < limit; i++) {
3208
3271
  const segment = segments[i]?.current;
3209
3272
  if (segment) {
3210
- positions[i * 6 + 0] = segment.start.x;
3211
- positions[i * 6 + 1] = segment.start.y;
3212
- positions[i * 6 + 2] = segment.start.z;
3213
- positions[i * 6 + 3] = segment.end.x;
3214
- positions[i * 6 + 4] = segment.end.y;
3215
- positions[i * 6 + 5] = segment.end.z;
3273
+ positions[i * 6 + 0] = segment.start?.x || 0;
3274
+ positions[i * 6 + 1] = segment.start?.y || 0;
3275
+ positions[i * 6 + 2] = segment.start?.z || 0;
3276
+ positions[i * 6 + 3] = segment.end?.x || 0;
3277
+ positions[i * 6 + 4] = segment.end?.y || 0;
3278
+ positions[i * 6 + 5] = segment.end?.z || 0;
3216
3279
  colors[i * 6 + 0] = segment.color.r;
3217
3280
  colors[i * 6 + 1] = segment.color.g;
3218
3281
  colors[i * 6 + 2] = segment.color.b;
@@ -3247,7 +3310,10 @@ class SegmentObject {
3247
3310
  this.end = new THREE.Vector3(0, 0, 0);
3248
3311
  }
3249
3312
  }
3250
- const normPos = (pos) => pos instanceof THREE.Vector3 ? pos : new THREE.Vector3(...typeof pos === "number" ? [pos, pos, pos] : pos);
3313
+ const normPos = (pos) => {
3314
+ if (!pos) return new THREE.Vector3(0, 0, 0);
3315
+ return pos instanceof THREE.Vector3 ? pos : new THREE.Vector3(...typeof pos === "number" ? [pos, pos, pos] : pos);
3316
+ };
3251
3317
  const Segment = /* @__PURE__ */ React.forwardRef(({ color, start, end }, forwardedRef) => {
3252
3318
  const api = React.useContext(context$9);
3253
3319
  if (!api) throw "Segment must used inside Segments component.";
@@ -3569,6 +3635,81 @@ const ArcballControls = /* @__PURE__ */ forwardRef(
3569
3635
  }
3570
3636
  );
3571
3637
 
3638
+ let DeviceOrientationControls$1 = class DeviceOrientationControls extends EventDispatcher {
3639
+ object;
3640
+ changeEvent = { type: "change" };
3641
+ EPS = 1e-6;
3642
+ enabled = true;
3643
+ deviceOrientation = { alpha: 0, beta: 0, gamma: 0 };
3644
+ screenOrientation = 0;
3645
+ alphaOffset = 0;
3646
+ // radians
3647
+ constructor(object) {
3648
+ super();
3649
+ this.object = object;
3650
+ this.object.rotation.reorder("YXZ");
3651
+ this.connect();
3652
+ }
3653
+ onDeviceOrientationChangeEvent = (event) => {
3654
+ this.deviceOrientation = event;
3655
+ };
3656
+ onScreenOrientationChangeEvent = () => {
3657
+ this.screenOrientation = window.orientation || 0;
3658
+ };
3659
+ // The angles alpha, beta and gamma form a set of intrinsic Tait-Bryan angles of type Z-X'-Y''
3660
+ zee = new Vector3(0, 0, 1);
3661
+ euler = new Euler();
3662
+ q0 = new Quaternion();
3663
+ q1 = new Quaternion(-Math.sqrt(0.5), 0, 0, Math.sqrt(0.5));
3664
+ // - PI/2 around the x-axis
3665
+ setObjectQuaternion = (quaternion, alpha, beta, gamma, orient) => {
3666
+ this.euler.set(beta, alpha, -gamma, "YXZ");
3667
+ quaternion.setFromEuler(this.euler);
3668
+ quaternion.multiply(this.q1);
3669
+ quaternion.multiply(this.q0.setFromAxisAngle(this.zee, -orient));
3670
+ };
3671
+ connect = () => {
3672
+ this.onScreenOrientationChangeEvent();
3673
+ if (window.DeviceOrientationEvent !== void 0 && // @ts-ignore
3674
+ typeof window.DeviceOrientationEvent.requestPermission === "function") {
3675
+ window.DeviceOrientationEvent.requestPermission().then((response) => {
3676
+ if (response == "granted") {
3677
+ window.addEventListener("orientationchange", this.onScreenOrientationChangeEvent);
3678
+ window.addEventListener("deviceorientation", this.onDeviceOrientationChangeEvent);
3679
+ }
3680
+ }).catch((error) => {
3681
+ console.error("THREE.DeviceOrientationControls: Unable to use DeviceOrientation API:", error);
3682
+ });
3683
+ } else {
3684
+ window.addEventListener("orientationchange", this.onScreenOrientationChangeEvent);
3685
+ window.addEventListener("deviceorientation", this.onDeviceOrientationChangeEvent);
3686
+ }
3687
+ this.enabled = true;
3688
+ };
3689
+ disconnect = () => {
3690
+ window.removeEventListener("orientationchange", this.onScreenOrientationChangeEvent);
3691
+ window.removeEventListener("deviceorientation", this.onDeviceOrientationChangeEvent);
3692
+ this.enabled = false;
3693
+ };
3694
+ lastQuaternion = new Quaternion();
3695
+ update = () => {
3696
+ if (this.enabled === false) return;
3697
+ const device = this.deviceOrientation;
3698
+ if (device) {
3699
+ const alpha = device.alpha ? MathUtils.degToRad(device.alpha) + this.alphaOffset : 0;
3700
+ const beta = device.beta ? MathUtils.degToRad(device.beta) : 0;
3701
+ const gamma = device.gamma ? MathUtils.degToRad(device.gamma) : 0;
3702
+ const orient = this.screenOrientation ? MathUtils.degToRad(this.screenOrientation) : 0;
3703
+ this.setObjectQuaternion(this.object.quaternion, alpha, beta, gamma, orient);
3704
+ if (8 * (1 - this.lastQuaternion.dot(this.object.quaternion)) > this.EPS) {
3705
+ this.lastQuaternion.copy(this.object.quaternion);
3706
+ this.dispatchEvent(this.changeEvent);
3707
+ }
3708
+ }
3709
+ };
3710
+ dispose = () => this.disconnect();
3711
+ };
3712
+
3572
3713
  const DeviceOrientationControls = /* @__PURE__ */ React.forwardRef(
3573
3714
  (props, ref) => {
3574
3715
  const { camera, onChange, makeDefault, ...rest } = props;
@@ -3604,11 +3745,11 @@ const DeviceOrientationControls = /* @__PURE__ */ React.forwardRef(
3604
3745
  );
3605
3746
 
3606
3747
  const initialModelPosition = /* @__PURE__ */ new THREE.Vector3();
3607
- const mousePosition2D = /* @__PURE__ */ new THREE.Vector2();
3608
3748
  const mousePosition3D = /* @__PURE__ */ new THREE.Vector3();
3609
3749
  const dragOffset = /* @__PURE__ */ new THREE.Vector3();
3610
3750
  const dragPlaneNormal = /* @__PURE__ */ new THREE.Vector3();
3611
3751
  const dragPlane = /* @__PURE__ */ new THREE.Plane();
3752
+ const startPointerPos = /* @__PURE__ */ new THREE.Vector3();
3612
3753
  const DragControls = React.forwardRef(
3613
3754
  ({
3614
3755
  autoTransform = true,
@@ -3624,89 +3765,132 @@ const DragControls = React.forwardRef(
3624
3765
  ...props
3625
3766
  }, fRef) => {
3626
3767
  const defaultControls = useThree((state) => state.controls);
3627
- const { camera, size, raycaster, invalidate } = useThree();
3768
+ const { camera, invalidate } = useThree();
3628
3769
  const ref = React.useRef(null);
3629
- const bind = useGesture(
3630
- {
3631
- onHover: ({ hovering }) => onHover && onHover(hovering ?? false),
3632
- onDragStart: ({ event }) => {
3633
- if (defaultControls) defaultControls.enabled = false;
3634
- const { point } = event;
3635
- ref.current.matrix.decompose(initialModelPosition, new THREE.Quaternion(), new THREE.Vector3());
3636
- mousePosition3D.copy(point);
3637
- dragOffset.copy(mousePosition3D).sub(initialModelPosition);
3638
- onDragStart && onDragStart(initialModelPosition);
3639
- invalidate();
3640
- },
3641
- onDrag: ({ xy: [dragX, dragY], intentional }) => {
3642
- if (!intentional) return;
3643
- const normalizedMouseX = (dragX - size.left) / size.width * 2 - 1;
3644
- const normalizedMouseY = -((dragY - size.top) / size.height) * 2 + 1;
3645
- mousePosition2D.set(normalizedMouseX, normalizedMouseY);
3646
- raycaster.setFromCamera(mousePosition2D, camera);
3647
- if (!axisLock) {
3648
- camera.getWorldDirection(dragPlaneNormal).negate();
3649
- } else {
3650
- switch (axisLock) {
3651
- case "x":
3652
- dragPlaneNormal.set(1, 0, 0);
3653
- break;
3654
- case "y":
3655
- dragPlaneNormal.set(0, 1, 0);
3656
- break;
3657
- case "z":
3658
- dragPlaneNormal.set(0, 0, 1);
3659
- break;
3660
- }
3661
- }
3662
- dragPlane.setFromNormalAndCoplanarPoint(dragPlaneNormal, mousePosition3D);
3663
- raycaster.ray.intersectPlane(dragPlane, mousePosition3D);
3664
- const previousLocalMatrix = ref.current.matrix.clone();
3665
- const previousWorldMatrix = ref.current.matrixWorld.clone();
3666
- const intendedNewPosition = new THREE.Vector3(
3667
- mousePosition3D.x - dragOffset.x,
3668
- mousePosition3D.y - dragOffset.y,
3669
- mousePosition3D.z - dragOffset.z
3670
- );
3671
- if (dragLimits) {
3672
- intendedNewPosition.x = dragLimits[0] ? Math.max(Math.min(intendedNewPosition.x, dragLimits[0][1]), dragLimits[0][0]) : intendedNewPosition.x;
3673
- intendedNewPosition.y = dragLimits[1] ? Math.max(Math.min(intendedNewPosition.y, dragLimits[1][1]), dragLimits[1][0]) : intendedNewPosition.y;
3674
- intendedNewPosition.z = dragLimits[2] ? Math.max(Math.min(intendedNewPosition.z, dragLimits[2][1]), dragLimits[2][0]) : intendedNewPosition.z;
3675
- }
3676
- if (autoTransform) {
3677
- ref.current.matrix.setPosition(intendedNewPosition);
3678
- const deltaLocalMatrix = ref.current.matrix.clone().multiply(previousLocalMatrix.invert());
3679
- const deltaWorldMatrix = ref.current.matrix.clone().multiply(previousWorldMatrix.invert());
3680
- onDrag && onDrag(ref.current.matrix, deltaLocalMatrix, ref.current.matrixWorld, deltaWorldMatrix);
3681
- } else {
3682
- const tempMatrix = new THREE.Matrix4().copy(ref.current.matrix);
3683
- tempMatrix.setPosition(intendedNewPosition);
3684
- const deltaLocalMatrix = tempMatrix.clone().multiply(previousLocalMatrix.invert());
3685
- const deltaWorldMatrix = tempMatrix.clone().multiply(previousWorldMatrix.invert());
3686
- 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;
3687
3803
  }
3688
- invalidate();
3689
- },
3690
- onDragEnd: () => {
3691
- if (defaultControls) defaultControls.enabled = true;
3692
- onDragEnd && onDragEnd();
3693
- invalidate();
3694
3804
  }
3805
+ dragPlane.setFromNormalAndCoplanarPoint(dragPlaneNormal, mousePosition3D);
3806
+ if (dragState.current.intentional) {
3807
+ onDragStart && onDragStart(initialModelPosition);
3808
+ }
3809
+ invalidate();
3695
3810
  },
3696
- {
3697
- drag: {
3698
- filterTaps: true,
3699
- threshold: 1,
3700
- ...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);
3701
3823
  }
3702
- }
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]
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]
3703
3867
  );
3868
+ const handlePointerEnter = React.useCallback(() => {
3869
+ onHover && onHover(true);
3870
+ }, [onHover]);
3871
+ const handlePointerLeave = React.useCallback(() => {
3872
+ onHover && onHover(false);
3873
+ }, [onHover]);
3704
3874
  React.useImperativeHandle(fRef, () => ref.current, []);
3705
3875
  React.useLayoutEffect(() => {
3706
3876
  if (!matrix) return;
3707
3877
  ref.current.matrix = matrix;
3708
3878
  }, [matrix]);
3709
- 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
+ );
3710
3894
  }
3711
3895
  );
3712
3896
 
@@ -3808,14 +3992,23 @@ function KeyboardControls({ map, children, onChange, domElement }) {
3808
3992
  obj.pressed = false;
3809
3993
  if (up) fn(false);
3810
3994
  };
3995
+ const blurHandler = () => {
3996
+ const resetState = map.reduce((prev, cur) => ({ ...prev, [cur.name]: false }), {});
3997
+ set(resetState);
3998
+ Object.values(keyMap).forEach((obj) => {
3999
+ obj.pressed = false;
4000
+ });
4001
+ };
3811
4002
  const source = domElement || window;
3812
4003
  source.addEventListener("keydown", downHandler, { passive: true });
3813
4004
  source.addEventListener("keyup", upHandler, { passive: true });
4005
+ window.addEventListener("blur", blurHandler);
3814
4006
  return () => {
3815
4007
  source.removeEventListener("keydown", downHandler);
3816
4008
  source.removeEventListener("keyup", upHandler);
4009
+ window.removeEventListener("blur", blurHandler);
3817
4010
  };
3818
- }, [domElement, key]);
4011
+ }, [domElement, key, onChange]);
3819
4012
  return /* @__PURE__ */ React.createElement(context$8.Provider, { value: api, children });
3820
4013
  }
3821
4014
  function useKeyboardControls(sel) {
@@ -3837,20 +4030,28 @@ const MapControls = /* @__PURE__ */ React.forwardRef((props = { enableDamping: t
3837
4030
  const controls = React.useMemo(() => new MapControls$1(explCamera), [explCamera]);
3838
4031
  React.useEffect(() => {
3839
4032
  controls.connect(explDomElement);
4033
+ return () => void controls.dispose();
4034
+ }, [explDomElement, controls]);
4035
+ React.useEffect(() => {
3840
4036
  const callback = (e) => {
3841
4037
  invalidate();
3842
4038
  if (onChange) onChange(e);
3843
4039
  };
4040
+ const onStartCb = (e) => {
4041
+ if (onStart) onStart(e);
4042
+ };
4043
+ const onEndCb = (e) => {
4044
+ if (onEnd) onEnd(e);
4045
+ };
3844
4046
  controls.addEventListener("change", callback);
3845
- if (onStart) controls.addEventListener("start", onStart);
3846
- if (onEnd) controls.addEventListener("end", onEnd);
4047
+ controls.addEventListener("start", onStartCb);
4048
+ controls.addEventListener("end", onEndCb);
3847
4049
  return () => {
3848
- controls.dispose();
4050
+ controls.removeEventListener("start", onStartCb);
4051
+ controls.removeEventListener("end", onEndCb);
3849
4052
  controls.removeEventListener("change", callback);
3850
- if (onStart) controls.removeEventListener("start", onStart);
3851
- if (onEnd) controls.removeEventListener("end", onEnd);
3852
4053
  };
3853
- }, [onChange, onStart, onEnd, controls, invalidate, explDomElement]);
4054
+ }, [onChange, onStart, onEnd, controls, invalidate]);
3854
4055
  React.useEffect(() => {
3855
4056
  if (makeDefault) {
3856
4057
  const old = get().controls;
@@ -3858,7 +4059,9 @@ const MapControls = /* @__PURE__ */ React.forwardRef((props = { enableDamping: t
3858
4059
  return () => set({ controls: old });
3859
4060
  }
3860
4061
  }, [makeDefault, controls]);
3861
- useFrame(() => controls.update(), -1);
4062
+ useFrame(() => {
4063
+ if (controls.enabled) controls.update();
4064
+ }, -1);
3862
4065
  return /* @__PURE__ */ React.createElement("primitive", { ref, object: controls, enableDamping: true, ...rest });
3863
4066
  });
3864
4067
 
@@ -3984,20 +4187,16 @@ const OrbitControls = /* @__PURE__ */ React.forwardRef(
3984
4187
  onEnd,
3985
4188
  ...restProps
3986
4189
  }, ref) => {
3987
- const invalidate = useThree((state) => state.invalidate);
3988
- const defaultCamera = useThree((state) => state.camera);
3989
- const gl = useThree((state) => state.gl);
3990
- const events = useThree((state) => state.events);
3991
- const setEvents = useThree((state) => state.setEvents);
3992
- const set = useThree((state) => state.set);
3993
- const get = useThree((state) => state.get);
3994
- const performance = useThree((state) => state.performance);
4190
+ const { invalidate, camera: defaultCamera, renderer, events, setEvents, set, get, performance } = useThree();
3995
4191
  const explCamera = camera || defaultCamera;
3996
- const explDomElement = domElement || events.connected || gl.domElement;
4192
+ const explDomElement = domElement || events.connected || renderer.domElement;
3997
4193
  const controls = React.useMemo(() => new OrbitControls$1(explCamera), [explCamera]);
3998
- useFrame(() => {
3999
- if (controls.enabled) controls.update();
4000
- }, -1);
4194
+ useFrame(
4195
+ () => {
4196
+ if (controls.enabled) controls.update();
4197
+ },
4198
+ { before: "update" }
4199
+ );
4001
4200
  React.useEffect(() => {
4002
4201
  if (keyEvents) {
4003
4202
  controls.connect(keyEvents === true ? explDomElement : keyEvents);
@@ -4111,9 +4310,8 @@ function PresentationControls({
4111
4310
  azimuth = [-Infinity, Infinity],
4112
4311
  damping = 0.25
4113
4312
  }) {
4114
- const events = useThree((state) => state.events);
4115
- const gl = useThree((state) => state.gl);
4116
- const explDomElement = domElement || events.connected || gl.domElement;
4313
+ const { events, renderer, invalidate } = useThree();
4314
+ const explDomElement = domElement || events.connected || renderer.domElement;
4117
4315
  const { size } = useThree();
4118
4316
  const rPolar = React.useMemo(
4119
4317
  () => [rotation[0] + polar[0], rotation[0] + polar[1]],
@@ -4130,18 +4328,20 @@ function PresentationControls({
4130
4328
  React.useEffect(() => {
4131
4329
  if (global && cursor && enabled) {
4132
4330
  explDomElement.style.cursor = "grab";
4133
- gl.domElement.style.cursor = "";
4331
+ renderer.domElement.style.cursor = "";
4134
4332
  return () => {
4135
4333
  explDomElement.style.cursor = "default";
4136
- gl.domElement.style.cursor = "default";
4334
+ renderer.domElement.style.cursor = "default";
4137
4335
  };
4138
4336
  }
4139
4337
  }, [global, cursor, explDomElement, enabled]);
4140
4338
  const [animation] = React.useState({ scale: 1, rotation: rInitial, damping });
4141
4339
  const ref = React.useRef(null);
4142
4340
  useFrame((state, delta) => {
4143
- easing.damp3(ref.current.scale, animation.scale, animation.damping, delta);
4144
- 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();
4145
4345
  });
4146
4346
  const bind = useGesture(
4147
4347
  {
@@ -4149,6 +4349,7 @@ function PresentationControls({
4149
4349
  if (cursor && !global && enabled) explDomElement.style.cursor = last ? "auto" : "grab";
4150
4350
  },
4151
4351
  onDrag: ({ down, delta: [x, y], memo: [oldY, oldX] = animation.rotation || rInitial }) => {
4352
+ invalidate();
4152
4353
  if (!enabled) return [y, x];
4153
4354
  if (cursor) explDomElement.style.cursor = down ? "grabbing" : "grab";
4154
4355
  x = MathUtils.clamp(oldX + x / size.width * Math.PI * speed, ...rAzimuth);
@@ -4157,14 +4358,27 @@ function PresentationControls({
4157
4358
  animation.rotation = snap && !down ? rInitial : [y, x, 0];
4158
4359
  animation.damping = snap && !down && typeof snap !== "boolean" ? snap : damping;
4159
4360
  return [y, x];
4361
+ },
4362
+ onDragEnd: () => {
4363
+ invalidate();
4160
4364
  }
4161
4365
  },
4162
- { target: global ? explDomElement : void 0 }
4366
+ { target: global ? explDomElement : void 0, enabled }
4163
4367
  );
4164
4368
  return /* @__PURE__ */ React.createElement("group", { ref, ...bind?.() }, children);
4165
4369
  }
4166
4370
 
4371
+ function roundToPixelRatio(value) {
4372
+ const ratio = window.devicePixelRatio || 1;
4373
+ return Math.round(value * ratio) / ratio;
4374
+ }
4375
+ function getMaxAnisotropy(renderer) {
4376
+ const r = renderer;
4377
+ return r?.getMaxAnisotropy?.() ?? r?.capabilities?.getMaxAnisotropy?.() ?? 1;
4378
+ }
4379
+
4167
4380
  const context$7 = /* @__PURE__ */ React.createContext(null);
4381
+ const rootCache = /* @__PURE__ */ new WeakMap();
4168
4382
  function useScroll() {
4169
4383
  return React.useContext(context$7);
4170
4384
  }
@@ -4178,6 +4392,7 @@ function ScrollControls({
4178
4392
  damping = 0.25,
4179
4393
  maxSpeed = Infinity,
4180
4394
  prepend = false,
4395
+ pixelPerfect = true,
4181
4396
  style = {},
4182
4397
  children
4183
4398
  }) {
@@ -4187,6 +4402,7 @@ function ScrollControls({
4187
4402
  const [fixed] = React.useState(() => document.createElement("div"));
4188
4403
  const target = gl.domElement.parentNode;
4189
4404
  const scroll = React.useRef(0);
4405
+ const disableScrollUpdate = React.useRef(false);
4190
4406
  const state = React.useMemo(() => {
4191
4407
  const state2 = {
4192
4408
  el,
@@ -4199,6 +4415,7 @@ function ScrollControls({
4199
4415
  delta: 0,
4200
4416
  scroll,
4201
4417
  pages,
4418
+ pixelPerfect,
4202
4419
  // 0-1 for a range between from -> from + distance
4203
4420
  range(from, distance2, margin = 0) {
4204
4421
  const start = from - margin;
@@ -4217,7 +4434,7 @@ function ScrollControls({
4217
4434
  }
4218
4435
  };
4219
4436
  return state2;
4220
- }, [eps, damping, horizontal, pages]);
4437
+ }, [eps, damping, horizontal, pages, pixelPerfect]);
4221
4438
  React.useEffect(() => {
4222
4439
  el.style.position = "absolute";
4223
4440
  el.style.width = "100%";
@@ -4267,16 +4484,20 @@ function ScrollControls({
4267
4484
  const scrollLength = el[horizontal ? "scrollWidth" : "scrollHeight"];
4268
4485
  const scrollThreshold = scrollLength - containerLength;
4269
4486
  let current = 0;
4270
- let disableScroll = true;
4487
+ let disableScroll = false;
4271
4488
  let firstRun = true;
4272
4489
  const onScroll = () => {
4273
4490
  if (!enabled || firstRun) return;
4491
+ if (disableScrollUpdate.current) {
4492
+ disableScrollUpdate.current = false;
4493
+ return;
4494
+ }
4274
4495
  invalidate();
4275
4496
  current = el[horizontal ? "scrollLeft" : "scrollTop"];
4276
4497
  scroll.current = current / scrollThreshold;
4277
4498
  if (infinite) {
4278
4499
  if (!disableScroll) {
4279
- if (current >= scrollThreshold) {
4500
+ if (current >= scrollThreshold - 1) {
4280
4501
  const damp = 1 - state.offset;
4281
4502
  el[horizontal ? "scrollLeft" : "scrollTop"] = 1;
4282
4503
  scroll.current = state.offset = -damp;
@@ -4293,11 +4514,34 @@ function ScrollControls({
4293
4514
  };
4294
4515
  el.addEventListener("scroll", onScroll, { passive: true });
4295
4516
  requestAnimationFrame(() => firstRun = false);
4296
- const onWheel = (e) => el.scrollLeft += e.deltaY / 2;
4297
- 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);
4298
4541
  return () => {
4299
4542
  el.removeEventListener("scroll", onScroll);
4300
- if (horizontal) el.removeEventListener("wheel", onWheel);
4543
+ if (horizontal || infinite) el.removeEventListener("wheel", onWheel);
4544
+ if (horizontal) window.removeEventListener("resize", onResize);
4301
4545
  };
4302
4546
  }
4303
4547
  }, [el, events, size, infinite, state, invalidate, horizontal, enabled]);
@@ -4330,10 +4574,23 @@ const ScrollHtml = React.forwardRef(
4330
4574
  React.useImperativeHandle(ref, () => group.current, []);
4331
4575
  const { width, height } = useThree((state2) => state2.size);
4332
4576
  const fiberState = React.useContext(context$a);
4333
- const root = React.useMemo(() => ReactDOM.createRoot(state.fixed), [state.fixed]);
4577
+ const root = React.useMemo(() => {
4578
+ let root2 = rootCache.get(state.fixed);
4579
+ if (!root2) {
4580
+ root2 = ReactDOM.createRoot(state.fixed);
4581
+ rootCache.set(state.fixed, root2);
4582
+ }
4583
+ return root2;
4584
+ }, [state.fixed]);
4334
4585
  useFrame(() => {
4335
4586
  if (state.delta > state.eps) {
4336
- 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)`;
4587
+ let x = state.horizontal ? -width * (state.pages - 1) * state.offset : 0;
4588
+ let y = state.horizontal ? 0 : height * (state.pages - 1) * -state.offset;
4589
+ if (state.pixelPerfect) {
4590
+ x = roundToPixelRatio(x);
4591
+ y = roundToPixelRatio(y);
4592
+ }
4593
+ group.current.style.transform = `translate3d(${x}px,${y}px,0)`;
4337
4594
  }
4338
4595
  });
4339
4596
  root.render(
@@ -4701,10 +4958,10 @@ const CloudInstance = /* @__PURE__ */ React.forwardRef(
4701
4958
  return /* @__PURE__ */ React.createElement("group", { ref, ...props });
4702
4959
  }
4703
4960
  );
4704
- const Cloud = /* @__PURE__ */ React.forwardRef((props, fref) => {
4961
+ const Cloud = /* @__PURE__ */ React.forwardRef(({ materialFactory, ...props }, fref) => {
4705
4962
  const parent = React.useContext(context$6);
4706
4963
  if (parent) return /* @__PURE__ */ React.createElement(CloudInstance, { ref: fref, ...props });
4707
- 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 }));
4708
4965
  });
4709
4966
 
4710
4967
  const CatmullRomLine = /* @__PURE__ */ React.forwardRef(function CatmullRomLine2({
@@ -5226,7 +5483,7 @@ const Text3D = /* @__PURE__ */ React.forwardRef(
5226
5483
  return {
5227
5484
  font,
5228
5485
  size,
5229
- height,
5486
+ depth: height,
5230
5487
  bevelThickness,
5231
5488
  bevelSize,
5232
5489
  bevelEnabled,
@@ -5262,380 +5519,9 @@ const Text3D = /* @__PURE__ */ React.forwardRef(
5262
5519
  }
5263
5520
  );
5264
5521
 
5265
- const WireframeMaterialShaders = {
5266
- uniforms: {
5267
- strokeOpacity: 1,
5268
- fillOpacity: 0.25,
5269
- fillMix: 0,
5270
- thickness: 0.05,
5271
- colorBackfaces: false,
5272
- dashInvert: true,
5273
- dash: false,
5274
- dashRepeats: 4,
5275
- dashLength: 0.5,
5276
- squeeze: false,
5277
- squeezeMin: 0.2,
5278
- squeezeMax: 1,
5279
- stroke: /* @__PURE__ */ new THREE.Color("#ff0000"),
5280
- backfaceStroke: /* @__PURE__ */ new THREE.Color("#0000ff"),
5281
- fill: /* @__PURE__ */ new THREE.Color("#00ff00")
5282
- },
5283
- vertex: (
5284
- /* glsl */
5285
- `
5286
- attribute vec3 barycentric;
5287
-
5288
- varying vec3 v_edges_Barycentric;
5289
- varying vec3 v_edges_Position;
5290
-
5291
- void initWireframe() {
5292
- v_edges_Barycentric = barycentric;
5293
- v_edges_Position = position.xyz;
5294
- }
5295
- `
5296
- ),
5297
- fragment: (
5298
- /* glsl */
5299
- `
5300
- #ifndef PI
5301
- #define PI 3.1415926535897932384626433832795
5302
- #endif
5303
-
5304
- varying vec3 v_edges_Barycentric;
5305
- varying vec3 v_edges_Position;
5306
-
5307
- uniform float strokeOpacity;
5308
- uniform float fillOpacity;
5309
- uniform float fillMix;
5310
- uniform float thickness;
5311
- uniform bool colorBackfaces;
5312
-
5313
- // Dash
5314
- uniform bool dashInvert;
5315
- uniform bool dash;
5316
- uniform bool dashOnly;
5317
- uniform float dashRepeats;
5318
- uniform float dashLength;
5319
-
5320
- // Squeeze
5321
- uniform bool squeeze;
5322
- uniform float squeezeMin;
5323
- uniform float squeezeMax;
5324
-
5325
- // Colors
5326
- uniform vec3 stroke;
5327
- uniform vec3 backfaceStroke;
5328
- uniform vec3 fill;
5329
-
5330
- // This is like
5331
- float wireframe_aastep(float threshold, float dist) {
5332
- float afwidth = fwidth(dist) * 0.5;
5333
- return smoothstep(threshold - afwidth, threshold + afwidth, dist);
5334
- }
5335
-
5336
- float wireframe_map(float value, float min1, float max1, float min2, float max2) {
5337
- return min2 + (value - min1) * (max2 - min2) / (max1 - min1);
5338
- }
5339
-
5340
- float getWireframe() {
5341
- vec3 barycentric = v_edges_Barycentric;
5342
-
5343
- // Distance from center of each triangle to its edges.
5344
- float d = min(min(barycentric.x, barycentric.y), barycentric.z);
5345
-
5346
- // for dashed rendering, we can use this to get the 0 .. 1 value of the line length
5347
- float positionAlong = max(barycentric.x, barycentric.y);
5348
- if (barycentric.y < barycentric.x && barycentric.y < barycentric.z) {
5349
- positionAlong = 1.0 - positionAlong;
5350
- }
5351
-
5352
- // the thickness of the stroke
5353
- float computedThickness = wireframe_map(thickness, 0.0, 1.0, 0.0, 0.34);
5354
-
5355
- // if we want to shrink the thickness toward the center of the line segment
5356
- if (squeeze) {
5357
- computedThickness *= mix(squeezeMin, squeezeMax, (1.0 - sin(positionAlong * PI)));
5358
- }
5359
-
5360
- // Create dash pattern
5361
- if (dash) {
5362
- // here we offset the stroke position depending on whether it
5363
- // should overlap or not
5364
- float offset = 1.0 / dashRepeats * dashLength / 2.0;
5365
- if (!dashInvert) {
5366
- offset += 1.0 / dashRepeats / 2.0;
5367
- }
5368
-
5369
- // if we should animate the dash or not
5370
- // if (dashAnimate) {
5371
- // offset += time * 0.22;
5372
- // }
5373
-
5374
- // create the repeating dash pattern
5375
- float pattern = fract((positionAlong + offset) * dashRepeats);
5376
- computedThickness *= 1.0 - wireframe_aastep(dashLength, pattern);
5377
- }
5378
-
5379
- // compute the anti-aliased stroke edge
5380
- float edge = 1.0 - wireframe_aastep(computedThickness, d);
5381
-
5382
- return edge;
5383
- }
5384
- `
5385
- )
5386
- };
5387
- const WireframeMaterial = /* @__PURE__ */ shaderMaterial(
5388
- WireframeMaterialShaders.uniforms,
5389
- WireframeMaterialShaders.vertex + /* glsl */
5390
- `
5391
- void main() {
5392
- initWireframe();
5393
- gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
5394
- }
5395
- `,
5396
- WireframeMaterialShaders.fragment + /* glsl */
5397
- `
5398
- void main () {
5399
- // Compute color
5400
-
5401
- float edge = getWireframe();
5402
- vec4 colorStroke = vec4(stroke, edge);
5403
-
5404
- #ifdef FLIP_SIDED
5405
- colorStroke.rgb = backfaceStroke;
5406
- #endif
5407
-
5408
- vec4 colorFill = vec4(fill, fillOpacity);
5409
- vec4 outColor = mix(colorFill, colorStroke, edge * strokeOpacity);
5410
-
5411
- gl_FragColor = outColor;
5412
- }
5413
- `
5414
- );
5415
- function setWireframeOverride(material, uniforms) {
5416
- material.onBeforeCompile = (shader) => {
5417
- shader.uniforms = {
5418
- ...shader.uniforms,
5419
- ...uniforms
5420
- };
5421
- shader.vertexShader = shader.vertexShader.replace(
5422
- "void main() {",
5423
- `
5424
- ${WireframeMaterialShaders.vertex}
5425
- void main() {
5426
- initWireframe();
5427
- `
5428
- );
5429
- shader.fragmentShader = shader.fragmentShader.replace(
5430
- "void main() {",
5431
- `
5432
- ${WireframeMaterialShaders.fragment}
5433
- void main() {
5434
- `
5435
- );
5436
- shader.fragmentShader = shader.fragmentShader.replace(
5437
- "#include <color_fragment>",
5438
- /* glsl */
5439
- `
5440
- #include <color_fragment>
5441
- float edge = getWireframe();
5442
- vec4 colorStroke = vec4(stroke, edge);
5443
- #ifdef FLIP_SIDED
5444
- colorStroke.rgb = backfaceStroke;
5445
- #endif
5446
- vec4 colorFill = vec4(mix(diffuseColor.rgb, fill, fillMix), mix(diffuseColor.a, fillOpacity, fillMix));
5447
- vec4 outColor = mix(colorFill, colorStroke, edge * strokeOpacity);
5448
-
5449
- diffuseColor.rgb = outColor.rgb;
5450
- diffuseColor.a *= outColor.a;
5451
- `
5452
- );
5453
- };
5454
- material.side = THREE.DoubleSide;
5455
- material.transparent = true;
5456
- }
5457
- function useWireframeUniforms(uniforms, props) {
5458
- React.useEffect(
5459
- () => void (uniforms.fillOpacity.value = props.fillOpacity ?? uniforms.fillOpacity.value),
5460
- [props.fillOpacity]
5461
- );
5462
- React.useEffect(() => void (uniforms.fillMix.value = props.fillMix ?? uniforms.fillMix.value), [props.fillMix]);
5463
- React.useEffect(
5464
- () => void (uniforms.strokeOpacity.value = props.strokeOpacity ?? uniforms.strokeOpacity.value),
5465
- [props.strokeOpacity]
5466
- );
5467
- React.useEffect(
5468
- () => void (uniforms.thickness.value = props.thickness ?? uniforms.thickness.value),
5469
- [props.thickness]
5470
- );
5471
- React.useEffect(() => void (uniforms.colorBackfaces.value = !!props.colorBackfaces), [props.colorBackfaces]);
5472
- React.useEffect(() => void (uniforms.dash.value = !!props.dash), [props.dash]);
5473
- React.useEffect(() => void (uniforms.dashInvert.value = !!props.dashInvert), [props.dashInvert]);
5474
- React.useEffect(
5475
- () => void (uniforms.dashRepeats.value = props.dashRepeats ?? uniforms.dashRepeats.value),
5476
- [props.dashRepeats]
5477
- );
5478
- React.useEffect(
5479
- () => void (uniforms.dashLength.value = props.dashLength ?? uniforms.dashLength.value),
5480
- [props.dashLength]
5481
- );
5482
- React.useEffect(() => void (uniforms.squeeze.value = !!props.squeeze), [props.squeeze]);
5483
- React.useEffect(
5484
- () => void (uniforms.squeezeMin.value = props.squeezeMin ?? uniforms.squeezeMin.value),
5485
- [props.squeezeMin]
5486
- );
5487
- React.useEffect(
5488
- () => void (uniforms.squeezeMax.value = props.squeezeMax ?? uniforms.squeezeMax.value),
5489
- [props.squeezeMax]
5490
- );
5491
- React.useEffect(
5492
- () => void (uniforms.stroke.value = props.stroke ? new THREE.Color(props.stroke) : uniforms.stroke.value),
5493
- [props.stroke]
5494
- );
5495
- React.useEffect(
5496
- () => void (uniforms.fill.value = props.fill ? new THREE.Color(props.fill) : uniforms.fill.value),
5497
- [props.fill]
5498
- );
5499
- React.useEffect(
5500
- () => void (uniforms.backfaceStroke.value = props.backfaceStroke ? new THREE.Color(props.backfaceStroke) : uniforms.backfaceStroke.value),
5501
- [props.backfaceStroke]
5502
- );
5503
- }
5504
-
5505
- function isWithGeometry(object) {
5506
- return !!object?.geometry;
5507
- }
5508
- function isGeometry(object) {
5509
- return !!object?.isBufferGeometry;
5510
- }
5511
- function isRefObject$1(object) {
5512
- return !!object?.current;
5513
- }
5514
- function isRef$1(object) {
5515
- return object?.current !== void 0;
5516
- }
5517
- function isWireframeGeometry(geometry) {
5518
- return geometry.type === "WireframeGeometry";
5519
- }
5520
- function getUniforms() {
5521
- const u = {};
5522
- for (const key in WireframeMaterialShaders.uniforms) {
5523
- u[key] = { value: WireframeMaterialShaders.uniforms[key] };
5524
- }
5525
- return u;
5526
- }
5527
- function getBarycentricCoordinates(geometry, removeEdge) {
5528
- const position = geometry.getAttribute("position");
5529
- const count = position.count;
5530
- const barycentric = [];
5531
- for (let i = 0; i < count; i++) {
5532
- const even = i % 2 === 0;
5533
- const Q = removeEdge ? 1 : 0;
5534
- if (even) {
5535
- barycentric.push(0, 0, 1, 0, 1, 0, 1, 0, Q);
5536
- } else {
5537
- barycentric.push(0, 1, 0, 0, 0, 1, 1, 0, Q);
5538
- }
5539
- }
5540
- return new THREE.BufferAttribute(Float32Array.from(barycentric), 3);
5541
- }
5542
- function getInputGeometry(inputGeometry) {
5543
- const geo = isRefObject$1(inputGeometry) ? inputGeometry.current : inputGeometry;
5544
- if (!isGeometry(geo)) {
5545
- if (isWireframeGeometry(geo)) {
5546
- throw new Error("Wireframe: WireframeGeometry is not supported.");
5547
- }
5548
- const parent = geo.parent;
5549
- if (isWithGeometry(parent)) {
5550
- if (isWireframeGeometry(parent.geometry)) {
5551
- throw new Error("Wireframe: WireframeGeometry is not supported.");
5552
- }
5553
- return parent.geometry;
5554
- }
5555
- } else {
5556
- return geo;
5557
- }
5558
- }
5559
- function setBarycentricCoordinates(geometry, simplify) {
5560
- if (geometry.index) {
5561
- console.warn("Wireframe: Requires non-indexed geometry, converting to non-indexed geometry.");
5562
- const nonIndexedGeo = geometry.toNonIndexed();
5563
- geometry.copy(nonIndexedGeo);
5564
- geometry.setIndex(null);
5565
- }
5566
- const newBarycentric = getBarycentricCoordinates(geometry, simplify);
5567
- geometry.setAttribute("barycentric", newBarycentric);
5568
- }
5569
- function WireframeWithCustomGeo({
5570
- geometry: customGeometry,
5571
- simplify = false,
5572
- ...props
5573
- }) {
5574
- extend({ MeshWireframeMaterial: WireframeMaterial });
5575
- const [geometry, setGeometry] = React.useState(null);
5576
- React.useLayoutEffect(() => {
5577
- const geom = getInputGeometry(customGeometry);
5578
- if (!geom) {
5579
- throw new Error("Wireframe: geometry prop must be a BufferGeometry or a ref to a BufferGeometry.");
5580
- }
5581
- setBarycentricCoordinates(geom, simplify);
5582
- if (isRef$1(customGeometry)) {
5583
- setGeometry(geom);
5584
- }
5585
- }, [simplify, customGeometry]);
5586
- const drawnGeo = isRef$1(customGeometry) ? geometry : customGeometry;
5587
- return /* @__PURE__ */ React.createElement(React.Fragment, null, drawnGeo && /* @__PURE__ */ React.createElement("mesh", { geometry: drawnGeo }, /* @__PURE__ */ React.createElement(
5588
- "meshWireframeMaterial",
5589
- {
5590
- attach: "material",
5591
- transparent: true,
5592
- side: THREE.DoubleSide,
5593
- polygonOffset: true,
5594
- polygonOffsetFactor: -4,
5595
- ...props
5596
- }
5597
- )));
5598
- }
5599
- function WireframeWithoutCustomGeo({
5600
- simplify = false,
5601
- ...props
5602
- }) {
5603
- const objectRef = React.useRef(null);
5604
- const uniforms = React.useMemo(() => getUniforms(), [WireframeMaterialShaders.uniforms]);
5605
- useWireframeUniforms(uniforms, props);
5606
- React.useLayoutEffect(() => {
5607
- const geom = getInputGeometry(objectRef);
5608
- if (!geom) {
5609
- throw new Error("Wireframe: Must be a child of a Mesh, Line or Points object or specify a geometry prop.");
5610
- }
5611
- const og = geom.clone();
5612
- setBarycentricCoordinates(geom, simplify);
5613
- return () => {
5614
- geom.copy(og);
5615
- og.dispose();
5616
- };
5617
- }, [simplify]);
5618
- React.useLayoutEffect(() => {
5619
- const parentMesh = objectRef.current.parent;
5620
- const og = parentMesh.material.clone();
5621
- setWireframeOverride(parentMesh.material, uniforms);
5622
- return () => {
5623
- parentMesh.material.dispose();
5624
- parentMesh.material = og;
5625
- };
5626
- }, []);
5627
- return /* @__PURE__ */ React.createElement("object3D", { ref: objectRef });
5628
- }
5629
- function Wireframe({ geometry: customGeometry, ...props }) {
5630
- if (customGeometry) {
5631
- return /* @__PURE__ */ React.createElement(WireframeWithCustomGeo, { geometry: customGeometry, ...props });
5632
- }
5633
- return /* @__PURE__ */ React.createElement(WireframeWithoutCustomGeo, { ...props });
5634
- }
5635
-
5636
- const Context = /* @__PURE__ */ React.createContext({});
5637
- const useGizmoContext = () => {
5638
- return React.useContext(Context);
5522
+ const Context = /* @__PURE__ */ React.createContext({});
5523
+ const useGizmoContext = () => {
5524
+ return React.useContext(Context);
5639
5525
  };
5640
5526
  const turnRate = 2 * Math.PI;
5641
5527
  const dummy = /* @__PURE__ */ new Object3D();
@@ -5780,7 +5666,7 @@ const FaceMaterial = ({
5780
5666
  "meshBasicMaterial",
5781
5667
  {
5782
5668
  map: texture,
5783
- "map-anisotropy": renderer.getMaxAnisotropy() || 1,
5669
+ "map-anisotropy": getMaxAnisotropy(renderer),
5784
5670
  attach: `material-${index}`,
5785
5671
  color: hover ? hoverColor : "white",
5786
5672
  transparent: true,
@@ -5891,7 +5777,7 @@ function AxisHead({
5891
5777
  "spriteMaterial",
5892
5778
  {
5893
5779
  map: texture,
5894
- "map-anisotropy": renderer.getMaxAnisotropy() || 1,
5780
+ "map-anisotropy": getMaxAnisotropy(renderer),
5895
5781
  alphaTest: 0.3,
5896
5782
  opacity: label ? 1 : 0.75,
5897
5783
  toneMapped: false
@@ -5928,6 +5814,11 @@ const GizmoViewport = ({
5928
5814
  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 }))));
5929
5815
  };
5930
5816
 
5817
+ const roundEven = (n) => {
5818
+ const rounded = Math.ceil(n);
5819
+ return rounded % 2 === 0 ? rounded : rounded + 1;
5820
+ };
5821
+
5931
5822
  const v1 = /* @__PURE__ */ new Vector3();
5932
5823
  const v2 = /* @__PURE__ */ new Vector3();
5933
5824
  const v3 = /* @__PURE__ */ new Vector3();
@@ -5999,7 +5890,7 @@ const getCameraCSSMatrix = /* @__PURE__ */ ((multipliers) => {
5999
5890
  const getObjectCSSMatrix = /* @__PURE__ */ ((scaleMultipliers) => {
6000
5891
  return (matrix, factor) => getCSSMatrix(matrix, scaleMultipliers(factor), "translate(-50%,-50%)");
6001
5892
  })((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]);
6002
- function isRefObject(ref) {
5893
+ function isRefObject$1(ref) {
6003
5894
  return ref && typeof ref === "object" && "current" in ref;
6004
5895
  }
6005
5896
  const Html = /* @__PURE__ */ React.forwardRef(
@@ -6026,9 +5917,12 @@ const Html = /* @__PURE__ */ React.forwardRef(
6026
5917
  as = "div",
6027
5918
  wrapperClass,
6028
5919
  pointerEvents = "auto",
5920
+ pixelPerfect = true,
6029
5921
  ...props
6030
5922
  }, ref) => {
6031
5923
  const { gl, camera, scene, size, raycaster, events, viewport } = useThree();
5924
+ const forceEven = useThree((state) => state.internal.forceEven);
5925
+ const { frustum } = useThree();
6032
5926
  const [el] = React.useState(() => document.createElement(as));
6033
5927
  const root = React.useRef(null);
6034
5928
  const group = React.useRef(null);
@@ -6040,7 +5934,7 @@ const Html = /* @__PURE__ */ React.forwardRef(
6040
5934
  const occlusionMeshRef = React.useRef(null);
6041
5935
  const isMeshSizeSet = React.useRef(false);
6042
5936
  const isRayCastOcclusion = React.useMemo(() => {
6043
- return occlude && occlude !== "blending" || Array.isArray(occlude) && occlude.length && isRefObject(occlude[0]);
5937
+ return occlude && occlude !== "blending" || Array.isArray(occlude) && occlude.length && isRefObject$1(occlude[0]);
6044
5938
  }, [occlude]);
6045
5939
  React.useLayoutEffect(() => {
6046
5940
  const el2 = gl.domElement;
@@ -6053,38 +5947,50 @@ const Html = /* @__PURE__ */ React.forwardRef(
6053
5947
  el2.style.position = null;
6054
5948
  el2.style.pointerEvents = null;
6055
5949
  }
6056
- }, [occlude]);
5950
+ }, [occlude, zIndexRange, gl.domElement]);
6057
5951
  React.useLayoutEffect(() => {
6058
5952
  if (group.current) {
6059
- const currentRoot = root.current = ReactDOM.createRoot(el);
6060
- scene.updateMatrixWorld();
6061
- if (transform) {
6062
- el.style.cssText = `position:absolute;top:0;left:0;pointer-events:none;overflow:hidden;`;
6063
- } else {
6064
- const vec = calculatePosition(group.current, camera, size);
6065
- el.style.cssText = `position:absolute;top:0;left:0;transform:translate3d(${vec[0]}px,${vec[1]}px,0);transform-origin:0 0;`;
5953
+ if (!root.current) {
5954
+ root.current = ReactDOM.createRoot(el);
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
+ }
6066
5961
  }
6067
5962
  if (target) {
6068
5963
  if (prepend) target.prepend(el);
6069
5964
  else target.appendChild(el);
6070
5965
  }
6071
5966
  return () => {
6072
- if (target) target.removeChild(el);
6073
- currentRoot.unmount();
5967
+ if (target && target.contains(el)) {
5968
+ target.removeChild(el);
5969
+ }
6074
5970
  };
6075
5971
  }
6076
- }, [target, transform]);
5972
+ }, [target, transform, el, prepend, scene]);
6077
5973
  React.useLayoutEffect(() => {
6078
5974
  if (wrapperClass) el.className = wrapperClass;
6079
- }, [wrapperClass]);
5975
+ }, [wrapperClass, el]);
5976
+ React.useLayoutEffect(() => {
5977
+ return () => {
5978
+ if (root.current) {
5979
+ root.current.unmount();
5980
+ root.current = null;
5981
+ }
5982
+ };
5983
+ }, []);
6080
5984
  const styles = React.useMemo(() => {
5985
+ const width = forceEven ? roundEven(size.width) : size.width;
5986
+ const height = forceEven ? roundEven(size.height) : size.height;
6081
5987
  if (transform) {
6082
5988
  return {
6083
5989
  position: "absolute",
6084
5990
  top: 0,
6085
5991
  left: 0,
6086
- width: size.width,
6087
- height: size.height,
5992
+ width,
5993
+ height,
6088
5994
  transformStyle: "preserve-3d",
6089
5995
  pointerEvents: "none"
6090
5996
  };
@@ -6093,15 +5999,15 @@ const Html = /* @__PURE__ */ React.forwardRef(
6093
5999
  position: "absolute",
6094
6000
  transform: center ? "translate3d(-50%,-50%,0)" : "none",
6095
6001
  ...fullscreen && {
6096
- top: -size.height / 2,
6097
- left: -size.width / 2,
6098
- width: size.width,
6099
- height: size.height
6002
+ top: forceEven ? roundEven(-size.height / 2) : -size.height / 2,
6003
+ left: forceEven ? roundEven(-size.width / 2) : -size.width / 2,
6004
+ width,
6005
+ height
6100
6006
  },
6101
6007
  ...style
6102
6008
  };
6103
6009
  }
6104
- }, [style, center, fullscreen, size, transform]);
6010
+ }, [style, center, fullscreen, size, transform, forceEven]);
6105
6011
  const transformInnerStyles = React.useMemo(
6106
6012
  () => ({ position: "absolute", pointerEvents }),
6107
6013
  [pointerEvents]
@@ -6115,7 +6021,7 @@ const Html = /* @__PURE__ */ React.forwardRef(
6115
6021
  } else {
6116
6022
  root.current?.render(/* @__PURE__ */ React.createElement("div", { ref, style: styles, className, children }));
6117
6023
  }
6118
- });
6024
+ }, [transform, styles, transformInnerStyles, ref, className, style, children]);
6119
6025
  const visible = React.useRef(true);
6120
6026
  useFrame((gl2) => {
6121
6027
  if (group.current) {
@@ -6147,7 +6053,12 @@ const Html = /* @__PURE__ */ React.forwardRef(
6147
6053
  const zRange = occlude ? isRayCastOcclusion ? [zIndexRange[0], halfRange] : [halfRange - 1, 0] : zIndexRange;
6148
6054
  el.style.zIndex = `${objectZIndex(group.current, camera, zRange)}`;
6149
6055
  if (transform) {
6150
- const [widthHalf, heightHalf] = [size.width / 2, size.height / 2];
6056
+ let widthHalf = forceEven ? roundEven(size.width / 2) : size.width / 2;
6057
+ let heightHalf = forceEven ? roundEven(size.height / 2) : size.height / 2;
6058
+ if (pixelPerfect) {
6059
+ widthHalf = roundToPixelRatio(widthHalf);
6060
+ heightHalf = roundToPixelRatio(heightHalf);
6061
+ }
6151
6062
  const fov = camera.projectionMatrix.elements[5] * heightHalf;
6152
6063
  const { isOrthographicCamera, top, left, bottom, right } = camera;
6153
6064
  const cameraMatrix = getCameraCSSMatrix(camera.matrixWorldInverse);
@@ -6158,8 +6069,10 @@ const Html = /* @__PURE__ */ React.forwardRef(
6158
6069
  matrix.elements[3] = matrix.elements[7] = matrix.elements[11] = 0;
6159
6070
  matrix.elements[15] = 1;
6160
6071
  }
6161
- el.style.width = size.width + "px";
6162
- el.style.height = size.height + "px";
6072
+ const elWidth = forceEven ? roundEven(size.width) : size.width;
6073
+ const elHeight = forceEven ? roundEven(size.height) : size.height;
6074
+ el.style.width = elWidth + "px";
6075
+ el.style.height = elHeight + "px";
6163
6076
  el.style.perspective = isOrthographicCamera ? "" : `${fov}px`;
6164
6077
  if (transformOuterRef.current && transformInnerRef.current) {
6165
6078
  transformOuterRef.current.style.transform = `${cameraTransform}${cameraMatrix}translate(${widthHalf}px,${heightHalf}px)`;
@@ -6167,7 +6080,13 @@ const Html = /* @__PURE__ */ React.forwardRef(
6167
6080
  }
6168
6081
  } else {
6169
6082
  const scale = distanceFactor === void 0 ? 1 : objectScale(group.current, camera) * distanceFactor;
6170
- el.style.transform = `translate3d(${vec[0]}px,${vec[1]}px,0) scale(${scale})`;
6083
+ let x = forceEven ? roundEven(vec[0]) : vec[0];
6084
+ let y = forceEven ? roundEven(vec[1]) : vec[1];
6085
+ if (pixelPerfect) {
6086
+ x = roundToPixelRatio(x);
6087
+ y = roundToPixelRatio(y);
6088
+ }
6089
+ el.style.transform = `translate3d(${x}px,${y}px,0) scale(${scale})`;
6171
6090
  }
6172
6091
  oldPosition.current = vec;
6173
6092
  oldZoom.current = camera.zoom;
@@ -6178,8 +6097,7 @@ const Html = /* @__PURE__ */ React.forwardRef(
6178
6097
  if (transformOuterRef.current) {
6179
6098
  const el2 = transformOuterRef.current.children[0];
6180
6099
  if (el2?.clientWidth && el2?.clientHeight) {
6181
- const { isOrthographicCamera } = camera;
6182
- if (isOrthographicCamera || geometry) {
6100
+ if (geometry) {
6183
6101
  if (props.scale) {
6184
6102
  if (!Array.isArray(props.scale)) {
6185
6103
  occlusionMeshRef.current.scale.setScalar(1 / props.scale);
@@ -6211,68 +6129,16 @@ const Html = /* @__PURE__ */ React.forwardRef(
6211
6129
  }
6212
6130
  }
6213
6131
  });
6214
- const shaders = React.useMemo(
6215
- () => ({
6216
- vertexShader: !transform ? (
6217
- /* glsl */
6218
- `
6219
- /*
6220
- This shader is from the THREE's SpriteMaterial.
6221
- We need to turn the backing plane into a Sprite
6222
- (make it always face the camera) if "transfrom"
6223
- is false.
6224
- */
6225
- #include <common>
6226
-
6227
- void main() {
6228
- vec2 center = vec2(0., 1.);
6229
- float rotation = 0.0;
6230
-
6231
- // This is somewhat arbitrary, but it seems to work well
6232
- // Need to figure out how to derive this dynamically if it even matters
6233
- float size = 0.03;
6234
-
6235
- vec4 mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );
6236
- vec2 scale;
6237
- scale.x = length( vec3( modelMatrix[ 0 ].x, modelMatrix[ 0 ].y, modelMatrix[ 0 ].z ) );
6238
- scale.y = length( vec3( modelMatrix[ 1 ].x, modelMatrix[ 1 ].y, modelMatrix[ 1 ].z ) );
6239
-
6240
- bool isPerspective = isPerspectiveMatrix( projectionMatrix );
6241
- if ( isPerspective ) scale *= - mvPosition.z;
6242
-
6243
- vec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale * size;
6244
- vec2 rotatedPosition;
6245
- rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;
6246
- rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;
6247
- mvPosition.xy += rotatedPosition;
6248
-
6249
- gl_Position = projectionMatrix * mvPosition;
6250
- }
6251
- `
6252
- ) : void 0,
6253
- fragmentShader: (
6254
- /* glsl */
6255
- `
6256
- void main() {
6257
- gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
6258
- }
6259
- `
6260
- )
6261
- }),
6262
- [transform]
6263
- );
6264
- 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(
6265
- "shaderMaterial",
6266
- {
6267
- side: DoubleSide,
6268
- vertexShader: shaders.vertexShader,
6269
- fragmentShader: shaders.fragmentShader
6270
- }
6271
- )));
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));
6272
6133
  }
6273
6134
  );
6274
6135
 
6275
6136
  const context$4 = /* @__PURE__ */ React.createContext(null);
6137
+ const resolveObject = (object, fallback) => {
6138
+ if (!object) return fallback;
6139
+ if ("current" in object) return object.current;
6140
+ return object;
6141
+ };
6276
6142
 
6277
6143
  const vec1$1 = /* @__PURE__ */ new THREE.Vector3();
6278
6144
  const vec2$1 = /* @__PURE__ */ new THREE.Vector3();
@@ -6307,6 +6173,8 @@ const AxisArrow = ({ direction, axis }) => {
6307
6173
  onDragStart,
6308
6174
  onDrag,
6309
6175
  onDragEnd,
6176
+ onHover,
6177
+ dragState,
6310
6178
  userData,
6311
6179
  LineComponent: Line
6312
6180
  } = React.useContext(context$4);
@@ -6338,7 +6206,13 @@ const AxisArrow = ({ direction, axis }) => {
6338
6206
  const onPointerMove = React.useCallback(
6339
6207
  (e) => {
6340
6208
  e.stopPropagation();
6341
- if (!isHovered) setIsHovered(true);
6209
+ if (!isHovered) {
6210
+ const drag = dragState.current;
6211
+ if (drag === null || drag.component === "Arrow" && drag.axis === axis) {
6212
+ setIsHovered(true);
6213
+ onHover({ component: "Arrow", axis, hovering: true });
6214
+ }
6215
+ }
6342
6216
  if (clickInfo.current) {
6343
6217
  const { clickPoint, dir } = clickInfo.current;
6344
6218
  const [min, max] = translationLimits?.[axis] || [void 0, void 0];
@@ -6357,7 +6231,7 @@ const AxisArrow = ({ direction, axis }) => {
6357
6231
  onDrag(offsetMatrix$1);
6358
6232
  }
6359
6233
  },
6360
- [annotations, onDrag, isHovered, translation, translationLimits, axis]
6234
+ [annotations, onDrag, onHover, dragState, isHovered, translation, translationLimits, axis]
6361
6235
  );
6362
6236
  const onPointerUp = React.useCallback(
6363
6237
  (e) => {
@@ -6372,10 +6246,16 @@ const AxisArrow = ({ direction, axis }) => {
6372
6246
  },
6373
6247
  [annotations, camControls, onDragEnd]
6374
6248
  );
6375
- const onPointerOut = React.useCallback((e) => {
6376
- e.stopPropagation();
6377
- setIsHovered(false);
6378
- }, []);
6249
+ const onPointerOut = React.useCallback(
6250
+ (e) => {
6251
+ e.stopPropagation();
6252
+ if (isHovered) {
6253
+ setIsHovered(false);
6254
+ onHover({ component: "Arrow", axis, hovering: false });
6255
+ }
6256
+ },
6257
+ [onHover, axis, isHovered]
6258
+ );
6379
6259
  const { cylinderLength, coneWidth, coneLength, matrixL } = React.useMemo(() => {
6380
6260
  const coneWidth2 = fixed ? lineWidth / scale * 1.6 : scale / 20;
6381
6261
  const coneLength2 = fixed ? 0.2 : scale / 5;
@@ -6479,6 +6359,8 @@ const rotMatrix = /* @__PURE__ */ new THREE.Matrix4();
6479
6359
  const posNew = /* @__PURE__ */ new THREE.Vector3();
6480
6360
  const ray$1 = /* @__PURE__ */ new THREE.Ray();
6481
6361
  const intersection$1 = /* @__PURE__ */ new THREE.Vector3();
6362
+ const viewPlane = /* @__PURE__ */ new THREE.Plane();
6363
+ const viewIntersection = /* @__PURE__ */ new THREE.Vector3();
6482
6364
  const AxisRotator = ({
6483
6365
  dir1,
6484
6366
  dir2,
@@ -6499,6 +6381,8 @@ const AxisRotator = ({
6499
6381
  onDragStart,
6500
6382
  onDrag,
6501
6383
  onDragEnd,
6384
+ onHover,
6385
+ dragState,
6502
6386
  userData,
6503
6387
  LineComponent: Line
6504
6388
  } = React.useContext(context$4);
@@ -6532,14 +6416,29 @@ const AxisRotator = ({
6532
6416
  const onPointerMove = React.useCallback(
6533
6417
  (e) => {
6534
6418
  e.stopPropagation();
6535
- if (!isHovered) setIsHovered(true);
6419
+ if (!isHovered) {
6420
+ const drag = dragState.current;
6421
+ if (drag === null || drag.component === "Rotator" && drag.axis === axis) {
6422
+ setIsHovered(true);
6423
+ onHover({ component: "Rotator", axis, hovering: true });
6424
+ }
6425
+ }
6536
6426
  if (clickInfo.current) {
6537
6427
  const { clickPoint, origin, e1, e2, normal, plane } = clickInfo.current;
6538
6428
  const [min, max] = rotationLimits?.[axis] || [void 0, void 0];
6539
6429
  ray$1.copy(e.ray);
6540
- ray$1.intersectPlane(plane, intersection$1);
6541
- ray$1.direction.negate();
6542
- ray$1.intersectPlane(plane, intersection$1);
6430
+ const dotProduct = Math.abs(ray$1.direction.dot(normal));
6431
+ if (dotProduct < 0.1) {
6432
+ viewPlane.setFromNormalAndCoplanarPoint(ray$1.direction.clone().negate(), origin);
6433
+ ray$1.intersectPlane(viewPlane, viewIntersection);
6434
+ const toIntersection = viewIntersection.clone().sub(origin);
6435
+ const projectedDist = toIntersection.dot(normal);
6436
+ intersection$1.copy(viewIntersection).sub(normal.clone().multiplyScalar(projectedDist));
6437
+ } else {
6438
+ ray$1.intersectPlane(plane, intersection$1);
6439
+ ray$1.direction.negate();
6440
+ ray$1.intersectPlane(plane, intersection$1);
6441
+ }
6543
6442
  let deltaAngle = calculateAngle(clickPoint, intersection$1, origin, e1, e2);
6544
6443
  let degrees = toDegrees(deltaAngle);
6545
6444
  if (e.shiftKey) {
@@ -6565,7 +6464,7 @@ const AxisRotator = ({
6565
6464
  onDrag(rotMatrix);
6566
6465
  }
6567
6466
  },
6568
- [annotations, onDrag, isHovered, rotationLimits, axis]
6467
+ [annotations, onDrag, onHover, dragState, isHovered, rotationLimits, axis]
6569
6468
  );
6570
6469
  const onPointerUp = React.useCallback(
6571
6470
  (e) => {
@@ -6581,10 +6480,16 @@ const AxisRotator = ({
6581
6480
  },
6582
6481
  [annotations, camControls, onDragEnd]
6583
6482
  );
6584
- const onPointerOut = React.useCallback((e) => {
6585
- e.stopPropagation();
6586
- setIsHovered(false);
6587
- }, []);
6483
+ const onPointerOut = React.useCallback(
6484
+ (e) => {
6485
+ e.stopPropagation();
6486
+ if (isHovered) {
6487
+ setIsHovered(false);
6488
+ onHover({ component: "Rotator", axis, hovering: false });
6489
+ }
6490
+ },
6491
+ [onHover, axis, isHovered]
6492
+ );
6588
6493
  const matrixL = React.useMemo(() => {
6589
6494
  const dir1N = dir1.clone().normalize();
6590
6495
  const dir2N = dir2.clone().normalize();
@@ -6685,6 +6590,8 @@ const PlaneSlider = ({
6685
6590
  onDragStart,
6686
6591
  onDrag,
6687
6592
  onDragEnd,
6593
+ onHover,
6594
+ dragState,
6688
6595
  userData,
6689
6596
  LineComponent: Line
6690
6597
  } = React.useContext(context$4);
@@ -6720,7 +6627,13 @@ const PlaneSlider = ({
6720
6627
  const onPointerMove = React.useCallback(
6721
6628
  (e) => {
6722
6629
  e.stopPropagation();
6723
- if (!isHovered) setIsHovered(true);
6630
+ if (!isHovered) {
6631
+ const drag = dragState.current;
6632
+ if (drag === null || drag.component === "Slider" && drag.axis === axis) {
6633
+ setIsHovered(true);
6634
+ onHover({ component: "Slider", axis, hovering: true });
6635
+ }
6636
+ }
6724
6637
  if (clickInfo.current) {
6725
6638
  const { clickPoint, e1, e2, plane } = clickInfo.current;
6726
6639
  const [minX, maxX] = translationLimits?.[(axis + 1) % 3] || [void 0, void 0];
@@ -6756,7 +6669,7 @@ const PlaneSlider = ({
6756
6669
  onDrag(offsetMatrix);
6757
6670
  }
6758
6671
  },
6759
- [annotations, onDrag, isHovered, translation, translationLimits, axis]
6672
+ [annotations, onDrag, onHover, dragState, isHovered, translation, translationLimits, axis]
6760
6673
  );
6761
6674
  const onPointerUp = React.useCallback(
6762
6675
  (e) => {
@@ -6771,10 +6684,16 @@ const PlaneSlider = ({
6771
6684
  },
6772
6685
  [annotations, camControls, onDragEnd]
6773
6686
  );
6774
- const onPointerOut = React.useCallback((e) => {
6775
- e.stopPropagation();
6776
- setIsHovered(false);
6777
- }, []);
6687
+ const onPointerOut = React.useCallback(
6688
+ (e) => {
6689
+ e.stopPropagation();
6690
+ if (isHovered) {
6691
+ setIsHovered(false);
6692
+ onHover({ component: "Slider", axis, hovering: false });
6693
+ }
6694
+ },
6695
+ [onHover, axis, isHovered]
6696
+ );
6778
6697
  const matrixL = React.useMemo(() => {
6779
6698
  const dir1N = dir1.clone().normalize();
6780
6699
  const dir2N = dir2.clone().normalize();
@@ -6912,6 +6831,8 @@ const ScalingSphere = ({ direction, axis }) => {
6912
6831
  onDragStart,
6913
6832
  onDrag,
6914
6833
  onDragEnd,
6834
+ onHover,
6835
+ dragState,
6915
6836
  userData
6916
6837
  } = React.useContext(context$4);
6917
6838
  const size = useThree((state) => state.size);
@@ -6948,7 +6869,13 @@ const ScalingSphere = ({ direction, axis }) => {
6948
6869
  const onPointerMove = React.useCallback(
6949
6870
  (e) => {
6950
6871
  e.stopPropagation();
6951
- if (!isHovered) setIsHovered(true);
6872
+ if (!isHovered) {
6873
+ const drag = dragState.current;
6874
+ if (drag === null || drag.component === "Sphere" && drag.axis === axis) {
6875
+ setIsHovered(true);
6876
+ onHover({ component: "Sphere", axis, hovering: true });
6877
+ }
6878
+ }
6952
6879
  if (clickInfo.current) {
6953
6880
  const { clickPoint, dir, mPLG, mPLGInv, offsetMultiplier } = clickInfo.current;
6954
6881
  const [min, max] = scaleLimits?.[axis] || [1e-5, void 0];
@@ -6974,7 +6901,7 @@ const ScalingSphere = ({ direction, axis }) => {
6974
6901
  onDrag(scaleMatrix);
6975
6902
  }
6976
6903
  },
6977
- [annotations, position, onDrag, isHovered, scaleLimits, axis]
6904
+ [annotations, position, onDrag, onHover, dragState, isHovered, scaleLimits, axis]
6978
6905
  );
6979
6906
  const onPointerUp = React.useCallback(
6980
6907
  (e) => {
@@ -6991,10 +6918,16 @@ const ScalingSphere = ({ direction, axis }) => {
6991
6918
  },
6992
6919
  [annotations, camControls, onDragEnd, position]
6993
6920
  );
6994
- const onPointerOut = React.useCallback((e) => {
6995
- e.stopPropagation();
6996
- setIsHovered(false);
6997
- }, []);
6921
+ const onPointerOut = React.useCallback(
6922
+ (e) => {
6923
+ e.stopPropagation();
6924
+ if (isHovered) {
6925
+ setIsHovered(false);
6926
+ onHover({ component: "Sphere", axis, hovering: false });
6927
+ }
6928
+ },
6929
+ [onHover, axis, isHovered]
6930
+ );
6998
6931
  const { radius, matrixL } = React.useMemo(() => {
6999
6932
  const radius2 = fixed ? lineWidth / scale * 1.8 : scale / 22.5;
7000
6933
  const quaternion = new THREE.Quaternion().setFromUnitVectors(upV, direction.clone().normalize());
@@ -7049,6 +6982,7 @@ const mW = /* @__PURE__ */ new THREE.Matrix4();
7049
6982
  const mL = /* @__PURE__ */ new THREE.Matrix4();
7050
6983
  const mL0Inv = /* @__PURE__ */ new THREE.Matrix4();
7051
6984
  const mdL = /* @__PURE__ */ new THREE.Matrix4();
6985
+ const mdW = /* @__PURE__ */ new THREE.Matrix4();
7052
6986
  const mG = /* @__PURE__ */ new THREE.Matrix4();
7053
6987
  const bb = /* @__PURE__ */ new THREE.Box3();
7054
6988
  const bbObj = /* @__PURE__ */ new THREE.Box3();
@@ -7064,9 +6998,11 @@ const PivotControls = /* @__PURE__ */ React.forwardRef(
7064
6998
  ({
7065
6999
  enabled = true,
7066
7000
  matrix,
7001
+ object,
7067
7002
  onDragStart,
7068
7003
  onDrag,
7069
7004
  onDragEnd,
7005
+ onHover,
7070
7006
  autoTransform = true,
7071
7007
  anchor,
7072
7008
  disableAxes = false,
@@ -7102,14 +7038,35 @@ const PivotControls = /* @__PURE__ */ React.forwardRef(
7102
7038
  const gizmoRef = React.useRef(null);
7103
7039
  const childrenRef = React.useRef(null);
7104
7040
  const translation = React.useRef([0, 0, 0]);
7041
+ const dragState = React.useRef(null);
7105
7042
  const cameraScale = React.useRef(new THREE.Vector3(1, 1, 1));
7106
7043
  const gizmoScale = React.useRef(new THREE.Vector3(1, 1, 1));
7044
+ React.useEffect(() => {
7045
+ if (object) {
7046
+ const target = resolveObject(object);
7047
+ if (target) {
7048
+ const pivot = ref.current;
7049
+ const didAutoUpdate = target.matrixAutoUpdate;
7050
+ target.updateWorldMatrix(true, true);
7051
+ target.matrixAutoUpdate = false;
7052
+ pivot.matrix.copy(target.matrix);
7053
+ return () => {
7054
+ if (didAutoUpdate) {
7055
+ target.matrixAutoUpdate = true;
7056
+ target.matrix.decompose(target.position, target.quaternion, target.scale);
7057
+ }
7058
+ };
7059
+ }
7060
+ }
7061
+ }, [object]);
7107
7062
  React.useLayoutEffect(() => {
7108
7063
  if (!anchor) return;
7109
- childrenRef.current.updateWorldMatrix(true, true);
7110
- mPInv.copy(childrenRef.current.matrixWorld).invert();
7064
+ const anchorTarget = resolveObject(object, childrenRef.current);
7065
+ if (!anchorTarget) return;
7066
+ anchorTarget.updateWorldMatrix(true, true);
7067
+ mPInv.copy(anchorTarget.matrixWorld).invert();
7111
7068
  bb.makeEmpty();
7112
- childrenRef.current.traverse((obj) => {
7069
+ anchorTarget.traverse((obj) => {
7113
7070
  if (!obj.geometry) return;
7114
7071
  if (!obj.geometry.boundingBox) obj.geometry.computeBoundingBox();
7115
7072
  mL.copy(obj.matrixWorld).premultiply(mPInv);
@@ -7124,15 +7081,24 @@ const PivotControls = /* @__PURE__ */ React.forwardRef(
7124
7081
  gizmoRef.current.position.copy(vPosition);
7125
7082
  invalidate();
7126
7083
  });
7084
+ const mergedUserData = React.useMemo(
7085
+ () => ({
7086
+ ...userData,
7087
+ ...depthTest === false && { interactivePriority: 1 }
7088
+ }),
7089
+ [userData, depthTest]
7090
+ );
7127
7091
  const config = React.useMemo(
7128
7092
  () => ({
7129
7093
  onDragStart: (props2) => {
7094
+ dragState.current = props2;
7130
7095
  mL0.copy(ref.current.matrix);
7131
7096
  mW0.copy(ref.current.matrixWorld);
7132
7097
  onDragStart && onDragStart(props2);
7133
7098
  invalidate();
7134
7099
  },
7135
- onDrag: (mdW) => {
7100
+ onDrag: (mdW_) => {
7101
+ mdW.copy(mdW_);
7136
7102
  mP.copy(parentRef.current.matrixWorld);
7137
7103
  mPInv.copy(mP).invert();
7138
7104
  mW.copy(mW0).premultiply(mdW);
@@ -7142,13 +7108,20 @@ const PivotControls = /* @__PURE__ */ React.forwardRef(
7142
7108
  if (autoTransform) {
7143
7109
  ref.current.matrix.copy(mL);
7144
7110
  }
7145
- onDrag && onDrag(mL, mdL, mW, mdW);
7111
+ const target = resolveObject(object);
7112
+ if (target) target.matrix.copy(mL);
7113
+ onDrag && onDrag({ local: mL, deltaLocal: mdL, world: mW, deltaWorld: mdW });
7146
7114
  invalidate();
7147
7115
  },
7148
7116
  onDragEnd: () => {
7149
- if (onDragEnd) onDragEnd();
7117
+ dragState.current = null;
7118
+ if (onDragEnd) onDragEnd({ local: mL, deltaLocal: mdL, world: mW, deltaWorld: mdW });
7150
7119
  invalidate();
7151
7120
  },
7121
+ onHover: (props2) => {
7122
+ if (onHover) onHover(props2);
7123
+ },
7124
+ dragState,
7152
7125
  translation,
7153
7126
  translationLimits,
7154
7127
  rotationLimits,
@@ -7160,7 +7133,7 @@ const PivotControls = /* @__PURE__ */ React.forwardRef(
7160
7133
  fixed,
7161
7134
  depthTest,
7162
7135
  renderOrder,
7163
- userData,
7136
+ userData: mergedUserData,
7164
7137
  annotations,
7165
7138
  annotationsClass,
7166
7139
  LineComponent: LineComp
@@ -7169,6 +7142,7 @@ const PivotControls = /* @__PURE__ */ React.forwardRef(
7169
7142
  onDragStart,
7170
7143
  onDrag,
7171
7144
  onDragEnd,
7145
+ onHover,
7172
7146
  translation,
7173
7147
  translationLimits,
7174
7148
  rotationLimits,
@@ -7180,11 +7154,12 @@ const PivotControls = /* @__PURE__ */ React.forwardRef(
7180
7154
  ...axisColors,
7181
7155
  hoveredColor,
7182
7156
  opacity,
7183
- userData,
7157
+ mergedUserData,
7184
7158
  autoTransform,
7185
7159
  annotations,
7186
7160
  annotationsClass,
7187
- LineComp
7161
+ LineComp,
7162
+ object
7188
7163
  ]
7189
7164
  );
7190
7165
  const vec = new THREE.Vector3();
@@ -7251,7 +7226,9 @@ const TransformControls = /* @__PURE__ */ React.forwardRef(
7251
7226
  const group = React.useRef(null);
7252
7227
  React.useLayoutEffect(() => {
7253
7228
  if (object) {
7254
- controls.attach(object instanceof THREE.Object3D ? object : object.current);
7229
+ controls.attach(
7230
+ object instanceof THREE.Object3D ? object : object.current
7231
+ );
7255
7232
  } else if (group.current instanceof THREE.Object3D) {
7256
7233
  controls.attach(group.current);
7257
7234
  }
@@ -7347,7 +7324,7 @@ const PointMaterial = /* @__PURE__ */ React.forwardRef((props, ref) => {
7347
7324
  return /* @__PURE__ */ React.createElement("primitive", { ...props, object: material, ref, attach: "material" });
7348
7325
  });
7349
7326
 
7350
- 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) => {
7351
7328
  const sound = React.useRef(null);
7352
7329
  React.useImperativeHandle(ref, () => sound.current, []);
7353
7330
  const camera = useThree(({ camera: camera2 }) => camera2);
@@ -7359,7 +7336,9 @@ const PositionalAudio = /* @__PURE__ */ React.forwardRef(({ url, distance = 1, l
7359
7336
  _sound.setBuffer(Array.isArray(buffer) ? buffer[0] : buffer);
7360
7337
  _sound.setRefDistance(distance);
7361
7338
  _sound.setLoop(loop);
7362
- if (autoplay && !_sound.isPlaying) _sound.play();
7339
+ if (autoplay && !_sound.isPlaying) {
7340
+ _sound.play();
7341
+ }
7363
7342
  }
7364
7343
  }, [buffer, camera, distance, loop]);
7365
7344
  React.useEffect(() => {
@@ -7428,7 +7407,6 @@ const tempMatrix = /* @__PURE__ */ new THREE.Matrix4();
7428
7407
  const translation = /* @__PURE__ */ new THREE.Vector3();
7429
7408
  const rotation = /* @__PURE__ */ new THREE.Quaternion();
7430
7409
  const scale = /* @__PURE__ */ new THREE.Vector3();
7431
- const isInstancedBufferAttribute = (attr) => attr.isInstancedBufferAttribute;
7432
7410
  const Instance = /* @__PURE__ */ React.forwardRef(({ context, children, ...props }, ref) => {
7433
7411
  React.useMemo(() => extend({ PositionMesh }), []);
7434
7412
  const group = React.useRef(null);
@@ -7448,24 +7426,20 @@ const Instances = /* @__PURE__ */ React.forwardRef(({ context, children, range,
7448
7426
  const parentRef = React.useRef(null);
7449
7427
  React.useImperativeHandle(ref, () => parentRef.current, []);
7450
7428
  const [instances, setInstances] = React.useState([]);
7429
+ const boundsDirty = React.useRef(true);
7451
7430
  const [[matrices, colors]] = React.useState(() => {
7452
7431
  const mArray = new Float32Array(limit * 16);
7453
7432
  for (let i = 0; i < limit; i++) tempMatrix.identity().toArray(mArray, i * 16);
7454
7433
  return [mArray, new Float32Array([...new Array(limit * 3)].map(() => 1))];
7455
7434
  });
7456
7435
  React.useEffect(() => {
7457
- parentRef.current.instanceMatrix.needsUpdate = true;
7436
+ if (parentRef.current) parentRef.current.instanceMatrix.needsUpdate = true;
7458
7437
  });
7459
7438
  let iterations = 0;
7460
7439
  let count = 0;
7461
- const attributes = React.useRef([]);
7462
- React.useLayoutEffect(() => {
7463
- attributes.current = Object.entries(parentRef.current.geometry.attributes).filter(
7464
- ([_name, value]) => isInstancedBufferAttribute(value)
7465
- );
7466
- });
7467
7440
  useFrame(() => {
7468
7441
  if (frames === Infinity || iterations < frames) {
7442
+ if (!parentRef.current) return;
7469
7443
  parentRef.current.updateMatrix();
7470
7444
  parentRef.current.updateMatrixWorld();
7471
7445
  parentMatrix.copy(parentRef.current.matrixWorld).invert();
@@ -7486,13 +7460,22 @@ const Instances = /* @__PURE__ */ React.forwardRef(({ context, children, range,
7486
7460
  }
7487
7461
  iterations++;
7488
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
+ }
7489
7468
  });
7490
7469
  const api = React.useMemo(
7491
7470
  () => ({
7492
7471
  getParent: () => parentRef,
7493
7472
  subscribe: (ref2) => {
7494
7473
  setInstances((instances2) => [...instances2, ref2]);
7495
- 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
+ };
7496
7479
  }
7497
7480
  }),
7498
7481
  []
@@ -7517,10 +7500,23 @@ const Merged = /* @__PURE__ */ React.forwardRef(function Merged2({ meshes, child
7517
7500
  if (!isArray) {
7518
7501
  for (const key of Object.keys(meshes)) if (!meshes[key].isMesh) delete meshes[key];
7519
7502
  }
7520
- const render = (args) => isArray ? children(...args) : children(
7521
- Object.keys(meshes).filter((key) => meshes[key].isMesh).reduce((acc, key, i) => ({ ...acc, [key]: args[i] }), {})
7522
- );
7523
- 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
+ });
7524
7520
  return /* @__PURE__ */ React.createElement("group", { ref }, renderRecursive(render, components));
7525
7521
  });
7526
7522
  function renderRecursive(render, components, results = []) {
@@ -7546,7 +7542,7 @@ function createInstances() {
7546
7542
  ];
7547
7543
  }
7548
7544
  const InstancedAttribute = React.forwardRef(
7549
- ({ name, defaultValue, normalized, usage = THREE.DynamicDrawUsage }, fref) => {
7545
+ ({ name, defaultValue, normalized, usage = THREE.DynamicDrawUsage, type = "float" }, fref) => {
7550
7546
  const ref = React.useRef(null);
7551
7547
  React.useImperativeHandle(fref, () => ref.current, []);
7552
7548
  React.useLayoutEffect(() => {
@@ -7554,7 +7550,26 @@ const InstancedAttribute = React.forwardRef(
7554
7550
  parent.geometry.attributes[name] = ref.current;
7555
7551
  const value = Array.isArray(defaultValue) ? defaultValue : [defaultValue];
7556
7552
  const array = Array.from({ length: parent.userData.limit }, () => value).flat();
7557
- ref.current.array = new Float32Array(array);
7553
+ switch (type) {
7554
+ case "float":
7555
+ ref.current.array = new Float32Array(array);
7556
+ break;
7557
+ case "int":
7558
+ ref.current.array = new Int32Array(array);
7559
+ break;
7560
+ case "uint":
7561
+ ref.current.array = new Uint32Array(array);
7562
+ break;
7563
+ case "short":
7564
+ ref.current.array = new Int16Array(array);
7565
+ break;
7566
+ case "ushort":
7567
+ ref.current.array = new Uint16Array(array);
7568
+ break;
7569
+ case "byte":
7570
+ ref.current.array = new Uint8Array(array);
7571
+ break;
7572
+ }
7558
7573
  ref.current.itemSize = value.length;
7559
7574
  ref.current.count = array.length / ref.current.itemSize;
7560
7575
  return () => {
@@ -8575,12 +8590,13 @@ function Sampler({ children, weight, transform, instances, mesh, count = 16, ...
8575
8590
  return /* @__PURE__ */ React.createElement("group", { ref: group, ...props }, children);
8576
8591
  }
8577
8592
 
8593
+ const pathStyle = (path) => path.userData?.style;
8578
8594
  const Svg = /* @__PURE__ */ forwardRef(
8579
8595
  function R3FSvg({ src, skipFill, skipStrokes, fillMaterial, strokeMaterial, fillMeshProps, strokeMeshProps, ...props }, ref) {
8580
8596
  const svg = useLoader(SVGLoader, !src.startsWith("<svg") ? src : `data:image/svg+xml;utf8,${src}`);
8581
8597
  const strokeGeometries = useMemo(
8582
8598
  () => skipStrokes ? [] : svg.paths.map(
8583
- (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)))
8584
8600
  ),
8585
8601
  [svg, skipStrokes]
8586
8602
  );
@@ -8588,21 +8604,21 @@ const Svg = /* @__PURE__ */ forwardRef(
8588
8604
  return () => strokeGeometries.forEach((group) => group && group.map((g) => g.dispose()));
8589
8605
  }, [strokeGeometries]);
8590
8606
  let renderOrder = 0;
8591
- 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(
8592
8608
  "meshBasicMaterial",
8593
8609
  {
8594
- color: path.userData.style.fill,
8595
- opacity: path.userData.style.fillOpacity,
8610
+ color: pathStyle(path).fill,
8611
+ opacity: pathStyle(path).fillOpacity,
8596
8612
  transparent: true,
8597
8613
  side: DoubleSide,
8598
8614
  depthWrite: false,
8599
8615
  ...fillMaterial
8600
8616
  }
8601
- ))), !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(
8602
8618
  "meshBasicMaterial",
8603
8619
  {
8604
- color: path.userData.style.stroke,
8605
- opacity: path.userData.style.strokeOpacity,
8620
+ color: pathStyle(path).stroke,
8621
+ opacity: pathStyle(path).strokeOpacity,
8606
8622
  transparent: true,
8607
8623
  side: DoubleSide,
8608
8624
  depthWrite: false,
@@ -8825,8 +8841,8 @@ function useVideoTexture(srcOrSrcObject, {
8825
8841
  });
8826
8842
  if (src && IS_BROWSER && src.endsWith(".m3u8")) {
8827
8843
  const hls = hlsRef.current = await getHls(hlsConfig);
8828
- if (hls) {
8829
- hls.on(Events.MEDIA_ATTACHED, () => void hls.loadSource(src));
8844
+ if (hls && _HLSModule) {
8845
+ hls.on(_HLSModule.Events.MEDIA_ATTACHED, () => void hls.loadSource(src));
8830
8846
  hls.attachMedia(video2);
8831
8847
  }
8832
8848
  }
@@ -9046,9 +9062,125 @@ function Fbx({
9046
9062
  return /* @__PURE__ */ React.createElement(Clone, { ...props, object });
9047
9063
  }
9048
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
+
9049
9169
  let dracoLoader = null;
9050
9170
  let decoderPath = "https://www.gstatic.com/draco/versioned/decoders/1.5.5/";
9051
- 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) {
9052
9184
  return (loader) => {
9053
9185
  if (extendLoader) {
9054
9186
  extendLoader(loader);
@@ -9064,31 +9196,110 @@ function extensions(useDraco = true, useMeshopt = true, extendLoader) {
9064
9196
  const decoder = typeof MeshoptDecoder === "function" ? MeshoptDecoder() : MeshoptDecoder;
9065
9197
  loader.setMeshoptDecoder(decoder);
9066
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
+ }
9067
9209
  };
9068
9210
  }
9069
- const useGLTF = (path, useDraco, useMeshopt, extendLoader) => useLoader(GLTFLoader, path, extensions(useDraco, useMeshopt, extendLoader));
9070
- 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
+ };
9071
9262
  useGLTF.clear = (path) => useLoader.clear(GLTFLoader, path);
9072
9263
  useGLTF.setDecoderPath = (path) => {
9073
9264
  decoderPath = path;
9074
9265
  };
9266
+ useGLTF.setKTX2TranscoderPath = (path) => {
9267
+ KTX2LoaderService.setTranscoderPath(path);
9268
+ };
9075
9269
  const Gltf = /* @__PURE__ */ React.forwardRef(
9076
- ({ src, useDraco, useMeshOpt, extendLoader, ...props }, ref) => {
9077
- 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);
9078
9277
  return /* @__PURE__ */ React.createElement(Clone, { ref, ...props, object: scene });
9079
9278
  }
9080
9279
  );
9081
9280
 
9082
- const cdn = "https://cdn.jsdelivr.net/gh/pmndrs/drei-assets@master";
9083
- function useKTX2(input, basisPath = `${cdn}/basis/`) {
9281
+ function useKTX2(input, basisPath = true, extendLoader) {
9084
9282
  const renderer = useThree((state) => state.renderer);
9283
+ useLayoutEffect(() => {
9284
+ KTX2LoaderService.registerRenderer(renderer);
9285
+ }, [renderer]);
9085
9286
  const textures = useLoader(KTX2Loader, IsObject(input) ? Object.values(input) : input, (loader) => {
9287
+ if (extendLoader) {
9288
+ extendLoader(loader);
9289
+ }
9290
+ if (basisPath) {
9291
+ const path = typeof basisPath === "string" ? basisPath : KTX2LoaderService.getTranscoderPath();
9292
+ loader.setTranscoderPath(path);
9293
+ }
9086
9294
  loader.detectSupport(renderer);
9087
- loader.setTranscoderPath(basisPath);
9088
9295
  });
9089
9296
  useEffect(() => {
9090
9297
  const array = Array.isArray(textures) ? textures : [textures];
9091
- array.forEach((texture) => renderer.initTexture(texture));
9298
+ array.forEach((texture) => {
9299
+ if ("initTexture" in renderer) {
9300
+ renderer.initTexture(texture);
9301
+ }
9302
+ });
9092
9303
  }, [renderer, textures]);
9093
9304
  if (IsObject(input)) {
9094
9305
  const keys = Object.keys(input);
@@ -9099,19 +9310,81 @@ function useKTX2(input, basisPath = `${cdn}/basis/`) {
9099
9310
  return textures;
9100
9311
  }
9101
9312
  }
9102
- useKTX2.preload = (url, basisPath = `${cdn}/basis/`) => useLoader.preload(KTX2Loader, url, (loader) => {
9103
- loader.setTranscoderPath(basisPath);
9104
- });
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
+ };
9105
9330
  useKTX2.clear = (input) => useLoader.clear(KTX2Loader, input);
9106
- const Ktx2 = ({
9107
- children,
9108
- input,
9109
- basisPath
9110
- }) => {
9111
- const texture = useKTX2(input, basisPath);
9331
+ useKTX2.setTranscoderPath = (path) => {
9332
+ KTX2LoaderService.setTranscoderPath(path);
9333
+ };
9334
+ const Ktx2 = ({ children, input, basisPath, extendLoader }) => {
9335
+ const texture = useKTX2(input, basisPath, extendLoader);
9112
9336
  return /* @__PURE__ */ React.createElement(React.Fragment, null, children?.(texture));
9113
9337
  };
9114
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
+
9115
9388
  const WebcamVideoTexture = /* @__PURE__ */ forwardRef(
9116
9389
  ({
9117
9390
  constraints = {
@@ -9299,6 +9572,7 @@ function useEffectfulState(fn, deps = [], cb) {
9299
9572
  }
9300
9573
 
9301
9574
  function Stats({ showPanel = 0, className, parent }) {
9575
+ React.useEffect(() => void console.warn("drei: <Stats /> is deprecated, use <StatsGl /> instead."), []);
9302
9576
  const stats = useEffectfulState(() => new StatsImpl(), []);
9303
9577
  React.useEffect(() => {
9304
9578
  if (stats) {
@@ -9327,6 +9601,7 @@ const StatsGl = /* @__PURE__ */ React.forwardRef(
9327
9601
  const stats2 = new Stats$1({
9328
9602
  ...props
9329
9603
  });
9604
+ stats2.init(renderer);
9330
9605
  return stats2;
9331
9606
  }, [renderer]);
9332
9607
  React.useImperativeHandle(fref, () => stats.domElement, [stats]);
@@ -9344,7 +9619,7 @@ const StatsGl = /* @__PURE__ */ React.forwardRef(
9344
9619
  return () => {
9345
9620
  if (classNames.length) stats.domElement.classList.remove(...classNames);
9346
9621
  node?.removeChild(stats.domElement);
9347
- stats.end();
9622
+ stats.dispose();
9348
9623
  };
9349
9624
  }
9350
9625
  }, [parent, stats, className, id, clearStatsGlStyle]);
@@ -9352,7 +9627,7 @@ const StatsGl = /* @__PURE__ */ React.forwardRef(
9352
9627
  () => {
9353
9628
  stats.begin();
9354
9629
  },
9355
- { before: "render" }
9630
+ { before: "update" }
9356
9631
  );
9357
9632
  useFrame(
9358
9633
  () => {
@@ -9402,11 +9677,11 @@ const RenderCubeTexture = /* @__PURE__ */ React.forwardRef(
9402
9677
  depthBuffer,
9403
9678
  generateMipmaps
9404
9679
  };
9405
- const fboA2 = new THREE.WebGLCubeRenderTarget(fboSize, settings);
9680
+ const fboA2 = new WebGLCubeRenderTarget(fboSize, settings);
9406
9681
  fboA2.texture.isRenderTargetTexture = !flip;
9407
9682
  fboA2.texture.flipY = true;
9408
9683
  fboA2.texture.type = THREE.HalfFloatType;
9409
- const fboB2 = new THREE.WebGLCubeRenderTarget(fboSize, settings);
9684
+ const fboB2 = new WebGLCubeRenderTarget(fboSize, settings);
9410
9685
  fboB2.texture.isRenderTargetTexture = !flip;
9411
9686
  fboB2.texture.flipY = true;
9412
9687
  fboB2.texture.type = THREE.HalfFloatType;
@@ -9584,18 +9859,21 @@ function Fisheye({
9584
9859
  const normal = new THREE.Vector3();
9585
9860
  const sph = new THREE.Sphere(new THREE.Vector3(), radius);
9586
9861
  const normalMatrix = new THREE.Matrix3();
9587
- const compute = React.useCallback((event, state, prev) => {
9588
- state.pointer.set(event.offsetX / state.size.width * 2 - 1, -(event.offsetY / state.size.height) * 2 + 1);
9589
- state.raycaster.setFromCamera(state.pointer, orthoC);
9590
- if (!state.raycaster.ray.intersectSphere(sph, normal)) return;
9591
- else normal.normalize();
9592
- normalMatrix.getNormalMatrix(cubeApi.current.camera.matrixWorld);
9593
- cubeApi.current.camera.getWorldPosition(state.raycaster.ray.origin);
9594
- state.raycaster.ray.direction.set(0, 0, 1).reflect(normal);
9595
- state.raycaster.ray.direction.x *= -1;
9596
- state.raycaster.ray.direction.applyNormalMatrix(normalMatrix).multiplyScalar(-1);
9597
- return void 0;
9598
- }, []);
9862
+ const compute = React.useCallback(
9863
+ (event, state, prev) => {
9864
+ state.pointer.set(event.offsetX / state.size.width * 2 - 1, -(event.offsetY / state.size.height) * 2 + 1);
9865
+ state.raycaster.setFromCamera(state.pointer, orthoC);
9866
+ if (!state.raycaster.ray.intersectSphere(sph, normal)) return;
9867
+ else normal.normalize();
9868
+ normalMatrix.getNormalMatrix(cubeApi.current.camera.matrixWorld);
9869
+ cubeApi.current.camera.getWorldPosition(state.raycaster.ray.origin);
9870
+ state.raycaster.ray.direction.set(0, 0, 1).reflect(normal);
9871
+ state.raycaster.ray.direction.x *= -1;
9872
+ state.raycaster.ray.direction.applyNormalMatrix(normalMatrix).multiplyScalar(-1);
9873
+ return void 0;
9874
+ },
9875
+ [width, height]
9876
+ );
9599
9877
  useFrame(
9600
9878
  ({ gl }) => {
9601
9879
  if (renderPriority) gl.render(sphere.current, orthoC);
@@ -9730,14 +10008,23 @@ const isWebGPURenderer = (def) => !!def.isWebGPURenderer;
9730
10008
  const col = /* @__PURE__ */ new THREE.Color();
9731
10009
  const TUNNEL_KEY = "__drei_view_tunnel__";
9732
10010
  const tracked = globalThis[TUNNEL_KEY] || (globalThis[TUNNEL_KEY] = tunnel());
9733
- function computeContainerPosition(canvasSize, trackRect) {
10011
+ function computeContainerPosition(canvasSize, trackRect, forceEven) {
9734
10012
  const { right, top: trackTop, left: trackLeft, bottom: trackBottom, width, height } = trackRect;
9735
- const isOffscreen = trackRect.bottom < 0 || trackTop > canvasSize.height || right < 0 || trackRect.left > canvasSize.width;
9736
10013
  const canvasBottom = canvasSize.top + canvasSize.height;
9737
- const bottom = canvasBottom - trackBottom;
9738
- const left = trackLeft - canvasSize.left;
9739
- const top = trackTop - canvasSize.top;
9740
- return { position: { width, height, left, top, bottom, right }, isOffscreen };
10014
+ const canvasRight = canvasSize.left + canvasSize.width;
10015
+ const isOffscreen = trackBottom < canvasSize.top || trackTop > canvasBottom || right < canvasSize.left || trackLeft > canvasRight;
10016
+ const rawBottom = canvasBottom - trackBottom;
10017
+ const rawLeft = trackLeft - canvasSize.left;
10018
+ const rawTop = trackTop - canvasSize.top;
10019
+ const position = forceEven ? {
10020
+ width: roundEven(width),
10021
+ height: roundEven(height),
10022
+ left: roundEven(rawLeft),
10023
+ top: roundEven(rawTop),
10024
+ bottom: roundEven(rawBottom),
10025
+ right: roundEven(right)
10026
+ } : { width, height, left: rawLeft, top: rawTop, bottom: rawBottom, right };
10027
+ return { position, isOffscreen };
9741
10028
  }
9742
10029
  function prepareSkissor(renderer, camera, {
9743
10030
  left,
@@ -9777,6 +10064,7 @@ function clear(renderer) {
9777
10064
  }
9778
10065
  function Container({ visible = true, canvasSize, scene, index, children, frames, rect, track }) {
9779
10066
  const { scene: portalScene, camera: portalCamera, renderer, get, setEvents } = useThree();
10067
+ const forceEven = useThree((state) => state.internal.forceEven);
9780
10068
  const [isOffscreen, setOffscreen] = React.useState(false);
9781
10069
  const mountedRef = React.useRef(true);
9782
10070
  React.useEffect(() => {
@@ -9794,7 +10082,7 @@ function Container({ visible = true, canvasSize, scene, index, children, frames,
9794
10082
  frameCount++;
9795
10083
  }
9796
10084
  if (rect.current) {
9797
- const { position, isOffscreen: _isOffscreen } = computeContainerPosition(canvasSize, rect.current);
10085
+ const { position, isOffscreen: _isOffscreen } = computeContainerPosition(canvasSize, rect.current, forceEven);
9798
10086
  if (isOffscreen !== _isOffscreen) setOffscreen(_isOffscreen);
9799
10087
  if (visible && !_isOffscreen && rect.current) {
9800
10088
  const targetScene = children ? portalScene : scene;
@@ -9816,14 +10104,14 @@ function Container({ visible = true, canvasSize, scene, index, children, frames,
9816
10104
  React.useLayoutEffect(() => {
9817
10105
  const curRect = rect.current;
9818
10106
  if (curRect && (!visible || !isOffscreen)) {
9819
- const { position, isOffscreen: _isOffscreen } = computeContainerPosition(canvasSize, curRect);
10107
+ const { position, isOffscreen: _isOffscreen } = computeContainerPosition(canvasSize, curRect, forceEven);
9820
10108
  if (!_isOffscreen) {
9821
10109
  const autoClear = prepareSkissor(renderer, portalCamera, position);
9822
10110
  clear(renderer);
9823
10111
  finishSkissor(renderer, autoClear);
9824
10112
  }
9825
10113
  }
9826
- }, [visible, isOffscreen, renderer, portalCamera, canvasSize]);
10114
+ }, [visible, isOffscreen, renderer, portalCamera, canvasSize, forceEven]);
9827
10115
  React.useEffect(() => {
9828
10116
  if (!track) return;
9829
10117
  const curRect = rect.current;
@@ -9831,7 +10119,7 @@ function Container({ visible = true, canvasSize, scene, index, children, frames,
9831
10119
  setEvents({ connected: track.current });
9832
10120
  return () => {
9833
10121
  if (curRect) {
9834
- const { position, isOffscreen: _isOffscreen } = computeContainerPosition(canvasSize, curRect);
10122
+ const { position, isOffscreen: _isOffscreen } = computeContainerPosition(canvasSize, curRect, forceEven);
9835
10123
  if (!_isOffscreen) {
9836
10124
  const autoClear = prepareSkissor(renderer, portalCamera, position);
9837
10125
  clear(renderer);
@@ -9840,13 +10128,14 @@ function Container({ visible = true, canvasSize, scene, index, children, frames,
9840
10128
  }
9841
10129
  setEvents({ connected: old });
9842
10130
  };
9843
- }, [track, renderer, portalCamera, canvasSize, get, setEvents]);
10131
+ }, [track, renderer, portalCamera, canvasSize, get, setEvents, forceEven]);
9844
10132
  return /* @__PURE__ */ React.createElement(React.Fragment, null, children, /* @__PURE__ */ React.createElement("group", { onPointerOver: () => null }));
9845
10133
  }
9846
10134
  const CanvasView = /* @__PURE__ */ React.forwardRef(
9847
10135
  ({ track, visible = true, index = 1, id, style, className, frames = Infinity, children, ...props }, fref) => {
9848
10136
  const rect = React.useRef(null);
9849
10137
  const { size, scene } = useThree();
10138
+ const forceEven = useThree((state) => state.internal.forceEven);
9850
10139
  const [virtualScene] = React.useState(() => new THREE.Scene());
9851
10140
  const [ready, toggle] = React.useReducer(() => true, false);
9852
10141
  const compute = React.useCallback(
@@ -9883,12 +10172,12 @@ const CanvasView = /* @__PURE__ */ React.forwardRef(
9883
10172
  {
9884
10173
  events: { compute, priority: index },
9885
10174
  size: {
9886
- width: rect.current?.width,
9887
- height: rect.current?.height,
10175
+ width: forceEven && rect.current?.width ? roundEven(rect.current.width) : rect.current?.width,
10176
+ height: forceEven && rect.current?.height ? roundEven(rect.current.height) : rect.current?.height,
9888
10177
  // @ts-ignore
9889
- top: rect.current?.top,
10178
+ top: forceEven && rect.current?.top ? roundEven(rect.current.top) : rect.current?.top,
9890
10179
  // @ts-ignore
9891
- left: rect.current?.left
10180
+ left: forceEven && rect.current?.left ? roundEven(rect.current.left) : rect.current?.left
9892
10181
  }
9893
10182
  }
9894
10183
  ));
@@ -10283,12 +10572,12 @@ useEnvironment.preload = (preloadOptions) => {
10283
10572
  if (extensions) extensions(loader2);
10284
10573
  });
10285
10574
  };
10286
- const clearDefaultOptins = {
10575
+ const clearDefaultOptions = {
10287
10576
  files: defaultFiles,
10288
10577
  preset: void 0
10289
10578
  };
10290
10579
  useEnvironment.clear = (clearOptions) => {
10291
- const options = { ...clearDefaultOptins, ...clearOptions };
10580
+ const options = { ...clearDefaultOptions, ...clearOptions };
10292
10581
  let { files } = options;
10293
10582
  const { preset } = options;
10294
10583
  if (preset) {
@@ -10315,8 +10604,8 @@ function getLoader(extension) {
10315
10604
  return loader;
10316
10605
  }
10317
10606
 
10318
- const isRef = (obj) => obj.current && obj.current.isScene;
10319
- const resolveScene = (scene) => isRef(scene) ? scene.current : scene;
10607
+ const isRef$1 = (obj) => obj.current && obj.current.isScene;
10608
+ const resolveScene = (scene) => isRef$1(scene) ? scene.current : scene;
10320
10609
  function setEnvProps(background, scene, defaultScene, texture, sceneProps = {}) {
10321
10610
  sceneProps = {
10322
10611
  backgroundBlurriness: 0,
@@ -10982,9 +11271,14 @@ function Select({
10982
11271
  const oldControlsEnabled = controls?.enabled;
10983
11272
  let isDown = false;
10984
11273
  function prepareRay(event, vec) {
10985
- 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;
10986
11278
  const { width, height } = size;
10987
- 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);
10988
11282
  }
10989
11283
  function onSelectStart(event) {
10990
11284
  if (controls) controls.enabled = false;
@@ -11102,83 +11396,454 @@ function AsciiRenderer({
11102
11396
  return /* @__PURE__ */ React.createElement(React.Fragment, null);
11103
11397
  }
11104
11398
 
11105
- function BakeShadows() {
11106
- const gl = useThree((state) => state.gl);
11107
- useEffect(() => {
11108
- gl.shadowMap.autoUpdate = false;
11109
- gl.shadowMap.needsUpdate = true;
11110
- return () => {
11111
- gl.shadowMap.autoUpdate = gl.shadowMap.needsUpdate = true;
11112
- };
11113
- }, [gl.shadowMap]);
11114
- return null;
11115
- }
11116
-
11117
- var GradientType = /* @__PURE__ */ ((GradientType2) => {
11118
- GradientType2["Linear"] = "linear";
11119
- GradientType2["Radial"] = "radial";
11120
- return GradientType2;
11121
- })(GradientType || {});
11122
- function GradientTexture({
11123
- stops,
11124
- colors,
11125
- size = 1024,
11126
- width = 16,
11127
- type = "linear" /* Linear */,
11128
- innerCircleRadius = 0,
11129
- outerCircleRadius = "auto",
11130
- ...props
11131
- }) {
11132
- const gl = useThree((state) => state.gl);
11133
- const canvas = React.useMemo(() => {
11134
- const canvas2 = document.createElement("canvas");
11135
- const context = canvas2.getContext("2d");
11136
- canvas2.width = width;
11137
- canvas2.height = size;
11138
- let gradient;
11139
- if (type === "linear" /* Linear */) {
11140
- gradient = context.createLinearGradient(0, 0, 0, size);
11141
- } else {
11142
- const canvasCenterX = canvas2.width / 2;
11143
- const canvasCenterY = canvas2.height / 2;
11144
- const radius = outerCircleRadius !== "auto" ? Math.abs(Number(outerCircleRadius)) : Math.sqrt(canvasCenterX ** 2 + canvasCenterY ** 2);
11145
- gradient = context.createRadialGradient(
11146
- canvasCenterX,
11147
- canvasCenterY,
11148
- Math.abs(innerCircleRadius),
11149
- canvasCenterX,
11150
- canvasCenterY,
11151
- radius
11152
- );
11153
- }
11154
- const tempColor = new THREE.Color();
11155
- let i = stops.length;
11156
- while (i--) {
11157
- gradient.addColorStop(stops[i], tempColor.set(colors[i]).getStyle());
11158
- }
11159
- context.save();
11160
- context.fillStyle = gradient;
11161
- context.fillRect(0, 0, width, size);
11162
- context.restore();
11163
- return canvas2;
11164
- }, [stops]);
11165
- return /* @__PURE__ */ React.createElement("canvasTexture", { colorSpace: gl.outputColorSpace, args: [canvas], attach: "map", ...props });
11166
- }
11167
-
11168
- const ImageMaterialImpl = /* @__PURE__ */ shaderMaterial(
11169
- {
11170
- color: /* @__PURE__ */ new THREE.Color("white"),
11171
- scale: /* @__PURE__ */ new THREE.Vector2(1, 1),
11172
- imageBounds: /* @__PURE__ */ new THREE.Vector2(1, 1),
11173
- resolution: 1024,
11174
- map: null,
11175
- zoom: 1,
11176
- radius: 0,
11177
- grayscale: 0,
11178
- opacity: 1
11399
+ const WireframeMaterialShaders = {
11400
+ uniforms: {
11401
+ strokeOpacity: 1,
11402
+ fillOpacity: 0.25,
11403
+ fillMix: 0,
11404
+ thickness: 0.05,
11405
+ colorBackfaces: false,
11406
+ dashInvert: true,
11407
+ dash: false,
11408
+ dashRepeats: 4,
11409
+ dashLength: 0.5,
11410
+ squeeze: false,
11411
+ squeezeMin: 0.2,
11412
+ squeezeMax: 1,
11413
+ stroke: /* @__PURE__ */ new THREE.Color("#ff0000"),
11414
+ backfaceStroke: /* @__PURE__ */ new THREE.Color("#0000ff"),
11415
+ fill: /* @__PURE__ */ new THREE.Color("#00ff00")
11179
11416
  },
11180
- /* glsl */
11181
- `
11417
+ vertex: (
11418
+ /* glsl */
11419
+ `
11420
+ attribute vec3 barycentric;
11421
+
11422
+ varying vec3 v_edges_Barycentric;
11423
+ varying vec3 v_edges_Position;
11424
+
11425
+ void initWireframe() {
11426
+ v_edges_Barycentric = barycentric;
11427
+ v_edges_Position = position.xyz;
11428
+ }
11429
+ `
11430
+ ),
11431
+ fragment: (
11432
+ /* glsl */
11433
+ `
11434
+ #ifndef PI
11435
+ #define PI 3.1415926535897932384626433832795
11436
+ #endif
11437
+
11438
+ varying vec3 v_edges_Barycentric;
11439
+ varying vec3 v_edges_Position;
11440
+
11441
+ uniform float strokeOpacity;
11442
+ uniform float fillOpacity;
11443
+ uniform float fillMix;
11444
+ uniform float thickness;
11445
+ uniform bool colorBackfaces;
11446
+
11447
+ // Dash
11448
+ uniform bool dashInvert;
11449
+ uniform bool dash;
11450
+ uniform bool dashOnly;
11451
+ uniform float dashRepeats;
11452
+ uniform float dashLength;
11453
+
11454
+ // Squeeze
11455
+ uniform bool squeeze;
11456
+ uniform float squeezeMin;
11457
+ uniform float squeezeMax;
11458
+
11459
+ // Colors
11460
+ uniform vec3 stroke;
11461
+ uniform vec3 backfaceStroke;
11462
+ uniform vec3 fill;
11463
+
11464
+ // This is like
11465
+ float wireframe_aastep(float threshold, float dist) {
11466
+ float afwidth = fwidth(dist) * 0.5;
11467
+ return smoothstep(threshold - afwidth, threshold + afwidth, dist);
11468
+ }
11469
+
11470
+ float wireframe_map(float value, float min1, float max1, float min2, float max2) {
11471
+ return min2 + (value - min1) * (max2 - min2) / (max1 - min1);
11472
+ }
11473
+
11474
+ float getWireframe() {
11475
+ vec3 barycentric = v_edges_Barycentric;
11476
+
11477
+ // Distance from center of each triangle to its edges.
11478
+ float d = min(min(barycentric.x, barycentric.y), barycentric.z);
11479
+
11480
+ // for dashed rendering, we can use this to get the 0 .. 1 value of the line length
11481
+ float positionAlong = max(barycentric.x, barycentric.y);
11482
+ if (barycentric.y < barycentric.x && barycentric.y < barycentric.z) {
11483
+ positionAlong = 1.0 - positionAlong;
11484
+ }
11485
+
11486
+ // the thickness of the stroke
11487
+ float computedThickness = wireframe_map(thickness, 0.0, 1.0, 0.0, 0.34);
11488
+
11489
+ // if we want to shrink the thickness toward the center of the line segment
11490
+ if (squeeze) {
11491
+ computedThickness *= mix(squeezeMin, squeezeMax, (1.0 - sin(positionAlong * PI)));
11492
+ }
11493
+
11494
+ // Create dash pattern
11495
+ if (dash) {
11496
+ // here we offset the stroke position depending on whether it
11497
+ // should overlap or not
11498
+ float offset = 1.0 / dashRepeats * dashLength / 2.0;
11499
+ if (!dashInvert) {
11500
+ offset += 1.0 / dashRepeats / 2.0;
11501
+ }
11502
+
11503
+ // if we should animate the dash or not
11504
+ // if (dashAnimate) {
11505
+ // offset += time * 0.22;
11506
+ // }
11507
+
11508
+ // create the repeating dash pattern
11509
+ float pattern = fract((positionAlong + offset) * dashRepeats);
11510
+ computedThickness *= 1.0 - wireframe_aastep(dashLength, pattern);
11511
+ }
11512
+
11513
+ // compute the anti-aliased stroke edge
11514
+ float edge = 1.0 - wireframe_aastep(computedThickness, d);
11515
+
11516
+ return edge;
11517
+ }
11518
+ `
11519
+ )
11520
+ };
11521
+ const WireframeMaterial = /* @__PURE__ */ shaderMaterial(
11522
+ WireframeMaterialShaders.uniforms,
11523
+ WireframeMaterialShaders.vertex + /* glsl */
11524
+ `
11525
+ void main() {
11526
+ initWireframe();
11527
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
11528
+ }
11529
+ `,
11530
+ WireframeMaterialShaders.fragment + /* glsl */
11531
+ `
11532
+ void main () {
11533
+ // Compute color
11534
+
11535
+ float edge = getWireframe();
11536
+ vec4 colorStroke = vec4(stroke, edge);
11537
+
11538
+ #ifdef FLIP_SIDED
11539
+ colorStroke.rgb = backfaceStroke;
11540
+ #endif
11541
+
11542
+ vec4 colorFill = vec4(fill, fillOpacity);
11543
+ vec4 outColor = mix(colorFill, colorStroke, edge * strokeOpacity);
11544
+
11545
+ gl_FragColor = outColor;
11546
+ }
11547
+ `
11548
+ );
11549
+ function setWireframeOverride(material, uniforms) {
11550
+ material.onBeforeCompile = (shader) => {
11551
+ shader.uniforms = {
11552
+ ...shader.uniforms,
11553
+ ...uniforms
11554
+ };
11555
+ shader.vertexShader = shader.vertexShader.replace(
11556
+ "void main() {",
11557
+ `
11558
+ ${WireframeMaterialShaders.vertex}
11559
+ void main() {
11560
+ initWireframe();
11561
+ `
11562
+ );
11563
+ shader.fragmentShader = shader.fragmentShader.replace(
11564
+ "void main() {",
11565
+ `
11566
+ ${WireframeMaterialShaders.fragment}
11567
+ void main() {
11568
+ `
11569
+ );
11570
+ shader.fragmentShader = shader.fragmentShader.replace(
11571
+ "#include <color_fragment>",
11572
+ /* glsl */
11573
+ `
11574
+ #include <color_fragment>
11575
+ float edge = getWireframe();
11576
+ vec4 colorStroke = vec4(stroke, edge);
11577
+ #ifdef FLIP_SIDED
11578
+ colorStroke.rgb = backfaceStroke;
11579
+ #endif
11580
+ vec4 colorFill = vec4(mix(diffuseColor.rgb, fill, fillMix), mix(diffuseColor.a, fillOpacity, fillMix));
11581
+ vec4 outColor = mix(colorFill, colorStroke, edge * strokeOpacity);
11582
+
11583
+ diffuseColor.rgb = outColor.rgb;
11584
+ diffuseColor.a *= outColor.a;
11585
+ `
11586
+ );
11587
+ };
11588
+ material.side = THREE.DoubleSide;
11589
+ material.transparent = true;
11590
+ }
11591
+ function useWireframeUniforms(uniforms, props) {
11592
+ React.useEffect(
11593
+ () => void (uniforms.fillOpacity.value = props.fillOpacity ?? uniforms.fillOpacity.value),
11594
+ [props.fillOpacity]
11595
+ );
11596
+ React.useEffect(() => void (uniforms.fillMix.value = props.fillMix ?? uniforms.fillMix.value), [props.fillMix]);
11597
+ React.useEffect(
11598
+ () => void (uniforms.strokeOpacity.value = props.strokeOpacity ?? uniforms.strokeOpacity.value),
11599
+ [props.strokeOpacity]
11600
+ );
11601
+ React.useEffect(
11602
+ () => void (uniforms.thickness.value = props.thickness ?? uniforms.thickness.value),
11603
+ [props.thickness]
11604
+ );
11605
+ React.useEffect(() => void (uniforms.colorBackfaces.value = !!props.colorBackfaces), [props.colorBackfaces]);
11606
+ React.useEffect(() => void (uniforms.dash.value = !!props.dash), [props.dash]);
11607
+ React.useEffect(() => void (uniforms.dashInvert.value = !!props.dashInvert), [props.dashInvert]);
11608
+ React.useEffect(
11609
+ () => void (uniforms.dashRepeats.value = props.dashRepeats ?? uniforms.dashRepeats.value),
11610
+ [props.dashRepeats]
11611
+ );
11612
+ React.useEffect(
11613
+ () => void (uniforms.dashLength.value = props.dashLength ?? uniforms.dashLength.value),
11614
+ [props.dashLength]
11615
+ );
11616
+ React.useEffect(() => void (uniforms.squeeze.value = !!props.squeeze), [props.squeeze]);
11617
+ React.useEffect(
11618
+ () => void (uniforms.squeezeMin.value = props.squeezeMin ?? uniforms.squeezeMin.value),
11619
+ [props.squeezeMin]
11620
+ );
11621
+ React.useEffect(
11622
+ () => void (uniforms.squeezeMax.value = props.squeezeMax ?? uniforms.squeezeMax.value),
11623
+ [props.squeezeMax]
11624
+ );
11625
+ React.useEffect(
11626
+ () => void (uniforms.stroke.value = props.stroke ? new THREE.Color(props.stroke) : uniforms.stroke.value),
11627
+ [props.stroke]
11628
+ );
11629
+ React.useEffect(
11630
+ () => void (uniforms.fill.value = props.fill ? new THREE.Color(props.fill) : uniforms.fill.value),
11631
+ [props.fill]
11632
+ );
11633
+ React.useEffect(
11634
+ () => void (uniforms.backfaceStroke.value = props.backfaceStroke ? new THREE.Color(props.backfaceStroke) : uniforms.backfaceStroke.value),
11635
+ [props.backfaceStroke]
11636
+ );
11637
+ }
11638
+
11639
+ function isWithGeometry(object) {
11640
+ return !!object?.geometry;
11641
+ }
11642
+ function isGeometry(object) {
11643
+ return !!object?.isBufferGeometry;
11644
+ }
11645
+ function isRefObject(object) {
11646
+ return !!object?.current;
11647
+ }
11648
+ function isRef(object) {
11649
+ return object?.current !== void 0;
11650
+ }
11651
+ function isWireframeGeometry(geometry) {
11652
+ return geometry.type === "WireframeGeometry";
11653
+ }
11654
+ function getUniforms() {
11655
+ const u = {};
11656
+ for (const key in WireframeMaterialShaders.uniforms) {
11657
+ u[key] = { value: WireframeMaterialShaders.uniforms[key] };
11658
+ }
11659
+ return u;
11660
+ }
11661
+ function getBarycentricCoordinates(geometry, removeEdge) {
11662
+ const position = geometry.getAttribute("position");
11663
+ const count = position.count;
11664
+ const barycentric = [];
11665
+ for (let i = 0; i < count; i++) {
11666
+ const even = i % 2 === 0;
11667
+ const Q = removeEdge ? 1 : 0;
11668
+ if (even) {
11669
+ barycentric.push(0, 0, 1, 0, 1, 0, 1, 0, Q);
11670
+ } else {
11671
+ barycentric.push(0, 1, 0, 0, 0, 1, 1, 0, Q);
11672
+ }
11673
+ }
11674
+ return new THREE.BufferAttribute(Float32Array.from(barycentric), 3);
11675
+ }
11676
+ function getInputGeometry(inputGeometry) {
11677
+ const geo = isRefObject(inputGeometry) ? inputGeometry.current : inputGeometry;
11678
+ if (!isGeometry(geo)) {
11679
+ if (isWireframeGeometry(geo)) {
11680
+ throw new Error("Wireframe: WireframeGeometry is not supported.");
11681
+ }
11682
+ const parent = geo.parent;
11683
+ if (isWithGeometry(parent)) {
11684
+ if (isWireframeGeometry(parent.geometry)) {
11685
+ throw new Error("Wireframe: WireframeGeometry is not supported.");
11686
+ }
11687
+ return parent.geometry;
11688
+ }
11689
+ } else {
11690
+ return geo;
11691
+ }
11692
+ }
11693
+ function setBarycentricCoordinates(geometry, simplify) {
11694
+ if (geometry.index) {
11695
+ console.warn("Wireframe: Requires non-indexed geometry, converting to non-indexed geometry.");
11696
+ const nonIndexedGeo = geometry.toNonIndexed();
11697
+ geometry.copy(nonIndexedGeo);
11698
+ geometry.setIndex(null);
11699
+ }
11700
+ const newBarycentric = getBarycentricCoordinates(geometry, simplify);
11701
+ geometry.setAttribute("barycentric", newBarycentric);
11702
+ }
11703
+ function WireframeWithCustomGeo({
11704
+ geometry: customGeometry,
11705
+ simplify = false,
11706
+ ...props
11707
+ }) {
11708
+ extend({ MeshWireframeMaterial: WireframeMaterial });
11709
+ const [geometry, setGeometry] = React.useState(null);
11710
+ React.useLayoutEffect(() => {
11711
+ const geom = getInputGeometry(customGeometry);
11712
+ if (!geom) {
11713
+ throw new Error("Wireframe: geometry prop must be a BufferGeometry or a ref to a BufferGeometry.");
11714
+ }
11715
+ setBarycentricCoordinates(geom, simplify);
11716
+ if (isRef(customGeometry)) {
11717
+ setGeometry(geom);
11718
+ }
11719
+ }, [simplify, customGeometry]);
11720
+ const drawnGeo = isRef(customGeometry) ? geometry : customGeometry;
11721
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, drawnGeo && /* @__PURE__ */ React.createElement("mesh", { geometry: drawnGeo }, /* @__PURE__ */ React.createElement(
11722
+ "meshWireframeMaterial",
11723
+ {
11724
+ attach: "material",
11725
+ transparent: true,
11726
+ side: THREE.DoubleSide,
11727
+ polygonOffset: true,
11728
+ polygonOffsetFactor: -4,
11729
+ ...props
11730
+ }
11731
+ )));
11732
+ }
11733
+ function WireframeWithoutCustomGeo({
11734
+ simplify = false,
11735
+ ...props
11736
+ }) {
11737
+ const objectRef = React.useRef(null);
11738
+ const uniforms = React.useMemo(() => getUniforms(), [WireframeMaterialShaders.uniforms]);
11739
+ useWireframeUniforms(uniforms, props);
11740
+ React.useLayoutEffect(() => {
11741
+ const geom = getInputGeometry(objectRef);
11742
+ if (!geom) {
11743
+ throw new Error("Wireframe: Must be a child of a Mesh, Line or Points object or specify a geometry prop.");
11744
+ }
11745
+ const og = geom.clone();
11746
+ setBarycentricCoordinates(geom, simplify);
11747
+ return () => {
11748
+ geom.copy(og);
11749
+ og.dispose();
11750
+ };
11751
+ }, [simplify]);
11752
+ React.useLayoutEffect(() => {
11753
+ const parentMesh = objectRef.current.parent;
11754
+ const og = parentMesh.material.clone();
11755
+ setWireframeOverride(parentMesh.material, uniforms);
11756
+ return () => {
11757
+ parentMesh.material.dispose();
11758
+ parentMesh.material = og;
11759
+ };
11760
+ }, []);
11761
+ return /* @__PURE__ */ React.createElement("object3D", { ref: objectRef });
11762
+ }
11763
+ function Wireframe({ geometry: customGeometry, ...props }) {
11764
+ if (customGeometry) {
11765
+ return /* @__PURE__ */ React.createElement(WireframeWithCustomGeo, { geometry: customGeometry, ...props });
11766
+ }
11767
+ return /* @__PURE__ */ React.createElement(WireframeWithoutCustomGeo, { ...props });
11768
+ }
11769
+
11770
+ function BakeShadows() {
11771
+ const gl = useThree((state) => state.gl);
11772
+ useEffect(() => {
11773
+ gl.shadowMap.autoUpdate = false;
11774
+ gl.shadowMap.needsUpdate = true;
11775
+ return () => {
11776
+ gl.shadowMap.autoUpdate = gl.shadowMap.needsUpdate = true;
11777
+ };
11778
+ }, [gl.shadowMap]);
11779
+ return null;
11780
+ }
11781
+
11782
+ var GradientType = /* @__PURE__ */ ((GradientType2) => {
11783
+ GradientType2["Linear"] = "linear";
11784
+ GradientType2["Radial"] = "radial";
11785
+ return GradientType2;
11786
+ })(GradientType || {});
11787
+ function GradientTexture({
11788
+ stops,
11789
+ colors,
11790
+ size = 1024,
11791
+ width = 16,
11792
+ type = "linear" /* Linear */,
11793
+ innerCircleRadius = 0,
11794
+ outerCircleRadius = "auto",
11795
+ ...props
11796
+ }) {
11797
+ const gl = useThree((state) => state.gl);
11798
+ const canvas = React.useMemo(() => {
11799
+ const canvas2 = document.createElement("canvas");
11800
+ const context = canvas2.getContext("2d");
11801
+ canvas2.width = width;
11802
+ canvas2.height = size;
11803
+ let gradient;
11804
+ if (type === "linear" /* Linear */) {
11805
+ gradient = context.createLinearGradient(0, 0, 0, size);
11806
+ } else {
11807
+ const canvasCenterX = canvas2.width / 2;
11808
+ const canvasCenterY = canvas2.height / 2;
11809
+ const radius = outerCircleRadius !== "auto" ? Math.abs(Number(outerCircleRadius)) : Math.sqrt(canvasCenterX ** 2 + canvasCenterY ** 2);
11810
+ gradient = context.createRadialGradient(
11811
+ canvasCenterX,
11812
+ canvasCenterY,
11813
+ Math.abs(innerCircleRadius),
11814
+ canvasCenterX,
11815
+ canvasCenterY,
11816
+ radius
11817
+ );
11818
+ }
11819
+ const tempColor = new THREE.Color();
11820
+ let i = stops.length;
11821
+ while (i--) {
11822
+ gradient.addColorStop(stops[i], tempColor.set(colors[i]).getStyle());
11823
+ }
11824
+ context.save();
11825
+ context.fillStyle = gradient;
11826
+ context.fillRect(0, 0, width, size);
11827
+ context.restore();
11828
+ return canvas2;
11829
+ }, [stops]);
11830
+ return /* @__PURE__ */ React.createElement("canvasTexture", { colorSpace: gl.outputColorSpace, args: [canvas], attach: "map", ...props });
11831
+ }
11832
+
11833
+ const ImageMaterialImpl = /* @__PURE__ */ shaderMaterial(
11834
+ {
11835
+ color: /* @__PURE__ */ new THREE.Color("white"),
11836
+ scale: /* @__PURE__ */ new THREE.Vector2(1, 1),
11837
+ imageBounds: /* @__PURE__ */ new THREE.Vector2(1, 1),
11838
+ resolution: 1024,
11839
+ map: null,
11840
+ zoom: 1,
11841
+ radius: 0,
11842
+ grayscale: 0,
11843
+ opacity: 1
11844
+ },
11845
+ /* glsl */
11846
+ `
11182
11847
  varying vec2 vUv;
11183
11848
  varying vec2 vPos;
11184
11849
  void main() {
@@ -11347,8 +12012,8 @@ vec3 highPassRandRGB(vec2 uv) {
11347
12012
 
11348
12013
 
11349
12014
  vec2 vogelDiskSample(int sampleIndex, int sampleCount, float angle) {
11350
- const float goldenAngle = 2.399963f; // radians
11351
- 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));
11352
12017
  float theta = float(sampleIndex) * goldenAngle + angle;
11353
12018
  float sine = sin(theta);
11354
12019
  float cosine = cos(theta);
@@ -11369,7 +12034,7 @@ float findBlocker(sampler2D shadowMap, vec2 uv, float compare, float angle) {
11369
12034
  #pragma unroll_loop_start
11370
12035
  for(int i = 0; i < ${samples}; i ++) {
11371
12036
  offset = (vogelDiskSample(j, ${samples}, angle) * texelSize) * 2.0 * PENUMBRA_FILTER_SIZE;
11372
- depth = unpackRGBAToDepth( texture2D( shadowMap, uv + offset));
12037
+ depth = texture2D( shadowMap, uv + offset ).r;
11373
12038
  if (depth < compare) {
11374
12039
  blockerDepthSum += depth;
11375
12040
  blockers++;
@@ -11387,7 +12052,7 @@ float findBlocker(sampler2D shadowMap, vec2 uv, float compare, float angle) {
11387
12052
 
11388
12053
  float vogelFilter(sampler2D shadowMap, vec2 uv, float zReceiver, float filterRadius, float angle) {
11389
12054
  float texelSize = 1.0 / float(textureSize(shadowMap, 0).x);
11390
- float shadow = 0.0f;
12055
+ float shadow = 0.0;
11391
12056
  int j = 0;
11392
12057
  vec2 vogelSample = vec2(0.0);
11393
12058
  vec2 offset = vec2(0.0);
@@ -11395,7 +12060,7 @@ float vogelFilter(sampler2D shadowMap, vec2 uv, float zReceiver, float filterRad
11395
12060
  for (int i = 0; i < ${samples}; i++) {
11396
12061
  vogelSample = vogelDiskSample(j, ${samples}, angle) * texelSize;
11397
12062
  offset = vogelSample * (1.0 + filterRadius * float(${size}));
11398
- shadow += step( zReceiver, unpackRGBAToDepth( texture2D( shadowMap, uv + offset ) ) );
12063
+ shadow += step( zReceiver, texture2D( shadowMap, uv + offset ).r );
11399
12064
  j++;
11400
12065
  }
11401
12066
  #pragma unroll_loop_end
@@ -11404,7 +12069,7 @@ float vogelFilter(sampler2D shadowMap, vec2 uv, float zReceiver, float filterRad
11404
12069
 
11405
12070
  float PCSS (sampler2D shadowMap, vec4 coords) {
11406
12071
  vec2 uv = coords.xy;
11407
- float zReceiver = coords.z; // Assumed to be eye-space z in this code
12072
+ float zReceiver = coords.z;
11408
12073
  float angle = highPassRandRGB(gl_FragCoord.xy).r * PI2;
11409
12074
  float avgBlockerDepth = findBlocker(shadowMap, uv, zReceiver, angle);
11410
12075
  if (avgBlockerDepth == -1.0) {
@@ -11428,14 +12093,27 @@ function SoftShadows({ focus = 0, samples = 10, size = 25 }) {
11428
12093
  const scene = useThree((state) => state.scene);
11429
12094
  const camera = useThree((state) => state.camera);
11430
12095
  React.useEffect(() => {
11431
- const original = THREE.ShaderChunk.shadowmap_pars_fragment;
11432
- THREE.ShaderChunk.shadowmap_pars_fragment = THREE.ShaderChunk.shadowmap_pars_fragment.replace("#ifdef USE_SHADOWMAP", "#ifdef USE_SHADOWMAP\n" + pcss({ size, samples, focus })).replace(
11433
- "#if defined( SHADOWMAP_TYPE_PCF )",
11434
- "\nreturn PCSS(shadowMap, shadowCoord);\n#if defined( SHADOWMAP_TYPE_PCF )"
11435
- );
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;
11436
12113
  reset(gl, scene, camera);
11437
12114
  return () => {
11438
- THREE.ShaderChunk.shadowmap_pars_fragment = original;
12115
+ THREE.ShaderChunk.shadowmap_pars_fragment = originalShader;
12116
+ gl.shadowMap.type = originalShadowMapType;
11439
12117
  reset(gl, scene, camera);
11440
12118
  };
11441
12119
  }, [focus, size, samples]);
@@ -11541,6 +12219,60 @@ class SpotLightMaterial extends THREE.ShaderMaterial {
11541
12219
  }
11542
12220
  }
11543
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
+
11544
12276
  class StarfieldMaterial extends ShaderMaterial {
11545
12277
  constructor() {
11546
12278
  super({
@@ -11615,8 +12347,8 @@ class SparklesMaterial extends ShaderMaterial {
11615
12347
  modelPosition.z += cos(time * speed + modelPosition.x * noise.y * 100.0) * 0.2;
11616
12348
  modelPosition.x += cos(time * speed + modelPosition.x * noise.z * 100.0) * 0.2;
11617
12349
  vec4 viewPosition = viewMatrix * modelPosition;
11618
- vec4 projectionPostion = projectionMatrix * viewPosition;
11619
- gl_Position = projectionPostion;
12350
+ vec4 projectionPosition = projectionMatrix * viewPosition;
12351
+ gl_Position = projectionPosition;
11620
12352
  gl_PointSize = size * 25. * pixelRatio;
11621
12353
  gl_PointSize *= (1.0 / - viewPosition.z);
11622
12354
  vColor = color;
@@ -11631,8 +12363,9 @@ class SparklesMaterial extends ShaderMaterial {
11631
12363
  varying float vOpacity;
11632
12364
  void main() {
11633
12365
  float distanceToCenter = distance(gl_PointCoord, vec2(0.5));
11634
- float strength = 0.05 / distanceToCenter - 0.1;
12366
+ float strength = clamp(0.05 / distanceToCenter - 0.1, 0.0, 1.0);
11635
12367
  gl_FragColor = vec4(vColor, strength * vOpacity);
12368
+ // Note: tonemapping may shift hue (e.g. orange \u2192 yellow) compared to the WebGPU version
11636
12369
  #include <tonemapping_fragment>
11637
12370
  #include <${version >= 154 ? "colorspace_fragment" : "encodings_fragment"}>
11638
12371
  }
@@ -12322,4 +13055,4 @@ const Text = /* @__PURE__ */ React.forwardRef(
12322
13055
  }
12323
13056
  );
12324
13057
 
12325
- 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 };