@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.
@@ -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, Vector4, PostProcessing } 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, Vector4, PostProcessing } from 'three/webgpu';
3
3
  import { Inspector } from 'three/addons/inspector/Inspector.js';
4
4
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
5
5
  import * as React from 'react';
@@ -149,7 +149,7 @@ function useEnvironment({
149
149
  useLoader$1.clear(loader, multiFile ? [files] : files);
150
150
  }
151
151
  renderer.domElement.addEventListener("webglcontextlost", clearGainmapTexture, { once: true });
152
- }, [files, renderer.domElement]);
152
+ }, [extension, files, loader, multiFile, renderer.domElement]);
153
153
  const loaderResult = useLoader$1(
154
154
  loader,
155
155
  multiFile ? [files] : files,
@@ -349,7 +349,22 @@ function EnvironmentPortal({
349
349
  environmentIntensity,
350
350
  environmentRotation
351
351
  });
352
- }, [children, virtualScene, fbo.texture, scene, defaultScene, background, frames, gl]);
352
+ }, [
353
+ children,
354
+ virtualScene,
355
+ fbo.texture,
356
+ scene,
357
+ defaultScene,
358
+ background,
359
+ frames,
360
+ gl,
361
+ blur,
362
+ backgroundBlurriness,
363
+ backgroundIntensity,
364
+ backgroundRotation,
365
+ environmentIntensity,
366
+ environmentRotation
367
+ ]);
353
368
  let count = 1;
