@react-three/fiber 10.0.0-canary.b0fafc8 → 10.0.0-canary.c3fa45d

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/legacy.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as three from 'three';
2
- import { WebGLRenderTarget, 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, Raycaster, OrthographicCamera, PerspectiveCamera, PCFSoftShadowMap, VSMShadowMap, PCFShadowMap, BasicShadowMap, ACESFilmicToneMapping, WebGLRenderer } from 'three';
2
+ import { WebGLRenderTarget, 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, Raycaster, OrthographicCamera, PerspectiveCamera, PCFShadowMap, VSMShadowMap, BasicShadowMap, ACESFilmicToneMapping, WebGLRenderer } from 'three';
3
3
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
4
4
  import * as React from 'react';
5
5
  import React__default, { useLayoutEffect, useRef, useMemo, useEffect, useContext, useImperativeHandle, useCallback, useState } from 'react';
@@ -148,7 +148,7 @@ function useEnvironment({
148
148
  useLoader$1.clear(loader, multiFile ? [files] : files);
149
149
  }
150
150
  renderer.domElement.addEventListener("webglcontextlost", clearGainmapTexture, { once: true });
151
- }, [files, renderer.domElement]);
151
+ }, [extension, files, loader, multiFile, renderer.domElement]);
152
152
  const loaderResult = useLoader$1(
153
153
  loader,
154
154
  multiFile ? [files] : files,
@@ -348,7 +348,22 @@ function EnvironmentPortal({
348
348
  environmentIntensity,
349
349
  environmentRotation
350
350
  });
351
- }, [children, virtualScene, fbo.texture, scene, defaultScene, background, frames, gl]);
351
+ }, [
352
+ children,
353
+ virtualScene,
354
+ fbo.texture,
355
+ scene,
356
+ defaultScene,
357
+ background,
358
+ frames,
359
+ gl,
360
+ blur,
361
+ backgroundBlurriness,
362
+ backgroundIntensity,
363
+ backgroundRotation,
364
+ environmentIntensity,
365
+ environmentRotation
366
+ ]);
352
367
  let count = 1;
