Skip to content

Commit

Permalink
Merge pull request #642 from matrix-org/tp3-refactor
Browse files Browse the repository at this point in the history
TP3 Refactor
  • Loading branch information
robertlong authored Jun 29, 2023
2 parents 3007c04 + b8327f2 commit 3febfae
Show file tree
Hide file tree
Showing 156 changed files with 4,138 additions and 3,821 deletions.
4 changes: 2 additions & 2 deletions src/engine/GameTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export interface NetworkListener {

export interface RemoteResourceManager {
id: string;
ctx: GameState;
ctx: GameContext;
resourceIds: Set<number>;
resourceMap: Map<number, string | ArrayBuffer | RemoteResource>;
gltfCache: Map<string, ResourceManagerGLTFCacheEntry>;
Expand All @@ -64,7 +64,7 @@ export interface RemoteResourceManager {
nextNetworkListenerId: number;
}

export interface GameState extends BaseThreadContext {
export interface GameContext extends BaseThreadContext {
mainToGameTripleBufferFlags: Uint8Array;
renderToGameTripleBufferFlags: Uint8Array;
gameToMainTripleBufferFlags: Uint8Array;
Expand Down
8 changes: 4 additions & 4 deletions src/engine/GameWorker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { maxEntities, tickRate } from "./config.common";
import { InitializeGameWorkerMessage, WorkerMessageType } from "./WorkerMessage";
import { Message, registerModules, Thread } from "./module/module.common";
import gameConfig from "./config.game";
import { GameState, World } from "./GameTypes";
import { GameContext, World } from "./GameTypes";

const workerScope = globalThis as typeof globalThis & Worker;

Expand Down Expand Up @@ -43,7 +43,7 @@ async function onInit({
// noop entity
addEntity(world);

const ctx: GameState = {
const ctx: GameContext = {
thread: Thread.Game,
renderToGameTripleBufferFlags,
mainToGameTripleBufferFlags,
Expand Down Expand Up @@ -156,13 +156,13 @@ async function onInit({
}
}

function timeoutGameLoop(ctx: GameState) {
function timeoutGameLoop(ctx: GameContext) {
// need to call setTimeout immediately, otherwise network jitter ensues
setTimeout(() => timeoutGameLoop(ctx), 1000 / tickRate);
update(ctx);
}

function update(ctx: GameState) {
function update(ctx: GameContext) {
const now = performance.now();
ctx.dt = (now - ctx.elapsed) / 1000;
ctx.elapsed = now;
Expand Down
17 changes: 11 additions & 6 deletions src/engine/MainThread.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,9 @@ import {
RenderQuality,
RenderQualitySetting,
} from "./renderer/renderer.common";
import { createInputRingBuffer, InputRingBuffer } from "./common/InputRingBuffer";

export type MainThreadSystem = (state: IMainThreadContext) => void;

export interface IMainThreadContext extends ConsumerThreadContext {
export interface MainContext extends ConsumerThreadContext {
useOffscreenCanvas: boolean;
mainToGameTripleBufferFlags: Uint8Array;
gameToMainTripleBufferFlags: Uint8Array;
Expand All @@ -35,8 +34,11 @@ export interface IMainThreadContext extends ConsumerThreadContext {
dt: number;
elapsed: number;
quality: RenderQuality;
inputRingBuffer: InputRingBuffer;
}

export type MainThreadSystem = (ctx: MainContext) => void;

async function getSupportedXRSessionModes(): Promise<false | XRSessionMode[]> {
let supportedXRSessionModes: XRSessionMode[] | false = false;

Expand Down Expand Up @@ -114,6 +116,8 @@ export async function MainThread(canvas: HTMLCanvasElement) {
update: () => {},
};

const inputRingBuffer = createInputRingBuffer();

const gameWorker = new GameWorker();
const renderWorker = await initRenderWorker(canvas, gameWorker, useOffscreenCanvas, singleConsumerThreadSharedState);
const interWorkerMessageChannel = new MessageChannel();
Expand All @@ -131,7 +135,7 @@ export async function MainThread(canvas: HTMLCanvasElement) {
const gameToRenderTripleBufferFlags = new Uint8Array(new SharedArrayBuffer(Uint8Array.BYTES_PER_ELEMENT)).fill(0x6);
const gameToMainTripleBufferFlags = new Uint8Array(new SharedArrayBuffer(Uint8Array.BYTES_PER_ELEMENT)).fill(0x6);

const ctx: IMainThreadContext = {
const ctx: MainContext = {
thread: Thread.Main,
mainToGameTripleBufferFlags,
gameToMainTripleBufferFlags,
Expand All @@ -152,6 +156,7 @@ export async function MainThread(canvas: HTMLCanvasElement) {
tick: 0,
singleConsumerThreadSharedState,
quality,
inputRingBuffer,
};

function onWorkerMessage(event: MessageEvent) {
Expand Down Expand Up @@ -267,11 +272,11 @@ async function initRenderWorker(
): Promise<Worker | MockMessagePort> {
if (useOffscreenCanvas) {
console.info("Browser supports OffscreenCanvas, rendering in WebWorker.");
const { default: RenderWorker } = await import("./RenderWorker?worker");
const { default: RenderWorker } = await import("./renderer/RenderWorker?worker");
return new RenderWorker();
} else {
console.info("Browser does not support OffscreenCanvas, rendering on main thread.");
const { default: initRenderWorkerOnMainThread } = await import("./RenderWorker");
const { default: initRenderWorkerOnMainThread } = await import("./renderer/RenderWorker");
return initRenderWorkerOnMainThread(canvas, gameWorker, singleConsumerThreadSharedState!);
}
}
6 changes: 3 additions & 3 deletions src/engine/MainThreadTripleBufferSystems.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { swapReadBufferFlags, swapWriteBufferFlags } from "./allocator/TripleBuffer";
import { IMainThreadContext } from "./MainThread";
import { MainContext } from "./MainThread";

export function IncomingMainThreadTripleBufferSystem(ctx: IMainThreadContext) {
export function IncomingMainThreadTripleBufferSystem(ctx: MainContext) {
swapReadBufferFlags(ctx.gameToMainTripleBufferFlags);
}

export function OutgoingMainThreadTripleBufferSystem(ctx: IMainThreadContext) {
export function OutgoingMainThreadTripleBufferSystem(ctx: MainContext) {
swapWriteBufferFlags(ctx.mainToGameTripleBufferFlags);
}
53 changes: 26 additions & 27 deletions src/engine/animation/animation.game.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import RAPIER, { Capsule } from "@dimforge/rapier3d-compat";
import { defineQuery, exitQuery, hasComponent } from "bitecs";
import { defineQuery, exitQuery } from "bitecs";
import { vec3 } from "gl-matrix";
import { AnimationAction, AnimationMixer, Bone, Object3D, Quaternion, Vector3 } from "three";
import { radToDeg } from "three/src/math/MathUtils";

import { getForwardVector, getPitch, getRightVector, getYaw } from "../component/math";
import { getForwardVector, getPitch, getRightVector, getYaw } from "../common/math";
import { maxEntities } from "../config.common";
import { GameState } from "../GameTypes";
import { GameContext } from "../GameTypes";
import { getModule } from "../module/module.common";
import { Networked, Owned } from "../network/NetworkComponents";
import { PhysicsModule, PhysicsModuleState, RigidBody } from "../physics/physics.game";
import { PhysicsModule, PhysicsModuleState } from "../physics/physics.game";
import { getRemoteResource, removeResourceRef } from "../resource/resource.game";
import { RemoteAnimation, RemoteNode } from "../resource/RemoteResources";
import { playerShapeCastCollisionGroups } from "../physics/CollisionGroups";
Expand Down Expand Up @@ -45,14 +44,13 @@ const exitAnimationQuery = exitQuery(animationQuery);
const boneQuery = defineQuery([BoneComponent]);
const exitBoneQuery = exitQuery(boneQuery);

export function AnimationSystem(ctx: GameState) {
export function AnimationSystem(ctx: GameContext) {
disposeAnimations(ctx);
disposeBones(ctx);
processAnimations(ctx);
syncBones(ctx);
}

const _vel = vec3.create();
const _forward = vec3.create();
const _right = vec3.create();

Expand All @@ -72,7 +70,7 @@ const shapeCastPosition = new Vector3();
const shapeCastRotation = new Quaternion();
const _obj = new Object3D();

const isGrounded = (ctx: GameState, physicsWorld: RAPIER.World, body: RAPIER.RigidBody) => {
const isGrounded = (ctx: GameContext, physicsWorld: RAPIER.World, body: RAPIER.RigidBody) => {
shapeCastPosition.copy(body.translation() as Vector3).add(shapeTranslationOffset);
shapeCastRotation.copy(_obj.quaternion).multiply(shapeRotationOffset);

Expand All @@ -92,7 +90,7 @@ const isGrounded = (ctx: GameState, physicsWorld: RAPIER.World, body: RAPIER.Rig
return isGrounded;
};

function processAnimations(ctx: GameState) {
function processAnimations(ctx: GameContext) {
const physics = getModule(ctx, PhysicsModule);
const ents = animationQuery(ctx.world);
for (let i = 0; i < ents.length; i++) {
Expand All @@ -108,14 +106,14 @@ function processAnimations(ctx: GameState) {
continue;
}

const rigidBody = RigidBody.store.get(parent.eid);
const body = parent.physicsBody?.body;

if (animation && rigidBody) {
if (animation && body) {
// collectively fade all animations out each frame
reduceClipActionWeights(animation.actions, fadeOutAmount * ctx.dt);

// select actions to play based on velocity
const actionsToPlay = getClipActionsUsingVelocity(ctx, physics, parent, rigidBody, animation);
const actionsToPlay = getClipActionsUsingVelocity(ctx, physics, parent, body, animation);
// synchronize selected clip action times
synchronizeClipActions(actionsToPlay);
// fade in selected animations
Expand All @@ -127,7 +125,7 @@ function processAnimations(ctx: GameState) {
return ctx;
}

function syncBones(ctx: GameState) {
function syncBones(ctx: GameContext) {
// sync bone positions
const bones = boneQuery(ctx.world);
for (let i = 0; i < bones.length; i++) {
Expand Down Expand Up @@ -180,29 +178,30 @@ function increaseClipActionWeights(actions: AnimationAction[], amount: number) {
}

function getClipActionsUsingVelocity(
ctx: GameState,
ctx: GameContext,
physics: PhysicsModuleState,
node: RemoteNode,
rigidBody: RAPIER.RigidBody,
body: RAPIER.RigidBody,
animation: IAnimationComponent
): AnimationAction[] {
const eid = node.eid;
const quaternion = node.quaternion;

// if remote object, take velocity from Networked component
// otherwise, take velocity from entity's RigidBody
const remote = hasComponent(ctx.world, Networked, eid) && !hasComponent(ctx.world, Owned, eid);
const linvel = remote ? new Vector3().fromArray(Networked.velocity[eid]) : rigidBody.linvel();
const vel = remote ? vec3.copy(_vel, Networked.velocity[eid]) : vec3.set(_vel, linvel.x, linvel.y, linvel.z);
const totalSpeed = linvel.x ** 2 + linvel.z ** 2;
if (!node.physicsBody) {
throw new Error(`Physics body not found on node ${node.eid}`);
}

const linvel = node.physicsBody.velocity;

const totalSpeed = linvel[0] ** 2 + linvel[2] ** 2;

const pitch = getPitch(quaternion);
const roll = getYaw(quaternion);
const forward = getForwardVector(_forward, pitch, roll);
const right = getRightVector(_right, roll);

const angle = radToDeg(vec3.angle(vel, forward));
const angle2 = radToDeg(vec3.angle(vel, right));
const angle = radToDeg(vec3.angle(linvel, forward));
const angle2 = radToDeg(vec3.angle(linvel, right));

const movingForward = angle < 50;
const movingBackward = angle > 120;
Expand All @@ -217,12 +216,12 @@ function getClipActionsUsingVelocity(
lastYrot[eid] = yRot;
}

const jumping = !isGrounded(ctx, physics.physicsWorld, rigidBody);
const jumping = !isGrounded(ctx, physics.physicsWorld, body);

// choose clip based on velocity
const actions: AnimationAction[] = [];

if (linvel.y < -20) {
if (linvel[1] < -20) {
actions.push(animation.actionMap.get(AnimationClipType.Fall2)!);
} else if (jumping) {
actions.push(animation.actionMap.get(AnimationClipType.Fall1)!);
Expand Down Expand Up @@ -279,7 +278,7 @@ function getClipActionsUsingVelocity(
return actions;
}

function disposeAnimations(ctx: GameState) {
function disposeAnimations(ctx: GameContext) {
const entities = exitAnimationQuery(ctx.world);

for (let i = 0; i < entities.length; i++) {
Expand All @@ -296,7 +295,7 @@ function disposeAnimations(ctx: GameState) {
}
}

function disposeBones(ctx: GameState) {
function disposeBones(ctx: GameContext) {
const entities = exitBoneQuery(ctx.world);

for (let i = 0; i < entities.length; i++) {
Expand Down
16 changes: 8 additions & 8 deletions src/engine/audio/audio.game.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { GameState } from "../GameTypes";
import { GameContext } from "../GameTypes";
import { RemoteAudioSource } from "../resource/RemoteResources";
import { defineModule, getModule, Thread } from "../module/module.common";
import { AudioAction, AudioPlaybackRingBuffer, enqueueAudioPlaybackRingBuffer } from "./AudioPlaybackRingBuffer";
Expand All @@ -14,7 +14,7 @@ interface GameAudioModule {
analyserTripleBuffer: AudioAnalyserTripleBuffer;
}

export const AudioModule = defineModule<GameState, GameAudioModule>({
export const AudioModule = defineModule<GameContext, GameAudioModule>({
name: "audio",
async create(ctx, { waitForMessage }) {
const { audioPlaybackRingBuffer, analyserTripleBuffer } = await waitForMessage<InitializeAudioStateMessage>(
Expand All @@ -31,7 +31,7 @@ export const AudioModule = defineModule<GameState, GameAudioModule>({
});

function enqueueAudioPlaybackItem(
ctx: GameState,
ctx: GameContext,
action: AudioAction,
audioSource: RemoteAudioSource,
gain: number,
Expand All @@ -55,22 +55,22 @@ function enqueueAudioPlaybackItem(
}
}

export function playAudio(ctx: GameState, audioSource: RemoteAudioSource, time?: number) {
export function playAudio(ctx: GameContext, audioSource: RemoteAudioSource, time?: number) {
enqueueAudioPlaybackItem(ctx, AudioAction.Play, audioSource, 1, 1, time || 0);
}

export function playOneShotAudio(ctx: GameState, audioSource: RemoteAudioSource, gain = 1, playbackRate = 1) {
export function playOneShotAudio(ctx: GameContext, audioSource: RemoteAudioSource, gain = 1, playbackRate = 1) {
enqueueAudioPlaybackItem(ctx, AudioAction.PlayOneShot, audioSource, gain, playbackRate, 0);
}

export function pauseAudio(ctx: GameState, audioSource: RemoteAudioSource) {
export function pauseAudio(ctx: GameContext, audioSource: RemoteAudioSource) {
enqueueAudioPlaybackItem(ctx, AudioAction.Pause, audioSource, 1, 1, 0);
}

export function seekAudio(ctx: GameState, audioSource: RemoteAudioSource, time: number) {
export function seekAudio(ctx: GameContext, audioSource: RemoteAudioSource, time: number) {
enqueueAudioPlaybackItem(ctx, AudioAction.Seek, audioSource, 1, 1, time);
}

export function stopAudio(ctx: GameState, audioSource: RemoteAudioSource) {
export function stopAudio(ctx: GameContext, audioSource: RemoteAudioSource) {
enqueueAudioPlaybackItem(ctx, AudioAction.Stop, audioSource, 1, 1, 0);
}
Loading

1 comment on commit 3febfae

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.