Skip to content

Commit

Permalink
Packaging (#126) (#127)
Browse files Browse the repository at this point in the history
* Packaging

* allows the user to load a compressed JPG HDR from Tweakpane

* fixes depth texture size recomputing issue when toggling SSAO on/off

---------

Co-authored-by: Thomas Bergmueller <[email protected]>
Co-authored-by: TheCodeTherapy <[email protected]>
  • Loading branch information
3 people authored Apr 26, 2024
1 parent a61f041 commit b2c739e
Show file tree
Hide file tree
Showing 119 changed files with 3,336 additions and 2,625 deletions.
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v18.16.0
v20.11.1
80 changes: 47 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,41 +1,51 @@
# (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`.
- A simple session-based [auth system](#auth) that can be used as a reference to implement arbitrary http-based auth.

It can be easily deployed to environments that support Node.js and expose ports to the internet.
supports [MML (Metaverse Markup Language)](https://mml.io/).

It can be easily deployed to environments that support Node.js and can expose ports to the internet.

<img src="./playground.jpg">

## Packages

This repository includes the following published packages:

- [`@mml-io/3d-web-experience-client`](./packages/3d-web-experience-client)
- Client for a 3D web experience that includes user position networking, MML content composition,
MML-based avatars, and text chat.
- [`@mml-io/3d-web-experience-server`](./packages/3d-web-experience-server)
- Server for a 3D web experience that includes user position networking, MML hosting, and text chat.
- [`@mml-io/3d-web-client-core`](./packages/3d-web-client-core)
- The main components of a 3D web experience (controls, rendering, MML composition etc.) that can be
extended with other packages to create a full 3D web experience.
- [`@mml-io/3d-web-user-networking`](./packages/3d-web-user-networking)
- WebSocket server and client implementations that synchronize user positions.
- [`@mml-io/3d-web-avatar`](./packages/3d-web-avatar)
- Creates and parses MML documents for avatars (using `m-character`).
- [`@mml-io/3d-web-avatar-editor-ui`](./packages/3d-web-avatar-editor-ui)
- UI components (e.g. parts pickers) for creating avatars.
- [`@mml-io/3d-web-standalone-avatar-editor`](./packages/3d-web-standalone-avatar-editor)
- An MML avatar editor (using `m-character`).
- [`@mml-io/3d-web-text-chat`](./packages/3d-web-text-chat)
- Contains WebSocket server and client implementations for text chat.
- [`@mml-io/3d-web-voice-chat`](./packages/3d-web-voice-chat)
- Client implementation for spatial voice chat.

## Main features

- Multiple users can connect to the experience using just a web browser.

- Users can interact simultaneously with the stateful MML documents.

- Easy to deploy and extend with interactive MML content.

## Running locally
### Auth Flow
- When the client page is rendered by the server, the server uses a UserAuthenticator implementation to determine if a session should be generated for the incoming http request and if so includes that session token on the client page.
- The client then sends the session token in the first message to the server when it connects via websocket.
- The server can use the session token to authenticate the user and determine what identity (username, avatar etc) the user should have.
- An example implementation of this is provided in the example server, but the interface is extensible enough that a more complex user authenticator can limit which avatar components should be permitted based on external systems.


## Running Examples & Iterating Locally

Making sure you have Node.js installed, run the following from the root of the repository:

Expand All @@ -44,10 +54,14 @@ npm install
npm run iterate
```

Once the example server is running, open `http://localhost:8080` in your browser.

## Auth
- When the client page is rendered by the server the server uses a UserAuthenticator implementation to determine if a session should be generated for the incoming http request and if so includes that session token on the client page.
- The client then sends the session token in the first message to the server when it connects via websocket.
- The server can use the session token to authenticate the user and determine what identity (username, avatar etc) the user should have.
- An example implementation of this is provided in the example server, but the interface is extensible enough that a more complex user authenticator can limit which avatar components should be permitted based on external systems.
## Examples

- [`example/multi-user-3d-web-experience`](./example/multi-user-3d-web-experience)
- Once the server is running (see [above](#running-examples--iterating-locally)), open `http://localhost:8080`.
- A client and server pair of packages that uses the `@mml-io/3d-web-experience-client` and `@mml-io/3d-web-experience-server` packages to create a multi-user 3d web experience that includes MML hosting and text chat.
- [`example/local-only-multi-user-3d-web-experience`](./example/local-only-multi-user-3d-web-experience)
- Once the server is running (see [above](#running-examples--iterating-locally)), open `http://localhost:8081`.
- A client that uses the various packages to create a 3d web experience that only works locally. No server is needed, but there is a server to serve the client.
- [`example/web-avatar-editor`](./example/web-avatar-editor)
- Once the server is running (see [above](#running-examples--iterating-locally)), open `http://localhost:8082`.
- An avatar editor that uses the `@mml-io/3d-web-standalone-avatar-editor` to create and edit MML avatars and a simple Express server that hosts the editor.
Binary file added example/assets/hdr/puresky_2k.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions example/assets/models/anim_air.glb
Git LFS file not shown
4 changes: 2 additions & 2 deletions example/assets/models/anim_idle.glb
Git LFS file not shown
4 changes: 2 additions & 2 deletions example/assets/models/anim_jog.glb
Git LFS file not shown
4 changes: 2 additions & 2 deletions example/assets/models/anim_run.glb
Git LFS file not shown
4 changes: 2 additions & 2 deletions example/assets/models/bot.glb
Git LFS file not shown
Binary file removed example/assets/textures/checker.png
Binary file not shown.
48 changes: 0 additions & 48 deletions example/local-multi-web-client/src/Room.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ const buildOptions: esbuild.BuildOptions = {
".glb": "file",
".hdr": "file",
},
outbase: "../",
outbase: "../../", // This is targeting the parent of the "assets" directory to avoid generated paths including a traversal
sourceRoot: "./src",
publicPath: "/local-multi-web-client/",
publicPath: "/",
plugins: [
copy({
resolveFrom: "cwd",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "@example/web-client",
"name": "@example/local-only-multi-user-3d-web-experience-client",
"private": true,
"version": "0.15.0",
"files": [
Expand All @@ -18,9 +18,10 @@
"@mml-io/3d-web-text-chat": "^0.15.0",
"@mml-io/3d-web-user-networking": "^0.15.0",
"@mml-io/3d-web-voice-chat": "^0.15.0",
"three": "0.153.0"
"mml-web-runner": "0.14.0",
"three": "0.163.0"
},
"devDependencies": {
"@types/three": "0.153.0"
"@types/three": "0.163.0"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg" href="/local-multi-web-client/favicon.svg" />
<link rel="icon" type="image/svg" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>MML 3D Web Experience</title>
<link rel="stylesheet" href="/local-multi-web-client/style.css" />
<link rel="stylesheet" href="/style.css" />
</head>
<body>
<div id="app"></div>
<div id="text-chat-ui"></div>
<div id="voice-chat-ui"></div>
<script type="application/javascript" src="/local-multi-web-client/index.js"></script>
<script type="application/javascript" src="/index.js"></script>
</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
CharacterState,
CollisionsManager,
Composer,
GroundPlane,
KeyInputManager,
MMLCompositionScene,
TimeManager,
Expand All @@ -15,15 +16,14 @@ import { EditableNetworkedDOM, NetworkedDOM } from "@mml-io/networked-dom-docume
import { MMLWebRunnerClient } from "mml-web-runner";
import { AudioListener, Euler, Scene, Vector3 } from "three";

import hdrUrl from "../../assets/hdr/puresky_2k.hdr";
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 defaultAvatarMeshFileUrl from "../../assets/models/bot.glb";
import hdrJpgUrl from "../../../assets/hdr/puresky_2k.jpg";
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 defaultAvatarMeshFileUrl from "../../../assets/models/bot.glb";

import { LocalAvatarServer } from "./LocalAvatarServer";
import { Room } from "./Room";

const animationConfig: AnimationConfig = {
airAnimationFileUrl,
Expand Down Expand Up @@ -90,7 +90,7 @@ export class LocalAvatarClient {
this.cameraManager.camera.add(this.audioListener);

this.composer = new Composer(this.scene, this.cameraManager.camera, true);
this.composer.useHDRI(hdrUrl);
this.composer.useHDRJPG(hdrJpgUrl);
this.element.appendChild(this.composer.renderer.domElement);

this.resizeObserver = new ResizeObserver(() => {
Expand All @@ -109,40 +109,40 @@ export class LocalAvatarClient {
},
);

this.characterManager = new CharacterManager(
this.composer,
this.characterModelLoader,
this.collisionsManager,
this.cameraManager,
this.timeManager,
this.keyInputManager,
this.remoteUserStates,
(characterState: CharacterState) => {
this.characterManager = new CharacterManager({
composer: this.composer,
characterModelLoader: this.characterModelLoader,
collisionsManager: this.collisionsManager,
cameraManager: this.cameraManager,
timeManager: this.timeManager,
keyInputManager: this.keyInputManager,
remoteUserStates: this.remoteUserStates,
sendUpdate: (characterState: CharacterState) => {
localAvatarServer.send(localClientId, characterState);
},
animationConfig,
() => {
characterResolve: () => {
return { username: "User", characterDescription };
},
);
});
this.scene.add(this.characterManager.group);

this.mmlComposition = new MMLCompositionScene(
this.element,
this.composer.renderer,
this.scene,
this.cameraManager.camera,
this.audioListener,
this.collisionsManager,
() => {
this.mmlComposition = new MMLCompositionScene({
targetElement: this.element,
renderer: this.composer.renderer,
scene: this.scene,
camera: this.cameraManager.camera,
audioListener: this.audioListener,
collisionsManager: this.collisionsManager,
getUserPositionAndRotation: () => {
return this.characterManager.getLocalCharacterPositionAndRotation();
},
);
});
this.scene.add(this.mmlComposition.group);

const room = new Room();
this.collisionsManager.addMeshesGroup(room);
this.scene.add(room);
const groundPlane = new GroundPlane();
this.collisionsManager.addMeshesGroup(groundPlane);
this.scene.add(groundPlane);

this.characterManager.spawnLocalCharacter(
localClientId,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<m-light type="point" y="50" intensity="0.5"></m-light>
<m-light type="point" y="10" intensity="100"></m-light>
<m-cube color="red" x="-3" y="1.5" z="-5">
<m-attr-anim attr="ry" start="0" end="360" duration="5000"></m-attr-anim>
</m-cube>
Expand Down Expand Up @@ -41,4 +41,4 @@
});
stairs.append(stair);
}
</script>
</script>
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"extends": "../../tsconfig.json",
"extends": "../../../tsconfig.json",
"compilerOptions": {
"noImplicitAny": true,
"module": "esnext",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as esbuild from "esbuild";

import { rebuildOnDependencyChangesPlugin } from "../../utils/rebuildOnDependencyChangesPlugin";
import { rebuildOnDependencyChangesPlugin } from "../../../utils/rebuildOnDependencyChangesPlugin";

const buildMode = "--build";
const watchMode = "--watch";
Expand Down
Loading

0 comments on commit b2c739e

Please sign in to comment.