Duke Gard wrote:
>
> Does anyone have a recommended way of depicting crude looking terrain with
> Java3D.
Attached are a couple of classes from my forthcoming book.
The first is Mesh.java which constructs a tri-strip mesh
using a passed array of post heights. The post heights
can represent anything from a 2-D mathematical function
to terrain elevations. Post heights should be scaled into
[0-1] (or even [0-0.3]) range before the Mesh is created.
The second is a simple default appearance I use--Chrome.java.
You'll need to remove the package statements to use them.
I've also attached a screen dump showing the Mesh being used to
display some digital terrain. It's a 1200x1200 DEM file
of the area in northern New Mexico where I live.
Here's a snippet of code that shows how to load the post
arrays and create the mesh. If you use really big meshes
you'll need to run java with the -Xms128m and -Xmx256m
switches.
=================================
protected void addScene() {
final int ROWS = 1200;
final int COLS = 1201;
float posts[][] = new float[ROWS][COLS]; // Heights
// Open DEM file and read terrain elevations. Keep track of the
// Maximum elevation to properly scale the unit-size mesh.
try {
FileInputStream fis = new FileInputStream(demFile);
DataInputStream din = new DataInputStream(fis);
for (int r=0; r<ROWS; r++) {
for (int c=0; c<COLS; c++) {
int elev = din.readShort();
posts[r][c] = (float)elev;
}
}
}
catch (IOException e) {
System.err.println(e);
}
// Find maximum elevation
float maxElev = 0.0f;
float minElev = 0.0f;
for (int r=0; r<ROWS; r++) {
for (int c=0; c<COLS; c++) {
if (posts[r][c] > maxElev) {
maxElev = posts[r][c];
System.out.println("max to: " + maxElev);
}
if (posts[r][c] < minElev) {
minElev = posts[r][c];
System.out.println("min to: " + minElev);
}
}
}
System.out.println("Max elevation: " + maxElev);
System.out.println("Min elevation: " + minElev);
// Scale the elevations to maximum of 1.0
for (int r=0; r<ROWS; r++) {
for (int c=0; c<COLS; c++) {
if (posts[r][c] < 0)
posts[r][c] = 0;
posts[r][c] = posts[r][c] / (maxElev * 12);
}
}
Mesh filledMesh = new Mesh(posts);
// Use the following to try different appearances.
if (false) {
Appearance app = filledMesh.getAppearance();
app.setMaterial(new Material(
new Color3f(0.65f, 0.45f, 0.25f), // ambient
new Color3f(0.0f, 0.0f, 0.0f), // emmisive
new Color3f(0.65f, 0.45f, 0.25f), // diffuse
new Color3f(0.4f, 0.4f, 0.4f), // specular
128.0f * 0.7f)); // shininess
}
top.addChild(filledMesh);
}
=======================
Bug reports and feedback are welcome. I haven't tried to compile
it outside of my framework, so let me know if you're unable to
build it.
--
Allen L. McPherson Member Holstein Aerobatic Club
Los Alamos National Laboratory Scientific Viz / Advanced Computing Lab
Box 1663 Group:CIC-ACL MS:B287 [EMAIL PROTECTED]
Los Alamos, NM 87545 Voice: (505) 665-6548 Fax: (505) 665-4939
[Personal mail to: [EMAIL PROTECTED]]

