@needle-tools/engine 4.7.2-next.d24ebbc → 4.7.3-next.1603bb0
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/CHANGELOG.md +15 -0
- package/dist/gltf-progressive-BCZdu3Gc.min.js +8 -0
- package/dist/gltf-progressive-C6QbvrB4.umd.cjs +8 -0
- package/dist/{gltf-progressive-CNdBjvz6.js → gltf-progressive-CCddD-3B.js} +413 -399
- package/dist/{needle-engine.bundle-CGXifNgp.min.js → needle-engine.bundle-6bPQlS1q.min.js} +102 -102
- package/dist/{needle-engine.bundle-CHfSBXXT.js → needle-engine.bundle-CL5SiInU.js} +1962 -1889
- package/dist/{needle-engine.bundle-Dcn5L5IY.umd.cjs → needle-engine.bundle-Dm9L7JpU.umd.cjs} +70 -70
- package/dist/needle-engine.d.ts +21 -0
- package/dist/needle-engine.js +3 -3
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/dist/{postprocessing-xYQWCHFu.min.js → postprocessing-BzY0H7ry.min.js} +9 -9
- package/dist/{postprocessing-CjW23fio.umd.cjs → postprocessing-Dw2OCMp4.umd.cjs} +9 -9
- package/dist/{postprocessing-DYLNOL3W.js → postprocessing-vKBVFpSz.js} +10 -10
- package/dist/{three-examples-DaDLBuy6.min.js → three-examples-BMOhDaYR.min.js} +17 -17
- package/dist/{three-examples-X3OadjXB.umd.cjs → three-examples-DUcCNw9s.umd.cjs} +17 -17
- package/dist/{three-examples-B50TT3Iu.js → three-examples-tvuhV8Ne.js} +688 -688
- package/lib/engine/engine_create_objects.js +24 -1
- package/lib/engine/engine_create_objects.js.map +1 -1
- package/lib/engine/engine_input.js +3 -0
- package/lib/engine/engine_input.js.map +1 -1
- package/lib/engine/engine_physics.d.ts +13 -1
- package/lib/engine/engine_physics.js +15 -7
- package/lib/engine/engine_physics.js.map +1 -1
- package/lib/engine/webcomponents/needle-engine.js +14 -11
- package/lib/engine/webcomponents/needle-engine.js.map +1 -1
- package/lib/engine-components/OrbitControls.d.ts +57 -0
- package/lib/engine-components/OrbitControls.js +68 -9
- package/lib/engine-components/OrbitControls.js.map +1 -1
- package/lib/engine-components/Skybox.d.ts +1 -0
- package/lib/engine-components/Skybox.js +14 -18
- package/lib/engine-components/Skybox.js.map +1 -1
- package/lib/engine-components/postprocessing/PostProcessingHandler.js +1 -1
- package/lib/engine-components/postprocessing/PostProcessingHandler.js.map +1 -1
- package/lib/engine-components/postprocessing/Volume.d.ts +3 -1
- package/lib/engine-components/postprocessing/Volume.js +2 -0
- package/lib/engine-components/postprocessing/Volume.js.map +1 -1
- package/lib/engine-components/ui/EventSystem.js +9 -10
- package/lib/engine-components/ui/EventSystem.js.map +1 -1
- package/package.json +3 -3
- package/plugins/common/logger.js +34 -16
- package/plugins/vite/logger.client.js +123 -52
- package/plugins/vite/logger.js +5 -5
- package/src/engine/engine_create_objects.ts +24 -1
- package/src/engine/engine_input.ts +8 -1
- package/src/engine/engine_physics.ts +26 -8
- package/src/engine/webcomponents/needle-engine.ts +15 -13
- package/src/engine-components/OrbitControls.ts +94 -14
- package/src/engine-components/Skybox.ts +15 -21
- package/src/engine-components/postprocessing/PostProcessingHandler.ts +1 -1
- package/src/engine-components/postprocessing/Volume.ts +2 -1
- package/src/engine-components/ui/EventSystem.ts +8 -9
- package/dist/gltf-progressive-C_oN6wCA.umd.cjs +0 -8
- package/dist/gltf-progressive-MgWOszRl.min.js +0 -8
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
|
|
3
|
+
let isStringifying = false;
|
|
1
4
|
|
|
2
5
|
/**
|
|
3
6
|
* Patches console methods to capture log messages and send them to the server.
|
|
@@ -6,52 +9,108 @@
|
|
|
6
9
|
* @param {any} message - The log message to capture.
|
|
7
10
|
*/
|
|
8
11
|
function sendLogToServer(level, ...message) {
|
|
12
|
+
if (isStringifying) return;
|
|
9
13
|
if ("hot" in import.meta) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
message = message
|
|
14
|
+
try {
|
|
15
|
+
isStringifying = true;
|
|
16
|
+
// console.time("sendLogToServer");
|
|
17
|
+
message = stringifyLog(message);
|
|
18
|
+
// console.timeEnd("sendLogToServer");
|
|
19
|
+
// keep messages below payload limit
|
|
20
|
+
if (message.length > 100_000) {
|
|
21
|
+
message = message.slice(0, 100_000) + "... <truncated>";
|
|
22
|
+
}
|
|
23
|
+
// @ts-ignore
|
|
24
|
+
import.meta.hot.send("needle:client-log", { level, message: message });
|
|
25
|
+
} catch (e) {
|
|
26
|
+
// silently fail but send a message
|
|
27
|
+
try {
|
|
28
|
+
import.meta.hot.send("needle:client-log", { level: "error", message: `Error during logging: ${e.message}` });
|
|
29
|
+
}
|
|
30
|
+
catch (e2) {
|
|
31
|
+
// fallback failed as well
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
finally {
|
|
35
|
+
isStringifying = false;
|
|
14
36
|
}
|
|
15
|
-
// @ts-ignore
|
|
16
|
-
import.meta.hot.send("needle:client-log", { level, message: message });
|
|
17
37
|
}
|
|
18
38
|
}
|
|
19
39
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
40
|
+
function logHelper(fn, args) {
|
|
41
|
+
const error = new Error();
|
|
42
|
+
const stack = error.stack;
|
|
43
|
+
const caller = stack?.split('\n')[3]; // Get the actual caller
|
|
44
|
+
const path = caller?.trim();
|
|
45
|
+
if (!path) {
|
|
46
|
+
return fn(...args);
|
|
47
|
+
}
|
|
48
|
+
const pathWithoutBrackets = path.replaceAll("(", "").replaceAll(")", "");
|
|
49
|
+
fn(...args, `\n» ${pathWithoutBrackets}`);
|
|
50
|
+
}
|
|
25
51
|
|
|
26
52
|
if (import.meta && "hot" in import.meta) {
|
|
27
53
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
54
|
+
function patchLogs() {
|
|
55
|
+
const originalLog = console.log.bind(console);
|
|
56
|
+
const originalWarn = console.warn.bind(console);
|
|
57
|
+
const originalInfo = console.info.bind(console);
|
|
58
|
+
const originalDebug = console.debug.bind(console);
|
|
59
|
+
const originalError = console.error.bind(console);
|
|
33
60
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
61
|
+
console.log = function (...args) {
|
|
62
|
+
logHelper(originalLog, args);
|
|
63
|
+
sendLogToServer("log", ...args);
|
|
64
|
+
}
|
|
65
|
+
console.warn = (...args) => {
|
|
66
|
+
logHelper(originalWarn, args);
|
|
67
|
+
sendLogToServer("warn", ...args);
|
|
68
|
+
}
|
|
69
|
+
console.info = (...args) => {
|
|
70
|
+
logHelper(originalInfo, args);
|
|
71
|
+
sendLogToServer("info", ...args);
|
|
72
|
+
}
|
|
73
|
+
console.debug = (...args) => {
|
|
74
|
+
logHelper(originalDebug, args);
|
|
75
|
+
sendLogToServer("debug", ...args);
|
|
76
|
+
}
|
|
77
|
+
console.error = (...args) => {
|
|
78
|
+
logHelper(originalError, args);
|
|
79
|
+
sendLogToServer("error", ...args);
|
|
80
|
+
}
|
|
81
|
+
return () => {
|
|
82
|
+
console.log = originalLog;
|
|
83
|
+
console.warn = originalWarn;
|
|
84
|
+
console.info = originalInfo;
|
|
85
|
+
console.debug = originalDebug;
|
|
86
|
+
console.error = originalError;
|
|
87
|
+
}
|
|
45
88
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
89
|
+
|
|
90
|
+
const query = new URLSearchParams(window.location.search);
|
|
91
|
+
if (query.has("needle-debug")) {
|
|
92
|
+
patchLogs();
|
|
49
93
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
94
|
+
else {
|
|
95
|
+
// const unpatch = patchLogs();
|
|
96
|
+
// setTimeout(() => {
|
|
97
|
+
// sendLogToServer("internal", "Stop listening to console.log.");
|
|
98
|
+
// unpatch();
|
|
99
|
+
// }, 10_000);
|
|
100
|
+
|
|
101
|
+
const threshold = 100;
|
|
102
|
+
const devToolsArePotentiallyOpen = window.outerHeight - window.innerHeight > threshold || window.outerWidth - window.innerWidth > threshold;
|
|
103
|
+
if (devToolsArePotentiallyOpen) {
|
|
104
|
+
sendLogToServer("internal", "Console logging is disabled (devtools are open)");
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
sendLogToServer("internal", "Console logging is enabled");
|
|
108
|
+
patchLogs();
|
|
109
|
+
}
|
|
53
110
|
}
|
|
54
111
|
|
|
112
|
+
|
|
113
|
+
|
|
55
114
|
try {
|
|
56
115
|
sendLogToServer("internal", `Page loaded
|
|
57
116
|
URL: ${window.location.href}
|
|
@@ -67,7 +126,6 @@ User Activation: ${"userActivation" in navigator ? JSON.stringify(navigator.user
|
|
|
67
126
|
`);
|
|
68
127
|
|
|
69
128
|
if ("gpu" in navigator) {
|
|
70
|
-
|
|
71
129
|
// @ts-ignore
|
|
72
130
|
navigator.gpu.requestAdapter()
|
|
73
131
|
.then(adapter => adapter ? adapter.requestDevice() : null)
|
|
@@ -102,10 +160,9 @@ User Activation: ${"userActivation" in navigator ? JSON.stringify(navigator.user
|
|
|
102
160
|
sendLogToServer("error", `Unhandled promise rejection: ${reason}`);
|
|
103
161
|
});
|
|
104
162
|
window.addEventListener('beforeunload', () => {
|
|
105
|
-
sendLogToServer("internal", "Page is unloading");
|
|
163
|
+
sendLogToServer("internal", "Page is unloading\n\n");
|
|
106
164
|
});
|
|
107
165
|
document.addEventListener('visibilitychange', () => {
|
|
108
|
-
console.log("Visibility changed:", document.visibilityState);
|
|
109
166
|
if (document.visibilityState === 'hidden') {
|
|
110
167
|
sendLogToServer("internal", "Page is hidden");
|
|
111
168
|
}
|
|
@@ -174,11 +231,10 @@ User Activation: ${"userActivation" in navigator ? JSON.stringify(navigator.user
|
|
|
174
231
|
* @param {Set<any>} [seen]
|
|
175
232
|
*/
|
|
176
233
|
function stringifyLog(log, seen = new Set(), depth = 0) {
|
|
177
|
-
|
|
178
234
|
const isServer = typeof window === "undefined";
|
|
179
235
|
const stringify_limits = {
|
|
180
|
-
string: isServer ? 100_000 :
|
|
181
|
-
object_keys: isServer ? 300 :
|
|
236
|
+
string: isServer ? 100_000 : 1_000,
|
|
237
|
+
object_keys: isServer ? 300 : 200,
|
|
182
238
|
object_depth: isServer ? 10 : 3,
|
|
183
239
|
array_items: isServer ? 2_000 : 100,
|
|
184
240
|
}
|
|
@@ -202,7 +258,20 @@ function stringifyLog(log, seen = new Set(), depth = 0) {
|
|
|
202
258
|
|
|
203
259
|
if (seen.has(log)) return "<circular>";
|
|
204
260
|
|
|
205
|
-
if (Array.isArray(log)
|
|
261
|
+
if (Array.isArray(log)
|
|
262
|
+
|| log instanceof ArrayBuffer
|
|
263
|
+
|| log instanceof Uint8Array
|
|
264
|
+
|| log instanceof Float32Array
|
|
265
|
+
|| log instanceof Int32Array
|
|
266
|
+
|| log instanceof Uint32Array
|
|
267
|
+
|| log instanceof Uint16Array
|
|
268
|
+
|| log instanceof Uint8ClampedArray
|
|
269
|
+
|| log instanceof Int16Array
|
|
270
|
+
|| log instanceof Int8Array
|
|
271
|
+
|| log instanceof BigInt64Array
|
|
272
|
+
|| log instanceof BigUint64Array
|
|
273
|
+
|| log instanceof Float64Array
|
|
274
|
+
) {
|
|
206
275
|
seen.add(log);
|
|
207
276
|
return stringifyArray(log);
|
|
208
277
|
}
|
|
@@ -213,25 +282,26 @@ function stringifyLog(log, seen = new Set(), depth = 0) {
|
|
|
213
282
|
}
|
|
214
283
|
|
|
215
284
|
seen.add(log);
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
// seen.add(value);
|
|
222
|
-
// }
|
|
223
|
-
// return value;
|
|
224
|
-
// });
|
|
225
|
-
// return str;
|
|
285
|
+
|
|
286
|
+
if (log instanceof Error) {
|
|
287
|
+
return `<Error: ${log.message}\nStack: ${log.stack}>`;
|
|
288
|
+
}
|
|
289
|
+
|
|
226
290
|
const keys = Object.keys(log);
|
|
227
291
|
let res = "{";
|
|
228
292
|
for (let i = 0; i < keys.length; i++) {
|
|
229
293
|
const key = keys[i];
|
|
230
294
|
let value = log[key];
|
|
295
|
+
if (i >= stringify_limits.object_keys) {
|
|
296
|
+
res += `, ... <truncated ${keys.length - i} keys>`;
|
|
297
|
+
break;
|
|
298
|
+
}
|
|
231
299
|
|
|
232
300
|
if (typeof value === "number") {
|
|
233
|
-
// clamp precision for numbers
|
|
234
|
-
value
|
|
301
|
+
// clamp precision for numbers if it has decimal places
|
|
302
|
+
if (value % 1 !== 0) {
|
|
303
|
+
value = Number(value.toFixed(4));
|
|
304
|
+
}
|
|
235
305
|
}
|
|
236
306
|
let str = stringifyLog(value, seen, depth + 1);
|
|
237
307
|
if (typeof value === "object") {
|
|
@@ -253,6 +323,7 @@ function stringifyLog(log, seen = new Set(), depth = 0) {
|
|
|
253
323
|
// });
|
|
254
324
|
// return `{ ${entries.join(", ")} }`;
|
|
255
325
|
}
|
|
326
|
+
|
|
256
327
|
return String(log);
|
|
257
328
|
|
|
258
329
|
function stringifyArray(arr) {
|
package/plugins/vite/logger.js
CHANGED
|
@@ -92,10 +92,10 @@ function logRequests(server, log_http_requests = false) {
|
|
|
92
92
|
});
|
|
93
93
|
}
|
|
94
94
|
// Log HTTP requests
|
|
95
|
-
|
|
96
|
-
|
|
95
|
+
server.middlewares.use((req, res, next) => {
|
|
96
|
+
if (log_http_requests) {
|
|
97
97
|
captureLogMessage("client-http", "info", [req.method, req.url], null);
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
98
|
+
}
|
|
99
|
+
next();
|
|
100
|
+
});
|
|
101
101
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createLoaders } from "@needle-tools/gltf-progressive";
|
|
2
|
-
import { BoxGeometry, BufferGeometry, Color, ColorRepresentation, CylinderGeometry, DoubleSide, ExtrudeGeometry, Group, Material, Mesh, MeshBasicMaterial, MeshStandardMaterial, Object3D, PlaneGeometry, Shape, SphereGeometry, Sprite, SpriteMaterial, Texture } from "three"
|
|
2
|
+
import { BoxGeometry, BufferGeometry, Color, ColorRepresentation, CylinderGeometry, DoubleSide, ExtrudeGeometry, Group, Material, Mesh, MeshBasicMaterial, MeshStandardMaterial, Object3D, PlaneGeometry, Shape, SphereGeometry, Sprite, SpriteMaterial, Texture, Vector2 } from "three"
|
|
3
3
|
import { TextGeometry } from "three/examples/jsm/geometries/TextGeometry.js";
|
|
4
4
|
import { Font, FontLoader } from "three/examples/jsm/loaders/FontLoader.js";
|
|
5
5
|
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
|
|
@@ -300,9 +300,29 @@ function createBoxWithRoundedEdges(width: number, height: number, _depth: number
|
|
|
300
300
|
bevelSize: radius,
|
|
301
301
|
bevelThickness: radius0,
|
|
302
302
|
curveSegments: smoothness,
|
|
303
|
+
UVGenerator: {
|
|
304
|
+
generateTopUV: (_, vertices: number[]) => {
|
|
305
|
+
const uvs: Vector2[] = [];
|
|
306
|
+
for (let i = 0; i < vertices.length; i += 3) {
|
|
307
|
+
uvs.push(new Vector2(vertices[i] / width, vertices[i + 1] / height));
|
|
308
|
+
}
|
|
309
|
+
return uvs;
|
|
310
|
+
},
|
|
311
|
+
generateSideWallUV: (_, vertices: number[], indexA: number, indexB: number, indexC: number, indexD: number) => {
|
|
312
|
+
const uvs: Vector2[] = [];
|
|
313
|
+
uvs.push(new Vector2(vertices[indexA] / width, vertices[indexA + 1] / height));
|
|
314
|
+
uvs.push(new Vector2(vertices[indexB] / width, vertices[indexB + 1] / height));
|
|
315
|
+
uvs.push(new Vector2(vertices[indexC] / width, vertices[indexC + 1] / height));
|
|
316
|
+
uvs.push(new Vector2(vertices[indexD] / width, vertices[indexD + 1] / height));
|
|
317
|
+
return uvs;
|
|
318
|
+
}
|
|
319
|
+
},
|
|
303
320
|
});
|
|
304
321
|
geometry.scale(1, 1, 1 - radius0)
|
|
305
322
|
geometry.center();
|
|
323
|
+
// Ensure we have an index buffer
|
|
324
|
+
if (!geometry.index)
|
|
325
|
+
geometry.setIndex(Array.from({ length: geometry.attributes.position.count }, (_, i) => i));
|
|
306
326
|
geometry.computeVertexNormals();
|
|
307
327
|
return geometry;
|
|
308
328
|
}
|
|
@@ -389,6 +409,9 @@ function loadShaderball(group: Group, opts?: ObjectOptions) {
|
|
|
389
409
|
const instance = res.clone();
|
|
390
410
|
const mesh = instance.children[0] as Mesh;
|
|
391
411
|
if (mesh?.type === "Mesh") {
|
|
412
|
+
// Ensure we have tangents for the Shaderball mesh
|
|
413
|
+
if (!mesh.geometry.attributes.tangent)
|
|
414
|
+
mesh.geometry.computeTangents();
|
|
392
415
|
updateShaderballMaterial(mesh, opts);
|
|
393
416
|
}
|
|
394
417
|
group.add(instance);
|
|
@@ -241,6 +241,9 @@ declare type EventListenerOptions = {
|
|
|
241
241
|
signal?: AbortSignal;
|
|
242
242
|
}
|
|
243
243
|
|
|
244
|
+
|
|
245
|
+
type RegisteredEventListenerValue = Array<{ priority: number, listeners: Array<{ callback: InputEventListener, options: EventListenerOptions }> }>;
|
|
246
|
+
|
|
244
247
|
/**
|
|
245
248
|
* The input system is responsible for handling all input events like pointer events (mouse, touch, xr controllers) and keyboard events.
|
|
246
249
|
*/
|
|
@@ -250,7 +253,7 @@ export class Input implements IInput {
|
|
|
250
253
|
* That way users can control if they want to receive events before or after other listeners (e.g subscribe to pointer events before the EventSystem receives them) - this allows certain listeners to be always invoked first (or last) and stop propagation
|
|
251
254
|
* Listeners per event are sorted
|
|
252
255
|
*/
|
|
253
|
-
private readonly _eventListeners:
|
|
256
|
+
private readonly _eventListeners: Record<string, RegisteredEventListenerValue> = {};
|
|
254
257
|
|
|
255
258
|
/** Adds an event listener for the specified event type. The callback will be called when the event is triggered.
|
|
256
259
|
* @param type The event type to listen for
|
|
@@ -846,6 +849,10 @@ export class Input implements IInput {
|
|
|
846
849
|
}
|
|
847
850
|
|
|
848
851
|
unbindEvents() {
|
|
852
|
+
for (const key in this._eventListeners) {
|
|
853
|
+
this._eventListeners[key].length = 0; // clear all listeners for this event
|
|
854
|
+
}
|
|
855
|
+
|
|
849
856
|
window.removeEventListener('contextmenu', this.onContextMenu);
|
|
850
857
|
|
|
851
858
|
this._htmlEventSource?.removeEventListener('pointerdown', this.onPointerDown);
|
|
@@ -68,9 +68,15 @@ export declare interface IRaycastOptions {
|
|
|
68
68
|
|
|
69
69
|
/**
|
|
70
70
|
* Use MeshBVH for raycasting. This is faster than the default threejs raycaster but uses more memory.
|
|
71
|
-
* @default
|
|
71
|
+
* @default true
|
|
72
72
|
*/
|
|
73
73
|
useAcceleratedRaycast?: boolean;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* When enabled raycasting will use the 'slower' traditional three.js raycasting method while the MeshBVH is being generated in the background. When disabled objects that don't have a BVH available *Yet* because it's still being generated will be ignored and not generate any hits. This is useful to improve performance for cases where raycasting happens frequently and it won't matter if raycasts don't produce hits for a few frames.
|
|
77
|
+
* @default true
|
|
78
|
+
*/
|
|
79
|
+
allowSlowRaycastFallback?: boolean;
|
|
74
80
|
}
|
|
75
81
|
|
|
76
82
|
export class RaycastOptions implements IRaycastOptions {
|
|
@@ -90,6 +96,7 @@ export class RaycastOptions implements IRaycastOptions {
|
|
|
90
96
|
ignore?: Object3D[];
|
|
91
97
|
testObject?: RaycastTestObjectCallback;
|
|
92
98
|
useAcceleratedRaycast?: boolean | undefined;
|
|
99
|
+
allowSlowRaycastFallback?: boolean = true;
|
|
93
100
|
|
|
94
101
|
screenPointFromOffset(ox: number, oy: number) {
|
|
95
102
|
if (this.screenPoint === undefined) this.screenPoint = new Vector2();
|
|
@@ -356,7 +363,7 @@ export class Physics {
|
|
|
356
363
|
// did handle raycast
|
|
357
364
|
}
|
|
358
365
|
else if (options.useAcceleratedRaycast !== false) {
|
|
359
|
-
NEMeshBVH.runMeshBVHRaycast(raycaster, mesh, results, this.context);
|
|
366
|
+
NEMeshBVH.runMeshBVHRaycast(raycaster, mesh, results, this.context, options);
|
|
360
367
|
}
|
|
361
368
|
else {
|
|
362
369
|
raycaster.intersectObject(mesh, false, results);
|
|
@@ -416,7 +423,7 @@ export class Physics {
|
|
|
416
423
|
sphere.center.copy(spherePos);
|
|
417
424
|
sphere.radius = radius;
|
|
418
425
|
const previousResults = results.length;
|
|
419
|
-
NEMeshBVH.runMeshBVHRaycast(this.sphere, mesh, results, this.context);
|
|
426
|
+
NEMeshBVH.runMeshBVHRaycast(this.sphere, mesh, results, this.context, {});
|
|
420
427
|
if (previousResults != results.length && !traverseChildsAfterHit) {
|
|
421
428
|
return;
|
|
422
429
|
}
|
|
@@ -539,7 +546,13 @@ declare module 'three' {
|
|
|
539
546
|
acceleratedRaycast?: any;
|
|
540
547
|
}
|
|
541
548
|
export interface SkinnedMesh {
|
|
549
|
+
/** @deprecated use autoUpdateMeshBvhInterval */
|
|
542
550
|
autoUpdateMeshBVH?: boolean;
|
|
551
|
+
/**
|
|
552
|
+
* Interval in milliseconds to automatically update the mesh BVH. When set to >= 0 the BVH will be updated every x milliseconds.
|
|
553
|
+
* @default undefined (disabled)
|
|
554
|
+
*/
|
|
555
|
+
autoUpdateMeshBvhInterval?: number;
|
|
543
556
|
bvhNeedsUpdate?: boolean;
|
|
544
557
|
}
|
|
545
558
|
}
|
|
@@ -547,7 +560,7 @@ declare module 'three' {
|
|
|
547
560
|
|
|
548
561
|
|
|
549
562
|
namespace NEMeshBVH {
|
|
550
|
-
export function runMeshBVHRaycast(method: Raycaster | Sphere, mesh: Mesh, results: Intersection[], context: Pick<Context, "xr"
|
|
563
|
+
export function runMeshBVHRaycast(method: Raycaster | Sphere, mesh: Mesh, results: Intersection[], context: Pick<Context, "xr">, options: { allowSlowRaycastFallback?: boolean }): boolean {
|
|
551
564
|
if (!mesh.geometry) {
|
|
552
565
|
return false;
|
|
553
566
|
}
|
|
@@ -570,15 +583,16 @@ namespace NEMeshBVH {
|
|
|
570
583
|
skinnedMesh.staticGeometry = skinnedMesh.staticGenerator.generate();
|
|
571
584
|
geom.boundsTree = _computeBoundsTree?.call(skinnedMesh.staticGeometry);
|
|
572
585
|
skinnedMesh.staticGeometryLastUpdate = performance.now() + Math.random() * 200;
|
|
573
|
-
|
|
574
|
-
skinnedMesh.autoUpdateMeshBVH = false;
|
|
586
|
+
skinnedMesh.bvhNeedsUpdate = true;
|
|
575
587
|
}
|
|
576
588
|
}
|
|
577
|
-
else if (geom.boundsTree && (skinnedMesh.
|
|
589
|
+
else if (geom.boundsTree && ((skinnedMesh.autoUpdateMeshBvhInterval !== undefined && skinnedMesh.autoUpdateMeshBvhInterval >= 0) || skinnedMeshBVHNeedsUpdate === true)) {
|
|
578
590
|
// automatically refit the tree every 300ms
|
|
579
591
|
const now = performance.now();
|
|
580
592
|
const timeSinceLastUpdate = now - skinnedMesh.staticGeometryLastUpdate!;
|
|
581
|
-
|
|
593
|
+
const interval = skinnedMesh.autoUpdateMeshBvhInterval ?? 100;
|
|
594
|
+
if (skinnedMeshBVHNeedsUpdate || timeSinceLastUpdate > interval) {
|
|
595
|
+
if(debugPhysics) console.warn(`Physics: updating skinned mesh bvh for ${mesh.name} after ${timeSinceLastUpdate.toFixed(2)}ms`);
|
|
582
596
|
skinnedMesh.bvhNeedsUpdate = false;
|
|
583
597
|
skinnedMesh.staticGeometryLastUpdate = now;
|
|
584
598
|
skinnedMesh.staticGenerator?.generate(skinnedMesh.staticGeometry);
|
|
@@ -694,6 +708,10 @@ namespace NEMeshBVH {
|
|
|
694
708
|
}
|
|
695
709
|
else {
|
|
696
710
|
if (debugPhysics) console.warn("No bounds tree found for mesh", mesh.name, { workerTask: geom[workerTaskSymbol], hasAcceleratedRaycast: _acceleratedRaycast != null });
|
|
711
|
+
if (options.allowSlowRaycastFallback === false) {
|
|
712
|
+
if (debugPhysics) console.warn("Skipping raycast because no bounds tree is available and allowSlowRaycastFallback is false");
|
|
713
|
+
return false;
|
|
714
|
+
}
|
|
697
715
|
}
|
|
698
716
|
const prevFirstHitOnly = raycaster.firstHitOnly;
|
|
699
717
|
raycaster.firstHitOnly = false;
|
|
@@ -869,19 +869,21 @@ function getDisplayName(str: string) {
|
|
|
869
869
|
|
|
870
870
|
|
|
871
871
|
function handleLoadingBlur(needleEngineElement: NeedleEngineWebComponent) {
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
872
|
+
onStart((ctx) => {
|
|
873
|
+
const userBlurSetting = needleEngineElement.getAttribute("loading-blur");
|
|
874
|
+
if (userBlurSetting !== null && userBlurSetting !== "0") {
|
|
875
875
|
if (ctx.domElement === needleEngineElement) {
|
|
876
876
|
|
|
877
877
|
const promise = ctx.lodsManager.manager?.awaitLoading({
|
|
878
|
+
frames: 5,
|
|
878
879
|
signal: AbortSignal.timeout(10_000), // Limit how long the page can be blurred
|
|
880
|
+
maxPromisesPerObject: 1,
|
|
879
881
|
}).catch(_ => {
|
|
880
882
|
// Ignore errors (none are expected tho...)
|
|
881
883
|
});
|
|
882
|
-
let blur = "
|
|
884
|
+
let blur = "20px";
|
|
883
885
|
if (userBlurSetting.endsWith("px")) blur = userBlurSetting;
|
|
884
|
-
const duration =
|
|
886
|
+
const duration = 170;
|
|
885
887
|
|
|
886
888
|
// If the scene has a transparent background we apply a blur to the canvas directly to not *also* blur images
|
|
887
889
|
// But don't always use this effect because the edges don't look as good as with a backdrop filter
|
|
@@ -896,30 +898,30 @@ function handleLoadingBlur(needleEngineElement: NeedleEngineWebComponent) {
|
|
|
896
898
|
const animation = canvas.animate([{
|
|
897
899
|
filter: "blur(0px)",
|
|
898
900
|
}
|
|
899
|
-
], { duration: duration, easing: "ease-in" });
|
|
901
|
+
], { duration: duration, easing: "ease-in", });
|
|
900
902
|
animation.onfinish = () => {
|
|
901
903
|
canvas.style.filter = originalFilterValue;
|
|
902
904
|
domElement.style.overflow = originalOverflowValue;
|
|
903
905
|
};
|
|
904
906
|
});
|
|
905
907
|
}
|
|
906
|
-
else
|
|
907
|
-
{
|
|
908
|
+
else {
|
|
908
909
|
const blurryElement = document.createElement("div");
|
|
909
910
|
ctx.domElement.prepend(blurryElement);
|
|
910
|
-
blurryElement.style.cssText = "position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 10;";
|
|
911
|
+
blurryElement.style.cssText = "position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 10; pointer-events: none";
|
|
911
912
|
blurryElement.style.backdropFilter = `blur(${blur})`;
|
|
912
913
|
promise?.then(() => {
|
|
913
914
|
const animation = blurryElement.animate([{
|
|
914
915
|
backdropFilter: "blur(0px)",
|
|
916
|
+
opacity: 0,
|
|
915
917
|
}
|
|
916
|
-
], { duration: duration, easing: "ease-in" });
|
|
918
|
+
], { duration: duration, easing: "ease-in", });
|
|
917
919
|
animation.onfinish = () => {
|
|
918
920
|
blurryElement.remove();
|
|
919
921
|
};
|
|
920
922
|
});
|
|
921
923
|
}
|
|
922
924
|
}
|
|
923
|
-
}
|
|
924
|
-
}
|
|
925
|
-
}
|
|
925
|
+
}
|
|
926
|
+
}, { once: true });
|
|
927
|
+
}
|