From d13b09667f17dd171ea4ccd649ec68c453394c8b Mon Sep 17 00:00:00 2001 From: Takahiro Date: Wed, 23 Aug 2023 22:43:02 -0700 Subject: [PATCH] bitECS: Fix grabbable pinned media objects ** 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. **Changes** - Add LoadedByMediaLoader component to detect a media entity is loaded via MediaLoader - Add a special path for media entity loaded via MediaLoader in hold-system that checks its parent is pinned to check whether it is pinned. --- src/bit-components.js | 1 + src/bit-systems/media-loading.ts | 2 ++ src/systems/hold-system.js | 56 +++++++++++++++++++++++++++++++- 3 files changed, 58 insertions(+), 1 deletion(-) 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 &&