diff --git a/src/bit-components.js b/src/bit-components.js index 26cf6c0240..21b8ef04e2 100644 --- a/src/bit-components.js +++ b/src/bit-components.js @@ -161,6 +161,31 @@ export const MediaContentBounds = defineComponent({ bounds: [Types.f32, 3] }); +// MediaImageLoaderData and MediaVideoLoaderData are +// for parameters that are set at glTF inflators +// inflateImageLoader and inflateVideoLoader and +// are needed to be transported to util image/audio loaders. +// They are handled as part of MediaLoader component data. + +/** + * @type {Map} + */ +export const MediaImageLoaderData = new Map(); + +/** + * @type {Map} + */ +export const MediaVideoLoaderData = new Map(); + export const SceneRoot = defineComponent(); export const NavMesh = defineComponent(); export const SceneLoader = defineComponent({ src: Types.ui32 }); @@ -173,10 +198,6 @@ export const MediaImage = defineComponent({ alphaCutoff: Types.f32 }); MediaImage.cacheKey[$isStringType] = true; -/** - * @type {Map} - */ -export const MediaImageLoaderData = new Map(); export const NetworkedPDF = defineComponent({ pageNumber: Types.ui8 @@ -191,10 +212,6 @@ export const MediaVideo = defineComponent({ flags: Types.ui8, projection: Types.ui8 }); -/** - * @type {Map} - */ -export const MediaVideoLoaderData = new Map(); /** * @type {Map} */ diff --git a/src/bit-systems/media-loading.ts b/src/bit-systems/media-loading.ts index 022fa1c8fb..9661cd6afa 100644 --- a/src/bit-systems/media-loading.ts +++ b/src/bit-systems/media-loading.ts @@ -4,8 +4,10 @@ import { HubsWorld } from "../app"; import { GLTFModel, MediaContentBounds, + MediaImageLoaderData, MediaLoaded, MediaLoader, + MediaVideoLoaderData, Networked, ObjectMenuTarget } from "../bit-components"; @@ -65,34 +67,6 @@ export function* waitForMediaLoaded(world: HubsWorld, eid: EntityID) { } } -// prettier-ignore -const loaderForMediaType = { - [MediaType.IMAGE]: ( - world: HubsWorld, - eid: EntityID, - { accessibleUrl, contentType }: { accessibleUrl: string, contentType: string } - ) => loadImage(world, eid, accessibleUrl, contentType), - [MediaType.VIDEO]: ( - world: HubsWorld, - eid: EntityID, - { accessibleUrl, contentType }: { accessibleUrl: string, contentType: string } - ) => loadVideo(world, eid, accessibleUrl, contentType), - [MediaType.MODEL]: ( - world: HubsWorld, - eid: EntityID, - { accessibleUrl, contentType }: { accessibleUrl: string, contentType: string } - ) => loadModel(world, accessibleUrl, contentType, true), - [MediaType.PDF]: (world: HubsWorld, eid: EntityID, { accessibleUrl }: { accessibleUrl: string }) => - loadPDF(world, accessibleUrl), - [MediaType.AUDIO]: (world: HubsWorld, eid: EntityID, { accessibleUrl }: { accessibleUrl: string }) => - loadAudio(world, eid, accessibleUrl), - [MediaType.HTML]: ( - world: HubsWorld, - eid: EntityID, - { canonicalUrl, thumbnail }: { canonicalUrl: string, thumbnail: string } - ) => loadHtml(world, canonicalUrl, thumbnail) -}; - export const MEDIA_LOADER_FLAGS = { RECENTER: 1 << 0, RESIZE: 1 << 1, @@ -189,6 +163,52 @@ type MediaInfo = { thumbnail: string; }; +function* loadByMediaType( + world: HubsWorld, + eid: EntityID, + { accessibleUrl, canonicalUrl, contentType, mediaType, thumbnail }: MediaInfo +) { + // Note: For Image, Video, and Audio, additional parameters can be + // set via glTF image/video/audio inflators inflateImageLoader + // and inflateVideoLoader. + // TODO: Refactor media loading flow to simplify. + // Only in loading glTF Image, Video, and Audio flows, + // specified parameters assignment is needed after loading + // content then using MediaImage/VideoLoaderData as like + // transporting data from the inflators. This may be like + // special and a bit less maintainable. + switch (mediaType) { + case MediaType.IMAGE: + return yield* loadImage( + world, + accessibleUrl, + contentType, + MediaImageLoaderData.has(eid) ? MediaImageLoaderData.get(eid)! : {} + ); + case MediaType.VIDEO: + return yield* loadVideo( + world, + accessibleUrl, + contentType, + MediaVideoLoaderData.has(eid) ? MediaVideoLoaderData.get(eid)! : {} + ); + case MediaType.MODEL: + return yield* loadModel(world, accessibleUrl, contentType, true); + case MediaType.PDF: + return yield* loadPDF(world, accessibleUrl); + case MediaType.AUDIO: + return yield* loadAudio( + world, + accessibleUrl, + MediaVideoLoaderData.has(eid) ? MediaVideoLoaderData.get(eid)! : {} + ); + case MediaType.HTML: + return yield* loadHtml(world, canonicalUrl, thumbnail); + default: + throw new UnsupportedMediaTypeError(eid, mediaType); + } +} + function* loadMedia(world: HubsWorld, eid: EntityID) { let loadingObjEid = 0; const addLoadingObjectTimeout = crTimeout(() => { @@ -200,11 +220,7 @@ function* loadMedia(world: HubsWorld, eid: EntityID) { let media: EntityID; try { const urlData = (yield resolveMediaInfo(src)) as MediaInfo; - const loader = urlData.mediaType && loaderForMediaType[urlData.mediaType]; - if (!loader) { - throw new UnsupportedMediaTypeError(eid, urlData.mediaType); - } - media = yield* loader(world, eid, urlData); + media = yield* loadByMediaType(world, eid, urlData); addComponent(world, MediaLoaded, media); } catch (e) { console.error(e); @@ -257,6 +273,14 @@ export function mediaLoadingSystem(world: HubsWorld) { mediaLoaderExitQuery(world).forEach(function (eid) { jobs.stop(eid); + + if (MediaImageLoaderData.has(eid)) { + MediaImageLoaderData.delete(eid); + } + + if (MediaVideoLoaderData.has(eid)) { + MediaVideoLoaderData.delete(eid); + } }); jobs.tick(); diff --git a/src/inflators/image-loader.ts b/src/inflators/image-loader.ts index d7b19d84b5..580e00eb24 100644 --- a/src/inflators/image-loader.ts +++ b/src/inflators/image-loader.ts @@ -1,8 +1,8 @@ import { HubsWorld } from "../app"; -import { ProjectionModeName } from "../utils/projection-mode"; -import { inflateMediaLoader } from "./media-loader"; import { MediaImageLoaderData } from "../bit-components"; -import { AlphaModeName } from "../utils/create-image-mesh"; +import { AlphaModeName, getAlphaModeFromAlphaModeName } from "../utils/create-image-mesh"; +import { ProjectionModeName, getProjectionFromProjectionName } from "../utils/projection-mode"; +import { inflateMediaLoader } from "./media-loader"; export interface ImageLoaderParams { src: string; @@ -27,5 +27,12 @@ export function inflateImageLoader(world: HubsWorld, eid: number, params: ImageL }); const requiredParams = Object.assign({}, DEFAULTS, params) as Required; - MediaImageLoaderData.set(eid, requiredParams); + MediaImageLoaderData.set(eid, { + alphaCutoff: requiredParams.alphaCutoff, + // This inflator is glTF inflator. alphaMode and projection are + // passed as strings from glTF. They are different typed, just regular enum, + // in Hubs Client internal. So needs to convert here. + alphaMode: getAlphaModeFromAlphaModeName(requiredParams.alphaMode), + projection: getProjectionFromProjectionName(requiredParams.projection) + }); } diff --git a/src/inflators/video-loader.ts b/src/inflators/video-loader.ts index cb3423f53f..48351cdde8 100644 --- a/src/inflators/video-loader.ts +++ b/src/inflators/video-loader.ts @@ -1,6 +1,6 @@ import { HubsWorld } from "../app"; import { MediaVideoLoaderData } from "../bit-components"; -import { ProjectionModeName } from "../utils/projection-mode"; +import { ProjectionModeName, getProjectionFromProjectionName } from "../utils/projection-mode"; import { inflateMediaLoader } from "./media-loader"; export interface VideoLoaderParams { @@ -28,5 +28,13 @@ export function inflateVideoLoader(world: HubsWorld, eid: number, params: VideoL }); const requiredParams = Object.assign({}, DEFAULTS, params) as Required; - MediaVideoLoaderData.set(eid, requiredParams); + MediaVideoLoaderData.set(eid, { + autoPlay: requiredParams.autoPlay, + controls: requiredParams.controls, + // This inflator is glTF inflator. projection is passed as strings + // from glTF. It is different typed, just regular enum, in Hubs Client + // internal. So needs to convert here. + projection: getProjectionFromProjectionName(requiredParams.projection), + loop: requiredParams.loop + }); } diff --git a/src/utils/load-audio.tsx b/src/utils/load-audio.tsx index cb5f8e0568..e0c9b36bc7 100644 --- a/src/utils/load-audio.tsx +++ b/src/utils/load-audio.tsx @@ -5,23 +5,23 @@ import { renderAsEntity } from "../utils/jsx-entity"; import { loadAudioTexture } from "../utils/load-audio-texture"; import { HubsWorld } from "../app"; import { HubsVideoTexture } from "../textures/HubsVideoTexture"; -import { EntityID } from "./networking-types"; -import { MediaVideoLoaderData } from "../bit-components"; -import { VideoLoaderParams } from "../inflators/video-loader"; -export function* loadAudio(world: HubsWorld, eid: EntityID, url: string) { - let loop = true; - let autoPlay = true; - let controls = true; - let projection = ProjectionMode.FLAT; - if (MediaVideoLoaderData.has(eid)) { - const params = MediaVideoLoaderData.get(eid)! as VideoLoaderParams; - loop = params.loop; - autoPlay = params.autoPlay; - controls = params.controls; - MediaVideoLoaderData.delete(eid); - } +type Params = { + loop?: boolean; + autoPlay?: boolean; + controls?: boolean; + projection?: ProjectionMode; +}; +const DEFAULTS: Required = { + loop: true, + autoPlay: true, + controls: true, + projection: ProjectionMode.FLAT +}; + +export function* loadAudio(world: HubsWorld, url: string, params: Params) { + const { loop, autoPlay, controls, projection } = Object.assign({}, DEFAULTS, params); const { texture, ratio, video }: { texture: HubsVideoTexture; ratio: number; video: HTMLVideoElement } = yield loadAudioTexture(url, loop, autoPlay); diff --git a/src/utils/load-image.tsx b/src/utils/load-image.tsx index 643434aa83..034f56ddc0 100644 --- a/src/utils/load-image.tsx +++ b/src/utils/load-image.tsx @@ -1,13 +1,11 @@ /** @jsx createElementEntity */ import { createElementEntity } from "../utils/jsx-entity"; -import { ProjectionMode, getProjectionFromProjectionName } from "./projection-mode"; +import { ProjectionMode } from "./projection-mode"; import { loadTextureCancellable } from "../utils/load-texture"; import { renderAsEntity } from "../utils/jsx-entity"; import { HubsWorld } from "../app"; import { Texture } from "three"; -import { AlphaMode, getAlphaModeFromAlphaModeName } from "./create-image-mesh"; -import { EntityID } from "./networking-types"; -import { MediaImageLoaderData } from "../bit-components"; +import { AlphaMode } from "./create-image-mesh"; import { ImageParams } from "../inflators/image"; export function* createImageDef(world: HubsWorld, url: string, contentType: string): Generator { @@ -34,15 +32,27 @@ export function* createImageDef(world: HubsWorld, url: string, contentType: stri }; } -export function* loadImage(world: HubsWorld, eid: EntityID, url: string, contentType: string) { +type Params = { + alphaCutoff?: number; + alphaMode?: AlphaMode; + projection?: ProjectionMode; +}; + +export function* loadImage(world: HubsWorld, url: string, contentType: string, params: Params) { + const { alphaCutoff, alphaMode, projection } = params; + const imageDef = yield* createImageDef(world, url, contentType); - if (MediaImageLoaderData.has(eid)) { - const params = MediaImageLoaderData.get(eid)!; - imageDef.projection = getProjectionFromProjectionName(params.projection); - imageDef.alphaMode = getAlphaModeFromAlphaModeName(params.alphaMode); - imageDef.alphaCutoff = params.alphaCutoff; - MediaImageLoaderData.delete(eid); + if (alphaCutoff !== undefined) { + imageDef.alphaCutoff = alphaCutoff; + } + + if (alphaMode !== undefined) { + imageDef.alphaMode = alphaMode; + } + + if (projection !== undefined) { + imageDef.projection = projection; } return renderAsEntity(world, ); diff --git a/src/utils/load-video.tsx b/src/utils/load-video.tsx index 4330865e52..b8d851bd42 100644 --- a/src/utils/load-video.tsx +++ b/src/utils/load-video.tsx @@ -1,28 +1,27 @@ /** @jsx createElementEntity */ import { createElementEntity } from "../utils/jsx-entity"; -import { ProjectionMode, getProjectionFromProjectionName } from "./projection-mode"; +import { ProjectionMode } from "./projection-mode"; import { renderAsEntity } from "../utils/jsx-entity"; import { loadVideoTexture } from "../utils/load-video-texture"; import { HubsWorld } from "../app"; import { HubsVideoTexture } from "../textures/HubsVideoTexture"; -import { EntityID } from "./networking-types"; -import { MediaVideoLoaderData } from "../bit-components"; -import { VideoLoaderParams } from "../inflators/video-loader"; -export function* loadVideo(world: HubsWorld, eid: EntityID, url: string, contentType: string) { - let loop = true; - let autoPlay = true; - let controls = true; - let projection = ProjectionMode.FLAT; - if (MediaVideoLoaderData.has(eid)) { - const params = MediaVideoLoaderData.get(eid)! as VideoLoaderParams; - loop = params.loop; - autoPlay = params.autoPlay; - controls = params.controls; - projection = getProjectionFromProjectionName(params.projection); - MediaVideoLoaderData.delete(eid); - } +type Params = { + loop?: boolean; + autoPlay?: boolean; + controls?: boolean; + projection?: ProjectionMode; +}; +const DEFAULTS: Required = { + loop: true, + autoPlay: true, + controls: true, + projection: ProjectionMode.FLAT +}; + +export function* loadVideo(world: HubsWorld, url: string, contentType: string, params: Params) { + const { loop, autoPlay, controls, projection } = Object.assign({}, DEFAULTS, params); const { texture, ratio, video }: { texture: HubsVideoTexture; ratio: number; video: HTMLVideoElement } = yield loadVideoTexture(url, contentType, loop, autoPlay);