Accelerating Terrain Following


Moving over the Surface

The KeyBehavior class is similar to the one in FractalLand3D in that it permits the user to move over the surface of the terrain and to float above it. However, there's one small change and one large one. The small change is the addition of the w key, which prints the user's current location on the landscape to System.out.

The major change is that a move doesn't immediately affect the user's vertical position on the terrain. The height calculation is delegated to a HeightFinder object, which may take one or two seconds to obtain the result through picking. In the meantime, KeyBehavior continues to use the old value. As a consequence, the user can move inside mountains, but the vertical position will be corrected. The reason for the slow picking is the large terrain size (e.g., over 66,000 vertices in test2.obj, specifying 131,000 faces), all packaged inside a single GeometryArray.

This approach has the advantage that key processing is decoupled from the height calculation. This means that the KeyBehavior object doesn't have to wait those few seconds after each move-related key press, which would quickly drive the user to distraction. At the end of the chapter, I discuss various alternatives to this coding technique.

Where Am I?

When the user presses the w key, printLandLocation( ) is called:

     private void printLandLocation( )     { targetTG.getTransform(t3d);       t3d.get(trans);       trans.y -= MOVE_STEP*zOffset;  // ignore user floating       Vector3d whereVec = land.worldToLand(trans);       System.out.println("Land location: (" + df.format(whereVec.x) + ", " +                       df.format(whereVec.y) + ", " + df.format(whereVec.z) + ")");     }

A slight problem is that the KeyBehavior object is attached to sceneBG, so it utilizes world coordinates. However, printing the landscape coordinates is more helpful, and so the (x, y, z) data maintained in KeyBehavior must be transformed. This is achieved by calling worldToLand( ) in Landscape:

     public Vector3d worldToLand(Vector3d worldVec)     {       double xCoord = (worldVec.x + LAND_LEN/2) / scaleLen;       double yCoord = (-worldVec.z + LAND_LEN/2) / scaleLen;                                                  // z-axis --> y-axis       double zCoord = worldVec.y / scaleLen;   // y-axis --> z-axis       return new Vector3d(xCoord, yCoord, zCoord);     }

The transformations apply the operations implicit in the subgraph in Figure 27-12: the world coordinates are translated, scaled and rotated to make them into terrain coordinates. The rotation (a -90-degree rotation around the x-axis) can be conveniently expressed as a switching of the y- and z-coordinates.

Another way of understanding worldToLand( ) is as the reverse of landToWorld( ), which is in Landscape.


The w key is quite useful when the programmer is deciding where to place scenery on the terrain. The user can move over the landscape inside Terra3D, and press w when a promising location is encountered. The outputted coordinates can be used in the scenery file to position 3D models.

Strolling Around the Terrain

The principal method for moving is moveBy( ), which is called with a predefined step for moving forward, back, left, or right. The viewpoint is adjusted in four stages:

  1. The next (x, z) position on the floor is calculated by carrying out the move but doesn't update the user's actual position. This is done by tryMove( ).

  2. The resulting (x, z) data is passed to a HeightFinder thread. HeightFinder uses picking to get the floor height.

  3. In the meantime, moveBy( ) uses the current floor height as the height of the new location.

  4. Laterperhaps a few seconds laterHeightFinder calls adjustHeight( ) in KeyBehavior. adjustHeight( ) updates the user's height by the difference between the current floor height and the height at the new location.

Here's that sequence of events in code:

     private void moveBy(Vector3d theMove)     {       Vector3d nextLoc = tryMove(theMove);   // next (x,?,z) position       if (!land.inLandscape(nextLoc.x, nextLoc.z))   // not on landscape          return;       hf.requestMoveHeight(nextLoc);  // height request to HeightFinder       Vector3d actualMove = new Vector3d(theMove.x, 0, theMove.z);                                            // no y-axis change... yet       doMove(actualMove);     }     public void adjustHeight(double newHeight)     {       double heightChg = newHeight - currLandHeight - (MOVE_STEP*zOffset);       Vector3d upVec = new Vector3d(0, heightChg, 0);       currLandHeight = newHeight;     // update current height       zOffset = 0;                    // back on floor, so no offset       doMove(upVec);     }

moveBy( ) and adjustHeight( ) call doMove( ), which updates the viewpoint position. This method is unchanged from the one in FractalLand3D, except that it is now prefixed with the synchronized keyword. This prevents KeyBehavior and HeightFinder from calling it at the same time.



Killer Game Programming in Java
Killer Game Programming in Java
ISBN: 0596007302
EAN: 2147483647
Year: 2006
Pages: 340

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net