diff options
author | Sven Gothel <[email protected]> | 2014-07-05 04:04:43 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2014-07-05 04:04:43 +0200 |
commit | f8f0f051604721bceaee214b8e5218fd47d2eb9e (patch) | |
tree | e9c3103498984fbc5d2f8567e88009c49e8810f7 /src/test | |
parent | 2293a53ba04a8cf2881e8919f8be97c16a9af336 (diff) |
Bug 1021: Make OVR access vendor agnostic: Package 'com.jogamp.opengl.util.stereo' contains all public interfaces/classes
Renamed interfaces:
CustomRendererListener -> CustomGLEventListener
StereoRendererListener -> StereoGLEventListener
New vendor agnostic 'stuff' in com.jogamp.opengl.util.stereo:
1 - StereoDeviceFactory
To create a vendor specific StereoDeviceFactory instance,
which creates the StereoDevice.
2 - StereoDevice
For vendor specific implementation.
Can create StereoDeviceRenderer.
3 - StereoDeviceRenderer
For vendor specific implementation.
4 - StereoClientRenderer
Vendor agnostic client StereoGLEventListener renderer,
using a StereoDeviceRenderer.
Now supports multiple StereoGLEventListener, via add/remove.
- MovieSBSStereo demo-able via StereoDemo01
can show SBS 3D movies.
Diffstat (limited to 'src/test')
5 files changed, 1182 insertions, 248 deletions
diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/GearsES2.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/GearsES2.java index 2709aa608..84cd8936e 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/GearsES2.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/GearsES2.java @@ -35,7 +35,7 @@ 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.CustomRendererListener; +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; @@ -43,7 +43,7 @@ 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.EyePose; -import com.jogamp.opengl.util.stereo.StereoRendererListener; +import com.jogamp.opengl.util.stereo.StereoGLEventListener; import java.nio.FloatBuffer; @@ -60,7 +60,7 @@ import javax.media.opengl.fixedfunc.GLMatrixFunc; * GearsES2.java <BR> * @author Brian Paul (converted to Java by Ron Cemer and Sven Gothel) <P> */ -public class GearsES2 implements StereoRendererListener, TileRendererBase.TileRendererListener { +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; @@ -419,8 +419,8 @@ public class GearsES2 implements StereoRendererListener, TileRendererBase.TileRe private final float[] vec3Tmp3 = new float[3]; @Override - public void reshapeEye(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height, - final EyeParameter eyeParam, final EyePose eyePose) { + public void reshapeForEye(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height, + final EyeParameter eyeParam, final EyePose eyePose) { final GL2ES2 gl = drawable.getGL().getGL2ES2(); pmvMatrix.glMatrixMode(GLMatrixFunc.GL_PROJECTION); final float[] mat4Projection = FloatUtil.makePerspective(mat4Tmp1, 0, true, eyeParam.fovhv, zNear, zFar); @@ -507,8 +507,8 @@ public class GearsES2 implements StereoRendererListener, TileRendererBase.TileRe 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 != ( CustomRendererListener.DISPLAY_REPEAT & flags ); - final boolean dontClear = 0 != ( CustomRendererListener.DISPLAY_DONTCLEAR & flags ); + final boolean repeatedFrame = 0 != ( CustomGLEventListener.DISPLAY_REPEAT & flags ); + final boolean dontClear = 0 != ( CustomGLEventListener.DISPLAY_DONTCLEAR & flags ); // Turn the gears' teeth if( doRotate && !repeatedFrame ) { diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/av/MovieSBSStereo.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/av/MovieSBSStereo.java new file mode 100644 index 000000000..a2a39d631 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/av/MovieSBSStereo.java @@ -0,0 +1,859 @@ +/** + * Copyright 2012 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.opengl.test.junit.jogl.demos.es2.av; + +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.FloatBuffer; + +import javax.media.opengl.GL; +import javax.media.opengl.GL2ES2; +import javax.media.opengl.GLAnimatorControl; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLES2; +import javax.media.opengl.GLException; +import javax.media.opengl.GLUniformData; +import javax.media.opengl.fixedfunc.GLMatrixFunc; + +import com.jogamp.common.os.Platform; +import com.jogamp.graph.curve.Region; +import com.jogamp.graph.curve.opengl.GLRegion; +import com.jogamp.graph.curve.opengl.RegionRenderer; +import com.jogamp.graph.font.Font; +import com.jogamp.newt.Window; +import com.jogamp.newt.event.KeyAdapter; +import com.jogamp.newt.event.KeyEvent; +import com.jogamp.newt.event.KeyListener; +import com.jogamp.newt.event.MouseAdapter; +import com.jogamp.newt.event.MouseEvent; +import com.jogamp.newt.event.MouseListener; +import com.jogamp.newt.opengl.GLWindow; +import com.jogamp.opengl.GLExtensions; +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.graph.TextRendererGLELBase; +import com.jogamp.opengl.test.junit.util.UITestCase; +import com.jogamp.opengl.util.CustomGLEventListener; +import com.jogamp.opengl.util.GLArrayDataServer; +import com.jogamp.opengl.util.PMVMatrix; +import com.jogamp.opengl.util.av.GLMediaPlayer; +import com.jogamp.opengl.util.av.GLMediaPlayer.GLMediaEventListener; +import com.jogamp.opengl.util.av.GLMediaPlayerFactory; +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.EyePose; +import com.jogamp.opengl.util.stereo.StereoClientRenderer; +import com.jogamp.opengl.util.stereo.StereoGLEventListener; +import com.jogamp.opengl.util.texture.Texture; +import com.jogamp.opengl.util.texture.TextureCoords; +import com.jogamp.opengl.util.texture.TextureSequence; +import com.jogamp.opengl.util.texture.TextureSequence.TextureFrame; + +/** + * Side-By-Side (SBS) 3D Movie Player for {@link StereoClientRenderer} + * <p> + * The movie is assumed to be symmetrical SBS, + * the left-eye receives the left-part of the texture + * and the right-eye the right-part. + * </p> + */ +public class MovieSBSStereo implements StereoGLEventListener { + public static final String WINDOW_KEY = "window"; + public static final String STEREO_RENDERER_KEY = "stereo"; + public static final String PLAYER = "player"; + + private static boolean waitForKey = false; + private int surfWidth, surfHeight; + private int prevMouseX; // , prevMouseY; + private int rotate = 0; + private float zoom0; + private float zoom1; + private float zoom; + private long startTime; + private final float alpha = 1.0f; + + private GLMediaPlayer mPlayer; + private boolean mPlayerScaleOrig; + private float[] verts = null; + private GLArrayDataServer interleavedVBOLeft; + private GLArrayDataServer interleavedVBORight; + private volatile boolean resetGLState = false; + private StereoClientRenderer stereoClientRenderer; + + private ShaderState st; + private PMVMatrix pmvMatrix; + private GLUniformData pmvMatrixUniform; + private static final String shaderBasename = "texsequence_xxx"; + private static final String myTextureLookupName = "myTexture2D"; + + /** Blender's Big Buck Bunny: 24f 416p H.264, AAC 48000 Hz, 2 ch, mpeg stream. */ + public static final URI defURI; + static { + URI _defURI = null; + try { + // Blender's Big Buck Bunny Trailer: 24f 640p VP8, Vorbis 44100Hz mono, WebM/Matroska Stream. + // _defURI = new URI("http://video.webmfiles.org/big-buck-bunny_trailer.webm"); + _defURI = new URI("http://archive.org/download/BigBuckBunny_328/BigBuckBunny_512kb.mp4"); + } catch (final URISyntaxException e) { + e.printStackTrace(); + } + defURI = _defURI; + } + + final int[] textSampleCount = { 4 }; + + private final class InfoTextRendererGLELBase extends TextRendererGLELBase { + private final Font font = getFont(0, 0, 0); + private final float fontSize = 1f; // 0.01f; + private final GLRegion regionFPS; + + InfoTextRendererGLELBase(final int rmode, final boolean lowPerfDevice) { + // FIXME: Graph TextRenderer does not AA well w/o MSAA and FBO + super(rmode, textSampleCount); + this.setRendererCallbacks(RegionRenderer.defaultBlendEnable, RegionRenderer.defaultBlendDisable); + if( lowPerfDevice ) { + regionFPS = null; + } else { + regionFPS = GLRegion.create(renderModes, null); + System.err.println("RegionFPS "+Region.getRenderModeString(renderModes)+", sampleCount "+textSampleCount[0]+", class "+regionFPS.getClass().getName()); + } + staticRGBAColor[0] = 0.9f; + staticRGBAColor[1] = 0.9f; + staticRGBAColor[2] = 0.9f; + staticRGBAColor[3] = 1.0f; + } + + @Override + public void init(final GLAutoDrawable drawable) { + super.init(drawable); + } + + @Override + public void dispose(final GLAutoDrawable drawable) { + if( null != regionFPS ) { + regionFPS.destroy(drawable.getGL().getGL2ES2()); + } + super.dispose(drawable); + } + + @Override + public void display(final GLAutoDrawable drawable) { + final GLAnimatorControl anim = drawable.getAnimator(); + final float lfps = null != anim ? anim.getLastFPS() : 0f; + final float tfps = null != anim ? anim.getTotalFPS() : 0f; + final boolean hasVideo = GLMediaPlayer.STREAM_ID_NONE != mPlayer.getVID(); + final float pts = ( hasVideo ? mPlayer.getVideoPTS() : mPlayer.getAudioPTS() ) / 1000f; + + // Note: MODELVIEW is from [ 0 .. height ] + + final int height = 0; // drawable.getSurfaceHeight(); + + final float aspect = (float)mPlayer.getWidth() / (float)mPlayer.getHeight(); + + final String ptsPrec = null != regionFPS ? "3.1" : "3.0"; + final String text1 = String.format("%0"+ptsPrec+"f/%0"+ptsPrec+"f s, %s (%01.2fx, vol %01.2f), a %01.2f, fps %02.1f -> %02.1f / %02.1f", + pts, mPlayer.getDuration() / 1000f, + mPlayer.getState().toString().toLowerCase(), mPlayer.getPlaySpeed(), mPlayer.getAudioVolume(), + aspect, mPlayer.getFramerate(), lfps, tfps); + final String text2 = String.format("audio: id %d, kbps %d, codec %s", + mPlayer.getAID(), mPlayer.getAudioBitrate()/1000, mPlayer.getAudioCodec()); + final String text3 = String.format("video: id %d, kbps %d, codec %s", + mPlayer.getVID(), mPlayer.getVideoBitrate()/1000, mPlayer.getVideoCodec()); + final String text4 = mPlayer.getURI().getRawPath(); + if( displayOSD && null != renderer ) { + // We share ClearColor w/ MovieSimple's init ! + final float pixelSize = font.getPixelSize(fontSize, dpiH); + if( null != regionFPS ) { + renderString(drawable, font, pixelSize, text1, 1 /* col */, 1 /* row */, 0, 0, -1, regionFPS); // no-cache + } else { + renderString(drawable, font, pixelSize, text1, 1 /* col */, 1 /* row */, 0, 0, -1, true); + } + renderString(drawable, font, pixelSize, text2, 1 /* col */, -4 /* row */, 0, height, -1, true); + renderString(drawable, font, pixelSize, text3, 1 /* col */, -3 /* row */, 0, height, 0, true); + renderString(drawable, font, pixelSize, text4, 1 /* col */, -2 /* row */, 0, height, 1, true); + } + } }; + private InfoTextRendererGLELBase textRendererGLEL = null; + private boolean displayOSD = false; + + private final MouseListener mouseAction = new MouseAdapter() { + public void mousePressed(final MouseEvent e) { + if(e.getY()<=surfHeight/2 && null!=mPlayer && 1 == e.getClickCount()) { + if(GLMediaPlayer.State.Playing == mPlayer.getState()) { + mPlayer.pause(false); + } else { + mPlayer.play(); + } + } + } + public void mouseReleased(final MouseEvent e) { + if(e.getY()<=surfHeight/2) { + rotate = -1; + zoom = zoom0; + System.err.println("zoom: "+zoom); + } + } + public void mouseMoved(final MouseEvent e) { + prevMouseX = e.getX(); + // prevMouseY = e.getY(); + } + public void mouseDragged(final MouseEvent e) { + final int x = e.getX(); + final int y = e.getY(); + + if(y>surfHeight/2) { + final float dp = (float)(x-prevMouseX)/(float)surfWidth; + final int pts0 = GLMediaPlayer.STREAM_ID_NONE != mPlayer.getVID() ? mPlayer.getVideoPTS() : mPlayer.getAudioPTS(); + mPlayer.seek(pts0 + (int) (mPlayer.getDuration() * dp)); + } else { + mPlayer.play(); + rotate = 1; + zoom = zoom1; + } + + prevMouseX = x; + // prevMouseY = y; + } + public void mouseWheelMoved(final MouseEvent e) { + if( !e.isShiftDown() ) { + zoom += e.getRotation()[1]/10f; // vertical: wheel + System.err.println("zoom: "+zoom); + } + } }; + + private final KeyListener keyAction = new KeyAdapter() { + public void keyReleased(final KeyEvent e) { + if( e.isAutoRepeat() ) { + return; + } + System.err.println("MC "+e); + final int pts0 = GLMediaPlayer.STREAM_ID_NONE != mPlayer.getVID() ? mPlayer.getVideoPTS() : mPlayer.getAudioPTS(); + int pts1 = 0; + switch(e.getKeySymbol()) { + case KeyEvent.VK_O: displayOSD = !displayOSD; break; + case KeyEvent.VK_RIGHT: pts1 = pts0 + 1000; break; + case KeyEvent.VK_UP: pts1 = pts0 + 10000; break; + case KeyEvent.VK_PAGE_UP: pts1 = pts0 + 30000; break; + case KeyEvent.VK_LEFT: pts1 = pts0 - 1000; break; + case KeyEvent.VK_DOWN: pts1 = pts0 - 10000; break; + case KeyEvent.VK_PAGE_DOWN: pts1 = pts0 - 30000; break; + case KeyEvent.VK_ESCAPE: + case KeyEvent.VK_HOME: + case KeyEvent.VK_BACK_SPACE: { + mPlayer.seek(0); + break; + } + case KeyEvent.VK_SPACE: { + if(GLMediaPlayer.State.Paused == mPlayer.getState()) { + mPlayer.play(); + } else { + mPlayer.pause(false); + } + break; + } + case KeyEvent.VK_MULTIPLY: + mPlayer.setPlaySpeed(1.0f); + break; + case KeyEvent.VK_SUBTRACT: { + float playSpeed = mPlayer.getPlaySpeed(); + if( e.isShiftDown() ) { + playSpeed /= 2.0f; + } else { + playSpeed -= 0.1f; + } + mPlayer.setPlaySpeed(playSpeed); + } break; + case KeyEvent.VK_ADD: { + float playSpeed = mPlayer.getPlaySpeed(); + if( e.isShiftDown() ) { + playSpeed *= 2.0f; + } else { + playSpeed += 0.1f; + } + mPlayer.setPlaySpeed(playSpeed); + } break; + case KeyEvent.VK_M: { + float audioVolume = mPlayer.getAudioVolume(); + if( audioVolume > 0.5f ) { + audioVolume = 0f; + } else { + audioVolume = 1f; + } + mPlayer.setAudioVolume(audioVolume); + } break; + } + + if( 0 != pts1 ) { + mPlayer.seek(pts1); + } + } }; + + /** user needs to issue {@link #initStream(URI, int, int, int)} afterwards. */ + public MovieSBSStereo() throws IllegalStateException { + mPlayerScaleOrig = false; + mPlayer = GLMediaPlayerFactory.createDefault(); + mPlayer.attachObject(PLAYER, this); + System.out.println("pC.1a "+mPlayer); + } + + public void initStream(final URI streamLoc, final int vid, final int aid, final int textureCount) { + mPlayer.initStream(streamLoc, vid, aid, textureCount); + System.out.println("pC.1b "+mPlayer); + } + + public GLMediaPlayer getGLMediaPlayer() { return mPlayer; } + + public void setScaleOrig(final boolean v) { + mPlayerScaleOrig = v; + } + + public void setStereoClientRenderer(final StereoClientRenderer scr) { + stereoClientRenderer = scr; + } + public StereoClientRenderer getStereoClientRenderer() { return stereoClientRenderer; } + + public void resetGLState() { + resetGLState = true; + } + + private void initShader(final GL2ES2 gl) { + // Create & Compile the shader objects + final ShaderCode rsVp = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, MovieSBSStereo.class, + "../shader", "../shader/bin", shaderBasename, true); + final ShaderCode rsFp = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, MovieSBSStereo.class, + "../shader", "../shader/bin", shaderBasename, true); + + boolean preludeGLSLVersion = true; + if( GLES2.GL_TEXTURE_EXTERNAL_OES == mPlayer.getTextureTarget() ) { + if( !gl.isExtensionAvailable(GLExtensions.OES_EGL_image_external) ) { + throw new GLException(GLExtensions.OES_EGL_image_external+" requested but not available"); + } + if( Platform.OSType.ANDROID == Platform.getOSType() && gl.isGLES3() ) { + // Bug on Nexus 10, ES3 - Android 4.3, where + // GL_OES_EGL_image_external extension directive leads to a failure _with_ '#version 300 es' ! + // P0003: Extension 'GL_OES_EGL_image_external' not supported + preludeGLSLVersion = false; + } + } + rsVp.defaultShaderCustomization(gl, preludeGLSLVersion, true); + + int rsFpPos = preludeGLSLVersion ? rsFp.addGLSLVersion(gl) : 0; + rsFpPos = rsFp.insertShaderSource(0, rsFpPos, mPlayer.getRequiredExtensionsShaderStub()); + rsFpPos = rsFp.addDefaultShaderPrecision(gl, rsFpPos); + + final String texLookupFuncName = mPlayer.getTextureLookupFunctionName(myTextureLookupName); + rsFp.replaceInShaderSource(myTextureLookupName, texLookupFuncName); + + // Inject TextureSequence shader details + final StringBuilder sFpIns = new StringBuilder(); + sFpIns.append("uniform ").append(mPlayer.getTextureSampler2DType()).append(" mgl_ActiveTexture;\n"); + sFpIns.append(mPlayer.getTextureLookupFragmentShaderImpl()); + rsFp.insertShaderSource(0, "TEXTURE-SEQUENCE-CODE-BEGIN", 0, sFpIns); + + // Create & Link the shader program + final ShaderProgram sp = new ShaderProgram(); + sp.add(rsVp); + sp.add(rsFp); + if(!sp.link(gl, System.err)) { + throw new GLException("Couldn't link program: "+sp); + } + + // Let's manage all our states using ShaderState. + st = new ShaderState(); + st.attachShaderProgram(gl, sp, false); + } + + @Override + public void init(final GLAutoDrawable drawable) { + if(null == mPlayer) { + throw new InternalError("mPlayer null"); + } + if( GLMediaPlayer.State.Uninitialized == mPlayer.getState() ) { + throw new IllegalStateException("mPlayer in uninitialized state: "+mPlayer); + } + final boolean hasVideo = GLMediaPlayer.STREAM_ID_NONE != mPlayer.getVID(); + resetGLState = false; + + zoom0 = -2.1f; + zoom1 = -5f; + zoom = 0f; + + final GL2ES2 gl = drawable.getGL().getGL2ES2(); + System.err.println(JoglVersion.getGLInfo(gl, null)); + System.err.println("Alpha: "+alpha+", opaque "+drawable.getChosenGLCapabilities().isBackgroundOpaque()+ + ", "+drawable.getClass().getName()+", "+drawable); + + if(waitForKey) { + UITestCase.waitForKey("Init>"); + } + final Texture tex; + try { + System.out.println("p0 "+mPlayer); + if(GLMediaPlayer.State.Initialized == mPlayer.getState() ) { + mPlayer.initGL(gl); + } + System.out.println("p1 "+mPlayer); + final TextureFrame frame = mPlayer.getLastTexture(); + if( null != frame ) { + if( !hasVideo ) { + throw new InternalError("XXX: "+mPlayer); + } + tex = frame.getTexture(); + if( null == tex ) { + throw new InternalError("XXX: "+mPlayer); + } + } else { + tex = null; + if( hasVideo ) { + throw new InternalError("XXX: "+mPlayer); + } + } + mPlayer.setTextureMinMagFilter( new int[] { GL.GL_NEAREST, GL.GL_NEAREST } ); + } catch (final Exception glex) { + glex.printStackTrace(); + if(null != mPlayer) { + mPlayer.destroy(gl); + mPlayer = null; + } + throw new GLException(glex); + } + + if( hasVideo ) { + initShader(gl); + + // Push the 1st uniform down the path + st.useProgram(gl, true); + + final int[] viewPort = new int[] { 0, 0, drawable.getSurfaceWidth(), drawable.getSurfaceHeight()}; + pmvMatrix = new PMVMatrix(); + reshapePMV(viewPort[2], viewPort[3]); + pmvMatrixUniform = new GLUniformData("mgl_PMVMatrix", 4, 4, pmvMatrix.glGetPMvMatrixf()); + if(!st.uniform(gl, pmvMatrixUniform)) { + throw new GLException("Error setting PMVMatrix in shader: "+st); + } + if(!st.uniform(gl, new GLUniformData("mgl_ActiveTexture", mPlayer.getTextureUnit()))) { + throw new GLException("Error setting mgl_ActiveTexture in shader: "+st); + } + + final float dWidth = drawable.getSurfaceWidth(); + final float dHeight = drawable.getSurfaceHeight(); + final float mWidth = mPlayer.getWidth(); + final float mHeight = mPlayer.getHeight(); + final float mAspect = mWidth/mHeight; + System.err.println("XXX0: mov aspect: "+mAspect); + float xs, ys; + if(mPlayerScaleOrig && mWidth < dWidth && mHeight < dHeight) { + xs = mAspect * ( mWidth / dWidth ) ; ys = xs / mAspect ; + } else { + xs = mAspect; ys = 1f; // b>h + } + verts = new float[] { -1f*xs, -1f*ys, 0f, // LB + 1f*xs, 1f*ys, 0f // RT + }; + { + System.err.println("XXX0: pixel LB: "+verts[0]+", "+verts[1]+", "+verts[2]); + System.err.println("XXX0: pixel RT: "+verts[3]+", "+verts[4]+", "+verts[5]); + final float[] winLB = new float[3]; + final float[] winRT = new float[3]; + pmvMatrix.gluProject(verts[0], verts[1], verts[2], viewPort, 0, winLB, 0); + pmvMatrix.gluProject(verts[3], verts[4], verts[5], viewPort, 0, winRT, 0); + System.err.println("XXX0: win LB: "+winLB[0]+", "+winLB[1]+", "+winLB[2]); + System.err.println("XXX0: win RT: "+winRT[0]+", "+winRT[1]+", "+winRT[2]); + } + + interleavedVBOLeft = GLArrayDataServer.createGLSLInterleaved(3+4+2, GL.GL_FLOAT, false, 3*4, GL.GL_STATIC_DRAW); + { + interleavedVBOLeft.addGLSLSubArray("mgl_Vertex", 3, GL.GL_ARRAY_BUFFER); + interleavedVBOLeft.addGLSLSubArray("mgl_Color", 4, GL.GL_ARRAY_BUFFER); + interleavedVBOLeft.addGLSLSubArray("mgl_MultiTexCoord", 2, GL.GL_ARRAY_BUFFER); + } + interleavedVBORight = GLArrayDataServer.createGLSLInterleaved(3+4+2, GL.GL_FLOAT, false, 3*4, GL.GL_STATIC_DRAW); + { + interleavedVBORight.addGLSLSubArray("mgl_Vertex", 3, GL.GL_ARRAY_BUFFER); + interleavedVBORight.addGLSLSubArray("mgl_Color", 4, GL.GL_ARRAY_BUFFER); + interleavedVBORight.addGLSLSubArray("mgl_MultiTexCoord", 2, GL.GL_ARRAY_BUFFER); + } + updateInterleavedVBO(gl, interleavedVBOLeft, tex, 0); + updateInterleavedVBO(gl, interleavedVBORight, tex, 1); + + st.ownAttribute(interleavedVBOLeft, true); + st.ownAttribute(interleavedVBORight, true); + gl.glClearColor(0.3f, 0.3f, 0.3f, 0.3f); + + gl.glEnable(GL.GL_DEPTH_TEST); + + st.useProgram(gl, false); + + // Let's show the completed shader state .. + System.out.println("iVBOLeft : "+interleavedVBOLeft); + System.out.println("iVBORight: "+interleavedVBORight); + System.out.println(st); + } + + mPlayer.play(); + System.out.println("play.0 "+mPlayer); + startTime = System.currentTimeMillis(); + + final Object upstreamWidget = drawable.getUpstreamWidget(); + if (upstreamWidget instanceof Window) { + final Window window = (Window) upstreamWidget; + window.addMouseListener(mouseAction); + window.addKeyListener(keyAction); + surfWidth = window.getSurfaceWidth(); + surfHeight = window.getSurfaceHeight(); + } + final int rmode = drawable.getChosenGLCapabilities().getSampleBuffers() ? 0 : Region.VBAA_RENDERING_BIT; + final boolean lowPerfDevice = gl.isGLES(); + textRendererGLEL = new InfoTextRendererGLELBase(rmode, lowPerfDevice); + textRendererGLEL.init(drawable); + } + + protected void updateInterleavedVBO(final GL gl, final GLArrayDataServer iVBO, final Texture tex, final int eyeNum) { + final boolean wasEnabled = iVBO.enabled(); + iVBO.seal(gl, false); + iVBO.rewind(); + { + final FloatBuffer ib = (FloatBuffer)iVBO.getBuffer(); + final TextureCoords tc = tex.getImageTexCoords(); + final float texHalfWidth = tc.right()/2f; + System.err.println("XXX0: "+tc+", texHalfWidth "+texHalfWidth); + System.err.println("XXX0: tex aspect: "+tex.getAspectRatio()); + System.err.println("XXX0: tex y-flip: "+tex.getMustFlipVertically()); + + // left-bottom + ib.put(verts[0]); ib.put(verts[1]); ib.put(verts[2]); + ib.put( 1); ib.put( 1); ib.put( 1); ib.put(alpha); + if( 0 == eyeNum ) { + ib.put( tc.left() ); ib.put( tc.bottom() ); + } else { + ib.put( tc.left() + texHalfWidth ); ib.put( tc.bottom() ); + } + + // right-bottom + ib.put(verts[3]); ib.put(verts[1]); ib.put(verts[2]); + ib.put( 1); ib.put( 1); ib.put( 1); ib.put(alpha); + if( 0 == eyeNum ) { + ib.put( texHalfWidth ); ib.put( tc.bottom() ); + } else { + ib.put( tc.right() ); ib.put( tc.bottom() ); + } + + // left-top + ib.put(verts[0]); ib.put(verts[4]); ib.put(verts[2]); + ib.put( 1); ib.put( 1); ib.put( 1); ib.put(alpha); + if( 0 == eyeNum ) { + ib.put( tc.left() ); ib.put( tc.top() ); + } else { + ib.put( tc.left() + texHalfWidth ); ib.put( tc.top() ); + } + + // right-top + ib.put(verts[3]); ib.put(verts[4]); ib.put(verts[2]); + ib.put( 1); ib.put( 1); ib.put( 1); ib.put(alpha); + if( 0 == eyeNum ) { + ib.put( texHalfWidth ); ib.put( tc.top() ); + } else { + ib.put( tc.right() ); ib.put( tc.top() ); + } + } + iVBO.seal(gl, true); + if( !wasEnabled ) { + iVBO.enableBuffer(gl, false); + } + } + + @Override + public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) { + final GL2ES2 gl = drawable.getGL().getGL2ES2(); + surfWidth = width; + surfHeight = height; + + if(null == mPlayer) { return; } + + if(null != st) { + reshapePMV(width, height); + st.useProgram(gl, true); + st.uniform(gl, pmvMatrixUniform); + st.useProgram(gl, false); + } + + System.out.println("pR "+mPlayer); + textRendererGLEL.reshape(drawable, x, y, width, height); + } + + private final float zNear = 0.1f; + private final float zFar = 10000f; + + private void reshapePMV(final int width, final int height) { + pmvMatrix.glMatrixMode(GLMatrixFunc.GL_PROJECTION); + pmvMatrix.glLoadIdentity(); + pmvMatrix.gluPerspective(45.0f, (float)width / (float)height, zNear, zFar); + + pmvMatrix.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); + pmvMatrix.glLoadIdentity(); + pmvMatrix.glTranslatef(0, 0, zoom0); + } + + 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]; + + GLArrayDataServer interleavedVBOCurrent = null; + + @Override + public void reshapeForEye(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height, + final EyeParameter eyeParam, final EyePose eyePose) { + final GL2ES2 gl = drawable.getGL().getGL2ES2(); + interleavedVBOCurrent = 0 == eyeParam.number ? interleavedVBOLeft : interleavedVBORight; + + surfWidth = drawable.getSurfaceWidth(); + surfHeight = drawable.getSurfaceHeight(); + + if(null == mPlayer) { return; } + if(null == st) { return; } + + pmvMatrix.glMatrixMode(GLMatrixFunc.GL_PROJECTION); + final float[] mat4Projection = FloatUtil.makePerspective(mat4Tmp1, 0, true, eyeParam.fovhv, zNear, zFar); + pmvMatrix.glLoadMatrixf(mat4Projection, 0); + + pmvMatrix.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); + final Quaternion rollPitchYaw = new Quaternion(); + final float[] shiftedEyePos = rollPitchYaw.rotateVector(vec3Tmp1, 0, eyePose.position, 0); + VectorUtil.addVec3(shiftedEyePos, shiftedEyePos, eyeParam.positionOffset); + + rollPitchYaw.mult(eyePose.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, 0, zoom0); + st.useProgram(gl, true); + st.uniform(gl, pmvMatrixUniform); + st.useProgram(gl, false); + textRendererGLEL.reshape(drawable, x, y, width, height); + } + + @Override + public void dispose(final GLAutoDrawable drawable) { + textRendererGLEL.dispose(drawable); + textRendererGLEL = null; + disposeImpl(drawable, true); + } + + private void disposeImpl(final GLAutoDrawable drawable, final boolean disposePlayer) { + if(null == mPlayer) { return; } + + final Object upstreamWidget = drawable.getUpstreamWidget(); + if (upstreamWidget instanceof Window) { + final Window window = (Window) upstreamWidget; + window.removeMouseListener(mouseAction); + window.removeKeyListener(keyAction); + } + + System.out.println("pD.1 "+mPlayer+", disposePlayer "+disposePlayer); + final GL2ES2 gl = drawable.getGL().getGL2ES2(); + if( disposePlayer ) { + mPlayer.destroy(gl); + System.out.println("pD.X "+mPlayer); + mPlayer=null; + } + pmvMatrixUniform = null; + if(null != pmvMatrix) { + pmvMatrix=null; + } + if(null != st) { + st.destroy(gl); + st=null; + } + } + + long lastPerfPos = 0; + + @Override + public void display(final GLAutoDrawable drawable) { + display(drawable, 0); + } + + @Override + public void display(final GLAutoDrawable drawable, final int flags) { + // TODO Auto-generated method stub + final boolean repeatedFrame = 0 != ( CustomGLEventListener.DISPLAY_REPEAT & flags ); + final boolean dontClear = 0 != ( CustomGLEventListener.DISPLAY_DONTCLEAR & flags ); + final GLArrayDataServer iVBO = null != interleavedVBOCurrent ? interleavedVBOCurrent : interleavedVBOLeft; + + final GL2ES2 gl = drawable.getGL().getGL2ES2(); + if(null == mPlayer) { return; } + + if( resetGLState ) { + resetGLState = false; + System.err.println("XXX resetGLState"); + disposeImpl(drawable, false); + init(drawable); + reshape(drawable, 0, 0, drawable.getSurfaceWidth(), drawable.getSurfaceHeight()); + } + + final long currentPos = System.currentTimeMillis(); + if( currentPos - lastPerfPos > 2000 ) { + System.err.println( mPlayer.getPerfString() ); + lastPerfPos = currentPos; + } + + if( !dontClear ) { + gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); + } + + if(null == st) { + return; + } + + st.useProgram(gl, true); + + pmvMatrix.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); + pmvMatrix.glPushMatrix(); + pmvMatrix.glTranslatef(0, 0, zoom); + if(rotate > 0) { + final float ang = ((System.currentTimeMillis() - startTime) * 360.0f) / 8000.0f; + pmvMatrix.glRotatef(ang, 0, 0, 1); + } else { + rotate = 0; + } + st.uniform(gl, pmvMatrixUniform); + iVBO.enableBuffer(gl, true); + Texture tex = null; + if(null!=mPlayer) { + final TextureSequence.TextureFrame texFrame; + if( repeatedFrame ) { + texFrame=mPlayer.getLastTexture(); + } else { + texFrame=mPlayer.getNextTexture(gl); + } + if(null != texFrame) { + tex = texFrame.getTexture(); + gl.glActiveTexture(GL.GL_TEXTURE0+mPlayer.getTextureUnit()); + tex.enable(gl); + tex.bind(gl); + } + } + gl.glDrawArrays(GL.GL_TRIANGLE_STRIP, 0, 4); + if(null != tex) { + tex.disable(gl); + } + iVBO.enableBuffer(gl, false); + st.useProgram(gl, false); + pmvMatrix.glPopMatrix(); + + textRendererGLEL.display(drawable); + } + + static class StereoGLMediaEventListener implements GLMediaEventListener { + void destroyWindow(final Window window) { + new Thread() { + public void run() { + window.destroy(); + } }.start(); + } + + @Override + public void newFrameAvailable(final GLMediaPlayer ts, final TextureFrame newFrame, final long when) { + } + + @Override + public void attributesChanged(final GLMediaPlayer mp, final int event_mask, final long when) { + System.err.println("MovieSimple AttributesChanges: events_mask 0x"+Integer.toHexString(event_mask)+", when "+when); + System.err.println("MovieSimple State: "+mp); + final GLWindow window = (GLWindow) mp.getAttachedObject(WINDOW_KEY); + final MovieSBSStereo ms = (MovieSBSStereo)mp.getAttachedObject(PLAYER); + final StereoClientRenderer stereoClientRenderer = (StereoClientRenderer) mp.getAttachedObject(STEREO_RENDERER_KEY); + + if( 0 != ( GLMediaEventListener.EVENT_CHANGE_SIZE & event_mask ) ) { + System.err.println("MovieSimple State: CHANGE_SIZE"); + // window.disposeGLEventListener(ms, false /* remove */ ); + ms.resetGLState(); + } + if( 0 != ( GLMediaEventListener.EVENT_CHANGE_INIT & event_mask ) ) { + System.err.println("MovieSimple State: INIT"); + // Use GLEventListener in all cases [A+V, V, A] + stereoClientRenderer.addGLEventListener(ms); + final GLAnimatorControl anim = window.getAnimator(); + anim.setUpdateFPSFrames(60, null); + anim.resetFPSCounter(); + ms.setStereoClientRenderer(stereoClientRenderer); + } + if( 0 != ( GLMediaEventListener.EVENT_CHANGE_PLAY & event_mask ) ) { + window.getAnimator().resetFPSCounter(); + } + + boolean destroy = false; + Throwable err = null; + + if( 0 != ( GLMediaEventListener.EVENT_CHANGE_EOS & event_mask ) ) { + err = ms.mPlayer.getStreamException(); + if( null != err ) { + System.err.println("MovieSimple State: EOS + Exception"); + destroy = true; + } else { + System.err.println("MovieSimple State: EOS"); + new Thread() { + public void run() { + mp.setPlaySpeed(1f); + mp.seek(0); + mp.play(); + } + }.start(); + } + } + if( 0 != ( GLMediaEventListener.EVENT_CHANGE_ERR & event_mask ) ) { + err = ms.mPlayer.getStreamException(); + if( null != err ) { + System.err.println("MovieSimple State: ERR + Exception"); + } else { + System.err.println("MovieSimple State: ERR"); + } + destroy = true; + } + if( destroy ) { + if( null != err ) { + err.printStackTrace(); + } + destroyWindow(window); + } + } + }; + public final static StereoGLMediaEventListener stereoGLMediaEventListener = new StereoGLMediaEventListener(); +} diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/av/MovieSimple.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/av/MovieSimple.java index 8760cd2c3..5918e4e6b 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/av/MovieSimple.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/av/MovieSimple.java @@ -90,8 +90,8 @@ public class MovieSimple implements GLEventListener { public static final int EFFECT_GRADIENT_BOTTOM2TOP = 1<<1; public static final int EFFECT_TRANSPARENT = 1<<3; - private static final String WINDOW_KEY = "window"; - private static final String PLAYER = "player"; + public static final String WINDOW_KEY = "window"; + public static final String PLAYER = "player"; private static boolean waitForKey = false; private int surfWidth, surfHeight; @@ -682,6 +682,9 @@ public class MovieSimple implements GLEventListener { System.out.println("pR "+mPlayer); } + private final float zNear = 1f; + private final float zFar = 10f; + private void reshapePMV(final int width, final int height) { pmvMatrix.glMatrixMode(GLMatrixFunc.GL_PROJECTION); pmvMatrix.glLoadIdentity(); @@ -691,7 +694,7 @@ public class MovieSimple implements GLEventListener { pmvMatrix.glOrthof(-fw, fw, -fh, fh, -1.0f, 1.0f); nearPlaneNormalized = 0f; } else { - pmvMatrix.gluPerspective(45.0f, (float)width / (float)height, 1f, 10.0f); + pmvMatrix.gluPerspective(45.0f, (float)width / (float)height, zNear, zFar); nearPlaneNormalized = 1f/(10f-1f); } System.err.println("XXX0: Perspective nearPlaneNormalized: "+nearPlaneNormalized); @@ -786,7 +789,7 @@ public class MovieSimple implements GLEventListener { Texture tex = null; if(null!=mPlayer) { final TextureSequence.TextureFrame texFrame; - if(mPlayerShared) { + if( mPlayerShared ) { texFrame=mPlayer.getLastTexture(); } else { texFrame=mPlayer.getNextTexture(gl); @@ -903,7 +906,7 @@ public class MovieSimple implements GLEventListener { } } }; - final static MyGLMediaEventListener myGLMediaEventListener = new MyGLMediaEventListener(); + public final static MyGLMediaEventListener myGLMediaEventListener = new MyGLMediaEventListener(); static boolean loopEOS = false; static boolean origSize; @@ -1068,4 +1071,5 @@ public class MovieSimple implements GLEventListener { mss[i].initStream(streamLocN, vid, aid, textureCount); } } + } diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/stereo/StereoDemo01.java b/src/test/com/jogamp/opengl/test/junit/jogl/stereo/StereoDemo01.java new file mode 100644 index 000000000..c4db20f37 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/jogl/stereo/StereoDemo01.java @@ -0,0 +1,307 @@ +/** + * Copyright 2014 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.test.junit.jogl.stereo; + +import java.io.File; +import java.net.URI; +import java.net.URISyntaxException; + +import javax.media.nativewindow.util.DimensionImmutable; +import javax.media.nativewindow.util.PointImmutable; +import javax.media.opengl.GL; +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLProfile; + +import com.jogamp.common.util.IOUtil; +import com.jogamp.common.util.ReflectionUtil; +import com.jogamp.newt.event.KeyAdapter; +import com.jogamp.newt.event.KeyEvent; +import com.jogamp.newt.opengl.GLWindow; +import com.jogamp.opengl.math.FovHVHalves; +import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2; +import com.jogamp.opengl.test.junit.jogl.demos.es2.av.MovieSBSStereo; +import com.jogamp.opengl.test.junit.jogl.demos.es2.av.MovieSimple; +import com.jogamp.opengl.test.junit.util.MiscUtils; +import com.jogamp.opengl.test.junit.util.QuitAdapter; +import com.jogamp.opengl.util.Animator; +import com.jogamp.opengl.util.AnimatorBase; +import com.jogamp.opengl.util.av.GLMediaPlayer; +import com.jogamp.opengl.util.stereo.StereoDevice; +import com.jogamp.opengl.util.stereo.StereoDeviceRenderer; +import com.jogamp.opengl.util.stereo.StereoDeviceFactory; +import com.jogamp.opengl.util.stereo.StereoClientRenderer; +import com.jogamp.opengl.util.stereo.StereoGLEventListener; + +/** + * All distortions, no multisampling, bilinear filtering, manual-swap and using two FBOs (default, good) + * <pre> + * java StereoDemo01 -time 10000000 + * </pre> + * All distortions, 8x multisampling, bilinear filtering, manual-swap and using two FBOs (best - slowest) + * <pre> + * java StereoDemo01 -time 10000000 -samples 8 + * </pre> + * All distortions, 8x multisampling, bilinear filtering, manual-swap and using one a big single FBO (w/ all commandline params) + * <pre> + * java StereoDemo01 -time 10000000 -vignette true -chromatic true -timewarp false -samples 8 -biLinear true -autoSwap false -singleFBO true -mainScreen false + * </pre> + * No distortions, no multisampling, no filtering, auto-swap and using a big single FBO (worst and fastest) + * <pre> + * java StereoDemo01 -time 10000000 -vignette false -chromatic false -timewarp false -samples 0 -biLinear false -autoSwap true -singleFBO true + * </pre> + * Test on main screen: + * <pre> + * java StereoDemo01 -time 10000000 -mainScreen true + * </pre> + * Test a 3D SBS Movie: + * <pre> + * java StereoDemo01 -time 10000000 -filmFile Some_SBS_3D_Movie.mkv + * java StereoDemo01 -time 10000000 -filmURI http://whoknows.not/Some_SBS_3D_Movie.mkv + * </pre> + * <p> + * Key 'R' enables/disables the VR's sensors, i.e. head rotation .. + * </p> + * + */ +public class StereoDemo01 { + static long duration = 10000; // ms + + static boolean useStereoScreen = true; + + static int numSamples = 0; + static boolean biLinear = true; + static boolean useSingleFBO = false; + static boolean useVignette = true; + static boolean useChromatic = true; + static boolean useTimewarp = true; + static boolean useAutoSwap = false; + static String useFilmFile = null; + static String useFilmURI = null; + static String stereoRendererListenerName = null; + + public static void main(final String args[]) throws InterruptedException, URISyntaxException { + for(int i=0; i<args.length; i++) { + if(args[i].equals("-time")) { + i++; + duration = MiscUtils.atol(args[i], duration); + } else if(args[i].equals("-samples")) { + i++; + numSamples = MiscUtils.atoi(args[i], numSamples); + } else if(args[i].equals("-biLinear")) { + i++; + biLinear = MiscUtils.atob(args[i], biLinear); + } else if(args[i].equals("-singleFBO")) { + i++; + useSingleFBO = MiscUtils.atob(args[i], useSingleFBO); + } else if(args[i].equals("-vignette")) { + i++; + useVignette = MiscUtils.atob(args[i], useVignette); + } else if(args[i].equals("-chromatic")) { + i++; + useChromatic = MiscUtils.atob(args[i], useChromatic); + } else if(args[i].equals("-timewarp")) { + i++; + useTimewarp = MiscUtils.atob(args[i], useTimewarp); + } else if(args[i].equals("-vignette")) { + i++; + useVignette = MiscUtils.atob(args[i], useVignette); + } else if(args[i].equals("-mainScreen")) { + i++; + useStereoScreen = !MiscUtils.atob(args[i], useStereoScreen); + } else if(args[i].equals("-autoSwap")) { + i++; + useAutoSwap = MiscUtils.atob(args[i], useAutoSwap); + } else if(args[i].equals("-test")) { + i++; + stereoRendererListenerName = args[i]; + } else if(args[i].equals("-filmFile")) { + i++; + useFilmFile = args[i]; + } else if(args[i].equals("-filmURI")) { + i++; + useFilmURI = args[i]; + } + } + if( null != stereoRendererListenerName ) { + try { + final Object stereoRendererListener = ReflectionUtil.createInstance(stereoRendererListenerName, null); + } catch (final Exception e) { + e.printStackTrace(); + } + } + final StereoGLEventListener upstream; + final MovieSBSStereo movieSimple; + final URI movieURI; + if( null != useFilmFile ) { + movieSimple = new MovieSBSStereo(); + movieURI = IOUtil.toURISimple(new File(useFilmFile)); + upstream = movieSimple; + } else if( null != useFilmURI ) { + movieSimple = new MovieSBSStereo(); + movieURI = new URI(useFilmURI); + upstream = movieSimple; + } else { + final GearsES2 demo = new GearsES2(0); + demo.setVerbose(false); + upstream = demo; + movieSimple = null; + movieURI = null; + } + final StereoDemo01 demo01 = new StereoDemo01(); + demo01.doIt(0, upstream, movieSimple, movieURI, biLinear, numSamples, useSingleFBO, useVignette, useChromatic, useTimewarp, + useAutoSwap, true /* useAnimator */, false /* exclusiveContext*/); + } + + public void doIt(final int stereoDeviceIndex, + final StereoGLEventListener upstream, final MovieSBSStereo movieSimple, final URI movieURI, + final boolean biLinear, final int numSamples, final boolean useSingleFBO, + final boolean useVignette, final boolean useChromatic, final boolean useTimewarp, + final boolean useAutoSwap, final boolean useAnimator, final boolean exclusiveContext) throws InterruptedException { + + System.err.println("glob duration "+duration); + System.err.println("glob useStereoScreen "+useStereoScreen); + System.err.println("biLinear "+biLinear); + System.err.println("numSamples "+numSamples); + System.err.println("useSingleFBO "+useSingleFBO); + System.err.println("useVignette "+useVignette); + System.err.println("useChromatic "+useChromatic); + System.err.println("useTimewarp "+useTimewarp); + System.err.println("useAutoSwap "+useAutoSwap); + + final StereoDeviceFactory stereoDeviceFactory = StereoDeviceFactory.createDefaultFactory(); + if( null == stereoDeviceFactory ) { + System.err.println("No StereoDeviceFactory available"); + return; + } + + final StereoDevice stereoDevice = stereoDeviceFactory.createDevice(stereoDeviceIndex, true /* verbose */); + if( null == stereoDevice ) { + System.err.println("No StereoDevice.Context available for index "+stereoDeviceIndex); + return; + } + + // Start the sensor which provides the Rift’s pose and motion. + if( !stereoDevice.startSensors(true) ) { + System.err.println("Could not start sensors on device "+stereoDeviceIndex); + } + + // + // + // + final GLCapabilities caps = new GLCapabilities(GLProfile.getMaxProgrammable(true /* favorHardwareRasterizer */)); + final GLWindow window = GLWindow.create(caps); + + final PointImmutable devicePos = stereoDevice.getPosition(); + final DimensionImmutable deviceRes = stereoDevice.getSurfaceSize(); + window.setSize(deviceRes.getWidth(), deviceRes.getHeight()); + if( useStereoScreen ) { + window.setPosition(devicePos.getX(), devicePos.getY()); + } + window.setAutoSwapBufferMode(useAutoSwap); + window.setUndecorated(true); + + final Animator animator = useAnimator ? new Animator() : null; + if( useAnimator ) { + animator.setModeBits(false, AnimatorBase.MODE_EXPECT_AWT_RENDERING_THREAD); + animator.setExclusiveContext(exclusiveContext); + } + + // + // Oculus Rift setup + // + // EyePos.y = ovrHmd_GetFloat(HMD, OVR_KEY_EYE_HEIGHT, EyePos.y); + final FovHVHalves[] defaultEyeFov = stereoDevice.getDefaultFOV(); + System.err.println("Default Fov[0]: "+defaultEyeFov[0]); + System.err.println("Default Fov[1]: "+defaultEyeFov[1]); + + final float[] eyePositionOffset = null == movieSimple ? StereoDevice.DEFAULT_EYE_POSITION_OFFSET // default + : new float[] { 0f, 0.3f, 0f }; // better fixed movie position + final int textureUnit = 0; + final int distortionBits = ( useVignette ? StereoDeviceRenderer.DISTORTION_VIGNETTE : 0 ) | + ( useChromatic ? StereoDeviceRenderer.DISTORTION_CHROMATIC : 0 ) | + ( useTimewarp ? StereoDeviceRenderer.DISTORTION_TIMEWARP : 0 ); + final float pixelsPerDisplayPixel = 1f; + final StereoDeviceRenderer stereoDeviceRenderer = + stereoDevice.createRenderer(distortionBits, useSingleFBO ? 1 : 2, eyePositionOffset, + defaultEyeFov, pixelsPerDisplayPixel, textureUnit); + System.err.println("StereoDeviceRenderer: "+stereoDeviceRenderer); + + final int texFilter = biLinear ? GL.GL_LINEAR : GL.GL_NEAREST; + final StereoClientRenderer renderer = new StereoClientRenderer(stereoDeviceRenderer, true /* ownsDist */, texFilter, texFilter, numSamples); + if( null != movieSimple && null != movieURI) { + movieSimple.setScaleOrig(true); + final GLMediaPlayer mp = movieSimple.getGLMediaPlayer(); + mp.attachObject(MovieSimple.WINDOW_KEY, window); + mp.attachObject(MovieSBSStereo.STEREO_RENDERER_KEY, renderer); + mp.addEventListener(MovieSBSStereo.stereoGLMediaEventListener); + movieSimple.initStream(movieURI, GLMediaPlayer.STREAM_ID_AUTO, GLMediaPlayer.STREAM_ID_AUTO, 3); + } else { + renderer.addGLEventListener(upstream); + } + window.addGLEventListener(renderer); + + final QuitAdapter quitAdapter = new QuitAdapter(); + window.addKeyListener(quitAdapter); + window.addWindowListener(quitAdapter); + + window.addKeyListener(new KeyAdapter() { + public void keyReleased(final KeyEvent e) { + if( e.isAutoRepeat() ) { + return; + } + switch(e.getKeySymbol()) { + case KeyEvent.VK_R: { + stereoDevice.startSensors(!stereoDevice.getSensorsStarted()); + break; + } + } + } } ); + + if( useAnimator ) { + animator.add(window); + animator.start(); + } + window.setVisible(true); + if( useAnimator ) { + animator.setUpdateFPSFrames(60, System.err); + } + + final long t0 = System.currentTimeMillis(); + long t1 = t0; + while(!quitAdapter.shouldQuit() && t1-t0<duration) { + Thread.sleep(100); + t1 = System.currentTimeMillis(); + } + + if( useAnimator ) { + animator.stop(); + } + window.destroy(); + stereoDevice.dispose(); + } +} diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/stereo/ovr/OVRDemo01.java b/src/test/com/jogamp/opengl/test/junit/jogl/stereo/ovr/OVRDemo01.java deleted file mode 100644 index 1cc07ec22..000000000 --- a/src/test/com/jogamp/opengl/test/junit/jogl/stereo/ovr/OVRDemo01.java +++ /dev/null @@ -1,236 +0,0 @@ -/** - * Copyright 2014 JogAmp Community. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied, of JogAmp Community. - */ -package com.jogamp.opengl.test.junit.jogl.stereo.ovr; - -import javax.media.opengl.GL; -import javax.media.opengl.GLCapabilities; -import javax.media.opengl.GLEventListener; -import javax.media.opengl.GLProfile; - -import jogamp.opengl.oculusvr.OVRDistortion; - -import com.jogamp.newt.opengl.GLWindow; -import com.jogamp.oculusvr.OVR; -import com.jogamp.oculusvr.OVRException; -import com.jogamp.oculusvr.OVRVersion; -import com.jogamp.oculusvr.OvrHmdContext; -import com.jogamp.oculusvr.ovrFovPort; -import com.jogamp.oculusvr.ovrHmdDesc; -import com.jogamp.oculusvr.ovrSizei; -import com.jogamp.oculusvr.ovrVector2i; -import com.jogamp.opengl.oculusvr.OVRSBSRendererDualFBO; -import com.jogamp.opengl.oculusvr.OVRSBSRendererSingleFBO; -import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2; -import com.jogamp.opengl.test.junit.util.MiscUtils; -import com.jogamp.opengl.test.junit.util.QuitAdapter; -import com.jogamp.opengl.util.Animator; -import com.jogamp.opengl.util.AnimatorBase; - -/** - * All distortions, no multisampling, bilinear filtering, manual-swap and using two FBOs (default, good) - * <pre> - * java OVRDemo01 -time 10000000 - * </pre> - * All distortions, 8x multisampling, bilinear filtering, manual-swap and using two FBOs (best - slowest) - * <pre> - * java OVRDemo01 -time 10000000 -samples 8 - * </pre> - * All distortions, 8x multisampling, bilinear filtering, manual-swap and using one a big single FBO (w/ all commandline params) - * <pre> - * java OVRDemo01 -time 10000000 -vignette true -chromatic true -timewarp false -samples 8 -biLinear true -autoSwap false -singleFBO true -mainScreen false - * </pre> - * No distortions, no multisampling, no filtering, auto-swap and using a big single FBO (worst and fastest) - * <pre> - * java OVRDemo01 -time 10000000 -vignette false -chromatic false -timewarp false -samples 0 -biLinear false -autoSwap true -singleFBO true - * </pre> - * Test on main screen: - * <pre> - * java OVRDemo01 -time 10000000 -mainScreen true - * </pre> - * - */ -public class OVRDemo01 { - static long duration = 10000; // ms - - static boolean useOVRScreen = true; - - static int numSamples = 0; - static boolean biLinear = true; - static boolean useSingleFBO = false; - static boolean useVignette = true; - static boolean useChromatic = true; - static boolean useTimewarp = true; - static boolean useAutoSwap = false; - - public static void main(final String args[]) throws InterruptedException { - for(int i=0; i<args.length; i++) { - if(args[i].equals("-time")) { - i++; - duration = MiscUtils.atol(args[i], duration); - } else if(args[i].equals("-samples")) { - i++; - numSamples = MiscUtils.atoi(args[i], numSamples); - } else if(args[i].equals("-biLinear")) { - i++; - biLinear = MiscUtils.atob(args[i], biLinear); - } else if(args[i].equals("-singleFBO")) { - i++; - useSingleFBO = MiscUtils.atob(args[i], useSingleFBO); - } else if(args[i].equals("-vignette")) { - i++; - useVignette = MiscUtils.atob(args[i], useVignette); - } else if(args[i].equals("-chromatic")) { - i++; - useChromatic = MiscUtils.atob(args[i], useChromatic); - } else if(args[i].equals("-timewarp")) { - i++; - useTimewarp = MiscUtils.atob(args[i], useTimewarp); - } else if(args[i].equals("-vignette")) { - i++; - useVignette = MiscUtils.atob(args[i], useVignette); - } else if(args[i].equals("-mainScreen")) { - i++; - useOVRScreen = !MiscUtils.atob(args[i], useOVRScreen); - } else if(args[i].equals("-autoSwap")) { - i++; - useAutoSwap = MiscUtils.atob(args[i], useAutoSwap); - } - } - final OVRDemo01 demo01 = new OVRDemo01(); - demo01.doIt(0, biLinear, numSamples, useSingleFBO, useVignette, useChromatic, useTimewarp, - useAutoSwap, true /* useAnimator */, false /* exclusiveContext*/); - } - - public void doIt(final int ovrHmdIndex, final boolean biLinear, final int numSamples, - final boolean useSingleFBO, - final boolean useVignette, final boolean useChromatic, final boolean useTimewarp, - final boolean useAutoSwap, - final boolean useAnimator, final boolean exclusiveContext) throws InterruptedException { - - System.err.println("glob duration "+duration); - System.err.println("glob useOVRScreen "+useOVRScreen); - System.err.println("biLinear "+biLinear); - System.err.println("numSamples "+numSamples); - System.err.println("useSingleFBO "+useSingleFBO); - System.err.println("useVignette "+useVignette); - System.err.println("useChromatic "+useChromatic); - System.err.println("useTimewarp "+useTimewarp); - System.err.println("useAutoSwap "+useAutoSwap); - - // Initialize LibOVR... - if( !OVR.ovr_Initialize() ) { // recursive .. - throw new OVRException("OVR not available"); - } - final OvrHmdContext hmdCtx = OVR.ovrHmd_Create(ovrHmdIndex); - if( null == hmdCtx ) { - throw new OVRException("OVR HMD #"+ovrHmdIndex+" not available"); - } - final ovrHmdDesc hmdDesc = ovrHmdDesc.create(); - OVR.ovrHmd_GetDesc(hmdCtx, hmdDesc); - System.err.println(OVRVersion.getAvailableCapabilitiesInfo(hmdDesc, ovrHmdIndex, null).toString()); - - // Start the sensor which provides the Rift’s pose and motion. - final int requiredSensorCaps = 0; - final int supportedSensorCaps = requiredSensorCaps | OVR.ovrSensorCap_Orientation | OVR.ovrSensorCap_YawCorrection | OVR.ovrSensorCap_Position; - if( !OVR.ovrHmd_StartSensor(hmdCtx, supportedSensorCaps, requiredSensorCaps) ) { - throw new OVRException("OVR HMD #"+ovrHmdIndex+" required sensors not available"); - } - - // - // - // - - final GLCapabilities caps = new GLCapabilities(GLProfile.getMaxProgrammable(true /* favorHardwareRasterizer */)); - final GLWindow window = GLWindow.create(caps); - final ovrVector2i ovrPos = hmdDesc.getWindowsPos(); - final ovrSizei ovrRes = hmdDesc.getResolution(); - window.setSize(ovrRes.getW(), ovrRes.getH()); - if( useOVRScreen ) { - window.setPosition(ovrPos.getX(), ovrPos.getY()); - } - window.setAutoSwapBufferMode(useAutoSwap); - window.setUndecorated(true); - - final Animator animator = useAnimator ? new Animator() : null; - if( useAnimator ) { - animator.setModeBits(false, AnimatorBase.MODE_EXPECT_AWT_RENDERING_THREAD); - animator.setExclusiveContext(exclusiveContext); - } - - // - // Oculus Rift setup - // - final float[] eyePositionOffset = { 0.0f, 1.6f, -5.0f }; - // EyePos.y = ovrHmd_GetFloat(HMD, OVR_KEY_EYE_HEIGHT, EyePos.y); - - final ovrFovPort[] defaultEyeFov = hmdDesc.getDefaultEyeFov(0, new ovrFovPort[2]); - final int distortionCaps = ( useVignette ? OVR.ovrDistortionCap_Vignette : 0 ) | - ( useChromatic ? OVR.ovrDistortionCap_Chromatic : 0 ) | - ( useTimewarp ? OVR.ovrDistortionCap_TimeWarp : 0 ); - final float pixelsPerDisplayPixel = 1f; - final OVRDistortion dist = OVRDistortion.create(hmdCtx, useSingleFBO, eyePositionOffset, defaultEyeFov, pixelsPerDisplayPixel, distortionCaps); - System.err.println("OVRDistortion: "+dist); - - final int texFilter = biLinear ? GL.GL_LINEAR : GL.GL_NEAREST; - final GearsES2 upstream = new GearsES2(0); - upstream.setVerbose(false); - final GLEventListener renderer; - if( useSingleFBO ) { - renderer = new OVRSBSRendererSingleFBO(dist, true /* ownsDist */, upstream, texFilter, texFilter, numSamples); - } else { - renderer = new OVRSBSRendererDualFBO(dist, true /* ownsDist */, upstream, texFilter, texFilter, numSamples); - } - window.addGLEventListener(renderer); - - final QuitAdapter quitAdapter = new QuitAdapter(); - window.addKeyListener(quitAdapter); - window.addWindowListener(quitAdapter); - - if( useAnimator ) { - animator.add(window); - animator.start(); - } - window.setVisible(true); - if( useAnimator ) { - animator.setUpdateFPSFrames(60, System.err); - } - - final long t0 = System.currentTimeMillis(); - long t1 = t0; - while(!quitAdapter.shouldQuit() && t1-t0<duration) { - Thread.sleep(100); - t1 = System.currentTimeMillis(); - } - - if( useAnimator ) { - animator.stop(); - } - window.destroy(); - // OVR.ovr_Shutdown(); - } -} |