Top Banner
Clover Mills
Home      Second Life     Woodwork    Pets     Links  

Windmill
Rotations In Second Life
Tutorial Article
Building a Windmill

 I have built a windmill in Second Life.   This wheel of the windmill spins based on the windmill,  and the top structture of the windmill (the wheel and the tail)  rotate to face into the wind.   

However, I had quite a struggle building this windmill, as I was trying to figure out how rotations worked.    So I wrote this  tutorial article about how I built this Windmill for :  

PDF Version  Rotations in Second Life - A windmill - Tutorial Article  - pdf  700kb

As HTML - See below

Table of Contents
  1. Introduction
  2. Some Preliminaries
    1. Multiple meanings of “Rotation”
    2. Orbit type rotation
    3. Vectors – A point of confusion
    4. Eulers and Quaternions
    5. Some Rotation problems for novices with llSetRot
    6. Setting the Position  - Rotating a Position Vector
    7. Getting the components of a rotation vector
  3. The Windmill Rotation
    1. Turning to face the Wind
    2. Overview of the Structure
    3. The rotations to be dealt with
    4. Initial Orientations
    5. llSetRot versus llSetLocalRot  - A Broken Windmill
    6. Diagram
    7. The Gearbox
    8. Wheel
    9. The Horizontal Shaft and the Tail
  4. Conclusion


Introduction

This article describes how I used rotations when building a windmill in Second Life.  It is written as a tutorial for people who are trying to come to grips with rotations, and for those who have avoided rotations because they’ve heard of the complexity.

The lslwiki.net is a great resource, but I found that it was not a good tool for learning.  They lacked real examples to get my head around and covered the issues in a piece-meal fashion.

This is going to be mostly a “plain English” article.  I expect you to have some basic scripting knowledge, understand linked objects, degrees and radians, and have some idea of the difference between global and local co-ordinates and axis.  Please note that the script examples given are extracts from the full script.

Things that will be covered: Functions such as llSetRot, llSetLocalRot, llSetPos, llSetLocalPos, llTargetOmega, and vectors, rotation vectors, quaternions.  This article does not cover related rotation functions such as llSetRotAt.

Go To Top

Some Preliminaries

Multiple meanings of “Rotation”

The term ‘rotation’ can be used in a variety of contexts, which makes it difficult to figure out what someone is talking about.

Orbit type rotation

Second Life does not provide a single function to do an orbit type rotation. You have to do this yourself, in two parts by setting a position and setting an orientation.

In these diagrams, imagine that the little yellow block is moved at intervals, to a new position in an orbit around the big yellow block in the centre.

Step 1 Set the new position of the prim using llSetPos (or llSetLocalPos if a child)

Step 2 Set the orientation/rotation of the prim using llSetRot (or llSetLocalRot if a child)

Setting The Position Setting the Orientation

Setting The Position

Here, each little yellow box is in a different position around the centre box but its orientation is not affected by its position.

Setting the Orientation

Each little yellow box here has been orientated to point/face towards the centre object.  The angle of the orientation depends on the position of the object.

Vectors – A point of confusionGo To Top

In Second Life, we have three types of vectors – position, rotation & colour.  They all look the same < a , b, c>.  I shall try to be clear whether I am referring to an euler rotation vector or a position vector. 

Eulers and Quaternions

In plain English

Edit WindowAn euler rotation vector is a way to describe a rotation. It is the angle to rotate around x-axis, the angle to rotate around the y-axis, and the angle to rotate around the z-axis. In Second Life it will look like
 < x, y, z>.  This is what you see in the edit window. 

I do not know why they are called ‘Euler’.  

Expressed in Second Life

vector my_rotation_vector; // define my variable 

my_rotation_vector = < angle_x, angle_y, angle_z>; 

// Where angle_x etc, are floats, and expressed in radians. 


Euler rotations sound quite simple, and are easily understood.  Unfortunately, when you combine two or more euler rotations, sometimes you don’t get the result that you expect or understand.  This problem arises because of the nature of rotations;  For some combinations of rotations, it is better to convert the Euler Rotation Vectors into Quaternions, and combine the quaternions in particular ways, to get the rotation you want.   If you want to know why, read the lsl wiki.  You do not need to understand what a quaternion is or the maths behind it.  If you want to know more, look it up.

As the rest of this article will show, it’s not the quaternions themselves that are difficult, it is figuring out what the angles are, what’s local , what’s global.

