@mml-io/3d-web-client-core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.js ADDED
@@ -0,0 +1,1476 @@
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, 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
+
31
+ // src/camera/CameraManager.ts
32
+ var CameraManager = class {
33
+ constructor() {
34
+ __publicField(this, "camera");
35
+ __publicField(this, "dragging", false);
36
+ __publicField(this, "target", new Vector32(0, 1.55, 0));
37
+ __publicField(this, "targetDistance");
38
+ __publicField(this, "maxTargetDistance", 20);
39
+ __publicField(this, "distance");
40
+ __publicField(this, "targetPhi", Math.PI / 2);
41
+ __publicField(this, "phi", Math.PI / 2);
42
+ __publicField(this, "targetTheta", -Math.PI / 2);
43
+ __publicField(this, "theta", -Math.PI / 2);
44
+ __publicField(this, "hadTarget", false);
45
+ this.camera = new PerspectiveCamera(80, window.innerWidth / window.innerHeight, 0.1, 2e3);
46
+ this.camera.position.set(0, 1.4, 3);
47
+ this.targetDistance = 2.5;
48
+ this.distance = this.targetDistance;
49
+ document.addEventListener("mousedown", this.onMouseDown.bind(this));
50
+ document.addEventListener("mouseup", this.onMouseUp.bind(this));
51
+ document.addEventListener("mousemove", this.onMouseMove.bind(this));
52
+ document.addEventListener("wheel", this.onMouseWheel.bind(this));
53
+ window.addEventListener("resize", this.onResize.bind(this));
54
+ }
55
+ onResize() {
56
+ const width = window.innerWidth;
57
+ const height = window.innerHeight;
58
+ this.camera.aspect = width / height;
59
+ this.camera.updateProjectionMatrix();
60
+ }
61
+ onMouseDown(_event) {
62
+ this.dragging = true;
63
+ }
64
+ onMouseUp(_event) {
65
+ this.dragging = false;
66
+ }
67
+ onMouseMove(event) {
68
+ if (!this.dragging) {
69
+ return;
70
+ }
71
+ if (this.targetTheta === null || this.targetPhi === null) {
72
+ return;
73
+ }
74
+ this.targetTheta += event.movementX * 0.01;
75
+ this.targetPhi -= event.movementY * 0.01;
76
+ this.targetPhi = Math.max(Math.PI * 0.1, Math.min(Math.PI - Math.PI * 0.1, this.targetPhi));
77
+ this.targetPhi = Math.min(Math.PI * 0.7, this.targetPhi);
78
+ }
79
+ onMouseWheel(event) {
80
+ const scrollAmount = event.deltaY * 0.01;
81
+ this.targetDistance += scrollAmount;
82
+ this.targetDistance = Math.max(0, this.targetDistance);
83
+ this.targetDistance = Math.min(this.targetDistance, this.maxTargetDistance);
84
+ }
85
+ setTarget(target) {
86
+ this.target.copy(target);
87
+ if (!this.hadTarget) {
88
+ this.hadTarget = true;
89
+ this.reverseUpdateFromPositions();
90
+ }
91
+ }
92
+ reverseUpdateFromPositions() {
93
+ if (this.phi === null || this.theta == null)
94
+ return;
95
+ const dx = this.camera.position.x - this.target.x;
96
+ const dy = this.camera.position.y - this.target.y;
97
+ const dz = this.camera.position.z - this.target.z;
98
+ this.targetDistance = Math.sqrt(dx * dx + dy * dy + dz * dz);
99
+ this.targetTheta = (this.theta + 2 * Math.PI) % (2 * Math.PI);
100
+ this.targetPhi = Math.max(0, Math.min(Math.PI, this.phi));
101
+ this.phi = this.targetPhi;
102
+ this.theta = this.targetTheta;
103
+ this.distance = this.targetDistance;
104
+ }
105
+ update() {
106
+ if (this.target === null) {
107
+ return;
108
+ }
109
+ if (this.phi !== null && this.targetPhi !== null && this.theta !== null && this.targetTheta !== null) {
110
+ this.distance += ease(this.targetDistance, this.distance, 0.02);
111
+ this.distance = Math.min(this.distance, this.maxTargetDistance);
112
+ this.phi += ease(this.targetPhi, this.phi, 0.07);
113
+ this.theta += ease(this.targetTheta, this.theta, 0.07);
114
+ const x = this.target.x + this.distance * Math.sin(this.phi) * Math.cos(this.theta);
115
+ const y = this.target.y + this.distance * Math.cos(this.phi);
116
+ const z = this.target.z + this.distance * Math.sin(this.phi) * Math.sin(this.theta);
117
+ this.camera.position.set(x, y, z);
118
+ this.camera.lookAt(this.target);
119
+ }
120
+ }
121
+ };
122
+
123
+ // src/character/Character.ts
124
+ import { Color as Color2, Vector3 as Vector34 } from "three";
125
+
126
+ // src/character/CharacterModel.ts
127
+ import {
128
+ AnimationClip as AnimationClip2,
129
+ AnimationMixer,
130
+ LoopRepeat,
131
+ MeshStandardMaterial,
132
+ Object3D as Object3D2
133
+ } from "three";
134
+
135
+ // src/character/CharacterMaterial.ts
136
+ import { Color, MeshPhysicalMaterial, UniformsUtils } from "three";
137
+
138
+ // src/rendering/shaders/bayer-dither.ts
139
+ var bayerDither = (
140
+ /* glsl */
141
+ `
142
+ const mat4 bayertl = mat4(
143
+ 0.0 / 64.0, 32.0 / 64.0, 8.0 / 64.0, 40.0 / 64.0,
144
+ 48.0 / 64.0, 16.0 / 64.0, 56.0 / 64.0, 24.0 / 64.0,
145
+ 12.0 / 64.0, 44.0 / 64.0, 4.0 / 64.0, 36.0 / 64.0,
146
+ 60.0 / 64.0, 28.0 / 64.0, 52.0 / 64.0, 20.0 / 64.0
147
+ );
148
+
149
+ const mat4 bayertr = mat4(
150
+ 2.0 / 64.0, 34.0 / 64.0, 10.0 / 64.0, 42.0 / 64.0,
151
+ 50.0 / 64.0, 18.0 / 64.0, 58.0 / 64.0, 26.0 / 64.0,
152
+ 14.0 / 64.0, 46.0 / 64.0, 6.0 / 64.0, 38.0 / 64.0,
153
+ 62.0 / 64.0, 30.0 / 64.0, 54.0 / 64.0, 22.0 / 64.0
154
+ );
155
+
156
+ const mat4 bayerbl = mat4(
157
+ 3.0 / 64.0, 35.0 / 64.0, 11.0 / 64.0, 43.0 / 64.0,
158
+ 51.0 / 64.0, 19.0 / 64.0, 59.0 / 64.0, 27.0 / 64.0,
159
+ 15.0 / 64.0, 47.0 / 64.0, 7.0 / 64.0, 39.0 / 64.0,
160
+ 63.0 / 64.0, 31.0 / 64.0, 55.0 / 64.0, 23.0 / 64.0
161
+ );
162
+
163
+ const mat4 bayerbr = mat4(
164
+ 1.0 / 64.0, 33.0 / 64.0, 9.0 / 64.0, 41.0 / 64.0,
165
+ 49.0 / 64.0, 17.0 / 64.0, 57.0 / 64.0, 25.0 / 64.0,
166
+ 13.0 / 64.0, 45.0 / 64.0, 5.0 / 64.0, 37.0 / 64.0,
167
+ 61.0 / 64.0, 29.0 / 64.0, 53.0 / 64.0, 21.0 / 64.0
168
+ );
169
+
170
+ float bayerDither(mat4 m, ivec2 p) {
171
+ if (p.y == 0) {
172
+ if (p.x == 0) { return m[0][0]; }
173
+ else if (p.x == 1) { return m[1][0]; }
174
+ else if (p.x == 2) { return m[2][0]; }
175
+ else { return m[3][0]; }
176
+ } else if (p.y == 1) {
177
+ if (p.x == 0) { return m[0][1]; }
178
+ else if (p.x == 1) { return m[1][1]; }
179
+ else if (p.x == 2) { return m[2][1]; }
180
+ else { return m[3][1]; }
181
+ } else if (p.y == 2) {
182
+ if (p.x == 0) { return m[0][1]; }
183
+ else if (p.x == 1) { return m[1][2]; }
184
+ else if (p.x == 2) { return m[2][2]; }
185
+ else { return m[3][2]; }
186
+ } else {
187
+ if (p.x == 0) { return m[0][3]; }
188
+ else if (p.x == 1) { return m[1][3]; }
189
+ else if (p.x == 2) { return m[2][3]; }
190
+ else { return m[3][3]; }
191
+ }
192
+ }
193
+ `
194
+ );
195
+
196
+ // src/rendering/shaders/shader-helpers.ts
197
+ function injectBeforeMain(shaderSource, codeToInject) {
198
+ return shaderSource.replace(
199
+ "void main() {",
200
+ `
201
+
202
+ ${codeToInject}
203
+
204
+ void main() {`
205
+ );
206
+ }
207
+ function injectInsideMain(shaderSource, codeToInject) {
208
+ return shaderSource.replace(
209
+ "void main() {",
210
+ `void main() {
211
+
212
+ ${codeToInject}
213
+
214
+ `
215
+ );
216
+ }
217
+ function injectBefore(shaderSource, before, codeToInject) {
218
+ return shaderSource.replace(
219
+ before,
220
+ `
221
+ ${codeToInject}
222
+
223
+ ${before}
224
+ `
225
+ );
226
+ }
227
+
228
+ // src/character/CharacterMaterial.ts
229
+ var CharacterMaterial = class extends MeshPhysicalMaterial {
230
+ constructor() {
231
+ super();
232
+ __publicField(this, "uniforms", {});
233
+ __publicField(this, "colorsCube216", []);
234
+ this.color = new Color(16777215);
235
+ this.transmission = 0.5;
236
+ this.metalness = 0.5;
237
+ this.roughness = 0.3;
238
+ this.ior = 2;
239
+ this.thickness = 0.1;
240
+ this.specularColor = new Color(30719);
241
+ this.specularIntensity = 0.1;
242
+ this.envMapIntensity = 1.8;
243
+ this.sheenColor = new Color(7798903);
244
+ this.sheen = 0.35;
245
+ this.onBeforeCompile = (shader) => {
246
+ this.uniforms = UniformsUtils.clone(shader.uniforms);
247
+ this.uniforms.nearClip = { value: 0.01 };
248
+ this.uniforms.farClip = { value: 1e3 };
249
+ this.uniforms.ditheringNear = { value: 0.25 };
250
+ this.uniforms.ditheringRange = { value: 0.5 };
251
+ this.uniforms.time = { value: 0 };
252
+ this.uniforms.diffuseRandomColor = { value: new Color() };
253
+ shader.uniforms = this.uniforms;
254
+ shader.vertexShader = injectBeforeMain(shader.vertexShader, "varying vec2 vUv;");
255
+ shader.vertexShader = injectInsideMain(shader.vertexShader, "vUv = uv;");
256
+ shader.fragmentShader = injectBeforeMain(
257
+ shader.fragmentShader,
258
+ /* glsl */
259
+ `
260
+ varying vec2 vUv;
261
+ uniform float nearClip;
262
+ uniform float farClip;
263
+ uniform float ditheringNear;
264
+ uniform float ditheringRange;
265
+ uniform float time;
266
+ uniform vec3 diffuseRandomColor;
267
+ ${bayerDither}
268
+ `
269
+ );
270
+ shader.fragmentShader = injectBefore(
271
+ shader.fragmentShader,
272
+ "#include <output_fragment>",
273
+ /* glsl */
274
+ `
275
+ float distance = length(vWorldPosition - cameraPosition);
276
+ float normalizedDistance = (distance - nearClip) / (farClip - nearClip);
277
+ ivec2 p = ivec2(mod(gl_FragCoord.xy, 8.0));
278
+ float d = 0.0;
279
+ if (p.x <= 3 && p.y <= 3) {
280
+ d = bayerDither(bayertl, p);
281
+ } else if (p.x > 3 && p.y <= 3) {
282
+ d = bayerDither(bayertr, p - ivec2(4, 0));
283
+ } else if (p.x <= 3 && p.y > 3) {
284
+ d = bayerDither(bayerbl, p - ivec2(0, 4));
285
+ } else if (p.x > 3 && p.y > 3) {
286
+ d = bayerDither(bayerbr, p - ivec2(4, 4));
287
+ }
288
+ if (distance <= ditheringNear + d * ditheringRange) discard;
289
+ vec2 suv = vUv;
290
+ float s = clamp(0.35 + 0.35 * sin(5.0 * -time + suv.y * 500.0), 0.0, 1.0);
291
+ float scanLines = pow(s, 1.33);
292
+ outgoingLight *= diffuseRandomColor;
293
+ outgoingLight += smoothstep(0.1, 0.0, scanLines) * 0.1;
294
+ `
295
+ );
296
+ };
297
+ this.generateColorCube();
298
+ }
299
+ generateColorCube() {
300
+ const saturation = 0.7;
301
+ const lightness = 0.8;
302
+ const goldenRatioConjugate = 0.618033988749895;
303
+ let hue = 0;
304
+ for (let i = 0; i < 216; i++) {
305
+ const color = new Color();
306
+ color.setHSL(hue, saturation, lightness);
307
+ this.colorsCube216.push(color);
308
+ hue = (hue + goldenRatioConjugate) % 1;
309
+ }
310
+ }
311
+ };
312
+
313
+ // src/character/CharacterState.ts
314
+ var AnimationState = /* @__PURE__ */ ((AnimationState2) => {
315
+ AnimationState2[AnimationState2["idle"] = 0] = "idle";
316
+ AnimationState2[AnimationState2["walking"] = 1] = "walking";
317
+ AnimationState2[AnimationState2["running"] = 2] = "running";
318
+ AnimationState2[AnimationState2["jumpToAir"] = 3] = "jumpToAir";
319
+ AnimationState2[AnimationState2["air"] = 4] = "air";
320
+ AnimationState2[AnimationState2["airToGround"] = 5] = "airToGround";
321
+ return AnimationState2;
322
+ })(AnimationState || {});
323
+
324
+ // src/character/ModelLoader.ts
325
+ import { LoadingManager } from "three";
326
+ import { FBXLoader } from "three/examples/jsm/loaders/FBXLoader.js";
327
+ import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
328
+ var ModelLoader = class {
329
+ constructor() {
330
+ __publicField(this, "debug", false);
331
+ __publicField(this, "loadingManager");
332
+ __publicField(this, "fbxLoader");
333
+ __publicField(this, "gltfLoader");
334
+ this.loadingManager = new LoadingManager();
335
+ this.fbxLoader = new FBXLoader(this.loadingManager);
336
+ this.gltfLoader = new GLTFLoader(this.loadingManager);
337
+ }
338
+ async load(fileUrl, fileType) {
339
+ const extension = fileUrl.split(".").pop();
340
+ if (typeof extension === "undefined") {
341
+ console.error(`Unable to identify model type from ${fileUrl}`);
342
+ return;
343
+ }
344
+ const name = fileUrl.split("/").pop().replace(`.${extension}`, "");
345
+ if (this.debug) {
346
+ console.log(`Loading ${extension} model ${name} from ${fileUrl}`);
347
+ }
348
+ if (["gltf", "glb"].includes(extension)) {
349
+ return new Promise((resolve, reject) => {
350
+ this.gltfLoader.load(
351
+ fileUrl,
352
+ (object) => {
353
+ if (fileType === "model") {
354
+ resolve(object.scene);
355
+ } else if (fileType === "animation") {
356
+ resolve(object.animations[0]);
357
+ } else {
358
+ const error = `Trying to load unknown ${fileType} type of element from file ${fileUrl}`;
359
+ console.error(error);
360
+ reject(error);
361
+ }
362
+ },
363
+ void 0,
364
+ (error) => {
365
+ console.error(`Error loading GL(B|TF) from ${fileUrl}: ${error}`);
366
+ reject(error);
367
+ }
368
+ );
369
+ });
370
+ } else if (extension === "fbx") {
371
+ return new Promise((resolve, reject) => {
372
+ this.fbxLoader.load(
373
+ fileUrl,
374
+ (object) => {
375
+ resolve(object);
376
+ },
377
+ void 0,
378
+ (error) => {
379
+ console.error(`Error loading FBX from ${fileUrl}: ${error}`);
380
+ reject(error);
381
+ }
382
+ );
383
+ });
384
+ }
385
+ }
386
+ };
387
+
388
+ // src/character/CharacterModel.ts
389
+ var CharacterModel = class {
390
+ constructor(characterDescription) {
391
+ this.characterDescription = characterDescription;
392
+ __publicField(this, "modelLoader", new ModelLoader());
393
+ __publicField(this, "mesh", null);
394
+ __publicField(this, "material", new CharacterMaterial());
395
+ __publicField(this, "animations", {});
396
+ __publicField(this, "animationMixer", null);
397
+ __publicField(this, "currentAnimation", 0 /* idle */);
398
+ }
399
+ async init() {
400
+ await this.loadMainMesh();
401
+ await this.setAnimationFromFile(
402
+ this.characterDescription.idleAnimationFileUrl,
403
+ 0 /* idle */
404
+ );
405
+ await this.setAnimationFromFile(
406
+ this.characterDescription.jogAnimationFileUrl,
407
+ 1 /* walking */
408
+ );
409
+ await this.setAnimationFromFile(
410
+ this.characterDescription.sprintAnimationFileUrl,
411
+ 2 /* running */
412
+ );
413
+ this.applyMaterialToAllSkinnedMeshes(this.material);
414
+ }
415
+ updateAnimation(targetAnimation, deltaTime) {
416
+ if (this.currentAnimation !== targetAnimation) {
417
+ this.transitionToAnimation(targetAnimation);
418
+ }
419
+ this.animationMixer?.update(deltaTime);
420
+ }
421
+ hideMaterialByMeshName(meshName) {
422
+ if (!this.mesh)
423
+ return;
424
+ this.mesh.traverse((child) => {
425
+ if (child.type === "SkinnedMesh" && child.name === meshName) {
426
+ child.material = new MeshStandardMaterial({
427
+ color: 16711680,
428
+ transparent: true,
429
+ opacity: 0
430
+ });
431
+ }
432
+ });
433
+ }
434
+ setShadows(mesh, castShadow = true, receiveShadow = true) {
435
+ mesh.traverse((child) => {
436
+ if (child.type === "SkinnedMesh") {
437
+ child.castShadow = castShadow;
438
+ child.receiveShadow = receiveShadow;
439
+ }
440
+ });
441
+ }
442
+ applyMaterialToAllSkinnedMeshes(material) {
443
+ if (!this.mesh)
444
+ return;
445
+ this.mesh.traverse((child) => {
446
+ if (child.type === "SkinnedMesh") {
447
+ child.material = material;
448
+ }
449
+ });
450
+ }
451
+ initAnimationMixer() {
452
+ if (this.animationMixer !== null || this.mesh === null)
453
+ return;
454
+ this.animationMixer = new AnimationMixer(this.mesh);
455
+ }
456
+ async loadMainMesh() {
457
+ const mainMeshUrl = this.characterDescription.meshFileUrl;
458
+ const scale = this.characterDescription.modelScale;
459
+ const extension = mainMeshUrl.split(".").pop();
460
+ const name = mainMeshUrl.split("/").pop().replace(`.${extension}`, "");
461
+ const mainMesh = await this.modelLoader.load(mainMeshUrl, "model");
462
+ if (typeof mainMesh !== "undefined") {
463
+ this.mesh = new Object3D2();
464
+ const model = mainMesh;
465
+ model.position.set(0, -0.35, 0);
466
+ this.mesh.add(model);
467
+ this.mesh.name = name;
468
+ this.mesh.scale.set(scale, scale, scale);
469
+ this.setShadows(this.mesh);
470
+ }
471
+ }
472
+ async setAnimationFromFile(animationFileUrl, animationType) {
473
+ return new Promise(async (resolve, reject) => {
474
+ this.initAnimationMixer();
475
+ const animation = await this.modelLoader.load(animationFileUrl, "animation");
476
+ if (typeof animation !== "undefined" && animation instanceof AnimationClip2) {
477
+ this.animations[animationType] = this.animationMixer.clipAction(animation);
478
+ this.animations[animationType].stop();
479
+ if (animationType === 0 /* idle */) {
480
+ this.animations[animationType].play();
481
+ }
482
+ resolve();
483
+ } else {
484
+ reject(`failed to load ${animationType} from ${animationFileUrl}`);
485
+ }
486
+ });
487
+ }
488
+ transitionToAnimation(targetAnimation, transitionDuration = 0.21) {
489
+ if (!this.mesh || this.currentAnimation === null)
490
+ return;
491
+ const currentAction = this.animations[this.currentAnimation];
492
+ const targetAction = this.animations[targetAnimation];
493
+ if (!targetAction)
494
+ return;
495
+ if (currentAction) {
496
+ currentAction.enabled = true;
497
+ currentAction.fadeOut(transitionDuration);
498
+ }
499
+ if (!targetAction.isRunning())
500
+ targetAction.play();
501
+ targetAction.setLoop(LoopRepeat, Infinity);
502
+ targetAction.enabled = true;
503
+ targetAction.fadeIn(transitionDuration);
504
+ this.currentAnimation = targetAnimation;
505
+ }
506
+ };
507
+
508
+ // src/character/LocalController.ts
509
+ import { Box3, Line3, Matrix4, Quaternion, Vector3 as Vector33 } from "three";
510
+ var LocalController = class {
511
+ constructor(model, id, collisionsManager, keyInputManager, cameraManager, timeManager) {
512
+ this.model = model;
513
+ this.id = id;
514
+ this.collisionsManager = collisionsManager;
515
+ this.keyInputManager = keyInputManager;
516
+ this.cameraManager = cameraManager;
517
+ this.timeManager = timeManager;
518
+ __publicField(this, "collisionDetectionSteps", 15);
519
+ __publicField(this, "capsuleInfo", {
520
+ radius: 0.4,
521
+ segment: new Line3(new Vector33(), new Vector33(0, 1.05, 0))
522
+ });
523
+ __publicField(this, "characterOnGround", false);
524
+ __publicField(this, "characterVelocity", new Vector33());
525
+ __publicField(this, "gravity", -20);
526
+ __publicField(this, "upVector", new Vector33(0, 1, 0));
527
+ __publicField(this, "rotationOffset", 0);
528
+ __publicField(this, "azimuthalAngle", 0);
529
+ __publicField(this, "tempBox", new Box3());
530
+ __publicField(this, "tempMatrix", new Matrix4());
531
+ __publicField(this, "tempSegment", new Line3());
532
+ __publicField(this, "tempVector", new Vector33());
533
+ __publicField(this, "tempVector2", new Vector33());
534
+ __publicField(this, "jumpInput", false);
535
+ __publicField(this, "jumpForce", 10);
536
+ __publicField(this, "canJump", true);
537
+ __publicField(this, "inputDirections", {
538
+ forward: false,
539
+ backward: false,
540
+ left: false,
541
+ right: false
542
+ });
543
+ __publicField(this, "runInput", false);
544
+ __publicField(this, "thirdPersonCamera", null);
545
+ __publicField(this, "speed", 0);
546
+ __publicField(this, "targetSpeed", 0);
547
+ __publicField(this, "networkState", {
548
+ id: 0,
549
+ position: { x: 0, y: 0, z: 0 },
550
+ rotation: { quaternionY: 0, quaternionW: 0 },
551
+ state: 0 /* idle */
552
+ });
553
+ }
554
+ update() {
555
+ if (!this.model?.mesh || !this.model?.animationMixer)
556
+ return;
557
+ if (!this.thirdPersonCamera)
558
+ this.thirdPersonCamera = this.cameraManager.camera;
559
+ const movementKeysPressed = this.keyInputManager.isMovementKeyPressed();
560
+ const forward = this.keyInputManager.isKeyPressed("w");
561
+ const backward = this.keyInputManager.isKeyPressed("s");
562
+ const left = this.keyInputManager.isKeyPressed("a");
563
+ const right = this.keyInputManager.isKeyPressed("d");
564
+ this.inputDirections = { forward, backward, left, right };
565
+ this.jumpInput = this.keyInputManager.isJumping();
566
+ this.runInput = this.keyInputManager.isShiftPressed();
567
+ if (movementKeysPressed) {
568
+ const targetAnimation = this.getTargetAnimation();
569
+ this.model.updateAnimation(targetAnimation, this.timeManager.deltaTime);
570
+ } else {
571
+ this.model.updateAnimation(0 /* idle */, this.timeManager.deltaTime);
572
+ }
573
+ if (Object.values(this.inputDirections).some((v) => v)) {
574
+ this.updateRotation();
575
+ }
576
+ for (let i = 0; i < this.collisionDetectionSteps; i++) {
577
+ this.updatePosition(this.timeManager.deltaTime / this.collisionDetectionSteps, i);
578
+ }
579
+ if (this.model.mesh.position.y < 0) {
580
+ this.resetPosition();
581
+ }
582
+ this.updateNetworkState();
583
+ }
584
+ getTargetAnimation() {
585
+ const { forward, backward, left, right } = this.inputDirections;
586
+ const hasAnyDirection = forward || backward || left || right;
587
+ const isRunning = this.runInput && hasAnyDirection;
588
+ const conflictingDirections = forward && backward || left && right;
589
+ if (conflictingDirections)
590
+ return 0 /* idle */;
591
+ return hasAnyDirection ? isRunning ? 2 /* running */ : 1 /* walking */ : 0 /* idle */;
592
+ }
593
+ updateRotationOffset() {
594
+ const { forward, backward, left, right } = this.inputDirections;
595
+ if (left && right || forward && backward)
596
+ return;
597
+ if (forward) {
598
+ this.rotationOffset = Math.PI;
599
+ if (left)
600
+ this.rotationOffset = Math.PI + Math.PI / 4;
601
+ if (right)
602
+ this.rotationOffset = Math.PI - Math.PI / 4;
603
+ } else if (backward) {
604
+ this.rotationOffset = Math.PI * 2;
605
+ if (left)
606
+ this.rotationOffset = -Math.PI * 2 - Math.PI / 4;
607
+ if (right)
608
+ this.rotationOffset = Math.PI * 2 + Math.PI / 4;
609
+ } else if (left) {
610
+ this.rotationOffset = Math.PI * -0.5;
611
+ } else if (right) {
612
+ this.rotationOffset = Math.PI * 0.5;
613
+ }
614
+ }
615
+ updateAzimuthalAngle() {
616
+ if (!this.thirdPersonCamera || !this.model?.mesh)
617
+ return;
618
+ this.azimuthalAngle = Math.atan2(
619
+ this.thirdPersonCamera.position.x - this.model.mesh.position.x,
620
+ this.thirdPersonCamera.position.z - this.model.mesh.position.z
621
+ );
622
+ }
623
+ updateRotation() {
624
+ if (!this.thirdPersonCamera || !this.model?.mesh)
625
+ return;
626
+ this.updateRotationOffset();
627
+ this.updateAzimuthalAngle();
628
+ const rotationQuaternion = new Quaternion();
629
+ rotationQuaternion.setFromAxisAngle(this.upVector, this.azimuthalAngle + this.rotationOffset);
630
+ this.model.mesh.quaternion.rotateTowards(rotationQuaternion, 0.07);
631
+ }
632
+ addScaledVectorToCharacter(deltaTime) {
633
+ if (!this.model?.mesh)
634
+ return;
635
+ this.model.mesh.position.addScaledVector(this.tempVector, this.speed * deltaTime);
636
+ }
637
+ updatePosition(deltaTime, _iter) {
638
+ if (!this.model?.mesh)
639
+ return;
640
+ const { forward, backward, left, right } = this.inputDirections;
641
+ this.targetSpeed = this.runInput ? 14 : 8;
642
+ this.speed += ease(this.targetSpeed, this.speed, 0.07);
643
+ if (this.characterOnGround) {
644
+ this.canJump = true;
645
+ if (this.jumpInput && this.canJump) {
646
+ this.characterVelocity.y += this.jumpForce;
647
+ this.canJump = false;
648
+ } else {
649
+ this.characterVelocity.y = deltaTime * this.gravity;
650
+ }
651
+ } else {
652
+ this.characterVelocity.y += deltaTime * this.gravity;
653
+ this.canJump = false;
654
+ }
655
+ this.model.mesh.position.addScaledVector(this.characterVelocity, deltaTime);
656
+ if (forward) {
657
+ this.tempVector.set(0, 0, -1).applyAxisAngle(this.upVector, this.azimuthalAngle);
658
+ this.addScaledVectorToCharacter(deltaTime);
659
+ }
660
+ if (backward) {
661
+ this.tempVector.set(0, 0, 1).applyAxisAngle(this.upVector, this.azimuthalAngle);
662
+ this.addScaledVectorToCharacter(deltaTime);
663
+ }
664
+ if (left) {
665
+ this.tempVector.set(-1, 0, 0).applyAxisAngle(this.upVector, this.azimuthalAngle);
666
+ this.addScaledVectorToCharacter(deltaTime);
667
+ }
668
+ if (right) {
669
+ this.tempVector.set(1, 0, 0).applyAxisAngle(this.upVector, this.azimuthalAngle);
670
+ this.addScaledVectorToCharacter(deltaTime);
671
+ }
672
+ this.model.mesh.updateMatrixWorld();
673
+ this.tempBox.makeEmpty();
674
+ this.tempSegment.copy(this.capsuleInfo.segment);
675
+ this.tempSegment.start.applyMatrix4(this.model.mesh.matrixWorld).applyMatrix4(this.tempMatrix);
676
+ this.tempSegment.end.applyMatrix4(this.model.mesh.matrixWorld).applyMatrix4(this.tempMatrix);
677
+ this.tempBox.expandByPoint(this.tempSegment.start);
678
+ this.tempBox.expandByPoint(this.tempSegment.end);
679
+ this.tempBox.min.subScalar(this.capsuleInfo.radius);
680
+ this.tempBox.max.addScalar(this.capsuleInfo.radius);
681
+ this.collisionsManager.applyColliders(this.tempSegment, this.capsuleInfo.radius, this.tempBox);
682
+ const newPosition = this.tempVector;
683
+ newPosition.copy(this.tempSegment.start);
684
+ const deltaVector = this.tempVector2;
685
+ deltaVector.subVectors(newPosition, this.model.mesh.position);
686
+ const offset = Math.max(0, deltaVector.length() - 1e-5);
687
+ deltaVector.normalize().multiplyScalar(offset);
688
+ this.model.mesh.position.add(deltaVector);
689
+ this.characterOnGround = deltaVector.y > Math.abs(deltaTime * this.characterVelocity.y * 0.25);
690
+ if (this.characterOnGround) {
691
+ this.characterVelocity.set(0, 0, 0);
692
+ } else {
693
+ deltaVector.normalize();
694
+ this.characterVelocity.addScaledVector(deltaVector, -deltaVector.dot(this.characterVelocity));
695
+ }
696
+ }
697
+ updateNetworkState() {
698
+ if (!this.model?.mesh)
699
+ return;
700
+ const characterQuaternion = this.model.mesh.getWorldQuaternion(new Quaternion());
701
+ const positionUpdate = new Vector33(
702
+ this.model.mesh.position.x,
703
+ this.model.mesh.position.y,
704
+ this.model.mesh.position.z
705
+ );
706
+ this.networkState = {
707
+ id: this.id,
708
+ position: positionUpdate,
709
+ rotation: { quaternionY: characterQuaternion?.y, quaternionW: characterQuaternion?.w },
710
+ state: this.model.currentAnimation
711
+ };
712
+ }
713
+ resetPosition() {
714
+ if (!this.model?.mesh)
715
+ return;
716
+ this.characterVelocity.y = 0;
717
+ this.model.mesh.position.y = 5;
718
+ this.characterOnGround = false;
719
+ }
720
+ };
721
+
722
+ // src/character/Character.ts
723
+ var Character = class {
724
+ constructor(characterDescription, id, isLocal, modelLoadedCallback, collisionsManager, keyInputManager, cameraManager, timeManager) {
725
+ this.characterDescription = characterDescription;
726
+ this.id = id;
727
+ this.isLocal = isLocal;
728
+ this.modelLoadedCallback = modelLoadedCallback;
729
+ this.collisionsManager = collisionsManager;
730
+ this.keyInputManager = keyInputManager;
731
+ this.cameraManager = cameraManager;
732
+ this.timeManager = timeManager;
733
+ __publicField(this, "controller", null);
734
+ __publicField(this, "name", null);
735
+ __publicField(this, "model", null);
736
+ __publicField(this, "color", new Color2());
737
+ __publicField(this, "position", new Vector34());
738
+ this.load();
739
+ }
740
+ async load() {
741
+ this.model = new CharacterModel(this.characterDescription);
742
+ await this.model.init();
743
+ this.color = this.model.material.colorsCube216[this.id];
744
+ if (this.isLocal) {
745
+ this.controller = new LocalController(
746
+ this.model,
747
+ this.id,
748
+ this.collisionsManager,
749
+ this.keyInputManager,
750
+ this.cameraManager,
751
+ this.timeManager
752
+ );
753
+ }
754
+ this.modelLoadedCallback();
755
+ }
756
+ update(time) {
757
+ if (!this.model)
758
+ return;
759
+ this.model.mesh.getWorldPosition(this.position);
760
+ if (typeof this.model.material.uniforms.time !== "undefined") {
761
+ this.model.material.uniforms.time.value = time;
762
+ this.model.material.uniforms.diffuseRandomColor.value = this.color;
763
+ }
764
+ }
765
+ };
766
+
767
+ // src/character/CharacterManager.ts
768
+ import { Group, Vector3 as Vector36 } from "three";
769
+
770
+ // src/character/RemoteController.ts
771
+ import {
772
+ AnimationMixer as AnimationMixer2,
773
+ LoadingManager as LoadingManager2,
774
+ Object3D as Object3D3,
775
+ Quaternion as Quaternion2,
776
+ Vector3 as Vector35
777
+ } from "three";
778
+ import { FBXLoader as FBXLoader2 } from "three/examples/jsm/loaders/FBXLoader.js";
779
+ import { GLTFLoader as GLTFLoader2 } from "three/examples/jsm/loaders/GLTFLoader.js";
780
+ var RemoteController = class {
781
+ constructor(character, id) {
782
+ this.character = character;
783
+ this.id = id;
784
+ __publicField(this, "characterModel", null);
785
+ __publicField(this, "loadManager", new LoadingManager2());
786
+ __publicField(this, "animationMixer", new AnimationMixer2(new Object3D3()));
787
+ __publicField(this, "animations", /* @__PURE__ */ new Map());
788
+ __publicField(this, "currentAnimation", 0 /* idle */);
789
+ __publicField(this, "fbxLoader", new FBXLoader2(this.loadManager));
790
+ __publicField(this, "gltfLoader", new GLTFLoader2(this.loadManager));
791
+ __publicField(this, "networkState", {
792
+ id: 0,
793
+ position: { x: 0, y: 0, z: 0 },
794
+ rotation: { quaternionY: 0, quaternionW: 0 },
795
+ state: this.currentAnimation
796
+ });
797
+ this.characterModel = this.character.model.mesh;
798
+ this.animationMixer = new AnimationMixer2(this.characterModel);
799
+ }
800
+ update(clientUpdate, time, deltaTime) {
801
+ if (!this.character)
802
+ return;
803
+ this.character.update(time);
804
+ this.updateFromNetwork(clientUpdate);
805
+ this.animationMixer.update(deltaTime);
806
+ }
807
+ setAnimationFromFile(animationType, fileName) {
808
+ const animationFile = `${fileName}`;
809
+ const extension = fileName.split(".").pop();
810
+ if (typeof extension !== "string") {
811
+ console.error(`Error: could not recognize extension of animation: ${animationFile}`);
812
+ return;
813
+ }
814
+ if (["gltf", "glb"].includes(extension)) {
815
+ this.gltfLoader.load(
816
+ animationFile,
817
+ (anim) => {
818
+ const animation = anim.animations[0];
819
+ const animationAction = this.animationMixer.clipAction(animation);
820
+ this.animations.set(animationType, animationAction);
821
+ if (animationType === 0 /* idle */) {
822
+ animationAction.play();
823
+ }
824
+ },
825
+ void 0,
826
+ (error) => console.error(`Error loading ${animationFile}: ${error}`)
827
+ );
828
+ } else if (["fbx"].includes(extension)) {
829
+ this.fbxLoader.load(
830
+ animationFile,
831
+ (anim) => {
832
+ const animation = anim.animations[0];
833
+ const animationAction = this.animationMixer.clipAction(animation);
834
+ this.animations.set(animationType, animationAction);
835
+ if (animationType === 0 /* idle */) {
836
+ animationAction.play();
837
+ }
838
+ },
839
+ void 0,
840
+ (error) => console.error(`Error loading ${animationFile}: ${error}`)
841
+ );
842
+ }
843
+ }
844
+ transitionToAnimation(targetAnimation, transitionDuration = 0.21) {
845
+ if (this.currentAnimation === targetAnimation)
846
+ return;
847
+ const currentAction = this.animations.get(this.currentAnimation);
848
+ const targetAction = this.animations.get(targetAnimation);
849
+ if (!targetAction)
850
+ return;
851
+ if (currentAction) {
852
+ currentAction.enabled = true;
853
+ targetAction.reset().setEffectiveTimeScale(1).setEffectiveWeight(1).fadeIn(transitionDuration).play();
854
+ currentAction.crossFadeTo(targetAction, transitionDuration, true);
855
+ } else {
856
+ targetAction.play();
857
+ }
858
+ this.currentAnimation = targetAnimation;
859
+ }
860
+ updateFromNetwork(clientUpdate) {
861
+ if (!this.characterModel)
862
+ return;
863
+ const { position, rotation, state } = clientUpdate;
864
+ this.characterModel.position.lerp(new Vector35(position.x, position.y, position.z), 0.2);
865
+ const rotationQuaternion = new Quaternion2(0, rotation.quaternionY, 0, rotation.quaternionW);
866
+ this.characterModel.quaternion.slerp(rotationQuaternion, 0.2);
867
+ if (state !== this.currentAnimation) {
868
+ this.transitionToAnimation(state);
869
+ }
870
+ }
871
+ };
872
+
873
+ // src/character/CharacterManager.ts
874
+ function encodeCharacterAndCamera(character, camera) {
875
+ return [
876
+ ...character.position.toArray(),
877
+ ...character.quaternion.toArray(),
878
+ ...camera.position.toArray(),
879
+ ...camera.quaternion.toArray()
880
+ ].join(",");
881
+ }
882
+ function decodeCharacterAndCamera(hash, character, camera) {
883
+ const values = hash.split(",").map(Number);
884
+ character.position.fromArray(values.slice(0, 3));
885
+ character.quaternion.fromArray(values.slice(3, 7));
886
+ camera.position.fromArray(values.slice(7, 10));
887
+ camera.quaternion.fromArray(values.slice(10, 14));
888
+ }
889
+ var CharacterManager = class {
890
+ constructor(collisionsManager, cameraManager, timeManager, inputManager, clientStates, sendUpdate) {
891
+ this.collisionsManager = collisionsManager;
892
+ this.cameraManager = cameraManager;
893
+ this.timeManager = timeManager;
894
+ this.inputManager = inputManager;
895
+ this.clientStates = clientStates;
896
+ this.sendUpdate = sendUpdate;
897
+ /*
898
+ TODO - re-enable updating location hash when there is a solution that waits for models to load (currently if the
899
+ character was standing on a model and the page is reloaded the character falls into the model before it loads and
900
+ can be trapped).
901
+ */
902
+ __publicField(this, "updateLocationHash", false);
903
+ __publicField(this, "loadingCharacters", /* @__PURE__ */ new Map());
904
+ __publicField(this, "remoteCharacters", /* @__PURE__ */ new Map());
905
+ __publicField(this, "remoteCharacterControllers", /* @__PURE__ */ new Map());
906
+ __publicField(this, "characterDescription", null);
907
+ __publicField(this, "character", null);
908
+ __publicField(this, "group");
909
+ this.group = new Group();
910
+ }
911
+ spawnCharacter(characterDescription, id, isLocal = false) {
912
+ this.characterDescription = characterDescription;
913
+ const characterLoadingPromise = new Promise((resolve) => {
914
+ const character = new Character(
915
+ characterDescription,
916
+ id,
917
+ isLocal,
918
+ () => {
919
+ if (window.location.hash && window.location.hash.length > 1) {
920
+ decodeCharacterAndCamera(
921
+ window.location.hash.substring(1),
922
+ character.model.mesh,
923
+ this.cameraManager.camera
924
+ );
925
+ } else {
926
+ const spawnPosition = getSpawnPositionInsideCircle(3, 30, id);
927
+ character.model.mesh.position.set(spawnPosition.x, spawnPosition.y, spawnPosition.z);
928
+ this.cameraManager.camera.position.set(
929
+ spawnPosition.x,
930
+ spawnPosition.y + 1.5,
931
+ spawnPosition.z + 3
932
+ );
933
+ }
934
+ character.model.hideMaterialByMeshName("SK_UE5Mannequin_1");
935
+ this.group.add(character.model.mesh);
936
+ if (isLocal) {
937
+ this.character = character;
938
+ } else {
939
+ this.remoteCharacters.set(id, character);
940
+ const remoteController = new RemoteController(character, id);
941
+ remoteController.setAnimationFromFile(
942
+ 0 /* idle */,
943
+ characterDescription.idleAnimationFileUrl
944
+ );
945
+ remoteController.setAnimationFromFile(
946
+ 1 /* walking */,
947
+ characterDescription.jogAnimationFileUrl
948
+ );
949
+ remoteController.setAnimationFromFile(
950
+ 2 /* running */,
951
+ characterDescription.sprintAnimationFileUrl
952
+ );
953
+ this.remoteCharacterControllers.set(id, remoteController);
954
+ }
955
+ resolve(character);
956
+ },
957
+ this.collisionsManager,
958
+ this.inputManager,
959
+ this.cameraManager,
960
+ this.timeManager
961
+ );
962
+ });
963
+ this.loadingCharacters.set(id, characterLoadingPromise);
964
+ return characterLoadingPromise;
965
+ }
966
+ getLocalCharacterPositionAndRotation() {
967
+ if (this.character && this.character.model && this.character.model.mesh) {
968
+ return {
969
+ position: this.character.model.mesh.position,
970
+ rotation: this.character.model.mesh.rotation
971
+ };
972
+ }
973
+ return {
974
+ position: { x: 0, y: 0, z: 0 },
975
+ rotation: { x: 0, y: 0, z: 0 }
976
+ };
977
+ }
978
+ clear() {
979
+ for (const [id, character] of this.remoteCharacters) {
980
+ this.group.remove(character.model.mesh);
981
+ this.remoteCharacters.delete(id);
982
+ this.remoteCharacterControllers.delete(id);
983
+ }
984
+ if (this.character) {
985
+ this.group.remove(this.character.model.mesh);
986
+ this.character = null;
987
+ }
988
+ this.loadingCharacters.clear();
989
+ }
990
+ update() {
991
+ if (this.character) {
992
+ this.character.update(this.timeManager.time);
993
+ this.cameraManager.setTarget(this.character.position.add(new Vector36(0, 1.3, 0)));
994
+ if (this.character.controller) {
995
+ this.character.controller.update();
996
+ if (this.timeManager.frame % 2 === 0) {
997
+ this.sendUpdate(this.character.controller.networkState);
998
+ }
999
+ }
1000
+ for (const [id, update] of this.clientStates) {
1001
+ if (!this.remoteCharacters.has(id) && !this.loadingCharacters.has(id)) {
1002
+ this.spawnCharacter(this.characterDescription, id).then(() => {
1003
+ this.loadingCharacters.delete(id);
1004
+ });
1005
+ }
1006
+ const characterController = this.remoteCharacterControllers.get(id);
1007
+ if (characterController) {
1008
+ characterController.update(update, this.timeManager.time, this.timeManager.deltaTime);
1009
+ }
1010
+ }
1011
+ for (const [id, character] of this.remoteCharacters) {
1012
+ if (!this.clientStates.has(id)) {
1013
+ this.group.remove(character.model.mesh);
1014
+ this.remoteCharacters.delete(id);
1015
+ this.remoteCharacterControllers.delete(id);
1016
+ }
1017
+ }
1018
+ if (this.updateLocationHash && this.timeManager.frame % 60 === 0) {
1019
+ window.location.hash = encodeCharacterAndCamera(
1020
+ this.character.model.mesh,
1021
+ this.cameraManager.camera
1022
+ );
1023
+ }
1024
+ }
1025
+ }
1026
+ };
1027
+
1028
+ // src/input/KeyInputManager.ts
1029
+ var KeyInputManager = class {
1030
+ constructor() {
1031
+ __publicField(this, "keys", /* @__PURE__ */ new Map());
1032
+ document.addEventListener("keydown", this.onKeyDown.bind(this));
1033
+ document.addEventListener("keyup", this.onKeyUp.bind(this));
1034
+ }
1035
+ onKeyDown(event) {
1036
+ this.keys.set(event.key.toLowerCase(), true);
1037
+ }
1038
+ onKeyUp(event) {
1039
+ this.keys.set(event.key.toLowerCase(), false);
1040
+ }
1041
+ isKeyPressed(key) {
1042
+ return this.keys.get(key) || false;
1043
+ }
1044
+ isMovementKeyPressed() {
1045
+ return ["w", "a", "s", "d"].some((key) => this.isKeyPressed(key));
1046
+ }
1047
+ isShiftPressed() {
1048
+ return this.isKeyPressed("shift");
1049
+ }
1050
+ isJumping() {
1051
+ return this.isKeyPressed(" ");
1052
+ }
1053
+ dispose() {
1054
+ document.removeEventListener("keydown", this.onKeyDown.bind(this));
1055
+ document.removeEventListener("keyup", this.onKeyDown.bind(this));
1056
+ }
1057
+ };
1058
+
1059
+ // src/mml/MMLCompositionScene.ts
1060
+ import {
1061
+ InteractionManager,
1062
+ MMLClickTrigger,
1063
+ PromptManager,
1064
+ registerCustomElementsToWindow,
1065
+ setGlobalMScene
1066
+ } from "mml-web";
1067
+ import { Group as Group2 } from "three";
1068
+ var MMLCompositionScene = class {
1069
+ constructor(renderer, scene, camera, audioListener, collisionsManager, getUserPositionAndRotation, documentAddresses) {
1070
+ this.renderer = renderer;
1071
+ this.scene = scene;
1072
+ this.camera = camera;
1073
+ this.audioListener = audioListener;
1074
+ this.collisionsManager = collisionsManager;
1075
+ this.getUserPositionAndRotation = getUserPositionAndRotation;
1076
+ __publicField(this, "group");
1077
+ __publicField(this, "debug", false);
1078
+ __publicField(this, "mmlScene");
1079
+ __publicField(this, "promptManager");
1080
+ __publicField(this, "interactionListener");
1081
+ __publicField(this, "clickTrigger");
1082
+ this.group = new Group2();
1083
+ this.promptManager = PromptManager.init(document.body);
1084
+ const { interactionListener } = InteractionManager.init(document.body, this.camera, this.scene);
1085
+ this.interactionListener = interactionListener;
1086
+ this.mmlScene = {
1087
+ getAudioListener: () => this.audioListener,
1088
+ getRenderer: () => this.renderer,
1089
+ getThreeScene: () => this.scene,
1090
+ getRootContainer: () => this.group,
1091
+ getCamera: () => this.camera,
1092
+ addCollider: (object, mElement) => {
1093
+ this.collisionsManager.addMeshesGroup(object, mElement);
1094
+ },
1095
+ updateCollider: (object) => {
1096
+ this.collisionsManager.updateMeshesGroup(object);
1097
+ },
1098
+ removeCollider: (object) => {
1099
+ this.collisionsManager.removeMeshesGroup(object);
1100
+ },
1101
+ getUserPositionAndRotation: this.getUserPositionAndRotation,
1102
+ addInteraction: (interaction) => {
1103
+ this.interactionListener.addInteraction(interaction);
1104
+ },
1105
+ updateInteraction: (interaction) => {
1106
+ this.interactionListener.updateInteraction(interaction);
1107
+ },
1108
+ removeInteraction: (interaction) => {
1109
+ this.interactionListener.removeInteraction(interaction);
1110
+ },
1111
+ prompt: (promptProps, callback) => {
1112
+ this.promptManager.prompt(promptProps, callback);
1113
+ }
1114
+ };
1115
+ setGlobalMScene(this.mmlScene);
1116
+ registerCustomElementsToWindow(window);
1117
+ this.clickTrigger = MMLClickTrigger.init(document, this.mmlScene);
1118
+ if (this.debug) {
1119
+ console.log(this.clickTrigger);
1120
+ }
1121
+ for (const address of documentAddresses) {
1122
+ const frameElement = document.createElement("m-frame");
1123
+ frameElement.setAttribute("src", address);
1124
+ document.body.appendChild(frameElement);
1125
+ }
1126
+ }
1127
+ };
1128
+
1129
+ // src/rendering/composer.ts
1130
+ import {
1131
+ EffectComposer,
1132
+ RenderPass,
1133
+ EffectPass,
1134
+ FXAAEffect,
1135
+ ShaderPass,
1136
+ BloomEffect
1137
+ } from "postprocessing";
1138
+ import {
1139
+ ACESFilmicToneMapping,
1140
+ PCFSoftShadowMap,
1141
+ Vector2 as Vector22,
1142
+ WebGLRenderer as WebGLRenderer2
1143
+ } from "three";
1144
+
1145
+ // src/rendering/post-effects/gauss-grain.ts
1146
+ import { ShaderMaterial, Uniform, Vector2 } from "three";
1147
+
1148
+ // src/rendering/shaders/vertex-shader.ts
1149
+ var vertexShader = (
1150
+ /* glsl */
1151
+ `
1152
+ precision highp float;
1153
+
1154
+ out vec2 vUv;
1155
+ void main() {
1156
+ vUv = uv;
1157
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
1158
+ }
1159
+ `
1160
+ );
1161
+
1162
+ // src/rendering/post-effects/gauss-grain.ts
1163
+ var GaussGrainEffect = new ShaderMaterial({
1164
+ uniforms: {
1165
+ tDiffuse: new Uniform(null),
1166
+ resolution: new Uniform(new Vector2()),
1167
+ time: new Uniform(0),
1168
+ amount: new Uniform(0),
1169
+ alpha: new Uniform(0)
1170
+ },
1171
+ vertexShader,
1172
+ fragmentShader: (
1173
+ /* glsl */
1174
+ `
1175
+ precision highp float;
1176
+ in vec2 vUv;
1177
+
1178
+ uniform sampler2D tDiffuse;
1179
+ uniform vec2 resolution;
1180
+ uniform float time;
1181
+ uniform float amount;
1182
+ uniform float alpha;
1183
+
1184
+ const float PI = acos(-1.0);
1185
+ const float TAU = PI * 2.0;
1186
+ const float SQRTAU = sqrt(TAU);
1187
+
1188
+ float gaussian(float z, float u, float o) {
1189
+ return (
1190
+ (1.0 / (o * SQRTAU)) *
1191
+ (exp(-(((z - u) * (z - u)) / (2.0 * (o * o)))))
1192
+ );
1193
+ }
1194
+
1195
+ vec3 gaussgrain() {
1196
+ vec2 ps = vec2(1.01) / resolution.xy;
1197
+ vec2 uv = gl_FragCoord.xy * ps;
1198
+ float t = time;
1199
+ float seed = dot(uv, vec2(12.9898, 78.233));
1200
+ float noise = fract(sin(seed) * 43758.5453123 + t);
1201
+ noise = gaussian(noise, 0.0, 0.5);
1202
+ return vec3(noise);
1203
+ }
1204
+
1205
+ void main(void) {
1206
+ vec2 uv = vUv;
1207
+ vec4 originalColor = texture(tDiffuse, uv);
1208
+ vec3 grain = gaussgrain();
1209
+ vec3 col = originalColor.rgb + (grain * amount);
1210
+ gl_FragColor = vec4(clamp(col, 0.0, 1.0), alpha);
1211
+ }
1212
+ `
1213
+ )
1214
+ });
1215
+
1216
+ // src/rendering/composer.ts
1217
+ var Composer = class {
1218
+ constructor(scene, camera) {
1219
+ __publicField(this, "width", window.innerWidth);
1220
+ __publicField(this, "height", window.innerHeight);
1221
+ __publicField(this, "resolution", new Vector22(this.width, this.height));
1222
+ __publicField(this, "scene");
1223
+ __publicField(this, "camera");
1224
+ __publicField(this, "renderer");
1225
+ __publicField(this, "composer");
1226
+ __publicField(this, "renderPass");
1227
+ __publicField(this, "fxaaEffect");
1228
+ __publicField(this, "fxaaPass");
1229
+ __publicField(this, "bloomEffect");
1230
+ __publicField(this, "bloomPass");
1231
+ __publicField(this, "gaussGrainEffect", GaussGrainEffect);
1232
+ __publicField(this, "gaussGrainPass");
1233
+ this.scene = scene;
1234
+ this.camera = camera;
1235
+ this.renderer = new WebGLRenderer2({
1236
+ powerPreference: "high-performance",
1237
+ antialias: false,
1238
+ stencil: false,
1239
+ depth: false
1240
+ });
1241
+ this.renderer.setSize(this.width, this.height);
1242
+ this.renderer.shadowMap.enabled = true;
1243
+ this.renderer.shadowMap.type = PCFSoftShadowMap;
1244
+ this.renderer.toneMapping = ACESFilmicToneMapping;
1245
+ this.renderer.toneMappingExposure = 0.5;
1246
+ document.body.appendChild(this.renderer.domElement);
1247
+ this.composer = new EffectComposer(this.renderer);
1248
+ this.renderPass = new RenderPass(this.scene, this.camera);
1249
+ this.fxaaEffect = new FXAAEffect();
1250
+ this.fxaaPass = new EffectPass(this.camera, this.fxaaEffect);
1251
+ this.bloomEffect = new BloomEffect();
1252
+ this.bloomPass = new EffectPass(this.camera, this.bloomEffect);
1253
+ this.gaussGrainPass = new ShaderPass(this.gaussGrainEffect, "tDiffuse");
1254
+ this.composer.addPass(this.renderPass);
1255
+ this.composer.addPass(this.fxaaPass);
1256
+ this.composer.addPass(this.bloomPass);
1257
+ this.composer.addPass(this.gaussGrainPass);
1258
+ window.addEventListener("resize", () => {
1259
+ this.updateProjection();
1260
+ });
1261
+ this.updateProjection();
1262
+ }
1263
+ updateProjection() {
1264
+ this.width = window.innerWidth;
1265
+ this.height = innerHeight;
1266
+ this.resolution = new Vector22(this.width, this.height);
1267
+ if (this.composer)
1268
+ this.composer.setSize(this.width, this.height);
1269
+ if (this.fxaaPass)
1270
+ this.fxaaPass.setSize(this.width, this.height);
1271
+ if (this.renderPass)
1272
+ this.renderPass.setSize(this.width, this.height);
1273
+ this.renderer.setSize(this.width, this.height);
1274
+ }
1275
+ render(time) {
1276
+ this.composer.render();
1277
+ this.gaussGrainEffect.uniforms.resolution.value = this.resolution;
1278
+ this.gaussGrainEffect.uniforms.time.value = time;
1279
+ this.gaussGrainEffect.uniforms.alpha.value = 1;
1280
+ this.gaussGrainEffect.uniforms.amount.value = 0.035;
1281
+ this.bloomEffect.intensity = 1;
1282
+ }
1283
+ };
1284
+
1285
+ // src/time/TimeManager.ts
1286
+ import { Clock } from "three";
1287
+ var TimeManager = class {
1288
+ constructor() {
1289
+ __publicField(this, "clock", new Clock());
1290
+ __publicField(this, "roundMagnitude", 2e5);
1291
+ __publicField(this, "maxAverageFrames", 300);
1292
+ __publicField(this, "deltaTimes", []);
1293
+ __publicField(this, "targetAverageDeltaTime", 0);
1294
+ __publicField(this, "lerpedAverageMagDelta", 0);
1295
+ __publicField(this, "fpsUpdateTime", 0);
1296
+ __publicField(this, "framesSinceLastFPSUpdate", 0);
1297
+ __publicField(this, "time", 0);
1298
+ __publicField(this, "deltaTime", 0);
1299
+ __publicField(this, "rawDeltaTime", 0);
1300
+ __publicField(this, "frame", 0);
1301
+ __publicField(this, "fps", 0);
1302
+ }
1303
+ update() {
1304
+ this.rawDeltaTime = this.clock.getDelta();
1305
+ this.frame++;
1306
+ this.time += this.rawDeltaTime;
1307
+ this.deltaTimes.push(this.rawDeltaTime);
1308
+ if (this.deltaTimes.length > this.maxAverageFrames) {
1309
+ this.deltaTimes.shift();
1310
+ }
1311
+ this.targetAverageDeltaTime = this.deltaTimes.reduce((prev, curr) => prev + curr, 0) / this.deltaTimes.length;
1312
+ this.lerpedAverageMagDelta += ease(
1313
+ this.targetAverageDeltaTime * this.roundMagnitude,
1314
+ this.lerpedAverageMagDelta,
1315
+ 0.12
1316
+ );
1317
+ const revertMagnitude = this.lerpedAverageMagDelta / this.roundMagnitude;
1318
+ const smoothDT = Math.round(revertMagnitude * this.roundMagnitude) / this.roundMagnitude;
1319
+ this.deltaTime = smoothDT > this.rawDeltaTime * 1.75 ? this.rawDeltaTime : smoothDT;
1320
+ this.framesSinceLastFPSUpdate++;
1321
+ if (this.framesSinceLastFPSUpdate >= this.maxAverageFrames) {
1322
+ this.fps = Math.round(this.framesSinceLastFPSUpdate / (this.time - this.fpsUpdateTime) * 100) / 100;
1323
+ this.fpsUpdateTime = this.time;
1324
+ this.framesSinceLastFPSUpdate = 0;
1325
+ }
1326
+ }
1327
+ };
1328
+
1329
+ // src/collisions/CollisionsManager.ts
1330
+ import {
1331
+ MMLCollisionTrigger,
1332
+ getRelativePositionAndRotationRelativeToObject
1333
+ } from "mml-web";
1334
+ import {
1335
+ Color as Color3,
1336
+ Euler,
1337
+ FrontSide,
1338
+ Mesh as Mesh2,
1339
+ MeshStandardMaterial as MeshStandardMaterial2,
1340
+ Vector3 as Vector37
1341
+ } from "three";
1342
+ import * as BufferGeometryUtils from "three/examples/jsm/utils/BufferGeometryUtils.js";
1343
+ import { MeshBVH, MeshBVHVisualizer } from "three-mesh-bvh";
1344
+ var CollisionsManager = class {
1345
+ constructor(scene) {
1346
+ __publicField(this, "debug", false);
1347
+ __publicField(this, "scene");
1348
+ __publicField(this, "tempVector", new Vector37());
1349
+ __publicField(this, "tempVector2", new Vector37());
1350
+ __publicField(this, "collisionMeshState", /* @__PURE__ */ new Map());
1351
+ __publicField(this, "collisionTrigger");
1352
+ this.scene = scene;
1353
+ this.collisionTrigger = MMLCollisionTrigger.init();
1354
+ }
1355
+ createCollisionMeshState(group) {
1356
+ const geometries = [];
1357
+ group.traverse((child) => {
1358
+ if (child.type === "Mesh") {
1359
+ const mesh = child;
1360
+ mesh.localToWorld(new Vector37());
1361
+ mesh.updateMatrixWorld();
1362
+ const clonedGeometry = mesh.geometry.clone();
1363
+ clonedGeometry.applyMatrix4(mesh.matrixWorld);
1364
+ for (const key in clonedGeometry.attributes) {
1365
+ if (key !== "position") {
1366
+ clonedGeometry.deleteAttribute(key);
1367
+ }
1368
+ }
1369
+ if (clonedGeometry.index) {
1370
+ geometries.push(clonedGeometry.toNonIndexed());
1371
+ } else {
1372
+ geometries.push(clonedGeometry);
1373
+ }
1374
+ }
1375
+ });
1376
+ const newBufferGeometry = BufferGeometryUtils.mergeGeometries(geometries);
1377
+ const meshBVH = new MeshBVH(newBufferGeometry);
1378
+ if (!this.debug) {
1379
+ return { source: group, visualizer: null, meshBVH };
1380
+ }
1381
+ const mergedMesh = new Mesh2(
1382
+ newBufferGeometry,
1383
+ new MeshStandardMaterial2({ color: 16711680, side: FrontSide, wireframe: true })
1384
+ );
1385
+ mergedMesh.geometry.boundsTree = meshBVH;
1386
+ const visualizer = new MeshBVHVisualizer(mergedMesh, 3);
1387
+ visualizer.edgeMaterial.color = new Color3(255);
1388
+ visualizer.update();
1389
+ return { source: group, visualizer, meshBVH };
1390
+ }
1391
+ addMeshesGroup(group, mElement) {
1392
+ if (mElement) {
1393
+ this.collisionTrigger.addCollider(group, mElement);
1394
+ }
1395
+ const meshState = this.createCollisionMeshState(group);
1396
+ if (meshState.visualizer) {
1397
+ this.scene.add(meshState.visualizer);
1398
+ }
1399
+ this.collisionMeshState.set(group, meshState);
1400
+ }
1401
+ updateMeshesGroup(group) {
1402
+ const meshState = this.collisionMeshState.get(group);
1403
+ if (meshState) {
1404
+ const newMeshState = this.createCollisionMeshState(group);
1405
+ if (meshState.visualizer) {
1406
+ this.scene.remove(meshState.visualizer);
1407
+ }
1408
+ if (newMeshState.visualizer) {
1409
+ this.scene.add(newMeshState.visualizer);
1410
+ }
1411
+ this.collisionMeshState.set(group, newMeshState);
1412
+ }
1413
+ }
1414
+ removeMeshesGroup(group) {
1415
+ this.collisionTrigger.removeCollider(group);
1416
+ const meshState = this.collisionMeshState.get(group);
1417
+ if (meshState) {
1418
+ if (meshState.visualizer) {
1419
+ this.scene.remove(meshState.visualizer);
1420
+ }
1421
+ this.collisionMeshState.delete(group);
1422
+ }
1423
+ }
1424
+ applyCollider(tempSegment, radius, boundingBox, meshState) {
1425
+ let collisionPosition = null;
1426
+ meshState.meshBVH.shapecast({
1427
+ intersectsBounds: (box) => box.intersectsBox(boundingBox),
1428
+ intersectsTriangle: (tri) => {
1429
+ const triPoint = this.tempVector;
1430
+ const capsulePoint = this.tempVector2;
1431
+ const distance = tri.closestPointToSegment(tempSegment, triPoint, capsulePoint);
1432
+ if (distance < radius) {
1433
+ const depth = radius - distance;
1434
+ collisionPosition = new Vector37().copy(capsulePoint);
1435
+ const direction = capsulePoint.sub(triPoint).normalize();
1436
+ tempSegment.start.addScaledVector(direction, depth);
1437
+ tempSegment.end.addScaledVector(direction, depth);
1438
+ }
1439
+ }
1440
+ });
1441
+ return collisionPosition;
1442
+ }
1443
+ applyColliders(tempSegment, radius, boundingBox) {
1444
+ let collidedElements = null;
1445
+ for (const meshState of this.collisionMeshState.values()) {
1446
+ const collisionPosition = this.applyCollider(tempSegment, radius, boundingBox, meshState);
1447
+ if (collisionPosition) {
1448
+ if (collidedElements === null) {
1449
+ collidedElements = /* @__PURE__ */ new Map();
1450
+ }
1451
+ const relativePosition = getRelativePositionAndRotationRelativeToObject(
1452
+ {
1453
+ position: collisionPosition,
1454
+ rotation: new Euler()
1455
+ },
1456
+ meshState.source
1457
+ );
1458
+ collidedElements.set(meshState.source, {
1459
+ position: relativePosition.position
1460
+ });
1461
+ }
1462
+ }
1463
+ this.collisionTrigger.setCurrentCollisions(collidedElements);
1464
+ }
1465
+ };
1466
+ export {
1467
+ AnimationState,
1468
+ CameraManager,
1469
+ CharacterManager,
1470
+ CollisionsManager,
1471
+ Composer,
1472
+ KeyInputManager,
1473
+ MMLCompositionScene,
1474
+ TimeManager
1475
+ };
1476
+ //# sourceMappingURL=index.js.map