Transformations

Overview

In the last session, we learned how to create primitive objects, how to change their colors, and a little bit about how nodes and fields interact in VRML. Today we will expand on all these ideas.

As you worked on your last project, you undoubtedly noticed some limitations in the things we can do now. All of the objects are created right on top of each other! There must be some way to move things around. Also, we learned how to change the basic shapes of objects, but not their orientation. For example, all of our cones are pointing up! What if we wanted a cone to be pointing down, or at some other angle? Also, it would be nice to have other ways to change the shapes. For example, what if we wanted some sort of elliptical shape rather than a perfect sphere?

All of these things can of course be done. The key to all of them is a node called the Transform node. Transform is a generic term meaning change the shape. There are three types of transformations available in VRML, and they are our topic of concern for today.

3D Coordinates

Although VRML takes care of most of the math for us, there are some very basic things you ought to understand. The most important is the concept of a coordinate system. Remember number lines and graphs? VRML is based on a 3-dimensional coordinate system. In 3D systems, we have three axes, usually known as X, Y, and Z. Different 3D systems can treat these axes differently, so pay close attention! In VRML, the X axis goes from left to right, parallel to the bottom of the screen. Smaller X values are on the left. The Y axis goes from top to bottom, parallel to the sides of the monitor. Smaller Y values are on the bottom. The Z axis goes from the monitor to your nose, and back into the screen. Smaller values are "deeper" into the monitor. This is difficult to visualize, but that is the primary issue of VR. We are trying to replicate a three-dimensional world on the monitor, which is a two - dimensional surface.

It might be easier to look at this world, which illustrates the coordinate system used in VRML. NOTE: You are always welcome to look at the source code, but this particular example uses some concepts we have not yet covered, so don't worry if you don't understand it.
axis.wrl

The (simplified) syntax of the Transform node

Transform {
  children [ ]
  translation 0, 0, 0
  rotation 0, 0, 1, 0
  scale 1, 1, 1
} # end transform

Transform as a grouping Node

In order to do one of the transformations, we will need to place any shapes that will be transformed into a "group." This is easily done. the Transform node is one of a series of what is called grouping nodes in VRML. What this means is this is a node that is designed to hold a bunch of other nodes. If you look at the syntax structure above, you will see that the transform has a 'children' node. This is a place to put other nodes, usually shapes. Thus, all the shapes that you want to transform in a certain way are described inside the children area. So, if we want to do transformations on a sphere, the code might look like this:

#ball
Transform {
  children [
    Shape {
      appearance Appearance {
        material Material {
          diffuseColor 0, 0, 1
        } # end material
      } # end appearance
      geometry Sphere { 
        radius .5
      } # end geometry
    } # end Shape
  ] # end children
} # end transform
Notice that I simply placed a shape definition inside the children node, and indented appropriately. While indentation is technically optional, you can see why it is such a good idea. Even our relatively simple worlds consist of many layers of nodes within nodes, and it's nice to see how things fit together. For the same reason, notice the comments that ended a particular structure. These are also a really good idea, as it is relatively easy to get totally confused about what structure you're currently in. Note also that the children node has [square brackets] instead of the more typical {curly ones.} This indicates that you could have more than one child of the node. The current code won't really do anything new, because we have defined that a transformation might happen, but we haven't specified exactly what kind of transformation we want to do.

Translation

