/**
* TerrainRenderWindow.java
* Copyright 2011, Craig A. Damon
* all rights reserved
*/
package edu.vtc.cis4210.collada;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.media.opengl.GL;
import javax.media.opengl.GL2;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLCapabilities;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.awt.GLJPanel;
import javax.media.opengl.fixedfunc.GLLightingFunc;
import javax.media.opengl.fixedfunc.GLMatrixFunc;
import javax.media.opengl.glu.GLU;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingConstants;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
/**
* TerrainRenderWindow - render a generic terrain
* @author Craig A. Damon
*
*/
public class TerrainRenderWindow extends JFrame implements GLEventListener
{
/**
*
*/
private static final long serialVersionUID = -639973996724650421L;
/**
* run a test version of this
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception
{
double[][] elev = new double[8][];
elev[0] = new double[]{ 0.0, 0.2, 0.8, 1.4, 1.8, 1.1, 0.7, 0.3 };
for (int i = 1; i < elev.length; i++)
{
elev[i] = new double[]{0.3+elev[i-1][1],0.7-elev[i-1][0],1.1-elev[i-1][3]*0.5,1.4+elev[i-1][2]*0.3,
1.3+elev[i-1][5]*0.1,0.9-elev[i-1][4]*elev[1-1][2],0.5+elev[i-1][7]*0.2,0.1+elev[i-1][6]*0.3};
}
ColorChooser colors = new ColorChooser(){
@Override
public float[] chooseColor(double x, double y, double z)
{
if (y > 0)
return new float[]{0,1,0};
if (y < 0.4)
{
System.out.println("Color for "+x+","+y+","+z+" is valley");
return new float[]{0.0f,1.0f,0.0f};
}
if (y < 1.0)
{
System.out.println("Color for "+x+","+y+","+z+" is hillside");
return new float[]{1.0f,0f,0f};
}
if (y < 1.7)
{
System.out.println("Color for "+x+","+y+","+z+" is mountain");
return new float[]{0.0f,0.0f,1.0f};
}
System.out.println("Color for "+x+","+y+","+z+" is mountaintop");
return new float[]{1,1,1};
}
};
new TerrainRenderWindow(elev, colors );
}
/**
* create a render window
* @param elevData the elevation at each point in the 2D grid
* @param coloring the color chooser, pass null for white
* @throws Exception if anything goes wrong
*/
public TerrainRenderWindow(double[][] elevData,ColorChooser coloring) throws Exception
{
super("Render"); // set the window title
_elevData = elevData;
for (int i = 0; i < _elevData.length; i++)
{
for (int j = 0; j < _elevData[i].length; j++)
{
System.out.print(_elevData[i][j]);
System.out.print('\t');
}
System.out.println();
}
// set the initial orientation
_distance = 30;
_x = 0;
_y = 0;
_z = 0;
_scale = 1;
// create the window elements
createCanvas();
getContentPane().setLayout(new BorderLayout());
getContentPane().add(_canvas,BorderLayout.CENTER);
getContentPane().add(new ControlPanel(),BorderLayout.SOUTH);
GLCapabilities capabilities = new GLCapabilities(null);
capabilities.setRedBits(8);
capabilities.setBlueBits(8);
capabilities.setGreenBits(8);
capabilities.setAlphaBits(8);
_drawColors = false; // coloring != null;
_coloring = coloring;
_drawSolid = true;
_drawWireFrame = false;
// finish up
pack();
addWindowListener(new WindowAdapter(){
@Override
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
setVisible(true);
}
/** create a drawable panel
*
*/
private void createCanvas()
{
_canvas = new GLJPanel();
_canvas.setPreferredSize(new Dimension(1000,1000));
_canvas.addGLEventListener(this);
}
/**
* @param canvas
* @see javax.media.opengl.GLEventListener#display(javax.media.opengl.GLAutoDrawable)
*/
@Override
public void display(GLAutoDrawable canvas)
{
GL2 gl = (GL2) canvas.getGL();
// Change back to model view matrix.
gl.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
gl.glLoadIdentity();
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
gl.glPushMatrix();
gl.glTranslatef(-_x,-_y,_z);
gl.glRotatef(_rotx,1,0,0);
gl.glRotatef(_roty,0,1,0);
gl.glRotatef(_rotz,0,0,1);
gl.glScalef(_scale,_scale,_scale);
// display it appropriately
if (_drawSolid)
{
if (_drawColors)
drawColors(gl);
else
drawSolid(gl);
}
if (_drawWireFrame)
drawWireFrame(gl);
gl.glPopMatrix();
}
private void chooseColorXYZ(GL2 gl,double x,double y, double z)
{
if (_coloring == null)
gl.glColor3f(1,1,1);
else
{
float[] color = _coloring.chooseColor(x,y,z);
System.out.println("Coloring "+x+","+y+" as "+((int)color[0]*255)+","+((int)color[1]*255)+","+((int)color[2]*255));
gl.glColor3f(color[0],color[1],color[2]);
}
}
private void drawWireFrame(GL2 gl)
{
gl.glLineWidth(1);
for (int i = 0; i < _elevData.length; i++)
{
gl.glColor3f(1,1,1);
gl.glBegin(GL.GL_LINE_STRIP);
for (int j = 0; j < _elevData[i].length; j++)
{
gl.glVertex3d(i,_elevData[i][j],j);
}
gl.glEnd();
if (i > 0)
{
for (int j = 0; j < _elevData[i].length; j++)
{
gl.glColor3f(1,1,1);
gl.glBegin(GL.GL_LINES);
gl.glVertex3d(i-1,_elevData[i-1][j],j);
gl.glVertex3d(i,_elevData[i][j],j);
gl.glEnd();
}
}
}
}
private void drawSolid(GL2 gl)
{
gl.glColor3f(1,1,1);
float p1x,p1y,p1z;
float p2x,p2y,p2z;
float p3x,p3y,p3z;
float nx,ny,nz;
gl.glColor3f(0.0f,1.0f,0.0f);
for (int i = 1; i < _elevData.length; i++)
{
gl.glBegin(GL.GL_TRIANGLE_STRIP);
p1x = i-1;
p1y = (float)_elevData[i-1][0];
p1z = 0;
gl.glVertex3f(p1x,p1y,p1z);
p2x = i;
p2y = (float)_elevData[i][0];
p2z = 0;
gl.glVertex3f(p2x,p2y,p2z);
for (int j = 1; j < _elevData[i].length; j++)
{
p3x = i-1;
p3y = (float)_elevData[i-1][j];
p3z = j;
gl.glVertex3f(p3x,p3y,p3z);
nx = (p1y-p2y)*(p3z-p2z) - (p1z-p2z)*(p3y-p2y);
ny = (p1z-p2z)*(p3x-p2x) - (p1x-p2x)*(p3z-p2z);
nz = (p1x-p2x)*(p3y-p2y) - (p1y-p1y)*(p3x-p2x);
gl.glNormal3f(nx,ny,nz);
p1x = p2x; p1y = p2y; p1z = p2z;
p2x = p3x; p2y = p3y; p2z = p3z;
p3x = i;
p3y = (float)_elevData[i][j];
p3z = j;
gl.glVertex3f(p3x,p3y,p3z);
nx = (p2y-p1y)*(p3z-p1z) - (p2z-p1z)*(p3y-p1y);
ny = (p2z-p1z)*(p3x-p1x) - (p2x-p1x)*(p3z-p1z);
nz = (p2x-p1x)*(p3y-p1y) - (p2y-p1y)*(p3x-p1x);
gl.glNormal3f(nx,ny,nz);
p1x = p2x; p1y = p2y; p1z = p2z;
p2x = p3x; p2y = p3y; p2z = p3z;
}
gl.glEnd();
}
}
private void drawColors(GL2 gl)
{
float p1x,p1y,p1z;
float p2x,p2y,p2z;
float p3x,p3y,p3z;
float nx,ny,nz;
gl.glShadeModel(GLLightingFunc.GL_SMOOTH);
for (int i = 1; i < _elevData.length; i++)
{
gl.glBegin(GL.GL_TRIANGLE_STRIP);
p1x = i-1;
p1y = (float)_elevData[i-1][0];
p1z = 0;
chooseColorXYZ(gl,p1x,p1y,p1z);
gl.glVertex3f(p1x,p1y,p1z);
p2x = i;
p2y = (float)_elevData[i][0];
p2z = 0;
chooseColorXYZ(gl,p2x,p2y,p2z);
gl.glVertex3f(p2x,p2y,p2z);
for (int j = 1; j < _elevData[i].length; j++)
{
p3x = i-1;
p3y = (float)_elevData[i-1][j];
p3z = j;
chooseColorXYZ(gl,p3x,p3y,p3z);
gl.glVertex3f(p3x,p3y,p3z);
nx = (p1y-p2y)*(p3z-p2z) - (p1z-p2z)*(p3y-p2y);
ny = (p1z-p2z)*(p3x-p2x) - (p1x-p2x)*(p3z-p2z);
nz = (p1x-p2x)*(p3y-p2y) - (p1y-p1y)*(p3x-p2x);
gl.glNormal3f(nx,ny,nz);
p1x = p2x; p1y = p2y; p1z = p2z;
p2x = p3x; p2y = p3y; p2z = p3z;
p3x = i;
p3y = (float)_elevData[i][j];
p3z = j;
chooseColorXYZ(gl,p3x,p3y,p3z);
gl.glVertex3f(p3x,p3y,p3z);
nx = (p2y-p1y)*(p3z-p1z) - (p2z-p1z)*(p3y-p1y);
ny = (p2z-p1z)*(p3x-p1x) - (p2x-p1x)*(p3z-p1z);
nz = (p2x-p1x)*(p3y-p1y) - (p2y-p1y)*(p3x-p1x);
gl.glNormal3f(nx,ny,nz);
p1x = p2x; p1y = p2y; p1z = p2z;
p2x = p3x; p2y = p3y; p2z = p3z;
}
gl.glEnd();
}
}
/**
* @param arg0
* @see javax.media.opengl.GLEventListener#dispose(javax.media.opengl.GLAutoDrawable)
*/
@Override
public void dispose(GLAutoDrawable canvas)
{
// nothing to do here
}
/**
* @param arg0
* @see javax.media.opengl.GLEventListener#init(javax.media.opengl.GLAutoDrawable)
*/
@Override
public void init(GLAutoDrawable canvas)
{
GL2 gl = (GL2) _canvas.getGL();
gl.glEnable(GL.GL_DEPTH_TEST);
gl.glDepthFunc(GL.GL_LEQUAL);
GLU glu = new GLU();
gl.glMatrixMode(GLMatrixFunc.GL_PROJECTION);
gl.glLoadIdentity();
// Perspective.
float widthHeightRatio = (float) getWidth() / (float) getHeight();
glu.gluPerspective(45, widthHeightRatio, 1, 2000);
glu.gluLookAt(0, 0, _distance, 0, 0, 0, 0, 1, 0);
// lighting
float[] light_pos = new float[]{0,1000,1000,1};
float[] diffuse_color = new float[]{1.0f,1.0f,0.8f,1.0f};
float[] ambient_color = new float[]{0.5f,0.5f,0.5f,1.0f};
gl.glEnable(GL2.GL_LIGHTING);
gl.glEnable(GL2.GL_LIGHT0);
gl.glLightfv(GL2.GL_LIGHT0, GL2.GL_POSITION, light_pos,0);
gl.glLightfv(GL2.GL_LIGHT0,GL2.GL_DIFFUSE, diffuse_color,0);
gl.glLightfv(GL2.GL_LIGHT0,GL2.GL_AMBIENT, ambient_color,0);
gl.glColorMaterial(GL2.GL_FRONT_AND_BACK,GL2.GL_AMBIENT_AND_DIFFUSE);
}
/**
* @param arg0
* @param arg1
* @param arg2
* @param arg3
* @param arg4
* @see javax.media.opengl.GLEventListener#reshape(javax.media.opengl.GLAutoDrawable, int, int, int, int)
*/
@Override
public void reshape(GLAutoDrawable canvas, int arg1, int arg2, int arg3,
int arg4)
{
// TODO Auto-generated method stub
}
private ColorChooser _coloring;
private boolean _drawSolid;
private boolean _drawColors;
private boolean _drawWireFrame;
private GLJPanel _canvas;
private double _distance;
private float _x;
private float _y;
private float _z;
private float _rotx;
private float _roty;
private float _rotz;
private float _scale;
private double[][] _elevData;
private class ControlPanel extends JPanel
{
/**
*
*/
private static final long serialVersionUID = -7606307784007999393L;
public ControlPanel()
{
setLayout(new BoxLayout(this,BoxLayout.X_AXIS));
add(Box.createHorizontalStrut(5));
add(Box.createHorizontalGlue());
add(new JLabel("Size:"));
add(Box.createHorizontalStrut(5));
JSlider slider = new JSlider(SwingConstants.HORIZONTAL,0,100,50);
slider.setPaintLabels(false);
slider.addChangeListener(new ChangeListener()
{
@Override
public void stateChanged(ChangeEvent e)
{
_scale = (float)Math.pow(10,(((JSlider)e.getSource()).getValue()-50)/20.0);
_canvas.repaint();
}
});
add(slider);
add(Box.createHorizontalGlue());
add(Box.createHorizontalStrut(5));
add(Box.createHorizontalGlue());
add(new JLabel("Rotate X:"));
add(Box.createHorizontalStrut(5));
slider = new JSlider(SwingConstants.VERTICAL,-180,180,0);
slider.setPaintLabels(false);
slider.addChangeListener(new ChangeListener()
{
@Override
public void stateChanged(ChangeEvent e)
{
_rotx = (float)((JSlider)e.getSource()).getValue();
_canvas.repaint();
}
});
add(slider);
add(Box.createHorizontalGlue());
add(Box.createHorizontalStrut(5));
add(Box.createHorizontalGlue());
add(new JLabel("Rotate Y:"));
add(Box.createHorizontalStrut(5));
slider = new JSlider(SwingConstants.HORIZONTAL,-180,180,0);
slider.setPaintLabels(false);
slider.addChangeListener(new ChangeListener()
{
@Override
public void stateChanged(ChangeEvent e)
{
_roty = (float)((JSlider)e.getSource()).getValue();
_canvas.repaint();
}
});
add(slider);
add(Box.createHorizontalGlue());
add(Box.createHorizontalStrut(5));
add(Box.createHorizontalGlue());
add(new JLabel("Rotate Z:"));
add(Box.createHorizontalStrut(5));
slider = new JSlider(SwingConstants.VERTICAL,-180,180,0);
slider.setPaintLabels(false);
slider.addChangeListener(new ChangeListener()
{
@Override
public void stateChanged(ChangeEvent e)
{
_rotz = (float)((JSlider)e.getSource()).getValue();
_canvas.repaint();
}
});
add(slider);
add(Box.createHorizontalGlue());
add(Box.createHorizontalStrut(5));
}
}
public static interface ColorChooser
{
/**
* choose a color based on x,y,z
* @param x
* @param y
* @param z
* @return an array of 3 floats (R,G,B)
*/
public float[] chooseColor(double x,double y,double z);
}
}