/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package ch.ethz.karto.map3d;
import javax.media.opengl.GL;
import javax.media.opengl.GL2;
/**
*
* @author jenny
*/
public class Map3DModelDisplayList extends Map3DModel {
/**
* The display-list name for the base of the terrain.
* This ID is only generated once. When it is reused with glNewList and
* glEndList, a pre-existing list is replaced when glEndList is called.
*/
private int baseListID = 0;
/**
* The display-list name for the terrain.
* This ID is only generated once. When it is reused with glNewList and
* glEndList, a pre-existing list is replaced when glEndList is called.
*/
private int terrainListID = 0;
@Override
public void loadModel(GL gl1, Map3DTexture texture) {
if (modelInitialized) {
return;
}
GL2 gl = (GL2)gl1;
if (this.terrainListID != 0) {
gl.glDeleteLists(this.terrainListID, 1);
}
this.terrainListID = gl.glGenLists(1);
gl.glNewList(this.terrainListID, GL2.GL_COMPILE);
boolean hasVoidValueOnBorder = constructSurface(gl, texture);
gl.glEndList();
// base of relief model
// base of relief model
if (this.baseListID != 0) {
gl.glDeleteLists(this.baseListID, 1);
}
this.baseListID = gl.glGenLists(1);
gl.glNewList(baseListID, GL2.GL_COMPILE);
if (!hasVoidValueOnBorder) {
this.constructBoundingBox(gl);
}
gl.glEndList();
this.modelInitialized = true;
}
@Override
public void draw(GL gl1, boolean shading, boolean fog) {
/*
// draw the base
gl.glDisable(GL2.GL_TEXTURE_2D);
this.enableLight(gl, true);
gl.glCallList(this.model.getBaseListID());
this.enableLight(gl, this.lightEnabled);
*/
GL2 gl = (GL2)gl1;
gl.glCallList(this.getTerrainListID());
}
/**
* Constructs the terrain surface.
* @param gl
* @param textureID This texture is drapped onto the terrain if it differs from 0.
* @return Returns true if the grid contains void values along its border.
*/
private boolean constructSurface(GL gl1, Map3DTexture texture) {
if (this.grid == null) {
return true;
}
GL2 gl = (GL2)gl1;
final int rows = this.getRows();
final int cols = this.getCols();
final float s = 1.0F / (Math.max(cols, rows) - 1);
final float zScale = 1.0F / this.cellSize;
final float[] normal = new float[3];
boolean hasVoidValueOnBorder = false;
texture.updateEnabledState(gl);
// compute surface normals and texture coordinates
// compute surface normals and texture coordinates
for (int rowCenter = 1; rowCenter < rows; ++rowCenter) {
gl.glBegin(GL2.GL_TRIANGLE_STRIP);
int rowTop = rowCenter - 1;
int rowBottom = Math.min(rowCenter + 1, rows - 1);
for (int colCenter = 0; colCenter < cols; ++colCenter) {
final int colRight = Math.min(colCenter + 1, cols - 1);
final float v00 = zScale * (this.grid[rowTop][colCenter] - this.minValue);
final float v10 = zScale * (this.grid[rowCenter][colCenter] - this.minValue);
final float v20 = zScale * (this.grid[rowBottom][colCenter] - this.minValue);
final float v01 = zScale * (this.grid[rowTop][colRight] - this.minValue);
final float v11 = zScale * (this.grid[rowCenter][colRight] - this.minValue);
if (Float.isNaN(v00) || Float.isNaN(v10) || Float.isNaN(v20) || Float.isNaN(v01) || Float.isNaN(v11)) {
hasVoidValueOnBorder = rowTop == 0 || rowTop == rows - 1 || colCenter == 0 || colCenter == cols - 1;
continue;
}
final float x = colCenter * s;
final float y = rowTop * s;
if (texture.hasTexture()) {
if (texture.is1D()) {
gl.glTexCoord1f(x);
} else {
gl.glTexCoord2f(x, y);
}
} /*
final float dx = v01 - v11;
final float dy = v10 - v20;
final float dz = 2f * s;
final float linv = (float)(1./Math.sqrt(dx*dx+dy*dy+dz*dz));
normal[0] = dx * linv;
normal[1] = dy * linv;
normal[2] = dz * linv;*/
/*
final float dx = v01 - v11;
final float dy = v10 - v20;
final float dz = 2f * s;
final float linv = (float)(1./Math.sqrt(dx*dx+dy*dy+dz*dz));
normal[0] = dx * linv;
normal[1] = dy * linv;
normal[2] = dz * linv;*/
computeNormal(normal, v00, v01, v10);
gl.glNormal3fv(normal, 0);
gl.glVertex3f(x, y, v00 * s + ZOFFSET);
if (texture.hasTexture()) {
if (texture.is1D()) {
gl.glTexCoord1f(x);
} else {
gl.glTexCoord2f(x, y);
}
}
computeNormal(normal, v10, v11, v20);
gl.glNormal3fv(normal, 0);
gl.glVertex3f(x, y + s, v10 * s + ZOFFSET);
}
gl.glEnd();
}
return hasVoidValueOnBorder;
}
/**
* Constructs the base below the terrain.
* @param gl
*/
private void constructBoundingBox(GL gl1) {
if (this.grid == null) {
return;
}
GL2 gl = (GL2)gl1;
float x;
float y;
int rows = this.getRows();
int cols = this.getCols();
float s = 1.0F / (Math.max(cols, rows) - 1);
float zScale = s / this.cellSize;
float boxWidth = Math.min(1, (float) cols / rows);
float boxHeight = Math.min(1, (float) rows / cols);
gl.glBegin(GL2.GL_TRIANGLE_STRIP);
// left plane
// left plane
gl.glNormal3f(-1.0F, 0.0F, 0.0F);
x = 0.0F;
for (int r = 0; r < rows; ++r) {
y = r * s;
float z = this.grid[r][0];
//gl.glTexCoord2f(y, 0.f);
//gl.glTexCoord2f(y, 0.f);
gl.glVertex3f(x, y, 0.0F);
//gl.glTexCoord2f(y, z * tScale);
//gl.glTexCoord2f(y, z * tScale);
gl.glVertex3f(x, y, (z - this.minValue) * zScale + ZOFFSET);
}
// front plane
// front plane
gl.glNormal3f(0.0F, 1.0F, 0.0F);
y = boxHeight;
for (int c = 0; c < cols; ++c) {
x = c * s;
float z = this.grid[rows - 1][c];
//gl.glTexCoord2f(x, 0.f);
//gl.glTexCoord2f(x, 0.f);
gl.glVertex3f(x, y, 0.0F);
//gl.glTexCoord2f(x, z * tScale);
//gl.glTexCoord2f(x, z * tScale);
gl.glVertex3f(x, y, (z - this.minValue) * zScale + ZOFFSET);
}
// right plane
// right plane
gl.glNormal3f(1.0F, 0.0F, 0.0F);
x = boxWidth;
for (int r = rows - 1; r >= 0; --r) {
y = r * s;
float z = this.grid[r][cols - 1];
//gl.glTexCoord2f(1.0f - y, 0.f);
//gl.glTexCoord2f(1.0f - y, 0.f);
gl.glVertex3f(x, y, 0.0F);
//gl.glTexCoord2f(1.0f - y, z * tScale);
//gl.glTexCoord2f(1.0f - y, z * tScale);
gl.glVertex3f(x, y, (z - this.minValue) * zScale + ZOFFSET);
}
// back plane
// back plane
gl.glNormal3f(0.0F, -1.0F, 0.0F);
y = 0.0F;
for (int c = cols - 1; c >= 0; --c) {
x = c * s;
float z = this.grid[0][c];
//gl.glTexCoord2f(1.0f - x, 0.f);
//gl.glTexCoord2f(1.0f - x, 0.f);
gl.glVertex3f(x, y, 0.0F);
//gl.glTexCoord2f(1.0f - x, z * tScale);
//gl.glTexCoord2f(1.0f - x, z * tScale);
gl.glVertex3f(x, y, (z - this.minValue) * zScale + ZOFFSET);
}
gl.glVertex3f(0.0F, 0.0F, 0.0F);
gl.glEnd();
}
/**
* Returns the OpenGL list name of the base of the terrain.
* @return
*/
public int getBaseListID() {
return baseListID;
}
/**
* Returns the OpenGL list name of the terrain surface.
* @return
*/
public int getTerrainListID() {
return terrainListID;
}
@Override
public void releaseModel(GL gl) {
}
@Override
public boolean canRun() {
return true;
}
}