Monday, March 23, 2009

Papervision 3D Sphere Globlal Axis Rotation

Problem:
In my previous post (link here) I realized the the quick and dirty way of turning a sphere was not cutting it for me because the sphere was always turning along the Sphere's own coordinate axis (x & y in this case). So, no matter how the sphere was oriented, meaning what the angle of the X axis was for example, if the user moused over the (absolute) lower half of the sphere it turned it (relatively) along it's own X axis.

Solved!
In this second iteration I converted the rotation on mouseOver so that the sphere rotates around the axis defined by the perpendicular of the vector created by the point on the surface of the sphere over which the user rolled the mouse over and the origin of the sphere coordinates (0,0,0).




How is this done?
Regular trigonometry doesn't cut it for this. Why? because both rotating axis are dependent on each other. As the sphere rotates along one axis by a given the angle, it affects the other... or something like that. A new axis of rotation needs to be resolved for every step of the 3d animation. For an in-depth explanation go to this website that deals with 3D axis rotations, spherical trigonometry and other nasty looking mathematical models. Specifically scroll down to the "Axis Angle rotation" and the link for "axis-angle to matrix conversion".

Luckily the Papervision Gods gave us full fledge transformation matrixes to resolve this in a relatively simple way. Particularly the rotationX & Y static methods of the Matrix3D class saves us a LOT of work. The calculations of the new revolving 3d axis looks something like this:

//get original sphere transform matrix
var tempMatrix:Matrix3D = sphere.transform;
//create the rotation Matrixes for each local rotation axis given the corresponding X & Y
//distances where the user rolled over on the surface of the sphere

//Note: The value passed to Matrix3D.rotationX() should really be radians but I'm just using the x & y values divided by rotSpeedRatio which is a constant to bring the values down to "radiany" magnitudes and thus decrease the speed of the rotation. this will have to be the variable to work with when I implement the slowing down of the rotation.
var rotX:Matrix3D = Matrix3D.rotationX(-[mouse Y here]/[rotSpeedRatio]);
var rotY:Matrix3D = Matrix3D.rotationY(-[mouse X here]/[rotSpeedRatio]);
//Mix both Matrixes together
var rotResult:Matrix3D = Matrix3D.multiply(rotX,rotY);
//Apply (Multiply) the final transform matrix to the original one and assign it to the sphere transform
sphere.transform = Matrix3D.multiply(rotResult,tempMatrix);

That's it.

What I've done also in the example is get the mouse X & Y on MouseMove over the sphere so that the rotation gets refreshed then and because we are using this values for the rotation matixes, it slows down as you move the mouse towards the center of of the sphere (Ie: x and/or y approximates to zero)

This was fun but I really should take a math class soon.

2 comments:

  1. Desperate question!

    Any idea how I would do this on a nested do3d? i.e. not positioned at origin 0, 0, 0?

    ReplyDelete