Skip to content

Commit

Permalink
Updatable client config
Browse files Browse the repository at this point in the history
  • Loading branch information
MarcusLongmuir committed Aug 2, 2024
1 parent 45912e3 commit 30a3d56
Show file tree
Hide file tree
Showing 12 changed files with 371 additions and 169 deletions.
7 changes: 5 additions & 2 deletions example/multi-user-3d-web-experience/client/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,12 @@ const app = new Networked3dWebExperienceClient(holder, {
sprintAnimationFileUrl,
doubleJumpAnimationFileUrl,
},
skyboxHdrJpgUrl: hdrJpgUrl,
mmlDocuments: [{ url: `${protocol}//${host}/mml-documents/example-mml.html` }],
environmentConfiguration: {},
environmentConfiguration: {
skybox: {
hdrJpgUrl: hdrJpgUrl,
},
},
avatarConfiguration: {
availableAvatars: [],
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { createRef, forwardRef } from "react";
import { flushSync } from "react-dom";
import { createRoot, Root } from "react-dom/client";

import { AvatarType } from "./AvatarType";
import { AvatarConfiguration, AvatarType } from "./AvatarType";
import { AvatarSelectionUIComponent } from "./components/AvatarPanel/AvatarSectionUIComponent";

const ForwardedAvatarSelectionUIComponent = forwardRef(AvatarSelectionUIComponent);
Expand All @@ -14,12 +14,9 @@ export type CustomAvatar = AvatarType & {

export type AvatarSelectionUIProps = {
holderElement: HTMLElement;
clientId: number;
visibleByDefault?: boolean;
availableAvatars: Array<AvatarType>;
sendMessageToServerMethod: (avatar: CustomAvatar) => void;
enableCustomAvatar?: boolean;
};
} & AvatarConfiguration;

export class AvatarSelectionUI {
private root: Root;
Expand All @@ -36,15 +33,23 @@ export class AvatarSelectionUI {
this.config.sendMessageToServerMethod(avatar);
};

public updateAvatarConfig(avatarConfig: AvatarConfiguration) {
this.config = {
...this.config,
...avatarConfig,
};
this.init();
}

init() {
flushSync(() =>
this.root.render(
<ForwardedAvatarSelectionUIComponent
ref={this.appRef}
onUpdateUserAvatar={this.onUpdateUserAvatar}
visibleByDefault={false}
visibleByDefault={this.config.visibleByDefault}
availableAvatars={this.config.availableAvatars}
enableCustomAvatar={this.config.enableCustomAvatar}
allowCustomAvatars={this.config.allowCustomAvatars}
/>,
),
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,8 @@ export type AvatarType = {
mmlCharacterUrl: string;
}
);

export type AvatarConfiguration = {
availableAvatars: Array<AvatarType>;
allowCustomAvatars?: boolean;
};
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ type AvatarSelectionUIProps = {
onUpdateUserAvatar: (avatar: AvatarType) => void;
visibleByDefault?: boolean;
availableAvatars: AvatarType[];
enableCustomAvatar?: boolean;
allowCustomAvatars?: boolean;
};

enum CustomAvatarType {
Expand Down Expand Up @@ -92,6 +92,10 @@ export const AvatarSelectionUIComponent: ForwardRefRenderFunction<any, AvatarSel
}
};

if (!props.availableAvatars.length && !props.allowCustomAvatars) {
return null;
}

return (
<>
<div className={styles.menuButton} onClick={handleRootClick}>
Expand Down Expand Up @@ -146,7 +150,7 @@ export const AvatarSelectionUIComponent: ForwardRefRenderFunction<any, AvatarSel
</div>
</div>
)}
{props.enableCustomAvatar && (
{props.allowCustomAvatars && (
<div className={styles.customAvatarSection}>
{!!props.availableAvatars.length && <hr />}
<h2>Custom Avatar Section</h2>
Expand Down
2 changes: 1 addition & 1 deletion packages/3d-web-avatar-selection-ui/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export * from "./avatar-selection-ui/AvatarSelectionUI";
export { AvatarType } from "./avatar-selection-ui/AvatarType";
export * from "./avatar-selection-ui/AvatarType";
3 changes: 3 additions & 0 deletions packages/3d-web-client-core/src/mml/MMLCompositionScene.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
ChatProbe,
LoadingProgressManager,
LinkProps,
MMLDocumentTimeManager,
} from "mml-web";
import { AudioListener, Group, Object3D, PerspectiveCamera, Scene, WebGLRenderer } from "three";

Expand All @@ -30,6 +31,7 @@ export class MMLCompositionScene {
public group: Group;

public readonly mmlScene: IMMLScene;
public readonly documentTimeManager: MMLDocumentTimeManager;
private readonly promptManager: PromptManager;
private readonly interactionManager: InteractionManager;
private readonly interactionListener: InteractionListener;
Expand All @@ -49,6 +51,7 @@ export class MMLCompositionScene {
this.interactionManager = interactionManager;
this.interactionListener = interactionListener;
this.loadingProgressManager = new LoadingProgressManager();
this.documentTimeManager = new MMLDocumentTimeManager();

this.mmlScene = {
getAudioListener: () => this.config.audioListener,
Expand Down
182 changes: 120 additions & 62 deletions packages/3d-web-client-core/src/rendering/composer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
Scene,
ShadowMapType,
SRGBColorSpace,
Texture,
ToneMapping,
Vector2,
WebGLRenderer,
Expand Down Expand Up @@ -65,7 +66,14 @@ export type EnvironmentConfiguration = {
blurriness?: number;
azimuthalAngle?: number;
polarAngle?: number;
};
} & (
| {
hdrJpgUrl: string;
}
| {
hdrUrl: string;
}
);
envMap?: {
intensity?: number;
};
Expand All @@ -88,8 +96,6 @@ export class Composer {
private resizeListener: () => void;
public resolution: Vector2 = new Vector2(this.width, this.height);

private isEnvHDRI: boolean = false;

private readonly scene: Scene;
public postPostScene: Scene;
private readonly camera: PerspectiveCamera;
Expand Down Expand Up @@ -123,6 +129,14 @@ export class Composer {
private ambientLight: AmbientLight | null = null;
private environmentConfiguration?: EnvironmentConfiguration;

private skyboxState: {
src: {
hdrJpgUrl?: string;
hdrUrl?: string;
};
latestPromise: Promise<unknown> | null;
} = { src: {}, latestPromise: null };

public sun: Sun | null = null;
public spawnSun: boolean;

Expand Down Expand Up @@ -264,6 +278,14 @@ export class Composer {
this.scene.add(this.sun);
}

if (this.environmentConfiguration?.skybox) {
if ("hdrJpgUrl" in this.environmentConfiguration.skybox) {
this.useHDRJPG(this.environmentConfiguration.skybox.hdrJpgUrl);
} else if ("hdrUrl" in this.environmentConfiguration.skybox) {
this.useHDRI(this.environmentConfiguration.skybox.hdrUrl);
}
}

this.updateSunValues();

this.resizeListener = () => {
Expand All @@ -273,6 +295,22 @@ export class Composer {
this.fitContainer();
}

public updateEnvironmentConfiguration(environmentConfiguration: EnvironmentConfiguration) {
this.environmentConfiguration = environmentConfiguration;

if (environmentConfiguration.skybox) {
if ("hdrJpgUrl" in environmentConfiguration.skybox) {
this.useHDRJPG(environmentConfiguration.skybox.hdrJpgUrl);
} else if ("hdrUrl" in environmentConfiguration.skybox) {
this.useHDRI(environmentConfiguration.skybox.hdrUrl);
}
}

this.updateSkyboxAndEnvValues();
this.updateAmbientLightValues();
this.updateSunValues();
}

public setupTweakPane(tweakPane: TweakPane) {
tweakPane.setupRenderPane(
this.effectComposer,
Expand Down Expand Up @@ -368,74 +406,76 @@ export class Composer {
);
}

public useHDRJPG(url: string, fromFile: boolean = false): void {
const pmremGenerator = new PMREMGenerator(this.renderer);
const hdrJpg = new HDRJPGLoader(this.renderer).load(url, () => {
const hdrJpgEquirectangularMap = hdrJpg.renderTarget.texture;

hdrJpgEquirectangularMap.mapping = EquirectangularReflectionMapping;
hdrJpgEquirectangularMap.needsUpdate = true;

const envMap = pmremGenerator!.fromEquirectangular(hdrJpgEquirectangularMap).texture;
if (envMap) {
envMap.colorSpace = LinearSRGBColorSpace;
envMap.needsUpdate = true;
this.scene.environment = envMap;
this.scene.environmentIntensity = envValues.envMapIntensity;
this.scene.environmentRotation = new Euler(
MathUtils.degToRad(envValues.skyboxPolarAngle),
MathUtils.degToRad(envValues.skyboxAzimuthalAngle),
0,
);
this.scene.background = envMap;
this.scene.backgroundIntensity = envValues.skyboxIntensity;
this.scene.backgroundBlurriness = envValues.skyboxBlurriness;
this.scene.backgroundRotation = new Euler(
MathUtils.degToRad(envValues.skyboxPolarAngle),
MathUtils.degToRad(envValues.skyboxAzimuthalAngle),
0,
);
this.isEnvHDRI = true;
private async loadHDRJPG(url: string): Promise<Texture> {
return new Promise((resolve, reject) => {
const pmremGenerator = new PMREMGenerator(this.renderer);
const hdrJpg = new HDRJPGLoader(this.renderer).load(url, () => {
const hdrJpgEquirectangularMap = hdrJpg.renderTarget.texture;
hdrJpgEquirectangularMap.mapping = EquirectangularReflectionMapping;
hdrJpgEquirectangularMap.needsUpdate = true;

const envMap = pmremGenerator!.fromEquirectangular(hdrJpgEquirectangularMap).texture;
hdrJpgEquirectangularMap.dispose();
pmremGenerator!.dispose();
}

hdrJpg.dispose();
hdrJpg.dispose();
if (envMap) {
envMap.colorSpace = LinearSRGBColorSpace;
envMap.needsUpdate = true;
resolve(envMap);
} else {
reject("Failed to generate environment map");
}
});
});
}

public useHDRI(url: string, fromFile: boolean = false): void {
if ((this.isEnvHDRI && fromFile === false) || !this.renderer) {
return;
}
const pmremGenerator = new PMREMGenerator(this.renderer);
new RGBELoader(new LoadingManager()).load(
url,
(texture) => {
private async loadHDRi(url: string): Promise<Texture> {
return new Promise((resolve, reject) => {
const pmremGenerator = new PMREMGenerator(this.renderer);
new RGBELoader(new LoadingManager()).load(url, (texture) => {
const envMap = pmremGenerator!.fromEquirectangular(texture).texture;
texture.dispose();
pmremGenerator!.dispose();
if (envMap) {
envMap.colorSpace = LinearSRGBColorSpace;
envMap.needsUpdate = true;
this.scene.environment = envMap;
this.scene.environmentIntensity = envValues.envMapIntensity;
this.scene.environmentRotation = new Euler(
MathUtils.degToRad(envValues.skyboxPolarAngle),
MathUtils.degToRad(envValues.skyboxAzimuthalAngle),
0,
);
this.scene.background = envMap;
this.scene.backgroundIntensity = envValues.skyboxIntensity;
this.scene.backgroundBlurriness = envValues.skyboxBlurriness;
this.isEnvHDRI = true;
texture.dispose();
pmremGenerator!.dispose();
resolve(envMap);
} else {
reject("Failed to generate environment map");
}
},
() => {},
(error: ErrorEvent) => {
console.error(`Can't load ${url}: ${JSON.stringify(error)}`);
},
);
});
});
}

public useHDRJPG(url: string, fromFile: boolean = false): void {
if (this.skyboxState.src.hdrJpgUrl === url) {
return;
}

const hdrJPGPromise = this.loadHDRJPG(url);
this.skyboxState.src = { hdrJpgUrl: url };
this.skyboxState.latestPromise = hdrJPGPromise;
hdrJPGPromise.then((envMap) => {
if (this.skyboxState.latestPromise !== hdrJPGPromise) {
return;
}
this.applyEnvMap(envMap);
});
}

public useHDRI(url: string): void {
if (this.skyboxState.src.hdrUrl === url) {
return;
}
const hdrPromise = this.loadHDRi(url);
this.skyboxState.src = { hdrUrl: url };
this.skyboxState.latestPromise = hdrPromise;
hdrPromise.then((envMap) => {
if (this.skyboxState.latestPromise !== hdrPromise) {
return;
}
this.applyEnvMap(envMap);
});
}

public setHDRIFromFile(): void {
Expand All @@ -453,7 +493,7 @@ export class Composer {
const fileURL = URL.createObjectURL(file);
if (fileURL) {
if (extension === "hdr") {
this.useHDRI(fileURL, true);
this.useHDRI(fileURL);
} else if (extension === "jpg") {
this.useHDRJPG(fileURL);
} else {
Expand Down Expand Up @@ -542,4 +582,22 @@ export class Composer {
}
this.setAmbientLight();
}

private applyEnvMap(envMap: Texture) {
this.scene.environment = envMap;
this.scene.environmentIntensity = envValues.envMapIntensity;
this.scene.environmentRotation = new Euler(
MathUtils.degToRad(envValues.skyboxPolarAngle),
MathUtils.degToRad(envValues.skyboxAzimuthalAngle),
0,
);
this.scene.background = envMap;
this.scene.backgroundIntensity = envValues.skyboxIntensity;
this.scene.backgroundBlurriness = envValues.skyboxBlurriness;
this.scene.backgroundRotation = new Euler(
MathUtils.degToRad(envValues.skyboxPolarAngle),
MathUtils.degToRad(envValues.skyboxAzimuthalAngle),
0,
);
}
}
Loading

0 comments on commit 30a3d56

Please sign in to comment.