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]]

GIF image

//  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
  }
}

Reply via email to