Skip to content

Commit

Permalink
Merge pull request #6458 from mozilla/bitcs-unpriv-object-menu
Browse files Browse the repository at this point in the history
Add unprivileged object menu and layout update
  • Loading branch information
keianhzo authored Jan 25, 2024
2 parents 852d3cb + 4eca39b commit fba1b4c
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 126 deletions.
25 changes: 1 addition & 24 deletions src/bit-systems/object-menu-transform-system.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { defineQuery } from "bitecs";
import { defineQuery, removeComponent } from "bitecs";
import { HubsWorld } from "../app";
import { ObjectMenuTarget, ObjectMenuTransform } from "../bit-components";
import { EntityID } from "../utils/networking-types";
Expand All @@ -7,7 +7,6 @@ import { isFacingCamera, setFromObject, setMatrixWorld } from "../utils/three-ut
import { ObjectMenuTargetFlags } from "../inflators/object-menu-target";
import { ObjectMenuTransformFlags } from "../inflators/object-menu-transform";

const offset = new Vector3();
const tmpVec1 = new Vector3();
const tmpVec2 = new Vector3();
const tmpQuat1 = new Quaternion();
Expand Down Expand Up @@ -45,18 +44,6 @@ function transformMenu(world: HubsWorld, menu: EntityID) {
if (!targetObj || !enabled) return;

const menuObj = world.eid2obj.get(menu)!;

// Calculate the menu offset based on visible elements
const center = (ObjectMenuTransform.flags[menu] & ObjectMenuTransformFlags.Center) !== 0 ? true : false;
if (center && ObjectMenuTransform.targetObjectRef[menu] !== ObjectMenuTransform.prevObjectRef[menu]) {
getAABB(menuObj, aabb);
aabb.getCenter(tmpVec1);
getAABB(menuObj, aabb, true);
aabb.getCenter(tmpVec2);
offset.subVectors(tmpVec1, tmpVec2);
offset.z = 0;
}

const camera = APP.scene?.systems["hubs-systems"].cameraSystem.viewingCamera;
camera.updateMatrices();

Expand All @@ -75,11 +62,6 @@ function transformMenu(world: HubsWorld, menu: EntityID) {
menuObj.lookAt(tmpVec2.setFromMatrixPosition(camera.matrixWorld));
menuObj.translateZ(sphere.radius);

if (center) {
menuObj.position.add(offset);
menuObj.matrixNeedsUpdate = true;
}

// TODO We need to handle the menu positioning when the player is inside the bounding sphere.
// For now we are defaulting to the current AFrame behavior.
} else {
Expand All @@ -102,11 +84,6 @@ function transformMenu(world: HubsWorld, menu: EntityID) {
tmpMat4.multiply(tmpMat42);
}

if (center) {
tmpMat42.makeTranslation(offset.x, offset.y, offset.z);
tmpMat4.multiply(tmpMat42);
}

setMatrixWorld(menuObj, tmpMat4);
}
ObjectMenuTransform.prevObjectRef[menu] = ObjectMenuTransform.targetObjectRef[menu];
Expand Down
69 changes: 53 additions & 16 deletions src/bit-systems/object-menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import { ObjectMenuTransformFlags } from "../inflators/object-menu-transform";
import { COLLISION_LAYERS } from "../constants";
import { FLOATY_OBJECT_FLAGS } from "../systems/floaty-object-system";
import { INSPECTABLE_FLAGS } from "./inspect-system";
import { ObjectMenuPositions } from "../prefabs/object-menu";
// Working variables.
const _vec3_1 = new Vector3();
const _vec3_2 = new Vector3();
Expand Down Expand Up @@ -282,26 +283,62 @@ function updateVisibility(world: HubsWorld, menu: EntityID, frozen: boolean) {
const media = MediaLoader.mediaRef[target];
const isVideoImagePdf = hasAnyComponent(world, [MediaVideo, MediaImage, MediaPDF], media);
const isMirrored = hasComponent(world, MediaMirrored, target);
const isDropped = hasComponent(world, ObjectDropped, target);
const isInspectable = hasComponent(world, Inspectable, target);
const isInspected = hasComponent(world, Inspected, target);
const isRefreshing = hasComponent(world, MediaRefresh, target);

// Parent visibility doesn't block raycasting, so we must set each button to be invisible
// TODO: Ensure that children of invisible entities aren't raycastable
world.eid2obj.get(ObjectMenu.unpinButtonRef[menu])!.visible = visible && isEntityPinned && canIPin;
world.eid2obj.get(ObjectMenu.pinButtonRef[menu])!.visible = visible && !isEntityPinned && canIPin;
world.eid2obj.get(ObjectMenu.removeButtonRef[menu])!.visible = visible && !isEntityPinned && canISpawnMove;
world.eid2obj.get(ObjectMenu.cloneButtonRef[menu])!.visible = visible && canISpawnMove;
world.eid2obj.get(ObjectMenu.rotateButtonRef[menu])!.visible =
visible && (!isEntityPinned || canIPin) && canISpawnMove;
world.eid2obj.get(ObjectMenu.scaleButtonRef[menu])!.visible =
visible && (!isEntityPinned || canIPin) && canISpawnMove;
world.eid2obj.get(ObjectMenu.openLinkButtonRef[menu])!.visible = visible;
world.eid2obj.get(ObjectMenu.dropButtonRef[menu])!.visible =
!isVideoImagePdf && !isEntityPinned && !hasComponent(world, ObjectDropped, target);
world.eid2obj.get(ObjectMenu.mirrorButtonRef[menu])!.visible = isVideoImagePdf && !isMirrored;
world.eid2obj.get(ObjectMenu.inspectButtonRef[menu])!.visible = isVideoImagePdf && isInspectable && !isInspected;
world.eid2obj.get(ObjectMenu.refreshButtonRef[menu])!.visible = visible && canIPin && canISpawnMove && !isRefreshing;
const openLinkButtonObj = world.eid2obj.get(ObjectMenu.openLinkButtonRef[menu])!;
const mirrorButtonObj = world.eid2obj.get(ObjectMenu.mirrorButtonRef[menu])!;
const inspectButtonObj = world.eid2obj.get(ObjectMenu.inspectButtonRef[menu])!;
const refreshButtonObj = world.eid2obj.get(ObjectMenu.refreshButtonRef[menu])!;

openLinkButtonObj.visible = visible;
mirrorButtonObj.visible = visible && isVideoImagePdf && !isMirrored;
inspectButtonObj.visible = visible && isInspectable && !isInspected;
refreshButtonObj.visible = visible && !isRefreshing;

if (canISpawnMove) {
world.eid2obj.get(ObjectMenu.unpinButtonRef[menu])!.visible = visible && isEntityPinned && canIPin;
world.eid2obj.get(ObjectMenu.pinButtonRef[menu])!.visible = visible && !isEntityPinned && canIPin;
world.eid2obj.get(ObjectMenu.removeButtonRef[menu])!.visible = visible && !isEntityPinned;
world.eid2obj.get(ObjectMenu.cloneButtonRef[menu])!.visible = visible;
world.eid2obj.get(ObjectMenu.rotateButtonRef[menu])!.visible = visible && (!isEntityPinned || canIPin);
world.eid2obj.get(ObjectMenu.scaleButtonRef[menu])!.visible = visible && (!isEntityPinned || canIPin);
world.eid2obj.get(ObjectMenu.dropButtonRef[menu])!.visible =
visible && !isVideoImagePdf && !isEntityPinned && !isDropped;

openLinkButtonObj.position.fromArray(ObjectMenuPositions.openLink);
mirrorButtonObj.position.fromArray(ObjectMenuPositions.mirror);
if (isEntityPinned) {
inspectButtonObj.position.fromArray(ObjectMenuPositions.inspectP);
} else {
inspectButtonObj.position.fromArray(ObjectMenuPositions.inspect);
}
refreshButtonObj.position.fromArray(ObjectMenuPositions.refresh);
} else {
[
ObjectMenu.unpinButtonRef[menu],
ObjectMenu.pinButtonRef[menu],
ObjectMenu.removeButtonRef[menu],
ObjectMenu.cloneButtonRef[menu],
ObjectMenu.rotateButtonRef[menu],
ObjectMenu.scaleButtonRef[menu],
ObjectMenu.dropButtonRef[menu]
].forEach(ref => {
world.eid2obj.get(ref)!.visible = false;
});

openLinkButtonObj.position.fromArray(ObjectMenuPositions.openLinkU);
mirrorButtonObj.position.fromArray(ObjectMenuPositions.mirrorU);
inspectButtonObj.position.fromArray(ObjectMenuPositions.inspectU);
refreshButtonObj.position.fromArray(ObjectMenuPositions.refreshU);
}

openLinkButtonObj.matrixNeedsUpdate = true;
mirrorButtonObj.matrixNeedsUpdate = true;
inspectButtonObj.matrixNeedsUpdate = true;
refreshButtonObj.matrixNeedsUpdate = true;

// This is a hacky way of giving a chance to the object-menu-transform system to center the menu based on the
// visible buttons without accounting for the background plane.
Expand Down
6 changes: 1 addition & 5 deletions src/inflators/object-menu-transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import { ObjectMenuTransform } from "../bit-components";

export const ObjectMenuTransformFlags = {
Enabled: 1 << 0,
Center: 1 << 1,
Scale: 1 << 2
Scale: 1 << 1
};

export type ObjectMenuTransformParams = {
Expand All @@ -21,9 +20,6 @@ const DEFAULTS = {
export function inflateObjectMenuTransform(world: HubsWorld, eid: EntityID, params: ObjectMenuTransformParams) {
params = Object.assign({}, DEFAULTS, params);
addComponent(world, ObjectMenuTransform, eid);
if (params.center === true) {
ObjectMenuTransform.flags[eid] |= ObjectMenuTransformFlags.Center;
}
if (params.scale === true) {
ObjectMenuTransform.flags[eid] |= ObjectMenuTransformFlags.Scale;
}
Expand Down
118 changes: 39 additions & 79 deletions src/prefabs/object-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ function InspectButton(props: Attrs) {
height={buttonHeight}
type={BUTTON_TYPES.DEFAULT}
holdable
holdableButton
icon={{ texture, cacheKey, scale: [0.165, 0.165, 0.165] }}
{...props}
/>
Expand Down Expand Up @@ -195,6 +196,7 @@ function RotateButton(props: Attrs) {
height={buttonHeight}
type={BUTTON_TYPES.ACTION}
holdable
holdableButton
icon={{ texture, cacheKey, scale: [0.165, 0.165, 0.165] }}
{...props}
/>
Expand Down Expand Up @@ -226,29 +228,35 @@ function ScaleButton(props: Attrs) {
height={buttonHeight}
type={BUTTON_TYPES.ACTION}
holdable
holdableButton
icon={{ texture, cacheKey, scale: [0.165, 0.165, 0.165] }}
{...props}
/>
);
}

// prettier-ignore
const position = {
export const ObjectMenuPositions = {
background: [ 0, 0, uiZ - 0.0005] as ArrayVec3,
pin: [ 0, 0.125, uiZ] as ArrayVec3,
unpin: [ 0, 0.125, uiZ] as ArrayVec3,
focus: [-0.25, 0.375, uiZ] as ArrayVec3,
track: [ 0.25, 0.375, uiZ] as ArrayVec3,
remove: [ 0, -0.275, uiZ] as ArrayVec3,
drop: [ 0, -0.425, uiZ] as ArrayVec3,
inspect: [ 0, -0.425, uiZ] as ArrayVec3,
deserializeDrawing: [ -0.3, -0.625, uiZ] as ArrayVec3,
openLink: [ 0.25, -0.275, uiZ] as ArrayVec3,
refresh: [ 0.25, -0.425, uiZ] as ArrayVec3,
clone: [-0.25, -0.275, uiZ] as ArrayVec3,
rotate: [ -0.2, -0.125, uiZ] as ArrayVec3,
mirror: [ 0, -0.125, uiZ] as ArrayVec3,
scale: [ 0.2, -0.125, uiZ] as ArrayVec3,
pin: [ 0, 0.225, uiZ] as ArrayVec3,
unpin: [ 0, 0.225, uiZ] as ArrayVec3,
focus: [-0.25, 0.575, uiZ] as ArrayVec3,
track: [ 0.25, 0.575, uiZ] as ArrayVec3,
remove: [ 0, -0.075, uiZ] as ArrayVec3,
drop: [ 0, -0.225, uiZ] as ArrayVec3,
inspect: [ 0, -0.225, uiZ] as ArrayVec3,
inspectU: [ 0, 0.075, uiZ] as ArrayVec3,
inspectP: [ 0, -0.075, uiZ] as ArrayVec3,
deserializeDrawing: [ -0.3, -0.425, uiZ] as ArrayVec3,
openLink: [ 0.25, -0.075, uiZ] as ArrayVec3,
openLinkU: [ 0, -0.075, uiZ] as ArrayVec3,
refresh: [ 0.25, -0.225, uiZ] as ArrayVec3,
refreshU: [ 0, -0.225, uiZ] as ArrayVec3,
clone: [-0.25, -0.075, uiZ] as ArrayVec3,
rotate: [ -0.2, 0.075, uiZ] as ArrayVec3,
mirror: [ 0, 0.075, uiZ] as ArrayVec3,
mirrorU: [ 0, 0.225, uiZ] as ArrayVec3,
scale: [ 0.2, 0.075, uiZ] as ArrayVec3,
};

export function ObjectMenuPrefab() {
Expand All @@ -273,7 +281,7 @@ export function ObjectMenuPrefab() {
return (
<entity
name="Interactable Object Menu"
objectMenuTransform={{ center: true }}
objectMenuTransform
objectMenu={{
backgroundRef: refs.background,
pinButtonRef: refs.pin,
Expand All @@ -295,75 +303,27 @@ export function ObjectMenuPrefab() {
<Plane
name={"Background"}
ref={refs.background}
position={position.background}
position={ObjectMenuPositions.background}
width={0.8}
height={0.8}
material={{ transparent: true, opacity: 0, side: FrontSide }}
renderOrder={APP.RENDER_ORDER.HUD_BACKGROUND}
layers={1 << Layers.CAMERA_LAYER_UI}
/>
<PinButton ref={refs.pin} position={position.pin} />
<UnpinButton ref={refs.unpin} position={position.unpin} />
<CameraFocusButton ref={refs.focus} position={position.focus} />
<CameraTrackButton ref={refs.track} position={position.track} />
<RemoveButton ref={refs.remove} position={position.remove} />
<DropButton ref={refs.drop} position={position.drop} />
<InspectButton ref={refs.inspect} position={position.inspect} />
<DeserializeDrawingButton ref={refs.deserializeDrawing} position={position.deserializeDrawing} />
<OpenLinkButton ref={refs.openLink} position={position.openLink} />
<RefreshButton ref={refs.refresh} position={position.refresh} />
<CloneButton ref={refs.clone} position={position.clone} />
<RotateButton ref={refs.rotate} position={position.rotate} />
<MirrorButton ref={refs.mirror} position={position.mirror} />
<ScaleButton ref={refs.scale} position={position.scale} />
<PinButton ref={refs.pin} position={ObjectMenuPositions.pin} />
<UnpinButton ref={refs.unpin} position={ObjectMenuPositions.unpin} />
<CameraFocusButton ref={refs.focus} position={ObjectMenuPositions.focus} />
<CameraTrackButton ref={refs.track} position={ObjectMenuPositions.track} />
<RemoveButton ref={refs.remove} position={ObjectMenuPositions.remove} />
<DropButton ref={refs.drop} position={ObjectMenuPositions.drop} />
<InspectButton ref={refs.inspect} position={ObjectMenuPositions.inspect} />
<DeserializeDrawingButton ref={refs.deserializeDrawing} position={ObjectMenuPositions.deserializeDrawing} />
<OpenLinkButton ref={refs.openLink} position={ObjectMenuPositions.openLink} />
<RefreshButton ref={refs.refresh} position={ObjectMenuPositions.refresh} />
<CloneButton ref={refs.clone} position={ObjectMenuPositions.clone} />
<RotateButton ref={refs.rotate} position={ObjectMenuPositions.rotate} />
<MirrorButton ref={refs.mirror} position={ObjectMenuPositions.mirror} />
<ScaleButton ref={refs.scale} position={ObjectMenuPositions.scale} />
</entity>
);
}

// ui interactable-ui
// layers mask 768
// withPermission: spawn_and_move_media

// pinButtonTip: Pinning will broadcast this object to Discord.

// sprite camera-action.png
// cameraFocusLabel focus

// sprite camera-action.png
// cameraTrackLabel track

// sprite remove-action.png
//

// visibility on content types:
// "contentTypes: video/ audio/ image/ application/vnd.apple.mpegurl application/x-mpegurl application/pdf; visible: false;"
// sprite drop-action.png
// hide-when-pinned-and-forbidden

// holdable
// sprite focus-action.png
// visibility-on-content-types="contentTypes: video/ audio/ image/ application/vnd.apple.mpegurl application/x-mpegurl application/pdf;"

// sprite deserialize-action.png
// class="deserialize-drawing" : lookup what else is using this

// text: open link

// text: refresh

// text: clone

// sprite: rotate-action.png
// hide-when-pinned-and-forbidden

// sprite: inspect-action.png
// visibility-on-content-types="contentTypes: video/ audio/ image/ application/vnd.apple.mpegurl application/x-mpegurl application/pdf;"

// sprite: scale-action.png
// hide-when-pinned-and-forbidden

// unprivileged menu...
// refreshButton
// openLinkButton
// mirrorMediaButton
// inspectButton
2 changes: 1 addition & 1 deletion src/prefabs/pdf-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export function PDFMenuPrefab() {
return (
<entity
name="PDF Menu"
objectMenuTransform={{ center: false, scale: true }}
objectMenuTransform={{ scale: true }}
pdfMenu={{
prevButtonRef: refPrev,
nextButtonRef: refNext,
Expand Down
2 changes: 1 addition & 1 deletion src/prefabs/video-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ export function VideoMenuPrefab() {
return (
<entity
name="Video Menu"
objectMenuTransform={{ center: false, scale: true }}
objectMenuTransform={{ scale: true }}
videoMenu={{
sliderRef,
timeLabelRef,
Expand Down

0 comments on commit fba1b4c

Please sign in to comment.