| |
"When Mars Is Too Big to Download"
Vol. 9, Issue 9, p. 57
Listing 1
// The height field contains a float value for
// each [row, col] vertex.
float[ ][ ] hf = getHeightField();
BranchGroup objRoot = new BranchGroup();
GeometryInfo gi = new GeometryInfo(GeometryInfo.QUAD_ARRAY);
// The number of vertices (or coordinates) is based on the
// number of squares in the grid (4 by 4) times the number
// of corners (4) for each.
Point3f[ ] coordinates = new Point3f[64];
// For each corner of each square in the grid, convert the
// height field altitude value into instances of Point3f.
// The row is mapped to the minus z-axis, the column to
// the x-axis and the height field altitude value
// to the y-axis. Each iteration adds one square.
int ci = 0; // coordinate index
for (int row = 0; row < 4; row++) {
for (int col = 0; col < 4; col++) {
// use compass bearings to id the corners
float sw = hf[row][col];
float se = hf[row][col + 1];
float ne = hf[row + 1][col + 1];
float nw = hf[row + 1][col];
coordinates[ci] = new Point3f(col, sw, -row);
coordinates[ci + 1] = new Point3f(col + 1, se, -row);
coordinates[ci + 2] =
new Point3f(col + 1, ne, - (row + 1));
coordinates[ci + 3] =
new Point3f(col, nw, - (row + 1));
ci = ci + 4;
}
}
gi.setCoordinates(coordinates);
Listing 2
float[ ][ ] hf = getHeightField();
// The height field
BranchGroup objRoot = new BranchGroup();
GeometryInfo gi =
new GeometryInfo(GeometryInfo.TRIANGLE_ARRAY);
// The number of vertices (or coordinates) is based on the
// number of triangles in the grid (4 by 4 by 2) times the
// number of corners (3) for each.
Point3f[ ] coordinates = new Point3f[96];
// For each corner of each square in the grid, convert the
// height field altitude value into instances of Point3f.
// The row is mapped to the minus z-axis, the column to
// the x-axis and the height field altitude value
// to the y-axis. Each iteration adds two triangles.
int ci = 0; // coordinate index
for (int row = 0; row < 4; row++) {
for (int col = 0; col < 4; col++) {
// use compass bearings to id the corners
float sw = hf[row][col];
float se = hf[row][col + 1];
float ne = hf[row + 1][col + 1];
float nw = hf[row + 1][col];
coordinates[ci] = new Point3f(col, sw, -row);
coordinates[ci + 1] = new Point3f(col + 1, se, -row);
coordinates[ci + 2] =
new Point3f(col, nw, - (row + 1));
coordinates[ci + 3] = new Point3f(col + 1, se, -row);
coordinates[ci + 4] =
new Point3f(col + 1, ne, - (row + 1));
coordinates[ci + 5] =
new Point3f(col, nw, - (row + 1));
ci = ci + 6;
}
}
gi.setCoordinates(coordinates);
Listing 3
float[ ][ ] hf = getHeightField();
// The height field
BranchGroup objRoot = new BranchGroup();
GeometryInfo gi =
new GeometryInfo(GeometryInfo.TRIANGLE_STRIP_ARRAY);
// The number of vertices (or coordinates) is based on the
// number of horizontal strips (height - 1) times the
// number of vertices per strip (width * 2).
Point3f[] coordinates = new Point3f[40];
// The secret is that the strip vertices must be ordered
// like this: NW, SW, NE, SE for each set of four corners
// of a quad. A convenient way to accomplish this is to
// organize the landscape in horizontal strips and iterate
// across the columns calculating two vertices at a time.
int ci = 0; // coordinate index
for (int row = 0; row < 4; row++) {
for (int col = 0; col < 5; col++) {
// use compass bearings to id the corners
float sw = hf[row][col];
float nw = hf[row + 1][col];
coordinates[ci] = new Point3f(col, nw, - (row + 1));
coordinates[ci + 1] = new Point3f(col, sw, -row);
ci = ci + 2;
}
}
// The strip count handling could be done above
// but is here to make it easier to understand
int[] stripCounts = new int[4];
for (int strip = 0; strip < 4; strip++) {
stripCounts[strip] = 10;
}
gi.setStripCounts(stripCounts);
gi.setCoordinates(coordinates);
Listing 4
float[ ][ ] hf = getHeightField();
// The height field
BranchGroup objRoot = new BranchGroup();
GeometryInfo gi =
new GeometryInfo(GeometryInfo.TRIANGLE_STRIP_ARRAY);
// The number of vertices (or coordinates) is based on the
// number of rows (5) times the number of columns (5)
// times 3 (one each for x, y, and z values).
float[] coordinates = new float[75]; // No more Point3f!
// Convert the height field to x, y, z values
for (int row = 0; row < 5; row++) {
for (int col = 0; col < 5; col++) {
// coordinate index is the column plus
// the row times the width of a row times
// the 3 (one for each x, y, and z).
int ci = (col + row * 5) * 3;
coordinates[ci + 0] = col; // x
coordinates[ci + 1] = hf[row][col]; // y
coordinates[ci + 2] = -row; // z
}
}
// The number of indices is based on the
// number of horizontal strips (height - 1) times the
// number of vertices per strip (width * 2).
int[ ] indices = new int[40];
// The secret is that the strip vertices must be ordered
// like this: NW, SW, NE, SE for each set of four corners
// of a quad. A convenient way to accomplish this is to
// organize the landscape in horizontal strips and iterate
// across the columns calculating two vertices at a time.
int pi = 0; // points index
for (int row = 0; row < 4; row++) {
int width = row * 5;
for (int col = 0; col < 5; col++) {
int coordinateIndex = width + col;
indices[pi + 0] = coordinateIndex + 5; //NW
indices[pi + 1] = coordinateIndex; //SW
pi = pi + 2;
}
}
int[] stripCounts = new int[4];
for (int strip = 0; strip < 4; strip++) {
stripCounts[strip] = 10;
}
gi.setStripCounts(stripCounts);
gi.setCoordinates(coordinates);
gi.setCoordinateIndices(indices);
Listing 5
float[][] hf = new float[divisions + 1][divisions + 1];
// Initialize the corners of the height field. You
// could use random() for these values.
hf[0][0] = 0f;
hf[0][divisions] = 0f;
hf[divisions][divisions] = 0f;
hf[divisions][0] = 0f;
float rough = roughness;
// Evaluate the fractal for each of the
// requested levels of detail.
for (int detail = lod; detail > 0; detail--) {
// The length of the side for
// this level of detail iteration.
int side = 1 << detail;
int half = side >> 1;
// Evaluate each square to create the diamond
// pattern by finding a corner of each square.
for (int x = 0; x < divisions; x += side) {
for (int y = 0; y < divisions; y += side) {
// x,y is the lower left corner of
// the square to evaluate...
diamond(hf, x, y, side, rough);
}
}
if (half > 0) {
// Evaluate each diamond to create the square
// pattern by finding the center of each
// diamond.
for (int x = 0; x <= divisions; x += half) {
for (int y = (x + half) % side;
y <= divisions;
y += side) {
// x, y is the center of the
// diamond to evaluate ...
square(hf, x, y, side, rough);
}
}
}
// Adjust the roughness factor
// to gradually scale it down.
rough *= roughness;
// Fractal purists would divide the roughness
// by 2 for each iteration. This creates a
// scene that is (subjectively) too rough.
// rough = rough/2.0f;
}
Listing 6
private void square(
float[][] terrain,
int x,
int y,
int side,
float roughness) {
// Because x, y is the center of the diamond,
// it is possible that corners of the diamond
// are outside the bounds of the landscape, so
// this method has a few boundary conditions
// to check for this possibility.
int half = side / 2;
float sum = 0.0f, number = 0.0f;
if (x - half >= 0) {
// West corner
sum += terrain[x - half][y];
number += 1.0;
}
if (y - half >= 0) {
// South corner
sum += terrain[x][y - half];
number += 1.0;
}
if (x + half <= divisions) {
// East corner
sum += terrain[x + half][y];
number += 1.0;
}
if (y + half <= divisions) {
// North corner
sum += terrain[x][y + half];
number += 1.0;
}
float elevation = sum / number;
terrain[x][y] = elevation + random() * roughness;
}
Listing 7
GeometryInfo gi =
new GeometryInfo(GeometryInfo.TRIANGLE_ARRAY);
// The number of vertices (or coordinates) is just for
// one triangle.
Point3f[] coordinates = new Point3f[3];
coordinates[0] = new Point3f(0,0,0);
coordinates[1] = new Point3f(5,0,0);
coordinates[2] = new Point3f(5,0,-5);
Color3f[] colors = new Color3f[3];
colors[0] = new Color3f(1,0,0);
colors[1] = new Color3f(0,1,0);
colors[2] = new Color3f(0,0,1);
gi.setCoordinates(coordinates);
gi.setColors(colors);
Listing 8
private int[] getElevationColorIndices(float[][] hf) {
int[] indices = new int[divisions * (divisions + 1) * 2];
int i = 0;
for (int row = 0; row < divisions; row++) {
for (int col = 0; col < (divisions + 1); col++) {
// Normalize the height value to a
// color index between 0 and NUMBER_OF_COLORS - 1
int nw =
Math.round(
(NUMBER_OF_COLORS - 1)
* ((100f - hf[row + 1][col]) / 100f));
indices[i] = nw;
int sw =
Math.round(
(NUMBER_OF_COLORS - 1)
* ((100f - hf[row][col]) / 100f));
indices[i + 1] = sw;
i = i + 2;
}
}
return indices;
}
|
|