@mml-io/3d-web-client-core 0.10.1 → 0.12.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 CHANGED
@@ -34,6 +34,9 @@ var round = (n, digits) => {
34
34
  var ease = (target, n, factor) => {
35
35
  return round((target - n) * factor, 5);
36
36
  };
37
+ function clamp(value, min, max) {
38
+ return Math.min(Math.max(value, min), max);
39
+ }
37
40
  var remap = (value, minValue, maxValue, minScaledValue, maxScaledValue) => {
38
41
  return minScaledValue + (maxScaledValue - minScaledValue) * (value - minValue) / (maxValue - minValue);
39
42
  };
@@ -106,6 +109,8 @@ var CameraManager = class {
106
109
  this.targetDistance = this.initialDistance;
107
110
  this.distance = this.initialDistance;
108
111
  this.desiredDistance = this.initialDistance;
112
+ this.phi = Math.PI / 2;
113
+ this.theta = Math.PI / 2;
109
114
  this.dragging = false;
110
115
  this.target = new Vector32(0, 1.55, 0);
111
116
  this.hadTarget = false;
@@ -174,27 +179,34 @@ var CameraManager = class {
174
179
  this.setTarget(target);
175
180
  }
176
181
  reverseUpdateFromPositions() {
177
- if (this.phi === null || this.theta == null)
178
- return;
179
182
  const dx = this.camera.position.x - this.target.x;
180
183
  const dy = this.camera.position.y - this.target.y;
181
184
  const dz = this.camera.position.z - this.target.z;
182
185
  this.targetDistance = Math.sqrt(dx * dx + dy * dy + dz * dz);
183
- this.targetTheta = (this.theta + 2 * Math.PI) % (2 * Math.PI);
184
- this.targetPhi = Math.max(0, Math.min(Math.PI, this.phi));
186
+ this.targetTheta = Math.atan2(dz, dx);
187
+ this.targetPhi = Math.acos(dy / this.targetDistance);
185
188
  this.phi = this.targetPhi;
186
189
  this.theta = this.targetTheta;
187
190
  this.distance = this.targetDistance;
191
+ this.desiredDistance = this.targetDistance;
192
+ this.targetFOV = remap(
193
+ this.targetDistance,
194
+ this.minDistance,
195
+ this.maxDistance,
196
+ this.minFOV,
197
+ this.maxFOV
198
+ );
199
+ this.fov = this.targetFOV;
188
200
  }
189
201
  adjustCameraPosition() {
190
202
  this.rayCaster.set(
191
203
  this.camera.position,
192
204
  this.target.clone().sub(this.camera.position).normalize()
193
205
  );
194
- const minimumDistance = this.collisionsManager.raycastFirstDistance(this.rayCaster.ray);
206
+ const firstRaycastHit = this.collisionsManager.raycastFirst(this.rayCaster.ray);
195
207
  const cameraToPlayerDistance = this.camera.position.distanceTo(this.target);
196
- if (minimumDistance !== null && minimumDistance <= cameraToPlayerDistance) {
197
- this.targetDistance = cameraToPlayerDistance - minimumDistance;
208
+ if (firstRaycastHit !== null && firstRaycastHit[0] <= cameraToPlayerDistance) {
209
+ this.targetDistance = cameraToPlayerDistance - firstRaycastHit[0];
198
210
  this.distance = this.targetDistance;
199
211
  } else {
200
212
  this.targetDistance += (this.desiredDistance - this.targetDistance) * this.dampingFactor * 4;
@@ -244,15 +256,18 @@ var CameraManager = class {
244
256
  };
245
257
 
246
258
  // src/character/Character.ts
247
- import { Color as Color3, Vector3 as Vector35 } from "three";
259
+ import { Color as Color3, Group, Vector3 as Vector34 } from "three";
248
260
 
249
261
  // src/character/CharacterModel.ts
262
+ import {
263
+ Character as MMLCharacter,
264
+ ModelLoader,
265
+ parseMMLDescription
266
+ } from "@mml-io/3d-web-avatar";
250
267
  import {
251
268
  AnimationClip,
252
269
  AnimationMixer,
253
- LoopRepeat,
254
- MeshStandardMaterial,
255
- Object3D
270
+ LoopRepeat
256
271
  } from "three";
257
272
 
258
273
  // src/character/CharacterMaterial.ts
@@ -351,15 +366,15 @@ ${before}
351
366
  // src/tweakpane/blades/characterFolder.ts
352
367
  var characterValues = {
353
368
  transmission: 0.01,
354
- metalness: 0.8,
355
- roughness: 0.05,
369
+ metalness: 0.2,
370
+ roughness: 0.8,
356
371
  ior: 1,
357
372
  thickness: 0.1,
358
373
  specularColor: { r: 1, g: 1, b: 1 },
359
374
  specularIntensity: 0.1,
360
375
  emissive: { r: 1, g: 1, b: 1 },
361
- emissiveIntensity: 0.1,
362
- envMapIntensity: 0.8,
376
+ emissiveIntensity: 0.01,
377
+ envMapIntensity: 0.12,
363
378
  sheenColor: { r: 1, g: 1, b: 1 },
364
379
  sheen: 0.5,
365
380
  clearcoat: 0,
@@ -457,11 +472,13 @@ var CharacterFolder = class {
457
472
 
458
473
  // src/character/CharacterMaterial.ts
459
474
  var CharacterMaterial = class extends MeshPhysicalMaterial {
460
- constructor() {
475
+ constructor(color) {
461
476
  super();
462
477
  this.uniforms = {};
463
478
  this.colorsCube216 = [];
464
- this.color = new Color(16777215);
479
+ if (color) {
480
+ this.color = color;
481
+ }
465
482
  this.transmission = characterValues.transmission;
466
483
  this.metalness = characterValues.metalness;
467
484
  this.roughness = characterValues.roughness;
@@ -567,16 +584,14 @@ var CharacterMaterial = class extends MeshPhysicalMaterial {
567
584
  vec3 grid = vec3(smoothstep(0.01, 0.0, a) * 1.15) * diffuseRandomColor;
568
585
  outgoingLight += grid;
569
586
  #endif
570
-
571
- outgoingLight += smoothstep(0.1, 0.0, scanLines) * 0.1;
572
587
  `
573
588
  );
574
589
  };
575
590
  this.generateColorCube();
576
591
  }
577
592
  generateColorCube() {
578
- const saturation = 0.4;
579
- const lightness = 0.7;
593
+ const saturation = 0.5;
594
+ const lightness = 0.9;
580
595
  const goldenRatioConjugate = 0.618033988749895;
581
596
  let hue = 0;
582
597
  for (let i = 0; i < 216; i++) {
@@ -629,8 +644,9 @@ var AnimationState = /* @__PURE__ */ ((AnimationState2) => {
629
644
 
630
645
  // src/character/CharacterModel.ts
631
646
  var CharacterModel = class {
632
- constructor(characterDescription, characterModelLoader) {
647
+ constructor(characterDescription, animationConfig, characterModelLoader) {
633
648
  this.characterDescription = characterDescription;
649
+ this.animationConfig = animationConfig;
634
650
  this.characterModelLoader = characterModelLoader;
635
651
  this.mesh = null;
636
652
  this.material = new CharacterMaterial();
@@ -641,91 +657,130 @@ var CharacterModel = class {
641
657
  }
642
658
  async init() {
643
659
  await this.loadMainMesh();
660
+ await this.setAnimationFromFile(this.animationConfig.idleAnimationFileUrl, 0 /* idle */);
644
661
  await this.setAnimationFromFile(
645
- this.characterDescription.idleAnimationFileUrl,
646
- 0 /* idle */
647
- );
648
- await this.setAnimationFromFile(
649
- this.characterDescription.jogAnimationFileUrl,
662
+ this.animationConfig.jogAnimationFileUrl,
650
663
  1 /* walking */
651
664
  );
652
665
  await this.setAnimationFromFile(
653
- this.characterDescription.sprintAnimationFileUrl,
666
+ this.animationConfig.sprintAnimationFileUrl,
654
667
  2 /* running */
655
668
  );
656
- await this.setAnimationFromFile(
657
- this.characterDescription.airAnimationFileUrl,
658
- 4 /* air */
659
- );
660
- this.applyMaterialToAllSkinnedMeshes(this.material);
661
- }
662
- updateAnimation(targetAnimation, deltaTime) {
663
- var _a;
664
- if (this.currentAnimation !== targetAnimation) {
665
- this.transitionToAnimation(targetAnimation);
669
+ await this.setAnimationFromFile(this.animationConfig.airAnimationFileUrl, 4 /* air */);
670
+ if (this.characterDescription.meshFileUrl) {
671
+ this.applyCustomMaterial(this.material);
666
672
  }
667
- (_a = this.animationMixer) == null ? void 0 : _a.update(deltaTime);
668
673
  }
669
- hideMaterialByMeshName(meshName) {
674
+ applyCustomMaterial(material) {
670
675
  if (!this.mesh)
671
676
  return;
672
677
  this.mesh.traverse((child) => {
673
- if (child.type === "Bone" && child.name === "mixamorigHeadTop_End") {
674
- this.headBone = child;
675
- }
676
- if (child.type === "SkinnedMesh" && child.name === meshName) {
677
- child.material = new MeshStandardMaterial({
678
- color: 16711680,
679
- transparent: true,
680
- opacity: 0
681
- });
678
+ const asSkinnedMesh = child;
679
+ if (asSkinnedMesh.isSkinnedMesh) {
680
+ const mat = asSkinnedMesh.material;
681
+ if (!mat.name.includes("joints")) {
682
+ asSkinnedMesh.material = material;
683
+ } else {
684
+ const color = mat.color;
685
+ asSkinnedMesh.material = new CharacterMaterial(color);
686
+ }
682
687
  }
683
688
  });
684
689
  }
685
- setShadows(mesh, castShadow = true, receiveShadow = true) {
686
- mesh.traverse((child) => {
687
- if (child.type === "SkinnedMesh") {
688
- child.castShadow = castShadow;
689
- child.receiveShadow = receiveShadow;
690
- }
691
- });
690
+ updateAnimation(targetAnimation) {
691
+ if (this.currentAnimation !== targetAnimation) {
692
+ this.transitionToAnimation(targetAnimation);
693
+ }
692
694
  }
693
- applyMaterialToAllSkinnedMeshes(material) {
694
- if (!this.mesh)
695
- return;
695
+ setMainMesh(mainMesh) {
696
+ this.mesh = mainMesh;
697
+ this.mesh.position.set(0, -0.4, 0);
696
698
  this.mesh.traverse((child) => {
697
699
  if (child.type === "SkinnedMesh") {
698
- child.material = material;
700
+ child.castShadow = true;
701
+ child.receiveShadow = true;
699
702
  }
700
703
  });
701
- }
702
- initAnimationMixer() {
703
- if (this.animationMixer !== null || this.mesh === null)
704
- return;
705
704
  this.animationMixer = new AnimationMixer(this.mesh);
706
705
  }
706
+ async composeMMLCharacter(mmlCharacterDescription) {
707
+ var _a, _b, _c;
708
+ if (((_a = mmlCharacterDescription.base) == null ? void 0 : _a.url.length) === 0) {
709
+ throw new Error(
710
+ "ERROR: An MML Character Description was provided but it's not a valid <m-character> string, or a valid URL"
711
+ );
712
+ }
713
+ let mergedCharacter = null;
714
+ if (mmlCharacterDescription) {
715
+ const characterBase = ((_b = mmlCharacterDescription.base) == null ? void 0 : _b.url) || null;
716
+ const parts = [];
717
+ (_c = mmlCharacterDescription.parts) == null ? void 0 : _c.forEach((part) => {
718
+ if (part.url)
719
+ parts.push(part.url);
720
+ });
721
+ if (characterBase) {
722
+ const mmlCharacter = new MMLCharacter(new ModelLoader());
723
+ mergedCharacter = await mmlCharacter.mergeBodyParts(characterBase, parts);
724
+ if (mergedCharacter) {
725
+ return mergedCharacter.children[0].children[0];
726
+ }
727
+ }
728
+ }
729
+ }
730
+ async loadCharacterFromDescription() {
731
+ if (this.characterDescription.meshFileUrl) {
732
+ return await this.characterModelLoader.load(this.characterDescription.meshFileUrl, "model") || null;
733
+ }
734
+ let mmlCharacterSource;
735
+ if (this.characterDescription.mmlCharacterUrl) {
736
+ const res = await fetch(this.characterDescription.mmlCharacterUrl);
737
+ mmlCharacterSource = await res.text();
738
+ } else if (this.characterDescription.mmlCharacterString) {
739
+ mmlCharacterSource = this.characterDescription.mmlCharacterString;
740
+ } else {
741
+ throw new Error(
742
+ "ERROR: No Character Description was provided. Specify one of meshFileUrl, mmlCharacterUrl or mmlCharacterString"
743
+ );
744
+ }
745
+ const parsedMMLDescription = parseMMLDescription(mmlCharacterSource);
746
+ const mmlCharacterDescription = parsedMMLDescription[0];
747
+ if (parsedMMLDescription[1].length > 0) {
748
+ console.warn("Errors parsing MML Character Description: ", parsedMMLDescription[1]);
749
+ }
750
+ const mmlCharacterBody = await this.composeMMLCharacter(mmlCharacterDescription);
751
+ if (mmlCharacterBody) {
752
+ return mmlCharacterBody;
753
+ }
754
+ return null;
755
+ }
707
756
  async loadMainMesh() {
708
- const mainMeshUrl = this.characterDescription.meshFileUrl;
709
- const scale = this.characterDescription.modelScale;
710
- const extension = mainMeshUrl.split(".").pop();
711
- const name = mainMeshUrl.split("/").pop().replace(`.${extension}`, "");
712
- const mainMesh = await this.characterModelLoader.load(mainMeshUrl, "model");
757
+ const mainMesh = await this.loadCharacterFromDescription();
713
758
  if (typeof mainMesh !== "undefined") {
714
- this.mesh = new Object3D();
715
- const model = mainMesh;
716
- model.position.set(0, -0.4, 0);
717
- this.mesh.add(model);
718
- this.mesh.name = name;
719
- this.mesh.scale.set(scale, scale, scale);
720
- this.setShadows(this.mesh);
759
+ this.setMainMesh(mainMesh);
760
+ } else {
761
+ throw new Error("ERROR: No Character Model was loaded");
721
762
  }
722
763
  }
764
+ cleanAnimationClips(skeletalMesh, animationClip) {
765
+ const availableBones = /* @__PURE__ */ new Set();
766
+ skeletalMesh.traverse((child) => {
767
+ const asBone = child;
768
+ if (asBone.isBone) {
769
+ availableBones.add(child.name);
770
+ }
771
+ });
772
+ animationClip.tracks = animationClip.tracks.filter((track) => {
773
+ const trackName = track.name.split(".")[0];
774
+ return availableBones.has(trackName);
775
+ });
776
+ return animationClip;
777
+ }
723
778
  async setAnimationFromFile(animationFileUrl, animationType) {
724
779
  return new Promise(async (resolve, reject) => {
725
- this.initAnimationMixer();
726
780
  const animation = await this.characterModelLoader.load(animationFileUrl, "animation");
727
- if (typeof animation !== "undefined" && animation instanceof AnimationClip) {
728
- this.animations[animationType] = this.animationMixer.clipAction(animation);
781
+ const cleanAnimation = this.cleanAnimationClips(this.mesh, animation);
782
+ if (typeof animation !== "undefined" && cleanAnimation instanceof AnimationClip) {
783
+ this.animations[animationType] = this.animationMixer.clipAction(cleanAnimation);
729
784
  this.animations[animationType].stop();
730
785
  if (animationType === 0 /* idle */) {
731
786
  this.animations[animationType].play();
@@ -737,9 +792,10 @@ var CharacterModel = class {
737
792
  });
738
793
  }
739
794
  transitionToAnimation(targetAnimation, transitionDuration = 0.15) {
740
- if (!this.mesh || this.currentAnimation === null)
795
+ if (!this.mesh)
741
796
  return;
742
797
  const currentAction = this.animations[this.currentAnimation];
798
+ this.currentAnimation = targetAnimation;
743
799
  const targetAction = this.animations[targetAnimation];
744
800
  if (!targetAction)
745
801
  return;
@@ -752,7 +808,11 @@ var CharacterModel = class {
752
808
  targetAction.setLoop(LoopRepeat, Infinity);
753
809
  targetAction.enabled = true;
754
810
  targetAction.fadeIn(transitionDuration);
755
- this.currentAnimation = targetAnimation;
811
+ }
812
+ update(time) {
813
+ if (this.animationMixer) {
814
+ this.animationMixer.update(time);
815
+ }
756
816
  }
757
817
  };
758
818
 
@@ -760,7 +820,7 @@ var CharacterModel = class {
760
820
  import {
761
821
  CircleGeometry,
762
822
  GLSL3,
763
- Mesh as Mesh2,
823
+ Mesh,
764
824
  RawShaderMaterial
765
825
  } from "three";
766
826
  var CharacterSpeakingIndicator = class {
@@ -824,7 +884,7 @@ var CharacterSpeakingIndicator = class {
824
884
  transparent: true,
825
885
  glslVersion: GLSL3
826
886
  });
827
- this.mesh = new Mesh2(this.geometry, this.material);
887
+ this.mesh = new Mesh(this.geometry, this.material);
828
888
  this.currentAlpha = 0;
829
889
  this.targetAlpha = 0;
830
890
  this.scene.add(this.mesh);
@@ -853,7 +913,7 @@ import {
853
913
  Color as Color2,
854
914
  FrontSide,
855
915
  LinearFilter as LinearFilter2,
856
- Mesh as Mesh3,
916
+ Mesh as Mesh2,
857
917
  MeshBasicMaterial as MeshBasicMaterial2,
858
918
  PlaneGeometry
859
919
  } from "three";
@@ -978,8 +1038,10 @@ var defaultLabelPadding = 0;
978
1038
  var defaultLabelWidth = 0.25;
979
1039
  var defaultLabelHeight = 0.125;
980
1040
  var defaultLabelCastShadows = true;
981
- var CharacterTooltip = class {
982
- constructor(parentModel) {
1041
+ var tooltipGeometry = new PlaneGeometry(1, 1, 1, 1);
1042
+ var CharacterTooltip = class extends Mesh2 {
1043
+ constructor() {
1044
+ super(tooltipGeometry);
983
1045
  this.visibleOpacity = 0.85;
984
1046
  this.targetOpacity = 0;
985
1047
  this.fadingSpeed = 0.02;
@@ -995,25 +1057,22 @@ var CharacterTooltip = class {
995
1057
  fontColor: defaultFontColor,
996
1058
  castShadows: defaultLabelCastShadows
997
1059
  };
998
- this.setText = this.setText.bind(this);
999
- this.material = new MeshBasicMaterial2({
1060
+ this.tooltipMaterial = new MeshBasicMaterial2({
1000
1061
  map: null,
1001
1062
  transparent: true,
1002
- opacity: 0
1063
+ opacity: 0,
1064
+ side: FrontSide
1003
1065
  });
1004
- this.material.side = FrontSide;
1005
- this.geometry = new PlaneGeometry(1, 1, 1, 1);
1006
- this.mesh = new Mesh3(this.geometry, this.material);
1007
- this.mesh.position.set(0, 1.6, 0);
1008
- this.mesh.visible = false;
1009
- parentModel.add(this.mesh);
1066
+ this.material = this.tooltipMaterial;
1067
+ this.position.set(0, 1.6, 0);
1068
+ this.visible = false;
1010
1069
  }
1011
1070
  redrawText(content) {
1012
- if (!this.material) {
1071
+ if (!this.tooltipMaterial) {
1013
1072
  return;
1014
1073
  }
1015
- if (this.material.map) {
1016
- this.material.map.dispose();
1074
+ if (this.tooltipMaterial.map) {
1075
+ this.tooltipMaterial.map.dispose();
1017
1076
  }
1018
1077
  const { texture, width, height } = THREECanvasTextTexture(content, {
1019
1078
  bold: true,
@@ -1037,17 +1096,17 @@ var CharacterTooltip = class {
1037
1096
  },
1038
1097
  alignment: this.props.alignment
1039
1098
  });
1040
- this.material.map = texture;
1041
- this.material.map.magFilter = LinearFilter2;
1042
- this.material.map.minFilter = LinearFilter2;
1043
- this.material.needsUpdate = true;
1044
- this.mesh.scale.x = width / (100 * fontScale);
1045
- this.mesh.scale.y = height / (100 * fontScale);
1046
- this.mesh.position.y = 1.6;
1099
+ this.tooltipMaterial.map = texture;
1100
+ this.tooltipMaterial.map.magFilter = LinearFilter2;
1101
+ this.tooltipMaterial.map.minFilter = LinearFilter2;
1102
+ this.tooltipMaterial.needsUpdate = true;
1103
+ this.scale.x = width / (100 * fontScale);
1104
+ this.scale.y = height / (100 * fontScale);
1105
+ this.position.y = 1.6;
1047
1106
  }
1048
1107
  setText(text, temporary = false) {
1049
1108
  this.redrawText(text);
1050
- this.mesh.visible = true;
1109
+ this.visible = true;
1051
1110
  this.targetOpacity = this.visibleOpacity;
1052
1111
  if (temporary) {
1053
1112
  setTimeout(() => {
@@ -1059,51 +1118,126 @@ var CharacterTooltip = class {
1059
1118
  this.targetOpacity = 0;
1060
1119
  }
1061
1120
  update(camera) {
1062
- this.mesh.lookAt(camera.position);
1063
- const opacity = this.mesh.material.opacity;
1121
+ this.lookAt(camera.position);
1122
+ const opacity = this.tooltipMaterial.opacity;
1064
1123
  if (opacity < this.targetOpacity) {
1065
- this.mesh.material.opacity = Math.min(
1066
- this.mesh.material.opacity + this.fadingSpeed,
1124
+ this.tooltipMaterial.opacity = Math.min(
1125
+ this.tooltipMaterial.opacity + this.fadingSpeed,
1067
1126
  this.targetOpacity
1068
1127
  );
1069
1128
  } else if (opacity > this.targetOpacity) {
1070
- this.mesh.material.opacity = Math.max(
1071
- this.mesh.material.opacity - this.fadingSpeed,
1129
+ this.tooltipMaterial.opacity = Math.max(
1130
+ this.tooltipMaterial.opacity - this.fadingSpeed,
1072
1131
  this.targetOpacity
1073
1132
  );
1074
- if (opacity >= 1 && this.mesh.material.transparent === true) {
1075
- this.mesh.material.transparent = false;
1076
- this.mesh.material.needsUpdate = true;
1077
- } else if (opacity > 0 && opacity < 1 && this.mesh.material.transparent === false) {
1078
- this.mesh.material.transparent = true;
1079
- this.mesh.material.needsUpdate = true;
1133
+ if (opacity >= 1 && this.tooltipMaterial.transparent) {
1134
+ this.tooltipMaterial.transparent = false;
1135
+ this.tooltipMaterial.needsUpdate = true;
1136
+ } else if (opacity > 0 && opacity < 1 && !this.tooltipMaterial.transparent) {
1137
+ this.tooltipMaterial.transparent = true;
1138
+ this.tooltipMaterial.needsUpdate = true;
1080
1139
  }
1081
- if (this.mesh.material.opacity <= 0) {
1082
- this.mesh.visible = false;
1140
+ if (this.tooltipMaterial.opacity <= 0) {
1141
+ this.visible = false;
1142
+ }
1143
+ }
1144
+ }
1145
+ };
1146
+
1147
+ // src/character/Character.ts
1148
+ var Character = class extends Group {
1149
+ constructor(characterDescription, animationConfig, characterModelLoader, characterId, modelLoadedCallback, cameraManager, composer) {
1150
+ super();
1151
+ this.characterDescription = characterDescription;
1152
+ this.animationConfig = animationConfig;
1153
+ this.characterModelLoader = characterModelLoader;
1154
+ this.characterId = characterId;
1155
+ this.modelLoadedCallback = modelLoadedCallback;
1156
+ this.cameraManager = cameraManager;
1157
+ this.composer = composer;
1158
+ this.model = null;
1159
+ this.color = new Color3();
1160
+ this.tooltip = null;
1161
+ this.speakingIndicator = null;
1162
+ this.tooltip = new CharacterTooltip();
1163
+ this.add(this.tooltip);
1164
+ this.load();
1165
+ }
1166
+ async load() {
1167
+ this.model = new CharacterModel(
1168
+ this.characterDescription,
1169
+ this.animationConfig,
1170
+ this.characterModelLoader
1171
+ );
1172
+ await this.model.init();
1173
+ this.add(this.model.mesh);
1174
+ if (this.speakingIndicator === null) {
1175
+ this.speakingIndicator = new CharacterSpeakingIndicator(this.composer.postPostScene);
1176
+ }
1177
+ this.color = this.model.material.colorsCube216[this.characterId];
1178
+ this.modelLoadedCallback();
1179
+ }
1180
+ updateAnimation(targetAnimation) {
1181
+ var _a;
1182
+ (_a = this.model) == null ? void 0 : _a.updateAnimation(targetAnimation);
1183
+ }
1184
+ update(time, deltaTime) {
1185
+ var _a;
1186
+ if (!this.model)
1187
+ return;
1188
+ if (this.tooltip) {
1189
+ this.tooltip.update(this.cameraManager.camera);
1190
+ }
1191
+ if (this.speakingIndicator) {
1192
+ this.speakingIndicator.setTime(time);
1193
+ if (this.model.mesh && this.model.headBone) {
1194
+ this.speakingIndicator.setBillboarding(
1195
+ (_a = this.model.headBone) == null ? void 0 : _a.getWorldPosition(new Vector34()),
1196
+ this.cameraManager.camera
1197
+ );
1083
1198
  }
1084
1199
  }
1200
+ if (typeof this.model.material.uniforms.time !== "undefined") {
1201
+ this.model.material.uniforms.time.value = time;
1202
+ this.model.material.uniforms.diffuseRandomColor.value = this.color;
1203
+ this.model.material.update();
1204
+ }
1205
+ this.model.update(deltaTime);
1206
+ }
1207
+ getCurrentAnimation() {
1208
+ var _a;
1209
+ return ((_a = this.model) == null ? void 0 : _a.currentAnimation) || 0 /* idle */;
1085
1210
  }
1086
1211
  };
1087
1212
 
1213
+ // src/character/CharacterManager.ts
1214
+ import { Euler as Euler2, Group as Group2, Quaternion as Quaternion5, Vector3 as Vector38 } from "three";
1215
+
1088
1216
  // src/character/LocalController.ts
1089
- import { Line3, Matrix4, Quaternion as Quaternion2, Raycaster as Raycaster2, Vector3 as Vector34 } from "three";
1217
+ import { Euler, Line3, Matrix4, Quaternion as Quaternion2, Ray, Raycaster as Raycaster2, Vector3 as Vector35 } from "three";
1218
+ var downVector = new Vector35(0, -1, 0);
1219
+ var airResistance = 0.5;
1220
+ var groundResistance = 0.99999999;
1221
+ var airControlModifier = 0.05;
1222
+ var groundWalkControl = 0.75;
1223
+ var groundRunControl = 1;
1224
+ var baseControl = 200;
1225
+ var collisionDetectionSteps = 15;
1226
+ var minimumSurfaceAngle = 0.9;
1090
1227
  var LocalController = class {
1091
- constructor(model, id, collisionsManager, keyInputManager, cameraManager, timeManager) {
1092
- this.model = model;
1228
+ constructor(character, id, collisionsManager, keyInputManager, cameraManager, timeManager) {
1229
+ this.character = character;
1093
1230
  this.id = id;
1094
1231
  this.collisionsManager = collisionsManager;
1095
1232
  this.keyInputManager = keyInputManager;
1096
1233
  this.cameraManager = cameraManager;
1097
1234
  this.timeManager = timeManager;
1098
- this.collisionDetectionSteps = 15;
1099
1235
  this.capsuleInfo = {
1100
1236
  radius: 0.4,
1101
- segment: new Line3(new Vector34(), new Vector34(0, 1.05, 0))
1237
+ segment: new Line3(new Vector35(), new Vector35(0, 1.05, 0))
1102
1238
  };
1103
- this.maxWalkSpeed = 6;
1104
- this.maxRunSpeed = 8.5;
1105
1239
  this.gravity = -42;
1106
- this.jumpForce = 16;
1240
+ this.jumpForce = 20;
1107
1241
  this.coyoteTimeThreshold = 70;
1108
1242
  this.coyoteTime = false;
1109
1243
  this.canJump = true;
@@ -1111,19 +1245,29 @@ var LocalController = class {
1111
1245
  this.characterWasOnGround = false;
1112
1246
  this.characterAirborneSince = 0;
1113
1247
  this.currentHeight = 0;
1114
- this.characterVelocity = new Vector34();
1115
- this.vectorUp = new Vector34(0, 1, 0);
1116
- this.vectorDown = new Vector34(0, -1, 0);
1248
+ this.currentSurfaceAngle = new Vector35();
1249
+ this.characterVelocity = new Vector35();
1250
+ this.vectorUp = new Vector35(0, 1, 0);
1251
+ this.vectorDown = new Vector35(0, -1, 0);
1117
1252
  this.rotationOffset = 0;
1118
1253
  this.azimuthalAngle = 0;
1119
1254
  this.tempMatrix = new Matrix4();
1120
1255
  this.tempSegment = new Line3();
1121
- this.tempVector = new Vector34();
1122
- this.tempVector2 = new Vector34();
1256
+ this.tempQuaternion = new Quaternion2();
1257
+ this.tempEuler = new Euler();
1258
+ this.tempVector = new Vector35();
1259
+ this.tempVector2 = new Vector35();
1260
+ this.tempVector3 = new Vector35();
1123
1261
  this.rayCaster = new Raycaster2();
1124
- this.thirdPersonCamera = null;
1125
- this.speed = 0;
1126
- this.targetSpeed = 0;
1262
+ this.surfaceTempQuaternion = new Quaternion2();
1263
+ this.surfaceTempQuaternion2 = new Quaternion2();
1264
+ this.surfaceTempVector1 = new Vector35();
1265
+ this.surfaceTempVector2 = new Vector35();
1266
+ this.surfaceTempVector3 = new Vector35();
1267
+ this.surfaceTempVector4 = new Vector35();
1268
+ this.surfaceTempVector5 = new Vector35();
1269
+ this.surfaceTempRay = new Ray();
1270
+ this.lastFrameSurfaceState = null;
1127
1271
  this.networkState = {
1128
1272
  id: this.id,
1129
1273
  position: { x: 0, y: 0, z: 0 },
@@ -1131,52 +1275,55 @@ var LocalController = class {
1131
1275
  state: 0 /* idle */
1132
1276
  };
1133
1277
  }
1278
+ updateControllerState() {
1279
+ this.forward = this.keyInputManager.forward;
1280
+ this.backward = this.keyInputManager.backward;
1281
+ this.left = this.keyInputManager.left;
1282
+ this.right = this.keyInputManager.right;
1283
+ this.run = this.keyInputManager.run;
1284
+ this.jump = this.keyInputManager.jump;
1285
+ this.anyDirection = this.keyInputManager.anyDirection;
1286
+ this.conflictingDirections = this.keyInputManager.conflictingDirection;
1287
+ }
1134
1288
  update() {
1135
- var _a, _b;
1136
- if (!((_a = this.model) == null ? void 0 : _a.mesh) || !((_b = this.model) == null ? void 0 : _b.animationMixer))
1137
- return;
1138
- if (!this.thirdPersonCamera)
1139
- this.thirdPersonCamera = this.cameraManager.camera;
1140
- const { forward, backward, left, right, run, jump, anyDirection, conflictingDirection } = this.keyInputManager;
1141
- this.forward = forward;
1142
- this.backward = backward;
1143
- this.left = left;
1144
- this.right = right;
1145
- this.run = run;
1146
- this.jump = jump;
1147
- this.anyDirection = anyDirection;
1148
- this.conflictingDirections = conflictingDirection;
1149
- this.targetSpeed = this.run ? this.maxRunSpeed : this.maxWalkSpeed;
1150
- this.speed += ease(this.targetSpeed, this.speed, 0.07);
1151
- this.rayCaster.set(this.model.mesh.position, this.vectorDown);
1152
- const minimumDistance = this.collisionsManager.raycastFirstDistance(this.rayCaster.ray);
1153
- if (minimumDistance !== null) {
1154
- this.currentHeight = minimumDistance;
1155
- }
1156
- if (anyDirection || !this.characterOnGround) {
1289
+ this.updateControllerState();
1290
+ this.rayCaster.set(this.character.position, this.vectorDown);
1291
+ const firstRaycastHit = this.collisionsManager.raycastFirst(this.rayCaster.ray);
1292
+ if (firstRaycastHit !== null) {
1293
+ this.currentHeight = firstRaycastHit[0];
1294
+ this.currentSurfaceAngle.copy(firstRaycastHit[1]);
1295
+ }
1296
+ if (this.anyDirection || !this.characterOnGround) {
1157
1297
  const targetAnimation = this.getTargetAnimation();
1158
- this.model.updateAnimation(targetAnimation, this.timeManager.deltaTime);
1298
+ this.character.updateAnimation(targetAnimation);
1159
1299
  } else {
1160
- this.model.updateAnimation(0 /* idle */, this.timeManager.deltaTime);
1300
+ this.character.updateAnimation(0 /* idle */);
1161
1301
  }
1162
- if (this.anyDirection)
1302
+ if (this.anyDirection) {
1163
1303
  this.updateRotation();
1164
- for (let i = 0; i < this.collisionDetectionSteps; i++) {
1165
- this.updatePosition(this.timeManager.deltaTime / this.collisionDetectionSteps, i);
1166
1304
  }
1167
- if (this.model.mesh.position.y < 0)
1305
+ for (let i = 0; i < collisionDetectionSteps; i++) {
1306
+ this.updatePosition(
1307
+ this.timeManager.deltaTime,
1308
+ this.timeManager.deltaTime / collisionDetectionSteps,
1309
+ i
1310
+ );
1311
+ }
1312
+ if (this.character.position.y < 0) {
1168
1313
  this.resetPosition();
1314
+ }
1169
1315
  this.updateNetworkState();
1170
1316
  }
1171
1317
  getTargetAnimation() {
1172
- if (!this.model.mesh)
1173
- return 0 /* idle */;
1174
- if (this.conflictingDirections)
1318
+ if (!this.character)
1175
1319
  return 0 /* idle */;
1176
1320
  const jumpHeight = this.characterVelocity.y > 0 ? 0.2 : 1.8;
1177
1321
  if (this.currentHeight > jumpHeight && !this.characterOnGround) {
1178
1322
  return 4 /* air */;
1179
1323
  }
1324
+ if (this.conflictingDirections) {
1325
+ return 0 /* idle */;
1326
+ }
1180
1327
  return this.run && this.anyDirection ? 2 /* running */ : this.anyDirection ? 1 /* walking */ : 0 /* idle */;
1181
1328
  }
1182
1329
  updateRotationOffset() {
@@ -1201,239 +1348,200 @@ var LocalController = class {
1201
1348
  }
1202
1349
  }
1203
1350
  updateAzimuthalAngle() {
1204
- var _a;
1205
- if (!this.thirdPersonCamera || !((_a = this.model) == null ? void 0 : _a.mesh))
1206
- return;
1207
- const camToModelDistance = this.thirdPersonCamera.position.distanceTo(this.model.mesh.position);
1351
+ const camToModelDistance = this.cameraManager.camera.position.distanceTo(
1352
+ this.character.position
1353
+ );
1208
1354
  const isCameraFirstPerson = camToModelDistance < 2;
1209
1355
  if (isCameraFirstPerson) {
1210
- const cameraForward = new Vector34(0, 0, 1).applyQuaternion(this.thirdPersonCamera.quaternion);
1356
+ const cameraForward = this.tempVector.set(0, 0, 1).applyQuaternion(this.cameraManager.camera.quaternion);
1211
1357
  this.azimuthalAngle = Math.atan2(cameraForward.x, cameraForward.z);
1212
1358
  } else {
1213
1359
  this.azimuthalAngle = Math.atan2(
1214
- this.thirdPersonCamera.position.x - this.model.mesh.position.x,
1215
- this.thirdPersonCamera.position.z - this.model.mesh.position.z
1360
+ this.cameraManager.camera.position.x - this.character.position.x,
1361
+ this.cameraManager.camera.position.z - this.character.position.z
1216
1362
  );
1217
1363
  }
1218
1364
  }
1219
1365
  computeAngularDifference(rotationQuaternion) {
1220
- var _a;
1221
- if (!((_a = this.model) == null ? void 0 : _a.mesh))
1222
- return 0;
1223
- return 2 * Math.acos(Math.abs(this.model.mesh.quaternion.dot(rotationQuaternion)));
1366
+ return 2 * Math.acos(Math.abs(this.character.quaternion.dot(rotationQuaternion)));
1224
1367
  }
1225
1368
  updateRotation() {
1226
- var _a;
1227
- if (!this.thirdPersonCamera || !((_a = this.model) == null ? void 0 : _a.mesh))
1228
- return;
1229
1369
  this.updateRotationOffset();
1230
1370
  this.updateAzimuthalAngle();
1231
- const rotationQuaternion = new Quaternion2();
1232
- rotationQuaternion.setFromAxisAngle(this.vectorUp, this.azimuthalAngle + this.rotationOffset);
1371
+ const rotationQuaternion = this.tempQuaternion.setFromAxisAngle(
1372
+ this.vectorUp,
1373
+ this.azimuthalAngle + this.rotationOffset
1374
+ );
1233
1375
  const angularDifference = this.computeAngularDifference(rotationQuaternion);
1234
1376
  const desiredTime = 0.07;
1235
1377
  const angularSpeed = angularDifference / desiredTime;
1236
1378
  const frameRotation = angularSpeed * this.timeManager.deltaTime;
1237
- this.model.mesh.quaternion.rotateTowards(rotationQuaternion, frameRotation);
1238
- }
1239
- addScaledVectorToCharacter(deltaTime) {
1240
- var _a;
1241
- if (!((_a = this.model) == null ? void 0 : _a.mesh))
1242
- return;
1243
- this.model.mesh.position.addScaledVector(this.tempVector, this.speed * deltaTime);
1379
+ this.character.quaternion.rotateTowards(rotationQuaternion, frameRotation);
1244
1380
  }
1245
- updatePosition(deltaTime, _iter) {
1246
- var _a;
1247
- if (!((_a = this.model) == null ? void 0 : _a.mesh))
1248
- return;
1381
+ applyControls(deltaTime) {
1382
+ const resistance = this.characterOnGround ? groundResistance : airResistance;
1383
+ const speedFactor = Math.pow(1 - resistance, deltaTime);
1384
+ this.characterVelocity.multiplyScalar(speedFactor);
1385
+ const acceleration = this.tempVector.set(0, 0, 0);
1249
1386
  if (this.characterOnGround) {
1250
- if (!this.jump)
1387
+ if (!this.jump) {
1251
1388
  this.canJump = true;
1389
+ }
1252
1390
  if (this.jump && this.canJump) {
1253
- this.characterVelocity.y += this.jumpForce;
1391
+ acceleration.y += this.jumpForce / deltaTime;
1254
1392
  this.canJump = false;
1255
1393
  } else {
1256
- this.characterVelocity.y = deltaTime * this.gravity;
1394
+ if (this.currentSurfaceAngle.y < minimumSurfaceAngle) {
1395
+ acceleration.y += this.gravity;
1396
+ }
1257
1397
  }
1258
1398
  } else if (this.jump && this.coyoteTime) {
1259
- this.characterVelocity.y = this.jumpForce;
1399
+ acceleration.y += this.jumpForce / deltaTime;
1260
1400
  this.canJump = false;
1261
1401
  } else {
1262
- this.characterVelocity.y += deltaTime * this.gravity;
1402
+ acceleration.y += this.gravity;
1263
1403
  this.canJump = false;
1264
1404
  }
1265
- this.model.mesh.position.addScaledVector(this.characterVelocity, deltaTime);
1266
- this.tempVector.set(0, 0, 0);
1267
- if (this.forward) {
1268
- const forward = new Vector34(0, 0, -1).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
1269
- this.tempVector.add(forward);
1270
- }
1271
- if (this.backward) {
1272
- const backward = new Vector34(0, 0, 1).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
1273
- this.tempVector.add(backward);
1274
- }
1275
- if (this.left) {
1276
- const left = new Vector34(-1, 0, 0).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
1277
- this.tempVector.add(left);
1278
- }
1279
- if (this.right) {
1280
- const right = new Vector34(1, 0, 0).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
1281
- this.tempVector.add(right);
1282
- }
1283
- if (this.tempVector.length() > 0) {
1284
- this.tempVector.normalize();
1285
- this.addScaledVectorToCharacter(deltaTime);
1286
- }
1287
- this.model.mesh.updateMatrixWorld();
1288
- this.tempSegment.copy(this.capsuleInfo.segment);
1289
- this.tempSegment.start.applyMatrix4(this.model.mesh.matrixWorld).applyMatrix4(this.tempMatrix);
1290
- this.tempSegment.end.applyMatrix4(this.model.mesh.matrixWorld).applyMatrix4(this.tempMatrix);
1291
- this.collisionsManager.applyColliders(this.tempSegment, this.capsuleInfo.radius);
1292
- const newPosition = this.tempVector;
1293
- newPosition.copy(this.tempSegment.start);
1294
- const deltaVector = this.tempVector2;
1295
- deltaVector.subVectors(newPosition, this.model.mesh.position);
1296
- const offset = Math.max(0, deltaVector.length() - 1e-5);
1297
- deltaVector.normalize().multiplyScalar(offset);
1298
- this.model.mesh.position.add(deltaVector);
1299
- this.characterOnGround = deltaVector.y > Math.abs(deltaTime * this.characterVelocity.y * 0.25);
1405
+ const control = (this.characterOnGround ? this.run ? groundRunControl : groundWalkControl : airControlModifier) * baseControl;
1406
+ const controlAcceleration = this.tempVector2.set(0, 0, 0);
1407
+ if (!this.conflictingDirections) {
1408
+ if (this.forward) {
1409
+ const forward = this.tempVector3.set(0, 0, -1).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
1410
+ controlAcceleration.add(forward);
1411
+ }
1412
+ if (this.backward) {
1413
+ const backward = this.tempVector3.set(0, 0, 1).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
1414
+ controlAcceleration.add(backward);
1415
+ }
1416
+ if (this.left) {
1417
+ const left = this.tempVector3.set(-1, 0, 0).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
1418
+ controlAcceleration.add(left);
1419
+ }
1420
+ if (this.right) {
1421
+ const right = this.tempVector3.set(1, 0, 0).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
1422
+ controlAcceleration.add(right);
1423
+ }
1424
+ }
1425
+ if (controlAcceleration.length() > 0) {
1426
+ controlAcceleration.normalize();
1427
+ controlAcceleration.multiplyScalar(control);
1428
+ }
1429
+ acceleration.add(controlAcceleration);
1430
+ this.characterVelocity.addScaledVector(acceleration, deltaTime);
1431
+ this.character.position.addScaledVector(this.characterVelocity, deltaTime);
1432
+ }
1433
+ updatePosition(deltaTime, stepDeltaTime, iter) {
1434
+ this.applyControls(stepDeltaTime);
1435
+ if (iter === 0) {
1436
+ const lastMovement = this.getMovementFromSurfaces(this.character.position, deltaTime);
1437
+ if (lastMovement) {
1438
+ this.character.position.add(lastMovement.position);
1439
+ const asQuaternion = this.tempQuaternion.setFromEuler(this.character.rotation);
1440
+ const lastMovementEuler = this.tempEuler.setFromQuaternion(lastMovement.rotation);
1441
+ lastMovementEuler.x = 0;
1442
+ lastMovementEuler.z = 0;
1443
+ lastMovement.rotation.setFromEuler(lastMovementEuler);
1444
+ asQuaternion.multiply(lastMovement.rotation);
1445
+ this.character.rotation.setFromQuaternion(asQuaternion);
1446
+ }
1447
+ }
1448
+ this.character.updateMatrixWorld();
1449
+ const avatarSegment = this.tempSegment;
1450
+ avatarSegment.copy(this.capsuleInfo.segment);
1451
+ avatarSegment.start.applyMatrix4(this.character.matrixWorld).applyMatrix4(this.tempMatrix);
1452
+ avatarSegment.end.applyMatrix4(this.character.matrixWorld).applyMatrix4(this.tempMatrix);
1453
+ const positionBeforeCollisions = this.tempVector.copy(avatarSegment.start);
1454
+ this.collisionsManager.applyColliders(avatarSegment, this.capsuleInfo.radius);
1455
+ this.character.position.copy(avatarSegment.start);
1456
+ const deltaCollisionPosition = avatarSegment.start.sub(positionBeforeCollisions);
1457
+ this.characterOnGround = deltaCollisionPosition.y > 0;
1300
1458
  if (this.characterWasOnGround && !this.characterOnGround) {
1301
1459
  this.characterAirborneSince = Date.now();
1302
1460
  }
1303
- this.coyoteTime = this.characterVelocity.y < 0 && this.characterOnGround === false && Date.now() - this.characterAirborneSince < this.coyoteTimeThreshold;
1461
+ this.coyoteTime = this.characterVelocity.y < 0 && !this.characterOnGround && Date.now() - this.characterAirborneSince < this.coyoteTimeThreshold;
1304
1462
  this.characterWasOnGround = this.characterOnGround;
1305
- if (this.characterOnGround) {
1306
- this.characterVelocity.set(0, 0, 0);
1463
+ }
1464
+ getMovementFromSurfaces(userPosition, deltaTime) {
1465
+ let lastMovement = null;
1466
+ if (this.lastFrameSurfaceState !== null) {
1467
+ const meshState = this.lastFrameSurfaceState[0];
1468
+ const currentFrameMatrix = meshState.matrix;
1469
+ const lastFrameMatrix = this.lastFrameSurfaceState[1].lastMatrix;
1470
+ if (lastFrameMatrix.equals(currentFrameMatrix)) {
1471
+ } else {
1472
+ const lastMeshPosition = this.surfaceTempVector1;
1473
+ const lastMeshRotation = this.surfaceTempQuaternion;
1474
+ lastFrameMatrix.decompose(lastMeshPosition, lastMeshRotation, this.surfaceTempVector3);
1475
+ const currentMeshPosition = this.surfaceTempVector2;
1476
+ const currentMeshRotation = this.surfaceTempQuaternion2;
1477
+ currentFrameMatrix.decompose(
1478
+ currentMeshPosition,
1479
+ currentMeshRotation,
1480
+ this.surfaceTempVector3
1481
+ );
1482
+ const meshTranslationDelta = this.surfaceTempVector5.copy(currentMeshPosition).sub(lastMeshPosition);
1483
+ const lastFrameRelativeUserPosition = this.surfaceTempVector3.copy(userPosition).sub(lastMeshPosition);
1484
+ const meshRotationDelta = lastMeshRotation.invert().multiply(currentMeshRotation);
1485
+ const translationDueToRotation = this.surfaceTempVector4.copy(lastFrameRelativeUserPosition).applyQuaternion(meshRotationDelta).sub(lastFrameRelativeUserPosition);
1486
+ const translationAndRotationPositionDelta = this.surfaceTempVector1.copy(meshTranslationDelta).add(translationDueToRotation);
1487
+ lastMovement = {
1488
+ position: translationAndRotationPositionDelta,
1489
+ rotation: meshRotationDelta
1490
+ };
1491
+ lastFrameMatrix.copy(currentFrameMatrix);
1492
+ }
1493
+ }
1494
+ const newPosition = this.surfaceTempVector3.copy(userPosition);
1495
+ if (lastMovement) {
1496
+ newPosition.add(lastMovement.position);
1497
+ }
1498
+ newPosition.setY(newPosition.y + 0.05);
1499
+ const ray = this.surfaceTempRay.set(newPosition, downVector);
1500
+ const hit = this.collisionsManager.raycastFirst(ray);
1501
+ if (hit && hit[0] < 0.8) {
1502
+ const currentCollisionMeshState = hit[2];
1503
+ this.lastFrameSurfaceState = [
1504
+ currentCollisionMeshState,
1505
+ { lastMatrix: currentCollisionMeshState.matrix.clone() }
1506
+ ];
1307
1507
  } else {
1308
- deltaVector.normalize();
1309
- this.characterVelocity.addScaledVector(deltaVector, -deltaVector.dot(this.characterVelocity));
1508
+ if (this.lastFrameSurfaceState !== null && lastMovement) {
1509
+ this.characterVelocity.add(
1510
+ lastMovement.position.clone().divideScalar(deltaTime)
1511
+ // The position delta is the result of one tick which is deltaTime seconds, so we need to divide by deltaTime to get the velocity per second
1512
+ );
1513
+ }
1514
+ this.lastFrameSurfaceState = null;
1310
1515
  }
1516
+ return lastMovement;
1311
1517
  }
1312
1518
  updateNetworkState() {
1313
- var _a;
1314
- if (!((_a = this.model) == null ? void 0 : _a.mesh)) {
1315
- this.networkState = {
1316
- id: this.id,
1317
- position: new Vector34(),
1318
- rotation: { quaternionY: 0, quaternionW: 1 },
1319
- state: 0 /* idle */
1320
- };
1321
- } else {
1322
- const characterQuaternion = this.model.mesh.getWorldQuaternion(new Quaternion2());
1323
- const positionUpdate = new Vector34(
1324
- this.model.mesh.position.x,
1325
- this.model.mesh.position.y,
1326
- this.model.mesh.position.z
1327
- );
1328
- this.networkState = {
1329
- id: this.id,
1330
- position: positionUpdate,
1331
- rotation: { quaternionY: characterQuaternion.y, quaternionW: characterQuaternion.w },
1332
- state: this.model.currentAnimation
1333
- };
1334
- }
1519
+ const characterQuaternion = this.character.getWorldQuaternion(this.tempQuaternion);
1520
+ this.networkState = {
1521
+ id: this.id,
1522
+ position: {
1523
+ x: this.character.position.x,
1524
+ y: this.character.position.y,
1525
+ z: this.character.position.z
1526
+ },
1527
+ rotation: { quaternionY: characterQuaternion.y, quaternionW: characterQuaternion.w },
1528
+ state: this.character.getCurrentAnimation()
1529
+ };
1335
1530
  }
1336
1531
  resetPosition() {
1337
- var _a;
1338
- if (!((_a = this.model) == null ? void 0 : _a.mesh))
1339
- return;
1340
1532
  this.characterVelocity.y = 0;
1341
- this.model.mesh.position.y = 3;
1533
+ this.character.position.y = 3;
1342
1534
  this.characterOnGround = false;
1343
1535
  }
1344
1536
  };
1345
1537
 
1346
- // src/character/Character.ts
1347
- var Character = class {
1348
- constructor(characterDescription, characterModelLoader, id, isLocal, modelLoadedCallback, collisionsManager, keyInputManager, cameraManager, timeManager, composer) {
1349
- this.characterDescription = characterDescription;
1350
- this.characterModelLoader = characterModelLoader;
1351
- this.id = id;
1352
- this.isLocal = isLocal;
1353
- this.modelLoadedCallback = modelLoadedCallback;
1354
- this.collisionsManager = collisionsManager;
1355
- this.keyInputManager = keyInputManager;
1356
- this.cameraManager = cameraManager;
1357
- this.timeManager = timeManager;
1358
- this.composer = composer;
1359
- this.controller = null;
1360
- this.name = null;
1361
- this.model = null;
1362
- this.color = new Color3();
1363
- this.position = new Vector35();
1364
- this.tooltip = null;
1365
- this.speakingIndicator = null;
1366
- this.load();
1367
- }
1368
- async load() {
1369
- this.model = new CharacterModel(this.characterDescription, this.characterModelLoader);
1370
- await this.model.init();
1371
- if (this.tooltip === null) {
1372
- this.tooltip = new CharacterTooltip(this.model.mesh);
1373
- }
1374
- if (this.speakingIndicator === null) {
1375
- this.speakingIndicator = new CharacterSpeakingIndicator(this.composer.postPostScene);
1376
- }
1377
- this.color = this.model.material.colorsCube216[this.id];
1378
- if (this.isLocal) {
1379
- this.controller = new LocalController(
1380
- this.model,
1381
- this.id,
1382
- this.collisionsManager,
1383
- this.keyInputManager,
1384
- this.cameraManager,
1385
- this.timeManager
1386
- );
1387
- }
1388
- this.modelLoadedCallback();
1389
- }
1390
- update(time) {
1391
- var _a;
1392
- if (!this.model)
1393
- return;
1394
- if (this.tooltip) {
1395
- this.tooltip.update(this.cameraManager.camera);
1396
- }
1397
- if (this.speakingIndicator) {
1398
- this.speakingIndicator.setTime(time);
1399
- if (this.model.mesh && this.model.headBone) {
1400
- this.speakingIndicator.setBillboarding(
1401
- (_a = this.model.headBone) == null ? void 0 : _a.getWorldPosition(new Vector35()),
1402
- this.cameraManager.camera
1403
- );
1404
- }
1405
- }
1406
- this.model.mesh.getWorldPosition(this.position);
1407
- if (typeof this.model.material.uniforms.time !== "undefined") {
1408
- this.model.material.uniforms.time.value = time;
1409
- this.model.material.uniforms.diffuseRandomColor.value = this.color;
1410
- this.model.material.update();
1411
- }
1412
- }
1413
- };
1414
-
1415
- // src/character/CharacterManager.ts
1416
- import { Euler, Group, Quaternion as Quaternion4, Vector3 as Vector37 } from "three";
1417
-
1418
1538
  // src/character/RemoteController.ts
1419
- import {
1420
- AnimationMixer as AnimationMixer2,
1421
- Object3D as Object3D4,
1422
- Quaternion as Quaternion3,
1423
- Vector3 as Vector36
1424
- } from "three";
1539
+ import { Quaternion as Quaternion3, Vector3 as Vector36 } from "three";
1425
1540
  var RemoteController = class {
1426
- constructor(character, characterModelLoader, id) {
1541
+ constructor(character, id) {
1427
1542
  this.character = character;
1428
- this.characterModelLoader = characterModelLoader;
1429
1543
  this.id = id;
1430
- this.characterModel = null;
1431
- this.animationMixer = new AnimationMixer2(new Object3D4());
1432
- this.animations = /* @__PURE__ */ new Map();
1433
1544
  this.currentAnimation = 0 /* idle */;
1434
- this.characterModel = this.character.model.mesh;
1435
- this.characterModel.updateMatrixWorld();
1436
- this.animationMixer = new AnimationMixer2(this.characterModel);
1437
1545
  this.networkState = {
1438
1546
  id: this.id,
1439
1547
  position: { x: 0, y: 0, z: 0 },
@@ -1444,48 +1552,23 @@ var RemoteController = class {
1444
1552
  update(clientUpdate, time, deltaTime) {
1445
1553
  if (!this.character)
1446
1554
  return;
1447
- this.character.update(time);
1448
1555
  this.updateFromNetwork(clientUpdate);
1449
- this.animationMixer.update(deltaTime);
1450
- }
1451
- async setAnimationFromFile(animationType, fileName) {
1452
- const animation = await this.characterModelLoader.load(fileName, "animation");
1453
- const animationAction = this.animationMixer.clipAction(animation);
1454
- this.animations.set(animationType, animationAction);
1455
- if (animationType === 0 /* idle */) {
1456
- animationAction.play();
1457
- }
1458
- }
1459
- transitionToAnimation(targetAnimation, transitionDuration = 0.15) {
1460
- if (this.currentAnimation === targetAnimation)
1461
- return;
1462
- const currentAction = this.animations.get(this.currentAnimation);
1463
- const targetAction = this.animations.get(targetAnimation);
1464
- if (!targetAction)
1465
- return;
1466
- if (currentAction) {
1467
- currentAction.enabled = true;
1468
- targetAction.reset().setEffectiveTimeScale(1).setEffectiveWeight(1).fadeIn(transitionDuration).play();
1469
- currentAction.crossFadeTo(targetAction, transitionDuration, true);
1470
- } else {
1471
- targetAction.play();
1472
- }
1473
- this.currentAnimation = targetAnimation;
1556
+ this.character.update(time, deltaTime);
1474
1557
  }
1475
1558
  updateFromNetwork(clientUpdate) {
1476
- if (!this.characterModel)
1477
- return;
1478
1559
  const { position, rotation, state } = clientUpdate;
1479
- this.characterModel.position.lerp(new Vector36(position.x, position.y, position.z), 0.15);
1560
+ this.character.position.lerp(new Vector36(position.x, position.y, position.z), 0.15);
1480
1561
  const rotationQuaternion = new Quaternion3(0, rotation.quaternionY, 0, rotation.quaternionW);
1481
- this.characterModel.quaternion.slerp(rotationQuaternion, 0.6);
1562
+ this.character.quaternion.slerp(rotationQuaternion, 0.6);
1482
1563
  if (state !== this.currentAnimation) {
1483
- this.transitionToAnimation(state);
1564
+ this.currentAnimation = state;
1565
+ this.character.updateAnimation(state);
1484
1566
  }
1485
1567
  }
1486
1568
  };
1487
1569
 
1488
- // src/character/CharacterManager.ts
1570
+ // src/character/url-position.ts
1571
+ import { Quaternion as Quaternion4, Vector3 as Vector37 } from "three";
1489
1572
  function encodeCharacterAndCamera(character, camera) {
1490
1573
  return [
1491
1574
  ...toArray(character.position),
@@ -1494,192 +1577,159 @@ function encodeCharacterAndCamera(character, camera) {
1494
1577
  ...toArray(camera.quaternion)
1495
1578
  ].join(",");
1496
1579
  }
1497
- function decodeCharacterAndCamera(hash, character, camera) {
1580
+ function decodeCharacterAndCamera(hash) {
1498
1581
  const values = hash.split(",").map(Number);
1499
- character.position.fromArray(values.slice(0, 3));
1500
- character.quaternion.fromArray(values.slice(3, 7));
1501
- camera.position.fromArray(values.slice(7, 10));
1502
- camera.quaternion.fromArray(values.slice(10, 14));
1582
+ return {
1583
+ character: {
1584
+ position: new Vector37(values[0], values[1], values[2]),
1585
+ quaternion: new Quaternion4(values[3], values[4], values[5], values[6])
1586
+ },
1587
+ camera: {
1588
+ position: new Vector37(values[7], values[8], values[9]),
1589
+ quaternion: new Quaternion4(values[10], values[11], values[12], values[13])
1590
+ }
1591
+ };
1503
1592
  }
1593
+
1594
+ // src/character/CharacterManager.ts
1504
1595
  var CharacterManager = class {
1505
- constructor(composer, characterModelLoader, collisionsManager, cameraManager, timeManager, inputManager, clientStates, sendUpdate) {
1596
+ constructor(composer, characterModelLoader, collisionsManager, cameraManager, timeManager, keyInputManager, clientStates, sendUpdate, animationConfig, characterDescription) {
1506
1597
  this.composer = composer;
1507
1598
  this.characterModelLoader = characterModelLoader;
1508
1599
  this.collisionsManager = collisionsManager;
1509
1600
  this.cameraManager = cameraManager;
1510
1601
  this.timeManager = timeManager;
1511
- this.inputManager = inputManager;
1602
+ this.keyInputManager = keyInputManager;
1512
1603
  this.clientStates = clientStates;
1513
1604
  this.sendUpdate = sendUpdate;
1514
- /*
1515
- TODO - re-enable updating location hash when there is a solution that waits for models to load (currently if the
1516
- character was standing on a model and the page is reloaded the character falls into the model before it loads and
1517
- can be trapped).
1518
- */
1519
- this.updateLocationHash = false;
1605
+ this.animationConfig = animationConfig;
1606
+ this.characterDescription = characterDescription;
1607
+ this.updateLocationHash = true;
1608
+ this.headTargetOffset = new Vector38(0, 1.3, 0);
1520
1609
  this.id = 0;
1521
- this.loadingCharacters = /* @__PURE__ */ new Map();
1522
1610
  this.remoteCharacters = /* @__PURE__ */ new Map();
1523
1611
  this.remoteCharacterControllers = /* @__PURE__ */ new Map();
1524
- this.characterDescription = null;
1525
- this.character = null;
1612
+ this.localCharacterSpawned = false;
1613
+ this.localCharacter = null;
1526
1614
  this.cameraOffsetTarget = 0;
1527
1615
  this.cameraOffset = 0;
1528
1616
  this.speakingCharacters = /* @__PURE__ */ new Map();
1529
- this.group = new Group();
1530
- }
1531
- /* TODO:
1532
- 1) Separate this method into spawnLocalCharacter and spawnRemoteCharacter
1533
- 2) Make this synchronous to avoid having loadingCharacters and instead manage
1534
- the mesh loading async (would allow us to show a nameplate where a remote
1535
- user is before the asset loads).
1536
- */
1537
- spawnCharacter(characterDescription, id, isLocal = false, spawnPosition = new Vector37(), spawnRotation = new Euler()) {
1538
- this.characterDescription = characterDescription;
1539
- const characterLoadingPromise = new Promise((resolve) => {
1540
- const character = new Character(
1541
- characterDescription,
1542
- this.characterModelLoader,
1543
- id,
1544
- isLocal,
1545
- () => {
1546
- var _a, _b, _c, _d, _e, _f, _g;
1547
- if (window.location.hash && window.location.hash.length > 1) {
1548
- decodeCharacterAndCamera(
1549
- window.location.hash.substring(1),
1550
- character.model.mesh,
1551
- this.cameraManager.camera
1552
- );
1553
- } else {
1554
- spawnPosition = spawnPosition || getSpawnPositionInsideCircle(3, 30, id, 0.4);
1555
- character.model.mesh.position.set(spawnPosition.x, spawnPosition.y, spawnPosition.z);
1556
- character.model.mesh.rotation.set(spawnRotation.x, spawnRotation.y, spawnRotation.z);
1557
- character.position.set(spawnPosition.x, spawnPosition.y, spawnPosition.z);
1558
- character.model.mesh.updateMatrixWorld();
1559
- const quaternion = new Quaternion4().setFromEuler(character.model.mesh.rotation);
1560
- this.sendUpdate({
1561
- id,
1562
- position: {
1563
- x: spawnPosition.x,
1564
- y: spawnPosition.y,
1565
- z: spawnPosition.z
1566
- },
1567
- rotation: { quaternionY: quaternion.y, quaternionW: quaternion.w },
1568
- state: 0 /* idle */
1569
- });
1570
- }
1571
- character.model.hideMaterialByMeshName("SK_Mannequin_2");
1572
- if (!isLocal) {
1573
- (_b = (_a = character.model) == null ? void 0 : _a.mesh) == null ? void 0 : _b.position.set(spawnPosition.x, spawnPosition.y, spawnPosition.z);
1574
- (_d = (_c = character.model) == null ? void 0 : _c.mesh) == null ? void 0 : _d.updateMatrixWorld();
1575
- character.position.set(spawnPosition.x, spawnPosition.y, spawnPosition.z);
1576
- }
1577
- this.group.add(character.model.mesh);
1578
- if (isLocal) {
1579
- this.id = id;
1580
- this.character = character;
1581
- (_e = this.character.tooltip) == null ? void 0 : _e.setText(`${id}`);
1582
- } else {
1583
- this.remoteCharacters.set(id, character);
1584
- const remoteController = new RemoteController(character, this.characterModelLoader, id);
1585
- remoteController.setAnimationFromFile(
1586
- 0 /* idle */,
1587
- characterDescription.idleAnimationFileUrl
1588
- );
1589
- remoteController.setAnimationFromFile(
1590
- 1 /* walking */,
1591
- characterDescription.jogAnimationFileUrl
1592
- );
1593
- remoteController.setAnimationFromFile(
1594
- 2 /* running */,
1595
- characterDescription.sprintAnimationFileUrl
1596
- );
1597
- remoteController.setAnimationFromFile(
1598
- 4 /* air */,
1599
- characterDescription.airAnimationFileUrl
1600
- );
1601
- (_f = remoteController.characterModel) == null ? void 0 : _f.position.set(
1602
- spawnPosition.x,
1603
- spawnPosition.y,
1604
- spawnPosition.z
1605
- );
1606
- this.remoteCharacterControllers.set(id, remoteController);
1607
- (_g = character.tooltip) == null ? void 0 : _g.setText(`${id}`);
1608
- }
1609
- resolve(character);
1610
- },
1611
- this.collisionsManager,
1612
- this.inputManager,
1613
- this.cameraManager,
1614
- this.timeManager,
1615
- this.composer
1616
- );
1617
- });
1618
- this.loadingCharacters.set(id, characterLoadingPromise);
1619
- return characterLoadingPromise;
1617
+ this.group = new Group2();
1620
1618
  }
1621
- getLocalCharacterPositionAndRotation() {
1622
- if (this.character && this.character.model && this.character.model.mesh) {
1623
- return {
1624
- position: this.character.model.mesh.position,
1625
- rotation: this.character.model.mesh.rotation
1626
- };
1627
- }
1628
- return {
1629
- position: { x: 0, y: 0, z: 0 },
1619
+ spawnLocalCharacter(characterDescription, id, spawnPosition = new Vector38(), spawnRotation = new Euler2()) {
1620
+ var _a;
1621
+ const character = new Character(
1622
+ characterDescription,
1623
+ this.animationConfig,
1624
+ this.characterModelLoader,
1625
+ id,
1626
+ () => {
1627
+ },
1628
+ this.cameraManager,
1629
+ this.composer
1630
+ );
1631
+ const quaternion = new Quaternion5().setFromEuler(character.rotation);
1632
+ this.sendUpdate({
1633
+ id,
1634
+ position: {
1635
+ x: spawnPosition.x,
1636
+ y: spawnPosition.y,
1637
+ z: spawnPosition.z
1638
+ },
1639
+ rotation: { quaternionY: quaternion.y, quaternionW: quaternion.w },
1640
+ state: 0 /* idle */
1641
+ });
1642
+ this.id = id;
1643
+ this.localCharacter = character;
1644
+ this.localController = new LocalController(
1645
+ this.localCharacter,
1646
+ this.id,
1647
+ this.collisionsManager,
1648
+ this.keyInputManager,
1649
+ this.cameraManager,
1650
+ this.timeManager
1651
+ );
1652
+ this.localCharacter.position.set(spawnPosition.x, spawnPosition.y, spawnPosition.z);
1653
+ this.localCharacter.rotation.set(spawnRotation.x, spawnRotation.y, spawnRotation.z);
1654
+ (_a = character.tooltip) == null ? void 0 : _a.setText(`${id}`);
1655
+ this.group.add(character);
1656
+ this.localCharacterSpawned = true;
1657
+ }
1658
+ spawnRemoteCharacter(characterDescription, id, spawnPosition = new Vector38(), spawnRotation = new Euler2()) {
1659
+ var _a;
1660
+ const character = new Character(
1661
+ characterDescription,
1662
+ this.animationConfig,
1663
+ this.characterModelLoader,
1664
+ id,
1665
+ () => {
1666
+ },
1667
+ this.cameraManager,
1668
+ this.composer
1669
+ );
1670
+ this.remoteCharacters.set(id, character);
1671
+ const remoteController = new RemoteController(character, id);
1672
+ remoteController.character.position.set(spawnPosition.x, spawnPosition.y, spawnPosition.z);
1673
+ remoteController.character.rotation.set(spawnRotation.x, spawnRotation.y, spawnRotation.z);
1674
+ this.remoteCharacterControllers.set(id, remoteController);
1675
+ (_a = character.tooltip) == null ? void 0 : _a.setText(`${id}`);
1676
+ this.group.add(character);
1677
+ }
1678
+ getLocalCharacterPositionAndRotation() {
1679
+ if (this.localCharacter && this.localCharacter && this.localCharacter) {
1680
+ return {
1681
+ position: this.localCharacter.position,
1682
+ rotation: this.localCharacter.rotation
1683
+ };
1684
+ }
1685
+ return {
1686
+ position: { x: 0, y: 0, z: 0 },
1630
1687
  rotation: { x: 0, y: 0, z: 0 }
1631
1688
  };
1632
1689
  }
1633
1690
  clear() {
1634
1691
  for (const [id, character] of this.remoteCharacters) {
1635
- this.group.remove(character.model.mesh);
1692
+ this.group.remove(character);
1636
1693
  this.remoteCharacters.delete(id);
1637
1694
  this.remoteCharacterControllers.delete(id);
1638
1695
  }
1639
- if (this.character) {
1640
- this.group.remove(this.character.model.mesh);
1641
- this.character = null;
1696
+ if (this.localCharacter) {
1697
+ this.group.remove(this.localCharacter);
1698
+ this.localCharacter = null;
1642
1699
  }
1643
- this.loadingCharacters.clear();
1644
1700
  }
1645
1701
  setSpeakingCharacter(id, value) {
1646
1702
  this.speakingCharacters.set(id, value);
1647
1703
  }
1648
1704
  update() {
1649
- var _a, _b, _c, _d;
1650
- if (this.character) {
1651
- this.character.update(this.timeManager.time);
1705
+ var _a, _b, _c;
1706
+ if (this.localCharacter) {
1707
+ this.localCharacter.update(this.timeManager.time, this.timeManager.deltaTime);
1652
1708
  if (this.speakingCharacters.has(this.id)) {
1653
- (_a = this.character.speakingIndicator) == null ? void 0 : _a.setSpeaking(this.speakingCharacters.get(this.id));
1654
- }
1655
- if ((_b = this.character.model) == null ? void 0 : _b.mesh) {
1656
- this.cameraOffsetTarget = this.cameraManager.targetDistance <= 0.4 ? 0.13 : 0;
1657
- this.cameraOffset += ease(this.cameraOffsetTarget, this.cameraOffset, 0.1);
1658
- const targetOffset = new Vector37(0, 1.3, this.cameraOffset);
1659
- targetOffset.applyQuaternion(this.character.model.mesh.quaternion);
1660
- this.cameraManager.setTarget(this.character.position.add(targetOffset));
1661
- }
1662
- if (this.character.controller) {
1663
- this.character.controller.update();
1664
- if (this.timeManager.frame % 2 === 0) {
1665
- this.sendUpdate(this.character.controller.networkState);
1666
- }
1709
+ (_a = this.localCharacter.speakingIndicator) == null ? void 0 : _a.setSpeaking(this.speakingCharacters.get(this.id));
1710
+ }
1711
+ this.localController.update();
1712
+ if (this.timeManager.frame % 2 === 0) {
1713
+ this.sendUpdate(this.localController.networkState);
1667
1714
  }
1715
+ this.cameraOffsetTarget = this.cameraManager.targetDistance <= 0.4 ? 0.13 : 0;
1716
+ this.cameraOffset += ease(this.cameraOffsetTarget, this.cameraOffset, 0.1);
1717
+ const targetOffset = new Vector38(0, 0, this.cameraOffset);
1718
+ targetOffset.add(this.headTargetOffset);
1719
+ targetOffset.applyQuaternion(this.localCharacter.quaternion);
1720
+ this.cameraManager.setTarget(targetOffset.add(this.localCharacter.position));
1668
1721
  for (const [id, update] of this.clientStates) {
1669
1722
  if (this.remoteCharacters.has(id) && this.speakingCharacters.has(id)) {
1670
1723
  const character = this.remoteCharacters.get(id);
1671
- (_c = character == null ? void 0 : character.speakingIndicator) == null ? void 0 : _c.setSpeaking(this.speakingCharacters.get(id));
1724
+ (_b = character == null ? void 0 : character.speakingIndicator) == null ? void 0 : _b.setSpeaking(this.speakingCharacters.get(id));
1672
1725
  }
1673
1726
  const { position } = update;
1674
- if (!this.remoteCharacters.has(id) && !this.loadingCharacters.has(id)) {
1675
- this.spawnCharacter(
1727
+ if (!this.remoteCharacters.has(id) && this.localCharacterSpawned === true) {
1728
+ this.spawnRemoteCharacter(
1676
1729
  this.characterDescription,
1677
1730
  id,
1678
- false,
1679
- new Vector37(position.x, position.y, position.z)
1680
- ).then((_character) => {
1681
- this.loadingCharacters.delete(id);
1682
- });
1731
+ new Vector38(position.x, position.y, position.z)
1732
+ );
1683
1733
  }
1684
1734
  const characterController = this.remoteCharacterControllers.get(id);
1685
1735
  if (characterController) {
@@ -1688,15 +1738,15 @@ var CharacterManager = class {
1688
1738
  }
1689
1739
  for (const [id, character] of this.remoteCharacters) {
1690
1740
  if (!this.clientStates.has(id)) {
1691
- (_d = character.speakingIndicator) == null ? void 0 : _d.dispose();
1692
- this.group.remove(character.model.mesh);
1741
+ (_c = character.speakingIndicator) == null ? void 0 : _c.dispose();
1742
+ this.group.remove(character);
1693
1743
  this.remoteCharacters.delete(id);
1694
1744
  this.remoteCharacterControllers.delete(id);
1695
1745
  }
1696
1746
  }
1697
1747
  if (this.updateLocationHash && this.timeManager.frame % 60 === 0) {
1698
1748
  window.location.hash = encodeCharacterAndCamera(
1699
- this.character.model.mesh,
1749
+ this.localCharacter,
1700
1750
  this.cameraManager.camera
1701
1751
  );
1702
1752
  }
@@ -1708,8 +1758,9 @@ var CharacterManager = class {
1708
1758
  import { LoadingManager } from "three";
1709
1759
  import { GLTFLoader as ThreeGLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
1710
1760
  var CachedGLTFLoader = class extends ThreeGLTFLoader {
1711
- constructor(manager) {
1761
+ constructor(manager, debug = false) {
1712
1762
  super(manager);
1763
+ this.debug = debug;
1713
1764
  this.blobCache = /* @__PURE__ */ new Map();
1714
1765
  }
1715
1766
  setBlobUrl(originalUrl, blobUrl) {
@@ -1721,7 +1772,9 @@ var CachedGLTFLoader = class extends ThreeGLTFLoader {
1721
1772
  load(url, onLoad, onProgress, onError) {
1722
1773
  const blobUrl = this.getBlobUrl(url);
1723
1774
  if (blobUrl) {
1724
- console.log(`Loading cached ${url.split("/").pop()}`);
1775
+ if (this.debug === true) {
1776
+ console.log(`Loading cached ${url.split("/").pop()}`);
1777
+ }
1725
1778
  super.load(blobUrl, onLoad, onProgress, onError);
1726
1779
  } else {
1727
1780
  super.load(url, onLoad, onProgress, onError);
@@ -1749,20 +1802,14 @@ var LRUCache = class {
1749
1802
  this.cache.set(key, value);
1750
1803
  }
1751
1804
  };
1752
- var _CharacterModelLoader = class _CharacterModelLoader {
1753
- constructor(maxCacheSize = 100) {
1805
+ var CharacterModelLoader = class {
1806
+ constructor(maxCacheSize = 100, debug = false) {
1807
+ this.debug = debug;
1754
1808
  this.ongoingLoads = /* @__PURE__ */ new Map();
1755
1809
  this.loadingManager = new LoadingManager();
1756
- this.gltfLoader = new CachedGLTFLoader(this.loadingManager);
1810
+ this.gltfLoader = new CachedGLTFLoader(this.loadingManager, this.debug);
1757
1811
  this.modelCache = new LRUCache(maxCacheSize);
1758
1812
  }
1759
- /* TODO: decide between below lazy initialization or eager on this file's bottom export */
1760
- static getInstance() {
1761
- if (!_CharacterModelLoader.instance) {
1762
- _CharacterModelLoader.instance = new _CharacterModelLoader();
1763
- }
1764
- return _CharacterModelLoader.instance;
1765
- }
1766
1813
  async load(fileUrl, fileType) {
1767
1814
  const cachedModel = this.modelCache.get(fileUrl);
1768
1815
  if (cachedModel) {
@@ -1770,19 +1817,27 @@ var _CharacterModelLoader = class _CharacterModelLoader {
1770
1817
  this.gltfLoader.setBlobUrl(fileUrl, blobURL);
1771
1818
  return this.loadFromUrl(fileUrl, fileType, cachedModel.originalExtension);
1772
1819
  } else {
1773
- console.log(`Loading ${fileUrl} from server`);
1820
+ if (this.debug === true) {
1821
+ console.log(`Loading ${fileUrl} from server`);
1822
+ }
1774
1823
  const ongoingLoad = this.ongoingLoads.get(fileUrl);
1775
1824
  if (ongoingLoad)
1776
- return ongoingLoad;
1825
+ return ongoingLoad.then((loadedModel) => {
1826
+ const blobURL = URL.createObjectURL(loadedModel.blob);
1827
+ return this.loadFromUrl(blobURL, fileType, loadedModel.originalExtension);
1828
+ });
1777
1829
  const loadPromise = fetch(fileUrl).then((response) => response.blob()).then((blob) => {
1778
1830
  const originalExtension = fileUrl.split(".").pop() || "";
1779
- this.modelCache.set(fileUrl, { blob, originalExtension });
1780
- const blobURL = URL.createObjectURL(blob);
1831
+ const cached = { blob, originalExtension };
1832
+ this.modelCache.set(fileUrl, cached);
1781
1833
  this.ongoingLoads.delete(fileUrl);
1782
- return this.loadFromUrl(blobURL, fileType, originalExtension);
1834
+ return cached;
1783
1835
  });
1784
1836
  this.ongoingLoads.set(fileUrl, loadPromise);
1785
- return loadPromise;
1837
+ return loadPromise.then((loadedModel) => {
1838
+ const blobURL = URL.createObjectURL(loadedModel.blob);
1839
+ return this.loadFromUrl(blobURL, fileType, loadedModel.originalExtension);
1840
+ });
1786
1841
  }
1787
1842
  }
1788
1843
  async loadFromUrl(url, fileType, extension) {
@@ -1813,9 +1868,6 @@ var _CharacterModelLoader = class _CharacterModelLoader {
1813
1868
  }
1814
1869
  }
1815
1870
  };
1816
- _CharacterModelLoader.instance = null;
1817
- var CharacterModelLoader = _CharacterModelLoader;
1818
- var MODEL_LOADER = CharacterModelLoader.getInstance();
1819
1871
 
1820
1872
  // src/input/KeyInputManager.ts
1821
1873
  var KeyInputManager = class {
@@ -1884,9 +1936,10 @@ var KeyInputManager = class {
1884
1936
  import {
1885
1937
  InteractionManager,
1886
1938
  MMLClickTrigger,
1887
- PromptManager
1939
+ PromptManager,
1940
+ LoadingProgressManager
1888
1941
  } from "mml-web";
1889
- import { Group as Group2 } from "three";
1942
+ import { Group as Group3 } from "three";
1890
1943
  var MMLCompositionScene = class {
1891
1944
  constructor(targetElement, renderer, scene, camera, audioListener, collisionsManager, getUserPositionAndRotation) {
1892
1945
  this.renderer = renderer;
@@ -1896,7 +1949,7 @@ var MMLCompositionScene = class {
1896
1949
  this.collisionsManager = collisionsManager;
1897
1950
  this.getUserPositionAndRotation = getUserPositionAndRotation;
1898
1951
  this.chatProbes = /* @__PURE__ */ new Set();
1899
- this.group = new Group2();
1952
+ this.group = new Group3();
1900
1953
  this.promptManager = PromptManager.init(targetElement);
1901
1954
  const { interactionListener, interactionManager } = InteractionManager.init(
1902
1955
  targetElement,
@@ -1905,6 +1958,7 @@ var MMLCompositionScene = class {
1905
1958
  );
1906
1959
  this.interactionManager = interactionManager;
1907
1960
  this.interactionListener = interactionListener;
1961
+ this.loadingProgressManager = new LoadingProgressManager();
1908
1962
  this.mmlScene = {
1909
1963
  getAudioListener: () => this.audioListener,
1910
1964
  getRenderer: () => this.renderer,
@@ -1940,6 +1994,9 @@ var MMLCompositionScene = class {
1940
1994
  },
1941
1995
  prompt: (promptProps, callback) => {
1942
1996
  this.promptManager.prompt(promptProps, callback);
1997
+ },
1998
+ getLoadingProgressManager: () => {
1999
+ return this.loadingProgressManager;
1943
2000
  }
1944
2001
  };
1945
2002
  this.clickTrigger = MMLClickTrigger.init(targetElement, this.mmlScene);
@@ -1963,7 +2020,7 @@ import { Pane } from "tweakpane";
1963
2020
  // src/tweakpane/blades/bcsFolder.ts
1964
2021
  var bcsValues = {
1965
2022
  brightness: 0,
1966
- contrast: 1.25,
2023
+ contrast: 1,
1967
2024
  saturation: 1
1968
2025
  };
1969
2026
  var bcsOptions = {
@@ -2010,22 +2067,22 @@ var BrightnessContrastSaturationFolder = class {
2010
2067
  // src/tweakpane/blades/environmentFolder.ts
2011
2068
  var sunValues = {
2012
2069
  sunPosition: {
2013
- sunAzimuthalAngle: 39,
2014
- sunPolarAngle: 50
2070
+ sunAzimuthalAngle: 215,
2071
+ sunPolarAngle: -39
2015
2072
  },
2016
- sunIntensity: 0.5,
2017
- sunColor: { r: 1, g: 0.74, b: 0.5 }
2073
+ sunIntensity: 1,
2074
+ sunColor: { r: 1, g: 1, b: 1 }
2018
2075
  };
2019
2076
  var sunOptions = {
2020
2077
  sunPosition: {
2021
2078
  sunAzimuthalAngle: { min: 0, max: 360, step: 1 },
2022
2079
  sunPolarAngle: { min: -90, max: 90, step: 1 }
2023
2080
  },
2024
- sunIntensity: { min: 0, max: 1, step: 0.05 }
2081
+ sunIntensity: { min: 0, max: 3, step: 0.05 }
2025
2082
  };
2026
2083
  var envValues = {
2027
2084
  ambientLight: {
2028
- ambientLightIntensity: 0,
2085
+ ambientLightIntensity: 0.05,
2029
2086
  ambientLightColor: { r: 1, g: 1, b: 1 }
2030
2087
  },
2031
2088
  fog: {
@@ -2384,7 +2441,7 @@ var n8ssaoOptions = {
2384
2441
  aoSamples: [2, 4, 8, 16, 32, 64],
2385
2442
  denoiseSamples: [2, 4, 8, 16, 32, 64],
2386
2443
  denoiseRadius: [3, 6, 12],
2387
- viewMode: ["Combined", "AO", "Split", "No AO"]
2444
+ viewMode: ["Combined", "AO", "No AO", "Split", "Split AO", "No AO"]
2388
2445
  };
2389
2446
  var ssaoMaterialParams = [
2390
2447
  "fade",
@@ -2480,10 +2537,10 @@ var SSAOFolder = class {
2480
2537
  this.aoDisplay = this.n8ssao.addBinding(n8ssaoValues, "viewMode", {
2481
2538
  view: "radiogrid",
2482
2539
  groupName: "viewMode",
2483
- size: [2, 2],
2540
+ size: [3, 2],
2484
2541
  cells: (x, y) => ({
2485
- title: `${n8ssaoOptions.viewMode[y * 2 + x]}`,
2486
- value: `${n8ssaoOptions.viewMode[y * 2 + x]}`
2542
+ title: `${n8ssaoOptions.viewMode[y * 3 + x]}`,
2543
+ value: `${n8ssaoOptions.viewMode[y * 3 + x]}`
2487
2544
  }),
2488
2545
  label: "viewMode"
2489
2546
  });
@@ -2855,7 +2912,6 @@ var TweakPane = class {
2855
2912
  };
2856
2913
 
2857
2914
  // src/rendering/composer.ts
2858
- import { N8AOPostPass } from "n8ao";
2859
2915
  import {
2860
2916
  EffectComposer as EffectComposer2,
2861
2917
  RenderPass,
@@ -2875,27 +2931,28 @@ import {
2875
2931
  } from "postprocessing";
2876
2932
  import {
2877
2933
  AmbientLight,
2878
- Color as Color6,
2879
- Fog,
2880
- HalfFloatType,
2934
+ Color as Color7,
2935
+ Fog as Fog2,
2936
+ HalfFloatType as HalfFloatType2,
2881
2937
  LinearSRGBColorSpace,
2882
2938
  LoadingManager as LoadingManager2,
2883
2939
  PMREMGenerator,
2884
- Scene as Scene3,
2885
- Vector2 as Vector22,
2886
- WebGLRenderer as WebGLRenderer2
2940
+ SRGBColorSpace,
2941
+ Scene as Scene4,
2942
+ Vector2 as Vector27,
2943
+ WebGLRenderer as WebGLRenderer4
2887
2944
  } from "three";
2888
2945
  import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js";
2889
2946
 
2890
2947
  // src/sun/Sun.ts
2891
- import { CameraHelper, Color as Color5, DirectionalLight, Group as Group3, OrthographicCamera, Vector3 as Vector38 } from "three";
2892
- var Sun = class extends Group3 {
2948
+ import { CameraHelper, Color as Color5, DirectionalLight, Group as Group4, OrthographicCamera, Vector3 as Vector39 } from "three";
2949
+ var Sun = class extends Group4 {
2893
2950
  constructor() {
2894
2951
  super();
2895
2952
  this.debug = false;
2896
- this.sunOffset = new Vector38(
2897
- 39 * (Math.PI / 180),
2898
- 50 * (Math.PI / 180),
2953
+ this.sunOffset = new Vector39(
2954
+ sunValues.sunPosition.sunAzimuthalAngle * (Math.PI / 180),
2955
+ sunValues.sunPosition.sunPolarAngle * (Math.PI / 180),
2899
2956
  100
2900
2957
  );
2901
2958
  this.shadowResolution = 8192;
@@ -2914,13 +2971,14 @@ var Sun = class extends Group3 {
2914
2971
  this.camHelper = new CameraHelper(this.shadowCamera);
2915
2972
  }
2916
2973
  this.directionalLight = new DirectionalLight(16777215, 0.5);
2974
+ this.directionalLight.intensity = sunValues.sunIntensity;
2917
2975
  this.directionalLight.shadow.normalBias = 0.05;
2918
2976
  this.directionalLight.shadow.radius = 1.5;
2919
2977
  this.directionalLight.shadow.camera = this.shadowCamera;
2920
2978
  this.directionalLight.shadow.mapSize.set(this.shadowResolution, this.shadowResolution);
2921
2979
  this.directionalLight.castShadow = true;
2922
2980
  this.setColor();
2923
- this.updateCharacterPosition(new Vector38(0, 0, 0));
2981
+ this.updateCharacterPosition(new Vector39(0, 0, 0));
2924
2982
  this.add(this.directionalLight);
2925
2983
  if (this.debug === true && this.camHelper instanceof CameraHelper) {
2926
2984
  this.add(this.camHelper);
@@ -2958,7 +3016,7 @@ var Sun = class extends Group3 {
2958
3016
  if (!this.target)
2959
3017
  return;
2960
3018
  const distance = this.sunOffset.z;
2961
- const sphericalPosition = new Vector38(
3019
+ const sphericalPosition = new Vector39(
2962
3020
  distance * Math.sin(polarAngle) * Math.cos(azimuthalAngle),
2963
3021
  distance * Math.cos(polarAngle),
2964
3022
  distance * Math.sin(polarAngle) * Math.sin(azimuthalAngle)
@@ -3114,27 +3172,1337 @@ var GaussGrainEffect = new ShaderMaterial2({
3114
3172
  dithering: true
3115
3173
  });
3116
3174
 
3175
+ // src/rendering/post-effects/n8-ssao/N8SSAOPass.ts
3176
+ import { Pass } from "postprocessing";
3177
+ import {
3178
+ Color as Color6,
3179
+ DataTexture,
3180
+ FloatType,
3181
+ Fog,
3182
+ FogExp2,
3183
+ HalfFloatType,
3184
+ LinearFilter as LinearFilter3,
3185
+ NearestFilter,
3186
+ NoColorSpace,
3187
+ OrthographicCamera as OrthographicCamera3,
3188
+ RGBAFormat as RGBAFormat2,
3189
+ RedFormat,
3190
+ RepeatWrapping,
3191
+ ShaderMaterial as ShaderMaterial4,
3192
+ Uniform as Uniform7,
3193
+ Vector2 as Vector26,
3194
+ Vector3 as Vector313,
3195
+ WebGLMultipleRenderTargets,
3196
+ WebGLRenderTarget
3197
+ } from "three";
3198
+
3199
+ // src/rendering/post-effects/n8-ssao/BlueNoise.ts
3200
+ var BlueNoise = "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";
3201
+
3202
+ // src/rendering/post-effects/n8-ssao/DepthDownSample.ts
3203
+ import { Matrix4 as Matrix42, Uniform as Uniform3, Vector2 as Vector22 } from "three";
3204
+ var DepthDownSample = {
3205
+ uniforms: {
3206
+ sceneDepth: new Uniform3(null),
3207
+ resolution: new Uniform3(new Vector22()),
3208
+ near: new Uniform3(0.1),
3209
+ far: new Uniform3(1e3),
3210
+ viewMatrixInv: new Uniform3(new Matrix42()),
3211
+ projectionMatrixInv: new Uniform3(new Matrix42()),
3212
+ logDepth: new Uniform3(false)
3213
+ },
3214
+ depthWrite: false,
3215
+ depthTest: false,
3216
+ vertexShader: (
3217
+ /* glsl */
3218
+ `
3219
+ varying vec2 vUv;
3220
+ void main(void) {
3221
+ vUv = uv;
3222
+ gl_Position = vec4(position, 1);
3223
+ }`
3224
+ ),
3225
+ fragmentShader: (
3226
+ /* glsl */
3227
+ `
3228
+ uniform highp sampler2D sceneDepth;
3229
+ uniform vec2 resolution;
3230
+ uniform float near;
3231
+ uniform float far;
3232
+ uniform bool logDepth;
3233
+ uniform mat4 viewMatrixInv;
3234
+ uniform mat4 projectionMatrixInv;
3235
+ varying vec2 vUv;
3236
+
3237
+ layout(location = 1) out vec4 gNormal;
3238
+
3239
+ vec3 getWorldPosLog(vec3 posS) {
3240
+ vec2 uv = posS.xy;
3241
+ float z = posS.z;
3242
+ float nearZ = near;
3243
+ float farZ = far;
3244
+ float depth = pow(2.0, z * log2(farZ + 1.0)) - 1.0;
3245
+ float a = farZ / (farZ - nearZ);
3246
+ float b = farZ * nearZ / (nearZ - farZ);
3247
+ float linDepth = a + b / depth;
3248
+ vec4 clipVec = vec4(uv, linDepth, 1.0) * 2.0 - 1.0;
3249
+ vec4 wpos = projectionMatrixInv * clipVec;
3250
+ return wpos.xyz / wpos.w;
3251
+ }
3252
+
3253
+ vec3 getWorldPos(float depth, vec2 coord) {
3254
+ if (logDepth) {
3255
+ return getWorldPosLog(vec3(coord, depth));
3256
+ }
3257
+ float z = depth * 2.0 - 1.0;
3258
+ vec4 clipSpacePosition = vec4(coord * 2.0 - 1.0, z, 1.0);
3259
+ vec4 viewSpacePosition = projectionMatrixInv * clipSpacePosition;
3260
+
3261
+ vec4 worldSpacePosition = viewSpacePosition;
3262
+ worldSpacePosition.xyz /= worldSpacePosition.w;
3263
+
3264
+ return worldSpacePosition.xyz;
3265
+ }
3266
+
3267
+ vec3 computeNormal(vec3 worldPos, vec2 vUv) {
3268
+ ivec2 p = ivec2(vUv * resolution);
3269
+ float c0 = texelFetch(sceneDepth, p, 0).x;
3270
+ float l2 = texelFetch(sceneDepth, p - ivec2(2, 0), 0).x;
3271
+ float l1 = texelFetch(sceneDepth, p - ivec2(1, 0), 0).x;
3272
+ float r1 = texelFetch(sceneDepth, p + ivec2(1, 0), 0).x;
3273
+ float r2 = texelFetch(sceneDepth, p + ivec2(2, 0), 0).x;
3274
+ float b2 = texelFetch(sceneDepth, p - ivec2(0, 2), 0).x;
3275
+ float b1 = texelFetch(sceneDepth, p - ivec2(0, 1), 0).x;
3276
+ float t1 = texelFetch(sceneDepth, p + ivec2(0, 1), 0).x;
3277
+ float t2 = texelFetch(sceneDepth, p + ivec2(0, 2), 0).x;
3278
+ float dl = abs((2.0 * l1 - l2) - c0);
3279
+ float dr = abs((2.0 * r1 - r2) - c0);
3280
+ float db = abs((2.0 * b1 - b2) - c0);
3281
+ float dt = abs((2.0 * t1 - t2) - c0);
3282
+ vec3 ce = getWorldPos(c0, vUv).xyz;
3283
+ vec3 dpdx = (
3284
+ (dl < dr)
3285
+ ? ce - getWorldPos(l1, (vUv - vec2(1.0 / resolution.x, 0.0))).xyz
3286
+ : -ce + getWorldPos(r1, (vUv + vec2(1.0 / resolution.x, 0.0))).xyz
3287
+ );
3288
+ vec3 dpdy = (
3289
+ (db < dt)
3290
+ ? ce - getWorldPos(b1, (vUv - vec2(0.0, 1.0 / resolution.y))).xyz
3291
+ : -ce + getWorldPos(t1, (vUv + vec2(0.0, 1.0 / resolution.y))).xyz
3292
+ );
3293
+ return normalize(cross(dpdx, dpdy));
3294
+ }
3295
+
3296
+ void main(void) {
3297
+ vec2 uv = vUv - vec2(0.5) / resolution;
3298
+ vec2 pixelSize = vec2(1.0) / resolution;
3299
+ vec2[] uvSamples = vec2[4](
3300
+ uv,
3301
+ uv + vec2(pixelSize.x, 0.0),
3302
+ uv + vec2(0.0, pixelSize.y),
3303
+ uv + pixelSize
3304
+ );
3305
+ float depth00 = texture2D(sceneDepth, uvSamples[0]).r;
3306
+ float depth10 = texture2D(sceneDepth, uvSamples[1]).r;
3307
+ float depth01 = texture2D(sceneDepth, uvSamples[2]).r;
3308
+ float depth11 = texture2D(sceneDepth, uvSamples[3]).r;
3309
+ float minDepth = min(min(depth00, depth10), min(depth01, depth11));
3310
+ float maxDepth = max(max(depth00, depth10), max(depth01, depth11));
3311
+ float targetDepth = minDepth;
3312
+
3313
+ if (mod(gl_FragCoord.x + gl_FragCoord.y, 2.0) > 0.5) {
3314
+ targetDepth = maxDepth;
3315
+ }
3316
+
3317
+ int chosenIndex = 0;
3318
+ float[] samples = float[4](depth00, depth10, depth01, depth11);
3319
+
3320
+ for(int i = 0; i < 4; ++i) {
3321
+ if (samples[i] == targetDepth) {
3322
+ chosenIndex = i;
3323
+ break;
3324
+ }
3325
+ }
3326
+
3327
+ gl_FragColor = vec4(samples[chosenIndex], 0.0, 0.0, 1.0);
3328
+ gNormal = vec4(
3329
+ computeNormal(
3330
+ getWorldPos(samples[chosenIndex], uvSamples[chosenIndex]), uvSamples[chosenIndex]
3331
+ ),
3332
+ 0.0
3333
+ );
3334
+ }`
3335
+ )
3336
+ };
3337
+
3338
+ // src/rendering/post-effects/n8-ssao/EffectCompositer.ts
3339
+ import { Matrix4 as Matrix43, Uniform as Uniform4, Vector2 as Vector23, Vector3 as Vector310 } from "three";
3340
+ var EffectCompositer = {
3341
+ uniforms: {
3342
+ sceneDiffuse: new Uniform4(null),
3343
+ sceneDepth: new Uniform4(null),
3344
+ tDiffuse: new Uniform4(null),
3345
+ projMat: new Uniform4(new Matrix43()),
3346
+ viewMat: new Uniform4(new Matrix43()),
3347
+ projectionMatrixInv: new Uniform4(new Matrix43()),
3348
+ viewMatrixInv: new Uniform4(new Matrix43()),
3349
+ cameraPos: new Uniform4(new Vector310()),
3350
+ resolution: new Uniform4(new Vector23()),
3351
+ color: new Uniform4(new Vector310()),
3352
+ blueNoise: new Uniform4(null),
3353
+ downsampledDepth: new Uniform4(null),
3354
+ time: new Uniform4(0),
3355
+ intensity: new Uniform4(10),
3356
+ renderMode: new Uniform4(0),
3357
+ gammaCorrection: new Uniform4(false),
3358
+ logDepth: new Uniform4(false),
3359
+ ortho: new Uniform4(false),
3360
+ near: new Uniform4(0.1),
3361
+ far: new Uniform4(1e3),
3362
+ screenSpaceRadius: new Uniform4(false),
3363
+ radius: new Uniform4(0),
3364
+ distanceFalloff: new Uniform4(1),
3365
+ fog: new Uniform4(false),
3366
+ fogExp: new Uniform4(false),
3367
+ fogDensity: new Uniform4(0),
3368
+ fogNear: new Uniform4(Infinity),
3369
+ fogFar: new Uniform4(Infinity),
3370
+ colorMultiply: new Uniform4(true)
3371
+ },
3372
+ depthWrite: false,
3373
+ depthTest: false,
3374
+ vertexShader: (
3375
+ /* glsl */
3376
+ `
3377
+ varying vec2 vUv;
3378
+ void main(void) {
3379
+ vUv = uv;
3380
+ gl_Position = vec4(position, 1);
3381
+ }`
3382
+ ),
3383
+ fragmentShader: (
3384
+ /* glsl */
3385
+ `
3386
+ uniform sampler2D sceneDiffuse;
3387
+ uniform highp sampler2D sceneDepth;
3388
+ uniform highp sampler2D downsampledDepth;
3389
+ uniform sampler2D tDiffuse;
3390
+ uniform sampler2D blueNoise;
3391
+ uniform vec2 resolution;
3392
+ uniform vec3 color;
3393
+ uniform mat4 projectionMatrixInv;
3394
+ uniform mat4 viewMatrixInv;
3395
+ uniform float intensity;
3396
+ uniform float renderMode;
3397
+ uniform float near;
3398
+ uniform float far;
3399
+ uniform bool gammaCorrection;
3400
+ uniform bool logDepth;
3401
+ uniform bool ortho;
3402
+ uniform bool screenSpaceRadius;
3403
+ uniform bool fog;
3404
+ uniform bool fogExp;
3405
+ uniform bool colorMultiply;
3406
+ uniform float fogDensity;
3407
+ uniform float fogNear;
3408
+ uniform float fogFar;
3409
+ uniform float radius;
3410
+ uniform float distanceFalloff;
3411
+ uniform vec3 cameraPos;
3412
+ varying vec2 vUv;
3413
+
3414
+ highp float linearize_depth(highp float d, highp float zNear,highp float zFar) {
3415
+ return (zFar * zNear) / (zFar - d * (zFar - zNear));
3416
+ }
3417
+
3418
+ highp float linearize_depth_ortho(highp float d, highp float nearZ, highp float farZ) {
3419
+ return nearZ + (farZ - nearZ) * d;
3420
+ }
3421
+
3422
+ highp float linearize_depth_log(highp float d, highp float nearZ,highp float farZ) {
3423
+ float depth = pow(2.0, d * log2(farZ + 1.0)) - 1.0;
3424
+ float a = farZ / (farZ - nearZ);
3425
+ float b = farZ * nearZ / (nearZ - farZ);
3426
+ float linDepth = a + b / depth;
3427
+ return (
3428
+ ortho
3429
+ ? linearize_depth_ortho(linDepth, nearZ, farZ)
3430
+ : linearize_depth(linDepth, nearZ, farZ)
3431
+ );
3432
+ }
3433
+
3434
+ vec3 getWorldPosLog(vec3 posS) {
3435
+ vec2 uv = posS.xy;
3436
+ float z = posS.z;
3437
+ float nearZ =near;
3438
+ float farZ = far;
3439
+ float depth = pow(2.0, z * log2(farZ + 1.0)) - 1.0;
3440
+ float a = farZ / (farZ - nearZ);
3441
+ float b = farZ * nearZ / (nearZ - farZ);
3442
+ float linDepth = a + b / depth;
3443
+ vec4 clipVec = vec4(uv, linDepth, 1.0) * 2.0 - 1.0;
3444
+ vec4 wpos = projectionMatrixInv * clipVec;
3445
+ return wpos.xyz / wpos.w;
3446
+ }
3447
+
3448
+ vec3 getWorldPos(float depth, vec2 coord) {
3449
+ #ifdef LOGDEPTH
3450
+ return getWorldPosLog(vec3(coord, depth));
3451
+ #endif
3452
+
3453
+ float z = depth * 2.0 - 1.0;
3454
+ vec4 clipSpacePosition = vec4(coord * 2.0 - 1.0, z, 1.0);
3455
+ vec4 viewSpacePosition = projectionMatrixInv * clipSpacePosition;
3456
+
3457
+ vec4 worldSpacePosition = viewSpacePosition;
3458
+ worldSpacePosition.xyz /= worldSpacePosition.w;
3459
+ return worldSpacePosition.xyz;
3460
+ }
3461
+
3462
+ vec3 computeNormal(vec3 worldPos, vec2 vUv) {
3463
+ ivec2 p = ivec2(vUv * resolution);
3464
+ float c0 = texelFetch(sceneDepth, p, 0).x;
3465
+ float l2 = texelFetch(sceneDepth, p - ivec2(2, 0), 0).x;
3466
+ float l1 = texelFetch(sceneDepth, p - ivec2(1, 0), 0).x;
3467
+ float r1 = texelFetch(sceneDepth, p + ivec2(1, 0), 0).x;
3468
+ float r2 = texelFetch(sceneDepth, p + ivec2(2, 0), 0).x;
3469
+ float b2 = texelFetch(sceneDepth, p - ivec2(0, 2), 0).x;
3470
+ float b1 = texelFetch(sceneDepth, p - ivec2(0, 1), 0).x;
3471
+ float t1 = texelFetch(sceneDepth, p + ivec2(0, 1), 0).x;
3472
+ float t2 = texelFetch(sceneDepth, p + ivec2(0, 2), 0).x;
3473
+ float dl = abs((2.0 * l1 - l2) - c0);
3474
+ float dr = abs((2.0 * r1 - r2) - c0);
3475
+ float db = abs((2.0 * b1 - b2) - c0);
3476
+ float dt = abs((2.0 * t1 - t2) - c0);
3477
+ vec3 ce = getWorldPos(c0, vUv).xyz;
3478
+ vec3 dpdx = (
3479
+ (dl < dr)
3480
+ ? ce - getWorldPos(l1, (vUv - vec2(1.0 / resolution.x, 0.0))).xyz
3481
+ : -ce + getWorldPos(r1, (vUv + vec2(1.0 / resolution.x, 0.0))).xyz
3482
+ );
3483
+ vec3 dpdy = (
3484
+ (db < dt)
3485
+ ? ce - getWorldPos(b1, (vUv - vec2(0.0, 1.0 / resolution.y))).xyz
3486
+ : -ce + getWorldPos(t1, (vUv + vec2(0.0, 1.0 / resolution.y))).xyz
3487
+ );
3488
+ return normalize(cross(dpdx, dpdy));
3489
+ }
3490
+
3491
+ #include <common>
3492
+ #include <dithering_pars_fragment>
3493
+
3494
+ void main(void) {
3495
+ vec4 sceneTexel = texture2D(sceneDiffuse, vUv);
3496
+ float depth = texture2D(sceneDepth, vUv).x;
3497
+
3498
+ #ifdef HALFRES
3499
+ vec4 texel;
3500
+ if (depth == 1.0) {
3501
+ texel = vec4(0.0, 0.0, 0.0, 1.0);
3502
+ } else {
3503
+ vec3 worldPos = getWorldPos(depth, vUv);
3504
+ vec3 normal = computeNormal(getWorldPos(depth, vUv), vUv);
3505
+
3506
+ float totalWeight = 0.0;
3507
+ float radiusToUse = (
3508
+ screenSpaceRadius
3509
+ ? distance(worldPos, getWorldPos(depth, vUv + vec2(radius, 0.0) / resolution))
3510
+ : radius
3511
+ );
3512
+ float distanceFalloffToUse = (
3513
+ screenSpaceRadius
3514
+ ? radiusToUse * distanceFalloff
3515
+ : distanceFalloff
3516
+ );
3517
+ for(float x = -1.0; x <= 1.0; x++) {
3518
+ for(float y = -1.0; y <= 1.0; y++) {
3519
+ vec2 offset = vec2(x, y);
3520
+ ivec2 p = ivec2((vUv * resolution * 0.5) + offset);
3521
+ vec2 pUv = vec2(p) / (resolution * 0.5);
3522
+ float sampleDepth = texelFetch(downsampledDepth,p, 0).x;
3523
+ vec4 sampleInfo = texelFetch(tDiffuse, p, 0);
3524
+ vec3 normalSample = sampleInfo.xyz * 2.0 - 1.0;
3525
+ vec3 worldPosSample = getWorldPos(sampleDepth, pUv);
3526
+ float tangentPlaneDist = abs(dot(worldPos - worldPosSample, normal));
3527
+ float rangeCheck = exp(
3528
+ -1.0 * tangentPlaneDist * (1.0 / distanceFalloffToUse)) * max(dot(normal, normalSample),
3529
+ 0.0
3530
+ );
3531
+ float weight = rangeCheck;
3532
+ totalWeight += weight;
3533
+ texel += sampleInfo * weight;
3534
+ }
3535
+ }
3536
+ if (totalWeight == 0.0) {
3537
+ texel = texture2D(tDiffuse, vUv);
3538
+ } else {
3539
+ texel /= totalWeight;
3540
+ }
3541
+ }
3542
+ #else
3543
+ vec4 texel = texture2D(tDiffuse, vUv);
3544
+ #endif
3545
+
3546
+ #ifdef LOGDEPTH
3547
+ texel.a = clamp(texel.a, 0.0, 1.0);
3548
+ if (texel.a == 0.0) {
3549
+ texel.a = 1.0;
3550
+ }
3551
+ #endif
3552
+
3553
+ float finalAo = pow(texel.a, intensity);
3554
+ float fogFactor;
3555
+ float fogDepth = distance(cameraPos, getWorldPos(depth, vUv));
3556
+
3557
+ if (fog) {
3558
+ if (fogExp) {
3559
+ fogFactor = 1.0 - exp(-fogDensity * fogDensity * fogDepth * fogDepth);
3560
+ } else {
3561
+ fogFactor = smoothstep(fogNear, fogFar, fogDepth);
3562
+ }
3563
+ }
3564
+
3565
+ finalAo = mix(finalAo, 1.0, fogFactor);
3566
+ vec3 aoApplied = color * mix(vec3(1.0), sceneTexel.rgb, float(colorMultiply));
3567
+ if (renderMode == 0.0) {
3568
+ gl_FragColor = vec4( mix(sceneTexel.rgb, aoApplied, 1.0 - finalAo), sceneTexel.a);
3569
+ } else if (renderMode == 1.0) {
3570
+ gl_FragColor = vec4( mix(vec3(1.0), aoApplied, 1.0 - finalAo), sceneTexel.a);
3571
+ } else if (renderMode == 2.0) {
3572
+ gl_FragColor = vec4( sceneTexel.rgb, sceneTexel.a);
3573
+ } else if (renderMode == 3.0) {
3574
+ if (vUv.x < 0.5) {
3575
+ gl_FragColor = vec4( sceneTexel.rgb, sceneTexel.a);
3576
+ } else if (abs(vUv.x - 0.5) < 1.0 / resolution.x) {
3577
+ gl_FragColor = vec4(1.0);
3578
+ } else {
3579
+ gl_FragColor = vec4(mix(sceneTexel.rgb, aoApplied, 1.0 - finalAo), sceneTexel.a);
3580
+ }
3581
+ } else if (renderMode == 4.0) {
3582
+ if (vUv.x < 0.5) {
3583
+ gl_FragColor = vec4( sceneTexel.rgb, sceneTexel.a);
3584
+ } else if (abs(vUv.x - 0.5) < 1.0 / resolution.x) {
3585
+ gl_FragColor = vec4(1.0);
3586
+ } else {
3587
+ gl_FragColor = vec4(mix(vec3(1.0), aoApplied, 1.0 - finalAo), sceneTexel.a);
3588
+ }
3589
+ }
3590
+
3591
+ #include <dithering_fragment>
3592
+
3593
+ if (gammaCorrection) {
3594
+ gl_FragColor = LinearTosRGB(gl_FragColor);
3595
+ }
3596
+ }
3597
+ `
3598
+ )
3599
+ };
3600
+
3601
+ // src/rendering/post-effects/n8-ssao/EffectShader.ts
3602
+ import { Matrix4 as Matrix44, Uniform as Uniform5, Vector2 as Vector24, Vector3 as Vector311 } from "three";
3603
+ var EffectShader = {
3604
+ uniforms: {
3605
+ sceneDiffuse: new Uniform5(null),
3606
+ sceneDepth: new Uniform5(null),
3607
+ sceneNormal: new Uniform5(null),
3608
+ projMat: new Uniform5(new Matrix44()),
3609
+ viewMat: new Uniform5(new Matrix44()),
3610
+ projViewMat: new Uniform5(new Matrix44()),
3611
+ projectionMatrixInv: new Uniform5(new Matrix44()),
3612
+ viewMatrixInv: new Uniform5(new Matrix44()),
3613
+ cameraPos: new Uniform5(new Vector311()),
3614
+ resolution: new Uniform5(new Vector24()),
3615
+ time: new Uniform5(0),
3616
+ samples: new Uniform5([]),
3617
+ samplesR: new Uniform5([]),
3618
+ bluenoise: new Uniform5(null),
3619
+ distanceFalloff: new Uniform5(1),
3620
+ radius: new Uniform5(5),
3621
+ near: new Uniform5(0.1),
3622
+ far: new Uniform5(1e3),
3623
+ logDepth: new Uniform5(false),
3624
+ ortho: new Uniform5(false),
3625
+ screenSpaceRadius: new Uniform5(false)
3626
+ },
3627
+ depthWrite: false,
3628
+ depthTest: false,
3629
+ vertexShader: (
3630
+ /* glsl */
3631
+ `
3632
+ varying vec2 vUv;
3633
+ void main(void) {
3634
+ vUv = uv;
3635
+ gl_Position = vec4(position, 1);
3636
+ }
3637
+ `
3638
+ ),
3639
+ fragmentShader: (
3640
+ /* glsl */
3641
+ `
3642
+ #define SAMPLES 16
3643
+ #define FSAMPLES 16.0
3644
+ uniform sampler2D sceneDiffuse;
3645
+ uniform highp sampler2D sceneNormal;
3646
+ uniform highp sampler2D sceneDepth;
3647
+ uniform mat4 projectionMatrixInv;
3648
+ uniform mat4 viewMatrixInv;
3649
+ uniform mat4 projMat;
3650
+ uniform mat4 viewMat;
3651
+ uniform mat4 projViewMat;
3652
+ uniform vec3 cameraPos;
3653
+ uniform vec2 resolution;
3654
+ uniform float time;
3655
+ uniform vec3[SAMPLES] samples;
3656
+ uniform float[SAMPLES] samplesR;
3657
+ uniform float radius;
3658
+ uniform float distanceFalloff;
3659
+ uniform float near;
3660
+ uniform float far;
3661
+ uniform bool logDepth;
3662
+ uniform bool ortho;
3663
+ uniform bool screenSpaceRadius;
3664
+ uniform sampler2D bluenoise;
3665
+ varying vec2 vUv;
3666
+
3667
+ highp float linearize_depth(highp float d, highp float zNear,highp float zFar) {
3668
+ return (zFar * zNear) / (zFar - d * (zFar - zNear));
3669
+ }
3670
+
3671
+ highp float linearize_depth_ortho(highp float d, highp float nearZ, highp float farZ) {
3672
+ return nearZ + (farZ - nearZ) * d;
3673
+ }
3674
+
3675
+ highp float linearize_depth_log(highp float d, highp float nearZ,highp float farZ) {
3676
+ float depth = pow(2.0, d * log2(farZ + 1.0)) - 1.0;
3677
+ float a = farZ / (farZ - nearZ);
3678
+ float b = farZ * nearZ / (nearZ - farZ);
3679
+ float linDepth = a + b / depth;
3680
+ return (
3681
+ ortho
3682
+ ? linearize_depth_ortho(linDepth, nearZ, farZ)
3683
+ : linearize_depth(linDepth, nearZ, farZ)
3684
+ );
3685
+ }
3686
+
3687
+ vec3 getWorldPosLog(vec3 posS) {
3688
+ vec2 uv = posS.xy;
3689
+ float z = posS.z;
3690
+ float nearZ =near;
3691
+ float farZ = far;
3692
+ float depth = pow(2.0, z * log2(farZ + 1.0)) - 1.0;
3693
+ float a = farZ / (farZ - nearZ);
3694
+ float b = farZ * nearZ / (nearZ - farZ);
3695
+ float linDepth = a + b / depth;
3696
+ vec4 clipVec = vec4(uv, linDepth, 1.0) * 2.0 - 1.0;
3697
+ vec4 wpos = projectionMatrixInv * clipVec;
3698
+ return wpos.xyz / wpos.w;
3699
+ }
3700
+
3701
+ vec3 getWorldPos(float depth, vec2 coord) {
3702
+ #ifdef LOGDEPTH
3703
+ return getWorldPosLog(vec3(coord, depth));
3704
+ #endif
3705
+ float z = depth * 2.0 - 1.0;
3706
+ vec4 clipSpacePosition = vec4(coord * 2.0 - 1.0, z, 1.0);
3707
+ vec4 viewSpacePosition = projectionMatrixInv * clipSpacePosition;
3708
+
3709
+ vec4 worldSpacePosition = viewSpacePosition;
3710
+ worldSpacePosition.xyz /= worldSpacePosition.w;
3711
+ return worldSpacePosition.xyz;
3712
+ }
3713
+
3714
+ vec3 computeNormal(vec3 worldPos, vec2 vUv) {
3715
+ ivec2 p = ivec2(vUv * resolution);
3716
+ float c0 = texelFetch(sceneDepth, p, 0).x;
3717
+ float l2 = texelFetch(sceneDepth, p - ivec2(2, 0), 0).x;
3718
+ float l1 = texelFetch(sceneDepth, p - ivec2(1, 0), 0).x;
3719
+ float r1 = texelFetch(sceneDepth, p + ivec2(1, 0), 0).x;
3720
+ float r2 = texelFetch(sceneDepth, p + ivec2(2, 0), 0).x;
3721
+ float b2 = texelFetch(sceneDepth, p - ivec2(0, 2), 0).x;
3722
+ float b1 = texelFetch(sceneDepth, p - ivec2(0, 1), 0).x;
3723
+ float t1 = texelFetch(sceneDepth, p + ivec2(0, 1), 0).x;
3724
+ float t2 = texelFetch(sceneDepth, p + ivec2(0, 2), 0).x;
3725
+ float dl = abs((2.0 * l1 - l2) - c0);
3726
+ float dr = abs((2.0 * r1 - r2) - c0);
3727
+ float db = abs((2.0 * b1 - b2) - c0);
3728
+ float dt = abs((2.0 * t1 - t2) - c0);
3729
+ vec3 ce = getWorldPos(c0, vUv).xyz;
3730
+ vec3 dpdx = (
3731
+ (dl < dr)
3732
+ ? ce - getWorldPos(l1, (vUv - vec2(1.0 / resolution.x, 0.0))).xyz
3733
+ : -ce + getWorldPos(r1, (vUv + vec2(1.0 / resolution.x, 0.0))).xyz
3734
+ );
3735
+ vec3 dpdy = (
3736
+ (db < dt)
3737
+ ? ce - getWorldPos(b1, (vUv - vec2(0.0, 1.0 / resolution.y))).xyz
3738
+ : -ce + getWorldPos(t1, (vUv + vec2(0.0, 1.0 / resolution.y))).xyz
3739
+ );
3740
+ return normalize(cross(dpdx, dpdy));
3741
+ }
3742
+
3743
+ void main(void) {
3744
+ vec4 diffuse = texture2D(sceneDiffuse, vUv);
3745
+ float depth = texture2D(sceneDepth, vUv).x;
3746
+
3747
+ if (depth == 1.0) {
3748
+ gl_FragColor = vec4(vec3(1.0), 1.0);
3749
+ return;
3750
+ }
3751
+
3752
+ vec3 worldPos = getWorldPos(depth, vUv);
3753
+
3754
+ #ifdef HALFRES
3755
+ vec3 normal = texture2D(sceneNormal, vUv).rgb;
3756
+ #else
3757
+ vec3 normal = computeNormal(worldPos, vUv);
3758
+ #endif
3759
+
3760
+ vec4 noise = texture2D(bluenoise, gl_FragCoord.xy / 128.0);
3761
+ vec3 randomVec = normalize(noise.rgb * 2.0 - 1.0);
3762
+ vec3 tangent = normalize(randomVec - normal * dot(randomVec, normal));
3763
+ vec3 bitangent = cross(normal, tangent);
3764
+ mat3 tbn = mat3(tangent, bitangent, normal);
3765
+ float occluded = 0.0;
3766
+ float totalWeight = 0.0;
3767
+
3768
+ float radiusToUse = (
3769
+ screenSpaceRadius
3770
+ ? distance(worldPos, getWorldPos(depth, vUv + vec2(radius, 0.0) / resolution))
3771
+ : radius
3772
+ );
3773
+ float distanceFalloffToUse = (
3774
+ screenSpaceRadius
3775
+ ? radiusToUse * distanceFalloff
3776
+ : distanceFalloff
3777
+ );
3778
+ float bias = (0.1 / near) * fwidth(distance(worldPos, cameraPos)) / radiusToUse;
3779
+ for(float i = 0.0; i < FSAMPLES; i++) {
3780
+ vec3 sampleDirection = tbn * samples[int(i)];
3781
+ float moveAmt = samplesR[int(mod(i + noise.a * FSAMPLES, FSAMPLES))];
3782
+ vec3 samplePos = worldPos + radiusToUse * moveAmt * sampleDirection;
3783
+ vec4 offset = projMat * vec4(samplePos, 1.0);
3784
+ offset.xyz /= offset.w;
3785
+ offset.xyz = offset.xyz * 0.5 + 0.5;
3786
+ float sampleDepth = textureLod(sceneDepth, offset.xy, 0.0).x;
3787
+ #ifdef LOGDEPTH
3788
+ float distSample = linearize_depth_log(sampleDepth, near, far);
3789
+ #else
3790
+ float distSample = (
3791
+ ortho
3792
+ ? linearize_depth_ortho(sampleDepth, near, far)
3793
+ : linearize_depth(sampleDepth, near, far)
3794
+ );
3795
+ #endif
3796
+ float distWorld = (
3797
+ ortho
3798
+ ? linearize_depth_ortho(offset.z, near, far)
3799
+ : linearize_depth(offset.z, near, far)
3800
+ );
3801
+ float rangeCheck = smoothstep(0.0, 1.0, distanceFalloffToUse / (abs(distSample - distWorld)));
3802
+ vec2 diff = gl_FragCoord.xy - ( offset.xy * resolution);
3803
+ float weight = dot(sampleDirection, normal);
3804
+ occluded += rangeCheck * weight *
3805
+ (distSample + bias < distWorld ? 1.0 : 0.0) *
3806
+ (
3807
+ (
3808
+ dot(diff, diff) < 1.0 ||
3809
+ (sampleDepth == depth) ||
3810
+ (offset.x < 0.0 || offset.x > 1.0 || offset.y < 0.0 || offset.y > 1.0) ? 0.0 : 1.0
3811
+ )
3812
+ );
3813
+ totalWeight += weight;
3814
+ }
3815
+ float occ = clamp(1.0 - occluded / totalWeight, 0.0, 1.0);
3816
+ gl_FragColor = vec4(0.5 + 0.5 * normal, occ);
3817
+ }`
3818
+ )
3819
+ };
3820
+
3821
+ // src/rendering/post-effects/n8-ssao/FullScreenTriangle.ts
3822
+ import {
3823
+ BufferAttribute,
3824
+ BufferGeometry,
3825
+ Mesh as Mesh3,
3826
+ OrthographicCamera as OrthographicCamera2,
3827
+ Sphere
3828
+ } from "three";
3829
+ var FullScreenTriangle = class {
3830
+ constructor(material) {
3831
+ this.camera = new OrthographicCamera2();
3832
+ this.geometry = new BufferGeometry();
3833
+ this.geometry.setAttribute(
3834
+ "position",
3835
+ new BufferAttribute(new Float32Array([-1, -1, 3, -1, -1, 3]), 2)
3836
+ );
3837
+ this.geometry.setAttribute("uv", new BufferAttribute(new Float32Array([0, 0, 2, 0, 0, 2]), 2));
3838
+ this.geometry.boundingSphere = new Sphere();
3839
+ this.geometry.computeBoundingSphere = function() {
3840
+ };
3841
+ this.mesh = new Mesh3(this.geometry, material);
3842
+ this.mesh.frustumCulled = false;
3843
+ }
3844
+ get material() {
3845
+ return this.mesh.material;
3846
+ }
3847
+ set material(value) {
3848
+ this.mesh.material = value;
3849
+ }
3850
+ render(renderer) {
3851
+ renderer.render(this.mesh, this.camera);
3852
+ }
3853
+ dispose() {
3854
+ this.mesh.material.dispose();
3855
+ this.mesh.geometry.dispose();
3856
+ }
3857
+ };
3858
+
3859
+ // src/rendering/post-effects/n8-ssao/PoissionBlur.ts
3860
+ import { Matrix4 as Matrix45, Uniform as Uniform6, Vector2 as Vector25, Vector3 as Vector312 } from "three";
3861
+ var PoissionBlur = {
3862
+ uniforms: {
3863
+ sceneDiffuse: new Uniform6(null),
3864
+ sceneDepth: new Uniform6(null),
3865
+ tDiffuse: new Uniform6(null),
3866
+ projMat: new Uniform6(new Matrix45()),
3867
+ viewMat: new Uniform6(new Matrix45()),
3868
+ projectionMatrixInv: new Uniform6(new Matrix45()),
3869
+ viewMatrixInv: new Uniform6(new Matrix45()),
3870
+ cameraPos: new Uniform6(new Vector312()),
3871
+ resolution: new Uniform6(new Vector25()),
3872
+ time: new Uniform6(0),
3873
+ r: new Uniform6(5),
3874
+ blueNoise: new Uniform6(null),
3875
+ radius: new Uniform6(12),
3876
+ worldRadius: new Uniform6(5),
3877
+ index: new Uniform6(0),
3878
+ poissonDisk: new Uniform6([]),
3879
+ distanceFalloff: new Uniform6(1),
3880
+ near: new Uniform6(0.1),
3881
+ far: new Uniform6(1e3),
3882
+ logDepth: new Uniform6(false),
3883
+ screenSpaceRadius: new Uniform6(false)
3884
+ },
3885
+ depthWrite: false,
3886
+ depthTest: false,
3887
+ vertexShader: (
3888
+ /* glsl */
3889
+ `
3890
+ varying vec2 vUv;
3891
+ void main(void) {
3892
+ vUv = uv;
3893
+ gl_Position = vec4(position, 1.0);
3894
+ }`
3895
+ ),
3896
+ fragmentShader: (
3897
+ /* glsl */
3898
+ `
3899
+ uniform sampler2D sceneDiffuse;
3900
+ uniform highp sampler2D sceneDepth;
3901
+ uniform sampler2D tDiffuse;
3902
+ uniform sampler2D blueNoise;
3903
+ uniform mat4 projectionMatrixInv;
3904
+ uniform mat4 viewMatrixInv;
3905
+ uniform vec2 resolution;
3906
+ uniform float r;
3907
+ uniform float radius;
3908
+ uniform float worldRadius;
3909
+ uniform float index;
3910
+ uniform float near;
3911
+ uniform float far;
3912
+ uniform float distanceFalloff;
3913
+ uniform bool logDepth;
3914
+ uniform bool screenSpaceRadius;
3915
+ varying vec2 vUv;
3916
+
3917
+ highp float linearize_depth(highp float d, highp float zNear,highp float zFar) {
3918
+ highp float z_n = 2.0 * d - 1.0;
3919
+ return 2.0 * zNear * zFar / (zFar + zNear - z_n * (zFar - zNear));
3920
+ }
3921
+
3922
+ highp float linearize_depth_log(highp float d, highp float nearZ,highp float farZ) {
3923
+ float depth = pow(2.0, d * log2(farZ + 1.0)) - 1.0;
3924
+ float a = farZ / (farZ - nearZ);
3925
+ float b = farZ * nearZ / (nearZ - farZ);
3926
+ float linDepth = a + b / depth;
3927
+ return linearize_depth(linDepth, nearZ, farZ);
3928
+ }
3929
+
3930
+ highp float linearize_depth_ortho(highp float d, highp float nearZ, highp float farZ) {
3931
+ return nearZ + (farZ - nearZ) * d;
3932
+ }
3933
+
3934
+ vec3 getWorldPosLog(vec3 posS) {
3935
+ vec2 uv = posS.xy;
3936
+ float z = posS.z;
3937
+ float nearZ =near;
3938
+ float farZ = far;
3939
+ float depth = pow(2.0, z * log2(farZ + 1.0)) - 1.0;
3940
+ float a = farZ / (farZ - nearZ);
3941
+ float b = farZ * nearZ / (nearZ - farZ);
3942
+ float linDepth = a + b / depth;
3943
+ vec4 clipVec = vec4(uv, linDepth, 1.0) * 2.0 - 1.0;
3944
+ vec4 wpos = projectionMatrixInv * clipVec;
3945
+ return wpos.xyz / wpos.w;
3946
+ }
3947
+
3948
+ vec3 getWorldPos(float depth, vec2 coord) {
3949
+ #ifdef LOGDEPTH
3950
+ return getWorldPosLog(vec3(coord, depth));
3951
+ #endif
3952
+
3953
+ float z = depth * 2.0 - 1.0;
3954
+ vec4 clipSpacePosition = vec4(coord * 2.0 - 1.0, z, 1.0);
3955
+ vec4 viewSpacePosition = projectionMatrixInv * clipSpacePosition;
3956
+
3957
+ vec4 worldSpacePosition = viewSpacePosition;
3958
+ worldSpacePosition.xyz /= worldSpacePosition.w;
3959
+ return worldSpacePosition.xyz;
3960
+ }
3961
+
3962
+ #include <common>
3963
+
3964
+ #define NUM_SAMPLES 16
3965
+
3966
+ uniform vec2 poissonDisk[NUM_SAMPLES];
3967
+
3968
+ void main(void) {
3969
+ const float pi = acos(-1.0);
3970
+ vec2 texelSize = vec2(1.0 / resolution.x, 1.0 / resolution.y);
3971
+ vec2 uv = vUv;
3972
+ vec4 data = texture2D(tDiffuse, vUv);
3973
+ float occlusion = data.a;
3974
+ float baseOcc = data.a;
3975
+ vec3 normal = data.rgb * 2.0 - 1.0;
3976
+ float count = 1.0;
3977
+ float d = texture2D(sceneDepth, vUv).x;
3978
+ if (d == 1.0) {
3979
+ gl_FragColor = data;
3980
+ return;
3981
+ }
3982
+ vec3 worldPos = getWorldPos(d, vUv);
3983
+ float size = radius;
3984
+ float angle;
3985
+
3986
+ if (index == 0.0) {
3987
+ angle = texture2D(blueNoise, gl_FragCoord.xy / 128.0).x * PI2;
3988
+ } else if (index == 1.0) {
3989
+ angle = texture2D(blueNoise, gl_FragCoord.xy / 128.0).y * PI2;
3990
+ } else if (index == 2.0) {
3991
+ angle = texture2D(blueNoise, gl_FragCoord.xy / 128.0).z * PI2;
3992
+ } else {
3993
+ angle = texture2D(blueNoise, gl_FragCoord.xy / 128.0).w * PI2;
3994
+ }
3995
+
3996
+ mat2 rotationMatrix = mat2(cos(angle), -sin(angle), sin(angle), cos(angle));
3997
+ float radiusToUse = (
3998
+ screenSpaceRadius
3999
+ ? distance(worldPos, getWorldPos(d, vUv + vec2(worldRadius, 0.0) / resolution))
4000
+ : worldRadius
4001
+ );
4002
+ float distanceFalloffToUse = (
4003
+ screenSpaceRadius
4004
+ ? radiusToUse * distanceFalloff
4005
+ : distanceFalloff
4006
+ );
4007
+
4008
+ for(int i = 0; i < NUM_SAMPLES; i++) {
4009
+ vec2 offset = (rotationMatrix * poissonDisk[i]) * texelSize * size;
4010
+ vec4 dataSample = texture2D(tDiffuse, uv + offset);
4011
+ float occSample = dataSample.a;
4012
+ vec3 normalSample = dataSample.rgb * 2.0 - 1.0;
4013
+ float dSample = texture2D(sceneDepth, uv + offset).x;
4014
+ vec3 worldPosSample = getWorldPos(dSample, uv + offset);
4015
+ float tangentPlaneDist = abs(dot(worldPos - worldPosSample, normal));
4016
+ float rangeCheck = (
4017
+ (dSample == 1.0)
4018
+ ? 0.0
4019
+ : exp(
4020
+ -1.0 * tangentPlaneDist * (1.0 / distanceFalloffToUse)
4021
+ ) * max(dot(normal, normalSample), 0.0) * (1.0 - abs(occSample - baseOcc))
4022
+ );
4023
+ occlusion += occSample * rangeCheck;
4024
+ count += rangeCheck;
4025
+ }
4026
+
4027
+ if (count > 0.0) {
4028
+ occlusion /= count;
4029
+ }
4030
+
4031
+ #ifdef LOGDEPTH
4032
+ occlusion = clamp(occlusion, 0.0, 1.0);
4033
+ if (occlusion == 0.0) {
4034
+ occlusion = 1.0;
4035
+ }
4036
+ #endif
4037
+ gl_FragColor = vec4(0.5 + 0.5 * normal, occlusion);
4038
+ }
4039
+ `
4040
+ )
4041
+ };
4042
+
4043
+ // src/rendering/post-effects/n8-ssao/N8SSAOPass.ts
4044
+ var bluenoiseBits = Uint8Array.from(atob(BlueNoise), (c) => c.charCodeAt(0));
4045
+ function checkTimerQuery(timerQuery, gl, pass) {
4046
+ const available = gl.getQueryParameter(timerQuery, gl.QUERY_RESULT_AVAILABLE);
4047
+ if (available) {
4048
+ const elapsedTimeInNs = gl.getQueryParameter(timerQuery, gl.QUERY_RESULT);
4049
+ const elapsedTimeInMs = elapsedTimeInNs / 1e6;
4050
+ pass.lastTime = elapsedTimeInMs;
4051
+ } else {
4052
+ setTimeout(() => checkTimerQuery(timerQuery, gl, pass), 1);
4053
+ }
4054
+ }
4055
+ var N8SSAOPass = class extends Pass {
4056
+ constructor(scene, camera, width = 512, height = 512) {
4057
+ super();
4058
+ this.lastTime = 0;
4059
+ this.autosetGamma = true;
4060
+ this.samples = [];
4061
+ this.samplesR = [];
4062
+ this.samplesDenoise = [];
4063
+ this.copyQuadMaterial = new ShaderMaterial4({
4064
+ uniforms: { tDiffuse: new Uniform7(null) },
4065
+ depthWrite: false,
4066
+ vertexShader: (
4067
+ /* glsl */
4068
+ `
4069
+ varying vec2 vUv;
4070
+ void main(void) {
4071
+ vUv = uv;
4072
+ gl_Position = vec4(position, 1);
4073
+ }
4074
+ `
4075
+ ),
4076
+ fragmentShader: (
4077
+ /* glsl */
4078
+ `
4079
+ uniform sampler2D tDiffuse;
4080
+ varying vec2 vUv;
4081
+ void main(void) {
4082
+ gl_FragColor = texture2D(tDiffuse, vUv);
4083
+ }
4084
+ `
4085
+ )
4086
+ });
4087
+ this.copyQuad = new FullScreenTriangle(this.copyQuadMaterial);
4088
+ this.width = width;
4089
+ this.height = height;
4090
+ this.camera = camera;
4091
+ this.scene = scene;
4092
+ this.configuration = new Proxy(
4093
+ {
4094
+ aoSamples: 16,
4095
+ aoRadius: 5,
4096
+ denoiseSamples: 8,
4097
+ denoiseRadius: 12,
4098
+ distanceFalloff: 1,
4099
+ intensity: 5,
4100
+ denoiseIterations: 2,
4101
+ renderMode: 0,
4102
+ color: new Color6(0, 0, 0),
4103
+ gammaCorrection: true,
4104
+ logarithmicDepthBuffer: false,
4105
+ screenSpaceRadius: false,
4106
+ halfRes: false,
4107
+ depthAwareUpsampling: true,
4108
+ colorMultiply: true
4109
+ },
4110
+ {
4111
+ set: (target, propName, value) => {
4112
+ const oldProp = target[propName];
4113
+ target[propName] = value;
4114
+ if (propName === "aoSamples" && oldProp !== value) {
4115
+ this.configureAOPass(this.configuration.logarithmicDepthBuffer);
4116
+ }
4117
+ if (propName === "denoiseSamples" && oldProp !== value) {
4118
+ this.configureDenoisePass(this.configuration.logarithmicDepthBuffer);
4119
+ }
4120
+ if (propName === "halfRes" && oldProp !== value) {
4121
+ this.configureAOPass(this.configuration.logarithmicDepthBuffer);
4122
+ this.configureHalfResTargets();
4123
+ this.configureEffectCompositer(this.configuration.logarithmicDepthBuffer);
4124
+ this.setSize(this.width, this.height);
4125
+ }
4126
+ if (propName === "depthAwareUpsampling" && oldProp !== value) {
4127
+ this.configureEffectCompositer(this.configuration.logarithmicDepthBuffer);
4128
+ }
4129
+ if (propName === "gammaCorrection") {
4130
+ this.autosetGamma = false;
4131
+ }
4132
+ return true;
4133
+ }
4134
+ }
4135
+ );
4136
+ this.configureEffectCompositer(this.configuration.logarithmicDepthBuffer);
4137
+ this.configureSampleDependentPasses();
4138
+ this.configureHalfResTargets();
4139
+ this.writeTargetInternal = new WebGLRenderTarget(this.width, this.height, {
4140
+ minFilter: LinearFilter3,
4141
+ magFilter: LinearFilter3,
4142
+ depthBuffer: false
4143
+ });
4144
+ this.readTargetInternal = new WebGLRenderTarget(this.width, this.height, {
4145
+ minFilter: LinearFilter3,
4146
+ magFilter: LinearFilter3,
4147
+ depthBuffer: false
4148
+ });
4149
+ this.outputTargetInternal = new WebGLRenderTarget(this.width, this.height, {
4150
+ minFilter: LinearFilter3,
4151
+ magFilter: LinearFilter3,
4152
+ depthBuffer: false
4153
+ });
4154
+ this.bluenoise = new DataTexture(bluenoiseBits, 128, 128);
4155
+ this.bluenoise.colorSpace = NoColorSpace;
4156
+ this.bluenoise.wrapS = RepeatWrapping;
4157
+ this.bluenoise.wrapT = RepeatWrapping;
4158
+ this.bluenoise.minFilter = NearestFilter;
4159
+ this.bluenoise.magFilter = NearestFilter;
4160
+ this.bluenoise.needsUpdate = true;
4161
+ this.lastTime = 0;
4162
+ this.needsDepthTexture = true;
4163
+ this.needsSwap = true;
4164
+ this.r = new Vector26();
4165
+ this.c = new Color6();
4166
+ }
4167
+ configureHalfResTargets() {
4168
+ if (this.configuration.halfRes) {
4169
+ this.depthDownsampleTarget = new WebGLMultipleRenderTargets(
4170
+ this.width / 2,
4171
+ this.height / 2,
4172
+ 2,
4173
+ {
4174
+ depthBuffer: false
4175
+ }
4176
+ );
4177
+ this.depthDownsampleTarget.texture[0].format = RedFormat;
4178
+ this.depthDownsampleTarget.texture[0].type = FloatType;
4179
+ this.depthDownsampleTarget.texture[0].minFilter = NearestFilter;
4180
+ this.depthDownsampleTarget.texture[0].magFilter = NearestFilter;
4181
+ this.depthDownsampleTarget.texture[1].format = RGBAFormat2;
4182
+ this.depthDownsampleTarget.texture[1].type = HalfFloatType;
4183
+ this.depthDownsampleTarget.texture[1].minFilter = NearestFilter;
4184
+ this.depthDownsampleTarget.texture[1].magFilter = NearestFilter;
4185
+ this.depthDownsampleQuad = new FullScreenTriangle(new ShaderMaterial4(DepthDownSample));
4186
+ } else {
4187
+ if (this.depthDownsampleTarget) {
4188
+ this.depthDownsampleTarget.dispose();
4189
+ this.depthDownsampleTarget = null;
4190
+ }
4191
+ if (this.depthDownsampleQuad) {
4192
+ this.depthDownsampleQuad.dispose();
4193
+ this.depthDownsampleQuad = null;
4194
+ }
4195
+ }
4196
+ }
4197
+ configureSampleDependentPasses() {
4198
+ this.configureAOPass(this.configuration.logarithmicDepthBuffer);
4199
+ this.configureDenoisePass(this.configuration.logarithmicDepthBuffer);
4200
+ }
4201
+ configureAOPass(logarithmicDepthBuffer = false) {
4202
+ this.samples = this.generateHemisphereSamples(this.configuration.aoSamples);
4203
+ this.samplesR = this.generateHemisphereSamplesR(this.configuration.aoSamples);
4204
+ const e = { ...EffectShader };
4205
+ e.fragmentShader = e.fragmentShader.replace("16", this.configuration.aoSamples.toString()).replace("16.0", this.configuration.aoSamples.toString() + ".0");
4206
+ if (logarithmicDepthBuffer) {
4207
+ e.fragmentShader = "#define LOGDEPTH\n" + e.fragmentShader;
4208
+ }
4209
+ if (this.configuration.halfRes) {
4210
+ e.fragmentShader = "#define HALFRES\n" + e.fragmentShader;
4211
+ }
4212
+ if (this.effectShaderQuad) {
4213
+ this.effectShaderQuad.material.dispose();
4214
+ this.effectShaderQuad.material = new ShaderMaterial4(e);
4215
+ } else {
4216
+ this.effectShaderQuad = new FullScreenTriangle(new ShaderMaterial4(e));
4217
+ }
4218
+ }
4219
+ configureDenoisePass(logarithmicDepthBuffer = false) {
4220
+ this.samplesDenoise = this.generateDenoiseSamples(this.configuration.denoiseSamples, 11);
4221
+ const p = { ...PoissionBlur };
4222
+ p.fragmentShader = p.fragmentShader.replace("16", this.configuration.denoiseSamples.toString());
4223
+ if (logarithmicDepthBuffer) {
4224
+ p.fragmentShader = "#define LOGDEPTH\n" + p.fragmentShader;
4225
+ }
4226
+ if (this.poissonBlurQuad) {
4227
+ this.poissonBlurQuad.material.dispose();
4228
+ this.poissonBlurQuad.material = new ShaderMaterial4(p);
4229
+ } else {
4230
+ this.poissonBlurQuad = new FullScreenTriangle(new ShaderMaterial4(p));
4231
+ }
4232
+ }
4233
+ configureEffectCompositer(logarithmicDepthBuffer = false) {
4234
+ const e = { ...EffectCompositer };
4235
+ if (logarithmicDepthBuffer) {
4236
+ e.fragmentShader = "#define LOGDEPTH\n" + e.fragmentShader;
4237
+ }
4238
+ if (this.configuration.halfRes && this.configuration.depthAwareUpsampling) {
4239
+ e.fragmentShader = "#define HALFRES\n" + e.fragmentShader;
4240
+ }
4241
+ if (this.effectCompositerQuad) {
4242
+ this.effectCompositerQuad.material.dispose();
4243
+ this.effectCompositerQuad.material = new ShaderMaterial4(e);
4244
+ } else {
4245
+ this.effectCompositerQuad = new FullScreenTriangle(new ShaderMaterial4(e));
4246
+ }
4247
+ }
4248
+ generateHemisphereSamples(n) {
4249
+ const points = [];
4250
+ for (let k = 0; k < n; k++) {
4251
+ const theta = 2.399963 * k;
4252
+ const r = Math.sqrt(k + 0.5) / Math.sqrt(n);
4253
+ const x = r * Math.cos(theta);
4254
+ const y = r * Math.sin(theta);
4255
+ const z = Math.sqrt(1 - (x * x + y * y));
4256
+ points.push(new Vector313(x, y, z));
4257
+ }
4258
+ return points;
4259
+ }
4260
+ generateHemisphereSamplesR(n) {
4261
+ const samplesR = [];
4262
+ for (let i = 0; i < n; i++) {
4263
+ samplesR.push((i + 1) / n);
4264
+ }
4265
+ return samplesR;
4266
+ }
4267
+ generateDenoiseSamples(numSamples, numRings) {
4268
+ const angleStep = 2 * Math.PI * numRings / numSamples;
4269
+ const invNumSamples = 1 / numSamples;
4270
+ const radiusStep = invNumSamples;
4271
+ const samples = [];
4272
+ let radius = invNumSamples;
4273
+ let angle = 0;
4274
+ for (let i = 0; i < numSamples; i++) {
4275
+ samples.push(
4276
+ new Vector26(Math.cos(angle), Math.sin(angle)).multiplyScalar(Math.pow(radius, 0.75))
4277
+ );
4278
+ radius += radiusStep;
4279
+ angle += angleStep;
4280
+ }
4281
+ return samples;
4282
+ }
4283
+ setSize(width, height) {
4284
+ this.width = width;
4285
+ this.height = height;
4286
+ const c = this.configuration.halfRes ? 0.5 : 1;
4287
+ this.writeTargetInternal.setSize(width * c, height * c);
4288
+ this.readTargetInternal.setSize(width * c, height * c);
4289
+ if (this.configuration.halfRes && this.depthDownsampleTarget) {
4290
+ this.depthDownsampleTarget.setSize(width * c, height * c);
4291
+ }
4292
+ this.outputTargetInternal.setSize(width, height);
4293
+ }
4294
+ setDepthTexture(depthTexture) {
4295
+ this.depthTexture = depthTexture;
4296
+ }
4297
+ render(renderer, inputBuffer, outputBuffer) {
4298
+ const xrEnabled = renderer.xr.enabled;
4299
+ renderer.xr.enabled = false;
4300
+ let ext;
4301
+ let timerQuery = null;
4302
+ let gl = null;
4303
+ gl = renderer.getContext();
4304
+ if (this.debugMode) {
4305
+ ext = gl.getExtension("EXT_disjoint_timer_query_webgl2");
4306
+ if (ext === null) {
4307
+ console.error("EXT_disjoint_timer_query_webgl2 not available, disabling debug mode.");
4308
+ this.debugMode = false;
4309
+ gl = null;
4310
+ }
4311
+ }
4312
+ if (this.debugMode && gl) {
4313
+ timerQuery = gl.createQuery();
4314
+ gl.beginQuery(ext.TIME_ELAPSED_EXT, timerQuery);
4315
+ }
4316
+ if (renderer.capabilities.logarithmicDepthBuffer !== this.configuration.logarithmicDepthBuffer) {
4317
+ this.configuration.logarithmicDepthBuffer = renderer.capabilities.logarithmicDepthBuffer;
4318
+ this.configureAOPass(this.configuration.logarithmicDepthBuffer);
4319
+ this.configureDenoisePass(this.configuration.logarithmicDepthBuffer);
4320
+ this.configureEffectCompositer(this.configuration.logarithmicDepthBuffer);
4321
+ }
4322
+ if (inputBuffer.texture.type !== this.outputTargetInternal.texture.type) {
4323
+ this.outputTargetInternal.texture.type = inputBuffer.texture.type;
4324
+ this.outputTargetInternal.texture.needsUpdate = true;
4325
+ }
4326
+ this.camera.updateMatrixWorld();
4327
+ this.r.set(this.width, this.height);
4328
+ let trueRadius = this.configuration.aoRadius;
4329
+ if (this.configuration.halfRes && this.configuration.screenSpaceRadius) {
4330
+ trueRadius *= 0.5;
4331
+ }
4332
+ if (this.configuration.halfRes && this.depthDownsampleQuad) {
4333
+ const depthDownsampleUniforms = this.depthDownsampleQuad.material.uniforms;
4334
+ renderer.setRenderTarget(this.depthDownsampleTarget);
4335
+ depthDownsampleUniforms.sceneDepth.value = this.depthTexture;
4336
+ depthDownsampleUniforms.resolution.value = this.r;
4337
+ depthDownsampleUniforms.near.value = this.camera.near;
4338
+ depthDownsampleUniforms.far.value = this.camera.far;
4339
+ depthDownsampleUniforms.projectionMatrixInv.value = this.camera.projectionMatrixInverse;
4340
+ depthDownsampleUniforms.viewMatrixInv.value = this.camera.matrixWorld;
4341
+ depthDownsampleUniforms.logDepth.value = this.configuration.logarithmicDepthBuffer;
4342
+ this.depthDownsampleQuad.render(renderer);
4343
+ }
4344
+ if (!this.effectShaderQuad)
4345
+ return;
4346
+ const effectShaderUniforms = this.effectShaderQuad.material.uniforms;
4347
+ effectShaderUniforms.sceneDiffuse.value = inputBuffer.texture;
4348
+ effectShaderUniforms.sceneDepth.value = this.configuration.halfRes ? this.depthDownsampleTarget.texture[0] : this.depthTexture;
4349
+ effectShaderUniforms.sceneNormal.value = this.configuration.halfRes ? this.depthDownsampleTarget.texture[1] : null;
4350
+ effectShaderUniforms.projMat.value = this.camera.projectionMatrix;
4351
+ effectShaderUniforms.viewMat.value = this.camera.matrixWorldInverse;
4352
+ effectShaderUniforms.projViewMat.value = this.camera.projectionMatrix.clone().multiply(this.camera.matrixWorldInverse.clone());
4353
+ effectShaderUniforms.projectionMatrixInv.value = this.camera.projectionMatrixInverse;
4354
+ effectShaderUniforms.viewMatrixInv.value = this.camera.matrixWorld;
4355
+ effectShaderUniforms.cameraPos.value = this.camera.getWorldPosition(new Vector313());
4356
+ effectShaderUniforms.resolution.value = this.configuration.halfRes ? this.r.clone().multiplyScalar(1 / 2).floor() : this.r;
4357
+ effectShaderUniforms.time.value = performance.now() / 1e3;
4358
+ effectShaderUniforms.samples.value = this.samples;
4359
+ effectShaderUniforms.samplesR.value = this.samplesR;
4360
+ effectShaderUniforms.bluenoise.value = this.bluenoise;
4361
+ effectShaderUniforms.radius.value = trueRadius;
4362
+ effectShaderUniforms.distanceFalloff.value = this.configuration.distanceFalloff;
4363
+ effectShaderUniforms.near.value = this.camera.near;
4364
+ effectShaderUniforms.far.value = this.camera.far;
4365
+ effectShaderUniforms.logDepth.value = renderer.capabilities.logarithmicDepthBuffer;
4366
+ effectShaderUniforms.ortho.value = this.camera instanceof OrthographicCamera3;
4367
+ effectShaderUniforms.screenSpaceRadius.value = this.configuration.screenSpaceRadius;
4368
+ renderer.setRenderTarget(this.writeTargetInternal);
4369
+ this.effectShaderQuad.render(renderer);
4370
+ const poissonBlurUniforms = this.poissonBlurQuad.material.uniforms;
4371
+ for (let i = 0; i < this.configuration.denoiseIterations; i++) {
4372
+ if (!poissonBlurUniforms || !this.poissonBlurQuad)
4373
+ return;
4374
+ [this.writeTargetInternal, this.readTargetInternal] = [
4375
+ this.readTargetInternal,
4376
+ this.writeTargetInternal
4377
+ ];
4378
+ poissonBlurUniforms.tDiffuse.value = this.readTargetInternal.texture;
4379
+ poissonBlurUniforms.sceneDepth.value = this.configuration.halfRes ? this.depthDownsampleTarget.texture[0] : this.depthTexture;
4380
+ poissonBlurUniforms.projMat.value = this.camera.projectionMatrix;
4381
+ poissonBlurUniforms.viewMat.value = this.camera.matrixWorldInverse;
4382
+ poissonBlurUniforms.projectionMatrixInv.value = this.camera.projectionMatrixInverse;
4383
+ poissonBlurUniforms.viewMatrixInv.value = this.camera.matrixWorld;
4384
+ poissonBlurUniforms.cameraPos.value = this.camera.getWorldPosition(new Vector313());
4385
+ poissonBlurUniforms.resolution.value = this.configuration.halfRes ? this.r.clone().multiplyScalar(1 / 2).floor() : this.r;
4386
+ poissonBlurUniforms.time.value = performance.now() / 1e3;
4387
+ poissonBlurUniforms.blueNoise.value = this.bluenoise;
4388
+ poissonBlurUniforms.radius.value = this.configuration.denoiseRadius * (this.configuration.halfRes ? 1 / 2 : 1);
4389
+ poissonBlurUniforms.worldRadius.value = trueRadius;
4390
+ poissonBlurUniforms.distanceFalloff.value = this.configuration.distanceFalloff;
4391
+ poissonBlurUniforms.index.value = i;
4392
+ poissonBlurUniforms.poissonDisk.value = this.samplesDenoise;
4393
+ poissonBlurUniforms.near.value = this.camera.near;
4394
+ poissonBlurUniforms.far.value = this.camera.far;
4395
+ poissonBlurUniforms.logDepth.value = renderer.capabilities.logarithmicDepthBuffer;
4396
+ poissonBlurUniforms.screenSpaceRadius.value = this.configuration.screenSpaceRadius;
4397
+ renderer.setRenderTarget(this.writeTargetInternal);
4398
+ this.poissonBlurQuad.render(renderer);
4399
+ }
4400
+ const effectCompositerUniforms = this.effectCompositerQuad.material.uniforms;
4401
+ if (!effectCompositerUniforms || !this.effectCompositerQuad)
4402
+ return;
4403
+ effectCompositerUniforms.sceneDiffuse.value = inputBuffer.texture;
4404
+ effectCompositerUniforms.sceneDepth.value = this.depthTexture;
4405
+ effectCompositerUniforms.near.value = this.camera.near;
4406
+ effectCompositerUniforms.far.value = this.camera.far;
4407
+ effectCompositerUniforms.projectionMatrixInv.value = this.camera.projectionMatrixInverse;
4408
+ effectCompositerUniforms.viewMatrixInv.value = this.camera.matrixWorld;
4409
+ effectCompositerUniforms.logDepth.value = renderer.capabilities.logarithmicDepthBuffer;
4410
+ effectCompositerUniforms.ortho.value = this.camera instanceof OrthographicCamera3;
4411
+ effectCompositerUniforms.downsampledDepth.value = this.configuration.halfRes ? this.depthDownsampleTarget.texture[0] : this.depthTexture;
4412
+ effectCompositerUniforms.resolution.value = this.r;
4413
+ effectCompositerUniforms.blueNoise.value = this.bluenoise;
4414
+ effectCompositerUniforms.intensity.value = this.configuration.intensity;
4415
+ effectCompositerUniforms.renderMode.value = this.configuration.renderMode;
4416
+ effectCompositerUniforms.screenSpaceRadius.value = this.configuration.screenSpaceRadius;
4417
+ effectCompositerUniforms.radius.value = trueRadius;
4418
+ effectCompositerUniforms.distanceFalloff.value = this.configuration.distanceFalloff;
4419
+ effectCompositerUniforms.gammaCorrection.value = this.autosetGamma ? this.renderToScreen : this.configuration.gammaCorrection;
4420
+ effectCompositerUniforms.tDiffuse.value = this.writeTargetInternal.texture;
4421
+ effectCompositerUniforms.color.value = this.c.copy(this.configuration.color).convertSRGBToLinear();
4422
+ effectCompositerUniforms.colorMultiply.value = this.configuration.colorMultiply;
4423
+ effectCompositerUniforms.cameraPos.value = this.camera.getWorldPosition(new Vector313());
4424
+ effectCompositerUniforms.fog.value = !!this.scene.fog;
4425
+ if (this.scene.fog) {
4426
+ if (this.scene.fog instanceof Fog && this.scene.fog.isFog === true) {
4427
+ effectCompositerUniforms.fogExp.value = false;
4428
+ effectCompositerUniforms.fogNear.value = this.scene.fog.near;
4429
+ effectCompositerUniforms.fogFar.value = this.scene.fog.far;
4430
+ } else if (this.scene.fog instanceof FogExp2) {
4431
+ effectCompositerUniforms.fogExp.value = true;
4432
+ effectCompositerUniforms.fogDensity.value = this.scene.fog.density;
4433
+ } else {
4434
+ console.error(`Unsupported fog type ${this.scene.fog.constructor.name} in SSAOPass.`);
4435
+ }
4436
+ }
4437
+ renderer.setRenderTarget(this.outputTargetInternal);
4438
+ this.effectCompositerQuad.render(renderer);
4439
+ renderer.setRenderTarget(this.renderToScreen ? null : outputBuffer);
4440
+ this.copyQuad.material.uniforms.tDiffuse.value = this.outputTargetInternal.texture;
4441
+ this.copyQuad.render(renderer);
4442
+ if (this.debugMode && gl && timerQuery) {
4443
+ gl.endQuery(ext.TIME_ELAPSED_EXT);
4444
+ checkTimerQuery(timerQuery, gl, this);
4445
+ }
4446
+ renderer.xr.enabled = xrEnabled;
4447
+ }
4448
+ enableDebugMode() {
4449
+ this.debugMode = true;
4450
+ }
4451
+ disableDebugMode() {
4452
+ this.debugMode = false;
4453
+ }
4454
+ setDisplayMode(mode) {
4455
+ this.configuration.renderMode = ["Combined", "AO", "No AO", "Split", "Split AO"].indexOf(
4456
+ mode
4457
+ );
4458
+ }
4459
+ setQualityMode(mode) {
4460
+ if (mode === "Performance") {
4461
+ this.configuration.aoSamples = 8;
4462
+ this.configuration.denoiseSamples = 4;
4463
+ this.configuration.denoiseRadius = 12;
4464
+ } else if (mode === "Low") {
4465
+ this.configuration.aoSamples = 16;
4466
+ this.configuration.denoiseSamples = 4;
4467
+ this.configuration.denoiseRadius = 12;
4468
+ } else if (mode === "Medium") {
4469
+ this.configuration.aoSamples = 16;
4470
+ this.configuration.denoiseSamples = 8;
4471
+ this.configuration.denoiseRadius = 12;
4472
+ } else if (mode === "High") {
4473
+ this.configuration.aoSamples = 64;
4474
+ this.configuration.denoiseSamples = 8;
4475
+ this.configuration.denoiseRadius = 6;
4476
+ } else if (mode === "Ultra") {
4477
+ this.configuration.aoSamples = 64;
4478
+ this.configuration.denoiseSamples = 16;
4479
+ this.configuration.denoiseRadius = 6;
4480
+ }
4481
+ }
4482
+ };
4483
+
3117
4484
  // src/rendering/composer.ts
3118
4485
  var Composer = class {
3119
4486
  constructor(scene, camera, spawnSun = false) {
3120
4487
  this.width = 1;
3121
4488
  this.height = 1;
3122
- this.resolution = new Vector22(this.width, this.height);
4489
+ this.resolution = new Vector27(this.width, this.height);
3123
4490
  this.isEnvHDRI = false;
3124
4491
  this.bcs = BrightnessContrastSaturation;
3125
4492
  this.gaussGrainEffect = GaussGrainEffect;
3126
4493
  this.ambientLight = null;
3127
4494
  this.sun = null;
3128
4495
  this.scene = scene;
3129
- this.postPostScene = new Scene3();
4496
+ this.postPostScene = new Scene4();
3130
4497
  this.camera = camera;
3131
4498
  this.spawnSun = spawnSun;
3132
- this.renderer = new WebGLRenderer2({
4499
+ this.renderer = new WebGLRenderer4({
3133
4500
  powerPreference: "high-performance",
3134
4501
  antialias: false,
3135
4502
  stencil: false,
3136
4503
  depth: false
3137
4504
  });
4505
+ this.renderer.outputColorSpace = SRGBColorSpace;
3138
4506
  this.renderer.info.autoReset = false;
3139
4507
  this.renderer.setSize(this.width, this.height);
3140
4508
  this.renderer.shadowMap.enabled = true;
@@ -3144,7 +4512,7 @@ var Composer = class {
3144
4512
  this.setAmbientLight();
3145
4513
  this.setFog();
3146
4514
  this.effectComposer = new EffectComposer2(this.renderer, {
3147
- frameBufferType: HalfFloatType
4515
+ frameBufferType: HalfFloatType2
3148
4516
  });
3149
4517
  this.renderPass = new RenderPass(this.scene, this.camera);
3150
4518
  this.normalPass = new NormalPass2(this.scene, this.camera);
@@ -3165,7 +4533,7 @@ var Composer = class {
3165
4533
  bias: ppssaoValues.bias,
3166
4534
  fade: ppssaoValues.fade,
3167
4535
  resolutionScale: ppssaoValues.resolutionScale,
3168
- color: new Color6().setRGB(ppssaoValues.color.r, ppssaoValues.color.g, ppssaoValues.color.b),
4536
+ color: new Color7().setRGB(ppssaoValues.color.r, ppssaoValues.color.g, ppssaoValues.color.b),
3169
4537
  worldDistanceThreshold: ppssaoValues.worldDistanceThreshold,
3170
4538
  worldDistanceFalloff: ppssaoValues.worldDistanceFalloff,
3171
4539
  worldProximityThreshold: ppssaoValues.worldProximityThreshold,
@@ -3177,11 +4545,11 @@ var Composer = class {
3177
4545
  this.bloomEffect = new BloomEffect({
3178
4546
  intensity: extrasValues.bloom
3179
4547
  });
3180
- this.n8aopass = new N8AOPostPass(this.scene, this.camera, this.width, this.height);
4548
+ this.n8aopass = new N8SSAOPass(this.scene, this.camera, this.width, this.height);
3181
4549
  this.n8aopass.configuration.aoRadius = n8ssaoValues.aoRadius;
3182
4550
  this.n8aopass.configuration.distanceFalloff = n8ssaoValues.distanceFalloff;
3183
4551
  this.n8aopass.configuration.intensity = n8ssaoValues.intensity;
3184
- this.n8aopass.configuration.color = new Color6().setRGB(
4552
+ this.n8aopass.configuration.color = new Color7().setRGB(
3185
4553
  n8ssaoValues.color.r,
3186
4554
  n8ssaoValues.color.g,
3187
4555
  n8ssaoValues.color.b
@@ -3357,19 +4725,19 @@ var Composer = class {
3357
4725
  fileInput.click();
3358
4726
  }
3359
4727
  setFog() {
3360
- const fogColor = new Color6().setRGB(
4728
+ const fogColor = new Color7().setRGB(
3361
4729
  envValues.fog.fogColor.r,
3362
4730
  envValues.fog.fogColor.g,
3363
4731
  envValues.fog.fogColor.b
3364
4732
  );
3365
- this.scene.fog = new Fog(fogColor, envValues.fog.fogNear, envValues.fog.fogFar);
4733
+ this.scene.fog = new Fog2(fogColor, envValues.fog.fogNear, envValues.fog.fogFar);
3366
4734
  }
3367
4735
  setAmbientLight() {
3368
4736
  if (this.ambientLight) {
3369
4737
  this.scene.remove(this.ambientLight);
3370
4738
  this.ambientLight.dispose();
3371
4739
  }
3372
- const ambientLightColor = new Color6().setRGB(
4740
+ const ambientLightColor = new Color7().setRGB(
3373
4741
  envValues.ambientLight.ambientLightColor.r,
3374
4742
  envValues.ambientLight.ambientLightColor.g,
3375
4743
  envValues.ambientLight.ambientLightColor.b
@@ -3435,66 +4803,75 @@ import {
3435
4803
  } from "mml-web";
3436
4804
  import {
3437
4805
  Box3,
3438
- Color as Color7,
4806
+ Color as Color8,
3439
4807
  DoubleSide,
3440
- Euler as Euler2,
3441
- Group as Group4,
4808
+ Euler as Euler3,
4809
+ Group as Group5,
3442
4810
  Line3 as Line32,
3443
- Matrix4 as Matrix42,
4811
+ Matrix4 as Matrix46,
3444
4812
  Mesh as Mesh4,
3445
4813
  MeshBasicMaterial as MeshBasicMaterial3,
3446
- Ray,
3447
- Vector3 as Vector39
4814
+ Quaternion as Quaternion6,
4815
+ Ray as Ray2,
4816
+ Vector3 as Vector314
3448
4817
  } from "three";
3449
4818
  import { VertexNormalsHelper } from "three/examples/jsm/helpers/VertexNormalsHelper.js";
3450
4819
  import * as BufferGeometryUtils from "three/examples/jsm/utils/BufferGeometryUtils.js";
3451
- import { MeshBVH, MeshBVHVisualizer } from "three-mesh-bvh";
4820
+ import { MeshBVH, MeshBVHHelper } from "three-mesh-bvh";
3452
4821
  var CollisionsManager = class {
3453
4822
  constructor(scene) {
3454
4823
  this.debug = false;
3455
- this.tempVector = new Vector39();
3456
- this.tempVector2 = new Vector39();
3457
- this.tempVector3 = new Vector39();
3458
- this.tempRay = new Ray();
3459
- this.tempMatrix = new Matrix42();
3460
- this.tempMatrix2 = new Matrix42();
4824
+ this.tempVector = new Vector314();
4825
+ this.tempVector2 = new Vector314();
4826
+ this.tempVector3 = new Vector314();
4827
+ this.tempQuaternion = new Quaternion6();
4828
+ this.tempRay = new Ray2();
4829
+ this.tempMatrix = new Matrix46();
4830
+ this.tempMatrix2 = new Matrix46();
3461
4831
  this.tempBox = new Box3();
3462
- this.tempEuler = new Euler2();
4832
+ this.tempEuler = new Euler3();
3463
4833
  this.tempSegment = new Line32();
3464
4834
  this.tempSegment2 = new Line32();
3465
4835
  this.collisionMeshState = /* @__PURE__ */ new Map();
3466
4836
  this.scene = scene;
3467
4837
  this.collisionTrigger = MMLCollisionTrigger.init();
3468
4838
  }
3469
- raycastFirstDistance(ray) {
4839
+ raycastFirst(ray) {
3470
4840
  let minimumDistance = null;
3471
- for (const [, value] of this.collisionMeshState) {
3472
- this.tempRay.copy(ray).applyMatrix4(this.tempMatrix.copy(value.matrix).invert());
3473
- const hit = value.meshBVH.raycastFirst(this.tempRay, DoubleSide);
4841
+ let minimumHit = null;
4842
+ let minimumNormal = new Vector314();
4843
+ for (const [, collisionMeshState] of this.collisionMeshState) {
4844
+ this.tempRay.copy(ray).applyMatrix4(this.tempMatrix.copy(collisionMeshState.matrix).invert());
4845
+ const hit = collisionMeshState.meshBVH.raycastFirst(this.tempRay, DoubleSide);
3474
4846
  if (hit) {
3475
4847
  this.tempSegment.start.copy(this.tempRay.origin);
3476
4848
  this.tempSegment.end.copy(hit.point);
3477
- this.tempSegment.applyMatrix4(value.matrix);
4849
+ this.tempSegment.applyMatrix4(collisionMeshState.matrix);
3478
4850
  const dist = this.tempSegment.distance();
3479
4851
  if (minimumDistance === null || dist < minimumDistance) {
3480
4852
  minimumDistance = dist;
4853
+ minimumHit = collisionMeshState;
4854
+ minimumNormal = (hit.normal ? minimumNormal.copy(hit.normal) : minimumNormal).applyQuaternion(this.tempQuaternion.setFromRotationMatrix(collisionMeshState.matrix)).normalize();
3481
4855
  }
3482
4856
  }
3483
4857
  }
3484
- return minimumDistance;
4858
+ if (minimumDistance === null || minimumNormal === null || minimumHit === null) {
4859
+ return null;
4860
+ }
4861
+ return [minimumDistance, minimumNormal, minimumHit];
3485
4862
  }
3486
4863
  createCollisionMeshState(group, trackCollisions) {
3487
4864
  const geometries = [];
3488
4865
  group.updateWorldMatrix(true, false);
3489
4866
  const invertedRootMatrix = this.tempMatrix.copy(group.matrixWorld).invert();
3490
4867
  group.traverse((child) => {
3491
- if (child.type === "Mesh") {
3492
- const mesh = child;
3493
- const clonedGeometry = mesh.geometry.clone();
4868
+ const asMesh = child;
4869
+ if (asMesh.isMesh) {
4870
+ const clonedGeometry = asMesh.geometry.clone();
3494
4871
  if (child !== group) {
3495
- mesh.updateWorldMatrix(true, false);
4872
+ asMesh.updateWorldMatrix(true, false);
3496
4873
  clonedGeometry.applyMatrix4(
3497
- this.tempMatrix2.multiplyMatrices(invertedRootMatrix, mesh.matrixWorld)
4874
+ this.tempMatrix2.multiplyMatrices(invertedRootMatrix, asMesh.matrixWorld)
3498
4875
  );
3499
4876
  }
3500
4877
  for (const key in clonedGeometry.attributes) {
@@ -3522,9 +4899,9 @@ var CollisionsManager = class {
3522
4899
  newBufferGeometry.boundsTree = meshBVH;
3523
4900
  const wireframeMesh = new Mesh4(newBufferGeometry, new MeshBasicMaterial3({ wireframe: true }));
3524
4901
  const normalsHelper = new VertexNormalsHelper(wireframeMesh, 0.25, 65280);
3525
- const visualizer = new MeshBVHVisualizer(wireframeMesh, 4);
3526
- visualizer.edgeMaterial.color = new Color7("blue");
3527
- const debugGroup = new Group4();
4902
+ const visualizer = new MeshBVHHelper(wireframeMesh, 4);
4903
+ visualizer.edgeMaterial.color = new Color8("blue");
4904
+ const debugGroup = new Group5();
3528
4905
  debugGroup.add(wireframeMesh, normalsHelper, visualizer);
3529
4906
  group.matrixWorld.decompose(debugGroup.position, debugGroup.quaternion, debugGroup.scale);
3530
4907
  visualizer.update();
@@ -3603,7 +4980,7 @@ var CollisionsManager = class {
3603
4980
  const realDistance = intersectionSegment.distance();
3604
4981
  if (realDistance < capsuleRadius) {
3605
4982
  if (!collisionPosition) {
3606
- collisionPosition = new Vector39().copy(closestPointOnSegment).applyMatrix4(meshState.matrix);
4983
+ collisionPosition = new Vector314().copy(closestPointOnSegment).applyMatrix4(meshState.matrix);
3607
4984
  }
3608
4985
  const ratio = realDistance / modelReferenceDistance;
3609
4986
  const realDepth = capsuleRadius - realDistance;
@@ -3659,6 +5036,15 @@ export {
3659
5036
  MMLCompositionScene,
3660
5037
  Sun,
3661
5038
  TimeManager,
3662
- TweakPane
5039
+ TweakPane,
5040
+ clamp,
5041
+ decodeCharacterAndCamera,
5042
+ ease,
5043
+ encodeCharacterAndCamera,
5044
+ getSpawnPositionInsideCircle,
5045
+ remap,
5046
+ round,
5047
+ roundToDecimalPlaces,
5048
+ toArray
3663
5049
  };
3664
5050
  //# sourceMappingURL=index.js.map