@mml-io/3d-web-client-core 0.0.0-experimental-751c031-20230804

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/index.js ADDED
@@ -0,0 +1,2544 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => {
4
+ __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
5
+ return value;
6
+ };
7
+
8
+ // src/camera/CameraManager.ts
9
+ import { PerspectiveCamera, Raycaster, Vector3 as Vector32 } from "three";
10
+
11
+ // src/helpers/math-helpers.ts
12
+ import { Vector3 } from "three";
13
+ var getSpawnPositionInsideCircle = (radius, positions, id, yPos = 0) => {
14
+ if (id > 0)
15
+ id += 3;
16
+ const goldenAngle = Math.PI * (3 - Math.sqrt(5));
17
+ const theta = id * goldenAngle;
18
+ const scale = id / positions;
19
+ const scaledRadius = scale * radius;
20
+ const x = Math.cos(theta) * scaledRadius;
21
+ const z = Math.sin(theta) * scaledRadius;
22
+ return new Vector3(x, yPos, z);
23
+ };
24
+ var round = (n, digits) => {
25
+ return Number(n.toFixed(digits));
26
+ };
27
+ var ease = (target, n, factor) => {
28
+ return round((target - n) * factor, 5);
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/tweakpane/tweakPaneActivity.ts
38
+ var isTweakpaneActive = false;
39
+ function setTweakpaneActive(status) {
40
+ isTweakpaneActive = status;
41
+ }
42
+ function getTweakpaneActive() {
43
+ return isTweakpaneActive;
44
+ }
45
+
46
+ // src/camera/CameraManager.ts
47
+ var CameraManager = class {
48
+ constructor(collisionsManager) {
49
+ this.collisionsManager = collisionsManager;
50
+ __publicField(this, "camera");
51
+ __publicField(this, "initialDistance", 2.5);
52
+ __publicField(this, "minDistance", 0.1);
53
+ __publicField(this, "maxDistance", 6);
54
+ __publicField(this, "initialFOV", 80);
55
+ __publicField(this, "fov", this.initialFOV);
56
+ __publicField(this, "minFOV", 63);
57
+ __publicField(this, "maxFOV", 85);
58
+ __publicField(this, "targetFOV", this.initialFOV);
59
+ __publicField(this, "minPolarAngle", Math.PI * 0.25);
60
+ __publicField(this, "maxPolarAngle", Math.PI * 0.95);
61
+ __publicField(this, "dampingFactor", 0.091);
62
+ __publicField(this, "targetDistance", this.initialDistance);
63
+ __publicField(this, "distance", this.initialDistance);
64
+ __publicField(this, "targetPhi", Math.PI / 2);
65
+ __publicField(this, "phi", Math.PI / 2);
66
+ __publicField(this, "targetTheta", -Math.PI / 2);
67
+ __publicField(this, "theta", -Math.PI / 2);
68
+ __publicField(this, "dragging", false);
69
+ __publicField(this, "target", new Vector32(0, 1.55, 0));
70
+ __publicField(this, "hadTarget", false);
71
+ __publicField(this, "rayCaster");
72
+ this.camera = new PerspectiveCamera(this.fov, window.innerWidth / window.innerHeight, 0.1, 400);
73
+ this.camera.position.set(0, 1.4, -this.initialDistance);
74
+ this.rayCaster = new Raycaster();
75
+ document.addEventListener("mousedown", this.onMouseDown.bind(this));
76
+ document.addEventListener("mouseup", this.onMouseUp.bind(this));
77
+ document.addEventListener("mousemove", this.onMouseMove.bind(this));
78
+ document.addEventListener("wheel", this.onMouseWheel.bind(this));
79
+ window.addEventListener("resize", this.onResize.bind(this));
80
+ }
81
+ onResize() {
82
+ const width = window.innerWidth;
83
+ const height = window.innerHeight;
84
+ this.camera.aspect = width / height;
85
+ this.camera.updateProjectionMatrix();
86
+ }
87
+ onMouseDown(_event) {
88
+ this.dragging = true;
89
+ }
90
+ onMouseUp(_event) {
91
+ this.dragging = false;
92
+ }
93
+ onMouseMove(event) {
94
+ if (!this.dragging || getTweakpaneActive() === true)
95
+ return;
96
+ if (this.targetTheta === null || this.targetPhi === null)
97
+ return;
98
+ this.targetTheta += event.movementX * 0.01;
99
+ this.targetPhi -= event.movementY * 0.01;
100
+ this.targetPhi = Math.max(this.minPolarAngle, Math.min(this.maxPolarAngle, this.targetPhi));
101
+ }
102
+ onMouseWheel(event) {
103
+ const scrollAmount = event.deltaY * 1e-3;
104
+ this.targetDistance += scrollAmount;
105
+ this.targetDistance = Math.max(
106
+ this.minDistance,
107
+ Math.min(this.maxDistance, this.targetDistance)
108
+ );
109
+ }
110
+ setTarget(target) {
111
+ this.target.copy(target);
112
+ if (!this.hadTarget) {
113
+ this.hadTarget = true;
114
+ this.reverseUpdateFromPositions();
115
+ }
116
+ }
117
+ reverseUpdateFromPositions() {
118
+ if (this.phi === null || this.theta == null)
119
+ return;
120
+ const dx = this.camera.position.x - this.target.x;
121
+ const dy = this.camera.position.y - this.target.y;
122
+ const dz = this.camera.position.z - this.target.z;
123
+ this.targetDistance = Math.sqrt(dx * dx + dy * dy + dz * dz);
124
+ this.targetTheta = (this.theta + 2 * Math.PI) % (2 * Math.PI);
125
+ this.targetPhi = Math.max(0, Math.min(Math.PI, this.phi));
126
+ this.phi = this.targetPhi;
127
+ this.theta = this.targetTheta;
128
+ this.distance = this.targetDistance;
129
+ }
130
+ adjustCameraPosition() {
131
+ this.rayCaster.set(
132
+ this.camera.position,
133
+ this.target.clone().sub(this.camera.position).normalize()
134
+ );
135
+ const minimumDistance = this.collisionsManager.raycastFirstDistance(this.rayCaster.ray);
136
+ const cameraToPlayerDistance = this.camera.position.distanceTo(this.target);
137
+ if (minimumDistance !== null && minimumDistance <= cameraToPlayerDistance) {
138
+ this.targetDistance = cameraToPlayerDistance - minimumDistance;
139
+ this.distance = this.targetDistance;
140
+ }
141
+ }
142
+ update() {
143
+ if (this.target === null)
144
+ return;
145
+ if (this.phi !== null && this.targetPhi !== null && this.theta !== null && this.targetTheta !== null) {
146
+ this.distance += (this.targetDistance - this.distance) * this.dampingFactor * 0.21;
147
+ this.phi += (this.targetPhi - this.phi) * this.dampingFactor;
148
+ this.theta += (this.targetTheta - this.theta) * this.dampingFactor;
149
+ const x = this.target.x + this.distance * Math.sin(this.phi) * Math.cos(this.theta);
150
+ const y = this.target.y + this.distance * Math.cos(this.phi);
151
+ const z = this.target.z + this.distance * Math.sin(this.phi) * Math.sin(this.theta);
152
+ this.targetFOV = remap(
153
+ this.targetDistance,
154
+ this.minDistance,
155
+ this.maxDistance,
156
+ this.minFOV,
157
+ this.maxFOV
158
+ );
159
+ this.fov += ease(this.targetFOV, this.fov, 0.07);
160
+ this.camera.fov = this.fov;
161
+ this.camera.updateProjectionMatrix();
162
+ this.camera.updateMatrixWorld();
163
+ this.camera.position.set(x, clamp(y, 0.1, Infinity), z);
164
+ this.adjustCameraPosition();
165
+ this.camera.lookAt(this.target);
166
+ }
167
+ }
168
+ };
169
+
170
+ // src/character/Character.ts
171
+ import { Color as Color2, Vector3 as Vector34 } from "three";
172
+
173
+ // src/character/CharacterModel.ts
174
+ import {
175
+ AnimationClip as AnimationClip2,
176
+ AnimationMixer,
177
+ LoopRepeat,
178
+ MeshStandardMaterial,
179
+ Object3D as Object3D2
180
+ } from "three";
181
+
182
+ // src/character/CharacterMaterial.ts
183
+ import { Color, MeshPhysicalMaterial, UniformsUtils } from "three";
184
+
185
+ // src/rendering/shaders/bayer-dither.ts
186
+ var bayerDither = (
187
+ /* glsl */
188
+ `
189
+ const mat4 bayertl = mat4(
190
+ 0.0 / 64.0, 32.0 / 64.0, 8.0 / 64.0, 40.0 / 64.0,
191
+ 48.0 / 64.0, 16.0 / 64.0, 56.0 / 64.0, 24.0 / 64.0,
192
+ 12.0 / 64.0, 44.0 / 64.0, 4.0 / 64.0, 36.0 / 64.0,
193
+ 60.0 / 64.0, 28.0 / 64.0, 52.0 / 64.0, 20.0 / 64.0
194
+ );
195
+
196
+ const mat4 bayertr = mat4(
197
+ 2.0 / 64.0, 34.0 / 64.0, 10.0 / 64.0, 42.0 / 64.0,
198
+ 50.0 / 64.0, 18.0 / 64.0, 58.0 / 64.0, 26.0 / 64.0,
199
+ 14.0 / 64.0, 46.0 / 64.0, 6.0 / 64.0, 38.0 / 64.0,
200
+ 62.0 / 64.0, 30.0 / 64.0, 54.0 / 64.0, 22.0 / 64.0
201
+ );
202
+
203
+ const mat4 bayerbl = mat4(
204
+ 3.0 / 64.0, 35.0 / 64.0, 11.0 / 64.0, 43.0 / 64.0,
205
+ 51.0 / 64.0, 19.0 / 64.0, 59.0 / 64.0, 27.0 / 64.0,
206
+ 15.0 / 64.0, 47.0 / 64.0, 7.0 / 64.0, 39.0 / 64.0,
207
+ 63.0 / 64.0, 31.0 / 64.0, 55.0 / 64.0, 23.0 / 64.0
208
+ );
209
+
210
+ const mat4 bayerbr = mat4(
211
+ 1.0 / 64.0, 33.0 / 64.0, 9.0 / 64.0, 41.0 / 64.0,
212
+ 49.0 / 64.0, 17.0 / 64.0, 57.0 / 64.0, 25.0 / 64.0,
213
+ 13.0 / 64.0, 45.0 / 64.0, 5.0 / 64.0, 37.0 / 64.0,
214
+ 61.0 / 64.0, 29.0 / 64.0, 53.0 / 64.0, 21.0 / 64.0
215
+ );
216
+
217
+ float bayerDither(mat4 m, ivec2 p) {
218
+ if (p.y == 0) {
219
+ if (p.x == 0) { return m[0][0]; }
220
+ else if (p.x == 1) { return m[1][0]; }
221
+ else if (p.x == 2) { return m[2][0]; }
222
+ else { return m[3][0]; }
223
+ } else if (p.y == 1) {
224
+ if (p.x == 0) { return m[0][1]; }
225
+ else if (p.x == 1) { return m[1][1]; }
226
+ else if (p.x == 2) { return m[2][1]; }
227
+ else { return m[3][1]; }
228
+ } else if (p.y == 2) {
229
+ if (p.x == 0) { return m[0][1]; }
230
+ else if (p.x == 1) { return m[1][2]; }
231
+ else if (p.x == 2) { return m[2][2]; }
232
+ else { return m[3][2]; }
233
+ } else {
234
+ if (p.x == 0) { return m[0][3]; }
235
+ else if (p.x == 1) { return m[1][3]; }
236
+ else if (p.x == 2) { return m[2][3]; }
237
+ else { return m[3][3]; }
238
+ }
239
+ }
240
+ `
241
+ );
242
+
243
+ // src/rendering/shaders/shader-helpers.ts
244
+ function injectBeforeMain(shaderSource, codeToInject) {
245
+ return shaderSource.replace(
246
+ "void main() {",
247
+ `
248
+
249
+ ${codeToInject}
250
+
251
+ void main() {`
252
+ );
253
+ }
254
+ function injectInsideMain(shaderSource, codeToInject) {
255
+ return shaderSource.replace(
256
+ "void main() {",
257
+ `void main() {
258
+
259
+ ${codeToInject}
260
+
261
+ `
262
+ );
263
+ }
264
+ function injectBefore(shaderSource, before, codeToInject) {
265
+ return shaderSource.replace(
266
+ before,
267
+ `
268
+ ${codeToInject}
269
+
270
+ ${before}
271
+ `
272
+ );
273
+ }
274
+
275
+ // src/tweakpane/characterSettings.ts
276
+ var characterValues = {
277
+ material: {
278
+ transmission: 1,
279
+ metalness: 0.8,
280
+ roughness: 0.12,
281
+ ior: 1.5,
282
+ thickness: 0.1,
283
+ specularColor: { r: 1, g: 1, b: 1 },
284
+ specularIntensity: 0.1,
285
+ emissive: { r: 1, g: 1, b: 1 },
286
+ emissiveIntensity: 0.1,
287
+ envMapIntensity: 1,
288
+ sheenColor: { r: 1, g: 1, b: 1 },
289
+ sheen: 0.5,
290
+ clearcoat: 0,
291
+ clearcoatRoughness: 0
292
+ }
293
+ };
294
+ var characterOptions = {
295
+ material: {
296
+ transmission: { min: 0.01, max: 3, step: 0.01 },
297
+ metalness: { min: 0, max: 1, step: 0.01 },
298
+ roughness: { min: 0, max: 1, step: 0.01 },
299
+ ior: { min: 0, max: 5, step: 0.01 },
300
+ thickness: { min: 0, max: 1, step: 0.01 },
301
+ specularIntensity: { min: 0, max: 1, step: 0.01 },
302
+ emissiveIntensity: { min: 0, max: 1, step: 0.01 },
303
+ envMapIntensity: { min: 0, max: 1, step: 0.01 },
304
+ sheen: { min: 0, max: 1, step: 0.01 },
305
+ clearcoat: { min: 0, max: 1, step: 0.01 },
306
+ clearcoatRoughness: { min: 0, max: 1, step: 0.01 }
307
+ }
308
+ };
309
+
310
+ // src/character/CharacterMaterial.ts
311
+ var CharacterMaterial = class extends MeshPhysicalMaterial {
312
+ constructor() {
313
+ super();
314
+ __publicField(this, "uniforms", {});
315
+ __publicField(this, "colorsCube216", []);
316
+ this.color = new Color(16777215);
317
+ this.transmission = characterValues.material.transmission;
318
+ this.metalness = characterValues.material.metalness;
319
+ this.roughness = characterValues.material.roughness;
320
+ this.ior = characterValues.material.ior;
321
+ this.thickness = characterValues.material.thickness;
322
+ this.specularColor = new Color().setRGB(
323
+ characterValues.material.specularColor.r,
324
+ characterValues.material.specularColor.g,
325
+ characterValues.material.specularColor.b
326
+ );
327
+ this.specularIntensity = characterValues.material.specularIntensity;
328
+ this.emissive = new Color().setRGB(
329
+ characterValues.material.emissive.r,
330
+ characterValues.material.emissive.g,
331
+ characterValues.material.emissive.b
332
+ );
333
+ this.emissiveIntensity = characterValues.material.emissiveIntensity;
334
+ this.envMapIntensity = characterValues.material.envMapIntensity;
335
+ this.sheenColor = new Color().setRGB(
336
+ characterValues.material.sheenColor.r,
337
+ characterValues.material.sheenColor.g,
338
+ characterValues.material.sheenColor.b
339
+ );
340
+ this.sheen = characterValues.material.sheen;
341
+ this.clearcoat = characterValues.material.clearcoat;
342
+ this.clearcoatRoughness = characterValues.material.clearcoatRoughness;
343
+ this.onBeforeCompile = (shader) => {
344
+ this.uniforms = UniformsUtils.clone(shader.uniforms);
345
+ this.uniforms.nearClip = { value: 0.01 };
346
+ this.uniforms.farClip = { value: 1e3 };
347
+ this.uniforms.ditheringNear = { value: 0.25 };
348
+ this.uniforms.ditheringRange = { value: 0.5 };
349
+ this.uniforms.time = { value: 0 };
350
+ this.uniforms.diffuseRandomColor = { value: new Color() };
351
+ shader.uniforms = this.uniforms;
352
+ shader.vertexShader = injectBeforeMain(shader.vertexShader, "varying vec2 vUv;");
353
+ shader.vertexShader = injectInsideMain(shader.vertexShader, "vUv = uv;");
354
+ shader.fragmentShader = injectBeforeMain(
355
+ shader.fragmentShader,
356
+ /* glsl */
357
+ `
358
+ //#define showPattern
359
+ varying vec2 vUv;
360
+ uniform float nearClip;
361
+ uniform float farClip;
362
+ uniform float ditheringNear;
363
+ uniform float ditheringRange;
364
+ uniform float time;
365
+ uniform vec3 diffuseRandomColor;
366
+ ${bayerDither}
367
+
368
+ #ifdef showPattern
369
+ vec2 rand2(vec2 p) {
370
+ return fract(vec2(sin(p.x * 591.32 + p.y * 154.077), cos(p.x * 391.32 + p.y * 49.077)));
371
+ }
372
+ float voronoi(in vec2 x) {
373
+ vec2 p = floor(x);
374
+ vec2 f = fract(x);
375
+ float minDistance = 1.0;
376
+ for(int j = -1; j <= 1; j ++)
377
+ for(int i = -1; i <= 1; i ++) {
378
+ vec2 b = vec2(i, j);
379
+ vec2 rand = 0.5 + 0.5 * sin(time * 1.5 + 12.0 * rand2(p + b));
380
+ vec2 r = vec2(b) - f + rand;
381
+ minDistance = min(minDistance, length(r));
382
+ }
383
+ return minDistance;
384
+ }
385
+ #endif
386
+ `
387
+ );
388
+ shader.fragmentShader = injectBefore(
389
+ shader.fragmentShader,
390
+ "#include <output_fragment>",
391
+ /* glsl */
392
+ `
393
+ float distance = length(vWorldPosition - cameraPosition);
394
+ float normalizedDistance = (distance - nearClip) / (farClip - nearClip);
395
+ ivec2 p = ivec2(mod(gl_FragCoord.xy, 8.0));
396
+ float d = 0.0;
397
+ if (p.x <= 3 && p.y <= 3) {
398
+ d = bayerDither(bayertl, p);
399
+ } else if (p.x > 3 && p.y <= 3) {
400
+ d = bayerDither(bayertr, p - ivec2(4, 0));
401
+ } else if (p.x <= 3 && p.y > 3) {
402
+ d = bayerDither(bayerbl, p - ivec2(0, 4));
403
+ } else if (p.x > 3 && p.y > 3) {
404
+ d = bayerDither(bayerbr, p - ivec2(4, 4));
405
+ }
406
+ if (distance <= ditheringNear + d * ditheringRange) discard;
407
+
408
+ vec2 uv = vUv;
409
+ float s = clamp(0.35 + 0.35 * sin(5.0 * -time + vUv.y * 600.0), 0.0, 1.0);
410
+ float scanLines = pow(s, 1.33);
411
+
412
+ outgoingLight *= diffuseRandomColor;
413
+
414
+ #ifdef showPattern
415
+ float val = pow(voronoi(uv * 8.0) * 1.2, 0.5);
416
+ float thickness = 1.0 / 500.0;
417
+ vec2 g = step(mod(uv, 0.015), vec2(thickness));
418
+ float a = 1.0 - clamp(val * (g.x + g.y), 0.0, 1.0);
419
+ vec3 grid = vec3(smoothstep(0.01, 0.0, a) * 1.15) * diffuseRandomColor;
420
+ outgoingLight += grid;
421
+ #endif
422
+
423
+ outgoingLight += smoothstep(0.1, 0.0, scanLines) * 0.1;
424
+ `
425
+ );
426
+ };
427
+ this.generateColorCube();
428
+ }
429
+ generateColorCube() {
430
+ const saturation = 0.4;
431
+ const lightness = 0.7;
432
+ const goldenRatioConjugate = 0.618033988749895;
433
+ let hue = 0;
434
+ for (let i = 0; i < 216; i++) {
435
+ const color = new Color();
436
+ color.setHSL(hue, saturation, lightness);
437
+ this.colorsCube216.push(color);
438
+ hue = (hue + goldenRatioConjugate) % 1;
439
+ }
440
+ }
441
+ update() {
442
+ this.transmission = characterValues.material.transmission;
443
+ this.metalness = characterValues.material.metalness;
444
+ this.roughness = characterValues.material.roughness;
445
+ this.ior = characterValues.material.ior;
446
+ this.thickness = characterValues.material.thickness;
447
+ this.specularColor = new Color().setRGB(
448
+ characterValues.material.specularColor.r,
449
+ characterValues.material.specularColor.g,
450
+ characterValues.material.specularColor.b
451
+ );
452
+ this.specularIntensity = characterValues.material.specularIntensity;
453
+ this.emissive = new Color().setRGB(
454
+ characterValues.material.emissive.r,
455
+ characterValues.material.emissive.g,
456
+ characterValues.material.emissive.b
457
+ );
458
+ this.emissiveIntensity = characterValues.material.emissiveIntensity;
459
+ this.envMapIntensity = characterValues.material.envMapIntensity;
460
+ this.sheenColor = new Color().setRGB(
461
+ characterValues.material.sheenColor.r,
462
+ characterValues.material.sheenColor.g,
463
+ characterValues.material.sheenColor.b
464
+ );
465
+ this.sheen = characterValues.material.sheen;
466
+ this.clearcoat = characterValues.material.clearcoat;
467
+ this.clearcoatRoughness = characterValues.material.clearcoatRoughness;
468
+ }
469
+ };
470
+
471
+ // src/character/CharacterState.ts
472
+ var AnimationState = /* @__PURE__ */ ((AnimationState2) => {
473
+ AnimationState2[AnimationState2["idle"] = 0] = "idle";
474
+ AnimationState2[AnimationState2["walking"] = 1] = "walking";
475
+ AnimationState2[AnimationState2["running"] = 2] = "running";
476
+ AnimationState2[AnimationState2["jumpToAir"] = 3] = "jumpToAir";
477
+ AnimationState2[AnimationState2["air"] = 4] = "air";
478
+ AnimationState2[AnimationState2["airToGround"] = 5] = "airToGround";
479
+ return AnimationState2;
480
+ })(AnimationState || {});
481
+
482
+ // src/character/ModelLoader.ts
483
+ import { LoadingManager } from "three";
484
+ import { GLTFLoader as ThreeGLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
485
+ var CachedGLTFLoader = class extends ThreeGLTFLoader {
486
+ constructor(manager) {
487
+ super(manager);
488
+ __publicField(this, "blobCache");
489
+ this.blobCache = /* @__PURE__ */ new Map();
490
+ }
491
+ setBlobUrl(originalUrl, blobUrl) {
492
+ this.blobCache.set(originalUrl, blobUrl);
493
+ }
494
+ getBlobUrl(originalUrl) {
495
+ return this.blobCache.get(originalUrl);
496
+ }
497
+ load(url, onLoad, onProgress, onError) {
498
+ const blobUrl = this.getBlobUrl(url);
499
+ if (blobUrl) {
500
+ console.log(`Loading cached ${url.split("/").pop()}`);
501
+ super.load(blobUrl, onLoad, onProgress, onError);
502
+ } else {
503
+ super.load(url, onLoad, onProgress, onError);
504
+ }
505
+ }
506
+ };
507
+ var LRUCache = class {
508
+ constructor(maxSize = 100) {
509
+ __publicField(this, "maxSize");
510
+ __publicField(this, "cache");
511
+ this.maxSize = maxSize;
512
+ this.cache = /* @__PURE__ */ new Map();
513
+ }
514
+ get(key) {
515
+ const item = this.cache.get(key);
516
+ if (item) {
517
+ this.cache.delete(key);
518
+ this.cache.set(key, item);
519
+ }
520
+ return item;
521
+ }
522
+ set(key, value) {
523
+ if (this.cache.size >= this.maxSize) {
524
+ const oldestKey = this.cache.keys().next().value;
525
+ this.cache.delete(oldestKey);
526
+ }
527
+ this.cache.set(key, value);
528
+ }
529
+ };
530
+ var _ModelLoader = class {
531
+ constructor(maxCacheSize = 100) {
532
+ __publicField(this, "loadingManager");
533
+ __publicField(this, "gltfLoader");
534
+ __publicField(this, "modelCache");
535
+ __publicField(this, "ongoingLoads", /* @__PURE__ */ new Map());
536
+ this.loadingManager = new LoadingManager();
537
+ this.gltfLoader = new CachedGLTFLoader(this.loadingManager);
538
+ this.modelCache = new LRUCache(maxCacheSize);
539
+ }
540
+ /* TODO: decide between below lazy initialization or eager on this file's bottom export */
541
+ static getInstance() {
542
+ if (!_ModelLoader.instance) {
543
+ _ModelLoader.instance = new _ModelLoader();
544
+ }
545
+ return _ModelLoader.instance;
546
+ }
547
+ async load(fileUrl, fileType) {
548
+ const cachedModel = this.modelCache.get(fileUrl);
549
+ if (cachedModel) {
550
+ const blobURL = URL.createObjectURL(cachedModel.blob);
551
+ this.gltfLoader.setBlobUrl(fileUrl, blobURL);
552
+ return this.loadFromUrl(fileUrl, fileType, cachedModel.originalExtension);
553
+ } else {
554
+ console.log(`Loading ${fileUrl} from server`);
555
+ const ongoingLoad = this.ongoingLoads.get(fileUrl);
556
+ if (ongoingLoad)
557
+ return ongoingLoad;
558
+ const loadPromise = fetch(fileUrl).then((response) => response.blob()).then((blob) => {
559
+ const originalExtension = fileUrl.split(".").pop() || "";
560
+ this.modelCache.set(fileUrl, { blob, originalExtension });
561
+ const blobURL = URL.createObjectURL(blob);
562
+ this.ongoingLoads.delete(fileUrl);
563
+ return this.loadFromUrl(blobURL, fileType, originalExtension);
564
+ });
565
+ this.ongoingLoads.set(fileUrl, loadPromise);
566
+ return loadPromise;
567
+ }
568
+ }
569
+ async loadFromUrl(url, fileType, extension) {
570
+ if (["gltf", "glb"].includes(extension)) {
571
+ return new Promise((resolve, reject) => {
572
+ this.gltfLoader.load(
573
+ url,
574
+ (object) => {
575
+ if (fileType === "model") {
576
+ resolve(object.scene);
577
+ } else if (fileType === "animation") {
578
+ resolve(object.animations[0]);
579
+ } else {
580
+ const error = `Trying to load unknown ${fileType} type of element from file ${url}`;
581
+ console.error(error);
582
+ reject(error);
583
+ }
584
+ },
585
+ void 0,
586
+ (error) => {
587
+ console.error(`Error loading GL(B|TF) from ${url}: ${error}`);
588
+ reject(error);
589
+ }
590
+ );
591
+ });
592
+ } else {
593
+ console.error(`Error: can't recognize ${url} extension: ${extension}`);
594
+ }
595
+ }
596
+ };
597
+ var ModelLoader = _ModelLoader;
598
+ __publicField(ModelLoader, "instance", null);
599
+ var MODEL_LOADER = ModelLoader.getInstance();
600
+ var ModelLoader_default = MODEL_LOADER;
601
+
602
+ // src/character/CharacterModel.ts
603
+ var CharacterModel = class {
604
+ constructor(characterDescription) {
605
+ this.characterDescription = characterDescription;
606
+ /* TODO: pick between below eager instantiation or ModelLoader.getInstance() lazy one */
607
+ __publicField(this, "modelLoader", ModelLoader_default);
608
+ __publicField(this, "mesh", null);
609
+ __publicField(this, "material", new CharacterMaterial());
610
+ __publicField(this, "animations", {});
611
+ __publicField(this, "animationMixer", null);
612
+ __publicField(this, "currentAnimation", 0 /* idle */);
613
+ }
614
+ async init() {
615
+ await this.loadMainMesh();
616
+ await this.setAnimationFromFile(
617
+ this.characterDescription.idleAnimationFileUrl,
618
+ 0 /* idle */
619
+ );
620
+ await this.setAnimationFromFile(
621
+ this.characterDescription.jogAnimationFileUrl,
622
+ 1 /* walking */
623
+ );
624
+ await this.setAnimationFromFile(
625
+ this.characterDescription.sprintAnimationFileUrl,
626
+ 2 /* running */
627
+ );
628
+ await this.setAnimationFromFile(
629
+ this.characterDescription.airAnimationFileUrl,
630
+ 4 /* air */
631
+ );
632
+ this.applyMaterialToAllSkinnedMeshes(this.material);
633
+ }
634
+ updateAnimation(targetAnimation, deltaTime) {
635
+ if (this.currentAnimation !== targetAnimation) {
636
+ this.transitionToAnimation(targetAnimation);
637
+ }
638
+ this.animationMixer?.update(deltaTime);
639
+ }
640
+ hideMaterialByMeshName(meshName) {
641
+ if (!this.mesh)
642
+ return;
643
+ this.mesh.traverse((child) => {
644
+ if (child.type === "SkinnedMesh" && child.name === meshName) {
645
+ child.material = new MeshStandardMaterial({
646
+ color: 16711680,
647
+ transparent: true,
648
+ opacity: 0
649
+ });
650
+ }
651
+ });
652
+ }
653
+ setShadows(mesh, castShadow = true, receiveShadow = true) {
654
+ mesh.traverse((child) => {
655
+ if (child.type === "SkinnedMesh") {
656
+ child.castShadow = castShadow;
657
+ child.receiveShadow = receiveShadow;
658
+ }
659
+ });
660
+ }
661
+ applyMaterialToAllSkinnedMeshes(material) {
662
+ if (!this.mesh)
663
+ return;
664
+ this.mesh.traverse((child) => {
665
+ if (child.type === "SkinnedMesh") {
666
+ child.material = material;
667
+ }
668
+ });
669
+ }
670
+ initAnimationMixer() {
671
+ if (this.animationMixer !== null || this.mesh === null)
672
+ return;
673
+ this.animationMixer = new AnimationMixer(this.mesh);
674
+ }
675
+ async loadMainMesh() {
676
+ const mainMeshUrl = this.characterDescription.meshFileUrl;
677
+ const scale = this.characterDescription.modelScale;
678
+ const extension = mainMeshUrl.split(".").pop();
679
+ const name = mainMeshUrl.split("/").pop().replace(`.${extension}`, "");
680
+ const mainMesh = await this.modelLoader.load(mainMeshUrl, "model");
681
+ if (typeof mainMesh !== "undefined") {
682
+ this.mesh = new Object3D2();
683
+ const model = mainMesh;
684
+ model.position.set(0, -0.4, 0);
685
+ this.mesh.add(model);
686
+ this.mesh.name = name;
687
+ this.mesh.scale.set(scale, scale, scale);
688
+ this.setShadows(this.mesh);
689
+ }
690
+ }
691
+ async setAnimationFromFile(animationFileUrl, animationType) {
692
+ return new Promise(async (resolve, reject) => {
693
+ this.initAnimationMixer();
694
+ const animation = await this.modelLoader.load(animationFileUrl, "animation");
695
+ if (typeof animation !== "undefined" && animation instanceof AnimationClip2) {
696
+ this.animations[animationType] = this.animationMixer.clipAction(animation);
697
+ this.animations[animationType].stop();
698
+ if (animationType === 0 /* idle */) {
699
+ this.animations[animationType].play();
700
+ }
701
+ resolve();
702
+ } else {
703
+ reject(`failed to load ${animationType} from ${animationFileUrl}`);
704
+ }
705
+ });
706
+ }
707
+ transitionToAnimation(targetAnimation, transitionDuration = 0.15) {
708
+ if (!this.mesh || this.currentAnimation === null)
709
+ return;
710
+ const currentAction = this.animations[this.currentAnimation];
711
+ const targetAction = this.animations[targetAnimation];
712
+ if (!targetAction)
713
+ return;
714
+ if (currentAction) {
715
+ currentAction.enabled = true;
716
+ currentAction.fadeOut(transitionDuration);
717
+ }
718
+ if (!targetAction.isRunning())
719
+ targetAction.play();
720
+ targetAction.setLoop(LoopRepeat, Infinity);
721
+ targetAction.enabled = true;
722
+ targetAction.fadeIn(transitionDuration);
723
+ this.currentAnimation = targetAnimation;
724
+ }
725
+ };
726
+
727
+ // src/character/LocalController.ts
728
+ import { Box3, Line3, Matrix4, Quaternion, Raycaster as Raycaster2, Vector3 as Vector33 } from "three";
729
+ var LocalController = class {
730
+ constructor(model, id, collisionsManager, keyInputManager, cameraManager, timeManager) {
731
+ this.model = model;
732
+ this.id = id;
733
+ this.collisionsManager = collisionsManager;
734
+ this.keyInputManager = keyInputManager;
735
+ this.cameraManager = cameraManager;
736
+ this.timeManager = timeManager;
737
+ __publicField(this, "collisionDetectionSteps", 15);
738
+ __publicField(this, "capsuleInfo", {
739
+ radius: 0.4,
740
+ segment: new Line3(new Vector33(), new Vector33(0, 1.05, 0))
741
+ });
742
+ __publicField(this, "maxWalkSpeed", 6);
743
+ __publicField(this, "maxRunSpeed", 8.5);
744
+ __publicField(this, "gravity", -42);
745
+ __publicField(this, "jumpForce", 16);
746
+ __publicField(this, "coyoteTimeThreshold", 290);
747
+ __publicField(this, "coyoteTime", false);
748
+ __publicField(this, "canJump", true);
749
+ __publicField(this, "characterOnGround", false);
750
+ __publicField(this, "characterWasOnGround", false);
751
+ __publicField(this, "characterAirborneSince", 0);
752
+ __publicField(this, "currentHeight", 0);
753
+ __publicField(this, "characterVelocity", new Vector33());
754
+ __publicField(this, "vectorUp", new Vector33(0, 1, 0));
755
+ __publicField(this, "vectorDown", new Vector33(0, -1, 0));
756
+ __publicField(this, "rotationOffset", 0);
757
+ __publicField(this, "azimuthalAngle", 0);
758
+ __publicField(this, "tempBox", new Box3());
759
+ __publicField(this, "tempMatrix", new Matrix4());
760
+ __publicField(this, "tempSegment", new Line3());
761
+ __publicField(this, "tempVector", new Vector33());
762
+ __publicField(this, "tempVector2", new Vector33());
763
+ __publicField(this, "rayCaster", new Raycaster2());
764
+ __publicField(this, "forward");
765
+ __publicField(this, "backward");
766
+ __publicField(this, "left");
767
+ __publicField(this, "right");
768
+ __publicField(this, "run");
769
+ __publicField(this, "jump");
770
+ __publicField(this, "anyDirection");
771
+ __publicField(this, "conflictingDirections");
772
+ __publicField(this, "thirdPersonCamera", null);
773
+ __publicField(this, "speed", 0);
774
+ __publicField(this, "targetSpeed", 0);
775
+ __publicField(this, "networkState", {
776
+ id: 0,
777
+ position: { x: 0, y: 0, z: 0 },
778
+ rotation: { quaternionY: 0, quaternionW: 0 },
779
+ state: 0 /* idle */
780
+ });
781
+ setInterval(() => this.update.bind(this), 3e3);
782
+ }
783
+ update() {
784
+ if (!this.model?.mesh || !this.model?.animationMixer)
785
+ return;
786
+ if (!this.thirdPersonCamera)
787
+ this.thirdPersonCamera = this.cameraManager.camera;
788
+ const { forward, backward, left, right, run, jump, anyDirection, conflictingDirection } = this.keyInputManager;
789
+ this.forward = forward;
790
+ this.backward = backward;
791
+ this.left = left;
792
+ this.right = right;
793
+ this.run = run;
794
+ this.jump = jump;
795
+ this.anyDirection = anyDirection;
796
+ this.conflictingDirections = conflictingDirection;
797
+ this.targetSpeed = this.run ? this.maxRunSpeed : this.maxWalkSpeed;
798
+ this.speed += ease(this.targetSpeed, this.speed, 0.07);
799
+ this.rayCaster.set(this.model.mesh.position, this.vectorDown);
800
+ const minimumDistance = this.collisionsManager.raycastFirstDistance(this.rayCaster.ray);
801
+ if (minimumDistance !== null) {
802
+ this.currentHeight = minimumDistance;
803
+ }
804
+ if (anyDirection || !this.characterOnGround) {
805
+ const targetAnimation = this.getTargetAnimation();
806
+ this.model.updateAnimation(targetAnimation, this.timeManager.deltaTime);
807
+ } else {
808
+ this.model.updateAnimation(0 /* idle */, this.timeManager.deltaTime);
809
+ }
810
+ if (this.anyDirection)
811
+ this.updateRotation();
812
+ for (let i = 0; i < this.collisionDetectionSteps; i++) {
813
+ this.updatePosition(this.timeManager.deltaTime / this.collisionDetectionSteps, i);
814
+ }
815
+ if (this.model.mesh.position.y < 0)
816
+ this.resetPosition();
817
+ this.updateNetworkState();
818
+ }
819
+ getTargetAnimation() {
820
+ if (!this.model.mesh)
821
+ return 0 /* idle */;
822
+ if (this.conflictingDirections)
823
+ return 0 /* idle */;
824
+ const jumpHeight = this.characterVelocity.y > 0 ? 0.2 : 1.8;
825
+ if (this.currentHeight > jumpHeight && !this.characterOnGround) {
826
+ return 4 /* air */;
827
+ }
828
+ return this.run && this.anyDirection ? 2 /* running */ : this.anyDirection ? 1 /* walking */ : 0 /* idle */;
829
+ }
830
+ updateRotationOffset() {
831
+ if (this.conflictingDirections)
832
+ return;
833
+ if (this.forward) {
834
+ this.rotationOffset = Math.PI;
835
+ if (this.left)
836
+ this.rotationOffset = Math.PI + Math.PI / 4;
837
+ if (this.right)
838
+ this.rotationOffset = Math.PI - Math.PI / 4;
839
+ } else if (this.backward) {
840
+ this.rotationOffset = Math.PI * 2;
841
+ if (this.left)
842
+ this.rotationOffset = -Math.PI * 2 - Math.PI / 4;
843
+ if (this.right)
844
+ this.rotationOffset = Math.PI * 2 + Math.PI / 4;
845
+ } else if (this.left) {
846
+ this.rotationOffset = Math.PI * -0.5;
847
+ } else if (this.right) {
848
+ this.rotationOffset = Math.PI * 0.5;
849
+ }
850
+ }
851
+ updateAzimuthalAngle() {
852
+ if (!this.thirdPersonCamera || !this.model?.mesh)
853
+ return;
854
+ const camToModelDistance = this.thirdPersonCamera.position.distanceTo(this.model.mesh.position);
855
+ const isCameraFirstPerson = camToModelDistance < 2;
856
+ if (isCameraFirstPerson) {
857
+ const cameraForward = new Vector33(0, 0, 1).applyQuaternion(this.thirdPersonCamera.quaternion);
858
+ this.azimuthalAngle = Math.atan2(cameraForward.x, cameraForward.z);
859
+ } else {
860
+ this.azimuthalAngle = Math.atan2(
861
+ this.thirdPersonCamera.position.x - this.model.mesh.position.x,
862
+ this.thirdPersonCamera.position.z - this.model.mesh.position.z
863
+ );
864
+ }
865
+ }
866
+ updateRotation() {
867
+ if (!this.thirdPersonCamera || !this.model?.mesh)
868
+ return;
869
+ this.updateRotationOffset();
870
+ this.updateAzimuthalAngle();
871
+ const rotationQuaternion = new Quaternion();
872
+ rotationQuaternion.setFromAxisAngle(this.vectorUp, this.azimuthalAngle + this.rotationOffset);
873
+ this.model.mesh.quaternion.rotateTowards(rotationQuaternion, 0.07);
874
+ }
875
+ addScaledVectorToCharacter(deltaTime) {
876
+ if (!this.model?.mesh)
877
+ return;
878
+ this.model.mesh.position.addScaledVector(this.tempVector, this.speed * deltaTime);
879
+ }
880
+ updatePosition(deltaTime, _iter) {
881
+ if (!this.model?.mesh)
882
+ return;
883
+ if (this.characterOnGround) {
884
+ if (!this.jump)
885
+ this.canJump = true;
886
+ if (this.jump && this.canJump) {
887
+ this.characterVelocity.y += this.jumpForce;
888
+ this.canJump = false;
889
+ } else {
890
+ this.characterVelocity.y = deltaTime * this.gravity;
891
+ }
892
+ } else if (this.jump && this.coyoteTime) {
893
+ console.log("coyoteJump");
894
+ this.characterVelocity.y = this.jumpForce;
895
+ this.canJump = false;
896
+ } else {
897
+ this.characterVelocity.y += deltaTime * this.gravity;
898
+ this.canJump = false;
899
+ }
900
+ this.model.mesh.position.addScaledVector(this.characterVelocity, deltaTime);
901
+ this.tempVector.set(0, 0, 0);
902
+ if (this.forward) {
903
+ const forward = new Vector33(0, 0, -1).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
904
+ this.tempVector.add(forward);
905
+ }
906
+ if (this.backward) {
907
+ const backward = new Vector33(0, 0, 1).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
908
+ this.tempVector.add(backward);
909
+ }
910
+ if (this.left) {
911
+ const left = new Vector33(-1, 0, 0).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
912
+ this.tempVector.add(left);
913
+ }
914
+ if (this.right) {
915
+ const right = new Vector33(1, 0, 0).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
916
+ this.tempVector.add(right);
917
+ }
918
+ if (this.tempVector.length() > 0) {
919
+ this.tempVector.normalize();
920
+ this.addScaledVectorToCharacter(deltaTime);
921
+ }
922
+ this.model.mesh.updateMatrixWorld();
923
+ this.tempBox.makeEmpty();
924
+ this.tempSegment.copy(this.capsuleInfo.segment);
925
+ this.tempSegment.start.applyMatrix4(this.model.mesh.matrixWorld).applyMatrix4(this.tempMatrix);
926
+ this.tempSegment.end.applyMatrix4(this.model.mesh.matrixWorld).applyMatrix4(this.tempMatrix);
927
+ this.tempBox.expandByPoint(this.tempSegment.start);
928
+ this.tempBox.expandByPoint(this.tempSegment.end);
929
+ this.tempBox.min.subScalar(this.capsuleInfo.radius);
930
+ this.tempBox.max.addScalar(this.capsuleInfo.radius);
931
+ this.collisionsManager.applyColliders(this.tempSegment, this.capsuleInfo.radius, this.tempBox);
932
+ const newPosition = this.tempVector;
933
+ newPosition.copy(this.tempSegment.start);
934
+ const deltaVector = this.tempVector2;
935
+ deltaVector.subVectors(newPosition, this.model.mesh.position);
936
+ const offset = Math.max(0, deltaVector.length() - 1e-5);
937
+ deltaVector.normalize().multiplyScalar(offset);
938
+ this.model.mesh.position.add(deltaVector);
939
+ this.characterOnGround = deltaVector.y > Math.abs(deltaTime * this.characterVelocity.y * 0.25);
940
+ if (this.characterWasOnGround && !this.characterOnGround) {
941
+ this.characterAirborneSince = Date.now();
942
+ }
943
+ this.coyoteTime = this.characterVelocity.y < 0 && this.characterOnGround === false && Date.now() - this.characterAirborneSince < this.coyoteTimeThreshold;
944
+ this.characterWasOnGround = this.characterOnGround;
945
+ if (this.characterOnGround) {
946
+ this.characterVelocity.set(0, 0, 0);
947
+ } else {
948
+ deltaVector.normalize();
949
+ this.characterVelocity.addScaledVector(deltaVector, -deltaVector.dot(this.characterVelocity));
950
+ }
951
+ }
952
+ updateNetworkState() {
953
+ if (!this.model?.mesh)
954
+ return;
955
+ const characterQuaternion = this.model.mesh.getWorldQuaternion(new Quaternion());
956
+ const positionUpdate = new Vector33(
957
+ this.model.mesh.position.x,
958
+ this.model.mesh.position.y,
959
+ this.model.mesh.position.z
960
+ );
961
+ this.networkState = {
962
+ id: this.id,
963
+ position: positionUpdate,
964
+ rotation: { quaternionY: characterQuaternion?.y, quaternionW: characterQuaternion?.w },
965
+ state: this.model.currentAnimation
966
+ };
967
+ }
968
+ resetPosition() {
969
+ if (!this.model?.mesh)
970
+ return;
971
+ this.characterVelocity.y = 0;
972
+ this.model.mesh.position.y = 3;
973
+ this.characterOnGround = false;
974
+ }
975
+ };
976
+
977
+ // src/character/Character.ts
978
+ var Character = class {
979
+ constructor(characterDescription, id, isLocal, modelLoadedCallback, collisionsManager, keyInputManager, cameraManager, timeManager) {
980
+ this.characterDescription = characterDescription;
981
+ this.id = id;
982
+ this.isLocal = isLocal;
983
+ this.modelLoadedCallback = modelLoadedCallback;
984
+ this.collisionsManager = collisionsManager;
985
+ this.keyInputManager = keyInputManager;
986
+ this.cameraManager = cameraManager;
987
+ this.timeManager = timeManager;
988
+ __publicField(this, "controller", null);
989
+ __publicField(this, "name", null);
990
+ __publicField(this, "model", null);
991
+ __publicField(this, "color", new Color2());
992
+ __publicField(this, "position", new Vector34());
993
+ this.load();
994
+ }
995
+ async load() {
996
+ this.model = new CharacterModel(this.characterDescription);
997
+ await this.model.init();
998
+ this.color = this.model.material.colorsCube216[this.id];
999
+ if (this.isLocal) {
1000
+ this.controller = new LocalController(
1001
+ this.model,
1002
+ this.id,
1003
+ this.collisionsManager,
1004
+ this.keyInputManager,
1005
+ this.cameraManager,
1006
+ this.timeManager
1007
+ );
1008
+ }
1009
+ this.modelLoadedCallback();
1010
+ }
1011
+ update(time) {
1012
+ if (!this.model)
1013
+ return;
1014
+ this.model.mesh.getWorldPosition(this.position);
1015
+ if (typeof this.model.material.uniforms.time !== "undefined") {
1016
+ this.model.material.uniforms.time.value = time;
1017
+ this.model.material.uniforms.diffuseRandomColor.value = this.color;
1018
+ this.model.material.update();
1019
+ }
1020
+ }
1021
+ };
1022
+
1023
+ // src/character/CharacterManager.ts
1024
+ import { Group, Vector3 as Vector36 } from "three";
1025
+
1026
+ // src/character/RemoteController.ts
1027
+ import {
1028
+ AnimationMixer as AnimationMixer2,
1029
+ Object3D as Object3D3,
1030
+ Quaternion as Quaternion2,
1031
+ Vector3 as Vector35
1032
+ } from "three";
1033
+ var RemoteController = class {
1034
+ constructor(character, id) {
1035
+ this.character = character;
1036
+ this.id = id;
1037
+ __publicField(this, "modelLoader", ModelLoader_default);
1038
+ __publicField(this, "characterModel", null);
1039
+ __publicField(this, "animationMixer", new AnimationMixer2(new Object3D3()));
1040
+ __publicField(this, "animations", /* @__PURE__ */ new Map());
1041
+ __publicField(this, "currentAnimation", 0 /* idle */);
1042
+ __publicField(this, "networkState", {
1043
+ id: 0,
1044
+ position: { x: 0, y: 0, z: 0 },
1045
+ rotation: { quaternionY: 0, quaternionW: 0 },
1046
+ state: this.currentAnimation
1047
+ });
1048
+ this.characterModel = this.character.model.mesh;
1049
+ this.characterModel.updateMatrixWorld();
1050
+ this.animationMixer = new AnimationMixer2(this.characterModel);
1051
+ }
1052
+ update(clientUpdate, time, deltaTime) {
1053
+ if (!this.character)
1054
+ return;
1055
+ this.character.update(time);
1056
+ this.updateFromNetwork(clientUpdate);
1057
+ this.animationMixer.update(deltaTime);
1058
+ }
1059
+ async setAnimationFromFile(animationType, fileName) {
1060
+ const animation = await this.modelLoader.load(fileName, "animation");
1061
+ const animationAction = this.animationMixer.clipAction(animation);
1062
+ this.animations.set(animationType, animationAction);
1063
+ if (animationType === 0 /* idle */) {
1064
+ animationAction.play();
1065
+ }
1066
+ }
1067
+ transitionToAnimation(targetAnimation, transitionDuration = 0.15) {
1068
+ if (this.currentAnimation === targetAnimation)
1069
+ return;
1070
+ const currentAction = this.animations.get(this.currentAnimation);
1071
+ const targetAction = this.animations.get(targetAnimation);
1072
+ if (!targetAction)
1073
+ return;
1074
+ if (currentAction) {
1075
+ currentAction.enabled = true;
1076
+ targetAction.reset().setEffectiveTimeScale(1).setEffectiveWeight(1).fadeIn(transitionDuration).play();
1077
+ currentAction.crossFadeTo(targetAction, transitionDuration, true);
1078
+ } else {
1079
+ targetAction.play();
1080
+ }
1081
+ this.currentAnimation = targetAnimation;
1082
+ }
1083
+ updateFromNetwork(clientUpdate) {
1084
+ if (!this.characterModel)
1085
+ return;
1086
+ const { position, rotation, state } = clientUpdate;
1087
+ this.characterModel.position.lerp(new Vector35(position.x, position.y, position.z), 0.15);
1088
+ const rotationQuaternion = new Quaternion2(0, rotation.quaternionY, 0, rotation.quaternionW);
1089
+ this.characterModel.quaternion.slerp(rotationQuaternion, 0.6);
1090
+ if (state !== this.currentAnimation) {
1091
+ this.transitionToAnimation(state);
1092
+ }
1093
+ }
1094
+ };
1095
+
1096
+ // src/character/CharacterManager.ts
1097
+ function encodeCharacterAndCamera(character, camera) {
1098
+ return [
1099
+ ...character.position.toArray(),
1100
+ ...character.quaternion.toArray(),
1101
+ ...camera.position.toArray(),
1102
+ ...camera.quaternion.toArray()
1103
+ ].join(",");
1104
+ }
1105
+ function decodeCharacterAndCamera(hash, character, camera) {
1106
+ const values = hash.split(",").map(Number);
1107
+ character.position.fromArray(values.slice(0, 3));
1108
+ character.quaternion.fromArray(values.slice(3, 7));
1109
+ camera.position.fromArray(values.slice(7, 10));
1110
+ camera.quaternion.fromArray(values.slice(10, 14));
1111
+ }
1112
+ var CharacterManager = class {
1113
+ constructor(collisionsManager, cameraManager, timeManager, inputManager, clientStates, sendUpdate) {
1114
+ this.collisionsManager = collisionsManager;
1115
+ this.cameraManager = cameraManager;
1116
+ this.timeManager = timeManager;
1117
+ this.inputManager = inputManager;
1118
+ this.clientStates = clientStates;
1119
+ this.sendUpdate = sendUpdate;
1120
+ /*
1121
+ TODO - re-enable updating location hash when there is a solution that waits for models to load (currently if the
1122
+ character was standing on a model and the page is reloaded the character falls into the model before it loads and
1123
+ can be trapped).
1124
+ */
1125
+ __publicField(this, "updateLocationHash", false);
1126
+ __publicField(this, "loadingCharacters", /* @__PURE__ */ new Map());
1127
+ __publicField(this, "remoteCharacters", /* @__PURE__ */ new Map());
1128
+ __publicField(this, "remoteCharacterControllers", /* @__PURE__ */ new Map());
1129
+ __publicField(this, "characterDescription", null);
1130
+ __publicField(this, "character", null);
1131
+ __publicField(this, "cameraOffsetTarget", 0);
1132
+ __publicField(this, "cameraOffset", 0);
1133
+ __publicField(this, "group");
1134
+ this.group = new Group();
1135
+ setInterval(() => this.update.bind(this), 3e3);
1136
+ }
1137
+ /* TODO:
1138
+ 1) Separate this method into spawnLocalCharacter and spawnRemoteCharacter
1139
+ 2) Make this synchronous to avoid having loadingCharacters and instead manage
1140
+ the mesh loading async (would allow us to show a nameplate where a remote
1141
+ user is before the asset loads).
1142
+ */
1143
+ spawnCharacter(characterDescription, id, isLocal = false, spawnPosition = new Vector36()) {
1144
+ this.characterDescription = characterDescription;
1145
+ const characterLoadingPromise = new Promise((resolve) => {
1146
+ const character = new Character(
1147
+ characterDescription,
1148
+ id,
1149
+ isLocal,
1150
+ () => {
1151
+ if (window.location.hash && window.location.hash.length > 1) {
1152
+ decodeCharacterAndCamera(
1153
+ window.location.hash.substring(1),
1154
+ character.model.mesh,
1155
+ this.cameraManager.camera
1156
+ );
1157
+ } else {
1158
+ spawnPosition = getSpawnPositionInsideCircle(3, 30, id, 0.4);
1159
+ character.model.mesh.position.set(spawnPosition.x, spawnPosition.y, spawnPosition.z);
1160
+ character.model.mesh.updateMatrixWorld();
1161
+ this.sendUpdate({
1162
+ id,
1163
+ position: {
1164
+ x: spawnPosition.x,
1165
+ y: spawnPosition.y,
1166
+ z: spawnPosition.z
1167
+ },
1168
+ rotation: { quaternionY: 0, quaternionW: 0 },
1169
+ state: 0 /* idle */
1170
+ });
1171
+ }
1172
+ character.model.hideMaterialByMeshName("SK_Mannequin_2");
1173
+ if (!isLocal) {
1174
+ character.model?.mesh?.position.set(spawnPosition.x, spawnPosition.y, spawnPosition.z);
1175
+ character.model?.mesh?.updateMatrixWorld();
1176
+ character.position.set(spawnPosition.x, spawnPosition.y, spawnPosition.z);
1177
+ }
1178
+ this.group.add(character.model.mesh);
1179
+ if (isLocal) {
1180
+ this.character = character;
1181
+ } else {
1182
+ this.remoteCharacters.set(id, character);
1183
+ const remoteController = new RemoteController(character, id);
1184
+ remoteController.setAnimationFromFile(
1185
+ 0 /* idle */,
1186
+ characterDescription.idleAnimationFileUrl
1187
+ );
1188
+ remoteController.setAnimationFromFile(
1189
+ 1 /* walking */,
1190
+ characterDescription.jogAnimationFileUrl
1191
+ );
1192
+ remoteController.setAnimationFromFile(
1193
+ 2 /* running */,
1194
+ characterDescription.sprintAnimationFileUrl
1195
+ );
1196
+ remoteController.setAnimationFromFile(
1197
+ 4 /* air */,
1198
+ characterDescription.airAnimationFileUrl
1199
+ );
1200
+ remoteController.characterModel?.position.set(
1201
+ spawnPosition.x,
1202
+ spawnPosition.y,
1203
+ spawnPosition.z
1204
+ );
1205
+ this.remoteCharacterControllers.set(id, remoteController);
1206
+ }
1207
+ resolve(character);
1208
+ },
1209
+ this.collisionsManager,
1210
+ this.inputManager,
1211
+ this.cameraManager,
1212
+ this.timeManager
1213
+ );
1214
+ });
1215
+ this.loadingCharacters.set(id, characterLoadingPromise);
1216
+ return characterLoadingPromise;
1217
+ }
1218
+ getLocalCharacterPositionAndRotation() {
1219
+ if (this.character && this.character.model && this.character.model.mesh) {
1220
+ return {
1221
+ position: this.character.model.mesh.position,
1222
+ rotation: this.character.model.mesh.rotation
1223
+ };
1224
+ }
1225
+ return {
1226
+ position: { x: 0, y: 0, z: 0 },
1227
+ rotation: { x: 0, y: 0, z: 0 }
1228
+ };
1229
+ }
1230
+ clear() {
1231
+ for (const [id, character] of this.remoteCharacters) {
1232
+ this.group.remove(character.model.mesh);
1233
+ this.remoteCharacters.delete(id);
1234
+ this.remoteCharacterControllers.delete(id);
1235
+ }
1236
+ if (this.character) {
1237
+ this.group.remove(this.character.model.mesh);
1238
+ this.character = null;
1239
+ }
1240
+ this.loadingCharacters.clear();
1241
+ }
1242
+ update() {
1243
+ if (this.character) {
1244
+ this.character.update(this.timeManager.time);
1245
+ if (this.character.model?.mesh) {
1246
+ this.cameraOffsetTarget = this.cameraManager.targetDistance <= 0.4 ? 0.6 : 0;
1247
+ this.cameraOffset += ease(this.cameraOffsetTarget, this.cameraOffset, 0.1);
1248
+ const targetOffset = new Vector36(0, 1.3, this.cameraOffset);
1249
+ targetOffset.applyQuaternion(this.character.model.mesh.quaternion);
1250
+ this.cameraManager.setTarget(this.character.position.add(targetOffset));
1251
+ }
1252
+ if (this.character.controller) {
1253
+ this.character.controller.update();
1254
+ if (this.timeManager.frame % 2 === 0) {
1255
+ this.sendUpdate(this.character.controller.networkState);
1256
+ }
1257
+ }
1258
+ for (const [id, update] of this.clientStates) {
1259
+ const { position } = update;
1260
+ if (!this.remoteCharacters.has(id) && !this.loadingCharacters.has(id)) {
1261
+ this.spawnCharacter(
1262
+ this.characterDescription,
1263
+ id,
1264
+ false,
1265
+ new Vector36(position.x, position.y, position.z)
1266
+ ).then((_character) => {
1267
+ this.loadingCharacters.delete(id);
1268
+ });
1269
+ }
1270
+ const characterController = this.remoteCharacterControllers.get(id);
1271
+ if (characterController) {
1272
+ characterController.update(update, this.timeManager.time, this.timeManager.deltaTime);
1273
+ }
1274
+ }
1275
+ for (const [id, character] of this.remoteCharacters) {
1276
+ if (!this.clientStates.has(id)) {
1277
+ this.group.remove(character.model.mesh);
1278
+ this.remoteCharacters.delete(id);
1279
+ this.remoteCharacterControllers.delete(id);
1280
+ }
1281
+ }
1282
+ if (this.updateLocationHash && this.timeManager.frame % 60 === 0) {
1283
+ window.location.hash = encodeCharacterAndCamera(
1284
+ this.character.model.mesh,
1285
+ this.cameraManager.camera
1286
+ );
1287
+ }
1288
+ }
1289
+ }
1290
+ };
1291
+
1292
+ // src/input/KeyInputManager.ts
1293
+ var KeyInputManager = class {
1294
+ constructor() {
1295
+ __publicField(this, "keys", /* @__PURE__ */ new Map());
1296
+ document.addEventListener("keydown", this.onKeyDown.bind(this));
1297
+ document.addEventListener("keyup", this.onKeyUp.bind(this));
1298
+ window.addEventListener("blur", this.handleUnfocus.bind(this));
1299
+ }
1300
+ handleUnfocus(_event) {
1301
+ this.keys.clear();
1302
+ }
1303
+ onKeyDown(event) {
1304
+ this.keys.set(event.key.toLowerCase(), true);
1305
+ }
1306
+ onKeyUp(event) {
1307
+ this.keys.set(event.key.toLowerCase(), false);
1308
+ }
1309
+ isKeyPressed(key) {
1310
+ return this.keys.get(key) || false;
1311
+ }
1312
+ isMovementKeyPressed() {
1313
+ return ["w", "a", "s", "d"].some((key) => this.isKeyPressed(key));
1314
+ }
1315
+ get forward() {
1316
+ return this.isKeyPressed("w");
1317
+ }
1318
+ get backward() {
1319
+ return this.isKeyPressed("s");
1320
+ }
1321
+ get left() {
1322
+ return this.isKeyPressed("a");
1323
+ }
1324
+ get right() {
1325
+ return this.isKeyPressed("d");
1326
+ }
1327
+ get run() {
1328
+ return this.isKeyPressed("shift");
1329
+ }
1330
+ get jump() {
1331
+ return this.isKeyPressed(" ");
1332
+ }
1333
+ get anyDirection() {
1334
+ return this.isMovementKeyPressed();
1335
+ }
1336
+ get conflictingDirection() {
1337
+ return this.isKeyPressed("w") && this.isKeyPressed("s") || this.isKeyPressed("a") && this.isKeyPressed("d");
1338
+ }
1339
+ dispose() {
1340
+ document.removeEventListener("keydown", this.onKeyDown.bind(this));
1341
+ document.removeEventListener("keyup", this.onKeyDown.bind(this));
1342
+ window.removeEventListener("blur", this.handleUnfocus.bind(this));
1343
+ }
1344
+ };
1345
+
1346
+ // src/mml/MMLCompositionScene.ts
1347
+ import {
1348
+ InteractionManager,
1349
+ MMLClickTrigger,
1350
+ PromptManager,
1351
+ registerCustomElementsToWindow,
1352
+ setGlobalMScene
1353
+ } from "mml-web";
1354
+ import { Group as Group2 } from "three";
1355
+ var MMLCompositionScene = class {
1356
+ constructor(renderer, scene, camera, audioListener, collisionsManager, getUserPositionAndRotation, documentAddresses) {
1357
+ this.renderer = renderer;
1358
+ this.scene = scene;
1359
+ this.camera = camera;
1360
+ this.audioListener = audioListener;
1361
+ this.collisionsManager = collisionsManager;
1362
+ this.getUserPositionAndRotation = getUserPositionAndRotation;
1363
+ __publicField(this, "group");
1364
+ __publicField(this, "debug", false);
1365
+ __publicField(this, "mmlScene");
1366
+ __publicField(this, "promptManager");
1367
+ __publicField(this, "interactionListener");
1368
+ __publicField(this, "clickTrigger");
1369
+ this.group = new Group2();
1370
+ this.promptManager = PromptManager.init(document.body);
1371
+ const { interactionListener } = InteractionManager.init(document.body, this.camera, this.scene);
1372
+ this.interactionListener = interactionListener;
1373
+ this.mmlScene = {
1374
+ getAudioListener: () => this.audioListener,
1375
+ getRenderer: () => this.renderer,
1376
+ getThreeScene: () => this.scene,
1377
+ getRootContainer: () => this.group,
1378
+ getCamera: () => this.camera,
1379
+ addCollider: (object, mElement) => {
1380
+ this.collisionsManager.addMeshesGroup(object, mElement);
1381
+ },
1382
+ updateCollider: (object) => {
1383
+ this.collisionsManager.updateMeshesGroup(object);
1384
+ },
1385
+ removeCollider: (object) => {
1386
+ this.collisionsManager.removeMeshesGroup(object);
1387
+ },
1388
+ getUserPositionAndRotation: this.getUserPositionAndRotation,
1389
+ addInteraction: (interaction) => {
1390
+ this.interactionListener.addInteraction(interaction);
1391
+ },
1392
+ updateInteraction: (interaction) => {
1393
+ this.interactionListener.updateInteraction(interaction);
1394
+ },
1395
+ removeInteraction: (interaction) => {
1396
+ this.interactionListener.removeInteraction(interaction);
1397
+ },
1398
+ prompt: (promptProps, callback) => {
1399
+ this.promptManager.prompt(promptProps, callback);
1400
+ }
1401
+ };
1402
+ setGlobalMScene(this.mmlScene);
1403
+ registerCustomElementsToWindow(window);
1404
+ this.clickTrigger = MMLClickTrigger.init(document, this.mmlScene);
1405
+ if (this.debug) {
1406
+ console.log(this.clickTrigger);
1407
+ }
1408
+ for (const address of documentAddresses) {
1409
+ const frameElement = document.createElement("m-frame");
1410
+ frameElement.setAttribute("src", address);
1411
+ document.body.appendChild(frameElement);
1412
+ }
1413
+ }
1414
+ };
1415
+
1416
+ // src/rendering/composer.ts
1417
+ import {
1418
+ EffectComposer as EffectComposer2,
1419
+ RenderPass,
1420
+ EffectPass as EffectPass2,
1421
+ FXAAEffect,
1422
+ ShaderPass,
1423
+ BloomEffect as BloomEffect2,
1424
+ SSAOEffect as SSAOEffect2,
1425
+ BlendFunction as BlendFunction3,
1426
+ TextureEffect,
1427
+ ToneMappingEffect as ToneMappingEffect2,
1428
+ SMAAEffect,
1429
+ SMAAPreset,
1430
+ EdgeDetectionMode,
1431
+ PredicationMode,
1432
+ NormalPass
1433
+ } from "postprocessing";
1434
+ import {
1435
+ Color as Color4,
1436
+ HalfFloatType,
1437
+ LinearSRGBColorSpace,
1438
+ LoadingManager as LoadingManager2,
1439
+ PMREMGenerator,
1440
+ Vector2 as Vector22,
1441
+ WebGLRenderer as WebGLRenderer3
1442
+ } from "three";
1443
+ import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js";
1444
+
1445
+ // src/tweakpane/composerSettings.ts
1446
+ import { BlendFunction } from "postprocessing";
1447
+ var composerValues = {
1448
+ renderer: {
1449
+ shadowMap: 2,
1450
+ toneMapping: 4,
1451
+ exposure: 1,
1452
+ bgIntensity: 0.6,
1453
+ bgBlurriness: 0
1454
+ },
1455
+ ssao: {
1456
+ blendFunction: BlendFunction.MULTIPLY,
1457
+ distanceScaling: true,
1458
+ depthAwareUpsampling: true,
1459
+ samples: 17,
1460
+ rings: 7,
1461
+ luminanceInfluence: 0.7,
1462
+ radius: 0.03,
1463
+ intensity: 2.5,
1464
+ bias: 0.05,
1465
+ fade: 0.03,
1466
+ resolutionScale: 0.75,
1467
+ color: { r: 0, g: 0, b: 0 },
1468
+ worldDistanceThreshold: 30,
1469
+ worldDistanceFalloff: 7,
1470
+ worldProximityThreshold: 0.5,
1471
+ worldProximityFalloff: 0.3
1472
+ },
1473
+ toneMapping: {
1474
+ mode: 2,
1475
+ resolution: 512,
1476
+ whitePoint: 32,
1477
+ middleGrey: 21,
1478
+ minLuminance: 0.01,
1479
+ averageLuminance: 0.01,
1480
+ adaptationRate: 2
1481
+ },
1482
+ brightness: -0.03,
1483
+ contrast: 1.3,
1484
+ saturation: 0.95,
1485
+ grain: 0.055,
1486
+ bloom: 0.4
1487
+ };
1488
+ var composerOptions = {
1489
+ renderer: {
1490
+ shadowMap: { min: 0, max: 2, step: 1 },
1491
+ toneMapping: { min: 0, max: 5, step: 1 },
1492
+ exposure: { min: 0, max: 3, step: 0.01 },
1493
+ bgIntensity: { min: 0, max: 1, step: 0.01 },
1494
+ bgBlurriness: { min: 0, max: 0.1, step: 1e-3 }
1495
+ },
1496
+ ssao: {
1497
+ samples: { min: 1, max: 50, step: 1 },
1498
+ rings: { min: 1, max: 50, step: 1 },
1499
+ luminanceInfluence: { min: 0, max: 1, step: 0.01 },
1500
+ radius: { min: 0, max: 0.1, step: 1e-3 },
1501
+ intensity: { min: 0, max: 5, step: 0.1 },
1502
+ bias: { min: 0, max: 0.1, step: 1e-3 },
1503
+ fade: { min: 0, max: 0.1, step: 1e-3 },
1504
+ resolutionScale: { min: 0.25, max: 2, step: 0.25 },
1505
+ worldDistanceThreshold: { min: 0, max: 200, step: 1 },
1506
+ worldDistanceFalloff: { min: 0, max: 200, step: 1 },
1507
+ worldProximityThreshold: { min: 0, max: 2, step: 0.01 },
1508
+ worldProximityFalloff: { min: 0, max: 2, step: 0.01 }
1509
+ },
1510
+ toneMapping: {
1511
+ mode: { min: 0, max: 4, step: 1 },
1512
+ resolution: { min: 64, max: 512, step: 64 },
1513
+ whitePoint: { min: 0, max: 32, step: 0.01 },
1514
+ middleGrey: { min: 0, max: 32, step: 0.01 },
1515
+ minLuminance: { min: 0, max: 32, step: 1e-3 },
1516
+ averageLuminance: { min: 1e-3, max: 0.2, step: 1e-3 },
1517
+ adaptationRate: { min: 0.1, max: 2, step: 0.1 }
1518
+ },
1519
+ brightness: {
1520
+ amount: { min: -1, max: 1, step: 0.01 }
1521
+ },
1522
+ contrast: {
1523
+ amount: { min: 0, max: 2, step: 0.01 }
1524
+ },
1525
+ saturation: {
1526
+ amount: { min: 0, max: 2, step: 0.01 }
1527
+ },
1528
+ grain: {
1529
+ amount: { min: 0, max: 0.2, step: 2e-3 }
1530
+ },
1531
+ bloom: {
1532
+ amount: { min: 0, max: 4, step: 0.1 }
1533
+ }
1534
+ };
1535
+ var shadowMapTypes = {
1536
+ 0: "BasicShadowMap",
1537
+ 1: "PCFShadowMap",
1538
+ 2: "PCFSoftShadowMap"
1539
+ };
1540
+ var rendererToneMappingTypes = {
1541
+ 0: "NoToneMapping",
1542
+ 1: "LinearToneMapping",
1543
+ 2: "ReinhardToneMapping",
1544
+ 3: "CineonToneMapping",
1545
+ 4: "ACESFilmicToneMapping",
1546
+ 5: "CustomToneMapping"
1547
+ };
1548
+ var customToneMappingTypes = {
1549
+ 0: "REINHARD",
1550
+ 1: "REINHARD2",
1551
+ 2: "REINHARD2_ADAPTIVE",
1552
+ 3: "OPTIMIZED_CINEON",
1553
+ 4: "ACES_FILMIC"
1554
+ };
1555
+ var rendererBlades = {
1556
+ shadowMapType: shadowMapTypes[composerValues.renderer.shadowMap],
1557
+ toneMappingType: rendererToneMappingTypes[composerValues.renderer.toneMapping]
1558
+ };
1559
+ var setShadowMapType = (value) => {
1560
+ rendererBlades.shadowMapType = shadowMapTypes[value];
1561
+ };
1562
+ var setToneMappingType = (value) => {
1563
+ rendererBlades.toneMappingType = rendererToneMappingTypes[value];
1564
+ };
1565
+ var customToneMappingBlade = {
1566
+ customToneMappingType: customToneMappingTypes[composerValues.toneMapping.mode]
1567
+ };
1568
+ var setCustomToneMappingType = (value) => {
1569
+ customToneMappingBlade.customToneMappingType = customToneMappingTypes[value];
1570
+ };
1571
+ var ssaoMaterialParams = [
1572
+ "fade",
1573
+ "bias",
1574
+ "minRadiusScale",
1575
+ "worldDistanceThreshold",
1576
+ "worldDistanceFalloff",
1577
+ "worldProximityThreshold",
1578
+ "worldProximityFalloff"
1579
+ ];
1580
+ var statsData = {
1581
+ triangles: "0",
1582
+ geometries: "0",
1583
+ textures: "0",
1584
+ shaders: "0",
1585
+ postPasses: "0",
1586
+ drawCalls: "0",
1587
+ rawDeltaTime: "0",
1588
+ deltaTime: "0",
1589
+ FPS: "0"
1590
+ };
1591
+
1592
+ // src/tweakpane/TweakPane.ts
1593
+ import {
1594
+ BlendFunction as BlendFunction2
1595
+ } from "postprocessing";
1596
+ import { Color as Color3 } from "three";
1597
+ import { Pane } from "tweakpane";
1598
+ var TweakPane = class {
1599
+ constructor(renderer, scene, composer) {
1600
+ __publicField(this, "renderer");
1601
+ __publicField(this, "scene");
1602
+ __publicField(this, "composer");
1603
+ __publicField(this, "gui", new Pane());
1604
+ __publicField(this, "render");
1605
+ __publicField(this, "stats");
1606
+ __publicField(this, "renderOptions");
1607
+ __publicField(this, "ssao");
1608
+ __publicField(this, "toneMapping");
1609
+ __publicField(this, "post");
1610
+ __publicField(this, "export");
1611
+ __publicField(this, "characterMaterial");
1612
+ __publicField(this, "saveVisibilityInLocalStorage", true);
1613
+ __publicField(this, "guiVisible", false);
1614
+ if (this.saveVisibilityInLocalStorage) {
1615
+ const localStorageGuiVisible = localStorage.getItem("guiVisible");
1616
+ if (localStorageGuiVisible !== null) {
1617
+ if (localStorageGuiVisible === "true") {
1618
+ this.guiVisible = true;
1619
+ } else if (localStorageGuiVisible === "false") {
1620
+ this.guiVisible = false;
1621
+ }
1622
+ }
1623
+ }
1624
+ this.renderer = renderer;
1625
+ this.scene = scene;
1626
+ this.composer = composer;
1627
+ this.render = this.gui.addFolder({ title: "rendering", expanded: true });
1628
+ this.stats = this.render.addFolder({ title: "stats", expanded: true });
1629
+ this.renderOptions = this.render.addFolder({ title: "renderOptions", expanded: false });
1630
+ this.toneMapping = this.render.addFolder({ title: "customToneMapping", expanded: false });
1631
+ this.ssao = this.render.addFolder({ title: "ambientOcclusion", expanded: false });
1632
+ this.post = this.render.addFolder({ title: "post", expanded: false });
1633
+ this.toneMapping.hidden = composerValues.renderer.toneMapping === 5 ? false : true;
1634
+ this.characterMaterial = this.gui.addFolder({ title: "characterMaterial", expanded: false });
1635
+ this.characterMaterial.addInput(
1636
+ characterValues.material,
1637
+ "transmission",
1638
+ characterOptions.material.transmission
1639
+ );
1640
+ this.characterMaterial.addInput(
1641
+ characterValues.material,
1642
+ "metalness",
1643
+ characterOptions.material.metalness
1644
+ );
1645
+ this.characterMaterial.addInput(
1646
+ characterValues.material,
1647
+ "roughness",
1648
+ characterOptions.material.roughness
1649
+ );
1650
+ this.characterMaterial.addInput(characterValues.material, "ior", characterOptions.material.ior);
1651
+ this.characterMaterial.addInput(
1652
+ characterValues.material,
1653
+ "thickness",
1654
+ characterOptions.material.thickness
1655
+ );
1656
+ this.characterMaterial.addInput(characterValues.material, "specularColor", {
1657
+ color: { type: "float" }
1658
+ });
1659
+ this.characterMaterial.addInput(
1660
+ characterValues.material,
1661
+ "specularIntensity",
1662
+ characterOptions.material.specularIntensity
1663
+ );
1664
+ this.characterMaterial.addInput(characterValues.material, "emissive", {
1665
+ color: { type: "float" }
1666
+ });
1667
+ this.characterMaterial.addInput(
1668
+ characterValues.material,
1669
+ "emissiveIntensity",
1670
+ characterOptions.material.emissiveIntensity
1671
+ );
1672
+ this.characterMaterial.addInput(
1673
+ characterValues.material,
1674
+ "envMapIntensity",
1675
+ characterOptions.material.envMapIntensity
1676
+ );
1677
+ this.characterMaterial.addInput(characterValues.material, "sheenColor", {
1678
+ color: { type: "float" }
1679
+ });
1680
+ this.characterMaterial.addInput(
1681
+ characterValues.material,
1682
+ "sheen",
1683
+ characterOptions.material.sheen
1684
+ );
1685
+ this.characterMaterial.addInput(
1686
+ characterValues.material,
1687
+ "clearcoat",
1688
+ characterOptions.material.clearcoat
1689
+ );
1690
+ this.characterMaterial.addInput(
1691
+ characterValues.material,
1692
+ "clearcoatRoughness",
1693
+ characterOptions.material.clearcoatRoughness
1694
+ );
1695
+ this.characterMaterial.on("change", (e) => {
1696
+ if (!e.presetKey) {
1697
+ return;
1698
+ }
1699
+ if (e.presetKey === "specularColor") {
1700
+ characterValues.material.specularColor = {
1701
+ r: e.value.r,
1702
+ g: e.value.g,
1703
+ b: e.value.b
1704
+ };
1705
+ return;
1706
+ }
1707
+ if (e.presetKey === "emissive") {
1708
+ characterValues.material.emissive = {
1709
+ r: e.value.r,
1710
+ g: e.value.g,
1711
+ b: e.value.b
1712
+ };
1713
+ return;
1714
+ }
1715
+ if (e.presetKey === "sheenColor") {
1716
+ characterValues.material.sheenColor = {
1717
+ r: e.value.r,
1718
+ g: e.value.g,
1719
+ b: e.value.b
1720
+ };
1721
+ return;
1722
+ }
1723
+ });
1724
+ this.export = this.gui.addFolder({ title: "import/export", expanded: false });
1725
+ window.addEventListener("keydown", this.processKey.bind(this));
1726
+ this.setupGUIListeners.bind(this)();
1727
+ this.setupRenderPane = this.setupRenderPane.bind(this);
1728
+ }
1729
+ processKey(e) {
1730
+ if (e.key === "p")
1731
+ this.toggleGUI();
1732
+ }
1733
+ setupGUIListeners() {
1734
+ const gui = this.gui;
1735
+ const paneElement = gui.containerElem_;
1736
+ paneElement.style.display = this.guiVisible ? "unset" : "none";
1737
+ this.gui.element.addEventListener("mousedown", () => setTweakpaneActive(true));
1738
+ this.gui.element.addEventListener("mouseup", () => setTweakpaneActive(false));
1739
+ this.gui.element.addEventListener("mouseleave", () => setTweakpaneActive(false));
1740
+ }
1741
+ setupRenderPane(ssaoEffect, toneMappingEffect, toneMappingPass, brightnessContrastSaturation, bloomEffect, gaussGrainEffect) {
1742
+ this.stats.addMonitor(statsData, "triangles");
1743
+ this.stats.addMonitor(statsData, "geometries");
1744
+ this.stats.addMonitor(statsData, "textures");
1745
+ this.stats.addMonitor(statsData, "shaders");
1746
+ this.stats.addMonitor(statsData, "postPasses");
1747
+ this.stats.addMonitor(statsData, "drawCalls");
1748
+ this.stats.addMonitor(statsData, "rawDeltaTime");
1749
+ this.stats.addMonitor(statsData, "deltaTime");
1750
+ this.stats.addMonitor(statsData, "FPS");
1751
+ this.renderOptions.addInput(
1752
+ composerValues.renderer,
1753
+ "shadowMap",
1754
+ composerOptions.renderer.shadowMap
1755
+ );
1756
+ this.renderOptions.addMonitor(rendererBlades, "shadowMapType");
1757
+ this.renderOptions.addInput(
1758
+ composerValues.renderer,
1759
+ "toneMapping",
1760
+ composerOptions.renderer.toneMapping
1761
+ );
1762
+ this.renderOptions.addMonitor(rendererBlades, "toneMappingType");
1763
+ this.renderOptions.addInput(
1764
+ composerValues.renderer,
1765
+ "exposure",
1766
+ composerOptions.renderer.exposure
1767
+ );
1768
+ this.renderOptions.addInput(
1769
+ composerValues.renderer,
1770
+ "bgIntensity",
1771
+ composerOptions.renderer.bgIntensity
1772
+ );
1773
+ this.renderOptions.addInput(
1774
+ composerValues.renderer,
1775
+ "bgBlurriness",
1776
+ composerOptions.renderer.bgBlurriness
1777
+ );
1778
+ this.renderOptions.on("change", (e) => {
1779
+ const target = e.target;
1780
+ switch (target.label) {
1781
+ case "shadowMap":
1782
+ this.renderer.shadowMap.type = e.value;
1783
+ setShadowMapType(e.value);
1784
+ break;
1785
+ case "toneMapping":
1786
+ this.renderer.toneMapping = e.value;
1787
+ if (e.value !== 5 && e.value !== 0) {
1788
+ this.toneMapping.hidden = true;
1789
+ } else {
1790
+ this.toneMapping.hidden = false;
1791
+ }
1792
+ toneMappingPass.enabled = e.value === 5 || e.value === 0 ? true : false;
1793
+ setToneMappingType(e.value);
1794
+ break;
1795
+ case "exposure":
1796
+ this.renderer.toneMappingExposure = e.value;
1797
+ break;
1798
+ case "bgIntensity":
1799
+ this.scene.backgroundIntensity = e.value;
1800
+ break;
1801
+ case "bgBlurriness":
1802
+ this.scene.backgroundBlurriness = e.value;
1803
+ break;
1804
+ default:
1805
+ break;
1806
+ }
1807
+ });
1808
+ this.ssao.addInput({ showEffectOnly: false }, "showEffectOnly");
1809
+ this.ssao.addInput(composerValues.ssao, "samples", composerOptions.ssao.samples);
1810
+ this.ssao.addInput(composerValues.ssao, "rings", composerOptions.ssao.rings);
1811
+ this.ssao.addInput(
1812
+ composerValues.ssao,
1813
+ "luminanceInfluence",
1814
+ composerOptions.ssao.luminanceInfluence
1815
+ );
1816
+ this.ssao.addInput(composerValues.ssao, "radius", composerOptions.ssao.radius);
1817
+ this.ssao.addInput(composerValues.ssao, "intensity", composerOptions.ssao.intensity);
1818
+ this.ssao.addInput(composerValues.ssao, "bias", composerOptions.ssao.bias);
1819
+ this.ssao.addInput(composerValues.ssao, "fade", composerOptions.ssao.fade);
1820
+ this.ssao.addInput(
1821
+ composerValues.ssao,
1822
+ "resolutionScale",
1823
+ composerOptions.ssao.resolutionScale
1824
+ );
1825
+ this.ssao.addInput(
1826
+ composerValues.ssao,
1827
+ "worldDistanceThreshold",
1828
+ composerOptions.ssao.worldDistanceThreshold
1829
+ );
1830
+ this.ssao.addInput(
1831
+ composerValues.ssao,
1832
+ "worldDistanceFalloff",
1833
+ composerOptions.ssao.worldDistanceFalloff
1834
+ );
1835
+ this.ssao.addInput(
1836
+ composerValues.ssao,
1837
+ "worldProximityThreshold",
1838
+ composerOptions.ssao.worldProximityThreshold
1839
+ );
1840
+ this.ssao.addInput(
1841
+ composerValues.ssao,
1842
+ "worldProximityFalloff",
1843
+ composerOptions.ssao.worldProximityFalloff
1844
+ );
1845
+ this.ssao.addInput(composerValues.ssao, "color", {
1846
+ color: { alpha: false, type: "float" }
1847
+ });
1848
+ this.ssao.on("change", (e) => {
1849
+ if (!e.presetKey) {
1850
+ return;
1851
+ }
1852
+ const preset = e.presetKey;
1853
+ if (preset === "showEffectOnly") {
1854
+ ssaoEffect.blendMode.blendFunction = e.value === true ? BlendFunction2.NORMAL : BlendFunction2.MULTIPLY;
1855
+ return;
1856
+ }
1857
+ if (preset === "resolutionScale") {
1858
+ ssaoEffect.resolution.scale = e.value;
1859
+ return;
1860
+ }
1861
+ if (ssaoMaterialParams.includes(e.presetKey)) {
1862
+ ssaoEffect.ssaoMaterial[preset] = e.value;
1863
+ return;
1864
+ }
1865
+ if (e.presetKey === "color") {
1866
+ ssaoEffect.color = new Color3().setRGB(e.value.r, e.value.g, e.value.b);
1867
+ return;
1868
+ }
1869
+ ssaoEffect[preset] = e.value;
1870
+ });
1871
+ this.toneMapping.addInput(composerValues.toneMapping, "mode", composerOptions.toneMapping.mode);
1872
+ this.toneMapping.addMonitor(customToneMappingBlade, "customToneMappingType");
1873
+ this.toneMapping.addInput(
1874
+ composerValues.toneMapping,
1875
+ "whitePoint",
1876
+ composerOptions.toneMapping.whitePoint
1877
+ );
1878
+ this.toneMapping.addInput(
1879
+ composerValues.toneMapping,
1880
+ "middleGrey",
1881
+ composerOptions.toneMapping.middleGrey
1882
+ );
1883
+ const minLuminance = this.toneMapping.addInput(
1884
+ composerValues.toneMapping,
1885
+ "minLuminance",
1886
+ composerOptions.toneMapping.minLuminance
1887
+ );
1888
+ minLuminance.hidden = composerValues.toneMapping.mode === 2 ? true : false;
1889
+ const averageLuminance = this.toneMapping.addInput(
1890
+ composerValues.toneMapping,
1891
+ "averageLuminance",
1892
+ composerOptions.toneMapping.averageLuminance
1893
+ );
1894
+ averageLuminance.hidden = composerValues.toneMapping.mode === 2 ? true : false;
1895
+ this.toneMapping.addInput(
1896
+ composerValues.toneMapping,
1897
+ "adaptationRate",
1898
+ composerOptions.toneMapping.adaptationRate
1899
+ );
1900
+ this.toneMapping.on("change", (e) => {
1901
+ if (!e.presetKey) {
1902
+ return;
1903
+ }
1904
+ const preset = e.presetKey;
1905
+ if (preset === "mode") {
1906
+ minLuminance.hidden = composerValues.toneMapping.mode === 2 ? true : false;
1907
+ averageLuminance.hidden = composerValues.toneMapping.mode === 2 ? true : false;
1908
+ setCustomToneMappingType(e.value);
1909
+ }
1910
+ toneMappingEffect[preset] = e.value;
1911
+ return;
1912
+ });
1913
+ this.post.addInput(composerValues, "brightness", composerOptions.brightness.amount);
1914
+ this.post.addInput(composerValues, "contrast", composerOptions.contrast.amount);
1915
+ this.post.addInput(composerValues, "saturation", composerOptions.saturation.amount);
1916
+ this.post.addInput(composerValues, "bloom", composerOptions.bloom.amount);
1917
+ this.post.addInput(composerValues, "grain", composerOptions.grain.amount);
1918
+ this.post.on("change", (e) => {
1919
+ const target = e.presetKey;
1920
+ switch (target) {
1921
+ case "brightness":
1922
+ brightnessContrastSaturation.uniforms.brightness.value = e.value;
1923
+ break;
1924
+ case "contrast":
1925
+ brightnessContrastSaturation.uniforms.contrast.value = e.value;
1926
+ break;
1927
+ case "saturation":
1928
+ brightnessContrastSaturation.uniforms.saturation.value = e.value;
1929
+ break;
1930
+ case "bloom":
1931
+ bloomEffect.intensity = e.value;
1932
+ break;
1933
+ case "grain":
1934
+ gaussGrainEffect.uniforms.amount.value = e.value;
1935
+ break;
1936
+ default:
1937
+ break;
1938
+ }
1939
+ });
1940
+ const exportButton = this.export.addButton({ title: "export" });
1941
+ exportButton.on("click", () => {
1942
+ this.downloadSettingsAsJSON(this.gui.exportPreset());
1943
+ });
1944
+ const importButton = this.export.addButton({ title: "import" });
1945
+ importButton.on("click", () => {
1946
+ this.importSettingsFromJSON((settings) => {
1947
+ this.gui.importPreset(settings);
1948
+ });
1949
+ });
1950
+ }
1951
+ formatDateForFilename() {
1952
+ const date = /* @__PURE__ */ new Date();
1953
+ const year = date.getFullYear();
1954
+ const month = String(date.getMonth() + 1).padStart(2, "0");
1955
+ const day = String(date.getDate()).padStart(2, "0");
1956
+ const hours = String(date.getHours()).padStart(2, "0");
1957
+ const minutes = String(date.getMinutes()).padStart(2, "0");
1958
+ const seconds = String(date.getSeconds()).padStart(2, "0");
1959
+ return `${year}-${month}-${day} ${hours}-${minutes}-${seconds}`;
1960
+ }
1961
+ downloadSettingsAsJSON(settings) {
1962
+ const jsonString = JSON.stringify(settings, null, 2);
1963
+ const blob = new Blob([jsonString], { type: "application/json" });
1964
+ const url = URL.createObjectURL(blob);
1965
+ const a = document.createElement("a");
1966
+ a.download = `settings ${this.formatDateForFilename()}.json`;
1967
+ a.href = url;
1968
+ a.click();
1969
+ URL.revokeObjectURL(url);
1970
+ }
1971
+ importSettingsFromJSON(callback) {
1972
+ const input = document.createElement("input");
1973
+ input.type = "file";
1974
+ input.accept = ".json";
1975
+ input.addEventListener("change", (event) => {
1976
+ const file = event.target.files?.[0];
1977
+ if (file) {
1978
+ const reader = new FileReader();
1979
+ reader.onload = (loadEvent) => {
1980
+ try {
1981
+ const settings = JSON.parse(loadEvent.target?.result);
1982
+ callback(settings);
1983
+ } catch (err) {
1984
+ console.error("Error parsing JSON:", err);
1985
+ }
1986
+ };
1987
+ reader.readAsText(file);
1988
+ }
1989
+ });
1990
+ input.click();
1991
+ }
1992
+ updateStats(timeManager) {
1993
+ const { geometries, textures } = this.renderer.info.memory;
1994
+ const { triangles, calls } = this.renderer.info.render;
1995
+ statsData.triangles = triangles.toString();
1996
+ statsData.geometries = geometries.toString();
1997
+ statsData.textures = textures.toString();
1998
+ statsData.shaders = this.renderer.info.programs.length.toString();
1999
+ statsData.postPasses = this.composer.passes.length.toString();
2000
+ statsData.drawCalls = calls.toString();
2001
+ statsData.rawDeltaTime = (Math.round(timeManager.rawDeltaTime * 1e5) / 1e5).toString();
2002
+ statsData.deltaTime = (Math.round(timeManager.deltaTime * 1e5) / 1e5).toString();
2003
+ statsData.FPS = timeManager.fps.toString();
2004
+ }
2005
+ toggleGUI() {
2006
+ const gui = this.gui;
2007
+ const paneElement = gui.containerElem_;
2008
+ paneElement.style.display = this.guiVisible ? "none" : "unset";
2009
+ this.guiVisible = !this.guiVisible;
2010
+ if (this.saveVisibilityInLocalStorage) {
2011
+ localStorage.setItem("guiVisible", this.guiVisible === true ? "true" : "false");
2012
+ }
2013
+ }
2014
+ };
2015
+
2016
+ // src/rendering/post-effects/bright-contrast-sat.ts
2017
+ import { ShaderMaterial, Uniform } from "three";
2018
+
2019
+ // src/rendering/shaders/vertex-shader.ts
2020
+ var vertexShader = (
2021
+ /* glsl */
2022
+ `
2023
+ precision highp float;
2024
+
2025
+ out vec2 vUv;
2026
+ void main() {
2027
+ vUv = uv;
2028
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
2029
+ }
2030
+ `
2031
+ );
2032
+
2033
+ // src/rendering/post-effects/bright-contrast-sat.ts
2034
+ var BrightnessContrastSaturation = new ShaderMaterial({
2035
+ uniforms: {
2036
+ tDiffuse: new Uniform(null),
2037
+ brightness: new Uniform(0),
2038
+ contrast: new Uniform(1),
2039
+ saturation: new Uniform(1)
2040
+ },
2041
+ vertexShader,
2042
+ fragmentShader: (
2043
+ /* glsl */
2044
+ `
2045
+ precision highp float;
2046
+ in vec2 vUv;
2047
+
2048
+ uniform sampler2D tDiffuse;
2049
+ uniform float brightness;
2050
+ uniform float contrast;
2051
+ uniform float saturation;
2052
+
2053
+ mat4 brightnessMatrix(float brightness) {
2054
+ return mat4(
2055
+ 1, 0, 0, 0,
2056
+ 0, 1, 0, 0,
2057
+ 0, 0, 1, 0,
2058
+ brightness, brightness, brightness, 1
2059
+ );
2060
+ }
2061
+
2062
+ mat4 contrastMatrix(float contrast) {
2063
+ float t = (1.0 - contrast) / 2.0;
2064
+
2065
+ return mat4(
2066
+ contrast, 0, 0, 0,
2067
+ 0, contrast, 0, 0,
2068
+ 0, 0, contrast, 0,
2069
+ t, t, t, 1
2070
+ );
2071
+ }
2072
+
2073
+ mat4 saturationMatrix(float saturation) {
2074
+ vec3 luminance = vec3(0.3086, 0.6094, 0.0820);
2075
+ float oneMinusSat = 1.0 - saturation;
2076
+ vec3 red = vec3(luminance.x * oneMinusSat);
2077
+ red += vec3(saturation, 0, 0);
2078
+ vec3 green = vec3(luminance.y * oneMinusSat);
2079
+ green += vec3(0, saturation, 0);
2080
+ vec3 blue = vec3(luminance.z * oneMinusSat);
2081
+ blue += vec3(0, 0, saturation);
2082
+ return mat4(
2083
+ red, 0,
2084
+ green, 0,
2085
+ blue, 0,
2086
+ 0, 0, 0, 1
2087
+ );
2088
+ }
2089
+
2090
+ void main(void) {
2091
+ vec4 color = texture(tDiffuse, vUv);
2092
+ gl_FragColor = (
2093
+ brightnessMatrix(brightness) *
2094
+ contrastMatrix(contrast) *
2095
+ saturationMatrix(saturation) *
2096
+ color
2097
+ );
2098
+ }
2099
+ `
2100
+ ),
2101
+ dithering: true
2102
+ });
2103
+
2104
+ // src/rendering/post-effects/gauss-grain.ts
2105
+ import { ShaderMaterial as ShaderMaterial2, Uniform as Uniform2, Vector2 } from "three";
2106
+ var GaussGrainEffect = new ShaderMaterial2({
2107
+ uniforms: {
2108
+ tDiffuse: new Uniform2(null),
2109
+ resolution: new Uniform2(new Vector2()),
2110
+ time: new Uniform2(0),
2111
+ amount: new Uniform2(0),
2112
+ alpha: new Uniform2(0)
2113
+ },
2114
+ vertexShader,
2115
+ fragmentShader: (
2116
+ /* glsl */
2117
+ `
2118
+ precision highp float;
2119
+ in vec2 vUv;
2120
+
2121
+ uniform sampler2D tDiffuse;
2122
+ uniform vec2 resolution;
2123
+ uniform float time;
2124
+ uniform float amount;
2125
+ uniform float alpha;
2126
+
2127
+ const float PI = acos(-1.0);
2128
+ const float TAU = PI * 2.0;
2129
+ const float SQRTAU = sqrt(TAU);
2130
+
2131
+ float gaussian(float z, float u, float o) {
2132
+ return (
2133
+ (1.0 / (o * SQRTAU)) *
2134
+ (exp(-(((z - u) * (z - u)) / (2.0 * (o * o)))))
2135
+ );
2136
+ }
2137
+
2138
+ vec3 gaussgrain() {
2139
+ vec2 ps = vec2(1.0) / resolution.xy;
2140
+ vec2 uv = gl_FragCoord.xy * ps;
2141
+ float t = time;
2142
+ float seed = dot(uv, vec2(12.9898, 78.233));
2143
+ float noise = fract(sin(seed) * 43758.5453123 + t);
2144
+ noise = gaussian(noise, 0.0, 0.5);
2145
+ return vec3(noise);
2146
+ }
2147
+
2148
+ void main(void) {
2149
+ vec2 uv = vUv;
2150
+ vec4 originalColor = texture(tDiffuse, uv);
2151
+ vec3 grain = gaussgrain();
2152
+ vec3 col = originalColor.rgb + (grain * amount);
2153
+ gl_FragColor = vec4(clamp(col, 0.0, 1.0), alpha);
2154
+ }
2155
+ `
2156
+ ),
2157
+ dithering: true
2158
+ });
2159
+
2160
+ // src/rendering/composer.ts
2161
+ var Composer = class {
2162
+ constructor(scene, camera) {
2163
+ __publicField(this, "width", window.innerWidth);
2164
+ __publicField(this, "height", window.innerHeight);
2165
+ __publicField(this, "resolution", new Vector22(this.width, this.height));
2166
+ __publicField(this, "isEnvHDRI", false);
2167
+ __publicField(this, "scene");
2168
+ __publicField(this, "camera");
2169
+ __publicField(this, "renderer");
2170
+ __publicField(this, "composer");
2171
+ __publicField(this, "renderPass");
2172
+ __publicField(this, "fxaaEffect");
2173
+ __publicField(this, "fxaaPass");
2174
+ __publicField(this, "bloomEffect");
2175
+ __publicField(this, "bloomPass");
2176
+ __publicField(this, "toneMappingEffect");
2177
+ __publicField(this, "smaaEffect");
2178
+ __publicField(this, "normalPass");
2179
+ __publicField(this, "normalTextureEffect");
2180
+ __publicField(this, "ssaoEffect");
2181
+ __publicField(this, "ssaoPass");
2182
+ __publicField(this, "toneMappingPass");
2183
+ __publicField(this, "smaaPass");
2184
+ __publicField(this, "bcs", BrightnessContrastSaturation);
2185
+ __publicField(this, "bcsPass");
2186
+ __publicField(this, "gaussGrainEffect", GaussGrainEffect);
2187
+ __publicField(this, "gaussGrainPass");
2188
+ __publicField(this, "tweakPane");
2189
+ this.scene = scene;
2190
+ this.camera = camera;
2191
+ this.renderer = new WebGLRenderer3({
2192
+ powerPreference: "high-performance",
2193
+ antialias: false,
2194
+ stencil: false,
2195
+ depth: false
2196
+ });
2197
+ this.renderer.info.autoReset = false;
2198
+ this.renderer.setSize(this.width, this.height);
2199
+ this.renderer.shadowMap.enabled = true;
2200
+ this.renderer.shadowMap.type = composerValues.renderer.shadowMap;
2201
+ this.renderer.toneMapping = composerValues.renderer.toneMapping;
2202
+ this.renderer.toneMappingExposure = composerValues.renderer.exposure;
2203
+ document.body.appendChild(this.renderer.domElement);
2204
+ this.composer = new EffectComposer2(this.renderer, {
2205
+ frameBufferType: HalfFloatType
2206
+ });
2207
+ this.tweakPane = new TweakPane(this.renderer, this.scene, this.composer);
2208
+ this.renderPass = new RenderPass(this.scene, this.camera);
2209
+ this.normalPass = new NormalPass(this.scene, this.camera);
2210
+ this.normalTextureEffect = new TextureEffect({
2211
+ blendFunction: BlendFunction3.SKIP,
2212
+ texture: this.normalPass.texture
2213
+ });
2214
+ this.fxaaEffect = new FXAAEffect();
2215
+ this.bloomEffect = new BloomEffect2({
2216
+ intensity: composerValues.bloom
2217
+ });
2218
+ this.ssaoEffect = new SSAOEffect2(this.camera, this.normalPass.texture, {
2219
+ blendFunction: composerValues.ssao.blendFunction,
2220
+ distanceScaling: composerValues.ssao.distanceScaling,
2221
+ depthAwareUpsampling: composerValues.ssao.depthAwareUpsampling,
2222
+ samples: composerValues.ssao.samples,
2223
+ rings: composerValues.ssao.rings,
2224
+ luminanceInfluence: composerValues.ssao.luminanceInfluence,
2225
+ radius: composerValues.ssao.radius,
2226
+ intensity: composerValues.ssao.intensity,
2227
+ bias: composerValues.ssao.bias,
2228
+ fade: composerValues.ssao.fade,
2229
+ resolutionScale: composerValues.ssao.resolutionScale,
2230
+ color: new Color4().setRGB(composerValues.ssao.color.r, composerValues.ssao.color.g, composerValues.ssao.color.b),
2231
+ worldDistanceThreshold: composerValues.ssao.worldDistanceThreshold,
2232
+ worldDistanceFalloff: composerValues.ssao.worldDistanceFalloff,
2233
+ worldProximityThreshold: composerValues.ssao.worldProximityThreshold,
2234
+ worldProximityFalloff: composerValues.ssao.worldProximityFalloff
2235
+ });
2236
+ this.fxaaPass = new EffectPass2(this.camera, this.fxaaEffect);
2237
+ this.bloomPass = new EffectPass2(this.camera, this.bloomEffect);
2238
+ this.ssaoPass = new EffectPass2(this.camera, this.ssaoEffect, this.normalTextureEffect);
2239
+ this.toneMappingEffect = new ToneMappingEffect2({
2240
+ mode: composerValues.toneMapping.mode,
2241
+ resolution: composerValues.toneMapping.resolution,
2242
+ whitePoint: composerValues.toneMapping.whitePoint,
2243
+ middleGrey: composerValues.toneMapping.middleGrey,
2244
+ minLuminance: composerValues.toneMapping.minLuminance,
2245
+ averageLuminance: composerValues.toneMapping.averageLuminance,
2246
+ adaptationRate: composerValues.toneMapping.adaptationRate
2247
+ });
2248
+ this.smaaEffect = new SMAAEffect({
2249
+ preset: SMAAPreset.ULTRA,
2250
+ edgeDetectionMode: EdgeDetectionMode.COLOR,
2251
+ predicationMode: PredicationMode.DEPTH
2252
+ });
2253
+ this.toneMappingPass = new EffectPass2(this.camera, this.toneMappingEffect);
2254
+ this.toneMappingPass.enabled = composerValues.renderer.toneMapping === 5 || composerValues.renderer.toneMapping === 0 ? true : false;
2255
+ this.bcsPass = new ShaderPass(this.bcs, "tDiffuse");
2256
+ this.bcs.uniforms.brightness.value = composerValues.brightness;
2257
+ this.bcs.uniforms.contrast.value = composerValues.contrast;
2258
+ this.bcs.uniforms.saturation.value = composerValues.saturation;
2259
+ this.gaussGrainPass = new ShaderPass(this.gaussGrainEffect, "tDiffuse");
2260
+ this.smaaPass = new EffectPass2(this.camera, this.smaaEffect);
2261
+ this.composer.addPass(this.renderPass);
2262
+ this.composer.addPass(this.normalPass);
2263
+ this.composer.addPass(this.ssaoPass);
2264
+ this.composer.addPass(this.fxaaPass);
2265
+ this.composer.addPass(this.smaaPass);
2266
+ this.composer.addPass(this.bloomPass);
2267
+ this.composer.addPass(this.toneMappingPass);
2268
+ this.composer.addPass(this.bcsPass);
2269
+ this.composer.addPass(this.gaussGrainPass);
2270
+ this.tweakPane.setupRenderPane(
2271
+ this.ssaoEffect,
2272
+ this.toneMappingEffect,
2273
+ this.toneMappingPass,
2274
+ this.bcs,
2275
+ this.bloomEffect,
2276
+ this.gaussGrainEffect
2277
+ );
2278
+ window.addEventListener("resize", () => this.updateProjection());
2279
+ this.updateProjection();
2280
+ }
2281
+ updateProjection() {
2282
+ this.width = window.innerWidth;
2283
+ this.height = innerHeight;
2284
+ this.resolution = new Vector22(this.width, this.height);
2285
+ this.composer.setSize(this.width, this.height);
2286
+ this.renderPass.setSize(this.width, this.height);
2287
+ this.normalPass.setSize(this.width, this.height);
2288
+ this.ssaoPass.setSize(this.width, this.height);
2289
+ this.fxaaPass.setSize(this.width, this.height);
2290
+ this.smaaPass.setSize(this.width, this.height);
2291
+ this.bloomPass.setSize(this.width, this.height);
2292
+ this.toneMappingPass.setSize(this.width, this.height);
2293
+ this.gaussGrainPass.setSize(this.width, this.height);
2294
+ this.renderer.setSize(this.width, this.height);
2295
+ }
2296
+ isTweakPaneVisible() {
2297
+ return this.tweakPane.guiVisible;
2298
+ }
2299
+ render(timeManager) {
2300
+ this.renderer.info.reset();
2301
+ this.normalPass.texture.needsUpdate = true;
2302
+ this.gaussGrainEffect.uniforms.resolution.value = this.resolution;
2303
+ this.gaussGrainEffect.uniforms.time.value = timeManager.time;
2304
+ this.gaussGrainEffect.uniforms.alpha.value = 1;
2305
+ this.composer.render();
2306
+ if (this.tweakPane.guiVisible) {
2307
+ this.tweakPane.updateStats(timeManager);
2308
+ }
2309
+ }
2310
+ useHDRI(url) {
2311
+ if (this.isEnvHDRI || !this.renderer)
2312
+ return;
2313
+ const pmremGenerator = new PMREMGenerator(this.renderer);
2314
+ new RGBELoader(new LoadingManager2()).load(
2315
+ url,
2316
+ (texture) => {
2317
+ const envMap = pmremGenerator.fromEquirectangular(texture).texture;
2318
+ if (envMap) {
2319
+ envMap.colorSpace = LinearSRGBColorSpace;
2320
+ envMap.needsUpdate = true;
2321
+ this.scene.environment = envMap;
2322
+ this.scene.background = envMap;
2323
+ this.scene.backgroundIntensity = composerValues.renderer.bgIntensity;
2324
+ this.isEnvHDRI = true;
2325
+ texture.dispose();
2326
+ pmremGenerator.dispose();
2327
+ }
2328
+ },
2329
+ () => {
2330
+ },
2331
+ (error) => {
2332
+ console.error(`Can't load ${url}: ${JSON.stringify(error)}`);
2333
+ }
2334
+ );
2335
+ }
2336
+ };
2337
+
2338
+ // src/time/TimeManager.ts
2339
+ import { Clock } from "three";
2340
+ var TimeManager = class {
2341
+ constructor() {
2342
+ __publicField(this, "clock", new Clock());
2343
+ __publicField(this, "roundMagnitude", 2e5);
2344
+ __publicField(this, "maxAverageFrames", 150);
2345
+ __publicField(this, "deltaTimes", []);
2346
+ __publicField(this, "targetAverageDeltaTime", 0);
2347
+ __publicField(this, "lerpedAverageMagDelta", 0);
2348
+ __publicField(this, "fpsUpdateTime", 0);
2349
+ __publicField(this, "framesSinceLastFPSUpdate", 0);
2350
+ __publicField(this, "time", 0);
2351
+ __publicField(this, "deltaTime", 0);
2352
+ __publicField(this, "rawDeltaTime", 0);
2353
+ __publicField(this, "smoothDeltaTime", 0);
2354
+ __publicField(this, "frame", 0);
2355
+ __publicField(this, "fps", 0);
2356
+ __publicField(this, "averageFPS", 0);
2357
+ }
2358
+ update() {
2359
+ this.rawDeltaTime = this.clock.getDelta();
2360
+ this.frame++;
2361
+ this.time += this.rawDeltaTime;
2362
+ this.deltaTimes.push(this.rawDeltaTime);
2363
+ if (this.deltaTimes.length > this.maxAverageFrames)
2364
+ this.deltaTimes.shift();
2365
+ this.targetAverageDeltaTime = this.deltaTimes.reduce((prev, curr) => prev + curr, 0) / this.deltaTimes.length;
2366
+ this.lerpedAverageMagDelta += ease(
2367
+ this.targetAverageDeltaTime * this.roundMagnitude,
2368
+ this.lerpedAverageMagDelta,
2369
+ 0.12
2370
+ );
2371
+ const revertMagnitude = this.lerpedAverageMagDelta / this.roundMagnitude;
2372
+ const smoothDT = Math.round(revertMagnitude * this.roundMagnitude) / this.roundMagnitude;
2373
+ this.smoothDeltaTime = smoothDT > this.rawDeltaTime * 1.75 ? this.rawDeltaTime : smoothDT;
2374
+ this.deltaTime = this.smoothDeltaTime;
2375
+ this.framesSinceLastFPSUpdate++;
2376
+ if (this.framesSinceLastFPSUpdate >= 60) {
2377
+ this.fps = Math.round(this.framesSinceLastFPSUpdate / (this.time - this.fpsUpdateTime) * 100) / 100;
2378
+ this.fpsUpdateTime = this.time;
2379
+ this.framesSinceLastFPSUpdate = 0;
2380
+ }
2381
+ }
2382
+ };
2383
+
2384
+ // src/collisions/CollisionsManager.ts
2385
+ import {
2386
+ MMLCollisionTrigger,
2387
+ getRelativePositionAndRotationRelativeToObject
2388
+ } from "mml-web";
2389
+ import {
2390
+ Color as Color5,
2391
+ DoubleSide,
2392
+ Euler,
2393
+ FrontSide,
2394
+ Mesh as Mesh2,
2395
+ MeshStandardMaterial as MeshStandardMaterial2,
2396
+ Vector3 as Vector37
2397
+ } from "three";
2398
+ import * as BufferGeometryUtils from "three/examples/jsm/utils/BufferGeometryUtils.js";
2399
+ import { MeshBVH, MeshBVHVisualizer } from "three-mesh-bvh";
2400
+ var CollisionsManager = class {
2401
+ constructor(scene) {
2402
+ __publicField(this, "debug", false);
2403
+ __publicField(this, "scene");
2404
+ __publicField(this, "tempVector", new Vector37());
2405
+ __publicField(this, "tempVector2", new Vector37());
2406
+ __publicField(this, "collisionMeshState", /* @__PURE__ */ new Map());
2407
+ __publicField(this, "collisionTrigger");
2408
+ this.scene = scene;
2409
+ this.collisionTrigger = MMLCollisionTrigger.init();
2410
+ }
2411
+ raycastFirstDistance(ray) {
2412
+ let minimumDistance = Infinity;
2413
+ for (const [, value] of this.collisionMeshState) {
2414
+ const hit = value.meshBVH.raycast(ray, DoubleSide);
2415
+ if (hit.length > 0) {
2416
+ if (hit[0].distance < minimumDistance) {
2417
+ minimumDistance = hit[0].distance;
2418
+ }
2419
+ }
2420
+ }
2421
+ return minimumDistance;
2422
+ }
2423
+ createCollisionMeshState(group) {
2424
+ const geometries = [];
2425
+ group.traverse((child) => {
2426
+ if (child.type === "Mesh") {
2427
+ const mesh = child;
2428
+ mesh.localToWorld(new Vector37());
2429
+ mesh.updateMatrixWorld();
2430
+ const clonedGeometry = mesh.geometry.clone();
2431
+ clonedGeometry.applyMatrix4(mesh.matrixWorld);
2432
+ for (const key in clonedGeometry.attributes) {
2433
+ if (key !== "position") {
2434
+ clonedGeometry.deleteAttribute(key);
2435
+ }
2436
+ }
2437
+ if (clonedGeometry.index) {
2438
+ geometries.push(clonedGeometry.toNonIndexed());
2439
+ } else {
2440
+ geometries.push(clonedGeometry);
2441
+ }
2442
+ }
2443
+ });
2444
+ const newBufferGeometry = BufferGeometryUtils.mergeGeometries(geometries);
2445
+ const meshBVH = new MeshBVH(newBufferGeometry);
2446
+ if (!this.debug) {
2447
+ return { source: group, visualizer: null, meshBVH };
2448
+ }
2449
+ const mergedMesh = new Mesh2(
2450
+ newBufferGeometry,
2451
+ new MeshStandardMaterial2({ color: 16711680, side: FrontSide, wireframe: true })
2452
+ );
2453
+ mergedMesh.geometry.boundsTree = meshBVH;
2454
+ const visualizer = new MeshBVHVisualizer(mergedMesh, 3);
2455
+ visualizer.edgeMaterial.color = new Color5(255);
2456
+ visualizer.update();
2457
+ return { source: group, visualizer, meshBVH };
2458
+ }
2459
+ addMeshesGroup(group, mElement) {
2460
+ if (mElement) {
2461
+ this.collisionTrigger.addCollider(group, mElement);
2462
+ }
2463
+ const meshState = this.createCollisionMeshState(group);
2464
+ if (meshState.visualizer) {
2465
+ this.scene.add(meshState.visualizer);
2466
+ }
2467
+ this.collisionMeshState.set(group, meshState);
2468
+ }
2469
+ updateMeshesGroup(group) {
2470
+ const meshState = this.collisionMeshState.get(group);
2471
+ if (meshState) {
2472
+ const newMeshState = this.createCollisionMeshState(group);
2473
+ if (meshState.visualizer) {
2474
+ this.scene.remove(meshState.visualizer);
2475
+ }
2476
+ if (newMeshState.visualizer) {
2477
+ this.scene.add(newMeshState.visualizer);
2478
+ }
2479
+ this.collisionMeshState.set(group, newMeshState);
2480
+ }
2481
+ }
2482
+ removeMeshesGroup(group) {
2483
+ this.collisionTrigger.removeCollider(group);
2484
+ const meshState = this.collisionMeshState.get(group);
2485
+ if (meshState) {
2486
+ if (meshState.visualizer) {
2487
+ this.scene.remove(meshState.visualizer);
2488
+ }
2489
+ this.collisionMeshState.delete(group);
2490
+ }
2491
+ }
2492
+ applyCollider(tempSegment, radius, boundingBox, meshState) {
2493
+ let collisionPosition = null;
2494
+ meshState.meshBVH.shapecast({
2495
+ intersectsBounds: (box) => box.intersectsBox(boundingBox),
2496
+ intersectsTriangle: (tri) => {
2497
+ const triPoint = this.tempVector;
2498
+ const capsulePoint = this.tempVector2;
2499
+ const distance = tri.closestPointToSegment(tempSegment, triPoint, capsulePoint);
2500
+ if (distance < radius) {
2501
+ const depth = radius - distance;
2502
+ collisionPosition = new Vector37().copy(capsulePoint);
2503
+ const direction = capsulePoint.sub(triPoint).normalize();
2504
+ tempSegment.start.addScaledVector(direction, depth);
2505
+ tempSegment.end.addScaledVector(direction, depth);
2506
+ }
2507
+ }
2508
+ });
2509
+ return collisionPosition;
2510
+ }
2511
+ applyColliders(tempSegment, radius, boundingBox) {
2512
+ let collidedElements = null;
2513
+ for (const meshState of this.collisionMeshState.values()) {
2514
+ const collisionPosition = this.applyCollider(tempSegment, radius, boundingBox, meshState);
2515
+ if (collisionPosition) {
2516
+ if (collidedElements === null) {
2517
+ collidedElements = /* @__PURE__ */ new Map();
2518
+ }
2519
+ const relativePosition = getRelativePositionAndRotationRelativeToObject(
2520
+ {
2521
+ position: collisionPosition,
2522
+ rotation: new Euler()
2523
+ },
2524
+ meshState.source
2525
+ );
2526
+ collidedElements.set(meshState.source, {
2527
+ position: relativePosition.position
2528
+ });
2529
+ }
2530
+ }
2531
+ this.collisionTrigger.setCurrentCollisions(collidedElements);
2532
+ }
2533
+ };
2534
+ export {
2535
+ AnimationState,
2536
+ CameraManager,
2537
+ CharacterManager,
2538
+ CollisionsManager,
2539
+ Composer,
2540
+ KeyInputManager,
2541
+ MMLCompositionScene,
2542
+ TimeManager
2543
+ };
2544
+ //# sourceMappingURL=index.js.map