@react-three/drei 9.96.5 → 9.97.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -71,6 +71,7 @@ The `native` route of the library **does not** export `Html` or `Loader`. The de
71
71
  <li><a href="#positionalaudio">PositionalAudio</a></li>
72
72
  <li><a href="#billboard">Billboard</a></li>
73
73
  <li><a href="#screenspace">ScreenSpace</a></li>
74
+ <li><a href="#screensizer">ScreenSizer</a></li>
74
75
  <li><a href="#effects">Effects</a></li>
75
76
  <li><a href="#gradienttexture">GradientTexture</a></li>
76
77
  <li><a href="#edges">Edges</a></li>
@@ -139,6 +140,7 @@ The `native` route of the library **does not** export `Html` or `Loader`. The de
139
140
  <li><a href="#usevideotexture">useVideoTexture</a></li>
140
141
  <li><a href="#usetrailtexture">useTrailTexture</a></li>
141
142
  <li><a href="#usefont">useFont</a></li>
143
+ <li><a href="#usespriteloader">useSpriteLoader</a></li>
142
144
  </ul>
143
145
  <li><a href="#performance">Performance</a></li>
144
146
  <ul>
@@ -214,7 +216,7 @@ The `native` route of the library **does not** export `Html` or `Loader`. The de
214
216
  <li><a href="#environment">Environment</a></li>
215
217
  <li><a href="#lightformer">Lightformer</a></li>
216
218
  <li><a href="#spotlight">SpotLight</a></li>
217
- <li><a href="#spotlightshadows">SpotLightShadows</a></li>
219
+ <li><a href="#spotlightshadow">SpotLightShadow</a></li>
218
220
  <li><a href="#shadow">Shadow</a></li>
219
221
  <li><a href="#caustics">Caustics</a></li>
220
222
  <li><a href="#contactshadows">ContactShadows</a></li>
@@ -1484,6 +1486,22 @@ Adds a `<group />` that aligns objects to screen space.
1484
1486
  </ScreenSpace>
1485
1487
  ```
1486
1488
 
1489
+ #### ScreenSizer
1490
+
1491
+ [![](https://img.shields.io/badge/-storybook-%23ff69b4)](https://drei.pmnd.rs/?path=/story/abstractions-screensizer--screen-sizer-story)
1492
+
1493
+ Adds a `<object3D />` that scales objects to screen space.
1494
+
1495
+ ```jsx
1496
+ <ScreenSizer
1497
+ scale={1} // scale factor
1498
+ >
1499
+ <Box
1500
+ args={[100, 100, 100]} // will render roughly as a 100px box
1501
+ />
1502
+ </ScreenSizer>
1503
+ ```
1504
+
1487
1505
  #### GradientTexture
1488
1506
 
1489
1507
  <p>
@@ -2070,9 +2088,9 @@ type MeshTransmissionMaterialProps = JSX.IntrinsicElements['meshPhysicalMaterial
2070
2088
  /* Distortion, default: 0 */
2071
2089
  distortion?: number
2072
2090
  /* Distortion scale, default: 0.5 */
2073
- distortionScale: number
2091
+ distortionScale?: number
2074
2092
  /* Temporal distortion (speed of movement), default: 0.0 */
2075
- temporalDistortion: number
2093
+ temporalDistortion?: number
2076
2094
  /** The scene rendered into a texture (use it to share a texture between materials), default: null */
2077
2095
  buffer?: THREE.Texture
2078
2096
  /** transmissionSampler, you can use the threejs transmission sampler texture that is
@@ -2572,6 +2590,8 @@ type Props = {
2572
2590
  instanceItems?: any[]
2573
2591
  /** The max number of items to instance (optional) */
2574
2592
  maxItems?: number
2593
+ /** External parsed sprite data, usually from useSpriteLoader ready for use */
2594
+ spriteDataset?: any
2575
2595
  }
2576
2596
  ```
2577
2597
 
@@ -2580,8 +2600,9 @@ The SpriteAnimator component provided by drei is a powerful tool for animating s
2580
2600
  Notes:
2581
2601
 
2582
2602
  - The SpriteAnimator component internally uses the useFrame hook from react-three-fiber (r3f) for efficient frame updates and rendering.
2583
- - The sprites should contain equal size frames
2603
+ - The sprites (without a JSON file) should contain equal size frames
2584
2604
  - Trimming of spritesheet frames is not yet supported
2605
+ - Internally uses the `useSpriteLoader` or can use data from it directly
2585
2606
 
2586
2607
  ```jsx
2587
2608
  <SpriteAnimator
@@ -2598,7 +2619,7 @@ Notes:
2598
2619
  ScrollControls example
2599
2620
 
2600
2621
  ```jsx
2601
- ;<ScrollControls damping={0.2} maxSpeed={0.5} pages={2}>
2622
+ <ScrollControls damping={0.2} maxSpeed={0.5} pages={2}>
2602
2623
  <SpriteAnimator
2603
2624
  position={[0.0, -1.5, -1.5]}
2604
2625
  startFrame={0}