// Copyright (c) 1999 by Allen McPherson. All rights reserved.
// This example is provided WITHOUT ANY WARRANTY either expressed or implied.
package alm.j3d.geometry;
import javax.media.j3d.*;
import javax.vecmath.*;
// Builds a triangle mesh from a rectangular array of post heights.
// Heights should be scaled into the range of -1 to 1 or entire Shape3D can
// be scaled in Y by a parent TransformGroup.
public class Mesh extends Shape3D {
Vector3f norms[][];
Point3f coords[][];
public Mesh(float posts[][]) {
// Make sure passed array is valid.
if (checkRegular(posts) == false) {
System.out.println("Non-conformant array passed to Mesh()");
return;
}
// Compute the starting coordinate values and spacing between
// rows and columns.
int rows = posts.length;
int cols = posts[0].length; // Any index OK if array conformant
float rowStart; // Z-coordinate starting value
float colStart; // X-coordinate starting value
float rcDelta; // Delta same for rows and cols
// Make sure that the bigger of rows or cols starts at -1.0
if (rows >= cols) {
rowStart = -1.0f;
colStart = (float) -(cols-1)/(rows-1);
rcDelta = 2.0f / (rows-1);
} else {
rowStart = (float) -(rows-1)/(cols-1);
colStart = -1.0f;
rcDelta = 2.0f / (cols-1);
}
// Setup coordinate data for this mesh and allocte normals.
norms = new Vector3f[rows][cols];
coords = new Point3f[rows][cols];
float rCoord = rowStart;
for (int r=0; r<rows; r++) {
float cCoord = colStart;
for (int c=0; c<cols; c++) {
norms[r][c] = new Vector3f();
coords[r][c] = new Point3f(cCoord, posts[r][c], rCoord);
cCoord += rcDelta;
}
rCoord += rcDelta;
}
// Create the index count and indices for all triangle strips.
int numStrips = rows-1;
int ixCounts[] = new int[numStrips];
int indices[][] = new int[numStrips][2*cols];
for (int strip=0; strip<numStrips; strip++) {
ixCounts[strip] = 2*cols;
for (int ix=0; ix<2*cols; ix+=2) {
indices[strip][ix] = strip * cols + (ix/2);
indices[strip][ix+1] = (strip+1) * cols + (ix/2);
}
}
// Compute and average all needed normals for each vertex.
// See text for details of loop.
for (int r=0; r<rows; r++) {
for (int c=0; c<cols; c++) {
if (c < cols-1) {
if (r != 0)
norms[r][c].add(case0(r, c));
if (r != rows-1)
norms[r][c].add(case1(r, c));
}
if (c > 0) {
if (r != 0)
norms[r][c].add(case3(r, c));
if (r != rows-1)
norms[r][c].add(case2(r, c));
}
norms[r][c].normalize();
}
}
// Create the geometry and set its per-vertex components
// and indices.
IndexedTriangleStripArray mesh =
new IndexedTriangleStripArray(rows*cols, GeometryArray.COORDINATES |
GeometryArray.NORMALS,
2*numStrips*cols, ixCounts);
for (int r=0; r<rows; r++)
mesh.setCoordinates(r*cols, coords[r]);
for (int strip=0; strip<numStrips; strip++)
mesh.setCoordinateIndices(strip*2*cols, indices[strip]);
for (int r=0; r<rows; r++)
mesh.setNormals(r*cols, norms[r]);
for (int strip=0; strip<numStrips; strip++)
mesh.setNormalIndices(strip*2*cols, indices[strip]);
// Enable garbage collection on the coordinates and normal data.
coords = null;
norms = null;
Appearance app = new Appearance();
app.setMaterial(new Chrome());
this.setGeometry(mesh);
this.setAppearance(app);
}
private Vector3f v0 = new Vector3f();
private Vector3f v1 = new Vector3f();
private Vector3f vc = new Vector3f();
private Vector3f vr = new Vector3f();
// Case 0: compute normal for one triangle.
private Vector3f case0(int r, int c) {
v0.sub(coords[r][c], coords[r][c+1]);
v1.sub(coords[r][c], coords[r-1][c]);
vc.cross(v0, v1);
return(vc);
}
// Case 1: compute normals for two triangles.
private Vector3f case1(int r, int c) {
vr.set(0.0f, 0.0f, 0.0f);
v0.sub(coords[r][c], coords[r][c+1]);
v1.sub(coords[r][c], coords[r+1][c+1]);
vc.cross(v1, v0);
vr.add(vc);
v0.sub(coords[r][c], coords[r+1][c+1]);
v1.sub(coords[r][c], coords[r+1][c]);
vc.cross(v1, v0);
vr.add(vc);
return(vr);
}
// Case 2: compute normal for one triangle.
private Vector3f case2(int r, int c) {
v0.sub(coords[r][c], coords[r][c-1]);
v1.sub(coords[r][c], coords[r+1][c]);
vc.cross(v0, v1);
return(vc);
}
// Case 3: compute normals for two triangles.
private Vector3f case3(int r, int c) {
vr.set(0.0f, 0.0f, 0.0f);
v0.sub(coords[r][c], coords[r][c-1]);
v1.sub(coords[r][c], coords[r-1][c-1]);
vc.cross(v1, v0);
vr.add(vc);
v0.sub(coords[r][c], coords[r-1][c-1]);
v1.sub(coords[r][c], coords[r-1][c]);
vc.cross(v1, v0);
vr.add(vc);
return(vr);
}
// Check for a valid array of post heights. Dimensions must be
// greater than 2, and it must be rectangular.
private boolean checkRegular(float posts[][]) {
int rows = posts.length;
int cols = posts[0].length;
if (rows < 2)
return false;
if (cols < 2)
return false;
for (int i=1; i<rows; i++) {
if (posts[i].length != cols)
return false;
}
return true;
}
}
// Copyright (c) 1999 by Allen McPherson. All rights reserved.
// This example is provided WITHOUT ANY WARRANTY either expressed or implied.
package alm.j3d.geometry;
import javax.media.j3d.*;
import javax.vecmath.*;
public class Chrome extends Material {
public Chrome() {
super(new Color3f(0.25f, 0.25f, 0.25f), // ambient
new Color3f(0.0f, 0.0f, 0.0f), // emmisive
new Color3f(0.4f, 0.4f, 0.4f), // diffuse
new Color3f(0.77f, 0.77f, 0.77f), // specular
128.0f * 0.6f); // shininess
}
}