Some Rotation problems for novices with llSetRotGo To Top

When setting a rotation (or more specifically, an orientation) on a prim, we use llSetRot() on a object, or llSetLocalRot() on a child.

Problem!

The llSetRot function wants a quaternion, not an euler vector. 

Answer:

No Problem! There’s an easy way to covert eulers to quaternions, and quaternions to eulers.

The_Rotation_Vector = llRot2Euler(The_Quaternion); 

The_Quaternion = llEuler2Rot(The_Rotation_Vector); 

Next Problem!

What’s the rotation vector that I need to use? What is the quaternion that I need to use? 

Answer:

That’s what the rest of this article is about. 

Next Problem!

How to extract or set the x, y & z components of an euler rotation vector? 

Answer:

It takes a few lines, but see the section below. 

Next problem! 

But those components are in radians, not degrees. 

Answer:

No Problem! Linden Labs have given us some constants to simplify the conversion. Multiply by DEG_TO_RAD or RAD_TO_DEG. 

Next Problem!

Keeping track of what type all the variables are. 

Answer:

That’s your problem. 



Go To Top

Rotating Position Vectors

Setting the Position - Rotating a Position Vector

When moving an object from B to C at an angle θ around the point A, you could use angles, cosines, sines and pythagorus and calculate the new position of C in terms of x and y.

If you would like to do it with fewer mistakes, and fewer lines, you could try the alternative; ie rotate the vector A_B.







The Principle

A position vector that is rotated gives a new position vector. 

In Plain English

To move the object at B to position C, take the position vector A_to_B and rotate it by angle θ around point A, to get the position vector A_to_C.  This vector identifies point C.  

The rotation of a vector is done by multiplying it by the quaternion that represents the rotation that is needed. 

Expressed more as a formula

Vector_A_to_C = Vector_A_to_B rotated by angle θ 

Vector_A_to_C = Vector_A_to_B multiplied by a Quaternion which represents the rotation θ. 

For a simple rotation by angle θ around the z-axis at point A, the quaternion can be found from the euler rotation vector, which is <0.0,0.0, θ> where θ is in radians. 

If your angle is in degrees, then θ = your_angle * DEG_TO_RAD 

In LSL

