Understanding VRML Rotations

What is the deal with vrml rotations why do they have so many numbers just so they can rotate something? would it not be simpler to have 3 different rotation types one for each of the x y and z axis? then all that would be required would be one value in order to rotate an object to the desired angle.

Would it not also be more efficient for the computer to use simpler rotations than what must be needed to do the more complex maths involved for vrml rotations?

It is true that it is possible to get a lot achieved by only ever rotating objects on a x, y and z axis and they are faster and they are simpler to grasp for any beginner. It is possible to use vrml rotations like this if we restrict the 3 values in the axis component of the rotations to one of the 3 simple values

    1 0 0    for x axis rotations
    0 1 0    for y axis rotations
    0 0 1    for z axis rotations

The angle component of the vrml rotation can then be set to rotate an object to the desired angle for the specified axis.

However this would be making the worst of a good deal. It would mean we would be suffering the extra complexity in having 4 values and we would be suffering the performance lost by the extra maths involved by the computer with out anything gained in replace.

Vrml rotations are a good example of how making things a little more complex to begin with can greatly simplify tasks later on.


What VRML rotations offer is they allow us to interpolate from any given orientation to any other orientation imaginable with ease. Although a VRML rotation is made up of 4 floating point numbers much of the time we can treat a VRML rotation as a single value object. For example to concatenate 2 VRML rotations we simply multiply 2 VRML rotation objects together with a single multiply function.

To understand what vrml rotations are all about we have to understand what it means to rotate an object on any possible axis and to understand this it is probably a good idea to understand what a 1 unit vector is too.

What is a One Unit Vector?

Although one unit vectors have no physical shape its good to conceptualize them as pointy thin things that are 1 meter in length. One unit vectors are used for specifying the orientation of something by pointing in a direction. A vector made up of 3 components x, y and z can represent any 3d orientation that exists.

We can easily grasp what these 3 values mean if we think of a vector as always having its base at 0 0 0
like this we can think of the 3 components x, y and z equaling the location of the tip of the vector.

What is an Axis?

For vrml rotations we can think of the axis as a one unit vector where the pointy shape doubles as a spindle for any object we stick on it to be rotated.

The spindle will never spin in a buckled manner. When it rotates it will always spin perfectly straight no matter what its orientation is set to. This means that the tip of the axis does not change location as a result of it spinning or what we change the angle component to.

So a vrml rotation is pretty simple when we view them like this.

Making an object point to another object anywhere in 3d space.

Making an object point to another in 2d space is easily achieved using the Math atan2 function.

for example in the following example we have 2 shapes

#VRML V2.0 utf8
NavigationInfo { type "examine"}
Viewpoint { position 0 0 11}

DEF objectA Transform {
    translation 1.7 2.6 0 #position of objectA
    children Shape {
        appearance Appearance {
            material Material {diffuseColor 0 0 1}
        }
        geometry Sphere {radius .5}
    }
}
DEF pointer Transform {
    translation 0 0 0 #position of pointer base
    children Transform {
        translation 2.5 0 0
        rotation 0 0 1 1.5708
        children Shape {
            appearance Appearance {material Material {diffuseColor 1 0 0}}
            geometry Cylinder {
                radius .08
                height 5
            }
        }
    }
}

One of the objects is a blue sphere named objectA. This will be used as the object we will point to. The other object is a long red thin cylinder named pointer. This will be the object we will use for pointing to objectA The base of the pointer is positioned at 0 0 0

To make our pointer point to the objectA on the z plane add the following script:

DEF myScript Script{
    directOutput TRUE
    field SFNode objectA USE objectA
    field SFNode pointer USE pointer

    url "javascript:
    function initialize(){

        zAngle=Math.atan2( objectA.translation.y, objectA.translation.x);

        pointer.rotation = new SFRotation(0,0,1,zAngle);
    }
    "
}

