@react-three/fiber 8.12.2 → 8.13.1

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.
@@ -1,11 +1,13 @@
1
- import * as React from 'react';
2
- import type { Options as ResizeOptions } from 'react-use-measure';
3
- import { RenderProps } from '../core';
4
- export interface Props extends Omit<RenderProps<HTMLCanvasElement>, 'size'>, React.HTMLAttributes<HTMLDivElement> {
5
- children: React.ReactNode;
6
- fallback?: React.ReactNode;
7
- resize?: ResizeOptions;
8
- eventSource?: HTMLElement | React.MutableRefObject<HTMLElement>;
9
- eventPrefix?: 'offset' | 'client' | 'page' | 'layer' | 'screen';
10
- }
11
- export declare const Canvas: React.ForwardRefExoticComponent<Props & React.RefAttributes<HTMLCanvasElement>>;
1
+ import * as React from 'react';
2
+ import type { Options as ResizeOptions } from 'react-use-measure';
3
+ import { RenderProps } from '../core';
4
+ export interface CanvasProps extends Omit<RenderProps<HTMLCanvasElement>, 'size'>, React.HTMLAttributes<HTMLDivElement> {
5
+ children: React.ReactNode;
6
+ fallback?: React.ReactNode;
7
+ resize?: ResizeOptions;
8
+ eventSource?: HTMLElement | React.MutableRefObject<HTMLElement>;
9
+ eventPrefix?: 'offset' | 'client' | 'page' | 'layer' | 'screen';
10
+ }
11
+ export interface Props extends CanvasProps {
12
+ }
13
+ export declare const Canvas: React.ForwardRefExoticComponent<Props & React.RefAttributes<HTMLCanvasElement>>;
@@ -1,4 +1,4 @@
1
- import { UseBoundStore } from 'zustand';
2
- import { RootState } from '../core/store';
3
- import { EventManager } from '../core/events';
4
- export declare function createPointerEvents(store: UseBoundStore<RootState>): EventManager<HTMLElement>;
1
+ import { UseBoundStore } from 'zustand';
2
+ import { RootState } from '../core/store';
3
+ import { EventManager } from '../core/events';
4
+ export declare function createPointerEvents(store: UseBoundStore<RootState>): EventManager<HTMLElement>;
@@ -150,13 +150,7 @@ function createRenderer(_roots, _getEventPriority) {
150
150
  }
151
151
 
152
152
  // Remove references
153
- if (child.__r3f) {
154
- delete child.__r3f.root;
155
- delete child.__r3f.objects;
156
- delete child.__r3f.handlers;
157
- delete child.__r3f.memoizedProps;
158
- if (!isPrimitive) delete child.__r3f;
159
- }
153
+ delete child.__r3f;
160
154
 
161
155
  // Dispose item whenever the reconciler feels like it
