This commit is contained in:
John Shaughnessy 2022-05-04 16:16:40 -04:00 коммит произвёл netpro2k
Родитель ebb0a4ef64
Коммит a32dbd772a
22 изменённых файлов: 768 добавлений и 181 удалений

5
package-lock.json сгенерированный
Просмотреть файл

@ -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",

Просмотреть файл

@ -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);
}
}

Просмотреть файл

@ -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