Now what ever position we change the translation field for objectA we should see the pointer always pointing towards it in the x and y directions. This can be easily verified if we keep objectA's translation's z component to 0. This will ensure that the pointer will always go through the center of objectA.

If we want to have our pointer be able to point to objectA when its base is in a different position to 0 0 0 we will have to do our calculations so that the pointer's base is at 0 0 0 in the world in relation to objectA

This is done by getting the difference in the pointer's position to objectA's position by subtracting the pointers position from objectA's position

To do this add the code:

    relObjectAPos=objectA.translation.subtract(pointer.translation);

so that its the first line of code inside our initialize function's curly brackets.

then modify the line of code:

    zAngle=Math.atan2( objectA.translation.y, objectA.translation.x);

to

    zAngle=Math.atan2( relObjectAPos.y, relObjectAPos.x);

Now we can change the position of both the pointer and objectA and we will still see the pointer going through the center of the objectA so long as the translation's z component is kept to 0 for both objects.

It is ok to change the z component to values other than 0. We will still get the pointer point to objectA but the pointer will only be able to point by rotating on its z axis, that is it wont be able to point in the z direction it will only be able to point in the x and y directions.

Since atan2 is a function that only gives us the angle for a location in 2d space how do we use it to get the orientation in 3d space?

To be able to make an object have any possible orientation in 3d space we only need to rotate it in 2 successive rotations using only 2 of the 3 x y and z axis for the rotations

In this example we will use the z and y axis for the 2 rotations

To get the 2 rotations for our pointer we will first do the calculations as if we were rotating objectA twice so that it would end up located on the x axis on the positive x side of the world the same as our pointer when its not rotated to any value.

Having done this means its logical to be able to rotate our object back to where it came from by rotationg back in reverse order negitivly but instead of rotating objectA back we will rotate the pointer so that it points exactly to where the object would have come from

Instead of actually rotating objectA we will only need to rotate the tempory SFVec3f variable relObjectAPos to do our calculations:

After the line of code that gets the zAngle we want to next rotate relObjectAPos on the z axis to negitive zAngle by adding the line of code:

    relObjectAPos = new SFRotation(0,0,1,-zAngle).multVec(relObjectAPos);

this line of code will make relObjectAPos's z component = 0

now we can get the yAngle by adding the line of code:

    yAngle=-Math.atan2( relObjectAPos.z, relObjectAPos.x);

If we rotate relObjectAPos's location with yAngle relObjectAPos should always = 0 for the y and z components and a positive value for the x component.

There is no need to actually rotate relObjectAPos with yAngle for our calculations as we already have the value we want at this stage but to verify that our code is keeping in with our concept of unwinding objectA we can add the following lines if we wish:

    relObjectAPos = new SFRotation(0,1,0,-yAngle).multVec(relObjectAPos);
    print(relObjectAPos);

which should show relObjectAPos's z and y components to both = 0 and its x component to = a positive value

to rotate the pointer back in reverse order to how we rotated relObjectAPos change the last line of code to

    pointer.rotation = new SFRotation(0,1,0,yAngle).multiply(new SFRotation(0,0,1,zAngle));

our whole initialize function should now look like:

function initialize(){
    relObjectAPos=objectA.translation.subtract(pointer.translation);
    zAngle=Math.atan2( relObjectAPos.y, relObjectAPos.x);
    relObjectAPos = new SFRotation(0,0,1,-zAngle).multVec(relObjectAPos);
    yAngle=-Math.atan2( relObjectAPos.z, relObjectAPos.x);
    pointer.rotation = new SFRotation(0,1,0,yAngle).multiply(new SFRotation(0,0,1,zAngle));
}

refreshing should show the pointer pointing to objectA in 3d space

Try changing the value of objectA's or the pointer's base translation field. This should always result in the pointer pointing to objectA in 3d space with presision.



Copyright© 2000-2006 Graham Perrett thyme@seamless3d.com