162
156
  if (shouldDispose && child.dispose && child.type !== 'Scene') {
@@ -350,6 +344,10 @@ function createRenderer(_roots, _getEventPriority) {
350
344
  }
351
345
 
352
346
  var _window$document, _window$navigator;
347
+ /**
348
+ * Returns `true` with correct TS type inference if an object has a configurable color space (since r152).
349
+ */
350
+ const hasColorSpace = object => 'colorSpace' in object || 'outputColorSpace' in object;
353
351
  /**
354
352
  * The current THREE.ColorManagement instance, if present.
355
353
  */
@@ -635,6 +633,21 @@ function applyProps$1(instance, data) {
635
633
  if (instance.__r3f) instance.__r3f.memoizedProps = memoized;
636
634
  for (let i = 0; i < changes.length; i++) {
637
635
  let [key, value, isEvent, keys] = changes[i];
636
+
637
+ // Alias (output)encoding => (output)colorSpace (since r152)
638
+ // https://github.com/pmndrs/react-three-fiber/pull/2829
639
+ if (hasColorSpace(instance)) {
640
+ const sRGBEncoding = 3001;
641
+ const SRGBColorSpace = 'srgb';
642
+ const LinearSRGBColorSpace = 'srgb-linear';
643
+ if (key === 'encoding') {
644
+ key = 'colorSpace';
645
+ value = value === sRGBEncoding ? SRGBColorSpace : LinearSRGBColorSpace;
646
+ } else if (key === 'outputEncoding') {
647
+ key = 'outputColorSpace';
648
+ value = value === sRGBEncoding ? SRGBColorSpace : LinearSRGBColorSpace;
649
+ }
650
+ }
638
651
  let currentInstance = instance;
639
652
  let targetProp = currentInstance[key];
640
653
 
@@ -703,12 +716,14 @@ function applyProps$1(instance, data) {
703
716
  // Else, just overwrite the value
704
717
  } else {
705
718
  currentInstance[key] = value;
719
+
706
720
  // Auto-convert sRGB textures, for now ...
707
721
  // https://github.com/pmndrs/react-three-fiber/issues/344
708
- if (!rootState.linear && currentInstance[key] instanceof THREE.Texture &&
722
+ if (currentInstance[key] instanceof THREE.Texture &&
709
723
  // sRGB textures must be RGBA8 since r137 https://github.com/mrdoob/three.js/pull/23129
710
724
  currentInstance[key].format === THREE.RGBAFormat && currentInstance[key].type === THREE.UnsignedByteType) {
711
- currentInstance[key].encoding = THREE.sRGBEncoding;
725
+ const texture = currentInstance[key];
726
+ if (hasColorSpace(texture) && hasColorSpace(rootState.gl)) texture.colorSpace = rootState.gl.outputColorSpace;else texture.encoding = rootState.gl.outputEncoding;
712
727
  }
713
728
  }
714
729
  invalidateInstance(instance);
@@ -1185,8 +1200,6 @@ function createEvents(store) {
1185
1200
  };
1186
1201
  }
1187
1202
 
1188
- // Keys that shouldn't be copied between R3F stores
1189
- const privateKeys = ['set', 'get', 'setSize', 'setFrameloop', 'setDpr', 'events', 'invalidate', 'advance', 'size', 'viewport'];
1190
1203
  const isRenderer = def => !!(def != null && def.render);
1191
1204
  const context = /*#__PURE__*/React.createContext(null);
1192
1205
  const createStore = (invalidate, advance) => {
@@ -1888,10 +1901,14 @@ function createRoot(canvas) {
1888
1901
  if (ColorManagement) {
1889
1902
  if ('enabled' in ColorManagement) ColorManagement.enabled = !legacy;else if ('legacyMode' in ColorManagement) ColorManagement.legacyMode = legacy;
1890
1903
  }
1891
- const outputEncoding = linear ? THREE.LinearEncoding : THREE.sRGBEncoding;
1892
- const toneMapping = flat ? THREE.NoToneMapping : THREE.ACESFilmicToneMapping;
1893
- if (gl.outputEncoding !== outputEncoding) gl.outputEncoding = outputEncoding;
1894
- if (gl.toneMapping !== toneMapping) gl.toneMapping = toneMapping;
1904
+
1905
+ // Set color space and tonemapping preferences
1906
+ const LinearEncoding = 3000;
1907
+ const sRGBEncoding = 3001;
1908
+ applyProps(gl, {
1909
+ outputEncoding: linear ? LinearEncoding : sRGBEncoding,
1910
+ toneMapping: flat ? THREE.NoToneMapping : THREE.ACESFilmicToneMapping
1911
+ });
1895
1912
 
1896
1913
  // Update color management state
1897
1914
  if (state.legacy !== legacy) state.set(() => ({
@@ -2012,7 +2029,6 @@ function unmountComponentAtNode(canvas, callback) {
2012
2029
  }
2013
2030
  function createPortal(children, container, state) {
2014
2031
  return /*#__PURE__*/React.createElement(Portal, {
2015
- key: container.uuid,
2016
2032
  children: children,
2017
2033
  container: container,
2018
2034
  state: state
@@ -2036,26 +2052,9 @@ function Portal({
2036
2052
  const previousRoot = useStore();
2037
2053
  const [raycaster] = React.useState(() => new THREE.Raycaster());
2038
2054
  const [pointer] = React.useState(() => new THREE.Vector2());
2039
- const inject = React.useCallback((rootState, injectState) => {
2040
- const intersect = {
2041
- ...rootState
2042
- }; // all prev state props
2043
-
2044
- // Only the fields of "rootState" that do not differ from injectState
2045
- // Some props should be off-limits
2046
- // Otherwise filter out the props that are different and let the inject layer take precedence
2047
- Object.keys(rootState).forEach(key => {
2048
- if (
2049
- // Some props should be off-limits
2050
- privateKeys.includes(key) ||
2051
- // Otherwise filter out the props that are different and let the inject layer take precedence
2052
- // Unless the inject layer props is undefined, then we keep the root layer
2053
- rootState[key] !== injectState[key] && injectState[key]) {
2054
- delete intersect[key];
2055
- }
2056
- });
2057
- let viewport = undefined;
2058
- if (injectState && size) {
2055
+ const inject = useMutableCallback((rootState, injectState) => {
2056
+ let viewport;
2057
+ if (injectState.camera && size) {
2059
2058
  const camera = injectState.camera;
2060
2059
  // Calculate the override viewport, if present
2061
2060
  viewport = rootState.viewport.getCurrentViewport(camera, new THREE.Vector3(), size);
@@ -2064,7 +2063,9 @@ function Portal({
2064
2063
  }
2065
2064
  return {
2066
2065
  // The intersect consists of the previous root state
2067
- ...intersect,
2066
+ ...rootState,
2067
+ get: injectState.get,
2068
+ set: injectState.set,
2068
2069
  // Portals have their own scene, which forms the root, a raycaster and a pointer
2069
2070
  scene: container,
2070
2071
  raycaster,
@@ -2075,7 +2076,7 @@ function Portal({
2075
2076
  // Events, size and viewport can be overridden by the inject layer
2076
2077
  events: {
2077
2078
  ...rootState.events,
2078
- ...(injectState == null ? void 0 : injectState.events),
2079
+ ...injectState.events,
2079
2080
  ...events
2080
2081
  },
2081
2082
  size: {
@@ -2086,53 +2087,33 @@ function Portal({
2086
2087
  ...rootState.viewport,
2087
2088
  ...viewport
2088
2089
  },
2089
- ...rest
2090
- };
2091
- }, [state]);
2092
- const [usePortalStore] = React.useState(() => {
2093
- // Create a mirrored store, based on the previous root with a few overrides ...
2094
- const previousState = previousRoot.getState();
2095
- const store = create((set, get) => ({
2096
- ...previousState,
2097
- scene: container,
2098
- raycaster,
2099
- pointer,
2100
- mouse: pointer,
2101
- previousRoot,
2102
- events: {
2103
- ...previousState.events,
2104
- ...events
2105
- },
2106
- size: {
2107
- ...previousState.size,
2108
- ...size
2109
- },
2110
- ...rest,
2111
- // Set and get refer to this root-state
2112
- set,
2113
- get,
2114
2090
  // Layers are allowed to override events
2115
- setEvents: events => set(state => ({
2091
+ setEvents: events => injectState.set(state => ({
2116
2092
  ...state,
2117
2093
  events: {
2118
2094
  ...state.events,
2119
2095
  ...events
2120
2096
  }
2121
2097
  }))
2122
- }));
2123
- return store;
2098
+ };
2124
2099
  });
2125
- React.useEffect(() => {
2100
+ const usePortalStore = React.useMemo(() => {
2101
+ const store = create((set, get) => ({
2102
+ ...rest,
2103
+ set,
2104
+ get
2105
+ }));
2106
+
2126
2107
  // Subscribe to previous root-state and copy changes over to the mirrored portal-state
2127
- const unsub = previousRoot.subscribe(prev => usePortalStore.setState(state => inject(prev, state)));
2128
- return () => {
2129
- unsub();
2130
- usePortalStore.destroy();
2131
- };
2132
- }, []);
2108
+ const onMutate = prev => store.setState(state => inject.current(prev, state));
2109
+ onMutate(previousRoot.getState());
2110
+ previousRoot.subscribe(onMutate);
2111
+ return store;
2112
+ // eslint-disable-next-line react-hooks/exhaustive-deps
2113
+ }, [previousRoot, container]);
2133
2114
  React.useEffect(() => {
2134
- usePortalStore.setState(injectState => inject(previousRoot.getState(), injectState));
2135
- }, [inject]);
2115
+ return () => usePortalStore.destroy();
2116
+ }, [usePortalStore]);
2136
2117
  return /*#__PURE__*/React.createElement(React.Fragment, null, reconciler.createPortal( /*#__PURE__*/React.createElement(context.Provider, {
2137
2118
  value: usePortalStore
2138
2119
  }, children), usePortalStore, null));
@@ -177,13 +177,7 @@ function createRenderer(_roots, _getEventPriority) {
177
177
  }
178
178
 
179
179
  // Remove references
180
- if (child.__r3f) {
181
- delete child.__r3f.root;
182
- delete child.__r3f.objects;
183
- delete child.__r3f.handlers;
184
- delete child.__r3f.memoizedProps;
185
- if (!isPrimitive) delete child.__r3f;
186
- }
180
+ delete child.__r3f;
187
181
 
188
182
  // Dispose item whenever the reconciler feels like it
189
183
  if (shouldDispose && child.dispose && child.type !== 'Scene') {
@@ -377,6 +371,10 @@ function createRenderer(_roots, _getEventPriority) {
377
371
  }
378
372
 
379
373
  var _window$document, _window$navigator;
374
+ /**
375
+ * Returns `true` with correct TS type inference if an object has a configurable color space (since r152).
376
+ */
377
+ const hasColorSpace = object => 'colorSpace' in object || 'outputColorSpace' in object;
380
378
  /**
381
379
  * The current THREE.ColorManagement instance, if present.
382
380
  */
@@ -662,6 +660,21 @@ function applyProps$1(instance, data) {
662
660
  if (instance.__r3f) instance.__r3f.memoizedProps = memoized;
663
661
  for (let i = 0; i < changes.length; i++) {
664
662
  let [key, value, isEvent, keys] = changes[i];
663
+
664
+ // Alias (output)encoding => (output)colorSpace (since r152)
665
+ // https://github.com/pmndrs/react-three-fiber/pull/2829
666
+ if (hasColorSpace(instance)) {
667
+ const sRGBEncoding = 3001;
668
+ const SRGBColorSpace = 'srgb';
669
+ const LinearSRGBColorSpace = 'srgb-linear';
670
+ if (key === 'encoding') {
671
+ key = 'colorSpace';
672
+ value = value === sRGBEncoding ? SRGBColorSpace : LinearSRGBColorSpace;
673
+ } else if (key === 'outputEncoding') {
674
+ key = 'outputColorSpace';
675
+ value = value === sRGBEncoding ? SRGBColorSpace : LinearSRGBColorSpace;
676
+ }
677
+ }
665
678
  let currentInstance = instance;
666
679
  let targetProp = currentInstance[key];
667
680
 
@@ -730,12 +743,14 @@ function applyProps$1(instance, data) {
730
743
  // Else, just overwrite the value
731
744
  } else {
732
745
  currentInstance[key] = value;
746
+
733
747
  // Auto-convert sRGB textures, for now ...
734
748
  // https://github.com/pmndrs/react-three-fiber/issues/344
735
- if (!rootState.linear && currentInstance[key] instanceof THREE__namespace.Texture &&
749
+ if (currentInstance[key] instanceof THREE__namespace.Texture &&
736
750
  // sRGB textures must be RGBA8 since r137 https://github.com/mrdoob/three.js/pull/23129
737
751
  currentInstance[key].format === THREE__namespace.RGBAFormat && currentInstance[key].type === THREE__namespace.UnsignedByteType) {
738
- currentInstance[key].encoding = THREE__namespace.sRGBEncoding;
752
+ const texture = currentInstance[key];
753
+ if (hasColorSpace(texture) && hasColorSpace(rootState.gl)) texture.colorSpace = rootState.gl.outputColorSpace;else texture.encoding = rootState.gl.outputEncoding;
739
754
  }
740
755
  }
741
756
  invalidateInstance(instance);
@@ -1212,8 +1227,6 @@ function createEvents(store) {
1212
1227
  };
1213
1228
  }
1214
1229
 
1215
- // Keys that shouldn't be copied between R3F stores
1216
- const privateKeys = ['set', 'get', 'setSize', 'setFrameloop', 'setDpr', 'events', 'invalidate', 'advance', 'size', 'viewport'];
1217
1230
  const isRenderer = def => !!(def != null && def.render);
1218
1231
  const context = /*#__PURE__*/React__namespace.createContext(null);
1219
1232
  const createStore = (invalidate, advance) => {
@@ -1915,10 +1928,14 @@ function createRoot(canvas) {
1915
1928
  if (ColorManagement) {
1916
1929
  if ('enabled' in ColorManagement) ColorManagement.enabled = !legacy;else if ('legacyMode' in ColorManagement) ColorManagement.legacyMode = legacy;
1917
1930
  }
1918
- const outputEncoding = linear ? THREE__namespace.LinearEncoding : THREE__namespace.sRGBEncoding;
1919
- const toneMapping = flat ? THREE__namespace.NoToneMapping : THREE__namespace.ACESFilmicToneMapping;
1920
- if (gl.outputEncoding !== outputEncoding) gl.outputEncoding = outputEncoding;
1921
- if (gl.toneMapping !== toneMapping) gl.toneMapping = toneMapping;
1931
+
1932
+ // Set color space and tonemapping preferences
1933
+ const LinearEncoding = 3000;
1934
+ const sRGBEncoding = 3001;
1935
+ applyProps(gl, {
1936
+ outputEncoding: linear ? LinearEncoding : sRGBEncoding,
1937
+ toneMapping: flat ? THREE__namespace.NoToneMapping : THREE__namespace.ACESFilmicToneMapping
1938
+ });
1922
1939
 
1923
1940
  // Update color management state
1924
1941
  if (state.legacy !== legacy) state.set(() => ({
@@ -2039,7 +2056,6 @@ function unmountComponentAtNode(canvas, callback) {
2039
2056
  }
2040
2057
  function createPortal(children, container, state) {
2041
2058
  return /*#__PURE__*/React__namespace.createElement(Portal, {
2042
- key: container.uuid,
2043
2059
  children: children,
2044
2060
  container: container,
2045
2061
  state: state
@@ -2063,26 +2079,9 @@ function Portal({
2063
2079
  const previousRoot = useStore();
2064
2080
  const [raycaster] = React__namespace.useState(() => new THREE__namespace.Raycaster());
2065
2081
  const [pointer] = React__namespace.useState(() => new THREE__namespace.Vector2());
2066
- const inject = React__namespace.useCallback((rootState, injectState) => {
2067
- const intersect = {
2068
- ...rootState
2069
- }; // all prev state props
2070
-
2071
- // Only the fields of "rootState" that do not differ from injectState
2072
- // Some props should be off-limits
2073
- // Otherwise filter out the props that are different and let the inject layer take precedence
2074
- Object.keys(rootState).forEach(key => {
2075
- if (
2076
- // Some props should be off-limits
2077
- privateKeys.includes(key) ||
2078
- // Otherwise filter out the props that are different and let the inject layer take precedence
2079
- // Unless the inject layer props is undefined, then we keep the root layer
2080
- rootState[key] !== injectState[key] && injectState[key]) {
2081
- delete intersect[key];
2082
- }
2083
- });
2084
- let viewport = undefined;
2085
- if (injectState && size) {
2082
+ const inject = useMutableCallback((rootState, injectState) => {
2083
+ let viewport;
2084
+ if (injectState.camera && size) {
2086
2085
  const camera = injectState.camera;
2087
2086
  // Calculate the override viewport, if present
2088
2087
  viewport = rootState.viewport.getCurrentViewport(camera, new THREE__namespace.Vector3(), size);
@@ -2091,7 +2090,9 @@ function Portal({
2091
2090
  }
2092
2091
  return {
2093
2092
  // The intersect consists of the previous root state
2094
- ...intersect,
2093
+ ...rootState,
2094
+ get: injectState.get,
2095
+ set: injectState.set,
2095
2096
  // Portals have their own scene, which forms the root, a raycaster and a pointer
2096
2097
  scene: container,
2097
2098
  raycaster,
@@ -2102,7 +2103,7 @@ function Portal({
2102
2103
  // Events, size and viewport can be overridden by the inject layer
2103
2104
  events: {
2104
2105
  ...rootState.events,
2105
- ...(injectState == null ? void 0 : injectState.events),
2106
+ ...injectState.events,
2106
2107
  ...events
2107
2108
  },
2108
2109
  size: {
@@ -2113,53 +2114,33 @@ function Portal({
2113
2114
  ...rootState.viewport,
2114
2115
  ...viewport
2115
2116
  },
2116
- ...rest
2117
- };
2118
- }, [state]);
2119
- const [usePortalStore] = React__namespace.useState(() => {
2120
- // Create a mirrored store, based on the previous root with a few overrides ...
2121
- const previousState = previousRoot.getState();
2122
- const store = create__default["default"]((set, get) => ({
2123
- ...previousState,
2124
- scene: container,
2125
- raycaster,
2126
- pointer,
2127
- mouse: pointer,
2128
- previousRoot,
2129
- events: {
2130
- ...previousState.events,
2131
- ...events
2132
- },
2133
- size: {
2134
- ...previousState.size,
2135
- ...size
2136
- },
2137
- ...rest,
2138
- // Set and get refer to this root-state
2139
- set,
2140
- get,
2141
2117
  // Layers are allowed to override events
2142
- setEvents: events => set(state => ({
2118
+ setEvents: events => injectState.set(state => ({
2143
2119
  ...state,
2144
2120
  events: {
2145
2121
  ...state.events,
2146
2122
  ...events
2147
2123
  }
2148
2124
  }))
2149
- }));
2150
- return store;
2125
+ };
2151
2126
  });
2152
- React__namespace.useEffect(() => {
2127
+ const usePortalStore = React__namespace.useMemo(() => {
2128
+ const store = create__default["default"]((set, get) => ({
2129
+ ...rest,
2130
+ set,
2131
+ get
2132
+ }));
2133
+
2153
2134
  // Subscribe to previous root-state and copy changes over to the mirrored portal-state
2154
- const unsub = previousRoot.subscribe(prev => usePortalStore.setState(state => inject(prev, state)));
2155
- return () => {
2156
- unsub();
2157
- usePortalStore.destroy();
2158
- };
2159
- }, []);
2135
+ const onMutate = prev => store.setState(state => inject.current(prev, state));
2136
+ onMutate(previousRoot.getState());
2137
+ previousRoot.subscribe(onMutate);
2138
+ return store;
2139
+ // eslint-disable-next-line react-hooks/exhaustive-deps
2140
+ }, [previousRoot, container]);
2160
2141
  React__namespace.useEffect(() => {
2161
- usePortalStore.setState(injectState => inject(previousRoot.getState(), injectState));
2162
- }, [inject]);
2142
+ return () => usePortalStore.destroy();
2143
+ }, [usePortalStore]);
2163
2144
  return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, reconciler.createPortal( /*#__PURE__*/React__namespace.createElement(context.Provider, {
2164
2145
  value: usePortalStore
2165
2146
  }, children), usePortalStore, null));