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

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.cjs CHANGED
@@ -1005,6 +1005,9 @@ function applyProps(object, props) {
1005
1005
  else target.set(value);
1006
1006
  } else {
1007
1007
  root[key] = value;
1008
+ if (key.endsWith("Node") && root.isMaterial) {
1009
+ root.needsUpdate = true;
1010
+ }
1008
1011
  if (rootState && rootState.renderer?.outputColorSpace === three.SRGBColorSpace && colorMaps.includes(key) && isTexture(value) && root[key]?.isTexture && // sRGB textures must be RGBA8 since r137 https://github.com/mrdoob/three.js/pull/23129
1009
1012
  root[key].format === three.RGBAFormat && root[key].type === three.UnsignedByteType) {
1010
1013
  root[key].colorSpace = rootState.textureColorSpace;
@@ -1038,38 +1041,60 @@ function applyProps(object, props) {
1038
1041
  return object;
1039
1042
  }
1040
1043
 
1044
+ const DEFAULT_POINTER_ID = 0;
1045
+ const XR_POINTER_ID_START = 1e3;
1046
+ function getPointerState(internal, pointerId) {
1047
+ let state = internal.pointerMap.get(pointerId);
1048
+ if (!state) {
1049
+ state = {
1050
+ hovered: /* @__PURE__ */ new Map(),
1051
+ captured: /* @__PURE__ */ new Map(),
1052
+ initialClick: [0, 0],
1053
+ initialHits: []
1054
+ };
1055
+ internal.pointerMap.set(pointerId, state);
1056
+ }
1057
+ return state;
1058
+ }
1059
+ function getPointerId(event) {
1060
+ return "pointerId" in event ? event.pointerId : DEFAULT_POINTER_ID;
1061
+ }
1041
1062
  function makeId(event) {
1042
1063
  return (event.eventObject || event.object).uuid + "/" + event.index + event.instanceId;
1043
1064
  }
1044
- function releaseInternalPointerCapture(capturedMap, obj, captures, pointerId) {
1045
- const captureData = captures.get(obj);
1065
+ function releaseInternalPointerCapture(internal, obj, pointerId) {
1066
+ const pointerState = internal.pointerMap.get(pointerId);
1067
+ if (!pointerState) return;
1068
+ const captureData = pointerState.captured.get(obj);
1046
1069
  if (captureData) {
1047
- captures.delete(obj);
1048
- if (captures.size === 0) {
1049
- capturedMap.delete(pointerId);
1050
- captureData.target.releasePointerCapture(pointerId);
1051
- }
1070
+ pointerState.captured.delete(obj);
1071
+ captureData.target.releasePointerCapture(pointerId);
1052
1072
  }
1053
1073
  }
1054
1074
  function removeInteractivity(store, object) {
1055
1075
  const { internal } = store.getState();
1056
1076
  internal.interaction = internal.interaction.filter((o) => o !== object);
1057
- internal.initialHits = internal.initialHits.filter((o) => o !== object);
1058
- internal.hovered.forEach((value, key) => {
1059
- if (value.eventObject === object || value.object === object) {
1060
- internal.hovered.delete(key);
1077
+ for (const [pointerId, pointerState] of internal.pointerMap) {
1078
+ pointerState.initialHits = pointerState.initialHits.filter((o) => o !== object);
1079
+ pointerState.hovered.forEach((value, key) => {
1080
+ if (value.eventObject === object || value.object === object) {
1081
+ pointerState.hovered.delete(key);
1082
+ }
1083
+ });
1084
+ if (pointerState.captured.has(object)) {
1085
+ releaseInternalPointerCapture(internal, object, pointerId);
1061
1086
  }
1062
- });
1063
- internal.capturedMap.forEach((captures, pointerId) => {
1064
- releaseInternalPointerCapture(internal.capturedMap, object, captures, pointerId);
1065
- });
1087
+ }
1066
1088
  unregisterVisibility(store, object);
1067
1089
  }
1068
1090
  function createEvents(store) {
1069
- function calculateDistance(event) {
1091
+ function calculateDistance(event, pointerId) {
1070
1092
  const { internal } = store.getState();
1071
- const dx = event.offsetX - internal.initialClick[0];
1072
- const dy = event.offsetY - internal.initialClick[1];
1093
+ const pointerState = internal.pointerMap.get(pointerId);
1094
+ if (!pointerState) return 0;
1095
+ const [initialX, initialY] = pointerState.initialClick;
1096
+ const dx = event.offsetX - initialX;
1097
+ const dy = event.offsetY - initialY;
1073
1098
  return Math.round(Math.sqrt(dx * dx + dy * dy));
1074
1099
  }
1075
1100
  function filterPointerEvents(objects) {
@@ -1105,6 +1130,15 @@ function createEvents(store) {
1105
1130
  return state2.raycaster.camera ? state2.raycaster.intersectObject(obj, true) : [];
1106
1131
  }
1107
1132
  let hits = eventsObjects.flatMap(handleRaycast).sort((a, b) => {
1133
+ const aInteractivePriority = a.object.userData?.interactivePriority;
1134
+ const bInteractivePriority = b.object.userData?.interactivePriority;
1135
+ if (aInteractivePriority !== void 0 || bInteractivePriority !== void 0) {
1136
+ if (aInteractivePriority !== void 0 && bInteractivePriority === void 0) return -1;
1137
+ if (bInteractivePriority !== void 0 && aInteractivePriority === void 0) return 1;
1138
+ if (aInteractivePriority !== bInteractivePriority) {
1139
+ return (bInteractivePriority ?? 0) - (aInteractivePriority ?? 0);
1140
+ }
1141
+ }
1108
1142
  const aState = getRootState(a.object);
1109
1143
  const bState = getRootState(b.object);
1110
1144
  const aPriority = aState?.events?.priority ?? 1;
@@ -1126,9 +1160,13 @@ function createEvents(store) {
1126
1160
  eventObject = eventObject.parent;
1127
1161
  }
1128
1162
  }
1129
- if ("pointerId" in event && state.internal.capturedMap.has(event.pointerId)) {
1130
- for (const captureData of state.internal.capturedMap.get(event.pointerId).values()) {
1131
- if (!duplicates.has(makeId(captureData.intersection))) intersections.push(captureData.intersection);
1163
+ if ("pointerId" in event) {
1164
+ const pointerId = event.pointerId;
1165
+ const pointerState = state.internal.pointerMap.get(pointerId);
1166
+ if (pointerState?.captured.size) {
1167
+ for (const captureData of pointerState.captured.values()) {
1168
+ if (!duplicates.has(makeId(captureData.intersection))) intersections.push(captureData.intersection);
1169
+ }
1132
1170
  }
1133
1171
  }
1134
1172
  return intersections;
@@ -1141,27 +1179,25 @@ function createEvents(store) {
1141
1179
  if (state) {
1142
1180
  const { raycaster, pointer, camera, internal } = state;
1143
1181
  const unprojectedPoint = new three.Vector3(pointer.x, pointer.y, 0).unproject(camera);
1144
- const hasPointerCapture = (id) => internal.capturedMap.get(id)?.has(hit.eventObject) ?? false;
1182
+ const hasPointerCapture = (id) => {
1183
+ const pointerState = internal.pointerMap.get(id);
1184
+ return pointerState?.captured.has(hit.eventObject) ?? false;
1185
+ };
1145
1186
  const setPointerCapture = (id) => {
1146
1187
  const captureData = { intersection: hit, target: event.target };
1147
- if (internal.capturedMap.has(id)) {
1148
- internal.capturedMap.get(id).set(hit.eventObject, captureData);
1149
- } else {
1150
- internal.capturedMap.set(id, /* @__PURE__ */ new Map([[hit.eventObject, captureData]]));
1151
- }
1188
+ const pointerState = getPointerState(internal, id);
1189
+ pointerState.captured.set(hit.eventObject, captureData);
1152
1190
  event.target.setPointerCapture(id);
1153
1191
  };
1154
1192
  const releasePointerCapture = (id) => {
1155
- const captures = internal.capturedMap.get(id);
1156
- if (captures) {
1157
- releaseInternalPointerCapture(internal.capturedMap, hit.eventObject, captures, id);
1158
- }
1193
+ releaseInternalPointerCapture(internal, hit.eventObject, id);
1159
1194
  };
1160
1195
  const extractEventProps = {};
1161
1196
  for (const prop in event) {
1162
1197
  const property = event[prop];
1163
1198
  if (typeof property !== "function") extractEventProps[prop] = property;
1164
1199
  }
1200
+ const eventPointerId = "pointerId" in event ? event.pointerId : void 0;
1165
1201
  const raycastEvent = {
1166
1202
  ...hit,
1167
1203
  ...extractEventProps,
@@ -1172,18 +1208,19 @@ function createEvents(store) {
1172
1208
  unprojectedPoint,
1173
1209
  ray: raycaster.ray,
1174
1210
  camera,
1211
+ pointerId: eventPointerId,
1175
1212
  // Hijack stopPropagation, which just sets a flag
1176
1213
  stopPropagation() {
1177
- const capturesForPointer = "pointerId" in event && internal.capturedMap.get(event.pointerId);
1214
+ const pointerState = eventPointerId !== void 0 ? internal.pointerMap.get(eventPointerId) : void 0;
1178
1215
  if (
1179
1216
  // ...if this pointer hasn't been captured
1180
- !capturesForPointer || // ... or if the hit object is capturing the pointer
1181
- capturesForPointer.has(hit.eventObject)
1217
+ !pointerState?.captured.size || // ... or if the hit object is capturing the pointer
1218
+ pointerState.captured.has(hit.eventObject)
1182
1219
  ) {
1183
1220
  raycastEvent.stopped = localState.stopped = true;
1184
- if (internal.hovered.size && Array.from(internal.hovered.values()).find((i) => i.eventObject === hit.eventObject)) {
1221
+ if (pointerState?.hovered.size && Array.from(pointerState.hovered.values()).find((i) => i.eventObject === hit.eventObject)) {
1185
1222
  const higher = intersections.slice(0, intersections.indexOf(hit));
1186
- cancelPointer([...higher, hit]);
1223
+ cancelPointer([...higher, hit], eventPointerId);
1187
1224
  }
1188
1225
  }
1189
1226
  },
@@ -1199,15 +1236,18 @@ function createEvents(store) {
1199
1236
  }
1200
1237
  return intersections;
1201
1238
  }
1202
- function cancelPointer(intersections) {
1239
+ function cancelPointer(intersections, pointerId) {
1203
1240
  const { internal } = store.getState();
1204
- for (const hoveredObj of internal.hovered.values()) {
1241
+ const pid = pointerId ?? DEFAULT_POINTER_ID;
1242
+ const pointerState = internal.pointerMap.get(pid);
1243
+ if (!pointerState) return;
1244
+ for (const [hoveredId, hoveredObj] of pointerState.hovered) {
1205
1245
  if (!intersections.length || !intersections.find(
1206
1246
  (hit) => hit.object === hoveredObj.object && hit.index === hoveredObj.index && hit.instanceId === hoveredObj.instanceId
1207
1247
  )) {
1208
1248
  const eventObject = hoveredObj.eventObject;
1209
1249
  const instance = eventObject.__r3f;
1210
- internal.hovered.delete(makeId(hoveredObj));
1250
+ pointerState.hovered.delete(hoveredId);
1211
1251
  if (instance?.eventCount) {
1212
1252
  const handlers = instance.handlers;
1213
1253
  const data = { ...hoveredObj, intersections };
@@ -1236,41 +1276,118 @@ function createEvents(store) {
1236
1276
  instance?.handlers.onDropMissed?.(event);
1237
1277
  }
1238
1278
  }
1279
+ function cleanupPointer(pointerId) {
1280
+ const { internal } = store.getState();
1281
+ const pointerState = internal.pointerMap.get(pointerId);
1282
+ if (pointerState) {
1283
+ for (const [, hoveredObj] of pointerState.hovered) {
1284
+ const eventObject = hoveredObj.eventObject;
1285
+ const instance = eventObject.__r3f;
1286
+ if (instance?.eventCount) {
1287
+ const handlers = instance.handlers;
1288
+ const data = { ...hoveredObj, intersections: [] };
1289
+ handlers.onPointerOut?.(data);
1290
+ handlers.onPointerLeave?.(data);
1291
+ }
1292
+ }
1293
+ internal.pointerMap.delete(pointerId);
1294
+ }
1295
+ internal.pointerDirty.delete(pointerId);
1296
+ }
1297
+ function processDeferredPointer(event, pointerId) {
1298
+ const state = store.getState();
1299
+ const { onPointerMissed, onDragOverMissed, internal } = state;
1300
+ if (!state.events.enabled) return;
1301
+ const filter = filterPointerEvents;
1302
+ const hits = intersect(event, filter);
1303
+ cancelPointer(hits, pointerId);
1304
+ function onIntersect(data) {
1305
+ const eventObject = data.eventObject;
1306
+ const instance = eventObject.__r3f;
1307
+ if (!instance?.eventCount) return;
1308
+ const handlers = instance.handlers;
1309
+ if (handlers.onPointerOver || handlers.onPointerEnter || handlers.onPointerOut || handlers.onPointerLeave) {
1310
+ const id = makeId(data);
1311
+ const pointerState = getPointerState(internal, pointerId);
1312
+ const hoveredItem = pointerState.hovered.get(id);
1313
+ if (!hoveredItem) {
1314
+ pointerState.hovered.set(id, data);
1315
+ handlers.onPointerOver?.(data);
1316
+ handlers.onPointerEnter?.(data);
1317
+ } else if (hoveredItem.stopped) {
1318
+ data.stopPropagation();
1319
+ }
1320
+ }
1321
+ handlers.onPointerMove?.(data);
1322
+ }
1323
+ handleIntersects(hits, event, 0, onIntersect);
1324
+ }
1239
1325
  function handlePointer(name) {
1240
1326
  switch (name) {
1241
1327
  case "onPointerLeave":
1242
- case "onPointerCancel":
1243
1328
  case "onDragLeave":
1244
1329
  return () => cancelPointer([]);
1330
+ // Global cancel of these events
1331
+ case "onPointerCancel":
1332
+ return (event) => {
1333
+ const pointerId = getPointerId(event);
1334
+ cleanupPointer(pointerId);
1335
+ };
1245
1336
  case "onLostPointerCapture":
1246
1337
  return (event) => {
1247
1338
  const { internal } = store.getState();
1248
- if ("pointerId" in event && internal.capturedMap.has(event.pointerId)) {
1339
+ const pointerId = getPointerId(event);
1340
+ const pointerState = internal.pointerMap.get(pointerId);
1341
+ if (pointerState?.captured.size) {
1249
1342
  requestAnimationFrame(() => {
1250
- if (internal.capturedMap.has(event.pointerId)) {
1251
- internal.capturedMap.delete(event.pointerId);
1252
- cancelPointer([]);
1343
+ const pointerState2 = internal.pointerMap.get(pointerId);
1344
+ if (pointerState2?.captured.size) {
1345
+ pointerState2.captured.clear();
1253
1346
  }
1347
+ cancelPointer([], pointerId);
1254
1348
  });
1255
1349
  }
1256
1350
  };
1257
1351
  }
1258
1352
  return function handleEvent(event) {
1259
1353
  const state = store.getState();
1260
- const { onPointerMissed, onDragOverMissed, onDropMissed, internal } = state;
1354
+ const { onPointerMissed, onDragOverMissed, onDropMissed, internal, events } = state;
1355
+ const pointerId = getPointerId(event);
1261
1356
  internal.lastEvent.current = event;
1262
- if (!state.events.enabled) return;
1357
+ if (!events.enabled) return;
1263
1358
  const isPointerMove = name === "onPointerMove";
1264
1359
  const isDragOver = name === "onDragOver";
1265
1360
  const isDrop = name === "onDrop";
1266
1361
  const isClickEvent = name === "onClick" || name === "onContextMenu" || name === "onDoubleClick";
1362
+ const isPointerDown = name === "onPointerDown";
1363
+ const isPointerUp = name === "onPointerUp";
1364
+ const isWheel = name === "onWheel";
1365
+ const canDeferRaycasts = events.frameTimedRaycasts && state.frameloop === "always";
1366
+ if (isPointerMove && canDeferRaycasts) {
1367
+ events.compute?.(event, state);
1368
+ internal.pointerDirty.set(pointerId, event);
1369
+ return;
1370
+ }
1371
+ if (isWheel && canDeferRaycasts && !events.alwaysFireOnScroll) {
1372
+ events.compute?.(event, state);
1373
+ internal.pointerDirty.set(pointerId, event);
1374
+ return;
1375
+ }
1376
+ if ((isClickEvent || isPointerDown || isPointerUp) && internal.pointerDirty.has(pointerId)) {
1377
+ const deferredEvent = internal.pointerDirty.get(pointerId);
1378
+ internal.pointerDirty.delete(pointerId);
1379
+ processDeferredPointer(deferredEvent, pointerId);
1380
+ }
1267
1381
  const filter = isPointerMove || isDragOver || isDrop ? filterPointerEvents : void 0;
1268
1382
  const hits = intersect(event, filter);
1269
- const delta = isClickEvent ? calculateDistance(event) : 0;
1270
- if (name === "onPointerDown") {
1271
- internal.initialClick = [event.offsetX, event.offsetY];
1272
- internal.initialHits = hits.map((hit) => hit.eventObject);
1273
- }
1383
+ const delta = isClickEvent ? calculateDistance(event, pointerId) : 0;
1384
+ if (isPointerDown) {
1385
+ const pointerState2 = getPointerState(internal, pointerId);
1386
+ pointerState2.initialClick = [event.offsetX, event.offsetY];
1387
+ pointerState2.initialHits = hits.map((hit) => hit.eventObject);
1388
+ }
1389
+ const pointerState = internal.pointerMap.get(pointerId);
1390
+ const initialHits = pointerState?.initialHits ?? [];
1274
1391
  if (isClickEvent && !hits.length) {
1275
1392
  if (delta <= 2) {
1276
1393
  pointerMissed(event, internal.interaction);
@@ -1285,7 +1402,9 @@ function createEvents(store) {
1285
1402
  dropMissed(event, internal.interaction);
1286
1403
  if (onDropMissed) onDropMissed(event);
1287
1404
  }
1288
- if (isPointerMove || isDragOver) cancelPointer(hits);
1405
+ if (isPointerMove || isDragOver) {
1406
+ cancelPointer(hits, pointerId);
1407
+ }
1289
1408
  function onIntersect(data) {
1290
1409
  const eventObject = data.eventObject;
1291
1410
  const instance = eventObject.__r3f;
@@ -1294,9 +1413,10 @@ function createEvents(store) {
1294
1413
  if (isPointerMove) {
1295
1414
  if (handlers.onPointerOver || handlers.onPointerEnter || handlers.onPointerOut || handlers.onPointerLeave) {
1296
1415
  const id = makeId(data);
1297
- const hoveredItem = internal.hovered.get(id);
1416
+ const pointerState2 = getPointerState(internal, pointerId);
1417
+ const hoveredItem = pointerState2.hovered.get(id);
1298
1418
  if (!hoveredItem) {
1299
- internal.hovered.set(id, data);
1419
+ pointerState2.hovered.set(id, data);
1300
1420
  handlers.onPointerOver?.(data);
1301
1421
  handlers.onPointerEnter?.(data);
1302
1422
  } else if (hoveredItem.stopped) {
@@ -1306,9 +1426,10 @@ function createEvents(store) {
1306
1426
  handlers.onPointerMove?.(data);
1307
1427
  } else if (isDragOver) {
1308
1428
  const id = makeId(data);
1309
- const hoveredItem = internal.hovered.get(id);
1429
+ const pointerState2 = getPointerState(internal, pointerId);
1430
+ const hoveredItem = pointerState2.hovered.get(id);
1310
1431
  if (!hoveredItem) {
1311
- internal.hovered.set(id, data);
1432
+ pointerState2.hovered.set(id, data);
1312
1433
  handlers.onDragOverEnter?.(data);
1313
1434
  } else if (hoveredItem.stopped) {
1314
1435
  data.stopPropagation();
@@ -1319,18 +1440,18 @@ function createEvents(store) {
1319
1440
  } else {
1320
1441
  const handler = handlers[name];
1321
1442
  if (handler) {
1322
- if (!isClickEvent || internal.initialHits.includes(eventObject)) {
1443
+ if (!isClickEvent || initialHits.includes(eventObject)) {
1323
1444
  pointerMissed(
1324
1445
  event,
1325
- internal.interaction.filter((object) => !internal.initialHits.includes(object))
1446
+ internal.interaction.filter((object) => !initialHits.includes(object))
1326
1447
  );
1327
1448
  handler(data);
1328
1449
  }
1329
1450
  } else {
1330
- if (isClickEvent && internal.initialHits.includes(eventObject)) {
1451
+ if (isClickEvent && initialHits.includes(eventObject)) {
1331
1452
  pointerMissed(
1332
1453
  event,
1333
- internal.interaction.filter((object) => !internal.initialHits.includes(object))
1454
+ internal.interaction.filter((object) => !initialHits.includes(object))
1334
1455
  );
1335
1456
  }
1336
1457
  }
@@ -1339,7 +1460,15 @@ function createEvents(store) {
1339
1460
  handleIntersects(hits, event, delta, onIntersect);
1340
1461
  };
1341
1462
  }
1342
- return { handlePointer };
1463
+ function flushDeferredPointers() {
1464
+ const { internal, events } = store.getState();
1465
+ if (!events.frameTimedRaycasts) return;
1466
+ for (const [pointerId, event] of internal.pointerDirty) {
1467
+ processDeferredPointer(event, pointerId);
1468
+ }
1469
+ internal.pointerDirty.clear();
1470
+ }
1471
+ return { handlePointer, flushDeferredPointers, processDeferredPointer };
1343
1472
  }
1344
1473
  const DOM_EVENTS = {
1345
1474
  onClick: ["click", false],
@@ -1358,10 +1487,15 @@ const DOM_EVENTS = {
1358
1487
  onLostPointerCapture: ["lostpointercapture", true]
1359
1488
  };
1360
1489
  function createPointerEvents(store) {
1361
- const { handlePointer } = createEvents(store);
1490
+ const { handlePointer, flushDeferredPointers, processDeferredPointer } = createEvents(store);
1491
+ let nextXRPointerId = XR_POINTER_ID_START;
1492
+ const xrPointers = /* @__PURE__ */ new Map();
1362
1493
  return {
1363
1494
  priority: 1,
1364
1495
  enabled: true,
1496
+ frameTimedRaycasts: true,
1497
+ alwaysFireOnScroll: true,
1498
+ updateOnFrame: false,
1365
1499
  compute(event, state) {
1366
1500
  state.pointer.set(event.offsetX / state.size.width * 2 - 1, -(event.offsetY / state.size.height) * 2 + 1);
1367
1501
  state.raycaster.setFromCamera(state.pointer, state.camera);
@@ -1371,9 +1505,30 @@ function createPointerEvents(store) {
1371
1505
  (acc, key) => ({ ...acc, [key]: handlePointer(key) }),
1372
1506
  {}
1373
1507
  ),
1374
- update: () => {
1508
+ update: (pointerId) => {
1375
1509
  const { events, internal } = store.getState();
1376
- if (internal.lastEvent?.current && events.handlers) events.handlers.onPointerMove(internal.lastEvent.current);
1510
+ if (!events.handlers) return;
1511
+ if (pointerId !== void 0) {
1512
+ const event = internal.pointerDirty.get(pointerId);
1513
+ if (event) {
1514
+ internal.pointerDirty.delete(pointerId);
1515
+ processDeferredPointer(event, pointerId);
1516
+ } else if (internal.lastEvent?.current) {
1517
+ processDeferredPointer(internal.lastEvent.current, pointerId);
1518
+ }
1519
+ } else {
1520
+ flushDeferredPointers();
1521
+ if (internal.lastEvent?.current) {
1522
+ events.handlers.onPointerMove(internal.lastEvent.current);
1523
+ }
1524
+ }
1525
+ },
1526
+ flush: () => {
1527
+ const { events, internal } = store.getState();
1528
+ flushDeferredPointers();
1529
+ if (events.updateOnFrame && internal.lastEvent?.current && events.handlers) {
1530
+ events.handlers.onPointerMove(internal.lastEvent.current);
1531
+ }
1377
1532
  },
1378
1533
  connect: (target) => {
1379
1534
  const { set, events } = store.getState();
@@ -1399,6 +1554,32 @@ function createPointerEvents(store) {
1399
1554
  }
1400
1555
  set((state) => ({ events: { ...state.events, connected: void 0 } }));
1401
1556
  }
1557
+ },
1558
+ registerPointer: (config) => {
1559
+ const pointerId = nextXRPointerId++;
1560
+ xrPointers.set(pointerId, config);
1561
+ const { internal } = store.getState();
1562
+ getPointerState(internal, pointerId);
1563
+ return pointerId;
1564
+ },
1565
+ unregisterPointer: (pointerId) => {
1566
+ xrPointers.delete(pointerId);
1567
+ const { internal } = store.getState();
1568
+ const pointerState = internal.pointerMap.get(pointerId);
1569
+ if (pointerState) {
1570
+ for (const [, hoveredObj] of pointerState.hovered) {
1571
+ const eventObject = hoveredObj.eventObject;
1572
+ const instance = eventObject.__r3f;
1573
+ if (instance?.eventCount) {
1574
+ const handlers = instance.handlers;
1575
+ const data = { ...hoveredObj, intersections: [] };
1576
+ handlers.onPointerOut?.(data);
1577
+ handlers.onPointerLeave?.(data);
1578
+ }
1579
+ }
1580
+ internal.pointerMap.delete(pointerId);
1581
+ }
1582
+ internal.pointerDirty.delete(pointerId);
1402
1583
  }
1403
1584
  };
1404
1585
  }
@@ -2529,7 +2710,14 @@ const createStore = (invalidate, advance) => {
2529
2710
  frustum: new three.Frustum(),
2530
2711
  autoUpdateFrustum: true,
2531
2712
  raycaster: null,
2532
- events: { priority: 1, enabled: true, connected: false },
2713
+ events: {
2714
+ priority: 1,
2715
+ enabled: true,
2716
+ connected: false,
2717
+ frameTimedRaycasts: true,
2718
+ alwaysFireOnScroll: true,
2719
+ updateOnFrame: false
2720
+ },
2533
2721
  scene: null,
2534
2722
  rootScene: null,
2535
2723
  xr: null,
@@ -2620,11 +2808,13 @@ const createStore = (invalidate, advance) => {
2620
2808
  },
2621
2809
  setError: (error) => set(() => ({ error })),
2622
2810
  error: null,
2623
- //* TSL State (managed via hooks: useUniforms, useNodes, useTextures, usePostProcessing) ==============================
2811
+ //* TSL State (managed via hooks: useUniforms, useNodes, useBuffers, useGPUStorage, useTextures, useRenderPipeline) ==============================
2624
2812
  uniforms: {},
2625
2813
  nodes: {},
2814
+ buffers: {},
2815
+ gpuStorage: {},
2626
2816
  textures: /* @__PURE__ */ new Map(),
2627
- postProcessing: null,
2817
+ renderPipeline: null,
2628
2818
  passes: {},
2629
2819
  _hmrVersion: 0,
2630
2820
  _sizeImperative: false,
@@ -2633,12 +2823,16 @@ const createStore = (invalidate, advance) => {
2633
2823
  internal: {
2634
2824
  // Events
2635
2825
  interaction: [],
2636
- hovered: /* @__PURE__ */ new Map(),
2637
2826
  subscribers: [],
2827
+ // Per-pointer state (new unified structure)
2828
+ pointerMap: /* @__PURE__ */ new Map(),
2829
+ pointerDirty: /* @__PURE__ */ new Map(),
2830
+ lastEvent: React__namespace.createRef(),
2831
+ // Deprecated but kept for backwards compatibility
2832
+ hovered: /* @__PURE__ */ new Map(),
2638
2833
  initialClick: [0, 0],
2639
2834
  initialHits: [],
2640
2835
  capturedMap: /* @__PURE__ */ new Map(),
2641
- lastEvent: React__namespace.createRef(),
2642
2836
  // Visibility tracking (onFramed, onOccluded, onVisible)
2643
2837
  visibilityRegistry: /* @__PURE__ */ new Map(),
2644
2838
  // Occlusion system (WebGPU only)
@@ -15248,6 +15442,18 @@ function createRoot(canvas) {
15248
15442
  system: true
15249
15443
  }
15250
15444
  );
15445
+ const unregisterEventsFlush = scheduler.register(
15446
+ () => {
15447
+ const state2 = store.getState();
15448
+ state2.events.flush?.();
15449
+ },
15450
+ {
15451
+ id: `${newRootId}_events`,
15452
+ rootId: newRootId,
15453
+ phase: "input",
15454
+ system: true
15455
+ }
15456
+ );
15251
15457
  const unregisterFrustum = scheduler.register(
15252
15458
  () => {
15253
15459
  const state2 = store.getState();
@@ -15282,7 +15488,7 @@ function createRoot(canvas) {
15282
15488
  const userHandlesRender = scheduler.hasUserJobsInPhase("render", newRootId);
15283
15489
  if (userHandlesRender || state2.internal.priority) return;
15284
15490
  try {
15285
- if (state2.postProcessing?.render) state2.postProcessing.render();
15491
+ if (state2.renderPipeline?.render) state2.renderPipeline.render();
15286
15492
  else if (renderer2?.render) renderer2.render(state2.scene, state2.camera);
15287
15493
  } catch (error) {
15288
15494
  state2.setError(error instanceof Error ? error : new Error(String(error)));
@@ -15307,6 +15513,7 @@ function createRoot(canvas) {
15307
15513
  unregisterRoot: () => {
15308
15514
  unregisterRoot();
15309
15515
  unregisterCanvasTarget();
15516
+ unregisterEventsFlush();
15310
15517
  unregisterFrustum();
15311
15518
  unregisterVisibility();
15312
15519
  unregisterRender();
@@ -15472,9 +15679,13 @@ function PortalInner({ state = {}, children, container }) {
15472
15679
  const store = traditional.createWithEqualityFn((set, get) => ({ ...rest, set, get }));
15473
15680
  const onMutate = (prev) => store.setState((state2) => inject.current(prev, state2));
15474
15681
  onMutate(previousRoot.getState());
15475
- previousRoot.subscribe(onMutate);
15476
15682
  return store;
15477
15683
  }, [previousRoot, container]);
15684
+ useIsomorphicLayoutEffect(() => {
15685
+ const onMutate = (prev) => usePortalStore.setState((state2) => inject.current(prev, state2));
15686
+ const unsubscribe = previousRoot.subscribe(onMutate);
15687
+ return unsubscribe;
15688
+ }, [previousRoot, usePortalStore]);
15478
15689
  return (
15479
15690
  // @ts-ignore, reconciler types are not maintained
15480
15691
  /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: reconciler.createPortal(
@@ -15695,6 +15906,7 @@ function CanvasImpl({
15695
15906
  queueMicrotask(() => {
15696
15907
  const rootEntry = _roots.get(canvas);
15697
15908
  if (rootEntry?.store) {
15909
+ console.log("[R3F] HMR detected \u2014 rebuilding nodes/uniforms");
15698
15910
  rootEntry.store.setState((state) => ({
15699
15911
  nodes: {},
15700
15912
  uniforms: {},
@@ -15706,8 +15918,7 @@ function CanvasImpl({
15706
15918
  if (typeof ({ url: (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('legacy.cjs', document.baseURI).href)) }) !== "undefined" && undefined) {
15707
15919
  const hot = undefined;
15708
15920
  hot.on("vite:afterUpdate", handleHMR);
15709
- return () => hot.dispose?.(() => {
15710
- });
15921
+ return () => hot.off?.("vite:afterUpdate", handleHMR);
15711
15922
  }
15712
15923
  if (typeof module !== "undefined" && module.hot) {
15713
15924
  const hot = module.hot;