Polygon Groups

In this section, you'll implement a container for groups of polygons, where every polygon group has its own transform. So far we've talked only about moving a 3D object as a whole. In real life, however, you might want different parts of a 3D object to move independently of one another. For example, a robot's arm might move separately from its body, and its fingers might move separately from its arm. And when the robot moves, its arm moves with it (unless the player "amputated" the arm at an earlier point of time, of course). To allow for movement like this, you'll allow polygon groups to have child polygon groups, each with their own transform, effectively giving each polygon group its own coordinate system. See Screenshot, for example. In this figure, the 3D object is actually composed of two polygon groups: the "turret" and the "base." This way, the turret can rotate and move independently from the base.

Screenshot 3D objects can be composed of multiple child polygon groups with their own coordinate system. In this example, the turret can move independently of the base.

Java graphics 09fig06.jpg


Also in this example, the turret's location is centered right above the base group. This way, when the turret rotates left or right, it rotates around the base of the object. You wouldn't want the turret to rotate around, say, the antennae, because then it wouldn't appear that the turret was actually connected to the base. The turret and the base have their own transforms, and the entire object has its own transform as well. This way of representing polygon groups creates a hierarchy of transforms, as shown in Screenshot.

Screenshot The 3D object is represented as a hierarchy of polygon groups (rectangles) and polygons (circles).

Java graphics 09fig07.gif


When you draw a polygon, first you apply its parent's transform, then that parent's transform, and so on all the way until you reach the root transform. Some code to implement these polygon groups is here in the PolygonGroup class in Listing 9.4.

Listing 9.4 PolygonGroup.java
package com.brackeen.javagamebook.math3D;
import java.util.List;
import java.util.ArrayList;
/**
 The PolygonGroup is a group of polygons with a
 MovingTransform3D. PolygonGroups can also contain other
 PolygonGroups.
*/
public class PolygonGroup {
 private String name;
 private List objects;
 private MovingTransform3D transform;
 /**
 Creates a new, empty PolygonGroup.
 */
 public PolygonGroup() {
 this("unnamed");
 }
 /**
 Creates a new, empty PolygonGroup with the specified name.
 */
 public PolygonGroup(String name) {
 setName(name);
 objects = new ArrayList();
 transform = new MovingTransform3D();
 }
 /**
 Gets the MovingTransform3D for this PolygonGroup.
 */
 public MovingTransform3D getTransform() {
 return transform;
 }
 /**
 Gets the name of this PolygonGroup.
 */
 public String getName() {
 return name;
 }
 /**
 Sets the name of this PolygonGroup.
 */
 public void setName(String name) {
 this.name = name;
 }
 /**
 Adds a polygon to this group.
 */
 public void addPolygon(Polygon3D o) {
 objects.add(o);
 }
 /**
 Adds a PolygonGroup to this group.
 */
 public void addPolygonGroup(PolygonGroup p) {
 objects.add(p);
 }
 /**
 Clones this polygon group. Polygon3Ds are shared between
 this group and the cloned group; Transform3Ds are copied.
 */
 public Object clone() {
 PolygonGroup group = new PolygonGroup(name);
 group.setFilename(filename);
 for (int i=0; i<objects.size(); i++) {
 Object obj = objects.get(i);
 if (obj instanceof Polygon3D) {
 group.addPolygon((Polygon3D)obj);
 }
 else {
 PolygonGroup grp = (PolygonGroup)obj;
 group.addPolygonGroup((PolygonGroup)grp.clone());
 }
 }
 group.transform = (MovingTransform3D)transform.clone();
 return group;
 }
 /**
 Gets the PolygonGroup in this group with the specified
 name, or null if none is found.
 */
 public PolygonGroup getGroup(String name) {
 // check for this group
 if (this.name != null && this.name.equals(name)) {
 return this;
 }
 for (int i=0; i<objects.size(); i++) {
 Object obj = objects.get(i);
 if (obj instanceof PolygonGroup) {
 PolygonGroup subgroup =
 ((PolygonGroup)obj).getGroup(name);
 if (subgroup != null) {
 return subgroup;
 }
 }
 }
 // group not found
 return null;
 }
 /**
 Updates the MovingTransform3Ds of this group and any
 subgroups.
 */
 public void update(long elapsedTime) {
 transform.update(elapsedTime);
 for (int i=0; i<objects.size(); i++) {
 Object obj = objects.get(i);
 if (obj instanceof PolygonGroup) {
 PolygonGroup group = (PolygonGroup)obj;
 group.update(elapsedTime);
 }
 }
 }
}


