/** * Copyright (C) 2011 JogAmp Community. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package com.jogamp.opengl.test.junit.jogl.demos.es2; import com.jogamp.common.nio.Buffers; import com.jogamp.newt.Window; import com.jogamp.newt.event.GestureHandler; import com.jogamp.newt.event.KeyAdapter; import com.jogamp.newt.event.KeyEvent; import com.jogamp.newt.event.KeyListener; import com.jogamp.newt.event.MouseEvent; import com.jogamp.newt.event.MouseListener; import com.jogamp.newt.event.PinchToZoomGesture; import com.jogamp.newt.event.GestureHandler.GestureEvent; import com.jogamp.opengl.GLRendererQuirks; import com.jogamp.opengl.JoglVersion; import com.jogamp.opengl.math.FloatUtil; import com.jogamp.opengl.math.Quaternion; import com.jogamp.opengl.math.VectorUtil; import com.jogamp.opengl.test.junit.jogl.demos.GearsObject; import com.jogamp.opengl.util.CustomGLEventListener; import com.jogamp.opengl.util.PMVMatrix; import com.jogamp.opengl.util.TileRendererBase; import com.jogamp.opengl.util.glsl.ShaderCode; import com.jogamp.opengl.util.glsl.ShaderProgram; import com.jogamp.opengl.util.glsl.ShaderState; import com.jogamp.opengl.util.stereo.EyeParameter; import com.jogamp.opengl.util.stereo.ViewerPose; import com.jogamp.opengl.util.stereo.StereoGLEventListener; import java.nio.FloatBuffer; import com.jogamp.nativewindow.NativeWindow; import com.jogamp.opengl.GL; import com.jogamp.opengl.GL2ES2; import com.jogamp.opengl.GLAnimatorControl; import com.jogamp.opengl.GLAutoDrawable; import com.jogamp.opengl.GLProfile; import com.jogamp.opengl.GLUniformData; import com.jogamp.opengl.fixedfunc.GLMatrixFunc; /** * GearsES2.java <BR> * @author Brian Paul (converted to Java by Ron Cemer and Sven Gothel) <P> */ public class GearsES2 implements StereoGLEventListener, TileRendererBase.TileRendererListener { private final FloatBuffer lightPos = Buffers.newDirectFloatBuffer( new float[] { 5.0f, 5.0f, 10.0f } ); private ShaderState st = null; private PMVMatrix pmvMatrix = null; private GLUniformData pmvMatrixUniform = null; private GLUniformData colorU = null; private float view_rotx = 20.0f, view_roty = 30.0f; private boolean flipVerticalInGLOrientation = false; private final boolean customRendering = false; private final float view_rotz = 0.0f; private float panX = 0.0f, panY = 0.0f, panZ=0.0f; private volatile GearsObjectES2 gear1=null, gear2=null, gear3=null; private GearsES2 sharedGears = null; private Object syncObjects = null; private boolean useMappedBuffers = false; private boolean validateBuffers = false; private volatile boolean usesSharedGears = false; private FloatBuffer gear1Color=GearsObject.red, gear2Color=GearsObject.green, gear3Color=GearsObject.blue; private float angle = 0.0f; private int swapInterval = 0; // private MouseListener gearsMouse = new TraceMouseAdapter(new GearsMouseAdapter()); public MouseListener gearsMouse = new GearsMouseAdapter(); public KeyListener gearsKeys = new GearsKeyAdapter(); private TileRendererBase tileRendererInUse = null; private boolean doRotateBeforePrinting; private boolean doRotate = true; private boolean ignoreFocus = false; private float[] clearColor = null; private boolean clearBuffers = true; private boolean verbose = true; private volatile boolean isInit = false; private PinchToZoomGesture pinchToZoomGesture = null; public GearsES2(final int swapInterval) { this.swapInterval = swapInterval; } public GearsES2() { this.swapInterval = 1; } @Override public void addTileRendererNotify(final TileRendererBase tr) { tileRendererInUse = tr; doRotateBeforePrinting = doRotate; setDoRotation(false); } @Override public void removeTileRendererNotify(final TileRendererBase tr) { tileRendererInUse = null; setDoRotation(doRotateBeforePrinting); } @Override public void startTileRendering(final TileRendererBase tr) { System.err.println("GearsES2.startTileRendering: "+sid()+""+tr); } @Override public void endTileRendering(final TileRendererBase tr) { System.err.println("GearsES2.endTileRendering: "+sid()+""+tr); } public void setIgnoreFocus(final boolean v) { ignoreFocus = v; } public void setDoRotation(final boolean rotate) { this.doRotate = rotate; } public void setClearBuffers(final boolean v) { clearBuffers = v; } public void setVerbose(final boolean v) { verbose = v; } public void setFlipVerticalInGLOrientation(final boolean v) { flipVerticalInGLOrientation=v; } /** float[4] */ public void setClearColor(final float[] clearColor) { this.clearColor = clearColor; } public void setGearsColors(final FloatBuffer gear1Color, final FloatBuffer gear2Color, final FloatBuffer gear3Color) { this.gear1Color = gear1Color; this.gear2Color = gear2Color; this.gear3Color = gear3Color; } public void setSharedGears(final GearsES2 shared) { sharedGears = shared; } public void setSyncObjects(final Object sync) { syncObjects = sync; } /** * @return gear1 */ public GearsObjectES2 getGear1() { return gear1; } /** * @return gear2 */ public GearsObjectES2 getGear2() { return gear2; } /** * @return gear3 */ public GearsObjectES2 getGear3() { return gear3; } public boolean usesSharedGears() { return usesSharedGears; } public void setUseMappedBuffers(final boolean v) { useMappedBuffers = v; } public void setValidateBuffers(final boolean v) { validateBuffers = v; } public PMVMatrix getPMVMatrix() { return pmvMatrix; } private static final int TIME_OUT = 2000; // 2s private static final int POLL_DIVIDER = 20; // TO/20 private static final int TIME_SLICE = TIME_OUT / POLL_DIVIDER ; /** * @return True if this GLEventListener became initialized within TIME_OUT 2s */ public boolean waitForInit(final boolean initialized) throws InterruptedException { int wait; for (wait=0; wait<POLL_DIVIDER && initialized != isInit ; wait++) { Thread.sleep(TIME_SLICE); } return wait<POLL_DIVIDER; } private final String sid() { return "0x"+Integer.toHexString(hashCode()); } @Override public void init(final GLAutoDrawable drawable) { if(null != sharedGears && !sharedGears.isInit() ) { System.err.println(Thread.currentThread()+" GearsES2.init.0 "+sid()+": pending shared Gears .. re-init later XXXXX"); drawable.setGLEventListenerInitState(this, false); return; } final GL2ES2 gl = drawable.getGL().getGL2ES2(); if(verbose) { System.err.println(Thread.currentThread()+" GearsES2.init.0 "+sid()+": tileRendererInUse "+tileRendererInUse+", "+this); System.err.println("GearsES2 init "+sid()+" on "+Thread.currentThread()); System.err.println("Chosen GLCapabilities: " + drawable.getChosenGLCapabilities()); System.err.println("INIT GL IS: " + gl.getClass().getName()); System.err.println(JoglVersion.getGLStrings(gl, null, false).toString()); } if( !gl.hasGLSL() ) { System.err.println("No GLSL available, no rendering."); return; } st = new ShaderState(); // st.setVerbose(true); final ShaderCode vp0 = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, this.getClass(), "shader", "shader/bin", "gears", true); final ShaderCode fp0 = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, this.getClass(), "shader", "shader/bin", "gears", true); vp0.defaultShaderCustomization(gl, true, true); fp0.defaultShaderCustomization(gl, true, true); final ShaderProgram sp0 = new ShaderProgram(); sp0.add(gl, vp0, System.err); sp0.add(gl, fp0, System.err); st.attachShaderProgram(gl, sp0, true); // Use debug pipeline // drawable.setGL(new DebugGL(drawable.getGL())); pmvMatrix = new PMVMatrix(); st.attachObject("pmvMatrix", pmvMatrix); pmvMatrixUniform = new GLUniformData("pmvMatrix", 4, 4, pmvMatrix.glGetPMvMvitMatrixf()); // P, Mv, Mvi and Mvit st.ownUniform(pmvMatrixUniform); st.uniform(gl, pmvMatrixUniform); final GLUniformData lightU = new GLUniformData("lightPos", 3, lightPos); st.ownUniform(lightU); st.uniform(gl, lightU); colorU = new GLUniformData("color", 4, GearsObject.red); st.ownUniform(colorU); st.uniform(gl, colorU); if( null != sharedGears ) { gear1 = new GearsObjectES2(sharedGears.getGear1(), st, pmvMatrix, pmvMatrixUniform, colorU); gear2 = new GearsObjectES2(sharedGears.getGear2(), st, pmvMatrix, pmvMatrixUniform, colorU); gear3 = new GearsObjectES2(sharedGears.getGear3(), st, pmvMatrix, pmvMatrixUniform, colorU); usesSharedGears = true; if(verbose) { System.err.println("gear1 "+sid()+" created w/ share: "+sharedGears.getGear1()+" -> "+gear1); System.err.println("gear2 "+sid()+" created w/ share: "+sharedGears.getGear2()+" -> "+gear2); System.err.println("gear3 "+sid()+" created w/ share: "+sharedGears.getGear3()+" -> "+gear3); } if( gl.getContext().hasRendererQuirk(GLRendererQuirks.NeedSharedObjectSync) ) { syncObjects = sharedGears; System.err.println("Shared GearsES2: Synchronized Objects due to quirk "+GLRendererQuirks.toString(GLRendererQuirks.NeedSharedObjectSync)); } else if( null == syncObjects ) { syncObjects = new Object(); System.err.println("Shared GearsES2: Unsynchronized Objects"); } } else { gear1 = new GearsObjectES2(gl, useMappedBuffers, st, gear1Color, 1.0f, 4.0f, 1.0f, 20, 0.7f, pmvMatrix, pmvMatrixUniform, colorU, validateBuffers); if(verbose) { System.err.println("gear1 "+sid()+" created: "+gear1); } gear2 = new GearsObjectES2(gl, useMappedBuffers, st, gear2Color, 0.5f, 2.0f, 2.0f, 10, 0.7f, pmvMatrix, pmvMatrixUniform, colorU, validateBuffers); if(verbose) { System.err.println("gear2 "+sid()+" created: "+gear2); } gear3 = new GearsObjectES2(gl, useMappedBuffers, st, gear3Color, 1.3f, 2.0f, 0.5f, 10, 0.7f, pmvMatrix, pmvMatrixUniform, colorU, validateBuffers); if(verbose) { System.err.println("gear3 "+sid()+" created: "+gear2); } if( null == syncObjects ) { syncObjects = new Object(); } } final Object upstreamWidget = drawable.getUpstreamWidget(); if (upstreamWidget instanceof Window) { final Window window = (Window) upstreamWidget; window.addMouseListener(gearsMouse); window.addKeyListener(gearsKeys); window.addGestureListener(pinchToZoomListener); pinchToZoomGesture = new PinchToZoomGesture(drawable.getNativeSurface(), false); window.addGestureHandler(pinchToZoomGesture); } else if (GLProfile.isAWTAvailable() && upstreamWidget instanceof java.awt.Component) { final java.awt.Component comp = (java.awt.Component) upstreamWidget; new com.jogamp.newt.event.awt.AWTMouseAdapter(gearsMouse, drawable).addTo(comp); new com.jogamp.newt.event.awt.AWTKeyAdapter(gearsKeys, drawable).addTo(comp); } st.useProgram(gl, false); gl.glFinish(); // make sure .. for shared context (impacts OSX 10.9) isInit = true; if(verbose) { System.err.println(Thread.currentThread()+" GearsES2.init.X "+sid()+" FIN "+this); } } public final boolean isInit() { return isInit; } private final GestureHandler.GestureListener pinchToZoomListener = new GestureHandler.GestureListener() { @Override public void gestureDetected(final GestureEvent gh) { final PinchToZoomGesture.ZoomEvent ze = (PinchToZoomGesture.ZoomEvent) gh; final float zoom = ze.getZoom(); // * ( ze.getTrigger().getPointerCount() - 1 ); <- too much .. panZ = zoom * 30f - 30f; // [0 .. 2] -> [-30f .. 30f] } }; @Override public void reshape(final GLAutoDrawable glad, final int x, final int y, final int width, final int height) { if( !isInit ) { return; } final GL2ES2 gl = glad.getGL().getGL2ES2(); gl.setSwapInterval(swapInterval); reshapeImpl(gl, x, y, width, height, width, height); } @Override public void reshapeTile(final TileRendererBase tr, final int tileX, final int tileY, final int tileWidth, final int tileHeight, final int imageWidth, final int imageHeight) { if( !isInit ) { return; } final GL2ES2 gl = tr.getAttachedDrawable().getGL().getGL2ES2(); gl.setSwapInterval(0); reshapeImpl(gl, tileX, tileY, tileWidth, tileHeight, imageWidth, imageHeight); } private float zNear = 5f; private float zFar = 10000f; private float zViewDist = 40.0f; public void setZ(final float zNear, final float zFar, final float zViewDist) { this.zNear = zNear; this.zFar = zFar; this.zViewDist = zViewDist; } void reshapeImpl(final GL2ES2 gl, final int tileX, final int tileY, final int tileWidth, final int tileHeight, final int imageWidth, final int imageHeight) { final boolean msaa = gl.getContext().getGLDrawable().getChosenGLCapabilities().getSampleBuffers(); if(verbose) { System.err.println(Thread.currentThread()+" GearsES2.reshape "+sid()+" "+tileX+"/"+tileY+" "+tileWidth+"x"+tileHeight+" of "+imageWidth+"x"+imageHeight+", swapInterval "+swapInterval+", drawable 0x"+Long.toHexString(gl.getContext().getGLDrawable().getHandle())+", msaa "+msaa+", tileRendererInUse "+tileRendererInUse); } if( !gl.hasGLSL() ) { return; } // compute projection parameters 'normal' float left, right, bottom, top; if( imageHeight > imageWidth ) { final float a = (float)imageHeight / (float)imageWidth; left = -1.0f; right = 1.0f; bottom = -a; top = a; } else { final float a = (float)imageWidth / (float)imageHeight; left = -a; right = a; bottom = -1.0f; top = 1.0f; } final float w = right - left; final float h = top - bottom; // compute projection parameters 'tiled' final float l = left + tileX * w / imageWidth; final float r = l + tileWidth * w / imageWidth; final float b = bottom + tileY * h / imageHeight; final float t = b + tileHeight * h / imageHeight; final float _w = r - l; final float _h = t - b; if(verbose) { System.err.println(">> GearsES2 "+sid()+", angle "+angle+", [l "+left+", r "+right+", b "+bottom+", t "+top+"] "+w+"x"+h+" -> [l "+l+", r "+r+", b "+b+", t "+t+"] "+_w+"x"+_h+", v-flip "+flipVerticalInGLOrientation); } pmvMatrix.glMatrixMode(GLMatrixFunc.GL_PROJECTION); pmvMatrix.glLoadIdentity(); if( flipVerticalInGLOrientation && gl.getContext().getGLDrawable().isGLOriented() ) { pmvMatrix.glScalef(1f, -1f, 1f); } pmvMatrix.glFrustumf(l, r, b, t, zNear, zFar); pmvMatrix.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); pmvMatrix.glLoadIdentity(); pmvMatrix.glTranslatef(0.0f, 0.0f, -zViewDist); st.useProgram(gl, true); st.uniform(gl, pmvMatrixUniform); st.useProgram(gl, false); } // private boolean useAndroidDebug = false; private final float[] mat4Tmp1 = new float[16]; private final float[] mat4Tmp2 = new float[16]; private final float[] vec3Tmp1 = new float[3]; private final float[] vec3Tmp2 = new float[3]; private final float[] vec3Tmp3 = new float[3]; private static final float[] vec3ScalePos = new float[] { 20f, 20f, 20f }; @Override public void reshapeForEye(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height, final EyeParameter eyeParam, final ViewerPose viewerPose) { final GL2ES2 gl = drawable.getGL().getGL2ES2(); pmvMatrix.glMatrixMode(GLMatrixFunc.GL_PROJECTION); final float[] mat4Projection = FloatUtil.makePerspective(mat4Tmp1, 0, true, eyeParam.fovhv, zNear, zFar); if( flipVerticalInGLOrientation && gl.getContext().getGLDrawable().isGLOriented() ) { pmvMatrix.glLoadIdentity(); pmvMatrix.glScalef(1f, -1f, 1f); pmvMatrix.glMultMatrixf(mat4Projection, 0); } else { pmvMatrix.glLoadMatrixf(mat4Projection, 0); } pmvMatrix.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); final Quaternion rollPitchYaw = new Quaternion(); // private final float eyeYaw = FloatUtil.PI; // 180 degrees in radians // rollPitchYaw.rotateByAngleY(eyeYaw); // final float[] shiftedEyePos = rollPitchYaw.rotateVector(vec3Tmp1, 0, viewerPose.position, 0); final float[] shiftedEyePos = VectorUtil.copyVec3(vec3Tmp1, 0, viewerPose.position, 0); VectorUtil.scaleVec3(shiftedEyePos, shiftedEyePos, vec3ScalePos); // amplify viewerPose position VectorUtil.addVec3(shiftedEyePos, shiftedEyePos, eyeParam.positionOffset); rollPitchYaw.mult(viewerPose.orientation); final float[] up = rollPitchYaw.rotateVector(vec3Tmp2, 0, VectorUtil.VEC3_UNIT_Y, 0); final float[] forward = rollPitchYaw.rotateVector(vec3Tmp3, 0, VectorUtil.VEC3_UNIT_Z_NEG, 0); final float[] center = VectorUtil.addVec3(forward, shiftedEyePos, forward); final float[] mLookAt = FloatUtil.makeLookAt(mat4Tmp1, 0, shiftedEyePos, 0, center, 0, up, 0, mat4Tmp2); final float[] mViewAdjust = FloatUtil.makeTranslation(mat4Tmp2, true, eyeParam.distNoseToPupilX, eyeParam.distMiddleToPupilY, eyeParam.eyeReliefZ); final float[] mat4Modelview = FloatUtil.multMatrix(mViewAdjust, mLookAt); pmvMatrix.glLoadMatrixf(mat4Modelview, 0); pmvMatrix.glTranslatef(0.0f, 0.0f, -zViewDist); st.useProgram(gl, true); st.uniform(gl, pmvMatrixUniform); st.useProgram(gl, false); } @Override public void dispose(final GLAutoDrawable drawable) { if( !isInit ) { return; } isInit = false; if(verbose) { System.err.println(Thread.currentThread()+" GearsES2.dispose "+sid()+": tileRendererInUse "+tileRendererInUse); } final Object upstreamWidget = drawable.getUpstreamWidget(); if (upstreamWidget instanceof Window) { final Window window = (Window) upstreamWidget; window.removeMouseListener(gearsMouse); window.removeKeyListener(gearsKeys); window.removeGestureHandler(pinchToZoomGesture); pinchToZoomGesture = null; window.removeGestureListener(pinchToZoomListener); } final GL2ES2 gl = drawable.getGL().getGL2ES2(); if( !gl.hasGLSL() ) { return; } st.useProgram(gl, false); gear1.destroy(gl); gear1 = null; gear2.destroy(gl); gear2 = null; gear3.destroy(gl); gear3 = null; pmvMatrix = null; colorU = null; st.destroy(gl); st = null; sharedGears = null; syncObjects = null; if(verbose) { System.err.println(Thread.currentThread()+" GearsES2.dispose "+sid()+" FIN"); } } @Override public void display(final GLAutoDrawable drawable) { display(drawable, 0); } @Override public void display(final GLAutoDrawable drawable, final int flags) { if( !isInit ) { return; } if(null != sharedGears && !sharedGears.isInit() ) { return; } final GLAnimatorControl anim = drawable.getAnimator(); if( verbose && ( null == anim || !anim.isAnimating() ) ) { System.err.println(Thread.currentThread()+" GearsES2.display "+sid()+" "+drawable.getSurfaceWidth()+"x"+drawable.getSurfaceHeight()+", swapInterval "+swapInterval+", drawable 0x"+Long.toHexString(drawable.getHandle())); } final boolean repeatedFrame = 0 != ( CustomGLEventListener.DISPLAY_REPEAT & flags ); final boolean dontClear = 0 != ( CustomGLEventListener.DISPLAY_DONTCLEAR & flags ); // Turn the gears' teeth if( doRotate && !repeatedFrame ) { angle += 0.5f; } // Get the GL corresponding to the drawable we are animating final GL2ES2 gl = drawable.getGL().getGL2ES2(); final boolean hasFocus; final Object upstreamWidget = drawable.getUpstreamWidget(); if(upstreamWidget instanceof NativeWindow) { hasFocus = ((NativeWindow)upstreamWidget).hasFocus(); } else { hasFocus = true; } if( clearBuffers && !dontClear ) { if( null != clearColor ) { gl.glClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); } else if( null != tileRendererInUse ) { gl.glClearColor(1.0f, 1.0f, 1.0f, 0.0f); } else if( ignoreFocus || hasFocus ) { gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); } else { gl.glClearColor(0.2f, 0.2f, 0.2f, 0.0f); } // Special handling for the case where the GLJPanel is translucent // and wants to be composited with other Java 2D content if (GLProfile.isAWTAvailable() && (drawable instanceof com.jogamp.opengl.awt.GLJPanel) && !((com.jogamp.opengl.awt.GLJPanel) drawable).isOpaque() && ((com.jogamp.opengl.awt.GLJPanel) drawable).shouldPreserveColorBufferIfTranslucent()) { gl.glClear(GL.GL_DEPTH_BUFFER_BIT); } else { gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); } } if( !gl.hasGLSL() ) { return; } setGLStates(gl, true); st.useProgram(gl, true); pmvMatrix.glPushMatrix(); pmvMatrix.glTranslatef(panX, panY, panZ); pmvMatrix.glRotatef(view_rotx, 1.0f, 0.0f, 0.0f); pmvMatrix.glRotatef(view_roty, 0.0f, 1.0f, 0.0f); pmvMatrix.glRotatef(view_rotz, 0.0f, 0.0f, 1.0f); synchronized ( syncObjects ) { gear1.draw(gl, -3.0f, -2.0f, 1f * angle - 0f); gear2.draw(gl, 3.1f, -2.0f, -2f * angle - 9.0f); gear3.draw(gl, -3.1f, 4.2f, -2f * angle - 25.0f); } pmvMatrix.glPopMatrix(); st.useProgram(gl, false); setGLStates(gl, false); } public void setGLStates(final GL2ES2 gl, final boolean enable) { // Culling only possible if we do not flip the projection matrix final boolean useCullFace = ! ( flipVerticalInGLOrientation && gl.getContext().getGLDrawable().isGLOriented() || customRendering ); if( enable ) { gl.glEnable(GL.GL_DEPTH_TEST); if( useCullFace ) { gl.glEnable(GL.GL_CULL_FACE); } } else { gl.glDisable(GL.GL_DEPTH_TEST); if( useCullFace ) { gl.glDisable(GL.GL_CULL_FACE); } } } @Override public String toString() { return "GearsES2[obj "+sid()+" isInit "+isInit+", usesShared "+usesSharedGears+", 1 "+gear1+", 2 "+gear2+", 3 "+gear3+", sharedGears "+sharedGears+"]"; } class GearsKeyAdapter extends KeyAdapter { public void keyPressed(final KeyEvent e) { final int kc = e.getKeyCode(); if(KeyEvent.VK_LEFT == kc) { view_roty -= 1; } else if(KeyEvent.VK_RIGHT == kc) { view_roty += 1; } else if(KeyEvent.VK_UP == kc) { view_rotx -= 1; } else if(KeyEvent.VK_DOWN == kc) { view_rotx += 1; } } } class GearsMouseAdapter implements MouseListener{ private int prevMouseX, prevMouseY; @Override public void mouseClicked(final MouseEvent e) { } @Override public void mouseEntered(final MouseEvent e) { } @Override public void mouseExited(final MouseEvent e) { } @Override public void mouseWheelMoved(final MouseEvent e) { final float[] rot = e.getRotation(); if( e.isControlDown() ) { // alternative zoom final float incr = e.isShiftDown() ? rot[0] : rot[1] * 0.5f ; panZ += incr; System.err.println("panZ.2: incr "+incr+", dblZoom "+e.isShiftDown()+" -> "+panZ); } else { // panning panX -= rot[0]; // positive -> left panY += rot[1]; // positive -> up } } public void mousePressed(final MouseEvent e) { if( e.getPointerCount()==1 ) { prevMouseX = e.getX(); prevMouseY = e.getY(); } else if( e.getPointerCount() == 4 ) { final Object src = e.getSource(); if( e.getPressure(0, true) > 0.7f && src instanceof Window) { // show Keyboard ((Window) src).setKeyboardVisible(true); } } } public void mouseReleased(final MouseEvent e) { } public void mouseMoved(final MouseEvent e) { if( e.isConfined() ) { navigate(e); } else { // track prev. position so we don't have 'jumps' // in case we move to confined navigation. prevMouseX = e.getX(); prevMouseY = e.getY(); } } public void mouseDragged(final MouseEvent e) { navigate(e); } private void navigate(final MouseEvent e) { final int x = e.getX(); final int y = e.getY(); int width, height; final Object source = e.getSource(); Window window = null; if(source instanceof Window) { window = (Window) source; width=window.getSurfaceWidth(); height=window.getSurfaceHeight(); } else if (source instanceof GLAutoDrawable) { final GLAutoDrawable glad = (GLAutoDrawable) source; width = glad.getSurfaceWidth(); height = glad.getSurfaceHeight(); } else if (GLProfile.isAWTAvailable() && source instanceof java.awt.Component) { final java.awt.Component comp = (java.awt.Component) source; width=comp.getWidth(); // FIXME HiDPI: May need to convert window units -> pixel units! height=comp.getHeight(); } else { throw new RuntimeException("Event source neither Window nor Component: "+source); } final float thetaY = 360.0f * ( (float)(x-prevMouseX)/(float)width); final float thetaX = 360.0f * ( (float)(prevMouseY-y)/(float)height); view_rotx += thetaX; view_roty += thetaY; prevMouseX = x; prevMouseY = y; // System.err.println("rotXY.1: "+view_rotx+"/"+view_roty+", source "+e); } } }