Skip to content

Commit

Permalink
BitECS object list support
Browse files Browse the repository at this point in the history
  • Loading branch information
keianhzo authored and takahirox committed Sep 7, 2023
1 parent 9b216c8 commit 317e01e
Show file tree
Hide file tree
Showing 11 changed files with 211 additions and 91 deletions.
6 changes: 6 additions & 0 deletions src/bit-components.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,12 @@ export const LoadedByMediaLoader = defineComponent();
export const MediaContentBounds = defineComponent({
bounds: [Types.f32, 3]
});
export const MediaInfo = defineComponent({
accessibleUrl: Types.ui32,
contentType: Types.ui32
});
MediaInfo.accessibleUrl[$isStringType] = true;
MediaInfo.contentType[$isStringType] = true;

export const SceneRoot = defineComponent();
export const NavMesh = defineComponent();
Expand Down
10 changes: 10 additions & 0 deletions src/bit-systems/media-loading.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
GLTFModel,
LoadedByMediaLoader,
MediaContentBounds,
MediaInfo,
MediaLoaded,
MediaLoader,
Networked,
Expand Down Expand Up @@ -207,6 +208,9 @@ function* loadMedia(world: HubsWorld, eid: EntityID) {
}
media = yield* loader(world, eid, urlData);
addComponent(world, MediaLoaded, media);
addComponent(world, MediaInfo, media);
MediaInfo.accessibleUrl[media] = APP.getSid(urlData.accessibleUrl);
MediaInfo.contentType[media] = APP.getSid(urlData.contentType);
} catch (e) {
console.error(e);
media = renderAsEntity(world, ErrorObject());
Expand Down Expand Up @@ -252,6 +256,9 @@ const jobs = new JobRunner();
const mediaLoaderQuery = defineQuery([MediaLoader]);
const mediaLoaderEnterQuery = enterQuery(mediaLoaderQuery);
const mediaLoaderExitQuery = exitQuery(mediaLoaderQuery);
const mediaLoadedQuery = defineQuery([MediaLoaded]);
const mediaLoadedEnterQuery = enterQuery(mediaLoadedQuery);
const mediaLoadedExitQuery = exitQuery(mediaLoadedQuery);
export function mediaLoadingSystem(world: HubsWorld) {
mediaLoaderEnterQuery(world).forEach(function (eid) {
jobs.add(eid, clearRollbacks => loadAndAnimateMedia(world, eid, clearRollbacks));
Expand All @@ -261,5 +268,8 @@ export function mediaLoadingSystem(world: HubsWorld) {
jobs.stop(eid);
});

mediaLoadedEnterQuery(world).forEach(() => APP.scene?.emit("listed_media_changed"));
mediaLoadedExitQuery(world).forEach(() => APP.scene?.emit("listed_media_changed"));

jobs.tick();
}
4 changes: 2 additions & 2 deletions src/components/media-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ AFRAME.registerComponent("media-loader", {
setMatrixWorld(mesh, originalMeshMatrix);
} else {
// Move the mesh such that the center of its bounding box is in the same position as the parent matrix position
const box = getBox(this.el, mesh);
const box = getBox(this.el.object3D, mesh);
const scaleCoefficient = fitToBox ? getScaleCoefficient(0.5, box) : 1;
const { min, max } = box;
center.addVectors(min, max).multiplyScalar(0.5 * scaleCoefficient);
Expand Down Expand Up @@ -283,7 +283,7 @@ AFRAME.registerComponent("media-loader", {
}

// TODO this does duplicate work in some cases, but finish() is the only consistent place to do it
const contentBounds = getBox(this.el, this.el.getObject3D("mesh")).getSize(new THREE.Vector3());
const contentBounds = getBox(this.el.object3D, this.el.getObject3D("mesh")).getSize(new THREE.Vector3());
addComponent(APP.world, MediaContentBounds, el.eid);
MediaContentBounds.bounds[el.eid].set(contentBounds.toArray());

Expand Down
2 changes: 1 addition & 1 deletion src/components/super-spawner.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ AFRAME.registerComponent("super-spawner", {
? 1
: 0.5;

const scaleCoefficient = getScaleCoefficient(boxSize, getBox(spawnedEntity, spawnedEntity.object3D));
const scaleCoefficient = getScaleCoefficient(boxSize, getBox(spawnedEntity.object3D, spawnedEntity.object3D));
this.spawnedMediaScale.divideScalar(scaleCoefficient);
},

Expand Down
1 change: 1 addition & 0 deletions src/prefabs/media.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export function MediaPrefab(params: MediaLoaderParams): EntityDef {
collisionMask: COLLISION_LAYERS.HANDS
}}
scale={[1, 1, 1]}
inspectable
/>
);
}
100 changes: 70 additions & 30 deletions src/react-components/room/hooks/useObjectList.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import React, { useState, useEffect, useContext, createContext, useCallback, Children, cloneElement } from "react";
import PropTypes from "prop-types";
import { mediaSort, getMediaType } from "../../../utils/media-sorting.js";
import { mediaSort, mediaSortAframe, getMediaType, getMediaTypeAframe } from "../../../utils/media-sorting.js";
import { shouldUseNewLoader } from "../../../utils/bit-utils";
import { defineQuery } from "bitecs";
import { MediaInfo } from "../../../bit-components.js";

function getDisplayString(el) {
// Having a listed-media component does not guarantee the existence of a media-loader component,
// so don't crash if there isn't one.
const url = (el.components["media-loader"] && el.components["media-loader"].data.src) || "";
function getDisplayString(url) {
const split = url.split("/");
const resourceName = split[split.length - 1].split("?")[0];
let httpIndex = -1;
Expand Down Expand Up @@ -46,12 +46,14 @@ function handleInspect(scene, object, callback) {

callback(object);

if (object.el.object3D !== cameraSystem.inspectable) {
const object3D = shouldUseNewLoader() ? APP.world.eid2obj.get(object.eid) : object.el.object3D;

if (object3D !== cameraSystem.inspectable) {
if (cameraSystem.inspectable) {
cameraSystem.uninspect(false);
}

cameraSystem.inspect(object.el, 1.5, false);
cameraSystem.inspect(object3D, 1.5, false);
}
}

Expand All @@ -63,10 +65,12 @@ function handleDeselect(scene, object, callback) {
cameraSystem.uninspect(false);

if (object) {
cameraSystem.inspect(object.el, 1.5, false);
const object3D = shouldUseNewLoader() ? APP.world.eid2obj.get(object.eid) : object.el.object3D;
cameraSystem.inspect(object3D, 1.5, false);
}
}

const queryListedMedia = defineQuery([MediaInfo]);
export function ObjectListProvider({ scene, children }) {
const [objects, setObjects] = useState([]);
const [focusedObject, setFocusedObject] = useState(null); // The object currently shown in the viewport
Expand All @@ -76,14 +80,28 @@ export function ObjectListProvider({ scene, children }) {

useEffect(() => {
function updateMediaEntities() {
const objects = scene.systems["listed-media"].els.sort(mediaSort).map(el => ({
id: el.object3D.id,
name: getDisplayString(el),
type: getMediaType(el),
el
}));

setObjects(objects);
if (shouldUseNewLoader()) {
const objects = queryListedMedia(APP.world)
.sort(mediaSort)
.map(eid => ({
id: APP.world.eid2obj.get(eid)?.id,
name: getDisplayString(APP.getString(MediaInfo.accessibleUrl[eid])),
type: getMediaType(eid),
eid: eid
}));
setObjects(objects);
} else {
const objects = scene.systems["listed-media"].els.sort(mediaSortAframe).map(el => ({
id: el.object3D.id,
// Having a listed-media component does not guarantee the existence of a media-loader component,
// so don't crash if there isn't one.
name: getDisplayString((el.components["media-loader"] && el.components["media-loader"].data.src) || ""),
type: getMediaTypeAframe(el),
eid: el.eid,
el
}));
setObjects(objects);
}
}

let timeout;
Expand All @@ -108,23 +126,45 @@ export function ObjectListProvider({ scene, children }) {
function onInspectTargetChanged() {
const cameraSystem = scene.systems["hubs-systems"].cameraSystem;

const inspectedEl = cameraSystem.inspectable && cameraSystem.inspectable.el;

if (inspectedEl) {
const object = objects.find(o => o.el === inspectedEl);

if (object) {
setSelectedObject(object);
if (shouldUseNewLoader()) {
const inspectedEid = cameraSystem.inspectable && cameraSystem.inspectable.eid;

if (inspectedEid) {
const object = objects.find(o => o.eid === inspectedEid);

if (object) {
setSelectedObject(object);
} else {
setSelectedObject({
id: inspectedEl.object3D.id,
name: getDisplayString(inspectedEl),
type: getMediaType(inspectedEl),
eid: inspectedEid
});
}
} else {
setSelectedObject({
id: inspectedEl.object3D.id,
name: getDisplayString(inspectedEl),
type: getMediaType(inspectedEl),
el: inspectedEl
});
setSelectedObject(null);
}
} else {
setSelectedObject(null);
const inspectedEl = cameraSystem.inspectable && cameraSystem.inspectable.el;

if (inspectedEl) {
const object = objects.find(o => o.el === inspectedEl);

if (object) {
setSelectedObject(object);
} else {
setSelectedObject({
id: inspectedEl.object3D.id,
name: getDisplayString(inspectedEl),
type: getMediaType(inspectedEl),
eid: inspectedEl.eid,
el: inspectedEl
});
}
} else {
setSelectedObject(null);
}
}
}

Expand Down
50 changes: 38 additions & 12 deletions src/react-components/room/object-hooks.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,36 @@
import { useEffect, useState, useCallback, useMemo } from "react";
import { removeNetworkedObject } from "../../utils/removeNetworkedObject";
import { shouldUseNewLoader } from "../../utils/bit-utils";
import { rotateInPlaceAroundWorldUp, affixToWorldUp } from "../../utils/three-utils";
import { getPromotionTokenForFile } from "../../utils/media-utils";
import { hasComponent } from "bitecs";
import { Static } from "../../bit-components";
import { isPinned as getPinnedState } from "../../bit-systems/networking";
import { MediaInfo, Static } from "../../bit-components";
import { deleteTheDeletableAncestor } from "../../bit-systems/delete-entity-system";

export function isMe(object) {
return object.el.id === "avatar-rig";
return object.id === "avatar-rig";
}

export function isPlayer(object) {
return !!object.el.components["networked-avatar"];
if (shouldUseNewLoader()) {
// TODO Add when networked avatar is migrated
return false;
} else {
return !!object.el.components["networked-avatar"];
}
}

export function getObjectUrl(object) {
const mediaLoader = object.el.components["media-loader"];

const url =
mediaLoader && ((mediaLoader.data.mediaOptions && mediaLoader.data.mediaOptions.href) || mediaLoader.data.src);
let url;
if (shouldUseNewLoader()) {
const urlSid = MediaInfo.accessibleUrl[object.eid];
url = APP.getString(urlSid);
} else {
const mediaLoader = object.el.components["media-loader"];
url =
mediaLoader && ((mediaLoader.data.mediaOptions && mediaLoader.data.mediaOptions.href) || mediaLoader.data.src);
}

if (url && !url.startsWith("hubs://")) {
return url;
Expand All @@ -28,7 +40,7 @@ export function getObjectUrl(object) {
}

export function usePinObject(hubChannel, scene, object) {
const [isPinned, setIsPinned] = useState(getPinnedState(object.el.eid));
const [isPinned, setIsPinned] = useState(getPinnedState(object.eid));

const pinObject = useCallback(() => {
const el = object.el;
Expand All @@ -51,6 +63,11 @@ export function usePinObject(hubChannel, scene, object) {
}, [isPinned, pinObject, unpinObject]);

useEffect(() => {
// TODO Add when pinning is migrated
if (shouldUseNewLoader()) {
return;
}

const el = object.el;

function onPinStateChanged() {
Expand All @@ -65,6 +82,11 @@ export function usePinObject(hubChannel, scene, object) {
};
}, [object]);

if (shouldUseNewLoader()) {
// TODO Add when pinning is migrated
return false;
}

const el = object.el;

let userOwnsFile = false;
Expand Down Expand Up @@ -114,16 +136,20 @@ export function useGoToSelectedObject(scene, object) {

export function useRemoveObject(hubChannel, scene, object) {
const removeObject = useCallback(() => {
removeNetworkedObject(scene, object.el);
if (shouldUseNewLoader()) {
deleteTheDeletableAncestor(APP.world, object.eid);
} else {
removeNetworkedObject(scene, object.el);
}
}, [scene, object]);

const el = object.el;
const eid = object.eid;

const canRemoveObject = !!(
scene.is("entered") &&
!isPlayer(object) &&
!getPinnedState(el.eid) &&
!hasComponent(APP.world, Static, el.eid) &&
!getPinnedState(eid) &&
!hasComponent(APP.world, Static, eid) &&
hubChannel.can("spawn_and_move_media")
);

Expand Down
Loading

0 comments on commit 317e01e

Please sign in to comment.