diff --git a/src/bit-components.js b/src/bit-components.js index a96dc66f63..6959257411 100644 --- a/src/bit-components.js +++ b/src/bit-components.js @@ -89,6 +89,8 @@ export const TextButton = defineComponent({ labelRef: Types.eid }); export const HoldableButton = defineComponent(); export const SingleActionButton = defineComponent(); export const Pen = defineComponent(); +export const PenActive = defineComponent(); +export const PenUpdated = defineComponent(); export const HoverMenuChild = defineComponent(); export const Static = defineComponent(); export const Inspectable = defineComponent(); diff --git a/src/components/tools/pen.js b/src/components/tools/pen.js index 7934aaa21c..1fa6f0576b 100644 --- a/src/components/tools/pen.js +++ b/src/components/tools/pen.js @@ -8,6 +8,9 @@ import { } from "../../systems/sound-effects-system"; import { waitForDOMContentLoaded } from "../../utils/async-utils"; import { convertStandardMaterial } from "../../utils/material-utils"; +import { addComponent } from "bitecs"; +import { PenUpdated } from "../../bit-components"; +import { shouldUseNewLoader } from "../../utils/bit-utils"; const pathsMap = { "player-right-controller": { @@ -479,11 +482,15 @@ AFRAME.registerComponent("pen", { populateEntities(targets) { targets.length = 0; - // TODO: Do not querySelectorAll on the entire scene every time anything changes! - const els = AFRAME.scenes[0].querySelectorAll(".collidable, .interactable, #environment-root"); - for (let i = 0; i < els.length; i++) { - if (!els[i].classList.contains("pen") && els[i].object3D) { - targets.push(els[i].object3D); + if (shouldUseNewLoader()) { + addComponent(APP.world, PenUpdated, this.el.eid); + } else { + // TODO: Do not querySelectorAll on the entire scene every time anything changes! + const els = AFRAME.scenes[0].querySelectorAll(".collidable, .interactable, #environment-root"); + for (let i = 0; i < els.length; i++) { + if (!els[i].classList.contains("pen") && els[i].object3D) { + targets.push(els[i].object3D); + } } } }, diff --git a/src/systems/bit-pen-system.ts b/src/systems/bit-pen-system.ts new file mode 100644 index 0000000000..1d867af1c1 --- /dev/null +++ b/src/systems/bit-pen-system.ts @@ -0,0 +1,39 @@ +import { defineQuery, enterQuery, entityExists, hasComponent, removeComponent } from "bitecs"; +import { HubsWorld } from "../app"; +import { CursorRaycastable, Pen, PenActive, PenUpdated, SceneRoot } from "../bit-components"; +import { PenComponent, PenToolsSystem } from "aframe"; +import { Object3D } from "three"; +import { EntityID } from "../utils/networking-types"; +import { anyEntityWith } from "../utils/bit-utils"; + +function addTarget(world: HubsWorld, target: EntityID, targets: Object3D[]) { + const object = APP.world.eid2obj.get(target)!; + if (!hasComponent(world, Pen, target) && !targets.includes(object)) { + targets.push(object); + } +} + +const raycastableQuery = defineQuery([CursorRaycastable]); +const sceneQuery = defineQuery([SceneRoot]); +const raycastableEnterQuery = enterQuery(raycastableQuery); +const penUpdateQuery = defineQuery([PenUpdated]); +const penUpdateEnterQuery = enterQuery(penUpdateQuery); +export function bitPenCompatSystem(world: HubsWorld, penToolsSystem: PenToolsSystem) { + const pen = penToolsSystem.myPen?.components["pen"] as PenComponent; + if (pen) { + removeComponent(world, PenUpdated, pen.el.eid); + } + if (anyEntityWith(world, PenActive)) { + penUpdateEnterQuery(world).forEach(eid => { + raycastableQuery(APP.world).forEach(eid => { + addTarget(world, eid, pen.targets); + }); + sceneQuery(world).forEach(eid => { + addTarget(world, eid, pen.targets); + }); + }); + raycastableEnterQuery(world).forEach(eid => { + addTarget(world, eid, pen.targets); + }); + } +} diff --git a/src/systems/hold-system.js b/src/systems/hold-system.js index 72316e506a..53b86b3043 100644 --- a/src/systems/hold-system.js +++ b/src/systems/hold-system.js @@ -14,13 +14,14 @@ import { AEntity, Networked, MediaLoader, - Deletable + Deletable, + PenActive } from "../bit-components"; import { canMove } from "../utils/permissions-utils"; import { canMove as canMoveEntity } from "../utils/bit-permissions-utils"; import { isPinned } from "../bit-systems/networking"; import { takeOwnership } from "../utils/take-ownership"; -import { findAncestorWithComponents } from "../utils/bit-utils"; +import { anyEntityWith, findAncestorWithComponents } from "../utils/bit-utils"; const GRAB_REMOTE_RIGHT = paths.actions.cursor.right.grab; const DROP_REMOTE_RIGHT = paths.actions.cursor.right.drop; @@ -87,7 +88,8 @@ function grab(world, userinput, queryHovered, held, grabPath) { target && userinput.get(grabPath) && (!isEntityPinned || AFRAME.scenes[0].is("frozen")) && - hasPermissionToGrab(world, target) + hasPermissionToGrab(world, target) && + !anyEntityWith(world, PenActive) ) { if (hasComponent(world, Networked, target)) { takeOwnership(world, target); @@ -99,7 +101,7 @@ function grab(world, userinput, queryHovered, held, grabPath) { function drop(world, userinput, queryHeld, held, dropPath) { const heldEid = queryHeld(world)[0]; - if (heldEid && userinput.get(dropPath)) { + if (heldEid && userinput.get(dropPath) && !anyEntityWith(world, PenActive)) { // TODO: Drop on ownership lost removeComponent(world, held, heldEid); diff --git a/src/systems/hubs-systems.ts b/src/systems/hubs-systems.ts index 521d536120..be7fc64c07 100644 --- a/src/systems/hubs-systems.ts +++ b/src/systems/hubs-systems.ts @@ -81,6 +81,7 @@ import { mixerAnimatableSystem } from "../bit-systems/mixer-animatable"; import { loopAnimationSystem } from "../bit-systems/loop-animation"; import { linkSystem } from "../bit-systems/link-system"; import { objectMenuTransformSystem } from "../bit-systems/object-menu-transform-system"; +import { bitPenCompatSystem } from "./bit-pen-system"; declare global { interface Window { @@ -277,6 +278,8 @@ export function mainTick(xrFrame: XRFrame, renderer: WebGLRenderer, scene: Scene videoTextureSystem(world); audioDebugSystem(world); + bitPenCompatSystem(world, aframeSystems["pen-tools"]); + deleteEntitySystem(world, aframeSystems.userinput); destroyAtExtremeDistanceSystem(world); removeNetworkedObjectButtonSystem(world); diff --git a/src/systems/pen-tools.js b/src/systems/pen-tools.js index 10dba58783..69e7fc9b67 100644 --- a/src/systems/pen-tools.js +++ b/src/systems/pen-tools.js @@ -1,4 +1,7 @@ +import { addComponent, removeComponent } from "bitecs"; import { waitForDOMContentLoaded } from "../utils/async-utils"; +import { Pen, PenActive } from "../bit-components"; +import { anyEntityWith } from "../utils/bit-utils"; // Used for tracking and managing pen tools in the scene AFRAME.registerSystem("pen-tools", { @@ -32,7 +35,12 @@ AFRAME.registerSystem("pen-tools", { if (this.myPen) { this.sceneEl.addState("pen"); + addComponent(APP.world, PenActive, this.myPen.eid); } else { + const pen = anyEntityWith(APP.world, Pen); + if (pen) { + removeComponent(APP.world, PenActive, pen); + } this.sceneEl.removeState("pen"); } } diff --git a/types/aframe.d.ts b/types/aframe.d.ts index 2a397bc53d..dbad160219 100644 --- a/types/aframe.d.ts +++ b/types/aframe.d.ts @@ -96,6 +96,14 @@ declare module "aframe" { invaders: PersonalSpaceInvader[]; } + interface PenToolsSystem extends ASystem { + myPen: AElement; + } + + interface PenComponent extends AComponent { + targets: Object3D[]; + } + interface TransformSelectedObjectSystem extends ASystem { startTransform(targetObj: Object3D, handObj: Object3D, data: object): void; stopTransform(): void; @@ -119,6 +127,7 @@ declare module "aframe" { /** @deprecated see bit-interaction-system */ interaction: InteractionSystem; nav: NavSystem; + "pen-tools": PenToolsSystem; }; emit(string, any?): void; addState(string): void;