@mml-io/3d-web-client-core 0.0.0-experimental-74874a9-20230815 → 0.0.0-experimental-9432cd6-20230816

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.
@@ -0,0 +1,32 @@
1
+ import * as THREE from "three";
2
+ type RGBA = {
3
+ r: number;
4
+ g: number;
5
+ b: number;
6
+ a: number;
7
+ };
8
+ type CanvasTextOptions = {
9
+ fontSize: number;
10
+ textColorRGB255A1: RGBA;
11
+ backgroundColorRGB255A1?: RGBA;
12
+ font?: string;
13
+ bold?: boolean;
14
+ paddingPx?: number;
15
+ alignment?: string;
16
+ dimensions?: {
17
+ width: number;
18
+ height: number;
19
+ };
20
+ };
21
+ export declare function CanvasText(message: string, options: CanvasTextOptions): HTMLCanvasElement;
22
+ export declare function THREECanvasTextTexture(text: string, options: CanvasTextOptions): {
23
+ texture: THREE.Texture;
24
+ width: number;
25
+ height: number;
26
+ };
27
+ export declare function THREECanvasTextMaterial(text: string, options: CanvasTextOptions): {
28
+ material: THREE.MeshBasicMaterial;
29
+ width: number;
30
+ height: number;
31
+ };
32
+ export {};
@@ -4,6 +4,7 @@ import { CollisionsManager } from "../collisions/CollisionsManager";
4
4
  import { KeyInputManager } from "../input/KeyInputManager";
5
5
  import { TimeManager } from "../time/TimeManager";
6
6
  import { CharacterModel } from "./CharacterModel";
7
+ import { CharacterTooltip } from "./CharacterTooltip";
7
8
  import { LocalController } from "./LocalController";