The PolygonGroup class keeps track of a list of objects that are either Polygon3Ds or PolygonGroups, so any polygon group can have child polygon groups. PolygonGroups can optionally have a name. The getGroup() method allows you to find a PolygonGroup by its name. For example, you would use this if you wanted to find the child PolygonGroup named turret to get its transform. This method looks at the polygon group itself and all child polygon groups, and returns null if no polygon group with the specified name is found. The update() method simply updates the PolygonGroups transform and the transform of any child PolygonGroups. The clone() method clones a PolygonGroup. You use this, for example, to create several different robots for a game. This method creates new MovingTransform3Ds for the new PolygonGroup, but the same polygons are used (because they are static anyway).

Iterating All the Polygons in a Group

When you're drawing a PolygonGroup, you really don't care which polygon belongs to which (child) group; you just want to draw each polygon the group contains, including polygons from child groups. Also, you want to draw the polygons only after they have been transformed. Here you'll create a few methods in the PolygonGroup class to iterate over every polygon. Each polygon is responsible for iterating over all its polygons. If a group has any child groups, each child group is responsible for iterating over its own polygons.

private int iteratorIndex;
...
/**
 Resets the polygon iterator for this group.
*/
public void resetIterator() {
 iteratorIndex = 0;
 for (int i=0; i<objects.size(); i++) {
 Object obj = objects.get(i);
 if (obj instanceof PolygonGroup) {
 ((PolygonGroup)obj).resetIterator();
 }
 }
}
/**
 Checks if there is another polygon in the current
 iteration.
*/
public boolean hasNext() {
 return (iteratorIndex < objects.size());
}
/**
 Gets the next polygon in the current iteration, applying
 the MovingTransform3Ds to it, and storing it in 'cache'.
*/
public void nextPolygonTransformed(Polygon3D cache) {
 Object obj = objects.get(iteratorIndex);
 if (obj instanceof PolygonGroup) {
 PolygonGroup group = (PolygonGroup)obj;
 group.nextPolygonTransformed(cache);
 if (!group.hasNext()) {
 iteratorIndex++;
 }
 }
 else {
 iteratorIndex++;
 cache.setTo((Polygon3D)obj);
 }
 cache.add(transform);
}


In these methods, each PolygonGroup has an iteratorIndex to indicate which polygon or child group is next to return from the list. The nextPolygonTransformed() method returns the polygon at the current iteratorIndex. If the object in the list at iteratorIndex is a PolygonGroup, that group's nextPolygonTransformed() is called. Also, the nextPolygonTransformed() applies the group's transform. So, a child group applies its transform, followed by its parent group. This way, the entire hierarchy of transforms is applied to each polygon. To draw all the polygons in a group, add another draw() method to the ZBufferedRenderer class:

public boolean draw(Graphics2D g, PolygonGroup group) {
 boolean visible = false;
 group.resetIterator();
 while (group.hasNext()) {
 group.nextPolygonTransformed(temp);
 visible |= draw(g, temp);
 }
 return visible;
}


Okay, you've accomplished a lot so far: You've created a z-buffer, moving transforms, polygon groups, and a polygon renderer to render polygon groups using the z-buffer. Next, you'll come up with a way to read polygon models from an external file.



   
Comments