package display.leavu3d;
import display.*;
import general.Vector;
import transforms.*;
import javax.media.opengl.*;
import com.sun.opengl.util.j2d.TextRenderer;
import java.awt.Font;
import java.awt.geom.Rectangle2D;
import javax.media.opengl.GL;
/**
*
* @author jkjolhed
*/
public class Leavu3DRenderer extends display.LeavuRenderer {
private final static double defaultCameraOffset = 10;
private final static double defaultXYrange = 1;
private final double defaultFOVdegrees = 2 * (180.0 / Math.PI) * Math.atan(defaultXYrange / defaultCameraOffset);
private boolean needToUpdateFOV = true;
private boolean fillDisplay = false;
private final TextRenderer textRenderer = new TextRenderer(new Font("SansSerif", Font.PLAIN, 72));
private final Vector<Text> textsRenderLast = new Vector<Text>();
private final float textScaleFactor = 0.0017f;
static class Text {
private final Text3D text;
private final double[] modelviewMatrix;
Text(Text3D text, double[] modelviewMatrix) {
this.text = text;
this.modelviewMatrix = modelviewMatrix;
}
}
/**
* Creates a new LEAVU renderer
*
* @param canvas
* @param graphicsBuffer
*/
public Leavu3DRenderer(final GLCanvas canvas, final GraphicsBuffer graphicsBuffer) {
super(canvas, graphicsBuffer);
}
public final void setFillDisplay(boolean onOff) {
fillDisplay = onOff;
needToUpdateFOV = true;
}
public final boolean getFillDisplay() {
return fillDisplay;
}
/**
* Sets up OpenGL once during creation
*
* @param gLDrawable
*/
@Override
public void init(GLAutoDrawable drawable) {
GL gl = drawable.getGL();
float mat_shininess[] = {550.0f};
float light_position[] = {0.0f, 1.0f, 3.0f, 0.0f};
gl.glShadeModel(GL.GL_SMOOTH);
gl.glMaterialfv(GL.GL_FRONT, GL.GL_SHININESS, mat_shininess, 0);
gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, light_position, 0);
gl.glEnable(GL.GL_LIGHTING);
gl.glEnable(GL.GL_LIGHT0);
gl.glEnable(GL.GL_DEPTH_TEST);
gl.glEnable(GL.GL_CULL_FACE);
}
/**
* Main drawing loop
*
* @param gLDrawable
*/
@Override
public void display(GLAutoDrawable drawable) {
GL gl = drawable.getGL();
ensureFOV(gl);
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
gl.glEnableClientState(GL.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL.GL_NORMAL_ARRAY);
for (GraphicsInstruction instr : graphicsBuffer.getInstructions()) {
draw(gl, (GraphicsInstruction3D) instr);
}
gl.glDisableClientState(GL.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL.GL_NORMAL_ARRAY);
// Render texts all at once ( much better performance )
textRenderer.begin3DRendering();
//gl.glEnable(GL.GL_LIGHTING);
gl.glPushMatrix();
for (Text text : textsRenderLast) {
renderText(gl, text.text, text.modelviewMatrix);
}
gl.glPopMatrix();
textRenderer.end3DRendering();
textsRenderLast.removeAllElements();
}
private final void ensureFOV(GL gl) {
if (needToUpdateFOV) {
int[] oldModeArray = new int[1];
gl.glGetIntegerv(GL.GL_MATRIX_MODE, oldModeArray, 0);
int oldMode = oldModeArray[0];
gl.glMatrixMode(GL.GL_PROJECTION);
gl.glLoadIdentity();
gl.glViewport(0, 0, res[0], res[1]);
double R = Math.min((double) res[0], (double) res[1]) / Math.max((double) res[0], (double) res[1]);
double aspect = fillDisplay ? 1.0 : (float) res[0] / (float) res[1];
if (!fillDisplay && res[0] > 0 && res[0] < res[1]) {
double FOV = 2 * (180.0 / Math.PI) * Math.atan(defaultXYrange / (R * defaultCameraOffset));
glu.gluPerspective(FOV, aspect, 1, 100);
} else {
glu.gluPerspective(defaultFOVdegrees, aspect, 1, 100);
}
needToUpdateFOV = false;
gl.glMatrixMode(oldMode);
}
}
/**
* This function is called when the window is reshaped.
*
* @param gl
*/
@Override
public void setMatricesOnReshape(GL gl) {
needToUpdateFOV = true;
gl.glMatrixMode(GL.GL_MODELVIEW);
gl.glLoadIdentity();
glu.gluLookAt(0, 0, defaultCameraOffset,
0, 0, 0,
0, 1, 0);
}
private final void renderText(GL gl, Text3D text, double[] modelviewMatrix) {
gl.glLoadMatrixd(modelviewMatrix, 0);
// textRenderer.setColor(1.0f, 1.0f, 1.0f, 1.0f);
gl.glColor4d(text.color.R[0], text.color.R[1], text.color.R[2], 1.0f);
Rectangle2D bounds = textRenderer.getBounds(text.text);
float alignOffset = 0f, heightOffset = -(0.9f) * textScaleFactor * (float) bounds.getHeight() / 2;
if (text.align == Text3D.AlignCenter) {
alignOffset = -textScaleFactor * (float) bounds.getWidth() / 2;
} else if (text.align == Text3D.AlignRight) {
alignOffset = -textScaleFactor * (float) bounds.getWidth();
}
textRenderer.draw3D(text.text, alignOffset, heightOffset, 0, textScaleFactor);
textRenderer.flush();
}
// This function is only called when the display settings are changed. Again, not often used.
@Override
public void displayChanged(GLAutoDrawable drawable, boolean a, boolean b) {
}
private void draw(GL gl, GraphicsInstruction3D instr) {
Vector<Transform> transforms = instr.getTransforms();
gl.glPushMatrix();
// Calculate transformation matrix
for (int i = transforms.size() - 1; i >= 0; i--) {
Transform cur = transforms.get(i);
switch (cur.type) {
case Transform.ROTATION:
gl.glRotated(cur.val[0], cur.val[1], cur.val[2], cur.val[3]);
break;
case Transform.TRANSLATION:
gl.glTranslated(cur.val[0], cur.val[1], cur.val[2]);
break;
case Transform.SCALE:
gl.glScaled(cur.val[0], cur.val[1], cur.val[2]);
break;
}
}
DrawType[] drawTypes = instr.getDrawtypes();
if (instr.numberOfCoords() > 0) {
if (instr.hasShaderDetails()) {
gl.glMaterialfv(GL.GL_FRONT, GL.GL_SPECULAR, instr.getSpecular(), 0);
gl.glMaterialfv(GL.GL_FRONT, GL.GL_DIFFUSE, instr.getDiffuse(), 0);
gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT, instr.getAmbient(), 0);
gl.glVertexPointer(3, GL.GL_FLOAT, 0, instr.getVerticesBuffer());
gl.glNormalPointer(GL.GL_FLOAT, 0, instr.getNormalsBuffer());
gl.glDrawElements(drawTypes.length > 0 ? drawTypes[0].type : GL.GL_TRIANGLE_STRIP, instr.numElements(), GL.GL_UNSIGNED_INT, instr.getIndicesBuffer());
} else {
//This section is likely not entered
Coordinate[] coordinates = instr.getCoordinates();
int primType = GL.GL_TRIANGLE_STRIP;
Color[] colors = instr.getColors();
gl.glDisable(GL.GL_LIGHTING);
gl.glBegin(primType);
for (int i = 0; i < coordinates.length; i++) {
if (drawTypes[i].type != primType) {
gl.glEnd();
gl.glBegin(primType = drawTypes[i].type);
}
gl.glColor4d(colors[i].R[0], colors[i].R[1], colors[i].R[2], colors[i].R[3]);
gl.glVertex3d(coordinates[i].R[0], coordinates[i].R[1], coordinates[i].R[2]);
}
gl.glEnd();
gl.glEnable(GL.GL_LIGHTING);
}
}
if (instr.text != null) {
double[] modelviewMatrix = new double[16];
gl.glGetDoublev(gl.GL_MODELVIEW_MATRIX, modelviewMatrix, 0);
textsRenderLast.add(new Text(instr.text, modelviewMatrix));
}
gl.glPopMatrix();
}
}