@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/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) => {
1494
+ const { events, internal } = store.getState();
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: () => {
1345
1512
  const { events, internal } = store.getState();
1346
- if (internal.lastEvent?.current && events.handlers) events.handlers.onPointerMove(internal.lastEvent.current);
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,13 @@ 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
+ renderPipeline: null,
2598
2804
  passes: {},
2599
2805
  _hmrVersion: 0,
2600
2806
  _sizeImperative: false,
@@ -2603,12 +2809,16 @@ const createStore = (invalidate, advance) => {
2603
2809
  internal: {
2604
2810
  // Events
2605
2811
  interaction: [],
2606
- hovered: /* @__PURE__ */ new Map(),
2607
2812
  subscribers: [],
2813
+ // Per-pointer state (new unified structure)
2814
+ pointerMap: /* @__PURE__ */ new Map(),
2815
+ pointerDirty: /* @__PURE__ */ new Map(),
2816
+ lastEvent: React.createRef(),
2817
+ // Deprecated but kept for backwards compatibility
2818
+ hovered: /* @__PURE__ */ new Map(),
2608
2819
  initialClick: [0, 0],
2609
2820
  initialHits: [],
2610
2821
  capturedMap: /* @__PURE__ */ new Map(),
2611
- lastEvent: React.createRef(),
2612
2822
  // Visibility tracking (onFramed, onOccluded, onVisible)
2613
2823
  visibilityRegistry: /* @__PURE__ */ new Map(),
2614
2824
  // Occlusion system (WebGPU only)
@@ -2696,14 +2906,16 @@ const createStore = (invalidate, advance) => {
2696
2906
  oldSize = size;
2697
2907
  oldDpr = viewport.dpr;
2698
2908
  updateCamera(camera, size);
2699
- if (canvasTarget) {
2909
+ if (internal.isSecondary && canvasTarget) {
2700
2910
  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);
2911
+ canvasTarget.setSize(size.width, size.height, false);
2703
2912
  } else {
2704
2913
  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);
2914
+ actualRenderer.setSize(size.width, size.height, false);
2915
+ if (canvasTarget) {
2916
+ if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
2917
+ canvasTarget.setSize(size.width, size.height, false);
2918
+ }
2707
2919
  }
2708
2920
  }
2709
2921
  if (camera !== oldCamera) {
@@ -14990,7 +15202,6 @@ function createRoot(canvas) {
14990
15202
  events,
14991
15203
  onCreated: onCreatedCallback,
14992
15204
  shadows = false,
14993
- textureColorSpace = SRGBColorSpace,
14994
15205
  orthographic = false,
14995
15206
  frameloop = "always",
14996
15207
  dpr = [1, 2],
@@ -15005,6 +15216,7 @@ function createRoot(canvas) {
15005
15216
  _sizeProps,
15006
15217
  forceEven
15007
15218
  } = props;
15219
+ const textureColorSpace = is.obj(glConfig) && !is.fun(glConfig) && !isRenderer(glConfig) && glConfig.textureColorSpace || is.obj(rendererConfig) && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && rendererConfig.textureColorSpace || SRGBColorSpace;
15008
15220
  const state = store.getState();
15009
15221
  const defaultGLProps = {
15010
15222
  canvas,
@@ -15060,6 +15272,12 @@ function createRoot(canvas) {
15060
15272
  } else if (!wantsGL && !state.internal.actualRenderer) {
15061
15273
  renderer = await resolveRenderer(rendererConfig, defaultGPUProps, WebGPURenderer);
15062
15274
  if (!renderer.hasInitialized?.()) {
15275
+ const size2 = computeInitialSize(canvas, propsSize);
15276
+ if (size2.width > 0 && size2.height > 0) {
15277
+ const pixelRatio = calculateDpr(dpr);
15278
+ canvas.width = size2.width * pixelRatio;
15279
+ canvas.height = size2.height * pixelRatio;
15280
+ }
15063
15281
  await renderer.init();
15064
15282
  }
15065
15283
  const backend = renderer.backend;
@@ -15169,7 +15387,7 @@ function createRoot(canvas) {
15169
15387
  lastConfiguredProps.performance = performance;
15170
15388
  }
15171
15389
  if (!state.xr) {
15172
- const handleXRFrame = (timestamp, frame) => {
15390
+ const handleXRFrame = (timestamp, _frame) => {
15173
15391
  const state2 = store.getState();
15174
15392
  if (state2.frameloop === "never") return;
15175
15393
  advance(timestamp);
@@ -15205,15 +15423,22 @@ function createRoot(canvas) {
15205
15423
  const oldType = renderer.shadowMap.type;
15206
15424
  renderer.shadowMap.enabled = !!shadows;
15207
15425
  if (is.boo(shadows)) {
15208
- renderer.shadowMap.type = PCFSoftShadowMap;
15426
+ renderer.shadowMap.type = PCFShadowMap;
15209
15427
  } else if (is.str(shadows)) {
15428
+ if (shadows === "soft") {
15429
+ notifyDepreciated({
15430
+ heading: 'shadows="soft" is deprecated',
15431
+ body: "Three has depreciated soft and improved basic PCFShadows, we converted for you.",
15432
+ link: "https://github.com/mrdoob/three.js/wiki/Migration-Guide?utm_source=chatgpt.com#181--182"
15433
+ });
15434
+ }
15210
15435
  const types = {
15211
15436
  basic: BasicShadowMap,
15212
15437
  percentage: PCFShadowMap,
15213
- soft: PCFSoftShadowMap,
15438
+ soft: PCFShadowMap,
15214
15439
  variance: VSMShadowMap
15215
15440
  };
15216
- renderer.shadowMap.type = types[shadows] ?? PCFSoftShadowMap;
15441
+ renderer.shadowMap.type = types[shadows] ?? PCFShadowMap;
15217
15442
  } else if (is.obj(shadows)) {
15218
15443
  Object.assign(renderer.shadowMap, shadows);
15219
15444
  }
@@ -15229,13 +15454,24 @@ function createRoot(canvas) {
15229
15454
  if (state.textureColorSpace !== textureColorSpace) state.set(() => ({ textureColorSpace }));
15230
15455
  lastConfiguredProps.textureColorSpace = textureColorSpace;
15231
15456
  }
15457
+ const r3fProps = ["textureColorSpace"];
15458
+ const constructorOnlyProps = ["samples", "antialias", "alpha", "canvas", "powerPreference"];
15459
+ const nonApplyProps = [...r3fProps, ...constructorOnlyProps];
15232
15460
  if (glConfig && !is.fun(glConfig) && !isRenderer(glConfig) && !is.equ(glConfig, renderer, shallowLoose)) {
15233
- applyProps(renderer, glConfig);
15461
+ const glProps = {};
15462
+ for (const key in glConfig) {
15463
+ if (!nonApplyProps.includes(key)) glProps[key] = glConfig[key];
15464
+ }
15465
+ applyProps(renderer, glProps);
15234
15466
  }
15235
15467
  if (rendererConfig && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && state.renderer) {
15236
15468
  const currentRenderer = state.renderer;
15237
15469
  if (!is.equ(rendererConfig, currentRenderer, shallowLoose)) {
15238
- applyProps(currentRenderer, rendererConfig);
15470
+ const rendererProps = {};
15471
+ for (const key in rendererConfig) {
15472
+ if (!nonApplyProps.includes(key)) rendererProps[key] = rendererConfig[key];
15473
+ }
15474
+ applyProps(currentRenderer, rendererProps);
15239
15475
  }
15240
15476
  }
15241
15477
  const scheduler = getScheduler();
@@ -15261,6 +15497,18 @@ function createRoot(canvas) {
15261
15497
  system: true
15262
15498
  }
15263
15499
  );
15500
+ const unregisterEventsFlush = scheduler.register(
15501
+ () => {
15502
+ const state2 = store.getState();
15503
+ state2.events.flush?.();
15504
+ },
15505
+ {
15506
+ id: `${newRootId}_events`,
15507
+ rootId: newRootId,
15508
+ phase: "input",
15509
+ system: true
15510
+ }
15511
+ );
15264
15512
  const unregisterFrustum = scheduler.register(
15265
15513
  () => {
15266
15514
  const state2 = store.getState();
@@ -15295,7 +15543,7 @@ function createRoot(canvas) {
15295
15543
  const userHandlesRender = scheduler.hasUserJobsInPhase("render", newRootId);
15296
15544
  if (userHandlesRender || state2.internal.priority) return;
15297
15545
  try {
15298
- if (state2.postProcessing?.render) state2.postProcessing.render();
15546
+ if (state2.renderPipeline?.render) state2.renderPipeline.render();
15299
15547
  else if (renderer2?.render) renderer2.render(state2.scene, state2.camera);
15300
15548
  } catch (error) {
15301
15549
  state2.setError(error instanceof Error ? error : new Error(String(error)));
@@ -15320,6 +15568,7 @@ function createRoot(canvas) {
15320
15568
  unregisterRoot: () => {
15321
15569
  unregisterRoot();
15322
15570
  unregisterCanvasTarget();
15571
+ unregisterEventsFlush();
15323
15572
  unregisterFrustum();
15324
15573
  unregisterVisibility();
15325
15574
  unregisterRender();
@@ -15485,9 +15734,13 @@ function PortalInner({ state = {}, children, container }) {
15485
15734
  const store = createWithEqualityFn((set, get) => ({ ...rest, set, get }));
15486
15735
  const onMutate = (prev) => store.setState((state2) => inject.current(prev, state2));
15487
15736
  onMutate(previousRoot.getState());
15488
- previousRoot.subscribe(onMutate);
15489
15737
  return store;
15490
15738
  }, [previousRoot, container]);
15739
+ useIsomorphicLayoutEffect(() => {
15740
+ const onMutate = (prev) => usePortalStore.setState((state2) => inject.current(prev, state2));
15741
+ const unsubscribe = previousRoot.subscribe(onMutate);
15742
+ return unsubscribe;
15743
+ }, [previousRoot, usePortalStore]);
15491
15744
  return (
15492
15745
  // @ts-ignore, reconciler types are not maintained
15493
15746
  /* @__PURE__ */ jsx(Fragment, { children: reconciler.createPortal(
@@ -15532,8 +15785,18 @@ function CanvasImpl({
15532
15785
  forceEven,
15533
15786
  ...props
15534
15787
  }) {
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;
15788
+ const isRendererConfig = typeof rendererProp === "object" && rendererProp !== null && !("render" in rendererProp) && ("primaryCanvas" in rendererProp || "scheduler" in rendererProp);
15789
+ let primaryCanvas;
15790
+ let scheduler;
15791
+ let renderer;
15792
+ if (isRendererConfig) {
15793
+ const { primaryCanvas: pc, scheduler: sc, ...rest } = rendererProp;
15794
+ primaryCanvas = pc;
15795
+ scheduler = sc;
15796
+ renderer = Object.keys(rest).length > 0 ? rest : rendererProp;
15797
+ } else {
15798
+ renderer = rendererProp;
15799
+ }
15537
15800
  React.useMemo(() => extend(THREE), []);
15538
15801
  const Bridge = useBridge();
15539
15802
  const backgroundProps = React.useMemo(() => {
@@ -15708,6 +15971,7 @@ function CanvasImpl({
15708
15971
  queueMicrotask(() => {
15709
15972
  const rootEntry = _roots.get(canvas);
15710
15973
  if (rootEntry?.store) {
15974
+ console.log("[R3F] HMR detected \u2014 rebuilding nodes/uniforms");
15711
15975
  rootEntry.store.setState((state) => ({
15712
15976
  nodes: {},
15713
15977
  uniforms: {},
@@ -15719,8 +15983,7 @@ function CanvasImpl({
15719
15983
  if (typeof import.meta !== "undefined" && import.meta.hot) {
15720
15984
  const hot = import.meta.hot;
15721
15985
  hot.on("vite:afterUpdate", handleHMR);
15722
- return () => hot.dispose?.(() => {
15723
- });
15986
+ return () => hot.off?.("vite:afterUpdate", handleHMR);
15724
15987
  }
15725
15988
  if (typeof module !== "undefined" && module.hot) {
15726
15989
  const hot = module.hot;
@@ -15743,7 +16006,16 @@ function CanvasImpl({
15743
16006
  ...style
15744
16007
  },
15745
16008
  ...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 }) })
16009
+ children: /* @__PURE__ */ jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsx(
16010
+ "canvas",
16011
+ {
16012
+ ref: canvasRef,
16013
+ id,
16014
+ className: "r3f-canvas",
16015
+ style: { display: "block", width: "100%", height: "100%" },
16016
+ children: fallback
16017
+ }
16018
+ ) })
15747
16019
  }
15748
16020
  );
15749
16021
  }