Responding to Key Presses


Constructing the Ground

Quads with similar heights are grouped together in a TexturedPlanes object (which is a subclass of Shape3D). All the quads in the object are covered with the same texture, thereby adding detail to the landscape. However, a quad cannot be assigned a texture until it has texture coordinates.

I want quads to reflect light, so patterns of light and dark are shown on the landscape. This requires that each quad coordinate has a normal vector, so the direction and intensity of reflected light at each point can be calculated.

Generating these normals manually would be time-consuming, so I use Java 3D's utility class, NormalGenerator, to do the job.

TheTexturedPlanes geometry is a QuadArray, with fields for the (x, y, z) coordinates, texture coordinates, and the normals. The ArrayList of (x, y, z) coordinates (stored in coords) is converted to an array before its points can be used:

     int numPoints = coords.size(  );     QuadArray plane = new QuadArray(numPoints,         GeometryArray.COORDINATES |         GeometryArray.TEXTURE_COORDINATE_2 |         GeometryArray.NORMALS );         // obtain points     Point3d[] points = new Point3d[numPoints];     coords.toArray( points );     

The texture coordinates are created in the same order as the points in a quad and repeated for each quad:

     TexCoord2f[] tcoords = new TexCoord2f[numPoints];     for(int i=0; i < numPoints; i=i+4) {       tcoords[i] = new TexCoord2f(0.0f, 0.0f);   // for 1 point       tcoords[i+1] = new TexCoord2f(1.0f, 0.0f);       tcoords[i+2] = new TexCoord2f(1.0f, 1.0f);       tcoords[i+3] = new TexCoord2f(0.0f, 1.0f);     } 

The calculation of the normals is carried out by a NormalGenerator object, which requires the coordinates and texels to be stored in a Java 3D GeometryInfo object:

     // create GeometryInfo     GeometryInfo gi = new GeometryInfo(GeometryInfo.QUAD_ARRAY);     gi.setCoordinates(points);     gi.setTextureCoordinateParams(1, 2); // one set of 2D texels     gi.setTextureCoordinates(0, tcoords);         // calculate normals with very smooth edges     NormalGenerator ng = new NormalGenerator(  );     ng.setCreaseAngle( (float) Math.toRadians(150));  // default is 44     ng.generateNormals(gi); 

The setTextureCoordinatesParams( ) specifies how many texture coordinate sets will be used with the geometry and their dimensionality (Java 3D offers 2D, 3D, and 4D texture coordinates).

The NormalGenerator adds vertex normals to the geometry inside GeometryInfo. The crease angle is used to decide when two adjacent quads should be given separate normals. The crease angle is set to some prescribed value (44 degrees is the default), and then the angles between adjacent quads are compared. If the angle between the two quads is less than the crease angle (as in the lefthand pair in Figure 26-6), then the quads will get to share a normal. If the angle between them is greater than the crease angle (as in the righthand pair in Figure 26-6), then the quads will use separate normals. The sharing of normals between the quads creates a lighting effect that smoothes (and nearly hides) the join between them.

Figure 26-6. Crease angle normals


By increasing the crease angle from 44 to 150 degrees in FractalLand3D, the edges between most quads are made smoother when light is reflected off them; instead of a sharp separation in light and shade at the shared edges, the pattern of light is more diffuse, giving the impression that the underlying geometry is more rounded. This is a useful trick for landscaping since no changes need to be done to the geometry's coordinates; only changes to the normals will be required.

Stripification

Stitching the landscape together from quads has several benefits, including making it easier to generate the (x, y, z) coordinates and map textures to the surface. However, Java 3D's graphics engine (OpenGL or DirectX) can optimize the scene rendering if the shapes are composed from triangle strips. Java 3D offers a Stripifier utility class to convert geometries into triangle strips, a process known as stripification.

The geometry chosen for conversion should be supplied to the stripifier in a GeometryInfo object, and the translation will be carried out in place:

     Stripifier st = new Stripifier(  );     st.stripify(gi);    // gi is a GeometryInfo object 

Triangle strips are sequences of triangles where the second and third vertices of one triangle are used as the first and second vertices of the next triangle, as in Figure 26-7.

Figure 26-7. Triangle strips examples


The ordering of the points allows the underlying graphics hardware to render the shape more quickly. However, the acceleration depends on the number of triangles in a strip, with larger accelerations when the strip is longer. A TexturedPlanes shape will generally consist of several medium length sequences of quads since the quads are grouped based on similar heights. As a consequence, the rendering gains will generally be modest for this application.

The outcome of stripification is a new geometry stored in the GeometryInfo object. This is the geometry made for the TexturedPlanes object:

     // extract and use GeometryArray     setGeometry( gi.getGeometryArray(  ) ); 

Texture Minification

The Appearance node for the TexturedPlanes object is the usual mix of Texture2D and Material nodes with lighting enabled. However, the size of the landscape means textures appear on quads at a great distance from the viewpoint. In terms of rendering, this requires the texture image to be reduced in size, with a consequent reduction in its resolution. As the viewpoint moves, the quads and textures are redrawn, and this redrawing changes the mix of colors in the screen's pixels. This change is seen by the user as shimmering, which can become annoying in a highly textured environment (as seen here).

Java 3D has a set of texture filtering modes which specify how a texture is made larger or smaller for viewing. Minification is the process of reducing the texture's size, and magnification is its enlargement. A minification filter is set using the setMinFilter( ) method:

     texture.setMinFilter(Texture2D.BASE_LEVEL_LINEAR); 

The minification specifies that the color in a single pixel should be based on an average of the four nearest texels in the texture. This averaging has the effect of smoothing out the transition of colors from one pixel to another, which reduces the shimmering of the texture as the viewpoint moves.

Unfortunately, minification is only an answer until the viewpoint moves too far from the shape. At some point, the texture will be rendered at so small a size that four or more texels are mapped to a pixel. The flicking will then return since the averaging is no longer smoothing the transition between the pixels.

This problem doesn't occur with mipmapping, where several lower resolution versions of the texture are precomputed as the texture is loaded and used as needed at runtime. The relevant code is the following:

     // load and set the texture; generate mipmaps for it     TextureLoader loader = new TextureLoader(fnm,                                     TextureLoader.GENERATE_MIPMAP, null);         Texture2D texture = (Texture2D) loader.getTexture(  );     texture.setMinFilter(Texture2D.MULTI_LEVEL_LINEAR); 

The GENERATE_MIPMAP flag switches on mipmapping, and the MULTI_LEVEL_LINEAR mode specifies that the color for a pixel comes from eight texels: the four closest texels from each of the two closest texture resolutions. This averaging approach removes shimmering and replaces the need for runtime scaling of the texture.

The reduced resolution texture is called a mip map (hence the verb mipmapping). The term mip is an acronym for the Latin phrase multum in parvo, which can be translated as "much on a small object."


Multiple Textures

The edges between one texture and another in FractalLand3D are sharply highlighted by the quad geometry, which tends to destroy the illusion of landscaping. One way to improve the edges is to use multiple textures for each Shape3D. By using a basic ground texture and then adding variations with additional layers of texturing, the edges are smoothed out. Textures representing different kinds of vegetation or even forms of lighting and shading work great for this. The layering of several textures onto a surface is known as multitexturing.

The texture attributes for multiple layers are specified with TextureUnitState objects, one for each layer. The setTextureUnitState( ) method is applied to the shape's Appearance node to add the textures. Several texture coordinates can be linked to each vertex of the shape's geometry and mapped to a particular TextureUnitState.

The Java 3D demo program TextureTest/MultiTextureTest.java shows how multitexturing can be utilized.


A drawback with multitexturing is that older graphics cards may not support it. The QueryProperties.java utility, in the Java 3D demo directory PackageInfo/, prints out several properties related to textures; multiple textures require that textureUnitStateMax be 2 or greater.



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