/** * 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; import java.nio.FloatBuffer; import com.jogamp.opengl.GL; import com.jogamp.opengl.GL2ES2; import com.jogamp.opengl.GLAutoDrawable; import com.jogamp.opengl.GLES2; import com.jogamp.opengl.GLEventListener; import com.jogamp.opengl.GLException; import com.jogamp.opengl.GLProfile; import com.jogamp.opengl.GLUniformData; import com.jogamp.opengl.fixedfunc.GLMatrixFunc; import com.jogamp.common.os.Platform; import com.jogamp.newt.Window; import com.jogamp.newt.event.MouseAdapter; import com.jogamp.newt.event.MouseEvent; import com.jogamp.newt.event.MouseListener; import com.jogamp.opengl.GLExtensions; import com.jogamp.opengl.JoglVersion; import com.jogamp.opengl.util.GLArrayDataServer; import com.jogamp.opengl.util.PMVMatrix; 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.texture.Texture; import com.jogamp.opengl.util.texture.TextureCoords; import com.jogamp.opengl.util.texture.TextureSequence; import com.jogamp.opengl.util.texture.TextureSequence.TextureFrame; public class TextureSequenceCubeES2 implements GLEventListener { public TextureSequenceCubeES2 (final TextureSequence texSource, final boolean innerCube, final float zoom0, final float rotx, final float roty) { this.texSeq = texSource; this.innerCube = innerCube; this.zoom = zoom0; this.view_rotx = rotx; this.view_roty = roty; } private TextureSequence texSeq; public ShaderState st; public PMVMatrix pmvMatrix; private GLUniformData pmvMatrixUniform; // private TextureCoords[] textureCoords = null; private float nearPlaneNormalized; // private float zoom0=-5.0f, zoom=zoom0; // private float view_rotx = 20.0f, view_roty = 30.0f, view_rotz = 0.0f; public float zoom=-2.3f; private float view_rotx = 0.0f, view_roty = 0.0f; private final float view_rotz = 0.0f; int[] vboNames = new int[4]; boolean innerCube; private final MouseListener mouseAction = new MouseAdapter() { int lx = 0; int ly = 0; boolean first = false; public void mousePressed(final MouseEvent e) { first = true; } public void mouseMoved(final MouseEvent e) { first = false; } public void mouseDragged(final MouseEvent e) { 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); } if(e.getPointerCount()==2) { // 2 pointers zoom .. if(first) { lx = Math.abs(e.getY(0)-e.getY(1)); first=false; return; } final int nv = Math.abs(e.getY(0)-e.getY(1)); final int dy = nv - lx; { final float o = zoom; final float d = 40f*Math.signum(dy)/height; zoom += d; System.err.println("zoom.d: "+o+" + "+d+" -> "+zoom); } lx = nv; } else { // 1 pointer rotate if(first) { lx = e.getX(); ly = e.getY(); first=false; return; } final int nx = e.getX(); final int ny = e.getY(); view_roty += 360f * ( (float)( nx - lx ) / (float)width ); view_rotx += 360f * ( (float)( ny - ly ) / (float)height ); lx = nx; ly = ny; } } public void mouseWheelMoved(final MouseEvent e) { // System.err.println("XXX "+e); if( !e.isShiftDown() ) { final float o = zoom; final float d = e.getRotation()[1]/10f; // vertical: wheel zoom += d; System.err.println("zoom.w: "+o+" + "+d+" -> "+zoom); } } }; static final String shaderBasename = "texsequence_xxx"; static final String myTextureLookupName = "myTexture2D"; private void initShader(final GL2ES2 gl) { // Create & Compile the shader objects final ShaderCode rsVp = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, this.getClass(), "shader", "shader/bin", shaderBasename, true); final ShaderCode rsFp = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, this.getClass(), "shader", "shader/bin", shaderBasename, true); boolean preludeGLSLVersion = true; if( GLES2.GL_TEXTURE_EXTERNAL_OES == texSeq.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, texSeq.getRequiredExtensionsShaderStub()); rsFp.addDefaultShaderPrecision(gl, rsFpPos); final String texLookupFuncName = texSeq.getTextureLookupFunctionName(myTextureLookupName); rsFp.replaceInShaderSource(myTextureLookupName, texLookupFuncName); // Inject TextureSequence shader details final StringBuilder sFpIns = new StringBuilder(); sFpIns.append("uniform ").append(texSeq.getTextureSampler2DType()).append(" mgl_ActiveTexture;\n"); sFpIns.append(texSeq.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); } GLArrayDataServer interleavedVBO, cubeIndicesVBO; public void init(final GLAutoDrawable drawable) { final GL2ES2 gl = drawable.getGL().getGL2ES2(); System.err.println(JoglVersion.getGLInfo(gl, null)); final TextureFrame frame = texSeq.getLastTexture(); if( null == frame ) { return; } final Texture tex= frame.getTexture(); initShader(gl); // Push the 1st uniform down the path st.useProgram(gl, true); pmvMatrix = new PMVMatrix(); reshapePMV(drawable.getSurfaceWidth(), drawable.getSurfaceHeight()); pmvMatrixUniform = new GLUniformData("mgl_PMVMatrix", 4, 4, pmvMatrix.glGetPMvMatrixf()); // P, Mv if(!st.uniform(gl, pmvMatrixUniform)) { throw new GLException("Error setting PMVMatrix in shader: "+st); } if(!st.uniform(gl, new GLUniformData("mgl_ActiveTexture", texSeq.getTextureUnit()))) { throw new GLException("Error setting mgl_ActiveTexture in shader: "+st); } // calculate centered tex coords w/ aspect ratio final float[] fixedCubeTexCoords = new float[s_cubeTexCoords.length]; { final float aspect = tex.getAspectRatio(); final TextureCoords tc = tex.getImageTexCoords(); System.err.println("XXX0: aspect: "+aspect); System.err.println("XXX0: y-flip: "+tex.getMustFlipVertically()); System.err.println("XXX0: "+tc); final float tc_x1 = Math.max(tc.left(), tc.right()); final float tc_y1 = Math.max(tc.bottom(), tc.top()); final float ss=1f, ts=aspect; // scale tex-coord final float dy = ( 1f - aspect ) / 2f ; for(int i=0; i<s_cubeTexCoords.length; i+=2) { final float tx = s_cubeTexCoords[i+0]; final float ty = s_cubeTexCoords[i+1]; if(tx!=0) { fixedCubeTexCoords[i+0] = tc_x1 * ss; } if(ty==0 && !tex.getMustFlipVertically() || ty!=0 && tex.getMustFlipVertically()) { fixedCubeTexCoords[i+1] = 0f + dy; } else { fixedCubeTexCoords[i+1] = tc_y1 * ts + dy; } } } interleavedVBO = GLArrayDataServer.createGLSLInterleaved(3+4+2, GL.GL_FLOAT, false, 3*6*4, GL.GL_STATIC_DRAW); { interleavedVBO.addGLSLSubArray("mgl_Vertex", 3, GL.GL_ARRAY_BUFFER); interleavedVBO.addGLSLSubArray("mgl_Color", 4, GL.GL_ARRAY_BUFFER); //interleavedVBO.addGLSLSubArray("mgl_Normal", 3, GL.GL_ARRAY_BUFFER); interleavedVBO.addGLSLSubArray("mgl_MultiTexCoord", 2, GL.GL_ARRAY_BUFFER); final FloatBuffer ib = (FloatBuffer)interleavedVBO.getBuffer(); for(int i=0; i<6*4; i++) { ib.put(s_cubeVertices, i*3, 3); ib.put(s_cubeColors, i*4, 4); //ib.put(s_cubeNormals, i*3, 3); ib.put(fixedCubeTexCoords, i*2, 2); } } interleavedVBO.seal(gl, true); interleavedVBO.enableBuffer(gl, false); st.ownAttribute(interleavedVBO, true); cubeIndicesVBO = GLArrayDataServer.createData(6, GL.GL_UNSIGNED_SHORT, 6, GL.GL_STATIC_DRAW, GL.GL_ELEMENT_ARRAY_BUFFER); for(int i=0; i<6*6; i++) { cubeIndicesVBO.puts(s_cubeIndices[i]); } cubeIndicesVBO.seal(gl, true); cubeIndicesVBO.enableBuffer(gl, false); st.ownAttribute(cubeIndicesVBO, true); gl.glEnable(GL.GL_DEPTH_TEST); st.useProgram(gl, false); final Object upstreamWidget = drawable.getUpstreamWidget(); if (upstreamWidget instanceof Window) { final Window window = (Window) upstreamWidget; window.addMouseListener(mouseAction); } 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(mouseAction, drawable).addTo(comp); } // Let's show the completed shader state .. System.out.println("iVBO: "+interleavedVBO); System.out.println(st); } public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) { final GL2ES2 gl = drawable.getGL().getGL2ES2(); gl.glViewport(0, 0, width, height); if(!innerCube) { // lights on } else { // lights off } // gl.glEnable(GL.GL_CULL_FACE); // gl.glDisable(GL.GL_DITHER); if(null != st) { reshapePMV(width, height); st.useProgram(gl, true); st.uniform(gl, pmvMatrixUniform); st.useProgram(gl, false); } } private void reshapePMV(final int width, final int height) { if(null != pmvMatrix) { pmvMatrix.glMatrixMode(GLMatrixFunc.GL_PROJECTION); pmvMatrix.glLoadIdentity(); if(!innerCube) { pmvMatrix.gluPerspective(45.0f, (float)width / (float)height, 1f, 10.0f); nearPlaneNormalized = 1f/(100f-1f); } else { pmvMatrix.glOrthof(-1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 10.0f); nearPlaneNormalized = 0f; } System.err.println("XXX0: Perspective nearPlaneNormalized: "+nearPlaneNormalized); pmvMatrix.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); pmvMatrix.glLoadIdentity(); pmvMatrix.glTranslatef(0, 0, zoom); } } @Override public void dispose(final GLAutoDrawable drawable) { final GL2ES2 gl = drawable.getGL().getGL2ES2(); texSeq = null; pmvMatrixUniform = null; pmvMatrix=null; if( null != st ) { st.destroy(gl); st=null; } } @Override public void display(final GLAutoDrawable drawable) { final GL2ES2 gl = drawable.getGL().getGL2ES2(); if(innerCube) { // Clear background to white gl.glClearColor(1.0f, 1.0f, 1.0f, 0.4f); } else { // Clear background to blue gl.glClearColor(0.0f, 0.0f, 1.0f, 1.0f); } 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.glLoadIdentity(); pmvMatrix.glTranslatef(0, 0, zoom); 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); st.uniform(gl, pmvMatrixUniform); interleavedVBO.enableBuffer(gl, true); Texture tex = null; if(null!=texSeq) { final TextureSequence.TextureFrame texFrame = texSeq.getNextTexture(gl); if(null != texFrame) { tex = texFrame.getTexture(); gl.glActiveTexture(GL.GL_TEXTURE0+texSeq.getTextureUnit()); tex.enable(gl); tex.bind(gl); } } cubeIndicesVBO.bindBuffer(gl, true); // keeps VBO binding gl.glDrawElements(GL.GL_TRIANGLES, cubeIndicesVBO.getElementCount() * cubeIndicesVBO.getComponentCount(), GL.GL_UNSIGNED_SHORT, 0); cubeIndicesVBO.bindBuffer(gl, false); if(null != tex) { tex.disable(gl); } interleavedVBO.enableBuffer(gl, false); st.useProgram(gl, false); } static final float[] light_position = { -50.f, 50.f, 50.f, 0.f }; static final float[] light_ambient = { 0.125f, 0.125f, 0.125f, 1.f }; static final float[] light_diffuse = { 1.0f, 1.0f, 1.0f, 1.f }; static final float[] material_spec = { 1.0f, 1.0f, 1.0f, 0.f }; static final float[] zero_vec4 = { 0.0f, 0.0f, 0.0f, 0.f }; private static final float[] s_cubeVertices = /* f b t b r l */ { -1f, 1f, 1f, 1f, -1f, 1f, 1f, 1f, 1f, -1f, -1f, 1f, -1f, 1f, -1f, 1f, -1f, -1f, 1f, 1f, -1f, -1f, -1f, -1f, -1f, -1f, 1f, 1f, -1f, -1f, 1f, -1f, 1f, -1f, -1f, -1f, -1f, 1f, 1f, 1f, 1f, -1f, 1f, 1f, 1f, -1f, 1f, -1f, 1f, -1f, 1f, 1f, 1f, -1f, 1f, 1f, 1f, 1f, -1f, -1f, -1f, -1f, 1f, -1f, 1f, -1f, -1f, 1f, 1f, -1f, -1f, -1f }; private static final float[] s_cubeTexCoords = { // LT RB RT LB 0f, 1f, 1f, 0f, 1f, 1f, 0f, 0f, 0f, 1f, 1f, 0f, 1f, 1f, 0f, 0f, 0f, 1f, 1f, 0f, 1f, 1f, 0f, 0f, 0f, 1f, 1f, 0f, 1f, 1f, 0f, 0f, 0f, 0f, 1f, 1f, 0f, 1f, 1f, 0f, 0f, 0f, 1f, 1f, 0f, 1f, 1f, 0f, }; private static final float[] s_cubeColors = { 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 40f/255f, 80f/255f, 160f/255f, 255f/255f, 40f/255f, 80f/255f, 160f/255f, 255f/255f, 40f/255f, 80f/255f, 160f/255f, 255f/255f, 40f/255f, 80f/255f, 160f/255f, 255f/255f, 40f/255f, 80f/255f, 160f/255f, 255f/255f, 40f/255f, 80f/255f, 160f/255f, 255f/255f, 40f/255f, 80f/255f, 160f/255f, 255f/255f, 40f/255f, 80f/255f, 160f/255f, 255f/255f, 128f/255f, 128f/255f, 128f/255f, 255f/255f, 128f/255f, 128f/255f, 128f/255f, 255f/255f, 128f/255f, 128f/255f, 128f/255f, 255f/255f, 128f/255f, 128f/255f, 128f/255f, 255f/255f, 255f/255f, 110f/255f, 10f/255f, 255f/255f, 255f/255f, 110f/255f, 10f/255f, 255f/255f, 255f/255f, 110f/255f, 10f/255f, 255f/255f, 255f/255f, 110f/255f, 10f/255f, 255f/255f, 255f/255f, 70f/255f, 60f/255f, 255f/255f, 255f/255f, 70f/255f, 60f/255f, 255f/255f, 255f/255f, 70f/255f, 60f/255f, 255f/255f, 255f/255f, 70f/255f, 60f/255f, 255f/255f }; /* private static final float[] s_cubeNormals = { 0f, 0f, 1f, 0f, 0f, 1f, 0f, 0f, 1f, 0f, 0f, 1f, 0f, 0f, -1f, 0f, 0f, -1f, 0f, 0f, -1f, 0f, 0f, -1f, 0f, -1f, 0f, 0f, -1f, 0f, 0f, -1f, 0f, 0f, -1f, 0f, 0f, 1f, 0f, 0f, 1f, 0f, 0f, 1f, 0f, 0f, 1f, 0f, 1f, 0f, 0f, 1f, 0f, 0f, 1f, 0f, 0f, 1f, 0f, 0f, -1f, 0f, 0f, -1f, 0f, 0f, -1f, 0f, 0f, -1f, 0f, 0f };*/ private static final short[] s_cubeIndices = { 0, 3, 1, 2, 0, 1, /* front */ 6, 5, 4, 5, 7, 4, /* back */ 8, 11, 9, 10, 8, 9, /* top */ 15, 12, 13, 12, 14, 13, /* bottom */ 16, 19, 17, 18, 16, 17, /* right */ 23, 20, 21, 20, 22, 21 /* left */ }; }