http://www.developer.com/
Understanding Transforms in Java 3DDecember 18, 2007 Java Programming Notes # 1552
PrefaceGeneralPart of a series This lesson is part of a series designed to start with Java 3D basics and work up to some very complicated programs, such as the program that I explained in the earlier lesson titled "Understanding Lighting in the Java 3D API" (see Resources). The first lesson in this series was titled "Back to Basics in the Java 3D API". The previous lesson was titled "Understanding Transforms in Java 2D." This lesson is titled "Understanding Transforms in Java 3D." My current plan is for future lessons to deal with user and object interaction, advanced animation, and textures in Java 3D. What you will learn In this lesson, you will learn to understand transforms in Java 3D. You will also learn how to write Java 3D code that makes use of that understanding. Compiling and running Java 3D programs In order to compile and run programs using the Java 3D API, you will need to download and install the Java 3D API software. As of the date of this writing, version 1.5.0 is available for download. In addition, you will need to download and install either Microsoft DirectX or OpenGL. All of the sample programs in this series of tutorials were developed and tested using Microsoft DirectX. They were not tested using OpenGL. Viewing tipI recommend that you open another copy of this document in a separate browser window and use the following links to easily find and view the figures and listings while you are reading about them. Figures
Listings
Supplementary materialI recommend that you also study the other lessons in my extensive collection of online Java tutorials. You will find a consolidated index at www.DickBaldwin.com. General background informationTypes of transforms of interest This lesson will be concerned with transforms of the following types:
Prerequisite background knowledge To understand transforms in either 2D or 3D (particularly the rotation transform) you need to have some understanding of trigonometry. I'm sorry, but that is simply the way that it is. In addition, it will be helpful if you have some knowledge of matrices and matrix algebra. While I will do my best to help you understand transforms, trying to teach you the requisite background knowledge is beyond the scope of this tutorial. I will provide only a brief discussion of the requisite background. For technical background, I recommend that you study the previous lesson titled "Understanding Transforms in Java 2D" and the articles titled "Trigonometry", "Planar transformations", "Spatial transformations", and "A little algebra" (see Resources). Visible objects in Java 3D Just to get us started, Figure 1 shows some spheres produced using Java 3D. The quality of the rendering of the yellow and white spheres in Figure 1 was purposely kept low to cause the facets to be visible. Figure 1. Visible objects in Java 3D. Constructed using a mesh Visible objects in 3D are typically constructed by constructing a mesh of inter-connected polygons (usually triangles) and then covering that mesh with a skin. For example, Figure 2 shows a sphere rendered in 3D with its skin intact. Figure 2. A sphere rendered in 3D with its skin intact. Let's see the mesh Figure 3 shows the same sphere rendered in 3D with its skin removed. (Figures 2 and 3 were created using the Alice program development environment. See Resources.) Figure 3. A sphere rendered in 3D without its skin. Figure 3 shows the mesh of inter-connecting triangles that I mentioned earlier. A wireframe drawing A rendering of the mesh as shown in Figure 3 is often referred to as a wireframe drawing. (Figure 3 is an Alice wireframe rendering. Most wireframe drawings probably don't include the 3D rendering created through the use of the light source in Figure 3. Wireframe drawings are often just black lines on a white background.) Visual attributes Various visual attributes of the object (such as shading, color, etc.) are achieved by the color, intensity, and other appearance factors that are applied to the section of skin that is drawn to cover each individual triangle. For example, in Figure 2, the skin sections covering the triangles in the upper right are very light while the skin covering the triangles in the lower left are much darker. This effect is used to create the optical illusion that we refer to as a 3D rendering. Controlling the quality Often, it is possible to control the number of triangles that are used to construct the mesh. Up to a point, the quality of the 3D rendering of an object can be improved by constructing the mesh with more triangles. For example, the rendering quality of the yellow and white spheres in Figure 1 was purposely made low in order to expose the facets on the surface of the two spheres. A wireframe drawing of these spheres would show that it was constructed with a small number of triangles. The sphere in Figures 2 and 3, on the other hand, was constructed using a larger number of triangles, which resulted in better rendering quality. The sphere in Figure 2 looks more like a true sphere than the yellow and white spheres in Figure 1. Just to show that these concepts apply to more complex visual objects than spheres, Figure 4 shows a solid rendering and a wireframe rendering of the Alice ice skater object. (A good way to get some experience in 3D programming very quickly is to visit my web page titled "Learn to Program using Alice" (see Resources) and study some of the tutorial lessons that you will find there.) Figure 4. Two renderings of the Alice ice skater. As you can see, the object in Figure 4 is constructed from hundreds of inter-connected triangles. What is a point? A point is a location in 3D space defined by three coordinate values (x,y,z). The value for x defines the location of the point relative to an origin along the x-axis (see the red line in Figures 2 and 3.). The value for y defines the location of the point along the y-axis (see the green line in Figures 2 and 3). The value for z defines the location of the point along the z-axis (see the blue line in Figures 2 and 3.). The x, y, and z axes intersect at the origin in 3D space. The sphere in Figures 2 and 3 is centered on that origin. The locations where the triangles connect to one another in Figures 3 and 4 are referred to as the vertices. The location of each vertex is specified by a point and its three coordinate values. Generally speaking, the transforms that are applied to 3D objects involve modifying the coordinate values that specify each vertex. The x, y, and z axes The axes may or may not be visible in the graph. (I purposely made them visible in Figures 2 and 3.) However, even when they are not visible, they are assumed to be present. The x-axis is typically considered to be the horizontal axis with positive values going from left to right. This is the case in Java 3D. The y-axis is typically considered to be the vertical axis with positive values going from bottom to top. (Fortunately, unlike Java 2D, the positive direction for y in Java 3D is from bottom to top.) The z-axis is typically considered to go from back to front, protruding out of the screen toward the viewer. In Java 3D, the positive z direction is toward the viewer. Where is the origin? In Java 3D, the origin is at the center of the universe. While it is possible to change the apparent location of the origin on the screen by working with the ViewingPlatform, the origin will be generally at the center of the Canvas object used to display the universe in this program. What is a vector? According to Wikipedia (see Resources), there are several kinds of vectors. A spatial vector is an object defined by both magnitude and direction; in contrast to a scalar, an object with magnitude only. Also, according to Wikipedia, the word vector is used to describe a one-dimensional, directional matrix; a row vector or a column vector. I will use the word vector in both senses (spatial and matrix) in this lesson. Hopefully the sense in which the word is being used will be clear from the context of the discussion. What is a transform? In the sense that it will be used in this lesson, a transform is an operation that converts the coordinate values for every point that defines an object into a different set of coordinate values. The object that is defined by the new coordinate values will probably be recognizable as representing the original object, although it may be a different size (scale), may be in a different location (translation), may have a different orientation (rotation), may be squashed in one dimension or the other, or may be flipped across one or both of the axes. What is an affine transform? The transforms that will be used in this lesson are actually affine transforms. According to the Java 3D documentation: "An affine matrix can translate, rotate, reflect, scale anisotropically, and shear. Lines remain straight, and parallel lines remain parallel, but the angle between intersecting lines can change. In order for a transform to be classified as affine, the 4th row must be: [0, 0, 0, 1]." Translation To translate an object means to move the object from one location in 3D space to a different location in 3D space. (You will see how this is accomplished later.) We can translate an object by translating all of the points that define the object. We can translate a point by adding offsets to the x, y, and z coordinate values that define the location of the point. If we express the original coordinates of a point as x1, y1, and z1, and express the offsets as tx, ty, and tz, we can express the coordinates of the new location of the point as: x2 = x1 + tx y2 = y1 + ty z2 = z1 + tz Scaling To scale an object means to change the locations of all the points that represent the object according to a very specific formula. Typically, but not always, a scaling transform will change the size of the object, making it larger or smaller. (Note that scaling by factors of 1 or -1 doesn't change the size.) We can accomplish a scaling transform by multiplying the x, y, and z coordinate values of all the points that define the object by the same or by different scale factors. If the scale factors used to multiply the coordinate values are the same, the object will simply become larger or smaller (and could be flipped across the axes if the scale factors are negative). If the scale factors applied to each axis are different, the object may be squashed in one dimension or the other, or possibly even flipped across the axes, but will probably still be recognizable as representing the same object. The scale factors that are used in a scaling transform are expressed relative to the origin. For example, a scale factor of 2.0 will cause a coordinate value for a point to represent the coordinate value for a new point that is twice as far from the origin. If we express the original coordinates of a point as x1, y1, and z1, and express the scale factors as sx, sy, and sz, then we can express the coordinates of the new location of the point as: x2 = x1 * sx y2 = y1 * sy z2 = z1 * sx Rotation Rotation is much less intuitive than either translation or scaling, and requires a fair knowledge of trigonometry to understand. However, it is possible to create and use rotation transforms in a cookbook fashion without understanding why they work. The following equations for rotating a point around the origin in 2D space are derived in "Planar transformations" (see Resources), where v represents the angle of rotation. x2 = x1 * cos(v) - y1 * sin(v) y2 = x1 * sin(v) + y1 * cos(v) These equations are extended into 3D in "Spatial transformations" (see Resources). Things get a good bit more complicated in 3D. In 2D, you can only rotate within the x-y plane, meaning that you can only rotate around the origin. However, in 3D, you are not constrained to rotate within any one of the x-y, y-z, or z-x planes. Rather, you can rotate around any or all of the three axes. As a result, there are three sets of rotation equations involved. I will show you those three sets of equations later in conjunction with the matrix equations for transforms. Positive angle of rotation The author who derived these equations tells us that they assume that a positive rotation angle is counterclockwise around the axis. What this means is that if you stand at the positive end of an axis and face the origin, a positive angle of rotation around that axis will be counterclockwise. Matrices In the earlier lesson titled "Understanding Transforms in Java 2D" (see Resources), I taught you about the use of matrices to perform transforms in Java 2D. I also taught you about homogeneous coordinates. The article titled "Planar transformations" (see Resources) gives us the matrix equations shown in Figure 2, which can be solved to accomplish translation, scaling, and rotation transforms in 2D. Figure 5. Matrix operations for the three basic Java 2D transforms.
Transform equations for Java 3D Although not nearly as explicit, in the article titled "Spatial transformations" (see Resources), that same author gives enough information to make it possible for us to extend the 2D equations into a set of 3D equations. The 3D versions of the homogeneous transform matrix equations are shown in Figure 6. Figure 6. Matrix operations for the five 3D transforms.
It is probably worth noting that the 3D matrix equation for rotating around the z-axis in Figure 6 is very similar to the 2D rotation equation shown in Figure 5. One way to think about this is that 2D is simply 3D constrained to include only the x-y plane. Therefore, rotation around the origin in 2D is analogous to rotation around the z-axis in 3D. Compound operations As you learned in the earlier lesson on 2D, one of the advantages of using the homogeneous form of the matrix equations is that this makes it possible to combine a series of transforms into a single matrix multiplication by first multiplying the individual matrices that represent of the individual transforms to produce a single compound matrix. Instead of having to perform a series of individual transforms on each point that represents an object, a compound transform matrix can be created first, and each point can be transformed by multiplying the column vector that represents that point by the compound matrix that represents the entire set of transforms. The savings in computational requirements can be enormous. The good news It is possible to write Java 3D programs where almost your entire thought process revolves around the creation and use of transform matrices down to the element level. However, the good news is that it is possible to write most Java 3D programs without having to think too deeply about matrices. This is because the Java 3D API provides convenience methods that abstract most of the matrix operations behind a fairly common Java programming interface. Even when you use the convenience methods, however, it is a good idea to have some appreciation as to what is actually taking place behind the curtains. The overall Java 3D program structure The overall program structure of a Java 3D scene is a hierarchical tree. The universe is the root of the tree. Moving down from the root, we next encounter an object of the class Locale. Here is a little what Sun has to say about the Locale class:
Thus, the Locale object is a node in the tree, which may have one or more children of the class BranchGroup. The BranchGroup class Here is some of what Sun has to say about the BranchGroup class: "The BranchGroup serves as a pointer to the root of a scene graph branch; BranchGroup objects are the only objects that can be inserted into a Locale's set of objects." Thus, the BranchGroup is also a node in the tree, which may have one or more children of the type Node. The Node class is an abstract class that, as of Java 3D version 1.5.0, has two subclasses:
This means, therefore, that a BranchGroup object may have any number of children of the types Group and Leaf. The Leaf class Here is some of what Sun has to say about the Leaf class: "The Leaf node is an abstract class for all scene graph nodes that have no children. Leaf nodes specify lights, geometry, and sounds. They specify special linking and instancing capabilities for sharing scene graphs and provide a view platform for positioning and orienting a view in the virtual world." In this program, we will be using a ColorCube object, which is a subclass of the Leaf class. This tells us that a ColorCube object can be added as a child of a BranchGroup object, which we will do. The Group class Here is some of what Sun has to say about the Group class: "The Group node object is a general-purpose grouping node. Group nodes have exactly one parent and an arbitrary number of children that are rendered in an unspecified order (or in parallel). Null children are allowed; no operation is performed on a null child node. Operations on Group node objects include adding, removing, and enumerating the children of the Group node. The subclasses of Group node add additional semantics." The subclasses of the Group class are:
Several of these classes, in turn, have several different subclasses. Thus, there are a fairly large number of types of objects that can be added as children to a BranchGroup object. However, in this program, we will use only Leaf objects and TransformGroup objects as children of a BranchGroup object. The TransformGroup class Here is some of what Sun has to say about the TransformGroup class:
Contains a transform... Although the documentation states that a TransformGroup object "contains a transform," it is important to understand that the transform is not a child of the object. Rather, the transform is a property of the object that is established by calling the setTransform method on the object, passing a reference to an object of the type Transform3D as a parameter. Thus, there can be only one transform associated with a TransformGroup object, whereas the object may have many children of the type Node. The effects are cumulative... What this means is that the effect of the transform contained in a TransformGroup object will be applied to all objects that are children, grand children, great grandchildren, etc. of the TransformGroup object in the hierarchy. A BranchGroup hierarchy Figure 7 shows the BranchGroup hierarchy for the program that I will present and explain in this lesson. Figure 7. BranchGroup hierarchy for the program named Java3D010.
Example screen output and user input GUI Figure 8 shows an example of the user input GUI and the screen output produced by the program. Figure 8. Sample screen output and user input GUI for Java3D010. The user input GUI The user input GUI on the right side of Figure 8 makes it possible for the user to enter parameters to control the behavior of the four transforms that are executed in the program:
The Replot button When the user clicks the Replot button at the bottom of the input GUI, the program constructs and displays a new 3D universe as shown on the left in Figure 8, making use of the user input parameters and the TransformGroup hierarchy shown in Figure 7. Eight leaf objects There are eight Leaf objects in the hierarchy and they are all objects of the class ColorCube. Those eight objects are highlighted in Italics in the hierarchy in Figure 7. They are also clearly visible in the universe in Figure 8. The universe in Figure 8 shows two sets of 3D axes (three objects per set) constructed from long skinny ColorCube objects. One set of axes is in the upper right portion of the universe image in Figure 8. The other set protrudes from inside a large ColorCube object with a blue face on the front, a red face on the left side, and a violet face on the top. The remaining ColorCube object is centered on the origin in 3D space with its red face showing. Transforms highlighted in boldface The transforms that are executed by the program are shown as boldface words inside parentheses in Figure 7. The four transforms shown in red boldface are the transforms over which the user has control via the entry of parameters in the input GUI in Figure 8. (These are the transforms listed in the leftmost column in the GUI in Figure 8.) The transforms shown in black boldface in Figure 7 have hard-coded parameters that are outside the control of the user. For example, the two black scale transforms near the top and bottom of Figure 7 are used to scale the dimensions of ColorCube objects to turn them into long skinny objects for use as the 3D axes shown in Figure 8. Similarly, the black rotate transform near the middle of Figure 7 is used to rotate a ColorCube object so that its blue face will be toward the user when it first appears on the screen (perpendicular to the z-axis). The redCube object The ColorCube object named redCube near the top of Figure 7 is positioned above all transforms in the hierarchy. Therefore, it is not affected by any of the transforms and it appears at the center of the universe in Figure 8 in its natural form. The natural form has it located at the origin with the red face perpendicular to the z-axis. The red translate and rotate transforms The red translate and rotate transforms near the top of Figure 7 correspond to the top two transforms in the list in the leftmost column of the input GUI in Figure 8. These two transforms affect the remaining seven ColorCube objects at the leaves of the hierarchy because all seven of those leaf objects belong to TransformGroup objects that are descendants (children, grand children, etc.) of the group named rotatedGroup. The topmost black scale transform However, the black scale transform shown with the plainAxisGroup in Figure 7 affects only the three ColorCube objects that are children of the plainAxisGroup. This transform is a property of that group and none of the other ColorCube objects are children of that group. The red translate and scale transforms The red translate and scale transforms near the center of Figure 7 correspond to the bottom two transforms in the list in the leftmost column of the input GUI in Figure 8. These two transforms affect the remaining four ColorCube objects because they are all descendants of the group named scaledGroup. However, they don't affect the four ColorCube objects that appear further up the hierarchy. Keep these hierarchical relationships in mind You should keep these hierarchical relationships in mind later as you run the program and experiment with the input parameters that control the behavior of the four transforms shown in red boldface in Figure 7. Enough background, let's see some code With that as background material, it's time to examine a program that makes it easy to experiment with and hopefully easy to understand how to create and to use transforms in Java 3D. PreviewIn this lesson, I will present and explain a Java 3D program named Java3D010. This program is very similar to the Java 2D program named Java2D001 in the earlier lesson titled "Understanding transforms in Java 2D" (see Resources). Purpose The purpose of this program is to make it easy to experiment with the following four transforms executed in sequence:
Operation The program creates a user input GUI that can be used to vary the parameters used for the sequence of transforms listed above (see Figure 8). Eight ColorCube objects are contained in the universe. Two of the ColorCube objects are shown in Figure 8 as the red square and the red and blue cube. The remaining six ColorCube objects are arranged into two sets of three per set. These are long skinny ColorCube objects that are arranged to simulate two visible sets of 3D axes. The ColorCube objects are inserted as children of TransformGroup objects at different levels of the BranchGroup hierarchy (see Figure 7). Because they occur at different levels of the hierarchy, the different objects are subjected to different transforms depending on their position in the hierarchy. Only the cube with the blue face showing and its associated visible axes are subjected to all four transforms in the above list. A Replot button allows the user to modify input parameters, re-compute the transforms, and produce a new output by clicking the button. Clicking the Replot button when one of the input fields contains String data that cannot be converted to a numeric type will cause the program to abort with a NumberFormatException. For example, a blank field falls into this category. The program was tested using Java SE 6, and Java 3D 1.5.0 running under Windows XP. Experimental ResultsTranslate and then rotate One of the main purposes of this program is to provide you with a tool for experimenting with transforms in Java 3D. I will walk you through a few experiments to get you started down that path. Let's begin by taking a look at what happens if you reverse the order of rotation and translation. We will begin by translating first and then rotating. Recall from Figure 7 that the universe contains eight ColorCube objects. The top left image in Figure 9 shows the result of translating seven of those objects by the distances and in the directions shown in the top row of input parameter values in the GUI to its right. No rotation was applied to those objects at this point. Figure 9. Translation followed by rotation. Which objects are affected? Referring back to the hierarchy in Figure 7, we see that this translation transform affects all of the objects other than the object referred to by redCube. Thus, the redCube object remains centered on the origin while all of the other objects are translated to the right, up, and forward toward the viewer. Add a rotation transform The bottom left image in Figure 9 shows the result of rotating the seven objects by the angles and around the axes shown in the second line of parameters in the GUI in the bottom right after those seven objects have been translated by the distances and in the directions given in the top right GUI. Prior to this rotation, the axes belonging to each of the seven objects were parallel to the corresponding axes belonging to the universe. However, once this rotation transform has been executed, the axes belonging to those objects are no longer parallel to the axes belonging to the universe. Rather, those axes are now aligned with the little stubs protruding from the large cube. (Each of these stubs is itself a long skinny ColorCube object.) The stub protruding from the blue face represents the new direction for the positive z axis belonging to the large cube. The stub protruding from the violet face represents the new direction for the positive y axis. The other two stubs represent the new directions for the positive and negative x axes. Transform matrices for parameters shown in Figure 9 Figure 10 shows the transform matrices for the parameters shown in the two GUIs in Figure 9. The matrices on the left in Figure 10 correspond to the top right GUI in Figure 9. The matrices on the right in Figure 10 correspond to the bottom right GUI in Figure 9. Figure 10. Transform matrices for parameters shown in Figure 9.
Individual transform matrices Note that each matrix shown in Figure 10 is an individual transform matrix. The top matrix shown in each group corresponds to the default transform matrix that is applied if no other transforms are performed. Each of the remaining four matrices in the group corresponds to one of the transforms listed in the user input GUI. The order of the display of the transform matrices in Figure 10 is reversed relative to the order of the transforms listed in the GUI in Figure 9. The bottom matrix in a group corresponds to the first transform at the top of the GUI. The matrix immediately below the default matrix corresponds to the last transform at the bottom of the GUI. The compound transform matrix The actual transform that is applied to seven of the ColorCube objects is the product of some of the matrices in the group. The interesting values in the matrices in Figure 10 are highlighted in boldface. If you scan down the matrices on the left in Figure 10, you will see that every matrix except the last one is the same as the default matrix at the top. The default matrix is an identity matrix. The product of two or more identity matrices is another identity matrix. Only the bottom matrix in the group of matrices on the left in Figure 10 will cause the product of the matrices to be different from the identity matrix. In fact, since the bottom matrix is the only matrix that is different from an identity matrix, the product matrix will be identical to the bottom matrix. The translation values The three boldface values in the bottom matrix are the translation values: tx, ty, and tz (see Figure 6). You will note that these three values match the three parameter values for the first translation in the top right GUI in Figure 9. Add the rotation transform The group of matrices on the right in Figure 10 contains two transform matrices that differ from the identity matrix. One is the translation matrix at the bottom right. It is the same as the translation matrix at the bottom left. The other matrix that differs from an identity matrix is the rotation matrix that is second from the bottom on the right. This rotation matrix is actually the product of the three individual transform matrices that represent rotation around the x, y, and z-axes shown in Figure 6. If you are of a mind to do so, you should be able to get the sine and cosine values for the angles involved, enter those values into three transform matrices using the matrix formulas shown in Figure 6, multiply them together, and replicate the values shown in Figure 10. Rotation followed by translation Now we will reverse the order of translation and rotation. Figure 11 shows the result of rotating and translating the seven objects by the same parameter values as in Figure 9, but reversing the order. Figure 11. Rotation followed by translation. Two cubes in the same place The top left image in Figure 11 shows the result of applying rotation without first applying translation. This image may look strange at first, but it makes sense once you think about it. The image in the top left of Figure 11 shows two cubes of the same size, both centered at the origin in 3D space. (There are also six of the long skinny ColorCube objects centered on the origin as well.) The cube with the blue face has been rotated around all three axes. The cube with the red face has not been rotated. Therefore, what we see in this image is the intersection of the two cubes. Only the portions of the red cube that protrude outside of the blue cube are visible. (There is nothing to prevent two objects from occupying exactly the same space in Java 3D.) Now translate seven of the cubes The bottom left image in Figure 11 shows the result of following the rotation with translation by the same distances and in the same directions as in Figure 9. If you compare the bottom left image in Figure 11 with the bottom left image in Figure 9, you will see that they are significantly different. Translation causes each of the cubes (other than the red cube) to be translated along the axes belonging to the cube. Since those axes were pointing in different directions in Figures 9 and 11 when the translation took place, the cubes ended up in a different location in 3D space in each of the two cases. The transform matrices I will leave it as exercise for the student to interpret the transform matrices that are displayed on the command line screen. Translation followed by rotation followed by translation and scale Figure 12 shows the result of:
Therefore, Figure 12 illustrates all four of the types of transforms that are the topic of this lesson. Figure 12. Translation followed by rotation followed by translation and scale. Translate and rotate The top left image in Figure 12 shows the result of executing a translation followed by a rotation. Recall from Figure 7 that this universe contains eight ColorCube objects. One has its red face perpendicular to the z-axis, which is the viewpoint of the viewer. Also, recall from Figure 7 that no transforms are applied to this object. Therefore, it always looks like a red square centered on the origin. The remaining seven ColorCube objects consist of two groups of three, which are used to simulate two sets of 3D axes, plus one that is initially oriented so that its blue face is perpendicular to the z-axis. Translation and rotation transforms affect seven of the eight objects Recall from Figure 7 that the first translation and the rotation affect all seven of these ColorCube objects in the same way. As a result, following the first translation and rotation, all seven objects are located at the same point in 3D space with the same orientation. At this point, the six ColorCube objects that simulate the 3D axes occupy the same space (in pairs) so they are visually indistinguishable from one another in pairs. Furthermore, because they are centered on the same point as the cube with the blue face, they appear to protrude from the four faces of the cube as shown in the top image in Figure 12. Translate and scale four of the seven objects The bottom image in Figure 12 shows the result of applying an additional translation along the x-axis followed by a scaling transform that scales along the x-dimension. Referring back to Figure 7, we see that these two transforms affect only four of the ColorCube objects. The affected objects are the object with the blue face and one set of three objects that are used to simulate a set of 3D axes. These four objects are affected in exactly the same way. They are translated along the x-axis and then they are scaled along the x-dimension. Three objects are left behind Because one set of three ColorCube objects is not affected by these two transforms, those three objects remain in their current location at their current size and orientation as shown in the bottom left image in Figure 12. (These are the three long skinny ColorCube objects in the upper right portion of the image.) Four objects are translated The other four objects are translated in the direction of their respective x-axes. Since all four objects have the same orientation, they are all translated by the same distance in the same direction. Thus, they stay together and continue to be centered at the same point in 3D space. A very important point It is very important to note that when an object is translated or rotated in Java 3D, that translation or rotation takes place relative to the axes belonging to that object and do not take place relative to the axes belonging to the universe. Thus, an object that has previously been rotated such that its axes are not parallel to the axes belonging to the universe will not move parallel to the x-axis of the universe when it is translated in the x direction. In this case, the four ColorCube objects move down, to the left, and slightly forward toward the viewer. You will note that the translated long skinny ColorCube object that has its maximum dimension in x appears to be aligned with the corresponding long skinny ColorCube object that was not translated and remains in the upper right portion of the image. This is because the x axes belonging to these two objects coincide. They coincide because they were earlier translated and rotated in an identical fashion. Discussion and sample codeAs is my custom, I will present and explain this program in fragments. A complete listing of the program is provided in Listing 15. Before getting into the main body of the program, I will present and explain several utility methods that are called throughout the main body of the program. The first utility method that I will explain is a method named translate, which is shown in its entirety in Listing 1. The method named translate This is the method that is called in the main body of the program whenever it is necessary to create a TransformGroup object that will translate a specific node in addition to all of its other children. Listing 1. The method named translate.
A parameter of type node This method receives two incoming parameters. The first parameter is a reference to an object of the type Node. As we learned earlier, Node is the superclass of both Leaf and Group. Therefore, the first parameter could be a reference either to a visible object such as ColorCube, or to another TransformGroup object. A parameter of type Vector3f The second parameter is a reference to an object of type Vector3f. This object encapsulates three numeric values of type float, which are the desired translation distances along the x, y, and z axes. The purpose of the method The purpose of the method is to create and return a TransformGroup object that will translate that node and all of its children (as well as any other children that may be added to the group later) according to the distances encapsulated in the vector. A new TransformGroup object This method begins by creating a new Transform3D object and setting its translation property to the vector object containing the translation distances. Then it creates a new TransformGroup object and sets its transform property to the new Transform3D object. Two alternatives programming approaches One alternative would have been to eliminate the first parameter and simply return the new TransformGroup method at this point. If programmed that way, the code that called the method could immediately add the node as a child to the TransformGroup. However, I elected to incorporate that step into the method. The incoming node object is added as a child to the new TransformGroup object (by calling the addChild method) and the TransformGroup object with the node already added as a child is returned. The method named scale This is the method that is called in the main body of the program whenever it is necessary to create a TransformGroup object that will scale a specific node in addition to all of its other children. Listing 2. The method named scale.
As is the case with the translate method explained above, this method receives a Node parameter and a Vector3f parameter. In this case, the purpose is to create and return a TransformGroup object that will scale that specific node and all of its children in addition to any other children that may also be added as children of the group. The code in this method is almost identical to the code in the translate method. However, that method calls the setTranslation method on the new Transform3D object to set its transform property, whereas this method calls the setScale method on the new Tarnsform3D object to set its scale property. The method named tiltTheAxes The purpose of the method is to create and return a TransformGroup object that will cause all of the leaf objects that are descendants of a specific node (plus all of the leaf objects that are descendants of any other nodes that may later be added as children of TransformGroup object) to be rotated around their x, y, and z axes. Except for a change in the name of one variable, this method is identical to a method having the same name that I explained in the lesson titled "Combining Rotation and Translation in Java 3d" (see Resources). Therefore, I won't repeat that explanation in this lesson. The method named displayMatrix This method receives an incoming parameter that is a reference to a Transform3D object and displays the contents of the 4x4 matrix contained in that Transform3D object. Listing 3. The method named displayMatrix.
The code in this method is very straightforward. The comments in Listing 3 should suffice as an explanation. The method named getAxesGroup This is a convenience method that constructs and returns a TransformGroup object containing a set of what look like three orthogonal axes. (See the upper right portion of the bottom left image in Figure 12.) Each of the three individual axes is constructed from a long skinny ColorCube object. Listing 4. The method named getAxesGroup.
The code in this method is straightforward and shouldn't require any explanation beyond the embedded comments. Beginning of the class named Java3D010 As I mentioned earlier, a complete listing of the program is provided in Listing 15. All of the code down to the beginning of the constructor for the class named Scene is totally straightforward. That code deals with creating the GUI, servicing the Replot button, etc. Beginning of the constructor for the class named Scene The Scene class is an inner class from which the universe is instantiated. One of the functions of the Replot button is to dispose of the old Scene object and to create a new Scene object. When the new Scene object is created, it will be created according to the transform parameter values that have been entered by the user into the GUI shown on the right in Figure 8. The constructor for the Scene class begins in Listing 5. Listing 5. Beginning of the constructor for the class named Scene.
A temporary TransformGroup object Listing 5 begins by creating a temporary TransformGroup object for the sole purpose of displaying the default transform matrix that is contained in a new TransformGroup object. The default transform matrix is the identity shown at the top left of Figure 10. The big bang! Then the code in Listing 5 calls the following two methods to create the universe:
The code in these two methods is similar or identical to code that I have explained in earlier lessons on Java 3D so I won't repeat that explanation in this lesson. Create and prepare two ColorCube objects Listing 6 creates the two different ColorCube objects shown in Figure 8. One of them is rotated around its vertical axes so that its blue face is perpendicular to the z axis. This object is added as a child to the TransformGroup named blueCubeGroup. Listing 6. Create and prepare two ColorCube objects.
Add visible axes to the blueCubeGroup Listing 7 calls the getAxesGroup method to get a set of visible axes (constructed from long skinny ColorCube objects). They are added as children to the blueCubeGroup, producing the blue cube with the protruding axes shown in Figure 8. Listing 7. Add visible axes to the blueCubeGroup.
Construct the transform hierarchy We have now eaten our way through the appetizers and it's time for the main course. The code that follows constructs the hierarchy of Leaf andTransformGroup objects. The transforms in the hierarchy are constructed using the parameter values entered by the user in the GUI shown in Figure 8. Note that this code constructs the hierarchy from the leaves toward the root. This is the reverse of the order in which the transforms are listed in the GUI. Create and display a scaling transform Listing 8 creates a scaling transform that will be applied to the blueCubeGroup. According to the hierarchy in Figure 7, the blueCubeGroup includes the blue cube (anonymous ColorCube with the blue face in front) and the blueCubeAxes. Listing 8. Create and display a scaling transform.
Then Listing 8 displays the scaling transform matrix. Create and display a translation transform Listing 9 creates and displays a translation transform. (This is the translation transform that is listed near the bottom of the GUI in Figure 8.) This transform will be applied to the scaledGroup. According to the hierarchy in Figure 7, the scaledGroup contains the blueCubeGroup, which in turn contains the blue cube and the blueCubeAxes. Only the blue cube and blueCubeAxes will be affected by this transform. Listing 9. Create and display a translation transform.
Note that the blue cube will be translated in a direction indicated by the corresponding axes belonging to the blueCubeGroup, even if the axes belonging to the blueCubeGroup have been rotated. Create and display a rotation transform Listing 10 creates a rotation transform that will be applied to the secondTransGroup. According to the hierarchy in Figure 7, the secondTransGroup contains the scaledGroup, which in turn contains the blueCubeGroup. Listing 10. Create and display a rotation transform.
Will also apply rotation to a new set of axes As you will see in Listing 11, the rotation transform will also be applied to a new set of simulated axes named plainAxesGroup. The purpose of adding plainAxesGroup at this point is to show the location and orientation of the blue cube immediately before the final translation is executed. (See Figure 8.) The blue cube, blueCubeAxes, and plainAxesGroup will all be rotated. Rotation takes place around the local axis (rather than the axes belonging to the universe), even if the cube has been translated away from the origin. Create and add another set of axes Listing 11 creates the plainAxesGroup and adds it to the rotatedGroup for the purpose described above. Listing 11. Create and add another set of axes.
Create and display another translation transform Listing 12 creates and displays the translation transform shown at the top of the GUI in Figure 8. This translation will be applied the rotatedGroup. According to the hierarchy in Figure 7, the rotatedGroup contains the plainAxesGroup and the secondTransGroup, which in turn contains the scaledGroup. The scaledGroup contains the blueCubeGroup. Thus, the plainAxesGroup, the blue cube, and blueCubeAxes will all be translated by this transform. Listing 12. Create and display another translation transform.
Complete the hierarchy Listing 13 completes the hierarchy by adding the firstTransGroup to the mainBranchGroup. Listing 13. Complete the hierarchy.
Finally, the code in Listing 13 adds a ColorCube object with the red face exposed as a child of the BranchGroup object. This cube is not subjected to any of the transforms. Therefore, it is centered on the origin of the universe when the universe is rendered, marking the location of the origin. Complete the constructor and the classes Listing 14 completes the constructor for the Scene class. It also signals the end of the Scene class and the class named Java3D010. Hence, Listing 14 is the end of the program. Listing 14. Complete the constructor and the classes.
Run the programI encourage you to compile and execute the code from Listing 15. Experiment with the code, making changes, and observing the results of your changes. Remember, you will need to download and install the Java 3D API plus either Microsoft DirectX or OpenGL to compile and execute these programs. See Download for links to the web sites from which this material can be downloaded. SummaryIn this lesson, you learned to understand transforms in Java 3D and how to write Java 3D code that makes use of that understanding. What's next?The topics for future lessons include interactive Java 3D programs, advanced animation, and surfaces. Download
Resources
Complete program listingA complete listing of the program discussed in this lesson is presented in Listing 15 below. Listing 15. Listing for the program named Java3D010.
CopyrightCopyright 2007, Richard G. Baldwin. Reproduction in whole or in part in any form or medium without express written permission from Richard Baldwin is prohibited. About the authorRichard Baldwin is a college professor (at Austin Community College in Austin, TX) and private consultant whose primary focus is a combination of Java, C#, and XML. In addition to the many platform and/or language independent benefits of Java and C# applications, he believes that a combination of Java, C#, and XML will become the primary driving force in the delivery of structured information on the Web.Richard has participated in numerous consulting projects and he frequently provides onsite training at the high-tech companies located in and around Austin, Texas. He is the author of Baldwin's Programming Tutorials, which have gained a worldwide following among experienced and aspiring programmers. He has also published articles in JavaPro magazine. In addition to his programming expertise, Richard has many years of practical experience in Digital Signal Processing (DSP). His first job after he earned his Bachelor's degree was doing DSP in the Seismic Research Department of Texas Instruments. (TI is still a world leader in DSP.) In the following years, he applied his programming and DSP expertise to other interesting areas including sonar and underwater acoustics. Richard holds an MSEE degree from Southern Methodist University and has many years of experience in the application of computer technology to real-world problems. |