0

I am working on an Aframe project and want to have a control/event that allows an entity to rotate down.

I'm trying to create a new animation and put that as a child object on the entity. This has good results turning left and right along the x axis, but I can't seem to get it working to rotate the box down.

In some cases, like {z: 90, x:90, y: 0}, there's no way to rotate the face that is pointing at the camera down. Changing to y, or z does not make a difference.

I created a minimal plunker to show the issue. https://plnkr.co/edit/B6apT3?p=preview

Edit: Adding the code from the original plunker.

To turn down I am using the following logic.

if(rotateOnY){
  z -= 90;
} else {
  y -= 90;
}
changePosition(x, y, z);

And to change the position I use the following function.

function changePosition(x, y, z){
  let animation = document.createElement('a-animation');
  animation.setAttribute("attribute","rotation");
  animation.setAttribute("dur", 300);
  animation.setAttribute("repeat", "0");
  animation.setAttribute("to",`${z} ${x} ${y}`);
  document.getElementById('box').appendChild(animation);
  document.getElementById('position-text').setAttribute('text', `value: Position(x, y, z): ${x} ${y} ${z}`)
}

There was a really helpful answer, but the rotations using Tween don't quite work.

We can change the logic to something like this, but the math isn't quite working correctly as can be seen in http://plnkr.co/edit/hr2l83?p=preview

const originalEuler = new THREE.Euler(x, y, z);
const originalQuaternion = new THREE.Quaternion();
originalQuaternion.setFromEuler(originalEuler);

var tarQ = originalQuaternion.premultiply(q);

var euler = new THREE.Euler();
euler.setFromQuaternion(tarQ);
let rot = { // rot not pos
  x: THREE.Math.radToDeg(euler._x),
  y: THREE.Math.radToDeg(euler._y),
  z: THREE.Math.radToDeg(euler._z)
};
// update to neares 45
for(let axis of Object.keys(rot)){
  rot[axis] = nearest45(rot[axis]);
}
// update global x, y, z;
x = rot.x; y = rot.y; z = rot.z;
changePosition(rot.x, rot.y, rot.z);
Amos47
  • 695
  • 6
  • 15
  • Hi, sorry if this is completely unrelated, but i'm curious if the `three.js` and `webgl` tags should be present here. It's safe to say that this has absolutely nothing to do with webgl. Webgl is a rasterization / parallel computing library and has no idea about boxes, rotations etc. Aframe is built on top of three.js, but i thought its supposed to abstract things away. Do aframe issues actually get solved by writing three.js code (never worked with it)? – pailhead Oct 04 '17 at 18:34
  • It's probably overkill for `webgl`. But I thought the experts there might know rotation problems and be able to solve it. I think `three.js` should probably stay though. It's very frequently just a small layer ontop of it, especially for rotation from what I've seen. EDIT: Removed `webgl` – Amos47 Oct 04 '17 at 18:36
  • That may make more sense. `webgl` doesnt know about your rotation, it can just do the matrix operations efficiently. `three.js` is a good one to stay because if there is a rotation problem it stems from three's math library. – pailhead Oct 04 '17 at 18:39
  • @pailhead - your main point notwithstanding, in response to your threejs/aframe question - yes, absolutely. A-Frame provides excellent ECS architecture, webVR capabilities and some other helpful goodies, but under the hood it's pure threejs and if you're doing any kind of custom components / interaction / behavior programming in aframe, you're almost definitely using threejs. – AquaVitae Oct 04 '17 at 23:40
  • @brian. Why was this post marked as off topic? – Amos47 Oct 06 '17 at 00:14
  • @sgress454 Why was this post marked as off topic? – Amos47 Oct 06 '17 at 00:14
  • @Amos47 Read the off-topic reason, it's right there in the box. What you have provided is a *link* to your code; you *must* put the code into the question. Copy and paste. – Ajean Oct 06 '17 at 01:41
  • Alright then. Updated. Not sure how to change it from being off-hold. – Amos47 Oct 06 '17 at 20:52
  • @Amos47 I have cast a reopen vote, it should go into the queue. – Ajean Oct 07 '17 at 00:39

1 Answers1

2

EDIT: apologies - while accurate, my original answer wasn't as helpful as it could have been. After some further digging, I found a handy clean way to apply world rotation without getting a phd in math, that essentially boils down to:

var q = new THREE.Quaternion(); // create once and reuse q.setFromAxisAngle( dirVec, Math.PI/2 );// desired world rotation axis, 90 degrees in radians var tarQ = box.quaternion.premultiply( q );

see updated plunker

(note that in order to tween the transition, I resorted to cloning the original quaternion, making the operation on it, then setting it as the target for the tween.)


Welcome to the wonderfully painful world of 3D rotation.

In 3D math terms, you want to be rotating your object in world space, as opposed to local/mesh space - the latter implicitly allows for a world of problems including what I believe is the issue you're running into here - gimbal lock.

Translating to and from both spaces involves a bit of matrix maths and takes some getting used to but luckily three has some excellent helper methods. See the great answer here for a detailed explanation of such conversions: Set an object's absolute rotation around the world axis

AquaVitae
  • 177
  • 1
  • 10
  • 1
    This seems like exactly it, but I'd love if someone could provide a code solution or an edit to the plunker. I'm having issues understanding what the simple way to handle this is. I really don't want to handle the matrix rotations manually, so I'm hoping someone can point me in the right direction of a library, or some functions from `THREE` or `AFrame` that can help. – Amos47 Oct 04 '17 at 20:59
  • Edited to include solution - hope this helps. IMO threejs needs to provide a helper method like the one I mention out of the box... – AquaVitae Oct 04 '17 at 23:20
  • This doesn't quite work. If you continuously click down, it does not always rotate and then the dimensions of the box get distorted too. If you could just show how to calculate what a euler rotation using the rotation matrix. – Amos47 Oct 05 '17 at 15:52
  • I see what you mean - I can repro only if I click quickly and repeatedly and this is probably a threejs bug. There's certainly nothing wrong with the math. All you have to do is insert a method that "locks" click for a setTimeOut. (And you can't trivially use Eulers in world coordinates - at some point you'll have to use Quats or matrices) Also see this threejs issue where @westlangley mentions the exact same method I'm using here: https://github.com/mrdoob/three.js/issues/12336 – AquaVitae Oct 05 '17 at 16:32
  • http://plnkr.co/edit/hr2l83 Is how I'd like to do this... But the math isn't right there. Maybe you could take one more look. – Amos47 Oct 05 '17 at 16:41
  • 1
    This has gotten a little out of hand. We had to add a debounce function to get it working properly, but other than that this answer really helped. – Amos47 Oct 10 '17 at 20:56
  • cool, glad to hear. By the way, your answer and the github issue that I raised resulted in a PR I made that was recently merged - three now has a built in world rotation method:) https://github.com/mrdoob/three.js/pull/12352 – AquaVitae Oct 10 '17 at 21:22