зеркало из https://github.com/mozilla/hubs.git
Check in Dom's code
This commit is contained in:
Родитель
ebb0a4ef64
Коммит
a32dbd772a
|
@ -19021,6 +19021,11 @@
|
|||
"integrity": "sha1-6C5D6OsXBkaB5D+cjbxzHjF9GJI=",
|
||||
"dev": true
|
||||
},
|
||||
"bitecs": {
|
||||
"version": "0.3.38",
|
||||
"resolved": "https://registry.npmjs.org/bitecs/-/bitecs-0.3.38.tgz",
|
||||
"integrity": "sha512-vfz6wjPElg/0O7c06Ttb/BeWMInBspSuekRvNoLqPyEZV87tLWipxqpjtGPKZBhin8jv4OuQkrkZZfZpaqOndQ=="
|
||||
},
|
||||
"bluebird": {
|
||||
"version": "3.5.1",
|
||||
"resolved": "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz",
|
||||
|
|
|
@ -79,6 +79,7 @@
|
|||
"ammo-debug-drawer": "github:infinitelee/ammo-debug-drawer",
|
||||
"ammo.js": "github:mozillareality/ammo.js#hubs/master",
|
||||
"animejs": "github:mozillareality/anime#hubs/master",
|
||||
"bitecs": "^0.3.38",
|
||||
"buffered-interpolation": "github:Infinitelee/buffered-interpolation",
|
||||
"classnames": "^2.2.5",
|
||||
"color": "^3.1.2",
|
||||
|
|
266
src/App.js
266
src/App.js
|
@ -1,6 +1,238 @@
|
|||
import Store from "./storage/store";
|
||||
import MediaSearchStore from "./storage/media-search-store";
|
||||
import qsTruthy from "./utils/qs_truthy";
|
||||
import { addEntity, createWorld, defineQuery, enterQuery, exitQuery, hasComponent, pipe } from "bitecs";
|
||||
|
||||
import {
|
||||
Spin,
|
||||
Object3DTag,
|
||||
Networked,
|
||||
NETWORK_FLAGS,
|
||||
RemoteHoverTarget,
|
||||
CursorRaycastable,
|
||||
Holdable,
|
||||
OffersRemoteConstraint,
|
||||
Rigidbody,
|
||||
FloatyObject,
|
||||
Held
|
||||
} from "./utils/jsx-entity";
|
||||
import cubeSchema from "./network-schemas/interactable-cube";
|
||||
import { ACTIVATION_STATE, FIT, SHAPE } from "three-ammo/constants";
|
||||
import { COLLISION_LAYERS } from "./constants";
|
||||
|
||||
Object.defineProperties(THREE.Object3D.prototype, {
|
||||
components: {
|
||||
get: function() {
|
||||
// console.warn("Accessing 'components' on an Object3D");
|
||||
return {};
|
||||
}
|
||||
},
|
||||
classList: {
|
||||
get: function() {
|
||||
// console.warn("Accessing 'classlist' on an Object3D");
|
||||
return {
|
||||
contains() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const timeSystem = world => {
|
||||
const { time } = world;
|
||||
const now = performance.now();
|
||||
const delta = now - time.then;
|
||||
time.delta = delta;
|
||||
time.elapsed += delta;
|
||||
time.then = now;
|
||||
return world;
|
||||
};
|
||||
|
||||
const spinQuery = defineQuery([Spin, Object3DTag]);
|
||||
const spinSystem = world => {
|
||||
const ents = spinQuery(world);
|
||||
for (let i = 0; i < ents.length; i++) {
|
||||
const eid = ents[i];
|
||||
const obj = world.eid2obj.get(eid);
|
||||
const deltaSeconds = world.time.delta / 1000;
|
||||
obj.rotation.x += Spin.x[eid] * deltaSeconds;
|
||||
obj.rotation.y += Spin.y[eid] * deltaSeconds;
|
||||
obj.rotation.z += Spin.z[eid] * deltaSeconds;
|
||||
obj.matrixNeedsUpdate = true;
|
||||
}
|
||||
|
||||
return world;
|
||||
};
|
||||
|
||||
const rigidbodyQuery = defineQuery([Rigidbody, Object3DTag]);
|
||||
const rigidbodyEnteredQuery = enterQuery(rigidbodyQuery);
|
||||
const physicsCompatSystem = world => {
|
||||
const eids = rigidbodyEnteredQuery(world);
|
||||
for (let i = 0; i < eids.length; i++) {
|
||||
const eid = eids[i];
|
||||
// TODO this is weird, handling the fact that body-helper sets this up already...
|
||||
if (Rigidbody.bodyId[eid]) {
|
||||
console.log("Skipping", eid, "since it already has a body");
|
||||
continue;
|
||||
}
|
||||
|
||||
const physicsSystem = AFRAME.scenes[0].systems["hubs-systems"].physicsSystem;
|
||||
const obj = world.eid2obj.get(eid);
|
||||
const bodyId = physicsSystem.addBody(obj, {
|
||||
mass: 1,
|
||||
gravity: { x: 0, y: 0, z: 0 },
|
||||
linearDamping: 0.01,
|
||||
angularDamping: 0.01,
|
||||
linearSleepingThreshold: 1.6,
|
||||
angularSleepingThreshold: 2.5,
|
||||
angularFactor: { x: 1, y: 1, z: 1 },
|
||||
activationState: ACTIVATION_STATE.ACTIVE_TAG,
|
||||
type: "kinematic",
|
||||
emitCollisionEvents: false,
|
||||
disableCollision: false,
|
||||
collisionFilterGroup: 1,
|
||||
collisionFilterMask: 15,
|
||||
scaleAutoUpdate: false
|
||||
});
|
||||
Rigidbody.bodyId[eid] = bodyId;
|
||||
const shapeId = physicsSystem.addShapes(bodyId, obj, {
|
||||
type: SHAPE.BOX,
|
||||
fit: FIT.MANUAL,
|
||||
halfExtents: { x: 0.5, y: 0.5, z: 0.25 },
|
||||
margin: 0.01,
|
||||
offset: { x: 0, y: 0, z: 0 },
|
||||
orientation: { x: 0, y: 0, z: 0, w: 1 }
|
||||
});
|
||||
console.log("added body", eid, bodyId, shapeId);
|
||||
}
|
||||
|
||||
return world;
|
||||
};
|
||||
|
||||
const pendingNetworkData = [];
|
||||
NAF.connection.subscribeToDataChannel("test", function(_, _dataType, data) {
|
||||
pendingNetworkData.push(data);
|
||||
});
|
||||
|
||||
let lasteNetworkTick = 0;
|
||||
const networkedQuery = defineQuery([Networked]);
|
||||
const networkSystem = world => {
|
||||
if (world.time.elapsed - lasteNetworkTick < 1000) {
|
||||
return world;
|
||||
}
|
||||
lasteNetworkTick = world.time.elapsed;
|
||||
|
||||
// for (let i = 0; i < pendingNetworkData.length; i++) {
|
||||
// const deserializedEids = world.networkSchemas[0].deserialize(pendingNetworkData[i]);
|
||||
// for (let j = 0; j < deserializedEids.length; j++) {
|
||||
// const eid = deserializedEids[j];
|
||||
// if (hasComponent(world, Networked, eid) && !Networked.flags[eid] & NETWORK_FLAGS.INFLATED) {
|
||||
// const eid = world.networkSchemas[Networked.templateId[eid]].addEntity(world, eid);
|
||||
// const obj = world.eid2obj.get(eid);
|
||||
// world.scene.add(obj);
|
||||
// Networked.flags[eid] &= NETWORK_FLAGS.INFLATED & NETWORK_FLAGS.IS_REMOTE_ENTITY;
|
||||
// console.log("inflated", Networked.flags[eid], obj);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// pendingNetworkData.length = 0;
|
||||
|
||||
for (let i = 0; i < pendingNetworkData.length; i++) {
|
||||
const templateDatas = pendingNetworkData[i];
|
||||
const schema = world.networkSchemas[i];
|
||||
for (let j = 0; j < templateDatas.length; j++) {
|
||||
const entityDatas = templateDatas[j];
|
||||
if (entityDatas) {
|
||||
const netIds = Object.keys(entityDatas);
|
||||
for (let k = 0; k < netIds.length; k++) {
|
||||
const netId = netIds[k];
|
||||
const entityData = entityDatas[netId];
|
||||
let eid = world.netIdToEid.get(netId);
|
||||
if (eid === undefined) {
|
||||
eid = schema.addEntity(world);
|
||||
world.scene.add(world.eid2obj.get(eid));
|
||||
world.netIdToEid.set(netId, eid);
|
||||
console.log(`New network entity ${netId} -> ${eid}`);
|
||||
}
|
||||
console.log(`Got data for ${netId} -> ${eid}`, entityData);
|
||||
schema.deserialize(world, eid, entityData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pendingNetworkData.length = 0;
|
||||
|
||||
const entities = networkedQuery(world);
|
||||
|
||||
const data = [];
|
||||
let haveData = false;
|
||||
|
||||
for (let i = 0; i < entities.length; i++) {
|
||||
const eid = entities[i];
|
||||
const netId = Networked.networkId[eid];
|
||||
const schemaId = Networked.templateId[eid];
|
||||
const schema = world.networkSchemas[schemaId];
|
||||
const schemaData = data[schemaId] || {};
|
||||
schemaData[netId] = schema.serialize(world, eid);
|
||||
data[schemaId] = schemaData;
|
||||
haveData = true;
|
||||
}
|
||||
if (haveData) {
|
||||
NAF.connection.broadcastData("test", data);
|
||||
console.log(data);
|
||||
}
|
||||
|
||||
return world;
|
||||
};
|
||||
|
||||
const heldFloatyObjectsQuery = defineQuery([FloatyObject, Rigidbody, Held]);
|
||||
const exitedHeldFloadyObjectsQuery = exitQuery(heldFloatyObjectsQuery);
|
||||
const floatyObjectSystem = world => {
|
||||
const ents = exitedHeldFloadyObjectsQuery(world);
|
||||
const physicsSystem = AFRAME.scenes[0].systems["hubs-systems"].physicsSystem;
|
||||
for (let i = 0; i < ents.length; i++) {
|
||||
const eid = ents[i];
|
||||
if (!(hasComponent(world, FloatyObject, eid) && hasComponent(world, Rigidbody, eid))) continue;
|
||||
|
||||
const bodyId = Rigidbody.bodyId[eid];
|
||||
const bodyData = physicsSystem.bodyUuidToData.get(bodyId);
|
||||
|
||||
if (bodyData.linearVelocity < 1.85) {
|
||||
Object.assign(bodyData.options, {
|
||||
gravity: { x: 0, y: 0, z: 0 },
|
||||
angularDamping: 0.5,
|
||||
linearDamping: 0.95,
|
||||
linearSleepingThreshold: 0.1,
|
||||
angularSleepingThreshold: 0.1,
|
||||
collisionFilterMask: COLLISION_LAYERS.HANDS | COLLISION_LAYERS.MEDIA_FRAMES,
|
||||
type: "kinematic"
|
||||
});
|
||||
console.log(eid, "droppsed slow", bodyData.linearVelocity);
|
||||
} else {
|
||||
Object.assign(bodyData.options, {
|
||||
gravity: { x: 0, y: -2, z: 0 },
|
||||
angularDamping: 0.01,
|
||||
linearDamping: 0.01,
|
||||
linearSleepingThreshold: 1.6,
|
||||
angularSleepingThreshold: 2.5,
|
||||
collisionFilterMask: COLLISION_LAYERS.DEFAULT_INTERACTABLE
|
||||
});
|
||||
console.log(eid, "droppsed fast", bodyData.linearVelocity);
|
||||
}
|
||||
physicsSystem.updateBody(bodyId, bodyData.options);
|
||||
}
|
||||
|
||||
return world;
|
||||
};
|
||||
|
||||
const pipeline = pipe(
|
||||
timeSystem,
|
||||
networkSystem,
|
||||
physicsCompatSystem,
|
||||
floatyObjectSystem,
|
||||
spinSystem
|
||||
);
|
||||
|
||||
export class App {
|
||||
constructor() {
|
||||
|
@ -23,6 +255,29 @@ export class App {
|
|||
this.clippingState = new Set();
|
||||
this.mutedState = new Set();
|
||||
this.isAudioPaused = new Set();
|
||||
|
||||
this.world = createWorld();
|
||||
this.world.eid2obj = new Map(); // eid -> Object3D
|
||||
|
||||
// reserve entity 0 to avoid needing to check for undefined everywhere eid is checked for existance
|
||||
addEntity(this.world);
|
||||
|
||||
this.world.netIdToEid = new Map();
|
||||
|
||||
// used for JSX and GLTFs
|
||||
this.world.nameToComponent = {
|
||||
object3d: Object3DTag,
|
||||
spin: Spin,
|
||||
networked: Networked,
|
||||
"cursor-raycastable": CursorRaycastable,
|
||||
"remote-hover-target": RemoteHoverTarget,
|
||||
holdable: Holdable,
|
||||
"offers-remote-constraint": OffersRemoteConstraint,
|
||||
rigidbody: Rigidbody,
|
||||
"floaty-object": FloatyObject
|
||||
};
|
||||
|
||||
this.world.networkSchemas = [cubeSchema];
|
||||
}
|
||||
|
||||
// This gets called by a-scene to setup the renderer, camera, and audio listener
|
||||
|
@ -73,6 +328,15 @@ export class App {
|
|||
// TODO NAF currently depends on this, it should not
|
||||
sceneEl.clock = renderClock;
|
||||
|
||||
// TODO we should have 1 source of truth for time
|
||||
APP.world.time = {
|
||||
delta: 0,
|
||||
elapsed: 0,
|
||||
then: performance.now()
|
||||
};
|
||||
|
||||
APP.world.scene = sceneEl.object3D;
|
||||
|
||||
// Main RAF loop
|
||||
function mainTick(_rafTime, xrFrame) {
|
||||
// TODO we should probably be using time from the raf loop itself
|
||||
|
@ -82,6 +346,8 @@ export class App {
|
|||
// TODO pass this into systems that care about it (like input) once they are moved into this loop
|
||||
sceneEl.frame = xrFrame;
|
||||
|
||||
pipeline(APP.world);
|
||||
|
||||
// Tick AFrame systems and components
|
||||
if (sceneEl.isPlaying) {
|
||||
sceneEl.tick(time, delta);
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import { addComponent, removeComponent } from "bitecs";
|
||||
import { CONSTANTS } from "three-ammo";
|
||||
import { Rigidbody } from "../utils/jsx-entity";
|
||||
const ACTIVATION_STATE = CONSTANTS.ACTIVATION_STATE,
|
||||
TYPE = CONSTANTS.TYPE;
|
||||
|
||||
const ACTIVATION_STATES = [
|
||||
export const ACTIVATION_STATES = [
|
||||
ACTIVATION_STATE.ACTIVE_TAG,
|
||||
ACTIVATION_STATE.ISLAND_SLEEPING,
|
||||
ACTIVATION_STATE.WANTS_DEACTIVATION,
|
||||
|
@ -42,6 +44,10 @@ AFRAME.registerComponent("body-helper", {
|
|||
init2: function() {
|
||||
this.el.object3D.updateMatrices();
|
||||
this.uuid = this.system.addBody(this.el.object3D, this.data);
|
||||
const eid = this.el.object3D.eid;
|
||||
console.log("Adding rigidbody", eid, this.uuid, Rigidbody.bodyId[eid]);
|
||||
addComponent(APP.world, Rigidbody, eid);
|
||||
Rigidbody.bodyId[eid] = this.uuid; //uuid is a lie, it's actually an int
|
||||
},
|
||||
|
||||
update: function(prevData) {
|
||||
|
@ -53,6 +59,8 @@ AFRAME.registerComponent("body-helper", {
|
|||
remove: function() {
|
||||
if (this.uuid !== -1) {
|
||||
this.system.removeBody(this.uuid);
|
||||
const eid = this.el.object3D.eid;
|
||||
removeComponent(APP.world, Rigidbody, eid);
|
||||
}
|
||||
this.alive = false;
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ const SYNC_DURATION_MS = 5000;
|
|||
//
|
||||
// This component, when added, will re-send a isFirstSync message for the networked object is it attached to
|
||||
// every SYNC_DURATION_MS milliseconds.
|
||||
AFRAME.registerComponent("periodic-full-syncs", {
|
||||
AFRAME.registerComponent("xx-periodic-full-syncs", {
|
||||
init() {
|
||||
this.lastSync = 0;
|
||||
},
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
import { addComponent, removeComponent } from "bitecs";
|
||||
import { Pinnable, Pinned } from "../utils/jsx-entity";
|
||||
|
||||
AFRAME.registerComponent("pinnable", {
|
||||
schema: {
|
||||
pinned: { default: false }
|
||||
|
@ -13,10 +16,18 @@ AFRAME.registerComponent("pinnable", {
|
|||
this.el.addEventListener("owned-pager-page-changed", this._persist);
|
||||
|
||||
this.el.addEventListener("owned-video-state-changed", this._persistAndAnimate);
|
||||
|
||||
addComponent(APP.world, Pinnable, this.el.object3D.eid);
|
||||
},
|
||||
|
||||
update() {
|
||||
this._animate();
|
||||
|
||||
if (this.data.pinned) {
|
||||
addComponent(APP.world, Pinned, this.el.object3D.eid);
|
||||
} else {
|
||||
removeComponent(APP.world, Pinned, this.el.object3D.eid);
|
||||
}
|
||||
},
|
||||
|
||||
_persistAndAnimate() {
|
||||
|
|
|
@ -1,9 +1,49 @@
|
|||
export function isTagged(el, tag) {
|
||||
return el && el.components && el.components.tags && el.components.tags.data[tag];
|
||||
import { addComponent, hasComponent, removeComponent } from "bitecs";
|
||||
import {
|
||||
Holdable,
|
||||
OffersRemoteConstraint,
|
||||
HandCollisionTarget,
|
||||
OffersHandConstraint,
|
||||
TogglesHoveredActionSet,
|
||||
SingleActionButton,
|
||||
HoldableButton,
|
||||
Pen,
|
||||
HoverMenuChild,
|
||||
Static,
|
||||
Inspectable,
|
||||
PreventAudioBoost,
|
||||
IgnoreSpaceBubble
|
||||
} from "../utils/jsx-entity";
|
||||
|
||||
const tag2ecs = {
|
||||
isHoldable: Holdable,
|
||||
offersRemoteConstraint: OffersRemoteConstraint,
|
||||
isHandCollisionTarget: HandCollisionTarget,
|
||||
offersHandConstraint: OffersHandConstraint,
|
||||
togglesHoveredActionSet: TogglesHoveredActionSet,
|
||||
singleActionButton: SingleActionButton,
|
||||
holdableButton: HoldableButton,
|
||||
isPen: Pen,
|
||||
isHoverMenuChild: HoverMenuChild,
|
||||
isStatic: Static,
|
||||
inspectable: Inspectable,
|
||||
preventAudioBoost: PreventAudioBoost,
|
||||
ignoreSpaceBubble: IgnoreSpaceBubble
|
||||
};
|
||||
|
||||
export function isTagged(elOrObject3D, tag) {
|
||||
return elOrObject3D && hasComponent(APP.world, tag2ecs[tag], elOrObject3D.eid);
|
||||
}
|
||||
|
||||
export function setTag(el, tag, value = true) {
|
||||
return (el.components.tags.data[tag] = !!value);
|
||||
export function setTag(elOrObject3D, tag, value = true) {
|
||||
const eid = elOrObject3D.eid;
|
||||
const Component = tag2ecs[tag];
|
||||
if (value) {
|
||||
addComponent(APP.world, Component, eid);
|
||||
} else {
|
||||
removeComponent(APP.world, Component, eid);
|
||||
}
|
||||
return !!value;
|
||||
}
|
||||
|
||||
AFRAME.registerComponent("tags", {
|
||||
|
@ -27,12 +67,19 @@ AFRAME.registerComponent("tags", {
|
|||
console.warn("Do not edit tags with .setAttribute");
|
||||
}
|
||||
this.didUpdateOnce = true;
|
||||
|
||||
const eid = this.el.eid;
|
||||
Object.entries(this.data).forEach(function([tagName, isSet]) {
|
||||
if (isSet) {
|
||||
addComponent(APP.world, tag2ecs[tagName], eid);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
remove() {
|
||||
const interaction = this.el.sceneEl.systems.interaction;
|
||||
if (interaction.isHeld(this.el)) {
|
||||
interaction.release(this.el);
|
||||
if (interaction.isHeld(this.el.object3D)) {
|
||||
interaction.release(this.el.object3D);
|
||||
this.el.sceneEl.systems["hubs-systems"].constraintsSystem.release(this.el);
|
||||
}
|
||||
}
|
||||
|
|
28
src/hub.html
28
src/hub.html
|
@ -890,18 +890,6 @@
|
|||
|
||||
<a-entity id="camera-counter" networked-counter="max: 1;"></a-entity>
|
||||
|
||||
<a-entity id="drawing-manager" drawing-manager="penSpawner: #pen-spawner"></a-entity>
|
||||
|
||||
<a-entity
|
||||
id="right-cursor-controller"
|
||||
cursor-controller="cursor: #right-cursor; camera: #avatar-pov-node; cursorVisual: #right-cursor-visual;"
|
||||
></a-entity>
|
||||
|
||||
<a-entity
|
||||
id="left-cursor-controller"
|
||||
cursor-controller="cursor: #left-cursor; camera: #avatar-pov-node; cursorVisual: #left-cursor-visual;"
|
||||
></a-entity>
|
||||
|
||||
<a-entity
|
||||
id="right-cursor"
|
||||
body-helper="type: kinematic; disableCollision: true; collisionFilterGroup: 8; collisionFilterMask: 0; scaleAutoUpdate: false; activationState: disable_deactivation;"
|
||||
|
@ -930,6 +918,16 @@
|
|||
></a-entity>
|
||||
</a-entity>
|
||||
|
||||
<a-entity
|
||||
id="right-cursor-controller"
|
||||
cursor-controller="cursor: #right-cursor; camera: #avatar-pov-node; cursorVisual: #right-cursor-visual;"
|
||||
></a-entity>
|
||||
|
||||
<a-entity
|
||||
id="left-cursor-controller"
|
||||
cursor-controller="cursor: #left-cursor; camera: #avatar-pov-node; cursorVisual: #left-cursor-visual;"
|
||||
></a-entity>
|
||||
|
||||
<!-- Avatar Rig -->
|
||||
<a-entity
|
||||
id="avatar-rig"
|
||||
|
@ -945,8 +943,8 @@
|
|||
id="player-hud"
|
||||
hud-controller="head: #viewing-camera;"
|
||||
vr-mode-toggle-class="class: ui"
|
||||
vr-mode-toggle-visibility
|
||||
vr-mode-toggle-playing__hud-controller
|
||||
x-vr-mode-toggle-visibility
|
||||
x-vr-mode-toggle-playing__hud-controller
|
||||
>
|
||||
<a-entity visibility-while-frozen="requireHoverOnNonMobile: false;" rotation="30 0 0">
|
||||
<a-entity class="leave-btn" position="0 0 0" scale="0.7 0.7 0.7" leave-room-button is-remote-hover-target tags="singleActionButton: true;">
|
||||
|
@ -1124,6 +1122,8 @@
|
|||
"
|
||||
></a-entity>
|
||||
|
||||
<a-entity id="drawing-manager" drawing-manager="penSpawner: #pen-spawner"></a-entity>
|
||||
|
||||
<a-entity class="ui interactable-ui vr-notice" follow-in-fov="target: #avatar-pov-node; offset: 0 0 -0.8; angle: 39;" visible="false">
|
||||
<a-entity slice9="width: 0.8; height: 0.25; left: 64; top: 64; right: 66; bottom: 66; opacity: 1.0; src: button"
|
||||
text-button="
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import cameraSchema from "./network-schemas/interactable-camera";
|
||||
import { cubeSchema } from "./network-schemas/interactable-cube";
|
||||
|
||||
function registerNetworkSchemas() {
|
||||
const vectorRequiresUpdate = epsilon => {
|
||||
|
@ -251,6 +252,7 @@ function registerNetworkSchemas() {
|
|||
});
|
||||
|
||||
NAF.schemas.add(cameraSchema);
|
||||
NAF.schemas.add(cubeSchema);
|
||||
|
||||
NAF.schemas.add({
|
||||
template: "#template-waypoint-avatar",
|
||||
|
|
|
@ -56,9 +56,9 @@ function ActionButton({
|
|||
...props
|
||||
}) {
|
||||
return (
|
||||
<entity {...buttonStyles[type]} is-remote-hover-target tags="singleActionButton: true;" {...props}>
|
||||
<a-entity {...buttonStyles[type]} is-remote-hover-target tags="singleActionButton: true;" {...props}>
|
||||
{image && (
|
||||
<entity
|
||||
<a-entity
|
||||
sprite
|
||||
className={iconClassName}
|
||||
icon-button={{ image, hoverImage }}
|
||||
|
@ -66,13 +66,13 @@ function ActionButton({
|
|||
position={[0, 0, 0.001]}
|
||||
/>
|
||||
)}
|
||||
</entity>
|
||||
</a-entity>
|
||||
);
|
||||
}
|
||||
|
||||
function RecordButton() {
|
||||
return (
|
||||
<entity
|
||||
<a-entity
|
||||
className="record-button"
|
||||
is-remote-hover-target
|
||||
tags={{ singleActionButton: true }}
|
||||
|
@ -80,21 +80,21 @@ function RecordButton() {
|
|||
scale={[0.75, 0.75, 0.75]}
|
||||
{...buttonStyles["rounded-action-button"]}
|
||||
>
|
||||
<entity
|
||||
<a-entity
|
||||
sprite=""
|
||||
className="record-icon"
|
||||
icon-button={{ image: "record-action.png", hoverImage: "record-action.png" }}
|
||||
scale={[0.175, 0.175, 0.175]}
|
||||
position={[0, 0, 0.001]}
|
||||
/>
|
||||
<entity
|
||||
<a-entity
|
||||
sprite
|
||||
className="record-alpha-icon"
|
||||
icon-button={{ image: "record-action-alpha.png", hoverImage: "record-action-alpha.png" }}
|
||||
scale={[0.175, 0.175, 0.175]}
|
||||
position={[0, 0, 0.001]}
|
||||
/>
|
||||
</entity>
|
||||
</a-entity>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -113,7 +113,7 @@ function InteractableCamera() {
|
|||
const screenGeometry = new THREE.PlaneBufferGeometry(width, width / aspect);
|
||||
|
||||
return (
|
||||
<entity
|
||||
<a-entity
|
||||
className="interactable"
|
||||
body-helper={{ type: "dynamic", mass: "0.001", collisionFilterGroup: "1", collisionFilterMask: "8" }}
|
||||
camera-tool={{ labelRef, snapButtonRef, snapMenuRef, screenRef, selfieScreenRef }}
|
||||
|
@ -146,8 +146,8 @@ function InteractableCamera() {
|
|||
scale={[-2, 2, 2]}
|
||||
/>
|
||||
|
||||
<entity className="ui interactable-ui" ref={snapMenuRef}>
|
||||
<entity
|
||||
<a-entity className="ui interactable-ui" ref={snapMenuRef}>
|
||||
<a-entity
|
||||
ref={labelRef}
|
||||
position={[0, 0.15, 0.081]}
|
||||
text={{ value: 3, textAlign: "center", color: "#fafafa", fontSize: 0.09 }}
|
||||
|
@ -159,7 +159,7 @@ function InteractableCamera() {
|
|||
scale={[0.75, 0.75, 0.75]}
|
||||
/>
|
||||
<ActionButton className="label-background" position={[0, 0.15, 0.08]} scale={[0.75, 0.75, 0.75]} />
|
||||
<entity
|
||||
<a-entity
|
||||
className="duration"
|
||||
text="value: 5s; textAlign: center; color: #fafafa; fontSize: 0.09;"
|
||||
position={[0, -0.15, 0.09]}
|
||||
|
@ -198,8 +198,8 @@ function InteractableCamera() {
|
|||
scale={[0.75, 0.75, 0.75]}
|
||||
image="stop-action.png"
|
||||
/>
|
||||
</entity>
|
||||
<entity
|
||||
</a-entity>
|
||||
<a-entity
|
||||
className="ui interactable-ui camera-menu"
|
||||
visibility-while-frozen={{ withinDistance: 100, withPermission: "spawn_camera" }}
|
||||
>
|
||||
|
@ -233,14 +233,14 @@ function InteractableCamera() {
|
|||
iconScale={0.165}
|
||||
image="remove-action.png"
|
||||
/>
|
||||
</entity>
|
||||
</entity>
|
||||
</a-entity>
|
||||
</a-entity>
|
||||
);
|
||||
}
|
||||
|
||||
export default {
|
||||
template: "#interactable-camera",
|
||||
createEntity: function() {
|
||||
addEntity: function() {
|
||||
const c = <InteractableCamera />;
|
||||
console.log(c);
|
||||
return renderAsAframeEntity(c);
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
import { createElementEntity, createRef, Networked, Spin } from "../utils/jsx-entity";
|
||||
/** @jsx createElementEntity */
|
||||
|
||||
import { renderAsAframeEntity } from "../utils/jsx-entity";
|
||||
import { defineQuery, Changed } from "bitecs";
|
||||
|
||||
import * as bitecs from "bitecs";
|
||||
window.$B = bitecs;
|
||||
|
||||
function ab2str(buffer) {
|
||||
var binary = "";
|
||||
var bytes = new Uint8Array(buffer);
|
||||
var len = bytes.byteLength;
|
||||
for (var i = 0; i < len; i++) {
|
||||
binary += String.fromCharCode(bytes[i]);
|
||||
}
|
||||
return window.btoa(binary);
|
||||
}
|
||||
|
||||
function str2ab(base64) {
|
||||
var binary_string = window.atob(base64);
|
||||
var len = binary_string.length;
|
||||
var bytes = new Uint8Array(len);
|
||||
for (var i = 0; i < len; i++) {
|
||||
bytes[i] = binary_string.charCodeAt(i);
|
||||
}
|
||||
return bytes.buffer;
|
||||
}
|
||||
|
||||
// const serializer = defineSerializer([Changed(Spin)]);
|
||||
// const deserializer = defineDeserializer([Changed(Spin)]);
|
||||
|
||||
function InteractableCube() {
|
||||
return (
|
||||
<entity
|
||||
spin={{ x: 0, y: 90 * THREE.Math.DEG2RAD, z: 0 }}
|
||||
scale={[1, 1, 0.5]}
|
||||
cursor-raycastable
|
||||
remote-hover-target
|
||||
offers-remote-constraint
|
||||
holdable
|
||||
rigidbody
|
||||
>
|
||||
<THREE.Mesh geometry={new THREE.BoxBufferGeometry()} material={new THREE.MeshStandardMaterial()} />
|
||||
</entity>
|
||||
);
|
||||
}
|
||||
|
||||
const changedSpinQuery = defineQuery([Changed(Spin)]);
|
||||
window.cs = changedSpinQuery;
|
||||
|
||||
export const cubeSchema = {
|
||||
template: "#interactable-cube-media",
|
||||
addEntity: function() {
|
||||
return renderAsAframeEntity(
|
||||
<a-entity>
|
||||
<InteractableCube />
|
||||
</a-entity>,
|
||||
APP.world
|
||||
);
|
||||
},
|
||||
serialize: function(el, isFullSync) {
|
||||
const eid = el.object3D.children[0].eid;
|
||||
const obj = APP.world.eid2obj.get(eid);
|
||||
|
||||
const spinChanged = changedSpinQuery(APP.world).includes(eid);
|
||||
// TODO position diff
|
||||
if (!(isFullSync || spinChanged)) return null;
|
||||
|
||||
const data = [obj.position.toArray(), Spin.x[eid], Spin.y[eid], Spin.z[eid]];
|
||||
console.log("sending data", eid, data);
|
||||
|
||||
return data;
|
||||
},
|
||||
deserialize: function(el, data) {
|
||||
const eid = el.object3D.children[0].eid;
|
||||
const obj = APP.world.eid2obj.get(eid);
|
||||
obj.position.fromArray(data[0]);
|
||||
Spin.x[eid] = data[1];
|
||||
Spin.y[eid] = data[2];
|
||||
Spin.z[eid] = data[3];
|
||||
},
|
||||
components: []
|
||||
};
|
||||
|
||||
export default {
|
||||
name: "#interactable-cube-media",
|
||||
addEntity: function(world, rootEid) {
|
||||
const obj = renderAsAframeEntity(<InteractableCube />, world, rootEid);
|
||||
return obj.eid;
|
||||
},
|
||||
serialize(world, eid) {
|
||||
// const data = serializer(entities);
|
||||
// const str = ab2str(data);
|
||||
// if (str) console.log("serialize", entities, data, str, str2ab(str));
|
||||
// return str || null;
|
||||
//
|
||||
const obj = world.eid2obj.get(eid);
|
||||
const data = [obj.position.toArray(), Spin.x[eid], Spin.y[eid], Spin.z[eid]];
|
||||
console.log("sending data", eid, data);
|
||||
return data;
|
||||
},
|
||||
|
||||
deserialize(world, eid, data) {
|
||||
// const data = str2ab(str);
|
||||
// console.log("got data", data);
|
||||
// const ents = deserializer(APP.world, data, DESERIALIZE_MODE.MAP);
|
||||
// console.log(ents);
|
||||
// return ents;
|
||||
const obj = world.eid2obj.get(eid);
|
||||
obj.position.fromArray(data[0]);
|
||||
Spin.x[eid] = data[1];
|
||||
Spin.y[eid] = data[2];
|
||||
Spin.z[eid] = data[3];
|
||||
}
|
||||
};
|
|
@ -3,6 +3,8 @@ import nextTick from "./utils/next-tick";
|
|||
import { hackyMobileSafariTest } from "./utils/detect-touchscreen";
|
||||
import { SignInMessages } from "./react-components/auth/SignInModal";
|
||||
|
||||
import { createNetworkedEntity } from "./utils/jsx-entity";
|
||||
|
||||
const isBotMode = qsTruthy("bot");
|
||||
const isMobile = AFRAME.utils.device.isMobile();
|
||||
const forceEnableTouchscreen = hackyMobileSafariTest();
|
||||
|
@ -417,19 +419,34 @@ export default class SceneEntryManager {
|
|||
|
||||
_setupCamera = () => {
|
||||
this.scene.addEventListener("action_toggle_camera", () => {
|
||||
if (!this.hubChannel.can("spawn_camera")) return;
|
||||
const myCamera = this.scene.systems["camera-tools"].getMyCamera();
|
||||
const avatarPov = document.querySelector("#avatar-pov-node").object3D;
|
||||
|
||||
if (myCamera) {
|
||||
myCamera.parentNode.removeChild(myCamera);
|
||||
} else {
|
||||
const entity = NAF.createNetworkedEntity("#interactable-camera");
|
||||
entity.setAttribute("offset-relative-to", {
|
||||
target: "#avatar-pov-node",
|
||||
offset: { x: 0, y: 0, z: -1.5 }
|
||||
});
|
||||
this.scene.appendChild(entity);
|
||||
}
|
||||
// const INTERACTABLE_CUBE = 0;
|
||||
// const eid = createNetworkedEntity(APP.world, INTERACTABLE_CUBE);
|
||||
// const obj = APP.world.eid2obj.get(eid);
|
||||
// obj.position.copy(avatarPov.localToWorld(new THREE.Vector3(0, 0, -1.5)));
|
||||
// this.scene.object3D.add(obj);
|
||||
|
||||
const aframeWrapper = NAF.createNetworkedEntity("#interactable-cube-media");
|
||||
this.scene.appendChild(aframeWrapper);
|
||||
|
||||
const eid = aframeWrapper.object3D.children[0].eid;
|
||||
const obj = APP.world.eid2obj.get(eid);
|
||||
obj.position.copy(avatarPov.localToWorld(new THREE.Vector3(0, 0, -1.5)));
|
||||
|
||||
// if (!this.hubChannel.can("spawn_camera")) return;
|
||||
// const myCamera = this.scene.systems["camera-tools"].getMyCamera();
|
||||
|
||||
// if (myCamera) {
|
||||
// myCamera.parentNode.removeChild(myCamera);
|
||||
// } else {
|
||||
// const entity = NAF.createNetworkedEntity("#interactable-camera");
|
||||
// entity.setAttribute("offset-relative-to", {
|
||||
// target: "#avatar-pov-node",
|
||||
// offset: { x: 0, y: 0, z: -1.5 }
|
||||
// });
|
||||
// this.scene.appendChild(entity);
|
||||
// }
|
||||
});
|
||||
|
||||
this.scene.addEventListener("photo_taken", e => this.hubChannel.sendMessage({ src: e.detail }, "photo"));
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { isTagged } from "../components/tags";
|
||||
import { CAMERA_MODE_INSPECT } from "./camera-system";
|
||||
|
||||
export class SingleActionButtonSystem {
|
||||
|
@ -11,12 +12,7 @@ export class SingleActionButtonSystem {
|
|||
return;
|
||||
}
|
||||
const hovered = interaction.state.rightRemote.hovered;
|
||||
if (
|
||||
hovered &&
|
||||
userinput.get(interaction.options.rightRemote.grabPath) &&
|
||||
hovered.components.tags &&
|
||||
hovered.components.tags.data.singleActionButton
|
||||
) {
|
||||
if (hovered && userinput.get(interaction.options.rightRemote.grabPath) && isTagged(hovered, "singleActionButton")) {
|
||||
this.didInteractThisFrame = true;
|
||||
hovered.object3D.dispatchEvent({
|
||||
type: "interact",
|
||||
|
@ -27,8 +23,7 @@ export class SingleActionButtonSystem {
|
|||
if (
|
||||
hovered2 &&
|
||||
userinput.get(interaction.options.leftRemote.grabPath) &&
|
||||
hovered2.components.tags &&
|
||||
hovered2.components.tags.data.singleActionButton
|
||||
isTagged(hovered2, "singleActionButton")
|
||||
) {
|
||||
this.didInteractThisFrame = true;
|
||||
hovered2.object3D.dispatchEvent({
|
||||
|
@ -45,6 +40,7 @@ export class HoldableButtonSystem {
|
|||
const held = interaction.state.rightRemote.held;
|
||||
|
||||
if (this.prevHeld && this.prevHeld !== held) {
|
||||
// TODO: Should this check for holdable button?
|
||||
this.prevHeld.object3D.dispatchEvent({
|
||||
type: "holdable-button-up",
|
||||
object3D: interaction.options.rightRemote.entity.object3D
|
||||
|
@ -62,16 +58,18 @@ export class HoldableButtonSystem {
|
|||
const heldLeft = interaction.state.leftRemote.held;
|
||||
|
||||
if (this.prevHeldLeft && this.prevHeldLeft !== heldLeft) {
|
||||
this.prevHeldLeft.object3D.dispatchEvent({
|
||||
type: "holdable-button-up",
|
||||
object3D: interaction.options.leftRemote.entity.object3D
|
||||
});
|
||||
isTagged(this.prevHeldLeft, "holdableButton") &&
|
||||
this.prevHeldLeft.object3D.dispatchEvent({
|
||||
type: "holdable-button-up",
|
||||
object3D: interaction.options.leftRemote.entity.object3D
|
||||
});
|
||||
}
|
||||
if (heldLeft && this.prevHeldLeft !== heldLeft) {
|
||||
heldLeft.object3D.dispatchEvent({
|
||||
type: "holdable-button-down",
|
||||
object3D: interaction.options.leftRemote.entity.object3D
|
||||
});
|
||||
isTagged(this.heldLeft, "holdableButton") &&
|
||||
heldLeft.object3D.dispatchEvent({
|
||||
type: "holdable-button-down",
|
||||
object3D: interaction.options.leftRemote.entity.object3D
|
||||
});
|
||||
}
|
||||
|
||||
this.prevHeldLeft = heldLeft;
|
||||
|
@ -97,7 +95,8 @@ const hasButtonComponent = (function() {
|
|||
})();
|
||||
|
||||
function getHoverableButton(hovered) {
|
||||
if (!hovered) return null;
|
||||
if (!(hovered && hovered.el)) return null;
|
||||
hovered = hovered.el;
|
||||
if (
|
||||
hasButtonComponent(hovered.components) ||
|
||||
hovered.classList.contains("teleport-waypoint-icon") ||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
/* global NAF AFRAME */
|
||||
import { CONSTANTS } from "three-ammo";
|
||||
import { isTagged } from "../components/tags";
|
||||
import { Rigidbody } from "../utils/jsx-entity";
|
||||
const ACTIVATION_STATE = CONSTANTS.ACTIVATION_STATE;
|
||||
|
||||
export class ConstraintsSystem {
|
||||
|
@ -25,66 +27,73 @@ export class ConstraintsSystem {
|
|||
this.constraintPairs = {};
|
||||
}
|
||||
|
||||
tickInteractor(constraintTag, entityId, state, prevState) {
|
||||
tickInteractor(constraintTag, interactorEid, state, prevState) {
|
||||
if (!this.physicsSystem) return;
|
||||
|
||||
// TODO what is spawning?
|
||||
|
||||
if (prevState.held === state.held) {
|
||||
if (
|
||||
!state.spawning &&
|
||||
prevState.spawning &&
|
||||
state.held &&
|
||||
state.held.components.tags &&
|
||||
state.held.components.tags.data[constraintTag]
|
||||
) {
|
||||
state.held.setAttribute("body-helper", {
|
||||
type: "dynamic",
|
||||
activationState: ACTIVATION_STATE.DISABLE_DEACTIVATION
|
||||
});
|
||||
const heldEntityId = state.held.id;
|
||||
const bodyUuid = state.held.components["body-helper"].uuid;
|
||||
const targetEl = document.querySelector(`#${entityId}`);
|
||||
const targetUuid = targetEl.components["body-helper"].uuid;
|
||||
if (bodyUuid !== -1 && targetUuid !== -1) {
|
||||
this.physicsSystem.addConstraint(entityId, bodyUuid, targetUuid, {});
|
||||
if (!this.constraintPairs[heldEntityId]) {
|
||||
this.constraintPairs[heldEntityId] = [];
|
||||
if (!state.spawning && prevState.spawning && state.held && isTagged(state.held, constraintTag)) {
|
||||
const eid = state.held.object3D.eid;
|
||||
const bodyId = Rigidbody.bodyId[eid];
|
||||
|
||||
const bodyOptions = this.physicsSystem.bodyUuidToData.get(bodyId).options;
|
||||
bodyOptions.type = "dynamic";
|
||||
bodyOptions.activationState = ACTIVATION_STATE.DISABLE_DEACTIVATION;
|
||||
this.physicsSystem.updateBody(bodyId, bodyOptions);
|
||||
|
||||
const interactorBodyId = Rigidbody.bodyId[interactorEid];
|
||||
if (bodyId !== -1 && interactorBodyId !== -1) {
|
||||
this.physicsSystem.addConstraint(interactorEid, bodyId, interactorBodyId, {});
|
||||
if (!this.constraintPairs[eid]) {
|
||||
this.constraintPairs[eid] = [];
|
||||
}
|
||||
this.constraintPairs[heldEntityId].push(entityId);
|
||||
this.constraintPairs[eid].push(interactorEid);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (prevState.held && prevState.held.components.tags && prevState.held.components.tags.data[constraintTag]) {
|
||||
const heldEntityId = prevState.held.id;
|
||||
if (this.constraintPairs[heldEntityId] && this.constraintPairs[heldEntityId].indexOf(entityId) !== -1) {
|
||||
this.constraintPairs[heldEntityId].splice(this.constraintPairs[heldEntityId].indexOf(entityId), 1);
|
||||
if (this.constraintPairs[heldEntityId].length === 0) {
|
||||
delete this.constraintPairs[heldEntityId];
|
||||
}
|
||||
this.physicsSystem.removeConstraint(entityId);
|
||||
}
|
||||
|
||||
if (!this.constraintPairs[heldEntityId] || this.constraintPairs[heldEntityId].length < 1) {
|
||||
prevState.held.setAttribute("body-helper", { activationState: ACTIVATION_STATE.ACTIVE_TAG });
|
||||
if (prevState.held && isTagged(prevState.held, constraintTag)) {
|
||||
console.log("remove constraint from", prevState.held);
|
||||
const eid = prevState.held.eid;
|
||||
const bodyId = Rigidbody.bodyId[eid];
|
||||
|
||||
// TODO do we need this constraintPairs logic?
|
||||
if (this.constraintPairs[eid] && this.constraintPairs[eid].indexOf(interactorEid) !== -1) {
|
||||
this.constraintPairs[eid].splice(this.constraintPairs[eid].indexOf(interactorEid), 1);
|
||||
if (this.constraintPairs[eid].length === 0) {
|
||||
delete this.constraintPairs[eid];
|
||||
}
|
||||
this.physicsSystem.removeConstraint(interactorEid);
|
||||
}
|
||||
if (!this.constraintPairs[eid] || this.constraintPairs[eid].length < 1) {
|
||||
const bodyOptions = this.physicsSystem.bodyUuidToData.get(bodyId).options;
|
||||
// bodyOptions.type = "kinematic";
|
||||
bodyOptions.activationState = ACTIVATION_STATE.ACTIVE_TAG;
|
||||
this.physicsSystem.updateBody(bodyId, bodyOptions);
|
||||
}
|
||||
}
|
||||
if (!state.spawning && state.held && state.held.components.tags.data[constraintTag]) {
|
||||
|
||||
if (!state.spawning && state.held && isTagged(state.held, constraintTag)) {
|
||||
if (!state.held.components["networked"] || NAF.utils.isMine(state.held) || NAF.utils.takeOwnership(state.held)) {
|
||||
state.held.setAttribute("body-helper", {
|
||||
type: "dynamic",
|
||||
activationState: ACTIVATION_STATE.DISABLE_DEACTIVATION
|
||||
});
|
||||
const heldEntityId = state.held.id;
|
||||
const bodyUuid = state.held.components["body-helper"].uuid;
|
||||
const targetEl = document.querySelector(`#${entityId}`);
|
||||
const targetUuid = targetEl.components["body-helper"].uuid;
|
||||
if (bodyUuid !== -1 && targetUuid !== -1) {
|
||||
this.physicsSystem.addConstraint(entityId, bodyUuid, targetUuid, {});
|
||||
if (!this.constraintPairs[heldEntityId]) {
|
||||
this.constraintPairs[heldEntityId] = [];
|
||||
}
|
||||
this.constraintPairs[heldEntityId].push(entityId);
|
||||
const eid = state.held.eid;
|
||||
|
||||
const interactorBodyId = Rigidbody.bodyId[interactorEid];
|
||||
const bodyId = Rigidbody.bodyId[eid];
|
||||
|
||||
const bodyOptions = this.physicsSystem.bodyUuidToData.get(bodyId).options;
|
||||
bodyOptions.type = "dynamic";
|
||||
bodyOptions.activationState = ACTIVATION_STATE.DISABLE_DEACTIVATION;
|
||||
this.physicsSystem.updateBody(bodyId, bodyOptions);
|
||||
|
||||
console.log("add constraint to", interactorEid, bodyId, interactorBodyId);
|
||||
this.physicsSystem.addConstraint(interactorEid, bodyId, interactorBodyId, {});
|
||||
// TODO do we need this constraintPairs logic?
|
||||
if (!this.constraintPairs[eid]) {
|
||||
this.constraintPairs[eid] = [];
|
||||
}
|
||||
this.constraintPairs[eid].push(interactorEid);
|
||||
} else {
|
||||
console.log("Failed to obtain ownership while trying to create constraint on networked object.");
|
||||
}
|
||||
|
@ -97,25 +106,25 @@ export class ConstraintsSystem {
|
|||
|
||||
this.tickInteractor(
|
||||
"offersHandConstraint",
|
||||
interaction.options.leftHand.entity.id,
|
||||
interaction.options.leftHand.entity.object3D.eid,
|
||||
interaction.state.leftHand,
|
||||
this.prevLeftHand
|
||||
);
|
||||
this.tickInteractor(
|
||||
"offersHandConstraint",
|
||||
interaction.options.rightHand.entity.id,
|
||||
interaction.options.rightHand.entity.object3D.eid,
|
||||
interaction.state.rightHand,
|
||||
this.prevRightHand
|
||||
);
|
||||
this.tickInteractor(
|
||||
"offersRemoteConstraint",
|
||||
interaction.options.rightRemote.entity.id,
|
||||
interaction.options.rightRemote.entity.object3D.eid,
|
||||
interaction.state.rightRemote,
|
||||
this.prevRightRemote
|
||||
);
|
||||
this.tickInteractor(
|
||||
"offersRemoteConstraint",
|
||||
interaction.options.leftRemote.entity.id,
|
||||
interaction.options.leftRemote.entity.object3D.eid,
|
||||
interaction.state.leftRemote,
|
||||
this.prevLeftRemote
|
||||
);
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import { defineQuery, enterQuery } from "bitecs";
|
||||
import { waitForDOMContentLoaded } from "../utils/async-utils";
|
||||
import { CursorRaycastable } from "../utils/jsx-entity";
|
||||
|
||||
const noop = function() {};
|
||||
AFRAME.registerComponent("overwrite-raycast-as-noop", {
|
||||
|
@ -18,6 +20,9 @@ AFRAME.registerComponent("overwrite-raycast-as-noop", {
|
|||
}
|
||||
});
|
||||
|
||||
const cursorRaycastableQuery = defineQuery([CursorRaycastable]);
|
||||
const enteredCursorRaycastableQuery = enterQuery(cursorRaycastableQuery);
|
||||
|
||||
export class CursorTargettingSystem {
|
||||
constructor() {
|
||||
this.targets = [];
|
||||
|
@ -78,6 +83,12 @@ export class CursorTargettingSystem {
|
|||
targets.push(els[i].object3D);
|
||||
}
|
||||
}
|
||||
|
||||
const eids = cursorRaycastableQuery(APP.world);
|
||||
console.log("raycastable ents", eids);
|
||||
for (let i = 0; i < eids.length; i++) {
|
||||
targets.push(APP.world.eid2obj.get(eids[i]));
|
||||
}
|
||||
}
|
||||
|
||||
remove() {
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { isTagged } from "../components/tags";
|
||||
|
||||
function getSpecificHoverMenu(el) {
|
||||
return (
|
||||
el &&
|
||||
|
@ -14,10 +16,12 @@ function getSpecificHoverMenu(el) {
|
|||
function findHoverMenu(hovered) {
|
||||
if (!hovered) return null;
|
||||
const hoverMenu = getSpecificHoverMenu(hovered);
|
||||
// console.log("hoverMenu", hoverMenu);
|
||||
if (hoverMenu) {
|
||||
return hoverMenu;
|
||||
}
|
||||
if (!(hovered.components.tags && hovered.components.tags.data.isHoverMenuChild)) {
|
||||
if (!isTagged(hovered, "isHoverMenuChild")) {
|
||||
// console.log("not a hover menu child, aborting", hovered);
|
||||
return null;
|
||||
}
|
||||
let el = hovered.parentNode;
|
||||
|
|
|
@ -73,6 +73,8 @@ AFRAME.registerSystem("hubs-systems", {
|
|||
this.gainSystem = new GainSystem();
|
||||
this.environmentSystem = new EnvironmentSystem(this.el);
|
||||
this.nameTagSystem = new NameTagVisibilitySystem(this.el);
|
||||
|
||||
window.$S = this;
|
||||
},
|
||||
|
||||
tick(t, dt) {
|
||||
|
|
|
@ -3,11 +3,13 @@ import { paths } from "./userinput/paths";
|
|||
import { waitForDOMContentLoaded } from "../utils/async-utils";
|
||||
import { canMove } from "../utils/permissions-utils";
|
||||
import { isTagged } from "../components/tags";
|
||||
import { addComponent, hasComponent, removeComponent } from "bitecs";
|
||||
import { Holdable, Hovered, Held, Pinned, RemoteHoverTarget, Rigidbody } from "../utils/jsx-entity";
|
||||
|
||||
function findHandCollisionTargetForHand(bodyHelper) {
|
||||
function findHandCollisionTargetForHand(bodyId) {
|
||||
const physicsSystem = this.el.sceneEl.systems["hubs-systems"].physicsSystem;
|
||||
|
||||
const handCollisions = physicsSystem.getCollisions(bodyHelper.uuid);
|
||||
const handCollisions = physicsSystem.getCollisions(bodyId);
|
||||
if (handCollisions) {
|
||||
for (let i = 0; i < handCollisions.length; i++) {
|
||||
const bodyData = physicsSystem.bodyUuidToData.get(handCollisions[i]);
|
||||
|
@ -22,19 +24,22 @@ function findHandCollisionTargetForHand(bodyHelper) {
|
|||
}
|
||||
|
||||
const notRemoteHoverTargets = new Map();
|
||||
const remoteHoverTargets = new Map();
|
||||
export function findRemoteHoverTarget(object3D) {
|
||||
if (!object3D) return null;
|
||||
if (notRemoteHoverTargets.get(object3D)) return null;
|
||||
const target = remoteHoverTargets.get(object3D);
|
||||
return target || findRemoteHoverTarget(object3D.parent);
|
||||
|
||||
if (object3D.eid !== undefined && hasComponent(APP.world, RemoteHoverTarget, object3D.eid)) {
|
||||
return object3D.el;
|
||||
}
|
||||
|
||||
return findRemoteHoverTarget(object3D.parent);
|
||||
}
|
||||
AFRAME.registerComponent("is-remote-hover-target", {
|
||||
init: function() {
|
||||
remoteHoverTargets.set(this.el.object3D, this.el);
|
||||
addComponent(APP.world, RemoteHoverTarget, this.el.object3D.eid);
|
||||
},
|
||||
remove: function() {
|
||||
remoteHoverTargets.delete(this.el.object3D);
|
||||
removeComponent(APP.world, RemoteHoverTarget, this.el.object3D.eid);
|
||||
}
|
||||
});
|
||||
AFRAME.registerComponent("is-not-remote-hover-target", {
|
||||
|
@ -222,26 +227,41 @@ AFRAME.registerSystem("interaction", {
|
|||
const networked = state.held.components["networked"];
|
||||
const lostOwnership = networked && networked.data && networked.data.owner !== NAF.clientId;
|
||||
if (userinput.get(options.dropPath) || lostOwnership) {
|
||||
removeComponent(APP.world, Held, state.held.eid);
|
||||
state.held = null;
|
||||
}
|
||||
} else {
|
||||
const interactorEid = options.entity.object3D.eid;
|
||||
state.hovered = options.hoverFn.call(
|
||||
this,
|
||||
options.entity.components["body-helper"] && options.entity.components["body-helper"]
|
||||
? options.entity.components["body-helper"]
|
||||
: null
|
||||
hasComponent(APP.world, Rigidbody, interactorEid) && Rigidbody.bodyId[interactorEid]
|
||||
);
|
||||
if (state.hovered) {
|
||||
const entity = state.hovered;
|
||||
const isFrozen = this.el.is("frozen");
|
||||
const isPinned = entity.components.pinnable && entity.components.pinnable.data.pinned;
|
||||
const hoveredEid = entity.object3D.eid;
|
||||
const sceneIsFrozen = this.el.is("frozen");
|
||||
const isPinned = hasComponent(APP.world, Pinned, hoveredEid);
|
||||
// console.log(
|
||||
// JSON.stringify(
|
||||
// {
|
||||
// holdable: hasComponent(APP.world, Holdable, hoveredEid),
|
||||
// grabbing: userinput.get(options.grabPath),
|
||||
// sceneIsFrozen,
|
||||
// isPinned,
|
||||
// canMove: canMove(entity)
|
||||
// },
|
||||
// null,
|
||||
// 4
|
||||
// )
|
||||
// );
|
||||
if (
|
||||
isTagged(entity, "isHoldable") &&
|
||||
hasComponent(APP.world, Holdable, hoveredEid) &&
|
||||
userinput.get(options.grabPath) &&
|
||||
(isFrozen || !isPinned) &&
|
||||
(sceneIsFrozen || !isPinned) &&
|
||||
canMove(entity)
|
||||
) {
|
||||
state.held = entity;
|
||||
addComponent(APP.world, Held, hoveredEid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,14 +25,15 @@ export class PhysicsSystem {
|
|||
this.indexToUuid = {};
|
||||
this.bodyUuidToData = new Map();
|
||||
|
||||
this.debug = true;
|
||||
this.debugRequested = false;
|
||||
this.debugEnabled = false;
|
||||
this.scene = scene;
|
||||
this.stepDuration = 0;
|
||||
|
||||
this.ready = false;
|
||||
this.nextBodyUuid = 0;
|
||||
this.nextShapeUuid = 0;
|
||||
this.nextBodyUuid = 1;
|
||||
this.nextShapeUuid = 1;
|
||||
|
||||
const arrayBuffer = new ArrayBuffer(4 * BUFFER_CONFIG.BODY_DATA_SIZE * MAX_BODIES);
|
||||
this.objectMatricesFloatArray = new Float32Array(arrayBuffer);
|
||||
|
|
|
@ -2,6 +2,7 @@ import { sets } from "./sets";
|
|||
import { isUI } from "./../interactions";
|
||||
import { CAMERA_MODE_INSPECT } from "../camera-system";
|
||||
import qsTruthy from "../../utils/qs_truthy";
|
||||
import { isTagged } from "../../components/tags";
|
||||
const debugUserInput = qsTruthy("dui");
|
||||
|
||||
let leftTeleporter, rightTeleporter;
|
||||
|
@ -43,35 +44,19 @@ export function resolveActionSets() {
|
|||
|
||||
userinput.toggleSet(
|
||||
sets.leftHandHoveringOnPen,
|
||||
!leftHand.held &&
|
||||
leftHand.hovered &&
|
||||
leftHand.hovered.components.tags &&
|
||||
leftHand.hovered.components.tags.data.isPen
|
||||
!leftHand.held && leftHand.hovered && isTagged(leftHand.hovered, "isPen")
|
||||
);
|
||||
userinput.toggleSet(
|
||||
sets.rightHandHoveringOnPen,
|
||||
!rightHand.held &&
|
||||
rightHand.hovered &&
|
||||
rightHand.hovered.components.tags &&
|
||||
rightHand.hovered.components.tags.data.isPen
|
||||
!rightHand.held && rightHand.hovered && isTagged(rightHand.hovered, "isPen")
|
||||
);
|
||||
userinput.toggleSet(
|
||||
sets.leftCursorHoveringOnPen,
|
||||
!leftHand.held &&
|
||||
!leftHand.hovered &&
|
||||
!leftRemote.held &&
|
||||
leftRemote.hovered &&
|
||||
leftRemote.hovered.components.tags &&
|
||||
leftRemote.hovered.components.tags.data.isPen
|
||||
!leftHand.held && !leftHand.hovered && !leftRemote.held && isTagged(leftRemote.hovered, "isPen")
|
||||
);
|
||||
userinput.toggleSet(
|
||||
sets.rightCursorHoveringOnPen,
|
||||
!rightHand.held &&
|
||||
!rightHand.hovered &&
|
||||
!rightRemote.held &&
|
||||
rightRemote.hovered &&
|
||||
rightRemote.hovered.components.tags &&
|
||||
rightRemote.hovered.components.tags.data.isPen
|
||||
!rightHand.held && !rightHand.hovered && !rightRemote.held && isTagged(rightRemote.hovered, "isPen")
|
||||
);
|
||||
|
||||
userinput.toggleSet(
|
||||
|
@ -129,8 +114,8 @@ export function resolveActionSets() {
|
|||
!rightHand.hovered &&
|
||||
!rightRemote.held &&
|
||||
rightRemote.hovered &&
|
||||
((rightRemote.hovered.components.tags && rightRemote.hovered.components.tags.data.offersRemoteConstraint) ||
|
||||
(rightRemote.hovered.components.tags && rightRemote.hovered.components.tags.data.togglesHoveredActionSet) ||
|
||||
(isTagged(rightRemote.hovered, "offersRemoteConstraint") ||
|
||||
isTagged(rightRemote.hovered, "togglesHoveredActionSet") ||
|
||||
rightRemote.hovered.components["super-spawner"])
|
||||
);
|
||||
|
||||
|
@ -169,19 +154,11 @@ export function resolveActionSets() {
|
|||
);
|
||||
userinput.toggleSet(
|
||||
sets.rightCursorHoldingPen,
|
||||
!rightHand.held &&
|
||||
!rightHand.hovered &&
|
||||
rightRemote.held &&
|
||||
rightRemote.held.components.tags &&
|
||||
rightRemote.held.components.tags.data.isPen
|
||||
!rightHand.held && !rightHand.hovered && rightRemote.held && isTagged(rightRemote.held, "isPen")
|
||||
);
|
||||
userinput.toggleSet(
|
||||
sets.leftCursorHoldingPen,
|
||||
!leftHand.held &&
|
||||
!leftHand.hovered &&
|
||||
leftRemote.held &&
|
||||
leftRemote.held.components.tags &&
|
||||
leftRemote.held.components.tags.data.isPen
|
||||
!leftHand.held && !leftHand.hovered && leftRemote.held && isTagged(leftRemote.held, "isPen")
|
||||
);
|
||||
userinput.toggleSet(sets.leftHandHoldingCamera, leftHand.held && leftHand.held.components["camera-tool"]);
|
||||
userinput.toggleSet(sets.rightHandHoldingCamera, rightHand.held && rightHand.held.components["camera-tool"]);
|
||||
|
|
|
@ -56,17 +56,12 @@ export function createElementEntity(tag, attrs, ...children) {
|
|||
}
|
||||
}
|
||||
if (tag === "entity" || tag === "a-entity") {
|
||||
let isLegacyEntity = tag === "a-entity";
|
||||
if (isLegacyEntity) {
|
||||
console.warn("Entity using 'a-entity' tag, it should be 'entity'.");
|
||||
// do any special handling to make porting templates easier
|
||||
}
|
||||
const outputAttrs = {};
|
||||
const components = [];
|
||||
let ref = null;
|
||||
|
||||
for (let attr in attrs) {
|
||||
if (isLegacyEntity && vecAttrs.includes(attr)) {
|
||||
if (vecAttrs.includes(attr)) {
|
||||
outputAttrs[attr] = parseVecAttr(attr, attrs[attr]);
|
||||
} else if (reservedAttrs.includes(attr)) {
|
||||
outputAttrs[attr] = attrs[attr];
|
||||
|
@ -80,7 +75,7 @@ export function createElementEntity(tag, attrs, ...children) {
|
|||
}
|
||||
|
||||
return {
|
||||
type: "entity",
|
||||
type: tag === "a-entity" ? "a-entity" : "entity",
|
||||
attrs: outputAttrs,
|
||||
components,
|
||||
children: children.filter(isValidChild),
|
||||
|
@ -89,8 +84,68 @@ export function createElementEntity(tag, attrs, ...children) {
|
|||
}
|
||||
}
|
||||
|
||||
export function renderAsAframeEntity(entityDef) {
|
||||
if (entityDef.type === "entity") {
|
||||
import { addComponent, addEntity, removeEntity, defineComponent, Types } from "bitecs";
|
||||
|
||||
export const Networked = defineComponent({ templateId: Types.ui8, networkId: Types.ui8, flags: Types.ui8 });
|
||||
export const Object3DTag = defineComponent();
|
||||
export const Spin = defineComponent({ x: Types.f32, y: Types.f32, z: Types.f32 });
|
||||
|
||||
export const CursorRaycastable = defineComponent();
|
||||
export const RemoteHoverTarget = defineComponent();
|
||||
export const Holdable = defineComponent();
|
||||
export const Hovered = defineComponent();
|
||||
export const Held = defineComponent();
|
||||
export const OffersRemoteConstraint = defineComponent();
|
||||
export const HandCollisionTarget = defineComponent();
|
||||
export const OffersHandConstraint = defineComponent();
|
||||
export const TogglesHoveredActionSet = defineComponent();
|
||||
export const SingleActionButton = defineComponent();
|
||||
export const HoldableButton = defineComponent();
|
||||
export const Pen = defineComponent();
|
||||
export const HoverMenuChild = defineComponent();
|
||||
export const Static = defineComponent();
|
||||
export const Inspectable = defineComponent();
|
||||
export const PreventAudioBoost = defineComponent();
|
||||
export const IgnoreSpaceBubble = defineComponent();
|
||||
|
||||
export const Rigidbody = defineComponent({ bodyId: Types.ui16 });
|
||||
|
||||
export const Pinnable = defineComponent();
|
||||
export const Pinned = defineComponent();
|
||||
|
||||
export const FloatyObject = defineComponent();
|
||||
|
||||
export const NETWORK_FLAGS = {
|
||||
INFLATED: 1,
|
||||
IS_REMOTE_ENTITY: 2,
|
||||
FLAG_3: 4
|
||||
};
|
||||
|
||||
let networkId = 1;
|
||||
|
||||
export function createNetworkedEntity(world, templateId, eid = addEntity(world)) {
|
||||
world.networkSchemas[templateId].addEntity(world, eid);
|
||||
addComponent(world, Networked, eid);
|
||||
Networked.networkId[eid] = networkId++;
|
||||
Networked.templateId[eid] = templateId;
|
||||
return eid;
|
||||
}
|
||||
|
||||
function addObject3DComponent(world, eid, obj) {
|
||||
addComponent(APP.world, world.nameToComponent["object3d"], eid);
|
||||
world.eid2obj.set(eid, obj);
|
||||
obj.eid = eid;
|
||||
obj.addEventListener("removed", function() {
|
||||
removeEntity(world, eid);
|
||||
// TODO should probably happen in a system that looks for Object3DTag component removal
|
||||
world.eid2obj.delete(eid);
|
||||
obj.eid = null;
|
||||
});
|
||||
return eid;
|
||||
}
|
||||
|
||||
export function renderAsAframeEntity(entityDef, world, eid) {
|
||||
if (entityDef.type === "a-entity") {
|
||||
const el = document.createElement("a-entity");
|
||||
if (entityDef.attrs.className) {
|
||||
el.className = entityDef.attrs.className;
|
||||
|
@ -115,10 +170,10 @@ export function renderAsAframeEntity(entityDef) {
|
|||
entityDef.ref.current = el;
|
||||
}
|
||||
entityDef.children.forEach(child => {
|
||||
if (child.type === "Object3D") {
|
||||
el.object3D.add(renderAsAframeEntity(child));
|
||||
if (child.type === "a-entity") {
|
||||
el.appendChild(renderAsAframeEntity(child, world));
|
||||
} else {
|
||||
el.appendChild(renderAsAframeEntity(child));
|
||||
el.object3D.add(renderAsAframeEntity(child, world));
|
||||
}
|
||||
});
|
||||
Object.keys(entityDef.components).forEach(name => {
|
||||
|
@ -126,6 +181,40 @@ export function renderAsAframeEntity(entityDef) {
|
|||
el.setAttribute(name, componentProps === true ? "" : componentProps);
|
||||
});
|
||||
return el;
|
||||
} else if (entityDef.type === "entity") {
|
||||
const obj = new THREE.Group();
|
||||
if (entityDef.attrs.position) {
|
||||
obj.position.fromArray(entityDef.attrs.position);
|
||||
}
|
||||
if (entityDef.attrs.rotation) {
|
||||
obj.rotation.fromArray(entityDef.attrs.rotation);
|
||||
}
|
||||
if (entityDef.attrs.scale) {
|
||||
obj.scale.fromArray(entityDef.attrs.scale);
|
||||
}
|
||||
if (eid === undefined) {
|
||||
eid = addEntity(world);
|
||||
}
|
||||
addObject3DComponent(world, eid, obj);
|
||||
if (entityDef.ref) {
|
||||
entityDef.ref.current = eid;
|
||||
}
|
||||
Object.keys(entityDef.components).forEach(name => {
|
||||
const componentProps = entityDef.components[name];
|
||||
const Component = world.nameToComponent[name];
|
||||
addComponent(world, Component, eid);
|
||||
Object.keys(componentProps).forEach(propName => {
|
||||
Component[propName][eid] = componentProps[propName];
|
||||
});
|
||||
});
|
||||
entityDef.children.forEach(child => {
|
||||
if (child.type === "a-entity") {
|
||||
throw "a-entity can only be children of a-entity";
|
||||
} else {
|
||||
obj.add(renderAsAframeEntity(child, world));
|
||||
}
|
||||
});
|
||||
return obj;
|
||||
} else if (entityDef.type === "Object3D") {
|
||||
const obj = new entityDef.func();
|
||||
for (const prop in entityDef.props) {
|
||||
|
@ -140,7 +229,7 @@ export function renderAsAframeEntity(entityDef) {
|
|||
}
|
||||
entityDef.children.forEach(child => {
|
||||
if (child.type === "Object3D") {
|
||||
obj.add(renderAsAframeEntity(child));
|
||||
obj.add(renderAsAframeEntity(child, world));
|
||||
} else {
|
||||
throw "entities can only be children of entities";
|
||||
}
|
||||
|
|
|
@ -271,7 +271,9 @@ module.exports = async (env, argv) => {
|
|||
};
|
||||
|
||||
const devServerHeaders = {
|
||||
"Access-Control-Allow-Origin": "*"
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
"Cross-Origin-Opener-Policy": "same-origin",
|
||||
"Cross-Origin-Embedder-Policy": "require-corp"
|
||||
};
|
||||
|
||||
// Behind and environment var for now pending further testing
|
||||
|
|
Загрузка…
Ссылка в новой задаче