/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package madsdf.shimmer.glview;
import com.google.common.eventbus.Subscribe;
import com.jogamp.opengl.util.FPSAnimator;
import com.jogamp.opengl.util.texture.Texture;
import com.jogamp.opengl.util.texture.TextureIO;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.media.opengl.DebugGL2;
import javax.media.opengl.GL;
import javax.media.opengl.GL2;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.glu.GLU;
import static javax.media.opengl.GL2.*; // GL2 constants
import javax.media.opengl.GLProfile;
import javax.media.opengl.awt.GLJPanel;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.event.MouseInputAdapter;
import madsdf.shimmer.glutils.ArcBall;
import madsdf.shimmer.glutils.Matrix4f;
import madsdf.shimmer.glutils.Quat4f;
import madsdf.shimmer.glview.ShimmerAngleConverter.AngleEvent;
/**
*
* @author julien
*/
// Can extend either GLCanvas or GLJPanel
public class ShimmerCanvas extends GLJPanel implements GLEventListener {
private class InputHandler extends MouseInputAdapter {
@Override
public void mouseClicked(MouseEvent e) {
if (SwingUtilities.isRightMouseButton(e)) {
// Reset drag
synchronized (matrixLock) {
lastRot.setIdentity();
thisRot.setIdentity();
}
}
}
@Override
public void mousePressed(MouseEvent mouseEvent) {
if (SwingUtilities.isLeftMouseButton(mouseEvent)) {
// Start drag
synchronized (matrixLock) {
lastRot.set(thisRot);
}
arcBall.click(mouseEvent.getPoint());
}
}
@Override
public void mouseDragged(MouseEvent mouseEvent) {
if (SwingUtilities.isLeftMouseButton(mouseEvent)) {
// Continue drag
Quat4f thisQuat = new Quat4f();
arcBall.drag(mouseEvent.getPoint(), thisQuat);
synchronized (matrixLock) {
thisRot.setRotation(thisQuat);
thisRot.mul(thisRot, lastRot); // accumulate rotations
}
}
}
}
private GLU glu;
private ArcBall arcBall = new ArcBall(1, 1, false);
// lastRot and thisRot are accessed by Swing. matrixLock should be locked
// when accessing them
private Matrix4f lastRot = new Matrix4f();
private Matrix4f thisRot = new Matrix4f();
private final Object matrixLock = new Object();
// Buffer used for temporary copy of lastRot/thisRot when rendering
private float[] matrix = new float[16];
// Angles in radians
private float[] shimmerAngles = new float[3];
private final Object angleLock = new Object();
private final InputHandler inputHandler = new InputHandler();
// Note : We can't use a cubemap because our textures aren't all square
// But what we want to do is pretty close to a cubemap
// Texture side
private final int POS_X = 0;
private final int NEG_X = 1;
private final int POS_Y = 2;
private final int NEG_Y = 3;
private final int POS_Z = 4;
private final int NEG_Z = 5;
private Texture[] cubeTextures = new Texture[6];
private FPSAnimator animator;
// Factory method
public static ShimmerCanvas createCanvas(JPanel panel) {
ShimmerCanvas c = new ShimmerCanvas();
c.addGLEventListener(c);
c.addMouseListener(c.inputHandler);
c.addMouseMotionListener(c.inputHandler);
c.animator = new FPSAnimator(c, 30);
c.animator.start();
return c;
}
public void stop() {
animator.stop();
}
private ShimmerCanvas() {}
@Subscribe
public void anglesUpdated(AngleEvent event) {
synchronized(angleLock) {
shimmerAngles[0] = event.roll;
shimmerAngles[1] = event.pitch;
shimmerAngles[2] = event.yaw;
}
}
@Override
public void init(GLAutoDrawable drawable) {
GLProfile profile = drawable.getGLProfile();
GL _gl = drawable.setGL(new DebugGL2(drawable.getGL().getGL2()));
GL2 gl = _gl.getGL2();
glu = new GLU(); // get GL Utilities
gl.glClearColor(0.9f,0.9f,0.9f,1); // set background (clear) color
gl.glClearDepth(1.0f); // set clear depth value to farthest
gl.glEnable(GL_DEPTH_TEST); // enables depth testing
gl.glDepthFunc(GL_LEQUAL); // the type of depth test to do
gl.glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // best perspective correction
gl.glShadeModel(GL_SMOOTH); // blends colors nicely, and smoothes out lighting
// Texture initialization
final String[] suffixes = {"posx", "negx", "posy", "negy", "posz", "negz"};
final int[] faces = {POS_X, NEG_X, POS_Y, NEG_Y, POS_Z, NEG_Z};
final String basename = "/madsdf/shimmer/textures/cubemap_";
final String ext = "png";
for (int i = 0; i < suffixes.length; i++) {
String resourceName = basename + suffixes[i] + "." + ext;
try {
InputStream stream = this.getClass().getResourceAsStream(resourceName);
cubeTextures[faces[i]] = TextureIO.newTexture(stream, true, TextureIO.PNG);
} catch (IOException ex) {
Logger.getLogger(ShimmerCanvas.class.getName()).log(Level.SEVERE, null, ex);
}
}
// Initial rotation
thisRot.set(-0.6528543f, 0.75170904f, -0.09335407f, 0.0f,
-0.35754132f, -0.1971564f, 0.9128492f, 0.0f,
0.66779155f, 0.6293354f, 0.39748144f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
}
@Override
public void dispose(GLAutoDrawable drawable) {}
@Override
public void display(GLAutoDrawable drawable) {
synchronized(matrixLock) {
thisRot.get(matrix);
}
float roll, pitch, yaw;
synchronized(angleLock) {
roll = shimmerAngles[0];
pitch = shimmerAngles[1];
yaw = shimmerAngles[2];
}
GL2 gl = drawable.getGL().getGL2(); // get the OpenGL 2 graphics context
gl.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear color and depth buffers
gl.glLoadIdentity(); // reset the model-view matrix
gl.glTranslatef(0, 0, -6.0f);
gl.glMultMatrixf(matrix, 0);
//System.out.println(thisRot);
gl.glLineWidth(3.0f);
gl.glPushMatrix();
gl.glScalef(2, 2, 2);
drawAxis(gl);
gl.glPopMatrix();
gl.glLineWidth(1.0f);
gl.glPushMatrix();
{
gl.glRotatef(roll, 1, 0, 0);
gl.glRotatef(pitch, 0, 1, 0);
gl.glRotatef(yaw, 0, 0, 1);
// Draw rotated axis
gl.glPushMatrix();
{
gl.glScalef(1.5f, 1.5f, 1.5f);
drawAxis(gl);
}
gl.glPopMatrix();
gl.glEnable(GL_TEXTURE_2D);
gl.glColor3f(1,1,1);
gl.glPushMatrix();
{
gl.glScalef(1/2.f, 1f, 1/8f);
drawCubeTex(gl, cubeTextures);
}
gl.glPopMatrix();
gl.glDisable(GL_TEXTURE_2D);
}
gl.glPopMatrix();
}
@Override
public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
GL2 gl = drawable.getGL().getGL2(); // get the OpenGL 2 graphics context
if (height == 0) {
height = 1; // prevent divide by zero
}
float aspect = (float) width / height;
// Set the view port (display area) to cover the entire window
gl.glViewport(0, 0, width, height);
// Setup perspective projection, with aspect ratio matches viewport
gl.glMatrixMode(GL_PROJECTION); // choose projection matrix
gl.glLoadIdentity(); // reset projection matrix
glu.gluPerspective(45.0, aspect, 0.1, 100.0); // fovy, aspect, zNear, zFar
// Enable the model-view transform
gl.glMatrixMode(GL_MODELVIEW);
gl.glLoadIdentity(); // reset
//System.out.println("New size : " + width + ", " + height);
arcBall.setBounds((float)width, (float)height);
}
private void drawAxis(GL2 gl) {
gl.glBegin(GL_LINES);
gl.glColor3f(1, 0, 0);
gl.glVertex3f(0, 0, 0);
gl.glVertex3f(1, 0, 0);
gl.glColor3f(0, 1, 0);
gl.glVertex3f(0, 0, 0);
gl.glVertex3f(0, 1, 0);
gl.glColor3f(0, 0, 1);
gl.glVertex3f(0, 0, 0);
gl.glVertex3f(0, 0, 1);
gl.glEnd();
}
private void drawCubeTex(GL2 gl, Texture[] textures) {
// +X Face
textures[POS_X].bind(gl);
gl.glBegin(GL_QUADS);
gl.glTexCoord2f(0.0f, 0.0f);
gl.glVertex3f(1.0f, -1.0f, -1.0f);
gl.glTexCoord2f(1.0f, 0.0f);
gl.glVertex3f(1.0f, 1.0f, -1.0f);
gl.glTexCoord2f(1.0f, 1.0f);
gl.glVertex3f(1.0f, 1.0f, 1.0f);
gl.glTexCoord2f(0.0f, 1.0f);
gl.glVertex3f(1.0f, -1.0f, 1.0f);
gl.glEnd();
// -X Face
textures[NEG_X].bind(gl);
gl.glBegin(GL_QUADS);
gl.glTexCoord2f(0.0f, 0.0f);
gl.glVertex3f(-1.0f, -1.0f, -1.0f);
gl.glTexCoord2f(0.0f, 1.0f);
gl.glVertex3f(-1.0f, -1.0f, 1.0f);
gl.glTexCoord2f(1.0f, 1.0f);
gl.glVertex3f(-1.0f, 1.0f, 1.0f);
gl.glTexCoord2f(1.0f, 0.0f);
gl.glVertex3f(-1.0f, 1.0f, -1.0f);
gl.glEnd();
// +Y Face
textures[POS_Y].bind(gl);
gl.glBegin(GL_QUADS);
gl.glTexCoord2f(0.0f, 0.0f);
gl.glVertex3f(-1.0f, 1.0f, -1.0f);
gl.glTexCoord2f(0.0f, 1.0f);
gl.glVertex3f(-1.0f, 1.0f, 1.0f);
gl.glTexCoord2f(1.0f, 1.0f);
gl.glVertex3f(1.0f, 1.0f, 1.0f);
gl.glTexCoord2f(1.0f, 0.0f);
gl.glVertex3f(1.0f, 1.0f, -1.0f);
gl.glEnd();
// -Y Face
textures[NEG_Y].bind(gl);
gl.glBegin(GL_QUADS);
gl.glTexCoord2f(1.0f, 1.0f);
gl.glVertex3f(-1.0f, -1.0f, -1.0f);
gl.glTexCoord2f(0.0f, 1.0f);
gl.glVertex3f(1.0f, -1.0f, -1.0f);
gl.glTexCoord2f(0.0f, 0.0f);
gl.glVertex3f(1.0f, -1.0f, 1.0f);
gl.glTexCoord2f(1.0f, 0.0f);
gl.glVertex3f(-1.0f, -1.0f, 1.0f);
gl.glEnd();
// +Z Face
textures[POS_Z].bind(gl);
gl.glBegin(GL_QUADS);
gl.glTexCoord2f(1.0f, 1.0f);
gl.glVertex3f(-1.0f, -1.0f, 1.0f);
gl.glTexCoord2f(0.0f, 1.0f);
gl.glVertex3f(1.0f, -1.0f, 1.0f);
gl.glTexCoord2f(0.0f, 0.0f);
gl.glVertex3f(1.0f, 1.0f, 1.0f);
gl.glTexCoord2f(1.0f, 0.0f);
gl.glVertex3f(-1.0f, 1.0f, 1.0f);
gl.glEnd();
// -Z Face
textures[NEG_Z].bind(gl);
gl.glBegin(GL_QUADS);
gl.glTexCoord2f(0.0f, 1.0f);
gl.glVertex3f(-1.0f, -1.0f, -1.0f);
gl.glTexCoord2f(0.0f, 0.0f);
gl.glVertex3f(-1.0f, 1.0f, -1.0f);
gl.glTexCoord2f(1.0f, 0.0f);
gl.glVertex3f(1.0f, 1.0f, -1.0f);
gl.glTexCoord2f(1.0f, 1.0f);
gl.glVertex3f(1.0f, -1.0f, -1.0f);
gl.glEnd();
}
}