The_Quaternion = llEuler2Rot(<0.0,0.0, θ>; // This represents a rotation by angle θ around the z-axis 

Vector_A_to_C = Vector_A_to_B * The_Quaternion; 

// Because this example is not a child need to add point A and use llSetPos 

Position_C = Position_A + Vector_A_to_C;  

// Note Position_C and Position_A are vectors which define the position of point A and point C. 

llSetPos(Position_C); 


Go To Top

Getting the components of a rotation vector

There are some applications where you need to see, use or set the values of the components of a vector.  During troubleshooting, I needed to see the components of the rotation so that I could understand what was going on.  So, I had the script tell me what the rotation of a prim was (using llGetLocalRot and llOwnerSay).

Don’t bother trying to display the values in a quaternion directly– they do not make sense.  If you can understand the values in radians, then you could display the rotation vector directly using llSay. 

But you will probably prefer to see or set the values in degrees, not radians. I wanted the script to give me a chat message which would tell me the orientation of a prim, (when it was touched), in degrees.


In plain English

To find the current orientation of a prim the script asks for llGetRot or llGetLocal Rot. But this gives me the current rotation as a quaternion.  

So I had to convert the quaternion to an euler rotation vector. But that gives me the values in radians, not degrees.  

So then I get the x, y & z components of that vector, and convert those three values to degrees.  

Now I can get the script to say those values, in something that looks like a rotation vector that I can understand, ie something like: 

“The rotation is <30.00000, 20.0000, 10.00000>” 

In LSL

// Define the variables 

rotation the_quaternion; 

vector as_a_vector; 

float component_x; 

float component_y; 

float component_z; 

 

//Get the rotation of the child prim 

llGetLocalRot(the_quaternion);  

 

// Convert it to a vector 

as_a_vector = llRot2Euler(the_quaternion);  

 

//Get the x, y & z components of that vector and convert to degrees 

component_x = as_a_vector.x * RAD_TO_DEG;   

component_y = as_a_vector.y * RAD_TO_DEG; 

component_z = as_a_vector.z * RAD_TO_DEG; 

 

// Say the vector components 

llOwnerSay(“The rotation is <” + (string)( component_x) + “, “
 + (string)( component_y) + “, “
 +(string)( component_z) + “ > “);  

Go To Top

The Windmill Rotation

Tail Rotation

Turning to face the Wind

There is wind in Second Life. You can see the effect of the wind on flexible prims that have been defined to be affected by the wind (see the features tab on the editing prim menu).

I want a realistic windmill.  Since a windmill is a fixed, vertical structure, all I need is a movement around the z-axis for the top section to face into the wind.  This is a really simple rotation, and is described by the euler rotation vector <0, 0, wind_angle>.

How to get the wind angle?

In plain English

To get the wind angle, I get the wind direction as a vector from llWind(). I’m going to ignore the z (vertical) component of the wind, and just use the x and y components of that wind vector.  With those components, I can calculate the angle from geometry. 

In LSL

// Define variables 

float wind_angle; 

float x;  

float y; 

// Get the x and y components of the wind vector  

x = llWind().x; 

y = llWind().y; 

//Get the wind angle from the arc-tangent function. Note that wind_angle is in radians. 

wind_angle = llAtan2(y,x);  


Note,  when we talk about a northerly wind, it means the wind is coming from the north So the wind angle we get is the angle facing into the wind. I was fortunate, because when I built my windmill, it turns out that the angle the components have to turn is the wind angle. If I had built the windmill facing the other way along the x-axis,  I would have had to add 180° to the wind angle for my operations in the script.  At each timer event, the new positions and orientations of the moving parts are calculated from their initial positions.  I am not using incremental rotations.

Overview of the Structure

Showing Structure OrientationAt the top of the windmill structure are the parts which rotate around a central vertical shaft to face into the wind - the gearbox at the top of the central vertical shaft, the horizontal shaft and the tail.  The wheel also spins, and is, for reasons explained later, a separate object.

The structure can be rotated by the owner – to put it into a visually pleasing position on their own land.

If the root prim was one of the parts that turned, then the whole structure would (and did!) turn.

So, the vertical shaft was selected as the root prim, because it was stationary, central and did not have to turn.  This central prim has a script with a timer that polls the wind every 15 seconds and passes this information to the scripts in the gearbox, horizontal shaft and tail, using llMessageLinked, and to the script in the wheel using a llListen.

Go To Top

The rotations to be dealt with

The rotations of the wheel, horizontal shaft and the tail are all very similar problems, but I shall include the approach for each, for the sake of completeness in a tutorial article and because there are differences in what rotation quaternions you use, depending on if the prim is a child or root.

Go To Top

Initial Orientations

This diagram shows the parts of the windmill with all initial orientations removed.  Notice how the tail, the horizontal shaft and the entire wheel object, are all out of alignment.  To get them into the correct arrangement, they each had to be given an initial orientation.

Components with no intitial Orientation Components with Correct Orientation

A Broken Windmill

Go To Top

llSetRot versus llSetLocalRot  - A Broken Windmill

Initially, I used llSetRot for the gearbox, horizontal shaft and tail.  Everything worked well until I gave the entire structure a new orientation by rotating it.  The windmill  broke  - The gearbox was not the right angle, and the horizontal shaft and the tail decided to do their own thing entirely.  This is a bug in Second Life, and there is more information on it at  http://forums.secondlife.com/showthread.php?t=94705 if you are interested.

While the root prim is at < 0,0,0> rotation,  using llSetRot on the child, with the wind angle, worked. If the root has an orientation, then you must use llSetLocalRot, with the relative angle between the wind angle and the windmill structure.

For the wheel script I used llSetRot with the wind angle because the wheel is a separate object; it has no dependence on the orientation of the windmill structure itself.

Diagram

A diagram is seldom a wasted effort, and often a huge time-saver.

Consider the following diagram showing the windmill structure, the gearbox at the centre point, and the windmill tail. 

Diagram of AnglesSo when it comes to setting the rotation of the child, I will be using the relative (local) angle between the child and the parent (purple). The “Required change to Tail” is the Wind Angle minus the Structure Angle.


Go To Top

The Gearbox

Gearbox and TailThe gearbox is easy. Although it is a child prim, it does not have to change position, just its orientation (rotation) ie a simple rotation around the z-axis. 

When built, this child has an initial rotation (orientation) of <0,0,0> so that wont come into the problem.

With such simple z-axis rotations, I could use vectors, by subtracting the appropriate z-axis component. However, if this were a more complex rotation, then I would have to use quaternions. So might as well use quaternions here. Now subtracting in quaternions is done by dividing, so:


In Plain English

The gearbox just has to rotate around its centre to get to the wind angle. But since it is a child,  I need to use llSetLocalRot. Therefore I need to use the (local) relative angle between the wind and the structure’s orientation. 

The local (relative angle) between the gearbox (child) and the root prim is the wind angle minus the Structure_Orientation. 

From inside a child, you can get the Structure Orientation (as a Quaternion) using llGetRootRotation();

Expressed more as a formula

The Childs Relative Rotation Required (as a Quaternion ) = Wind Angle (as Quaternion) divided by  the Structure Orientation (as Quaternion) 

Expressed in LSL

rotation Required_Rotation;          //Define variable 

Required_Rotation = llEuler2Rot( <0.0,0.0, Wind_angle>) / llGetRootRotation 

llSetLocalRot(Required_Rotation) ;  



Go To Top

Wheel

Spinning  & Control

I want the wheel to spin smoothly, using llTargetOmega.  But the wheel itself consists of multiple prims.  The easiest way to co-ordinate all parts of the wheel so that it spins together smoothly, is to have the wheel as its own separate object, with llTargetOmega in the root prim.  This is why the structure has to communicate with the wheel using llSay on a channel, and have the wheel listen for messages on that channel.  I can't use llMessageLinked because it is not a linked prim.

So the wheel has a script that listens to the windmill structure to get the windspeed, wind direction and the position of the windmill structure.  The script then sets the spinning speed using llTargetOmega, and then moves the wheel to the correct position and orientation around the windmill structure.

Rotation around the vertical shaft

Now the wheel is not at the centre of the windmill structure – it has an offset, so we are in the orbit situation, so:

1 Set the position

2 Set the orientation

Step 1 Set the Position of the wheel

The wheel will be orbiting the around the vertical axis of the centre of the windmill.  Its position relative to the root prim is <-0.5, 0, 2.5>  (note the z-component here is from the centre of the long vertical shaft).

In Principle

A position vector that is rotated gives us a new position vector.

In plain English

There is an offset vector from the structure to the wheel in its initial position.  

If we rotate that offset vector around the z-axis by the wind angle, we will have the new position of the wheel 

Since the wheel is actually an independent object,  the wheel’s (global) position is the position of the root of the windmill structure plus its relative position (offset). 

The wind angle is based on global co-ordinates, and so is the initial offset.   The required rotation around the structure is the wind angle. 

Expressed more a formula

New_Offset_of_the_wheel (position vector) = the_initial_offset_of_the_wheel (position vector) multiplied by the rotation required (as a quaternion) 

The (global) position of the wheel = the position of the structure plus the new offset of the wheel from the structure  

Expressed in Second Life

Initial_Offset_Vector = <-0.5, 0.0, 2.5>; 

Wind_Angle_Q = llEuler2Rot (<0.0,0.0,wind_angle>); 

New_Offset_Vector = Initial_Offset_Vector * Wind_Angle_Q;  

New_Position_Vector = Structure_Position + New_Offset_Vector;  

llSetPos (New_Position_Vector); 



Step 2 Set the Orientation of the Wheel

The wheel just has to be turned around the z-axis by the wind angle. But we have the complication of the initial 90° orientation on the wheel


In principle

An orientation multiplied by a rotation to change by, gives you a new orientation.

In plain English

As we have already changed the position of the wheel, now the initial orientation of the wheel has to be rotated around the z-axis by the wind angle too. 

Because it is a separate object, and not a child, we use llSetRot. 

Expressed more as a formula

The new orientation of the wheel = initial orientation of the wheel multiplied by the required rotation.

Required rotation  = wind angle 

In LSL script

// Defining the initial orientation of the wheel in the as-built situation 

Initial_Wheel_Orientation = llEuler2Rot(< 0.0, 90* DEG_TO_RAD, 0.0>);  

// Defining the wind angle rotation as a quaternion 

Wind_Angle_Q = llEuler2Rot(<0.0, 0.0, wind_angle>); 

// Calculating the new wheel orientation. 

New_Wheel Orientation_Q = Initial_Wheel_Orientation * Wind_Angle_Q; 

// Set the new orientation of the wheel 

llSetRot( New_Wheel_Orientaion_Q); 



Go To Top

The Horizontal Shaft and the Tail

Rotation - The Problem

The scripts in the Horizontal Shaft and the Tail are exactly the same, just the numbers for their individual initial off-set positions, and initial orientations, are different, so I will base this explanation around the tail.

Similar to the wheel, the tail is not at the centre of the windmill structure – it has an offset, so we are in the orbit example, but this time, the prim is a child. So,

1 Set the position of the child

2 Set the orientation of the child

Step 1 Setting the Position

How to get the new position of the Tail? 

The Principle

A vector that is rotated gives us a new vector. 

In plain English

The tail is offset from the root prim, by an initial vector
< 1.5, 0, 2.5>. 

If we rotate that vector around the z-axis by the wind angle, we will have the new position of the tail 

Because the tail is a child, we will have to use llSetLocalPos.This means that we have to find the relative angle between the root axis, and the tail (child). Looking at the diagram from earlier on, this local angle is the Wind_angle minus the Structure_Angle. 

Note: This is very similar to the wheel, except here we are using llSetLocalPos and the position vector will be relative to the parent (root). Also, the angle that the offset is rotated by is the relative angle between the structure and the wind, ie the wind_angle minus the structure_angle 

Expressed more a formula

New Position (vector) =the initial position (vector) multiplied by the required rotation (quaternion)

Expressed in Second Life

// Set and Get the initial values 

Initial_Offset_Vector = <1.5, 0.0, 2.5>; 

Wind_Angle_Q = llEuler2Rot (<0.0,0.0,wind_angle>); 

Structure_Orientation_Q = llGetRootRotation(); 

// Determine the Relative rotation required, the change 

Angle_to_Change_By_Q = Wind_Angle_Q / Structure_Orientation_Q; 

// Calculate the new offset (local) vector 

New_Offset_Vector = Initial_Offset_Vector * Angle_to_Change_By;  

// Set the child to the new offset (local) position 

llSetLocalPos (New_Offset_Vector); 


Step 2 Setting the orientation of the Tail

The Tail ComplicationSince we are dealing with a child, we will be using local rotations, which means use llSetLocalRot.  But what is the quaternion to set the tail too?

Eeek! Now I have three Rotations to combine. 

Euler vectors can’t be combined successfully in more complex situations.  This is definitely a situation for quaternions.

The child has already been moved from initial to new position. At this new position, it needs a new orientation. 

What is that new orientation?



In principle

A rotation (or orientation) multiplied by a required rotation (change) gives you a new orientation. (same as for the wheel) 

In plain English

We should be able to set the orientation of the tail to be the wind angle minus the structure_angle, just like the gearbox. But this time the initial orientation of the child is not <0,0,0>, it is <0, 90°,0>: 

So we have to rotate that initial orientation of the tail, around the z-axis.
 
Because it is a child, we have to use llSetLocalRot. 

But that means the angle of rotation that we use has to be relative angle between the child (tail) and the root axis. Looking at the diagram, this is Wind_angle minus the Structure_Angle, (just like the gearbox).  

Expressed more as a formula

The new orientation of the child = initial orientation of the child multiplied by the required change rotation.  

As a child, we need local (relative) angles, so similar to get gearbox, the Change_Required = Wind Angle  minus Structure Orientation 

In quaternions, the Change_Requried_Q = Wind_angle_Q divided by the Structure_Orientation_Q 

In LSL script

// Set the value of the tails’ initial orientation based on its as built position 

Tail_Initial_Orientation = llEuler2Rot( < 0.0, 90*DEG_TO_RAD, 0.0>; 

// From the child, find the root’s orientation 

Structure_Orientation_Q = llGetRootRotation(); 

// Calculate the ‘change’ 

Change_Required_Q = Wind_Rotation_Q / Structure_Orientation_Q; 

// Calculate and set the new orientation. 

New_Tail_Orientation_Q = Tail_Initial_Orientation * Change_Required_Q; 

llSetLocalRot( New_Tail_Orientation_Q); 


Go To Top

Conclusion

The result?   A working windmill.    It’s a bit jerky when the 15 second timer event tells all the components to move,  but as an exercise in understanding rotations in Second Life, it was worthwhile.

Quaternions are not as scary as they sound.

When dealing with rotations in Second Life, my recommendations are:

Feel free to give some feedback on this article to Clover Mills, in Second Life; corrections and suggestions are appreciated.

Go To Top