Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

Navigate through virtual worlds using Java 3D

Use level-of-detail and fly-through behaviors

  • Print
  • Feedback

Page 5 of 6

Figure 3. ElevationModel LOD grid

ElevationModel

The ElevationModel object is the top-level object in the terrain-modeling hierarchy. It divides the terrain data into a series of segments. Each segment is implemented as a LODSegment. Each LODSegment object creates three different ElevationSegment objects for its segment of the terrain. One segment is at full resolution, another plots every fifth elevation, and the third segment plots every tenth elevation. The system is created such that the segment where the viewer is located and the next adjacent segment are seen in full resolution, middle range segments are drawn at every fifth level, and distant segments are drawn at every tenth level. Figure 4 depicts what a cliff face looks like when viewed in the various levels of detail (from the same observation point).

Figure 4. Level-of-detail differences

The code segment below details how ElevationModel constructs the LODSegment objects. ElevationModel first determines how many segments are needed by dividing the SECONDS_PER_SEGMENT constant into the geographic length and width. A LODSegment array is then allocated to hold references to the segments. A set of nested for loops does all the work. During each iteration, a set of groundCoordinates, maximum and minimum x, z display coordinates, and the starting and stopping indexes into the elevation array are calculated. These pass to the LODSegment constructor. An important note: the end of one segment matches the start of the next segment. This prevents visible seams in the display. For example, one segment's maxX and stopColumn equals the minX and startColumn of the segment to its right.

Once the LODSegment is created, as shown in the code below, it is added as a child to the ElevationModel object (recall, ElevationModel is a BranchGroup). Once all segments have been created, the normals along their edges are adjusted to remove seams, and the scene is compiled to enhance performance. Normals are vectors attached to each vertex indicating the surface's orientation for lighting purposes. Refer to the section describing the ElevationSegment for more information on normals. To make the display more interesting, the ElevationModel class uses an exaggeration factor to make the elevation differences more apparent:

//
 //   Create LODSegments
 //
   sColumns = (int)Math.ceil(groundCoordinates.lengthSeconds()/SECONDS_PER_SEGMENT);
   sRows = (int)Math.ceil(groundCoordinates.widthSeconds()/SECONDS_PER_SEGMENT);
   segments = new LODSegment[sRows][sColumns];
   GroundCoordinates gc = new GroundCoordinates();
   int rowRatio = (int) (1.0d*file.nRows/sRows); int colRatio = (int) (1.0d*file.nColumns/sColumns);
   deltaRow = (north_Z-south_Z)/sRows;
   deltaCol = (east_X-west_X)/sColumns;
   for(int row = 0; row < sRows; row++)
   {
     float minX, maxX, minZ, maxZ;
     int startRow, stopRow, startCol, stopCol;
     gc.sw[GroundCoordinates.LATITUDE] = groundCoordinates.sw[GroundCoordinates.LATITUDE] + 
        row*SECONDS_PER_SEGMENT;
     gc.se[GroundCoordinates.LATITUDE] = groundCoordinates.sw[GroundCoordinates.LATITUDE]+ 
        row*SECONDS_PER_SEGMENT;
     gc.nw[GroundCoordinates.LATITUDE] = groundCoordinates.sw[GroundCoordinates.LATITUDE] + 
        row+1)*SECONDS_PER_SEGMENT;
     gc.ne[GroundCoordinates.LATITUDE] = groundCoordinates.sw[GroundCoordinates.LATITUDE]+ 
        (row+1)*SECONDS_PER_SEGMENT;
     minZ = south_Z + row*deltaRow;
     maxZ = south_Z +(row+1.0f)*deltaRow;
     startRow = row*(rowRatio);
     stopRow = (row+1)*(rowRatio);
     for(int col = 0 ; col < sColumns; col++)
     {
      if(stat != null)
         stat.setLabel2("Creating geometry segment ",row*sColumns+col+1,sRows*sColumns);
     minX = west_X + col*deltaCol;
     maxX = west_X +(col+1.0f)*deltaCol;
     startCol = col*(colRatio);
     stopCol = (col+1)*(colRatio);
     gc.sw[GroundCoordinates.LONGITUDE] = groundCoordinates.sw[GroundCoordinates.LONGITUDE] 
        - col*SECONDS_PER_SEGMENT;
     gc.nw[GroundCoordinates.LONGITUDE] = roundCoordinates.sw[GroundCoordinates.LONGITUDE] 
        -  col*SECONDS_PER_SEGMENT;
     gc.se[GroundCoordinates.LONGITUDE] = groundCoordinates.sw[GroundCoordinates.LONGITUDE]
        -  (col+1)*SECONDS_PER_SEGMENT;
     gc.ne[GroundCoordinates.LONGITUDE] = groundCoordinates.sw[GroundCoordinates.LONGITUDE]
        - (col+1)*SECONDS_PER_SEGMENT;
     segments[row][col] = new LODSegment(file.elevations,  startRow,startCol, stopRow,stopCol,
        minElevation, maxElevation, gc, exageration, minX, maxX, minZ, maxZ);
      addChild(segments[row][col]);
    }
 }
