Skip to content

Commit

Permalink
Polish text bitecs
Browse files Browse the repository at this point in the history
This commit pilishes text bitecs. The changes are

* Rename Text component to TextTag to avoid the confliction
  with the WebAPI Text
* Rewrite inflators/text from JavaScript to TypeScript
* Explicitly declare text inflator params
* Add Text system that calls sync() and remove sync() calls
  from other systems
* Update type/troika-three-text.d.ts to match the
  troika-three-text API
* Fix some bugs
  • Loading branch information
takahirox committed Feb 7, 2023
1 parent 0bac051 commit 5baf4e2
Show file tree
Hide file tree
Showing 11 changed files with 182 additions and 55 deletions.
2 changes: 1 addition & 1 deletion src/bit-components.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const MediaFrame = defineComponent({
preview: Types.eid,
previewingNid: Types.eid
});
export const Text = defineComponent();
export const TextTag = defineComponent();
export const ReflectionProbe = defineComponent();
export const Slice9 = defineComponent({
insets: [Types.ui32, 4],
Expand Down
3 changes: 0 additions & 3 deletions src/bit-systems/camera-tool.js
Original file line number Diff line number Diff line change
Expand Up @@ -178,17 +178,14 @@ function updateUI(world, camera) {
if (countdownLblObj.visible) {
const timeLeftSec = Math.ceil((CameraTool.snapTime[camera] - world.time.elapsed) / 1000);
countdownLblObj.text = timeLeftSec;
countdownLblObj.sync(); // TODO this should probably happen in 1 spot per frame for all Texts
}

if (captureDurLblObj.visible) {
captureDurLblObj.text = CAPTURE_DURATIONS[CameraTool.captureDurIdx[camera]];
captureDurLblObj.sync(); // TODO this should probably happen in 1 spot per frame for all Texts
}

if (sndToggleBtnObj.visible) {
sndToggleLblObj.text = captureAudio ? "Sound ON" : "Sound OFF";
sndToggleLblObj.sync();
}
}

Expand Down
1 change: 0 additions & 1 deletion src/bit-systems/network-debug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,5 @@ export function networkDebugSystem(world: HubsWorld, scene: Scene) {
textObj.matrixNeedsUpdate = true;

textObj.text = formatObjectName(networkedObj) + "\nNetworked " + formatComponentProps(eid, Networked);
textObj.sync();
});
}
22 changes: 22 additions & 0 deletions src/bit-systems/text.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { defineQuery } from "bitecs";
import { Text as TroikaText } from "troika-three-text";
import { HubsWorld } from "../app";
import { TextTag } from "../bit-components";

const textQuery = defineQuery([TextTag]);

