@react-three/fiber 10.0.0-canary.604355a → 10.0.0-canary.9806389

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/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as webgpu from 'three/webgpu';
2
- import { RenderTarget, CubeReflectionMapping, EquirectangularReflectionMapping, CubeTextureLoader, Scene, WebGLCubeRenderTarget, HalfFloatType, Color, Frustum, Matrix4, Group, BoxGeometry, MeshBasicNodeMaterial, Mesh, Node, NodeUpdateType, Layers, SRGBColorSpace, RGBAFormat, UnsignedByteType, Vector3, Vector2, TextureLoader, Texture as Texture$1, CanvasTarget, Raycaster, OrthographicCamera, PerspectiveCamera, PCFSoftShadowMap, VSMShadowMap, PCFShadowMap, BasicShadowMap, ACESFilmicToneMapping, WebGPURenderer } from 'three/webgpu';
2
+ import { RenderTarget, CubeReflectionMapping, EquirectangularReflectionMapping, CubeTextureLoader, Scene, WebGLCubeRenderTarget, HalfFloatType, Color, Frustum, Matrix4, Group, BoxGeometry, MeshBasicNodeMaterial, Mesh, Node, NodeUpdateType, Layers, SRGBColorSpace, RGBAFormat, UnsignedByteType, Vector3, Vector2, TextureLoader, Texture as Texture$1, CanvasTarget, Raycaster, OrthographicCamera, PerspectiveCamera, PCFShadowMap, VSMShadowMap, BasicShadowMap, ACESFilmicToneMapping, WebGPURenderer } from 'three/webgpu';
3
3
  import { WebGLRenderTarget, WebGLRenderer } from 'three';
4
4
  import { Inspector } from 'three/addons/inspector/Inspector.js';
5
5
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
@@ -139,7 +139,7 @@ function useEnvironment({
139
139
  useLoader$1.clear(loader, multiFile ? [files] : files);
140
140
  }
141
141
  renderer.domElement.addEventListener("webglcontextlost", clearGainmapTexture, { once: true });
142
- }, [files, renderer.domElement]);
142
+ }, [extension, files, loader, multiFile, renderer.domElement]);
143
143
  const loaderResult = useLoader$1(
144
144
  loader,
145
145
  multiFile ? [files] : files,
@@ -339,7 +339,22 @@ function EnvironmentPortal({
339
339
  environmentIntensity,
340
340
  environmentRotation
341
341
  });
342
- }, [children, virtualScene, fbo.texture, scene, defaultScene, background, frames, gl]);
342
+ }, [
343
+ children,
344
+ virtualScene,
345
+ fbo.texture,
346
+ scene,
347
+ defaultScene,
348
+ background,
349
+ frames,
350
+ gl,
351
+ blur,
352
+ backgroundBlurriness,
353
+ backgroundIntensity,
354
+ backgroundRotation,
355
+ environmentIntensity,
356
+ environmentRotation
357
+ ]);
343
358
  let count = 1;