...
if(stat != null)
     stat.setLabel2("Compiling/Optimizing the geometry");
  compile(); // Compile the model


LODSegment

The LODSegment object creates the level-of-detail components for one segment of the terrain model. To implement a level of detail, we must create Switch and DistanceLOD objects. Switch provides the capability to selectively display one of its children (ElevationSegment). The DistanceLOD object is a behavior object that tells Switch which of its children to display. LODSegment is based on a BranchGroup so that it can be used as a single point of reference for the DistanceLOD, Switch, and all ElevationSegments.

The LODSegment constructor is shown in the code segment below. LODSegment first creates a Switch object, then creates three ElevationSegment objects at different resolutions, and adds them to Switch. LODSegment then creates a DistanceLOD object and initializes it with its position, distance array, and bounds. The position passed to the DistanceLOD object is calculated to be a location at the center of the region and at the model's highest elevation. The bounds passed to the DistanceLOD object are set to infinite so the segment can be seen from any location. The distance array, sized to have one fewer items than the resolution array, determines which segment will display. If the distance from the segment to the viewer is less than the first entry, then the first segment is used; if the distance from the segment to the view is less than the second entry, then the second segment is used; and so forth.

In my code, I calculated the distance array such that distances are based on twice a segment's length. This ensures that the segment where the viewer is currently located and the one immediately adjacent to it display in full detail. LODSegment then passes to the DistanceLOD object a reference to the Switch object that it will control. LODSegment must add both the DistanceLOD and the Switch objects. Examine LODSegment below:

public LODSegment( int elevations[][], int startRow, int startColumn, int stopRow, int stopColumn,
        int minEl, int maxEl, GroundCoordinates gc ,float exageration, 
       float minX, float maxX, float minZ, float maxZ)
  {
   super();
   groundCoordinates = gc;
  //
  // Initialize the switch node and create the child segments in varying resolutions
  //
 switchNode.setCapability(Switch.ALLOW_SWITCH_WRITE);
 segments = new ElevationSegment[resolutions.length];
  for(int i = 0; i < resolutions.length; i++)
  {
   segments[i] = new ElevationSegment(elevations, startRow,startColumn, stopRow,stopColumn,
      minEl,maxEl,groundCoordinates,
      exageration,minX,maxX,minZ,maxZ,resolutions[i]);
  switchNode.addChild(segments[i]);
 }
 //
 // Set the position and bounds of the object
 //
 Point3f position = new Point3f((float)((maxX+minX)/2),  maxEl*exageration,(float)((maxZ+minZ)/2));
 Bounds bounds = new BoundingSphere(new Point3d(0,0,0),Double.MAX_VALUE);
 //
 //  Calculate distances based on size of segment (east-west length)
 //
 distances = new float[resolutions.length-1];
 for(int i=0; i < distances.length; i++)
      distances[i] = Math.abs((float)((i+1)*2*(maxX-minX)));
//
//  Create the distanceLOD object
//
  dLOD = new DistanceLOD(distances,position);
  dLOD.setSchedulingBounds(bounds);
  dLOD.addSwitch(switchNode);
//
// Add the switch and the distance LOD to this object
//
 addChild(dLOD);
 addChild(switchNode);
 }


ElevationSegment

ElevationSegment is based on the Shape3D Java 3D object. Shape3D is a leaf node object that contains the actual geometry displayed on the screen. This geometry is based on the TriangleStripArray using the interleaved and by-reference parameters. A TriangleStripArray is a geometric primitive consisting of an array of vertices that form a series of triangles. The set of vertices is divided into a number of strips; each strip holds many triangles, as shown in Figure 5.

  • Print
  • Feedback

Archived Discussions (Read only)
Subject
. Forum migration complete By AthenAdministrator
. Forum migration update By AthenAdministrator
. trwlShzpMohpr By wlitqxudb
Resources