From 5006510a480390e4b31e415733a13f5f80bf2c79 Mon Sep 17 00:00:00 2001 From: johnshaughnessy <johnfshaughnessy@gmail.com> Date: Tue, 4 Sep 2018 15:07:19 -0700 Subject: [PATCH] Get image rotation and rotate the resulting object accordingly. --- doc/image_orientations.gif | Bin 0 -> 19680 bytes src/components/offset-relative-to.js | 38 ++++++++++++++++++++ src/components/super-spawner.js | 2 +- src/hub.js | 11 +++--- src/react-components/2d-hud.js | 1 + src/utils/media-utils.js | 50 ++++++++++++++++++++++++++- 6 files changed, 96 insertions(+), 6 deletions(-) create mode 100755 doc/image_orientations.gif diff --git a/doc/image_orientations.gif b/doc/image_orientations.gif new file mode 100755 index 0000000000000000000000000000000000000000..89c4c3ea1444de6b32e1cecc2d2825e430c833e8 GIT binary patch literal 19680 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�SBVMF| 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 literal 0 HcmV?d00001 diff --git a/src/components/offset-relative-to.js b/src/components/offset-relative-to.js index e00acd4e5..877cfcf8a 100644 --- a/src/components/offset-relative-to.js +++ b/src/components/offset-relative-to.js @@ -13,6 +13,9 @@ AFRAME.registerComponent("offset-relative-to", { on: { type: "string" }, + orientation: { + default: 1 // see doc/image_orientations.gif + }, selfDestruct: { default: false } @@ -27,6 +30,9 @@ AFRAME.registerComponent("offset-relative-to", { }, updateOffset: (function() { + const y = new THREE.Vector3(0, 1, 0); + const z = new THREE.Vector3(0, 0, -1); + const QUARTER_CIRCLE = Math.PI / 2; const offsetVector = new THREE.Vector3(); return function() { const obj = this.el.object3D; @@ -40,6 +46,38 @@ AFRAME.registerComponent("offset-relative-to", { this.el.body && this.el.body.position.copy(obj.position); target.getWorldQuaternion(obj.quaternion); this.el.body && this.el.body.quaternion.copy(obj.quaternion); + + // See doc/image_orientations.gif + switch (this.data.orientation) { + case 8: + obj.rotateOnAxis(z, 3 * QUARTER_CIRCLE); + break; + case 7: + obj.rotateOnAxis(z, 3 * QUARTER_CIRCLE); + obj.rotateOnAxis(y, 2 * QUARTER_CIRCLE); + break; + case 6: + obj.rotateOnAxis(z, QUARTER_CIRCLE); + break; + case 5: + obj.rotateOnAxis(z, QUARTER_CIRCLE); + obj.rotateOnAxis(y, 2 * QUARTER_CIRCLE); + break; + case 4: + obj.rotateOnAxis(z, 2 * QUARTER_CIRCLE); + obj.rotateOnAxis(y, 2 * QUARTER_CIRCLE); + break; + case 3: + obj.rotateOnAxis(z, 2 * QUARTER_CIRCLE); + break; + case 2: + obj.rotateOnAxis(y, 2 * QUARTER_CIRCLE); + break; + case 1: + default: + break; + } + if (this.data.selfDestruct) { if (this.data.on) { this.el.sceneEl.removeEventListener(this.data.on, this.updateOffset); diff --git a/src/components/super-spawner.js b/src/components/super-spawner.js index f760a29e2..0c69e82da 100644 --- a/src/components/super-spawner.js +++ b/src/components/super-spawner.js @@ -84,7 +84,7 @@ AFRAME.registerComponent("super-spawner", { const thisGrabId = nextGrabId++; this.heldEntities.set(hand, thisGrabId); - const entity = addMedia(this.data.src, ObjectContentOrigins.SPAWNER); + const entity = addMedia(this.data.src, ObjectContentOrigins.SPAWNER).entity; entity.object3D.position.copy( this.data.useCustomSpawnPosition ? this.data.spawnPosition : this.el.object3D.position ); diff --git a/src/hub.js b/src/hub.js index 8db01eadd..5e347527c 100644 --- a/src/hub.js +++ b/src/hub.js @@ -306,11 +306,14 @@ const onReady = async () => { const offset = { x: 0, y: 0, z: -1.5 }; const spawnMediaInfrontOfPlayer = (src, contentOrigin) => { - const entity = addMedia(src, contentOrigin, true); + const { entity, orientation } = addMedia(src, contentOrigin, true); - entity.setAttribute("offset-relative-to", { - target: "#player-camera", - offset + orientation.then(or => { + entity.setAttribute("offset-relative-to", { + target: "#player-camera", + offset, + orientation: or + }); }); }; diff --git a/src/react-components/2d-hud.js b/src/react-components/2d-hud.js index effa07f29..e50d29598 100644 --- a/src/react-components/2d-hud.js +++ b/src/react-components/2d-hud.js @@ -75,6 +75,7 @@ const BottomHUD = ({ onCreateObject, showImageOnlyButton, onMediaPicked }) => ( BottomHUD.propTypes = { onCreateObject: PropTypes.func, + showImageOnlyButton: PropTypes.bool, onMediaPicked: PropTypes.func }; diff --git a/src/utils/media-utils.js b/src/utils/media-utils.js index 83a5f788e..825865dca 100644 --- a/src/utils/media-utils.js +++ b/src/utils/media-utils.js @@ -51,6 +51,45 @@ export const upload = file => { }).then(r => r.json()); }; +// https://stackoverflow.com/questions/7584794/accessing-jpeg-exif-rotation-data-in-javascript-on-the-client-side/32490603#32490603 +function getOrientation(file, callback) { + const reader = new FileReader(); + reader.onload = function(e) { + const view = new DataView(e.target.result); + if (view.getUint16(0, false) != 0xffd8) { + return callback(-2); + } + const length = view.byteLength; + let offset = 2; + while (offset < length) { + if (view.getUint16(offset + 2, false) <= 8) return callback(-1); + const marker = view.getUint16(offset, false); + offset += 2; + if (marker == 0xffe1) { + if (view.getUint32((offset += 2), false) != 0x45786966) { + return callback(-1); + } + + const little = view.getUint16((offset += 6), false) == 0x4949; + offset += view.getUint32(offset + 4, little); + const tags = view.getUint16(offset, little); + offset += 2; + for (let i = 0; i < tags; i++) { + if (view.getUint16(offset + i * 12, little) == 0x0112) { + return callback(view.getUint16(offset + i * 12 + 8, little)); + } + } + } else if ((marker & 0xff00) != 0xff00) { + break; + } else { + offset += view.getUint16(offset, false); + } + } + return callback(-1); + }; + reader.readAsArrayBuffer(file); +} + let interactableId = 0; export const addMedia = (src, contentOrigin, resize = false) => { const scene = AFRAME.scenes[0]; @@ -61,6 +100,15 @@ export const addMedia = (src, contentOrigin, resize = false) => { entity.setAttribute("media-loader", { resize, src: typeof src === "string" ? src : "" }); scene.appendChild(entity); + const orientation = new Promise(function(resolve) { + if (src instanceof File) { + getOrientation(src, x => { + resolve(x); + }); + } else { + resolve(1); + } + }); if (src instanceof File) { upload(src) .then(response => { @@ -78,5 +126,5 @@ export const addMedia = (src, contentOrigin, resize = false) => { scene.emit("object_spawned", { objectType }); }); - return entity; + return { entity, orientation }; }; -- GitLab