353
368
  useFrame$1(() => {
354
369
  if (frames === Infinity || count < frames) {
@@ -984,6 +999,9 @@ function applyProps(object, props) {
984
999
  else target.set(value);
985
1000
  } else {
986
1001
  root[key] = value;
1002
+ if (key.endsWith("Node") && root.isMaterial) {
1003
+ root.needsUpdate = true;
1004
+ }
987
1005
  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
988
1006
  root[key].format === RGBAFormat && root[key].type === UnsignedByteType) {
989
1007
  root[key].colorSpace = rootState.textureColorSpace;
@@ -1017,38 +1035,60 @@ function applyProps(object, props) {
1017
1035
  return object;
1018
1036
  }
1019
1037
 
1038
+ const DEFAULT_POINTER_ID = 0;
1039
+ const XR_POINTER_ID_START = 1e3;
1040
+ function getPointerState(internal, pointerId) {
1041
+ let state = internal.pointerMap.get(pointerId);
1042
+ if (!state) {
1043
+ state = {
1044
+ hovered: /* @__PURE__ */ new Map(),
1045
+ captured: /* @__PURE__ */ new Map(),
1046
+ initialClick: [0, 0],
1047
+ initialHits: []
1048
+ };
1049
+ internal.pointerMap.set(pointerId, state);
1050
+ }
1051
+ return state;
1052
+ }
1053
+ function getPointerId(event) {
1054
+ return "pointerId" in event ? event.pointerId : DEFAULT_POINTER_ID;
1055
+ }
1020
1056
  function makeId(event) {
1021
1057
  return (event.eventObject || event.object).uuid + "/" + event.index + event.instanceId;
1022
1058
  }
1023
- function releaseInternalPointerCapture(capturedMap, obj, captures, pointerId) {
1024
- const captureData = captures.get(obj);
1059
+ function releaseInternalPointerCapture(internal, obj, pointerId) {
1060
+ const pointerState = internal.pointerMap.get(pointerId);
1061
+ if (!pointerState) return;
1062
+ const captureData = pointerState.captured.get(obj);
1025
1063
  if (captureData) {
1026
- captures.delete(obj);
1027
- if (captures.size === 0) {
1028
- capturedMap.delete(pointerId);
1029
- captureData.target.releasePointerCapture(pointerId);
1030
- }
1064
+ pointerState.captured.delete(obj);
1065
+ captureData.target.releasePointerCapture(pointerId);
1031
1066
  }
1032
1067
  }
1033
1068
  function removeInteractivity(store, object) {
1034
1069
  const { internal } = store.getState();
1035
1070
  internal.interaction = internal.interaction.filter((o) => o !== object);
1036
- internal.initialHits = internal.initialHits.filter((o) => o !== object);
1037
- internal.hovered.forEach((value, key) => {
1038
- if (value.eventObject === object || value.object === object) {
1039
- internal.hovered.delete(key);
1071
+ for (const [pointerId, pointerState] of internal.pointerMap) {
1072
+ pointerState.initialHits = pointerState.initialHits.filter((o) => o !== object);
1073
+ pointerState.hovered.forEach((value, key) => {
1074
+ if (value.eventObject === object || value.object === object) {
1075
+ pointerState.hovered.delete(key);
1076
+ }
1077
+ });
1078
+ if (pointerState.captured.has(object)) {
1079
+ releaseInternalPointerCapture(internal, object, pointerId);
1040
1080
  }
1041
- });
1042
- internal.capturedMap.forEach((captures, pointerId) => {
1043
- releaseInternalPointerCapture(internal.capturedMap, object, captures, pointerId);
1044
- });
1081
+ }
1045
1082
  unregisterVisibility(store, object);
1046
1083
  }
1047
1084
  function createEvents(store) {
1048
- function calculateDistance(event) {
1085
+ function calculateDistance(event, pointerId) {
1049
1086
  const { internal } = store.getState();
1050
- const dx = event.offsetX - internal.initialClick[0];
1051
- const dy = event.offsetY - internal.initialClick[1];
1087
+ const pointerState = internal.pointerMap.get(pointerId);
1088
+ if (!pointerState) return 0;
1089
+ const [initialX, initialY] = pointerState.initialClick;
1090
+ const dx = event.offsetX - initialX;
1091
+ const dy = event.offsetY - initialY;
1052
1092
  return Math.round(Math.sqrt(dx * dx + dy * dy));
1053
1093
  }
1054
1094
  function filterPointerEvents(objects) {
@@ -1084,6 +1124,15 @@ function createEvents(store) {
1084
1124
  return state2.raycaster.camera ? state2.raycaster.intersectObject(obj, true) : [];
1085
1125
  }
1086
1126
  let hits = eventsObjects.flatMap(handleRaycast).sort((a, b) => {
1127
+ const aInteractivePriority = a.object.userData?.interactivePriority;
1128
+ const bInteractivePriority = b.object.userData?.interactivePriority;
1129
+ if (aInteractivePriority !== void 0 || bInteractivePriority !== void 0) {
1130
+ if (aInteractivePriority !== void 0 && bInteractivePriority === void 0) return -1;
1131
+ if (bInteractivePriority !== void 0 && aInteractivePriority === void 0) return 1;
1132
+ if (aInteractivePriority !== bInteractivePriority) {
1133
+ return (bInteractivePriority ?? 0) - (aInteractivePriority ?? 0);
1134
+ }
1135
+ }
1087
1136
  const aState = getRootState(a.object);
1088
1137
  const bState = getRootState(b.object);
1089
1138
  const aPriority = aState?.events?.priority ?? 1;
@@ -1105,9 +1154,13 @@ function createEvents(store) {
1105
1154
  eventObject = eventObject.parent;
1106
1155
  }
1107
1156
  }
1108
- if ("pointerId" in event && state.internal.capturedMap.has(event.pointerId)) {
1109
- for (const captureData of state.internal.capturedMap.get(event.pointerId).values()) {
1110
- if (!duplicates.has(makeId(captureData.intersection))) intersections.push(captureData.intersection);
1157
+ if ("pointerId" in event) {
1158
+ const pointerId = event.pointerId;
1159
+ const pointerState = state.internal.pointerMap.get(pointerId);
1160
+ if (pointerState?.captured.size) {
1161
+ for (const captureData of pointerState.captured.values()) {
1162
+ if (!duplicates.has(makeId(captureData.intersection))) intersections.push(captureData.intersection);
1163
+ }
1111
1164
  }
1112
1165
  }
1113
1166
  return intersections;
@@ -1120,27 +1173,25 @@ function createEvents(store) {
1120
1173
  if (state) {
1121
1174
  const { raycaster, pointer, camera, internal } = state;
1122
1175
  const unprojectedPoint = new Vector3(pointer.x, pointer.y, 0).unproject(camera);
1123
- const hasPointerCapture = (id) => internal.capturedMap.get(id)?.has(hit.eventObject) ?? false;
1176
+ const hasPointerCapture = (id) => {
1177
+ const pointerState = internal.pointerMap.get(id);
1178
+ return pointerState?.captured.has(hit.eventObject) ?? false;
1179
+ };
1124
1180
  const setPointerCapture = (id) => {
1125
1181
  const captureData = { intersection: hit, target: event.target };
1126
- if (internal.capturedMap.has(id)) {
1127
- internal.capturedMap.get(id).set(hit.eventObject, captureData);
1128
- } else {
1129
- internal.capturedMap.set(id, /* @__PURE__ */ new Map([[hit.eventObject, captureData]]));
1130
- }
1182
+ const pointerState = getPointerState(internal, id);
1183
+ pointerState.captured.set(hit.eventObject, captureData);
1131
1184
  event.target.setPointerCapture(id);
1132
1185
  };
1133
1186
  const releasePointerCapture = (id) => {
1134
- const captures = internal.capturedMap.get(id);
1135
- if (captures) {
1136
- releaseInternalPointerCapture(internal.capturedMap, hit.eventObject, captures, id);
1137
- }
1187
+ releaseInternalPointerCapture(internal, hit.eventObject, id);
1138
1188
  };
1139
1189
  const extractEventProps = {};
1140
1190
  for (const prop in event) {
1141
1191
  const property = event[prop];
1142
1192
  if (typeof property !== "function") extractEventProps[prop] = property;
1143
1193
  }
1194
+ const eventPointerId = "pointerId" in event ? event.pointerId : void 0;
1144
1195
  const raycastEvent = {
1145
1196
  ...hit,
1146
1197
  ...extractEventProps,
@@ -1151,18 +1202,19 @@ function createEvents(store) {
1151
1202
  unprojectedPoint,
1152
1203
  ray: raycaster.ray,
1153
1204
  camera,
1205
+ pointerId: eventPointerId,
1154
1206
  // Hijack stopPropagation, which just sets a flag
1155
1207
  stopPropagation() {
1156
- const capturesForPointer = "pointerId" in event && internal.capturedMap.get(event.pointerId);
1208
+ const pointerState = eventPointerId !== void 0 ? internal.pointerMap.get(eventPointerId) : void 0;
1157
1209
  if (
1158
1210
  // ...if this pointer hasn't been captured
1159
- !capturesForPointer || // ... or if the hit object is capturing the pointer
1160
- capturesForPointer.has(hit.eventObject)
1211
+ !pointerState?.captured.size || // ... or if the hit object is capturing the pointer
1212
+ pointerState.captured.has(hit.eventObject)
1161
1213
  ) {
1162
1214
  raycastEvent.stopped = localState.stopped = true;
1163
- if (internal.hovered.size && Array.from(internal.hovered.values()).find((i) => i.eventObject === hit.eventObject)) {
1215
+ if (pointerState?.hovered.size && Array.from(pointerState.hovered.values()).find((i) => i.eventObject === hit.eventObject)) {
1164
1216
  const higher = intersections.slice(0, intersections.indexOf(hit));
1165
- cancelPointer([...higher, hit]);
1217
+ cancelPointer([...higher, hit], eventPointerId);
1166
1218
  }
1167
1219
  }
1168
1220
  },
@@ -1178,15 +1230,18 @@ function createEvents(store) {
1178
1230
  }
1179
1231
  return intersections;
1180
1232
  }
1181
- function cancelPointer(intersections) {
1233
+ function cancelPointer(intersections, pointerId) {
1182
1234
  const { internal } = store.getState();
1183
- for (const hoveredObj of internal.hovered.values()) {
1235
+ const pid = pointerId ?? DEFAULT_POINTER_ID;
1236
+ const pointerState = internal.pointerMap.get(pid);
1237
+ if (!pointerState) return;
1238
+ for (const [hoveredId, hoveredObj] of pointerState.hovered) {
1184
1239
  if (!intersections.length || !intersections.find(
1185
1240
  (hit) => hit.object === hoveredObj.object && hit.index === hoveredObj.index && hit.instanceId === hoveredObj.instanceId
1186
1241
  )) {
1187
1242
  const eventObject = hoveredObj.eventObject;
1188
1243
  const instance = eventObject.__r3f;
1189
- internal.hovered.delete(makeId(hoveredObj));
1244
+ pointerState.hovered.delete(hoveredId);
1190
1245
  if (instance?.eventCount) {
1191
1246
  const handlers = instance.handlers;
1192
1247
  const data = { ...hoveredObj, intersections };
@@ -1215,41 +1270,118 @@ function createEvents(store) {
1215
1270
  instance?.handlers.onDropMissed?.(event);
1216
1271
  }
1217
1272
  }
1273
+ function cleanupPointer(pointerId) {
1274
+ const { internal } = store.getState();
1275
+ const pointerState = internal.pointerMap.get(pointerId);
1276
+ if (pointerState) {
1277
+ for (const [, hoveredObj] of pointerState.hovered) {
1278
+ const eventObject = hoveredObj.eventObject;
1279
+ const instance = eventObject.__r3f;
1280
+ if (instance?.eventCount) {
1281
+ const handlers = instance.handlers;
1282
+ const data = { ...hoveredObj, intersections: [] };
1283
+ handlers.onPointerOut?.(data);
1284
+ handlers.onPointerLeave?.(data);
1285
+ }
1286
+ }
1287
+ internal.pointerMap.delete(pointerId);
1288
+ }
1289
+ internal.pointerDirty.delete(pointerId);
1290
+ }
1291
+ function processDeferredPointer(event, pointerId) {
1292
+ const state = store.getState();
1293
+ const { internal } = state;
1294
+ if (!state.events.enabled) return;
1295
+ const filter = filterPointerEvents;
1296
+ const hits = intersect(event, filter);
1297
+ cancelPointer(hits, pointerId);
1298
+ function onIntersect(data) {
1299
+ const eventObject = data.eventObject;
1300
+ const instance = eventObject.__r3f;
1301
+ if (!instance?.eventCount) return;
1302
+ const handlers = instance.handlers;
1303
+ if (handlers.onPointerOver || handlers.onPointerEnter || handlers.onPointerOut || handlers.onPointerLeave) {
1304
+ const id = makeId(data);
1305
+ const pointerState = getPointerState(internal, pointerId);
1306
+ const hoveredItem = pointerState.hovered.get(id);
1307
+ if (!hoveredItem) {
1308
+ pointerState.hovered.set(id, data);
1309
+ handlers.onPointerOver?.(data);
1310
+ handlers.onPointerEnter?.(data);
1311
+ } else if (hoveredItem.stopped) {
1312
+ data.stopPropagation();
1313
+ }
1314
+ }
1315
+ handlers.onPointerMove?.(data);
1316
+ }
1317
+ handleIntersects(hits, event, 0, onIntersect);
1318
+ }
1218
1319
  function handlePointer(name) {
1219
1320
  switch (name) {
1220
1321
  case "onPointerLeave":
1221
- case "onPointerCancel":
1222
1322
  case "onDragLeave":
1223
1323
  return () => cancelPointer([]);
1324
+ // Global cancel of these events
1325
+ case "onPointerCancel":
1326
+ return (event) => {
1327
+ const pointerId = getPointerId(event);
1328
+ cleanupPointer(pointerId);
1329
+ };
1224
1330
  case "onLostPointerCapture":
1225
1331
  return (event) => {
1226
1332
  const { internal } = store.getState();
1227
- if ("pointerId" in event && internal.capturedMap.has(event.pointerId)) {
1333
+ const pointerId = getPointerId(event);
1334
+ const pointerState = internal.pointerMap.get(pointerId);
1335
+ if (pointerState?.captured.size) {
1228
1336
  requestAnimationFrame(() => {
1229
- if (internal.capturedMap.has(event.pointerId)) {
1230
- internal.capturedMap.delete(event.pointerId);
1231
- cancelPointer([]);
1337
+ const pointerState2 = internal.pointerMap.get(pointerId);
1338
+ if (pointerState2?.captured.size) {
1339
+ pointerState2.captured.clear();
1232
1340
  }
1341
+ cancelPointer([], pointerId);
1233
1342
  });
1234
1343
  }
1235
1344
  };
1236
1345
  }
1237
1346
  return function handleEvent(event) {
1238
1347
  const state = store.getState();
1239
- const { onPointerMissed, onDragOverMissed, onDropMissed, internal } = state;
1348
+ const { onPointerMissed, onDragOverMissed, onDropMissed, internal, events } = state;
1349
+ const pointerId = getPointerId(event);
1240
1350
  internal.lastEvent.current = event;
1241
- if (!state.events.enabled) return;
1351
+ if (!events.enabled) return;
1242
1352
  const isPointerMove = name === "onPointerMove";
1243
1353
  const isDragOver = name === "onDragOver";
1244
1354
  const isDrop = name === "onDrop";
1245
1355
  const isClickEvent = name === "onClick" || name === "onContextMenu" || name === "onDoubleClick";
1356
+ const isPointerDown = name === "onPointerDown";
1357
+ const isPointerUp = name === "onPointerUp";
1358
+ const isWheel = name === "onWheel";
1359
+ const canDeferRaycasts = events.frameTimedRaycasts && state.frameloop === "always";
1360
+ if (isPointerMove && canDeferRaycasts) {
1361
+ events.compute?.(event, state);
1362
+ internal.pointerDirty.set(pointerId, event);
1363
+ return;
1364
+ }
1365
+ if (isWheel && canDeferRaycasts && !events.alwaysFireOnScroll) {
1366
+ events.compute?.(event, state);
1367
+ internal.pointerDirty.set(pointerId, event);
1368
+ return;
1369
+ }
1370
+ if ((isClickEvent || isPointerDown || isPointerUp) && internal.pointerDirty.has(pointerId)) {
1371
+ const deferredEvent = internal.pointerDirty.get(pointerId);
1372
+ internal.pointerDirty.delete(pointerId);
1373
+ processDeferredPointer(deferredEvent, pointerId);
1374
+ }
1246
1375
  const filter = isPointerMove || isDragOver || isDrop ? filterPointerEvents : void 0;
1247
1376
  const hits = intersect(event, filter);
1248
- const delta = isClickEvent ? calculateDistance(event) : 0;
1249
- if (name === "onPointerDown") {
1250
- internal.initialClick = [event.offsetX, event.offsetY];
1251
- internal.initialHits = hits.map((hit) => hit.eventObject);
1252
- }
1377
+ const delta = isClickEvent ? calculateDistance(event, pointerId) : 0;
1378
+ if (isPointerDown) {
1379
+ const pointerState2 = getPointerState(internal, pointerId);
1380
+ pointerState2.initialClick = [event.offsetX, event.offsetY];
1381
+ pointerState2.initialHits = hits.map((hit) => hit.eventObject);
1382
+ }
1383
+ const pointerState = internal.pointerMap.get(pointerId);
1384
+ const initialHits = pointerState?.initialHits ?? [];
1253
1385
  if (isClickEvent && !hits.length) {
1254
1386
  if (delta <= 2) {
1255
1387
  pointerMissed(event, internal.interaction);
@@ -1264,7 +1396,9 @@ function createEvents(store) {
1264
1396
  dropMissed(event, internal.interaction);
1265
1397
  if (onDropMissed) onDropMissed(event);
1266
1398
  }
1267
- if (isPointerMove || isDragOver) cancelPointer(hits);
1399
+ if (isPointerMove || isDragOver) {
1400
+ cancelPointer(hits, pointerId);
1401
+ }
1268
1402
  function onIntersect(data) {
1269
1403
  const eventObject = data.eventObject;
1270
1404
  const instance = eventObject.__r3f;
@@ -1273,9 +1407,10 @@ function createEvents(store) {
1273
1407
  if (isPointerMove) {
1274
1408
  if (handlers.onPointerOver || handlers.onPointerEnter || handlers.onPointerOut || handlers.onPointerLeave) {
1275
1409
  const id = makeId(data);
1276
- const hoveredItem = internal.hovered.get(id);
1410
+ const pointerState2 = getPointerState(internal, pointerId);
1411
+ const hoveredItem = pointerState2.hovered.get(id);
1277
1412
  if (!hoveredItem) {
1278
- internal.hovered.set(id, data);
1413
+ pointerState2.hovered.set(id, data);
1279
1414
  handlers.onPointerOver?.(data);
1280
1415
  handlers.onPointerEnter?.(data);
1281
1416
  } else if (hoveredItem.stopped) {
@@ -1285,9 +1420,10 @@ function createEvents(store) {
1285
1420
  handlers.onPointerMove?.(data);
1286
1421
  } else if (isDragOver) {
1287
1422
  const id = makeId(data);
1288
- const hoveredItem = internal.hovered.get(id);
1423
+ const pointerState2 = getPointerState(internal, pointerId);
1424
+ const hoveredItem = pointerState2.hovered.get(id);
1289
1425
  if (!hoveredItem) {
1290
- internal.hovered.set(id, data);
1426
+ pointerState2.hovered.set(id, data);
1291
1427
  handlers.onDragOverEnter?.(data);
1292
1428
  } else if (hoveredItem.stopped) {
1293
1429
  data.stopPropagation();
@@ -1298,18 +1434,18 @@ function createEvents(store) {
1298
1434
  } else {
1299
1435
  const handler = handlers[name];
1300
1436
  if (handler) {
1301
- if (!isClickEvent || internal.initialHits.includes(eventObject)) {
1437
+ if (!isClickEvent || initialHits.includes(eventObject)) {
1302
1438
  pointerMissed(
1303
1439
  event,
1304
- internal.interaction.filter((object) => !internal.initialHits.includes(object))
1440
+ internal.interaction.filter((object) => !initialHits.includes(object))
1305
1441
  );
1306
1442
  handler(data);
1307
1443
  }
1308
1444
  } else {
1309
- if (isClickEvent && internal.initialHits.includes(eventObject)) {
1445
+ if (isClickEvent && initialHits.includes(eventObject)) {
1310
1446
  pointerMissed(
1311
1447
  event,
1312
- internal.interaction.filter((object) => !internal.initialHits.includes(object))
1448
+ internal.interaction.filter((object) => !initialHits.includes(object))
1313
1449
  );
1314
1450
  }
1315
1451
  }
@@ -1318,7 +1454,15 @@ function createEvents(store) {
1318
1454
  handleIntersects(hits, event, delta, onIntersect);
1319
1455
  };
1320
1456
  }
1321
- return { handlePointer };
1457
+ function flushDeferredPointers() {
1458
+ const { internal, events } = store.getState();
1459
+ if (!events.frameTimedRaycasts) return;
1460
+ for (const [pointerId, event] of internal.pointerDirty) {
1461
+ processDeferredPointer(event, pointerId);
1462
+ }
1463
+ internal.pointerDirty.clear();
1464
+ }
1465
+ return { handlePointer, flushDeferredPointers, processDeferredPointer };
1322
1466
  }
1323
1467
  const DOM_EVENTS = {
1324
1468
  onClick: ["click", false],
@@ -1337,10 +1481,15 @@ const DOM_EVENTS = {
1337
1481
  onLostPointerCapture: ["lostpointercapture", true]
1338
1482
  };
1339
1483
  function createPointerEvents(store) {
1340
- const { handlePointer } = createEvents(store);
1484
+ const { handlePointer, flushDeferredPointers, processDeferredPointer } = createEvents(store);
1485
+ let nextXRPointerId = XR_POINTER_ID_START;
1486
+ const xrPointers = /* @__PURE__ */ new Map();
1341
1487
  return {
1342
1488
  priority: 1,
1343
1489
  enabled: true,
1490
+ frameTimedRaycasts: true,
1491
+ alwaysFireOnScroll: true,
1492
+ updateOnFrame: false,
1344
1493
  compute(event, state) {
1345
1494
  state.pointer.set(event.offsetX / state.size.width * 2 - 1, -(event.offsetY / state.size.height) * 2 + 1);
1346
1495
  state.raycaster.setFromCamera(state.pointer, state.camera);
@@ -1350,11 +1499,33 @@ function createPointerEvents(store) {
1350
1499
  (acc, key) => ({ ...acc, [key]: handlePointer(key) }),
1351
1500
  {}
1352
1501
  ),
1353
- update: () => {
1502
+ update: (pointerId) => {
1503
+ const { events, internal } = store.getState();
1504
+ if (!events.handlers) return;
1505
+ if (pointerId !== void 0) {
1506
+ const event = internal.pointerDirty.get(pointerId);
1507
+ if (event) {
1508
+ internal.pointerDirty.delete(pointerId);
1509
+ processDeferredPointer(event, pointerId);
1510
+ } else if (internal.lastEvent?.current) {
1511
+ processDeferredPointer(internal.lastEvent.current, pointerId);
1512
+ }
1513
+ } else {
1514
+ flushDeferredPointers();
1515
+ if (internal.lastEvent?.current) {
1516
+ events.handlers.onPointerMove(internal.lastEvent.current);
1517
+ }
1518
+ }
1519
+ },
1520
+ flush: () => {
1354
1521
  const { events, internal } = store.getState();
1355
- if (internal.lastEvent?.current && events.handlers) events.handlers.onPointerMove(internal.lastEvent.current);
1522
+ flushDeferredPointers();
1523
+ if (events.updateOnFrame && internal.lastEvent?.current && events.handlers) {
1524
+ events.handlers.onPointerMove(internal.lastEvent.current);
1525
+ }
1356
1526
  },
1357
1527
  connect: (target) => {
1528
+ if (!target) return;
1358
1529
  const { set, events } = store.getState();
1359
1530
  events.disconnect?.();
1360
1531
  set((state) => ({ events: { ...state.events, connected: target } }));
@@ -1378,6 +1549,32 @@ function createPointerEvents(store) {
1378
1549
  }
1379
1550
  set((state) => ({ events: { ...state.events, connected: void 0 } }));
1380
1551
  }
1552
+ },
1553
+ registerPointer: (config) => {
1554
+ const pointerId = nextXRPointerId++;
1555
+ xrPointers.set(pointerId, config);
1556
+ const { internal } = store.getState();
1557
+ getPointerState(internal, pointerId);
1558
+ return pointerId;
1559
+ },
1560
+ unregisterPointer: (pointerId) => {
1561
+ xrPointers.delete(pointerId);
1562
+ const { internal } = store.getState();
1563
+ const pointerState = internal.pointerMap.get(pointerId);
1564
+ if (pointerState) {
1565
+ for (const [, hoveredObj] of pointerState.hovered) {
1566
+ const eventObject = hoveredObj.eventObject;
1567
+ const instance = eventObject.__r3f;
1568
+ if (instance?.eventCount) {
1569
+ const handlers = instance.handlers;
1570
+ const data = { ...hoveredObj, intersections: [] };
1571
+ handlers.onPointerOut?.(data);
1572
+ handlers.onPointerLeave?.(data);
1573
+ }
1574
+ }
1575
+ internal.pointerMap.delete(pointerId);
1576
+ }
1577
+ internal.pointerDirty.delete(pointerId);
1381
1578
  }
1382
1579
  };
1383
1580
  }
@@ -1691,7 +1888,7 @@ function shouldRun(job, now) {
1691
1888
  const minInterval = 1e3 / job.fps;
1692
1889
  const lastRun = job.lastRun ?? 0;
1693
1890
  const elapsed = now - lastRun;
1694
- if (elapsed < minInterval) return false;
1891
+ if (elapsed < minInterval - 1) return false;
1695
1892
  if (job.drop) {
1696
1893
  job.lastRun = now;
1697
1894
  } else {
@@ -2508,7 +2705,14 @@ const createStore = (invalidate, advance) => {
2508
2705
  frustum: new Frustum(),
2509
2706
  autoUpdateFrustum: true,
2510
2707
  raycaster: null,
2511
- events: { priority: 1, enabled: true, connected: false },
2708
+ events: {
2709
+ priority: 1,
2710
+ enabled: true,
2711
+ connected: false,
2712
+ frameTimedRaycasts: true,
2713
+ alwaysFireOnScroll: true,
2714
+ updateOnFrame: false
2715
+ },
2512
2716
  scene: null,
2513
2717
  rootScene: null,
2514
2718
  xr: null,
@@ -2599,11 +2803,13 @@ const createStore = (invalidate, advance) => {
2599
2803
  },
2600
2804
  setError: (error) => set(() => ({ error })),
2601
2805
  error: null,
2602
- //* TSL State (managed via hooks: useUniforms, useNodes, useTextures, usePostProcessing) ==============================
2806
+ //* TSL State (managed via hooks: useUniforms, useNodes, useBuffers, useGPUStorage, useTextures, useRenderPipeline) ==============================
2603
2807
  uniforms: {},
2604
2808
  nodes: {},
2809
+ buffers: {},
2810
+ gpuStorage: {},
2605
2811
  textures: /* @__PURE__ */ new Map(),
2606
- postProcessing: null,
2812
+ renderPipeline: null,
2607
2813
  passes: {},
2608
2814
  _hmrVersion: 0,
2609
2815
  _sizeImperative: false,
@@ -2612,12 +2818,16 @@ const createStore = (invalidate, advance) => {
2612
2818
  internal: {
2613
2819
  // Events
2614
2820
  interaction: [],
2615
- hovered: /* @__PURE__ */ new Map(),
2616
2821
  subscribers: [],
2822
+ // Per-pointer state (new unified structure)
2823
+ pointerMap: /* @__PURE__ */ new Map(),
2824
+ pointerDirty: /* @__PURE__ */ new Map(),
2825
+ lastEvent: React.createRef(),
2826
+ // Deprecated but kept for backwards compatibility
2827
+ hovered: /* @__PURE__ */ new Map(),
2617
2828
  initialClick: [0, 0],
2618
2829
  initialHits: [],
2619
2830
  capturedMap: /* @__PURE__ */ new Map(),
2620
- lastEvent: React.createRef(),
2621
2831
  // Visibility tracking (onFramed, onOccluded, onVisible)
2622
2832
  visibilityRegistry: /* @__PURE__ */ new Map(),
2623
2833
  // Occlusion system (WebGPU only)
@@ -2705,14 +2915,16 @@ const createStore = (invalidate, advance) => {
2705
2915
  oldSize = size;
2706
2916
  oldDpr = viewport.dpr;
2707
2917
  updateCamera(camera, size);
2708
- if (canvasTarget) {
2918
+ if (internal.isSecondary && canvasTarget) {
2709
2919
  if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
2710
- const updateStyle = typeof HTMLCanvasElement !== "undefined" && canvasTarget.domElement instanceof HTMLCanvasElement;
2711
- canvasTarget.setSize(size.width, size.height, updateStyle);
2920
+ canvasTarget.setSize(size.width, size.height, false);
2712
2921
  } else {
2713
2922
  if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
2714
- const updateStyle = typeof HTMLCanvasElement !== "undefined" && actualRenderer.domElement instanceof HTMLCanvasElement;
2715
- actualRenderer.setSize(size.width, size.height, updateStyle);
2923
+ actualRenderer.setSize(size.width, size.height, false);
2924
+ if (canvasTarget) {
2925
+ if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
2926
+ canvasTarget.setSize(size.width, size.height, false);
2927
+ }
2716
2928
  }
2717
2929
  }
2718
2930
  if (camera !== oldCamera) {
@@ -14997,7 +15209,6 @@ function createRoot(canvas) {
14997
15209
  events,
14998
15210
  onCreated: onCreatedCallback,
14999
15211
  shadows = false,
15000
- textureColorSpace = SRGBColorSpace,
15001
15212
  orthographic = false,
15002
15213
  frameloop = "always",
15003
15214
  dpr = [1, 2],
@@ -15012,6 +15223,7 @@ function createRoot(canvas) {
15012
15223
  _sizeProps,
15013
15224
  forceEven
15014
15225
  } = props;
15226
+ const textureColorSpace = is.obj(glConfig) && !is.fun(glConfig) && !isRenderer(glConfig) && glConfig.textureColorSpace || is.obj(rendererConfig) && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && rendererConfig.textureColorSpace || SRGBColorSpace;
15015
15227
  const state = store.getState();
15016
15228
  const defaultGLProps = {
15017
15229
  canvas,
@@ -15135,7 +15347,7 @@ function createRoot(canvas) {
15135
15347
  lastConfiguredProps.performance = performance;
15136
15348
  }
15137
15349
  if (!state.xr) {
15138
- const handleXRFrame = (timestamp, frame) => {
15350
+ const handleXRFrame = (timestamp, _frame) => {
15139
15351
  const state2 = store.getState();
15140
15352
  if (state2.frameloop === "never") return;
15141
15353
  advance(timestamp);
@@ -15171,15 +15383,22 @@ function createRoot(canvas) {
15171
15383
  const oldType = renderer.shadowMap.type;
15172
15384
  renderer.shadowMap.enabled = !!shadows;
15173
15385
  if (is.boo(shadows)) {
15174
- renderer.shadowMap.type = PCFSoftShadowMap;
15386
+ renderer.shadowMap.type = PCFShadowMap;
15175
15387
  } else if (is.str(shadows)) {
15388
+ if (shadows === "soft") {
15389
+ notifyDepreciated({
15390
+ heading: 'shadows="soft" is deprecated',
15391
+ body: "Three has depreciated soft and improved basic PCFShadows, we converted for you.",
15392
+ link: "https://github.com/mrdoob/three.js/wiki/Migration-Guide?utm_source=chatgpt.com#181--182"
15393
+ });
15394
+ }
15176
15395
  const types = {
15177
15396
  basic: BasicShadowMap,
15178
15397
  percentage: PCFShadowMap,
15179
- soft: PCFSoftShadowMap,
15398
+ soft: PCFShadowMap,
15180
15399
  variance: VSMShadowMap
15181
15400
  };
15182
- renderer.shadowMap.type = types[shadows] ?? PCFSoftShadowMap;
15401
+ renderer.shadowMap.type = types[shadows] ?? PCFShadowMap;
15183
15402
  } else if (is.obj(shadows)) {
15184
15403
  Object.assign(renderer.shadowMap, shadows);
15185
15404
  }
@@ -15195,13 +15414,24 @@ function createRoot(canvas) {
15195
15414
  if (state.textureColorSpace !== textureColorSpace) state.set(() => ({ textureColorSpace }));
15196
15415
  lastConfiguredProps.textureColorSpace = textureColorSpace;
15197
15416
  }
15417
+ const r3fProps = ["textureColorSpace"];
15418
+ const constructorOnlyProps = ["samples", "antialias", "alpha", "canvas", "powerPreference"];
15419
+ const nonApplyProps = [...r3fProps, ...constructorOnlyProps];
15198
15420
  if (glConfig && !is.fun(glConfig) && !isRenderer(glConfig) && !is.equ(glConfig, renderer, shallowLoose)) {
15199
- applyProps(renderer, glConfig);
15421
+ const glProps = {};
15422
+ for (const key in glConfig) {
15423
+ if (!nonApplyProps.includes(key)) glProps[key] = glConfig[key];
15424
+ }
15425
+ applyProps(renderer, glProps);
15200
15426
  }
15201
15427
  if (rendererConfig && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && state.renderer) {
15202
15428
  const currentRenderer = state.renderer;
15203
15429
  if (!is.equ(rendererConfig, currentRenderer, shallowLoose)) {
15204
- applyProps(currentRenderer, rendererConfig);
15430
+ const rendererProps = {};
15431
+ for (const key in rendererConfig) {
15432
+ if (!nonApplyProps.includes(key)) rendererProps[key] = rendererConfig[key];
15433
+ }
15434
+ applyProps(currentRenderer, rendererProps);
15205
15435
  }
15206
15436
  }
15207
15437
  const scheduler = getScheduler();
@@ -15227,6 +15457,18 @@ function createRoot(canvas) {
15227
15457
  system: true
15228
15458
  }
15229
15459
  );
15460
+ const unregisterEventsFlush = scheduler.register(
15461
+ () => {
15462
+ const state2 = store.getState();
15463
+ state2.events.flush?.();
15464
+ },
15465
+ {
15466
+ id: `${newRootId}_events`,
15467
+ rootId: newRootId,
15468
+ phase: "input",
15469
+ system: true
15470
+ }
15471
+ );
15230
15472
  const unregisterFrustum = scheduler.register(
15231
15473
  () => {
15232
15474
  const state2 = store.getState();
@@ -15261,7 +15503,7 @@ function createRoot(canvas) {
15261
15503
  const userHandlesRender = scheduler.hasUserJobsInPhase("render", newRootId);
15262
15504
  if (userHandlesRender || state2.internal.priority) return;
15263
15505
  try {
15264
- if (state2.postProcessing?.render) state2.postProcessing.render();
15506
+ if (state2.renderPipeline?.render) state2.renderPipeline.render();
15265
15507
  else if (renderer2?.render) renderer2.render(state2.scene, state2.camera);
15266
15508
  } catch (error) {
15267
15509
  state2.setError(error instanceof Error ? error : new Error(String(error)));
@@ -15286,6 +15528,7 @@ function createRoot(canvas) {
15286
15528
  unregisterRoot: () => {
15287
15529
  unregisterRoot();
15288
15530
  unregisterCanvasTarget();
15531
+ unregisterEventsFlush();
15289
15532
  unregisterFrustum();
15290
15533
  unregisterVisibility();
15291
15534
  unregisterRender();
@@ -15451,9 +15694,13 @@ function PortalInner({ state = {}, children, container }) {
15451
15694
  const store = createWithEqualityFn((set, get) => ({ ...rest, set, get }));
15452
15695
  const onMutate = (prev) => store.setState((state2) => inject.current(prev, state2));
15453
15696
  onMutate(previousRoot.getState());
15454
- previousRoot.subscribe(onMutate);
15455
15697
  return store;
15456
15698
  }, [previousRoot, container]);
15699
+ useIsomorphicLayoutEffect(() => {
15700
+ const onMutate = (prev) => usePortalStore.setState((state2) => inject.current(prev, state2));
15701
+ const unsubscribe = previousRoot.subscribe(onMutate);
15702
+ return unsubscribe;
15703
+ }, [previousRoot, usePortalStore]);
15457
15704
  return (
15458
15705
  // @ts-ignore, reconciler types are not maintained
15459
15706
  /* @__PURE__ */ jsx(Fragment, { children: reconciler.createPortal(
@@ -15498,8 +15745,18 @@ function CanvasImpl({
15498
15745
  forceEven,
15499
15746
  ...props
15500
15747
  }) {
15501
- 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 };
15502
- const renderer = Object.keys(rendererConfig).length > 0 ? rendererConfig : rendererProp;
15748
+ const isRendererConfig = typeof rendererProp === "object" && rendererProp !== null && !("render" in rendererProp) && ("primaryCanvas" in rendererProp || "scheduler" in rendererProp);
15749
+ let primaryCanvas;
15750
+ let scheduler;
15751
+ let renderer;
15752
+ if (isRendererConfig) {
15753
+ const { primaryCanvas: pc, scheduler: sc, ...rest } = rendererProp;
15754
+ primaryCanvas = pc;
15755
+ scheduler = sc;
15756
+ renderer = Object.keys(rest).length > 0 ? rest : rendererProp;
15757
+ } else {
15758
+ renderer = rendererProp;
15759
+ }
15503
15760
  React.useMemo(() => extend(THREE), []);
15504
15761
  const Bridge = useBridge();
15505
15762
  const backgroundProps = React.useMemo(() => {
@@ -15674,6 +15931,7 @@ function CanvasImpl({
15674
15931
  queueMicrotask(() => {
15675
15932
  const rootEntry = _roots.get(canvas);
15676
15933
  if (rootEntry?.store) {
15934
+ console.log("[R3F] HMR detected \u2014 rebuilding nodes/uniforms");
15677
15935
  rootEntry.store.setState((state) => ({
15678
15936
  nodes: {},
15679
15937
  uniforms: {},
@@ -15685,8 +15943,7 @@ function CanvasImpl({
15685
15943
  if (typeof import.meta !== "undefined" && import.meta.hot) {
15686
15944
  const hot = import.meta.hot;
15687
15945
  hot.on("vite:afterUpdate", handleHMR);
15688
- return () => hot.dispose?.(() => {
15689
- });
15946
+ return () => hot.off?.("vite:afterUpdate", handleHMR);
15690
15947
  }
15691
15948
  if (typeof module !== "undefined" && module.hot) {
15692
15949
  const hot = module.hot;
@@ -15709,7 +15966,16 @@ function CanvasImpl({
15709
15966
  ...style
15710
15967
  },
15711
15968
  ...props,
15712
- 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 }) })
15969
+ children: /* @__PURE__ */ jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsx(
15970
+ "canvas",
15971
+ {
15972
+ ref: canvasRef,
15973
+ id,
15974
+ className: "r3f-canvas",
15975
+ style: { display: "block", width: "100%", height: "100%" },
15976
+ children: fallback
15977
+ }
15978
+ ) })
15713
15979
  }
15714
15980
  );
15715
15981
  }