@@ -2832,7 +2853,7 @@ return (
2832
2853
 
2833
2854
  ![](https://img.shields.io/badge/-Dom only-red)
2834
2855
 
2835
- A small hook that sets the css body cursor according to the hover state of a mesh, so that you can give the use visual feedback when the mouse enters a shape. Arguments 1 and 2 determine the style, the defaults are: onPointerOver = 'pointer', onPointerOut = 'auto'.
2856
+ A small hook that sets the css body cursor according to the hover state of a mesh, so that you can give the user visual feedback when the mouse enters a shape. Arguments 1 and 2 determine the style, the defaults are: onPointerOver = 'pointer', onPointerOut = 'auto'.
2836
2857
 
2837
2858
  ```jsx
2838
2859
  const [hovered, set] = useState()
@@ -3234,6 +3255,58 @@ In order to preload you do this:
3234
3255
  useFont.preload('/fonts/helvetiker_regular.typeface.json')
3235
3256
  ```
3236
3257
 
3258
+ #### useSpriteLoader
3259
+
3260
+ Loads texture and JSON files with multiple or single animations and parses them into appropriate format. These assets can be used by multiple SpriteAnimator components to save memory and loading times.
3261
+
3262
+ ```jsx
3263
+ /** The texture url to load the sprite frames from */
3264
+ input?: Url | null,
3265
+ /** The JSON data describing the position of the frames within the texture (optional) */
3266
+ json?: string | null,
3267
+ /** The animation names into which the frames will be divided into (optional) */
3268
+ animationNames?: string[] | null,
3269
+ /** The number of frames on a standalone (no JSON data) spritesheet (optional)*/
3270
+ numberOfFrames?: number | null,
3271
+ /** The callback to call when all textures and data have been loaded and parsed */
3272
+ onLoad?: (texture: Texture, textureData?: any) => void
3273
+ ```
3274
+
3275
+ ```jsx
3276
+ const { spriteObj } = useSpriteLoader(
3277
+ 'multiasset.png',
3278
+ 'multiasset.json',
3279
+
3280
+ ['orange', 'Idle Blinking', '_Bat'],
3281
+ null
3282
+ )
3283
+
3284
+ <SpriteAnimator
3285
+ position={[4.5, 0.5, 0.1]}
3286
+ autoPlay={true}
3287
+ loop={true}
3288
+ scale={5}
3289
+ frameName={'_Bat'}
3290
+ animationNames={['_Bat']}
3291
+ spriteDataset={spriteObj}
3292
+ alphaTest={0.01}
3293
+ asSprite={false}
3294
+ />
3295
+
3296
+ <SpriteAnimator
3297
+ position={[5.5, 0.5, 5.8]}
3298
+ autoPlay={true}
3299
+ loop={true}
3300
+ scale={5}
3301
+ frameName={'Idle Blinking'}
3302
+ animationNames={['Idle Blinking']}
3303
+ spriteDataset={spriteObj}
3304
+ alphaTest={0.01}
3305
+ asSprite={false}
3306
+ />
3307
+ ```
3308
+
3309
+
3237
3310
  # Performance
3238
3311
 
3239
3312
  #### Instances
@@ -4500,7 +4573,7 @@ function Foo() {
4500
4573
  return <SpotLight depthBuffer={depthBuffer} />
4501
4574
  ```
4502
4575
 
4503
- #### SpotLightShadows
4576
+ #### SpotLightShadow
4504
4577
 
4505
4578
  [![](https://img.shields.io/badge/-storybook-%23ff69b4)](https://drei.pmnd.rs/?path=/story/staging-spotlight--spotlight-shadows-st)
4506
4579
 
@@ -4512,7 +4585,7 @@ A shadow caster that can help cast shadows of different patterns (textures) onto
4512
4585
 
4513
4586
  ```jsx
4514
4587
  <SpotLight>
4515
- <SpotLightShadows
4588
+ <SpotLightShadow
4516
4589
  distance={0.4} // Distance between the shadow caster and light
4517
4590
  alphaTest={0.5} // Sets the alpha value to be used when running an alpha test. See Material.alphaTest
4518
4591
  scale={1} // Scale of the shadow caster plane
@@ -4524,7 +4597,7 @@ A shadow caster that can help cast shadows of different patterns (textures) onto
4524
4597
  </SpotLight>
4525
4598
  ```
4526
4599
 
4527
- An optinal `shader` prop lets you run a custom shader to modify/add effects to your shadow texture. The shader privides the following uniforms and varyings.
4600
+ An optional `shader` prop lets you run a custom shader to modify/add effects to your shadow texture. The shader provides the following uniforms and varyings.
4528
4601
 
4529
4602
  | Type | Name | Notes |
4530
4603
  | ------------------- | ------------ | -------------------------------------- |
@@ -1,4 +1,4 @@
1
- import { OrthographicCamera, PerspectiveCamera } from 'three';
1
+ import { EventDispatcher, OrthographicCamera, PerspectiveCamera } from 'three';
2
2
  import { ReactThreeFiber } from '@react-three/fiber';
3
3
  import CameraControlsImpl from 'camera-controls';
4
4
  import { ForwardRefComponent } from '../helpers/ts-utils';
@@ -17,6 +17,6 @@ export type CameraControlsProps = Omit<ReactThreeFiber.Overwrite<ReactThreeFiber
17
17
  }) => void;
18
18
  events?: boolean;
19
19
  regress?: boolean;
20
- }>, 'ref'>;
20
+ }>, 'ref' | keyof EventDispatcher>;
21
21
  export declare const CameraControls: ForwardRefComponent<CameraControlsProps, CameraControlsImpl>;
22
22
  export type CameraControls = CameraControlsImpl;
@@ -8,8 +8,8 @@ type MeshTransmissionMaterialType = Omit<JSX.IntrinsicElements['meshPhysicalMate
8
8
  anisotropy?: number;
9
9
  anisotropicBlur?: number;
10
10
  distortion?: number;
11
- distortionScale: number;
12
- temporalDistortion: number;
11
+ distortionScale?: number;
12
+ temporalDistortion?: number;
13
13
  buffer?: THREE.Texture;
14
14
  time?: number;
15
15
  args?: [samples: number, transmissionSampler: boolean];
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("@babel/runtime/helpers/extends"),r=require("@react-three/fiber"),t=require("react"),c=require("react-merge-refs"),a=require("three"),u=require("./calculateScaleFactor.cjs.js");function n(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}function l(e){if(e&&e.__esModule)return e;var r=Object.create(null);return e&&Object.keys(e).forEach((function(t){if("default"!==t){var c=Object.getOwnPropertyDescriptor(e,t);Object.defineProperty(r,t,c.get?c:{enumerable:!0,get:function(){return e[t]}})}})),r.default=e,Object.freeze(r)}var o=n(e),f=l(t),i=n(c);const s=new a.Vector3,d=t.forwardRef((({scale:e=1,...c},a)=>{const n=t.useRef(null);return r.useFrame((r=>{const t=n.current;if(!t)return;const c=u.calculateScaleFactor(t.getWorldPosition(s),e,r.camera,r.size);t.scale.setScalar(c*e)})),f.createElement("object3D",o.default({ref:i.default([n,a])},c))}));exports.ScreenSizer=d;
@@ -0,0 +1,7 @@
1
+ import { Object3DProps } from '@react-three/fiber';
2
+ import { ForwardRefComponent } from 'helpers/ts-utils';
3
+ import { Object3D } from 'three';
4
+ export interface ScreenSizerProps extends Object3DProps {
5
+ scale: number;
6
+ }
7
+ export declare const ScreenSizer: ForwardRefComponent<ScreenSizerProps, Object3D>;
@@ -0,0 +1,39 @@
1
+ import _extends from '@babel/runtime/helpers/esm/extends';
2
+ import { useFrame } from '@react-three/fiber';
3
+ import * as React from 'react';
4
+ import { forwardRef, useRef } from 'react';
5
+ import mergeRefs from 'react-merge-refs';
6
+ import { Vector3 } from 'three';
7
+ import { calculateScaleFactor } from './calculateScaleFactor.js';
8
+
9
+ const worldPos = /* @__PURE__ */new Vector3();
10
+ /**
11
+ * Wraps children in an `Object3D` and attempts to scale from
12
+ * world units to screen units * scale factor.
13
+ *
14
+ * For example, this will render a box of roughly 1x1 pixel size,
15
+ * independently of how far the camera is.
16
+ *
17
+ * ```jsx
18
+ * <ScreenSizer>
19
+ * <Box />
20
+ * </ScreenSizer>
21
+ * ```
22
+ */
23
+ const ScreenSizer = /* @__PURE__ */forwardRef(({
24
+ scale = 1,
25
+ ...props
26
+ }, ref) => {
27
+ const container = useRef(null);
28
+ useFrame(state => {
29
+ const obj = container.current;
30
+ if (!obj) return;
31
+ const sf = calculateScaleFactor(obj.getWorldPosition(worldPos), scale, state.camera, state.size);
32
+ obj.scale.setScalar(sf * scale);
33
+ });
34
+ return /*#__PURE__*/React.createElement("object3D", _extends({
35
+ ref: mergeRefs([container, ref])
36
+ }, props));
37
+ });
38
+
39
+ export { ScreenSizer };
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("@babel/runtime/helpers/extends"),r=require("react"),t=require("@react-three/fiber"),n=require("three"),a=require("./Instances.cjs.js");function u(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}function c(e){if(e&&e.__esModule)return e;var r=Object.create(null);return e&&Object.keys(e).forEach((function(t){if("default"!==t){var n=Object.getOwnPropertyDescriptor(e,t);Object.defineProperty(r,t,n.get?n:{enumerable:!0,get:function(){return e[t]}})}})),r.default=e,Object.freeze(r)}require("react-merge-refs"),require("react-composer"),require("../helpers/deprecated.cjs.js");var s=u(e),o=c(r),f=c(n);const l=o.createContext(null);const m=o.forwardRef((({startFrame:e,endFrame:r,fps:n,frameName:u,textureDataURL:c,textureImageURL:m,loop:i,numberOfFrames:p,autoPlay:h,animationNames:d,onStart:w,onEnd:y,onLoopEnd:E,onFrame:g,play:x,pause:S,flipX:b,alphaTest:F,children:R,asSprite:j,offset:v,playBackwards:z,resetOnEnd:M,maxItems:A,instanceItems:O,...N},L)=>{const k=o.useRef(),q=o.useRef(null),I=o.useRef(),P=o.useRef(),T=o.useRef(window.performance.now()),C=o.useRef(),D=o.useRef(e||0),_=o.useRef(u||""),B=1e3/(n||30),[U,G]=o.useState(new f.Texture),H=o.useRef(0),[W,X]=o.useState([1,1,1]),J=b?-1:1,[K,Q]=o.useState(null==j||j),V=o.useRef(S),Y=o.useRef(v),Z=o.useRef(!1),$=o.useRef([]);function ee(){}const re=o.useMemo((()=>({current:Y.current,offset:Y.current,imageUrl:m,reset:ee,hasEnded:!1,ref:L})),[m]);o.useImperativeHandle(L,(()=>k.current),[]),o.useLayoutEffect((()=>{Y.current=v}),[v]);const te=(e,r)=>{const t=r/e;return P.current&&P.current.scale.set(1,t,1),[1,t,1]};o.useEffect((()=>{if(c&&m)!function(e,r,t){const n=new f.TextureLoader,a=fetch(e).then((e=>e.json())),u=new Promise((e=>{n.load(r,e)}));Promise.all([a,u]).then((e=>{t(e[0],e[1])}))}(c,m,ne);else if(m){const e=new f.TextureLoader;new Promise((r=>{e.load(m,r)})).then((e=>{ne(null,e)}))}}),[]),o.useEffect((()=>{Q(null==j||j)}),[j]),o.useEffect((()=>{re.hasEnded=!1,q.current&&!0===z?D.current=q.current.frames.length-1:D.current=0}),[z]),o.useLayoutEffect((()=>{ue()}),[U,b]),o.useEffect((()=>{h&&(V.current=!1)}),[h]),o.useEffect((()=>{if(_.current!==u&&u&&(D.current=0,_.current=u,re.hasEnded=!1,ue(),q.current)){const{w:e,h:r}=se(q.current.frames).sourceSize,t=te(e,r);X(t)}}),[u]);const ne=(e,r)=>{if(null===e){if(p){const e=r.image.width,t=r.image.height,n=e/p,a=t;if(C.current=r,H.current=p,z&&(D.current=p-1),q.current={frames:[],meta:{version:"1.0",size:{w:e,h:t},scale:"1"}},parseInt(n.toString(),10)===n)for(let e=0;e<p;e++)q.current.frames.push({frame:{x:e*n,y:0,w:n,h:a},rotated:!1,trimmed:!1,spriteSourceSize:{x:0,y:0,w:n,h:a},sourceSize:{w:n,h:t}})}}else{q.current=e,q.current.frames=Array.isArray(e.frames)?e.frames:ae(),H.current=Array.isArray(e.frames)?e.frames.length:Object.keys(e.frames).length,C.current=r,z&&(D.current=H.current-1);const{w:t,h:n}=se(e.frames).sourceSize,a=te(t,n);X(a),I.current&&(I.current.map=r)}if(O)for(var t=0;t<O.length;t++){const e=Object.keys(q.current.frames),r=e[Math.floor(Math.random()*e.length)];$.current.push({key:t,frames:q.current.frames,selectedFrame:r,offset:{x:0,y:0}})}r.premultiplyAlpha=!1,G(r)},ae=()=>{const e={},r=q.current,t=d;if(t){for(let n=0;n<t.length;n++){e[t[n]]=[];for(const a in r.frames){const u=r.frames[a],c=u.frame,s=c.x,o=c.y,f=c.w,l=c.h,m=u.sourceSize.w,i=u.sourceSize.h;-1!==a.toLowerCase().indexOf(t[n].toLowerCase())&&e[t[n]].push({x:s,y:o,w:f,h:l,frame:c,sourceSize:{w:m,h:i}})}}return e}if(u){const e=[];for(const t in r.frames)e.push(r.frames[t]);return e}},ue=()=>{if(!q.current)return;const{meta:{size:e},frames:r}=q.current,{w:t,h:n}=Array.isArray(r)?r[0].sourceSize:u&&r[u]?r[u][0].sourceSize:{w:0,h:0};I.current.map.wrapS=I.current.map.wrapT=f.RepeatWrapping,I.current.map.center.set(0,0),I.current.map.repeat.set(1*J/(e.w/t),1/(e.h/n));const a=1/((e.h-1)/n);I.current.map.offset.x=0,I.current.map.offset.y=1-a,w&&w({currentFrameName:u,currentFrame:D.current})},ce=(e,r,t,n)=>{var a=void 0===v?re.current:v;const u=D.current;let c=0,s=0;te(e,r);const o=(t.w-1)/e,f=(t.h-1)/r;if(!n[u])return;const{frame:{x:l,y:m},sourceSize:{w:i,h:p}}=n[u],h=1/o,d=1/f;if(c=J>0?h*(l/i):h*(l/i)-I.current.map.repeat.x,s=Math.abs(1-d)-d*(m/p),I.current.map.offset.x=c,I.current.map.offset.y=s,null!=a){let e=Math.floor(a*n.length);e=Math.max(0,Math.min(e,n.length-1)),isNaN(e)&&(console.log("nan frame detected"),e=0),D.current=e}else z?D.current-=1:D.current+=1};t.useFrame(((t,n)=>{var a,c;null!=(a=q.current)&&a.frames&&null!=(c=I.current)&&c.map&&(V.current||re.hasEnded||!h&&!x||((()=>{const t=window.performance.now(),n=t-T.current,{meta:{size:a},frames:c}=q.current,{w:s,h:o}=se(c).sourceSize,f=Array.isArray(c)?c:u?c[u]:[],l=r||f.length-1;var m=void 0===v?re.current:v,p=z?D.current<0:D.current>l,h=z?D.current===l:0===D.current,d=z?D.current<0:D.current>=l;if(p){if(D.current=i&&null!=e?e:0,z&&(D.current=l),i?null==E||E({currentFrameName:u,currentFrame:D.current}):(null==y||y({currentFrameName:u,currentFrame:D.current}),m||console.log("will end"),re.hasEnded=!M,M&&(V.current=!0)),!i)return}else h&&(null==w||w({currentFrameName:u,currentFrame:D.current}));void 0!==m&&d?!1===Z.current&&(null==y||y({currentFrameName:u,currentFrame:D.current}),Z.current=!0):Z.current=!1,n<=B||(T.current=t-n%B,ce(s,o,a,f))})(),g&&g({currentFrameName:_.current,currentFrame:D.current})))}));const se=e=>{if(Array.isArray(e))return e[0];if("object"==typeof e&&null!==e){const r=Object.keys(e);return u?e[u][0]:e[r[0]][0]}return{w:0,h:0}};return o.createElement("group",s.default({},N,{ref:k}),o.createElement(l.Provider,{value:re},o.createElement(o.Suspense,{fallback:null},K&&o.createElement("sprite",{ref:P,scale:W},o.createElement("spriteMaterial",{toneMapped:!1,ref:I,map:U,transparent:!0,alphaTest:null!=F?F:0})),!K&&o.createElement(a.Instances,{limit:A},o.createElement("planeGeometry",{args:[1,1]}),o.createElement("meshBasicMaterial",{toneMapped:!1,side:f.DoubleSide,ref:I,map:U,transparent:!0,alphaTest:null!=F?F:0}),(null!=O?O:[0]).map(((e,r)=>{const t=U.clone();return I.current&&$.current[r]&&t.offset.set($.current[r].offset.x,$.current[r].offset.y),o.createElement(a.Instance,{key:r,ref:P,position:e,scale:W},o.createElement("meshBasicMaterial",{toneMapped:!1,side:f.DoubleSide,map:t,transparent:!0,alphaTest:null!=F?F:0}))})))),R))}));exports.SpriteAnimator=m,exports.useSpriteAnimator=function(){return o.useContext(l)};
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("@babel/runtime/helpers/extends"),r=require("react"),t=require("@react-three/fiber"),n=require("three"),u=require("./Instances.cjs.js"),a=require("./useSpriteLoader.cjs.js");function c(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}function s(e){if(e&&e.__esModule)return e;var r=Object.create(null);return e&&Object.keys(e).forEach((function(t){if("default"!==t){var n=Object.getOwnPropertyDescriptor(e,t);Object.defineProperty(r,t,n.get?n:{enumerable:!0,get:function(){return e[t]}})}})),r.default=e,Object.freeze(r)}require("react-merge-refs"),require("react-composer"),require("../helpers/deprecated.cjs.js");var l=c(e),o=s(r),f=s(n);const i=o.createContext(null);const m=o.forwardRef((({startFrame:e,endFrame:r,fps:n,frameName:c,textureDataURL:s,textureImageURL:m,loop:p,numberOfFrames:d,autoPlay:h,animationNames:y,onStart:E,onEnd:w,onLoopEnd:v,onFrame:g,play:b,pause:x,flipX:F,alphaTest:S,children:j,asSprite:R,offset:M,playBackwards:O,resetOnEnd:A,maxItems:z,instanceItems:N,spriteDataset:q,...T},k)=>{const I=o.useRef(),L=o.useRef(null),D=o.useRef(),P=o.useRef(),_=o.useRef(window.performance.now()),U=o.useRef(e||0),B=o.useRef(c||""),C=1e3/(n||30),[G,H]=o.useState(new f.Texture),J=o.useRef(0),[W,X]=o.useState([1,1,1]),K=F?-1:1,[Q,V]=o.useState(null==R||R),Y=o.useRef(x),Z=o.useRef(M),$=o.useRef(!1),ee=o.useRef([]),{spriteObj:re,loadJsonAndTexture:te}=a.useSpriteLoader(null,null,y,d);function ne(){}const ue=o.useMemo((()=>({current:Z.current,offset:Z.current,imageUrl:m,reset:ne,hasEnded:!1,ref:k})),[m,q]);o.useImperativeHandle(k,(()=>I.current),[]),o.useLayoutEffect((()=>{Z.current=M}),[M]);const ae=(e,r)=>{const t=r/e;return P.current&&P.current.scale.set(1,t,1),[1,t,1]};o.useEffect((()=>{var e;q?ce(null==q||null==(e=q.spriteTexture)?void 0:e.clone(),q.spriteData):te(m,s)}),[q]),o.useEffect((()=>{var e;re&&ce(null==re||null==(e=re.spriteTexture)?void 0:e.clone(),null==re?void 0:re.spriteData)}),[re]),o.useEffect((()=>{V(null==R||R)}),[R]),o.useEffect((()=>{ue.hasEnded=!1,L.current&&!0===O?U.current=L.current.frames.length-1:U.current=0}),[O]),o.useLayoutEffect((()=>{se()}),[G,F]),o.useEffect((()=>{h&&(Y.current=!1)}),[h]),o.useEffect((()=>{if(B.current!==c&&c&&(U.current=0,B.current=c,ue.hasEnded=!1,se(),L.current)){const{w:e,h:r}=oe(L.current.frames).sourceSize,t=ae(e,r);X(t)}}),[c]);const ce=(e,r=null)=>{if(null===r){if(d){const t=e.image.width,n=e.image.height;J.current=d,O&&(U.current=d-1),L.current={frames:[],meta:{version:"1.0",size:{w:t,h:n},scale:"1"}},L.current.frames=r}}else{L.current=r,J.current=L.current.frames.length,O&&(U.current=J.current-1);const{w:t,h:n}=oe(L.current.frames).sourceSize,u=ae(t,n);X(u),D.current&&(D.current.map=e)}if(N)for(var t=0;t<N.length;t++){const e=Object.keys(L.current.frames),r=e[Math.floor(Math.random()*e.length)];ee.current.push({key:t,frames:L.current.frames,selectedFrame:r,offset:{x:0,y:0}})}H(e)},se=()=>{if(!L.current)return;const{meta:{size:e},frames:r}=L.current,{w:t,h:n}=Array.isArray(r)?r[0].sourceSize:c&&r[c]?r[c][0].sourceSize:{w:0,h:0};D.current.map.wrapS=D.current.map.wrapT=f.RepeatWrapping,D.current.map.center.set(0,0),D.current.map.repeat.set(1*K/(e.w/t),1/(e.h/n));const u=1/((e.h-1)/n);D.current.map.offset.x=0,D.current.map.offset.y=1-u,E&&E({currentFrameName:c,currentFrame:U.current})},le=(e,r,t,n)=>{var u=void 0===M?ue.current:M;const a=U.current;let c=0,s=0;ae(e,r);const l=(t.w-1)/e,o=(t.h-1)/r;if(!n[a])return;const{frame:{x:f,y:i},sourceSize:{w:m,h:p}}=n[a],d=1/l,h=1/o;if(c=K>0?d*(f/m):d*(f/m)-D.current.map.repeat.x,s=Math.abs(1-h)-h*(i/p),D.current.map.offset.x=c,D.current.map.offset.y=s,null!=u){let e=Math.floor(u*n.length);e=Math.max(0,Math.min(e,n.length-1)),isNaN(e)&&(console.log("nan frame detected"),e=0),U.current=e}else O?U.current-=1:U.current+=1};t.useFrame(((t,n)=>{var u,a;null!=(u=L.current)&&u.frames&&null!=(a=D.current)&&a.map&&(Y.current||ue.hasEnded||!h&&!b||((()=>{const t=window.performance.now(),n=t-_.current,{meta:{size:u},frames:a}=L.current,{w:s,h:l}=oe(a).sourceSize,o=Array.isArray(a)?a:c?a[c]:[],f=r||o.length-1;var i=void 0===M?ue.current:M,m=O?U.current<0:U.current>f,d=O?U.current===f:0===U.current,h=O?U.current<0:U.current>=f;if(m){if(U.current=p&&null!=e?e:0,O&&(U.current=f),p?null==v||v({currentFrameName:c,currentFrame:U.current}):(null==w||w({currentFrameName:c,currentFrame:U.current}),ue.hasEnded=!A,A&&(Y.current=!0)),!p)return}else d&&(null==E||E({currentFrameName:c,currentFrame:U.current}));void 0!==i&&h?!1===$.current&&(null==w||w({currentFrameName:c,currentFrame:U.current}),$.current=!0):$.current=!1,n<=C||(_.current=t-n%C,le(s,l,u,o))})(),g&&g({currentFrameName:B.current,currentFrame:U.current})))}));const oe=e=>{if(Array.isArray(e))return e[0];if("object"==typeof e&&null!==e){const r=Object.keys(e);return c?e[c][0]:e[r[0]][0]}return{w:0,h:0}};return o.createElement("group",l.default({},T,{ref:I}),o.createElement(i.Provider,{value:ue},o.createElement(o.Suspense,{fallback:null},Q&&o.createElement("sprite",{ref:P,scale:W},o.createElement("spriteMaterial",{premultipliedAlpha:!1,toneMapped:!1,ref:D,map:G,transparent:!0,alphaTest:null!=S?S:0})),!Q&&o.createElement(u.Instances,{limit:z},o.createElement("planeGeometry",{args:[1,1]}),o.createElement("meshBasicMaterial",{premultipliedAlpha:!1,toneMapped:!1,side:f.DoubleSide,ref:D,map:G,transparent:!0,alphaTest:null!=S?S:0}),(null!=N?N:[0]).map(((e,r)=>o.createElement(u.Instance,{key:r,ref:P,position:e,scale:W}))))),j))}));exports.SpriteAnimator=m,exports.useSpriteAnimator=function(){return o.useContext(i)};
@@ -5,7 +5,7 @@ export type SpriteAnimatorProps = {
5
5
  fps?: number;
6
6
  frameName?: string;
7
7
  textureDataURL?: string;
8
- textureImageURL: string;
8
+ textureImageURL?: string;
9
9
  loop?: boolean;
10
10
  numberOfFrames?: number;
11
11
  autoPlay?: boolean;
@@ -25,6 +25,7 @@ export type SpriteAnimatorProps = {
25
25
  resetOnEnd?: boolean;
26
26
  maxItems?: number;
27
27
  instanceItems?: any[];
28
+ spriteDataset?: any;
28
29
  } & JSX.IntrinsicElements['group'];
29
30
  type SpriteAnimatorState = {
30
31
  current: number | undefined;
@@ -3,6 +3,7 @@ import * as React from 'react';
3
3
  import { useFrame } from '@react-three/fiber';
4
4
  import * as THREE from 'three';
5
5
  import { Instances, Instance } from './Instances.js';
6
+ import { useSpriteLoader } from './useSpriteLoader.js';
6
7
 
7
8
  const context = /*#__PURE__*/React.createContext(null);
8
9
  function useSpriteAnimator() {
@@ -34,15 +35,14 @@ const SpriteAnimator = /* @__PURE__ */React.forwardRef(({
34
35
  resetOnEnd,
35
36
  maxItems,
36
37
  instanceItems,
38
+ spriteDataset,
37
39
  ...props
38
40
  }, fref) => {
39
41
  const ref = React.useRef();
40
42
  const spriteData = React.useRef(null);
41
- //const hasEnded = React.useRef(false)
42
43
  const matRef = React.useRef();
43
44
  const spriteRef = React.useRef();
44
45
  const timerOffset = React.useRef(window.performance.now());
45
- const textureData = React.useRef();
46
46
  const currentFrame = React.useRef(startFrame || 0);
47
47
  const currentFrameName = React.useRef(frameName || '');
48
48
  const fpsInterval = 1000 / (fps || 30);
@@ -55,6 +55,10 @@ const SpriteAnimator = /* @__PURE__ */React.forwardRef(({
55
55
  const pos = React.useRef(offset);
56
56
  const softEnd = React.useRef(false);
57
57
  const frameBuffer = React.useRef([]);
58
+ const {
59
+ spriteObj,
60
+ loadJsonAndTexture
61
+ } = useSpriteLoader(null, null, animationNames, numberOfFrames);
58
62
  //
59
63
 
60
64
  function reset() {}
@@ -65,21 +69,11 @@ const SpriteAnimator = /* @__PURE__ */React.forwardRef(({
65
69
  reset: reset,
66
70
  hasEnded: false,
67
71
  ref: fref
68
- }), [textureImageURL]);
72
+ }), [textureImageURL, spriteDataset]);
69
73
  React.useImperativeHandle(fref, () => ref.current, []);
70
74
  React.useLayoutEffect(() => {
71
75
  pos.current = offset;
72
76
  }, [offset]);
73
- function loadJsonAndTextureAndExecuteCallback(jsonUrl, textureUrl, callback) {
74
- const textureLoader = new THREE.TextureLoader();
75
- const jsonPromise = fetch(jsonUrl).then(response => response.json());
76
- const texturePromise = new Promise(resolve => {
77
- textureLoader.load(textureUrl, resolve);
78
- });
79
- Promise.all([jsonPromise, texturePromise]).then(response => {
80
- callback(response[0], response[1]);
81
- });
82
- }
83
77
  const calculateAspectRatio = (width, height) => {
84
78
  const aspectRatio = height / width;
85
79
  if (spriteRef.current) {
@@ -90,18 +84,19 @@ const SpriteAnimator = /* @__PURE__ */React.forwardRef(({
90
84
 
91
85
  // initial loads
92
86
  React.useEffect(() => {
93
- if (textureDataURL && textureImageURL) {
94
- loadJsonAndTextureAndExecuteCallback(textureDataURL, textureImageURL, parseSpriteData);
95
- } else if (textureImageURL) {
96
- // only load the texture, this is an image sprite only
97
- const textureLoader = new THREE.TextureLoader();
98
- new Promise(resolve => {
99
- textureLoader.load(textureImageURL, resolve);
100
- }).then(texture => {
101
- parseSpriteData(null, texture);
102
- });
87
+ if (spriteDataset) {
88
+ var _spriteDataset$sprite;
89
+ parseSpriteDataLite(spriteDataset == null || (_spriteDataset$sprite = spriteDataset.spriteTexture) == null ? void 0 : _spriteDataset$sprite.clone(), spriteDataset.spriteData);
90
+ } else {
91
+ loadJsonAndTexture(textureImageURL, textureDataURL);
92
+ }
93
+ }, [spriteDataset]);
94
+ React.useEffect(() => {
95
+ if (spriteObj) {
96
+ var _spriteObj$spriteText;
97
+ parseSpriteDataLite(spriteObj == null || (_spriteObj$spriteText = spriteObj.spriteTexture) == null ? void 0 : _spriteObj$spriteText.clone(), spriteObj == null ? void 0 : spriteObj.spriteData);
103
98
  }
104
- }, []);
99
+ }, [spriteObj]);
105
100
  React.useEffect(() => {
106
101
  setDisplayAsSprite(asSprite !== null && asSprite !== void 0 ? asSprite : true);
107
102
  }, [asSprite]);
@@ -140,17 +135,13 @@ const SpriteAnimator = /* @__PURE__ */React.forwardRef(({
140
135
  }
141
136
  }, [frameName]);
142
137
 
143
- // parse sprite-data from JSON file (jsonHash or jsonArray)
144
- const parseSpriteData = (json, _spriteTexture) => {
145
- // sprite only case
146
- if (json === null) {
138
+ // lite version for pre-loaded assets
139
+ const parseSpriteDataLite = (textureData, frameData = null) => {
140
+ if (frameData === null) {
147
141
  if (numberOfFrames) {
148
142
  //get size from texture
149
- const width = _spriteTexture.image.width;
150
- const height = _spriteTexture.image.height;
151
- const frameWidth = width / numberOfFrames;
152
- const frameHeight = height;
153
- textureData.current = _spriteTexture;
143
+ const width = textureData.image.width;
144
+ const height = textureData.image.height;
154
145
  totalFrames.current = numberOfFrames;
155
146
  if (playBackwards) {
156
147
  currentFrame.current = numberOfFrames - 1;
@@ -166,48 +157,22 @@ const SpriteAnimator = /* @__PURE__ */React.forwardRef(({
166
157
  scale: '1'
167
158
  }
168
159
  };
169
- if (parseInt(frameWidth.toString(), 10) === frameWidth) {
170
- // if it fits
171
- for (let i = 0; i < numberOfFrames; i++) {
172
- spriteData.current.frames.push({
173
- frame: {
174
- x: i * frameWidth,
175
- y: 0,
176
- w: frameWidth,
177
- h: frameHeight
178
- },
179
- rotated: false,
180
- trimmed: false,
181
- spriteSourceSize: {
182
- x: 0,
183
- y: 0,
184
- w: frameWidth,
185
- h: frameHeight
186
- },
187
- sourceSize: {
188
- w: frameWidth,
189
- h: height
190
- }
191
- });
192
- }
193
- }
160
+ spriteData.current.frames = frameData;
194
161
  }
195
162
  } else {
196
- spriteData.current = json;
197
- spriteData.current.frames = Array.isArray(json.frames) ? json.frames : parseFrames();
198
- totalFrames.current = Array.isArray(json.frames) ? json.frames.length : Object.keys(json.frames).length;
199
- textureData.current = _spriteTexture;
163
+ spriteData.current = frameData;
164
+ totalFrames.current = spriteData.current.frames.length;
200
165
  if (playBackwards) {
201
166
  currentFrame.current = totalFrames.current - 1;
202
167
  }
203
168
  const {
204
169
  w,
205
170
  h
206
- } = getFirstItem(json.frames).sourceSize;
171
+ } = getFirstItem(spriteData.current.frames).sourceSize;
207
172
  const aspect = calculateAspectRatio(w, h);
208
173
  setAspect(aspect);
209
174
  if (matRef.current) {
210
- matRef.current.map = _spriteTexture;
175
+ matRef.current.map = textureData;
211
176
  }
212
177
  }
213
178
 
@@ -227,50 +192,7 @@ const SpriteAnimator = /* @__PURE__ */React.forwardRef(({
227
192
  });
228
193
  }
229
194
  }
230
- _spriteTexture.premultiplyAlpha = false;
231
- setSpriteTexture(_spriteTexture);
232
- };
233
-
234
- // for frame based JSON Hash sprite data
235
- const parseFrames = () => {
236
- const sprites = {};
237
- const data = spriteData.current;
238
- const delimiters = animationNames;
239
- if (delimiters) {
240
- for (let i = 0; i < delimiters.length; i++) {
241
- sprites[delimiters[i]] = [];
242
- for (const innerKey in data['frames']) {
243
- const value = data['frames'][innerKey];
244
- const frameData = value['frame'];
245
- const x = frameData['x'];
246
- const y = frameData['y'];
247
- const width = frameData['w'];
248
- const height = frameData['h'];
249
- const sourceWidth = value['sourceSize']['w'];
250
- const sourceHeight = value['sourceSize']['h'];
251
- if (innerKey.toLowerCase().indexOf(delimiters[i].toLowerCase()) !== -1) {
252
- sprites[delimiters[i]].push({
253
- x: x,
254
- y: y,
255
- w: width,
256
- h: height,
257
- frame: frameData,
258
- sourceSize: {
259
- w: sourceWidth,
260
- h: sourceHeight
261
- }
262
- });
263
- }
264
- }
265
- }
266
- return sprites;
267
- } else if (frameName) {
268
- const spritesArr = [];
269
- for (const key in data.frames) {
270
- spritesArr.push(data.frames[key]);
271
- }
272
- return spritesArr;
273
- }
195
+ setSpriteTexture(textureData);
274
196
  };
275
197
 
276
198
  // modify the sprite material after json is parsed and state updated
@@ -310,6 +232,7 @@ const SpriteAnimator = /* @__PURE__ */React.forwardRef(({
310
232
  // run the animation on each frame
311
233
  const runAnimation = () => {
312
234
  //if (!frameName) return
235
+
313
236
  const now = window.performance.now();
314
237
  const diff = now - timerOffset.current;
315
238
  const {
@@ -345,9 +268,6 @@ const SpriteAnimator = /* @__PURE__ */React.forwardRef(({
345
268
  currentFrameName: frameName,
346
269
  currentFrame: currentFrame.current
347
270
  });
348
- if (!_offset) {
349
- console.log('will end');
350
- }
351
271
  state.hasEnded = resetOnEnd ? false : true;
352
272
  if (resetOnEnd) {
353
273
  pauseRef.current = true;
@@ -476,6 +396,7 @@ const SpriteAnimator = /* @__PURE__ */React.forwardRef(({
476
396
  ref: spriteRef,
477
397
  scale: aspect
478
398
  }, /*#__PURE__*/React.createElement("spriteMaterial", {
399
+ premultipliedAlpha: false,
479
400
  toneMapped: false,
480
401
  ref: matRef,
481
402
  map: spriteTexture,
@@ -486,6 +407,7 @@ const SpriteAnimator = /* @__PURE__ */React.forwardRef(({
486
407
  }, /*#__PURE__*/React.createElement("planeGeometry", {
487
408
  args: [1, 1]
488
409
  }), /*#__PURE__*/React.createElement("meshBasicMaterial", {
410
+ premultipliedAlpha: false,
489
411
  toneMapped: false,
490
412
  side: THREE.DoubleSide,
491
413
  ref: matRef,
@@ -493,23 +415,12 @@ const SpriteAnimator = /* @__PURE__ */React.forwardRef(({
493
415
  transparent: true,
494
416
  alphaTest: alphaTest !== null && alphaTest !== void 0 ? alphaTest : 0.0
495
417
  }), (instanceItems !== null && instanceItems !== void 0 ? instanceItems : [0]).map((item, index) => {
496
- const texture = spriteTexture.clone();
497
- if (matRef.current && frameBuffer.current[index]) {
498
- texture.offset.set(frameBuffer.current[index].offset.x, frameBuffer.current[index].offset.y); // Set the offset for this item
499
- }
500
-
501
418
  return /*#__PURE__*/React.createElement(Instance, {
502
419
  key: index,
503
420
  ref: spriteRef,
504
421
  position: item,
505
422
  scale: aspect
506
- }, /*#__PURE__*/React.createElement("meshBasicMaterial", {
507
- toneMapped: false,
508
- side: THREE.DoubleSide,
509
- map: texture,
510
- transparent: true,
511
- alphaTest: alphaTest !== null && alphaTest !== void 0 ? alphaTest : 0.0
512
- }));
423
+ });
513
424
  }))), children));
514
425
  });
515
426
 
@@ -0,0 +1 @@
1
+ "use strict";function e(e){if(e&&e.__esModule)return e;var t=Object.create(null);return e&&Object.keys(e).forEach((function(r){if("default"!==r){var o=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(t,r,o.get?o:{enumerable:!0,get:function(){return e[r]}})}})),t.default=e,Object.freeze(t)}Object.defineProperty(exports,"__esModule",{value:!0});var t=e(require("three"));const r=new t.Vector3,o=new t.Vector3,n=new t.Vector3,c=(e,t,o,n=1)=>{const c=r.set(e.x/o.width*2-1,-e.y/o.height*2+1,n);return c.unproject(t),c};exports.calculateScaleFactor=(e,t,r,u)=>{const a=((e,t,r)=>{const o=r.width/2,n=r.height/2;t.updateMatrixWorld(!1);const c=e.project(t);return c.x=c.x*o+o,c.y=-c.y*n+n,c})(n.copy(e),r,u);let s=0;for(let n=0;n<2;++n){const i=o.copy(a).setComponent(n,a.getComponent(n)+t),l=c(i,r,u,i.z);s=Math.max(s,e.distanceTo(l))}return s};
@@ -0,0 +1,3 @@
1
+ import { Size } from '@react-three/fiber';
2
+ import * as THREE from 'three';
3
+ export declare const calculateScaleFactor: (point3: THREE.Vector3, radiusPx: number, camera: THREE.Camera, size: Size) => number;
@@ -0,0 +1,31 @@
1
+ import * as THREE from 'three';
2
+
3
+ const tV0 = /* @__PURE__ */new THREE.Vector3();
4
+ const tV1 = /* @__PURE__ */new THREE.Vector3();
5
+ const tV2 = /* @__PURE__ */new THREE.Vector3();
6
+ const getPoint2 = (point3, camera, size) => {
7
+ const widthHalf = size.width / 2;
8
+ const heightHalf = size.height / 2;
9
+ camera.updateMatrixWorld(false);
10
+ const vector = point3.project(camera);
11
+ vector.x = vector.x * widthHalf + widthHalf;
12
+ vector.y = -(vector.y * heightHalf) + heightHalf;
13
+ return vector;
14
+ };
15
+ const getPoint3 = (point2, camera, size, zValue = 1) => {
16
+ const vector = tV0.set(point2.x / size.width * 2 - 1, -(point2.y / size.height) * 2 + 1, zValue);
17
+ vector.unproject(camera);
18
+ return vector;
19
+ };
20
+ const calculateScaleFactor = (point3, radiusPx, camera, size) => {
21
+ const point2 = getPoint2(tV2.copy(point3), camera, size);
22
+ let scale = 0;
23
+ for (let i = 0; i < 2; ++i) {
24
+ const point2off = tV1.copy(point2).setComponent(i, point2.getComponent(i) + radiusPx);
25
+ const point3off = getPoint3(point2off, camera, size, point2off.z);
26
+ scale = Math.max(scale, point3.distanceTo(point3off));
27
+ }
28
+ return scale;
29
+ };
30
+
31
+ export { calculateScaleFactor };