@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/camera/CameraManager.d.ts +1 -1
- package/build/character/Character.d.ts +16 -21
- package/build/character/CharacterManager.d.ts +11 -7
- package/build/character/CharacterMaterial.d.ts +1 -1
- package/build/character/CharacterModel.d.ts +10 -7
- package/build/character/CharacterModelLoader.d.ts +4 -6
- package/build/character/CharacterTooltip.d.ts +4 -6
- package/build/character/LocalController.d.ts +23 -11
- package/build/character/RemoteController.d.ts +1 -9
- package/build/character/url-position.d.ts +12 -0
- package/build/collisions/CollisionsManager.d.ts +4 -4
- package/build/index.d.ts +3 -1
- package/build/index.js +2007 -621
- package/build/index.js.map +4 -4
- package/build/mml/MMLCompositionScene.d.ts +1 -0
- package/build/rendering/post-effects/n8-ssao/BlueNoise.d.ts +1 -0
- package/build/rendering/post-effects/n8-ssao/DepthDownSample.d.ts +17 -0
- package/build/rendering/post-effects/n8-ssao/EffectCompositer.d.ts +39 -0
- package/build/rendering/post-effects/n8-ssao/EffectShader.d.ts +31 -0
- package/build/rendering/post-effects/n8-ssao/FullScreenTriangle.d.ts +11 -0
- package/build/rendering/post-effects/n8-ssao/N8SSAOPass.d.ts +65 -0
- package/build/rendering/post-effects/n8-ssao/PoissionBlur.d.ts +30 -0
- package/build/tweakpane/blades/ssaoFolder.d.ts +21 -0
- package/package.json +9 -9
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 =
|
184
|
-
this.targetPhi = Math.
|
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
|
206
|
+
const firstRaycastHit = this.collisionsManager.raycastFirst(this.rayCaster.ray);
|
195
207
|
const cameraToPlayerDistance = this.camera.position.distanceTo(this.target);
|
196
|
-
if (
|
197
|
-
this.targetDistance = cameraToPlayerDistance -
|
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
|
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.
|
355
|
-
roughness: 0.
|
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.
|
362
|
-
envMapIntensity: 0.
|
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
|
-
|
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.
|
579
|
-
const lightness = 0.
|
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.
|
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.
|
666
|
+
this.animationConfig.sprintAnimationFileUrl,
|
654
667
|
2 /* running */
|
655
668
|
);
|
656
|
-
await this.setAnimationFromFile(
|
657
|
-
|
658
|
-
|
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
|
-
|
674
|
+
applyCustomMaterial(material) {
|
670
675
|
if (!this.mesh)
|
671
676
|
return;
|
672
677
|
this.mesh.traverse((child) => {
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
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
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
child.receiveShadow = receiveShadow;
|
690
|
-
}
|
691
|
-
});
|
690
|
+
updateAnimation(targetAnimation) {
|
691
|
+
if (this.currentAnimation !== targetAnimation) {
|
692
|
+
this.transitionToAnimation(targetAnimation);
|
693
|
+
}
|
692
694
|
}
|
693
|
-
|
694
|
-
|
695
|
-
|
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.
|
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
|
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.
|
715
|
-
|
716
|
-
|
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
|
-
|
728
|
-
|
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
|
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
|
-
|
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
|
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
|
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
|
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
|
982
|
-
|
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.
|
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
|
1005
|
-
this.
|
1006
|
-
this.
|
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.
|
1071
|
+
if (!this.tooltipMaterial) {
|
1013
1072
|
return;
|
1014
1073
|
}
|
1015
|
-
if (this.
|
1016
|
-
this.
|
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.
|
1041
|
-
this.
|
1042
|
-
this.
|
1043
|
-
this.
|
1044
|
-
this.
|
1045
|
-
this.
|
1046
|
-
this.
|
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.
|
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.
|
1063
|
-
const opacity = this.
|
1121
|
+
this.lookAt(camera.position);
|
1122
|
+
const opacity = this.tooltipMaterial.opacity;
|
1064
1123
|
if (opacity < this.targetOpacity) {
|
1065
|
-
this.
|
1066
|
-
this.
|
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.
|
1071
|
-
this.
|
1129
|
+
this.tooltipMaterial.opacity = Math.max(
|
1130
|
+
this.tooltipMaterial.opacity - this.fadingSpeed,
|
1072
1131
|
this.targetOpacity
|
1073
1132
|
);
|
1074
|
-
if (opacity >= 1 && this.
|
1075
|
-
this.
|
1076
|
-
this.
|
1077
|
-
} else if (opacity > 0 && opacity < 1 && this.
|
1078
|
-
this.
|
1079
|
-
this.
|
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.
|
1082
|
-
this.
|
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
|
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(
|
1092
|
-
this.
|
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
|
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 =
|
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.
|
1115
|
-
this.
|
1116
|
-
this.
|
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.
|
1122
|
-
this.
|
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.
|
1125
|
-
this.
|
1126
|
-
this.
|
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
|
-
|
1136
|
-
|
1137
|
-
|
1138
|
-
if (
|
1139
|
-
this.
|
1140
|
-
|
1141
|
-
|
1142
|
-
this.
|
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.
|
1298
|
+
this.character.updateAnimation(targetAnimation);
|
1159
1299
|
} else {
|
1160
|
-
this.
|
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
|
-
|
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.
|
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
|
-
|
1205
|
-
|
1206
|
-
|
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 =
|
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.
|
1215
|
-
this.
|
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
|
-
|
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 =
|
1232
|
-
|
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.
|
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
|
-
|
1246
|
-
|
1247
|
-
|
1248
|
-
|
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
|
-
|
1391
|
+
acceleration.y += this.jumpForce / deltaTime;
|
1254
1392
|
this.canJump = false;
|
1255
1393
|
} else {
|
1256
|
-
this.
|
1394
|
+
if (this.currentSurfaceAngle.y < minimumSurfaceAngle) {
|
1395
|
+
acceleration.y += this.gravity;
|
1396
|
+
}
|
1257
1397
|
}
|
1258
1398
|
} else if (this.jump && this.coyoteTime) {
|
1259
|
-
|
1399
|
+
acceleration.y += this.jumpForce / deltaTime;
|
1260
1400
|
this.canJump = false;
|
1261
1401
|
} else {
|
1262
|
-
|
1402
|
+
acceleration.y += this.gravity;
|
1263
1403
|
this.canJump = false;
|
1264
1404
|
}
|
1265
|
-
this.
|
1266
|
-
this.
|
1267
|
-
if (this.
|
1268
|
-
|
1269
|
-
|
1270
|
-
|
1271
|
-
|
1272
|
-
|
1273
|
-
|
1274
|
-
|
1275
|
-
|
1276
|
-
|
1277
|
-
|
1278
|
-
|
1279
|
-
|
1280
|
-
|
1281
|
-
|
1282
|
-
|
1283
|
-
|
1284
|
-
|
1285
|
-
|
1286
|
-
|
1287
|
-
|
1288
|
-
|
1289
|
-
|
1290
|
-
this.
|
1291
|
-
this.
|
1292
|
-
|
1293
|
-
|
1294
|
-
|
1295
|
-
|
1296
|
-
|
1297
|
-
|
1298
|
-
|
1299
|
-
|
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
|
1461
|
+
this.coyoteTime = this.characterVelocity.y < 0 && !this.characterOnGround && Date.now() - this.characterAirborneSince < this.coyoteTimeThreshold;
|
1304
1462
|
this.characterWasOnGround = this.characterOnGround;
|
1305
|
-
|
1306
|
-
|
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
|
-
|
1309
|
-
|
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
|
-
|
1314
|
-
|
1315
|
-
this.
|
1316
|
-
|
1317
|
-
|
1318
|
-
|
1319
|
-
|
1320
|
-
}
|
1321
|
-
|
1322
|
-
|
1323
|
-
|
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.
|
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,
|
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.
|
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.
|
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.
|
1562
|
+
this.character.quaternion.slerp(rotationQuaternion, 0.6);
|
1482
1563
|
if (state !== this.currentAnimation) {
|
1483
|
-
this.
|
1564
|
+
this.currentAnimation = state;
|
1565
|
+
this.character.updateAnimation(state);
|
1484
1566
|
}
|
1485
1567
|
}
|
1486
1568
|
};
|
1487
1569
|
|
1488
|
-
// src/character/
|
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
|
1580
|
+
function decodeCharacterAndCamera(hash) {
|
1498
1581
|
const values = hash.split(",").map(Number);
|
1499
|
-
|
1500
|
-
|
1501
|
-
|
1502
|
-
|
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,
|
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.
|
1602
|
+
this.keyInputManager = keyInputManager;
|
1512
1603
|
this.clientStates = clientStates;
|
1513
1604
|
this.sendUpdate = sendUpdate;
|
1514
|
-
|
1515
|
-
|
1516
|
-
|
1517
|
-
|
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.
|
1525
|
-
this.
|
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
|
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
|
-
|
1622
|
-
|
1623
|
-
|
1624
|
-
|
1625
|
-
|
1626
|
-
|
1627
|
-
|
1628
|
-
|
1629
|
-
|
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
|
1692
|
+
this.group.remove(character);
|
1636
1693
|
this.remoteCharacters.delete(id);
|
1637
1694
|
this.remoteCharacterControllers.delete(id);
|
1638
1695
|
}
|
1639
|
-
if (this.
|
1640
|
-
this.group.remove(this.
|
1641
|
-
this.
|
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
|
1650
|
-
if (this.
|
1651
|
-
this.
|
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.
|
1654
|
-
}
|
1655
|
-
|
1656
|
-
|
1657
|
-
this.
|
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
|
-
(
|
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) &&
|
1675
|
-
this.
|
1727
|
+
if (!this.remoteCharacters.has(id) && this.localCharacterSpawned === true) {
|
1728
|
+
this.spawnRemoteCharacter(
|
1676
1729
|
this.characterDescription,
|
1677
1730
|
id,
|
1678
|
-
|
1679
|
-
|
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
|
-
(
|
1692
|
-
this.group.remove(character
|
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.
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
1780
|
-
|
1831
|
+
const cached = { blob, originalExtension };
|
1832
|
+
this.modelCache.set(fileUrl, cached);
|
1781
1833
|
this.ongoingLoads.delete(fileUrl);
|
1782
|
-
return
|
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
|
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
|
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
|
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:
|
2014
|
-
sunPolarAngle:
|
2070
|
+
sunAzimuthalAngle: 215,
|
2071
|
+
sunPolarAngle: -39
|
2015
2072
|
},
|
2016
|
-
sunIntensity:
|
2017
|
-
sunColor: { r: 1, g:
|
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:
|
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: [
|
2540
|
+
size: [3, 2],
|
2484
2541
|
cells: (x, y) => ({
|
2485
|
-
title: `${n8ssaoOptions.viewMode[y *
|
2486
|
-
value: `${n8ssaoOptions.viewMode[y *
|
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
|
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
|
-
|
2885
|
-
|
2886
|
-
|
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
|
2892
|
-
var Sun = class extends
|
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
|
2897
|
-
|
2898
|
-
|
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
|
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
|
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
|
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
|
4496
|
+
this.postPostScene = new Scene4();
|
3130
4497
|
this.camera = camera;
|
3131
4498
|
this.spawnSun = spawnSun;
|
3132
|
-
this.renderer = new
|
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:
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
4806
|
+
Color as Color8,
|
3439
4807
|
DoubleSide,
|
3440
|
-
Euler as
|
3441
|
-
Group as
|
4808
|
+
Euler as Euler3,
|
4809
|
+
Group as Group5,
|
3442
4810
|
Line3 as Line32,
|
3443
|
-
Matrix4 as
|
4811
|
+
Matrix4 as Matrix46,
|
3444
4812
|
Mesh as Mesh4,
|
3445
4813
|
MeshBasicMaterial as MeshBasicMaterial3,
|
3446
|
-
|
3447
|
-
|
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,
|
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
|
3456
|
-
this.tempVector2 = new
|
3457
|
-
this.tempVector3 = new
|
3458
|
-
this.
|
3459
|
-
this.
|
3460
|
-
this.
|
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
|
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
|
-
|
4839
|
+
raycastFirst(ray) {
|
3470
4840
|
let minimumDistance = null;
|
3471
|
-
|
3472
|
-
|
3473
|
-
|
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(
|
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
|
-
|
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
|
-
|
3492
|
-
|
3493
|
-
const clonedGeometry =
|
4868
|
+
const asMesh = child;
|
4869
|
+
if (asMesh.isMesh) {
|
4870
|
+
const clonedGeometry = asMesh.geometry.clone();
|
3494
4871
|
if (child !== group) {
|
3495
|
-
|
4872
|
+
asMesh.updateWorldMatrix(true, false);
|
3496
4873
|
clonedGeometry.applyMatrix4(
|
3497
|
-
this.tempMatrix2.multiplyMatrices(invertedRootMatrix,
|
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
|
3526
|
-
visualizer.edgeMaterial.color = new
|
3527
|
-
const debugGroup = new
|
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
|
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
|