344
359
  useFrame$1(() => {
345
360
  if (frames === Infinity || count < frames) {
@@ -975,6 +990,9 @@ function applyProps(object, props) {
975
990
  else target.set(value);
976
991
  } else {
977
992
  root[key] = value;
993
+ if (key.endsWith("Node") && root.isMaterial) {
994
+ root.needsUpdate = true;
995
+ }
978
996
  if (rootState && rootState.renderer?.outputColorSpace === SRGBColorSpace && colorMaps.includes(key) && isTexture(value) && root[key]?.isTexture && // sRGB textures must be RGBA8 since r137 https://github.com/mrdoob/three.js/pull/23129
979
997
  root[key].format === RGBAFormat && root[key].type === UnsignedByteType) {
980
998
  root[key].colorSpace = rootState.textureColorSpace;
@@ -1008,38 +1026,60 @@ function applyProps(object, props) {
1008
1026
  return object;
1009
1027
  }
1010
1028
 
1029
+ const DEFAULT_POINTER_ID = 0;
1030
+ const XR_POINTER_ID_START = 1e3;
1031
+ function getPointerState(internal, pointerId) {
1032
+ let state = internal.pointerMap.get(pointerId);
1033
+ if (!state) {
1034
+ state = {
1035
+ hovered: /* @__PURE__ */ new Map(),
1036
+ captured: /* @__PURE__ */ new Map(),
1037
+ initialClick: [0, 0],
1038
+ initialHits: []
1039
+ };
1040
+ internal.pointerMap.set(pointerId, state);
1041
+ }
1042
+ return state;
1043
+ }
1044
+ function getPointerId(event) {
1045
+ return "pointerId" in event ? event.pointerId : DEFAULT_POINTER_ID;
1046
+ }
1011
1047
  function makeId(event) {
1012
1048
  return (event.eventObject || event.object).uuid + "/" + event.index + event.instanceId;
1013
1049
  }
1014
- function releaseInternalPointerCapture(capturedMap, obj, captures, pointerId) {
1015
- const captureData = captures.get(obj);
1050
+ function releaseInternalPointerCapture(internal, obj, pointerId) {
1051
+ const pointerState = internal.pointerMap.get(pointerId);
1052
+ if (!pointerState) return;
1053
+ const captureData = pointerState.captured.get(obj);
1016
1054
  if (captureData) {
1017
- captures.delete(obj);
1018
- if (captures.size === 0) {
1019
- capturedMap.delete(pointerId);
1020
- captureData.target.releasePointerCapture(pointerId);
1021
- }
1055
+ pointerState.captured.delete(obj);
1056
+ captureData.target.releasePointerCapture(pointerId);
1022
1057
  }
1023
1058
  }
1024
1059
  function removeInteractivity(store, object) {
1025
1060
  const { internal } = store.getState();
1026
1061
  internal.interaction = internal.interaction.filter((o) => o !== object);
1027
- internal.initialHits = internal.initialHits.filter((o) => o !== object);
1028
- internal.hovered.forEach((value, key) => {
1029
- if (value.eventObject === object || value.object === object) {
1030
- internal.hovered.delete(key);
1062
+ for (const [pointerId, pointerState] of internal.pointerMap) {
1063
+ pointerState.initialHits = pointerState.initialHits.filter((o) => o !== object);
1064
+ pointerState.hovered.forEach((value, key) => {
1065
+ if (value.eventObject === object || value.object === object) {
1066
+ pointerState.hovered.delete(key);
1067
+ }
1068
+ });
1069
+ if (pointerState.captured.has(object)) {
1070
+ releaseInternalPointerCapture(internal, object, pointerId);
1031
1071
  }
1032
- });
1033
- internal.capturedMap.forEach((captures, pointerId) => {
1034
- releaseInternalPointerCapture(internal.capturedMap, object, captures, pointerId);
1035
- });
1072
+ }
1036
1073
  unregisterVisibility(store, object);
1037
1074
  }
1038
1075
  function createEvents(store) {
1039
- function calculateDistance(event) {
1076
+ function calculateDistance(event, pointerId) {
1040
1077
  const { internal } = store.getState();
1041
- const dx = event.offsetX - internal.initialClick[0];
1042
- const dy = event.offsetY - internal.initialClick[1];
1078
+ const pointerState = internal.pointerMap.get(pointerId);
1079
+ if (!pointerState) return 0;
1080
+ const [initialX, initialY] = pointerState.initialClick;
1081
+ const dx = event.offsetX - initialX;
1082
+ const dy = event.offsetY - initialY;
1043
1083
  return Math.round(Math.sqrt(dx * dx + dy * dy));
1044
1084
  }
1045
1085
  function filterPointerEvents(objects) {
@@ -1075,6 +1115,15 @@ function createEvents(store) {
1075
1115
  return state2.raycaster.camera ? state2.raycaster.intersectObject(obj, true) : [];
1076
1116
  }
1077
1117
  let hits = eventsObjects.flatMap(handleRaycast).sort((a, b) => {
1118
+ const aInteractivePriority = a.object.userData?.interactivePriority;
1119
+ const bInteractivePriority = b.object.userData?.interactivePriority;
1120
+ if (aInteractivePriority !== void 0 || bInteractivePriority !== void 0) {
1121
+ if (aInteractivePriority !== void 0 && bInteractivePriority === void 0) return -1;
1122
+ if (bInteractivePriority !== void 0 && aInteractivePriority === void 0) return 1;
1123
+ if (aInteractivePriority !== bInteractivePriority) {
1124
+ return (bInteractivePriority ?? 0) - (aInteractivePriority ?? 0);
1125
+ }
1126
+ }
1078
1127
  const aState = getRootState(a.object);
1079
1128
  const bState = getRootState(b.object);
1080
1129
  const aPriority = aState?.events?.priority ?? 1;
@@ -1096,9 +1145,13 @@ function createEvents(store) {
1096
1145
  eventObject = eventObject.parent;
1097
1146
  }
1098
1147
  }
1099
- if ("pointerId" in event && state.internal.capturedMap.has(event.pointerId)) {
1100
- for (const captureData of state.internal.capturedMap.get(event.pointerId).values()) {
1101
- if (!duplicates.has(makeId(captureData.intersection))) intersections.push(captureData.intersection);
1148
+ if ("pointerId" in event) {
1149
+ const pointerId = event.pointerId;
1150
+ const pointerState = state.internal.pointerMap.get(pointerId);
1151
+ if (pointerState?.captured.size) {
1152
+ for (const captureData of pointerState.captured.values()) {
1153
+ if (!duplicates.has(makeId(captureData.intersection))) intersections.push(captureData.intersection);
1154
+ }
1102
1155
  }
1103
1156
  }
1104
1157
  return intersections;
@@ -1111,27 +1164,25 @@ function createEvents(store) {
1111
1164
  if (state) {
1112
1165
  const { raycaster, pointer, camera, internal } = state;
1113
1166
  const unprojectedPoint = new Vector3(pointer.x, pointer.y, 0).unproject(camera);
1114
- const hasPointerCapture = (id) => internal.capturedMap.get(id)?.has(hit.eventObject) ?? false;
1167
+ const hasPointerCapture = (id) => {
1168
+ const pointerState = internal.pointerMap.get(id);
1169
+ return pointerState?.captured.has(hit.eventObject) ?? false;
1170
+ };
1115
1171
  const setPointerCapture = (id) => {
1116
1172
  const captureData = { intersection: hit, target: event.target };
1117
- if (internal.capturedMap.has(id)) {
1118
- internal.capturedMap.get(id).set(hit.eventObject, captureData);
1119
- } else {
1120
- internal.capturedMap.set(id, /* @__PURE__ */ new Map([[hit.eventObject, captureData]]));
1121
- }
1173
+ const pointerState = getPointerState(internal, id);
1174
+ pointerState.captured.set(hit.eventObject, captureData);
1122
1175
  event.target.setPointerCapture(id);
1123
1176
  };
1124
1177
  const releasePointerCapture = (id) => {
1125
- const captures = internal.capturedMap.get(id);
1126
- if (captures) {
1127
- releaseInternalPointerCapture(internal.capturedMap, hit.eventObject, captures, id);
1128
- }
1178
+ releaseInternalPointerCapture(internal, hit.eventObject, id);
1129
1179
  };
1130
1180
  const extractEventProps = {};
1131
1181
  for (const prop in event) {
1132
1182
  const property = event[prop];
1133
1183
  if (typeof property !== "function") extractEventProps[prop] = property;
1134
1184
  }
1185
+ const eventPointerId = "pointerId" in event ? event.pointerId : void 0;
1135
1186
  const raycastEvent = {
1136
1187
  ...hit,
1137
1188
  ...extractEventProps,
@@ -1142,18 +1193,19 @@ function createEvents(store) {
1142
1193
  unprojectedPoint,
1143
1194
  ray: raycaster.ray,
1144
1195
  camera,
1196
+ pointerId: eventPointerId,
1145
1197
  // Hijack stopPropagation, which just sets a flag
1146
1198
  stopPropagation() {
1147
- const capturesForPointer = "pointerId" in event && internal.capturedMap.get(event.pointerId);
1199
+ const pointerState = eventPointerId !== void 0 ? internal.pointerMap.get(eventPointerId) : void 0;
1148
1200
  if (
1149
1201
  // ...if this pointer hasn't been captured
1150
- !capturesForPointer || // ... or if the hit object is capturing the pointer
1151
- capturesForPointer.has(hit.eventObject)
1202
+ !pointerState?.captured.size || // ... or if the hit object is capturing the pointer
1203
+ pointerState.captured.has(hit.eventObject)
1152
1204
  ) {
1153
1205
  raycastEvent.stopped = localState.stopped = true;
1154
- if (internal.hovered.size && Array.from(internal.hovered.values()).find((i) => i.eventObject === hit.eventObject)) {
1206
+ if (pointerState?.hovered.size && Array.from(pointerState.hovered.values()).find((i) => i.eventObject === hit.eventObject)) {
1155
1207
  const higher = intersections.slice(0, intersections.indexOf(hit));
1156
- cancelPointer([...higher, hit]);
1208
+ cancelPointer([...higher, hit], eventPointerId);
1157
1209
  }
1158
1210
  }
1159
1211
  },
@@ -1169,15 +1221,18 @@ function createEvents(store) {
1169
1221
  }
1170
1222
  return intersections;
1171
1223
  }
1172
- function cancelPointer(intersections) {
1224
+ function cancelPointer(intersections, pointerId) {
1173
1225
  const { internal } = store.getState();
1174
- for (const hoveredObj of internal.hovered.values()) {
1226
+ const pid = pointerId ?? DEFAULT_POINTER_ID;
1227
+ const pointerState = internal.pointerMap.get(pid);
1228
+ if (!pointerState) return;
1229
+ for (const [hoveredId, hoveredObj] of pointerState.hovered) {
1175
1230
  if (!intersections.length || !intersections.find(
1176
1231
  (hit) => hit.object === hoveredObj.object && hit.index === hoveredObj.index && hit.instanceId === hoveredObj.instanceId
1177
1232
  )) {
1178
1233
  const eventObject = hoveredObj.eventObject;
1179
1234
  const instance = eventObject.__r3f;
1180
- internal.hovered.delete(makeId(hoveredObj));
1235
+ pointerState.hovered.delete(hoveredId);
1181
1236
  if (instance?.eventCount) {
1182
1237
  const handlers = instance.handlers;
1183
1238
  const data = { ...hoveredObj, intersections };
@@ -1206,41 +1261,118 @@ function createEvents(store) {
1206
1261
  instance?.handlers.onDropMissed?.(event);
1207
1262
  }
1208
1263
  }
1264
+ function cleanupPointer(pointerId) {
1265
+ const { internal } = store.getState();
1266
+ const pointerState = internal.pointerMap.get(pointerId);
1267
+ if (pointerState) {
1268
+ for (const [, hoveredObj] of pointerState.hovered) {
1269
+ const eventObject = hoveredObj.eventObject;
1270
+ const instance = eventObject.__r3f;
1271
+ if (instance?.eventCount) {
1272
+ const handlers = instance.handlers;
1273
+ const data = { ...hoveredObj, intersections: [] };
1274
+ handlers.onPointerOut?.(data);
1275
+ handlers.onPointerLeave?.(data);
1276
+ }
1277
+ }
1278
+ internal.pointerMap.delete(pointerId);
1279
+ }
1280
+ internal.pointerDirty.delete(pointerId);
1281
+ }
1282
+ function processDeferredPointer(event, pointerId) {
1283
+ const state = store.getState();
1284
+ const { internal } = state;
1285
+ if (!state.events.enabled) return;
1286
+ const filter = filterPointerEvents;
1287
+ const hits = intersect(event, filter);
1288
+ cancelPointer(hits, pointerId);
1289
+ function onIntersect(data) {
1290
+ const eventObject = data.eventObject;
1291
+ const instance = eventObject.__r3f;
1292
+ if (!instance?.eventCount) return;
1293
+ const handlers = instance.handlers;
1294
+ if (handlers.onPointerOver || handlers.onPointerEnter || handlers.onPointerOut || handlers.onPointerLeave) {
1295
+ const id = makeId(data);
1296
+ const pointerState = getPointerState(internal, pointerId);
1297
+ const hoveredItem = pointerState.hovered.get(id);
1298
+ if (!hoveredItem) {
1299
+ pointerState.hovered.set(id, data);
1300
+ handlers.onPointerOver?.(data);
1301
+ handlers.onPointerEnter?.(data);
1302
+ } else if (hoveredItem.stopped) {
1303
+ data.stopPropagation();
1304
+ }
1305
+ }
1306
+ handlers.onPointerMove?.(data);
1307
+ }
1308
+ handleIntersects(hits, event, 0, onIntersect);
1309
+ }
1209
1310
  function handlePointer(name) {
1210
1311
  switch (name) {
1211
1312
  case "onPointerLeave":
1212
- case "onPointerCancel":
1213
1313
  case "onDragLeave":
1214
1314
  return () => cancelPointer([]);
1315
+ // Global cancel of these events
1316
+ case "onPointerCancel":
1317
+ return (event) => {
1318
+ const pointerId = getPointerId(event);
1319
+ cleanupPointer(pointerId);
1320
+ };
1215
1321
  case "onLostPointerCapture":
1216
1322
  return (event) => {
1217
1323
  const { internal } = store.getState();
1218
- if ("pointerId" in event && internal.capturedMap.has(event.pointerId)) {
1324
+ const pointerId = getPointerId(event);
1325
+ const pointerState = internal.pointerMap.get(pointerId);
1326
+ if (pointerState?.captured.size) {
1219
1327
  requestAnimationFrame(() => {
1220
- if (internal.capturedMap.has(event.pointerId)) {
1221
- internal.capturedMap.delete(event.pointerId);
1222
- cancelPointer([]);
1328
+ const pointerState2 = internal.pointerMap.get(pointerId);
1329
+ if (pointerState2?.captured.size) {
1330
+ pointerState2.captured.clear();
1223
1331
  }
1332
+ cancelPointer([], pointerId);
1224
1333
  });
1225
1334
  }
1226
1335
  };
1227
1336
  }
1228
1337
  return function handleEvent(event) {
1229
1338
  const state = store.getState();
1230
- const { onPointerMissed, onDragOverMissed, onDropMissed, internal } = state;
1339
+ const { onPointerMissed, onDragOverMissed, onDropMissed, internal, events } = state;
1340
+ const pointerId = getPointerId(event);
1231
1341
  internal.lastEvent.current = event;
1232
- if (!state.events.enabled) return;
1342
+ if (!events.enabled) return;
1233
1343
  const isPointerMove = name === "onPointerMove";
1234
1344
  const isDragOver = name === "onDragOver";
1235
1345
  const isDrop = name === "onDrop";
1236
1346
  const isClickEvent = name === "onClick" || name === "onContextMenu" || name === "onDoubleClick";
1347
+ const isPointerDown = name === "onPointerDown";
1348
+ const isPointerUp = name === "onPointerUp";
1349
+ const isWheel = name === "onWheel";
1350
+ const canDeferRaycasts = events.frameTimedRaycasts && state.frameloop === "always";
1351
+ if (isPointerMove && canDeferRaycasts) {
1352
+ events.compute?.(event, state);
1353
+ internal.pointerDirty.set(pointerId, event);
1354
+ return;
1355
+ }
1356
+ if (isWheel && canDeferRaycasts && !events.alwaysFireOnScroll) {
1357
+ events.compute?.(event, state);
1358
+ internal.pointerDirty.set(pointerId, event);
1359
+ return;
1360
+ }
1361
+ if ((isClickEvent || isPointerDown || isPointerUp) && internal.pointerDirty.has(pointerId)) {
1362
+ const deferredEvent = internal.pointerDirty.get(pointerId);
1363
+ internal.pointerDirty.delete(pointerId);
1364
+ processDeferredPointer(deferredEvent, pointerId);
1365
+ }
1237
1366
  const filter = isPointerMove || isDragOver || isDrop ? filterPointerEvents : void 0;
1238
1367
  const hits = intersect(event, filter);
1239
- const delta = isClickEvent ? calculateDistance(event) : 0;
1240
- if (name === "onPointerDown") {
1241
- internal.initialClick = [event.offsetX, event.offsetY];
1242
- internal.initialHits = hits.map((hit) => hit.eventObject);
1243
- }
1368
+ const delta = isClickEvent ? calculateDistance(event, pointerId) : 0;
1369
+ if (isPointerDown) {
1370
+ const pointerState2 = getPointerState(internal, pointerId);
1371
+ pointerState2.initialClick = [event.offsetX, event.offsetY];
1372
+ pointerState2.initialHits = hits.map((hit) => hit.eventObject);
1373
+ }
1374
+ const pointerState = internal.pointerMap.get(pointerId);
1375
+ const initialHits = pointerState?.initialHits ?? [];
1244
1376
  if (isClickEvent && !hits.length) {
1245
1377
  if (delta <= 2) {
1246
1378
  pointerMissed(event, internal.interaction);
@@ -1255,7 +1387,9 @@ function createEvents(store) {
1255
1387
  dropMissed(event, internal.interaction);
1256
1388
  if (onDropMissed) onDropMissed(event);
1257
1389
  }
1258
- if (isPointerMove || isDragOver) cancelPointer(hits);
1390
+ if (isPointerMove || isDragOver) {
1391
+ cancelPointer(hits, pointerId);
1392
+ }
1259
1393
  function onIntersect(data) {
1260
1394
  const eventObject = data.eventObject;
1261
1395
  const instance = eventObject.__r3f;
@@ -1264,9 +1398,10 @@ function createEvents(store) {
1264
1398
  if (isPointerMove) {
1265
1399
  if (handlers.onPointerOver || handlers.onPointerEnter || handlers.onPointerOut || handlers.onPointerLeave) {
1266
1400
  const id = makeId(data);
1267
- const hoveredItem = internal.hovered.get(id);
1401
+ const pointerState2 = getPointerState(internal, pointerId);
1402
+ const hoveredItem = pointerState2.hovered.get(id);
1268
1403
  if (!hoveredItem) {
1269
- internal.hovered.set(id, data);
1404
+ pointerState2.hovered.set(id, data);
1270
1405
  handlers.onPointerOver?.(data);
1271
1406
  handlers.onPointerEnter?.(data);
1272
1407
  } else if (hoveredItem.stopped) {
@@ -1276,9 +1411,10 @@ function createEvents(store) {
1276
1411
  handlers.onPointerMove?.(data);
1277
1412
  } else if (isDragOver) {
1278
1413
  const id = makeId(data);
1279
- const hoveredItem = internal.hovered.get(id);
1414
+ const pointerState2 = getPointerState(internal, pointerId);
1415
+ const hoveredItem = pointerState2.hovered.get(id);
1280
1416
  if (!hoveredItem) {
1281
- internal.hovered.set(id, data);
1417
+ pointerState2.hovered.set(id, data);
1282
1418
  handlers.onDragOverEnter?.(data);
1283
1419
  } else if (hoveredItem.stopped) {
1284
1420
  data.stopPropagation();
@@ -1289,18 +1425,18 @@ function createEvents(store) {
1289
1425
  } else {
1290
1426
  const handler = handlers[name];
1291
1427
  if (handler) {
1292
- if (!isClickEvent || internal.initialHits.includes(eventObject)) {
1428
+ if (!isClickEvent || initialHits.includes(eventObject)) {
1293
1429
  pointerMissed(
1294
1430
  event,
1295
- internal.interaction.filter((object) => !internal.initialHits.includes(object))
1431
+ internal.interaction.filter((object) => !initialHits.includes(object))
1296
1432
  );
1297
1433
  handler(data);
1298
1434
  }
1299
1435
  } else {
1300
- if (isClickEvent && internal.initialHits.includes(eventObject)) {
1436
+ if (isClickEvent && initialHits.includes(eventObject)) {
1301
1437
  pointerMissed(
1302
1438
  event,
1303
- internal.interaction.filter((object) => !internal.initialHits.includes(object))
1439
+ internal.interaction.filter((object) => !initialHits.includes(object))
1304
1440
  );
1305
1441
  }
1306
1442
  }
@@ -1309,7 +1445,15 @@ function createEvents(store) {
1309
1445
  handleIntersects(hits, event, delta, onIntersect);
1310
1446
  };
1311
1447
  }
1312
- return { handlePointer };
1448
+ function flushDeferredPointers() {
1449
+ const { internal, events } = store.getState();
1450
+ if (!events.frameTimedRaycasts) return;
1451
+ for (const [pointerId, event] of internal.pointerDirty) {
1452
+ processDeferredPointer(event, pointerId);
1453
+ }
1454
+ internal.pointerDirty.clear();
1455
+ }
1456
+ return { handlePointer, flushDeferredPointers, processDeferredPointer };
1313
1457
  }
1314
1458
  const DOM_EVENTS = {
1315
1459
  onClick: ["click", false],
@@ -1328,10 +1472,15 @@ const DOM_EVENTS = {
1328
1472
  onLostPointerCapture: ["lostpointercapture", true]
1329
1473
  };
1330
1474
  function createPointerEvents(store) {
1331
- const { handlePointer } = createEvents(store);
1475
+ const { handlePointer, flushDeferredPointers, processDeferredPointer } = createEvents(store);
1476
+ let nextXRPointerId = XR_POINTER_ID_START;
1477
+ const xrPointers = /* @__PURE__ */ new Map();
1332
1478
  return {
1333
1479
  priority: 1,
1334
1480
  enabled: true,
1481
+ frameTimedRaycasts: true,
1482
+ alwaysFireOnScroll: true,
1483
+ updateOnFrame: false,
1335
1484
  compute(event, state) {
1336
1485
  state.pointer.set(event.offsetX / state.size.width * 2 - 1, -(event.offsetY / state.size.height) * 2 + 1);
1337
1486
  state.raycaster.setFromCamera(state.pointer, state.camera);
@@ -1341,11 +1490,33 @@ function createPointerEvents(store) {
1341
1490
  (acc, key) => ({ ...acc, [key]: handlePointer(key) }),
1342
1491
  {}
1343
1492
  ),
1344
- update: () => {
1493
+ update: (pointerId) => {
1345
1494
  const { events, internal } = store.getState();
1346
- if (internal.lastEvent?.current && events.handlers) events.handlers.onPointerMove(internal.lastEvent.current);
1495
+ if (!events.handlers) return;
1496
+ if (pointerId !== void 0) {
1497
+ const event = internal.pointerDirty.get(pointerId);
1498
+ if (event) {
1499
+ internal.pointerDirty.delete(pointerId);
1500
+ processDeferredPointer(event, pointerId);
1501
+ } else if (internal.lastEvent?.current) {
1502
+ processDeferredPointer(internal.lastEvent.current, pointerId);
1503
+ }
1504
+ } else {
1505
+ flushDeferredPointers();
1506
+ if (internal.lastEvent?.current) {
1507
+ events.handlers.onPointerMove(internal.lastEvent.current);
1508
+ }
1509
+ }
1510
+ },
1511
+ flush: () => {
1512
+ const { events, internal } = store.getState();
1513
+ flushDeferredPointers();
1514
+ if (events.updateOnFrame && internal.lastEvent?.current && events.handlers) {
1515
+ events.handlers.onPointerMove(internal.lastEvent.current);
1516
+ }
1347
1517
  },
1348
1518
  connect: (target) => {
1519
+ if (!target) return;
1349
1520
  const { set, events } = store.getState();
1350
1521
  events.disconnect?.();
1351
1522
  set((state) => ({ events: { ...state.events, connected: target } }));
@@ -1369,6 +1540,32 @@ function createPointerEvents(store) {
1369
1540
  }
1370
1541
  set((state) => ({ events: { ...state.events, connected: void 0 } }));
1371
1542
  }
1543
+ },
1544
+ registerPointer: (config) => {
1545
+ const pointerId = nextXRPointerId++;
1546
+ xrPointers.set(pointerId, config);
1547
+ const { internal } = store.getState();
1548
+ getPointerState(internal, pointerId);
1549
+ return pointerId;
1550
+ },
1551
+ unregisterPointer: (pointerId) => {
1552
+ xrPointers.delete(pointerId);
1553
+ const { internal } = store.getState();
1554
+ const pointerState = internal.pointerMap.get(pointerId);
1555
+ if (pointerState) {
1556
+ for (const [, hoveredObj] of pointerState.hovered) {
1557
+ const eventObject = hoveredObj.eventObject;
1558
+ const instance = eventObject.__r3f;
1559
+ if (instance?.eventCount) {
1560
+ const handlers = instance.handlers;
1561
+ const data = { ...hoveredObj, intersections: [] };
1562
+ handlers.onPointerOut?.(data);
1563
+ handlers.onPointerLeave?.(data);
1564
+ }
1565
+ }
1566
+ internal.pointerMap.delete(pointerId);
1567
+ }
1568
+ internal.pointerDirty.delete(pointerId);
1372
1569
  }
1373
1570
  };
1374
1571
  }
@@ -1682,7 +1879,7 @@ function shouldRun(job, now) {
1682
1879
  const minInterval = 1e3 / job.fps;
1683
1880
  const lastRun = job.lastRun ?? 0;
1684
1881
  const elapsed = now - lastRun;
1685
- if (elapsed < minInterval) return false;
1882
+ if (elapsed < minInterval - 1) return false;
1686
1883
  if (job.drop) {
1687
1884
  job.lastRun = now;
1688
1885
  } else {
@@ -2499,7 +2696,14 @@ const createStore = (invalidate, advance) => {
2499
2696
  frustum: new Frustum(),
2500
2697
  autoUpdateFrustum: true,
2501
2698
  raycaster: null,
2502
- events: { priority: 1, enabled: true, connected: false },
2699
+ events: {
2700
+ priority: 1,
2701
+ enabled: true,
2702
+ connected: false,
2703
+ frameTimedRaycasts: true,
2704
+ alwaysFireOnScroll: true,
2705
+ updateOnFrame: false
2706
+ },
2503
2707
  scene: null,
2504
2708
  rootScene: null,
2505
2709
  xr: null,
@@ -2590,11 +2794,14 @@ const createStore = (invalidate, advance) => {
2590
2794
  },
2591
2795
  setError: (error) => set(() => ({ error })),
2592
2796
  error: null,
2593
- //* TSL State (managed via hooks: useUniforms, useNodes, useTextures, usePostProcessing) ==============================
2797
+ //* TSL State (managed via hooks: useUniforms, useNodes, useBuffers, useGPUStorage, useTextures, useRenderPipeline) ==============================
2594
2798
  uniforms: {},
2595
2799
  nodes: {},
2800
+ buffers: {},
2801
+ gpuStorage: {},
2596
2802
  textures: /* @__PURE__ */ new Map(),
2597
- postProcessing: null,
2803
+ _textureRefs: /* @__PURE__ */ new Map(),
2804
+ renderPipeline: null,
2598
2805
  passes: {},
2599
2806
  _hmrVersion: 0,
2600
2807
  _sizeImperative: false,
@@ -2603,12 +2810,16 @@ const createStore = (invalidate, advance) => {
2603
2810
  internal: {
2604
2811
  // Events
2605
2812
  interaction: [],
2606
- hovered: /* @__PURE__ */ new Map(),
2607
2813
  subscribers: [],
2814
+ // Per-pointer state (new unified structure)
2815
+ pointerMap: /* @__PURE__ */ new Map(),
2816
+ pointerDirty: /* @__PURE__ */ new Map(),
2817
+ lastEvent: React.createRef(),
2818
+ // Deprecated but kept for backwards compatibility
2819
+ hovered: /* @__PURE__ */ new Map(),
2608
2820
  initialClick: [0, 0],
2609
2821
  initialHits: [],
2610
2822
  capturedMap: /* @__PURE__ */ new Map(),
2611
- lastEvent: React.createRef(),
2612
2823
  // Visibility tracking (onFramed, onOccluded, onVisible)
2613
2824
  visibilityRegistry: /* @__PURE__ */ new Map(),
2614
2825
  // Occlusion system (WebGPU only)
@@ -2696,14 +2907,16 @@ const createStore = (invalidate, advance) => {
2696
2907
  oldSize = size;
2697
2908
  oldDpr = viewport.dpr;
2698
2909
  updateCamera(camera, size);
2699
- if (canvasTarget) {
2910
+ if (internal.isSecondary && canvasTarget) {
2700
2911
  if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
2701
- const updateStyle = typeof HTMLCanvasElement !== "undefined" && canvasTarget.domElement instanceof HTMLCanvasElement;
2702
- canvasTarget.setSize(size.width, size.height, updateStyle);
2912
+ canvasTarget.setSize(size.width, size.height, false);
2703
2913
  } else {
2704
2914
  if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
2705
- const updateStyle = typeof HTMLCanvasElement !== "undefined" && actualRenderer.domElement instanceof HTMLCanvasElement;
2706
- actualRenderer.setSize(size.width, size.height, updateStyle);
2915
+ actualRenderer.setSize(size.width, size.height, false);
2916
+ if (canvasTarget) {
2917
+ if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
2918
+ canvasTarget.setSize(size.width, size.height, false);
2919
+ }
2707
2920
  }
2708
2921
  }
2709
2922
  if (camera !== oldCamera) {
@@ -2951,18 +3164,18 @@ function buildFromCache(input, textureCache) {
2951
3164
  function useTexture(input, optionsOrOnLoad) {
2952
3165
  const renderer = useThree((state) => state.internal.actualRenderer);
2953
3166
  const store = useStore();
2954
- const textureCache = useThree((state) => state.textures);
2955
3167
  const options = typeof optionsOrOnLoad === "function" ? { onLoad: optionsOrOnLoad } : optionsOrOnLoad ?? {};
2956
- const { onLoad, cache = false } = options;
3168
+ const { onLoad, cache = true } = options;
2957
3169
  const onLoadRef = useRef(onLoad);
2958
3170
  onLoadRef.current = onLoad;
2959
3171
  const onLoadCalledForRef = useRef(null);
2960
3172
  const urls = useMemo(() => getUrls(input), [input]);
2961
3173
  const cachedResult = useMemo(() => {
2962
3174
  if (!cache) return null;
2963
- if (!allUrlsCached(urls, textureCache)) return null;
2964
- return buildFromCache(input, textureCache);
2965
- }, [cache, urls, textureCache, input]);
3175
+ const textures = store.getState().textures;
3176
+ if (!allUrlsCached(urls, textures)) return null;
3177
+ return buildFromCache(input, textures);
3178
+ }, [cache, urls, input, store]);
2966
3179
  const loadedTextures = useLoader(
2967
3180
  TextureLoader,
2968
3181
  IsObject(input) ? Object.values(input) : input
@@ -3006,8 +3219,6 @@ function useTexture(input, optionsOrOnLoad) {
3006
3219
  }, [input, loadedTextures, cachedResult]);
3007
3220
  useEffect(() => {
3008
3221
  if (!cache) return;
3009
- if (cachedResult) return;
3010
- const set = store.setState;
3011
3222
  const urlTextureMap = [];
3012
3223
  if (typeof input === "string") {
3013
3224
  urlTextureMap.push([input, mappedTextures]);
@@ -3021,18 +3232,32 @@ function useTexture(input, optionsOrOnLoad) {
3021
3232
  urlTextureMap.push([url, textureRecord[key]]);
3022
3233
  }
3023
3234
  }
3024
- set((state) => {
3025
- const newMap = new Map(state.textures);
3026
- let changed = false;
3235
+ store.setState((state) => {
3236
+ const refs = new Map(state._textureRefs);
3237
+ let textures = state.textures;
3238
+ let added = false;
3027
3239
  for (const [url, texture] of urlTextureMap) {
3028
- if (!newMap.has(url)) {
3029
- newMap.set(url, texture);
3030
- changed = true;
3240
+ if (!textures.has(url)) {
3241
+ if (!added) {
3242
+ textures = new Map(textures);
3243
+ added = true;
3244
+ }
3245
+ textures.set(url, texture);
3031
3246
  }
3247
+ refs.set(url, (refs.get(url) ?? 0) + 1);
3032
3248
  }
3033
- return changed ? { textures: newMap } : state;
3249
+ return added ? { textures, _textureRefs: refs } : { _textureRefs: refs };
3250
+ });
3251
+ return () => store.setState((state) => {
3252
+ const refs = new Map(state._textureRefs);
3253
+ for (const [url] of urlTextureMap) {
3254
+ const next = (refs.get(url) ?? 0) - 1;
3255
+ if (next <= 0) refs.delete(url);
3256
+ else refs.set(url, next);
3257
+ }
3258
+ return { _textureRefs: refs };
3034
3259
  });
3035
- }, [cache, input, mappedTextures, store, cachedResult]);
3260
+ }, [cache, input, mappedTextures, store]);
3036
3261
  return mappedTextures;
3037
3262
  }
3038
3263
  useTexture.preload = (url) => useLoader.preload(TextureLoader, url);
@@ -3048,96 +3273,63 @@ const Texture = ({
3048
3273
  return /* @__PURE__ */ jsx(Fragment, { children: children?.(ret) });
3049
3274
  };
3050
3275
 
3051
- function getTextureValue(entry) {
3052
- if (entry instanceof Texture$1) return entry;
3053
- if (entry && typeof entry === "object" && "value" in entry && entry.value instanceof Texture$1) {
3054
- return entry.value;
3055
- }
3056
- return null;
3057
- }
3058
- function useTextures() {
3276
+ function useTextures(selector) {
3059
3277
  const store = useStore();
3060
- return useMemo(() => {
3061
- const set = store.setState;
3278
+ const registry = useMemo(() => {
3062
3279
  const getState = store.getState;
3063
- const add = (key, value) => {
3064
- set((state) => {
3065
- const newMap = new Map(state.textures);
3066
- newMap.set(key, value);
3067
- return { textures: newMap };
3068
- });
3069
- };
3070
- const addMultiple = (items) => {
3071
- set((state) => {
3072
- const newMap = new Map(state.textures);
3073
- const entries = items instanceof Map ? items.entries() : Object.entries(items);
3074
- for (const [key, value] of entries) {
3075
- newMap.set(key, value);
3076
- }
3077
- return { textures: newMap };
3078
- });
3079
- };
3080
- const remove = (key) => {
3081
- set((state) => {
3082
- const newMap = new Map(state.textures);
3083
- newMap.delete(key);
3084
- return { textures: newMap };
3085
- });
3086
- };
3087
- const removeMultiple = (keys) => {
3088
- set((state) => {
3089
- const newMap = new Map(state.textures);
3090
- for (const key of keys) newMap.delete(key);
3091
- return { textures: newMap };
3092
- });
3093
- };
3094
- const dispose = (key) => {
3095
- const entry = getState().textures.get(key);
3096
- if (entry) {
3097
- const tex = getTextureValue(entry);
3098
- tex?.dispose();
3099
- }
3100
- remove(key);
3101
- };
3102
- const disposeMultiple = (keys) => {
3103
- const textures = getState().textures;
3104
- for (const key of keys) {
3105
- const entry = textures.get(key);
3106
- if (entry) {
3107
- const tex = getTextureValue(entry);
3108
- tex?.dispose();
3109
- }
3110
- }
3111
- removeMultiple(keys);
3112
- };
3113
- const disposeAll = () => {
3114
- const textures = getState().textures;
3115
- for (const entry of textures.values()) {
3116
- const tex = getTextureValue(entry);
3117
- tex?.dispose();
3118
- }
3119
- set({ textures: /* @__PURE__ */ new Map() });
3120
- };
3280
+ const setState = store.setState;
3281
+ const getOne = (key) => getState().textures.get(key);
3121
3282
  return {
3122
- // Getter for the textures Map (reactive via getState)
3123
- get textures() {
3283
+ get all() {
3124
3284
  return getState().textures;
3125
3285
  },
3126
- // Read
3127
- get: (key) => getState().textures.get(key),
3286
+ get(input) {
3287
+ if (typeof input === "string") return getOne(input);
3288
+ if (Array.isArray(input)) return input.map(getOne);
3289
+ const out = {};
3290
+ for (const name in input) out[name] = getOne(input[name]);
3291
+ return out;
3292
+ },
3128
3293
  has: (key) => getState().textures.has(key),
3129
- // Write
3130
- add,
3131
- addMultiple,
3132
- // Remove (cache only)
3133
- remove,
3134
- removeMultiple,
3135
- // Dispose (GPU + cache)
3136
- dispose,
3137
- disposeMultiple,
3138
- disposeAll
3294
+ add(keyOrRecord, texture) {
3295
+ setState((state) => {
3296
+ const textures = new Map(state.textures);
3297
+ if (typeof keyOrRecord === "string") {
3298
+ textures.set(keyOrRecord, texture);
3299
+ } else {
3300
+ for (const key in keyOrRecord) textures.set(key, keyOrRecord[key]);
3301
+ }
3302
+ return { textures };
3303
+ });
3304
+ },
3305
+ dispose(key, options) {
3306
+ const state = getState();
3307
+ const refs = state._textureRefs.get(key) ?? 0;
3308
+ if (refs > 0 && !options?.force) {
3309
+ console.warn(
3310
+ `[useTextures] "${key}" still has ${refs} active reference(s); skipping dispose. Pass { force: true } to override.`
3311
+ );
3312
+ return false;
3313
+ }
3314
+ state.textures.get(key)?.dispose();
3315
+ setState((s) => {
3316
+ const textures = new Map(s.textures);
3317
+ textures.delete(key);
3318
+ const nextRefs = new Map(s._textureRefs);
3319
+ nextRefs.delete(key);
3320
+ return { textures, _textureRefs: nextRefs };
3321
+ });
3322
+ return true;
3323
+ },
3324
+ disposeAll() {
3325
+ for (const texture of getState().textures.values()) texture.dispose();
3326
+ setState({ textures: /* @__PURE__ */ new Map(), _textureRefs: /* @__PURE__ */ new Map() });
3327
+ }
3139
3328
  };
3140
3329
  }, [store]);
3330
+ const subscribe = selector ? () => selector(registry) : (state) => state.textures;
3331
+ const selected = useThree(subscribe);
3332
+ return selector ? selected : registry;
3141
3333
  }
3142
3334
 
3143
3335
  function useRenderTarget(widthOrOptions, heightOrOptions, options) {
@@ -14990,7 +15182,6 @@ function createRoot(canvas) {
14990
15182
  events,
14991
15183
  onCreated: onCreatedCallback,
14992
15184
  shadows = false,
14993
- textureColorSpace = SRGBColorSpace,
14994
15185
  orthographic = false,
14995
15186
  frameloop = "always",
14996
15187
  dpr = [1, 2],
@@ -15005,6 +15196,7 @@ function createRoot(canvas) {
15005
15196
  _sizeProps,
15006
15197
  forceEven
15007
15198
  } = props;
15199
+ const textureColorSpace = is.obj(glConfig) && !is.fun(glConfig) && !isRenderer(glConfig) && glConfig.textureColorSpace || is.obj(rendererConfig) && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && rendererConfig.textureColorSpace || SRGBColorSpace;
15008
15200
  const state = store.getState();
15009
15201
  const defaultGLProps = {
15010
15202
  canvas,
@@ -15060,6 +15252,12 @@ function createRoot(canvas) {
15060
15252
  } else if (!wantsGL && !state.internal.actualRenderer) {
15061
15253
  renderer = await resolveRenderer(rendererConfig, defaultGPUProps, WebGPURenderer);
15062
15254
  if (!renderer.hasInitialized?.()) {
15255
+ const size2 = computeInitialSize(canvas, propsSize);
15256
+ if (size2.width > 0 && size2.height > 0) {
15257
+ const pixelRatio = calculateDpr(dpr);
15258
+ canvas.width = size2.width * pixelRatio;
15259
+ canvas.height = size2.height * pixelRatio;
15260
+ }
15063
15261
  await renderer.init();
15064
15262
  }
15065
15263
  const backend = renderer.backend;
@@ -15169,7 +15367,7 @@ function createRoot(canvas) {
15169
15367
  lastConfiguredProps.performance = performance;
15170
15368
  }
15171
15369
  if (!state.xr) {
15172
- const handleXRFrame = (timestamp, frame) => {
15370
+ const handleXRFrame = (timestamp, _frame) => {
15173
15371
  const state2 = store.getState();
15174
15372
  if (state2.frameloop === "never") return;
15175
15373
  advance(timestamp);
@@ -15205,15 +15403,22 @@ function createRoot(canvas) {
15205
15403
  const oldType = renderer.shadowMap.type;
15206
15404
  renderer.shadowMap.enabled = !!shadows;
15207
15405
  if (is.boo(shadows)) {
15208
- renderer.shadowMap.type = PCFSoftShadowMap;
15406
+ renderer.shadowMap.type = PCFShadowMap;
15209
15407
  } else if (is.str(shadows)) {
15408
+ if (shadows === "soft") {
15409
+ notifyDepreciated({
15410
+ heading: 'shadows="soft" is deprecated',
15411
+ body: "Three has depreciated soft and improved basic PCFShadows, we converted for you.",
15412
+ link: "https://github.com/mrdoob/three.js/wiki/Migration-Guide?utm_source=chatgpt.com#181--182"
15413
+ });
15414
+ }
15210
15415
  const types = {
15211
15416
  basic: BasicShadowMap,
15212
15417
  percentage: PCFShadowMap,
15213
- soft: PCFSoftShadowMap,
15418
+ soft: PCFShadowMap,
15214
15419
  variance: VSMShadowMap
15215
15420
  };
15216
- renderer.shadowMap.type = types[shadows] ?? PCFSoftShadowMap;
15421
+ renderer.shadowMap.type = types[shadows] ?? PCFShadowMap;
15217
15422
  } else if (is.obj(shadows)) {
15218
15423
  Object.assign(renderer.shadowMap, shadows);
15219
15424
  }
@@ -15229,13 +15434,24 @@ function createRoot(canvas) {
15229
15434
  if (state.textureColorSpace !== textureColorSpace) state.set(() => ({ textureColorSpace }));
15230
15435
  lastConfiguredProps.textureColorSpace = textureColorSpace;
15231
15436
  }
15437
+ const r3fProps = ["textureColorSpace"];
15438
+ const constructorOnlyProps = ["samples", "antialias", "alpha", "canvas", "powerPreference"];
15439
+ const nonApplyProps = [...r3fProps, ...constructorOnlyProps];
15232
15440
  if (glConfig && !is.fun(glConfig) && !isRenderer(glConfig) && !is.equ(glConfig, renderer, shallowLoose)) {
15233
- applyProps(renderer, glConfig);
15441
+ const glProps = {};
15442
+ for (const key in glConfig) {
15443
+ if (!nonApplyProps.includes(key)) glProps[key] = glConfig[key];
15444
+ }
15445
+ applyProps(renderer, glProps);
15234
15446
  }
15235
15447
  if (rendererConfig && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && state.renderer) {
15236
15448
  const currentRenderer = state.renderer;
15237
15449
  if (!is.equ(rendererConfig, currentRenderer, shallowLoose)) {
15238
- applyProps(currentRenderer, rendererConfig);
15450
+ const rendererProps = {};
15451
+ for (const key in rendererConfig) {
15452
+ if (!nonApplyProps.includes(key)) rendererProps[key] = rendererConfig[key];
15453
+ }
15454
+ applyProps(currentRenderer, rendererProps);
15239
15455
  }
15240
15456
  }
15241
15457
  const scheduler = getScheduler();
@@ -15261,6 +15477,18 @@ function createRoot(canvas) {
15261
15477
  system: true
15262
15478
  }
15263
15479
  );
15480
+ const unregisterEventsFlush = scheduler.register(
15481
+ () => {
15482
+ const state2 = store.getState();
15483
+ state2.events.flush?.();
15484
+ },
15485
+ {
15486
+ id: `${newRootId}_events`,
15487
+ rootId: newRootId,
15488
+ phase: "input",
15489
+ system: true
15490
+ }
15491
+ );
15264
15492
  const unregisterFrustum = scheduler.register(
15265
15493
  () => {
15266
15494
  const state2 = store.getState();
@@ -15271,7 +15499,7 @@ function createRoot(canvas) {
15271
15499
  {
15272
15500
  id: `${newRootId}_frustum`,
15273
15501
  rootId: newRootId,
15274
- phase: "preRender",
15502
+ before: "render",
15275
15503
  system: true
15276
15504
  }
15277
15505
  );
@@ -15283,7 +15511,7 @@ function createRoot(canvas) {
15283
15511
  {
15284
15512
  id: `${newRootId}_visibility`,
15285
15513
  rootId: newRootId,
15286
- phase: "preRender",
15514
+ before: "render",
15287
15515
  system: true,
15288
15516
  after: `${newRootId}_frustum`
15289
15517
  }
@@ -15295,7 +15523,7 @@ function createRoot(canvas) {
15295
15523
  const userHandlesRender = scheduler.hasUserJobsInPhase("render", newRootId);
15296
15524
  if (userHandlesRender || state2.internal.priority) return;
15297
15525
  try {
15298
- if (state2.postProcessing?.render) state2.postProcessing.render();
15526
+ if (state2.renderPipeline?.render) state2.renderPipeline.render();
15299
15527
  else if (renderer2?.render) renderer2.render(state2.scene, state2.camera);
15300
15528
  } catch (error) {
15301
15529
  state2.setError(error instanceof Error ? error : new Error(String(error)));
@@ -15320,6 +15548,7 @@ function createRoot(canvas) {
15320
15548
  unregisterRoot: () => {
15321
15549
  unregisterRoot();
15322
15550
  unregisterCanvasTarget();
15551
+ unregisterEventsFlush();
15323
15552
  unregisterFrustum();
15324
15553
  unregisterVisibility();
15325
15554
  unregisterRender();
@@ -15485,9 +15714,13 @@ function PortalInner({ state = {}, children, container }) {
15485
15714
  const store = createWithEqualityFn((set, get) => ({ ...rest, set, get }));
15486
15715
  const onMutate = (prev) => store.setState((state2) => inject.current(prev, state2));
15487
15716
  onMutate(previousRoot.getState());
15488
- previousRoot.subscribe(onMutate);
15489
15717
  return store;
15490
15718
  }, [previousRoot, container]);
15719
+ useIsomorphicLayoutEffect(() => {
15720
+ const onMutate = (prev) => usePortalStore.setState((state2) => inject.current(prev, state2));
15721
+ const unsubscribe = previousRoot.subscribe(onMutate);
15722
+ return unsubscribe;
15723
+ }, [previousRoot, usePortalStore]);
15491
15724
  return (
15492
15725
  // @ts-ignore, reconciler types are not maintained
15493
15726
  /* @__PURE__ */ jsx(Fragment, { children: reconciler.createPortal(
@@ -15501,6 +15734,46 @@ function flushSync(fn) {
15501
15734
  return reconciler.flushSyncFromReconciler(fn);
15502
15735
  }
15503
15736
 
15737
+ function parseBackground(background) {
15738
+ if (!background) return null;
15739
+ if (typeof background === "object" && !background.isColor) {
15740
+ const { backgroundMap, envMap, files, preset, ...rest } = background;
15741
+ return {
15742
+ ...rest,
15743
+ preset,
15744
+ files: envMap || files,
15745
+ backgroundFiles: backgroundMap,
15746
+ background: true
15747
+ };
15748
+ }
15749
+ if (typeof background === "number") {
15750
+ return { color: background, background: true };
15751
+ }
15752
+ if (typeof background === "string") {
15753
+ if (background in presetsObj) {
15754
+ return { preset: background, background: true };
15755
+ }
15756
+ if (/^(https?:\/\/|\/|\.\/|\.\.\/)|\.(hdr|exr|jpg|jpeg|png|webp|gif)$/i.test(background)) {
15757
+ return { files: background, background: true };
15758
+ }
15759
+ return { color: background, background: true };
15760
+ }
15761
+ if (background.isColor) {
15762
+ return { color: background, background: true };
15763
+ }
15764
+ return null;
15765
+ }
15766
+
15767
+ function clearHmrCaches(store) {
15768
+ store.setState((state) => ({
15769
+ nodes: {},
15770
+ uniforms: {},
15771
+ buffers: {},
15772
+ gpuStorage: {},
15773
+ _hmrVersion: state._hmrVersion + 1
15774
+ }));
15775
+ }
15776
+
15504
15777
  function CanvasImpl({
15505
15778
  ref,
15506
15779
  children,
@@ -15521,6 +15794,8 @@ function CanvasImpl({
15521
15794
  raycaster,
15522
15795
  camera,
15523
15796
  scene,
15797
+ autoUpdateFrustum,
15798
+ occlusion,
15524
15799
  onPointerMissed,
15525
15800
  onDragOverMissed,
15526
15801
  onDropMissed,
@@ -15532,39 +15807,21 @@ function CanvasImpl({
15532
15807
  forceEven,
15533
15808
  ...props
15534
15809
  }) {
15535
- const { primaryCanvas, scheduler, ...rendererConfig } = typeof rendererProp === "object" && rendererProp !== null && !("render" in rendererProp) && ("primaryCanvas" in rendererProp || "scheduler" in rendererProp) ? rendererProp : { primaryCanvas: void 0, scheduler: void 0 };
15536
- const renderer = Object.keys(rendererConfig).length > 0 ? rendererConfig : rendererProp;
15810
+ const isRendererConfig = typeof rendererProp === "object" && rendererProp !== null && !("render" in rendererProp) && ("primaryCanvas" in rendererProp || "scheduler" in rendererProp);
15811
+ let primaryCanvas;
15812
+ let scheduler;
15813
+ let renderer;
15814
+ if (isRendererConfig) {
15815
+ const { primaryCanvas: pc, scheduler: sc, ...rest } = rendererProp;
15816
+ primaryCanvas = pc;
15817
+ scheduler = sc;
15818
+ renderer = Object.keys(rest).length > 0 ? rest : rendererProp;
15819
+ } else {
15820
+ renderer = rendererProp;
15821
+ }
15537
15822
  React.useMemo(() => extend(THREE), []);
15538
15823
  const Bridge = useBridge();
15539
- const backgroundProps = React.useMemo(() => {
15540
- if (!background) return null;
15541
- if (typeof background === "object" && !background.isColor) {
15542
- const { backgroundMap, envMap, files, preset, ...rest } = background;
15543
- return {
15544
- ...rest,
15545
- preset,
15546
- files: envMap || files,
15547
- backgroundFiles: backgroundMap,
15548
- background: true
15549
- };
15550
- }
15551
- if (typeof background === "number") {
15552
- return { color: background, background: true };
15553
- }
15554
- if (typeof background === "string") {
15555
- if (background in presetsObj) {
15556
- return { preset: background, background: true };
15557
- }
15558
- if (/^(https?:\/\/|\/|\.\/|\.\.\/)|\\.(hdr|exr|jpg|jpeg|png|webp|gif)$/i.test(background)) {
15559
- return { files: background, background: true };
15560
- }
15561
- return { color: background, background: true };
15562
- }
15563
- if (background.isColor) {
15564
- return { color: background, background: true };
15565
- }
15566
- return null;
15567
- }, [background]);
15824
+ const backgroundProps = React.useMemo(() => parseBackground(background), [background]);
15568
15825
  const hasInitialSizeRef = React.useRef(false);
15569
15826
  const measureConfig = React.useMemo(() => {
15570
15827
  if (!hasInitialSizeRef.current) {
@@ -15648,6 +15905,8 @@ function CanvasImpl({
15648
15905
  performance,
15649
15906
  raycaster,
15650
15907
  camera,
15908
+ autoUpdateFrustum,
15909
+ occlusion,
15651
15910
  size: effectiveSize,
15652
15911
  // Store size props for reset functionality
15653
15912
  _sizeProps: width !== void 0 || height !== void 0 ? { width, height } : null,
@@ -15707,20 +15966,13 @@ function CanvasImpl({
15707
15966
  const handleHMR = () => {
15708
15967
  queueMicrotask(() => {
15709
15968
  const rootEntry = _roots.get(canvas);
15710
- if (rootEntry?.store) {
15711
- rootEntry.store.setState((state) => ({
15712
- nodes: {},
15713
- uniforms: {},
15714
- _hmrVersion: state._hmrVersion + 1
15715
- }));
15716
- }
15969
+ if (rootEntry?.store) clearHmrCaches(rootEntry.store);
15717
15970
  });
15718
15971
  };
15719
15972
  if (typeof import.meta !== "undefined" && import.meta.hot) {
15720
15973
  const hot = import.meta.hot;
15721
15974
  hot.on("vite:afterUpdate", handleHMR);
15722
- return () => hot.dispose?.(() => {
15723
- });
15975
+ return () => hot.off?.("vite:afterUpdate", handleHMR);
15724
15976
  }
15725
15977
  if (typeof module !== "undefined" && module.hot) {
15726
15978
  const hot = module.hot;
@@ -15743,7 +15995,16 @@ function CanvasImpl({
15743
15995
  ...style
15744
15996
  },
15745
15997
  ...props,
15746
- children: /* @__PURE__ */ jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsx("canvas", { ref: canvasRef, id, className: "r3f-canvas", style: { display: "block" }, children: fallback }) })
15998
+ children: /* @__PURE__ */ jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsx(
15999
+ "canvas",
16000
+ {
16001
+ ref: canvasRef,
16002
+ id,
16003
+ className: "r3f-canvas",
16004
+ style: { display: "block", width: "100%", height: "100%" },
16005
+ children: fallback
16006
+ }
16007
+ ) })
15747
16008
  }
15748
16009
  );
15749
16010
  }