diff --git a/Playground.png b/Playground.png deleted file mode 100644 index 3d3b12af..00000000 Binary files a/Playground.png and /dev/null differ diff --git a/README.md b/README.md index d972be6c..98ddc448 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,30 @@ # (MML) 3D Web Experience -This repository contains packages used to run a web-based, multi-user 3D web experience that supports -[MML (Metaverse Markup Language)](https://mml.io/). This repository includes two published packages: - -* [`@mml-io/3d-web-client-core`](./packages/3d-web-client-core) - A package that implements the main components of a 3D web experience. -* [`@mml-io/3d-web-user-networking`](./packages/3d-web-user-networking) - A package that contains WebSocket server and client implementations that synchronize user positions. - -There is an example implementation of a 3D web experience in the `examples` directory. This example contains: - -* `web-client` - * A THREE.js 3D experience utilizing the `@mml-io/3d-web-client-core` and `@mml-io/3d-web-user-networking` packages to create a multi-user 3D web client that connects to the server. -* `server` - * A server which serves the `web-client` and handles user networking WebSocket connections with `@mml-io/3d-web-user-networking` - * Additionally, the server runs MML documents in the `mml-documents` directory which are then connected to by the `web-client`. +This repository contains packages used to run a web-based, multi-user 3D web experience that +supports [MML (Metaverse Markup Language)](https://mml.io/). This repository includes two published +packages: + +- [`@mml-io/3d-web-client-core`](./packages/3d-web-client-core) - A package that implements the main + components of a 3D web experience. +- [`@mml-io/3d-web-user-networking`](./packages/3d-web-user-networking) - A package that contains + WebSocket server and client implementations that synchronize user positions. + +There is an example implementation of a 3D web experience in the `examples` directory. This example +contains: + +- `web-client` + - A THREE.js 3D experience utilizing the `@mml-io/3d-web-client-core` and + `@mml-io/3d-web-user-networking` packages to create a multi-user 3D web client that connects to + the server. +- `server` + - A server which serves the `web-client` and handles user networking WebSocket connections with + `@mml-io/3d-web-user-networking` + - Additionally, the server runs MML documents in the `mml-documents` directory which are then + connected to by the `web-client`. It can be easily deployed to environments that support Node.js and expose ports to the internet. - + ## Main features diff --git a/example/assets/models/andor.glb b/example/assets/models/andor.glb deleted file mode 100644 index 01c2f22b..00000000 --- a/example/assets/models/andor.glb +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3715b17ffae5379afeac8598d93dc9d378d909fbd473a8f51c9af51e2fcd6881 -size 7305648 diff --git a/example/assets/models/bot.glb b/example/assets/models/bot.glb new file mode 100644 index 00000000..050e7026 --- /dev/null +++ b/example/assets/models/bot.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:28effaa4bc6aca601e8988d46e8285e4c126107538055bd03962aa99929cb40b +size 2142360 diff --git a/example/local-multi-web-client/src/LocalAvatarClient.ts b/example/local-multi-web-client/src/LocalAvatarClient.ts index 75990a13..5d1706bd 100644 --- a/example/local-multi-web-client/src/LocalAvatarClient.ts +++ b/example/local-multi-web-client/src/LocalAvatarClient.ts @@ -15,11 +15,11 @@ import { MMLWebRunnerClient } from "mml-web-runner"; import { AudioListener, Euler, Scene, Vector3 } from "three"; import hdrUrl from "../../assets/hdr/industrial_sunset_2k.hdr"; -import meshFileUrl from "../../assets/models/andor.glb"; import airAnimationFileUrl from "../../assets/models/anim_air.glb"; import idleAnimationFileUrl from "../../assets/models/anim_idle.glb"; import jogAnimationFileUrl from "../../assets/models/anim_jog.glb"; import sprintAnimationFileUrl from "../../assets/models/anim_run.glb"; +import meshFileUrl from "../../assets/models/bot.glb"; import { LocalAvatarServer } from "./LocalAvatarServer"; import { Room } from "./Room"; diff --git a/example/web-client/src/index.ts b/example/web-client/src/index.ts index 84f1d9f5..3a3be56d 100644 --- a/example/web-client/src/index.ts +++ b/example/web-client/src/index.ts @@ -29,11 +29,11 @@ import { import { AudioListener, Euler, Scene, Vector3 } from "three"; import hdrUrl from "../../assets/hdr/industrial_sunset_2k.hdr"; -import meshFileUrl from "../../assets/models/andor.glb"; import airAnimationFileUrl from "../../assets/models/anim_air.glb"; import idleAnimationFileUrl from "../../assets/models/anim_idle.glb"; import jogAnimationFileUrl from "../../assets/models/anim_jog.glb"; import sprintAnimationFileUrl from "../../assets/models/anim_run.glb"; +import meshFileUrl from "../../assets/models/bot.glb"; import { LoadingScreen } from "./LoadingScreen"; import { Room } from "./Room"; @@ -157,10 +157,19 @@ export class App { this.latestCharacterObject.characterState = characterState; this.networkClient.sendUpdate(characterState); }, + /* + If you want to use an MML Character in the experience, this is where you should + pass it as an argument. + + MML Characters can be loaded by passing a URL that can be fetched and serve your + tag, or just by passing your MML Character description in a string + like in the example below: + ` - + `, + */ ); this.scene.add(this.characterManager.group); diff --git a/packages/3d-web-client-core/src/character/Character.ts b/packages/3d-web-client-core/src/character/Character.ts index cf7c7bbf..bc922fb6 100644 --- a/packages/3d-web-client-core/src/character/Character.ts +++ b/packages/3d-web-client-core/src/character/Character.ts @@ -1,4 +1,4 @@ -import { Color, Group, Object3D, Vector3 } from "three"; +import { Color, Group, MeshStandardMaterial, Object3D, SkinnedMesh, Vector3 } from "three"; import { CameraManager } from "../camera/CameraManager"; import { Composer } from "../rendering/composer"; @@ -32,7 +32,6 @@ export class Character extends Group { private readonly modelLoadedCallback: () => void, private readonly cameraManager: CameraManager, private readonly composer: Composer, - private readonly isLocal: boolean, ) { super(); this.tooltip = new CharacterTooltip(); @@ -41,11 +40,7 @@ export class Character extends Group { } private async load(): Promise { - this.model = new CharacterModel( - this.characterDescription, - this.characterModelLoader, - this.isLocal, - ); + this.model = new CharacterModel(this.characterDescription, this.characterModelLoader); await this.model.init(); this.add(this.model.mesh!); if (this.speakingIndicator === null) { diff --git a/packages/3d-web-client-core/src/character/CharacterManager.ts b/packages/3d-web-client-core/src/character/CharacterManager.ts index 35adc937..98f2b84c 100644 --- a/packages/3d-web-client-core/src/character/CharacterManager.ts +++ b/packages/3d-web-client-core/src/character/CharacterManager.ts @@ -127,7 +127,6 @@ export class CharacterManager { }, this.cameraManager, this.composer, - true, ); const quaternion = new Quaternion().setFromEuler(character.rotation); this.sendUpdate({ @@ -172,7 +171,6 @@ export class CharacterManager { }, this.cameraManager, this.composer, - false, ); this.remoteCharacters.set(id, character); diff --git a/packages/3d-web-client-core/src/character/CharacterMaterial.ts b/packages/3d-web-client-core/src/character/CharacterMaterial.ts index 5883ccca..48c1e7ce 100644 --- a/packages/3d-web-client-core/src/character/CharacterMaterial.ts +++ b/packages/3d-web-client-core/src/character/CharacterMaterial.ts @@ -14,9 +14,11 @@ export class CharacterMaterial extends MeshPhysicalMaterial { public uniforms: Record = {}; public colorsCube216: Color[] = []; - constructor() { + constructor(color?: Color) { super(); - this.color = new Color(0xffffff); + if (color) { + this.color = color; + } this.transmission = characterValues.transmission; this.metalness = characterValues.metalness; this.roughness = characterValues.roughness; @@ -124,8 +126,6 @@ export class CharacterMaterial extends MeshPhysicalMaterial { vec3 grid = vec3(smoothstep(0.01, 0.0, a) * 1.15) * diffuseRandomColor; outgoingLight += grid; #endif - - outgoingLight += smoothstep(0.1, 0.0, scanLines) * 0.1; `, ); }; @@ -134,8 +134,8 @@ export class CharacterMaterial extends MeshPhysicalMaterial { } private generateColorCube() { - const saturation = 0.4; - const lightness = 0.7; + const saturation = 0.5; + const lightness = 0.9; const goldenRatioConjugate = 0.618033988749895; let hue = 0; diff --git a/packages/3d-web-client-core/src/character/CharacterModel.ts b/packages/3d-web-client-core/src/character/CharacterModel.ts index 7276b9fe..165d4e7c 100644 --- a/packages/3d-web-client-core/src/character/CharacterModel.ts +++ b/packages/3d-web-client-core/src/character/CharacterModel.ts @@ -4,10 +4,13 @@ import { AnimationClip, AnimationMixer, Bone, + Color, Group, LoopRepeat, Mesh, + MeshStandardMaterial, Object3D, + SkinnedMesh, } from "three"; import { CharacterDescription } from "./Character"; @@ -27,7 +30,6 @@ export class CharacterModel { constructor( private readonly characterDescription: CharacterDescription, private characterModelLoader: CharacterModelLoader, - private isLocal: boolean, ) {} public async init(): Promise { @@ -54,23 +56,33 @@ export class CharacterModel { this.characterDescription.airAnimationFileUrl, AnimationState.air, ); - } - - public updateAnimation(targetAnimation: AnimationState) { - if (this.currentAnimation !== targetAnimation) { - this.transitionToAnimation(targetAnimation); + if (!this.characterDescription.meshModel) { + this.applyCustomMaterial(this.material); } } - private applyMaterialToAllSkinnedMeshes(material: CharacterMaterial): void { + private applyCustomMaterial(material: CharacterMaterial): void { if (!this.mesh) return; this.mesh.traverse((child: Object3D) => { - if (child.type === "SkinnedMesh") { - (child as Mesh).material = material; + const asSkinnedMesh = child as SkinnedMesh; + if (asSkinnedMesh.isSkinnedMesh) { + const mat = asSkinnedMesh.material as MeshStandardMaterial; + if (!mat.name.includes("joints")) { + asSkinnedMesh.material = material; + } else { + const color = mat.color; + asSkinnedMesh.material = new CharacterMaterial(color); + } } }); } + public updateAnimation(targetAnimation: AnimationState) { + if (this.currentAnimation !== targetAnimation) { + this.transitionToAnimation(targetAnimation); + } + } + private setMainMesh(mainMesh: Object3D, name: string): void { this.mesh = mainMesh; this.mesh.position.set(0, -0.4, 0); diff --git a/packages/3d-web-client-core/src/tweakpane/blades/characterFolder.ts b/packages/3d-web-client-core/src/tweakpane/blades/characterFolder.ts index 43273026..5bb3142f 100644 --- a/packages/3d-web-client-core/src/tweakpane/blades/characterFolder.ts +++ b/packages/3d-web-client-core/src/tweakpane/blades/characterFolder.ts @@ -3,15 +3,15 @@ import { BladeApi, FolderApi, TpChangeEvent } from "tweakpane"; export const characterValues = { transmission: 0.01, - metalness: 0.8, - roughness: 0.05, + metalness: 0.2, + roughness: 0.8, ior: 1.0, thickness: 0.1, specularColor: { r: 1.0, g: 1.0, b: 1.0 }, specularIntensity: 0.1, emissive: { r: 1.0, g: 1.0, b: 1.0 }, - emissiveIntensity: 0.1, - envMapIntensity: 0.9, + emissiveIntensity: 0.01, + envMapIntensity: 0.12, sheenColor: { r: 1.0, g: 1.0, b: 1.0 }, sheen: 0.5, clearcoat: 0.0, diff --git a/packages/3d-web-client-core/src/tweakpane/blades/environmentFolder.ts b/packages/3d-web-client-core/src/tweakpane/blades/environmentFolder.ts index 3b1afc68..1dda4ab1 100644 --- a/packages/3d-web-client-core/src/tweakpane/blades/environmentFolder.ts +++ b/packages/3d-web-client-core/src/tweakpane/blades/environmentFolder.ts @@ -9,7 +9,7 @@ export const sunValues = { sunPolarAngle: -39, }, sunIntensity: 1, - sunColor: { r: 1.0, g: 0.74, b: 0.5 }, + sunColor: { r: 1.0, g: 1.0, b: 1.0 }, }; const sunOptions = { @@ -22,7 +22,7 @@ const sunOptions = { export const envValues = { ambientLight: { - ambientLightIntensity: 0.21, + ambientLightIntensity: 0.05, ambientLightColor: { r: 1, g: 1, b: 1 }, }, fog: { diff --git a/playground.png b/playground.png new file mode 100644 index 00000000..502ce33f Binary files /dev/null and b/playground.png differ