export function textSystem(world: HubsWorld) {
textQuery(world).forEach(eid => {
const text = world.eid2obj.get(eid)! as TroikaText;

// sync() invokes async text processing in workers.
// It is safe to call sync() every frame from the
// performance and efficiency perspective because
// sync() checks whether to invoke costly processing
// inside.
// https://github.com/protectwise/troika/tree/main/packages/troika-three-text#handling-asynchronous-updates
// Question: Is it safe even if text object is
// disposed before the async processing is done?
text.sync();
});
}
1 change: 0 additions & 1 deletion src/bit-systems/video-menu-system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,6 @@ export function videoMenuSystem(world: HubsWorld, userinput: any) {

const timeLabelRef = world.eid2obj.get(VideoMenu.timeLabelRef[eid])! as TroikaText;
timeLabelRef.text = `${timeFmt(video.currentTime)} / ${timeFmt(video.duration)}`;
timeLabelRef.sync();

if (rightMenuIndicatorCoroutine && rightMenuIndicatorCoroutine().done) {
rightMenuIndicatorCoroutine = null;
Expand Down
42 changes: 0 additions & 42 deletions src/inflators/text.js

This file was deleted.

120 changes: 120 additions & 0 deletions src/inflators/text.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import { addComponent } from "bitecs";
import { BackSide, DoubleSide, FrontSide } from "three";
import { Text as TroikaText } from "troika-three-text";
import { HubsWorld } from "../app";
import { TextTag } from "../bit-components";
import { addObject3DComponent } from "../utils/jsx-entity";

export type TextParams = {
value: string;
anchorX?: "left" | "center" | "right";
anchorY?: "top" | "top-baseline" | "top-cap" | "top-ex" | "middle" | "bottom-baseline" | "bottom";
clipRect?: [number, number, number, number] | null;
color?: string;
curveRadius?: number;
depthOffset?: number;
direction?: "auto" | "ltr" | "trl";
fillOpacity?: number;
fontUrl?: string | null;
fontSize?: number;
letterSpacing?: number;
lineHeight?: number | "normal";
maxWidth?: number;
opacity?: number;
outlineBlur?: number | `${number}%`;
outlineColor?: string;
outlineOffsetX?: number | `${number}%`;
outlineOffsetY?: number | `${number}%`;
outlineOpacity?: number;
outlineWidth?: number | `${number}%`;
overflowWrap?: "normal" | "break-word";
sdfGlyphSize?: number;
side?: "front" | "back" | "double";
strokeColor?: string;
strokeOpacity?: number;
strokeWidth?: number | `${number}%`;
textAlign?: 'left' | 'right' | 'center' | 'justify';
textIndent?: number;
whiteSpace?: "normal" | "nowrap";
};

const THREE_SIDES = {
front: FrontSide,
back: BackSide,
double: DoubleSide
};

// Defaults values must be set for all the optional properties
// to use foo! operator in the inflator.
const DEFAULTS: TextParams = {
anchorX: "center",
anchorY: "middle",
clipRect: null,
color: "#ffffff",
curveRadius: 0,
depthOffset: 0,
direction: "auto",
fillOpacity: 1.0,
fontUrl: null,
fontSize: 0.075,
letterSpacing: 0,
lineHeight: "normal",
maxWidth: Infinity,
opacity: 1.0,
outlineBlur: 0,
outlineColor: "#000000",
outlineOffsetX: 0,
outlineOffsetY: 0,
outlineOpacity: 1.0,
outlineWidth: 0,
overflowWrap: "normal",
side: "front",
strokeColor: "grey",
strokeOpacity: 1.0,
strokeWidth: 0,
textAlign: "center",
textIndent: 0,
value: "",
whiteSpace: "normal"
};

export function inflateText(world: HubsWorld, eid: number, params: TextParams) {
params = Object.assign({}, DEFAULTS, params);
const text = new TroikaText();

text.text = params.value;
text.material!.side = THREE_SIDES[params.side!];
text.material!.opacity = params.opacity!;
text.font = params.fontUrl!;

text.anchorX = params.anchorX!;
text.anchorY = params.anchorY!;
text.clipRect = params.clipRect!;
text.color = params.color!;
text.curveRadius = params.curveRadius!;
text.depthOffset = params.depthOffset!;
text.direction = params.direction!;
text.fillOpacity = params.fillOpacity!;
text.fontSize = params.fontSize!;
text.letterSpacing = params.letterSpacing!;
text.lineHeight = params.lineHeight!;
text.maxWidth = params.maxWidth!;
text.outlineBlur = params.outlineBlur!;
text.outlineColor = params.outlineColor!;
text.outlineOffsetX = params.outlineOffsetX!;
text.outlineOffsetY = params.outlineOffsetY!;
text.outlineOpacity = params.outlineOpacity!;
text.outlineWidth = params.outlineWidth!;
text.overflowWrap = params.overflowWrap!;
text.strokeColor = params.strokeColor!;
text.strokeOpacity = params.strokeOpacity!;
text.strokeWidth = params.strokeWidth!;
text.textAlign = params.textAlign!;
text.textIndent = params.textIndent!;
text.whiteSpace = params.whiteSpace!;

text.sync();

addComponent(world, TextTag, eid);
addObject3DComponent(world, eid, text);
}
4 changes: 3 additions & 1 deletion src/systems/hubs-systems.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ import { uvScrollSystem } from "../bit-systems/uv-scroll";
import { simpleWaterSystem } from "../bit-systems/simple-water";
import { pdfSystem } from "../bit-systems/pdf-system";
import { particleEmitterSystem } from "../bit-systems/particle-emitter";
import { textSystem } from "../bit-systems/text";

declare global {
interface Window {
Expand Down Expand Up @@ -245,7 +246,8 @@ export function mainTick(xrFrame: XRFrame, renderer: WebGLRenderer, scene: Scene
hubsSystems.gainSystem.tick();
hubsSystems.nameTagSystem.tick();
simpleWaterSystem(world);

textSystem(world);

videoTextureSystem(world);

deleteEntitySystem(world, aframeSystems.userinput);
Expand Down
4 changes: 2 additions & 2 deletions src/systems/remove-object3D-system.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
SimpleWater,
Skybox,
Slice9,
Text,
TextTag,
VideoMenu,
ParticleEmitterTag
} from "../bit-components";
Expand Down Expand Up @@ -47,7 +47,7 @@ const cleanupGLTFs = cleanupObjOnExit(GLTFModel, obj => {
}
});
const cleanupLights = cleanupObjOnExit(LightTag, obj => obj.dispose());
const cleanupTexts = cleanupObjOnExit(Text, obj => obj.dispose());
const cleanupTexts = cleanupObjOnExit(TextTag, obj => obj.dispose());
const cleanupMediaFrames = cleanupObjOnExit(MediaFrame, obj => obj.geometry.dispose());
const cleanupAudioEmitters = cleanupObjOnExit(AudioEmitter, obj => {
obj.disconnect();
Expand Down
4 changes: 2 additions & 2 deletions src/utils/jsx-entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ import { inflateVideoLoader, VideoLoaderParams } from "../inflators/video-loader
import { inflateImageLoader, ImageLoaderParams } from "../inflators/image-loader";
import { inflateModel, ModelParams } from "../inflators/model";
import { inflateSlice9 } from "../inflators/slice9";
import { inflateText } from "../inflators/text";
import { TextParams, inflateText } from "../inflators/text";
import {
BackgroundParams,
EnvironmentSettingsParams,
Expand Down Expand Up @@ -325,7 +325,7 @@ export interface JSXComponentData extends ComponentData {
sceneLoader?: { src: string };
mediaFrame?: any;
object3D?: any;
text?: any;
text?: TextParams;
model?: ModelParams;
networkDebug?: boolean;
waypointPreview?: boolean;
Expand Down
34 changes: 32 additions & 2 deletions types/troika-three-text.d.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,38 @@
declare module "troika-three-text" {
import { Mesh } from "three";
import { Color, Material, Mesh } from "three";

export interface Text extends Mesh {
export class Text extends Mesh {
text: string;
anchorX: number | `${number}%` | 'left' | 'center' | 'right';
anchorY: number | `${number}%` | 'top' | 'top-baseline' | 'top-cap' | 'top-ex' | 'middle' | 'bottom-baseline' | 'bottom';
clipRect: [number, number, number, number] | null;
color: string | number | Color | null;
curveRadius: number;
depthOffset: number;
direction: string;
fillOpacity: number;
font: string | null;
fontSize: number;
glyphGeometryDetail: number;
gpuAccelerateSDF: boolean;
letterSpacing: number;
lineHeight: number | 'normal';
material: Material | null;
maxWidth: number;
outlineBlur: number | `${number}%`;
outlineColor: string | number | Color;
outlineOffsetX: number | `${number}%`;
outlineOffsetY: number | `${number}%`;
outlineOpacity: number;
outlineWidth: number | `${number}%`;
overflowWrap: 'normal' | 'break-word';
sdfGlyphSize: number;
strokeColor: string | number | Color;
strokeOpacity: number;
strokeWidth: number | `${number}%`;
textAlign: 'left' | 'right' | 'center' | 'justify';
textIndent: number;
whiteSpace: 'normal' | 'nowrap';
sync(callback?: () => void): void;
}

Expand Down

0 comments on commit 5baf4e2

Please sign in to comment.