//
// Highfield.java, Frank Buß 03/1999
//
// Eine Klasse zur Darstellung eines sogenannten Highfields.
//
// imports für verschiendene Knoten-Klassen
import javax.media.j3d.*;
// imports für verschiedene Vektor-Klassen
import javax.vecmath.*;
/**
* Die Klasse Highfield
erzeugt ein sogenanntes Highfield
* mit Hilfe eines IndexedTriangleArray
-Objekts. Dabei
* werden die Werte eines zweidimensionalen Arrays als
* Höheninformationen fü eine dreidimensionale Landschaft
* gewertet.
*
* @version 1.1 03/1999
* @author Frank Buß
*/
public class Highfield extends Shape3D {
/**
* Hilfsvektor 1 zur Normalenberechnug.
*/
private Vector3f v1 = new Vector3f();
/**
* Hilfsvektor 2 zur Normalenberechnug.
*/
private Vector3f v2 = new Vector3f();
/**
* Berechnet den Normalenvektor eines Dreiecks
* und liefert ihn als neuen Vektor zurück.
*
* @param p0 Erster Punkt des Dreiecks.
* @param p1 Zweiter Punkt des Dreiecks.
* @param p2 Dritter Punkt des Dreiecks.
* @return Neu erzeugter Normalenvektor des Dreiecks.
*/
private Vector3f getNormal(Point3f p0, Point3f p1, Point3f p2) {
v1.sub(p1, p0);
v2.sub(p2, p0);
Vector3f normal = new Vector3f();
normal.cross(v1, v2);
normal.normalize();
return normal;
}
/**
* Erzeugt ein neues Highfield. In dem zweidimensionalen float-Array
* sind die Höheninformationen für das Highfield gespeichert.
*
* @param values Die Höheninformationen.
*/
public Highfield(float values[][]) {
// Konstruktor der Basisklasse aufrufen
super();
// Abmessung in horizontaler und vertikaler Richtung
int sizeX = values[0].length;
int sizeY = values.length;
// Anzahl Rechtecke
int rects = (sizeX - 1) * (sizeY - 1);
// Anzahl Punkte
int points = sizeX * sizeY;
// neues indiziertes Dreiecks Array anlegen
IndexedTriangleArray tris =
new IndexedTriangleArray(
points,
IndexedTriangleArray.COORDINATES | IndexedTriangleArray.NORMALS,
6 * rects);
//
// Punkte erzeugen
//
// Punktzähler initialisieren
int pointCounter = 0;
// Skalierung berechnen
float scaleX = 2f / (float) (sizeX - 1);
float scaleY = 2f / (float) (sizeY - 1);
for (int y = 0; y < sizeY; y++) {
for (int x = 0; x < sizeX; x++) {
// Punkt setzen
tris.setCoordinate(
pointCounter,
new Point3f(
(float) x * scaleX - 1f,
(float) y * scaleY - 1f,
values[y][x]));
// Punktindex erhöhen
pointCounter++;
}
}
//
// Dreiecke und Normalenvektoren für jedes Dreieck einzeln erzeugen
//
// Punkte für Normalenberechnung
Point3f p0 = new Point3f();
Point3f p1 = new Point3f();
Point3f p2 = new Point3f();
Point3f p3 = new Point3f();
// Array zur Speicherung der Normalenvektoren der Dreiecke
Vector3f triNormals[] = new Vector3f[2 * rects];
// Punktzähler zurücksetzen
pointCounter = 0;
// Dreieckszähler initialisieren
int triCounter = 0;
// Zähler für den Koordinatenindex initialisieren
int i = 0;
// alle Rechtecke durchgehen und jeweils 2 Dreiecke erzeugen
for (int y = 0; y < sizeY - 1; y++) {
for (int x = 0; x < sizeX - 1; x++) {
// Koordinaten- und Normalenindizes für 1. Dreieck festlegen
tris.setCoordinateIndex(i, pointCounter);
tris.setNormalIndex(i++, pointCounter);
tris.setCoordinateIndex(i, pointCounter + 1);
tris.setNormalIndex(i++, pointCounter + 1);
tris.setCoordinateIndex(i, pointCounter + sizeX + 1);
tris.setNormalIndex(i++, pointCounter + sizeX + 1);
// Koordinaten- und Normalenindizes für 2. Dreieck festlegen
tris.setCoordinateIndex(i, pointCounter);
tris.setNormalIndex(i++, pointCounter);
tris.setCoordinateIndex(i, pointCounter + sizeX + 1);
tris.setNormalIndex(i++, pointCounter + sizeX + 1);
tris.setCoordinateIndex(i, pointCounter + sizeX);
tris.setNormalIndex(i++, pointCounter + sizeX);
// Koordinaten des Rechtecks holen
tris.getCoordinate(pointCounter, p0);
tris.getCoordinate(pointCounter + 1, p1);
tris.getCoordinate(pointCounter + sizeX + 1, p2);
tris.getCoordinate(pointCounter + sizeX, p3);
// Normalenvektoren der beiden Dreiecke setzen
triNormals[triCounter++] = getNormal(p0, p1, p2);
triNormals[triCounter++] = getNormal(p0, p2, p3);
// Punktzähler erhöhen
pointCounter++;
}
// Punktzähler wieder erhöhen, da die Anzahl Punkte pro Reihe
// eins mehr ist als die Anzahl Rechtecke pro Reihe
pointCounter++;
}
//
// Normalenvektoren für jeden Punkt erzeugen, indem der Durchschnitt
// der Normalenvektoren aller angrenzenden Dreiecke berechnet wird
//
// Dreieckszähler zurücksetzen
triCounter = pointCounter = 0;
// Offsets für angrenzende Dreiecke
int triOfsX[] = { 0, 1, -2, -1, -2, 1 };
int triOfsY[] =
{ 0, 0, 0, -2 * sizeX + 2, -2 * sizeX + 2, -2 * sizeX + 2 };
for (int y = 0; y < sizeY; y++) {
for (int x = 0; x < sizeX; x++) {
// neuen Vektor anlegen
Vector3f normal = new Vector3f(0f, 0f, 0f);
// Zähler für Anzahl angrenzender Dreiecke initialisieren
int div = 0;
// mögliche angrenzende Dreiecke testen
for (int t = 0; t < 6; t++) {
// Sonderfälle für Ränder und Ecken berücksichtigen
if (triOfsX[t] < 0 && x == 0)
continue;
if (triOfsX[t] >= 0 && x == sizeX - 1)
continue;
if (triOfsY[t] < 0 && y == 0)
continue;
if (triOfsY[t] >= 0 && y == sizeY - 1)
continue;
// Normalenvektor addieren und Zähler erhöhen
normal.add(
triNormals[triCounter + triOfsX[t] + triOfsY[t]]);
div++;
}
// Durchschnitt berechnen, normalisieren und setzen
normal.scale(1f / ((float) div));
normal.normalize();
tris.setNormal(pointCounter, normal);
// nächstes Rechteck
triCounter += 2;
// Punktindex für nächsten Normalenvektor setzen
pointCounter++;
}
// Überlauf korrigieren
triCounter -= 2;
}
//
// Geometrie-Daten setzen
//
setGeometry(tris);
}
}