Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate billboard to bit-ecs #5901

Merged
merged 9 commits into from
Jan 23, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/aframe-to-bit-components.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import {
RemoteHoverTarget,
NotRemoteHoverTarget,
RemoveNetworkedEntityButton,
DestroyAtExtremeDistance
DestroyAtExtremeDistance,
Billboard
} from "./bit-components";

[
Expand All @@ -18,7 +19,8 @@ import {
["is-remote-hover-target", RemoteHoverTarget],
["is-not-remote-hover-target", NotRemoteHoverTarget],
["remove-networked-object-button", RemoveNetworkedEntityButton],
["destroy-at-extreme-distances", DestroyAtExtremeDistance]
["destroy-at-extreme-distances", DestroyAtExtremeDistance],
["billboard", Billboard]
keianhzo marked this conversation as resolved.
Show resolved Hide resolved
].forEach(([aframeComponentName, bitecsComponent]) => {
AFRAME.registerComponent(aframeComponentName, {
init: function () {
Expand Down
4 changes: 3 additions & 1 deletion src/app.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import * as bitecs from "bitecs";
import { addEntity, createWorld, IWorld } from "bitecs";
import "./aframe-to-bit-components";
import { AEntity, Networked, Object3DTag, Owned } from "./bit-components";
Expand All @@ -14,6 +13,7 @@ import { EffectComposer, EffectPass } from "postprocessing";
import {
Audio,
AudioListener,
Camera,
Object3D,
PerspectiveCamera,
PositionalAudio,
Expand Down Expand Up @@ -94,6 +94,7 @@ export class App {
sid2str: Map<number, string | null>;
nextSid = 1;

camera: Camera;
audioListener: AudioListener;

dialog = new DialogAdapter();
Expand Down Expand Up @@ -195,6 +196,7 @@ export class App {
const audioListener = new AudioListener();
this.audioListener = audioListener;
camera.add(audioListener);
this.camera = camera;
keianhzo marked this conversation as resolved.
Show resolved Hide resolved

this.world.time = {
delta: 0,
Expand Down
3 changes: 3 additions & 0 deletions src/bit-components.js
Original file line number Diff line number Diff line change
Expand Up @@ -194,3 +194,6 @@ export const ObjectSpawner = defineComponent({
src: Types.ui32,
flags: Types.ui8
});
export const Billboard = defineComponent({
onlyY: Types.ui8
});
93 changes: 93 additions & 0 deletions src/bit-systems/billboard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { FrameSchedulerSystem } from "aframe";
import { defineQuery, enterQuery, exitQuery } from "bitecs";
import { HubsWorld } from "../app";
import { Billboard } from "../bit-components";

const billboardQuery = defineQuery([Billboard]);
const billboardEnterQuery = enterQuery(billboardQuery);
const billboardExitQuery = exitQuery(billboardQuery);

const isMobileVR = AFRAME.utils.device.isMobileVR();

const targetPos = new THREE.Vector3();
keianhzo marked this conversation as resolved.
Show resolved Hide resolved
const worldPos = new THREE.Vector3();
const inView = new Map();

const updateIsInView = (world: HubsWorld, billboard: number) => {
const frustum = new THREE.Frustum();
const frustumMatrix = new THREE.Matrix4();
const box = new THREE.Box3();
const boxTemp = new THREE.Box3();

const expandBox = (child: THREE.Mesh) => {
if (child.geometry && child.geometry.boundingBox) {
child.updateMatrices();
child.geometry.computeBoundingBox();
boxTemp.copy(child.geometry.boundingBox).applyMatrix4(child.matrixWorld);
box.expandByPoint(boxTemp.min);
box.expandByPoint(boxTemp.max);
}
};

const isInViewOfCamera = (object3D: THREE.Object3D, camera: THREE.Camera) => {
frustumMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
frustum.setFromProjectionMatrix(frustumMatrix);
box.makeEmpty();
object3D.traverse(expandBox);

// NOTE: not using box.setFromObject here because text nodes do not have Z values in their geometry buffer,
// and that routine ultimately assumes they do.
return frustum.intersectsBox(box);
};

return () => {
const object3D = world.eid2obj.get(billboard);
if (!object3D || !object3D.visible) {
keianhzo marked this conversation as resolved.
Show resolved Hide resolved
inView.set(billboard, false);
return;
}

if (!APP.camera) return;

const inVR = APP.scene?.is("vr-mode");
inView.set(billboard, inVR ? true : isInViewOfCamera(object3D, APP.camera));
keianhzo marked this conversation as resolved.
Show resolved Hide resolved
};
};

let updateIsInViewCallback: (world: HubsWorld, billboard: number) => void;

export function billboardSystem(world: HubsWorld, frameScheduler: FrameSchedulerSystem) {
billboardEnterQuery(world).forEach(billboard => {
if (isMobileVR) {
updateIsInViewCallback = updateIsInView(world, billboard);
frameScheduler.schedule(updateIsInViewCallback, "billboards");
keianhzo marked this conversation as resolved.
Show resolved Hide resolved
}
});
billboardExitQuery(world).forEach(billboard => {
if (isMobileVR && updateIsInViewCallback) {
frameScheduler.unschedule(updateIsInViewCallback, "billboards");
}
});
billboardQuery(world).forEach(billboard => {
const isInView = inView.get(billboard);
if (isInView || !isMobileVR) {
const object3D = world.eid2obj.get(billboard);
const onlyY = Billboard.onlyY[billboard];
if (!object3D || !object3D.visible) return;
keianhzo marked this conversation as resolved.
Show resolved Hide resolved

const camera = APP.camera;
keianhzo marked this conversation as resolved.
Show resolved Hide resolved
if (camera) {
// Set the camera world position as the target.
targetPos.setFromMatrixPosition(camera.matrixWorld);

if (onlyY) {
object3D.getWorldPosition(worldPos);
targetPos.y = worldPos.y;
}
object3D.lookAt(targetPos);

object3D.matrixNeedsUpdate = true;
}
}
});
}
95 changes: 0 additions & 95 deletions src/components/billboard.js

This file was deleted.

1 change: 0 additions & 1 deletion src/components/scene-components.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import "./animation";
import "./ambient-light";
import "./animation-mixer";
import "./audio-feedback";
import "./billboard";
import "./slice9";
import "./css-class";
import "./directional-light";
Expand Down
7 changes: 5 additions & 2 deletions src/gltf-component-mappings.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ import { TYPE, SHAPE, FIT } from "three-ammo/constants";
import { COLLISION_LAYERS } from "./constants";
import { AudioType, DistanceModelType, SourceType } from "./components/audio-params";
import { updateAudioSettings } from "./update-audio-settings";
import { renderAsEntity } from "./utils/jsx-entity";
import { billboardInflator, renderAsEntity } from "./utils/jsx-entity";
import { Networked } from "./bit-components";
import { addComponent } from "bitecs";

const inflatorWrapper = inflator => (el, _componentName, componentData) =>
inflator(APP.world, el.object3D.eid, componentData);

AFRAME.GLTFModelPlus.registerComponent("duck", "duck", el => {
el.setAttribute("duck", "");
el.setAttribute("quack", { quackPercentage: 0.1 });
Expand Down Expand Up @@ -57,7 +60,7 @@ AFRAME.GLTFModelPlus.registerComponent("directional-light", "directional-light")
AFRAME.GLTFModelPlus.registerComponent("hemisphere-light", "hemisphere-light");
AFRAME.GLTFModelPlus.registerComponent("point-light", "point-light");
AFRAME.GLTFModelPlus.registerComponent("spot-light", "spot-light");
AFRAME.GLTFModelPlus.registerComponent("billboard", "billboard");
AFRAME.GLTFModelPlus.registerComponent("billboard", "billboard", inflatorWrapper(billboardInflator));
keianhzo marked this conversation as resolved.
Show resolved Hide resolved
AFRAME.GLTFModelPlus.registerComponent("simple-water", "simple-water");
AFRAME.GLTFModelPlus.registerComponent("skybox", "skybox");
AFRAME.GLTFModelPlus.registerComponent("layers", "layers");
Expand Down
2 changes: 2 additions & 0 deletions src/systems/hubs-systems.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import { networkDebugSystem } from "../bit-systems/network-debug";
import qsTruthy from "../utils/qs_truthy";
import { waypointSystem } from "../bit-systems/waypoint";
import { objectSpawnerSystem } from "../bit-systems/object-spawner";
import { billboardSystem } from "../bit-systems/billboard";

declare global {
interface Window {
Expand Down Expand Up @@ -184,6 +185,7 @@ export function mainTick(xrFrame: XRFrame, renderer: WebGLRenderer, scene: Scene
// We run this earlier in the frame so things have a chance to override properties run by animations
hubsSystems.animationMixerSystem.tick(dt);

billboardSystem(world, aframeSystems["frame-scheduler"]);
waypointSystem(world, hubsSystems.characterController, sceneEl.is("frozen"));
hubsSystems.characterController.tick(t, dt);
hubsSystems.cursorTogglingSystem.tick(aframeSystems.interaction, aframeSystems.userinput, hubsSystems.el);
Expand Down
11 changes: 9 additions & 2 deletions src/utils/jsx-entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ import {
SceneRoot,
NetworkDebug,
WaypointPreview,
NetworkedFloatyObject
NetworkedFloatyObject,
Billboard
} from "../bit-components";
import { inflateMediaLoader } from "../inflators/media-loader";
import { inflateMediaFrame } from "../inflators/media-frame";
Expand Down Expand Up @@ -301,6 +302,7 @@ export interface JSXComponentData extends ComponentData {
model?: ModelParams;
networkDebug?: boolean;
waypointPreview?: boolean;
billboard?: { onlyY: boolean };
}

export interface GLTFComponentData extends ComponentData {
Expand All @@ -317,6 +319,7 @@ export interface GLTFComponentData extends ComponentData {
skybox: SkyboxParams;
fog: FogParams;
background: BackgroundParams;
billboard?: { onlyY: boolean };
}

declare global {
Expand All @@ -334,6 +337,8 @@ declare global {
}
}

export const billboardInflator = createDefaultInflator(Billboard);

export const commonInflators: Required<{ [K in keyof ComponentData]: InflatorFn }> = {
grabbable: inflateGrabbable,

Expand Down Expand Up @@ -374,6 +379,7 @@ const jsxInflators: Required<{ [K in keyof JSXComponentData]: InflatorFn }> = {
networkDebug: createDefaultInflator(NetworkDebug),
waypointPreview: createDefaultInflator(WaypointPreview),
mediaLoader: inflateMediaLoader,
billboard: billboardInflator,

// inflators that create Object3Ds
mediaFrame: inflateMediaFrame,
Expand All @@ -397,7 +403,8 @@ export const gltfInflators: Required<{ [K in keyof GLTFComponentData]: InflatorF
background: inflateBackground,
spawnPoint: inflateSpawnpoint,
skybox: inflateSkybox,
spawner: inflateSpawner
spawner: inflateSpawner,
billboard: billboardInflator
keianhzo marked this conversation as resolved.
Show resolved Hide resolved
};

function jsxInflatorExists(name: string): name is keyof JSXComponentData {
Expand Down
14 changes: 14 additions & 0 deletions types/aframe.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ declare module "aframe" {
mesh?: Mesh;
}

interface FrameSchedulerSystem extends ASystem {
schedule(func: Function, queue: string);
unschedule(func: Function, queue: string);
}

interface AScene extends AElement {
object3D: Scene;
renderStarted: boolean;
Expand All @@ -100,16 +105,25 @@ declare module "aframe" {
/** @deprecated see bit-interaction-system */
interaction: InteractionSystem;
nav: NavSystem;
"frame-scheduler": FrameSchedulerSystem;
};
emit(string, any): void;
addState(string): void;
is(string): boolean;
}

interface Device {
isMobileVR: Function;
}
interface Utils {
device: Device;
}

declare global {
const AFRAME: {
registerSystem(name: string, def: Partial<ASystem>);
scenes: AScene[];
utils: Util;
};
}
}