@react-three/fiber 10.0.0-canary.b0fafc8 → 10.0.0-canary.d6fdf72
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +440 -188
- package/dist/index.d.cts +236 -80
- package/dist/index.d.mts +236 -80
- package/dist/index.d.ts +236 -80
- package/dist/index.mjs +441 -189
- package/dist/legacy.cjs +434 -188
- package/dist/legacy.d.cts +236 -80
- package/dist/legacy.d.mts +236 -80
- package/dist/legacy.d.ts +236 -80
- package/dist/legacy.mjs +435 -189
- package/dist/webgpu/index.cjs +815 -199
- package/dist/webgpu/index.d.cts +321 -93
- package/dist/webgpu/index.d.mts +321 -93
- package/dist/webgpu/index.d.ts +321 -93
- package/dist/webgpu/index.mjs +812 -200
- package/package.json +1 -1
package/dist/webgpu/index.cjs
CHANGED
|
@@ -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
|
-
}, [
|
|
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(
|
|
1046
|
-
const
|
|
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
|
-
|
|
1049
|
-
|
|
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
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
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
|
|
1073
|
-
|
|
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
|
|
1131
|
-
|
|
1132
|
-
|
|
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) =>
|
|
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
|
-
|
|
1149
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
!
|
|
1182
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1355
|
+
const pointerId = getPointerId(event);
|
|
1356
|
+
const pointerState = internal.pointerMap.get(pointerId);
|
|
1357
|
+
if (pointerState?.captured.size) {
|
|
1250
1358
|
requestAnimationFrame(() => {
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
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 (!
|
|
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 (
|
|
1272
|
-
|
|
1273
|
-
|
|
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)
|
|
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
|
|
1432
|
+
const pointerState2 = getPointerState(internal, pointerId);
|
|
1433
|
+
const hoveredItem = pointerState2.hovered.get(id);
|
|
1299
1434
|
if (!hoveredItem) {
|
|
1300
|
-
|
|
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
|
|
1445
|
+
const pointerState2 = getPointerState(internal, pointerId);
|
|
1446
|
+
const hoveredItem = pointerState2.hovered.get(id);
|
|
1311
1447
|
if (!hoveredItem) {
|
|
1312
|
-
|
|
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 ||
|
|
1459
|
+
if (!isClickEvent || initialHits.includes(eventObject)) {
|
|
1324
1460
|
pointerMissed(
|
|
1325
1461
|
event,
|
|
1326
|
-
internal.interaction.filter((object) => !
|
|
1462
|
+
internal.interaction.filter((object) => !initialHits.includes(object))
|
|
1327
1463
|
);
|
|
1328
1464
|
handler(data);
|
|
1329
1465
|
}
|
|
1330
1466
|
} else {
|
|
1331
|
-
if (isClickEvent &&
|
|
1467
|
+
if (isClickEvent && initialHits.includes(eventObject)) {
|
|
1332
1468
|
pointerMissed(
|
|
1333
1469
|
event,
|
|
1334
|
-
internal.interaction.filter((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
|
-
|
|
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 (
|
|
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: {
|
|
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,14 @@ 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,
|
|
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
|
-
|
|
2834
|
+
_textureRefs: /* @__PURE__ */ new Map(),
|
|
2835
|
+
renderPipeline: null,
|
|
2629
2836
|
passes: {},
|
|
2630
2837
|
_hmrVersion: 0,
|
|
2631
2838
|
_sizeImperative: false,
|
|
@@ -2634,12 +2841,16 @@ const createStore = (invalidate, advance) => {
|
|
|
2634
2841
|
internal: {
|
|
2635
2842
|
// Events
|
|
2636
2843
|
interaction: [],
|
|
2637
|
-
hovered: /* @__PURE__ */ new Map(),
|
|
2638
2844
|
subscribers: [],
|
|
2845
|
+
// Per-pointer state (new unified structure)
|
|
2846
|
+
pointerMap: /* @__PURE__ */ new Map(),
|
|
2847
|
+
pointerDirty: /* @__PURE__ */ new Map(),
|
|
2848
|
+
lastEvent: React__namespace.createRef(),
|
|
2849
|
+
// Deprecated but kept for backwards compatibility
|
|
2850
|
+
hovered: /* @__PURE__ */ new Map(),
|
|
2639
2851
|
initialClick: [0, 0],
|
|
2640
2852
|
initialHits: [],
|
|
2641
2853
|
capturedMap: /* @__PURE__ */ new Map(),
|
|
2642
|
-
lastEvent: React__namespace.createRef(),
|
|
2643
2854
|
// Visibility tracking (onFramed, onOccluded, onVisible)
|
|
2644
2855
|
visibilityRegistry: /* @__PURE__ */ new Map(),
|
|
2645
2856
|
// Occlusion system (WebGPU only)
|
|
@@ -2727,14 +2938,16 @@ const createStore = (invalidate, advance) => {
|
|
|
2727
2938
|
oldSize = size;
|
|
2728
2939
|
oldDpr = viewport.dpr;
|
|
2729
2940
|
updateCamera(camera, size);
|
|
2730
|
-
if (canvasTarget) {
|
|
2941
|
+
if (internal.isSecondary && canvasTarget) {
|
|
2731
2942
|
if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
|
|
2732
|
-
|
|
2733
|
-
canvasTarget.setSize(size.width, size.height, updateStyle);
|
|
2943
|
+
canvasTarget.setSize(size.width, size.height, false);
|
|
2734
2944
|
} else {
|
|
2735
2945
|
if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
|
|
2736
|
-
|
|
2737
|
-
|
|
2946
|
+
actualRenderer.setSize(size.width, size.height, false);
|
|
2947
|
+
if (canvasTarget) {
|
|
2948
|
+
if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
|
|
2949
|
+
canvasTarget.setSize(size.width, size.height, false);
|
|
2950
|
+
}
|
|
2738
2951
|
}
|
|
2739
2952
|
}
|
|
2740
2953
|
if (camera !== oldCamera) {
|
|
@@ -2982,18 +3195,18 @@ function buildFromCache(input, textureCache) {
|
|
|
2982
3195
|
function useTexture(input, optionsOrOnLoad) {
|
|
2983
3196
|
const renderer = useThree((state) => state.internal.actualRenderer);
|
|
2984
3197
|
const store = useStore();
|
|
2985
|
-
const textureCache = useThree((state) => state.textures);
|
|
2986
3198
|
const options = typeof optionsOrOnLoad === "function" ? { onLoad: optionsOrOnLoad } : optionsOrOnLoad ?? {};
|
|
2987
|
-
const { onLoad, cache =
|
|
3199
|
+
const { onLoad, cache = true } = options;
|
|
2988
3200
|
const onLoadRef = React.useRef(onLoad);
|
|
2989
3201
|
onLoadRef.current = onLoad;
|
|
2990
3202
|
const onLoadCalledForRef = React.useRef(null);
|
|
2991
3203
|
const urls = React.useMemo(() => getUrls(input), [input]);
|
|
2992
3204
|
const cachedResult = React.useMemo(() => {
|
|
2993
3205
|
if (!cache) return null;
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
3206
|
+
const textures = store.getState().textures;
|
|
3207
|
+
if (!allUrlsCached(urls, textures)) return null;
|
|
3208
|
+
return buildFromCache(input, textures);
|
|
3209
|
+
}, [cache, urls, input, store]);
|
|
2997
3210
|
const loadedTextures = useLoader(
|
|
2998
3211
|
webgpu.TextureLoader,
|
|
2999
3212
|
IsObject(input) ? Object.values(input) : input
|
|
@@ -3037,8 +3250,6 @@ function useTexture(input, optionsOrOnLoad) {
|
|
|
3037
3250
|
}, [input, loadedTextures, cachedResult]);
|
|
3038
3251
|
React.useEffect(() => {
|
|
3039
3252
|
if (!cache) return;
|
|
3040
|
-
if (cachedResult) return;
|
|
3041
|
-
const set = store.setState;
|
|
3042
3253
|
const urlTextureMap = [];
|
|
3043
3254
|
if (typeof input === "string") {
|
|
3044
3255
|
urlTextureMap.push([input, mappedTextures]);
|
|
@@ -3052,18 +3263,32 @@ function useTexture(input, optionsOrOnLoad) {
|
|
|
3052
3263
|
urlTextureMap.push([url, textureRecord[key]]);
|
|
3053
3264
|
}
|
|
3054
3265
|
}
|
|
3055
|
-
|
|
3056
|
-
const
|
|
3057
|
-
let
|
|
3266
|
+
store.setState((state) => {
|
|
3267
|
+
const refs = new Map(state._textureRefs);
|
|
3268
|
+
let textures = state.textures;
|
|
3269
|
+
let added = false;
|
|
3058
3270
|
for (const [url, texture] of urlTextureMap) {
|
|
3059
|
-
if (!
|
|
3060
|
-
|
|
3061
|
-
|
|
3271
|
+
if (!textures.has(url)) {
|
|
3272
|
+
if (!added) {
|
|
3273
|
+
textures = new Map(textures);
|
|
3274
|
+
added = true;
|
|
3275
|
+
}
|
|
3276
|
+
textures.set(url, texture);
|
|
3062
3277
|
}
|
|
3278
|
+
refs.set(url, (refs.get(url) ?? 0) + 1);
|
|
3063
3279
|
}
|
|
3064
|
-
return
|
|
3280
|
+
return added ? { textures, _textureRefs: refs } : { _textureRefs: refs };
|
|
3281
|
+
});
|
|
3282
|
+
return () => store.setState((state) => {
|
|
3283
|
+
const refs = new Map(state._textureRefs);
|
|
3284
|
+
for (const [url] of urlTextureMap) {
|
|
3285
|
+
const next = (refs.get(url) ?? 0) - 1;
|
|
3286
|
+
if (next <= 0) refs.delete(url);
|
|
3287
|
+
else refs.set(url, next);
|
|
3288
|
+
}
|
|
3289
|
+
return { _textureRefs: refs };
|
|
3065
3290
|
});
|
|
3066
|
-
}, [cache, input, mappedTextures, store
|
|
3291
|
+
}, [cache, input, mappedTextures, store]);
|
|
3067
3292
|
return mappedTextures;
|
|
3068
3293
|
}
|
|
3069
3294
|
useTexture.preload = (url) => useLoader.preload(webgpu.TextureLoader, url);
|
|
@@ -3079,96 +3304,63 @@ const Texture = ({
|
|
|
3079
3304
|
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: children?.(ret) });
|
|
3080
3305
|
};
|
|
3081
3306
|
|
|
3082
|
-
function
|
|
3083
|
-
if (entry instanceof webgpu.Texture) return entry;
|
|
3084
|
-
if (entry && typeof entry === "object" && "value" in entry && entry.value instanceof webgpu.Texture) {
|
|
3085
|
-
return entry.value;
|
|
3086
|
-
}
|
|
3087
|
-
return null;
|
|
3088
|
-
}
|
|
3089
|
-
function useTextures() {
|
|
3307
|
+
function useTextures(selector) {
|
|
3090
3308
|
const store = useStore();
|
|
3091
|
-
|
|
3092
|
-
const set = store.setState;
|
|
3309
|
+
const registry = React.useMemo(() => {
|
|
3093
3310
|
const getState = store.getState;
|
|
3094
|
-
const
|
|
3095
|
-
|
|
3096
|
-
const newMap = new Map(state.textures);
|
|
3097
|
-
newMap.set(key, value);
|
|
3098
|
-
return { textures: newMap };
|
|
3099
|
-
});
|
|
3100
|
-
};
|
|
3101
|
-
const addMultiple = (items) => {
|
|
3102
|
-
set((state) => {
|
|
3103
|
-
const newMap = new Map(state.textures);
|
|
3104
|
-
const entries = items instanceof Map ? items.entries() : Object.entries(items);
|
|
3105
|
-
for (const [key, value] of entries) {
|
|
3106
|
-
newMap.set(key, value);
|
|
3107
|
-
}
|
|
3108
|
-
return { textures: newMap };
|
|
3109
|
-
});
|
|
3110
|
-
};
|
|
3111
|
-
const remove = (key) => {
|
|
3112
|
-
set((state) => {
|
|
3113
|
-
const newMap = new Map(state.textures);
|
|
3114
|
-
newMap.delete(key);
|
|
3115
|
-
return { textures: newMap };
|
|
3116
|
-
});
|
|
3117
|
-
};
|
|
3118
|
-
const removeMultiple = (keys) => {
|
|
3119
|
-
set((state) => {
|
|
3120
|
-
const newMap = new Map(state.textures);
|
|
3121
|
-
for (const key of keys) newMap.delete(key);
|
|
3122
|
-
return { textures: newMap };
|
|
3123
|
-
});
|
|
3124
|
-
};
|
|
3125
|
-
const dispose = (key) => {
|
|
3126
|
-
const entry = getState().textures.get(key);
|
|
3127
|
-
if (entry) {
|
|
3128
|
-
const tex = getTextureValue(entry);
|
|
3129
|
-
tex?.dispose();
|
|
3130
|
-
}
|
|
3131
|
-
remove(key);
|
|
3132
|
-
};
|
|
3133
|
-
const disposeMultiple = (keys) => {
|
|
3134
|
-
const textures = getState().textures;
|
|
3135
|
-
for (const key of keys) {
|
|
3136
|
-
const entry = textures.get(key);
|
|
3137
|
-
if (entry) {
|
|
3138
|
-
const tex = getTextureValue(entry);
|
|
3139
|
-
tex?.dispose();
|
|
3140
|
-
}
|
|
3141
|
-
}
|
|
3142
|
-
removeMultiple(keys);
|
|
3143
|
-
};
|
|
3144
|
-
const disposeAll = () => {
|
|
3145
|
-
const textures = getState().textures;
|
|
3146
|
-
for (const entry of textures.values()) {
|
|
3147
|
-
const tex = getTextureValue(entry);
|
|
3148
|
-
tex?.dispose();
|
|
3149
|
-
}
|
|
3150
|
-
set({ textures: /* @__PURE__ */ new Map() });
|
|
3151
|
-
};
|
|
3311
|
+
const setState = store.setState;
|
|
3312
|
+
const getOne = (key) => getState().textures.get(key);
|
|
3152
3313
|
return {
|
|
3153
|
-
|
|
3154
|
-
get textures() {
|
|
3314
|
+
get all() {
|
|
3155
3315
|
return getState().textures;
|
|
3156
3316
|
},
|
|
3157
|
-
|
|
3158
|
-
|
|
3317
|
+
get(input) {
|
|
3318
|
+
if (typeof input === "string") return getOne(input);
|
|
3319
|
+
if (Array.isArray(input)) return input.map(getOne);
|
|
3320
|
+
const out = {};
|
|
3321
|
+
for (const name in input) out[name] = getOne(input[name]);
|
|
3322
|
+
return out;
|
|
3323
|
+
},
|
|
3159
3324
|
has: (key) => getState().textures.has(key),
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3325
|
+
add(keyOrRecord, texture) {
|
|
3326
|
+
setState((state) => {
|
|
3327
|
+
const textures = new Map(state.textures);
|
|
3328
|
+
if (typeof keyOrRecord === "string") {
|
|
3329
|
+
textures.set(keyOrRecord, texture);
|
|
3330
|
+
} else {
|
|
3331
|
+
for (const key in keyOrRecord) textures.set(key, keyOrRecord[key]);
|
|
3332
|
+
}
|
|
3333
|
+
return { textures };
|
|
3334
|
+
});
|
|
3335
|
+
},
|
|
3336
|
+
dispose(key, options) {
|
|
3337
|
+
const state = getState();
|
|
3338
|
+
const refs = state._textureRefs.get(key) ?? 0;
|
|
3339
|
+
if (refs > 0 && !options?.force) {
|
|
3340
|
+
console.warn(
|
|
3341
|
+
`[useTextures] "${key}" still has ${refs} active reference(s); skipping dispose. Pass { force: true } to override.`
|
|
3342
|
+
);
|
|
3343
|
+
return false;
|
|
3344
|
+
}
|
|
3345
|
+
state.textures.get(key)?.dispose();
|
|
3346
|
+
setState((s) => {
|
|
3347
|
+
const textures = new Map(s.textures);
|
|
3348
|
+
textures.delete(key);
|
|
3349
|
+
const nextRefs = new Map(s._textureRefs);
|
|
3350
|
+
nextRefs.delete(key);
|
|
3351
|
+
return { textures, _textureRefs: nextRefs };
|
|
3352
|
+
});
|
|
3353
|
+
return true;
|
|
3354
|
+
},
|
|
3355
|
+
disposeAll() {
|
|
3356
|
+
for (const texture of getState().textures.values()) texture.dispose();
|
|
3357
|
+
setState({ textures: /* @__PURE__ */ new Map(), _textureRefs: /* @__PURE__ */ new Map() });
|
|
3358
|
+
}
|
|
3170
3359
|
};
|
|
3171
3360
|
}, [store]);
|
|
3361
|
+
const subscribe = selector ? () => selector(registry) : (state) => state.textures;
|
|
3362
|
+
const selected = useThree(subscribe);
|
|
3363
|
+
return selector ? selected : registry;
|
|
3172
3364
|
}
|
|
3173
3365
|
|
|
3174
3366
|
function useRenderTarget(widthOrOptions, heightOrOptions, options) {
|
|
@@ -15019,7 +15211,6 @@ function createRoot(canvas) {
|
|
|
15019
15211
|
events,
|
|
15020
15212
|
onCreated: onCreatedCallback,
|
|
15021
15213
|
shadows = false,
|
|
15022
|
-
textureColorSpace = webgpu.SRGBColorSpace,
|
|
15023
15214
|
orthographic = false,
|
|
15024
15215
|
frameloop = "always",
|
|
15025
15216
|
dpr = [1, 2],
|
|
@@ -15034,6 +15225,7 @@ function createRoot(canvas) {
|
|
|
15034
15225
|
_sizeProps,
|
|
15035
15226
|
forceEven
|
|
15036
15227
|
} = props;
|
|
15228
|
+
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
15229
|
const state = store.getState();
|
|
15038
15230
|
const defaultGPUProps = {
|
|
15039
15231
|
canvas,
|
|
@@ -15071,6 +15263,12 @@ function createRoot(canvas) {
|
|
|
15071
15263
|
} else if (!state.internal.actualRenderer) {
|
|
15072
15264
|
renderer = await resolveRenderer(rendererConfig, defaultGPUProps, webgpu.WebGPURenderer);
|
|
15073
15265
|
if (!renderer.hasInitialized?.()) {
|
|
15266
|
+
const size2 = computeInitialSize(canvas, propsSize);
|
|
15267
|
+
if (size2.width > 0 && size2.height > 0) {
|
|
15268
|
+
const pixelRatio = calculateDpr(dpr);
|
|
15269
|
+
canvas.width = size2.width * pixelRatio;
|
|
15270
|
+
canvas.height = size2.height * pixelRatio;
|
|
15271
|
+
}
|
|
15074
15272
|
await renderer.init();
|
|
15075
15273
|
}
|
|
15076
15274
|
const backend = renderer.backend;
|
|
@@ -15180,7 +15378,7 @@ function createRoot(canvas) {
|
|
|
15180
15378
|
lastConfiguredProps.performance = performance;
|
|
15181
15379
|
}
|
|
15182
15380
|
if (!state.xr) {
|
|
15183
|
-
const handleXRFrame = (timestamp,
|
|
15381
|
+
const handleXRFrame = (timestamp, _frame) => {
|
|
15184
15382
|
const state2 = store.getState();
|
|
15185
15383
|
if (state2.frameloop === "never") return;
|
|
15186
15384
|
advance(timestamp);
|
|
@@ -15216,15 +15414,22 @@ function createRoot(canvas) {
|
|
|
15216
15414
|
const oldType = renderer.shadowMap.type;
|
|
15217
15415
|
renderer.shadowMap.enabled = !!shadows;
|
|
15218
15416
|
if (is.boo(shadows)) {
|
|
15219
|
-
renderer.shadowMap.type = webgpu.
|
|
15417
|
+
renderer.shadowMap.type = webgpu.PCFShadowMap;
|
|
15220
15418
|
} else if (is.str(shadows)) {
|
|
15419
|
+
if (shadows === "soft") {
|
|
15420
|
+
notifyDepreciated({
|
|
15421
|
+
heading: 'shadows="soft" is deprecated',
|
|
15422
|
+
body: "Three has depreciated soft and improved basic PCFShadows, we converted for you.",
|
|
15423
|
+
link: "https://github.com/mrdoob/three.js/wiki/Migration-Guide?utm_source=chatgpt.com#181--182"
|
|
15424
|
+
});
|
|
15425
|
+
}
|
|
15221
15426
|
const types = {
|
|
15222
15427
|
basic: webgpu.BasicShadowMap,
|
|
15223
15428
|
percentage: webgpu.PCFShadowMap,
|
|
15224
|
-
soft: webgpu.
|
|
15429
|
+
soft: webgpu.PCFShadowMap,
|
|
15225
15430
|
variance: webgpu.VSMShadowMap
|
|
15226
15431
|
};
|
|
15227
|
-
renderer.shadowMap.type = types[shadows] ?? webgpu.
|
|
15432
|
+
renderer.shadowMap.type = types[shadows] ?? webgpu.PCFShadowMap;
|
|
15228
15433
|
} else if (is.obj(shadows)) {
|
|
15229
15434
|
Object.assign(renderer.shadowMap, shadows);
|
|
15230
15435
|
}
|
|
@@ -15240,13 +15445,24 @@ function createRoot(canvas) {
|
|
|
15240
15445
|
if (state.textureColorSpace !== textureColorSpace) state.set(() => ({ textureColorSpace }));
|
|
15241
15446
|
lastConfiguredProps.textureColorSpace = textureColorSpace;
|
|
15242
15447
|
}
|
|
15448
|
+
const r3fProps = ["textureColorSpace"];
|
|
15449
|
+
const constructorOnlyProps = ["samples", "antialias", "alpha", "canvas", "powerPreference"];
|
|
15450
|
+
const nonApplyProps = [...r3fProps, ...constructorOnlyProps];
|
|
15243
15451
|
if (glConfig && !is.fun(glConfig) && !isRenderer(glConfig) && !is.equ(glConfig, renderer, shallowLoose)) {
|
|
15244
|
-
|
|
15452
|
+
const glProps = {};
|
|
15453
|
+
for (const key in glConfig) {
|
|
15454
|
+
if (!nonApplyProps.includes(key)) glProps[key] = glConfig[key];
|
|
15455
|
+
}
|
|
15456
|
+
applyProps(renderer, glProps);
|
|
15245
15457
|
}
|
|
15246
15458
|
if (rendererConfig && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && state.renderer) {
|
|
15247
15459
|
const currentRenderer = state.renderer;
|
|
15248
15460
|
if (!is.equ(rendererConfig, currentRenderer, shallowLoose)) {
|
|
15249
|
-
|
|
15461
|
+
const rendererProps = {};
|
|
15462
|
+
for (const key in rendererConfig) {
|
|
15463
|
+
if (!nonApplyProps.includes(key)) rendererProps[key] = rendererConfig[key];
|
|
15464
|
+
}
|
|
15465
|
+
applyProps(currentRenderer, rendererProps);
|
|
15250
15466
|
}
|
|
15251
15467
|
}
|
|
15252
15468
|
const scheduler = getScheduler();
|
|
@@ -15272,6 +15488,18 @@ function createRoot(canvas) {
|
|
|
15272
15488
|
system: true
|
|
15273
15489
|
}
|
|
15274
15490
|
);
|
|
15491
|
+
const unregisterEventsFlush = scheduler.register(
|
|
15492
|
+
() => {
|
|
15493
|
+
const state2 = store.getState();
|
|
15494
|
+
state2.events.flush?.();
|
|
15495
|
+
},
|
|
15496
|
+
{
|
|
15497
|
+
id: `${newRootId}_events`,
|
|
15498
|
+
rootId: newRootId,
|
|
15499
|
+
phase: "input",
|
|
15500
|
+
system: true
|
|
15501
|
+
}
|
|
15502
|
+
);
|
|
15275
15503
|
const unregisterFrustum = scheduler.register(
|
|
15276
15504
|
() => {
|
|
15277
15505
|
const state2 = store.getState();
|
|
@@ -15306,7 +15534,7 @@ function createRoot(canvas) {
|
|
|
15306
15534
|
const userHandlesRender = scheduler.hasUserJobsInPhase("render", newRootId);
|
|
15307
15535
|
if (userHandlesRender || state2.internal.priority) return;
|
|
15308
15536
|
try {
|
|
15309
|
-
if (state2.
|
|
15537
|
+
if (state2.renderPipeline?.render) state2.renderPipeline.render();
|
|
15310
15538
|
else if (renderer2?.render) renderer2.render(state2.scene, state2.camera);
|
|
15311
15539
|
} catch (error) {
|
|
15312
15540
|
state2.setError(error instanceof Error ? error : new Error(String(error)));
|
|
@@ -15331,6 +15559,7 @@ function createRoot(canvas) {
|
|
|
15331
15559
|
unregisterRoot: () => {
|
|
15332
15560
|
unregisterRoot();
|
|
15333
15561
|
unregisterCanvasTarget();
|
|
15562
|
+
unregisterEventsFlush();
|
|
15334
15563
|
unregisterFrustum();
|
|
15335
15564
|
unregisterVisibility();
|
|
15336
15565
|
unregisterRender();
|
|
@@ -15496,9 +15725,13 @@ function PortalInner({ state = {}, children, container }) {
|
|
|
15496
15725
|
const store = traditional.createWithEqualityFn((set, get) => ({ ...rest, set, get }));
|
|
15497
15726
|
const onMutate = (prev) => store.setState((state2) => inject.current(prev, state2));
|
|
15498
15727
|
onMutate(previousRoot.getState());
|
|
15499
|
-
previousRoot.subscribe(onMutate);
|
|
15500
15728
|
return store;
|
|
15501
15729
|
}, [previousRoot, container]);
|
|
15730
|
+
useIsomorphicLayoutEffect(() => {
|
|
15731
|
+
const onMutate = (prev) => usePortalStore.setState((state2) => inject.current(prev, state2));
|
|
15732
|
+
const unsubscribe = previousRoot.subscribe(onMutate);
|
|
15733
|
+
return unsubscribe;
|
|
15734
|
+
}, [previousRoot, usePortalStore]);
|
|
15502
15735
|
return (
|
|
15503
15736
|
// @ts-ignore, reconciler types are not maintained
|
|
15504
15737
|
/* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: reconciler.createPortal(
|
|
@@ -15543,8 +15776,18 @@ function CanvasImpl({
|
|
|
15543
15776
|
forceEven,
|
|
15544
15777
|
...props
|
|
15545
15778
|
}) {
|
|
15546
|
-
const
|
|
15547
|
-
|
|
15779
|
+
const isRendererConfig = typeof rendererProp === "object" && rendererProp !== null && !("render" in rendererProp) && ("primaryCanvas" in rendererProp || "scheduler" in rendererProp);
|
|
15780
|
+
let primaryCanvas;
|
|
15781
|
+
let scheduler;
|
|
15782
|
+
let renderer;
|
|
15783
|
+
if (isRendererConfig) {
|
|
15784
|
+
const { primaryCanvas: pc, scheduler: sc, ...rest } = rendererProp;
|
|
15785
|
+
primaryCanvas = pc;
|
|
15786
|
+
scheduler = sc;
|
|
15787
|
+
renderer = Object.keys(rest).length > 0 ? rest : rendererProp;
|
|
15788
|
+
} else {
|
|
15789
|
+
renderer = rendererProp;
|
|
15790
|
+
}
|
|
15548
15791
|
React__namespace.useMemo(() => extend(THREE), []);
|
|
15549
15792
|
const Bridge = useBridge();
|
|
15550
15793
|
const backgroundProps = React__namespace.useMemo(() => {
|
|
@@ -15719,6 +15962,7 @@ function CanvasImpl({
|
|
|
15719
15962
|
queueMicrotask(() => {
|
|
15720
15963
|
const rootEntry = _roots.get(canvas);
|
|
15721
15964
|
if (rootEntry?.store) {
|
|
15965
|
+
console.log("[R3F] HMR detected \u2014 rebuilding nodes/uniforms");
|
|
15722
15966
|
rootEntry.store.setState((state) => ({
|
|
15723
15967
|
nodes: {},
|
|
15724
15968
|
uniforms: {},
|
|
@@ -15730,8 +15974,7 @@ function CanvasImpl({
|
|
|
15730
15974
|
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
15975
|
const hot = undefined;
|
|
15732
15976
|
hot.on("vite:afterUpdate", handleHMR);
|
|
15733
|
-
return () => hot.
|
|
15734
|
-
});
|
|
15977
|
+
return () => hot.off?.("vite:afterUpdate", handleHMR);
|
|
15735
15978
|
}
|
|
15736
15979
|
if (typeof module !== "undefined" && module.hot) {
|
|
15737
15980
|
const hot = module.hot;
|
|
@@ -15754,7 +15997,16 @@ function CanvasImpl({
|
|
|
15754
15997
|
...style
|
|
15755
15998
|
},
|
|
15756
15999
|
...props,
|
|
15757
|
-
children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
16000
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
16001
|
+
"canvas",
|
|
16002
|
+
{
|
|
16003
|
+
ref: canvasRef,
|
|
16004
|
+
id,
|
|
16005
|
+
className: "r3f-canvas",
|
|
16006
|
+
style: { display: "block", width: "100%", height: "100%" },
|
|
16007
|
+
children: fallback
|
|
16008
|
+
}
|
|
16009
|
+
) })
|
|
15758
16010
|
}
|
|
15759
16011
|
);
|
|
15760
16012
|
}
|
|
@@ -15830,6 +16082,8 @@ function createScopedStore(data) {
|
|
|
15830
16082
|
function createLazyCreatorState(state) {
|
|
15831
16083
|
let _uniforms = null;
|
|
15832
16084
|
let _nodes = null;
|
|
16085
|
+
let _buffers = null;
|
|
16086
|
+
let _gpuStorage = null;
|
|
15833
16087
|
return Object.create(state, {
|
|
15834
16088
|
uniforms: {
|
|
15835
16089
|
get() {
|
|
@@ -15840,6 +16094,16 @@ function createLazyCreatorState(state) {
|
|
|
15840
16094
|
get() {
|
|
15841
16095
|
return _nodes ?? (_nodes = createScopedStore(state.nodes));
|
|
15842
16096
|
}
|
|
16097
|
+
},
|
|
16098
|
+
buffers: {
|
|
16099
|
+
get() {
|
|
16100
|
+
return _buffers ?? (_buffers = createScopedStore(state.buffers));
|
|
16101
|
+
}
|
|
16102
|
+
},
|
|
16103
|
+
gpuStorage: {
|
|
16104
|
+
get() {
|
|
16105
|
+
return _gpuStorage ?? (_gpuStorage = createScopedStore(state.gpuStorage));
|
|
16106
|
+
}
|
|
15843
16107
|
}
|
|
15844
16108
|
});
|
|
15845
16109
|
}
|
|
@@ -15922,6 +16186,10 @@ function vectorize(inObject) {
|
|
|
15922
16186
|
if (obj.isVector2 || obj.isVector3 || obj.isVector4) return inObject;
|
|
15923
16187
|
if (obj.isMatrix3 || obj.isMatrix4) return inObject;
|
|
15924
16188
|
if (obj.isColor || obj.isEuler || obj.isQuaternion || obj.isSpherical) return inObject;
|
|
16189
|
+
if ("r" in obj && "g" in obj && "b" in obj && typeof obj.r === "number" && typeof obj.g === "number" && typeof obj.b === "number") {
|
|
16190
|
+
const scale = obj.r > 1 || obj.g > 1 || obj.b > 1 ? 1 / 255 : 1;
|
|
16191
|
+
return new webgpu.Color(obj.r * scale, obj.g * scale, obj.b * scale);
|
|
16192
|
+
}
|
|
15925
16193
|
if ("x" in obj && "y" in obj && typeof obj.x === "number" && typeof obj.y === "number") {
|
|
15926
16194
|
if ("w" in obj && typeof obj.w === "number" && "z" in obj && typeof obj.z === "number") {
|
|
15927
16195
|
return new webgpu.Vector4(obj.x, obj.y, obj.z, obj.w);
|
|
@@ -16377,7 +16645,351 @@ function useLocalNodes(creator) {
|
|
|
16377
16645
|
}, [store, creator, uniforms, nodes, textures, hmrVersion]);
|
|
16378
16646
|
}
|
|
16379
16647
|
|
|
16380
|
-
|
|
16648
|
+
const isBufferLike = (value) => {
|
|
16649
|
+
if (value === null || typeof value !== "object") return false;
|
|
16650
|
+
if (ArrayBuffer.isView(value)) return true;
|
|
16651
|
+
if ("isBufferAttribute" in value) return true;
|
|
16652
|
+
if ("uuid" in value || "nodeType" in value) return true;
|
|
16653
|
+
return false;
|
|
16654
|
+
};
|
|
16655
|
+
const disposeBuffer = (buffer) => {
|
|
16656
|
+
if (buffer === null || typeof buffer !== "object") return;
|
|
16657
|
+
if ("dispose" in buffer && typeof buffer.dispose === "function") {
|
|
16658
|
+
buffer.dispose();
|
|
16659
|
+
}
|
|
16660
|
+
};
|
|
16661
|
+
function useBuffers(creatorOrScope, scope) {
|
|
16662
|
+
const store = useStore();
|
|
16663
|
+
const removeBuffers = React.useCallback(
|
|
16664
|
+
(names, targetScope) => {
|
|
16665
|
+
const nameArray = Array.isArray(names) ? names : [names];
|
|
16666
|
+
store.setState((state) => {
|
|
16667
|
+
if (targetScope) {
|
|
16668
|
+
const currentScope = { ...state.buffers[targetScope] };
|
|
16669
|
+
for (const name of nameArray) delete currentScope[name];
|
|
16670
|
+
return { buffers: { ...state.buffers, [targetScope]: currentScope } };
|
|
16671
|
+
}
|
|
16672
|
+
const buffers2 = { ...state.buffers };
|
|
16673
|
+
for (const name of nameArray) if (isBufferLike(buffers2[name])) delete buffers2[name];
|
|
16674
|
+
return { buffers: buffers2 };
|
|
16675
|
+
});
|
|
16676
|
+
},
|
|
16677
|
+
[store]
|
|
16678
|
+
);
|
|
16679
|
+
const clearBuffers = React.useCallback(
|
|
16680
|
+
(targetScope) => {
|
|
16681
|
+
store.setState((state) => {
|
|
16682
|
+
if (targetScope && targetScope !== "root") {
|
|
16683
|
+
const { [targetScope]: _, ...rest } = state.buffers;
|
|
16684
|
+
return { buffers: rest };
|
|
16685
|
+
}
|
|
16686
|
+
if (targetScope === "root") {
|
|
16687
|
+
const buffers2 = {};
|
|
16688
|
+
for (const [key, value] of Object.entries(state.buffers)) {
|
|
16689
|
+
if (!isBufferLike(value)) buffers2[key] = value;
|
|
16690
|
+
}
|
|
16691
|
+
return { buffers: buffers2 };
|
|
16692
|
+
}
|
|
16693
|
+
return { buffers: {} };
|
|
16694
|
+
});
|
|
16695
|
+
},
|
|
16696
|
+
[store]
|
|
16697
|
+
);
|
|
16698
|
+
const rebuildBuffers = React.useCallback(
|
|
16699
|
+
(targetScope) => {
|
|
16700
|
+
store.setState((state) => {
|
|
16701
|
+
let newBuffers = state.buffers;
|
|
16702
|
+
if (targetScope && targetScope !== "root") {
|
|
16703
|
+
const { [targetScope]: _, ...rest } = state.buffers;
|
|
16704
|
+
newBuffers = rest;
|
|
16705
|
+
} else if (targetScope === "root") {
|
|
16706
|
+
newBuffers = {};
|
|
16707
|
+
for (const [key, value] of Object.entries(state.buffers)) {
|
|
16708
|
+
if (!isBufferLike(value)) newBuffers[key] = value;
|
|
16709
|
+
}
|
|
16710
|
+
} else {
|
|
16711
|
+
newBuffers = {};
|
|
16712
|
+
}
|
|
16713
|
+
return { buffers: newBuffers, _hmrVersion: state._hmrVersion + 1 };
|
|
16714
|
+
});
|
|
16715
|
+
},
|
|
16716
|
+
[store]
|
|
16717
|
+
);
|
|
16718
|
+
const disposeBuffers = React.useCallback(
|
|
16719
|
+
(names, targetScope) => {
|
|
16720
|
+
const nameArray = Array.isArray(names) ? names : [names];
|
|
16721
|
+
const state = store.getState();
|
|
16722
|
+
for (const name of nameArray) {
|
|
16723
|
+
const buffer = targetScope ? state.buffers[targetScope]?.[name] : state.buffers[name];
|
|
16724
|
+
if (buffer && isBufferLike(buffer)) {
|
|
16725
|
+
disposeBuffer(buffer);
|
|
16726
|
+
}
|
|
16727
|
+
}
|
|
16728
|
+
removeBuffers(names, targetScope);
|
|
16729
|
+
},
|
|
16730
|
+
[store, removeBuffers]
|
|
16731
|
+
);
|
|
16732
|
+
const isReader = creatorOrScope === void 0 || typeof creatorOrScope === "string";
|
|
16733
|
+
const storeBuffers = useThree((s) => s.buffers);
|
|
16734
|
+
const hmrVersion = useThree((s) => s._hmrVersion);
|
|
16735
|
+
const scopeDep = typeof creatorOrScope === "string" ? creatorOrScope : scope;
|
|
16736
|
+
const readerDep = isReader ? storeBuffers : null;
|
|
16737
|
+
const creatorDep = isReader ? null : hmrVersion;
|
|
16738
|
+
const buffers = React.useMemo(() => {
|
|
16739
|
+
if (creatorOrScope === void 0) {
|
|
16740
|
+
return storeBuffers;
|
|
16741
|
+
}
|
|
16742
|
+
if (typeof creatorOrScope === "string") {
|
|
16743
|
+
const scopeData = storeBuffers[creatorOrScope];
|
|
16744
|
+
if (scopeData && !isBufferLike(scopeData)) return scopeData;
|
|
16745
|
+
return {};
|
|
16746
|
+
}
|
|
16747
|
+
const state = store.getState();
|
|
16748
|
+
const set = store.setState;
|
|
16749
|
+
const creator = creatorOrScope;
|
|
16750
|
+
const wrappedState = createLazyCreatorState(state);
|
|
16751
|
+
const created = creator(wrappedState);
|
|
16752
|
+
const result = {};
|
|
16753
|
+
let hasNewBuffers = false;
|
|
16754
|
+
if (scope) {
|
|
16755
|
+
const currentScope = state.buffers[scope] ?? {};
|
|
16756
|
+
for (const [name, buffer] of Object.entries(created)) {
|
|
16757
|
+
if (currentScope[name]) {
|
|
16758
|
+
result[name] = currentScope[name];
|
|
16759
|
+
} else {
|
|
16760
|
+
if ("setName" in buffer && typeof buffer.setName === "function") {
|
|
16761
|
+
buffer.setName(`${scope}.${name}`);
|
|
16762
|
+
}
|
|
16763
|
+
result[name] = buffer;
|
|
16764
|
+
hasNewBuffers = true;
|
|
16765
|
+
}
|
|
16766
|
+
}
|
|
16767
|
+
if (hasNewBuffers) {
|
|
16768
|
+
set((s) => ({
|
|
16769
|
+
buffers: {
|
|
16770
|
+
...s.buffers,
|
|
16771
|
+
[scope]: { ...s.buffers[scope], ...result }
|
|
16772
|
+
}
|
|
16773
|
+
}));
|
|
16774
|
+
}
|
|
16775
|
+
return result;
|
|
16776
|
+
}
|
|
16777
|
+
for (const [name, buffer] of Object.entries(created)) {
|
|
16778
|
+
const existing = state.buffers[name];
|
|
16779
|
+
if (existing && isBufferLike(existing)) {
|
|
16780
|
+
result[name] = existing;
|
|
16781
|
+
} else {
|
|
16782
|
+
if ("setName" in buffer && typeof buffer.setName === "function") {
|
|
16783
|
+
buffer.setName(name);
|
|
16784
|
+
}
|
|
16785
|
+
result[name] = buffer;
|
|
16786
|
+
hasNewBuffers = true;
|
|
16787
|
+
}
|
|
16788
|
+
}
|
|
16789
|
+
if (hasNewBuffers) {
|
|
16790
|
+
set((s) => ({ buffers: { ...s.buffers, ...result } }));
|
|
16791
|
+
}
|
|
16792
|
+
return result;
|
|
16793
|
+
}, [store, scopeDep, readerDep, creatorDep]);
|
|
16794
|
+
return { ...buffers, removeBuffers, clearBuffers, rebuildBuffers, disposeBuffers };
|
|
16795
|
+
}
|
|
16796
|
+
function rebuildAllBuffers(store, scope) {
|
|
16797
|
+
store.setState((state) => {
|
|
16798
|
+
let newBuffers = state.buffers;
|
|
16799
|
+
if (scope && scope !== "root") {
|
|
16800
|
+
const { [scope]: _, ...rest } = state.buffers;
|
|
16801
|
+
newBuffers = rest;
|
|
16802
|
+
} else if (scope === "root") {
|
|
16803
|
+
newBuffers = {};
|
|
16804
|
+
for (const [key, value] of Object.entries(state.buffers)) {
|
|
16805
|
+
if (!isBufferLike(value)) newBuffers[key] = value;
|
|
16806
|
+
}
|
|
16807
|
+
} else {
|
|
16808
|
+
newBuffers = {};
|
|
16809
|
+
}
|
|
16810
|
+
return { buffers: newBuffers, _hmrVersion: state._hmrVersion + 1 };
|
|
16811
|
+
});
|
|
16812
|
+
}
|
|
16813
|
+
|
|
16814
|
+
const isStorageLike = (value) => {
|
|
16815
|
+
if (value === null || typeof value !== "object") return false;
|
|
16816
|
+
if ("isTexture" in value) return true;
|
|
16817
|
+
if ("isData3DTexture" in value) return true;
|
|
16818
|
+
if ("uuid" in value || "nodeType" in value) return true;
|
|
16819
|
+
return false;
|
|
16820
|
+
};
|
|
16821
|
+
const disposeStorage = (storage) => {
|
|
16822
|
+
if (storage === null || typeof storage !== "object") return;
|
|
16823
|
+
if ("dispose" in storage && typeof storage.dispose === "function") {
|
|
16824
|
+
storage.dispose();
|
|
16825
|
+
}
|
|
16826
|
+
};
|
|
16827
|
+
function useGPUStorage(creatorOrScope, scope) {
|
|
16828
|
+
const store = useStore();
|
|
16829
|
+
const removeStorage = React.useCallback(
|
|
16830
|
+
(names, targetScope) => {
|
|
16831
|
+
const nameArray = Array.isArray(names) ? names : [names];
|
|
16832
|
+
store.setState((state) => {
|
|
16833
|
+
if (targetScope) {
|
|
16834
|
+
const currentScope = { ...state.gpuStorage[targetScope] };
|
|
16835
|
+
for (const name of nameArray) delete currentScope[name];
|
|
16836
|
+
return { gpuStorage: { ...state.gpuStorage, [targetScope]: currentScope } };
|
|
16837
|
+
}
|
|
16838
|
+
const gpuStorage2 = { ...state.gpuStorage };
|
|
16839
|
+
for (const name of nameArray) if (isStorageLike(gpuStorage2[name])) delete gpuStorage2[name];
|
|
16840
|
+
return { gpuStorage: gpuStorage2 };
|
|
16841
|
+
});
|
|
16842
|
+
},
|
|
16843
|
+
[store]
|
|
16844
|
+
);
|
|
16845
|
+
const clearStorage = React.useCallback(
|
|
16846
|
+
(targetScope) => {
|
|
16847
|
+
store.setState((state) => {
|
|
16848
|
+
if (targetScope && targetScope !== "root") {
|
|
16849
|
+
const { [targetScope]: _, ...rest } = state.gpuStorage;
|
|
16850
|
+
return { gpuStorage: rest };
|
|
16851
|
+
}
|
|
16852
|
+
if (targetScope === "root") {
|
|
16853
|
+
const gpuStorage2 = {};
|
|
16854
|
+
for (const [key, value] of Object.entries(state.gpuStorage)) {
|
|
16855
|
+
if (!isStorageLike(value)) gpuStorage2[key] = value;
|
|
16856
|
+
}
|
|
16857
|
+
return { gpuStorage: gpuStorage2 };
|
|
16858
|
+
}
|
|
16859
|
+
return { gpuStorage: {} };
|
|
16860
|
+
});
|
|
16861
|
+
},
|
|
16862
|
+
[store]
|
|
16863
|
+
);
|
|
16864
|
+
const rebuildStorage = React.useCallback(
|
|
16865
|
+
(targetScope) => {
|
|
16866
|
+
store.setState((state) => {
|
|
16867
|
+
let newStorage = state.gpuStorage;
|
|
16868
|
+
if (targetScope && targetScope !== "root") {
|
|
16869
|
+
const { [targetScope]: _, ...rest } = state.gpuStorage;
|
|
16870
|
+
newStorage = rest;
|
|
16871
|
+
} else if (targetScope === "root") {
|
|
16872
|
+
newStorage = {};
|
|
16873
|
+
for (const [key, value] of Object.entries(state.gpuStorage)) {
|
|
16874
|
+
if (!isStorageLike(value)) newStorage[key] = value;
|
|
16875
|
+
}
|
|
16876
|
+
} else {
|
|
16877
|
+
newStorage = {};
|
|
16878
|
+
}
|
|
16879
|
+
return { gpuStorage: newStorage, _hmrVersion: state._hmrVersion + 1 };
|
|
16880
|
+
});
|
|
16881
|
+
},
|
|
16882
|
+
[store]
|
|
16883
|
+
);
|
|
16884
|
+
const disposeStorageFn = React.useCallback(
|
|
16885
|
+
(names, targetScope) => {
|
|
16886
|
+
const nameArray = Array.isArray(names) ? names : [names];
|
|
16887
|
+
const state = store.getState();
|
|
16888
|
+
for (const name of nameArray) {
|
|
16889
|
+
const storage = targetScope ? state.gpuStorage[targetScope]?.[name] : state.gpuStorage[name];
|
|
16890
|
+
if (storage && isStorageLike(storage)) {
|
|
16891
|
+
disposeStorage(storage);
|
|
16892
|
+
}
|
|
16893
|
+
}
|
|
16894
|
+
removeStorage(names, targetScope);
|
|
16895
|
+
},
|
|
16896
|
+
[store, removeStorage]
|
|
16897
|
+
);
|
|
16898
|
+
const isReader = creatorOrScope === void 0 || typeof creatorOrScope === "string";
|
|
16899
|
+
const storeStorage = useThree((s) => s.gpuStorage);
|
|
16900
|
+
const hmrVersion = useThree((s) => s._hmrVersion);
|
|
16901
|
+
const scopeDep = typeof creatorOrScope === "string" ? creatorOrScope : scope;
|
|
16902
|
+
const readerDep = isReader ? storeStorage : null;
|
|
16903
|
+
const creatorDep = isReader ? null : hmrVersion;
|
|
16904
|
+
const gpuStorage = React.useMemo(() => {
|
|
16905
|
+
if (creatorOrScope === void 0) {
|
|
16906
|
+
return storeStorage;
|
|
16907
|
+
}
|
|
16908
|
+
if (typeof creatorOrScope === "string") {
|
|
16909
|
+
const scopeData = storeStorage[creatorOrScope];
|
|
16910
|
+
if (scopeData && !isStorageLike(scopeData)) return scopeData;
|
|
16911
|
+
return {};
|
|
16912
|
+
}
|
|
16913
|
+
const state = store.getState();
|
|
16914
|
+
const set = store.setState;
|
|
16915
|
+
const creator = creatorOrScope;
|
|
16916
|
+
const wrappedState = createLazyCreatorState(state);
|
|
16917
|
+
const created = creator(wrappedState);
|
|
16918
|
+
const result = {};
|
|
16919
|
+
let hasNewStorage = false;
|
|
16920
|
+
if (scope) {
|
|
16921
|
+
const currentScope = state.gpuStorage[scope] ?? {};
|
|
16922
|
+
for (const [name, storage] of Object.entries(created)) {
|
|
16923
|
+
if (currentScope[name]) {
|
|
16924
|
+
result[name] = currentScope[name];
|
|
16925
|
+
} else {
|
|
16926
|
+
if ("setName" in storage && typeof storage.setName === "function") {
|
|
16927
|
+
storage.setName(`${scope}.${name}`);
|
|
16928
|
+
}
|
|
16929
|
+
if ("name" in storage && typeof storage.name === "string") {
|
|
16930
|
+
storage.name = `${scope}.${name}`;
|
|
16931
|
+
}
|
|
16932
|
+
result[name] = storage;
|
|
16933
|
+
hasNewStorage = true;
|
|
16934
|
+
}
|
|
16935
|
+
}
|
|
16936
|
+
if (hasNewStorage) {
|
|
16937
|
+
set((s) => ({
|
|
16938
|
+
gpuStorage: {
|
|
16939
|
+
...s.gpuStorage,
|
|
16940
|
+
[scope]: { ...s.gpuStorage[scope], ...result }
|
|
16941
|
+
}
|
|
16942
|
+
}));
|
|
16943
|
+
}
|
|
16944
|
+
return result;
|
|
16945
|
+
}
|
|
16946
|
+
for (const [name, storage] of Object.entries(created)) {
|
|
16947
|
+
const existing = state.gpuStorage[name];
|
|
16948
|
+
if (existing && isStorageLike(existing)) {
|
|
16949
|
+
result[name] = existing;
|
|
16950
|
+
} else {
|
|
16951
|
+
if ("setName" in storage && typeof storage.setName === "function") {
|
|
16952
|
+
storage.setName(name);
|
|
16953
|
+
}
|
|
16954
|
+
if ("name" in storage && typeof storage.name === "string") {
|
|
16955
|
+
storage.name = name;
|
|
16956
|
+
}
|
|
16957
|
+
result[name] = storage;
|
|
16958
|
+
hasNewStorage = true;
|
|
16959
|
+
}
|
|
16960
|
+
}
|
|
16961
|
+
if (hasNewStorage) {
|
|
16962
|
+
set((s) => ({ gpuStorage: { ...s.gpuStorage, ...result } }));
|
|
16963
|
+
}
|
|
16964
|
+
return result;
|
|
16965
|
+
}, [store, scopeDep, readerDep, creatorDep]);
|
|
16966
|
+
return {
|
|
16967
|
+
...gpuStorage,
|
|
16968
|
+
removeStorage,
|
|
16969
|
+
clearStorage,
|
|
16970
|
+
rebuildStorage,
|
|
16971
|
+
disposeStorage: disposeStorageFn
|
|
16972
|
+
};
|
|
16973
|
+
}
|
|
16974
|
+
function rebuildAllStorage(store, scope) {
|
|
16975
|
+
store.setState((state) => {
|
|
16976
|
+
let newStorage = state.gpuStorage;
|
|
16977
|
+
if (scope && scope !== "root") {
|
|
16978
|
+
const { [scope]: _, ...rest } = state.gpuStorage;
|
|
16979
|
+
newStorage = rest;
|
|
16980
|
+
} else if (scope === "root") {
|
|
16981
|
+
newStorage = {};
|
|
16982
|
+
for (const [key, value] of Object.entries(state.gpuStorage)) {
|
|
16983
|
+
if (!isStorageLike(value)) newStorage[key] = value;
|
|
16984
|
+
}
|
|
16985
|
+
} else {
|
|
16986
|
+
newStorage = {};
|
|
16987
|
+
}
|
|
16988
|
+
return { gpuStorage: newStorage, _hmrVersion: state._hmrVersion + 1 };
|
|
16989
|
+
});
|
|
16990
|
+
}
|
|
16991
|
+
|
|
16992
|
+
function useRenderPipeline(mainCB, setupCB) {
|
|
16381
16993
|
const store = useStore();
|
|
16382
16994
|
const { scene, camera, renderer, isLegacy } = useThree();
|
|
16383
16995
|
const callbacksRanRef = React.useRef(false);
|
|
@@ -16396,7 +17008,7 @@ function usePostProcessing(mainCB, setupCB) {
|
|
|
16396
17008
|
}, [store]);
|
|
16397
17009
|
const reset = React.useCallback(() => {
|
|
16398
17010
|
store.setState({
|
|
16399
|
-
|
|
17011
|
+
renderPipeline: null,
|
|
16400
17012
|
passes: {}
|
|
16401
17013
|
});
|
|
16402
17014
|
callbacksRanRef.current = false;
|
|
@@ -16409,13 +17021,13 @@ function usePostProcessing(mainCB, setupCB) {
|
|
|
16409
17021
|
}, []);
|
|
16410
17022
|
React.useLayoutEffect(() => {
|
|
16411
17023
|
if (isLegacy) {
|
|
16412
|
-
throw new Error("
|
|
17024
|
+
throw new Error("useRenderPipeline is only available with WebGPU renderer. Set renderer prop on Canvas.");
|
|
16413
17025
|
}
|
|
16414
17026
|
if (!renderer || !scene || !camera) return;
|
|
16415
17027
|
const state = store.getState();
|
|
16416
17028
|
const set = store.setState;
|
|
16417
17029
|
try {
|
|
16418
|
-
let pp = state.
|
|
17030
|
+
let pp = state.renderPipeline;
|
|
16419
17031
|
let currentPasses = { ...state.passes };
|
|
16420
17032
|
let justCreatedPP = false;
|
|
16421
17033
|
if (!pp) {
|
|
@@ -16432,7 +17044,7 @@ function usePostProcessing(mainCB, setupCB) {
|
|
|
16432
17044
|
}
|
|
16433
17045
|
currentPasses.scenePass = scenePass;
|
|
16434
17046
|
if (!pp.outputNode || justCreatedPP) pp.outputNode = scenePass;
|
|
16435
|
-
set({
|
|
17047
|
+
set({ renderPipeline: pp, passes: currentPasses });
|
|
16436
17048
|
const shouldRunCallbacks = justCreatedPP || !callbacksRanRef.current || !cacheValid;
|
|
16437
17049
|
if (shouldRunCallbacks) {
|
|
16438
17050
|
if (setupCBRef.current) {
|
|
@@ -16454,19 +17066,19 @@ function usePostProcessing(mainCB, setupCB) {
|
|
|
16454
17066
|
callbacksRanRef.current = true;
|
|
16455
17067
|
}
|
|
16456
17068
|
} catch (error) {
|
|
16457
|
-
console.error("[
|
|
17069
|
+
console.error("[useRenderPipeline] Setup error:", error);
|
|
16458
17070
|
}
|
|
16459
17071
|
}, [store, renderer, scene, camera, isLegacy, rebuildVersion]);
|
|
16460
17072
|
const passes = useThree((s) => s.passes);
|
|
16461
|
-
const
|
|
17073
|
+
const renderPipeline = useThree((s) => s.renderPipeline);
|
|
16462
17074
|
return {
|
|
16463
17075
|
passes,
|
|
16464
|
-
|
|
17076
|
+
renderPipeline,
|
|
16465
17077
|
clearPasses,
|
|
16466
17078
|
reset,
|
|
16467
17079
|
rebuild,
|
|
16468
|
-
// isReady indicates if
|
|
16469
|
-
isReady:
|
|
17080
|
+
// isReady indicates if RenderPipeline is configured and ready for rendering
|
|
17081
|
+
isReady: renderPipeline !== null
|
|
16470
17082
|
};
|
|
16471
17083
|
}
|
|
16472
17084
|
|
|
@@ -16543,7 +17155,9 @@ exports.isVectorLike = isVectorLike;
|
|
|
16543
17155
|
exports.once = once;
|
|
16544
17156
|
exports.prepare = prepare;
|
|
16545
17157
|
exports.presetsObj = presetsObj;
|
|
17158
|
+
exports.rebuildAllBuffers = rebuildAllBuffers;
|
|
16546
17159
|
exports.rebuildAllNodes = rebuildAllNodes;
|
|
17160
|
+
exports.rebuildAllStorage = rebuildAllStorage;
|
|
16547
17161
|
exports.rebuildAllUniforms = rebuildAllUniforms;
|
|
16548
17162
|
exports.reconciler = reconciler;
|
|
16549
17163
|
exports.registerPrimary = registerPrimary;
|
|
@@ -16556,8 +17170,10 @@ exports.unregisterPrimary = unregisterPrimary;
|
|
|
16556
17170
|
exports.updateCamera = updateCamera;
|
|
16557
17171
|
exports.updateFrustum = updateFrustum;
|
|
16558
17172
|
exports.useBridge = useBridge;
|
|
17173
|
+
exports.useBuffers = useBuffers;
|
|
16559
17174
|
exports.useEnvironment = useEnvironment;
|
|
16560
17175
|
exports.useFrame = useFrame;
|
|
17176
|
+
exports.useGPUStorage = useGPUStorage;
|
|
16561
17177
|
exports.useGraph = useGraph;
|
|
16562
17178
|
exports.useInstanceHandle = useInstanceHandle;
|
|
16563
17179
|
exports.useIsomorphicLayoutEffect = useIsomorphicLayoutEffect;
|
|
@@ -16565,7 +17181,7 @@ exports.useLoader = useLoader;
|
|
|
16565
17181
|
exports.useLocalNodes = useLocalNodes;
|
|
16566
17182
|
exports.useMutableCallback = useMutableCallback;
|
|
16567
17183
|
exports.useNodes = useNodes;
|
|
16568
|
-
exports.
|
|
17184
|
+
exports.useRenderPipeline = useRenderPipeline;
|
|
16569
17185
|
exports.useRenderTarget = useRenderTarget;
|
|
16570
17186
|
exports.useStore = useStore;
|
|
16571
17187
|
exports.useTexture = useTexture;
|