Skip to content

Commit

Permalink
bitECS: Fix grabbable pinned media objects
Browse files Browse the repository at this point in the history
** 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.
  • Loading branch information
takahirox committed Sep 1, 2023
1 parent 89f8c91 commit d13b096
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 1 deletion.
1 change: 1 addition & 0 deletions src/bit-components.js
Original file line number Diff line number Diff line change
Expand Up @@ -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]
});
Expand Down
2 changes: 2 additions & 0 deletions src/bit-systems/media-loading.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Box3, Euler, Vector3 } from "three";
import { HubsWorld } from "../app";
import {
GLTFModel,
LoadedByMediaLoader,
MediaContentBounds,
MediaLoaded,
MediaLoader,
Expand Down Expand Up @@ -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;
Expand Down
56 changes: 55 additions & 1 deletion src/systems/hold-system.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
HeldHandRight,
HoveredHandLeft,
HeldHandLeft,
LoadedByMediaLoader,
AEntity
} from "../bit-components";
import { canMove } from "../utils/permissions-utils";
Expand Down Expand Up @@ -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 &&
Expand Down

0 comments on commit d13b096

Please sign in to comment.