diff --git a/src/bit-components.js b/src/bit-components.js index 26cf6c0240..0059457998 100644 --- a/src/bit-components.js +++ b/src/bit-components.js @@ -157,6 +157,7 @@ export const MediaLoader = defineComponent({ MediaLoader.src[$isStringType] = true; MediaLoader.fileId[$isStringType] = true; export const MediaLoaded = defineComponent(); +export const LoadedByMediaLoader = defineComponent(); export const MediaContentBounds = defineComponent({ bounds: [Types.f32, 3] }); diff --git a/src/bit-systems/media-loading.ts b/src/bit-systems/media-loading.ts index 022fa1c8fb..cdb1ef64c4 100644 --- a/src/bit-systems/media-loading.ts +++ b/src/bit-systems/media-loading.ts @@ -3,6 +3,7 @@ import { Box3, Euler, Vector3 } from "three"; import { HubsWorld } from "../app"; import { GLTFModel, + LoadedByMediaLoader, MediaContentBounds, MediaLoaded, MediaLoader, @@ -210,6 +211,7 @@ function* loadMedia(world: HubsWorld, eid: EntityID) { console.error(e); media = renderAsEntity(world, ErrorObject()); } + addComponent(world, LoadedByMediaLoader, media); crClearTimeout(addLoadingObjectTimeout); loadingObjEid && removeEntity(world, loadingObjEid); return media; diff --git a/src/systems/hold-system.js b/src/systems/hold-system.js index 5625de006b..149e6f21de 100644 --- a/src/systems/hold-system.js +++ b/src/systems/hold-system.js @@ -11,6 +11,7 @@ import { HeldHandRight, HoveredHandLeft, HeldHandLeft, + LoadedByMediaLoader, AEntity } from "../bit-components"; import { canMove } from "../utils/permissions-utils"; @@ -38,9 +39,62 @@ function isAEntityPinned(world, eid) { return false; } +// Special check for Droped/Pasted Media with new loader enabled. +// +// ** Problem ** +// When you drop or paste a media file or url, a MediaLoader entity is created. +// This entity downloads the media file and creates a media (e.g., video) +// entity. The MediaLoader entity object then adds the media entity object +// as its child. +// +// The MediaLoader entity does not have a visible object after the loading +// cube disappears, but the media entity does. Both entities are hover targets, +// but after the loading cube disappears, the MediaLoader entity can no longer +// be hovered because it does not have a visible object. Only the media entity +// can be hovered at this point. +// +// The media entity is not a networked entity, but the MediaLoader entity is. +// This means that the MediaLoader entity can be pinned (the creator is "reticulum"), +// but the media entity cannot. If you check whether the media entity is pinned, +// it will always return false. Then media entity will always be grabbable even if +// it is pinned (more precisely its parent is pinned). +// +// **Solution** +// Check its parent is pinned to check media entity is pinned. +// +// TODO: This solution is hacky. Fix the root issue. +// +// Alternate solution: Simply recognize an entity as pinned if its any +// ancestor is pinned (in hold-system) unless there is a case that +// descendant entity under pinned entity wants to be grabbable. +function isParentPinned(world, eid) { + if (!world.eid2obj.has(eid)) { + return false; + } + + const obj = world.eid2obj.get(eid); + + if (obj.parent === null) { + return false; + } + + const parent = obj.parent; + + if (parent.eid === undefined) { + return false; + } + + return isPinned(parent.eid); +} + function grab(world, userinput, queryHovered, held, grabPath) { const hovered = queryHovered(world)[0]; - const isEntityPinned = isPinned(hovered) || isAEntityPinned(world, hovered); + let isEntityPinned = isPinned(hovered) || isAEntityPinned(world, hovered); + + // Special path for Dropped/Pasted Media with new loader enabled + if (!isEntityPinned && hasComponent(world, LoadedByMediaLoader, hovered)) { + isEntityPinned = isParentPinned(world, hovered); + } if ( hovered &&