The first kind of transformation we will examine is called translation. This refers to moving objects to different places in the coordinate system. Experiment with the following example:
X Y Z
NOTE:
This function no longer generates automatic worlds (IE removed support for this feature and Netscape is no longer universal) To see the resulting world, select the code from the window that will pop up, and use the right click (click and hold on a Mac) to copy the text. Then paste the text into your editor, save and view it. Extra credit for anybody who figures out how to get this feature back in a client-side only (we can't count on an internet connection in CD-ROM delivery) system.
Sorry about the inconvenience...
-Andy

Notice that in all of your trials with this experiment (You DID try a bunch of things, didn't you?) the code is almost identical. The only thing that is different is the values in the translate field. As you no doubt have guessed, these values are X, Y, Z coordinates, and they mean "shift the center of this object X many units along the X axis, Y units along the Y axis, and Z units along the Z axis. Translation is an ADDING function. Whatever values you place here are ADDED to the coordinates of the model. Adding by zero leaves a component alone. Adding negative values moves in a negative direction. Zero and negative values are fine here, as they make sense in this context. When you are moving objects around, you will often find yourself needing to write code, look at the world in a browser, and 'nudge' the values until it looks correct to you. This process is nothing new if you have ever done any HTML or programming. As in these endeavors, it's a very good idea to check on your progress very frequently, and to only change one thing at a time.

Another thing you might have noticed in this code was the use of the inline node. This little beauty allows me to bring in the value of any VRML file I can find on the Internet, and incorporate it into my own world. As you can guess, this is an incredibly powerful feature. In this case, it simply allows us to ignore the details of creating the axis, since it has already been created for us.

Scale

Another kind of transformation we can do is called scaling. This is a tranformation that allows us to modify an existing shape by specifying a factor to multiply on each axis. As usual, an example might make more sense than a description, so play with some values, and examine the output:
X Y Z
NOTE:
This function no longer generates automatic worlds (IE removed support for this feature and Netscape is no longer universal) To see the resulting world, select the code from the window that will pop up, and use the right click (click and hold on a Mac) to copy the text. Then paste the text into your editor, save and view it. Extra credit for anybody who figures out how to get this feature back in a client-side only (we can't count on an internet connection in CD-ROM delivery) system.
Sorry about the inconvenience...
-Andy

Notice some things about how scaling works. Zero is not a legal value, which makes sense if you think about it. The scale factor of X, for example allows you to multiply the X values by that amount. If you multiply them by zero, you will make the shape invisible. There are better ways to do that. In fact, most VRML browsers will not allow you to scale by zero. Likewise, although you could in theory scale by a negative value, it would be unnecessarily confusing, so most browsers do not allow this either. Remember, scaling is a MULTIPLYING function, so think about your values here in terms of multiplying. If you want to make the model smaller along an axis, give it a scale factor between zero and one. If you want to make the model larger along an axis, use a factor larger than 1.

Making a more complex model:

Examine this code: jet.wrl
#VRML V2.0 utf8
#Jet plane
#demonstrates scale and translation

#body
Shape {
  appearance Appearance {
    material Material {
      diffuseColor .7 .7 .7
    } # end material
  } # end appearance
  geometry Cone {
    height 3
    bottomRadius .5
  } # end geometry
}  # end shape

#wing
#notice this is EXACTLY like the body, except the scale!!
Transform {
  scale 5, .5 , .1
  children [
    Shape {
      appearance Appearance {
        material Material {
          diffuseColor .7 .7 .7
        } # end material
      } # end appearance
      geometry Cone {
        height 3
        bottomRadius .5
      } # end geometry
    }  # end shape
  ] # end children
} # end transform

#cockpit
Transform{
  translation 0, 0, .2
  scale .8, 1.2, 1
  children [
    Shape {
      appearance Appearance {
        material Material {
          diffuseColor .5 .5 1
        } # end material
      } # end appearance
      geometry Sphere {
        radius .25
      } # end geometry
    } # end shape
  ] # children
} # end transform

Try to guess what this will look like BEFORE loading it up in a browser. Now you can look at the model and wiggle it around a bit, then come back here, and let's discuss how it was designed. Things are starting to look complicated here, but there is nothing new here at all.

I started by designing the body. I figured I'd keep this really simple, so I went for a simple cone. I played around with the size and color until I was happy with it. Notice this shape is NOT inside a transformation. It's just a plain vanilla cone. (Sorry, I couldn't resist!) Notice also that I don't yet know how to turn things, so the cone is pointing up. For now, the jet is standing on its rear end. (stay tuned, we'll learn how to rotate later).

Now I decided to make the wing a sort of a delta shape, but when I thought about it, I realized I could just take a cone shape, and 'squish and stretch' it via the scale transformation to make it look like a wing. Here's the amazing thing. The fuselage and the wing are EXACTLY THE SAME CONE!!! The only difference is that I performed a scale transform on the wing. This illustrates how useful scaling can be. It allows you to take these primitive shapes, and make them into all kinds of other shapes. There is no 'flat triangle' primitive in VRML, but such a shape is easy to make when transforming a primitive shape. How did I know what values to put in the scale fields? I didn't. I guessed, but it was an educated guess. I already knew what the original cone looked like, because I had one on the screen. It was just a matter of thinking about how I wanted it changed. My thought process was something like this:

X is side-to-side. I want my wing wider than the body, so I'll multiply X by something greater than 1, maybe 5. Y is up and down. I want the wing to be not as tall as the plane (when it's standing on its tail as it is now), so I'll go with half-height, or multiply y by .5. Z is front to back. I want the wing to be thin looking, so I'll try .1 for the z factor.
Amazingly enough, all my guesses were just right. I decided not to modify them at all. This is very unusual. Most of the time you have to fiddle a bit to get things working.

Make sure your comfortable with the relationship between the wing and the body. Now we're gonna look at the cockpit.

To make the cockpit, I envisioned a 'bubble canopy' like on modern fighter planes. The shape is very much like an elongated sphere. Again, it's easy to make a sphere, and I could use the scale transformation to make it the shape I wanted, but it kept disappearing inside the fuselage. Also, I only want it to show up on one side of the plane. The answer is to have both a translation and a scale field in the Transform node of the cockpit. Take another look at the source code and see what I mean. This time, my numbers didn't come out perfect the first time, so I had to play with them a bit. I ended up moving the shape around a bit, and playing with the scale some until it looked like what I wanted. This seems complicated, but the code that you have to change is simply values inside the scale and translate fields.

Rotation

The last major transform we need to consider is the rotation. As you have no doubt guessed, this allows us to rotate an object. It is very handy, but can be a bit tricky in implimentation. As usual, we will start with a demonstration to play with. At first, leave the values of X, Y, and Z alone, and let's just concentrate on the rotation value. Note the BIZARRE default value!! Try these values as well in the rotation field: 0.785, -0.785, 2.36, -2.36, 3.14, -3.14
X Y Z Rotation
NOTE:
This function no longer generates automatic worlds (IE removed support for this feature and Netscape is no longer universal) To see the resulting world, select the code from the window that will pop up, and use the right click (click and hold on a Mac) to copy the text. Then paste the text into your editor, save and view it. Extra credit for anybody who figures out how to get this feature back in a client-side only (we can't count on an internet connection in CD-ROM delivery) system.
Sorry about the inconvenience...
-Andy

You might be experiencing some cognitive dissonance a this point. The numbers that you placed in the rotation field surely did something, but where did they come from? They seem truly random. Actually, look more carefully at the last one, which caused the jet to point straight down. Oooooh! 3.14 DOES look familar, doesn't it? 3.14 is an approximation of pi. (3.1415927 and so on, but 3.14 is good enough for our purposes) The other values are all fractions of pi. EG pi/4 is .785 (well, that's close enough for VRML anyway.)

How did pi get in here? Well, the amount that you rotate a figure is a measure of angles. We often measure angles in terms of degrees, but mathematicians prefer in another unit, radians. It seems scary, but once you get used to it, radians can actually be easier to understand than degrees. Examine the following table for some equivalences:
Fraction of a circle degrees Radians (pi) decimal Radians
1/24th 15 pi/12 .262
1/12th 30 pi/6 .524
1/8th 45 pi/4 .785
1/6th 60 pi/3 1.05
1/4th 90 pi/2 1.57
1/2 180 pi 3.14
These angles are often all you need. You can use negative values to rotate in the other direction, and you can add the values together to generate other angles.

See if you can find values that would a negative and a positive value that would point between 7 and 8 on a clock face.

This requires some practice to get used to, especially if you have never worked with radians before, but it can be very useful. Later on, you can come back to this program to experiment with some angles when you need to see them interactively.

So what about the X, Y, and Z fields?

In the example above, you have been manipulating an image around the Z axis. The part of the jet that is pierced by the Z axis is sitting still, and the model is rotating around that line described by the z axis.

The reason the model is rotating around the Z axis has to do with the first three fields of the rotation node. As you may have guessed, they represent X, Y, and Z. Usually, though, you will only have ones or zeroes in these fields, because you will often be rotating around the X, Y, and Z axes. Play around with 1, 0, 0 and 0, 1, 0 for X, Y, and Z in the rotation example above, until you understand how they work. By the way, because the XYZ fields only define an axis, 1, 0, 0 is identical to 300, 0, 0 and .0001, 0, 0 when you are defining a rotation axis. 1 is easier to work with, so that's what we usually use.

Play with other rotation vectors as well, such as 1, 1, 0, or 1, 0, -1, and see what happens. You really need to experiment a lot with the rotation transform to get it working for you, but it is worth the effort.


© Andy Harris
Indiana University / Purdue University, Indianapolis
email: aharris@klingon.cs.iupui.edu
homepage: www.cs.iupui.edu/~aharris