From 9ab12a798ff6681f40ad067a7f7f495236128695 Mon Sep 17 00:00:00 2001 From: netpro2k <netpro2k@gmail.com> Date: Tue, 5 Jun 2018 20:30:46 -0700 Subject: [PATCH] First pass at basic image interactables --- src/components/image-plus.js | 92 ++++++++++++++++++++++++++++++++++++ src/hub.html | 16 +++++++ src/hub.js | 14 ++++++ src/network-schemas.js | 5 ++ 4 files changed, 127 insertions(+) create mode 100644 src/components/image-plus.js diff --git a/src/components/image-plus.js b/src/components/image-plus.js new file mode 100644 index 000000000..f23be48bb --- /dev/null +++ b/src/components/image-plus.js @@ -0,0 +1,92 @@ +AFRAME.registerComponent("image-plus", { + dependencies: ["geometry", "material"], + + schema: { + src: { type: "string" }, + + freezeSpeedLimit: { default: 1 }, + + initialOffset: { default: { x: 0, y: 0, z: -1.5 } }, + reorientOnGrab: { default: false } + }, + + _fit(w, h) { + const ratio = (h || 1.0) / (w || 1.0); + const geo = this.el.geometry; + let width, height; + if (geo && geo.width) { + if (geo.height && ratio > 1) { + width = geo.width / ratio; + } else { + height = geo.height * ratio; + } + } else if (geo && geo.height) { + width = geo.width / ratio; + } else { + width = Math.min(1.0, 1.0 / ratio); + height = Math.min(1.0, ratio); + } + this.el.setAttribute("geometry", { width, height }); + this.el.setAttribute("shape", { + halfExtents: { + x: width / 2, + y: height / 2, + z: 0.05 + } + }); + }, + + _onMaterialLoaded(e) { + const src = e.detail.src; + const w = src.videoWidth || src.width; + const h = src.videoHeight || src.height; + if (w || h) { + this._fit(w, h); + } + }, + + _sleepIfStill(e) { + if ( + e.target === this.el && + this.el.body.velocity.lengthSquared() < this.data.freezeSpeedLimit * this.data.freezeSpeedLimit + ) { + this.el.body.sleep(); + } + }, + + _onGrab: (function() { + const q = new THREE.Quaternion(); + return function() { + this.el.body.wakeUp(); + if (this.data.reorientOnGrab) { + this.billboardTarget.getWorldQuaternion(q); + this.el.body.quaternion.copy(q); + } + }; + })(), + + init() { + this._onMaterialLoaded = this._onMaterialLoaded.bind(this); + this._sleepIfStill = this._sleepIfStill.bind(this); + this._onGrab = this._onGrab.bind(this); + + this.billboardTarget = document.querySelector("#player-camera").object3D; + + const el = this.el; + el.addEventListener("materialtextureloaded", this._onMaterialLoaded); + el.addEventListener("materialvideoloadeddata", this._onMaterialLoaded); + + el.addEventListener("grab-start", this._onGrab); + el.addEventListener("grab-end", this._sleepIfStill); + el.addEventListener("body-loaded", this._sleepIfStill); + + const worldPos = new THREE.Vector3().copy(this.data.initialOffset); + this.billboardTarget.localToWorld(worldPos); + this.el.object3D.position.copy(worldPos); + this.billboardTarget.getWorldQuaternion(this.el.object3D.quaternion); + }, + + update() { + this.el.setAttribute("material", "src", this.data.src); + } +}); diff --git a/src/hub.html b/src/hub.html index a7f615b37..d24077252 100644 --- a/src/hub.html +++ b/src/hub.html @@ -182,6 +182,21 @@ ></a-entity> </template> + <template id="interactable-image"> + <a-entity + class="interactable" + super-networked-interactable="counter: #media-counter; mass: 1;" + body="type: dynamic; shape: none; mass: 1;" + grabbable="uusePhysics: never;" + stretchable="useWorldPosition: true;" + hoverable + geometry="primitive: plane" + material="side: double;" + image-plus + > + </a-entity> + </template> + <a-mixin id="super-hands" super-hands=" colliderEvent: collisions; colliderEventProperty: els; @@ -196,6 +211,7 @@ <!-- Interactables --> <a-entity id="counter" networked-counter="max: 3; ttl: 120"></a-entity> + <a-entity id="media-counter" networked-counter="max: 3; ttl: 120"></a-entity> <a-entity id="cursor-controller" diff --git a/src/hub.js b/src/hub.js index a9f63cc3c..a62c2f650 100644 --- a/src/hub.js +++ b/src/hub.js @@ -63,6 +63,7 @@ import "./components/networked-avatar"; import "./components/css-class"; import "./components/scene-shadow"; import "./components/avatar-replay"; +import "./components/image-plus"; import ReactDOM from "react-dom"; import React from "react"; @@ -292,6 +293,19 @@ const onReady = async () => { NAF.connection.entities.completeSync(ev.detail.clientId); }); + document.addEventListener("paste", e => { + const scene = AFRAME.scenes[0]; + const imgUrl = e.clipboardData.getData("text"); + console.log("Pasted: ", imgUrl); + + const image = document.createElement("a-entity"); + image.id = "interactable-image-" + Date.now(); + image.setAttribute("position", { x: 0, y: 2, z: 1 }); + image.setAttribute("image-plus", "src", imgUrl); + image.setAttribute("networked", { template: "#interactable-image" }); + scene.appendChild(image); + }); + if (!qsTruthy("offline")) { document.body.addEventListener("connected", () => { if (!isBotMode) { diff --git a/src/network-schemas.js b/src/network-schemas.js index 9e20a17ca..4ae8e1839 100644 --- a/src/network-schemas.js +++ b/src/network-schemas.js @@ -90,6 +90,11 @@ function registerNetworkSchemas() { "scale" ] }); + + NAF.schemas.add({ + template: "#interactable-image", + components: ["position", "rotation", "image-plus"] + }); } export default registerNetworkSchemas; -- GitLab