@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.
@@ -170,7 +170,7 @@ function useEnvironment({
170
170
  fiber.useLoader.clear(loader, multiFile ? [files] : files);
171
171
  }
172
172
  renderer.domElement.addEventListener("webglcontextlost", clearGainmapTexture, { once: true });
173
- }, [files, renderer.domElement]);
173
+ }, [extension, files, loader, multiFile, renderer.domElement]);
174
174
  const loaderResult = fiber.useLoader(
175
175
  loader,
176
176
  multiFile ? [files] : files,
@@ -370,7 +370,22 @@ function EnvironmentPortal({
370
370
  environmentIntensity,
371
371
  environmentRotation
372
372
  });
373
- }, [children, virtualScene, fbo.texture, scene, defaultScene, background, frames, gl]);
373
+ }, [
374
+ children,
375
+ virtualScene,
376
+ fbo.texture,
377
+ scene,
378
+ defaultScene,
379
+ background,
380
+ frames,
381
+ gl,
382
+ blur,
383
+ backgroundBlurriness,
384
+ backgroundIntensity,
385
+ backgroundRotation,
386
+ environmentIntensity,
387
+ environmentRotation
388
+ ]);
374
389
  let count = 1;
375
390
  fiber.useFrame(() => {
376
391
  if (frames === Infinity || count < frames) {
@@ -1006,6 +1021,9 @@ function applyProps(object, props) {
1006
1021
  else target.set(value);
1007
1022
  } else {
1008
1023
  root[key] = value;
1024
+ if (key.endsWith("Node") && root.isMaterial) {
1025
+ root.needsUpdate = true;
1026
+ }
1009
1027
  if (rootState && rootState.renderer?.outputColorSpace === webgpu.SRGBColorSpace && colorMaps.includes(key) && isTexture(value) && root[key]?.isTexture && // sRGB textures must be RGBA8 since r137 https://github.com/mrdoob/three.js/pull/23129
1010
1028
  root[key].format === webgpu.RGBAFormat && root[key].type === webgpu.UnsignedByteType) {
1011
1029
  root[key].colorSpace = rootState.textureColorSpace;
@@ -1039,38 +1057,60 @@ function applyProps(object, props) {
1039
1057
  return object;
1040
1058
  }
1041
1059
 
1060
+ const DEFAULT_POINTER_ID = 0;
1061
+ const XR_POINTER_ID_START = 1e3;
1062
+ function getPointerState(internal, pointerId) {
1063
+ let state = internal.pointerMap.get(pointerId);
1064
+ if (!state) {
1065
+ state = {
1066
+ hovered: /* @__PURE__ */ new Map(),
1067
+ captured: /* @__PURE__ */ new Map(),
1068
+ initialClick: [0, 0],
1069
+ initialHits: []
1070
+ };
1071
+ internal.pointerMap.set(pointerId, state);
1072
+ }
1073
+ return state;
1074
+ }
1075
+ function getPointerId(event) {
1076
+ return "pointerId" in event ? event.pointerId : DEFAULT_POINTER_ID;
1077
+ }
1042
1078
  function makeId(event) {
1043
1079
  return (event.eventObject || event.object).uuid + "/" + event.index + event.instanceId;
1044
1080
  }
1045
- function releaseInternalPointerCapture(capturedMap, obj, captures, pointerId) {
1046
- const captureData = captures.get(obj);
1081
+ function releaseInternalPointerCapture(internal, obj, pointerId) {
1082
+ const pointerState = internal.pointerMap.get(pointerId);
1083
+ if (!pointerState) return;
1084
+ const captureData = pointerState.captured.get(obj);
1047
1085
  if (captureData) {
1048
- captures.delete(obj);
1049
- if (captures.size === 0) {
1050
- capturedMap.delete(pointerId);
1051
- captureData.target.releasePointerCapture(pointerId);
1052
- }
1086
+ pointerState.captured.delete(obj);
1087
+ captureData.target.releasePointerCapture(pointerId);
1053
1088
  }
1054
1089
  }
1055
1090
  function removeInteractivity(store, object) {
1056
1091
  const { internal } = store.getState();
1057
1092
  internal.interaction = internal.interaction.filter((o) => o !== object);
1058
- internal.initialHits = internal.initialHits.filter((o) => o !== object);
1059
- internal.hovered.forEach((value, key) => {
1060
- if (value.eventObject === object || value.object === object) {
1061
- internal.hovered.delete(key);
1093
+ for (const [pointerId, pointerState] of internal.pointerMap) {
1094
+ pointerState.initialHits = pointerState.initialHits.filter((o) => o !== object);
1095
+ pointerState.hovered.forEach((value, key) => {
1096
+ if (value.eventObject === object || value.object === object) {
1097
+ pointerState.hovered.delete(key);
1098
+ }
1099
+ });
1100
+ if (pointerState.captured.has(object)) {
1101
+ releaseInternalPointerCapture(internal, object, pointerId);
1062
1102
  }
1063
- });
1064
- internal.capturedMap.forEach((captures, pointerId) => {
1065
- releaseInternalPointerCapture(internal.capturedMap, object, captures, pointerId);
1066
- });
1103
+ }
1067
1104
  unregisterVisibility(store, object);
1068
1105
  }
1069
1106
  function createEvents(store) {
1070
- function calculateDistance(event) {
1107
+ function calculateDistance(event, pointerId) {
1071
1108
  const { internal } = store.getState();
1072
- const dx = event.offsetX - internal.initialClick[0];
1073
- const dy = event.offsetY - internal.initialClick[1];
1109
+ const pointerState = internal.pointerMap.get(pointerId);
1110
+ if (!pointerState) return 0;
1111
+ const [initialX, initialY] = pointerState.initialClick;
1112
+ const dx = event.offsetX - initialX;
1113
+ const dy = event.offsetY - initialY;
1074
1114
  return Math.round(Math.sqrt(dx * dx + dy * dy));
1075
1115
  }
1076
1116
  function filterPointerEvents(objects) {
@@ -1106,6 +1146,15 @@ function createEvents(store) {
1106
1146
  return state2.raycaster.camera ? state2.raycaster.intersectObject(obj, true) : [];
1107
1147
  }
1108
1148
  let hits = eventsObjects.flatMap(handleRaycast).sort((a, b) => {
1149
+ const aInteractivePriority = a.object.userData?.interactivePriority;
1150
+ const bInteractivePriority = b.object.userData?.interactivePriority;
1151
+ if (aInteractivePriority !== void 0 || bInteractivePriority !== void 0) {
1152
+ if (aInteractivePriority !== void 0 && bInteractivePriority === void 0) return -1;
1153
+ if (bInteractivePriority !== void 0 && aInteractivePriority === void 0) return 1;
1154
+ if (aInteractivePriority !== bInteractivePriority) {
1155
+ return (bInteractivePriority ?? 0) - (aInteractivePriority ?? 0);
1156
+ }
1157
+ }
1109
1158
  const aState = getRootState(a.object);
1110
1159
  const bState = getRootState(b.object);
1111
1160
  const aPriority = aState?.events?.priority ?? 1;
@@ -1127,9 +1176,13 @@ function createEvents(store) {
1127
1176
  eventObject = eventObject.parent;
1128
1177
  }
1129
1178
  }
1130
- if ("pointerId" in event && state.internal.capturedMap.has(event.pointerId)) {
1131
- for (const captureData of state.internal.capturedMap.get(event.pointerId).values()) {
1132
- if (!duplicates.has(makeId(captureData.intersection))) intersections.push(captureData.intersection);
1179
+ if ("pointerId" in event) {
1180
+ const pointerId = event.pointerId;
1181
+ const pointerState = state.internal.pointerMap.get(pointerId);
1182
+ if (pointerState?.captured.size) {
1183
+ for (const captureData of pointerState.captured.values()) {
1184
+ if (!duplicates.has(makeId(captureData.intersection))) intersections.push(captureData.intersection);
1185
+ }
1133
1186
  }
1134
1187
  }
1135
1188
  return intersections;
@@ -1142,27 +1195,25 @@ function createEvents(store) {
1142
1195
  if (state) {
1143
1196
  const { raycaster, pointer, camera, internal } = state;
1144
1197
  const unprojectedPoint = new webgpu.Vector3(pointer.x, pointer.y, 0).unproject(camera);
1145
- const hasPointerCapture = (id) => internal.capturedMap.get(id)?.has(hit.eventObject) ?? false;
1198
+ const hasPointerCapture = (id) => {
1199
+ const pointerState = internal.pointerMap.get(id);
1200
+ return pointerState?.captured.has(hit.eventObject) ?? false;
1201
+ };
1146
1202
  const setPointerCapture = (id) => {
1147
1203
  const captureData = { intersection: hit, target: event.target };
1148
- if (internal.capturedMap.has(id)) {
1149
- internal.capturedMap.get(id).set(hit.eventObject, captureData);
1150
- } else {
1151
- internal.capturedMap.set(id, /* @__PURE__ */ new Map([[hit.eventObject, captureData]]));
1152
- }
1204
+ const pointerState = getPointerState(internal, id);
1205
+ pointerState.captured.set(hit.eventObject, captureData);
1153
1206
  event.target.setPointerCapture(id);
1154
1207
  };
1155
1208
  const releasePointerCapture = (id) => {
1156
- const captures = internal.capturedMap.get(id);
1157
- if (captures) {
1158
- releaseInternalPointerCapture(internal.capturedMap, hit.eventObject, captures, id);
1159
- }
1209
+ releaseInternalPointerCapture(internal, hit.eventObject, id);
1160
1210
  };
1161
1211
  const extractEventProps = {};
1162
1212
  for (const prop in event) {
1163
1213
  const property = event[prop];
1164
1214
  if (typeof property !== "function") extractEventProps[prop] = property;
1165
1215
  }
1216
+ const eventPointerId = "pointerId" in event ? event.pointerId : void 0;
1166
1217
  const raycastEvent = {
1167
1218
  ...hit,
1168
1219
  ...extractEventProps,
@@ -1173,18 +1224,19 @@ function createEvents(store) {
1173
1224
  unprojectedPoint,
1174
1225
  ray: raycaster.ray,
1175
1226
  camera,
1227
+ pointerId: eventPointerId,
1176
1228
  // Hijack stopPropagation, which just sets a flag
1177
1229
  stopPropagation() {
1178
- const capturesForPointer = "pointerId" in event && internal.capturedMap.get(event.pointerId);
1230
+ const pointerState = eventPointerId !== void 0 ? internal.pointerMap.get(eventPointerId) : void 0;
1179
1231
  if (
1180
1232
  // ...if this pointer hasn't been captured
1181
- !capturesForPointer || // ... or if the hit object is capturing the pointer
1182
- capturesForPointer.has(hit.eventObject)
1233
+ !pointerState?.captured.size || // ... or if the hit object is capturing the pointer
1234
+ pointerState.captured.has(hit.eventObject)
1183
1235
  ) {
1184
1236
  raycastEvent.stopped = localState.stopped = true;
1185
- if (internal.hovered.size && Array.from(internal.hovered.values()).find((i) => i.eventObject === hit.eventObject)) {
1237
+ if (pointerState?.hovered.size && Array.from(pointerState.hovered.values()).find((i) => i.eventObject === hit.eventObject)) {
1186
1238
  const higher = intersections.slice(0, intersections.indexOf(hit));
1187
- cancelPointer([...higher, hit]);
1239
+ cancelPointer([...higher, hit], eventPointerId);
1188
1240
  }
1189
1241
  }
1190
1242
  },
@@ -1200,15 +1252,18 @@ function createEvents(store) {
1200
1252
  }
1201
1253
  return intersections;
1202
1254
  }
1203
- function cancelPointer(intersections) {
1255
+ function cancelPointer(intersections, pointerId) {
1204
1256
  const { internal } = store.getState();
1205
- for (const hoveredObj of internal.hovered.values()) {
1257
+ const pid = pointerId ?? DEFAULT_POINTER_ID;
1258
+ const pointerState = internal.pointerMap.get(pid);
1259
+ if (!pointerState) return;
1260
+ for (const [hoveredId, hoveredObj] of pointerState.hovered) {
1206
1261
  if (!intersections.length || !intersections.find(
1207
1262
  (hit) => hit.object === hoveredObj.object && hit.index === hoveredObj.index && hit.instanceId === hoveredObj.instanceId
1208
1263
  )) {
1209
1264
  const eventObject = hoveredObj.eventObject;
1210
1265
  const instance = eventObject.__r3f;
1211
- internal.hovered.delete(makeId(hoveredObj));
1266
+ pointerState.hovered.delete(hoveredId);
1212
1267
  if (instance?.eventCount) {
1213
1268
  const handlers = instance.handlers;
1214
1269
  const data = { ...hoveredObj, intersections };
@@ -1237,41 +1292,118 @@ function createEvents(store) {
1237
1292
  instance?.handlers.onDropMissed?.(event);
1238
1293
  }
1239
1294
  }
1295
+ function cleanupPointer(pointerId) {
1296
+ const { internal } = store.getState();
1297
+ const pointerState = internal.pointerMap.get(pointerId);
1298
+ if (pointerState) {
1299
+ for (const [, hoveredObj] of pointerState.hovered) {
1300
+ const eventObject = hoveredObj.eventObject;
1301
+ const instance = eventObject.__r3f;
1302
+ if (instance?.eventCount) {
1303
+ const handlers = instance.handlers;
1304
+ const data = { ...hoveredObj, intersections: [] };
1305
+ handlers.onPointerOut?.(data);
1306
+ handlers.onPointerLeave?.(data);
1307
+ }
1308
+ }
1309
+ internal.pointerMap.delete(pointerId);
1310
+ }
1311
+ internal.pointerDirty.delete(pointerId);
1312
+ }
1313
+ function processDeferredPointer(event, pointerId) {
1314
+ const state = store.getState();
1315
+ const { internal } = state;
1316
+ if (!state.events.enabled) return;
1317
+ const filter = filterPointerEvents;
1318
+ const hits = intersect(event, filter);
1319
+ cancelPointer(hits, pointerId);
1320
+ function onIntersect(data) {
1321
+ const eventObject = data.eventObject;
1322
+ const instance = eventObject.__r3f;
1323
+ if (!instance?.eventCount) return;
1324
+ const handlers = instance.handlers;
1325
+ if (handlers.onPointerOver || handlers.onPointerEnter || handlers.onPointerOut || handlers.onPointerLeave) {
1326
+ const id = makeId(data);
1327
+ const pointerState = getPointerState(internal, pointerId);
1328
+ const hoveredItem = pointerState.hovered.get(id);
1329
+ if (!hoveredItem) {
1330
+ pointerState.hovered.set(id, data);
1331
+ handlers.onPointerOver?.(data);
1332
+ handlers.onPointerEnter?.(data);
1333
+ } else if (hoveredItem.stopped) {
1334
+ data.stopPropagation();
1335
+ }
1336
+ }
1337
+ handlers.onPointerMove?.(data);
1338
+ }
1339
+ handleIntersects(hits, event, 0, onIntersect);
1340
+ }
1240
1341
  function handlePointer(name) {
1241
1342
  switch (name) {
1242
1343
  case "onPointerLeave":
1243
- case "onPointerCancel":
1244
1344
  case "onDragLeave":
1245
1345
  return () => cancelPointer([]);
1346
+ // Global cancel of these events
1347
+ case "onPointerCancel":
1348
+ return (event) => {
1349
+ const pointerId = getPointerId(event);
1350
+ cleanupPointer(pointerId);
1351
+ };
1246
1352
  case "onLostPointerCapture":
1247
1353
  return (event) => {
1248
1354
  const { internal } = store.getState();
1249
- if ("pointerId" in event && internal.capturedMap.has(event.pointerId)) {
1355
+ const pointerId = getPointerId(event);
1356
+ const pointerState = internal.pointerMap.get(pointerId);
1357
+ if (pointerState?.captured.size) {
1250
1358
  requestAnimationFrame(() => {
1251
- if (internal.capturedMap.has(event.pointerId)) {
1252
- internal.capturedMap.delete(event.pointerId);
1253
- cancelPointer([]);
1359
+ const pointerState2 = internal.pointerMap.get(pointerId);
1360
+ if (pointerState2?.captured.size) {
1361
+ pointerState2.captured.clear();
1254
1362
  }
1363
+ cancelPointer([], pointerId);
1255
1364
  });
1256
1365
  }
1257
1366
  };
1258
1367
  }
1259
1368
  return function handleEvent(event) {
1260
1369
  const state = store.getState();
1261
- const { onPointerMissed, onDragOverMissed, onDropMissed, internal } = state;
1370
+ const { onPointerMissed, onDragOverMissed, onDropMissed, internal, events } = state;
1371
+ const pointerId = getPointerId(event);
1262
1372
  internal.lastEvent.current = event;
1263
- if (!state.events.enabled) return;
1373
+ if (!events.enabled) return;
1264
1374
  const isPointerMove = name === "onPointerMove";
1265
1375
  const isDragOver = name === "onDragOver";
1266
1376
  const isDrop = name === "onDrop";
1267
1377
  const isClickEvent = name === "onClick" || name === "onContextMenu" || name === "onDoubleClick";
1378
+ const isPointerDown = name === "onPointerDown";
1379
+ const isPointerUp = name === "onPointerUp";
1380
+ const isWheel = name === "onWheel";
1381
+ const canDeferRaycasts = events.frameTimedRaycasts && state.frameloop === "always";
1382
+ if (isPointerMove && canDeferRaycasts) {
1383
+ events.compute?.(event, state);
1384
+ internal.pointerDirty.set(pointerId, event);
1385
+ return;
1386
+ }
1387
+ if (isWheel && canDeferRaycasts && !events.alwaysFireOnScroll) {
1388
+ events.compute?.(event, state);
1389
+ internal.pointerDirty.set(pointerId, event);
1390
+ return;
1391
+ }
1392
+ if ((isClickEvent || isPointerDown || isPointerUp) && internal.pointerDirty.has(pointerId)) {
1393
+ const deferredEvent = internal.pointerDirty.get(pointerId);
1394
+ internal.pointerDirty.delete(pointerId);
1395
+ processDeferredPointer(deferredEvent, pointerId);
1396
+ }
1268
1397
  const filter = isPointerMove || isDragOver || isDrop ? filterPointerEvents : void 0;
1269
1398
  const hits = intersect(event, filter);
1270
- const delta = isClickEvent ? calculateDistance(event) : 0;
1271
- if (name === "onPointerDown") {
1272
- internal.initialClick = [event.offsetX, event.offsetY];
1273
- internal.initialHits = hits.map((hit) => hit.eventObject);
1274
- }
1399
+ const delta = isClickEvent ? calculateDistance(event, pointerId) : 0;
1400
+ if (isPointerDown) {
1401
+ const pointerState2 = getPointerState(internal, pointerId);
1402
+ pointerState2.initialClick = [event.offsetX, event.offsetY];
1403
+ pointerState2.initialHits = hits.map((hit) => hit.eventObject);
1404
+ }
1405
+ const pointerState = internal.pointerMap.get(pointerId);
1406
+ const initialHits = pointerState?.initialHits ?? [];
1275
1407
  if (isClickEvent && !hits.length) {
1276
1408
  if (delta <= 2) {
1277
1409
  pointerMissed(event, internal.interaction);
@@ -1286,7 +1418,9 @@ function createEvents(store) {
1286
1418
  dropMissed(event, internal.interaction);
1287
1419
  if (onDropMissed) onDropMissed(event);
1288
1420
  }
1289
- if (isPointerMove || isDragOver) cancelPointer(hits);
1421
+ if (isPointerMove || isDragOver) {
1422
+ cancelPointer(hits, pointerId);
1423
+ }
1290
1424
  function onIntersect(data) {
1291
1425
  const eventObject = data.eventObject;
1292
1426
  const instance = eventObject.__r3f;
@@ -1295,9 +1429,10 @@ function createEvents(store) {
1295
1429
  if (isPointerMove) {
1296
1430
  if (handlers.onPointerOver || handlers.onPointerEnter || handlers.onPointerOut || handlers.onPointerLeave) {
1297
1431
  const id = makeId(data);
1298
- const hoveredItem = internal.hovered.get(id);
1432
+ const pointerState2 = getPointerState(internal, pointerId);
1433
+ const hoveredItem = pointerState2.hovered.get(id);
1299
1434
  if (!hoveredItem) {
1300
- internal.hovered.set(id, data);
1435
+ pointerState2.hovered.set(id, data);
1301
1436
  handlers.onPointerOver?.(data);
1302
1437
  handlers.onPointerEnter?.(data);
1303
1438
  } else if (hoveredItem.stopped) {
@@ -1307,9 +1442,10 @@ function createEvents(store) {
1307
1442
  handlers.onPointerMove?.(data);
1308
1443
  } else if (isDragOver) {
1309
1444
  const id = makeId(data);
1310
- const hoveredItem = internal.hovered.get(id);
1445
+ const pointerState2 = getPointerState(internal, pointerId);
1446
+ const hoveredItem = pointerState2.hovered.get(id);
1311
1447
  if (!hoveredItem) {
1312
- internal.hovered.set(id, data);
1448
+ pointerState2.hovered.set(id, data);
1313
1449
  handlers.onDragOverEnter?.(data);
1314
1450
  } else if (hoveredItem.stopped) {
1315
1451
  data.stopPropagation();
@@ -1320,18 +1456,18 @@ function createEvents(store) {
1320
1456
  } else {
1321
1457
  const handler = handlers[name];
1322
1458
  if (handler) {
1323
- if (!isClickEvent || internal.initialHits.includes(eventObject)) {
1459
+ if (!isClickEvent || initialHits.includes(eventObject)) {
1324
1460
  pointerMissed(
1325
1461
  event,
1326
- internal.interaction.filter((object) => !internal.initialHits.includes(object))
1462
+ internal.interaction.filter((object) => !initialHits.includes(object))
1327
1463
  );
1328
1464
  handler(data);
1329
1465
  }
1330
1466
  } else {
1331
- if (isClickEvent && internal.initialHits.includes(eventObject)) {
1467
+ if (isClickEvent && initialHits.includes(eventObject)) {
1332
1468
  pointerMissed(
1333
1469
  event,
1334
- internal.interaction.filter((object) => !internal.initialHits.includes(object))
1470
+ internal.interaction.filter((object) => !initialHits.includes(object))
1335
1471
  );
1336
1472
  }
1337
1473
  }
@@ -1340,7 +1476,15 @@ function createEvents(store) {
1340
1476
  handleIntersects(hits, event, delta, onIntersect);
1341
1477
  };
1342
1478
  }
1343
- return { handlePointer };
1479
+ function flushDeferredPointers() {
1480
+ const { internal, events } = store.getState();
1481
+ if (!events.frameTimedRaycasts) return;
1482
+ for (const [pointerId, event] of internal.pointerDirty) {
1483
+ processDeferredPointer(event, pointerId);
1484
+ }
1485
+ internal.pointerDirty.clear();
1486
+ }
1487
+ return { handlePointer, flushDeferredPointers, processDeferredPointer };
1344
1488
  }
1345
1489
  const DOM_EVENTS = {
1346
1490
  onClick: ["click", false],
@@ -1359,10 +1503,15 @@ const DOM_EVENTS = {
1359
1503
  onLostPointerCapture: ["lostpointercapture", true]
1360
1504
  };
1361
1505
  function createPointerEvents(store) {
1362
- const { handlePointer } = createEvents(store);
1506
+ const { handlePointer, flushDeferredPointers, processDeferredPointer } = createEvents(store);
1507
+ let nextXRPointerId = XR_POINTER_ID_START;
1508
+ const xrPointers = /* @__PURE__ */ new Map();
1363
1509
  return {
1364
1510
  priority: 1,
1365
1511
  enabled: true,
1512
+ frameTimedRaycasts: true,
1513
+ alwaysFireOnScroll: true,
1514
+ updateOnFrame: false,
1366
1515
  compute(event, state) {
1367
1516
  state.pointer.set(event.offsetX / state.size.width * 2 - 1, -(event.offsetY / state.size.height) * 2 + 1);
1368
1517
  state.raycaster.setFromCamera(state.pointer, state.camera);
@@ -1372,11 +1521,33 @@ function createPointerEvents(store) {
1372
1521
  (acc, key) => ({ ...acc, [key]: handlePointer(key) }),
1373
1522
  {}
1374
1523
  ),
1375
- update: () => {
1524
+ update: (pointerId) => {
1376
1525
  const { events, internal } = store.getState();
1377
- if (internal.lastEvent?.current && events.handlers) events.handlers.onPointerMove(internal.lastEvent.current);
1526
+ if (!events.handlers) return;
1527
+ if (pointerId !== void 0) {
1528
+ const event = internal.pointerDirty.get(pointerId);
1529
+ if (event) {
1530
+ internal.pointerDirty.delete(pointerId);
1531
+ processDeferredPointer(event, pointerId);
1532
+ } else if (internal.lastEvent?.current) {
1533
+ processDeferredPointer(internal.lastEvent.current, pointerId);
1534
+ }
1535
+ } else {
1536
+ flushDeferredPointers();
1537
+ if (internal.lastEvent?.current) {
1538
+ events.handlers.onPointerMove(internal.lastEvent.current);
1539
+ }
1540
+ }
1541
+ },
1542
+ flush: () => {
1543
+ const { events, internal } = store.getState();
1544
+ flushDeferredPointers();
1545
+ if (events.updateOnFrame && internal.lastEvent?.current && events.handlers) {
1546
+ events.handlers.onPointerMove(internal.lastEvent.current);
1547
+ }
1378
1548
  },
1379
1549
  connect: (target) => {
1550
+ if (!target) return;
1380
1551
  const { set, events } = store.getState();
1381
1552
  events.disconnect?.();
1382
1553
  set((state) => ({ events: { ...state.events, connected: target } }));
@@ -1400,6 +1571,32 @@ function createPointerEvents(store) {
1400
1571
  }
1401
1572
  set((state) => ({ events: { ...state.events, connected: void 0 } }));
1402
1573
  }
1574
+ },
1575
+ registerPointer: (config) => {
1576
+ const pointerId = nextXRPointerId++;
1577
+ xrPointers.set(pointerId, config);
1578
+ const { internal } = store.getState();
1579
+ getPointerState(internal, pointerId);
1580
+ return pointerId;
1581
+ },
1582
+ unregisterPointer: (pointerId) => {
1583
+ xrPointers.delete(pointerId);
1584
+ const { internal } = store.getState();
1585
+ const pointerState = internal.pointerMap.get(pointerId);
1586
+ if (pointerState) {
1587
+ for (const [, hoveredObj] of pointerState.hovered) {
1588
+ const eventObject = hoveredObj.eventObject;
1589
+ const instance = eventObject.__r3f;
1590
+ if (instance?.eventCount) {
1591
+ const handlers = instance.handlers;
1592
+ const data = { ...hoveredObj, intersections: [] };
1593
+ handlers.onPointerOut?.(data);
1594
+ handlers.onPointerLeave?.(data);
1595
+ }
1596
+ }
1597
+ internal.pointerMap.delete(pointerId);
1598
+ }
1599
+ internal.pointerDirty.delete(pointerId);
1403
1600
  }
1404
1601
  };
1405
1602
  }
@@ -1713,7 +1910,7 @@ function shouldRun(job, now) {
1713
1910
  const minInterval = 1e3 / job.fps;
1714
1911
  const lastRun = job.lastRun ?? 0;
1715
1912
  const elapsed = now - lastRun;
1716
- if (elapsed < minInterval) return false;
1913
+ if (elapsed < minInterval - 1) return false;
1717
1914
  if (job.drop) {
1718
1915
  job.lastRun = now;
1719
1916
  } else {
@@ -2530,7 +2727,14 @@ const createStore = (invalidate, advance) => {
2530
2727
  frustum: new webgpu.Frustum(),
2531
2728
  autoUpdateFrustum: true,
2532
2729
  raycaster: null,
2533
- events: { priority: 1, enabled: true, connected: false },
2730
+ events: {
2731
+ priority: 1,
2732
+ enabled: true,
2733
+ connected: false,
2734
+ frameTimedRaycasts: true,
2735
+ alwaysFireOnScroll: true,
2736
+ updateOnFrame: false
2737
+ },
2534
2738
  scene: null,
2535
2739
  rootScene: null,
2536
2740
  xr: null,
@@ -2621,11 +2825,13 @@ const createStore = (invalidate, advance) => {
2621
2825
  },
2622
2826
  setError: (error) => set(() => ({ error })),
2623
2827
  error: null,
2624
- //* TSL State (managed via hooks: useUniforms, useNodes, useTextures, usePostProcessing) ==============================
2828
+ //* TSL State (managed via hooks: useUniforms, useNodes, useBuffers, useGPUStorage, useTextures, useRenderPipeline) ==============================
2625
2829
  uniforms: {},
2626
2830
  nodes: {},
2831
+ buffers: {},
2832
+ gpuStorage: {},
2627
2833
  textures: /* @__PURE__ */ new Map(),
2628
- postProcessing: null,
2834
+ renderPipeline: null,
2629
2835
  passes: {},
2630
2836
  _hmrVersion: 0,
2631
2837
  _sizeImperative: false,
@@ -2634,12 +2840,16 @@ const createStore = (invalidate, advance) => {
2634
2840
  internal: {
2635
2841
  // Events
2636
2842
  interaction: [],
2637
- hovered: /* @__PURE__ */ new Map(),
2638
2843
  subscribers: [],
2844
+ // Per-pointer state (new unified structure)
2845
+ pointerMap: /* @__PURE__ */ new Map(),
2846
+ pointerDirty: /* @__PURE__ */ new Map(),
2847
+ lastEvent: React__namespace.createRef(),
2848
+ // Deprecated but kept for backwards compatibility
2849
+ hovered: /* @__PURE__ */ new Map(),
2639
2850
  initialClick: [0, 0],
2640
2851
  initialHits: [],
2641
2852
  capturedMap: /* @__PURE__ */ new Map(),
2642
- lastEvent: React__namespace.createRef(),
2643
2853
  // Visibility tracking (onFramed, onOccluded, onVisible)
2644
2854
  visibilityRegistry: /* @__PURE__ */ new Map(),
2645
2855
  // Occlusion system (WebGPU only)
@@ -2727,14 +2937,16 @@ const createStore = (invalidate, advance) => {
2727
2937
  oldSize = size;
2728
2938
  oldDpr = viewport.dpr;
2729
2939
  updateCamera(camera, size);
2730
- if (canvasTarget) {
2940
+ if (internal.isSecondary && canvasTarget) {
2731
2941
  if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
2732
- const updateStyle = typeof HTMLCanvasElement !== "undefined" && canvasTarget.domElement instanceof HTMLCanvasElement;
2733
- canvasTarget.setSize(size.width, size.height, updateStyle);
2942
+ canvasTarget.setSize(size.width, size.height, false);
2734
2943
  } else {
2735
2944
  if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
2736
- const updateStyle = typeof HTMLCanvasElement !== "undefined" && actualRenderer.domElement instanceof HTMLCanvasElement;
2737
- actualRenderer.setSize(size.width, size.height, updateStyle);
2945
+ actualRenderer.setSize(size.width, size.height, false);
2946
+ if (canvasTarget) {
2947
+ if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
2948
+ canvasTarget.setSize(size.width, size.height, false);
2949
+ }
2738
2950
  }
2739
2951
  }
2740
2952
  if (camera !== oldCamera) {
@@ -15019,7 +15231,6 @@ function createRoot(canvas) {
15019
15231
  events,
15020
15232
  onCreated: onCreatedCallback,
15021
15233
  shadows = false,
15022
- textureColorSpace = webgpu.SRGBColorSpace,
15023
15234
  orthographic = false,
15024
15235
  frameloop = "always",
15025
15236
  dpr = [1, 2],
@@ -15034,6 +15245,7 @@ function createRoot(canvas) {
15034
15245
  _sizeProps,
15035
15246
  forceEven
15036
15247
  } = props;
15248
+ const textureColorSpace = is.obj(glConfig) && !is.fun(glConfig) && !isRenderer(glConfig) && glConfig.textureColorSpace || is.obj(rendererConfig) && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && rendererConfig.textureColorSpace || webgpu.SRGBColorSpace;
15037
15249
  const state = store.getState();
15038
15250
  const defaultGPUProps = {
15039
15251
  canvas,
@@ -15071,6 +15283,12 @@ function createRoot(canvas) {
15071
15283
  } else if (!state.internal.actualRenderer) {
15072
15284
  renderer = await resolveRenderer(rendererConfig, defaultGPUProps, webgpu.WebGPURenderer);
15073
15285
  if (!renderer.hasInitialized?.()) {
15286
+ const size2 = computeInitialSize(canvas, propsSize);
15287
+ if (size2.width > 0 && size2.height > 0) {
15288
+ const pixelRatio = calculateDpr(dpr);
15289
+ canvas.width = size2.width * pixelRatio;
15290
+ canvas.height = size2.height * pixelRatio;
15291
+ }
15074
15292
  await renderer.init();
15075
15293
  }
15076
15294
  const backend = renderer.backend;
@@ -15180,7 +15398,7 @@ function createRoot(canvas) {
15180
15398
  lastConfiguredProps.performance = performance;
15181
15399
  }
15182
15400
  if (!state.xr) {
15183
- const handleXRFrame = (timestamp, frame) => {
15401
+ const handleXRFrame = (timestamp, _frame) => {
15184
15402
  const state2 = store.getState();
15185
15403
  if (state2.frameloop === "never") return;
15186
15404
  advance(timestamp);
@@ -15216,15 +15434,22 @@ function createRoot(canvas) {
15216
15434
  const oldType = renderer.shadowMap.type;
15217
15435
  renderer.shadowMap.enabled = !!shadows;
15218
15436
  if (is.boo(shadows)) {
15219
- renderer.shadowMap.type = webgpu.PCFSoftShadowMap;
15437
+ renderer.shadowMap.type = webgpu.PCFShadowMap;
15220
15438
  } else if (is.str(shadows)) {
15439
+ if (shadows === "soft") {
15440
+ notifyDepreciated({
15441
+ heading: 'shadows="soft" is deprecated',
15442
+ body: "Three has depreciated soft and improved basic PCFShadows, we converted for you.",
15443
+ link: "https://github.com/mrdoob/three.js/wiki/Migration-Guide?utm_source=chatgpt.com#181--182"
15444
+ });
15445
+ }
15221
15446
  const types = {
15222
15447
  basic: webgpu.BasicShadowMap,
15223
15448
  percentage: webgpu.PCFShadowMap,
15224
- soft: webgpu.PCFSoftShadowMap,
15449
+ soft: webgpu.PCFShadowMap,
15225
15450
  variance: webgpu.VSMShadowMap
15226
15451
  };
15227
- renderer.shadowMap.type = types[shadows] ?? webgpu.PCFSoftShadowMap;
15452
+ renderer.shadowMap.type = types[shadows] ?? webgpu.PCFShadowMap;
15228
15453
  } else if (is.obj(shadows)) {
15229
15454
  Object.assign(renderer.shadowMap, shadows);
15230
15455
  }
@@ -15240,13 +15465,24 @@ function createRoot(canvas) {
15240
15465
  if (state.textureColorSpace !== textureColorSpace) state.set(() => ({ textureColorSpace }));
15241
15466
  lastConfiguredProps.textureColorSpace = textureColorSpace;
15242
15467
  }
15468
+ const r3fProps = ["textureColorSpace"];
15469
+ const constructorOnlyProps = ["samples", "antialias", "alpha", "canvas", "powerPreference"];
15470
+ const nonApplyProps = [...r3fProps, ...constructorOnlyProps];
15243
15471
  if (glConfig && !is.fun(glConfig) && !isRenderer(glConfig) && !is.equ(glConfig, renderer, shallowLoose)) {
15244
- applyProps(renderer, glConfig);
15472
+ const glProps = {};
15473
+ for (const key in glConfig) {
15474
+ if (!nonApplyProps.includes(key)) glProps[key] = glConfig[key];
15475
+ }
15476
+ applyProps(renderer, glProps);
15245
15477
  }
15246
15478
  if (rendererConfig && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && state.renderer) {
15247
15479
  const currentRenderer = state.renderer;
15248
15480
  if (!is.equ(rendererConfig, currentRenderer, shallowLoose)) {
15249
- applyProps(currentRenderer, rendererConfig);
15481
+ const rendererProps = {};
15482
+ for (const key in rendererConfig) {
15483
+ if (!nonApplyProps.includes(key)) rendererProps[key] = rendererConfig[key];
15484
+ }
15485
+ applyProps(currentRenderer, rendererProps);
15250
15486
  }
15251
15487
  }
15252
15488
  const scheduler = getScheduler();
@@ -15272,6 +15508,18 @@ function createRoot(canvas) {
15272
15508
  system: true
15273
15509
  }
15274
15510
  );
15511
+ const unregisterEventsFlush = scheduler.register(
15512
+ () => {
15513
+ const state2 = store.getState();
15514
+ state2.events.flush?.();
15515
+ },
15516
+ {
15517
+ id: `${newRootId}_events`,
15518
+ rootId: newRootId,
15519
+ phase: "input",
15520
+ system: true
15521
+ }
15522
+ );
15275
15523
  const unregisterFrustum = scheduler.register(
15276
15524
  () => {
15277
15525
  const state2 = store.getState();
@@ -15306,7 +15554,7 @@ function createRoot(canvas) {
15306
15554
  const userHandlesRender = scheduler.hasUserJobsInPhase("render", newRootId);
15307
15555
  if (userHandlesRender || state2.internal.priority) return;
15308
15556
  try {
15309
- if (state2.postProcessing?.render) state2.postProcessing.render();
15557
+ if (state2.renderPipeline?.render) state2.renderPipeline.render();
15310
15558
  else if (renderer2?.render) renderer2.render(state2.scene, state2.camera);
15311
15559
  } catch (error) {
15312
15560
  state2.setError(error instanceof Error ? error : new Error(String(error)));
@@ -15331,6 +15579,7 @@ function createRoot(canvas) {
15331
15579
  unregisterRoot: () => {
15332
15580
  unregisterRoot();
15333
15581
  unregisterCanvasTarget();
15582
+ unregisterEventsFlush();
15334
15583
  unregisterFrustum();
15335
15584
  unregisterVisibility();
15336
15585
  unregisterRender();
@@ -15496,9 +15745,13 @@ function PortalInner({ state = {}, children, container }) {
15496
15745
  const store = traditional.createWithEqualityFn((set, get) => ({ ...rest, set, get }));
15497
15746
  const onMutate = (prev) => store.setState((state2) => inject.current(prev, state2));
15498
15747
  onMutate(previousRoot.getState());
15499
- previousRoot.subscribe(onMutate);
15500
15748
  return store;
15501
15749
  }, [previousRoot, container]);
15750
+ useIsomorphicLayoutEffect(() => {
15751
+ const onMutate = (prev) => usePortalStore.setState((state2) => inject.current(prev, state2));
15752
+ const unsubscribe = previousRoot.subscribe(onMutate);
15753
+ return unsubscribe;
15754
+ }, [previousRoot, usePortalStore]);
15502
15755
  return (
15503
15756
  // @ts-ignore, reconciler types are not maintained
15504
15757
  /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: reconciler.createPortal(
@@ -15543,8 +15796,18 @@ function CanvasImpl({
15543
15796
  forceEven,
15544
15797
  ...props
15545
15798
  }) {
15546
- 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 };
15547
- const renderer = Object.keys(rendererConfig).length > 0 ? rendererConfig : rendererProp;
15799
+ const isRendererConfig = typeof rendererProp === "object" && rendererProp !== null && !("render" in rendererProp) && ("primaryCanvas" in rendererProp || "scheduler" in rendererProp);
15800
+ let primaryCanvas;
15801
+ let scheduler;
15802
+ let renderer;
15803
+ if (isRendererConfig) {
15804
+ const { primaryCanvas: pc, scheduler: sc, ...rest } = rendererProp;
15805
+ primaryCanvas = pc;
15806
+ scheduler = sc;
15807
+ renderer = Object.keys(rest).length > 0 ? rest : rendererProp;
15808
+ } else {
15809
+ renderer = rendererProp;
15810
+ }
15548
15811
  React__namespace.useMemo(() => extend(THREE), []);
15549
15812
  const Bridge = useBridge();
15550
15813
  const backgroundProps = React__namespace.useMemo(() => {
@@ -15719,6 +15982,7 @@ function CanvasImpl({
15719
15982
  queueMicrotask(() => {
15720
15983
  const rootEntry = _roots.get(canvas);
15721
15984
  if (rootEntry?.store) {
15985
+ console.log("[R3F] HMR detected \u2014 rebuilding nodes/uniforms");
15722
15986
  rootEntry.store.setState((state) => ({
15723
15987
  nodes: {},
15724
15988
  uniforms: {},
@@ -15730,8 +15994,7 @@ function CanvasImpl({
15730
15994
  if (typeof ({ url: (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)) }) !== "undefined" && undefined) {
15731
15995
  const hot = undefined;
15732
15996
  hot.on("vite:afterUpdate", handleHMR);
15733
- return () => hot.dispose?.(() => {
15734
- });
15997
+ return () => hot.off?.("vite:afterUpdate", handleHMR);
15735
15998
  }
15736
15999
  if (typeof module !== "undefined" && module.hot) {
15737
16000
  const hot = module.hot;
@@ -15754,7 +16017,16 @@ function CanvasImpl({
15754
16017
  ...style
15755
16018
  },
15756
16019
  ...props,
15757
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx("canvas", { ref: canvasRef, id, className: "r3f-canvas", style: { display: "block" }, children: fallback }) })
16020
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx(
16021
+ "canvas",
16022
+ {
16023
+ ref: canvasRef,
16024
+ id,
16025
+ className: "r3f-canvas",
16026
+ style: { display: "block", width: "100%", height: "100%" },
16027
+ children: fallback
16028
+ }
16029
+ ) })
15758
16030
  }
15759
16031
  );
15760
16032
  }
@@ -15830,6 +16102,8 @@ function createScopedStore(data) {
15830
16102
  function createLazyCreatorState(state) {
15831
16103
  let _uniforms = null;
15832
16104
  let _nodes = null;
16105
+ let _buffers = null;
16106
+ let _gpuStorage = null;
15833
16107
  return Object.create(state, {
15834
16108
  uniforms: {
15835
16109
  get() {
@@ -15840,6 +16114,16 @@ function createLazyCreatorState(state) {
15840
16114
  get() {
15841
16115
  return _nodes ?? (_nodes = createScopedStore(state.nodes));
15842
16116
  }
16117
+ },
16118
+ buffers: {
16119
+ get() {
16120
+ return _buffers ?? (_buffers = createScopedStore(state.buffers));
16121
+ }
16122
+ },
16123
+ gpuStorage: {
16124
+ get() {
16125
+ return _gpuStorage ?? (_gpuStorage = createScopedStore(state.gpuStorage));
16126
+ }
15843
16127
  }
15844
16128
  });
15845
16129
  }
@@ -15922,6 +16206,10 @@ function vectorize(inObject) {
15922
16206
  if (obj.isVector2 || obj.isVector3 || obj.isVector4) return inObject;
15923
16207
  if (obj.isMatrix3 || obj.isMatrix4) return inObject;
15924
16208
  if (obj.isColor || obj.isEuler || obj.isQuaternion || obj.isSpherical) return inObject;
16209
+ if ("r" in obj && "g" in obj && "b" in obj && typeof obj.r === "number" && typeof obj.g === "number" && typeof obj.b === "number") {
16210
+ const scale = obj.r > 1 || obj.g > 1 || obj.b > 1 ? 1 / 255 : 1;
16211
+ return new webgpu.Color(obj.r * scale, obj.g * scale, obj.b * scale);
16212
+ }
15925
16213
  if ("x" in obj && "y" in obj && typeof obj.x === "number" && typeof obj.y === "number") {
15926
16214
  if ("w" in obj && typeof obj.w === "number" && "z" in obj && typeof obj.z === "number") {
15927
16215
  return new webgpu.Vector4(obj.x, obj.y, obj.z, obj.w);
@@ -16377,7 +16665,351 @@ function useLocalNodes(creator) {
16377
16665
  }, [store, creator, uniforms, nodes, textures, hmrVersion]);
16378
16666
  }
16379
16667
 
16380
- function usePostProcessing(mainCB, setupCB) {
16668
+ const isBufferLike = (value) => {
16669
+ if (value === null || typeof value !== "object") return false;
16670
+ if (ArrayBuffer.isView(value)) return true;
16671
+ if ("isBufferAttribute" in value) return true;
16672
+ if ("uuid" in value || "nodeType" in value) return true;
16673
+ return false;
16674
+ };
16675
+ const disposeBuffer = (buffer) => {
16676
+ if (buffer === null || typeof buffer !== "object") return;
16677
+ if ("dispose" in buffer && typeof buffer.dispose === "function") {
16678
+ buffer.dispose();
16679
+ }
16680
+ };
16681
+ function useBuffers(creatorOrScope, scope) {
16682
+ const store = useStore();
16683
+ const removeBuffers = React.useCallback(
16684
+ (names, targetScope) => {
16685
+ const nameArray = Array.isArray(names) ? names : [names];
16686
+ store.setState((state) => {
16687
+ if (targetScope) {
16688
+ const currentScope = { ...state.buffers[targetScope] };
16689
+ for (const name of nameArray) delete currentScope[name];
16690
+ return { buffers: { ...state.buffers, [targetScope]: currentScope } };
16691
+ }
16692
+ const buffers2 = { ...state.buffers };
16693
+ for (const name of nameArray) if (isBufferLike(buffers2[name])) delete buffers2[name];
16694
+ return { buffers: buffers2 };
16695
+ });
16696
+ },
16697
+ [store]
16698
+ );
16699
+ const clearBuffers = React.useCallback(
16700
+ (targetScope) => {
16701
+ store.setState((state) => {
16702
+ if (targetScope && targetScope !== "root") {
16703
+ const { [targetScope]: _, ...rest } = state.buffers;
16704
+ return { buffers: rest };
16705
+ }
16706
+ if (targetScope === "root") {
16707
+ const buffers2 = {};
16708
+ for (const [key, value] of Object.entries(state.buffers)) {
16709
+ if (!isBufferLike(value)) buffers2[key] = value;
16710
+ }
16711
+ return { buffers: buffers2 };
16712
+ }
16713
+ return { buffers: {} };
16714
+ });
16715
+ },
16716
+ [store]
16717
+ );
16718
+ const rebuildBuffers = React.useCallback(
16719
+ (targetScope) => {
16720
+ store.setState((state) => {
16721
+ let newBuffers = state.buffers;
16722
+ if (targetScope && targetScope !== "root") {
16723
+ const { [targetScope]: _, ...rest } = state.buffers;
16724
+ newBuffers = rest;
16725
+ } else if (targetScope === "root") {
16726
+ newBuffers = {};
16727
+ for (const [key, value] of Object.entries(state.buffers)) {
16728
+ if (!isBufferLike(value)) newBuffers[key] = value;
16729
+ }
16730
+ } else {
16731
+ newBuffers = {};
16732
+ }
16733
+ return { buffers: newBuffers, _hmrVersion: state._hmrVersion + 1 };
16734
+ });
16735
+ },
16736
+ [store]
16737
+ );
16738
+ const disposeBuffers = React.useCallback(
16739
+ (names, targetScope) => {
16740
+ const nameArray = Array.isArray(names) ? names : [names];
16741
+ const state = store.getState();
16742
+ for (const name of nameArray) {
16743
+ const buffer = targetScope ? state.buffers[targetScope]?.[name] : state.buffers[name];
16744
+ if (buffer && isBufferLike(buffer)) {
16745
+ disposeBuffer(buffer);
16746
+ }
16747
+ }
16748
+ removeBuffers(names, targetScope);
16749
+ },
16750
+ [store, removeBuffers]
16751
+ );
16752
+ const isReader = creatorOrScope === void 0 || typeof creatorOrScope === "string";
16753
+ const storeBuffers = useThree((s) => s.buffers);
16754
+ const hmrVersion = useThree((s) => s._hmrVersion);
16755
+ const scopeDep = typeof creatorOrScope === "string" ? creatorOrScope : scope;
16756
+ const readerDep = isReader ? storeBuffers : null;
16757
+ const creatorDep = isReader ? null : hmrVersion;
16758
+ const buffers = React.useMemo(() => {
16759
+ if (creatorOrScope === void 0) {
16760
+ return storeBuffers;
16761
+ }
16762
+ if (typeof creatorOrScope === "string") {
16763
+ const scopeData = storeBuffers[creatorOrScope];
16764
+ if (scopeData && !isBufferLike(scopeData)) return scopeData;
16765
+ return {};
16766
+ }
16767
+ const state = store.getState();
16768
+ const set = store.setState;
16769
+ const creator = creatorOrScope;
16770
+ const wrappedState = createLazyCreatorState(state);
16771
+ const created = creator(wrappedState);
16772
+ const result = {};
16773
+ let hasNewBuffers = false;
16774
+ if (scope) {
16775
+ const currentScope = state.buffers[scope] ?? {};
16776
+ for (const [name, buffer] of Object.entries(created)) {
16777
+ if (currentScope[name]) {
16778
+ result[name] = currentScope[name];
16779
+ } else {
16780
+ if ("setName" in buffer && typeof buffer.setName === "function") {
16781
+ buffer.setName(`${scope}.${name}`);
16782
+ }
16783
+ result[name] = buffer;
16784
+ hasNewBuffers = true;
16785
+ }
16786
+ }
16787
+ if (hasNewBuffers) {
16788
+ set((s) => ({
16789
+ buffers: {
16790
+ ...s.buffers,
16791
+ [scope]: { ...s.buffers[scope], ...result }
16792
+ }
16793
+ }));
16794
+ }
16795
+ return result;
16796
+ }
16797
+ for (const [name, buffer] of Object.entries(created)) {
16798
+ const existing = state.buffers[name];
16799
+ if (existing && isBufferLike(existing)) {
16800
+ result[name] = existing;
16801
+ } else {
16802
+ if ("setName" in buffer && typeof buffer.setName === "function") {
16803
+ buffer.setName(name);
16804
+ }
16805
+ result[name] = buffer;
16806
+ hasNewBuffers = true;
16807
+ }
16808
+ }
16809
+ if (hasNewBuffers) {
16810
+ set((s) => ({ buffers: { ...s.buffers, ...result } }));
16811
+ }
16812
+ return result;
16813
+ }, [store, scopeDep, readerDep, creatorDep]);
16814
+ return { ...buffers, removeBuffers, clearBuffers, rebuildBuffers, disposeBuffers };
16815
+ }
16816
+ function rebuildAllBuffers(store, scope) {
16817
+ store.setState((state) => {
16818
+ let newBuffers = state.buffers;
16819
+ if (scope && scope !== "root") {
16820
+ const { [scope]: _, ...rest } = state.buffers;
16821
+ newBuffers = rest;
16822
+ } else if (scope === "root") {
16823
+ newBuffers = {};
16824
+ for (const [key, value] of Object.entries(state.buffers)) {
16825
+ if (!isBufferLike(value)) newBuffers[key] = value;
16826
+ }
16827
+ } else {
16828
+ newBuffers = {};
16829
+ }
16830
+ return { buffers: newBuffers, _hmrVersion: state._hmrVersion + 1 };
16831
+ });
16832
+ }
16833
+
16834
+ const isStorageLike = (value) => {
16835
+ if (value === null || typeof value !== "object") return false;
16836
+ if ("isTexture" in value) return true;
16837
+ if ("isData3DTexture" in value) return true;
16838
+ if ("uuid" in value || "nodeType" in value) return true;
16839
+ return false;
16840
+ };
16841
+ const disposeStorage = (storage) => {
16842
+ if (storage === null || typeof storage !== "object") return;
16843
+ if ("dispose" in storage && typeof storage.dispose === "function") {
16844
+ storage.dispose();
16845
+ }
16846
+ };
16847
+ function useGPUStorage(creatorOrScope, scope) {
16848
+ const store = useStore();
16849
+ const removeStorage = React.useCallback(
16850
+ (names, targetScope) => {
16851
+ const nameArray = Array.isArray(names) ? names : [names];
16852
+ store.setState((state) => {
16853
+ if (targetScope) {
16854
+ const currentScope = { ...state.gpuStorage[targetScope] };
16855
+ for (const name of nameArray) delete currentScope[name];
16856
+ return { gpuStorage: { ...state.gpuStorage, [targetScope]: currentScope } };
16857
+ }
16858
+ const gpuStorage2 = { ...state.gpuStorage };
16859
+ for (const name of nameArray) if (isStorageLike(gpuStorage2[name])) delete gpuStorage2[name];
16860
+ return { gpuStorage: gpuStorage2 };
16861
+ });
16862
+ },
16863
+ [store]
16864
+ );
16865
+ const clearStorage = React.useCallback(
16866
+ (targetScope) => {
16867
+ store.setState((state) => {
16868
+ if (targetScope && targetScope !== "root") {
16869
+ const { [targetScope]: _, ...rest } = state.gpuStorage;
16870
+ return { gpuStorage: rest };
16871
+ }
16872
+ if (targetScope === "root") {
16873
+ const gpuStorage2 = {};
16874
+ for (const [key, value] of Object.entries(state.gpuStorage)) {
16875
+ if (!isStorageLike(value)) gpuStorage2[key] = value;
16876
+ }
16877
+ return { gpuStorage: gpuStorage2 };
16878
+ }
16879
+ return { gpuStorage: {} };
16880
+ });
16881
+ },
16882
+ [store]
16883
+ );
16884
+ const rebuildStorage = React.useCallback(
16885
+ (targetScope) => {
16886
+ store.setState((state) => {
16887
+ let newStorage = state.gpuStorage;
16888
+ if (targetScope && targetScope !== "root") {
16889
+ const { [targetScope]: _, ...rest } = state.gpuStorage;
16890
+ newStorage = rest;
16891
+ } else if (targetScope === "root") {
16892
+ newStorage = {};
16893
+ for (const [key, value] of Object.entries(state.gpuStorage)) {
16894
+ if (!isStorageLike(value)) newStorage[key] = value;
16895
+ }
16896
+ } else {
16897
+ newStorage = {};
16898
+ }
16899
+ return { gpuStorage: newStorage, _hmrVersion: state._hmrVersion + 1 };
16900
+ });
16901
+ },
16902
+ [store]
16903
+ );
16904
+ const disposeStorageFn = React.useCallback(
16905
+ (names, targetScope) => {
16906
+ const nameArray = Array.isArray(names) ? names : [names];
16907
+ const state = store.getState();
16908
+ for (const name of nameArray) {
16909
+ const storage = targetScope ? state.gpuStorage[targetScope]?.[name] : state.gpuStorage[name];
16910
+ if (storage && isStorageLike(storage)) {
16911
+ disposeStorage(storage);
16912
+ }
16913
+ }
16914
+ removeStorage(names, targetScope);
16915
+ },
16916
+ [store, removeStorage]
16917
+ );
16918
+ const isReader = creatorOrScope === void 0 || typeof creatorOrScope === "string";
16919
+ const storeStorage = useThree((s) => s.gpuStorage);
16920
+ const hmrVersion = useThree((s) => s._hmrVersion);
16921
+ const scopeDep = typeof creatorOrScope === "string" ? creatorOrScope : scope;
16922
+ const readerDep = isReader ? storeStorage : null;
16923
+ const creatorDep = isReader ? null : hmrVersion;
16924
+ const gpuStorage = React.useMemo(() => {
16925
+ if (creatorOrScope === void 0) {
16926
+ return storeStorage;
16927
+ }
16928
+ if (typeof creatorOrScope === "string") {
16929
+ const scopeData = storeStorage[creatorOrScope];
16930
+ if (scopeData && !isStorageLike(scopeData)) return scopeData;
16931
+ return {};
16932
+ }
16933
+ const state = store.getState();
16934
+ const set = store.setState;
16935
+ const creator = creatorOrScope;
16936
+ const wrappedState = createLazyCreatorState(state);
16937
+ const created = creator(wrappedState);
16938
+ const result = {};
16939
+ let hasNewStorage = false;
16940
+ if (scope) {
16941
+ const currentScope = state.gpuStorage[scope] ?? {};
16942
+ for (const [name, storage] of Object.entries(created)) {
16943
+ if (currentScope[name]) {
16944
+ result[name] = currentScope[name];
16945
+ } else {
16946
+ if ("setName" in storage && typeof storage.setName === "function") {
16947
+ storage.setName(`${scope}.${name}`);
16948
+ }
16949
+ if ("name" in storage && typeof storage.name === "string") {
16950
+ storage.name = `${scope}.${name}`;
16951
+ }
16952
+ result[name] = storage;
16953
+ hasNewStorage = true;
16954
+ }
16955
+ }
16956
+ if (hasNewStorage) {
16957
+ set((s) => ({
16958
+ gpuStorage: {
16959
+ ...s.gpuStorage,
16960
+ [scope]: { ...s.gpuStorage[scope], ...result }
16961
+ }
16962
+ }));
16963
+ }
16964
+ return result;
16965
+ }
16966
+ for (const [name, storage] of Object.entries(created)) {
16967
+ const existing = state.gpuStorage[name];
16968
+ if (existing && isStorageLike(existing)) {
16969
+ result[name] = existing;
16970
+ } else {
16971
+ if ("setName" in storage && typeof storage.setName === "function") {
16972
+ storage.setName(name);
16973
+ }
16974
+ if ("name" in storage && typeof storage.name === "string") {
16975
+ storage.name = name;
16976
+ }
16977
+ result[name] = storage;
16978
+ hasNewStorage = true;
16979
+ }
16980
+ }
16981
+ if (hasNewStorage) {
16982
+ set((s) => ({ gpuStorage: { ...s.gpuStorage, ...result } }));
16983
+ }
16984
+ return result;
16985
+ }, [store, scopeDep, readerDep, creatorDep]);
16986
+ return {
16987
+ ...gpuStorage,
16988
+ removeStorage,
16989
+ clearStorage,
16990
+ rebuildStorage,
16991
+ disposeStorage: disposeStorageFn
16992
+ };
16993
+ }
16994
+ function rebuildAllStorage(store, scope) {
16995
+ store.setState((state) => {
16996
+ let newStorage = state.gpuStorage;
16997
+ if (scope && scope !== "root") {
16998
+ const { [scope]: _, ...rest } = state.gpuStorage;
16999
+ newStorage = rest;
17000
+ } else if (scope === "root") {
17001
+ newStorage = {};
17002
+ for (const [key, value] of Object.entries(state.gpuStorage)) {
17003
+ if (!isStorageLike(value)) newStorage[key] = value;
17004
+ }
17005
+ } else {
17006
+ newStorage = {};
17007
+ }
17008
+ return { gpuStorage: newStorage, _hmrVersion: state._hmrVersion + 1 };
17009
+ });
17010
+ }
17011
+
17012
+ function useRenderPipeline(mainCB, setupCB) {
16381
17013
  const store = useStore();
16382
17014
  const { scene, camera, renderer, isLegacy } = useThree();
16383
17015
  const callbacksRanRef = React.useRef(false);
@@ -16396,7 +17028,7 @@ function usePostProcessing(mainCB, setupCB) {
16396
17028
  }, [store]);
16397
17029
  const reset = React.useCallback(() => {
16398
17030
  store.setState({
16399
- postProcessing: null,
17031
+ renderPipeline: null,
16400
17032
  passes: {}
16401
17033
  });
16402
17034
  callbacksRanRef.current = false;
@@ -16409,13 +17041,13 @@ function usePostProcessing(mainCB, setupCB) {
16409
17041
  }, []);
16410
17042
  React.useLayoutEffect(() => {
16411
17043
  if (isLegacy) {
16412
- throw new Error("usePostProcessing is only available with WebGPU renderer. Set renderer prop on Canvas.");
17044
+ throw new Error("useRenderPipeline is only available with WebGPU renderer. Set renderer prop on Canvas.");
16413
17045
  }
16414
17046
  if (!renderer || !scene || !camera) return;
16415
17047
  const state = store.getState();
16416
17048
  const set = store.setState;
16417
17049
  try {
16418
- let pp = state.postProcessing;
17050
+ let pp = state.renderPipeline;
16419
17051
  let currentPasses = { ...state.passes };
16420
17052
  let justCreatedPP = false;
16421
17053
  if (!pp) {
@@ -16432,7 +17064,7 @@ function usePostProcessing(mainCB, setupCB) {
16432
17064
  }
16433
17065
  currentPasses.scenePass = scenePass;
16434
17066
  if (!pp.outputNode || justCreatedPP) pp.outputNode = scenePass;
16435
- set({ postProcessing: pp, passes: currentPasses });
17067
+ set({ renderPipeline: pp, passes: currentPasses });
16436
17068
  const shouldRunCallbacks = justCreatedPP || !callbacksRanRef.current || !cacheValid;
16437
17069
  if (shouldRunCallbacks) {
16438
17070
  if (setupCBRef.current) {
@@ -16454,19 +17086,19 @@ function usePostProcessing(mainCB, setupCB) {
16454
17086
  callbacksRanRef.current = true;
16455
17087
  }
16456
17088
  } catch (error) {
16457
- console.error("[usePostProcessing] Setup error:", error);
17089
+ console.error("[useRenderPipeline] Setup error:", error);
16458
17090
  }
16459
17091
  }, [store, renderer, scene, camera, isLegacy, rebuildVersion]);
16460
17092
  const passes = useThree((s) => s.passes);
16461
- const postProcessing = useThree((s) => s.postProcessing);
17093
+ const renderPipeline = useThree((s) => s.renderPipeline);
16462
17094
  return {
16463
17095
  passes,
16464
- postProcessing,
17096
+ renderPipeline,
16465
17097
  clearPasses,
16466
17098
  reset,
16467
17099
  rebuild,
16468
- // isReady indicates if PostProcessing is configured and ready for rendering
16469
- isReady: postProcessing !== null
17100
+ // isReady indicates if RenderPipeline is configured and ready for rendering
17101
+ isReady: renderPipeline !== null
16470
17102
  };
16471
17103
  }
16472
17104
 
@@ -16543,7 +17175,9 @@ exports.isVectorLike = isVectorLike;
16543
17175
  exports.once = once;
16544
17176
  exports.prepare = prepare;
16545
17177
  exports.presetsObj = presetsObj;
17178
+ exports.rebuildAllBuffers = rebuildAllBuffers;
16546
17179
  exports.rebuildAllNodes = rebuildAllNodes;
17180
+ exports.rebuildAllStorage = rebuildAllStorage;
16547
17181
  exports.rebuildAllUniforms = rebuildAllUniforms;
16548
17182
  exports.reconciler = reconciler;
16549
17183
  exports.registerPrimary = registerPrimary;
@@ -16556,8 +17190,10 @@ exports.unregisterPrimary = unregisterPrimary;
16556
17190
  exports.updateCamera = updateCamera;
16557
17191
  exports.updateFrustum = updateFrustum;
16558
17192
  exports.useBridge = useBridge;
17193
+ exports.useBuffers = useBuffers;
16559
17194
  exports.useEnvironment = useEnvironment;
16560
17195
  exports.useFrame = useFrame;
17196
+ exports.useGPUStorage = useGPUStorage;
16561
17197
  exports.useGraph = useGraph;
16562
17198
  exports.useInstanceHandle = useInstanceHandle;
16563
17199
  exports.useIsomorphicLayoutEffect = useIsomorphicLayoutEffect;
@@ -16565,7 +17201,7 @@ exports.useLoader = useLoader;
16565
17201
  exports.useLocalNodes = useLocalNodes;
16566
17202
  exports.useMutableCallback = useMutableCallback;
16567
17203
  exports.useNodes = useNodes;
16568
- exports.usePostProcessing = usePostProcessing;
17204
+ exports.useRenderPipeline = useRenderPipeline;
16569
17205
  exports.useRenderTarget = useRenderTarget;
16570
17206
  exports.useStore = useStore;
16571
17207
  exports.useTexture = useTexture;