package de.gaalop.visualizer.engines.lwjgl;
import de.gaalop.visualizer.PointCloud;
import de.gaalop.visualizer.PointClouds;
import de.gaalop.visualizer.Rendering;
import de.gaalop.visualizer.engines.lwjgl.recording.GIFRecorder;
import de.gaalop.visualizer.engines.lwjgl.recording.Recorder;
import java.util.HashMap;
import java.util.HashSet;
import org.lwjgl.LWJGLException;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.glu.GLU;
/**
* Implements a rendering engine based on LwJgl
* @author Christian Steinmetz
*/
public abstract class LwJglRenderingEngine extends RenderingEngine {
private double near = 0.1, far = 30;
// Camera information
private Vec3f camPos = new Vec3f(0.0f, 2.0f, -10.0f); // camera position
private Vec3f camDir = new Vec3f(0.0f, 0.0f, 1.0f); // camera lookat (always Z)
private Vec3f camUp = new Vec3f(0.0f, 1.0f, 0.0f); // camera up direction (always Y)
private float camAngleX = 0.0f, camAngleY = 0.0f; // camera angles
// Mouse information
private int mouseX, mouseY, mouseButton;
private float mouseSensitivy = 1.0f;
private boolean[] buttonDown = new boolean[]{false, false, false};
private static final int STATE_DOWN = 1;
private static final int STATE_UP = 2;
protected Rendering rendering;
public Recorder recorder;
private boolean changed = false;
private boolean firstFrame = true;
private int list = -1;
public LwJglRenderingEngine(String lwJglNativePath, Rendering rendering) {
this.rendering = rendering;
System.setProperty("org.lwjgl.librarypath", lwJglNativePath);
}
/**
* Starts the lwjgl engine and shows a window, where the point clouds are rendered
*/
public void startEngine() {
int width = 800;
int height = 600;
try {
Display.setDisplayMode(new DisplayMode(width, height));
Display.setFullscreen(false);
Display.setTitle("Gaalop Visualization Window");
Display.create();
} catch (LWJGLException e) {
e.printStackTrace();
System.exit(0);
}
GL11.glEnable(GL11.GL_DEPTH_TEST);
GL11.glShadeModel(GL11.GL_SMOOTH);
changeSize(width, height);
GL11.glDisable(GL11.GL_LIGHTING);
// init OpenGL
GL11.glViewport(0, 0, width, height);
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glLoadIdentity();
GLU.gluPerspective((float) 65.0, (float) width / (float) height, (float) 0.1, 100);
GL11.glMatrixMode(GL11.GL_MODELVIEW);
}
@Override
public void run() {
startEngine();
//long start = System.currentTimeMillis();
while (!Display.isCloseRequested()) {
//System.out.println(System.currentTimeMillis()-start);
//start = System.currentTimeMillis();
if (rendering.isNewDataSetAvailable()) {
if (list != -1) GL11.glDeleteLists(list, 1);
list = GL11.glGenLists(1);
GL11.glNewList(list, GL11.GL_COMPILE);
draw(rendering.getDataSet(), rendering.getVisibleObjects(), rendering.getLoadedPointClouds());
GL11.glEndList();
changed = true;
}
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT); // clear the screen
GL11.glLoadIdentity(); // apply camPos before rotation
GL11.glTranslatef(0.0f, 0.0f, -5.0f);
// draw
GLU.gluLookAt(camPos.x, camPos.y, camPos.z, // Position
camPos.x + camDir.x, camPos.y + camDir.y, camPos.z + camDir.z, // Lookat
camUp.x, camUp.y, camUp.z); // Up-direction
// apply rotation
GL11.glRotatef(camAngleX, 0, 1, 0); // window x axis rotates around up vector
GL11.glRotatef(camAngleY, 1, 0, 0); // window y axis rotates around x
//Render the scene
if (list != -1) GL11.glCallList(list);
pollInput();
Display.update();
if (recorder != null) {
if (changed || firstFrame) {
recorder.makeScreenshot();
changed = false;
}
firstFrame = false;
Display.sync(25); // cap fps to 60fps
}
else
Display.sync(60);
}
Display.destroy();
}
/**
* Implements the action which is done, when mouse is moved on the lwjgl window
* @param x The x coordinate of the position of the mouse
* @param y The y coordinate of the position of the mouse
*/
public void mouseMoved(int x, int y) {
switch (mouseButton) {
// 1 => rotate
case 1:
// update angle with relative movement
camAngleX = fmod(camAngleX + (x - mouseX) * mouseSensitivy, 360.0f);
camAngleY -= (y - mouseY) * mouseSensitivy;
// limit y angle by 85 degree
if (camAngleY > 85) {
camAngleY = 85;
}
if (camAngleY < -85) {
camAngleY = -85;
}
changed = true;
break;
// 2 => zoom
case 2:
camPos.z -= 0.1f * (y - mouseY) * mouseSensitivy;
changed = true;
break;
// 3 => translate
case 3:
// update camPos
camPos.x += 0.1f * (x - mouseX) * mouseSensitivy;
camPos.y -= 0.1f * (y - mouseY) * mouseSensitivy;
changed = true;
break;
default:
break;
}
// update mouse for next relative movement
mouseX = x;
mouseY = y;
}
/**
* Implements the action which is done, when a mouse button is pressed on the lwjgl window
* @param button The button which is pressed
* @param state The state of the button
* @param x The x coordinate of the position of the mouse
* @param y The y coordinate of the position of the mouse
*/
void mouseAction(int button, int state, int x, int y) {
switch (button) {
case 0:
if (state == STATE_DOWN) {
mouseButton = 1;
mouseX = x;
mouseY = y;
} else {
mouseButton = 0;
}
break;
case 1:
if (state == STATE_DOWN) {
mouseButton = 3;
mouseX = x;
mouseY = y;
} else {
mouseButton = 0;
}
break;
case 2:
if (state == STATE_DOWN) {
mouseButton = 2;
mouseX = x;
mouseY = y;
} else {
mouseButton = 0;
}
break;
}
}
/**
* Poll all inputs of the keyboard<br>
* F3:Start recording
* F4:Stop recording
* ESC: Close window
*/
private void pollInput() {
if (Keyboard.isKeyDown(Keyboard.KEY_F3)) {
//Start recording
if (recorder == null) {
recorder = new GIFRecorder();
recorder.startRecording();
}
}
if (Keyboard.isKeyDown(Keyboard.KEY_F4)) {
//Stop recording
if (recorder != null) {
recorder.stopRecording();
recorder = null;
}
}
if (Keyboard.isKeyDown(Keyboard.KEY_ESCAPE)) {
if (recorder != null)
recorder.stopRecording();
Display.destroy();
System.exit(0);
}
int x = Mouse.getX();
int y = Mouse.getY();
for (int button = 0; button <= 2; button++) {
if (Mouse.isButtonDown(button)) {
if (!buttonDown[button]) {
mouseAction(button, STATE_DOWN, x, y);
} else {
mouseMoved(x, y);
}
buttonDown[button] = true;
} else {
if (buttonDown[button]) {
mouseAction(button, STATE_UP, x, y);
}
buttonDown[button] = false;
}
}
}
/**
* Changes the size of the lwjgl window
* @param w The new width of the lwjgl window
* @param h The new height of the lwjgl window
*/
private void changeSize(float w, float h) {
// Prevent a division by zero, when window is too short
if (h == 0) {
h = 1;
}
float wRatio = 1.0f * w / h;
// Reset the coordinate system before modifying
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glLoadIdentity();
// Set the viewport to be the entire window
GL11.glViewport(0, 0, (int) w, (int) h);
// Set the correct perspective.
GLU.gluPerspective(45.0f, wRatio, (float) near, (float) far);
GL11.glMatrixMode(GL11.GL_MODELVIEW);
GL11.glLoadIdentity();
GLU.gluLookAt(camPos.x, camPos.y, camPos.z, // Position
camPos.x + camDir.x, camPos.y + camDir.y, camPos.z + camDir.z, // Lookat
camUp.x, camUp.y, camUp.z); // Up-direction}
}
/**
* Implements a float modulo
* @param value The float value
* @param modulo The modulo value
* @return The result of the modulo operation on the float value
*/
private float fmod(float value, float modulo) {
while (value < 0) {
value += modulo;
}
while (value > modulo) {
value -= modulo;
}
return value;
}
/**
* Draws the concrete scene
* @param clouds The point clouds
* @param visibleObjects The visible point clouds
*/
public abstract void draw(HashMap<String, PointCloud> clouds, HashSet<String> visibleObjects, PointClouds loadedClouds);
}