package jo.util.lwjgl.win; import java.awt.Canvas; import java.awt.Dimension; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.event.MouseWheelEvent; import java.awt.event.MouseWheelListener; import java.nio.FloatBuffer; import java.nio.IntBuffer; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; import jo.util.jgl.enm.JGLColorMaterialFace; import jo.util.jgl.enm.JGLColorMaterialMode; import jo.util.jgl.enm.JGLFogMode; import jo.util.jgl.obj.JGLScene; import jo.vecmath.Point3f; import jo.vecmath.logic.Color4fLogic; import jo.vecmath.logic.Matrix4fLogic; import org.lwjgl.BufferUtils; import org.lwjgl.LWJGLException; import org.lwjgl.input.Keyboard; import org.lwjgl.input.Mouse; import org.lwjgl.opengl.Display; import org.lwjgl.opengl.GL11; import org.lwjgl.util.glu.GLU; @SuppressWarnings("serial") public class JGLCanvas extends Canvas { private JGLScene mScene; private Point3f mEyeRay; private final IntBuffer mIB16; //private int mViewportX; //private int mViewportBottom; private int mWidth; private int mHeight; private boolean mCloseRequested = false; private AtomicReference<Dimension> mNewCanvasSize; private boolean[] mMouseState; private final List<MouseListener> mMouseListeners; private final List<MouseMotionListener> mMouseMotionListeners; private final List<MouseWheelListener> mMouseWheelListeners; private final List<KeyListener> mKeyListeners; public JGLCanvas() { this.mNewCanvasSize = new AtomicReference<>(); this.mMouseListeners = new ArrayList<>(); this.mMouseWheelListeners = new ArrayList<>(); this.mMouseMotionListeners = new ArrayList<>(); this.mKeyListeners = new ArrayList<>(); mIB16 = BufferUtils.createIntBuffer(16); addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { mNewCanvasSize.set(getSize()); } }); } /** * <p> * Queries the current view port size & position and updates all related * internal state.</p> * * <p> * It is important that the internal state matches the OpenGL viewport or * clipping won't work correctly.</p> * * <p> * This method should only be called when the viewport size has changed. It * can have negative impact on performance to call every frame.</p> * * @see #getWidth() * @see #getHeight() */ public void syncViewportSize() { mIB16.clear(); GL11.glGetInteger(GL11.GL_VIEWPORT, mIB16); //mViewportX = mIB16.get(0); mWidth = mIB16.get(2); mHeight = mIB16.get(3); //mViewportBottom = mIB16.get(1) + mHeight; } private void init() { Thread t = new Thread("Render Thread") { @Override public void run() { doRenderLoop(); } }; t.start(); } private void initFog() { GL11.glEnable(GL11.GL_FOG); GL11.glFogi(GL11.GL_FOG_MODE, conv(mScene.getFogMode())); if (!Matrix4fLogic.epsilonEquals(mScene.getFogDensity(), 1)) { GL11.glFogf(GL11.GL_FOG_DENSITY, mScene.getFogDensity()); } if (!Matrix4fLogic.epsilonEquals(mScene.getFogStart(), 0)) { GL11.glFogf(GL11.GL_FOG_START, mScene.getFogStart()); } if (!Matrix4fLogic.epsilonEquals(mScene.getFogEnd(), 1)) { GL11.glFogf(GL11.GL_FOG_END, mScene.getFogEnd()); } if (!Matrix4fLogic.epsilonEquals(mScene.getFogIndex(), 0)) { GL11.glFogf(GL11.GL_FOG_INDEX, mScene.getFogIndex()); } if (mScene.getFogColor() != null) { GL11.glFog(GL11.GL_FOG_COLOR, Color4fLogic.toFloatBuffer(mScene.getFogColor())); } } private void initMaterial() { int face = conv(mScene.getColorMaterialFace()); GL11.glEnable(GL11.GL_COLOR_MATERIAL); if (mScene.getColorMaterialFace() != JGLColorMaterialFace.UNSET) { GL11.glColorMaterial(face, conv(mScene.getColorMaterialMode())); } if (mScene.getMaterialAmbient() != null) { GL11.glMaterial(face, GL11.GL_AMBIENT, Color4fLogic.toFloatBuffer(mScene.getMaterialAmbient())); } if (mScene.getMaterialDiffuse() != null) { GL11.glMaterial(face, GL11.GL_DIFFUSE, Color4fLogic.toFloatBuffer(mScene.getMaterialDiffuse())); } if (mScene.getMaterialSpecular() != null) { GL11.glMaterial(face, GL11.GL_SPECULAR, Color4fLogic.toFloatBuffer(mScene.getMaterialSpecular())); } if (mScene.getMaterialEmission() != null) { GL11.glMaterial(face, GL11.GL_EMISSION, Color4fLogic.toFloatBuffer(mScene.getMaterialEmission())); } if (mScene.getMaterialShininess() >= 0) { GL11.glMaterialf(face, GL11.GL_SHININESS, mScene.getMaterialShininess()); } } private int conv(JGLFogMode fogMode) { switch (fogMode) { case UNSET: return -1; case LINEAR: return GL11.GL_LINEAR; case EXP: return GL11.GL_EXP; case EXP2: return GL11.GL_EXP2; } return -1; } private int conv(JGLColorMaterialFace colorMaterialFace) { switch (colorMaterialFace) { case UNSET: return -1; case FRONT: return GL11.GL_FRONT; case BACK: return GL11.GL_BACK; case FRONT_AND_BACK: return GL11.GL_FRONT_AND_BACK; } return -1; } private int conv(JGLColorMaterialMode colorMaterialMode) { switch (colorMaterialMode) { case UNSET: return -1; case EMISSION: return GL11.GL_EMISSION; case AMBIENT: return GL11.GL_AMBIENT; case DIFFUSE: return GL11.GL_DIFFUSE; case SPECULAR: return GL11.GL_SPECULAR; case AMBIENT_AND_DIFFUSE: return GL11.GL_AMBIENT_AND_DIFFUSE; } return -1; } private void doRenderLoop() { try { while (!isDisplayable()) { Thread.sleep(50); } Display.setParent(this); Display.setVSyncEnabled(true); Display.create(); mMouseState = new boolean[Mouse.getButtonCount()]; for (int i = 0; i < mMouseState.length; i++) { mMouseState[i] = Mouse.isButtonDown(i); } //GL11.glsetSwapInterval(1); GL11.glClearColor(1.0f, 1.0f, 1.0f, 1.0f); //gl11.glColor3f(1.0f, 0.0f, 0.0f); GL11.glHint(GL11.GL_PERSPECTIVE_CORRECTION_HINT, GL11.GL_NICEST); GL11.glClearDepth(1.0); GL11.glLineWidth(2); GL11.glEnable(GL11.GL_DEPTH_TEST); if (mScene.getAmbientLight() != null) { GL11.glEnable(GL11.GL_LIGHTING); GL11.glLightModel(GL11.GL_LIGHT_MODEL_AMBIENT, Color4fLogic.toFloatBuffer(mScene.getAmbientLight())); } if (mScene.getColorMaterialFace() != JGLColorMaterialFace.UNSET) { initMaterial(); } if (mScene.getFogMode() != JGLFogMode.UNSET) { initFog(); } Dimension newDim; while (!Display.isCloseRequested() && !mCloseRequested) { newDim = mNewCanvasSize.getAndSet(null); if (newDim != null) { GL11.glViewport(0, 0, newDim.width, newDim.height); syncViewportSize(); } doRender(); doMouse(); doKeys(); doEye(); Display.update(); } Display.destroy(); } catch (InterruptedException | LWJGLException e) { e.printStackTrace(); } } private static final Map<Integer, Integer> KEY_LWJGL_TO_AWT = new HashMap<>(); static { KEY_LWJGL_TO_AWT.put(Keyboard.KEY_0, KeyEvent.VK_0); KEY_LWJGL_TO_AWT.put(Keyboard.KEY_1, KeyEvent.VK_1); KEY_LWJGL_TO_AWT.put(Keyboard.KEY_2, KeyEvent.VK_2); KEY_LWJGL_TO_AWT.put(Keyboard.KEY_3, KeyEvent.VK_3); KEY_LWJGL_TO_AWT.put(Keyboard.KEY_4, KeyEvent.VK_4); KEY_LWJGL_TO_AWT.put(Keyboard.KEY_5, KeyEvent.VK_5); KEY_LWJGL_TO_AWT.put(Keyboard.KEY_6, KeyEvent.VK_6); KEY_LWJGL_TO_AWT.put(Keyboard.KEY_7, KeyEvent.VK_7); KEY_LWJGL_TO_AWT.put(Keyboard.KEY_8, KeyEvent.VK_8); KEY_LWJGL_TO_AWT.put(Keyboard.KEY_9, KeyEvent.VK_9); KEY_LWJGL_TO_AWT.put(Keyboard.KEY_A, KeyEvent.VK_A); KEY_LWJGL_TO_AWT.put(Keyboard.KEY_B, KeyEvent.VK_B); KEY_LWJGL_TO_AWT.put(Keyboard.KEY_C, KeyEvent.VK_C); KEY_LWJGL_TO_AWT.put(Keyboard.KEY_D, KeyEvent.VK_D); KEY_LWJGL_TO_AWT.put(Keyboard.KEY_E, KeyEvent.VK_E); KEY_LWJGL_TO_AWT.put(Keyboard.KEY_F, KeyEvent.VK_F); KEY_LWJGL_TO_AWT.put(Keyboard.KEY_G, KeyEvent.VK_G); KEY_LWJGL_TO_AWT.put(Keyboard.KEY_H, KeyEvent.VK_H); KEY_LWJGL_TO_AWT.put(Keyboard.KEY_I, KeyEvent.VK_I); KEY_LWJGL_TO_AWT.put(Keyboard.KEY_J, KeyEvent.VK_J); KEY_LWJGL_TO_AWT.put(Keyboard.KEY_K, KeyEvent.VK_K); KEY_LWJGL_TO_AWT.put(Keyboard.KEY_L, KeyEvent.VK_L); KEY_LWJGL_TO_AWT.put(Keyboard.KEY_M, KeyEvent.VK_M); KEY_LWJGL_TO_AWT.put(Keyboard.KEY_N, KeyEvent.VK_N); KEY_LWJGL_TO_AWT.put(Keyboard.KEY_O, KeyEvent.VK_O); KEY_LWJGL_TO_AWT.put(Keyboard.KEY_P, KeyEvent.VK_P); KEY_LWJGL_TO_AWT.put(Keyboard.KEY_Q, KeyEvent.VK_Q); KEY_LWJGL_TO_AWT.put(Keyboard.KEY_R, KeyEvent.VK_R); KEY_LWJGL_TO_AWT.put(Keyboard.KEY_S, KeyEvent.VK_S); KEY_LWJGL_TO_AWT.put(Keyboard.KEY_T, KeyEvent.VK_T); KEY_LWJGL_TO_AWT.put(Keyboard.KEY_U, KeyEvent.VK_U); KEY_LWJGL_TO_AWT.put(Keyboard.KEY_V, KeyEvent.VK_V); KEY_LWJGL_TO_AWT.put(Keyboard.KEY_W, KeyEvent.VK_W); KEY_LWJGL_TO_AWT.put(Keyboard.KEY_X, KeyEvent.VK_X); KEY_LWJGL_TO_AWT.put(Keyboard.KEY_Y, KeyEvent.VK_Y); KEY_LWJGL_TO_AWT.put(Keyboard.KEY_Z, KeyEvent.VK_Z); } private int mModifiers = 0; private void doKeys() { while (Keyboard.next()) { char eventChar = Keyboard.getEventCharacter(); int eventKey = Keyboard.getEventKey(); long eventTick = Keyboard.getEventNanoseconds(); boolean eventState = Keyboard.getEventKeyState(); //System.out.println("doKeys("+eventKey+")"); if (eventKey == Keyboard.KEY_LSHIFT) { if (eventState) { mModifiers |= KeyEvent.VK_SHIFT; } else { mModifiers &= ~KeyEvent.VK_SHIFT; } } else if (eventKey == Keyboard.KEY_RSHIFT) { if (eventState) { mModifiers |= KeyEvent.VK_SHIFT; } else { mModifiers &= ~KeyEvent.VK_SHIFT; } } else if (eventKey == Keyboard.KEY_LCONTROL) { if (eventState) { mModifiers |= KeyEvent.VK_CONTROL; } else { mModifiers &= ~KeyEvent.VK_CONTROL; } } else if (eventKey == Keyboard.KEY_RCONTROL) { if (eventState) { mModifiers |= KeyEvent.VK_CONTROL; } else { mModifiers &= ~KeyEvent.VK_CONTROL; } } else if (eventKey == Keyboard.KEY_LMENU) { if (eventState) { mModifiers |= KeyEvent.VK_ALT; } else { mModifiers &= ~KeyEvent.VK_ALT; } } else if (eventKey == Keyboard.KEY_RMENU) { if (eventState) { mModifiers |= KeyEvent.VK_ALT; } else { mModifiers &= ~KeyEvent.VK_ALT; } } if (KEY_LWJGL_TO_AWT.containsKey(eventKey)) { eventKey = KEY_LWJGL_TO_AWT.get(eventKey); } KeyEvent e = new KeyEvent(this, eventState ? KeyEvent.KEY_PRESSED : KeyEvent.KEY_RELEASED, eventTick, mModifiers, eventKey, eventChar); fireKeyEvent(e); } } private void doEye() { int mouseX = Mouse.getX(); int mouseY = Mouse.getY(); FloatBuffer modelview = BufferUtils.createFloatBuffer(16); GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, modelview); FloatBuffer projection = BufferUtils.createFloatBuffer(16); GL11.glGetFloat(GL11.GL_PROJECTION_MATRIX, projection); IntBuffer viewport = BufferUtils.createIntBuffer(16); GL11.glGetInteger(GL11.GL_VIEWPORT, viewport); float winX = mouseX; float winY = viewport.get(3) - mouseY; FloatBuffer winZBuffer = BufferUtils.createFloatBuffer(1); GL11.glReadPixels(mouseX, mouseY, 1, 1, GL11.GL_DEPTH_COMPONENT, GL11.GL_FLOAT, winZBuffer); float winZ = winZBuffer.get(0); FloatBuffer pos = BufferUtils.createFloatBuffer(3); GLU.gluUnProject(winX, winY, winZ, modelview, projection, viewport, pos); mEyeRay = new Point3f(pos.get(0), pos.get(1), pos.get(2)); } private void doMouse() { while (Mouse.next()) { int button = Mouse.getEventButton(); boolean buttonState = Mouse.getEventButtonState(); int dWheel = Mouse.getDWheel(); int dX = Mouse.getEventDX(); int dY = Mouse.getEventDY(); long nanoseconds = Mouse.getEventNanoseconds(); int x = Mouse.getEventX(); int y = Mouse.getEventY(); int modifiers = 0; if (button >= 0) { int jButton = (button == 0) ? MouseEvent.BUTTON1 : (button == 1) ? MouseEvent.BUTTON2 : MouseEvent.BUTTON3; if (mMouseState[button] != buttonState) { //System.out.println("Button="+button+", state="+buttonState+", x="+x+", y="+y); if (buttonState) { MouseEvent event = new MouseEvent(this, MouseEvent.MOUSE_PRESSED, nanoseconds, modifiers, x, y, 1, false, jButton); fireMouseEvent(event); } else { MouseEvent event = new MouseEvent(this, MouseEvent.MOUSE_RELEASED, nanoseconds, modifiers, x, y, 1, false, jButton); fireMouseEvent(event); } mMouseState[button] = buttonState; } } if ((dX > 0) || (dY > 0)) { int jButton = 0; if (mMouseState[0]) { jButton |= MouseEvent.BUTTON1; } if (mMouseState[1]) { jButton |= MouseEvent.BUTTON2; } if (mMouseState[2]) { jButton |= MouseEvent.BUTTON3; } if (jButton != 0) { MouseEvent event = new MouseEvent(this, MouseEvent.MOUSE_DRAGGED, nanoseconds, modifiers, x, y, 1, false, jButton); fireMouseMoveEvent(event); } else { MouseEvent event = new MouseEvent(this, MouseEvent.MOUSE_MOVED, nanoseconds, modifiers, x, y, 1, false, jButton); fireMouseMoveEvent(event); } } if (dWheel != 0) { MouseWheelEvent event = new MouseWheelEvent(this, MouseEvent.MOUSE_WHEEL, nanoseconds, modifiers, x, y, 1, false, MouseWheelEvent.WHEEL_UNIT_SCROLL, dWheel, dWheel / 120); fireMouseWheelEvent(event); } } } private void doRender() { for (Runnable r : mScene.getBetweenRenderers()) { r.run(); } DrawLogic.draw(mWidth, mHeight, System.currentTimeMillis(), mScene); } public JGLScene getScene() { return mScene; } public void setScene(JGLScene scene) { if ((mScene != null) && (mScene != scene)) { throw new IllegalArgumentException("Cannot set a new scene!"); } mScene = scene; if (mScene != null) { init(); } } public boolean isCloseRequested() { return mCloseRequested; } public void setCloseRequested(boolean closeRequested) { mCloseRequested = closeRequested; } @Override public synchronized void addMouseListener(MouseListener l) { if (System.getProperty("os.name").contains("Mac")) { super.addMouseListener(l); } else { mMouseListeners.add(l); } } @Override public synchronized void addMouseMotionListener(MouseMotionListener l) { if (System.getProperty("os.name").contains("Mac")) { super.addMouseMotionListener(l); } else { mMouseMotionListeners.add(l); } } @Override public synchronized void addMouseWheelListener(MouseWheelListener l) { if (System.getProperty("os.name").contains("Mac")) { super.addMouseWheelListener(l); } else { mMouseWheelListeners.add(l); } } @Override public synchronized void removeMouseListener(MouseListener l) { mMouseListeners.remove(l); } @Override public synchronized void removeMouseMotionListener(MouseMotionListener l) { mMouseMotionListeners.remove(l); } @Override public synchronized void removeMouseWheelListener(MouseWheelListener l) { mMouseWheelListeners.remove(l); } @Override public synchronized void addKeyListener(KeyListener l) { mKeyListeners.add(l); super.addKeyListener(l); } @Override public synchronized void removeKeyListener(KeyListener l) { mKeyListeners.remove(l); } private void fireMouseEvent(MouseEvent e) { for (MouseListener l : mMouseListeners) { if (e.getID() == MouseEvent.MOUSE_PRESSED) { l.mousePressed(e); } else if (e.getID() == MouseEvent.MOUSE_RELEASED) { l.mouseReleased(e); } } } private void fireMouseMoveEvent(MouseEvent e) { for (MouseMotionListener l : mMouseMotionListeners) { if (e.getID() == MouseEvent.MOUSE_MOVED) { l.mouseMoved(e); } else if (e.getID() == MouseEvent.MOUSE_DRAGGED) { l.mouseDragged(e); } } } private void fireMouseWheelEvent(MouseWheelEvent e) { for (MouseWheelListener l : mMouseWheelListeners) { if (e.getID() == MouseEvent.MOUSE_WHEEL) { l.mouseWheelMoved(e); } } } private void fireKeyEvent(KeyEvent e) { for (KeyListener l : mKeyListeners) { if (e.getID() == KeyEvent.KEY_PRESSED) { l.keyPressed(e); } else if (e.getID() == KeyEvent.KEY_RELEASED) { l.keyReleased(e); } else if (e.getID() == KeyEvent.KEY_TYPED) { l.keyTyped(e); } } } public Point3f getEyeRay() { return mEyeRay; } public void setEyeRay(Point3f eyeRay) { mEyeRay = eyeRay; } }