package ch.ethz.karto.map3d;
import javax.media.opengl.*;
public abstract class Map3DModel {
protected static final float ZOFFSET = 0;//0.03f;
protected float cellSize = 1.0f;
protected float grid[][];
protected float minValue = Float.MAX_VALUE;
protected float maxValue = Float.MIN_VALUE;
protected boolean modelInitialized = false;
protected Map3DTexture1DMapper texture1DMapper;
public Map3DModel() {
}
/**
* Constructs the terrain model.
* @param gl
*/
public abstract void loadModel(GL gl, Map3DTexture texture);
/**
* Draws the terrain model
* @param gl
*/
public abstract void draw(GL gl, boolean shading, boolean fog);
/**
* Releases any resources allocated. The model will not be used anymore
* after <code>release</code> is called.
* @param gl
*/
public abstract void releaseModel(GL gl);
/**
* Returns whether the required hardware and software are present to run.
* @return
*/
public abstract boolean canRun();
public boolean canDisplay(GL gl, float[][] grid) {
return grid != null && grid.length >= 2 && grid[0].length >= 2;
}
public boolean isInitialized() {
return this.grid != null;
}
public void setModel(float grid[][], float cellSize, Map3DTexture1DMapper texture1DMapper) {
this.grid = grid;
this.cellSize = cellSize;
computeMinMax();
this.modelInitialized = false;
this.texture1DMapper = texture1DMapper;
this.texture1DMapper.init(grid, minValue, maxValue);
}
public int getCols() {
return grid[0].length;
}
public int getRows() {
return grid.length;
}
public float getNormalizedModelWidth() {
if (grid == null) {
return Float.NaN;
}
return Math.min(1, (float) this.getCols() / this.getRows());
}
public float getNormalizedModelHeight() {
if (grid == null) {
return Float.NaN;
}
return Math.min(1, (float) this.getRows() / this.getCols());
}
public float getNormalizedMinimumValue() {
if (grid == null) {
return Float.NaN;
}
return minValue / Math.max(this.getRows(), this.getCols());
}
private void computeMinMax() {
final int rows = this.getRows();
final int cols = this.getCols();
this.minValue = Float.MAX_VALUE;
this.maxValue = Float.MIN_VALUE;
for (int r = 0; r < rows; ++r) {
final float[] row = this.grid[r];
for (int c = 0; c < cols; ++c) {
final float value = row[c];
if (!Float.isNaN(value)) {
this.minValue = Math.min(this.minValue, value);
this.maxValue = Math.max(this.maxValue, value);
}
}
}
}
public float getCenterElevation() {
if (this.grid == null) {
return 0;
}
return (0.3f * this.maxValue) / ((this.getCols() - 1) * this.cellSize);
}
public float getMinElevation() {
if (this.grid == null) {
return 0;
}
else return this.minValue;
}
public float getMaxElevation() {
if (this.grid == null) {
return 0;
}
else return this.maxValue;
}
/**
* Return the elevation at the point closest to the location provided.
* @returns The elevation at the closest point (if one exists), otherwise -1.
*/
public float getNearestNeighborElevation(double x, double y){
final int rows = this.getRows();
final int cols = this.getCols();
int row = (int) Math.round(y * rows);
int col = (int) Math.round(x * cols);
if(row >= 0 && row < rows && col >= 0 && col < cols)
return this.grid[row][col];
else return -1;
}
protected static void computeNormal(float[] normal, float z00, float z01, float z10) {
final float dx = z00 - z01;
final float dy = z00 - z10;
final float len_inv = 1.f / (float) Math.sqrt(dx * dx + dy * dy + 1.0f);
normal[0] = dx * len_inv;
normal[1] = dy * len_inv;
normal[2] = len_inv;
}
/**
* Call textureChanged() after the texture for this terrain model has changed.
*/
public void textureChanged() {
this.modelInitialized = false;
}
/**
* Returns a grid cell elevation
* @param row
* @param col
* @return
*/
public float z(int row, int col) {
final float zScale = 1.0F / cellSize;
final float s = 1.0F / (Math.max(getCols(), getRows()) - 1);
if(row < 0 || row >= getRows() || col < 0 || col >= getCols()) return 0;
else return (grid[row][col]) * s * zScale;
}
/**
* nearest neighbor elevation
* @param x
* @param y
* @return
*/
public float z(double x, double y) {
final float s = (Math.max(getCols(), getRows()) - 1);
int col = (int)Math.round(x * s);
int row = (int)Math.round(y * s);
return z(row, col);
}
}