8
9
  export type CharacterDescription = {
9
10
  meshFileUrl: string;
@@ -27,6 +28,7 @@ export declare class Character {
27
28
  model: CharacterModel | null;
28
29
  color: Color;
29
30
  position: Vector3;
31
+ tooltip: CharacterTooltip | null;
30
32
  constructor(characterDescription: CharacterDescription, id: number, isLocal: boolean, modelLoadedCallback: () => void, collisionsManager: CollisionsManager, keyInputManager: KeyInputManager, cameraManager: CameraManager, timeManager: TimeManager);
31
33
  private load;
32
34
  update(time: number): void;
@@ -0,0 +1,17 @@
1
+ import { Camera, Object3D } from "three";
2
+ export declare class CharacterTooltip {
3
+ private texture;
4
+ private geometry;
5
+ private material;
6
+ private mesh;
7
+ private visibleOpacity;
8
+ private targetOpacity;
9
+ private fadingSpeed;
10
+ private secondsToFadeOut;
11
+ private props;
12
+ constructor(parentModel: Object3D);
13
+ private redrawText;
14
+ setText(text: string, temporary?: boolean): void;
15
+ hide(): void;
16
+ update(camera: Camera): void;
17
+ }
package/build/index.js CHANGED
@@ -168,7 +168,7 @@ var CameraManager = class {
168
168
  };
169
169
 
170
170
  // src/character/Character.ts
171
- import { Color as Color2, Vector3 as Vector34 } from "three";
171
+ import { Color as Color3, Vector3 as Vector34 } from "three";
172
172
 
173
173
  // src/character/CharacterModel.ts
174
174
  import {
@@ -724,6 +724,247 @@ var CharacterModel = class {
724
724
  }
725
725
  };
726
726
 
727
+ // src/character/CharacterTooltip.ts
728
+ import {
729
+ Color as Color2,
730
+ FrontSide,
731
+ LinearFilter as LinearFilter2,
732
+ Mesh as Mesh2,
733
+ MeshBasicMaterial as MeshBasicMaterial2,
734
+ PlaneGeometry
735
+ } from "three";
736
+
737
+ // src/character/CanvasText.ts
738
+ import * as THREE from "three";
739
+ function getTextAlignOffset(textAlign, width) {
740
+ switch (textAlign) {
741
+ case "center":
742
+ return width / 2;
743
+ case "right":
744
+ return width;
745
+ default:
746
+ return 0;
747
+ }
748
+ }
749
+ function printAtWordWrap(context, fullText, x, y, lineHeight, fitWidth, padding, alignment) {
750
+ const lines = fullText.split("\n");
751
+ let currentLine = 0;
752
+ for (const text of lines) {
753
+ fitWidth = fitWidth || 0;
754
+ if (fitWidth <= 0) {
755
+ context.fillText(text, x, y + lineHeight * currentLine);
756
+ currentLine++;
757
+ continue;
758
+ }
759
+ let words = text.split(" ");
760
+ let lastWordIndex = 1;
761
+ while (words.length > 0 && lastWordIndex <= words.length) {
762
+ const str = words.slice(0, lastWordIndex).join(" ");
763
+ const textWidth = context.measureText(str).width;
764
+ if (textWidth + padding * 2 > fitWidth) {
765
+ if (lastWordIndex === 1) {
766
+ lastWordIndex = 2;
767
+ }
768
+ context.fillText(
769
+ words.slice(0, lastWordIndex - 1).join(" "),
770
+ x + padding,
771
+ y + lineHeight * currentLine + padding
772
+ );
773
+ currentLine++;
774
+ words = words.splice(lastWordIndex - 1);
775
+ lastWordIndex = 1;
776
+ } else {
777
+ lastWordIndex++;
778
+ }
779
+ }
780
+ if (lastWordIndex > 0 && words.length > 0) {
781
+ const xOffset = alignment === "center" ? 0 : padding;
782
+ context.fillText(words.join(" "), x + xOffset, y + lineHeight * currentLine + padding);
783
+ currentLine++;
784
+ }
785
+ }
786
+ }
787
+ function CanvasText(message, options) {
788
+ const fontsize = options.fontSize;
789
+ const textColor = options.textColorRGB255A1;
790
+ const backgroundColor = options.backgroundColorRGB255A1 || { r: 255, g: 255, b: 255, a: 1 };
791
+ const padding = options.paddingPx || 0;
792
+ const font = options.font || "Arial";
793
+ const fontString = (options.bold ? "bold " : "") + fontsize + "px " + font;
794
+ const canvas = document.createElement("canvas");
795
+ const ct = canvas.getContext("2d");
796
+ const textAlign = options.alignment ?? "left";
797
+ if (options.dimensions) {
798
+ canvas.width = options.dimensions.width;
799
+ canvas.height = options.dimensions.height;
800
+ ct.clearRect(0, 0, canvas.width, canvas.height);
801
+ ct.font = fontString;
802
+ ct.textAlign = textAlign;
803
+ ct.fillStyle = `rgba(${backgroundColor.r}, ${backgroundColor.g}, ${backgroundColor.b}, ${backgroundColor.a})`;
804
+ ct.lineWidth = 0;
805
+ ct.fillRect(0, 0, canvas.width, canvas.height);
806
+ ct.fillStyle = `rgba(${textColor.r}, ${textColor.g}, ${textColor.b}, ${textColor.a})`;
807
+ ct.font = fontString;
808
+ printAtWordWrap(
809
+ ct,
810
+ message,
811
+ getTextAlignOffset(textAlign, canvas.width),
812
+ fontsize,
813
+ fontsize,
814
+ canvas.width,
815
+ padding,
816
+ textAlign
817
+ );
818
+ } else {
819
+ ct.font = fontString;
820
+ const metrics = ct.measureText(message);
821
+ const textWidth = metrics.width;
822
+ const textHeight = metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent;
823
+ canvas.width = textWidth + padding * 2;
824
+ canvas.height = textHeight + padding;
825
+ ct.clearRect(0, 0, canvas.width, canvas.height);
826
+ ct.font = fontString;
827
+ ct.textAlign = textAlign;
828
+ ct.fillStyle = `rgba(${backgroundColor.r}, ${backgroundColor.g}, ${backgroundColor.b}, ${backgroundColor.a})`;
829
+ ct.lineWidth = 0;
830
+ ct.fillRect(0, 0, canvas.width, canvas.height);
831
+ ct.fillStyle = `rgba(${textColor.r}, ${textColor.g}, ${textColor.b}, ${textColor.a})`;
832
+ ct.font = fontString;
833
+ ct.fillText(message, padding + getTextAlignOffset(textAlign, textWidth), textHeight);
834
+ }
835
+ return canvas;
836
+ }
837
+ function THREECanvasTextTexture(text, options) {
838
+ const canvas = CanvasText(text, options);
839
+ const texture = new THREE.Texture(canvas);
840
+ texture.minFilter = THREE.LinearFilter;
841
+ texture.magFilter = THREE.LinearFilter;
842
+ texture.format = THREE.RGBAFormat;
843
+ texture.needsUpdate = true;
844
+ return { texture, width: canvas.width, height: canvas.height };
845
+ }
846
+
847
+ // src/character/CharacterTooltip.ts
848
+ var fontScale = 5;
849
+ var defaultLabelColor = new Color2(0);
850
+ var defaultFontColor = new Color2(16777215);
851
+ var defaultLabelAlignment = "center" /* center */;
852
+ var defaultLabelFontSize = 9;
853
+ var defaultLabelPadding = 0;
854
+ var defaultLabelWidth = 0.25;
855
+ var defaultLabelHeight = 0.125;
856
+ var defaultLabelCastShadows = true;
857
+ var CharacterTooltip = class {
858
+ constructor(parentModel) {
859
+ __publicField(this, "texture");
860
+ __publicField(this, "geometry");
861
+ __publicField(this, "material");
862
+ __publicField(this, "mesh");
863
+ __publicField(this, "visibleOpacity", 0.85);
864
+ __publicField(this, "targetOpacity", 0);
865
+ __publicField(this, "fadingSpeed", 0.02);
866
+ __publicField(this, "secondsToFadeOut", 15);
867
+ __publicField(this, "props", {
868
+ content: "",
869
+ alignment: defaultLabelAlignment,
870
+ width: defaultLabelWidth,
871
+ height: defaultLabelHeight,
872
+ fontSize: defaultLabelFontSize,
873
+ padding: defaultLabelPadding,
874
+ color: defaultLabelColor,
875
+ fontColor: defaultFontColor,
876
+ castShadows: defaultLabelCastShadows
877
+ });
878
+ this.setText = this.setText.bind(this);
879
+ this.material = new MeshBasicMaterial2({
880
+ map: this.texture,
881
+ transparent: true,
882
+ opacity: 0
883
+ });
884
+ this.material.side = FrontSide;
885
+ this.geometry = new PlaneGeometry(1, 1, 1, 1);
886
+ this.mesh = new Mesh2(this.geometry, this.material);
887
+ this.mesh.position.set(0, 1.6, 0);
888
+ this.mesh.visible = false;
889
+ parentModel.add(this.mesh);
890
+ }
891
+ redrawText(content) {
892
+ if (!this.material) {
893
+ return;
894
+ }
895
+ if (this.material.map) {
896
+ this.material.map.dispose();
897
+ }
898
+ const { texture, width, height } = THREECanvasTextTexture(content, {
899
+ bold: true,
900
+ fontSize: this.props.fontSize * fontScale,
901
+ paddingPx: this.props.padding,
902
+ textColorRGB255A1: {
903
+ r: this.props.fontColor.r * 255,
904
+ g: this.props.fontColor.g * 255,
905
+ b: this.props.fontColor.b * 255,
906
+ a: 1
907
+ },
908
+ backgroundColorRGB255A1: {
909
+ r: this.props.color.r * 255,
910
+ g: this.props.color.g * 255,
911
+ b: this.props.color.b * 255,
912
+ a: 1
913
+ },
914
+ dimensions: {
915
+ width: this.props.width * (100 * fontScale),
916
+ height: this.props.height * (100 * fontScale)
917
+ },
918
+ alignment: this.props.alignment
919
+ });
920
+ this.material.map = texture;
921
+ this.material.map.magFilter = LinearFilter2;
922
+ this.material.map.minFilter = LinearFilter2;
923
+ this.material.needsUpdate = true;
924
+ this.mesh.scale.x = width / (100 * fontScale);
925
+ this.mesh.scale.y = height / (100 * fontScale);
926
+ this.mesh.position.y = 1.6;
927
+ }
928
+ setText(text, temporary = false) {
929
+ this.redrawText(text);
930
+ this.mesh.visible = true;
931
+ this.targetOpacity = this.visibleOpacity;
932
+ if (temporary) {
933
+ setTimeout(() => {
934
+ this.hide();
935
+ }, this.secondsToFadeOut * 1e3);
936
+ }
937
+ }
938
+ hide() {
939
+ this.targetOpacity = 0;
940
+ }
941
+ update(camera) {
942
+ this.mesh.lookAt(camera.position);
943
+ const opacity = this.mesh.material.opacity;
944
+ if (opacity < this.targetOpacity) {
945
+ this.mesh.material.opacity = Math.min(
946
+ this.mesh.material.opacity + this.fadingSpeed,
947
+ this.targetOpacity
948
+ );
949
+ } else if (opacity > this.targetOpacity) {
950
+ this.mesh.material.opacity = Math.max(
951
+ this.mesh.material.opacity - this.fadingSpeed,
952
+ this.targetOpacity
953
+ );
954
+ if (opacity >= 1 && this.mesh.material.transparent === true) {
955
+ this.mesh.material.transparent = false;
956
+ this.mesh.material.needsUpdate = true;
957
+ } else if (opacity > 0 && opacity < 1 && this.mesh.material.transparent === false) {
958
+ this.mesh.material.transparent = true;
959
+ this.mesh.material.needsUpdate = true;
960
+ }
961
+ if (this.mesh.material.opacity <= 0) {
962
+ this.mesh.visible = false;
963
+ }
964
+ }
965
+ }
966
+ };
967
+
727
968
  // src/character/LocalController.ts
728
969
  import { Box3, Line3, Matrix4, Quaternion, Raycaster as Raycaster2, Vector3 as Vector33 } from "three";
729
970
  var LocalController = class {
@@ -997,13 +1238,17 @@ var Character = class {
997
1238
  __publicField(this, "controller", null);
998
1239
  __publicField(this, "name", null);
999
1240
  __publicField(this, "model", null);
1000
- __publicField(this, "color", new Color2());
1241
+ __publicField(this, "color", new Color3());
1001
1242
  __publicField(this, "position", new Vector34());
1243
+ __publicField(this, "tooltip", null);
1002
1244
  this.load();
1003
1245
  }
1004
1246
  async load() {
1005
1247
  this.model = new CharacterModel(this.characterDescription);
1006
1248
  await this.model.init();
1249
+ if (this.tooltip === null) {
1250
+ this.tooltip = new CharacterTooltip(this.model.mesh);
1251
+ }
1007
1252
  this.color = this.model.material.colorsCube216[this.id];
1008
1253
  if (this.isLocal) {
1009
1254
  this.controller = new LocalController(
@@ -1020,6 +1265,9 @@ var Character = class {
1020
1265
  update(time) {
1021
1266
  if (!this.model)
1022
1267
  return;
1268
+ if (this.tooltip) {
1269
+ this.tooltip.update(this.cameraManager.camera);
1270
+ }
1023
1271
  this.model.mesh.getWorldPosition(this.position);
1024
1272
  if (typeof this.model.material.uniforms.time !== "undefined") {
1025
1273
  this.model.material.uniforms.time.value = time;
@@ -1035,7 +1283,7 @@ import { Group, Vector3 as Vector36 } from "three";
1035
1283
  // src/character/RemoteController.ts
1036
1284
  import {
1037
1285
  AnimationMixer as AnimationMixer2,
1038
- Object3D as Object3D3,
1286
+ Object3D as Object3D4,
1039
1287
  Quaternion as Quaternion2,
1040
1288
  Vector3 as Vector35
1041
1289
  } from "three";
@@ -1045,7 +1293,7 @@ var RemoteController = class {
1045
1293
  this.id = id;
1046
1294
  __publicField(this, "modelLoader", ModelLoader_default);
1047
1295
  __publicField(this, "characterModel", null);
1048
- __publicField(this, "animationMixer", new AnimationMixer2(new Object3D3()));
1296
+ __publicField(this, "animationMixer", new AnimationMixer2(new Object3D4()));
1049
1297
  __publicField(this, "animations", /* @__PURE__ */ new Map());
1050
1298
  __publicField(this, "currentAnimation", 0 /* idle */);
1051
1299
  __publicField(this, "networkState", {
@@ -1187,6 +1435,7 @@ var CharacterManager = class {
1187
1435
  this.group.add(character.model.mesh);
1188
1436
  if (isLocal) {
1189
1437
  this.character = character;
1438
+ this.character.tooltip?.setText(`${id}`);
1190
1439
  } else {
1191
1440
  this.remoteCharacters.set(id, character);
1192
1441
  const remoteController = new RemoteController(character, id);
@@ -1212,6 +1461,7 @@ var CharacterManager = class {
1212
1461
  spawnPosition.z
1213
1462
  );
1214
1463
  this.remoteCharacterControllers.set(id, remoteController);
1464
+ character.tooltip?.setText(`${id}`);
1215
1465
  }
1216
1466
  resolve(character);
1217
1467
  },
@@ -1441,7 +1691,7 @@ import {
1441
1691
  NormalPass
1442
1692
  } from "postprocessing";
1443
1693
  import {
1444
- Color as Color4,
1694
+ Color as Color5,
1445
1695
  HalfFloatType,
1446
1696
  LinearSRGBColorSpace,
1447
1697
  LoadingManager as LoadingManager2,
@@ -1602,7 +1852,7 @@ var statsData = {
1602
1852
  import {
1603
1853
  BlendFunction as BlendFunction2
1604
1854
  } from "postprocessing";
1605
- import { Color as Color3 } from "three";
1855
+ import { Color as Color4 } from "three";
1606
1856
  import { Pane } from "tweakpane";
1607
1857
  var TweakPane = class {
1608
1858
  constructor(renderer, scene, composer) {
@@ -1868,7 +2118,7 @@ var TweakPane = class {
1868
2118
  return;
1869
2119
  }
1870
2120
  if (e.presetKey === "color") {
1871
- ssaoEffect.color = new Color3().setRGB(e.value.r, e.value.g, e.value.b);
2121
+ ssaoEffect.color = new Color4().setRGB(e.value.r, e.value.g, e.value.b);
1872
2122
  return;
1873
2123
  }
1874
2124
  ssaoEffect[preset] = e.value;
@@ -2232,7 +2482,7 @@ var Composer = class {
2232
2482
  bias: composerValues.ssao.bias,
2233
2483
  fade: composerValues.ssao.fade,
2234
2484
  resolutionScale: composerValues.ssao.resolutionScale,
2235
- color: new Color4().setRGB(composerValues.ssao.color.r, composerValues.ssao.color.g, composerValues.ssao.color.b),
2485
+ color: new Color5().setRGB(composerValues.ssao.color.r, composerValues.ssao.color.g, composerValues.ssao.color.b),
2236
2486
  worldDistanceThreshold: composerValues.ssao.worldDistanceThreshold,
2237
2487
  worldDistanceFalloff: composerValues.ssao.worldDistanceFalloff,
2238
2488
  worldProximityThreshold: composerValues.ssao.worldProximityThreshold,
@@ -2392,11 +2642,11 @@ import {
2392
2642
  getRelativePositionAndRotationRelativeToObject
2393
2643
  } from "mml-web";
2394
2644
  import {
2395
- Color as Color5,
2645
+ Color as Color6,
2396
2646
  DoubleSide,
2397
2647
  Euler,
2398
- FrontSide,
2399
- Mesh as Mesh2,
2648
+ FrontSide as FrontSide2,
2649
+ Mesh as Mesh3,
2400
2650
  MeshStandardMaterial as MeshStandardMaterial2,
2401
2651
  Vector3 as Vector37
2402
2652
  } from "three";
@@ -2451,13 +2701,13 @@ var CollisionsManager = class {
2451
2701
  if (!this.debug) {
2452
2702
  return { source: group, visualizer: null, meshBVH };
2453
2703
  }
2454
- const mergedMesh = new Mesh2(
2704
+ const mergedMesh = new Mesh3(
2455
2705
  newBufferGeometry,
2456
- new MeshStandardMaterial2({ color: 16711680, side: FrontSide, wireframe: true })
2706
+ new MeshStandardMaterial2({ color: 16711680, side: FrontSide2, wireframe: true })
2457
2707
  );
2458
2708
  mergedMesh.geometry.boundsTree = meshBVH;
2459
2709
  const visualizer = new MeshBVHVisualizer(mergedMesh, 3);
2460
- visualizer.edgeMaterial.color = new Color5(255);
2710
+ visualizer.edgeMaterial.color = new Color6(255);
2461
2711
  visualizer.update();
2462
2712
  return { source: group, visualizer, meshBVH };
2463
2713
  }