354
369
  useFrame$1(() => {
355
370
  if (frames === Infinity || count < frames) {
@@ -985,6 +1000,9 @@ function applyProps(object, props) {
985
1000
  else target.set(value);
986
1001
  } else {
987
1002
  root[key] = value;
1003
+ if (key.endsWith("Node") && root.isMaterial) {
1004
+ root.needsUpdate = true;
1005
+ }
988
1006
  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
989
1007
  root[key].format === RGBAFormat && root[key].type === UnsignedByteType) {
990
1008
  root[key].colorSpace = rootState.textureColorSpace;
@@ -1018,38 +1036,60 @@ function applyProps(object, props) {
1018
1036
  return object;
1019
1037
  }
1020
1038
 
1039
+ const DEFAULT_POINTER_ID = 0;
1040
+ const XR_POINTER_ID_START = 1e3;
1041
+ function getPointerState(internal, pointerId) {
1042
+ let state = internal.pointerMap.get(pointerId);
1043
+ if (!state) {
1044
+ state = {
1045
+ hovered: /* @__PURE__ */ new Map(),
1046
+ captured: /* @__PURE__ */ new Map(),
1047
+ initialClick: [0, 0],
1048
+ initialHits: []
1049
+ };
1050
+ internal.pointerMap.set(pointerId, state);
1051
+ }
1052
+ return state;
1053
+ }
1054
+ function getPointerId(event) {
1055
+ return "pointerId" in event ? event.pointerId : DEFAULT_POINTER_ID;
1056
+ }
1021
1057
  function makeId(event) {
1022
1058
  return (event.eventObject || event.object).uuid + "/" + event.index + event.instanceId;
1023
1059
  }
1024
- function releaseInternalPointerCapture(capturedMap, obj, captures, pointerId) {
1025
- const captureData = captures.get(obj);
1060
+ function releaseInternalPointerCapture(internal, obj, pointerId) {
1061
+ const pointerState = internal.pointerMap.get(pointerId);
1062
+ if (!pointerState) return;
1063
+ const captureData = pointerState.captured.get(obj);
1026
1064
  if (captureData) {
1027
- captures.delete(obj);
1028
- if (captures.size === 0) {
1029
- capturedMap.delete(pointerId);
1030
- captureData.target.releasePointerCapture(pointerId);
1031
- }
1065
+ pointerState.captured.delete(obj);
1066
+ captureData.target.releasePointerCapture(pointerId);
1032
1067
  }
1033
1068
  }
1034
1069
  function removeInteractivity(store, object) {
1035
1070
  const { internal } = store.getState();
1036
1071
  internal.interaction = internal.interaction.filter((o) => o !== object);
1037
- internal.initialHits = internal.initialHits.filter((o) => o !== object);
1038
- internal.hovered.forEach((value, key) => {
1039
- if (value.eventObject === object || value.object === object) {
1040
- internal.hovered.delete(key);
1072
+ for (const [pointerId, pointerState] of internal.pointerMap) {
1073
+ pointerState.initialHits = pointerState.initialHits.filter((o) => o !== object);
1074
+ pointerState.hovered.forEach((value, key) => {
1075
+ if (value.eventObject === object || value.object === object) {
1076
+ pointerState.hovered.delete(key);
1077
+ }
1078
+ });
1079
+ if (pointerState.captured.has(object)) {
1080
+ releaseInternalPointerCapture(internal, object, pointerId);
1041
1081
  }
1042
- });
1043
- internal.capturedMap.forEach((captures, pointerId) => {
1044
- releaseInternalPointerCapture(internal.capturedMap, object, captures, pointerId);
1045
- });
1082
+ }
1046
1083
  unregisterVisibility(store, object);
1047
1084
  }
1048
1085
  function createEvents(store) {
1049
- function calculateDistance(event) {
1086
+ function calculateDistance(event, pointerId) {
1050
1087
  const { internal } = store.getState();
1051
- const dx = event.offsetX - internal.initialClick[0];
1052
- const dy = event.offsetY - internal.initialClick[1];
1088
+ const pointerState = internal.pointerMap.get(pointerId);
1089
+ if (!pointerState) return 0;
1090
+ const [initialX, initialY] = pointerState.initialClick;
1091
+ const dx = event.offsetX - initialX;
1092
+ const dy = event.offsetY - initialY;
1053
1093
  return Math.round(Math.sqrt(dx * dx + dy * dy));
1054
1094
  }
1055
1095
  function filterPointerEvents(objects) {
@@ -1085,6 +1125,15 @@ function createEvents(store) {
1085
1125
  return state2.raycaster.camera ? state2.raycaster.intersectObject(obj, true) : [];
1086
1126
  }
1087
1127
  let hits = eventsObjects.flatMap(handleRaycast).sort((a, b) => {
1128
+ const aInteractivePriority = a.object.userData?.interactivePriority;
1129
+ const bInteractivePriority = b.object.userData?.interactivePriority;
1130
+ if (aInteractivePriority !== void 0 || bInteractivePriority !== void 0) {
1131
+ if (aInteractivePriority !== void 0 && bInteractivePriority === void 0) return -1;
1132
+ if (bInteractivePriority !== void 0 && aInteractivePriority === void 0) return 1;
1133
+ if (aInteractivePriority !== bInteractivePriority) {
1134
+ return (bInteractivePriority ?? 0) - (aInteractivePriority ?? 0);
1135
+ }
1136
+ }
1088
1137
  const aState = getRootState(a.object);
1089
1138
  const bState = getRootState(b.object);
1090
1139
  const aPriority = aState?.events?.priority ?? 1;
@@ -1106,9 +1155,13 @@ function createEvents(store) {
1106
1155
  eventObject = eventObject.parent;
1107
1156
  }
1108
1157
  }
1109
- if ("pointerId" in event && state.internal.capturedMap.has(event.pointerId)) {
1110
- for (const captureData of state.internal.capturedMap.get(event.pointerId).values()) {
1111
- if (!duplicates.has(makeId(captureData.intersection))) intersections.push(captureData.intersection);
1158
+ if ("pointerId" in event) {
1159
+ const pointerId = event.pointerId;
1160
+ const pointerState = state.internal.pointerMap.get(pointerId);
1161
+ if (pointerState?.captured.size) {
1162
+ for (const captureData of pointerState.captured.values()) {
1163
+ if (!duplicates.has(makeId(captureData.intersection))) intersections.push(captureData.intersection);
1164
+ }
1112
1165
  }
1113
1166
  }
1114
1167
  return intersections;
@@ -1121,27 +1174,25 @@ function createEvents(store) {
1121
1174
  if (state) {
1122
1175
  const { raycaster, pointer, camera, internal } = state;
1123
1176
  const unprojectedPoint = new Vector3(pointer.x, pointer.y, 0).unproject(camera);
1124
- const hasPointerCapture = (id) => internal.capturedMap.get(id)?.has(hit.eventObject) ?? false;
1177
+ const hasPointerCapture = (id) => {
1178
+ const pointerState = internal.pointerMap.get(id);
1179
+ return pointerState?.captured.has(hit.eventObject) ?? false;
1180
+ };
1125
1181
  const setPointerCapture = (id) => {
1126
1182
  const captureData = { intersection: hit, target: event.target };
1127
- if (internal.capturedMap.has(id)) {
1128
- internal.capturedMap.get(id).set(hit.eventObject, captureData);
1129
- } else {
1130
- internal.capturedMap.set(id, /* @__PURE__ */ new Map([[hit.eventObject, captureData]]));
1131
- }
1183
+ const pointerState = getPointerState(internal, id);
1184
+ pointerState.captured.set(hit.eventObject, captureData);
1132
1185
  event.target.setPointerCapture(id);
1133
1186
  };
1134
1187
  const releasePointerCapture = (id) => {
1135
- const captures = internal.capturedMap.get(id);
1136
- if (captures) {
1137
- releaseInternalPointerCapture(internal.capturedMap, hit.eventObject, captures, id);
1138
- }
1188
+ releaseInternalPointerCapture(internal, hit.eventObject, id);
1139
1189
  };
1140
1190
  const extractEventProps = {};
1141
1191
  for (const prop in event) {
1142
1192
  const property = event[prop];
1143
1193
  if (typeof property !== "function") extractEventProps[prop] = property;
1144
1194
  }
1195
+ const eventPointerId = "pointerId" in event ? event.pointerId : void 0;
1145
1196
  const raycastEvent = {
1146
1197
  ...hit,
1147
1198
  ...extractEventProps,
@@ -1152,18 +1203,19 @@ function createEvents(store) {
1152
1203
  unprojectedPoint,
1153
1204
  ray: raycaster.ray,
1154
1205
  camera,
1206
+ pointerId: eventPointerId,
1155
1207
  // Hijack stopPropagation, which just sets a flag
1156
1208
  stopPropagation() {
1157
- const capturesForPointer = "pointerId" in event && internal.capturedMap.get(event.pointerId);
1209
+ const pointerState = eventPointerId !== void 0 ? internal.pointerMap.get(eventPointerId) : void 0;
1158
1210
  if (
1159
1211
  // ...if this pointer hasn't been captured
1160
- !capturesForPointer || // ... or if the hit object is capturing the pointer
1161
- capturesForPointer.has(hit.eventObject)
1212
+ !pointerState?.captured.size || // ... or if the hit object is capturing the pointer
1213
+ pointerState.captured.has(hit.eventObject)
1162
1214
  ) {
1163
1215
  raycastEvent.stopped = localState.stopped = true;
1164
- if (internal.hovered.size && Array.from(internal.hovered.values()).find((i) => i.eventObject === hit.eventObject)) {
1216
+ if (pointerState?.hovered.size && Array.from(pointerState.hovered.values()).find((i) => i.eventObject === hit.eventObject)) {
1165
1217
  const higher = intersections.slice(0, intersections.indexOf(hit));
1166
- cancelPointer([...higher, hit]);
1218
+ cancelPointer([...higher, hit], eventPointerId);
1167
1219
  }
1168
1220
  }
1169
1221
  },
@@ -1179,15 +1231,18 @@ function createEvents(store) {
1179
1231
  }
1180
1232
  return intersections;
1181
1233
  }
1182
- function cancelPointer(intersections) {
1234
+ function cancelPointer(intersections, pointerId) {
1183
1235
  const { internal } = store.getState();
1184
- for (const hoveredObj of internal.hovered.values()) {
1236
+ const pid = pointerId ?? DEFAULT_POINTER_ID;
1237
+ const pointerState = internal.pointerMap.get(pid);
1238
+ if (!pointerState) return;
1239
+ for (const [hoveredId, hoveredObj] of pointerState.hovered) {
1185
1240
  if (!intersections.length || !intersections.find(
1186
1241
  (hit) => hit.object === hoveredObj.object && hit.index === hoveredObj.index && hit.instanceId === hoveredObj.instanceId
1187
1242
  )) {
1188
1243
  const eventObject = hoveredObj.eventObject;
1189
1244
  const instance = eventObject.__r3f;
1190
- internal.hovered.delete(makeId(hoveredObj));
1245
+ pointerState.hovered.delete(hoveredId);
1191
1246
  if (instance?.eventCount) {
1192
1247
  const handlers = instance.handlers;
1193
1248
  const data = { ...hoveredObj, intersections };
@@ -1216,41 +1271,118 @@ function createEvents(store) {
1216
1271
  instance?.handlers.onDropMissed?.(event);
1217
1272
  }
1218
1273
  }
1274
+ function cleanupPointer(pointerId) {
1275
+ const { internal } = store.getState();
1276
+ const pointerState = internal.pointerMap.get(pointerId);
1277
+ if (pointerState) {
1278
+ for (const [, hoveredObj] of pointerState.hovered) {
1279
+ const eventObject = hoveredObj.eventObject;
1280
+ const instance = eventObject.__r3f;
1281
+ if (instance?.eventCount) {
1282
+ const handlers = instance.handlers;
1283
+ const data = { ...hoveredObj, intersections: [] };
1284
+ handlers.onPointerOut?.(data);
1285
+ handlers.onPointerLeave?.(data);
1286
+ }
1287
+ }
1288
+ internal.pointerMap.delete(pointerId);
1289
+ }
1290
+ internal.pointerDirty.delete(pointerId);
1291
+ }
1292
+ function processDeferredPointer(event, pointerId) {
1293
+ const state = store.getState();
1294
+ const { internal } = state;
1295
+ if (!state.events.enabled) return;
1296
+ const filter = filterPointerEvents;
1297
+ const hits = intersect(event, filter);
1298
+ cancelPointer(hits, pointerId);
1299
+ function onIntersect(data) {
1300
+ const eventObject = data.eventObject;
1301
+ const instance = eventObject.__r3f;
1302
+ if (!instance?.eventCount) return;
1303
+ const handlers = instance.handlers;
1304
+ if (handlers.onPointerOver || handlers.onPointerEnter || handlers.onPointerOut || handlers.onPointerLeave) {
1305
+ const id = makeId(data);
1306
+ const pointerState = getPointerState(internal, pointerId);
1307
+ const hoveredItem = pointerState.hovered.get(id);
1308
+ if (!hoveredItem) {
1309
+ pointerState.hovered.set(id, data);
1310
+ handlers.onPointerOver?.(data);
1311
+ handlers.onPointerEnter?.(data);
1312
+ } else if (hoveredItem.stopped) {
1313
+ data.stopPropagation();
1314
+ }
1315
+ }
1316
+ handlers.onPointerMove?.(data);
1317
+ }
1318
+ handleIntersects(hits, event, 0, onIntersect);
1319
+ }
1219
1320
  function handlePointer(name) {
1220
1321
  switch (name) {
1221
1322
  case "onPointerLeave":
1222
- case "onPointerCancel":
1223
1323
  case "onDragLeave":
1224
1324
  return () => cancelPointer([]);
1325
+ // Global cancel of these events
1326
+ case "onPointerCancel":
1327
+ return (event) => {
1328
+ const pointerId = getPointerId(event);
1329
+ cleanupPointer(pointerId);
1330
+ };
1225
1331
  case "onLostPointerCapture":
1226
1332
  return (event) => {
1227
1333
  const { internal } = store.getState();
1228
- if ("pointerId" in event && internal.capturedMap.has(event.pointerId)) {
1334
+ const pointerId = getPointerId(event);
1335
+ const pointerState = internal.pointerMap.get(pointerId);
1336
+ if (pointerState?.captured.size) {
1229
1337
  requestAnimationFrame(() => {
1230
- if (internal.capturedMap.has(event.pointerId)) {
1231
- internal.capturedMap.delete(event.pointerId);
1232
- cancelPointer([]);
1338
+ const pointerState2 = internal.pointerMap.get(pointerId);
1339
+ if (pointerState2?.captured.size) {
1340
+ pointerState2.captured.clear();
1233
1341
  }
1342
+ cancelPointer([], pointerId);
1234
1343
  });
1235
1344
  }
1236
1345
  };
1237
1346
  }
1238
1347
  return function handleEvent(event) {
1239
1348
  const state = store.getState();
1240
- const { onPointerMissed, onDragOverMissed, onDropMissed, internal } = state;
1349
+ const { onPointerMissed, onDragOverMissed, onDropMissed, internal, events } = state;
1350
+ const pointerId = getPointerId(event);
1241
1351
  internal.lastEvent.current = event;
1242
- if (!state.events.enabled) return;
1352
+ if (!events.enabled) return;
1243
1353
  const isPointerMove = name === "onPointerMove";
1244
1354
  const isDragOver = name === "onDragOver";
1245
1355
  const isDrop = name === "onDrop";
1246
1356
  const isClickEvent = name === "onClick" || name === "onContextMenu" || name === "onDoubleClick";
1357
+ const isPointerDown = name === "onPointerDown";
1358
+ const isPointerUp = name === "onPointerUp";
1359
+ const isWheel = name === "onWheel";
1360
+ const canDeferRaycasts = events.frameTimedRaycasts && state.frameloop === "always";
1361
+ if (isPointerMove && canDeferRaycasts) {
1362
+ events.compute?.(event, state);
1363
+ internal.pointerDirty.set(pointerId, event);
1364
+ return;
1365
+ }
1366
+ if (isWheel && canDeferRaycasts && !events.alwaysFireOnScroll) {
1367
+ events.compute?.(event, state);
1368
+ internal.pointerDirty.set(pointerId, event);
1369
+ return;
1370
+ }
1371
+ if ((isClickEvent || isPointerDown || isPointerUp) && internal.pointerDirty.has(pointerId)) {
1372
+ const deferredEvent = internal.pointerDirty.get(pointerId);
1373
+ internal.pointerDirty.delete(pointerId);
1374
+ processDeferredPointer(deferredEvent, pointerId);
1375
+ }
1247
1376
  const filter = isPointerMove || isDragOver || isDrop ? filterPointerEvents : void 0;
1248
1377
  const hits = intersect(event, filter);
1249
- const delta = isClickEvent ? calculateDistance(event) : 0;
1250
- if (name === "onPointerDown") {
1251
- internal.initialClick = [event.offsetX, event.offsetY];
1252
- internal.initialHits = hits.map((hit) => hit.eventObject);
1253
- }
1378
+ const delta = isClickEvent ? calculateDistance(event, pointerId) : 0;
1379
+ if (isPointerDown) {
1380
+ const pointerState2 = getPointerState(internal, pointerId);
1381
+ pointerState2.initialClick = [event.offsetX, event.offsetY];
1382
+ pointerState2.initialHits = hits.map((hit) => hit.eventObject);
1383
+ }
1384
+ const pointerState = internal.pointerMap.get(pointerId);
1385
+ const initialHits = pointerState?.initialHits ?? [];
1254
1386
  if (isClickEvent && !hits.length) {
1255
1387
  if (delta <= 2) {
1256
1388
  pointerMissed(event, internal.interaction);
@@ -1265,7 +1397,9 @@ function createEvents(store) {
1265
1397
  dropMissed(event, internal.interaction);
1266
1398
  if (onDropMissed) onDropMissed(event);
1267
1399
  }
1268
- if (isPointerMove || isDragOver) cancelPointer(hits);
1400
+ if (isPointerMove || isDragOver) {
1401
+ cancelPointer(hits, pointerId);
1402
+ }
1269
1403
  function onIntersect(data) {
1270
1404
  const eventObject = data.eventObject;
1271
1405
  const instance = eventObject.__r3f;
@@ -1274,9 +1408,10 @@ function createEvents(store) {
1274
1408
  if (isPointerMove) {
1275
1409
  if (handlers.onPointerOver || handlers.onPointerEnter || handlers.onPointerOut || handlers.onPointerLeave) {
1276
1410
  const id = makeId(data);
1277
- const hoveredItem = internal.hovered.get(id);
1411
+ const pointerState2 = getPointerState(internal, pointerId);
1412
+ const hoveredItem = pointerState2.hovered.get(id);
1278
1413
  if (!hoveredItem) {
1279
- internal.hovered.set(id, data);
1414
+ pointerState2.hovered.set(id, data);
1280
1415
  handlers.onPointerOver?.(data);
1281
1416
  handlers.onPointerEnter?.(data);
1282
1417
  } else if (hoveredItem.stopped) {
@@ -1286,9 +1421,10 @@ function createEvents(store) {
1286
1421
  handlers.onPointerMove?.(data);
1287
1422
  } else if (isDragOver) {
1288
1423
  const id = makeId(data);
1289
- const hoveredItem = internal.hovered.get(id);
1424
+ const pointerState2 = getPointerState(internal, pointerId);
1425
+ const hoveredItem = pointerState2.hovered.get(id);
1290
1426
  if (!hoveredItem) {
1291
- internal.hovered.set(id, data);
1427
+ pointerState2.hovered.set(id, data);
1292
1428
  handlers.onDragOverEnter?.(data);
1293
1429
  } else if (hoveredItem.stopped) {
1294
1430
  data.stopPropagation();
@@ -1299,18 +1435,18 @@ function createEvents(store) {
1299
1435
  } else {
1300
1436
  const handler = handlers[name];
1301
1437
  if (handler) {
1302
- if (!isClickEvent || internal.initialHits.includes(eventObject)) {
1438
+ if (!isClickEvent || initialHits.includes(eventObject)) {
1303
1439
  pointerMissed(
1304
1440
  event,
1305
- internal.interaction.filter((object) => !internal.initialHits.includes(object))
1441
+ internal.interaction.filter((object) => !initialHits.includes(object))
1306
1442
  );
1307
1443
  handler(data);
1308
1444
  }
1309
1445
  } else {
1310
- if (isClickEvent && internal.initialHits.includes(eventObject)) {
1446
+ if (isClickEvent && initialHits.includes(eventObject)) {
1311
1447
  pointerMissed(
1312
1448
  event,
1313
- internal.interaction.filter((object) => !internal.initialHits.includes(object))
1449
+ internal.interaction.filter((object) => !initialHits.includes(object))
1314
1450
  );
1315
1451
  }
1316
1452
  }
@@ -1319,7 +1455,15 @@ function createEvents(store) {
1319
1455
  handleIntersects(hits, event, delta, onIntersect);
1320
1456
  };
1321
1457
  }
1322
- return { handlePointer };
1458
+ function flushDeferredPointers() {
1459
+ const { internal, events } = store.getState();
1460
+ if (!events.frameTimedRaycasts) return;
1461
+ for (const [pointerId, event] of internal.pointerDirty) {
1462
+ processDeferredPointer(event, pointerId);
1463
+ }
1464
+ internal.pointerDirty.clear();
1465
+ }
1466
+ return { handlePointer, flushDeferredPointers, processDeferredPointer };
1323
1467
  }
1324
1468
  const DOM_EVENTS = {
1325
1469
  onClick: ["click", false],
@@ -1338,10 +1482,15 @@ const DOM_EVENTS = {
1338
1482
  onLostPointerCapture: ["lostpointercapture", true]
1339
1483
  };
1340
1484
  function createPointerEvents(store) {
1341
- const { handlePointer } = createEvents(store);
1485
+ const { handlePointer, flushDeferredPointers, processDeferredPointer } = createEvents(store);
1486
+ let nextXRPointerId = XR_POINTER_ID_START;
1487
+ const xrPointers = /* @__PURE__ */ new Map();
1342
1488
  return {
1343
1489
  priority: 1,
1344
1490
  enabled: true,
1491
+ frameTimedRaycasts: true,
1492
+ alwaysFireOnScroll: true,
1493
+ updateOnFrame: false,
1345
1494
  compute(event, state) {
1346
1495
  state.pointer.set(event.offsetX / state.size.width * 2 - 1, -(event.offsetY / state.size.height) * 2 + 1);
1347
1496
  state.raycaster.setFromCamera(state.pointer, state.camera);
@@ -1351,11 +1500,33 @@ function createPointerEvents(store) {
1351
1500
  (acc, key) => ({ ...acc, [key]: handlePointer(key) }),
1352
1501
  {}
1353
1502
  ),
1354
- update: () => {
1503
+ update: (pointerId) => {
1355
1504
  const { events, internal } = store.getState();
1356
- if (internal.lastEvent?.current && events.handlers) events.handlers.onPointerMove(internal.lastEvent.current);
1505
+ if (!events.handlers) return;
1506
+ if (pointerId !== void 0) {
1507
+ const event = internal.pointerDirty.get(pointerId);
1508
+ if (event) {
1509
+ internal.pointerDirty.delete(pointerId);
1510
+ processDeferredPointer(event, pointerId);
1511
+ } else if (internal.lastEvent?.current) {
1512
+ processDeferredPointer(internal.lastEvent.current, pointerId);
1513
+ }
1514
+ } else {
1515
+ flushDeferredPointers();
1516
+ if (internal.lastEvent?.current) {
1517
+ events.handlers.onPointerMove(internal.lastEvent.current);
1518
+ }
1519
+ }
1520
+ },
1521
+ flush: () => {
1522
+ const { events, internal } = store.getState();
1523
+ flushDeferredPointers();
1524
+ if (events.updateOnFrame && internal.lastEvent?.current && events.handlers) {
1525
+ events.handlers.onPointerMove(internal.lastEvent.current);
1526
+ }
1357
1527
  },
1358
1528
  connect: (target) => {
1529
+ if (!target) return;
1359
1530
  const { set, events } = store.getState();
1360
1531
  events.disconnect?.();
1361
1532
  set((state) => ({ events: { ...state.events, connected: target } }));
@@ -1379,6 +1550,32 @@ function createPointerEvents(store) {
1379
1550
  }
1380
1551
  set((state) => ({ events: { ...state.events, connected: void 0 } }));
1381
1552
  }
1553
+ },
1554
+ registerPointer: (config) => {
1555
+ const pointerId = nextXRPointerId++;
1556
+ xrPointers.set(pointerId, config);
1557
+ const { internal } = store.getState();
1558
+ getPointerState(internal, pointerId);
1559
+ return pointerId;
1560
+ },
1561
+ unregisterPointer: (pointerId) => {
1562
+ xrPointers.delete(pointerId);
1563
+ const { internal } = store.getState();
1564
+ const pointerState = internal.pointerMap.get(pointerId);
1565
+ if (pointerState) {
1566
+ for (const [, hoveredObj] of pointerState.hovered) {
1567
+ const eventObject = hoveredObj.eventObject;
1568
+ const instance = eventObject.__r3f;
1569
+ if (instance?.eventCount) {
1570
+ const handlers = instance.handlers;
1571
+ const data = { ...hoveredObj, intersections: [] };
1572
+ handlers.onPointerOut?.(data);
1573
+ handlers.onPointerLeave?.(data);
1574
+ }
1575
+ }
1576
+ internal.pointerMap.delete(pointerId);
1577
+ }
1578
+ internal.pointerDirty.delete(pointerId);
1382
1579
  }
1383
1580
  };
1384
1581
  }
@@ -1692,7 +1889,7 @@ function shouldRun(job, now) {
1692
1889
  const minInterval = 1e3 / job.fps;
1693
1890
  const lastRun = job.lastRun ?? 0;
1694
1891
  const elapsed = now - lastRun;
1695
- if (elapsed < minInterval) return false;
1892
+ if (elapsed < minInterval - 1) return false;
1696
1893
  if (job.drop) {
1697
1894
  job.lastRun = now;
1698
1895
  } else {
@@ -2509,7 +2706,14 @@ const createStore = (invalidate, advance) => {
2509
2706
  frustum: new Frustum(),
2510
2707
  autoUpdateFrustum: true,
2511
2708
  raycaster: null,
2512
- events: { priority: 1, enabled: true, connected: false },
2709
+ events: {
2710
+ priority: 1,
2711
+ enabled: true,
2712
+ connected: false,
2713
+ frameTimedRaycasts: true,
2714
+ alwaysFireOnScroll: true,
2715
+ updateOnFrame: false
2716
+ },
2513
2717
  scene: null,
2514
2718
  rootScene: null,
2515
2719
  xr: null,
@@ -2600,11 +2804,13 @@ const createStore = (invalidate, advance) => {
2600
2804
  },
2601
2805
  setError: (error) => set(() => ({ error })),
2602
2806
  error: null,
2603
- //* TSL State (managed via hooks: useUniforms, useNodes, useTextures, usePostProcessing) ==============================
2807
+ //* TSL State (managed via hooks: useUniforms, useNodes, useBuffers, useGPUStorage, useTextures, useRenderPipeline) ==============================
2604
2808
  uniforms: {},
2605
2809
  nodes: {},
2810
+ buffers: {},
2811
+ gpuStorage: {},
2606
2812
  textures: /* @__PURE__ */ new Map(),
2607
- postProcessing: null,
2813
+ renderPipeline: null,
2608
2814
  passes: {},
2609
2815
  _hmrVersion: 0,
2610
2816
  _sizeImperative: false,
@@ -2613,12 +2819,16 @@ const createStore = (invalidate, advance) => {
2613
2819
  internal: {
2614
2820
  // Events
2615
2821
  interaction: [],
2616
- hovered: /* @__PURE__ */ new Map(),
2617
2822
  subscribers: [],
2823
+ // Per-pointer state (new unified structure)
2824
+ pointerMap: /* @__PURE__ */ new Map(),
2825
+ pointerDirty: /* @__PURE__ */ new Map(),
2826
+ lastEvent: React.createRef(),
2827
+ // Deprecated but kept for backwards compatibility
2828
+ hovered: /* @__PURE__ */ new Map(),
2618
2829
  initialClick: [0, 0],
2619
2830
  initialHits: [],
2620
2831
  capturedMap: /* @__PURE__ */ new Map(),
2621
- lastEvent: React.createRef(),
2622
2832
  // Visibility tracking (onFramed, onOccluded, onVisible)
2623
2833
  visibilityRegistry: /* @__PURE__ */ new Map(),
2624
2834
  // Occlusion system (WebGPU only)
@@ -2706,14 +2916,16 @@ const createStore = (invalidate, advance) => {
2706
2916
  oldSize = size;
2707
2917
  oldDpr = viewport.dpr;
2708
2918
  updateCamera(camera, size);
2709
- if (canvasTarget) {
2919
+ if (internal.isSecondary && canvasTarget) {
2710
2920
  if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
2711
- const updateStyle = typeof HTMLCanvasElement !== "undefined" && canvasTarget.domElement instanceof HTMLCanvasElement;
2712
- canvasTarget.setSize(size.width, size.height, updateStyle);
2921
+ canvasTarget.setSize(size.width, size.height, false);
2713
2922
  } else {
2714
2923
  if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
2715
- const updateStyle = typeof HTMLCanvasElement !== "undefined" && actualRenderer.domElement instanceof HTMLCanvasElement;
2716
- actualRenderer.setSize(size.width, size.height, updateStyle);
2924
+ actualRenderer.setSize(size.width, size.height, false);
2925
+ if (canvasTarget) {
2926
+ if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
2927
+ canvasTarget.setSize(size.width, size.height, false);
2928
+ }
2717
2929
  }
2718
2930
  }
2719
2931
  if (camera !== oldCamera) {
@@ -14998,7 +15210,6 @@ function createRoot(canvas) {
14998
15210
  events,
14999
15211
  onCreated: onCreatedCallback,
15000
15212
  shadows = false,
15001
- textureColorSpace = SRGBColorSpace,
15002
15213
  orthographic = false,
15003
15214
  frameloop = "always",
15004
15215
  dpr = [1, 2],
@@ -15013,6 +15224,7 @@ function createRoot(canvas) {
15013
15224
  _sizeProps,
15014
15225
  forceEven
15015
15226
  } = props;
15227
+ const textureColorSpace = is.obj(glConfig) && !is.fun(glConfig) && !isRenderer(glConfig) && glConfig.textureColorSpace || is.obj(rendererConfig) && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && rendererConfig.textureColorSpace || SRGBColorSpace;
15016
15228
  const state = store.getState();
15017
15229
  const defaultGPUProps = {
15018
15230
  canvas,
@@ -15050,6 +15262,12 @@ function createRoot(canvas) {
15050
15262
  } else if (!state.internal.actualRenderer) {
15051
15263
  renderer = await resolveRenderer(rendererConfig, defaultGPUProps, WebGPURenderer);
15052
15264
  if (!renderer.hasInitialized?.()) {
15265
+ const size2 = computeInitialSize(canvas, propsSize);
15266
+ if (size2.width > 0 && size2.height > 0) {
15267
+ const pixelRatio = calculateDpr(dpr);
15268
+ canvas.width = size2.width * pixelRatio;
15269
+ canvas.height = size2.height * pixelRatio;
15270
+ }
15053
15271
  await renderer.init();
15054
15272
  }
15055
15273
  const backend = renderer.backend;
@@ -15159,7 +15377,7 @@ function createRoot(canvas) {
15159
15377
  lastConfiguredProps.performance = performance;
15160
15378
  }
15161
15379
  if (!state.xr) {
15162
- const handleXRFrame = (timestamp, frame) => {
15380
+ const handleXRFrame = (timestamp, _frame) => {
15163
15381
  const state2 = store.getState();
15164
15382
  if (state2.frameloop === "never") return;
15165
15383
  advance(timestamp);
@@ -15195,15 +15413,22 @@ function createRoot(canvas) {
15195
15413
  const oldType = renderer.shadowMap.type;
15196
15414
  renderer.shadowMap.enabled = !!shadows;
15197
15415
  if (is.boo(shadows)) {
15198
- renderer.shadowMap.type = PCFSoftShadowMap;
15416
+ renderer.shadowMap.type = PCFShadowMap;
15199
15417
  } else if (is.str(shadows)) {
15418
+ if (shadows === "soft") {
15419
+ notifyDepreciated({
15420
+ heading: 'shadows="soft" is deprecated',
15421
+ body: "Three has depreciated soft and improved basic PCFShadows, we converted for you.",
15422
+ link: "https://github.com/mrdoob/three.js/wiki/Migration-Guide?utm_source=chatgpt.com#181--182"
15423
+ });
15424
+ }
15200
15425
  const types = {
15201
15426
  basic: BasicShadowMap,
15202
15427
  percentage: PCFShadowMap,
15203
- soft: PCFSoftShadowMap,
15428
+ soft: PCFShadowMap,
15204
15429
  variance: VSMShadowMap
15205
15430
  };
15206
- renderer.shadowMap.type = types[shadows] ?? PCFSoftShadowMap;
15431
+ renderer.shadowMap.type = types[shadows] ?? PCFShadowMap;
15207
15432
  } else if (is.obj(shadows)) {
15208
15433
  Object.assign(renderer.shadowMap, shadows);
15209
15434
  }
@@ -15219,13 +15444,24 @@ function createRoot(canvas) {
15219
15444
  if (state.textureColorSpace !== textureColorSpace) state.set(() => ({ textureColorSpace }));
15220
15445
  lastConfiguredProps.textureColorSpace = textureColorSpace;
15221
15446
  }
15447
+ const r3fProps = ["textureColorSpace"];
15448
+ const constructorOnlyProps = ["samples", "antialias", "alpha", "canvas", "powerPreference"];
15449
+ const nonApplyProps = [...r3fProps, ...constructorOnlyProps];
15222
15450
  if (glConfig && !is.fun(glConfig) && !isRenderer(glConfig) && !is.equ(glConfig, renderer, shallowLoose)) {
15223
- applyProps(renderer, glConfig);
15451
+ const glProps = {};
15452
+ for (const key in glConfig) {
15453
+ if (!nonApplyProps.includes(key)) glProps[key] = glConfig[key];
15454
+ }
15455
+ applyProps(renderer, glProps);
15224
15456
  }
15225
15457
  if (rendererConfig && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && state.renderer) {
15226
15458
  const currentRenderer = state.renderer;
15227
15459
  if (!is.equ(rendererConfig, currentRenderer, shallowLoose)) {
15228
- applyProps(currentRenderer, rendererConfig);
15460
+ const rendererProps = {};
15461
+ for (const key in rendererConfig) {
15462
+ if (!nonApplyProps.includes(key)) rendererProps[key] = rendererConfig[key];
15463
+ }
15464
+ applyProps(currentRenderer, rendererProps);
15229
15465
  }
15230
15466
  }
15231
15467
  const scheduler = getScheduler();
@@ -15251,6 +15487,18 @@ function createRoot(canvas) {
15251
15487
  system: true
15252
15488
  }
15253
15489
  );
15490
+ const unregisterEventsFlush = scheduler.register(
15491
+ () => {
15492
+ const state2 = store.getState();
15493
+ state2.events.flush?.();
15494
+ },
15495
+ {
15496
+ id: `${newRootId}_events`,
15497
+ rootId: newRootId,
15498
+ phase: "input",
15499
+ system: true
15500
+ }
15501
+ );
15254
15502
  const unregisterFrustum = scheduler.register(
15255
15503
  () => {
15256
15504
  const state2 = store.getState();
@@ -15285,7 +15533,7 @@ function createRoot(canvas) {
15285
15533
  const userHandlesRender = scheduler.hasUserJobsInPhase("render", newRootId);
15286
15534
  if (userHandlesRender || state2.internal.priority) return;
15287
15535
  try {
15288
- if (state2.postProcessing?.render) state2.postProcessing.render();
15536
+ if (state2.renderPipeline?.render) state2.renderPipeline.render();
15289
15537
  else if (renderer2?.render) renderer2.render(state2.scene, state2.camera);
15290
15538
  } catch (error) {
15291
15539
  state2.setError(error instanceof Error ? error : new Error(String(error)));
@@ -15310,6 +15558,7 @@ function createRoot(canvas) {
15310
15558
  unregisterRoot: () => {
15311
15559
  unregisterRoot();
15312
15560
  unregisterCanvasTarget();
15561
+ unregisterEventsFlush();
15313
15562
  unregisterFrustum();
15314
15563
  unregisterVisibility();
15315
15564
  unregisterRender();
@@ -15475,9 +15724,13 @@ function PortalInner({ state = {}, children, container }) {
15475
15724
  const store = createWithEqualityFn((set, get) => ({ ...rest, set, get }));
15476
15725
  const onMutate = (prev) => store.setState((state2) => inject.current(prev, state2));
15477
15726
  onMutate(previousRoot.getState());
15478
- previousRoot.subscribe(onMutate);
15479
15727
  return store;
15480
15728
  }, [previousRoot, container]);
15729
+ useIsomorphicLayoutEffect(() => {
15730
+ const onMutate = (prev) => usePortalStore.setState((state2) => inject.current(prev, state2));
15731
+ const unsubscribe = previousRoot.subscribe(onMutate);
15732
+ return unsubscribe;
15733
+ }, [previousRoot, usePortalStore]);
15481
15734
  return (
15482
15735
  // @ts-ignore, reconciler types are not maintained
15483
15736
  /* @__PURE__ */ jsx(Fragment, { children: reconciler.createPortal(
@@ -15522,8 +15775,18 @@ function CanvasImpl({
15522
15775
  forceEven,
15523
15776
  ...props
15524
15777
  }) {
15525
- 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 };
15526
- const renderer = Object.keys(rendererConfig).length > 0 ? rendererConfig : rendererProp;
15778
+ const isRendererConfig = typeof rendererProp === "object" && rendererProp !== null && !("render" in rendererProp) && ("primaryCanvas" in rendererProp || "scheduler" in rendererProp);
15779
+ let primaryCanvas;
15780
+ let scheduler;
15781
+ let renderer;
15782
+ if (isRendererConfig) {
15783
+ const { primaryCanvas: pc, scheduler: sc, ...rest } = rendererProp;
15784
+ primaryCanvas = pc;
15785
+ scheduler = sc;
15786
+ renderer = Object.keys(rest).length > 0 ? rest : rendererProp;
15787
+ } else {
15788
+ renderer = rendererProp;
15789
+ }
15527
15790
  React.useMemo(() => extend(THREE), []);
15528
15791
  const Bridge = useBridge();
15529
15792
  const backgroundProps = React.useMemo(() => {
@@ -15698,6 +15961,7 @@ function CanvasImpl({
15698
15961
  queueMicrotask(() => {
15699
15962
  const rootEntry = _roots.get(canvas);
15700
15963
  if (rootEntry?.store) {
15964
+ console.log("[R3F] HMR detected \u2014 rebuilding nodes/uniforms");
15701
15965
  rootEntry.store.setState((state) => ({
15702
15966
  nodes: {},
15703
15967
  uniforms: {},
@@ -15709,8 +15973,7 @@ function CanvasImpl({
15709
15973
  if (typeof import.meta !== "undefined" && import.meta.hot) {
15710
15974
  const hot = import.meta.hot;
15711
15975
  hot.on("vite:afterUpdate", handleHMR);
15712
- return () => hot.dispose?.(() => {
15713
- });
15976
+ return () => hot.off?.("vite:afterUpdate", handleHMR);
15714
15977
  }
15715
15978
  if (typeof module !== "undefined" && module.hot) {
15716
15979
  const hot = module.hot;
@@ -15733,7 +15996,16 @@ function CanvasImpl({
15733
15996
  ...style
15734
15997
  },
15735
15998
  ...props,
15736
- 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 }) })
15999
+ children: /* @__PURE__ */ jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsx(
16000
+ "canvas",
16001
+ {
16002
+ ref: canvasRef,
16003
+ id,
16004
+ className: "r3f-canvas",
16005
+ style: { display: "block", width: "100%", height: "100%" },
16006
+ children: fallback
16007
+ }
16008
+ ) })
15737
16009
  }
15738
16010
  );
15739
16011
  }
@@ -15809,6 +16081,8 @@ function createScopedStore(data) {
15809
16081
  function createLazyCreatorState(state) {
15810
16082
  let _uniforms = null;
15811
16083
  let _nodes = null;
16084
+ let _buffers = null;
16085
+ let _gpuStorage = null;
15812
16086
  return Object.create(state, {
15813
16087
  uniforms: {
15814
16088
  get() {
@@ -15819,6 +16093,16 @@ function createLazyCreatorState(state) {
15819
16093
  get() {
15820
16094
  return _nodes ?? (_nodes = createScopedStore(state.nodes));
15821
16095
  }
16096
+ },
16097
+ buffers: {
16098
+ get() {
16099
+ return _buffers ?? (_buffers = createScopedStore(state.buffers));
16100
+ }
16101
+ },
16102
+ gpuStorage: {
16103
+ get() {
16104
+ return _gpuStorage ?? (_gpuStorage = createScopedStore(state.gpuStorage));
16105
+ }
15822
16106
  }
15823
16107
  });
15824
16108
  }
@@ -15901,6 +16185,10 @@ function vectorize(inObject) {
15901
16185
  if (obj.isVector2 || obj.isVector3 || obj.isVector4) return inObject;
15902
16186
  if (obj.isMatrix3 || obj.isMatrix4) return inObject;
15903
16187
  if (obj.isColor || obj.isEuler || obj.isQuaternion || obj.isSpherical) return inObject;
16188
+ if ("r" in obj && "g" in obj && "b" in obj && typeof obj.r === "number" && typeof obj.g === "number" && typeof obj.b === "number") {
16189
+ const scale = obj.r > 1 || obj.g > 1 || obj.b > 1 ? 1 / 255 : 1;
16190
+ return new Color(obj.r * scale, obj.g * scale, obj.b * scale);
16191
+ }
15904
16192
  if ("x" in obj && "y" in obj && typeof obj.x === "number" && typeof obj.y === "number") {
15905
16193
  if ("w" in obj && typeof obj.w === "number" && "z" in obj && typeof obj.z === "number") {
15906
16194
  return new Vector4(obj.x, obj.y, obj.z, obj.w);
@@ -16356,7 +16644,351 @@ function useLocalNodes(creator) {
16356
16644
  }, [store, creator, uniforms, nodes, textures, hmrVersion]);
16357
16645
  }
16358
16646
 
16359
- function usePostProcessing(mainCB, setupCB) {
16647
+ const isBufferLike = (value) => {
16648
+ if (value === null || typeof value !== "object") return false;
16649
+ if (ArrayBuffer.isView(value)) return true;
16650
+ if ("isBufferAttribute" in value) return true;
16651
+ if ("uuid" in value || "nodeType" in value) return true;
16652
+ return false;
16653
+ };
16654
+ const disposeBuffer = (buffer) => {
16655
+ if (buffer === null || typeof buffer !== "object") return;
16656
+ if ("dispose" in buffer && typeof buffer.dispose === "function") {
16657
+ buffer.dispose();
16658
+ }
16659
+ };
16660
+ function useBuffers(creatorOrScope, scope) {
16661
+ const store = useStore();
16662
+ const removeBuffers = useCallback(
16663
+ (names, targetScope) => {
16664
+ const nameArray = Array.isArray(names) ? names : [names];
16665
+ store.setState((state) => {
16666
+ if (targetScope) {
16667
+ const currentScope = { ...state.buffers[targetScope] };
16668
+ for (const name of nameArray) delete currentScope[name];
16669
+ return { buffers: { ...state.buffers, [targetScope]: currentScope } };
16670
+ }
16671
+ const buffers2 = { ...state.buffers };
16672
+ for (const name of nameArray) if (isBufferLike(buffers2[name])) delete buffers2[name];
16673
+ return { buffers: buffers2 };
16674
+ });
16675
+ },
16676
+ [store]
16677
+ );
16678
+ const clearBuffers = useCallback(
16679
+ (targetScope) => {
16680
+ store.setState((state) => {
16681
+ if (targetScope && targetScope !== "root") {
16682
+ const { [targetScope]: _, ...rest } = state.buffers;
16683
+ return { buffers: rest };
16684
+ }
16685
+ if (targetScope === "root") {
16686
+ const buffers2 = {};
16687
+ for (const [key, value] of Object.entries(state.buffers)) {
16688
+ if (!isBufferLike(value)) buffers2[key] = value;
16689
+ }
16690
+ return { buffers: buffers2 };
16691
+ }
16692
+ return { buffers: {} };
16693
+ });
16694
+ },
16695
+ [store]
16696
+ );
16697
+ const rebuildBuffers = useCallback(
16698
+ (targetScope) => {
16699
+ store.setState((state) => {
16700
+ let newBuffers = state.buffers;
16701
+ if (targetScope && targetScope !== "root") {
16702
+ const { [targetScope]: _, ...rest } = state.buffers;
16703
+ newBuffers = rest;
16704
+ } else if (targetScope === "root") {
16705
+ newBuffers = {};
16706
+ for (const [key, value] of Object.entries(state.buffers)) {
16707
+ if (!isBufferLike(value)) newBuffers[key] = value;
16708
+ }
16709
+ } else {
16710
+ newBuffers = {};
16711
+ }
16712
+ return { buffers: newBuffers, _hmrVersion: state._hmrVersion + 1 };
16713
+ });
16714
+ },
16715
+ [store]
16716
+ );
16717
+ const disposeBuffers = useCallback(
16718
+ (names, targetScope) => {
16719
+ const nameArray = Array.isArray(names) ? names : [names];
16720
+ const state = store.getState();
16721
+ for (const name of nameArray) {
16722
+ const buffer = targetScope ? state.buffers[targetScope]?.[name] : state.buffers[name];
16723
+ if (buffer && isBufferLike(buffer)) {
16724
+ disposeBuffer(buffer);
16725
+ }
16726
+ }
16727
+ removeBuffers(names, targetScope);
16728
+ },
16729
+ [store, removeBuffers]
16730
+ );
16731
+ const isReader = creatorOrScope === void 0 || typeof creatorOrScope === "string";
16732
+ const storeBuffers = useThree((s) => s.buffers);
16733
+ const hmrVersion = useThree((s) => s._hmrVersion);
16734
+ const scopeDep = typeof creatorOrScope === "string" ? creatorOrScope : scope;
16735
+ const readerDep = isReader ? storeBuffers : null;
16736
+ const creatorDep = isReader ? null : hmrVersion;
16737
+ const buffers = useMemo(() => {
16738
+ if (creatorOrScope === void 0) {
16739
+ return storeBuffers;
16740
+ }
16741
+ if (typeof creatorOrScope === "string") {
16742
+ const scopeData = storeBuffers[creatorOrScope];
16743
+ if (scopeData && !isBufferLike(scopeData)) return scopeData;
16744
+ return {};
16745
+ }
16746
+ const state = store.getState();
16747
+ const set = store.setState;
16748
+ const creator = creatorOrScope;
16749
+ const wrappedState = createLazyCreatorState(state);
16750
+ const created = creator(wrappedState);
16751
+ const result = {};
16752
+ let hasNewBuffers = false;
16753
+ if (scope) {
16754
+ const currentScope = state.buffers[scope] ?? {};
16755
+ for (const [name, buffer] of Object.entries(created)) {
16756
+ if (currentScope[name]) {
16757
+ result[name] = currentScope[name];
16758
+ } else {
16759
+ if ("setName" in buffer && typeof buffer.setName === "function") {
16760
+ buffer.setName(`${scope}.${name}`);
16761
+ }
16762
+ result[name] = buffer;
16763
+ hasNewBuffers = true;
16764
+ }
16765
+ }
16766
+ if (hasNewBuffers) {
16767
+ set((s) => ({
16768
+ buffers: {
16769
+ ...s.buffers,
16770
+ [scope]: { ...s.buffers[scope], ...result }
16771
+ }
16772
+ }));
16773
+ }
16774
+ return result;
16775
+ }
16776
+ for (const [name, buffer] of Object.entries(created)) {
16777
+ const existing = state.buffers[name];
16778
+ if (existing && isBufferLike(existing)) {
16779
+ result[name] = existing;
16780
+ } else {
16781
+ if ("setName" in buffer && typeof buffer.setName === "function") {
16782
+ buffer.setName(name);
16783
+ }
16784
+ result[name] = buffer;
16785
+ hasNewBuffers = true;
16786
+ }
16787
+ }
16788
+ if (hasNewBuffers) {
16789
+ set((s) => ({ buffers: { ...s.buffers, ...result } }));
16790
+ }
16791
+ return result;
16792
+ }, [store, scopeDep, readerDep, creatorDep]);
16793
+ return { ...buffers, removeBuffers, clearBuffers, rebuildBuffers, disposeBuffers };
16794
+ }
16795
+ function rebuildAllBuffers(store, scope) {
16796
+ store.setState((state) => {
16797
+ let newBuffers = state.buffers;
16798
+ if (scope && scope !== "root") {
16799
+ const { [scope]: _, ...rest } = state.buffers;
16800
+ newBuffers = rest;
16801
+ } else if (scope === "root") {
16802
+ newBuffers = {};
16803
+ for (const [key, value] of Object.entries(state.buffers)) {
16804
+ if (!isBufferLike(value)) newBuffers[key] = value;
16805
+ }
16806
+ } else {
16807
+ newBuffers = {};
16808
+ }
16809
+ return { buffers: newBuffers, _hmrVersion: state._hmrVersion + 1 };
16810
+ });
16811
+ }
16812
+
16813
+ const isStorageLike = (value) => {
16814
+ if (value === null || typeof value !== "object") return false;
16815
+ if ("isTexture" in value) return true;
16816
+ if ("isData3DTexture" in value) return true;
16817
+ if ("uuid" in value || "nodeType" in value) return true;
16818
+ return false;
16819
+ };
16820
+ const disposeStorage = (storage) => {
16821
+ if (storage === null || typeof storage !== "object") return;
16822
+ if ("dispose" in storage && typeof storage.dispose === "function") {
16823
+ storage.dispose();
16824
+ }
16825
+ };
16826
+ function useGPUStorage(creatorOrScope, scope) {
16827
+ const store = useStore();
16828
+ const removeStorage = useCallback(
16829
+ (names, targetScope) => {
16830
+ const nameArray = Array.isArray(names) ? names : [names];
16831
+ store.setState((state) => {
16832
+ if (targetScope) {
16833
+ const currentScope = { ...state.gpuStorage[targetScope] };
16834
+ for (const name of nameArray) delete currentScope[name];
16835
+ return { gpuStorage: { ...state.gpuStorage, [targetScope]: currentScope } };
16836
+ }
16837
+ const gpuStorage2 = { ...state.gpuStorage };
16838
+ for (const name of nameArray) if (isStorageLike(gpuStorage2[name])) delete gpuStorage2[name];
16839
+ return { gpuStorage: gpuStorage2 };
16840
+ });
16841
+ },
16842
+ [store]
16843
+ );
16844
+ const clearStorage = useCallback(
16845
+ (targetScope) => {
16846
+ store.setState((state) => {
16847
+ if (targetScope && targetScope !== "root") {
16848
+ const { [targetScope]: _, ...rest } = state.gpuStorage;
16849
+ return { gpuStorage: rest };
16850
+ }
16851
+ if (targetScope === "root") {
16852
+ const gpuStorage2 = {};
16853
+ for (const [key, value] of Object.entries(state.gpuStorage)) {
16854
+ if (!isStorageLike(value)) gpuStorage2[key] = value;
16855
+ }
16856
+ return { gpuStorage: gpuStorage2 };
16857
+ }
16858
+ return { gpuStorage: {} };
16859
+ });
16860
+ },
16861
+ [store]
16862
+ );
16863
+ const rebuildStorage = useCallback(
16864
+ (targetScope) => {
16865
+ store.setState((state) => {
16866
+ let newStorage = state.gpuStorage;
16867
+ if (targetScope && targetScope !== "root") {
16868
+ const { [targetScope]: _, ...rest } = state.gpuStorage;
16869
+ newStorage = rest;
16870
+ } else if (targetScope === "root") {
16871
+ newStorage = {};
16872
+ for (const [key, value] of Object.entries(state.gpuStorage)) {
16873
+ if (!isStorageLike(value)) newStorage[key] = value;
16874
+ }
16875
+ } else {
16876
+ newStorage = {};
16877
+ }
16878
+ return { gpuStorage: newStorage, _hmrVersion: state._hmrVersion + 1 };
16879
+ });
16880
+ },
16881
+ [store]
16882
+ );
16883
+ const disposeStorageFn = useCallback(
16884
+ (names, targetScope) => {
16885
+ const nameArray = Array.isArray(names) ? names : [names];
16886
+ const state = store.getState();
16887
+ for (const name of nameArray) {
16888
+ const storage = targetScope ? state.gpuStorage[targetScope]?.[name] : state.gpuStorage[name];
16889
+ if (storage && isStorageLike(storage)) {
16890
+ disposeStorage(storage);
16891
+ }
16892
+ }
16893
+ removeStorage(names, targetScope);
16894
+ },
16895
+ [store, removeStorage]
16896
+ );
16897
+ const isReader = creatorOrScope === void 0 || typeof creatorOrScope === "string";
16898
+ const storeStorage = useThree((s) => s.gpuStorage);
16899
+ const hmrVersion = useThree((s) => s._hmrVersion);
16900
+ const scopeDep = typeof creatorOrScope === "string" ? creatorOrScope : scope;
16901
+ const readerDep = isReader ? storeStorage : null;
16902
+ const creatorDep = isReader ? null : hmrVersion;
16903
+ const gpuStorage = useMemo(() => {
16904
+ if (creatorOrScope === void 0) {
16905
+ return storeStorage;
16906
+ }
16907
+ if (typeof creatorOrScope === "string") {
16908
+ const scopeData = storeStorage[creatorOrScope];
16909
+ if (scopeData && !isStorageLike(scopeData)) return scopeData;
16910
+ return {};
16911
+ }
16912
+ const state = store.getState();
16913
+ const set = store.setState;
16914
+ const creator = creatorOrScope;
16915
+ const wrappedState = createLazyCreatorState(state);
16916
+ const created = creator(wrappedState);
16917
+ const result = {};
16918
+ let hasNewStorage = false;
16919
+ if (scope) {
16920
+ const currentScope = state.gpuStorage[scope] ?? {};
16921
+ for (const [name, storage] of Object.entries(created)) {
16922
+ if (currentScope[name]) {
16923
+ result[name] = currentScope[name];
16924
+ } else {
16925
+ if ("setName" in storage && typeof storage.setName === "function") {
16926
+ storage.setName(`${scope}.${name}`);
16927
+ }
16928
+ if ("name" in storage && typeof storage.name === "string") {
16929
+ storage.name = `${scope}.${name}`;
16930
+ }
16931
+ result[name] = storage;
16932
+ hasNewStorage = true;
16933
+ }
16934
+ }
16935
+ if (hasNewStorage) {
16936
+ set((s) => ({
16937
+ gpuStorage: {
16938
+ ...s.gpuStorage,
16939
+ [scope]: { ...s.gpuStorage[scope], ...result }
16940
+ }
16941
+ }));
16942
+ }
16943
+ return result;
16944
+ }
16945
+ for (const [name, storage] of Object.entries(created)) {
16946
+ const existing = state.gpuStorage[name];
16947
+ if (existing && isStorageLike(existing)) {
16948
+ result[name] = existing;
16949
+ } else {
16950
+ if ("setName" in storage && typeof storage.setName === "function") {
16951
+ storage.setName(name);
16952
+ }
16953
+ if ("name" in storage && typeof storage.name === "string") {
16954
+ storage.name = name;
16955
+ }
16956
+ result[name] = storage;
16957
+ hasNewStorage = true;
16958
+ }
16959
+ }
16960
+ if (hasNewStorage) {
16961
+ set((s) => ({ gpuStorage: { ...s.gpuStorage, ...result } }));
16962
+ }
16963
+ return result;
16964
+ }, [store, scopeDep, readerDep, creatorDep]);
16965
+ return {
16966
+ ...gpuStorage,
16967
+ removeStorage,
16968
+ clearStorage,
16969
+ rebuildStorage,
16970
+ disposeStorage: disposeStorageFn
16971
+ };
16972
+ }
16973
+ function rebuildAllStorage(store, scope) {
16974
+ store.setState((state) => {
16975
+ let newStorage = state.gpuStorage;
16976
+ if (scope && scope !== "root") {
16977
+ const { [scope]: _, ...rest } = state.gpuStorage;
16978
+ newStorage = rest;
16979
+ } else if (scope === "root") {
16980
+ newStorage = {};
16981
+ for (const [key, value] of Object.entries(state.gpuStorage)) {
16982
+ if (!isStorageLike(value)) newStorage[key] = value;
16983
+ }
16984
+ } else {
16985
+ newStorage = {};
16986
+ }
16987
+ return { gpuStorage: newStorage, _hmrVersion: state._hmrVersion + 1 };
16988
+ });
16989
+ }
16990
+
16991
+ function useRenderPipeline(mainCB, setupCB) {
16360
16992
  const store = useStore();
16361
16993
  const { scene, camera, renderer, isLegacy } = useThree();
16362
16994
  const callbacksRanRef = useRef(false);
@@ -16375,7 +17007,7 @@ function usePostProcessing(mainCB, setupCB) {
16375
17007
  }, [store]);
16376
17008
  const reset = useCallback(() => {
16377
17009
  store.setState({
16378
- postProcessing: null,
17010
+ renderPipeline: null,
16379
17011
  passes: {}
16380
17012
  });
16381
17013
  callbacksRanRef.current = false;
@@ -16388,13 +17020,13 @@ function usePostProcessing(mainCB, setupCB) {
16388
17020
  }, []);
16389
17021
  useLayoutEffect(() => {
16390
17022
  if (isLegacy) {
16391
- throw new Error("usePostProcessing is only available with WebGPU renderer. Set renderer prop on Canvas.");
17023
+ throw new Error("useRenderPipeline is only available with WebGPU renderer. Set renderer prop on Canvas.");
16392
17024
  }
16393
17025
  if (!renderer || !scene || !camera) return;
16394
17026
  const state = store.getState();
16395
17027
  const set = store.setState;
16396
17028
  try {
16397
- let pp = state.postProcessing;
17029
+ let pp = state.renderPipeline;
16398
17030
  let currentPasses = { ...state.passes };
16399
17031
  let justCreatedPP = false;
16400
17032
  if (!pp) {
@@ -16411,7 +17043,7 @@ function usePostProcessing(mainCB, setupCB) {
16411
17043
  }
16412
17044
  currentPasses.scenePass = scenePass;
16413
17045
  if (!pp.outputNode || justCreatedPP) pp.outputNode = scenePass;
16414
- set({ postProcessing: pp, passes: currentPasses });
17046
+ set({ renderPipeline: pp, passes: currentPasses });
16415
17047
  const shouldRunCallbacks = justCreatedPP || !callbacksRanRef.current || !cacheValid;
16416
17048
  if (shouldRunCallbacks) {
16417
17049
  if (setupCBRef.current) {
@@ -16433,22 +17065,22 @@ function usePostProcessing(mainCB, setupCB) {
16433
17065
  callbacksRanRef.current = true;
16434
17066
  }
16435
17067
  } catch (error) {
16436
- console.error("[usePostProcessing] Setup error:", error);
17068
+ console.error("[useRenderPipeline] Setup error:", error);
16437
17069
  }
16438
17070
  }, [store, renderer, scene, camera, isLegacy, rebuildVersion]);
16439
17071
  const passes = useThree((s) => s.passes);
16440
- const postProcessing = useThree((s) => s.postProcessing);
17072
+ const renderPipeline = useThree((s) => s.renderPipeline);
16441
17073
  return {
16442
17074
  passes,
16443
- postProcessing,
17075
+ renderPipeline,
16444
17076
  clearPasses,
16445
17077
  reset,
16446
17078
  rebuild,
16447
- // isReady indicates if PostProcessing is configured and ready for rendering
16448
- isReady: postProcessing !== null
17079
+ // isReady indicates if RenderPipeline is configured and ready for rendering
17080
+ isReady: renderPipeline !== null
16449
17081
  };
16450
17082
  }
16451
17083
 
16452
17084
  extend(THREE);
16453
17085
 
16454
- export { Block, Canvas, Environment, EnvironmentCube, EnvironmentMap, EnvironmentPortal, ErrorBoundary, FROM_REF, IsObject, ONCE, Portal, R3F_BUILD_LEGACY, R3F_BUILD_WEBGPU, REACT_INTERNAL_PROPS, RESERVED_PROPS, Scheduler, Texture, _roots, act, addAfterEffect, addEffect, addTail, advance, applyProps, attach, buildGraph, calculateDpr, clearNodeScope, clearRootNodes, clearRootUniforms, clearScope, context, createEvents, createPointerEvents, createPortal, createRoot, createScopedStore, createStore, createTextureOperations, detach, diffProps, dispose, createPointerEvents as events, extend, findInitialRoot, flushSync, fromRef, getInstanceProps, getPrimary, getPrimaryIds, getRootState, getScheduler, getUuidPrefix, hasConstructor, hasPrimary, invalidate, invalidateInstance, is, isColorRepresentation, isCopyable, isFromRef, isObject3D, isOnce, isOrthographicCamera, isRef, isRenderer, isTexture, isVectorLike, once, prepare, presetsObj, rebuildAllNodes, rebuildAllUniforms, reconciler, registerPrimary, removeInteractivity, removeNodes, removeUniforms, resolve, unmountComponentAtNode, unregisterPrimary, updateCamera, updateFrustum, useBridge, useEnvironment, useFrame, useGraph, useInstanceHandle, useIsomorphicLayoutEffect, useLoader, useLocalNodes, useMutableCallback, useNodes, usePostProcessing, useRenderTarget, useStore, useTexture, useTextures, useThree, useUniform, useUniforms, waitForPrimary };
17086
+ export { Block, Canvas, Environment, EnvironmentCube, EnvironmentMap, EnvironmentPortal, ErrorBoundary, FROM_REF, IsObject, ONCE, Portal, R3F_BUILD_LEGACY, R3F_BUILD_WEBGPU, REACT_INTERNAL_PROPS, RESERVED_PROPS, Scheduler, Texture, _roots, act, addAfterEffect, addEffect, addTail, advance, applyProps, attach, buildGraph, calculateDpr, clearNodeScope, clearRootNodes, clearRootUniforms, clearScope, context, createEvents, createPointerEvents, createPortal, createRoot, createScopedStore, createStore, createTextureOperations, detach, diffProps, dispose, createPointerEvents as events, extend, findInitialRoot, flushSync, fromRef, getInstanceProps, getPrimary, getPrimaryIds, getRootState, getScheduler, getUuidPrefix, hasConstructor, hasPrimary, invalidate, invalidateInstance, is, isColorRepresentation, isCopyable, isFromRef, isObject3D, isOnce, isOrthographicCamera, isRef, isRenderer, isTexture, isVectorLike, once, prepare, presetsObj, rebuildAllBuffers, rebuildAllNodes, rebuildAllStorage, rebuildAllUniforms, reconciler, registerPrimary, removeInteractivity, removeNodes, removeUniforms, resolve, unmountComponentAtNode, unregisterPrimary, updateCamera, updateFrustum, useBridge, useBuffers, useEnvironment, useFrame, useGPUStorage, useGraph, useInstanceHandle, useIsomorphicLayoutEffect, useLoader, useLocalNodes, useMutableCallback, useNodes, useRenderPipeline, useRenderTarget, useStore, useTexture, useTextures, useThree, useUniform, useUniforms, waitForPrimary };