@mml-io/3d-web-client-core 0.1.0 → 0.2.0
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/build/camera/CameraManager.d.ts +13 -3
- package/build/character/Character.d.ts +1 -0
- package/build/character/CharacterManager.d.ts +2 -2
- package/build/character/LocalController.d.ts +21 -7
- package/build/character/ModelLoader.d.ts +8 -3
- package/build/character/RemoteController.d.ts +2 -4
- package/build/collisions/CollisionsManager.d.ts +4 -0
- package/build/helpers/math-helpers.d.ts +2 -0
- package/build/index.js +721 -207
- package/build/index.js.map +4 -4
- package/build/input/KeyInputManager.d.ts +9 -2
- package/build/rendering/composer.d.ts +20 -1
- package/build/rendering/composerSettings.d.ts +154 -0
- package/build/rendering/tweakPaneActivity.d.ts +2 -0
- package/build/time/TimeManager.d.ts +3 -0
- package/package.json +4 -2
package/build/index.js
CHANGED
@@ -27,25 +27,53 @@ var round = (n, digits) => {
|
|
27
27
|
var ease = (target, n, factor) => {
|
28
28
|
return round((target - n) * factor, 5);
|
29
29
|
};
|
30
|
+
function clamp(value, min, max) {
|
31
|
+
return Math.min(Math.max(value, min), max);
|
32
|
+
}
|
33
|
+
var remap = (value, minValue, maxValue, minScaledValue, maxScaledValue) => {
|
34
|
+
return minScaledValue + (maxScaledValue - minScaledValue) * (value - minValue) / (maxValue - minValue);
|
35
|
+
};
|
36
|
+
|
37
|
+
// src/rendering/tweakPaneActivity.ts
|
38
|
+
var isTweakpaneActive = false;
|
39
|
+
function setTweakpaneActive(status) {
|
40
|
+
isTweakpaneActive = status;
|
41
|
+
}
|
42
|
+
function getTweakpaneActive() {
|
43
|
+
return isTweakpaneActive;
|
44
|
+
}
|
30
45
|
|
31
46
|
// src/camera/CameraManager.ts
|
32
47
|
var CameraManager = class {
|
33
48
|
constructor() {
|
34
49
|
__publicField(this, "camera");
|
35
|
-
__publicField(this, "
|
36
|
-
__publicField(this, "
|
37
|
-
__publicField(this, "
|
38
|
-
__publicField(this, "
|
39
|
-
__publicField(this, "
|
50
|
+
__publicField(this, "initialDistance", 2.5);
|
51
|
+
__publicField(this, "minDistance", 0.1);
|
52
|
+
__publicField(this, "maxDistance", 6);
|
53
|
+
__publicField(this, "initialFOV", 80);
|
54
|
+
__publicField(this, "fov", this.initialFOV);
|
55
|
+
__publicField(this, "minFOV", 65);
|
56
|
+
__publicField(this, "maxFOV", 85);
|
57
|
+
__publicField(this, "targetFOV", this.initialFOV);
|
58
|
+
__publicField(this, "minPolarAngle", Math.PI * 0.25);
|
59
|
+
__publicField(this, "maxPolarAngle", Math.PI * 0.95);
|
60
|
+
__publicField(this, "dampingFactor", 0.091);
|
61
|
+
__publicField(this, "targetDistance", this.initialDistance);
|
62
|
+
__publicField(this, "distance", this.initialDistance);
|
40
63
|
__publicField(this, "targetPhi", Math.PI / 2);
|
41
64
|
__publicField(this, "phi", Math.PI / 2);
|
42
65
|
__publicField(this, "targetTheta", -Math.PI / 2);
|
43
66
|
__publicField(this, "theta", -Math.PI / 2);
|
67
|
+
__publicField(this, "dragging", false);
|
68
|
+
__publicField(this, "target", new Vector32(0, 1.55, 0));
|
44
69
|
__publicField(this, "hadTarget", false);
|
45
|
-
this.camera = new PerspectiveCamera(
|
46
|
-
|
47
|
-
|
48
|
-
|
70
|
+
this.camera = new PerspectiveCamera(
|
71
|
+
this.fov,
|
72
|
+
window.innerWidth / window.innerHeight,
|
73
|
+
0.1,
|
74
|
+
2e3
|
75
|
+
);
|
76
|
+
this.camera.position.set(0, 1.4, -this.initialDistance);
|
49
77
|
document.addEventListener("mousedown", this.onMouseDown.bind(this));
|
50
78
|
document.addEventListener("mouseup", this.onMouseUp.bind(this));
|
51
79
|
document.addEventListener("mousemove", this.onMouseMove.bind(this));
|
@@ -65,22 +93,21 @@ var CameraManager = class {
|
|
65
93
|
this.dragging = false;
|
66
94
|
}
|
67
95
|
onMouseMove(event) {
|
68
|
-
if (!this.dragging)
|
96
|
+
if (!this.dragging || getTweakpaneActive() === true)
|
69
97
|
return;
|
70
|
-
|
71
|
-
if (this.targetTheta === null || this.targetPhi === null) {
|
98
|
+
if (this.targetTheta === null || this.targetPhi === null)
|
72
99
|
return;
|
73
|
-
}
|
74
100
|
this.targetTheta += event.movementX * 0.01;
|
75
101
|
this.targetPhi -= event.movementY * 0.01;
|
76
|
-
this.targetPhi = Math.max(
|
77
|
-
this.targetPhi = Math.min(Math.PI * 0.7, this.targetPhi);
|
102
|
+
this.targetPhi = Math.max(this.minPolarAngle, Math.min(this.maxPolarAngle, this.targetPhi));
|
78
103
|
}
|
79
104
|
onMouseWheel(event) {
|
80
|
-
const scrollAmount = event.deltaY *
|
105
|
+
const scrollAmount = event.deltaY * 1e-3;
|
81
106
|
this.targetDistance += scrollAmount;
|
82
|
-
this.targetDistance = Math.max(
|
83
|
-
|
107
|
+
this.targetDistance = Math.max(
|
108
|
+
this.minDistance,
|
109
|
+
Math.min(this.maxDistance, this.targetDistance)
|
110
|
+
);
|
84
111
|
}
|
85
112
|
setTarget(target) {
|
86
113
|
this.target.copy(target);
|
@@ -103,18 +130,26 @@ var CameraManager = class {
|
|
103
130
|
this.distance = this.targetDistance;
|
104
131
|
}
|
105
132
|
update() {
|
106
|
-
if (this.target === null)
|
133
|
+
if (this.target === null)
|
107
134
|
return;
|
108
|
-
}
|
109
135
|
if (this.phi !== null && this.targetPhi !== null && this.theta !== null && this.targetTheta !== null) {
|
110
|
-
this.distance +=
|
111
|
-
this.
|
112
|
-
this.
|
113
|
-
this.theta += ease(this.targetTheta, this.theta, 0.07);
|
136
|
+
this.distance += (this.targetDistance - this.distance) * this.dampingFactor * 0.21;
|
137
|
+
this.phi += (this.targetPhi - this.phi) * this.dampingFactor;
|
138
|
+
this.theta += (this.targetTheta - this.theta) * this.dampingFactor;
|
114
139
|
const x = this.target.x + this.distance * Math.sin(this.phi) * Math.cos(this.theta);
|
115
140
|
const y = this.target.y + this.distance * Math.cos(this.phi);
|
116
141
|
const z = this.target.z + this.distance * Math.sin(this.phi) * Math.sin(this.theta);
|
117
|
-
this.
|
142
|
+
this.targetFOV = remap(
|
143
|
+
this.targetDistance,
|
144
|
+
this.minDistance,
|
145
|
+
this.maxDistance,
|
146
|
+
this.minFOV,
|
147
|
+
this.maxFOV
|
148
|
+
);
|
149
|
+
this.fov += ease(this.targetFOV, this.fov, 0.07);
|
150
|
+
this.camera.fov = this.fov;
|
151
|
+
this.camera.updateProjectionMatrix();
|
152
|
+
this.camera.position.set(x, clamp(y, 0.1, Infinity), z);
|
118
153
|
this.camera.lookAt(this.target);
|
119
154
|
}
|
120
155
|
}
|
@@ -233,15 +268,17 @@ var CharacterMaterial = class extends MeshPhysicalMaterial {
|
|
233
268
|
__publicField(this, "colorsCube216", []);
|
234
269
|
this.color = new Color(16777215);
|
235
270
|
this.transmission = 0.5;
|
236
|
-
this.metalness = 0.
|
237
|
-
this.roughness = 0.
|
238
|
-
this.ior = 2;
|
271
|
+
this.metalness = 0.9;
|
272
|
+
this.roughness = 0.1;
|
273
|
+
this.ior = 1.2;
|
239
274
|
this.thickness = 0.1;
|
240
275
|
this.specularColor = new Color(30719);
|
241
276
|
this.specularIntensity = 0.1;
|
277
|
+
this.emissive = new Color(16777215);
|
278
|
+
this.emissiveIntensity = 0.1;
|
242
279
|
this.envMapIntensity = 1.8;
|
243
|
-
this.sheenColor = new Color(
|
244
|
-
this.sheen = 0.
|
280
|
+
this.sheenColor = new Color(16777215);
|
281
|
+
this.sheen = 0.5;
|
245
282
|
this.onBeforeCompile = (shader) => {
|
246
283
|
this.uniforms = UniformsUtils.clone(shader.uniforms);
|
247
284
|
this.uniforms.nearClip = { value: 0.01 };
|
@@ -257,6 +294,7 @@ var CharacterMaterial = class extends MeshPhysicalMaterial {
|
|
257
294
|
shader.fragmentShader,
|
258
295
|
/* glsl */
|
259
296
|
`
|
297
|
+
//#define showPattern
|
260
298
|
varying vec2 vUv;
|
261
299
|
uniform float nearClip;
|
262
300
|
uniform float farClip;
|
@@ -265,6 +303,25 @@ var CharacterMaterial = class extends MeshPhysicalMaterial {
|
|
265
303
|
uniform float time;
|
266
304
|
uniform vec3 diffuseRandomColor;
|
267
305
|
${bayerDither}
|
306
|
+
|
307
|
+
#ifdef showPattern
|
308
|
+
vec2 rand2(vec2 p) {
|
309
|
+
return fract(vec2(sin(p.x * 591.32 + p.y * 154.077), cos(p.x * 391.32 + p.y * 49.077)));
|
310
|
+
}
|
311
|
+
float voronoi(in vec2 x) {
|
312
|
+
vec2 p = floor(x);
|
313
|
+
vec2 f = fract(x);
|
314
|
+
float minDistance = 1.0;
|
315
|
+
for(int j = -1; j <= 1; j ++)
|
316
|
+
for(int i = -1; i <= 1; i ++) {
|
317
|
+
vec2 b = vec2(i, j);
|
318
|
+
vec2 rand = 0.5 + 0.5 * sin(time * 1.5 + 12.0 * rand2(p + b));
|
319
|
+
vec2 r = vec2(b) - f + rand;
|
320
|
+
minDistance = min(minDistance, length(r));
|
321
|
+
}
|
322
|
+
return minDistance;
|
323
|
+
}
|
324
|
+
#endif
|
268
325
|
`
|
269
326
|
);
|
270
327
|
shader.fragmentShader = injectBefore(
|
@@ -286,10 +343,22 @@ var CharacterMaterial = class extends MeshPhysicalMaterial {
|
|
286
343
|
d = bayerDither(bayerbr, p - ivec2(4, 4));
|
287
344
|
}
|
288
345
|
if (distance <= ditheringNear + d * ditheringRange) discard;
|
289
|
-
|
290
|
-
|
346
|
+
|
347
|
+
vec2 uv = vUv;
|
348
|
+
float s = clamp(0.35 + 0.35 * sin(5.0 * -time + vUv.y * 600.0), 0.0, 1.0);
|
291
349
|
float scanLines = pow(s, 1.33);
|
350
|
+
|
292
351
|
outgoingLight *= diffuseRandomColor;
|
352
|
+
|
353
|
+
#ifdef showPattern
|
354
|
+
float val = pow(voronoi(uv * 8.0) * 1.2, 0.5);
|
355
|
+
float thickness = 1.0 / 500.0;
|
356
|
+
vec2 g = step(mod(uv, 0.015), vec2(thickness));
|
357
|
+
float a = 1.0 - clamp(val * (g.x + g.y), 0.0, 1.0);
|
358
|
+
vec3 grid = vec3(smoothstep(0.01, 0.0, a) * 1.15) * diffuseRandomColor;
|
359
|
+
outgoingLight += grid;
|
360
|
+
#endif
|
361
|
+
|
293
362
|
outgoingLight += smoothstep(0.1, 0.0, scanLines) * 0.1;
|
294
363
|
`
|
295
364
|
);
|
@@ -297,8 +366,8 @@ var CharacterMaterial = class extends MeshPhysicalMaterial {
|
|
297
366
|
this.generateColorCube();
|
298
367
|
}
|
299
368
|
generateColorCube() {
|
300
|
-
const saturation = 0.
|
301
|
-
const lightness = 0.
|
369
|
+
const saturation = 0.4;
|
370
|
+
const lightness = 0.7;
|
302
371
|
const goldenRatioConjugate = 0.618033988749895;
|
303
372
|
let hue = 0;
|
304
373
|
for (let i = 0; i < 216; i++) {
|
@@ -323,73 +392,130 @@ var AnimationState = /* @__PURE__ */ ((AnimationState2) => {
|
|
323
392
|
|
324
393
|
// src/character/ModelLoader.ts
|
325
394
|
import { LoadingManager } from "three";
|
326
|
-
import {
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
__publicField(this, "
|
395
|
+
import { GLTFLoader as ThreeGLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
|
396
|
+
var CachedGLTFLoader = class extends ThreeGLTFLoader {
|
397
|
+
constructor(manager) {
|
398
|
+
super(manager);
|
399
|
+
__publicField(this, "blobCache");
|
400
|
+
this.blobCache = /* @__PURE__ */ new Map();
|
401
|
+
}
|
402
|
+
setBlobUrl(originalUrl, blobUrl) {
|
403
|
+
this.blobCache.set(originalUrl, blobUrl);
|
404
|
+
}
|
405
|
+
getBlobUrl(originalUrl) {
|
406
|
+
return this.blobCache.get(originalUrl);
|
407
|
+
}
|
408
|
+
load(url, onLoad, onProgress, onError) {
|
409
|
+
const blobUrl = this.getBlobUrl(url);
|
410
|
+
if (blobUrl) {
|
411
|
+
console.log(`Loading cached ${url.split("/").pop()}`);
|
412
|
+
super.load(blobUrl, onLoad, onProgress, onError);
|
413
|
+
} else {
|
414
|
+
super.load(url, onLoad, onProgress, onError);
|
415
|
+
}
|
416
|
+
}
|
417
|
+
};
|
418
|
+
var LRUCache = class {
|
419
|
+
constructor(maxSize = 100) {
|
420
|
+
__publicField(this, "maxSize");
|
421
|
+
__publicField(this, "cache");
|
422
|
+
this.maxSize = maxSize;
|
423
|
+
this.cache = /* @__PURE__ */ new Map();
|
424
|
+
}
|
425
|
+
get(key) {
|
426
|
+
const item = this.cache.get(key);
|
427
|
+
if (item) {
|
428
|
+
this.cache.delete(key);
|
429
|
+
this.cache.set(key, item);
|
430
|
+
}
|
431
|
+
return item;
|
432
|
+
}
|
433
|
+
set(key, value) {
|
434
|
+
if (this.cache.size >= this.maxSize) {
|
435
|
+
const oldestKey = this.cache.keys().next().value;
|
436
|
+
this.cache.delete(oldestKey);
|
437
|
+
}
|
438
|
+
this.cache.set(key, value);
|
439
|
+
}
|
440
|
+
};
|
441
|
+
var _ModelLoader = class {
|
442
|
+
constructor(maxCacheSize = 100) {
|
331
443
|
__publicField(this, "loadingManager");
|
332
|
-
__publicField(this, "fbxLoader");
|
333
444
|
__publicField(this, "gltfLoader");
|
445
|
+
__publicField(this, "modelCache");
|
446
|
+
__publicField(this, "ongoingLoads", /* @__PURE__ */ new Map());
|
334
447
|
this.loadingManager = new LoadingManager();
|
335
|
-
this.
|
336
|
-
this.
|
448
|
+
this.gltfLoader = new CachedGLTFLoader(this.loadingManager);
|
449
|
+
this.modelCache = new LRUCache(maxCacheSize);
|
337
450
|
}
|
338
|
-
|
339
|
-
|
340
|
-
if (
|
341
|
-
|
342
|
-
return;
|
451
|
+
/* TODO: decide between below lazy initialization or eager on this file's bottom export */
|
452
|
+
static getInstance() {
|
453
|
+
if (!_ModelLoader.instance) {
|
454
|
+
_ModelLoader.instance = new _ModelLoader();
|
343
455
|
}
|
344
|
-
|
345
|
-
|
346
|
-
|
456
|
+
return _ModelLoader.instance;
|
457
|
+
}
|
458
|
+
async load(fileUrl, fileType) {
|
459
|
+
const cachedModel = this.modelCache.get(fileUrl);
|
460
|
+
if (cachedModel) {
|
461
|
+
const blobURL = URL.createObjectURL(cachedModel.blob);
|
462
|
+
this.gltfLoader.setBlobUrl(fileUrl, blobURL);
|
463
|
+
return this.loadFromUrl(fileUrl, fileType, cachedModel.originalExtension);
|
464
|
+
} else {
|
465
|
+
console.log(`Loading ${fileUrl} from server`);
|
466
|
+
const ongoingLoad = this.ongoingLoads.get(fileUrl);
|
467
|
+
if (ongoingLoad)
|
468
|
+
return ongoingLoad;
|
469
|
+
const loadPromise = fetch(fileUrl).then((response) => response.blob()).then((blob) => {
|
470
|
+
const originalExtension = fileUrl.split(".").pop() || "";
|
471
|
+
this.modelCache.set(fileUrl, { blob, originalExtension });
|
472
|
+
const blobURL = URL.createObjectURL(blob);
|
473
|
+
this.ongoingLoads.delete(fileUrl);
|
474
|
+
return this.loadFromUrl(blobURL, fileType, originalExtension);
|
475
|
+
});
|
476
|
+
this.ongoingLoads.set(fileUrl, loadPromise);
|
477
|
+
return loadPromise;
|
347
478
|
}
|
479
|
+
}
|
480
|
+
async loadFromUrl(url, fileType, extension) {
|
348
481
|
if (["gltf", "glb"].includes(extension)) {
|
349
482
|
return new Promise((resolve, reject) => {
|
350
483
|
this.gltfLoader.load(
|
351
|
-
|
484
|
+
url,
|
352
485
|
(object) => {
|
353
486
|
if (fileType === "model") {
|
354
487
|
resolve(object.scene);
|
355
488
|
} else if (fileType === "animation") {
|
356
489
|
resolve(object.animations[0]);
|
357
490
|
} else {
|
358
|
-
const error = `Trying to load unknown ${fileType} type of element from file ${
|
491
|
+
const error = `Trying to load unknown ${fileType} type of element from file ${url}`;
|
359
492
|
console.error(error);
|
360
493
|
reject(error);
|
361
494
|
}
|
362
495
|
},
|
363
496
|
void 0,
|
364
497
|
(error) => {
|
365
|
-
console.error(`Error loading GL(B|TF) from ${
|
366
|
-
reject(error);
|
367
|
-
}
|
368
|
-
);
|
369
|
-
});
|
370
|
-
} else if (extension === "fbx") {
|
371
|
-
return new Promise((resolve, reject) => {
|
372
|
-
this.fbxLoader.load(
|
373
|
-
fileUrl,
|
374
|
-
(object) => {
|
375
|
-
resolve(object);
|
376
|
-
},
|
377
|
-
void 0,
|
378
|
-
(error) => {
|
379
|
-
console.error(`Error loading FBX from ${fileUrl}: ${error}`);
|
498
|
+
console.error(`Error loading GL(B|TF) from ${url}: ${error}`);
|
380
499
|
reject(error);
|
381
500
|
}
|
382
501
|
);
|
383
502
|
});
|
503
|
+
} else {
|
504
|
+
console.error(`Error: can't recognize ${url} extension: ${extension}`);
|
384
505
|
}
|
385
506
|
}
|
386
507
|
};
|
508
|
+
var ModelLoader = _ModelLoader;
|
509
|
+
__publicField(ModelLoader, "instance", null);
|
510
|
+
var MODEL_LOADER = ModelLoader.getInstance();
|
511
|
+
var ModelLoader_default = MODEL_LOADER;
|
387
512
|
|
388
513
|
// src/character/CharacterModel.ts
|
389
514
|
var CharacterModel = class {
|
390
515
|
constructor(characterDescription) {
|
391
516
|
this.characterDescription = characterDescription;
|
392
|
-
|
517
|
+
/* TODO: pick between below eager instantiation or ModelLoader.getInstance() lazy one */
|
518
|
+
__publicField(this, "modelLoader", ModelLoader_default);
|
393
519
|
__publicField(this, "mesh", null);
|
394
520
|
__publicField(this, "material", new CharacterMaterial());
|
395
521
|
__publicField(this, "animations", {});
|
@@ -410,6 +536,10 @@ var CharacterModel = class {
|
|
410
536
|
this.characterDescription.sprintAnimationFileUrl,
|
411
537
|
2 /* running */
|
412
538
|
);
|
539
|
+
await this.setAnimationFromFile(
|
540
|
+
this.characterDescription.airAnimationFileUrl,
|
541
|
+
4 /* air */
|
542
|
+
);
|
413
543
|
this.applyMaterialToAllSkinnedMeshes(this.material);
|
414
544
|
}
|
415
545
|
updateAnimation(targetAnimation, deltaTime) {
|
@@ -462,7 +592,7 @@ var CharacterModel = class {
|
|
462
592
|
if (typeof mainMesh !== "undefined") {
|
463
593
|
this.mesh = new Object3D2();
|
464
594
|
const model = mainMesh;
|
465
|
-
model.position.set(0, -0.
|
595
|
+
model.position.set(0, -0.4, 0);
|
466
596
|
this.mesh.add(model);
|
467
597
|
this.mesh.name = name;
|
468
598
|
this.mesh.scale.set(scale, scale, scale);
|
@@ -485,7 +615,7 @@ var CharacterModel = class {
|
|
485
615
|
}
|
486
616
|
});
|
487
617
|
}
|
488
|
-
transitionToAnimation(targetAnimation, transitionDuration = 0.
|
618
|
+
transitionToAnimation(targetAnimation, transitionDuration = 0.15) {
|
489
619
|
if (!this.mesh || this.currentAnimation === null)
|
490
620
|
return;
|
491
621
|
const currentAction = this.animations[this.currentAnimation];
|
@@ -506,7 +636,7 @@ var CharacterModel = class {
|
|
506
636
|
};
|
507
637
|
|
508
638
|
// src/character/LocalController.ts
|
509
|
-
import { Box3, Line3, Matrix4, Quaternion, Vector3 as Vector33 } from "three";
|
639
|
+
import { Box3, Line3, Matrix4, Quaternion, Raycaster, Vector3 as Vector33 } from "three";
|
510
640
|
var LocalController = class {
|
511
641
|
constructor(model, id, collisionsManager, keyInputManager, cameraManager, timeManager) {
|
512
642
|
this.model = model;
|
@@ -520,10 +650,20 @@ var LocalController = class {
|
|
520
650
|
radius: 0.4,
|
521
651
|
segment: new Line3(new Vector33(), new Vector33(0, 1.05, 0))
|
522
652
|
});
|
653
|
+
__publicField(this, "maxWalkSpeed", 6);
|
654
|
+
__publicField(this, "maxRunSpeed", 8.5);
|
655
|
+
__publicField(this, "gravity", -42);
|
656
|
+
__publicField(this, "jumpForce", 16);
|
657
|
+
__publicField(this, "coyoteTimeThreshold", 290);
|
658
|
+
__publicField(this, "coyoteTime", false);
|
659
|
+
__publicField(this, "canJump", true);
|
523
660
|
__publicField(this, "characterOnGround", false);
|
661
|
+
__publicField(this, "characterWasOnGround", false);
|
662
|
+
__publicField(this, "characterAirborneSince", 0);
|
663
|
+
__publicField(this, "currentHeight", 0);
|
524
664
|
__publicField(this, "characterVelocity", new Vector33());
|
525
|
-
__publicField(this, "
|
526
|
-
__publicField(this, "
|
665
|
+
__publicField(this, "vectorUp", new Vector33(0, 1, 0));
|
666
|
+
__publicField(this, "vectorDown", new Vector33(0, -1, 0));
|
527
667
|
__publicField(this, "rotationOffset", 0);
|
528
668
|
__publicField(this, "azimuthalAngle", 0);
|
529
669
|
__publicField(this, "tempBox", new Box3());
|
@@ -531,16 +671,15 @@ var LocalController = class {
|
|
531
671
|
__publicField(this, "tempSegment", new Line3());
|
532
672
|
__publicField(this, "tempVector", new Vector33());
|
533
673
|
__publicField(this, "tempVector2", new Vector33());
|
534
|
-
__publicField(this, "
|
535
|
-
__publicField(this, "
|
536
|
-
__publicField(this, "
|
537
|
-
__publicField(this, "
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
__publicField(this, "runInput", false);
|
674
|
+
__publicField(this, "rayCaster", new Raycaster());
|
675
|
+
__publicField(this, "forward");
|
676
|
+
__publicField(this, "backward");
|
677
|
+
__publicField(this, "left");
|
678
|
+
__publicField(this, "right");
|
679
|
+
__publicField(this, "run");
|
680
|
+
__publicField(this, "jump");
|
681
|
+
__publicField(this, "anyDirection");
|
682
|
+
__publicField(this, "conflictingDirections");
|
544
683
|
__publicField(this, "thirdPersonCamera", null);
|
545
684
|
__publicField(this, "speed", 0);
|
546
685
|
__publicField(this, "targetSpeed", 0);
|
@@ -550,65 +689,72 @@ var LocalController = class {
|
|
550
689
|
rotation: { quaternionY: 0, quaternionW: 0 },
|
551
690
|
state: 0 /* idle */
|
552
691
|
});
|
692
|
+
setInterval(() => this.update.bind(this), 3e3);
|
553
693
|
}
|
554
694
|
update() {
|
555
695
|
if (!this.model?.mesh || !this.model?.animationMixer)
|
556
696
|
return;
|
557
697
|
if (!this.thirdPersonCamera)
|
558
698
|
this.thirdPersonCamera = this.cameraManager.camera;
|
559
|
-
const
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
this.
|
565
|
-
this.
|
566
|
-
this.
|
567
|
-
|
699
|
+
const { forward, backward, left, right, run, jump, anyDirection, conflictingDirection } = this.keyInputManager;
|
700
|
+
this.forward = forward;
|
701
|
+
this.backward = backward;
|
702
|
+
this.left = left;
|
703
|
+
this.right = right;
|
704
|
+
this.run = run;
|
705
|
+
this.jump = jump;
|
706
|
+
this.anyDirection = anyDirection;
|
707
|
+
this.conflictingDirections = conflictingDirection;
|
708
|
+
this.targetSpeed = this.run ? this.maxRunSpeed : this.maxWalkSpeed;
|
709
|
+
this.speed += ease(this.targetSpeed, this.speed, 0.07);
|
710
|
+
this.rayCaster.set(this.model.mesh.position, this.vectorDown);
|
711
|
+
const hit = this.rayCaster.intersectObjects([this.collisionsManager.colliders]);
|
712
|
+
if (hit.length > 0)
|
713
|
+
this.currentHeight = hit[0].distance;
|
714
|
+
if (anyDirection || !this.characterOnGround) {
|
568
715
|
const targetAnimation = this.getTargetAnimation();
|
569
716
|
this.model.updateAnimation(targetAnimation, this.timeManager.deltaTime);
|
570
717
|
} else {
|
571
718
|
this.model.updateAnimation(0 /* idle */, this.timeManager.deltaTime);
|
572
719
|
}
|
573
|
-
if (
|
720
|
+
if (this.anyDirection)
|
574
721
|
this.updateRotation();
|
575
|
-
}
|
576
722
|
for (let i = 0; i < this.collisionDetectionSteps; i++) {
|
577
723
|
this.updatePosition(this.timeManager.deltaTime / this.collisionDetectionSteps, i);
|
578
724
|
}
|
579
|
-
if (this.model.mesh.position.y < 0)
|
725
|
+
if (this.model.mesh.position.y < 0)
|
580
726
|
this.resetPosition();
|
581
|
-
}
|
582
727
|
this.updateNetworkState();
|
583
728
|
}
|
584
729
|
getTargetAnimation() {
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
const conflictingDirections = forward && backward || left && right;
|
589
|
-
if (conflictingDirections)
|
730
|
+
if (!this.model.mesh)
|
731
|
+
return 0 /* idle */;
|
732
|
+
if (this.conflictingDirections)
|
590
733
|
return 0 /* idle */;
|
591
|
-
|
734
|
+
const jumpHeight = this.characterVelocity.y > 0 ? 0.2 : 1.8;
|
735
|
+
if (this.currentHeight > jumpHeight && !this.characterOnGround) {
|
736
|
+
return 4 /* air */;
|
737
|
+
}
|
738
|
+
return this.run && this.anyDirection ? 2 /* running */ : this.anyDirection ? 1 /* walking */ : 0 /* idle */;
|
592
739
|
}
|
593
740
|
updateRotationOffset() {
|
594
|
-
|
595
|
-
if (left && right || forward && backward)
|
741
|
+
if (this.conflictingDirections)
|
596
742
|
return;
|
597
|
-
if (forward) {
|
743
|
+
if (this.forward) {
|
598
744
|
this.rotationOffset = Math.PI;
|
599
|
-
if (left)
|
745
|
+
if (this.left)
|
600
746
|
this.rotationOffset = Math.PI + Math.PI / 4;
|
601
|
-
if (right)
|
747
|
+
if (this.right)
|
602
748
|
this.rotationOffset = Math.PI - Math.PI / 4;
|
603
|
-
} else if (backward) {
|
749
|
+
} else if (this.backward) {
|
604
750
|
this.rotationOffset = Math.PI * 2;
|
605
|
-
if (left)
|
751
|
+
if (this.left)
|
606
752
|
this.rotationOffset = -Math.PI * 2 - Math.PI / 4;
|
607
|
-
if (right)
|
753
|
+
if (this.right)
|
608
754
|
this.rotationOffset = Math.PI * 2 + Math.PI / 4;
|
609
|
-
} else if (left) {
|
755
|
+
} else if (this.left) {
|
610
756
|
this.rotationOffset = Math.PI * -0.5;
|
611
|
-
} else if (right) {
|
757
|
+
} else if (this.right) {
|
612
758
|
this.rotationOffset = Math.PI * 0.5;
|
613
759
|
}
|
614
760
|
}
|
@@ -626,7 +772,7 @@ var LocalController = class {
|
|
626
772
|
this.updateRotationOffset();
|
627
773
|
this.updateAzimuthalAngle();
|
628
774
|
const rotationQuaternion = new Quaternion();
|
629
|
-
rotationQuaternion.setFromAxisAngle(this.
|
775
|
+
rotationQuaternion.setFromAxisAngle(this.vectorUp, this.azimuthalAngle + this.rotationOffset);
|
630
776
|
this.model.mesh.quaternion.rotateTowards(rotationQuaternion, 0.07);
|
631
777
|
}
|
632
778
|
addScaledVectorToCharacter(deltaTime) {
|
@@ -637,36 +783,43 @@ var LocalController = class {
|
|
637
783
|
updatePosition(deltaTime, _iter) {
|
638
784
|
if (!this.model?.mesh)
|
639
785
|
return;
|
640
|
-
const { forward, backward, left, right } = this.inputDirections;
|
641
|
-
this.targetSpeed = this.runInput ? 14 : 8;
|
642
|
-
this.speed += ease(this.targetSpeed, this.speed, 0.07);
|
643
786
|
if (this.characterOnGround) {
|
644
|
-
this.
|
645
|
-
|
787
|
+
if (!this.jump)
|
788
|
+
this.canJump = true;
|
789
|
+
if (this.jump && this.canJump) {
|
646
790
|
this.characterVelocity.y += this.jumpForce;
|
647
791
|
this.canJump = false;
|
648
792
|
} else {
|
649
793
|
this.characterVelocity.y = deltaTime * this.gravity;
|
650
794
|
}
|
795
|
+
} else if (this.jump && this.coyoteTime) {
|
796
|
+
console.log("coyoteJump");
|
797
|
+
this.characterVelocity.y = this.jumpForce;
|
798
|
+
this.canJump = false;
|
651
799
|
} else {
|
652
800
|
this.characterVelocity.y += deltaTime * this.gravity;
|
653
801
|
this.canJump = false;
|
654
802
|
}
|
655
803
|
this.model.mesh.position.addScaledVector(this.characterVelocity, deltaTime);
|
656
|
-
|
657
|
-
|
658
|
-
this.
|
804
|
+
this.tempVector.set(0, 0, 0);
|
805
|
+
if (this.forward) {
|
806
|
+
const forward = new Vector33(0, 0, -1).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
|
807
|
+
this.tempVector.add(forward);
|
659
808
|
}
|
660
|
-
if (backward) {
|
661
|
-
|
662
|
-
this.
|
809
|
+
if (this.backward) {
|
810
|
+
const backward = new Vector33(0, 0, 1).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
|
811
|
+
this.tempVector.add(backward);
|
663
812
|
}
|
664
|
-
if (left) {
|
665
|
-
|
666
|
-
this.
|
813
|
+
if (this.left) {
|
814
|
+
const left = new Vector33(-1, 0, 0).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
|
815
|
+
this.tempVector.add(left);
|
816
|
+
}
|
817
|
+
if (this.right) {
|
818
|
+
const right = new Vector33(1, 0, 0).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
|
819
|
+
this.tempVector.add(right);
|
667
820
|
}
|
668
|
-
if (
|
669
|
-
this.tempVector.
|
821
|
+
if (this.tempVector.length() > 0) {
|
822
|
+
this.tempVector.normalize();
|
670
823
|
this.addScaledVectorToCharacter(deltaTime);
|
671
824
|
}
|
672
825
|
this.model.mesh.updateMatrixWorld();
|
@@ -687,6 +840,11 @@ var LocalController = class {
|
|
687
840
|
deltaVector.normalize().multiplyScalar(offset);
|
688
841
|
this.model.mesh.position.add(deltaVector);
|
689
842
|
this.characterOnGround = deltaVector.y > Math.abs(deltaTime * this.characterVelocity.y * 0.25);
|
843
|
+
if (this.characterWasOnGround && !this.characterOnGround) {
|
844
|
+
this.characterAirborneSince = Date.now();
|
845
|
+
}
|
846
|
+
this.coyoteTime = this.characterVelocity.y < 0 && this.characterOnGround === false && Date.now() - this.characterAirborneSince < this.coyoteTimeThreshold;
|
847
|
+
this.characterWasOnGround = this.characterOnGround;
|
690
848
|
if (this.characterOnGround) {
|
691
849
|
this.characterVelocity.set(0, 0, 0);
|
692
850
|
} else {
|
@@ -714,7 +872,7 @@ var LocalController = class {
|
|
714
872
|
if (!this.model?.mesh)
|
715
873
|
return;
|
716
874
|
this.characterVelocity.y = 0;
|
717
|
-
this.model.mesh.position.y =
|
875
|
+
this.model.mesh.position.y = 3;
|
718
876
|
this.characterOnGround = false;
|
719
877
|
}
|
720
878
|
};
|
@@ -770,24 +928,19 @@ import { Group, Vector3 as Vector36 } from "three";
|
|
770
928
|
// src/character/RemoteController.ts
|
771
929
|
import {
|
772
930
|
AnimationMixer as AnimationMixer2,
|
773
|
-
LoadingManager as LoadingManager2,
|
774
931
|
Object3D as Object3D3,
|
775
932
|
Quaternion as Quaternion2,
|
776
933
|
Vector3 as Vector35
|
777
934
|
} from "three";
|
778
|
-
import { FBXLoader as FBXLoader2 } from "three/examples/jsm/loaders/FBXLoader.js";
|
779
|
-
import { GLTFLoader as GLTFLoader2 } from "three/examples/jsm/loaders/GLTFLoader.js";
|
780
935
|
var RemoteController = class {
|
781
936
|
constructor(character, id) {
|
782
937
|
this.character = character;
|
783
938
|
this.id = id;
|
939
|
+
__publicField(this, "modelLoader", ModelLoader_default);
|
784
940
|
__publicField(this, "characterModel", null);
|
785
|
-
__publicField(this, "loadManager", new LoadingManager2());
|
786
941
|
__publicField(this, "animationMixer", new AnimationMixer2(new Object3D3()));
|
787
942
|
__publicField(this, "animations", /* @__PURE__ */ new Map());
|
788
943
|
__publicField(this, "currentAnimation", 0 /* idle */);
|
789
|
-
__publicField(this, "fbxLoader", new FBXLoader2(this.loadManager));
|
790
|
-
__publicField(this, "gltfLoader", new GLTFLoader2(this.loadManager));
|
791
944
|
__publicField(this, "networkState", {
|
792
945
|
id: 0,
|
793
946
|
position: { x: 0, y: 0, z: 0 },
|
@@ -795,6 +948,7 @@ var RemoteController = class {
|
|
795
948
|
state: this.currentAnimation
|
796
949
|
});
|
797
950
|
this.characterModel = this.character.model.mesh;
|
951
|
+
this.characterModel.updateMatrixWorld();
|
798
952
|
this.animationMixer = new AnimationMixer2(this.characterModel);
|
799
953
|
}
|
800
954
|
update(clientUpdate, time, deltaTime) {
|
@@ -804,44 +958,15 @@ var RemoteController = class {
|
|
804
958
|
this.updateFromNetwork(clientUpdate);
|
805
959
|
this.animationMixer.update(deltaTime);
|
806
960
|
}
|
807
|
-
setAnimationFromFile(animationType, fileName) {
|
808
|
-
const
|
809
|
-
const
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
}
|
814
|
-
if (["gltf", "glb"].includes(extension)) {
|
815
|
-
this.gltfLoader.load(
|
816
|
-
animationFile,
|
817
|
-
(anim) => {
|
818
|
-
const animation = anim.animations[0];
|
819
|
-
const animationAction = this.animationMixer.clipAction(animation);
|
820
|
-
this.animations.set(animationType, animationAction);
|
821
|
-
if (animationType === 0 /* idle */) {
|
822
|
-
animationAction.play();
|
823
|
-
}
|
824
|
-
},
|
825
|
-
void 0,
|
826
|
-
(error) => console.error(`Error loading ${animationFile}: ${error}`)
|
827
|
-
);
|
828
|
-
} else if (["fbx"].includes(extension)) {
|
829
|
-
this.fbxLoader.load(
|
830
|
-
animationFile,
|
831
|
-
(anim) => {
|
832
|
-
const animation = anim.animations[0];
|
833
|
-
const animationAction = this.animationMixer.clipAction(animation);
|
834
|
-
this.animations.set(animationType, animationAction);
|
835
|
-
if (animationType === 0 /* idle */) {
|
836
|
-
animationAction.play();
|
837
|
-
}
|
838
|
-
},
|
839
|
-
void 0,
|
840
|
-
(error) => console.error(`Error loading ${animationFile}: ${error}`)
|
841
|
-
);
|
961
|
+
async setAnimationFromFile(animationType, fileName) {
|
962
|
+
const animation = await this.modelLoader.load(fileName, "animation");
|
963
|
+
const animationAction = this.animationMixer.clipAction(animation);
|
964
|
+
this.animations.set(animationType, animationAction);
|
965
|
+
if (animationType === 0 /* idle */) {
|
966
|
+
animationAction.play();
|
842
967
|
}
|
843
968
|
}
|
844
|
-
transitionToAnimation(targetAnimation, transitionDuration = 0.
|
969
|
+
transitionToAnimation(targetAnimation, transitionDuration = 0.15) {
|
845
970
|
if (this.currentAnimation === targetAnimation)
|
846
971
|
return;
|
847
972
|
const currentAction = this.animations.get(this.currentAnimation);
|
@@ -861,9 +986,9 @@ var RemoteController = class {
|
|
861
986
|
if (!this.characterModel)
|
862
987
|
return;
|
863
988
|
const { position, rotation, state } = clientUpdate;
|
864
|
-
this.characterModel.position.lerp(new Vector35(position.x, position.y, position.z), 0.
|
989
|
+
this.characterModel.position.lerp(new Vector35(position.x, position.y, position.z), 0.15);
|
865
990
|
const rotationQuaternion = new Quaternion2(0, rotation.quaternionY, 0, rotation.quaternionW);
|
866
|
-
this.characterModel.quaternion.slerp(rotationQuaternion, 0.
|
991
|
+
this.characterModel.quaternion.slerp(rotationQuaternion, 0.6);
|
867
992
|
if (state !== this.currentAnimation) {
|
868
993
|
this.transitionToAnimation(state);
|
869
994
|
}
|
@@ -907,8 +1032,15 @@ var CharacterManager = class {
|
|
907
1032
|
__publicField(this, "character", null);
|
908
1033
|
__publicField(this, "group");
|
909
1034
|
this.group = new Group();
|
910
|
-
|
911
|
-
|
1035
|
+
setInterval(() => this.update.bind(this), 3e3);
|
1036
|
+
}
|
1037
|
+
/* TODO:
|
1038
|
+
1) Separate this method into spawnLocalCharacter and spawnRemoteCharacter
|
1039
|
+
2) Make this synchronous to avoid having loadingCharacters and instead manage
|
1040
|
+
the mesh loading async (would allow us to show a nameplate where a remote
|
1041
|
+
user is before the asset loads).
|
1042
|
+
*/
|
1043
|
+
spawnCharacter(characterDescription, id, isLocal = false, spawnPosition = new Vector36()) {
|
912
1044
|
this.characterDescription = characterDescription;
|
913
1045
|
const characterLoadingPromise = new Promise((resolve) => {
|
914
1046
|
const character = new Character(
|
@@ -923,15 +1055,26 @@ var CharacterManager = class {
|
|
923
1055
|
this.cameraManager.camera
|
924
1056
|
);
|
925
1057
|
} else {
|
926
|
-
|
1058
|
+
spawnPosition = getSpawnPositionInsideCircle(3, 30, id, 0.4);
|
927
1059
|
character.model.mesh.position.set(spawnPosition.x, spawnPosition.y, spawnPosition.z);
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
1060
|
+
character.model.mesh.updateMatrixWorld();
|
1061
|
+
this.sendUpdate({
|
1062
|
+
id,
|
1063
|
+
position: {
|
1064
|
+
x: spawnPosition.x,
|
1065
|
+
y: spawnPosition.y,
|
1066
|
+
z: spawnPosition.z
|
1067
|
+
},
|
1068
|
+
rotation: { quaternionY: 0, quaternionW: 0 },
|
1069
|
+
state: 0 /* idle */
|
1070
|
+
});
|
1071
|
+
}
|
1072
|
+
character.model.hideMaterialByMeshName("SK_Mannequin_2");
|
1073
|
+
if (!isLocal) {
|
1074
|
+
character.model?.mesh?.position.set(spawnPosition.x, spawnPosition.y, spawnPosition.z);
|
1075
|
+
character.model?.mesh?.updateMatrixWorld();
|
1076
|
+
character.position.set(spawnPosition.x, spawnPosition.y, spawnPosition.z);
|
933
1077
|
}
|
934
|
-
character.model.hideMaterialByMeshName("SK_UE5Mannequin_1");
|
935
1078
|
this.group.add(character.model.mesh);
|
936
1079
|
if (isLocal) {
|
937
1080
|
this.character = character;
|
@@ -950,6 +1093,15 @@ var CharacterManager = class {
|
|
950
1093
|
2 /* running */,
|
951
1094
|
characterDescription.sprintAnimationFileUrl
|
952
1095
|
);
|
1096
|
+
remoteController.setAnimationFromFile(
|
1097
|
+
4 /* air */,
|
1098
|
+
characterDescription.airAnimationFileUrl
|
1099
|
+
);
|
1100
|
+
remoteController.characterModel?.position.set(
|
1101
|
+
spawnPosition.x,
|
1102
|
+
spawnPosition.y,
|
1103
|
+
spawnPosition.z
|
1104
|
+
);
|
953
1105
|
this.remoteCharacterControllers.set(id, remoteController);
|
954
1106
|
}
|
955
1107
|
resolve(character);
|
@@ -998,8 +1150,14 @@ var CharacterManager = class {
|
|
998
1150
|
}
|
999
1151
|
}
|
1000
1152
|
for (const [id, update] of this.clientStates) {
|
1153
|
+
const { position } = update;
|
1001
1154
|
if (!this.remoteCharacters.has(id) && !this.loadingCharacters.has(id)) {
|
1002
|
-
this.spawnCharacter(
|
1155
|
+
this.spawnCharacter(
|
1156
|
+
this.characterDescription,
|
1157
|
+
id,
|
1158
|
+
false,
|
1159
|
+
new Vector36(position.x, position.y, position.z)
|
1160
|
+
).then((_character) => {
|
1003
1161
|
this.loadingCharacters.delete(id);
|
1004
1162
|
});
|
1005
1163
|
}
|
@@ -1031,6 +1189,10 @@ var KeyInputManager = class {
|
|
1031
1189
|
__publicField(this, "keys", /* @__PURE__ */ new Map());
|
1032
1190
|
document.addEventListener("keydown", this.onKeyDown.bind(this));
|
1033
1191
|
document.addEventListener("keyup", this.onKeyUp.bind(this));
|
1192
|
+
window.addEventListener("blur", this.handleUnfocus.bind(this));
|
1193
|
+
}
|
1194
|
+
handleUnfocus(_event) {
|
1195
|
+
this.keys.clear();
|
1034
1196
|
}
|
1035
1197
|
onKeyDown(event) {
|
1036
1198
|
this.keys.set(event.key.toLowerCase(), true);
|
@@ -1044,15 +1206,34 @@ var KeyInputManager = class {
|
|
1044
1206
|
isMovementKeyPressed() {
|
1045
1207
|
return ["w", "a", "s", "d"].some((key) => this.isKeyPressed(key));
|
1046
1208
|
}
|
1047
|
-
|
1209
|
+
get forward() {
|
1210
|
+
return this.isKeyPressed("w");
|
1211
|
+
}
|
1212
|
+
get backward() {
|
1213
|
+
return this.isKeyPressed("s");
|
1214
|
+
}
|
1215
|
+
get left() {
|
1216
|
+
return this.isKeyPressed("a");
|
1217
|
+
}
|
1218
|
+
get right() {
|
1219
|
+
return this.isKeyPressed("d");
|
1220
|
+
}
|
1221
|
+
get run() {
|
1048
1222
|
return this.isKeyPressed("shift");
|
1049
1223
|
}
|
1050
|
-
|
1224
|
+
get jump() {
|
1051
1225
|
return this.isKeyPressed(" ");
|
1052
1226
|
}
|
1227
|
+
get anyDirection() {
|
1228
|
+
return this.isMovementKeyPressed();
|
1229
|
+
}
|
1230
|
+
get conflictingDirection() {
|
1231
|
+
return this.isKeyPressed("w") && this.isKeyPressed("s") || this.isKeyPressed("a") && this.isKeyPressed("d");
|
1232
|
+
}
|
1053
1233
|
dispose() {
|
1054
1234
|
document.removeEventListener("keydown", this.onKeyDown.bind(this));
|
1055
1235
|
document.removeEventListener("keyup", this.onKeyDown.bind(this));
|
1236
|
+
window.removeEventListener("blur", this.handleUnfocus.bind(this));
|
1056
1237
|
}
|
1057
1238
|
};
|
1058
1239
|
|
@@ -1133,14 +1314,125 @@ import {
|
|
1133
1314
|
EffectPass,
|
1134
1315
|
FXAAEffect,
|
1135
1316
|
ShaderPass,
|
1136
|
-
BloomEffect
|
1317
|
+
BloomEffect,
|
1318
|
+
SSAOEffect,
|
1319
|
+
NormalPass,
|
1320
|
+
BlendFunction as BlendFunction2,
|
1321
|
+
TextureEffect
|
1137
1322
|
} from "postprocessing";
|
1138
1323
|
import {
|
1139
|
-
|
1140
|
-
|
1324
|
+
Color as Color4,
|
1325
|
+
LinearSRGBColorSpace,
|
1326
|
+
LoadingManager as LoadingManager2,
|
1327
|
+
PMREMGenerator,
|
1141
1328
|
Vector2 as Vector22,
|
1142
1329
|
WebGLRenderer as WebGLRenderer2
|
1143
1330
|
} from "three";
|
1331
|
+
import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js";
|
1332
|
+
import { Pane } from "tweakpane";
|
1333
|
+
|
1334
|
+
// src/rendering/composerSettings.ts
|
1335
|
+
import { BlendFunction } from "postprocessing";
|
1336
|
+
import { Color as Color3 } from "three";
|
1337
|
+
var composerValues = {
|
1338
|
+
renderer: {
|
1339
|
+
shadowMap: 2,
|
1340
|
+
toneMapping: 1,
|
1341
|
+
exposure: 0.75,
|
1342
|
+
bgIntensity: 0.6,
|
1343
|
+
bgBlurriness: 0
|
1344
|
+
},
|
1345
|
+
ssao: {
|
1346
|
+
blendFunction: BlendFunction.MULTIPLY,
|
1347
|
+
distanceScaling: true,
|
1348
|
+
depthAwareUpsampling: true,
|
1349
|
+
samples: 50,
|
1350
|
+
rings: 11,
|
1351
|
+
luminanceInfluence: 0.3,
|
1352
|
+
radius: 0.07,
|
1353
|
+
intensity: 3,
|
1354
|
+
bias: 0.03,
|
1355
|
+
fade: 0.03,
|
1356
|
+
resolutionScale: 1,
|
1357
|
+
color: new Color3(0),
|
1358
|
+
worldDistanceThreshold: 200,
|
1359
|
+
worldDistanceFalloff: 2,
|
1360
|
+
worldProximityThreshold: 100,
|
1361
|
+
worldProximityFalloff: 2
|
1362
|
+
},
|
1363
|
+
grain: 0.04,
|
1364
|
+
bloom: 0.7
|
1365
|
+
};
|
1366
|
+
var composerOptions = {
|
1367
|
+
renderer: {
|
1368
|
+
shadowMap: { min: 0, max: 2, step: 1 },
|
1369
|
+
toneMapping: { min: 0, max: 5, step: 1 },
|
1370
|
+
exposure: { min: 0, max: 1, step: 0.01 },
|
1371
|
+
bgIntensity: { min: 0, max: 1, step: 0.01 },
|
1372
|
+
bgBlurriness: { min: 0, max: 0.1, step: 1e-3 }
|
1373
|
+
},
|
1374
|
+
ssao: {
|
1375
|
+
samples: { min: 1, max: 50, step: 1 },
|
1376
|
+
rings: { min: 1, max: 50, step: 1 },
|
1377
|
+
luminanceInfluence: { min: 0, max: 1, step: 0.01 },
|
1378
|
+
radius: { min: 0, max: 0.1, step: 1e-3 },
|
1379
|
+
intensity: { min: 0, max: 5, step: 0.1 },
|
1380
|
+
bias: { min: 0, max: 0.1, step: 1e-3 },
|
1381
|
+
fade: { min: 0, max: 0.1, step: 1e-3 },
|
1382
|
+
resolutionScale: { min: 0.25, max: 2, step: 0.25 },
|
1383
|
+
worldDistanceThreshold: { min: 0, max: 200, step: 1 },
|
1384
|
+
worldDistanceFalloff: { min: 0, max: 200, step: 1 },
|
1385
|
+
worldProximityThreshold: { min: 0, max: 2, step: 0.01 },
|
1386
|
+
worldProximityFalloff: { min: 0, max: 2, step: 0.01 }
|
1387
|
+
},
|
1388
|
+
grain: {
|
1389
|
+
amount: { min: 0, max: 0.2, step: 2e-3 }
|
1390
|
+
},
|
1391
|
+
bloom: {
|
1392
|
+
amount: { min: 0, max: 4, step: 0.1 }
|
1393
|
+
}
|
1394
|
+
};
|
1395
|
+
var shadowMapTypes = {
|
1396
|
+
0: "BasicShadowMap",
|
1397
|
+
1: "PCFShadowMap",
|
1398
|
+
2: "PCFSoftShadowMap"
|
1399
|
+
};
|
1400
|
+
var rendererToneMappingTypes = {
|
1401
|
+
0: "NoToneMapping",
|
1402
|
+
1: "LinearToneMapping",
|
1403
|
+
2: "ReinhardToneMapping",
|
1404
|
+
3: "CineonToneMapping",
|
1405
|
+
4: "ACESFilmicToneMapping",
|
1406
|
+
5: "CustomToneMapping"
|
1407
|
+
};
|
1408
|
+
var rendererBlades = {
|
1409
|
+
shadowMapType: shadowMapTypes[composerValues.renderer.shadowMap],
|
1410
|
+
toneMappingType: rendererToneMappingTypes[composerValues.renderer.toneMapping]
|
1411
|
+
};
|
1412
|
+
var setShadowMapType = (value) => {
|
1413
|
+
rendererBlades.shadowMapType = shadowMapTypes[value];
|
1414
|
+
};
|
1415
|
+
var setToneMappingType = (value) => {
|
1416
|
+
rendererBlades.toneMappingType = rendererToneMappingTypes[value];
|
1417
|
+
};
|
1418
|
+
var ssaoMaterialParams = [
|
1419
|
+
"fade",
|
1420
|
+
"bias",
|
1421
|
+
"minRadiusScale",
|
1422
|
+
"worldDistanceThreshold",
|
1423
|
+
"worldDistanceFalloff",
|
1424
|
+
"worldProximityThreshold",
|
1425
|
+
"worldProximityFalloff"
|
1426
|
+
];
|
1427
|
+
var statsData = {
|
1428
|
+
triangles: "0",
|
1429
|
+
geometries: "0",
|
1430
|
+
textures: "0",
|
1431
|
+
shaders: "0",
|
1432
|
+
postPasses: "0",
|
1433
|
+
drawCalls: "0",
|
1434
|
+
FPS: "0"
|
1435
|
+
};
|
1144
1436
|
|
1145
1437
|
// src/rendering/post-effects/gauss-grain.ts
|
1146
1438
|
import { ShaderMaterial, Uniform, Vector2 } from "three";
|
@@ -1219,6 +1511,7 @@ var Composer = class {
|
|
1219
1511
|
__publicField(this, "width", window.innerWidth);
|
1220
1512
|
__publicField(this, "height", window.innerHeight);
|
1221
1513
|
__publicField(this, "resolution", new Vector22(this.width, this.height));
|
1514
|
+
__publicField(this, "isEnvHDRI", false);
|
1222
1515
|
__publicField(this, "scene");
|
1223
1516
|
__publicField(this, "camera");
|
1224
1517
|
__publicField(this, "renderer");
|
@@ -1228,8 +1521,19 @@ var Composer = class {
|
|
1228
1521
|
__publicField(this, "fxaaPass");
|
1229
1522
|
__publicField(this, "bloomEffect");
|
1230
1523
|
__publicField(this, "bloomPass");
|
1524
|
+
__publicField(this, "normalPass");
|
1525
|
+
__publicField(this, "normalTextureEffect");
|
1526
|
+
__publicField(this, "ssaoEffect");
|
1527
|
+
__publicField(this, "ssaoPass");
|
1231
1528
|
__publicField(this, "gaussGrainEffect", GaussGrainEffect);
|
1232
1529
|
__publicField(this, "gaussGrainPass");
|
1530
|
+
__publicField(this, "gui", new Pane());
|
1531
|
+
__publicField(this, "guiVisible", false);
|
1532
|
+
__publicField(this, "stats", this.gui.addFolder({ title: "stats", expanded: true }));
|
1533
|
+
__publicField(this, "renderOptions", this.gui.addFolder({ title: "renderOptions", expanded: false }));
|
1534
|
+
__publicField(this, "ssao", this.gui.addFolder({ title: "ambientOcclusion", expanded: false }));
|
1535
|
+
__publicField(this, "post", this.gui.addFolder({ title: "post", expanded: false }));
|
1536
|
+
__publicField(this, "export", this.gui.addFolder({ title: "import/export", expanded: false }));
|
1233
1537
|
this.scene = scene;
|
1234
1538
|
this.camera = camera;
|
1235
1539
|
this.renderer = new WebGLRenderer2({
|
@@ -1238,27 +1542,161 @@ var Composer = class {
|
|
1238
1542
|
stencil: false,
|
1239
1543
|
depth: false
|
1240
1544
|
});
|
1545
|
+
this.renderer.info.autoReset = false;
|
1241
1546
|
this.renderer.setSize(this.width, this.height);
|
1242
1547
|
this.renderer.shadowMap.enabled = true;
|
1243
|
-
this.renderer.shadowMap.type =
|
1244
|
-
this.renderer.toneMapping =
|
1245
|
-
this.renderer.toneMappingExposure = 0.
|
1548
|
+
this.renderer.shadowMap.type = composerValues.renderer.shadowMap;
|
1549
|
+
this.renderer.toneMapping = composerValues.renderer.toneMapping;
|
1550
|
+
this.renderer.toneMappingExposure = 0.7;
|
1246
1551
|
document.body.appendChild(this.renderer.domElement);
|
1247
1552
|
this.composer = new EffectComposer(this.renderer);
|
1248
1553
|
this.renderPass = new RenderPass(this.scene, this.camera);
|
1554
|
+
this.normalPass = new NormalPass(this.scene, this.camera);
|
1555
|
+
this.normalTextureEffect = new TextureEffect({
|
1556
|
+
blendFunction: BlendFunction2.SKIP,
|
1557
|
+
texture: this.normalPass.texture
|
1558
|
+
});
|
1249
1559
|
this.fxaaEffect = new FXAAEffect();
|
1560
|
+
this.bloomEffect = new BloomEffect({
|
1561
|
+
intensity: composerValues.bloom
|
1562
|
+
});
|
1563
|
+
this.ssaoEffect = new SSAOEffect(this.camera, this.normalPass.texture, {
|
1564
|
+
...composerValues.ssao
|
1565
|
+
});
|
1250
1566
|
this.fxaaPass = new EffectPass(this.camera, this.fxaaEffect);
|
1251
|
-
this.bloomEffect = new BloomEffect();
|
1252
1567
|
this.bloomPass = new EffectPass(this.camera, this.bloomEffect);
|
1568
|
+
this.ssaoPass = new EffectPass(this.camera, this.ssaoEffect, this.normalTextureEffect);
|
1253
1569
|
this.gaussGrainPass = new ShaderPass(this.gaussGrainEffect, "tDiffuse");
|
1254
1570
|
this.composer.addPass(this.renderPass);
|
1571
|
+
this.composer.addPass(this.normalPass);
|
1572
|
+
this.composer.addPass(this.ssaoPass);
|
1255
1573
|
this.composer.addPass(this.fxaaPass);
|
1256
1574
|
this.composer.addPass(this.bloomPass);
|
1257
1575
|
this.composer.addPass(this.gaussGrainPass);
|
1258
|
-
window.addEventListener("resize", () =>
|
1259
|
-
|
1260
|
-
|
1576
|
+
window.addEventListener("resize", () => this.updateProjection());
|
1577
|
+
window.addEventListener("keydown", this.processKey.bind(this));
|
1578
|
+
this.setupGUIListeners.bind(this)();
|
1261
1579
|
this.updateProjection();
|
1580
|
+
this.setupTweakPane();
|
1581
|
+
}
|
1582
|
+
setupGUIListeners() {
|
1583
|
+
const gui = this.gui;
|
1584
|
+
const paneElement = gui.containerElem_;
|
1585
|
+
paneElement.style.display = this.guiVisible ? "unset" : "none";
|
1586
|
+
this.gui.element.addEventListener("mousedown", () => setTweakpaneActive(true));
|
1587
|
+
this.gui.element.addEventListener("mouseup", () => setTweakpaneActive(false));
|
1588
|
+
this.gui.element.addEventListener("mouseleave", () => setTweakpaneActive(false));
|
1589
|
+
}
|
1590
|
+
setupTweakPane() {
|
1591
|
+
this.stats.addMonitor(statsData, "triangles");
|
1592
|
+
this.stats.addMonitor(statsData, "geometries");
|
1593
|
+
this.stats.addMonitor(statsData, "textures");
|
1594
|
+
this.stats.addMonitor(statsData, "shaders");
|
1595
|
+
this.stats.addMonitor(statsData, "postPasses");
|
1596
|
+
this.stats.addMonitor(statsData, "drawCalls");
|
1597
|
+
this.stats.addMonitor(statsData, "FPS");
|
1598
|
+
this.renderOptions.addInput(composerValues.renderer, "shadowMap", composerOptions.renderer.shadowMap);
|
1599
|
+
this.renderOptions.addMonitor(rendererBlades, "shadowMapType");
|
1600
|
+
this.renderOptions.addInput(composerValues.renderer, "toneMapping", composerOptions.renderer.toneMapping);
|
1601
|
+
this.renderOptions.addMonitor(rendererBlades, "toneMappingType");
|
1602
|
+
this.renderOptions.addInput(composerValues.renderer, "exposure", composerOptions.renderer.exposure);
|
1603
|
+
this.renderOptions.addInput(composerValues.renderer, "bgIntensity", composerOptions.renderer.bgIntensity);
|
1604
|
+
this.renderOptions.addInput(composerValues.renderer, "bgBlurriness", composerOptions.renderer.bgBlurriness);
|
1605
|
+
this.renderOptions.on("change", (e) => {
|
1606
|
+
const target = e.target;
|
1607
|
+
switch (target.label) {
|
1608
|
+
case "shadowMap":
|
1609
|
+
this.renderer.shadowMap.type = e.value;
|
1610
|
+
setShadowMapType(e.value);
|
1611
|
+
break;
|
1612
|
+
case "toneMapping":
|
1613
|
+
this.renderer.toneMapping = e.value;
|
1614
|
+
setToneMappingType(e.value);
|
1615
|
+
break;
|
1616
|
+
case "exposure":
|
1617
|
+
this.renderer.toneMappingExposure = e.value;
|
1618
|
+
break;
|
1619
|
+
case "bgIntensity":
|
1620
|
+
this.scene.backgroundIntensity = e.value;
|
1621
|
+
break;
|
1622
|
+
case "bgBlurriness":
|
1623
|
+
this.scene.backgroundBlurriness = e.value;
|
1624
|
+
break;
|
1625
|
+
default:
|
1626
|
+
break;
|
1627
|
+
}
|
1628
|
+
});
|
1629
|
+
this.ssao.addInput({ showEffectOnly: false }, "showEffectOnly");
|
1630
|
+
this.ssao.addInput(composerValues.ssao, "samples", composerOptions.ssao.samples);
|
1631
|
+
this.ssao.addInput(composerValues.ssao, "rings", composerOptions.ssao.rings);
|
1632
|
+
this.ssao.addInput(composerValues.ssao, "luminanceInfluence", composerOptions.ssao.luminanceInfluence);
|
1633
|
+
this.ssao.addInput(composerValues.ssao, "radius", composerOptions.ssao.radius);
|
1634
|
+
this.ssao.addInput(composerValues.ssao, "intensity", composerOptions.ssao.intensity);
|
1635
|
+
this.ssao.addInput(composerValues.ssao, "bias", composerOptions.ssao.bias);
|
1636
|
+
this.ssao.addInput(composerValues.ssao, "fade", composerOptions.ssao.fade);
|
1637
|
+
this.ssao.addInput(composerValues.ssao, "resolutionScale", composerOptions.ssao.resolutionScale);
|
1638
|
+
this.ssao.addInput(composerValues.ssao, "worldDistanceThreshold", composerOptions.ssao.worldDistanceThreshold);
|
1639
|
+
this.ssao.addInput(composerValues.ssao, "worldDistanceFalloff", composerOptions.ssao.worldDistanceFalloff);
|
1640
|
+
this.ssao.addInput(composerValues.ssao, "worldProximityThreshold", composerOptions.ssao.worldProximityThreshold);
|
1641
|
+
this.ssao.addInput(composerValues.ssao, "worldProximityFalloff", composerOptions.ssao.worldProximityFalloff);
|
1642
|
+
this.ssao.addInput(composerValues.ssao, "color");
|
1643
|
+
this.ssao.on("change", (e) => {
|
1644
|
+
if (!e.presetKey) {
|
1645
|
+
return;
|
1646
|
+
}
|
1647
|
+
const preset = e.presetKey;
|
1648
|
+
if (preset === "showEffectOnly") {
|
1649
|
+
this.ssaoEffect.blendMode.blendFunction = e.value === true ? BlendFunction2.NORMAL : BlendFunction2.MULTIPLY;
|
1650
|
+
return;
|
1651
|
+
}
|
1652
|
+
if (preset === "resolutionScale") {
|
1653
|
+
this.ssaoEffect.resolution.scale = e.value;
|
1654
|
+
return;
|
1655
|
+
}
|
1656
|
+
if (ssaoMaterialParams.includes(e.presetKey)) {
|
1657
|
+
this.ssaoEffect.ssaoMaterial[preset] = e.value;
|
1658
|
+
return;
|
1659
|
+
}
|
1660
|
+
if (e.presetKey === "color") {
|
1661
|
+
this.ssaoEffect.color = new Color4().setRGB(
|
1662
|
+
e.value.r / 255,
|
1663
|
+
e.value.g / 255,
|
1664
|
+
e.value.b / 255
|
1665
|
+
);
|
1666
|
+
return;
|
1667
|
+
}
|
1668
|
+
this.ssaoEffect[preset] = e.value;
|
1669
|
+
});
|
1670
|
+
this.post.addInput(composerValues, "bloom", composerOptions.bloom.amount);
|
1671
|
+
this.post.addInput(composerValues, "grain", composerOptions.grain.amount);
|
1672
|
+
this.post.on("change", (e) => {
|
1673
|
+
const target = e.presetKey;
|
1674
|
+
console.log(target);
|
1675
|
+
switch (target) {
|
1676
|
+
case "bloom":
|
1677
|
+
this.bloomEffect.intensity = e.value;
|
1678
|
+
break;
|
1679
|
+
case "grain":
|
1680
|
+
this.gaussGrainEffect.uniforms.amount.value = e.value;
|
1681
|
+
break;
|
1682
|
+
default:
|
1683
|
+
break;
|
1684
|
+
}
|
1685
|
+
});
|
1686
|
+
const button = this.export.addButton({ title: "export" });
|
1687
|
+
button.on("click", () => {
|
1688
|
+
console.log(this.gui.exportPreset());
|
1689
|
+
});
|
1690
|
+
}
|
1691
|
+
toggleGUI() {
|
1692
|
+
const gui = this.gui;
|
1693
|
+
const paneElement = gui.containerElem_;
|
1694
|
+
paneElement.style.display = this.guiVisible ? "none" : "unset";
|
1695
|
+
this.guiVisible = !this.guiVisible;
|
1696
|
+
}
|
1697
|
+
processKey(e) {
|
1698
|
+
if (e.key === "p")
|
1699
|
+
this.toggleGUI();
|
1262
1700
|
}
|
1263
1701
|
updateProjection() {
|
1264
1702
|
this.width = window.innerWidth;
|
@@ -1270,15 +1708,59 @@ var Composer = class {
|
|
1270
1708
|
this.fxaaPass.setSize(this.width, this.height);
|
1271
1709
|
if (this.renderPass)
|
1272
1710
|
this.renderPass.setSize(this.width, this.height);
|
1711
|
+
if (this.bloomPass)
|
1712
|
+
this.bloomPass.setSize(this.width, this.height);
|
1713
|
+
if (this.ssaoPass)
|
1714
|
+
this.ssaoPass.setSize(this.width, this.height);
|
1715
|
+
if (this.normalPass)
|
1716
|
+
this.normalPass.setSize(this.width, this.height);
|
1273
1717
|
this.renderer.setSize(this.width, this.height);
|
1274
1718
|
}
|
1275
|
-
|
1276
|
-
this.
|
1719
|
+
updateStats(timeManager) {
|
1720
|
+
const { geometries, textures } = this.renderer.info.memory;
|
1721
|
+
const { triangles, calls } = this.renderer.info.render;
|
1722
|
+
statsData.triangles = triangles.toString();
|
1723
|
+
statsData.geometries = geometries.toString();
|
1724
|
+
statsData.textures = textures.toString();
|
1725
|
+
statsData.shaders = this.renderer.info.programs.length.toString();
|
1726
|
+
statsData.postPasses = this.composer.passes.length.toString();
|
1727
|
+
statsData.drawCalls = calls.toString();
|
1728
|
+
statsData.FPS = Math.round(timeManager.averageFPS).toString();
|
1729
|
+
}
|
1730
|
+
render(timeManager) {
|
1731
|
+
this.renderer.info.reset();
|
1732
|
+
this.normalPass.texture.needsUpdate = true;
|
1277
1733
|
this.gaussGrainEffect.uniforms.resolution.value = this.resolution;
|
1278
|
-
this.gaussGrainEffect.uniforms.time.value = time;
|
1734
|
+
this.gaussGrainEffect.uniforms.time.value = timeManager.time;
|
1279
1735
|
this.gaussGrainEffect.uniforms.alpha.value = 1;
|
1280
|
-
this.
|
1281
|
-
this.
|
1736
|
+
this.composer.render();
|
1737
|
+
this.updateStats(timeManager);
|
1738
|
+
}
|
1739
|
+
useHDRI(url) {
|
1740
|
+
if (this.isEnvHDRI || !this.renderer)
|
1741
|
+
return;
|
1742
|
+
const pmremGenerator = new PMREMGenerator(this.renderer);
|
1743
|
+
new RGBELoader(new LoadingManager2()).load(
|
1744
|
+
url,
|
1745
|
+
(texture) => {
|
1746
|
+
const envMap = pmremGenerator.fromEquirectangular(texture).texture;
|
1747
|
+
if (envMap) {
|
1748
|
+
envMap.colorSpace = LinearSRGBColorSpace;
|
1749
|
+
envMap.needsUpdate = true;
|
1750
|
+
this.scene.environment = envMap;
|
1751
|
+
this.scene.background = envMap;
|
1752
|
+
this.scene.backgroundIntensity = 0.5;
|
1753
|
+
this.isEnvHDRI = true;
|
1754
|
+
texture.dispose();
|
1755
|
+
pmremGenerator.dispose();
|
1756
|
+
}
|
1757
|
+
},
|
1758
|
+
() => {
|
1759
|
+
},
|
1760
|
+
(error) => {
|
1761
|
+
console.error(`Can't load ${url}: ${JSON.stringify(error)}`);
|
1762
|
+
}
|
1763
|
+
);
|
1282
1764
|
}
|
1283
1765
|
};
|
1284
1766
|
|
@@ -1288,8 +1770,10 @@ var TimeManager = class {
|
|
1288
1770
|
constructor() {
|
1289
1771
|
__publicField(this, "clock", new Clock());
|
1290
1772
|
__publicField(this, "roundMagnitude", 2e5);
|
1291
|
-
__publicField(this, "maxAverageFrames",
|
1773
|
+
__publicField(this, "maxAverageFrames", 700);
|
1292
1774
|
__publicField(this, "deltaTimes", []);
|
1775
|
+
__publicField(this, "fpsMaxSamples", 10);
|
1776
|
+
__publicField(this, "fpsSamples", []);
|
1293
1777
|
__publicField(this, "targetAverageDeltaTime", 0);
|
1294
1778
|
__publicField(this, "lerpedAverageMagDelta", 0);
|
1295
1779
|
__publicField(this, "fpsUpdateTime", 0);
|
@@ -1299,15 +1783,15 @@ var TimeManager = class {
|
|
1299
1783
|
__publicField(this, "rawDeltaTime", 0);
|
1300
1784
|
__publicField(this, "frame", 0);
|
1301
1785
|
__publicField(this, "fps", 0);
|
1786
|
+
__publicField(this, "averageFPS", 0);
|
1302
1787
|
}
|
1303
1788
|
update() {
|
1304
1789
|
this.rawDeltaTime = this.clock.getDelta();
|
1305
1790
|
this.frame++;
|
1306
1791
|
this.time += this.rawDeltaTime;
|
1307
1792
|
this.deltaTimes.push(this.rawDeltaTime);
|
1308
|
-
if (this.deltaTimes.length > this.maxAverageFrames)
|
1793
|
+
if (this.deltaTimes.length > this.maxAverageFrames)
|
1309
1794
|
this.deltaTimes.shift();
|
1310
|
-
}
|
1311
1795
|
this.targetAverageDeltaTime = this.deltaTimes.reduce((prev, curr) => prev + curr, 0) / this.deltaTimes.length;
|
1312
1796
|
this.lerpedAverageMagDelta += ease(
|
1313
1797
|
this.targetAverageDeltaTime * this.roundMagnitude,
|
@@ -1318,8 +1802,14 @@ var TimeManager = class {
|
|
1318
1802
|
const smoothDT = Math.round(revertMagnitude * this.roundMagnitude) / this.roundMagnitude;
|
1319
1803
|
this.deltaTime = smoothDT > this.rawDeltaTime * 1.75 ? this.rawDeltaTime : smoothDT;
|
1320
1804
|
this.framesSinceLastFPSUpdate++;
|
1321
|
-
if (this.framesSinceLastFPSUpdate >=
|
1805
|
+
if (this.framesSinceLastFPSUpdate >= 60) {
|
1322
1806
|
this.fps = Math.round(this.framesSinceLastFPSUpdate / (this.time - this.fpsUpdateTime) * 100) / 100;
|
1807
|
+
this.fpsSamples.push(this.fps);
|
1808
|
+
if (this.fpsSamples.length > this.fpsMaxSamples)
|
1809
|
+
this.fpsSamples.shift();
|
1810
|
+
this.averageFPS = this.fpsSamples.length === this.fpsMaxSamples ? Math.round(
|
1811
|
+
this.fpsSamples.reduce((prev, curr) => prev + curr, 0) / this.fpsSamples.length
|
1812
|
+
) : this.fps;
|
1323
1813
|
this.fpsUpdateTime = this.time;
|
1324
1814
|
this.framesSinceLastFPSUpdate = 0;
|
1325
1815
|
}
|
@@ -1332,9 +1822,10 @@ import {
|
|
1332
1822
|
getRelativePositionAndRotationRelativeToObject
|
1333
1823
|
} from "mml-web";
|
1334
1824
|
import {
|
1335
|
-
Color as
|
1825
|
+
Color as Color5,
|
1336
1826
|
Euler,
|
1337
1827
|
FrontSide,
|
1828
|
+
Group as Group3,
|
1338
1829
|
Mesh as Mesh2,
|
1339
1830
|
MeshStandardMaterial as MeshStandardMaterial2,
|
1340
1831
|
Vector3 as Vector37
|
@@ -1349,9 +1840,24 @@ var CollisionsManager = class {
|
|
1349
1840
|
__publicField(this, "tempVector2", new Vector37());
|
1350
1841
|
__publicField(this, "collisionMeshState", /* @__PURE__ */ new Map());
|
1351
1842
|
__publicField(this, "collisionTrigger");
|
1843
|
+
__publicField(this, "colliders", new Group3());
|
1844
|
+
__publicField(this, "collisionEnabledMeshes", {});
|
1352
1845
|
this.scene = scene;
|
1353
1846
|
this.collisionTrigger = MMLCollisionTrigger.init();
|
1354
1847
|
}
|
1848
|
+
safeAddColliders(child) {
|
1849
|
+
if (!(child.uuid in this.collisionEnabledMeshes)) {
|
1850
|
+
const clone = child.clone();
|
1851
|
+
this.collisionEnabledMeshes[child.uuid] = clone;
|
1852
|
+
this.colliders.add(clone);
|
1853
|
+
}
|
1854
|
+
}
|
1855
|
+
removeFromColliders(child) {
|
1856
|
+
if (child.uuid in this.collisionEnabledMeshes) {
|
1857
|
+
this.colliders.remove(this.collisionEnabledMeshes[child.uuid]);
|
1858
|
+
delete this.collisionEnabledMeshes[child.uuid];
|
1859
|
+
}
|
1860
|
+
}
|
1355
1861
|
createCollisionMeshState(group) {
|
1356
1862
|
const geometries = [];
|
1357
1863
|
group.traverse((child) => {
|
@@ -1359,6 +1865,7 @@ var CollisionsManager = class {
|
|
1359
1865
|
const mesh = child;
|
1360
1866
|
mesh.localToWorld(new Vector37());
|
1361
1867
|
mesh.updateMatrixWorld();
|
1868
|
+
this.safeAddColliders(mesh);
|
1362
1869
|
const clonedGeometry = mesh.geometry.clone();
|
1363
1870
|
clonedGeometry.applyMatrix4(mesh.matrixWorld);
|
1364
1871
|
for (const key in clonedGeometry.attributes) {
|
@@ -1384,7 +1891,7 @@ var CollisionsManager = class {
|
|
1384
1891
|
);
|
1385
1892
|
mergedMesh.geometry.boundsTree = meshBVH;
|
1386
1893
|
const visualizer = new MeshBVHVisualizer(mergedMesh, 3);
|
1387
|
-
visualizer.edgeMaterial.color = new
|
1894
|
+
visualizer.edgeMaterial.color = new Color5(255);
|
1388
1895
|
visualizer.update();
|
1389
1896
|
return { source: group, visualizer, meshBVH };
|
1390
1897
|
}
|
@@ -1419,6 +1926,13 @@ var CollisionsManager = class {
|
|
1419
1926
|
this.scene.remove(meshState.visualizer);
|
1420
1927
|
}
|
1421
1928
|
this.collisionMeshState.delete(group);
|
1929
|
+
if (group) {
|
1930
|
+
group.traverse((child) => {
|
1931
|
+
if (child.type === "Mesh") {
|
1932
|
+
this.removeFromColliders(child);
|
1933
|
+
}
|
1934
|
+
});
|
1935
|
+
}
|
1422
1936
|
}
|
1423
1937
|
}
|
1424
1938
|
applyCollider(tempSegment, radius, boundingBox, meshState) {
|